PGN 原理与实现
指针生成网络的词表生成、原文复制与重复抑制。
指针生成网络(Pointer-Generator Network,PGN)合并词表生成与原文复制的概率,让摘要能够输出原文中的词表外词。 Coverage 记录历史注意力,约束反复关注相同位置的行为,缓解重复生成。
See 等人在 2017 年的论文中提出的 PGN 摘要模型由双向 LSTM 编码器、单向 LSTM 解码器和注意力机制组成,属于早期的序列生成模型。
文本摘要有两种常见方式:
- 抽取式摘要:选择原文中的句子或片段,TextRank 属于这一类。
- 生成式摘要:逐步输出词,能够重新组织表达。
PageRank 根据网页之间的链接关系计算重要性。网页的得分不仅取决于有多少页面指向它,还取决于这些页面的得分;各页面的分数通过迭代更新。
TextRank 把类似的图排序方法用于文本。做抽取式摘要时,以句子为节点,按内容相似度建立带权边,通过迭代评分选择重要句子。
它直接选取原句,不需要训练逐词生成的解码器。句子排序本身不负责改写或消除句间重复。
1. Seq2Seq 与注意力
Seq2Seq 将输入序列映射为输出序列。摘要任务的输入是原文,输出是较短的摘要;两者长度不必相同。
假设分词后的原文是 :
- 编码器:双向 LSTM 为每个原文位置产生隐状态 ,保留该位置的表示。
- 解码器:接收上一个词的向量表示,递推更新状态,每个时间步预测一个词。第 步的状态记为 ,包含当前输出前缀的信息。
注意力机制根据 和每个 计算分数,经 softmax 得到原文各位置的权重:
、、 和 是可学习参数。 表示当前步分配给第 个原文位置的注意力,有效位置的权重之和为 。
PAD 是补齐序列长度的标识,对应位置不参与注意力分配。
按注意力权重汇总编码器状态,就得到当前步的上下文向量:
上下文向量随解码步骤变化,使不同的输出词能够依赖不同的原文内容。解码器状态 与 拼接,经过线性层和 softmax,得到固定词表上的概率分布 。
普通的固定词表输出层只能为词表内的词分配概率。 某个人名若不在词表里,即使注意力指向它,输出层也没有对应的类别。
词表外词称为 OOV(Out-of-Vocabulary),输入时通常映射为统一的 UNK 标识。
这里的“词”指分词后的 token。按词分词、按字分词和子词分词会改变 OOV 的范围,不能脱离分词方式讨论 OOV 比例。
2. PGN 的复制机制
2.1 词表分布与位置分布
PGN 保留词表生成分支,同时把注意力分布作为复制分支的依据。两条分支描述的对象不同:
| 分布 | 每个元素对应什么 | 能提供哪些输出 |
|---|---|---|
| 词表分布 | 固定词表中的一个词 | 词表内的词,包括原文未出现的词 |
| 注意力分布 | 原文中的一个位置 | 该位置上的词,包括原文中的 OOV |
同一个词可能出现在原文的多个位置。复制这个词的概率,要把所有对应位置的注意力加起来。 因而复制分布为:
求和条件 表示只收集内容为 的原文位置。经过这一步,复制分支的概率才从“位置”对应到“词”。
2.2 生成权重与最终分布
生成权重 由当前上下文向量、解码器状态和当前解码输入的词向量 共同计算:
是 sigmoid 函数,将结果映射到 与 之间。式中的权重和偏置通过训练学习,每步的输入和状态变化会影响 。
是上一个输出词的输入表示,首步使用开始标识。
控制词表分支的权重, 控制复制分支的权重。 两条分支共同形成最终分布:
这是软混合:计算时保留两部分贡献,不需要按某个阈值把其中一条分支关掉。同一个词既在固定词表内、又出现在原文中时,两部分概率会加到一起。
示例
原文 token 为
[岑川, 到访, 青岚, ,, 岑川, 发言],对应注意力为[0.30, 0.05, 0.15, 0.05, 0.35, 0.10],总和为 。固定词表不含“岑川”,但含“发言”。设 ,。
- “岑川”出现两次,复制概率为 ,最终概率为 。
- “发言”的词表概率和复制概率均为 ,最终概率为 。
“岑川”虽然不在固定词表内,仍获得了非零输出概率。这里计算的是当前步的概率,不代表模型已经选中该词,也不代表完整摘要的质量。
2.3 扩展词表与 OOV
最终分布定义在固定词表与当前原文词集合的并集上,这个并集称为扩展词表。原文中的每个不同 OOV 获得一个临时编号;同一个 OOV 重复出现时,共用该编号。
实现概率合并时,词表分布在新增位置补零,复制分支按照原文的扩展词表编号累加概率。scatter_add 这类按索引累加的操作,就能把同词位置的贡献加到一起。
同一个源内 OOV,作为输入和预测目标时使用的编号不同:
- Embedding 输入使用固定词表中的
UNK编号,因为 Embedding 表没有临时扩展编号对应的向量。 - 复制分支保留能够还原原词的扩展编号。训练时以这个编号作为目标,从最终分布中取对应概率。
原作者的解码实现将复制出的 OOV 映射为 UNK,作为下一步输入;目标编号处理则将参考摘要中既不在固定词表、也不在原文里的词映射为 UNK。
不在固定词表内、也没出现在原文里的词,两条分支都无法提供。
即使复制出的名字正确,模型仍可能把它与错误的动作、时间或数字组合起来。复制机制不能单独保证整句符合原文事实。
3. Coverage 的重复抑制
PGN 能复制原文,但仍可能在多个解码步骤反复关注相同位置,产生重复短语。Coverage 为注意力机制加入历史信息。
3.1 累积历史注意力
第 步开始时,coverage 向量记录之前所有步骤的注意力之和:
较大,表示原文第 个位置此前获得过较多注意力。它是累计量,不是归一化概率,单个位置的值能够超过 。
Coverage 作为额外输入参与当前注意力打分:
是可学习向量。历史累计量因此能够影响当前注意力,但“累计量大”并不直接等于“当前权重一定小”:最终分数仍由输入特征和学习到的参数共同决定。
3.2 覆盖损失
覆盖损失衡量当前注意力与历史累计注意力的重叠:
某个位置此前几乎没被关注时, 很小,对该位置分配当前注意力产生的重叠也小;若此前已经获得较多注意力,当前继续分配的部分就更可能被计入损失。
示例
假设原文只有三个有效位置,第一步注意力为 。第二步开始时,累计量为 。
比较两个假设的当前分布:
- :覆盖损失为 。
- :覆盖损失为 。
第二个分布把更多注意力分给此前关注较少的第三个位置,因此重叠损失更低。这个比较只说明覆盖损失的作用;哪个分布更有利于生成正确摘要,还取决于目标词的预测损失。
PGN 使用最终词分布计算目标词的负对数似然,加上覆盖损失,形成每一步的训练目标:
是参考摘要在当前步的词, 控制重复关注受到多大的惩罚。
- 目标词的概率越高,第一项损失越低。
- 当前注意力与历史重叠越多,第二项损失越大。
训练对摘要有效位置的损失取平均,PAD 不计入。
计算当前覆盖损失时必须使用更新前的 。 当前步完成后,历史量更新为 。
若误把更新后的累计量代入,因为 ,每一项都会变为 。损失总和恒为 ,就无法区分重复程度。
Coverage 惩罚的是原文位置上的注意力重叠,不要求摘要提及每个原文位置,也没有直接识别语义重复。多个位置表达同一件事时,模型仍可能换一个位置重复相同内容。
4. 训练输入与权重共享
4.1 数据与特殊标识
训练数据是成对的原文和参考摘要。固定词表由训练集建立;原文和摘要使用相同的分词、编号规则。
- 长度统计:观察原文、摘要的长度分布,确定截断长度。原文被截去的部分无法参与复制,OOV 扩展编号应按截断后的内容建立。
- 特殊标识:
BOS表示开始,EOS表示结束,PAD补齐 batch 内的长度,UNK接收固定词表外的输入。 - 清洗规则:删除网页标签、损坏字符等噪声,同时保留正文中的人名、数字、否定词和句子结构。生成式摘要不应直接套用抽取关键词时的停用词过滤。
4.2 训练时的输入来源
Teacher Forcing 在训练时把参考摘要的上一个词作为当前输入。推理时没有参考答案,模型只能使用自己已经输出的词。
假设“记者”和“到访”都在固定词表内,参考摘要以“记者/到访”开头。预测第二个词时:
- 训练:Teacher Forcing 输入参考词“记者”,即使模型第一步预测错了。
- 推理:输入模型实际输出的词,后续预测可能受到错误前缀影响。
这种训练与推理时历史输入的差异称为 Exposure Bias(曝光偏差)。模型训练时主要接触正确前缀,推理时却需要处理自身错误累积产生的前缀。
Scheduled Sampling 在训练中按一定概率选择真实前词或模型预测词,并随训练进展调整比例,让模型接触自身预测形成的输入。
它改变训练时的输入来源,不能仅凭采用了这种策略就认定摘要质量提高。
4.3 权重共享
Weight Tying(权重共享)改变的是参数关系。 两种不同的共享方式是:
- 编码器与解码器共享输入 Embedding:需要对齐词表及编号含义。
- 解码器的输入 Embedding 与输出投影共享权重:需要匹配矩阵维度。
共享权重能减少独立参数,但不会改变“训练输入真实前词、推理输入预测前词”的差异。两套 Embedding 不同本身也不构成曝光偏差。
5. Beam Search 的候选搜索
模型为下一步提供概率分布,解码算法据此选择输出序列。贪心解码每步只保留概率最大的词,一旦选错前缀,后续步骤无法回到被舍弃的分支。
Beam Search(束搜索) 同时保留至多 条尚未完成的候选序列。每一步的搜索过程是:
- 扩展当前候选,得到可能的后续词。
- 将新词的条件概率计入整条路径的分数。
- 按新分数保留得分最高的 条候选。
通常用累积对数概率评分:
这里的 是完整原文, 是当前候选的历史前缀。每条候选都有自己的解码器状态;带 coverage 的模型还需要分别保存候选对应的累计注意力。
下面是自拟的两步搜索例子。设第一步只有 A、B 两个候选,第二步只有 X、Y 两个候选,之后都以概率 输出结束符 EOS,因此第二步的路径概率就是完整序列概率。
- 贪心解码在第一步选择 A,最终只能得到概率为 的 AX 或 AY。
- 的束搜索保留 A 和 B,第二步能够发现概率为 的 BX。
保留多个前缀能改变搜索结果,但有限宽度的束搜索仍可能丢弃后续更好的路径,不保证全局最优。
输出 EOS 的候选进入完成集合,不继续扩展。其余候选继续搜索,并设置最大输出长度防止无限生成。
直接比较不同长度序列的概率乘积,会倾向于较短结果:每增加一个词,都会乘上不大于 的概率。实际评分常加入长度归一化,比较实验时需要说明规则。
Beam Search 本身没有重复惩罚。 如果模型给重复序列较高概率,搜索仍可能选中它。扩大 会增加需要计算和保存的候选状态,是否值得取决于质量变化与推理成本。
6. 核心源码
下面的 PGN + Coverage 核心实现包含编码器、带 coverage 的注意力、解码器、生成与复制概率合并,以及训练损失计算。
它接收已经编号的原文与摘要,支持一次训练计算和单步解码。代码未包含数据集加载、完整训练流程或 Beam Search,也不代表已经训练好的摘要模型。
6.1 输入与调用
固定词表大小记为 ,batch 大小为 ,补齐后的原文、摘要长度分别为 、。四组编号都使用 torch.long,与模型放在同一设备上:
| 输入 | 形状 | 编号含义 |
|---|---|---|
source_ids | 原文的固定词表编号,OOV 换成 UNK | |
source_extended_ids | 相同原文的扩展编号,源内 OOV 从 起编号 | |
decoder_input_ids | BOS 加参考摘要前缀,OOV 输入使用 UNK | |
target_extended_ids | 参考摘要加 EOS,源内 OOV 保留扩展编号 |
每条原文独立维护 OOV 表;同一个编号在不同样本中可能代表不同词。max_oov 是本批次中单条原文最多包含多少个不同 OOV,最终分布的最后一维为 。
示例
固定词表含
到访,不含岑川;设岑川在当前原文中的扩展编号为 。参考摘要为岑川/到访时,解码输入是[BOS, UNK, 到访],训练目标是[V, 到访, EOS]。
source_ids中的“岑川”同样写成UNK,source_extended_ids中每次出现的“岑川”都写成 。
原文非空,摘要目标至少包含 EOS。原文和摘要均在右侧补 PAD;参考摘要中既不在词表、也不在原文的词,目标编号使用 UNK。
6.2 模型与损失
编码器与解码器共享输入 Embedding。编码器的双向末状态拼接后,通过线性层和 tanh 转为解码器初态;注意力和生成权重使用解码器的 hidden、cell 两部分状态。
- 训练:
forward()对每条摘要的有效目标位置取平均,再对 batch 取平均,返回标量损失与各步最终分布。 - 解码:
decode_step()返回单步分布、新状态和注意力。调用方负责选择输出词、累加 coverage,并处理EOS或最大长度停止条件。
将下面代码保存为 pgn_core.py,在 Python 3.12 环境中安装依赖并执行:
| |
| |
示例使用随机初始化参数和两条手工构造的输入,检查分布形状、反向传播和源内 OOV 的输出概率,不用于评估摘要质量。
| |
此实现保留
p_gen的 sigmoid 结果,不额外截断生成权重;只在目标词概率取对数时设置数值下限,避免计算log(0)。
7. 效果判断与适用范围
复制、coverage 和束搜索作用在不同位置,检查效果时需要分别观察:
| 要判断的问题 | 可观察的结果 | 需要避免的误判 |
|---|---|---|
| 复制机制是否有帮助 | 原文 OOV 的保留情况,以及人名、数字等细节是否正确 | 复制出的词正确,不代表它与其他词组成的事实关系正确 |
| Coverage 是否减少重复 | 重复短语比例、重复句子,以及摘要是否遗漏重要信息 | 单纯降低重复,不代表主要内容保留得更好 |
| 搜索设置是否合适 | 摘要质量、输出长度、延迟与候选状态的存储开销 | 更高的模型概率,不等于更高的人类评价 |
ROUGE 常用于摘要评测:
- ROUGE-1:衡量单词的重合。
- ROUGE-2:衡量连续词对的重合。
- ROUGE-L:基于最长公共子序列。
这些指标反映与参考摘要的重合程度,不能单独判定事实一致性。 人名与动作的对应关系、数字归属、否定和时间关系仍需要单独核对。
模型对照应使用相同的数据划分与分词规则,并交代输入截断长度、摘要长度限制和解码评分。若一个模型使用贪心解码,另一个使用较宽的束搜索,结果差异就同时包含模型与搜索设置的影响。
在支持充分字符或字节覆盖的子词分词方案下,生僻词能够拆成多个 token,传统词级模型的 OOV 问题会有所变化。
PGN 的扩展词表机制因而不能直接当作现代大语言模型的通用必需组件。是否需要显式复制机制,要结合分词方案和任务对原文片段的保留要求判断。
评论
使用 GitHub 登录后参与讨论,评论将公开保存在 GitHub。
站长管理删除会同步到 GitHub,无法撤销。
阅读到这里时加载评论。