自然語(yǔ)言處理N天-從seq2seq到Transformer01

新建 Microsoft PowerPoint 演示文稿 (2).jpg

這個(gè)算是在課程學(xué)習(xí)之外的探索晰筛,不過(guò)希望能盡快用到項(xiàng)目實(shí)踐中谨究。在文章里會(huì)引用較多的博客,文末會(huì)進(jìn)行reference夫晌。
搜索Transformer機(jī)制雕薪,會(huì)發(fā)現(xiàn)高分結(jié)果基本上都源于一篇論文Jay Alammar的《The Illustrated Transformer》(圖解Transformer),提到最多的Attention是Google的《Attention Is All You Need》晓淀。

  • 對(duì)于Transformer的運(yùn)行機(jī)制了解即可蹦哼,所以會(huì)基于這篇論文來(lái)學(xué)習(xí)Transformer,結(jié)合《Sklearn+Tensorflow》中Attention注意力機(jī)制一章完成基本的概念學(xué)習(xí)要糊;
  • 找一個(gè)基于Transformer的項(xiàng)目練手

0.引言

Transformer由論文《Attention is All You Need》提出纲熏。Transformer和傳統(tǒng)RNN的主要區(qū)別
1.傳統(tǒng)RNN是通過(guò)不斷循環(huán)完成學(xué)習(xí)妆丘,通過(guò)每次迭代后的輸出實(shí)現(xiàn)對(duì)上下文的記憶功能,這樣才有了LSTM和GRU模型局劲,因?yàn)樗鼈兡軌蜉^好處理RNN梯度爆炸和梯度消失問(wèn)題勺拣,通過(guò)對(duì)各類門(mén)的操作實(shí)現(xiàn)“記憶”。但是問(wèn)題是RNN的訓(xùn)練非常緩慢鱼填,之前實(shí)現(xiàn)作詩(shī)軟件的那篇文章药有,一個(gè)2層的LSTM我用I7處理器跑了將近8個(gè)小時(shí)……
2.Transformer不需要循環(huán),通過(guò)構(gòu)建self-attention機(jī)制來(lái)完成對(duì)上下文和距離較遠(yuǎn)詞匯的結(jié)合苹丸,并且通過(guò)并行進(jìn)行處理愤惰,讓每個(gè)單詞在多個(gè)處理步驟中注意到句子中的其他單詞,Transformer 的訓(xùn)練速度比 RNN 快很多赘理,而且其翻譯結(jié)果也比 RNN 好得多宦言。但是在處理小型結(jié)構(gòu)化的語(yǔ)言理解任務(wù)或是簡(jiǎn)單算法任務(wù)時(shí)表現(xiàn)不如傳統(tǒng)模型。

現(xiàn)在Transformer已經(jīng)被擴(kuò)展為一個(gè)通用模型

研究者將該模型建立在 Transformer 的并行結(jié)構(gòu)上商模,以保持其快速的訓(xùn)練速度奠旺。但是他們用單一、時(shí)間并行循環(huán)的變換函數(shù)的多次應(yīng)用代替了 Transformer 中不同變換函數(shù)的固定堆疊(即施流,相同的學(xué)習(xí)變換函數(shù)在多個(gè)處理步驟中被并行應(yīng)用于所有符號(hào)响疚,其中每個(gè)步驟的輸出饋入下一個(gè))。RNN 逐個(gè)符號(hào)(從左到右)處理序列瞪醋,而 Universal Transformer 同時(shí)處理所有符號(hào)(像 Transformer 一樣)忿晕,然后使用自注意力機(jī)制在循環(huán)處理步驟(步驟數(shù)量可變)上,對(duì)每個(gè)符號(hào)的解釋進(jìn)行改進(jìn)银受。這種時(shí)間并行循環(huán)機(jī)制比 RNN 中使用的順序循環(huán)(serial recurrence)更快践盼,也使得 Universal Transformer 比標(biāo)準(zhǔn)前饋 Transformer 更強(qiáng)大。

Transformer模型能做什么蚓土?
目前最火的那個(gè)帖子認(rèn)為T(mén)ransformer已經(jīng)可以完成大多數(shù)的任務(wù)宏侍。這個(gè)也是這幾天我要探究的赖淤。

1.從Encoder到Decoder實(shí)現(xiàn)Seq2Seq模型

本節(jié)主要記錄了seq2seq模型基本概念和encoder和decoder層的構(gòu)建
本文來(lái)源《從Encoder到Decoder實(shí)現(xiàn)Seq2Seq模型》
采用seq2seq框架來(lái)實(shí)現(xiàn)MT(機(jī)器翻譯)現(xiàn)在已經(jīng)是一個(gè)非常熱點(diǎn)的研究方向蜀漆,各種花式設(shè)計(jì)歸根離不開(kāi)RNN、CNN同時(shí)輔以attention咱旱。但是正如上文所說(shuō)的确丢,對(duì)于NLP來(lái)講不論是RNN還是CNN都存在其固有的缺陷,即使attention可以緩解長(zhǎng)距依賴的問(wèn)題吐限。
因此我覺(jué)得可以先從seq2seq開(kāi)始鲜侥,在這里觀察一個(gè)簡(jiǎn)單的Seq2Seq,使用TensorFlow來(lái)實(shí)現(xiàn)一個(gè)基礎(chǔ)版本的Seq2Seq诸典,主要幫助理解Seq2Seq中的基礎(chǔ)架構(gòu)描函。

最基礎(chǔ)的Seq2Seq模型包含三個(gè)部分:Encoder、Decoder以及連接兩者固定大小的State Vector
Encoder通過(guò)學(xué)習(xí)輸入舀寓,將其編碼成一個(gè)固定大小的狀態(tài)向量S胆数,繼而將S傳給Decoder,Decoder再通過(guò)對(duì)狀態(tài)向量S的學(xué)習(xí)來(lái)進(jìn)行輸出互墓。
下面利用TensorFlow來(lái)構(gòu)建一個(gè)基礎(chǔ)的Seq2Seq模型必尼,通過(guò)向我們的模型輸入一個(gè)單詞(字母序列),例如hello篡撵,模型將按照字母順序排序輸出判莉,即輸出ehllo。

1)數(shù)據(jù)集

數(shù)據(jù)集包括兩個(gè)部分育谬,sorce_data和target_data券盅,放置在datasets/seq2seqdata文件夾下

  • sorce_data:每一行是一個(gè)單詞
  • target_data:: 每一行是經(jīng)過(guò)字母排序后的“單詞”,它的每一行與source_data中每一行一一對(duì)應(yīng)

2)讀取數(shù)據(jù)和預(yù)處理

在神經(jīng)網(wǎng)絡(luò)中斑司,對(duì)于文本的數(shù)據(jù)預(yù)處理無(wú)非是將文本轉(zhuǎn)化為模型可理解的數(shù)字渗饮。
在這里加入以下四種字符

  • <PAD>主要用來(lái)進(jìn)行字符補(bǔ)全
  • <EOS>和<GO>都是用在Decoder端的序列中,告訴解碼器句子的起始與結(jié)束
  • <UNK>則用來(lái)替代一些未出現(xiàn)過(guò)的詞或者低頻詞宿刮。
import numpy as np
import time
import tensorflow as tf

with open(r'C:\\Users\\01\\Desktop\\機(jī)器學(xué)習(xí)作業(yè)\\sklearn+tensorflow\\datasets\\seq2seqdata\\letters_source.txt', 'r',
          encoding='utf-8') as f:
    source_data = f.read()
with open(r'C:\\Users\\01\\Desktop\\機(jī)器學(xué)習(xí)作業(yè)\\sklearn+tensorflow\\datasets\\seq2seqdata\\letters_target.txt', 'r',
          encoding='utf-8') as f:
    target_data = f.read()

