☰
DeepOpen × Banking77:可训练余弦原型分支的结构改动实验——验证集消融、测试集归因与完整复现记录
2026/9/26 15:50:44 网站建设 项目流程

【免费下载链接】deepopen

非自回归System 1决策引擎,专为结构化类型决策场景设计 DeepOpen Multilingual, non-autoregressive System 1 decision engine.

项目地址:https://gitcode.com/gh_mirrors/de/deepopen
点击查看免费下载

本文基于 banking77/RESULTS_V2.md 展开,完整记录 DeepOpen 项目在 Banking77 77 类银行意图分类任务上的一次模型结构改动实验:以 Laya 编码器 + ModernBERT 分类头为起点,叠加一个可训练的余弦类别原型残差分支,并给出验证集消融、官方测试集对比、McNemar 统计检验与可复现命令。读者读完后既能理解"原型分支 + 残差融合 + 辅助损失"这一组合结构的完整设计,也能掌握如何通过控制组对照严谨归因结构收益,并能按本文命令在服务器上复现或部署该模型。

一、实验结论速览:一次记录在案的"未成功"结构改动

本轮实验的核心目标是验证"在分类头上叠加可训练余弦原型分支"能否带来超过 94% 的官方测试集准确率。结论是明确的负结果:

  • 按验证集选中semantic变体,官方测试集准确率93.70%,macro-F193.71%,未达到 94% 的阶段目标;
  • 同轮数普通训练对照(control)达到 94.09%,超过了 94%,但该收益来自额外训练本身,不能归因于新增的原型结构;
  • 相对旧分类头的 93.80%,本轮没有提高测试准确率;验证集上的提升未能转化为测试集提升,因此应记录为未成功的结构改动实验,旧模型和推荐链接均保留原状。

这条记录的价值在于它是一份严谨的"负结果实验报告":结构改动有明确消融、有对照归因、有统计检验、有完整复现命令,方法论上比一份"只报最好成绩"的文档更值得借鉴。

二、结构改动细节:余弦原型分支如何叠加到分类头

2.1 改动起点

以Laya 编码器 + Transformers ModernBERT 分类头为起点(即 banking77/classifier.py 中训练的固定 77 类分类器)。注意:这是固定的 77 类分类接口,不是原生 Laya 的可变候选决策头(详见 banking77/README.md 对架构差异的说明)。

  • 原分类头保留其原有池化方式(cls或均值池化,按 backbone 配置决定);
  • 新增的原型分支利用全部非填充 token 的均值表示(masked mean pooling);
  • 两条路径共享同一个 Laya 编码器,推理时仍只运行一个编码器,不增加第二套编码器。

2.2 融合公式

融合 logits 的数学形式为:

logits = original_logits + sigmoid(gate) * exp(scale) * cosine(mean_hidden, prototypes)

其中:

符号含义初始化与约束(源码确认)
gate残差融合门控系数初始化为-1,经sigmoid后取值 (0, 1),随训练更新(prototype_model.py)
scale原型 logits 的温度缩放以log(20.)初始化,前向时先clamp(0, 4.6)再exp(),即缩放系数约束在[1, e^4.6]区间
prototypes可训练类别原型矩阵形状(77, hidden_size),nn.Parameter,从锚点表示初始化
mean_hidden全部非填充 token 的均值表示sum(h * mask) / sum(mask),推理与训练一致

原型、融合系数(gate/scale)、温度和编码器共同训练;新增可训练参数78,850 个(77 × 1024 的原型矩阵 78,848 个 + gate 和 scale 各 1 个,与 ModernBERT-large 的 hidden_size=1024 吻合),未增加第二套编码器。

2.3 两种原型初始化:prototype 与 semantic

训练脚本 prototype_train.py 提供三个变体:

  • control:仅继续普通分类训练,关闭原型分支和辅助损失,用于区分"新增结构的收益"和"额外训练的收益";
  • prototype:原型从训练样本各类别的平均表示(类中心)初始化;
  • semantic:在 prototype 基础上,额外混入20% 类别名称表示,即anchors = normalize(0.8 * centroid + 0.2 * semantic),随后归一化。

