机器学习(三)之数据表示和特征工程:One-Hot编码、分箱处理、交互特征、多项式特征、单变量非线性变换、自动化特征选择

0 本文简介

在数据处理时常常见到连续特征(continuous feature)和离散特征(discrete feature)。连续特征是我们最常见的数据,也是容易处理的一类数据。离散特征也叫分类特征(categorical feature),通常并不是数值。在利用前面提到的数学模型时,离散特征往往不能直接用于模型中,需要找到合适的表示方式。对于某个特定应用来说,如何找到最佳数据表示,这个问题被称为特征工程(feature engineering)
相关笔记:
机器学习(一)之 2万多字的监督学习模型总结V1.0:K近邻、线性回归、岭回归、朴素贝叶斯模型、决策树、随机森林、梯度提升回归树、SVM、神经网络
机器学习(二)之无监督学习:数据变换、聚类分析
机器学习(三)之数据表示和特征工程:One-Hot编码、分箱处理、交互特征、多项式特征、单变量非线性变换、自动化特征选择
机器学习(四)之参数选择:交叉验证、网格搜索
机器学习(五)之评价指标:二分类指标、多分类指标、混淆矩阵、不确定性、ROC曲线、AUC、回归指标

1 分类变量

1.1 One-Hot编码

使用参考教材上的例子,该例子是一个分类任务,预测一名工人的收入是高于50000美元还是低于50000 美元。如下表所示,数据集包括连续特征和离散特征。

ageworkclasseducationgenderhours-per-weekoccupationincome
039State-govBachelorsMale40Adm-clerical<=50K
150Self-emp-not-incBachelorsMale13Exec-managerial<=50K
238PrivateHS-gradMale40Handlers-cleaners<=50K
353Private11thMale40Handlers-cleaners<=50K
428PrivateBachelorsFemale40Prof-specialty<=50K

如果直接使用Logistic 回归是不行的,我们需要换一种方式来表示数据。

表示分类变量最常用的方法就是one-hot编码(one-hot-encoding)或N取一编码(one-out-of-N encoding),也叫虚拟变量(dummy variable)。虚拟变量背后的思想是将一个分类变量替换为一个或多个新特征,新特征取值0和1。

如workclass特征有4中可能的取值,我们用这4个取值分别创建4个特征,同时删除workclass这一特征,如果一个人的workclass 取某个值,那么对应的特征取值为1,其他特征均取值为0。对每个数据点来说,4个新特征中只有一个的取值1,这就是one-hot编码或N取一编码。

如下表所示,利用one-hot编码来编码workclass特征。

workclassGovernment EmployeePrivate EmployeeSelf EmployedSelf Employed Incorporated
Government Employee1000
Private Employee0100
Self Employed0010
Self Employed Incorporated0001

这里的one-hot 编码与统计学中使用的虚拟编码(dummy encoding)非常相似,但并不完全相同。在统计学中,通常将具有k 个可能取值的分类特征编码为k-1 个特征(都等于零表示最后一个可能取值)。这么做是为了简化分析,更专业的说法是,这可以避免使数据矩阵秩亏

使用pandas和scikit-learn都可以实现one-hot编码,pandas要简单一些。要注意在编解码之前,需要检查字符串编码的分类数据。例如,有人可能将性别填为“male”(男性),有人可能填为“man”,而我们希望能用同一个类别来表示这两种输入。检查列的内容有一个好方法,就是使用pandas Series(Series 是DataFrame 中单列对应的数据类型)的value_counts 函数,以显示唯一值及其出现次数。然后我们可以将变换后的数据转化为NumPy数组,最后使用scikit-learn进行分类。

1.2 数字可以编码分类变量

在上面的例子中,分类变量被编码为字符串。一方面,可能会有拼写错误;但另一方面,它明确地将一个变量标记为分类变量。但是无论是为了便于存储还是因为数据的收集方式,分类变量通常被编码为整数。

例如,workclass 的回答被记录为0(在第一个框打勾)、1(在第二个框打勾)、2(在第三个框打勾),等等。现在该列包含数字0 到8,而不是像"Private" 这样的字符串。但是这样不容易看出来是连续变量还是离散变量。

分类特征通常用整数进行编码。它们是数字并不意味着它们必须被视为连续特征。一个整数特征应该被视为连续的还是离散的,有时并不明确。如果在被编码的语义之间没有顺序关系(比如workclass 的例子),那么特征必须被视为离散特征。对于其他情况(比如五星评分),哪种
编码更好取决于具体的任务和数据,以及使用哪种机器学习算法。

pandas 的get_dummies 函数将所有数字看作是连续的,不会为其创建虚拟变量。我们可以使用scikit-learn 的OneHotEncoder,指定哪些变量是连续的、哪些变量是离散的,也可以将数据框中的数值列转换为字符串。

2 分箱、离散化、线性模型与树

数据表示的最佳方法不仅取决于数据的语义,还取决于所使用的模型种类。线性模型与基于树的模型(比如决策树、梯度提升树和随机森林)是两种成员很多同时又非常常用的模型,它们在处理不同的特征表示时就具有非常不同的性质。
在这里插入图片描述
如上图所示,线性模型只能对线性关系建模,对于单个特征的情况就是直线。决策树可以构建更为复杂的数据模型,但这强烈依赖于数据表示。有一种方法可以让线性模型在连续数据上变得更加强大,就是使用特征分箱(binning),也叫离散化(discretization),将其划分为多个特征。

我们首先定义箱子。在这个例子中,我们在-3 和3 之间定义10 个均匀分布的箱子。我们用np.linspace 函数创建11 个元素,从而创建10 个箱子,即两个连续边界之间的空间。

接下来,我们记录每个数据点所属的箱子。这可以用np.digitize 函数轻松计算出来。

我们在这里做的是将数据集中单个连续输入特征变换为一个分类特征,用于表示数据点所在的箱子。要想在这个数据上使用scikit-learn 模型,我们利用preprocessing 模块的OneHotEncoder 将这个离散特征变换为one-hot 编码。OneHotEncoder 实现的编码与pandas.get_dummies 相同,但目前它只适用于值为整数的分类变量。

然后,我们在one-hot 编码后的数据上构建新的线性模型和新的决策树模型。结果见下图,箱子的边界由黑色虚线表示。

在这里插入图片描述
虚线和实线完全重合,说明线性回归模型和决策树做出了完全相同的预测。对于每个箱子,二者都预测一个常数值。因为每个箱子内的特征是不变的,所以对于一个箱子内的所有点,任何模型都会预测相同的值。

比较对特征进行分箱前后模型学到的内容,我们发现,线性模型变得更加灵活了,因为现在它对每个箱子具有不同的取值,而决策树模型的灵活性降低了。分箱特征对基于树的模型通常不会产生更好的效果,因为这种模型可以学习在任何位置划分数据。从某种意义上来看,决策树可以学习如何分箱对预测这些数据最为有用。此外,决策树可以同时查看多个特征,而分箱通常针对的是单个特征。

对于特定的数据集,如果有充分的理由使用线性模型——比如数据集很大、维度很高,但有些特征与输出的关系是非线性的——那么分箱是提高建模能力的好方法。

3 交互特征与多项式特征

想要丰富特征表示,另一种方法是添加原始数据的交互特征(interaction feature)和多项式特征(polynomial feature)。

3.1 交互特征

线性模型不仅可以学习偏移,还可以学习斜率。想要向分箱数据上的线性模型添加斜率,一种方法是重新加入原始特征(图中的x 轴)。结果如下图所示(在程序下面)

这里对程序做一个注释:

X_combined = np.hstack([X, X_binned])
print("X前五个\n",X[:5])
print("\nX_binned前五个:\n",X_binned[:5])
print("\nX_combined前五个:\n",X_combined[:5])
print(X_combined.shape)

#### 结果 ####
X前五个
 [[-0.75275929]
 [ 2.70428584]
 [ 1.39196365]
 [ 0.59195091]
 [-2.06388816]]

