news 2026/9/12 14:23:12

开源大模型食用指南:基于 transformers 与 peft 对 DeepSeek-7B-Chat 进行 LoRA 指令微调实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
开源大模型食用指南:基于 transformers 与 peft 对 DeepSeek-7B-Chat 进行 LoRA 指令微调实战

开源大模型食用指南:基于 transformers 与 peft 对 DeepSeek-7B-Chat 进行 LoRA 指令微调实战

【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm

本教程是《开源大模型食用指南》(self-llm)中 DeepSeek 模型系列的第 4 篇实战指南,讲解如何基于transformerspeft等主流框架,在 Linux 环境下对 DeepSeek-7B-Chat 开源对话模型进行LoRA(Low-Rank Adaptation)指令微调。通篇以"复现一个会模仿《甄嬛传》甄嬛语气的个性化聊天模型"为落地案例,读者学完后将掌握:指令集构建与格式化、半精度模型加载、LoRA 配置、训练参数调优、Trainer 训练与推理验证的完整闭环,并能在个人显卡上低成本复现同类角色扮演类大模型。

概述:为什么选择 LoRA 微调 DeepSeek-7B-Chat

DeepSeek-7B-Chat 是由 70 亿参数组成的中英文对话模型,在包含 2 万亿 token 的海量语料上训练而来(详见同目录 01-DeepSeek-7B-chat FastApi.md 中的介绍)。通用对话模型在开放领域表现优秀,但面对"固定角色人设""特定领域问答"等垂直任务时,直接使用的效果往往不够理想,此时就需要微调来让模型对齐目标任务。

微调通常分为两类:

  • 全参数微调:更新模型全部权重,效果好但显存开销巨大,7B 模型往往需要多卡甚至 80G+ 显存;
  • 高效微调(PEFT):冻结大部分参数,只训练少量新增参数,其中LoRA是最主流的方法。

LoRA 的核心思想是:在冻结的预训练权重旁注入低秩分解矩阵(AB两个小矩阵),训练时只更新这两个小矩阵,推理时再将增量合并回原权重。由于训练参数量骤降,普通消费级显卡也能轻松微调大模型。深入了解 LoRA 原理可参阅相关公开论文与技术博客(此处不展开外部链接)。

本教程在 04-DeepSeek-7B-chat Lora 微调.ipynb 中提供了与本文完全对应的 Notebook,建议对照学习:Notebook 按"导入环境 → 处理数据集 → 创建模型 → 定义 LoRA → 配置训练参数 → 训练 → 推理"的顺序组织,每一步都有可运行的代码单元。

环境配置

在完成基本环境配置(Python、CUDA、PyTorch 已就绪)和本地模型部署的前提下,需要安装以下第三方库:

pip install transformers==4.35.2 pip install peft==0.4.0 pip install datasets==2.10.1 pip install accelerate==0.20.3 pip install tiktoken pip install transformers_stream_generator

版本说明:

依赖库版本用途
transformers4.35.2加载模型、分词器,提供 Trainer 训练框架
peft0.4.0提供LoraConfigget_peft_model等高效微调接口
datasets2.10.1将 JSON 数据封装为 Dataset 并做批量映射预处理
accelerate0.20.3分布式/单机多卡训练加速,支撑device_map="auto"自动设备分配
tiktoken-DeepSeek 官方分词所需的 BPE 编码依赖
transformers_stream_generator-流式生成支持(部分场景依赖)

提示:本文依赖版本为教程撰写时的验证组合。若你的环境已装有更高版本,建议在独立 conda 虚拟环境中按上述版本安装,避免版本冲突;若显卡较新(如 30/40 系及以上),也可在后续加载模型时改用torch.bfloat16

本节教程使用的微调数据集位于仓库根目录 dataset/huanhuan.json,这是基于《甄嬛传》剧本构建的甄嬛角色对话指令集。

指令集构建

LLM 的微调一般指指令微调(Instruction Tuning),即使用"指令-输入-输出"三元组形式的数据训练模型,让模型学会遵循指令完成任务。单条数据形如:

