深度学习第四阶段:NLP第二章 Transformer学习笔记

引言1:什么是注意力机制

参考我的一篇文章:https://blog.csdn.net/weixin_42110638/article/details/134011134?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22134011134%22%2C%22source%22%3A%22weixin_42110638%22%7D

引言2:什么是自注意力机制

参考我的一篇文章:

https://blog.csdn.net/weixin_42110638/article/details/134016569?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22134016569%22%2C%22source%22%3A%22weixin_42110638%22%7D

 

Transformer --- 超nb的特征提取器

《1》Transformer简明教程(蓝斯诺特)

(Transformer原理及其各领域应用分析)

 

 

 

 

 

 

 

注意dk表示每个头维数的平方根(同时当然也是K矩阵维数的平方根)

eg:

下面两张图非常重要

 

 

 

 

 

 

 

 所以橙色的x1和x2此时一定不相等(在绿色的x1x2相等的情况下)

 

 上面的图又属于天才设计了

 

 这就是PE矩阵(位置编码矩阵)

 

 

 

 

 

 

在Transformer模型中,掩码(mask)用于控制模型对输入序列的访问和关注程度。具体来说,Transformer模型使用两种类型的掩码:填充掩码(padding mask)和注意力掩码(attention mask)。

1. **填充掩码(Padding Mask)**:填充掩码用于处理可变长度的输入序列。在Transformer中,输入序列通常会被填充到相同的长度,较短的序列会用特定的填充符号进行填充。填充掩码的目的是告诉模型哪些位置是填充符号,模型在计算注意力时应该忽略这些位置。通过将填充位置的掩码值设置为一个较大的负数,或者使用二进制掩码(0代表填充位,1代表非填充位),可以实现填充掩码的效果。

2. **注意力掩码(Attention Mask)**:注意力掩码用于控制模型在计算注意力时关注哪些位置。在Transformer中,自注意力机制(self-attention)被用于计算输入序列中每个位置与其他位置的关联程度。注意力掩码可以告诉模型在每个位置上是否应该考虑其他位置的信息。例如,在语言生成任务中,为了保持生成的语言的自洽性,通常会使用上三角矩阵掩码,使模型只能关注该位置之前的信息,而不能看到未来的信息。注意力掩码通常使用二进制掩码来表示,其中值为0表示需要被掩码的位置,1表示不需要被掩码的位置。

掩码的应用通常是通过与注意机制结合来实现的。掩码通过在注意力计算的过程中对某些位置进行屏蔽或过滤,使模型能够有选择地关注感兴趣或有效的位置,从而提高模型的性能和效果。

Add就表示短接(可简单理解为直接相加)

Transformer中的短接操作是指将某些位置的输入直接连接到某些位置的输出,从而在不同的位置共享参数。这种共享参数的方式可以使得模型训练更加高效,并且能够捕捉到长距离的依赖关系。具体而言,Transformer中的短接操作有两种形式:多头自注意力机制中的短接和残差连接中的短接。

在多头自注意力机制中的短接操作中,模型会通过在不同的层次上计算自注意力矩阵来学习输入序列中不同位置之间的依赖关系。在这个过程中,模型会将输入序列中的每个位置都与其他位置进行计算,从而得到一个注意力分布向量。为了加速计算,多头自注意力机制中使用了短接操作,即将输入序列的某些位置直接连接到输出序列的某些位置,从而共享参数。

在残差连接中的短接操作中,模型会通过将输入序列的某些位置直接连接到输出序列的某些位置,从而在不同层次之间共享参数。这样做的目的是为了避免在深层网络中出现梯度消失或梯度爆炸等问题。对于每个层次,模型会首先进行一些短接操作,然后再通过一些线性变换和非线性激活函数来生成输出序列。最终,所有层次的输出序列都会被相加,并通过一些线性变换和激活函数来生成最终的Transformer输出序列。

 

 

 decoder也是如此

解码器和编码器的不同之处仅仅是两个自注意力层有一点小改进:

第一个自注意力层引入了mask机制来防止对于未来词的预测

第一个自注意力层即这个编码解码注意力层,其本质还是self-Attention,小小的区别如下: 

Encoder-Decoder Attention中的V来自解码器(解码器中第一个自注意力层计算出来的结果作为V),而Q和K来自编码器(是拿编码器计算的结果作为Q和K而不是解码器!!!);Self-Attention中的Q、K、V都来自于同一个输入序列。这是两种不同的注意力机制,在不同的上下文中有不同的使用方式和作用。

注意:上图解码器有两个自注意力层!!

 

 

之前的图和下图都是一个意思只是画的方式不一样 

 

 

 

 

 

 

 

 

 上图第二行有问题

 

 

 

 

 

 

随机采样是指从一个集合中随机地选择一个或多个元素。在计算机科学和统计学中,随机采样是一种常用的方法,用于从大量的数据中抽取一部分样本用于分析或实验。

在自然语言处理中,随机采样常常用于从一个词汇表或语料库中选择一些词语或句子。通过随机采样,可以获取一部分数据用于统计分析、模型训练或数据预处理等任务。随机采样可以提供一些随机性和多样性,以更好地代表整个数据集。

在代码示例中,随机采样用于从一个词汇列表中选择n个词语。通过随机采样,可以获取一个随机的、符合一定概率分布的词语样本,用于后续的处理或分析。这种随机采样一般用于数据的扩增、生成随机样本、构建负样本集等应用场景。

 

 

 

 

 

 

# 定义字典
zidian_x = '<SOS>,<EOS>,<PAD>,0,1,2,3,4,5,6,7,8,9,q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l,z,x,c,v,b,n,m'
zidian_x = {word: i for i, word in enumerate(zidian_x.split(','))}

zidian_xr = [k for k, v in zidian_x.items()]

zidian_y = {k.upper(): v for k, v in zidian_x.items()}

zidian_yr = [k for k, v in zidian_y.items()]

import random

import numpy as np
import torch


def get_data():
    # 定义词集合
    words = [
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'q', 'w', 'e', 'r',
        't', 'y', 'u', 'i', 'o', 'p', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k',
        'l', 'z', 'x', 'c', 'v', 'b', 'n', 'm'
    ]

    # 定义每个词被选中的概率
    p = np.array([
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
        13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26
    ])
    p = p / p.sum()

    # 随机选n个词
    n = random.randint(30, 48)
    x = np.random.choice(words, size=n, replace=True, p=p)

    # 采样的结果就是x
    x = x.tolist()

    # y是对x的变换得到的
    # 字母大写,数字取10以内的互补数
    def f(i):
        i = i.upper()
        if not i.isdigit():
            return i
        i = 9 - int(i)
        return str(i)

    y = [f(i) for i in x]
    y = y + [y[-1]]
    # 逆序
    y = y[::-1]

    # 加上首尾符号
    x = ['<SOS>'] + x + ['<EOS>']
    y = ['<SOS>'] + y + ['<EOS>']

    # 补pad到固定长度
    x = x + ['<PAD>'] * 50
    y = y + ['<PAD>'] * 51
    x = x[:50]
    y = y[:51]

    # 编码成数据
    x = [zidian_x[i] for i in x]
    y = [zidian_y[i] for i in y]

    # 转tensor
    x = torch.LongTensor(x)
    y = torch.LongTensor(y)

    return x, y




# 定义数据集
class Dataset(torch.utils.data.Dataset):
    def __init__(self):
        super(Dataset, self).__init__()

    def __len__(self):
        return 100000

    def __getitem__(self, i):
        return get_data()


# 数据加载器
loader = torch.utils.data.DataLoader(dataset=Dataset(),
                                     batch_size=8,
                                     drop_last=True,
                                     shuffle=True,
                                     collate_fn=None)