semantic 变体的类别名称文本模板为"A banking customer asks about " + s.replace('_',' ') + "."(源码确认),将类别语义信息注入原型初始值。

2.4 训练目标

训练目标为融合 logits 的交叉熵,叠加两项辅助损失(系数在 prototype_model.py 的prototype_config.json中持久化):

L = CE(logits, y) + 0.2 * CE(prototype_logits, y) + 0.01 * anchor_loss

其中anchor_loss = (1 - cosine_similarity(prototypes, anchors)).mean(),即约束可训练原型不要偏离其初始化锚点太远。

2.5 架构图

flowchart LR A[输入文本] --> B[Laya 编码器] B --> C[均值池化] C --> D[原非线性分类头] C --> E[余弦原型分类分支] P[可训练类别原型] --> E D --> F[学习权重后融合 logits] E --> F F --> G[77 类预测]

2.6 源码级实现要点

核心实现位于 banking77/prototype_model.py 的PrototypeClassifier:

  • 前向计算时先取last_hidden_state,用 attention mask 做加权均值得到mean,再按classifier_pooling配置决定原分类头的池化输入;
  • 原分类头 logits(ce_logits)与原型 logits(scale * cosine(mean, prototypes))通过gate.sigmoid()加权求和;
  • control变体直接返回ce_logits,proto=None,无辅助损失,行为与普通分类器完全一致;
  • save_pretrained将 backbone 存为标准 Transformers 子目录,额外分支单独存为prototype.safetensors,并写入prototype_config.json(记录 variant、aux_ce、anchor_regularization);
  • from_pretrained用AutoModelForSequenceClassification.from_pretrained(path/backbone, attn_implementation="sdpa")重建 backbone,加载原型分支权重时严格断言额外参数字典与模型状态完全一致。

这正是文档中"推理仍只运行一个编码器"的实现基础:额外分支只是编码器之上的轻量计算。

三、验证集消融:三个分支的对比

3.1 实验协议

三组实验使用同一个仅见过 8,502 条训练样本的初始 checkpoint(即classifier_runs/encoder_lr2e5/best),验证集 1,501 条。训练脚本对非 full-train 模式有硬性断言:起点必须为该固定 checkpoint,防止从全量训练模型出发污染验证结论(源码确认)。

统一超参数如下:

超参数设置
最大追加 epoch4
随机种子seed=42
编码器学习率5e-6
头部学习率5e-5(编码器的 10 倍)
实际 batch32(micro-batch 16,累积 2 次)
weight decay0.01(仅矩阵参数,偏置与归一化参数为 0)
warmup0.1
优化器 / 调度AdamW + 线性学习率衰减
精度 / 硬件BF16 自动混合精度,NVIDIA L20
选模规则按验证 accuracy 选模型及 epoch,同分看 macro-F1

3.2 消融结果

结构验证 accuracy验证 macro-F1追加 epoch
原分类头,不追加训练91.27%91.22%0
control91.61%91.62%2
prototype91.87%91.76%2
semantic92.14%92.02%4

参考值:原生 Laya 的历史最佳验证 accuracy 为 91.61%(不同结构和训练设置,单独列为参考,不可直接对比)。

从消融看,semantic 在验证集上最优,且控制组本身也有提升(91.27% → 91.61%),说明"额外训练"本身就在起作用——这正是后续设置同轮数普通训练对照的原因。按验证集规则选中 semantic。

四、官方测试结果与统计检验

4.1 测试集对比

完整测试集 3,080 条(每类 40 条)结果:

模型AccuracyMacro-F1
之前的原生 Laya 微调93.44%93.42%
之前的 Laya 编码器 + 普通分类头93.80%93.81%
同轮数普通训练对照94.09%94.09%
本轮 semantic93.70%93.71%

