特征工程

本节将介绍特征工程的一些常见示例:表示分类数据的特征、表示文本的特征和表示图像的特征。另外,还会介绍提高模型复杂度的衍生特征和处理缺失数据的填充方法。这个过程通常被称为向量化,因为它把任意格式的数据转换成具有良好特性的向量形式。

 

01

分类数据

一种常见的非数值数据类型是分类数据。例如,浏览房屋数据的时候,除了看到“房价”(price)和“面积”(rooms)之类的数值特征,还会有“地点”(neighborhood)信息,数据可能像这样:

data = [

{'price': 850000, 'rooms': 4, 'neighborhood': 'Queen Anne'},

{'price': 700000, 'rooms': 3, 'neighborhood': 'Fremont'},

{'price': 650000, 'rooms': 3, 'neighborhood': 'Wallingford'},

{'price': 600000, 'rooms': 2, 'neighborhood': 'Fremont'}

]

你可能会把分类特征用映射关系编码成整数:

{'Queen Anne': 1, 'Fremont': 2, 'Wallingford': 3};

在Scikit-Learn 中这么做并不是一个好办法:这个程序包的所有模块都有一个基本假设,那就是数值特征可以反映代数量(algebraic quantities)。因此,这样映射编码可能会让人觉得存在

甚至还有

这显然是没有意义的。

面对这种情况,常用的解决方法是独热编码。

它可以有效增加额外的列,让0 和1 出现在对应的列分别表示每个分类值有或无。当你的数据是像上面那样的字典列表时,用Scikit-Learn 的DictVectorizer 类就可以实现:

from sklearn.feature_extraction import DictVectorizer

vec = DictVectorizer(sparse=False, dtype=int)

vec.fit_transform(data)

neighborhood 字段转换成三列来表示三个地点标签,每一行中用1 所在的列对应一个地点。当这些分类特征编码之后,你就可以和之前一样拟合Scikit-Learn 模型了:

如果要看每一列的含义,可以用下面的代码查看特征名称

vec.get_feature_names()

如果你的分类特征有许多枚举值,那么数据集的维度就会急剧增加。然而,由于被编码的数据中有许多0,因此用稀疏矩阵表示会非常高效:

vec = DictVectorizer(sparse=True, dtype=int)
vec.fit_transform(data)

在拟合和评估模型时,Scikit-Learn 的许多(并非所有)评估器都支持稀疏矩阵输入。sklearn.preprocessing.OneHotEncoder 和sklearn.feature_extraction.FeatureHasher 是Scikit-Learn 另外两个为分类特征编码的工具。

 

02

文本特征

另一种常见的特征工程需求是将文本转换成一组数值。例如,绝大多数社交媒体数据的自动化采集,都是依靠将文本编码成数字的技术手段。数据采集最简单的编码方法之一就是单词统计。

sample = ['problem of evil',
'evil queen',
'horizon problem']

面对单词统计的数据向量化问题时,可以创建一个列来表示单词“problem”、单词“evil”和单词“horizon”等。虽然手动做也可以,但是用Scikit-Learn 的CountVectorizer 更是可以轻松实现:

from sklearn.feature_extraction.text import CountVectorizer
vec = CountVectorizer()
X = vec.fit_transform(sample)
X

结果是一个稀疏矩阵,里面记录了每个短语中每个单词的出现次数。用带列标签的DataFrame 来表示这个稀疏矩阵

import pandas as pd
pd.DataFrame(X.toarray(), columns=vec.get_feature_names())

不过这种统计方法也有一些问题:原始的单词统计会让一些常用词聚集太高的权重,在分类算法中这样并不合理。解决这个问题的方法就是通过TF–IDF(term frequency–inversedocument frequency,词频逆文档频率),通过单词在文档中出现的频率来衡量其权重

from sklearn.feature_extraction.text import TfidfVectorizer
vec = TfidfVectorizer()
X = vec.fit_transform(sample)
pd.DataFrame(X.toarray(), columns=vec.get_feature_names())

03

图像特征

机器学习还有一种常见需求,那就是对图像进行编码。我们在处理手写数字图像时使用的方法,也是最简单的图像编码方法:用像素表示图像。

