跑Transformer模型(完结)

搭建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。看看大模型的结构

Transformer发轫于NLP(自然语言处理),并跨界应用到CV(计算机视觉)领域。 Swin Transformer是基于Transformer的计算机视觉骨干网,在图像分类、目标检测、实例分割、语义分割等多项下游CV应用中取得了SOTA的性能。该项工作也获得了ICCV 2021顶会最佳论文奖。 本课程将手把手地教大家使用labelImg标注和使用Swin Transformer训练自己的数据集。  本课程将介绍Transformer及在CV领域的应用、Swin Transformer的原理。 课程以多目标检测(足球和梅西同时检测)为例进行Swin Transformer实战演示。 课程在Windows和Ubuntu系统上分别做项目演示。包括:安装软件环境、安装Pytorch、安装Swin-Transformer-Object-Detection、标注自己的数据集、准备自己的数据集(自动划分训练集和验证集)、数据集格式转换(Python脚本完成)、修改配置文件、训练自己的数据集、测试训练出的网络模型、性能统计、日志分析。  相关课程: 《Transformer原理与代码精讲(PyTorch)》https://edu.csdn.net/course/detail/36697《Transformer原理与代码精讲(TensorFlow)》https://edu.csdn.net/course/detail/36699《ViT(Vision Transformer)原理与代码精讲》https://edu.csdn.net/course/detail/36719《DETR原理与代码精讲》https://edu.csdn.net/course/detail/36768《Swin Transformer实战目标检测:训练自己的数据集》https://edu.csdn.net/course/detail/36585《Swin Transformer实战实例分割:训练自己的数据集》https://edu.csdn.net/course/detail/36586《Swin Transformer原理与代码精讲》 https://download.csdn.net/course/detail/37045
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值