1. 程式人生 > >sklearn學習——特徵工程(特徵選擇)

sklearn學習——特徵工程(特徵選擇)

特徵選擇方法總結

什麼是特徵工程?

定義:特徵工程是將原始資料轉化為特徵,更好表示預測模型處理的實際問題,提升對於未知資料的準確性。它是用目標問題所在的特定領域知識或者自動化的方法來生成、提取、刪減或者組合變化得到特徵。

為什麼要特徵工程?

簡單的說,你給我的資料能不能直接放到模型裡?顯然不能,第一,你的資料可能是假(異常值); 第二,你的資料太髒了(各種噪聲);第三,你的資料可能不夠,或者資料量不平衡(資料取樣);第三,清洗完資料能直接用嗎?顯然不能!輸入模型的資料要和模型輸出有關係,沒關係的那是噪聲!(特徵提取或處理);第四,特徵之間屬於親戚關係,你們是一家,不能反映問題!(特徵選擇)。

特徵工程有何意義?

這裡用七月線上寒老師的PPT做解釋:
這裡寫圖片描述
這裡寫圖片描述

如何特徵進行工程?

這裡寫圖片描述

本文重點敘述特徵選擇(Feature selection)

方法一:過濾型(Fillter)

評估單個特徵和結果值之間的相關程度,排序留下Top相關的特徵部分。

Person相關係數法:

import numpy as np
from scipy.stats import pearsonr#引入數值計算包的統計函式(scipy.stats)
np.random.seed(0)#保證每次隨機取值相同
size = 300
x = np.random
.normal(0, 1, size)#正態分佈 print ("Lower noise", pearsonr(x, x + np.random.normal(0, 1, size))) print ("Higher noise", pearsonr(x, x + np.random.normal(0, 10, size)))

Person相關係數的缺點: 作為特徵排序機制,他只對線性關係敏感。如果關係是非線性的,即便兩個變數具有一一對應的關係,Pearson相關性也可能會接近0。

x = np.random.uniform(-1, 1, 100000)
print pearsonr(x, x**2)[0
]

互資訊和最大資訊係數 Mutual information and maximal information coefficient (MIC)

這裡寫圖片描述

import numpy as np
from minepy import MINE#minepy包——基於最大資訊的非引數估計
m = MINE()
x = np.random.uniform(-1, 1, 10000)#均勻分佈
m.compute_score(x, x**2)
print (m.mic())

距離相關度

距離相關係數是為了克服Pearson相關係數的弱點而生的。在x和x^2這個例子中,即便Pearson相關係數是0,我們也不能斷定這兩個變數是獨立的(有可能是非線性相關);但如果距離相關係數是0,那麼我們就可以說這兩個變數是獨立的。

這裡寫程式碼片

過濾型(Fillter)缺點:沒有考慮到特徵之間的關聯作用,可能把有用的關聯特徵誤踢掉。
sklearn.feature_selection
這裡寫圖片描述

方法2:包裹型(Wrapper)

遞迴特徵刪除演算法

把特徵選擇看做一個特徵子集搜尋問題,篩選各種特徵子集,用模型評估效果。 典型的包裹型演算法為 “遞迴特徵刪除演算法”(recursive feature elimination algorithm)
比如用邏輯迴歸,怎麼做這個事情呢?
①用全量特徵跑一個模型
② 根據線性模型的係數(體現相關性),刪掉5-10%的弱特徵,觀
察準確率/auc的變化
③ 逐步進行,直至準確率/auc出現大的下滑停止
sklearn中用到的是sklearn.feature_selection.RFE
這裡寫圖片描述
這裡寫圖片描述
返回的是特徵貢獻的排序情況

基於學習模型的特徵排序 (Model based ranking)

這種方法的思路是直接使用你要用的機器學習演算法,針對每個單獨的特徵和響應變數建立預測模型。其實Pearson相關係數等價於線性迴歸裡的標準化迴歸係數。假如某個特徵和響應變數之間的關係是非線性的,可以用基於樹的方法(決策樹、隨機森林)、或者擴充套件的線性模型等。基於樹的方法比較易於使用,因為他們對非線性關係的建模比較好,並且不需要太多的除錯。但要注意過擬合問題,因此樹的深度最好不要太大,再就是運用交叉驗證。

from sklearn.cross_validation import cross_val_score, ShuffleSplit
from sklearn.datasets import load_boston#波士頓房屋價格預測
from sklearn.ensemble import RandomForestRegressor
#整合學習ensemble庫中的隨機森林迴歸RandomForestRegressor

#Load boston housing dataset as an example
boston = load_boston()
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]

rf = RandomForestRegressor(n_estimators=20, max_depth=4)
#20個弱分類器,深度為4
scores = []
for i in range(X.shape[1]):#分別讓每個特徵與響應變數做模型分析並得到誤差率
     score = cross_val_score(rf, X[:, i:i+1], Y, scoring="r2",
                              cv=ShuffleSplit(len(X), 3, .3))
     scores.append((round(np.mean(score), 3), names[i]))
print (sorted(scores, reverse=True))#對每個特徵的分數排序

方法3:嵌入型(Embeded)

根據模型來分析特徵的重要性(有別於上面的方式,是從生產的模型權重等)。最常見的方式為用正則化方式來做特徵選擇。
舉個例子,最早在電商用LR做CTR預估,在3-5億維的係數特徵上用L1正則化的LR模型。剩餘2-3千萬的feature,意味著其他的feature重要度不夠。

正則化模型

正則化就是把額外的約束或者懲罰項加到已有模型(損失函式)上,以防止過擬合併提高泛化能力。損失函式由原來的E(X,Y)變為E(X,Y)+alpha||w||,w是模型係數組成的向量(有些地方也叫引數parameter,coefficients),||·||一般是L1或者L2範數,alpha是一個可調的引數,控制著正則化的強度。當用線上性模型上時,L1正則化和L2正則化也稱為Lasso和Ridge。
Lasso
L1正則化 是指向量中各個元素絕對值之和,將係數w的l1範數作為懲罰項加到損失函式上,由於正則項非零,這就迫使那些弱的特徵所對應的係數變成0。因此L1正則化往往會使學到的模型很稀疏(係數w經常為0),這個特性使得L1正則化成為一種很好的特徵選擇方法。L1正則化像非正則化線性模型一樣也是不穩定的,如果特徵集合中具有相關聯的特徵,當資料發生細微變化時也有可能導致很大的模型差異。
Ridge
L2範數是指向量各元素的平方和然後求平方根,將係數向量的L2範數新增到了損失函式中。由於L2懲罰項中係數是二次方的,這使得L2和L1有著諸多差異,最明顯的一點就是,L2正則化會讓係數的取值變得平均。對於關聯特徵,這意味著他們能夠獲得更相近的對應係數。還是以Y=X1+X2為例,假設X1和X2具有很強的關聯,如果用L1正則化,不論學到的模型是Y=X1+X2還是Y=2X1,懲罰都是一樣的,都是2alpha。但是對於L2來說,第一個模型的懲罰項是2alpha,但第二個模型的是4*alpha。可以看出,係數之和為常數時,各系數相等時懲罰是最小的,所以才有了L2會讓各個係數趨於相同的特點。
L2正則化對於特徵選擇來說一種穩定的模型,不像L1正則化那樣,係數會因為細微的資料變化而波動。所以L2正則化和L1正則化提供的價值是不同的,L2正則化對於特徵理解來說更加有用:表示能力強的特徵對應的係數是非零。
這裡寫圖片描述

from sklearn.feature_selection import RFE                #包裹型特徵選擇
from sklearn.preprocessing import StandardScaler    #資料標準化
from sklearn.cross_validation import train_test_split  #交叉驗證
import matplotlib.pyplot as plt 
from sklearn.linear_model import LinearRegression  #線性迴歸
from sklearn.datasets import load_boston     
import numpy as np
from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import Ridge                   #L2正則化
from sklearn.linear_model import Lasso                   #L1正則化

#資料匯入
boston=load_boston()                          
scaler = StandardScaler()                                        #資料標準化
X=scaler.fit_transform(boston.data)                        #特徵變數的資料
y=boston.target                                                      #結果-->房價
names=boston.feature_names                                  #特徵名

#演算法擬合
lr=LinearRegression()              #線性迴歸演算法
rfe=RFE(lr,n_features_to_select=1) 
rfe.fit(X,y)                              #擬合數據

print("原有特徵名:")
print("\t",list(names))
print("排序後的特徵名:")
print("\t",sorted(zip(map(lambda x: round(x,4),rfe.ranking_),names))) #對特徵進行排序

#提取排序後的屬性在原屬性列的序列號
rank_fea=sorted(zip(map(lambda x: round(x,4),rfe.ranking_),names))   #排序好的特徵
rank_fea_list=[]                                              #用來裝排序的特徵的屬性名
for i in rank_fea:
    rank_fea_list.append(i[1])  
index_list=[0]*13                                         #記錄特徵屬性名對應原屬性names的序列號
for j,i in enumerate(rank_fea_list):
    index=list(names).index(i) #獲取序列號
    index_list[j]=index
print("排序後特徵對應原特徵名的序列號:")
print("\t",index_list)
print("------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------")
'''
#如果想要看一看每個特徵與結果之間的散點分佈情況的話,請把''' ''''去掉即可,即把註釋符號去掉
#給予排序號的每個特徵和結果畫圖,看看每個特徵和結果之間的關係
'''
for i in index_list:                                #共有13個特徵,所以畫13張圖
    plt.figure(names[i])            #每張圖以該特徵為名
    plt.scatter(X[:,i],y)             #畫出散點圖
    plt.xlabel(names[i])
    plt.ylabel("price house")
#提取排名前n個特徵的演算法擬合
print("提取排序後的前n個特徵向量進行訓練:")
for time in range(2,13):
    X_exc=np.zeros((X.shape[0],time))     #把排序好前六個特徵向量提取出來,放在X—exc矩陣裡
    for  j,i in enumerate(index_list[:time]):
        X_exc[:,j]=X[:,i]

    X_train1,X_test1,y_train1,y_test1=train_test_split(X_exc,y)
    lr1=LinearRegression()
    lr1.fit(X_train1,y_train1)
    print("\t提取{0}個的特徵-->R方值\t".format(time),lr1.score(X_test1,y_test1))
print()

#原資料全部特徵擬合
print("全部特徵向量進行訓練:")
X_train_raw,X_test_raw,y_train_raw,y_test_raw=train_test_split(X,y)
lr_raw=LinearRegression()
lr_raw.fit(X_train_raw,y_train_raw)
print("\t全部特徵---->R方值\t",lr_raw.score(X_test_raw,y_test_raw))
print()

#只提取一個特徵向量
print("只提取一個特徵向量進行訓練:")
for i in index_list:
    X2=np.zeros((X.shape[0],1))
    X2[:,0]=X[:,index_list[i]]
    X_train2,X_test2,y_train2,y_test2=train_test_split(X2,y)
    lr2=LinearRegression()
    lr2.fit(X_train2,y_train2)
    print("\t特徵",names[i],"---->R方值","\t",lr2.score(X_test2,y_test2))
print()

#採取L1正則化的方法
print("採取L1正則化的方法:")
lasso= Lasso(alpha=0.3)        #alpha引數由網友用網格搜尋方法確定下來的
lasso.fit(X_train_raw,y_train_raw)
print("\tL1正則化特徵---->R方值\t",lasso.score(X_test_raw,y_test_raw))
print()

#採取L2正則化的方法
print("採取L2正則化的方法")
ridge = Ridge(alpha=10)         #alpha引數由網友用網格搜尋方法確定下來的
ridge.fit(X_train_raw,y_train_raw)
print("\tL2正則化特徵---->R方值\t",ridge.score(X_test_raw,y_test_raw))
plt.show()        #顯示圖片

例子中用到基於線性模型用到RFE,正則化等方法,也看出特徵選擇後對結論的影響。特徵選擇是進行模型訓練前的最後一步,是資料加工過程的最後一步,幾乎每個例子中都會涉及到。
如前面所述,在機器學習領域大部分時間將會與資料打交道,因此,在學習演算法的過程中一定要利用python把資料玩兒的很溜,邊學python邊學資料!!!
PS
正則化是一個比較繞人的問題,下一篇會專門講正則化,會參考幾篇CSDN的部落格加上自己的理解。