專欄 | 基於 Jupyter 的特徵工程手冊:特徵選擇(二)

作者:陳穎祥、楊子晗

編譯:AI有道

數據預處理後,我們生成了大量的新變量(比如獨熱編碼生成了大量僅包含0或1的變量)。但實際上,部分新生成的變量可能是多餘:一方面它們本身不一定包含有用的信息,故無法提高模型性能;另一方面過這些多餘變量在構建模型時會消耗大量內存和計算能力。因此,我們應該進行特徵選擇並選擇特徵子集進行建模。

項目地址:

https://github.com/YC-Coder-Chen/feature-engineering-handbook

本文將介紹特徵工程第一種算法:Filter Methods 過濾法(下)。

目錄:

1.1.1.5 Mutual Information (regression problem) 互信息 (迴歸問題)

互信息(Mutual Information)衡量變量間的相互依賴性。其本質爲熵差,即 ????(????)−????(????|????),即知道另一個變量信息後混亂的降低程度 。當且僅當兩個隨機變量獨立時MI等於零。MI值越高,兩變量之間的相關性則越強。與Pearson相關和F統計量相比,它還捕獲了非線性關係。

公式:

  • 若兩個變量均爲離散變量:

p(????,????)(????,????) 爲x和y的聯合概率質量函數 (PMF), p????(????)則爲x的聯合概率質量函數 (PMF)。 

  • 若兩個變量均爲連續變量:

p(????,????)(????,????) 爲x和y的聯合概率密度函數 (PDF),p????(????)則爲x的概率密度函數 (PDF)。連續變量情形下,在實際操作中,往往先對數據離散化分桶,然後逐個桶進行計算。 

但是實際上,一種極有可能的情況是,x和y中的一個可能是離散變量,而另一個是連續變量。因此在sklearn中,它基於[1]和[2]中提出的基於k最臨近算法的熵估計非參數方法。 

[1] A. Kraskov, H. Stogbauer and P. Grassberger, “Estimating mutual information”. Phys. Rev. E 69, 2004. 

[2] B. C. Ross “Mutual Information between Discrete and Continuous Data Sets”. PLoS ONE 9(2), 2014.

import numpy as np
from sklearn.feature_selection import mutual_info_regression
from sklearn.feature_selection import SelectKBest


# 直接載入數據集
from sklearn.datasets import fetch_california_housing
dataset = fetch_california_housing()
X, y = dataset.data, dataset.target # 利用 california_housing 數據集來演示
# 此數據集中,X,y均爲連續變量,故此滿足使用MI的條件


# 選擇前15000個觀測點作爲訓練集
# 剩下的作爲測試集
train_set = X[0:15000,:].astype(float)
test_set = X[15000:,].astype(float)
train_y = y[0:15000].astype(float)


# KNN中的臨近數是一個非常重要的參數
# 故我們重寫了一個新的MI計算方程更好的來控制這一參數
def udf_MI(X, y):
    result = mutual_info_regression(X, y, n_neighbors = 5) # 用戶可以輸入想要的臨近數
    return result


# SelectKBest 將會基於一個判別方程自動選擇得分高的變量
# 這裏的判別方程爲F統計量
selector = SelectKBest(udf_MI, k=2) # k => 我們想要選擇的變量數
selector.fit(train_set, train_y) # 在訓練集上訓練
transformed_train = selector.transform(train_set) # 轉換訓練集
transformed_train.shape #(15000, 2), 其選擇了第一個及第八個變量
assert np.array_equal(transformed_train, train_set[:,[0,7]])


transformed_test = selector.transform(test_set) # 轉換測試集
assert np.array_equal(transformed_test, test_set[:,[0,7]]);
# 可見對於測試集,其依然選擇了第一個及第八個變量
# 驗算上述結果
for idx in range(train_set.shape[1]):
    score = mutual_info_regression(train_set[:,idx].reshape(-1,1), train_y, n_neighbors = 5)
    print(f"第{idx + 1}個變量與因變量的互信息爲{round(score[0],2)}")
# 故應選擇第一個及第八個變量
第1個變量與因變量的互信息爲0.37
第2個變量與因變量的互信息爲0.03
第3個變量與因變量的互信息爲0.1
第4個變量與因變量的互信息爲0.03
第5個變量與因變量的互信息爲0.02
第6個變量與因變量的互信息爲0.09
第7個變量與因變量的互信息爲0.37
第8個變量與因變量的互信息爲0.46

1.1.1.6 Chi-squared Statistics (classification problem) 卡方統計量 (分類問題)

卡方統計量主要用於衡量兩個類別特徵之間的相關性。sklearn提供了chi2方程用於計算卡方統計量。其輸入的特徵變量必須爲布爾值或頻率(故對於類別變量應考慮獨熱編碼)。卡方統計量的零假設爲兩個變量是獨立的,因爲卡方統計量值越高,則兩個類別變量的相關性越強。因此,我們應該選擇具有較高卡方統計量的特徵。

公式:

其中, ????????,???? 爲在變量X上具有i-th類別值且在變量Y上具有j-th類別值的實際觀測點計數。????????,???? 爲利用概率估計的應在在變量X上具有i-th類別值且在變量Y上具有j-th類別值的觀測點數量。n爲總觀測數, ???????? 爲在變量X上具有i-th類別值的概率, ???????? 爲在變量Y上具有j-th類別值的概率。

值得注意的是,通過解析源代碼,我們發現在sklearn中利用chi2計算出來的卡方統計量並不是統計意義上的卡方統計量。當輸入變量爲布爾變量時,chi2計算值爲該布爾變量爲True時候的卡方統計量(我們將會在下文舉例說明)。這樣的優勢是,獨熱編碼生成的所有布爾值變量的chi2值之和將等於原始變量統計意義上的卡方統計量。 

舉個簡單的例子,假設一個變量I有0,1,2兩種可能的值,則獨特編碼後一共會產生3個新的布爾值變量。這三個布爾值變量的chi2計算出來的值之和,將等於變量I與因變量直接計算得出的統計意義上的卡方統計量。

解析sklearn中chi2的計算

# 首先,隨機生成一個數據集
import pandas as pd
sample_dict = {'Type': ['J','J','J',
                        'B','B','B',
                        'C','C','C','C','C'], 
               'Output': [0, 1, 0, 
                          2, 0, 1,  
                          0, 0, 1, 2, 2,]}
sample_raw = pd.DataFrame(sample_dict)
sample_raw #原始數據,Output是我們的目標變量,Type爲類別變量

# 下面利用獨熱編碼生成布爾變量,並利用sklearn計算每一個布爾變量的chi2值
sample = pd.get_dummies(sample_raw)
from sklearn.feature_selection import chi2
chi2(sample.values[:,[1,2,3]],sample.values[:,[0]])
# 第一行爲每一個布爾變量的chi2值
(array([0.17777778, 0.42666667, 1.15555556]),
 array([0.91494723, 0.8078868 , 0.56114397]))
# 下面直接計算原始變量Type與output統計學意義上的卡方統計量
# 首先,先統計每一個類別下出現的觀測數,用於創建列聯表
obs_df = sample_raw.groupby(['Type','Output']).size().reset_index()
obs_df.columns = ['Type','Output','Count']
obs_df

即列聯表(contingency table)爲:

from scipy.stats import chi2_contingency
obs = np.array([[1, 1, 1], [2, 1, 2],[2, 1, 0]])
chi2_contingency(obs) # 第一個值即爲變量Type與output統計學意義上的卡方統計量
(1.7600000000000002,
 0.779791873961373,
 4,
 array([[1.36363636, 0.81818182, 0.81818182],
        [2.27272727, 1.36363636, 1.36363636],
        [1.36363636, 0.81818182, 0.81818182]]))
# 而chi2方程算出來的布爾值之和爲即爲原始變量的統計意義上的卡方統計量
chi2(sample.values[:,[1,2,3]],sample.values[:,[0]])[0].sum() == chi2_contingency(obs)[0]
True
# 那麼sklearn中的chi2是如何計算的呢?
# 不妨以第一個生成的布爾值爲例,即Type爲B
# chi2出來的值爲0.17777778
# 而這與利用scipy以下代碼計算出的計算一致
from scipy.stats import chisquare
f_exp = np.array([5/11, 3/11, 3/11]) * 3 # 預期頻數爲 output的先驗概率 * Type爲B 的樣本數
chisquare([1,1,1], f_exp=f_exp) # [1,1,1] 即Type爲B 的樣本實際頻數
# 即sklearn 中的chi2 僅考慮了Type爲B情形下的列連表
Power_divergenceResult(statistic=0.17777777777777778, pvalue=0.9149472287300311)

如何利用sklearn 來進行特徵選擇

import numpy as np
from sklearn.feature_selection import chi2
from sklearn.feature_selection import SelectKBest


# 直接載入數據集
from sklearn.datasets import load_iris # 利用iris數據作爲演示數據集
iris = load_iris()
X, y = iris.data, iris.target
# 此數據集中,X爲連續變量,y爲類別變量
# 不滿足chi2的使用條件


# 將連續變量變爲布爾值變量以滿足chi2使用條件
# 不妨利用其是否大於均值來生成布爾值(僅作爲演示用)
X = X > X.mean(0)


# iris 數據集使用前需要被打亂順序
np.random.seed(1234)
idx = np.random.permutation(len(X))
X = X[idx]
y = y[idx]


