使用 PyTorch 构建自定义 GPT

一、介绍

       介绍大模型,首先考虑一下使用 ChatGPT、Bing Chat 或 Bard 。您是否想过拥有自己的 ChatGPT 会是什么样子?想象一下创建自己的 GPT 模型的兴奋程度。这确实是一种难以置信的感觉!

为了开始构建自定义 GPT 的旅程,让我们仔细看看 GPT 的运作方式。

二、了解 GPT

        GPT 是Generative Pre-trained Transformer的缩写,是一种充当解码器的 Transformer 模型。它已在 BookCorpus 和 WebText 等广泛数据集上进行了预训练。为了更深入地了解 GPT,有必要探索各种类型的变压器并深入研究纯解码器变压器的运行机制。

三、变形金刚解码

        Transformer 是一种深度神经网络架构,专门用于生成类似人类的文本。LLM 模型基于三种类型的变压器架构进行开发,有些模型正在其基础上进行开发。

  • 仅编码器变压器:仅编码器变压器是变压器架构的一种特殊变体,仅专注于理解和编码输入序列的任务。它们是仅使用 Transformer 模型的编码器部分的神经网络。仅编码器转换器用于各种自然语言处理任务,例如文本分类、命名实体识别和情感分析。仅编码器 Transformer 最突出的例子是BERT(来自 Transformers 的双向编码器表示)
  • 仅解码器变压器:仅解码器变压器是一种仅使用变压器模型的解码器组件的变压器架构。这些模型用于需要解码器组件的任务,例如文本生成、机器翻译和摘要。解码器组件负责从输入序列生成输出序列。仅解码器变压器最突出的例子是GPT(生成预训练变压器)
  • Encoder-Decoder Transformers(Cross-Attention):基于 Transformer 的 Encoder-Decoder 模型是一种神经网络架构,广泛应用于语言翻译、文本摘要等 NLP 任务中。这就是原来的变压器。该架构由两个主要组件组成:编码器和解码器。编码器处理输入序列并生成输入的连续表示,而解码器则根据编码器的表示生成输出序列。基于Transformer的编码器-解码器模型已在T5、Bart、Pegasus、ProphetNet、Marge等许多预训练模型中使用。

四、了解 GPT 架构:变压器块

Transformer 块具有三个目标:准备、丰富和预测。

4.1 准备阶段:

变压器模型的初始输入由一系列单词标记组成,然后将其转换为单词嵌入。这些嵌入通过位置编码来丰富以传达其位置信息。

  • 位置编码:在 Transformer 模型中,位置编码用于定义序列中实体的位置,确保每个位置都有不同的表示。变压器本身缺乏对输入序列顺序的固有理解。序列内特定位置 (p)和嵌入空间内维度 (i)位置编码是通过正弦和余弦函数的组合来实现的。

这里,“d”表示词嵌入的维度。这些函数为各个单词位置生成不同的代码。这些代码可以扩展到训练阶段未遇到的序列长度。下一步是将这个新序列输入到变压器块中,其中每个元素都被视为密集向量。

4.2 浓缩阶段:

        丰富包括多头注意力、位置前馈神经网络、残差连接和层归一化。

        多头注意力:注意力用于评估单词之间的重要性和联系。它通过合并更多上下文和逻辑来增强矢量表示。注意力依赖于三个向量:查询向量、键向量和值向量,它们源自初始层,即词嵌入向量。查询向量对应于当前标记,键向量包含序列中的所有其他标记,并且值向量包含序列中的所有标记。

        在自注意力过程中,我们首先计算查询向量和关键向量的缩放积以获得注意力分数。接下来,通过 softmax 函数处理该注意力分数,产生一组范围从0 到 1的注意力权重。然后,每个值向量按其各自的注意力权重进行缩放,最后,它们的总和产生自注意力层的输出。

        术语“多头注意力”源自其多(三个)注意力层的组成。

  • 早期注意: 在这种情况下,短程依赖涉及相邻标记之间的关系,例如词序、词性和基本句子结构,而不需要逐步方法。
  • 中间注意力:它包含输入序列的更广泛的上下文,其中可能包括语义信息、含义、短语之间的关系以及句子中不同单词的角色。
  • 后期注意力:它结合较低层以产生有凝聚力且上下文相关的结果,包括高级抽象、话语结构、情感分析和复杂的长期联系

        Position-wise Feed-Forward Neural Network:该组件的作用是获取序列中每个元素的注意力阶段收集的信息,并将其转换为更丰富的状态。它促进序列中每个元素的非线性变换,并在后续层中继续构建自身。

        残余连接:残余连接促进信息从较早的层直接流向较晚的层。它们在解决深度神经网络中经常遇到的梯度消失问题方面发挥着至关重要的作用。

        层标准化:它不是根据批次标准化输入,而是跨特征执行标准化。这有助于通过确保一致的输入分布来稳定网络的训练,这对于具有不同序列长度的任务至关重要。

