layazh — Laya 中文版决策模型

已发布:https://huggingface.co/NovaAI6868/layazh-zh

⚖️ 许可协议(重要)

本模型采用自定义协议,不是开源许可:

个人使用免费 · 商业使用需付费授权

  • 个人使用(含研究、学习、非营利、以及年营收 < 1 万元人民币的个人开发者):免费
  • 商业使用:需事先取得付费商业授权
  • 商业授权联系:novaweb6868@outlook.com

完整条款见本仓库的 LICENSE 文件。 提交任何贡献即视为同意该文件第三节的贡献者许可协议(CLA)。

注意:本模型中推理与训练代码源自 Apache-2.0 许可的 convaiinnovations/laya;上述自定义协议 主要约束模型权重。代码部分您仍可按 Apache-2.0 使用。


非自回归的中文 System 1 决策模型。 给它一个 state(中文文本 / 邮件 / 工单 / JSON) 和若干带类型的问题,它在一次前向传播里返回带校准概率的答案。它不生成任何文本, 所以没有东西需要解析,也没有东西可以幻觉。

架构与训练配方对齐 convaiinnovations/laya, 编码器换为中文预训练 backbone 并在中文决策数据上微调。

与上游 Laya 的关系

上游 convaiinnovations/laya(仓库根) layazh(本模型)
定位 英文 中文
编码器 answerdotai/ModernBERT-large(28 层, 1024 hidden, 395M) hfl/chinese-roberta-wwm-ext-large(24 层, 1024 hidden, 325M)
决策头 2 层 TransformerEncoder + marker scorer + act head 同构(逐行对齐上游 DecisionModel)
总参数 421M 352M
上下文 512 tokens 512 tokens
选项预算 head_max_len = 192 head_max_len = 192
训练配方 RLCD:log + 0.5·spherical(序数题另加 RPS),REINFORCE + group-mean baseline,多轮 TD(λ=1) 同配方,实测采用:rl_weight=8、kl_weight=1、group=4、σ=0.5、τ=1.5

训练配方的具体做法(同构部分与实测取值)

奖励函数两侧逐字一致(我方 layazh.model.proper_reward 与上游 laya.common.proper_reward 对拍 确认默认参数相同):

reward = log_score + 0.5 × spherical_score        # 所有题型
       − 1.0 × ranked_probability_score           # 仅序数题 (score)

三者都是严格恰当评分规则,因此最大化期望奖励的唯一方式就是如实报告概率—— 这是校准性的来源,而不是事后补丁。

具体训练设置(我方为实测值;上游的这几项未随 checkpoint 发布,故不臆测):

项 上游 本模型(layazh)
策略梯度估计 REINFORCE + group-mean baseline(GRPO 风格) 同
探索噪声 对选项 logits 加零均值高斯噪声 σ = 0.5,每组采样 4 次取组均值作基线
采样温度 未公开 τ = 1.5
目标函数 策略梯度 + 稠密 masked-KL 辅助项 8.0 × PG + 1.0 × KL
多轮对话 TD(λ = 1.0) 于前缀切片 同(代码路径一致)
决策头 lr 未公开 3e-4(AdamW)
编码器 lr 未公开 2e-5(分组,encoder_lr_scale=0.067)
可训练范围 全量微调编码器 顶部 8/24 层 + 决策头(12 GB 显存约束)
LR 调度 未公开 线性 warmup 6% + cosine 衰减
其他 — 选项顺序增强开、grad clip 1.0、gradient checkpointing 开、fp32

诚实说明:上游只发布了权重与推理配置,没有发布 RLCD 的训练超参 (rl_agent_config.json 里只有 temperature 与 updates: 7313 / hours: 1.96 这类元数据)。 因此"同配方"指的是算法与奖励函数相同,而上表中的具体数值是本项目自行确定并实测的, 不能声称与上游的隐藏设置一致。

序列模板与上游完全一致(已用上游 laya 包内的 render_options / QTYPES 做过程序化对齐校验):

[CLS] <qtype> 问题:<中文指令> [SEP] [MASK] 选项0 [MASK] 选项1 ... [SEP] <state> [SEP]

