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

1.SVD奇異值分解的基本原理介紹

參見前面博客:SVD奇異值分解的基本原理介紹

2.降維後的數據矩陣的數據相關性

有了SVD降維後的用戶對商品評分的矩陣後,現在有一個新的用戶,怎麼利用降維後的評分矩陣爲此新用戶給出個性化推薦商品呢?這時候就需要利用數據矩陣的數據相關性了。

單刀直入,直接給出數據相關性的求解思路。

Step1:求出原評分矩陣的降維矩陣

 [[ 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]]

Step2:新用戶的評分數據

newUser = np.array([5,5,0,0,0,5])

Step3:確定計算相似度的距離函數

關於機器學習中距離表示的方法有多中,詳見以前博客:【機器學習】5種距離度量方法詳解+Python實現([]+lambda+np.frompyfunc+向量法等多種方法實現)

爲了方便講解,我這裏用了簡單的歐氏距離,其實效果更好的可能是夾角餘弦,都可以,在實際項目中要按照不同性質的評分矩陣,選取最合適的距離方法,上面博客提供了5中方法供挑選。

    def _EM(self, userA, userB):
        '''計算A和B用戶的相似度,使用歐氏距離
        :param userA,用戶A給所有商品的評分序列
        :param userB,用戶B給所有商品的評分序列
        :return userA和userB的相似度
        Note: userA和userB的評分行爲的相似度,距離越小,相似度越高
        '''
        C = userA - userB
        em = np.sqrt(sum(np.multiply(C,C)))
        return em

Step4:求出新用戶和所有舊用戶的相似度,找出最高相似度的舊用戶

最後求得新用戶評分數據和上面4箇舊用戶評分數據的歐氏距離爲:

[5.8309518948453007, 7.6157731058639087, 8.3666002653407556, 6.6332495807107996]

可知歐氏距離越小,新用戶和此舊用戶的相似度越高,可知新用戶和第一個舊用戶的評分行爲相似度最高,即:

新的用戶評分數據: [5 5 0 0 0 5] 和第一個舊用戶的評分數據[5,5,3,0,5,5]的評分行爲相似度最高,即他們倆的相關性最高~

這裏求得和新用戶評分行爲相關性最高的用戶,就是第一個老客戶,其評分數據爲:[5,5,3,0,5,5]

到這裏你應該瞭解了數據的相關性以及求解方法了。

3.SVD推薦系統的基本過程

Step1:準備用戶對商品評分的評分矩陣

Step2:確定並實現定義相似度的函數,我這裏了用例歐氏距離,也可以使用夾角餘弦等距離算法

Step3:根據給定的奇異值平方和佔比閾值percentage,計算降維的k值,然後通過SVD分解公式得到降維後的評分矩陣

Step4:在降維矩陣中,使用相似度函數找到最高相似度的舊用戶數據,找到舊用戶評分而新用戶未評分的商品

Step5:返回step4中商品的評分表和商品表,作爲推薦商品給新用戶

4.SVD推薦系統的Python代碼(Release版本)

4.1代碼

