【深度学习实验】注意力机制(四):点积注意力与缩放点积注意力之比较

2024-07-30 09:14:44 浏览数 (2)

一、实验介绍

注意力机制作为一种模拟人脑信息处理的关键工具,在深度学习领域中得到了广泛应用。本系列实验旨在通过理论分析和代码演示,深入了解注意力机制的原理、类型及其在模型中的实际应用。

本文将介绍将介绍带有掩码的 softmax 操作

二、实验环境

  本系列实验使用了PyTorch深度学习框架,相关操作如下:

1. 配置虚拟环境

代码语言:javascript复制
conda create -n DL python=3.7 
代码语言:javascript复制
conda activate DL
代码语言:javascript复制
pip install torch==1.8.1 cu102 torchvision==0.9.1 cu102 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html
代码语言:javascript复制
conda install matplotlib
代码语言:javascript复制
 conda install scikit-learn

2. 库版本介绍

软件包

本实验版本

目前最新版

matplotlib

3.5.3

3.8.0

numpy

1.21.6

1.26.0

python

3.7.16

scikit-learn

0.22.1

1.3.0

torch

1.8.1 cu102

2.0.1

torchaudio

0.8.1

2.0.2

torchvision

0.9.1 cu102

0.15.2

三、实验内容

0. 理论介绍

a. 认知神经学中的注意力

  人脑每个时刻接收的外界输入信息非常多,包括来源于视 觉、听觉、触觉的各种各样的信息。单就视觉来说,眼睛每秒钟都会发送千万比特的信息给视觉神经系统。人脑通过注意力来解决信息超载问题,注意力分为两种主要类型:

  • 聚焦式注意力(Focus Attention):
    • 这是一种自上而下的有意识的注意力,通常与任务相关。
    • 在这种情况下,个体有目的地选择关注某些信息,而忽略其他信息。
    • 在深度学习中,注意力机制可以使模型有选择地聚焦于输入的特定部分,以便更有效地进行任务,例如机器翻译、文本摘要等。
  • 基于显著性的注意力(Saliency-Based Attention)
    • 这是一种自下而上的无意识的注意力,通常由外界刺激驱动而不需要主动干预。
    • 在这种情况下,注意力被自动吸引到与周围环境不同的刺激信息上。
    • 在深度学习中,这种注意力机制可以用于识别图像中的显著物体或文本中的重要关键词。

  在深度学习领域,注意力机制已被广泛应用,尤其是在自然语言处理任务中,如机器翻译、文本摘要、问答系统等。通过引入注意力机制,模型可以更灵活地处理不同位置的信息,提高对长序列的处理能力,并在处理输入时动态调整关注的重点。

b. 注意力机制
  1. 注意力机制(Attention Mechanism):
    • 作为资源分配方案,注意力机制允许有限的计算资源集中处理更重要的信息,以应对信息超载的问题。
    • 在神经网络中,它可以被看作一种机制,通过选择性地聚焦于输入中的某些部分,提高了神经网络的效率。
  2. 基于显著性的注意力机制的近似: 在神经网络模型中,最大汇聚(Max Pooling)和门控(Gating)机制可以被近似地看作是自下而上的基于显著性的注意力机制,这些机制允许网络自动关注输入中与周围环境不同的信息。
  3. 聚焦式注意力的应用: 自上而下的聚焦式注意力是一种有效的信息选择方式。在任务中,只选择与任务相关的信息,而忽略不相关的部分。例如,在阅读理解任务中,只有与问题相关的文章片段被选择用于后续的处理,减轻了神经网络的计算负担。
  4. 注意力的计算过程:注意力机制的计算分为两步。首先,在所有输入信息上计算注意力分布,然后根据这个分布计算输入信息的加权平均。这个计算依赖于一个查询向量(Query Vector),通过一个打分函数来计算每个输入向量和查询向量之间的相关性。
    • 注意力分布(Attention Distribution):注意力分布表示在给定查询向量和输入信息的情况下,选择每个输入向量的概率分布。Softmax 函数被用于将分数转化为概率分布,其中每个分数由一个打分函数计算得到。
    • 打分函数(Scoring Function):打分函数衡量查询向量与输入向量之间的相关性。文中介绍了几种常用的打分函数,包括加性模型、点积模型、缩放点积模型和双线性模型。这些模型通过可学习的参数来调整注意力的计算。
      • 加性模型
      mathbf{s}(mathbf{x}, mathbf{q}) = mathbf{v}^T tanh(mathbf{W}mathbf{x} mathbf{U}mathbf{q})
      • 点积模型
      mathbf{s}(mathbf{x}, mathbf{q}) = mathbf{x}^T mathbf{q}
      • 缩放点积模型
      mathbf{s}(mathbf{x}, mathbf{q}) = frac{mathbf{x}^T mathbf{q}}{sqrt{D}}

      (缩小方差,增大softmax梯度)

      • 双线性模型
      mathbf{s}(mathbf{x}, mathbf{q}) = mathbf{x}^T mathbf{W} mathbf{q}

      (非对称性)

  5. 软性注意力机制
    • 定义:软性注意力机制通过一个“软性”的信息选择机制对输入信息进行汇总,允许模型以概率形式对输入的不同部分进行关注,而不是强制性地选择一个部分。
    • 加权平均:软性注意力机制中的加权平均表示在给定任务相关的查询向量时,每个输入向量受关注的程度,通过注意力分布实现。
    • Softmax 操作:注意力分布通常通过 Softmax 操作计算,确保它们成为一个概率分布。

