大模型微调实战: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 社区许可协议。
更多推荐




所有评论(0)