深度学习教程之基于代码一步一步教你深度学习中Transformer的原理

首先把所有代码放在一块,整体上了解一下transformer的结构和原理,后面继续分段介绍。

以下是一个基于代码的简单示例,演示Transformer模型的原理:

  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as F
  4.  
  5. class Transformer(nn.Module):
  6. def __init__(self, input_size, hidden_size, num_heads, num_layers):
  7. super(Transformer, self).__init__()
  8.  
  9. self.embedding = nn.Embedding(input_size, hidden_size)
  10. self.positional_encoding = PositionalEncoding(hidden_size)
  11. self.encoder = nn.ModuleList([
  12. EncoderLayer(hidden_size, num_heads) for _ in range(num_layers)
  13. ])
  14. self.decoder = nn.ModuleList([
  15. DecoderLayer(hidden_size, num_heads) for _ in range(num_layers)
  16. ])
  17. self.fc = nn.Linear(hidden_size, input_size)
  18.  
  19. def forward(self, src, trg):
  20. src = self.embedding(src) # 输入序列的嵌入表示
  21. trg = self.embedding(trg) # 目标序列的嵌入表示
  22. src = self.positional_encoding(src) # 加入位置编码
  23. # 编码器
  24. for encoder_layer in self.encoder:
  25. src = encoder_layer(src)
  26. trg = self.positional_encoding(trg) # 加入位置编码
  27. # 解码器
  28. for decoder_layer in self.decoder:
  29. trg = decoder_layer(src, trg)
  30. output = self.fc(trg) # 最后的线性变换得到输出结果
  31. return F.log_softmax(output, dim=-1)
  32.  
  33.  
  34. class EncoderLayer(nn.Module):
  35. def __init__(self, hidden_size, num_heads):
  36. super(EncoderLayer, self).__init__()
  37. self.multihead_attention = MultiheadAttention(hidden_size, num_heads)
  38. self.feed_forward = FeedForward(hidden_size)
  39.  
  40. def forward(self, x):
  41. residual = x
  42. x = self.multihead_attention(x)
  43. x = x + residual
  44. residual = x
  45. x = self.feed_forward(x)
  46. x = x + residual
  47. return x
  48.  
  49.  
  50. class DecoderLayer(nn.Module):
  51. def __init__(self, hidden_size, num_heads):
  52. super(DecoderLayer, self).__init__()
  53. self.masked_multihead_attention = MultiheadAttention(hidden_size, num_heads)
  54. self.multihead_attention = MultiheadAttention(hidden_size, num_heads)
  55. self.feed_forward = FeedForward(hidden_size)
  56.  
  57. def forward(self, src, trg):
  58. residual = trg
  59. trg = self.masked_multihead_attention(trg)
  60. trg = trg + residual
  61. residual = trg
  62. trg = self.multihead_attention(src, trg)
  63. trg = trg + residual
  64. residual = trg
  65. trg = self.feed_forward(trg)
  66. trg = trg + residual
  67. return trg
  68.  
  69.  
  70. class MultiheadAttention(nn.Module):
  71. def __init__(self, hidden_size, num_heads):
  72. super(MultiheadAttention, self).__init__()
  73. self.num_heads = num_heads
  74. self.head_size = hidden_size // num_heads
  75. self.q_linear = nn.Linear(hidden_size, hidden_size)
  76. self.k_linear = nn.Linear(hidden_size, hidden_size)
  77. self.v_linear = nn.Linear(hidden_size, hidden_size)
  78. self.fc = nn.Linear(hidden_size, hidden_size)
  79.  
  80. def forward(self, x):
  81. batch_size, seq_len, hidden_size = x.size()
  82. q = self.q_linear(x).view(batch_size, seq_len, self.num_heads, self.head_size).transpose(1, 2)
  83. k = self.k_linear(x).view(batch_size, seq_len, self.num_heads, self.head_size).transpose(1
  84.  
  85. , 2)
  86. v = self.v_linear(x).view(batch_size, seq_len, self.num_heads, self.head_size).transpose(1, 2)
  87. attention_scores = torch.matmul(q, k.transpose(-2, -1)) / torch.sqrt(torch.tensor(self.head_size, dtype=torch.float32))
  88. attention_weights = F.softmax(attention_scores, dim=-1)
  89. x = torch.matmul(attention_weights, v).transpose(1, 2).contiguous().view(batch_size, seq_len, hidden_size)
  90. x = self.fc(x)
  91. return x
  92.  
  93.  
  94. class FeedForward(nn.Module):
  95. def __init__(self, hidden_size, feed_forward_size=2048):
  96. super(FeedForward, self).__init__()
  97. self.fc1 = nn.Linear(hidden_size, feed_forward_size)
  98. self.fc2 = nn.Linear(feed_forward_size, hidden_size)
  99.  
  100. def forward(self, x):
  101. x = F.relu(self.fc1(x))
  102. x = self.fc2(x)
  103. return x
  104.  
  105.  
  106. class PositionalEncoding(nn.Module):
  107. def __init__(self, hidden_size, max_seq_len=5000):
  108. super(PositionalEncoding, self).__init__()
  109. self.positional_encoding = self.generate_positional_encoding(hidden_size, max_seq_len)
  110.  
  111. def forward(self, x):
  112. x = x + self.positional_encoding[:x.size(0), :]
  113. return x
  114.  
  115. def generate_positional_encoding(self, hidden_size, max_seq_len):
  116. positional_encoding = torch.zeros(max_seq_len, hidden_size)
  117. position = torch.arange(0, max_seq_len, dtype=torch.float32).unsqueeze(1)
  118. div_term = torch.exp(torch.arange(0, hidden_size, 2, dtype=torch.float32) * -(math.log(10000.0) / hidden_size))
  119. positional_encoding[:, 0::2] = torch.sin(position * div_term)
  120. positional_encoding[:, 1::2] = torch.cos(position * div_term)
  121. return positional_encoding