{ "instruction": "回答以下用户问题,仅输出答案。", "input": "1+1等于几?", "output": "2" }

字段含义:

  • instruction:用户指令,告知模型需要完成的任务;
  • input:用户输入,是完成指令所必须的输入内容(可留空);
  • output:模型应当给出的标准输出。

核心训练目标是让模型具备理解并遵循用户指令的能力。因此在构建指令集时,应围绕目标任务针对性构造。本节以合作开源的Chat-甄嬛项目(仓库内示例见 examples/Chat-嬛嬛/readme.md)为示例,目标是构建一个能够模拟甄嬛对话风格的个性化 LLM,构造的指令形如:

{ "instruction": "现在你要扮演皇帝身边的女人--甄嬛", "input": "你是谁?", "output": "家父是大理寺少卿甄远道。" }

实际存放在 dataset/huanhuan.json 中的甄嬛语料即为这种格式,例如:

{ "instruction": "皇上驾到!", "input": "", "output": "皇上万福金安。" }

整份数据集包含上万条此类"对话上下文 → 甄嬛台词"的问答对,训练目标就是让模型在看到对应语境时,能以甄嬛的语气与用词习惯作答。

拓展:如何从剧本原始文本得到这样的指令集?参照 examples/Chat-嬛嬛/readme.md 的 Step 2 可知,通用思路是:① 从原始剧本/小说文本中提取出"角色 + 台词"对;② 筛选出关注角色的对话;③ 将对话整理成{"instruction", "input", "output"}的 JSON 格式。若原始文本是《西游记白话文》这类叙述体,还可借助大模型从段落中抽取角色对话。

数据格式化

LoRA 训练的数据需要经过格式化、编码之后再输入给模型。熟悉 PyTorch 训练流程的同学知道,文本需要编码为input_ids,输出文本编码为labels,编码结果都是多维向量。我们定义预处理函数process_func,对每个样本编码输入、输出文本并返回编码后的字典:

def process_func(example): MAX_LENGTH = 384 # Llama分词器会将一个中文字切分为多个token,因此需要放开一些最大长度,保证数据的完整性 input_ids, attention_mask, labels = [], [], [] instruction = tokenizer(f"User: {example['instruction']+example['input']}\n\n", add_special_tokens=False) # add_special_tokens 不在开头加 special_tokens response = tokenizer(f"Assistant: {example['output']}<|end▁of▁sentence|>", 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 }

对这段预处理逻辑做逐项拆解:

  1. 对话模板User: {instruction+input}\n\nAssistant: {output}<|end▁of▁sentence|>分别构成输入与回答的格式。这里<|end▁of▁sentence|>是 DeepSeek 的结束符(EOS token)Assistant:前缀让模型在推理时"接话"。
  2. add_special_tokens=False:编码 User/Assistant 片段时不额外添加特殊 token,避免重复。
  3. attention_mask:输入片段与回答片段的 mask 拼接后,末尾再补1——因为 EOS token 也是模型需要关注的,不能置 0。
  4. labels:输入部分(User: ...)用-100填充(PyTorch 交叉熵损失会忽略该值,即不计算输入部分的损失,只监督回答部分);回答部分保留真实 token id;末尾补pad_token_id。这正是"只让模型学会输出答案"的关键。
  5. 截断:超过MAX_LENGTH=384的序列直接截断。由于中文在 BPE 分词下可能被切分为多个 token,因此长度需适当放宽以保证数据完整性。

上述对话格式参考了 DeepSeek 官方仓库 README 中的指令介绍,官方推荐的对话格式为:

User: {messages[0]['content']} Assistant: {messages[1]['content']}<|end▁of▁sentence|>User: {messages[2]['content']} Assistant:

即:历史多轮对话按User:/Assistant:交替排列,每轮回答后跟结束符,最后以Assistant:结尾等待模型补全。

数据集的加载与映射

在 Notebook 中,数据加载与格式化的完整流程如下(对应 Notebook "导入环境"与"处理数据集"两节):

