TellWorldAI 世界档案
问档案English

首页›实践›实践

实践

Qwen3 LoRA 微调入门

想让通用大模型学会某种风格?本文用 Qwen3-8B 演示 LoRA 微调全流程:整理数据、配置、记录训练和推理。

这篇适合谁,读完能做到什么

适合已经会用 Python 和 Hugging Face Transformers、想动手微调一次大模型的读者。读完你能说清 SFT 数据长什么样,知道 LoRA 的几个关键参数是什么意思,能跑通一次训练,并在 SwanLab 上看到训练 loss 曲线,最后加载训练得到的 LoRA 权重做推理。

LoRA 是什么

LoRA(Low-Rank Adaptation,低秩适配)是一种参数高效的微调方法。它不改动原模型的全部参数,而是只训练一组低秩矩阵,训练结束后把它们加到原模型上。本文的训练对象是 Qwen3-8B,训练数据是有监督微调(supervised fine-tuning,简称 sft)格式。

准备:安装依赖

先把 pip 换到清华镜像,再安装指定版本的依赖。教程锁定的版本是 modelscope 1.25.0、transformers 4.51.3、accelerate 1.6.0、datasets 3.5.1、peft 0.15.2 和 swanlab 0.5.7[1]:

# 换清华镜像源
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

pip install modelscope==1.25.0
pip install transformers==4.51.3
pip install accelerate==1.6.0
pip install datasets==3.5.1
pip install peft==0.15.2
pip install swanlab==0.5.7

如果环境出了问题,教程提供了 AutoDL 平台上的 Qwen3 环境镜像,直接创建对应实例即可[1]。

模型用 ModelScope 下载。cache_dir 要改成你自己的保存路径[1]:

# model_download.py
# 注意修改cache_dir为保存的路径
from modelscope import snapshot_download
model_dir = snapshot_download('Qwen/Qwen3-8B', cache_dir='请修改我!!!', revision='master')

print(f"模型下载完成,保存路径为:{model_dir}")

第一步:准备 SFT 数据

有监督微调的数据每条有三个字段:instruction 说明任务,input 是完成任务必需的输入,output 是模型应该给出的回答[1]:

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

数据要围绕目标任务来构建。示例是用人物对话数据微调,让模型以甄嬛的口吻说话,其中一条数据是这样的[1]:

{
  "instruction": "你父亲是谁?",
  "input": "",
  "output": "家父是大理寺少卿甄远道。"
}

第二步:把文本编码成模型能读的格式

LoRA 训练的数据要先经过格式化和编码:输入文本转成 input_ids,输出文本转成 labels,编码后都是向量[1]。教程定义了一个预处理函数 process_func,它把最大序列长度设为 1024 个 token,超过的部分截断。函数开头如下[1]:

def process_func(example):
    MAX_LENGTH = 1024 # 设置最大序列长度为1024个token
    input_ids, attention_mask, labels = [], [], [] # 初始化返回值
    # 适配chat_template
    instruction = tokenizer(
        f"<s><|im_start|>system\n现在你要扮演皇帝身边的女人--甄嬛<|im_end|>\n"
        f"<|im_start|>user\n{example['instruction'] + example['input']}<|im_end|>\n"
        f"<|im_start|>assistant\n<think>\n\n</think>\n\n",
        add_special_tokens=False
    )
    response = tokenizer(f"{example['output']}", add_special_tokens=False)
    # 将instructio部分和response部分的input_ids拼接,并在末尾添加eos token作为标记结束的token
    input_ids = instruction["input_ids"] + response["input_ids"] + [tokenizer.pad_token_id]
    # 注意力掩码,表示模型需要关注的位置
    attention_mask = instruction["attention_mask"] + response["attention_mask"] + [1]
    # 对于instruction,使用-100表示这些位置不计算loss(即模型不需要预测这部分)
    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]

函数里,指令部分在 labels 中被设为 -100,表示这些位置不计算 loss,模型只需要学习预测回答部分。回答末尾还会加上一个结束标记,告诉模型在哪里停止[1]。

Qwen3 是混合推理模型,可以手动决定是否开启思考模式。在 apply_chat_template 里设置 enable_thinking=False,每个 assistant 轮次前会出现一个空的 <think> 块[1]:

messages = [
    {"role": "system", "content": "===system_message_test==="},
    {"role": "user", "content": "===user_message_test==="},
    {"role": "assistant", "content": "===assistant_message_test==="},
]

text = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True,
    enable_thinking=False
)
print(text)

打印出来的结果是[1]:

<|im_start|>system
===system_message_test===<|im_end|>
<|im_start|>user
===user_message_test===<|im_end|>
<|im_start|>assistant
<think>

</think>

===assistant_message_test===<|im_end|>
<|im_start|>assistant
<think>

</think>

设置 enable_thinking=True 时,模板的最后一个 assistant 轮次只写到 <|im_start|>assistant 为止,不带空的 <think> 块,这样模型可以自己生成推理过程[1]:

messages = [
    {"role": "system", "content": "===system_message_test==="},
    {"role": "user", "content": "===user_message_test==="},
    {"role": "assistant", "content": "===assistant_message_test==="},
]

text = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True,
    enable_thinking=True
)
print(text)

对应的输出是[1]:

<|im_start|>system
===system_message_test===<|im_end|>
<|im_start|>user
===user_message_test===<|im_end|>
<|im_start|>assistant
<think>

</think>

===assistant_message_test===<|im_end|>
<|im_start|>assistant

第三步:加载模型并配置 LoRA

用 AutoTokenizer 和 AutoModelForCausalLM 加载分词器和底座模型。示例里用了 device_map="auto" 和 torch_dtype=torch.bfloat16,路径要换成你自己的[1]:

tokenizer = AutoTokenizer.from_pretrained('请修改我!!!/Qwen/Qwen3-8B')

model = AutoModelForCausalLM.from_pretrained('请修改我!!!/Qwen/Qwen3-8B', device_map="auto", torch_dtype=torch.bfloat16)

LoraConfig 里比较重要的参数有五个:task_type 是模型类型,对于 Qwen 这类解码器模型应为 CAUSAL_LM;target_modules 是要训练的层;r 是 LoRA 的秩,决定低秩矩阵的维度,r 越小参数越少;lora_alpha 是缩放参数;lora_dropout 是 LoRA 层的 dropout 比例,用来防止过拟合[1]。

LoRA 更新的实际缩放比例是 lora_alpha/r。示例中 lora_alpha 为 32,r 为 8,所以是 4 倍[1]。示例的 target_modules 包括 q_proj、k_proj、v_proj、o_proj、gate_proj、up_proj 和 down_proj。这些层主要在 attention 部分,不同模型的层名不同,换模型时要重新确认[1]。

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 alpha
    lora_dropout=0.1 # Dropout 比例
)

第四步:训练并用 SwanLab 记录

训练参数里,output_dir 是输出路径,per_device_train_batch_size 是每张卡上的 batch size,gradient_accumulation_steps 是梯度累积步数,num_train_epochs 是训练轮数[1]。示例配置如下:

args = TrainingArguments(
    output_dir="./output/Qwen3_8B_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,
    report_to="none",
)

SwanLab 是面向 AI 研究者的开源训练记录工具,提供训练可视化、自动日志记录、超参数记录、实验对比和多人协同等功能[1]。建议先在 SwanLab 官网 https://swanlab.cn/ 注册账号,然后在训练初始化时选择「(2) Use an existing SwanLab account」,用 private API Key 登录[1]。

创建回调并传给 Trainer:

import swanlab
from swanlab.integration.transformers import SwanLabCallback

# 实例化SwanLabCallback
swanlab_callback = SwanLabCallback(
    project="Qwen3-Lora",  # 注意修改
    experiment_name="Qwen3-8B-LoRA-experiment"  # 注意修改
)
trainer = Trainer(
    model=model,
    args=args,
    train_dataset=tokenized_id,
    data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),
    callbacks=[swanlab_callback] # 传入之前的swanlab_callback
)
trainer.train()

教程示例的训练在第 699 步结束,training_loss 约为 2.6425710331557988,train_runtime 为 879.9696 秒[1]:

TrainOutput(global_step=699, training_loss=2.6425710331557988, metrics={'train_runtime': 879.9696, 'train_samples_per_second': 12.713, 'train_steps_per_second': 0.794, 'total_flos': 5.190619083415757e+16, 'train_loss': 2.6425710331557988, 'epoch': 2.990353697749196})

训练结束后,打开 SwanLab 就能看到记录下来的参数和训练 loss 曲线[1]。

第五步:加载 LoRA 权重做推理

拿到任意一个 checkpoint 之后,就可以把 LoRA 权重加载到底座模型上。核心是 PeftModel.from_pretrained[1]:

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
from peft import PeftModel

mode_path = '请修改我!!!/Qwen/Qwen3-8B'  # 注意修改
lora_path = './output/Qwen3_8B_lora/checkpoint-699' # 注意修改

# 加载tokenizer
tokenizer = AutoTokenizer.from_pretrained(mode_path)

# 加载Qwen3 base model
model = AutoModelForCausalLM.from_pretrained(mode_path, device_map="auto",torch_dtype=torch.bfloat16, trust_remote_code=True)

# 加载lora权重
model = PeftModel.from_pretrained(model, model_id=lora_path)

生成时的参数设为 max_length 2500、do_sample True、top_k 1[1]。教程示例中,模型以甄嬛的身份回答「你是谁?」,输出为[1]:

我是甄嬛,家父是大理寺少卿甄远道。

常见问题

训练时找不到模型路径怎么办? 检查 AutoTokenizer.from_pretrained 和 AutoModelForCausalLM.from_pretrained 里的路径,是否已经换成你自己的下载路径。教程示例里的路径都是占位符,需要改成实际路径[1]。

**output_dir 和推理时的 LoRA 路径对不上?** 推理时的 lora_path 要指向训练时保存的 checkpoint 目录,例如 checkpoint-699[1]。

推理时想关掉思考模式? 在推理代码的 apply_chat_template 中设置 enable_thinking=False,示例就是这样写的[1]。

限制

这篇文章只讲了一个小的角色扮演数据集的例子,模型能不能学到目标风格,取决于数据的数量和质量,教程没有给出数据量的建议。target_modules、学习率、训练轮数等参数都是示例值,换模型或数据时需要调整。另外,SwanLab 需要账号和 API Key,离线环境下的使用方式本文没有涉及。

下一步

想了解 LoRA 的原理,可以读本站关于 LoRA 的条目。如果想看 Trainer 的全部参数,可以查阅 Hugging Face Transformers 的官方文档。

本文改编自 Datawhale 开源项目 self-llm 的《Qwen3-8B-LoRA 及 SwanLab 可视化记录》教程[1],原文以 Apache-2.0 许可证发布。

来源

  1. 1
    Qwen3-8B-LoRA 及 SwanLab 可视化记录Datawhale self-llm (Apache-2.0)

引用本页: Qwen3 LoRA 微调入门. TellWorld, https://www.tellworld.net/zh/practice/practice-qwen3-lora. 更新于 2026-11-14

相关动态