首先给大家引入一个github博客,这份代码是我在看了4份transformer的源码后选出来的,这位作者的写法非常易懂,代码质量比较高。https://github.com/Separius/BERT-keras

这篇文章主要跟大家分享四个点:多头机制(multi-head)、LN和GELU、位置编码。

在这再给大家安利几篇博客,便于大家更具体的理解自注意力的内在原理。

https://zhuanlan.zhihu.com/p/44121378

https://zhuanlan.zhihu.com/p/47282410(精华)

https://www.cnblogs.com/robert-dlut/p/8638283.html

transformer是self-attention的落地或者说扩展,多头机制把自注意力机制发挥得淋漓尽致。transformer最亮眼的地方就是完全抛弃了常规的链式RNN结构(包括LSTM等其他变体),即:并行计算能力特别弱的计算方法。它应该会是早期NLP训练技术跟当期技术的一个里程碑,毕竟人家BERT是吧,刷新了不造几个记录,虽然XLNET又刷新了BERT的记录,但是这也正证实了这种设计理念的优秀!优秀啊。。。[斜眼笑]。。。

言归正传!

一、自注意力机制(self-attention)和多头机制(mutil-head)

常规的语言生成模型长这样

下一个字的生成,依靠且只依靠上一个字的输出状态和当前输入的输入状态,也就是说,预测值在某一程度上说只跟上一个字关系大一些,而自注意力模型,差不多长下面这个样子。

这个图的意思是,每一个字的生成,会跟所有的字(encode的时候)都有关系,这就是所谓的“注意力”机制。整个文字的生成过程中,每一个字都可能会跟所有的字做加权,为什么是“可能”呢,因为有mask嘛,随机给屏蔽掉一些词,屏蔽掉的那就没办法顾及了。这样的好处有两个:

一是能“照顾”所有的词,也就是我们理解的“语境”,

比如,句子1:“优秀!这就很优秀了!我做梦都没想到他这么175的个子能在中场投篮投进了!”;

和句子2:“优秀!这就很优秀了!他这185的个子在篮下那么好的位置还是没进球!”。

同样位置的一个词“优秀!”,一模一样的字,它的意思的完全相反的(中华文化博大精深),自注意力机制就需要在即使后面说了一堆废话的基础上,还是能学出这个词是褒义还是贬义。换句话说,它在判断优秀是褒义还是贬义时,甚至需要看到最后几个关键语气的词,才能做出判断,而这个功能正是RNN系列模型做不到的!数学意义上可以叫做“贡献度”。

二是,这样所以词就能并行计算了,至少这一步是可以并行计算了!

OK,自注意力就是大致这样个流程,多头又是什么鬼!很简单,经过嵌入层后,每个词有多个维度(代码嵌入为768列),把这些维度均分成n_head(12)份,每一份都去做这么一件事,就是多头机制。简而言之,就是自注意力的模式,复制了几次,这个“几次”就是“多头”,12次就是12头。。。只不过,不是做复制,二是做拆分,均分成12次来进行注意力的计算。

原理懂了哈,咱们看下人家是怎么实现的。

(1)funcs的multihead_attention函数

_q, _k, _v = x[:, :, :n_state], x[:, :, n_state:2 * n_state], x[:, :, -n_state:]
 
# [B, n_head, max_len, 768 // n_head]
q = split_heads(_q, n_head)  # B, H, L, C//H
# [B, n_head, 768 // n_head, max_len]
k = split_heads(_k, n_head, k=True)  # B, H, C//H, L
# [B, n_head, max_len, 768 // n_head]
v = split_heads(_v, n_head)
x是embedding后的输入,经过3*768个1x1卷积,变成[B, max_len, 3*768]的特征矩阵,q、k和v各占1/3,即:[B, max_len, 768]

(2)funcs的split_heads函数

