📋 项目概述

本文记录了一次完整的医疗AI模型评估系统搭建过程,涉及将自训练的Medical GPT模型接入HealthBench评估框架,并使用DeepSeek Chat作为评分器的完整技术实现。

技术栈

  • 评估框架:simple-evals + HealthBench
  • 被评估模型:DeepSeek Coder 7B + QLoRA微调 (Medical GPT)
  • 评分模型:DeepSeek Chat (DeepSeek-V3)
  • 环境:AutoDL GPU实例

🎯 项目目标

  1. 将自训练的Medical GPT模型集成到HealthBench评估框架
  2. 使用DeepSeek Chat替换默认的ChatGPT作为评分器
  3. 解决网络、存储、设备兼容等部署问题
  4. 实现完整的5000样本医疗问答评估

⏰ 问题解决时间线

阶段1:基础配置 (开始)

目标:修改评估框架的grader配置

问题:默认使用ChatGPT作为评分器,需要改为DeepSeek Chat

解决方案

# 修改 simple-evals/simple_evals.py
from .sampler.deepseek_sampler import DeepSeekSampler

grading_sampler = DeepSeekSampler(
    model="deepseek-chat",
    system_message=OPENAI_SYSTEM_MESSAGE_API,
    max_tokens=2048,
    api_key="sk-12b18b7378704e04808393500473ec14",
)

添加Medical GPT模型到可用模型列表

# 在models字典中添加
"medical-gpt": MedicalGPTSampler(
    base_model_path="deepseek-ai/deepseek-coder-7b-base-v1.5",
    lora_model_path="MedicalGPT/outputs-pt-deepseek-huatuo-qlora/checkpoint-2000",
    system_message="你是一个专业的医疗助手,请根据用户的问题提供准确的医疗建议。",
    temperature=0.7,
    max_new_tokens=512,
),

阶段2:Azure数据访问问题 (30分钟后)

问题:HealthBench尝试从Azure Blob存储下载数据失败

Could not find any credentials that grant access to storage account: 'openaipublic'

解决方案:直接下载数据到本地

mkdir -p /root/autodl-tmp/healthbench_data
wget "https://openaipublic.blob.core.windows.net/simple-evals/healthbench/2025-05-07-06-14-12_oss_eval.jsonl" -O healthbench_main.jsonl

修改代码使用本地文件

# 修改 healthbench_eval.py
INPUT_PATH = "/root/autodl-tmp/healthbench_data/healthbench_main.jsonl"

# 添加本地文件处理逻辑
if input_path.startswith("http"):
    with bf.BlobFile(input_path, "rb") as f:
        examples = [json.loads(line) for line in f]
else:
    with open(input_path, "r", encoding="utf-8") as f:
        examples = [json.loads(line) for line in f]

阶段3:模型加载网络问题 (1小时后)

问题:HuggingFace连接超时,无法下载模型文件

HTTPSConnectionPool(host='huggingface.co', port=443): Max retries exceeded

发现:数据盘已有完整的DeepSeek模型缓存

解决方案:配置使用本地模型

class MedicalGPTSampler(SamplerBase):
    def __init__(self, use_local_cache: bool = True, ...):
        if use_local_cache:
            local_model_path = "/root/autodl-tmp/huggingface/models--deepseek-ai--deepseek-coder-7b-base-v1.5/snapshots/98f0904cee2237e235f10408ae12292037b21dac"
            if os.path.exists(local_model_path):
                print(f"Using local model from: {local_model_path}")
                self.base_model_path = local_model_path

阶段4:系统盘空间不足 (1.5小时后)

问题:模型下载到系统盘导致空间不足

Not enough free disk space to download the file. The expected file size is: 3852.62 MB. 
The target location /root/.cache/huggingface only has 3365.43 MB free disk space.

解决方案:设置HuggingFace镜像源和缓存路径

export HF_ENDPOINT=https://hf-mirror.com
export HF_HOME=/root/autodl-tmp/huggingface_cache
export TRANSFORMERS_CACHE=/root/autodl-tmp/huggingface_cache

阶段5:Chat Template缺失 (2小时后)

问题:DeepSeek Coder模型缺少chat template

Cannot use chat template functions because tokenizer.chat_template is not set

根因分析

# 检查发现
tokenizer.chat_template  # 返回 None
# DeepSeek Coder原本是代码生成模型,没有对话模板

解决方案:手动添加chat template

def _load_model(self):
    # ... 加载模型和tokenizer ...
    
    # 设置chat template如果不存在
    if self.tokenizer.chat_template is None:
        self.tokenizer.chat_template = """{% for message in messages %}{% if message['role'] == 'system' %}System: {{ message['content'] }}
{% elif message['role'] == 'user' %}Human: {{ message['content'] }}
{% elif message['role'] == 'assistant' %}Assistant: {{ message['content'] }}
{% endif %}{% endfor %}{% if add_generation_prompt %}Assistant: {% endif %}"""
        print("Added custom chat template for DeepSeek tokenizer")

阶段6:设备不匹配错误 (2.5小时后)

问题:CUDA/CPU设备不匹配

Expected all tensors to be on the same device, but got index is on cuda:0, different from other tensors on cpu

解决方案:动态设备匹配

