【機器學習】【SVD-5】SVD在推薦(策略:TopK)系統中的應用簡介 + 示例展示 + Python代碼實現

1.SVD在推薦系統中的應用簡介

此部分可以詳見:SVD奇異值分解的基本原理介紹

2.SVD的TopK推薦系統的簡介

TopK推薦策略就是指:找到和新用戶最高相似度的TopK舊用戶,將這k箇舊用戶評分而新用戶未評分的所有商品推薦給新用戶

每個舊用戶對商品的評分都有一個權值,權值大小正相關於和新用戶的相似度

注:本文系統還實現了推薦的商品是按照綜合評分降序的順序給出的~,用pandas.DataFrame很容易達到

3.svd TopK推薦系統的Python實現(Release版本)

3.1代碼

# -*- coding: utf-8 -*-
"""
@author: 蔚藍的天空Tom
Talk is cheap, show me the code
Aim:基於svd的推薦系統(推薦策略:topK)的代碼實現(Release版本)
"""

import numpy as np
import pandas as pd
from pandas import DataFrame

class CSVD(object):
    '''
    實現基於svd的推薦系統
    推薦策略:找到和新用戶相似度最高的舊用戶,找到舊用戶評分商品但新用戶沒有評分的商品,將這些商品推薦給新用戶
    此係統是按照評分降序的順序將商品推薦給用戶
    '''
    
    def __init__(self, data):
        self.data = data       #用戶數據
        self.S = []  #用戶數據矩陣的奇異值序列 singular values
        self.U = []  #svd後的單位正交向量
        self.VT = []  #svd後的單位正交向量
        self.k = 0   #滿足self.p的最小k值(k表示奇異值的個數)
        self.SD = [] #對角矩陣,對角線上元素是奇異值 singular values diagonal matrix
        self.n = np.shape(data)[0] #用戶對商品的評分矩陣中,商品個數
        self.m = np.shape(data)[1] #用戶對商品的評分矩陣中,用戶個數
        
    def _svd(self):
        '''
        用戶數據矩陣的svd奇異值分解
        '''
        self.U, self.S, self.VT = np.linalg.svd(self.data)
        return self.U, self.S, self.VT
        
    def _calc_k(self, percentge):
        '''確定k值:前k個奇異值的平方和佔比 >=percentage, 求滿足此條件的最小k值
        :param percentage, 奇異值平方和的佔比的閾值
        :return 滿足閾值percentage的最小k值
        '''
        self.k = 0
        #用戶數據矩陣的奇異值序列的平方和
        total = sum(np.square(self.S))
        svss = 0 #奇異值平方和 singular values square sum
        for i in range(np.shape(self.S)[0]):
            svss += np.square(self.S[i])
            if (svss/total) >= percentge:
                self.k = i+1
                break
        return self.k

    def _buildSD(self, k):
        '''構建由奇異值組成的對角矩陣
        :param k,根據奇異值開放和的佔比閾值計算出來的k值
        :return 由k個前奇異值組成的對角矩陣
        '''
        #方法1:用數組乘方法
        self.SD = np.eye(self.k) * self.S[:self.k]

        #方法2:用自定義方法
        e = np.eye(self.k)
        for i in range(self.k):
            e[i,i] = self.S[i]

        return self.SD
        
    def _dimReduce(self, percentage):
        '''
        SVD降維
        :param percentage, 奇異值開方和的佔比閾值
        :return 降維後的用戶數據矩陣
        '''
        #Step1:svd奇異值分解
        self._svd()
        #Step2:計算k值
        self._calc_k(percentage)
        print('\n按照奇異值開方和佔比閾值percentage=%d, 求得降維的k=%d'%(percentage, self.k))
        #Step3:構建由奇異值組成的對角矩陣singular values diagonal
        self._buildSD(self.k)
        k,U,SD,VT = self.k,self.U, self.SD, self.VT
        #Step4:按照svd分解公式對用戶數據矩陣進行降維,得到降維壓縮後的數據矩陣
        
        a = U[:len(U), :k]
        b = np.dot(SD, VT[:k, :len(VT)])
        newData = np.dot(a,b)
        return newData

    def TopKUser_Recommand(self, percentage, newUser, topKRatio):
        '''
        給新用戶newUser做出個性化推薦,這裏實現:推送給newUser最有可能進行購買的商品
        :param percentage, 奇異值平方和佔比閾值
        :param newUser,新用戶給所有商品的評分序列
        :param topKRatio,topK所佔的百分比,由topKRatio計算topK,注:floor取整
        :return 給新用戶的推薦商品
        :Note 推薦策略:找到和新用戶最高相似度的TopK舊用戶,將舊用戶評分而新用戶未評分的商品推薦給新用戶
        每個舊用戶對商品的評分都有一個權值,權值大小正相關於和新用戶的相似度
        此係統還實現了推薦的商品是按照綜合評分降序的順序給出的
        '''
        self.data = np.array(self.data)
        #Step1:svd降維,得到降維後的用戶的商品評分矩陣
        newData = self._dimReduce(percentage)
        newData = np.array(newData)
        #Step2:計算新用戶和每個舊用戶的相似度,並找出最高相似度的舊用戶ID
        em = lambda oldUser : np.sqrt(sum(np.multiply(newUser-oldUser, newUser-oldUser)))
        sim = [em(oldUser) for oldUser in newData.T] #新用戶和舊用戶的相似度
        w = np.divide(1.0, sim) #舊用戶權值表
        topID = np.argsort(sim) #相似度降序的舊用戶ID表
        k = int(np.floor(self.n * topKRatio))          #topK中的K
        wUser = self.data[:, topID[:k]] * w[topID[:k]] #topk用戶加權評分
        recommand_user = np.sum(wUser, axis=1)
        
        #Step3:找出舊用戶評分而新用戶未評分的商品
        goodsID = []
        for i in range(np.shape(newUser)[0]):
            if newUser[i] == 0 and recommand_user[i] != 0:
                goodsID.append(i)
        #Step4:按照綜合評分大小依次給出推薦商品
        recommand_goods = {'ID':list(goodsID),
                           'Score':list(recommand_user[goodsID])}
        df = pd.DataFrame(recommand_goods)
        df.index.name = 'row'
        df.columns.name = 'goods'
        df = df.sort_values(by='Score', ascending=False)
        
        return list(df['ID'])
        