每个选项前一个 [MASK] marker,决策头读这些 marker 位置的隐状态并在该问题的选项上做 softmax。 noul 恒渲染为 [false, true],故 p[1] = “该命题成立”的概率。

安装

layazh 包已随本仓库提供 wheel,无需克隆仓库、也无需 Hugging Face token。

第 1 步:安装 layazh 包

# 推荐:uv(也可直接用 pip,把 uv pip 换成 pip 即可)
uv venv --python 3.11 .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate

# 从本仓库安装(固定到当前 commit,长期可复现;依赖会自动装好)
uv pip install https://huggingface.co/NovaAI6868/layazh-zh/resolve/d55f726a84aa2c17186c49d20fc35cf49c633f0e/layazh-0.1.0-py3-none-any.whl

这会把 torch / transformers / numpy / safetensors / huggingface_hub 一起装上, 之后 from layazh import ZhAgent 在任何目录都能直接用,**不需要改 sys.path**。

第 2 步(老显卡必读):换成匹配你 GPU 架构的 PyTorch

上一步装的是默认 PyTorch(面向较新的 CUDA)。如果你的卡比较老,需要替换:

你的 GPU 命令
Maxwell / Pascal(GTX 750–1080、Titan X 等,sm_50–sm_61) uv pip install torch==2.7.1 --index-url https://download.pytorch.org/whl/cu128
Turing / Volta(RTX 20 系、T4) uv pip install torch --index-url https://download.pytorch.org/whl/cu121
Ampere 及更新(RTX 30/40 系、A100) 默认装的即可,无需替换

为什么 Maxwell 要锁 2.7.1:torch 2.7.1 + cu128 是最后一个包含 sm_50 cubin 的 wheel(可在 sm_52 上执行)。更新的版本不再为这些架构编译内核。本模型就是在 GTX Titan X(sm_52)上用这个组合训练和推理的。

另外该架构不支持 bfloat16,代码会自动把 dtype 收敛到 fp16/fp32, 不需要你手动处理。

第 3 步:验证安装

import layazh
print(layazh.__version__)          # -> 0.1.0

from layazh import ZhAgent
agent = ZhAgent.from_pretrained("NovaAI6868/layazh-zh")   # 首次下载约 1.4 GB
print(agent.device)                # -> cuda 或 cpu

没有 GPU 也能跑(CPU 推理,只是慢一些)。仅安装 CPU 版 PyTorch 可以用 --index-url https://download.pytorch.org/whl/cpu。

想从源码安装 / 复现训练

git clone https://huggingface.co/NovaAI6868/layazh-zh layazh-zh
cd layazh-zh
uv pip install -e .                # 可编辑安装
uv pip install -e ".[train]"       # 训练所需额外依赖(pandas/pyarrow/scipy)

用法

from layazh import ZhAgent

# 从 Hub 加载(首次自动下载约 1.4 GB)
agent = ZhAgent.from_pretrained("NovaAI6868/layazh-zh")
# 也可从本地目录加载:agent = ZhAgent("checkpoints/layazh-zh")

res = agent.system_one(
    "这家酒店位置很好,出门就是地铁站,前台小姐姐特别热情,还帮我们升级了房型。"
    "房间干净整洁,早餐品种也很丰富,下次来一定还住这里。",
    {
        "sentiment": {
            "type": "choice",
            "instructions": "这条评论的情感倾向是什么?",
            "criteria": {"positive": "正面评价,表达满意或推荐",
                         "negative": "负面评价,表达不满或抱怨"},
        },
        "rating": {
            "type": "score",
            "instructions": "这条评论给出的总体评分是多少?",
            "criteria": ["非常差", "较差", "一般", "不错", "非常好"],
        },
        "recommend": {
            "type": "noul",
            "instructions": "这位用户是否推荐该酒店?",
        },
    },
)

print(res["answers"]["sentiment"]["choice"])      # -> positive
print(res["answers"]["sentiment"]["confidence"])  # -> 1.0
print(res["answers"]["rating"]["score"])          # -> 3.10   (0–4 的期望值)
print(res["answers"]["recommend"]["noul"])        # -> 0.986  P(推荐)
print(res["usage"]["output_tokens"])              # -> 0      不生成任何文本

