从头开始构建自己的 GPT 大型语言模型

图片来源: Tatev Aslanyan

一、说明

        我们将使用 PyTorch 从头开始构建生成式 AI、大型语言模型——包括嵌入、位置编码、多头自注意、残差连接、层归一化,Baby GPT 是一个探索性项目,旨在逐步构建类似 GPT 的语言模型。在这个项目中,我不会太详细地解释理论,而是主要展示编码部分。该项目从一个简单的 Bigram 模型开始,并逐渐融入了 Transformer 模型架构的高级概念。

        按照教程进行操作,这是我的 Github 存储库

        在这篇博客中,我们将讨论:

1. Introduction- Explanation of hyperparameters in GPT model2. Step 1: Data Preparation Including Tokenization- Loading and preprocessing data for training- Tokenization and data splitting3. Step 2: Building a Simple Bigram Language Model- Mini-batching for efficient training- Defining the Bigram Language Model- Training procedure using Adam optimizer4. Step 3: Adding Positional Encodings- Incorporating positional information5. Step 4: Incorporating AdamW Optimizer- Introduction to the AdamW optimizer- Training the model with AdamW6. Step 5: Introducing Self-Attention- Explanation of Self-Attention mechanism- Dot Product vs Sclaed Dot Product- Implementation of Self-Attention in PyTorch7. Step 6: Transitioning to Multi-Head Self-Attention- Implementing Multi-Head Self-Attention- Combining multiple attention heads8. Step 7: Adding Feed-Forward Networks- Implementing Feed-Forward neural network with ReLU activation9. Step 8: Formulating Blocks (Nx in Model)- Stacking Transformer blocks- Explanation of the components within a Transformer block10. Step 9: Adding Residual Connections- Enhancing the Block class with residual connections11. Step 10: Incorporating Layer Normalization- Adding Layer Normalization to the model12. Step 11: Implementing Dropout- Introduction of Dropout for regularization13. Step 12: Scaling Model - NVIDIA CUDA for GPU- Instructions for scaling up the model using GPU accelerationThis blog provides a comprehensive overview of building a language model, starting from data preprocessing and tokenization, through the implementation of core Transformer components like self-attention, multi-head attention, and feed-forward networks, all the way to optimizing and scaling the model using GPU acceleration.

图片来源:Attention is All You Need Paper

二、GPT 模型中的超参数

使用以下超参数调整模型的性能:

  • batch_size:训练期间并行处理的序列数
  • block_size:模型正在处理的序列的长度
  • d_model:模型中的特征数(嵌入的大小)
  • d_k:每个注意力头的特征数。
  • num_iter:模型将运行的训练迭代总数
  • Nx:模型中变压器块或层数。
  • eval_interval:计算和评估模型损失的时间间隔
  • lr_rate:Adam 优化器的学习率
  • device:如果兼容的 GPU 可用,则自动设置为,否则默认为 。'cuda''cpu'
  • eval_iters:平均评估损失的迭代次数
  • h:多头注意力机制中的注意力头数
  • dropout_rate:训练期间用于防止过拟合的辍学率

这些超参数经过精心选择,以平衡模型在不过度拟合的情况下从数据中学习的能力,并有效地管理计算资源。

三、解码器只是我们 GPT 的 Transformer 的一部分

OpenAI 于 2018 年发表的原始论文 [链接在这里]

3.1 CPU 与 GPU 模型示例

图片来源: Tatev Aslanyan

3.1 第 1 步:数据准备,包括标记化

  • 负载数据:open('./GPT Series/input.txt', 'r', encoding = 'utf-8')
  • BuildVocab:使用 和 创建词汇词典。chars_to_intint_to_chars
  • CharacterTokenization:使用函数将字符串转换为整数,然后使用函数转换回整数。encodedecode
  • DataSplit:将数据集划分为训练集 () 和验证集 ()。train_datavalid_data

3.2 第 2 步:构建简单的 Bigram 语言模型(初始模型)

  • 小批量:该函数以小批量的形式准备数据进行训练。get_batch
  • BigramModel:定义类中的模型体系结构。BigramLM
  • TrainModel:使用 Adam 优化器和损失估计概述训练过程。

