自然語言處理基本概念、詞向量發展、語言模型

1. 基本概念

1. 1 語料庫&詞典

  • 一般語料庫就是很多篇文章(可能一篇文章有好幾句話,也可能只有一句話),在實際業務中,每篇文章一般要先進行分詞
  • 詞典:語料庫中詞的種類數,即有多少個詞,一般用|V|表示
  • 樹中根節點就是最上面那個,葉子結點就是結果(如分類的標籤),結點泛指所有(包括根節點、葉子結點)

2. 詞向量:one-hot & 特徵、標籤的ont-hot編碼

2.1 詞向量one-hot

對於訓練數據,如
[[the, cat, is, running],
[the, dog, is, cute, that, dog, is, smart]]
one_hot就是詞出現一次就打上標籤1。上述訓練數據轉換之後爲
在這裏插入圖片描述
可以看到最上面一行就是詞的種類,第二行是第一個樣本的one-hot轉換,第三行是第二個樣本的ont-hot轉換。只要出現了該詞,就打1,無論出現了多少次。
ont-hot首先沒考慮詞的順序,也沒考慮詞出現的次數。且容易造成很大的稀疏矩陣。

  • one-hot代碼實現
from sklearn.feature_extraction.text import CountVectorizer
corpus = [
     'This is the first document.',   # 輸入的文本是一個字符串
     'This document is the second document.',
     'And this is the third one.',
     'Is this the first document?']
vectorizer = CountVectorizer(binary=True)    # 注這裏binary一定爲True,否則就不是one-hot了
X = vectorizer.fit_transform(corpus)
print(vectorizer.get_feature_names())
print(X.toarray())
['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']
[[0 1 1 1 0 0 1 0 1]
 [0 1 0 1 0 1 1 0 1]
 [1 0 0 1 1 0 1 1 1]
 [0 1 1 1 0 0 1 0 1]]        

2.2 特徵、標籤的ont-hot編碼

  • 除了上述將整個文本用one-hot表示,機器學習中還會遇到將類別特徵進行ont-hot編碼。如多元線性迴歸中,其中一個特徵是性別(男,女),這時,我們就想讓男、女各單獨作爲一個特徵,如果是男,男的特徵就爲1,相應的女的特徵就爲0。具體實現如下
# sklearn 實現---1
# sklearn中OneHotEncoder可以同時對多列特徵就行one-hot處理
from sklearn.preprocessing import OneHotEncoder
enc = OneHotEncoder(handle_unknown='ignore')
X = [['Male', 'a', 'c'], 
     ['Female', 'b', 'd'], 
     ['Female', 'c', 'e']]
enc.fit(X)  # 第一列是性別特徵,可以分成兩列,分別代表是不是男,是不是女;其他兩列亦同。
print(enc.categories_)
enc.transform([['Female', 1, 2],    
               ['Male', 'e', 'e']]).toarray()  # 當transform出現訓練集沒有的,如本來是男女,突然出現一個“unknown”,這時男、女兩列都爲0,因爲handle_unknown='ignore',還可以設置成’error’
               # 即出現訓練集沒有的就報錯
[array(['Female', 'Male'], dtype=object), array(['a', 'b', 'c'], dtype=object), array(['c', 'd', 'e'], dtype=object)]
array([[1., 0., 0., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0., 0., 1.]])
# pandas實現
s = pd.Series(['male', 'female', 'male', 'female', np.nan]) 
pd.get_dummies(s)

在這裏插入圖片描述
上面pandas實現的時候是以特徵性別爲例的,如果換成標籤,就是對標籤進行one-hot編碼
在這裏插入圖片描述
標籤的one-hot編碼,pytorch、tensorflow(包括keras)都實現了自己的api,在此不再贅述。

3. 詞向量:詞袋

上一章one-hot詞向量不考慮單詞出現的次數,如

