前言:
先简单介绍一下常见的推荐算法:
Algorithm | description |
---|---|
基于关系型规则的推荐 (Association Rule) | • 消费者购买产品A, 那么他有多大机会购买产品 B • 购物车分析 啤酒和尿布) |
基于内容的推荐 (Content-based) | • 分析网页内容自动分类,再将用户自动分类 • 将新进已分类的网页推荐给感兴趣的用户 |
人口统计式的推荐 (Demographic) | • 将用户以个人属性(性别,年龄,教育背景,居住地,语言) 作为分类指标 • 以此类作为推荐的基础 |
协同过滤式的推荐 (Collaborative Filtering) | • 通过观察所有用户对产品的评分来推断用户的喜好 • 找出对产品评分相近的其他用户,他们喜欢的产品当前用户多半也会喜欢 |
本篇博客即采用协同过滤算法来实现推荐
协同过滤算法主要有下面几个优缺点:
优点 | 缺点 |
---|---|
• 可以达到个性化推荐 • 不需要内容分析 • 可以发现用户新的兴趣点 • 自动化程度高 | • 冷启动问题:如果没有历史数据就没法分析 • 新用户问题: 新用户没有评分,就不知道他的喜好 |
在实际的开发过程当中都是采用多种推荐算法协调处理的模式,这样才能实现互补,从而达到精准推荐的目的,博主这里使用python和spark来进行算法的实现,所以要预先搭建好spark环境,可以参照博主前面的文章进行配置,除了环境还要准备MovieLens的开源数据,博主这里采用的是100k的数据集,大家根据自己的机器配置进行选择;下面步入正题:
先导入一些必要的库
import pyspark
# 这个类的导入是次要的,可要可不要,主要是一个Rating类用于方便表示rating
from pyspark.mllib.recommendation import Rating
# 这个就是导入推荐算法中的交替最小二乘法库
from pyspark.mllib.recommendation import ALS
实例化一个spark的上下问对象
sc = pyspark.SparkContext(master='local[*]',appName="test1")
准备数据
从本地导入数据
raw_user_data = sc.textFile("file:/home/zh123/.jupyter/workspace/ml-100k/u.data")
查看导入的数据格式
raw_user_data.take(5)
['196\t242\t3\t881250949',
'186\t302\t3\t891717742',
'22\t377\t1\t878887116',
'244\t51\t2\t880606923',
'166\t346\t1\t886397596']
将每行数据先以"’\t"进行分片处理(只取前面三列 user, product, rating)
raw_ratings = raw_user_data.map(lambda line:line.split("\t")[:3])
查看一下分片后的数据格式
raw_ratings.take(5)
[['196', '242', '3'],
['186', '302', '3'],
['22', '377', '1'],
['244', '51', '2'],
['166', '346', '1']]
将每行数据够造为 Rating 对象 Rating(user,product,rating)
ratings_rdd = raw_ratings.map(lambda x:Rating(*x))
查看构造完成的数据格式
ratings_rdd.take(5)
[Rating(user=196, product=242, rating=3.0),
Rating(user=186, product=302, rating=3.0),
Rating(user=22, product=377, rating=1.0),
Rating(user=244, product=51, rating=2.0),
Rating(user=166, product=346, rating=1.0)]
初始化训练集中的用户个数
# 流程:投影出用户字段 -> 然后对用户字段去重 -> 统计用户的个数
user_count = ratings_rdd.map(lambda x:x.user).distinct().count()
user_count
943
初始化训练集中的产品个数
# 流程:投影出产品字段 -> 对产品进行去重 -> 统计产品数量
product_count = ratings_rdd.map(lambda x:x.product).distinct().count()
product_count
1682
开始训练模型
model = ALS.train(ratings_rdd,rank=10,iterations=10,lambda_=0.01)
模型为用户进行推荐产品
# recommendProducts参数含义 (user:用户id,num:需要为该用户推荐的产品数量)
model.recommendProducts(user=100,num=10)
[Rating(user=100, product=464, rating=6.328077627837865),
Rating(user=100, product=1114, rating=6.0246494675621864),
Rating(user=100, product=888, rating=5.957080472811217),
Rating(user=100, product=776, rating=5.94518524812436),
Rating(user=100, product=1456, rating=5.570928201780987),
Rating(user=100, product=1153, rating=5.276014679950186),
Rating(user=100, product=745, rating=5.273830915853978),
Rating(user=100, product=309, rating=5.2226922589528755),
Rating(user=100, product=1159, rating=5.214222938082385),
Rating(user=100, product=967, rating=5.191467123059153)]
用模型为产品推荐用户
# recommendUsers 参数含义 (product:产品id,num:需要推荐的用户数量)
model.recommendUsers(product=101,num=10)
[Rating(user=575, product=101, rating=7.786829895272848),
Rating(user=842, product=101, rating=7.385093317833681),
Rating(user=777, product=101, rating=6.815955510387151),
Rating(user=310, product=101, rating=6.749508509094776),
Rating(user=752, product=101, rating=6.4059841032560945),
Rating(user=745, product=101, rating=6.303744522941349),
Rating(user=281, product=101, rating=6.268866937046303),
Rating(user=51, product=101, rating=6.250170924530637),
Rating(user=695, product=101, rating=6.158916996903442),
Rating(user=649, product=101, rating=5.951955147976774)]
导入产品信息数据
product_data = sc.textFile("file:/home/zh123/.jupyter/workspace/ml-100k/u.item")
查看产品数据样例
product_data.take(5)
['1|Toy Story (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Toy%20Story%20(1995)|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0',
'2|GoldenEye (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?GoldenEye%20(1995)|0|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0',
'3|Four Rooms (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Four%20Rooms%20(1995)|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0',
'4|Get Shorty (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Get%20Shorty%20(1995)|0|1|0|0|0|1|0|0|1|0|0|0|0|0|0|0|0|0|0',
'5|Copycat (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Copycat%20(1995)|0|0|0|0|0|0|1|0|1|0|0|0|0|0|0|0|1|0|0']
将每行产品数据根据 “|” 进行分割,然后取前两项
# 产品数据格式(product_id,product_name)
product_rdd = product_data.map(lambda line:line.split("|")).map(lambda x:(int(x[0]),x[1]))
product_rdd.take(5)
[(1, 'Toy Story (1995)'),
(2, 'GoldenEye (1995)'),
(3, 'Four Rooms (1995)'),
(4, 'Get Shorty (1995)'),
(5, 'Copycat (1995)')]
利用产品信息生成 id -> name的映射字典
product_id_to_name = product_rdd.collectAsMap()
list(product_id_to_name.items())[:5]
[(1, 'Toy Story (1995)'),
(2, 'GoldenEye (1995)'),
(3, 'Four Rooms (1995)'),
(4, 'Get Shorty (1995)'),
(5, 'Copycat (1995)')]
显示推荐的产品名称
for item in model.recommendProducts(user=100,num=10):
print("为用户 {} 推荐 {} 推荐指数: {}".format(item.user,
product_id_to_name[item.product],
item.rating))
为用户 100 推荐 Vanya on 42nd Street (1994) 推荐指数: 6.328077627837865
为用户 100 推荐 Faithful (1996) 推荐指数: 6.0246494675621864
为用户 100 推荐 One Night Stand (1997) 推荐指数: 5.957080472811217
为用户 100 推荐 Three Wishes (1995) 推荐指数: 5.94518524812436
为用户 100 推荐 Beat the Devil (1954) 推荐指数: 5.570928201780987
为用户 100 推荐 Backbeat (1993) 推荐指数: 5.276014679950186
为用户 100 推荐 Ruling Class, The (1972) 推荐指数: 5.273830915853978
为用户 100 推荐 Deceiver (1997) 推荐指数: 5.2226922589528755
为用户 100 推荐 Stalker (1979) 推荐指数: 5.214222938082385
为用户 100 推荐 Little Lord Fauntleroy (1936) 推荐指数: 5.191467123059153