3.2.1 小批量技术

        小批量是机器学习中的一种技术,其中训练数据被分成小批量。在模型训练期间,每个小批次都单独处理。这种方法有助于:

  • 有效利用内存:通过不一次将整个数据集加载到内存中,它可以减少计算开销。
  • 更快的收敛:与单独处理每个数据点相比,批量处理数据可以带来更快的收敛。
  • 改进泛化:小批量在训练过程中引入噪声,这可以帮助模型更好地泛化到看不见的数据。
# Function to create mini-batches for training or validation.
def get_batch(split):# Select data based on training or validation split.data = train_data if split == "train" else valid_data# Generate random start indices for data blocks, ensuring space for 'block_size' elements.ix = torch.randint(len(data)-block_size, (batch_size,))# Create input (x) and target (y) sequences from data blocks.x = torch.stack([data[i:i+block_size] for i in ix])y = torch.stack([data[i+1:i+block_size+1] for i in ix])# Move data to GPU if available for faster processing.x, y = x.to(device), y.to(device)return x, yay

3.2.2 如何选择您的批量大小?

        批量大小的选择对于训练 Baby GPT 等神经网络模型至关重要。以下是对其重要性的简要说明:

  1. 计算效率:更大的批量大小可以更有效地利用 GPU 的并行处理功能,从而加快训练速度。但是,超大批处理可能会超出 GPU 内存限制。
  2. 学习动态:较小的批次通常会为模型提供更频繁的更新,从而可能导致更快的收敛。但是,它们也可能导致训练不太稳定。
  3. 泛化:在批量大小和模型的泛化能力之间需要权衡。较小的批次往往可以更好地泛化,但可能会导致更嘈杂的梯度估计。
  4. 资源约束:最终,批处理大小的选择也受到可用计算资源的限制。较大的批处理需要更多的内存和计算能力。
  5. 模型性能:批量大小的选择会影响模型的最终性能。通常需要进行试验,以找到平衡训练速度、稳定性和模型准确性的最佳大小。

图片来源: Tatev Aslanyan

3.2.3 估计损失(负损失可能性或交叉熵)

        该函数计算模型在指定迭代次数 (eval_iters) 内的平均损失。它用于在不影响其参数的情况下评估模型的性能。estimate_loss

        该模型设置为评估模式,以禁用某些层(如流失),以实现一致的损失计算。计算训练和验证数据的平均损失后,模型将恢复到训练模式。此功能对于监控培训过程并在必要时进行调整至关重要。

@torch.no_grad()
def estimate_loss():result = {}# setting model in evaluation statemodel.eval()for split in ['train', 'valid_date']:losses = torch.zeros(eval_iters)for e in range(eval_iters):X,Y = get_batch(split)logits, loss = model(X,Y)# storing each iterations losslosses[e] = loss.item()result[split] = losses.mean()# setting back to training statemodel.train()return result

3.3 第 3 步:添加位置编码

        位置编码:使用类中的位置信息向模型添加位置信息。我们将位置编码添加到角色的嵌入中,就像在转换器架构中一样。positional_encodings_tableBigramLM

3.4 第 4 步:合并 Adam W Optimizer

        在这里,我们设置并使用 AdamW 优化器在 PyTorch 中训练神经网络模型。Adam 优化器在许多深度学习场景中都受到青睐,因为它结合了随机梯度下降的另外两个扩展的优点:AdaGrad 和 RMSProp。

        Adam 计算每个参数的自适应学习率。除了存储过去平方梯度的指数衰减平均值(如 RMSProp)之外,Adam 还保留了过去梯度的指数衰减平均值,类似于动量。

        这使优化器能够调整神经网络每个权重的学习速率,从而在复杂的数据集和架构上进行更有效的训练。

        AdamW 修改了将权重衰减合并到优化过程中的方式,解决了原始 Adam 优化器的一个问题,即权重衰减与梯度更新没有很好地分离,导致正则化的应用欠佳。

        使用 AdamW 有时可以提高训练性能,并泛化到看不见的数据。我们之所以选择 AdamW,是因为它能够比标准的 Adam 优化器更有效地处理权重衰减,从而有可能改进模型训练和泛化。

