回归预测中的降维处理

  • 1 降维是什么?
  • 2 降维有哪些方式?
  • 2.1 特征选择
  • 2.2 特征提取
  • 3 特征选择的方法
  • 3.1 Filter过滤法:
  • 方差过滤
  • 卡方过滤
  • F检验
  • 互信息法


1 降维是什么?

降维就是减少原来数据的维度。
在进行数据分析时,可能当前数据有非常高维的特征,并且通常他们都有着非常强烈的相关性,难以通过常规的多元回归分析变量之间的关系。

2 降维有哪些方式?

2.1 特征选择

着重注意 选择 这里的选择指的是:从初始的特征集中选择对所求目标描述效果最好的特征子集。

举例说明:
对于一个数据集,目标值为 体重,自变量为年龄与身高。经特征选择之后,可能选择出来的唯一变量为
身高
因为在我们的认识中,身高越高往往体重越重,而年龄与体重没有太强的联系。

2.2 特征提取

用已有的特征 提取 出一个抽象程度更高的特征集,也指计算得到某个特征的算法,是对已经有的数据进行处理,将其中的某几项组合成一个能够更好地描述目标的特征。

举例说明:
还是刚才的数据集。经特征选择之后,可能选择出来的特征为
身高x0.8-年龄x0.2
因为在我们的认识中,不仅身高越高体重越重,而年龄越大往往体重越轻。

3 特征选择的方法

当数据预处理完成之后,我们需要选择有意义的特征来进行训练。通常来说,从两个方面考虑来选择特征:

  • 特征是否发散:
    如果一个特征不发散,例如方差接近于0,也就是说样本在这个特征上基本上没有差异,则特征对于样本的区分的作用很低,所以我们可以根据特征的发散程度来判断是否选择该特征。
  • 特征与目标的相关性:
    应该选择与目标相关性高的特征。例如大家在高考选择学校时,在分数面前,虽然每个人的其他特征都足够发散,但是仍需要选择与目标值相关性最大的参数,即高考成绩。

特征选择方法主要有三种:Filter过滤法、Wrapper包装法、Embedded嵌入法

3.1 Filter过滤法:

按照发散性或者相关性对各个特征进行评分,设定阈值来选择特征,仅对数据分析中的特征进行分析,不与后续的模型相关联。

方差过滤

这种方式是通过特征本身的方差来筛选特征,对于方差越大的特征,模型能够更好地分析出每个样本之间的区别。假设对于某数据集,有一个特征全部相等,模型不能分辨出样本之间的区别。所以,无论接下来的特征工程将进行哪一步,都要首先消除数据集中方差为零的特征。
这里对sklearn中的boston数据集进行分析

from sklearn.datasets import load_boston
from sklearn.feature_selection import VarianceThreshold
import pandas as pd
from xgboost import XGBRegressor 

bc = load_boston()  
X = pd.DataFrame(bc.data)
X.columns = bc.feature_names
y = bc.target

得到的数据集如下

lasso回归筛选变量多_sklearn

vt = VarianceThreshold(threshold=0.5)     
X_vt = pd.DataFrame(vt.fit_transform(X),columns=X.columns[vt.get_support()] )

以方差0.5为阈值进行筛选,得到以下数据集

lasso回归筛选变量多_python_02


这些特征的并不一定是不重要的。接下来我们看看,这13个特征的重要性排序,来验证一下,刚才被删除的3个特征是不是应该被删除。我们这里使用树模型的feature_importances_属性,对特征进行排序。

model = XGBRegressor() 
model.fit(X,y)   
fea_imp = pd.DataFrame(zip(X.columns,model.feature_importances_),columns=["feature","importances_"])      
fea_imp.sort_values(by="importances_",ascending=False)

得到重要性的排序结果为:

lasso回归筛选变量多_sklearn_03


可见,重要性排名第二的RM特征被方差过滤过滤掉了,所以使用方差过滤法的筛选效果未必令人满意。

下面对选择前后的特征进行简单的线性回归,对回归效果进行对比:

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
x_train, x_test, y_train, y_test = train_test_split(X,y, random_state=22)
estimator = LinearRegression()
estimator.fit(x_train, y_train)
y_predict = estimator.predict(x_test)
score1=estimator.score(x_test,y_test)
print("未方差过滤的置信度:"+str(score1))
xvt_train, xvt_test, yvt_train, yvt_test = train_test_split(X_vt,y, random_state=22)
estimatorvt = LinearRegression()
estimatorvt.fit(xvt_train, yvt_train)
yvt_predict = estimatorvt.predict(xvt_test)
score2=estimatorvt.score(xvt_test,yvt_test)
print("方差过滤的置信度:"+str(score2))

未方差过滤的置信度:0.7541207651940612
方差过滤的置信度:0.6914420587948746

方差过滤未必会令结果提升

卡方过滤

计算每个非负特征和标签之间的卡方统计量,选择顺序排在前k得特征。卡方值越大,二者之间的相关性越大,相互影响越明显。

  • 说明:
    针对离散型标签(分类)问题;
    所有特征必须非负,数据先归一化或标准化。
  • 原理:
    计算样本的实际值和理论值,然后进行计算卡方值。将得到的卡方值与对应自由度下的临界值进行对比,当卡方值大于临界值的时候,不成立,反之成立。