from datasets import Dataset import pandas as pd from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer, GenerationConfig # 将 JSON 文件转换为 Dataset df = pd.read_json('./huanhuan.json') ds = Dataset.from_pandas(df) ds[:3] # 预览前 3 条样本 # 加载 tokenizer 并对全量数据集应用 process_func tokenizer = AutoTokenizer.from_pretrained('./deepseek-ai/deepseek-llm-7b-chat/', use_fast=False, trust_remote_code=True) tokenizer.padding_side = 'right' tokenized_id = ds.map(process_func, remove_columns=ds.column_names)

ds.map会逐样本调用process_func,并用remove_columns移除原始文本列,只保留input_ids/attention_mask/labels。格式化完成后,可通过 decode 反向验证数据是否正确:

# 查看第一条样本的 input_ids 还原文本 tokenizer.decode(tokenized_id[0]['input_ids']) # 过滤掉 -100 后,还原 labels 对应的回答文本 tokenizer.decode(list(filter(lambda x: x != -100, tokenized_id[1]["labels"])))

这两条验证命令分别确认"输入侧格式正确"与"标签侧只包含 Assistant 回答",是训练前排查数据问题的有效手段。

加载 tokenizer 和半精度模型

模型以**半精度(torch.half,即 FP16)**形式加载以节省显存;如果显卡较新(如 Ampere 架构及以上),可以用torch.bfloat16加载以获得更好的数值稳定性。对于 DeepSeek 这类使用远程代码(trust_remote_code)的自定义模型,必须trust_remote_code设为True

tokenizer = AutoTokenizer.from_pretrained('./deepseek-ai/deepseek-llm-7b-chat/', use_fast=False, trust_remote_code=True) tokenizer.padding_side = 'right' # padding在右边 model = AutoModelForCausalLM.from_pretrained('./deepseek-ai/deepseek-llm-7b-chat/', trust_remote_code=True, torch_dtype=torch.half, device_map="auto") model.generation_config = GenerationConfig.from_pretrained('./deepseek-ai/deepseek-llm-7b-chat/') model.generation_config.pad_token_id = model.generation_config.eos_token_id

几个关键点:

  • use_fast=False:使用原始(非 Rust 加速版)分词器,兼容 DeepSeek 自定义分词实现;
  • padding_side = 'right':padding 对齐方向设为右侧,避免影响因果语言模型的注意力遮蔽;
  • device_map="auto":由 accelerate 自动把模型各层分配到可用设备(GPU/CPU),简化多卡与显存管理;
  • torch_dtype=torch.half:以 FP16 半精度加载权重,显存约为全精度的一半;
  • 手动设置pad_token_id = eos_token_id:DeepSeek 没有独立 pad token,用 EOS token 兼任,保证 batch 内 padding 合法。

加载完成后,还需要执行(配合梯度检查点使用):

model.enable_input_require_grads() # 开启梯度检查点时,要执行该方法

该方法让输入层也保留梯度路径,是gradient_checkpointing=True生效的必要前置条件(对应 Notebook "创建模型"一节)。

定义 LoraConfig

LoraConfig可以设置很多参数,核心参数如下:

  • task_type:模型类型,此处为因果语言建模任务TaskType.CAUSAL_LM
  • target_modules:需要注入 LoRA 的模型层名字,主要是 attention 部分(也可含 MLP 的投影层)。不同模型层命名不同,可以传数组、字符串,甚至正则表达式;
  • r:LoRA 的秩(rank),决定低秩矩阵的维度,是 LoRA 原理中的核心超参数;
  • lora_alpha:LoRA 缩放系数,与r共同决定实际缩放比例;
  • lora_dropout:LoRA 分支的 Dropout 比例,用于防止过拟合。

一个常见的误区是认为缩放因子是r(秩),实际上 LoRA 的缩放为lora_alpha / r。例如下面配置中32 / 8 = 4,即 LoRA 增量被放大 4 倍:

from peft import LoraConfig, TaskType, get_peft_model 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 比例 ) model = get_peft_model(model, config) model.print_trainable_parameters() # 打印可训练参数数量

