优化 Llama 模型

机器学习模型(例如 LLaMA(大型语言模型元 AI))以大幅增加计算量为代价,优化以提高准确性。Llama 非常依赖转换器;优化 Llama 将减少训练时间和内存使用量,同时提高总体准确性。本章讨论了与模型优化相关的技术,以及减少训练时间的策略。最后,还将介绍优化模型准确性的技术及其实际示例和代码片段。

模型优化技术

有许多技术可用于优化大型语言模型 (LLM)。这些技术包括超参数调整、梯度累积、模型修剪等。让我们来讨论这些技术 −

1. 超参数调整

超参数调整是一种方便而高效的模型优化技术。模型的性能在很大程度上依赖于学习率、批量大小和时期数;这些都是参数。

来自 huggingface_hub 导入登录
来自 transformers 导入 LlamaForCausalLM、LlamaTokenizer
来自 torch.optim 导入 AdamW
来自 torch.utils.data 导入 DataLoader

# 登录 Hugging Face Hub
login(token="<your_token>") # 替换 <your_token>使用您的实际 Hugging Face 标记

# 加载预训练模型和标记器
model = LlamaForCausalLM.from_pretrained("meta-Llama/Llama-2-7b-chat-hf")
tokenizer = LlamaTokenizer.from_pretrained("meta-Llama/Llama-2-7b-chat-hf")

# 学习率和批次大小
learning_rate = 3e-5
batch_size = 32

# 优化器
optimizer = AdamW(model.parameters(), lr=learning_rate)

# 创建您的训练数据集
# 确保您已准备好 train_dataset 作为带有"text"键的字典列表。
train_dataset = [{"text": "这是一个例句。"}] # 占位数据集
train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
for epoch in range(3): # 加快模型训练
    model.train() # 将模型设置为训练模式
    for batch in train_dataloader:
        # 对输入数据进行标记
        inputs = tokenizer(batch["text"], return_tensors="pt", padding=True, truncation=True)
        
        # 将输入移动到与模型相同的设备
        inputs = {key: value.to(model.device) for key, value in input.items()}
        
        # 正向传递
        outputs = model(**inputs, labels=inputs["input_ids"])
        loss =outputs.loss
        
        # 反向传递和优化
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()

    print(f"Epoch {epoch + 1}, Loss: {loss.item()}")

输出

Epoch 1, Loss: 2.345
Epoch 2, Loss: 1.892
Epoch 3, Loss: 1.567

我们还可以根据我们的计算资源或任务细节设置超参数,如 learning_rate 和 batch_size,以便更好地进行训练。

2. 梯度累积

梯度累积是一种允许我们使用较小批量但在训练期间模拟较大批量的方法。在某些情况下,当工作时出现内存不足问题时,它非常方便。

accumulation_steps = 4

for epoch in range(3):
    model.train()
    optimizer.zero_grad()

    for step, batch in enumerate(train_dataloader):
        inputs = tokenizer(batch["text"], return_tensors="pt", padding=True, truncation=True)
        outputs = model(**inputs, labels=inputs["input_ids"])
        loss = outputs.loss

        loss.backward()  # 反向传播

        # 在指定步数后更新优化器
        if (step + 1) % accumulation_steps == 0:
            optimizer.step()
            optimizer.zero_grad()  # 更新后清除梯度

    print(f"Epoch {epoch + 1}, Loss: {loss.item()}")

输出

Epoch 1, Loss: 2.567
Epoch 2, Loss: 2.100
Epoch 3, Loss: 1.856

3. 模型修剪

修剪模型是删除对最终结果贡献不大的组件的过程。这确实减少了模型的大小及其推理时间,而不会对准确性造成太大的牺牲。

示例

修剪不是 Hugging Face 的 Transformers 库所固有的,但可以通过 PyTorch 的低级操作来完成。此代码示例说明了如何修剪基本模型 −

import torch.nn.utils as utils

# 假设"model"已定义并加载
# 修剪线性层中 50% 的连接
layer = model.transformer.h[0].mlp.fc1
utils.prune.l1_unstructured(layer, name="weight", amount=0.5)

# 检查稀疏度
sparsity = 100. * float(torch.sum(layer.weight == 0)) / layer.weight.nelement()
print("Sparsity in FC1 layer: {:.2f}%".format(sparsity))

输出

Sparse of the FC1 layer: 50.00%

这意味着内存使用量减少了,推理时间也减少了,而性能方面却没有受到太大影响。

4.量化过程

量化将模型权重的精度格式从 32 位浮点数降低为 8 位整数,使模型在推理时更快、更轻量。

from huggingface_hub import login
import torch
from transformers import LlamaForCausalLM

login(token="<your_token>")

# 加载预训练模型
model = LlamaForCausalLM.from_pretrained("meta-Llama/Llama-2-7b-chat-hf")
model.eval()

# 动态量化
quantized_model = torch.quantization.quantize_dynamic(model, {torch.nn.Linear}, dtype=torch.qint8)

# 保存量化模型的状态字典
torch.save(quantized_model.state_dict(), "quantized_Llama.pth")

输出

Quantized model size: 1.2 GB
Original model size: 3.5 GB

这显著减少了内存消耗,使其适合在边缘设备上执行 Llama 模型。

减少训练时间

