特征提升之特征提取、特征筛选

特征提升

特征提升主要通过对数据特征抽取或者筛选来达到提升模型的目的。
特征提取就是逐条将原始数据转化为特征向量的形式,这个过程同时涉及对数据特征向量的向量化表示。
特征筛选就是在高维度、已量化的特征向量中选择对指定任务更有效的特征组合,进一步提升模型性能。

一、特征提取

1.1 DictVectorizer

原始数据的种类有很多种,除了数字化的信号数据(图像、声音),还有大量符号化的文本。然而我们无法将文字本身用于计算任务,需要通过某些处理手段,预先将文本量化为特征向量。
有些用符号表示的数据特征已经相对结构化,并且以字典这种数据结构存储。这时使用DictVectorizer对特征进行抽取和向量化。

## DictVectorizer对使用字典存储的数据进行特征抽取和向量化
## 从sklearn.feature_extraction 导入 DictVectorizer
from sklearn.feature_extraction import DictVectorizer
df=[{'city':'shanghai','temperature':33},{'city':'beijing','temperature':12},{'city':'guangzhou','temperature':38}]
vec=DictVectorizer() #初始化特征抽取器
print(vec.fit_transform(df).toarray()) #输出转化之后的特征矩阵
print(vec.get_feature_names_out()) #输出各个维度的特征含义

在这里插入图片描述
在特征向量化的过程中,DictVectorizer对于类别型和数值型特征的处理方式有很大差异,类别特征采用0/1二值方式进行量化,数值型特征的转化相对方便,一般只需要维持原始特征值即可。

1.2 CountVectorizer

存在一些文本数据没有使用特殊的数据结构进行存储,只是一系列字符串。处理这些数据时,比较常用的文本特征表示方法为词袋法,不考虑词语出现的顺序只是将训练文本中每个出现过的词汇单独视作一列特征。称这些不重复的词汇集合为词表,于是每条训练文本都可以在高维度的此表上映射出一个特征向量。而特征数值的计算方式有两种分别是CountVectorizer 和TfidfVectorizer。
CountVectorizer对于每一条训练文本只考虑每种词汇在该条训练文本中出现的频率。
TfidfVectorizer除了考虑每种词汇在该条训练文本中出现的频率之外,同时关注包含这个词汇的文本条数的倒数,相比之下,训练的文本数目越多TfidfVectorizer这种特征量化方式就更有优势。

#使用CountVectorizer并且不去掉停用词的条件下,对文本特征进行量化的朴素贝叶斯分类性能测试
#从sklearn.datasets里导入20类新闻文本数据
from sklearn.datasets import fetch_20newsgroups  #加载数据集
from sklearn.model_selection import train_test_split #用于分割数据集
from sklearn.feature_extraction.text import CountVectorizer  
from sklearn.naive_bayes import MultinomialNB            #导入朴素贝叶斯分类器
from sklearn.metrics import classification_report  #输出更加详细的其他评价分类性能的指标

news=fetch_20newsgroups(subset='all')
x_train,x_test,y_train,y_test = train_test_split(news.data,news.target,test_size=0.25,random_state=33)
count_vec=CountVectorizer()  #默认配置不去除停用词
x_count_train = count_vec.fit_transform(x_train) #只使用词频统计的方式将原始训练和测试文本转化为特征向量
x_count_test = count_vec.transform(x_test)

mnb_count= MultinomialNB() #分类器初始化
mnb_count.fit(x_count_train,y_train)
print('the accuary of classifying 20newsgroups using Naive Bayes:',mnb_count.score(x_count_test,y_test))
y_count_predict =mnb_count.predict(x_count_test) #将分类预测的结果存储
print(classification_report(y_test,y_count_predict,target_names=news.target_names))

在这里插入图片描述
使用CountVectorizer在不去掉停用词的条件下,在测试文本上可以得到83.98%的预测准确性。而且平均精度、召回率、F1指标分别为0.86、0.84、0.82。
接下来,利用TfidfVectorizer进行特征量化,并且评估模型性能。

