ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

GPT-2中文预训练模型实战:从数据清洗到文本生成微调全流程

GPT-2中文预训练模型实战:从数据清洗到文本生成微调全流程 简介这套压缩包提供面向中文白话文生成任务的GPT2中文预训练模型基于Transformer架构在ClueCorpusSmall大规模中文语料上预训练专门适配新闻、评论、日常对话等白话文场景适合需要快速生成文本的自然语言处理开发者和研究者。压缩包共11个文件包括7个json配置与参数文件、1个txt说明文档、safetensors与bin两种格式的模型权重文件以及1个main入口脚本各配置对应模型结构、分词器及训练超参数权重文件可直接被常见深度学习框架加载整体体积约362.34MB。已有191人学习下载属于轻量实用的中文预训练模型资源。下载后可获得完整可用的模型权重、配置和调用脚本无需自行预训练即可应用于文本生成、自动摘要、问答对话、文本校对等下游任务也可作为迁移学习基础在领域数据上继续微调显著降低中文文本生成项目的入门与部署成本。 最近在折腾中文文本生成绕了一圈发现最顺手的一个组合还是哈工大讯飞联合实验室放出来的 gpt2-chinese-cluecorpussmall。这是一个典型的中文预训练模型基座是 GPT-2训练语料是 CLUECorpusSmall专门用来做中文白话文文章生成。给它一句开头它能接着往下写几百字通顺的中文用在故事续写、文案草稿、新闻摘要扩写这类场景里特别省事。这个项目实际做的事情很聚焦把模型怎么下载、数据怎么清洗、怎么在自有语料上继续训练、推理时怎么调参数完整跑通一遍。适合刚入门文本生成的同学也适合那些已经在用 BERT 做分类任务、想横向扩展到生成方向的同学。1. 项目拆解为什么要做中文白话文生成1.1 白话文生成到底解决什么问题很多工业场景里需求其实不是“造一个对话机器人”而是“给定一个主题词或开头句自动生成一段可读的中文段落”。比如电商要批量生成商品描述新媒体要快速产出文章开头客服要自动拟写回复草稿。传统模板填充写出来的东西生硬、重复稍微换个语境就废掉而基于预训练模型做自回归生成句式和用词要自然得多。GPT-2 是自回归语言模型它做的是“给定前文预测下一个词”这个结构和人类写作过程天然一致。对比 LSTM 时代GPT-2 用了 Transformer 的因果注意力每个位置只能看到自己和之前的 token训练时可以并行计算生成时再逐个往后推。好处是长程依赖抓得比 RNN 稳RNN 在序列超过 100 之后几乎记不住前文信息而 GPT-2 在 512 token 范围内表现依然可用这就给中文长句和段落生成提供了基础保障。1.2 为什么是 GPT-2与 BERT、RoBERTa、ResNet 预训练模型的定位差异预训练模型这个词最近出现频率很高经常有人把 BERT、RoBERTa、GPT-2、ResNet 放在一起聊但其实它们解决的问题完全不同。BERT 和 RoBERTa 是双向编码器训练目标通常是完形填空MLM它们做文本分类、序列标注、语义匹配很在行但输出的是语义表示而不是文本不能直接用来续写文章。RoBERTa 中文预训练模型在 CLUE 榜单上效果很好它是“阅读理解型”选手拿来做生成任务还得挂解码器改造成本不低。ResNet 预训练模型又完全是另一条路线它是图像领域的卷积网络靠残差结构解决深层网络退化问题权重和文本特征不通用。虽然“预训练模型”这个词在迁移学习里被泛化了但不要指望 ResNet 权重能直接延伸到文本生成上。所以选 GPT-2核心原因就是它是纯解码器架构训练目标和生成目标一致拿来做白话文文章生成不需要额外改造。而 gpt2-chinese-cluecorpussmall 这个模型已经在 CLUECorpusSmall 上做过中文预训练模型学会了中文词法、句法和一定的事实知识我们再微调时只需要少量领域数据就能收敛比自己从零训练省下大量时间和算力。2. 环境准备与预训练模型下载2.1 硬件和依赖要求跑这个小体量模型不需要太夸张的机器。推理场景下8GB 显存已经很宽裕纯 CPU 也能跑但速度会慢到让人怀疑人生。微调场景建议至少 8GB 显存如果显存不够可以通过减小 batch size 和梯度累积来缓解。软件环境方面推荐 Python 3.8 以上PyTorch 1.10 以上transformers 4.20 以上。安装命令非常简单pip install torch transformers tokenizers datasets accelerate这里有个细节需要注意如果你的机器是 NVIDIA 显卡务必先确认 PyTorch 版本和本地 CUDA 版本匹配否则torch.cuda.is_available()会一直返回 False训练时悄悄跑在 CPU 上速度差几十倍。检查方式import torch print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else CPU)2.2 模型下载与文件结构这个模型的 Hugging Face 路径是uer/gpt2-chinese-cluecorpussmall在代码里直接指定模型名就能下载。如果你不方便联网也可以手动下载后放到本地目录目录里应该有config.json、pytorch_model.bin、vocab.txt、tokenizer_config.json这几个关键文件。加载代码from transformers import AutoTokenizer, AutoModelForCausalLM model_name uer/gpt2-chinese-cluecorpussmall tokenizer AutoTokenizer.from_pretrained(model_name) tokenizer.eos_token tokenizer.sep_token model AutoModelForCausalLM.from_pretrained(model_name)这里有个非常重要的点这个模型用的 tokenizer 是 BERT 风格的 WordPiece不是 GPT-2 原版 BPE。所以序列里会出现[PAD]、[UNK]、[SEP]这类特殊 token。实际生成时如果不把eos_token设置好模型可能不会自主输出结束符导致生成停不下来。常见做法是把[SEP]作为结束符也就是上面那行tokenizer.eos_token tokenizer.sep_token这个细节能省掉你后面大量麻烦。3. 数据准备与训练细节3.1 CLUECorpusSmall 数据清洗与样本构造CLUECorpusSmall 是中文开源语料里比较好上手的原始数据通常是 json 格式每条记录包含title和content字段覆盖百科、新闻、互动问答等类型的书面语文本。虽然叫 Small实际体量也有十几 GB足够让模型学到丰富的中文表达。拿到原始语料后第一件事是清洗。我习惯按这几步处理去掉 HTML 标签统一换行符全角英文数字转半角过滤掉长度小于 20 个字符的短文本以及明显乱码的行。中文文本不需要手动分词交给 tokenizer 处理就行但标点要保留尤其是句号和逗号它们是模型学习停顿和断句的重要信号。构造训练样本时中文文本要切成模型能处理的最大长度。这个模型的n_positions是 512也就是最多支持 512 个 token超过的部分会被截断。我推荐用 sliding window 的方式窗口大小 512步长 256相邻样本有 256 token 的重叠这样既避免文章被硬生生切断也能让模型在样本边界处有足够上下文。每个样本末尾接上[SEP]作为句子结束信号。清洗和切分的核心代码大概长这样import json from transformers import AutoTokenizer tokenizer AutoTokenizer.from_pretrained(uer/gpt2-chinese-cluecorpussmall) tokenizer.eos_token tokenizer.sep_token def clean_text(text): text text.replace(br, \n).replace(p, ).replace(/p, ) text text.strip() return text def split_text(text, tokenizer, max_len512, stride256): tokens tokenizer.encode(text, add_special_tokensFalse) samples [] for start in range(0, len(tokens), max_len - stride): end start max_len sample tokens[start:end] if len(sample) 50: continue samples.append(sample [tokenizer.sep_token_id]) return samples with open(cluecorpussmall.json, r, encodingutf-8) as f: lines f.readlines() all_samples [] for line in lines: data json.loads(line) content clean_text(data.get(content, )) if len(content) 50: continue all_samples.extend(split_text(content, tokenizer))这里没有做严格去重如果读者有精力可以先用 MinHash 跑一遍近似去重能明显减少模型复读现象。3.2 训练脚本关键代码训练逻辑不算复杂核心是构造 DataLoader、计算语言模型损失、更新参数。用AutoModelForCausalLM加载模型后把labels设置为输入序列本身模型内部会自动把 logits 左移一位对每个位置计算下一个 token 的交叉熵损失不需要手工构造 decoder 输入。一个简化但可运行的训练循环如下import torch from torch.utils.data import Dataset, DataLoader from transformers import AdamW, get_linear_schedule_with_warmup class TextGenDataset(Dataset): def __init__(self, samples): self.samples samples def __len__(self): return len(self.samples) def __getitem__(self, i): return torch.tensor(self.samples[i], dtypetorch.long) def collate_fn(batch): max_len max(len(x) for x in batch) padded torch.full((len(batch), max_len), tokenizer.pad_token_id, dtypetorch.long) for i, x in enumerate(batch): padded[i, :len(x)] x return padded dataset TextGenDataset(all_samples) dataloader DataLoader(dataset, batch_size8, shuffleTrue, collate_fncollate_fn) total_steps len(dataloader) * 3 optimizer AdamW(model.parameters(), lr5e-5) scheduler get_linear_schedule_with_warmup(optimizer, num_warmup_stepsint(total_steps * 0.1), num_training_stepstotal_steps) device torch.device(cuda if torch.cuda.is_available() else cpu) model.to(device) for epoch in range(3): for step, input_ids in enumerate(dataloader): input_ids input_ids.to(device) attention_mask (input_ids ! tokenizer.pad_token_id).long() outputs model(input_idsinput_ids, attention_maskattention_mask, labelsinput_ids) loss outputs.loss loss.backward() optimizer.step() scheduler.step() optimizer.zero_grad() if step % 100 0: print(fepoch {epoch} step {step} loss {loss.item():.4f}) torch.save(model.state_dict(), fgpt2_zh_epoch_{epoch}.bin)关于collate_fn这里用tokenizer.pad_token_id填充注意这个模型的 pad token id 默认可能也是 0和[PAD]对应。有些版本里tokenizer.pad_token是 None建议加载后手动检查没有就设置为tokenizer.eos_token。3.3 超参数调节心得微调 GPT-2 这类模型超参数直接决定生成效果但也不需要太多花活。以下几个参数是我实际跑下来的经验值参数推荐范围说明max_len128-512文本较短用 128长段落生成用 512batch_size4-16根据显存调整显存不够就减半learning_rate2e-5 到 5e-5学习率太大 loss 会震荡epochs3-5数据量大可以少跑几轮warmup_steps总步数 10%帮助模型稳定收敛如果发现 loss 下降很慢可以优先把学习率降到 2e-5而不是改模型结构。数据量只有几万条时epochs 甚至可以只设 2避免过拟合后生成内容变成复读机。4. 文本生成推理与效果优化4.1 快速生成一段中文训练完或直接加载预训练模型推理代码很短model.eval() prompt 人工智能在未来的发展 inputs tokenizer.encode(prompt, return_tensorspt).to(device) with torch.no_grad(): output_ids model.generate( inputs, max_new_tokens200, do_sampleTrue, temperature0.9, top_p0.9, repetition_penalty1.2, pad_token_idtokenizer.pad_token_id, eos_token_idtokenizer.eos_token_id, ) print(tokenizer.decode(output_ids[0], skip_special_tokensTrue))max_new_tokens是生成的最大长度200 相当于中文白话文的一小段太长容易开始跑题。do_sampleTrue表示使用随机采样如果设成 False模型会走贪心解码每一句都选概率最高的词结果非常容易陷入重复循环。4.2 生成参数这么调效果才能看生成参数有点像炒菜的火候每个参数影响的不只是多样性还有语义连贯性。推荐用下面这个表当参照参数推荐范围作用与注意点temperature0.7-0.95越低越保守越高越跳跃中文生成建议 0.9 附近top_p0.8-0.95保留累计概率达 p 的候选词过滤掉尾巴上的奇怪词top_k20-40只在概率最高的 k 个词里采样防止低概率词捣乱repetition_penalty1.1-1.3对重复出现的词做惩罚太低没用太高会让句子前言不搭后语举个例子同样的 prompttemperature 设成 0.5输出会比较规矩但略显死板设到 1.2句子可能开始出现语法错误。我在实际使用中会先固定 top_p0.9再调 repetition_penalty如果模型总是复读某个词就把惩罚从 1.1 提到 1.25基本能压住。4.3 应用场景与局限别把模型想得太全能这个模型的优势是书面语风格稳定适合做公众号文章开头扩写、电商文案生成、故事续写、新闻标题改写。它生成的句子语法自然而且能覆盖很多常见话题直接作为初稿完全没问题。局限也很明显模型上下文窗口只有 512 token超过这个长度后它根本不记得前面在讲什么长文会前后矛盾其次它没有真正的事实推理能力生成的内容可能看似正确其实是编的需要人工审核第三模型学的是通用书面语如果要做垂直领域的口播稿或对话文本必须先在对应语料上微调否则风格会很飘。5. 常见问题与排查技巧5.1 训练阶段问题速查实际跑训练时大部分人卡住的点其实都差不多现象可能原因解决办法CUDA OOMbatch_size 或 max_len 太大减小 batch_size或开 gradient_accumulationloss 不下降学习率太高 / 数据噪声太多学习率降到 2e-5检查清洗逻辑模型加载报错transformers 版本不一致升级或降级到 4.20 以上GPU 利用率低DataLoader 没开 num_workers读取数据耗时太长适当增加并行生成内容出现 [PAD]pad_token_id 设置不对generate 时手动指定 pad_token_id里面最容易忽略的是tokenizer.pad_token为 None 的问题。加载 UER 这个模型后建议加一行代码if tokenizer.pad_token is None: tokenizer.pad_token tokenizer.eos_token5.2 针对中文生成的独门技巧中文生成和英文不太一样有几件事需要额外注意。一是标点前常常会多出空格。WordPiece tokenizer 对中文按字切分但某些 token 自带空格前缀解码后可能变成“人工 智能 在 未来”需要后处理把空格去掉。可以用简单的正则re.sub(r\s, , text)但要注意英文单词中间的空格也会被去掉如果文本里有英文最好先按句子判断再处理。二是数据清洗不要过度。有些人觉得标点没用就全删掉结果模型学不会断句生成一大段不带逗号的文字。中文标点是句法的一部分保留逗号、句号、引号训练时才能学到正确的停顿位置。三是 prompt 风格决定了生成风格。如果你想生成口语化白话文prompt 就用“我跟你说”“最近我发现”这种开头模型会顺着风格往下走如果你给定一个非常学术化的开头生成结果也会跟着书面语起来。5.3 卡在原地时先看这三个坑兜底说一下我坑过的三个地方。第一个是 PyTorch 和 CUDA 版本不匹配导致 GPU 完全不可用训练隐形变慢。第二个是dataset忘记开 shuffle模型每个 epoch 看到的都是相同顺序loss 震荡剧烈。第三个是保存模型时只存了state_dict结果加载时忘记先实例化模型报 key 不匹配的错。更稳妥的方式是直接存全量模型model.save_pretrained(./my_gpt2_zh) tokenizer.save_pretrained(./my_gpt2_zh)之后加载就用AutoModelForCausalLM.from_pretrained(./my_gpt2_zh)省心很多。结尾我个人在实际操作中的体会是gpt2-chinese-cluecorpussmall 特别适合做中文白话文生成的起步模型。它的训练语料覆盖面广微调难度低参数量在一亿左右单卡就能跑起来。如果你手里有几十万条领域文本在这个模型上继续训练半天效果就能超过很多从零训的模型。后续还可以往两个方向扩展一是换更大的 GPT-2 large 版本二是加 LoRA 做参数高效微调在效果和成本之间找平衡。最关键的还是先把基础链路跑通数据清洗、训练、生成、调参这几步走顺了后面换再大的模型也只是一个参数迁移问题。本文还有配套的精品资源点击获取
返回列表