[[the, cat, is, running],
[the, dog, is, cute, that, dog, is, smart]]
雖然第二句話,the出現了兩次,但最終the仍表示成1。
詞袋就是在one-hot基礎上發展而來的,只是不再僅僅表示成1,而是單詞出現的個數。此時該例的結果爲
在這裏插入圖片描述

  • 代碼實現
from sklearn.feature_extraction.text import CountVectorizer
corpus = [
     'the cat is running',
     'this dog is cute that dog is smart']
vectorizer = CountVectorizer(binary=False)   # binary改爲False就是統計出現的次數,即詞袋了 
X = vectorizer.fit_transform(corpus)
print(vectorizer.get_feature_names())
print(X.toarray())
['cat', 'cute', 'dog', 'is', 'running', 'smart', 'that', 'the', 'this']
[[1 0 0 1 1 0 0 1 0]
 [0 1 2 2 0 1 1 0 1]]

4. 詞向量:n-gram & 語言模型:n-gram

4.1 詞向量

還是該例
[[the, cat, is, running],
[this, dog, is, cute, that, dog, is, smart]]
上兩章都是以一個單詞爲單元統計是否出現或出現的次數,但有時兩個單詞可能更有意義,如do not,統計do not出現的次數更加符合實際,這就是n-gram詞向量表示。下面從代碼中具體理解,依然用的是sklearn的CountVectorizer這個API

from sklearn.feature_extraction.text import CountVectorizer
corpus = ['the cat is running',
         'this dog is cute, that dog is smart']
vectorizer = CountVectorizer(ngram_range=(1,2)) # (1,2)表示統計單個詞出現的次數及兩個詞一起出現的次數  
X = vectorizer.fit_transform(corpus)
print(vectorizer.get_feature_names())
print(X.toarray())
['cat', 'cat is', 'cute', 'cute that', 'dog', 'dog is', 'is', 'is cute', 'is running', 'is smart', 'running', 'smart', 'that', 'that dog', 'the', 'the cat', 'this', 'this dog']
[[1 1 0 0 0 0 1 0 1 0 1 0 0 0 1 1 0 0]
 [0 0 1 1 2 2 2 1 0 1 0 1 1 1 0 0 1 1]]

4.2 sklearn.feature_extraction.text.CountVectorizer講解

one-hot向量、詞袋向量、n-gram向量都是用的該API,下面就對該API詳細介紹一下
在這裏插入圖片描述
藍色填充表示比較重要。其中,重音可參考https://zimt8.com/questions/517923/

from sklearn.feature_extraction.text import CountVectorizer
corpus = [
     'a fine cut',
     '他 喜歡 動物']
vectorizer = CountVectorizer(binary=False, analyzer='word', ) 
X = vectorizer.fit_transform(corpus)
print(vectorizer.get_feature_names())
print(X.toarray())
['cut', 'fine', '動物', '喜歡']
[[1 1 0 0]
 [0 0 1 1]]

從結果我們可以看到,在生成的詞典裏沒有a、他。原因如下:
沒有指定vocabulary,即生成的詞典需要CountVectorizer根據分詞結果及max_df、max_features等自己生成;此外沒有指定tokenizer,及分詞函數沒有指定;在上述兩個參數都沒指定的情況下,就會看參數token_pattern,而我們又沒有指定,那麼CountVectorizer就會以單個字母或漢字及各種標點符號當做分詞的標準。所以上述代碼沒有a和他。如果想讓a和他也加入到生成的詞典中,有三種方法

  • 指定vocabulary,此時tokenizer/token_pattern/stop_words/max_df等都無效,即和分詞有關的參數都無效。可以看到最終生成的詞典只有我們參數中指定的a/他/喜歡
    在這裏插入圖片描述
  • 在不指定vocabulary的情況下,指定tokenizer,即指定分詞的函數,此時stop_words等還是有效的,因爲我們指定這個參數,僅僅是確定如何分詞,不影響最終生成的詞典還需要去停用詞,去不滿足max_features等的詞,但是token_pattern無效
    在這裏插入圖片描述
  • 在不指定vocabulary和tokenizer的情況下,指定token_pattern,該參數和tokenizer相似,指定了要保留哪些詞,需要用正則表達式。
    在這裏插入圖片描述

