【機器學習】【SVD-3】SVD降維的應用簡介 + 降維示例展示 + Python代碼實現

1.SVD降維的基本原理

SVD降維的基本原理,可以詳見以前文章:https://blog.csdn.net/u012421852/article/details/80433463

2.降維示例展示

降維的理論以及和意義不再贅述,此處僅僅給出SVD分解降維的一個應用示例。

Step1:準備要降維的數據矩陣M,以及奇異值開方和佔比閾值percentage

    data = np.array([[5, 5, 0, 5],
                     [5, 0, 3, 4],
                     [3, 4, 0, 3],
                     [0, 0, 5, 3],
                     [5, 4, 4, 5],
                     [5, 4, 5, 5]])
    percentage = 0.9

Step2:對數據矩陣M進行奇異值分解

(U, S, VT) = np.linalg.svd(M)

解釋np.linalg.svd(M)的返回值U,S,VT依次對應於下面SVD公式中的U,∑,V.T,

注意:svd()方法返回的第三個值是SVD中的V.T,而不是V,後面降維時直接使用VT[:K, :len(VT)]作爲降維後的SVD公式中的VT

注意:S是M的所有奇異值的數組


求得的U,S,VT如下所示:

降維前的U,S,VT依次爲:
(6, 6) U:
 [[-0.44721867  0.53728743  0.00643789 -0.50369332 -0.38572204 -0.32982993]
 [-0.35861531 -0.24605053 -0.86223083 -0.14584826  0.07797125  0.20015231]
 [-0.29246336  0.40329582  0.22754042 -0.10376096  0.4360044   0.70652449]
 [-0.20779151 -0.67004393  0.3950621  -0.58878098  0.02599042  0.06671744]
 [-0.50993331 -0.05969518  0.10968053  0.28687443  0.59460659 -0.53714128]
 [-0.53164501 -0.18870999  0.19141061  0.53413013 -0.54845844  0.24290419]]
(4,) S:
 [ 17.71392084   6.39167145   3.09796097   1.32897797]
(4, 4) VT:
 [[-0.57098887 -0.4274751  -0.38459931 -0.58593526]
 [ 0.22279713  0.51723555 -0.82462029 -0.05319973]
 [-0.67492385  0.69294472  0.2531966  -0.01403201]
 [ 0.41086611  0.26374238  0.32859738 -0.80848795]]

Step3:根據percentage求k值

    def _calc_k(self, percentge):
        '''確定k值:前k個奇異值的平方和佔比 >=percentage, 求滿足此條件的最小k值
        :param percentage, 奇異值平方和的佔比的閾值
        :return 滿足閾值percentage的最小k值
        '''
        self.k = 0
        #用戶數據矩陣的奇異值序列的平方和
        total = sum(np.square(self.S))
        svss = 0 #奇異值平方和 singular values square sum
        for i in range(np.shape(self.S)[0]):
            svss += np.square(self.S[i])
            if (svss/total) >= percentge:
                self.k = i+1
                break
        return self.k

求得的k值爲:2

即成立:前2個奇異值平方和 / 所有奇異值平方和 >= 0.9

Step4:根據求得的k構造k階奇異值對角陣

    def _buildSD(self, k):
        '''構建由奇異值組成的對角矩陣
        :param k,根據奇異值開放和的佔比閾值計算出來的k值
        :return 由k個前奇異值組成的對角矩陣
        '''
        #方法1:用數組乘方法
        self.SD = np.eye(self.k) * self.S[:self.k]

        #方法2:用自定義方法
        e = np.eye(self.k)
        for i in range(self.k):
            e[i,i] = self.S[i]

        return self.SD

Step5:根據求得的k求降維後的U和VT

new_U  = U[:len(U), :k]
new_VT = VT[:k, :len(VT)]

k=2時,降維後的U和VT爲:

降維後的U, VT依次爲:
(6,) new_U=U[:6,:2]:
 [[-0.44721867  0.53728743]
 [-0.35861531 -0.24605053]
 [-0.29246336  0.40329582]
 [-0.20779151 -0.67004393]
 [-0.50993331 -0.05969518]
 [-0.53164501 -0.18870999]]

(2, 4) new_VT=VT[:2, :4]:
 [[-0.57098887 -0.4274751  -0.38459931 -0.58593526]
 [ 0.22279713  0.51723555 -0.82462029 -0.05319973]]

Step6:用降維後的U,SD,VT求降維後的用戶數據矩陣new_M

記原始用戶數據矩陣的被評價的商品數爲n,用戶數爲m降維後的SVD公式稱爲:

new_M = U(n,k)SD(k,k)VT(k,m)

注:U,SD,VT之間是矩陣乘操作np.dot(),不是矩陣點乘np.multiply()

求得降維後的用戶數據矩陣爲:

用戶對商品的評分矩陣M(4個用戶對6個商品的評分矩陣):
 [[5 5 0 5] #所有用戶對商品1的評分值序列
 [5 0 3 4]  #所有用戶對商品2的評分值序列
 [3 4 0 3]  #所有用戶對商品3的評分值序列
 [0 0 5 3]  #所有用戶對商品4的評分值序列
 [5 4 4 5]  #所有用戶對商品5的評分值序列
 [5 4 5 5]] #所有用戶對商品6的評分值序列
降維後的評分矩陣new_M:
 [[ 5.28849359  5.16272812  0.21491237  4.45908018]
 [ 3.27680994  1.90208543  3.74001972  3.80580978]
 [ 3.53241827  3.54790444 -0.13316888  2.89840405]
 [ 1.14752376 -0.64171368  4.94723586  2.3845504 ]
 [ 5.07268706  3.66399535  3.78868965  5.31300375]
 [ 5.10856595  3.40187905  4.6166049   5.58222363]]

可以直觀會發現,降維後的數據矩陣new_M 和 原始用戶數據矩陣M 很接近,new_M能夠表達原始數據矩陣M的中所有數據的相關性

也會發現,SVD降維是一種數據的有損壓縮.

3.Python代碼實現(Release版本)

3.1代碼

此代碼是降維應用實例的精簡代碼,如果是理解SVD分解降維示例的數學求解過程,可以看4節給出的Debug版本代碼。

人肉出品,代碼詳見如下:

# -*- coding: utf-8 -*-
"""
@author: 蔚藍的天空Tom
Talk is cheap, show me the code
Aim:svd分解降維應用示例的代碼實現(Release版本)
CSDN URL:https://mp.csdn.net/postedit/80450590
"""

import numpy as np

class CSVD(object):
    '''
    實現svd分解降維應用示例的Python代碼
    '''
    
    def __init__(self, data):
        self.data = data       #用戶數據
        self.S = []  #用戶數據矩陣的奇異值序列 singular values
        self.U = []  #svd後的單位正交向量
        self.VT = []  #svd後的單位正交向量
        self.k = 0   #滿足self.p的最小k值(k表示奇異值的個數)
        self.SD = [] #對角矩陣,對角線上元素是奇異值 singular values diagonal matrix
        
    def _svd(self):
        '''
        用戶數據矩陣的svd奇異值分解
        '''
        self.U, self.S, self.VT = np.linalg.svd(self.data)
        return self.U, self.S, self.VT
        
    def _calc_k(self, percentge):
        '''確定k值:前k個奇異值的平方和佔比 >=percentage, 求滿足此條件的最小k值
        :param percentage, 奇異值平方和的佔比的閾值
        :return 滿足閾值percentage的最小k值
        '''
        self.k = 0
        #用戶數據矩陣的奇異值序列的平方和
        total = sum(np.square(self.S))
        svss = 0 #奇異值平方和 singular values square sum
        for i in range(np.shape(self.S)[0]):
            svss += np.square(self.S[i])
            if (svss/total) >= percentge:
                self.k = i+1
                break
        return self.k

    def _buildSD(self, k):
        '''構建由奇異值組成的對角矩陣
        :param k,根據奇異值開放和的佔比閾值計算出來的k值
        :return 由k個前奇異值組成的對角矩陣
        '''
        #方法1:用數組乘方法
        self.SD = np.eye(self.k) * self.S[:self.k]

        #方法2:用自定義方法
        e = np.eye(self.k)
        for i in range(self.k):
            e[i,i] = self.S[i]

        return self.SD
    
    def DimReduce(self, percentage):
        '''
        SVD降維
        :param percentage, 奇異值開方和的佔比閾值
        :return 降維後的用戶數據矩陣
        '''
        #Step1:svd奇異值分解
        self._svd()
        #Step2:計算k值
        self._calc_k(percentage)
        print('\n按照奇異值開方和佔比閾值percentage=%d, 求得降維的k=%d'%(percentage, self.k))
        #Step3:構建由奇異值組成的對角矩陣singular values diagonal
        self._buildSD(self.k)
        k,U,SD,VT = self.k,self.U, self.SD, self.VT
        #Step4:按照svd分解公式對用戶數據矩陣進行降維,得到降維壓縮後的數據矩陣
        
        a = U[:len(U), :k]
        b = np.dot(SD, VT[:k, :len(VT)])
        newData = np.dot(a,b)
        return newData
        
def CSVD_manual():
    ##訓練數據集,用戶對商品的評分矩陣,行爲多個用戶對單個商品的評分,列爲用戶對每個商品的評分
    data = np.array([[5, 5, 0, 5],
                     [5, 0, 3, 4],
                     [3, 4, 0, 3],
                     [0, 0, 5, 3],
                     [5, 4, 4, 5],
                     [5, 4, 5, 5]])
    percentage = 0.9
    svdor = CSVD(data)
    ret = svdor.DimReduce(percentage)
    print('====================================================')
    print('原始用戶數據矩陣:\n', data)
    print('降維後的數據矩陣:\n', ret)
    print('====================================================')
    
if __name__=='__main__':
    CSVD_manual()

3.2運行結果

runfile('C:/Users/tom/svd_reduceDim_release.py', wdir='C:/Users/tom')

按照奇異值開方和佔比閾值percentage=0, 求得降維的k=2
====================================================
原始用戶數據矩陣:
 [[5 5 0 5]
 [5 0 3 4]
 [3 4 0 3]
 [0 0 5 3]
 [5 4 4 5]
 [5 4 5 5]]
降維後的數據矩陣:
 [[ 5.28849359  5.16272812  0.21491237  4.45908018]
 [ 3.27680994  1.90208543  3.74001972  3.80580978]
 [ 3.53241827  3.54790444 -0.13316888  2.89840405]
 [ 1.14752376 -0.64171368  4.94723586  2.3845504 ]
 [ 5.07268706  3.66399535  3.78868965  5.31300375]
 [ 5.10856595  3.40187905  4.6166049   5.58222363]]
====================================================

4.Python代碼實現(debug版本)

4.1代碼

注:此代碼爲展示示例的數學求解過程的代碼,不是精簡代碼

# -*- coding: utf-8 -*-
"""
@author: 蔚藍的天空Tom
Talk is cheap, show me the code
Aim:svd分解降維應用示例的代碼實現
CSDN URL:https://mp.csdn.net/postedit/80450590
"""

import numpy as np
from numpy import linalg as LA

class CSVD(object):
    '''
    實現SVD分解降維應用示例的數學求解過程的Python代碼
    '''
    
    def __init__(self, data):
        self.data = data       #用戶數據
        self.S = []  #用戶數據矩陣的奇異值序列 singular values
        self.U = []  #svd後的單位正交向量
        self.VT = []  #svd後的單位正交向量
        self.k = 0   #滿足self.p的最小k值(k表示奇異值的個數)
        self.SD = [] #對角矩陣,對角線上元素是奇異值 singular values diagonal matrix
        
        #svd奇異值分解
        self._svd()
        
    def _svd(self):
        '''
        用戶數據矩陣的svd奇異值分解
        '''
        u,s,v = np.linalg.svd(self.data)
        (self.U, self.S, self.VT) = (u, s, v)
        return self.U, self.S, self.VT
        
    def _calc_k(self, percentge):
        '''確定k值:前k個奇異值的平方和佔比 >=percentage, 求滿足此條件的最小k值
        :param percentage, 奇異值平方和的佔比的閾值
        :return 滿足閾值percentage的最小k值
        '''
        self.k = 0
        #用戶數據矩陣的奇異值序列的平方和
        total = sum(np.square(self.S))
        svss = 0 #奇異值平方和 singular values square sum
        for i in range(np.shape(self.S)[0]):
            svss += np.square(self.S[i])
            if (svss/total) >= percentge:
                self.k = i+1
                break
        return self.k

    def _buildSD(self, k):
        '''構建由奇異值組成的對角矩陣
        :param k,根據奇異值開放和的佔比閾值計算出來的k值
        :return 由k個前奇異值組成的對角矩陣
        '''
        #方法1:用數組乘方法
        self.SD = np.eye(self.k) * self.S[:self.k]

        #方法2:用自定義方法
        e = np.eye(self.k)
        for i in range(self.k):
            e[i,i] = self.S[i]

        return self.SD
    
    def DimReduce(self, percentage):
        '''
        SVD降維
        :param percentage, 奇異值開方和的佔比閾值
        :return 降維後的用戶數據矩陣
        '''
        #計算k值
        self._calc_k(percentage)
        print('\n按照奇異值開方和佔比閾值percentage=%d, 求得降維的k=%d'%(percentage, self.k))
        #構建由奇異值組成的對角矩陣singular values diagonal
        self._buildSD(self.k)
        k,U,SD,VT = self.k,self.U, self.SD, self.VT
        #按照svd分解公式對用戶數據矩陣進行降維,得到降維壓縮後的數據矩陣
        print('\n降維前的U,S,VT依次爲:')
        print(np.shape(U),      'U:\n', U)
        print(np.shape(self.S), 'S:\n', self.S)
        print(np.shape(VT),     'VT:\n', VT)
        print('\n降維後的U,SD,VT依次爲:')
        print(np.shape(U[:len(U),k]), 'U=U[:%d,:%d]:\n'%(len(U),k), U[:len(U), :k])
        print(np.shape(SD), 'SD=SD[:%d, :%d]:\n'%(k,k), SD[:k, :k])
        print(np.shape(VT[:k, :len(VT)]), 'VT=VT[:%d, :%d]:\n'%(k, len(VT)), VT[:k, :len(VT)])

        a = U[:len(U), :k]
        b = np.dot(SD, VT[:k, :len(VT)])
        newData = np.dot(a,b)
        return newData
        
