SVD奇異值分解:
SVD是一種可靠的正交矩陣分解法。可以把A矩陣分解成U,∑,VT三個矩陣相乘的形式。(Svd(A)=[U*∑*VT],A不必是方陣,U,VT必定是正交陣,S是對角陣<以奇異值為對角線,其他全為0>)
用途:
信息檢索(LSA:隱性語義索引,LSA:隱性語義分析),分解后的奇異值代表了文章的主題或者概念,信息檢索的時候同義詞,或者說同一主題下的詞會映射為同一主題,這樣就可以提高搜索效率
數據壓縮:通過奇異值分解,選擇能量較大的前N個奇異值來代替所有的數據信息,這樣可以降低噪聲,節省空間。
推薦系統:主要是降噪,矩陣變換至低維空間,方便計算(目前沒有意識到它對推薦精確度的提升有什么具體作用)。
原理:矩陣分解,矩陣變換,數據降維
基於協同過濾的推薦系統(相關知識):
相似度計算:A(a1,a2,a3),B(b1,b2,b3)
1.歐氏距離相似度:點到點的距離在多維空間的推廣 ,||A-B||表示A-B的2范數。
,
2.皮爾遜相關系數:
3.余玄相似度:
SVD的矩陣空間變換:
1.奇異值分解
2.奇異值選擇,數據矩陣重構:
協同過濾算法,就是在重構后的矩陣空間上做相似度計算。
下面就《機器學習實戰》來看一下具體矩陣分解和奇異值選擇的操作(后面會附上具體的代碼,大家一看就懂,很多東西都被Python封裝好了,直接調用):
原始數據data1:每一列代表一種商品,每一行代表一個用戶,數據是用戶對商品的評價
Data:(M*N)7*5
奇異值分解:
U:(M*M)7*7
∑:(M*N對角矩陣,前N*N是對角矩陣,對角線時奇異值,后M-N是0)7*5
VT:(N*N)5*5
奇異值選擇:
∑=(e1,e2,e3...em)
從上圖 分解后的∑可以看出前2個奇異值之和遠大於后面的奇異值,所以說,前兩個奇異值中代表的信息足以描述整個數據。我們可以計算前x個奇異值得平方和占所有奇異值的平方和的比例,如果大於90%,我們就選這x個奇異值重構矩陣(剩余的數據代表的可能是噪聲,無用數據)
我們通過矩陣重構來看一下理論是否正確
矩陣重構:
U:(M*X)7*2
∑:(X*X)2*2,以前X個奇異值構建對角矩陣
VT:(x*n)2*5
A’:重構后的U*∑*VT
可以發現原始數據中非零的部分都完整的保存了下來,說明選擇的奇異值幾乎完整地保存了所有有用信息。其他部分都是近似為零的小數,將他們損失精度,強轉成整形后就是0強轉之后如下圖:
原始數據Data:
可以看到相比較於原始數據出現了部分損失,這是由於強轉后將損失信息放大所致,在浮點數情況下這些微小的損失被忽略掉了(個人理解)。
基於以下數據data2做商品推薦:行:用戶,列:商品(由於上一個數據集維數較低已經用於展示了這個步驟中的操作,下面就直接放代碼實現)
步驟:
1.進行矩陣奇異值分解
2.矩陣進行低維空間的映射 降維后的數據A’
3.在低維空間做相似度計算,並進行估計評分
貼代碼:(沒有代碼說個卵呀!,最后會放上源碼)python(才開始用可能風格有點怪異),代碼是機器學習實戰的內容,注釋也很多,不做多說了
Exp: 用戶A,評價了1,2,3,4,5這5個商品中的1,2,3
用戶B,評價了1,2,3,4,5這5個商品中的1,3,4
現在要給A做推薦4,5號商品(未評價過的才需要推薦),首先我們遍歷A評價過得商品的每一列(在矩陣中代表其他用戶對這個商品的評價),然后和指定的4號商品所在的列做相似度計算。
在這里就是1,2,3,列分別於第四列做相似度計算給出一個評分。然后1,2,3列再與第5列做相似度評分。最終我們比較4,5的估計評分值,誰大,我們就說,喜歡1,2,3號商品的用戶可能也喜歡4號。
就以上的說明並沒有用到SVD,我們再取數據的列的時候並不是從原矩陣中去取,而是從利用SVD降維后的矩陣中去取(這是唯一用到SVD的部分)。
根據評分推薦:
遍歷所有未評分的商品,進行評分,然后排序取TOPN(這里選三個),輸出的結果就是給這個用戶推薦的商品。
基於SVD實現的數據壓縮:
SVD數據壓縮說白了就是奇異值分解后,
A可以近似的用U’*∑’*VT’表示A,原始的A需要M*N個存儲空間,我們現在只需要存儲U’,∑’,VT’三個矩陣在使用的時候做乘積就可以得到A,而且U’,∑’,VT’需要的空間M*X+X*X+X*N遠小於M*N,這就實現了數據壓縮。從M*N壓縮到了M*X+X*X+X*N
Exp:對一個圖像數據進行壓縮:32*32的圖像數據 總空間需要:32*32=1024
壓縮前:
壓縮后還原:可以發現有微小的差異
壓縮后的三個矩陣:sigma(2),VT(2*32),U(32*2)總空間=130相比1024極大縮小了占有空間
源代碼:(py2.7可直接運行)

