如何理解Transformers中矩阵点积计算词向量的相似度
** 先声明,下面的过程都是我自己的理解,可能不一定正确,大家参考,有不对的,欢迎评论区指正。**
最近在看Transformer的原理,其中通过点积计算词向量相似度的逻辑优点懵,思考了许久貌似开窍了,先上计算相似度的代码:
import torch
from math import sqrt
query = key = value = inputs_embeds
dim_k = key.size(-1) # 获得最后一个维度的大小(768)
scores = torch.bmm(query, key.transpose(1,2)) / sqrt(dim_k)
scores.size()
torch.Size([1, 5, 5])
inputs_embeds是词向量矩阵
inputs_embeds = token_emb(inputs.input_ids)
inputs_embeds.size()
torch.Size([1, 5, 768])
key.transpose(1,2) 做了矩阵的转置,也就是把最后两个维度互换了一下,相当于列转行
torch.bmm(query, key.transpose(1,2))是对两个矩阵做点积,计算相似度,而最后除以sqrt(dim_k)则是进行归一化处理
最终这个Score实际上就是自注意力计算词向量相似度的分数,最终得到一个5x5的矩阵,前面的1代表批次可以先不考虑。
那么为什么做个点积就可以计算相似度呢?我们先来看下向量点积的含义:
点积(Dot Product)是一种常见的计算两个向量相似度的方法。当两个向量进行点积操作时,结果的大小可以反映两个向量的相似性。这是因为点积操作考虑了向量的方向和大小。
假设我们有两个向量 A 和 B,它们的点积定义为:
cssCopy code A . B = |A| * |B| * cos(θ)
其中,|A| 和 |B| 分别是 A 和 B 的长度,θ 是 A 和 B 之间的夹角。
可以看到,当 A 和 B 的方向相同时(即它们的夹角接近0),cos(θ) 接近 1,所以 A . B 较大。这表明 A 和 B 很相似。
反之,当 A 和 B 的方向相反时(即它们的夹角接近180度),cos(θ) 接近 -1,所以 A . B 较小,甚至为负。这表明 A 和 B 不相似。
当 A 和 B 互相垂直时(即它们的夹角为90度),cos(θ) 为 0,所以 A . B 为 0。这表明 A 和 B 没有相似性。
因此,通过计算向量的点积,我们可以得到一个衡量两个向量相似性的标量值。
在深度学习中,尤其是在自然语言处理和推荐系统中,我们经常需要衡量和比较向量(例如,单词嵌入或用户和物品的嵌入)的相似性。在这些情况下,点积是一种简单且有效的方法。
因为词向量表示了一个词的多个方面的属性,比如上面例子中可以理解为每个词定义了768个属性,那么我们用一个例子来类比:
假设有三个人分别是小张、小明和小红,他们有4个属性,分别是:爱好、性别、年龄、籍贯;那么三个人的属性如下
姓名 | 性别 | 年龄 | 籍贯 | 爱好 |
---|---|---|---|---|
小张 | 男 | 青年 | 浙江 | 音乐 |
小明 | 男 | 中年 | 湖北 | 绘画 |
小红 | 女 | 青年 | 江苏 | 音乐 |
那么,我们可以把上面的表格转换为一个矩阵:
[
[男, 青年, 浙江, 音乐],
[男, 中年, 湖北, 绘画],
[女, 青年, 江苏, 音乐],
]
然后我们把这个矩阵转置:
[
[男, 男, 女]
[青年, 中年, 青年]
[浙江, 湖北, 江苏]
[音乐, 绘画, 音乐]
]
当然,我们来看一个简单的例子,以更好地理解矩阵点积(矩阵乘法)的过程。
假设我们有以下两个矩阵 A 和 B:
矩阵A:
Copy code1 2 3 4 5 6
矩阵B:
Copy code7 8 9 10 11 12
这里,矩阵A的形状是(2,3),矩阵B的形状是(3,2)。我们可以进行矩阵乘法,因为A的列数等于B的行数。结果矩阵C的形状将会是(2,2)。
我们如何计算结果矩阵C的每个元素呢?我们来看一个具体的例子,计算结果矩阵C的第一个元素:
C[0,0] = A[0,0]B[0,0] + A[0,1]*B[1,0] + A[0,2]*B[2,0] = 1*7 + 2*9 + 311 = 58
同理,我们可以计算结果矩阵C的其他元素。最终,矩阵C将是:
58 64 139 154
这就是矩阵乘法(或者说矩阵点积)的过程。希望这个例子能帮助你理解矩阵乘法。
所以上面的矩阵点乘后,结果矩阵就变成:
[
[男 * 男 + 青年 * 青年 + 浙江 * 浙江 + 音乐 * 音乐,男 * 男 + 青年 * 中年 + 浙江 * 湖北 + 音乐 * 绘画,男 * 女 + 青年 * 青年 + 浙江 * 江苏 + 音乐 * 音乐],
[男 * 男 + 中年 * 青年 + 湖北 * 浙江 + 绘画 * 音乐,男 * 男 + 中年 * 中年 + 湖北 * 湖北 + 绘画 * 绘画,男 * 女 + 中年 * 青年 + 湖北 * 江苏 + 绘画 * 音乐],
[女 * 男 + 青年 * 青年 + 江苏 * 浙江 + 音乐 * 音乐,女 * 男 + 青年 * 中年 + 江苏 * 湖北 + 音乐 * 绘画,女 * 女 + 青年 * 青年 + 江苏 * 江苏 + 音乐 * 音乐]
]
为了便于计算,我们将值相等的点积值设为1,不相等设为0,于是,整个矩阵就变成了:
[
[4, 1, 2]
[1, 4, 0]
[2, 0, 4]
]
显然这就变成了3 x 3的了,我们可以认为这个矩阵代表了,三个人之间两两的关联度:
小张 | 小明 | 小红 | |
---|---|---|---|
小张 | 4 | 1 | 2 |
小明 | 1 | 4 | 0 |
小红 | 2 | 0 | 4 |
于是,当你遇到任何一个人,你都可以说出来他和其他人的相似性。
词向量和这个过程应该是类似的,只不过,它的属性有768个之多。因此在后续的匹配当中,每个词都可以找到与之相似度分数最高的一个关联词。但是,目前还有一个问题,就是这个相似度评分无法解决同意词的问题,同样是“绘画”动词和名词的相似度是一样的。要解决这个问题,就得要通过多层编码器的神经网络来解决了,这也是编码过程要解决的主要问题。
代码的最后把相似度除以了sqrt(dim_k),这一步是为了进行归一化,进行的缩放处理:
/ sqrt(dim_k)
:这是一个缩放操作,将上述计算得到的张量除以 dim_k 的平方根。这是一种常见的归一> 化手段,dim_k 通常是 key 的维度。在 "Scaled Dot-Product Attention" 中,这样的缩放操> 作可以防止点积在维度很高时变得过大。
最后,对相似度进行softmax处理,就转换为了概率:
import torch.nn.functional as F
weights = F.softmax(scores, dim=-1)
weights.sum(dim=-1)
这样就变成了A词和B词的相似度概率,找到和A词最匹配的词就是找概率对打的。
网友评论