2024.5.5 机器学习周报

发布于:2024-05-05 ⋅ 阅读:(29) ⋅ 点赞:(0)

目录

引言

Abstract

文献阅读

1、题目

2、引言

3、创新点

4、匹配问题

5、SuperGlue架构

5.1、注意力图神经网络(Attentional Graph Neural Network)

5.2、最佳匹配层(Optimal matching layer)

5.3、损失

6、实验

6.1、单应性估算

 6.2、室内姿态估计

6.3、室外姿态估计

7、结论

深度学习 Transformer(图解+手撕)

1、理解注意力机制

2、自注意力是如何工作的?

3、Transformer 模型的基础

4、Transformer 组件的详细说明

4.1、位置编码

 4.2、多头注意力机制

4.3、前馈网络

4.4、编码器 

4.5、解码器 

5、Transformer 模型架构

 6、模型的训练与评估

7、训练和评估的实现

 总结

引言

本周阅读了一篇关于SuperGlue的文献,SuperGlue是一种神经网络,通过找到对应关系并拒绝不匹配的点,将两组局部特征进行匹配。它使用可微的最优传输问题来估计分配,成本由图神经网络预测。SuperGlue通过端到端训练学习几何变换的先验知识和三维世界的规律。在具有挑战性的真实环境中,它优于其他学习方法,并在姿态估计方面取得了最先进的结果。并且对transformer重温复习,以及对其代码手撕学习,加深了印象。

Abstract

This week I read a literature on SuperGlue, which is a neural network that matches two sets of local features by finding corresponding relationships and rejecting mismatched points. It uses differentiable optimal transmission problems to estimate allocation, and costs are predicted by graph neural networks. SuperGlue learns prior knowledge of geometric transformations and the laws of the three-dimensional world through end-to-end training. In challenging real-world environments, it outperforms other learning methods and achieves state-of-the-art results in attitude estimation. And I also revisited and reviewed the transformer, as well as learned its code by hand, which deepened my impression.

文献阅读

1、题目

SuperGlue: Learning Feature Matching with Graph Neural Networks

2、引言

本文介绍了SuperGlue,这是一种神经网络,它通过联合寻找对应关系并拒绝不匹配的点来匹配两组局部特征。通过求解一个可微的最优运输问题来估计运输成本,其成本由图神经网络来预测。我们引入了一个灵活的上下文聚合机制的基础上的注意力,使SuperGlue的理由有关的基础3D场景和功能分配联合。与传统的手工设计的几何学相比,我们的技术通过图像对的端到端训练来学习几何变换和3D世界的先验知识。SuperGlue优于其他学习方法,并在具有挑战性的真实室内和室外环境中的姿态估计任务上取得了最先进的结果。所提出的方法在现代GPU上实时执行匹配,并且可以容易地集成到现代SfM或SLAM系统中。

3、创新点

  • SuperGlue架构利用了自注意力和交叉注意力,有效处理局部特征匹配问题。
  • SuperGlue通过解决最优传输问题,优雅地处理了局部分配和遮挡点。
  • SuperGlue实现了在极端宽基线室内和室外图像对上高度准确的相对姿态估计。

4、匹配问题

  • 本地特征匹配:通常,这是通过以下三个步骤来完成的:
  1. 提取图像特征;
  2. 使用最近邻居搜索匹配这些特征;
  3.  使用几何变换(例如RANSAC)对它们进行过滤。传统的方法通常基于SIFT,并使用邻域共识等技术。
  • 深度学习与匹配:一些工作尝试直接从深度学习模型中获取匹配,这些模型可能使用卷积神经网络从数据中获取特征。一些方法显式地考虑更广泛的上下文,或通过学习区分内外点(inliers and outliers)来过滤匹配。其他工作着重于深度学习来进行特征匹配,这些工作可能基于3D点云或视觉信息。PostRANSAC等方法可以从头到尾地执行内容聚合、匹配和过滤。
  • 图匹配问题:这些问题通常被描述为二次分配问题,这些问题是NP-hard,需要复杂且昂贵的解算器。早期的方法依赖于手工制作的代价函数,而现代的方法则更加简化,例如使用Sinkhorn算法来近似解决问题。
  • 深度学习与置换:深度学习可以被用于求解排列等式或不变的问题。这可以通过全局池化、区间规范化或在特征空间中的局部邻域来实现。此外,深度学习还可以进行全局和数据依赖的数据集聚合。

5、SuperGlue架构

  • Motivation:在图像匹配问题中,需要注意一些规律性。例如,某些关键点在静态场景中更容易匹配,而在动态场景中可能更难匹配。

  • Formulation:考虑两个图像A和B,每个图像都有一组关键点位置和关联的视觉描述符。SuperGlue旨在学习从数据中直接获取的相关先验知识

