基于矩阵分解算法的推荐系统实战算法
推荐系统,能够根据用户的喜爱来推荐给用户不一样的事物。bash
推荐系统类型:网络
设:app
U 为全部用户集合dom
P 为全部物品集合函数
R 为用户对物品的喜爱程度学习
模型 Model(R) = U * P测试
算法核心:网站
经过用户对不一样物品的打分,来预测用户对其余物品的喜爱程度。此处并无考虑用户和物品的属性,如:用户年龄,性别,学历,工做等,物品价格,品类,外观等。spa
经过用户对物品的打分,能够创建一个推荐值矩阵,以后就能够经过运算该矩阵来预测用户喜爱,即为矩阵分解算法!
矩阵分解:
将推荐值矩阵 R 分解为矩阵 U 和 矩阵 P,使得 U 和 P 的乘积获得的新矩阵 R* 中的元素与 R 中的已知元素的值很是接近,那么 R* 中对应于 R 中的未知元素的值就是预测值。
推荐值矩阵:
时间简史 | 万历三十年 | 大秦帝国 | 红楼梦 | 数学简史 | |
---|---|---|---|---|---|
小明 | 1 | 4 | 1 | ||
小王 | 2 | 2 | 4 | ||
小李 | 4 | 1 | 4 | ||
小张 | 5 | 1 | 4 |
推荐值矩阵关键性问题:
通常能够采起网络爬虫的方式,好比对于数据的评分,能够爬取豆瓣读书上的数据,也能够在本身能够控制的网站上作埋点等来收集用户信息。
关键挑战:
当用户和物品的数量都比较大时,推荐之矩阵一般会是一个稀疏矩阵(在矩阵中,若数值为0的元素数目远远多于非0元素的数目,而且非0元素分布没有规律时,则称该矩阵为稀疏矩阵),说明大多数用户可能并无对大多数物品表达喜爱。
冷启动问题,是每个推荐系统都须要面对的问题。
矩阵分解实例:
即:
对比最左侧的元素矩阵和最右侧的预测矩阵,预测矩阵中位于原始矩阵缺失数值位置的元素值,即为预测值。
同时也能够获得
即:对于在 ij 位置上的物品的喜爱数据,能够经过第 i 个用户的画像向量和第 j 个物品的画像向量表明。
使用图形表示以下:
其中 k 在数学上的意义为矩阵分解的秩,在业务上的意义为 影响用户给物品评分的 k 个影响因子,当前咱们没法直接知道 k 的值,在模型训练时,通常采起交叉验证的方式来寻找最优的 k 值。
咱们可使用“和方差”来做为损失函数
这里经过已知的{
},计算“和方差”,使之达到最小,即预测值越接近真实值。以此得出的 U 和 P 的值就是咱们须要的值。
单独取出偏差
对偏差 L 分别在 U 和 P 上求导可得
如今咱们已经知道了损失函数的梯度(导数),下面就可使用梯度降低法来求解 U 和 P 的值。
梯度降低法
随机选取一个起始点,而后在负梯度的方向上持续训练,直到损失函数的梯度愈来愈接近零,此时便可取得最优解。
为了防止过拟合的发生,对损失函数加入正则化参数
λ>0
这样,当 U 和 P 都保证比较小的状况下,U 或者 P 的数值剧烈变化时,U 和 P 的点积也不会有太大的变化。
最终的损失函数为:
+
最终损失函数的梯度为:
设定梯度降低的速率 γ(学习速率)和 k 值,并随机初始化 U 和 P,重复训练,直到偏差满意为止。
n:测试集中推荐值的总数量
:真实的用户 u 对物品 p 的推荐值
:预测的用户 u 对物品 p 的推荐值
数据集格式以下:
1 1119 9.000000
1 167 8.000000
1 6265 8.000000
1 1440 9.000000
1 1427 9.000000
1 5404 8.000000
1 259 7.000000
1 4156 8.000000
2 419 9.000000
2 415 10.000000
2 2834 9.000000
2 228 10.000000
2 107 10.000000
2 440 9.000000
2 44 10.000000
2 455 10.000000
复制代码
第一列为用户 ID,第二列为物品 ID,第三列为对应的打分(1-10)
整体代码基于 surprise 库,能够先安装
pip install scikit-surprise
复制代码
下面导入相关库和数据集
import numpy as np
import surprise
from surprise import BaselineOnly
from surprise import Dataset
from surprise import Reader
from surprise import accuracy
from surprise.model_selection import cross_validate
from surprise.model_selection import train_test_split
reader = Reader(line_format='user item rating', sep='\t', rating_scale=(1, 10))
data = Dataset.load_from_file('book_ratings.dat.txt', reader=reader)
# 将数据随机分为训练和测试数据集
trainset, testset = train_test_split(data, test_size=.25)
复制代码
根据公式,定义算法函数
class MatrixFactorization(surprise.AlgoBase):
def __init__(self, lr, n_epochs, n_factors, lmd):
self.lr = lr # 梯度降低法的学习速率
self.n_epochs = n_epochs # 梯度降低法的迭代次数
self.n_factors = n_factors # 分解的矩阵的秩,即影响用户打分的隐藏因子
self.lmd = lmd # 正则化参数
def fit(self, trainset):
print("Fitting data...")
# 随机初始化 u 和 p 矩阵
u = np.random.normal(0, .1, (trainset.n_users, self.n_factors)) # 均值为0,方差为0.1,(行数,列数)
p = np.random.normal(0, .1, (trainset.n_items, self.n_factors))
# 梯度降低法
for _ in range(self.n_epochs):
print("Round:", _)
for i, j, r_ij in trainset.all_ratings():
# 这里就是套用上面获得的公式
# u_old[i] = u[i]
err = r_ij - np.dot(u[i], p[j])
u[i] -= -self.lr * err * p[j] + self.lr * self.lmd * u[i]
p[j] -= -self.lr * err * u[i] + self.lr * self.lmd * p[j]
self.u, self.p = u, p
self.trainset = trainset
print("End fitting!")
def estimate(self, i, j):
if self.trainset.knows_user(i) and self.trainset.knows_item(j):
return np.dot(self.u[i], self.p[j])
else:
return self.trainset.global_mean # 返回平均值
复制代码
最后再训练、预测,评估
algo = MatrixFactorization(0.005, 60, 3, 0.2)
algo.fit(trainset)
predictions = algo.test(testset)
accuracy.mae(predictions)
复制代码
能够调整学习速率,迭代次数,隐藏因子个数和正则化参数等来训练不一样的模型,并评估结果,获取满意的模型。