4.2 特征提取
sklearn.feature_extraction 模塊可以被用來從包含文本或者特片的數據集中提取出適用於機器學習算法的特征。
注意:特征提取和特征選擇是極不相同的:前者由任意數據組成,比如文本或者圖片,轉換為適用於機器學習的數字。后者是應用於這些特征的機器學習方法。 |
4.2.1 從字典中加載特征
類DictVectorizer可以將由python標准的列表dict對象所表示的特征轉換為由scikit-learn中常用的NumPy/SciPy所表示的對象。
雖然dict處理起來不是特別快,但是其優勢在於方便的使用,創建系數矩陣(缺失值不會被存儲)以及將特征名字存儲與值上。
DictVectorizer可以創建我們 稱之為one-of-K或者"one-hot"編碼用於分類特征(又名aka nominal,discrete)特征。分類特征是"屬性-值"對,值限制為一系列的無序離散變量(比如,主題標識、對象類型、標記、名字。。。)
在下面的例子中,"城市"是一個分類屬性,"溫度"是另一個數值特征:
>>> measurements = [ ... {'city': 'Dubai', 'temperature': 33.}, ... {'city': 'London', 'temperature': 12.}, ... {'city': 'San Francisco', 'temperature': 18.}, ... ] >>>from sklearn.feature_extration import DictVectorizer >>>vec = DictVectorizer() #聲明一個字典到向量的變換
>>>vec.fit_transform(measurements).toarray() #fit_transform將上述的字典轉換為一個稀疏矩陣,toarray()將稀疏矩陣轉換為矩陣的形式。 array([[ 1., 0., 0., 33.], [ 0., 1., 0., 12.], [ 0., 0., 1., 18.]])
>>>vec.fit_transform(measurements) (0, 0) 1.0 (0, 3) 33.0 (1, 1) 1.0 (1, 3) 12.0 (2, 2) 1.0 (2, 3) 18.0
|
DictVectorizer對於自然語言處理模型來說也是一個有用的變換,可以提提取特定的單詞的特征。
舉例來說,假設我們有一個算法可以提取出部分演講的標簽(PoS),我們想要使用這些標簽作為補充的標簽來訓練一個序列分類器(比如,一個chunker)。下面的字典可以作為這樣的特征窗口從句子"The cat sat on the mat"中提取出單詞"sat"。
|
該描述可以被向量化一個適用於分類器(可能會被用於text.TfidfTransformer用於正則化)的稀疏的二維矩陣。
|
你可以想象,如果一個人這樣提取在一篇文檔語料庫中的個體單詞的上下語義,最終結果中的矩陣一定是非常大的(許多one-hot-features),大多數時間里,他們的值都為0。為了使結果數據結構更好的適合內存,DictVectorizer類使用一個scipy.sparse矩陣,默認代替numpy.ndarray。
4.2.2 特征散列
類FeatrueHasher是一個高速,低內存向量化程序,其使用一個名為feature hashing的技術,或者"散列方法"。不是對每個參與的特征都建立散列表,像vectorizers那樣,FeatureHasher應用一個散列功能於特征上,直接確定他們的列索引。這樣做的結果就是提高了速度,降低了內存使用,以被檢查為代價。哈希方法不會記住輸入特性的樣子,因此也沒有使用inverse_transform方法。
因為哈希功能可能會造成(不相關)特征之間的沖突,使用有符號哈希函數,哈希值的符號決定了存儲在特性輸出矩陣中的值的符號。
這樣的話,沖突可能會抵消而不是累積誤差,並且任何輸出特性的期望平均值都是零。這種機制是默認使用的alternate_sign=True,對於小的哈希表大小特別有用(n_features < 10000)。對於大的哈希表,可能不適用,允許輸出傳遞給類似於estimators的成員類似於sklearn.naive_bayes.MultinomialNB或sklearn.feature_sele
ction.chi2期望非負輸入的特性選擇器。
FeatureHasher接受要么是映射(dict以及他在collection模塊中的變體),(feature, value)對,或者是字符串,取決於構建參數的類型input_type。映射被當作是(feature, value)對,單一的字符串,被默認為值為1,比如,['feat1', 'feat2', 'feat3']被解釋為[('feat1', 1), ('feat2', 1), ('feat3', 1)]。如果一個單一的字符串在同一個樣本中出現了多次,其對應的值將被累加,如('feat', 2) and ('feat', 3.5) become ('feat', 5.5))。FeatureHasher的輸出經常使用CSR格式的scipy.sparse矩陣。
特征散列可以被應用於文檔分類,但是不像text.CountVectorizer,FeatureHasher不會切分單詞或者其他任何的預處理,處理將unicode編碼轉換為utf-8編碼;參見用hashing trick將大型的預料庫向量化,下面是一個混合的tokenizer/hasher。
作為一個例子,考慮到單詞水平的自然語言處理任務,需要從(token,part_of_speech)中提出特征。可以使用python生成器lai 提取特征。
def token_features(token, part_of_speech): if token.isdigit(): #isdigit()檢驗字符串是否只由數字構成 yield "numeric" else: yield "token={}".format(token.lower())#對字符串進行格式化 yield "token,pos={},{}".format(token, part_of_speech) if token[0].isupper(): #檢驗是否為大寫字母 yield "uppercase_initial" if token.isupper(): yield "all_uppercase" yield "pos={}".format(part_of_speech) |
之后, raw_X使用FeatureHasher.transform進行創建:
raw_X = (token_features(tok, pos_tagger(tok)) for tok in corpus) |
並以hasher表示為:
hasher = FeatureHasher(input_type='string') X = hasher.transform(raw_X) |
得到scipy.sparse矩陣X。
4.2.2.1 實現細節
FeatureHasher使用32位的變量。結果就是該特征支持的最大數字為2^31-1。
由於使用簡單的模將哈希函數轉換為列索引,因此建議使用2的冪作為n_features參數;否則,特性將不會均勻地映射到列。
4.2.3文本特征提取
4.2.3.1詞袋表示
文本分析是機器學習算法的一個主要應用領域。然而,原始數據、符號序列不能直接輸入到算法本身中,因為大多數算法期望的是固定大小的數字特征向量,而不是長度不一的原始文本文檔。
為了解決這個問題,scikit-learn為從文本內容中提取數字特性的最常見方法提供了實用工具,即:
- tokenizing strings and giving an integer id for each possible token, for instance by using white-spaces and punctuation as token separators.
- counting the occurrences of tokens in each document.
- normalizing and weighting with diminishing importance tokens that occur in the majority of samples / documents.
-
tokenizing 文檔中的字符串單位,通常使用空格和標點分割。
-
counting 統計每個文檔中token的個數
-
normalizing 正則化
在該方案中,特征和樣本定義如下:
-
每個標記(token)出現頻率(無論其規范化與否)都被視為一個特征。
-
給定文檔的所有token頻率的向量被認為是一個多變量樣本。
因此,一個文檔語料庫可以用一個矩陣表示,每個文檔有一行,每個標記(例如單詞)有一列出現在語料庫中。
我們將向量化稱為將文本文檔集合轉換為數字特征向量的一般過程。該具體策略(標記化、計數和規范化)被稱為"字袋"或"n-g袋"表示。文檔是通過單詞出現來描述的,而完全忽略了文檔中單詞的相對位置信息。
4.2.3.2 稀疏性
由於大多數文檔通常使用語料庫中使用的單詞的一個非常小的子集,因此生成的矩陣將具有許多0的特征值(通常超過99%)。
例如,包含10,000個短文本文檔(如電子郵件)的集合將使用一個詞匯表,其大小總計為100,000個唯一單詞,而每個文檔單獨使用100到1000個唯一的單詞。
為了能夠在內存中存儲這樣一個矩陣,同時也為了加快代數運算矩陣/向量,實現通常使用稀疏表示,例如scipy中可用的實現。稀疏的包。
4.2.3.3 常見的向量化使用
CountVectorizer以一個簡單的類實現了標記化和頻率計數:
>>> from sklearn.feature_extraction.text import CountVectorizer |
這個模型有很多參數,但是默認值是相當合理的(詳情請看參考文檔):
>>> vectorizer = CountVectorizer() #創建一個詞向量 >>> vectorizer CountVectorizer(analyzer=...'word', binary=False, decode_error=...'strict', dtype=<... '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) |
使用CountVectorizer()統計語料中的單詞:
>>> corpus = [ ... 'This is the first document.', ... 'This is the second second document.', ... 'And the third one.', ... 'Is this the first document?', ... ] >>> X = vectorizer.fit_transform(corpus) #轉換成稀疏矩陣的表示形式 >>> X <4x9 sparse matrix of type '<... 'numpy.int64'>' with 19 stored elements in Compressed Sparse ... format> |
默認配置通過提取至少兩個字母的單詞來標記字符串。可以顯式地請求執行此步驟的特定函數:
>>> analyze = vectorizer.build_analyzer() >>> analyze("This is a text document to analyze.") == ( ... ['this', 'is', 'text', 'document', 'to', 'analyze']) True #a是一個字母,因此默認忽略 |
在此過程中,矩陣的每一列都相當於一個索引。列的解釋可以按照下面的方式進行檢索:
>>> vectorizer.get_feature_names() == ( ... ['and', 'document', 'first', 'is', 'one', ... 'second', 'the', 'third', 'this']) True #獲取特征的名字(token的名字)
>>> X.toarray() array([[0, 1, 1, 1, 0, 0, 1, 0, 1], [0, 1, 0, 1, 0, 2, 1, 0, 1], [1, 0, 0, 0, 1, 0, 1, 1, 0], [0, 1, 1, 1, 0, 0, 1, 0, 1]]...) #每一列都是一個索引,按照上面的特征名字進行排序 |
從特性名稱到列索引的逆向映射存儲在vectorizer的vocabulary ary_屬性中:
>>> vectorizer.vocabulary_.get('document') 1 |
因此,在以后的調用中,將完全忽略在訓練語料庫中沒有看到的單詞:
>>> vectorizer.transform(['Something completely new.']).toarray() ... array([[0, 0, 0, 0, 0, 0, 0, 0, 0]]...) |
指的注意的是,在前面的語料庫中,第一個和最后一個文檔的單詞完全相同,因此以相同的向量編碼。特別是我們失去了最后一份文件是疑問形式的信息。為了保存一些本地排序信息,除了1-g(單個單詞)外,我們還可以提取2-g的單詞。
>>> bigram_vectorizer = CountVectorizer(ngram_range=(1, 2), ... token_pattern=r'\b\w+\b', min_df=1) >>> analyze = bigram_vectorizer.build_analyzer() >>> analyze('Bi-grams are cool!') == ( ... ['bi', 'grams', 'are', 'cool', 'bi grams', 'grams are', 'are cool']) True |
因此,該向量可以提取出更大的詞匯量,從而解決本地位置模式編碼歧義的問題:
>>> X_2 = bigram_vectorizer.fit_transform(corpus).toarray() >>> X_2 ... array([[0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0], [0, 0, 1, 0, 0, 1, 1, 0, 0, 2, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0], [1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1]]...) |
尤其是疑問句"Is this"只出現在最后一份文件中:
>>> feature_index = bigram_vectorizer.vocabulary_.get('is this') >>> X_2[:, feature_index] array([0, 0, 0, 1]...) |
4.2.3.4 Tf-idf加權
在一個大的文本語料庫中,有些單詞非常常見(例如英語中的"the"、"a"、"is"),因此,對於文檔的實際內容幾乎沒有什么有意義的信息。如果我們將直接計數數據直接提供給一個分類器,這些高頻詞將會遮蔽更稀有的、關鍵的術語的頻率。
為了將計數特性重新加權為適合於分類器使用的浮點值,常見的手段是使用tf-idf轉換。
Tf表示詞頻率,Tf - idf表示詞頻乘以該詞的逆文檔頻率。
使用TfidfTransformer的默認設置。
TfidfTransformer(norm='l2', use_idf=True, smooth_idf=True, sublinear_tf=False)
詞語頻率(詞語在給定文檔中出現的次數)與idf相乘,idf組件計算為:
where is the total number of documents,(文檔的總數量) and
is the number of documents that contain term
(包含詞語t的文檔的數量). The resulting tf-idf vectors are then normalized by the Euclidean norm:
這最初是一種用於信息檢索(作為搜索引擎結果的排序函數)的詞語加權方案,在文檔分類和聚類中也得到了很好的應用。
以下部分包含進一步的解釋和示例,說明tf-idfs是如何精確計算的,以及scikit-learn的TfidfTransformer和TfidfVectorizer中tf-idfs是如何計算的,與定義idf的標准教科書符號略有不同
在TfidfTransformer和TfidfVectorizer的smooth_idf=False中,"1"計數被添加到idf中,而不是idf的分母:
TfidfTransformer類實現了這種標准化:
>>> from sklearn.feature_extraction.text import TfidfTransformer >>> transformer = TfidfTransformer(smooth_idf=False) #構建一個分類器 >>> transformer TfidfTransformer(norm=...'l2', smooth_idf=False, sublinear_tf=False,use_idf=True) |
讓我們以下面的計數為例。第一項是100%出現的,所以不是很令人關注。另外兩個在不到50%的情況下出現的特征才可能更能代表文檔的內容:
>>> counts = [[3, 0, 1], ... [2, 0, 0], ... [3, 0, 0], ... [4, 0, 0], ... [3, 2, 0], ... [3, 0, 2]] ... >>> tfidf = transformer.fit_transform(counts) >>> tfidf <6x3 sparse matrix of type '<... 'numpy.float64'>' with 9 stored elements in Compressed Sparse ... format>
>>> tfidf.toarray() array([[ 0.81940995, 0. , 0.57320793], [ 1. , 0. , 0. ], [ 1. , 0. , 0. ], [ 1. , 0. , 0. ], [ 0.47330339, 0.88089948, 0. ], [ 0.58149261, 0. , 0.81355169]]) |
每一行都歸一化得到單位歐幾里德范數:
例如,我們可以計算count數組中第一個文檔中第一項的tf-idf,如下所示:
#一共有6項,即有6個document
#包含第一項的文檔有6個
#計算其逆文檔頻率
#計算其詞頻*逆文檔頻率
現在,如果我們對文檔中剩下的兩項重復這個計算,我們得到
原始tf-idfs向量:
然后,應用歐幾里德(L2)范數,我們得到了文檔1的tf-idfs:
此外,默認參數smooth_idf=True在分子和分母上增加了"1",分母就像每個文檔都包含額外的詞一樣,這是為了防止0除的出現:
使用此修改,文檔1第三項tf-idf更改為1.8473:
l2歸一化tf-idf變為
>>> transformer = TfidfTransformer() >>> transformer.fit_transform(counts).toarray() array([[ 0.85151335, 0. , 0.52433293], [ 1. , 0. , 0. ], [ 1. , 0. , 0. ], [ 1. , 0. , 0. ], [ 0.55422893, 0.83236428, 0. ], [ 0.63035731, 0. , 0.77630514]]) |
通過fit方法調用計算的每個特征的權重都存儲在模型屬性中:
>>> transformer.idf_ array([ 1. ..., 2.25..., 1.84...])#這里指的就是單詞的逆文檔頻率 |
雖然tf-idf經常用於文本特性,還有另一個類TfidfVectorizer,它將CountVectorizer和TfidfTransformer的所有選項組合在一個模型中:
>>> from sklearn.feature_extraction.text import TfidfVectorizer >>> vectorizer = TfidfVectorizer() >>> vectorizer.fit_transform(corpus) ... <4x9 sparse matrix of type '<... 'numpy.float64'>' with 19 stored elements in Compressed Sparse ... format> |
雖然tf-idf規范化通常非常有用,但在某些情況下,二進制標記可能提供更好的特性。這可以通過使用CountVectorizer的二進制參數來實現。特別地,一些估計,如伯努利朴素貝葉斯顯式模型離散布爾隨機變量。此外,非常短的文本很可能有噪聲tf-idf值,而二進制信息更穩定。
通常,調整特征提取參數的最佳方法是使用交叉驗證的網格搜索,例如使用分類器流水線化特征提取器:
Sample pipeline for text feature extraction and evaluation
4.2.3.5. 文本文件解碼
文本由字符組成,而文件由字節組成。這些字節根據某種編碼表示字符。要使用Python中的文本文件,它們的字節必須被解碼為一個稱為Unicode的字符集。常見的編碼有ASCII、Latin-1(西歐)、KOI8-R(俄語)和通用編碼UTF-8和UTF-16。
注意:編碼也可以稱為"字符集",但這個術語不太准確:單個字符集可以存在多個編碼。
scikit中的文本特征提取器知道如何解碼文本文件,但前提是你告訴他們文件的編碼是什么。CountVectorizer為此接受一個編碼參數。對於許多文本文件,正確的編碼可能是UTF-8,因此它是默認的(編碼=" UTF-8 ")。
但是,如果正在加載的文本實際上不是用UTF-8編碼的,那么將得到一個UnicodeDecodeError。通過將decode_error參數設置為"ignore"或"replace",可以讓向量化工具對解碼錯誤不會報錯。有關Python函數bytes.decode的詳細信息請參閱文檔(在Python提示符處輸入help(bytes.decode))。
如果你在解碼文本時有困難,可以做如下嘗試:
-
找出文本的實際編碼是什么。文件可能帶有一個頭或README,告訴您編碼,或者可能有一些標准編碼,您可以根據文本的來源假設。
-
可以嘗試UTF-8並忽略錯誤。使用bytes.decode(errors='replace')對字節字符串進行解碼,替換所有解碼錯誤,或者在vectorizer中設置decode_error='replace'。這可能會破壞你的特征的有用性。
-
真正的文本可能來自各種不同的來源,這些來源可能使用不同的編碼,甚至可能被草率地解碼為不同的編碼,而不是它所使用的編碼。這在從Web檢索的文本中很常見。Python包ftfy可以自動整理某些類型的解碼錯誤,因此您可以嘗試將未知文本解碼為latin-1,然后使用ftfy來修復錯誤。
-
如果文本是混合編碼的,很難進行分類(這是20個新聞組數據集的情況),那么可以使用簡單的單字節編碼,比如latin-1。某些文本可能顯示不正確,但至少相同的字節序列始終表示相同的特性。
4.2.3.6. Applications and examples
詞袋表示非常簡單,但在實踐中非常有用。
特別是在有監督的設置中,它可以成功地與快速和可伸縮的線性模型相結合,以訓練文檔分類器;
在無監督的情況下,它可以通過應用K-means等聚類算法將類似的文檔分組在一起;
最后,可以通過放松聚類的硬性分配約束來發現語料庫的主要主題,例如使用非負矩陣分解(NMF或NNMF)。
4.2.3.7. Limitations of the Bag of Words representation(詞袋模型的限制)
一組unigram(就是一袋單詞)不能捕捉短語和多詞表達,忽略了詞序依賴。此外,單詞袋模型不考慮潛在的拼寫錯誤或單詞派生
N-gram可以克服這點!與其構建一個簡單的unigrams集合(n=1),不如選擇一個bigrams集合(n=2),在這里統計連續單詞成對出現的次數。
可以考慮使用字符n-g的集合,這是一種抵抗拼寫錯誤和派生的表示。
例如,假設我們正在處理一個由兩個文檔組成的語料庫:['words', 'wprds']。第二個文檔包含單詞"words"的拼寫錯誤。一個簡單的詞匯表示可以將這兩個文檔看作是非常不同的文檔,它們在兩個可能的特性上都有所不同。然而,一個2-gram
>>> ngram_vectorizer = CountVectorizer(analyzer='char_wb', ngram_range=(2, 2)) >>> counts = ngram_vectorizer.fit_transform(['words', 'wprds']) >>> ngram_vectorizer.get_feature_names() == ( ... [' w', 'ds', 'or', 'pr', 'rd', 's ', 'wo', 'wp']) True >>> counts.toarray().astype(int) array([[1, 1, 1, 0, 1, 1, 1, 0], [1, 1, 0, 1, 1, 1, 0, 1]]) |
表示法可以在8個特征中找到4個匹配的文檔,這可以幫助首選分類器更好地決定:
具有邊界感知功能的變體char_wb對於使用空白分隔詞的語言來說特別有趣,因為在這種情況下,它生成的嘈雜特性要比原始char變體少得多。對於這類語言,它可以提高使用這類特性訓練的分類器的預測精度和收斂速度,同時在拼寫和詞源方面保持魯棒性。
而一些局部定位信息可以通過提取n-g而不是單個單詞來保存,一袋一袋的單詞和一袋一袋的n-g破壞了文檔的大部分內部結構,從而破壞了內部結構所承載的大部分意義。
因此,為了解決更廣泛的自然語言理解任務,應該考慮句子和段落的局部結構。因此,許多這樣的模型將被定義為"結構化輸出"問題,而這些問題目前不在scikitlearn的范圍之內。
4.2.3.8. Vectorizing a large text corpus with the hashing trick
(使用哈希技巧向量化大型文本語料庫)
上面的矢量化方案很簡單,但是它在內存中保存了從字符串token到整數特征索引(詞匯表_屬性)的映射,這在處理大型數據集時造成了一些問題:
-
語料庫越大,詞匯量就越大,因此使用的內存也就越多;
-
擬合需要分配與原始數據集大小成比例的中間數據結構。
-
構建單詞映射需要對數據集進行完整的傳遞,因此不可能以嚴格的在線方式對文本分類器進行匹配。
-
具有較大詞匯表的pickling和未pickling矢量化程序可能非常慢(通常比pickling /未pickling平面數據結構(如大小相同的NumPy數組)慢得多)
-
不能簡單的將向量化工作分割成子任務
通過結合sklearn.feature_extraction實現的"哈希技巧"(特性哈希)、文本預處理及CountVectorizer的tokenization特征可以克服這些限制。
這個組合是在HashingVectorizer中實現的,一個經常結合CountVectorizer的轉換類。HashingVectorizer是無狀態的,這意味着不必調用fit:
>>> from sklearn.feature_extraction.text import HashingVectorizer >>> hv = HashingVectorizer(n_features=10) >>> hv.transform(corpus) ... <4x10 sparse matrix of type '<... 'numpy.float64'>' with 16 stored elements in Compressed Sparse ... format> |
可以看到向量輸出16個非零的tokens,比CountVectorizer的19個非零的tokens要少這種差異來自於哈希函數沖突,因為n_features參數的值很低。
在實際設置中,n_features參數可以保留為其默認值2 ** 20(大約100萬個可能的特性)。如果內存或下游模型大小是一個問題,那么選擇一個較低的值(比如2 ** 18)可能會有幫助,而不會在典型的文本分類任務中引入太多額外的沖突。
讓我們用默認設置再試一次:
>>> hv = HashingVectorizer() >>> hv.transform(corpus) ... <4x1048576 sparse matrix of type '<... 'numpy.float64'>' with 19 stored elements in Compressed Sparse ... format> |
我們不再得到沖突,但這是以輸出空間更大維度為代價的。當然,這里使用的其他術語可能仍然會相互沖突。
HashingVectorizer也有以下限制:
-
由於執行映射的散列函數是單向的,因此不可能反轉模型(沒有inverse_transform方法),也不可能訪問特性的原始字符串表示形式。
-
它不提供IDF權重,因為這會在模型中引入狀態性。如果需要,可以將TfidfTransformer追加到管道中。
4.2.3.9. Performing out-of-core scaling with HashingVectorizer
使用HashingVectorizer的一個有趣的開發是執行內核外擴展的能力。這意味着我們可以從不適合計算機主存的數據中學習。
實現核外擴展的一個策略是將數據以小批量的形式流到評估器。使用HashingVectorizer對每個迷你批處理進行矢量化,以保證估計器的輸入空間始終具有相同的維數。因此,在任何時候使用的內存數量都受一個迷你批處理大小的限制。雖然使用這種方法可以攝取的數據量沒有限制,但從實際的角度來看,學習時間通常受希望在任務上花費的CPU時間的限制。
有關文本分類任務中的核心外擴展的完整示例,請參閱文本文檔的核心外分類。
4.2.3.10. Customizing the vectorizer classes(定制向量類)
可以通過向vectorizer構造函數傳遞一個可調用的函數來定制行為:
>>> def my_tokenizer(s): ... return s.split() ... >>> vectorizer = CountVectorizer(tokenizer=my_tokenizer) >>> vectorizer.build_analyzer()(u"Some... punctuation!") == ( ... ['some...', 'punctuation!']) True |
特別地,我們做如下命名:
-
預處理器:可調用的,它將整個文檔作為輸入(作為單個字符串),並返回可能經過轉換的文檔版本,仍然是一個完整的字符串。這可以用來刪除HTML標簽,小寫整個文檔,等等。
-
記號賦予器:一個可調用的,它從預處理器獲取輸出並將其分割成標記,然后返回這些標記的列表。
-
分析器:一個可調用的,替換預處理器和標記器。默認的分析器都調用預處理器和標記器,但是自定義分析器將跳過這一步。N-gram提取和停止字過濾發生在分析器級別,因此自定義分析器可能必須重現這些步驟。
要使預處理器、標記器和分析器知道模型參數,可以從類派生並覆蓋build_預處理器build_tokenizer'和build_analyzer方法,而不是傳遞定制函數。
一些提示與技巧:
-
如果文檔是由外部包預先標記的,那么將它們存儲在文件(或字符串)中,標記用空格隔開, analyzer=str.split
-
一些的標記級別的分析,如詞干提取、詞根提取、復合分裂、基於詞性的過濾等,並不包括在scikit-learn代碼基中,但是可以通過定制標記器或分析器來添加。這時使用一個帶有NLTK分詞器和詞還原器的CountVectorizer實現:
-
>>> from nltk import word_tokenize
-
>>> from nltk.stem import WordNetLemmatizer
-
>>> class LemmaTokenizer(object):
-
... def __init__(self):
-
... self.wnl = WordNetLemmatizer()
-
... def __call__(self, doc):
-
... return [self.wnl.lemmatize(t) for t in word_tokenize(doc)]
-
...
-
>>> vect = CountVectorizer(tokenizer=LemmaTokenizer())
(注意,這里沒有濾掉標點符號。)
例如,下面的示例將把一些英式拼寫轉換為美式拼寫:
>>> import re >>> def to_british(tokens): ... for t in tokens: ... t = re.sub(r"(...)our$", r"\1or", t) ... t = re.sub(r"([bt])re$", r"\1er", t) ... t = re.sub(r"([iy])s(e$|ing|ation)", r"\1z\2", t) ... t = re.sub(r"ogue$", "og", t) ... yield t ... >>> class CustomVectorizer(CountVectorizer): ... def build_tokenizer(self): ... tokenize = super(CustomVectorizer, self).build_tokenizer() ... return lambda doc: list(to_british(tokenize(doc))) ... >>> print(CustomVectorizer().build_analyzer()(u"color colour")) [...'color', ...'color'] |
在處理不使用顯式分隔符(如空格)的亞洲語言時,定制向量器也很有用。
4.2.4. Image feature extraction(圖像特征提取)
4.2.4.1. Patch extraction(補丁提取)
extract_patches_2d函數從存儲為二維數組的圖像中提取補丁,或者沿着第三軸提取具有顏色信息的三維圖像。要從所有補丁中重新構建圖像,請使用reconstruct_from_patches_2d。樣例生成帶有3個顏色通道(例如RGB格式)的4x4像素圖像。
>>> import numpy as np >>> from sklearn.feature_extraction import image
>>> one_image = np.arange(4 * 4 * 3).reshape((4, 4, 3)) >>> one_image[:, :, 0] # R channel of a fake RGB picture array([[ 0, 3, 6, 9], [12, 15, 18, 21], [24, 27, 30, 33], [36, 39, 42, 45]])
>>> patches = image.extract_patches_2d(one_image, (2, 2), max_patches=2, ... random_state=0) >>> patches.shape (2, 2, 2, 3) >>> patches[:, :, :, 0] array([[[ 0, 3], [12, 15]],
[[15, 18], [27, 30]]]) >>> patches = image.extract_patches_2d(one_image, (2, 2)) >>> patches.shape (9, 2, 2, 3) >>> patches[4, :, :, 0] array([[15, 18], [27, 30]]) |
現在讓我們通過對重疊區域的平均來嘗試從這些斑塊中重建原始圖像:
>>> reconstructed = image.reconstruct_from_patches_2d(patches, (4, 4, 3)) >>> np.testing.assert_array_equal(one_image, reconstructed) |
PatchExtractor類的工作方式與extract_patches_2d相同,只是它支持多個圖像作為輸入。它作為一個估計器實現,因此可以在管道中使用。
>>> five_images = np.arange(5 * 4 * 4 * 3).reshape(5, 4, 4, 3) >>> patches = image.PatchExtractor((2, 2)).transform(five_images) >>> patches.shape (45, 2, 2, 3) |
4.2.4.2. Connectivity graph of an image
scikit-learn中的幾個評估器可以使用特性或示例之間的連接信息。例如,Ward聚類(分層聚類)只能將圖像的相鄰像素聚在一起,從而形成相鄰的斑塊:
為此,估計器使用一個"連通"矩陣,給出的樣例就是連通的。
函數img_to_graph從2D或3D圖像返回這樣一個矩陣。類似地,grid_to_graph為給定這些圖像形狀的圖像構建一個連接矩陣。
這些矩陣可用於在使用連通性信息(如Ward聚類(分層聚類))的估計器中強制連接,也可用於構建預先計算的內核或相似矩陣。