这个代码示例实现了一个简化版的Transformer模型,包括编码器(Encoder)和解码器(Decoder)以及注意力机制(MultiheadAttention)、前馈神经网络(FeedForward)和位置编码(PositionalEncoding)等组件。在模型的前向传播过程中,输入的源语言和目标语言序列经过嵌入层(Embedding)和位置编码层(PositionalEncoding)后,分别经过编码器和解码器的多层堆叠。在每个编码器和解码器层中,都包含了自注意力机制和前馈神经网络层。最终,通过线性变换层(Linear)和softmax函数将解码器的输出转化为概率分布,用于预测目标语言序列。 请注意,这只是一个简化的示例代码,实际的Transformer模型可能包含更多的细节和优化。此示例旨在提供一个基本的理解,帮助解释Transformer模型的核心原理和组成部分。

以下是分段介绍各部分的含义。

  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as F

这些代码段导入了PyTorch库和所需的模块。

  1. class Transformer(nn.Module):
  2. def __init__(self, input_size, hidden_size, num_heads, num_layers):
  3. super(Transformer, self).__init__()
  4.  
  5. self.embedding = nn.Embedding(input_size, hidden_size)
  6. self.positional_encoding = PositionalEncoding(hidden_size)
  7. self.encoder = nn.ModuleList([
  8. EncoderLayer(hidden_size, num_heads) for _ in range(num_layers)
  9. ])
  10. self.decoder = nn.ModuleList([
  11. DecoderLayer(hidden_size, num_heads) for _ in range(num_layers)
  12. ])
  13. self.fc = nn.Linear(hidden_size, input_size)
  14.  
  15. def forward(self, src, trg):
  16. src = self.embedding(src) # 输入序列的嵌入表示
  17. trg = self.embedding(trg) # 目标序列的嵌入表示
  18. src = self.positional_encoding(src) # 加入位置编码
  19. # 编码器
  20. for encoder_layer in self.encoder:
  21. src = encoder_layer(src)
  22. trg = self.positional_encoding(trg) # 加入位置编码
  23. # 解码器
  24. for decoder_layer in self.decoder:
  25. trg = decoder_layer(src, trg)
  26. output = self.fc(trg) # 最后的线性变换得到输出结果
  27. return F.log_softmax(output, dim=-1)

这段代码定义了一个名为Transformer的模型类。在初始化方法__init__中,模型类定义了Transformer的各个组件。具体来说:

  • embedding是一个嵌入层(Embedding Layer),用于将输入的整数序列转换为密集向量表示。
  • positional_encoding是一个位置编码层(PositionalEncoding),用于为序列中的每个位置添加编码信息,以保留序列中的位置顺序信息。
  • encoder是一个由多个编码器层(EncoderLayer)组成的编码器堆叠。在初始化时,通过循环创建了指定数量的编码器层,并将其存储在nn.ModuleList中。
  • decoder是一个由多个解码器层(DecoderLayer)组成的解码器堆叠。与编码器类似,它也通过循环创建了指定数量的解码器层,并将其存储在nn.ModuleList中。
  • fc是一个线性变换层(Linear Layer),将解码器的输出转换为与目标词汇表大小相同的张量。最后,通过应用log_softmax函数,将输出转换为对目标语言词汇表中每个单词的对数概率。