4.3 预测阶段:

        在这个过程中,线性函数和softmax函数发挥了至关重要的作用。首先,我们有一系列来自最后一个转换器块的上下文感知向量。该序列中的每个向量代表一个输入标记,并受到其与所有其他标记的交互的影响。为了将向量的输出序列投影到维度为N_w的空间(其中N_w词汇表大小),我们使用线性函数。然后,我们将 softmax 函数应用于这些投影向量,以创建词汇表上的概率分布。这种分布有助于我们预测序列中的下一个标记。

五、变压器中的重要变量

5.1 输入变量

  • 词汇量:这是指模型可以识别的唯一标记的数量。
  • 嵌入/模型大小:表示词嵌入的维度,也称为隐藏大小。
  • 序列/上下文长度:这表示模型一次性可以处理的最大标记数。

5.2 内部变量

  • 注意力头计数:在多头注意力中,输入被分为特定数量的注意力头。
  • 中间层大小:前馈网络中中间层的大小通常大于嵌入大小。
  • 层数:这对应于变压器块的数量。

5.3 训练变量

  • 批量大小:它是指在训练期间一次前向传递中一起处理多少个示例。
  • 训练的令牌:它表示模型在训练期间遇到的令牌总数,通常比纪元数更频繁地报告。

六、自定义类似 GPT 的模型

使用 Pytorch,我们将构建自定义的类似 GPT 的模型。首先我们将导入所有必需的库。

# Import the necessary libraries
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
import time
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

        我们将定义 DecoderBlock,它是变压器块的单层。我们将定义解码器块所需的超参数:

  • d_model:输入向量的维度。
  • num_heads:多头注意力机制中头的数量。
  • ff_hidden_​​layer:前馈隐藏层的维度。
  • 辍学率:辍学率。

前向方法需要两个输入

  • x:输入张量。
  • target_mask:防止对某些位置的关注的掩码。
# Decoder Block
class DecoderBlock(nn.Module):def __init__(self, d_model, num_heads, ff_hidden_layer, dropout):super(DecoderBlock, self).__init__()self.self_attention = nn.MultiheadAttention(d_model, num_heads, dropout=dropout)self.norm1 = nn.LayerNorm(d_model)self.dropout1 = nn.Dropout(dropout)self.linear1 = nn.Linear(d_model, ff_hidden_layer)self.linear2 = nn.Linear(ff_hidden_layer, d_model)self.norm2 = nn.LayerNorm(d_model)self.dropout2 = nn.Dropout(dropout)def forward(self, x,target_mask):attn_output, _ = self.self_attention(x, x, x, attn_mask=target_mask)x = x + self.dropout1(attn_output)x = self.norm1(x)ff_output = self.linear2(F.relu(self.linear1(x)))x = x + self.dropout2(ff_output)x = self.norm2(x)return x

        现在,让我们创建 PositionalEncoding 类,它应用唯一的位置编码来为模型提供有关序列中标记的相对或绝对位置的信息。

# Positional Encodingclass PositionalEncoding(nn.Module):def __init__(self, d_model, dropout=0.1, max_len=5000):super(PositionalEncoding, self).__init__()self.dropout = nn.Dropout(p=dropout)pe = torch.zeros(max_len, d_model)position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))pe[:, 0::2] = torch.sin(position * div_term)pe[:, 1::2] = torch.cos(position * div_term)pe = pe.unsqueeze(0).transpose(0, 1)self.register_buffer("pe", pe)def forward(self, x):x = x + self.pe[:x.size(0), :]return self.dropout(x)

我们需要屏蔽解码器的输入,以防止关注未来的位置。

def generate_square_subsequent_mask(sz):"""Generate a mask to prevent attention to future positions."""mask = (torch.triu(torch.ones(sz, sz)) == 1).transpose(0, 1)mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))return maskmask = generate_square_subsequent_mask(sz=5)plt.figure(figsize=(5,5))
sns.heatmap(mask, cmap="crest", cbar=False, square=True)
plt.title("Mask for Transformer Decoder")
plt.show()

        现在,我们来描述完整的 Transformer 解码器,包括初始嵌入层、单独的 Transformer 解码器块以及最终的线性层和 Softmax 层。

        在 TransformerDecoder 中,线性层执行线性变换,映射输出维度以匹配词汇表大小。随后,应用softmax 层将输出转换为词汇表上的概率分布。整个过程被封装在转发方法中,该方法指示通过解码器的数据流。

