transformer 学习

 原理学习:

(3条消息) The Illustrated Transformer【译】_于建民的博客-CSDN博客

代码学习:

https://github.com/jadore801120/attention-is-all-you-need-pytorch/tree/master/transformer

 mask学习:

(3条消息) NLP 中的Mask全解_mask在自然语言处理代表什么_郝伟博士的博客-CSDN博客

多头注意力机制学习:

【1】代码:

class MultiHeadAttention(nn.Module):
    ''' Multi-Head Attention module '''

    def __init__(self, n_head, d_model, d_k, d_v, dropout=0.1):
        super().__init__()

        self.n_head = n_head
        self.d_k = d_k
        self.d_v = d_v

        self.w_qs = nn.Linear(d_model, n_head * d_k, bias=False)
        self.w_ks = nn.Linear(d_model, n_head * d_k, bias=False)
        self.w_vs = nn.Linear(d_model, n_head * d_v, bias=False)
        self.fc = nn.Linear(n_head * d_v, d_model, bias=False)

        self.attention = ScaledDotProductAttention(temperature=d_k ** 0.5)

        self.dropout = nn.Dropout(dropout)
        self.layer_norm = nn.LayerNorm(d_model, eps=1e-6)


    def forward(self, q, k, v, mask=None):

        d_k, d_v, n_head = self.d_k, self.d_v, self.n_head
        sz_b, len_q, len_k, len_v = q.size(0), q.size(1), k.size(1), v.size(1)

        residual = q

        # Pass through the pre-attention projection: b x lq x (n*dv)
        # Separate different heads: b x lq x n x dv
        q = self.w_qs(q).view(sz_b, len_q, n_head, d_k)
        k = self.w_ks(k).view(sz_b, len_k, n_head, d_k)
        v = self.w_vs(v).view(sz_b, len_v, n_head, d_v)

        # Transpose for attention dot product: b x n x lq x dv
        q, k, v = q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2)

        if mask is not None:
            mask = mask.unsqueeze(1)   # For head axis broadcasting.

        q, attn = self.attention(q, k, v, mask=mask)

        # Transpose to move the head dimension back: b x lq x n x dv
        # Combine the last two dimensions to concatenate all the heads together: b x lq x (n*dv)
        q = q.transpose(1, 2).contiguous().view(sz_b, len_q, -1)
        q = self.dropout(self.fc(q))
        q += residual

        q = self.layer_norm(q)

        return q, attn

 【2】解读代码:

定义了一个名为 MultiHeadAttention 的类,继承自 nn.Module。该类实现了多头注意力模块。

在初始化方法 __init__ 中,接受了一些参数:n_head(注意力头数)、d_model(模型维度)、d_k(查询和键的维度)、d_v(值的维度)和 dropout(dropout 比率)。

首先,这些参数被存储在对应的类属性中。

然后,定义了四个线性变换层 w_qsw_ksw_vsfc。这些变换层将输入数据投影到不同的维度上,以用于多头注意力的计算。

接下来,创建了一个 ScaledDotProductAttention 类的实例 self.attention。这个类尚未提供代码,但根据命名可以猜测是实现了缩放点积注意力机制。

然后,创建了一个 nn.Dropout 类的实例 self.dropout,用于进行 dropout 操作。

最后,创建了一个 nn.LayerNorm 类的实例 self.layer_norm,用于进行层归一化操作。

在前向传播方法 forward 中,首先从类属性中获取各个维度的大小,并保存在局部变量中。

然后,将输入 q 复制给 residual,以便后面进行残差连接操作。

接下来,将输入 qkv 分别通过线性变换层 w_qsw_ksw_vs 进行线性变换,并调整维度以适应多头注意力的计算。

通过调用线性变换层的方法,将输入数据分别投影到多个注意力头的维度上,得到的结果维度为 (sz_b, len_q, n_head, d_k)(sz_b, len_k, n_head, d_k)(sz_b, len_v, n_head, d_v)

对于进行注意力计算的维度调整,需要将 qkv 的维度进行转置,以便后续进行点积操作。

如果提供了掩码 mask,则将其增加一个维度使其与注意力张量的维度相匹配。

