推薦系統實踐-1基於用戶的協同過濾算法學習與實現


本隨筆主要記錄本人對協同過濾算法的學習理解與Python的實現,主要參考資料為項亮老師的《推薦系統實踐》和Prateek Joshi 老師的《Python機器學習經典實例》兩本書。

一.基於用戶的協同過濾簡介

利用用戶行為數據構建推薦系統有三類算法:基於鄰域的算法、隱語義模型和基於圖的模型。

基於鄰域的算法主要有基於用戶的協同過濾算法和基於物品的協同過濾算法,這里要學習的是基於用戶的協同過濾算法。

基於用戶的協同過濾算法(UserCF)是推薦系統中最古老的算法。可以不誇張地說,這個算法的誕生標志了推薦系統的誕生。

 

二.算法思路

當一個用戶 A 需要個性化推薦時,可以先找到和他有相似興趣的其他用戶,然后把那些用戶喜歡的、而用戶 A 沒有聽說過的物品推薦給 A 。這種方法稱為基於用戶的協同過濾算法。

從上面的描述中可以看到,基於用戶的協同過濾算法主要包括兩個步驟。

(1) 找到和目標用戶興趣相似的用戶集合。
(2) 找到這個集合中的用戶喜歡的,且目標用戶沒有聽說過的物品推薦給目標用戶。

幾個關鍵的問題:

用戶之間的興趣相似度如何度量?

答:目前提供四種選擇

首先得到下面的評分矩陣數據結構(矩陣或是字典),橫軸為用戶,縱軸為物品,Fij為用戶i對物品j的評分值(可以看作已經選擇了該物品),空值為未選擇該物品。

1. 歐式距離,有選擇物品為1,未選擇物品為0,可以計算兩個用戶選擇物品向量的歐式距離。

2.Jaccard 公式,分子為兩個用戶共同選擇的物品數,分母為兩個用戶選擇物品的並集物品數。

 

3.余弦相似度,分子為兩個用戶共同選擇的物品數,分母為兩個用戶選擇物品數乘積的平方根。

4.對余弦相似度的改進,|N(i)|為選擇物品i的用戶數(物品i的流行度),物品i是兩個用戶共同選擇的物品。分子為基於這些物品的流行度的算式結果的和,分母為兩個用戶選擇物品數乘積的平方根。

 

三.Python實現

1.數據

這里使用MovieLens電影評分數據集進行實驗,包含6040個用戶,3900個電影,1000209個評分。可以自己下載,或者網盤連接提取。

鏈接:https://pan.baidu.com/s/1ZLnw4-z88GAYTdgf_ZAq9A
提取碼:fqx3

數據文件夾如下,

README為數據介紹,

movies為電影信息數據,有電影ID,電影名稱,電影風格三個屬性。

users為用戶信息數據,有用戶ID,性別,年齡,職業,zip-code五個屬性。

ratings為用戶評分數據,有用戶ID,電影ID,評分三個屬性。

2.目標

使用MovieLens電影評分數據集構建一個推薦系統引擎。推薦引擎可以對數據集中的用戶推薦若干電影(不涉及冷啟動問題),輸入用戶ID,返回給該用戶的電影推薦候選集。

3.代碼結構

代碼結構如下,

UserDF_1用於讀取原始數據,並將評分數據構建成字典並存儲為json文件持久化,即得到評分矩陣(見二部分)。

UserDF_2用於讀取評分矩陣,計算用戶興趣相似度矩陣且建成字典存儲為json文件持久化,即得到用戶興趣相似度矩陣。

圖用戶興趣相似度矩陣。

UserDF_3用於基於評分矩陣和用戶興趣相似度矩陣計算目標用戶的TopN個候選推薦電影的集合。

UserDF_1.py

#coding=utf-8
#基於用戶的協同過濾算法

#1.讀取數據
#2.構建需要的數據結構
#3.計算用戶間的興趣相似度 (余弦相似度),取K個與目標用戶興趣最相似用戶
#4.計算目標用戶對其未觀看電影感興趣程度  (在K的興趣最相似用戶觀看的且目標用戶未觀看的電影中,計算目標用戶對每個電影的感興趣程度,
# 感興趣程度=K個相似用戶對電影有評分記錄的用戶的相似度之和)

import pandas as pd
import numpy as np
from sklearn.model_selection import KFold
import json
import pickle

#1-n:讀取數據,將原始數據轉換為方便計算的數據結構
r_nanes = ['userid','movieid','rating','timestamp']
ratings_data = pd.read_table(filepath_or_buffer=u'E:\DataSet\ml-1m-電影推薦\ml-1m\\ratings.dat',
                             sep='::',header=None,names=r_nanes,engine='python')

print(ratings_data.columns)
print(ratings_data.shape)
print(ratings_data.head(5))

u_nanes = ['userid','gender','age','occupation','zip-code']
users_data = pd.read_table(filepath_or_buffer=u'E:\DataSet\ml-1m-電影推薦\ml-1m\\users.dat',
                           sep='::',header=None,names=u_nanes,engine='python')
print(users_data.columns)
print(users_data.shape)

m_nanes = ['movieid','title','genres']
movies_data = pd.read_table(filepath_or_buffer=u'E:\DataSet\ml-1m-電影推薦\ml-1m\\movies.dat',
                            sep='::',header=None,names=m_nanes,engine='python')

print(movies_data.columns)
print(movies_data.shape)


num_user = len(users_data['userid'])
num_movie = len(movies_data['movieid'])

print(num_user)
print(num_movie)


userids = users_data['userid'].values
movieids = movies_data['movieid'].values

ratings_data_narray = ratings_data.values

userid_movieid_rating = {}

