ユーザーの行動は、二部グラフのモデルとして表現しました。ユーザ仮定する\(U \)パーソナライズ推薦、ユーザに対して計算される、すべてのノード\(U \)ユーザPersonalRankから相関度を、\(U \)開始ノードの移動に対応して、各ノードへの(1-Dの\)\の確率とからの移行を停止\(U \)の再起動、またはであり、\(D \)ランダムに一様な分布に応じてノードを選択する確率は、現在のノードから指し示す、歩き続けます歩きます。非常に多くの歩行ラウンドの後、各頂点の確率は収束が、我々は可能性をランク付けするために、この時間を使用することができ、安定してアクセスされます。
アルゴリズムの実装に先立ち、我々は、各ノードの初期確率値を初期化する必要があります。我々はユーザにある場合(U \)\推奨すること、次いでせ\(uは\)を1に初期アクセス確率のノードに対応し、他のノード0へのアクセスの初期確率は、次いで、反復式を用いて算出します。
\ [PR(I)=( 1-D)R_iを+ D \ sum_ {J \(I)中において} \ FRAC {PR(J)} {|アウト(I)|} \\ R_iを= \開始{ケースI = U \\ \} 1 \ I \ 0 \!= U \端{ケース} \]
一般的に2つのアルゴリズムがあり、一つはマトリックスの実現非実装の行列です。
非マトリクス化を達成
ユーザID ItemIDを有する二部グラフの確立。コードにおいて、self.Gグラフは、グローバルを表す有向、ユーザIDとアイテムID異なるプレフィックスを区別するために添加されます。図面に格納されているのに加えて、ユーザがアイテム、互いの方向。その後、それは確率図に転送されます。
ここで、G = dictの(item_user、** user_item)辞書の辞書に2つの意味
import pandas as pd
import time
class PersonalRank:
def __init__(self,X,Y):
X,Y = ['user_'+str(x) for x in X],['item_'+str(y) for y in Y]
self.G = self.get_graph(X,Y)
def get_graph(self,X,Y):
"""
Args:
X: user id
Y: item id
Returns:
graph:dic['user_id1':{'item_id1':1}, ... ]
"""
item_user = dict()
for i in range(len(X)):
user = X[i]
item = Y[i]
if item not in item_user:
item_user[item] = {}
item_user[item][user]=1
user_item = dict()
for i in range(len(Y)):
user = X[i]
item = Y[i]
if user not in user_item:
user_item[user] = {}
user_item[user][item]=1
G = dict(item_user,**user_item)
return G
def recommend(self, alpha, userID, max_depth,K=10):
# rank = dict()
userID = 'user_' + str(userID)
rank = {x: 0 for x in self.G.keys()}
rank[userID] = 1
# 开始迭代
begin = time.time()
for k in range(max_depth):
tmp = {x: 0 for x in self.G.keys()}
# 取出节点i和他的出边尾节点集合ri
for i, ri in self.G.items():
# 取节点i的出边的尾节点j以及边E(i,j)的权重wij,边的权重都为1,归一化后就是1/len(ri)
for j, wij in ri.items():
tmp[j] += alpha * rank[i] / (1.0 * len(ri))
tmp[userID] += (1 - alpha)
rank = tmp
end = time.time()
print('use_time', end - begin)
lst = sorted(rank.items(), key=lambda x: x[1], reverse=True)[:K]
for ele in lst:
print("%s:%.3f, \t" % (ele[0], ele[1]))
if __name__ == '__main__':
moviesPath = '../data/ml-1m/movies.dat'
ratingsPath = '../data/ml-1m/ratings.dat'
usersPath = '../data/ml-1m/users.dat'
# usersDF = pd.read_csv(usersPath,index_col=None,sep='::',header=None,names=['user_id', 'gender', 'age', 'occupation', 'zip'])
# moviesDF = pd.read_csv(moviesPath,index_col=None,sep='::',header=None,names=['movie_id', 'title', 'genres'])
ratingsDF = pd.read_csv(ratingsPath, index_col=None, sep='::', header=None,names=['user_id', 'movie_id', 'rating', 'timestamp'])
X=ratingsDF['user_id'][:1000]
Y=ratingsDF['movie_id'][:1000]
PersonalRank(X,Y).recommend(alpha=0.8,userID=1,max_depth=50,K=30)#输出对用户1推荐的 top10 item
# print('PersonalRank result',rank)
マトリクス化を達成
\ [R =(アルファ\ 1-
)R_O + \アルファM ^ TのR \] 前記\(R&LT \)されている\(M + N \)行、行列は、各行は頂点固定頂点を表しますPR値は、ある\(M + N \)マトリックス1の行は、選択された頂点の電荷が一つだけ値1を作用する固定された頂点であり、残りはゼロ。\(M \) mで+ N行、M + N行列の列は、転送行列値である\(M_ {IJ} = \ FRAC {1} {OUT(I)}、アウトj個の\(I) \他に\ 0 \)エッジを接続していない場合、それは、頂点の逆数、または0です。式に変換することができる:
\ [R&LT =(E- \アルファM ^ T)^ { - } 1(1- \アルファ)R_O \]
前記\((E- \アルファM ^ T)^ { - 1 } \)推奨されるすべての頂点の結果として見ることができ、各列は、頂点アイテム、頂点のPR値を表します。
#-*-coding:utf-8-*-
"""
author:jamest
date:20190310
PersonalRank function with Matrix
"""
import pandas as pd
import numpy as np
import time
import operator
from scipy.sparse import coo_matrix
from scipy.sparse.linalg import gmres
class PersonalRank:
def __init__(self,X,Y):
X,Y = ['user_'+str(x) for x in X],['item_'+str(y) for y in Y]
self.G = self.get_graph(X,Y)
def get_graph(self,X,Y):
"""
Args:
X: user id
Y: item id
Returns:
graph:dic['user_id1':{'item_id1':1}, ... ]
"""
item_user = dict()
for i in range(len(X)):
user = X[i]
item = Y[i]
if item not in item_user:
item_user[item] = {}
item_user[item][user]=1
user_item = dict()
for i in range(len(Y)):
user = X[i]
item = Y[i]
if user not in user_item:
user_item[user] = {}
user_item[user][item]=1
G = dict(item_user,**user_item)
return G
def graph_to_m(self):
"""
Returns:
a coo_matrix sparse mat M
a list,total user item points
a dict,map all the point to row index
"""
graph = self.G
vertex = list(graph.keys())
address_dict = {}
total_len = len(vertex)
for index in range(len(vertex)):
address_dict[vertex[index]] = index
row = []
col = []
data = []
for element_i in graph:
weight = round(1/len(graph[element_i]),3)
row_index= address_dict[element_i]
for element_j in graph[element_i]:
col_index = address_dict[element_j]
row.append(row_index)
col.append(col_index)
data.append(weight)
row = np.array(row)
col = np.array(col)
data = np.array(data)
m = coo_matrix((data,(row,col)),shape=(total_len,total_len))
return m,vertex,address_dict
def mat_all_point(self,m_mat,vertex,alpha):
"""
get E-alpha*m_mat.T
Args:
m_mat
vertex:total item and user points
alpha:the prob for random walking
Returns:
a sparse
"""
total_len = len(vertex)
row = []
col = []
data = []
for index in range(total_len):
row.append(index)
col.append(index)
data.append(1)
row = np.array(row)
col = np.array(col)
data = np.array(data)
eye_t = coo_matrix((data,(row,col)),shape=(total_len,total_len))
return eye_t.tocsr()-alpha*m_mat.tocsr().transpose()
def recommend_use_matrix(self, alpha, userID, K=10,use_matrix=True):
"""
Args:
alpha:the prob for random walking
userID:the user to recom
K:recom item num
Returns:
a dic,key:itemid ,value:pr score
"""
m, vertex, address_dict = self.graph_to_m()
userID = 'user_' + str(userID)
print('add',address_dict)
if userID not in address_dict:
return []
score_dict = {}
recom_dict = {}
mat_all = self.mat_all_point(m,vertex,alpha)
index = address_dict[userID]
initial_list = [[0] for row in range(len(vertex))]
initial_list[index] = [1]
r_zero = np.array(initial_list)
res = gmres(mat_all,r_zero,tol=1e-8)[0]
for index in range(len(res)):
point = vertex[index]
if len(point.strip().split('_'))<2:
continue
if point in self.G[userID]:
continue
score_dict[point] = round(res[index],3)
for zuhe in sorted(score_dict.items(),key=operator.itemgetter(1),reverse=True)[:K]:
point,score = zuhe[0],zuhe[1]
recom_dict[point] = score
return recom_dict
if __name__ == '__main__':
moviesPath = '../data/ml-1m/movies.dat'
ratingsPath = '../data/ml-1m/ratings.dat'
usersPath = '../data/ml-1m/users.dat'
# usersDF = pd.read_csv(usersPath,index_col=None,sep='::',header=None,names=['user_id', 'gender', 'age', 'occupation', 'zip'])
# moviesDF = pd.read_csv(moviesPath,index_col=None,sep='::',header=None,names=['movie_id', 'title', 'genres'])
ratingsDF = pd.read_csv(ratingsPath, index_col=None, sep='::', header=None,names=['user_id', 'movie_id', 'rating', 'timestamp'])
X=ratingsDF['user_id'][:1000]
Y=ratingsDF['movie_id'][:1000]
rank = PersonalRank(X,Y).recommend_use_matrix(alpha=0.8,userID=1,K=30)
print('PersonalRank result',rank)
参考:
推奨システムの概要(A)
のGithub