debug一下

 

 

 

 

 

 

 - `masked_fill_(mask, -float('inf'))` 方法将 mask 张量的值为 True 的位置,对应到 score 张量上,并把这些位置的值替换为 -float('inf'),也就是负无穷。这样,当使用 softmax 函数计算这些位置对应的概率时,它们的结果会趋近于 0,从而达到 mask 的效果。 - `torch.softmax(score, dim=-1)` 方法使用 softmax 函数,对 score 张量在最后一个维度上进行计算,并返回结果张量。在这种情况下,最后一个维度通常对应于输出类别的数量,因此这个操作相当于计算每个输出类别的概率分布。

# 注意力计算函数
def attention(Q, K, V, mask):
    # b句话,每句话50个词,每个词编码成32维向量,4个头,每个头分到8维向量
    # Q,K,V = [b, 4, 50, 8]

    # [b, 4, 50, 8] * [b, 4, 8, 50] -> [b, 4, 50, 50]
    # Q,K矩阵相乘,求每个词相对其他所有词的注意力
    score = torch.matmul(Q, K.permute(0, 1, 3, 2))

    # 除以每个头维数的平方根,做数值缩放
    score /= 8 ** 0.5

    # mask遮盖,mask是true的地方都被替换成-inf(负无穷),这样在计算softmax的时候,-inf会被压缩到0
    # mask = [b, 1, 50, 50]
    score = score.masked_fill_(mask, -float('inf'))
    score = torch.softmax(score, dim=-1)

    # 以注意力分数乘以V,得到最终的注意力结果
    # [b, 4, 50, 50] * [b, 4, 50, 8] -> [b, 4, 50, 8]
    score = torch.matmul(score, V)

    # 每个头计算的结果合一
    # [b, 4, 50, 8] -> [b, 50, 32]
    score = score.permute(0, 2, 1, 3).reshape(-1, 50, 32)

    return score

 注意力计算函数的返回结果通常是一个代表注意力权重的张量或矩阵它用于表示模型在进行注意力机制时对输入的关注程度

 

 

 

 

 

 

 下面说的就比较专业了(看看即可)

BN(Batch Normalization)和LN(Layer Normalization)是两种常用的归一化技术,它们具有相似的目标,即通过归一化输入数据的分布来帮助神经网络训练。然而,它们的操作范围和计算方式有一些不同。

1. BN(Batch Normalization):
   - BN 是在批次维度上对数据进行归一化处理。在卷积神经网络(CNN)中,BN 在每个通道维度上计算均值和方差,并使用批次中的所有样本来规范化数据。
   - BN 的计算是基于每个批次数据的统计信息,因此它能够平衡每个批次之间的分布差异。
   - BN 可以作为模型的一部分,在网络的每一层后面应用 BN 操作。它可以显著减少模型对初始化和学习率的依赖,有助于加速模型的收敛,同时还可以起到一定程度的正则化作用。

2. LN(Layer Normalization):
   - LN 是在特征维度上对数据进行归一化处理。对于一维信号(例如自然语言处理中的句子表示),LN 在每个样本上计算均值和方差,并使用当前样本的特征维度进行规范化。
   - LN 的计算是基于每个样本的统计信息,因此它可以平衡样本之间特征的分布差异。
   - LN 通常应用于循环神经网络(RNN)等模型中,它可以处理不定长的序列数据,因为 LN 的计算是在每个样本上独立进行的。
   
总结来说,BN 在批次维度上计算均值和方差,LN 在特征维度上计算均值和方差。BN 的计算是在每个批次中进行的,而 LN 的计算是在每个样本中进行的。这些差异使得 BN 和 LN 在使用场景和计算方式上有所不同,需要根据具体的模型和任务来选择合适的归一化方法。

 

 

 

 

 经过广泛论证后的结论,这样能够帮助模型更好地收敛

 

# 多头注意力计算层
class MultiHead(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.fc_Q = torch.nn.Linear(32, 32)# 就是那三个矩阵
        self.fc_K = torch.nn.Linear(32, 32)
        self.fc_V = torch.nn.Linear(32, 32)

        self.out_fc = torch.nn.Linear(32, 32)

        # 规范化之后,均值是0,标准差是1
        # BN是取不同样本做归一化
        # LN是取不同通道做归一化
        # affine=True,elementwise_affine=True,指定规范化后,再计算一个线性映射
        # norm = torch.nn.BatchNorm1d(num_features=4, affine=True)
        # print(norm(torch.arange(32, dtype=torch.float32).reshape(2, 4, 4)))
        """
        [[[-1.1761, -1.0523, -0.9285, -0.8047],
         [-1.1761, -1.0523, -0.9285, -0.8047],
         [-1.1761, -1.0523, -0.9285, -0.8047],
         [-1.1761, -1.0523, -0.9285, -0.8047]],

        [[ 0.8047,  0.9285,  1.0523,  1.1761],
         [ 0.8047,  0.9285,  1.0523,  1.1761],
         [ 0.8047,  0.9285,  1.0523,  1.1761],
         [ 0.8047,  0.9285,  1.0523,  1.1761]]]"""

        # norm = torch.nn.LayerNorm(normalized_shape=4, elementwise_affine=True)
        # print(norm(torch.arange(32, dtype=torch.float32).reshape(2, 4, 4)))
        """
        [[[-1.3416, -0.4472,  0.4472,  1.3416],
         [-1.3416, -0.4472,  0.4472,  1.3416],
         [-1.3416, -0.4472,  0.4472,  1.3416],
         [-1.3416, -0.4472,  0.4472,  1.3416]],

        [[-1.3416, -0.4472,  0.4472,  1.3416],
         [-1.3416, -0.4472,  0.4472,  1.3416],
         [-1.3416, -0.4472,  0.4472,  1.3416],
         [-1.3416, -0.4472,  0.4472,  1.3416]]]"""

        self.norm = torch.nn.LayerNorm(normalized_shape=32, elementwise_affine=True)

        self.dropout = torch.nn.Dropout(p=0.1)

    def forward(self, Q, K, V, mask):
        # b句话,每句话50个词,每个词编码成32维向量
        # Q,K,V = [b, 50, 32]
        b = Q.shape[0]

        # 保留下原始的Q,后面要做短接用
        clone_Q = Q.clone() # 克隆副本,并不共享空间!

        # 规范化
        Q = self.norm(Q)
        K = self.norm(K)
        V = self.norm(V)

        # 线性运算,维度不变
        # [b, 50, 32] -> [b, 50, 32]
        K = self.fc_K(K)
        V = self.fc_V(V)
        Q = self.fc_Q(Q)

        # 拆分成多个头
        # b句话,每句话50个词,每个词编码成32维向量,4个头,每个头分到8维向量
        # [b, 50, 32] -> [b, 4, 50, 8]
        Q = Q.reshape(b, 50, 4, 8).permute(0, 2, 1, 3)
        K = K.reshape(b, 50, 4, 8).permute(0, 2, 1, 3)
        V = V.reshape(b, 50, 4, 8).permute(0, 2, 1, 3)

        # 计算注意力
        # [b, 4, 50, 8] -> [b, 50, 32]
        score = attention(Q, K, V, mask)

        # 计算输出,维度不变
        # [b, 50, 32] -> [b, 50, 32]
        score = self.dropout(self.out_fc(score))# dropout放在过拟合

        # 短接
        score = clone_Q + score
        return score

 他是一个工具层,他的使用是在model.py中,在Transformer建模时会用到这个工具层

 

 

 

 

 

 

 

 

# 位置编码层
class PositionEmbedding(torch.nn.Module):
    def __init__(self):
        super().__init__()

        # pos是第几个词,i是第几个维度,d_model是维度总数
        def get_pe(pos, i, d_model):# 用于计算位置编码的值(计算PE矩阵)(都是数学公式)
            fenmu = 1e4 ** (i / d_model)
            pe = pos / fenmu

            if i % 2 == 0:
                return math.sin(pe)
            return math.cos(pe)

        # 初始化位置编码矩阵
        pe = torch.empty(50, 32)
        for i in range(50):
            for j in range(32):
                pe[i, j] = get_pe(i, j, 32)
        pe = pe.unsqueeze(0)# 维度扩展,将维度从 `(50, 32)` 扩展到 `(1, 50, 32),第0个维度为批次大小

        # 定义为不更新的常量
        self.register_buffer('pe', pe)

        # 词编码层
        self.embed = torch.nn.Embedding(39, 32)
        # 初始化参数
        self.embed.weight.data.normal_(0, 0.1)

    def forward(self, x):
        # [8, 50] -> [8, 50, 32]
        embed = self.embed(x)

        # 词编码和位置编码相加
        # [8, 50, 32] + [1, 50, 32] -> [8, 50, 32](广播机制)
        embed = embed + self.pe
        return embed# 这个张量包含了词嵌入和位置编码的融合信息

 

 很简单很好理解

 

在自然语言处理任务中,序列数据通常会被填充(pad)到固定长度,以方便进行批处理和输入到模型中。然而,填充的数据对于模型的学习是没有意义的,因为它们不包含任何有用的信息。因此,在进行自注意力计算或其他涉及注意力机制的操作时,需要对填充部分进行掩码(mask)处理,以排除填充部分的影响。

**Mask的原理**:Mask的目的是在计算注意力时将填充部分的影响消除。常见的Mask方式是通过将填充部分的注意力权重设置为0来实现。这样,在进行注意力计算时,填充部分的词与其他词之间的注意力权重就会被置为0,从而排除它们的影响。

**Pad Mask的原理**:Pad Mask是一种常见的Mask方式,用于在自注意力计算中排除填充部分的影响。Pad Mask的原理是将填充部分对应的位置设置为True,其他位置设置为False。在计算注意力时,如果某个位置的Mask为True,即表示该位置对其他位置的注意力为0,即不考虑它对其他位置的影响。通过使用Pad Mask,模型可以忽略填充部分的输入,并且不会为填充部分产生无意义的注意力权重。

在实际应用中,Pad Mask通常与注意力计算结合使用。在计算自注意力得分时,将注意力分数乘以Pad Mask的张量,以实现填充部分注意力为0的效果。这样,填充部分的影响在计算注意力权重时会被抑制,不会对模型的学习造成干扰。

 

 

 

def mask_pad(data):
    # b句话,每句话50个词,这里是还没embed的
    # data = [b, 50]
    # 判断每个词是不是<PAD>,并将结果保存在 `mask` 中
    mask = data == zidian_x['<PAD>']# `zidian_x['<PAD>']` 表示 `<PAD>` 这个特殊词在词汇表中的索引。
    # `mask` 是一个布尔类型的张量,与 `data` 的形状相同,其中的值为 `True` 表示该位置的词是 `<PAD>`,为 `False` 表示该位置的词不是 `<PAD>`

    # [b, 50] -> [b, 1, 1, 50]
    mask = mask.reshape(-1, 1, 1, 50)

    # 在计算注意力时,是计算50个词和50个词相互之间的注意力,所以是个50*50的矩阵
    # 是pad的列是true,意味着任何词对pad的注意力都是0
    # 但是pad本身对其他词的注意力并不是0
    # 所以是pad的行不是true

    # 复制n次
    # [b, 1, 1, 50] -> [b, 1, 50, 50]
    mask = mask.expand(-1, 1, 50, 50)# 在指定维度上复制张量元素,从而扩展张量的大小
    # -1,表示在第一维度上不进行复制,即保留原来的大小。
    # 在第二维和第三维上进行复制,每个元素复制50次

    return mask

经过expand函数的扩展,原来大小为[b, 1, 1, 50]的4维张量被扩展为大小为[b, 1, 50, 50]的4维张量,其中第二维和第三维都扩展为50,以便在后续的计算中使用。这个扩展操作的目的是为了在将注意力分配给词时,能够避免填充项对注意力分配的干扰

def mask_tril(data): # 上三角mask
    # b句话,每句话50个词,这里是还没embed的
    # data = [b, 50]

    # 50*50的矩阵表示每个词对其他词是否可见
    # 上三角矩阵,不包括对角线,意味着,对每个词而言,他只能看到他自己,和他之前的词,而看不到之后的词
    # [1, 50, 50]
    """ 只画了五行,实际是50 * 50
    [[0, 1, 1, 1, 1],
     [0, 0, 1, 1, 1],
     [0, 0, 0, 1, 1],
     [0, 0, 0, 0, 1],
     [0, 0, 0, 0, 0]]"""
    tril = 1 - torch.tril(torch.ones(1, 50, 50, dtype=torch.long))

    # 判断y当中每个词是不是pad,如果是pad则不可见
    # [b, 50]
    mask = data == zidian_y['<PAD>']

    # 变形+转型,为了之后的计算
    # [b, 50] -》 [b, 1, 50]
    mask = mask.unsqueeze(1).long()

    # mask和tril求并集(看ppt)
    # [b, 1, 50] + [1, 50, 50] -> [b, 50, 50]
    mask = mask + tril

    # 转布尔型
    mask = mask > 0

    # 转布尔型,增加一个维度,便于后续的计算(为了广播)
    # [b, 50, 50]扩展为[b, 1, 50, 50]
    mask = (mask == 1).unsqueeze(dim=1)

    return mask

 

 

 

三个编码器层是一个前后串联的关系

 

 

 

 

 

 

 

# 主模型
class Transformer(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.embed_x = PositionEmbedding()
        self.embed_y = PositionEmbedding()
        self.encoder = Encoder()
        self.decoder = Decoder()
        self.fc_out = torch.nn.Linear(32, 39)

    def forward(self, x, y):
        # [b, 1, 50, 50]
        mask_pad_x = mask_pad(x)
        mask_tril_y = mask_tril(y)

        # 编码,添加位置信息
        # x = [b, 50] -> [b, 50, 32]
        # y = [b, 50] -> [b, 50, 32]
        x, y = self.embed_x(x), self.embed_y(y)

        # 编码层计算
        # [b, 50, 32] -> [b, 50, 32]
        x = self.encoder(x, mask_pad_x)

        # 解码层计算
        # [b, 50, 32],[b, 50, 32] -> [b, 50, 32]
        y = self.decoder(x, y, mask_pad_x, mask_tril_y)

        # 全连接输出
        # [b, 50, 32] -> [b, 50, 39]
        y = self.fc_out(y)

        return y

 

 

 

model = Transformer()
loss_func = torch.nn.CrossEntropyLoss()
optim = torch.optim.Adam(model.parameters(), lr=2e-3)
sched = torch.optim.lr_scheduler.StepLR(optim, step_size=3, gamma=0.5)# 本例用不上

for epoch in range(1):# 只有一个epoch
    for i, (x, y) in enumerate(loader):
        # x = [8, 50]
        # y = [8, 51]  (y比x多一位,故意的)

        # 在训练时,是拿y的每一个字符输入,预测下一个字符,所以不需要最后一个字
        # [8, 50, 39]
        pred = model(x, y[:, :-1])# 训练时不要最后一位(y:51 -》 50)

        # [8, 50, 39] -> [400, 39]
        pred = pred.reshape(-1, 39) # 方便我们一会计算loss

        # [8, 51] -> [400] , 同样也是方便我们一会计算loss
        y = y[:, 1:].reshape(-1)# 把y的第一位也移除掉(因为第0个字符一定是sos,没有预测的必要)

        # 忽略pad(过滤操作)
        select = y != zidian_y['<PAD>'] # 只要那些不是pad的
        pred = pred[select]
        y = y[select]

        loss = loss_func(pred, y)
        optim.zero_grad()
        loss.backward()
        optim.step()

        if i % 200 == 0: # 200个batch输出一次
            # [select, 39] -> [select]
            pred = pred.argmax(1)
            correct = (pred == y).sum().item()
            accuracy = correct / len(pred)
            lr = optim.param_groups[0]['lr']
            print(epoch, i, lr, loss.item(), accuracy)

    sched.step()

# 预测函数
def predict(x):
    # x = [1, 50]
    model.eval()

    # [1, 1, 50, 50]
    mask_pad_x = mask_pad(x)

    # 初始化输出,这个是固定值
    # [1, 50]
    # [[0,2,2,2...]]
    target = [zidian_y['<SOS>']] + [zidian_y['<PAD>']] * 49
    target = torch.LongTensor(target).unsqueeze(0)

    # x编码,添加位置信息
    # [1, 50] -> [1, 50, 32]
    x = model.embed_x(x)

    # 编码层计算,维度不变
    # [1, 50, 32] -> [1, 50, 32]
    x = model.encoder(x, mask_pad_x)

    # 遍历生成第1个词到第49个词
    for i in range(49):
        # [1, 50]
        y = target

        # [1, 1, 50, 50]
        mask_tril_y = mask_tril(y)

        # y编码,添加位置信息
        # [1, 50] -> [1, 50, 32]
        y = model.embed_y(y)

        # 解码层计算,维度不变
        # [1, 50, 32],[1, 50, 32] -> [1, 50, 32]
        y = model.decoder(x, y, mask_pad_x, mask_tril_y)

        # 全连接输出,39分类
        # [1, 50, 32] -> [1, 50, 39]
        out = model.fc_out(y)

        # 取出当前词的输出
        # [1, 50, 39] -> [1, 39]
        out = out[:, i, :]

        # 取出分类结果
        # [1, 39] -> [1]
        out = out.argmax(dim=1).detach()

        # 以当前词预测下一个词,填到结果中
        target[:, i + 1] = out

    return target

# 测试
for i, (x, y) in enumerate(loader):
    break

for i in range(8):
    print(i)
    print(''.join([zidian_xr[i] for i in x[i].tolist()]))
    print(''.join([zidian_yr[i] for i in y[i].tolist()]))
    print(''.join([zidian_yr[i] for i in predict(x[i].unsqueeze(0))[0].tolist()]))

 

 

# 输出结果展示
0 0 0.002 3.794543743133545 0.02531645569620253
0 200 0.002 3.379639148712158 0.05688622754491018
0 400 0.002 3.355764627456665 0.07669616519174041
0 600 0.002 3.296332597732544 0.07784431137724551
0 800 0.002 3.2488296031951904 0.0935483870967742
0 1000 0.002 3.2213733196258545 0.11884057971014493
0 1200 0.002 2.5024263858795166 0.29102167182662536
0 1400 0.002 1.6208170652389526 0.5173501577287066
0 1600 0.002 1.292130708694458 0.5825242718446602
0 1800 0.002 0.8708481192588806 0.743202416918429
0 2000 0.002 0.846079409122467 0.735632183908046
0 2200 0.002 0.6251144409179688 0.8128834355828221
0 2400 0.002 0.3578456938266754 0.8832807570977917
0 2600 0.002 0.36309942603111267 0.8727272727272727
0 2800 0.002 0.289601594209671 0.9083094555873925
0 3000 0.002 0.2831581234931946 0.9191616766467066
0 3200 0.002 0.526832640171051 0.8436578171091446
0 3400 0.002 0.2148950845003128 0.94
0 3600 0.002 0.15710806846618652 0.9449838187702265
0 3800 0.002 0.41593486070632935 0.875
0 4000 0.002 0.36190298199653625 0.9034267912772586
0 4200 0.002 0.25557422637939453 0.9335260115606936
0 4400 0.002 0.22930747270584106 0.9373219373219374
0 4600 0.002 0.17708970606327057 0.9525316455696202
0 4800 0.002 0.17756684124469757 0.9362416107382551
0 5000 0.002 0.1397412270307541 0.9628482972136223
0 5200 0.002 0.15159684419631958 0.9538461538461539
0 5400 0.002 0.10061994940042496 0.9659863945578231
0 5600 0.002 0.08851948380470276 0.972972972972973
0 5800 0.002 0.16964299976825714 0.9625
0 6000 0.002 0.09395483881235123 0.9695121951219512
0 6200 0.002 0.17452788352966309 0.9519230769230769
0 6400 0.002 0.10666704177856445 0.964968152866242
0 6600 0.002 0.04998258501291275 0.9867109634551495
0 6800 0.002 0.17233484983444214 0.9582089552238806
0 7000 0.002 0.07549930363893509 0.9748427672955975
0 7200 0.002 0.20033639669418335 0.946031746031746
0 7400 0.002 0.3773764371871948 0.9191616766467066
0 7600 0.002 0.08495186269283295 0.9722222222222222
0 7800 0.002 0.17021967470645905 0.9597523219814241
0 8000 0.002 0.0637650415301323 0.9878787878787879
0 8200 0.002 0.13649322092533112 0.9552238805970149
0 8400 0.002 0.08063231408596039 0.9713467048710601
0 8600 0.002 0.15839743614196777 0.9619883040935673
0 8800 0.002 0.21824541687965393 0.9416666666666667
0 9000 0.002 0.08099698275327682 0.9737704918032787
0 9200 0.002 0.06642208248376846 0.976878612716763
0 9400 0.002 0.1371884047985077 0.9662576687116564
0 9600 0.002 0.036332424730062485 0.99079754601227
0 9800 0.002 0.04949095472693443 0.9817629179331308
0 10000 0.002 0.03725859150290489 0.9876160990712074
0 10200 0.002 0.10319410264492035 0.9627329192546584
0 10400 0.002 0.03377262130379677 0.9913294797687862
0 10600 0.002 0.07356279343366623 0.9698996655518395
0 10800 0.002 0.11908997595310211 0.9585798816568047
0 11000 0.002 0.04587508365511894 0.9847094801223242
0 11200 0.002 0.03367609903216362 0.9881656804733728
0 11400 0.002 0.08084876090288162 0.9766081871345029
0 11600 0.002 0.12784895300865173 0.9541284403669725
0 11800 0.002 0.07926677912473679 0.9757575757575757
0 12000 0.002 0.11273867636919022 0.9516616314199395
0 12200 0.002 0.0546293631196022 0.9838709677419355
0 12400 0.002 0.08362181484699249 0.9706840390879479
0
<SOS>sc8bgsya6d56jvzfcekjpugizuofb64bgddfxjasjnx3<EOS><PAD><PAD><PAD><PAD>
<SOS>66XNJSAJXFDDGB53BFOUZIGUPJKECFZVJ34D3AYSGB1CS<EOS><PAD><PAD><PAD><PAD>
<SOS>66XNJSAJXFDDGB53BFOUZIGUPJKECFZVJ34D3AYSGB1CS<EOS><EOS><EOS><EOS>
1
<SOS>binptnbk2zjzchgmypphafbbo8scvrmdbnnlcn1<EOS><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD>
<SOS>88NCLNNBDMRVCS1OBBFAHPPYMGHCZJZ7KBNTPNIB<EOS><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD>
<SOS>88NCLNNBDMRVCS1OBBFAHPPYMGHCZJZ7KBNTPNIB<EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS>
2
<SOS>i7zjmlvl7jzxt8nvfjbfn9mn8mvakm<EOS><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD>
<SOS>MMKAVM1NM0NFBJFVN1TXZJ2LVLMJZ2I<EOS><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD>
<SOS>MMKAVM1NM0NFBJFVN1TXZJ2LVLMJZ2I<EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS>
3
<SOS>hxntj37xhz3cmhfbb7mjhkknabbfhpflmxbjzp6bitgyfh<EOS><PAD><PAD>
<SOS>HHFYGTIB3PZJBXMLFPHFBBANKKHJM2BBFHMC6ZHX26JTNXH<EOS><PAD><PAD>
<SOS>HHFGGTIB3PZJBXMLFPHFBBANKKHJM2BBFHMC6ZHX26JTNXH<EOS><EOS>
4
<SOS>bz59xzg8xznxvufflgrb7unvcvs84mbabbx6mclks<EOS><PAD><PAD><PAD><PAD><PAD><PAD><PAD>
<SOS>SSKLCM3XBBABM51SVCVNU2BRGLFFUVXNZX1GZX04ZB<EOS><PAD><PAD><PAD><PAD><PAD><PAD><PAD>
<SOS>SSKCMC3XBBABM51SVCVNU2BRGLFFUVXNZX1GZX04ZB<EOS><EOS><EOS><EOS><EOS><EOS><EOS>
5
<SOS>ufoxlvbacbdln9svhzl497j2nxachfmvxzd5gfn9h87blsc<EOS><PAD>
<SOS>CCSLB21H0NFG4DZXVMFHCAXN7J205LZHVS0NLDBCABVLXOFU<EOS><PAD>
<SOS>CCSLB21H0NFG4DZXVMFHCAXN7J205LZHVS0NLDBCABVLXOFU<EOS>
6
<SOS>m75z8poxut1nmihbvaavnm2f9cczzw8y48czpx8jmi<EOS><PAD><PAD><PAD><PAD><PAD><PAD>
<SOS>IIMJ1XPZC15Y1WZZCC0F7MNVAAVBHIMN8TUXOP1Z42M<EOS><PAD><PAD><PAD><PAD><PAD><PAD>
<SOS>IIMJ1XPZC15Y1WZZCC0F7MNVAAVBHIMN8TUXOP1Z42M<EOS><EOS><EOS><EOS><EOS><EOS>
7
<SOS>zfnnvf8cobjyzn9uxgnzcvv21mdcmcajmi<EOS><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD>
<SOS>IIMJACMCDM87VVCZNGXU0NZYJBOC1FVNNFZ<EOS><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD><PAD>
<SOS>IIMJACMCDM87VVCZNGXU0NZYJBOC1FVNNFZ<EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS><EOS>

Process finished with exit code 0

 《2》Transformer动画解释(Eve的科学频道)(个人认为讲的最好)

 

 

 

 

 

 

 

 

 

 

 在Transformer模型中,输入向量x通过Linear变换可以转换成三个不同的向量Q、K和V,是为了进行自注意力机制的操作。

转换为Q、K、V向量的操作是通过线性变换实现的。具体来说,通过两个不同的线性层,将输入向量x映射到三个不同的空间(Q空间、K空间、V空间)。这个线性变换允许模型将输入的不同方面信息进行区分和加权。Q、K和V向量分别在查询、键和值的角色下使用,以计算自注意力分数。

注意:KQV是需要训练的!!!

在Transformer模型中,Q(查询)、K(键)和V(值)是通过对输入向量进行线性变换得到的,而这些线性变换的参数是需要训练得到的

具体来说,Q、K和V分别通过对输入向量进行三个不同的线性变换得到。每个线性变换都涉及一个权重矩阵和一个偏置向量,它们都需要在模型训练过程中进行学习和更新

意味着Q、K和V在每个时间步骤都会根据输入数据的特征进行调整,以使得模型能够更好地捕捉输入序列中的语义和语境信息。

因此,Q、K和V是在Transformer模型中需要通过训练来获取的矩阵,其参数会根据任务和数据集进行优化和调整。

 

 

 

 

 

 

softmax的主要作用是将一个向量中的元素进行归一化,使得向量中的每个元素都表示为一个概率值

使得突出的特征更突出了

缩放和softmax之后得到的就是注意力权重矩阵了

 

 

 

 

 

 

 

这里的残差连接(应该就是Add/短接)可以简单理解为自注意力层的输出+原始的输入(为了解决梯度消失问题)

 

 

 

 

 

 

 

 

 

 解码器是自回归的

自回归(Autoregressive)和回归(Regression)是两种不同的建模方法,它们有以下区别:

1. 建模对象:回归通常用于预测连续型的目标变量,也可以用于建立输入特征与目标变量之间的关系。自回归则主要用于建模时间序列数据或序列数据中的自相关性,其中每个观测值被建模为过去观测值的函数。

2. 数据结构:回归问题通常涉及到多个特征(自变量)和一个目标变量(因变量),并且特征和目标变量之间的关系可以是线性的或非线性的。自回归问题涉及到一维或多维时间序列数据,其中当前观测值的取值可以通过过去观测值的函数来预测。

3. 建模方法:回归问题可以使用多种建模方法,包括线性回归、多项式回归、岭回归、Lasso回归等。自回归问题则常常使用基于统计模型的方法,如AR(Autoregressive)模型、ARMA(Autoregressive Moving Average)模型、ARIMA(Autoregressive Integrated Moving Average)模型等,或者基于深度学习的方法,如循环神经网络(RNN)和变种的自回归模型(如LSTM和GRU)。

4. 建模目标:回归模型的目标是预测连续型的目标变量的值,或者推断输入特征对目标变量的影响程度。自回归模型的目标是通过过去观测值预测当前观测值的值,或者生成符合数据分布的新样本。

总的来说,回归是一种预测目标变量取值的建模方法,可以用于连续型目标变量和多个特征的预测。自回归是一种建模序列数据的方法,主要用于时间序列数据或序列数据中观测值之间的自相关性建模。

 

 ​​​​​​​

 即因为解码器的自回归特性,所以才要引入mask来防止它对未来的标记进行预测

 

 

 之间,打错字了

 

 softmax函数会对向量中的每个元素进行指数运算,然后将得到的指数值进行归一化,使得所有元素的和等于1。

在进行softmax操作时,使用负无穷大的值对应的是被mask的位置,目的是将这些位置的概率值置为0。这是因为在softmax函数中,输入值越大,经过指数函数的变换后对应的概率值越接近于1。而负无穷大对应的指数值为0,所以softmax操作后对应的概率值也为0。

 

 

 

 

 

最后的linear是一个简单的全连接网络,将解码器输出投影成一个一维向量,该向量的维度很长,包含了所有可能出现的单词总和,
比如模型从训练集中学会10,000个英文单词,那么logits vector就是10000维的,每一维对应一个词,记过softmax计算后输出分数最高的那个词就是这一步的输出

 

 

 

 

 

  《3》Transformer动手学深度学习(李沐)(前面看懂了这个就不用看了)

 

 

 

 

 

  

 

 注意:批归一化不能处理RNN

 

 

更多经验补充:

1)批归一化:多用于图像领域;BN是取不同样本做归一化(按行)(同一特征/通道)

2)层归一化:多用于自然语言处理领域;LN是取不同通道做归一化(按列,即一个样本一个样本的处理)