# 選擇前100個觀測點作爲訓練集
# 剩下的作爲測試集
train_set = X[0:100,:]
test_set = X[100:,]
train_y = y[0:100]


# sklearn 中直接提供了方程用於計算卡方統計量
# SelectKBest 將會基於一個判別方程自動選擇得分高的變量
# 這裏的判別方程爲F統計量
selector = SelectKBest(chi2, k=2) # k => 我們想要選擇的變量數
selector.fit(train_set, train_y) # 在訓練集上訓練
transformed_train = selector.transform(train_set) # 轉換訓練集
transformed_train.shape #(100, 2), 其選擇了第三個及第四個變量 
assert np.array_equal(transformed_train, train_set[:,[2,3]])


transformed_test = selector.transform(test_set) # 轉換測試集
assert np.array_equal(transformed_test, test_set[:,[2,3]]);
# 可見對於測試集,其依然選擇了第三個及第四個變量
# 驗證上述結果
for idx in range(train_set.shape[1]):
    score, p_value = chi2(train_set[:,idx].reshape(-1,1), train_y)
    print(f"第{idx + 1}個變量與因變量的卡方統計量爲{round(score[0],2)},p值爲{round(p_value[0],3)}")
# 故應選擇第三個及第四個變量
第1個變量與因變量的卡方統計量爲29.69,p值爲0.0
第2個變量與因變量的卡方統計量爲19.42,p值爲0.0
第3個變量與因變量的卡方統計量爲31.97,p值爲0.0
第4個變量與因變量的卡方統計量爲31.71,p值爲0.0

1.1.1.7 F-Score (classification problem) F-統計量 (分類問題)

在分類機器學習問題中,若變量特徵爲類別特徵,則我們可以使用獨熱編碼配合上述chi2方法選擇最重要的特徵。但若特徵爲連續變量,則我們可以使用ANOVA-F值。ANOVA F統計量的零假設是若按目標變量(類別)分組,則連續變量的總體均值是相同的。故我們應選擇具有高ANOVA-F統計量的連續變量,因爲這些連續變量與目標變量的關聯性強。 

公式:

其中,SS(between)爲組間的平方和,即組均值和總體均值之間的平方和。SS(error)是組內的平方和,即數據與組均值之間的平方和。m是目標變量的總類別數,n是觀測數。

import numpy as np
from sklearn.feature_selection import f_classif
from sklearn.feature_selection import SelectKBest


# 直接載入數據集
from sklearn.datasets import load_iris # 利用iris數據作爲演示數據集
iris = load_iris()
X, y = iris.data, iris.target
# 此數據集中,X爲連續變量,y爲類別變量
# 滿足ANOVA-F的使用條件


# iris 數據集使用前需要被打亂順序
np.random.seed(1234)
idx = np.random.permutation(len(X))
X = X[idx]
y = y[idx]


# 選擇前100個觀測點作爲訓練集
# 剩下的作爲測試集
train_set = X[0:100,:]
test_set = X[100:,]
train_y = y[0:100]


# sklearn 中直接提供了方程用於計算ANOVA-F
# SelectKBest 將會基於一個判別方程自動選擇得分高的變量
# 這裏的判別方程爲F統計量
selector = SelectKBest(f_classif, k=2) # k => 我們想要選擇的變量數
selector.fit(train_set, train_y) # 在訓練集上訓練
transformed_train = selector.transform(train_set) # 轉換訓練集
transformed_train.shape #(100, 2), 其選擇了第三個及第四個變量 
assert np.array_equal(transformed_train, train_set[:,[2,3]])


transformed_test = selector.transform(test_set) # 轉換測試集
assert np.array_equal(transformed_test, test_set[:,[2,3]]);
# 可見對於測試集,其依然選擇了第三個及第四個變量
# 驗證上述結果
for idx in range(train_set.shape[1]):
    score, p_value = f_classif(train_set[:,idx].reshape(-1,1), train_y)
    print(f"第{idx + 1}個變量與因變量的ANOVA-F統計量爲{round(score[0],2)},p值爲{round(p_value[0],3)}")
# 故應選擇第三個及第四個變量
第1個變量與因變量的ANOVA-F統計量爲91.39,p值爲0.0
第2個變量與因變量的ANOVA-F統計量爲33.18,p值爲0.0
第3個變量與因變量的ANOVA-F統計量爲733.94,p值爲0.0
第4個變量與因變量的ANOVA-F統計量爲608.95,p值爲0.0

1.1.1.7 Mutual Information (classification problem) 互信息 (分類問題)

