大模型原理:计算文本生成损失(交叉熵损失函数) 作者:马育民 • 2026-01-28 14:33 • 阅读:10002 # 介绍 在训练过程中通过计算 **文本生成损失**,来对 **生成的文本质量** 进行 **数值评估** 作用:文本生成损失用于量化模型预测下一个 token 的误差,反向传播更新模型参数,让模型逐步学会符合人类语言规律、贴合任务需求的文本生成能力 # 整体流程 从输入文本到大语言模型生成文本的整体流程:  对于左侧显示的 `3个输入词元` 中的每一个,计算包含词汇表中每个词元对应 **概率分数的向量**。 每个向量中 **最高概率分数** 的 **索引位置**,表示 **最有可能的下一个 `词元ID`**。这些与最高概率分数相关联的 `词元ID` 会被选中并映射回表示模型生成的文本 **提示:** 为了将该图片完整地放在一个页面上,使用包含 `7个词元`的小词汇表。然而,`GPTModel`使用的是由 `50 257` 个单词组成的更大的词汇表。因此,代码中的词元ID范围将是 `从0到50 256`,而不是 `从0到6` # 准备代码 从 [大模型原理:全部代码](https://www.malaoshi.top/show_1GW2f5VTAjjY.html "大模型原理:全部代码") 复制以下类或函数: - MultiHeadAttention - GELU - FeedForward - LayerNorm - TransformerBlock - GPTModel - generate_text_simple ### 配置 从 [大模型原理:修改大模型配置](https://www.malaoshi.top/show_1GW2fqaTnZGg.html "大模型原理:修改大模型配置") 复制 `GPT_CONFIG_124M` # 实现上图的流程 ### 第一步:准备数据 省略第一步,直接给定下面词元ID ##### 准备输入词元ID 以下两个输入示例,表示已经被映射词元ID(参见上图的第1步): ``` inputs = torch.tensor([[16833, 3626, 6100], # ["every effort moves", [40, 1107, 588]]) # "I really like"] ``` ##### 输出输出词元ID 与上面 `输入词元ID` 相匹配,`targets` 是希望模型生成的词元ID: ``` targets = torch.tensor([[3626, 6100, 345 ], # [" effort moves you", [1107, 588, 11311]]) # " really like chocolate"] ``` **提示:**`targets` 是对输入数据的复制,但 **向前移动一个位置** ### 第2步:将输出结果转换为概率分数 1. 将 `输入词元ID`(包含 `3个词元` 的两个输入示例) 传给模型,计算得到 `logits` 向量。 2. 然后,应用 `softmax函数` 将这些 `logits` 转换为 **概率分数(**probas,参见上图的第2步) ``` # 启用 eval() 模式,禁用诸如dropout等只在训练期间使用的随机组件 torch.manual_seed(123) model = GPTModel(GPT_CONFIG_124M) model.eval() with torch.no_grad(): logits = model(inputs) probas = torch.softmax(logits, dim=-1) print(probas) print(probas.shape) ``` 概率分数(probas)张量的最终张量维度如下所示: ``` torch.Size([2, 3, 50257]) ``` - 第一个数值 `2` 对应于输入中的两个示例(行),也称为“批次大小”。 - 第二个数值 `3` 对应于每个输入(行)中的词元数量。 - 最后一个数值 `50257` 对应于嵌入维度,由词汇表大小确定。 ### 第3-4步:将结果概率分数转换回词元ID 通过 `softmax函数` 将 `logits` 转换为 **概率后**,将结果概率分数转换回词元ID(参见上图的第3~4步)。 ``` token_ids = torch.argmax(probas, dim=-1, keepdim=True) print("Token IDs:\n", token_ids) ``` 考虑到我们有两个包含3个词元的输入批次,将 `argmax函数` 应用于概率分数(参见上图的第3步)会产生两组输出,每组包含3个预测的词元ID:  ### 第5步:将词元ID转换回文本 第5步会将词元ID转换回文本: ``` tokenizer = tiktoken.get_encoding("gpt2") print(f"目标 batch 1: {token_ids_to_text(targets[0], tokenizer)}") print(f"输出 batch 1: {token_ids_to_text(token_ids[0].flatten(), tokenizer)}") ``` 执行结果: ``` 目标 batch 1: effort moves you 输出 batch 1: Armed heNetflix ``` 发现这些输出词元与希望模型生成的目标词元 **非常不同** ### 原因 模型 **没有经过训练**,权重是随机的,所以 **生成的是随机文本**,与目标文本不同 # 解决 希望通过 **损失指标**(参见下图)来量化评估模型生成的文本的性能。这不仅有助于衡量生成的文本的质量,同时也是实现训练函数的一个构建块,将使用它来更新模型的权重,从而改善生成的文本  已经完成了第(1)步,现在准备实现文本评估函数(第(2)步) 文本评估过程的一部分是衡量生成词元与正确预测(目标)之间的偏差程度。 ### 模型训练的目标 在训练之前,模型会生成随机的下一个词元的概率向量。 **模型训练的目标** 是确保与图中框出的 `目标词元ID` 对应的 **概率值 最大化**。即:是增大与正确目标词元ID对应的索引位置的`softmax概率`,如下图所示。这个 `softmax概率` 也将用于接下来要实现的评估指标中,以量化评估模型生成的输出:正确位置的概率越高,效果越好。  **提示:**为了将所有内容放入一张图中,上图展示了 **七个词元** 的词汇表的 `softmax概率`。所以,随机值在 `1/7` 左右,大约等于 `0.14`。但本文中,用于GPT-2模型的词汇表有 `50 257个词元`,因此大多数初始概率将在 `0.000 02` 左右( `1/50 257` ) ### 打印概率分布 对于两个输入文本,可以使用以下代码打印与目标词元对应的初始 `softmax概率分数`: ``` text_idx = 0 target_probas_1 = probas[text_idx, [0, 1, 2], targets[text_idx]] print("文本 1:", target_probas_1) text_idx = 1 target_probas_2 = probas[text_idx, [0, 1, 2], targets[text_idx]] print("文本 2:", target_probas_2) ``` 每个批次的 `3个目标词元ID` **概率分数** 如下所示: ``` 文本 1: tensor([7.4540e-05, 3.1061e-05, 1.1563e-05]) 文本 2: tensor([1.0337e-05, 5.6776e-05, 4.7559e-06]) ``` **训练大语言模型的目标:**是 **最大化正确词元的可能性**,这涉及增大其相对于其他词元的概率。通过这种方式,可以确保大语言模型始终选择目标词元(实质上是句子中的下一个单词)作为它生成的下一个词元。 # 反向传播 ### 如何最大化与目标词元对应的softmax概率值呢? 更新模型权重,以便模型为想要生成的相应 `词元ID` 输出更高的值。权重更新是通过一种称为 **反向传播** 的过程完成的,这是训练深度神经网络的标准技术 ### 损失函数 反向传播需要 **损失函数**,**损失函数** 会计算模型的 **预测输出**(在这里是与 `目标词元ID` 对应的概率),与 `目标值` 之间的偏差 ### 计算两个批次的概率分数的损失 计算两个示例批次的概率分数的损失,即 `target_probas_1` 和 `target_probas_2`  主要步骤如上图所示:已经完成 `第1~3步`,计算了与目标张量对应的词元的概率,即: `target_probas_1` 和 `target_probas_2`,接下来会继续进行 `第4步`,对概率分数应用 **对数**(使用对数的好处,详见[链接](https://www.malaoshi.top/show_1EF4La0KENhc.html "链接")) ``` log_probas = torch.log(torch.cat((target_probas_1, target_probas_2))) print(log_probas) ``` 执行结果: ``` tensor([ -9.5042, -10.3796, -11.3677, -11.4798, -9.7764, -12.2561]) ``` 通过计算 **平均值** 将这些对数概率组合成一个单一分数(参见上图的 `第5步`): ``` avg_log_probas = torch.mean(log_probas) print(avg_log_probas) ``` 得到的平均对数概率分数如下所示: ``` tensor(-10.7940) ``` ### 目标 目标是通过在 **训练过程中更新模型的权重**,使 **平均对数概率尽可能接近 `0` **。 >**为什么平均对数概率尽可能接近0?** >概率 `p∈(0,1]`,是与目标张量对应的词元的概率 对数概率 $$\log_ep$$: >- 当 `p=1` 时,$$\log_ep = 0$$,代表模型 `100%` 确定这个位置是真实 token。 >- 当 `0 原文出处:http://malaoshi.top/show_1GW2fyLPhdP0.html