深度学习文本摘要生成实战:BART微调与ROUGE评估全解析
简介一套基于深度学习的文本摘要自动生成完整项目方案面向本科毕业设计或NLP入门实践者。项目以Transformer编码器-解码器架构为核心完整覆盖文本预处理、模型构建、训练、评估与测试全流程并包含beam search解码、ROUGE/BLEU指标评测等关键环节能够帮助读者系统掌握自动摘要生成的核心技术。资源包共34个文件体积仅360KB内部以18个Python脚本和5个Shell脚本为主体配合配置文件、词表、Dockerfile、依赖清单等便于快速搭建可复现的实验环境也支持本地与Docker两种运行方式。目前已有3700余人学习下载代码结构清晰尤其适合希望动手实践Transformer模型的本科生通过运行训练与测试脚本读者可完整走通从原始文本到生成摘要的流程并借助评测指标持续优化模型效果为后续NLP研究或毕业设计答辩打下扎实基础。1. 基于深度学习的文本摘要自动生成毕业设计真能做出来但坑比你想的多文本摘要这个方向很多初学者以为就是“提取关键词拼一段话”但真正用深度学习做生成式摘要后会发现它本质上是“理解原文后重新组织语言”的过程和你用 Word 的自动摘要完全是两回事。这套基于深度学习与自然语言处理的本科毕业设计资源核心是用 Transformer 架构实现从新闻长文到短摘要的自动生成包含数据处理、模型训练、推理和评估的完整闭环。适合选了 NLP 方向但还没定题、或者定了文本摘要但不知道从哪下手的本科生。我拆完这套资源后最直观的感受是跑通一个 BART 摘要模型不难难的是把数据集清洗干净、把解码参数调对、把指标看懂这几件事做完论文的核心工作量也就立住了。2. 摘要任务先定性抽取式还是生成式决定你后面一半的工作量2.1 抽取式和生成式的本质区别与选型判断文本摘要任务分成两大类。抽取式是从原文里挑出重要句子拼在一起本质是“排序截取”生成式是模型重新写出一段话虽然信息来自原文但措辞是新的。本科毕设选哪个方向直接影响你的硬件需求、模型复杂度和论文工作量。表格对比一下维度抽取式Extractive生成式Abstractive实现难度低TextRank 即可高需要 Transformer 类模型训练资源CPU 可跑需要 GPU哪怕是入门级摘要流畅度一般句子拼接感强接近人写表达更自然本科毕设适配度偏简单容易被质疑工作量适中深度学习特征明显评估指标ROUGE-1/2容易偏高ROUGE-1/2/L 均需关注这套资源走的是生成式路线模型用 Transformer 家族。我个人建议如果你的毕设导师比较看重“深度学习”这个标签就老老实实做生成式纯抽取式的 TextRank 写进论文里只能算前置实验不能作为主模型。但反过来如果你的显卡只有显存 4GB 以下生成式会很痛苦要么用蒸馏后的小模型要么把最大长度压得很短后面我会讲怎么处理。2.2 数据集预处理摘要任务真正消耗时间的地方很多人拿到数据集就直接 train 了这是最大的错误。摘要任务的数据集处理比分类任务讲究得多核心有三件事清洗、长度过滤、划分。清洗阶段要去掉 HTML 标签、乱码、连续空格还要处理数据里的特殊字符比如全角引号。常见做法是用正则表达式统一清洗。长度过滤是为了防止训练时 Padding 太长浪费显存也防止过短的样本让模型学到无意义映射。我一般会设置原文长度在 100 到 800 词之间摘要长度在 10 到 128 词之间超出范围的样本直接丢弃或用 3 倍下采样。下面是这套资源里给的数据预处理脚本核心逻辑语言是 Pythonimport re import json def clean_text(text): # 去掉 HTML 标签摘要数据里最常见的污染源 text re.sub(r[^], , text) # 将多个连续空白符压缩为单个空格 text re.sub(r\s, , text) # 统一全角逗号和句号为半角 text text.replace(, ,).replace(。, .) return text.strip() def filter_by_length(src, tgt, max_src800, min_src100, max_tgt128, min_tgt10): # 按词数过滤这里用空格分词中文场景建议用 jieba 提前切好 src_len len(src.split()) tgt_len len(tgt.split()) if src_len min_src or src_len max_src: return False if tgt_len min_tgt or tgt_len max_tgt: return False return True def build_dataset(raw_path, out_path): with open(raw_path, r, encodingutf-8) as f: lines f.readlines() samples [] for line in lines: data json.loads(line) src clean_text(data[document]) tgt clean_text(data[summary]) if filter_by_length(src, tgt): samples.append({src: src, tgt: tgt}) with open(out_path, w, encodingutf-8) as f: for s in samples: f.write(json.dumps(s, ensure_asciiFalse) \n) print(fraw{len(lines)}, kept{len(samples)}, ratio{len(samples)/len(lines):.2f})这段代码的逻辑顺序是先清洗再过滤最后写入文件。清洗必须在过滤之前否则带标签的文本长度统计会失真。filter_by_length 里的阈值不是拍脑袋定的BART 和 T5 这类模型的最大输入长度通常是 512 到 1024 token如果原文超过 800 词实际切分后能用的 token 也会超过模型上限就算勉强塞进去注意力计算也会变得很稀疏。另外注意这里用 split() 按空格分词只适用于英文如果是中文数据集建议先按字符级处理或者用 jieba 分词后再过滤否则长度统计会偏差很大。3. 抽取式 Baseline 先跑通TextRank 实现与 ROUGE 评估的对应关系3.1 用 TextRank 做基线模型不是为了用是为了“陪跑”毕设论文里需要对比实验生成式模型再先进也需要一个基线来衬托。TextRank 是抽取式里最经典的算法理论上不需要训练无监督就能跑非常适合当基线。这套资源里包含 TextRank 的完整实现我拆完代码后发现它用了标准的图排序思路句子是节点句子间的相似度是边权重然后迭代计算句子权重最后取 Top-K 句拼成摘要。关键代码结构如下import numpy as np from sklearn.metrics.pairwise import cosine_similarity import networkx as nx def textrank_summarize(sentences, top_k3): # 句子向量化这里用 TF-IDF简单且不需要额外模型 vectorizer TfidfVectorizer(stop_wordsenglish) X vectorizer.fit_transform(sentences) # 计算句子间余弦相似度矩阵作为图的邻接矩阵 sim_mat cosine_similarity(X) np.fill_diagonal(sim_mat, 0) # 构建无向加权图并运行 PageRank 迭代 graph nx.from_numpy_array(sim_mat) scores nx.pagerank(graph, alpha0.85) # 按得分排序取前 top_k 个句子并按原始顺序输出 ranked sorted(((scores[i], i) for i in range(len(sentences))), reverseTrue) selected sorted([idx for _, idx in ranked[:top_k]]) return .join(sentences[i] for i in selected)这里有个容易被忽略的细节选出来的句子要按原始顺序重新排序否则摘要读起来是乱的。很多入门实现直接按得分降序输出结果摘要第一句是原文的第五句第二句是原文的第二句语句完全不通。我建议你在论文里明确写清楚这一步这属于抽取式摘要的经典实现细节能体现你对任务的理解。参数方面alpha0.85 是 PageRank 的阻尼系数一般不用改。top_k 是摘要句子数量要根据原文长度和目标摘要字数调整常见做法是原文句子数的 30% 左右或者直接固定为 3 到 5 句。3.2 ROUGE 指标的计算方式和阅读方法做摘要方向的毕设ROUGE 是绕不开的评估指标。ROUGE-1 比较单字/单词重叠ROUGE-2 比较相邻两个词的重叠ROUGE-L 比较最长公共子序列。这套资源里给了 ROUGE 的计算脚本代码不算长核心是精确率、召回率和 F1 的权衡问题。from rouge_score import rouge_scorer scorer rouge_scorer.RougeScorer([rouge1, rouge2, rougeL], use_stemmerTrue) def evaluate_summary(pred, ref): scores scorer.score(ref, pred) print(fROUGE-1 F1: {scores[rouge1].fmeasure:.4f}) print(fROUGE-2 F1: {scores[rouge2].fmeasure:.4f}) print(fROUGE-L F1: {scores[rougeL].fmeasure:.4f}) return scores注意参数顺序scorer.score 的第一个参数是参考摘要 ref第二个是生成摘要 pred很多人传反结果指标完全无意义。ROUGE 的召回率表示参考摘要里有多少内容被生成摘要覆盖了精确率表示生成摘要里的内容有多少来自参考摘要F1 是两者的调和平均。写论文时一般报告 F1但审稿老师如果仔细看你还需要解释为什么只报 F1 而不分析精确率和召回率的差异。这里有一个很现实的注意点ROUGE 是基于词面重叠计算的如果模型把“苹果”改写成了“水果”哪怕语义完全正确ROUGE 分数也很低。这导致生成式模型的 ROUGE 分数往往比抽取式模型低这是正常的。你的论文讨论部分必须解释这个现象否则答辩老师会质疑“你的深度学习模型怎么还没传统算法分数高”。这个资源里点到了这一点我觉得很有价值。4. 生成式主模型BART 的微调训练与推理参数详解4.1 为什么选 BART 而不是 T5 或 GPT这套资源的主模型是 BART不是 T5。原因很直接BART 是专门为生成任务设计的去噪自编码模型预训练时就做了文本损坏恢复这和摘要任务高度同构——摘要本质上就是把“长文压缩成短文”的文本变换任务。而 T5 是 Text-to-Text 架构虽然也适合摘要但同等规模下 BART 在 ROUGE 指标上通常略优。GPT 类模型做摘要需要 Prompt 工程对本科毕设来说不稳定因素太多不推荐。BART 的训练方式是在源文本上做 Denoising 预训练比如 Token 掩码、句子顺序打乱、文本删除等。微调时我们把原文作为输入序列把目标摘要作为输出序列训练目标是最小化生成摘要和参考摘要之间的交叉熵损失。资源里用的模型权重是 BART-base也就是 6 层编码器加 6 层解码器参数量约 1.4 亿单卡 8GB 显存勉强够用。4.2 训练脚本核心参数与数据加载方式训练阶段最核心的类有两个一个是数据集类另一个是训练循环。下面这段代码是资源里的数据加载部分我把关键参数做了注释from torch.utils.data import Dataset from transformers import BartTokenizer, BartForConditionalGeneration class SummarizationDataset(Dataset): def __init__(self, data_path, tokenizer, max_src_len512, max_tgt_len128): self.samples [json.loads(line) for line in open(data_path, encodingutf-8)] self.tokenizer tokenizer self.max_src_len max_src_len self.max_tgt_len max_tgt_len def __len__(self): return len(self.samples) def __getitem__(self, idx): sample self.samples[idx] # 原文编码截断到 max_src_len这里不用 padding交给 collator 统一处理 src_ids self.tokenizer( sample[src], max_lengthself.max_src_len, truncationTrue, return_tensorspt ).input_ids.squeeze(0) # 目标摘要编码同样截断到 max_tgt_len tgt_ids self.tokenizer( sample[tgt], max_lengthself.max_tgt_len, truncationTrue, return_tensorspt ).input_ids.squeeze(0) return {input_ids: src_ids, labels: tgt_ids}这里有一个重要的参数细节max_src_len 和 max_tgt_len 分别限制原文和摘要的长度。如果你显存有限把 max_src_len 从 512 降到 256训练速度会快很多但摘要质量会下降因为模型看不到文章后半部分的信息。资源里的默认设置是 512 和 128硬件条件好的话可以保持如果显存吃紧建议优先缩减训练 batch size 而不是收缩长度。训练循环中需要注意 labels 的处理。Hugging Face 的 Trainer 会自动将 labels 中的 -100 位置忽略掉但手动写训练循环时你需要把 padding token 的 id 替换为 -100否则模型会学习预测填充符降低训练质量。下面是资源里的处理方式def collate_fn(batch): input_ids [item[input_ids] for item in batch] labels [item[labels] for item in batch] # padding 时 input_ids 和 labels 都填充到 batch 内最大长度 input_ids_padded torch.nn.utils.rnn.pad_sequence(input_ids, batch_firstTrue, padding_valuetokenizer.pad_token_id) labels_padded torch.nn.utils.rnn.pad_sequence(labels, batch_firstTrue, padding_value-100) return {input_ids: input_ids_padded, labels: labels_padded}pad_sequence 默认按 batch 内最长样本补零但这里 padding_value 必须区分开输入侧用 tokenizer 的 pad_token_id标签侧用 -100。如果两边都用 0 补损失函数会计算填充位置的概率模型会学偏。这个细节我当年调了两天才发现属于典型的“看不出来但影响巨大”的坑。4.3 推理阶段beam search 与 length penalty 的调参经验推理阶段是生成式摘要的另一个调参重灾区。资源里给的生成函数使用了 beam search核心参数有三个num_beams、length_penalty、early_stopping。下面这段代码可以直接用作测试脚本from transformers import BartTokenizer, BartForConditionalGeneration model BartForConditionalGeneration.from_pretrained(./bart-summarization-checkpoint) tokenizer BartTokenizer.from_pretrained(./bart-summarization-checkpoint) def generate_summary(text, model, tokenizer): inputs tokenizer( text, max_length512, truncationTrue, return_tensorspt ) summary_ids model.generate( inputs.input_ids, num_beams4, length_penalty2.0, max_length128, min_length20, early_stoppingTrue, no_repeat_ngram_size3 ) return tokenizer.decode(summary_ids[0], skip_special_tokensTrue)num_beams 控制搜索宽度4 是性价比最高的选择继续增加到 8 会显著减慢推理速度但 ROUGE 提升很小。length_penalty2.0 的意思是鼓励生成长一点的摘要如果设为 1.0 或更低模型容易输出很短的内容尤其在没有 min_length 约束时会更明显。no_repeat_ngram_size3 是防止生成重复短语的约束这个参数对摘要质量影响很大不加的话模型在长摘要里经常会陷入重复循环比如反复输出“某某表示某某表示”。我建议你测试时至少对比 length_penalty 在 1.0 到 2.5 之间的表现差异论文里可以放一个小的调参表格这是很加分的工作量。推理阶段还有个常被忽略的问题beam search 输出的摘要可能存在反复重复的 n-gram加上 no_repeat_ngram_size 可以解决。但另一个现象更隐蔽——模型在解码初期容易出现“模型还没看完原文就开始输出”的问题这属于注意力分布异常通常和训练数据中摘要与原文长度比有关一般是加长训练轮次或调整学习率来缓解不是推理阶段能解决的。5. 实战避坑指南训练摘要模型最容易踩的六个常见问题5.1 ROUGE 分数一直不涨先检查数据清洗和长度映射现象训练了多个 epoch验证集 ROUGE-2 分数始终在 10 以下而且不随时间波动。原因大概率是原始数据里 HTML 标签没有被清除或者原文和摘要的对应关系错位模型等于在学习噪声。解决用本章第二节的清洗逻辑重新处理数据并且随机抽 50 条样本人工检查原文是否与摘要匹配。我自己拆的这套资源里数据清洗脚本里有一个细节处理把摘要中的换行符统一替换成空格防止模型学到换行位置作为截断信号。5.2 模型输出大量重复片段现象生成的摘要出现连续重复词比如“该研究该研究该研究”或者一个句子重复三次。原因解码时没有加 no_repeat_ngram_size再加上 beam search 的早期解码错误会不断自我强化。解决在 generate 参数中加上 no_repeat_ngram_size3如果仍然重复把 beam 宽度从 4 减到 3减少搜索分支的累积误差。5.3 显存溢出CUDA out of memory现象训练刚开始就报显存不足。原因max_src_len 过长batch size 太大或者模型用的是 BART-large。解决优先把 batch size 从 8 降到 4如果还不行就把 max_src_len 从 512 降到 256。注意降 max_src_len 会改变训练数据的分布需要重新统计样本长度不能盲目全局截断。另外检查是否开启了梯度累积batch size 小的场景建议用 gradient_accumulation_steps2 来模拟更大的有效批量。5.4 用验证集调参导致指标虚高现象验证集 ROUGE 很好但测试集表现明显下降。原因反复用验证集做早停和调参验证集信息已经泄漏到模型选择过程中。解决把数据集划分成训练、验证、测试三份验证集只用于早停所有最终指标在测试集上计算一次。资源里的数据划分比例是 8:1:1并且用了固定随机种子这样重复实验时结果可复现论文里可以写清楚。5.5 BLEU 还是 ROUGE评估指标没选对现象论文里用了 BLEU 指标但摘要场景下 BLEU 不能反映内容覆盖度。原因BLEU 是机器翻译指标强调译文和参考译文的 n-gram 精确匹配对摘要任务来说它会惩罚那些表述不同但语义正确的摘要。解决摘要任务统一用 ROUGE 家族指标主报告 ROUGE-1、ROUGE-2、ROUGE-L 的 F1 值。如果你想增加说服力可以额外报告人工评估的流畅度和忠实度打分这个比任何自动指标都有说服力。5.6 中文数据集的 Token 切分问题现象中文摘要训练时ROUGE 分数很低但看生成的摘要好像还行。原因ROUGE 默认按空格分词中文如果不分词所有中文文本会被当成一个超长 token指标计算失去意义。解决中文摘要数据要先用 jieba 分词再用空格连接后训练和评估。资源里这段处理写在数据预处理脚本里如果你自己处理中文数据务必在清洗后加这一步。提示上面这些坑基本都是摘要任务特有的分类或序列标注任务里不会遇到。如果你在训练过程中遇到了以上没有覆盖的问题优先检查数据管道不要急着调模型结构。6. 写出能进论文的摘要评估方案ROUGE 之外的验证技巧模型训练完不能只贴一个 ROUGE 表格就完事。答辩老师大概率会问“你生成的摘要质量到底怎么样”如果你只拿出 ROUGE 分数很难让老师信服。我建议在论文里加入人工评估方案这是资源里也特别强调的一步。人工评估通常从三个维度打分信息忠实度摘要内容是否在原文中有依据满分 5 分、流畅度读起来是否通顺满分 5 分、信息覆盖度原文核心信息是否都出现在摘要中满分 5 分。找三个同学或者同组成员每人抽 50 条生成摘要独立打分然后计算平均分和一致性。具体做法是从测试集随机抽 50 条原文用模型生成摘要再把原文、参考摘要、模型摘要三样放在一起让评估者给模型摘要打分。如果有多个人打分计算一下肯德尔和谐系数来验证评价一致性这个在论文的方法部分写出来非常有说服力。另外还有一个值得在论文中提的实验设计解码参数敏感性分析。固定模型权重只改变 num_beams 和 length_penalty给出一个 3x3 的 ROUGE 分数网格。比如 num_beams 取 1等价于贪心搜索、4、8length_penalty 取 1.0、2.0、2.5统计每个组合在测试集上的 ROUGE-L 分数。你会发现贪心搜索的 ROUGE 通常比 beam search 低不少这就是你在讨论部分解释 beam search 有效性的直接证据。这个实验成本很低跑一次推理的时间就能完成但工作量看起来非常扎实。我还养成了一个习惯现在每次跑完摘要模型都会额外做一个“去停用词再算 ROUGE”的验证把生成摘要和参考摘要里的停用词删掉再算 ROUGE。这个指标滤掉了虚词的干扰更能反映信息层面的覆盖情况虽然不能作为正式指标写进论文但自己判断模型质量时很有效。如果去停用词后 ROUGE 明显提升说明模型学到了关键信息但措辞偏冗余反过来如果提升不大说明模型的内容覆盖确实有问题。从那以后我每次做完摘要实验都强制走一遍“清洗数据统计抽样、跑抽取式基线、微调 BART、beam search 调参、ROUGE 加人工评估”这条完整流程缺一步都会在答辩时暴露问题。这套资源把整条链路串起来了对本科毕设来说足够撑起工作量。希望帮到你。本文还有配套的精品资源点击获取