Python 機器學習及實踐 Codeing 模型實用技巧 (特徵提高 模型正則化 模型檢測 超參數搜索)

以前的數據都是通過了規範化處理,並且模型也大多數採用了默認的初始化配置ios

可是在世紀研究和工做種接觸到的數據都是這樣規整的嗎?難道默認配置就最佳的嗎?數組

3.1模型實用技巧數據結構

一旦咱們肯定使用某個模型 本書所提供的程序庫就能夠幫助咱們從標準的訓練數據種,依靠默認的配置學習到模型所須要的參數;app

接下來,咱們即可以利用這組得來的參數指導模型在測試數據上進行預測,進而對模型的表現進行評價dom

可是這套方案不能保證:機器學習

①全部用於訓練的數據特徵都是最好的函數

②學習獲得的參數必定是最優的性能

③默認配置下的模型老是最佳的學習

Together 咱們能夠從多個角度對在前面使用過的模型進行性能提高 (預處理數據 控制參數 優化模型配置)測試

特徵提高(特徵抽取和特徵篩選)

特徵抽取

所謂特徵抽取 就是逐條將原始數據轉化維特徵向量的形式 這個過程同時涉及對數據特徵的量化表示

原始數據 :

      1數字化的信號數據(聲紋,圖像)

      2還有大量符號化的文本 

①咱們沒法直接將符號化的文字自己用於計算任務 而是須要經過某些處理手段 ,預先將文本量化爲特徵向量

有些用符號表示的數據特徵已經相對結構化,而且以字典這種數據結構進行存儲。

這時咱們使用DictVectorizer 對特徵進行抽取和向量化

measurements=[{'city':'Dubai','temperature':33.},{'city':'London','temperature':12.},
              {'city':'San Fransisco','temperature':18.}]

from sklearn.feature_extraction import DictVectorizer
vec=DictVectorizer()   #初始化DictVectorizer()特徵抽取器
print(vec.fit_transform(measurements).toarray())   #輸出轉化後的特徵矩陣
print(vec.get_feature_names())   #輸出各個維度特徵含義

#DictVectorizer()特徵抽取器對類別型特徵進行了one-hot處理,對數值型特徵沒作進一步處理

 

DiceVectorizer 對特徵的處理方式(字典):

1類別行 使用0/1二值方式

2數字型 維持原始數值便可

②另一些文本數據更爲原始 知識一系列的字符串 咱們採用詞袋法對特徵進行抽取和向量化

詞袋法的兩種計算方式

CountVectorizer

TfidVectorizer

(a.)CountVectorizer——每一個詞(Term)在該訓練文本中出現的頻率(Term Frequency);

(b.)TfidfVectorizer——除了考量某一詞彙在當前文本中出現的頻率(Term Frequency)外,還要關注包含這個詞彙的文本條數的倒數(Inverse Document Frequency)。訓練文本的條目越多,TfidfVectorizer這種特徵量化的方式就更有優點,由於計算詞頻(Term Frequency)的目的在於找出對所在文本的含義更有貢獻的重要詞彙

若是一個詞幾乎在每篇文章中都出現,說明這個詞是經常使用詞,不會幫助模型對文本進行分類。此外,在每條文本中都出現的經常使用詞彙成爲停用詞(Stop Words),停用詞在文本特徵抽取時須要過濾掉。

#使用CountVectorizer  而且在不去掉停用詞的條件下,對文本進行量化的樸素貝葉斯分類性能預測
from sklearn.datasets import fetch_20newsgroups#20類新聞文本
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import classification_report
news=fetch_20newsgroups(subset='all')

X_train,X_test,y_train,y_test=train_test_split(news.data,news.target,test_size=0.25,random_state=33)

count_vec=CountVectorizer()   #默認形式初始化詞頻統計器(不去停用詞)
X_count_train=count_vec.fit_transform(X_train)
X_count_test=count_vec.transform(X_test)

mnb_count=MultinomialNB()
mnb_count.fit(X_count_train,y_train)   #用樸素貝葉斯分類器對詞頻統計器(不去停用詞)處理後的樣本進行參數學習
print('The accuracy of classifying 20newsgroups using Naive Bayes(CountVectorizer without filtering stopwords):',
     mnb_count.score(X_count_test,y_test))

y_count_predict=mnb_count.predict(X_count_test)

print(classification_report(y_test,y_count_predict,target_names=news.target_names))
#使用TfidVectorizer  而且在不去掉停用詞的條件下,對文本進行量化的樸素貝葉斯分類性能預測
from sklearn.datasets import fetch_20newsgroups
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import classification_report
news=fetch_20newsgroups(subset='all')

X_train,X_test,y_train,y_test=train_test_split(news.data,news.target,test_size=0.25,random_state=33)

tfidf_vec=TfidfVectorizer()   #默認形式初始化tfidf統計器(不去停用詞)
X_tfidf_train=tfidf_vec.fit_transform(X_train)
X_tfidf_test=tfidf_vec.transform(X_test)

mnb_tfidf=MultinomialNB()
mnb_tfidf.fit(X_tfidf_train,y_train)   #用樸素貝葉斯分類器對tfidf統計器(不去停用詞)處理後的樣本進行參數學習
print('The accuracy of classifying 20news with Naive Bayes(TfidfVectorizer without filtering stopwords):',
      mnb_tfidf.score(X_tfidf_test,y_test))

y_tfidf_predict=mnb_tfidf.predict(X_tfidf_test)
print(classification_report(y_test,y_tfidf_predict,target_names=news.target_names))
#在訓練文本量較多的時候,利用TfidfVectorizer壓制一些經常使用詞彙對分類決策的干擾
#分別使用CountVectorizer與TfidVectorizer  而且在去掉停用詞的條件下,對文本進行量化的樸素貝葉斯分類性能預測
from sklearn.datasets import fetch_20newsgroups
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB

news=fetch_20newsgroups(subset='all')

X_train,X_test,y_train,y_test=train_test_split(news.data,news.target,test_size=0.25,random_state=33)

count_filter_vec=CountVectorizer(analyzer='word',stop_words='english')   #初始化詞頻統計器(去停用詞)
X_count_filter_train=count_filter_vec.fit_transform(X_train)
X_count_filter_test=count_filter_vec.transform(X_test)

mnb_count_filter=MultinomialNB()
mnb_count_filter.fit(X_count_filter_train,y_train)
print('The accuarcy of classifying 20newsgroups using Naive Bayes(CountVetorizer by filtering stopwords):',
      mnb_count_filter.score(X_count_filter_test,y_test))

y_count_filter_predict=mnb_count_filter.predict(X_count_filter_test)
from sklearn.metrics import classification_report
print(classification_report(y_test,y_count_filter_predict,target_names=news.target_names))

tfidf_filter_vec=TfidfVectorizer(analyzer='word',stop_words='english')   #初始化tfidf統計器(去停用詞)
X_tfidf_filter_train=tfidf_filter_vec.fit_transform(X_train)
X_tfidf_filter_test=tfidf_filter_vec.transform(X_test)

mnb_tfidf_filter=MultinomialNB()
mnb_tfidf_filter.fit(X_tfidf_filter_train,y_train)
print('The accuracy of classifying 20newsgroups with Naive Bayes(TfidfVectorizer by filtering stopwords):',
      mnb_tfidf_filter.score(X_tfidf_filter_test,y_test))

y_tfidf_filter_predict=mnb_tfidf_filter.predict(X_tfidf_filter_test)
print(classification_report(y_test,y_tfidf_filter_predict,target_names=news.target_names))

 

特徵篩選

良好的數據特徵組合不須要太多,即可以使模型的性能表現突出。

冗餘的特徵不會影響到模型的性能,不過卻使得CPU的計算作了無用功

主成分分析主要用於去除多餘的那些線性相關的特長組合,緣由在於這些冗餘的特徵組合並不會對模型訓練有更多貢獻。而不良的特徵天然會下降模型的精度。

特徵篩選與PCA 這類經過選擇主成分進行對特徵進行重建的方法略有區別:對於PCA而言,咱們常常沒法解釋重建以後的特徵;

可是特徵篩選不存在對特徵值的修改 而更加側重於尋找那些對模型的性能提高較大的少許特徵。

#使用Titanic數據集,經過特徵篩選的方法一步步提高決策樹的預測性能
import pandas as pd
titanic=pd.read_csv('http://biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/titanic.txt')

y=titanic['survived']   #分離特徵與標籤
X=titanic.drop(['row.names','name','survived'],axis=1)

X['age'].fillna(X['age'].mean(),inplace=True)   #填充缺失值
X.fillna('UNKNOWN',inplace=True)

from sklearn.cross_validation import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X, y, test_size=0.25, random_state=33)

from sklearn.feature_extraction import DictVectorizer
vec=DictVectorizer()
X_train=vec.fit_transform(X_train.to_dict(orient='record'))   #類別型特徵向量化one-hot
X_test=vec.transform(X_test.to_dict(orient='record'))
print(len(vec.feature_names_))

#使用決策樹模型對全部特徵進行預測並評估
from sklearn.tree import DecisionTreeClassifier
dt=DecisionTreeClassifier(criterion='entropy')
dt.fit(X_train,y_train)
dt.score(X_test,y_test)

from sklearn import feature_selection
fs=feature_selection.SelectPercentile(feature_selection.chi2,percentile=20)   #篩選前20%特徵建模
X_train_fs=fs.fit_transform(X_train,y_train)
dt.fit(X_train_fs,y_train)
X_test_fs=fs.transform(X_test)
dt.score(X_test_fs,y_test)

from sklearn.cross_validation import cross_val_score
import numpy as np
percentiles=range(1,100,2)
results=[]

for i in percentiles:
    fs=feature_selection.SelectPercentile(feature_selection.chi2,percentile=i)
    X_train_fs=fs.fit_transform(X_train,y_train)
    scores=cross_val_score(dt,X_train_fs,y_train,cv=5)   #經過交叉驗證,按固定間隔2%的步長篩選特徵
    results=np.append(results,scores.mean())
print(results)

opt=np.where(results==results.max())[0]
print('Optimal number of features %d' % percentiles[opt[0]])

import pylab as pl
pl.plot(percentiles,results)
pl.xlabel('percentiles of features')
pl.ylabel('accuracy')
pl.show()

from sklearn import feature_selection
fs=feature_selection.SelectPercentile(feature_selection.chi2,percentile=7)   #使用最佳篩選後的特徵進行相同建模
X_train_fs=fs.fit_transform(X_train,y_train)
dt.fit(X_train_fs,y_train)
X_test_fs=fs.transform(X_test)
dt.score(X_test_fs,y_test)

 

模型正則化

欠擬合與過擬合

模型的泛化能力:模型對未知數據的預測能力

擬合:機器學習模型在訓練的過程當中,經過更新參數,使模型不斷契合可觀測數據(訓練集)的過程

存在過擬合和欠擬合兩種模型的狀態

 

#使用線性迴歸模型在披薩訓練樣本上進行擬合
X_train=[[6],[8],[10],[14],[18]]
y_train=[[7],[9],[13],[17.5],[18]]

from sklearn.linear_model import LinearRegression
regressor=LinearRegression()   #默認配置初始化線性迴歸模型
regressor.fit(X_train,y_train)   #根據訓練集數據建模

import numpy as np
xx=np.linspace(0,26,100)   #構建測試集 
xx=xx.reshape(xx.shape[0],1)
yy=regressor.predict(xx)

import matplotlib.pyplot as plt
plt.scatter(X_train,y_train)
plt1,=plt.plot(xx,yy,label='Degree=1')
plt.axis([0,25,0,25])
plt.xlabel('independent variable')
plt.ylabel('dependent variable')
plt.legend(handles=[plt1])
plt.show()

print('The R-squared value of Linear Regressor performing on the training data is',
      regressor.score(X_train,y_train))

#使用二次多項式迴歸模型在披薩訓練樣本上進行擬合
X_train=[[6],[8],[10],[14],[18]]
y_train=[[7],[9],[13],[17.5],[18]]

from sklearn.preprocessing import PolynomialFeatures
poly2=PolynomialFeatures(degree=2)   #多項式特徵產生器
X_train_poly2=poly2.fit_transform(X_train)   #X_train_poly2即爲由訓練集構造出的二次多項式特徵

from sklearn.linear_model import LinearRegression
regressor_poly2=LinearRegression()
regressor_poly2.fit(X_train_poly2,y_train)   #建模生產的二次多項式迴歸模型

import numpy as np
xx=np.linspace(0,26,100)
xx=xx.reshape(xx.shape[0],1)
xx_poly2=poly2.transform(xx)   #對測試集數據構造二次多項式特徵
yy_poly2=regressor_poly2.predict(xx_poly2)

import matplotlib.pyplot as plt
plt.scatter(X_train,y_train)
plt2,=plt.plot(xx,yy_poly2,label='Degree=2')
plt.axis([0,25,0,25])
plt.xlabel('independent variable')
plt.ylabel('dependent variable')
plt.legend(handles=[plt2])
plt.show()

print('The R-squared value of Polynomial Regressor(Degree=2) performing on the training data is',
      regressor_poly2.score(X_train_poly2,y_train))

#使用4次多項式迴歸模型在披薩訓練樣本上進行擬合
X_train=[[6],[8],[10],[14],[18]]
y_train=[[7],[9],[13],[17.5],[18]]

from sklearn.preprocessing import PolynomialFeatures
poly4=PolynomialFeatures(degree=4)   #多項式特徵產生器
X_train_poly4=poly4.fit_transform(X_train)   #X_train_poly2即爲由訓練集構造出的二次多項式特徵

from sklearn.linear_model import LinearRegression
regressor_poly4=LinearRegression()
regressor_poly4.fit(X_train_poly4,y_train)   #建模生產的二次多項式迴歸模型

import numpy as np
xx=np.linspace(0,26,100)
xx=xx.reshape(xx.shape[0],1)
xx_poly2=poly4.transform(xx)   #對測試集數據構造二次多項式特徵
yy_poly2=regressor_poly4.predict(xx_poly2)

import matplotlib.pyplot as plt
plt.scatter(X_train,y_train)
plt2,=plt.plot(xx,yy_poly2,label='Degree=2')
plt.axis([0,25,0,25])
plt.xlabel('independent variable')
plt.ylabel('dependent variable')
plt.legend(handles=[plt2])
plt.show()

print('The R-squared value of Polynomial Regressor(Degree=2) performing on the training data is',
      regressor_poly4.score(X_train_poly4,y_train))

#評估3種迴歸模型在測試數據集上的性能表現
X_test=[[6],[8],[11],[16]]
y_test=[[8],[12],[15],[18]]
print('Linear regression:',regressor.score(X_test,y_test))

X_test_poly2=poly2.transform(X_test)
print('Polynomial 2 regression:',regressor_poly2.score(X_test_poly2,y_test))

X_test_poly4=poly4.transform(X_test)
print('Polynomial 4 regression:',regressor_poly4.score(X_test_poly4,y_test))

Together

當模型複雜度很低 模型不只沒有對訓練集上的數據有良好的擬合狀態,並且在測試集上也表現平平,這種狀況叫欠擬合,如線性迴歸預測

可是,當咱們意味追求很高的模型複雜度,儘管模型幾乎徹底擬合了所有訓練數據 但模型也變得很是波動 幾乎喪失了對未知數據的預測能力 ,這種狀況叫過擬合,如四次多項式迴歸預測。

避免欠擬合 在要求增長模型複雜度 提升在可觀測數據上的表現的同時 又須要兼顧模型的泛化力 防止發生過擬合。

 

正則化方法(L1正則化和L2正則化)


正則化的目的在於提升模型在未知測試數據上的泛化力,避免參數過擬合

所以,正則化的常見方法都是在原模型優化目標的基礎上,增長對參數的懲罰項 

L1正則化是讓參數向量中的許多元素趨向於0,讓有效特徵變得稀疏,對應的L1正則化模型稱爲Lasso。 
L2正則化是讓參數向量中的大部分元素都變得很小,壓制了參數之間的差別性,對應的L2正則化模型稱爲Ridge

#Lasso模型在4次多項式特徵上擬合的擬合
from sklearn.linear_model import Lasso
lasso_poly4=Lasso()   #默認配置初始化Lasso
lasso_poly4.fit(X_train_poly4,y_train)   #利用Lasso對四次多項式特徵迴歸擬合
print(lasso_poly4.score(X_test_poly4,y_test))   #在測試集上進行評估
print(lasso_poly4.coef_)   #輸出Lasso模型的參數列表
print(' ')
print(regressor_poly4.score(X_test_poly4,y_test))   #對比看一下不加正則化項的四次多項式迴歸擬合
print(regressor_poly4.coef_)

#Ridge模型在4次多項式特徵上的擬合表現
from sklearn.linear_model import Ridge
ridge_poly4=Ridge()   #默認配置初始化Ridge
ridge_poly4.fit(X_train_poly4,y_train)   #利用Ridge對四次多項式特徵迴歸擬合
print(ridge_poly4.score(X_test_poly4,y_test))   #在測試集上進行評估
print(ridge_poly4.coef_)   #輸出Ridge模型的參數列表
print(np.sum(ridge_poly4.coef_**2))
print(' ')
print(regressor_poly4.coef_)   #對比看一下不加正則化項的四次多項式迴歸擬合
print(np.sum(regressor_poly4.coef_**2))

 模型檢驗

在實際生活種(比賽)咱們不知道 模型對測試集預測的答案 (當咱們書中沒有測試集數據的狀況下)

咱們把現有的數據進行採樣分割 成 訓練集和開發集(驗證集)

模型校驗方式 留一驗證 交叉驗證

