Python 實現協同過濾算法

 一、推薦系統

在信息暴漲的時代,每天大量的微博轉載和創作,給用戶不斷更新信息的同時,也增加了用戶篩選信息的難度,當用戶有明確的需求時可以使用搜索引擎。但是在用戶沒有明確的需求時,只是爲了打發時間,在微博中爲了給用戶篩選出他們感興趣的信息,就要分析用戶的興趣,從海量的信息中選擇與用戶興趣相似的信息,並將此推薦給用戶。推薦系統(Recommendation System RS)被提出,推薦系統的任務就是能夠鏈接信息和用戶,幫助用戶找到其感興趣的信息,同時讓一些有價值的信息能夠觸達到潛在的用戶。

推薦算法是根據用戶的歷史行爲,挖掘出用戶的喜好,併爲用戶推薦與其喜好相符的商品或者信息。

推薦系統的核心問題是爲用戶推薦與其興趣相似度比較高的商品。爲此需要函數 f(x)計算候選商品與用戶之間的相似度,並向用戶推薦相似度比較高的商品。爲了預測函數 f(x)可用的歷史數據有:用戶的歷史行爲數據、與該用戶相關的其他用戶信息,商品之間的相似性、文本的描述。

二、推薦的常用方法

  1. 協同過濾的推薦:主要依據的是用戶或者項之間的相似性。
  2. 基於內容的推薦:主要依據的是推薦項的性質。
  3. 基於關聯規則的推薦
  4. 基於效用的推薦
  5. 基於知識的推薦
  6. 組合推薦

三、基於協同過濾的推薦

1.協同過濾算法概述

協同過濾(Collaborative Filtering CF)推薦算法是通過在用戶的行爲中尋找特定的模式,並通過該模式爲用戶產生有效推薦,依賴於系統中用戶的行爲數據。

基於協同過濾的推薦算法的核心思想是:通過對用戶的歷史行爲數據的挖掘發現用戶的偏好,基於不同的偏好對用戶進行羣組劃分並推薦品味相似的項。在計算推薦結果的過程中,只與用戶對項的評分有關。

2.協同過濾算法分類

  • 基於項的協同過濾算法:主要依據的是項與項之間的相似性。
  • 基於用戶的協同過濾算法:主要依據的是用戶與用戶之間的相似性。

下圖基於項的協同過濾算法中,用戶 u1、u2、u3,與用戶 u1 互動的商品有 i1,i2,與用戶 u2 互動的商品有 i1,i2,i3,用戶u3 互動的商品 i1。通過計算 i1,i3 商品相似,對於用戶 u3 來說,用戶 u1 互動過的 i3 用戶 u3 爲互動,所以爲用戶 u3 推薦商品 i3。

                                                           

下圖基於用戶的協同過濾算法中,與用戶 u1 互動的商品 i1,i3,用戶 u2 互動的商品 i2,用戶 u3 互動的商品 i1,i3,i4,。通過計算,用戶 u1 和 u3 較爲相似,對於用戶 u1,用戶 u3 互動過的商品 i4 使用戶 u1 未互動過的,所以爲用戶 u1 推薦商品 i4。

                                                   

四、相似度的度量方法

相似性的度量方法必須滿足拓撲學中的度量空間的基本條件:

假設 d 是度量空間 M 上的度量:d:M\times M\rightarrow R, 其中度量 d 滿足:

  • 非負性:d(x,y)\geq 0,x=y取等號
  • 對稱性:d(x,y)=d(y,x)
  • 三角不等式:d(x,z)\leq d(x,y)+d(y,z)

主要介紹三種度量方法: 歐氏距離、 皮爾遜相關係數、 餘弦相似度

1.歐氏距離

在之前的K-Means算法中講到過

2.皮爾遜相關係數

在歐氏距離的計算中,不同特徵之間的量級對歐氏距離的影響比較大,而皮爾遜相關係數的度量方法對量級不敏感,其具體形式爲:

                                                            Corr(X,Y)=\frac{\left \langle X-\bar{X},Y-\bar{Y} \right \rangle}{\left \| X-\bar{X} \right \|\left \| Y-\bar{Y} \right \|}

\left \langle X,Y \right \rangle表示內積,\left \| X \right \|表示向量X的範數。

3.餘弦相似度

