一、推荐系统相关
互联网上有海量的信息工人选择使用,也正是面临这种大量信息使人选择起来耗费大量时间精力或者不知怎么选择。推荐系统本质上一种信息过滤工程,使用一定的计算方法将不太可能让人产生行为的信息过滤掉,对偶的来说也就是保留推荐大概率会让人产生行为的信息。
现在的推荐系统主要思路有基于内容、协同过滤、混合系统
(1)基于内容:针对用户之前的行为,推荐与之前内容相似的信息,使用户再次产生行为。
(2)协同过滤:相似的人往往会对类似的信息感兴趣,通过相似用户的行为进行推荐
(3)混合:即将以上两种方式融合
推荐的流程框架:
(1)召回:从数据中快速产生一个候选集
(2)计分:对候选集进行评分和排序
(3)重排:考虑其他的限制(如用户已经标注不喜欢)进行最后排序
搭建步骤:
(1)准备数据
(2)选择算法
目前主流的推荐算法使预估点击率的算法,GBDT、LR、FM(因子分解)、FFM(基于域的因子分解)、XGBoost、nn
(3)模型训练
(4)效果评估
二、实现一个电影推荐系统
这里实现一个电影推荐系统。基本思路是:行为相似的用户具有相似的喜好,通过对比用户对同一部电影的评价得到喜好相近的用户,再将此用户评价高的电影推荐给他的相似用户,实现推荐。
(1)模型1
#导入库
import os
import json
import random
import math
import re
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'SimHei'
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
数据为json格式,内容为用户ID、电影ID以及电影评分
#将json数据读入字典
train = dict()
train = json.load(open('train.json'))
构造皮尔逊相关系数,通过两个用户对电影评分的打分情况计算两个用户的喜好相近程度
#皮尔逊相关系数函数
def pearson(rating1,rating2):
sum_xy = 0
sum_x = 0
sum_y = 0
sum_x2 = 0
sum_y2 = 0
num = 0
#循环两个用户的电影评分
for key in rating1.keys():
if key in rating2.keys():
num += 1
x = rating1[key] #用户1评分
y = rating2[key] #用户2评分
sum_xy += x * y
sum_x += x
sum_y += y
sum_x2 += math.pow(x, 2)
sum_y2 += math.pow(y, 2)
if num == 0:
return 0
# 皮尔逊相关系数分母
denominator = math.sqrt(sum_x2-math.pow(sum_x, 2) / num) * math.sqrt( sum_y2-math.pow(sum_y, 2) / num)
if denominator == 0:
return 0
else:
return (sum_xy-(sum_x*sum_y) / num) / denominator
用户推荐函数,通过相关性可以将用户给高分的电影推荐给相关性强的用户
#用户ID推荐
def recommend_1(userID, k=15):
neighborUser = dict()
for user in train.keys():
if userID != user:
distance = pearson(train[userID], train[user]) #计算相关性
neighborUser[user]=distance
#字典排序
newNU = sorted(neighborUser.items(), key=lambda k:k[1], reverse=True)
movies = dict()
for (sim_user, sim) in newNU[:k]:
for movieID in train[sim_user].keys():
movies.setdefault(movieID, 0)
movies[movieID] += sim * train[sim_user][movieID]
newMovies = sorted(movies.items(), key=lambda k:k[1], reverse=True)
return newMovies
模型使用
#计算两个用户的相似度
pearson_r = pearson(train['1738198'], train['428296'])
pearson_r
#给用户推荐电影
recommend_movies = recommend_1('1738198')
recommend_movies
(2)模型2
模型1的数据是已经被整理过的json数据,模型2使用未被整理的数据
#用户评分数据
df = pd.read_csv('combined_data_1.txt', header=None, names=['customer_id', 'rating', 'date'], usecols=[0, 1]) #读取数据
df['rating'] = df['rating'].astype(float) #将rating列转为字符串
#电影信息数据
df_title = pd.read_csv('movie_titles.csv',
encoding="ISO-8859-1", header=None, names=['movie_id', 'year', 'name'])
df_title.set_index('movie_id', inplace = True)
截取部分数据,减小运算压力,df = df[:1000000]
#各分数频数
p = df.groupby('rating')['rating'].agg(['count'])
#通过计算数据中的缺失列得到电影数
movie_count = df.isnull().sum()[1]
#计算用户个数
cust_count = df['customer_id'].nunique() - movie_count
#计算评分个数(总数据减去电影数)
rating_count = df['customer_id'].count() - movie_count
#画出每个评分在数据中的占比
ax = p.plot(kind='barh', legend = False, figsize = (10, 5))
plt.title('{:,} 个电影, {:,} 个用户, {:,} 条记录'.format(movie_count, cust_count, rating_count))
plt.axis('off')
for i in range(1,6):
ax.text(p.iloc[i-1][0]/4, i-1, 'Rating {}: {:.0f}%'.format(i, p.iloc[i-1][0]*100 / p.sum()[0]), color='white', weight='bold')
#通过空值否将数据转为布尔值
df_nan = pd.DataFrame(pd.isnull(df.rating))
#取出布尔值为真的行(没有评分的行)
df_nan = df_nan[df_nan['rating'] == True]
#没有评分的行为电影ID
df_nan = df_nan.reset_index()
movie_np = []
movie_id = 1
#
for i,j in zip(df_nan['index'][1:], df_nan['index'][:-1]):
# numpy approach
temp = np.full((1, i-j-1), movie_id)
movie_np = np.append(movie_np, temp)
movie_id += 1
# Account for last record and corresponding length
# numpy approach
last_record = np.full((1,len(df) - df_nan.iloc[-1, 0] - 1), movie_id)
movie_np = np.append(movie_np, last_record)
# 将电影ID列取出
df = df[pd.notnull(df['rating'])]
#放入数据
df['movie_id'] = movie_np.astype(int)
df['customer_id'] = df['customer_id'].astype(int)
f = ['count', 'mean']
#计算频数和均值
df_movie_summary = df.groupby('movie_id')['rating'].agg(f)
df_movie_summary.index = df_movie_summary.index.map(int)
#剔除数少数movie
movie_benchmark = round(df_movie_summary['count'].quantile(0.8), 0)
drop_movie_list = df_movie_summary[df_movie_summary['count'] < movie_benchmark].index
#剔除少数customer
df_cust_summary = df.groupby('customer_id')['rating'].agg(f)
df_cust_summary.index = df_cust_summary.index.map(int)
cust_benchmark = round(df_cust_summary['count'].quantile(0.8),0)
drop_cust_list = df_cust_summary[df_cust_summary['count'] < cust_benchmark].index
将数据pivot转为,行为用户ID,列为电影ID,数值为评分,行列计算相关性
#将数据pivot,转为用户为行,电影为列
df_p = pd.pivot_table(df, values='rating', index='customer_id', columns='movie_id')
定义推荐函数
def recommend_2(movie_title, min_count):
print('依据电影:{}'.format(movie_title))
print('10部推荐电影:')
#依据电影名取出电影ID
i = int(df_title.index[df_title['name'] == movie_title][0])
#放入矩阵得到分数
target = df_p[i]
#计算相关性
similar_to_target = df_p.corrwith(target)
#生成DataFrame
corr_target = pd.DataFrame(similar_to_target, columns=['pearson_r'])
corr_target.dropna(inplace = True)
#降序重排
corr_target = corr_target.sort_values('pearson_r', ascending = False)
corr_target.index = corr_target.index.map(int)
corr_target = corr_target.join(df_title).join(df_movie_summary)[['pearson_r', 'name', 'count', 'mean']]
#选取10相关性最强
print(corr_target[corr_target['count']>min_count][:10].to_string(index=False))
recommend_2(‘Love Reinvented’, 0),使用推荐函数进行推荐