【火爐煉AI】機器學習038-NLP建立詞袋模型
(本文所使用的Python庫和版本號: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2, NLTK 3.3)
詞袋模型(Bag Of Words, BOW)和詞向量(Word Embedding, 也叫詞巢狀等)是自然語言處理和文字分析的兩個最常用的模型。
詞袋模型將一段文字看成一系列單詞的集合,由於單詞很多,故而這段文字就相當於一個袋子,裡面裝著一系列單詞。故而計算機的NLP分析就是對這個袋子進行分析,但是計算機不認識文字,只認識數字,那麼我們需要一種機制將袋子裡的文字轉換成數字,這種機制可以是一種Dict對映(key為數字,value為文字等),或陣列(索引為數字,值為文字),或者還可以用HashCode來計算文字的數字表示,而NLP建模就是使用這些數字來建模。詞袋在學習之後,就可以通過構建文件中所有單詞的直方圖來對每篇文件進行建模。
詞向量模型是將單個單詞對映到一個高維空間(維度可以到幾千幾萬甚至幾十萬),這個高維空間就用陣列,或者成為向量來表示,故而建立一種單詞-向量的對映關係,所以成為詞向量模型。但是這種模型能表示的僅僅是單個單詞,對於有多個單片語成的一句話,那麼就需要做進一步處理,比如一個單詞就是一個向量,N個單片語成的一句話就是N個一維向量了,故而可以用N個一維向量組成的矩陣來表示一句話,只不過不同長度的句子,該矩陣的行數不一樣罷了。
下面我們僅僅學習用NLP建立詞袋模型,建立過程主要是提取文字的特徵,構建特徵向量。有兩種方法可以構建特徵向量,分別是CountVectorizer和TfidfVectorizer。
1. 用CountVectorizer提取文字特徵
sklearn模組中的CountVectorizer方法可以直接提取文字特徵,這個函式只考慮詞彙在文字中出現的頻率,這個函式有一個引數:stop_words,表示是否取出停用詞,所謂的停用詞是指為了節省空間和提高效率而自動過濾到的詞語,比如 the, is, at, which等,對於不同的,預設的stop_words不去除停用詞。
# 資料集暫時用簡·奧斯丁的《愛瑪》中的文字 dataset=nltk.corpus.gutenberg.words('austen-emma.txt') # print(len(dataset)) # 192427 代表讀入正常 chunks=split(" ".join(dataset[:10000]), 2000) # 將前面的10000個單詞分成五個詞袋,每個袋子裝2000個單詞 # 構建一個文件-詞矩陣,該矩陣記錄了文件中每個單詞出現的頻次 # 用sk-learn的CountVectorizer函式來實現這種構建過程 from sklearn.feature_extraction.text import CountVectorizer vectorizer = CountVectorizer(min_df=4, max_df=.99) # fit_transform函式需要輸入一維陣列,且陣列元素是用空格連起來的文字 chunks=[" ".join(chunk) for chunk in chunks] # 故而需要轉換一下 doc_term_matrix = vectorizer.fit_transform(chunks) feature_names=vectorizer.get_feature_names() # 獲取 print(len(feature_names)) print(doc_term_matrix.shape) # print(doc_term_matrix.T.toarray()) 複製程式碼
上面將簡·奧斯丁的《愛瑪》中的前面10000個單詞分成了五個詞袋,每個詞袋包含2000個單詞,然後用CountVectorizer建立文字特徵向量,通過fit_transform後就在CountVectorizer物件內部建立了這種文件-詞矩陣,通過print可以看出結果。
為了更加明確的看出裡面的文件-詞矩陣,可以用下面的程式碼將其打印出來:
# 列印看看doc_term_matrix這個文件-詞矩陣裡面的內容 print('Document Term Matrix------>>>>') bag_names=['Bag_'+str(i) for i in range(5)] # 5個詞袋 formatted_row='{:>12}'*(1+len(bag_names)) # 每一行第一列是單詞,後面是每個詞袋中的頻率 print(formatted_row.format('Word', *bag_names)) for word, freq in zip(feature_names,doc_term_matrix.T.toarray()): # 需要裝置矩陣 # 此處的freq是csr_matrix資料結構 output = [str(x) for x in freq.data] print(formatted_row.format(word,*output)) 複製程式碼
-----------------------輸---------出--------------------
Document Term Matrix------>>>> WordBag_0Bag_1Bag_2Bag_3Bag_4 about34011 among11110 because11011 believe01113 believed01112 best12110 better03112 beyond10123
...
-----------------------完--------------------------------
以上是部分結果,可以看出about在Bag_0中出現了3次,在Bag_1中出現了4次,以此類推。
如果對這種矩陣的出現次數有疑惑,可以看我的ofollow,noindex"> 我的github中程式碼 ,裡面有更詳細的解釋。
值得注意的是,CountVectorizer也可以用於中文特徵的提取,但是需要對自定義的split函式進行修改,原來的函式用空格作為分隔符,可以很好的將英文分詞,但對中文無效,故而中文的情況需要將split中的分詞方式改成jieba分詞。
2. 用TfidfVectorizer提取文字特徵
TfidfVectorizer的主要特點是:除了考量某詞彙在文字出現的頻率,還關注包含這個詞彙的所有文字的數量,這個方法能夠削減高頻沒有意義的詞彙帶來的影響,挖掘更有意義的特徵。一般的,當文字條目越多,這個方法的效果越顯著。
在程式碼上,用TfidfVectorizer和上面的CountVectorizer幾乎一樣,只是將類名稱替換一下即可。
# 資料集暫時用簡·奧斯丁的《愛瑪》中的文字 dataset=nltk.corpus.gutenberg.words('austen-emma.txt') # print(len(dataset)) # 192427 代表讀入正常 chunks=split(" ".join(dataset[:10000]), 2000) # 將前面的10000個單詞分成五個詞袋,每個袋子裝2000個單詞 from sklearn.feature_extraction.text import TfidfVectorizer vectorizer = TfidfVectorizer() # fit_transform函式需要輸入一維陣列,且陣列元素是用空格連起來的文字 chunks=[" ".join(chunk) for chunk in chunks] # 故而需要轉換一下 doc_term_matrix = vectorizer.fit_transform(chunks) feature_names=vectorizer.get_feature_names() # 獲取 print(len(feature_names)) print(doc_term_matrix.shape) 複製程式碼
打印出來的結果並不是某個單詞在詞袋中出現的頻率,而是tf-idf權重,這個權重有個計算公式,tf-idf=tf*idf,也就是說tf與idf分別是兩個不同的東西。其中tf為謀個訓練文字中,某個詞的出現次數,即詞頻(Term Frequency);idf為逆文件頻率(Inverse Document Frequency),對於詞頻的權重調整係數。
########################小**********結###############################
1,用於詞袋模型中提取文字特徵主要有兩種方法:CountVectorizer和TfidfVectorizer,其中CountVectorizer構建的文件-詞矩陣裡面是單詞在某個詞袋中出現的頻率,而TfidfVectorizer構建的矩陣中是單詞的tf-idf權重。
2,一般情況下,文件的文字都比較長,故而使用TfidfVectorizer更好一些,推薦首選這個方法。
#################################################################
注:本部分程式碼已經全部上傳到( 我的github )上,歡迎下載。
參考資料:
1, Python機器學習經典例項,Prateek Joshi著,陶俊傑,陳小莉譯