天然語言是很是複雜多變的,計算機也不認識我們的語言,那麼我們如何讓我們的計算機學習我們的語言呢?首先確定得對我們的全部文字進行編碼吧,那我們不少小夥伴確定立馬就想出了這還不簡單嘛,我們的計算機不都是ASCII編碼的嘛,咱直接拿來用不就好啦?我只能說too young too simple。我們的計算機只是對我們的「字母」進行ASCII編碼,並無對我們的「Word」編碼。world應該是我們處理天然語言的最基本的元素,而不是字母。那麼世界上有千千萬萬的Word,我們具體怎麼表示呢?就算找出了一種方式來表示每個Word,那麼這些Word之間的關係如何來表示,畢竟有些詞彙在某種維度上是比較類似的,有些詞彙在某些維度上的距離則是比較遠的,那麼我們如何還找到他們的關係呢?還有一個問題就是我們如何經過我們的text corpus(training dataset)來最終來學習我們的模型呢?等等這些問題都是我們NLP的內容的最基礎也是最根本的問題。這一節主要就是解決這些問題的。node
首先我們來看我們如何在機器學習系統中表示我們的詞彙,這裏呢先說答案,那就是有兩種方式,一種就是One-hot encoding, 另一種就是 Featured representation (word embedding)。首先我們來看一下One-hot encoding,它是我們定義的長度固定的vocabulary中賦予每個Word一個index,例如vocabulary的長度是10000,其中「cat」的index是3202,那麼我們對於cat的表示方式就是[0,0,0,0,..........1.........,0], 其中只在3202的位置是1,其餘的9999個位置都是0. 我們也能夠結合下面的圖片來更加直觀的展現一下Word的one-hot encoding。算法
上面的圖片是否是一目瞭然了什麼是Word的one-hot encoding呢?那麼問題來了?這種方式的representation有沒有什麼缺點是避免不了的以致於我們必須得用Word Embedding呢?固然有啦,其實one-hot encoding的主要的缺點主要有2個:第一個你們主要到沒有,one-hot encoding表示的方式是很是sparse的,每個Word都必需要有更vocabulary長度同樣多的元素element,並且這些element絕大可能是都是0,這是很是浪費資源的一種方式;第二個問題就更加嚴重了,那就是用one-hot encoding 的方式表示Word的話,那麼所用的Word之間都是沒有任何聯繫的,任何兩個Word之間的cosin值cos(word1,word2)都是0,這顯然是很不符合我們的實際狀況的,例如實際中cat和dog之間關係老是要比cat和book之間的關係要更加緊密纔對啊,但是在one-hot encoding中cos(cat, dog) == cos(cat, book) ==0,表示它們之間的關係都是同樣的,這顯然不符合我們的實際狀況。網絡
Word embedding 應該是我們整個NLP中最最經常使用的一個技術點了,它的核心思想就是每個Word都有一些維度,具體有多少維度我們用戶本身定義,可是這些維度能保證類似的詞彙在這個多維的空間上的值是類似的,例如我們用5維的空間來表示一個詞,那麼cat多是[1.0, 1.5, 0.0, 6.4, 0.0], dog多是[0.95, 1.2, 0.11, 5.5, 0.0], book可能的值是[9.5, 0.0, 3.4, 0.3, 6.2]。從前面的幾個值我們能夠很顯然的看出cat和dog在這個五維空間是比較接近的,而他們跟book則在這個五維空間上的距離要遠的多,這也更加符合我們的實際狀況的認知,那麼具體這個五維空間的每個feature是什麼,我們不須要知道,在後面的部分我會介紹2紅算法來計算embedding的。那麼下面就接着上面的例子,我畫一個圖片更加方便你們的理解app
上面的這個圖片就是對我們上面的例子的一個簡單的embedding的展現,你們如今沒必要糾結上面這些數據是怎麼來的,你們只須要知道embedding的展示形式和意思就好了。還有一個你們也不須要被多維空間這個詞給嚇到了,其實就把它當作是多個features的就行,超過三維的數據我們肉眼是沒法經過形象的圖像展現的,因此你也不用費神在腦子裏面想象多維數據是啥樣子了,你就把它當作多個features多個特徵就行,至少每個物體都有不少特性的,例如人有身高,體重,膚色,性別等等等不少的特性。而後我們還有一個算法就是T-SNE算法能夠將我們多維的數據轉化成2維數據給展現出來,你們稍微知道有這麼個東西就行,無需深究。機器學習
首先我們介紹第一個計算embedding的算法,那就是經過傳統的Neural Network來學習訓練我們的embedding和neural network自己的參數的。那麼我們來看一下它的具體流程,在正式介紹以前我們展現一下它的流程圖,而後在來解釋,畢竟這種方式要容易理解的多,我們的大腦處理圖片的速度仍是要比文字快的多滴。。。。。函數
上面的圖片我不但展現了用DNN的方式來求Embedding的流程而且應用這個embedding來訓練model的流程,同時我還配了一個例子來解釋這個流程。這個過程當中,大家必定要注意的一點就是這裏的Embedding 即做爲這個模型的Input data,同時也是做爲這個模型的parameters來不斷學習更新的。 那麼我們如今就來解釋這個模型學習的流程吧,首先第一步將我們的語言sentence經過encoding的方式轉成sequences, 具體這個過程是如何實現的呢?實際上是想根據我們的語義集(text corpus)生成tokenizer, 而後用這個tokenizer來作encoding的工做,具體的代碼我會在最後的應用部分展現。而後第二步我們來到我們的embedding中找到前面sequences相對應的數據,而且將這些數據提出來,這裏的embedding我們根據用戶自定義它有多少個words多少個維度,而後這個embedding會隨機初始化一些數據出來;第三步我們將我們前面從embedding中提取出來的數據進行flatten處理,以便於我們將數據feed到後面的DNN中;最後一步就是我們的DNN訓練的過程,在這個訓練的過程當中,我們不但會訓練DNN本身的paramets,它同時會訓練而且更新前面從embedding中提取的數據。那麼這個流程就是我們用DNN的方式來計算embedding的方式。上面的I LOVE YOU在這裏叫作context words, 用上面的方式來計算而且訓練embedding的時候,我們的context words的數量必定得是固定的,不然我們沒辦法flatten我們的數據feed到同一個neural network(由於同一個neural network的input layer的units是固定的)。同時具體要選擇幾個context words也是隨便用戶本身定義的,可是一旦選定了context words,則後面的context words必需要遵循前面的規則,規則必須一致。例如我們便可以選擇target的前4個words做爲context words, 也能夠選擇target前面的2個words做爲context words,甚至能夠選擇target的後2個數據做爲context words。可是一旦選擇了,後面就必須一致。固然了,若是我們data中的context words的長度不夠,我們能夠經過padded的方式補齊。post
還有一種經常使用的embedding的算法,那就是應用Skip-Gram算法來計算我們的embedding和模型。那麼到底它是如何工做的呢?首先我們仍是先看一下這個算法的流程圖,而後我們詳細解釋一下流程哈學習
我們的skip-gram的算法,首先第一步是我們在training data(text corpus)中的的sentence中任意選擇一個Word做爲context Word;其次在我們初始化的embedding中找到這個context Word對應的值ec,而後將我們的ec 值帶入到我們的softmax model中,softmax會計算我們vocabulary全部的詞彙的機率,而後選擇機率最大的那個Word就是我們根據這個模型選擇出來的target Word。上面是我們應用skip-Grams的步驟,實際中的softmax model中的參數還有embedding是我們經過上圖中的Loss函數的gradient descent來不斷的學習出來的。在這個算法中一樣的embedding即做爲我們softmax模型的input data同時也做爲我們softmax模型的參數parameter。這裏面的關係容易混淆啊,上圖的softmax模型P(t|c)是我們定義的模型,這個模型裏面的參數和embedding是經過上圖中的Loss函數不斷的gradient descent得來的,我們的training data是在我們的text corpus中的每一行sentence中隨機選擇出來的一個context Word和在這個context Word先後必定範圍內隨機選擇的一個target Word。這就是這個Skip-Grams算法的核心,固然啦,若是要實踐這個算法,裏面還有不少的細節須要處理的,可是這裏面最核心的思想就是上面提到的步驟了。可是這個算法也有一個致命的弱點,那就是,這個算法須要大量的運算,我們每走一個gradient descent,我們就得計算出我們全部的10000個words的值,實際中我們word可能還遠遠不止10000個,多是百萬都有可能。優化
上面說了半天的理論內容,着實有點無聊,如今我們來看看如何用TensorFlow來用Embedding吧,雖然上面的理論內容很枯燥無聊,但其實仍是很重要的,一來我們偶爾能夠裝B用,二來若是你不理解上面的理論,我們在應用的時候你連參數都不知道怎麼調,你怎麼去優化你的模型啊??因此啊,這一塊仍是有必要理解一下滴。好了,那我們就來看一個用TensorFlow應用embedding的例子,假設我們的需求是判斷你們對一部電影的review是好仍是壞。谷歌自帶的dataset中給了我們這些數據,我們能夠直接下載下來直接用的。這一個例子其實也是我們的NLP中經常使用的一個場景叫作text sentiment analysis。好吧, 那我們開始吧。編碼
第一步:加載我們的數據
import tensorflow as tf import tensorflow_datasets as tfds import numpy as np imdb,info = tfds.load("imdb_reviews",with_info=True,as_supervised=True) train_data = imdb["train"] test_data = imdb["test"]
這一步很簡單,就是直接從我們的谷歌的tfds中加載movie reviews的數據。
第二步:將dataset從TensorFlow中的數據對象轉成Python list而且分割出features 和 labels。
training_sentences = [] training_labels = [] test_sentences = [] test_labels = [] for s,l in train_data: training_sentences.append(str(s.numpy())) training_labels.append(l.numpy()) for s,l in test_data: test_sentences.append(str(s.numpy())) test_labels.append(l.numpy())
上面的代碼是遍歷了我們training dataset和test dataset中的數據,而且把它們加載到Python中的list,同時將我們數據中的features和labels分開。這一步算是數據準備階段, 接下來就是我們來配置我們的tokenizer參數的時候了
第三部:配置tokenizer信息
from tensorflow.keras.preprocessing.text import Tokenizer from tensorflow.keras.preprocessing.sequence import pad_sequences max_length = 120 trunc_type="post" oov_tok = "<OOV>" #initialize a tokenizer tokenizer = Tokenizer(num_words = vocab_size,oov_token = oov_tok) #fit the text to create word index, result a dict of the word:index key-values tokenizer.fit_on_texts(training_sentences) word_index = tokenizer.word_index #create a sequences of the training sentences based on the dictionary above(word-index) training_sequences = tokenizer.texts_to_sequences(training_sentences) #pad the sequences training_padded_sequences = pad_sequences(training_sequences,maxlen=max_length,truncating=trunc_type) #create a sequences of the test sentences based on the dictionary above(word-index) test_sequences = tokenizer.texts_to_sequences(test_sentences) #pad the sequences test_padded_sequences = pad_sequences(test_sequences,maxlen=max_length,truncating=trunc_type)
這一步的主要功能是講我們上面獲得的原始的data(text)轉化成我們的計算機熟悉的數字格式,從而每個句子都是一個數字的sequence。其實就是encoding的過程,首先把我們上面的training_sentences中全部出現的Word都賦予一個數字,這一步是經過fit_on_texts()函數來實現的,這一步我們生成了一個dict對象word_index, 這個dict將training_sentences中出現的每個Word(不重複)做爲key, 每個Word都對應一個value(不重複)。接下來第二步就是經過texts_to_sequences()這個函數,將我們的全部的sentence都根據上面的word_index來一一對應從text轉化成數字的sequence。上面的代碼主要就是這兩個功能,整個過程,我們稱之爲encoding。這裏有一個小細節,那就是我們在配置tokenizer的時候設置了一個oov_tok參數,這個參數是幹什麼的呢?我們雖然根據traininig dataset編碼了不少的word, 可是實際中總有一些詞是我們training dataset中沒有出現過的,那麼這種狀況下,我們就須要一個out-of-value (oov)來表示這些未見過的word啦,因此這裏我們就配置了oov_tok = "<oov>", 就是一旦未來遇到了生詞,我們一致用「<oov>」這個詞表示,而且它在我們的word_index中也有鍵值對,通常狀況下,它個value是1。還有一個細節就是pad_sequences()函數,由於我們的text的長度是不同的,爲了保證未來能正確的feed到我們的DNN中,它們的長度必須同樣長,這時候我們就得用到pad技術了,他會將我們所用的text同補充到max_length那麼長。
第四步:配置神經網絡和embedding
這一步就是我們的核心部分了,那就是配置我們的embedding信息和我們的DNN網絡
vocab_size = 10000 embedding_dim = 16 #define model structure model = tf.keras.Sequential([ tf.keras.layers.Embedding(vocab_size,embedding_dim,input_length=max_length), tf.keras.layers.Flatten(), tf.keras.layers.Dense(64,activation="relu"), tf.keras.layers.Dense(1,activation="sigmoid") ]) model.compile(loss="binary_crossentropy",optimizer="Adam",metrics=["accuracy"]) model.summary()
上面的embedding我們配置的是一個10000長度,16個維度的embedding,這個embedding裏面每個Word都有16個維度。由於我們的是一個binary classification的問題,output layer只有一個node,而且activation是sigmoid。經過summary()函數我們看一下我們的整個的網絡結構
上面能夠完整的看到我們定義的網絡結構和一些參數。
第五步:train model
我們既然已經有了複合格式的數據,也定義了我們的模型,那麼接下來我們就用我們的數據來訓練我們的模型了
#training the model model.fit( training_padded_sequences, training_labels_final, epochs=10, validation_data=(test_padded_sequences,test_labels_final) )
這一步跟前面章節講個訓練過程如出一轍,也很簡單這裏就不細講了。上面五個步驟以後,我們已經訓練好了我們模型了,也是我們在遇到text sentiment analysis這一類問題的主要流程,就是從數據加載,encoding,模型定義和訓練這幾個步驟。