【與1.1.1.5一樣】互信息(Mutual Information)衡量變量間的相互依賴性。其本質爲熵差,即 ????(????)−????(????|????),即知道另一個變量信息後混亂的降低程度 。當且僅當兩個隨機變量獨立時MI等於零。MI值越高,兩變量之間的相關性則越強。與Pearson相關和F統計量相比,它還捕獲了非線性關係。 

公式: 

  • 若兩個變量均爲離散變量:

p(????,????)(????,????) 爲x和y的聯合概率質量函數 (PMF), p????(????)則爲x的的聯合概率質量函數 (PMF)。 

  • 若兩個變量均爲連續變量:

p(????,????)(????,????) 爲x和y的聯合概率密度函數 (PDF),p????(????)則爲x的的聯合概率密度函數 (PDF)。連續變量情形下,在實際操作中,往往先對數據離散化分桶,然後逐個桶進行計算。 

但是實際上,一種極有可能的情況是,x和y中的一個可能是離散變量,而另一個是連續變量。因此在sklearn中,它基於[1]和[2]中提出的基於k最臨近算法的熵估計非參數方法。 

[1] A. Kraskov, H. Stogbauer and P. Grassberger, “Estimating mutual information”. Phys. Rev. E 69, 2004. 

[2] B. C. Ross “Mutual Information between Discrete and Continuous Data Sets”. PLoS ONE 9(2), 2014.

import numpy as np
from sklearn.feature_selection import mutual_info_classif
from sklearn.feature_selection import SelectKBest


# 直接載入數據集
from sklearn.datasets import load_iris # 利用iris數據作爲演示數據集
iris = load_iris()
X, y = iris.data, iris.target
# 此數據集中,X爲連續變量,y爲類別變量
# 滿足MI的使用條件


# iris 數據集使用前需要被打亂順序
np.random.seed(1234)
idx = np.random.permutation(len(X))
X = X[idx]
y = y[idx]


# 選擇前100個觀測點作爲訓練集
# 剩下的作爲測試集
train_set = X[0:100,:]
test_set = X[100:,]
train_y = y[0:100]


# KNN中的臨近數是一個非常重要的參數
# 故我們重寫了一個新的MI計算方程更好的來控制這一參數
def udf_MI(X, y):
    result = mutual_info_classif(X, y, n_neighbors = 5) # 用戶可以輸入想要的臨近數
    return result


# SelectKBest 將會基於一個判別方程自動選擇得分高的變量
# 這裏的判別方程爲F統計量
selector = SelectKBest(udf_MI, k=2) # k => 我們想要選擇的變量數
selector.fit(train_set, train_y) # 在訓練集上訓練
transformed_train = selector.transform(train_set) # 轉換訓練集
transformed_train.shape #(100, 2), 其選擇了第三個及第四個變量 
assert np.array_equal(transformed_train, train_set[:,[2,3]])


transformed_test = selector.transform(test_set) # 轉換測試集
assert np.array_equal(transformed_test, test_set[:,[2,3]]);
# 可見對於測試集,其依然選擇了第三個及第四個變量
# 驗算上述結果
for idx in range(train_set.shape[1]):
    score = mutual_info_classif(train_set[:,idx].reshape(-1,1), train_y, n_neighbors = 5)
    print(f"第{idx + 1}個變量與因變量的互信息爲{round(score[0],2)}")
# 故應選擇第三個及第四個變量
第1個變量與因變量的互信息爲0.56
第2個變量與因變量的互信息爲0.28
第3個變量與因變量的互信息爲0.99
第4個變量與因變量的互信息爲1.02

專欄系列:

專欄 | 基於 Jupyter 的特徵工程手冊:數據預處理(一)

專欄 | 基於 Jupyter 的特徵工程手冊:數據預處理(二)

專欄 | 基於 Jupyter 的特徵工程手冊:數據預處理(三)

專欄 | 基於 Jupyter 的特徵工程手冊:數據預處理(四)

專欄 | 基於 Jupyter 的特徵工程手冊:特徵選擇(一)

目前該項目完整中文版正在製作中,請持續關注哦~

中文版 Jupyter 地址:

https://github.com/YC-Coder-Chen/feature-engineering-handbook/blob/master/中文版/2.%20特徵選擇.ipynb


推薦閱讀

(點擊標題可跳轉閱讀)

乾貨 | 公衆號歷史文章精選

我的深度學習入門路線

我的機器學習入門路線圖

算法工程師必備

AI有道年度技術文章電子版PDF來啦!

掃描下方二維碼,添加 AI有道小助手微信,可申請入羣,並獲得2020完整技術文章合集PDF(一定要備註:入羣 + 地點 + 學校/公司。例如:入羣+上海+復旦。 

長按掃碼,申請入羣

(添加人數較多,請耐心等待)

 

最新 AI 乾貨,我在看 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章