print(source_data.split('\\n')[:10])
print(target_data.split('\\n')[:10])


def extract_character_vocab(data):
    special_words = ['<PAD>', '<UNK>', '<GO>', '<EOS>']
    set_words = list(set([character for line in data.split('\n') for character in line]))
    int_to_vocab = {idx: word for idx, word in enumerate(special_words + set_words)}
    vocab_to_int = {word: idx for idx, word in int_to_vocab.items()}

    return int_to_vocab, vocab_to_int


# 構(gòu)造映射表互站,就是將source和target的每一個(gè)字母轉(zhuǎn)為數(shù)值
source_int_to_letter, source_letter_to_int = extract_character_vocab(source_data)
target_int_to_letter, target_letter_to_int = extract_character_vocab(target_data)

source_int = [[source_letter_to_int.get(letter, source_letter_to_int['<UNK>']) for letter in line] for line in
              source_data.split('\n')]
target_int = [[target_letter_to_int.get(letter, target_letter_to_int['<UNK>']) for letter in line] for line in
              target_data.split('\n')]

print(source_int[:10])
print(target_int[:10])

3)模型構(gòu)建

如上文所述,模型包括兩個(gè)部分僵缺,Encoder和Decoder胡桃。
在Encoder層,首先需要對(duì)定義輸入的tensor磕潮,同時(shí)要對(duì)字母進(jìn)行Embedding翠胰,再輸入到RNN層。使用TensorFlow中的tf.contrib.layers.embed_sequence來(lái)對(duì)輸入進(jìn)行embedding自脯。

輸入層

def get_inputs():
    '''
    模型輸入tensor
    '''
    inputs = tf.placeholder(tf.int32, [None, None], name='inputs')
    targets = tf.placeholder(tf.int32, [None, None], name='targets')
    learning_rate = tf.placeholder(tf.float32, name='learning_rate')
    
    # 定義target序列最大長(zhǎng)度(之后target_sequence_length和source_sequence_length會(huì)作為feed_dict的參數(shù))
    target_sequence_length = tf.placeholder(tf.int32, (None,), name='target_sequence_length')
    max_target_sequence_length = tf.reduce_max(target_sequence_length, name='max_target_len')
    source_sequence_length = tf.placeholder(tf.int32, (None,), name='source_sequence_length')
    
    return inputs, targets, learning_rate, target_sequence_length, max_target_sequence_length, source_sequence_length

Encoder
參數(shù)說(shuō)明:

  • input_data: 輸入tensor
  • rnn_size: rnn隱層結(jié)點(diǎn)數(shù)量
  • num_layers: 堆疊的rnn cell數(shù)量
  • source_sequence_length: 源數(shù)據(jù)的序列長(zhǎng)度
  • source_vocab_size: 源數(shù)據(jù)的詞典大小
  • encoding_embedding_size: embedding的大小
def get_encoder_layer(input_data, rnn_size, num_layers,
                      source_sequence_length, source_vocab_size,
                      encoding_embedding_size):
    encoder_embed_input = tf.contrib.layers.embed_sequence(input_data, source_vocab_size, encoding_embedding_size)

    # RNN cell
    def get_lstm_cell(rnn_size):
        lstm_cell = tf.contrib.rnn.LSTMCell(rnn_size, initializer=tf.random_uniform_initializer(-0.1, 0.1, seed=2))
        return lstm_cell

    cell = tf.contrib.rnn.MultiRNNCell([get_lstm_cell(rnn_size) for _ in range(num_layers)])
    encoder_output, encoder_state = tf.nn.dynamic_rnn(cell, encoder_embed_input, sequence_length=source_sequence_length,
                                                      dtype=tf.float32)

    return encoder_output, encoder_state

