文章目录
搭建conda环境
transformer是一个最基本的模型结构,想跑大模型可以是bert等都是基于transformer。
transformer有很多代码,可以基于py也可以基于tf环境。
安装py+transformer 或者 tf+transformer
简单搭建即可完成环境的搭建
transformer 模型
对上述代码 加入自己的理解,理解transformer的原理,等理解明白这个,跑bert模型。我使用的是py+transformer 环境
源码
写这部分主要是学会transformer模型
参考的代码是
https://github.com/BostonMilk/transformer-tutorial-code/blob/main/transformer.ipynb
import torch
import torch.nn as nn
class SelfAttention(nn.Module):
def __init__(self, embed_size, heads):
super(SelfAttention, self).__init__()
self.embed_size = embed_size
self.heads = heads
self.head_dim = embed_size // heads
assert (
self.head_dim * heads == embed_size
), "Embedding size needs to be divisible by heads"
self.values = nn.Linear(embed_size, embed_size)
self.keys = nn.Linear(embed_size, embed_size)
self.queries = nn.Linear(embed_size, embed_size)
self.fc_out = nn.Linear(embed_size, embed_size)
def forward(self, values, keys, query, mask):
# Get number of training examples
N = query.shape[0]
value_len, key_len, query_len = values.shape[1], keys.shape[1], query.shape[1]
values = self.values(values) # (N, value_len, embed_size)
keys = self.keys(keys) # (N, key_len, embed_size)
queries = self.queries(query) # (N, query_len, embed_size)
# Split the embedding into self.heads different pieces
values = values.reshape(N, value_len, self.heads, self.head_dim)
keys = keys.reshape(N, key_len, self.heads, self.head_dim)
queries = queries.reshape(N, query_len, self.heads, self.head_dim)
# Einsum does matrix mult. for query*keys for each training example
# with every other training example, don't be confused by einsum
# it's just how I like doing matrix multiplication & bmm
energy = torch.einsum("nqhd,nkhd->nhqk", [queries, keys])
# queries shape: (N, query_len, heads, heads_dim),
# keys shape: (N, key_len, heads, heads_dim)
# energy: (N, heads, query_len, key_len)
# Mask padded indices so their weights become 0
if mask is not None:
energy = energy.masked_fill(mask == 0, float("-1e20"))
# Normalize energy values similarly to seq2seq + attention
# so that they sum to 1. Also divide by scaling factor for
# better stability
attention = torch.softmax(energy / (self.embed_size ** (1 / 2)), dim=3)
# attention shape: (N, heads, query_len, key_len)
out = torch.einsum("nhql,nlhd->nqhd", [attention, values]).reshape(
N, query_len, self.heads * self.head_dim
)
# attention shape: (N, heads, query_len, key_len)
# values shape: (N, value_len, heads, heads_dim)
# out after matrix multiply: (N, query_len, heads, head_dim), then
# we reshape and flatten the last two dimensions.
out = self.fc_out(out)
# Linear layer doesn't modify the shape, final shape will be
# (N, query_len, embed_size)
return out
class TransformerBlock(nn.Module):
def __init__(self, embed_size, heads, dropout, forward_expansion):
super(TransformerBlock, self).__init__()
self.attention = SelfAttention(embed_size, heads)
self.norm1 = nn.LayerNorm(embed_size)
self.norm2 = nn.LayerNorm(embed_size)
self.feed_forward = nn.Sequential(
nn.Linear(embed_size, forward_expansion * embed_size),
nn.ReLU(),
nn.Linear(forward_expansion * embed_size, embed_size),
)
self.dropout = nn.Dropout(dropout)
def forward(self, value, key, query, mask):
attention = self.attention(value, key, query, mask)
# Add skip connection, run through normalization and finally dropout
x = self.dropout(self.norm1(attention + query))
forward = self.feed_forward(x)
out = self.dropout(self.norm2(forward + x))
return out
class Encoder(nn.Module):
def __init__(
self,
src_vocab_size,
embed_size,
num_layers,
heads,
device,
forward_expansion,
dropout,
max_length,
):
super(Encoder, self).__init__()
self.embed_size = embed_size
self.device = device
self.word_embedding = nn.Embedding(src_vocab_size, embed_size)
self.position_embedding = nn.Embedding(max_length, embed_size)
self.layers = nn.ModuleList(
[
TransformerBlock(
embed_size,
heads,
dropout=dropout,
forward_expansion=forward_expansion,
)
for _ in range(num_layers)
]
)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask):
N, seq_length = x.shape
positions = torch.arange(0, seq_length).expand(N, seq_length).to(self.device)
out = self.dropout(
(self.word_embedding(x) + self.position_embedding(positions))
)
# In the Encoder the query, key, value are all the same, it's in the
# decoder this will change. This might look a bit odd in this case.
for layer in self.layers:
out = layer(out, out, out, mask)
return out
class DecoderBlock(nn.Module):
def __init__(self, embed_size, heads, forward_expansion, dropout, device):
super(DecoderBlock, self).__init__()
self.norm = nn.LayerNorm(embed_size)
self.attention = SelfAttention(embed_size, heads=heads)
self.transformer_block = TransformerBlock(
embed_size, heads, dropout, forward_expansion
)
self.dropout = nn.Dropout(dropout)
def forward(self, x, value, key, src_mask, trg_mask):
attention = self.attention(x, x, x, trg_mask)
query = self.dropout(self.norm(attention + x))
out = self.transformer_block(value, key, query, src_mask)
return out
class Decoder(nn.Module):
def __init__(
self,
trg_vocab_size,
embed_size,
num_layers,
heads,
forward_expansion,
dropout,
device,
max_length,
):
super(Decoder, self).__init__()
self.device = device
self.word_embedding = nn.Embedding(trg_vocab_size, embed_size)
self.position_embedding = nn.Embedding(max_length, embed_size)
self.layers = nn.ModuleList(
[
DecoderBlock(embed_size, heads, forward_expansion, dropout, device)
for _ in range(num_layers)
]
)
self.fc_out = nn.Linear(embed_size, trg_vocab_size)
self.dropout = nn.Dropout(dropout)
def forward(self, x, enc_out, src_mask, trg_mask):
N, seq_length = x.shape
positions = torch.arange(0, seq_length).expand(N, seq_length).to(self.device)
x = self.dropout((self.word_embedding(x) + self.position_embedding(positions)))
for layer in self.layers:
x = layer(x, enc_out, enc_out, src_mask, trg_mask)
out = self.fc_out(x)
return out
class Transformer(nn.Module):
def __init__(
self,
src_vocab_size,
trg_vocab_size,
src_pad_idx,
trg_pad_idx,
embed_size=512,
num_layers=6,
forward_expansion=4,
heads=8,
dropout=0,
device="cpu",
max_length=100,
):
super(Transformer, self).__init__()
self.encoder = Encoder(
src_vocab_size,
embed_size,
num_layers,
heads,
device,
forward_expansion,
dropout,
max_length,
)
self.decoder = Decoder(
trg_vocab_size,
embed_size,
num_layers,
heads,
forward_expansion,
dropout,
device,
max_length,
)
self.src_pad_idx = src_pad_idx
self.trg_pad_idx = trg_pad_idx
self.device = device
def make_src_mask(self, src):
src_mask = (src != self.src_pad_idx).unsqueeze(1).unsqueeze(2)
# (N, 1, 1, src_len)
return src_mask.to(self.device)
def make_trg_mask(self, trg):
N, trg_len = trg.shape
trg_mask = torch.tril(torch.ones((trg_len, trg_len))).expand(
N, 1, trg_len, trg_len
)
return trg_mask.to(self.device)
def forward(self, src, trg):
src_mask = self.make_src_mask(src)
trg_mask = self.make_trg_mask(trg)
enc_src = self.encoder(src, src_mask)
out = self.decoder(trg, enc_src, src_mask, trg_mask)
return out
if __name__ == "__main__":
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)
x = torch.tensor([[1, 5, 6, 4, 3, 9, 5, 2, 0], [1, 8, 7, 3, 4, 5, 6, 7, 2]]).to(
device
)
trg = torch.tensor([[1, 7, 4, 3, 5, 9, 2, 0], [1, 5, 6, 2, 4, 7, 6, 2]]).to(device)
src_pad_idx = 0
trg_pad_idx = 0
src_vocab_size = 10
trg_vocab_size = 10
model = Transformer(src_vocab_size, trg_vocab_size, src_pad_idx, trg_pad_idx, device=device).to(
device
)
out = model(x, trg[:, :-1])
print(out.shape)
自注意力层
在 transformer 模型中会用到自注意力层,在其初始化的时候,有一个embed_size,那它是什么意思?
答:做nlp(自然语言处理)很多时候要用到嵌入层,embedding是一种重要的技术,它能够将高维稀疏的数据转化为低维稠密的向量表示。Embedding的概念来自于word embeddings,embed_size这个表示嵌入表示的维度,由我们决定想要多长的向量
onehot编码是 你好吗,如果词典是200,则这个编码方式如下
你 ----------------------[1,0,0,0,.....,0] (1*200维的向量)
好 ----------------------[0,1,0,0,.....,0] (1*200维的向量)
吗 ----------------------[0,0,1,0,.....,0] (1*200维的向量)
所以比起onehot,embedding的编码方式更节省空间,不仅仅是词语,任何通过嵌入层 Embedding 都可以转换成向量
embedding size是embedding技术中的一个重要参数,它决定了转化后的向量的维度。embedding size的选择和调整对于模型的性能有着重要的影响。如果embedding size过小,那么转化后的向量可能无法保留原始数据中的足够信息,导致模型的性能下降。如果embedding size过大,那么可能会导致模型的复杂度过高,出现过拟合等问题。
在实际应用中,我们可以通过实验和调整来找到最合适的embedding size。一般来说,我们可以从较小的embedding size开始,然后逐渐增大,观察模型性能的变化。当模型性能达到最优时,对应的embedding size可能就是最合适的。
此外,我们还可以通过一些技巧来优化embedding size的选择。例如,我们可以使用降维技术(如PCA、t-SNE等)来降低embedding的维度,同时保留尽可能多的信息。我们还可以使用一些预训练的embedding模型(如Word2Vec、GloVe等),这些模型已经在大量数据上进行了训练,可以为我们提供高质量的embedding向量。
# 这个类别主要计算自注意力权重,并根据权重聚合值向量。
class SelfAttention(nn.Module):
def __init__(self, embed_size, heads):
super(SelfAttention, self).__init__()
self.embed_size = embed_size #这个表示嵌入表示的维度,由我们决定想要多长的向量
# 如果embedding size过小,那么转化后的向量可能无法保留原始数据中的足够信息,导致模型的性能下降。
# 如果embedding size过大,那么可能会导致模型的复杂度过高,出现过拟合等问题
self.heads = heads #这个应该是多头注意力的头数设置
self.head_dim = embed_size // heads ## 得到每个头的维度
assert ( #在多头注意力机制中,embed_size 必须能够被头数(heads)整除。每个注意力头处理 embed_size // heads 维度的子空间。
# 这样可以将嵌入空间分成多个头,每个头关注输入的不同方面。
self.head_dim * heads == embed_size
), "Embedding size needs to be divisible by heads"
#这种全连接层通常用于映射数据到指定的维度空间。
self.values = nn.Linear(embed_size, embed_size) ## 线性层(又叫全连接层)用于计算值向量,
self.keys = nn.Linear(embed_size, embed_size) ## 线性层用于计算键向量
self.queries = nn.Linear(embed_size, embed_size) ## 线性层用于计算查询向量
self.fc_out = nn.Linear(embed_size, embed_size) ## 线性层用于将输出映射回原始维度
def forward(self, values, keys, query, mask):
# Get number of training examples # 获取训练样本的数量
# query: 这是一个形状为 [N, L, D] 的张量,其中 N 是批量大小,L 是序列长度,D 是每个单元的嵌入维度。
N = query.shape[0]
value_len, key_len, query_len = values.shape[1], keys.shape[1], query.shape[1]
# 通过 self.values、self.keys 和 self.queries 对输入进行线性变换,得到每个向量的嵌入表示
values = self.values(values) # (N, value_len, embed_size)
keys = self.keys(keys) # (N, key_len, embed_size)
queries = self.queries(query) # (N, query_len, embed_size)
# Split the embedding into self.heads different pieces # 将嵌入分成多个头
values = values.reshape(N, value_len, self.heads, self.head_dim)
keys = keys.reshape(N, key_len, self.heads, self.head_dim)
queries = queries.reshape(N, query_len, self.heads, self.head_dim)
# Einsum does matrix mult. for query*keys for each training example
# with every other training example, don't be confused by einsum
# it's just how I like doing matrix multiplication & bmm
# torch.einsum 函数提供了一种灵活和高效的方式来进行张量操作,尤其是矩阵乘法和内积运算,得到注意力矩阵
energy = torch.einsum("nqhd,nkhd->nhqk", [queries, keys])
# queries shape: (N, query_len, heads, heads_dim),
# keys shape: (N, key_len, heads, heads_dim)
# energy: (N, heads, query_len, key_len)
# Mask padded indices so their weights become 0
if mask is not None:
# mask形状同energy,将mask=0的位置的同等位置替换为float,经过softmax变成0
energy = energy.masked_fill(mask == 0, float("-1e20"))
# Normalize energy values similarly to seq2seq + attention
# so that they sum to 1. Also divide by scaling factor for
# better stability 进行 softmax 归一化以得到注意力权重,同时除以缩放因子以提高数值稳定性
attention = torch.softmax(energy / (self.embed_size ** (1 / 2)), dim=3)
# attention shape: (N, heads, query_len, key_len) 缩放因子的引入是为了提高数值稳定性,特别是在计算 softmax 时。
# 将 energy 张量的每个值除以缩放因子。这是为了避免在 softmax 操作时数值过大或过小,导致梯度消失或爆炸的问题。
# torch.softmax 计算沿着 dim=3 维度(即 key_len 维度)进行 softmax 操作,确保每个查询位置的注意力权重在所有键位置上求和为 1。
out = torch.einsum("nhql,nlhd->nqhd", [attention, values]).reshape(
N, query_len, self.heads * self.head_dim
)
# attention shape: (N, heads, query_len, key_len)
# values shape: (N, value_len, heads, heads_dim)
# out after matrix multiply: (N, query_len, heads, head_dim), then
# we reshape and flatten the last two dimensions.
out = self.fc_out(out)
# Linear layer doesn't modify the shape, final shape will be
# (N, query_len, embed_size) # 线性层不会改变形状,最终形状为 (N, query_len, embed_size)
return out
TransformerBlock
在 Transformer 模型中,TransformerBlock 是一个基本的构建模块,用于实现标准的 Transformer 编码器块。它包括自注意力机制、层归一化、前馈神经网络以及跳跃连接。
forward_expansion 是一个超参数,控制中间层的扩展比例。在前馈网络中,它用于决定第一个线性层的输出维度。在实际应用中,这个参数的设置可以影响模型的表现:
较大的 forward_expansion:会增加隐藏层的维度,从而引入更多的特征,但也会增加计算复杂度和内存使用。
较小的 forward_expansion:会减小隐藏层的维度,减少计算量,但可能会限制模型的表现力。
dropOut 是为了减少过拟合的问题
class TransformerBlock(nn.Module): #Transformer的核心构建单元
def __init__(self, embed_size, heads, dropout, forward_expansion):
super(TransformerBlock, self).__init__()
self.attention = SelfAttention(embed_size, heads)
self.norm1 = nn.LayerNorm(embed_size)
self.norm2 = nn.LayerNorm(embed_size) #层归一化主要用于神经网络中的隐藏层。
#初始化两个归一化层,用于在模型的两个主要阶段进行归一化,确保训练过程中的稳定性。
#初始化前馈神经网络,feed_forward
self.feed_forward = nn.Sequential( #。在 nn.Sequential 中,网络的层会按照给定的顺序被逐一调用,这种方式非常适合于那些层次结构简单、线性堆叠的神经网络结构。
nn.Linear(embed_size, forward_expansion * embed_size),
nn.ReLU(),
nn.Linear(forward_expansion * embed_size, embed_size),
)
# 初始化 dropout 层,用于正则化,防止过拟合。dropout 参数决定了丢弃率。
self.dropout = nn.Dropout(dropout)
def forward(self, value, key, query, mask):
attention = self.attention(value, key, query, mask)
# Add skip connection, run through normalization and finally dropout
x = self.dropout(self.norm1(attention + query))
# 添加残差连接:增强网络的训练稳定性。
# 层归一化:稳定训练过程,加速模型收敛。
# 应用 dropout:减少过拟合,提高模型的泛化能力。
forward = self.feed_forward(x)
# 将经过归一化和 dropout 的结果 x 传入前馈神经网络。这个前馈网络进一步处理数据,以便生成更复杂的特征表示。
out = self.dropout(self.norm2(forward + x))
return out
# 返回最终的输出张量 out,它经过了自注意力机制、前馈神经网络、归一化和 dropout 的处理。
Encoder
在Encoder 类中,我们实现了 Transformer 编码器的主要组件.
对源序列执行多层TransformerBlock操作,嵌入加位置编码后传入。
class Encoder(nn.Module):
def __init__(
self,
src_vocab_size, #词汇表大小
embed_size, #embed_size 决定了每个词汇和位置的向量表示的维度
num_layers,
heads,
device,
forward_expansion,
dropout,
max_length, ):
super(Encoder, self).__init__()
self.embed_size = embed_size
self.device = device
self.word_embedding = nn.Embedding(src_vocab_size, embed_size) #定义词汇嵌入层,将词汇ID转换成向量
self.position_embedding = nn.Embedding(max_length, embed_size) #位置ID转换成向量
# nn.ModuleList 是 PyTorch 的一个容器,用于存储子模块。这里用它来存储多个 TransformerBlock 实例。
self.layers = nn.ModuleList(
[
TransformerBlock(
embed_size,
heads,
dropout=dropout,
forward_expansion=forward_expansion,
)
for _ in range(num_layers) #num_layers 决定了堆叠的层数。
]
)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask):
N, seq_length = x.shape
# 从输入 x 的形状中获取批量大小 N 和序列长度 seq_length。x 是输入的词汇 ID 张量,形状为 (N, seq_length)。
# N代表每次处理几个句子。seq_length代表所有句子中最长的句子长度。
positions = torch.arange(0, seq_length).expand(N, seq_length).to(self.device)
# print(positions) 输出 tensor([[0, 1, 2, 3, 4, 5, 6, 7, 。。。seq_length],这样的一共有N个
# [],[])
out = self.dropout(
(self.word_embedding(x) + self.position_embedding(positions))
# 分别将词汇id和位置id转换为向量,将词汇嵌入和位置嵌入相加,以获得带有位置信息的词汇嵌入。对嵌入向量应用 dropout,以提高模型的泛化能力。
)
# In the Encoder the query, key, value are all the same, it's in the
# decoder this will change. This might look a bit odd in this case.
for layer in self.layers: #依次遍历编码器中的每一层。每层是一个 TransformerBlock
out = layer(out, out, out, mask) #将out为查询、键、值向量传入
return out #这个输出经过了所有的编码器层的处理
DecoderBlock
这部分与编码器块的结构类似,但有一些关键的区别。 在Decoder中引入了与Encoder不同的自注意力机制,并结合TransformerBlock结构。
主要是 一层组归一化、自注意力层、TransformerBlock块构成。
class DecoderBlock(nn.Module):
def __init__(self, embed_size, heads, forward_expansion, dropout, device):
super(DecoderBlock, self).__init__()
self.norm = nn.LayerNorm(embed_size)
# 初始化自注意力机制,用于解码器中,计算解码器中位置的注意力
self.attention = SelfAttention(embed_size, heads=heads)
# 初始化 TransformerBlock,处理来自编码器的输出
self.transformer_block = TransformerBlock(
embed_size, heads, dropout, forward_expansion
)
self.dropout = nn.Dropout(dropout)
# src_mask 是源序列的掩码,用于处理编码器的输出,使得在计算注意力时忽略填充的位置。
# trg_mask 是目标序列的掩码,用于防止模型在生成序列时看到未来的信息。
def forward(self, x, value, key, src_mask, trg_mask):
# 计算解码器的自注意力,应用于目标序列 x
attention = self.attention(x, x, x, trg_mask)
query = self.dropout(self.norm(attention + x))
out = self.transformer_block(value, key, query, src_mask)
return out
DecoderBlock 类实现了 Transformer 解码器块的主要功能,包括自注意力机制和对编码器输出的处理。它通过自注意力机制处理目标序列中的信息,并通过 Transformer 块进一步融合来自编码器的信息。在前向传播中,它先计算自注意力,然后通过跳跃连接和层归一化,最后将结果传递给 Transformer 块进行最终处理。
Decoder
由多个DecoderBlock组成,包含词汇表嵌入、位置编码以及最终输出的全连接层。
class Decoder(nn.Module):
def __init__(
self,
trg_vocab_size,
embed_size,
num_layers,
heads,
forward_expansion,
dropout,
device,
max_length,
):
super(Decoder, self).__init__()
self.device = device
self.word_embedding = nn.Embedding(trg_vocab_size, embed_size)
self.position_embedding = nn.Embedding(max_length, embed_size)
self.layers = nn.ModuleList(
[
DecoderBlock(embed_size, heads, forward_expansion, dropout, device)
for _ in range(num_layers)
]
)
self.fc_out = nn.Linear(embed_size, trg_vocab_size)
self.dropout = nn.Dropout(dropout)
def forward(self, x, enc_out, src_mask, trg_mask):
N, seq_length = x.shape
positions = torch.arange(0, seq_length).expand(N, seq_length).to(self.device)
x = self.dropout((self.word_embedding(x) + self.position_embedding(positions)))
for layer in self.layers:
x = layer(x, enc_out, enc_out, src_mask, trg_mask)
out = self.fc_out(x) #初始化一个全连接层,用于将解码器输出的嵌入向量映射到目标词汇表的维度。这个层用于生成最终的预测分布。
return out #返回解码器的最终输出,用于后续的损失计算或预测。
Decoder 类定义了 Transformer 解码器部分的结构,包括词嵌入、位置嵌入、多个解码器块、全连接层和 dropout 层。在 forward 方法中,它首先计算词嵌入和位置嵌入的和,然后通过每个解码器块进行处理,最后通过全连接层生成对目标词汇表的预测。
Transformer
class Transformer(nn.Module):
def __init__(
self,
src_vocab_size,
trg_vocab_size,
src_pad_idx,
trg_pad_idx,
embed_size=512,
num_layers=6,
forward_expansion=4,
heads=8,
dropout=0,
device="cpu",
max_length=100,
):
super(Transformer, self).__init__()
self.encoder = Encoder(
src_vocab_size,
embed_size,
num_layers,
heads,
device,
forward_expansion,
dropout,
max_length,
)
self.decoder = Decoder(
trg_vocab_size,
embed_size,
num_layers,
heads,
forward_expansion,
dropout,
device,
max_length,
)
self.src_pad_idx = src_pad_idx # 源序列填充的索引,比如原来句子长度是2,最大长度是5,那就需要对句子为2的向量表示进行填充0
# 那填充什么由self.src_pad_idx决定,一般我们会设置为0
self.trg_pad_idx = trg_pad_idx # 目标序列填充的索引
self.device = device
def make_src_mask(self, src): #用于标记源序列中的有效位置。掩码张量的形状是 (N, 1, 1, src_len)
# 创建源序列掩码,确保填充位置不参与计算
# 原系列中有和填充不一样的话,就会设置为true,在通过扩充实现掩码的初始化
src_mask = (src != self.src_pad_idx).unsqueeze(1).unsqueeze(2)
# unsqueeze 方法在维度 1 和 2 处添加新的维度
# (N, 1, 1, src_len)
return src_mask.to(self.device)
def make_trg_mask(self, trg): #生成目标序列的掩码。掩码确保在生成目标序列时不会看到未来的词。
N, trg_len = trg.shape
trg_mask = torch.tril(torch.ones((trg_len, trg_len))).expand(
N, 1, trg_len, trg_len
)#tril创建一个下三角矩阵,用于掩盖目标序列中的未来信息。下面是1,上面是0
return trg_mask.to(self.device)
def forward(self, src, trg):
src_mask = self.make_src_mask(src)
trg_mask = self.make_trg_mask(trg)
enc_src = self.encoder(src, src_mask)
out = self.decoder(trg, enc_src, src_mask, trg_mask)
return out
主函数中验证了 out的输出shape
if __name__ == "__main__":
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)
x = torch.tensor([[1, 5, 6, 4, 3, 9, 5, 2, 0], [1, 8, 7, 3, 4, 5, 6, 7, 2]]).to(
device
)
trg = torch.tensor([[1, 7, 4, 3, 5, 9, 2, 0], [1, 5, 6, 2, 4, 7, 6, 2]]).to(device)
src_pad_idx = 0
trg_pad_idx = 0
src_vocab_size = 10
trg_vocab_size = 10
model = Transformer(src_vocab_size, trg_vocab_size, src_pad_idx, trg_pad_idx, device=device).to(
device
)
out = model(x, trg[:, :-1]) #将源序列和目标序列的前一个时间步传递给模型进行前向推理,得到模型的输出
print(out.shape)
实操(实现github源码运行)
上面的transformer模型的代码就一个py文件,可能没想到能这么少,这个上面是transformer原理形成的代码,后续的大模型都是在这个内容的基础上改进的。我们在github上搜索transformer 模型,一般会搜索到这个项目,这一部分是把他跑起来。
搭建环境的所有相关代码
conda create -n transformer python=3.10
conda activate transformer
安装pytorch
conda install pytorch==2.2.2 torchvision==0.17.2 torchaudio==2.2.2 pytorch-cuda=12.1 -c pytorch -c nvidia
卸载numpy,换版本1.24.1
测试才不会报错,下面代码测试是否pytorch版本安装成功,cuda12.1,pytorch2.2
import torch
print(torch.__version__) # 查看torch当前版本号
print(torch.version.cuda) # 编译当前版本的torch使用的cuda版本号
print(torch.cuda.is_available()) # 查看当前cuda是否可用于当前版本的Torch,如果输出True,则表示可用
安装transformer
pip install transformers
验证测试
from transformers import pipeline
classifier = pipeline('sentiment-analysis')
classifier('We are very happy to introduce pipeline to the transformers repository.')
成功 说明环境安装成功 在这里直接pip transformer就能行
跑transformer模型文本分类
跑这个模型最好pip install . ,在github里安装,直接pip 会报错版本问题当前是4.42.0,而需要4.45.0,按照github安装就是准确版本
拉取代码
git clone https://github.com/huggingface/transformers.git
cd ../transformers/examples/pytorch/text-classification #打开这个文件
运行先安装依赖
Pip install -r requestment.txt
想跑transformer的代码,在这个example下,本文跑的是文本分类,选了这个以后直接安装这个文件里的readme跑就行
运行脚本我不知道什么意思,一般都是直接copy,代码报错我在改,这个直接可以跑起来
export TASK_NAME=mrpc
cd /root/lx/transformers/examples/pytorch/text-classification
python run_glue.py \
--model_name_or_path google-bert/bert-base-cased \
--task_name $TASK_NAME \
--do_train \
--do_eval \
--max_seq_length 128 \
--per_device_train_batch_size 32 \
--learning_rate 2e-5 \
--num_train_epochs 3 \
--output_dir /tmp/$TASK_NAME/
运行结果是这样的
在这之前会遇到两个问题,一个是哪怕解决了网络问题,还是hugface的模型下载不下来,这样的话用这句,下载切换到国内的镜像,就能下载了
export HF_ENDPOINT=https://hf-mirror.com
第二个就是还是下载不下来,长这个样子,就说databases版本过高,降低datasets版本 <=2.14.6,
我跑这个代码就遇到这两个问题,完结,撒花。
总结
至此,这个代码就已经看完了,懂了他的大致结构也会跑transformer的示例,下一步跑跑bert。看看大模型的结构