想象你有一本“干饭日记”:里面按时间顺序记着过去两个月的每一顿饭——加班深夜的麻辣火锅、周末早晨的牛油果沙拉、和朋友聚餐的东北烧烤。当你想预测“下一顿吃什么”时,并不会只盯着最后一顿饭,而是在心里把所有日记条目都过一遍:深夜吃火锅往往因为白天太累,沙拉之后总跟着一杯奶茶……每一个历史行为,都因为“和谁挨着、在什么处境下”而有了新的含义。 推荐系统面临的正是同样的问题:用户点击、购买、观看的序列,就像一本行为日记。自注意力机制(Self-Attention)就是帮系统“复盘日记”的思维过程——让序列中的每个物品与所有其他物品对话,动态计算彼此的关联度;多头注意力(Multi-Head Attention)则是请来多位“美食顾问”,分别从时间规律、口味搭配、消费场景等不同角度审读这本日记。本文就从这本“干饭日记”出发,带你拆解自注意力与多头注意力的原理、位置编码、掩码机制,以及它们在 SASRec 等主流序列推荐模型和多任务推荐系统中的落地之道。

岗位关联

学习自注意力与多头注意力,是解锁 SASRec、BERT4Rec 等主流序列推荐模型的钥匙,也是推荐算法工程师笔试面试绕不开的考点。

目录
  1. 可视化演示
  2. 序列推荐背景
  3. 自注意力原理
  4. 多头注意力机制
  5. 位置编码设计
  6. 掩码因果约束
  7. 推荐模型实战
  8. 多任务多目标
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示自注意力机制中Query-Key权重矩阵的softmax归一化过程,以及多头注意力并行捕获不同维度特征的模式,揭示推荐系统中序列建模的核心原理。

本节配有交互动效(进入视口后自动加载,约 3KB)。

自注意力权重矩阵 Query-Key点积经softmax归一化为注意力权重 对角线高权重表示自相关性强 Attn(Q,K)=softmax(QK^T/sqrt(d)) 温度越低分布越尖锐

序列推荐背景

小明开始记干饭日记时,只写“今天吃了火锅”。后来他发现,单看这一顿根本解释不了为什么凌晨三点会点麻辣烫。真正有用的信息藏在顺序里:加班 → 想吃重口 → 深夜火锅;沙拉 → 心里委屈 → 奶茶安慰。每一顿饭的含义,都要放在它前面的故事里读。

推荐系统也是一样。传统推荐模型(协同过滤、矩阵分解)把用户抽象成一袋静态偏好——“喜欢辣、偶尔吃草、甜口七分”,却看不到“上周连喝三天奶茶,今天该换清爽的”这种趋势。于是研究者把用户交互记录当作“行为日记”来建模,这就是序列推荐(Sequential Recommendation)。

在数学上,序列推荐把用户最近的交互序列记为 $s_1, s_2, \ldots, s_t$,每个物品先经过嵌入(Embedding)变成一个稠密向量,再按时间顺序堆叠成矩阵,作为后续自注意力层的输入:

有了这本“数字日记”,模型就有了复盘的基础。但更关键的问题是:怎么让每一顿饭在“所有其他饭”的背景下被重新理解?这正是自注意力要解决的事。

E=[e1e2⋮et]∈Rt×d\displaystyle E = \begin{bmatrix} \mathbf{e}_1 \\ \mathbf{e}_2 \\ \vdots \\ \mathbf{e}_t \end{bmatrix} \in \mathbb{R}^{t \times d}
用户最近的 t 次交互被编码成一个 t 行 d 列的矩阵:行是“第几次交互”,列是“隐藏特征”。
  1. 物品嵌入把离散的物品 ID 映射成稠密的 d 维向量,相当于给每道菜写一份“口味档案”。
    ei∈Rd\mathbf{e}_i \in \mathbb{R}^{d}
  2. 按序堆叠t 个向量按时间先后从上到下排成矩阵,行号就是日记的页码。
    E=[e1;e2;…;et]∈Rt×dE = [\mathbf{e}_1; \mathbf{e}_2; \ldots; \mathbf{e}_t] \in \mathbb{R}^{t \times d}
  3. 形状含义t 是序列长度(回看多少顿饭),d 是嵌入维度(每顿饭描述得多细致)。

