带你解锁 LLaMA 3 源代码的焦点逻辑,附简化版实现

打印 上一主题 下一主题

主题 1983|帖子 1983|积分 5949

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
在大型语言模型的领域中,LLaMA 3 是 Meta 发布的一款具有强大性能的开源模型。本文将对 LLaMA 3 的源代码结构进行具体分析,并基于简化的 mini 版代码资助各人明确模型的实现细节。末了,我们将通过一个小型的数据集进行模型练习的示例,展示如何应用这一模型。
一、LLaMA 3 源代码结构分析

LLaMA 3 基于 Transformer 架构,具备多头注意力机制、前馈网络、旋转位置编码等现代语言模型的关键技术。在代码结构上,LLaMA 3 通过模块化设计,分解成多个组件来实现每个部门的功能。让我们逐步分析每个模块的作用。
1. ModelArgs:模型参数类

LLaMA 模型起首定义了一个 ModelArgs 类,用来保存模型的各种超参数。这些参数包罗嵌入维度、模型层数、多头注意力头数、词汇表大小等。代码结构如下:
  1. class ModelArgs:
  2.     def __init__(self, dim=4096, n_layers=32, n_heads=32, vocab_size=50257, max_seq_len=2048):
  3.         self.dim = dim  # 嵌入维度
  4.         self.n_layers = n_layers  # Transformer 层数
  5.         self.n_heads = n_heads  # 多头注意力的头数
  6.         self.vocab_size = vocab_size  # 词汇表大小
  7.         self.max_seq_len = max_seq_len  # 最大序列长度
复制代码
这些参数直接影响模型的规模和性能。例如,dim 决定了每个 token 嵌入向量的维度,n_layers 决定了 Transformer 堆叠的层数,而 n_heads 则决定了每一层注意力机制的复杂度。
2. RMSNorm:均方根归一化

LLaMA 3 使用了 RMSNorm(均方根归一化)取代传统的 LayerNorm。RMSNorm 可以更好地在深度网络中保持数值稳固性,尤其是在嵌入维度较大的情况下。
  1. class RMSNorm(nn.Module):
  2.     def __init__(self, dim, eps=1e-6):
  3.         super().__init__()
  4.         self.eps = eps
  5.         self.weight = nn.Parameter(torch.ones(dim))
  6.     def forward(self, x):
  7.         norm = x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
  8.         return self.weight * norm
复制代码
RMSNorm 会将输入的每个向量进行归一化操纵,再通过可学习的权重进行缩放。它的结果类似于 LayerNorm,但计算方式更加简便。
3. MultiheadAttention:多头注意力机制

