从头开始训练 Llama

从头开始训练 Llama 非常耗费资源,但回报丰厚。在正确准备训练数据集和正确设置训练参数的情况下运行训练循环将确保您生成足够可靠的语言模型,以应用于许多 NLP 任务。成功的秘诀是在训练期间进行适当的预处理、参数调整和优化。

与其他 GPT 样式的模型相比,Llama版本 是一个开源版本。该模型需要大量资源、充分的准备以及更多内容才能从头开始训练。本章报告了从头开始训练 Llama 的过程。该方法包括从准备好训练数据集到配置训练参数以及实际进行训练的所有内容。

Llama 旨在支持几乎所有 NLP 应用程序,包括但不限于生成文本、翻译和摘要。大型语言模型可以通过三个关键步骤从头开始训练 −

  • 准备训练数据集
  • 适当的训练参数
  • 管理过程并确保正确的优化生效

所有步骤将逐步进行,并附上代码片段和输出含义。

准备训练数据集

训练任何 LLM 最重要的第一步是为其提供优秀、多样化且广泛的数据集。Llama 需要大量的文本数据来捕捉人类语言的丰富性。

收集数据

训练 Llama 需要一个包含来自各个领域的各种文本样本的单一数据集。一些用于训练 LLM 的示例数据集包括 Common Crawl、Wikipedia、BooksCorpus 和 OpenWebText。

示例:下载数据集

import 请求
import os

# 为数据集创建目录
os.makedirs("datasets", exist_ok=True)

# 数据集的 URL
url = "https://example.com/openwebtext.zip"
output = "datasets/openwebtext.zip"

# 下载数据集
response = requests.get(url)
with open(output, "wb") as file:
    file.write(response.content)
print(f"Dataset downloaded and saved at {output}")

输出

数据集已下载并保存在 datasets/openwebtext.zip

下载数据集时,您需要在训练之前预处理文本数据。大多数预处理涉及标记化、小写化、删除特殊字符以及设置数据以适应给定结构。

示例:预处理数据集

from transformers import LlamaTokenizer

# 加载预训练的标记器
tokenizer = LlamaTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf", token=token)

# 加载原始文本
with open('/content/raw_data.txt', 'r') as file:
raw_text = file.read()

# 对文本进行标记
tokens = tokenizer.encode(raw_text, add_special_tokens=True)

# 将标记保存到文件
with open('/tokenized_text.txt', 'w') as token_file:
    token_file.write(str(tokens))
    
print(f"文本被标记并保存为标记。")

输出

文本被标记并保存为标记。

设置模型训练参数

现在,我们将继续设置训练参数。这些参数设置了您的模型如何从数据集中学习;因此,它们会直接影响模型的性能。

主要训练参数

  • 批次大小 − 在更新模拟权重之前经过的样本数量。
  • 学习率 − 根据损失梯度设置更新模型参数的量。
  • 时期 −模型在整个数据集上运行的次数。
  • 优化器 −用于通过改变权重来最小化损失函数

您可以使用 AdamW 作为优化器和热身学习率调度程序来训练 Llama。

示例:训练参数配置

import torch
from transformers import LlamaForCausalLM, AdamW, get_linear_schedule_with_warmup
# token="you_token"

# 加载模型
model = LlamaForCausalLM.from_pretrained('meta-llama/Llama-2-7b-chat-hf', token=token)

model = model.to("cuda") if torch.cuda.is_available() else model.to("cpu")
# 训练参数
epochs = 3
batch_size = 8
learning_rate = 5e-5
warmup_steps = 200

# 设置优化器和调度器
optimizer = AdamW(model.parameters(), lr=learning_rate)
scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=warmup_steps, num_training_steps=epochs)
print("训练参数已设置。")

输出

训练参数已设置。

用于批量的数据加载器

训练需要批量数据。使用 PyTorch 的 DataLoader 可以轻松完成此操作。

from torch.utils.data import DataLoader, Dataset
# 自定义数据集类
class TextDataset(Dataset):
    def __init__(self, tokenized_text):
       self.data = tokenized_text
    def __len__(self): 
        return len(self.data) // batch_size 
    def __getitem__(self, idx): 
        return self.data[idx * batch_size : (idx + 1) * batch_size]

with open("/tokenized_text.txt", 'r') as f:
  tokens_str = f.read()
tokens = eval(tokens_str)  # 评估字符串以获取列表

# DataLoader 定义
train_data = TextDataset(tokens)
train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True)

print(f"DataLoader created with batch size {batch_size}.")

输出

DataLoader created with batch size 8.

现在,学习过程和数据加载程序的要求已经确定,是时候进入实际训练阶段了。

训练模型

所有这些准备工作都在训练循环的运行中协同工作。训练数据集无非就是简单地批量输入模型,然后用损失函数更新其参数。

运行训练循环

现在开始训练过程,所有这些准备工作都将与现实世界相结合。分阶段向算法提供数据集合,以便根据其变量的损失函数对其进行更新。

import tqdm

# 如果可用,将模型移动到 GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)

for epoch in range(epochs):
   print(f"Epoch {epoch + 1}/{epochs}")
   model.train
   total_loss = 0  
   for batch in tqdm.tqdm(train_loader
      batch = [torch.tensor(sub_batch, device=device) for sub_batch in batch]
      max_len = max(len(seq) for seq in batch)
      padded_batch = torch.zeros((len(batch), max_len), dtype=torch.long, device=device)
      for i, seq in enumerate(batch):
         padded_batch[i, :len(seq)] = seq

        # 前向传播,使用 padded_batch
        output = model(padded_batch, labels=padded_batch
        loss = output.loss
        # 后向传播
        optimizer.zero_grad() # 重置梯度。
        loss.backward() # 计算梯度。
        optimizer.step() # 更新模型参数。
        scheduler.step() # 更新学习率。
        
        total_loss += loss.item() # 累积损失。

   print(f"Epoch {epoch + 1} completed. Loss: {total_loss:.4f}")  

输出

Epoch 1 completed. Loss: 424.4011
Epoch 2 completed. Loss: 343.4245
Epoch 3 completed. Loss: 328.7054

保存模型

训练完成后,保存模型;否则,每次训练时都要保存。

# 保存训练好的模型
model.save_pretrained('trained_Llama_model')
print("模型已成功保存。")

输出

模型已成功保存。

现在我们已经从头开始训练了模型并将其保存。我们可以使用该模型来预测新字符/单词。我们将在接下来的章节中详细介绍。