中医古籍问答模型黄帝:基于Ziya-LLaMA-13B微调与部署实战

发布时间:2026/10/8 11:20:51
中医古籍问答模型黄帝:基于Ziya-LLaMA-13B微调与部署实战 简介面向人工智能大模型应用与中医古籍知识问答场景的工程代码包基于Ziya-LLaMA-13B-V1底座整理出黄帝模型仓库从数据准备到训练部署的完整流程。压缩包共54个文件大小约150KB主要包含22个Python脚本、22个pyc编译文件、5个Shell启动脚本、2个YAML配置文件以及示例JSON、许可证和说明文档。Python代码覆盖SFT、PPO、RM等关键训练流程以及模板构建、数据整理、知识工具等模块Shell脚本提供中医古籍预训练、微调、评测和命令行演示的快捷入口YAML与JSON则用于默认参数和示例数据。目录下还内置网页、接口、命令行等多种推理入口便于快速体验问答效果。目前已有673人浏览学习资源虽小但结构清晰适合希望借鉴大模型微调流程、搭建中医知识问答原型的研究者和开发者也能为环境搭建、脚本调用、数据处理等环节提供直接参考。1. 为什么“黄帝”仓库值得聊一个中医古籍问答模型是怎么装进 zip 的《AI大模型应用》这类落地项目里最容易被高估的是通用对话能力最容易被低估的是领域知识对齐。基于 Ziya-LLaMA-13B-V1 微调出来的黄帝Huang-Di模型仓库把中医古籍知识问答这件事打包成了一个可以本地解压的模型包基座是在中文上继续预训练过的 13B 参数 LLaMA叠加古籍问答指令数据做指令微调最后用 zip 分发权重、配置和推理脚本。它解决的痛点是既要有大模型的语言生成能力又不能让回答变成百科式空转。适合想自己做领域大模型但不打算从零预训练、又受限于显卡预算的算法工程师和学生研究。2. Ziya-LLaMA-13B-V1 为什么适合当中医古籍底座选型逻辑与领域适配2.1 基座模型选型Ziya 在中文上古文本上的优势从哪来先解释一下 Ziya-LLaMA-13B-V1 到底是什么来头。它本质上是把 Meta 的 LLaMA-13B 拿来做中文持续预训练的产物在 LLaMA 原有英文能力基础上用大规模中文语料继续训练让模型重新校准词表和语义分布。市面上很多中文开源模型走的是同一条路Ziya 作为较早一批把这条路走通、而且把权重完整放出来的基座在 2023 年之后成了不少领域微调项目的默认起点。选它做中医古籍问答要看的不是排行榜分数而是三个实际工程指标。第一13B 参数量对显存很友好单卡 A100 40G 可以全参数训练消费级 4090 配合 LoRA 也能跑推理和轻量微调这决定了普通团队能不能玩得起。第二Ziya 在持续预训练阶段做了中文词表扩充和文本长度适配对文言文里大量单字成词、句式省略的现象分词和注意力分配比原版 LLaMA 更稳。第三它是开放权重没有商用授权上的模糊地带做学术研究和内部验证都少一层风险。但要泼一盆冷水Ziya 本身没有专门的中医知识它能做好古籍问答靠的是“知道中文长什么样 知道怎么跟着指令作答”真正的中医内容要靠后面的领域微调喂进去。这就像招了一个中文功底不错的实习生中医知识得靠入职培训补。从模型结构上看Ziya-LLaMA-13B-V1 保持了 LLaMA 的 decoder-only 架构40 层 transformer隐藏维度 5120。这个结构对长文本处理有一个隐含要求输入长度受限于训练时的最大序列长度一般在 2048 左右。中医古籍原文经常一段话就是几百字加上问句和答案很容易把上下文窗口顶满。所以后面做数据构造时我一般会把“史料原文 问题 答案”压缩在一个 1024 token 以内的单元里宁可多切几条样本也别让长文本把注意力打散。2.2 领域适配思路古籍语料、指令对和问答目标的构建把通用基座变成中医古籍问答模型标准路线是 SFT监督指令微调而不是继续预训练。原因很直接继续预训练解决的是“让模型觉得古文读起来顺”但用户要的是“问一句话得到一段有理有据的回答”。这个从补全到对话的行为转变必须靠指令数据来约束。常见做法是构造三类样本。第一类是原文翻译解释型例如问题“《伤寒论》中‘太阳之为病脉浮头项强痛而恶寒’如何理解”答案是逐句白话解释加病机分析。第二类是方剂问答型例如“桂枝汤中桂枝与芍药的配伍意义”答案需要落到具体条文和药物功效。第三类是症状辨证型例如“患者汗出恶风、脉浮缓当用何方”答案是辨证思路加方剂出处。数据格式按 instruction 风格组织每条包括 system、user、assistant 三段。黄帝仓库这一类模型仓库在分发时通常也会附带已经格式化好的 jsonl 训练数据样例但真正可用的数据量往往不够我一般建议自己再从《伤寒论》《金匮要略》《本草纲目》这类公开古籍里补充。这里有一个容易被新手忽略的点古籍问答的答案质量取决于“答案里有没有原文锚点”。同样一句“桂枝汤调和营卫”有出处的回答和模型自己编的回答在可信度上是两个量级。所以构造训练样本时我会强制要求每条 assistant 回答里至少要引用一句原文并且在训练时把引用部分用特殊标记包起来例如“出自《伤寒论》辨太阳病脉证并治”。这个做法对后面验证幻觉非常有用后面避坑章节会细说。3. 把黄帝模型在本地跑起来硬件底线、依赖配置与最小推理命令3.1 环境与硬件底线显存、CUDA 和依赖怎么配先明确一个结论跑一个 13B 的量化模型门槛比大多数人想象的低但也没有低到普通笔记本就能流畅跑的程度。我把常见部署方案按显存需求列一张表方便对照自己的机器。部署方式显存需求速度感受适用场景全精度 FP32约 52GB最慢且不现实基本不推荐FP16 / BF16约 26GB正常单卡 A100/A800 或双卡 30908bit 量化约 13-14GB略慢但可用单卡 4090 / 30904bit 量化约 7-8GB生成变慢单卡 3060 12G 赶鸭子上架我一般建议至少准备一张 24GB 显存的卡用 8bit 量化跑推理留出余量给长文本。如果只有 16GB 显存也可以跑但要把max_new_tokens调小并且用load_in_4bit后面会给出对应参数。软件环境方面Python 建议 3.10 或 3.11CUDA 用 11.8 或 12.1 均可关键是 PyTorch 和 transformers 版本要匹配。我踩过最典型的坑是 transformers 版本太老不认识模型包里自定义的 modeling 文件。处理方式是设一个干净的 conda 环境先装torch对应 CUDA 版本再装transformers、accelerate、bitsandbytes、sentencepiece。不要一次性装最新版 transformers有些老模型仓库对 4.40 的接口变动会报错。3.2 用 transformers 加载权重推理最小脚本与参数说明拿到黄帝仓库的 zip 后解压后通常能看到几个固定成员模型权重文件、config.json、tokenizer相关文件以及一个modeling_*.py的自定义模型脚本。用 transformers 加载这类仓库最小推理脚本如下import torch from transformers import AutoModelForCausalLM, AutoTokenizer model_path ./Huang-Di-13B tokenizer AutoTokenizer.from_pretrained( model_path, trust_remote_codeTrue ) model AutoModelForCausalLM.from_pretrained( model_path, torch_dtypetorch.float16, device_mapauto, trust_remote_codeTrue ) prompt 问桂枝汤中芍药的作用是什么\n答 inputs tokenizer(prompt, return_tensorspt).to(cuda) out model.generate( **inputs, max_new_tokens256, temperature0.7, top_p0.85, repetition_penalty1.05, do_sampleTrue ) print(tokenizer.decode(out[0], skip_special_tokensTrue))逻辑说明torch_dtypetorch.float16让模型以半精度加载显存减半device_mapauto由 accelerate 自动分配层到 GPU 或 CPU单卡场景最省心trust_remote_codeTrue是必须加的因为中文模型仓库普遍带自定义 modeling 文件不加会直接报“找不到类定义”的错误。生成参数里temperature0.7是我在中医问答场景下的默认值偏低会显得机械偏高容易跑偏。top_p0.85用来裁掉尾部低概率词对古文这种用词集中的文本很有效。repetition_penalty一定要设至少 1.05否则模型在生成长句时很容易把“之乎者也”重复三遍。max_new_tokens不要贪大中医答案一般三百字内够用给太大反而增加幻觉风险。这里有一个新手常犯的错直接用模型自带的generate默认参数不设do_sampleTrue。默认 greedy decoding 会让回答变得干巴巴尤其在古文场景下几乎每次都会输出最保守的模板句。把do_sampleTrue打开配合上面一组参数输出才有多样性和语气。4. 从古籍原文到训练样本SFT 数据构造与关键参数怎么定4.1 微调数据准备把古文问答转成 instruction 格式真正动手微调之前数据格式是第一道关卡。黄帝仓库这类基于 Ziya 的模型训练时用的是标准 ChatML 或 Alpaca 格式。下面是一个我在实践中验证过的指令模板转换脚本可以直接抄走改改import json def build_sample(question, answer, source): return { instruction: 你是黄帝精通中医古籍与现代临床问答。请结合古籍原文回答问题。, input: question, output: f{answer}\n原文依据{source} } samples [] samples.append(build_sample( 《伤寒论》中桂枝汤的服法提到‘服已须臾啜热稀粥’的目的是什么, 啜热稀粥目的在于资助汗源使谷气内充外合药力以助发汗解肌之力。, 《伤寒论》辨太阳病脉证并治 )) with open(train_data.jsonl, w, encodingutf-8) as f: for s in samples: f.write(json.dumps(s, ensure_asciiFalse) \n)逻辑说明build_sample把问题、答案、来源三要素组装成一个完整样本。注意我在 output 末尾强制拼接了“原文依据”这是刻意为之。训练时模型会学会这个输出习惯推理时就会主动带出处给后续做可信度校验留了抓手。ensure_asciiFalse保证中文原样写入不然打开 jsonl 会看到一堆\uXXXX逼疯调试的人。参数说明这里没有参数只有数据格式约定但格式统一比参数更重要。所有 tokenizer 的 padding 策略、attention mask 计算都依赖每条样本字段对齐。如果样本里有的带input有的不带模型训练时指令跟随能力会退化。数据量方面我的经验是中医古籍问答不是越多越好。常见做法是先拿公开的通用医疗问答做 2 万条预热再叠 5000 条高质量古籍问答做精调。5000 条看似不大但每条都带原文出处模型就能把“引经据典”这个行为学会。相反如果直接扔 50 万条无出处的医疗问答模型会变成一个泛泛的医学聊天机器人而不是古籍问答助手。4.2 关键训练参数与断点续训流程微调 13B 模型显存不够时首选 LoRA这也是当前做领域大模型的主流做法。训练脚本核心参数可以对照下面这张表来设参数我的默认值说明lora_rank64秩越大可学习参数越多但过大会丢失基座知识lora_alpha128缩放系数一般取 rank 的两倍max_length1024超过会被截断古籍长文注意预截断per_device_train_batch_size2显存紧张就设为 1gradient_accumulation_steps8等效总 batch 为 2×816learning_rate2e-5古典文本要小步走过大会毁掉基座能力num_epochs3古籍数据量少3 轮足够多了会过拟合warmup_ratio0.03前 3% 步骤让学习率从零爬升lr_scheduler_typecosine余弦退火收敛更稳训练时的经验法则不要直接改全参数。13B 全参数微调对优化器状态、梯度、激活值的显存开销极大普通团队没这个硬件条件也用不上这个能力。LoRA 只训练注入的低秩矩阵可学习参数不到总参数的 2%效果却往往能逼近全参数微调前提是学习率控制住。断点续训一定要开。我一般设--save_strategysteps --save_steps500每 500 步落一个 checkpoint。训练到一半显存溢出或者断电直接从前一个 checkpoint 续而不是从头再来。续训时注意保持per_device_train_batch_size和其他超参一致学习率调度器最好从保存时的步数继续否则 warmup 会重新开始损失曲线会有一个明显的尖峰。这里要特别提醒中医古籍文本里的标点符号、生僻字会影响 tokenizer 效率。Ziya 的分词器对古文支持不错但遇到“㽲”“瘥”这类生僻字会拆成多个 token导致有效上下文变短。预处理时我会先跑一遍分词统计把高频生僻字加入词表做 embedding 初始化。这个操作看着不起眼但能显著提升长文本情况下的回答质量。5. 本地部署黄帝模型的五个避坑记录从加载失败到幻觉泛滥5.1 加载就翻车自定义模型代码被 transformers 拒绝现象from_pretrained报错提示找不到HuangDiModel或类似的自定义类或者直接提示does not have an attribute。原因模型仓库自带modeling_huangdi.py这类自定义脚本而当前 transformers 版本默认不信任远程代码。这是设计上的安全机制不是 bug。解决加载时加trust_remote_codeTrue。如果加了还报错检查 transformers 版本是否过高部分老脚本调用的是旧版apply_chunking_to_forward之类的接口新版已移除。我遇到过一次解决办法是固定transformers4.35.2并安装依赖里的sentencepiece。5.2 回答变成复读机古文输出反复重复同一句现象模型生成的答案里某一句完整的话连续出现三遍或者“也者”“也者”这样的虚词无限循环。原因采样参数没有限制解码路径陷入重复死循环。古籍语料本身用词高度集中模型对高概率 token 的偏好会被放大。解决repetition_penalty至少设 1.08同时打开no_repeat_ngram_size3让模型不能原样重复三个以上的连续 token。如果还不行把temperature从 0.7 降到 0.5但别低于 0.3否则输出会退化成模板句。5.3 显存不够但一直想上全精度OOM 后救不回来现象加载模型时直接 CUDA out of memory或者生成到一半崩掉killed 进程。原因13B 全精度权重需要 52GB 显存绝大多数单卡都不可能。很多人一开始就from_pretrained不带量化参数必然 OOM。解决加载时加上load_in_8bitTrue配合device_mapauto24GB 卡轻松跑。显存只有 16GB就用load_in_4bitTrue加bnb_4bit_compute_dtypetorch.float16。另外torch_dtypetorch.float16和load_in_8bit不要同时写量化加载会自动处理精度重复指定反而会出 warning 或黑屏。5.4 回答流畅但没依据古籍幻觉填满了没看过的条文现象模型一本正经地引用了一个《伤寒论》里根本不存在的条文号比如“《伤寒论》第 999 条”整体读起来还通顺。原因这是大模型的典型幻觉SFT 数据里文献出处不够硬模型学会了“每条答案结尾加出处”这个形式但没有真的记住原文映射。这在大模型里几乎是必然发生的事只能缓解不能根除。解决推理后在应用层做硬校验。我把原文录入一个 SQLite 表模型回答后抽取“出处”字段去匹配匹配不到就把这句话替换成“未找到原文依据”。这个后置校验成本极低却能把模型的“自信”挡在用户面前。血泪经验不要指望模型自己诚实要指望流程替它把关。5.5 对话模板污染system 指令被当成待续写文本现象第一次提问正常第二次提问时模型把上一轮的用户问题又复述了一遍或者把 system 指令当作正文输出。原因推理脚本没有维护正确的对话历史结构。很多人只用tokenizer.apply_chat_template或手动拼接 prompt但如果训练时用的是 ChatML 格式推理时就必须按同样的格式拼接否则分错边界。解决检查模型自带的tokenizer_config.json里是否有chat_template字段。有的话直接用tokenizer.apply_chat_template(messages, tokenizeFalse)生成 prompt不要手工加s和/s。如果模型包没带这个字段就按它的generation_config.json里定义的模板逻辑来拼。记住一个原则推理的 prompt 格式必须和训练数据完全轴对称否则你永远在和一个行为错乱的模型搏斗。6. 进阶验证用检索增强与人工评分卡把问答从“像回事”变成“可校验”把黄帝模型部署出来只是起点真正让它可用我建议叠加一层轻量检索增强。做法不复杂把《伤寒论》《金匮要略》《温病条辨》拆成段落用 embedding 模型向量化用户提问时先检索 top-3 相关条文拼在 prompt 里再让模型回答。这样做之后回答准确率提升非常明显因为模型不用再从记忆里捞条文而是看着原文回答。验证效果不要光看感觉我一般建一张人工评分卡每轮抽 50 个问题按三个维度打分语义相关性、原文依据正确性、术语规范性。每个维度 1 到 5 分最后算平均分。检索增强前后的对照通常是这样相关性从 3.8 涨到 4.5依据正确性从 2.1 涨到 4.2术语规范性变化不大但稳定在 4 分以上。这个验证流程大概花一下午但能让你对模型有没有实际使用价值心里有底。我自己的习惯是每次拿到这类领域模型仓库先复制骨架脚本再替换数据重训一遍做到能重启、能续训、能换基座。这个动作跑顺了后续再换到更新的中文基座或引入多模态大模型做舌象图片输入都只是替换接口的事。这一步的功夫花得很值。希望这篇笔记能帮你在做本地大模型落地的路上少走一段弯路。本文还有配套的精品资源点击获取