eg:规范化之后,均值是0,标准差是1 

 

 

 《4》Transformer算法解读(唐宇迪)

 

 分词/分字后的每个小段都统一叫做一个token

 

 传统RNN的三个问题:只能串型计算;输入不可更新没有融入语境;只考虑上文不考虑下文

传统的循环神经网络 (RNN) 在计算时存在以下几个问题:

1. **梯度消失和梯度爆炸**:传统的 RNN 在反向传播算法中容易出现梯度消失或梯度爆炸的问题。这是由于 RNN 中反向传播的链式乘法中,梯度会随时间步指数级地增加或减少,导致网络的训练非常困难。

2. **长期依赖问题**:传统的 RNN 在处理长时间序列时,难以建立长期依赖关系。因为梯度传播会逐渐衰减,较早时间步的信息很难在后续时间步中传递,这导致 RNN 难以捕捉到远距离的依赖关系。

3. **计算效率低下**:传统的 RNN 是序列模型,每个时间步之间的计算是顺序执行的,无法进行并行计算。这使得 RNN 在处理长序列时的计算效率较低。

为了应对这些问题,研究人员提出了一些改进版本的 RNN,如长短期记忆网络(LSTM)、门控循环单元(GRU)等。这些改进模型引入了门控机制,可以更好地处理长期依赖问题,并且减少了梯度消失和爆炸的风险。LSTM 和 GRU 通过不同的门控单元来控制在网络中传递的信息量,从而提高了模型的计算效率和学习能力。

