46

基于矩阵分解算法的推荐系统实战

 5 years ago
source link: https://www.tuicool.com/articles/iy2a63y
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

设:

U 为所有用户集合

P 为所有物品集合

R 为用户对物品的喜好程度

模型 Model(R) = U * P

算法核心:

通过用户对不同物品的打分,来预测用户对其他物品的喜好程度。此处并没有考虑用户和物品的属性,如:用户年龄,性别,学历,工作等,物品价格,品类,外观等。

通过用户对物品的打分,可以建立一个推荐值矩阵,之后就可以通过运算该矩阵来预测用户喜好,即为矩阵分解算法!

矩阵分解:

将推荐值矩阵 R 分解为矩阵 U 和 矩阵 P,使得 U 和 P 的乘积得到的新矩阵 R* 中的元素与 R 中的已知元素的值非常接近,那幺 R* 中对应于 R 中的未知元素的值就是预测值。

推荐值矩阵:

时间简史 万历三十年 大秦帝国 红楼梦 数学简史 小明 1 4 1 小王 2 2 4 小李 4 1 4 小张 5 1 4

推荐值矩阵关键性问题:

    1. 初始值获取,数据的收集
    1. 从推荐值矩阵中已知数据预测未知数据
    1. 建立评价系统,用于检验推荐系统的效果

收集数据

一般可以采取网络爬虫的方式,比如对于数据的评分,可以爬取豆瓣读书上的数据,也可以在自己可以控制的网站上做埋点等来收集用户信息。

预测未知数据

关键挑战:

当用户和物品的数量都比较大时,推荐之矩阵通常会是一个稀疏矩阵(在矩阵中,若数值为0的元素数目远远多于非0元素的数目,并且非0元素分布没有规律时,则称该矩阵为稀疏矩阵),说明大多数用户可能并没有对大多数物品表达喜好。

冷启动问题,是每一个推荐系统都需要面对的问题。

矩阵分解实例:

即:

对比最左侧的元素矩阵和最右侧的预测矩阵,预测矩阵中位于原始矩阵缺失数值位置的元素值,即为预测值。

同时也可以得到

即:对于在 ij 位置上的物品的喜好数据,可以通过第 i 个用户的画像向量和第 j 个物品的画像向量代表。

使用图形表示如下:

RJ7fu2r.png!web

其中 k 在数学上的意义为矩阵分解的秩,在业务上的意义为 影响用户给物品评分的 k 个影响因子,当前我们无法直接知道 k 的值,在模型训练时,一般采取交叉验证的方式来寻找最优的 k 值。

我们可以使用“和方差”来作为损失函数

这里通过已知的{ },计算“和方差”,使之达到最小,即预测值越接近真实值。以此得出的 U 和 P 的值就是我们需要的值。

损失函数的梯度

单独取出误差

对误差 L 分别在 U 和 P 上求导可得

现在我们已经知道了损失函数的梯度(导数),下面就可以使用梯度下降法来求解 U 和 P 的值。

梯度下降法

qQraIby.png!web

随机选取一个起始点,然后在负梯度的方向上持续训练,直到损失函数的梯度越来越接近零,此时即可取得最优解。

引入正则化

为了防止过拟合的发生,对损失函数加入正则化参数

λ>0

这样,当 U 和 P 都保证比较小的情况下,U 或者 P 的数值剧烈变化时,U 和 P 的点积也不会有太大的变化。

最终的损失函数为:

+

最终损失函数的梯度为:

运用梯度下降法求最优解

设定梯度下降的速率 γ(学习速率)和 k 值,并随机初始化 U 和 P,重复训练,直到误差满意为止。

评估推荐系统

最基本的就是,通过训练集训练模型,通过测试集测试模型,如果模型在测试集上的表现达到我们的预期,则该模型可以上线部署。 一般采用平均绝对离差来验证模型预测值的好坏

n:测试集中推荐值的总数量

:真实的用户 u 对物品 p 的推荐值

:预测的用户 u 对物品 p 的推荐值

在线的 A/B 测试

项目实战

数据集格式如下:

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)

可以调整学习速率,迭代次数,隐藏因子个数和正则化参数等来训练不同的模型,并评估结果,获取满意的模型。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK