雖然之前也寫過gensim庫的word2vec的教程,但是對於文本分析的特徵提取並不太理解。最近看了幾篇scikit的外文教程,對機器學習中文本的特徵提取有了一些瞭解。
首先做文本的機器學習(自然語言處理),我們要理解這幾個概念:
-
文檔(document)這裏是指一段單獨的文本信息。這可能是一則短信、一條推特、一封郵件、一本書、或者一首歌詞。一般一個文檔對應於一個觀測值或一行數據。
-
語料(corpus) 文檔的集合(語料大於等於1條文檔)。這相當於我們要研究對象的所有文本數據
- 單詞或詞語(token) 英文中對應的是單詞,漢語是詞語。例如“How are you”這個文檔,是由how、are、you三個單詞組成的。token相當於機器學習中的特徵(列)。
首先讓我們給出一個語料例子。
corpus = ["Hey hey hey lets go get lunch today :)",
"Did you go home?",
"Hey!!! I need a favor"]
CountVectorizer
首先我們要明白,計算機是不能從文本字符串中發現規律的。只有將字符串編碼爲計算機可以理解的數字,計算機纔有可能發現文本中的規律。
最容易理解的實現思路:
-
對文本編碼,就是讓詞語與數字對應起來,建立基於給定文本的詞典。(fit方法 )
- 再根據詞典對所有的文本數據進行轉碼。(transform方法)
scikit庫的CountVectorize類就是這種思路。
from sklearn.feature_extraction.text import CountVectorizer
vectorize = CountVectorizer()
使用fit方法,CountVectorizer()類的會從corpus語料中學習到所有詞語,進而構建出corpus詞典。
#fit學會語料中的所有詞語,構建詞典
vectorize.fit(corpus)
CountVectorizer(analyzer='word', binary=False, decode_error='strict',
dtype=<class 'numpy.int64'>, encoding='utf-8', input='content',
lowercase=True, max_df=1.0, max_features=None, min_df=1,
ngram_range=(1, 1), preprocessor=None, stop_words=None,
strip_accents=None, token_pattern='(?u)\\b\\w\\w+\\b',
tokenizer=None, vocabulary=None)
#這裏我們查看下“詞典”,也就是特徵集(11個特徵詞)
vectorize.get_feature_names()
['did',
'favor',
'get',
'go',
'hey',
'home',
'lets',
'lunch',
'need',
'today',
'you']
這裏,特徵集(get_feature——name)返回的結果,有要注意的事情:
-
所有的單詞都是小寫
-
單詞長度小於兩個字母的,會被剔除掉
-
標點符號會剔除掉
-
不重複
- 這個特徵集是有順序的
接下來會按照這個順序去編碼文本數據,這裏會出現一個新概念文檔-詞頻矩陣(document-term matrix),英文簡寫爲dtm。我們讓機器挖掘文本中的規律時,餵給機器的數據就是 這文檔-詞頻矩陣(document-term matrix)。
#構建 文檔詞頻矩陣
dtm = vectorize.transform(corpus)dtm
<3x11 sparse matrix of type '<class 'numpy.int64'>'
with 13 stored elements in Compressed Sparse Row format>
構建文檔詞頻矩陣。結果3*11。
3指的是語料中的三個文檔;11指的是corpus中11個特徵詞。爲了方便我們理解,我用pandas庫將其展現出來
import pandas as pd
pd.DataFrame(dtm.toarray(), columns = vectorize.get_feature_names())
從上面的dataframe表中,行代表一個文檔,列代表特徵詞。比如第1行,hey列的所對應的單元格值爲3,說明corpus中第一個document(Hey hey hey lets go get lunch today :) 出現了三次hey。
這裏有一個我要提示的重點,在此時我們已經構件號了文檔詞頻矩陣dtm,如果你還想加入新的文檔。我們應該注意tranform和fit的區別。
new_document = ['Hey lets go get a drink tonight']
new_dtm = vectorize.transform(new_document)
pd.DataFrame(new_dtm.toarray(), columns=vectorize.get_feature_names())
即使new_document含有6個單詞,但是在上面的dataframe表中只有4個特徵詞被有效編碼,drink和tonight詞未被表徵。這是因爲我們初識的corpus語料所構建的詞典並未含有這些詞。但是對文本進行特徵表徵時,使用的確實corpus所生產的詞典。
我們機器學習所用的數據,一般被分成訓練集和測試集。訓練集是爲了讓機器學習數據的規律,測試集是爲了驗證規律的有效性。訓練集本質上代表的是過去及現在已知的數據,測試集本質上代表的是未來的未知數據(現在不存在的數據),我們是用已知的數據預測未來。
所以我們只能讓fit方法操作於訓練集,構建基於過去或已知數據的特徵集。
TfidfVectorizer
scikit庫除了CountVectorizer類,還有TfidfVectorizer類。TF-IDF這個定義相信大家應該已經耳熟能詳了:
TF 詞頻出現的次數 IDF = 1/(語料中含有該詞語的文檔的數目)
TF簡單點說有的時候詞語出現越多,這個詞越是特徵詞。但同時,有些“的它呢”等無意義詞出現的多並沒有什麼意義,反而是像“核能”這種詞,雖然不怎麼出現,但是一出現往往很具有特徵性。
綜合TF*IDF,就能很好的刻畫一個詞語是否具有表徵文本信息能力,作爲特徵是否合適。有了上面的基礎知識,我們接下來繼續寫代碼,便於理解TFIDF
from sklearn.feature_extraction.text import TfidfVectorizer
def createDTM(corpus):
"""構建文檔詞語矩陣"""
vectorize = TfidfVectorizer()
#注意fit_transform相當於fit之後又transform。
dtm = vectorize.fit_transform(corpus)
#打印dtm
return pd.DataFrame(dtm.toarray(), columns=vectorize.get_feature_names())
corpus = ["Hey lets get lunch :)",
"Hey!!! I need a favor"]
createDTM(corpus)
我們看到hey這個詞在所有大於0的值裏面,是最小的。因爲hey同時出現在兩個文檔中,不具有文本的表徵能力,所以TFIDF小。而favor和need因爲他們僅僅出現在第二個文檔中,所以他們的TFIDF值更高。
現在我們改變corpus內容。將第一個文檔從Hey lets get lunch改爲Hey hey hey lets get lunch。我們希望第一個文檔的hey詞頻增大,進而使得其TFIDF值變大。
corpus = ["Hey hey hey lets get lunch :)",
"Hey!!! I need a favor"]
createDTM(corpus)
我們發現第一個文檔hey的TFIDF值變大,但這裏有兩點要注意:
-
第一個文檔中其餘特徵詞的TFIDF值變小了
- 第二個文檔中hey的TFIDF未發生變化
上面的例子是通過TF詞頻操作TFIDF值的。下面我們從IDF出發,改變TFIDF值。
corpus = ["Hey hey hey lets get lunch :)",
"I need a favor"]
createDTM(corpus)