三步计算

按位相乘再相加求注意力

 
向量a与向量b求向量按位相乘 再相加 得一个数,一组向量即矩阵A与矩阵B计算后得向量C 

向量C做softmax -- 得分矩阵S 

得分矩阵S 左乘 矩阵A -- B相对A的注意力 

 
按位相乘再相加,其实就是向量内积,多个向量同时运算,就是矩阵运算

一类事物[元素个数,单个元素维度] 这样的事物求自注意力,

[元素个数,单个元素维度] @ [单个元素维度,元素个数] = [元素个数,元素个数]

一组数据中的每个元素/向量,相对另外一组数据中的每个数据/向量,都有一个特征 
用句子与单词来描述就是,
一个句子的每个单词,相对另外一个句子的每个单词,都有一个特征 
一个句子的每个单词,相对另外一个句子,都有一个特征向量 
一个句子的每个单词,相对多个句子,都有一个特征矩阵

转概率后就是得分矩阵,这个得分矩阵是二者共同的特征,是交集,是共性 
由于转了概率,再乘回去,类似于期望,
即特征向量中的每个数值都是一个元素相对一组元素的期望

余弦相似度

什么是余弦相似度

 
余弦相似度就是两个向量之间的夹角的余弦值
有的地方也叫余弦距离,
夹角为0时距离为1,夹角为90度时,距离为0 

余弦相似度的用途

 
余弦相似度可以衡量两个向量之间的差异,
就是比一比谁离谁更近,谁又离谁更远 

不知你有没有这样的疑问?
向量的核心要素除了方向,不是还有大小吗?
怎么比个方向就说两个向量近似了?

这是因为此问的背景是所有向量的模已经近似/接近于1,
至少是大致相等/相近
夹角之间的差异就是两个向量之间的主要差异

如果是你想更精确一些,可以在比较之前将所有向量再次约束一次,将其模归为1

向量内积

向量内积在AI中的地位

 
向量之间的运算,较常用的有两个:
线性运算:按位 加, 减, 数乘
内积运算:接位 相乘 再 相加 

要不断尝试使用矩阵去描述及解决问题(此句的重要性属于一个学科总纲级的...),
而矩阵运算实际上就是一系列的向量内积,维度的变换... 

从向量的角度看矩阵

 
这里特指2维矩阵,AI最常用的也是这个,
三维矩阵实际上有一维是批次,就是多个2维矩阵

2维矩阵常说行与列,
行与列是人的视角上的一个描述,
如果写在纸上,你能看到一组类似长方形/正方形的一组数,

实际对于一组数据来说,
它们本没有什么行与列的概念,更不需要排列成什么形状,
它们通常用第m维,第n维这样纯数字的描述

对于一个矩阵来说,行与列都是它的一个维度,无区别
但对业务来说,这个数据是有行与列的差异的
我们将业务进行数字化,矩阵化之后,对应的行与列就有不同的含义

此时,用向量/向量组来描述更为合适

从向量的角度看矩阵举例

 
一个单词 用一个索引来表示
一句话n个单词就是一个n维向量,记作seq_len 
多句话时,就是多个向量,形成一个向量组,

向量组比矩阵有更强的方向性,
更能突显 一句话对应一个向量 这个业务含义
向量组个数这个维度记作batch_size

通常情况下,批次放dim=0的维度,即[batch_size, seq_len]=A  
这就是关系型数据库的表的结构

但对于一个矩阵来说,每个维度都只是一组数据而已
所以将批次放第2维,依然能有相同的含义[seq_len,batch_size]=B 

A[0,:]表示取一句话,B[:,0]也是取一句话
A与B都看作 每个向量维度为seq_len的向量组
A中用 行向量 表示一句话
B中用 列向量 表示一句话

后续,看具有业务含义的矩阵,
都从 向量/向量组 的角度去看,
因为业务通常要求矩阵有批次的维度 

要习惯用A[0,:]取一行,而不是A[0],尽管它们取出的数据是一样的
A[0,:]在形式上就可以提醒你,dim=0的维度 取一,剩下所有维度全取

向量内积更深一层的含义

 
向量内积:按位 相乘 再 相加

首先,将一个事物的整体看作 1,就是我们先有了全集,将全集看作1 
全集中所有的子集,事物 都小于1,是1的一部分

如果用两个圆圈表示A与B两个事物,那么重叠的部分就是交集,
类似于/近似于/等价于 两个事物相乘的结果 
这个结果即属于A也属于B,是事物之间的共性
可存在这种可能,两个圆圈没有交集,
那么这样的两个圈圈对应的两个矩阵相乘的结果就是0 

即乘法可以提取事物之间的共性,这不就是AI需要的吗?
将不同维度得到的共性结果 相加,就得到了两个向量之间共性的一个映射

 
如果一个规律足够底层,那么它在很多方面都能得到体现,