optimizer = torch.optim.AdamW(model.parameters(), lr = lr_rate)
for iter in range(num_iter):# estimating the loss for per X intervalif iter % eval_interval == 0:losses = estimate_loss()print(f"step {iter}: train loss is {losses['train']:.5f} and validation loss is {losses['valid_date']:.5f}")# sampling a mini batch of dataxb, yb = get_batch("train")# Forward Pass logits, loss = model(xb, yb)# Zeroing Gradients: Before computing the gradients, existing gradients are reset to zero. This is necessary because gradients accumulate by default in PyTorch.optimizer.zero_grad(set_to_none=True)# Backward Pass or Backpropogation: Computing Gradientsloss.backward()# Updating the Model Parametersoptimizer.step()

3.5 第 5 步:引入自我关注

        自注意力是一种机制,允许模型以不同的方式权衡输入数据不同部分的重要性。它是 Transformer 架构的关键组件,使模型能够专注于输入序列的相关部分进行预测。

  • 点积注意: 一种简单的注意力机制,它根据查询和键之间的点积计算加权值的总和。
  • 缩放点积注意事项:对点积注意力的改进,通过键的维度缩小点积,防止梯度在训练期间变得太小。
  • OneHeadSelfAttention:实现单头自注意力机制,允许模型关注输入序列的不同位置。该课程展示了注意力机制及其缩放版本背后的直觉。SelfAttention

        Baby GPT 项目中的每个相应模型都以前一个模型为基础,从自我注意力机制背后的直觉开始,然后是点积和缩放点积注意力的实际实现,最终集成了一个单头自我注意力模块。

class SelfAttention(nn.Module):"""Self Attention (One Head)"""""" d_k = C """def __init__(self, d_k):super().__init__() #superclass initialization for proper torch functionality# keys self.keys = nn.Linear(d_model, d_k, bias = False)# queriesself.queries = nn.Linear(d_model, d_k, bias = False)# valuesself.values = nn.Linear(d_model, d_k, bias = False)# buffer for the modelself.register_buffer('tril', torch.tril(torch.ones(block_size, block_size)))def forward(self, X):"""Computing Attention Matrix"""B, T, C = X.shape# Keys matrix KK = self.keys(X) # (B, T, C)# Query matrix QQ = self.queries(X) # (B, T, C)# Scaled Dot Productscaled_dot_product = Q @ K.transpose(-2,-1) * 1/math.sqrt(C) # (B, T, T)# Masking upper trianglescaled_dot_product_masked = scaled_dot_product.masked_fill(self.tril[:T, :T]==0, float('-inf'))# SoftMax transformationattention_matrix = F.softmax(scaled_dot_product_masked, dim=-1) # (B, T, T)# Weighted AggregationV = self.values(X) # (B, T, C)output =  attention_matrix @ V # (B, T, C)retur

        该类表示 Transformer 模型的基本构建块,用单个磁头封装自注意力机制。以下是对其组件和流程的深入了解:SelfAttention

  • 初始化:构造函数初始化键、查询和值的线性层,所有层都具有维度。这些线性变换将输入投射到不同的子空间中,以便进行后续的注意力计算。__init__(self, d_k)d_k
  • 缓冲区:将较低的三角矩阵注册为不被视为模型参数的持久缓冲区。该矩阵用于注意力机制中的掩码,以防止在每个计算步骤中考虑未来位置(在解码器自注意力中很有用)。self.register_buffer('tril', torch.tril(torch.ones(block_size, block_size)))
  • 前向传球:该方法定义了在每次调用自注意力模块时执行的计算forward(self, X)

3.6 第 6 步:过渡到多头自我关注

图片来源:Attention is All You Need Paper

MultiHeadAttention:组合类中多个磁头的输出。MultiHeadAttention 类是自注意力机制的扩展实现,具有上一步中的一个磁头,但现在多个注意力磁头并行运行,每个注意力磁头都专注于输入的不同部分。SelfAttentionMultiHeadAttention

