news 2026/9/23 2:03:29

开源大模型私有化部署全流程:环境配置、LoRA微调与LangChain接入

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
开源大模型私有化部署全流程:环境配置、LoRA微调与LangChain接入

简介:围绕开源大模型从环境配置到落地应用的全链路实践合集,面向具备一定Python基础的AI算法工程师、应用开发者和学习者,聚焦环境搭建、私有化部署、LoRA微调及LangChain集成四大核心场景。资源共79个文件,压缩包约23.88MB,其中包含45个Python脚本、12个Jupyter Notebook、9个JSON配置文件以及模型权重、Markdown说明等,覆盖DeepSeek、Qwen、Yi、Baichuan、ChatGLM、miniCPM等主流开源模型的下载、训练、推理与接口封装代码。除核心脚本外,还提供向量数据库示例、嵌入式模型配置、依赖需求清单及一个轻量语音样例文件,可帮助读者快速跑通本地大模型环境。已有858人学习浏览,适合用来复现微调实验、搭建私有化服务,或借鉴LangChain结合业务的应用思路。整体目录结构清晰,按模型与任务划分模块,从数据准备、LoRA训练、模型合并到API服务均有对应参考实现,能够缩短上手周期,减少环境与兼容性踩坑,具有较强的工程参考价值。

1. 先别急着下数据集,这条链路才是开源大模型项目的完整骨架

开源大模型从拿到手到能产生业务价值,中间隔着环境配置、私有化部署、lora微调、langchain应用四道工序。这个标题把这四件事用一个 zip 包串起来,说明真正值得关注的不只是某一个环节,而是整个闭环怎么跑通。很多项目死在第一步——conda 环境冲突、CUDA 版本不匹配、transformers 加载不出模型;更多项目倒在最后一公里——模型部署好了,却不知道怎么用 langchain 把它变成能回答问题的 Agent。

这篇文章按实际推进顺序拆解这条链路:先在裸机上把 Python、CUDA、PyTorch 环境配好,再拉模型做私有化部署,接着用 LoRA 低成本微调让它懂你的业务,最后用 langchain 把这套模型接入应用。适合想在自己的机器或内网服务器上完整跑通开源大模型项目的工程师,也适合刚接触这一整套流程、需要一份可复现路线图的人。

2. 环境配置:conda 隔离、CUDA 匹配、PyTorch 验证,半小时搭出可用环境

2.1 为什么环境配置是第一个真正的坑

开源大模型项目回传的报错,绝大多数发生在模型加载之前。undefined symbolCUDA error: no kernel imageGLIBCXX not found,这些问题看着像代码问题,实际全是环境问题。深度学习框架对 CUDA 工具包版本、cuDNN 版本、Python 版本、GCC 版本都有隐性要求,混装很容易把整个系统搞乱。

常见做法是用 conda 给每个项目建独立环境,把系统级 Python 和项目依赖隔离开。这样做的理由很直接:不同项目可能锁定不同版本的 PyTorch 和 transformers,一个环境一套版本,互相完全不影响。即使本机已经装了 Python 3.10,也不要直接pip install torch,因为系统 Python 可能被其他服务依赖,贸然升级或装包会污染全局环境。

2.2 最小可复现的环境安装命令

安装 Miniconda 时,我一般会装到用户目录下,避免写/usr/bin需要的权限问题。以下是完整命令序列:

# 安装 Miniconda 到用户目录 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh -b -p $HOME/miniconda3 echo 'export PATH="$HOME/miniconda3/bin:$PATH"' >> ~/.bashrc source ~/.bashrc # 创建独立 Python 环境,Python 版本定为 3.10 conda create -n llm python=3.10 -y conda activate llm # 安装 PyTorch,按 CUDA 版本选对应命令 # CUDA 11.8 用: pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # CUDA 12.1 用: pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121

这里有几个参数需要重点理解。-b -p $HOME/miniconda3表示静默安装并指定安装目录,llm是环境名称,可以按项目名改。PyTorch 的安装命令里--index-url指定了配套 CUDA 版本的预编译包源,这是避免no kernel image报错的关键——用默认 PyPI 源装的 torch 是 CPU 版,用错 CUDA 版本则会在运行时找不到核函数。

安装完成后必须验证 CUDA 是否真的通了:

import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))

如果torch.cuda.is_available()返回 False,优先查nvidia-smi显示的驱动版本是否支持目标 CUDA 版本,别急着重装。驱动版本不用和 CUDA 工具包版本完全一致,只要驱动支持的 CUDA 版本不低于 PyTorch 要求的就行。

2.3 安装项目依赖时的顺序和版本锁定策略

下载好开源项目或微调框架后,依赖安装不能无脑pip install -r requirements.txt。我一般会在激活环境后先装基本依赖,再逐项核对关键包的版本:

pip install transformers datasets accelerate peft bitsandbytes pip install langchain langchain-community langchain-core langchainhub pip install huggingface_hub sentencepiece protobuf

关键是 transformers、peft、accelerate 三者的版本兼容。LoRA 微调需要 peft 配合 transformers 使用,peft 太新或太旧都会出现AttributeError: 'PeftModel' object has no attribute 'merge_and_unload'这类问题。保险起见,可以用pip install transformers==4.45.0 peft==0.13.0 accelerate==0.34.0这样锁定一组经过验证的版本。

提示:生产环境不要用pip install -r requirements.txt后不做任何记录。安装完依赖后用pip freeze > requirements-lock.txt把实际版本导出,后续复现环境时可以直接用。

3. 私有化部署:模型下载、transformers 加载、GPU 加速与推理接口

3.1 私有化部署要解决的核心问题

私有化部署的核心诉求是数据不出内网。企业选择开源模型而不是调用云端 API,通常是因为业务数据有保密要求,或者推理频次高到按 Token 计费不划算。这也决定了部署方案的选择方向:模型必须能完全跑在自己的 GPU 服务器上,推理接口要可控、可监控、可替换。

模型的获取渠道主要是 Hugging Face 和 ModelScope。国内网络环境访问 Hugging Face 不稳定时,我一般优先从 ModelScope 下载,速度快且不需要额外配置。下载时用和推理环境中完全一致的版本,避免模型权重和 transformers 版本不兼容导致的key mismatch。下面以 Qwen2.5-7B-Instruct 为例演示完整部署流程。

3.2 用 transformers 在本地加载和推理

下载模型到本地目录后,推理脚本只做三件事:加载模型、加载分词器、生成回复。先看最小可用的推理代码:

import torch from transformers import AutoModelForCausalLM, AutoTokenizer model_path = "/data/models/qwen2.5-7b-instruct" tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) model = AutoModelForCausalLM.from_pretrained( model_path, torch_dtype=torch.float16, device_map="auto", trust_remote_code=True ) prompt = "介绍一下锂离子电池的工作原理" messages = [ {"role": "system", "content": "你是一个专业的电池技术顾问。"}, {"role": "user", "content": prompt} ] text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) model_inputs = tokenizer([text], return_tensors="pt").to(model.device) generated_ids = model.generate( model_inputs.input_ids, max_new_tokens=512, do_sample=True, temperature=0.7, top_p=0.9 ) response = tokenizer.batch_decode(generated_ids[:, model_inputs.input_ids.shape[1]:], skip_special_tokens=True)[0] print(response)

这段代码里值得细看的是三个点。device_map="auto"让 accelerate 自动把模型分配到所有可见 GPU 上,单卡和多卡场景都能跑,不需要手写cuda:0torch_dtype=torch.float16把权重加载为半精度,显存占用直接减半。apply_chat_template是关键——直接用模型自带的对话模板组装 system 和 user 消息,如果手动拼模板,很容易因为格式不匹配导致模型回答风格崩坏或重复生成。

3.3 GPU 显存预估和量化方案