SuperGlue由两个主要组件组成:注意力图神经网络(Attentional Graph Neural Network)和最佳匹配层(Optimal matching layer)。第一个组件使用关键点编码器将关键点位置p及其视觉描述符d映射到单个向量中,然后使用交替的自我和交叉注意层(重复L次)来创建更强大的表示f。最佳匹配层创建一个M × N得分矩阵,用垃圾箱对其进行扩充,然后使用Sinkhorn算法(T次迭代)找到最佳部分分配。

5.1、注意力图神经网络(Attentional Graph Neural Network)

当被要求匹配一个给定的模糊关键点时,人类会来回查看两张图像:他们筛选试探性匹配的关键点,检查每个关键点,并寻找上下文线索,以帮助从其他自相似性中消除真正的匹配。这暗示了一个迭代过程,可以将注意力集中在特定的位置。

关键点编码器:将关键点位置嵌入到具有多层感知器(MLP)的高维向量中:

这个编码器使图形网络能够在以后联合推理外观和位置,特别是当与注意力结合时,并且是语言处理中流行的“位置编码器”的一个实例。

多重图神经网络:图有两种类型的无向边-它是一个多重图。图像内边缘或自边缘将关键点i连接到同一图像内的所有其他关键点。图像间边缘或交叉边缘将关键点i连接到其他图像中的所有关键点。我们使用以下消息传递公式来沿着沿着两种类型的边传播信息:

由此产生的多路复用图神经网络从每个节点的高维状态开始,并通过同时聚合所有节点的所有给定边的消息来在每一层计算更新的表示。令(xA i)为图像A中位于层xB处的元素i的中间表示。消息m_{\varepsilon }→i是来自所有关键点{j :(i,j)∈ \varepsilon}的聚合的结果。其中[·||·]表示连接。

如上图是可视化自我注意力和交叉注意力。注意力聚合在关键点之间构建动态图。权重αij显示为射线。自我注意力(顶部)可以出现在同一图像中的任何地方,例如独特的位置,因此不限于附近的位置。交叉关注(底部)关注其他图像中的位置,例如具有相似外观的潜在匹配。 

注意力聚合:注意力机制执行聚合并计算消息m_{\varepsilon \rightarrow i}。消息计算为以下值的加权平均值:

5.2、最佳匹配层(Optimal matching layer)

SuperGlue的第二个主要模块是最优匹配层,它产生一个部分分配矩阵。如在标准图匹配公式中

分配P可以通过计算所有可能匹配的得分矩阵S ∈ R^{M\times N}并在等式中的约束下最大化总得分 \sum_{i,j} S_{i,j} P_{i,j}来获得。这相当于解决一个线性分配问题。

分数预测:为所有M×N个潜在匹配构建单独的表示将是禁止的。相反,我们将成对得分表示为匹配描述符的相似性:

遮挡和可见性:为了让网络抑制一些关键点,我们用垃圾箱来增加每个集合,以便将不匹配的关键点显式分配给它。 

虽然A中的关键点将被分配给B中的单个关键点或垃圾箱,但每个垃圾箱具有与另一组中的关键点一样多的匹配:

5.3、损失

通过设计,图神经网络和最佳匹配层都是可区分的,这使得从匹配到视觉描述符的反向传播成为可能。SuperGlue以监督的方式从地面真实匹配M = {(i,j)}  \subset A × B进行训练。这些是从地面实况相对变换估计的,使用姿势和深度图或单应性。这也允许将一些关键点标记为不匹配,如果它们在其附近没有任何重投影。给定这些标签,最小化分配的负对数似然:

6、实验

6.1、单应性估算

使用真实的图像和具有鲁棒性(RANSAC)和非鲁棒性(DLT)估计器的合成单应性进行大规模单应性估计实验。

通过对随机单应性进行采样并将随机光度失真应用于真实的图像来生成图像对,底层图像来自牛津和巴黎数据集中的1M干扰项图像集,分为训练集、验证集和测试集。

SuperGlue恢复几乎所有可能的匹配,同时抑制大多数离群值。由于SuperGlue对应关系是高质量的,因此直接线性变换(DLT),一种基于最小二乘的解决方案,没有鲁棒性机制,优于RANSAC。

 6.2、室内姿态估计

室内图像匹配由于缺乏纹理、自相似性丰富、场景三维几何形状复杂、视点变化大等特点而具有很大的挑战性。

数据集使用ScanNet,由具有地面真实姿势和深度图像的单目序列组成,以及对应于不同场景的定义良好的训练,验证和测试分割。

