【3D点云暑期学习】精读笔记(1):Attention Is All You Need
研0暑期进组学习 · 3D点云多模态方向 · 论文精读系列第 1 篇
论文信息
- 标题:Attention Is All You Need
- 作者:Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N. Gomez, Łukasz Kaiser, Illia Polosukhin
- 发表venue:NeurIPS 2017
- 阅读日期:2026年6月
写在前面
这篇论文是整个深度学习领域的分水岭。在 2017 年之前,序列建模几乎被 RNN/LSTM 统治;之后,"注意力机制"成为了所有任务的通用语言。
为什么一个做 3D 点云方向的研究生要从 Transformer 读起?因为后续所有要读的论文——ViT 把图像变成序列、CLIP 用双塔对比学习、ULIP 把 3D 点云对齐到图文空间、Uni3D 用 2D 预训练的 ViT 做 3D 编码器——它们的底层全部是 Transformer。不懂 Transformer,后面的一切都是空中楼阁。
所以,第一周从这篇论文开始,把根基打牢。
一、为什么序列到序列建模不再需要 RNN/CNN,纯注意力即可?
这是整篇论文最核心的命题。论文标题 “Attention Is All You Need” 本身就是答案——你只需要注意力,不需要循环,也不需要卷积。下面从四个角度理解为什么。
1.1 并行化的根本性解放(打破时序枷锁)
RNN 的致命伤:RNN 在时间步 t 的计算必须依赖 t−1 的隐状态 h_{t-1}。这意味着计算必须一步步串行执行,无法并行。当序列长度为 n 时,RNN 必须执行O(n) 次顺序操作。
Transformer 的解法:自注意力机制同时计算序列中所有位置之间的相关性。Transformer 的最小顺序操作次数为O(1),而 RNN 为 O(n)。这使得在 GPU 上,所有位置的矩阵乘法可以一次性完成,训练时间从数周压缩到几天——论文中用 8 个 GPU 仅需 3.5 天即达到 SOTA。
1.2 极短的信号传播路径(攻克长程依赖)
学习长距离依赖是序列建模的噩梦,RNN 和 CNN 都受限于路径长度。
- RNN 的困境:信号必须沿着时间链一步步反向传播,路径长度与序列长度 n 成正比,极易梯度消失。
- CNN 的困境:卷积核受限于局部感受野,要连接远距离位置需要堆叠多层(如 ByteNet 需要 O(log_k n) 层,ConvS2S 需要 O(n/k) 层)。
- Transformer 的革命:自注意力机制将任意两个输入或输出位置之间的最大路径长度降为O(1)。无论两个词相隔多远,模型计算它们相关性的计算路径都只有一步之距,极大降低了学习全局依赖的难度。
1.3 复杂度与维度的博弈(计算效率依然能打)
很多人担心自注意力的 O(n²) 复杂度,但论文给出了其在常见场景下的优势:
在机器翻译等任务中,序列长度 n(句子长度)通常小于表征维度 d(论文设为 512)。通常的英文句子在分词后都没有 512 个词,而表征维度 d 一般设置得比较大,是因为在深度学习实践中,为了确保模型有足够的"容量"去拟合复杂任务,嵌入维度通常设为 256、512、1024。
如论文表 1 所示:
| 层类型 | 复杂度 | 顺序操作数 | 最大路径长度 |
|---|---|---|---|
| Self-Attention | O(n²·d) | O(1) | O(1) |
| RNN | O(n·d²) | O(n) | O(n) |
| Convolutional | O(k·n·d²) | O(1) | O(log_k n) |
由于 n < d,自注意力层复杂度 O(n²·d) 在计算量上甚至比 RNN 的 O(n·d²) 更便宜。
对于极长序列,论文也提出了"受限自注意力"(Restricted Self-attention)作为后备方案,但默认设置下纯注意力已经足够高效。
1.4 多头的"子空间"加持(提升表征能力)
论文强调,纯注意力之所以能生效,不仅因为它替代了 RNN,还因为它变得更强。通过多头注意力(Multi-Head Attention),模型允许不同的注意力头关注不同的表示子空间(如语法、语义、指代消解)。这是单头注意力或 RNN 隐状态混为一谈所不具备的优势,进一步证明了"纯注意力"不仅够用,而且更灵活。
二、如何处理"顺序"信息?(唯一的补丁)
既然摒弃了循环和卷积,模型如何感知词的位置?这是论证中最关键的一环。
2.1 位置编码原理
论文并没有说"位置信息不重要",而是用位置编码(Positional Encoding)作为"注入"信息。它是通过正弦/余弦函数直接加到输入嵌入上的。
重点:位置编码仅仅是输入特征的一部分,而后续所有语义关系的建模完全由注意力权重的点积计算完成,不再依赖于位置链式的传递。这证明了"顺序"可以通过特征携带,而不必通过计算流程强绑。
2.2 数值示例:"我爱你"的位置编码
假设输入序列是["我", "爱", "你"],维度 d=4(简化):
| 步骤 | 词向量(语义) | + | 位置向量(坐标) | = | 最终输入模型的数据 |
|---|---|---|---|---|---|
| “我” (pos=0) | [0.5, 0.1, 0.3, 0.8] | + | [0.0, 1.0, 0.0, 1.0] (sin/cos生成) | = | [0.5, 1.1, 0.3, 1.8] |
| “爱” (pos=1) | [0.2, 0.9, 0.4, 0.1] | + | [0.84, 0.54, 0.91, -0.41] | = | [1.04, 1.44, 1.31, -0.31] |
| “你” (pos=2) | [0.7, 0.3, 0.6, 0.5] | + | [0.91, -0.41, 0.14, -0.75] | = | [1.61, -0.11, 0.74, -0.25] |
最终,模型拿到的[0.5, 1.1, 0.3, 1.8]这个数字,既包含了"我是’我’这个字"的信息,也包含了"我是句子里的第 1 个词"的信息。
核心哲学:它不是把位置当作"标签"贴上去(拼接),而是把位置当作"时空坐标"混进语义里。因为在神经网络中,数字的大小只有相对意义,模型通过这种"叠加扰动",可以自主地通过自注意力机制去挖掘"这个扰动代表距离多远",从而完美解决了"无顺序"的难题。
2.3 为什么偏偏用"正弦/余弦"函数?
很多人以为正弦/余弦只是为了生成一个唯一的坐标值,其实它的核心妙处在于**“线性相对位置表达”**。
论文原话提到:如果单纯用数字 1, 2, 3, 4 来做位置编码,模型学到的只有"绝对距离"。但正弦/余弦函数有一个绝佳的数学性质:
位置 pos+k 的编码,可以由位置 pos 的编码经过一个线性变换(旋转矩阵)直接计算出来。
这意味着,模型不需要记忆"第 5 个词"是什么,它只需要学会"偏移量 k"这个线性运算。
举例说明位移不变性:假设 PE(pos) = [sin(pos), cos(pos)],那么:
| 位置 | 编码向量 | 旋转角度 |
|---|---|---|
| 0 | [0, 1] | - |
| 1 | [0.84, 0.54] | 逆时针 ~54° |
| 2 | [0.91, -0.41] | 逆时针 ~54° |
从 0 到 1 和从 1 到 2,向量都旋转了约 54°——无论你处于句子的开头还是中间,只要想寻找距离自己 k 步的词,只需做一次完全相同的"旋转操作"即可。
另外,选择不同频率(波长从 2π 到 10000·2π)的正余弦,是为了让不同的维度代表不同级别的粒度:低维度(低频)负责捕捉长距离位置,高维度(高频)负责捕捉短距离位置。
三、如何理解缩放点积注意力计算公式?
Attention ( Q , K , V ) = softmax ( Q K T d k ) V \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right) VAttention(Q,K,V)=softmax(dkQKT)V
3.1 直观理解:图书馆找书
| 符号 | 含义 | 类比 |
|---|---|---|
| Q(Query,查询) | 你脑子里想的问题 | “讲水果的书有哪些?” |
| K(Key,索引/标签) | 每本书封面上的标签 | 《苹果种植》标签是"水果"、《柴油机维修》标签是"机械" |
| V(Value,内容) | 书里实实在在写的内容 | 书的具体内容 |
计算步骤:
- 打分(QK^T):拿你的问题(Q)去和所有书的标签(K)做点积(匹配度)。点积结果越大,说明这本书的标签和你的问题越匹配。
- 归一化(softmax):把这些原始得分转化为概率权重(所有书的权重加起来等于 1)。匹配度越高的书,权重越大。
- 加权求和(×V):根据权重,把所有书的内容(V)按比例"揉"在一起。权重高的书,在最终结果里占的比重大。
3.2 d_k 为什么是 64 而不是 512?
d_k 是 Key 和 Query 向量的维度大小。模型整体的特征维度是 d_model = 512。那为什么 d_k 只有 64 呢?
因为多头注意力做了"降维切割"。论文使用了 h=8 个头,为了让总计算量不变,每个头分到的维度就是:512 / 8 =64。
3.3 为什么要除以 √d_k?
论文原文:对于 d_k 较大的情况,点积的数值会变得很大,从而将 Softmax 推入梯度极小的区域。为了抵消这种影响,我们将其缩放 1/√d_k。
若不除以 √d_k,会发生什么?
得分极大 → Softmax 指数爆炸 → 输出趋近 One-Hot [1, 0, 0](某一个位置权重趋近 1,别的趋近 0) → Softmax 导数公式算出 0 → 链式法则传给 Q/K 的梯度全是 0(注意力权重的梯度要传回去更新投影矩阵) → 模型参数不更新 → 模型"脑死亡",彻底学不动了这就是为什么缩放因子 √d_k 看似简单,却至关重要——它保护了梯度流通的命脉。
四、模型架构详解
论文 Figure 1:Transformer 架构图(Encoder-Decoder 结构)
4.1 整体结构
Transformer 采用Encoder-Decoder架构:
- Encoder:由 N=6 层堆叠,每层包含两个子层——Multi-Head Self-Attention 和 Feed Forward Network(FFN),每个子层外面包裹残差连接 + LayerNorm。
- Decoder:同样 N=6 层,但多了一个带掩码的自注意力子层(Masked Multi-Head Attention),保证每个位置只能看到之前的词(自回归属性),外加一个 Cross-Attention 子层(Q 来自解码器,K/V 来自编码器输出)。
4.2 关键模块
| 模块 | 作用 | 关键细节 |
|---|---|---|
| Multi-Head Attention | 捕获全局依赖 | 8 个头并行,每个头 64 维,concat 后线性投影 |
| FFN | 非线性变换 | 两层全连接 + ReLU,对每个位置独立作用 |
| Positional Encoding | 注入顺序信息 | sin/cos 固定编码,直接加到输入嵌入 |
| Residual + LayerNorm | 训练稳定 | 残差防止梯度消失,LayerNorm 稳定分布 |
| Masked Attention | 自回归保证 | Decoder 中掩码未来位置 |
五、实验亮点
- 任务:WMT 2014 英德/英法翻译
- 主要结果:English-to-German BLEU 28.4(新 SOTA),English-to-French BLEU 41.8
- 训练效率:8 个 P100 GPU 训练 3.5 天,远低于当时同等性能模型
- 关键消融:去掉缩放因子 √d_k 后,大 d_k 时模型性能显著下降,验证了缩放的必要性
六、个人思考
局限性
- O(n²) 复杂度:虽然 n < d 时计算量可控,但面对超长序列(如整篇文档、高分辨率图像)时,自注意力的平方复杂度仍是瓶颈。后续的 Linformer、Performer 等工作都在解决这个问题。
- 位置编码的固定性:sin/cos 编码是硬编码的,不能自适应学习。ViT 后续改用可学习位置编码。
与 3D 点云主线的联系
Transformer 是我整个暑期学习路线的第零块基石:
- ViT(本周第二篇):把 Transformer 从 NLP 带入 CV,用 Patch Embedding 替代词嵌入
- CLIP(第二周):双塔结构,图像编码器和文本编码器都是 Transformer
- ULIP(第五周):三模态对比学习,用 PointNet++ 做 3D 编码器对齐到 CLIP 空间
- Uni3D(第六周):直接用 2D 预训练的 ViT 做 3D 编码器,端到端训练
- Point-Bind/Point-LLM(第七周):3D 多模态对齐 + 接入 LLM
可以说,不理解 Attention,就无法理解任何一篇后续论文的核心机制。
可改进点
- 可以尝试用可学习的位置编码替代固定 sin/cos,对比效果
- 可以探索稀疏注意力(Sparse Attention)在 3D 点云上的应用(点云的 N 通常远大于文本序列)
七、动手实践:从零实现 Multi-Head Attention
读论文和写代码是两回事。你可以背下softmax(QK^T/√d_k)V的公式,但如果不亲手实现一遍,永远不会遇到 split_heads 维度变换、.contiguous()报错、softmax 维度混淆这些问题。所以本周复现了 Multi-Head Attention 模块。
7.1 核心实现:Scaled Dot-Product Attention
defscaled_dot_product_attention(Q,K,V,mask=None):d_k=Q.size(-1)# 计算缩放点积:(batch, seq_q, d_k) × (batch, d_k, seq_k)scores=torch.matmul(Q,K.transpose(-2,-1))/(d_k**0.5)ifmaskisnotNone:scores=scores.masked_fill(mask==0,-1e9)# mask=0 的位置置为极大负数attn_weights=F.softmax(scores,dim=-1)# 在 Key 维度上归一化output=torch.matmul(attn_weights,V)# 加权求和returnoutput,attn_weights7.2 关键难点:split_heads 维度变换
这是整个实现中最容易出错的地方:
defsplit_heads(self,x):# (batch, seq, d_model) → (batch, num_heads, seq, d_k)batch_size,seq_len,_=x.size()returnx.view(batch_size,seq_len,self.num_heads,self.d_k).transpose(1,2)为什么要 transpose?view 之后是(batch, seq, heads, d_k),交换 1、2 维变成(batch, heads, seq, d_k)。这样torch.matmul会把(batch, heads)当作批量维度,8 个头可以同时独立计算注意力。
合并时反过来,但注意 transpose 后内存不连续,必须加.contiguous()才能 view。
7.3 验证与可视化
前向传播形状验证(超参数同论文 d_model=512, num_heads=8):
Input: (4, 8, 512) → Output: (4, 8, 512) ✅ 形状一致 Attn: (4, 8, 8, 8) ✅ (batch, heads, seq_q, seq_k)反向传播梯度检查:8 个参数(W_Q/W_K/W_V/W_O 各含 weight 和 bias)梯度全部非空,计算图连通。
注意力热力图:构造 Q≈K 的输入(相同位置几乎相等),热力图呈现明显的对角线主导模式——每个 Query 高度关注对应位置的 Key,验证了注意力机制的正确性。
7.4 踩坑记录
| 坑 | 错误写法 | 正确写法 | 原因 |
|---|---|---|---|
| split_heads 维度 | x.view(batch, heads, seq, d_k) | x.view(batch, seq, heads, d_k).transpose(1,2) | view 只重新解释内存,必须先按数据排列重塑再转置 |
| Softmax 维度 | F.softmax(scores, dim=1) | F.softmax(scores, dim=-1) | 注意力是每个 Query 对所有 Key 的分配,softmax 在 Key 维度 |
| 内存不连续 | x.transpose(1,2).view(...) | x.transpose(1,2).contiguous().view(...) | transpose 返回视图不连续,view 要求连续内存 |
与官方实现对比:torch.nn.MultiheadAttention用batch_first=False(seq 在前)、内部用 fused kernel 优化。我的实现更易读、适合学习;官方实现更高效、适合生产。学习阶段手写一遍,理解后再用官方的——这是正确路径。
本周系列博文:
- 精读笔记(1):Attention Is All You Need← 当前
- 精读笔记(2):ViT
- 对比学习(1):Transformer vs ViT 架构对比
- 周报(1):第一周学习总结
下一篇:精读笔记(2):ViT — 当 Transformer 遇见图像