# [-1, max_len, 768]
x_shape = shape_list(x)
# 768
m = x_shape[-1]
# [-1, max_len, n_head,  768 // n_head]
new_x_shape = x_shape[:-1] + [n, m // n]
# [B, max_len, n_head,  768 // n_head]
 
new_x = K.reshape(x, new_x_shape)
# return [B, n_head, max_len, 768 // n_head] False
# return [B, n_head, 768 // n_head, max_len] True
return K.permute_dimensions(new_x, [0, 2, 3, 1] if k else [0, 2, 1, 3])
这里对q和v拆分成[B, 12,  max_len, 768 // 12] = [B, 12, max_len, 64]的长度,k拆成[B, 12, 64, max_len]

也就是说,每个词拆成了12等分,每一等分特征由之前的768变成了64。整个分成了B个batch,12的小batch,每个小batch的句子是max_len个长度的词组成,每个词有64的特征。

(3)这里咱们细讲一下这个permute,permute是自注意力计算逻辑最核心最抽象的地方之一。先上一张图

permute跟numpy的transpose是异曲同工的,都可以理解为转置。只是我们在对张量做转置的时候,用常规的二维矩阵的思维去理解比较难。但是!解释还是得用二维矩阵的方法去解释!

假设我们的q是一个2X6的矩阵,每一个字被嵌入成6个特征。k跟q一毛一样,但是我把k做一下转置,变成6X2的矩阵。这样的形式,我们用矩阵相乘的方法,把2x6的矩阵跟6x2的矩阵相乘,是不是变成了2x2,这个2x2是不是就可以理解为2个字对自己的排列组合。换句话说,所有的字与字之间的加权值得出来了。如图:优跟优的加权值是50(1x1+2x2+3x3+4x4+5x5),优跟秀的加权值是70(1x2+2x3+3x4+4x5+5x6),秀跟优的加权值是70(1x2+2x3+3x4+4x5+5x6),秀跟秀的加权值是90(2x2+3x3+4x4+5x5+6x6)。

(4)funcs的scaled_dot_product_attention_tf函数

先看这个函数:w = K.batch_dot(q, k)

这一步就是上一步所说的矩阵乘法。算法中,对每一组嵌入数据

[B, max_len, 768*3]通过均分,拆成3份[B, max_len, 768],分别作为q、k和v的前身;通过reshape,都变成[B, max_len, 12, 64];在通过permute变成q=[B, 12, max_len, 64]、k=[B, 12, 64, max_len]和v=[B, 12, max_len, 64]。

其中,q和k进行矩阵乘法,把max_len个字彼此求加权值(q=[B, 12, max_len, 64] * k=[B, 12, max_len, 64]),变成w=[B, 12, max_len, max_len]。这里的max_len放在“优秀”一组词里面就是2,即:当前句子的长度。

接着,继续对w和v做矩阵乘法,再求一次加权值(至于这一步有什么实际的原理,我不太确定),这一次矩阵乘法,在数学上把维度还原到[B, 12, max_len, 64],以方便后期继续还原成输入的shape。我揣测,这一步w*v的意义跟全连接层的意义是接近的。只不过在设计的出发点和可解释性上,要稍强于全连接层的意义。

所以,我用我自己组织的方式,给大家解释一下整个自注意力的过程

a、首先是split_heads,就是切分出qkv

b、矩阵乘法求权值,即:scaled_dot_product_attention_tf

c、merge_heads

经过上述两步,O(output)=[B, 12, max_len, 64],在这个函数里面,还原成[B, max_len, 64]

到此,多头+自注意力机制暂告一段落!

接下来你想在这一步重复多少次就重复多少次,因为输入输出都是一个shape。

二、LN(Layer Normalization)

https://blog.csdn.net/weixin_42078618/article/details/90730488

往这看!!!

三、GELU

一个公式说明一切(这个是近似函数,不是本征函数)

然后看看图像

当我看到这个图像的时候,我第一反应想起了Swish激活函数

他俩真的是异曲同工,几乎长得都一毛一样。从论文来看,GELU函数的收敛性会比RELU和ELU都有略好。

四、位置编码

位置信息对于理解一句话来说,也是很重要的。比如:

a、难受!滑板坏了,水还洒身上了!

b、滑板坏了,难受!水还洒身上了!

对于句子b,其实表达出来的意思,难受的重心是滑板,水是附加的负面影响。对于a,可能整体对心情造成的负面影响是差不多的。这就是词在不同位置可能对语境带了影响的一种情况。

常用的位置编码一般无外乎两种:一种是词嵌入,相当于先加一步全连接层,并且该层参数可学;另一种是自己设计位置编码方法。比如我印象中bert是用正余弦函数做编码的,以后看到再跟大家分享;或者,做一个递进的简单累加也不是不行哇,哈哈。

这里的Transformer阶段的位置编码只是使用了简单的词嵌入的方式,你也可以理解其实就是全连接层的一种应用方式。

结语:transformer以及后来的bert模型最核心的地方就是自注意力机制,大家能把自注意力的实现原理看懂,其核心思想也就一通百通了。
--------------------- 
作者:罗大黑 
来源:CSDN 
原文:https://blog.csdn.net/weixin_42078618/article/details/94394348 
版权声明:本文为博主原创文章,转载请附上博文链接!

源码剖析transformer、self-attention(自注意力机制)原理!相关推荐

  1. Spark源码分析之Master状态改变处理机制原理

    一Master故障挥着宕机,可能触发新的Master选举 当重新选择Leader的时候,会进行集群的恢复,在恢复的过程中,就会向Worker和AppClient发送MasterChanged消息. p ...

  2. tomcat(12)org.apache.catalina.core.StandardContext源码剖析

    [0]README 0)本文部分文字描述转自 "how tomcat works",旨在学习 "tomcat(12)StandardContext源码剖析" 的 ...

  3. tomcat(11)org.apache.catalina.core.StandardWrapper源码剖析

    [0]README 0.0)本文部分文字描述转自 "how tomcat works",旨在学习 "tomcat(11)StandardWrapper源码剖析" ...

  4. Chrome源码剖析

    转自http://www.wy182000.com/2011/05/06/chrome%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90/ 开源是口好东西,它让这个充斥着大量工业 ...

  5. Google Chrome源码剖析

    http://www.ha97.com/2903.html Google Chrome源码剖析[序] 发表于: Google, Google Chrome, 开源世界, 旧文存档, 编程开发 | 作者 ...

  6. Chrome-Chrome源码剖析

    Chrome源码剖析 [序] && [一] [序] 开源是口好东西,它让这个充斥着大量工业垃圾代码和教材玩具代码的行业,多了一些艺术气息和美的潜质.它使得每个人,无论你来自米国纽约还是 ...

  7. 读文章笔记(三):从源码学习Transformer

    读文章笔记(三):从源码学习Transformer encoder分为两部分: decoder 公众号机器学习算法工程师 文章链接: https://mp.weixin.qq.com/s/0NajB_ ...

  8. 老李推荐:第14章4节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-端口转发 1...

    老李推荐:第14章4节<MonkeyRunner源码剖析> HierarchyViewer实现原理-装备ViewServer-端口转发 在初始化HierarchyViewer的实例过程中, ...

  9. JS魔法堂:mmDeferred源码剖析

    一.前言 avalon.js的影响力愈发强劲,而作为子模块之一的mmDeferred必然成为异步调用模式学习之旅的又一站呢!本文将记录我对mmDeferred的认识,若有纰漏请各位指正,谢谢.项目请见 ...

