深度學(xué)習(xí)利器:TensorFlow與NLP模型
前言
自然語(yǔ)言處理(簡(jiǎn)稱NLP),是研究計(jì)算機(jī)處理人類語(yǔ)言的一門(mén)技術(shù),NLP技術(shù)讓計(jì)算機(jī)可以基于一組技術(shù)和理論,分析、理解人類的溝通內(nèi)容。傳統(tǒng)的自然語(yǔ)言處理方法涉及到了很多語(yǔ)言學(xué)本身的知識(shí),而深度學(xué)習(xí),是表征學(xué)習(xí)(representation learning)的一種方法,在機(jī)器翻譯、自動(dòng)問(wèn)答、文本分類、情感分析、信息抽取、序列標(biāo)注、語(yǔ)法解析等領(lǐng)域都有廣泛的應(yīng)用。
2013年末谷歌發(fā)布的word2vec工具,將一個(gè)詞表示為詞向量,將文字?jǐn)?shù)字化,有效地應(yīng)用于文本分析。2016年谷歌開(kāi)源自動(dòng)生成文本摘要模型及相關(guān)TensorFlow代碼。2016/2017年,谷歌發(fā)布/升級(jí)語(yǔ)言處理框架SyntaxNet,識(shí)別率提高25%,為40種語(yǔ)言帶來(lái)文本分割和詞態(tài)分析功能。2017年谷歌官方開(kāi)源tf-seq2seq,一種通用編碼器/解碼器框架,實(shí)現(xiàn)自動(dòng)翻譯。本文主要結(jié)合TensorFlow平臺(tái),講解TensorFlow詞向量生成模型(Vector Representations of Words);使用RNN、LSTM模型進(jìn)行語(yǔ)言預(yù)測(cè);以及TensorFlow自動(dòng)翻譯模型。
Word2Vec數(shù)學(xué)原理簡(jiǎn)介
我們將自然語(yǔ)言交給機(jī)器學(xué)習(xí)來(lái)處理,但機(jī)器無(wú)法直接理解人類語(yǔ)言。那么首先要做的事情就是要將語(yǔ)言數(shù)學(xué)化,Hinton于1986年提出Distributed Representation方法,通過(guò)訓(xùn)練將語(yǔ)言中的每一個(gè)詞映射成一個(gè)固定長(zhǎng)度的向量。所有這些向量構(gòu)成詞向量空間,每個(gè)向量可視為空間中的一個(gè)點(diǎn),這樣就可以根據(jù)詞之間的距離來(lái)判斷它們之間的相似性,并且可以把其應(yīng)用擴(kuò)展到句子、文檔及中文分詞。
Word2Vec中用到兩個(gè)模型,CBOW模型(Continuous Bag-of-Words model)和Skip-gram模型(Continuous Skip-gram Model)。模型示例如下,是三層結(jié)構(gòu)的神經(jīng)網(wǎng)絡(luò)模型,包括輸入層,投影層和輸出層。
其中score(wt, h),表示在的上下文環(huán)境下,預(yù)測(cè)結(jié)果是的概率得分。上述目標(biāo)函數(shù),可以轉(zhuǎn)換為極大化似然函數(shù),如下所示:
求解上述概率模型的計(jì)算成本是非常高昂的,需要在神經(jīng)網(wǎng)絡(luò)的每一次訓(xùn)練過(guò)程中,計(jì)算每個(gè)詞在他的上下文環(huán)境中出現(xiàn)的概率得分,如下所示:
然而在使用word2vec方法進(jìn)行特性學(xué)習(xí)的時(shí)候,并不需要計(jì)算全概率模型。在CBOW模型和skip-gram模型中,使用了邏輯回歸(logistic regression)二分類方法進(jìn)行的預(yù)測(cè)。如下圖CBOW模型所示,為了提高模型的訓(xùn)練速度和改善詞向量的質(zhì)量,通常采用隨機(jī)負(fù)采樣(Negative Sampling)的方法,噪音樣本w1,w2,w3,wk…為選中的負(fù)采樣。
TensorFlow近義詞模型
本章講解使用TensorFlow word2vec模型尋找近義詞,輸入數(shù)據(jù)是一大段英文文章,輸出是相應(yīng)詞的近義詞。比如,通過(guò)學(xué)習(xí)文章可以得到和five意思相近的詞有: four, three, seven, eight, six, two, zero, nine。通過(guò)對(duì)大段英文文章的訓(xùn)練,當(dāng)神經(jīng)網(wǎng)絡(luò)訓(xùn)練到10萬(wàn)次迭代,網(wǎng)絡(luò)Loss值減小到4.6左右的時(shí)候,學(xué)習(xí)得到的相關(guān)近似詞,如下圖所示:
下面為T(mén)ensorFlow word2vec API 使用說(shuō)明:
構(gòu)建詞向量變量,vocabulary_size為字典大小,embedding_size為詞向量大小
- embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
定義負(fù)采樣中邏輯回歸的權(quán)重和偏置
- nce_weights = tf.Variable(tf.truncated_normal
- ([vocabulary_size, embedding_size], stddev=1.0 / math.sqrt(embedding_size)))
- nce_biases = tf.Variable(tf.zeros([vocabulary_size]))
定義訓(xùn)練數(shù)據(jù)的接入
- train_inputs = tf.placeholder(tf.int32, shape=[batch_size])
- train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1])
定義根據(jù)訓(xùn)練數(shù)據(jù)輸入,并尋找對(duì)應(yīng)的詞向量
- embed = tf.nn.embedding_lookup(embeddings, train_inputs)
基于負(fù)采樣方法計(jì)算Loss值
- loss = tf.reduce_mean( tf.nn.nce_loss
- (weights=nce_weights, biases=nce_biases, labels=train_labels,
- inputs=embed, num_sampled=num_sampled, num_classes=vocabulary_size))
定義使用隨機(jī)梯度下降法執(zhí)行優(yōu)化操作,最小化loss值
- optimizer = tf.train.GradientDescentOptimizer(learning_rate=1.0).minimize(loss)
通過(guò)TensorFlow Session Run的方法執(zhí)行模型訓(xùn)練
- for inputs, labels in generate_batch(...):
- feed_dict = {train_inputs: inputs, train_labels: labels}
- _, cur_loss = session.run([optimizer, loss], feed_dict=feed_dict)
TensorFlow語(yǔ)言預(yù)測(cè)模型
本章主要回顧RNN、LSTM技術(shù)原理,并基于RNN/LSTM技術(shù)訓(xùn)練語(yǔ)言模型。也就是給定一個(gè)單詞序列,預(yù)測(cè)最有可能出現(xiàn)的下一個(gè)單詞。例如,給定[had, a, general] 3個(gè)單詞的LSTM輸入序列,預(yù)測(cè)下一個(gè)單詞是什么?如下圖所示:
RNN技術(shù)原理
循環(huán)神經(jīng)網(wǎng)絡(luò)(Recurrent Neural Network, RNN)是一類用于處理序列數(shù)據(jù)的神經(jīng)網(wǎng)絡(luò)。和卷積神經(jīng)網(wǎng)絡(luò)的區(qū)別在于,卷積網(wǎng)絡(luò)是適用于處理網(wǎng)格化數(shù)據(jù)(如圖像數(shù)據(jù))的神經(jīng)網(wǎng)絡(luò),而循環(huán)神經(jīng)網(wǎng)絡(luò)是適用于處理序列化數(shù)據(jù)的神經(jīng)網(wǎng)絡(luò)。例如,你要預(yù)測(cè)句子的下一個(gè)單詞是什么,一般需要用到前面的單詞,因?yàn)橐粋€(gè)句子中前后單詞并不是獨(dú)立的。RNN之所以稱為循環(huán)神經(jīng)網(wǎng)路,即一個(gè)序列當(dāng)前的輸出與前面的輸出也有關(guān)。具體的表現(xiàn)形式為網(wǎng)絡(luò)會(huì)對(duì)前面的信息進(jìn)行記憶并應(yīng)用于當(dāng)前輸出的計(jì)算中,即隱藏層之間的節(jié)點(diǎn)不再無(wú)連接而是有連接的,并且隱藏層的輸入不僅包括輸入層的輸出還包括上一時(shí)刻隱藏層的輸出。如下圖所示:
LSTM技術(shù)原理
RNN有一問(wèn)題,反向傳播時(shí),梯度也會(huì)呈指數(shù)倍數(shù)的衰減,導(dǎo)致經(jīng)過(guò)許多階段傳播后的梯度傾向于消失,不能處理長(zhǎng)期依賴的問(wèn)題。雖然RNN理論上可以處理任意長(zhǎng)度的序列,但實(shí)習(xí)應(yīng)用中,RNN很難處理長(zhǎng)度超過(guò)10的序列。為了解決RNN梯度消失的問(wèn)題,提出了Long Short-Term Memory模塊,通過(guò)門(mén)的開(kāi)關(guān)實(shí)現(xiàn)序列上的記憶功能,當(dāng)誤差從輸出層反向傳播回來(lái)時(shí),可以使用模塊的記憶元記下來(lái)。所以 LSTM 可以記住比較長(zhǎng)時(shí)間內(nèi)的信息。常見(jiàn)的LSTM模塊如下圖所示:
output gate類似于input gate同樣會(huì)產(chǎn)生一個(gè)0-1向量來(lái)控制Memory Cell到輸出層的輸出,如下公式所示:
三個(gè)門(mén)協(xié)作使得 LSTM 存儲(chǔ)塊可以存取長(zhǎng)期信息,比如說(shuō)只要輸入門(mén)保持關(guān)閉,記憶單元的信息就不會(huì)被后面時(shí)刻的輸入所覆蓋。
使用TensorFlow構(gòu)建單詞預(yù)測(cè)模型
首先下載PTB的模型數(shù)據(jù),該數(shù)據(jù)集大概包含10000個(gè)不同的單詞,并對(duì)不常用的單詞進(jìn)行了標(biāo)注。
首先需要對(duì)樣本數(shù)據(jù)集進(jìn)行預(yù)處理,把每個(gè)單詞用整數(shù)標(biāo)注,即構(gòu)建詞典索引,如下所示:
讀取訓(xùn)練數(shù)據(jù)
- data = _read_words(filename)
- #按照單詞出現(xiàn)頻率,進(jìn)行排序
- counter = collections.Counter(data)
- count_pairs = sorted(counter.items(), key=lambda x: (-x1, x[0]))
- #構(gòu)建詞典及詞典索引
- words, _ = list(zip(*count_pairs))
- word_to_id = dict(zip(words, range(len(words))))
接著讀取訓(xùn)練數(shù)據(jù)文本,把單詞序列轉(zhuǎn)換為單詞索引序列,生成訓(xùn)練數(shù)據(jù),如下所示:
讀取訓(xùn)練數(shù)據(jù)單詞,并轉(zhuǎn)換為單詞索引序列
- data = _read_words(filename) data = [word_to_id[word] for word in data if word in word_to_id]
生成訓(xùn)練數(shù)據(jù)的data和label,其中epoch_size為該epoch的訓(xùn)練迭代次數(shù),num_steps為L(zhǎng)STM的序列長(zhǎng)度
- i = tf.train.range_input_producer(epoch_size, shuffle=False).dequeue()
- x = tf.strided_slice(data, [0, i * num_steps], [batch_size, (i + 1) * num_steps])
- x.set_shape([batch_size, num_steps])
- y = tf.strided_slice(data, [0, i * num_steps + 1], [batch_size, (i + 1) * num_steps + 1])
- y.set_shape([batch_size, num_steps])
構(gòu)建LSTM Cell,其中size為隱藏神經(jīng)元的數(shù)量
- lstm_cell = tf.contrib.rnn.BasicLSTMCell(size,
- forget_bias=0.0, state_is_tuple=True)
如果為訓(xùn)練模式,為保證訓(xùn)練魯棒性,定義dropout操作
- attn_cell = tf.contrib.rnn.DropoutWrapper(lstm_cell,
- output_keep_prob=config.keep_prob)
根據(jù)層數(shù)配置,定義多層RNN神經(jīng)網(wǎng)絡(luò)
- cell = tf.contrib.rnn.MultiRNNCell( [ attn_cell for _ in range(config.num_layers)],
- state_is_tuple=True)
根據(jù)詞典大小,定義詞向量
- embedding = tf.get_variable("embedding",
- [vocab_size, size], dtype=data_type())
根據(jù)單詞索引,查找詞向量,如下圖所示。從單詞索引找到對(duì)應(yīng)的One-hot encoding,然后紅色的weight就直接對(duì)應(yīng)了輸出節(jié)點(diǎn)的值,也就是對(duì)應(yīng)的embedding向量。
- inputs = tf.nn.embedding_lookup(embedding, input_.input_data)
定義RNN網(wǎng)絡(luò),其中state為L(zhǎng)STM Cell的狀態(tài),cell_output為L(zhǎng)STM Cell的輸出
- for time_step in range(num_steps):
- if time_step > 0: tf.get_variable_scope().reuse_variables()
- (cell_output, state) = cell(inputs[:, time_step, :], state)
- outputs.append(cell_output)
定義訓(xùn)練的loss值就,如下公式所示。
- softmax_w = tf.get_variable("softmax_w", [size, vocab_size], dtype=data_type())
- softmax_b = tf.get_variable("softmax_b", [vocab_size], dtype=data_type())
- logits = tf.matmul(output, softmax_w) + softmax_b
Loss值
- loss = tf.contrib.legacy_seq2seq.sequence_loss_by_example([logits],
- [tf.reshape(input_.targets, [-1])], [tf.ones([batch_size * num_steps], dtype=data_type())])
定義梯度及優(yōu)化操作
- cost = tf.reduce_sum(loss) / batch_size
- tvars = tf.trainable_variables()
- grads, _ = tf.clip_by_global_norm(tf.gradients(cost, tvars), config.max_grad_norm)
- optimizer = tf.train.GradientDescentOptimizer(self._lr)
單詞困惑度eloss
- perplexity = np.exp(costs / iters)
TensorFlow語(yǔ)言翻譯模型
本節(jié)主要講解使用TensorFlow實(shí)現(xiàn)RNN、LSTM的語(yǔ)言翻譯模型。基礎(chǔ)的sequence-to-sequence模型主要包含兩個(gè)RNN網(wǎng)絡(luò),一個(gè)RNN網(wǎng)絡(luò)用于編碼Sequence的輸入,另一個(gè)RNN網(wǎng)絡(luò)用于產(chǎn)生Sequence的輸出。基礎(chǔ)架構(gòu)如下圖所示
上圖中的每個(gè)方框表示RNN中的一個(gè)Cell。在上圖的模型中,每個(gè)輸入會(huì)被編碼成固定長(zhǎng)度的狀態(tài)向量,然后傳遞給解碼器。2014年,Bahdanau在論文“Neural Machine Translation by Jointly Learning to Align and Translate”中引入了Attention機(jī)制。Attention機(jī)制允許解碼器在每一步輸出時(shí)參與到原文的不同部分,讓模型根據(jù)輸入的句子以及已經(jīng)產(chǎn)生的內(nèi)容來(lái)影響翻譯結(jié)果。一個(gè)加入attention機(jī)制的多層LSTM sequence-to-sequence網(wǎng)絡(luò)結(jié)構(gòu)如下圖所示:
針對(duì)上述sequence-to-sequence模型,TensorFlow封裝成了可以直接調(diào)用的函數(shù)API,只需要幾百行的代碼就能實(shí)現(xiàn)一個(gè)初級(jí)的翻譯模型。tf.nn.seq2seq文件共實(shí)現(xiàn)了5個(gè)seq2seq函數(shù):
- basic_rnn_seq2seq:輸入和輸出都是embedding的形式;encoder和decoder用相同的RNN cell,但不共享權(quán)值參數(shù);
- tied_rnn_seq2seq:同basic_rnn_seq2seq,但encoder和decoder共享權(quán)值參數(shù);
- embedding_rnn_seq2seq:同basic_rnn_seq2seq,但輸入和輸出改為id的形式,函數(shù)會(huì)在內(nèi)部創(chuàng)建分別用于encoder和decoder的embedding矩陣;
- embedding_tied_rnn_seq2seq:同tied_rnn_seq2seq,但輸入和輸出改為id形式,函數(shù)會(huì)在內(nèi)部創(chuàng)建分別用于encoder和decoder的embedding矩陣;
- embedding_attention_seq2seq:同embedding_rnn_seq2seq,但多了attention機(jī)制;
embedding_rnn_seq2seq函數(shù)接口使用說(shuō)明如下:
- encoder_inputs:encoder的輸入
- decoder_inputs:decoder的輸入
- cell:RNN_Cell的實(shí)例
- num_encoder_symbols,num_decoder_symbols:分別是編碼和解碼的大小
- embedding_size:詞向量的維度
- output_projection:decoder的output向量投影到詞表空間時(shí),用到的投影矩陣和偏置項(xiàng)
- feed_previous:若為T(mén)rue, 只有第一個(gè)decoder的輸入符號(hào)有用,所有的decoder輸入都依賴于上一步的輸出;
- outputs, states = embedding_rnn_seq2seq(
- encoder_inputs, decoder_inputs, cell,
- num_encoder_symbols, num_decoder_symbols,
- embedding_size, output_projection=None,
- feed_previous=False)
TensorFlow官方提供了英語(yǔ)到法語(yǔ)的翻譯示例,采用的是statmt網(wǎng)站提供的語(yǔ)料數(shù)據(jù),主要包含:giga-fren.release2.fixed.en(英文語(yǔ)料,3.6G)和giga-fren.release2.fixed.fr(法文語(yǔ)料,4.3G)。該示例的代碼結(jié)構(gòu)如下所示:
- seq2seq_model.py:seq2seq的TensorFlow模型采用了embedding_attention_seq2seq用于創(chuàng)建seq2seq模型。
- data_utils.py:對(duì)語(yǔ)料數(shù)據(jù)進(jìn)行數(shù)據(jù)預(yù)處理,根據(jù)語(yǔ)料數(shù)據(jù)生成詞典庫(kù);并基于詞典庫(kù)把要翻譯的語(yǔ)句轉(zhuǎn)換成用用詞ID表示的訓(xùn)練序列。如下圖所示:
(點(diǎn)擊放大圖像)
translate.py:主函數(shù)入口,執(zhí)行翻譯模型的訓(xùn)練
執(zhí)行模型訓(xùn)練
- python translate.py
- --data_dir [your_data_directory] --train_dir [checkpoints_directory]
- --en_vocab_size=40000 --fr_vocab_size=40000
總結(jié)
隨著TensorFlow新版本的不斷發(fā)布以及新模型的不斷增加,TensorFlow已成為主流的深度學(xué)習(xí)平臺(tái)。本文主要介紹了TensorFlow在自然語(yǔ)言處理領(lǐng)域的相關(guān)模型和應(yīng)用。首先介紹了Word2Vec數(shù)學(xué)原理,以及如何使用TensorFlow學(xué)習(xí)詞向量;接著回顧了RNN、LSTM的技術(shù)原理,講解了TensorFlow的語(yǔ)言預(yù)測(cè)模型;最后實(shí)例分析了TensorFlow sequence-to-sequence的機(jī)器翻譯 API及官方示例。