自注意力机制与批量归一化在深度学习中的应用(Datawhale X 李宏毅苹果书 AI夏令营)

引言

在深度学习领域,自注意力机制和批量归一化已经成为推动模型性能提升的关键技术。自注意力机制通过捕捉序列中的全局依赖关系,极大地增强了模型的表达能力;批量归一化则通过规范化输入数据,提升了模型的训练效率和泛化能力。本文将详细介绍这两种技术的原理及其在实际模型中的应用,尤其是如何将它们结合使用来提升模型的性能。

自注意力机制的基本原理

原理概述

自注意力机制(Self-Attention Mechanism)允许模型在处理输入序列时关注序列中的不同位置,从而捕捉到长距离的依赖关系。其核心思想是计算序列中每个位置的表示与其他位置的关系,进而调整表示以包含全局信息。

数学背景

自注意力机制的核心计算过程包括以下步骤:

  1. 计算注意力得分(Attention Scores): 对于输入序列中的每个位置,我们计算它与其他位置的相似度,得到注意力得分。假设输入序列为 X,其对应的查询(Query)、键(Key)和值(Value)矩阵分别为 Q、K 和 V,注意力得分计算公式为:

  2. 计算注意力权重(Attention Weights): 对注意力得分应用 softmax 操作,以获得归一化的注意力权重:

                                       Attention Weights=softmax(Attention Scores)
  3. 计算加权和(Weighted Sum): 使用注意力权重对值(Value)矩阵进行加权求和,得到最终的输出:

                                                   Output=Attention Weights⋅V

传统序列模型的局限性

在序列数据的处理上,传统的模型如循环神经网络(RNN)和长短期记忆网络(LSTM)通过逐步处理每个时间步来捕捉序列的时序依赖关系。这些模型在处理短序列时表现优异,但在面对长序列时,常常面临梯度消失或爆炸的问题。此外,由于这些模型无法并行化处理,导致计算效率较低。

自注意力机制的引入

自注意力机制的引入解决了上述问题。它通过计算序列中每个元素与其他所有元素的相似度,从而在全局范围内捕捉依赖关系。具体而言,对于序列中的每个元素,自注意力机制都会生成一个查询向量(Query),并将其与序列中其他元素的键向量(Key)进行点积运算以计算相似度。然后,使用这些相似度对对应的数值向量(Value)进行加权求和,最终得到该元素的新的表示。

自注意力机制的数学公式

给定输入序列 X=[x1,x2,…,xn],对于序列中的每个元素 xix_ixi​,自注意力机制的计算过程如下:

  1. 生成查询、键和值向量

    其中,​ 是可学习的权重矩阵。

  2. 计算相似度得分

    为了稳定训练过程,这些得分通常会除以,其中 dk​ 是键向量的维度。

  3. 计算注意力权重

  4. 加权求和生成新表示

通过这种机制,序列中的每个元素都能够参考全局上下文来更新自身的表示,从而克服传统序列模型无法捕捉长距离依赖关系的局限。

多头注意力机制(Multi-Head Attention)

自注意力机制的一个显著扩展是多头注意力机制(Multi-Head Attention)。在这种机制中,模型会将输入数据分为多个子空间,并在每个子空间上独立执行自注意力计算。最终将这些子空间的输出拼接起来,形成一个更丰富的特征表示。这种方法不仅能够捕捉数据的多样性信息,还提高了模型的表达能力和鲁棒性。

具体来说,假设我们有 hhh 个头,那么多头注意力机制的过程如下:

  1. 对输入数据分别生成多个不同的查询、键和值向量:

    其中  是每个头独立的可学习权重矩阵。

  2. 在每个头上独立计算注意力输出:

  3. 将所有头的输出拼接起来:

  4. 通过一个线性变换整合所有头的输出:

    其中 WOW_OWO​ 是拼接后输出的线性变换矩阵。

多头注意力机制极大地提高了模型的表达能力,使得模型能够在不同的子空间中学习到不同的特征表示。

批量归一化的基本原理

内部协变量偏移问题