但是在其他类型的任务中,这类方法可能不太合适。虽然完整地介绍图像特征的提取技术超出了本章的范围,但是你可以在Scikit-Image 项目(http://scikit-image.org)中找到许多标准方法的高品质实现。关于同时使用Scikit-Learn 和Scikit-Image 的示例,请参见应用 人脸识别管道。

 

04

衍生特征

还有一种有用的特征是输入特征经过数学变换衍生出来的新特征。

将一个线性回归转换成多项式回归时,并不是通过改变模型来实现,而是通过改变输入数据!这种处理方式有时被称为基函数回归(basis function regression)。

例如,下面的数据显然不能用一条直线描述

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

x = np.array([1, 2, 3, 4, 5])
y = np.array([4, 2, 1, 3, 7])
plt.scatter(x, y);

但是我们仍然用LinearRegression 拟合出一条直线,并获得直线的最优解:

from sklearn.linear_model import LinearRegression
X = x[:, np.newaxis]
model = LinearRegression().fit(X, y)
yfit = model.predict(X)
plt.scatter(x, y)
plt.plot(x, yfit);

很显然,我们需要用一个更复杂的模型来描述x 与y 的关系。可以对数据进行变换,并增加额外的特征来提升模型的复杂度。例如,可以在数据中增加多项式特征:

from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=3, include_bias=False)
X2 = poly.fit_transform(X)
print(X2)

在衍生特征矩阵中,第1 列表示 x;

第2列表示 x²;

第3 列表示 x³;

通过对这个扩展的输入矩阵计算线性回归,就可以获得更接近原始数据的结果了。

model = LinearRegression().fit(X2, y)
yfit = model.predict(X2)

plt.scatter(x, y)
plt.plot(x, yfit);

这种不通过改变模型,而是通过变换输入来改善模型效果的理念,正是许多更强大的机器学习方法的基础。

 

05

缺失值填充

特征工程中还有一种常见需求是处理缺失值。例如,有如下一个数据集:

from numpy import nan

X = np.array([[ nan, 0, 3 ],
[ 3, 7, 9 ],
[ 3, 5, 2 ],
[ 4, nan, 6 ],
[ 8, 8, 1 ]])
y = np.array([14, 16, -1, 8, -5])

当将一个普通的机器学习模型应用到这份数据时,首先需要用适当的值替换这些缺失数据。这个操作被称为缺失值填充,相应的策略很多,有的简单(例如用列均值替换缺失值),有的复杂(例如用矩阵填充或其他模型来处理缺失值)。

对于一般的填充方法,如均值、中位数、众数,Scikit-Learn 有Imputer 类可以实现:

from sklearn.preprocessing import Imputer

imp = Imputer(strategy='mean')
X2 = imp.fit_transform(X)
X2

结果矩阵中的两处缺失值都被所在列剩余数据的均值替代了。这个被填充的数据就可以直接放到评估器里训练了,例如LinearRegression 评估器:

model = LinearRegression().fit(X2, y)
model.predict(X2)

 

06

特征管道

如果经常需要手动应用前文介绍的任意一种方法,你很快就会感到厌倦,尤其是当你需要将多个步骤串起来使用时。例如,我们可能需要对一些数据做如下操作。

  •  用均值填充缺失值。
  •  将衍生特征转换为二次方。
  •  拟合线性回归模型。

Scikit-Learn 提供了一个管道对象,如下所示:

from sklearn.pipeline import make_pipeline

model = make_pipeline(Imputer(strategy='mean'),
PolynomialFeatures(degree=2),
LinearRegression())

这个管道看起来就像一个标准的Scikit-Learn 对象,可以对任何输入数据进行所有步骤的处理:

model.fit(X, y) # X with missing values, from above
print(y)
print(model.predict(X))

这样的话,所有的步骤都会自动完成。请注意,出于简化演示考虑,将模型应用到已经训练过的数据上,模型能够非常完美地预测结果。


搜索公众号:ai_works,获取更多机器学习资讯:

长按二维码,关注:机器学习AI工场

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值