python具有可扩展的特性_扩展特征的三个方法《Python机器学习》之十五

1533697538236783939b23a

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

数据表示的最佳方法不仅取决于数据的语义,还取决于所使用的模型种类。线性模型与基于树的模型(比如决策树、梯度提升树和随机森林)是两种成员很多同时又非常常用的模型,它们在处理不同的特征表示时就具有非常不同的性质。我们回到第2 章用过的wave 回归数据集。它只有一个输入特征。下面是线性回归模型与决策树回归在这个数据集上的对比(见图4-1):

1533695406174c213566a10

153369522967373b17c3961

正如你所知,线性模型只能对线性关系建模,对于单个特征的情况就是直线。决策树可以构建更为复杂的数据模型,但这强烈依赖于数据表示。有一种方法可以让线性模型在连续数据上变得更加强大,就是使用特征分箱(binning,也叫离散化,即discretization)将其划分为多个特征,如下所述。

我们假设将特征的输入范围(在这个例子中是从-3 到3)划分成固定个数的箱子(bin),比如10 个,那么数据点就可以用它所在的箱子来表示。为了确定这一点,我们首先需要定义箱子。在这个例子中,我们在-3 和3 之间定义10 个均匀分布的箱子。我们用np.linspace 函数创建11 个元素,从而创建10 个箱子,即两个连续边界之间的空间:

15336954684925e58a4afb3

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

1533695558923e2a9b34475

15336955803375c2b8e794f

153369522972438eab3ea61

虚线和实线完全重合,说明线性回归模型和决策树做出了完全相同的预测。对于每个箱子,二者都预测一个常数值。因为每个箱子内的特征是不变的,所以对于一个箱子内的所有点,任何模型都会预测相同的值。比较对特征进行分箱前后模型学到的内容,我们发现,线性模型变得更加灵活了,因为现在它对每个箱子具有不同的取值,而决策树模型的灵活性降低了。分箱特征对基于树的模型通常不会产生更好的效果,因为这种模型可以学习在任何位置划分数据。从某种意义上来看,决策树可以学习如何分箱对预测这些数据最为有用。此外,决策树可以同时查看多个特征,而分箱通常针对的是单个特征。不过,线性模型的表现力在数据变换后得到了极大的提高。

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

交互特征与多项式特征

想要丰富特征表示,特别是对于线性模型而言,另一种方法是添加原始数据的交互特征(interaction feature)和多项式特征(polynomial feature)。这种特征工程通常用于统计建模,但也常用于许多实际的机器学习应用中。

作为第一个例子,我们再看一次图4-2。线性模型对wave 数据集中的每个箱子都学到一个常数值。但我们知道,线性模型不仅可以学习偏移,还可以学习斜率。想要向分箱数据上的线性模型添加斜率,一种方法是重新加入原始特征(图中的x 轴)。这样会得到11 维的数据集,如图4-3 所示。

15336956459352a6a96af8f

1533695229696776ceb9f88

在这个例子中,模型在每个箱子中都学到一个偏移,还学到一个斜率。学到的斜率是向下的,并且在所有箱子中都相同——只有一个x 轴特征,也就只有一个斜率。因为斜率在所有箱子中是相同的,所以它似乎不是很有用。我们更希望每个箱子都有一个不同的斜率!为了实现这一点,我们可以添加交互特征或乘积特征,用来表示数据点所在的箱子以及数据点在x 轴上的位置。这个特征是箱子指示符与原始特征的乘积。我们来创建数据集:

x_product = np.hstack([x_binned,x*x_binned])

print(x_product.shape)

(100, 20)

这个数据集现在有20 个特征:数据点所在箱子的指示符与原始特征和箱子指示符的乘积。你可以将乘积特征看作每个箱子x 轴特征的单独副本。它在箱子内等于原始特征,在其他位置等于零。图4-4 给出了线性模型在这种新表示上的结果:

1533695761914ad68707523

153369522976384e9132d9b

如你所见,现在这个模型中每个箱子都有自己的偏移和斜率。使用分箱是扩展连续特征的一种方法。另一种方法是使用原始特征的多项式(polynomial)。对于给定特征x,我们可以考虑x ** 2、x ** 3、x ** 4,等等。这在preprocessing 模块的PolynomialFeatures 中实现:

1533695834130258cf9e062

15336958476550ac998f0e9

你可以看到,x_poly 的第一列与x 完全对应,而其他列则是第一列的幂。有趣的是,你可以发现有些值非常大。第二行有大于20 000 的元素,数量级与其他行都不相同。将多项式特征与线性回归模型一起使用,可以得到经典的多项式回归(polynomialregression)模型(见图4-5):

1533695896919a589a2e827

1533695229887ec67744baf

如你所见,多项式特征在这个一维数据上得到了非常平滑的拟合。但高次多项式在边界上或数据很少的区域可能有极端的表现。

作为对比,下面是在原始数据上学到的核SVM 模型,没有做任何变换(见图4-6):

15336959420164597b90fba

1533695234473932b65c5f7

使用更加复杂的模型(即核SVM),我们能够学到一个与多项式回归的复杂度类似的预测结果,且不需要进行显式的特征变换。