调用 self.attention 的前向传播函数,对输入的 qkv 进行多头注意力计算。

得到计算后的注意力张量 q 和注意力权重 attn

根据多头注意力计算的结果,对 q 进行维度转置和重塑,然后通过线性变换层 fc 进行线性变换,并进行 dropout 操作。

接下来,将残差连接操作的结果与处理后的 q 相加,得到多头注意力模块的输出。

最后,对输出进行层归一化操作,并返回输出结果和注意力权重。

希望以上解释能够帮助到您理解这段代码的工作原理。如果还有其他问题,请随时提问。

 【3】代码:

class Transformer(nn.Module):
    def __init__(self, feature_dim, num_heads, num_layers,drop_p):
        super(Transformer, self).__init__()
        self.num_layers = num_layers
        self.self_attention_layers = nn.ModuleList([
            SelfAttention(feature_dim, num_heads)
            for _ in range(num_layers)
        ])
        self.feed_forward_layers = nn.ModuleList([
            nn.Sequential(
                nn.Dropout(p=drop_p),
                nn.Linear(feature_dim, feature_dim))
            for _ in range(num_layers)
        ])
        self.norm=nn.LayerNorm(feature_dim)
    def forward(self, x):
        for layer_idx in range(self.num_layers):
            attention_output = self.self_attention_layers[layer_idx](x)
            x = self.norm(x + attention_output)
            feed_forward_output = self.feed_forward_layers[layer_idx](x)
            x = self.norm(x + feed_forward_output)
        return x

class SelfAttention(nn.Module):
    def __init__(self, feature_dim, num_heads):
        super(SelfAttention, self).__init__()
        self.num_heads = num_heads
        self.feature_dim=feature_dim
        self.query = nn.Linear(feature_dim, feature_dim)
        self.key = nn.Linear(feature_dim, feature_dim)
        self.value = nn.Linear(feature_dim, feature_dim)
        self.concat_projection = nn.Linear(feature_dim, feature_dim)
    def forward(self, x):
        q = self.query(x)
        k = self.key(x)
        v = self.value(x)
        query = self.split_heads(q)
        key = self.split_heads(k)
        value = self.split_heads(v)
        scores = torch.matmul(query, key.transpose(-1, -2))
        scaled_scores = scores / torch.sqrt(torch.tensor(self.feature_dim / self.num_heads))
        attention_weights = torch.softmax(scaled_scores, dim=-1)
        attention_output = torch.matmul(attention_weights, value)
        attention_output = self.concat_heads(attention_output)
        return attention_output
    def split_heads(self, x):
        batch_size, seq_len, _ = x.size()
        x_ = x.view(batch_size, seq_len, self.num_heads, self.feature_dim // self.num_heads)
        x_ = x_.permute(0, 2, 1, 3)
        return x_
    def concat_heads(self, x):
        batch_size, _, seq_len, _ = x.size()
        x = x.permute(0, 2, 1, 3).contiguous()
        x = x.view(batch_size, seq_len, self.feature_dim)
        x = self.concat_projection(x)
        return x

【4】与【1】代码的区别:

这两种方法都是为了在模型中使用多头注意力机制,处理输入数据并计算注意力权重。它们的目标都是将输入数据分割为多个部分,每个部分对应一个注意力头,然后对每个部分进行投影转换。

不同之处在于具体的实现方式:

  • 第一种方法中,每个注意力头有独立的权重参数,也就是说,每个注意力头都有自己的“专属”线性变换层。这样做的好处是可以对每个注意力头进行单独的处理,使其更具灵活性,以适应不同的任务需求。

  • 第二种方法中,所有的注意力头共享相同的权重参数,使用统一的线性变换层来处理输入数据。这样做的好处是减少了模型的参数量,提高了计算效率,但也可能限制了每个注意力头的个性化表达能力。

更通俗来讲区别就是:

第一种 多头,每一头的数据不一样,第二这种 多头 每一头的数据都一样 

第一种: 数据x 分为多头就是 heads1--->x  heads2--->x

第二种:数据x 分为多头就是 heads1---->x/num_heads



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值