def CSVD_manual():
    #訓練數據集,用戶對商品的評分矩陣,行爲多個用戶對單個商品的評分,列爲用戶對每個商品的評分
    data = np.array([[5, 5, 0, 5],
                     [5, 0, 3, 4],
                     [3, 4, 0, 3],
                     [0, 0, 5, 3],
                     [5, 4, 4, 5],
                     [5, 4, 5, 5]])
    percentage = 0.9
    newUser = np.array([5,5,0,0,0,5])
    svdor = CSVD(data)
    topKRatio = 0.6
    recommand_goods = svdor.TopKUser_Recommand(percentage, newUser, topKRatio)
    print('===================================================')
    print('原始用戶對商品的評分矩陣爲:\n', data)
    print('新的用戶評分數據:', newUser)
    print('推薦系統提供的按照綜合評分降序給出的推薦商品列表:', recommand_goods)
    print('===================================================')
    
if __name__=='__main__':
    CSVD_manual()

3.2運行結果

runfile('C:/Users/tom/svd_topk_recommand_release.py', wdir='C:/Users/tom')

按照奇異值開方和佔比閾值percentage=0, 求得降維的k=2
===================================================
原始用戶對商品的評分矩陣爲:
 [[5 5 0 5]
 [5 0 3 4]
 [3 4 0 3]
 [0 0 5 3]
 [5 4 4 5]
 [5 4 5 5]]
新的用戶評分數據: [5 5 0 0 0 5]
推薦系統提供的按照綜合評分降序給出的推薦商品列表: [4, 2, 3]
===================================================

4.SVD TopK推薦系統的Python實現(Debug版本)

4.1代碼

# -*- coding: utf-8 -*-
"""
@author: 蔚藍的天空Tom
Talk is cheap, show me the code
Aim:基於svd的推薦系統(推薦策略:topK)的代碼實現(Debug版本)
"""

import numpy as np
import pandas as pd
from pandas import DataFrame

class CSVD(object):
    '''
    實現基於svd的推薦系統
    推薦策略:找到和新用戶相似度最高的舊用戶,找到舊用戶評分商品但新用戶沒有評分的商品,將這些商品推薦給新用戶
    此係統是按照評分降序的順序將商品推薦給用戶
    '''
    
    def __init__(self, data):
        self.data = data       #用戶數據
        self.S = []  #用戶數據矩陣的奇異值序列 singular values
        self.U = []  #svd後的單位正交向量
        self.VT = []  #svd後的單位正交向量
        self.k = 0   #滿足self.p的最小k值(k表示奇異值的個數)
        self.SD = [] #對角矩陣,對角線上元素是奇異值 singular values diagonal matrix
        self.n = np.shape(data)[0] #用戶對商品的評分矩陣中,商品個數
        self.m = np.shape(data)[1] #用戶對商品的評分矩陣中,用戶個數
        
    def _svd(self):
        '''
        用戶數據矩陣的svd奇異值分解
        '''
        self.U, self.S, self.VT = np.linalg.svd(self.data)
        return self.U, self.S, self.VT
        
    def _calc_k(self, percentge):
        '''確定k值:前k個奇異值的平方和佔比 >=percentage, 求滿足此條件的最小k值
        :param percentage, 奇異值平方和的佔比的閾值
        :return 滿足閾值percentage的最小k值
        '''
        self.k = 0
        #用戶數據矩陣的奇異值序列的平方和
        total = sum(np.square(self.S))
        svss = 0 #奇異值平方和 singular values square sum
        for i in range(np.shape(self.S)[0]):
            svss += np.square(self.S[i])
            if (svss/total) >= percentge:
                self.k = i+1
                break
        return self.k

    def _buildSD(self, k):
        '''構建由奇異值組成的對角矩陣
        :param k,根據奇異值開放和的佔比閾值計算出來的k值
        :return 由k個前奇異值組成的對角矩陣
        '''
        #方法1:用數組乘方法
        self.SD = np.eye(self.k) * self.S[:self.k]

        #方法2:用自定義方法
        e = np.eye(self.k)
        for i in range(self.k):
            e[i,i] = self.S[i]

        return self.SD
        
    def _dimReduce(self, percentage):
        '''
        SVD降維
        :param percentage, 奇異值開方和的佔比閾值
        :return 降維後的用戶數據矩陣
        '''
        #Step1:svd奇異值分解
        self._svd()
        #Step2:計算k值
        self._calc_k(percentage)
        print('\n按照奇異值開方和佔比閾值percentage=%d, 求得降維的k=%d'%(percentage, self.k))
        #Step3:構建由奇異值組成的對角矩陣singular values diagonal
        self._buildSD(self.k)
        k,U,SD,VT = self.k,self.U, self.SD, self.VT
        #Step4:按照svd分解公式對用戶數據矩陣進行降維,得到降維壓縮後的數據矩陣
        
        a = U[:len(U), :k]
        b = np.dot(SD, VT[:k, :len(VT)])
        newData = np.dot(a,b)
        return newData

    def TopKUser_Recommand(self, percentage, newUser, topKRatio):
        '''
        給新用戶newUser做出個性化推薦,這裏實現:推送給newUser最有可能進行購買的商品
        :param percentage, 奇異值平方和佔比閾值
        :param newUser,新用戶給所有商品的評分序列
        :param topKRatio,topK所佔的百分比,由topKRatio計算topK,注:floor取整
        :return 給新用戶的推薦商品
        :Note 推薦策略:找到和新用戶最高相似度的TopK舊用戶,將舊用戶評分而新用戶未評分的商品推薦給新用戶
        每個舊用戶對商品的評分都有一個權值,權值大小正相關於和新用戶的相似度
        此係統還實現了推薦的商品是按照綜合評分降序的順序給出的
        '''
        #debug 降維前的用戶對商品的評分矩陣
        print('商品數%d'%self.n, '用戶數:%d'%self.m)
        self.data = np.array(self.data)
        print('降維前的用戶對商品的評分矩陣爲:\n', self.data)
        #Step1:svd降維,得到降維後的用戶的商品評分矩陣
        newData = self._dimReduce(percentage)
        print('降維後的用戶對商品的評分矩陣爲:\n', newData)
        #Step2:計算新用戶和每個舊用戶的相似度,並找出最高相似度的舊用戶ID
        em = lambda oldUser : np.sqrt(sum(np.multiply(newUser-oldUser, newUser-oldUser)))
        sim = [em(oldUser) for oldUser in self.data.T] #新用戶和舊用戶的相似度
        w = np.divide(1.0, sim) #舊用戶權值表
        topID = np.argsort(sim) #相似度降序的舊用戶ID表
        k = int(np.floor(self.n * topKRatio))          #topK中的K
        debug_topKUser = self.data[:,topID[:k]]        #topK用戶評分
        wUser = self.data[:, topID[:k]] * w[topID[:k]] #topk用戶加權評分
        recommand_user = np.sum(wUser, axis=1)
        print('相似度:',sim)
        print('權值表:', w)
        print('相似度降序的用戶ID:', topID)
        print('topK, k=',k)
        print('相似度最高的top%d用戶ID:'%k,  topID[:k])
        print('相似度最高的top%d用戶權值:'%k, w[topID[:k]])
        print('相似度最高的top%d用戶評分:\n'%k, np.array(debug_topKUser))
        print('相似度最高的top%d用戶加權評分:\n'%k, np.array(wUser))
        print('綜合評分的商品列表:\n', recommand_user)
        
        #Step3:找出舊用戶評分而新用戶未評分的商品
        goodsID = []
        for i in range(np.shape(newUser)[0]):
            if newUser[i] == 0 and recommand_user[i] != 0:
                goodsID.append(i)
        
        #Step4:按照綜合評分大小依次給出推薦商品
        recommand_goods = {'ID':list(goodsID),
                           'Score':list(recommand_user[goodsID])}
        df = pd.DataFrame(recommand_goods)
        df.index.name = 'row'
        df.columns.name = 'goods'
        df = df.sort_values(by='Score', ascending=False)
        print('按照綜合評分降序的推薦商品信息爲:\n', df)
        print('按照綜合評分降序的推薦商品ID爲:', list(df['ID']))
        
        return list(df['ID'])
        
