常用公开数据集:
为让原始数据可用于机器学习算法,需要先对其进行清理,并可能需要将其进行各种转换,之后才能从转换后的数据里提取有用的特征。数据的转换和特征提取联系紧密。某些情况下,一些转换本身便是特征提取的过程。一般来说,现实中的数据会存在信息不规整、数据点缺失和异常值问题。理想情况下,我们会修复非规整数据。大致的处理方法如下:
特征(feature) 指那些用于模型训练的变量。特征可以概括地分为如下几种:
原始的数值和一个数值特征之间的区别是什么?实际上,任何数值数据都能作为输入变量。但是,机器学习模型中所学习的是各个特征所对应的向量的权值。这些权值在特征值到输出或是目标变量(指在监督学习模型中)的映射过程中扮演重要角色。由此我们会想使用那些合理的特征,让模型能从这些特征学到特征值和目标变量之间的关系。比如年龄就是一个合理的特征。年龄的增加和某项支出之间可能就存在直接关系。
当数值特征仍处于原始形式时,其可用性相对较低,但可以转化为更有用的表示形式。位置信息便是如此。若使用原始位置信息(比如用经纬度表示的),我们的模型可能学习不到该信息和某个输出之间的有用关系,这就使得该信息的可用性不高。然而若对位置进行聚合或挑选后(比如聚焦为一个城市或国家),便容易和特定输出之间存在某种关联了。
当类别特征仍为原始形式时,其取值来自所有可能取值所构成的集合而不是一个数字,故不能作为输入。
名义(nominal)变量,其各个可能取值之间没有顺序关系(比如职业)
有序(ordinal)变量,其值存在顺序关系(比如评级)
将类别特征表示为数字形式,常可借助k之1(1-of-k)方法进行。将名义变量表示为可用于机器学习任务的形式,会需要借助如k之1编码这样的方法。有序变量的原始值可能就能直接使用,但也常会经过和名义变量一样的编码处理。
假设变量可取的值有k个。如果对这些值用1到k编序,则可以用长度为k的二元向量来表示一个变量的取值。在这个向量里,该取值对应的序号所在的元素为1,其他元素都为0。
从现有的一个或多个变量派生出新的特征常常是有帮助的。理想情况下,派生出的特征能比原始属性带来更多信息。从原始数据派生特征的例子包括计算平均值、中位值、方差、和、差、最大值或最小值以及计数。数值特征到类别特征的转换也很常见,比如划分为区间特征。进行这类转换的变量常见的有年龄、地理位置和时间。
def assign_tod(hr):
times_of_day = {
'morning' : range(7, 12),
'lunch' : range(12, 14),
'afternoon' : range(14, 18),
'evening' : range(18, 23),
'night' : range(23, 7)
}
for k, v in times_of_day.iteritems():
if hr in v:
return k
timestamps = rating_data.map(lambda fields: int(fields[3]))
hour_of_day = timestamps.map(lambda ts: extract_datetime(ts).hour)
time_of_day = hour_of_day.map(lambda hr: assign_tod(hr))
time_of_day.take(5)
从某种意义上说,文本特征也是一种类别特征或派生特征。文本的处理方式有很多种。自然语言处理便是专注于文本内容的处理、表示和建模的一个领域。
词袋法将一段文本视为由其中的文本或数字组成的集合,其处理过程如下:
# 下面用简单空白分词法将标题分词为词
title_terms = movie_titles.map(lambda t: t.split(" "))
# 下面取回所有可能的词,以便构建一个词到序号的映射字典
all_terms = title_terms.flatMap(lambda x: x).distinct().collect()
idx = 0
all_terms_dict = {}
for term in all_terms:
all_terms_dict[term] = idx
idx +=1
#以下代码达到上面相同效果
all_terms_dict2 = title_terms.flatMap(lambda x: x).distinct().zipWithIndex().collectAsMap()
# 该函数输入一个词列表,
# 并用k之1编码类似的方式将其编码为一个scipy稀疏向量
def create_vector(terms, term_dict):
from scipy import sparse as sp
num_terms = len(term_dict)
x = sp.csc_matrix((1, num_terms))
for t in terms:
if t in term_dict:
idx = term_dict[t]
x[0, idx] = 1
return x
#代码中用Spark的broadcast函数来创建了一个包含词字典的广播变量。
#现实场景中该字典可能会极大,故适合使用广播变量。
all_terms_bcast = sc.broadcast(all_terms_dict)
term_vectors = title_terms.map(lambda terms: create_vector(terms, all_terms_bcast.value))
在将特征提取为向量形式后,一种常见的预处理方式是将数值数据正则化(normalization)。其背后的思想是将各个数值特征进行转换,以将它们的值域规范到一个标准区间内。正则化的方法有如下几种:
#向量正则化可通过numpy的norm函数来实现
np.random.seed(42)
x = np.random.randn(10)
norm_x_2 = np.linalg.norm(x)
normalized_x = x / norm_x_2
#Spark在其MLlib机器学习库中内置了一些函数用于特征的缩放和标准化。
#它们包括供标准正态变换的StandardScaler,
#以及提供与上述相同的特征向量正则化的Normalizer。
from pyspark.mllib.feature import Normalizer
normalizer = Normalizer()
vector = sc.parallelize([x])
normalized_x_mllib = normalizer.transform(vector).first().toArray()
我们可以通过开发的软件包,借助其中完善的工具箱来实现特征的处理和提取,以及向量表示。特征提取可借助的软件包有scikit-learn、gensim、 scikit-image、 matplotlib、 Python的NLTK、 Java编写的OpenNLP以及用Scala编写的Breeze和Chalk。