target_modules中 7 个模块覆盖了 DeepSeek(基于 LLaMA 架构)的完整线性层集合:attention 的q/k/v/o_proj与 MLP 的gate/up/down_proj,这样 LoRA 既能调整注意力交互,也能调整前馈网络的知识映射。调用get_peft_model后模型即被包装为 PEFT 模型,print_trainable_parameters()会输出可训练参数占比——在本配置(r=8,7 个模块)下,可训练参数通常仅占全模型的 1% 以内,这正是 LoRA 高效的原因。

自定义 TrainingArguments 参数

TrainingArguments的源码对每个参数都有详细注释,这里说明常用参数:

  • output_dir:模型 checkpoint 的输出路径;
  • per_device_train_batch_size:单卡 batch size;
  • gradient_accumulation_steps:梯度累加步数。显存较小时可调小batch_size、增大梯度累加,等效扩大 batch;
  • logging_steps:每隔多少步输出一次训练日志;
  • num_train_epochs:训练轮数;
  • save_steps:每隔多少步保存一次 checkpoint;
  • learning_rate:学习率;
  • save_on_each_node:多节点训练时每节点都保存;
  • gradient_checkpointing:梯度检查点,开启后以少量计算换显存,但必须配合model.enable_input_require_grads()使用。
args = TrainingArguments( output_dir="./output/DeepSeek", per_device_train_batch_size=8, gradient_accumulation_steps=2, logging_steps=10, num_train_epochs=3, save_steps=100, learning_rate=1e-4, save_on_each_node=True, gradient_checkpointing=True )

参数组合解读:

  • 有效 batch size =8 × 2 = 16,兼顾了收敛速度与显存占用;
  • gradient_checkpointing=True是 7B 模型在单卡 24G 显存下跑通 LoRA 的关键;
  • learning_rate=1e-4是 LoRA 微调的常用量级(远小于全量微调的 1e-5 量级,因为只更新少量低秩参数);
  • save_steps=100配合output_dir会在每 100 步落盘 checkpoint,训练中断可断点续训。

进阶提示:若显存更紧张,可参考同目录 05-DeepSeek-7B-chat 4bits量化 Qlora 微调.md 的 QLoRA 方案——以 4bit 量化加载基座模型,并将optim设为"paged_adamw_32bit",用 6G 显存即可训练 7B 模型。

使用 Trainer 训练

训练阶段使用 HuggingFaceTrainer,传入 PEFT 模型、训练参数、已格式化的数据集与序列到序列的 DataCollator:

trainer = Trainer( model=model, args=args, train_dataset=tokenized_id, data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True), ) trainer.train()

要点:

  • data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True)会在每个 batch 内做动态 padding,将不同长度的样本补齐到 batch 内最长长度,避免全量 padding 浪费算力;
  • 由于labels-100会被损失函数自动忽略,即使input_ids中混入了 pad token,也不会影响回答部分梯度的正确性;
  • trainer.train()启动训练后,日志会按logging_steps=10输出 loss,checkpoint 按save_steps=100保存到output_dir

训练完成后,模型与 LoRA 适配器权重即保存在./output/DeepSeek下,可用于后续推理或加载部署。

模型推理

训练结束后,可以用经典的生成方式直接验证微调效果(此时model仍是内存中的 PeftModel):

text = "小姐,别的秀女都在求中选,唯有咱们小姐想被撂牌子,菩萨一定记得真真儿的——" inputs = tokenizer(f"User: {text}\n\n", return_tensors="pt") outputs = model.generate(**inputs.to(model.device), max_new_tokens=100) result = tokenizer.decode(outputs[0], skip_special_tokens=True) print(result)

推理结果(教程实测效果,微调后模型已具备甄嬛语气):

User: 小姐,别的秀女都在求中选,唯有咱们小姐想被撂牌子,菩萨一定记得真真儿的—— Assistant: 菩萨也会看错眼的时候。

可以看到,模型不仅理解了输入语境,还以符合甄嬛人设的口吻作答。max_new_tokens=100限制生成长度,skip_special_tokens=True会在解码时剔除<|end▁of▁sentence|>等特殊 token。