部署前需要预计显存是否够用,Qwen2.5-7B 的权重约 15GB(float16),加上 KV Cache 和推理中间变量,7B 模型实际需要约 16GB 到 20GB 显存。也就是说,单张 24GB 的 3090/4090 可以勉强跑,两张才舒服。

显存不够时,优先做法是加载 4-bit 量化版本。用 bitsandbytes 的load_in_4bit参数把模型量化加载,7B 模型的显存占用能降到 6GB 左右:

from transformers import BitsAndBytesConfig bnb_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_compute_dtype=torch.float16, bnb_4bit_quant_type="nf4", bnb_4bit_use_double_quant=True ) model = AutoModelForCausalLM.from_pretrained( model_path, quantization_config=bnb_config, device_map="auto", trust_remote_code=True )

这里的参数含义值得逐个理清。load_in_4bit=True表示以 4-bit 精度加载权重;bnb_4bit_compute_dtype=torch.float16指定计算时反量化到 fp16,保证运算精度不至于掉太多;bnb_4bit_quant_type="nf4"用的是 NF4 量化格式,相比普通 4-bit 整数量化在低比特下保留更多精度;bnb_4bit_use_double_quant=True对量化常量再量化一次,进一步减少显存占用。量化会让生成速度略有下降,但换来的是小显存跑大模型的能力。

3.4 把推理封装成 HTTP 服务

部署到内网供其他服务调用,需要把推理脚本封装成接口。常见做法是基于 FastAPI 起一个轻量的推理服务:

from fastapi import FastAPI, Request from pydantic import BaseModel import uvicorn, torch from transformers import AutoModelForCausalLM, AutoTokenizer app = FastAPI() model_path = "/data/models/qwen2.5-7b-instruct" tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) model = AutoModelForCausalLM.from_pretrained( model_path, torch_dtype=torch.float16, device_map="auto", trust_remote_code=True ) class Query(BaseModel): prompt: str max_new_tokens: int = 512 temperature: float = 0.7 @app.post("/generate") async def generate(query: Query): messages = [{"role": "user", "content": query.prompt}] text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) inputs = tokenizer([text], return_tensors="pt").to(model.device) with torch.no_grad(): outputs = model.generate(**inputs, max_new_tokens=query.max_new_tokens, temperature=query.temperature) response = tokenizer.batch_decode(outputs[:, inputs.input_ids.shape[1]:], skip_special_tokens=True)[0] return {"response": response} if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)

启动后可以用 curl 快速验证接口是否可用:

curl -X POST http://localhost:8000/generate \ -H "Content-Type: application/json" \ -d '{"prompt": "什么是LangChain?", "max_new_tokens": 256}'

这个最小封装已经够内网联调使用。host="0.0.0.0"表示监听所有网卡接口,内网其他机器可通过服务器 IP 访问。后面接了 langchain 后,这个接口就是 Agent 的推理后端。

提示:with torch.no_grad()在推理时要始终带着,否则显存会被梯度计算撑爆。

4. lora微调:低秩适应原理、数据集准备与单卡可跑的实战脚本

4.1 从原理上理解:为什么 LoRA 比全参微调省资源

大模型全参微调意味着更新 7B 个参数,优化器状态加梯度加上权重本身,光训练态显存就是推理态的 3 到 4 倍。LoRA 的策略是冻结原模型权重,只在注意力层的 Wq、Wv 等线性层旁边插入两个低秩矩阵 A 和 B。前向计算时输出变成原输出加 BA 的投影,训练时只更新 A 和 B。秩 r 通常取 8 到 64,参数量只有原模型的 0.1% 到 1%,7B 模型微调也只需要单张 24GB 显卡。

4.2 数据集:指令微调用的格式和构造方法

LoRA 微调的数据集一般用指令格式组织,每一条包含 instruction、input 和 output 三段。开源社区常用的格式是 Alpaca 格式的 JSON:

[ { "instruction": "解释什么是低秩适应", "input": "", "output": "低秩适应是一种参数高效微调技术,通过冻结原模型权重并训练低秩矩阵来实现领域适配。" }, { "instruction": "判断下面这段代码的时间复杂度", "input": "for i in range(n): for j in range(n): print(i*j)", "output": "O(n^2)。存在两层循环,每层循环次数为 n。" } ]

指令数据不是越多越好。我一般建议单领域 1000 条以上开始有效果,但超过 1 万条后收益会明显递减。关键是数据质量——格式统一、答案准确、覆盖真实业务场景,这三点比数量重要得多。如果只有原始文档没有问答对,可以先让模型用 few-shot 方式生成候选指令对,再做人工筛选,这是最常见的冷启动做法。

4.3 可运行的 LoRA 微调脚本(QLoRA + Qwen2.5-7B)

下面的脚本在单张 24GB 显卡上可以跑通 7B 模型的 LoRA 微调,使用 4-bit 量化加载节省显存:

import torch from datasets import load_dataset from transformers import ( AutoModelForCausalLM, AutoTokenizer, TrainingArguments, BitsAndBytesConfig ) from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training from trl import SFTTrainer # 4-bit 量化配置,减少显存占用 bnb_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_compute_dtype=torch.float16, bnb_4bit_quant_type="nf4" ) model_path = "/data/models/qwen2.5-7b-instruct" model = AutoModelForCausalLM.from_pretrained( model_path, quantization_config=bnb_config, device_map="auto", trust_remote_code=True ) tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) tokenizer.pad_token = tokenizer.eos_token # 为 4-bit 训练做准备:冻结原权重,启用梯度检查点 model = prepare_model_for_kbit_training(model) # LoRA 配置:目标模块按 Qwen 结构设置 lora_config = LoraConfig( r=16, lora_alpha=32, target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM" ) model = get_peft_model(model, lora_config) # 加载本地数据集(JSON 格式) dataset = load_dataset("json", data_files="./train_data.json", split="train") def format_instruction(example): if example["input"]: prompt = f"### 指令:\n{example['instruction']}\n\n### 输入:\n{example['input']}\n\n### 回答:\n" else: prompt = f"### 指令:\n{example['instruction']}\n\n### 回答:\n" return {"text": prompt + example["output"]} dataset = dataset.map(format_instruction) training_args = TrainingArguments( output_dir="./qwen-lora-output", per_device_train_batch_size=1, gradient_accumulation_steps=16, learning_rate=2e-4, num_train_epochs=3, logging_steps=10, save_steps=200, fp16=True, remove_unused_columns=False ) trainer = SFTTrainer( model=model, args=training_args, train_dataset=dataset, tokenizer=tokenizer, dataset_text_field="text", max_seq_length=1024 ) trainer.train() model.save_pretrained("./qwen-lora-adapter") tokenizer.save_pretrained("./qwen-lora-adapter")

这个脚本里需要特别留意的参数:r=16是 LoRA 秩,秩越高表达能力越强,但显存和过拟合风险同步上升,领域数据量不足时建议降到 8。lora_alpha=32是缩放系数,权重更新幅度和alpha / r成正比,一般设为r的 2 倍。target_modules指定插入 LoRA 的注意力层模块名,不同模型结构这个列表不一样,Qwen、Llama 的模块名都以_proj结尾,但具体命名要看model.config或模型源码。gradient_accumulation_steps=16配合per_device_train_batch_size=1达到等效 batch size 16 的效果,这是单卡训练的标准做法,用小显存模拟大 batch。

4.4 微调后如何和基础模型合并或单独加载

训练结束会生成adapter_model.safetensorsadapter_config.json。加载微调后的模型有两个路径。不想动原模型文件,就用 peft 加载 adapter 叠加在基础模型上;部署性能优先,把 adapter 和基础模型合并成一个完整模型文件再部署。合并的代码:

from peft import PeftModel from transformers import AutoModelForCausalLM, AutoTokenizer base_model_path = "/data/models/qwen2.5-7b-instruct" adapter_path = "./qwen-lora-adapter" base_model = AutoModelForCausalLM.from_pretrained( base_model_path, torch_dtype=torch.float16, device_map="auto", trust_remote_code=True ) tokenizer = AutoTokenizer.from_pretrained(base_model_path, trust_remote_code=True) model = PeftModel.from_pretrained(base_model, adapter_path) merged_model = model.merge_and_unload() merged_model.save_pretrained("./qwen2.5-7b-merged") tokenizer.save_pretrained("./qwen2.5-7b-merged")

提示:合并后的模型体积和原模型一致,部署方式和未微调模型完全相同,直接用AutoModelForCausalLM.from_pretrained加载即可。这也说明 LoRA 在生产部署上不增加任何额外负担。

5. langchain接入:用 HuggingFacePipeline 把本地模型变成 Agent 推理后端

5.1 为什么用 langchain 包一层而不是直接调接口

模型部署好之后,业务方需要的是问答、文档处理、工具调用这些能力,而不是一个裸的 HTTP 接口。langchain 提供了标准化的 Chain 和 Agent 抽象,可以把模型、提示词模板、外部工具、记忆组件串成一条流水线。这样做的直接收益是:模型可以随时替换——今天用 Qwen,明天用 Llama,上层业务代码一行不用改。

langchain 和 langgraph 的区别也值得在这里说清楚。langchain 是组件库,提供模型封装、提示词管理和工具集成;langgraph 是在其之上构建有状态、可控制循环的图执行框架,适合复杂的多步 Agent 流程。这个标题的场景用 langchain 的链式调用就够了,不需要引入 langgraph 的状态机复杂度。

5.2 用 HuggingFacePipeline 接入本地推理服务

langchain 接入方式主要分两种:直接加载本地模型走HuggingFacePipeline,或者通过HuggingFaceEndpoint调用已部署的推理服务。先看直接在 langchain 里加载本地模型的方式:

from langchain_huggingface import HuggingFacePipeline from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline import torch model_path = "/data/models/qwen2.5-7b-instruct" tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) model = AutoModelForCausalLM.from_pretrained( model_path, torch_dtype=torch.float16, device_map="auto", trust_remote_code=True ) pipe = pipeline( "text-generation", model=model, tokenizer=tokenizer, max_new_tokens=512, do_sample=True, temperature=0.7, top_p=0.9 ) llm = HuggingFacePipeline(pipeline=pipe)

如果你的模型已经封装成了 HTTP 服务,更好的方式是HuggingFaceEndpoint,这样 langchain 进程和模型推理进程解耦,模型更新不影响上层应用:

from langchain_huggingface import HuggingFaceEndpoint llm = HuggingFaceEndpoint( endpoint_url="http://localhost:8000/generate", task="text-generation", max_new_tokens=512, temperature=0.7 )

注意endpoint_url填的是第 3 章中 FastAPI 服务的地址。这里有个容易踩的坑:FastAPI 的输入格式是{"prompt": "..."},而 HuggingFaceEndpoint 默认会发送{"inputs": "..."}的 payload,两者字段名不一致会导致 422 错误。需要改造一下第 3 章的服务接口,让它兼容inputs字段,或者在 langchain 侧自定义请求体。字段兼容问题是这个环节最常见的联调失败原因。

5.3 用 LangChain 搭一个带上下文记忆的客服问答链

把 LLM 接入 Prompt 模板和记忆组件,构建一个带历史对话能力的问答链:

from langchain.memory import ConversationBufferWindowMemory from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain.chains import LLMChain prompt = ChatPromptTemplate.from_messages([ ("system", "你是一名耐心、专业的技术客服,回答问题时先给出结论再解释原因。"), MessagesPlaceholder(variable_name="history"), ("human", "{input}") ]) memory = ConversationBufferWindowMemory( k=3, return_messages=True, memory_key="history" ) chain = LLMChain( llm=llm, prompt=prompt, memory=memory, verbose=True ) # 轮对话测试 resp1 = chain.invoke({"input": "我的服务器显卡显存不够,怎么办?"}) print(resp1["text"]) resp2 = chain.invoke({"input": "那如果我用4-bit量化呢?"}) print(resp2["text"])

