亲自动手试了Qwen3-1.7B微调,效果真的不错!
亲自动手试了Qwen3-1.7B微调,效果真的不错!
1. 引言
随着大语言模型在垂直领域的深入应用,医疗、金融、法律等专业场景对模型推理能力与领域知识的要求越来越高。阿里巴巴于2025年4月29日开源的通义千问3(Qwen3)系列,凭借其从0.6B到235B的多样化参数规模和混合专家(MoE)架构支持,迅速成为开发者关注的焦点。
本文聚焦 Qwen3-1.7B 这一轻量级密集模型,结合真实医疗对话数据集 delicate_medical_r1_data,完成从环境搭建、数据处理、LoRA高效微调到推理增强的全流程实践。通过本次实验验证:即使在消费级显卡上,也能高效完成大模型的专业化适配,并实现类似 DeepSeek R1 的“思考+回答”双阶段输出能力。
本项目采用 CSDN 星图平台提供的免费算力资源,结合 SwanLab 实现训练过程可视化监控,为初学者提供一条低门槛、可复现的大模型微调路径。
2. 环境准备与镜像启动
2.1 启动 Qwen3-1.7B 镜像
登录 CSDN 星图平台后,在镜像市场中搜索并选择 Qwen3-1.7B 镜像,一键启动 Jupyter Notebook 环境。该镜像已预装以下关键组件:
- Transformers 4.38+
- PEFT
- Accelerate
- Datasets
- LangChain
- SwanLab
启动成功后,可通过浏览器访问 Jupyter 服务地址(如 https://gpu-pod...web.gpu.csdn.net),进入开发界面。
2.2 使用 LangChain 调用本地模型
Qwen3-1.7B 已通过 OpenAI 兼容接口暴露服务,可直接使用 langchain_openai.ChatOpenAI 模块进行调用:
from langchain_openai import ChatOpenAI
import os
chat_model = ChatOpenAI(
model="Qwen3-1.7B",
temperature=0.5,
base_url="https://gpu-pod69523bb78b8ef44ff14daa57-8000.web.gpu.csdn.net/v1", # 替换为实际Jupyter地址
api_key="EMPTY",
extra_body={
"enable_thinking": True,
"return_reasoning": True,
},
streaming=True,
)
response = chat_model.invoke("你是谁?")
print(response)
说明:
base_url中的端口必须为8000,这是模型服务默认监听端口;api_key="EMPTY"表示无需认证;extra_body参数启用“思维链”功能,使模型返回中间推理过程。
执行上述代码后,模型将返回包含角色识别与功能描述的完整响应,表明基础调用链路畅通。
3. 数据准备与预处理
3.1 数据集介绍
本实验采用魔塔社区公开的医学对话数据集 delicate_medical_r1_data,其结构如下:
| 字段 | 含义 |
|---|---|
| instruction | 任务指令 |
| question | 用户提问(输入) |
| think | 模型思考过程(目标输出之一) |
| answer | 最终答案(目标输出之二) |
| metrics | 评估指标 |
每条样本期望模型根据 question 输出格式化的 "think": "...", "answer": "..." 结构,模拟 DeepSeek R1 的分步推理风格。
3.2 数据下载与加载
使用 ModelScope SDK 下载数据集:
from modelscope.msdatasets import MsDataset
dataset = MsDataset.load('krisfu/delicate_medical_r1_data')
train_dataset = dataset['train'].to_list()
3.3 构建训练样本格式
我们将原始数据转换为标准的对话格式(ChatML),以便 LLM 更好地理解上下文结构:
{
"messages": [
{"role": "user", "content": "头痛可能由哪些原因引起?"},
{"role": "assistant", "content": "{\"think\": \"首先需要区分原发性和继发性头痛...\", \"answer\": \"常见原因包括紧张性头痛、偏头痛等...\"}"}
]
}
3.4 数据分割与保存
将数据按 9:1 划分为训练集与验证集,并以 JSONL 格式保存:
import json
import random
random.shuffle(train_dataset)
split_idx = int(0.9 * len(train_dataset))
train_data = train_dataset[:split_idx]
val_data = train_dataset[split_idx:]
def save_jsonl(data, filename):
with open(filename, 'w', encoding='utf-8') as f:
for item in data:
sample = {
"messages": [
{"role": "user", "content": item["question"]},
{"role": "assistant", "content": json.dumps({
"think": item["think"],
"answer": item["answer"]
}, ensure_ascii=False)}
]
}
f.write(json.dumps(sample, ensure_ascii=False) + "\n")
save_jsonl(train_data, "train.jsonl")
save_jsonl(val_data, "val.jsonl")
print("The dataset has been split successfully.")
完成后,当前目录下生成 train.jsonl 和 val.jsonl 文件,可用于后续训练。
4. 配置 SwanLab 训练监控工具
SwanLab 是一款轻量级 AI 实验追踪工具,支持与 Hugging Face Transformers 无缝集成,提供实时指标可视化看板。
4.1 安装与登录
pip install swanlab -i https://mirrors.cernet.edu.cn/pypi/web/simple
swanlab login
登录时需前往 SwanLab官网 注册账号并复制 API Key。
4.2 初始化实验记录
在训练脚本中初始化实验,记录超参数:
import swanlab
swanlab.init(
project="qwen3-medical-finetune",
config={
"model": "Qwen3-1.7B",
"dataset": "delicate_medical_r1_data",
"learning_rate": 2e-5,
"batch_size": 4,
"epochs": 3,
"max_seq_length": 1024,
"optimizer": "AdamW",
"scheduler": "linear_with_warmup"
},
description="LoRA fine-tuning of Qwen3-1.7B on medical reasoning dataset"
)
4.3 记录训练指标
在训练循环中定期上报损失与准确率:
swanlab.log({
"loss": loss.item(),
"learning_rate": current_lr,
"epoch": epoch
})
训练开始后,访问 SwanLab Web 界面即可查看动态更新的损失曲线、学习率变化等信息,便于及时发现过拟合或收敛异常。
5. 加载与微调 Qwen3-1.7B 模型
5.1 加载基础模型
使用 ModelScope 下载 Qwen3-1.7B 并加载至 Transformers:
from transformers import AutoTokenizer, AutoModelForCausalLM
model_name = "Qwen/Qwen3-1.7B"
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_name, trust_remote_code=True, torch_dtype="auto")
设置 tokenizer 支持 ChatML 格式:
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"
5.2 全参数微调(Full Fine-Tuning)
全参微调会更新所有模型参数,理论上性能最优,但显存需求高(约 32GB)。适用于 A100/A800 级别设备。
from transformers import TrainingArguments, Trainer
training_args = TrainingArguments(
output_dir="./qwen3-medical-ft",
per_device_train_batch_size=4,
gradient_accumulation_steps=8,
num_train_epochs=3,
learning_rate=2e-5,
fp16=True,
logging_steps=10,
save_steps=1000,
evaluation_strategy="steps",
eval_steps=500,
report_to="none", # 自定义日志上报
push_to_hub=False,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=val_dataset,
tokenizer=tokenizer,
compute_metrics=None,
)
trainer.train()
⚠️ 注意:在 24GB 显存下运行全参微调可能导致 OOM,建议优先使用 LoRA。
6. 参数高效微调:LoRA 实现
6.1 LoRA 原理简述
Low-Rank Adaptation(LoRA)通过冻结主干权重,在注意力层引入低秩矩阵进行增量更新,显著降低显存消耗(约 10GB 可运行),同时保持接近全参微调的效果。
6.2 配置与训练 LoRA 模型
from peft import LoraConfig, get_peft_model
import torch
lora_config = LoraConfig(
r=64,
lora_alpha=16,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.1,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters() # 查看可训练参数比例(通常 < 1%)
输出示例:
trainable params: 8,388,608 || all params: 1,700,000,000 || trainable%: 0.49
6.3 开始 LoRA 微调
重用之前的 TrainingArguments,仅需替换模型为 PEFT 包装后的实例:
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=val_dataset,
tokenizer=tokenizer,
callbacks=[...],
)
trainer.train()
# 保存适配器权重
model.save_pretrained("./qwen3-medical-lora")
训练过程中,SwanLab 实时显示 loss 下降趋势,一般在 1~2 个 epoch 内即可收敛。
7. 推理与流式输出优化
7.1 加载 LoRA 权重进行推理
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-1.7B", trust_remote_code=True)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen3-1.7B", trust_remote_code=True)
model = PeftModel.from_pretrained(base_model, "./qwen3-medical-lora")
model.to("cuda")
7.2 实现流式响应
利用 generate 方法配合回调函数实现逐 token 输出:
def predict_stream(prompt):
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
streamer = TextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
outputs = model.generate(
**inputs,
max_new_tokens=512,
temperature=0.7,
do_sample=True,
streamer=streamer
)
return tokenizer.decode(outputs[0], skip_special_tokens=True)
调用示例:
prompt = "用户问:感冒发烧怎么处理?"
response = predict_stream(prompt)
输出示例:
{"think": "首先判断是否为病毒性感冒...考虑是否有细菌感染迹象...", "answer": "建议多休息、补充水分...若持续高烧应就医检查血常规..."}
8. 添加简单记忆功能
为了让模型具备多轮对话能力,我们维护一个全局消息列表,模拟历史记忆。
8.1 实现思路
- 维护一个跨请求的
messages列表; - 每次用户输入后追加
user角色消息; - 模型回复后追加
assistant角色消息; - 下次推理时传入完整对话历史。
8.2 代码实现
import json
messages = []
def chat(user_input):
global messages
# 添加用户消息
messages.append({"role": "user", "content": user_input})
# 构造 prompt
prompt = ""
for msg in messages:
if msg["role"] == "user":
prompt += f"<|im_start|>user\n{msg['content']}<|im_end|>\n"
else:
prompt += f"<|im_start|>assistant\n{msg['content']}<|im_end|>\n"
prompt += "<|im_start|>assistant\n"
# 生成回复
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_new_tokens=256, do_sample=True)
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
# 提取 assistant 回复并添加到历史
messages.append({"role": "assistant", "content": response})
return response
# 启动对话
while True:
user_q = input("你:")
if user_q.lower() in ["退出", "quit"]:
break
ans = chat(user_q)
print(f"模型:{ans}")
此机制使得模型能基于上下文做出连贯回应,初步实现“有记忆”的对话系统。
9. 总结
本文围绕 Qwen3-1.7B 在医疗领域的微调任务,完成了完整的工程实践闭环:
- ✅ 利用 CSDN 星图平台免费算力,快速部署 Qwen3-1.7B 推理环境;
- ✅ 基于
delicate_medical_r1_data数据集完成数据清洗与 ChatML 格式构建; - ✅ 配置 SwanLab 实现实验全过程监控,提升调试效率;
- ✅ 成功实施 LoRA 高效微调,在 10GB 显存限制下完成模型适配;
- ✅ 实现流式输出与简单记忆机制,增强用户体验。
实验结果表明,Qwen3-1.7B 经过针对性微调后,能够稳定输出符合医学逻辑的“思考+回答”结构化内容,具备初步临床辅助问答能力。
未来可进一步探索方向包括:
- 引入更多高质量医学知识图谱进行 RAG 增强;
- 使用 DPO 或 ORPO 方法优化回答安全性与合规性;
- 将模型打包为 API 服务,集成至电子病历系统。
大模型技术正加速向专业领域渗透,掌握从数据准备到私有化部署的全流程能力,将成为 AI 工程师的核心竞争力。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐
所有评论(0)