赞
踩
本教程旨在全面阐述如何运用PyTorch构建Transformer模型。Transformer作为现代机器学习中最为强大的模型之一,彻底改变了诸多领域,尤其在自然语言处理(Natural Language Processing, NLP)任务,如语言翻译与文本摘要方面展现出革新性影响。由于其在处理长程依赖及并行计算方面的优越性,Transformer已逐步取代长短期记忆网络(Long Short-Term Memory, LSTM)在网络这些任务中的应用。
本指南中构造Transformer所采用的工具是PyTorch,这是一款广受欢迎且开源的机器学习库,以其简洁性、多用途性和高效性著称。PyTorch凭借其动态计算图以及丰富的库资源,已成为机器学习与人工智能领域研究者与开发者首选的开发工具。
背景和理论
Transformers最初由Vaswani等人在论文《Attention is All You Need》中提出,自此因其独特设计与高效性成为了众多自然语言处理任务的基石。
Transformer的核心在于注意力机制,尤其是“自注意力”这一概念,它使模型能够对输入数据的不同部分进行加权和优先排序。正是这一机制使得Transformer能够处理数据中的长程依赖。本质上,这是一种加权方案,允许模型在生成输出时聚焦于输入的不同部分。
此机制使模型在考虑输入序列中的不同词汇或特征时,能为每个词汇或特征分配一个表示其对产生特定输出重要性的“权重”。
例如,在句子翻译任务中,当翻译某个特定单词时,模型可能会对与目标词在语法或语义上有联系的单词赋予更高的注意力权重。这一过程使得Transformer能够捕捉序列中词汇或特征间的依赖关系,无论它们之间相隔多远。
Transformer在NLP领域的影响力不容小觑。它们在多项任务上超越了传统模型,展现了更精细地理解和生成人类语言的卓越能力。
设置 PyTorch
在开始构建 Transformer 之前,必须正确设置工作环境。首先,需要安装 PyTorch。PyTorch(当前稳定版本 - 2.0.1)可以通过 pip 或 conda 包管理器轻松安装。
对于pip,使用以下命令:
pip3 install torch torchvision torchaudio
对于conda,使用以下命令:
conda install pytorch torchvision torchaudio pytorch-cuda=11.7 -c pytorch -c nvidia
如需在CPU上使用PyTorch,请访问PyTorch官方文档。
使用 PyTorch 构建 Transformer 模型
要构建 Transformer 模型,需要遵循以下步骤:
导入库和模块
定义基本构建单元 - 多头注意力(Multi-head Attention)、位置感知前馈网络(Position-Wise Feed-Forward Networks)、位置编码(Positional Encoding)
构建编码器(Encoder)块
构建解码器(Decoder)块
将编码器和解码器层结合以创建完整的Transformer网络
导入必要的库和模块
我们从导入PyTorch库开始,用于核心功能;神经网络模块用于创建神经网络;优化模块用于训练网络;数据实用函数模块用于处理数据。此外,我们还将导入Python标准数学模块进行数学运算,以及复制模块来创建复杂对象的副本。
这些工具为定义模型架构、管理数据及建立训练流程奠定了基础。
import torch``import torch.nn as nn``import torch.optim as optim``import torch.utils.data as data``import math``import copy
定义基本构建模块:多头注意力(Multi-Head Attention)、位置感知前馈网络(Position-wise Feed-Forward Networks)、位置编码(Positional Encoding)
Multi-Head Attention 机制计算序列中每对位置之间的注意力。它由多个“注意力头”组成,可捕获输入序列的不同方面。
要了解有关多头注意力的更多信息,请查看大型语言模型 (LLM) 概念课程[的注意机制]部分。
Figure 1. Multi-Head Attention
class MultiHeadAttention(nn.Module):` `def __init__(self, d_model, num_heads):` `super(MultiHeadAttention, self).__init__()` `# Ensure that the model dimension (d_model) is divisible by the number of heads` `assert d_model % num_heads == 0, "d_model must be divisible by num_heads"` ` # Initialize dimensions` `self.d_model = d_model # Model's dimension` `self.num_heads = num_heads # Number of attention heads` `self.d_k = d_model // num_heads # Dimension of each head's key, query, and value` ` # Linear layers for transforming inputs` `self.W_q = nn.Linear(d_model, d_model) # Query transformation` `self.W_k = nn.Linear(d_model, d_model) # Key transformation` `self.W_v = nn.Linear(d_model, d_model) # Value transformation` `self.W_o = nn.Linear(d_model, d_model) # Output transformation` ` def scaled_dot_product_attention(self, Q, K, V, mask=None):` `# Calculate attention scores` `attn_scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)` ` # Apply mask if provided (useful for preventing attention to certain parts like padding)` `if mask is not None:` `attn_scores = attn_scores.masked_fill(mask == 0, -1e9)` ` # Softmax is applied to obtain attention probabilities` `attn_probs = torch.softmax(attn_scores, dim=-1)` ` # Multiply by values to obtain the final output` `output = torch.matmul(attn_probs, V)` `return output` ` def split_heads(self, x):` `# Reshape the input to have num_heads for multi-head attention` `batch_size, seq_length, d_model = x.size()` `return x.view(batch_size, seq_length, self.num_heads, self.d_k).transpose(1, 2)` ` def combine_heads(self, x):` `# Combine the multiple heads back to original shape` `batch_size, _, seq_length, d_k = x.size()` `return x.transpose(1, 2).contiguous().view(batch_size, seq_length, self.d_model)` ` def forward(self, Q, K, V, mask=None):` `# Apply linear transformations and split heads` `Q = self.split_heads(self.W_q(Q))` `K = self.split_heads(self.W_k(K))` `V = self.split_heads(self.W_v(V))` ` # Perform scaled dot-product attention` `attn_output = self.scaled_dot_product_attention(Q, K, V, mask)` ` # Combine heads and apply output transformation` `output = self.W_o(self.combine_heads(attn_output))` `return output
类定义和初始化:
class MultiHeadAttention(nn.Module):` `def __init__(self, d_model, num_heads):
该类被定义为PyTorch的nn.Module
的一个子类。
d_model:输入的维度。
num_heads:将输入分割成的注意力头数量。
初始化过程中会检查d_model是否能被num_heads整除,并随后定义查询(query)、键(key)、值(value)及输出的转换权重。
缩放点积注意力(Scaled Dot-Product Attention):
def scaled_dot_product_attention(self, Q, K, V, mask=None):
计算注意力得分:attn_scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
。此处,通过计算查询(Q)与键(K)的点积,并随后除以键维度(d_k)的平方根来得到注意力得分。
应用掩码:如果提供了掩码,则将其应用于注意力得分,以屏蔽特定值。
计算注意力权重:将注意力得分通过softmax函数传递,将其转化为总和为1的概率。
计算输出:最终的注意力输出通过将注意力权重乘以值(V)得到。
分割头(Splitting Heads):
def split_heads(self, x):
此方法将输入x
重塑为形状(batch_size, num_heads, seq_length, d_k)
。这使得模型能够并行处理多个注意力头,实现并行计算。
合并头(Combining Heads):
def combine_heads(self, x):
在对每个头单独应用注意力后,此方法将结果重新组合成单个形状为(batch_size, seq_length, d_model)
的张量。这为后续处理做好准备。
前向传播方法(Forward Method):
def forward(self, Q, K, V, mask=None):
前向方法是实际计算发生的地方:
应用线性变换:首先将查询(Q)、键(K)和值(V)通过初始化时定义的权重进行线性变换。
分割头:转换后的Q、K、V利用split_heads
方法分割成多个头。
应用缩放点积注意力:在分割的头上调用scaled_dot_product_attention
方法。
合并头:使用combine_heads
方法将每个头的结果重新组合成单一张量。
应用输出变换:最后,将组合的张量通过输出线性变换。
总结:
MultiHeadAttention
类封装了Transformer模型中常用的多头注意力机制。它负责将输入分割成多个注意力头,对每个头应用注意力,然后合并结果。通过这种方式,模型能够在不同尺度上捕获输入数据中的多种关系,从而增强模型的表达能力。
class PositionWiseFeedForward(nn.Module):` `def __init__(self, d_model, d_ff):` `super(PositionWiseFeedForward, self).__init__()` `self.fc1 = nn.Linear(d_model, d_ff)` `self.fc2 = nn.Linear(d_ff, d_model)` `self.relu = nn.ReLU()`` ` `def forward(self, x):` `return self.fc2(self.relu(self.fc1(x)))
类定义:
class PositionWiseFeedForward(nn.Module):
此类作为PyTorch的nn.Module
的一个子类,意味着它将继承所有与神经网络层工作所需的功能。
初始化:
def __init__(self, d_model, d_ff):` `super(PositionWiseFeedForward, self).__init__()` `self.fc1 = nn.Linear(d_model, d_ff)` `self.fc2 = nn.Linear(d_ff, d_model)` `self.relu = nn.ReLU()
d_model
:模型输入和输出的维度。
d_ff
:前馈网络内部层的维度。
self.fc1
和 self.fc2
:两个全连接(线性)层,其输入和输出维度由d_model
和d_ff
定义。
self.relu
:ReLU(Rectified Linear Unit)激活函数,它在两个线性层之间引入非线性。
前向传播方法(Forward Method):
def forward(self, x):` `return self.fc2(self.relu(self.fc1(x)))
x
:前馈网络的输入。
self.fc1(x)
:输入首先通过第一层线性层(fc1)。
self.relu(...)
:fc1的输出随后通过ReLU激活函数。ReLU将所有负值替换为零,为模型引入非线性。
self.fc2(...)
:激活后的输出接着通过第二层线性层(fc2),产生最终输出。
总结:
PositionWiseFeedForward
类定义了一个位置wise的前馈神经网络,该网络由两层线性层组成,中间夹带ReLU激活函数。在Transformer模型的上下文中,这个前馈网络独立且相同地应用于每个位置。它有助于转换Transformer内部注意力机制所学到的特征,充当注意力输出的额外处理步骤,增强了模型的表达和学习能力。
位置编码用于注入输入序列中每个标记的位置信息。它使用不同频率的正弦和余弦函数来生成位置编码。
class PositionalEncoding(nn.Module):` `def __init__(self, d_model, max_seq_length):` `super(PositionalEncoding, self).__init__()` ` pe = torch.zeros(max_seq_length, d_model)` `position = torch.arange(0, max_seq_length, 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)` ` self.register_buffer('pe', pe.unsqueeze(0))` ` def forward(self, x):` `return x + self.pe[:, :x.size(1)]
类定义:
class PositionalEncoding(nn.Module):
该类被定义为PyTorch的nn.Module
的一个子类,使其可以作为标准的PyTorch层使用。
初始化:
def __init__(self, d_model, max_seq_length):` `super(PositionalEncoding, self).__init__()` ` pe = torch.zeros(max_seq_length, d_model)` `position = torch.arange(0, max_seq_length, 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)` ` self.register_buffer('pe', pe.unsqueeze(0))
d_model
:模型输入的维度。
max_seq_length
:预先计算位置编码的最大序列长度。
pe
:一个填充有零的张量,将用于存储位置编码。
position
:一个包含序列中每个位置索引的张量。
div_term
:一个用于特定方式缩放位置索引的项。
正弦函数应用于pe
的偶数索引,而余弦函数应用于奇数索引。
最后,pe
被注册为缓冲区,这意味着它将成为模块状态的一部分,但不会被视为可训练参数。
前向传播方法(Forward Method):
def forward(self, x):` `return x + self.pe[:, :x.size(1)]
前向方法简单地将位置编码添加到输入x
上。
它使用pe
的前x.size(1)
个元素,以确保位置编码与x
的实际序列长度匹配。
总结:
PositionalEncoding
类为序列中标记的位置添加了信息。由于Transformer模型的自注意力机制本身不包含标记顺序的知识,此类帮助模型考虑序列中标记的位置。所使用的正弦波函数便于模型学习关注相对位置,因为它们为序列中的每个位置生成了唯一且平滑的编码。这增强了模型在处理序列数据时的位置感知能力,特别是在需要考虑单词或符号顺序的任务中。
构建编码器块(Building the Encoder Blocks)
Figure 2. The Encoder part of the transformer network
class EncoderLayer(nn.Module):` `def __init__(self, d_model, num_heads, d_ff, dropout):` `super(EncoderLayer, self).__init__()` `self.self_attn = MultiHeadAttention(d_model, num_heads)` `self.feed_forward = PositionWiseFeedForward(d_model, d_ff)` `self.norm1 = nn.LayerNorm(d_model)` `self.norm2 = nn.LayerNorm(d_model)` `self.dropout = nn.Dropout(dropout)` ` def forward(self, x, mask):` `attn_output = self.self_attn(x, x, x, mask)` `x = self.norm1(x + self.dropout(attn_output))` `ff_output = self.feed_forward(x)` `x = self.norm2(x + self.dropout(ff_output))` `return x
类定义:
class EncoderLayer(nn.Module):
该类被定义为PyTorch的nn.Module
的一个子类,意味着它可用作PyTorch中神经网络的构建模块。
初始化:
def __init__(self, d_model, num_heads, d_ff, dropout):` `super(EncoderLayer, self).__init__()` `self.self_attn = MultiHeadAttention(d_model, num_heads)` `self.feed_forward = PositionWiseFeedForward(d_model, d_ff)` `self.norm1 = nn.LayerNorm(d_model)` `self.norm2 = nn.LayerNorm(d_model)` `self.dropout = nn.Dropout(dropout)
参数:
d_model
:输入的维度。
num_heads
:多头注意力中的注意力头数量。
d_ff
:位置wise前馈网络内部层的维度。
dropout
:用于正则化的丢弃率。
组件:
self.self_attn
:多头注意力机制。
self.feed_forward
:位置wise的前馈神经网络。
self.norm1
和 self.norm2
:层归一化,用于平滑层的输入。
self.dropout
:丢弃层,通过在训练过程中随机将某些激活置零来防止过拟合。
前向传播方法(Forward Method):
def forward(self, x, mask):` `attn_output = self.self_attn(x, x, x, mask)` `x = self.norm1(x + self.dropout(attn_output))` `ff_output = self.feed_forward(x)` `x = self.norm2(x + self.dropout(ff_output))` `return x
输入:
x
:编码器层的输入。
mask
:可选的掩码,用于忽略输入的某些部分。
处理步骤:
自注意力(Self-Attention):输入x
通过多头自注意力机制。
添加与归一化(Attention后):注意力输出与原始输入相加(残差连接),随后进行丢弃操作和使用norm1
的归一化。
前馈网络(Feed-Forward Network):前一步的输出通过位置wise的前馈网络。
添加与归一化(Feed-Forward后):类似于步骤2,前馈网络的输出与这一阶段输入相加(残差连接),之后进行丢弃操作和使用norm2
的归一化。
输出:处理后的张量作为编码器层的输出返回。
总结:
EncoderLayer
类定义了变换器编码器中的单一层。它封装了一个多头自注意力机制,随后是位置wise的前馈神经网络,结合残差连接、层归一化以及适当应用的丢弃策略。这些组成部分共同使编码器能够捕获输入数据中的复杂关系,并将其转换为对下游任务有用的表现形式。通常,多个这样的编码器层会被堆叠起来,形成变换器模型完整的编码器部分。
构建解码器块(Building the Decoder Blocks)
class DecoderLayer(nn.Module):` `def __init__(self, d_model, num_heads, d_ff, dropout):` `super(DecoderLayer, self).__init__()` `self.self_attn = MultiHeadAttention(d_model, num_heads)` `self.cross_attn = MultiHeadAttention(d_model, num_heads)` `self.feed_forward = PositionWiseFeedForward(d_model, d_ff)` `self.norm1 = nn.LayerNorm(d_model)` `self.norm2 = nn.LayerNorm(d_model)` `self.norm3 = nn.LayerNorm(d_model)` `self.dropout = nn.Dropout(dropout)` ` def forward(self, x, enc_output, src_mask, tgt_mask):` `attn_output = self.self_attn(x, x, x, tgt_mask)` `x = self.norm1(x + self.dropout(attn_output))` `attn_output = self.cross_attn(x, enc_output, enc_output, src_mask)` `x = self.norm2(x + self.dropout(attn_output))` `ff_output = self.feed_forward(x)` `x = self.norm3(x + self.dropout(ff_output))` `return x
类定义:
class DecoderLayer(nn.Module):
初始化:
def __init__(self, d_model, num_heads, d_ff, dropout):` `super(DecoderLayer, self).__init__()` `self.self_attn = MultiHeadAttention(d_model, num_heads)` `self.cross_attn = MultiHeadAttention(d_model, num_heads)` `self.feed_forward = PositionWiseFeedForward(d_model, d_ff)` `self.norm1 = nn.LayerNorm(d_model)` `self.norm2 = nn.LayerNorm(d_model)` `self.norm3 = nn.LayerNorm(d_model)` `self.dropout = nn.Dropout(dropout)
参数:
d_model
:输入的维度。
num_heads
:多头注意力中的注意力头数量。
d_ff
:前馈网络内部层的维度。
dropout
:用于正则化的丢弃率。
组件:
self.self_attn
:目标序列的多头自注意力机制。
self.cross_attn
:关注编码器输出的多头注意力机制。
self.feed_forward
:位置wise的前馈神经网络。
self.norm1
, self.norm2
, self.norm3
:层归一化组件。
self.dropout
:用于正则化的丢弃层。
前向传播方法(Forward Method):
def forward(self, x, enc_output, src_mask, tgt_mask):` `attn_output = self.self_attn(x, x, x, tgt_mask)` `x = self.norm1(x + self.dropout(attn_output))` `attn_output = self.cross_attn(x, enc_output, enc_output, src_mask)` `x = self.norm2(x + self.dropout(attn_output))` `ff_output = self.feed_forward(x)` `x = self.norm3(x + self.dropout(ff_output))` `return x
输入:
x
:解码器层的输入。
enc_output
:对应编码器的输出(在交叉注意力步骤中使用)。
src_mask
:源掩码,用于忽略编码器输出的某些部分。
tgt_mask
:目标掩码,用于忽略解码器输入的某些部分。
处理步骤:
目标序列的自注意力(Self-Attention on Target Sequence):输入x
通过自注意力机制处理。
添加与归一化(Self-Attention后):自注意力的输出与原始x
相加(残差连接),随后进行丢弃操作和使用norm1
的归一化。
与编码器输出的交叉注意力(Cross-Attention with Encoder Output):前一步归一化后的输出通过一个关注编码器输出enc_output
的交叉注意力机制处理。
添加与归一化(Cross-Attention后):交叉注意力的输出与这一阶段的输入相加(残差连接),之后进行丢弃操作和使用norm2
的归一化。
前馈网络(Feed-Forward Network):前一步的输出通过前馈网络。
添加与归一化(Feed-Forward后):前馈网络的输出与这一阶段的输入相加(残差连接),之后进行丢弃操作和使用norm3
的归一化。
输出:处理后的张量作为解码器层的输出返回。
总结:
DecoderLayer
类定义了变换器解码器中的单一层。它包括一个多头自注意力机制、一个多头交叉注意力机制(关注编码器的输出)、一个位置wise的前馈神经网络,以及相应的残差连接、层归一化和丢弃层。这种组合使得解码器能够基于编码器的表示生成有意义的输出,同时考虑到目标序列和源序列。与编码器类似,多个解码器层通常会被堆叠起来,形成变换器模型的完整解码器部分。
接下来,将编码器和解码器块结合起来构建完整的Transformer模型。
将编码器和解码器层结合以创建完整的Transformer网络
*Figure 4. The Transformer Network *
class Transformer(nn.Module):` `def __init__(self, src_vocab_size, tgt_vocab_size, d_model, num_heads, num_layers, d_ff, max_seq_length, dropout):` `super(Transformer, self).__init__()` `self.encoder_embedding = nn.Embedding(src_vocab_size, d_model)` `self.decoder_embedding = nn.Embedding(tgt_vocab_size, d_model)` `self.positional_encoding = PositionalEncoding(d_model, max_seq_length)`` ` `self.encoder_layers = nn.ModuleList([EncoderLayer(d_model, num_heads, d_ff, dropout) for _ in range(num_layers)])` `self.decoder_layers = nn.ModuleList([DecoderLayer(d_model, num_heads, d_ff, dropout) for _ in range(num_layers)])`` ` `self.fc = nn.Linear(d_model, tgt_vocab_size)` `self.dropout = nn.Dropout(dropout)`` ` `def generate_mask(self, src, tgt):` `src_mask = (src != 0).unsqueeze(1).unsqueeze(2)` `tgt_mask = (tgt != 0).unsqueeze(1).unsqueeze(3)` `seq_length = tgt.size(1)` `nopeak_mask = (1 - torch.triu(torch.ones(1, seq_length, seq_length), diagonal=1)).bool()` `tgt_mask = tgt_mask & nopeak_mask` `return src_mask, tgt_mask`` ` `def forward(self, src, tgt):` `src_mask, tgt_mask = self.generate_mask(src, tgt)` `src_embedded = self.dropout(self.positional_encoding(self.encoder_embedding(src)))` `tgt_embedded = self.dropout(self.positional_encoding(self.decoder_embedding(tgt)))`` ` `enc_output = src_embedded` `for enc_layer in self.encoder_layers:` `enc_output = enc_layer(enc_output, src_mask)`` ` `dec_output = tgt_embedded` `for dec_layer in self.decoder_layers:` `dec_output = dec_layer(dec_output, enc_output, src_mask, tgt_mask)`` ` `output = self.fc(dec_output)` `return output
类定义:
class Transformer(nn.Module):
初始化:
def __init__(self, src_vocab_size, tgt_vocab_size, d_model, num_heads, num_layers, d_ff, max_seq_length, dropout):
构造函数采用以下参数:
src_vocab_size
:源词汇表大小。
tgt_vocab_size
:目标词汇表大小。
d_model
:模型嵌入的维度。
num_heads
:多头注意力机制中的注意力头数。
num_layers
:编码器和解码器的层数。
d_ff
:前馈网络内部层的维度。
max_seq_length
:位置编码的最大序列长度。
dropout
:用于正则化的丢弃率。
并定义以下组件:
self.encoder_embedding
:源序列的嵌入层。
self.decoder_embedding
:目标序列的嵌入层。
self.positional_encoding
:位置编码组件。
self.encoder_layers
:编码器层的列表。
self.decoder_layers
:解码器层的列表。
self.fc
:最终的全连接(线性)层,映射到目标词汇表大小。
self.dropout
:丢弃层。
生成掩码方法(Generate Mask Method):
def generate_mask(self, src, tgt):
此方法用于创建源序列和目标序列的掩码,确保填充符号被忽略,并且在训练过程中目标序列的未来符号不可见。
前向传播方法(Forward Method):
def forward(self, src, tgt):
该方法定义了Transformer的前向传播,接收源序列和目标序列,产生输出预测。
输入嵌入与位置编码:首先使用各自的嵌入层对源序列和目标序列进行嵌入,然后加上它们的位置编码。
编码器层:源序列通过编码器层,最后的编码器输出代表处理过的源序列。
解码器层:目标序列和编码器的输出一起通过解码器层,产生解码器的输出。
最终线性层:解码器的输出通过一个全连接(线性)层映射到目标词汇表大小。
输出:
最终输出是一个张量,表示模型对目标序列的预测。
总结:
Transformer类整合了Transformer模型的各个组成部分,包括嵌入、位置编码、编码器层和解码器层。它提供了一个方便的接口来进行训练和推理,封装了多头注意力、前馈网络和层归一化的复杂性。
此实现遵循标准的Transformer架构,适用于诸如机器翻译、文本摘要等序列到序列任务。掩码的加入确保模型遵循序列内的因果依赖,忽略填充符号并防止来自未来符号的信息泄露。
这些顺序步骤使Transformer模型能够高效地处理输入序列并生成相应的输出序列。
PyTorch Transformer 模型的训练
为了说明目的,本例将构建一个虚拟数据集。然而,在实际场景中,会采用更大型的数据集,并且过程会涉及文本预处理以及为源语言和目标语言创建词汇映射。
src_vocab_size = 5000``tgt_vocab_size = 5000``d_model = 512``num_heads = 8``num_layers = 6``d_ff = 2048``max_seq_length = 100``dropout = 0.1`` ``transformer = Transformer(src_vocab_size, tgt_vocab_size, d_model, num_heads, num_layers, d_ff, max_seq_length, dropout)`` ``# Generate random sample data``src_data = torch.randint(1, src_vocab_size, (64, max_seq_length)) # (batch_size, seq_length)``tgt_data = torch.randint(1, tgt_vocab_size, (64, max_seq_length)) # (batch_size, seq_length)
超参数:
这些值定义了Transformer模型的架构和行为:
src_vocab_size, tgt_vocab_size
:源序列和目标序列的词汇表大小,均设为5000。
d_model
:模型嵌入的维度,设为512。
num_heads
:多头注意力机制中的注意力头数,设为8。
num_layers
:编码器和解码器的层数,均设为6。
d_ff
:前馈网络内部层的维度,设为2048。
max_seq_length
:位置编码的最大序列长度,设为100。
dropout
:用于正则化的丢弃率,设为0.1。
创建Transformer实例:
transformer = Transformer(src_vocab_size, tgt_vocab_size, d_model, num_heads, num_layers, d_ff, max_seq_length, dropout)
这行代码使用给定的超参数创建了Transformer类的一个实例。该实例将具有由这些超参数定义的架构和行为。
生成随机样本数据:
以下代码行生成随机的源序列和目标序列:
src_data
:介于1和src_vocab_size
之间的随机整数,代表一批源序列,形状为(64, max_seq_length)。
tgt_data
:介于1和tgt_vocab_size
之间的随机整数,代表一批目标序列,形状为(64, max_seq_length)。
这些随机序列可用作变压器模型的输入,模拟包含64个示例且序列长度为100的数据批次。
总结:
代码片段展示了如何初始化一个Transformer模型并生成可输入模型的随机源序列和目标序列。所选的超参数决定了Transformer的具体结构和特性。这个设置可以作为更大脚本的一部分,在该脚本中,模型将在实际的序列到序列任务上进行训练和评估,比如机器翻译或文本摘要。
接下来,将利用上述样本数据对模型进行训练。但是,在实际场景中,会使用大得多的数据集,该数据集通常被划分为不同的集合以进行训练和验证。
criterion = nn.CrossEntropyLoss(ignore_index=0)``optimizer = optim.Adam(transformer.parameters(), lr=0.0001, betas=(0.9, 0.98), eps=1e-9)`` ``transformer.train()`` ``for epoch in range(100):` `optimizer.zero_grad()` `output = transformer(src_data, tgt_data[:, :-1])` `loss = criterion(output.contiguous().view(-1, tgt_vocab_size), tgt_data[:, 1:].contiguous().view(-1))` `loss.backward()` `optimizer.step()` `print(f"Epoch: {epoch+1}, Loss: {loss.item()}")
损失函数与优化器:
criterion = nn.CrossEntropyLoss(ignore_index=0)
:定义损失函数为交叉熵损失。ignore_index
参数设为0,意味着损失计算时会忽略索引为0的目标(通常用于填充token)。
optimizer = optim.Adam(...)
:定义优化器为Adam,学习率为0.0001,并设定特定的beta值。
模型训练模式:
transformer.train()
:将Transformer模型设置为训练模式,启用仅在训练期间应用的行为,如dropout。训练循环:
代码片段通过典型的训练循环对模型进行了100轮的训练:
for epoch in range(100):
遍历100个训练周期。
optimizer.zero_grad()
:清空前一迭代的梯度。
output = transformer(src_data, tgt_data[:, :-1])
:将源数据和目标数据(每个序列排除最后一个token)传递给Transformer。这是序列到序列任务中的常见做法,其中目标序列相对于输入向后移一位。
loss = criterion(...)
:计算模型预测与目标数据(每个序列排除第一个token)之间的损失。损失是通过对数据进行一维重塑并使用交叉熵损失函数来计算的。
loss.backward()
:计算关于模型参数的损失梯度。
optimizer.step()
:利用计算出的梯度更新模型参数。
print(f"Epoch: {epoch+1}, Loss: {loss.item()}")
:打印当前的迭代次数和该迭代的损失值。
总结:
此代码片段对Transformer模型进行了100轮的训练,使用的源序列和目标序列是随机生成的。它采用了Adam优化器和交叉熵损失函数。每个周期都会打印损失值,便于监控训练进度。在实际应用中,您会将随机的源序列和目标序列替换为来自具体任务的实际数据,例如机器翻译的数据。
Transformer 模型性能评估
完成模型训练后,其性能可以在验证数据集或测试数据集上进行评估。以下是如何执行这一操作的一个示例:
transformer.eval()`` ``# Generate random sample validation data``val_src_data = torch.randint(1, src_vocab_size, (64, max_seq_length)) # (batch_size, seq_length)``val_tgt_data = torch.randint(1, tgt_vocab_size, (64, max_seq_length)) # (batch_size, seq_length)`` ``with torch.no_grad():`` ` `val_output = transformer(val_src_data, val_tgt_data[:, :-1])` `val_loss = criterion(val_output.contiguous().view(-1, tgt_vocab_size), val_tgt_data[:, 1:].contiguous().view(-1))` `print(f"Validation Loss: {val_loss.item()}")
评估模式:
transformer.eval()
:将Transformer模型置于评估模式。这一步很关键,因为它会关闭诸如dropout这类仅在训练期间使用的特性。生成随机验证数据:
val_src_data
:生成介于1和src_vocab_size
之间的随机整数,代表一批形状为(64, max_seq_length)的验证源序列。
val_tgt_data
:生成介于1和tgt_vocab_size
之间的随机整数,代表一批形状为(64, max_seq_length)的验证目标序列。
验证循环:
with torch.no_grad():
禁止梯度计算,因为在验证过程中我们不需要计算梯度。这样做可以减少内存消耗并加速计算过程。
val_output = transformer(val_src_data, val_tgt_data[:, :-1])
:将验证源数据和验证目标数据(每个序列排除最后一个token)传递给Transformer。
val_loss = criterion(...)
:计算模型预测与验证目标数据(每个序列排除第一个token)之间的损失。损失是通过对数据进行一维重塑并使用之前定义的交叉熵损失函数来计算的。
print(f"Validation Loss: {val_loss.item()}")
:打印验证损失值。
总结
这段代码示例展示了如何在随机生成的验证数据集上评估Transformer模型,计算验证损失,并打印该值。在实际场景中,随机的验证数据应当被替换为您正在处理的任务的实际验证数据。验证损失可为您提供模型在未见过的数据上的表现情况,这是衡量模型泛化能力的一个关键指标。
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。
保证100%免费
】Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。