0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

使用PyTorch搭建Transformer模型

CHANBAEK 来源:网络整理 2024-07-02 11:41 次阅读

引言

Transformer模型自其问世以来,在自然语言处理(NLP)领域取得了巨大的成功,并成为了许多先进模型(如BERT、GPT等)的基础。本文将深入解读如何使用PyTorch框架搭建Transformer模型,包括模型的结构、训练过程、关键组件以及实现细节。

Transformer模型概述

Transformer模型是一种基于自注意力机制的序列到序列(Seq2Seq)模型,由Vaswani等人在2017年的论文《Attention is All You Need》中提出。它彻底摒弃了传统的循环神经网络(RNN)和卷积神经网络(CNN)架构,通过自注意力机制捕捉序列中元素之间的依赖关系,从而实现了更好的并行化和可扩展性。

Transformer模型主要由编码器和解码器两部分组成:

  • 编码器 :将输入序列转换为一系列连续的向量表示(也称为上下文向量)。
  • 解码器 :根据编码器输出的上下文向量生成目标序列。

Transformer模型的关键组件

1. 自注意力机制(Self-Attention)

自注意力机制是Transformer模型的核心,它允许模型在处理序列中的每个元素时,都能够关注到序列中的其他元素。具体来说,自注意力机制通过计算序列中每对元素之间的注意力分数,并根据这些分数对元素进行加权求和,从而生成每个元素的上下文表示。

注意力分数的计算

在自注意力机制中,每个元素(通常是词向量)被表示为三个向量:查询向量(Query, Q)、键向量(Key, K)和值向量(Value, V)。注意力分数通过计算查询向量与所有键向量的点积,并应用softmax函数得到。这个过程可以并行化,从而显著提高计算效率。

多头注意力机制(Multi-Head Attention)

多头注意力机制通过并行计算多个自注意力层,并将它们的输出拼接起来,赋予模型捕捉不同子空间信息的能力。这有助于模型学习到更丰富的特征表示。

2. 位置编码(Positional Encoding)

由于Transformer模型没有使用RNN或CNN等具有位置信息的结构,因此需要通过位置编码来注入每个元素在序列中的位置信息。位置编码通常是通过不同频率的正弦和余弦函数生成的,这些函数可以确保模型能够区分不同位置的元素。

3. 编码器层和解码器层

编码器层和解码器层都由多个子层组成,包括自注意力层、位置前馈网络(Position-wise Feed-Forward Network)以及层归一化(Layer Normalization)和残差连接(Residual Connection)。

  • 编码器层 :首先通过自注意力层捕捉输入序列的依赖关系,然后通过位置前馈网络进一步处理,最后通过层归一化和残差连接稳定训练过程。
  • 解码器层 :除了包含与编码器层相同的子层外,还额外包含一个编码器-解码器注意力层(Encoder-Decoder Attention),用于将编码器输出的上下文向量与解码器的当前输出进行交互。

PyTorch实现Transformer模型

1. 导入必要的库和模块

首先,我们需要导入PyTorch及其相关模块:

import torch  

import torch.nn as nn  

import torch.nn.functional as F

2. 定义基本构建块

接下来,我们定义Transformer模型的基本构建块,包括多头注意力机制、位置前馈网络和位置编码。

多头注意力机制

class MultiHeadAttention(nn.Module):  
    def __init__(self, d_model, num_heads):  
        super(MultiHeadAttention, self).__init__()  
        self.d_model = d_model  
        self.num_heads = num_heads  
        self.d_k = d_model // num_heads  
        self.qkv_proj = nn.Linear(d_model, d_model * 3, bias=False)  
        self.proj = nn.Linear(d_model, d_model)  
  
    def forward(self, x, mask=None):  
        # 分割qkv  
        qkv = self.qkv_proj(x).chunk(3, dim=-1)  
        q, k, v = map(lambda t: t.view(t.size(0), -1, self.num_heads, self.d_k).transpose(1, 2), qkv)  
  
        # 计算注意力分数  
        scores = torch.matmul(q, k.transpose(-2, -1)) / torch.sqrt(torch.tensor(self.d_k, dtype=torch.float32))  
        if mask is not None:  
            scores = scores.masked_fill(mask == 0, float('-1e20'))

