ARTICLE DETAIL

资讯详情

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

GPT2-Chinese训练全流程:分词器选型、参数调优与避坑指南

GPT2-Chinese训练全流程:分词器选型、参数调优与避坑指南 简介该资源面向中文自然语言处理研究者与开发者提供一套基于Sentencepiece和Bert Tokenizer的GPT2-Chinese模型训练源码帮助快速搭建并训练中文语言模型降低从数据预处理到文本生成全流程的实现门槛。压缩包共42个文件约13.8MB以9个Python脚本为核心涵盖训练、单次训练、文本生成与模型评估等环节另含8个PNG与5个JPG图像、7个文本文件、5个JSON配置、3个Shell脚本以及分词模型、词表、许可证和说明文档分别承担流程展示、配置记录、命令简化与法律声明等作用。目前已有341人学习下载。读者可直接复用train、generate、eval等脚本结合BPE子词切分与BERT分词器完成语料编码并参考诗词、散文、武侠小说等示例数据理解中文语料组织方式按需修改配置扩展自己的训练任务获得一套结构完整、可运行的中文GPT2训练工具集。1. 拆开这个 GPT2-Chinese 训练包42 个文件里到底藏了什么如果你手头有一批中文语料想从零训一个能写诗、能续写散文的小模型大概率绕不开 GPT2-Chinese 这条路线。这个源码包就是干这件事的它把 GPT2 的中文训练流程拆成了可执行的脚本分词层同时给了 Sentencepiece 的 BPE 和 Bert Tokenizer 两套方案训练、评估、生成各管一段。42 个文件里9 个 Python 脚本是主干3 个 Shell 脚本负责把命令串起来剩下的 vocab、config、tokenizations 目录是配套的词典和分词器实现。适合谁适合已经跑通过 transformers 基础 demo、想进一步控制分词和训练细节的人。不适合连 Python 虚拟环境都没配过的新手因为脚本之间的路径依赖和编码问题会让人卡很久。下面按“先看清结构、再动手跑通、最后避坑”的顺序拆。2. 分词层选型Sentencepiece 与 Bert Tokenizer 的取舍2.1 两套分词器在文件层面的对应关系打开压缩包分词相关的文件分两组。一组是 Sentencepiece 路线vocab_guwen.txt、vocab_seg.txt、vocab_all.txt、vocab_small.txt、vocab.txt加上make_vocab.sh和make_vocab.py这套流程是先用原始语料训练一个 BPE 模型产出 vocab 文件再交给bpe_tokenizer.py加载。另一组是 Bert Tokenizer 路线tokenizations目录下的tokenization_bert.py、tokenization_bert_word_level.py、encoder.json、vocab.bpe这套直接复用 BERT 的中文词表按字或按词切分。两条路线的核心差异在词表覆盖和未登录词处理。Sentencepiece 的 BPE 是从你的语料里统计出来的子词单元对古诗词、特定领域文本的切分更贴合Bert Tokenizer 用的是通用中文词表泛化好但遇到生僻字或专业术语时可能切得碎。我一般会先看语料规模如果语料小于 50MB用 Bert Tokenizer 省事如果语料是古文、诗歌这类风格集中的文本走 Sentencepiece 自己训词表生成质量会稳一些。2.2 用 make_vocab.py 训练自己的 BPE 词表make_vocab.sh是个包装脚本真正干活的是make_vocab.py。常见做法是先把语料整理成一行一条的纯文本然后调用 Sentencepiece 的 trainer。下面这段是我按脚本逻辑还原的核心调用参数含义逐条说明。# make_vocab.py 核心逻辑还原 import sentencepiece as spm spm.SentencePieceTrainer.train( inputtrain.json, # 训练语料一行一条文本 model_prefixvocab_seg, # 输出前缀生成 vocab_seg.model 和 vocab_seg.vocab vocab_size30000, # 词表大小中文建议 20000-40000 character_coverage0.9995, # 字符覆盖率中文生僻字多别设 1.0 model_typebpe, # 用 BPE 算法 bos_id1, eos_id2, unk_id0, pad_id3, # 特殊 token 的 id 分配 user_defined_symbols[[SEP], [CLS]] # 预留符号和下游任务对齐 )vocab_size不是越大越好。设到 50000 以上词表稀疏训练时 embedding 层参数量暴涨小语料下反而容易过拟合。character_coverage设 0.9995 是中文场景的常见值留一点余量给极生僻字避免训练时因为某个字不在字符集里直接报错。user_defined_symbols要和后面训练脚本里的特殊 token 对齐否则加载模型时 id 对不上生成结果会乱。2.3 Bert Tokenizer 的加载方式与词表文件如果走 Bert 路线tokenization_bert.py里定义了BertTokenizer类加载的是vocab.txt和encoder.json。调用方式和 transformers 库里的 BertTokenizer 基本一致但这里是独立实现不依赖 transformers。from tokenization_bert import BertTokenizer tokenizer BertTokenizer( vocab_filevocab.txt, # 词表文件每行一个 token do_lower_caseTrue # 中文场景下这个参数影响不大但保持默认 ) text 床前明月光 tokens tokenizer.tokenize(text) print(tokens) # 按字切分[床, 前, 明, 月, 光] ids tokenizer.convert_tokens_to_ids(tokens) print(ids)tokenization_bert_word_level.py是词级别版本切分粒度更粗适合已经做过分词、词表以词为单位的场景。两个文件不要混用加载哪个取决于你的vocab.txt是按字还是按词构建的。判断方法很简单打开vocab.txt看前几行如果都是单字用字级别如果出现“明月”“故乡”这类多字词用词级别。3. 训练脚本怎么跑train.py 与 train_single.py 的参数拆解3.1 配置文件 model_config.json 的字段含义训练前先看config目录下的 JSON。model_config.json、model_config_small.json、model_config_test.json三个文件对应不同规模。以model_config.json为例关键字段如下表。字段含义常见取值调整建议n_ctx上下文长度512 / 1024显存不够先降到 256n_embd嵌入维度768 / 1024小语料用 512 足够n_layerTransformer 层数12 / 24层数越多越吃显存n_head注意力头数12 / 16必须能整除 n_embdvocab_size词表大小与 vocab 文件一致写错直接报 embedding 越界n_positions位置编码长度通常等于 n_ctx不要小于 n_ctxmodel_config_small.json是给显存有限的机器准备的层数和维度都压低了。model_config_test.json用于快速验证流程跑几十步就能看出有没有报错。我一般先用 test 配置跑通再换 small最后上完整配置。3.2 train.py 的启动命令与关键参数train.sh里封装了启动命令核心是调用train.py。下面这条命令是我按脚本内容整理的参数按实际作用标注。python train.py \ --train_data train.json \ # 训练语料路径 --model_config config/model_config_small.json \ # 模型配置 --tokenizer_path vocab_seg.model \ # Sentencepiece 模型路径 --batch_size 8 \ # 批大小显存不够就减 --lr 1.5e-4 \ # 学习率GPT2 微调常用 1e-4 到 5e-5 --epochs 10 \ # 训练轮数 --save_every 1000 \ # 每多少步存一次 checkpoint --log_every 100 \ # 每多少步打印一次 loss --output_dir ./output # 模型保存目录batch_size和n_ctx是显存杀手。8GB 显存下n_ctx512、n_embd768、n_layer12的配置batch_size开到 4 左右就接近上限。想再大只能上梯度累积但脚本里没内置这个参数需要自己改训练循环。lr设 1.5e-4 是 GPT2-Chinese 原版的推荐值语料小的时候可以降到 5e-5避免 loss 震荡。3.3 train_single.py 与 train.py 的区别train_single.py是单卡单文件的简化版去掉了分布式相关的逻辑适合本地调试。train.py里保留了多卡并行的接口但需要配合torch.distributed启动。如果你只有一张卡直接用train_single.py少一层分布式初始化报错信息也更干净。两个脚本的数据加载逻辑一致都读train.json区别只在训练循环的并行处理上。3.4 训练过程中的日志与 checkpoint 管理训练启动后终端会按log_every的间隔打印 loss。正常情况 loss 从 6 左右开始下降前 500 步降得快后面趋缓。如果 loss 一直卡在 6 以上不降先检查分词器是否加载正确——用错分词器会导致输入 id 全乱模型学不到东西。save_every控制 checkpoint 保存频率建议设 1000 到 2000太频繁会拖慢训练太少则中断后损失大。output_dir下会生成pytorch_model.bin和config.json生成脚本靠这两个文件加载模型。4. 生成与评估generate.py 和 eval.py 的实操细节4.1 generate.py 的采样参数怎么调训练完最直接验证效果的方式是跑generate.py。这个脚本加载 checkpoint按给定前缀续写。核心参数是temperature、top_k、top_p三个参数控制生成的随机性。python generate.py \ --model_path ./output/pytorch_model.bin \ # 训练好的模型 --tokenizer_path vocab_seg.model \ # 与训练时一致的分词器 --prefix 床前明月光 \ # 续写前缀 --temperature 0.9 \ # 温度越高越随机 --top_k 40 \ # 只从概率最高的 40 个 token 里采样 --top_p 0.95 \ # 核采样阈值 --length 50 # 生成的最大长度temperature设 0.7 到 1.0 之间比较稳。低于 0.5 生成会变得重复、死板高于 1.2 容易出现不通顺的句子。top_k和top_p通常只开一个同时开的话以top_p为准。古诗续写场景下top_k40、temperature0.8是我试过比较平衡的组合既有变化又不至于跑偏。4.2 eval.py 评估的是什么指标eval.py计算的是语言模型的困惑度perplexity。困惑度越低说明模型对语料的预测越准。但要注意困惑度只在同分词器、同测试集下可比。换了分词器词表大小变了困惑度的绝对值没有横向对比意义。评估时用train.json里预留的验证集不要拿训练集本身去算否则困惑度会低得离谱没有参考价值。4.3 生成结果与示例图片的对照包里带了律诗绝句.png、poem_1.png、散文1.png等图片这些是原项目展示的生成样例。对照着看能快速判断自己的模型有没有训到位如果生成的古诗在格式上对仗、押韵大致成立说明模型学到了结构如果只是随机拼字大概率是训练步数不够或学习率设大了。图片里的样例是参考不是标准答案自己的语料风格不同生成结果也会有差异。5. 避坑与排查训练 GPT2-Chinese 时最容易翻车的五件事5.1 分词器与模型不匹配导致生成乱码现象训练 loss 正常下降但生成结果全是无意义字符或重复 token。原因训练时用的分词器和生成时加载的分词器不是同一个。比如训练用vocab_seg.model生成时误加载了vocab.txtid 映射完全错位。解决把分词器路径写进配置文件训练和生成都从同一个配置读避免手误。生成前先打印几个 token 的 id和训练时的 id 对一下。5.2 显存溢出与 batch_size 的取舍现象训练启动几秒后报CUDA out of memory。原因batch_size或n_ctx超过显存上限。GPT2 的显存占用随序列长度平方增长n_ctx从 512 提到 1024显存需求翻倍不止。解决先把n_ctx降到 256 跑通再逐步往上加。batch_size降到 1 还报错的话换model_config_small.json把n_embd和n_layer都减半。5.3 vocab_size 与配置文件不一致现象加载模型时报size mismatch for embedding。原因model_config.json里的vocab_size和实际 vocab 文件的词表大小不一致。比如 vocab 训出来是 30000配置里写了 21128BERT 默认值。解决用wc -l vocab_seg.vocab看实际行数把vocab_size改成一致的值。改完重新训练不能只改配置加载旧 checkpoint。5.4 语料格式问题导致训练中断现象训练到一半报JSONDecodeError或读取到空行。原因train.json不是严格的每行一个 JSON 对象或者文件末尾有多余空行。脚本按行读取格式不对直接抛异常。解决训练前用python -c import json; [json.loads(l) for l in open(train.json)]过一遍确认每行都能解析。空行用sed -i /^$/d train.json删掉。5.5 学习率过大导致 loss 震荡不收敛现象loss 在 5 到 8 之间反复跳降不下去。原因lr设得太大或者语料太小但batch_size太大梯度方向不稳定。解决把lr降到 5e-5 甚至 2e-5同时减小batch_size。如果还震荡加 warmup但脚本里没内置需要在训练循环里手动加线性预热。6. 进阶技巧用自定义语料微调出风格化模型跑通默认流程后真正有意思的是拿自己的语料微调。我一般会先准备一份 10MB 到 50MB 的纯文本按行整理然后走一遍完整的“训词表 → 改配置 → 训练 → 生成”流程。这里有个容易被忽略的点微调时的vocab_size如果和预训练模型不一致embedding 层对不上只能从头训。所以如果手里有现成的 GPT2-Chinese 预训练权重要么用它的分词器要么接受从头训练。另一个技巧是控制生成风格。同样的模型换不同的prefix和采样参数输出差异很大。写古诗时prefix给一句五言或七言temperature压到 0.7top_k设 30生成的对仗感会明显好于默认参数。写散文则反过来temperature提到 1.0top_p设 0.95让模型有更多发挥空间。验证模型有没有真正学到东西我习惯用“完形填空”法从语料里抽一句遮住后半段让模型补全看补出来的和原文差多少。差得少说明模型记住了这个领域的表达习惯差得多则说明训练还不够或者语料太杂。这个方法比看困惑度直观也比单纯看生成样例更能暴露问题。从那以后我每次训中文模型都先把分词器对齐检查一遍再跑 100 步看 loss 曲线确认没有玄学波动才正式开训。希望帮到你。本文还有配套的精品资源点击获取
返回列表