在深度神经网络中,随着层数的增加,输入数据的分布可能会不断变化,这种现象称为“内部协变量偏移”(Internal Covariate Shift)。它会导致模型训练变得困难,因为模型的每一层都必须不断适应前一层输出分布的变化。

批量归一化的引入

批量归一化通过对每一层的输入进行标准化,来减少这种偏移,从而加速训练并提高模型的稳定性。批量归一化的操作如下:

  1. 计算当前批次的均值和方差: 对于输入批次 X=[x1,x2,…,xm]

  2. 标准化输入数据

    其中,ϵ 是一个很小的数,用于防止分母为零。

  3. 恢复模型表达能力: 引入两个可学习的参数,用于缩放和平移标准化后的数据:

通过这种方式,批量归一化能够保证每一层的输入数据分布保持稳定,从而加快训练并减少模型对初始参数的敏感性。

批量归一化的优点

  1. 加速训练:批量归一化能够显著加速模型的训练过程,通常情况下,可以使用更大的学习率。
  2. 提高泛化能力:批量归一化通过减少内部协变量偏移,使得模型更容易泛化到新的数据。
  3. 减少对初始化的依赖:批量归一化减少了模型对权重初始化的敏感性,使得模型更容易收敛。

自注意力机制与批量归一化的结合

在深度神经网络中,自注意力机制与批量归一化的结合使用可以极大地提升模型的表现。尤其是在Transformer等模型中,多头自注意力机制通过捕捉全局依赖关系,为每个输入生成更丰富的上下文信息。而批量归一化则确保了这些上下文信息的稳定性,从而提高了模型的训练速度和泛化能力。

自注意力机制中的批量归一化

批量归一化通常应用于自注意力机制的以下部分:

  1. 自注意力层的输出:在计算自注意力机制输出后,通过批量归一化来规范化输出数据,避免输入数据分布的剧烈变化。
  2. 多头注意力的拼接输出:在多头注意力机制中,多个子空间的输出拼接后,通过批量归一化来保持输出数据的稳定分布。

下面是一个简单的代码示例,展示如何在自注意力机制中结合批量归一化:

import torch
import torch.nn as nn

class SelfAttentionWithBatchNorm(nn.Module):
    def __init__(self, embed_size, heads):
        super(SelfAttentionWithBatchNorm, self).__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, embed_size, bias=False)
        self.keys = nn.Linear(self.head_dim, embed_size, bias=False)
        self.queries = nn.Linear(self.head_dim, embed_size, bias=False)
        self.fc_out = nn.Linear(embed_size, embed_size)
        self.batch_norm = nn.BatchNorm1d(embed_size)  # Batch Normalization layer

    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)

        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.embed_size
        )

        out = self.fc_out(out)
        out = self.batch_norm(out)  # Apply Batch Normalization
        return out

代码解释

  1. 初始化部分

    • valueskeysqueries 分别是计算注意力所需的线性变换,用于生成值向量、键向量和查询向量。
    • fc_out 是用于将多头注意力机制的输出映射回原始的嵌入维度。
    • batch_norm 是批量归一化层,用于规范化注意力层的输出。
  2. 前向传播

    • 将输入的 valueskeysqueries 数据按照头数拆分为多个子空间。
    • 使用爱因斯坦求和(torch.einsum)计算查询向量和键向量的点积,得到注意力得分。
    • 对得分进行 softmax 操作,得到注意力权重。
    • 使用注意力权重对值向量进行加权求和,并重新组合成原始的嵌入维度。
    • 最后,通过 fc_out 线性变换和 batch_norm 批量归一化来处理输出。

原理概述

批量归一化(Batch Normalization, BN)是一种对网络中的每一层进行归一化的技术,其目的是加快训练速度、提高模型的稳定性。BN 通过规范化每一层的输入,使其均值为 0,方差为 1,来减少内部协方差偏移(Internal Covariate Shift)。

数学背景