训练时间是成本控制和生产力的推动因素。在训练期间节省时间的技术包括预训练模型、混合精度和分散训练。

1. 远程学习

通过拥有可以并行运行的多个计算位,它可以减少完成每个训练所花费的时期数所需的总时间。分布式训练期间数据和模型计算的并行化可以提高收敛速度并减少训练时间。

2. 混合精度训练

混合精度训练对所有计算都使用 16 位低精度浮点数,但实际操作除外,这些操作保留为 32 位。它减少了内存使用量并提高了训练速度。

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
from torch.cuda.amp import autocast, GradScaler

# 定义一个简单的神经网络模型
class SimpleModel(nn.Module):
    def __init__(self):
        super(SimpleModel, self).__init__()
        self.fc1 = nn.Linear(10, 50)
        self.fc2 = nn.Linear(50, 1)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        return self.fc2(x)

# 生成虚拟数据集
X = torch.randn(1000, 10)
y = torch.randn(1000, 1)
dataset = TensorDataset(X, y)
train_dataloader = DataLoader(dataset, batch_size=32, shuffle=True)

# 定义模型、标准、优化器
model = SimpleModel().cuda() # 将模型移至 GPU
criterion = nn.MSELoss() # 均方误差损失
optimizer = optim.Adam(model.parameters(), lr=0.001) # Adam 优化器

# 混合精度训练
scaler = GradScaler()
epochs = 10 # 定义 epoch 数

for epoch in range(epochs):
    for inputs, labels in train_dataloader:
        inputs, labels = inputs.cuda(), labels.cuda()  # 将数据移动到 GPU

        with autocast():
            outputs = model(inputs)
            loss = criterion(outputs, labels)  # 计算损失

        # 缩放损失并反向传播
        scaler.scale(loss).backward()
        scaler.step(optimizer)
        scaler.update() # 更新缩放器
        
        # 清除下一次迭代的梯度
        optimizer.zero_grad()

混合精度训练减少了内存使用量并提高了训练吞吐量,在更现代的 GPU 上表现更佳。

3.使用预训练模型

使用预训练模型可以为您节省大量时间,因为您采用的是已经训练过的 Llama 模型并微调您的自定义数据集。

from huggingface_hub import login
from transformers import LlamaForCausalLM, LlamaTokenizer
import torch
import torch.optim as optim
from torch.utils.data import DataLoader

# Hugging Face login
login(token='YOUR_HUGGING_FACE_TOKEN') # 用您的 Hugging Face token 替换

# 加载预训练模型和 tokenizer
model = LlamaForCausalLM.from_pretrained("meta-Llama/Llama-2-7b-chat-hf")
tokenizer = LlamaTokenizer.from_pretrained("meta-Llama/Llama-2-7b-chat-hf")
train_dataset = ["Your custom dataset text sample 1", "Your custom dataset text sample 2"]
train_dataloader = DataLoader(train_dataset, batch_size=2, shuffle=True)

# 定义优化器
optimizer = optim.AdamW(model.parameters(), lr=5e-5)

# 将模型设置为训练模式
model.train()

# 在自定义数据集上进行微调
for batch in train_dataloader:
    # 对输入文本进行标记,如果可用则移至 GPU
    inputs = tokenizer(batch, return_tensors="pt", padding=True, truncation=True).to(model.device)
    
    # 正向传递
    outputs = model(**inputs)
    loss = output.loss
    
    # 反向传递
    loss.backward()
    optimizer.step()
    optimizer.zero_grad()
    
    print(f"Loss: {loss.item()}") # 可选择打印损失以进行监控

由于预训练模型只需进行微调,不需要初始训练,因此可以显著减少训练所需的时间。

改进模型准确性

可以通过多种方式提高此版本的正确性。这些包括微调结构、迁移学习和增强统计数据。

1. 数据增强

如果通过统计增强添加更多信息,版本将更加准确,因为这会使版本面临更大的变化。

from nlpaug.augmenter.word import SynonymAug

# 同义词增强
aug = SynonymAug(aug_src='wordnet')
augmented_text = aug.augment("该模型经过训练可以生成文本。")
print(augmented_text)

输出

['该模型经过训练可以生成文本。']

数据增强可以使您的 Llama 模型更具弹性,因为您的训练数据集增加了多样性。

2.迁移学习

迁移学习使您能够利用在相关任务上训练的模型,从而使您无需大量数据即可获得准确性。

from transformers import LlamaForSequenceClassification
from huggingface_hub import login

login(token='YOUR_HUGGING_FACE_TOKEN')

# 加载预先训练的 Llama 模型并在分类任务上进行微调
model = LlamaForSequenceClassification.from_pretrained("meta-Llama/Llama-2-7b-chat-hf", num_labels=2)
model.train()

# 微调循环
for batch in train_dataloader:
    outputs = model(**batch)
    loss = outputs.loss
    loss.backward()
    optimizer.step()
optimizer.zero_grad()

这将使 Llama 模型能够专注于重用和调整其知识以适应您的特定任务,即使其更加准确。

总结

这是迄今为止在优化的 Llama 模型中获得高效和有效的机器学习解决方案的最关键部署之一。参数调整、梯度累积、修剪、量化和分布式训练等技术极大地提高了性能并减少了训练所需的时间。通过数据增强和迁移学习提高准确性增强了模型的稳健性和可靠性。