天天看點

為何推薦sklearn做單機特征工程?【下】

一、特征選擇

當資料預處理完成後,我們需要選擇有意義的特征輸入機器學習的算法和模型進行訓練。通常來說,從兩個方面考慮來選擇特征:

  • 特征是否發散:如果一個特征不發散,例如方差接近于0,也就是說樣本在這個特征上基本上沒有差異,這個特征對于樣本的區分并沒有什麼用。
  • 特征與目标的相關性:這點比較顯見,與目标相關性高的特征,應當優選選擇。除方差法外,本文介紹的其他方法均從相關性考慮。

根據特征選擇的形式又可以将特征選擇方法分為3種:

  • Filter:過濾法,按照發散性或者相關性對各個特征進行評分,設定門檻值或者待選擇門檻值的個數,選擇特征。
  • Wrapper:包裝法,根據目标函數(通常是預測效果評分),每次選擇若幹特征,或者排除若幹特征。
  • Embedded:嵌入法,先使用某些機器學習的算法和模型進行訓練,得到各個特征的權值系數,根據系數從大到小選擇特征。類似于Filter方法,但是是通過訓練來确定特征的優劣。

我們使用sklearn中的feature_selection庫來進行特征選擇。

1. Filter

方差選擇法

使用方差選擇法,先要計算各個特征的方差,然後根據門檻值,選擇方差大于門檻值的特征。使用feature_selection庫的VarianceThreshold類來選擇特征的代碼如下:

from sklearn.feature_selection import VarianceThreshold

#方差選擇法,傳回值為特征選擇後的資料
#參數threshold為方差的門檻值
VarianceThreshold(threshold=3).fit_transform(iris.data)           

相關系數法

使用相關系數法,先要計算各個特征對目标值的相關系數以及相關系數的P值。用feature_selection庫的SelectKBest類結合相關系數來選擇特征的代碼如下:

import numpy as np
from sklearn.feature_selection import SelectKBest
from scipy.stats import pearsonr

#選擇K個最好的特征,傳回選擇特征後的資料
#第一個參數為計算評估特征是否好的函數,該函數輸入特征矩陣和目标向量,輸出二進制組(評分,P值)的數組,數組第i項為第i個特征的評分和P值。在此定義為計算相關系數
#參數k為選擇的特征個數
#SelectKBest(lambda X, Y: array(map(lambda x:pearsonr(x, Y), X.T)).T, k=2).fit_transform(iris.data, iris.target)

SelectKBest(lambda X,Y:np.array(list(map(lambda x:pearsonr(x, Y), X.T))).T[0], k=2).fit_transform(iris.data, iris.target)           

卡方檢驗

經典的卡方檢驗是檢驗定性自變量對定性因變量的相關性。假設自變量有N種取值,因變量有M種取值,考慮自變量等于i且因變量等于j的樣本頻數的觀察值與期望的差距,建構統計量:

為何推薦sklearn做單機特征工程?【下】

這個統計量的含義簡而言之就是自變量對因變量的相關性。用feature_selection庫的SelectKBest類結合卡方檢驗來選擇特征的代碼如下:

from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2

#選擇K個最好的特征,傳回選擇特征後的資料
SelectKBest(chi2, k=2).fit_transform(iris.data, iris.target)           

互資訊法

經典的互資訊也是評價定性自變量對定性因變量的相關性的,互資訊計算公式如下:

為何推薦sklearn做單機特征工程?【下】

為了處理定量資料,最大資訊系數法被提出,使用feature_selection庫的SelectKBest類結合最大資訊系數法來選擇特征的代碼如下:

!pip install minepy
from sklearn.feature_selection import SelectKBest
from minepy import MINE

#由于MINE的設計不是函數式的,定義mic方法将其為函數式的,傳回一個二進制組,二進制組的第2項設定成固定的P值0.5
def mic(x, y):
    m = MINE()
    m.compute_score(x, y)
    return (m.mic(), 0.5)

#選擇K個最好的特征,傳回特征選擇後的資料
#SelectKBest(lambda X, Y: array(map(lambda x:mic(x, Y), X.T)).T, k=2).fit_transform(iris.data, iris.target)
SelectKBest(lambda X, Y: array(list(map(lambda x:mic(x, Y), X.T))).T[0], k=2).fit_transform(iris.data, iris.target)           

2.Wrapper

遞歸特征消除法

遞歸消除特征法使用一個基模型來進行多輪訓練,每輪訓練後,消除若幹權值系數的特征,再基于新的特征集進行下一輪訓練。使用feature_selection庫的RFE類來選擇特征的代碼如下:

from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression

#遞歸特征消除法,傳回特征選擇後的資料
#參數estimator為基模型
#參數n_features_to_select為選擇的特征個數
RFE(estimator=LogisticRegression(), n_features_to_select=2).fit_transform(iris.data, iris.target)           

3.Embedded

基于懲罰項的特征選擇法

使用帶懲罰項的基模型,除了篩選出特征外,同時也進行了降維。使用feature_selection庫的SelectFromModel類結合帶L1懲罰項的邏輯回歸模型,來選擇特征的代碼如下:

from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression

#帶L1懲罰項的邏輯回歸作為基模型的特征選擇
SelectFromModel(LogisticRegression(penalty="l1", C=0.1)).fit_transform(iris.data, iris.target)           

L1懲罰項降維的原理在于保留多個對目标值具有同等相關性的特征中的一個,是以沒選到的特征不代表不重要。故,可結合L2懲罰項來優化。具體操作為:若一個特征在L1中的權值為1,選擇在L2中權值差别不大且在L1中權值為0的特征構成同類集合,将這一集合中的特征平分L1中的權值,故需要建構一個新的邏輯回歸模型:

from sklearn.linear_model import LogisticRegression

class LR(LogisticRegression):
    def __init__(self, threshold=0.01, dual=False, tol=1e-4, C=1.0,
                 fit_intercept=True, intercept_scaling=1, class_weight=None,
                 random_state=None, solver='liblinear', max_iter=100,
                 multi_class='ovr', verbose=0, warm_start=False, n_jobs=1):

        #權值相近的門檻值
        self.threshold = threshold
        LogisticRegression.__init__(self, penalty='l1', dual=dual, tol=tol, C=C,
                 fit_intercept=fit_intercept, intercept_scaling=intercept_scaling, class_weight=class_weight,
                 random_state=random_state, solver=solver, max_iter=max_iter,
                 multi_class=multi_class, verbose=verbose, warm_start=warm_start, n_jobs=n_jobs)
        #使用同樣的參數建立L2邏輯回歸
        self.l2 = LogisticRegression(penalty='l2', dual=dual, tol=tol, C=C, fit_intercept=fit_intercept, intercept_scaling=intercept_scaling, class_weight = class_weight, random_state=random_state, solver=solver, max_iter=max_iter, multi_class=multi_class, verbose=verbose, warm_start=warm_start, n_jobs=n_jobs)

    def fit(self, X, y, sample_weight=None):
        #訓練L1邏輯回歸
        super(LR, self).fit(X, y, sample_weight=sample_weight)
        self.coef_old_ = self.coef_.copy()
        #訓練L2邏輯回歸
        self.l2.fit(X, y, sample_weight=sample_weight)

        cntOfRow, cntOfCol = self.coef_.shape
        #權值系數矩陣的行數對應目标值的種類數目
        for i in range(cntOfRow):
            for j in range(cntOfCol):
                coef = self.coef_[i][j]
                #L1邏輯回歸的權值系數不為0
                if coef != 0:
                    idx = [j]
                    #對應在L2邏輯回歸中的權值系數
                    coef1 = self.l2.coef_[i][j]
                    for k in range(cntOfCol):
                        coef2 = self.l2.coef_[i][k]
                        #在L2邏輯回歸中,權值系數之差小于設定的門檻值,且在L1中對應的權值為0
                        if abs(coef1-coef2) < self.threshold and j != k and self.coef_[i][k] == 0:
                            idx.append(k)
                    #計算這一類特征的權值系數均值
                    mean = coef / len(idx)
                    self.coef_[i][idx] = mean
        return self           

使用feature_selection庫的SelectFromModel類結合帶L1以及L2懲罰項的邏輯回歸模型,來選擇特征的代碼如下:

from sklearn.feature_selection import SelectFromModel

#帶L1和L2懲罰項的邏輯回歸作為基模型的特征選擇
#參數threshold為權值系數之差的門檻值
SelectFromModel(LR(threshold=0.5, C=0.1)).fit_transform(iris.data, iris.target)           

基于樹模型的特征選擇法

樹模型中GBDT也可用來作為基模型進行特征選擇,使用feature_selection庫的SelectFromModel類結合GBDT模型,來選擇特征的代碼如下:

from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import GradientBoostingClassifier

#GBDT作為基模型的特征選擇
SelectFromModel(GradientBoostingClassifier()).fit_transform(iris.data, iris.target)
           

4.回顧

為何推薦sklearn做單機特征工程?【下】

二、降維

當特征選擇完成後,可以直接訓練模型了,但是可能由于特征矩陣過大,導緻計算量大,訓練時間長的問題,是以降低特征矩陣次元也是必不可少的。常見的降維方法除了以上提到的基于L1懲罰項的模型以外,另外還有主成分分析法(PCA)和線性判别分析(LDA),線性判别分析本身也是一個分類模型。PCA和LDA有很多的相似點,其本質是要将原始的樣本映射到次元更低的樣本空間中,但是PCA和LDA的映射目标不一樣:PCA是為了讓映射後的樣本具有最大的發散性;而LDA是為了讓映射後的樣本有最好的分類性能。是以說PCA是一種無監督的降維方法,而LDA是一種有監督的降維方法。

1. 主成分分析法(PCA)

使用decomposition庫的PCA類選擇特征的代碼如下:

from sklearn.decomposition import PCA

#主成分分析法,傳回降維後的資料
#參數n_components為主成分數目
PCA(n_components=2).fit_transform(iris.data)           

2. 線性判别分析法(LDA)

使用lda庫的LDA類選擇特征的代碼如下:

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA

#線性判别分析法,傳回降維後的資料
#參數n_components為降維後的維數
LDA(n_components=2).fit_transform(iris.data, iris.target)           

⭐總結

再讓我們回歸一下本文開始的特征工程的思維導圖,我們可以使用sklearn完成幾乎所有特征處理的工作,而且不管是資料預處理,還是特征選擇,抑或降維,它們都是通過某個類的方法fit_transform完成的。fit_transform要不隻帶一個參數:特征矩陣,要不帶兩個參數:特征矩陣加目标向量。

這些難道都是巧合嗎?還是故意設計成這樣?方法fit_transform中有fit這一單詞,它和訓練模型的fit方法有關聯嗎?接下來,請期待《如何使用sklearn優雅地進行資料挖掘》闡述其中的奧妙!

繼續閱讀