自然語言處理的相關應用
先來看看在工業界對於自然語言處理的一些常見需求與任務。
自動摘要
這個在搜索引擎中非常常用。指計算機能自動去閱讀一篇文章然後去提取這篇文章的摘要。
指代消解
比如「小明放學了,媽媽去接他」這句話中的「他」指代為「小明」。這個計算機本身並不知道,而我們希望計算機能自己去識別這些指代的對象。
機器翻譯
這個非常熟悉,比如中英文的互相翻譯。我們希望計算機能自動地將一種語言翻譯成另一種語言。
詞性標註
即計算機能自動去標註出每個詞的詞性(動詞,形容詞,副詞,名次等等)
分詞
即為一個句子去斷句,比如「小明/很/難過」,「大水溝/很/難/過」。在前一個句子中「難過」是一個詞語,表示一種感情;後一個句子中「難過」表示「難以/通過」。我們人很容易去區分句子中的每個詞,但是計算機本身是不知道哪兩個字是詞語的。另外,英文的分詞可以通過空格,而中文卻需要建立其他規則,這個為中文分詞(日文韓文也是)增加了許多難度。
主題識別
即在一堆文章中去識別各個文章的主題。比如輸入1000篇文章,通過某種演算法可以知道有100篇文章是在講同一個主題--「體育」。
文本分類
這是分類問題中的一種,只是現在是對「文本」進行分類,文本的特徵不是自帶與直觀的,需要通過文本中的內容(詞)來提取。在工業界(其實我不知道為啥要叫工業界,而不是商業界),經常需要去區分文章的情感類別是負面還是正面,或者區分文章是在屬於哪個行業等等。
......
傳統的NLP處理方法是基於規則的
現代的處理方法更傾向與統計機器學習,比如HMM,CRF,SVM.LDA,CNN等,」規則「是隱含在模型參數里的。
2. 詞向量的傳統方法介紹
任何一篇文本都是由「詞」組成的。所以對大多數對文本問題的分析,其實最終還是歸結於對「詞」的分析。這裡先不討論如何「分詞」的知識,假設我們已經成功地分好了詞。要將自然語言的問題來運用機器學習或深度學習的模型來解決的話,就必須要將這些計算機本身不認識的「詞」轉化為「數字」,即通過「向量」的形式來表示。如此一來,我們就可以通過對向量的各種統計運算來解決問題了。(這個過程也可以看成是給「詞」進行「編碼」)
那麼問題來了,如何對詞進行編碼才能更有效呢?
2.1 離散表示--One-hot
假設我們語料庫中有以下兩句話,按空格進行分詞:
可以對每個詞進行標註,形成詞典,這樣每個詞都有它唯一和獨有的編號或索引。
根據每個詞在詞典中的索引,可以用One-hot的形式如下表示,在一個向量中,只有在這個詞所在的索引處的值為1,其他都為0。
這是一個比較傳統的詞向量表示法,在很多場景中都仍然被使用。使用這種詞向量的缺點是,詞在詞典中的順序和在句子中的順序是沒有關聯的。而且詞和詞之間也是無法通過這種方式計算出相似性的。
2.2 離散表示--Bag of words
運用One-hot方法,將一整個文檔映射成一個向量的時候可以將各個詞的詞向量表示加和。如下:
通過文檔的向量我們可以運用TF-IDF演算法去求取每個詞在文檔中的重要程度。
TF值--> 求每個文檔中的各個詞出現的頻數,頻數越大則說明這個詞在該篇文檔中的重要性越大。
IDF值--> 另一個角度,如果一個詞在所有文章中出現得都很多,那麼可能是停用詞,比如「的」,「啊」等,說明越不重要
只有在某篇文章中出現地很多,但在其他文件中出現得少的詞才是對這篇文章重要的詞,那麼這個詞的權重應該比較高。基於這樣的理論,我們將TF*IDF來表示詞對某篇文章的重要值。
將第一句話用TF-IDF來表示:
另一種粗暴的方式是Binary weighting。在計算「短文本相似性」可以使用。
即只要TF-IDF值大於0,則標註為1,否則為0.
2.3 離散表示--Bi-gram 和 N-gram
為2-gram建索引如下:
即從第一個詞開始,取出所有相鄰的兩個詞對(保持兩個詞的先後順序),並對他們建立索引。這樣的話之前的兩句話的文檔向量可以如下便是
這個方法考慮了詞的順序,但是缺點是詞表的膨脹。下表羅列了當n-gram中的n增大,模型的參數數量也指數膨脹。
離散表示存在的問題
1.無法衡量詞向量之間的關係
比如下面三個詞,酒店,賓館,旅社,應該是三個近義詞,但是他們在用離散方式表示出來的詞向量中完全無從得知他們是否是相似的。
2.詞表維度隨著語料庫的增長而不斷膨脹。
3.n-gram詞序列隨語料庫膨脹更快。
4.存在數據稀疏問題。
2.4 分散式表示--distribued representation
由於分散表示存在諸多問題,所以人們開始想能不能用分散式的方式來表示呢?
1957年就有學者提出「用一個詞附近的其他詞來表示該詞」
基於以上理論,我們來講一講「共現矩陣(Cocurrence matrix)
共現矩陣現在主要用於發現主題(topic),比如LAS(Latent Semantic Analysis)
假設我們現在有以下三個文檔(分別是是三句話)
將window length設置為1,即對任意一個詞,在其左右兩邊相隔1個詞之內的才可以看作是這個詞的相近詞。(窗口長度一般設為5-10之間)
基於窗口大小為1,可以轉換成如下矩陣(共現矩陣)。比如當I出現的時候,1個長度的窗口內,like出現了2次,enjoy出現了1次。如果兩個詞共同出現的次數越多則說明這兩個詞越相似。
將共現矩陣的行或列作為詞向量也存在著一些問題:
1.向量維數隨著詞典大小線性增長,即詞越多,則向量維度也隨之越大。
2.存儲整個詞典的空間消耗非常大。
3.一些模型如文本分類模型會面臨稀疏性問題
4.模型會欠穩定。當有新的文檔進來之後,整個矩陣要重新計算。
2.5 SVD降維
因為共現矩陣的存在維度大又稀疏的問題,於是人們考慮著使用某種方法將其降維。最直接的想法就是用SVD對貢獻句子向量做降維。
下面貼上了一份python代碼用來做詞向量的降維。
SVD其實就是矩陣分解,將原來的稀疏矩陣分成三個稠密矩陣。
S是一個對角矩陣。每個元素就是一個主成分,我們可以提取出主成分比較高的一些元素。
SVD降維也存在一些問題:
1.計算量隨著語料庫和詞典的增長膨脹太快。
2.難以為慈溪店中新加入的詞分配詞向量。
3.與其他深度學習模型框架差異大。
3. 神經網路語言模型NNLM
NNLM全稱Neural Network Language model,直接從語言模型出發,將模型最優化過程轉化為求詞向量表示的過程。
既然離散的表示有辣么多缺點,於是有小夥伴就嘗試著用模型最優化的過程去轉換詞向量了。
3.1 目標函數
NNLM的目標函數如下:
比如」我/是/中國/人「這句話,Wt是「人」,是「人」前面的詞,前面的詞的長度我們叫前向窗口函數,窗口長度為n-1,因為只有前面的詞,所以是非對稱的前向窗口函數。也就是說目標函數求的是,當「我」「是」「中國」這幾個詞出現的時候,後面出現「人」的概率的最大值。
這個窗口會滑動遍歷整個語料庫並且求和,計算量正比與語料庫的大小。
概率P滿足歸一化條件,這樣不同位置t處的概率才能相加,即:
3.2 NNLM的結構
首先,不要方。
然後,請看左邊的神經網路圖,解讀這個NNLM的神經網路總共可以分成4步:
第一步:模型的輸入
最底層的小綠方塊是輸入的數據,每一個輸入是一個詞,但不是一個文本形式的詞,而是一個One-hot的詞向量,即一個向量中,只有這個詞所在的索引處為1,其他位置都為0。假設我們又40000個詞,那麼就有40000個詞向量的輸入。
第二步:詞嵌入
從最後一層的小綠到最後第二層的過程叫做word-embedding,詞嵌入。
是這樣的,首先我們要自己初始化一個投影矩陣C(用稠密響亮表示)。這個投影矩陣的行數是「維度」一般設置為500,列數是輸入的詞向量的大小,40000。(500*40000)
矩陣中的權值w是可以事先人為初始化,在訓練模型的時候會找到最優的w值的,所以初始化的時候隨意。
將輸入的每個One-hot詞向量都分別乘以投影矩陣C,因為每個詞向量上只有自己的索引處為1,所以相乘後C中只會有對應的一列被保留,這一列就是導數第二層所得到的數據。此時One-hot的向量變成了一個500*1的向量。也就是說整一個詞嵌入層有500*1*40000維。
第三步:隱藏層
將詞嵌入向量輸入隱藏層。這裡隱藏層和我們之前學的神經網路中的隱藏層是一樣的,同時也是個全鏈接。如果隱藏層有100個神經元,那麼權重θ的個數就是500*40000*100。在進行線性轉換後輸入激勵函數tanh,激勵函數的輸出為隱藏層的輸出。
第四步:輸出層
最上面的一層是輸出層,輸出層的個數與輸入的詞的個數相同,為40000。
從激勵層出來進過softmax轉換,就會有40000個輸出結果,每個結果是一個向量,對應一個詞,向量里是這個詞屬於每個詞的概率,如果效果好的話,應該是one-hot向量里本來是1的位置,在輸出的概率向量里,應該是概率越大越接近於1才好(語無倫次了。。。)。
以上就是NNLM的結構了。然後根據上面提到的目標函數求解最大值,利用BP+SGD去尋找最優的權重θ和投影矩陣中的W值。
最後,NNLM就做好了。。。
3.3 計算複雜度
計算的複雜度如下計算
N * D + N * D * H + H * V
N是輸入的詞的個數,D是投影矩陣的維度,H是隱藏層的維度,V是詞數
可以看出,計算的複雜度相當高啊,而且語料庫中的詞越多的話複雜度越大,這麼逆天的複雜度要讓工業界實戰的小夥伴分分鐘淚崩的。谷歌的高智商童鞋們肯定耐不住寂寞,於是的於是有了接下來要講的內容。
4. Word2vec -- CBOM(連續詞袋)4.1 結構
由於NNLM的計算複雜度,谷歌的小夥伴們提出了word2vec的兩個方法,一個是CBOM,中文一般叫連續的詞袋。
計算量最大的應該是從詞嵌入層到隱藏層的這一步,因為詞嵌入層的輸出是500*40000,隱藏層如果有100維的話,權重的數量就達到了500*40000*100個了,於是小夥伴們就想能不能壓縮這個詞嵌入層呢?再於是,他們將40000個維度相加成了1個維度,也就是將500*40000壓縮成了500*1,現在詞嵌入後的輸出就變成了500維,隱藏層的計算複雜度就減小到了500*100了。
但是這樣的複雜度還是沒有達到完美主義學者的要求,他們又將原來的隱藏層也去掉了,直接將詞嵌入的結果相加降維後輸入了softmax到達輸出層。這樣計算的維度就從500*100 + 100 *40000變成了500*40000了。
除了以上兩個變化,CBOM還有一些特殊特性。CBOM不再採用之前NNLM的前向窗口函數,而是使用雙向上下文窗口,比如「我/是/中國/人」中的「中國」的概率是由左右兩邊的詞共同影響的:P(中國/我,是,人),並且是沒有順序的:P(中國/我,是,人) = P(中國/是,我,人)
4.2 目標函數
仍然是通過求目標函數的最大值來獲取最優的參數。
第一個公式,w是某個詞,context(w)是w的上下文,也就是左右的詞,詞數根據設置的窗口大小而定。
第二個公式是由第一個公式轉化而來,在求最優化時我們一般將它轉化成log的形式。並且帶入了降維後的詞嵌入矩陣到輸出層的運算公式。
第三個公式是第二個公式轉化而來,因為一個詞的出現有多個上下文詞,所以用j去遍歷。
4.3 層次Softmax
雖然通過以上若干的改進,CBOM已經比NNLM要簡化與優化很多了。但是雖然通過去隱層,求和等方式將維度將至了500*40000,但是好不容易降到了500,在最後一步又上升到了40000,讓人內心略感不爽。所以,之後就提出了兩種解決辦法來降低最後一步的計算複雜度。首先介紹一下「層次Softmax」
我們來看output layer的這一步,這裡使用了Huffman樹。如果要找到「足球」這個詞的輸出結果,只需要沿著這棵樹的根,一直下來走4步就到了「足球」的葉子節點。如果按照之前的方法,我們需要去對40000個詞都平鋪計算一遍才能獲得「足球」這個詞的輸出概率向量。
霍夫曼樹上的每個節點其實可以看成是一個分類器,「足球」這個詞就是經過了4個二分類的分類器計算出來的。通過這種方式,只需要計算路徑傻瓜所有非葉子結點詞向量的貢獻即可。計算量降為樹的深度:V => log_2(V)
下面是層次Softmax的目標函數:
4.4 負例採樣
負例採樣是另一種改進輸出層的方式。
在原來的輸出層中,會輸出40000個向量,每個向量又會有40000個維度,然後只有一個維度是正樣本,V-1個(39999)為負樣本,但其實我們要得到的只是哪一個正樣本,所以對於那麼多的負樣本我們沒有必要全部去計算,只需要通過某種方式去採樣然後計算即可。
它的目標函數是:對語料庫中所有詞W求和
詞典中的每個詞對應一條線段。這些線段組成了[0,1]這個區間。
現在將[0,1]劃分成M=10^8等分,每次隨機生成[1,M-1]間的整數,看看這些整數落在哪個詞對應的部分上。
5 Word2vec -- Skip-Gram 模型
這是 Word2vec的另一個模型(Spark mmlib中提供的方法包就是依賴於這個模型的)
與CBOM不同的地方只有一個。CBOM是通過上下文去求中心詞的最大概率。而Skip-Gram是通過中心詞去求上下文詞的最大概率。
目標函數是:
概率密度有Softmax給出:
因為其他過程與CBOM一致,故再次不贅述了。
Word2Vec存在的問題:
1.對每個local context window單獨訓練,沒有利用包含在global cocurrence矩陣中的統計信息。
2.對多義詞無法很好的表示和處理,因為使用了唯一的詞向量。
6 GloVe
基於Word2Vec的局部性的缺陷,後來提出了GloVe予以解決。
關於GloVe的詳細講解,後續再附文詳說。
從小就錯別字多。。改不掉了。。
關於Spark學習技巧
kafka,hbase,spark,Flink等入門到深入源碼,spark機器學習,大數據安全,大數據運維,請關注浪尖公眾號,看高質量文章。
更多文章,敬請期待


TAG:Spark高級玩法 |