注意力分数的softmax和加权求和

在得到注意力分数后,我们需要对这些分数应用softmax函数,以便将分数归一化为概率分布,并根据这些概率对值向量进行加权求和。

# 应用softmax并加权求和  
        attention_weights = F.softmax(scores, dim=-1)  
        output = torch.matmul(attention_weights, v).transpose(1, 2).contiguous()  
        output = output.view(output.size(0), -1, self.d_model)  
  
        # 输出通过最后的线性层  
        output = self.proj(output)  
  
        return output

位置前馈网络

位置前馈网络是一个简单的两层全连接网络,用于进一步处理自注意力层的输出。

class PositionwiseFeedForward(nn.Module):  
    def __init__(self, d_model, d_ff, dropout=0.1):  
        super(PositionwiseFeedForward, self).__init__()  
        self.w_1 = nn.Linear(d_model, d_ff)  
        self.w_2 = nn.Linear(d_ff, d_model)  
        self.dropout = nn.Dropout(dropout)  
  
    def forward(self, x):  
        return self.w_2(self.dropout(F.relu(self.w_1(x))))

位置编码

位置编码通常是在模型外部预先计算好的,然后通过加法或拼接的方式与词嵌入向量结合。

def positional_encoding(position, d_model):  
    # 创建一个与d_model相同维度的位置编码  
    # 使用正弦和余弦函数生成位置编码  
    encoding = torch.zeros(position, d_model)  
    position = torch.arange(0, position, dtype=torch.float).unsqueeze(1)  
    div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))  
    encoding[:, 0::2] = torch.sin(position * div_term)  
    encoding[:, 1::2] = torch.cos(position * div_term)  
    encoding = encoding.unsqueeze(0)  # 增加一个批次维度  
    return encoding

3. 编码器层和解码器层

接下来,我们将这些基本构建块组合成编码器层和解码器层。

编码器层

class EncoderLayer(nn.Module):  
    def __init__(self, d_model, num_heads, d_ff, dropout=0.1):  
        super(EncoderLayer, self).__init__()  
        self.self_attn = MultiHeadAttention(d_model, num_heads)  
        self.feed_forward = PositionwiseFeedForward(d_model, d_ff, dropout)  
        self.layer_norm1 = nn.LayerNorm(d_model)  
        self.layer_norm2 = nn.LayerNorm(d_model)  
        self.dropout1 = nn.Dropout(dropout)  
        self.dropout2 = nn.Dropout(dropout)  
  
    def forward(self, x, mask):  
        x = self.layer_norm1(x + self.dropout1(self.self_attn(x, mask)))  
        x = self.layer_norm2(x + self.dropout2(self.feed_forward(x)))  
        return x

解码器层

解码器层与编码器层类似,但额外包含一个编码器-解码器注意力层。

class DecoderLayer(nn.Module):  
    # ... 类似EncoderLayer,但包含额外的encoder-decoder attention  
    pass

4. 完整的Transformer模型

最后,我们将多个编码器层和解码器层堆叠起来,形成完整的Transformer模型。

class Transformer(nn.Module):  
    def __init__(self, num_encoder_layers, num_decoder_layers, d_model, num_heads, d_ff, input_vocab_size, output_vocab_size, max_length=5000):  
        super(Transformer, self).__init__()  
        # 编码器部分  
        self.encoder = nn.ModuleList([  
            EncoderLayer(d_model, num_heads, d_ff)  
            for _ in range(num_encoder_layers)  
        ])  
        self.src_emb = nn.Embedding(input_vocab_size, d_model)

