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/

相關文章
相關標籤/搜索