李宏毅自注意力机制学习笔记:从理论到实践的新手入门指南

1次阅读
没有评论

共计 2878 个字符,预计需要花费 8 分钟才能阅读完成。

image.webp

背景介绍:Transformer 架构中的自注意力

自注意力机制是 Transformer 模型的核心组件,它允许模型在处理序列数据时动态地关注不同位置的信息。与传统 RNN 和 CNN 不同,自注意力机制能够直接建模任意两个位置之间的关系,无论它们之间的距离有多远。这使得 Transformer 在机器翻译、文本生成等任务中表现出色。

李宏毅自注意力机制学习笔记:从理论到实践的新手入门指南

数学原理逐步推导

1. 基本概念

自注意力机制的核心是 Query、Key 和 Value 三个矩阵。给定一个输入序列 $X \in \mathbb{R}^{n \times d}$,其中 $n$ 是序列长度,$d$ 是特征维度,我们首先通过线性变换得到这三个矩阵:

$$
Q = XW^Q, \quad K = XW^K, \quad V = XW^V
$$

其中 $W^Q, W^K, W^V \in \mathbb{R}^{d \times d_k}$ 是可学习的参数矩阵。

2. 注意力分数计算

接下来计算注意力分数,表示一个位置对另一个位置的关注程度:

$$
\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
$$

这里除以 $\sqrt{d_k}$ 是为了防止点积结果过大导致 softmax 梯度消失的问题。

PyTorch 代码实现

1. 输入嵌入和位置编码

import torch
import torch.nn as nn
import math

class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=5000):
        super().__init__()
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        self.register_buffer('pe', pe)

    def forward(self, x):
        return x + self.pe[:x.size(1)]

2. 自注意力层实现

class SelfAttention(nn.Module):
    def __init__(self, embed_size, heads):
        super().__init__()
        self.embed_size = embed_size
        self.heads = heads
        self.head_dim = embed_size // heads

        assert (self.head_dim * heads == embed_size), "Embedding size needs to be divisible by heads"

        self.values = nn.Linear(self.head_dim, self.head_dim, bias=False)
        self.keys = nn.Linear(self.head_dim, self.head_dim, bias=False)
        self.queries = nn.Linear(self.head_dim, self.head_dim, bias=False)
        self.fc_out = nn.Linear(heads * self.head_dim, embed_size)

    def forward(self, values, keys, query, mask):
        N = query.shape[0]
        value_len, key_len, query_len = values.shape[1], keys.shape[1], query.shape[1]

        # Split the embedding into self.heads different pieces
        values = values.reshape(N, value_len, self.heads, self.head_dim)
        keys = keys.reshape(N, key_len, self.heads, self.head_dim)
        queries = query.reshape(N, query_len, self.heads, self.head_dim)

        values = self.values(values)
        keys = self.keys(keys)
        queries = self.queries(queries)

        # Scaled dot-product attention
        energy = torch.einsum("nqhd,nkhd->nhqk", [queries, keys])

        if mask is not None:
            energy = energy.masked_fill(mask == 0, float("-1e20"))

        attention = torch.softmax(energy / (self.embed_size ** (1/2)), dim=3)

        out = torch.einsum("nhql,nlhd->nqhd", [attention, values]).reshape(N, query_len, self.heads * self.head_dim)

        out = self.fc_out(out)
        return out

实际应用案例分析

1. 处理变长序列的技巧

  • 使用 padding mask:对于不同长度的序列,用特殊 token 填充到相同长度,并通过 mask 机制忽略这些位置
  • 相对位置编码:在处理极长序列时,传统的绝对位置编码可能失效,可以考虑使用相对位置编码

2. 计算复杂度优化

  • 稀疏注意力:只计算部分位置对的注意力分数
  • 局部注意力:限制每个位置只能关注周围一定窗口内的位置
  • 分块计算:将长序列分成多个块分别计算注意力

性能优化与调试技巧

  1. 梯度检查:自注意力机制中有大量矩阵运算,容易出现梯度爆炸或消失问题

  2. 初始化策略:使用 Xavier 或 Kaiming 初始化注意力层的参数

  3. 学习率调整:自注意力层通常需要较小的学习率

  4. 可视化调试:使用热力图检查注意力权重分布是否合理

延伸学习资源推荐

  1. 原始论文:《Attention Is All You Need》
  2. 李宏毅 2022 年深度学习课程视频
  3. Hugging Face Transformer 库源码
  4. 《The Illustrated Transformer》博客文章

动手实践建议

  1. 在简单的文本分类任务上实现自注意力机制
  2. 使用 TensorBoard 可视化不同层的注意力模式
  3. 尝试修改注意力头数,观察模型性能变化
  4. 比较自注意力与 CNN、RNN 在相同任务上的表现

通过这篇学习笔记,我们系统地学习了自注意力机制的数学原理、PyTorch 实现以及实际应用中的各种技巧。自注意力机制作为 Transformer 的核心组件,理解其工作原理对于深入学习现代 NLP 模型至关重要。建议读者在实际项目中尝试实现这些概念,以加深理解。

正文完
 0
评论(没有评论)