餘弦相似度有着與皮爾遜相似度同樣的性質,對量級不敏感,是計算兩個向量的夾角。對於兩個向量X和Y,對應的形式爲:

                                                                   CosSim(X,Y)=\frac{\left \langle X,Y \right \rangle}{\left \| X \right \|\left \| Y \right \|}

五、協同過濾算法實現

我們將用戶——商品數據轉換爲商品——用戶矩陣,並計算商品之間的相似度。

\begin{bmatrix} 4 &3 &0 &5 &0 \\ 5 &0 &4 &4 &0 \\ 4 &0 &5 &0 &3 \\ 2 &3 &0 &1 &0 \\ 0&4 &2 &0 &5 \end{bmatrix}

基於項的協同過濾算法的商品之間的相似度:

 0.         0.39524659 0.76346445 0.82977382 0.26349773
 0.39524659 0.         0.204524   0.47633051 0.58823529
 0.76346445 0.204524   0.         0.36803496 0.63913749
 0.82977382 0.47633051 0.36803496 0.         0.        
 0.26349773 0.58823529 0.63913749 0.         0.        

基於用戶的協同過濾算法的商品之間的相似度 :

0.         0.74926865 0.32       0.83152184 0.25298221
0.74926865 0.         0.74926865 0.49559463 0.1579597 
0.32       0.74926865 0.         0.30237158 0.52704628
0.83152184 0.49559463 0.30237158 0.         0.47809144
0.25298221 0.1579597  0.52704628 0.47809144 0.        

 1.基於項的協同過濾算法進行推薦

# -*- coding: utf-8 -*-
"""
Created on Thu Apr  4 17:24:02 2019

@author: 2018061801
"""
import numpy as np
from user_based_CF import load_data, similarity

def item_based_recommend(data, w, user):
    '''基於商品相似度爲用戶user推薦商品
    input:  data(mat):商品用戶矩陣
            w(mat):商品與商品之間的相似性
            user(int):用戶的編號
    output: predict(list):推薦列表
    '''
    m, n = np.shape(data) # m:商品數量 n:用戶數量
    interaction = data[:,user].T # 用戶user的互動商品信息
    
    # 1、找到用戶user沒有互動的商品
    not_inter = []
    for i in range(n):
        if interaction[0, i] == 0: # 用戶user未打分項
            not_inter.append(i)
            
    # 2、對沒有互動過的商品進行預測
    predict = {}
    for x in not_inter:
        item = np.copy(interaction) # 獲取用戶user對商品的互動信息
        for j in range(m): # 對每一個商品
            if item[0, j] != 0: # 利用互動過的商品預測
                if x not in predict:
                    predict[x] = w[x, j] * item[0, j]
                else:
                    predict[x] = predict[x] + w[x, j] * item[0, j]
    # 按照預測的大小從大到小排序
    return sorted(predict.items(), key=lambda d:d[1], reverse=True)

def top_k(predict, k):
    '''爲用戶推薦前k個商品
    input:  predict(list):排好序的商品列表
            k(int):推薦的商品個數
    output: top_recom(list):top_k個商品
    '''
    top_recom = []
    len_result = len(predict)
    if k >= len_result:
        top_recom = predict
    else:
        for i in range(k):
            top_recom.append(predict[i])
    return top_recom

if __name__ == "__main__":
    # 1、導入用戶商品數據
    print ("------------ 1. load data ------------")
    data = load_data("D:/anaconda4.3/spyder_work/data7.txt")
    # 將用戶商品矩陣轉置成商品用戶矩陣
    data = data.T
    # 2、計算商品之間的相似性
    print ("------------ 2. calculate similarity between items -------------")   
    w = similarity(data)
    # 3、利用用戶之間的相似性進行預測評分
    print ("------------ 3. predict ------------")    
    predict = item_based_recommend(data, w, 0)
    # 4、進行Top-K推薦
    print ("------------ 4. top_k recommendation ------------")
    top_recom = top_k(predict, 2)
    print (top_recom)

結果:

------------ 1. load data ------------
------------ 2. calculate similarity between items -------------
------------ 3. predict ------------
------------ 4. top_k recommendation ------------
[(2, 5.507604598998138), (4, 2.8186967825714824)]

用戶未打分的商品爲2和4, 最終2號打分5.5,,4號打分2.8

