案例来源于花椒直播 ,作者花椒智能工程
一、协同过滤算法概述
协同过滤 (Collaborative Filtering) 算法一经发明便在推荐系统中取得了非凡的成果。许多知名的系统早期都采用了协同过滤算法,例如 Google News,亚马逊、Hulu、Netflix 等。
协同过滤算法一般采用评分矩阵来表示用户和物品的交互,评分矩阵 中的每一个元素 表示用户 对物品 的喜好评分。由于用户不能对大部分物品都有交互,所以在很多场景下评分矩阵都很稀疏,稀疏率在 90% 以上,稀疏度很高决定算法在优化和选取上有很多考量。
传统协同过滤算法
传统的协同过滤分为 user base 和 item base 两种算法,这两种算法的核心思想是一样的,我们以 item base 算法为例,说明算法的具体流程:
1. 计算物品之间的相似度,我们利用评分矩阵的列向量作为每一个物品 的向量,然后运用余弦相似度来计算每两个物品之间的相似度
2. 通过用户喜欢的物品集合,计算出用户 对物品 的分数
-
是用户 喜欢的物品集合
-
是和物品 最相似的 个物品的集合
-
是物品 和 的相似度
-
代表用户 对物品 的分数
3. 通过上述计算的分数,从大到小召回 TopK 个物品
基于隐向量的协同过滤
传统的基于物品或用户的协同过滤有如下缺陷:
-
它们是一种基于统计的方法,而不是优化学习的方法,没有学习过程和设立指标进行优化得到最优模型的过程
-
没有用到全局数据,只用了局部数据计算相似度、进行推荐
-
当用户或物品维度很大时,会占用很大的内存
基于以上缺陷,学者们又提出了广义的协同过滤算法,即基于基于隐向量的协同过滤。基于隐向量的协同过滤最早是隐语义模型 (LFM)。他的核心思想很朴素,利用矩阵分解,把很大很稀疏的评分矩阵 分解成两个较为稠密的矩阵:
它的优化目标一般使用 MSE 作为损失函数:
其中 就是用户 的隐向量, 是物品 的隐向量。
目前推荐系统中应用的协同过滤算法一般都是基于隐向量的协同过滤算法。登录 B 站 Google 中国 TensorFlow 频道观看《使用 TensorFlow 搭建推荐系统系列(全八讲)》视频,可了解更多关于过滤与协同过滤的内容。
显式反馈和隐式反馈
显示反馈是用户直接对物品进行喜好打分,比如豆瓣电影,IMDB 电影打分。隐式反馈主要出现在互联网应用场景,一般很难收集到用户对物品的直接喜好打分,转而寻求隐含的偏好倾向,例如点击次数、点赞、观看时长等
显式反馈 | 隐式反馈 |
---|---|
用户主动表示对物品的喜好分数,例如豆瓣电影打分,IMDB 电影打分 | 用户对物品的行为交互频率,比如说观看次数,点赞次数,观看时长…… |
分数代表喜欢程度 | 数值只是代表交互程度,并不能明确表示喜欢程度 |
适用于有收集喜好打分的网站,应用面比较狭窄 | 适用于目前互联网大部分应用场景 |
花椒中用到的矩阵分解算法也是基于隐式反馈,本文接下来篇幅中介绍的神经网络协同过滤算法也都是基于隐式反馈。需要注意的是,基于隐式反馈的算法,都会引入负反馈来训练,由于负反馈的量级远远大于正反馈,负反馈一般采用随机采样的方法,一个正样本搭配 5 个负样本。
在基于隐式反馈的协同过滤算法中, Spark
上实现了 ALS 算法,Spark
上实现的接口简单易用,计算迅速,部署简答,目前很多应用都基于 ALS 算法进行快速搭建。ALS 算法的应用实例如下:
from pyspark.mllib.recommendation import ALS, MatrixFactorizationModel, Rating
# Load and parse the data
data = sc.textFile("data/mllib/als/test.data")
ratings = data.map(lambda l: l.split(','))\
.map(lambda l: Rating(int(l[0]), int(l[1]), float(l[2])))
# Build the recommendation model using Alternating Least Squares
rank = 10
numIterations = 10
model = ALS.trainImplicit(ratings, rank, numIterations, alpha=10)
# Evaluate the model on training data
testdata = ratings.map(lambda p: (p[0], p[1]))
predictions = model.predictAll(testdata).map(lambda r: ((r[0], r[1]), r[2]))
ratesAndPreds = ratings.map(lambda r: ((r[0], r[1]), r[2])).join(predictions)
MSE = ratesAndPreds.map(lambda r: (r[1][0] - r[1][1])**2).mean()
print("Mean Squared Error = " + str(MSE))
二、损失函数的选择
基于回归的损失函数
基于显式反馈的矩阵分解算法,采用 MSE 作为损失函数,他拟合稀疏矩阵中所有有值的元素。他的计算公式如下:
它的求解方法一般采用交替最小二乘法,交替固定 ,不断迭代求解
隐式反馈的损失函数,设置稍微复杂,需要考虑隐式反馈强度的设定,步骤如下:
-
首先定义用户对物品的偏好
-
其次定义用户 对物品 偏好置信度,反馈次数越多,越确信用户对物品的偏好