当前位置: 首页 > news >正文

大模型微调实战:QLoRA 单 GPU 微调 Llama 3 专属模型,显存 16GB 就能跑

本文为原创技术分享,仅用于技术学习交流。

适用场景:个人开发者、学生、算法爱好者想微调专属大模型,但显卡显存不够(只有 16GB),QLoRA 技术正好解决这个痛点,本文亲测可跑。

一、痛点场景:大模型微调,你是否也卡在这里?

现在 Llama 3 开源了,大家都想微调一个自己的专属模型,比如:

  • 微调一个专属写作助手

  • 把自己的领域知识灌进去

  • 做一个垂直领域的问答机器人

但是全参数微调一下就要几十 GB 显存,普通人哪有这么好的显卡?A100 一天租金都要好几十,个人玩家玩不起。

QLoRA 论文出来说 4-bit 量化 + LoRA 可以达到全微调接近的效果,还能省很多显存。网上都说好,但真正从环境配置到训练完成的 step by step 教程不多,很多人跟着教程走还是会卡。

这篇文章就带大家用 16GB 显存微调 Llama 3 8B,从环境配置到训练脚本,踩过的坑都给你写清楚。

二、最终效果展示

我用 16GB 显存的 RTX 3080 成功跑完了微调:

项目

参数

基础模型

Llama 3 8B Instruct

量化

4-bit NF4

训练数据

1k 条中文对话样本

Batch size

4

显存占用

~14GB

训练时间

~3 小时

训练完之后,模型能够学到你给的对话风格和领域知识,效果接近全参数微调,但是显存只用了 16GB,普通消费级显卡就能跑。

三、实战代码:完整可运行

环境依赖安装

pip install torch transformers accelerate peft bitsandbytes huggingface_hub

pip install git+https://github.com/huggingface/trl.git

#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ QLoRA 微调 Llama 3 示例 显存要求: 16GB 足够 """ import torch from datasets import load_dataset from transformers import ( AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, TrainingArguments, pipeline, ) from peft import LoraConfig, PeftModel, get_peft_model from trl import SFTTrainer # ============ 配置 ============ MODEL_NAME = "meta-llama/Meta-Llama-3-8B-Instruct" DATASET_PATH = "your_dataset.jsonl" # 你的数据集,每条一个 "text" 字段 OUTPUT_DIR = "./llama3-8b-qlora" # QLoRA 配置 LORA_R = 8 LORA_ALPHA = 16 LORA_DROPOUT = 0.05 LORA_TARGET_MODULES = ["q_proj", "v_proj"] # 4-bit 量化配置 BNB_4BIT = True BNB_4BIT_USE_DOUBLE_QUANTIZATION = True BNB_4BIT_QUANT_TYPE = "nf4" # 训练参数 BATCH_SIZE = 4 GRADIENT_ACCUMULATION_STEPS = 4 LEARNING_RATE = 2e-4 NUM_EPOCHS = 3 MAX_SEQ_LENGTH = 512 def main(): # ============ 4-bit 量化配置 ============ bnb_config = BitsAndBytesConfig( load_in_4bit=BNB_4BIT, bnb_4bit_use_double_quant=BNB_4BIT_USE_DOUBLE_QUANTIZATION, bnb_4bit_quant_type=BNB_4BIT_QUANT_TYPE, ) # ============ 加载模型和分词器 ============ model = AutoModelForCausalLM.from_pretrained( MODEL_NAME, quantization_config=bnb_config, device_map="auto", trust_remote_code=True, ) tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME) tokenizer.pad_token = tokenizer.eos_token tokenizer.padding_side = "right" # 避免警告 # ============ LoRA 配置 ============ lora_config = LoraConfig( r=LORA_R, lora_alpha=LORA_ALPHA, target_modules=LORA_TARGET_MODULES, lora_dropout=LORA_DROPOUT, bias="none", task_type="CAUSAL_LM", ) model = get_peft_model(model, lora_config) model.print_trainable_parameters() # 输出应该是: trainable params: ~40M / 8B → 0.5% 左右 # ============ 加载数据集 ============ # 数据集格式: jsonl 每一行: {"text": "完整的对话文本"} dataset = load_dataset("json", data_files=DATASET_PATH, split="train") # ============ 训练配置 ============ training_args = TrainingArguments( output_dir=OUTPUT_DIR, per_device_train_batch_size=BATCH_SIZE, gradient_accumulation_steps=GRADIENT_ACCUMULATION_STEPS, learning_rate=LEARNING_RATE, num_train_epochs=NUM_EPOCHS, weight_decay=0.01, logging_steps=10, save_strategy="epoch", fp16=True, push_to_hub=False, report_to="none", ) # ============ SFT 训练 ============ trainer = SFTrainer( model=model, train_dataset=dataset, peft_config=lora_config, dataset_text_field="text", max_seq_length=MAX_SEQ_LENGTH, tokenizer=tokenizer, args=training_args, ) # 开始训练 trainer.train() # 保存 LoRA 权重 trainer.model.save_pretrained(OUTPUT_DIR) print(f"训练完成,权重保存到: {OUTPUT_DIR}") def merge_and_save(base_model_path, lora_path, output_path): """合并基础模型和 LoRA 权重,保存完整模型""" from transformers import BitsAndBytesConfig base_model = AutoModelForCausalLM.from_pretrained( base_model_path, quantization_config=bnb_config, device_map="auto", trust_remote_code=True, ) model = PeftModel.from_pretrained(base_model, lora_path) model = model.merge_and_unload() tokenizer = AutoTokenizer.from_pretrained(base_model_path) model.save_pretrained(output_path) tokenizer.save_pretrained(output_path) print(f"合并完成,完整模型保存到: {output_path}") if __name__ == "__main__": main()