4.2 配对统计检验(McNemar)

  • 相对旧分类头:纠正 15 条,新增错误 18 条,正确预测数净变化-3条;配对精确 McNemar 检验p=0.7283,远未达到显著性水平;
  • 相对同轮数普通训练对照:纠正 11 条,新增错误 23 条,净变化-12条;配对精确 McNemarp=0.0576,未达到常用的 0.05 显著性门槛,应视为初步证据。

文档明确提醒:McNemar 检验不能替代多随机种子实验,当前结论仅基于一个训练种子。

4.3 实验边界(诚实声明)

  • 选定方案后,从原有全量训练 7 epoch 的分类器起点,使用全部 10,003 条训练样本重建原型,并追加选定 epoch 数量;测试集为完整 3,080 条;
  • 原有测试分数此前已经查看,因此本轮属于探索性后续实验,不能称作"从未接触测试结果的确认实验";
  • 本轮未利用测试样本、标签或错误构建原型;模型选择在测试评估前写入selection_before_test.json,新测试结果不用于开启进一步搜索;
  • 重新加载 checkpoint 的逐条预测一致,accuracy/F1 独立重算通过;
  • 同轮数普通训练对照在新测试评估前预先规定(同起点、同样追加 4 个 epoch、相同学习率与 batch,仅关闭原型分支和相应辅助损失),用于归因分析,不改变验证集选定的主模型;
  • 当前只有一个训练种子,尚不能证明跨种子稳定性,也没有外部榜单提交。

五、"94.09% 从何而来":与榜单分数的关系

文档在此处做了非常严格的边界划分:

  • 94.09% 是官方公开 test split 上的本地结果,不是已经认证的外部榜单成绩,它来自预先指定的普通训练对照,原型改动的验证选定主模型仍为 93.70%;
  • 普通训练对照已用标准AutoModelForSequenceClassification独立加载导出文件复核,3,080 条预测与实验评估逐条一致,见STANDARD_EXPORT_VERIFIED.json;
  • 复现设置:完整 test split、原标签映射、max_length=128、batch=16、BF16、SDPA,tokenizer 与权重均从同一 checkpoint 加载(与 banking77/deepopen_evaluate.py 的评测口径一致,该脚本还会校验测试数据 SHA256);
  • 若目标榜单允许全量监督微调并采用相同测试集与评估规则,该结果具有可复现依据;隐藏测试集、少样本/零样本设置、不同数据版本或推理规则均不能直接套用本分数;
  • 具体榜单尚未指定,因此没有宣称榜单等价性;上传模型本身也不等于获得榜单认证。

在原服务器复核标准导出模型:

cd /opt/laya-banking77 CUDA_VISIBLE_DEVICES=0 venv/bin/python verify_standard_export.py

六、方法定位:原型分类与余弦间隔的已有工作

类别原型和归一化余弦分类已有明确先例,文档明确引用了两类经典工作:

  • Prototypical Networks(原型网络):用类别原型做度量学习的代表性方法;
  • CosFace:归一化余弦分类间隔方法。

本轮的定位是:可训练原型 + 残差融合 + 训练期辅助损失的组合实现,没有实现 CosFace 的余弦间隔,也不是其复现实验。因此现阶段适合作为"有结构改动、有消融"的技术报告,论文的新颖性和稳定性仍需进一步验证——文档没有宣称任何论文级新颖性,这一边界在 prototype_model.py 的模块 docstring 中同样有明确声明。

七、复现与部署:文件结构、命令与加载方式

7.1 服务器目录结构

/opt/laya-banking77/ ├── research_v2/ │ ├── full_train_semantic/best/ # 验证集选定的实验 checkpoint │ ├── matched_control/best/backbone # 94%+ 普通训练对照的标准 Transformers checkpoint │ ├── anchors_train.pt / anchors_full.pt # 锚点缓存(含来源与数据指纹校验) │ └── ...(各实验配置、逐轮日志、选择依据、测试预测、权重 SHA256) └── ...