上面这些输出是本模型在实际运行中产生的(非示意)。

一次调用里的所有问题在同一次前向传播中批量作答。

三种 primitive:

  • choice — 给定 criteria 键值对,返回 argmax 选项 + 全部选项概率
    • confidence(1 − 归一化熵)。
  • score — 有序选项列表,返回期望分值 Σ i·pᵢ 与完整分布。 序数感知:训练时额外用 ranked probability score 惩罚累积分布偏差。
  • noul — 二元命题,返回 P(成立)。

架构

  • 编码器:hfl/chinese-roberta-wwm-ext-large,24 层 / hidden 1024 / 16 头 / 21128 词表, 中文全词掩码(whole word masking)预训练,325M 参数。使用 SDPA 注意力。
  • 决策头(从头训练,26.5M 参数):
    • type_emb(3) — 题型嵌入(choice / score / noul)
    • 2 层 nn.TransformerEncoderLayer(norm_first=True, src_key_padding_mask)
    • scorer:LayerNorm → Linear(d,d) → GELU → Linear(d,1),作用于每个 marker 位置
    • act_head:Linear(d+4, 256) → GELU → Linear(256, n_act),输入为 [CLS] 池化表示 加上答案分布的已 detach 摘要(top1、top1−top2、归一化熵、选项数/255)
  • 单次前向:一次 system_one 调用里所有问题在同一次前向传播中批量作答。

训练

数据(全部为人工标注中文语料,非模型自标注)

来源 许可 产出 记录数
TNews (CLUE) Apache-2.0 choice(15 类新闻主题) 18,756
ChnSentiCorp research / 仅研究用途 choice + score + noul 10,160
yf_dianping research / 仅研究用途 choice + score + noul 18,836
合计(train / eval) 99,184 / 6,244 个问题 47,752 / 3,048

⚠️ 上游数据许可提示(请务必阅读)

三个语料中,TNews 为 Apache-2.0,可自由使用;而 ChnSentiCorp 与 yf_dianping 在其 原始发布页标注为研究用途(research-only),并未提供明确的商业使用授权。

本模型仓库采用"个人免费 / 商用付费"协议,但该协议无法覆盖上游数据集的限制。 因此:

  • 若您计划商业使用本模型,请自行确认 ChnSentiCorp / yf_dianping 的原始许可, 或联系我们获取仅用 Apache-2.0 语料(TNews 等)重训的版本;
  • 本项目已尽量披露数据来源以便下游合规评估,但不对下游使用场景的合规性作担保 (见 LICENSE 第五节)。

一条文本扇出成多个 typed question(一条大众点评评论同时给出情感 choice、1–5 星序数 score、以及“是否推荐”的 noul),与 Laya 自己构造 typed-decisions benchmark 的方式一致。 大众点评原始文件按餐厅分组,因此用蓄水池采样从 2,979,616 条可用评论中均匀抽样, 而不是截取文件头部(那会严重偏向少数餐厅的客群)。

训练配置

决策头随机初始化,与编码器顶部 8 层联合微调:

项 值
可训练参数 128.3M / 352M(编码器顶部 8/24 层 101.8M + 决策头 26.5M)
决策头 lr 3e-4
编码器 lr 2e-5
LR 调度 线性 warmup 6% + cosine 衰减
batch 4096 padded tokens,≤64 序列
epochs 3(14,493 步)
梯度裁剪 1.0
选项顺序增强 开启(choice/noul 的选项顺序每轮打乱,target 同步重排;score 不打乱,因其为序数)
梯度检查点 开启(12 GB 显存约束)
混合精度 fp32(本机 sm_52 无原生 bf16)

训练中每 100 步在一个固定的、跨来源均匀抽样的 400 条探针集上记录真实准确率, 并把探针最优的权重单独存为 *-best 快照——因为这比 running-mean loss 可靠得多: batch 混合了 2 选项与 15 选项的题目,loss 水平主要由构成比例决定。

训练过程中测得的关键结论

