作者:机器学习算法工程师
本文约4200字,建议阅读10分钟
本篇文章的主要内容是引导您完成Self-Attention模块中涉及的数学运算。
标签:深度学习
前言
译者: 在 medium 看到一篇文章从代码的角度,作者直接用 pytorch 可视化了 Attention 的 QKV 矩阵,之前我对 self-Attention 的理解还是比较表面的,大部分时候也是直接就调用 API 来用, 看看原理也挺有意思的,作者同时制作了可在线运行的 colab作为演示,遂翻译给大家一起看看:The illustrations are best viewed on Desktop. A Colab version can be found here, (thanks to Manuel Romero!).
有人问在transformer模型的众多派生BERT,RoBERTa,ALBERT,SpanBERT,DistilBERT,SesameBERT,SemBERT,SciBERT,BioBERT,MobileBERT,TinyBERT和CamemBERT有什么共同点?我们的并不期待你回答都有字母"BERT"?。
事实上,答案是 Self-Attention?。我们不仅要谈论“BERT”的架构,更正确地说是基于`Transformer架构。基于Transformer的架构主要用于对自然语言理解任务进行建模,避免使用神经网络中的递归神经网络,而是完全依赖Self-Attention机制来绘制输入和输出之间的全局依存关系。但是,这背后的数学原理是什么?
这就是我们今天要发掘的问题。这篇文章的主要内容是引导您完成Self-Attention模块中涉及的数学运算。在本文结尾处,您应该能够从头开始编写或编写Self-Attention模块。
什么是自注意力机制?
一个self-attention模块输入为 n,输出也为 n.那么在这个模块内部发生了什么?用门外汉的术语来说,self-attention机制允许输入彼此之间进行交互(“self”)并找出它们应该更多关注的区域(“Attention”)。输出是这些交互作用和注意力得分的总和。
实例演示
准备输入 初始化权重 导出key, query and value的表示 计算输入1 的注意力得分(attention scores) 计算softmax 将attention scores乘以value 对加权后的value求和以得到输出1 对输入2重复步骤4–7
实际上,数学运算是向量化的,即所有输入都一起进行数学运算。我们稍后会在“代码”部分中看到此信息。
Input 1: [1, 0, 1, 0] Input 2: [0, 2, 0, 2] Input 3: [1, 1, 1, 1]
稍后我们将看到value的维度也就是输出的维度。
[[0, 0, 1], [1, 1, 0], [0, 1, 0], [1, 1, 0]]
[[1, 0, 1], [1, 0, 0], [0, 0, 1], [0, 1, 1]]
[[0, 2, 0], [0, 3, 0], [1, 0, 3], [1, 1, 0]]
[0, 0, 1]
[1, 0, 1, 0] x [1, 1, 0] = [0, 1, 1]
[0, 1, 0]
[1, 1, 0]
使用相同的权重集获得输入 2 的key的表示形式:
[0, 0, 1]
[0, 2, 0, 2] x [1, 1, 0] = [4, 4, 0]
[0, 1, 0]
[1, 1, 0]
使用相同的权重集获得输入 3 的key的表示形式:
[0, 0, 1]
[1, 1, 1, 1] x [1, 1, 0] = [2, 3, 1]
[0, 1, 0]
[1, 1, 0]
一种更快的方法是对上述操作进行矩阵运算:
[0, 0, 1]
[1, 0, 1, 0] [1, 1, 0] [0, 1, 1]
[0, 2, 0, 2] x [0, 1, 0] = [4, 4, 0]
[1, 1, 1, 1] [1, 1, 0] [2, 3, 1]
让我们做同样的事情以获得每个输入的value表示形式:
[0, 2, 0]
[1, 0, 1, 0] [0, 3, 0] [1, 2, 3]
[0, 2, 0, 2] x [1, 0, 3] = [2, 8, 0]
[1, 1, 1, 1] [1, 1, 0] [2, 6, 3]
以及query的表示形式:
[1, 0, 1]
[1, 0, 1, 0] [1, 0, 0] [1, 0, 2]
[0, 2, 0, 2] x [0, 0, 1] = [2, 2, 2]
[1, 1, 1, 1] [0, 1, 1] [2, 1, 3]
[0, 4, 2]
[1, 0, 2] x [1, 4, 3] = [2, 4, 4]
[1, 0, 1]
Fig. 1.4: Calculating attention scores (blue) from query 1
请注意,在这里我们仅使用输入1的query。稍后,我们将对其他查询重复相同的步骤。
上面的操作被称为"点积注意力",是几种sorce之一。其他评分功能包括缩放的点积和拼接。
更多:
sorce:https://towardsdatascience.com/attn-illustrated-attention-5ec4ad276ee3
将attention scores通过 softmax 函数(蓝色)得到概率
softmax([2, 4, 4]) = [0.0, 0.5, 0.5]
Fig. 1.6: Derive weighted value representation (yellow) from multiply value(purple) and score (blue)
1: 0.0 * [1, 2, 3] = [0.0, 0.0, 0.0]
2: 0.5 * [2, 8, 0] = [1.0, 4.0, 0.0]
3: 0.5 * [2, 6, 3] = [1.0, 3.0, 1.5]
7 对加权后的value求和以得到输出1
对所有加权值(黄色)按元素求和:
[0.0, 0.0, 0.0]
+ [1.0, 4.0, 0.0]
+ [1.0, 3.0, 1.5]
-----------------
= [2.0, 7.0, 1.5]
得到的向量[2.0, 7.0, 1.5] (深绿)是输出 1 , 它是基于“输入1”的“query表示的形式” 与所有其他key(包括其自身)进行的交互。
8 对输入2重复步骤4–7
因为点积得分函数 query和key的维度必须始终相同.但是value的维数可能与query和key的维数不同。因此输出结果将遵循value的维度。
代码
import torch
x = [
[1, 0, 1, 0], # Input 1
[0, 2, 0, 2], # Input 2
[1, 1, 1, 1] # Input 3
]
x = torch.tensor(x, dtype=torch.float32)
Step 2: 初始化权重
w_key = [
[0, 0, 1],
[1, 1, 0],
[0, 1, 0],
[1, 1, 0]
]
w_query = [
[1, 0, 1],
[1, 0, 0],
[0, 0, 1],
[0, 1, 1]
]
w_value = [
[0, 2, 0],
[0, 3, 0],
[1, 0, 3],
[1, 1, 0]
]
w_key = torch.tensor(w_key, dtype=torch.float32)
w_query = torch.tensor(w_query, dtype=torch.float32)
w_value = torch.tensor(w_value, dtype=torch.float32)
Step 3:导出key, query and value的表示
keys = x @ w_key
querys = x @ w_query
values = x @ w_value
print(keys)
# tensor([[0., 1., 1.],
# [4., 4., 0.],
# [2., 3., 1.]])
print(querys)
# tensor([[1., 0., 2.],
# [2., 2., 2.],
# [2., 1., 3.]])
print(values)
# tensor([[1., 2., 3.],
# [2., 8., 0.],
# [2., 6., 3.]])
Step 4: 计算输入的注意力得分(attention scores)
attn_scores = querys @ keys.T
# tensor([[ 2., 4., 4.], # attention scores from Query 1
# [ 4., 16., 12.], # attention scores from Query 2
# [ 4., 12., 10.]]) # attention scores from Query 3
Step 5: 计算softmax
from torch.nn.functional import softmax
attn_scores_softmax = softmax(attn_scores, dim=-1)
# tensor([[6.3379e-02, 4.6831e-01, 4.6831e-01],
# [6.0337e-06, 9.8201e-01, 1.7986e-02],
# [2.9539e-04, 8.8054e-01, 1.1917e-01]])
# For readability, approximate the above as follows
attn_scores_softmax = [
[0.0, 0.5, 0.5],
[0.0, 1.0, 0.0],
[0.0, 0.9, 0.1]
]
attn_scores_softmax = torch.tensor(attn_scores_softmax)
Step 6: 将attention scores乘以value
weighted_values = values[:,None] * attn_scores_softmax.T[:,:,None]
# tensor([[[0.0000, 0.0000, 0.0000],
# [0.0000, 0.0000, 0.0000],
# [0.0000, 0.0000, 0.0000]],
#
# [[1.0000, 4.0000, 0.0000],
# [2.0000, 8.0000, 0.0000],
# [1.8000, 7.2000, 0.0000]],
#
# [[1.0000, 3.0000, 1.5000],
# [0.0000, 0.0000, 0.0000],
# [0.2000, 0.6000, 0.3000]]])
Step 7: 对加权后的value求和以得到输出
outputs = weighted_values.sum(dim=0)
# tensor([[2.0000, 7.0000, 1.5000], # Output 1
# [2.0000, 8.0000, 0.0000], # Output 2
# [2.0000, 7.8000, 0.3000]]) # Output 3
扩展到Transformers
那么我们该何去何从?Transformers!确实,我们生活在深度学习研究和高计算资源令人兴奋的时代。Transformers是 Attention Is All You Need的应用。研究人员从这里开始进行组装,切割,添加和扩展零件,并将其用途扩展到更多的语言任务。
Dimension Bias
Embedding module Positional encoding Truncating Masking
Multihead Layer stacking
Linear transformations LayerNorm