Nature Language Processing:自然語言處理
本文爲斯坦福課程CS224N的課堂筆記。極力推薦入門NLP的朋友閱讀此課,因爲課件完整,b站有搬運,同時還有作業,可以一邊實踐一邊做。b站自查:CS224N
自然語言處理簡介
語言是一種充滿不確定性的信息,是人類在發展過程中進步不可或缺的一塊拼圖。可以想象,經過了數千甚至上萬年的衍化,人類已經將語言創造成爲一個十分神奇的工具——只用極少數的文字就能傳遞大量的信息,如同我現在在寫博客,如同郭b說:“我看到太陽從海平面升起。”,我就立馬回想到這個場景。這是一個十分神奇的過程,所以我們常常將語言理解爲高度壓縮的信息。
自然語言處理即是我們想使用計算機模型去建模語言,以使計算機完成語言處理的任務。可想而知,這個過程十分的複雜和困難。同時NLP任務的可拓展性也很強,因爲語言是一種時序信號,對語言的預測可以延伸到對所有時序信號的預測。如果把數值和詞語做等號,我們預測任何不完整句子的下一個單詞,就等同於在天氣預報中預測明天的天氣。
但過程說起來簡單,實際上是如何執行的呢?我們首先從最基礎的單元,詞語(word)說起。
Word Meaning
詞典
我們如何去表示一個詞語,這是一個很大的問題。首先我們想到的是建立一個詞典,WordNet就是如此,大家可以去自行百度。這是一個詞庫,根據不同的詞性建立起詞和詞的關係。同樣,想使用這樣的詞典可以通過python的NLTK庫完成。
然而,使用詞典存在很大的弊端:人類的語言是不斷進步的,每年都會創造出新的詞語,整新活,這是老邏輯了。所以詞典存在最大的弊端就是:
- 缺少新詞的含義;
- 並不能完全的整合所有詞,即便可以,它的數量也是十分龐大的。
其中第二點是與我們對詞的表示相關的,一個很明顯的事實是,我們不能直接將詞、字母(或漢字)輸入電腦,因爲計算機是不會知道你輸入的“滾”,“Hello” 和 “金b” 這些詞有什麼區別,對於他們來說,如果輸入的內容沒有與其佔用的存儲無關,則他們都是一樣的。
所以,在傳統的NLP中,我們將任意一個詞看作一個離散的符號,則我們處理詞的方式是:
獨熱編碼
使用獨熱編碼(one-hot)進行表示。使用one-hot即如果我想表示的詞典有{“你”, “我”, “他”}這麼大,那麼我令每一個字都對應一個三維向量,所以:“你”→[1, 0, 0]; “我”→[0, 1, 0]; “他”→[0, 0, 1]。
獨熱編碼有明顯的缺陷:
1. 它不能表示所有的詞。對於處理在詞典中從未出現過的詞,我們稱爲Out of Vocabulary Problem(OOV),許多學者都在嘗試解決這樣的問題。OOV問題出現的場景非常多,或者說100%會出現,這是因爲當文中出現了不常見的人名或地名,或合成詞時,他們都屬於OOV場景。解決OOV問題有許多有效的方法,比如Cache Model等,這是題外話。
對於這種問題,學者們也有對應的解決方案,就是使用character-level作爲詞的輸入,即詞根,或者詞綴。這樣的方式非常的有啓發性,因爲對於一個單詞,往往許多詞綴有着相同的意思,如:work - worker; dance - dancer。且對於新詞可以使用增加新詞根的方式而回避了一味增加詞典的問題。我們通常稱之爲派生詞法,derivational morphology。
可是,這種派生詞法儘管十分合理,但也存在着致命問題,即每個詞根可能有多個意思,儘管有人提出了詞袋(類似word-bag的方法,給一個詞根多個意思)的方法,但是由於詞根出現的地方實在是有太多了,這種重複的意思同樣也會傷害詞的表意。
2. 無法表示和學習詞語之間的相關性。由於one-hot編碼中,每個詞都是正交的,我們無法找到詞和詞之間的相關性,他們相互獨立。我們當然想,更好的,使用一個向量去表示一個詞,這裏我們就引入了一個熟悉的名詞:word vector,詞向量。
詞向量
一個one-hot編碼轉化爲一個詞向量的過程非常的簡單,同上例,我們建立一個簡單的詞典,{'I', 'You', 'He'},並分別用一個向量(隨機設定的一個四維向量)表達一個詞,那麼過程是:
我們可以用一個矩陣A運算來代替:
我們稱矩陣A爲一個詞嵌入矩陣(Word embedding matrix),通過A,我們可以將one-hot編碼直接轉化成一個向量。
回過頭來,我們思考,這應該是一個怎樣的向量呢?
語義分佈:用語境表達詞語
直覺上,我們認爲詞語和詞語是由句子構成相關性的:貓是一種寵物,狗也是一種寵物,他們都有四條腿,一個尾巴一張嘴。這個句子表達了貓和狗的相關性。但詞語間的相關性可能比我們想象的更復雜。
既然句子構成了語言,我們將句子裏位置接近的詞,認爲是兩者相關的詞,我們將一個詞在句子中其附近出現的詞稱爲語境(context)。根據句子中每個詞的語境,我們能推斷詞向量的分佈情況,我們成爲distributed representaion。通常,每個詞向量都是一個高維向量,一般的爲50維以上,當然也有100維,1000維。往往詞向量維度越高,它對詞語的表達越好。
這樣的詞向量是如何訓練得,我們先放在一邊,來看一下詞向量得神奇之處。
上圖是將訓練好的詞向量經過降維後得到的結果,可以看到,神奇的是,所有音頻聚集在了圖的左上角,動物在左下角,還有一些國家、學校等等的聚集。如果我們使用訓練好的向量做向量加減法,那麼我們可以很神奇發現:
那麼如此好的向量是如何訓練的呢?
在我們的假設中,詞得相關性與詞和詞互相作爲語境的出現次數有關。則在固定的語料庫(corpus)(即一個超大型的句子庫,作爲訓練的數據集)中,我們可以利用統計,得到詞A與詞B的概率模型,我們可以得到良好的詞向量。
詞頻
使用詞頻構建詞向量時,我們需要統計不同的詞同時出現的頻數,則如果我們還是用剛剛的詞典['I', 'You', 'He'],在一個爲:
"I love you."
"He loves you."
"I do not love him."
"I do not love you."
"He thinks I am a fool."
的corpus中,我們可以統計出以下頻數表格(注意 如果我們詞典只有這三個詞,且統計的爲在一個句子裏同時出現,那麼'love', 'loves', 'do', 'not', '.'等token都爲OOV!):
I | He | You | |
I | 0 | 1 | 1 |
He | 1 | 0 | 1 |
You | 1 | 1 | 0 |
(表格也太醜了)那麼我們就得到了一個詞頻表,這樣我們對每一行做歸一化,就可以得到他們的頻數。
但實際上,真實的語料庫包括了百萬甚至上十億個預料,而詞典的大小也是百萬級,這就讓我們難以畫出表格,更難以統計數據,因爲在獲得word embedding矩陣後,只是將one-hot編程word vector 就需要做 N * N * N 次左右的運算(N爲詞典大小)。
所以我們可以考慮使用降維的方法降低embedding矩陣,以較小運算量。更何況,有些詞很少同時出現,比如“自行車”和“廁紙”;“郭德綱”和“www._____.com”;或者“金b”和“正常人”。
這反映出:
1. word embedding矩陣原本就是十分稀疏的;
2. 語料庫越完備,獲得的詞向量越好。
通過SVD或者PCA降維等方式,我們可以獲得降維後的詞表:,這樣我們就能更好的精煉詞向量,並減少運算量。
而顯然,這樣的方法太過暴力,所得到的詞向量效果也不好,直到Tomas Mikolov 引入了word2vec模型,即大名鼎鼎的skip2gram和CBOW模型。