基于SVD矩阵分解的用户商品推荐(python实现)
加粗樣式## SVD矩陣分解
SVD奇異值分解
優點:簡化數據,去除噪聲,提高算法的結果
缺點:數據的轉換可能難以理解
適用范圍:數值性數據
原始數據data,我們把它分解成3個矩陣。
其中
只有對角元素,是奇異值。奇異值中數值是按從大到小排列的,r個之后數值為0.。 這點高等數學里有。
如果我們把第二個奇異值矩陣中只保留一些值,把其他值全設為0.。從右到左重構數據即得到簡化后的數據。
如奇異值數據中只保留最大2個奇異值,再重構數據。
可以認為原始數據去除了噪聲。
python實現svd
import numpy as np U,sigma,VT=np.linalg.svd([[1,1],[7,7]]) print('U:',U) print('sigma:',sigma) print('VT:',VT)其中sigma以對角矩陣出現,我們需要記住它其實是個矩陣。
svd重構
import numpy as np def loadExData():return [[1,1,1,0,0],[2,2,2,0,0],[1,1,1,0,0],[5,5,5,0,0],[1,1,0,2,2],[0,0,0,3,3],[0,0,0,1,1]] data=loadExData() U,sigma,VT=np.linalg.svd(data) print('U:',U) print('sigma:',sigma) print('VT:',VT)可以發現sigma從大到小排列,如果我們只取前面3個奇異值。
則矩陣分解變成
sig3=np.mat([[sigma[0],0,0],[0,sigma[1],0],[0,0,sigma[2]]])
sig3=np.mat(np.eye(3)*sigma[:3])
我們如何得知我們需要三個奇異值呀。
把奇異值求取平方和,將奇異值平方和累積到總值的0.9或者其他值即停止累積。即得到我們需要幾個奇異值。
用戶商品推薦
用戶商品推薦在現實生活中有很多應用場景。比如如果你在今日頭條上經常看某一類新聞,然后系統會經常給你推送該類新聞而不是其他類。
推薦算法:分為基于商品和基于用戶。
基于商品:是比較商品之間的相似度,如果你喜歡某類電影,如果有一款新的電影出現啦,你還未看過,系統會計算該部電影與你看過的電影的相似度,如果相似度高則認為該類電影是你喜歡的,會把該類電影推送給你。
基于用戶:計算不同用戶之間的相似度,如果相似度高。會把其他用戶看過的電影而你沒有看過的電影推送給你。
1.相識度公式
歐式距離
我們希望相似度在0到1之間,
可以用相似度=1/(1+距離)
皮爾遜相關系數
皮爾遜公式相比較于歐式距離有個優勢:對數值的大小不敏感。比如一個極端值對某一商品打分為5分,而另一個則打1分,皮爾遜系數會認為兩者是等價的。
皮爾遜系數是-1到1之間。我們希望得到的值是0到1之間。
所以0.5+0.5*皮爾遜系數
因為皮爾遜系數得到的是一個系數矩陣,我們只取一個值就可以啦,所以系數后面有[0][1]
from numpy import linalg as la def pearsSim(inA, inB):# 此時兩個向量完全相關return 0.5 + 0.5 * np.corrcoef(inA, inB, rowvar=0)[0][1]余弦距離
余弦距離得到的值為-1到1,我們將其映射到0到1
from numpy import linalg as la def cosSim(inA, inB):num = float(inA.T * inB)denom = la.norm(inA) * la.norm(inB)return 0.5 + 0.5 * (num / denom)2.基于用戶和基于商品方法選擇:
算法復雜度都會隨著數量增加而增加。基于商品是商品增加復雜度增加,基于用戶時用戶數量增加而復雜度增加。對于絕大多數商店而言,用戶數遠遠大于商品數,所以本文選擇基于商品。
3.推薦流程
推薦系統的工作流程:給定一個用戶,系統會為此用戶返回N個最好的推薦菜。
(1):尋找用戶沒有評級打分的菜肴,即在用戶-物品矩陣中的0值。
(2):在用戶沒有評級的所有物品中,對每個物品預計一個可能的評級分數。這就是說我們認為用戶可能對物品的打分(這就是相似度計算的初衷)
(3):對這些物品的評分從高到底進行排序,返回前N 個商品。
4.原始數據
""" 函數說明:加載數據,菜肴矩陣 行:代表人 列:代表菜肴名詞 值:代表人對菜肴的評分,0代表未評分 """ def loadExData():return[[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5],[0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 3],[0, 0, 0, 0, 4, 0, 0, 1, 0, 4, 0],[3, 3, 4, 0, 0, 0, 0, 2, 2, 0, 0],[5, 4, 5, 0, 0, 0, 0, 5, 5, 0, 0],[0, 0, 0, 0, 5, 0, 1, 0, 0, 5, 0],[4, 3, 4, 0, 0, 0, 0, 5, 5, 0, 1],[0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4],[0, 0, 0, 2, 0, 2, 5, 0, 0, 1, 2],[0, 0, 0, 0, 5, 0, 0, 0, 0, 4, 0],[1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]]5.尋找兩個用戶都評級的物品
""" Parameters:dataMat - 訓練數據集user - 用戶編號simMeas - 相似度計算方法item - 未評分的物品編號Returns:評分(0~5之間的值) """ def svdEst(dataMat, user, simMeas, item):#假設user=1# 得到數據集中的物品數目n = np.shape(dataMat)[1]# 初始化兩個評分值simTotal = 0.0ratSimTotal = 0.0# 奇異值分解# 在SVD分解之后,我們只利用包含90%能量值的奇異值,這些奇異值會以Numpy數組形式得以保存U, Sigma, VT = la.svd(dataMat)# 如果要進行矩陣運算,就必須要用這些奇異值構造出一個對角陣Sig4 = np.mat(np.eye(4) * Sigma[: 4])# 利用U矩陣將物品轉換到低維空間中,構建轉換后的物品(物品的4個主要特征)xformedItems = dataMat.T * U[:, :4] * Sig4.I#shape(11,4)# 遍歷行中的每個物品(對用戶評過分的物品進行遍歷,并將它與其他物品進行比較)for j in range(n):userRating = dataMat[user, j]# 如果某個物品的評分值為0,則跳過這個物品if userRating == 0:continue# 相似度的計算也會作為一個參數傳遞給該函數similarity = simMeas(xformedItems[item, :].T, xformedItems[j, :].T)print('商品 %d 和商品 %d 相似度similarity is: %f' % (item, j, similarity))# 相似度會不斷累加,每次計算時還考慮相似度和當前用戶評分的乘積# similarity 用戶相似度 userRating 用戶評分simTotal += similarityratSimTotal += similarity * userRatingif simTotal == 0:return 0# 通過除以所有的評分和,對上述相似度評分的乘積進行歸一化,使得最后評分在0~5之間,這些評分用來對預測值進行排序else:return ratSimTotal / simTotal為了說明該函數功能,我們假設當前用戶user=1,未評分商品為0
dataMat=np.mat(loadExData()) #假設當前用戶時1 print('查看用戶1未評級商品:',np.nonzero(dataMat[1, :].A == 0)[1]) # 得到數據集中的物品數目 n = np.shape(dataMat)[1] # 初始化兩個評分值 simTotal = 0.0 ratSimTotal = 0.0 # 奇異值分解 # 在SVD分解之后,我們只利用包含90%能量值的奇異值,這些奇異值會以Numpy數組形式得以保存 U, Sigma, VT = la.svd(dataMat) # 如果要進行矩陣運算,就必須要用這些奇異值構造出一個對角陣 Sig4 = np.mat(np.eye(4) * Sigma[: 4]) # 利用U矩陣將物品轉換到低維空間中,構建轉換后的物品(物品的4個主要特征) xformedItems = dataMat.T * U[:, :4] * Sig4.I print('降維重構后的數據:',xformedItems)重構后的數據行為商品,列為商品的幾個特征。
實現降維
通過此方法,我們可以給未知得分的商品0打上分啦。
xformedItems[0, :].T 這里有裝置,我們采用的方法是基于商品。
簡單說下該方法思路:
1.對于一個用戶,找到他未打分的商品,和已打分過的商品。
2.計算未打分商品與已打分商品的相似度,這里的相似度計算輸入的所有用戶對該商品的打分。
3.將相似度乘以那個已打分的商品得分值。
4.求和 歸一化 得到未打分商品的得分。未打分商品的得分主要貢獻來源:未知商品與已打分商品相似度,已打分商品的得分
6.尋找前個未評級商品 推薦給用戶
""" 函數說明:推薦引擎Parameters:dataMat - 訓練數據集user - 用戶編號N- 產生N個推薦結果simMeas - 相似度計算方法estMethod - 推薦引擎方法Returns:評分(0~5之間的值) """def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=svdEst):# 尋找未評級的物品# 對給定的用戶建立一個未評分的物品列表unratedItems = np.nonzero(dataMat[user, :].A == 0)[1]# 如果不存在未評分物品,那么就退出函數if len(unratedItems) == 0:return ('本店所有商品你均嘗試過,打過分')# 物品的編號和評分值itemScores = []# 在未評分的物品上進行循環for item in unratedItems:estimatedScore = estMethod(dataMat, user, simMeas, item)# 尋找前N個未評級的物品,調用svdEst()來產生該物品的預測得分,該物品的編號和估計值會放在一個元素列表itemScores中itemScores.append((item, estimatedScore))# 返回元素列表,第一個就是最大值return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[: N]7.全部代碼
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # @Author: yudengwu # @Date : 2020/8/22 import numpy as np from numpy import linalg as la#相似度 def cosSim(inA, inB):num = float(inA.T * inB)denom = la.norm(inA) * la.norm(inB)return 0.5 + 0.5 * (num / denom)#數據導入 """ 函數說明:加載數據,菜肴矩陣 行:代表人 列:代表菜肴名詞 值:代表人對菜肴的評分,0代表未評分 """def loadExData():return[[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5],[0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 3],[0, 0, 0, 0, 4, 0, 0, 1, 0, 4, 0],[3, 3, 4, 0, 0, 0, 0, 2, 2, 0, 0],[5, 4, 5, 0, 0, 0, 0, 5, 5, 0, 0],[0, 0, 0, 0, 5, 0, 1, 0, 0, 5, 0],[4, 3, 4, 0, 0, 0, 0, 5, 5, 0, 1],[0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4],[0, 0, 0, 2, 0, 2, 5, 0, 0, 1, 2],[0, 0, 0, 0, 5, 0, 0, 0, 0, 4, 0],[1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]]""" 給未打分的商品打分Parameters: dataMat - 訓練數據集 user - 用戶編號 simMeas - 相似度計算方法 item - 未評分的物品編號Returns:評分(0~5之間的值) """ def svdEst(dataMat, user, simMeas, item):#假設user=1# 得到數據集中的物品數目n = np.shape(dataMat)[1]# 初始化兩個評分值simTotal = 0.0ratSimTotal = 0.0# 奇異值分解# 在SVD分解之后,我們只利用包含90%能量值的奇異值,這些奇異值會以Numpy數組形式得以保存U, Sigma, VT = la.svd(dataMat)# 如果要進行矩陣運算,就必須要用這些奇異值構造出一個對角陣Sig4 = np.mat(np.eye(4) * Sigma[: 4])# 利用U矩陣將物品轉換到低維空間中,構建轉換后的物品(物品的4個主要特征)xformedItems = dataMat.T * U[:, :4] * Sig4.I#shape(11,4)# 遍歷行中的每個物品(對用戶評過分的物品進行遍歷,并將它與其他物品進行比較)for j in range(n):userRating = dataMat[user, j]# 如果某個物品的評分值為0,則跳過這個物品if userRating == 0:continue# 相似度的計算也會作為一個參數傳遞給該函數similarity = simMeas(xformedItems[item, :].T, xformedItems[j, :].T)print('商品 %d 和商品 %d 相似度similarity is: %f' % (item, j, similarity))# 相似度會不斷累加,每次計算時還考慮相似度和當前用戶評分的乘積# similarity 用戶相似度 userRating 用戶評分simTotal += similarityratSimTotal += similarity * userRatingif simTotal == 0:return 0# 通過除以所有的評分和,對上述相似度評分的乘積進行歸一化,使得最后評分在0~5之間,這些評分用來對預測值進行排序else:return ratSimTotal / simTotal""" 函數說明:推薦引擎Parameters:dataMat - 訓練數據集user - 用戶編號N- 產生N個推薦結果simMeas - 相似度計算方法estMethod - 推薦引擎方法Returns:評分(0~5之間的值)Modify:2018-08-08 """def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=svdEst):# 尋找未評級的物品# 對給定的用戶建立一個未評分的物品列表unratedItems = np.nonzero(dataMat[user, :].A == 0)[1]# 如果不存在未評分物品,那么就退出函數if len(unratedItems) == 0:return ('本店所有商品你均嘗試過,打過分')# 物品的編號和評分值itemScores = []# 在未評分的物品上進行循環for item in unratedItems:estimatedScore = estMethod(dataMat, user, simMeas, item)# 尋找前N個未評級的物品,調用svdEst()來產生該物品的預測得分,該物品的編號和估計值會放在一個元素列表itemScores中itemScores.append((item, estimatedScore))# 返回元素列表,第一個就是最大值return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[: N]if __name__ == '__main__':myMat = np.mat(loadExData())recommend(myMat, 1, estMethod=svdEst)#對用戶1進行推薦print('\n----------------------------------------\n')A = recommend(myMat, 1, estMethod=svdEst, simMeas=cosSim)A=np.array(A)for i in range(A.shape[0]):print('給您推薦的商品是{},計算得分是{}'.format(A[i][0],A[i][1]))給用戶1的推薦結果是商品4,7,9
如果不使用SVD矩陣分解
未打分商品得分計算函數
電氣專業的計算機萌新,寫博文不容易,在大佬面前獻丑啦。如果你覺得本文對你有用,請點個贊支持下,謝謝。
總結
以上是生活随笔為你收集整理的基于SVD矩阵分解的用户商品推荐(python实现)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql数据库入门教程(6):数据的增
- 下一篇: python自动化办公:文件篇(自动整理