所有实验配置、逐轮日志、选择依据、测试预测和权重 SHA256 均保存在research_v2目录;旧模型和结果没有覆盖。可复现包包含本轮代码及旧轮基础包,但不包含大型模型权重。

7.2 复核已保存模型

只读取 checkpoint,写入独立评估目录,不会改动原模型:

CUDA_VISIBLE_DEVICES=0 venv/bin/python prototype_train.py evaluate \ --checkpoint /opt/laya-banking77/research_v2/full_train_semantic/best \ --split test --name user_reproduction

该命令会生成research_v2/user_reproduction/test_predictions.json(含 metrics、gold、predicted 和 checkpoint 路径)与test_report.json(分类报告)。evaluate模式还会断言模型的id2label与数据标签表完全一致。

7.3 从旧轮训练起点重跑获选方案

# 验证集协议重跑(起点必须为 encoder_lr2e5/best,脚本硬性断言) CUDA_VISIBLE_DEVICES=0 venv/bin/python prototype_train.py train \ --variant semantic --name rerun_validation --epochs 4 --seed 42 # 全量重跑 semantic(从全量 7 epoch 分类器起点) CUDA_VISIBLE_DEVICES=0 venv/bin/python prototype_train.py train \ --variant semantic --name rerun_full --full-train \ --source /opt/laya-banking77/classifier_runs/encoder_full_train/best \ --epochs 4 --seed 42 # 重跑相同轮数的普通训练对照 CUDA_VISIBLE_DEVICES=0 venv/bin/python prototype_train.py train \ --variant control --name rerun_control --full-train \ --source /opt/laya-banking77/classifier_runs/encoder_full_train/best \ --epochs 4 --seed 42

7.4 CLI 参数说明(源码确认)

prototype_train.py 支持的参数与默认值:

参数取值 / 默认说明
modetrain/evaluate/smokesmoke 校验 base logits 一致、原型梯度有限、保存/加载逐位一致
--variantcontrol/prototype/semantic,默认semantic结构开关
--name默认semantic_s42输出目录名(research_v2/<name>)
--source默认classifier_runs/encoder_lr2e5/best起点 checkpoint
--epochs默认 4追加训练轮数
--seed默认 42随机种子
--lr默认 5e-6编码器学习率,头部为其 10 倍
--full-train布尔开关使用全部 10,003 条训练样本
--checkpoint可选evaluate 模式指定 checkpoint
--splitvalidation/test,默认validation评估数据划分

训练内部固定:micro-batch=16 累积 2 次得到有效 batch=32、梯度裁剪 1.0、AdamW 分组参数(编码器 lr=5e-6、头部 lr=5e-5,矩阵参数 weight decay=0.01,bias/norm 无衰减)、线性 warmup 0.1、BF16 autocast。每个 epoch 结束记录 loss、耗时、gate与scale的实际收敛值到history.json;验证协议下按 (accuracy, macro-F1) 保存最优 checkpoint 并写入best.json。

7.5 部署方式

初始化模型、训练数据、隔离环境的复现方法见旧轮 banking77/RESULTS.md 与 banking77/README.md。部署时:

  • 将 banking77/prototype_model.py 放在 Python 路径中;
  • 使用PrototypeClassifier.from_pretrained(checkpoint)加载;
  • tokenizer 从checkpoint/backbone读取;
  • 该类支持标准 Transformers 编码器和 tokenizer,额外分支单独保存在prototype.safetensors中(prototype_config.json记录变体与辅助损失系数)。

需要特别注意:普通训练对照(control)部署时不需要自定义原型类,直接用标准AutoModelForSequenceClassification加载matched_control/best/backbone即可;而 semantic 模型必须使用完整best/目录和PrototypeClassifier。

八、从源码看训练细节:锚点构建与数值严谨性

8.1 锚点缓存的构建与复用

训练脚本首次运行时会从声明的训练划分(train或full_train,绝不使用 validation/test)计算锚点:

  1. 前向得到每个样本的归一化均值表示,按类别累加求类中心,归一化得到centroid;
  2. 对 77 个类别名称文本(模板见上文)前向,取均值表示归一化得到semantic;
  3. semantic 变体组合为normalize(0.8 * centroid + 0.2 * semantic)。

