从头理解self-attention机制

注意力机制中较为重要的是self-attention机制,直接做了个小白能看懂的总结,也便于自己复习。

简介

self-attention机制就是想实现一连串的特征编码,两两之间的互相注意。有一串特征编码,x1, x2, …, xn,这里x1 x2 …都是一个特征向量,即让每个特征向量都关注到所有的特征向量(包括其自己),然后转变成一个更深层次的向量。

最原始的做法就是,两两之间作点积运算,然后生成一个n ✖️ n的矩阵,其中 i 行 j 列表示 xi 和 xj 之间的相似度。然后我们用这个相似度矩阵,再对 x 做加权平均,就得到一个y1, y2, …, yn,这个就是self-attention的输出

举个简单的🌰,比如三个特征向量 x1 = [0.1, 0.5], x2 = [0.3, 0.4], x3 = [0.8, 0],然后两两之间做点积运算,得到一个矩阵如下:
0.26 0.23 0.08
0.23 0.25 0.24
0.08 0.24 0.64
然后用这个矩阵对 x1, x2, x3 做加权平均,就是该权重矩阵的第 i 行单独拿出来,就是 yi 的加权值。计算方法就是:
y1 = 0.26 ✖️ x1 + 0.23 ✖️ x2 + 0.08 ✖️ x3
y2 = 0.23 ✖️ x1 + 0.25 ✖️ x2 + 0.24 ✖️ x3
y3 = 0.08 ✖️ x1 + 0.24 ✖️ x2 + 0.64 ✖️ x3

主要的操作

归一化

首先,加权值应当要归一化,即一行权重值加起来应该是1,否则 y 在数值上会变得不稳定(变得很大或者变得很小),所以权重矩阵要按行做归一化。归一化的方法就是Softmax,因为softmax更好求导,数值上相比于hard max也更稳定。

假设原先的计算方法是:Y = (X X^T) X
那么加入归一化后就是:Y = Softmax(X X^T) X

数值稳定

X X^T 这个权重矩阵也有问题,一般来说,一个特征向量中,每一个元素都是一个期望为0,方差为1的随机变量(为了数值稳定,特征向量每层都要做归一化,就是为了保证这个性质),我们希望后续运算过程中,所有的中间变量都可以保持期望为0,方差为1。

期望为0,方差为1的原因:假设方差很多,数值为出现很大的数字,比如100,这样求导的梯度也会变得很大,模型更新的幅度也会很大,就很不稳定了。甚至会出现梯度爆炸(就是更新的数值变成无穷大)或者梯度消失(更新数值为0,就是更新不动了)

但是点积运算会打破这一性质(所有的中间变量都可以保持期望为0,方差为1),例如,假设x1是一个64维的向量,x2也是个64维向量,两者点积后的结果,期望还是0,方差已经不是1了,方差变成了根号64=8。
见如下概率论公式:D(XY)=D(X)D(Y) - (E(X)E(Y))

d个随机变量相乘后相加,就是多个随机变量运算后的方差计算公式。应该除以根号d_k,才能把方差控制在1,d_k是特征向量的维度,所以公式就变成了:
Y = (X X^T / sqrt(d_k)) X

线性变换

这里运算都是X,太单一了,所以作者就让三个X分别做了三个线性变换,K、Q、V都是从 X 变换过来的线性变换的权重矩阵,根源都是同一个,所以其实就是自己跟自己做运算,分别是:
Q = W^Q X
K = W^K X
V = W^V X
这里线性变换就是nn.Linear(d_k, d_k),不改变维度,只是做了一个线性投影。

把线性变换后的结果再去做attention运算:
Y = (Q K^T / sqrt(d_k)) V

一些总结

1. self-attention是线性运算吗?
答:不是。前面全是线性运算,非线性来自于归一化softmax。

2. self-attention的时间复杂度?
就看这个算法做了多少次原子运算
例如:
两个向量点积,就是对应的元素乘起来然后累加(n次乘法+n次加法),复杂度是O(n)。
两个n✖️n矩阵相乘,结果有n✖️n个元素,每个元素是由n次乘法+n次加法得来的,所以时间复杂度是O(n^3).

所以,self-attention的时间复杂度如下:
1.每行归一化,一行softmax的时间复杂度是o(n),那么n行 就是o(n2)
2.往里面看,权重矩阵是看成一个矩阵乘法X X^T,X是nd_k维,所以是O(n✖️n✖️d)。过程:矩阵乘法,n✖️d d✖️n,结果n✖️n,每个元素d次
3.和V相乘,权重矩阵是n✖️n对吧,这个V是n✖️d,是O(n✖️n✖️d)
所以,最后相加,总时间复杂度是O(n✖️n✖️d)。

3.根据公式写出代码

import numpy as np
import math
def self_attention(X):
    n, d = X.shape[:2]
    # 假设W_q  W_k  W_v是三个线性变换的权重矩阵
    W_q = np.random.rand(n * n)
    W_k = np.random.rand(n * n)
    W_v = np.random.rand(n * n)
    # 你来填 d个随机变量
    Q = np.matmul(W_q,X)
    K = np.matmul(W_k,X)
    V = np.matmul(W_v,X)
    W = np.matmul(Q,np.transpose(K))/math.sqrt(d)
    # -*- coding: utf-8 -*-
  
    def softmax(X):
        # reshape后面加个1就是为了让他可以跟x_exp相除
        # 比如说一个3 * 3的矩阵,可以跟3 * 1的矩阵相除的
        # 相除的时候,3 * 1的矩阵会扩展成3 * 3的,就是把列复制3份
        x_row_max = X.max(axis=1).reshape(list(X.shape)[:-1]+[1]) #按行找最大值
        X = X - x_row_max
        # 取最大值防溢出
        x_exp = np.exp(X)
        x_exp_row_sum = x_exp.sum(axis=1).reshape(list(X.shape)[:-1]+[1])
        softmax = x_exp / x_exp_row_sum #归一化
        return softmax
    
    # Y=softmax(Q K^T/sqrt(d))*V

    Y = np.matmul(softmax(W),V)
    return Y
  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值