4.3 語言模型:n-gram

在講n-gram之前,首先了解什麼是樸素貝葉斯,而樸素貝葉斯模型和n-gram模型都屬於語言模型的一種

4.3.1 語言模型

語言模型就是計算一個句子的概率模型。比如以下兩句話
今天天氣很好,我們出去玩吧。
今天天氣很好,俺們出去玩吧。
如果第一句話出現的概率爲80%,第二句話出現的概率爲60%,那麼我們認爲第一句話更爲合理。
如何計算一句話出現的概率?
假如計算"我愛你"這句話出現的概率
令s=“我愛你”
那麼
p(s)=p()p()p()p(s)=p(我)p(愛|我)p(你|我愛)
一般得,一句話s=(ω1,ω2,...,ωn)s=(\omega_1, \omega_2,...,\omega_n)出現的概率爲:
p(s)=p(ω1)p(ω2ω1)p(ω3ω1,ω2)...p(ωnω1,ω2,...,ωn1)p(s) = p(\omega_1)p(\omega_2|\omega_1)p(\omega_3|\omega_1,\omega_2)...p(\omega_n|\omega_1,\omega_2,...,\omega_{n-1})
語言模型的缺點:

  • 參數空間過大,條件概率p(ωnω1,ω2,...,ωn1)p(\omega_n|\omega_1,\omega_2,...,\omega_{n-1})d的可能性太多,無法估算
  • 數據稀疏嚴重,對於非常多詞的組合,在語料庫中都沒有出現,依據最大似然估計得到的概率將爲0

4.3.2 n-gram

上一小節提到參數空間過大的問題,因此引入一個假設:任意一個詞出現的概率只與它前面出現的有限的一個或幾個詞有關。
假設一個詞出現的概率只有他前面的一個詞有關,這種假設被稱爲馬爾科夫假設。此時S的概率變爲
p(ω1,...ωn)=p(ω1)p(ω2ω1)p(ω3ω2)...p(ωnωn1)p(\omega_1,...\omega_n)=p(\omega_1)p(\omega_2|\omega_1)p(\omega_3|\omega_2)...p(\omega_n|\omega_{n-1})
接下來的問題變成估計條件概率p(ωiωi1)p(\omega_i|\omega_{i-1}):
p(ωiωi1)=p(ωi,ωi1)p(ωi1)p(\omega_i|\omega_{i-1})=\frac{p(\omega_i, \omega_{i-1})}{p(\omega_{i-1})}
當樣本量很大時,基於大數定律,一個短語或者詞語出現的概率可以用其頻率來表示,即:
p(ωiωi1)=count(ωi,ωi1)count(ωi1)p(\omega_i|\omega_{i-1})=\frac{count(\omega_i, \omega_{i-1})}{count(\omega_{i-1})}
舉個例子,假設語料庫詞語出現的次數如下表所示:
在這裏插入圖片描述
對於第二個表格,其意義是豎列中的單詞出現後,橫列單詞出現的次數。那麼如何從語料庫得到上述表格?一般語料庫是很多篇文檔(個人認爲如果一篇文檔也很多句話,那麼可以將每句話都當成一個獨立的文檔,後續所說文檔指的是文檔中的每句話),首先將文檔進行分詞。最終是類列表的結構,比如
[[the, cat, is, running],
[the, dog, is, cute, that, dog, is, smart]]
然後根據前面講到的詞袋進行統計單個單詞出現的次數,兩個單詞出現的次數
現在假如我們現在要計算p(want|i):
p(wanti)=count(i,want)count(i)=8272533=0.33p(want|i)=\frac{count(i,want)}{count(i)}=\frac{827}{2533}=0.33
對於一句話s = “I want food”,其計算公式爲:
p(s)=p(I)p(wantI)p(foodwant)p(s)=p(I)p(want|I)p(food|want)
一般地,對於首個單詞的概率p(I)我們認爲p(I)=p(I<s>)p(I)=p(I|<s>),其中<s><s>表示一篇文章的開始,此外<e><e>表示一篇文章的結束。因此p(s)=p(I<s>)p(wantI)p(foodwant)p(<e>food)p(s)=p(I|<s>)p(want|I)p(food|want)p(<e>|food)
對於p(I)=p(I<s>)p(I)=p(I|<s>),上表中沒有體現,現假設我們有10000篇文章,其中5000個文章的首個單詞(除了<s><s>)都是以I開頭的,那麼p(I)=p(I<s>)=500010000=0.5p(I)=p(I|<s>)=\frac{5000}{10000}=0.5
對於p(wantI)p(foodwant)p(want|I)、p(food|want)在表格中有體現,分別爲0.33和0.0065。
對於p(<e>food)p(<e>|food)在表格中也沒有體現,假設我們10000篇文章中有100篇是以food結尾的,那麼p(<e>food)=10010000=0.01p(<e>|food)=\frac{100}{10000}=0.01
因此,最終:
p(s)=p(I<s>)p(wantI)p(foodwant)p(<e>food)=0.50.330.00650.01=1.0725e5p(s)=p(I|<s>)p(want|I)p(food|want)p(<e>|food)=0.5*0.33*0.0065*0.01=1.0725e-5