对于每个批次中的每个特征,批量归一化的计算过程如下:

  1. 计算均值和方差: 对于输入 x,计算其均值 μ 和方差 σ^2:

    其中 mmm 是批次的大小。

  2. 归一化: 使用均值和方差对输入进行归一化:

    其中 ϵ 是一个小常数,用于避免除零错误。

  3. 缩放和平移: 应用可训练的缩放因子 γ 和偏移因子 β:

批量归一化的实现

以下是使用 PyTorch 实现的批量归一化层的代码示例:

import torch
import torch.nn as nn

class BatchNormLayer(nn.Module):
    def __init__(self, num_features):
        super(BatchNormLayer, self).__init__()
        self.batch_norm = nn.BatchNorm1d(num_features)

    def forward(self, x):
        return self.batch_norm(x)

批量归一化在测试阶段的处理 

批量归一化在训练和测试阶段的行为有所不同。在训练阶段,批量归一化使用当前批次的均值和方差进行标准化。在测试阶段,由于每次只处理一个样本,因此需要使用训练过程中累积的均值和方差。为了确保一致性,模型在测试时会切换到评估模式(model.eval()),此时批量归一化会使用训练期间记录的全局均值和方差来进行标准化。

# 切换到测试模式
model.eval()

自注意力机制与批量归一化在卷积神经网络中的应用

结合的动机

自注意力机制和批量归一化可以在同一模型中结合使用,以提升模型的表现。自注意力机制用于捕捉长距离依赖关系,而批量归一化则可以加速训练和提升模型的稳定性。将它们结合在一起,可以进一步提高模型的性能。

卷积神经网络(CNN)的传统挑战

卷积神经网络(CNN)在处理图像数据时表现出色,尤其是在特征提取方面。然而,传统的CNN往往缺乏对长距离依赖的处理能力,这在处理复杂视觉任务时可能成为瓶颈。为了克服这些挑战,研究者们开始将自注意力机制引入到CNN中,以提升模型的性能。

Vision Transformer(ViT)架构

Vision Transformer(ViT)是一种将自注意力机制应用于计算机视觉任务的模型。它通过将输入图像切分为一系列的图像块(patches),并将这些图像块视为序列数据来应用自注意力机制。这种方法充分利用了自注意力机制捕捉全局信息的优势,同时结合了Transformer架构的优势。

Vision Transformer的工作流程
  1. 图像切分

    • 将输入图像切分为固定大小的图像块,并将每个图像块展平成一维向量。
  2. 位置编码

    • 对每个图像块向量添加位置编码,以保留图像中每个块的位置信息。
  3. 自注意力机制

    • 使用自注意力机制处理这些图像块,以捕捉图像中不同区域之间的全局依赖关系。
  4. 分类头

    • 将自注意力层的输出传递到一个分类头,以进行最终的分类任务。
Vision Transformer与CNN的结合

尽管Vision Transformer展示了强大的性能,但它也面临着计算复杂度高、数据需求大的问题。为了解决这些问题,研究者们提出了一些结合CNN和Transformer的混合架构,例如使用CNN作为特征提取器,然后将提取的特征输入到Transformer模型中进行进一步处理。这种方法能够结合CNN在局部特征提取方面的优势和Transformer在全局依赖捕捉方面的优势。

实现细节

以下是结合自注意力机制和批量归一化的完整模型示例:

import torch
import torch.nn as nn

class SelfAttentionWithBatchNorm(nn.Module):
    def __init__(self, embed_size, heads):
        super(SelfAttentionWithBatchNorm, self).__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, embed_size, bias=False)
        self.keys = nn.Linear(self.head_dim, embed_size, bias=False)
        self.queries = nn.Linear(self.head_dim, embed_size, bias=False)
        self.fc_out = nn.Linear(embed_size, embed_size)
        self.batch_norm = nn.BatchNorm1d(embed_size)  # Batch Normalization layer

    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)

        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.embed_size
        )

        out = self.fc_out(out)
        out = self.batch_norm(out)  # Apply batch normalization
        return out

