第 18 天 · 输出不是词,而是指向输入的位置
一句话
论文把注意力权重直接当作输出分布:解码器每一步从输入元素里选一个,输出的是位置,而不是固定词表里的词。输出类别数因此随输入长度变化。模型从样例中学会近似求解凸包、Delaunay 三角剖分和小规模旅行商问题。
论文讲了什么
问题。标准 seq2seq 的输出是固定词表上的 softmax。很多问题的答案却是"输入里的某几个元素"。例如给 10 个点求凸包,答案是其中几个点的编号。点数一变,可选答案的个数也跟着变。固定大小的输出词表做不到这一点。
方法。作者给第 15 天的加性注意力换了一个用途。原来,注意力权重用来混合编码器状态,得到上下文向量。指针网络不再混合,直接把权重当作输出:
- 打分:
u_j = v^T tanh(W1 e_j + W2 d_i)。e_j是第 j 个输入的编码器状态,d_i是第 i 步的解码器状态。 - 输出:
p(C_i | C_1, ..., C_{i-1}, P) = softmax(u)。概率最高的 j,就是这一步"指向"的输入。
输出类别数等于输入长度。输入 50 个点,每一步就是 50 选 1。输入 500 个点,就是 500 选 1。模型参数量不随输入长度变化,所以同一个模型能处理不同长度的输入。
3 个任务。输入都是平面上的点坐标:
- 凸包:输出凸包顶点的下标序列。
- Delaunay 三角剖分:输出一组三角形,每个三角形用 3 个下标表示。
- 平面旅行商问题(TSP):输出一条访问所有城市各一次的环路。训练标签来自精确解法或近似算法。
结果。凸包任务上,指针网络远好于带注意力的普通 seq2seq。用最多 50 个点训练的模型,可以直接用于 500 个点的输入。这时完全正确的比例很低,但预测的凸包覆盖了真实凸包的绝大部分面积。TSP 任务上,城市数不超过 20 时,模型给出的环路长度接近最优解。城市数超出训练范围后,质量明显下降。论文全程使用监督学习,需要现成的解作为标签。
例子。输入 5 个点 [A, B, C, D, E],其中 B 在其他 4 个点围成的区域内部。理想输出是 [A, C, D, E] 这样按逆时针排列的 4 个下标,B 不出现。解码器第 1 步指向 A,第 2 步指向 C,依此类推,最后输出结束符。
关键概念
- 指针(Pointer)
- 解码器每一步在输入位置上的概率分布,取最大值即选中一个输入元素。它复用注意力的打分,但不再做加权求和。
- 可变输出词表(Variable-Size Output Dictionary)
- 输出类别数等于当前输入长度,而不是训练前固定的词表大小。这是指针网络能处理不同规模输入的原因。
- 凸包(Convex Hull)
- 包住全部点的最小凸多边形。经典算法复杂度为 O(n log n),论文用它测试模型能否从样例中学会几何算法。
- 旅行商问题(Traveling Salesman Problem)
- 给定城市坐标,找出访问每个城市一次并回到起点的最短环路。它是 NP 难问题,常作为组合优化的基准。
- 组合优化(Combinatorial Optimization)
- 在有限但数量巨大的离散方案中找最优解,例如排序、路径和分配问题。指针网络开启了用神经网络直接输出这类方案的路线。
- 教师强制(Teacher Forcing)
- 训练时把标签中上一步的正确答案喂给解码器,而不是模型自己的预测。指针网络喂入的是被指向点的坐标。
历史位置:为什么值得读
第一作者 Vinyals 也是 2014 年 seq2seq 论文的作者之一。seq2seq 解决了"序列到序列",指针网络解决了"输出就是输入的一部分"。几个月后,同一团队发表了第 16 天的 Order Matters,继续处理输入无序的问题。
指针网络的影响分 3 条线:
- 神经组合优化:2016 年 Bello 等人用强化学习训练指针网络,不再需要最优解当标签。2019 年 Kool 等人用 Transformer 编码器加指针解码器求解路径规划问题。
- 复制机制:2016 年的 CopyNet 和 2017 年的指针-生成网络(pointer-generator)让摘要模型能直接从原文复制人名和数字,解决了未登录词问题。
- 抽取式问答:阅读理解模型输出答案在原文中的起点和终点位置,这也是一种指针。BERT 做 SQuAD 时就用这种输出方式。
值得读原文的理由:论文只改了一个小地方,就是把注意力的用途从"读"换成"选"。读懂这个改动,就能理解后来大量"从输入中挑选"的模型。
学前准备
- 先看第 15 天,掌握加性注意力的打分公式
- 理解 seq2seq 的编码器、解码器和教师强制
- 知道凸包和旅行商问题是什么
- 会用 PyTorch 写 LSTM 和 softmax 掩码
在原仓库怎么学
- 读 README.md,看它把指针注意力对应到论文的哪个公式。
- 对照 paper_notes.md 读原论文第 2.3 节和第 4 节实验。
- 读 implementation.py,先看
PointerAttention,再看PointerNetwork怎么在解码循环中调用它。 - 做 exercises/ 里的 5 个练习:
exercise_01_pointer_attention.py写指针注意力,exercise_02_masking.py写掩码防止重复选择,exercise_03_convex_hull.py生成凸包的指针标签,exercise_04_tsp_cost.py计算环路总长度,exercise_05_greedy_decoding.py写贪心解码循环。答案在 exercises/solutions/。 - 运行 train_minimal.py,在排序任务上训练,用
--seq_len改变序列长度,看准确率怎么变化。 - 用 visualization.py 画指针分布热力图,检查排序结果。
- 用 notebook.ipynb 串一遍,用 CHEATSHEET.md 复习。
今天再看
"从输入里挑选"这个想法今天无处不在:
- 大语言模型里的复制:可解释性研究发现了归纳头(induction heads)。这种注意力头在上下文中找到前面出现过的模式,再把后面的词复制过来。它就是模型内部自己长出来的指针。
- 引用和抽取:让模型标出答案出自原文哪一段,本质上也是指向输入位置。
- 组合优化:神经求解器仍在发展,但在大规模旅行商问题上,Concorde、LKH 这类经典求解器依然更准。神经方法多用于需要快速近似、或问题结构不断变化的场景。
论文用监督学习,需要现成的最优解,这个限制已被强化学习方法绕开。论文的另一个局限是推广:训练 20 个城市、测试 50 个城市时质量明显下降。这仍是神经组合优化的核心难题。
容易卡住的地方
- 把指针输出和普通注意力混为一谈:普通注意力用权重对编码器状态求加权和,指针网络直接对权重取 argmax 或计算交叉熵,两者只共用打分公式。
- 忘记掩码:解码时要把已选过的位置在 softmax 前设成负无穷,否则旅行商环路会重复访问同一个城市。训练和推理都要加掩码。
- 凸包标签顺序不统一:同一个凸包可以从任意顶点开始,方向也有顺、逆两种。必须固定起点和方向,否则模型会学到互相矛盾的标签。这正是第 16 天讨论的问题。
- 解码器输入喂错:下一步应该喂入被指向元素的坐标或编码,而不是它的下标数字。下标本身没有几何意义。
延伸阅读
- 原论文:Pointer Networks
- 原仓库第 18 天目录:18_pointer_networks/ —— 代码、笔记本、练习与答案(英文)
- Neural Combinatorial Optimization with RL(Bello et al.) —— 用强化学习训练指针网络
- Attention, Learn to Solve Routing Problems!(Kool et al.) —— Transformer 加指针求解路径问题
- Get To The Point(See et al. 2017) —— 指针-生成网络用于文本摘要
- In-context Learning and Induction Heads —— 大模型内部的复制机制