Multi-head Attention from Chapter 3

第二章没整理所以没发,凑合看吧

RNN(recruent neruo network)

早期翻译模型

  • 长期依赖问题(Long-term Dependency Issue):
    RNN在处理较长序列时会遇到困难,因为它们依赖于逐步更新的隐藏状态,长序列中的早期信息可能会逐渐被遗忘。因此,在较长句子中,很难保持准确的信息传递,这就影响了翻译的效果。
  • 序列处理的效率低:
    RNN需要按顺序处理数据,意味着它们无法进行并行计算。因此,处理时间较长的句子时,效率会很低。这与transformer不同,后者可以并行处理整个序列,从而显著加快了速度。
    梯度消失和爆炸问题(Gradient Vanishing and Exploding Issues):
  • 在深度神经网络中,由于反向传播的过程,梯度可能会迅速衰减或增长。RNN在序列较长时尤为容易出现梯度消失,导致模型难以有效训练

Attention

  • attention score:
    dot product
    给定输入序列中的每个元素,先通过线性变换生成查询向量(Query)、键向量(Key)、和值向量(Value),以及温度(T)。
    Attention Score=\(\frac{Q\cdot K\cdot V^T}{\sqrt{d_k}}\)

Scaled Dot-Product

  • attention weights:
    Use Softmax: The softmax function converts a vector of values into probabilities, scaling them so that they sum to 1. It does this by exponentiating each value and then dividing by the sum of these exponentials.

  • Context vector

梯度消失问题

梯度消失,就是随着网络层数的增加,梯度在反向传播过程中逐渐减小,甚至接近于0,导致网络参数更新缓慢或停滞,影响模型的训练效果。

  • 梯度消失产生的原因:
    • 激活函数的导数: 许多常用的激活函数,如Sigmoid函数,其导数在饱和区域(即输入值很大或很小)接近于0。当网络层数较多时,多次乘以小于1的导数,就会导致梯度急剧减小。
    • 链式法则: 神经网络的训练是通过链式法则来计算梯度的,每一层的梯度都是前面所有层的梯度的乘积。如果每一层的梯度都小于1,那么随着层数的增加,梯度就会越来越小。
  • 解决梯度消失的方法:
    • 选择合适的激活函数: ReLU、LeakyReLU等激活函数在大部分输入区域的导数为1,可以缓解梯度消失问题。
      Batch Normalization: 通过对每一层的输入进行归一化,可以加速网络的收敛,并减轻梯度消失问题。
    • 残差网络: 通过引入残差连接,可以有效地缓解梯度消失问题,使得网络更容易训练。
      LSTM、GRU等RNN变种: 这些RNN变种在设计上考虑到了梯度消失问题,通过门控机制来控制信息的流动,从而缓解梯度消失。

过拟合及其解决办法

过拟合,就是模型过于复杂,对训练数据拟合得过于完美,以至于对新的、未见过的数据泛化能力很差。

正则化

  • 正则化的原理
    正则化的基本思想是在损失函数中加入一个正则化项。这个正则化项通常是对模型参数的一种惩罚。通过最小化这个新的损失函数,可以使得模型的参数值不会过大,从而减小模型的复杂度,降低过拟合的风险。

  • 常用的正则化方法
    L1正则化: 也称为Lasso回归,它是在损失函数中加入模型参数的绝对值之和。L1正则化倾向于产生稀疏解,即很多参数的值为0,可以起到特征选择的作用。
    L2正则化: 也称为Ridge回归,它是在损失函数中加入模型参数的平方和。L2正则化倾向于让参数的取值尽可能小,从而防止过拟合。
    Dropout: 在训练过程中,随机地“丢弃”一部分神经元,迫使网络学习更加鲁棒的特征。

dropout

随机丢弃神经元: 在每次训练迭代中,Dropout会以一定的概率随机地“丢弃”一部分神经元,也就是暂时不考虑这些神经元的输出。
防止过拟合:

  • 减少神经元之间的共适应: Dropout迫使每个神经元不能依赖于其他特定的神经元,从而减少了神经元之间的复杂的共适应关系,提高了模型的泛化能力。
  • 类似于集成学习: 每一次训练,相当于训练了一个不同的神经网络,Dropout可以看作是对许多不同神经网络的集成,从而减少过拟合。
  • 增强鲁棒性: Dropout使得网络对输入数据的噪声具有更强的鲁棒性。

