首页 / 资讯中心 / 文章详情

中文医疗问答机器人实战:LoRA微调与检索增强

中文医疗问答机器人实战:LoRA微调与检索增强 ★ FEATURED ARTICLE
简介这份资源是一个基于大模型微调的中文医疗问答机器人应用面向希望将大语言模型落地到垂直医疗场景的开发者与学习者帮助解决通用模型在医疗领域回答不专业、术语不准确等问题。压缩包共11个文件约539KB包含4个Python脚本用于应用主逻辑与命令行交互4张PNG图片用于界面头像与演示素材另有requirements.txt依赖清单、README说明文档及配置文件结构紧凑、便于快速运行与二次开发。目前已有177人学习下载。通过该资源读者可以了解医疗问答机器人的整体工程组织方式掌握大模型微调应用的基本流程并参考配置与依赖管理思路完成本地环境搭建适合具备一定Python与自然语言处理基础、希望探索AI大模型行业落地方案的人群。1. 中文医疗问答机器人为什么通用大模型直接上会翻车把通用大模型直接接到医疗问答场景十有八九会在第一周就被打回重做。原因不玄学通用模型见过海量网页但没见过你手里那份科室级的诊疗规范、药品说明书和随访问答记录问它「二甲双胍肾功能不全怎么调剂量」它可能给你一段听起来很顺、但和最新说明书对不上的回答。中文医疗问答机器人要解决的就是让模型在中文语境下稳定输出符合本地诊疗习惯的答案而不是背教科书。这个方向适合三类人手里有科室问答语料、想做内部辅助工具的工程师想用 LoRA 微调跑通一个垂直领域 LLM 应用的学生或转行者以及需要把大模型私有化部署、数据不出内网的团队。核心链路是「中文医疗语料清洗 → 指令微调 → 检索增强 → 安全兜底」本文按这条链路拆开讲每一步都给能抄的命令和参数。2. 语料与基座选型中文医疗问答的数据从哪来、模型选哪个2.1 医疗问答语料的三种来源与清洗底线做中文医疗问答机器人语料质量决定上限。常见来源有三类公开中文医疗问答数据集问答对形式适合冷启动、科室内部随访问答记录最贴业务但噪声大、药品说明书与诊疗指南结构化程度高适合做知识库而非微调语料。我一般把前两类混着用第三类留给检索增强。清洗底线只有四条去掉患者姓名、身份证、手机号等隐私字段去掉「请到院就诊」这类无信息量的套话把口语化提问改写成完整问句同一问题多个答案时保留最具体的那条。下面是一段可复用的清洗脚本骨架。import re import json # 隐私字段正则命中即整条丢弃 PII_PATTERNS [ r\d{17}[\dXx], # 身份证 r1[3-9]\d{9}, # 手机号 r[\u4e00-\u9fa5]{2,4}(先生|女士|同志), # 称呼姓名 ] def is_clean(text: str) - bool: for p in PII_PATTERNS: if re.search(p, text): return False # 过滤过短或纯套话样本 if len(text) 8: return False if 请到院 in text or 建议就医 in text: return False return True def build_sft_sample(q: str, a: str) - dict: # 统一成指令微调格式方便后续套 chat template return { instruction: q.strip(), input: , output: a.strip() } if __name__ __main__: raw [{q: 血糖高怎么办, a: 请到院就诊}, {q: 二甲双胍怎么吃, a: 随餐服用具体剂量遵医嘱调整。}] cleaned [build_sft_sample(r[q], r[a]) for r in raw if is_clean(r[q]) and is_clean(r[a])] with open(sft_clean.jsonl, w, encodingutf-8) as f: for item in cleaned: f.write(json.dumps(item, ensure_asciiFalse) \n)逻辑说明is_clean先做隐私和套话过滤build_sft_sample把问答对转成统一的 instruction/output 结构方便后面直接套 Qwen 或同类中文基座的 chat template。参数上len(text) 8这个阈值按你的语料调整医疗短问句多的话可以降到 5PII_PATTERNS建议按实际数据再补几条比如住院号、医保卡号。提示清洗后的语料一定要人工抽检 50100 条机器过滤永远会漏医疗场景漏一条隐私就是事故。2.2 基座模型怎么选中文能力、显存和许可三条线选基座看三件事中文医疗语料覆盖度、单卡能不能微调、许可证能不能商用。7B 级别是当前中文医疗问答最稳的起点单张 24G 显存卡用 LoRA 就能跑量化后 16G 也能试。13B 以上效果通常更好但微调和推理成本翻倍除非你有 A100 级别的卡否则不建议一上来就冲。考量维度7B 级别13B 及以上中文医疗问答冷启动效果够用需语料补更好语料需求略低LoRA 微调显存约 1624G约 40G 起推理延迟单卡低明显升高适合场景内部辅助、原型验证对外服务、高准确率要求选型时还要确认基座的上下文长度。医疗问答经常要带一段病史或说明书上下文太短会截断关键信息常见做法是选支持 8K 以上上下文的基座微调时把max_length设到 10242048 之间既覆盖长问答又不至于爆显存。2.3 指令格式与 chat template 对齐微调前必须把语料套成基座自己的 chat template否则模型学到的格式和推理时不一致表现会明显掉。以常见的中文对话基座为例模板大致是 system/user/assistant 三段。下面是把清洗后的 jsonl 套模板的写法。from transformers import AutoTokenizer tokenizer AutoTokenizer.from_pretrained(your-base-model-path) def format_sample(sample: dict) - str: messages [ {role: system, content: 你是一名严谨的中文医疗问答助手回答需基于已知医学知识不确定时明确说明。}, {role: user, content: sample[instruction]}, {role: assistant, content: sample[output]}, ] # apply_chat_template 会自动处理特殊 token return tokenizer.apply_chat_template(messages, tokenizeFalse) if __name__ __main__: s {instruction: 二甲双胍怎么吃, output: 随餐服用剂量遵医嘱。} print(format_sample(s))逻辑说明apply_chat_template负责插入基座要求的特殊 tokensystem 段固定成医疗助手的角色设定这一步别省。参数上system 内容要和你推理时用的完全一致否则等于白微调如果基座不支持 system 角色就把它并进第一条 user 消息里。3. LoRA 微调实战从配置到跑通第一个中文医疗问答模型3.1 LoRA 参数怎么设rank、alpha 和 target_modulesLoRA 微调的核心是只训练一小部分低秩矩阵冻结原模型权重。三个关键参数rrank控制新增参数量医疗问答这种垂直任务一般 816 就够语料上万条可以上 32lora_alpha通常设成r的 2 倍target_modules决定挂在哪几层常见做法是挂 attention 的 q/k/v/o 四个投影层效果和成本比较平衡。from peft import LoraConfig lora_config LoraConfig( r16, # 低秩维度垂直任务 8~32 lora_alpha32, # 一般取 r 的 2 倍 target_modules[q_proj, k_proj, v_proj, o_proj], lora_dropout0.05, # 小语料防过拟合 biasnone, task_typeCAUSAL_LM, )逻辑说明r越大拟合能力越强但越容易过拟合医疗问答语料通常几千到几万条r16是稳妥起点。lora_dropout0.05在语料少于 5000 条时建议保留语料充足可以设 0。target_modules的名字要和你选的基座实际层名对上写错会直接报找不到模块。3.2 训练脚本与关键超参训练用 HuggingFace 的Trainer或SFTTrainer都行下面给一个精简可跑的骨架重点是超参和显存相关设置。from transformers import TrainingArguments, Trainer, AutoModelForCausalLM from peft import get_peft_model import torch model AutoModelForCausalLM.from_pretrained( your-base-model-path, torch_dtypetorch.bfloat16, device_mapauto, ) model get_peft_model(model, lora_config) args TrainingArguments( output_dir./med-lora-out, per_device_train_batch_size2, # 显存不够就降到 1 gradient_accumulation_steps8, # 等效 batch 2*8 16 learning_rate2e-4, # LoRA 常用 1e-4 ~ 3e-4 num_train_epochs3, lr_scheduler_typecosine, warmup_ratio0.03, logging_steps10, save_strategyepoch, bf16True, # 卡支持 bf16 就开 gradient_checkpointingTrue, # 省显存慢一点 ) trainer Trainer(modelmodel, argsargs, train_datasettrain_ds) trainer.train() model.save_pretrained(./med-lora-adapter)逻辑说明per_device_train_batch_size乘gradient_accumulation_steps是等效 batch显存紧张就减前者、加后者。learning_rate2e-4是 LoRA 的常见区间太高会训崩、太低学不动。gradient_checkpointingTrue用时间换显存24G 卡跑 7B 基本靠它。num_train_epochs3是起点语料少容易过拟合就降到 2。注意训练日志里重点看 loss 曲线如果前 100 步 loss 不降先查 chat template 是否对齐、学习率是否过大别急着加数据。3.3 合并权重与推理验证训练完得到的是 adapter推理时可以动态加载也可以合并进基座。动态加载灵活合并后部署简单。from peft import PeftModel from transformers import AutoModelForCausalLM, AutoTokenizer base AutoModelForCausalLM.from_pretrained(your-base-model-path, torch_dtypetorch.bfloat16, device_mapauto) model PeftModel.from_pretrained(base, ./med-lora-adapter) model model.merge_and_unload() # 合并权重方便后续部署 tokenizer AutoTokenizer.from_pretrained(your-base-model-path) prompt 二甲双胍肾功能不全怎么调剂量 inputs tokenizer.apply_chat_template( [{role: user, content: prompt}], add_generation_promptTrue, return_tensorspt ).to(model.device) out model.generate(inputs, max_new_tokens256, temperature0.3, do_sampleTrue) print(tokenizer.decode(out[0][inputs.shape[1]:], skip_special_tokensTrue))逻辑说明merge_and_unload把 LoRA 权重合进基座之后就是一个普通模型部署时不用再带 PEFT。temperature0.3让医疗回答更稳定别用默认的 1.0否则同一问题每次答案都不一样。max_new_tokens256对多数问答够用长回答场景再调大。4. 检索增强与安全兜底让答案有出处、不越界4.1 用向量检索给回答挂上知识出处微调让模型学会医疗问答的语气和格式但具体药品剂量、诊疗细节还是靠检索更可靠。常见做法是把药品说明书、诊疗指南切块后建向量库提问时先检索 top-k 片段拼进 prompt 再让模型回答。# 伪代码示意向量库用常见方案即可 def answer_with_rag(question: str, retriever, model, tokenizer, top_k3): docs retriever.search(question, top_ktop_k) context \n.join(d[text] for d in docs) prompt f参考资料\n{context}\n\n请基于以上资料回答{question} inputs tokenizer.apply_chat_template( [{role: user, content: prompt}], add_generation_promptTrue, return_tensorspt ).to(model.device) out model.generate(inputs, max_new_tokens256, temperature0.3) return tokenizer.decode(out[0][inputs.shape[1]:], skip_special_tokensTrue)逻辑说明top_k3是起点太多会挤占上下文还引入噪声太少可能漏关键信息。切块大小常见 300500 字重叠 50 字保证语义完整。检索到的片段要保留来源方便回答里标注出处。4.2 拒答与免责医疗场景的安全底线医疗问答机器人必须会拒答。遇到急症描述、明确用药剂量请求、超出知识范围的问题要给出统一的安全回复而不是硬编一个答案。常见做法是在 system prompt 里写死规则再在输出后做一层关键词过滤。触发类型处理方式急症关键词胸痛、呼吸困难等直接回复建议立即就医具体处方剂量请求回复需遵医嘱不给出具体数值检索无相关内容明确说明不确定建议咨询医生提示安全兜底要放在模型输出之后再做一次模型可能绕过 system 规则后置过滤是最后一道防线。4.3 评测怎么判断微调到底有没有用别只看几个样例就下结论。准备 100200 条带参考答案的测试问答从三个维度打分答案准确性是否和参考一致、安全性有没有越界回答、格式合规是否符合助手语气。微调前后各跑一遍准确率提升低于 5 个百分点就要回头查语料和参数。5. 避坑与排查中文医疗问答微调最常见的 5 个翻车现场现象一训练 loss 正常下降但推理时答非所问。原因多半是 chat template 没对齐训练和推理用了不同格式。解决把训练时apply_chat_template的输出打印出来和推理时的输入逐字符对比system 段必须完全一致。现象二模型开始编造药品剂量。原因是微调语料里混入了不准确的剂量信息模型照单全收。解决语料里所有剂量相关样本必须人工核对拿不准的直接删宁可少不可错。现象三显存溢出训练跑不起来。常见于 batch size 设太大或没开 gradient checkpointing。解决per_device_train_batch_size降到 1开gradient_checkpointingTrue序列长度从 2048 降到 1024 试。现象四模型对同一问题每次回答都不一样。推理时temperature用了默认值。解决医疗问答把temperature设到 0.10.3需要更稳定就设 0 走贪心解码。现象五微调后通用能力明显下降。语料太单一或训练轮数太多导致灾难性遗忘。解决num_train_epochs降到 2语料里掺 10%20% 的通用中文问答LoRA 的r别设太大。6. 进阶技巧把中文医疗问答机器人做成能长期维护的工具跑通第一个版本只是开始真正决定这个方向值不值得投入的是它能不能持续维护。我自己的习惯是给每次微调都留一份「训练档案」语料版本、LoRA 参数、评测分数、已知问题全部记在一个 markdown 里。下次换基座或加语料时直接对比档案不用凭记忆猜上次为什么效果好。进阶上可以试两件事。一是把 LoRA 权重按科室拆开内科、儿科各训一个 adapter推理时按问题路由比一个大模型硬扛所有科室更准也更好维护。二是做增量语料回流把线上答得不好的问题定期捞出来人工修正后补进下一轮训练集形成闭环。下面是一个简单的评测对比表模板每次迭代填一行。版本语料条数r准确率安全违规数备注v13200871%3冷启动v258001679%1补了说明书语料v358001682%0加了检索增强验证方法上除了固定测试集我还会每周抽 20 条真实提问做盲测只看答案能不能让非医学背景的人看懂、有没有明显错误。这一步比跑分更接近真实使用。血泪经验是别等模型「完美」再上线先在小范围内部用起来收集真实问题比闭门调参快得多。医疗问答这个方向数据闭环比模型大小重要安全兜底比效果惊艳重要。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站