一.背景介绍

随着人工智能在医疗领域的深入应用,大语言模型(LLMs)在医学问答、临床决策支持和科研文献分析等方面展现出巨大潜力。然而,通用大模型(如GPT-4、Qwen等)在专业医学场景下仍存在以下问题:医学知识准确性不足可能产生不符合临床指南的回答,推理逻辑不够严谨难以满足医学问题对因果分析的高要求,同时全参数微调大模型带来的高昂计算成本也限制了实际应用。因此,如何在有限算力下,高效定制专业医学大模型成为关键挑战。

本项目基于 DeepSeek-R1-0528-Qwen3-8B(由DeepSeek团队在 Qwen3-8B 架构上优化),通过多项技术创新实现了高效的医学推理微调。项目采用4-bit量化技术显著降低显存占用,使用单张4090显卡即可完成训练;运用LoRA方法仅微调部分参数,大幅提升训练效率;设计结构化提示工程引导模型输出符合医学规范的推理过程;并采用专业医学选择题库进行训练,有效增强了模型的诊断与决策能力。

本项目为医疗AI开发者提供了低成本、高精度的医学模型微调方案,帮助临床和科研人员快速构建专科问答助手,同时也为LLM技术社区验证了"小参数适配+领域知识增强"技术路线的可行性。项目成果可广泛应用于医学考试题库自动解答、电子病历关键信息提取、患者咨询智能回复生成以及医学文献结构化分析等多个场景。通过本教程,开发者能够快速复现合规、可靠且具备可解释性的医学推理模型,为推动人工智能在医疗领域的可信落地提供有力支持。

二.项目步骤详解

1. 环境配置

首先,从镜像地址拉取预训练模型(https://hf-mirror.com/deepseek-ai/DeepSeek-R1-0528-Qwen3-8B)和数据集(https://hf-mirror.com/datasets/mamachang/medical-reasoning)至本地,并将其挂载到 BitaHub 工作台的文件存储中。在BitaHub工作台创建开发环境,选择单卡4090GPU,并通过JupyterLab访问方式进入开发环境。

图片

随后,在 JupyterLab 环境中运行以下命令安装 Python 库。

%pip install -q -U transformers==4.52.1
%pip install -q -U datasets 
%pip install -q -U accelerate 
%pip install -q -U peft 
%pip install -q -U trl 
%pip install -q -U bitsandbytes

2.模型加载

首先,导入必要的库并配置量化参数,使用 NF4 量化类型和 bfloat16 计算精度,保持性能的同时减少显存占用。

from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
import torch

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,                # 使用4位量化加载模型
    bnb_4bit_use_double_quant=False,  # 不使用二次量化
    bnb_4bit_quant_type="nf4",        # 使用NF4量化类型
    bnb_4bit_compute_dtype=torch.bfloat16,  # 计算时使用bfloat16精度
)

随后加载分词器和模型,应用量化配置并设置自动设备映射,最后调整模型配置,禁用缓存以节省内存,设置张量并行度为 1。这种量化加载方式使 8B 参数模型可在单卡 GPU 上运行,为后续医学推理任务的训练和推理提供了计算支持。

model_dir = "/model/DeepSeek-R1-0528-Qwen3-8B"

tokenizer = AutoTokenizer.from_pretrained(model_dir, use_fast=True)

model = AutoModelForCausalLM.from_pretrained(
    model_dir,
    quantization_config=bnb_config,    # 应用量化配置
    device_map="auto",  
    torch_dtype=torch.bfloat16,
    trust_remote_code=True             
)

model.config.use_cache = False     # 禁用缓存以节省内存
model.config.pretraining_tp = 1     # 设置张量并行度为1

3.数据处理

首先,定义训练提示模板,将问题和答案组合成特定的格式。formatting_prompts_func函数用于对数据集进行格式化处理,去除问题中的Q:前缀,并在答案末尾添加结束符EOS_TOKEN,最后将问题和答案按照模板组合成文本。

train_prompt_style = """
Please answer with one of the options in the bracket. Write reasoning in between <analysis></analysis>. Write the answer in between <answer></answer>.
### Question:
{}

### Response:
{}"""

EOS_TOKEN = tokenizer.eos_token 

defformatting_prompts_func(examples):
    inputs = examples["input"]
    outputs = examples["output"]
    texts = []
    for question, response inzip(inputs, outputs):
        question = question.replace("Q:", "")   
        ifnot response.endswith(tokenizer.eos_token):
            response += tokenizer.eos_token
            
        text = train_prompt_style.format(question, response)
        texts.append(text)
    return {"text": texts}

随后,从本地路径加载医学推理数据集,应用之前定义的格式化函数,打印一个样本检查格式是否正确。

from datasets import load_dataset

dataset = load_dataset(
    "/data/medical-reasoning",
    split="train",
    trust_remote_code=True,
)
dataset = dataset.map(
    formatting_prompts_func,
    batched=True,
)
print(dataset["text"][10])

    输出包含系统提示、问题、推理和答案,格式如下:

    图片

    4.微调前的模型推理

    在微调模型之前,我们将测试其基础性能以建立比较基准。包括创建推理提示、从数据集中选择示例问题以及使用基本模型生成响应。

    首先,创建推理提示模板,用于在推理时将问题组合成特定的格式。

    inference_prompt_style = """
    Please answer with one of the options in the bracket. Write reasoning in between <analysis></analysis>. Write the answer in between <answer></answer>.
    
    ### Question:
    {}
    
    ### Response:
    <analysis>
    """

    随后,选择数据集中的问题,使用分词器将问题按照推理提示模板进行编码并提供给模型,并输出推理结果。

    question = dataset[10]['input']
    question = question.replace("Q:", "")
    
    inputs = tokenizer(
        [inference_prompt_style.format(question) + tokenizer.eos_token],
        return_tensors="pt"
    ).to("cuda")
    
    outputs = model.generate(
        input_ids=inputs.input_ids,
        attention_mask=inputs.attention_mask,
        max_new_tokens=1200,
        eos_token_id=tokenizer.eos_token_id,
        use_cache=True,
    )
    response = tokenizer.batch_decode(outputs, skip_special_tokens=True)
    print(response[0].split("### Response:")[1])

    ​​​​​​​可以看到模型的响应效果不理想:分析部分过于冗长,占用了大量token限额,导致最终未能生成答案部分。

    图片

    5.数据整理和 LoRA 配置

    创建了一个语言模型训练专用的数据整理器(DataCollator),自动将文本数据批处理为模型可训练的格式。

    from transformers import DataCollatorForLanguageModeling
    data_collator = DataCollatorForLanguageModeling(
        tokenizer=tokenizer,
        mlm=False
    )

    ​​​​​​​配置LoRA参数,与完全微调相比,LoRA 仅修改模型参数的一小部分,节省内存,速度更快。

    from peft import LoraConfig, get_peft_model
    
    peft_config = LoraConfig(
        lora_alpha=16,                           
        lora_dropout=0.05,                    
        r=64,                                    
        bias="none",                         
        task_type="CAUSAL_LM",                   
        target_modules=[
            "q_proj",
            "k_proj",
            "v_proj",
            "o_proj",
            "gate_proj",
            "up_proj",
            "down_proj",
        ], 
    )
    
    model = get_peft_model(model, peft_config)

    ​​​​​​​6.模型训练

    配置训练参数

    from trl import SFTTrainer
    from transformers import TrainingArguments
    
    training_arguments = TrainingArguments(
        output_dir="DeepSeek-R1-0528-Qwen3-8B-Medical-Reasoning",
        per_device_train_batch_size=1,
        per_device_eval_batch_size=1,
        gradient_accumulation_steps=2,
        optim="paged_adamw_32bit",
        num_train_epochs=1,
        logging_steps=0.2,
        warmup_steps=10,
        logging_strategy="steps",
        learning_rate=2e-4,
        fp16=False,
        bf16=False,
        group_by_length=True,
        report_to="none"
    )
    
    trainer = SFTTrainer(
        model=model,
        args=training_arguments,
        train_dataset=dataset,
        peft_config=peft_config,
        data_collator=data_collator,
    )

    ​​​​​​​释放内存资源,确保训练开始时拥有最大的可用显存。接着将模型的use_cache设置为False,这个设置能显著减少训练过程中的显存占用,特别适合资源受限的环境。最后启动实际的微调训练过程。

    import gc, torch
    gc.collect()
    torch.cuda.empty_cache()
    model.config.use_cache = False
    trainer.train()

      图片

      图片

      随着训练的进行,训练损失逐渐减少。表明模型正在成功学习和微调。

      7.微调后的模型推理

      微调模型后,我们通过之前的示例问题来测试其性能。这使我们能够将结果与基本模型进行比较,并评估通过微调实现的改进。

      question = dataset[10]['input']
      question = question.replace("Q:", "")
      
      inputs = tokenizer(
          [inference_prompt_style.format(question,) + tokenizer.eos_token],
          return_tensors="pt"
      ).to("cuda")
      
      outputs = model.generate(
          input_ids=inputs.input_ids,
          attention_mask=inputs.attention_mask,
          max_new_tokens=1200,
          eos_token_id=tokenizer.eos_token_id,
          use_cache=True,
      )
      response = tokenizer.batch_decode(outputs, skip_special_tokens=True)
      print(response[0].split("### Response:")[1])

        图片

        微调模型的响应显示出显著的改进:

        • 分析部分简洁明了,并正确使用了 <analysis> 标签而不是 <think>。

        • 答案部分正确且格式正确。

        三.总结

        本项目围绕 DeepSeek-R1-0528-Qwen3-8B 模型展开医学推理任务的全流程实现。具体包括环境准备、模型和分词器的加载、数据处理、推理提示模板的定义、LoRA 配置、微调训练等过程,最终通过生成推理演示验证模型效果,展示其在医学问题分析与答案选择中的应用能力,整个流程兼顾内存优化与任务适配,实现了大模型在垂直领域的高效部署。

        Logo

        Agent 垂直技术社区,欢迎活跃、内容共建。

        更多推荐