[機器學習]-SVD奇異值分解的基本原理和運用


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()
View Code

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM