SVD奇異值分解:python
SVD是一種可靠的正交矩陣分解法。能夠把A矩陣分解成U,∑,VT三個矩陣相乘的形式。(Svd(A)=[U*∑*VT],A沒必要是方陣,U,VT一定是正交陣,S是對角陣<以奇異值爲對角線,其餘全爲0>)算法
用途:app
信息檢索(LSA:隱性語義索引,LSA:隱性語義分析),分解後的奇異值表明了文章的主題或者概念,信息檢索的時候同義詞,或者說同一主題下的詞會映射爲同一主題,這樣就能夠提升搜索效率機器學習
數據壓縮:經過奇異值分解,選擇能量較大的前N個奇異值來代替全部的數據信息,這樣能夠下降噪聲,節省空間。ide
推薦系統:主要是降噪,矩陣變換至低維空間,方便計算(目前沒有意識到它對推薦精確度的提高有什麼具體做用)。學習
原理:矩陣分解,矩陣變換,數據降維spa
基於協同過濾的推薦系統(相關知識):3d
類似度計算:A(a1,a2,a3),B(b1,b2,b3)code
1.歐氏距離類似度:點到點的距離在多維空間的推廣 ,||A-B||表示A-B的2範數。orm
,
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()