Decoder
在Decoder端主要要完成以下幾件事情:

  • 對(duì)target數(shù)據(jù)進(jìn)行處理
  • 構(gòu)造Decoder
    • Embedding
    • 構(gòu)造Decoder
    • 構(gòu)造輸出層之景,輸出層會(huì)告訴我們每個(gè)時(shí)間序列的RNN輸出結(jié)果
    • 訓(xùn)練Decoder
    • 預(yù)測(cè)Decoder

target數(shù)據(jù)進(jìn)行處理
target數(shù)據(jù)有兩個(gè)作用:

  • 在訓(xùn)練過(guò)程中,需要將的target序列作為輸入傳給Decoder端RNN的每個(gè)階段膏潮,而不是使用前一階段預(yù)測(cè)輸出锻狗,這樣會(huì)使得模型更加準(zhǔn)確。(這就是為什么構(gòu)建Training和Predicting兩個(gè)Decoder的原因焕参,下面還會(huì)有對(duì)這部分的解釋)轻纪。
  • 需要用target數(shù)據(jù)來(lái)計(jì)算模型的loss。

對(duì)target數(shù)據(jù)的處理時(shí)要將每一行最后一個(gè)標(biāo)記處理掉叠纷,同時(shí)在每一行第一個(gè)元素前添加起始標(biāo)記刻帚。"h,o,w,<EOS>"->"<go>,h,o,w"。
使用tf.strided_slice()來(lái)進(jìn)行這一步處理涩嚣。

def process_decoder_input(data, vocab_to_int, batch_size):
    ending = tf.strided_slice(data, [0, 0], [batch_size, -1], [1, 1])
    decoder_input = tf.concat([tf.fill([batch_size, 1], vocab_to_int['<GO>']), ending], 1)

    return decoder_input

構(gòu)造Decoder
注意崇众,這里將decoder分為了training和predicting掂僵,這兩個(gè)encoder實(shí)際上是共享參數(shù)的,也就是通過(guò)training decoder學(xué)得的參數(shù)顷歌,predicting會(huì)拿來(lái)進(jìn)行預(yù)測(cè)看峻。那么為什么我們要分兩個(gè)呢,這里主要考慮模型的robust(魯棒性)衙吩。
在training階段互妓,為了能夠讓模型更加準(zhǔn)確,我們并不會(huì)把t-1的預(yù)測(cè)輸出作為t階段的輸入坤塞,而是直接使用target data中序列的元素輸入到Encoder中冯勉。而在predict階段,我們沒(méi)有target data摹芙,有的只是t-1階段的輸出和隱層狀態(tài)灼狰。
在training過(guò)程中,不會(huì)把每個(gè)階段的預(yù)測(cè)輸出作為下一階段的輸入浮禾,下一階段的輸入我們會(huì)直接使用target data交胚,這樣能夠保證模型更加準(zhǔn)確。
代碼中做了一些更新盈电,另外好像batch_size參數(shù)丟失了……