下面对sklearn自带的鸢尾花数据集进行分类,由于鸢尾花数据集相对简单,在卡方过滤保存两个特征的时候,仍然与保存四个特征时的分类效果相同,所以这里仅对运行时间进行区分。

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.feature_selection import SelectKBest
from time import time

分别对未处理数据与卡方过滤后的数据进行训练集测试集的划分

data =load_iris() 
x_feature = data.data #特征数据
y_target =data.target #分类数据
x_train, x_test, y_train, y_test = train_test_split(x_feature,y_target, test_size=0.33,random_state=21)#保证random_state不变,先对未处理数据划分训练集测试集
x_new=SelectKBest(chi2, k=2).fit_transform(x_feature, y_target)
xnew_train, xnew_test, ynew_train, ynew_test = train_test_split(x_new,y_target, test_size=0.33,random_state=21)#对经过卡方过滤后的两项划分训练集测试集

对时间以及划分效果进行判断

t0=time()
dt_model = LogisticRegression() # 所以参数均置为默认状态
dt_model.fit(x_train,y_train) # 使用训练集训练模型|
predict_results = dt_model.predict(x_test) # 使用模型对测试集进行预测
scores = dt_model.score(x_test, y_test)
print("未进行卡方过滤:"+str(scores))
print("未进行卡方过滤时间:"+str(time()-t0))
t1=time()
dtnew_model = LogisticRegression() # 所以参数均置为默认状态
dtnew_model.fit(xnew_train,ynew_train) # 使用训练集训练模型|
predict_results = dtnew_model.predict(xnew_test) # 使用模型对测试集进行预测
scores = dtnew_model.score(xnew_test, ynew_test)
print("进行卡方过滤:"+str(scores))
print("进行卡方过滤时间:"+str(time()-t1))

最终得到的结果为

未进行卡方过滤:0.94
未进行卡方过滤时间:0.04879355430603027
进行卡方过滤:0.94
进行卡方过滤时间:0.012000083923339844

可见,仅在鸢尾花数据集中,经过卡方过滤的数据,在保存2个特征的前提下,准确度仍未发生下降,并且训练速度得到了很大提高。

p.s. 运行时两个模型的顺序好像会影响时间,但大体仍能反映出降维处理对于模型运行时间的提升。

F检验

F检验,是用来捕捉每一个特征与标签之间的线性关系的过滤方法。它便可以作回归也能够作分类,所以包含feature_selection.f_classif(F检验分类) 和 feature_selection.f_regression(F检验回归)两个类。其中F检验分类用于标签是离散型变量的数据,而F检验回归用于标签是连续型变量的数据。
和卡方检验同样,这两个类须要和类SelectKBest连用,而且咱们也能够直接经过输出的统计量来判断咱们到底要设置一个什么样的K。须要注意的是,F检验在数据服从正态分布时效果会很是稳定,所以若是使用F检验过滤,需要先将数据转换成服从正态分布的方式。
以波士顿房价预测模型为例,查看一下各个特征与目标之间的关系

from sklearn.datasets import load_boston
import pandas as pd
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import f_regressionr
bc = load_boston()  
X = pd.DataFrame(bc.data)
X.columns = bc.feature_names
y = bc.target
f,p=f_regression(X,y)

和卡方过滤一样,希望选取p值小于0.05或0.01的特征,这些特征与标签时显著线性相关的,而p值大于0.05或0.01的特征则被我们认为是和标签没有显著线性关系的特征,应该被删除。这个操作,和卡方检验一样,需要和类SelectKBest连用。
很可惜,波士顿房价模型中的所有特征与目标的关系都不错,都删不了
下面是每个特征与目标之间关系的p值的大小

[1.17398708e-19, 5.71358415e-17, 4.90025998e-31, 7.39062317e-05,
7.06504159e-24, 2.48722887e-74, 1.56998221e-18, 1.20661173e-08,
5.46593257e-19, 5.63773363e-29, 1.60950948e-34, 1.31811273e-14,
5.08110339e-88]

互信息法

互信息法是用来捕捉每一个特征与标签之间的任意关系(包括线性和非线性关系)的过滤方法。和F检验类似,它既能够作回归也能够作分类,而且包含两个类feature_selection.mutual_info_classif(互信息分类)和feature_selection.mutual_info_regression(互信息回归)。这两个类的用法和参数都和F检验如出一辙,不过互信息法比F检验更增强大,F检验只可以找出线性关系,而互信息法能够找出任意关系。
互信息法不返回p值或F值相似的统计量,它返回“每一个特征与目标之间的互信息量的估计”,这个估计量在[0,1]之间取值,为0则表示两个变量独立,为1则表示两个变量彻底相关。
再以波士顿房价预测模型为例,查看一下各个特征与目标之间的关系

from sklearn.datasets import load_boston
import pandas as pd
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import mutual_info_regression as MIC
bc = load_boston()  
X = pd.DataFrame(bc.data)
X.columns = bc.feature_names
y = bc.target
mi=MIC(X,y)

最后得到的结果为:

[0.34260446, 0.19145172, 0.46498486, 0.02742458, 0.4635668 ,
0.52793539, 0.31570697, 0.29637422, 0.22343913, 0.36396275,
0.44956186, 0.16002988, 0.6685777 ]