Spark与机器学习----数据的获取、处理与准备

1. 数据获取

常用公开数据集:

  • UCL机器学习知识库: 包括近300个不同大小和类型的数据集,可用于分类、回归、聚类和推荐系统任务。数据集列表位于: http://archive.ics.uci.edu/ml/。
  • Amazon AWS公开数据集: 包含的通常是大型数据集,可通过Amazon S3访问。相关信息可参见: http://aws.amazon.com/publicdatasets/。
  • Kaggle: 这里集合了Kaggle举行的各种机器学习竞赛所用的数据集。下载:http://www.kaggle.com/competitions。
  • KDnuggets: 这里包含一个详细的公开数据集列表,其中一些上面提到过的。该列表位于: http://www.kdnuggets.com/datasets/index.html。

2. 探索与可视化数据

载入数据,通过IPython notebook的可视化编程,来分析数据结构,统计数据分布,了解数据的缺失性,为数据的整理和清洗做前期准备工作。
可通过Matplotlib对数据生成统计图表,了解数据的分布情况。
数值型数据通过统计:数量、最大值、最小值、平均值、中值、方差等数学指标了解其特征
类别信数据通过统计:数量、类别分布等信息了解其特征。
数据探索可通过Spark中对RDD操作API函数实现。

3. 数据的处理与转换

为让原始数据可用于机器学习算法,需要先对其进行清理,并可能需要将其进行各种转换,之后才能从转换后的数据里提取有用的特征。数据的转换和特征提取联系紧密。某些情况下,一些转换本身便是特征提取的过程。一般来说,现实中的数据会存在信息不规整、数据点缺失和异常值问题。理想情况下,我们会修复非规整数据。大致的处理方法如下:

  • 过滤掉或删除非规整或有值缺失的数据: 这通常是必须的,但的确会损失这些数据里那些好的信息。
  • 填充非规整或缺失的数据: 可以根据其他的数据来填充非规整或缺失的数据。方法包括用零值、全局期望或中值来填充,或是根据相邻或类似的数据点来做插值(通常针对时序数据)等。选择正确的方式并不容易,它会因数据、应用场景和个人经验而不同。
  • 对异常值做鲁棒处理: 异常值的主要问题在于即使它们是极值也不一定就是错的。到底是对是错通常很难分辨。异常值可被移除或是填充,但的确存在某些统计技术(如鲁棒回归)可用于处理异常值或是极值。
  • 对可能的异常值进行转换: 另一种处理异常值或极值的方法是进行转换。对那些可能存在异常值或值域覆盖过大的特征,利用如对数或高斯核对其转换。这类转换有助于降低变量存在的值跳跃的影响,并将非线性关系变为线性的。

4. 数据中提取有用特征

特征(feature) 指那些用于模型训练的变量。特征可以概括地分为如下几种:

  • 数值特征( numerical feature): 这些特征通常为实数或整数,比如年龄。
  • 类别特征( categorical feature): 它们的取值只能是可能状态集合中的某一种。我们数据集中的用户性别、职业或电影类别便是这类。
  • 文本特征( text feature): 它们派生自数据中的文本内容,比如名称、描述或是评论。
  • 其他特征: 大部分其他特征都最终表示为数值。比如图像、视频和音频可被表示为数值数据的集合。地理位置则可由经纬度或地理散列(geohash)表示。

4.1. 数值特征

原始的数值和一个数值特征之间的区别是什么?实际上,任何数值数据都能作为输入变量。但是,机器学习模型中所学习的是各个特征所对应的向量的权值。这些权值在特征值到输出或是目标变量(指在监督学习模型中)的映射过程中扮演重要角色。由此我们会想使用那些合理的特征,让模型能从这些特征学到特征值和目标变量之间的关系。比如年龄就是一个合理的特征。年龄的增加和某项支出之间可能就存在直接关系。

当数值特征仍处于原始形式时,其可用性相对较低,但可以转化为更有用的表示形式。位置信息便是如此。若使用原始位置信息(比如用经纬度表示的),我们的模型可能学习不到该信息和某个输出之间的有用关系,这就使得该信息的可用性不高。然而若对位置进行聚合或挑选后(比如聚焦为一个城市或国家),便容易和特定输出之间存在某种关联了。

4.2. 类别特征

当类别特征仍为原始形式时,其取值来自所有可能取值所构成的集合而不是一个数字,故不能作为输入。
名义(nominal)变量,其各个可能取值之间没有顺序关系(比如职业)
有序(ordinal)变量,其值存在顺序关系(比如评级)
将类别特征表示为数字形式,常可借助k之1(1-of-k)方法进行。将名义变量表示为可用于机器学习任务的形式,会需要借助如k之1编码这样的方法。有序变量的原始值可能就能直接使用,但也常会经过和名义变量一样的编码处理。

假设变量可取的值有k个。如果对这些值用1到k编序,则可以用长度为k的二元向量来表示一个变量的取值。在这个向量里,该取值对应的序号所在的元素为1,其他元素都为0。

4.3. 派生特征

从现有的一个或多个变量派生出新的特征常常是有帮助的。理想情况下,派生出的特征能比原始属性带来更多信息。从原始数据派生特征的例子包括计算平均值、中位值、方差、和、差、最大值或最小值以及计数。数值特征到类别特征的转换也很常见,比如划分为区间特征。进行这类转换的变量常见的有年龄、地理位置和时间。

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)

4.4. 文本特征

从某种意义上说,文本特征也是一种类别特征或派生特征。文本的处理方式有很多种。自然语言处理便是专注于文本内容的处理、表示和建模的一个领域。
词袋法将一段文本视为由其中的文本或数字组成的集合,其处理过程如下:

  • 分词( tokenization): 首先会应用某些分词方法来将文本分隔为一个由词(一般如单词、数字等)组成的集合。
  • 删除停用词( stop words removal): 之后,它通常会删除常见的单词,比如the、 and和but(这些词被称作停用词)。
  • 提取词干( stemming): 下一步则是词干的提取。这是指将各个词简化为其基本的形式或者干词。常见的例子如复数变为单数(比如dogs变为dog等)
  • 向量化( vectorization): 最后一步就是用向量来表示处理好的词。二元向量可能是最为简单的表示方式。它用1和0来分别表示是否存在某个词。从根本上说,这与之前提到的k之1编码相同。
# 下面用简单空白分词法将标题分词为词
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))

4.5. 正则化特征

在将特征提取为向量形式后,一种常见的预处理方式是将数值数据正则化(normalization)。其背后的思想是将各个数值特征进行转换,以将它们的值域规范到一个标准区间内。正则化的方法有如下几种:

  • 正则化特征:这实际上是对数据集中的单个特征进行转换。比如减去平均值(特征对齐)或是进行标准的正则转换(以使得该特征的平均值和标准差分别为0和1)
  • 正则化特征向量:这通常是对数据中的某一行的所有特征进行转换,以让转换后的特征向量的长度标准化。也就是缩放向量中的各个特征以使得向量的范数为1(常指一阶或二阶范数)。
#向量正则化可通过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()

4.6. 用软件包提取特征

我们可以通过开发的软件包,借助其中完善的工具箱来实现特征的处理和提取,以及向量表示。特征提取可借助的软件包有scikit-learn、gensim、 scikit-image、 matplotlib、 Python的NLTK、 Java编写的OpenNLP以及用Scala编写的Breeze和Chalk。

你可能感兴趣的:(数据采集和整理)