基於物品的協同過濾算法(ItemCF)-- 原理與實戰

在這裏插入圖片描述

1、 ItemCF算法原理:

ItemCF算法並不利用物品的內容屬性計算物品之間的相似度,它主要通過分析用戶的行爲記錄計算物品之間的相似度。該算法認爲一個人的興趣都侷限在幾個方面,當很多人都對兩個物品感興趣時,就認爲這兩個物品具有較大的相似度,即物品A,B具有很大的相似度是因爲喜歡物品A的用戶大都也喜歡物品B。

2、 ItemCF算法步驟:

  1. 計算物品之間的相似度
  2. 根據物品的相似度和用戶的歷史行爲給用戶生成推薦列表。
2.1 物品相似度計算
  1. 建立用戶-物品的倒排表
  2. 對於每個用戶,將用戶列表中的物品兩兩組合,並在共現矩陣中加1
  3. 遍歷共現矩陣,計算出物品相似度矩陣WW,計算公式如下:

Wi,j=N(i)N(j)N(i)N(j) W_{i,j}=\frac{|N(i) \cap N(j)|} {\sqrt{|N(i)| |N(j)|}}

2.2 生成推薦列表

通過如下公式計算用戶u對一個物品j的興趣 :
pu,j=iN(u)S(j,K)Wj,iru,i p_{u,j}=\sum_{i \in N(u) \cap S(j,K)}{W_{j,i}r_{u,i}}
N(u)N(u)是用戶喜歡的物品的集合, S(j,K)S(j,K)是和物品j最相似的K個物品的集合, wj,iw_{j,i}是物品jjii的相似度,$ r_{u,i}$是用戶u對物品i的興趣。

3、ItemCF的優缺點

優點:
  • 推薦結果中長尾物品豐富,適用於用戶個性化需求強烈的領域
  • 可以利用用戶的歷史行爲給推薦結果做出解釋
  • 用戶有新行爲,就會實時導致推薦結果的實時變化
缺點:
  • 適用於物品數明顯小於用戶數的場合;如果物品很多,計算物品的相似度矩陣代價很大

4、優化點

  1. 相似度矩陣的計算中引入對活躍用戶的懲罰(活躍用戶對物品相似度的貢獻應該小於不活躍的用戶 ),增加IUF參數來修正物品相似度的計算公式

Wi,j=uN(i)N(j)1log1+N(U)N(i)N(j) W_{i,j}=\frac{\sum_{u \in |N(i) \cap N(j)|} \frac{1}{log1+|N(U)|}} {\sqrt{|N(i)| |N(j)|}}

  1. 對於過於活躍的用戶,往往忽略他們的興趣列表。
  2. 將ItemCF的相似度矩陣按最大值歸一化,可以提高推薦的準確率和覆蓋率

Wi,j=Wi,jmaxjWi,j W_{i,j} = \frac{W_{i,j}}{\max_{j}W_{i,j}}

實戰

下面代碼使用了電影評分數據集,該數據集可以從這裏下載(提取碼:7k1w )。數據中包含了943個用戶對1682個電影的10W條評分數據,數據已經處理成csv格式,可通過pandas.read_csv直接讀取。

在這裏插入圖片描述

import numpy as np
import pandas as pd
from itertools import combinations, permutations
from operator import itemgetter

def trans_df2dict(df):
    """將數據轉化成字典格式"""
    user_rating = dict()  # 用戶評分數據
    for row in df.values:
        uesr_id, movie_id, rating = row[0], row[1], row[2]
        if uesr_id not in user_rating.keys():
            user_rating[uesr_id] = {}
        user_rating[uesr_id][movie_id] = rating
    return user_rating


def get_items_similarity(df, item_num):
    """計算items相似性矩陣,返回相似性矩陣"""
    # 1. 建立用戶-物品的倒排表
    inverted_table = df.groupby(by='userId')['moviesId'].agg(list).to_dict()

    # 2. 初始化共現矩陣,遍歷每個用戶,將物品兩兩組合,並在共現矩陣中加1
    W = np.zeros((item_num, item_num))

    # 統計每個電影被多少人看過
    count_item_users_num = df.groupby(by='moviesId')['userId'].agg('count').to_dict()

    for key, val in inverted_table.items():
        val.sort(reverse=True)  # 降序
        for per in combinations(val, 2):
            W[per[0] - 1][per[1] - 1] += 1
            W[per[1] - 1][per[0] - 1] += 1

    # 計算相似性
    for i in range(W.shape[0]):
        for j in range(W.shape[1]):
            W[i][j] /= np.sqrt(count_item_users_num.get(i + 1) * count_item_users_num.get(j + 1))

    w_dict = {}
    for i in range(W.shape[0]):
        tmp = []
        for index, k in enumerate(W[i]):
            tmp.append((index + 1, k))
        w_dict[i + 1] = tmp
    return w_dict


def user_interest_with_items(user_id, item_id, K, user_rating, w_dict):
    """計算指定用戶與指定物品的興趣程度"""
    interest = 0
    for i in sorted(w_dict[item_id], key=itemgetter(1), reverse=True)[0:K]:
        item_index = i[0]
        item_simi = i[1]
        if item_index in user_rating[user_id].keys():
            interest += item_simi * user_rating[user_id][item_index]
    return interest


def get_user_interest_list(user_id, K, user_rating, w_dict):
    """計算用戶的興趣列表"""
    rank = []
    item_id_list = w_dict.keys()
    for item_id in item_id_list:
        if item_id in user_rating[user_id].keys():
            continue
        interest = user_interest_with_items(user_id, item_id, K, user_rating, w_dict)
        rank.append((item_id, interest))
    return sorted(rank, key=itemgetter(1), reverse=True)


if  __name__ == '__main__':
    df = pd.read_csv('./ml-100k.csv')
    item_num = df.moviesId.nunique()
    user_num = df.userId.nunique()
    
    user_rating = trans_df2dict(df)
    
    w_dict = get_items_similarity(df, item_num)
    
    recommend_list = get_user_interest_list(2, 20, user_rating, w_dict)
    print(recommend_list[0:20])

輸出的前20個推薦的電影ID:
在這裏插入圖片描述

參考資料:

推薦系統實戰

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章