多头注意力机制是 Transformer 架构的焦点之一。LLaMA 的实现与标准的 Transformer 基本一致,每个注意力头会计算不同子空间上的 Query、Key 和 Value。
  1. class MultiheadAttention(nn.Module):
  2.     def __init__(self, args):
  3.         super().__init__()
  4.         self.n_heads = args.n_heads  # 多头数量
  5.         self.head_dim = args.dim // args.n_heads  # 每个注意力头的维度
  6.         assert self.head_dim * self.n_heads == args.dim  # 确保维度匹配
  7.         self.q_proj = nn.Linear(args.dim, args.dim, bias=False)
  8.         self.k_proj = nn.Linear(args.dim, args.dim, bias=False)
  9.         self.v_proj = nn.Linear(args.dim, args.dim, bias=False)
  10.         self.out_proj = nn.Linear(args.dim, args.dim, bias=False)
  11.     def forward(self, x):
  12.         B, T, C = x.shape  # B: batch size, T: seq_len, C: embedding dim
  13.         q = self.q_proj(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
  14.         k = self.k_proj(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
  15.         v = self.v_proj(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
  16.         attn_scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.head_dim)
  17.         attn_probs = F.softmax(attn_scores, dim=-1)
  18.         attn_output = torch.matmul(attn_probs, v)
  19.         attn_output = attn_output.transpose(1, 2).contiguous().view(B, T, C)
  20.         return self.out_proj(attn_output)
复制代码
每个输入颠末 Query、Key 和 Value 的线性变更后,计算注意力得分,并利用 softmax 天生权重矩阵,最终对 Value 加权求和,输出多头注意力的结果。
4. FeedForward:前馈网络

Transformer 的每个层中除了多头注意力,还包罗一个前馈网络,它用于对每个 token 进行独立的非线性变更。
  1. class FeedForward(nn.Module):
  2.     def __init__(self, args):
  3.         super().__init__()
  4.         self.fc1 = nn.Linear(args.dim, 4 * args.dim)  # 扩展 4 倍的隐藏层维度
  5.         self.fc2 = nn.Linear(4 * args.dim, args.dim)  # 将维度映射回嵌入维度
  6.     def forward(self, x):
  7.         return self.fc2(F.gelu(self.fc1(x)))  # 使用 GELU 激活函数,增加非线性
复制代码
FeedForward 网络由两层线性变更组成,激活函数使用 GELU,它在性能上优于 ReLU。
5. TransformerBlock:Transformer 层

一个完整的 Transformer 层由多头注意力、前馈网络和归一化层构成,每个部门之间都有残差连接。
  1. class TransformerBlock(nn.Module):
  2.     def __init__(self, args):
  3.         super().__init__()
  4.         self.attn = MultiheadAttention(args)
  5.         self.norm1 = RMSNorm(args.dim)
  6.         self.ffn = FeedForward(args)
  7.         self.norm2 = RMSNorm(args.dim)
  8.     def forward(self, x):
  9.         attn_out = x + self.attn(self.norm1(x))
  10.         return attn_out + self.ffn(self.norm2(attn_out))
复制代码
在这个 TransformerBlock 中,输入数据起首颠末注意力层和归一化,再颠末前馈网络。每个子层都有残差连接,以保持梯度稳固性。
6. Transformer:完整的 Transformer 模型

LLaMA 3 的焦点是多个 Transformer 层的堆叠,每个层之间依次通报上下文信息。
  1. class Transformer(nn.Module):
  2.     def __init__(self, args):
  3.         super().__init__()
  4.         self.tok_embeddings = nn.Embedding(args.vocab_size, args.dim)
  5.         self.blocks = nn.ModuleList([TransformerBlock(args) for _ in range(args.n_layers)])
  6.         self.norm = RMSNorm(args.dim)
  7.         self.output = nn.Linear(args.dim, args.vocab_size, bias=False)
  8.     def forward(self, tokens):
  9.         x = self.tok_embeddings(tokens)
  10.         for block in self.blocks:
  11.             x = block(x)
  12.         x = self.norm(x)
  13.         return self.output(x)
复制代码
这个 Transformer 模型包罗了:


  • 嵌入层:将输入 token 转换为向量。
  • 多层 TransformerBlock:处置惩罚序列中的上下文信息。
  • 输出层:将最终的向量映射到词汇表中的 token 概率分布。
二、简化版 mini LLaMA 代码

为了资助各人更好地明确 LLaMA 的代码结构,这里提供一个简化版的 mini LLaMA 模型。我们将使用 WikiText-2 数据集,并通过 Hugging Face 的 datasets 和 transformers 库加载数据和分词器。
  1. import mathimport torchfrom torch.utils.data import DataLoader, Datasetfrom transformers import AutoTokenizerfrom datasets import load_datasetfrom torch import nn, optimfrom tqdm import tqdmimport torch.nn.functional as F# 使用的WikiText-2数据集dataset = load_dataset("wikitext", "wikitext-2-v1")tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")  # 使用BERT分词器作为示例# 模型参数设置class ModelArgs:    def __init__(self, dim=512, n_layers=6, n_heads=8, vocab_size=30522,                 max_seq_len=128):  # vocab_size = 30522 for BERT        self.dim = dim        self.n_layers = n_layers        self.n_heads = n_heads        self.vocab_size = vocab_size        self.max_seq_len = max_seq_lenargs = ModelArgs()# 自定义数据集类,适用于语言模型任务class TextDataset(Dataset):    def __init__(self, texts, tokenizer, seq_len):        self.texts = texts        self.tokenizer = tokenizer        self.seq_len = seq_len    def __len__(self):        return len(self.texts)    def __getitem__(self, idx):        text = self.texts[idx]["text"]        tokens = self.tokenizer.encode(text, max_length=self.seq_len, truncation=True, padding="max_length")        return torch.tensor(tokens), torch.tensor(tokens)  # 自回归任务:输入序列猜测自身# 将练习和验证集转换为DataLoadertrain_data = TextDataset(dataset["train"], tokenizer, args.max_seq_len)valid_data = TextDataset(dataset["validation"], tokenizer, args.max_seq_len)train_loader = DataLoader(train_data, batch_size=32, shuffle=True)valid_loader = DataLoader(valid_data, batch_size=32)# 模型定义与构建class RMSNorm(nn.Module):
  2.     def __init__(self, dim, eps=1e-6):
  3.         super().__init__()
  4.         self.eps = eps
  5.         self.weight = nn.Parameter(torch.ones(dim))
  6.     def forward(self, x):
  7.         norm = x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
  8.         return self.weight * norm
  9. class MultiheadAttention(nn.Module):    def __init__(self, args):        super().__init__()        self.n_heads = args.n_heads        self.head_dim = args.dim // args.n_heads        assert self.head_dim * self.n_heads == args.dim        self.q_proj = nn.Linear(args.dim, args.dim, bias=False)        self.k_proj = nn.Linear(args.dim, args.dim, bias=False)        self.v_proj = nn.Linear(args.dim, args.dim, bias=False)        self.out_proj = nn.Linear(args.dim, args.dim, bias=False)    def forward(self, x):        B, T, C = x.shape        q = self.q_proj(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2)        k = self.k_proj(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2)        v = self.v_proj(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2)        attn_scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.head_dim)        attn_probs = F.softmax(attn_scores, dim=-1)        attn_output = torch.matmul(attn_probs, v)        attn_output = attn_output.transpose(1, 2).contiguous().view(B, T, C)        return self.out_proj(attn_output)class FeedForward(nn.Module):    def __init__(self, args):        super().__init__()        self.fc1 = nn.Linear(args.dim, 4 * args.dim)        self.fc2 = nn.Linear(4 * args.dim, args.dim)    def forward(self, x):        return self.fc2(F.gelu(self.fc1(x)))class TransformerBlock(nn.Module):
  10.     def __init__(self, args):
  11.         super().__init__()
  12.         self.attn = MultiheadAttention(args)
  13.         self.norm1 = RMSNorm(args.dim)
  14.         self.ffn = FeedForward(args)
  15.         self.norm2 = RMSNorm(args.dim)
  16.     def forward(self, x):
  17.         attn_out = x + self.attn(self.norm1(x))
  18.         return attn_out + self.ffn(self.norm2(attn_out))
  19. class Transformer(nn.Module):
  20.     def __init__(self, args):
  21.         super().__init__()
  22.         self.tok_embeddings = nn.Embedding(args.vocab_size, args.dim)
  23.         self.blocks = nn.ModuleList([TransformerBlock(args) for _ in range(args.n_layers)])
  24.         self.norm = RMSNorm(args.dim)
  25.         self.output = nn.Linear(args.dim, args.vocab_size, bias=False)
  26.     def forward(self, tokens):
  27.         x = self.tok_embeddings(tokens)
  28.         for block in self.blocks:
  29.             x = block(x)
  30.         x = self.norm(x)
  31.         return self.output(x)
  32. # 初始化模型、丧失函数和优化器model = Transformer(args)  # 新建模型criterion = nn.CrossEntropyLoss()  # 使用交织熵丧失optimizer = optim.Adam(model.parameters(), lr=1e-4)  # Adam 优化器# 练习函数def train(model, dataloader, optimizer, criterion, device):    model.train()    total_loss = 0    for batch in tqdm(dataloader, desc="Training"):        inputs, targets = batch        inputs, targets = inputs.to(device), targets.to(device)        optimizer.zero_grad()        outputs = model.to(device)(inputs)        # 计算丧失并进行反向传播        loss = criterion(outputs.view(-1, outputs.size(-1)), targets.view(-1))        loss.backward()        optimizer.step()        total_loss += loss.item()    return total_loss / len(dataloader)# 验证函数def validate(model, dataloader, criterion, device):    model.eval()    total_loss = 0    with torch.no_grad():        for batch in tqdm(dataloader, desc="Validating"):            inputs, targets = batch            inputs, targets = inputs.to(device), targets.to(device)            outputs = model(inputs)            loss = criterion(outputs.view(-1, outputs.size(-1)), targets.view(-1))            total_loss += loss.item()    return total_loss / len(dataloader)# 练习模型device = torch.device("cuda" if torch.cuda.is_available() else "cpu")epochs = 3  # 练习3个epochfor epoch in range(epochs):    print(f"Epoch {epoch + 1}/{epochs}")    train_loss = train(model, train_loader, optimizer, criterion, device)    print(f"Training Loss: {train_loss:.4f}")    valid_loss = validate(model, valid_loader, criterion, device)    print(f"Validation Loss: {valid_loss:.4f}")
复制代码
代码分析


  • 加载数据:通过 Hugging Face 的 datasets 库加载 WikiText-2 数据集,并使用 BERT 的分词器进行 token 化。
  • 定义 Transformer 模型:我们简化了模型的结构,保持了 LLaMA 3 的关键组件(RMSNorm、多头注意力、前馈网络等)。
  • 练习与验证:使用交织熵丧失函数和 Adam 优化器,练习模型并在验证集上评估其性能。
总结

通过简化的 mini 版 LLaMA 代码,我们成功分析了 LLaMA 3 的焦点架构和实现逻辑。该模型基于 Transformer,联合了现代 NLP 技术,如多头注意力机制、RMSNorm 和前馈网络等。对于想要明确和复现 LLaMA 3 的开发者,本文提供的代码息争析将是一个精良的出发点。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

美食家大橙子

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表