隐语义模型:奇异值矩阵分解SVD在推荐系统的运用

前言

一开始了解SVD的是因为降维,但后面发现有人用它来做推荐,所以我去了解了一下,但我觉得推荐系统跟SVD好像关系并不大,本质上没有用SVD的理论,只是用了SVD的思想:矩阵分解。

矩阵分解

矩阵分解很简单,线性代数里两个矩阵相乘得到了一个矩阵:
P 2 ∗ 3 Q 3 ∗ 4 = R 2 ∗ 4 P^{2*3} Q^{3*4}=R^{2*4} P23Q34=R24
P 2 ∗ 3 P^{2*3} P23表示矩阵是2x3维的,这样一个矩阵相乘的思想可以被我们反过来利用,将一个矩阵R n * m 分解为一个n * f 的矩阵P和 f * m 的矩阵Q,这样做的目的是什么?可以将一个大的矩阵分解为两个小矩阵,至少存储比较简单,类似压缩数据的技巧,降维也是这个思想。

推荐系统为什么需要矩阵分解?

因为大型网站的用户太多了,而且推荐的商品也多,你想象一下3亿的用户对上300万商品,这是个什么概念,这个维度得多大,所以,直接求解这个大矩阵不现实,所以通过求解两个小矩阵还是比较方便存储。
另一个是我们想挖掘用户和物品的隐藏关联,但这个关联是未知的,隐藏的关联,比如:用户看电影,用户与电影类型的关系,电影与电影类型的关系,那么电影类型就是一个隐藏因子,通过隐藏因子将用户和物品关联起来。

如何落地?

实际类似word2vec的思想,我们一开始初始化这两个矩阵P、Q,把它们作为需要训练的参数,然后通过样本来训练模型,构建损失函数,通过梯度下降来更新参数,这就是一个完整的落地流程,拆解一下详情大概就是:

  • 正负样本
    因为是做训练,就需要正负样本,正样本还是比较好找的,用户对这个商品评分或者购买等行为就是一个正样本,但负样本怎么选择?一般是选择那些热门商品、销量高的商品,但用户不感兴趣的作为负样本

  • 构建模型
    模型表达式:
    R n , m = ∑ k = 1 K P n , k Q k , m R_{n,m}=\sum_{k=1}^{K}P_{n,k}Q_{k,m} Rn,m=k=1KPn,kQk,m
    其实这个表达式表明的是第n个用户对第m个物品的预测评分,不是所有的样本。

  • 损失函数
    一般选用RMSE作为损失函数,
    m e s = ∑ ( R n , m ^ − ∑ k = 1 K P n , k Q k , m ) 2 + α ∣ ∣ P ∣ ∣ 2 + β ∣ ∣ Q ∣ ∣ 2 mes=\sum(\hat{R_{n,m}} - \sum_{k=1}^{K}P_{n,k}Q_{k,m})^2+\alpha||P||^2+\beta||Q||^2 mes=(Rn,m^k=1KPn,kQk,m)2+αP2+βQ2
    R n , m ^ \hat{R_{n,m}} Rn,m^是第n个用户对第m个物品的真实评分。
    尾项有两个,这两个是正则化表达式。

  • 梯度更新
    这一块主要是对矩阵每个元素做更新

存在的问题:

  • 这样每次来一个用户,是不是都要训练一次模型?
    因为这个用户的之前都不在样本集中,增加一个样本也就修改了一个矩阵的行数,似乎这个问题挺恶心的。
  • 同样增加一个新的物品,也有同样的问题
    于是基于物品的、用户的推荐似乎可以解决冷启动的问题。

代码

网上给你很多代码通过评分来构建模型,一个用户对物品的评分,这个评分的高低就说明了用户对物品的喜爱程度,通过样本来训练模型。

#-*- coding:utf-8 -*-
# 可以使用上面提到的各种推荐系统算法
from surprise import SVD
from surprise import Dataset
from surprise.model_selection import cross_validate

from surprise import Reader
from surprise import BaselineOnly, KNNBasic, NormalPredictor
from surprise import accuracy
from surprise.model_selection import KFold, split
from surprise import SVD,SVDpp


import numpy as np
import pandas as pd
from collections import defaultdict
import os


# 指定文件所在路径
file_path = os.path.expanduser('mydata.csv')
# 告诉文本阅读器,文本的格式是怎么样的
reader = Reader(line_format='user item rating', sep=',')
# 加载数据
data = Dataset.load_from_file(file_path, reader=reader)
trainset = data.build_full_trainset()

algo = SVD()
algo.fit(trainset)

def get_top_n(predictions, n=10):

    # First map the predictions to each user.
    top_n = defaultdict(list)
    # uid: 用户ID
    # iid: item ID
    # true_r: 真实得分
    # est:估计得分
    for uid, iid, true_r, est, _ in predictions:
        top_n[uid].append((iid, est))

    # Then sort the predictions for each user and retrieve the k highest ones.
    # 为每一个用户都寻找K个得分最高的item
    for uid, user_ratings in top_n.items():
        user_ratings.sort(key=lambda x: x[1], reverse=True)
        top_n[uid] = user_ratings[:n]

    return top_n

testset =  [
    ('5','1',0),# 想获取第5个用户对第1个item的得分
    ('5','4',0),# 0这个位置是真实得分,不知道时可以写0
    ('5','5',0),# 但写0后,就没法进行算法评估了,因为不知道真实值
]
predictions = algo.test(testset)
top_n = get_top_n(predictions,10)

# Print the recommended items for each user
for uid, user_ratings in top_n.items():
    print(uid, [iid for (iid, _) in user_ratings])

其中数据集部分,非常简单:

1,1,1
1,2,2
1,3,3
1,4,4
1,5,5
2,1,1
2,2,2
2,3,3
2,4,4
2,5,5
3,1,1
3,2,2
3,3,3
3,4,4
3,5,5
4,1,1
4,2,2
4,3,3
4,4,4
4,5,5
5,2,2
5,3,3

当然大家也可以主动去学习:



# 默认载入movielens数据集
data = Dataset.load_builtin('ml-100k')


# 试一把SVD矩阵分解
algo = SVD()

# 在数据集上测试一下效果
perf = cross_validate(algo, data, measures=['RMSE', 'MAE'], cv=3)
#输出结果
print(perf)

参考博客

从SVD到推荐系统
用surprise实现SVD协同过滤推荐算法对本地数据做推荐

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值