个性化推荐系统

 

 

 

(推荐系统)

 

 

推荐系统概述

 

 

 

 

 

 

 

 

 

 

2020-08

 

 

 

 

 

 

 

 

 

 

 

推荐系统

 

推荐系统概述

一.推荐系统的背景与价值

1.推荐系统的应用场景:网易音乐、电商推荐、新闻推荐

个性化推荐:在面向用户的互联网产品中发挥着极其重要的作用。

 

 

 

 

2.推荐系统的价值

       在面向用户的互联网产品中,代替用户评估其从未看过、接触过和使用过的物品,包括电影、新闻、音乐、餐馆、旅游景点等。推荐系统作为一种信息过滤的重要手段,是解决“信息过载”问题的最有效的方法之一。

(1)对于用户来说:帮助用户准确找到感兴趣的信息;

具体表现:在有限时间内,给用户曝光他潜意识里想看的、想买的物品…

(2)对于商家来说:帮助商家带来更多的用户关注、销量;

具体表现:浏览更多、点击更多、购买更多…

 

3.推荐方式

(1)推荐用户感兴趣的

(2)推荐热门的商品

(3)随机的推荐,保证多样性

 

4.推荐系统的原理

(1)推荐系统:可以被简单的抽象成一个m*n的矩阵R,行和列分别表示:用户、物品,m,n分别表示用户和物品数量,矩阵中的每一项代表:一个用户对一个物品的评分,“空”表示用户和物品之间没有交互。

(2)目标:预测矩阵中所有未知项的评分,然后给用户推荐评分较高的未交互物品。

 

 

5.推荐引擎VS搜索引擎

(1)搜索引擎:满足了用户有明确目的的主动查找需求(主观意识的查询搜索关键词)。搜索引擎的输出更贴近用户的主管搜索(可能会有一定的“发散,如百度边栏信息”)。

(2)推荐引擎:在用户没有明确目的的时候,帮助用户发现感兴趣的新内容。推荐系统的输出会充分考虑“协同过滤”的结果,关联的结果在“深度”、“广度”上更进一步。

(3)相似之处:

①模型上:都存在猜测用户潜在心理、行为的味道

②形式上:都存在要输出一份“排序列表”的问题

 

 

 

 

 

 

 

 

二.推荐系统的历史与分类

1.推荐系统的发展历史

 

 

2.推荐系统分类发展

(1)基于内容的推荐

①基于物品的属性、用户的画像、一般需要人工打标签

②方法朴素、渐淡,工业界用的比较多

③可以作为解决冷启动的候选方法

# 评分预测:基于显式的信息进行推荐

# 非评分的:隐反馈(浏览,点击,收藏。。。)

 

 

(2)基于协同过滤的推荐

①基于用户的协同过滤推荐 (User-Based CF):本质就是计算用户之间的相似度,然后获取与目标用户相似度较高的用户,并将其交互物品中评分较高的推荐给目标用户。

②基于物品的协同过滤推荐 (Item-Base CF): 本质就是计算物品之间的相似度,然后获取与目标用户交互物品相似度较高的物品,并将其推荐给目标用户。

 

(3)基于隐语义模型的推荐

①矩阵分解:核心思想是认为用户的兴趣只受少数几个因素的影响,因此将稀疏且高维的User-Item评分矩阵分解为两个低维矩阵,即通过User,Item评分信息来学习用户特征矩阵P物品特征矩阵Q,通过重构的低维矩阵来预测用户对物品的评分。

②宏观理解:一个向量表示:物品包含几个方面特征的程度;另一个向量表示:用户对特征的喜欢程度。两个向量的内积体现:用户对物品的喜欢程度。

 

 

 

 

 

 

 

 

三.个性化推荐的典型算法思想

1.算法1——基于内容的推荐

①基于物品的属性、用户的画像、一般需要人工打标签

②方法朴素、渐淡,工业界用的比较多

③可以作为解决冷启动的候选方法

 

 

 

 

 

 

2.算法2——基于协同过滤的推荐

(1)基于用户的协同过滤推荐 (User-Based CF):本质就是计算用户之间的相似度,然后获取与目标用户相似度较高的用户,并将其交互物品中评分较高的推荐给目标用户。

 

(2)基于用户协同过滤推荐思想

相似度矩阵(向量的余弦相似度)

 

# —*- coding: utf-8 -*-
# @time     : 2020/8/21 23:07
# @Author   : FZY

# 计算余弦相似度
import numpy as np
import pandas as pd
from pandas import DataFrame

n = [
    [4, 3, 0, 0, 5, 0],
    [5, 0, 4, 0, 4, 0],
    [4, 0, 5, 3, 4, 0],
    [0, 3, 0, 0, 0, 5],
    [0, 4, 0, 0, 0, 4],
    [0, 0, 2, 4, 0, 5],
]
n1 = []
count = 0