当然,我们继续讲解Transformer模型的剩余部分,包括解码器部分、位置编码的整合以及最终的前向传播方法。

解码器部分

解码器部分包含多个解码器层,每个解码器层都包含自注意力层、编码器-解码器注意力层以及位置前馈网络。解码器还需要处理掩码(mask)来避免自注意力层中的未来信息泄露。

class DecoderLayer(nn.Module):  
    def __init__(self, d_model, num_heads, d_ff, dropout=0.1):  
        super(DecoderLayer, self).__init__()  
        self.self_attn = MultiHeadAttention(d_model, num_heads, dropout=dropout)  
        self.enc_attn = MultiHeadAttention(d_model, num_heads, dropout=dropout)  
        self.feed_forward = PositionwiseFeedForward(d_model, d_ff, dropout)  
        self.layer_norm1 = nn.LayerNorm(d_model)  
        self.layer_norm2 = nn.LayerNorm(d_model)  
        self.layer_norm3 = nn.LayerNorm(d_model)  
        self.dropout1 = nn.Dropout(dropout)  
        self.dropout2 = nn.Dropout(dropout)  
        self.dropout3 = nn.Dropout(dropout)  
  
    def forward(self, x, encoder_output, src_mask, tgt_mask, memory_mask):  
        # 自注意力层  
        x2 = self.layer_norm1(x + self.dropout1(self.self_attn(x, tgt_mask)))  
        # 编码器-解码器注意力层  
        x3 = self.layer_norm2(x2 + self.dropout2(self.enc_attn(x2, encoder_output, memory_mask)))  
        # 前馈网络  
        return self.layer_norm3(x3 + self.dropout3(self.feed_forward(x3)))

完整的Transformer模型

现在我们可以定义完整的Transformer模型,包括编码器、解码器以及它们之间的连接。

class Transformer(nn.Module):  
    def __init__(self, num_encoder_layers, num_decoder_layers, d_model, num_heads, d_ff, input_vocab_size, output_vocab_size, max_length=5000):  
        super(Transformer, self).__init__()  
        self.encoder = nn.ModuleList([  
            EncoderLayer(d_model, num_heads, d_ff)  
            for _ in range(num_encoder_layers)  
        ])  
        self.decoder = nn.ModuleList([  
            DecoderLayer(d_model, num_heads, d_ff)  
            for _ in range(num_decoder_layers)  
        ])  
        self.src_emb = nn.Embedding(input_vocab_size, d_model)  
        self.tgt_emb = nn.Embedding(output_vocab_size, d_model)  
        self.src_pos_enc = positional_encoding(max_length, d_model)  
        self.tgt_pos_enc = positional_encoding(max_length, d_model)  
        self.final_linear = nn.Linear(d_model, output_vocab_size)  
  
    def forward(self, src, tgt, src_mask, tgt_mask, memory_mask):  
        # 对输入和输出进行嵌入和位置编码  
        src = self.src_emb(src) + self.src_pos_enc[:src.size(0), :]  
        tgt = self.tgt_emb(tgt) + self.tgt_pos_enc[:tgt.size(0), :]  
  
        # 编码器  
        memory = src  
        for layer in self.encoder:  
            memory = layer(memory, src_mask)  
  
        # 解码器  
        output = tgt  
        for layer in self.decoder:  
            output = layer(output, memory, src_mask, tgt_mask, memory_mask)  
  
        # 最终线性层,输出预测  
        output = self.final_linear(output)  
        return output

注意事项

  • 位置编码 :在实际应用中,位置编码通常是与嵌入向量相加,而不是拼接。这有助于模型学习位置信息,同时保持输入维度的一致性。
  • 掩码src_masktgt_maskmemory_mask用于在自注意力和编码器-解码器注意力层中防止信息泄露。

