Transformer
Transformer 是 2017 年引入的深度学习模型,主要用于自然语言处理领域。与循环神经网络一样,Transformers 旨在处理顺序数据(例如自然语言),以执行翻译和文本摘要等任务。但是,与 RNN 不同,Transformers 不需要按顺序处理顺序数据。
Why do we need the Transformer?
- RNN
- 是最经典的处理 Sequence 的模型,单向 RNN 或双向 RNN 等等。
- RNN 存在的问题:难以并行化处理
于是,就有人提出用 CNN 取代 RNN
-
CNN 替代 RNN
- CNN filters:每一个三角形代表一个
filter
输入为seq
的一小段,输出一个数值(做内积得到),不同的filter
对应seq
中不同的部分。
- 每一个 CNN 只能考虑有限的内容,RNN 能考虑一整个句子
- 考虑很长的句子:叠很多层 CNN,上层的
filter
就可以考虑较多资讯,因为上层的filter会把下层的filter的输出当作输入 - 问题:必须叠很多层才有办法做到考虑较长的句子,因此出现了
self-attention
机制
- CNN filters:每一个三角形代表一个
self-attention layer
例如,输入
x
1
,
x
2
,
x
3
,
x
4
x_1,x_2,x_3,x_4
x1,x2,x3,x4 通过 embedding
得到
a
1
,
a
2
,
a
3
,
a
4
a_1,a_2,a_3,a_4
a1,a2,a3,a4,每一个 input
都分别乘以三个不同的 transformation(矩阵)得到三个不同的 vector
q
,
k
,
v
q,k,v
q,k,v。
- q q q: query (to match others)
q i = W q a i q^i = W^q a^i qi=Wqai
- k k k: key (to be matched)
k i = W k a i k^i = W^k a^i ki=Wkai
- v v v: information to be extracted
v i = W v a i v^i = W^v a^i vi=Wvai
拿每个 query
q
q
q 去对每个 key
k
k
k 做 attention,文中使用 Scaled Dot-Product Attention
:
α
1
,
i
=
q
1
⋅
k
i
/
d
\alpha_{1,i} = q^1 \cdot k^i / \sqrt{d}
α1,i=q1⋅ki/d
其中,
⋅
\cdot
⋅ 表示 dot product,
d
d
d 是
q
q
q 的维度,能够抵消因维度引起的不平衡。
然后,再进行 softmax
进行激活
α
^
1
,
i
=
exp
(
α
1
,
i
)
∑
j
exp
(
α
1
,
i
)
\hat{\alpha}_{1,i} = \frac{\exp \left( \alpha_{1,i} \right)}{\sum\limits_j \exp\left( \alpha_{1,i} \right)}
α^1,i=j∑exp(α1,i)exp(α1,i)
拿每个 query
q
q
q 去对每个 key k 做 attention
b
1
=
∑
i
α
^
1
,
i
v
i
b^1 = \sum\limits_i \hat{\alpha}_{1,i} v^i
b1=i∑α^1,ivi
这样, b 1 , b 2 , b 3 , b 4 b^1,b^2,b^3,b^4 b1,b2,b3,b4 可以并行计算得到。
接下来,使用矩阵运算来说明 self-attention layer
的过程:
- Step 1:
- Step 2:
-
Step 3:
-
Step 4:
于是,可得
Attention
(
Q
,
K
,
V
)
=
softmax
(
Q
K
T
d
k
)
V
\text{Attention}(Q,K,V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
Attention(Q,K,V)=softmax(dkQKT)V
如下图所示:
反正就是一堆矩阵乘法,可以用 GPU 可以加速
Multi-head Self-attention
Multi-head Self-attention
允许模型共同注意来自不同位置的不同表示子空间的信息。不同的 head
可以各司其职,学习到不同意义的特征(如 local 的或者 global 的)。
MultiHead
(
Q
,
K
,
V
)
=
Concat
(
head
1
,
⋯
,
head
h
)
W
O
\text{MultiHead}(Q,K,V) = \text{Concat}(\text{head}_1,\cdots,\text{head}_h) W^O
MultiHead(Q,K,V)=Concat(head1,⋯,headh)WO
其中
head
i
=
Attention
(
Q
W
i
Q
,
K
W
i
K
,
V
W
i
V
)
\text{head}_i = \text{Attention}(QW_i^Q,KW_i^K, VW_i^V)
headi=Attention(QWiQ,KWiK,VWiV)
下图是 2 head 的注意力图
得到
b
i
,
1
,
b
i
,
2
b^{i,1},b^{i,2}
bi,1,bi,2 可以通过 concatenate
进行拼接,还可以引入
W
W
W 进行维度的变化
Positional Encoding
- 原始的
self-attention
中没有考虑位置信息,所以可以引入一个的位置向量 e i e_i ei,不是学出来的而是人设置的。 - 其他方法:使用
one-hot encoding
表示的 p i p_i pi 为 x i x_i xi 表示其位置
具体地,可以将
x
x
x 和
p
p
p 进行 concatenate
或者上图直接在
a
i
a^i
ai 中加入
e
i
e^i
ei。
keras
官方给出的 embedding layer
实现如下
class TokenAndPositionEmbedding(layers.Layer):
def __init__(self, maxlen, vocab_size, embed_dim):
super(TokenAndPositionEmbedding, self).__init__()
self.token_emb = layers.Embedding(input_dim=vocab_size, output_dim=embed_dim)
self.pos_emb = layers.Embedding(input_dim=maxlen, output_dim=embed_dim)
def call(self, x):
maxlen = tf.shape(x)[-1]
positions = tf.range(start=0, limit=maxlen, delta=1)
positions = self.pos_emb(positions)
x = self.token_emb(x)
return x + positions
Seq2seq Architecture
原始 seq2seq2 model
由两个 RNN 分别组成了 Encoder、Decoder,可以应用于机器翻译。
上图中原本 Encoder 里面是双向 RNN,Decoder 里面是一个单向 RNN,下图把两个都用 Self-attention layer
取代,可以到达一样的目的而且可以平行运算。
一个动画形象地刻画这一过程
Transformer
下图是 transformer 的架构图:
-
Encoder
-
Input
通过Input Embedding
,考虑位置资讯,加上人为设置的Positional Encoding
,进入会重复 $N $次的block
Multi-head
:进到 Encoder 里面,他是 Multi-head Attention的,也就是 q , k , v q,k,v q,k,v 有多个,在里面做 q k v qkv qkv 个别乘以 a a a 的运算,算出 α \alpha α 最后得到 b b b
Add & Norm
: 把Multi-head attention
的input
a a a 和output
b b b 加起来得到 b ′ b' b′,再做Layer Normalization
- 计算完后丢到前向传播,再经过一个
Add & Norm
-
-
Decoder
input
为前一个 time step 的output
,通过output embedding
,考虑位置资讯,加上人为设置的positional encoding
,进入会重复 n n n 次的block
Masked Multi-head Attention
:做Attention,Masked 表示只会 attend 到已经产生出来的 sequence,再经过Add & Norm Layer
- 再经过
Multi-head Attention Layer
,attend 到之前 Encoder 的输出,然后再进到Add & Norm Layer
- 计算完丢到 Feed Forward 前向传播,再做
Linear
和Softmax
,产生最终输出
Transformer block
实现如下:
class TransformerBlock(layers.Layer):
def __init__(self, embed_dim, num_heads, ff_dim, rate=0.1):
super(TransformerBlock, self).__init__()
self.att = layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim)
self.ffn = keras.Sequential(
[layers.Dense(ff_dim, activation="relu"), layers.Dense(embed_dim),]
)
self.layernorm1 = layers.LayerNormalization(epsilon=1e-6)
self.layernorm2 = layers.LayerNormalization(epsilon=1e-6)
self.dropout1 = layers.Dropout(rate)
self.dropout2 = layers.Dropout(rate)
def call(self, inputs, training):
attn_output = self.att(inputs, inputs)
attn_output = self.dropout1(attn_output, training=training)
out1 = self.layernorm1(inputs + attn_output)
ffn_output = self.ffn(out1)
ffn_output = self.dropout2(ffn_output, training=training)
return self.layernorm2(out1 + ffn_output)
下载和准备数据集:
vocab_size = 20000 # Only consider the top 20k words
maxlen = 200 # Only consider the first 200 words of each movie review
(x_train, y_train), (x_val, y_val) = keras.datasets.imdb.load_data(num_words=vocab_size)
print(len(x_train), "Training sequences")
print(len(x_val), "Validation sequences")
x_train = keras.preprocessing.sequence.pad_sequences(x_train, maxlen=maxlen)
x_val = keras.preprocessing.sequence.pad_sequences(x_val, maxlen=maxlen)
使用 transformer 创建分类器模型
embed_dim = 32 # Embedding size for each token
num_heads = 2 # Number of attention heads
ff_dim = 32 # Hidden layer size in feed forward network inside transformer
inputs = layers.Input(shape=(maxlen,))
embedding_layer = TokenAndPositionEmbedding(maxlen, vocab_size, embed_dim)
x = embedding_layer(inputs)
transformer_block = TransformerBlock(embed_dim, num_heads, ff_dim)
x = transformer_block(x)
x = layers.GlobalAveragePooling1D()(x)
x = layers.Dropout(0.1)(x)
x = layers.Dense(20, activation="relu")(x)
x = layers.Dropout(0.1)(x)
outputs = layers.Dense(2, activation="softmax")(x)
model = keras.Model(inputs=inputs, outputs=outputs)
训练和评估
model.compile("adam", "sparse_categorical_crossentropy", metrics=["accuracy"])
history = model.fit(
x_train, y_train, batch_size=32, epochs=2, validation_data=(x_val, y_val)
)
打印信息如下:
Epoch 1/2
782/782 [==============================] - 15s 18ms/step - loss: 0.5112 - accuracy: 0.7070 - val_loss: 0.3598 - val_accuracy: 0.8444
Epoch 2/2
782/782 [==============================] - 13s 17ms/step - loss: 0.1942 - accuracy: 0.9297 - val_loss: 0.2977 - val_accuracy: 0.8745
Attention Visualization
single head
multi-head
Example Application
- Summarizer By Google
input
为一堆文档,output
为一篇文章(summarize)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fEMpvaW3-1612535321252)(https://arxiv.org/abs/1801.10198 “Generating Wikipedia by Summarizing Long Sequences”)]](https://cdn.jsdelivr.net/gh/ZhouKanglei/jidianxia/2021-2-5/1612530043763-example.png)
- Universal Transformer
横向(时间上)是Transformer,纵向是RNN
- Self-Attention GAN
也可以用在影像生成上