一.推薦系統的總體架構
1.1選取使用者偏好
二.開源的推薦系統
2.1協同過濾及其演算法
(1)資料預先處理與UI矩陣:分組為"查看"和"購買",對資料進行一定的預先處理,減噪和歸一化
(2)推薦模型:User CF和Item CF:基於物品的協同過濾(Amason,Netfix,Hulu,YouTube等採用)這裡使用kNN近鄰演算法。
(1)User CF
# -*- coding: utf-8 -*-from numpy import *import numpy as npfrom Recommand_Lib import kNNdataMat=mat([[0.238,0,0.1905,0.1905,0.1905,0.1905],[0,0.177,0,0.294,0.235,0.294],[0.2,0.16,0.12,0.12,0.2,0.2]])testSet = [0.2174,0.2174,0.1304,0,0.2174,0.2174]classLabel = np.array(['B','C','D'])print kNN(testSet,dataMat,classLabel,3)
(1) Item CF
# -*- coding: utf-8 -*-from numpy import *import numpy as npfrom Recommand_Lib import kNNdataMat=mat([[0.417,0.0,0.25,0.333],[0.3,0.4,0.0,0.3],[0.0,0.0,0.625,0.375],[0.278,0.222,0.222,0.278],[0.263,0.211,0.263,0.263]])testSet = [0.334,0.333,0.0,0.333]classLabel = np.array(['B','C','D','E','F'])print kNN(testSet,dataMat,classLabel,3)
運行結果一樣:
(3)KMeans (Scikit-Learn庫中)聚類演算法計算相似性:縮減計算量的方法,最優的選擇就是對資料進行聚類。演算法簡單實現代碼如下:
# -*- coding: utf-8 -*-# Filename : 02kMeans1.pyimport timeimport numpy as npfrom Recommand_Lib import *from sklearn.cluster import KMeansimport matplotlib.pyplot as plt k = 4dataSet = file2matrix("testData/4k2_far.txt","\t") dataMat = mat(dataSet[:,1:]) # 轉換為矩陣形式kmeans = KMeans(init='k-means++', n_clusters=4)kmeans.fit(dataMat)# 輸出產生的ClustDist:對應的聚類中心(列1),到聚類中心的距離(列2),行與dataSet一一對應drawScatter(plt,dataMat,size=20,color='b',mrkr='.')# 繪製聚類中心drawScatter(plt,kmeans.cluster_centers_,size=60,color='red',mrkr='D')plt.show() 運行結果如下:
聚類的演算法改進:二分KMeans 演算法:
# -*- coding: utf-8 -*-# Filename : 02kMeans1.pyfrom numpy import *import numpy as npfrom Recommand_Lib import *import matplotlib.pyplot as plt # 從檔案構建的資料集 dataMat = file2matrix("testData/4k2_far.txt","\t") dataSet = mat(dataMat[:,1:]) # 轉換為矩陣形式 k = 4 # 分類數m = shape(dataSet)[0]# 初始化第一個聚類中心: 每一列的均值centroid0 = mean(dataSet, axis=0).tolist()[0] centList =[centroid0] # 把均值聚類中心加入中心表中# 初始化聚類距離表,距離方差: ClustDist = mat(zeros((m,2)))for j in range(m):ClustDist[j,1] = distEclud(centroid0,dataSet[j,:])**2 # 依次產生k個聚類中心while (len(centList) < k): lowestSSE = inf # 初始化最小誤差平方和。核心參數,這個值越小就說明聚類的效果越好。 # 遍曆cenList的每個向量 #----1. 使用ClustDist計算lowestSSE,以此確定:bestCentToSplit、bestNewCents、bestClustAss----# for i in xrange(len(centList)): ptsInCurrCluster = dataSet[nonzero(ClustDist[:,0].A==i)[0],:] # 應用標準kMeans演算法(k=2),將ptsInCurrCluster劃分出兩個聚類中心,以及對應的聚類距離表 centroidMat,splitClustAss = kMeans(ptsInCurrCluster, 2) # 計算splitClustAss的距離平方和 sseSplit = sum(splitClustAss[:,1]) # 計算ClustDist[ClustDist第1列!=i的距離平方和 sseNotSplit = sum(ClustDist[nonzero(ClustDist[:,0].A!=i)[0],1]) if (sseSplit + sseNotSplit) < lowestSSE: # 演算法公式: lowestSSE = sseSplit + sseNotSplit bestCentToSplit = i # 確定聚類中心的最優分隔點 bestNewCents = centroidMat # 用新的聚類中心更新最優聚類中心 bestClustAss = splitClustAss.copy() # 深拷貝聚類距離表為最優聚類距離表 lowestSSE = sseSplit + sseNotSplit # 更新lowestSSE # 回到外迴圈 #----2. 計算新的ClustDist----# # 計算bestClustAss 分了兩部分: # 第一部分為bestClustAss[bIndx0,0]賦值為聚類中心的索引 bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList) # 第二部分 用最優分隔點的指定聚類中心索引 bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit # 以上為計算bestClustAss # 更新ClustDist對應最優分隔點下標的距離,使距離值等於最優聚類距離對應的值 #以上為計算ClustDist #----3. 用最優分隔點來重構聚類中心----# # 覆蓋: bestNewCents[0,:].tolist()[0]附加到原有聚類中心的bestCentToSplit位置 # 增加: 聚類中心增加一個新的bestNewCents[1,:].tolist()[0]向量 centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0] centList.append(bestNewCents[1,:].tolist()[0]) ClustDist[nonzero(ClustDist[:,0].A == bestCentToSplit)[0],:]= bestClustAss # 以上為計算centListcolor_cluster(ClustDist[:,0:1],dataSet,plt)print "cenList:",mat(centList)# print "ClustDist:", ClustDist# 繪製聚類中心圖形drawScatter(plt,mat(centList),size=60,color='red',mrkr='D')plt.show()# -*- coding: utf-8 -*-# Filename : 02kMeans1.pyfrom numpy import *import numpy as npfrom Recommand_Lib import *import matplotlib.pyplot as plt # 從檔案構建的資料集 dataMat = file2matrix("testData/4k2_far.txt","\t") dataSet = mat(dataMat[:,1:]) # 轉換為矩陣形式 k = 4 # 分類數m = shape(dataSet)[0]# 初始化第一個聚類中心: 每一列的均值centroid0 = mean(dataSet, axis=0).tolist()[0] centList =[centroid0] # 把均值聚類中心加入中心表中# 初始化聚類距離表,距離方差: ClustDist = mat(zeros((m,2)))for j in range(m):ClustDist[j,1] = distEclud(centroid0,dataSet[j,:])**2 # 依次產生k個聚類中心while (len(centList) < k): lowestSSE = inf # 初始化最小誤差平方和。核心參數,這個值越小就說明聚類的效果越好。 # 遍曆cenList的每個向量 #----1. 使用ClustDist計算lowestSSE,以此確定:bestCentToSplit、bestNewCents、bestClustAss----# for i in xrange(len(centList)): ptsInCurrCluster = dataSet[nonzero(ClustDist[:,0].A==i)[0],:] # 應用標準kMeans演算法(k=2),將ptsInCurrCluster劃分出兩個聚類中心,以及對應的聚類距離表 centroidMat,splitClustAss = kMeans(ptsInCurrCluster, 2) # 計算splitClustAss的距離平方和 sseSplit = sum(splitClustAss[:,1]) # 計算ClustDist[ClustDist第1列!=i的距離平方和 sseNotSplit = sum(ClustDist[nonzero(ClustDist[:,0].A!=i)[0],1]) if (sseSplit + sseNotSplit) < lowestSSE: # 演算法公式: lowestSSE = sseSplit + sseNotSplit bestCentToSplit = i # 確定聚類中心的最優分隔點 bestNewCents = centroidMat # 用新的聚類中心更新最優聚類中心 bestClustAss = splitClustAss.copy() # 深拷貝聚類距離表為最優聚類距離表 lowestSSE = sseSplit + sseNotSplit # 更新lowestSSE # 回到外迴圈 #----2. 計算新的ClustDist----# # 計算bestClustAss 分了兩部分: # 第一部分為bestClustAss[bIndx0,0]賦值為聚類中心的索引 bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList) # 第二部分 用最優分隔點的指定聚類中心索引 bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit # 以上為計算bestClustAss # 更新ClustDist對應最優分隔點下標的距離,使距離值等於最優聚類距離對應的值 #以上為計算ClustDist #----3. 用最優分隔點來重構聚類中心----# # 覆蓋: bestNewCents[0,:].tolist()[0]附加到原有聚類中心的bestCentToSplit位置 # 增加: 聚類中心增加一個新的bestNewCents[1,:].tolist()[0]向量 centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0] centList.append(bestNewCents[1,:].tolist()[0]) ClustDist[nonzero(ClustDist[:,0].A == bestCentToSplit)[0],:]= bestClustAss # 以上為計算centListcolor_cluster(ClustDist[:,0:1],dataSet,plt)print "cenList:",mat(centList)# print "ClustDist:", ClustDist# 繪製聚類中心圖形drawScatter(plt,mat(centList),size=60,color='red',mrkr='D')plt.show() 演算法運行結果:
(4)SVD (隱語義模型:奇異值分解)計算相似性
# -*- coding: utf-8 -*-# Filename : svdRec2.py'''Created on Mar 8, 2011@author: Peter'''from numpy import *from numpy import linalg as ladef loadExData(): return[[0, 0, 0, 2, 2], [0, 0, 0, 3, 3], [0, 0, 0, 1, 1], [1, 1, 1, 0, 0], [2, 2, 2, 0, 0], [5, 5, 5, 0, 0], [1, 1, 1, 0, 0]]def loadReData(): return[[4, 4, 0, 2, 2], [4, 0, 0, 3, 3], [4, 0, 0, 1, 1], [1, 1, 1, 0, 0], [2, 2, 2, 0, 0], [5, 5, 5, 0, 0], [1, 1, 1, 0, 0]] def loadExData2(): 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]]# 歐氏距離:# 二維空間的歐氏距離公式:sqrt((x1-x2)^2+(y1-y2)^2)def ecludSim(inA,inB): return 1.0/(1.0 + la.norm(inA - inB))# 皮爾遜相似性:corrcoef相關係數:衡量X與Y線性相關程度,其絕對值越大,則表明X與Y相關度越高。# E((X-EX)(Y-EY))/sqrt(D(X)D(Y))def pearsSim(inA,inB): if len(inA) < 3 : return 1.0 return 0.5+0.5*corrcoef(inA, inB, rowvar = 0)[0][1]# 夾角餘弦:計算空間內兩點之間的夾角餘弦# 兩個n維樣本點a(x11,x12,…,x1n)和b(x21,x22,…,x2n)的夾角餘弦# cos(theta) = a*b/(|a|*|b|)def cosSim(inA,inB): num = float(inA.T*inB) denom = la.norm(inA)*la.norm(inB) return 0.5+0.5*(num/denom)# 標準相似性計算方法下的使用者估計值def standEst(dataMat, user, simMeas, item): n = shape(dataMat)[1] # 列數 simTotal = 0.0; ratSimTotal = 0.0 for j in range(n): userRating = dataMat[user,j] # 資料集第user行第j列的元素值 if userRating == 0: continue # 跳過未評估項目 # logical_and:矩陣逐個元素運行邏輯與,傳回值為每個元素的True,False # dataMat[:,item].A>0: 第item列中大於0的元素 # dataMat[:,j].A: 第j列中大於0的元素 # overLap: dataMat[:,item],dataMat[:,j]中同時都大於0的那個元素的行下標 overLap = nonzero(logical_and(dataMat[:,item].A>0,dataMat[:,j].A>0))[0] # 計算相似性 if len(overLap) == 0: similarity = 0 else: similarity = simMeas(dataMat[overLap,item],dataMat[overLap,j]) # 計算overLap矩陣的相似性 # print "第%d列和第%d列的相似性是: %f" %(item, j, similarity) # 累計總相似性 simTotal += similarity # ratSimTotal = 相似性*元素值 ratSimTotal += similarity * userRating if simTotal == 0: return 0 # 如果總相似性為0,返回0 # 返回相似性*元素值/總相似性 else: # print "ratSimTotal:",ratSimTotal # print "simTotal:",simTotal return ratSimTotal/simTotal#使用svd進行估計 def svdEst(dataMat, user, simMeas, item): n = shape(dataMat)[1] simTotal = 0.0; ratSimTotal = 0.0 # svd相似性計算的核心 U,Sigma,VT = la.svd(dataMat) # 計算矩陣的奇異值分解 # Sig4 = mat(eye(4)*Sigma[:4]) # 取Svd特徵值的前4個構成對角陣 # xformedItems = dataMat.T * U[:,:4] * Sig4.I # 建立變換後的項目矩陣create transformed items V = VT.T # V是dataMat的相似矩陣 xformedItems = V[:,:4] # print "xformedItems:",xformedItems # 逐列遍曆資料集 for j in range(n): userRating = dataMat[user,j] # 未評級使用者為0,因此不會計算。其他的均有值 # print "userRating:",userRating # 跳過未評級的項目 if userRating == 0 or j==item: continue # 使用指定的計算公式計算向量間的相似性 similarity = simMeas(xformedItems[item,:].T,xformedItems[j,:].T) # 相似性計算公式 # print "待評估%d列和第%d列的相似性是: %f" % (item, j, similarity) simTotal += similarity # 計算累計總相似性 ratSimTotal += similarity * userRating # ratSim = 相似性*項目評估值 if simTotal == 0: return 0 else: return ratSimTotal/simTotal # 產生推薦結果的主方法# simMeas取值:cosSim, pearsSim, ecludSim# estMethod取值:standEst,svdEst# user 使用者項目矩陣中進行評估的行下標# N=3返回前3項def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=svdEst): unratedItems = nonzero(dataMat[user,:].A==0)[1] # 尋找未評級的項目--即使用者--項目矩陣中user行對應的零值 # print "unratedItems:",unratedItems # unratedItems: 未評估的項目--項目矩陣中user行對應零值的列下標 if len(unratedItems) == 0: return "已經對所有項目評級" # 初始化項目積分資料類型,是一個二維矩陣 # 元素1:item;元素2:評分值 itemScores = [] # 迴圈進行評估:將每個未評估項目於已評估比較,計算相似性 # 本例中未評估項目取值1,2 for item in unratedItems: estimatedScore = estMethod(dataMat, user, simMeas, item) # 使用評估方法對資料評估,返回評估積分 itemScores.append((item, estimatedScore)) # 並在項目積分內加入項目和對應的評估積分 # 返回排好序的項目和積分,N=3返回前3項 return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N] # 輸出矩陣def printMat(inMat, thresh=0.8): for i in range(32): for k in range(32): if float(inMat[i,k]) > thresh: print 1, else: print 0, print ''# 圖片壓縮def imgCompress(numSV=3, thresh=0.8,flag=True): myl = [] for line in open('0_5.txt').readlines(): newRow = [] for i in range(32): newRow.append(int(line[i])) myl.append(newRow) myMat = mat(myl) print "****original matrix******" printMat(myMat, thresh) U,Sigma,VT = la.svd(myMat) print "U 行列數:",shape(U)[0],",",shape(U)[1] print "Sigma:",Sigma print "VT 行列數:",shape(VT)[0],",",shape(VT)[1] if flag: SigRecon = mat(zeros((numSV, numSV))) for k in range(numSV):#construct diagonal matrix from vector SigRecon[k,k] = Sigma[k] reconMat = U[:,:numSV]*SigRecon*VT[:numSV,:] print "****reconstructed matrix using %d singular values******" % numSV printMat(reconMat, thresh)
# -*- coding: utf-8 -*-# Filename : testRecomm01.pyfrom numpy import *import numpy as np import operatorfrom svdRec import *import matplotlib.pyplot as plt eps = 1.0e-6# 載入修正後資料A = mat([[5, 5, 3, 0, 5, 5],[5, 0, 4, 0, 4, 4],[0, 3, 0, 5, 4, 5],[5, 4, 3, 3, 5, 5]])# 手工分解求矩陣的svdU = A*A.Tlamda,hU = linalg.eig(U) # hU:U特徵向量VT = A.T*AeV,hVT = linalg.eig(VT) # hVT:VT特徵向量hV = hVT.T# print "hU:",hU# print "hV:",hVsigma = sqrt(lamda) # 特徵值print "sigma:",sigmaprint"svd:驗證結果:"Sigma = np.zeros([shape(A)[0], shape(A)[1]])U,S,VT = linalg.svd(A)# Sigma[:shape(A)[0], :shape(A)[0]] = np.diag(S)# print Uprint S# print VT# print U*Sigma*VT
運行結果如下:
本章主要介紹了KMeans無監督聚類演算法及其python的實現,另外一個講解了推薦系統最核心的演算法-SVD演算法,最後用Numpy的庫函數實現了SVD演算法.