X_binned前五个:
 [[0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
 [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
 [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
 [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]]

X_combined前五个:
 [[-0.75275929  0.          0.          0.          1.          0.
   0.          0.          0.          0.          0.        ]
 [ 2.70428584  0.          0.          0.          0.          0.
   0.          0.          0.          0.          1.        ]
 [ 1.39196365  0.          0.          0.          0.          0.
   0.          0.          1.          0.          0.        ]
 [ 0.59195091  0.          0.          0.          0.          0.
   1.          0.          0.          0.          0.        ]
 [-2.06388816  0.          1.          0.          0.          0.
   0.          0.          0.          0.          0.        ]]
(120, 11)
# 训练模型
reg = LinearRegression().fit(X_combined, y)

line_combined = np.hstack([line, line_binned])# 水平方向组合
plt.plot(line, reg.predict(line_combined), label='linear regression combined')

for bin in bins:
 plt.plot([bin, bin], [-3, 3], ':', c='k')

plt.legend(loc="best")
plt.ylabel("Regression output")
plt.xlabel("Input feature")
plt.plot(X[:, 0], y, 'o', c='k')

结果:
在这里插入图片描述
模型在每个箱子中都学到一个偏移,还学到一个斜率。在所有箱子中都相同——只有一个x 轴特征,也就只有一个斜率。我们更希望每个箱子都有一个不同的斜率!为了实现这一点,我们可以添加交互特征或乘积特征,用来表示数据点所在的箱子以及数据点在x 轴上的位置。这个特征是箱子指示符与原始特征的乘积

创建数据集后,这个数据集现在有20 个特征:数据点所在箱子的指示符与原始特征和箱子指示符的乘积。可以将乘积特征看作每个箱子x 轴特征的单独副本。它在箱子内等于原始特征,在其他位置等于零。

同样的这里给出程序的解释:

XX_binned = X * X_binned # 对应元素相乘
X_product = np.hstack([X_binned, X * X_binned])# 水平方向组合
print(X[:5])
# print(X.shape)
print(X_binned[:5])
print("XX_binned\n:",XX_binned[:5])
print(XX_binned.shape)
print(X_product[:5])
print(X_product.shape)

### 结果 ###
X前五个:
[[-0.75275929]
 [ 2.70428584]
 [ 1.39196365]
 [ 0.59195091]
 [-2.06388816]]
X_binned前五个:
[[0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
 [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
 [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
 [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]]
XX_binned前五个:
[[-0.         -0.         -0.         -0.75275929 -0.         -0.
  -0.         -0.         -0.         -0.        ]
 [ 0.          0.          0.          0.          0.          0.
   0.          0.          0.          2.70428584]
 [ 0.          0.          0.          0.          0.          0.
   0.          1.39196365  0.          0.        ]
 [ 0.          0.          0.          0.          0.          0.59195091
   0.          0.          0.          0.        ]
 [-0.         -2.06388816 -0.         -0.         -0.         -0.
  -0.         -0.         -0.         -0.        ]]

XX_binned.shape = (120, 10)

X_product前五个:
[[ 0.          0.          0.          1.          0.          0.
   0.          0.          0.          0.         -0.         -0.
  -0.         -0.75275929 -0.         -0.         -0.         -0.
  -0.         -0.        ]
 [ 0.          0.          0.          0.          0.          0.
   0.          0.          0.          1.          0.          0.
   0.          0.          0.          0.          0.          0.
   0.          2.70428584]
 [ 0.          0.          0.          0.          0.          0.
   0.          1.          0.          0.          0.          0.
   0.          0.          0.          0.          0.          1.39196365
   0.          0.        ]
 [ 0.          0.          0.          0.          0.          1.
   0.          0.          0.          0.          0.          0.
   0.          0.          0.          0.59195091  0.          0.
   0.          0.        ]
 [ 0.          1.          0.          0.          0.          0.
   0.          0.          0.          0.         -0.         -2.06388816
  -0.         -0.         -0.         -0.         -0.         -0.
  -0.         -0.        ]]

X_product.shape = (120, 20)
# 训练模型
reg = LinearRegression().fit(X_product, y)
line_product = np.hstack([line_binned, line * line_binned])
plt.plot(line, reg.predict(line_product), label='linear regression product')

for bin in bins:
 plt.plot([bin, bin], [-3,3], ':', c='k')

plt.plot(X[:, 0], y, 'o', c='k')
plt.ylabel("Regression output")
plt.xlabel("Input feature")
plt.legend(loc="best")

结果:现在这个模型中每个箱子都有自己的偏移和斜率。
在这里插入图片描述

3.2 多项式特征

扩展特征的另一种方法是使用原始特征的多项式(polynomial)。对于给定特征x,我们可以考虑x ** 2、x ** 3、x ** 4,等等。这在preprocessing 模块的PolynomialFeatures 中实现。

将多项式特征与线性回归模型一起使用,可以得到经典的多项式回归(polynomial regression)模型。对与于线性模型,多项式特征在一维数据上可能得到非常平滑的拟合。但高次多项式在边界上或数据很少的区域可能有极端的表现。使用更加复杂的模型(即核SVM),我们能够学到一个与多项式回归的复杂度类似的预测结果,且不需要进行显式的特征变换。

4 单变量非线性变换

添加特征的平方或立方可以改进线性回归模型,其他变换通常也对变换某些特征有用,特别是应用数学函数,比如log、exp 或sin。虽然基于树的模型只关注特征的顺序,但线性模型和神经网络依赖于每个特征的尺度和分布。如果在特征和目标之间存在非线性关系,那么建模就变得非常困难,特别是对于回归问题。log 和exp 函数可以帮助调节数据的相对比例,从而改进线性模型或神经网络的学习效果在处理具有周期性模式的数据时,sin 和cos 函数非常有用。

大部分模型都在每个特征(在回归问题中还包括目标值)大致遵循高斯分布时表现最好,也就是说,每个特征的直方图应该具有类似于熟悉的“钟形曲线”的形状。这种变换在处理整数计数数据时特别有用。

我们可以看出,分箱、多项式和交互项都对模型在给定数据集上的性能有很大影响,对于复杂度较低的模型更是这样,比如线性模型和朴素贝叶斯模型。与之相反,基于树的模型通常能够自己发现重要的交互项,大多数情况下不需要显式地变换数据。其他模型,比如SVM、最近邻和神经网络,有时可能会从使用分箱、交互项或多项式中受益,但其效果通常不如线性模型那么明显。

5 自动化特征选择

在添加新特征或处理一般的高维数据集时,最好将特征的数量减少到只包含最有用的那些特征,并删除其余特征。这样会避免过拟合,得到泛化能力更好、更简单的模型。

判断每个特征的作用有多大通常有三种基本的策略:单变量统计(univariate statistics)、基于模型的选择(model-based selection)和迭代选择(iterative selection)。这些方法都是监督方法,我们需要将数据划分为训练集和测试集,并只在训练集上拟合特征选择。

5.1 单变量统计

在单变量统计中,我们计算每个特征和目标值之间的关系是否存在统计显著性,然后选择具有最高置信度的特征,并没有使用模型。对于分类问题,这也被称为方差分析(analysis of variance,ANOVA)。这些测试的一个关键性质就是它们是单变量的(univariate),即它们只单独考虑每个特征。因此,如果一个特征只有在与另一个特征合并时才具有信息量,那么这个特征将被舍弃。单变量测试的计算速度通常很快,并且不需要构建模型。另一方面,它们完全独立于你可能想要在特征选择之后应用的模型。

想要在scikit-learn中使用单变量特征选择,需要选择一项测试——对分类问题通常是f_classif(默认值),对回归问题通常是f_regression——然后基于测试中确定的p 值来选择一种舍弃特征的方法。所有舍弃参数的方法都使用阈值来舍弃所有p 值过大的特征(意味着它们不可能与目标值相关)。计算阈值的方法各有不同,最简单的是SelectKBestSelectPercentile,前者选择固定数量的k 个特征,后者选择固定百分比的特征。在选择过程中,我们可以使用select.get_support()来查看哪些特征被选中,哪些被删除。

如果特征量太大以至于无法构建模型,或者怀疑许多特征完全没有信息量,那么单变量特征选择还是非常有用的。

5.2 基于模型的特征选择

基于模型的特征选择使用一个监督机器学习模型来判断每个特征的重要性,并且仅保留最重要的特征,使用单个模型用于特征选择的监督模型不需要与用于最终监督建模的模型相同

特征选择模型需要为每个特征提供某种重要性度量,以便用这个度量对特征进行排序。决策树和基于决策树的模型提供了feature_importances_属性,可以直接编码每个特征的重要性。线性模型系数的绝对值也可以用于表示特征重要性。L1 惩罚的线性模型学到的是稀疏系数,它只用到了特征的一个很小的子集。这可以被视为模型本身的一种特征选择形式,但也可以用作另一个模型选择特征的预处理步骤。

与单变量选择不同,基于模型的选择同时考虑所有特征,因此可以获取交互项(如果模型能够获取它们的话)。要想使用基于模型的特征选择,我们需要使用SelectFromModel 变换器。SelectFromModel 类选出重要性度量(由监督模型提供)大于给定阈值的所有特征。

5.3 迭代特征选择

在迭代特征选择中,将会构建一系列模型,每个模型都使用不同数量的特征。

两种基本方法

​ (1)开始时没有特征,然后逐个添加特征,直到满足某个终止条件.

​ (2)从所有特征开始,然后逐个删除特征,直到满足某个终止条件。

由于构建了一系列模型,所以这些方法的计算成本要比前面讨论过的方法更高。其中一种特殊方法是递归特征消除(recursive feature elimination,RFE),它从所有特征开始构建模型,并根据模型舍弃最不重要的特征,然后使用除被舍弃特征之外的所有特征来构建一个新模型,如此继续,直到仅剩下预设数量的特征。为了让这种方法能够运行,用于选择的模型需要提供某种确定特征重要性的方法,正如基于模型的选择所做的那样。

如果我们不确定何时选择使用哪些特征作为机器学习算法的输入,那么自动化特征选择可能特别有用。它还有助于减少所需要的特征数量,加快预测速度,或允许可解释性更强的模型在大多数现实情况下,使用特征选择不太可能大幅提升性能,但它仍是特征工程工具箱中一个非常有价值的工具。

  • 7
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值