Slope One算法

介绍

Slope One算法是一种比较流行的基于物品的协同过滤算法, 最大的优点就是简单. 它是在一篇名为: Slope One: 基于在线评分系统的协同过滤算法的论文中提出的.

用一个简单的例子来了解它. 假设Amy给SPY打了3分, Whitney Houston打了4分; Ben给SPY打了4分. 来预测Ben会给Whitney Houston打几分.

PSY Whitney Houston
Amy 3 4
Ben 4 ?

先来简单预测一下: 由于Amy给Whitney Houston打的分数比PSY高一分, 所以预测Ben也会给高一分, 即5分.

还有其他的Slope One算法, 比如加权Slope One.

可以将Slope One分为2个步骤:

  1. 计算两个物品之间的差值(可以在闲时批量计算). 在上文的例子中, 这个步骤就是计算出Whitney Hoston要比PSY高一分.
  2. 进行预测, 比如一个新用户Ben, 从未听过Whitney Houston的歌曲, 想要预测他是否喜欢这个歌手.

通过利用他评价过的歌手以及计算好的歌手之间的评分差值, 就可以进行预测了.

第一步: 计算差值

计算物品之间的差异的公式:

$$ dev_{i,j} = \sum_{u \in S_{i,j}(X)} {u_i - u_j \over card(S_{i,j}(X))} $$

其中,card(S)表示S中有多少个元素;X表示所有评分值的集合;card(Sj,i(X))则表示同时评
价过物品j和i的用户数。
我们来考察PSY和Taylor Swift之间的差值,card(Sj,i(X))的值是2——因为有两个用户(Amy
和Ben)同时对PSY和Taylor Swift打过分。

第二步: 使用加权的Slope One算法进行预测

计算公式:

$$ P^{wS1}(u)_j = {\sum_{i \in S(u)-{j}}
{(dev_{j,i} + u_i)c_{j,i}} \over {\sum_{i\in S(u) - {j}}c_{j,i}}} $$

PWS1(u)j 表示我们将预测用户u对物品i的评分。比如PWS1(Ben)Whitney Houston 表示Ben对
Whitney Houston的预测评分。下面就让我们来求解这个问题。
首先来看分子:
$$ \sum_{i \in S(u)-{j}} $$
表示遍历Ben评价过的所有歌手,除了Whitney Houston以外(也就是-{j}的意思)。
整个分子的意思是:对于Ben评价过的所有歌手(Whitney Houston除外),找出Whitney
Houston和这些歌手之间的差值,并将差值加上Ben对这个歌手的评分。
同时,我们要将这个结果乘以同时评价过两位歌手的用户数。
让我们分解开来看,先将Ben的评分情况和两两歌手之间的差异值展示如下:

代码如下:

import pandas as pd
import numpy as np
from pandas import DataFrame
data = pd.read_csv('../chapter-2/Movie_Ratings.csv')
# 计算m1和m2的差异值
def get_slope(m1, m2, dataframe):
slope_df = dataframe.loc[[m1, m2]].T.dropna()
return (slope_df[m2] - slope_df[m1]).sum() / len(slope_df)
def get_slope_df(dataframe):
df = DataFrame(
np.zeros(len(data.index) ** 2).reshape(
len(data.index), -1),
index=data.index,
columns=data.index
)
for m in dataframe.index:
for _m in dataframe.index.drop(m):
df[m][_m] = get_slope(m, _m, dataframe)
return df
def get_numerator(user, movie, slope_df, dataframe):
user_series = dataframe[user]
if movie in user_series:
user_series = user_series.drop(movie)
user_series = user_series.dropna()
movies = dataframe[user].dropna()
if movie in movies:
movies = movies.drop(movie)
result = 0.0
for m in movies.index:
_m = slope_df[movie][m] + dataframe[user][m]
_m *= len(dataframe.loc[[movie, m]].T.dropna())
result += _m
return result
def get_denominator(user, movie, slope_df, dataframe):
movies = dataframe[user].dropna()
if movie in movies:
movies = movies.drop(movie)
result = 0
for m in movies.index:
result += len(dataframe.loc[[movie, m]].T.dropna())
return result
def get_slope_value(user, movie, slope_df, dataframe):
numerator = get_numerator(user, movie, slope_df, dataframe)
denominator = get_denominator(user, movie, slope_df, dataframe)
return numerator / denominator
user = 'Heather'
movie = 'Blade Runner'
data = pd.read_csv('../chapter-2/Movie_Ratings.csv')
slope_df = get_slope_df(data)
print (get_slope_value(user, movie, slope_df, data))

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器