def CSVD_manual():
    ##訓練數據集,用戶對商品的評分矩陣,行爲多個用戶對單個商品的評分,列爲用戶對每個商品的評分
    data = np.array([[5, 5, 0, 5],
                     [5, 0, 3, 4],
                     [3, 4, 0, 3],
                     [0, 0, 5, 3],
                     [5, 4, 4, 5],
                     [5, 4, 5, 5]])
    percentage = 0.9
    svdor = CSVD(data)
    ret = svdor.DimReduce(percentage)
    print('====================================================')
    print('原始用戶數據矩陣:\n', data)
    print('降維後的數據矩陣:\n', ret)
    print('====================================================')
    
if __name__=='__main__':
    CSVD_manual()

4.2運行結果

按照奇異值開方和佔比閾值percentage=0, 求得降維的k=2

降維前的U,S,VT依次爲:
(6, 6) U:
 [[-0.44721867  0.53728743  0.00643789 -0.50369332 -0.38572204 -0.32982993]
 [-0.35861531 -0.24605053 -0.86223083 -0.14584826  0.07797125  0.20015231]
 [-0.29246336  0.40329582  0.22754042 -0.10376096  0.4360044   0.70652449]
 [-0.20779151 -0.67004393  0.3950621  -0.58878098  0.02599042  0.06671744]
 [-0.50993331 -0.05969518  0.10968053  0.28687443  0.59460659 -0.53714128]
 [-0.53164501 -0.18870999  0.19141061  0.53413013 -0.54845844  0.24290419]]
(4,) S:
 [ 17.71392084   6.39167145   3.09796097   1.32897797]
(4, 4) VT:
 [[-0.57098887 -0.4274751  -0.38459931 -0.58593526]
 [ 0.22279713  0.51723555 -0.82462029 -0.05319973]
 [-0.67492385  0.69294472  0.2531966  -0.01403201]
 [ 0.41086611  0.26374238  0.32859738 -0.80848795]]

降維後的U,SD,VT依次爲:
(6,) U=U[:6,:2]:
 [[-0.44721867  0.53728743]
 [-0.35861531 -0.24605053]
 [-0.29246336  0.40329582]
 [-0.20779151 -0.67004393]
 [-0.50993331 -0.05969518]
 [-0.53164501 -0.18870999]]
(2, 2) SD=SD[:2, :2]:
 [[ 17.71392084   0.        ]
 [  0.           6.39167145]]
(2, 4) VT=VT[:2, :4]:
 [[-0.57098887 -0.4274751  -0.38459931 -0.58593526]
 [ 0.22279713  0.51723555 -0.82462029 -0.05319973]]
====================================================
===原始用戶數據矩陣:
 [[5 5 0 5]
 [5 0 3 4]
 [3 4 0 3]
 [0 0 5 3]
 [5 4 4 5]
 [5 4 5 5]]
===降維後的數據矩陣:
 [[ 5.28849359  5.16272812  0.21491237  4.45908018]
 [ 3.27680994  1.90208543  3.74001972  3.80580978]
 [ 3.53241827  3.54790444 -0.13316888  2.89840405]
 [ 1.14752376 -0.64171368  4.94723586  2.3845504 ]
 [ 5.07268706  3.66399535  3.78868965  5.31300375]
 [ 5.10856595  3.40187905  4.6166049   5.58222363]]
====================================================

參考文獻:

[1][機器學習筆記]奇異值分解SVD簡介及其在推薦系統中的簡單應用

[2]【機器學習】推薦系統、SVD分解降維

[3]SVD在推薦系統中的應用

[4]SVD Recommendation System in Ruby

[5]矩陣特徵值分解與奇異值分解含義解析及應用

[6]We Recommend a Singular Value Decomposition

(end)


發佈了248 篇原創文章 · 獲贊 190 · 訪問量 41萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章