def decoding_layer(target_letter_to_int, decoding_embedding_size, num_layers, rnn_size,
                   target_sequence_length, max_target_sequence_length, encoder_state, decoder_input,batch_size):
    '''
    構(gòu)造Decoder層

    參數(shù):
    - target_letter_to_int: target數(shù)據(jù)的映射表
    - decoding_embedding_size: embed向量大小
    - num_layers: 堆疊的RNN單元數(shù)量
    - rnn_size: RNN單元的隱層結(jié)點(diǎn)數(shù)量
    - target_sequence_length: target數(shù)據(jù)序列長(zhǎng)度
    - max_target_sequence_length: target數(shù)據(jù)序列最大長(zhǎng)度
    - encoder_state: encoder端編碼的狀態(tài)向量
    - decoder_input: decoder端輸入
    '''
    # 1. Embedding
    target_vocab_size = len(target_letter_to_int)
    decoder_embeddings = tf.Variable(tf.random_uniform([target_vocab_size, decoding_embedding_size]))
    decoder_embed_input = tf.nn.embedding_lookup(decoder_embeddings, decoder_input)

    # 2. 構(gòu)造Decoder中的RNN單元
    def get_decoder_cell(rnn_size):
        decoder_cell = tf.nn.rnn_cell.LSTMCell(rnn_size, initializer=tf.random_uniform_initializer(-0.1, 0.1, seed=2))
        return decoder_cell

    cell = tf.nn.rnn_cell.MultiRNNCell([get_decoder_cell(rnn_size) for _ in range(num_layers)])

    # 3. Output全連接層
    output_layer = Dense(target_vocab_size, kernel_initializer=tf.truncated_normal_initializer(mean=0.0, stddev=0.1))

    # 4. Training decoder
    with tf.variable_scope("decode"):
        # 得到help對(duì)象
        training_helper = tf.contrib.seq2seq.TrainingHelper(inputs=decoder_embed_input,
                                                            sequence_length=target_sequence_length,
                                                            time_major=False)
        # 構(gòu)造decoder
        training_decoder = tf.contrib.seq2seq.BasicDecoder(cell,
                                                           training_helper,
                                                           encoder_state,
                                                           output_layer)
        training_decoder_output, _ = tf.contrib.seq2seq.dynamic_decode(training_decoder,
                                                                       impute_finished=True,
                                                                       maximum_iterations=max_target_sequence_length)
    # 5. Predicting decoder
    # 與training共享參數(shù)
    with tf.variable_scope("decode", reuse=True):
        # 創(chuàng)建一個(gè)常量tensor并復(fù)制為batch_size的大小
        start_tokens = tf.tile(tf.constant([target_letter_to_int['<GO>']], dtype=tf.int32), [batch_size],
                               name='start_tokens')
        predicting_helper = tf.contrib.seq2seq.GreedyEmbeddingHelper(decoder_embeddings,
                                                                     start_tokens,
                                                                     target_letter_to_int['<EOS>'])
        predicting_decoder = tf.contrib.seq2seq.BasicDecoder(cell,
                                                             predicting_helper,
                                                             encoder_state,
                                                             output_layer)
        predicting_decoder_output, _ = tf.contrib.seq2seq.dynamic_decode(predicting_decoder,
                                                                         impute_finished=True,
                                                                         maximum_iterations=max_target_sequence_length)

    return training_decoder_output, predicting_decoder_output

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蝴簇,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子匆帚,更是在濱河造成了極大的恐慌熬词,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吸重,死亡現(xiàn)場(chǎng)離奇詭異互拾,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)嚎幸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)颜矿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人嫉晶,你說(shuō)我怎么就攤上這事骑疆。” “怎么了车遂?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵封断,是天一觀的道長(zhǎng)斯辰。 經(jīng)常有香客問(wèn)我舶担,道長(zhǎng),這世上最難降的妖魔是什么彬呻? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任衣陶,我火速辦了婚禮柄瑰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘剪况。我一直安慰自己教沾,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布译断。 她就那樣靜靜地躺著授翻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪孙咪。 梳的紋絲不亂的頭發(fā)上堪唐,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音翎蹈,去河邊找鬼淮菠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛荤堪,可吹牛的內(nèi)容都是我干的合陵。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼澄阳,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拥知!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起碎赢,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤举庶,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后揩抡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體户侥,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年峦嗤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蕊唐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡烁设,死狀恐怖替梨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情装黑,我是刑警寧澤副瀑,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站恋谭,受9級(jí)特大地震影響糠睡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜疚颊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一狈孔、第九天 我趴在偏房一處隱蔽的房頂上張望信认。 院中可真熱鬧,春花似錦均抽、人聲如沸嫁赏。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)潦蝇。三九已至,卻和暖如春深寥,著一層夾襖步出監(jiān)牢的瞬間护蝶,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工翩迈, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留持灰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓负饲,卻偏偏與公主長(zhǎng)得像堤魁,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子返十,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容