首页
图床推荐
实用链接
友链
关于
Search
1
彻底卸载Cloudflare Tunnel(解决 cloudflared service uninstall 报错问题)
385 阅读
2
从零开始注册Hugging Face账号到部署网页应用
91 阅读
3
Debian 11.2 搭建 Typecho 个人博客教程
83 阅读
4
Linux配置frps与frpc的四种隧道并设置开机启动
77 阅读
5
紫电猫8.8元随身WIFI刷Debian系统教程
73 阅读
默认分类
教程
随笔
软件开发
笔记
登录
/
注册
Search
标签搜索
Datawhale
AI+X
Fun-Transformer
#Datewhale组队学习
隧道
Debian
Transformer
教程
随身wifi
frp
frpc
frps
内网穿透
Linux
toml
我的世界
Minecraft
MySQL
单片机
OLED
Simuoss
累计撰写
17
篇文章
累计收到
8
条评论
首页
栏目
默认分类
教程
随笔
软件开发
笔记
页面
图床推荐
实用链接
友链
关于
搜索到
5
篇与
的结果
2025-01-28
【2025.1 Datawhale AI+X 共学活动】Fun-Transformer —— Task5:项目实践(手写Transformer)
Datewhale组队学习从零实现注意力机制:NumPy 和 SciPy 的实践之旅注意力机制,作为现代深度学习中的核心组件之一,早已在自然语言处理、计算机视觉等领域大放异彩。它的魅力在于能够动态地聚焦于输入数据的关键部分,从而让模型更“聪明”地处理信息。今天,我们将抛开复杂的框架,仅用 NumPy 和 SciPy,从零实现一个通用的注意力机制,深入理解其背后的数学逻辑。1. 词向量:将词汇语义嵌入高维空间一切从词向量开始。词向量是单词在嵌入空间中的数学表示,它将离散的符号转化为连续的向量,从而能够进行数学运算。这里,定义了四个单词的词向量,每个向量的维度为3:import numpy as np from scipy.special import softmax word_1 = np.array([1, 0, 0]) word_2 = np.array([0, 1, 0]) word_3 = np.array([1, 1, 0]) word_4 = np.array([0, 0, 1]) words = np.array([word_1, word_2, word_3, word_4])这些词向量被堆叠成一个矩阵 words,形状为 (4, 3),每一行代表一个单词的嵌入。这一步的意义在于,将离散的符号转化为可计算的数学对象,为后续的注意力计算奠定基础。2. 权重矩阵:映射到查询、键和值注意力机制的核心在于查询(Query)、键(Key)和值(Value)的生成。这些概念听起来抽象,但本质上是将词向量映射到不同的空间中,以便计算它们之间的关系。通过随机初始化三个权重矩阵 W_Q、W_K 和 W_V,词向量被转换为查询、键和值:np.random.seed(42) W_Q = np.random.randint(3, size=(3, 3)) W_K = np.random.randint(3, size=(3, 3)) W_V = np.random.randint(3, size=(3, 3)) Q = words @ W_Q K = words @ W_K V = words @ W_V查询、键和值的生成过程,本质上是矩阵乘法。这一步的意义在于,将原始的词向量映射到不同的语义空间中,为后续的相似度计算做准备。3. 得分计算:量化相似度接下来,通过查询和键的点积,计算得分矩阵。每个得分表示一个查询向量与一个键向量的相似度:scores = Q @ K.T得分的计算是注意力机制的关键步骤之一。点积越大,表示两个向量越相似。这一步的意义在于,量化每个查询向量与所有键向量之间的关系,为后续的权重分配提供依据。4. 权重分配:Softmax 概率化得分矩阵虽然量化了相似度,但还需要将其转化为概率分布。这里,softmax 函数登场了。通过对得分进行缩放并应用 softmax,得到了注意力权重:weights = softmax(scores / np.sqrt(K.shape[1]), axis=1)softmax 的作用是将得分转化为概率分布,使得每个查询向量对所有键向量的权重之和为1。缩放操作则是为了保持数值稳定性,防止得分过大或过小导致计算溢出或下溢。5. 注意力输出:加权求和最后,通过加权求和的方式,计算注意力输出。每个注意力输出是所有值向量的加权和,权重由 softmax 函数计算得到:attention = weights @ V print(attention)注意力输出的计算是注意力机制的最终步骤。通过加权求和,模型能够动态地聚焦于输入序列中最相关的部分,从而生成更准确的输出。多头注意力机制:从单头到多头的进化注意力机制的核心思想是让模型能够动态地关注输入序列中的不同部分,从而更好地捕捉上下文信息。然而,单头注意力机制有一个明显的局限性:它只能从一个角度捕捉输入序列的信息。为了克服这一限制,多头注意力机制(Multi-Head Attention)应运而生。通过并行地计算多个注意力头,模型能够从不同的子空间中提取信息,从而更全面地理解输入数据。今天,我们将深入探讨多头注意力机制的实现,并通过 PyTorch 实现一个完整的 MultiHeadAttention 类。1. 多头注意力机制的核心思想多头注意力机制的核心在于将输入数据映射到多个子空间中,并在每个子空间中独立地计算注意力。具体来说,输入数据会被线性变换为多个查询(Query)、键(Key)和值(Value),然后在每个头上分别计算注意力。最后,所有头的输出会被拼接起来,并通过一个线性层映射回原始维度。这种设计的好处在于:多视角捕捉信息:每个头可以关注输入序列的不同部分,从而捕捉更丰富的特征。并行计算:多个头的计算可以并行进行,提高了计算效率。灵活性:通过调整头的数量,可以控制模型的复杂度和表达能力。2. 实现细节:从代码中理解多头注意力以下是一个完整的 MultiHeadAttention 类的实现,我们将逐行解析其关键部分。import math import torch import torch.nn as nn import torch.nn.functional as F class MultiHeadAttention(nn.Module): def __init__(self, heads, d_model, dropout=0.1): super().__init__() self.d_model = d_model # 模型的总维度 self.d_k = d_model // heads # 每个头的维度 self.h = heads # 头的数量 # 线性层,用于生成 Q、K、V self.q_linear = nn.Linear(d_model, d_model) self.k_linear = nn.Linear(d_model, d_model) self.v_linear = nn.Linear(d_model, d_model) # Dropout 层,用于正则化 self.dropout = nn.Dropout(dropout) # 输出线性层,用于将多头输出映射回模型维度 self.out = nn.Linear(d_model, d_model) def attention(self, q, k, v, mask=None): # 计算 Q 和 K 的点积,并除以 sqrt(d_k) 进行缩放 scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.d_k) # 如果提供了掩码,则将掩码对应的位置设置为负无穷 if mask is not None: scores = scores.masked_fill(mask == 0, -1e9) # 应用 softmax 计算注意力权重 scores = F.softmax(scores, dim=-1) # 应用 dropout scores = self.dropout(scores) # 将注意力权重与 V 相乘,得到输出 output = torch.matmul(scores, v) return output def forward(self, q, k, v, mask=None): batch_size = q.size(0) # 将输入通过线性层,并调整形状以进行多头计算 q = self.q_linear(q).view(batch_size, -1, self.h, self.d_k).transpose(1, 2) k = self.k_linear(k).view(batch_size, -1, self.h, self.d_k).transpose(1, 2) v = self.v_linear(v).view(batch_size, -1, self.h, self.d_k).transpose(1, 2) # 计算注意力输出 scores = self.attention(q, k, v, mask) # 将多头输出拼接,并调整形状以匹配模型维度 concat = scores.transpose(1, 2).contiguous().view(batch_size, -1, self.d_model) # 通过输出线性层 output = self.out(concat) return output3. 关键步骤解析1. 初始化参数d_model:模型的总维度。d_k:每个头的维度,等于 d_model // heads。heads:头的数量。线性层 q_linear、k_linear、v_linear:用于将输入映射到查询、键和值。out:输出线性层,用于将多头输出映射回模型维度。2. 注意力计算点积计算:通过矩阵乘法计算查询和键的点积,并除以 sqrt(d_k) 进行缩放,以防止点积过大。掩码处理:如果提供了掩码,则将掩码对应的位置设置为负无穷,这样在 softmax 后这些位置的值为0。Softmax:将得分转换为概率分布。Dropout:对注意力权重进行随机丢弃,防止过拟合。加权求和:将注意力权重与值相乘,得到输出。3. 前向传播线性变换:将输入通过线性层,生成查询、键和值。形状调整:将输入调整为 (batch_size, heads, seq_len, d_k),以便进行多头计算。注意力计算:在每个头上独立计算注意力。拼接输出:将所有头的输出拼接起来,并通过输出线性层映射回模型维度。4. 测试与验证以下代码用于测试 MultiHeadAttention 类的功能:if __name__ == "__main__": heads = 4 d_model = 128 # d_model 必须是 heads 的整数倍 dropout = 0.1 model = MultiHeadAttention(heads, d_model, dropout) batch_size = 2 seq_len = 5 q = torch.rand(batch_size, seq_len, d_model) # Query k = torch.rand(batch_size, seq_len, d_model) # Key v = torch.rand(batch_size, seq_len, d_model) # Value output = model(q, k, v) print("Output shape:", output.shape) # 应该是 [batch_size, seq_len, d_model] loss = output.mean() loss.backward() print("Backward pass completed.")输出结果:Output shape: torch.Size([2, 5, 128]) Backward pass completed.欢迎各位大佬在评论区交流&批评指正!Datewhale组队学习参考链接Datawhale AI+X 共学活动 Fun-Transformer —— Task5:项目实践Attention Is All You Need
2025年01月28日
2 阅读
0 评论
0 点赞
2025-01-25
【2025.1 Datawhale AI+X 共学活动】Fun-Transformer —— Task4:Decoder
Datewhale组队学习*即将到来欢迎各位大佬在评论区交流&批评指正!Datewhale组队学习参考链接Datawhale AI+X 共学活动 Fun-Transformer —— Task4:DecoderAttention Is All You Need
2025年01月25日
2 阅读
0 评论
0 点赞
2025-01-21
【2025.1 Datawhale AI+X 共学活动】Fun-Transformer —— Task3:Encoder
Datewhale组队学习1. 编码器(Encoder)1.1 Encoder 工作流程输入阶段初始输入:输入数据(如文本中的单词)首先通过嵌入层(Input Embedding)转换为向量表示。为了捕捉序列中元素的位置信息,位置编码(Position Embedding)被添加到嵌入向量中。位置编码可以是固定的(如正弦余弦函数)或可学习的。第一个Encoder子模块接收嵌入和位置编码的组合输入。后续 Encoder 输入:除了第一个Encoder子模块外,其他Encoder子模块从前一个Encoder接收输入,形成链式结构。核心处理阶段多头自注意力层(Multi-Head Self-Attention):输入序列通过线性变换生成查询(Q)、键(K)、值(V)向量。通过缩放点积注意力(Scaled Dot-Product Attention)计算序列中不同位置之间的关联关系。注意力分数通过softmax归一化,加权求和后得到自注意力输出。前馈层(Feedforward Layer):自注意力层的输出经过一个全连接网络(通常包含两层线性变换和ReLU激活函数),提取更高层次的特征。前馈层的输出传递给下一个Encoder子模块(如果不是最后一个Encoder)。残差与归一化阶段残差连接(Residual Connection):自注意力层和前馈层的输入与输出通过残差连接相加(如输入为 $x$,输出为 $y$,则结果为 $x + y$)。残差连接缓解了深层网络中的梯度消失问题,使模型更容易训练。层归一化(Layer Normalization):在残差连接后,对输出进行层归一化,加速模型收敛并提高泛化能力。归一化操作对每一层的神经元输入进行标准化处理,使其均值为0,方差为1。1.2 Encoder 组成成分每个Encoder子模块包含以下部分:多头自注意力层(Multi-Head Self-Attention):捕捉序列内部关系。残差连接(Residual Connection):缓解梯度消失问题。层归一化(Layer Normalization):加速训练并提高稳定性。前馈全连接网络(Position-wise Feed-Forward Networks):对特征进行非线性变换。2. 多头自注意力(Multi-Head Self-Attention)2.1 缩放点积注意力(Scaled Dot-Product Attention)输入处理:输入序列通过线性变换生成查询(Q)、键(K)、值(V)向量:$$ Q = W_Q \cdot x, \quad K = W_K \cdot x, \quad V = W_V \cdot x $$其中,$W_Q$、$W_K$、$W_V$ 是可学习的权重矩阵。注意力分数计算:通过点积计算查询和键之间的相似度:$$ \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V $$缩放因子 $\sqrt{d_k}$ 防止点积值过大,避免softmax进入饱和区。Mask机制:在训练时,mask用于遮蔽未来信息(如解码器中);在推理时,mask用于控制输出序列的生成。2.2 多头注意力机制(Multi-Head Attention)多头计算:输入序列被映射到多个子空间(头),每个头独立计算注意力:$$ \text{head}_i = \text{Attention}(QW_i^Q, KW_i^K, VW_i^V) $$所有头的输出拼接后通过线性变换得到最终输出:$$ \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \dots, \text{head}_h)W_O $$优点:并行化:多个头可以并行计算,提升效率。多样性:每个头关注输入序列的不同方面,增强模型表示能力。泛化性:捕捉局部和全局依赖关系,提高模型泛化能力。2.3 自注意力机制(Self-Attention)工作原理:查询(Q)、键(K)、值(V)均来自同一输入序列。通过自注意力机制,模型能够捕捉序列内部元素之间的关系(如句子中单词的语义关联)。优点:参数效率:参数数量较少,计算复杂度为 $O(n^2d)$。全局信息:一步计算即可捕捉序列的全局依赖关系。并行化:不依赖序列顺序,适合并行计算。缺点:计算量大:序列较长时,计算复杂度较高。位置信息缺失:自注意力机制本身无法捕捉序列顺序,需通过位置编码补充。3. 交叉注意力(Cross Attention)3.1 简述概念:交叉注意力允许一个序列(查询序列)关注另一个序列(键-值序列),建立两者之间的联系。序列维度要求:查询序列和键-值序列的维度必须相同。应用场景:多模态任务(如文本与图像对齐)、序列到序列模型(如机器翻译中的编码器-解码器交互)。3.2 操作查询序列(Q):定义输出的序列长度。键-值序列(K, V):提供输入信息,用于计算与查询序列的相似度。4. Cross Attention 和 Self Attention 的区别Self Attention:查询、键、值来自同一序列。用于捕捉序列内部关系(如句子中单词的语义关联)。适用于序列内部依赖建模。Cross Attention:查询来自一个序列,键和值来自另一个序列。用于跨序列信息交互(如编码器-解码器之间的信息传递)。适用于多模态任务和序列到序列模型。5. 前馈全连接网络(Position-wise Feed-Forward Networks)结构:包含两个全连接层和一个ReLU激活函数:$$ \text{FFN}(x) = \max(0, xW_1 + b_1)W_2 + b_2 $$第一层将输入维度从 $d_{model}$ 扩展到 $d_{ff}$(如2048),第二层将其还原为 $d_{model}$。作用:增强模型的非线性表达能力。对自注意力层输出的特征进行进一步变换。6. Add & Norm残差连接(Add):输入与子层输出相加,缓解梯度消失问题。层归一化(Norm):对残差连接的输出进行归一化,加速训练并提高稳定性。总结Encoder:通过多头自注意力和前馈层处理输入序列,残差连接和层归一化提高训练稳定性。多头自注意力:通过多个注意力头并行计算,增强模型表示能力。自注意力:捕捉序列内部关系,适用于序列内部依赖建模。交叉注意力:用于不同序列之间的信息交互,适用于多模态任务。前馈网络:增强模型的非线性表达能力。Add & Norm:提高训练稳定性和收敛速度。欢迎各位大佬在评论区交流&批评指正!Datewhale组队学习参考链接Datawhale AI+X 共学活动 Fun-Transformer —— Task3:EncoderAttention Is All You Need
2025年01月21日
6 阅读
0 评论
0 点赞
2025-01-18
【2025.1 Datawhale AI+X 共学活动】Fun-Transformer —— Task2:Transformer
Datewhale组队学习Transformer结构概述1. Attention 机制1.1 引入Attention机制 Attention机制最初是为了改进基于Encoder-Decoder的神经机器翻译系统而提出的,解决了RNN/LSTM在处理长序列时的长程依赖问题。传统的Encoder-Decoder模型通过编码器生成一个固定长度的上下文向量,解码器基于该向量生成输出。然而,这种方法在处理长句子时效果不佳,因为编码器难以有效总结长序列信息。Bahdanau等人(2015)提出了Attention机制,允许模型在生成每个输出时动态关注输入序列的不同部分,从而更好地捕捉长距离依赖关系。1.2 Attention机制的工作原理 Attention机制的核心是“加权求和”。它通过以下步骤实现:分解输入:将输入序列分解为单个元素(如单词)。分配重要性:根据每个元素与当前任务的关联性,分配一个权重。加权求和:根据权重对输入元素进行加权求和,生成上下文向量。Attention机制的优势在于它能够动态地为输入序列中的不同部分分配不同的权重,从而更好地捕捉序列中的关键信息。1.3 全局注意力与局部注意力全局注意力:考虑所有输入元素的隐藏状态,计算复杂度较高,适合处理较短的序列。局部注意力:只考虑输入序列的一部分,减少了计算量,适合处理较长的序列。局部注意力通过预测对齐位置和定义注意力窗口来实现。2. Transformer模型2.1 Transformer模型架构 Transformer模型由编码器和解码器组成,每个编码器和解码器层包含自注意力机制和前馈网络。Transformer通过自注意力机制捕捉序列中的全局依赖关系,并通过位置编码引入序列的顺序信息。2.2 Transformer的工作流程 Transformer通过自注意力机制为每个输入元素生成多个注意力分数,从而捕捉序列中不同元素之间的关系。例如,在处理代词“it”时,模型可以通过自注意力机制确定“it”指代的具体对象。2.3 Transformer的发展历程 Transformer模型自2017年提出以来,迅速成为自然语言处理领域的主流模型。后续的BERT、GPT等模型基于Transformer架构,进一步推动了NLP技术的发展。2.4 Transformer对seq2seq模型的影响摒弃RNN结构:Transformer完全摒弃了RNN结构,使用自注意力机制处理序列数据,提高了并行计算能力。引入自注意力机制:自注意力机制允许模型在处理每个序列元素时考虑到序列中所有其他元素的信息,有效捕捉长距离依赖关系。位置编码:由于Transformer没有递归结构,位置编码被引入以保留序列的顺序信息。训练效率提升:Transformer的并行化处理显著提高了训练效率,能够处理更长的序列。2.5 迁移学习与预训练模型 Transformer的成功推动了预训练模型的发展,如BERT、GPT等。这些模型通过在大规模语料上进行预训练,然后在特定任务上进行微调,显著提升了NLP任务的性能。3. Transformer vs CNN vs RNN计算复杂度:Transformer的自注意力机制计算复杂度为O(n²d),RNN为O(nd²),CNN为O(knd²)。当序列长度n小于维度d时,Transformer的计算效率更高。并行操作数量:Transformer和CNN的并行度较高,而RNN由于序列依赖关系,难以并行化。最长计算路径:Transformer的最长计算路径为O(1),而RNN为O(n),CNN为O(n/k)。4. 输入嵌入与位置编码4.1 词嵌入(Word Embedding) 词嵌入通过将单词映射到低维向量空间,捕捉单词的语义信息。可以使用预训练的词嵌入(如Word2Vec、GloVe)或随机初始化的词嵌入。4.2 位置编码(Position Embedding) 由于Transformer没有递归结构,位置编码被引入以保留序列的顺序信息。位置编码通过正弦和余弦函数生成,能够捕捉序列中元素的相对位置关系。词向量生成过程总结词向量是将自然语言中的词语转化为计算机可理解的数值向量表示的过程。词向量的生成方法主要分为两类:基于统计的方法和基于神经网络的方法。以下是词向量生成过程的详细总结:一、引言词向量的核心目标是将自然语言中的词语转化为计算机可处理的数值向量。词向量的生成方法主要包括:独热编码(One-hot Encoding):稀疏表示,每个词用一个高维向量表示,向量中只有一个位置为1,其余为0。分布式表征(Distributed Representation):稠密表示,每个词用一个低维稠密向量表示,向量中的每个元素都是实数。分布式表征能够捕捉词语之间的语义关系。Word2Vec 是生成分布式表征词向量的主流方法之一,其他方法包括 LSA、PLSA、LDA 等。二、基于统计的词向量生成方法(一)词袋模型(Bag-of-Words, BoW)原理:将文本视为词的集合,忽略词序和语法结构。每个文本表示为一个向量,向量的每个元素对应词汇表中的一个词,值为该词在文本中出现的次数。构建过程:构建词汇表:统计语料库中所有不重复的词。对每个文本,统计词汇表中每个词的出现次数,生成向量。缺点:忽略词序和语义信息。向量维度高,计算成本大。(二)TF-IDF(词频-逆文档频率)原理:在词袋模型的基础上,引入词的重要性权重。TF(词频):词在文本中出现的频率。IDF(逆文档频率):词在整个语料库中的稀有程度。TF-IDF = TF × IDF。构建过程:计算每个词的 TF 和 IDF。将 TF 和 IDF 相乘,得到每个词的 TF-IDF 值。优点:能够体现词在文本中的重要性。缺点:仍然无法捕捉语义信息。对常见词(如“的”“是”)的权重可能过高。三、基于神经网络的词向量生成方法(一)Word2Vec原理:通过神经网络学习词的分布式表示。两种主要架构:CBOW(Continuous Bag-of-Words):根据上下文预测中心词。Skip-Gram:根据中心词预测上下文。训练过程:CBOW:输入:上下文词的 one-hot 编码。输出:预测中心词的概率分布。Skip-Gram:输入:中心词的 one-hot 编码。输出:预测上下文词的概率分布。通过反向传播算法优化模型参数。优点:能够捕捉词的语义信息。支持词向量的语义运算(如“国王 - 男人 + 女人 ≈ 女王”)。(二)GloVe(Global Vectors for Word Representation)原理:结合统计方法和神经网络方法,基于词的共现矩阵学习词向量。训练过程:构建词的共现矩阵。定义目标函数,最小化预测共现概率与实际共现概率的差异。通过优化算法(如随机梯度下降)更新词向量。优点:有效利用语料库的统计信息。训练速度快,适合大规模语料库。四、词向量的完整生成过程(一)数据收集与预处理阶段收集语料库:获取大量文本数据(如新闻、论文、社交媒体等)。文本清洗:去除噪声(如 HTML 标签、特殊字符)。分词:对中文等语言进行分词。构建词汇表:统计语料库中所有不重复的词,并为每个词分配唯一索引。(二)模型选择与架构设计阶段基于统计的模型:词袋模型(BoW):简单,忽略词序。TF-IDF:引入词的重要性权重。基于神经网络的模型:Word2Vec:CBOW 和 Skip-Gram 架构。GloVe:基于共现矩阵学习词向量。(三)模型训练阶段定义目标函数:最小化预测结果与真实标签之间的误差。选择优化算法:常用算法:随机梯度下降(SGD)、Adam 等。训练过程监督与调整:监控损失值,调整超参数(如学习率)。(四)词向量生成与评估阶段生成词向量:训练完成后,为词汇表中的每个词生成词向量。评估词向量质量:通过词向量之间的相似度评估语义关系。在下游任务(如文本分类、命名实体识别)中评估词向量的性能。欢迎各位大佬在评论区交流&批评指正!Datewhale组队学习参考链接Datawhale AI+X 共学活动 Fun-Transformer —— Task2:TransformerAttention Is All You Need
2025年01月18日
2 阅读
0 评论
0 点赞
2025-01-15
【2025.1 Datawhale AI+X 共学活动】Fun-Transformer —— Task1:引言 (Seq2Seq、Encoder-Decoder、Attention机制)
Datewhale组队学习为什么我们需要 Seq2Seq ?历史背景首先我们要知道,在Seq2Seq出现之前,我们是如何处理输入输出数据的在 Seq2Seq 框架提出之前,深度神经网络在图像分类等问题上取得了非常好的效果。在其擅长解决的问题中,输入和输出通常都可以表示为固定长度的向量,如果长度稍有变化,会使用填充(Padding)等操作,其目的是将序列数据转换成固定长度的格式,以便于输入到需要固定长度输入的神经网络中。比如,我们有两个序列是输入数据:[1, 1, 4]和[1, 9, 1, 9]但模型只能处理长度为6的序列那就需要先对两个序列做填充:[1, 1, 4] -> [1, 1, 4, 0, 0, 0][1, 9, 1, 9] -> [1, 9, 1, 9, 0, 0]才能正确地输入模型如果要类比的话,这种填充操作有些类似以下概念:离心机配平后才能开机(除非你喜欢艺术)PC历史上出现过一种RDRAM内存,必须一次插满偶数个内存槽。如果你只买了一条内存条,还需要再买一条“空内存条”补到主板上,以防止信号反射。(空内存条也不便宜)一把手枪弹匣有10发子弹容量,但规定必须装满才能发射,而你只有3发子弹。所以你找来了7发塑料假子弹先塞进去,再放3发真子弹,这样就能开枪了。填充操作的缺陷这种填充操作当然不是没有代价的。比如第一个序列[1, 1, 4],我明明想处理的是[1, 1, 4],为什么非要输入[1, 1, 4, 0, 0, 0]不可?这样一来,如果我本来就想输入[1, 1, 4, 0, 0, 0],那岂不是这两个完全不同的序列,在模型看起来却完全一致吗?为了解决这个问题,我们还需要引入一个掩码的概念。比如对于[1, 1, 4]:原始序列:[1, 1, 4]填充序列:[1, 1, 4, 0, 0, 0]掩码序列:[1, 1, 1, 0, 0, 0](把原始序列的内容标记为1,后续填充的位置标记为0)这样就解决了[1, 1, 4]和[1, 1, 4, 0, 0, 0]的趋同问题,因为此时他们有不一样的掩码。类比到我们前文手枪的例子:要如何区分枪里到底是10颗真子弹,还是3颗真7颗假呢?我们可以提前告诉枪手,第1、2、3颗是真的,打完就可以停手了。这个“哪些子弹是真子弹”的信息,就是掩码这样的操作逻辑一定程度上合理,但明显可以更合理——为什么一定要填充呢?所以,Seq2Seq模型应运而生。Seq2Seq 的巧妙设计前面提到的填充操作之所以会存在,是因为模型的输入和输出表示为固定长度的向量,我们为了让数据正确进到模型,就需要对输入数据做填充和掩码的预处理。而Seq2Seq模型,即“序列到序列”,提供了一种直接输入任意长度序列,并输出期望序列的方式。模型直接能够读取一个长度可变的向量,无需进行预处理。这是如何实现的呢?其实非常简单:既然传统模型只能接受固定长度向量,那我们只需要在模型中加一步将任意长度的向量都转化为一个固定长度(维度)的向量,再拿这个定长向量去进行后续操作,不就好了吗?甜菜!Seq2Seq 的缺点虽然Seq2Seq的处理方式很巧妙,但是不可否认的是,它也有一些问题没有解决。首先,Seq2Seq模型的本质,还是最终生成了一个固定长度的向量。那这个向量能表达的意思的复杂程度,就严重受限于向量的长度(或者说维度)。如何理解这个问题?比如你和朋友有个约定,用一种特殊的交流方式传递信息。你们都买了一套12色水彩笔,规定不同的颜色搭配有着不同的意思。不过,受限于只有12色,所以你们只能表达出一些简单的意思,比如今天老地方见,或者下课之后一起去打球。你们对这种交流方式极其满意。班里的两个富哥发现了你们的游戏,并决定模仿。他们一人买了一桶128色水彩笔!复杂的颜色可以让他们表达出2024年12月31日,我们开车一起去xx景点跨年,晚上不通宵到6点谁也不许走这种复杂的语义。而你和朋友看着自己的12色水彩,欲哭无泪。另一个问题,Seq2Seq模型具有良好的模块化特性,能够与卷积神经网络(CNNs)、循环神经网络(RNNs)等神经网络架构无缝集成。这本来是好事。但是,由于循环神经网络(RNN)的固有特性,Seq2Seq模型在处理长序列时存在短期记忆限制,难以有效捕获和传递长期依赖性。其实这口锅应该让RNN来背为什么会出现这种问题呢?假设你在阅读一本侦探小说,开头提到了一些重要的信息:“罪犯的外套上有一个红色的线头。”但是在 300 页后,侦探揭开真相时需要用到这个线索。如果你忘了“红色线头”这个细节,可能就无法理解故事的结局。在机器学习中,这个“开头的信息”对应的是序列的早期数据,“故事结局的推理”对应的是模型对后续步骤的预测。如果模型忘了早期的重要信息,预测结果就可能出现错误。而 RNN 是一个时间步接一个时间步地处理序列数据,每次都将当前输入和之前的记忆(隐藏状态)结合起来,然后输出一个新的隐藏状态。这样会出现以下几点问题:信息传递过程的逐步稀释:早期的信息在隐藏状态中一层层传递,像是一瓶颜料滴入流水,逐渐被稀释。如果序列太长,早期信息可能会被完全“冲淡”。梯度消失问题:模型通过反向传播优化参数时,早期时间步的梯度(学习信号)会因为连乘关系而指数级减小,导致模型难以学会捕捉这些早期时间步的信息。隐藏状态容量有限:RNN 的隐藏状态是一个固定大小的向量,比如 256 维,但长序列的信息可能需要更大的容量才能完整存储,这就导致了一种“信息拥挤”的现象。最后,Seq2Seq模型还存在一个暴露偏差(Exposure Bias)问题。在Seq2Seq模型的训练过程中,经常采用“teacher forcing”策略,即在每个时间步提供真实的输出作为解码器的输入。然而,这种训练模式与模型在推理时的自回归生成模式存在不一致性,导致模型在测试时可能无法很好地适应其自身的错误输出。如何理解这句话呢?这需要借助一个理解来比喻Seq2Seq模型的训练过程:训练阶段(Teacher Forcing)就像你在学习开车时,有一位教练坐在副驾驶,一直告诉你前方的道路情况(真实的目标输出),比如“前方是直路”“前方有个右转弯”,你只需要按教练的指令操作方向盘,几乎不会出错。推理阶段(自回归生成)当你独自开车时,突然被蒙上了眼睛(没有真实的目标输出),只能靠自己上一秒的操作和记忆来判断接下来的路该怎么走。如果前一秒稍微偏了方向,后续可能就越走越偏,甚至撞车。Encoder-Decoder模型是什么与Seq2Seq模型的关系Seq2Seq模型,指的是一种“序列到序列”的处理思路。而Encoder-Decoder模型,是实现这种思路的具体方式。如果将Seq2Seq比作“健康的生活需要多吃水果”,那Encoder-Decoder就是:星期水果种类每日摄入量周一苹果 + 猕猴桃200克 + 150克周二香蕉 + 蓝莓150克 + 100克周三橙子 + 草莓200克 + 150克周四葡萄 + 梨150克 + 200克周五芒果 + 火龙果200克 + 150克周六桃子 + 柚子150克 + 200克周日哈密瓜 + 樱桃200克 + 150克编码器Encoder在做什么简单来说,Encoder是一个RNN,里面包含很多堆叠起来的循环单元(如 LSTM 或 GRU)。当我们输入一个序列,比如 “今天吃什么好” ,输入的句子会先被分解为一个序列:输入序列:[今天, 吃, 什么, 好]在实际模型中,每个词会被转换为对应的词向量(embedding),比如:今天 → 向量 [0.5, 0.1, 0.3, ...]吃 → 向量 [0.8, 0.2, 0.4, ...]什么 → 向量 [0.3, 0.7, 0.9, ...]好 → 向量 [0.6, 0.4, 0.2, ...]这些向量是输入到编码器的内容。假设编码器是一个 两层 LSTM,它会一层一层地处理输入。每个时间步的处理过程如下:时间步 1:处理“今天”输入词向量:[今天]初始隐藏状态:随机初始化为 [0, 0, 0, ...]输出隐藏状态:h1 = [0.6, 0.2, 0.1, ...]解释:编码器初步理解了“今天”的信息,并把它存储在隐藏状态 h1 中。时间步 2:处理“吃”输入词向量:[吃]隐藏状态:h1(包含“今天”的信息)输出隐藏状态:h2 = [0.7, 0.3, 0.4, ...]解释:编码器在隐藏状态中同时记录了“今天”和“吃”的信息。时间步 3:处理“什么”输入词向量:[什么]隐藏状态:h2(包含“今天”和“吃”的信息)输出隐藏状态:h3 = [0.8, 0.5, 0.6, ...]解释:编码器进一步整合了“今天、吃、什么”的上下文信息。时间步 4:处理“好”输入词向量:[好]隐藏状态:h3(包含“今天、吃、什么”的信息)输出隐藏状态:h4 = [0.9, 0.7, 0.8, ...]解释:最终隐藏状态 h4 汇总了整个句子的上下文信息。h4,就是 “今天吃什么好” 这句话的 意思,专业一些叫 语义。解码器Decoder又做了什么假设我们在这里是想实现一个中英互译,输入是中文 “今天吃什么好” ,输出是英文翻译。解码器接收上下文向量 h4(编码器输出)并开始生成目标序列 “What should I eat today?”。解码器的生成过程是逐步进行的,每个时间步生成一个单词。这里使用 RNN(如 LSTM 或 GRU)来逐步生成目标序列。在解码器开始生成序列之前,它的参数(权重和偏置)已经被初始化(也就是预先训练好了)。权重(Weights):连接不同神经网络层的参数,它们决定了层与层之间信息的传递方式。决定了解码器如何将输入(上下文向量、前一步的输出)映射到当前的输出。偏置(Biases):在神经网络层中加入的固定值,用于调节激活函数的输出。帮助解码器对模型的输出进行调整,使其能够灵活地生成更符合目标语言规律的翻译。随后,解码器开始基于h4这个语义来生成目标序列(翻译后的英文)时间步 1:生成第一个单词 "What"解码器接收到上下文向量 h4(包含了整个中文句子的理解)作为输入。解码器根据当前输入(h4)和其隐藏状态 h0(由h4初始化来的)预测第一个单词。输出:What(解码器预测的第一个单词)。类比:翻译官开始翻译时,首先从大意(上下文向量)出发,决定翻译的第一个单词。时间步 2:生成第二个单词 "should"解码器将上一步生成的单词 What 作为当前时间步的输入。结合上下文向量 h4 和前一步的隐藏状态,解码器生成第二个单词。输出:should(解码器预测的第二个单词)。类比:翻译官根据翻译的第一个单词(What)和对整句话的理解(h4),生成第二个单词。时间步 3:生成第三个单词 "I"解码器将上一步生成的单词 should 作为当前时间步的输入。根据上下文向量 h4 和前一步的隐藏状态,解码器生成第三个单词。输出:I(解码器预测的第三个单词)。类比:翻译官继续根据上下文和前一步的翻译来生成下一个单词。时间步 4:生成第四个单词 "eat"解码器输入前一个单词 I。结合上下文向量 h4 和前一步的隐藏状态,解码器生成第四个单词。输出:eat(解码器预测的第四个单词)。类比:翻译官根据已翻译的单词 What should I 以及对中文句子的理解(h4)生成下一个单词。时间步 5:生成第五个单词 "today?"解码器输入前一个单词 eat。结合上下文向量 h4 和前一步的隐藏状态,解码器生成第五个单词。输出:today?(解码器预测的第五个单词)。类比:翻译官最终将最后的单词“今天”翻译成英文,并加上疑问符号“?”。Encoder-Decoder 的应用我们前文提到,Encoder-Decoder是Seq2Seq的具体实现。所以,只要是能转化为序列的数据,都可以作为其应用场景。视频、音频、图像、文字,任何可以被编码成序列数据的实体都能够被Encoder-Decoder结构处理。机器翻译、对话机器人、诗词生成、代码补全、文章摘要(文本 – 文本)语音识别(音频 – 文本)图像描述生成(图片 – 文本)图片转视频(图片 - 视频)给视频配音(视频 - 音频)有太多可以想到的应用场景了!Encoder-Decoder 的缺陷其实也就是上面提过的,Seq2Seq模型的最经典的缺点:固定长度的向量,无法表达出复杂的信息。就像12色的水彩笔无法表达2024年12月31日,我们开车一起去xx景点跨年,晚上不通宵到6点谁也不许走一样。这样的操作逻辑一定程度上合理,但明显可以更合理——为什么一定要固定长度呢?这就是Attention机制带来的翻天覆地的大变革了。Attention机制Attention 解决定长向量问题的方法Attention:Seq2Seq,定长的能力是有极限的,我从短暂的人生当中学到一件事,越是固定维度,就越会发现定长向量的能力是有极限的,除非超越定长。Seq2Seq:你到底想说什么。Attention:所以我不做定长辣!Seq2Seq!Attention 模型的特点是 Encoder 不再将整个输入序列编码为固定长度的“向量C” ,而是编码成一个向量(Context vector)的序列(“C1”、“C2”、“C3”)。也就是,原先是将输入数据编码成单个变量(固定长度的向量C),而现在将输入数据编码成一整个数组!数组里面存着任意数量的定长向量,但是数组长度是可变的!这样,在产生每一个输出的时候,都能够做到充分利用输入序列携带的信息。Attention 的核心工作Attention Is All You Need Ура!!!!在传统的 Seq2Seq 模型中,编码器会将整个输入序列压缩成一个单一的 上下文向量,这个向量包含了输入序列的全部信息。解码器使用这个上下文向量生成目标序列。然而,上下文向量在编码长序列时存在信息丢失的问题,因为它只能携带一个固定长度的摘要,随着输入长度的增加,信息会逐渐被压缩,导致模型难以捕获远距离的依赖关系。Attention 机制的引入解决了这一问题,它允许解码器在生成每个目标单词时,都能够动态地关注输入序列的不同部分,而不是依赖于一个单一的上下文向量。在 Seq2Seq 中加入 Attention 后,模型的工作原理发生了变化:编码器的输出编码器在每个时间步都会生成一个隐藏状态(h1, h2, h3,...),这些隐藏状态反映了输入序列中每个位置的信息。解码器的 Attention 权重解码器生成每个目标单词时,首先会为输入序列的每个位置计算一个 注意力权重(Attention weight)。这个权重决定了当前解码器生成单词时,应该多大程度地关注输入序列中的哪个部分。权重计算:解码器的当前状态与编码器每个时间步的隐藏状态进行比较,计算它们之间的相似度。这个相似度即为 Attention 权重,表示当前目标单词与输入序列各个部分的关联程度。加权求和生成上下文向量解码器根据计算得到的 Attention 权重,对编码器的所有隐藏状态进行加权求和,得到一个新的 上下文向量。这个上下文向量是动态的,依赖于目标序列当前生成的单词,因此它包含了关于输入序列的更丰富、更具体的信息。生成目标单词解码器将这个新的上下文向量与当前的隐藏状态结合,生成目标序列中的下一个单词。这很像人类看图片的逻辑,当我们看下面这张熊猫图片的时候,我们将注意力集中在了图片的焦点上(熊猫)。在图片上,我们的视线聚焦在熊猫及其攀爬的树干,熊猫和树干在这个区域内非常清晰,在此之外的区域则相对模糊,表示其他景物(例如熊猫背后的山林)没有被“注意力”所关注。这样的图像就能够直观地展示注意力机制是如何在众多信息中挑选出关键部分进行重点处理的。 如上面所说的,我们的视觉系统就是一种 Attention机制,将有限的注意力集中在重点信息上,从而节省资源,快速获得最有效的信息。Attention 的3大优点之所以要引入 Attention 机制,主要是3个原因:参数少:模型复杂度跟 CNN、RNN 相比,复杂度更小,参数也更少。所以对算力的要求也就更小。速度快:Attention 解决了 RNN 不能并行计算的问题。Attention机制每一步计算不依赖于上一步的计算结果,因此可以和CNN一样并行处理。效果好:在 Attention 机制引入之前,有一个问题大家一直很苦恼:长距离的信息会被弱化,就好像记忆能力弱的人,记不住过去的事情是一样的。欢迎各位大佬在评论区交流&批评指正!Datewhale组队学习参考链接Datawhale AI+X 共学活动 Fun-Transformer —— Task1:引言Attention Is All You Need
2025年01月15日
8 阅读
0 评论
1 点赞