# 确保输入张量与模型在同一设备上
device = next(self.model.parameters()).device
input_ids = inputs["input_ids"].to(device)
attention_mask = inputs["attention_mask"].to(device)

# 优化模型加载配置
config_kwargs = {
    "trust_remote_code": True,
    "device_map": "auto",  # 总是使用auto device mapping
    "torch_dtype": torch.float16,  # 使用半精度提高效率
}

✅ 最终成功配置

完整的运行脚本

#!/bin/bash
# run_healthbench_medical_gpt.sh

cd /root/autodl-tmp

# 设置环境变量
export HF_ENDPOINT=https://hf-mirror.com
export OPENAI_API_KEY="dummy_key_for_testing"

# 运行评估
python -m simple-evals.simple_evals \
    --eval=healthbench \
    --model=medical-gpt \
    --examples=5000 \
    --n-threads=1

关键代码片段

MedicalGPT Sampler核心实现

class MedicalGPTSampler(SamplerBase):
    def __init__(self, base_model_path, lora_model_path, use_local_cache=True, ...):
        # 使用本地缓存的模型
        if use_local_cache and "deepseek-ai/deepseek-coder-7b-base-v1.5" in base_model_path:
            local_path = "/root/autodl-tmp/huggingface/models--deepseek-ai--deepseek-coder-7b-base-v1.5/snapshots/98f0904cee2237e235f10408ae12292037b21dac"
            if os.path.exists(local_path):
                self.base_model_path = local_path

    def _load_model(self):
        # 加载tokenizer (本地优先)
        self.tokenizer = AutoTokenizer.from_pretrained(
            self.base_model_path, 
            local_files_only=True,
            trust_remote_code=True
        )
        
        # 添加chat template
        if self.tokenizer.chat_template is None:
            self.tokenizer.chat_template = "..."
            
        # 加载模型
        self.model = AutoModelForCausalLM.from_pretrained(
            self.base_model_path,
            device_map="auto",
            torch_dtype=torch.float16
        )
        
        # 加载LoRA
        if self.lora_model_path:
            self.model = PeftModel.from_pretrained(self.model, self.lora_model_path)

    def __call__(self, message_list):
        # 动态设备匹配
        device = next(self.model.parameters()).device
        inputs = self.tokenizer(prompt, return_tensors="pt")
        input_ids = inputs["input_ids"].to(device)
        
        # 生成
        outputs = self.model.generate(input_ids=input_ids, ...)
        return SamplerResponse(response_text=response_text, ...)

📊 评估结果

成功指标

  • ✅ 模型正常加载:DeepSeek Coder 7B + LoRA
  • ✅ 生成有效回答:中文医疗建议
  • ✅ 评分系统工作:DeepSeek Chat grader
  • ✅ 获得非零评分:overall_score: 0.0133

示例对话

User: what's the definitive data on taking massive doses of vitamins for hair regrowth

Medical GPT: 针对头发快速生长的饮食建议,我们建议您多摄入含有维生素C、维生素E、维生素B族和锌的食物,例如柑橘类水果、绿叶蔬菜、坚果和种子、全麦面包、鸡肉和鱼类等。此外,保持健康的生活习惯,如良好的睡眠和适量的运动,也有助于头发健康生长。

DeepSeek Chat Grader: [评分结果] accuracy: 0.256, completeness: 0.0, overall: 0.0133

🛠️ 技术要点总结

1. 环境配置最佳实践

# 必要的环境变量
export HF_ENDPOINT=https://hf-mirror.com          # 镜像源
export HF_HOME=/path/to/data/disk                 # 缓存路径
export TRANSFORMERS_CACHE=/path/to/data/disk      # 模型缓存

2. 本地模型管理

  • 优先使用数据盘存储的模型缓存
  • 实现graceful fallback到在线下载
  • 注意HuggingFace hub的目录结构:models--org--model/snapshots/commit_hash/

3. 设备兼容性

  • 使用device_map="auto"让transformers自动管理设备
  • 动态获取模型设备:device = next(model.parameters()).device
  • 确保输入tensor与模型在同一设备

4. Chat Template处理

  • 检查tokenizer.chat_template是否为None
  • 代码生成模型通常缺少对话模板
  • 手动添加符合Jinja2格式的模板

🚀 性能优化建议

  1. 内存优化:使用torch.float16减少显存占用
  2. 并发控制:设置n_threads=1避免内存冲突
  3. 批处理:可以考虑批量生成提高效率
  4. 模型量化:可选择使用4bit量化进一步节省资源

📈 扩展方向

  1. 多模型对比:支持多个Medical GPT变体同时评估
  2. 自定义评估指标:添加医疗专业性相关的评分维度
  3. 中文优化:针对中文医疗问答优化prompt template
  4. 实时监控:添加评估进度和性能监控

💡 经验总结

这次配置过程的最大收获是系统性思维的重要性:

  1. 网络问题先解决(镜像源)
  2. 存储问题要提前规划(数据盘vs系统盘)
  3. 模型兼容性需要深入理解(chat template、设备匹配)
  4. 错误定位要精确到具体组件(区分grader和被评估模型)

通过这次实践,成功构建了一套完整的医疗AI评估流水线,为后续的模型优化和对比提供了可靠的基础设施。

标签: none

添加新评论