在前向传播方法forward中,输入的源语言和目标语言序列经过嵌入层和位置编码层后,分别传入编码器和解码器的多层堆叠中。编码器层循环遍历,并依次对源语言序列进行编码。解码器层也类似地循环遍历,并在每个解码器层中,传入源语言序列和目标语言序列,进行解码操作。最后,通过线性变换层将解码器的输出转化为概率分布,并应用log_softmax函数进行标准化,以便用于预测目标语言序列。

  1. class EncoderLayer(nn.Module):
  2. def __init__(self, hidden_size, num_heads):
  3. super(EncoderLayer, self).__init__()
  4. self.multihead_attention = MultiheadAttention(hidden_size, num_heads)
  5. self.feed_forward = FeedForward(hidden_size)
  6.  
  7. def forward(self, x):
  8. residual = x
  9. x = self.multihead_attention(x)
  10. x = x + residual
  11. residual = x
  12. x = self.feed_forward(x)
  13. x = x + residual
  14. return x

这段代码定义了编码器层(EncoderLayer)类。编码器层由两个子层组成:

  • multihead_attention是一个多头注意力机制层(MultiheadAttention),用于在输入序列内部进行自注意力计算。
  • feed_forward是一个前馈神经网络层(FeedForward),用于对每个位置的向量进行非线性变换。

在前向传播方法中,输入通过多头注意力机制层,然后与输入进行残差连接(residual connection)并进行加法操作。接着,将结果输入到前馈神经网络层,并再次与之前的结果进行残差连接和加法操作。最后,输出经过编码器层的变换后返回。

  1. class DecoderLayer(nn.Module):
  2. def __init__(self, hidden_size, num_heads):
  3. super(DecoderLayer, self).__init__()
  4. self.masked_multihead_attention = MultiheadAttention(hidden_size, num_heads)
  5. self.multihead_attention = MultiheadAttention(hidden_size, num_heads)
  6. self.feed_forward = FeedForward(hidden_size)
  7.  
  8. def forward(self, src, trg):
  9. residual = trg
  10. trg = self.masked_multihead_attention(trg)
  11. trg = trg + residual
  12. residual = trg
  13. trg = self.multihead_attention(src, trg)
  14. trg = trg + residual
  15. residual = trg
  16. trg = self.feed_forward(trg)
  17. trg = trg + residual
  18. return trg

这段代码定义了解码器层(DecoderLayer)类。解码器层由三个子层组成:

  • masked_multihead_attention是一个带有遮蔽的多头注意力机制层(MultiheadAttention),用于在解码器内部进行自注意力计算,并且在计算过程中遮蔽(mask)了未来的信息。
  • multihead_attention是一个多头注意力机制层(MultiheadAttention),用于使解码器能够关注编码器的输出。
  • feed_forward是一个前馈神经网络层(FeedForward),用于对每个位置的向量进行非线性变换。

在前向传播方法中,输入的解码器序列通过带有遮蔽的多头注意力机制层。然后将结果与输入进行残差连接并进行加法操作。接下来,将结果传入多头注意力机制层,以便解码器关注编码器的输出。同样地,将结果与之前的结果进行残差连接和加法操作。最后,将结果输入到前馈神经网络层,并再次进行残差连接和加法操作。输出经过解码器层的变换后返回。

  1. class MultiheadAttention(nn.Module):
  2. def __init__(self, hidden_size, num_heads):
  3. super(MultiheadAttention, self).__init__()
  4. self.num_heads = num_heads
  5. self.head_size = hidden_size // num_heads
  6. self.q_linear = nn.Linear(hidden_size, hidden_size)
  7. self.k_linear = nn.Linear(hidden_size, hidden_size)
  8. self.v_linear = nn.Linear(hidden_size, hidden_size)
  9. self.fc = nn.Linear(hidden_size, hidden_size)
  10.  
  11. def forward(self, x):
  12. batch_size, seq_len, hidden_size = x.size()
  13. q = self.q_linear(x).view(batch_size, seq_len, self.num_heads, self.head_size).transpose(1, 2)
  14. k = self.k_linear(x).view(batch_size, seq_len, self.num_heads, self.head_size).transpose(1, 2)
  15. v = self.v_linear(x).view(batch_size, seq_len, self.num_heads, self.head_size).transpose(1, 2)
  16. attention_scores = torch.matmul(q, k.transpose(-2, -1)) / torch.sqrt(torch.tensor(self.head_size, dtype=torch.float32))
  17. attention_weights = F.softmax(attention_scores, dim=-1)
  18. x = torch.matmul(attention_weights, v).transpose(1, 2).contiguous().view(batch_size, seq_len, hidden_size)
  19. x = self.fc(x)
  20. return x