from sklearn.feature_extraction.text import TfidfVectorizer #sklearn.feature_extraction.text导入TfidfVectorizer
tdidf_vec=TfidfVectorizer()
x_tdidf_train = tdidf_vec.fit_transform(x_train) #shiy
x_tdidf_test = tdidf_vec.transform(x_test)
mnb_tdidf= MultinomialNB() #分类器初始化
mnb_tdidf.fit(x_tdidf_train,y_train)
print('the accuary of classifying 20newsgroups using Naive Bayes:',mnb_tdidf.score(x_tdidf_test,y_test))
y_tdidf_predict =mnb_tdidf.predict(x_tdidf_test) #将分类预测的结果存储
print(classification_report(y_test,y_tdidf_predict,target_names=news.target_names))

在这里插入图片描述
使用TfidfVectorizer而不去掉停用词的条件下,可以得到比CountVectorizer更加高的预测准确性,即从83.98%上升到84.64%,而且平均精度、召回率、F1值都得到了提升。

分别使用CountVectorizer和TfidfVectorizer,在去掉停用词的条件下对文本特征进行向量化的朴素贝叶斯分类性能测试。

count_filter_vec,tfidf_filter_vec=CountVectorizer(analyzer='word',stop_words='english'),TfidfVectorizer(analyzer='word',stop_words='english')
x_count_filter_train=count_filter_vec.fit_transform(x_train)
x_count_filter_test=count_filter_vec.transform(x_test)

x_tfidf_filter_train=tfidf_filter_vec.fit_transform(x_train)
x_tfidf_filter_test=tfidf_filter_vec.transform(x_test)

mnb_count_filter=MultinomialNB()
mnb_count_filter.fit(x_count_filter_train,y_train)
print('the accuary of classifying 20newsgroups using Naive Bayes:',mnb_count_filter.score(x_count_filter_test,y_test))
y_count_filter_predict =mnb_count_filter.predict(x_count_filter_test) #将分类预测的结果存储
print(classification_report(y_test,y_count_filter_predict,target_names=news.target_names))

在这里插入图片描述

mnb_tfidf_filter=MultinomialNB()
mnb_tfidf_filter.fit(x_tfidf_filter_train,y_train)
print('the accuary of classifying 20newsgroups using Naive Bayes:',mnb_tfidf_filter.score(x_tfidf_filter_test,y_test))
y_tfidf_filter_predict =mnb_tfidf_filter.predict(x_tfidf_filter_test) #将分类预测的结果存储
print(classification_report(y_test,y_tfidf_filter_predict,target_names=news.target_names))

在这里插入图片描述
综上所述:TfidfVectorizer的特征抽取和量化方法更加具有优势,通过对停用词进行过滤的文本特征抽取方法,平均药比不过滤停用词的模型综合性能高出3%-4%

二、特征筛选

特征筛选(特征选择)是特征工程中的重要问题(另一个重要的问题是特征提取),通常而言,特征筛选(特征选择)是指选择获得相应模型和算法最好性能的特征集。
通常来说,从两个方面考虑来选择特征:
1、特征是否发散(低方差):如果一个特征不发散,例如方差接近于0,也就是说样本在这个特征上基本上没有差异,这个特征对于样本的区分并没有什么用。

(df.select_dtypes(include=np.number).var().astype('str'))

2、特征与目标的相关性:这点比较显见,与目标相关性高的特征,应当优先选择。
根据特征选择的形式又可以将特征选择方法分为3种:
(1)Filter:过滤法,按照发散性或者相关性对各个特征进行评分,设定值或者待选择阑值的个数,选择特征。
(2)Wrapper:包装法,根据目标函数(通常是预测效果评分),每次选择若干特征,或者排除若干特征。
(3)Embedded:嵌入法,先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据系数从大到小选择特征。类似于Filter方法,但是是通过训练来确定特征的优劣。