此外,近年来,针对序列建模的其他模型,如Transformer,也在自然语言处理等任务中取得了显著的成果。Transformer 使用自注意力机制来建模序列中的依赖关系,避免了传统 RNN 的问题,并取得了卓越的性能。

综上所述,传统 RNN 的主要问题是梯度消失和梯度爆炸,以及处理长期依赖和计算效率低下。改进的 RNN 模型,如 LSTM、GRU,以及其他序列模型,如 Transformer,被广泛应用于序列建模任务,取得了较好的结果。

 

 

 

 现在看不懂整体很正常,我们先看细节再看整体

 

 先假设这些权重我们能算出来

 

 以后会有办法降低复杂度的

 

自注意力机制(Self-Attention)是一种用于序列建模的机制,最早用于机器翻译任务中的Transformer模型中。它通过计算序列中各个元素之间的关系来建模序列的依赖关系。

自注意力机制的核心思想是,通过在序列中的每个位置上计算元素之间的相关性来获得每个元素的表示。对于序列中的每个元素,自注意力机制会根据该元素与序列中其他元素之间的相似度来为它分配一个权重,用于计算聚合后的表示。

具体来说,自注意力机制可以按以下步骤进行计算:

1. **计算相似度**(KQ点乘):对于一个输入序列,可以计算每对元素之间的相似度得分。这通常使用点积、加性或拼接等操作进行计算。