上表报告了姿势误差的AUC、匹配分数(MS)和精度(P),全部以百分比%表示。SuperGlue在应用于SIFT和SuperPoint时优于所有手工制作和学习的匹配器。 

SuperGlue使用SIFT或SuperPoint局部特征,并持续大幅提高OANet(一种最先进的离群值拒绝神经网络)的姿态精度。

6.3、室外姿态估计

由于室外图像序列呈现出它们自己的一组挑战(例如,照明变化和遮挡),训练和评估SuperGlue在户外环境中的姿态估计,使用与室内姿态估计任务中相同的评估指标和基线方法。

使用PhotoTourism数据集进行评估,PhotoTourism测试集中的场景将从训练集中移除。与室内情况类似,选择具有挑战性的图像对进行训练和评估,使用从SfM共视性计算的重叠分数。

由上表可知,使用SuperGlue匹配SuperPoint和SIFT特征,与手工制作或其他学习方法相比,姿势准确度(AUC)、精度(P)和匹配分数(MS)显著更高

SuperGlue的消融中,虽然最佳匹配层单独改进了基线最近邻匹配器,但图神经网络解释了SuperGlue带来的大部分收益。交叉注意和位置编码对于强粘合都是至关重要的,更深的网络进一步提高了精度。

7、结论

本文展示了基于注意力的图神经网络用于局部特征匹配的能力。SuperGlue的架构使用了两种注意力:(i)自我注意力,它增强了局部描述符的接受域;(ii)交叉注意力,它实现了跨图像通信,并受到人类在匹配图像时来回观看的方式的启发。本文的方法优雅地处理部分分配和闭塞点,通过解决最优运输问题。本文实验表明,SuperGlue实现了显着的改进,现有的方法,使极宽基线的室内和室外图像对高度准确的相对姿态估计。此外,SuperGlue实时运行,并与经典和学习功能配合良好。总之,本文的可学习中端用一个强大的神经模型取代了手工制作的算法,该模型在一个统一的架构中同时执行上下文聚合、匹配和过滤。

深度学习 Transformer(图解+手撕)

1、理解注意力机制

注意力机制是神经网络中一个迷人的概念,特别是在涉及到像 NLP 这样的任务时。它就像给模型一个聚光灯,让它能够集中注意力在输入序列的某些部分,同时忽略其他部分,就像我们人类在理解句子时关注特定的单词或短语一样。

现在,深入了解一种特定类型的注意力机制,称为自注意力,也称为内部注意力。想象一下,当你阅读一句话时,你的大脑会自动突出显示重要的单词或短语来理解意思。这就是神经网络中自注意力的基本原理。它使序列中的每个单词都能“关注”其他单词,包括自己在内,以更好地理解上下文。

2、自注意力是如何工作的?

以下是自注意力在一个简单示例中的工作原理:考虑一句话:“The cat sat on the mat.”

嵌入

首先,模型将输入序列中的每个单词嵌入到一个高维向量表示中。这个嵌入过程允许模型捕捉单词之间的语义相似性。

查询、键和值向量

接下来,模型为序列中的每个单词计算三个向量:查询向量、键向量和值向量。在训练过程中,模型学习这些向量,每个向量都有不同的作用。查询向量表示单词的查询,即模型在序列中寻找的内容。键向量表示单词的键,即序列中其他单词应该注意的内容。值向量表示单词的值,即单词对输出所贡献的信息。

注意力分数

一旦模型计算了每个单词的查询、键和值向量,它就会为序列中的每一对单词计算注意力分数。这通常通过取查询向量和键向量的点积来实现,以评估单词之间的相似性。

SoftMax 归一化

然后,使用 softmax 函数对注意力分数进行归一化,以获得注意力权重。这些权重表示每个单词应该关注序列中其他单词的程度。注意力权重较高的单词被认为对正在执行的任务更为关键。

加权求和

最后,使用注意力权重计算值向量的加权和。这产生了每个序列中单词的自注意力机制输出,捕获了来自其他单词的上下文信息。

# 导入库
import torch
import torch.nn.functional as F

# 示例输入序列
input_sequence = torch.tensor([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6], [0.7, 0.8, 0.9]])

# 生成 Key、Query 和 Value 矩阵的随机权重
random_weights_key = torch.randn(input_sequence.size(-1), input_sequence.size(-1))
random_weights_query = torch.randn(input_sequence.size(-1), input_sequence.size(-1))
random_weights_value = torch.randn(input_sequence.size(-1), input_sequence.size(-1))

# 计算 Key、Query 和 Value 矩阵
key = torch.matmul(input_sequence, random_weights_key)
query = torch.matmul(input_sequence, random_weights_query)
value = torch.matmul(input_sequence, random_weights_value)

