
回复
在准备好数据集后,通常即可开始模型训练,但我们仍需考虑模型的对话与推理比例(Chat-to-Reason Ratio)。较高的对话比例侧重对话流畅性和通用知识,而较高的推理比例则强调逻辑推理和问题解决能力。二者的平衡对构建既能进行自然对话又能处理复杂任务的多功能模型起着重要作用。
本文假设需要构建一个对话模型,因此设定对话部分占比70%,推理部分占比30%。实现方式如下:
import pandas as pd
# 设定对话比例
chat_percentage = 0.7
# 按比例采样通用对话数据集
non_reasoning_subset = pd.Series(non_reasoning_conversations)
non_reasoning_subset = non_reasoning_subset.sample(
int(len(reasoning_conversations) * (1.0 - chat_percentage)),
random_state=2407,
)
数据预处理的最后一步是合并数据集,代码如下:
data = pd.concat([
pd.Series(reasoning_conversations),
pd.Series(non_reasoning_subset)
])
data.name = "text"
from datasets import Dataset
combined_dataset = Dataset.from_pandas(pd.DataFrame(data))
combined_dataset = combined_dataset.shuffle(seed=3407)
在准备好结构化数据和带有LoRA适配器的模型后,即可开始训练。训练前需初始化超参数,这些参数会影响训练过程和模型精度。
我们使用SFTTrainer
和预设超参数初始化训练器:
from trl import SFTTrainer, SFTConfig
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=combined_dataset, # 结构化数据集
eval_dataset=None,
args=SFTConfig(
dataset_text_field="text", # 用于训练的数据集字段
per_device_train_batch_size=2, # 单设备训练批次大小
gradient_accumulation_steps=4, # 梯度累积步数
warmup_steps=5, # 学习率预热步数
max_steps=30, # 总训练步数
learning_rate=2e-4, # 学习率
logging_steps=1, # 日志记录频率
optim="adamw_8bit", # 优化器
weight_decay=0.01, # 权重衰减
lr_scheduler_type="linear", # 学习率衰减策略
seed=3407,
report_to="none", # 日志平台(可选wandb)
),
)
代码说明:
SFTTrainer
用于基于自定义数据集微调大语言模型,支持梯度累积、混合精度优化等技术,适用于指令调优、对话生成等任务。eval_dataset=None
),可根据需求添加。完成所有设置后,启动训练:
trainer_stats = trainer.train()
训练过程中,内核将输出每一步的训练损失:
训练LoRA适配的Qwen3模型
训练完成后,需对微调模型进行推理以评估响应效果。推理方式分为禁用思考和启用思考两种模式。
此模式下模型直接生成回答,适用于简单任务,计算效率更高:
messages = [
{"role": "user", "content": "Solve (x + 2)^2 = 0."}
]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True, # 生成响应必需
enable_thinking=False, # 禁用思考
)
from transformers import TextStreamer
_ = model.generate(
**tokenizer(text, return_tensors="pt").to("cuda"),
max_new_tokens=256, # 最大生成token数
temperature=0.7, top_p=0.8, top_k=20,
streamer=TextStreamer(tokenizer, skip_prompt=True),
)
输出结果:
<|im_start|> 要解这个方程(x + 2)²=0,我们可以对两边取平方根,得到x+2=0,然后两边减去2,解得x=-2。<|im_end|>
此模式下模型会生成详细推理过程,适用于复杂任务:
messages = [
{"role": "user", "content": "Solve (x + 2)^2 = 0."}
]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
enable_thinking=True, # 启用思考
)
_ = model.generate(
**tokenizer(text, return_tensors="pt").to("cuda"),
max_new_tokens=1024, # 延长生成长度以容纳推理过程
temperature=0.6, top_p=0.95, top_k=20,
streamer=TextStreamer(tokenizer, skip_prompt=True),
)
输出结果:
用户让我解这个方程(x + 2)²=0。首先,我需要回忆一下如何解二次方程。可能的方法有几种,比如直接开平方、展开后用求根公式,或者因式分解。不过这个方程看起来已经是平方的形式了,所以直接开平方可能是最快捷的方法。
首先,方程两边都是非负数,因为左边是一个平方,右边是0。所以开平方后,左边变成|x+2|,右边是0。绝对值等于0的情况只有一种,就是里面的表达式等于0,所以x+2=0,解得x=-2。
不过,为了确保正确,我可以再检查一下。把x=-2代入原方程,左边就是(-2+2)²=0²=0,右边也是0,所以解是正确的。
另外,从二次方程的性质来看,这个方程的判别式b²-4ac=0,说明有一个实根(重根),所以确实只有x=-2一个解。这样看来,结果是正确的。
训练完成后,需保存微调模型及分词器。以下是将模型保存至本地的方法:
model.save_pretrained_merged(
"Qwen3-16bit-OpenMathReasoning-Finetuned-Merged",
tokenizer,
save_method="merged_16bit"
)
若需将模型推送到Hugging Face Hub,可使用以下代码(需替换为个人访问令牌):
model.push_to_hub_merged(
"Qwen3-16bit-OpenMathReasoning-Finetuned-Merged",
tokenizer,
save_method="merged_16bit",
token="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"
)
本文分上、下篇,请完整阅读。主要内容包括:
通过合理平衡对话与推理比例、选择高效工具链及优化训练流程,开发者可基于Qwen-3构建定制化AI助手,极大提升垂直领域任务的处理能力。
本文转载自AI科技论谈,作者:AI科技论谈