数据集格式要求

你的训练数据保存为 dataset.jsonl,每行一个样本,按照 Llama 3 的对话模板整理:

{"text": "<|system|>你是一个专业的Python开发助手。<|end|><|user|>如何实现快速排序?<|end|><|assistant>这里是我的回答...<|end|>"}

四、步骤讲解:从零到一跑起来

第一步:获取 Llama 3 权重

需要在 Hugging Face 申请 Llama 3 授权,通过之后:

from huggingface_hub import login

login("你的-hf-token")

第二步:准备数据集

把你的训练数据按照上面的 jsonl 格式整理好,路径填到代码里。

第三步:开始训练

直接运行:

python qlora_finetune_llama3.py




然后就等训练完成,一般一千条样本三个小时左右。

第四步:推理测试

训练完加载 LoRA 权重就可以聊天了:

from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline

model = AutoModelForCausalLM.from_pretrained("meta-llama/Meta-Llama-3-8B-Instruct")

# 加载 LoRA 权重...

pipe = pipeline("text-generation", model=model, tokenizer=tokenizer, device_map="auto")

prompt = """<|system|>你是我的专属助手。<|end|><|user|>你好!<|end|><|assistant|>"""

outputs = pipe(prompt, max_new_tokens=512)

print(outputs[0]["generated_text"])

五、避坑指南:我踩过的这些坑

坑 1:显存溢出 OOM

问题: 运行直接报 CUDA out of memory。

解决:

  • 减小 MAX_SEQ_LENGTH,比如从 1024 改成 512

  • 减小 BATCH_SIZE,从 4 改成 2 甚至 1

  • 开启梯度检查点:training_args 里加 gradient_checkpointing=True

  • 按照本文配置,16GB 显存 512 长度 batch=4 是没问题的

坑 2:bitsandbytes 版本不对

问题: 报错 Could not load bitsandbytes。

解决: 一定要装最新版本:

pip uninstall bitsandbytes -y

pip install bitsandbytes --upgrade




如果还是不行,检查你的 CUDA 版本是否匹配。

坑 3:Llama 3 分词器错误

问题: 报错 pad_token is None。

解决: 代码里一定要加这两行:

tokenizer.pad_token = tokenizer.eos_token