for i in range(6):
    for j in range(6):
        x1 = np.around(np.dot(n[i:i + 1][0], n[j:j + 1][0]), decimals=2)
        x2 = np.around((np.linalg.norm(n[i:i + 1][0]) * np.linalg.norm(n[j:j + 1][0])), decimals=2)
        n1.append(np.around((x1 / x2), decimals=2))
        count += 1
        # if count % 6 == 0:
        #     n1.append('\n')
        # n1.append(((np.dot(n[i:i+1][0], n[j:j+1][0])/(np.linalg.norm(n[i:i+1][0])*np.linalg.norm(n[j:j+1][0])))))
print(np.array(n1).reshape(6, 6))
# n1.reshape(6,6)

生成相似度矩阵:

 

 

 

 

 

 

 

 

(3)基于用户的额协同过滤计算过程

 

 

推荐产生的结果一定是目标用户未交互过的物品。

 

3.基于物品的协同过滤的推荐

(1)基于物品的协同过滤推荐 (Item-Base CF): 本质就是计算物品之间的相似度,然后获取与目标用户交互物品相似度较高的物品,并将其推荐给目标用户。

 

(2)基于物品的协同过滤推荐思想

 

(3)基于物品的协同过滤计算过程

 

3.算法3——基于矩阵分解的推荐

宏观理解:一个向量表示:物品包含某几个方面特征的程度;另一个向量表示:用户对特征的喜欢程度。两个向量的内积体现:用户对物品的喜欢程度。

 

 

 

四.推荐系统面临的挑战与发展趋势

1.发展挑战

(1)冷启动问题:用户冷启动,物品冷启动

(2)数据稀疏条件下的推荐精度问题

(3)推荐的多样性、新颖性问题

(4)非结构化、多中模态,高质量的数据问题

(5)实时计算、大数据存储

(6)推荐系统中的用户隐私问题

2.发展趋势

(1)移动环境下推荐系统成为主流

(2)近乎实时的个性化推荐服务

(3)可解释的推荐系统

(4)完整的推荐系统引擎+推荐系统作为云服务方式提供

 

 

 

 

 

 

 

五.推荐系统Surprise

# —*- coding: utf-8 -*-
# @time     : 2020/8/22 9:06
# @Author   : FZY
import pandas as pd
# from surprise import Reader
from IPython import get_ipython

import surprise
# from surprise import Dataset
# from surprise.model_selection import cross_validate
# from surprise import NormalPredictor
#

import os
import zipfile

# os.chdir(r'F:\推荐系统\项目\03推荐系统相似度计算\home\aistudio\data\data17529')
# extracting = zipfile.ZipFile('BX-CSV-Dump.zip')
# extracting.extractall()

# get_ipython().system('ls F:\推荐系统\项目\03推荐系统相似度计算\home\aistudio\data')
user = pd.read_csv(r'F:\推荐系统\项目\03推荐系统相似度计算\home\aistudio\data\data17529\BX-Users.csv'
                   , sep=';', error_bad_lines=False, encoding="Latin-1")
user.columns = ['userID', 'Location', 'Age']
rating = pd.read_csv(r'F:\推荐系统\项目\03推荐系统相似度计算\home\aistudio\data\data17529\BX-Book-Ratings.csv',
                     sep=';', error_bad_lines=False, encoding="Latin-1")
rating.columns = ['userID', 'ISBN', 'bookRating']
print(len(user))

print(user.tail())

df = pd.merge(user, rating, on='userID', how='inner')
df.drop(['Location', 'Age'], axis=1, inplace=True)
print(len(df))
print(df.head())

输出:

 

 

from plotly.offline import init_notebook_mode, plot, iplot
# get_ipython().run_line_magic('matplotlib', 'notebook')
import plotly.graph_objs as go

init_notebook_mode(connected=True)

data = df['bookRating'].value_counts().sort_index(ascending=False)
trace = go.Bar(x=data.index,
               text=['{:.1f} %'.format(val) for val in (data.values / df.shape[0] * 100)],
               textposition='auto',
               textfont=dict(color='#000000'),
               y=data.values, )
layout = dict(title='Distribution Of {} book-ratings'.format(df.shape[0]),
              xaxis=dict(title='Rating'),
              yaxis=dict(title='Count'))
fig = go.Figure(data=[trace], layout=layout)
# 这里使用iplot(fig)就不会输出网页信息
# 网络查找答案为:py.plot会生成一个离线的html文件,里面放置图片。而py.iplot则直接在ipython notebook里面生成图片。
plot(fig)

结果:直方图中,x轴表示书籍被评分次数,Y轴表示书籍的数量,

 

 

df.groupby('ISBN')['bookRating'].count().reset_index().sort_values('bookRating', ascending=False)[:10]

# Number of ratings per user
data = df.groupby('userID')['bookRating'].count().clip(upper=50)

# Create trace
trace = go.Histogram(x=data.values,
                     name='Ratings',
                     xbins=dict(start=0,
                                end=50,
                                size=2))
