在推荐、搜索与自然语言处理任务中,Embedding层往往占据模型参数的绝大部分。当标注数据有限或用户行为序列较短时,Embedding矩阵容易对每个稀疏id形成过强记忆,导致线上效果远低于离线训练指标。这种过拟合并非传统全连接层的权重大幅震荡,而是低维向量空间中的id聚类崩塌,少数高频item占据中心、长尾item被推到边缘。

正则化系数的选取与梯度行为分析
最常用的抑制手段是在优化器中加入L2权重衰减,或对Embedding参数单独设置weight_decay。系数过小(如1e-6)基本无效,过大(如1e-2)则会让反向传播中对应梯度被强烈拉向原点,向量模长集体缩小,点积相似度区分度消失。实践中,先以1e-4为基线,观察验证集召回率曲线:若训练损失继续降而验证指标第三轮后拐头,提升到3e-4;若首轮就欠拟合,则降到5e-5。
除了全局L2,也可在Embedding后接 dropout 层,以行级屏蔽模拟用户缺失行为。注意 dropout 比率应随训练轮次退火,前期0.3增强鲁棒性,后期降至0.1保留记忆。下面代码展示带系数配置的 PyTorch 片段:
import torch
import torch.nn as nn
class Model(nn.Module):
def __init__(self, num_items, dim):
super().__init__()
# Embedding权重用自定义衰减,不计入默认weight_decay
self.emb = nn.Embedding(num_items, dim)
self.drop = nn.Dropout(0.3)
def forward(self, idx):
x = self.emb(idx)
return self.drop(x)
# 优化时单独对emb施加1e-4 L2
optimizer = torch.optim.Adam(
[{'params': model.emb.parameters(), 'weight_decay': 1e-4},
{'params': [p for n, p in model.named_parameters() if 'emb' not in n]}],
lr=1e-3
)
另一个隐蔽问题是稀疏更新下的动量累积。若使用带动量优化器且部分id多日不出现,其缓冲量会过时,再出现时一步更新过冲。可改用稀疏版 SGD 或限制动量0.9以下,配合前述系数,过拟合斜率明显平缓。
数据集多样性增强的实操路径
单纯调系数只是压缩容量,真正缓解过拟合要靠输入分布变宽。对于行为序列,可采用字段掩码:随机将用户画像中的年龄、城市置为未知桶,迫使模型不依赖单一强特征。对于内容侧,同义替换与回译能生成近似正样本,让同一个item的上下文向量更分散。下列表格对比三类增强的离线增益:
| 增强方式 | 实现成本 | 验证集AUC提升 | 长尾覆盖 |
|---|---|---|---|
| 字段掩码 | 低 | +0.4% | 中 |
| 同义替换 | 中 | +0.7% | 高 |
| 跨域混合 | 高 | +1.1% | 高 |
跨域混合指将相似场景的点击序列拼接,例如将站内搜索词与电商浏览序列按用户映射合并,前提是id空间做哈希对齐。这种方案能引入完全不同的转移模式,显著降低Embedding对单一业务的过配。代码上可用合并字典方式重排index:
def merge_vocab(dict_a, dict_b):
# 将B域id偏移避免冲突
offset = len(dict_a)
merged = dict(dict_a)
for k, v in dict_b.items():
merged[k] = v + offset
return merged, offset
vocab, off = merge_vocab(search_vocab, shop_vocab)
print('合并后词表大小', len(vocab))
增强不是越多越好,过度回译会引入语义漂移,使item向量中心偏移。建议每轮仅对20%样本做重采样,并保持原始样本主导,验证集必须无任何增强以保证评估干净。
联合策略与训练闭环设计
将正则化与多样性结合时,要注意两者耦合效应。若已做跨域混合,L2系数应回调一档,因为数据本身已提供泛化,过强惩罚会压制新域信号。我们通常采用早停配合滑动验证:每五百步在干净验证集测一次,连续三次无提升即停,并回滚到最优checkpoint。
工程上推荐把Embedding导出做单独监控,观察各频度分位的模长均值。若高频item模长远高于长尾,说明仍偏记忆,可增大 dropout 或降低学习率。如下片段记录模长分布:
with torch.no_grad():
norms = model.emb.weight.norm(dim=1)
freq = torch.bincount(train_ids)
for bucket in [0, 1, 5, 20]:
mask = (freq > bucket)
print('freq>', bucket, '平均模长', norms[mask].mean().item())
最终上线前,用线上真实未增强日志做影子打分,对比离线增强模型与基线模型的CTR差异。只有当影子指标稳定正向,才说明正则化系数与多样性增强真正解决了过拟合,而非在验证集上偶然拟合。