class MultiHeadAttention(nn.Module):"""Multi Head Self Attention""""""h: #heads"""def __init__(self, h, d_k):super().__init__()# initializing the heads, we want h times attention heads wit size d_kself.heads = nn.ModuleList([SelfAttention(d_k) for _ in range(h)])# adding linear layer to project the concatenated heads to the original dimensionself.projections = nn.Linear(h*d_k, d_model)# adding dropout layerself.droupout = nn.Dropout(dropout_rate)def forward(self, X):# running multiple self attention heads in parallel and concatinate them at channel dimensioncombined_attentions = torch.cat([h(X) for h in self.heads], dim = -1)# projecting the concatenated heads to the original dimensioncombined_attentions = self.projections(combined_attentions)# applying dropoutcombined_attentions = self.droupout(combined_attentions)return combined_attentions

3.7 步骤 7:添加前馈网络

        FeedForward:在类中实现具有 ReLU 激活的前馈神经网络。将这种全连接的前馈添加到我们的模型中,就像在原始 Transformer 模型中一样。FeedForward

class FeedForward(nn.Module):"""FeedForward Layer with ReLU activation function"""def __init__(self, d_model):super().__init__()self.net = nn.Sequential(# 2 linear layers with ReLU activation functionnn.Linear(d_model, 4*d_model),nn.ReLU(),nn.Linear(4*d_model, d_model),nn.Dropout(dropout_rate))def forward(self, X):# applying the feedforward layerreturn self.net(X)

3.8 第 8 步:制定模块(模型中的 Nx)

        变压器块: 使用类堆叠变压器块以创建更深层次的网络架构。Block

        深度和复杂性:在神经网络中,深度是指处理数据的层数。每个额外的层(或块,在Transformers的情况下)允许网络捕获输入数据的更复杂和抽象的特征。

        顺序处理:每个 Transformer 模块处理其前一个模块的输出,逐渐建立对输入的更复杂的理解。这种顺序处理允许网络开发数据的深度分层表示。变压器块的组件

  • 多头注意力:C对每个块进行集中,它通过同时关注序列的不同部分来处理输入。这种并行处理是模型理解复杂数据关系的关键。
  • 前馈网络:它在关注后进一步处理数据,增加了另一层复杂性。
  • 残余连接:它们有助于维持整个网络的信息流,防止输入数据通过层丢失,并有助于解决梯度消失问题。
  • 图层归一化: 在每个主要组件之前应用,它可以稳定学习过程,确保跨深层的顺利训练。
class Block(nn.Module):"""Multiple Blocks of Transformer"""def __init__(self, d_model, h):super().__init__()d_k = d_model // h# Layer 4: Adding Attention layerself.attention_head = MultiHeadAttention(h, d_k) # h heads of d_k dimensional self-attention# Layer 5: Feed Forward layerself.feedforward = FeedForward(d_model)# Layer Normalization 1self.ln1 = nn.LayerNorm(d_model)# Layer Normalization 2self.ln2 = nn.LayerNorm(d_model)# Adding additional X for Residual Connectionsdef forward(self,X):X = X + self.attention_head(self.ln1(X))X = X + self.feedforward(self.ln2(X))return X

3.9 步骤 9:添加残差连接

        ResidualConnections:增强班级以包含剩余连接,提高学习效率。残差连接,也称为跳跃连接,是深度神经网络设计中的一项关键创新,尤其是在 Transformer 模型中。它们解决了训练深度网络的主要挑战之一:梯度消失问题。Block

# Adding additional X for Residual Connectionsdef forward(self,X):X = X + self.attention_head(self.ln1(X))X = X + self.feedforward(self.ln2(X))return X

3. 10 第 10 步:合并图层归一化

LayerNorm:使用 Block 类中的 nn.LayerNorm(d_model) 将层归一化添加到 Transformer.Normalizing 层输出。

class LayerNorm:def __init__(self, dim, eps=1e-5):self.eps = epsself.gamma = torch.ones(dim)self.beta = torch.zeros(dim)def __call__(self, x):# orward pass calculatonxmean = x.mean(1, keepdim=True)  # layer meanxvar = x.var(1, keepdim=True)  # layer variancexhat = (x - xmean) / torch.sqrt(xvar + self.eps)  # normalize to unit varianceself.out = self.gamma * xhat + self.beta      return self.outdef parameters(self):return [self.gamma, self.beta]

3.11 第 11 步:实现辍学

Dropout:作为正则化方法添加到 and 层中,以防止过拟合。我们将辍学添加到:SelfAttentionFeedForward

  • 自我关注类
  • 多头自我关注
  • 前馈

3.12 第 12 步:扩展模型:适用于 GPU 的 NVIDIA CUDA

ScaleUp:通过扩展 、 、 、 和 来增加模型的复杂性。您将需要 CUDA 工具包以及配备 NVIDIA GPU 的机器来训练和测试这个更大的模型。batch_sizeblock_sized_modeld_kNx

如果要试用 CUDA 进行 GPU 加速,请确保安装了支持 CUDA 的相应 PyTorch 版本。

import torch
torch.cuda.is_available()

您可以通过在 PyTorch 安装命令中指定 CUDA 版本来执行此操作,例如在命令行中:

pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu113

四、结论

        以上论文是塔特夫·卡伦·阿斯兰扬的研究记录。因为来不及验证实践活动。这里暂时作为一个记录,等待日后研究和发现。论文的参考地址为:

BabyGPT: Build Your Own GPT Large Language Model from Scratch | by Tatev Karen Aslanyan | LunarTech

    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/1516.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 文件描述符

1、文件描述符 程序和进程的区别: 1、test.c:是一个程序,只占用磁盘空间,不占用内存空间 2、可执行文件 test:是一个程序,只占用磁盘空间,不占用内存空间 3、启动 可执行文件 test&#xff…

强固型工业电脑在码头智能化,龙门吊/流机车载电脑的行业应用

码头智能化行业应用 对码头运营来说,如何优化集装箱从船上到码头堆场到出厂区的各个流程以及达到提高效率。 降低成本的目的,是码头营运获利最重要的议题。为了让集装箱码头客户能够安心使用TOS系统来调度指挥码头上各种吊车、叉车、拖车和人员&#xf…

第一届 _帕鲁杯_ - CTF挑战赛

Mis 签到 题目附件: 27880 30693 25915 21892 38450 23454 39564 23460 21457 36865 112 108 98 99 116 102 33719 21462 21069 27573 102 108 97 103 20851 27880 79 110 101 45 70 111 120 23433 20840 22242 38431 22238 22797 112 108 98 99 116 102 33719 2…

matplotlib从起点出发(15)_Tutorial_15_blitting

0 位图传输技术与快速渲染 Blitting,即位图传输、块传输技术是栅格图形化中的标准技术。在Matplotlib的上下文中,该技术可用于(大幅度)提高交互式图形的性能。例如,动画和小部件模块在内部使用位图传输。在这里&#…

揭开ChatGPT面纱(3):使用OpenAI进行文本情感分析(embeddings接口)

文章目录 一、embeddings接口解析二、代码实现1.数据集dataset.csv2.代码3.运行结果 openai版本1.6.1 本系列博客源码仓库:gitlab,本博客对应文件夹03 在这一篇博客中我将使用OpenAI的embeddings接口判断21条服装评价是否是好评。 首先来看实现思路&am…

Llama3新一代 Llama模型

最近,Meta 发布了 Llama3 模型,从发布的数据来看,性能已经超越了 Gemini 1.5 和 Claud 3。 Llama 官网说,他们未来是要支持多语言和多模态的,希望那天赶紧到来。 未来 Llama3还将推出一个 400B大模型,目前…

计算机网络——数据链路层(介质访问控制)

计算机网络——数据链路层(介质访问控制) 介质访问控制静态划分信道动态划分信道ALOHA协议纯ALOHA(Pure ALOHA)原理特点 分槽ALOHA(Slotted ALOHA)原理特点 CSMA协议工作流程特点 CSMA-CD 协议工作原理主要…

JVM虚拟机(十二)ParallelGC、CMS、G1垃圾收集器的 GC 日志解析

目录 一、如何开启 GC 日志?二、GC 日志分析2.1 PSPO 日志分析2.2 ParNewCMS 日志分析2.3 G1 日志分析 三、GC 发生的原因3.1 Allocation Failure:新生代空间不足,触发 Minor GC3.2 Metadata GC Threshold:元数据(方法…

【数据结构|C语言版】算法效率和复杂度分析

前言1. 算法效率2. 大O的渐进表示法3. 时间复杂度3.1 时间复杂度概念3.2 时间复杂度计算举例 4. 空间复杂度4.1 空间复杂度的概念4.2 空间复杂度计算举例 5. 常见复杂度对比结语 ↓ 个人主页:C_GUIQU 个人专栏:【数据结构(C语言版&#xff09…

Kafka源码分析(四) - Server端-请求处理框架

系列文章目录 Kafka源码分析-目录 一. 总体结构 先给一张概览图: 服务端请求处理过程涉及到两个模块:kafka.network和kafka.server。 1.1 kafka.network 该包是kafka底层模块,提供了服务端NIO通信能力基础。 有4个核心类:…

【Django】django.core.exceptions.AppRegistryNotReady: Apps aren‘t loaded yet.

其中django后台manage.py入口程序报错,检索很多问题解决方案,这里记录下个人问题原因 1.django启动异常问题详情 django.core.exceptions.AppRegistryNotReady: Apps aren’t loaded yet. 2.问题原因 Python第三方包安装版本不一致或缺少依赖包&…

Flink窗口机制

1.窗口的概念 时间是为窗口服务的。窗口是什么?为什么会有窗口呢? (1)Flink要处理的数据,一般是从Kafka过来的流式数据,如果只是单纯地统计流的数据量,是没办法统计的。 (2&#xff…

C语言程序设计:简易版的printf函数实现

简易版的printf函数实现 功能说明 (1)使用putchar函数、va可变参完成printf函数基本功能的实现; (2)函数说明: 实现对下列数据类型的输出,并返回成功输出打印的字符个数: 整数&…

在CSDN创作了6个月,我收获了什么?文末送书~

作者主页:阿玥的小东东主页! 正在学习:python和C/C 期待大家的关注哦 目录 一次很好的机会,让我开始了CSDN之旅 首先来看看我的几位领路人 创作动力 1W粉丝 在CSDN我收获了什么? 很高的展现量 认证创作者身份 社…

【Linux】系统安全及应用

目录 一、账号安全基本措施 1.系统账号清理 2.密码安全控制 3.历史命令安全管理 4.限制su切换用户 1)将信任的用户加入到wheel组中 2)修改su的PAM认证配置文件 5.ssh远程登录输入三次密码错误则锁定用户 二、Linux中的PAM安全认证 1.su命令的…

Redis入门到通关之数据结构解析-动态字符串SDS

文章目录 Redis数据结构-动态字符串动态扩容举例二进制安全SDS优点与C语言中的字符串的区别 Redis数据结构-动态字符串 我们都知道 Redis 中保存的Key是字符串,value 往往是字符串或者字符串的集合。可见字符串是 Redis 中最常用的一种数据结构。 不过 Redis 没有…

Android Studio超级详细讲解下载、安装配置教程(建议收藏)

博主介绍:✌专注于前后端、机器学习、人工智能应用领域开发的优质创作者、秉着互联网精神开源贡献精神,答疑解惑、坚持优质作品共享。本人是掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战,深受全网粉丝喜爱与支持✌有…

贪吃蛇游戏实现(VS编译环境)

贪吃蛇游戏 🥕个人主页:开敲🍉 🔥所属专栏:C语言🍓 🌼文章目录🌼 0. 前言 1. 游戏背景 2. 实现后游戏画面展示 3. 技术要求 4. Win32 API介绍 4.1 Win32 API 4.2 控制台程序 4.…

Java之类和对象

一面向对象的初步认知 1.什么是面向对象 Java是一门纯面向对象的语言(Object Oriented Program,简称OOP),在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。用面向对象的思想…

嵌入式物联网实战开发笔记-乐鑫ESP32开发环境ESP-IDF搭建【doc.yotill.com】

乐鑫ESP32入门到精通项目开发参考百例下载: 链接:百度网盘 请输入提取码 提取码:4e33 3.1 ESP-IDF 简介 ESP-IDF(Espressif IoT Development Framework)是乐鑫(Espressif Systems)为 ESP 系列…