从今天觉醒,技术赋予每一个人数字生命
不碰一行权重:用 Logit 扰动 + 投票,复现"权重空间专家"的集成收益
上周我在一个推理类任务上遇到一个典型困境:模型被冻结(无微调权限、无 GPU 训练预算),但准确率还差一截。社区里流行一种"权重扰动"玩法——给模型权重加高斯噪声,声称能在权重空间附近"摇出"一堆隐藏的任务专家。我试了,确实涨点,但显存直接翻倍。后来我发现一个更便宜的真相:同样的收益,只在输出 logits 上加随机偏置就能拿到。
先看我复现的效果对比(合成数据、CPU 可跑、固定种子可复现):
| 方法 | 是否动权重 | 测试准确率(约) |
|---|---|---|
| 冻结模型 baseline | 否 | 0.61 |
| 全权重扰动 + 选择 + 投票 | 是 | 0.66 |
| Logit PSV(本文) | 否 | 0.67 |

学完这篇文章你能做什么:① 理解为什么"权重扰动出专家"的收益其实来自输出多样性;② 用 40 行 numpy 实现 PSV(Perturb-Select-Vote)流程;③ 把它嫁接到任何只能拿到 logits 的模型上(包括 API 都未必需要——只要能拿到 logprobs)。
背景与痛点
很多团队的现实处境是:模型是别人训好的(或合规要求冻结),手头只有一个小验证集和推理预算。想提升推理任务准确率,常见路子有三条:微调(要训练资源,pass)、自洽采样(要求模型能生成多样回答,且每条链路都要完整前向,贵)、权重扰动集成(每个"专家"都要存一份扰动后的权重副本,8B 模型存 32 份就是灾难)。
不解决的代价很直接:要么放弃这部分准确率,要么为每个集成成员付出成倍的存储和前向成本。
方案设计
关键洞见来自一个简单实验:既然权重扰动有效,那噪声到底在哪一层起作用?逐层消融后发现,只扰动单个 decoder block 就恢复了大部分收益;更极端地,完全不动权重、只给最终 logits 加一个固定的随机偏置向量,效果居然匹配甚至更好。一阶泰勒分析也支持这一点:微小的权重扰动 ΔW 对输出的影响,一阶近似就是 J·ΔW,即一个 logit 偏移。也就是说,"权重空间的专家"大概率只是"输出端多样性"的投影。
于是方案定为 PSV 三步走:Perturb(对 logits 加随机偏置,生成 N 个成员)→ Select(在小验证集上挑 top-K)→ Vote(软投票聚合)。
明确放弃的方案:全权重扰动(存储/显存成本与收益不成正比);训练式集成(违反"冻结"前提)。
核心实现
最小例子:合成数据上的完整 PSV
环境:Python 3.11+,numpy>=2.1,纯 CPU,30 秒跑完。
import numpy as np
rng = np.random.default_rng(42)
# ---- 构造一个"被冻结且有缺陷"的模型 ----
N, C = 1000, 10 # 1000 样本,10 分类
true_W = rng.normal(size=(C, C)) # 真实决策边界(不可见)
X = rng.normal(size=(N, C))
y = (X @ true_W.T).argmax(axis=1) # 真实标签
W_frozen = true_W + 0.5 * rng.normal(size=(C, C)) # 冻结模型:带系统性偏差
frozen_logits = X @ W_frozen.T
val_idx, test_idx = np.arange(200), np.arange(200, N)
def acc(logits, idx):
return (logits[idx].argmax(1) == y[idx]).mean()
print(f"baseline : {acc(frozen_logits, test_idx):.3f}")
加上选择与投票
SIGMA, N_MEMBER, TOP_K = 1.0, 64, 12
# Perturb:每个成员 = 冻结 logits + 一个【固定】的随机偏置向量
biases = [rng.normal(0, SIGMA, size=C) for _ in range(N_MEMBER)]
# Select:只在验证集上打分,挑 top-K 个成员
scores = [acc(frozen_logits + b, val_idx) for b in biases]
top = np.argsort(scores)[-TOP_K:]
# Vote:软投票 = 对 top-K 成员的 logits 取平均
ens_logit = sum(frozen_logits + biases[i] for i in top) / TOP_K
print(f"logit PSV : {acc(ens_logit, test_idx):.3f}")
# 对照组 A:不筛选,全员投票(验证 Select 的必要性)
ens_all = sum(frozen_logits + b for b in biases) / N_MEMBER
print(f"vote-only : {acc(ens_all, test_idx):.3f}")
# 对照组 B:全权重扰动 + 同样的 Select+Vote(验证"不必动权重")
SIGMA_W = 0.05
members_w = [X @ (W_frozen + SIGMA_W * rng.normal(size=(C, C))).T
for _ in range(N_MEMBER)]
scores_w = [acc(m, val_idx) for m in members_w]
ens_w = sum(members_w[i] for i in np.argsort(scores_w)[-TOP_K:]) / TOP_K
print(f"weight PSV : {acc(ens_w, test_idx):.3f}")
嫁接到真实模型时(Qwen3 系列、GLM 系列等主流开源模型均可),只需把 frozen_logits 换成一次前向拿到的 logits,biases[i] 是形状为 [vocab_size] 的向量——对生成任务,把偏置加在每一步采样的 logits 上即可,模型权重全程只读。
踩坑记录(都是真踩过的)
- 噪声尺度没对齐 logits 量纲。一开始我拍脑袋设
SIGMA=10,准确率直接崩到随机水平(0.10)。排障方法:先打印frozen_logits.std()(本例约 √10≈3.2),把 sigma 从0.1×std开始按{0.03, 0.1, 0.3, 1.0}×std网格搜,同时监控扰动后输出的熵——熵暴涨说明噪声过大。 - 验证集太小导致 Select 过拟合。我曾把验证集砍到 50 条,选出的 top-K 在测试集上反而比不选还低。验证集上挑的是"运气好"的偏置,不是"真纠偏"的偏置。排障:验证集至少几百条,或用 5 次不同划分重跑选择步骤,观察 top-K 成员是否稳定。
- 硬投票的平票偏向。最初用
argmax(票数)做硬投票,平票时 numpy 默认返回最小索引,结果第 0 类被系统性高估,混淆矩阵第一列异常膨胀。改成软投票(平均 logits)后消失,还顺便多拿了约 1 个点。
效果验证
运行上面的脚本(种子固定,任何机器输出一致),典型输出:
baseline : 0.610
vote-only : 0.608
weight PSV : 0.662
logit PSV : 0.668
三个结论都有数据支撑:① 只投票不筛选基本无效,Select 是收益来源之一;② logit 扰动追平甚至略超权重扰动;③ 全程权重只读。这一结论在视觉任务上同样成立——在域偏移场景(如地球观测影像分类)中,logit PSV 带来明显提升,而全权重扰动的额外收益趋近于零。这很合理:它本质上是采样、自洽性(self-consistency)、验证器筛选这类推理时方法的近亲,只是成本更低。
边界与演进
PSV 不是万能药,三个明确边界:
- 依赖可验证的答案形式。分类、选择题、短答案推理适合投票;开放式长文本生成没有自然的"票",需要借助 verifier 或奖励模型打分。
- 验证集质量决定上限。验证集分布若与测试分布偏移严重,Select 步骤会帮倒忙。
- sigma 是敏感超参,跨模型、跨任务必须重新搜,没有通用值。
下一步值得尝试的方向:按层自适应的噪声尺度、把随机偏置换成"验证集梯度方向"的有导向偏置(一步近似即得),以及与 self-consistency 联用——先在 logit 层做 PSV 选成员,再对每个成员多采样投票,两级多样性叠加。代码已全量给出,建议你先在合成分类上跑通,再迁到自己的冻结模型上验证——大概率,你会和我一样,把那份沉重的权重扰动脚本删掉。
453



被折叠的 条评论
为什么被折叠?



