基于TensorFlow与Transformers的GPT2模型续训、增量训练及epoch设置咨询
适配当前环境的三个问题落地方案
现有代码的小笔误修正(避免运行报错)
- 导入Path的代码行
from pathlib import Pa th改为from pathlib import Path - 导入os的代码行
import os# the folder 'text' contains all the files改为import os # the folder 'text' contains all the files - BPETrainer的参数
inital_alphabet改为initial_alphabet(拼写错误) - 保存模型的代码段需要补充导入常量,否则会报变量未定义错误:在文件开头添加
from transformers import WEIGHTS_NAME, CONFIG_NAME, GPT2Tokenizer, GPT2Config, TFGPT2LMHeadModel
一、训练暂停与重启的检查点实现
适配你当前TensorFlow 2.3、Transformers 4.2.2的版本,不需要引入新依赖,只需要在现有代码基础上小幅修改即可。
现有代码只有全程训练结束才会存模型,中途关进程、让出算力时所有进度都会丢失,核心原因是没有实现全量状态的检查点机制。
实现逻辑是:用TensorFlow内置的tf.train.Checkpoint存储所有需要恢复的训练状态,包括模型权重、优化器动量、当前训练轮次,每个轮次结束自动存盘,随时可以关进程暂停,重启时自动加载到上次的断点继续训练。
- 具体代码修改:
- 替换
model.compile之后、model.fit之前的代码块:
- 替换
# 新建检查点存储目录 checkpoint_dir = './training_checkpoints' os.makedirs(checkpoint_dir, exist_ok=True) # 定义需要持久化的所有状态:模型、优化器、当前epoch ckpt = tf.train.Checkpoint( model=model, optimizer=optimizer, current_epoch=tf.Variable(0) ) # 检查点管理器,自动保留最近3份检查点,不占用多余磁盘 ckpt_manager = tf.train.CheckpointManager( ckpt, directory=checkpoint_dir, max_to_keep=3 ) # 启动时自动加载已有检查点 if ckpt_manager.latest_checkpoint: ckpt.restore(ckpt_manager.latest_checkpoint) print(f'已加载历史训练进度,从第 {ckpt.current_epoch.numpy()+1} 个epoch开始继续训练') else: print('无历史检查点,从头开始训练')
- 加自定义回调,每个epoch结束自动保存检查点,同时修改
model.fit参数支持断点续训:
# 自定义回调:每个epoch结束时更新进度、存检查点 class CheckpointSaver(tf.keras.callbacks.Callback): def on_epoch_end(self, epoch, logs=None): ckpt.current_epoch.assign(epoch) ckpt_manager.save() print(f'第 {epoch+1} 轮训练完成,进度已自动保存') # 替换原有model.fit的调用 total_epoch = 20 # 总计划训练轮数 history = model.fit( dataset, epochs=total_epoch, initial_epoch=ckpt.current_epoch.numpy(), # 自动从上次暂停的轮次开始 callbacks=[CheckpointSaver()] )
- 额外优化:如果需要让出算力时不需要等一个epoch跑完,可以把存盘逻辑移到
on_train_batch_end方法里,设置每100个训练步存一次,最多丢100步的进度。 - 注意:给dataset的shuffle操作加固定随机种子,避免每次重启时数据集顺序打乱导致进度错位:
# 原有shuffle行修改为 dataset = dataset.shuffle(BUFFER_SIZE, seed=42)
二、增量投喂数据集的实现
完全支持这种训练方式,本质是持续训练,不需要重新初始化模型,唯一的硬要求是前后两次训练的预处理流程、超参数完全对齐,不能随便改。
- 具体实现步骤:
- 第一次用10%的数据集训完之后,正常把模型、tokenizer存到
./model_bn_custom/目录下,不要重新训练tokenizer——一旦词表确定,后续追加任何数据集都不要修改tokenizer,否则会出现模型输入维度不匹配的问题。 - 下次追加10%新数据集的时候,不要重新新建模型,直接加载之前训好的模型权重当初始值:
- 第一次用10%的数据集训完之后,正常把模型、tokenizer存到
# 替换原有新建模型的代码 model = TFGPT2LMHeadModel.from_pretrained('./model_bn_custom/') tokenizer = GPT2Tokenizer.from_pretrained('./model_bn_custom/')
- 用和之前完全一样的参数(block_size、batch_size、学习率范围)处理新的10%数据集,构建成和之前格式完全一致的tf.data.Dataset。
- 直接在新的数据集上调用
model.fit训练即可,训完之后再把新的权重覆盖保存到原目录。
- 注意事项:
- 追加新数据集时,学习率建议降到原来的1/3左右,比如原来用3e-5,续训时用1e-5,避免高学习率冲掉之前已经学好的权重。
- 如果新旧数据集分布差异很大(比如之前训练的是通用网文,现在加的是专业法律文本),可以混合10%-20%的旧数据集和新数据集一起训,避免出现旧知识被新内容覆盖的问题。
三、训练方案选择与epoch参考值
不存在绝对的小数据多epoch比大数据集好的结论,尤其你现在是从零训小体量GPT2,优先保证数据多样性的收益远高于多堆epoch数。
- 核心判断逻辑:
- 如果总清洗后的语料量低于100万字,哪怕把小数据集的epoch堆到20以上,也会很快出现过拟合(模型只会背诵训练集内容,不会生成连贯的新句子),效果远不如100万字语料训3个epoch。
- 从零开始训GPT2,数据量的权重远高于epoch数,只有当总语料量已经足够覆盖你需要的语言风格时,再谈调整epoch数。
- 适配你当前场景的epoch参考值:
- 语料量<100万字:epoch数控制在5-8,不要超过10,提前拆分10%的语料当验证集,当验证集损失连续2轮不下降甚至上升时直接停止训练,不要硬跑满预设的epoch数。
- 语料量在100万-1000万字之间:epoch数控制在2-5即可,通常2个epoch就能让小模型收敛到不错的效果。
- 不要一开始就设置20个epoch,你当前代码没有验证集,根本无法判断过拟合的节点,纯粹靠epoch数硬跑会浪费大量算力。
内容的提问来源于stack exchange,提问作者Magnus V.
相关产品推荐
相关产品推荐

