文章目录
-
- [1. 什么是 DeepSeek Harness](#1. 什么是 DeepSeek Harness)
- [2. 为什么需要 Harness](#2. 为什么需要 Harness)
- [3. 核心架构与工作流程](#3. 核心架构与工作流程)
- [4. 安装与依赖](#4. 安装与依赖)
- [5. 快速上手](#5. 快速上手)
- [6. 评测任务配置](#6. 评测任务配置)
- [7. 核心模块解析](#7. 核心模块解析)
-
- [7.1 Model Adapter](#7.1 Model Adapter)
- [7.2 Task Registry](#7.2 Task Registry)
- [7.3 Metric 计算](#7.3 Metric 计算)
- [8. 实战:评测一个本地 DeepSeek 模型](#8. 实战:评测一个本地 DeepSeek 模型)
- [9. 常见问题](#9. 常见问题)
- [10. 总结](#10. 总结)
1. 什么是 DeepSeek Harness
DeepSeek Harness 是围绕 DeepSeek 系列大模型所构建的一套评测与实验框架,用于统一管理模型的加载、推理、任务适配与指标计算。它的定位与社区常见的 lm-evaluation-harness 类似,但在内部结构上更贴合 DeepSeek 模型的多卡部署、长上下文以及 MoE(Mixture of Experts)特性。
简单来说,Harness 解决的是一个核心问题:如何用一套可复现的流程,量化一个大模型"到底有多强"。
它通常包含以下能力:
- 统一的数据集加载与预处理
- 统一的模型封装与推理接口
- 统一的评测指标计算与结果汇总
- 支持多 GPU、多进程、分布式评测
- 支持自定义任务的注册与扩展
2. 为什么需要 Harness
在大模型开发中,评测往往比训练更繁琐。不同的模型可能使用不同的 tokenizer、不同的对话模板,不同的数据集又有各自的格式与指标。如果没有统一框架,每评测一个模型就要写一套脚本,既容易出错,也难以横向对比。
DeepSeek Harness 的价值在于:
- 标准化:把模型、数据集、指标三者解耦,通过配置即可组合。
- 可复现:固定随机种子、固定 few-shot 示例,保证评测结果可比。
- 可扩展:通过注册机制快速接入新模型或新任务。
- 工程化:内置分布式推理逻辑,减少显存管理与数据并行的重复劳动。
3. 核心架构与工作流程
DeepSeek Harness 的整体流程可以概括为四个阶段:模型加载 → 数据构建 → 推理生成 → 指标计算。
#mermaid-svg-abwW8oJ1bCsJ6nOm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-abwW8oJ1bCsJ6nOm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-abwW8oJ1bCsJ6nOm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-abwW8oJ1bCsJ6nOm .error-icon{fill:#552222;}#mermaid-svg-abwW8oJ1bCsJ6nOm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-abwW8oJ1bCsJ6nOm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-abwW8oJ1bCsJ6nOm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-abwW8oJ1bCsJ6nOm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-abwW8oJ1bCsJ6nOm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-abwW8oJ1bCsJ6nOm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-abwW8oJ1bCsJ6nOm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-abwW8oJ1bCsJ6nOm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-abwW8oJ1bCsJ6nOm .marker.cross{stroke:#333333;}#mermaid-svg-abwW8oJ1bCsJ6nOm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-abwW8oJ1bCsJ6nOm p{margin:0;}#mermaid-svg-abwW8oJ1bCsJ6nOm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-abwW8oJ1bCsJ6nOm .cluster-label text{fill:#333;}#mermaid-svg-abwW8oJ1bCsJ6nOm .cluster-label span{color:#333;}#mermaid-svg-abwW8oJ1bCsJ6nOm .cluster-label span p{background-color:transparent;}#mermaid-svg-abwW8oJ1bCsJ6nOm .label text,#mermaid-svg-abwW8oJ1bCsJ6nOm span{fill:#333;color:#333;}#mermaid-svg-abwW8oJ1bCsJ6nOm .node rect,#mermaid-svg-abwW8oJ1bCsJ6nOm .node circle,#mermaid-svg-abwW8oJ1bCsJ6nOm .node ellipse,#mermaid-svg-abwW8oJ1bCsJ6nOm .node polygon,#mermaid-svg-abwW8oJ1bCsJ6nOm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-abwW8oJ1bCsJ6nOm .rough-node .label text,#mermaid-svg-abwW8oJ1bCsJ6nOm .node .label text,#mermaid-svg-abwW8oJ1bCsJ6nOm .image-shape .label,#mermaid-svg-abwW8oJ1bCsJ6nOm .icon-shape .label{text-anchor:middle;}#mermaid-svg-abwW8oJ1bCsJ6nOm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-abwW8oJ1bCsJ6nOm .rough-node .label,#mermaid-svg-abwW8oJ1bCsJ6nOm .node .label,#mermaid-svg-abwW8oJ1bCsJ6nOm .image-shape .label,#mermaid-svg-abwW8oJ1bCsJ6nOm .icon-shape .label{text-align:center;}#mermaid-svg-abwW8oJ1bCsJ6nOm .node.clickable{cursor:pointer;}#mermaid-svg-abwW8oJ1bCsJ6nOm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-abwW8oJ1bCsJ6nOm .arrowheadPath{fill:#333333;}#mermaid-svg-abwW8oJ1bCsJ6nOm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-abwW8oJ1bCsJ6nOm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-abwW8oJ1bCsJ6nOm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-abwW8oJ1bCsJ6nOm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-abwW8oJ1bCsJ6nOm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-abwW8oJ1bCsJ6nOm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-abwW8oJ1bCsJ6nOm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-abwW8oJ1bCsJ6nOm .cluster text{fill:#333;}#mermaid-svg-abwW8oJ1bCsJ6nOm .cluster span{color:#333;}#mermaid-svg-abwW8oJ1bCsJ6nOm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-abwW8oJ1bCsJ6nOm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-abwW8oJ1bCsJ6nOm rect.text{fill:none;stroke-width:0;}#mermaid-svg-abwW8oJ1bCsJ6nOm .icon-shape,#mermaid-svg-abwW8oJ1bCsJ6nOm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-abwW8oJ1bCsJ6nOm .icon-shape p,#mermaid-svg-abwW8oJ1bCsJ6nOm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-abwW8oJ1bCsJ6nOm .icon-shape .label rect,#mermaid-svg-abwW8oJ1bCsJ6nOm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-abwW8oJ1bCsJ6nOm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-abwW8oJ1bCsJ6nOm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-abwW8oJ1bCsJ6nOm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 加载模型与 Tokenizer
解析任务配置
构建评测样本
批量推理生成
计算指标
汇总输出结果
各阶段的职责如下:
| 阶段 | 核心模块 | 主要职责 |
|---|---|---|
| 模型加载 | Model Adapter | 统一不同后端模型的加载与推理接口 |
| 数据构建 | Task / Dataset | 读取数据、应用 prompt 模板、构造输入 |
| 推理生成 | Evaluator | 组织 batch、管理分布式推理、产出预测 |
| 指标计算 | Metric | 计算准确率、F1、BLEU 等指标并汇总 |
4. 安装与依赖
在开始之前,建议准备 Python 3.10 及以上环境,并使用独立虚拟环境隔离依赖。
bash
python -m venv venv
source venv/bin/activate
pip install --upgrade pip
安装基础依赖,通常包括 PyTorch、Transformers、Accelerate 以及数据集相关库:
bash
pip install torch transformers accelerate datasets
pip install deepseek-harness
如果使用 DeepSeek 官方发布的权重,还需要确保与模型版本匹配的 tokenizer 和对话模板已经正确拉取。
5. 快速上手
下面演示一个最小化的评测流程:加载模型,并在一组样本上完成生成式任务评测。
python
from deepseek_harness import Harness, ModelConfig, TaskConfig
model_config = ModelConfig(
model_name="deepseek-ai/deepseek-llm-7b-chat",
dtype="bfloat16",
device_map="auto",
)
task_config = TaskConfig(
task_name="gsm8k",
num_fewshot=5,
batch_size=8,
max_length=2048,
)
harness = Harness(model_config=model_config)
results = harness.run(task_config)
print(results.metrics)
如果只需要评测某个通用能力,可以通过命令行快速启动:
bash
python -m deepseek_harness \
--model deepseek-ai/deepseek-llm-7b-chat \
--tasks gsm8k,hellaswag \
--num_fewshot 5 \
--output_path ./results
6. 评测任务配置
Harness 中的任务通常由几个关键字段描述:
task_name:任务标识,例如gsm8k、mmlu、humaneval。num_fewshot:上下文示例数量,0 表示零样本。batch_size:每个 GPU 上的批量大小。max_length:最大序列长度,超出部分截断。template:对话或指令模板,决定如何把样本包装成 prompt。
对于对话模型,模板尤其重要。错误的模板会导致模型能力没有被正确激发。例如一个简单的 QA 模板可能是:
python
template = "问题:{question}\n答案:"
sample = template.format(question="1 + 1 等于几?")
对于多轮对话任务,则需要将历史消息按角色序列化后再送入模型。
7. 核心模块解析
7.1 Model Adapter
Model Adapter 负责屏蔽不同后端的差异,无论是 Hugging Face Transformers、vLLM,还是 DeepSeek 的自研推理引擎,对上暴露一致的能力:
python
class BaseModelAdapter:
def load(self) -> None:
raise NotImplementedError
def generate(self, prompts: list[str], **kwargs) -> list[str]:
raise NotImplementedError
在实际实现中,Adapter 会处理:
- 显存分配与设备映射
- bfloat16 / float16 / int8 精度转换
- 并行推理的 batch 调度
- 输出解码与停止符控制
7.2 Task Registry
新任务通过装饰器注册到框架中,避免修改核心代码:
python
from deepseek_harness import register_task, Task
@register_task("my_custom_task")
class MyCustomTask(Task):
def load_data(self):
return [{"question": "2 + 2 = ?", "answer": "4"}]
def build_prompt(self, sample):
return f"问题:{sample['question']}\n答案:"
def compute_metric(self, predictions, references):
correct = sum(p == r for p, r in zip(predictions, references))
return {"accuracy": correct / len(references)}
注册完成后,即可在配置中直接使用 my_custom_task。
7.3 Metric 计算
指标模块将预测结果与参考答案进行比对。不同任务使用不同指标:
- 分类任务:accuracy
- 生成任务:exact match、BLEU
- 代码任务:pass@k
- 多标签任务:F1
Harness 通常会保留每个样本的细节日志,方便后续定位是提示词问题还是模型能力问题。
8. 实战:评测一个本地 DeepSeek 模型
假设我们已经在本地部署了 DeepSeek 模型,并通过 OpenAI 兼容接口提供服务,可以这样接入评测:
python
from deepseek_harness import Harness, ModelConfig
model_config = ModelConfig(
model_name="local-deepseek",
backend="openai",
api_base="http://127.0.0.1:8000/v1",
api_key="EMPTY",
)
harness = Harness(model_config=model_config)
results = harness.run("mmlu", num_fewshot=0)
print(results.metrics)
如果模型规模较大,需要多卡并行,可以在配置中扩展并行参数:
python
model_config = ModelConfig(
model_name="deepseek-ai/deepseek-r1",
dtype="bfloat16",
tensor_parallel_size=4,
gpu_memory_utilization=0.9,
)
9. 常见问题
Q1:评测结果与论文报告不一致怎么办?
优先检查三点:模型精度是否一致、few-shot 示例是否一致、对话模板与停止符是否一致。这些因素对最终分数影响很大。
Q2:长文本任务显存不足怎么办?
可以降低 batch_size,启用梯度检查点无关的推理优化,或调整 max_length 和 gpu_memory_utilization。
Q3:如何新增自有数据集?
实现 Task 子类并完成注册,重点保证 build_prompt 与 compute_metric 逻辑正确,即可复用 Harness 的加载与推理流程。
10. 总结
DeepSeek Harness 的本质,是把大模型评测中的重复工程抽象成一套可配置、可扩展的框架。理解它的核心思路------模型、任务、指标解耦------之后,无论是评测官方模型、接入本地服务,还是定制私有任务,都能以较低的成本获得稳定、可复现的结果。
对于正在构建模型能力评估体系的团队,建议优先把评测流程固化到 Harness 中,并持续记录 prompt 模板、模型版本与指标口径,这样每一次模型迭代都能沉淀为可对比的数据资产。