视频作者:菜菜TsaiTsai 链接:【技术干货】菜菜的机器学习sklearn【全85集】Python进阶_哔哩哔哩_bilibili
方差挑选完毕之后,我们就要考虑下一个问题:相关性了。我们希望选出与标签相关且有意义的特征,因为这样的特征能够为我们提供大量信息。
$\chi^{2}$分布可以用来检验模型的适合性,变量的独立性等,对象是类别变量。$\chi^{2}$的检验公式是 $$ \chi^{2}=\sum\limits_{i=1}^{n}\frac{(O_{i}-E_{i})^{2}}{E_{i}} $$ 其中,$O_{i}$是observation,观测数据,$E_{i}$是Expectation,期望数据
卡方分布的思想:我们先假设数据服从某一分布或者是某一模型,然后通过假设,计算出数据应该有的样子$E_{i}$,再用现实中观察到的值$O_{i}$与$E_{i}$比较,即代入卡方检验公式。如果观测值与期望值相差值很大,$\chi^{2}$就很大,我们就会拒绝假设 对于列联表,假设数据服从的模型就是每一行列最后的统计量,每一格$E_{ij}=E_{i \cdot }\cdot E_{\cdot j}$,然后计算其与$O_{ij}$的$\chi^{2}$……
例:
假设这里有三个搜索引擎,我们假设$H_{0}$:三个算法一样好(不同算法对搜索结果的好坏无影响、属性和组别无关),$H_{1}$:三个算法不一样好
一般思路我们是假设$H_{0}$成立,对于取定的显著性水平$\alpha$,若计算出的$\chi^{2}\leq \chi^{2}{\alpha}$,则接受$H{0}$;若计算出的$\chi^{2}>\chi^{2}{\alpha}$,则拒绝$H{0}$,接受$H_{1}$
对于本例,对于第一行第一列,有
$$
E_{11}=5000 \times \frac{7078}{10000}=3539
$$
也就是表中括号内的数字。由此,我们可以得到
$$
\chi^{2}=\frac{(3511-3539)^{2}}{3539}+ \frac{(1749-1769.5)^{2}}{1769.5}+\cdots +\frac{(682-730.5)^{2}}{730.5}=6.120
$$
得到$\chi^{2}$后我们还需要知道对应$\chi^{2}$分布的自由度$df$ $$ df=(Row-1)(column-1) $$ 因为我们知道了Total行和Total列,因此,对于某一行假设有$column$个元素,只要我们确定$column-1$个元素,就能固定剩余一个元素的取值。对于列同理。因此自由度为$(Row-1)(column-1)$
对于上例,有 $$ df = (2-1)\times (3-1)=2 $$ 自由度为2的卡方分布有$\chi^{2}{0.050}=5.99147,\chi^{2}{0.010}=9.21034$,因此如果我们取$\alpha=0.05$,有$5.99147<6.12$,则接受$H_{0}$,如果$\alpha=0.01$,则拒绝$H_{0}$,接受$H_{1}$
卡方过滤是专门针对离散型标签(即分类问题)的相关性过滤。 卡方检验类feature_selection.chi2计算每个非负特征和标签之间的卡方统计量,并依照卡方统计量由高到低为特征排名(卡方越大特征越好)。再结合feature_selection.SelectKBest这个可以输入”评分标准“来选出前K个分数最高的特征的类,我们可以借此除去最可能独立于标签,与我们分类目的无关的特征。
为什么卡方越大越好,这是因为卡方越大,根据上面的数学推理,我们可以认为标签和特征无关,就是二者之间独立的可能性越大。对于独立的标签和特征,如果我们知道了特征的取值,但由于二者独立,即其中一个的取值不影响另一个,因此该特征对于标签是哪个的贡献并不大,所以我们要求卡方越大越好
如果卡方检验检测到某个特征中所有的值都相同,会提示我们使用方差先进行方差过滤。
SelectKBest(score_func=<function f_classif at 0x00000202ABD80620>, k=10)
# score:评分标准
# k:保留k个最优特征
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.model_selection import cross_val_score
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
# 假设在这里我知道我需要300个特征
X_fschi = SelectKBest(chi2,k=300).fit_transform(X_fsvar,y)
X_fschi.shape
---
(42000, 300)
cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean()
---
0.9333098667649198
分数降低了,说明我们这里卡方过滤的效果并不好,不如没过滤,我们需要更换k值或者换方法
选取超参数k
我们可以话学习曲线,但由于过于耗时间,所以实际应用不考虑
import matplotlib.pyplot as plt
score = []
for i in range(350,200,-10):
X_fschi = SelectKBest(chi2,k=i).fit_transform(X_fsvar,y)
once = cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean()
score.append(once)
plt.plot(range(350,200,-10),score)
plt.show()
通过这条曲线,我们可以观察到,随着k值的不断增加,模型的表现不断上升,这说明,k越大越好,数据中所有的特征都是与标签相关的。
实际上,我们是通过指定p来选择k,一般指定p=0.05或0.01,要求特征对于标签的p值≤0.05或0.01
所谓差异不是自然形成的,可以这样理解,假设标签可能取值为0,1,特征可能取值为2,3,当特征取2的时候,标签取0的可能性大于取1的时候的可能性
从特征工程的角度,我们希望选择卡饭值很大,p值小于0.05的特征,即和标签是相关的特征。 实际上我们调用SelectKBest之前,我们可以直接从chi2实例化后的模型中获得各个特征所对应的卡方值和P值,由此来过滤p值>0.05的特征
chi2(X, y)
# 返回两个值,分别是卡方,p值,
chivalue, pvalues_chi = chi2(X_fsvar,y)
chivalue.shape,pvalues_chi.shape# 392个特征
---
((392,), (392,))
k = chivalue.shape[0] - (pvalues_chi > 0.05).sum()# True=1,False=0
k # p值≤0.05特征的数量
---
392
X_fschi = SelectKBest(chi2,k).fit_transform(X_fsvar,y)
# 这里k=392也就是没过滤效果
cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean
可以观察到,所有特征的p值都是≤0.05,这说明对于digit recognizor这个数据集来说,方差验证已经把所有和标签无关的特征都剔除了,或者这个数据集本身就不含与标签无关的特征。在这种情况下,舍弃任何一个特征,都会舍弃对模型有用的信息,而使模型表现下降,因此在这种情况下,在我们对计算速度感到满意时,我们不需要使用相关性过滤来过滤我们的数据。如果我们认为运算速度太缓慢,那我们可以酌情删除一些特征,但前提是,我们必须牺牲模型的表现。
卡方验证的python实现(其实我觉得还挺有用的)
from sklearn.preprocessing import LabelBinarizer
from scipy.special import chdtrc
import numpy as np
import pandas as pd
X = np.array([3.4, 3.4, 3. , 2.8, 2.7, 2.9, 3.3, 3. , 3.8, 2.5])
y = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2, 2])
y = LabelBinarizer().fit_transform(y)
observed = y.T.dot(X)
observed # 不同y的观测值
---
array([ 9.8, 8.4, 12.6])
feature_count = X.sum().reshape(-1,1)
feature_count # X求和,用于算预测值
---
array([[30.8]])
class_prob = y.mean(axis=0).reshape(1,-1)
class_prob # 不同y出现的次数来估计概率
---
array([[0.3, 0.3, 0.4]])
expected = np.dot(class_prob.T, feature_count)
expected # 不同y的期望值
---
array([[ 9.24],
[ 9.24],
[12.32]])
chi2 = ((observed.reshape(-1,1) - expected) ** 2 / expected).sum(axis=0) # 卡方公式
chi2
---
array([0.11666667])
p = chdtrc(3-1,chi2) # 用自由度和卡方值来算p值
p
---
array([0.94333545])
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
X = np.array([[3.4, 3.4, 3. , 2.8, 2.7, 2.9, 3.3, 3. , 3.8, 2.5],[0, 0, 9, 0, 0, 0, 0, 0, 0, 0]])
# 添加[0, 0, 9, 0, 0, 0, 0, 0, 0, 0],为了更好的理解什么叫标签与特征相关,也就是说,如果我们给定x=0,那么我们就能有很大概率y=0
y = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2, 2])
chivalue, pvalues_chi = chi2(X.T,y)
chivalue, pvalues_chi
---
(array([ 0.11666667, 21. ]), array([9.43335450e-01, 2.75364493e-05]))
X = SelectKBest(chi2,k=1).fit_transform(X.T,y)
X
---
array([[0.],
[0.],
[9.],
[0.],
[0.],
[0.],
[0.],
[0.],
[0.],
[0.]])