# 计算注意力分数
attention_scores = torch.matmul(query, key.T) / torch.sqrt(torch.tensor(query.size(-1), dtype=torch.float32))

# 使用 softmax 函数获得注意力权重
attention_weights = F.softmax(attention_scores, dim=-1)

# 计算 Value 向量的加权和
output = torch.matmul(attention_weights, value)

print("自注意力机制后的输出:")
print(output)

3、Transformer 模型的基础

编码器-解码器架构

在Transformer的核心是其编码器-解码器架构——两个关键组件之间的共生关系,分别负责处理输入序列和生成输出序列。编码器和解码器中的每一层都包含相同的子层,包括自注意力机制和前馈网络。这种架构不仅有助于全面理解输入序列,而且能够生成上下文丰富的输出序列。

位置编码

尽管Transformer模型具有强大的功能,但它缺乏对元素顺序的内在理解——这是位置编码所解决的一个缺点。通过将输入嵌入与位置信息结合起来,位置编码使模型能够区分序列中元素的相对位置。这种细致的理解对于捕捉语言的时间动态和促进准确理解至关重要。

多头注意力

Transformer模型的一个显著特征是它能够同时关注输入序列的不同部分——这是多头注意力实现的。通过将查询、键和值向量分成多个头,并进行独立的自注意力计算,模型获得了对输入序列的细致透视,丰富了其表示,带有多样化的上下文信息。

前馈网络

与人类大脑能够并行处理信息的能力类似,Transformer模型中的每一层都包含一个前馈网络——一种能够捕捉序列中元素之间复杂关系的多功能组件。通过使用线性变换和非线性激活函数,前馈网络使模型能够在语言的复杂语义景观中航行,促进文本的稳健理解和生成。

4、Transformer 组件的详细说明

要实现,首先运行位置编码、多头注意力机制和前馈网络的代码,然后是编码器、解码器和Transformer架构。

#import libraries
import math
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F

4.1、位置编码

在Transformer模型中,位置编码是一个关键组件,它将关于标记位置的信息注入到输入嵌入中。

与循环神经网络(RNNs)或卷积神经网络(CNNs)不同,由于其置换不变性,Transformers 缺乏对标记位置的内在知识。位置编码通过为模型提供位置信息来解决这一限制,使其能够按照正确的顺序处理序列。

位置编码的概念

通常在将输入嵌入传入Transformer模型之前,会将位置编码添加到嵌入中。它由一组具有不同频率和相位的正弦函数组成,允许模型根据它们在序列中的位置区分标记。

位置编码的公式如下

假设有一个长度为L的输入序列,并且需要在该序列中找到第k个对象的位置。位置编码由不同频率的正弦和余弦函数给出:

其中:

  • k:输入序列中对象的位置,0≤k<L/2

  • d:输出嵌入空间的维度

  • P(k,j):位置函数,用于将输入序列中的位置k映射到位置矩阵的索引(k,j)

  • n:用户定义的标量,由《Attention Is All You Need》的作者设置为10,000。

  • i:用于将列索引映射到0≤i<d/2的值,单个i值同时映射到正弦和余弦函数。

不同的位置编码方案

在Transformer中使用了各种位置编码方案,每种方案都有其优点和缺点:

  • 固定位置编码:在这种方案中,位置编码是预定义的,并对所有序列固定不变。虽然简单高效,但固定位置编码可能无法捕捉序列中的复杂模式。

  • 学习位置编码:另一种选择是在训练过程中学习位置编码,使模型能够自适应地从数据中捕捉位置信息。学习位置编码提供了更大的灵活性,但需要更多的参数和计算资源。

位置编码的实现

# 位置编码的实现
class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=5000):
        super(PositionalEncoding, self).__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() * (-torch.log(
        torch.tensor(10000.0)) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0)
        self.register_buffer('pe', pe)

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

# 示例用法
d_model = 512
max_len = 100
num_heads = 8

# 位置编码
pos_encoder = PositionalEncoding(d_model, max_len)

# 示例输入序列
input_sequence = torch.randn(5, max_len, d_model)

# 应用位置编码
input_sequence = pos_encoder(input_sequence)
print("输入序列的位置编码:")
print(input_sequence.shape)

 4.2、多头注意力机制

在Transformer架构中,多头注意力机制是一个关键组件,它使模型能够同时关注输入序列的不同部分。它允许模型捕捉序列内的复杂依赖关系和关联,从而提高了语言翻译、文本生成和情感分析等任务的性能。

多头注意力的重要性 