如果一個詞的出現與它周圍的詞是獨立的,稱爲一元語言模型(unigram);
如果一個詞的出現僅依賴於它前面的一個詞,稱爲二元語言模型(bigram);
如果一個詞的出現依賴於它前面的兩個詞,稱爲三元語言模型(trigram);
其他的以此類推

下表是各種語言模型的參數量:
在這裏插入圖片描述
由此可見當n很大時,參數量也是很大的,一般我們用2-3即可。
關於語言模型的代碼好像還沒有進行封裝,因此我們可以根據上述的CountVectorizer等自己寫代碼計算。語言模型更多的是理論,作爲後續推出的各種模型的基礎,如nnlm。

4.3.3 樸素貝葉斯模型&n-gram模型

上一小節講到了n-gram,此n-garm是語言模型的一種假設,本小節所述n-garm以及貝葉斯是一種分類模型,具體詳見博客https://blog.csdn.net/weixin_43178406/article/details/105702387

5. 詞向量tf-idf

給定訓練語料(corpus),如
[[the, cat, is, running],
[this, dog, is, cute, that, dog, is, smart]]
假如我們最終的詞典爲[‘cat’, ‘cute’, ‘dog’, ‘is’, ‘running’, ‘smart’, ‘that’, ‘the’, ‘this’]
那麼the這個詞經過tf-idf處理,就會得到一個對應的數字,詞典中其他單詞亦同。
那麼這個數字是如何得到的?
tf-idf = tf × idf

  1. 計算tf值,tf值就是詞頻,表示一個給定詞語t在一篇給定文檔d中出現的頻率,即單詞t出現的次數除以當前文檔中詞出現的總次數。tf越高,則詞語t對文檔d來說越重要,TF越低,則詞語t對文檔d來說越不重要。
  2. 計算idf值:逆文檔頻率。tf值很高,並不能說明該詞對文章的貢獻度高,比如單詞is在所有文章中出現的次數都很高,其tf值甚至高於關鍵詞如doctor,但is並不是我們想要的(doctor纔是)。所以此時需要計算一個逆文檔頻率。如is所有文檔中出現次數都很高,那麼出現該詞的文章數除以所有文章數就越大,其倒數越小。因此用logtlog\frac{總文章數}{出現單詞t的文章數}來限制tf
  3. 實際應用中,idf需要進行平滑處理,因爲除數爲0,就無法計算了。常用的平滑方法如下:
    log+1t+1+1log\frac{總文章數+1}{出現單詞t的文章數+1}+1
    logt+1log\frac{總文章數}{出現單詞t的文章數+1}
  • 實例代碼