自注意力原理

小明开始真正地复盘日记。想预测下一顿,他不会傻盯最后一顿,而是先问自己:现在的处境是什么?加班到崩溃、周末放松、还是刚运动完?这个“当前状态发出的问题”,就是自注意力里的查询(Query)。

序列中的每个物品,也都随身带着“键”(Key)和“值”(Value):键是日记的关键词——菜系、时间、场景;值才是日记正文。模型把问题 Q 与每篇日记的键 K 做匹配打分,分数高说明这篇日记对预测下一顿很有参考价值,然后按分数高低决定“重点读哪几篇”。

打分用的是点积 $\mathbf{Q}\mathbf{K}^\top$,再除以 $\sqrt{d_k}$ 防止数值过大导致 softmax 梯度消失,之后用 softmax 归一化成权重,最后对值 V 做加权求和。每一步都可微,可以端到端训练:

自注意力让每个行为都不再是孤立记录,而是在全部历史中被重新解读。但这只是“一位顾问”的复盘——如果口味顾问和场景顾问看法冲突怎么办?这就引出了多头注意力。

Attention(Q,K,V)=softmax(QK⊤dk)V\displaystyle \text{Attention}(\mathbf{Q}, \mathbf{K}, \mathbf{V}) = \text{softmax}\left(\frac{\mathbf{Q}\mathbf{K}^\top}{\sqrt{d_k}}\right)\mathbf{V}
一句话:问对所有问题,翻遍所有日记,按相关度加权记住重点。
  1. 点积打分Q 与每个 K 做内积,衡量“当前问题”和“这篇日记关键词”的匹配度。
    scoreij=qi⋅kj\text{score}_{ij} = \mathbf{q}_i \cdot \mathbf{k}_j
  2. 缩放稳定除以 $\sqrt{d_k}$,把分数拉回合理区间,避免 softmax 进入饱和区梯度消失。
    QK⊤dk\frac{\mathbf{Q}\mathbf{K}^\top}{\sqrt{d_k}}
  3. softmax 归一化把分数变成非负且和为 1 的注意力权重,理解为“该参考多少成”。
    softmax(⋅)\text{softmax}(\cdot)
  4. 加权聚合用权重对 V 加权求和,得到携带全局上下文的输出向量。
    ∑jαijvj\sum_j \alpha_{ij} \mathbf{v}_j

多头注意力机制

一位美食顾问容易有盲区,小明于是请了三位:时间顾问盯着“加班 → 深夜 → 重口”的节奏,口味顾问盯着“辣 → 甜 → 辣”的搭配,场景顾问盯着“工作日便当、周末大餐”的切换。三位各看各的,互不干扰,最后汇总成一份综合判断。

多头注意力就是这个思路:把 Q、K、V 分别通过不同的线性投影矩阵($\mathbf{W}_i^Q, \mathbf{W}_i^K, \mathbf{W}_i^V$)映射到不同的子空间,在每个子空间里独立做一次自注意力,得到第 i 个头 $\mathbf{head}_i$。不同头捕捉不同类型的关系模式。

所有头的结果拼接起来,再乘一个输出投影矩阵 $\mathbf{W}^O$ 完成融合。注意:多头并不增加网络层数,它是在同一层内并行展开多个“视角”:

多头就像让三位顾问分别写下观察,最后由小明汇总判断。工程上它还带来一个意外好处:所有头可以并行计算,速度远快于串行展开的 RNN。