多头注意力机制具有几个优点:

  • 并行化:通过同时关注输入序列的不同部分,多头注意力显著加快了计算速度,使其比传统的注意力机制更加高效。

  • 增强表示:每个注意力头都关注输入序列的不同方面,使模型能够捕捉各种模式和关系。这导致输入的表示更丰富、更强大,增强了模型理解和生成文本的能力。

  • 改进泛化性:多头注意力使模型能够关注序列内的局部和全局依赖关系,从而提高了跨不同任务和领域的泛化性。

多头注意力的计算:

分解计算多头注意力所涉及的步骤:

  • 线性变换:输入序列经历可学习的线性变换,将其投影到多个较低维度的表示,称为“头”。每个头关注输入的不同方面,使模型能够捕捉各种模式。

  • 缩放点积注意力:每个头独立地计算输入序列的查询、键和值表示之间的注意力分数。这一步涉及计算令牌及其上下文之间的相似度,乘以模型深度的平方根进行缩放。得到的注意力权重突出了每个令牌相对于其他令牌的重要性。

  • 连接和线性投影:来自所有头的注意力输出被连接并线性投影回原始维度。这个过程将来自多个头的见解结合起来,增强了模型理解序列内复杂关系的能力。

代码实现

# 多头注意力的代码实现
class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, num_heads):
        super(MultiHeadAttention, self).__init__()
        self.num_heads = num_heads
        self.d_model = d_model
        assert d_model % num_heads == 0
        self.depth = d_model // num_heads
        
        # 查询、键和值的线性投影
        self.query_linear = nn.Linear(d_model, d_model)
        self.key_linear = nn.Linear(d_model, d_model)
        self.value_linear = nn.Linear(d_model, d_model)
        
        # 输出线性投影
        self.output_linear = nn.Linear(d_model, d_model)
    
    def split_heads(self, x):
      batch_size, seq_length, d_model = x.size()
      return x.view(batch_size, seq_length, self.num_heads, self.depth).transpose(1, 2)
    
    def forward(self, query, key, value, mask=None):
        
        # 线性投影
        query = self.query_linear(query)
        key = self.key_linear(key)
        value = self.value_linear(value)
        
        # 分割头部
        query = self.split_heads(query)
        key = self.split_heads(key)
        value = self.split_heads(value)
        
        # 缩放点积注意力
        scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(self.depth)
        
        # 如果提供了掩码,则应用掩码
        if mask is not None:
            scores += scores.masked_fill(mask == 0, -1e9)
        
        # 计算注意力权重并应用softmax
        attention_weights = torch.softmax(scores, dim=-1)
        
        # 应用注意力到值
        attention_output = torch.matmul(attention_weights, value)
        
        # 合并头部
        batch_size, _, seq_length, d_k = attention_output.size()
        attention_output = attention_output.transpose(1, 2).contiguous().view(batch_size,
        seq_length, self.d_model)
        
        # 线性投影
        attention_output = self.output_linear(attention_output)
        
        return attention_output

# 示例用法
d_model = 512
max_len = 100
num_heads = 8
d_ff = 2048

# 多头注意力
multihead_attn = MultiHeadAttention(d_model, num_heads)

# 示例输入序列
input_sequence = torch.randn(5, max_len, d_model)

# 多头注意力
attention_output= multihead_attn(input_sequence, input_sequence, input_sequence)
print("attention_output shape:", attention_output.shape)

4.3、前馈网络

在Transformer的背景下,前馈网络在处理信息和从输入序列中提取特征方面发挥着关键作用。它们是模型的支柱,促进了不同层之间表示的转换。

前馈网络的作用

每个Transformer层内的前馈网络负责对输入表示应用非线性变换。它使模型能够捕捉数据中的复杂模式和关系,促进了高级特征的学习。

前馈层的结构和功能

前馈层由两个线性变换组成,两者之间通过一个非线性激活函数(通常是ReLU)分隔。让我们来解析一下结构和功能:

  • 线性变换1:使用可学习的权重矩阵将输入表示投影到更高维度的空间中。

  • 非线性激活:第一个线性变换的输出通过非线性激活函数(例如ReLU)传递。这引入了模型的非线性,使其能够捕捉数据中的复杂模式和关系。

  • 线性变换2:激活函数的输出然后通过另一个可学习的权重矩阵投影回原始的维度空间中。

代码实现

# 前馈网络的代码实现
class FeedForward(nn.Module):
    def __init__(self, d_model, d_ff):
        super(FeedForward, self).__init__()
        self.linear1 = nn.Linear(d_model, d_ff)
        self.linear2 = nn.Linear(d_ff, d_model)
        self.relu = nn.ReLU()

    def forward(self, x):
        # 线性变换1
        x = self.relu(self.linear1(x))
        
        # 线性变换2
        x = self.linear2(x)
        
        return x

# 示例用法
d_model = 512
max_len = 100
num_heads = 8
d_ff = 2048

# 多头注意力
multihead_attn = MultiHeadAttention(d_model, num_heads)

# 前馈网络
ff_network = FeedForward(d_model, d_ff)

# 示例输入序列
input_sequence = torch.randn(5, max_len, d_model)

# 多头注意力
attention_output= multihead_attn(input_sequence, input_sequence, input_sequence)

# 前馈网络
output_ff = ff_network(attention_output)
print('input_sequence',input_sequence.shape)
print("output_ff", output_ff.shape)

4.4、编码器 

在Transformer模型中起着至关重要的作用,其主要任务是将输入序列转换为有意义的表示,捕捉输入的重要信息。

每个编码器层的结构和功能

编码器由多个层组成,每个层依次包含以下组件:输入嵌入、位置编码、多头自注意力机制和位置逐点前馈网络。

  1. 输入嵌入:我们首先将输入序列转换为密集向量表示,称为输入嵌入。我们使用预训练的词嵌入或在训练过程中学习的嵌入,将输入序列中的每个单词映射到高维向量空间中。

  2. 位置编码:我们将位置编码添加到输入嵌入中,以将输入序列的顺序信息合并到其中。这使得模型能够区分序列中单词的位置,克服了传统神经网络中缺乏顺序信息的问题。

  3. 多头自注意力机制:在位置编码之后,输入嵌入通过一个多头自注意力机制。这个机制使编码器能够根据单词之间的关系权衡输入序列中不同单词的重要性。通过关注输入序列的相关部分,编码器可以捕捉长距离的依赖关系和语义关系。

  4. 位置逐点前馈网络:在自注意力机制之后,编码器对每个位置独立地应用位置逐点前馈网络。这个网络由两个线性变换组成,两者之间通过一个非线性激活函数(通常是ReLU)分隔。它有助于捕捉输入序列中的复杂模式和关系。

代码实现

# 编码器的代码实现
class EncoderLayer(nn.Module):
    def __init__(self, d_model, num_heads, d_ff, dropout):
        super(EncoderLayer, self).__init__()
        self.self_attention = MultiHeadAttention(d_model, num_heads)
        self.feed_forward = FeedForward(d_model, d_ff)
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, x, mask):
        
        # 自注意力层
        attention_output= self.self_attention(x, x,
        x, mask)
        attention_output = self.dropout(attention_output)
        x = x + attention_output
        x = self.norm1(x)
        
        # 前馈层
        feed_forward_output = self.feed_forward(x)
        feed_forward_output = self.dropout(feed_forward_output)
        x = x + feed_forward_output
        x = self.norm2(x)
        
        return x

d_model = 512
max_len = 100
num_heads = 8
d_ff = 2048


# 多头注意力
encoder_layer = EncoderLayer(d_model, num_heads, d_ff, 0.1)

# 示例输入序列
input_sequence = torch.randn(1, max_len, d_model)

# 多头注意力
encoder_output= encoder_layer(input_sequence, None)
print("encoder output shape:", encoder_output.shape)

4.5、解码器 

在Transformer模型中,解码器在基于输入序列的编码表示生成输出序列方面起着至关重要的作用。它接收来自编码器的编码输入序列,并将其用于生成最终的输出序列。

解码器的功能

解码器的主要功能是生成输出序列,同时注意到输入序列的相关部分和先前生成的标记。它利用输入序列的编码表示来理解上下文,并对生成下一个标记做出明智的决策。

解码器层及其组件

解码器层包括以下组件:

  1. 输出嵌入右移:在处理输入序列之前,模型将输出嵌入向右移动一个位置。这确保解码器中的每个标记在训练期间都能从先前生成的标记接收到正确的上下文。

  2. 位置编码:与编码器类似,模型将位置编码添加到输出嵌入中,以合并标记的顺序信息。这种编码帮助解码器根据标记在序列中的位置进行区分。

  3. 掩码的多头自注意力机制:解码器采用掩码的多头自注意力机制,以便注意输入序列的相关部分和先前生成的标记。在训练期间,模型应用掩码以防止注意到未来的标记,确保每个标记只能注意到前面的标记。

  4. 编码器-解码器注意力机制:除了掩码的自注意力机制外,解码器还包括编码器-解码器注意力机制。这种机制使解码器能够注意到输入序列的相关部分,有助于生成受输入上下文影响的输出标记。

  5. 位置逐点前馈网络:在注意力机制之后,解码器对每个标记独立地应用位置逐点前馈网络。这个网络捕捉输入和先前生成的标记中的复杂模式和关系,有助于生成准确的输出序列。

代码实现

