信息抽取与文本压缩:智能摘要的两段式流水线解析
最近帮同事调试一个自动摘要的管线发现他在配置里把“信息抽取”和“文本压缩”这两个模块当成同一个东西来调参摘要结果一直不对劲要么抓不住重点要么句子被砍得支离破碎。这让我想认真聊聊智能摘要里的这对核心概念。它们虽然经常被放在一起说但在技术实现上完全是两个动作信息抽取负责搞明白“这篇文本里什么内容重要”文本压缩负责解决“怎么把内容说得更短更干净”。这篇文章会围绕这两个环节展开把原理、方法、落地选型和避坑经验都过一遍。如果你正在做文本摘要、信息流内容精炼或者调研报告自动化这篇应该能直接帮上忙。1. 先厘清一件事信息抽取和文本压缩在摘要里到底各干各的活1.1 摘要不是“复制开头几句”而是两段式流水线人写摘要的时候通常先看完全文划出重点然后把重点用自己的话精简表达。机器做智能摘要其实也是流水线信息抽取先干“划重点”的活从原文中挑出那些承载核心信息的句子或片段文本压缩再干“精简表达”的活把这些候选内容变短、变干净去掉冗余成分。很多开发者容易把这两步合并理解以为“摘要重要句子拼接”所以遇到生成结果过长、信息重复、表达啰嗦时不知道应该在哪一环下手调优。实际排错的时候我会先问一个问题结果里的内容是不是都来自原文如果是说明问题大概率出在抽取阶段如果内容来自原文但表达太啰嗦那就要去查压缩阶段。把两个阶段分开看待排查效率会高很多。1.2 抽取、压缩、摘要三者到底差在哪这三个概念在自然语言处理里经常交替出现但边界其实很清晰术语目标输入/输出典型方法信息抽取从原文中定位重要信息输入原文输出候选片段/句子特征打分、图排序、序列标注文本压缩缩短已选内容保留核心意思输入候选片段输出更短的表达句法剪枝、删除修饰语、同义替换智能摘要最终生成一段凝练且完整的概述输入原文输出摘要文本抽取式、生成式、两者混合简单说抽取管“选什么”压缩管“怎么省”摘要是二者叠加之后的产物。我见过很多教程把三类混着讲导致初学者以为TextRank就是摘要的全部忘记了压缩这一步。实际上只做抽取不做压缩摘要往往又长又碎只做压缩不做抽取压完的内容又缺少重点。1.3 混为一谈的代价把抽取和压缩混为一谈最直接的代价是调参方向会错。我见过不少项目做ROUGE评估时只算整篇摘要的分数却把抽取阶段的错误归因到压缩阶段。比如摘要漏掉关键结论明明是抽取阶段没选到那句话却去调压缩参数结果越调越糟。反过来也有抽取阶段选了很长的句子压缩不足导致摘要超标也会被误判成抽取失效。正确的做法是先分别评估两个阶段抽取阶段看候选句的召回率压缩阶段看压缩率和语义保持率。这样分开看之后问题在哪个环节基本一目了然。2. 抽取式摘要是怎么把“重要”从文本里捞出来的2.1 最朴素的打分公式词频、位置与线索词组合抽取式摘要里最经典的方法就是给每个句子算一个得分然后按分数取Top N。最简单也最实用的打分公式是三要素组合句子得分 词频得分 位置权重 线索词权重词频得分可以理解成一句话里如果出现了很多原文里的高频实词那这句话多半承载了文章主旨。位置权重的意思是文章首段、每段首句、结尾段往往包含关键信息这些位置的句子天生就该多加分。线索词则是“综上所述”“重要的是”“实验结果表明”这类提示词它们往往跟在核心结论前面。我自己写过一个最简单的Python打分逻辑大概长这样def score_sentence(sentence, word_freq, position_ratio, cue_words): score 0.0 # 词频得分句中实词的频率之和 tokens [w for w in sentence.split() if w not in stop_words] for token in tokens: if token in word_freq: score word_freq[token] # 位置权重靠前的句子加分 score (1 - position_ratio) * 2.0 # 线索词权重 for cue in cue_words: if cue in sentence: score 1.5 return score这段代码没什么神奇之处但胜在稳定。词频统计加位置加权基本上就能把一篇文章的关键句捞个七七八八。线索词的权重可以按业务场景自己调比如做科技资讯摘要时“突破”“首次”“最新”这类词的权重可以给高一点。2.2 TextRank让句子之间互相投票词频打分有个天然缺陷它只看句子和词的关系没看句子和句子的关系。TextRank就是来解决这个问题的它的思想来自PageRank把每个句子当成一个节点两个句子越相似它们之间的“投票权重”就越高最后迭代收敛出每个句子的得分。句子相似度通常用词重合度或者词向量余弦相似度来计算。词重合度的形式非常简单sim 两句话共现词数 / 两句话平均词数不用引入什么大模型跑起来也快。TextRank的好处是它能区分“重要的句子”和“包含重要词的句子”。举个例子一篇文章反复出现“收益率”这个词但真正讲清楚“收益率怎么算”的句子可能只有一句TextRank通过句间投票能让这一句在图中获得更高权重。实际调参时有两个参数我很在意一个是迭代次数一般50到100次就够另一个是阻尼系数通常取0.85和PageRank一致。参数太小句子得分会偏向局部参数太大收敛变慢但结果更平滑具体得看语料情况。2.3 MMR去重别让前三句都在讲同一件事只按分数从高到低选句子会经常翻车分数最高的几个句子可能是同一段里的“孪生句”讲的全是同一个例子。那摘要就会变得特别单调。这时候就要引入MMR最大边际相关。MMR的公式理解起来也不难我直接用大白话解释它希望选出来的句子一方面和文档主题相关另一方面和“已经选中的句子”尽量不重复。两者之间用候选句与已选句的相似度做惩罚这就是“最大边际相关”里“边际”两个字的由来。selected [] for _ in range(top_n): scores [] for sent in candidates: rel sentence_doc_similarity(sent, doc_vector) redundancy max(sentence_similarity(sent, s) for s in selected) if selected else 0 scores.append(rel - lam * redundancy) best candidates.pop(index_of_max(scores)) selected.append(best)这里的lam是个权衡参数。lam取0的时候不退重取1的时候重度去重。我在实践中通常选0.3到0.5之间既能保证内容相关又能避免摘要变成同一段话的复读。这个技巧对新闻类语料特别有效因为新闻里的导语、正文、总结经常重复同一个结论。3. 文本压缩的三种落地手段删句子、剪短语、换说法3.1 句子级删除是压缩的第一道闸门文本压缩最粗粒度的手段就是直接删除句子。放到摘要场景里抽取阶段其实已经干了一部分“删句子”的活——从几十个句子里挑出五六个本质上就是句子级压缩。如果抽取出来之后发现摘要还是太长可以在候选句内部再做一次次级筛选。我习惯给句子加一个“信息密度”指标如果一个句子里的实词数量多、停用词占比低那它的信息密度就高更值得保留。反过来那种满是连接词、感叹词、空话的句子即使被抽取出来了也应该在压缩阶段干掉。3.2 短语级剪枝站在语法树上动刀句子级删除之后压缩粒度再细一层就是短语级剪枝。它的核心工具是依存句法分析把句子解析成一棵语法树主谓宾是树的主干定语、状语、插入语是枝叶压缩就是站在树上修剪枝叶。举一个我试过的例子原文是“那项在去年年底启动的、由多个研究小组共同参与的技术评估项目最终如期完成了全部测试环节”。这句话主干只有“项目完成了测试”但被大量修饰语包裹。通过依存句法剪掉“在去年年底启动的”“由多个研究小组共同参与的”“技术评估”这些定语/修饰成分后就变成“项目如期完成了全部测试环节”长度几乎减半核心意思还在。剪枝时要注意哪些成分能删哪些不能删。我整理过一个简单的经验表可删除成分例子不能轻易删除的成分例子时间状语“在2019年”“昨天”否定词“没有”“不是”地点状语“在上海”“在实验室”限定性定语“唯一的”“最关键的”插入语“众所周知”“据称”核心主语/宾语句子的主干冗余修饰“非常”“极其”数字与单位“50%”“3公斤”3.3 词汇级改写同义替换与代词还原再细一层的压缩是在词汇级别做动作。同义替换是其中的常见手段把“迅速降低”替换成“骤降”把“进行了一个认真的分析”改写成“认真分析”字数自然就下来了。代词还原则是把“该项目”“这种方案”这类指代替换成前面出现过的实体名称或类别词让摘要即使脱离原文也能读懂。写到这里我必须提醒词汇级改写的风险是最高的。同义替换容易改变语义强度“有效缓解”和“解决”之间差着十万八千里代词还原一旦找错先行词摘要就出现事实错误。所以我在实际项目里词汇级改写通常只用在人工后编辑阶段或者在规则非常明确的情况下才自动化比如“本公司”统一替换成公司简称。4. 从抽取到生成摘要模型为什么绕不开“改写”这一步4.1 抽取式的天花板拼出来的摘要缺乏连贯性既然抽取加压缩已经能产出摘要为什么业界现在几乎都在搞生成式摘要原因很简单——抽取式的输出是原文句子的拼接天然缺乏连贯性。句子之间没有逻辑连接词读起来像在跳读指代关系也可能悬空上一句说“该项目”下一句已经跳到另一个主题。我测试过一段用抽取式方法生成的摘要内容上没错但可读性很差“报告指出问题。采用新方法。效果显著。”三个句子之间没有因果关系读者还得自己脑补。这就是抽取式的天花板。生成式摘要用全新的句子来复述原文就绕开了这个限制这也是它成为主流方向的原因。4.2 Encoder-Decoder结构在摘要里做了什么生成式摘要的模型基础是Encoder-Decoder结构它的逻辑可以理解成两个步骤编码器把原文读成一个语义向量或一组向量表示解码器再从这个表示中逐个词生成摘要。训练阶段模型输入是“原文-参考摘要”这样的配对数据目标只有一个让生成出来的摘要和参考摘要尽量接近。这个“接近”通常用交叉熵损失来衡量。推理阶段模型不再有标准答案而是一个词一个词地自己预测直到生成出结束标记。模型选型上现在的做法基本都是拿预训练语言模型做微调而不是从头训练。预训练模型已经掌握了语言的一般规律微调只需要让它适应“摘要”这个任务。概括类模型在这件事上做得尤其好因为它们的预训练目标本身就是“把一段话遮住然后生成”这类接近摘要的任务。4.3 生成式压缩的本质理解约束下的改写生成式摘要看起来和“压缩”没关系因为它根本不是对原文字面上的删减而是“重新写”。但如果深看一步会发现它本质上还是在做压缩模型在解码的时候核心动作是放弃不重要的词、合并重复的信息、用更短的表达替换冗长描述。这个过程的本质就是“在理解约束下做改写式压缩”。很多刚入门的朋友容易陷入一种误区生成式摘要一定会比抽取式好。实际上模型生成的内容可能出现原文里根本不存在的错误推理而抽取式至少每一个字都能溯源。我在工程落地时倾向于把两者结合用抽取式锁定重要句子再利用生成式模型对句子做压缩改写这样既有可溯源性又有连贯性。5. 评估与落地ROUGE分数不是唯一答案5.1 ROUGE是怎么算的它算不到什么评估摘要质量业内最常用的自动指标是ROUGE核心思想是看“生成摘要”和“参考摘要”之间的词或短语重叠度。ROUGE-1比较的是单元词的重叠ROUGE-2比较的是相邻词对的重叠ROUGE-L则基于最长公共子序列衡量的是整体语序的接近程度。指标比较粒度能反映什么典型短板ROUGE-1单个词关键词是否覆盖同义词替换后会虚低ROUGE-2相邻词对短语级表达是否接近参考摘要少时噪声大ROUGE-L最长公共子序列语序与结构接近度不关心语法正确性我必须强调一个反直觉的点ROUGE对摘要里“可能的表述方式”非常不敏感。两个摘要语义完全一致但一个用了“大幅增长”另一个用了“明显上涨”ROUGE-1可能直接掉好几个点。所以我从来不把ROUGE当成绝对质量指标而是把它当成“回归测试指标”——每次改模型或改参数之后跑一遍ROUGE如果分数掉了大概率哪里出了问题。5.2 一套可复现的两步走落地流程结合前面说的内容我落地摘要功能时通常采用“先抽取后压缩”的两段式流水线。核心原因是抽取能保证摘要内容都源于原文压缩能保证摘要表达精炼两者各司其职出问题也好定位。具体的流程大概是这样的文本预处理分句、分词、去停用词。候选句抽取用TextRank或MMR选出与文档主题最相关的6到10个句子。句子压缩对候选句做依存句法剪枝去掉冗余修饰成分。句子排序按原文顺序重新排列保持叙事逻辑。生成式改写可选把压缩后的句子交给生成模型统一改写成流畅的段落。我实测过这个流程跑在新闻语料上ROUGE-L能在0.35到0.40之间浮动速度和纯生成式相比还快不少。作为一个基础版本的流水线这个效果已经足够上线试运行。5.3 长文档场景分块与层次压缩长文档的摘要和短文本是两种难度。短文本压缩容易保持全局一致但长文档有几千上万字直接整篇丢给抽取模型或生成模型速度慢而且容易漂移。我的做法是分层处理先把长文档按语义切块切块粒度通常控制在三四百字左右相当于一个自然段或两三段合并然后对每一块单独做摘要得到“块级摘要”最后把块级摘要拼起来再做一个二次摘要得到最终的整篇摘要。分层处理有个额外的好处中间层的块级摘要天然形成了一套“结果可回溯”的中间产物。用户想知道某条摘要结论出自文档哪里直接定位到对应块即可。这个特性在调研报告、法律文书这类场景里非常实用。6. 这几个坑我实测下来最值得注意6.1 生成式摘要的“复读机”现象生成式摘要最容易让人崩溃的问题就是复读机同一个句子在摘要里连续出现好几遍或者说来说去都在重复同一条信息。这背后有解码策略的原因——模型在beam search里对结尾标记的概率估计偏低或者某个片段概率异常高导致它反复围绕同一段话生成。我处理复读机问题的经验很简单先打开解码参数里的重复惩罚项设置一个合理的重复惩罚系数如果还有问题就考虑在候选生成结果里做规则去重检测连续重复片段并删除。工程上后者往往比调模型参数更快见效。6.2 压缩改写带来事实扭曲这是我认为最隐秘也最危险的问题。文本压缩在简化表达的时候很容易把限定条件丢掉。举个例子“该方法在特定条件下将错误率降低了30%”这句话压缩后如果变成“该方法将错误率降低了30%”语义就从“特定条件下有效”变成了“普遍有效”这是事实层级的错误。应对办法有两层。第一层是解析层面的压缩规则中要保留否定词、数量词和程度限定词第二层是检查层面的做完压缩或生成之后用实体抽取工具对比原文和摘要的实体及数字凡是文章里出现的新增实体或数值都要重点核查。这套“实体一致性检查”我每次上线前都会跑一遍。6.3 摘要长度的动态控制摘要长度怎么定很多人直接设个固定字数上限比如“最多200字”。但固定上限有个问题源文本身很短时摘要会为了凑字数加入废话源文很长时200字又装不下核心信息。更合理的做法是让摘要长度随源文长度动态变化例如取源文字数的四分之一到三分之一并设置上下限。我常用的参数组合是下限100字上限350字目标比例0.25。这个组合在新闻和报告语料下效果都不错。要注意上限的目的是控制成本、保证阅读体验不是衡量摘要好坏的标尺所以不用卡得特别死。6.4 把抽取和生成串成两段式pipeline的经验最后聊聊我最推荐的做法两段式pipeline。第一步用抽取式方法把候选句子范围缩小第二步用生成式模型对候选句做改写压缩。相比纯端到端生成这种方案有两个明显优势第一抽取阶段已经保证了关键信息在候选集内生成阶段就算随机发挥也很难把主题彻底带偏第二整个流程可以插桩调试某一步效果不对可以单独替换。我自己在长文档摘要场景里会在这个pipeline后面再加一道“标题增强”的步骤把原文标题当作第一句候选内容喂给压缩模型因为标题本身就是最强的一句话摘要。实测下来光是这一步就能让ROUGE-L提高一到两个点算是性价比极高的技巧。我在实际使用中还有一个体会摘要系统的阈值参数比如抽取句数、MMR的lambda、重复惩罚系数一定要按语料去调别指望一组参数通吃所有场景。新闻要突出新鲜信息论文要保留方法和结论财经要保留数字趋势。每一次调整都要拿真实badcase来回测而不是只看ROUGE涨没涨。调了半年摘要之后我的感受是智能摘要的技术难点从来不在模型选得够不够新而在于你有没有把“抽取”和“压缩”这两个环节真正分开想清楚。分清楚了系统就稳了一大半。