上次我们首次训练了自己的模型,并尝试了通过不同长度的文本进行训练(单句文本和飞鸟集)。同时在随机配置的参数中找到了一种效果比较好的,本篇文章我们将来探索如何找到最适合的训练参数。

首先我们介绍下各个参数的作用:

这些参数会影响模型的学习速度、表现和稳定性:

  1. batch_size(批大小)
    • 含义:一次训练中并行处理的样本数量。
    • 小 batch:更新更频繁 → 学习更快,但噪声大,可能震荡。
    • 大 batch:更新更稳定 → 更平滑收敛,但需要更多显存。
    • 探索点:小数据集时 batch 不宜太大,否则容易过拟合。
  2. block_size(上下文长度)
    • 含义:模型一次能看到的序列长度。
    • 小 block:模型“视野”短,只能学习局部关系。
    • 大 block:模型能学到长程依赖,但训练时间和显存消耗显著增加。
    • 发现点:很多 GPT 论文里就是逐渐增大 block_size,观察性能提升的。
  3. embed_size(嵌入维度 / hidden size)
    • 含义:字符/词向量的维度。
    • 小 embedding:模型表达能力不足。
    • 大 embedding:表达能力更强,但参数量、训练时间都增加。
  4. n_layers(层数)
    • 含义:Transformer 堆叠层数。
    • 层数少:模型浅,学不到复杂模式。
    • 层数多:更强的建模能力,但更容易过拟合、训练变慢。
  5. learning_rate(学习率)
    • 含义:参数更新的步长。
    • 太小:收敛慢。
    • 太大:loss 震荡甚至发散。
    • 一般配合 学习率调度器,比如 warmup 或衰减。
  6. dropout(丢弃率)
    • 含义:防止过拟合的一种正则化方法。
    • 0:完全不过滤,拟合能力最强,但过拟合风险大。
    • 0.1~0.3:常用范围。

二、如何判断训练结果的好坏

  1. 训练/验证 Loss
    • loss 越低说明模型在训练集(或验证集)上拟合得更好。
    • 但要防止过拟合(训练 loss 低、验证 loss 高)。
  2. 生成文本质量
    • 更直观。
    • 可观察生成文本:
      • 连贯(句子是否自然流畅)。
      • 合理(逻辑是否自洽)。
      • 多样性(是否不是机械重复)。
  3. 收敛速度
    • 同样的参数下,能更快收敛到较低 loss,说明配置更高效。
    • 这也是记录每轮训练时间的价值。
  4. 困惑度 (Perplexity, PPL)
    • 语言模型的常用指标。
    • PPL = exp(loss),数值越低越好。
    • 举例:
      • PPL=50 → 模型选择下一个 token 的平均不确定性比较大。
      • PPL=10 → 模型更有信心。

 三、目标

想比较 不同参数对训练的影响,可以观察:

  1. loss 曲线(是否下降更快、更稳定)。
  2. 训练时间(代价 vs 收益)。
  3. 生成样例文本(是否更像原文)。

这样就能逐步建立“参数 → 训练表现”的直观映射。

基于此,我们在上次工程的基础上添加了sweep.py,用于调用train.py针对训练数据,使用不同的参数组合进行训练,并观察训练的效果。每次训练都会生成对应参数的文件夹,loss记录和曲线,对应的模型,以及最后调用模型生产的文本样例。

需要注意的是我在sweep 里做了 block_size=64 组合,而原始数据可能太短,load_dataset 里生成 xb/yb 时索引溢出。如果没有 drop_last=True 时,最后一个 batch 的长度可能小于 block_size,导致模型 forward 时用到不存在的 token,所以必须加这个配置。

由于本次训练的时间过长,最终在训练完单句话的文本后我结束了训练,先来观察在单句文本上的训练效果。

Gpt认为的最佳结果

最佳结果

  • 最优实验配置
    • batch_size=8
    • embed_dim=64
    • num_layers=2
    • hidden_dim=128
    • lr=0.0001
    • dropout=0.3
  • final_loss = 0.000022,表现非常好。

参数对结果的影响

1. Dropout

  • 平均表现最好的是 0.1(0.5439),其次是 0.3。
  • 0.0(无正则)和 0.2 反而略差。
    ➡️ 说明少量 dropout 有助于提升效果。

2. 学习率 (lr)

  • 0.0001 最好(0.5465),随着 lr 增大,效果略差:
    • 0.0001 < 0.0005 < 0.0010
      ➡️ 更小的学习率更稳定。

3. Batch size

  • 8 和 64 较优(0.5459 左右),中间值(16, 32)反而稍差。
    ➡️ 小批次或大批次更合适,中间可能震荡。

4. Embedding 维度

  • 越大越好:256 最优(0.5457),其次 128。
  • 小 embedding(32, 64)效果差。
    ➡️ 语义表达能力随维度上升有提升。

5. 隐藏层维度

  • 128 最佳(0.5453),大维度提升有限,64 和 256 稍差。

6. 层数 (num_layers)

  • 2 层明显最佳(0.00011),层数增加 loss 急剧上升,12 层几乎不可用。
    ➡️ 模型太深反而过拟合或训练不稳定。

总结建议

  1. 最优配置
    batch_size=8, embed_dim=128~256, hidden_dim=128, num_layers=2, lr=0.0001, dropout=0.1~0.3
  2. 趋势
    • 小学习率稳定收敛。
    • 适度的 dropout 有益。
    • embedding 越大越好,但 hidden_dim 128 足够。
    • 模型浅层效果最佳,深层导致训练崩坏。

但就整个训练数据而言,这组的loss值比较优秀,但生成的文本并不是很理想。可能是数据太简单/过拟合:模型可能记住了训练集,导致训练 loss 近乎为 0。有很多组的训练结果都生成了与训练数据完全一致的结果,只是之后混杂了太多无效内容。

为什么会出现这种现象

  1. 数据量太小
    • 训练数据只有一句话,模型的参数量却非常大(几十万甚至上百万)。
    • 结果就是模型很容易死记硬背那句话,在训练时 loss 接近 0。
  2. 生成时重复+失控
    • 在推理阶段,模型会按照概率继续生成 token。
    • 它能复现训练句子,但之后就会进入“瞎编”状态(因为没有别的训练数据可以参考)。
    • 所以开头完全一致,后面混杂无效内容。
  3. 语言模型的本质
    • LM 学到的是 下一个词的分布,不是只背诵一句话。
    • 当上下文超出给的那一句话,模型就“没学过”,只能乱采样。

如何改进

如果想让模型只生成那句话,或者能更稳定地复现内容,可以考虑几种方法:

  1. 微调思路
    • 单句训练 ≠ 微调。你现在的结果是“过拟合到一句话”。
    • 可以加上更多类似的句子(比如几十~几百个短句),这样模型能学到更稳健的分布。
  2. 训练方式调整
    • 减少模型大小:比如用更小的 embed_dim、hidden_dim、num_layers。模型过大会导致死记硬背+胡乱生成。
    • 增加 dropout:让模型不要那么快就“背死”一句话。
    • 训练目标修改:如果你只是要复现,可以改成 seq2seq 或者直接分类任务,而不是语言建模。
  3. 推理时控制
    • 调低 temperature(比如 0.1),模型会更确定地输出你训练过的内容,不会乱发散。
    • 甚至可以设置 greedy decoding(top_k=1),这样它几乎只会复现训练数据。

总结一句:
你的模型已经“完美记住”了那句话(所以 loss 接近 0),但因为训练数据太少,没法学到自然的语言分布,所以在生成时后半段就“胡编”。

本次主要是针对原工程进行了扩展,下一次我们将用飞鸟集来进行训练,目前还有很多问题需要解决,例如训练时间过长等问题。

本次对应的版本为1.6-1.8。

发布于2025/08/24。

船长

发表评论