Liquid4All/encoder_eval
GitHub: Liquid4All/encoder_eval
一个可复现的 encoder 模型下游微调评测框架,通过严格的实验协议在 17 个标准任务上实现公平、精确的模型间性能对比。
Stars: 2 | Forks: 0
# encoder-eval-harness
**一个通过微调精确且公平地比较 encoder 模型的测试框架。**
在包含 17 个任务的套件(5 个多语言 + 8 个 GLUE + 4 个 SuperGLUE)上微调任意一组 encoder 模型,并报告
**无需选择的 `avg@5 ± std`**,因此表格中两个单元格之间只有模型权重不同。
**模型**:
- XLM-R {base, large, XL},
- mDeBERTa-v3-base,
- mGTE-MLM-base,
- EuroBERT {210M, 610M, 2.1B},
- ModernBERT {base, large},以及
- LFM2.5 encoder 家族 (Encoder-{230M, 350M}, Embedding-350M, ColBERT-350M)。
**任务** (17):
| 类别 | 任务 |
|---|---|
| 多语言 (5) | XNLI · PAWS-X · Amazon Reviews-Multi · MASSIVE-Intent · SeaHorse |
| GLUE (8) | CoLA · SST-2 · MRPC · STS-B · QQP · MNLI · QNLI · RTE |
| SuperGLUE (4) | BoolQ · CB · WiC · WSC |
对于每个多语言任务,所有语言的训练集都会被连接并打乱,进行一次微调,然后按语言进行评估,并在欧洲语言集上取平均值。所有数据集均取自公开的 Hugging Face 仓库。
## 安装
需要 Python ≥3.11、CUDA 或 ROCm GPU,以及 [`uv`](https://github.com/astral-sh/uv)
(或普通的 `pip`)。
```
make install # uv venv .venv + editable install
# 或: pip install -e .
```
`transformers` 版本被固定为 **4.56.2**,这样依赖项偏差就不会成为不受控制的评估变量。参见
[`EVALUATION_METHODOLOGY.md`](EVALUATION_METHODOLOGY.md) §5。
### ROCm (AMD GPU)
默认情况下,`pip install -e .` 和 `make install` 会安装 CUDA 版本的 PyTorch,这将无法检测到 AMD GPU。在 ROCm 上,请先安装匹配的 PyTorch wheel,然后再安装本框架。Pip 会发现已经安装了 `torch>=2.4`,因此它会保留你的 ROCm 构建版本:
```
python -m venv .venv && source .venv/bin/activate # Python >=3.11
# 从 https://pytorch.org/get-started/locally/ 中为你的 ROCm 版本选择 wheel index
pip install --index-url https://download.pytorch.org/whl/rocm6.4 torch
pip install -e . # installs transformers==4.56.2 etc.; torch untouched
```
在运行扫描之前,请确认 GPU 可见:
```
python -c "import torch; print(torch.__version__, torch.version.hip, torch.cuda.is_available())"
# 例如: 2.9.1+rocm6.4 6.4.xxxxx True
```
之后,`make smoke-paws`、扫描和 `summarize` 的工作方式与在 CUDA 上完全相同。
## 使用方法
### 运行单个微调
一个单元格是指在一个任务上以一个学习率对单个模型进行的一次微调。它将结果写入一个 JSON 文件。
```
python -m encoder_eval.run \
--model-path FacebookAI/xlm-roberta-base \
--task paws_x --mode auto \
--lr 3.59e-05 --seed 45 --bsz 32 --max-steps 10000 --max-seq-len 256 \
--early-stop --patience 3 --epoch-cap 20 \
--out-json results/xlm-r_paws_lr06_s45.json
```
要检查你的安装是否正常,请运行 `make smoke-paws`。它会运行上述命令的一个快速 1000 步版本。
### 运行扫描
该协议分两个阶段运行。首先是选择阶段,仅使用 dev 分数来扫描学习率网格。然后是报告阶段,在选定的学习率下运行新的随机种子。每个阶段都会写入各自的 `run_all.sh`。
```
# 1. selection sweep — 10 个 LRs x 3 个 selection seeds (42-44),仅限 dev
python -m encoder_eval.sweep --phase select \
--out-dir runs/select --results-dir results/select --mode shell
bash runs/select/run_all.sh
# 2. 通过按 seed 平均的 dev 均值,为每个 (model, task) 挑选出最佳的 LR
python -m encoder_eval.select --results-dir results/select --out selection.json
# 3. reporting sweep — 在选定的 LR 下使用 5 个全新的 seeds (45-49)
python -m encoder_eval.sweep --phase report --selection selection.json \
--out-dir runs/report --results-dir results/report --mode shell
bash runs/report/run_all.sh
```
这两个阶段都明确应用了共享方案(`--weight-decay 0.1 --adam-beta2 0.95`,patience 为 3 的早停机制,epoch 上限 20)。两者都是幂等的:如果某个单元格的结果 JSON 已存在,则会跳过,因此重新运行只会填补空缺。结果文件名包含随机种子(`{model}_{task}_lr{key}_s{seed}.json`),因此种子之间绝不会相互覆盖。
使用 `--models` 和 `--tasks` 控制运行内容。默认情况下,扫描会使用注册表中的每个模型以及 5 个多语言任务。要添加 GLUE 或 SuperGLUE 任务,请指定它们的名称,例如 `--tasks glue_sst2,glue_mnli,sg_boolq`。
完整的 14 个模型、17 个任务的协议大约包含
7,100 个选择作业(10 个学习率 × 3 个种子)以及约 1,200 个报告作业(5 个种子)。
每个作业都是独立的单 GPU 进程,因此你可以将它们分配到你拥有的任何调度程序中。
### 构建表格
```
python -m encoder_eval.summarize --results-dir results/report \
--selection selection.json --format markdown # or json / csv
```
`summarize.py` 会计算每个单元格的报告种子的平均值,并报告 `mean ± std`。它不会挑选最佳运行结果,因为学习率已经在第 2 步中使用独立的种子确定了。每个任务都使用其标准指标:CoLA 使用 MCC,MRPC 和 QQP 使用 F1,CB 使用 macro-F1,STS-B 和 SeaHorse 使用 Spearman,其他情况使用准确率。分数来自每个任务的 report 拆分,即多语言任务的带标签 test 拆分,以及 GLUE 和 SuperGLUE 的 dev 拆分(标记为 `*`)。所有内容均无需手动输入。
默认情况下,如果表格不完整,`summarize.py` 将拒绝打印。如果某个单元格缺少报告种子、处于非选定学习率,或者在没有协议标志的情况下生成,则会将其视为错误,而不是默默地取平均值。传递 `--allow-partial` 可强制打印;此时脚注将指明真实的种子数量,而不会谎称是 avg@5。
## 方法论
每一个设计选择都将模型隔离为唯一的变量,控制了种子、学习率和精度。完整的协议和复现陷阱详见
[`EVALUATION_METHODOLOGY.md`](EVALUATION_METHODOLOGY.md)。
1. **统一精度。** 每个模型都加载 fp32 主权重和 bf16 autocast。不允许直接以 bf16 加载模型,因为那将是在比较数字格式而不是模型本身。
2. **共享单一优化器方案。** 每个模型都使用相同的 AdamW 设置($\beta_2$=0.95,`wd`=0.1,10% 线性 warmup,`bsz` 32),源自 EuroBERT 模型卡,适用于所有模型。
3. **基于种子平均的 dev 均值选择 LR。** 对于每个模型和任务,测试框架会在 3 个种子上尝试从 1e-5 到 1e-4 的 10 个学习率,并选择平均表现最佳的一个。
4. **来自全新留出种子的分数。** 报告的分数是 5 个从未参与选择的种子的 mean ± std。
5. **有界且早停的训练。** `max_steps = min(10 000, N epochs)`,patience 为 3,`load_best_model_at_end`。
6. **报告拆分。** 多语言任务报告带标签的 test 拆分。GLUE/SuperGLUE 报告 dev 拆分(它们的测试标签是隐藏的)。
7. **原子化、幂等的 I/O** — 通过 `tmp + os.replace` 写入结果;跳过已有的单元格,忽略无法解析的文件。
## 结果
此源代码库特意不包含任何未发布的组织结果工件或预发布性能声明。运行文档中记录的扫描并使用 `encoder_eval.summarize` 从你自己的结果文件中生成比较表。
## 扩展
### 添加模型
你可以通过在 [`encoder_eval/sweep.py`](encoder_eval/sweep.py) 中的 `MODEL_REGISTRY` 中添加一行来添加模型:
```
"my-encoder-base": ("org/my-encoder-base", "auto"), # standard HF *ForSequenceClassification
"my-encoder-body": ("org/my-encoder-body", "liquid-bidir"), # encoder body + pooled head
```
- **`auto`** — 标准的 `AutoModelForSequenceClassification`(+ `trust_remote_code`)。
适用于任何暴露了序列分类头的 HF encoder。
- **`liquid-bidir`** — 通过 `AutoModel` 加载 encoder *主体*,并附加一个全新的 mean-pool + linear 分类器头。适用于仅提供 backbone 的双向 encoder(例如 LFM2.5 encoder 家族)。
任何特定于架构的设置都会在
[`encoder_eval/loaders.py`](encoder_eval/loaders.py) 中为你处理。
### 添加任务
在 [`encoder_eval/tasks.py`](encoder_eval/tasks.py) 中添加一个加载器,返回
`(train_dict, per_lang_val, per_lang_test, num_labels, problem)`,然后将其注册到
`LOADERS` 和 `TASK_KNOBS` 中。`problem` 的值为 `single_label_classification` 或 `regression`。
## 来源与可复现性
每个结果 JSON 都记录了生成它所需的全部内容:测试框架版本和 git SHA,`transformers` / `datasets` / `torch` 版本,CUDA 或 HIP 构建版本,GPU 名称,完整的方案(权重衰减、β₂、warmup、有效批次大小、精度),以及——最重要的是——**解析出的模型版本**(模型在 Hub 上的确切 commit hash,而不是你请求的浮动 tag)。因此,可以检查两个 JSON 是否具有可比性,而不是假定可比。
已知限制:
- 只有 `transformers` 的版本被固定;其他依赖项都是版本范围,并且该仓库**不提供 lockfile**。对于可作为引用的运行,请冻结你的环境
(`uv pip freeze > requirements.lock`)并将其与结果一并保存。
- 数据集版本默认为**未固定**(加载器会跟踪每个公共 HF 数据集的当前版本)。对于可作为引用的运行,请固定它们——实际生效的任何内容都会记录在
`provenance.dataset_revisions` 下:
export ENCODER_EVAL_DATASET_REVISIONS='{"nyu-mll/glue": "", "aps/super_glue": ""}'
(或编辑 `encoder_eval/tasks.py` 中的 `DATASET_REVISIONS`)。
- 结果只有在同一技术栈生成的运行之间才具有可比性。在将两个结果 JSON 中的数字放入同一个表格之前,请比较它们的 `provenance`。
### `trust_remote_code`
两种加载器模式都会传递 `trust_remote_code=True`,这会**从 Hub 执行由模型作者编写的 Python 代码**。这对于注册表中几个架构未包含在 `transformers` 中的 encoder(EuroBERT、mGTE、LFM2.5 家族)是必需的。相关后果:
- 仅评估你愿意从中运行任意代码的模型。
- 浮动版本意味着你执行的代码在不同运行之间可能会发生变化。在必要时固定一个特定的
commit —— `--model-revision `(被 `encoder_eval.run` 接受)—— 并检查结果 JSON 中记录的 `provenance.model_revision`,以查看具体运行的内容。
## 限制
- GLUE/SuperGLUE 报告 **dev** 拆分(它们的测试标签是隐藏的);全新的种子规则消除了选择耦合,但对于这 12 个任务,dev 仍然是报告拆分(参见方法论第 6 点)。
- 保留 **WSC** 是为了完整性,但它没有提供有用的信息——在此方案下,没有模型能胜过多数类。
- 结果 JSON 和生成的启动脚本被 git 忽略(`results*/`, `runs*/`);
它们是运行工件,而非源码。
- 默认情况下,结果 JSON 中会省略确切的模型路径,因为本地路径和私有仓库名称可能属于敏感信息。仅对受到适当限制的输出使用 `--record-model-path`。
## 文档
- [`AGENTS.md`](AGENTS.md) — 通过代码代理驱动测试框架
## 安全性
有关凭证处理和私下报告的指南,请参阅 [`SECURITY.md`](SECURITY.md)。
该仓库包含一个可选的 `detect-secrets` pre-commit hook。
## 许可证
Apache-2.0。请参阅 [`LICENSE`](LICENSE)。
## 引用
```
@article{liquidAI2026Encoders,
author = {Liquid AI},
title = {LFM2.5-Encoders: Fast at Long Context, Even on CPU},
journal = {Liquid AI Blog},
year = {2026},
note = {www.liquid.ai/blog/lfm2-5-encoders},
}
```
标签:Apex, DLL 劫持, Python, Vectored Exception Handling, 人工智能, 凭据扫描, 大语言模型, 微调, 无后门, 机器学习, 模型评估, 用户模式Hook绕过, 逆向工具