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)