• 基于位置的前馈神经网络


    目录

    1、什么是前馈全连接层

    2、前馈全连接层的作用

    3、代码实现FFN


    1、什么是前馈全连接层

    在Transformers中前馈全连接层就是具有两层线性层的全连接网络

    2、前馈全连接层的作用

    考虑注意力机制可能对复杂过程的拟合程度不够,通过增加家两层网络来增强模型的能力

    3、代码实现FFN

    1. import torch.nn as nn
    2. import numpy as np
    3. from torch.autograd import Variable
    4. from torch.autograd import Variable
    5. import copy
    6. from torch import tensor, softmax
    7. import math
    8. # 构建Embedding类来实现文本嵌入层
    9. class Embeddings(nn.Module):
    10. def __init__(self,vocab,d_model):
    11. """
    12. :param vocab: 词表的大小
    13. :param d_model: 词嵌入的维度
    14. """
    15. super(Embeddings,self).__init__()
    16. self.lut = nn.Embedding(vocab,d_model)
    17. self.d_model = d_model
    18. def forward(self,x):
    19. """
    20. :param x: 因为Embedding层是首层,所以代表输入给模型的文本通过词汇映射后的张量
    21. :return:
    22. """
    23. return self.lut(x) * math.sqrt(self.d_model)
    24. class PositionalEncoding(nn.Module):
    25. def __init__(self,d_model,dropout,max_len=5000):
    26. """
    27. :param d_model: 词嵌入的维度
    28. :param dropout: 随机失活,置0比率
    29. :param max_len: 每个句子的最大长度,也就是每个句子中单词的最大个数
    30. """
    31. super(PositionalEncoding,self).__init__()
    32. self.dropout = nn.Dropout(p=dropout)
    33. pe = torch.zeros(max_len,d_model) # 初始化一个位置编码器矩阵,它是一个0矩阵,矩阵的大小是max_len * d_model
    34. position = torch.arange(0,max_len).unsqueeze(1) # 初始一个绝对位置矩阵 max_len * 1
    35. div_term = torch.exp(torch.arange(0,d_model,2)*-(math.log(1000.0)/d_model)) # 定义一个变换矩阵,跳跃式的初始化
    36. # 将前面定义的变换矩阵进行奇数、偶数的分别赋值
    37. pe[:,0::2] = torch.sin(position*div_term)
    38. pe[:,1::2] = torch.cos(position*div_term)
    39. pe = pe.unsqueeze(0) # 将二维矩阵扩展为三维和embedding的输出(一个三维向量)相加
    40. self.register_buffer('pe',pe) # 把pe位置编码矩阵注册成模型的buffer,对模型是有帮助的,但是却不是模型结构中的超参数或者参数,不需要随着优化步骤进行更新的增益对象。注册之后我们就可以在模型保存后重加载时,将这个位置编码与模型参数一同加载进来
    41. def forward(self, x):
    42. """
    43. :param x: 表示文本序列的词嵌入表示
    44. :return: 最后使用self.dropout(x)对对象进行“丢弃”操作,并返回结果
    45. """
    46. x = x + Variable(self.pe[:, :x.size(1)],requires_grad = False) # 不需要梯度求导,而且使用切片操作,因为我们默认的max_len为5000,但是很难一个句子有5000个词汇,所以要根据传递过来的实际单词的个数对创建的位置编码矩阵进行切片操作
    47. return self.dropout(x)
    48. def subsequent_mask(size):
    49. """
    50. :param size: 生成向后遮掩的掩码张量,参数 size 是掩码张量的最后两个维度大小,它的最后两个维度形成一个方阵
    51. :return:
    52. """
    53. attn_shape = (1,size,size) # 定义掩码张量的形状
    54. subsequent_mask = np.triu(np.ones(attn_shape),k = 1).astype('uint8') # 定义一个上三角矩阵,元素为1,再使用其中的数据类型变为无符号8位整形
    55. return torch.from_numpy(1 - subsequent_mask) # 先将numpy 类型转化为 tensor,再做三角的翻转,将位置为 0 的地方变为 1,将位置为 1 的方变为 0
    56. def attention(query, key, value, mask = None, dropout = None):
    57. """
    58. :param query: 三个张量输入
    59. :param key: 三个张量输入
    60. :param value: 三个张量输入
    61. :param mask: 掩码张量
    62. :param dropout: 传入的 dropout 实例化对象
    63. :return:
    64. """
    65. d_model = query.size(-1) # 得到词嵌入的维度,取 query 的最后一维大小
    66. scores = torch.matmul(query,key.transpose(-2,-1)) / math.sqrt(d_model) # 按照注意力公式,将 query 和 key 的转置相乘,这里是将 key 的最后两个维度进行转置,再除以缩放系数,得到注意力得分张量 scores
    67. if mask is not None:
    68. scores = torch.masked_fill(scores,mask == 0,-1e9) # 使用 tensor 的 mask_fill 方法,将掩码张量和 scores 张量中每一个位置进行一一比较,如果掩码张量处为 0 ,则使用 -1e9 替换
    69. # scores = scores.masked_fill(mask == 0,-1e9)
    70. p_attn = softmax(scores, dim = -1) # 对 scores 的最后一维进行 softmax 操作,使用 F.softmax 方法,第一个参数是 softmax 对象,第二个参数是最后一个维度,得到注意力矩阵
    71. print('scores.shape ',scores.shape)
    72. if dropout is not None:
    73. p_attn = dropout(p_attn)
    74. return torch.matmul(p_attn,value),p_attn # 返回注意力表示
    75. class MultiHeadAttention(nn.Module):
    76. def __init__(self, head, embedding_dim , dropout=0.1):
    77. """
    78. :param head: 代表几个头的参数
    79. :param embedding_dim: 词向量维度
    80. :param dropout: 置零比率
    81. """
    82. super(MultiHeadAttention, self).__init__()
    83. assert embedding_dim % head == 0 # 确认一下多头的数量可以整除词嵌入的维度 embedding_dim
    84. self.d_k = embedding_dim // head # 每个头获得词向量的维度
    85. self.head = head
    86. self.linears = nn.ModuleList([copy.deepcopy(nn.Linear(embedding_dim, embedding_dim)) for _ in range(4)]) # 深层拷贝4个线性层,每一个层都是独立的,保证内存地址是独立的,分别是 Q、K、V以及最终的输出线性层
    87. self.attn = None # 初始化注意力张量
    88. self.dropout = nn.Dropout(p=dropout)
    89. def forward(self, query, key, value, mask=None):
    90. """
    91. :param query: 查询query [batch size, sentence length, d_model]
    92. :param key: 待查询key [batch size, sentence length, d_model]
    93. :param value: 待查询value [batch size, sentence length, d_model]
    94. :param mask: 计算相似度得分时的掩码(设置哪些输入不计算到score中)[batch size, 1, sentence length]
    95. :return:
    96. """
    97. if mask is not None:
    98. mask = mask.unsqueeze(1) # 将掩码张量进行维度扩充,代表多头中的第 n 个头
    99. batch_size = query.size(0)
    100. query, key, value = [l(x).view(batch_size, -1, self.head, self.d_k).transpose(1, 2) for l, x in zip(self.linears, (query, key, value))] # 将1、2维度进行调换,目的是让句子长度维度和词向量维度靠近,这样注意力机制才能找到词义与句子之间的关系
    101. # 将每个头传递到注意力层
    102. x, self.attn = attention(query, key, value, mask=mask,
    103. dropout=self.dropout)
    104. # 得到每个头的计算结果是 4 维的张量,需要形状的转换
    105. # 前面已经将1,2两个维度进行转置了,所以这里要重新转置回来
    106. # 前面已经经历了transpose,所以要使用contiguous()方法,不然无法使用 view 方法
    107. x = x.transpose(1, 2).contiguous() \
    108. .view(batch_size, -1, self.head * self.d_k)
    109. return self.linears[-1](x) # 在最后一个线性层中进行处理,得到最终的多头注意力结构输出
    1. import torch.nn as nn
    2. import torch
    3. class PositionFeedForward(nn.Module):
    4. def __init__(self, d_model, d_ff, dropout=0.1):
    5. """
    6. :param d_model: 输入维度,词嵌入的维度
    7. :param d_ff: 第一个的输出连接第二个的输入
    8. :param dropout: 置零比率
    9. """
    10. super(PositionFeedForward, self).__init__()
    11. self.linear1 = nn.Linear(d_model, d_ff)
    12. self.linear2 = nn.Linear(d_ff, d_model)
    13. self.dropout = nn.Dropout(p = dropout)
    14. def forward(self, x):
    15. return self.linear2(self.dropout(torch.relu(self.linear1(x))))
    1. # 实例化参数
    2. d_model = 512
    3. dropout = 0.1
    4. max_len = 60 # 句子最大长度
    5. #-----------------------------------------词嵌入层
    6. # 输入 x 是 Embedding层输出的张量,形状为 2 * 4 * 512
    7. x = Variable(torch.LongTensor([[100,2,42,508],[491,998,1,221]]))
    8. emb = Embeddings(1000,512) # 嵌入层
    9. embr = emb(x)
    10. #-----------------------------------------位置编码
    11. pe = PositionalEncoding(d_model, dropout,max_len) # 位置编码
    12. pe_result = pe(embr)
    13. #-----------------------------------------多头注意力机制
    14. head = 8
    15. embedding_dim = 512
    16. dropout = 0.2
    17. query = key = value = pe_result
    18. mask = torch.zeros(2,4,4)
    19. mha = MultiHeadAttention(head,embedding_dim,dropout)
    20. mha_result = mha(query,key,value,mask)
    21. #-----------------------------------------FFN
    22. d_model = 512
    23. d_ff = 62
    24. dropout = 0.2
    25. ff = PositionFeedForward(d_model,d_ff,dropout)
    26. x = mha_result
    27. ff_result = ff(x)
    28. print(ff_result)
    29. print(ff_result.shape)

    scores.shape  torch.Size([2, 8, 4, 4])
    tensor([[[ 0.4427, -1.4212, -0.3038,  ..., -0.0478, -0.9644,  1.0128],
             [-0.1761, -1.7056,  0.2831,  ..., -0.2049, -1.4458,  0.6031],
             [-1.0499, -0.6499, -0.6668,  ..., -0.4854, -1.1480,  0.2326],
             [-0.4020, -1.5800,  0.0879,  ...,  0.4629, -0.1393,  0.6648]],

            [[ 0.9657, -1.0271, -0.8961,  ..., -2.3648, -0.1479, -0.0632],
             [ 1.1462, -0.6441, -0.6195,  ..., -1.3566, -0.8735, -0.9672],
             [ 0.7842, -0.7735, -0.1261,  ..., -0.8950, -0.1457, -0.5313],
             [ 0.2848, -0.3641,  0.3231,  ..., -3.7226,  1.0617, -0.1215]]],
           grad_fn=)
    torch.Size([2, 4, 512])

  • 相关阅读:
    Spring Boot 面试热点(一)
    Redis6通信协议升级至RESP3,一口气看完13种新数据类型
    极限多标签之-PfastreXML
    docker管理之consul注册中心
    测试行业本科应届生薪资大概是多少?外行人15k垫底25k是人均水平...
    CNN反向求导推导
    Condition底层源码
    头歌实训答案:招聘数据分析
    华为海思雄起!正出货国产监控镜头芯片 | 百能云芯
    手机也可以搭建个人博客?安卓Termux+Hexo搭建属于你自己的博客网站【cpolar实现公网访问】
  • 原文地址:https://blog.csdn.net/qq_51691366/article/details/133755776