基于 Self-LLM 的 DeepSeek-7B-Chat 4bits 量化 QLoRA 微调实战:6G 显存训练 7B 模型
基于 Self-LLM 的 DeepSeek-7B-Chat 4bits 量化 QLoRA 微调实战6G 显存训练 7B 模型【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调全参数/Lora、部署国内外开源大模型LLM/多模态大模型MLLM教程项目地址: https://gitcode.com/datawhalechina/self-llm本篇技术指南围绕 models/DeepSeek/05-DeepSeek-7B-chat 4bits量化 Qlora 微调.md 展开讲解如何在 Linux 环境下基于 transformers、peft、bitsandbytes 框架对 DeepSeek-7B-Chat 模型执行 4bits 量化的 QLoRA 高效微调并用 6G 显存级别的消费级显卡训练一个完整的 7B 参数模型。读完本文你将掌握 4bit 量化模型加载、QLoRA 配置、低显存训练参数调优以及微调后的模型推理验证这一整套实战技能。概述为什么选择 4bits 量化 QLoRA传统全量微调 7B 级模型对显存的要求极高普通消费级显卡难以承受。LoRALow-Rank Adaptation通过冻结原模型权重、只训练注入的低秩矩阵大幅降低了可训练参数量与显存开销而 QLoRA 更进一步将基础模型以 4bits 量化精度加载进显存使得「6G 显存训练 7B 模型」成为可能。本教程使用的方案要点如下基于transformers、peft、bitsandbytes三个核心框架完成全流程以 4bitsNF4 量化格式加载 DeepSeek-7B-Chat 基础模型加载完成后用nvidia-smi查看显存占用约5.7G只对注入的 LoRA 低秩矩阵做反向传播与参数更新原模型权重保持冻结优化器采用 QLoRA 配套的paged_adamw_32bit分页加载方案进一步压低显存峰值。配套的可运行实现见 05-DeepSeek-7B-chat 4bits量化 Qlora 微调.ipynb可与本文对照学习。若需对比不加量化的普通 LoRA 微调流程可参考同目录下的 04-DeepSeek-7B-chat Lora 微调.md。环境配置安装量化微调依赖在完成基础环境配置Python、CUDA 驱动、PyTorch与本地模型部署之后还需要安装一组经过本教程验证的第三方库版本组合如下pip install transformers4.35.2 pip install peft0.4.0 pip install datasets2.10.1 pip install accelerate0.20.3 pip install tiktoken pip install transformers_stream_generator pip install bitsandbytes0.41.1其中bitsandbytes是 QLoRA 的量化基石负责在 GPU 上执行 4bits 线性层量化与反量化计算peft提供LoraConfig、get_peft_model等低秩适配 APIaccelerate支撑device_mapauto的自动设备分配datasets用于将 JSON 指令集转换为 Dataset 对象。本教程使用的微调数据集为仓库根目录下的 dataset/huanhuan.json该数据集由约 3700 条甄嬛风格对话样本组成notebook 中处理结果显示num_rows: 3729是后续指令微调的原料。若安装依赖时网络受限可先参考 General-Setting/01-pip、conda换源.md 配置国内镜像源。指令集构建让模型学会「扮演甄嬛」LLM 的微调通常指指令微调Instruction Tuning即使用形如下方的三元组数据{ instrution:回答以下用户问题仅输出答案。, input:11等于几?, output:2 }字段语义如下instruction用户指令告知模型需要完成的任务input用户输入是完成指令所必需的输入内容output模型应当给出的标准输出。核心训练目标是让模型具备理解并遵循用户指令的能力因此指令集必须针对目标任务专门构建。本教程以「模拟甄嬛对话风格」的个性化 LLM 为目标构造的指令样本形如{ instruction: 现在你要扮演皇帝身边的女人--甄嬛, input:你是谁, output:家父是大理寺少卿甄远道。 }全部构造好的指令数据存放在 dataset/huanhuan.json 中。从实际样本看dataset/huanhuan.json多数样本的input字段为空字符串instruction直接承载完整语境output为甄嬛口吻的回答例如{ instruction: 皇上驾到, input: , output: 皇上万福金安。 }仓库中 examples/Chat-嬛嬛 目录同样围绕该对话风格数据集展开可看到同一数据集在不同模型上的复用方式。数据格式化将指令集编码为模型可读的序列LoRA 训练数据需要先经过格式化、编码再送入模型。熟悉 PyTorch 训练流程的同学知道通常需要把输入文本编码为input_ids把输出文本编码为labels编码结果都是多维向量。为此定义一个预处理函数process_func对每个样本编码输入、输出文本并返回编码后的字典def process_func(example): MAX_LENGTH 384 # Llama分词器会将一个中文字切分为多个token因此需要放开一些最大长度保证数据的完整性 input_ids, attention_mask, labels [], [], [] instruction tokenizer(fUser: {example[instruction]example[input]}\n\n, add_special_tokensFalse) # add_special_tokens 不在开头加 special_tokens response tokenizer(fAssistant: {example[output]}end▁of▁sentence, add_special_tokensFalse) 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 }该格式化逻辑参考了 DeepSeek 官方仓库 README 中给出的对话模板User: {messages[0][content]} Assistant: {messages[1][content]}end▁of▁sentenceUser: {messages[2][content]} Assistant:几个关键细节输入侧拼接为User: ...前缀输出侧拼接为Assistant: ...前缀并以 DeepSeek 的结束符end▁of▁sentence收尾保持与官方对话格式一致add_special_tokensFalse表示不在开头额外添加 special tokenlabels中输入部分User:段全部置为-100PyTorch 交叉熵损失会忽略该值只有Assistant:输出段参与损失计算序列末尾补充pad_token_id并将对应attention_mask位置补1保证长度对齐MAX_LENGTH 384用于截断超长样本因为 DeepSeek 使用的 Llama 系分词器会把一个中文字切分为多个 token需要放宽最大长度以保留数据完整性。在 notebook 中加载数据集并应用该函数的过程为from datasets import Dataset import pandas as pd df pd.read_json(../../data/huanhuan.json) ds Dataset.from_pandas(df) tokenized_id ds.map(process_func, remove_columnsds.column_names)处理完成后tokenized_id的特征列为[input_ids, attention_mask, labels]共 3729 行。用tokenizer.decode可以验证格式化结果例如解码某样本得到User: 小姐别的秀女都在求中选唯有咱们小姐想被撂牌子菩萨一定记得真真儿的——\n\nAssistant: 嘘——都说许愿说破是不灵的。end▁of▁sentenceend▁of▁sentence过滤掉-100后单独解码labels则能还原出仅含 Assistant 回复的训练目标Assistant: 你们俩话太多了我该和温太医要一剂药好好治治你们。end▁of▁sentenceend▁of▁sentence加载 Tokenizer 与 4bit 量化模型模型以 4bits 精度加载可大幅压缩显存占用。如果你的显卡较新也可以改用torch.bfloat16作为计算精度。对于 DeepSeek 这类自定义代码模型加载时必须指定trust_remote_codeTrue。tokenizer AutoTokenizer.from_pretrained(./deepseek-ai/deepseek-llm-7b-chat/, use_fastFalse, trust_remote_codeTrue) tokenizer.padding_side right # padding在右边 model AutoModelForCausalLM.from_pretrained( /root/model/deepseek-ai/deepseek-llm-7b-chat/, trust_remote_codeTrue, torch_dtypetorch.half, device_mapauto, low_cpu_mem_usageTrue, # 是否使用低CPU内存 load_in_4bitTrue, # 是否在4位精度下加载模型。如果设置为True则在4位精度下加载模型。 bnb_4bit_compute_dtypetorch.half, # 4位精度计算的数据类型。这里设置为torch.half表示使用半精度浮点数。 bnb_4bit_quant_typenf4, # 4位精度量化的类型。这里设置为nf4表示使用nf4量化类型。 bnb_4bit_use_double_quantTrue # 是否使用双精度量化。如果设置为True则使用双精度量化。 ) model.generation_config GenerationConfig.from_pretrained(/root/model/deepseek-ai/deepseek-llm-7b-chat/) model.generation_config.pad_token_id model.generation_config.eos_token_id4bit 加载相关参数的作用如下参数取值作用load_in_4bitTrue以 4bits 精度加载模型是 QLoRA 的核心开关bnb_4bit_compute_dtypetorch.half4bits 权重参与计算时反量化到的计算精度通常设为半精度bnb_4bit_quant_typenf44bits 量化格式nf4Normal Float 4是 bitsandbytes 针对正态分布权重设计的 4bit 量化格式精度优于传统的 int4bnb_4bit_use_double_quantTrue双重量化即对量化缩放因子再做一次量化进一步压缩显存占用device_mapauto由 accelerate 自动将各层分配到可用设备low_cpu_mem_usageTrue降低加载过程中的 CPU 内存峰值加载完成后可以在终端用nvidia-smi确认显存占用本教程实践观察约5.7G左右。从 notebook 输出的模型结构也可以印证量化确实生效模型各线性层均显示为Linear4bit例如(self_attn): LlamaAttention( (q_proj): Linear4bit(in_features4096, out_features4096, biasFalse) (k_proj): Linear4bit(in_features4096, out_features4096, biasFalse) (v_proj): Linear4bit(in_features4096, out_features4096, biasFalse) (o_proj): Linear4bit(in_features4096, out_features4096, biasFalse) ) (mlp): LlamaMLP( (gate_proj): Linear4bit(in_features4096, out_features11008, biasFalse) (up_proj): Linear4bit(in_features4096, out_features11008, biasFalse) (down_proj): Linear4bit(in_features11008, out_features4096, biasFalse) ... )可见 DeepSeek-7B-Chat 的注意力四投影q/k/v/o与 MLP 三个投影gate/up/down全部被替换为Linear4bit这正是显存被压到 6G 量级的直接原因。定义 LoraConfig注入低秩适配器LoraConfig是 peft 中配置低秩适配的核心类可设置参数较多但核心参数并不多task_type模型任务类型因果语言模型填TaskType.CAUSAL_LMtarget_modules需要注入 LoRA 的层名主要是 attention 部分不同模型层名不同可传数组、字符串或正则表达式rLoRA 的秩ranklora_alphaLoRA 的缩放系数lora_dropoutLoRA 层 dropout 比例。需要特别澄清的是LoRA 的实际缩放比例是lora_alpha / r而不是r本身。下面配置中lora_alpha32、r8缩放即为 4 倍。config LoraConfig( task_typeTaskType.CAUSAL_LM, target_modules[q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj], inference_modeFalse, # 训练模式 r8, # Lora 秩 lora_alpha32, # Lora alaph具体作用参见 Lora 原理 lora_dropout0.1# Dropout 比例 )需要说明的是配套 notebook 中target_modules实际使用的是[q_proj, k_proj, v_proj, o_proj]即只对 attention 四个投影注入 LoRA而本教程正文给出的是同时覆盖 MLP 三个投影gate_proj、up_proj、down_proj的扩展版本。从训练成本角度推断覆盖 MLP 层会增加可训练参数量与计算开销但对模型表达能力可能有一定增益读者可根据显存余量自行取舍。对应地notebook 中 4 模块配置下调用get_peft_model后的可训练参数统计为trainable params: 7,864,320 || all params: 3,882,602,496 || trainable%: 0.20255279823525874即在约 38.8 亿参数含 4bit 量化基座的模型中仅约 786 万参数参与训练训练比例仅约0.20%这正是 LoRA 高效微调的直接量化证据。在正式训练前还需注意若开启梯度检查点gradient_checkpointingTrue必须执行model.enable_input_require_grads()model get_peft_model(model, config) model.enable_input_require_grads() # 开启梯度检查点时要执行该方法 model.print_trainable_parameters()自定义 TrainingArguments为低显存场景调参TrainingArguments的源码对每个参数都有说明这里介绍几个训练中最常用的output_dir模型输出目录per_device_train_batch_size单卡 batch sizegradient_accumulation_steps梯度累加步数显存较小时可调小 batch size、增大梯度累加logging_steps每隔多少步输出一次日志num_train_epochs训练轮数epochsave_steps每隔多少步保存一次 checkpointlearning_rate学习率gradient_checkpointing梯度检查点开启后以计算换显存可显著降低激活值占用但必须配合model.enable_input_require_grads()使用optimpaged_adamw_32bit使用 QLoRA 的分页优化器当显存不足时将优化器状态换出到 CPU 内存从而避免 OOM。args TrainingArguments( output_dir./output/DeepSeek, per_device_train_batch_size8, gradient_accumulation_steps2, logging_steps10, num_train_epochs3, save_steps100, learning_rate1e-4, save_on_each_nodeTrue, gradient_checkpointingTrue, optimpaged_adamw_32bit # 优化器类型 )显存适配说明上方正文配置batch_size8、梯度累加 2适合显存较充裕的场景而 notebook 中为了演示低显存训练实际采用了per_device_train_batch_size1、gradient_accumulation_steps1的更保守组合配合 4bit 量化与梯度检查点即可在约 6G 显存条件下完成 7B 模型的训练。两种配置的其余超参数保持一致读者可根据自身显卡显存灵活调整。使用 Trainer 训练数据、模型与训练参数就绪后使用 transformers 的Trainer启动训练并用DataCollatorForSeq2Seq对批次内序列做动态 paddingtrainer Trainer( modelmodel, argsargs, train_datasettokenized_id, data_collatorDataCollatorForSeq2Seq(tokenizertokenizer, paddingTrue), ) trainer.train()几点补充说明DataCollatorForSeq2Seq(tokenizertokenizer, paddingTrue)会在每个 batch 内部按最长序列动态补齐pad_token_id相比固定长度 padding 更省显存由于TrainingArguments中设置了save_steps自动保存策略训练结束后并不需要手动保存模型checkpoint 会自动写入output_dir整个训练集为 3729 条样本3 个 epoch 下迭代步数有限适合在消费级显卡上一次跑通全流程。模型推理与效果验证训练完成后用经典方式做一次推理验证输入一句甄嬛风格的台词观察模型回复text 小姐别的秀女都在求中选唯有咱们小姐想被撂牌子菩萨一定记得真真儿的—— inputs tokenizer(fUser: {text}\n\n, return_tensorspt) outputs model.generate(**inputs.to(model.device), max_new_tokens100) result tokenizer.decode(outputs[0], skip_special_tokensTrue) print(result)本教程的推理结果如下可见即使是 4bits 量化的微调模型对话风格还原效果依然不错User: 小姐别的秀女都在求中选唯有咱们小姐想被撂牌子菩萨一定记得真真儿的—— Assistant: 姐姐你别说了我自有打算。推理时需注意保持与训练一致的对话模板前缀User: ...并设置max_new_tokens控制生成长度skip_special_tokensTrue用于在解码时剔除end▁of▁sentence等特殊 token。小结本文完整复现了基于 models/DeepSeek/05-DeepSeek-7B-chat 4bits量化 Qlora 微调.md 的 DeepSeek-7B-Chat QLoRA 微调全流程从bitsandbytes等依赖安装、甄嬛指令集构建与格式化到 4bits NF4 量化模型加载、LoraConfig注入低秩适配器、TrainingArguments低显存调参再到Trainer训练与推理验证。核心要点回顾4bits 量化load_in_4bitbnb_4bit_quant_typenf4 双重量化将 7B 模型的显存占用压到约 5.7G使消费级显卡训练成为可能LoRA 只训练约 0.20% 的参数配合paged_adamw_32bit分页优化器与梯度检查点进一步控制显存峰值数据格式必须与 DeepSeek 官方User:/Assistant:对话模板保持一致labels中仅 Assistant 段参与损失计算配套完整实现可直接参考 05-DeepSeek-7B-chat 4bits量化 Qlora 微调.ipynb数据与更多示例见 dataset/huanhuan.json 与 examples/Chat-嬛嬛。【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调全参数/Lora、部署国内外开源大模型LLM/多模态大模型MLLM教程项目地址: https://gitcode.com/datawhalechina/self-llm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →