内容概览
教程以约 3.2 万个人名为数据集,逐步构建一个能够生成新名字的字符级语言模型。首先,每个名字被拆成相邻字符对,并使用统一的特殊标记表示序列的开始和结束;随后统计全部二元组,形成 27×27 的计数矩阵,再按行归一化为“给定当前字符时,下一个字符的概率分布”。模型通过重复采样下一个字符生成名字,但由于它只记住前一个字符,结果虽比均匀随机更像名字,仍缺乏长程结构。教程进一步用平均负对数似然衡量预测质量,并通过添加伪计数避免未见二元组产生零概率和无限损失。接着,同一问题被改写为神经网络:字符索引经过独热编码,乘以权重矩阵得到 logits,再通过 softmax 转为概率,最终利用反向传播和梯度下降最小化损失。两种训练方式收敛到近似相同的模型,但神经网络方法更容易扩展到多个上下文字符、更复杂网络乃至 Transformer。
更多内容
核心概念
教程中用于理解字符级语言模型、概率训练和神经网络优化的关键术语。
- 字符级语言模型:以单个字符为基本符号,对字符序列建模并预测下一个字符。
- 二元组:序列中连续出现的两个字符;二元语言模型利用第一个字符预测第二个字符。
- 广播:在满足维度兼容规则时,框架隐式扩展张量尺寸,以执行逐元素运算。
- 负对数似然:真实目标概率的对数取负后求和或求平均得到的损失;数值越低,模型越好。
- 模型平滑:向计数加入伪计数,避免未见事件获得零概率,并使概率分布更均匀。
- 独热编码:把类别索引表示成仅对应位置为 1、其余位置为 0 的向量。
- Logits:神经网络输出的未归一化实数分数,可经 softmax 转换为概率。
- Softmax:对 logits 指数化并归一化,使输出均为正数且总和为 1。
- 正则化:在主要损失之外惩罚过大的权重,使权重趋近于零,并产生类似计数平滑的效果。
技术细节
数据规模、张量表示、训练目标和实现中容易出错的关键配置。
- 数据集约含 32000 个名字,最短名字长度为 2,最长为 15;完整训练集包含约 228000 个字符二元组样本。
- 合并序列开始与结束标记后,字符表由 26 个字母和 1 个特殊标记组成,计数及概率矩阵尺寸均为 27×27。
- 计数矩阵应使用整数类型,例如 torch.int32;归一化前需要转换为浮点数。
- 按行求和应使用等价于 p.sum(1, keepdim=True) 的操作,分母形状保持为 27×1,防止广播沿错误方向执行。
- 采样使用 torch.multinomial;replacement=True 允许重复抽取,固定 torch.Generator 的种子可复现实验结果。
- 独热输入矩阵形状为样本数×27,权重矩阵形状为 27×27,矩阵乘法输出每个样本对应的 27 个 logits。
- 网络概率计算等价于 logits = xenc @ W、counts = exp(logits)、probs = counts / counts.sum(1, keepdim=True)。
- 训练损失等价于 -probs[torch.arange(n), ys].log().mean();完整数据上的目标损失约在 2.45 附近。
- 权重需要启用 requires_grad;每轮训练先把 W.grad 设为 None,再调用 loss.backward() 并沿梯度反方向更新权重。
- 使用 torch.tensor 小写构造器可以从输入推断数据类型;独热编码结果仍需显式转换为浮点数后再输入神经网络。
实践建议
复现教程或继续扩展模型时应执行的具体步骤。
- 先检查数据规模、字符集合、最短长度和最长长度,再生成包含边界标记的全部二元组。
- 对概率矩阵执行归一化后,显式验证每一行之和接近 1,并同步检查张量形状。
- 固定随机生成器种子,以便采样结果、初始化和损失变化能够被准确复现。
- 为计数加入少量伪计数,避免未见二元组导致零概率和无限负对数似然。
- 先用少量名字验证索引、独热编码、概率提取和反向传播,再无须修改算法地扩展到完整训练集。
- 逐项核对 PyTorch API 的数据类型、维度语义和广播规则,不要仅以代码能够运行作为正确性依据。
- 在掌握单字符输入后,逐步增加上下文长度并替换前向网络,同时保留 logits、softmax、负对数似然和梯度优化框架。