def CSVD_manual():
    #訓練數據集,用戶對商品的評分矩陣,行爲多個用戶對單個商品的評分,列爲用戶對每個商品的評分
    data = np.array([[5, 5, 0, 5],
                     [5, 0, 3, 4],
                     [3, 4, 0, 3],
                     [0, 0, 5, 3],
                     [5, 4, 4, 5],
                     [5, 4, 5, 5]])
    percentage = 0.9
    newUser = np.array([5,5,0,0,0,5])
    svdor = CSVD(data)
    #svdor.TopOneUser_Recommand(percentage, newUser)
    topKRatio = 0.6
    recommand_goods = svdor.TopKUser_Recommand(percentage, newUser, topKRatio)
    print('===================================================')
    print('原始用戶對商品的評分矩陣爲:\n', data)
    print('新的用戶評分數據:', newUser)
    print('推薦系統提供的按照綜合評分降序給出的推薦商品列表:', recommand_goods)
    print('===================================================')
    
if __name__=='__main__':
    CSVD_manual()

4.2運行結果

runfile('C:/Users/tom/svd_topk_recommand_debug.py', wdir='C:/Users/tom')
商品數6 用戶數:4
降維前的用戶對商品的評分矩陣爲:
 [[5 5 0 5]
 [5 0 3 4]
 [3 4 0 3]
 [0 0 5 3]
 [5 4 4 5]
 [5 4 5 5]]

按照奇異值開方和佔比閾值percentage=0, 求得降維的k=2
降維後的用戶對商品的評分矩陣爲:
 [[ 5.28849359  5.16272812  0.21491237  4.45908018]
 [ 3.27680994  1.90208543  3.74001972  3.80580978]
 [ 3.53241827  3.54790444 -0.13316888  2.89840405]
 [ 1.14752376 -0.64171368  4.94723586  2.3845504 ]
 [ 5.07268706  3.66399535  3.78868965  5.31300375]
 [ 5.10856595  3.40187905  4.6166049   5.58222363]]
相似度: [5.8309518948453007, 7.6157731058639087, 8.3666002653407556, 6.6332495807107996]
權值表: [ 0.17149859  0.13130643  0.11952286  0.15075567]
相似度降序的用戶ID: [0 3 1 2]
topK, k= 2
相似度最高的top2用戶ID: [0 3]
相似度最高的top2用戶權值: [ 0.17149859  0.15075567]
相似度最高的top2用戶評分:
 [[5 5]
 [5 4]
 [3 3]
 [0 3]
 [5 5]
 [5 5]]
相似度最高的top2用戶加權評分:
 [[ 0.85749293  0.75377836]
 [ 0.85749293  0.60302269]
 [ 0.51449576  0.45226702]
 [ 0.          0.45226702]
 [ 0.85749293  0.75377836]
 [ 0.85749293  0.75377836]]
綜合評分的商品列表:
 [ 1.61127129  1.46051561  0.96676277  0.45226702  1.61127129  1.61127129]
按照綜合評分降序的推薦商品信息爲:
 goods  ID     Score
row                
2       4  1.611271
0       2  0.966763
1       3  0.452267
按照綜合評分降序的推薦商品ID爲: [4, 2, 3]
===================================================
原始用戶對商品的評分矩陣爲:
 [[5 5 0 5]
 [5 0 3 4]
 [3 4 0 3]
 [0 0 5 3]
 [5 4 4 5]
 [5 4 5 5]]
新的用戶評分數據: [5 5 0 0 0 5]
推薦系統提供的按照綜合評分降序給出的推薦商品列表: [4, 2, 3]
===================================================

在推薦時,也可以給定一個最小評分閾值,小於此最小評分閾值的商品可以不被推薦

(end)

發佈了248 篇原創文章 · 獲贊 190 · 訪問量 41萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章