# 使用sklearn中的TfidfVectorizor
from sklearn.feature_extraction.text import TfidfVectorizer
corpus = ['the cat is running',
         'this dog is cute, that dog is smart']
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(corpus)
print(vectorizer.get_feature_names())
print(X.toarray())
['cat', 'cute', 'dog', 'is', 'running', 'smart', 'that', 'the', 'this']
[[0.53404633 0.         0.         0.37997836 0.53404633 0.
  0.         0.53404633 0.        ]
 [0.         0.3158336  0.6316672  0.44943642 0.         0.3158336
  0.3158336  0.         0.3158336 ]]
# 使用sklearn中的TfidfTransformer,與CountVectorizer搭配使用

# 先使用CountVectorizer
from sklearn.feature_extraction.text import CountVectorizer
corpus = ['the cat is running',
         'this dog is cute, that dog is smart']
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(corpus)
print(vectorizer.get_feature_names())
print(X.toarray())

# 將X傳入TfidfTransformer
from sklearn.feature_extraction.text import TfidfTransformer
idf = TfidfTransformer()
res = idf.fit_transform(X)  # 傳入的X可以是稀疏矩陣,也可以是轉換後的array
print(res.toarray())
['cat', 'cute', 'dog', 'is', 'running', 'smart', 'that', 'the', 'this']
[[1 0 0 1 1 0 0 1 0]
 [0 1 2 2 0 1 1 0 1]]
[[0.53404633 0.         0.         0.37997836 0.53404633 0.
  0.         0.53404633 0.        ]
 [0.         0.3158336  0.6316672  0.44943642 0.         0.3158336
  0.3158336  0.         0.3158336 ]]

可以發現TfidfVectorizer是TfidfTransformer和CountVectorizer的結合。因此TfidfVectorizer中的參數也是TfidfTransformer和CountVectorizer參數的結合。上文已經講過CountVectorizer的參數了,現在詳解TfidfTransformer的參數。

  • use_idf:boolean,是否使用idf限制tf,如果爲False,結果僅僅是tf
  • smooth_idf:boolean,是否對idf加入平滑
    idf教科書公式logt+1log\frac{總文章數}{出現單詞t的文章數+1}
    smooth_idf=Truelog+1t+1+1log\frac{總文章數+1}{出現單詞t的文章數+1}+1
    smooth_idf=Falselogt+1log\frac{總文章數}{出現單詞t的文章數}+1
  • sublinear_tf:boolean,是否對tf進行轉換。如果爲False,tf計算的是詞出現的次數,而不是詞的頻率,這一點和平時也不一樣;如果爲True,則tf爲1+log(tf),這裏的tf仍然指詞出現的次數
  • norm:'l1’或’l2’或None。l1表示對計算出的tf-idf值進行l1範數的標準化;l2表示對計算出的tf-idf值進行l2範數的標準化;None不進行標準化。其中l1範數和l2範數都是對每一行,即每個文章
    l1範數公式vnorm=vv1=vv1+v2+...+vnv_{norm}=\frac{v}{||v||_1}=\frac{v}{|v_1|+|v_2|+...+|v_n|}
    l2範數公式vnorm=vv1=v(v12+v22+...+vn2)12v_{norm}=\frac{v}{||v||_1}=\frac{v}{(|v_1|^2+|v_2|^2+...+|v_n|^2)^\frac{1}{2}}

6. 詞向量發展&語言模型

在這裏插入圖片描述

目前語言模型就是n-gram語言模型,有時,利用n-gram模型可以計算出副產品詞向量
上述其他詞向量可見其他博客

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章