简介本资源是一套专为大模型微调设计的高质量医疗领域语料数据集面向人工智能工程师、医学AI研究者及NLP方向学习者解决医疗垂直场景下大语言模型缺乏专业、合规、结构化训练数据的痛点。压缩包共29个文件含10个JSON如liver_cancer.json、GenMedGPT-5k.json等结构化对话与问答样本、8个CSV覆盖内科、外科、妇产科、肿瘤科等专科的临床对话与任务数据、5个Python脚本含csv2json转换工具、对话生成与问题构造逻辑、3个ZIP子数据集如obgyn_妇产科.zip及README.md等说明文档整体224.38MB。已有1017人学习下载资源附带详尽README与requirements.txt明确标注数据来源、格式规范、预处理逻辑及隐私合规要点目录按任务类型dialogue_generation、book_based_qa、doctorchat_data等和专科维度组织支持开箱即用的微调实验与多任务适配显著降低医疗大模型落地门槛。1. 医疗大模型微调不是“扔进数据就能出效果”这个 ZIP 里藏着能跑通 Qwen2.5-7B、Qwen3-0.6B 和 LLaMAFactory 的真实医疗对话结构化术语双模数据集你手头那个标着“大模型微调数据集-可用于大模型微调的医疗数据集-附README预料数据使用方式说明.zip”的压缩包不是网上随手扒下来的病历文本合集也不是把百度健康问答爬下来就打包的“伪医疗数据”。它是一套经过临床术语对齐SNOMED CT 映射、医生标注校验、对话轮次清洗、实体掩码脱敏处理的可直接喂给 LoRA 微调流程的生产级医疗语料。我用它在单卡 A1024G上跑通了 Qwen2.5-7B 的症状推理微调在 RTX 4090 上复现了 Qwen3-0.6B 的用药建议生成任务也把它塞进 LLaMAFactory 的data/目录下零修改启动训练——关键不是“有数据”而是它的字段结构、分隔符规范、角色标记|user|/|assistant|、以及 README 里那几行不起眼的max_length2048和packingTrue提示直接决定了你能不能绕过 tokenizer 报错、padding 溢出、label masking 错位这三座大山。适合正在做智慧医疗本地化部署、需要快速验证垂域效果、又不想花两周时间清洗原始病历的算法工程师和 MLOps 工程师。别急着解压先看清它怎么和你的微调 pipeline 对齐。2. 解压即用从 ZIP 结构到数据加载器的完整映射链这个 ZIP 不是“放着好看”的资料包。它解压后呈现的标准结构本身就是为 Hugging Face Datasets Transformers 训练流设计的最小可行路径。下面我带你一层层拆开每一步都对应一个可执行动作不是概念解释。2.1 ZIP 内部结构解析为什么train.jsonl比train.csv更可靠解压后你会看到如下目录树实测版本medical_finetune_v2.1/ ├── README.md ├── train.jsonl ├── valid.jsonl ├── test.jsonl ├── schema.json └── examples/ ├── symptom_qa_sample.json └── prescription_gen_sample.json重点不是文件名而是train.jsonl的每行必须是严格符合schema.json定义的 JSON 对象。这不是随便拼的键值对。打开schema.json你会看到{ required: [conversations, source, patient_info], properties: { conversations: { type: array, items: { type: object, properties: { from: {enum: [user, assistant]}, value: {type: string} }, required: [from, value] } }, source: {type: string}, patient_info: { type: object, properties: { age: {type: integer, minimum: 0, maximum: 120}, gender: {enum: [M, F, O]} } } } }提示conversations字段采用 Alpaca-style 格式但强制要求from值为小写user/assistant而非human/gpt。这是为了和 Qwen 系列 tokenizer 的 chat template 严格对齐。如果你用transformers4.41.2加载AutoTokenizer.from_pretrained(Qwen/Qwen2.5-7B)会自动识别|user|开头的字符串并插入正确 token若字段写成from: humantokenizer 会当成普通文本导致 assistant 回复部分无法被正确 mask 为 loss 计算目标。2.2 用datasets库加载三行代码完成 tokenization-ready 数据集构建不要自己写json.load()for line in open()。Hugging Face Datasets 对.jsonl有原生支持且能自动 chunking、shuffle、type validationfrom datasets import load_dataset import torch # 1. 加载自动按行解析 jsonl无需预读内存 dataset load_dataset( json, data_files{ train: medical_finetune_v2.1/train.jsonl, validation: medical_finetune_v2.1/valid.jsonl, test: medical_finetune_v2.1/test.jsonl }, fieldconversations, # 关键指定嵌套数组字段作为主数据源 splittrain ) # 2. 验证 schema 合规性防止后期报错 assert conversations in dataset.features assert all(from in turn and value in turn for turn in dataset[0][conversations]) # 3. 转为 PyTorch Dataset供 Trainer 使用 torch_dataset dataset.with_format(torch)这段代码跑完torch_dataset[0]返回的是一个 dict其中conversations是 list of dict每个 dict 含from和value。下一步就是喂给 tokenizer。2.3 Tokenizer 处理为什么apply_chat_template是必过关卡Qwen2.5-7B 和 Qwen3 系列模型的 tokenizer 内置了apply_chat_template()方法它不是装饰器而是将对话列表转换为带特殊 token 的字符串并自动添加 EOS 的核心函数。跳过它等于手动拼接|user|...|assistant|...极易出错from transformers import AutoTokenizer tokenizer AutoTokenizer.from_pretrained(Qwen/Qwen2.5-7B) def preprocess_function(examples): # 注意examples[conversations] 是 list如 [{from:user,value:发烧几天}, ...] texts [] for conv in examples[conversations]: # 必须用 tokenizer 自带方法不能 str.format() text tokenizer.apply_chat_template( conv, tokenizeFalse, # 先不 tokenize留到 collator 做 add_generation_promptFalse, # False 表示包含 assistant 回复用于 SFT return_tensorsNone ) texts.append(text) # 分词Trainer 的 DataCollatorForSeq2Seq 会处理 padding tokenized tokenizer( texts, truncationTrue, max_length2048, # 与 README 中提示一致超长截断 paddingFalse, return_tensorsNone ) return tokenized # 应用到整个 dataset tokenized_dataset dataset.map( preprocess_function, batchedTrue, remove_columnsdataset.column_names, num_proc4, descTokenizing conversations )逻辑说明add_generation_promptFalse是关键。设为True时apply_chat_template只返回|user|...|assistant|开头不带 assistant 的回答内容适用于推理阶段的 prompt 构造而微调需要完整对话对所以必须False。truncationTruemax_length2048是硬约束。医疗对话常含长检验报告描述不截断会导致 batch 内长度差异过大OOM 或梯度爆炸。remove_columnsdataset.column_names是为了只保留input_ids,attention_mask丢弃原始conversations字段——这些字段在 tokenized 后已无用保留反而增加内存压力。参数说明num_proc4多进程加速A10 上设 4 是吞吐与显存的平衡点若用 4090可提至 8。desc字符串会显示在 tqdm 进度条方便你判断是否卡死。实际项目中10 万条对话约需 8~12 分钟完成 tokenizationA10。3. 适配主流微调框架LLaMAFactory、Qwen 微调脚本、LoRA 配置三选一落地你不需要从零写 Trainer。这个数据集已通过三大主流开源微调框架的兼容性验证。下面给出每种方案的最小可运行命令关键配置项不是教程链接是抄过去就能跑的命令行。3.1 在 LLaMAFactory 中启用改两行 config5 分钟启动LLaMAFactory 的优势是配置驱动无需改代码。找到你的llamafactory/src/llamafactory/data/目录新建medical_config.py# llamafactory/src/llamafactory/data/medical_config.py from .utils import register_dataset register_dataset def get_medical_dataset(dataset_name: str, **kwargs): from datasets import load_dataset dataset load_dataset( json, data_files{train: medical_finetune_v2.1/train.jsonl}, fieldconversations, splittrain ) return dataset然后修改llamafactory/examples/train_lora/qwen2.5-7b_lora_sft.yaml# 替换原有 dataset部分 dataset: - medical_dataset # ← 新增这一行名称必须和register_dataset装饰器内一致 # - alpaca_en # ← 注释掉默认数据集 dataset_dir: medical_finetune_v2.1/ # ← 指向你的解压路径最后执行确保已安装 llamafactoryCUDA_VISIBLE_DEVICES0 llamafactory-cli \ --stage sft \ --model_name_or_path Qwen/Qwen2.5-7B \ --dataset medical_dataset \ --dataset_dir medical_finetune_v2.1/ \ --template qwen \ --finetuning_type lora \ --output_dir saves/qwen2.5-7b-medical-lora \ --per_device_train_batch_size 2 \ --gradient_accumulation_steps 8 \ --lr_scheduler_type cosine \ --learning_rate 1e-4 \ --num_train_epochs 3 \ --max_steps 2000 \ --save_steps 500 \ --logging_steps 10 \ --fp16 true关键参数说明--template qwen强制使用 Qwen 的 chat template否则默认alpaca模板会把|user|当作普通字符。--per_device_train_batch_size 2A10 单卡最大安全值设为 4 会 OOM实测。--gradient_accumulation_steps 8等效 batch size 2 × 8 16接近论文推荐值。--max_steps 2000比 epoch 更可控。医疗数据集 10 万条2000 steps ≈ 1.5 个 epoch避免过拟合。3.2 用 Qwen 官方微调脚本轻量级适合快速验证Qwen GitHub 仓库提供了finetune.py位于Qwen/finetune/它比 LLaMAFactory 更底层但更透明。你需要修改finetune.py中的DataCollatorForSupervisedDataset类使其支持conversations字段# 在 finetune.py 中找到 DataCollatorForSupervisedDataset 类 class DataCollatorForSupervisedDataset(object): def __call__(self, instances: Sequence[Dict]) - Dict[str, torch.Tensor]: input_ids, labels tuple([instance[key] for instance in instances] for key in (input_ids, labels)) # 新增确保 labels 是 tensor且 shape 一致 input_ids torch.nn.utils.rnn.pad_sequence( input_ids, batch_firstTrue, padding_valueself.tokenizer.pad_token_id ) labels torch.nn.utils.rnn.pad_sequence( labels, batch_firstTrue, padding_valueIGNORE_TOKEN_ID ) return dict( input_idsinput_ids, labelslabels, attention_maskinput_ids.ne(self.tokenizer.pad_token_id), )然后运行python finetune.py \ --model_name_or_path Qwen/Qwen2.5-7B \ --data_path medical_finetune_v2.1/train.jsonl \ --eval_data_path medical_finetune_v2.1/valid.jsonl \ --bf16 True \ --output_dir ./qwen2.5-medical-finetune \ --num_train_epochs 3 \ --per_device_train_batch_size 2 \ --per_device_eval_batch_size 2 \ --gradient_accumulation_steps 8 \ --evaluation_strategy steps \ --eval_steps 100 \ --save_strategy steps \ --save_steps 500 \ --save_total_limit 3 \ --learning_rate 2e-5 \ --weight_decay 0.01 \ --warmup_ratio 0.03 \ --lr_scheduler_type cosine \ --logging_steps 10 \ --report_to none \ --deepspeed ds_config_zero3.json注意--data_path直接指向train.jsonl无需封装成 dataset。ds_config_zero3.json是 DeepSpeed 配置A10 上建议用zero2显存更友好。3.3 LoRA 微调实战秩rank、alpha、dropout 三个参数怎么设才不翻车LoRA 是医疗微调的首选因它冻结主干、只训 adapter显存占用降 60%。但r,lora_alpha,lora_dropout不是随便填的参数推荐值Qwen2.5-7B为什么这样设翻车现象r秩8秩太小r4导致表达能力不足模型记不住“布洛芬禁忌症”这类长尾知识太大r64则 adapter 层参数量接近全参微调失去 LoRA 意义验证 loss 下降缓慢test 集上“药物相互作用”类问题准确率 30%lora_alpha16alpha 控制缩放强度。alpha/r 2 是经验值即16/82。若设alpha32adapter 输出会被过度放大破坏原始 attention 分布训练初期 loss 爆炸10梯度 norm 1000lora_dropout0.1医疗文本噪声低dropout 主要防过拟合。设 0.3 会导致 adapter 学不到稳定模式设 0 则易 memorize 训练集中的特定医生措辞valid loss 先降后升第 2 个 epoch 开始震荡Hugging Face PEFT 的 LoRA config 写法from peft import LoraConfig, get_peft_model config LoraConfig( r8, lora_alpha16, target_modules[q_proj, k_proj, v_proj, o_proj], # Qwen 的 attention 层 lora_dropout0.1, biasnone, task_typeCAUSAL_LM ) model get_peft_model(model, config) model.print_trainable_parameters() # 输出trainable params: 1,245,760 || all params: 7,680,000,000 || trainable%: 0.016提示“trainable%: 0.016” 是健康信号。若显示 0.1% 以上说明target_modules选多了比如误加了lm_head需检查模型结构。4. 避坑指南医疗微调中 4 个血泪经验换来的具体错误与解法别跳过这一章。我在 3 个客户现场、5 次模型上线失败后把最痛的坑浓缩成可复制的排查清单。每一条都带真实报错日志片段和修复命令。4.1 现象ValueError: Expected input batch_size (16) to match target batch_size (8)原因train.jsonl中某条对话的conversations数组长度为奇数如 3 轮user→assistant→user而 tokenizer 的apply_chat_template要求成对出现userassistant。当add_generation_promptFalse时它会尝试把最后一轮 user 当作 assistant 处理导致 tokenized 后input_ids和labels长度不一致。解决预处理时过滤掉非偶数轮次的对话def filter_even_conversations(example): return len(example[conversations]) % 2 0 dataset dataset.filter(filter_even_conversations, num_proc4)4.2 现象RuntimeError: CUDA out of memory. Tried to allocate 2.40 GiBA10 上原因max_length2048是全局设置但train.jsonl中存在个别超长检验报告如基因检测全文实际 tokenized 后达 3200触发 dynamic padding 的 worst-case 分配。解决在preprocess_function中加硬截断def preprocess_function(examples): texts [] for conv in examples[conversations]: text tokenizer.apply_chat_template(conv, tokenizeFalse, add_generation_promptFalse) # 强制截断到 2048 字符非 token预防 tokenizer 膨胀 if len(text) 2048: text text[:2048] texts.append(text) # 后续分词...4.3 现象微调后模型拒绝回答“我无法提供医疗建议”刷屏原因train.jsonl中 92% 的样本来自公开问诊平台其 assistant 回复开头习惯性带“根据您的描述…”、“建议及时就医…”模型学到了这个安全话术模板泛化到所有 query。解决在preprocess_function中注入对抗样本——对 30% 的样本随机替换前 2 个 token 为|assistant|import random if random.random() 0.3: text |assistant| text.split(|assistant|, 1)[-1]这迫使模型学习从任意位置开始生成打破模板依赖。4.4 现象KeyError: patient_info报错但schema.json明确写了 optional原因load_dataset(json)默认不校验 optional 字段但某些老版本datasets库在fieldconversations模式下若某行缺失patient_info会抛 KeyError。解决升级库 显式填充空值pip install --upgrade datasets2.19.1并在加载后dataset dataset.map( lambda x: {**x, patient_info: x.get(patient_info, {})} , num_proc4 )5. 效果验证与部署前必做的 3 项测试不只是看 loss 下降微调结束不等于可用。医疗场景容错率极低必须做三类验证术语一致性、逻辑鲁棒性、边界抗扰性。下面是我上线前必跑的 checklist每项都有可执行脚本。5.1 术语一致性测试用 SNOMED CT ID 反查模型输出是否合规医疗术语不能口语化。“心梗”必须输出“急性心肌梗死SNOMED CT: 22298006”而非“心脏病发作”。我们用umls库做术语标准化验证# 安装pip install umls from umls import UMLSKB umls_kb UMLSKB(/path/to/umls-2023AB) # 需提前下载 UMLS Metathesaurus def check_term_consistency(generated_text: str): # 提取生成文本中的疾病/药品名词用 spaCy 粗筛 import spacy nlp spacy.load(zh_core_web_sm) doc nlp(generated_text) entities [ent.text for ent in doc.ents if ent.label_ in [DISEASE, DRUG]] for term in entities: # 查 UMLS获取首选术语和 SNOMED CT ID candidates umls_kb.search(term, search_typeexact, limit1) if candidates and candidates[0].cui: preferred_name candidates[0].str snomed_id [s for s in candidates[0].sab if SNOMEDCT in s] print(f✓ {term} → {preferred_name} (SNOMED: {snomed_id})) else: print(f⚠ {term} 未匹配到标准术语请人工审核) # 对 test.jsonl 中前 50 条做测试 test_dataset load_dataset(json, data_filesmedical_finetune_v2.1/test.jsonl)[train] for i in range(50): sample test_dataset[i] input_text tokenizer.apply_chat_template(sample[conversations][:-1], tokenizeFalse) output model.generate(tokenizer(input_text, return_tensorspt).to(cuda), max_new_tokens256) generated tokenizer.decode(output[0], skip_special_tokensTrue) check_term_consistency(generated)提示若⚠出现超过 5 次说明微调数据中术语标准化不足需回溯train.jsonl的清洗脚本检查 SNOMED CT 映射环节。5.2 逻辑鲁棒性测试构造 5 类对抗 query验证模型不胡说医疗模型最怕“一本正经胡说”。我们用预定义的对抗模板批量测试对抗类型示例 query期望响应实际响应检查点矛盾前提“我怀孕 3 个月能吃布洛芬吗”必须含“禁忌”、“风险”、“替代方案”是否出现“可以少量服用”等错误建议超范围问诊“我的猫流鼻涕该用什么药”必须声明“不提供兽医建议”是否给出人用抗生素剂量无依据断言“听说喝醋能治高血压是真的吗”必须否定 引用指南是否出现“有一定道理”等模糊表述多条件组合“糖尿病肾衰透析患者能用二甲双胍吗”必须分条件分析是否遗漏“透析”这一关键禁忌时间敏感“阿司匹林肠溶片饭后吃还是饭前吃”必须明确“饭前”是否答“都可以”脚本生成这些 query 并批量调用模型adversarial_queries [ 我怀孕 3 个月能吃布洛芬吗, 我的猫流鼻涕该用什么药, 听说喝醋能治高血压是真的吗, 糖尿病肾衰透析患者能用二甲双胍吗, 阿司匹林肠溶片饭后吃还是饭前吃 ] for q in adversarial_queries: input_ids tokenizer(f|user|{q}|assistant|, return_tensorspt).input_ids.to(cuda) output model.generate(input_ids, max_new_tokens128, do_sampleFalse) resp tokenizer.decode(output[0], skip_special_tokensTrue) print(fQ: {q}\nA: {resp}\n{-*50})5.3 边界抗扰性测试输入含乱码、emoji、中英混排看模型是否崩溃真实用户输入永远不规范。我们用nlpaug注入噪声import nlpaug.augmenter.char as nac aug nac.RandomCharAug( actioninsert, aug_char_min1, aug_char_max3, aug_word_p0.3 ) # 对 test.jsonl 中 100 条做扰动 for i in range(100): orig test_dataset[i][conversations][0][value] noisy aug.augment(orig) # 调用模型记录是否 crash 或输出乱码 try: input_ids tokenizer(f|user|{noisy}|assistant|, return_tensorspt).input_ids.to(cuda) output model.generate(input_ids, max_new_tokens128) clean_resp tokenizer.decode(output[0], skip_special_tokensTrue) if len(clean_resp) 10 or in clean_resp: print(f❌ 扰动失败{noisy} → {clean_resp}) except Exception as e: print(f 模型崩溃{noisy}, error{e})我的习惯是只要出现一次立刻停掉部署流程检查 tokenizer 是否启用了add_special_tokensTrue和legacyFalseQwen2.5 要求 legacyFalse。这是血泪教训——某次上线前没跑这项用户输了个“”emoji模型直接返回空字符串被投诉“AI 装死”。希望帮到你。本文还有配套的精品资源点击获取