1 # -*- coding:utf-8 -*- 2 # Filename: svd.py 3 # Author:Ljcx 4 5 6 from numpy import* 7 8 9 class Svd(object): 10 11 def loadExData(self): 12 data = [[0, 0, 0, 2, 2], 13 [0, 0, 0, 3, 3], 14 [0, 0, 0, 1, 1], 15 [1, 1, 1, 0, 0], 16 [2, 2, 2, 0, 0], 17 [5, 5, 5, 0, 0], 18 [1, 1, 1, 0, 0]] 19 return data 20 21 def loadExData2(self): 22 """ 23 列表示商品,行表示用戶的評分 24 """ 25 return[[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5], 26 [0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 3], 27 [0, 0, 0, 0, 4, 0, 0, 1, 0, 4, 0], 28 [3, 3, 4, 0, 0, 0, 0, 2, 2, 0, 0], 29 [5, 4, 5, 0, 0, 0, 0, 5, 5, 0, 0], 30 [0, 0, 0, 0, 5, 0, 1, 0, 0, 5, 0], 31 [4, 3, 4, 0, 0, 0, 0, 5, 5, 0, 1], 32 [0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4], 33 [0, 0, 0, 2, 0, 2, 5, 0, 0, 1, 2], 34 [0, 0, 0, 0, 5, 0, 0, 0, 0, 4, 0], 35 [1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]] 36 37 # 相似度計算:inA,inB為列向量還是行向量,基於我們需要計算相似的維度 38 def ecludSim(self, inA, inB): 39 """norm()求范數 40 范數表示數值平方開方,inA-inB的范數 = inA和inB的歐氏距離 41 """ 42 return 1.0 / (1.0 + linalg.norm(inA - inB)) 43 44 def pearsSim(self, inA, inB): 45 """corrcoef()求皮爾遜相關系數 [-1,1] 46 皮爾遜相關系數:0.5+0.5*corrcoef()規范化到[0,1] 47 """ 48 if len(inA) < 3: 49 return 1.0 50 return 0.5 + 0.5 * corrcoef(inA, inB, rowvar=0)[0][1] 51 52 def cosSim(self, inA, inB): 53 """ 54 余玄相似度:即兩個向量的余玄夾角值[-1,1] 55 """ 56 num = float(inA.T * inB) 57 denom = linalg.norm(inA) * linalg.norm(inB) 58 return 0.5 + 0.5 * (num / denom) 59 60 # 奇異值分解==》矩陣重構:可用於圖像壓縮 61 def svdMt(self, data): 62 """ 63 奇異值分解矩陣data = U * Sigma *VT (用分解后的矩陣可以近似地表示原矩陣 64 節省空間, 65 Sigma是個奇異值方陣) 66 """ 67 U, Sigma, VT = linalg.svd(data) 68 """ 前兩個奇異值已經幾乎包含了所有的信息遠大於后三個數據,所以忽略掉后三個 69 數據 70 啟發式搜索:選擇奇異之平方和大於總平方和90%為標准 71 """ 72 num = 0 # 需要保存的奇異值個數 73 for i in range(len(Sigma)): 74 if (linalg.norm(Sigma[:i + 1]) / linalg.norm(Sigma)) > 0.9: 75 num = i + 1 76 break 77 # 構建對角矩陣 78 sig3 = mat(eye(num) * Sigma[:num]) 79 """選取前num個奇異值重構數據集 80 """ 81 newData = U[:, :num] * mat(sig3) * VT[:num, :] 82 print newData 83 print newData.astype(int) 84 return U, Sigma, VT, num, newData 85 86 """ 87 基於相似度的推薦引擎: 88 只需要對用戶所購商品和其他商品做相似度計算,選取TOPn個作為推薦 89 基於SVD的推薦引擎: 90 先進行奇異值分解,選取前n個奇異值(能量之和大於90%,奇異之平方和大於總平方和 91 90%為標准),作為需要降維的維數,原數據往低維空間投影。Data.T*U[:,:n]*Sigma[:,:4] 92 尋找指定一個商品的所有評分x[,,,]和每一個商品的所有評分做相似度計算,相似度求和 93 """ 94 95 # 相似度推薦 96 def standEst(self): 97 pass 98 99 def svdEst(self, dataMat, xformedItems, user, simMeas, item): 100 """計算相似度並計算評分 101 # dataMat:原始數據 102 # user:用戶編號 103 # simMeas:相似度計算方法 104 # item:商品編號 105 # xformedItems:降維后的數據 106 """ 107 n = shape(dataMat)[1] # 獲取列,多少個商品 108 simTotal = 0.0 109 ratSimTotal = 0.0 110 # 計算指定用戶評價過的商品與其他所有用戶的評價過的商品做相似度計算,來估計 111 # 指定的未評價商品item的評分 112 for j in range(n): 113 userRating = dataMat[user, j] 114 if userRating == 0 or j == item: 115 continue 116 similarity = simMeas(xformedItems[item, :].T, xformedItems[j, :].T) 117 print 'the %d and %d similarity is: %f' % (item, j, similarity) 118 simTotal += similarity # 相似度求和 119 ratSimTotal += similarity * userRating # 相似度乘以評分在求和 120 if simTotal == 0: 121 return 0 122 else: 123 return ratSimTotal / simTotal # 根據相似度對一個指定商品給一個評分 124 125 def recommend(self, dataMat, user, N=3, simMeas=cosSim, estMethod=svdEst): 126 """ 127 # 根據SVD空間評分推薦:尋找所有該用戶未評分的商品,對每個商品進行評分估計() 128 """ 129 unratedItems = nonzero(dataMat[user, :].A == 0)[1] # findunrated items 130 if len(unratedItems) == 0: 131 return 'you rated everything' 132 U, Sigma, VT, num, newData = self.svdMt(dataMat) 133 sig = mat(eye(num) * Sigma[:num]) # 構建對角矩陣 134 xformedItems = dataMat.T * U[:, :num] * sig.I # 數據投影降維 135 print "----xform---" 136 print xformedItems 137 itemScores = [] 138 for item in unratedItems: 139 estimatedScore = estMethod( 140 dataMat, xformedItems, user, simMeas, item) # 評分 141 itemScores.append((item, estimatedScore)) 142 return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N] 143 144 def loadImageData(self): 145 """ 146 加載圖像數據 147 """ 148 fp = open("image.txt", "r") 149 imageData = [] 150 for line in fp.readlines(): 151 lineData = [] 152 for i in range(len(line) - 1): 153 lineData.append(int(line[i])) 154 imageData.append(lineData) 155 return mat(imageData) 156 157 def imageCompress(self): 158 """svd圖像壓縮 == 分解矩陣之后 選擇幾個重要的奇異值對U ,Sigma ,VT 進行切割, 159 切割后的矩陣的乘積仍可以表示原矩陣,我們只需存儲這三個矩陣就可以在使用的時候 160 還原原矩陣了 161 """ 162 data = self.loadImageData() 163 self.printMat(data, 0.8) # 壓縮前數據 164 print"---------------------------------------------------------" 165 U, Sigma, VT, num, newData = self.svdMt(data) 166 self.printMat(newData, 0.8) # 壓縮后還原的數據 167 print Sigma 168 print "num:" + str(num) 169 print '前 %d 個奇異值的平方和達到了所有奇異值平方和的0.9以上則2個奇異值重構矩陣可表示原矩陣:' % (num) 170 U = U[:, :num] 171 Sigma = Sigma[:num] 172 VT = VT[:num, :] 173 print "U:" + str(shape(U)) 174 print U 175 print "Sigma:" + str(shape(Sigma)) 176 print Sigma 177 print "VT:" + str(shape(VT)) 178 print VT 179 print "壓縮前存儲空間:", str(shape(data)[0] * shape(data)[1]) 180 print "壓縮后存儲空間:", str(shape(U)[0] * shape(U)[1] 181 + shape(Sigma)[0] * shape(Sigma)[0] 182 + shape(VT)[0] * shape(VT)[1]) 183 184 def printMat(self, inMat, thresh=0.8): 185 for i in range(32): 186 for k in range(32): 187 if float(inMat[i, k]) > thresh: 188 print 1, 189 else: 190 print 0, 191 print '' 192 193 194 if __name__ == "__main__": 195 sd = Svd() 196 data = sd.loadExData2() 197 sd.recommend(mat(data), 2, 3, sd.cosSim, sd.svdEst) 198 sd.imageCompress()