Transformer
- 1、引言
- 2、Transformer
- 2.1 引言
- 2.2 核心概念
- 2.3 应用
- 2.4 算法公式
- 2.5 代码示例
- 3、总结
1、引言
小屌丝:鱼哥,昨天的感受咋样
小鱼:啥感受啊?
小屌丝:你确定不知道?
小鱼:我…
小屌丝:再想一想… 嘿嘿~
小鱼:嗯… 确实值得回味。
小屌丝:那咱再去体验体验?
小鱼:这… 不太好吧。
小屌丝:那有啥不太好的。
小鱼:我可是正经人,体验一次就够了,可不能上瘾的
小屌丝: 那你不去,我可去了。反正都约好了
小鱼:额… 都约好了,那不去岂不是浪费了。
小屌丝:但是,咱的有个条件
小鱼:啥条件?
小屌丝:就是,跟我讲一讲 Transformer
小鱼: 这岂不是张口就来。
小屌丝:那讲讲。
小鱼:路上讲。
小屌丝:也就这个时候,你最积极。
2、Transformer
2.1 引言
Transformer模型是一种深度学习算法,自2017年由Google的研究者Vaswani等人在论文《Attention is All You Need》中首次提出以来,它迅速成为了自然语言处理(NLP)领域的一种革命性技术。
Transformer模型的核心思想是利用“自注意力(Self-Attention)”机制来处理序列数据,这使得它能够在处理长距离依赖问题时表现得更加出色。
2.2 核心概念
- 自注意力(Self-Attention):
- 自注意力机制允许模型在处理每个序列元素时,考虑到序列中的所有其他元素,这样能够捕获序列内的长距离依赖关系。
- 自注意力机制通过计算序列中每个元素对其他所有元素的注意力分数来实现,这些分数表示了元素间的相关性强度。
- 多头注意力(Multi-Head Attention):
- Transformer模型使用多头注意力机制来增强模型的注意力能力。
- 通过并行地执行多个自注意力操作,模型可以从不同的表示子空间中学习信息,这样能够让模型更好地理解和处理数据。
- 位置编码(Positional Encoding):
- 由于Transformer完全基于注意力机制,没有像循环神经网络(RNN)那样的递归结构来处理序列数据,因此需要一种方式来理解序列中元素的位置信息。
- Transformer通过给输入元素添加位置编码来实现这一点,位置编码与元素的嵌入向量相加,从而让模型能够利用序列的顺序信息。
- 编码器-解码器架构:
- Transformer模型采用编码器-解码器架构。
- 编码器负责处理输入序列,解码器则负责生成输出序列。
- 在编码器和解码器中,都使用了多层自注意力和全连接网络。
- Transformer模型采用编码器-解码器架构。
2.3 应用
Transformer模型已经被广泛应用于各种自然语言处理任务中,包括但不限于:
- 机器翻译
- 文本摘要
- 问答系统
- 文本生成
2.4 算法公式
Transformer模型的核心是自注意力机制,其关键的计算公式如下:
-
自注意力得分计算:
Attention ( Q , K , V ) = softmax ( Q K T d k ) V \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V Attention(Q,K,V)=softmax(dkQKT)V
其中,- Q Q Q、 K K K、 V V V分别代表查询(Query)、键(Key)和值(Value),
- d k d_k dk是键的维度。
这个公式通过计算查询和所有键之间的相似度,然后用softmax归一化得到权重分布,最后用这个分布来加权值。
-
多头注意力:
- Transformer采用多头注意力机制来并行地执行多次自注意力操作,从而能够让模型从不同的子空间学习信息。
- 多头注意力的计算可以表示为: MultiHead ( Q , K , V ) = Concat ( head 1 , head 2 , . . . , head h ) W O \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \text{head}_2, ..., \text{head}_h)W^O MultiHead(Q,K,V)=Concat(head1,head2,...,headh)WO
- 其中,每个 head i = Attention ( Q W i Q , K W i K , V W i V ) \text{head}_i = \text{Attention}(QW_i^Q, KW_i^K, VW_i^V) headi=Attention(QWiQ,KWiK,VWiV), W O W^O WO是输出线性变换的权重矩阵。
-
位置编码:
- 位置编码的公式如下,用于给每个位置的元素编码一个唯一的位置信息: P E ( p o s , 2 i ) = sin ( p o s 1000 0 2 i / d model ) PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) PE(pos,2i)=sin(100002i/dmodelpos) P E ( p o s , 2 i + 1 ) = cos ( p o s 1000 0 2 i / d model ) PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) PE(pos,2i+1)=cos(100002i/dmodelpos)
- 其中, p o s pos pos是位置序号, i i i是维度序号, d model d_{\text{model}} dmodel是模型的维度。
2.5 代码示例
# -*- coding:utf-8 -*-
# @Time : 2024-04-06
# @Author : Carl_DJ
import torch
import torch.nn as nn
import torch.nn.functional as F
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, num_heads):
super(MultiHeadAttention, self).__init__()
# 初始化参数
self.num_heads = num_heads
self.d_k = d_model // num_heads
# 定义线性层用于生成查询(Q)、键(K)和值(V)
self.queries_linear = nn.Linear(d_model, d_model)
self.keys_linear = nn.Linear(d_model, d_model)
self.values_linear = nn.Linear(d_model, d_model)
# 初始化最后的线性层,用于将多头注意力结果合并回原始维度
self.fc_out = nn.Linear(d_model, d_model)
# 定义缩放因子
self.scale = 1 / (self.d_k ** 0.5)
def forward(self, values, keys, query, mask=None):
# 将输入通过线性层得到Q、K、V
N = query.shape[0]
query = self.queries_linear(query).view(N, -1, self.num_heads, self.d_k).transpose(1, 2)
keys = self.keys_linear(keys).view(N, -1, self.num_heads, self.d_k).transpose(1, 2)
values = self.values_linear(values).view(N, -1, self.num_heads, self.d_k).transpose(1, 2)
# 计算注意力分数
energy = torch.einsum("nqhd,nkhd->nhqk", [query, keys]) * self.scale
# 如果提供了掩码,则将其应用到注意力分数上
if mask is not None:
energy = energy.masked_fill(mask == 0, float("-1e20"))
# 通过softmax计算注意力权重
attention = torch.softmax(energy / (self.d_k ** 0.5), dim=3)
# 应用注意力权重到值上,得到输出
out = torch.einsum("nhql,nlhd->nqhd", [attention, values]).transpose(1, 2).contiguous().view(N, -1, self.d_model)
# 通过最后的线性层得到最终输出
out = self.fc_out(out)
return out
# 假设我们有以下输入维度和头数
d_model = 512
num_heads = 8
# 实例化自注意力层
self_attn = MultiHeadAttention(d_model, num_heads)
# 创建一些模拟数据
batch_size = 64
seq_length = 10
values = torch.randn(batch_size, seq_length, d_model)
keys = torch.randn(batch_size, seq_length, d_model)
query = torch.randn(batch_size, seq_length, d_model)
# 创建掩码(例如,为了处理填充)
mask = torch.ones(batch_size, 1, seq_length).bool()
# 将数据输入自注意力层进行前向传播
output = self_attn(values, keys, query, mask=mask)
print(output.shape) # 输出形状应该是 (batch_size, seq_length, d_model)
步骤:
- 首先,定义了一个MultiHeadAttention类,并创建了一个自注意力层的实例self_attn;
- 然后,创建了一些模拟数据values、keys、query以及一个掩码mask;
- 最后,我们调用self_attn的forward方法,将模拟数据作为输入进行前向传播,得到输出output;
3、总结
Transformer模型通过其独特的自注意力机制解决了序列处理中的长距离依赖问题,其多头注意力和位置无关的特性使其在处理各种序列数据时都能获得卓越的表现。
由于其强大的性能和灵活的结构,Transformer及其衍生模型已经成为了自然语言处理领域的基石,影响了整个人工智能领域的发展方向。
我是小鱼:
- CSDN 博客专家;
- 阿里云 专家博主;
- 51CTO博客专家;
- 企业认证金牌面试官;
- 多个名企认证&特邀讲师等;
- 名企签约职场面试培训、职场规划师;
- 多个国内主流技术社区的认证专家博主;
- 多款主流产品(阿里云等)测评一、二等奖获得者;
关注小鱼,学习【机器学习】&【深度学习】领域的知识。