代码解释

  1. 注意力计算

    • 将输入数据拆分为多个头,计算注意力得分并应用 softmax 操作,得到注意力权重。
    • 使用注意力权重对值向量进行加权求和,并通过线性变换获得输出。
  2. 批量归一化

    • 在自注意力输出之后,应用批量归一化层。这有助于规范化每一层的输出,从而提升模型训练的稳定性和收敛速度。

自注意力机制与批量归一化在卷积神经网络中的应用

卷积神经网络(CNN)中的应用

在卷积神经网络中,自注意力机制和批量归一化可以用来增强特征提取能力和稳定训练过程。以下是一个结合自注意力机制和批量归一化的卷积神经网络的实现示例:

import torch
import torch.nn as nn

class ConvSelfAttentionWithBatchNorm(nn.Module):
    def __init__(self, in_channels, out_channels, embed_size, heads):
        super(ConvSelfAttentionWithBatchNorm, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=1, padding=1)
        self.self_attention = SelfAttentionWithBatchNorm(embed_size, heads)
        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1)
        self.batch_norm = nn.BatchNorm2d(out_channels)

    def forward(self, x):
        x = self.conv1(x)
        x = x.flatten(2).permute(0, 2, 1)  # Reshape for self-attention
        x = self.self_attention(x, x, x, None)
        x = x.permute(0, 2, 1).reshape(x.shape[0], -1, int(x.shape[1]**0.5), int(x.shape[1]**0.5))
        x = self.conv2(x)
        x = self.batch_norm(x)  # Apply batch normalization
        return x

代码解释

  1. 卷积层

    • conv1:第一个卷积层用于提取特征。
    • conv2:第二个卷积层进一步处理特征。
  2. 自注意力机制

    • 在卷积层之后,使用自注意力机制对特征进行处理。输入数据需要进行适当的重塑和转置,以符合自注意力机制的输入要求。
  3. 批量归一化

    • 在自注意力机制之后,通过卷积层进行处理,再次应用批量归一化以稳定训练过程。

自注意力机制与批量归一化在 Vision Transformer(ViT)中的应用

Vision Transformer 概述

Vision Transformer 是一种基于 Transformer 的模型,用于处理图像数据。与传统的卷积神经网络不同,ViT 将图像分成若干个小块,然后通过自注意力机制处理这些块。批量归一化可以与 ViT 的自注意力机制结合使用,提升模型性能。

实现细节

以下是一个结合自注意力机制和批量归一化的 Vision Transformer 模型的实现示例:

import torch
import torch.nn as nn
from torchvision import transforms

class VisionTransformer(nn.Module):
    def __init__(self, image_size, patch_size, in_channels, embed_size, num_heads, num_classes):
        super(VisionTransformer, self).__init__()
        self.patch_size = patch_size
        self.embedding = nn.Linear(patch_size * patch_size * in_channels, embed_size)
        self.self_attention = SelfAttentionWithBatchNorm(embed_size, num_heads)
        self.fc_out = nn.Linear(embed_size, num_classes)

    def forward(self, x):
        N, C, H, W = x.shape
        x = x.unfold(2, self.patch_size, self.patch_size).unfold(3, self.patch_size, self.patch_size)
        x = x.contiguous().view(N, C, -1, self.patch_size * self.patch_size).permute(0, 2, 1, 3).flatten(2)
        x = self.embedding(x)
        x = self.self_attention(x, x, x, None)
        x = x.mean(dim=1)
        x = self.fc_out(x)
        return x

代码解释

  1. 图像嵌入

    • 将图像划分为小块,并将每个块展平为一维向量。通过线性变换将这些向量映射到嵌入空间。
  2. 自注意力机制

    • 使用自注意力机制处理图像块的嵌入表示,从而捕捉全局特征。
  3. 分类

    • 将处理后的特征通过全连接层进行分类。

总结

自注意力机制和批量归一化在深度学习模型中扮演着至关重要的角色。自注意力机制通过捕捉序列中的长距离依赖关系来增强模型的表达能力,而批量归一化则通过规范化每一层的输入来加速训练和提高模型的稳定性。将这两种技术结合使用,可以在多种深度学习任务中取得显著的性能提升。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值