AI => Seq2Seq+Attention+Transformer(简)

数据预处理(TF20-Keras-Preprocessing)

咱们本身的普通数据集(经常使用)

主要使用tensorflow.keras.preprocessing这个库中的(image, text,sequence)这三个模块。
text: 能够用来 (统计词频,分字,word_2_id, id_2_word等操做。)
sequence 能够(给句子作结构化操做(填充0,裁剪长度))git

from tensorflow.keras.preprocessing.text import Tokenizer    # 主干,句子编码
from tensorflow.keras.preprocessing.sequence import pad_sequences # 辅助,填充,剪枝

q1 = '欢 迎 你 你 你'
q2 = '我 很 好'
q_list = [q1,q2]    # 须要特别注意,由于此API对英文友好,因此,咱们必须把句子用 空格 隔开输入

token = Tokenizer(
    num_words=2, # num_words表明设置过滤num_words-1个词频, 例如num_words=2,
                 # 那么过滤掉2-1=1个词频, 因此一会你会看到下面词频为1的都被过滤掉了
)  # 这里面参数不少,还有标点符号过滤器等
token.fit_on_texts(q_list)  # 把原始句子集合,放进去拟合一下(封装成一个类)

print(token.document_count) # 2    # 句子个数
print(token.word_index)  # {'你': 1, '欢': 2, '迎': 3, '我': 4, '很': 5, '好': 6}   # word_2_id
print(token.index_word)  # {1: '你', 2: '欢', 3: '迎', 4: '我', 5: '很', 6: '好'}   # id_2_word
print(token.word_counts) # OrderedDict([('欢', 1), ('迎', 1), ('你', 3), ('我', 1), ('很', 1), ('好', 1)])  # 统计词频

seq = token.texts_to_sequences(q_list) # 先把全部的输入,变成一一变成编码化
print(seq) # [[1, 1, 1], []]     # 会不会好奇?数据怎么没了?由于咱们上面设置了过滤词频为1的都过滤了

pad_seq = pad_sequences(
        seq,                # 输入编码化后的 句子
        maxlen=2,           # 统一句子最大长度
        padding='pre',      # 不足的补0, 从前面补0, (也能够用 post,表明后面)
        truncating='pre'    # 多余的长度裁剪,从前面裁剪
    )
print(pad_seq)     # 打印一下咱们填充后的句子形状。
# [
#   [1 1],      # 如你所愿,最大长度为2,[1,1,1] 已经裁剪成了 [1,1]
#   [0 0],      # 如你所愿,以前[] ,已经都填满了0
# ]

虽然咱们用不到 image这个模块数据加强模块,可是我把了解的API也写出来。github

train_datagen = keras.preprocessing.image.ImageDataGenerator( # 数据加强生成器(定义)
    rescale=1. / 255,         # 数据归一化
    rotation_range = 40,      #  -40-40  随机角度 (数据加强)
    width_shift_range = 0.2,  # 宽度位移(0-20%随机选个比例去平移) (数据加强)
    height_shift_range = 0.2, # 高度位移(同上)   (数据加强)
    shear_range=0.2,          # 图片剪切(0.2)    (数据加强)
    zoom_range=0.2,           # 图片缩放(0.2)    (数据加强)
    horizontal_flip=True,     # 图片随机水平反转    (数据加强)
    fill_mode='nearest',      # 图片填充像素(放大后失帧)用附近像素值来填充 (数据加强)
)

# train_generator = train_datagen.flow_from_dataframe()  # 若是你用Pandas,你能够选这个
train_generator = train_datagen.flow_from_directory(     # 从文件中读取(Kaggle)
    train_dir,                       # 图片目录
    target_size = (height, width),   # 图片读取进来后缩放大小
    batch_size = batch_size,         # 就是批次
    seed=6,                          # 随机化种子
    shuffle=True,                    # 样本随机打散训练,加强模型泛化能力
    class_mode='categorical',        # label格式,是否须要one_hot, 是
)
...
...
train_num = train_generator.samples  # 打印样本形状

history = model.fit_generator(       # 注意咱们上面是用的数据生成器,因此这要用 fit_generator
    train_generator,
    steps_per_epoch=train_num//batch_size, # 每一个epoch多少 step(由于数据加强API是生成器方式,因此须要本身手动计算一下)
    epochs=epochs,
    validation_data=valid_generator,  # 若是你有验证集,你也能够用这个。不然能够不用
    validation_steps=valid_num//batch_size # 同上
)

Seq2Seq

思想

语言不一样,那么咱们能够搭建桥梁。 
即便咱们表面上不相同。 可是咱们映射到这个桥梁上的结果是几乎相似的。

样本句子长度统一

为何每一个句子的长度须要统一?算法

由于,每一个句子for循环操做会很耗算力, 而转化为矩阵/向量化操做,会节约太多算力。
由于矩阵运算严格要求样本的形状,因此每一个句子的长度须要一致

如何作到句子长度统一?函数

填0, 对应TF操做就是padding, 不过TF20 的keras预处理包中已经有 成品的数据统一化操做。
而且还具备 word_2_id,词向量编码操做。

