【机器学习】【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)


你可能感兴趣的:(人工智能,机器学习,跟我一起学机器学习,Machine,Learning)