我们再次观察波士顿房价数据集,作为对交互特征和多项式特征更加实际的应用。我们在第2 章已经在这个数据集上使用过多项式特征了。现在来看一下这些特征的构造方式,以及多项式特征的帮助有多大。首先加载数据,然后利用MinMaxScaler 将其缩放到0 和1之间:

1533696052916884d944863

原始数据有13 个特征,现在被扩展到105 个交互特征。这些新特征表示两个不同的原始特征之间所有可能的交互项,以及每个原始特征的平方。这里degree=2 的意思是,我们需要由最多两个原始特征的乘积组成的所有特征。利用get_feature_names 方法可以得到输入特征和输出特征之间的确切对应关系:

print("Polynomial feature names:\n{}".format(poly.get_feature_names()))

Polynomial feature names:

['1', 'x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9', 'x10', 'x11', 'x12', 'x0^2', 'x0 x1', 'x0 x2', 'x0 x3', 'x0 x4', 'x0 x5', 'x0 x6', 'x0 x7', 'x0 x8', 'x0 x9', 'x0 x10', 'x0 x11', 'x0 x12', 'x1^2', 'x1 x2', 'x1 x3', 'x1 x4', 'x1 x5', 'x1 x6', 'x1 x7', 'x1 x8', 'x1 x9', 'x1 x10', 'x1 x11', 'x1 x12', 'x2^2', 'x2 x3', 'x2 x4', 'x2 x5', 'x2 x6', 'x2 x7', 'x2 x8', 'x2 x9', 'x2 x10', 'x2 x11', 'x2 x12', 'x3^2', 'x3 x4', 'x3 x5', 'x3 x6', 'x3 x7', 'x3 x8', 'x3 x9', 'x3 x10', 'x3 x11', 'x3 x12', 'x4^2', 'x4 x5', 'x4 x6', 'x4 x7', 'x4 x8', 'x4 x9', 'x4 x10', 'x4 x11', 'x4 x12', 'x5^2', 'x5 x6', 'x5 x7', 'x5 x8', 'x5 x9', 'x5 x10', 'x5 x11', 'x5 x12', 'x6^2', 'x6 x7', 'x6 x8', 'x6 x9', 'x6 x10', 'x6 x11', 'x6 x12', 'x7^2', 'x7 x8', 'x7 x9', 'x7 x10', 'x7 x11', 'x7 x12', 'x8^2', 'x8 x9', 'x8 x10', 'x8 x11', 'x8 x12', 'x9^2', 'x9 x10', 'x9 x11', 'x9 x12', 'x10^2', 'x10 x11', 'x10 x12', 'x11^2', 'x11 x12', 'x12^2']

第一个新特征是常数特征,这里的名称是"1"。接下来的13 个特征是原始特征(名称从"x0" 到"x12")。然后是第一个特征的平方("x0^2")以及它与其他特征的组合。我们对Ridge 在有交互特征的数据上和没有交互特征的数据上的性能进行对比:

1533696121322c7528820a7

显然,在使用Ridge 时,交互特征和多项式特征对性能有很大的提升。但如果使用更加复杂的模型(比如随机森林),情况会稍有不同:

1533696153375c7d75733a9

你可以看到,即使没有额外的特征,随机森林的性能也要优于Ridge。添加交互特征和多项式特征实际上会略微降低其性能。

单变量非线性变换

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

大部分模型都在每个特征(在回归问题中还包括目标值)大致遵循高斯分布时表现最好,也就是说,每个特征的直方图应该具有类似于熟悉的“钟形曲线”的形状。使用诸如log和exp 之类的变换并不稀奇,但却是实现这一点的简单又有效的方法。在一种特别常见的情况下,这样的变换非常有用,就是处理整数计数数据时。计数数据是指类似“用户A 多长时间登录一次?”这样的特征。计数不可能取负值,并且通常遵循特定的统计模式。下面我们使用一个模拟的计数数据集,其性质与在自然状态下能找到的数据集类似。特征全都是整数值,而响应是连续的:

15336972268743deeef1faf

数字2 似乎是最常见的,共出现了68 次(bincount 始终从0 开始),更大数字的出现次数快速下降。但也有一些很大的数字,比如134 出现了2 次。我们在图4-7 中将计数可视化。

1533697258170043a48eec5

15336952298501f0402c99d

特征X[:, 1] 和X[:, 2] 具有类似的性质。这种类型的数值分布(许多较小的值和一些非常大的值)在实践中非常常见。 但大多数线性模型无法很好地处理这种数据。我们尝试拟合一个岭回归模型:

1533697284661e4fa2a3917

你可以从相对较小的R^2 分数中看出,Ridge 无法真正捕捉到x 和y 之间的关系。不过应用对数变换可能有用。由于数据取值中包括0(对数在0 处没有定义),所以我们不能直接应用log,而是要计算log(x + 1):

1533697308064146ba8f054

153369522996844f31be4f1

变换之后,数据分布的不对称性变小,也不再有非常大的异常值(见图4-8),

在新数据上构建一个岭回归模型,可以得到更好的拟合:

1533697336254cb745f9dcf

由于篇幅所限,特征工程的其他内容另文发表。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值