Tensorflow1.15实现Transformer(⼀):使⽤lf-attenti。。。
要学会⼀个算法,最好的办法还是⾃⼰复现⼀遍
这⾥也是对⾃⼰学习的过程做⼀个记录了o( ̄▽ ̄)ブ
尽量⽤最简洁的语⾔和最短的代码来实现⼀个Transformer,ViT,BERT,Swin Transformer
这篇主要实现了Transformer⾥⾯的Self-Attention,并实践了⽂本分类问题
Transformer介绍
Transformer结构是google在17年的Attention Is All You Need论⽂中提出,在NLP的多个任务上取得了⾮常好的效果,可以说⽬前NLP发展都离不开transformer。最⼤特点是抛弃了传统的CNN和RNN,整个⽹络结构完全是由Attention机制组成。
更准确地讲,Transformer由且仅由lf-Attenion和Feed Forward Neural Network组成。⼀个基于Transformer的可训练的神经⽹络可以通过堆叠Transformer的形式进⾏搭建,作者的实验是通过搭建编码器和解码器各6层,总共12层的Encoder-Decoder,并在机器翻译中取得了BLEU值得新⾼。
⾕歌团队近期提出的⽤于⽣成词向量的BERT[3]算法在NLP的11项任务中取得了效果的⼤幅提升,堪称2018年深度学习领域最振奋⼈⼼的消息。⽽BERT算法的最重要的部分便是本⽂中提出的Transformer的概念。
高一学习
参考资料:
这⾥就不怎么重复讨论Transformer的结构问题了,我们探讨下代码的实现问题
Embedding
Embedding⽬的主要是为了将⾃然语⾔处理中离散的单词转化为连续的变量,进⽽运⽤神经⽹络进⾏训练,具体的,是将单词转化为词向量,可以看作⼀种⽆监督聚类算法(或者应该是⾃监督学习?这⾥我不是很确定),单词语义相近的对应的词向量会⽐较相近
" Once again Mr. Costner has dragged out a movie for far longer than necessary. Aside from the terrific a rescue quences, of which there are very few I just did not care about any of the characters. Most of us have ghosts in the clot, and Costner’s character are realized early on, and then forgotten until much later, by which time I did not care. The character we should really care about is a very cocky, overconfident Ashton Kutcher. The problem is he comes off as kid who thinks
he’s better than anyone el around him and shows no signs of a cluttered clot. His only obstacle appears to be winning over Costner. Finally when we are well past the half way point of this stinker, Costner tells us all about
Kutcher’s ghosts. We are told why Kutcher is driven to be the best with no prior inkling or foreshadowing. No magic here, it was all I could do to keep from turning it off an hour in."
如何将这个内容转为⼀个词向量?
⾸先要有⼀个字典,字典有固定的长度,字典囊括了数据集中出现的词,词在字典中的位置按照词在数据集中出现的次数从⼤到⼩排列。⽐如这个字典中,‘the’在评论中出现次数最⼤,the放在字典的第⼀个位置上;‘and’出现的次数第⼆多,所以排在第⼆ …
评论为“I like this movie!”
‘I’在字典中的index为9;
‘like’在字典中的index为37;
‘this’‘在字典中的index为10;
‘movie’在字典中的index为16;
‘!’在字典中的index为28;
这个评论对应的词向量为[9 37 10 16 28]
我们会训练出⼀个矩阵,⼤⼩为[字典⼤⼩,词向量维度]
这个矩阵的每⼀个⾏向量,都是对应⼀个单词的词向量
class Embedding(Layer):
幼儿教师心得体会def__init__(lf,vocab_size,model_dim,**kwargs):
lf.vocab_size=vocab_sizewps连接
super(Embedding,lf).__init__(**kwargs)
def build(lf,input_shape):
夫妻相处七个原则
shape=(lf.vocab_del_dim),
initializer="glorot_uniform",
trainable=True,
name="embeddings"
)
super(Embedding,lf).build(input_shape)
def call(lf,inputs):#其实就是简单的取个⾏向量出来
inputs=tf.cast(inputs,tf.int32)
embeddings=tf.beddings,inputs)
embeddings*=lf.model_dim**0.5#暂时不清楚为什么return embeddings
def get_config(lf):
config=super(Embedding,lf).get_config()
config.update({
"vocab_size":lf.vocab_size,
"model_dim":lf.model_dim
})
return config PositionEncoding
区别如RNN,Transformer模型并没有捕捉顺序序列的能⼒,也就是说⽆论句⼦的结构怎么打乱,Transformer都会得到类似的结果。换句话说,Transformer只是⼀个功能更强⼤的词袋模型⽽已。
为了解决这个问题,论⽂中在编码词向量时引⼊了位置编码(Position Embedding)的特征。具体地说,位置编码会在词向量中加⼊了单词的位置信息,这样Transformer就能区分不同位置的单词了。
那么怎么编码这个位置信息呢?常见的模式有:a. 根据数据学习;b. ⾃⼰设计编码规则。在这⾥作者
采⽤了第⼆种⽅式(⽽在ViT中采⽤了第⼀种⽅式)。那么这个位置编码该是什么样⼦呢?通常位置编码是⼀个长度为 model_dim 的特征向量,这样便于和词向量进⾏单位加的操作
论⽂给出的编码公式如下:
在上式中, pos表⽰单词的位置, i 表⽰单词的维度。作者这么设计的原因是考虑到在NLP任务中,除了单词的绝对位置,单词的相对位置也⾮常重要。
松鼠的简笔画
根据公式
以及
这表明位置 K+P的位置向量可以表⽰为位置 K 和P的特征向量的线性变化,这为模型捕捉单词之间的
相对位置关系提供了⾮常⼤的便利可视化编码的结果可以得到如下图像:
代码实现就是公式所⽰
class PositionEncoding(Layer):
def__init__(lf,**kwargs):
super(PositionEncoding,lf).__init__(**kwargs)
def build(lf,input_shape):
def get_position_encoding(q_len,model_dim):
position_s(shape=(q_len,model_dim))
for pos in range(q_len):
for i in range(model_dim):
position_encoding[pos,i]=pos/(np.power(10000,2*i/model_dim))
position_encoding[::,::2]=np.sin(position_encoding[::,::2])
position_encoding[::,1::2]=np.cos(position_encoding[::,1::2])
pand_dims(position_encoding,axis=0)
q_len,model_dim=input_shape.as_list()[1:3]
lf.position_encoding=lf.add_weight(
shape=(1,q_len,model_dim),
initializer=Constant(get_position_encoding(q_len,model_dim)),
trainable=Fal,
name="position_encoding"
)
super(PositionEncoding,lf).build(input_shape)
def call(lf,inputs):
return lf.position_encoding扒皮鱼的做法
ScaledDotProductAttention
这⼀步就是实现SelfAttention过程
具体内容请参阅开头的阅读资料
Attention的计算⽅法,整个过程可以分成7步:
如上⽂,将输⼊单词转化成嵌⼊向量;
根据嵌⼊向量得到 q,k,v 三个向量;
为每个向量计算⼀个score=q·k
为了梯度的稳定,Transformer使⽤了score归⼀化,即除以 model_dim**0.5
对score施以softmax激活函数;
softmax点乘Value值 ,得到加权的每个输⼊向量的评分
相加之后得到最终的输出结果
每个 Query 序列对应着⼀个 Key 序列,但这 Query-Key 组合彼此之间是独⽴的。完全可以将 Query、Key、Value 堆叠成批,⼀次运算搞定。矩阵乘法或是转置是针对最后的两个维度,所以只需要保持前置维度匹配(对应,下⽅注释的要求1.),计算结果和上⾯完全等效。
Mask(填充遮挡)
如果⼀个输⼊句⼦由于长短不⼀不⽅便计算或是其他原因需要补充⼀些填充标记(pad tokens),显然在输出结果的时候应该把这些⽆意义的填充标记排除,因此需要⼀个函数产⽣此⽤途的 mask
mask 以乘以⼀个极⼤的负数-1e9,然后在加上注意⼒权重,最终达到使⼀些位置的 Value 失效的效果
这⾥tensor在乘以mask后紧跟着就是softmax,⼀个极⼤的负数经过softmax后会变为0
def masks(lf,inputs,masking):
masking=tf.cast(masking,tf.float32)
masking=tf.tile(masking,[tf.shape(inputs)[0]//tf.shape(masking)[0],1])
一个句一个鸟#因为MultiHeadAttention的问题,masking的长度和inputs
#长度可能并不等价,⽽是倍数关系,具体为n_head倍
pand_dims(masking,axis=1)
中国有什么大学
outputs=inputs+masking*lf.masking_num
#乘以⼀个很⼤的负数,⽬的是为了让当前位置的数值失效
return outputs
Lookahead Mask(前瞻遮挡)