MultiHead(Q,K,V)=Concat(head1,…,headh)WO,headi=Attention(QWiQ,KWiK,VWiV)\displaystyle \text{MultiHead}(\mathbf{Q}, \mathbf{K}, \mathbf{V}) = \text{Concat}(\mathbf{head}_1, \ldots, \mathbf{head}_h)\mathbf{W}^O, \quad \mathbf{head}_i = \text{Attention}(\mathbf{Q}\mathbf{W}_i^Q, \mathbf{K}\mathbf{W}_i^K, \mathbf{V}\mathbf{W}_i^V)
多位顾问各自带着不同关注点读日记,最后把意见拼接压缩成一份总报告。
  1. 线性投影每个头用独立的矩阵把输入压到低维子空间,相当于给顾问分配专属“滤镜”。
    QWiQ,KWiK,VWiV\mathbf{Q}\mathbf{W}_i^Q, \quad \mathbf{K}\mathbf{W}_i^K, \quad \mathbf{V}\mathbf{W}_i^V
  2. 并行注意力每个头在子空间内独立做一次自注意力,头与头之间互不干扰。
    headi=Attention(⋅)\mathbf{head}_i = \text{Attention}(\cdot)
  3. 结果拼接把 h 个头的输出按列拼接,保留所有视角的信息。
    Concat(head1,…,headh)\text{Concat}(\mathbf{head}_1, \ldots, \mathbf{head}_h)
  4. 融合投影乘 $\mathbf{W}^O$ 把多视角信息压缩回原始维度 d。
    Concat(⋅) WO∈Rd\text{Concat}(\cdot)\, \mathbf{W}^O \in \mathbb{R}^{d}

位置编码设计

日记如果丢了日期,顺序就全乱了。自注意力有一个数学上的“盲点”——它把序列当集合处理,打乱顺序后算出的注意力一模一样,这叫做置换不变性(Permutation Invariance)。“先火锅后沙拉”和“先沙拉后火锅”,在它眼里没有区别。

为了解决顺序问题,Transformer 系模型会给每个位置加上“位置编码”(Positional Encoding):要么用正弦余弦函数生成固定编码,要么把位置向量作为可学习参数随模型一起训练。编码加到物品向量上,模型就知道“这是第几个行为”。

正弦位置编码的巧妙之处在于:不同维度拥有不同频率,整体类似二进制编码的“低位/高位”,且可以外推到训练时没见过的更长序列:

有了日期戳,自注意力才分得清“先火锅后沙拉”和“先沙拉后火锅”是两段完全不同的故事。可学习位置编码在数据充足时效果更好,相对位置编码则在长序列上更稳健,工业界按场景取舍。

PE(pos,2i)=sin⁡(pos100002i/d),PE(pos,2i+1)=cos⁡(pos100002i/d)\displaystyle \text{PE}(pos, 2i) = \sin\left(\frac{pos}{10000^{2i/d}}\right), \quad \text{PE}(pos, 2i+1) = \cos\left(\frac{pos}{10000^{2i/d}}\right)
位置编码就是日记上的日期戳:偶数维度用正弦、奇数维度用余弦,频率逐维衰减。
  1. 位置序号pos 表示物品在序列中的位置,从 0 开始编号。
    pos∈{0,1,…,t−1}pos \in \{0, 1, \ldots, t-1\}
  2. 维度分工第 $2i$ 维用正弦、第 $2i+1$ 维用余弦,奇偶配对。
    sin(⋅), cos(⋅)\text{sin}(\cdot), \ \text{cos}(\cdot)
  3. 频率衰减分母 $10000^{2i/d}$ 随维度增大而增大,频率越来越低,类似尺子上的刻度由密到疏。
    pos100002i/d\frac{pos}{10000^{2i/d}}
  4. 逐元素相加位置编码与物品嵌入相加后送入注意力层,顺序信息融入每个向量。
    ei+PE(i,:)\mathbf{e}_i + \text{PE}(i, :)

掩码因果约束

预测未来有个前提——不能偷看未来。小明预测周五晚饭时,绝不会参考周五之后才发生的日记。但自注意力默认能看到序列的全部位置,如果训练时让它“偷看”,预测时就露馅了,这就是信息泄露(Label Leakage)。

因果掩码(Causal Mask)的做法很直接:在注意力得分矩阵上,把所有“未来位置”对应的分数设为负无穷大。经过 softmax 后,这些位置的权重会变成 0,第 i 个位置就只能看到前 i 个位置的信息。

掩码矩阵 M 是一个上三角为负无穷的矩阵。加上掩码后,模型变成自回归式:每一步输出只依赖过去。这也是 GPT 和 SASRec 的标准做法;而 BERT4Rec 这类模型预测被 mask 的物品时故意用双向注意力,两类模型的本质区别就在这里:

掩码保证了推荐系统不“作弊”:训练时怎么预测,线上就怎么预测。它和位置编码一起,把“无序的集合”重新变成“有序且守规矩的日记本”。

Attention(Q,K,V)=softmax(QK⊤+Mdk)V,Mij={0j≤i−∞j>i\displaystyle \text{Attention}(\mathbf{Q}, \mathbf{K}, \mathbf{V}) = \text{softmax}\left(\frac{\mathbf{Q}\mathbf{K}^\top + \mathbf{M}}{\sqrt{d_k}}\right)\mathbf{V}, \quad M_{ij} = \begin{cases} 0 & j \leq i \\ -\infty & j > i \end{cases}
掩码是“时间单向门”:复盘只能朝过去看,未来的日记一律合上。
  1. 构造掩码矩阵第 i 行第 j 列,当 $j > i$(未来)时为负无穷,否则为 0。
    Mij=−∞ (j>i),0 (j≤i)M_{ij} = -\infty \ (j > i), \quad 0 \ (j \leq i)
  2. 加到得分上注意力分数加上掩码,未来位置的分数被压到极小。
    QK⊤+M\mathbf{Q}\mathbf{K}^\top + \mathbf{M}
  3. softmax 生效负无穷处指数趋近 0,对应权重变为 0。
    softmax(⋅)\text{softmax}(\cdot)
  4. 自回归解码t 时刻的输出只能看到 $1$ 到 $t-1$ 的历史,与在线预测一致。

推荐模型实战

把前面所有零件组装起来,就是一台能读日记的机器。经典代表是 SASRec(Self-Attentive Sequential Recommendation):先把用户行为序列的嵌入加上位置编码,送入若干层“带掩码的多头自注意力”,再用最后一个位置的输出预测下一项交互。

SASRec 的每一层都包含多头自注意力、残差连接(Residual Connection)和层归一化(Layer Normalization),结构类似 Transformer 的编码器块。层数越多,模型越能提炼“偏好语义”——从单顿饭的偏好,到“深夜+重口+独食”这种高层次模式。

训练时用交叉熵损失:序列中每个位置都预测“下一个是谁”,共享同一套参数。预测时取最后时间步的输出 $\mathbf{F}_t$,与全部物品嵌入做点积,再 softmax 得到各物品的概率:

SASRec 本质上就是“反复复盘日记 + 严格不看未来 + 预测下一页”。它用自注意力替代 RNN,既支持并行又擅长捕捉长期依赖,是目前序列推荐的事实标准之一。

L=−1∣S∣∑Slog⁡p(st+1∣s1,s2,…,st),p(s)=softmax(FtWb⊤+b)\displaystyle \mathcal{L} = -\frac{1}{|\mathcal{S}|}\sum_{\mathcal{S}} \log p(s_{t+1} \mid s_1, s_2, \ldots, s_t), \quad p(s) = \text{softmax}(\mathbf{F}_t \mathbf{W}_b^\top + \mathbf{b})
整个模型的目标很纯粹:读完前 t 顿饭后,最大化“下一顿真的吃了 s_{t+1}”的概率。
  1. 序列表示$\mathbf{F}_t$ 是最后一个行为经过多层多头注意力后得到的向量,浓缩了整本日记。
    Ft∈Rd\mathbf{F}_t \in \mathbb{R}^{d}
  2. 全物品打分$\mathbf{F}_t \mathbf{W}_b^\top + \mathbf{b}$ 对候选物品库里的每个物品打分。
    FtWb⊤+b\mathbf{F}_t \mathbf{W}_b^\top + \mathbf{b}
  3. 概率化softmax 把分数转成概率分布。
    p(s)=softmax(⋅)p(s) = \text{softmax}(\cdot)
  4. 交叉熵平均对所有训练序列的“下一项预测”求平均负对数似然。
    L=−1∣S∣∑Slog⁡p(st+1)\mathcal{L} = -\frac{1}{|\mathcal{S}|}\sum_{\mathcal{S}} \log p(s_{t+1})

多任务多目标

现实世界没有单一标准。小明在觅食 app 里考虑“下一顿吃什么”时,同时要权衡三个目标:健康吗?买得起吗?做起来快吗?推荐系统同样要同时优化点击率、加购率、转化率等多个目标。

多任务学习(Multi-Task Learning)让一个模型主干同时服务多个目标。最朴素的做法是共享底层编码参数,顶部接多个任务输出头;更进一步用门控(Gating)机制,让不同任务动态地“借用”不同比例的专家子网络,这就是 MMoE(Mixture-of-Experts)的思路。

训练时,总损失是各任务损失的加权和,权重 $\lambda_k$ 可以人工设定,也可以用不确定性加权自动学习:

多任务就像一位主厨同时管理“好吃、快、便宜”三条标准:共享同一套食材(底座参数),但不同目标按需调配不同厨师(专家)。而自注意力与多头注意力,正是这套复杂决策中读懂用户历史的“眼睛”。

gk(x)=exp⁡(wk⊤x)∑j=1Kexp⁡(wj⊤x),L=∑k=1KλkLk\displaystyle g_k(\mathbf{x}) = \frac{\exp(\mathbf{w}_k^\top \mathbf{x})}{\sum_{j=1}^{K}\exp(\mathbf{w}_j^\top \mathbf{x})}, \quad \mathcal{L} = \sum_{k=1}^{K} \lambda_k \mathcal{L}_k
门控就是“总调度师”:看一眼用户状态,决定每个目标该多依赖哪位专家。
  1. 门控得分$\mathbf{w}_k^\top \mathbf{x}$ 衡量输入 $\mathbf{x}$ 与第 k 个专家的契合程度。
    wk⊤x\mathbf{w}_k^\top \mathbf{x}
  2. softmax 归一所有专家的门控权重相加为 1,形成软分配。
    gk(x)∈[0,1],∑kgk(x)=1g_k(\mathbf{x}) \in [0, 1], \quad \sum_k g_k(\mathbf{x}) = 1
  3. 专家加权融合每个任务按门控比例融合多个专家子网络的输出。
    ∑kgk(x)⋅Expertk(x)\sum_k g_k(\mathbf{x}) \cdot \text{Expert}_k(\mathbf{x})
  4. 损失加权各任务损失按 $\lambda_k$ 加权求和,平衡不同目标的量级与重要度。
    L=∑k=1KλkLk\mathcal{L} = \sum_{k=1}^{K} \lambda_k \mathcal{L}_k

AI 应用场景

电商“下一件商品”预测

阶段:召回(Recall)

解决的问题:传统协同过滤只看“买了 A 的用户也买 B”的静态相似,看不到“买了手机之后通常接着买钢化膜”这种顺序规律。自注意力能动态捕捉序列中每次交互的上下文,把“上一个买了什么”变成强信号。

具体案例:淘宝、亚马逊的“经常一起买”和“猜你喜欢”中,模型读入用户最近的浏览/购买序列,用因果掩码的多头注意力预测下一件商品,再进入精排。深夜下单手机壳的用户,下一轮召回里会出现数据线、贴膜,而不是三个月前看过的沙发。

短视频与信息流 Session 推荐

阶段:粗排 / 个性排序

解决的问题:用户连续滑动视频是一条高噪声的会话序列,传统 CTR 模型把每次曝光当独立样本,白白浪费了“连续看了三个搞笑视频”这种节奏信息。序列模型把会话当作干饭日记,自注意力负责找出“哪个视频才是这次刷到底的原因”。

具体案例:抖音、快手的信息流中,模型把用户最近 50 次滑动的视频 ID 作为序列输入,叠加视频类别、作者、时长等特征,多头注意力分别从“内容品类迁移”和“完播节奏”两个视角建模,显著提升人均时长与留存。

广告与电商的多目标排序

阶段:精排(Ranking)

解决的问题:一个推荐结果既要让用户愿意点、愿意加购、还愿意付款,三个目标天然矛盾。共享底座 + 门控的多任务框架,让点击率子任务和转化率子任务共享用户历史行为的注意力表示,又各自保留专属专家,避免“只顾点击不顾转化”。

具体案例:电商大促场景下,MMoE 结构把“点击预估”“加购预估”“支付预估”三个任务挂在同一个多头上,门控网络根据用户实时行为动态分配专家权重,大盘 GMV 与点击率同步提升,同时服务端推理耗时几乎不变。

Python 演示

python75 行
import numpy as np np.random.seed(42)  def softmax(x, axis=-1):    e = np.exp(x - x.max(axis=axis, keepdims=True))    return e / e.sum(axis=axis, keepdims=True)  class MiniAttention:    """迷你多头自注意力,演示前向计算。"""     def __init__(self, d_model=16, num_heads=2):        self.d_model = d_model        self.num_heads = num_heads        self.d_head = d_model // num_heads        self.WQ = np.random.randn(d_model, d_model) * 0.05        self.WK = np.random.randn(d_model, d_model) * 0.05        self.WV = np.random.randn(d_model, d_model) * 0.05        self.WO = np.random.randn(d_model, d_model) * 0.05     def split_heads(self, x):        B, T, D = x.shape        x = x.reshape(B, T, self.num_heads, self.d_head)        return x.transpose(0, 2, 1, 3)     def forward(self, x, mask=None):        q = x @ self.WQ        k = x @ self.WK        v = x @ self.WV        qh = self.split_heads(q)        kh = self.split_heads(k)        vh = self.split_heads(v)        scores = qh @ kh.transpose(0, 1, 3, 2) / (self.d_head ** 0.5)        if mask is not None:            scores = scores + mask        weights = softmax(scores)        out = weights @ vh        B, H, T, Dh = out.shape        out = out.transpose(0, 2, 1, 3).reshape(B, T, self.d_model)        return out @ self.WO, weights  class MiniSASRec:    def __init__(self, num_items=20, d_model=16, num_heads=2):        self.num_items = num_items        self.d_model = d_model        self.item_emb = np.random.randn(num_items, d_model) * 0.1        self.pos_emb = np.random.randn(20, d_model) * 0.1        self.attn_layer = MiniAttention(d_model, num_heads)     def predict(self, seq):        e = self.item_emb[seq] + self.pos_emb[:len(seq)]        mask = causal_mask(len(seq))        out, _ = self.attn_layer.forward(e[np.newaxis, :, :], mask=mask)        logits = out[0, -1] @ self.item_emb.T        return softmax(logits)  def causal_mask(length):    m = np.zeros((1, 1, length, length))    for i in range(length):        m[0, 0, i, i + 1:] = -1e9    return m  if __name__ == "__main__":    model = MiniSASRec(num_items=20)    seq = [3, 7, 12, 18, 5]    probs = model.predict(seq)    top5 = np.argsort(probs)[::-1][:5]    print("历史行为序列:", seq)    print("预测下个交互 Top-5 物品:", top5.tolist())    print("对应概率:", [round(float(probs[i]), 4) for i in top5])
目的:用 NumPy 从零实现一个极简版 SASRec——带因果掩码的多头自注意力序列推荐模型,跑一遍“读历史行为 → 预测下一项”的完整前向流程。
  1. qh = self.split_heads(q)把 Q 矩阵切分成 num_heads 个子空间,对应多位“美食顾问”。
  2. scores = qh @ kh.transpose(0, 1, 3, 2) / (self.d_head ** 0.5)缩放点积打分,衡量每个历史行为与当前问题的相关度。
  3. weights = softmax(scores)把原始分数归一化成非负且和为 1 的注意力权重。
  4. out = weights @ vh按权重对值向量加权求和,完成“按相关度记忆”。
  5. m[0, 0, i, i + 1:] = -1e9构造因果掩码,把未来位置全部屏蔽。
  6. e = self.item_emb[seq] + self.pos_emb[:len(seq)]物品嵌入加位置编码,恢复序列顺序信息。
  7. logits = out[0, -1] @ self.item_emb.T取最后位置输出与全物品库做点积,生成候选打分。
  8. top5 = np.argsort(probs)[::-1][:5]按概率降序取 Top-5,模拟线上召回结果。

常见误区

误区我以为自注意力就是算出每个历史物品“有多重要”,然后挑最重要的几个,其他没用的丢掉。

正解自注意力不是静态排序,而是“动态加权融合”。权重是针对当前查询(Query)算出来的:同一个历史行为,在“加班后”和“周末早晨”两种情境下权重完全不同;最终输出是所有历史行为的加权求和,而不是选 Top-K 丢弃。就像复盘日记时,没有哪顿饭绝对没用,只是参考程度随处境变化。

为何常见:因为注意力权重天然是“看起来像重要性”的数值,初学者容易把加权求和的机制误解成筛选机制。

误区我以为 head 越多 = 层数越多 = 模型越深,所以多头就是加了几个注意力层。

正解多头是在“同一层内”并行做多次独立的线性投影和注意力计算,再拼接融合,并不增加网络深度。它增加的是“表达宽度”:每个头关注不同子空间的关系模式。好比同一道菜请三位顾问同时尝,而不是让三位顾问排队尝三遍。

为何常见:因为直觉上“多个”很容易联想到“多层叠加”,忽略了并行与串行的结构差异。

误区我觉得自注意力已经看了序列里所有物品,顺序自然就记住了,不用专门管位置。

正解恰恰相反,自注意力对位置“天然失忆”——它把输入当集合,打乱顺序后输出不变,这叫置换不变性。没有位置编码,模型根本分不清“先火锅后沙拉”和“先沙拉后火锅”。位置信息必须显式注入(正弦编码或可学习编码),否则序列推荐毫无意义。

为何常见:因为自注意力确实“看到了所有物品”,让人误以为它连顺序也看到了,忽略了它的数学本质是集合操作。

误区反正训练时数据都在手里,让每个位置都能看到完整序列,信息更多,效果应该更好才对。

正解这是标准的信息泄露陷阱。如果训练时允许看未来,模型会学到“参考答案”;一旦线上预测时必须按时间顺序输出,它就彻底失灵。因果掩码把未来位置设为负无穷,强制训练与推测一致。就像小明预测周五晚饭时,绝不参考周五之后才写的日记。

为何常见:因为离线实验里“偷看”往往能让损失降得更低,让人误以为模型变强了,实则是在自欺欺人。

误区多任务推荐嘛,把点击率损失和转化率损失加在一起反向传播就行了。

正解直接相加有两个问题:一是各任务损失量级不同,大的会主导训练;二是任务间可能负迁移,共享参数互相打架。所以工业界普遍用门控分配专家(如 MMoE)、不确定性加权、动态调节 $\lambda_k$ 等手段,让任务既能共享信息又保持独立。就像主厨分配三名厨师时,要按当天菜量动态调配,而不是每人平均干三分之一。

为何常见:因为“多任务 = 多损失”看起来顺理成章,忽略了损失量级和任务冲突这两个工程关键点。

面试题

Q1. 自注意力中为什么要除以 $\sqrt{d_k}$?

单选题 · easy

  • A.防止点积过大导致梯度消失← 正确答案
  • B.让所有分数都小于一
  • C.加快矩阵乘法的速度
  • D.让权重之和恰好等于一

答案:A。点积结果随维度 $d_k$ 增大而增大,过大数值会让 softmax 进入饱和区,梯度趋近于 0。除以 $\sqrt{d_k}$ 把分数拉回合理范围,稳定训练。B、D 描述的是 softmax 的性质,C 与缩放无关。

Q2. 多头注意力中“头”的本质是什么?

单选题 · mid

  • A.多个并行的线性投影子空间← 正确答案
  • B.多个堆叠的注意力网络层
  • C.多份参数做简单投票表决
  • D.多个损失函数加权求和

答案:A。每个头用独立的 $\mathbf{W}_i^Q, \mathbf{W}_i^K, \mathbf{W}_i^V$ 把输入投影到不同子空间并独立做注意力,最后拼接融合。它增加的是宽度而非深度,B 混淆了层数与头数,C、D 与多头机制无关。

Q3. 自注意力具有置换不变性,序列推荐中如何恢复顺序信息?

单选题 · easy

  • A.加入位置编码保留先后次序← 正确答案
  • B.对序列做随机打乱重排
  • C.全部换成循环神经网络模型
  • D.对物品向量直接取平均处理

答案:A。自注意力把序列当集合,打乱顺序输出不变,必须显式加入位置编码(正弦或可学习)让模型感知先后。B 会彻底丢失顺序,C 是绕开问题而非解决,D 连物品本身的信息都会丢失。

Q4. 训练序列推荐模型时使用因果掩码的目的有哪些?

多选题 · mid

  • A.防止模型偷看未来物品← 正确答案
  • B.保证预测过程是自回归的← 正确答案
  • C.增加参数量以拟合数据
  • D.模拟真实在线预测的场景← 正确答案

答案:ABD。因果掩码把未来位置设为负无穷,经过 softmax 后权重为 0,从而防止信息泄露,并让每一步输出只依赖过去,与线上自回归预测一致。它与参数量无关,C 错误。

Q5. softmax 在注意力机制中的作用是什么?

单选题 · easy

  • A.将注意力权重归一化且和为 1← 正确答案
  • B.把向量投影到更高维空间
  • C.让最大权重变成一其余为零
  • D.对梯度做裁剪防止训练爆炸

答案:A。softmax 把原始分数转成非负且总和为 1 的概率权重,决定每篇“日记”被参考多少成。B 是线性投影的作用,C 是 argmax 或 one-hot 的效果,D 是梯度裁剪,都与 softmax 无关。

Q6. 正弦位置编码的核心优势是什么?

单选题 · mid

  • A.可以外推到更长的序列← 正确答案
  • B.完全不需要任何额外计算量
  • C.只能编码有限的整数位置
  • D.与物品嵌入完全相互等价

答案:A。正弦编码由固定函数生成,任意位置都能算出编码,因此可外推到训练未见过的长度。可学习编码才有位置数量上限,B 说“零计算量”过于绝对,C、D 描述有误。

Q7. 工业界多任务推荐系统中常用的手段有哪些?

多选题 · hard

  • A.共享底层编码器参数← 正确答案
  • B.门控网络动态分配专家← 正确答案
  • C.对各任务损失加权平衡← 正确答案
  • D.只保留点击率单一目标

答案:ABC。共享底座让多任务互相补充数据,门控(如 MMoE)让任务按需借用专家,损失加权解决量级不平衡。D 放弃多目标,与多任务学习的初衷背道而驰。

Q8. 多头注意力拼接完成后还需要做什么?

单选题 · mid

  • A.乘以输出投影矩阵完成融合← 正确答案
  • B.直接输出不加任何处理
  • C.再做一次 softmax 归一化
  • D.按元素取平均并四舍五入

答案:A。拼接后的维度是 $h \cdot d_v$,需要乘以 $\mathbf{W}^O$ 压缩回原始维度 $d$,同时也完成多视角信息的交叉融合。B 会导致维度不匹配,C、D 都不是标准做法。

Q9. 相比 RNN,自注意力在序列建模上的改进包括哪些?

多选题 · extreme

  • A.支持并行并缩短依赖路径← 正确答案
  • B.参数数量必然大幅减少
  • C.任意位置信息直达无损耗← 正确答案
  • D.完全不需要再引入位置信息

答案:AC。RNN 必须按时间步串行,且长距离依赖要经过多步传递;自注意力一步直达所有位置且可并行,这是它取代 RNN 的核心原因。B 不成立,自注意力参数量通常不小;D 错误,自注意力仍需位置编码。

Q10. 序列推荐中“预测下一项”通常使用什么损失函数?

单选题 · easy

  • A.交叉熵损失函数来优化← 正确答案
  • B.均方误差损失函数优化
  • C.合页损失函数来度量错误
  • D.感知损失函数来惩罚噪声

答案:A。下一项预测本质是「从全物品库中选一个正确项」的多分类问题,标准做法是对 softmax 概率取负对数似然,即交叉熵。均方误差更适合回归,合页损失多用于 SVM 类模型,感知损失用于图像生成,都与推荐预测目标不匹配。