回忆欧氏距离,是自己与自己相乘再相加,
然后开方就得到了一个n维向量的模
这就是该向量的距离/长度 

回忆方差计算,每个元素的误差=每个元素的值-均值,
这个误差自己跟自己相乘再相加就是整个分布的误差

回忆协方差,是两个分布之间的误差相乘再相加,再除以两个分布的模

它们都有一个共性的内核:
在各个对应的维度上 相乘,然后将不同的维度 相加起来 
这不就是向量内积吗?

 
从解析几何的角度看向量内积,
a·b = |a|·|b|·夹角余弦,
就是一个向量投影到另外一个向量上,然后再相乘
谁投影到谁上面无所谓,因为结果一样,
因为向量内积算的是共性,是交融后的结果

扯的有些远了,回收一下注意力,
这里只是为了说明向量内积可以提取特征(实际上是共性),
理论归理论,
关键是,实践也验证了,向量内积的确可以提取特征
这才是我们相信 向量内积可以提取特征 的主要原因 

注意力计算

求矩阵A相对矩阵B的注意力

 
1. 矩阵A与矩阵B做 点乘,得交集矩阵C  
2. 对矩阵C做softmax求 得分矩阵D
3. 得分矩阵D@矩阵A = 矩阵A相对B的上下文向量,也就是注意力

序列到序列的注意力计算

 
hidden.shape = [1,batch_size,hidden_size]
encoder_outputs.shape = [seq_len,batch_size,hidden_size]

点乘,按位相乘再让特征相加,特征维度消失
[1,批次,特征]*[序列,批次,特征]= [序列,批次]
attn_energies = torch.sum(hidden * encoder_output, dim=2)

# [sel_len,batch_size] -- [batch_size,sel_len]
attn_energies = attn_energies.t()

# 按seq_len维度转为概率,[batch_size,seq_len]
# [batch_size,seq_len] -- [batch_size, 1, seq_len] 添加这个1是为了后面进行bmm
attn_weights = F.softmax(attn_energies, dim=1).unsqueeze(1)

# context.shape = [batch_size,1,hidden_size]
# decoder中每个单词对应一个encoder的上下文向量
# 加权平均,[1, seq_len] sum(dim=1)=1,注意力的得分之和为1,将这个得分乘到原来解码器上
context = attn_weights.bmm(encoder_outputs.transpose(0, 1))

注意力计算通俗举例

 
注意力指事物联系中的关键点,它通常有一个语境/提前

seq1: 老板 ,这 馒头 多少 钱 一个 ?
seq2: 1块

seq1分词后得到8个词,seq2分词后得到1个词,
现在求seq1相对seq2的注意力

seq1中的每个单词与seq2的注意力都是 一个值,
两个向量进行内积运算,按位相乘再相加,得到一个值 
seq1有8个词,批次计算后,seq1的shape为[8,1],
其实就是[8],一个列表8个值,设此列表为A 

这里有一个大提前,文本向量化后的数据皆在[0,1]之间,或者[-1,1]之间 
负号是数的一个特殊维度/方向,
若将事物的全体定为1,那么两个事物相乘就是其交集,
是两个事物共同的部分
而向量内积就是二者在各个维度上交集的和
将一个事物在各个维度上的交集强行投影到一维上,然后相加
所以,此处矩阵相乘得到的就是seq1与seq2的交集 

我们的目标是求注意力,
是要看看seq1中各个单词到底哪个单词相对seq2是关键单词
孰轻孰重,若轻则轻多少,若重则重多少
要看它们之间的相对差异,用数字清晰地体现出来
差异本身是多少,不重要
重要的是,相对来说差多少
百分比的概念呼之欲出... 
全体为100%,我若在你心中最重的话,那么就没有其他事物比我更重

这是其思想,极其重要,
softmax是实现这种思想的“其中一种”方法
列表A做softmax后,得到 分数列表 

分数列表再与单词列表,矩阵相乘,得到seq1相对seq2的上下文向量,
这就是注意力

上下文向量

序列到序列的上下文向量

 
这里以序列到序列为背景,比如:
十一 放假 准备 去哪 玩? -- 不 出去 了 

编码序列有一个上下文,涉及 旅游 这个主题,
但序列到序列中的上下文向量,
是 编码序列相对于 解码器的每个单词都有一个 上下文向量 

这里,解码器有三个单词,那么就有三个上下文向量,
这三个上下文向量分别与解码器的三个单词拼接,
形成包含前置序列上下文的 新的向量

对编码器只是提取特征,解码器,才是真正推理的开始,
当前单词+由注意力机制提取的上下文向量 推理出 下一个单词,

求编码器相对下一个单词的注意力,再推理下下个单词
这样一层层推理下去

所得的上下文向量中,都有 旅游 这个主题,只是比重不一样 

参考