機器學習實戰---SVD簡化資料
阿新 • • 發佈:2020-08-02
一:參考資料
(一)機器學習實戰
(二)如何讓奇異值分解(SVD)變得不“奇異”?(特別好理解SVD)
https://blog.csdn.net/yusisc/article/details/82216800
(三)奇異值分解(SVD)原理與在降維中的應用
(四)深入理解PCA與SVD的關係
二:推薦系統協調過濾演算法
(一)文章回顧
https://www.cnblogs.com/ssyfj/p/12951542.html
(二)另一種實現方法
import numpy as np #一:相似度計算,傳入的都是列向量 def eulidSim(inA,inB): #相似度=1/(1+距離) 物品對越相似,它們的相似度值就越大。 return 1/(1+np.linalg.norm(inA-inB)) #距離按照第二正規化計算 def pearsSim(inA,inB): #皮爾遜相關係數 相似度=0.5+0.5*corrcoef if len(inA) < 3: #為啥小於3???書上說小於3的兩個向量完全相關(不應該是2嗎?) return 1 return 0.5 + 0.5*np.corrcoef(inA,inB,rowvar=0)[0][1] def cosSim(inA,inB): #餘弦相似度 num= inA.T@inB denom = np.linalg.norm(inA)*np.linalg.norm(inB) return 0.5+0.5*(num/denom) #二:資料載入 def loadDataSet(): return np.array([[4,4,0,2,3], [4,0,0,3,3], [4,0,0,1,1], [1,1,1,2,0], [2,2,2,0,0], [1,1,1,0,0], [5,5,5,0,0]]) #三:實現基於物品相似度的推薦引擎 #1.根據某個使用者編號和其對應的多個未評價的物品中的一個,進行估計評分值(是直接對該物品評分) def standEst(dataArr,userId,itemId,simMeas): n = dataArr.shape[1] #所有物品數目 simTotal = 0 #計算整體相似度 ratSimTotal = 0 #計算相似度作為權重,對現有另一個使用者評分加權後的評分 #迴圈所有物品 for j in range(n): userScore = dataArr[userId,j] #獲取本使用者對物品的評分 if userScore == 0: continue #如果使用者沒有評分,則不進行相似度評價 #找到一個物品,同我們指定的物品。都同時給予了評價的使用者(有無重合,有重合,才可以進行相似度計算) overLap = np.where(np.logical_and(dataArr[:,itemId],dataArr[:,j]))[0] #獲取使用者索引 if len(overLap) == 0: #無相似度 similarity = 0 else: similarity = simMeas(dataArr[overLap,itemId],dataArr[overLap,j]) #計算重合部分的相似度 simTotal += similarity ratSimTotal += similarity*userScore #進行返回 if simTotal == 0: return 0 #如果沒有找到相似物品,返回評分0即可 else: return ratSimTotal/simTotal #通過除以所有的相似度和,對上述相似度評分的乘積進行歸一化,使得最後評分在0~5之間,這些評分用來對預測值進行排序 #2.使用上面估分,進行輔助。推薦物品 def recommend(dataArr,userId,N=3,simMeas=cosSim,estMethod=standEst): #N是想要獲取的最符合的物品(估計評分最高) #操作該使用者沒有評價的物品 unratedItems = np.where(dataArr[userId] == 0)[0] if len(unratedItems) == 0: #全部評價了 print("you rated everything") return #獲取全部未評價的物品評分值 itemsScore = [] for i in unratedItems: estimatedScore = estMethod(dataArr,userId,i,simMeas) itemsScore.append((i,estimatedScore)) #傳入物品編號和評分 #返回我們想要的物品數 return sorted(itemsScore,key=lambda p:p[1],reverse=True)[:N] #獲取前N個最高評分的物品 data = loadDataSet() print(recommend(data,2))
三:使用SVD提高推薦效果
(一)不變程式碼
import numpy as np #一:相似度計算,傳入的都是列向量 def eulidSim(inA,inB): #相似度=1/(1+距離) 物品對越相似,它們的相似度值就越大。 return 1/(1+np.linalg.norm(inA-inB)) #距離按照第二正規化計算 def pearsSim(inA,inB): #皮爾遜相關係數 相似度=0.5+0.5*corrcoef if len(inA) < 3: #為啥小於3???書上說小於3的兩個向量完全相關(不應該是2嗎?) return 1 return 0.5 + 0.5*np.corrcoef(inA,inB,rowvar=0)[0][1] def cosSim(inA,inB): #餘弦相似度 num = float(inA.T@inB) denom = np.linalg.norm(inA)*np.linalg.norm(inB) return 0.5+0.5*(num/denom) #二:資料載入 def loadDataSet(): return np.array([[4,4,0,2,3], [4,0,0,3,3], [4,0,0,1,1], [1,1,1,2,0], [2,2,2,0,0], [1,1,1,0,0], [5,5,5,0,0]]) #三:實現基於物品相似度的推薦引擎 #1.根據某個使用者編號和其對應的多個未評價的物品中的一個,進行估計評分值(是直接對該物品評分) def standEst(dataArr,userId,itemId,simMeas): n = dataArr.shape[1] #所有物品數目 simTotal = 0 #計算整體相似度 ratSimTotal = 0 #計算相似度作為權重,對現有另一個使用者評分加權後的評分 #迴圈所有物品 for j in range(n): userScore = dataArr[userId,j] #獲取本使用者對物品的評分 if userScore == 0: continue #如果使用者沒有評分,則不進行相似度評價 #找到一個物品,同我們指定的物品。都同時給予了評價的使用者(有無重合,有重合,才可以進行相似度計算) overLap = np.where(np.logical_and(dataArr[:,itemId],dataArr[:,j]))[0] #獲取使用者索引 if len(overLap) == 0: #無相似度 similarity = 0 else: similarity = simMeas(dataArr[overLap,itemId],dataArr[overLap,j]) #計算重合部分的相似度 simTotal += similarity ratSimTotal += similarity*userScore #進行返回 if simTotal == 0: return 0 #如果沒有找到相似物品,返回評分0即可 else: return ratSimTotal/simTotal #通過除以所有的相似度和,對上述相似度評分的乘積進行歸一化,使得最後評分在0~5之間,這些評分用來對預測值進行排序 #2.使用上面估分,進行輔助。推薦物品 def recommend(dataArr,userId,N=3,simMeas=cosSim,estMethod=standEst): #N是想要獲取的最符合的物品(估計評分最高) #操作該使用者沒有評價的物品 unratedItems = np.where(dataArr[userId] == 0)[0] if len(unratedItems) == 0: #全部評價了 print("you rated everything") return #獲取全部未評價的物品評分值 itemsScore = [] for i in unratedItems: estimatedScore = estMethod(dataArr,userId,i,simMeas) itemsScore.append((i,estimatedScore)) #傳入物品編號和評分 #返回我們想要的物品數 return sorted(itemsScore,key=lambda p:p[1],reverse=True)[:N] #獲取前N個最高評分的物品
(二)SVD程式碼實現
def loadExData2(): return np.array([[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]]) def svdEst(dataArr,userId,itemId,simMeas): #https://zhuanlan.zhihu.com/p/58064462 n = dataArr.shape[1] #獲取全部物品數量 simTotal = 0 ratSimTotal = 0 #使用SVD進行分解 #注意:python返回的sigma只有數值,不是矩陣 U,Sigma,VT = np.linalg.svd(dataArr) #注意行為使用者,列為物品 #建立對角矩陣 Sig4 = np.eye(4)*Sigma[:4] #建立對角矩陣 注意:我們選取4個最大特徵值,保持這四個特徵值的總能量>90%全部特徵總能量,使用前,先測試 #建立低維空間,使用U特徵矩陣,降低冗餘樣本 xformedItems = dataArr.T@U[:,:4]@np.linalg.inv(Sig4) #現在是n,4但是後面進行轉置,變為4,n達到降低冗餘 for j in range(n): userRating = dataArr[userId,j] #獲取評分 if userRating == 0 or j == itemId: continue #使用者沒有評分,或者物品就是本身,不處理 similarity = simMeas(np.array([xformedItems[itemId,:]]).T,np.array([xformedItems[j,:]]).T) print(np.array([xformedItems[itemId,:]]).T, np.array([xformedItems[j,:]]).T) print('the %d and %d similarity is: %f' % (itemId, j, similarity)) #[-0.01591788 -0.39205093 0.55707516 0.04356321] [-0.0552444 -0.52034959 -0.36330956 -0.19023805] simTotal += similarity ratSimTotal += similarity*userRating #加權評分 if simTotal == 0: return 0 else: return ratSimTotal/simTotal data = loadExData2() print(recommend(data,1,estMethod=svdEst))