Spark机器学习实战(三)电影评分数据处理与特征提取

Spark机器学习实战(三)电影评分数据处理与特征提取

这部分主要讲了进行数据可视化之后如何进行必要的数据处理,原因是原始数据并非完整。随后,我们要从数据中提取出我们需要的特征。使用的数据集依然是MovieLens 100k数据集,平台为Python Spark。

文章中列出了关键代码,完整代码见我的github repository,这篇文章的代码在chapter03/movielens_feature.py

第1步:数据处理与转换

数据出现缺失或者异常时,常见的处理方法有:

  • 过滤或删除非规整或有缺失的数据
  • 填充非规整或有缺失的数据
  • 对异常值作鲁棒处理
  • 对可能的异常值进行转换

由于我们采用的数据集数据缺失问题几乎没有,因此这部分不用特别处理。

第2步:特征提取

特征主要包含以下三种:

  • 数值特征:比如年龄,可以直接作为数据的一个维度
  • 类别特征:多个类别中的一种,但是类别特征一般有多少个类就会有多少个维度
  • 文本特征:如电影评论

数值特征

数值特征也需要进行转换,因为不是所有的数值特征都有意义。

比如年龄就是一个很好的数值特征,可以不加处理直接用,因为年龄的增加与减少与目标有直接关系。然而,如经纬度的位置特征,有时就不太好直接用,需要做一些处理,甚至可以转换为类别特征。

类别特征

k类的类别特征需要转换成一个k bits的向量

我们来对MovieLens数据集中的用户职业进行处理,转换为类别特征。

all_occupations = occupation_data.distinct().collect()
all_occupations.sort()
occupation_dict = {}
for i, occu in enumerate(all_occupations):
    occupation_dict[occu] = i
user_tom_occupation = 'programmer'
tom_occupation_feature = np.zeros(len(all_occupations))
tom_occupation_feature[occupation_dict[user_tom_occupation]] = 1
print("Binary feature of tom's occupation (programmer) is:")
print(tom_occupation_feature)

结果为:

Binary feature of tom's occupation (programmer) is:
[ 0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  1.  0.  0.  0.
  0.  0.  0.]

派生特征

派生特征是指从原始数据经过一些处理后得到的特征,如之前计算过的用户打分电影总数,电影年龄等等。

下面的例子,是把u.data中的时间戳特征转换为类别特征,表征这条评分是在一天中的什么时段给出的。

rating_data = sc.textFile("%s/ml-100k/u.data" % PATH)
rating_fields = rating_data.map(lambda line: line.split('\t'))
timestamps = rating_fields.map(lambda fields: int(fields[3]))
hour_of_day = timestamps.map(lambda ts: datetime.fromtimestamp(ts).hour)
times_of_day_dict = {}
for hour in range(24):
    if hour in range(7, 12):
        times_of_day_dict[hour] = "morning"
    elif hour in range(12, 14):
        times_of_day_dict[hour] = "lunch"
    elif hour in range(14, 18):
        times_of_day_dict[hour] = "afternoon"
    elif hour in range(18, 23):
        times_of_day_dict[hour] = "evening"
    else:
        times_of_day_dict[hour] = "night"
time_of_day = hour_of_day.map(lambda hour: times_of_day_dict[hour])
print(hour_of_day.take(5))
print(time_of_day.take(5))

这段代码的运行结果为

[23, 3, 15, 13, 13]
['night', 'night', 'afternoon', 'lunch', 'lunch']

可以看到,时间戳先被转化为当天的小时点,随后转化为了时段,之后可以转化为类别特征

文本特征

理论上来说,文本特征也可以看作一个类别特征,然而文本很少出现重复,因此效果会很不理想。

下面用的是自然语言处理(NLP)常见的词袋法(bag-of-word),简而言之,词袋法就是把数据集中出现过的所有单词构成一个词典,比如说有K个单词。随后以一个K维向量表示一段文字,文字中出现过的单词记录为1,其余为0。由于大部分词不会出现,因此很适合用稀疏矩阵表示。

首先我们用正则表达式去除电影标题中括号内的年份信息,再把每个电影标题分解为单词的列表。

def extract_title(raw):
    grps = re.search("\((\w+)\)", raw)
    if grps:
        return raw[:grps.start()].strip()
    else:
        return raw
movie_data = sc.textFile("%s/ml-100k/u.item" % PATH)
movie_fields = movie_data.map(lambda line: line.split('|'))
raw_titles = movie_fields.map(lambda fields: fields[1])
print
print("Remove year information in '()'")
for raw_title in raw_titles.take(5):
    print(extract_title(raw_title))
movie_titles = raw_titles.map(extract_title)
title_terms = movie_titles.map(lambda line: line.split(' '))
print
print("Split words.")
print(title_terms.take(5))

输出为:

Remove year information in '()'
Toy Story
GoldenEye
Four Rooms
Get Shorty
Copycat

Split words.
[[u'Toy', u'Story'], [u'GoldenEye'], [u'Four', u'Rooms'], [u'Get', u'Shorty'], [u'Copycat']]

再利用flatMap RDD操作把所有出现过的单词统计出来,构建成单词辞典,形式为(单词,编号)。

all_terms = title_terms.flatMap(lambda x: x).distinct().collect()
all_terms_dict = {}
for i, term in enumerate(all_terms):
    all_terms_dict[term] = i
print
print("Total number of terms: %d" % len(all_terms_dict))

最后把标题映射成一个高维的稀疏矩阵,出现过的单词处为1。注意我们把词典all_terms_dict作为一个广播变量是因为这个变量会非常大,事先分发给每个计算节点会比较好。

from scipy import sparse as sp
def create_vector(terms, term_dict):
    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
all_terms_bcast = sc.broadcast(all_terms_dict)
term_vectors = title_terms.map(lambda 
    terms: create_vector(terms, all_terms_bcast.value))
print
print("The first five terms of converted sparse matrix of title")
print(term_vectors.take(5))

输出为:

[<1x2645 sparse matrix of type '<type 'numpy.float64'>'
    with 2 stored elements in Compressed Sparse Column format>, 
..., <1x2645 sparse matrix of type '<type 'numpy.float64'>'
    with 1 stored elements in Compressed Sparse Column format>]

正则化特征

通常我们获得的特征需要进行一下正则化处理。正则化特征分为两种:

  • 第一种为正则化某一个特征,比如对数据集中的年龄进行正则化,使它们的平均值为0,方差为1

  • 第二种为正则化特征向量,就是对某一个sample的特征进行正则化,使得它的范数为 1(常见为二阶范数为1,二阶范数是指平方和开根号)

例子是第二种,正则化特征向量。第一种方式是用numpy的函数。

np.random.seed(42)
x = np.random.randn(4)
norm_x = np.linalg.norm(x)
normalized_x = x / norm_x
print
print("x: %s" % x)
print("2-norm of x: %.4f" % norm_x)
print("normalized x: %s" % normalized_x)

输出为:

x: [ 0.49671415 -0.1382643   0.64768854  1.52302986]
2-norm of x: 1.7335
normalized x: [ 0.28654116 -0.07976099  0.37363426  0.87859535]

第二种方式是用MLlib正则化特征向量

from pyspark.mllib.feature import Normalizer
normalizer = Normalizer()
vector = sc.parallelize([x])
normalized_x_mllib = normalizer.transform(vector).first().toArray()
print("MLlib normalized x: %s" % normalized_x)

结果自然是一样的,当然是选择使用MLlib自带函数更好了。

至此,这篇文章内容就结束了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容