layout = go.Layout(title='Distribution Of Number of Ratings for Users (Clipped at 50)',
                   xaxis=dict(title='Number of Rating for Users'),
                   yaxis=dict(title='Count'),
                   bargap=0.2)

fig = go.Figure(data=[trace], layout=layout)
plot(fig)

结果:直方图中,x轴表示所有用户的评分的次数,Y轴表示用户的数量

 

# 由上面两个直方图呈现的分布特性,为减少计算复杂度,避免在计算时出席那内存泄漏问题,需降低数据维度,将评分次数很低(小于50次)的书籍和用户过滤掉。

 

min_book_ratings = 50
filter_books = df['ISBN'].value_counts() > min_book_ratings
filter_books = filter_books[filter_books].index.tolist()

min_user_ratings = 50
filter_users = df['userID'].value_counts() > min_user_ratings
filter_users = filter_users[filter_users].index.tolist()

df_new = df[(df['ISBN'].isin(filter_books)) & (df['userID'].isin(filter_users))]
print('The original data frame shape:\t{}'.format(df.shape))
print('The new data frame shape:\t{}'.format(df_new.shape))

结果:数据量由114万减少到14万

 

# 我们将使用load_from_df()方法从上面的 pandas dataframe 加载数据,我们还需要一个Reader对象,并且必须指定rating_scale参数。
# 数据必须有包含3列,对应于userID,ISBN和bookRating,必须严格按照这样的顺序排列。

reader = Reader(rating_scale=(0, 9))
data = Dataset.load_from_df(df_new[['userID','ISBN','bookRating']], reader)

benchmark = []
# 尝试所有算法
for algorithm in [SVD(), SVDpp(), SlopeOne(), NormalPredictor(), KNNBaseline(), KNNBasic(), KNNWithMeans(), KNNWithZScore(), BaselineOnly(), CoClustering()]:
    #在交叉集上的表现
    results = cross_validate(algorithm, data, measures=['RMSE'], cv=3, verbose=False)

    # 记录结果
    tmp = pd.DataFrame.from_dict(results).mean(axis=0)
    tmp = tmp.append(pd.Series([str(algorithm).split(' ')[0].split('.')[-1]], index=['Algorithm']))
    benchmark.append(tmp)
surprise_results = pd.DataFrame(benchmark).set_index('Algorithm').sort_values('test_rmse')
print(surprise_results)

结果:

 

 

 

# 训练和预测
# BaselineOnly算法给了我们最好的rmse,因此,我们将使用BaselineOnly训练和预测,并使用交替最小二乘法(ALS)寻找最优值。
print('Using ASL')
bsl_options = {'method': 'als',
               'n_epochs': 5,
               'reg_u': 12,
               'reg_i': 5
               }
algo = BaselineOnly(bsl_options)
cross_validate(algo, data, measures=['RMSE'], cv=3, verbose=False)

# 我们使用train_test_split()方法创建训练集和测试集,并使用rmse作为评估指标。
# 然后我们将使用fit()方法在训练集上进行训练,最后使用test()方法将返回测试集上做出的预测结果。

trainset, testset = train_test_split(data, test_size=0.25)
algo = BaselineOnly(bsl_options=bsl_options)
predictions = algo.fit(trainset).test(testset)
accuracy.rmse(predictions)

结果:

 

 

# 为了详细检查我们的预测,我们将构建一个包含所有预测的pandas的数据集。
def get_Iu(uid):
    """ return the number of items rated by given user
        args:
          uid: the id of the user
        returns:
          the number of items rated by the user
        """
   
try:
        return len(trainset.ur[trainset.to_inner_uid(uid)])
    except ValueError:  # user was not part of the trainset
        return 0

def get_Ui(iid):
    """ return number of users that have rated given item
    args:
      iid: the raw id of the item
    returns:
      the number of users that have rated the item.
    """
   
try:
        return len(trainset.ir[trainset.to_inner_iid(iid)])
    except ValueError:
        return 0


df = pd.DataFrame(predictions, columns=['uid', 'iid', 'rui', 'est', 'details'])
df['Iu'] = df.uid.apply(get_Iu)
df['Ui'] = df.iid.apply(get_Ui)
df['err'] = abs(df.est - df.rui)

best_predictions = df.sort_values(by='err')[:10]
worst_predictions = df.sort_values(by='err')[-10:]
print(best_predictions)
print(worst_predictions)

结果:

 

 

 

 

import matplotlib.pyplot as plt

# get_ipython().run_line_magic('matplotlib', 'inline')

df_new.loc[df_new['ISBN'] == '0440241537']['bookRating'].hist()
plt.xlabel('rating')
plt.ylabel('Number of ratings')
plt.title('Number of ratings book ISBN 0440241537 has received')
plt.show()

结果:

 

posted @ 2020-09-26 17:03  一玎  阅读(862)  评论(0)    收藏  举报