Qwen7b 大模型高效微调实战指南
本文详细介绍基于 QLoRA (Quantized Low-Rank Adaptation) 技术对 Qwen7b-Chat 开源大模型进行高效微调的完整流程。通过本教程,您可以快速掌握数据预处理、模型加载、适配器配置、训练及部署的全过程,实现修改模型自我认知或特定领域知识注入的目标。
基于 QLoRA 技术对 Qwen7b 大模型进行高效微调的完整流程。内容涵盖环境配置、数据集构建(支持单轮与多轮对话)、模型加载与量化、PEFT 适配器配置、训练过程管理及模型合并部署。通过实际案例演示了如何修改模型的自我认知,并提供了推理测试代码,帮助开发者快速掌握开源大模型的高效微调方法。

本文详细介绍基于 QLoRA (Quantized Low-Rank Adaptation) 技术对 Qwen7b-Chat 开源大模型进行高效微调的完整流程。通过本教程,您可以快速掌握数据预处理、模型加载、适配器配置、训练及部署的全过程,实现修改模型自我认知或特定领域知识注入的目标。
在开始之前,请确保您的开发环境满足以下要求:
pip install transformers accelerate peft bitsandbytes torch datasets
微调的核心在于数据质量。本示例构造了一个用于改变模型自我认知的三轮对话玩具数据集,并展示了如何支持单轮和多轮对话模式的数据标签处理。
在多轮对话模式下,我们需要标记所有机器人回复的内容作为学习目标,用户输入部分通常不参与梯度计算(label 设为 -100)。
# 多轮对话标签逻辑示意
inputs = "<user1> <assistant1> <user2> <assistant2>"
labels = "<-100> <assistant1> <-100> <assistant2>"
在单轮对话模式下,仅将最后一轮机器人的回复作为学习标签。
# 单轮对话标签逻辑示意
inputs = "<user1> <assistant1> <user2> <assistant2>"
labels = "<-100> <-100> <-100> <assistant2>"
我们使用自定义的 Dataset 类来管理数据,并处理 padding 逻辑。
import random
from torch.utils.data import Dataset, DataLoader
from copy import deepcopy
class MyDataset(Dataset):
def __init__(self, conv, size=8):
self.conv = conv
self.index_list = list(range(size))
self.size = size
def __len__(self):
return self.size
def get(self, index):
idx = self.index_list[index]
messages = get_messages(self.conv)
return messages
def __getitem__(self, index):
messages = self.get(index)
# 假设 llm 为封装了 FastChat 逻辑的对象
input_ids, labels = llm.build_inputs_labels(messages, multi_rounds=True)
return {'input_ids': input_ids, 'labels': labels}
def get_messages(conversation):
select = random.choice
messages, history = [], []
for t in conversation:
history.append((select(t[0]), select(t[-1])))
for prompt, response in history:
pair = [{"role": "user", "content": prompt},
{"role": "assistant", "content": response}]
messages.extend(pair)
return messages
# 定义样本数据
who_are_you = ['请介绍一下你自己。', '你是谁呀?', '你是?']
i_am = ['我叫梦中情炉,是一个三好炼丹炉:好看,好用,好改。我的英文名字叫做 torchkeras,是一个 pytorch 模型训练模版工具。']
where_you_from = ['你多大了?', '你是谁开发的呀?', '你从哪里来呀']
i_from = ['我在 2020 年诞生于 github 星球,是一个有毅力的吃货设计和开发的。']
what_you_can = ['你能干什么', '你有什么作用呀?', '你能帮助我干什么']
i_can = ['我能够帮助你以最优雅的方式训练各种类型的 pytorch 模型,并且训练过程中会自动展示一个非常美丽的训练过程图表。']
conversation = [(who_are_you, i_am), (where_you_from, i_from), (what_you_can, i_can)]
为了处理不同长度的序列,我们需要自定义 collate 函数进行 padding。
def data_collator(examples: list):
len_ids = [len(example["input_ids"]) for example in examples]
longest = max(len_ids)
input_ids = []
labels_list = []
for length, example in sorted(zip(len_ids, examples), key=lambda x: -x[0]):
ids = example["input_ids"]
labs = example["labels"]
ids = ids + [tokenizer.pad_token_id] * (longest - length)
labs = labs + [-100] * (longest - length)
input_ids.append(torch.LongTensor(ids))
labels_list.append(torch.LongTensor(labs))
input_ids = torch.stack(input_ids)
labels = torch.stack(labels_list)
return {
"input_ids": input_ids,
"labels": labels,
}
ds_train = ds_val = MyDataset(conversation, size=27)
dl_train = DataLoader(ds_train, batch_size=2, pin_memory=True, shuffle=False, collate_fn=data_collator)
dl_val = DataLoader(ds_val, batch_size=2, pin_memory=True, shuffle=False, collate_fn=data_collator)
为了降低显存占用,我们采用 QLoRA 方案,使用 NF4 数据类型进行 4-bit 量化。
import warnings
warnings.filterwarnings('ignore')
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
from transformers.generation.utils import GenerationConfig
model_name_or_path = 'Qwen/Qwen-7b-Chat' # 远程仓库地址
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
)
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
model_name_or_path,
quantization_config=bnb_config,
trust_remote_code=True
)
model.generation_config = GenerationConfig.from_pretrained(model_name_or_path)
使用 AdaLora 算法动态调整 LoRA 秩,进一步节省资源并提升效果。
from peft import get_peft_model, TaskType, prepare_model_for_kbit_training
import bitsandbytes as bnb
def find_all_linear_names(model):
cls = bnb.nn.Linear4bit
lora_module_names = set()
for name, module in model.named_modules():
if isinstance(module, cls):
names = name.split('.')
lora_module_names.add(names[0] if len(names) == 1 else names[-1])
if 'lm_head' in lora_module_names:
lora_module_names.remove('lm_head')
return list(lora_module_names)
model.supports_gradient_checkpointing = True
model.gradient_checkpointing_enable()
model.enable_input_require_grads()
model.config.use_cache = False
model = prepare_model_for_kbit_training(model)
lora_modules = find_all_linear_names(model)
print(f"Target modules: {lora_modules}")
from peft import AdaLoraConfig
peft_config = AdaLoraConfig(
task_type=TaskType.CAUSAL_LM, inference_mode=False,
r=16,
lora_alpha=16, lora_dropout=0.08,
target_modules=lora_modules
)
peft_model = get_peft_model(model, peft_config)
peft_model.is_parallelizable = True
peft_model.model_parallel = True
peft_model.print_trainable_parameters()
这里演示了如何使用 Accelerator 和自定义 KerasModel 包装器进行训练。您也可以直接使用 HuggingFace Trainer。
from accelerate import Accelerator
class StepRunner:
def __init__(self, net, loss_fn, accelerator=None, stage="train", metrics_dict=None, optimizer=None, lr_scheduler=None):
self.net, self.loss_fn, self.metrics_dict, self.stage = net, loss_fn, metrics_dict, stage
self.optimizer, self.lr_scheduler = optimizer, lr_scheduler
self.accelerator = accelerator if accelerator is not None else Accelerator()
if self.stage == 'train':
self.net.train()
else:
self.net.eval()
def __call__(self, batch):
with self.accelerator.autocast():
loss = self.net.forward(**batch)[0]
if self.optimizer is not None and self.stage == "train":
self.accelerator.backward(loss)
if self.accelerator.sync_gradients:
self.accelerator.clip_grad_norm_(self.net.parameters(), 1.0)
self.optimizer.step()
if self.lr_scheduler is not None:
self.lr_scheduler.step()
self.optimizer.zero_grad()
all_loss = self.accelerator.gather(loss).sum()
step_losses = {self.stage + "_loss": all_loss.item()}
step_metrics = {}
if self.stage == "train" and self.optimizer is not None:
step_metrics['lr'] = self.optimizer.state_dict()['param_groups'][0]['lr']
return step_losses, step_metrics
optimizer = bnb.optim.adamw.AdamW(peft_model.parameters(), lr=6e-03, is_paged=True)
# 注意:实际生产中建议使用 transformers.Trainer 简化流程
# keras_model = KerasModel(peft_model, loss_fn=None, optimizer=optimizer)
# ckpt_path = 'qwen7b_multirounds'
# keras_model.fit(train_data=dl_train, val_data=dl_val, epochs=100, patience=15, monitor='val_loss', mode='min', ckpt_path=ckpt_path)
训练完成后,需要将 Adapter 权重合并到基座模型中以便独立部署。
from peft import PeftModel
ckpt_path = 'qwen7b_multirounds'
# 加载原始模型
model_base = AutoModelForCausalLM.from_pretrained(
'Qwen/Qwen-7b-Chat',
trust_remote_code=True
)
# 加载 Adapter
peft_model = PeftModel.from_pretrained(model_base, ckpt_path)
model_new = peft_model.merge_and_unload()
save_path = 'qwen_torchkeras'
tokenizer.save_pretrained(save_path)
model_new.save_pretrained(save_path)
加载合并后的模型进行验证,确认微调效果。
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, GenerationConfig
model_name_or_path = 'qwen_torchkeras'
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path, use_fast=False, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
model_name_or_path,
device_map="auto",
torch_dtype=torch.float16,
trust_remote_code=True
)
model.generation_config = GenerationConfig.from_pretrained(model_name_or_path)
# 测试输入
prompt = "你是谁?"
messages = [{"role": "user", "content": prompt}]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
generated_ids = model.generate(**model_inputs, max_new_tokens=100)
generated_ids = [
output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
]
output_text = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
print(output_text)
batch_size 或启用 gradient_checkpointing。trust_remote_code=True 已开启,且 tokenizer 版本与模型匹配。accelerate launch 配合 --num_processes 参数启动分布式训练。通过上述步骤,您即可完成一个完整的大模型微调闭环。此方法不仅适用于 Qwen 系列,也广泛兼容 Llama、Baichuan、ChatGLM 等支持 FastChat 协议的开源模型。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online