这段代码里,MessagesPlaceholder是对话历史注入的位置,ConversationBufferWindowMemoryk=3表示只保留最近 3 轮对话,防止上下文无限膨胀超出模型窗口。return_messages=True表示历史以消息列表形式返回,和 ChatPromptTemplate 配合时必须开启。

这里的分工很清晰:langchain 管对话流程和状态,模型只需要做好生成这一件事。替换模型时只需修改llm的初始化方式,Chain 的代码完全不动,这就是用 langchain 封装推理后端的最大优势。

5.4 本地知识库问答的常见组合

标题场景中另一个高频需求是让模型回答私有文档内容。一种常见做法是 RAG:用 embedding 模型把文档向量化存入向量库,查询时先检索相关片段再和问题一起交给 LLM 生成回答。部署在公网的 embedding API 不一定适合内网,本地可以用BAAI/bge-large-zh-v1.5这类中文 embedding 模型:

from langchain_community.embeddings import HuggingFaceBgeEmbeddings from langchain_community.vectorstores import FAISS from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_core.documents import Document # 加载本地 embedding 模型 embedding_model = HuggingFaceBgeEmbeddings( model_name="/data/models/bge-large-zh-v1.5", encode_kwargs={"normalize_embeddings": True} ) documents = [Document(page_content="LoRA 是一种低秩适应微调方法,冻结原模型……", metadata={"source": "lora_doc.txt"})] splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50) chunks = splitter.split_documents(documents) vector_store = FAISS.from_documents(chunks, embedding_model) retriever = vector_store.as_retriever(search_kwargs={"k": 4}) from langchain.chains import RetrievalQA qa_chain = RetrievalQA.from_chain_type( llm=llm, retriever=retriever, chain_type="stuff" ) response = qa_chain.invoke("LoRA 微调需要多少显存?") print(response["result"])

RecursiveCharacterTextSplitter按 500 字符切块并保留 50 字符重叠,保证跨块语义不分裂。k=4表示检索返回 4 个相关片段。这套组合的全部组件都跑在内网,数据和推理不经过任何外部服务,正好符合私有化场景的安全要求。

6. 一个真正的进阶技巧:Quantized LoRA 合并时的 shape mismatch 修复

最后落在一个实际工程中几乎必然会遇到的问题:使用 QLoRA(4-bit 量化基座)微调后合并 adapter,或者把 adapter 单独打包部署时,出现size mismatch for base_model.model.model.layers.0.self_attn.q_proj.lora_A.weight: copying a param with shape torch.Size([16, 4096]) from checkpoint, the shape in current model is torch.Size([8, 4096])。这类报错几乎都指向同一个原因:训练时指定的r和加载 adapter 时指定的r不一致,或者 target_modules 列表与训练时不匹配。lora_A.weight的形状是[r, hidden_size],r 变了形状自然对不上。

修复方法很直接:加载 adapter 时显式指定和训练时一致的LoraConfig。假设训练时用的是r=16target_modules=["q_proj","k_proj","v_proj","o_proj"],加载代码应该这样写:

from peft import PeftModel, LoraConfig from transformers import AutoModelForCausalLM, AutoTokenizer base_model_path = "/data/models/qwen2.5-7b-instruct" adapter_path = "./qwen-lora-adapter" base_model = AutoModelForCausalLM.from_pretrained( base_model_path, torch_dtype=torch.float16, device_map="auto", trust_remote_code=True ) # 加载 adapter 前先重建相同的 LoRA 配置 lora_config = LoraConfig( r=16, lora_alpha=32, target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], task_type="CAUSAL_LM" ) model = PeftModel.from_pretrained(base_model, adapter_path, config=lora_config)