当然,我们继续深入探讨Transformer模型的几个关键方面,包括掩码(masking)的具体实现、训练过程以及在实际应用中的挑战和解决方案。 这主要有两种类型的掩码:

  1. 填充掩码(Padding Mask) :由于不同长度的输入序列在批次处理中会被填充到相同的长度,填充掩码用于指示哪些位置是填充的,以便在注意力计算中忽略这些位置。
  2. 序列掩码(Sequence Mask)未来掩码(Future Mask) :在解码器中,序列掩码用于确保在预测某个位置的输出时,模型只能看到该位置及之前的输出,而不能看到未来的输出。这是为了防止在训练过程中泄露未来的信息。

实现示例

这里是一个简单的序列掩码实现示例(仅用于说明,具体实现可能因框架而异):

def generate_square_subsequent_mask(sz):  
    """生成一个用于解码器的掩码,用于遮盖未来的位置"""  
    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 mask  
  
# 假设 tgt 是一个形状为 [batch_size, tgt_len] 的张量  
tgt_len = tgt.size(1)  
tgt_mask = generate_square_subsequent_mask(tgt_len).to(tgt.device)

训练过程

Transformer模型的训练通常涉及以下步骤:

  1. 数据预处理 :包括文本清洗、分词(tokenization)、构建词汇表、转换为张量等。
  2. 前向传播 :将输入序列和目标序列(在训练时)通过编码器和解码器进行前向传播,计算损失。
  3. 损失计算 :通常使用交叉熵损失(Cross-Entropy Loss)来比较模型预测的输出和目标输出之间的差异。
  4. 反向传播 :根据损失梯度更新模型参数
  5. 优化器 :使用如Adam等优化器来更新权重。
  6. 迭代训练 :重复上述步骤,直到模型在验证集上表现良好或达到预定的训练轮次。

挑战和解决方案

  1. 过拟合 :使用正则化技术(如dropout)、早停(early stopping)或更大的数据集来防止过拟合。
  2. 计算资源Transformer模型,尤其是大型模型,需要大量的计算资源。使用分布式训练、混合精度训练等技术可以加速训练过程。
  3. 位置编码 :虽然位置编码能够给模型提供位置信息,但它不是可学习的。一些研究提出了可学习的位置嵌入(如相对位置编码)来改进性能。
  4. 长序列处理Transformer模型在处理非常长的序列时可能会遇到内存和性能问题。一些改进模型(如Transformer-XL、Longformer等)旨在解决这一问题。
  5. 多语言和多任务学习Transformer模型在多语言和多任务学习方面也表现出色,但需要仔细设计模型架构和训练策略以充分利用跨语言和跨任务的信息。

总之,Transformer模型是一种强大的序列到序列模型,通过精心设计的架构和训练策略,它在许多自然语言处理任务中取得了显著的成果。然而,为了充分发挥其潜力,还需要不断研究和改进模型的不同方面。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 模型
    +关注

    关注

    1

    文章

    2887

    浏览量

    48076
  • Transformer
    +关注

    关注

    0

    文章

    132

    浏览量

    5916
  • pytorch
    +关注

    关注

    2

    文章

    777

    浏览量

    12906