1. 注意力权重矩阵可视化(矩阵热图)

【深度学习实验】注意力机制(一):注意力权重矩阵可视化(矩阵热图heatmap)

2. 掩码Softmax 操作

【深度学习实验】注意力机制(二):掩码Softmax 操作

3. 打分函数——加性注意力模型

  • 加性模型
mathbf{s}(mathbf{x}, mathbf{q}) = mathbf{v}^T tanh(mathbf{W}mathbf{x} mathbf{U}mathbf{q})

【深度学习实验】注意力机制(三):打分函数——加性注意力模型

3. 打分函数——点积注意力与缩放点积注意力

  • 点积模型
mathbf{s}(mathbf{x}, mathbf{q}) = mathbf{x}^T mathbf{q}
  • 缩放点积模型
mathbf{s}(mathbf{x}, mathbf{q}) = frac{mathbf{x}^T mathbf{q}}{sqrt{D}}

(缩小方差,增大softmax梯度)

a. 缩放点积注意力模型
代码语言:javascript复制
class DotProductAttention(nn.Module):
    """缩放点积注意力"""

    def __init__(self, dropout, **kwargs):
        super(DotProductAttention, self).__init__(**kwargs)
        # 使用暂退法进行模型正则化
        self.dropout = nn.Dropout(dropout)

    def forward(self, queries, keys, values, valid_lens=None):
        d = queries.shape[-1]
        self.scores = torch.bmm(queries, keys.transpose(1, 2)) / math.sqrt(d)
        self.attention_weights = masked_softmax(self.scores, valid_lens)
        return torch.bmm(self.dropout(self.attention_weights), values)
  1. 初始化方法 (__init__):
    • 参数:
      • dropout: Dropout 正则化的概率。
    • 说明: 初始化方法定义了模型的组件,仅包含了一个 Dropout 正则化层。
  2. 前向传播方法 (forward):
    • 参数:
      • queries: 查询张量,形状为 (batch_size, num_queries, d)
      • keys: 键张量,形状为 (batch_size, num_kv_pairs, d)
      • values: 值张量,形状为 (batch_size, num_kv_pairs, value_size)
      • valid_lens: 有效长度张量,形状为 (batch_size,)(batch_size, num_queries)
    • 返回值: 加权平均后的值张量,形状为 (batch_size, num_queries, value_size)
  3. 实现细节:
    • 计算缩放点积得分:通过张量乘法计算 querieskeys 的点积,然后除以
    sqrt{d}

    进行缩放,其中

    d

    是查询或键的维度。

    • 使用 masked_softmax 函数计算注意力权重,根据有效长度对注意力进行掩码。
    • 将注意力权重应用到值上,得到最终的加权平均结果。
    • 使用 Dropout 对注意力权重进行正则化。
b. 点积注意力模型
代码语言:javascript复制
class DotProductAttention2(nn.Module):
    """点积注意力"""

    def __init__(self, dropout, **kwargs):
        super(DotProductAttention2, self).__init__(**kwargs)
        # 使用暂退法进行模型正则化
        self.dropout = nn.Dropout(dropout)

    def forward(self, queries, keys, values, valid_lens=None):
        # P195:(8.3),(8.4)
        # 在计算得分时不进行缩放操作(即不再除以sqrt(d))
        self.scores = torch.bmm(queries, keys.transpose(1, 2))
        self.attention_weights = masked_softmax(self.scores, valid_lens)
        return torch.bmm(self.dropout(self.attention_weights), values)
  • 区别:
    • 计算点积得分:通过张量乘法计算 querieskeys 的点积。
c. 模拟实验
  • 模型数据
代码语言:javascript复制
queries, keys = torch.normal(0, 1, (2, 1, 2)), torch.ones((2, 10, 2))
values = torch.arange(40, dtype=torch.float32).reshape(1, 10, 4).repeat(
    2, 1, 1)
valid_lens = torch.tensor([2, 6])
  • 模型应用
代码语言:javascript复制
# 创建缩放点积注意力模型
attention = DotProductAttention(0.5)
attention.eval()
# 使用模型进行前向传播
attention(queries, keys, values, valid_lens)

# 创建点积注意力模型
attention2 = DotProductAttention2(0.5)
attention2.eval()
# 使用模型进行前向传播
attention2(queries, keys, values, valid_lens)
  • 权重可视化   为了直观地展示模型在不同输入下的注意力分布,使用前文 show_heatmaps 函数,通过热图的形式展示注意力权重。
