Press "Enter" to skip to content

AI = Seq2Seq+Attention+Transformer(简)

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

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

 

我们自己的普通数据集(常用)

 

主要使用tensorflow.keras.preprocessing这个库中的(image, text,sequence)这三个模块。

 

text: 可以用来 (统计词频,分字,word_2_id, id_2_word等操作。)

 

sequence 可以(给句子做结构化操作(填充0,裁剪长度))

 

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也写出来。

 

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 # 同上
)

 

 

思想

 

语言不同,那幺我们可以搭建桥梁。 
即使我们表面上不相同。 但是我们映射到这个桥梁上的结果是几乎类似的。

 

样本句子长度统一

 

为什幺每个句子的长度需要统一?

 

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

 

如何做到句子长度统一?

 

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

 

组成

 

 

    1. 编码器 (输入每条样本句子的每个单词, 编码器的最后一个RNN单元,浓缩了整个句子的信息)

 

    1. 中间向量 (作为中间特征桥梁, 用来保存,输入进来的整个句子)

 

    1. 解码器 (中间向量作为解码器第一个RNN单元的输入,而每个单元的输出y,作为下一个单元的输入)

 

 

其中解码器部分的输出y会用 softmax 对 词库(词典)求多分类概率。

 

然后求损失(MSE或者CrossEntropy)

 

注意了: softmax求出的概率该如何选择,这是个问题:

 

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

 

BeamSearch

 

由于贪心搜索(只取概率的一个最大值,的结果不尽人意。所以 BeamSearch来啦)

 

BeamSearch的主要思想:

 

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

 

 

(注意力机制)

 

前情回顾

 

Seq2Seq 的 Encoder部分虽然用的是 高效的 LSTM,并且也很好的解决了,记忆的问题。

 

但是他不能很好的解决每个单词的权重分配问题。

 

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

 

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

 

Attentioin原理

 

我觉得墨迹半天不如自己画一张图~~~ (只会mspaint画图)

 

 

上图中计算权重那里”通过一个函数,可以是求相似度”, 我简写了。 其实有两种常用的方式:

 

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 )

 

 

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

 

RNN-LSTM-GRU虽然这些NN的主要特色就是”时间序列”。(缺点:慢,记忆弥散)

 

但是我们上面说了,要想取得好的效果。那幺需要加Attention。

 

于是有人想到了,既然Attention效果这幺好,为什幺不直接用Attention呢?

 

Attention效果虽好,关联性强,但是它不能保证时间序列模式。

 

于是后来出现了 Transformer。(既能保证记忆注意力,又能保证时间序列)。具体如下!

 

Transformer整体结构组成

 

 

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/

Be First to Comment

发表评论

邮箱地址不会被公开。 必填项已用*标注