收藏 人收藏

    评论

    相关推荐

    请问电脑端Pytorch训练的模型如何转化为能在ESP32S3平台运行的模型

    由题目, 电脑端Pytorch训练的模型如何转化为能在ESP32S3平台运行的模型? 如何把这个Pytorch模型烧录到ESP32S3上去?
    发表于 06-27 06:06

    Pytorch模型训练实用PDF教程【中文】

    模型部分?还是优化器?只有这样不断的通过可视化诊断你的模型,不断的对症下药,才能训练出一个较满意的模型。本教程内容及结构:本教程内容主要为在 PyTorch 中训练一个
    发表于 12-21 09:18

    Pytorch模型如何通过paddlelite部署到嵌入式设备?

    Pytorch模型如何通过paddlelite部署到嵌入式设备?
    发表于 12-23 09:38

    怎样去解决pytorch模型一直无法加载的问题呢

    rknn的模型转换过程是如何实现的?怎样去解决pytorch模型一直无法加载的问题呢?
    发表于 02-11 06:03

    pytorch模型转化为onxx模型的步骤有哪些

    首先pytorch模型要先转化为onxx模型,然后从onxx模型转化为rknn模型直接转化会出现如下问题,环境都是正确的,论坛询问后也没给出
    发表于 05-09 16:36

    怎样使用PyTorch Hub去加载YOLOv5模型

    在Python>=3.7.0环境中安装requirements.txt,包括PyTorch>=1.7。模型和数据集从最新的 YOLOv5版本自动下载。简单示例此示例从
    发表于 07-22 16:02

    通过Cortex来非常方便的部署PyTorch模型

    到软件中。如何从“跨语言语言模型”转换为谷歌翻译?在这篇博客文章中,我们将了解在生产环境中使用 PyTorch 模型意味着什么,然后介绍一种允许部署任何 PyTorch
    发表于 11-01 15:25

    Pytorch模型转换为DeepViewRT模型时出错怎么解决?

    我正在寻求您的帮助以解决以下问题.. 我在 Windows 10 上安装了 eIQ Toolkit 1.7.3,我想将我的 Pytorch 模型转换为 DeepViewRT (.rtm) 模型,这样
    发表于 06-09 06:42

    如何将PyTorch模型与OpenVINO trade结合使用?

    无法确定如何转换 PyTorch 掩码 R-CNN 模型以配合OpenVINO™使用。
    发表于 08-15 07:04

    pytorch模型转换需要注意的事项有哪些?

    什么是JIT(torch.jit)? 答:JIT(Just-In-Time)是一组编译工具,用于弥合PyTorch研究与生产之间的差距。它允许创建可以在不依赖Python解释器的情况下运行的模型
    发表于 09-18 08:05

    超大Transformer语言模型的分布式训练框架

    NVIDIA Megatron 是一个基于 PyTorch 的框架,用于训练基于 Transformer 架构的巨型语言模型。本系列文章将详细介绍Megatron的设计和实践,探索这一框架如何助力
    的头像 发表于 10-11 16:46 2424次阅读
    超大<b class='flag-5'>Transformer</b>语言<b class='flag-5'>模型</b>的分布式训练框架

    PyTorch教程11.9之使用Transformer进行大规模预训练

    电子发烧友网站提供《PyTorch教程11.9之使用Transformer进行大规模预训练.pdf》资料免费下载
    发表于 06-05 15:07 0次下载
    <b class='flag-5'>PyTorch</b>教程11.9之使用<b class='flag-5'>Transformer</b>进行大规模预训练

    如何加速生成2 PyTorch扩散模型

    加速生成2 PyTorch扩散模型
    的头像 发表于 09-04 16:09 882次阅读
    如何加速生成2 <b class='flag-5'>PyTorch</b>扩散<b class='flag-5'>模型</b>

    基于PyTorch模型并行分布式训练Megatron解析

    NVIDIA Megatron 是一个基于 PyTorch 的分布式训练框架,用来训练超大Transformer语言模型,其通过综合应用了数据并行,Tensor并行和Pipeline并行来复现 GPT3,值得我们深入分析其背后机
    的头像 发表于 10-23 11:01 1425次阅读
    基于<b class='flag-5'>PyTorch</b>的<b class='flag-5'>模型</b>并行分布式训练Megatron解析

    如何使用PyTorch建立网络模型

    PyTorch是一个基于Python的开源机器学习库,因其易用性、灵活性和强大的动态图特性,在深度学习领域得到了广泛应用。本文将从PyTorch的基本概念、网络模型构建、优化方法、实际应用等多个方面,深入探讨使用
    的头像 发表于 07-02 14:08 75次阅读