线性变换

  • 数学定义:

设 V 和 W 是两个向量空间,一个映射 T: V → W 称为线性变换,当且仅当对于任意向量 u, v ∈ V 和标量 c,满足以下两个条件:

加法性: T(u + v) = T(u) + T(v)
齐次性: T(cu) = cT(u)

  • 线性投影层
    它本质上是一个全连接层,通过矩阵乘法将输入的特征映射到另一个特征空间。
    • 将高维特征映射到低维空间,从而减少计算量。将不同特征空间的特征映射到同一个空间,以便进行后续的计算。
    • 结合非线性激活函数,可以引入模型的非线性表达能力,从而学习更复杂的特征表示。
    • 将输出特征的维度调整为后续层所需要的维度,从而实现不同层之间的连接。
#创建为nn.Module的子类
class CausalSelfAttention(nn.Module):
    def __init__(self, d_in, d_out, context_length, dropout, qkv_bias=False):
        #super init通常出现在子类函数构造中以便先构造父类再构造子类,确保构造正确性
        #也在构造子类时调用父类构造方法
        super().__init__()
        self.d_out = d_out#dimension of output
        self.W_query = nn.Linear(d_in, d_out, bias=qkv_bias)#query matrix
        self.W_key   = nn.Linear(d_in, d_out, bias=qkv_bias)#key matrix
        self.W_value = nn.Linear(d_in, d_out, bias=qkv_bias)#value matrix
        self.dropout = nn.Dropout(dropout) # 丢弃率,防止过拟合
        #掩码矩阵(上三角矩阵)用于避免后面token影响前面token
        #PyTorch's tril function with elements below the main diagonal 
        #(including the diagonal itself) set to 1 and above the main diagonal set to 0:
        self.register_buffer('mask', torch.triu(torch.ones(context_length, context_length), diagonal=1)) # New
    #前向传播
    def forward(self, x):
        b, n_tokens, d_in = x.shape # New batch dimension b
        #计算键,查询,值向量
        keys = self.W_key(x)
        queries = self.W_query(x)
        values = self.W_value(x)

        attn_scores = queries @ keys.transpose(1, 2) # Changed transpose
        #将前面的元素分数设为负无穷
        attn_scores.masked_fill_(  # New, _ ops are in-place
            self.mask.bool()[:n_tokens, :n_tokens], -torch.inf) 
        #softmax使总和为1,除以维度根号防止数据过大
        attn_weights = torch.softmax(attn_scores / keys.shape[-1]**0.5, dim=-1)
        #random dropout
        attn_weights = self.dropout(attn_weights) # New
        #计算上下文向量
        context_vec = attn_weights @ values
        return context_vec


class MultiHeadAttentionWrapper(nn.Module):
    def __init__(self, d_in, d_out, context_length, dropout, num_heads, qkv_bias=False):
        super().__init__()
        self.heads = nn.ModuleList(
            #for _ in range是因为不会使用这个值所以可以不写
            #生成一个ModuleList包含num_heads个头
            [CausalSelfAttention(d_in, d_out, context_length, dropout, qkv_bias) 
             for _ in range(num_heads)]
        )
        #nn.Linear是一个能可学习调整参数进行线性变换的子类
        #将合并后的输出通过可学习投影层拟合生成最终格式固定的输出
        self.out_proj = nn.Linear(d_out*num_heads, d_out*num_heads)

    def forward(self, x):
        #用cat函数进行张量维度拼接,dim=-1表示沿着最后一个维度拼接
        context_vec = torch.cat([head(x) for head in self.heads], dim=-1)
        #调用self.out_proj的call方法进行线性变换
        return self.out_proj(context_vec)
#设置随机变量
torch.manual_seed(123)
#设置关联上下文长度
context_length = max_length
d_in = output_dim
#根据输入量设置输出量,压缩信息,提取特征,降低维度
num_heads=2
d_out = d_in // num_heads

mha = MultiHeadAttentionWrapper(d_in, d_out, context_length, 0.0, num_heads)
#许多putorch重写了__call__方法使其能像函数一样被调用
batch = input_embeddings
context_vecs = mha(batch)

print("context_vecs.shape:", context_vecs.shape)
posted @ 2024-10-13 11:15  lyrrr  阅读(82)  评论(0)    收藏  举报