另一个常见问题是训练时用的分词器带自定义特殊 token(比如加了系统提示 token),而加载环境的 tokenizer 是从 base model 重新加载的,导致词表不一致,合并后推理时出现unk_token刷屏。解决方法是把训练环境的 tokenizer 一并保存并随 adapter 一起部署,加载时优先用 adapter 目录下的 tokenizer:

tokenizer = AutoTokenizer.from_pretrained(adapter_path, trust_remote_code=True)

验证微调和部署效果最直接的方法是写一个并排对比脚本,分别加载 base model 和 merged model,输入同一组测试 prompt,对比生成结果。重点看领域术语是否准确、回答风格是否贴合业务、是否出现原模型不曾有的重复或胡言乱语。对比脚本也正好可以作为回归测试的基础,后续每次微调迭代都跑一遍,数据说话比肉眼感觉可靠得多。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 2:03:17

3个步骤解决photoshop在线报错,一文搞懂版本差异

3个步骤解决photoshop在线报错,一文搞懂版本差异 刚把旧项目代码拉下来跑,控制台直接红屏?别慌,这大概率不是你的问题,而是 Photoshop 在线版和桌面版 API 接口彻底变了。很多前端或全栈开发者在集成图像编辑功能时,习惯沿用老旧的 JSAPI 调用方式,结果发现 ps.api…

作者头像 李华
网站建设 2026/9/23 2:03:11

搞定AX88179驱动坑:一文搞懂Linux网卡实战

搞定AX88179驱动坑:一文搞懂Linux网卡实战 你是不是也遇到过这种情况?手里拿着一块AX88179芯片的USB网卡,或者内嵌在开发板里的网口,教程看了几十篇,代码复制粘贴了一堆,结果插进机器里, ip a…

作者头像 李华
网站建设 2026/9/23 2:03:06

搞定非法程序检测:从源码解析到实战避坑指南

搞定非法程序检测:从源码解析到实战避坑指南 刚接触嵌入式开发或者负责项目现场运维的朋友,是不是经常遇到这种糟心时刻?明明代码逻辑跑通了,一上真机或者在特定环境下,系统突然弹窗提示“检测到非法程序”或者“权限不足”,甚至直接闪退。配置环境就卡半天,查文档半天找不到头绪,这种挫败感真的让人想摔键盘。…

作者头像 李华
网站建设 2026/9/23 2:03:03

清洗打印机喷头步骤实战项目

5步搞定打印机喷头清洗源码逻辑,附完整示例与避坑指南 打印报错堆满屏幕,StackTrace 看不懂,直接卡死。别慌,今天拆解打印机喷头清洗的底层逻辑,给你一套可落地的完整示例。 很多开发者面对硬件交互,总觉得是黑盒。其实,无论是 Python 调用 CUPS 后端,还是 Go 语言直接操作…

作者头像 李华
网站建设 2026/9/23 2:02:59

q宠大乐斗挂性能调优避坑指南:告别卡顿与封号

q宠大乐斗挂性能调优避坑指南:告别卡顿与封号 刚学会 Python 或 Java 基础语法,看着网上那些关于《q宠大乐斗挂》的源码,心里是不是特别痒?想自己跑起来,结果一运行就卡死,或者刚上线没两分钟就被系统判定异常。这种“学会语法却不知怎么搭项目”的无力感,是每个开发者从新手迈向实战时的最大拦路虎…

作者头像 李华
网站建设 2026/9/23 2:02:53

pip什么意思避坑指南

图解原理揭秘 pip 底层机制,3 个优化技巧让依赖安装提速 50% 看了一堆教程还是不会写项目?别急着怪自己笨,很可能是环境搭建这一步就卡住了。很多新手对着 pip install 发呆,以为它只是个下载工具,结果项目一复杂,依赖冲突、安装缓慢、版本混乱接踵而至。今天不聊虚的,直接用 图解原理…

作者头像 李华