SimBert张量运算--生成相似句&句子相似度判断
SimBert前言原理mask矩阵实现及运算前言SimBert是由苏剑林开发的模型,基于UniLM思路做成的,具体可以参考:https://kexue.fm/archives/7427本文主要介绍SimBert的张量矩阵运算原理,以及其shape的变换。对于UniLM,可以认真看下苏神写的《从语言模型到Seq2Seq:Transformer如戏,全靠Mask》原理在上面苏神写的文章中,有张图特别的
SimBert
前言
SimBert是由苏剑林开发的模型,基于UniLM思路做成的,具体可以参考:https://kexue.fm/archives/7427
SimBert可以做相似句生成&句子相似度判断
比如生成句子:
gen_synonyms(“我和吴彦祖比谁更帅”)
['我和吴彦祖比谁更帅?', '我和吴彦祖比较谁更帅', '我和吴彦祖比谁更帅一些', '我和吴彦祖谁更帅', '我和吴彦祖谁更帅?']
本文主要介绍SimBert的张量矩阵运算原理,以及其shape的变换。
对于UniLM,可以认真看下苏神写的《从语言模型到Seq2Seq:Transformer如戏,全靠Mask》
原理
在上面苏神写的文章中,有张图特别的形象,横坐标是原句子(y_true),纵坐标是预测的句子(y_pred),通俗理解就是:原句子的“[CLS]”预测出“你”,原句子的“你”预测出“想”…以此方式进行下去…
UniLM最重要的实现是对mask的设置,原理苏神已经在博客中写的很清楚,但是怎么实现一个字一个字预测下去的,具体的实现方式还是不太清楚,
用mask来做,那mask具体长什么样?怎么生成这种mask?生成的mask后面怎么发生作用?
出现了种种这些问题无法解决,于是自己认认真真的研究了下源码:
在bert4keras的models.py这个文件中的unilm_mask就可以解决第一、二个问题。
mask矩阵实现及运算
def unilm_mask(s): # s:Tensor("Input-Segment:0", shape=(?, ?), dtype=float32)
idxs = K.cumsum(s, axis=1) # (?,?) 在一个维度上累加
mask = idxs[:, None, :] <= idxs[:, :, None] # (?,?,?) (?,1,?), (?,?,1) None会加上一维
mask = K.cast(mask, K.floatx())
return mask[:, None] # (?,1,?,?)
对这个函数可以细细的研究和品位,因为做法实在是太优雅了,两行代码就实现了主要的功能。
拆分讲解:
1、输入s:是segment_ids,shape=(btz, seq_len)
eg:
[[0 0 0 ... 0 1 1 ... 1],
[0 0 0 ... 0 0 1 ... 1],
...
[0 0 0 ... 0 1 1 ... 1]]
2、idxs = K.cumsum(s, axis=1) # (?,?) 在一个维度上累加
cumsum函数的作用就是当前位置的值为前面值的累加,比如[1 2 3 4 5],经过cumsum后就是[1 3 6 10 15]
而这里的累加是为了s中为1的第二个句子的预测做准备,因为第二个句子是一个一个的预测出来
比如:
[[0 0 0 ... 0 1 2 3 4],
[0 0 0 ... 1 2 3 4 5],
...
[0 0 0 ... 0 1 2 3 4]]
3、mask = idxs[:, None, :] <= idxs[:, :, None]
首先要分别看idxs[:, None, :]和idxs[:, :, None] 都长什么样
- idxs[:, None, :]:在None的位置加一维=>shape=(btz,1,seg_len)
注意看这个矩阵和2中不太一样,多了一个[],也就是多了一维
eg:
[[[0 0 0 ... 0 1 2 3 4]],
[[0 0 0 ... 1 2 3 4 5]],
...
[[0 0 0 ... 0 1 2 3 4]]]
- idxs[:, :, None]:同理 shape=(btz,seq_len, 1)
[[[0]
[0]
[0]
[1]
[2]
[3]
[4]]
...
[[0]
[0]
[0]
[1]
[2]
[3]
[4]]]
最后得出mask的shape=(btz,seq_len,seq_len)
大概长这样(这里和1)、 2)不一一对应):
[[[T T T F F F]
[T T T F F F]
[T T T F F F]
[T T T T F F]
[T T T T T F]
[T T T T T T]]]
计算方法就是拿出2)中的每一行去和1)中的每一行做比较,最后得出的shape=(btz,seq_len,seq_len)
4、输出mask[:, None],此时shape=(btz, 1, seq_len, seq_len)
为什么要加一维后再输出呢?因为这是为后续做多头注意力做准备,在多头注意力中,q和k相乘后得到a的shape=(btz, heads, seq_len, seq_len),可以直接和mask做运算。
多头注意力的矩阵运算详解可见:【终于全面理解多维矩阵运算】 – 基于keras的MultiAttention实现及实例
我是卓师叔,欢迎关注,GitHub地址为:bert4keras_note
更多推荐
所有评论(0)