2. **计算权重**(短接+归一化):通过对相似度得分进行归一化,可以获得每个元素与其他元素之间的权重。这个权重可以决定每个元素对其他元素的关注程度。

3. **加权聚合**(得分矩阵(注意力权重) * v = z):通过将每个元素乘以其与其他元素的权重,可以获得每个元素的聚合表示。这使得每个元素能够考虑到序列中其他位置的信息。

自注意力机制的优势在于它能够捕捉到序列中不同位置之间的长距离依赖关系,而无需像传统的循环神经网络那样依赖于逐步的传递。自注意力机制还具有并行计算的优势,对于长序列的处理速度较快。

自注意力机制已经成功应用于自然语言处理任务中,如机器翻译、语言建模、文本分类等。此外,它还被用于计算机视觉任务中,如图像描述生成、视觉问答等。

权重是一个值,我们要权重就是要一个值,这里可以借助矩阵内积,因为没关系的两个向量会相互垂直,所以内积为0,而有关系则不会,而且关系越紧密内积会越大

在自注意力机制中,计算元素之间的相似度通常使用矩阵内积来计算。具体来说,它涉及到三个矩阵的乘积操作,分别是查询矩阵(Q)、键矩阵(K)、值矩阵(V)。

给定一个输入序列,它的表示可以通过自注意力机制进行计算。具体步骤如下:

1. **计算查询、键和值的矩阵**:将输入序列通过线性变换分别得到查询矩阵 Q、键矩阵 K 和值矩阵 V。这些变换通常是通过学习到的权重矩阵来实现的。

2. **计算相似度得分**:通过计算查询矩阵 Q 和键矩阵 K 之间的矩阵内积,可以得到一个相似度矩阵,用于表示每对元素之间的相似程度。

3. **计算注意力权重**:通过对相似度矩阵进行归一化,可以得到注意力权重矩阵,用于表示每个元素对其他元素的关注程度。

4. **加权聚合**:通过将注意力权重矩阵与值矩阵 V 进行矩阵乘法,可以得到加权聚合后的表示。这个聚合表示会考虑每个元素对其他元素的权重。

总结起来,矩阵内积在自注意力机制中用于计算相似度得分,进而得到每个元素的注意力权重。这个权重用于加权聚合输入序列的值矩阵,从而获得每个元素的最终表示。

需要注意的是,尽管矩阵内积是常见的计算相似度的方法,但在实践中,也可以使用其他方法,如加性注意力(将查询和键进行相加)或拼接注意力(将查询和键进行拼接)。

如果你还有其他问题,请随时向我提问。

现在问题来了,我们手里只有输入的x1和x2,那么QKV三个(辅助)向量是哪来的呢

 在自注意力机制中,查询矩阵(Q)、键矩阵(K)和值矩阵(V)是通过学习得到的。它们分别用于计算元素之间的相似度、注意力权重和加权聚合的表示。 具体来说,在使用自注意力机制进行序列建模时,可以通过以下方式来获取这三个矩阵: 1. **线性变换**:首先,将输入序列的每个元素通过线性变换映射到一个特定的维度空间。这个映射通常是通过学习到的权重矩阵来实现的。对于查询矩阵 Q、键矩阵 K 和值矩阵 V,可以使用不同的权重矩阵来进行不同的映射。 2. **分割**:在一些特定的任务中,可能需要使用多个自注意力机制来处理不同的注意力方面。在这种情况下,可以将线性变换后的输入序列划分为多个子序列,然后为每个子序列学习不同的查询矩阵 Q、键矩阵 K 和值矩阵 V。 总之,查询矩阵(Q)、键矩阵(K)和值矩阵(V)是通过线性变换得到的,其中线性变换的权重矩阵可以通过训练神经网络模型来学习得到。每个矩阵的维度可以根据具体任务和模型的需求进行设计。

 

 

 归一化

 

在自注意力机制中,softmax函数用于计算注意力权重。softmax函数可以将一组数值转化为概率分布,使得每个数值都在0到1之间,并且它们的和等于1。 具体来说,在自注意力机制中,首先通过计算相似度得分矩阵,得到了每对元素之间的相似度得分。然后,通过应用softmax函数,对相似度得分进行归一化,以获得注意力权重。

需要注意的是,softmax函数的使用可以使得较大的得分更加突出,而较小的得分则更接近于0。因此,自注意力机制中的注意力权重会对相似度得分较高的元素给予更多的关注。(使突出的更加突出!!!)

 

 

 

 

多头注意力(Multi-Head Attention)是一种在自注意力机制中引入并行计算的方法,旨在增加模型的表达能力和学习能力。

在传统的自注意力机制中,通过计算查询矩阵 Q 和键矩阵 K 之间的矩阵内积来计算相似度得分。然后,应用softmax函数对相似度得分进行归一化,得到注意力权重。最后,将注意力权重与值矩阵 V 进行加权聚合,得到最终的表示。

而在多头注意力中,可以同时使用多个注意头(attention head)进行上述计算。每个注意头具有独立的查询矩阵 Q、键矩阵 K 和值矩阵 V,以及对应的注意力权重和加权聚合结果。

具体来说,多头注意力的计算过程如下:

1. 将输入序列通过不同的线性变换映射到多组查询矩阵 Q、键矩阵 K 和值矩阵 V。这些映射使用不同的权重矩阵来实现,每组映射对应一个注意头。

2. 对于每个注意头,计算相似度得分矩阵,并通过softmax函数得到注意力权重。

3. 对于每个注意头,将注意力权重与值矩阵 V 进行加权聚合,得到加权聚合结果。

4. 将多个注意头的加权聚合结果进行拼接或加权求和,得到最终的表示。

通过使用多个注意头,模型可以同时关注不同的特征子空间和关系,从而捕捉到更多的信息。这有助于提升模型在复杂任务上的性能。

需要注意的是,多头注意力是通过在模型中引入并行计算来扩展模型的能力。多头注意力在大型模型如Transformer中得到了广泛应用,可以显著提升模型在自然语言处理和其他序列建模任务上的性能。

Transformer的本质就是个for循环

做多层的目的是为了更好地提特征

Transformer是一种流行的神经网络模型,在自然语言处理领域取得了很大的成功。虽然Transformer的架构比较复杂,但其本质上是一个多头自注意力机制的堆叠。

Transformer通过多个编码器和解码器层组成,每个层都包含了一个自注意力机制和一个前馈神经网络。在使用Transformer进行自然语言处理时,我们需要先将输入序列经过多个编码器层的处理,以提取出输入序列的语义信息,再将编码器的输出输入到解码器中生成对应的输出序列。

在这个过程中,可以将每个编码器和解码器层看作是一个for循环,每个循环中都会对输入的数据进行处理并输出结果。因此,整个Transformer模型可以看作是一个多层嵌套的for循环。