代码语言:javascript复制
# 可视化缩放点积注意力权重
show_heatmaps(attention.attention_weights.reshape((1, 1, 2, 10)),
              xlabel='Keys', ylabel='Queries')

# 可视化点积注意力权重
show_heatmaps(attention2.attention_weights.reshape((1, 1, 2, 10)),
              xlabel='Keys', ylabel='Queries')
d. 模型比较与选择
  • 缩放点积注意力模型
    • 适用于处理高维度的查询和键。
    • 通过缩放操作有助于防止点积得分的方差过大。
  • 点积注意力模型
    • 适用于处理相对较低维度的查询和键。
    • 更方便地利用矩阵乘积提高计算效率。
e. 代码整合
代码语言:javascript复制
# 导入必要的库
import math
import torch
from torch import nn
import torch.nn.functional as F
from d2l import torch as d2l
from torch.utils import data


def masked_softmax(X, valid_lens):
    """通过在最后一个轴上掩蔽元素来执行softmax操作"""
    # X:3D张量,valid_lens:1D或2D张量
    if valid_lens is None:
        return nn.functional.softmax(X, dim=-1)
    else:
        shape = X.shape
        if valid_lens.dim() == 1:
            valid_lens = torch.repeat_interleave(valid_lens, shape[1])
        else:
            valid_lens = valid_lens.reshape(-1)
        # 最后一轴上被掩蔽的元素使用一个非常大的负值替换,从而其softmax输出为0
        X = d2l.sequence_mask(X.reshape(-1, shape[-1]), valid_lens, value=-1e6)
        return nn.functional.softmax(X.reshape(shape), dim=-1)


def show_heatmaps(matrices, xlabel, ylabel, titles=None, figsize=(2.5, 2.5), cmap='Reds'):
    """显示矩阵热图"""
    d2l.use_svg_display()
    num_rows, num_cols = matrices.shape[0], matrices.shape[1]
    fig, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize,
                                 sharex=True, sharey=True, squeeze=False)
    for i, (row_axes, row_matrices) in enumerate(zip(axes, matrices)):
        for j, (ax, matrix) in enumerate(zip(row_axes, row_matrices)):
            pcm = ax.imshow(matrix.detach().numpy(), cmap=cmap)
            if i == num_rows - 1:
                ax.set_xlabel(xlabel)
            if j == 0:
                ax.set_ylabel(ylabel)
            if titles:
                ax.set_title(titles[j])
    fig.colorbar(pcm, ax=axes, shrink=0.6)

class DotProductAttention(nn.Module):
    """缩放点积注意力"""

    def __init__(self, dropout, **kwargs):
        super(DotProductAttention, self).__init__(**kwargs)
        # 使用暂退法进行模型正则化
        self.dropout = nn.Dropout(dropout)

    # queries的形状:(batch_size,查询的个数,d)
    # keys的形状:(batch_size,“键-值”对的个数,d)
    # values的形状:(batch_size,“键-值”对的个数,值的维度)
    # valid_lens的形状:(batch_size,)或者(batch_size,查询的个数)
    def forward(self, queries, keys, values, valid_lens=None):
        print(queries)
        d = queries.shape[-1]
        print(d)
        self.scores = torch.bmm(queries, keys.transpose(1, 2)) / math.sqrt(d)
        print(self.scores)
        self.attention_weights = masked_softmax(self.scores, valid_lens)
        return torch.bmm(self.dropout(self.attention_weights), values)


class DotProductAttention2(nn.Module):
    """点积注意力"""

    def __init__(self, dropout, **kwargs):
        super(DotProductAttention2, self).__init__(**kwargs)
        # 使用暂退法进行模型正则化
        self.dropout = nn.Dropout(dropout)

    # queries的形状:(batch_size,查询的个数,d)
    # keys的形状:(batch_size,“键-值”对的个数,d)
    # values的形状:(batch_size,“键-值”对的个数,值的维度)
    # valid_lens的形状:(batch_size,)或者(batch_size,查询的个数)

    def forward(self, queries, keys, values, valid_lens=None):
        # P195:(8.3),(8.4)
        # 在计算得分时不进行缩放操作(即不再除以sqrt(d))
        self.scores = torch.bmm(queries, keys.transpose(1, 2))
        print(self.scores)
        self.attention_weights = masked_softmax(self.scores, valid_lens)
        return torch.bmm(self.dropout(self.attention_weights), values)


queries, keys = torch.normal(0, 1, (2, 1, 2)), torch.ones((2, 10, 2))
values = torch.arange(40, dtype=torch.float32).reshape(1, 10, 4).repeat(
    2, 1, 1)
valid_lens = torch.tensor([2, 6])


# 缩放点积注意力模型
attention = DotProductAttention(0.5)
attention.eval()
attention(queries, keys, values, valid_lens)

# 点积注意力模型
attention2 = DotProductAttention2(0.5)
attention2.eval()
attention2(queries, keys, values, valid_lens)

0 人点赞