组成

  1. 编码器 (输入每条样本句子的每一个单词, 编码器的最后一个RNN单元,浓缩了整个句子的信息)
  2. 中间向量 (做为中间特征桥梁, 用来保存,输入进来的整个句子)
  3. 解码器 (中间向量做为解码器第一个RNN单元的输入,而每一个单元的输出y,做为下一个单元的输入)

其中解码器部分的输出y会用 softmax 对 词库(词典)求多分类几率。
而后求损失(MSE或者CrossEntropy)
注意了: softmax求出的几率该如何选择,这是个问题:post

假如: 每一个单元的输出y的几率都取最大值, 那么可能一步错,步步错。 太极端了(贪心搜索)
接下来,聊一聊一周 集束搜索的算法 BeamSearch

BeamSearch

因为贪心搜索(只取几率的一个最大值,的结果不尽人意。因此 BeamSearch来啦)
BeamSearch的主要思想:编码

只取一个太冒险了,因此:     BeamSearch 取每一个通过softmax输出几率集合的 Top-N个
Top-N: 的 N 表明你保留几个几率   (触类旁通理解: 贪心算法就是 Top-1)
假如咱们取Top-3个
那么你一个RNN节点的预测y将会保留3个几率值, 并将这3个几率值做为 下一个节点的输入。
具体流程看:下图 (可能有点丑)
而后,咱们会选择出:        3 个 "红线" 最优路径。
最终: 咱们经过单独的语言模型,来从这 3 个 "红线" 较优路径中,选出一个 最优路径。

clipboard.png

Attention(注意力机制)

前情回顾

Seq2Seq 的 Encoder部分虽然用的是 高效的 LSTM,而且也很好的解决了,记忆的问题。
可是他不能很好的解决每一个单词的权重分配问题。spa

虽然: Encoder的全部单元都会经过LSTM的记忆传递, 输入进“中间桥梁向量”。
可是: 仍是有"偏爱"成分, 最后一个LSTM单元信息必定是最浓的。 (新鲜的,热乎的)
因此: 你第1个LSTM单元的信息,或者说前面的LSTM单元的信息,这些记忆到最后可能会被稀释。

为了解决上面的问题, Attention就出来帮忙了~~~3d

Attentioin原理

我以为墨迹半天不如本身画一张图~~~ (只会mspaint画图)
clipboard.png
上图中计算权重那里"经过一个函数,能够是求类似度", 我简写了。 其实有两种经常使用的方式:rest

Bahdanau注意力:
    weight = FC层( tanh ( FC层(Encoder的每一个输出y) + FC层(Decoder的一个H) ) )
luong注意力:
    weight = Encoder的每一个输出y @ W随机权重矩阵 @ Decoder的一个H    # @是TF20的矩阵乘法操做符
不管使用上面哪一种: 都要套一层 Softmax
    weight = softmax(weight, axis=1)
    注意力向量C = sum( weight * Encoder的每一个输出y , axis=1)   # 加权求和,最终获得一个向量
    Decoder的下一个输入 = concat( 注意力向量C, 上一个预测y4 )

Transformer

第一印象挑明: 他是一种无RNN的一种特殊的 Seq2Seq 模型。code

RNN-LSTM-GRU虽然这些NN的主要特点就是"时间序列"。(缺点:慢,记忆弥散)
可是咱们上面说了,要想取得好的效果。那么须要加Attention。
因而有人想到了,既然Attention效果这么好,为何不直接用Attention呢?
Attention效果虽好,关联性强,可是它不能保证时间序列模式。
因而后来出现了 Transformer。(既能保证记忆注意力,又能保证时间序列)。具体以下!

Transformer总体结构组成

image.png

Self-Attention

self-attention原理就是各类链式矩阵乘法(并行计算,可用GPU加速)
self-attention计算过程以下:(假设输入句子切分单词为:矩阵X = ["早","上","好"])

矩阵X @ 权重矩阵Q(Q1,Q2,Q3)=> Q矩阵(Q1,Q2,Q3)
矩阵X @ 权重矩阵K(Q1,Q2,Q3)=> K矩阵(Q1,Q2,Q3)
矩阵X @ 权重矩阵V(Q1,Q2,Q3)=> V矩阵(Q1,Q2,Q3)

α = softmax( (Q矩阵 @ K矩阵) / q^0.5 )
self_attention = α @ V矩阵

Multi-Head Self-Attention

Multi-Head Attention 对 Self-Attention 对了以下扩展:

self-attention:             一组 Q矩阵,K矩阵,V矩阵 
Multi-Head Self-Attention:  多组 Q矩阵,K矩阵,V矩阵
扩张为多头注意力的过程:
    Q @ W ====> [Q1, Q2, Q3]
    K @ W ====> [K1, K2, K3]
    V @ W ====> [V1, V2, V3]

可理解为,多个卷积核的意思能提取不一样特征的意思。

Position Encoder

上述的self-attention有个问题, 咱们没有用到RNN等序列NN,那么矩阵相乘的过程当中。
单词的计算顺序多是不一样的。
那么如何保证让他们位置有条不紊?
可使用位置编码,融入到Embedding,造成带有时间序列性质的模型。
可自行查找计算位置编码的博文。

传送门

至于Transformer,如今官方已经有TF20和Pytorch的库了。
传送门以下。
https://github.com/huggingface/transformers
Transformer延申的各类模型,像Bert等也有可调用的API
https://huggingface.co/transformers/

相关文章
相关标签/搜索