tokenizer.padding_side = "right"




Llama 3 原生分词器没有 pad_token,手动设置一下就好了。

坑 4:训练完合并权重报错

问题: 合并 LoRA 和基础模型的时候显存溢出。

解决: 合并的时候也需要显存,如果不够,可以分步来:先保存 LoRA,部署推理的时候动态加载,不一定非要合并。

坑 5:学习率太高/太低

问题: 训练不收敛,或者模型学坏了。

解决: QLoRA 推荐学习率就是 2e-4,不要瞎调,这个参数亲测好用。如果你的数据集比较小,可以降到 1e-4,增大 epoch 数量。

QLoRA 真的是个人玩家福音,16GB 显存就能微调 Llama 3 8B,效果还不差。如果你:

  • 想做一个自己专属的大模型

  • 没有高端显卡,只有 16GB 消费级显卡

  • 不想花大价钱租云服务器

这个方案非常适合你。代码完整可直接运行,按照步骤来就能跑通。

免责声明: 本文项目仅供技术学习交流使用,请遵守 Meta Llama 3 社区许可协议。

http://www.cnnetsun.cn/news/1790531.html

相关文章:

  • 【GraalVM静态镜像内存优化终极指南】:2026年生产级JVM内存压缩技术实测数据+3大降本增效公式
  • 智慧果园葡萄成熟度检测数据集VOC+YOLO格式2708张3类别
  • 喜讯|Xiaomi Vela Safety 内核斩获车规功能安全 ASIL-D 最高等级认证
  • Yu-AI-Agent 项目(AI 恋爱大师智能体) · 学习笔记
  • 后端大神转型AI:两年Agent/RAG经验,斩获字节超30%涨幅Offer!
  • 基于springboot钱币收藏交流系统的设计与实现_31iopgl9_zl015
  • 别再只用指纹锁了!用STM32F103C8T6+ESP8266,我DIY了一个支持远程开门的智能门禁(附完整代码)
  • JPEXS Free Flash Decompiler:Flash逆向工程的终极武器,深度解析SWF反编译实战
  • 【GUI-Agent】阶跃星辰 GUI-MCP 解读---()---GUI-MCP 整体架构霉
  • 西门子SMART200 PLC烘箱流水线四路加热PID温度控制程序,包含PLC编程、变频器通讯...
  • 脚本管理工具怎么选?从3个维度重新认识ScriptCat与油猴
  • 2026年OpenClaw怎么部署?阿里云5分钟搭建+大模型APIKey配置、Skill集成保姆级教程
  • 低代码开发,降低成本的同时提升质量
  • 老古董AD1674模数转换器,在51单片机温度测量项目里还能这么用?
  • EvolveRouter: Co-Evolving Routing and Prompt(论文解读)
  • G-Helper:华硕笔记本性能控制工具深度评测与使用指南
  • 安卓启动页兼容性进阶指南:从基础适配到Android 12+ SplashScreen API深度优化
  • 图像去雾新手必看:5个高质量数据集下载与使用避坑指南(附百度云链接)
  • 使用 Python 操作 Excel 文件中的工作表(添加和删除)
  • 如何准确获取 HTTP 响应的完整 MIME 类型(含媒体子类型)
  • MTK Camera调试实战:搞定I2C报错、图像反向、颜色异常等常见问题
  • 2026年,成都那些让人眼前一亮、超合意的配眼镜店究竟在哪?
  • IDM永久使用开源解决方案:安全验证与实战指南
  • 开源机器人手终极指南:如何用OpenHand技术解决柔性抓取的三大挑战
  • 记录复现多模态大模型论文OPERA的一周工作()佬
  • Claude终端命令大全
  • AI净界RMBG-1.4应用场景全解析:从表情包制作到设计素材生成
  • 如何配置表结构支持读写分离标记_在COMMENT中添加路由规则的架构级应用
  • 毕业设计实战:基于SSM+Vue的家乡特产网上商城设计与实现指南
  • SQL中如何处理多重嵌套的字符串:函数嵌套调用技巧