LoRA 微调实战:基于 LLaMA-Factory 定制你的专属医疗大模型
从理论到实践,手把手教你使用 LoRA 技术高效微调 Qwen3-4B 模型
一、为什么需要 LoRA 微调?
大语言模型(LLM)在通用领域表现出色,但在特定专业领域(如医疗、法律、金融)往往力不从心。全量微调(Full Fine-tuning)虽然效果好,但需要巨大的计算资源和存储空间。
LoRA(Low-Rank Adaptation) 提供了一种参数高效微调方案:
- ✅ 显存友好:只训练少量参数(通常 <1% 的总参数量)
- ✅ 训练快速:显著减少训练时间和计算成本
- ✅ 效果可佳:在特定任务上接近全量微调效果
- ✅ 部署灵活:支持多 LoRA 动态切换,一个基座模型服务多个场景
二、LoRA 核心原理
2.1 基本思想
LoRA 的核心思想是不改变原始预训练权重,通过引入可训练的低秩分解矩阵来调整模型行为,使其适应特定任务。
2.2 工作机制
- 冻结基座模型:原始权重 W 保持不动
- 引入旁路矩阵:添加低秩矩阵 A 和 B ,其中 A 负责降维,B 负责升维
- 前向传播修改:h=Wx+BAx (原始输出 + 旁路输出)
2.3 初始化技巧
- 矩阵 B 初始化为 全 0,确保训练开始时偏移为 0,维持网络原有输出
- 矩阵 A 采用高斯初始化,保证开始学习后能够正常收敛
三、实战环境准备
3.1 硬件配置参考
| 配置 | 单卡 A100 40GB | 双卡 4090 |
|---|---|---|
| 训练时间 | 19 分 15 秒 | 8 分 12 秒 |
| 显存占用 | ~16GB | ~8GB/卡 |
| 训练参数 | 单卡 batch=1, 梯度累积=8 | 双卡 batch=1, 梯度累积=8 |
💡 结论:双卡训练不仅更快,而且单卡显存压力更小,性价比更高
3.2 环境搭建
# 创建 Python 3.11 环境 conda create -n lora python=3.11 -y conda activate lora # 安装核心依赖(注意版本匹配) pip install torch==2.7.1 pip install transformers==4.57.1 pip install datasets==4.0.0 pip install peft==0.17.0 pip install accelerate==1.11.0 pip install deepspeed==0.18.2 pip install bitsandbytes==0.48.2 pip install vllm==0.10.0 pip install trl==0.18.0 pip install modelscope # 国内下载模型必备3.3 下载 Qwen3-4B 模型
# download.py from modelscope import snapshot_download model_dir = snapshot_download( "Qwen/Qwen3-4B", cache_dir="./Qwen3-4B" ) print('model_dir:', model_dir)3.4 安装 LLaMA-Factory 框架
git clone -b v0.9.4 --depth 1 https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip install --no-deps -e .验证安装:
llamafactory-cli version # 输出:Welcome to LLaMAFactory, version 0.9.4🍎安装成功会显示如下信息:
(Lora_py311) root@feadc3bc4205:/workspace/LLaMA-Factory# llamafactory-cli version ---------------------------------------------------------- | Welcome to LLaMA Factory, version 0.9.4 | | | | Project page: https://github.com/hiyouga/LLaMA-Factory | ----------------------------------------------------------四、数据准备:医疗问答数据集
4.1 数据格式规范
LLaMA-Factory 使用 Alpaca 格式,每个样本包含 instruction、input、output 三个字段:
[ { "instruction": "现在你是一个颅咽管瘤医生,请根据患者的问题给出建议:", "input": "颅咽管瘤手术成功率到底有多少?在川医手术切除后1个月之久,总是未见好转...", "output": "可以手术,但是成功率很小。可以做放射治疗。" }, { "instruction": "现在你是一个前列腺癌医生,请根据患者的问题给出建议:", "input": "男性如何预防前列腺癌疾病?", "output": "前列腺癌的发生,是环境污染、遇事心态、诸多不良的生活习惯..." } ]4.2 数据预处理
如果你的原始数据是 JSONL 格式,需要先转换:
import json output = [] with open('train_medical.json', 'r', encoding='utf-8') as f: for line in f.readlines(): line = line.strip() data = json.loads(line) output.append(data) print(f"总样本数: {len(output)}") with open('alpaca_zh_medical.json', 'w', encoding='utf-8') as f: json.dump(output, f, ensure_ascii=False, indent=2)4.3 注册数据集
修改 data/dataset_info.json,添加你的数据集:
{ "alpaca_zh_medical": { "file_name": "alpaca_zh_medical.json" } }五、配置 LoRA 微调参数
5.1 核心配置文件 qwen3_lora_sft.yaml
### model 模型配置 model_name_or_path: /workspace/Qwen3-4B/Qwen/Qwen3-4B # 本地模型路径 trust_remote_code: true ### method 训练方法配置 stage: sft # 监督微调阶段 do_train: true finetuning_type: lora # 使用 LoRA 微调 lora_rank: 8 # LoRA 秩,通常 4-64 lora_target: all # 应用到所有线性层(q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj) ### dataset 数据集配置 dataset: alpaca_zh_medical # 使用我们准备的医疗数据集 template: qwen3_nothink # Qwen3 模板,无思考链模式 cutoff_len: 2048 # 最大序列长度 max_samples: 1000 # 训练样本数限制 preprocessing_num_workers: 16 # 数据预处理进程数 ### output 输出配置 output_dir: saves/qwen3-4b/lora/sft logging_steps: 10 # 每 10 步记录日志 save_steps: 500 # 每 500 步保存检查点 plot_loss: true # 绘制损失曲线 overwrite_output_dir: true ### train 训练参数 per_device_train_batch_size: 1 # 单卡 batch size gradient_accumulation_steps: 8 # 梯度累积,等效 batch size = 8 learning_rate: 1.0e-4 # 学习率 num_train_epochs: 3.0 # 训练轮数 lr_scheduler_type: cosine # 余弦退火调度 warmup_ratio: 0.1 # 预热比例 bf16: true # 使用 bfloat16 混合精度 ddp_timeout: 180000000 # 分布式训练超时时间5.2 关键参数解析
| 参数 | 说明 | 建议值 |
|---|---|---|
lora_rank | 低秩矩阵的秩,越大表达能力越强 | 4-64,医疗任务建议 8-16 |
lora_target | 目标模块 | all 或指定具体投影层 |
learning_rate | LoRA 层学习率 | 1e-4 ~ 5e-4,比全量微调大 |
gradient_accumulation_steps | 梯度累积步数 | 根据显存调整,保持有效 batch size ≥ 8 |
六、启动训练
6.1 单卡训练(A100)
llamafactory-cli train examples/train_lora/qwen3_lora_sft.yaml训练日志关键信息:
trainable params: 16,515,072 || all params: 4,038,983,168 || trainable%: 0.4089 Num examples = 1,000 Num Epochs = 3 Total optimization steps = 375 Training completed. train_loss = 2.4426 train_runtime = 1155.54 seconds (19分15秒)🎯 观察点:仅 0.41% 的参数参与训练,却能让模型掌握医疗领域知识!
完整训练日志如下:
(lora) ➜ LLaMA-Factory git:(v0.9.4) ✗ llamafactory-cli train examples/train_lora/qwen3_lora_sft.yaml /root/.pyenv/versions/3.11.1/lib/python3.11/site-packages/jieba/_compat.py:18: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81. import pkg_resources [INFO|2026-02-04 06:06:31] llamafactory.hparams.parser:465 >> Process rank: 0, world size: 1, device: cuda:0, distributed training: False, compute dtype: torch.bfloat16 [INFO|tokenization_utils_base.py:2093] 2026-02-04 06:06:31,695 >> loading file vocab.json [INFO|tokenization_utils_base.py:2093] 2026-02-04 06:06:31,695 >> loading file merges.txt [INFO|tokenization_utils_base.py:2093] 2026-02-04 06:06:31,695 >> loading file tokenizer.json [INFO|tokenization_utils_base.py:2093] 2026-02-04 06:06:31,695 >> loading file added_tokens.json [INFO|tokenization_utils_base.py:2093] 2026-02-04 06:06:31,695 >> loading file special_tokens_map.json [INFO|tokenization_utils_base.py:2093] 2026-02-04 06:06:31,695 >> loading file tokenizer_config.json [INFO|tokenization_utils_base.py:2093] 2026-02-04 06:06:31,695 >> loading file chat_template.jinja [INFO|tokenization_utils_base.py:2364] 2026-02-04 06:06:32,030 >> Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained. [INFO|configuration_utils.py:763] 2026-02-04 06:06:32,031 >> loading configuration file /workspace/Qwen3-4B/Qwen/Qwen3-4B/config.json [INFO|configuration_utils.py:839] 2026-02-04 06:06:32,033 >> Model config Qwen3Config { "architectures": [ "Qwen3ForCausalLM" ], "attention_bias": false, "attention_dropout": 0.0, "bos_token_id": 151643, "dtype": "bfloat16", "eos_token_id": 151645, "head_dim": 128, "hidden_act": "silu", "hidden_size": 2560, "initializer_range": 0.02, "intermediate_size": 9728, "layer_types": [ "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention", "full_attention",