2.基於用戶的協同過濾算法進行推薦

# -*- coding: utf-8 -*-
"""
Created on Thu Apr  4 17:32:21 2019

@author: 2018061801
"""
import numpy as np

def load_data(file_path):
    '''導入用戶商品數據
    input:  file_path(string):用戶商品數據存放的文件
    output: data(mat):用戶商品矩陣
    '''
    f = open(file_path)   
    data = []
    for line in f.readlines():
        lines = line.strip().split("\t")
        tmp = []
        for x in lines:
            if x != "-":
                tmp.append(float(x))  # 直接存儲用戶對商品的打分
            else:
                tmp.append(0)
        data.append(tmp)
    f.close()
    
    return np.mat(data)

def cos_sim(x, y):
    '''餘弦相似性
    input:  x(mat):以行向量的形式存儲,可以是用戶或者商品
            y(mat):以行向量的形式存儲,可以是用戶或者商品
    output: x和y之間的餘弦相似度
    '''
    numerator = x * y.T  # x和y之間的額內積
    denominator = np.sqrt(x * x.T) * np.sqrt(y * y.T) 
    return (numerator / denominator)[0, 0]


def similarity(data):
    '''計算矩陣中任意兩行之間的相似度
    input:  data(mat):任意矩陣
    output: w(mat):任意兩行之間的相似度
    '''
    m = np.shape(data)[0]  # 用戶的數量
    # 初始化相似度矩陣
    w = np.mat(np.zeros((m, m)))
    
    for i in range(m):
        for j in range(i, m):
            if j != i:
                # 計算任意兩行之間的相似度
                w[i, j] = cos_sim(data[i, ], data[j, ])
                w[j, i] = w[i, j]
            else:
                w[i, j] = 0
    return w

def user_based_recommend(data, w, user):
    '''基於用戶相似性爲用戶user推薦商品
    input:  data(mat):用戶商品矩陣
            w(mat):用戶之間的相似度
            user(int):用戶的編號
    output: predict(list):推薦列表
    '''
    m, n = np.shape(data)
    interaction = data[user, ]  # 用戶user與商品信息
    
    # 1、找到用戶user沒有互動過的商品
    not_inter = []
    for i in range(n):
        if interaction[0, i] == 0:  # 沒有互動的商品
            not_inter.append(i)
    
    # 2、對沒有互動過的商品進行預測
    predict = {}
    for x in not_inter:
        item = np.copy(data[:, x])  # 找到所有用戶對商品x的互動信息
        for i in range(m):  # 對每一個用戶
            if item[i, 0] != 0:  # 若該用戶對商品x有過互動
                if x not in predict:
                    predict[x] = w[user, i] * item[i, 0]
                else:
                    predict[x] = predict[x] + w[user, i] * item[i, 0]
    # 3、按照預測的大小從大到小排序
    return sorted(predict.items(), key=lambda d:d[1], reverse=True)

def top_k(predict, k):
    '''爲用戶推薦前k個商品
    input:  predict(list):排好序的商品列表
            k(int):推薦的商品個數
    output: top_recom(list):top_k個商品
    '''
    top_recom = []
    len_result = len(predict)
    if k >= len_result:
        top_recom = predict
    else:
        for i in range(k):
            top_recom.append(predict[i])
    return top_recom   
          
if __name__ == "__main__":
    # 1、導入用戶商品數據
    print ("------------ 1. load data ------------")
    data = load_data("D:/anaconda4.3/spyder_work/data7.txt")
    # 2、計算用戶之間的相似性
    print ("------------ 2. calculate similarity between users -------------")   
    w = similarity(data)
    # 3、利用用戶之間的相似性進行推薦
    print ("------------ 3. predict ------------")   
    predict = user_based_recommend(data, w, 0)
    # 4、進行Top-K推薦
    print ("------------ 4. top_k recommendation ------------")
    top_recom = top_k(predict, 2)
    print (top_recom)

結果:

------------ 1. load data ------------
------------ 2. calculate similarity between users -------------
------------ 3. predict ------------
------------ 4. top_k recommendation ------------
[(2, 5.10303902268836), (4, 2.2249110640673515)]

用戶未打分的商品爲2和4, 最終2號打分5.1,,4號打分2.2

 

參考文獻

協同過濾推薦算法

趙志勇——Python 機器學習算法

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