- 大模型
- 人工智能
- 教程
- 本地部署
- 微调
【免费下载链接】self-llm
《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程
本篇技术指南以 Datawhale《开源大模型食用指南》仓库中的 Qwen2.5-Coder-7B-Instruct Lora 微调文档 为核心,完整讲解如何基于transformers、peft、datasets等框架,在 Linux 环境下使用 ModelScope 下载模型并对 Qwen2.5-Coder-7B-Instruct 执行 LoRA 指令微调。读者学完后将掌握:指令数据集的构建与格式化、Qwen2.5 系列 ChatML 模板的正确拼接、LoRA 配置与训练参数的选择、Trainer 训练全流程,以及训练后 LoRA 权重的加载与推理验证,可直接复用到"角色扮演、领域问答、代码风格迁移"等个性化微调场景。
一、前置准备与依赖环境
微调本质上依赖完整的 PyTorch/CUDA 环境与本地模型部署基础。在开始前,请确保已完成基本环境配置,并在此基础上安装以下指定版本的第三方库(版本锁定有助于复现本文结果):
python -m pip install --upgrade pip pip install modelscope==1.20.0 pip install transformers==4.46.2 pip install sentencepiece==0.2.0 pip install accelerate==1.1.1 pip install datasets==3.1.0 pip install peft==0.13.2各依赖的职责如下:
modelscope:负责从 ModelScope 模型库拉取模型权重;transformers:提供模型、分词器加载与Trainer训练框架;sentencepiece:Qwen 系列分词器(Qwen2Tokenizer)依赖的底层分词库;accelerate:支撑device_map="auto"的模型加载与分布式训练;datasets:用于将 JSON 数据集转换为 HuggingFaceDataset对象;peft:提供LoraConfig、get_peft_model、PeftModel等 LoRA 核心 API。
说明:仓库同目录还提供了基于 SwanLab 的可视化微调版本,其环境清单中额外引入了
swanlab==0.3.25,可参见 05-Qwen2.5-Coder-7B-Instruct Lora 微调 SwanLab 可视化记录版.md。
二、模型下载:使用 ModelScope snapshot_download
微调的第一步是把模型权重拉到本地。仓库统一采用 ModelScope 的snapshot_download函数,第一个参数为模型名称,cache_dir参数指定模型下载路径。
在/root/autodl-tmp路径下新建model_download.py文件,输入以下内容并保存:
import torch from modelscope import snapshot_download import os model_dir = snapshot_download('Qwen/Qwen2.5-Coder-7B-Instruct', cache_dir='/root/autodl-tmp', revision='master')然后执行下载:
python /root/autodl-tmp/model_download.py模型大小约 14.18GB(仓库其他教程记录为 15GB 量级),视网络情况下载约需数分钟。revision='master'指定拉取 master 分支的最新权重。
需要特别留意ModelScope 下载目录的命名转义规则:下载后的模型目录名与模型 ID 略有差异,Qwen/Qwen2.5-Coder-7B-Instruct在本地下沉为/root/autodl-tmp/Qwen/Qwen2___5-Coder-7B-Instruct/(模型 ID 中的点.被替换为三个下划线___)。这一规则在仓库的 FastApi 部署文档 与 SwanLab 可视化版 中均有体现,后续加载模型时必须使用转义后的本地路径,否则会报路径不存在。
三、指令集构建:让模型学会"遵循指令"
LLM 的微调一般指指令微调(Instruction Tuning)。所谓指令微调,是指我们使用的微调数据形如:
{ "instruction": "回答以下用户问题,仅输出答案。", "input": "1+1等于几?", "output": "2" }三个字段的含义:
instruction:用户指令,告知模型需要完成的任务;input:用户输入,是完成指令所必需的上下文内容(可为空字符串);output:模型应当给出的标准输出。
核心训练目标是让模型具备理解并遵循用户指令的能力。因此,在构建指令集时,必须针对目标任务针对性构造。例如,本节以构建一个能模拟甄嬛对话风格的个性化 LLM 为目标,构造的指令形如:
{ "instruction": "你是谁?", "input": "", "output": "家父是大理寺少卿甄远道。" }该示例所使用的完整指令数据集即仓库根目录下的 dataset/huanhuan.json,数据以 JSON 数组形式组织,全部为"指令-输入-输出"三元组,共约 1.8 万行,内容为甄嬛传风格的中文多轮对白。例如数据集中的真实样本:
{ "instruction": "皇上驾到!", "input": "", "output": "皇上万福金安。" }仓库同目录还提供了 huanhuan-100.json(约百条规模,适合快速跑通流程)与 huanhuan.jsonl(逐行 JSON 格式)两种变体。若只做流程验证,可先用 100 条的小数据集;追求对话效果,则使用完整的huanhuan.json。
四、数据格式化:将文本编码为 input_ids 与 labels
LoRA 训练的数据需要经过格式化、编码之后再输入给模型。熟悉 PyTorch 训练流程的同学知道,我们一般将输入文本编码为input_ids,将输出文本编码为labels,编码结果都是多维向量。
首先定义一个预处理函数process_func,对每个样本编码其输入、输出文本并返回编码字典:
def process_func(example): MAX_LENGTH = 384 # 中文会被切分为多个token,因此需要放开一些最大长度,保证数据的完整性 input_ids, attention_mask, labels = [], [], [] instruction = tokenizer(f"<|im_start|>system\n现在你要扮演皇帝身边的女人--甄嬛<|im_end|>\n<|im_start|>user\n{example['instruction'] + example['input']}<|im_end|>\n<|im_start|>assistant\n", add_special_tokens=False) # add_special_tokens 不在开头加 special_tokens response = tokenizer(f"{example['output']}", add_special_tokens=False) input_ids = instruction["input_ids"] + response["input_ids"] + [tokenizer.pad_token_id] attention_mask = instruction["attention_mask"] + response["attention_mask"] + [1] # 因为eos token咱们也是要关注的所以 补充为1 labels = [-100] * len(instruction["input_ids"]) + response["input_ids"] + [tokenizer.pad_token_id] if len(input_ids) > MAX_LENGTH: # 做一个截断 input_ids = input_ids[:MAX_LENGTH] attention_mask = attention_mask[:MAX_LENGTH] labels = labels[:MAX_LENGTH] return { "input_ids": input_ids, "attention_mask": attention_mask, "labels": labels }这段函数是 LoRA 微调中最重要的"数据管线",逐行拆解如下:
MAX_LENGTH = 384:Qwen 分词器会将一个中文字切分为多个 token,因此放宽最大长度以保证数据完整性,超长样本按 384 截断;- 模板拼接:
instruction部分将 system 提示、user 指令/输入拼接成完整的 ChatML 模板文本后编码;response部分单独编码output。两段编码均使用add_special_tokens=False,避免在拼接段中间重复插入特殊 token; input_ids:由instruction + response + pad_token_id拼接而成,pad token 用于标记序列结束;attention_mask:全部置 1(含末尾的 pad 位置),因为 pad/eos token 也是模型需要关注的;labels:这是训练目标的核心——instruction 部分全部用-100掩码掉(-100是 PyTorch 交叉熵损失的忽略索引,即不计算 prompt 部分的损失),只有response部分的 token id 参与损失计算,确保模型只学习"如何作答"而不是"如何复述问题";- 截断:超长时对三个序列同步截断,保证三者长度一致。
Qwen2.5-Coder 采用的 Prompt Template(ChatML 格式)如下:
<|im_start|>system You are Qwen, created by Alibaba Cloud. You are a helpful assistant.<|im_end|> <|im_start|>user {user_prompt}<|im_end|> <|im_start|>assistant {assistant_response}<|im_end|>其中<|im_start|>与<|im_end|>是 Qwen 系列模型新增的特殊 token,用于分隔 system/user/assistant 三端。从仓库 Notebook 的运行输出可以确认,Qwen2.5-Coder 的 tokenizer 词表大小为 151643,eos_token为<|im_end|>,pad_token为<|endoftext|>,additional_special_tokens中注册了<|im_start|>、<|im_end|>以及 FIM(Fill-In-the-Middle)代码补全相关的<|fim_prefix|>、<|fim_middle|>、<|fim_suffix|>等 token——这是其作为代码模型的重要特征,微调时同样沿用这套词表。
数据集应用该预处理函数时,通过datasets的map完成批量转换并移除原字段:
from datasets import Dataset import pandas as pd df = pd.read_json('dataset/huanhuan.json') ds = Dataset.from_pandas(df) tokenized_id = ds.map(process_func, remove_columns=ds.column_names)Notebook 中实际运行tokenized_id得到num_rows: 3729(即完整数据集约 3729 条样本),并可通过tokenizer.decode验证编码结果——例如对第一条样本解码可还原出完整的<|im_start|>system\n现在你要扮演皇帝身边的女人--甄嬛...<|im_end|>模板文本,对 labels 过滤掉-100后解码可得到纯output内容,这印证了上述格式化逻辑的正确性。
五、加载 tokenizer 与半精度模型
模型以半精度形式加载,如果你的显卡比较新,可以用torch.bfloat16形式加载。对于自定义模型(如使用远程代码定义的模型),一定要指定trust_remote_code=True:
tokenizer = AutoTokenizer.from_pretrained('/root/autodl-tmp/qwen/Qwen2-7B-Instruct/', use_fast=False, trust_remote_code=True) model = AutoModelForCausalLM.from_pretrained('/root/autodl-tmp/qwen/Qwen2-7B-Instruct/', device_map="auto", torch_dtype=torch.bfloat16)需要注意两点:
- 路径务必改为上一步实际的下载目录(如
/root/autodl-tmp/Qwen/Qwen2___5-Coder-7B-Instruct/),原文档此处为笔误路径,直接照抄会报错; use_fast=False使用慢速(纯 Python)分词器,与 Qwen 官方 tokenizer 文件保持兼容。
从 Notebook 的运行输出可以观察到该模型的底层结构(Qwen2ForCausalLM):共 28 层Qwen2DecoderLayer,隐藏维度 3584,注意力部分q_proj/o_proj为 3584→3584,k_proj/v_proj为 3584→512,MLP 部分gate_proj/up_proj为 3584→18944、down_proj为 18944→3584,激活函数为 SiLU。这为下一步选择target_modules提供了直接依据。
此外,若训练时开启梯度检查点(见后文gradient_checkpointing=True),则必须额外执行model.enable_input_require_grads(),否则会因前向过程中输入无梯度而报错:
model.enable_input_require_grads() # 开启梯度检查点时,要执行该方法六、定义 LoraConfig
LoraConfig类可以设置很多参数,但主要参数不多,简单介绍如下:
task_type:模型类型,因果语言模型填TaskType.CAUSAL_LM;target_modules:需要训练的模型层名称,主要是 attention 部分与 MLP 部分的投影层;可传数组、字符串,也支持正则表达式;r:LoRA 的秩(rank),决定低秩分解矩阵的维度;lora_alpha:LoRA 的缩放超参数;lora_dropout:LoRA 分支的 Dropout 比例,用于缓解过拟合;inference_mode:是否为推理模式,训练时置False。
关于LoRA 的缩放系数:LoRA 前向的最终缩放是lora_alpha / r,而不是r本身。在下述配置中lora_alpha=32, r=8,缩放为 4 倍。
config = LoraConfig( task_type=TaskType.CAUSAL_LM, target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], inference_mode=False, # 训练模式 r=8, # Lora 秩 lora_alpha=32, # Lora alaph,具体作用参见 Lora 原理 lora_dropout=0.1 # Dropout 比例 )target_modules中 7 个模块覆盖了两类投影层:q_proj/k_proj/v_proj/o_proj是注意力四件套,gate_proj/up_proj/down_proj是 Qwen2MLP 的三层(对应上节模型结构中的Qwen2MLP),正好与 Notebook 打印出的模型结构一一对应。对这两类层都注入 LoRA 适配器,是 Qwen 系模型微调的标准做法。
将配置应用到模型,并统计可训练参数量:
from peft import LoraConfig, TaskType, get_peft_model model = get_peft_model(model, config) model.print_trainable_parameters()Notebook 中的真实输出为:
trainable params: 20,185,088 || all params: 7,635,801,600 || trainable%: 0.2643即在约 76.36 亿参数的全模型中,仅 2018 万参数可训练,可训练占比约 0.26%——这正是 LoRA 高效微调的直观体现:冻结原始权重,只训练注入的低秩矩阵,大幅降低显存占用与训练成本。
七、自定义 TrainingArguments 参数
TrainingArguments的源码中对每个参数都有说明,这里介绍几个常用参数:
output_dir:模型的输出路径(checkpoint 保存目录);per_device_train_batch_size:单卡 batch_size;gradient_accumulation_steps:梯度累加步数。显存较小时可调小 batch_size、调大梯度累加,等效扩大 batch;logging_steps:每隔多少步输出一次 log;num_train_epochs:训练轮数;save_steps:每隔多少步保存一次 checkpoint;learning_rate:学习率;save_on_each_node:多节点训练时每节点都保存;gradient_checkpointing:梯度检查点。一旦开启,模型必须执行model.enable_input_require_grads()(见第五节)。
args = TrainingArguments( output_dir="./output/Qwen2_instruct_lora", per_device_train_batch_size=4, gradient_accumulation_steps=4, logging_steps=10, num_train_epochs=3, save_steps=100, learning_rate=1e-4, save_on_each_node=True, gradient_checkpointing=True )参数组合的"等效 batch size"约为4(batch)× 4(梯度累加)= 16条样本/更新步。若显存吃紧,可将per_device_train_batch_size降至 2 并把gradient_accumulation_steps升至 8,保持等效 batch 不变——仓库的 SwanLab 可视化版 正是采用batch_size=2, gradient_accumulation_steps=8的组合在 24GB 级显卡上完成训练的。此外,开启梯度检查点后use_cache会自动置为False(Transformers 会打印相应提示),推理阶段若需要缓存加速,需另行处理。
八、使用 Trainer 训练
将模型、参数、数据集与 DataCollator 组装进Trainer并启动训练:
trainer = Trainer( model=model, args=args, train_dataset=tokenized_id, data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True), ) trainer.train()DataCollatorForSeq2Seq负责在 batch 内部将变长序列 padding 到相同长度(右 padding),保证批量前向计算可行。
从仓库 Notebook 的实际运行记录可以观察到完整的训练收敛过程:
- 训练共699 步,历时约23 分钟(
train_runtime: 1418s),吞吐约 7.9 samples/s; - 训练损失从第 10 步的3.97逐步下降到 2.1~2.4 区间,最终
training_loss ≈ 2.75(3 个 epoch 平均),loss 整体呈稳定下降趋势,未见明显发散; - 训练结束后输出
TrainOutput(global_step=699, ...),并在./output/Qwen2_instruct_lora下按save_steps生成 checkpoint 目录(如checkpoint-690)。
如果想在训练过程中监控 loss 曲线与超参数,仓库提供了基于 SwanLab 的增强版——在Trainer的callbacks参数中挂载SwanLabCallback即可自动记录指标,并支持在每个 epoch 结束时对测试样本做主观评测(详见 SwanLab 可视化微调文档)。
九、加载 LoRA 权重进行推理
训练完成后,权重保存在output_dir的 checkpoint 目录中(只包含 LoRA 适配器,体积远小于全量权重)。加载 LoRA 权重进行推理的方式如下:
from transformers import AutoModelForCausalLM, AutoTokenizer import torch from peft import PeftModel model_path = 'Qwen/Qwen2.5-Coder-7B-Instruct' lora_path = 'lora_path' # 改为你训练输出的 checkpoint 路径,如 './output/Qwen2_instruct_lora/checkpoint-690' # 加载tokenizer tokenizer = AutoTokenizer.from_pretrained(model_path) # 加载模型 model = AutoModelForCausalLM.from_pretrained(model_path, device_map="auto", torch_dtype=torch.bfloat16) # 加载lora权重 model = PeftModel.from_pretrained(model, model_id=lora_path, config=config) 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('cuda') generated_ids = model.generate( model_inputs.input_ids, max_new_tokens=512 ) generated_ids = [ output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids) ] response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] print(response)关键点说明:
PeftModel.from_pretrained在基础模型之上挂载训练好的 LoRA 适配器,model_id指向 checkpoint 目录,无需传入config也能自动读取适配器配置;- 通过
tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)将对话历史渲染成标准 ChatML 模板(模板内嵌于 tokenizer 的chat_template中),并附加生成提示符,避免手工拼接出错; model.generate采用贪婪/默认解码,max_new_tokens=512控制最大生成长度;将生成的 token 减去输入部分的长度,只解码新增内容并跳过特殊 token,即可得到干净的回复;- Notebook 中微调后实测推理输出为"我是甄嬛,家父是大理寺少卿甄远道。",说明模型已习得甄嬛人设——验证了整套微调流程的有效性。
十、微调完成后的衔接:部署与可视化
微调产出的 LoRA 权重既可像上文那样直接以 PeftModel 形式加载使用,也可与仓库中同模型的其他教程衔接成完整闭环:
- FastAPI 部署:将(合并后的)模型封装为 HTTP 服务,见 01-Qwen2.5-Coder-7B-Instruct FastApi 部署调用.md;
- WebDemo 部署:构建可交互的 Gradio 聊天界面,见 03-Qwen2.5-Coder-7B-Instruct WebDemo部署.md;
- vLLM 部署:面向高吞吐生产场景,见 04-Qwen2.5-Coder-7B-Instruct vLLM 部署调用.md;
- SwanLab 可视化训练:若希望在训练中实时观测 loss 曲线、超参数与逐 epoch 生成效果,直接参考 SwanLab 可视化记录版。
结语
本文以 models/Qwen2.5-Coder/Qwen2.5-Coder-7B-Instruct Lora 微调.md 为骨架,结合仓库中的 完整 Notebook 与 huanhuan 数据集 走通了"数据构建 → 模板格式化 → LoRA 注入 → Trainer 训练 → 权重加载推理"的完整链路。掌握这一流程后,只需替换为领域数据集(如法律问答、代码补全、角色对话),即可低成本地把 Qwen2.5-Coder-7B-Instruct 定制成面向特定任务的专属模型。
- 大模型
- 人工智能
- 教程
- 本地部署
- 微调
【免费下载链接】self-llm
《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程
相关推荐
self-llm 实战:基于 transformers 与 peft 对 Qwen2.5-Coder-7B-Instruct 进行 LoRA 微调
self llm 实战:基于 transformers 与 peft 对 Qwen2.5 Coder 7B Instruct 进行 LoRA 微调 本教程是《开
教程大模型本地部署微调Datawhale Self-LLM 实战:基于 transformers 与 peft 的 InternLM3-8B-Instruct LoRA 微调教程
Datawhale Self LLM 实战:基于 transformers 与 peft 的 InternLM3 8B Instruct LoRA 微调教程 本
大模型人工智能教程本地部署微调Datawhale self-llm 实战:Baichuan2-7B-Chat LoRA 高效微调全流程指南(transformers + peft)
Datawhale self llm 实战:Baichuan2 7B Chat LoRA 高效微调全流程指南(transformers + peft) 本篇技术
大模型人工智能教程本地部署微调
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考