Chapter 5: Pretraining on Unlabeled Data
从模型配置和初始化,到训练、评估、优化,再到使用各种技巧控制文本生成,最后加载预训练权重,是一个使用 GPT 架构进行自然语言处理任务的典型示例,适合学习和理解深度学习和自然语言处理的实践
- 导入必要的库和模块:
- 导入了多个常用的 Python 库,如
matplotlib
、numpy
、tiktoken
、torch
、tensorflow
等,还使用importlib.metadata
中的version
函数来获取这些库的版本信息。 - 定义了一些函数,如
text_to_token_ids
、token_ids_to_text
、calc_loss_batch
、calc_loss_loader
、train_model_simple
、evaluate_model
、generate_and_print_sample
、plot_losses
、softmax_with_temperature
、print_sampled_tokens
、generate
、assign
、load_weights_into_gpt
等,用于不同的功能,包括数据处理、模型训练、评估、文本生成、损失计算和权重加载等。
- 导入了多个常用的 Python 库,如
功能概述
- 模型配置和初始化:
GPT_CONFIG_124M
定义了 GPT 模型的配置,包括词汇表大小、上下文长度、嵌入维度、注意力头数、层数、丢弃率等。- 使用
GPTModel
类(可能在previous_chapters
模块中定义)初始化一个 GPT 模型,设置torch.manual_seed(123)
以确保可重复性,将模型设置为评估模式model.eval()
以在推理时禁用 dropout。 - 展示了如何使用
generate_text_simple
函数(来自previous_chapters
)生成文本,并使用text_to_token_ids
和token_ids_to_text
函数在文本和 token 之间进行转换。 - 初始生成的文本质量不佳,因为模型尚未训练。
- 计算生成文本的损失:交叉熵和困惑度:
- 解释了如何计算交叉熵损失,通过输入和目标 token 计算 logits,将 logits 转换为概率,使用
torch.argmax
找到预测的 token,然后计算交叉熵。 - 展示了 PyTorch 的
torch.nn.functional.cross_entropy
函数的使用,该函数内部自动完成了一些计算步骤。 - 困惑度是交叉熵损失的指数,它可以更直观地表示模型对词汇表的不确定程度,越低表示模型预测越接近实际分布。
- 解释了如何计算交叉熵损失,通过输入和目标 token 计算 logits,将 logits 转换为概率,使用
- 训练和验证集的损失计算:
- 从 GitHub 下载一个文本文件
the-verdict.txt
,并将其分为训练集和验证集。 - 使用
create_dataloader_v1
(来自previous_chapters
)创建数据加载器,将文本数据转换为批次,使用calc_loss_batch
和calc_loss_loader
计算批次和加载器的损失。 - 考虑了设备选择(GPU 或 CPU),将模型和数据移动到相应设备。
- 从 GitHub 下载一个文本文件
- 模型训练:
- 定义
train_model_simple
函数进行简单的训练,包括训练循环、梯度计算、更新权重、评估和打印生成的样本。 - 调用
train_model_simple
函数训练模型,设置num_epochs
为 10,使用AdamW
优化器,观察训练和验证损失以及生成的文本在训练过程中的变化,显示出开始时生成无意义的字符串,后来逐渐生成语法更正确的句子,但最终会出现过拟合,因为训练集太小。 - 最后使用
plot_losses
函数将训练和验证损失绘制成图。
- 定义
- 解码策略以控制随机性:
- 介绍了
generate_text_simple
函数在生成文本时的确定性,然后引入了温度缩放(temperature scaling
)和 top-k 采样来控制生成文本的随机性和多样性。 - 温度缩放通过将 logits 除以一个大于 0 的数,改变 softmax 后的概率分布,温度大于 1 会使分布更均匀,小于 1 会使分布更尖锐。
- top-k 采样通过保留 top-k 个最可能的 token 并将其余的 logits 设为负无穷,再计算 softmax 来实现。
- 修改
generate
函数,将温度缩放和 top-k 采样结合,生成更具多样性的文本。
- 介绍了
- PyTorch 中的模型权重保存和加载:
- 使用
torch.save
保存模型权重(model.state_dict()
),并使用torch.load
加载权重。 - 对于使用自适应优化器(如
AdamW
)的情况,还可以保存和加载优化器的状态。
- 使用
- 从 OpenAI 加载预训练权重:
- 从 OpenAI 下载 GPT-2 的预训练权重,使用 TensorFlow 加载权重(因为 OpenAI 使用 TensorFlow)。
- 定义
load_weights_into_gpt
函数将下载的权重分配给自定义的GPTModel
实例,考虑了形状匹配和不同权重的分配。 - 加载权重后,使用
generate
函数生成文本,验证模型是否正确加载。
关键知识点和技术
- 自然语言处理:使用 GPT 架构进行文本生成任务,涉及文本的 token 化、生成和评估。
- 深度学习基础:包括模型训练、损失函数(交叉熵)、优化器(如
AdamW
)、评估指标(如困惑度)。 - 深度学习技巧:使用了层归一化(
LayerNorm
)、dropout、残差连接等,以及如何避免过拟合。 - 数据处理:将文本数据转换为 token 并分批处理,使用数据加载器进行训练和验证。
- 生成策略:通过温度缩放和 top-k 采样控制文本生成的随机性和多样性。
代码示例解释
- 计算交叉熵损失示例:
# 输入和目标张量,分别表示输入和期望生成的 token ID
inputs = torch.tensor([[16833, 3626, 6100], [40, 1107, 588]])
targets = torch.tensor([[3626, 6100, 345], [1107, 588, 11311]])
# 模型前向传播得到 logits
with torch.no_grad():
logits = model(inputs)
# 将 logits 转换为概率
probas = torch.softmax(logits, dim=-1)
# 计算交叉熵损失
logits_flat = logits.flatten(0, 1)
targets_flat = targets.flatten()
loss = torch.nn.functional.cross_entropy(logits_flat, targets_flat)
这里,首先将输入传递给模型得到 logits,然后将 logits 转换为概率分布,最后使用 PyTorch 的 cross_entropy
函数计算交叉熵损失。在使用 cross_entropy
函数前,需要将 logits 和 targets 展平以满足函数的输入要求。
- 训练循环示例:
def train_model_simple(model, train_loader, val_loader, optimizer, device, num_epochs, eval_freq, eval_iter, start_context, tokenizer):
train_losses, val_losses, track_tokens_seen = [], [], []
tokens_seen, global_step = 0, -1
for epoch in range(num_epochs):
model.train()
for input_batch, target_batch in train_loader:
optimizer.zero_grad()
loss = calc_loss_batch(input_batch, target_batch, model, device)
loss.backward()
optimizer.step()
tokens_seen += input_batch.numel()
global_step += 1
if global_step % eval_freq == 0:
train_loss, val_loss = evaluate_model(model, train_loader, val_loader, device, eval_iter)
train_losses.append(train_loss)
val_losses.append(val_loss)
track_tokens_seen.append(tokens_seen)
print(f"Ep {epoch+1} (Step {global_step:06d}): Train loss {train_loss:.3f}, Val loss {val_loss:.3f}")
generate_and_print_sample(model, tokenizer, device, start_context)
return train_losses, val_losses, track_tokens_seen
在 train_model_simple
函数中,在每个 epoch 中,将模型设置为训练模式,遍历训练数据加载器,计算损失,反向传播梯度,更新权重。在一定的评估频率下评估训练和验证损失,并打印生成的样本。
- 温度缩放示例:
def softmax_with_temperature(logits, temperature):
scaled_logits = logits / temperature
return torch.softmax(scaled_logits, dim=0)
# 不同温度下的缩放
temperatures = [1, 0.1, 5]
scaled_probas = [softmax_with_temperature(next_token_logits, T) for T in temperatures]
softmax_with_temperature
函数将 logits 除以温度,然后进行 softmax 操作,不同的温度会导致不同的概率分布,影响后续的 token 采样。
总的来说,这段代码展示了一个完整的流程,从模型配置和初始化,到训练、评估、优化,再到使用各种技巧控制文本生成,最后加载预训练权重,是一个使用 GPT 架构进行自然语言处理任务的典型示例,适合学习和理解深度学习和自然语言处理的实践。
In [1]:
- In this chapter, we implement the training loop and code for basic model evaluation to pretrain an LLM
- At the end of this chapter, we also load openly available pretrained weights from OpenAI into our model
-
The topics covered in this chapter are shown below
5.1 Evaluating generative text models
- 这段文本主要描述了关于评估生成文本模型的内容。首先提到以回顾上一章中初始化 GPT 模型的代码作为这一部分的开始。接着阐述了语言模型的基本评估指标。最后说明在这一部分中,会将这些评估指标应用于训练集和验证集。
5.1.1 Using GPT to generate text
- We initialize a GPT model using the code from the previous chapter
In [2]:
import torch from previous_chapters import GP