特征工程之特征选择
一、为什么要做特征选择?
如果一个模型在训练数据上的表现比在测试数据上要好很多,这就表示这个模型过拟合了。过拟合是指模型的参数对于训练数据的特定观测值拟合的非常接近,而训练数据的分布于真实数据的分布并不一致,所以模型具有较高的方差。产生过拟合的原因是因为,对于训练数据集上的模型过于复杂,通常我们可以通过以下几种方式来降低过拟合:
1、收集更多的训练数据
2、通过正则化引入罚项
3、选择一个参数相对较少的简单模型
4、降低数据的维度
特征选择除了防止模型过拟合降低模型的泛化误差之外,它还可以减少硬件资源的损耗,降低模型的开发成本,减少训练的时间。下面我们会介绍通过正则化、随机森林算法进行特征选择。
二、使用L1正则化进行特征选择
常用的正则化有L1正则化和L2正则化,L1正则化和L2正则化都是为代价函数添加罚项,不同的是L1正则化增加的罚项是使用权重绝对值的和而L2正则化增加的罚项时权重的平方和。L1正则化生成的是一个稀疏的特征向量,且大多数的权值为0。如果数据集中包含了许多不相关的特征,尤其是不相关的特征数量大于样本的数量时,通过L1正则化处理之后能降低模型的复杂度。
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
if __name__ == "__main__":
data = pd.read_csv("G:/dataset/wine.csv")
#将data分为特征和类标
x,y = data.ix[:,1:],data.ix[:,0]
#对特征进行标准化处理
std = StandardScaler()
x_std = std.fit_transform(x)
#将数据集分为训练集和测试集
train_x,test_x,train_y,test_y = train_test_split(x_std,y,test_size=0.3,random_state=1)
#使用L1正则化进行特征选择
L1_model = LogisticRegression(penalty="l1",C=0.1)
L1_model.fit(train_x,train_y)
#判断模型在训练数据上的准确率
print("training accuracy:",L1_model.score(train_x,train_y))
#判断模型在测试数据上的准确率
print("test accuracy:",L1_model.score(test_x,test_y))
通过模型在训练集和测试集上的准确率可以发现,模型没有产生过拟合,因为训练集和测试集的准确率差不多。
1、查看模型的截距
#查看logistic模型的截距
print(L1_model.intercept_)
2、查看模型的权重系数
#查看模型的权重系数
print(L1_model.coef_)
因为我们使用的是多类别分类的logistic回归,所以它使用一对多(one-vs-Rest,OvR)的方法。第一个权重表示类别1相对于类别2和3的匹配结果,第二个权重表示的是类别2相对于类别1和3的匹配结果,第三个权重表示的是类别3相对于类别1和2的匹配结果。可以发现每一个权重都有13数,其中有很多项的值都为0,这是因为通过L1正则化之后产生的,这13个数表示模型在13个权重上的比重,对应不为0的特征系数,表示是选择的特征。
3、获取L1正则化所选择的列名
#获取特征的列名,因为第一列表示的酒的类别
data_cols_name = data.columns[1:]
#获取第一个特征的系数
coef1 = L1_model.coef_[0]
#获取一个特征系数的bool类型的数组,不为0为true
coef1_bool = coef1 != 0
print(data_cols_name[coef1_bool])
#获取第二个特征的系数
coef2 = L1_model.coef_[1]
coef2_bool = coef2 != 0
print(data_cols_name[coef2_bool])
#获取第三个特征的系数
coef3 = L1_model.coef_[2]
coef3_bool = coef3 != 0
print(data_cols_name[coef3_bool])
3、参数C对于L1正则化的影响
if __name__ == "__main__":
data = pd.read_csv("G:/dataset/wine.csv")
# 将data分为特征和类标
x, y = data.ix[:, 1:], data.ix[:, 0]
# 对特征进行标准化处理
std = StandardScaler()
x_std = std.fit_transform(x)
# 将数据集分为训练集和测试集
train_x, test_x, train_y, test_y = train_test_split(x_std, y, test_size=0.3, random_state=1)
fig = plt.figure()
ax = plt.figure(111)
colors = ["blue","green","red","cyan",
"magenta","yellow","black","pink",
"lightgreen","lightblue","gray","indigo",
"indigo","organge"]
weights,params = [],[]
for c in np.arange(-4,6):
model = LogisticRegression(penalty="l1",C=float(10)**c,random_state=0)
model.fit(train_x,train_y)
weights.append(model.coef_[1])
params.append(float(10)**c)
weights = np.array(weights)
for column,color in zip(range(weights.shape[1]),colors):
plt.plot(params,weights[:,column],label=data.columns[column+1],color=color)
plt.axhline(0,color="black",linestyle="--",linewidth=3)
plt.xlim([10**(-5),10**5])
plt.ylabel("weight coefficent")
plt.xlabel("C")
plt.xscale("log")
plt.legend(loc="upper left")
plt.show()
参数C是正则化参数的倒数,,当C越小的时候,所有参数的权重都接近0。通过上面的图,我们可以发现特征与参数C的变化关系。
三、使用随机森林判断特征的重要性
使用随机森林来判断特征的重要性的时候,不需要考虑特征是否是线性可分的,也不需要对特征做归一化或者标准化处理。通过随机森林算法之后,可以知道每一个特征的重要性,特征的重要性之和为1。
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
import matplotlib.pyplot as plt
import numpy as np
if __name__ == "__main__":
data = pd.read_csv("G:/dataset/wine.csv")
#将data分为特征和类标
x,y = data.ix[:,1:],data.ix[:,0]
#将数据集分为训练集和测试集
train_x,test_x,train_y,test_y = train_test_split(x,y,test_size=0.3,random_state=1)
#定义一个由10000颗决策树组成的随机森林模型
rf_model = RandomForestClassifier(n_estimators=10000,random_state=0,n_jobs=-1)
#训练
rf_model.fit(train_x,train_y)
#获取特征的重要性
importances = rf_model.feature_importances_
#对特征的重要性进行排序
indices = np.argsort(importances)[::-1]
cols_name = data.columns[1:]
for f in range(train_x.shape[1]):
print("%2d) %-*s %f" % (f + 1,30,cols_name[f],importances[indices[f]]))
plt.title("特征的重要性")
plt.bar(range(train_x.shape[1]),importances[indices],color="lightblue",align="center")
plt.xticks(range(train_x.shape[1]),cols_name,rotation=90)
plt.xlim([-1,train_x.shape[1]])
plt.show()