结果缓存到anchors_train.pt/anchors_full.pt,复用前会严格校验缓存的 source checkpoint 与 data SHA256,防止缓存污染。构建前保存 CPU RNG 状态、构建后恢复,确保锚点缓存创建/复用不会改变后续训练顺序。

8.2 数值与复现严谨性

  • 前向与评估均在 BF16 autocast 下进行,并断言 logits/loss 全为有限值;
  • 冒烟测试(smoke模式)断言:原型分支启用时 base logits 与原 backbone 输出在容差内一致、原型梯度有限、save/load 后 logits 逐位一致(torch.equal校验 argmax、allclose校验数值);
  • 实验配置、数据 SHA256、gate/scale收敛值、选择依据全部落盘,便于事后审计。

九、结论与后续方向

9.1 本轮实验的完整图景

把验证集与测试集放在一起看,结论非常清晰:

  1. 验证集提升 ≠ 测试集提升:semantic 在验证集以 92.14% 胜出,测试集却只有 93.70%,低于 control 的 94.09% 和旧分类头的 93.80%;
  2. 对照实验不可省略:同轮数 control 本身就比"不追加训练"的 91.27% 高出 0.34 个百分点,若没有这个对照,很容易把额外训练的收益误归因于新结构;
  3. 统计检验约束结论:与旧分类头的差异 p=0.7283 无显著性,与 control 的差异 p=0.0576 也仅属初步证据,加上单一训练种子,任何"结构有效"的说法都不成立。

9.2 建议的后续方向(文档与 README 中均为待验证计划)

方向建议做法判断标准
多种子稳定性为 7 轮分类器、control、semantic 配对运行至少 3 个种子报告所有种子、均值、标准差和配对差值
分离额外训练收益固定训练预算,比较连续 11 轮、7+4 分阶段、只降低学习率区分训练时长与调度重启的贡献
简化原型分支分别关闭语义初始化、辅助 CE、锚点约束,固定其余超参数先比较验证集,再冻结一次测试
置信度与拒识仅用 validation 拟合温度,另准备 OOS 数据同时报告分类准确率、校准指标和 OOS 指标
推理成本同机同 batch 比较原生决策结构与普通分类头分别测分词、前向、峰值显存和端到端延迟

为避免继续使用同一测试集造成选择偏差,后续实验应先写好候选范围与选择规则再开展实验,有条件时引入独立外部数据验证。

9.3 一份可复用的实验方法论

即使结构改动本身是负结果,本轮实验仍留下了完整的方法论资产:固定划分与数据指纹(banking77/banking77.py)、验证集选模协议、同轮数控制组归因、配对 McNemar 检验、测试前选择落盘(selection_before_test.json)、标准 Transformers 导出复核(STANDARD_EXPORT_VERIFIED.json)以及完整的smoke数值校验。这套流程可以原样迁移到其他结构改动实验——先证明收益可归因,再谈结构创新,这正是本文最值得引用的实践结论。

延伸阅读

  • banking77/README.md:主线复现指南(环境准备、7+4 两阶段训练、评测与推理示例)
  • banking77/RESULTS.md:旧轮实验记录(原生 Laya 微调与分类头对照)
  • banking77/prototype_model.py:PrototypeClassifier 完整实现
  • banking77/prototype_train.py:训练、评估、冒烟测试入口
  • banking77/classifier.py:基础 77 类分类头训练
  • banking77/deepopen_evaluate.py:标准分类器完整 test 评测与数据哈希校验
  • banking77/SOURCE_MANIFEST.json:脚本来源与原文件哈希

【免费下载链接】deepopen

非自回归System 1决策引擎,专为结构化类型决策场景设计 DeepOpen Multilingual, non-autoregressive System 1 decision engine.

项目地址:https://gitcode.com/gh_mirrors/de/deepopen
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询