学习率与 warmup 是决定性的(同一份 6000 条数据、同一探针集,唯一变量是 lr/warmup):

配置 探针准确率轨迹
编码器 lr 1e-4,无 warmup 0.5113 → 0.3251 → 0.2551(坍缩)
编码器 lr 2e-5 + warmup 6% + cosine 0.4199 → 0.5621 → 0.7032 → 0.7404(持续上升)

RLCD 项当前贡献很小。 日志中策略梯度项 pg ≈ 0.002–0.024,稠密 masked-KL 项 kl ≈ 0.5–2.0。即使 rl_weight 已从 1.0 提到 8.0,RLCD 仍只占损失的一小部分, 因此本版主要由有监督 KL 驱动;RLCD 的严格恰当评分规则主要作用于概率校准 (见下方温度标定结果),对 argmax 准确率贡献有限。

本机硬件与数值选择

训练在本机 GTX Titan X(Maxwell, compute capability 5.2, 12 GB, CUDA 12.6) 上完成。 该架构有三个硬性后果,已在代码中显式处理(均为实测,非推测):

  • 不支持 bfloat16 → 上游配置里的 "amp_dtype": "bf16" 在本机不可用; layazh.config.resolve_amp_dtype 会把 dtype 收敛到 fp16/fp32, 绝不在 sm_52 上使用 bf16。
  • 无 FlashAttention → 编码器使用 SDPA(math kernel)。
  • PyTorch 支持止于 2.7.1 + cu128 —— 该 wheel 内含可从 sm_52 执行的 sm_50 SASS (torch.cuda.get_arch_list() = ['sm_50','sm_60',...,'sm_120'])。

实测数值特性(反直觉,故记录):fp16 matmul 3.80 TFLOP/s 略快于 fp32 2.94 TFLOP/s, 因此推理用 fp16 权重是净收益;训练仍用 fp32 以保证稳定性。

评测

同台对比(1,200 道完全相同的中文题目、修正后的 ground truth)

两个模型回答 byte-identical 的中文问题,走同一套代码路径与指标实现 (scripts/benchmark_ab.py):

指标 layazh-zh Laya multilingual 变化
总体准确率 0.7150 0.4783 +0.237
choice 准确率 0.6127 0.6039 +0.009
score 准确率 0.6854 0.1461 +0.539
noul 准确率 0.9638 0.6486 +0.315
Brier(越低越好) 0.3930 0.8040 −0.411
ECE(越低越好) 0.2639 0.2626 +0.001
AURC(越低越好) 0.1127 0.4239 −0.311
单题延迟 67.0 ms 35.5 ms 慢 1.9×

关于 choice 的一项必要说明。 上游在 choice 上看似打平(0.6039 vs 0.6127), 但这是巧合:上游对 2 选项题的点预测不随输入变化,其"准确率"等于多数类占比; 修正标签极性后恰好落在多数类一侧。证据是它的 choice Brier 高达 0.6191 (本模型 0.5319),且 choice ECE 0.1752 是在"几乎恒定输出"下取得的。 逐来源看更能说明问题:在 15 类的 TNews 上上游接近随机水平。

其余三个 primitive 上本模型全面领先,其中 score 提升 +0.539。

全量评测(6,244 个问题,本模型)

分组 n 准确率 ECE
ALL 6,244 0.6951 0.3440
type:noul 1,392 0.9375 0.2131
type:score 1,804 0.6996 0.1645
type:choice 3,048 0.5817 0.5100
senticorp/choice 640 0.8688 0.8097
senticorp/score 640 0.9109 0.2334
senticorp/noul 640 0.9125 0.2588
dianping/noul 752 0.9588 0.1743
dianping/choice 1,164 0.7096 0.6000
dianping/score 1,164 0.5833 0.1266
tnews/choice(15 类) 1,244 0.3143 0.2715

必须看地板线,否则准确率数字没有意义:

总体准确率
随机猜 0.2959
多数类(先验) 0.4734
Laya multilingual(上游实测) 0.4783
layazh-zh 0.6951

温度标定

温度按 (题型, 选项数) 分桶拟合,在同一半数据上拟合、在不相交的另一半上报告。 三种方案在留出半集(3,122 题)上的 ECE 对比:

方案 留出集 ECE
不标定(恒等) 0.3512
每题型一个温度 0.2684
每 (题型, 选项数) 分桶(最终采用) 0.2547

最终写入本仓库 rl_agent_config.json 的温度:

{"choice:11+": 0.466635, "choice:2": 0.253066, "choice:3-5": 0.545042,
 "noul:2": 0.970277, "score:3-5": 1.034508}

与上游 SDK 的一处已知差异。 上游 laya 包在加载 checkpoint 时会用 clamp_temperature 把温度限制在 **[0.5, 5.0]**,而本模型的 choice:2 桶 拟合值为 0.253(低于下界)。我实测了加这个限幅的影响:

温度限幅 ECE(全部 6,244 题)
不限幅(本模型默认) 0.2498
[0.5, 5.0](上游 SDK 行为) 0.2799

限幅会让 ECE 变差,说明该桶确实需要更强的锐化(模型对 2 选项题过于保守)。 因此本模型不做限幅。若你通过上游 laya SDK 加载本 checkpoint, 温度会被自动限幅到 0.5,ECE 将为 0.2799 —— 这是可预期的差异,不是 bug。

分桶温度是由留出数据挑选出来的,不是默认采用上游做法。 这一点很重要:同一套分桶在各方案比较中只赢了 0.0137 ECE, 如果不做留出对比就无从判断它是否真的更好。

标定必须拟合在未经温度缩放的原始 logits 上,否则拟合会永远收敛到 ~1.0。

已知弱点

  • choice 是明确弱项(0.5817),15 类 TNews 仅 0.3143。 15 个选项在 512 token 预算下每个只剩几个 token,正是上游模型卡承认的"选项数越多每项 token 越少导致准确率骤降"。 改进方向:调大 head_max_len,或改层级式 coarse-to-fine 选择。
  • choice 校准最差(ECE 0.5100),因为 choice 混合了 2/3/15 三种选项数。
  • 上下文上限 512 token(中文约 500 字),超长文档需先切分。
  • 延迟慢于上游 1.9×(67.0 ms vs 35.5 ms):中文 RoBERTa-large(24 层)比 上游 mmBERT-base 更大,这是精度换速度的取舍。
  • RLCD 贡献有限:本版准确率主要由有监督 KL 驱动,RLCD 的严格恰当评分规则 主要体现于校准。
  • 训练未跑满计划轮数(见 REPORT.md),是一个接近平台期、而非训练到极限的版本。

诚实的边界

以下限制继承自上游 Laya 的 Honest Limits,中文版同样适用,另外加上中文特有的几条:

  • 零样本能力弱。 上游基础 checkpoint 在 typed-decisions benchmark 上仅 0.362 (随机 0.318、多数类 0.461)。Laya 是快速专精的底座,不是开箱即用的决策引擎; 价值在于在中文数据上定向微调后的表现。
  • score 序数题是最弱的 primitive(上游 SST-5 仅 0.372)。
  • 选项数 > 20 时准确率骤降:选项共享固定的 head_max_len 预算,77 个选项时每项只剩 3–4 个 token。解法是调大 head_max_len 或改用层级式 coarse-to-fine 选择。
  • noul 可能跟着选项标签走而不是跟着 state 走(上游 issue #156)。 若怀疑卡住,改用中性 key 的双选项 choice。
  • action.act_probability 暂无可用信号(上游 issue #185)。请用 confidence 门控。
  • 开箱过度自信:务必在自己的数据上重新拟合温度。
  • 中文上下文只有 512 token。 中文 BERT 词表基本 1 字 1 token,因此有效正文约 500 字。 超长中文文档需要先切分或用支持更长上下文的编码器。
  • 数据域偏移:训练语料是新闻 / 酒店评论 / 餐厅点评,不等于你业务里的邮件或工单。 在你的领域上继续微调仍然必要。

引用

Downloads last month

-

Downloads are not tracked for this model. How to track
Safetensors
Model size
0.4B params
Tensor type
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for NovaAI6868/layazh-zh

Finetuned
(15)
this model