# Transformer Decoderclass TransformerDecoder(nn.Module):def __init__(self, vocab_size, d_model, num_heads, ff_hidden_layer, dropout):super(TransformerDecoder, self).__init__()self.embedding = nn.Embedding(vocab_size, d_model)self.pos_encoder = PositionalEncoding(d_model, dropout)self.transformer_block = DecoderBlock(d_model, num_heads, ff_hidden_layer, dropout)self.linear = nn.Linear(d_model, vocab_size)self.softmax = nn.LogSoftmax(dim=-1)def forward(self, x):x = self.embedding(x)x = self.pos_encoder(x)tgt_mask = generate_square_subsequent_mask(x.size(0))x = self.transformer_block(x,tgt_mask)output = self.linear(x)output = self.softmax(output)return output

        首先,让我们建立初始解码器。为此,我们首先概述我们的超参数。接下来,我们将构造一个表示批量大小和上下文长度的张量。之后,我们将继续对模型进行前向传播。生成的输出将采用张量的形式。最后,我们将使用“argmax”函数来提取预测的单词索引。

# Define the hyperparameters
vocab_size     = 1000
d_model        = 512
num_heads      = 1
ff_hidden_layer  = 2*d_model
dropout        = 0.1
num_layers     = 10
context_length = 50
batch_size     = 1
# Initialize the model
model = TransformerDecoder(vocab_size, d_model, num_heads, ff_hidden_layer, dropout)# Create a tensor representing batch size and context length
input_tensor = torch.randint(0, vocab_size, (context_length, batch_size))# Forward pass through the model
output = model(input_tensor)print(output.shape)  # To get the predicted word indices, we can use the `argmax` function
predicted_indices = output.argmax(dim=-1)print(predicted_indices.shape)

现在,计算参数。

def count_parameters(model):return sum(p.numel() for p in model.parameters() if p.requires_grad)print(f"The model has {count_parameters(model):,} trainable parameters")

为了查看输出,我们会将对数概率转换为 概率,并将输出张量转换为 numpy 数组。

# Convert the log probabilities to probabilities
distribution = torch.exp(output[0, 0, :])# Convert the output tensor to numpy array
distribution = distribution.detach().numpy()# Plot the distribution
plt.figure(figsize=(12, 6))
plt.bar(np.arange(vocab_size), distribution)
plt.xlabel("Word Index")
plt.ylabel("Probability")
plt.title("Output Distribution over Vocabulary")
plt.show()

现在制作一个多层解码器,这将以层数作为参数。

class MultiLayerTransformerDecoder(nn.Module):def __init__(self, vocab_size, d_model, num_heads, ff_hidden_layer, dropout, num_layers):super(MultiLayerTransformerDecoder, self).__init__()self.embedding = nn.Embedding(vocab_size, d_model)self.pos_encoder = PositionalEncoding(d_model, dropout)self.transformer_blocks = nn.ModuleList([DecoderBlock(d_model, num_heads, ff_hidden_layer, dropout)for _ in range(num_layers)])self.linear = nn.Linear(d_model, vocab_size)self.softmax = nn.LogSoftmax(dim=-1)def forward(self, x):x = self.embedding(x)x = self.pos_encoder(x)for transformer_block in self.transformer_blocks:target_mask = generate_square_subsequent_mask(x.size(0))x = transformer_block(x,target_mask)output = self.linear(x)output = self.softmax(output)return output

遵循相同的过程。

# Define the hyperparameters
vocab_size     = 10000
d_model        = 2048
num_heads      = 2
ff_hidden_layer  = 8*d_model
dropout        = 0.1
num_layers     = 20
context_length = 1000
batch_size     = 1# Create our input to the model to process
input_tensor = torch.randint(0, vocab_size, (context_length, batch_size))# Initialize the model with `num_layer` layers
model = MultiLayerTransformerDecoder(vocab_size, d_model, num_heads, ff_hidden_layer, dropout, num_layers)# Print the number of trainable parameters
print(f"The model has {count_parameters(model):,} trainable parameters")# Let's use the same input_tensor from the previous example
output = model(input_tensor)# Convert the log probabilities to probabilities for the first sequence in the batch and the first position in the sequence
distribution = torch.exp(output[0, 0, :])# Convert the output tensor to numpy array
distribution = distribution.detach().numpy()# Now plot the distribution
plt.figure(figsize=(12, 6))
plt.bar(np.arange(vocab_size), distribution)
plt.xlabel("Word Index")
plt.ylabel("Probability")
plt.title("Output Distribution over Vocabulary")
plt.show()