# -*- coding: utf-8 -*-
"""
@author: 蔚藍的天空Tom
Talk is cheap, show me the code
Aim:基於svd的推薦系統(策略:topOne)的代碼實現(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 _EM(self, userA, userB):
        '''計算A和B用戶的相似度,使用歐氏距離
        :param userA,用戶A給所有商品的評分序列
        :param userB,用戶B給所有商品的評分序列
        :return userA和userB的相似度
        Note: userA和userB的評分行爲的相似度,距離越小,相似度越高
        '''
        C = userA - userB
        em = np.sqrt(sum(np.multiply(C,C)))
        return em
    
    def TopOneUser_Recommand(self, percentage, newUser):
        '''
        給新用戶newUser做出個性化推薦,這裏實現:推送給newUser最有可能進行購買的商品
        :param newUser,新用戶給所有商品的評分序列
        :return 給新用戶的推薦商品
        :Note 推薦策略:找到和新用戶最高相似度的舊用戶,將舊用戶評分而新用戶未評分的商品推薦給新用戶
        '''
        #Step1:svd降維,得到降維後的用戶的商品評分矩陣
        newData = self._dimReduce(percentage)
        #Step2:計算新用戶和每個舊用戶的相似度,並找出最高相似度的舊用戶ID
        sim = [] #最終shape=(m,), m爲舊用戶數
        for i in range(self.m):
            sim.append(self._EM(newUser, self.data[:,i]))
        bestUserID = np.argmin(sim)
        bestUser = self.data[:, bestUserID]
        
        #Step3:找出舊用戶評分而新用戶未評分的商品,將這些商品推薦給新用戶
        goodsID = []
        for i in range(np.shape(bestUser)[0]):
            if newUser[i] == 0 and bestUser[i] != 0:
                goodsID.append(bestUser[i])
        
        #Step4:按照評分大小依次給出推薦商品
        recommandGoods = {'ID':list(goodsID),
                          'Score':list(bestUser[goodsID])}
        df = pd.DataFrame(recommandGoods)
        df.index.name = 'row'
        df.columns.name = 'goods'
        df = df.sort_values(by='Score', ascending=False)
        print('按照評分降序的推薦商品信息:\n', df)
        print('按照評分降序的推薦商品ID:\n', 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)
    recommand_goods = svdor.TopOneUser_Recommand(percentage, newUser)
    print('===================================================')
    print('原始用戶對商品的評分矩陣爲:\n', data)
    print('新的用戶評分數據:', newUser)
    print('推薦系統提供的按照綜合評分降序給出的推薦商品列表:', recommand_goods)
    print('===================================================')
    
if __name__=='__main__':
    CSVD_manual()

4.2運行結果

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

按照奇異值開方和佔比閾值percentage=0, 求得降維的k=2
按照評分降序的推薦商品信息:
 goods  ID  Score
row             
1       5      5
0       3      0
按照評分降序的推薦商品ID:
 [5, 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]
推薦系統提供的按照綜合評分降序給出的推薦商品列表: [5, 3]
===================================================

5.SVD推薦系統的Python代碼(Debug版本)

5.1代碼

# -*- coding: utf-8 -*-
"""
@author: 蔚藍的天空Tom
Talk is cheap, show me the code
Aim:基於svd的推薦系統(策略:topOne)的代碼實現(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 _EM(self, userA, userB):
        '''計算A和B用戶的相似度,使用歐氏距離
        :param userA,用戶A給所有商品的評分序列
        :param userB,用戶B給所有商品的評分序列
        :return userA和userB的相似度
        Note: userA和userB的評分行爲的相似度,距離越小,相似度越高
        '''
        C = userA - userB
        em = np.sqrt(sum(np.multiply(C,C)))
        return em
    
    def TopOneUser_Recommand(self, percentage, newUser):
        '''
        給新用戶newUser做出個性化推薦,這裏實現:推送給newUser最有可能進行購買的商品
        :param newUser,新用戶給所有商品的評分序列
        :return 給新用戶的推薦商品
        :Note 推薦策略:找到和新用戶最高相似度的舊用戶,將舊用戶評分而新用戶未評分的商品推薦給新用戶
        '''
        #debug 降維前的用戶對商品的評分矩陣
        print('降維前的用戶對商品的評分矩陣爲:\n', self.data)
        #Step1:svd降維,得到降維後的用戶的商品評分矩陣
        newData = self._dimReduce(percentage)
        print('降維後的用戶對商品的評分矩陣爲:\n', newData)
        #Step2:計算新用戶和每個舊用戶的相似度,並找出最高相似度的舊用戶ID
        sim = [] #最終shape=(m,), m爲舊用戶數
        for i in range(self.m):
            sim.append(self._EM(newUser, self.data[:,i]))
        bestUserID = np.argmin(sim)
        bestUser = self.data[:, bestUserID]
        print('新用戶和每個舊用戶的相似度:',sim)
        print('相似度最高的用戶爲:', bestUserID)
        print('相似度最高的用戶的評分:', bestUser)
        
        #Step3:找出舊用戶評分而新用戶未評分的商品,將這些商品推薦給新用戶
        print('newUser:', newUser)
        print('bestUser:', bestUser)
        goodsID = []
        for i in range(np.shape(bestUser)[0]):
            if newUser[i] == 0 and bestUser[i] != 0:
                goodsID.append(bestUser[i])
        print('求得推薦商品爲:', goodsID)
        
        #Step4:按照評分大小依次給出推薦商品
        recommandGoods = {'ID':list(goodsID),
                          'Score':list(bestUser[goodsID])}
        df = pd.DataFrame(recommandGoods)
        df.index.name = 'row'
        df.columns.name = 'goods'
        df = df.sort_values(by='Score', ascending=False)
        print('按照評分降序的推薦商品信息:\n', df)
        print('按照評分降序的推薦商品ID:\n', 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)
    recommand_goods = svdor.TopOneUser_Recommand(percentage, newUser)
    print('===================================================')
    print('原始用戶對商品的評分矩陣爲:\n', data)
    print('新的用戶評分數據:', newUser)
    print('推薦系統提供的按照綜合評分降序給出的推薦商品列表:', recommand_goods)
    print('===================================================')
    
if __name__=='__main__':
    CSVD_manual()

5.2運行結果

runfile('C:/Users/tom/svd_top1_recommand_debug.py', wdir='C:/Users/tom')
降維前的用戶對商品的評分矩陣爲:
 [[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
相似度最高的用戶的評分: [5 5 3 0 5 5]
newUser: [5 5 0 0 0 5]
bestUser: [5 5 3 0 5 5]
求得推薦商品ID爲: [3, 5]
按照評分降序的推薦商品信息:
 goods  ID  Score
row             
1       5      5
0       3      0
按照評分降序的推薦商品ID:
 [5, 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]
推薦系統提供的按照綜合評分降序給出的推薦商品列表: [5, 3]
===================================================

參考文獻:

見前面文章中的參考文獻:https://blog.csdn.net/u012421852/article/details/80433463

(end)


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