最新文章

  1. C#用Tesseract进行OCR识别,可识别中英日韩所有语言
  2. knn实战:如何对手写数字进行识别?
  3. 项目中的软件需求说明书的访谈部分
  4. 杭电 Let's go to play
  5. 使用tail和head读取字节流
  6. 三维计算机视觉(七)--Spin image
  7. Chrome 92 版本新特性 92 版本发行说明
  8. win7下IDEA黑色主题看不到鼠标指针的修改方式
  9. 免费开源PDF复制格式整理神器
  10. Conficker.AE病毒局域网扫描工具
  11. jdk32位安装包下载_Win10离线安装.NET Framework 3.5方法汇总(附下载)
  12. 去掉桌面鼠标右键英特尔R显卡设置的方法
  13. 广告机-开机自动播放-视频-电影-图片-竖屏-分屏-展示机
  14. 惯性力偶矩公式中j_惯性力偶矩的计算
  15. 生成1000个不同的随机数
  16. 前端接收pdf文件_前端实现PDF导出功能
  17. ultravnc 设置代理_选择代理记账有何好处及注意事项
  18. Phaser帧动画没有效果
  19. React、umi和dva之间的关系
  20. 人脸识别:特征脸(Eigenface)

热门文章

  1. cdr文件过大导出pdf打不开_PDF快速导出JPG对应的分辨率技巧
  2. undertale人物_【undertale】传说之下精美人物图包 (Frisk篇)
  3. qpython3h手机版 写弹窗代码_Android Q之气泡弹窗的实现示例
  4. gridcontrol值为0时设置为空_XASSET 4.0入门指南
  5. Mvc多级Views目录 asp.net mvc4 路由重写及 修改view 的寻找视图的规则
  6. 如何成为一名Android架构师,乃至高级架构师,文末有路线图
  7. 英语写作学习笔录 task1 body
  8. 安装sqoop1.4.6-cdh5.5.2
  9. 生产环境 Apache 和 php 配置优化(一)
  10. 汉化 Hirens.BootCD 中的 XP 系统