特征选择主要有两个功能:
1、减少特征数量、降维,使模型泛化能力更强,减少过拟合
2、增强对特征和特征值之间的理解

2.1 过滤式

主要思想:对每一维特征“打分”,即给每一维的特征赋予权重,这样的权重就代表着该特征的重要性,然后依据权重排序。先进行特征选择,然后去训练学习器,所以特征选择的过程与学习器无关。相当于先对特征进行过滤操作,然后用特征子集来训练分类器。
主要方法
(1)移除低方差的特征;
(2)相关系数排序,分别计算每个特征与输出值之间的相关系数,设定一个阈值,选择相关系数大于阑值的部分特征;
(3)利用假设检验得到特征与输出值之间的相关性,方法有比如卡方检验、t检验、F检验等。
(4)互信息,利用互信息从信息煽的角度分析相关性。(通过minepy库MINE计算)
这里,本文为大家提供一些有价值的小tricks
(1)对于数值型特征,方差很小的特征可以不要,因为太小没有什么区分度,提供不了太多的信息,对于分类特征,也是同理,取值个数高度偏斜的那种可以先去掉。
(2)根据与目标的相关性等选出比较相关的特征(当然有时候根据字段含义也可以选)
(3)卡方检验一般是检查离散变量与离散变量的相关性,当然离散变量的相关性信息增益和信息增益比也是不错的选择(可以通过决策树模型来评估来看),person系数一般是查看连续变量与连续变量的线性相关关系。

2.1.1去掉取值变化小的特征

这应该是最简单的特征选择方法了:假设某特征的特征值只有0和1,并且在所有输入样本中,95%的实例的该特征取值都是1,那就可以认为这个特征作用不大。如果100%都是1,那这个特征就没意义了。
当特征值都是离散型变量的时候这种方法才能用,如果是连续型变量,就需要将连续变量离散化之后才能用,而且实际当中,一般不太会有95%以上都取某个值的特征存在,所以这种方法虽然简单但是不太好用。
可以把它作为特征选择的预处理,先去掉那些取值变化小的特征,然后再从接下来提到的的特征选择方法中选择合适的进行进一步的特征选择。例如,我们前面的seller和offerType特征。

# 对方差的大小排序select_data.std().sort_values()    
# select_data是final_data去掉了独热的那些特征

2.1.2单变量特征选择

单变量特征选择能够对每一个特征进行测试,衡量该特征和响应变量之间的关系,根据得分扔掉不好的特征。对于回归和分类问题可以采用卡方检验等方式对特征进行测试。

这种方法比较简单,易于运行,易于理解,通常对于理解数据有较好的效果(但对特征优化、提高泛化能力来说不一定有效);这种方法有许多改进的版本、变种。

下面重点介绍一下pearson相关系数,皮尔森相关系数是一种最简单的,比较常用的方式。能帮助理解特征和响应变量之间关系的方法,该方法衡量的是变量之间的线性相关性,结果的取值区间为[-1,1],-1表示完全的负相关(这个变量下降,那个就会上升),+1表示完全的正相关,0表示没有线性相关。

Pearson Correlation速度快、易于计算,经常在拿到数据(经过清洗和特征提取之后的)之后第一时间就执行。Scipy的pearsonr方法能够同时计算相关系数和p-value,当然pandas的corr也可以计算。

corr = select_data.corr('pearson')    # .corr('spearman')
plt.figure(figsize=(25, 15))
corr['price'].sort_values(ascending=False)[1:].plot(kind='bar')
plt.tight_layout()

当然,这个数据用pearson系数可能不是那么合理,可以使用spearman系数,这个被认为是排列后的变量的pearson的相关系数,具体的可以看(Pearson)皮尔逊相关系数和spearman相关系数,这里只整理两者的区别和使用场景,区别如下:
(1)连续数据,正态分布,线性关系,用pearson相关系数是最恰当,当然用spearman相关系数也可以,效率没有pearson相关系数高。
上述任一条件不满足,就用spearman相关系数,不能用pearson相关系数。
(2)两个定序测量数据(顺序变量)之间也用spearman相关系数,不能用pearson相关系数。
(3)Pearson相关系数的一个明显缺陷是,作为特征排序机制,他只对线性关系敏感。如果关系是非线性的,即便两个变量具有一一对应的关系,Pearson相关性也可能会接近0。

2.2 包裹式

主要思想:包裹式从初始特征集合中不断的选择特征子集,训练学习器,根据学习器的性能来对子集进行评价,直到选择出最佳的子集。包裹式特征选择直接针对给定学习器进行优化。
主要方法:递归特征消除算法,基于机器学习模型的特征排序优缺点:
优点:从最终学习器的性能来看,包裹式比过滤式更好;缺点:由于特征选择过程中需要多次训练学习器,因此包裹式特征选择的计算开销通常比过滤式特征选择要大得多。

下面,这里整理基于学习模型的特征排序方法,这种方法的思路是直接使用你要用的机器学习算法,针对每个单独的特征和响应变量建立预测模型。其实Pearson相关系数等价于线性回归里的标准化回归系数。

假如某个特征和响应变量之间的关系是非线性的,可以用基于树的方法(决策树、随机森林)、或者扩展的线性模型等。基于树的方法比较易于使用,因为他们对非线性关系的建模比较好,并且不需要太多的调试。但要注意过拟合问题,因此树的深度最好不要太大,再就是运用交叉验证。

from sklearn.model_selection import cross_val_score, ShuffleSplit
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import KFold
X = select_data.iloc[:, :-1]
Y = select_data['price']
names = select_data.columns
rf = RandomForestRegressor(n_estimators=20, max_depth=4)
kfold = KFold(n_splits=5, shuffle=True, random_state=7)
scores = []
for column in X.columns:   
     print(column)    
    tempx = X[column].values.reshape(-1, 1)    
    score = cross_val_score(rf, tempx, Y, scoring="r2", cv=kfold)    
    scores.append((round(np.mean(score), 3), column))
print(sorted(scores, reverse=True))

这里对喜欢的特征排序并打分,结果如下:
在这里插入图片描述
如果你是xgboost,xgboost里面有个画特征重要性的函数,可以这样做:

# 下面再用xgboost跑一下
from xgboost import XGBRegressor
from xgboost import plot_importance
xgb = XGBRegressor()
xgb.fit(X, Y)
plt.figure(figsize=(20, 10))plot_importance(xgb)
plt.show()

在这里插入图片描述

2.3 嵌入式

在过滤式和包裹式特征选择方法中,特征选择过程与学习器训练过程有明显的分别。而嵌入式特征选择在学习器训练过程中自动地进行特征选择。嵌入式选择最常用的是L1正则化与L2正则化。在对线性回归模型加入两种正则化方法后,他们分别变成了岭回归与Lasso回归。
主要思想:在模型既定的情况下学习出对提高模型准确性最好的特征。也就是在确定模型的过程中,挑选出那些对模型的训练有重要意义的特征。
主要方法:简单易学的机器学习算法-岭回归(Ridge Regression),就是线性回归过程加入了L2正则项。
L1正则化有助于生成一个稀疏权值矩阵,进而可以用于特征选择L2正则化在拟合过程中通常都倾向于让权值尽可能小,最后构造一个所有参数都比较小的模型。因为一般认为参数值小的模型比较简单,能适应不同的数据集,也在一定程度上避免了过拟合现象。可以设想一下对于一个线性回归方程,若参数很大,那么只要数据偏移一点点,就会对结果造成很大的影响;但如果参数足够小,数据偏移得多一点也不会对结果造成什么影响,专业一点的说法是抗扰动能力强]
这里简单介绍一下怎么使用,其实和上面机器学习模型的使用方法一样,所以有时候这些方法没有必要严格的区分开:

from sklearn.linear_model import LinearRegression, Ridge,Lasso
models = [LinearRegression(), Ridge(), Lasso()]
result = dict()
for model in models:    
    model_name = str(model).split('(')[0]    
    scores = cross_val_score(model, X=train_X, y=train_y, verbose=0, cv=5, scoring='r2')    
    result[model_name] = scores

三、案例

使用Titanic数据集,通过特征筛选的方法一步步提升决策树的预测性能

import pandas as pd
from sklearn.model_selection import train_test_split #用于分割数据集
from sklearn.feature_extraction import DictVectorizer

titanic = pd.read_csv('titanic.csv')
y=titanic['Survived']
x=titanic.drop(['Name','Survived','Cabin','PassengerId'],axis=1) #'Ticket',
#对缺失数据进行填充
x['Age'].fillna(x['Age'].mean(),inplace=True)
x.fillna('UNKNOWN',inplace=True)
#分割数据,采样25%用于测试
x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.3,random_state=33)
#类别特征向量化
vec=DictVectorizer()
x_train = vec.fit_transform(x_train.to_dict(orient='record'))
x_test=vec.transform(x_test.to_dict(orient='record'))
print(len(vec.feature_names_))
#输出533

这里对titanic数据集每列的label进行解释:
PassengerId=》乘客的ID
Survived=》是否存活
Pclass=》船舱等级
Name=》乘客姓名
Sex=》乘客性别
Age=》乘客年龄
SibSp=》有无兄弟姐妹
Parch=》有无父母子女
Ticket=》登船票号
Fare=》票价
Cabin=》船舱类型
Embarked=》所到达的港口

#使用决策树模型依靠所有特征进行预测,并做出评估
from sklearn.tree import DecisionTreeClassifier
dt=DecisionTreeClassifier(criterion='entropy')
dt.fit(x_train,y_train)
dt.score(x_test,y_test)

在这里插入图片描述

#从sklearn导入特征筛选器
from sklearn import feature_selection
fs=feature_selection.SelectPercentile(feature_selection.chi2,percentile=20) #筛选前20%的特征,使用相同配置的决策树进行预测,并评估
x_train_fs =fs.fit_transform(x_train,y_train)
dt.fit(x_train_fs,y_train)
x_test_fs=fs.transform(x_test)
dt.score(x_test_fs,y_test)

在这里插入图片描述

#通过交叉验证的方法,按照固定间隔的百分比筛选特征,并做图展示性能岁特征筛选比例的变化
from sklearn.model_selection import cross_val_score
import numpy as np
percentiles=range(1,100,2)
results=[]
for i in percentiles:
    fs=feature_selection.SelectPercentile(feature_selection.chi2,percentile=i)
    x_train_fs =fs.fit_transform(x_train,y_train)
    scores=cross_val_score(dt,x_train_fs,y_train,cv=5)
    results=np.append(results,scores.mean())
print(results)

opt=np.where(results==results.max())[0]
print('optimal number of features %d' %percentiles[opt[0]])

在这里插入图片描述

import pylab as pl
pl.plot(percentiles,results)
pl.xlabel('percentiles of features')
pl.ylabel('accusry')
pl.show()

在这里插入图片描述

#使用最佳筛选后的特征,利用相同配置的模型在测试集上进行性能评估
from sklearn import feature_selection
fs=feature_selection.SelectPercentile(feature_selection.chi2,percentile=79)
x_train_fs =fs.fit_transform(x_train,y_train)
dt.fit(x_train_fs,y_train)
x_test_fs=fs.transform(x_test)
dt.score(x_test_fs,y_test)

在这里插入图片描述
综上所述:
(1)如果直接使用全部474个特征用于训练决策树模型进行分类预测,那么模型在测试集上的准确性为82.84%
(2)如果筛选前20%维度的特征,在相同的模型配置下进行预测,测试集的准确性为81.72%
(3)通过交叉验证得出的准确性有很大波动,并且最好的模型性能表现在选取前79%维度的特征时候,如果选择使用前79%维度的特征,在相同的模型配置下进行预测,测试集的准确性为85.45%

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值