资讯详情

t5-pegasus中文生成模型:原理、微调与避坑指南

📅 2026/10/11 16:49:32 | 华诺云谱 👁 阅读
t5-pegasus中文生成模型:原理、微调与避坑指南
简介T5-PEGASUS是一款面向中文自然语言生成任务的开源预训练模型资源以mT5为基础架构和初始权重借鉴PEGASUS的预训练思路适用于文本摘要、标题生成等场景适合有一定深度学习基础、希望快速上手中文生成式模型的研究者或工程师。资源包共5个文件内含2个Python脚本train.py与finetune.py、2幅效果示意图CSL-LCSTS、few-shot以及1份README说明文档压缩包仅418KB结构精简便于快速阅读与二次开发。目前已有2591人学习下载。通过这份资源读者可直观了解模型将分词器改造为BERT风格并重排5万词表的具体实现同时对照论文思路理解“抽取若干句子构造伪摘要”的数据构建流程还能借助脚本快速开展训练与微调实验是入门中文生成式预训练的一条便捷路径。1. 中文生成式任务的默认起点t5-pegasus 是什么能替你省下几天微调时间中文生成式预训练模型这两年迭代很快可真要落地“给长文本生成摘要、给新闻生成标题、把口语改写成书面语”这类需求我第一时间想到的还是 t5-pegasus。它发布得早参数规模也不是最大但一直是最稳的中文生成底子之一。它预训练阶段用关键句遮蔽模型从第一天就在学“长文中哪些信息必须保留”下游微调对数据量很友好几千条标注样本也能把效果拉到接近可用。这篇文章围绕它在 Python 环境里的落地路径展开讲清选型理由、加载方式、微调要点和文档不会写的坑。适合正在做中文摘要、标题生成、文案改写的工程师也适合刚接触生成式 NLP、想快速拿到可用基线的从业者。2. 原理与选型T5 骨架、Pegasus 遮蔽和三个适用边界2.1 为什么中文生成式任务偏爱 encoder-decoder而不是单向 GPT先看两种架构的差别。GPT 这类纯解码器模型输入和输出拼在同一个序列里生成第 i 个 token 时模型能看到的只有提示词和已经生成的 i-1 个 token源文本里的信息只能依赖 prompt 的弱约束被“记住”。对于摘要这种“输入很长、输出很短”的任务长文本里的时间、数字、否定关系、专有名词散落在不同位置单靠注意力在单向解码器内部传递很容易丢信息。encoder-decoder 结构则不同encoder 负责把整个源文本编码成一串携带全局信息的向量decoder 生成时通过跨注意力层回看 encoder 的所有输出位置。相当于给解码器装了一根“回看源文”的缆绳每一步生成都能随时核对原文。t5-pegasus 的 encoder 是完整双向注意力源文本每个位置能看到左右全部上下文decoder 是自回归结构从左到右逐个生成 token。这套组合天然契合“长输入、短输出”的中文生成任务。我选型时习惯先跑一个通用 baseline再看任务要不要换纯解码器模型。同一个摘要数据集上同样数据量encoder-decoder 的收敛速度和输出忠实度通常都优于直接用 GPT 风格模型做。不是 GPT 做不到而是它需要更多数据、更长 prompts 或者外部检索来弥补“无法主动回头核对原文”的结构短板。这里有一个很实际的选型对比表可以直接用在技术方案评审里。对比维度纯解码器GPT 风格encoder-decodert5-pegasus源文本利用方式拼入同一序列靠 prompt 弱约束encoder 全量编码decoder 跨注意力回看长输入处理输入长度被总长度限制挤压源文与输出长度分开设置灵活性高生成忠实度容易遗漏细粒度信息更稳专有名词和数字更容易保留中文摘要/标题任务数据充足时可用少量数据也能出效果我并不是说纯解码器模型不行而是说在不追求“对话外挂”这类纯生成场景时t5-pegasus 这种结构更省事。生成式对话另说那是单解码器的主场。2.2 Pegasus 遮蔽与 gap sentence generation预训练阶段就在学“抓重点”T5 本身是一个通用文本到文本框架任何任务都能被改写成“输入一段文本输出一段文本”。Pegasus 则是一种预训练策略核心思路是 gap sentence generation把文档里的重要句子整个删除留出几个空洞让模型根据剩下内容把被删掉的句子补全。这里的关键在“重要句子”怎么选。Pegasus 预训练时不是随机删句而是用类似 ROUGE 相关度的指标对每个句子打分选出那些和全文主题最相关的句子作为“被挖掉的空”。这种策略等于把摘要任务的核心能力直接放进了预训练目标模型在预训练阶段就反复练习“读长篇、判断哪些句子的信息密度最高、生成与原句语义一致但是重新组织过的文本”。所以下游做摘要时它不是从零学怎么抓重点而是学怎么把抓重点的能力迁移到目标领域。t5-pegasus 把这套策略用在了中文语料上。中文预训练语料的处理比英文麻烦英文天然按空格分词中文要先断句还要处理全角半角标点混用。开源版本在中文清洗语料上继续预训练对新闻、百科、社交文本里的“重点感”明显强于直接用英文模型翻译。我遇到的一个典型例子是同一篇产品公告直接跑英文摘要模型再翻译经常把“降价幅度”这种关键信息丢掉而 t5-pegasus 会保留。当然它也不是万能。遇到方言、口语化极强的客服对话预训练阶段的“重点感”可能失效微调数据就要多备一些。2.3 什么时候不该用它长文本、数字事实、强格式输出的三个边界第一输入长度超过模型窗口的文本会被截断。t5-pegasus 在处理输入时有一个默认长度上限超出部分静默丢弃。这不是模型 bug而是结构约束。如果你想摘要一篇 2000 字的产品说明模型实际看到的可能只有前面一部分结论段落在末尾时自然被漏掉。处理方式后面会讲但选型时要清楚它不解决“超长文本全局摘要”问题。第二金融公告、实验记录这类对数字要求极高的场景要谨慎。生成模型追求语义通顺对“3.87 亿”和“5.89 亿”这种数字的敏感度并不高容易写错。如果你有真实数据做后置校验或者接受“摘要仅供参考”那可以上如果要求逐字精确最好在模型输出后加一层数字抽校。第三输出有强格式约束的任务它也不擅长。比如要直接输出 JSON 字段、固定顺序的名单、或者带编号的模板模型输出的结构很可能不稳定。常见做法是把模型当“内容生成器”生成自然语言后再用规则代码解析成目标格式而不是强行让模型一步到位。与人相关的分类任务也更建议用判别式模型。3. 用 Python 把 t5-pegasus 跑起来加载、微调、生成全流程3.1 环境、权重与分词器先把“加载”这一步走通依赖安装这块尤其要盯住 Transformers 框架的版本。这个模型权重出来得早早期版本与最新 Transformers 在键名映射上有过兼容问题。我一般会锁定一个相对稳定的版本区间避免装最新版导致权重加载报错。下面这组依赖是一个可以跑通的组合。pip install transformers4.40.0 torch2.2.0 datasets rouge-score装完后先验证加载。model_path 可以是一个本地模型目录也可以直接填复现时使用的权重名称from transformers import AutoTokenizer, AutoModelForSeq2SeqLM model_path t5-pegasus # 本地目录或权重名 tokenizer AutoTokenizer.from_pretrained(model_path) model AutoModelForSeq2SeqLM.from_pretrained(model_path) model.eval()加载逻辑其实很直接AutoTokenizer 读取模型目录里的词表文件负责中文切词和 token 到 id 的转换AutoModelForSeq2SeqLM 按配置文件搭建网络结构并载入权重。模型加载完成后调用 eval()把 dropout 等训练行为关掉推理结果才稳定。如果你在离线环境工作需要提前把模型文件下载到本地包括 config.json、权重文件、分词器文件。整个目录拷到离线机器后把 model_path 换成目录路径即可。加载时如果出现“Some weights of the model checkpoint were not used”一类的提示不用慌先看具体缺的是哪几个键很多是预训练任务头没用的权重不是模型损坏。真正要担心的是 missing keys 里出现 encoder、decoder 的主干层。3.2 微调数据的组织源文-摘要成对样本与 Dataset 封装微调 t5-pegasus 的数据格式是“源文-目标文”成对样本。最省事的存储方式是 JSONL每行一条记录包含 src 和 tgt 两个字段。# train.jsonl {src: 原文文本……, tgt: 对应摘要……} {src: 原文文本……, tgt: 对应摘要……}数据清洗这一步很多人会跳过但它直接影响微调效果。我一般会做三件事HTML 标签剥离、空白合并、标点统一。中文模型对全角半角标点混排很敏感训练语料里如果一会儿中文引号一会儿英文引号生成结果就会学着混着用。import re def clean_text(text): text re.sub(r[^], , text) # 去掉 HTML 标签 text re.sub(r\s, , text).strip() # 合并连续空白 text text.replace(\u201c, ).replace(\u201d, ) return text逻辑说明\u201c 和 \u201d 是中文左右引号的 Unicode 编码统一替换成英文引号避免生成结果里引号配对混乱。这并不会丢失中文语义但能让标点更干净。如果你做的是标题生成目标文本通常要短还可以顺手过滤掉长度小于 5 个字的样本避免模型学出“空摘要”。数据封装我习惯写一个 torch Dataset把 tokenizer 的转换逻辑放在getitem里这样训练循环不用关心数据预处理细节。import json import torch from torch.utils.data import Dataset class PairDataset(Dataset): def __init__(self, path, tokenizer, max_src_len512, max_tgt_len128): self.samples [] self.tokenizer tokenizer self.max_src_len max_src_len self.max_tgt_len max_tgt_len with open(path, r, encodingutf-8) as f: for line in f: item json.loads(line.strip()) self.samples.append((item[src], item[tgt])) def __len__(self): return len(self.samples) def __getitem__(self, idx): src, tgt self.samples[idx] enc self.tokenizer( src, max_lengthself.max_src_len, truncationTrue, paddingmax_length, return_tensorspt ) dec self.tokenizer( tgt, max_lengthself.max_tgt_len, truncationTrue, paddingmax_length, return_tensorspt ) return { input_ids: enc[input_ids].squeeze(0), attention_mask: enc[attention_mask].squeeze(0), labels: dec[input_ids].squeeze(0) }这里有两个参数值得多说一句。paddingmax_length 会把每条样本都垫到固定长度换来的是训练张量形状整齐DataLoader 不用额外写 collate 逻辑代价是少数短文本会浪费显存。如果训练显存吃紧可以改成不 padding再单独写一个 collate_fn 在 batch 内动态垫齐。max_src_len 我建议压在 512 以内这既是模型预训练窗口硬上限也是显存开销的主要来源。max_tgt_len 看任务摘要给 128标题生成给 32 就够。3.3 训练细节批次、学习率、梯度累积与检查点恢复训练循环看起来很长其实核心逻辑只有几步取一个 batch算交叉熵损失反向传播梯度裁剪更新参数。T5 类模型在接收 labels 时loss 已经在模型内部算好了 teacher forcing 交叉熵不需要自己把 logits 拉出来再做 softmax。from torch.utils.data import DataLoader device torch.device(cuda if torch.cuda.is_available() else cpu) model.to(device) dataset PairDataset(train.jsonl, tokenizer) loader DataLoader(dataset, batch_size4, shuffleTrue) optimizer torch.optim.AdamW(model.parameters(), lr2e-5) grad_accum 8 steps 0 for epoch in range(3): model.train() total_loss 0.0 for batch in loader: input_ids batch[input_ids].to(device) attention_mask batch[attention_mask].to(device) labels batch[labels].to(device) outputs model( input_idsinput_ids, attention_maskattention_mask, labelslabels ) loss outputs.loss / grad_accum loss.backward() steps 1 if steps % grad_accum 0: torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() optimizer.zero_grad() total_loss loss.item() * grad_accum avg_loss total_loss / len(loader) print(fepoch {epoch} loss {avg_loss:.4f})几个参数要注意学习率 2e-5 是微调场景里很稳的起点数据量少于一万条就不要再往大了调否则 loss 会在前两个 step 冲高然后震荡batch_size 4 加 grad_accum 8等效 batch size 32兼顾显存与收敛稳定性clip_grad_norm_ 的 1.0 是常用阈值生成模型在训练后期容易因为个别离群样本产生梯度爆炸加这一行能让 loss 曲线稳很多。训练完之后检查点保存用 save_pretrained把模型和分词器一起存到一个目录里model.save_pretrained(checkpoints/t5-pegasus-finetuned) tokenizer.save_pretrained(checkpoints/t5-pegasus-finetuned)保存时的目录名最好带 epoch 或时间戳。我习惯把“epoch2”和“epoch3”两个检查点都留一份因为生成任务里 loss 最低的检查点不一定生成质量最好留两个候选可以后续用 ROUGE 挑。断点续训也方便把 from_pretrained 的路径指向这个目录再继续跑训练循环就行学习率调度器如果之前没有保存状态重训时就从头开始影响不大。3.4 生成推理beam search 和习惯性后处理微调完成后生成推理的代码相对独立。我会把常用参数固定成一个函数避免每个脚本里参数不一致导致结果无法对比def summarize(text, max_src_len512, max_tgt_len128, num_beams4, repetition_penalty1.2): inputs tokenizer( text, max_lengthmax_src_len, truncationTrue, return_tensorspt ).to(device) with torch.no_grad(): out model.generate( input_idsinputs[input_ids], attention_maskinputs[attention_mask], max_new_tokensmax_tgt_len, num_beamsnum_beams, no_repeat_ngram_size3, repetition_penaltyrepetition_penalty, length_penalty1.0, early_stoppingTrue ) result tokenizer.decode(out[0], skip_special_tokensTrue) return result.replace( , )参数说明max_new_tokens 控制生成部分的最大长度和总长度参数不一样在 T5 类模型里用它更直观num_beams 越大搜索越充分但也要付出更多推理时间摘要任务 4 够用no_repeat_ngram_size3 是抑制重复的关键参数它禁止同一个 3-gram 在输出里连续出现两次以上repetition_penalty1.2 在 1.0 的基础上进一步压低重复 token 的概率。最后一步把空格去掉并不是偷懒中文分词器在 decode 时经常在字词间插入空格不清理会让人怀疑模型没训练好。推理时还要注意输入侧的截断。如果一篇文档长于 512 tokentokenizer 会截掉尾部而这个动作不会报错。想验证截断是否影响了摘要质量可以分别取文档前 300 字、中间 300 字、尾部 300 字做三个局部摘要拼起来作为长文档摘要的底稿。这种方式比单纯拉长 max_src_len 可靠因为模型的预训练窗口早就在 512 附近。4. 避坑清单跑 t5-pegasus 时最常见的五个翻车现场4.1 token 长度超限导致静默截断摘要内容缺尾现象输入一篇 2000 字文章生成的摘要读着通顺但漏掉了文本末尾最关键的建议或结论。如果你只看摘要根本发现不了模型漏读了内容。原因tokenizer 对超出 max_length 的输入默认截断而且是静默的不打印任何警告。模型以为自己读完全文了实际上只读了前面一小段。这个坑对长文本摘要任务几乎是必踩。解决训练和推理统一设置 max_src_len512 并显式开启 truncationTrue同时写一个统计脚本统计每个输入样本的 token 长度分布把那些超长的样本单独标记出来。我一般会在数据清洗阶段给样本加一个“是否超长”字段上线排查时先看是不是截断问题再决定要不要做分段摘要。4.2 Transformers 版本不匹配导致权重加载报错现象from_pretrained 加载时出现大量 missing keys 或 unexpected keys模型推理输出乱码甚至整段重复。原因Transformers 框架在不同版本里对 T5 权重键名的映射有过调整这份权重如果是在旧版本框架下保存的新版本可能不认某些键或者把键名对应到错误的层。解决加载之前先看模型目录里的 config.json确认它记录的模型结构再选择兼容的 Transformers 版本。用独立 conda 环境锁版本是最省心的办法别在系统级 Python 里混装。真遇到键名不匹配可以对比 config 和加载日志逐层确认哪些键是多余的预训练任务头哪些是主干层缺失。主干层缺失就得换版本。4.3 显存 OOM 但 batch size 已经很小优化器状态占了大头现象batch size 已经降到 2 甚至 1显存还是爆报 CUDA out of memory。很多人第一反应是继续降 batch size降到 1 仍然不够。原因生成式模型反向传播要存大量中间激活模型参数、梯度和优化器状态三项叠加显存占用比普通分类模型高一个量级。batch size 降下去之后固定开销仍然很大。解决优先开混合精度训练用 torch.cuda.amp 的 autocast 和 GradScaler显存能省下近一半。其次把 max_src_len 从 512 降到 384输入长度是显存最大变量。最后再考虑梯度累积它不省显存但能让小 batch 训练更稳。4.4 beam search 输出大段重复no_repeat_ngram 没设现象生成结果前两句正常第三句开始把前面某句话原样重复甚至循环。greedy 解码也可能遇到beam search 更容易出现。原因beam search 追求序列概率积最大一旦某个高概率片段被重复生成整个序列的累积概率反而更高。模型没有显式的“不能说重复话”约束就掉进重复循环。中文里重复单元常是两字词比英文整词重复更难发现。解决至少设置 no_repeat_ngram_size3这个参数的含义是不允许任意 3-gram 在输出中连续出现两次。如果还重复把 repetition_penalty 从 1.0 提到 1.3。要注意 penalty 太大会让生成文本变得支离破碎建议从 1.2 开始试。4.5 微调后中英文混排输出词表与语料不匹配现象模型微调前输出纯中文微调后隔几个字就蹦出一个英文单词或英文标点看起来像两种语言在抢输出。原因训练语料里混入了英文标点、URL、HTML entity 或半角字符模型在微调时把这些当成了正常中文文本的组成部分生成时自然学着混排。词表里也有英文 token模型一旦在某个位置激活了英文分支后续就容易被带偏。解决数据清洗阶段统一把所有标点转成中文标点数字保留阿拉伯数字但两侧加空格任务里用不到的英文单词直接过滤。另一个有效技巧是给目标文本统一加前缀“摘要”推理时也加同样的前缀相当于给模型一个稳定的启动信号混排概率会明显下降。5. 一个更进阶的调参技巧把生成结果做成可验证的文本5.1 用 ROUGE 快速验证每个 checkpoint微调到第三步不能只看训练 loss。我见过 loss 一路下降、生成质量却越来越差的案例这是因为模型在背训练集而不是学摘要。验证方法很简单固定 100 条验证集对每个 checkpoint 跑同一组生成参数算 ROUGE-1、ROUGE-2、ROUGE-L。from rouge_score import rouge_scorer scorer rouge_scorer.RougeScorer([rouge1, rouge2, rougeL], use_stemmerFalse) total {rouge1: 0.0, rouge2: 0.0, rougeL: 0.0} for ref, pred in zip(references, predictions): scores scorer.score(ref, pred) for key in total: total[key] scores[key].fmeasure for key in total: total[key] / len(references) print(f{key}: {total[key]:.4f})逻辑说明ROUGE-1 和 ROUGE-2 分别看一元组和二元组重合度ROUGE-L 看最长公共子序列对中文句子语序更敏感。中文场景记得设 use_stemmerFalse英文分词器的词干还原对中文没用。看分数的重点不是绝对值而是不同 checkpoint 之间的走势如果 ROUGE-L 在上升但 ROUGE-2 纹丝不动说明模型学会了大意但没学会用词搭配如果两个都掉那就要回查训练数据。5.2 生成参数组合经验表调参这件事我给不出一个万能公式但下面这组经验值能在大多数中文摘要场景里少踩坑。表格里的参数是起点不是终点效果不好就围绕它再挪。场景max_tgt_lennum_beamsno_repeat_ngram_sizerepetition_penaltylength_penalty新闻标题32431.21.0通知公告64321.00.8长文档摘要128531.30.8口语改写64431.21.0表格不是定律。比如长文档摘要场景把 num_beams 拉到 5配合 length_penalty0.8会让模型更愿意生成紧凑的句子这里 length_penalty 小于 1 的含义是对长序列略加惩罚避免摘要越写越长。通知公告场景结构固定beam 太宽反而容易让模型套用训练集里的句式所以 beam 降到 3 更稳。我踩过最狠的坑就是 4.1 那个静默截断。线上有一批摘要突然少了结论排查了大半天最后发现是某个特征开关把输入文本长度撑到了 700 多 token。从那以后我每次给 t5-pegasus 喂数据之前都强制走一遍“长度统计 标点清洗”脚本训练完再固定同一组生成参数跑一遍验证 ROUGE。前前后后不过半小时却能把大多数翻车概率压到最低。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