scikit-learn 4.2 Feature extraction特徵提取

4.2 特徵提取

sklearn.feature_extraction 模塊能夠被用來從包含文本或者特片的數據集中提取出適用於機器學習算法的特徵。 html

注意:特徵提取和特徵選擇是極不相同的:前者由任意數據組成,好比文本或者圖片,轉換爲適用於機器學習的數字。後者是應用於這些特徵的機器學習方法。python

4.2.1 從字典中加載特徵

類DictVectorizer能夠將由python標準的列表dict對象所表示的特徵轉換爲由scikit-learn中經常使用的NumPy/SciPy所表示的對象。 git

雖然dict處理起來不是特別快,可是其優點在於方便的使用,建立係數矩陣(缺失值不會被存儲)以及將特徵名字存儲與值上。 算法

DictVectorizer能夠建立咱們 稱之爲one-of-K或者"one-hot"編碼用於分類特徵(又名aka nominal,discrete)特徵。分類特徵是"屬性-值"對,值限制爲一系列的無序離散變量(好比,主題標識、對象類型、標記、名字。。。) 數組

在下面的例子中,"城市"是一個分類屬性,"溫度"是另外一個數值特徵: 數據結構

>>> measurements = [ app

... {'city': 'Dubai', 'temperature': 33.}, dom

... {'city': 'London', 'temperature': 12.}, 機器學習

... {'city': 'San Francisco', 'temperature': 18.}, ide

... ]

>>>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

>>> vec.get_feature_names()  #輸出特徵的名字

['city=Dubai', 'city=London', 'city=San Francisco', 'temperature']

DictVectorizer對於天然語言處理模型來講也是一個有用的變換,能夠提提取特定的單詞的特徵。

舉例來講,假設咱們有一個算法能夠提取出部分演講的標籤(PoS),咱們想要使用這些標籤做爲補充的標籤來訓練一個序列分類器(好比,一個chunker)。下面的字典能夠做爲這樣的特徵窗口從句子"The cat sat on the mat"中提取出單詞"sat"。

>>> pos_window = [

...     {

...         'word-2': 'the',

...         'pos-2': 'DT',

...         'word-1': 'cat',

...         'pos-1': 'NN',

...         'word+1': 'on',

...         'pos+1': 'PP',

...     },

...     # in a real application one would extract many such dictionaries

... ]

該描述能夠被向量化一個適用於分類器(可能會被用於text.TfidfTransformer用於正則化)的稀疏的二維矩陣。

>>> vec = DictVectorizer()    #建立一個變換聲明

>>> pos_vectorized = vec.fit_transform(pos_window)  #將pos_window變換爲稀疏矩陣表示的形式。 

>>> pos_vectorized

<1x6 sparse matrix of type '<... 'numpy.float64'>'

with 6 stored elements in Compressed Sparse ... format>

>>> pos_vectorized.toarray()   #輸出矩陣形式 

array([[ 1.,  1.,  1.,  1.,  1.,  1.]])

>>> vec.get_feature_names()   #獲得特徵名字 

['pos+1=PP', 'pos-1=NN', 'pos-2=DT', 'word+1=on', 'word-1=cat', 'word-2=the']

你能夠想象,若是一我的這樣提取在一篇文檔語料庫中的個體單詞的上下語義,最終結果中的矩陣必定是很是大的(許多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.MultinomialNBsklearn.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.CountVectorizerFeatureHasher不會切分單詞或者其餘任何的預處理,處理將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聚類(分層聚類))的估計器中強制鏈接,也可用於構建預先計算的內核或類似矩陣。

相關文章
相關標籤/搜索