你可以看到你的模型。

MultiLayerTransformerDecoder((embedding): Embedding(10000, 2048)(pos_encoder): PositionalEncoding((dropout): Dropout(p=0.1, inplace=False))(transformer_blocks): ModuleList((0-19): 20 x DecoderBlock((self_attention): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=2048, out_features=2048, bias=True))(norm1): LayerNorm((2048,), eps=1e-05, elementwise_affine=True)(dropout1): Dropout(p=0.1, inplace=False)(linear1): Linear(in_features=2048, out_features=16384, bias=True)(linear2): Linear(in_features=16384, out_features=2048, bias=True)(norm2): LayerNorm((2048,), eps=1e-05, elementwise_affine=True)(dropout2): Dropout(p=0.1, inplace=False)))(linear): Linear(in_features=2048, out_features=10000, bias=True)(softmax): LogSoftmax(dim=-1)
)

        现在,您必须使用任何您想要用自己的仅解码器变压器模型进行实验的数据集,并且您将拥有自己的 GPT。玩得开心!(代码阿克里蒂·乌帕迪亚

七、结论

        创建我们自己的类似 GPT 的模型的过程包括理解架构、在代码中实现它以及使用数据集进行实验和微调。

这次旅程让我们能够释放创造力并探索令人兴奋的自然语言处理世界。

构建自定义 GPT 不仅是一项技术成就,而且还邀请您享受乐趣并探索文本生成的无限可能性。

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

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

相关文章

MATLAB和S7-1200PLC OPC通信(激活S7-1200PLC OPC UA服务器)

MATLAB和SMART PLC OPC通信请参考下面文章博客: MATLAB和西门子SMART PLC OPC通信-CSDN博客文章浏览阅读123次。西门子S7-200SMART PLC OPC软件的下载和使用,请查看下面文章Smart 200PLC PC Access SMART OPC通信_基于pc access smart的opc通信_RXXW_Dor的博客-CSDN博客OPC是…

通讯网关软件032——利用CommGate X2OPC实现OPC客户端访问Modbus TCP设备

本文介绍利用CommGate X2OPC实现OPC客户端连接Modbus TCP设备。CommGate X2OPC是宁波科安网信开发的网关软件,软件可以登录到网信智汇(http://wangxinzhihui.com)下载。 【案例】如下图所示,SCADA系统上位机、PLC、设备具备Modbus TCP通讯接口&#xff…

机器学习2:决策树--基于信息增益的ID3算法

1.决策树的简介 建立决策树的过程可以分为以下几个步骤: 计算每个特征的信息增益或信息增益比,选择最优的特征作为当前节点的划分标准。根据选择的特征将数据集划分为不同的子集。对每个子集递归执行步骤 1 和步骤 2,直到满足终止条件。构建决策树,并输出。基于信息增益的…

k8s调度约束

List-Watch Kubernetes 是通过 List-Watch的机制进行每个组件的协作,保持数据同步的,每个组件之间的设计实现了解耦。 List-Watch机制 工作机制:用户通过 kubectl请求给 APIServer 来建立一个 Pod。APIServer会将Pod相关元信息存入 etcd 中…

移动路由器Cellular Router命令执行漏洞复现 [附POC]

文章目录 移动路由器Cellular Router命令执行漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 移动路由器Cellular Router命令执行漏洞复现 [附POC] 0x01 前言 免责声明:请勿利用文章…

SQL面试

#(1)请写出要查询员工J开头的名字其工号(EMPNO)及部门名称(DEPTNA)的 SQL语句SELECT e.emp,e.name,d.deptna FROM emp e left join dept d on d.deptno e.deptno where e.name like J%#(2)请写出要查询 Kevin 所在部门的部门代号(DEPTNO)及部门名称(DEPTNA)的 SQL 语句SELECT e…

Pure-Pursuit 跟踪双移线 Gazebo 仿真

Pure-Pursuit 跟踪双移线 Gazebo 仿真 主要参考学习下面的博客和开源项目 自动驾驶规划控制(A*、pure pursuit、LQR算法,使用c在ubuntu和ros环境下实现) https://github.com/NeXTzhao/planning Pure-Pursuit 的理论基础见今年六月…

如何巧妙公布成绩

宝子们,来来来!听说你们对如何公布学生成绩很头疼?别担心,今天就让我来给大家支支招! 1在家长群内发公告,孩子的成绩已出,想知道具体成绩可以私信哦~简单粗暴!关心孩子的…

频谱仪超外差和零中频架构

文章目录 超外差结构零中频结构接收机结构发射机结构 优缺点对比附录相关词汇多次变频的形象解释 参考文献 频谱仪的本质就是一个超宽带、超宽调谐范围、高动态范围的通信接收机, 频谱仪的原理即通信接收机的原理。 遇到高频率高带宽谐波成分复杂的通信信号的话&am…

【网络协议】聊聊HTTPS协议

前面的文章,我们描述了网络是怎样进行传输数据包的,但是网络是不安全的,对于这种流量门户网站其实还好,对于支付类场景其实容易将数据泄漏,所以安全的方式是通过加密,加密方式主要是对称加密和非对称加密。…

【蓝桥杯选拔赛真题08】C++最大值最小值平均值 青少年组蓝桥杯C++选拔赛真题 STEMA比赛真题解析

目录 C/C++最大值最小值平均值 一、题目要求 1、编程实现 2、输入输出 二、算法分析</

gcc/g++使用格式+各种选项,预处理/编译(分析树,编译优化,生成目标代码)/汇编/链接过程(函数库,动态链接)

目录 gcc/g--编译器 介绍 使用格式 通用选项 编译选项 链接选项 程序编译过程 预处理(宏替换) 编译 (生成汇编) 分析树(parse tree) 编译优化 删除死代码 寄存器分配和调度 强度削弱 内联函数 生成目标代码 汇编 (生成二进制代码) 链接(生成可执行文件) 函…

数据抽取+dataworks的使用+ADB的应用

一&#xff0c;大数据处理之数据抽取 1&#xff0c;什么是数据抽取 在大数据领域中&#xff0c;数据抽取是指从原始数据源中提取所需的数据子集或特定数据项的过程&#xff0c; 数据抽取是数据预处理的重要步骤&#xff0c;它为后续的数据分析和建模提供了基础。 2&#xff…

嵌入式linux常用的文件传输方式

做嵌入式就避免不了移植工作&#xff0c;所谓移植就是将交叉编译生成的可执行程序&#xff0c;库&#xff0c;配置文件等传输到开发板上进行工作。 常用传输方式有以下几种&#xff1a;1.串口传输 就是使用串口传输工具rz/sz; 该工具通过串口传输在SRT串口工具…

什么是用户体验测试? 为什么很重要?

在当今数字化时代&#xff0c;用户体验(User Experience&#xff0c;简称UX)已经成为产品成功的关键因素之一。无论是应用程序、网站、硬件设备还是软件&#xff0c;提供出色的用户体验不仅能够吸引更多用户&#xff0c;还能够增加用户满意度&#xff0c;提高品牌忠诚度&#x…

残差网络ResNet

残差网络的提出,是为了解决深度学习中的退化问题。 退化问题指的是随着神经网络层数的增加&#xff0c;网络性能反而逐渐降低的现象。换句话说&#xff0c;当我们不断增加神经网络的层数时&#xff0c;神经网络的训练误差可能会持续下降&#xff0c;但是验证集误差却不断增加&…

前端项目 index.html 中发请求 fetch

想要在前端项目 index.html文件中向后端发起请求&#xff0c;但是引入axios报错&#xff08;我这边会报错&#xff09;&#xff0c;可以使用fetch。 //window.location.origin----获取域名&#xff0c;包括协议、主机号、端口号fetch(window.location.origin "/api/pla…

MPLAB X IDE 仿真打断点提示已中断的断点?

这种中间带裂缝的是无效断点。 原因可能与XC编译器的优化有关&#xff0c;最后生成的汇编与C语言并不是一一对应的(官方给的解释是效率高)。所以这一行C语言转换的汇编代码可能并不在这个位置&#xff0c;也可能与其它汇编合并后根本就没有 我的解决方法是把优化等级调到最低&a…

2014年亚太杯APMCM数学建模大赛A题无人机创造安全环境求解全过程文档及程序

2014年亚太杯APMCM数学建模大赛 A题 无人机创造安全环境 原题再现 20 国集团&#xff0c;又称 G20&#xff0c;是一个国际经济合作论坛。2016 年第 11 届 20 国集团峰会将在中国召开&#xff0c;这是继 APEC 后中国将举办的另一个大型峰会。此类大型峰会&#xff0c;举办城市…

prometheus服务发现

Consul简介 ◼ 一款基于golang开发的开源工具&#xff0c;主要面向分布式&#xff0c;服务化的系统提供服务注册、服务发现和配置管理 的功能 ◼ 提供服务注册/发现、健康检查、Key/Value存储、多数据中心和分布式一致性保证等功能 部署 curl -LO https://releases.hashicorp…