法律大模型微调实战:Qwen2.5-7B-Instruct 与 LLaMA-Factory 快速入门

发布时间:2026/9/24 20:49:42
法律大模型微调实战:Qwen2.5-7B-Instruct 与 LLaMA-Factory 快速入门 简介这份资源面向自然语言处理入门与进阶开发者聚焦大语言模型在法律垂直领域的微调实践解决通用模型难以准确理解法律术语与案例逻辑的问题。包内共11个文件以yaml训练配置、jsonl指令数据集、py推理脚本为主辅以docx与txt说明文档压缩包约35KB体量轻便便于快速上手。资源基于Qwen2.5-7B-Instruct架构配合LLaMA-Factory框架提供LoRA、QLoRA及合并权重三套训练配置并内置DISC-Law-SFT-Pair、Triplet等专业法律数据集覆盖法律问答与案例分析场景。读者可据此复现从数据准备、参数配置到模型微调与对话测试的完整流程理解不同微调策略的取舍并借助说明文档与附赠资料排查常见问题。目前已有97人学习适合希望掌握领域微调方法、积累法律AI项目经验的中高级开发者参考。1. 法律大模型微调为什么 Qwen2.5-7B-Instruct 配 LLaMA-Factory 是当前最稳的起步组合法律领域的自然语言处理NLP和通用对话完全是两回事。你拿一个原版 Qwen2.5-7B-Instruct 去问“民间借贷利率超过 LPR 四倍的部分法院怎么处理”它大概率给你一段听起来很对、但引用条文编号错得离谱的回答。原因不复杂通用指令微调阶段喂进去的法律语料占比极低模型对法条编号、司法解释层级、裁判文书说理结构的“肌肉记忆”几乎为零。而 DISC-Law-SFT-Pair 这类专业法律数据集恰恰补的就是这块肌肉。这篇要讲清楚一件事用 LLaMA-Factory 框架把 Qwen2.5-7B-Instruct 在 DISC-Law-SFT-Pair 上做一轮监督微调SFT让模型在法律问答场景下从“能聊”变成“敢用”。适合两类人一是手里有法律业务场景、想验证微调到底能不能带来可感知提升的工程师二是刚接触大语言模型微调、想找一个数据格式规整、框架成熟、显存门槛可控的实战入口的 NLP 方向学习者。整套流程在单卡 24GB 显存上就能跑通不需要多机多卡也不需要自己写训练循环。2. 环境搭建与 LLaMA-Factory 的安装配置从零到能跑训练2.1 为什么选 LLaMA-Factory 而不是自己写训练脚本自己写 SFT 训练脚本不是不行但你会很快发现时间花在三个地方数据格式转换、显存优化配置、训练中断恢复。LLaMA-Factory 把这三件事都做成了配置项。它支持 Qwen2.5 系列的原生对话模板内置了 LoRA、QLoRA、全量微调等多种模式而且对 DeepSpeed、FlashAttention 的集成是开箱即用的。对于法律领域微调这种“数据量中等、显存有限、需要快速迭代”的场景用 LLaMA-Factory 能让你把精力放在数据质量和评测上而不是跟 CUDA OOM 搏斗。另一个实际考量是社区活跃度。LLaMA-Factory 对 Qwen2.5 的支持在官方仓库里更新很及时你不需要自己去改 modeling 文件来适配 chat template。法律数据集 DISC-Law-SFT-Pair 的格式是标准的 instruction/input/output 三元组LLaMA-Factory 的dataset_info.json注册方式可以直接吃进去省掉写预处理脚本的环节。2.2 安装步骤与关键依赖版本先建一个干净的 Python 环境。我一般用 conda因为后面装 flash-attn 和 deepspeed 时依赖冲突少一些。conda create -n law-sft python3.10 -y conda activate law-sft # 安装 PyTorch注意 CUDA 版本要和驱动匹配 pip install torch2.4.0 torchvision0.19.0 torchaudio2.4.0 --index-url https://download.pytorch.org/whl/cu121 # 克隆 LLaMA-Factory 并安装 git clone https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip install -e .[torch,metrics] # 可选但强烈建议安装 flash-attn 加速训练 pip install flash-attn --no-build-isolation这里有几个参数值得说清楚。torch2.4.0配cu121是当前和 Qwen2.5 兼容性最稳的组合再新的版本有时会在transformers的Qwen2ForCausalLM加载时报一些奇怪的 attention mask 警告。pip install -e .[torch,metrics]里的metrics会装上evaluate和scikit-learn后面做评测用得上。flash-attn 不是必须的但在 7B 模型 2048 序列长度下开启后训练速度大概能提升 20% 到 30%显存占用也会降一点。安装完成后验证一下python -c import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0)) llamafactory-cli version如果torch.cuda.is_available()返回 False先检查驱动和 CUDA 版本不要急着往下走。llamafactory-cli能打印版本号说明框架安装成功。2.3 下载 Qwen2.5-7B-Instruct 权重与目录规划权重下载用huggingface-cli或者modelscope都行国内网络环境用 modelscope 更顺滑。# 用 modelscope 下载国内推荐 pip install modelscope modelscope download --model Qwen/Qwen2.5-7B-Instruct --local_dir ./models/Qwen2.5-7B-Instruct下载完成后你的工作目录建议这样组织law-sft-project/ ├── models/ │ └── Qwen2.5-7B-Instruct/ ├── data/ │ └── disc-law-sft-pair.json ├── output/ │ └── qwen2.5-7b-law-lora/ └── configs/ └── qwen_law_sft.yaml把模型权重、数据集、输出目录、训练配置分开后面做多组实验时不会乱。output目录下每次训练会生成 checkpointLoRA 权重通常只有几十到几百 MB方便对比不同超参的效果。3. DISC-Law-SFT-Pair 数据集处理与 LLaMA-Factory 注册3.1 数据集结构解析与格式对齐DISC-Law-SFT-Pair 是法律领域指令微调数据集每条样本通常包含instruction、input、output三个字段。instruction是任务描述比如“请根据以下案情分析违约责任归属”input是具体案情或问题output是期望的模型回答通常包含法条引用和说理逻辑。LLaMA-Factory 的alpaca格式要求字段名就是instruction、input、output所以如果你的原始数据字段名一致几乎不需要转换。但实际拿到的数据可能有嵌套或字段名差异比如有的版本用question和answer。这时候写一个轻量转换脚本import json def convert_to_alpaca(raw_path, save_path): with open(raw_path, r, encodingutf-8) as f: raw_data json.load(f) alpaca_data [] for item in raw_data: # 字段映射根据实际数据结构调整 alpaca_data.append({ instruction: item.get(instruction, item.get(question, )), input: item.get(input, ), output: item.get(output, item.get(answer, )) }) with open(save_path, w, encodingutf-8) as f: json.dump(alpaca_data, f, ensure_asciiFalse, indent2) print(f转换完成共 {len(alpaca_data)} 条样本) convert_to_alpaca(data/raw_disc_law.json, data/disc-law-sft-pair.json)这段脚本的核心逻辑是字段名归一化。ensure_asciiFalse保证中文不被转义成 Unicode 码点方便你直接打开文件检查内容。转换后务必抽查几条确认output字段不是空的也确认没有把input和instruction搞反。法律数据里经常出现“instruction 是长案情、input 为空”的情况这本身没问题但如果你把长案情塞进input而instruction只写“请回答”模型学到的就是“看到短指令就输出长文本”泛化会变差。3.2 在 dataset_info.json 中注册自定义数据集LLaMA-Factory 通过data/dataset_info.json来识别数据集。你需要在这个文件里加一条记录{ disc_law_sft_pair: { file_name: disc-law-sft-pair.json, formatting: alpaca, columns: { prompt: instruction, query: input, response: output } } }这里formatting选alpaca是最省事的因为字段名已经对齐。columns里的映射告诉框架哪一列对应 prompt、哪一列对应 query、哪一列对应 response。注意query对应的是input如果某条样本input为空框架会自动处理成只有 instruction 的 prompt不会报错。注册完成后可以用 LLaMA-Factory 自带的数据集检查命令验证llamafactory-cli dataset info disc_law_sft_pair如果输出里能看到样本数量和字段示例说明注册成功。这一步看起来简单但很多人卡在file_name路径上——它默认相对于data目录所以你的 json 文件必须放在LLaMA-Factory/data/下面或者你在配置里写绝对路径。3.3 数据质量筛查法律数据集特有的三个坑法律数据集的质量问题比通用数据集更隐蔽。通用数据里回答得不好你一眼能看出来法律数据里法条编号写错一位外行根本发现不了。我一般会做三件事第一检查output里引用的法条编号是否在有效范围内。比如《民法典》合同编是第 463 条到第 988 条如果输出里出现“根据《民法典》第 1200 条”那大概率是模型幻觉或数据标注错误。写个正则批量扫一遍import re def check_article_range(text, min_article1, max_article1260): pattern r第(\d)条 articles [int(m) for m in re.findall(pattern, text)] invalid [a for a in articles if a min_article or a max_article] return invalid with open(data/disc-law-sft-pair.json, r, encodingutf-8) as f: data json.load(f) for i, item in enumerate(data): invalid check_article_range(item[output]) if invalid: print(f样本 {i} 存在可疑法条编号: {invalid})第二检查output长度分布。如果大部分样本输出只有一两句话模型学不到完整的说理结构如果个别样本输出超过 3000 字训练时会被截断反而引入噪声。用numpy算一下分位数把超过 95 分位数的样本单独拿出来看。第三检查重复样本。法律问答里“民间借贷利率上限”这类高频问题可能重复出现多次如果重复样本的答案不一致模型会困惑。用output的哈希去重保留第一条即可。4. LoRA 微调配置与训练启动参数怎么设、显存怎么省4.1 用 YAML 配置文件组织训练参数LLaMA-Factory 支持命令行传参但参数一多就容易写错。我习惯用 YAML 配置文件清晰且可复用。在configs/下新建qwen_law_sft.yamlmodel_name_or_path: ./models/Qwen2.5-7B-Instruct stage: sft do_train: true finetuning_type: lora lora_target: all lora_rank: 16 lora_alpha: 32 lora_dropout: 0.05 dataset: disc_law_sft_pair template: qwen cutoff_len: 2048 max_samples: 10000 overwrite_cache: true preprocessing_num_workers: 8 output_dir: ./output/qwen2.5-7b-law-lora logging_steps: 10 save_steps: 500 plot_loss: true overwrite_output_dir: true per_device_train_batch_size: 2 gradient_accumulation_steps: 8 learning_rate: 1.0e-4 num_train_epochs: 3.0 lr_scheduler_type: cosine warmup_ratio: 0.1 bf16: true gradient_checkpointing: true flash_attn: fa2逐项说关键参数。lora_target: all表示对所有线性层加 LoRA法律领域任务对模型各层都有调整需求只调 q_proj 和 v_proj 效果通常不够。lora_rank: 16是平衡效果和显存的常用值法律数据量如果超过 5 万条可以提到 32 或 64。template: qwen必须写对Qwen2.5 的 chat template 和 Qwen1.5 不同写错会导致 prompt 格式错乱模型学不到东西。cutoff_len: 2048是法律场景的甜点值。法律问答的输入输出加起来通常不超过 1500 token2048 留了余量同时显存占用可控。per_device_train_batch_size: 2配合gradient_accumulation_steps: 8等效 batch size 是 16在 24GB 显存上跑 7B LoRA 比较稳。learning_rate: 1.0e-4是 LoRA 微调的常用起点全量微调要降到 1e-5 级别。4.2 启动训练与显存监控配置文件写好后一条命令启动llamafactory-cli train configs/qwen_law_sft.yaml训练启动后另开一个终端用nvidia-smi -l 2监控显存。正常情况下 24GB 卡会用到 18GB 到 22GB。如果直接 OOM按这个顺序调先把per_device_train_batch_size降到 1再把cutoff_len降到 1536最后考虑把lora_rank降到 8。gradient_checkpointing: true已经开了这个对显存帮助很大但会牺牲约 15% 的速度。训练日志里重点关注loss曲线。法律 SFT 的 loss 通常从 1.5 左右开始下降3 个 epoch 后降到 0.6 到 0.8 之间算正常。如果 loss 降到 0.3 以下大概率过拟合了需要减少 epoch 或增加数据量。如果 loss 震荡不降检查template是否写对以及数据里是否有大量空output。4.3 训练中断恢复与多组实验管理LLaMA-Factory 默认每save_steps存一个 checkpoint。如果训练中断把配置里的resume_from_checkpoint指向最新 checkpoint 目录即可续跑。做多组实验时我习惯在output_dir里带上关键参数比如qwen2.5-7b-law-lora-r16-lr1e4这样对比结果时一目了然。另外max_samples: 10000是我建议第一次跑时设的。DISC-Law-SFT-Pair 全量可能更大但第一轮先用 1 万条验证流程和效果确认 loss 正常下降、推理结果有改善后再放开全量数据跑第二轮。这样能避免花几个小时训练完才发现数据格式有问题。5. 微调效果验证与推理测试怎么判断模型真的学到了法律知识5.1 合并 LoRA 权重并加载推理训练完成后output目录下会有多个 checkpoint。选最后一个或验证 loss 最低的用 LLaMA-Factory 的export命令合并 LoRA 权重到基础模型llamafactory-cli export \ --model_name_or_path ./models/Qwen2.5-7B-Instruct \ --adapter_name_or_path ./output/qwen2.5-7b-law-lora/checkpoint-1500 \ --template qwen \ --finetuning_type lora \ --export_dir ./models/qwen2.5-7b-law-merged \ --export_size 2 \ --export_legacy_format falseexport_size: 2表示分片大小为 2GB方便后续加载。合并后的模型可以直接用transformers加载推理from transformers import AutoModelForCausalLM, AutoTokenizer model_path ./models/qwen2.5-7b-law-merged tokenizer AutoTokenizer.from_pretrained(model_path, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained( model_path, torch_dtypeauto, device_mapauto, trust_remote_codeTrue ) prompt 民间借贷中约定的利率超过合同成立时一年期LPR四倍法院一般怎么处理 messages [{role: user, content: prompt}] text tokenizer.apply_chat_template(messages, tokenizeFalse, add_generation_promptTrue) inputs tokenizer(text, return_tensorspt).to(model.device) outputs model.generate(**inputs, max_new_tokens512, temperature0.3, do_sampleTrue) response tokenizer.decode(outputs[0][inputs[input_ids].shape[1]:], skip_special_tokensTrue) print(response)temperature: 0.3是法律问答场景的推荐值太低会重复太高会引入幻觉。max_new_tokens: 512足够覆盖大多数法律回答如果输出被截断说明模型没学会收敛需要检查训练数据里是否有大量超长输出。5.2 构建法律评测集不要只看 lossLoss 下降不代表模型在法律任务上变好了。你需要一个独立的评测集至少覆盖三类任务法条检索问某行为违反哪条法律、案例分析给案情问责任归属、程序问答问诉讼流程或时效。从 DISC-Law-SFT-Pair 里留出 200 条不参与训练作为验证集。评测指标不用太复杂人工看 50 条就能有感觉。我一般按三个维度打分法条引用是否正确、说理逻辑是否完整、是否有明显幻觉。微调后的模型在法条引用准确率上通常能从基座的 40% 左右提升到 75% 以上说理完整性提升更明显。如果提升不明显优先检查数据质量而不是调超参。5.3 和基座模型做 A/B 对比的实操方法把同一个问题分别喂给基座模型和微调后模型并排看输出。建议选 20 个高频法律问题覆盖合同、侵权、婚姻、劳动四个领域。对比时关掉随机性do_sampleFalse这样差异完全来自模型本身。一个常见的观察是基座模型倾向于给出“一般性法律原则”比如“应当遵循公平原则”微调后模型会具体到“根据《民法典》第 680 条禁止高利放贷借款的利率不得违反国家有关规定”。这种从“泛泛而谈”到“有条文支撑”的转变就是法律 SFT 的核心价值。6. 法律微调避坑记录从数据到部署的五个血泪教训6.1 坑一template 写错导致 loss 正常但推理胡言乱语现象训练 loss 从 1.4 降到 0.7看起来一切正常但推理时模型输出格式混乱有时把用户问题重复一遍就结束。原因Qwen2.5 的 chat template 和 Qwen2 不同如果 YAML 里template写成qwen但实际加载的是旧版 tokenizer或者写成了defaultprompt 的 special token 位置就错了。模型在训练时看到的输入格式和推理时不一致学到的映射关系完全错位。解决确认template: qwen并且在训练前用tokenizer.apply_chat_template打印一条样本的完整 prompt肉眼检查|im_start|和|im_end|是否出现在正确位置。这个检查花两分钟能省几小时返工。6.2 坑二数据里混入空 output 导致模型学会“沉默”现象微调后模型对某些问题直接输出空字符串或只输出一个句号。原因DISC-Law-SFT-Pair 原始数据里可能有少量output为空或只有空白字符的样本。训练时这些样本的 loss 会被 mask 掉如果配置了train_on_inputs: false但模型仍然学到了“遇到这类 prompt 就输出空”的模式。解决在数据转换阶段过滤掉output长度小于 10 个字符的样本。加一行if len(item[output].strip()) 10: continue就能解决。6.3 坑三cutoff_len 设太大导致显存爆炸且训练变慢现象把cutoff_len设成 4096 后24GB 卡直接 OOM降到 2048 才能跑但训练速度慢了一倍。原因法律数据里确实有少量长文本但占比很低。为了覆盖这些长尾样本把cutoff_len翻倍显存占用是平方级增长的而大部分短样本被 padding 到 4096浪费了大量计算。解决先统计训练数据 token 长度的 95 分位数把cutoff_len设成这个值加 128 的余量。法律 SFT 数据 95 分位数通常在 1200 到 1600 之间2048 已经足够。超长样本直接截断或者单独做一轮长文本微调。6.4 坑四LoRA target 只设 q_proj 和 v_proj 导致法律知识注入不足现象微调后模型在通用对话上表现正常但法律问答的提升很有限法条引用仍然经常出错。原因只对 attention 的 q_proj 和 v_proj 加 LoRA可训练参数量太少模型没有足够的容量去学习法律领域特有的知识映射。法律 SFT 需要模型在 FFN 层也做调整因为法条记忆和说理生成更多依赖 FFN 的参数。解决lora_target: all让 LoRA 覆盖所有线性层。参数量会增加但 7B 模型 rank 16 的 LoRA 仍然只占基础模型参数的 1% 到 2%显存和速度影响可控。6.5 坑五用训练集当验证集导致过拟合而不自知现象训练 loss 降到 0.4以为效果很好但拿真实业务问题测试时发现模型在“背答案”稍微换个问法就答错。原因没有留出独立验证集或者验证集和训练集有重叠。法律问答里同一个法条可能对应多种问法如果验证集里的问题和训练集高度相似评估结果会虚高。解决按output的语义哈希去重后再划分训练集和验证集确保同一法条的不同问法不会同时出现在两边。验证集至少 200 条且人工检查过没有和训练集重复。7. 进阶技巧用多轮对话数据和拒绝采样把法律微调再推一步单轮 SFT 能让模型学会“问什么答什么”但法律咨询的真实场景是多轮追问。比如用户先问“借款利息怎么算”模型回答后用户追问“那逾期利息呢”模型需要记住上一轮的法条上下文。DISC-Law-SFT-Pair 是单轮 pair 数据要支持多轮需要自己构造多轮样本。一个实用做法是从单轮数据里挑出同一法条相关的多条 QA按逻辑顺序拼成多轮对话。比如把“民间借贷利率上限”“逾期利率”“复利计算”三条拼成一个三轮对话instruction只保留第一轮的后续轮次的input和output按对话历史格式组织。LLaMA-Factory 的sharegpt格式支持这种结构把formatting改成sharegpt用conversations字段组织多轮即可。另一个进阶方向是拒绝采样rejection sampling。用微调后的模型对一批法律问题生成多个回答然后用一个更强的模型比如 Qwen2.5-72B或者规则脚本对回答打分把高分回答作为新的训练数据再做一轮 SFT。这个过程能显著提升模型输出的法条引用准确率因为低质量回答在采样阶段就被过滤掉了。我自己的习惯是第一轮 SFT 用全量 DISC-Law-SFT-Pair 跑 3 个 epoch导出模型后做拒绝采样把采样后的高质量数据通常保留 60% 到 70%和原始数据混合再跑一轮 2 个 epoch。第二轮的学习率降到 5e-5避免破坏第一轮学到的知识。两轮下来法律问答的法条引用准确率通常能从 75% 推到 85% 以上。验证方法上除了人工看可以写一个简单的规则脚本检查输出里是否包含“根据《XX法》第XX条”的模式以及引用的法条编号是否在有效范围内。这个脚本不能替代人工评估但能快速筛出明显幻觉的样本帮你判断模型是否在“胡说八道”。最后说一个我踩过的坑不要在第一轮就追求完美。法律微调是一个迭代过程数据质量、超参、评测集都需要反复调整。先跑通全流程拿到一个能用的模型再逐步优化。希望帮到你。本文还有配套的精品资源点击获取