这段代码定义了多头注意力机制层(MultiheadAttention)。在初始化方法中,指定了注意力头数(num_heads)和每个注意力头的大小(head_size)。

在前向传播方法中,输入通过三个线性变换层(q_lineark_linearv_linear)进行线性变换,并进行形状重塑操作,以便进行多头注意力计算。然后,通过矩阵乘法计算注意力分数。注意力分数除以根号下注意力头大小,以进行缩放。接着,通过softmax函数计算注意力权重。

然后,将注意力权重与值(v)相乘,得到注意力加权的表示。最后,通过转置和形状重塑操作,将结果重新恢复为原始形状,并通过线性变换层进行最终的变换。

  1. class FeedForward(nn.Module):
  2. def __init__(self, hidden_size, feed_forward_size=2048):
  3. super(FeedForward, self).__init__()
  4. self.fc1 = nn.Linear(hidden_size, feed_forward_size)
  5. self.fc2 = nn.Linear(feed_forward_size, hidden_size)
  6.  
  7. def forward(self, x):
  8. x = F.relu(self.fc1(x))
  9. x = self.fc2(x)
  10. return x

这段代码定义了前馈神经网络层(FeedForward)。它由两个线性变换层(fc1fc2)组成,中间使用了ReLU激活函数。

在前向传播方法中,输入通过第一个线性变换层,并经过ReLU激活函数进行非线性变换。然后,结果通过第二个线性变换层进行最终的线性变换,并返回结果。

  1. class PositionalEncoding(nn.Module):
  2. def __init__(self, hidden_size, max_seq_len=5000):
  3. super(PositionalEncoding, self).__init__()
  4. self.positional_encoding = self.generate_positional_encoding(hidden_size, max_seq_len)
  5.  
  6. def forward(self, x):
  7. x = x + self.positional_encoding[:x.size(0), :]
  8. return x
  9.  
  10. def generate_positional_encoding(self, hidden_size, max_seq_len):
  11. positional_encoding = torch.zeros(max_seq_len, hidden_size)
  12. position = torch.arange(0, max_seq_len, dtype=torch.float32).unsqueeze(1)
  13. div_term = torch.exp(torch.arange(0, hidden_size, 2, dtype=torch.float32) * -(math.log(10000.0) / hidden_size))
  14. positional_encoding[:, 0::2] = torch.sin(position * div_term)
  15. positional_encoding[:, 1::2] = torch.cos(position * div_term)
  16. return positional_encoding

这段代码定义了位置编码层(PositionalEncoding)。位置编码用于为序列中的每个位置添加编码信息,以保留序列中的位置顺序信息。

在初始化方法中,通过调用generate_positional_encoding方法生成了位置编码矩阵。在前向传播方法中,将输入序列与位置编码矩阵相加,以添加位置编码信息。最后,返回添加位置编码后的结果。

generate_positional_encoding方法根据位置和维度计算位置编码矩阵。首先,创建一个全零矩阵,大小为最大序列长度(max_seq_len)乘以隐藏维度(hidden_size)。然后,通过一系列的数学运算计算每个位置的编码值,并将奇数维度和偶数维度分别填充到位置编码矩阵的对应位置。最后,返回生成的位置编码矩阵。

以上是基于代码的Transformer模型的详细介绍。这个模型由编码器、解码器、注意力机制、前馈神经网络和位置编码等组件构成,用于处理序列数据的建模任务。通过堆叠多个编码器和解码器层,Transformer模型能够捕捉序列中的长距离依赖关系,并在机器翻译、文本生成等任务中取得了显著的效果。

 

 

发表评论

匿名网友

拖动滑块以完成验证
加载失败