# 解码器的代码实现
class DecoderLayer(nn.Module):
    def __init__(self, d_model, num_heads, d_ff, dropout):
        super(DecoderLayer, self).__init__()
        self.masked_self_attention = MultiHeadAttention(d_model, num_heads)
        self.enc_dec_attention = MultiHeadAttention(d_model, num_heads)
        self.feed_forward = FeedForward(d_model, d_ff)
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.norm3 = nn.LayerNorm(d_model)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x, encoder_output, src_mask, tgt_mask):
        
        # 掩码的自注意力层
        self_attention_output= self.masked_self_attention(x, x, x, tgt_mask)
        self_attention_output = self.dropout(self_attention_output)
        x = x + self_attention_output
        x = self.norm1(x)
        
        # 编码器-解码器注意力层
        enc_dec_attention_output= self.enc_dec_attention(x, encoder_output, 
        encoder_output, src_mask)
        enc_dec_attention_output = self.dropout(enc_dec_attention_output)
        x = x + enc_dec_attention_output
        x = self.norm2(x)
        
        # 前馈层
        feed_forward_output = self.feed_forward(x)
        feed_forward_output = self.dropout(feed_forward_output)
        x = x + feed_forward_output
        x = self.norm3(x)
        
        return x

# 定义DecoderLayer的参数
d_model = 512  # 模型的维度
num_heads = 8  # 注意力头的数量
d_ff = 2048    # 前馈网络的维度
dropout = 0.1  # 丢弃概率
batch_size = 1 # 批量大小
max_len = 100  # 序列的最大长度

# 定义DecoderLayer实例
decoder_layer = DecoderLayer(d_model, num_heads, d_ff, dropout)


src_mask = torch.rand(batch_size, max_len, max_len) > 0.5
tgt_mask = torch.tril(torch.ones(max_len, max_len)).unsqueeze(0) == 0

# 将输入张量传递到DecoderLayer
output = decoder_layer(input_sequence, encoder_output, src_mask, tgt_mask)

# 输出形状
print("Output shape:", output.shape)

5、Transformer 模型架构

Transformer模型概述

在其核心,Transformer模型由编码器和解码器模块堆叠在一起,用于处理输入序列并生成输出序列。以下是架构的高级概述

编码器

  • 编码器模块处理输入序列,提取特征并创建输入的丰富表示。

  • 它由多个编码器层组成,每个层包含自注意力机制和前馈网络。

  • 自注意力机制允许模型同时关注输入序列的不同部分,捕捉依赖关系和关联。

  • 我们将位置编码添加到输入嵌入中,以提供有关序列中标记位置的信息。

解码器

  • 解码器模块以编码器的输出作为输入,并生成输出序列。

  • 与编码器类似,它由多个解码器层组成,每个层包含自注意力、编码器-解码器注意力和前馈网络。

  • 除了自注意力外,解码器还包含编码器-解码器注意力,以在生成输出时关注输入序列。

  • 与编码器类似,我们将位置编码添加到输入嵌入中,以提供位置信息。

连接和标准化

  • 在编码器和解码器模块的每一层之间,都有残差连接后跟层标准化。

  • 这些机制有助于在网络中流动梯度,并有助于稳定训练。

完整的Transformer模型通过将多个编码器和解码器层堆叠在一起来构建。每个层独立处理输入序列,使模型能够学习分层表示并捕获数据中的复杂模式。编码器将其输出传递给解码器,后者根据输入生成最终的输出序列。

Transformer模型的实现

# TRANSFORMER的实现
class Transformer(nn.Module):
    def __init__(self, src_vocab_size, tgt_vocab_size, d_model, num_heads, num_layers, d_ff,
    max_len, dropout):
        super(Transformer, self).__init__()

        # 定义编码器和解码器的词嵌入层
        self.encoder_embedding = nn.Embedding(src_vocab_size, d_model)
        self.decoder_embedding = nn.Embedding(tgt_vocab_size, d_model)

        # 定义位置编码层
        self.positional_encoding = PositionalEncoding(d_model, max_len)

        # 定义编码器和解码器的多层堆叠
        self.encoder_layers = nn.ModuleList([EncoderLayer(d_model, num_heads, d_ff, dropout)
        for _ in range(num_layers)])
        self.decoder_layers = nn.ModuleList([DecoderLayer(d_model, num_heads, d_ff, dropout)
        for _ in range(num_layers)])

        # 定义线性层
        self.linear = nn.Linear(d_model, tgt_vocab_size)
        self.dropout = nn.Dropout(dropout)

    # 生成掩码
    def generate_mask(self, src, tgt):
        src_mask = (src != 0).unsqueeze(1).unsqueeze(2)
        tgt_mask = (tgt != 0).unsqueeze(1).unsqueeze(3)
        seq_length = tgt.size(1)
        nopeak_mask = (1 - torch.triu(torch.ones(1, seq_length, seq_length), diagonal=1)).bool()
        tgt_mask = tgt_mask & nopeak_mask
        return src_mask, tgt_mask

    # 前向传播
    def forward(self, src, tgt):
        src_mask, tgt_mask = self.generate_mask(src, tgt)

        # 编码器输入的词嵌入和位置编码
        encoder_embedding = self.encoder_embedding(src)
        en_positional_encoding = self.positional_encoding(encoder_embedding)
        src_embedded = self.dropout(en_positional_encoding)

        # 解码器输入的词嵌入和位置编码
        decoder_embedding = self.decoder_embedding(tgt)
        de_positional_encoding = self.positional_encoding(decoder_embedding)
        tgt_embedded = self.dropout(de_positional_encoding)

        enc_output = src_embedded
        for enc_layer in self.encoder_layers:
            enc_output = enc_layer(enc_output, src_mask)

        dec_output = tgt_embedded
        for dec_layer in self.decoder_layers:
            dec_output = dec_layer(dec_output, enc_output, src_mask, tgt_mask)

        output = self.linear(dec_output)
        return output