key = 0
for userid in userids:
    print('userid:',userid)
    ratings_dict = {}
    for i in (range(len(ratings_data_narray))[key:]):
        if ratings_data_narray[i][0]==userid:
            ratings_dict[str(ratings_data_narray[i][1])]=str(ratings_data_narray[i][2])
            print(key)
            key += 1
        else:
             continue
    userid_movieid_rating[str(userid)]=ratings_dict
    print('----------------')

print(key)
print(len(userid_movieid_rating))



with open(file='userid_movieid_rating.json',mode='w') as f:
    json.dump(userid_movieid_rating,f)

UserDF_2.py

import numpy as np
import json


# 2-n讀取持久化數據,計算用戶相似度,得到用戶相似度矩陣
def simlar_score(dataset, user1, user2):
    if user1 not in dataset:
        raise TypeError('User ' + user1 + ' not present in the dataset')
    if user1 not in dataset:
        raise TypeError('User ' + user2 + ' not present in the dataset')

    Itemset_user1 = list(dataset[user1].keys())
    Itemset_user2 = list(dataset[user2].keys())
    #print(type(Itemset_user1))


    intersection = np.intersect1d(Itemset_user1, Itemset_user2)
    # if intersection!=[]:
    #     print(type(Itemset_user1))
    #     print(Itemset_user2)
    #     print(intersection)

    numerator = len(intersection)

    #print(numerator)
    denominator = np.sqrt(len(Itemset_user1) * len(Itemset_user2))
    #print(denominator)

    simlar_score = numerator / denominator
    #print(simlar_score)

    return simlar_score


with open(file='userid_movieid_rating.json',mode='r') as f:
    dataset = json.load(f)

users_simlar_score = {}
for user1 in dataset:
    print(user1)
    user_user_simlar_scores = {}
    for user2 in dataset:
        simlar_sc = simlar_score(dataset,user1,user2)
        user_user_simlar_scores[user2] = str(simlar_sc)
    users_simlar_score[user1] = user_user_simlar_scores

with open(file='users_simlar_score.json',mode='w') as f:
    json.dump(users_simlar_score,f)

UserDF_3.py

import numpy as np
import json


#取得一個用戶的作品
# def getItemOfUser(dataset_users_rating,userid):
#
#     items = list[dataset_users_rating[userid].keys()]
#
#     return items

#獲取目標用戶的推薦候選集
def getRecommendationItemForUser(dataset_users_rating,dataset_users_simlar,tuserid,K=5,topN=500):

    #得到目標用戶與其他用戶的相似度
    item_ranks = np.array([[userid,float(users_simalar)] for userid,users_simalar in dataset_users_simlar[tuserid].items()])
    #按相似度排序(由大到小),取得前K個其他用戶的[用戶ID,相似度值]
    item_ranks_K_ousers = item_ranks[np.argsort(item_ranks[:,1])[::-1]][1:K+1]
    #print(item_ranks_K_ousers)

    #得到相似度最大的K的用戶的用戶ID
    K_ousers = item_ranks_K_ousers[:,0]
    print('與目標用戶興趣最相似的',str(K),'個用戶')
    print(K_ousers)
    print('----------')

    #得到似度最大的K的用戶的選擇物品的並集
    items_candidate = []
    for userid in K_ousers:
        items_user = list(dataset_users_rating[userid].keys())
        #print(len(items_user))

        items_candidate = np.union1d(items_candidate,items_user)

    print('似度最大的K的用戶的選擇物品的並集,物品數量為:')
    print(len(items_candidate))
    print('----------')

    #得到在似度最大的K的用戶的選擇物品的並集中且不在目標用戶選擇物品集合中的物品作為推薦候選集合
    RecommendationItems = np.setdiff1d(items_candidate,list(dataset_users_rating[tuserid].keys()))

    print('在似度最大的K的用戶的選擇物品的並集中且不在目標用戶選擇物品集合中的物品作為推薦候選集合,物品數量為:')
    print(len(RecommendationItems))
    print('----------')

    #print(RecommendationItems)

    #計算用戶對於所有候選集合物品的偏好度,偏好度=K個用戶中對物品有行為的用戶與目標用戶的相似度的和
    user_like_scores = {}
    for item_r in RecommendationItems:
        user_like_score = []
        for user in K_ousers:
            if item_r in list(dataset_users_rating[user].keys()):
                #print(tuserid,user)
                #print(dataset_users_simlar[tuserid][user])
                user_like_score.append(float(dataset_users_simlar[tuserid][user]))
        user_like_scores[item_r] = str(sum(user_like_score))

    #按偏好度排序取topN
    user_like_scores_list = np.array([[item,like_score] for item,like_score in user_like_scores.items()])
    RecommendationItems_topN = user_like_scores_list[np.argsort(user_like_scores_list[:,1])[::-1]][:topN]

    #print(len(RecommendationItems_topN),'--------------')
    print('興趣最高的top',str(topN),'個物品及用戶興趣度:')
    print(RecommendationItems_topN)
    print('----------')

    #返回topN的物品名稱
    return RecommendationItems_topN[:,0]



if __name__ == '__main__':

    with open(file='userid_movieid_rating.json', mode='r') as f:
        dataset_users_rating = json.load(f)

    with open(file='users_simlar_score.json', mode='r') as f:
        dataset_users_simlar = json.load(f)

    tuserid = '10'
    topN = 100
    K = 3

    RecommendationItems_topN = getRecommendationItemForUser(dataset_users_rating, dataset_users_simlar,
                                                            tuserid,K=K,topN=topN)
    print('用戶',tuserid,'的top',str(topN),'推薦物品列表:')
    print(RecommendationItems_topN)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM