BERT抽取式摘要实战:从论文代码到工程落地

发布时间:2026/11/2 11:28:48
BERT抽取式摘要实战:从论文代码到工程落地 简介这份资源是面向自然语言处理学习者与开发者的BERT抽取式摘要实战代码包基于BertSum-master项目帮助读者理解如何将预训练BERT微调应用于文本摘要生成任务。包内共36个文件以20个Python脚本为主涵盖数据预处理、模型定义、训练与评估等核心流程另含7个txt映射文件、2个json配置与样例数据、1个README说明及LICENSE等压缩包约14.99MB目录按数据、模型、脚本分模块组织便于按流程阅读。已有1525人学习下载适合具备一定深度学习基础、希望动手复现论文摘要模型的读者。通过梳理代码可掌握Token IDs与Mask构造、编码器-解码器结构搭建、交叉熵损失与Adam优化、ROUGE评估及生成结果后处理等关键环节是理解BERT在序列到序列任务中落地方式的实践参考。1. 从一份论文代码说起BERT 抽取式摘要到底怎么落地很多人第一次接触抽取式摘要是在论文里看到一串漂亮的 ROUGE 分数然后兴冲冲去找开源实现结果打开仓库发现只有几个.py文件和一堆没写清楚的超参跑起来不是显存爆掉就是数据格式对不上。这份 Python 微调 BERT 做摘要提取的论文代码解决的正是这个断层它把「用预训练 BERT 做句子级二分类、挑出原文中最该保留的句子拼成摘要」这条链路完整写了出来包含数据预处理、模型封装、训练循环和推理打分。适合两类人一是想复现论文结果、搞清楚抽取式摘要内部机制的学生和算法初学者二是手里有一批长文档、想快速搭一个能用的摘要基线再往上叠业务逻辑的开发者。下面按「能干什么 → 怎么跑 → 怎么改」的顺序拆。2. 项目结构与技术选型为什么是 BERT 句子分类2.1 整体架构把摘要问题降维成逐句打分抽取式摘要和生成式摘要最大的区别是它不造新句子只从原文里挑句子。这个项目采用的就是最经典也最稳的做法——把文档切成句子每个句子前面拼上[CLS]让 BERT 输出一个二分类概率表示「这句该不该进摘要」。训练时用人工摘要和原文句子的重叠度构造标签推理时对所有句子打分、排序、按长度预算截断。整条链路可以拆成四层层次职责对应模块数据层文档分句、构造样本、生成标签数据预处理脚本模型层BERT 编码 分类头模型封装类训练层损失计算、反向传播、评估训练主循环推理层逐句打分、选句、拼摘要推理脚本这种分层的好处是每一层都能单独替换。比如你后面想换 RoBERTa只动模型层想换标签构造策略只动数据层。常见做法是把配置抽到一个config字典或argparse里避免超参散落在各个文件。2.2 目录结构一个能跑的最小工程长什么样论文代码往往不追求工程规范但至少要能让人找到入口。按这类项目的典型组织目录大致如下bert-summarization/ ├── data/ │ ├── raw/ # 原始文档与参考摘要 │ └── processed/ # 分句、编码后的训练样本 ├── src/ │ ├── preprocess.py # 分句、标签构造、tokenize │ ├── dataset.py # Dataset / DataLoader 封装 │ ├── model.py # BERT 分类头 │ ├── train.py # 训练与验证循环 │ └── predict.py # 推理与摘要拼接 ├── configs/ │ └── base.json # 超参、路径配置 ├── requirements.txt └── README.mddata/raw放原始语料data/processed放预处理产物这样重跑预处理不会污染原始数据。configs/base.json集中管理max_seq_length、batch_size、learning_rate这些关键参数。我一般会再加一个outputs/目录存 checkpoint 和日志避免模型文件和数据混在一起。2.3 依赖与版本别在环境上翻车这类项目对transformers和torch的版本比较敏感尤其是BertTokenizer的do_lower_case行为和TrainerAPI 在不同版本间有差异。建议锁一个能跑通的组合pip install torch1.13.1 pip install transformers4.26.1 pip install scikit-learn nltk tqdmnltk用来做英文分句sent_tokenize中文场景要换成jieba或按标点切分。scikit-learn主要用来算评估指标。注意transformers4.26 之后部分旧接口有弃用警告但不影响运行如果直接装最新版Trainer的参数名可能对不上这是最常见的环境坑。3. 核心模块拆解从分句到摘要输出的完整链路3.1 数据预处理标签怎么造才合理抽取式摘要的训练标签不是现成的得从「参考摘要」反推。标准做法是对原文每个句子计算它和参考摘要的 ROUGE 或词重叠度超过阈值标 1否则标 0。这样模型学的是「哪些句子更像人工会选的句子」。import re from nltk.tokenize import sent_tokenize def split_sentences(text): # 先按段落切再段内分句避免跨段错误合并 paragraphs [p.strip() for p in text.split(\n) if p.strip()] sentences [] for para in paragraphs: sentences.extend(sent_tokenize(para)) return sentences def build_labels(sentences, reference_summary, threshold0.3): ref_tokens set(re.findall(r\w, reference_summary.lower())) labels [] for sent in sentences: sent_tokens set(re.findall(r\w, sent.lower())) if not sent_tokens: labels.append(0) continue overlap len(sent_tokens ref_tokens) / len(sent_tokens) labels.append(1 if overlap threshold else 0) return labelssplit_sentences先按段落再分句是为了防止sent_tokenize把跨段内容错误拼接。build_labels里的threshold是关键参数设太高会导致正样本太少、模型全预测 0设太低则正样本泛滥、摘要冗余。经验值在 0.2 到 0.4 之间具体看参考摘要长度。overlap用句子自身词数做分母而不是参考摘要词数这样长句不会被系统性压低分数。3.2 模型封装BERT 加一个二分类头模型部分很直接BertModel取[CLS]位置的输出接一个Linear(hidden_size, 2)。关键是forward里要把attention_mask传对否则 padding 位置会污染[CLS]表示。import torch import torch.nn as nn from transformers import BertModel class BertSentenceClassifier(nn.Module): def __init__(self, model_namebert-base-uncased, num_labels2, dropout0.1): super().__init__() self.bert BertModel.from_pretrained(model_name) hidden self.bert.config.hidden_size self.dropout nn.Dropout(dropout) self.classifier nn.Linear(hidden, num_labels) def forward(self, input_ids, attention_mask, token_type_idsNone): outputs self.bert( input_idsinput_ids, attention_maskattention_mask, token_type_idstoken_type_ids, ) cls_output outputs.last_hidden_state[:, 0, :] # [CLS] 向量 logits self.classifier(self.dropout(cls_output)) return logitsmodel_name可以换成任意 HuggingFace 上的 BERT 变体中文场景换成bert-base-chinese。dropout放在分类头前是常规操作防止过拟合如果数据量很小可以调到 0.2 到 0.3。token_type_ids在单句分类里其实用不上但保留参数是为了兼容某些 tokenizer 的输出传None时 BERT 内部会自己处理。3.3 训练循环损失、评估与早停训练用标准的交叉熵但要注意类别不平衡——大多数句子是负样本。常见做法是给正样本加权或者用pos_weight传给BCEWithLogitsLoss。这里用CrossEntropyLoss配合weight参数。from torch.optim import AdamW from transformers import get_linear_schedule_with_warmup def train_one_epoch(model, dataloader, optimizer, scheduler, device, pos_weight2.0): model.train() total_loss 0.0 # 正样本加权缓解类别不平衡 weight torch.tensor([1.0, pos_weight], devicedevice) criterion nn.CrossEntropyLoss(weightweight) for batch in dataloader: input_ids batch[input_ids].to(device) attention_mask batch[attention_mask].to(device) labels batch[labels].to(device) optimizer.zero_grad() logits model(input_ids, attention_mask) loss criterion(logits, labels) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() scheduler.step() total_loss loss.item() return total_loss / len(dataloader)pos_weight控制正样本的相对权重设 2.0 意味着正样本的损失贡献是负样本的两倍适合正样本占比 20% 到 30% 的情况。clip_grad_norm_的max_norm1.0是防梯度爆炸的保险BERT 微调时几乎必加。scheduler用线性 warmupwarmup 比例一般设总步数的 10%学习率2e-5到5e-5之间。3.4 推理与摘要拼接打分之后怎么选句推理阶段对每个句子单独打分然后按概率排序在长度预算内依次选入。这里有个细节选句时要尽量保持原文顺序否则摘要读起来跳跃。def summarize(model, tokenizer, document, max_length512, budget3, devicecuda): sentences split_sentences(document) model.eval() scored [] with torch.no_grad(): for idx, sent in enumerate(sentences): enc tokenizer(sent, truncationTrue, max_lengthmax_length, paddingmax_length, return_tensorspt) logits model(enc[input_ids].to(device), enc[attention_mask].to(device)) prob torch.softmax(logits, dim-1)[0, 1].item() scored.append((idx, prob, sent)) # 按概率降序选前 budget 句再按原序排列 top sorted(scored, keylambda x: x[1], reverseTrue)[:budget] top_sorted sorted(top, keylambda x: x[0]) return .join([s for _, _, s in top_sorted])budget是摘要句数上限按业务需求定一般取原文句数的 20% 到 30%。max_length512是单句编码上限超长句会被截断所以预处理时最好先把超长句再切一刀。torch.softmax取第 1 维概率作为「该句入选」的置信度。按原序重排这一步很多人会漏导致摘要逻辑断裂属于血泪经验。4. 跑起来环境、数据与训练命令4.1 数据准备格式对齐是第一步项目期望的原始数据通常是「一篇文档一个文件参考摘要一个文件」或者一个 JSON 列表。常见格式如下[ { document: 第一段内容。第二段内容。, summary: 参考摘要句子。 } ]预处理脚本读入后对每篇文档分句、构造标签再用 tokenizer 编码成input_ids、attention_mask、labels存成.pt或直接喂给Dataset。注意document里不要混入 HTML 标签或多余空白否则分句结果会很脏。4.2 训练命令与关键参数训练入口一般长这样python src/train.py \ --train_file data/processed/train.json \ --val_file data/processed/val.json \ --model_name bert-base-uncased \ --batch_size 8 \ --epochs 3 \ --lr 3e-5 \ --max_seq_length 512 \ --output_dir outputs/batch_size8是 8GB 显存下的保守值显存够可以上 16。epochs3是 BERT 微调的常见区间再多容易过拟合。lr3e-5在2e-5到5e-5之间都合理。max_seq_length要和预处理时一致否则 padding 逻辑对不上。训练日志里重点看验证集的 F1而不是准确率——负样本多的时候准确率虚高。4.3 推理验证先看输出再调参训练完用predict.py对几篇验证集文档生成摘要人工看一眼。如果摘要全是短句或者全是首句说明标签构造或pos_weight有问题。建议先在小样本20 到 50 篇上跑通全流程再上全量数据这样排查成本低。5. 避坑与排查这几处最容易翻车5.1 显存溢出不是 batch 太大就是序列太长现象是训练一开始就CUDA out of memory。原因通常是max_seq_length512加上batch_size16超过了显存。解决先把batch_size降到 4 或 8再考虑用梯度累积模拟大 batch。如果单句仍然超长预处理阶段就把超过 512 token 的句子截断或再切分。5.2 模型全预测 0标签阈值和权重没调好现象是验证集上模型对所有句子输出概率都接近 0摘要为空。原因是正样本太少或pos_weight没设。解决统计训练集正样本比例如果低于 15%把threshold降到 0.2同时把pos_weight提到 3.0 到 5.0观察正样本召回是否回升。5.3 分句错误导致标签错位现象是摘要里出现半截句子或跨段拼接。原因是sent_tokenize对缩写、小数点处理不好。解决预处理时先做简单清洗去掉多余换行、统一标点中文场景直接换jieba或按。切分别硬套英文分句器。5.4 推理结果顺序混乱现象是摘要句子读起来跳跃。原因是按概率排序后直接拼接没恢复原文顺序。解决选完 top-k 后按句子原始索引重排这一步在 3.4 的代码里已经体现但很多人自己改写时会漏掉。5.5 版本不兼容导致 Trainer 报错现象是TypeError: __init__() got an unexpected keyword argument。原因是transformers版本和代码里的Trainer参数不匹配。解决按requirements.txt锁版本或者干脆不用Trainer自己写训练循环3.3 的做法可控性更强。6. 二次开发换模型、换标签、接业务想在这个基线上继续做有三个方向最值得动。第一是换编码器把BertModel换成RobertaModel或中文的bert-base-chinese只改model.py里一行tokenizer 同步换掉即可注意hidden_size从配置里读别写死 768。第二是改标签策略现在的词重叠阈值比较粗糙可以换成 ROUGE-L 计算句子与参考摘要的匹配度或者引入位置特征首句、末句加权把标签质量提上去模型上限会明显不同。第三是接业务把summarize函数包成一个 HTTP 接口输入文档、输出摘要前面加一层长度判断——短文档直接返回原文长文档才走模型这样能省掉大量无谓推理。改造后的验证方法我一般固定三步先用 20 篇人工标注样本算 ROUGE-1 和 ROUGE-L和基线对比再抽 10 篇看摘要可读性重点看有没有断句和重复最后压测一下单篇推理耗时确认在业务可接受范围内。表格对比可以这样记改造点改动文件验证指标换 RoBERTamodel.pyROUGE-L 是否提升换 ROUGE 标签preprocess.py正样本比例、F1加长度短路predict.py平均耗时下降有个习惯我保持了挺久每次改标签构造逻辑都先把新旧标签在同一批样本上的正负比例打出来对比比例偏差超过 10% 就先不训练回去查分句和阈值。因为标签一错后面调参全是白费功夫这个后悔药不好吃。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询