我们可以把每个编码器层或解码器层看做一个时间步,然后将所有的时间步依次串联起来,就可以得到整个Transformer模型的过程。其中,每个时间步都会对前一个时间步的输出进行处理,并输出新的结果。

因此,我们可以说,Transformer的本质就是一个for循环,只是这个for循环是在多个编码器层和解码器层中嵌套的,每个循环中都会经过多头自注意力机制和前馈神经网络来对数据进行处理。这种自注意力机制和前馈神经网络的处理方式,使得Transformer在自然语言处理中取得了很好的效果。

 

位置编码(Positional Encoding)是用于为序列中的每个位置添加位置信息的一种技术,通常用于自注意力机制中的Transformer模型。 在自注意力机制中,输入序列的顺序是无关的,因此模型无法直接获取和利用位置信息。为了保留序列中的位置信息,位置编码被添加到输入序列的表示中,以便帮助模型理解输入序列中元素的相对位置。 Transformer中常用的位置编码方法是使用正弦和余弦函数来对位置进行编码。具体来说,通过以下公式计算位置编码: $$ PE_{(pos, 2i)} = \sin(pos / 10000^{2i/d_{\text{model}}}) $$ $$ PE_{(pos, 2i+1)} = \cos(pos / 10000^{2i/d_{\text{model}}}) $$ 其中,$pos$ 表示序列中的位置,$i$ 表示位置编码的维度索引,$d_{\text{model}}$ 表示模型的表示维度。通过对不同位置和不同维度的位置编码进行计算,可以为序列中的每个位置生成一个唯一的位置向量。 位置编码的长度与输入序列的长度相同,可以直接与输入序列的表示进行相加,以获得包含位置信息的输入表示。 位置编码的作用是在不同维度中编码位置信息的变化,使得模型能够理解输入序列中元素的相对位置。通过将位置编码添加到输入序列的表示中,模型可以更好地处理序列中的顺序关系和距离关系。

当你在处理一句话时,位置编码可以帮助模型理解单词在句子中的顺序关系。

例如,我们有一个简单的句子:“I love natural language processing.”。如果我们不使用位置编码,模型只能根据单词的内容进行处理,无法区分它们在句子中的顺序。

但是,通过使用位置编码,我们可以为每个单词添加一个表示其位置的向量。假设我们使用一个表示维度为4的位置编码,那么对于句子中的每个单词,我们可以添加以下位置编码:

```
Word     Position Encoding
I        [0.0000, 1.0000, 0.0000, 1.0000]
love     [0.8415, 0.5403, 0.9093, -0.4161]
natural  [0.9093, -0.4161, -0.8415, 0.5403]
language [-0.7568, -0.6536, -0.9589, -0.2837]
processing [-0.9589, -0.2837, 0.7568, 0.6536]
```

通过将位置编码与单词的表示相加,模型能够区分相同的单词在句子中的不同位置。例如,位置编码中的第二个维度的值会在单词“love”和“natural”之间变化,这样模型可以了解到这两个单词在句子中的相对位置关系。

通过位置编码,模型可以更好地理解句子的上下文和语法结构。这对于自然语言处理任务,如机器翻译或文本分类,非常重要。

 

在Transformer中,"Add" 是指矩阵相加的操作。Transformer模型的核心是自注意力机制和残差连接(residual connection),其中"Add"操作用于将注意力机制的输出与模型的输入进行相加。

在具体实现中,假设我们有一个残差块(residual block),它的输入是x。在残差块中,首先对输入x进行一系列操作,比如多头自注意力(multi-head self-attention)和前馈神经网络(feed-forward neural network)。然后,将这些操作的输出与输入x进行相加,得到残差块的输出:

output = x + sublayer(output)

这里,sublayer(output)表示残差块中的操作的输出。通过将操作的输出与输入进行相加,可以使信息在模型的不同层之间更好地流动,加快训练速度并提高模型性能。

总之,"Add"操作在Transformer中用于将注意力机制的输出与模型的输入进行相加,实现了残差连接。这有助于提高模型的表示能力和训练效率。

 解码比编码更难!

在序列到序列(sequence-to-sequence)模型中,解码器(Decoder)是一个重要的组件,负责将编码器生成的上下文向量和先前生成的目标序列作为输入,逐步生成输出序列。

解码器的目标是根据上下文信息和已生成的部分序列,逐步生成下一个输出。通常,解码器采用递归的方式,一个时间步一个时间步地生成输出。(逐步生成很重要,知道GPT为啥不是直接出结果了吧)

常见的解码器结构是基于循环神经网络(Recurrent Neural Network,RNN)的模型,如基本循环神经网络(Simple RNN)或长短时记忆网络(Long Short-Term Memory,LSTM)。在解码器中,RNN单元接收上一个时间步的隐藏状态和当前的输入,然后产生当前时间步的输出和新的隐藏状态。

在每个时间步,解码器根据上一个时间步生成的输出和隐藏状态,选择生成下一个输出。可以选择不同的策略来进行选择,如使用贪婪策略选择概率最大的输出,或者使用束搜索(Beam Search)来搜索概率最高的输出序列。

除了基于RNN的解码器,也有一些其他的解码器结构,如基于自注意力机制的Transformer解码器。这种解码器通过多头注意力机制和位置编码,能够同时关注输入序列的不同位置和特征子空间,从而更好地捕捉输入和输出之间的关系。

解码器在机器翻译、文本生成、语言模型等任务中起着重要的作用。它能够根据上下文信息生成连续的输出序列,实现对输入序列的理解和转换。

 注意力机制(Attention Mechanism)是一种用于从一组输入中选择相关部分的机制,它通常应用于序列到序列(sequence-to-sequence)任务中。自注意力机制(Self-Attention Mechanism),也称为多头注意力(Multi-Head Attention),是注意力机制的一种特殊形式。 在序列到序列任务中,特别是在Transformer模型中,注意力机制用于解码器(Decoder)中的自注意力层(Self-Attention Layer)和编码器-解码器注意力层(Encoder-Decoder Attention Layer)。 自注意力机制与传统的注意力机制的区别在于,它不仅考虑输入序列中的各个元素之间的关系,还考虑了元素自身与其他元素的关系。更具体地说,自注意力机制基于输入序列的各个元素计算出一个针对该元素的权重向量,用于加权求和生成表示。 自注意力机制的计算过程包括以下几个步骤: 1. 通过三个矩阵变换(Wq、Wk、Wv),将输入序列映射到查询(Query)、键(Key)和值(Value)空间。 2. 对查询(Q)、键(K)和值(V)进行相似度计算,得到注意力权重。 3. 使用注意力权重对值(V)进行加权求和,得到自注意力表示。 自注意力机制的关键是计算注意力权重,它是通过计算查询(Q)和键(K)之间的相似度来得到的。在计算相似度时,常用的方法是使用点积(Dot Product)或将查询(Q)和键(K)分别映射到一个共享的低维空间,然后计算它们的内积。然后,通过对相似度进行归一化,得到注意力权重。 自注意力机制的一个重要特性是它能够在一个模型中同时关注输入序列的不同位置和特征子空间,从而更好地捕捉输入之间的关联关系。 总结起来,自注意力机制是注意力机制的一种特殊形式,它不仅考虑输入序列的各个元素之间的关系,还考虑了元素自身与其他元素的关系。Transformer模型中的自注意力机制在解码器的自注意力层和编码器-解码器注意力层中起着重要作用,帮助模型捕捉输入之间的关联关系。

 

 我感觉老师讲的mask的意思就是只关注前面的不考虑后面的

很多时候都是有这个mask机制的

遮蔽机制(Masking)是在序列到序列(sequence-to-sequence)任务中常用的一种技术,特别是在Transformer模型中。

在序列到序列任务中,输入序列和输出序列的长度可以不同。当训练模型时,为了避免模型通过简单地复制输入序列的部分来生成输出序列,需要引入遮蔽机制。

