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=“我愛你”
那麼
一般得,一句話出現的概率爲:
語言模型的缺點:
- 參數空間過大,條件概率d的可能性太多,無法估算
- 數據稀疏嚴重,對於非常多詞的組合,在語料庫中都沒有出現,依據最大似然估計得到的概率將爲0
4.3.2 n-gram
上一小節提到參數空間過大的問題,因此引入一個假設:任意一個詞出現的概率只與它前面出現的有限的一個或幾個詞有關。
假設一個詞出現的概率只有他前面的一個詞有關,這種假設被稱爲馬爾科夫假設。此時S的概率變爲
接下來的問題變成估計條件概率:
當樣本量很大時,基於大數定律,一個短語或者詞語出現的概率可以用其頻率來表示,即:
舉個例子,假設語料庫詞語出現的次數如下表所示:
對於第二個表格,其意義是豎列中的單詞出現後,橫列單詞出現的次數。那麼如何從語料庫得到上述表格?一般語料庫是很多篇文檔(個人認爲如果一篇文檔也很多句話,那麼可以將每句話都當成一個獨立的文檔,後續所說文檔指的是文檔中的每句話),首先將文檔進行分詞。最終是類列表的結構,比如
[[the, cat, is, running],
[the, dog, is, cute, that, dog, is, smart]]
然後根據前面講到的詞袋進行統計單個單詞出現的次數,兩個單詞出現的次數
現在假如我們現在要計算p(want|i):
對於一句話s = “I want food”,其計算公式爲:
一般地,對於首個單詞的概率p(I)我們認爲,其中表示一篇文章的開始,此外表示一篇文章的結束。因此
對於,上表中沒有體現,現假設我們有10000篇文章,其中5000個文章的首個單詞(除了)都是以I開頭的,那麼
對於在表格中有體現,分別爲0.33和0.0065。
對於在表格中也沒有體現,假設我們10000篇文章中有100篇是以food結尾的,那麼
因此,最終:
如果一個詞的出現與它周圍的詞是獨立的,稱爲一元語言模型(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
- 計算tf值,tf值就是詞頻,表示一個給定詞語t在一篇給定文檔d中出現的頻率,即單詞t出現的次數除以當前文檔中詞出現的總次數。tf越高,則詞語t對文檔d來說越重要,TF越低,則詞語t對文檔d來說越不重要。
- 計算idf值:逆文檔頻率。tf值很高,並不能說明該詞對文章的貢獻度高,比如單詞is在所有文章中出現的次數都很高,其tf值甚至高於關鍵詞如doctor,但is並不是我們想要的(doctor纔是)。所以此時需要計算一個逆文檔頻率。如is所有文檔中出現次數都很高,那麼出現該詞的文章數除以所有文章數就越大,其倒數越小。因此用來限制tf
- 實際應用中,idf需要進行平滑處理,因爲除數爲0,就無法計算了。常用的平滑方法如下:
- 實例代碼
# 使用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教科書公式
smooth_idf=True
smooth_idf=False - 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範數公式
l2範數公式
6. 詞向量發展&語言模型
目前語言模型就是n-gram語言模型,有時,利用n-gram模型可以計算出副產品詞向量
上述其他詞向量可見其他博客