留一驗證(用於早期)

隨機採樣必定比例做爲訓練集 ,留下的做爲驗證集 一般比例爲7 3  可是因爲對於驗證集合隨機採樣的不肯定性 使模型的性能不穩定

交叉驗證(留一驗證的高級版本)

屢次進行留一驗證 後求得平均結果

 超參數搜索

多數狀況先,超參數的選擇是無限的。所以在有限的時間內,除了能夠驗證預設幾種超參數組合之外,也能夠經過啓發式的搜索方式對超參數組合進行調優(網格搜索),同時因爲超參數的驗證過程之間彼此獨立,所以爲並行計算提供了可能(並行搜索)。

 

網格搜索

對多種超參數組合的空間進行暴力搜索。每一套超參數組合被帶入到學習函數中做爲新的模型,而且爲了比較新模型之間的性能,每一個模型都會採用交叉驗證的方法在多組相同的訓練集和開發數據集下進行評估

 

#使用單線程對文本分類的樸素貝葉斯模型的超參數組合執行網格搜索
from sklearn.datasets import fetch_20newsgroups
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV


news=fetch_20newsgroups(subset='all')

#選取前3000條數據
X_train,X_test,y_train,y_test=train_test_split(news.data[:3000],news.target[:3000],test_size=0.25,random_state=33)

#使用Pipline 簡化系統搭建流程 將文本抽取與分類器模型串聯起來
clf=Pipeline([('vect',TfidfVectorizer(stop_words='english',analyzer='word')),('svc',SVC())])

#超參數svc_gamma有4個 svc_C有3個 超參數組合一共有12種  使用np.logspace函數來選取超參數
parameters = {'svc__gamma': np.logspace(-2, 1, 4), 'svc__C': np.logspace(-1, 1, 3)}
gs=GridSearchCV(clf,parameters,verbose=2,refit=True,cv=3)

gs.fit(X_train,y_train)
gs.best_params_,gs.best_score_

print(gs.score(X_test,y_test))

 關於網格搜索的參數:

GridSearchCV(estimator,param_grid, scoring=None, fit_params=None, n_jobs=1, iid=True, refit=True,cv=None, verbose=0,

         pre_dispatch='2*n_jobs', error_score='raise',return_train_score=True)

estimator:所使用的分類器。

param_grid:值爲字典或者列表,即須要最優化的參數的取值。

scoring :準確度評價標準,默認None。

cv :交叉驗證參數,默認None,使用三折交叉驗證(cv=3)。

refit :默認爲True,程序將會以交叉驗證訓練集獲得的最佳參數,從新對全部可用的訓練集與開發集進行,做爲最終用於性能評估的最佳模型參數。即在搜索參數結束後,用最佳參數結果再次fit一遍所有數據集

verbose:日誌冗長度,int:冗長度,0:不輸出訓練過程,1:偶爾輸出,>1:對每一個子模型都輸出。

 n_jobs: 並行數,int:個數,-1:跟CPU核數一致, 1:默認值。

 pre_dispatch:指定總共分發的並行任務數。當n_jobs大於1時,數據將在每一個運行點進行復制,這可能致使OOM,

而設置pre_dispatch參數,則能夠預先劃分總共的job數量,使數據最多被複制pre_dispatch次

並行搜索

n_jobs=-1是用cpu全部的核訓練 即並行的方式

#使用多個線程對文本分類的樸素貝葉斯模型的超參數組合執行並行化的網格搜索
from sklearn.datasets import fetch_20newsgroups
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV


news=fetch_20newsgroups(subset='all')

#選取前3000條數據
X_train,X_test,y_train,y_test=train_test_split(news.data[:3000],news.target[:3000],test_size=0.25,random_state=33)

#使用Pipline 簡化系統搭建流程 將文本抽取與分類器模型串聯起來
clf=Pipeline([('vect',TfidfVectorizer(stop_words='english',analyzer='word')),('svc',SVC())])

#超參數svc_gamma有4個 svc_C有3個 超參數組合一共有12種  使用np.logspace函數來選取超參數
parameters = {'svc__gamma': np.logspace(-2, 1, 4), 'svc__C': np.logspace(-1, 1, 3)}
#初始化配置並行網格搜索 n_jobs=-1表明使用該計算機的所有CPU
gs=GridSearchCV(clf,parameters,verbose=2,refit=True,cv=3,n_jobs=-1)

gs.fit(X_train,y_train)
gs.best_params_,gs.best_score_

print(gs.score(X_test,y_test))
相關文章
相關標籤/搜索