若训练过程中断或需要从 checkpoint 恢复推理,可先用基座模型加载 LoRA 适配器:

from peft import PeftModel base_model = AutoModelForCausalLM.from_pretrained('./deepseek-ai/deepseek-llm-7b-chat/', trust_remote_code=True, torch_dtype=torch.half, device_map="auto") model = PeftModel.from_pretrained(base_model, './output/DeepSeek/checkpoint-xxx') # 替换为实际 checkpoint 路径

说明:完整复现以上案例后,将本文的 LoRA 流程迁移到其他角色/领域时,只需替换dataset/huanhuan.json为同格式的自有指令集、调整process_func中的系统提示词即可;将微调基座换成同架构模型(如 LLaMA 系列)时,仅需同步修改target_modules与对话模板。更多端到端案例可参考 examples/Chat-嬛嬛/train.py(基于 Llama-3.1-8B 的同类实现)与 examples/Chat-嬛嬛/readme.md(完整数据加工流程说明)。

总结与进阶路径

至此,我们完成了 DeepSeek-7B-Chat LoRA 微调的完整闭环:指令集构建 → 数据格式化与验证 → 半精度模型加载 → LoRA 配置 → 训练参数设定 → Trainer 训练 → 推理验证。这套流程同样适用于仓库内其他模型的 LoRA 教程(如 LLaMA3、Qwen 等),核心差异仅在于对话模板与target_modules的层命名。

进阶学习路径:

  • 若显存有限,推荐阅读 05-DeepSeek-7B-chat 4bits量化 Qlora 微调.md,掌握 4bit 量化 + LoRA 的组合方案(6G 显存可训 7B 模型);
  • 若想将微调产物落地为服务,可参照 01-DeepSeek-7B-chat FastApi.md 将模型封装为 API;
  • 微调后的 LoRA 权重也可通过 models/DeepSeek 目录下的部署教程加载使用。

【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

Agent记忆处理机制:结构化、可演化、上下文感知的认知存档系统

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 14:21:48

四自由度SCARA机器人MATLAB轨迹规划仿真完整实践指南

做SCARA机器人轨迹规划仿真这事&#xff0c;很多人一开始拿到MATLAB就懵&#xff1a;明明文档里全是函数&#xff0c;可真到自己搭一个四自由度模型&#xff0c;却连DH参数都填不对。这篇文章不打算复述官方手册&#xff0c;而是把从建模到轨迹规划、再到仿真踩坑的完整过程摆出…

作者头像 李华
网站建设 2026/9/12 14:21:10

CMSIS-4不是标准,而是2013年封存的嵌入式工程契约

1. CMSIS-4不是“标准”&#xff0c;而是一套被时间封印的工程契约 CMSIS-4这个名词&#xff0c;今天在很多嵌入式工程师简历里、技术方案PPT中、甚至招聘JD里&#xff0c;依然带着一种“权威认证”的光泽。但如果你真把它当标准去用&#xff0c;尤其是想在新项目里直接拉进来跑…

作者头像 李华
网站建设 2026/9/12 14:20:24

Java技术栈在AI中台架构中的实践与优化

1. 企业智能化转型的痛点与破局点Java技术栈在企业级应用中占据主导地位&#xff0c;但传统Java架构在AI时代面临三大核心矛盾&#xff1a;首先是单体架构与AI算力需求的矛盾&#xff0c;传统Java EE架构难以支撑深度学习模型的高并发推理&#xff1b;其次是开发效率与AI复杂度…

作者头像 李华
网站建设 2026/9/12 14:17:13

在MuJoCo中实现PPO:从环境安装到调参的完整指南

简介&#xff1a;面向希望在Mujoco物理仿真环境中实践强化学习的开发者&#xff0c;这份资源提供了基于PyTorch的PPO算法实现&#xff0c;覆盖Ant-v2、Humanoid-v2、Hopper-v2、HalfCheetah-v2等常见连续控制任务。压缩包共13个文件&#xff0c;大小仅598KB&#xff0c;包含4个…

作者头像 李华