# 示例用法
src_vocab_size = 5000
tgt_vocab_size = 5000
d_model = 512
num_heads = 8
num_layers = 6
d_ff = 2048
max_len = 100
dropout = 0.1

transformer = Transformer(src_vocab_size, tgt_vocab_size, d_model, num_heads, num_layers, 
d_ff, max_len, dropout)

# 生成随机示例数据
src_data = torch.randint(1, src_vocab_size, (5, max_len))  # (batch_size, seq_length)
tgt_data = torch.randint(1, tgt_vocab_size, (5, max_len))  # (batch_size, seq_length)
transformer(src_data, tgt_data[:, :-1]).shape

 6、模型的训练与评估

训练Transformer模型涉及优化其参数以最小化损失函数,通常使用梯度下降和反向传播。一旦训练完成,就会使用各种指标评估模型的性能,以评估其解决目标任务的有效性。

训练过程

梯度下降和反向传播:

  • 在训练期间,将输入序列输入模型,并生成输出序列。

  • 将模型的预测与地面真相进行比较,涉及使用损失函数(例如交叉熵损失)来衡量预测值与实际值之间的差异。

  • 梯度下降用于更新模型的参数,使损失最小化的方向。

  • 优化器根据这些梯度调整参数,迭代更新它们以提高模型性能。

学习率调度:

  • 可以应用学习率调度技术来动态调整训练期间的学习率。

  • 常见策略包括热身计划,其中学习率从低开始逐渐增加,以及衰减计划,其中学习率随时间降低。

评估指标

困惑度:

  • 困惑度是用于评估语言模型性能的常见指标,包括Transformer。

  • 它衡量模型对给定标记序列的预测能力。

  • 较低的困惑度值表示更好的性能,理想值接近词汇量大小。

BLEU分数:

  • BLEU(双语评估研究)分数通常用于评估机器翻译文本的质量。

  • 它将生成的翻译与一个或多个由人类翻译人员提供的参考翻译进行比较。

  • BLEU分数范围从0到1,较高的分数表示更好的翻译质量。

7、训练和评估的实现

# Transformer 模型的训练和评估
criterion = nn.CrossEntropyLoss(ignore_index=0)
optimizer = optim.Adam(transformer.parameters(), lr=0.0001, betas=(0.9, 0.98), eps=1e-9)

# 训练循环
transformer.train()

for epoch in range(10):
    optimizer.zero_grad()
    output = transformer(src_data, tgt_data[:, :-1])
    loss = criterion(output.contiguous().view(-1, tgt_vocab_size), tgt_data[:, 1:]
    .contiguous().view(-1))
    loss.backward()
    optimizer.step()
    print(f"第 {epoch+1} 轮:损失= {loss.item():.4f}")


# 虚拟数据
src_data = torch.randint(1, src_vocab_size, (5, max_len))  # (batch_size, seq_length)
tgt_data = torch.randint(1, tgt_vocab_size, (5, max_len))  # (batch_size, seq_length)

# 评估循环
transformer.eval()
with torch.no_grad():
    output = transformer(src_data, tgt_data[:, :-1])
    loss = criterion(output.contiguous().view(-1, tgt_vocab_size), tgt_data[:, 1:]
    .contiguous().view(-1))
    print(f"\n虚拟数据的评估损失= {loss.item():.4f}")

 总结

 本周通过阅读姿态估计相关论文,对姿态估计的知识有了进一步的学习,同时对transformer的学习有了更深的立即

 


网站公告

今日签到

点亮在社区的每一天
去签到