遮蔽机制的主要目的是在解码器(Decoder)中屏蔽(或遮蔽)与当前时间步无关的输入信息。这样,解码器只能看到当前时间步之前的输入,从而强制模型按顺序逐步生成输出。

在Transformer解码器中,有两种常见的遮蔽机制:

1. **掩盖未来信息(Masking Future Information)**:在解码器的自注意力层中,为了避免当前时间步的位置关注到未来的位置,会对当前时间步之后的位置进行遮蔽。这样,解码器在生成当前时间步的输出时,只能依赖于当前时间步之前的输入。

2. **掩盖填充信息(Masking Padding Information)**:在序列中,填充(padding)通常用于将不同长度的序列对齐到相同的长度。为了避免填充的位置对模型的输出产生影响,可以使用掩盖填充信息的方法。具体做法是,在输入序列中的填充位置设置一个特殊的掩盖值,使解码器在生成输出时忽略这些填充位置的信息。

这两种遮蔽机制通常会在解码器的自注意力层中联合使用,以确保解码器只关注当前时间步之前的有效输入,并忽略未来信息和填充信息。

遮蔽机制在训练过程中起着重要的作用,特别是在生成任务中,如机器翻译和文本生成。通过使用遮蔽机制,可以帮助模型正确地学习生成输出序列的过程,避免产生不合理的输出。

希望这解释了遮蔽机制的概念。如果还有其他问题,请随时提问。

补充一下Transformer的机器翻译!

Transformer是一种基于自注意力机制的神经网络模型,它在机器翻译任务中取得了显著的成功。

机器翻译是一项将一种语言的文本(称为源语言)翻译成另一种语言的文本(称为目标语言)的任务。传统的机器翻译方法主要基于统计机器翻译(Statistical Machine Translation,SMT),使用词汇和短语的统计模型来进行翻译。然而,这些方法通常需要大量的人工特征工程和繁复的模型调整。

Transformer模型的出现极大地改变了机器翻译领域。它利用自注意力机制(Self-Attention)来捕捉源语言和目标语言之间的关联关系,而无需像传统方法那样依赖于固定长度的短语或词汇。这使得Transformer模型能够更好地处理长距离依赖性,并且具有更好的并行化能力。

Transformer模型由编码器(Encoder)和解码器(Decoder)组成。编码器将源语言的文本作为输入,并学习源语言的表示。解码器以编码器的输出作为上下文向量,并逐步生成目标语言的文本。在训练过程中,模型通过最大似然估计(Maximum Likelihood Estimation,MLE)来优化其参数,使得模型能够生成最接近目标翻译的输出。

Transformer模型在机器翻译任务中表现出色,取得了与以往方法相媲美甚至超越的结果。其优势包括:

1. **处理长距离依赖性**:Transformer模型通过自注意力机制能够捕捉源语言和目标语言之间的长距离依赖性,避免了传统方法中的局限性。

2. **并行化能力强**:由于自注意力机制的并行计算特性,Transformer模型能够高效地利用计算资源,加快训练和推断的速度。

3. **更好的表示学习**:通过多层的自注意力和前馈神经网络,Transformer模型能够学习更丰富、更抽象的表示,从而提升翻译的质量。

总而言之,Transformer模型在机器翻译任务中以其创新的架构和强大的表示学习能力取得了显著的成果。它已成为当今机器翻译领域的主流方法,并被广泛应用于许多实际场景。

希望这解释了Transformer模型在机器翻译中的应用。如果还有其他问题,请随时提问。

 关于BERT和Transformer的关系以后再说

补充一下Transformer在CV的应用!!!

 

 

 

VIT指的是视觉Transformer

VIT代表Vision Transformer,是一种基于Transformer模型的视觉领域的模型架构。它通过将图像分割成一组图像补丁(image patches)并将它们视为序列,然后将序列输入Transformer模型,以实现图像分类和其他视觉任务。

VIT的主要思想是将Transformer模型应用于图像数据,类比于自然语言处理中将Transformer模型应用于序列数据。它的关键步骤如下:

1. **图像补丁(Image Patches)**:将输入图像分成固定大小的图像补丁。这些补丁通常是重叠的,以保留图像中的空间信息。

2. **图像补丁的嵌入(Patch Embedding)**:将每个图像补丁转换为低维特征向量,通常使用一个线性层或卷积层。

3. **位置编码(Positional Encoding)**:为了在Transformer中引入图像的位置信息,需要对图像补丁的嵌入进行位置编码,以表示它们在图像中的相对位置。

4. **Transformer编码器(Transformer Encoder)**:将位置编码后的图像补丁嵌入作为输入,经过一系列的Transformer编码器层来处理和提取特征。每个编码器层通常由多头自注意力机制(Multi-Head Self-Attention)和前馈神经网络(Feed-Forward Neural Network)组成。

5. **分类器(Classifier)**:使用图像补丁的嵌入输出或Transformer编码器的最后一个时间步的表示作为输入,传入一个全连接层进行图像分类或其他任务的预测。

VIT模型的优点在于,它利用了Transformer模型在自然语言处理中取得的成功,并将其应用于视觉领域。相比于传统的基于卷积神经网络(CNN)的方法,VIT模型能够更好地捕捉全局上下文信息,对大尺寸图像和长程依赖关系有更好的处理能力。

 

 

 

 

 

 ​​​​​​​

 

 Informer对于时序的项目很重要!!!一定要学!!!

 注意:CV,NLP,时间序列这些都是有很大相关性的,学一些别的不会吃亏的

以后到公司你会的多比你一个点会的精要更吃香

 

 

在计算机视觉中,图像的"patch"指的是对图像进行分割得到的小块图像区域。它可以被看作是图像的局部部分或者子区域。

Patch的大小可以根据具体任务的需求而定,一般情况下它是一个固定的正方形或矩形。在处理图像时,常常使用固定大小的patch来对图像进行切分,以便于对每个小块图像区域进行单独的处理和分析。

在一些图像处理或计算机视觉任务中,例如图像分类或目标检测,将图像分割成小的patch有助于提取局部特征和捕捉图像中的细节信息。通过对每个patch进行独立的处理,可以减少计算量,并使模型更加关注局部区域的特征。

在Vision Transformer模型(VIT)中,图像被分成一组小的图像补丁(image patches),并将它们视为模型的输入序列。这样的处理方式使得VIT能够将Transformer模型应用于图像数据,将图像信息转化为序列数据进行处理。

总之,patch是图像分割得到的小块图像区域,用于局部处理和分析。希望能解答您的疑问,如果还有其他问题,请随时提问。

 

 

 

 

 

 《5》Transformer经典论文(Attention is all your need)讲解

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 ​​​​​​​

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

杂例补充1:Transformer架构原理(Rebnerd逆律极客)(一个科普视频)

1)KQV怎么理解:你可以理解为数据库表里已知一组Key,通过查询语句Q进行查询得到一组Value

2)编码器的作用可以理解为可以将自然语言编码记录成句子信息的某种内部表示形态(hidden representation)(即句子中的每个单词应该尤其注意句中的哪些其他单词)

3)self-attention简单来讲就是他不依赖额外输入的信息,即他只统计句中的每个单词和其他单词之间的注意力

4)没有位置编码,相当于你扔给模型的只是一大袋词汇,没有先后顺序,但是实际上我们是需要先后顺序这个信息的

5)q与k中的每一个单词做向量点乘(因为数学中我们常用余弦值来表示两个向量的关系,而余弦值与点乘成正比)

 

 

 

 

 

 

 矩阵的每一行都是一个value,代表当前这个词语在这个句子中Attention的分部

6)此时的模型还没有参数(w,b),要给这三个矩阵加三个全连接层

7) 这就需要多头注意力机制了(注意多头注意力机制是并行的(可以利用GPU的优势))

 8)如果说encoder的作用是用来分析和理解句子,那么decoder的作用就是用来生成一个与当前句子关联的新句子

 

杂例补充2:什么是图注意力网络

参考我的文章:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值