目录
本文免费发表于我的微信公众号:“AI与编程之窗”,请关注我的公众号后免费阅读,搜索“AI与编程之窗”或扫描以下二维码添加关注。
1. 简介
Llama 3.1 是 Meta 公司于 2024 年 7 月 23 日发布的最新开源大语言模型。这一版本在先前 Llama 3 的基础上进行了增强,支持多模态处理,并具有多种参数规模,包括 8B、70B 和 405B 参数。Llama 3.1 经过大规模文本数据的预训练,并结合了超过 1000 万个人工标注的示例,显著提高了模型的性能和适应性。Llama 3.1 尤其在多语言对话和编码能力方面表现出色,是目前 Meta 最先进的大语言模型之一。
Meta公司开源了Llama 3.1大模型,任何人都可以下载并用于二次开发。本教程的目的是帮助用户从头开始构建自己的Llama 3.1模型,包括在本地搭建自己的大模型和对大模型进行微调。通过详细的步骤讲解,我们将引导您完成Llama 3.1模型的安装、配置、训练以及微调,帮助您充分利用这个先进的开源大语言模型。
2. 准备工作
2.1 系统要求
在开始之前,确保您的系统满足以下要求:
- 操作系统:Linux (推荐Ubuntu)
- Python 版本:Python 3.8及以上
- GPU:NVIDIA GPU,支持CUDA 11及以上
- CUDA和cuDNN已正确安装
2.2 安装必要的依赖库
首先,安装所需的Python库:
pip install torch matplotlib tiktoken
2.3 下载模型和分词器
从Meta的官方网站下载Llama 3.1模型权重和分词器文件。假设下载链接如下:
- 模型权重:
https://llama.meta.com/llama3.1/model.pth
- 分词器:
https://llama.meta.com/llama3.1/tokenizer.model
下载后,将它们保存在您的工作目录中。
3. 核心实现步骤
3.1 加载分词器
使用 tiktoken 库加载和使用 Llama 3.1 的分词器。以下是下载分词器模型并进行初始化的详细步骤。
安装 tiktoken
首先,确保您已经安装了 tiktoken 库。如果没有安装,请使用以下命令进行安装:
pip install tiktoken
下载分词器模型
从Meta的官方网站下载Llama 3.1的分词器模型。假设下载链接为:
- 分词器模型:
https://llama.meta.com/llama3.1/tokenizer.model
将下载的文件保存到您的工作目录中,例如 path/to/tokenizer.model
。
初始化分词器
接下来,使用 tiktoken 库加载并初始化分词器模型。以下是示例代码:
from pathlib import Path
import tiktoken
# 指定分词器模型的路径
tokenizer_path = "path/to/tokenizer.model"
# 特殊令牌,可以根据需要添加更多的特殊令牌
special_tokens = ["<|startoftext|>", "<|endoftext|>"]
# 加载分词器模型
mergeable_ranks = tiktoken.load(tokenizer_path)
# 初始化分词器
tokenizer = tiktoken.Encoding(
name=Path(tokenizer_path).name,
mergeable_ranks=mergeable_ranks,
special_tokens={token: len(mergeable_ranks) + i for i, token in enumerate(special_tokens)},
)
# 测试分词器
encoded = tokenizer.encode("hello world!")
decoded = tokenizer.decode(encoded)
print("Encoded:", encoded)
print("Decoded:", decoded)
代码解析
- 安装和导入库:首先确保安装了 tiktoken 库,并导入需要的模块。
- 指定路径:设置分词器模型的路径为
tokenizer_path
。 - 定义特殊令牌:定义一些特殊令牌,如文本开始和结束标记。
- 加载模型:使用
tiktoken.load
方法加载分词器模型。 - 初始化分词器:通过
tiktoken.Encoding
初始化分词器,将模型名称、可合并的词汇表(mergeable_ranks)和特殊令牌传递给它。 - 测试分词器:使用
encode
方法将文本转换为 token,并使用decode
方法将 token 转换回文本,以验证分词器的工作是否正常。
3.2 读取模型文件
从模型文件中读取参数和权重是构建 Llama 3.1 模型的关键步骤之一。这部分将详细介绍如何读取模型文件中的参数和权重,并进行初始化设置。
安装依赖库
确保安装了 PyTorch 库,因为我们将使用 PyTorch 来加载模型文件。
pip install torch
读取模型文件
首先,我们需要从模型文件中加载模型权重和配置参数。假设我们已经下载了模型权重文件 model.pth
和配置文件 params.json
。
import torch
import json
# 加载模型权重
model_path = "path/to/model.pth"
model = torch.load(model_path)
# 打印模型的部分键以验证加载
print(json.dumps(list(model.keys())[:20], indent=4))
# 加载配置参数
config_path = "path/to/params.json"
with open(config_path, "r") as f:
config = json.load(f)
# 提取配置参数
dim = config["dim"]
n_layers = config["n_layers"]
n_heads = config["n_heads"]
n_kv_heads = config["n_kv_heads"]
vocab_size = config["vocab_size"]
multiple_of = config["multiple_of"]
ffn_dim_multiplier = config["ffn_dim_multiplier"]
norm_eps = config["norm_eps"]
rope_theta = torch.tensor(config["rope_theta"])
# 打印配置参数以验证加载
print(f"Model Dimension: {dim}")
print(f"Number of Layers: {n_layers}")
print(f"Number of Heads: {n_heads}")
print(f"Vocabulary Size: {vocab_size}")
代码解析
- 安装和导入库:首先,确保安装了 PyTorch,并导入必要的库。
- 加载模型权重:使用
torch.load
方法加载模型文件model.pth
。加载后,可以通过打印模型的键来验证权重是否正确加载。 - 加载配置文件:打开并读取配置文件
params.json
,该文件包含了模型的超参数和其他设置。 - 提取配置参数:从配置文件中提取必要的参数,如模型维度、层数、头数、词汇表大小等。这些参数将在后续步骤中用于初始化模型结构。
- 打印配置参数:为了确保配置参数正确加载,打印一些关键参数进行验证。
3.3 文本到Token的转换
在构建语言模型时,首先需要将输入的自然语言文本转换为模型所需的 tokens。Llama 3.1 使用的分词器可以将文本转换为一系列 tokens,这些 tokens 是模型可以理解和处理的基本单元。
转换步骤
以下是将输入文本转换为模型所需 tokens 的详细步骤:
- 定义输入文本:首先,我们需要定义要转换的输入文本。
- 使用分词器进行编码:使用已经加载和初始化的分词器将输入文本转换为 tokens。
- 添加特殊 tokens:在模型的要求下,有时需要在 tokens 序列的开头或结尾添加特殊 tokens。
- 将 tokens 转换为张量:为了便于后续的处理,将 tokens 序列转换为 PyTorch 张量。
示例代码
以下是具体的代码实现:
# 定义输入文本
prompt = "the answer to the ultimate question of life, the universe, and everything is "
# 使用分词器将文本编码为 tokens
tokens = tokenizer.encode(prompt)
# 打印编码后的 tokens
print(f"Encoded tokens: {tokens}")
# 添加特殊 tokens
# 假设 128000 是模型要求的特殊 token
tokens = [128000] + tokens
# 将 tokens 转换为 PyTorch 张量
tokens_tensor = torch.tensor(tokens)
# 打印转换后的张量
print(f"Tokens Tensor: {tokens_tensor}")
代码解析
- 定义输入文本:
prompt
是我们要转换的输入文本。 - 使用分词器进行编码:
tokenizer.encode(prompt)
方法将输入文本转换为 tokens。tokenizer
是之前加载并初始化的分词器对象。 - 打印编码后的 tokens:打印编码后的 tokens 序列以验证分词器的输出。
- 添加特殊 tokens:根据模型的要求,可能需要在 tokens 序列的开头添加特殊 tokens。在此示例中,我们假设 128000 是一个特殊 token,并将其添加到 tokens 序列的开头。
- 将 tokens 转换为 PyTorch 张量:使用
torch.tensor(tokens)
方法将 tokens 序列转换为 PyTorch 张量。这一步是为了便于后续的模型处理,因为 PyTorch 模型通常处理张量数据。 - 打印转换后的张量:打印转换后的张量以验证最终的 tokens 张量。
3.4 Token到Embedding的转换
将 tokens 转换为 embeddings 是深度学习模型处理文本数据的重要步骤。Embedding 是一种将离散的 tokens 转换为连续向量表示的方法,使模型能够处理和理解文本数据。
依赖库
确保您已经安装并导入了必要的库:
import torch
import torch.nn as nn
步骤详解
- 初始化 Embedding 层:使用模型的词汇表大小和向量维度初始化一个 Embedding 层。
- 加载预训练的 Embedding 权重:将模型中预训练的权重加载到 Embedding 层。
- 转换 Tokens:将 tokens 转换为 embeddings。
- 处理数据类型:将 embeddings 转换为适当的数据类型,以适应模型的需求。
示例代码
以下是将 tokens 转换为 embeddings 的具体代码实现:
# 定义模型参数
vocab_size = 50257 # 假设词汇表大小为 50257
dim = 768 # 假设每个 token 的 embedding 维度为 768
# 初始化 Embedding 层
embedding_layer = nn.Embedding(vocab_size, dim)
# 加载预训练的 Embedding 权重
embedding_layer.weight.data.copy_(model["tok_embeddings.weight"])
# 将 tokens 转换为 embeddings
token_embeddings_unnormalized = embedding_layer(tokens_tensor).to(torch.bfloat16)
# 打印 Embeddings 的形状
print(f"Token Embeddings Shape: {token_embeddings_unnormalized.shape}")
代码解析
- 定义模型参数:首先,定义模型的词汇表大小 (
vocab_size
) 和 embedding 向量的维度 (dim
)。 - 初始化 Embedding 层:使用
nn.Embedding
初始化一个 Embedding 层。这个层将词汇表中的每个 token 映射到一个维度为dim
的向量。 - 加载预训练的 Embedding 权重:从模型中加载预训练的 embedding 权重。
model["tok_embeddings.weight"]
包含了预训练的 embedding 权重,通过embedding_layer.weight.data.copy_
方法将这些权重复制到初始化的 Embedding 层中。 - 将 tokens 转换为 embeddings:使用
embedding_layer
将 tokens 转换为 embeddings。tokens_tensor
是之前步骤中转换的 tokens 张量。.to(torch.bfloat16)
将 embeddings 转换为bfloat16
数据类型,以适应后续的模型处理需求。 - 打印 Embeddings 的形状:通过
print
打印 embeddings 的形状,以验证转换是否正确。输出的形状应该是(序列长度, embedding 维度)
。
3.5 归一化Embedding
对 embeddings 进行归一化处理是确保模型稳定性和性能的重要步骤。归一化有助于避免数值不稳定,确保各层之间的数值范围一致,从而提升训练效率和模型性能。
归一化的原理
在本教程中,我们使用均方根归一化(RMS Normalization)来处理 embeddings。均方根归一化是一种对向量进行归一化的方法,它通过计算向量的均方根(RMS)并使用该值对向量进行缩放。
均方根的计算方法如下:
归一化后的向量为: