svd分解 复原 sklearn和numpy实现
迪丽瓦拉
2024-03-25 20:20:36
0

网上有很多分析svd分解原理的文章,例如下面的链接,
svd原理解释,本文主要介绍在sklearn和numpy中,如何进行svd分解以及如何复原,可以利用到图像压缩和复原等任务中。

原理

1.pca的主要过程

pca可以将m∗nm*nm∗n的矩阵AAA进行降维,需要先求出A的协方差矩阵C=1mATAC=\frac{1}{m}A^{T}AC=m1​ATA,再求出CCC的特征值和特征向量,并将前kkk大的特征值对应的特征向量按列拼接,得到P∈n∗kP \in n*kP∈n∗k,最后得到降维后的矩阵Anew=AP∈m∗kA_{new}=AP \in m*kAnew​=AP∈m∗k

2.svd的主要过程

svd的主要作用就也是将一个m∗nm*nm∗n的矩阵AAA进行降维,与pca不同,svd可以将一个矩阵AAA分解成A=USVTA=USV^{T}A=USVT的形式,UUU是m∗*∗m的矩阵,VVV是n∗*∗n的矩阵,SSS是m∗*∗n的对角矩阵,进行降维时,只需要计算A∗VTA*V^{T}A∗VT即可(需要取VtV^{t}Vt的前k行,才可以降维成m∗*∗ k的新矩阵)

代码实现

接下来就是具体实现了,主要对一个随机构造的矩阵xxx,进行svd降维和复原。

1. 使用sklearn进行降维和复原

首先构造数据,

from sklearn.decomposition import TruncatedSVD
from scipy.sparse import random as r
# 假设数据是5*10的
x=np.random.randint(1,10,size=[5,10])
print("原始数据\n",x)
原始数据[[4 5 5 5 9 1 4 7 3 9][7 7 3 5 2 7 3 7 8 3][3 2 5 8 4 9 4 2 6 7][7 5 2 5 8 6 5 5 9 9][5 3 4 8 9 9 1 1 1 2]]

降维,假设只保留5个主要维度,首先使用原生svd,得到降维后的结果 new_x :

#假设只保留5个主要维度
n_com=5
svd=TruncatedSVD(n_components=n_com)
svd.fit(x)
new_x=svd.transform(x)
# 降维后的结果
print(new_x)
[[16.34513059 -4.8106942  -5.82035261 -0.90765052 -1.73020042][16.20910717 -1.61394637  6.43060373 -2.77069517 -1.27695179][16.36007388  3.50982968  1.07136253  4.50162336 -1.61760551][19.80727866 -3.17339531  0.55105757  1.28356345  3.26344521][14.51824224  7.59233143 -2.58588072 -2.70863565  0.744083  ]]

也可以进行手工降维,主要用到svd.components_:

# 手工降维,right即为svd分解后的右矩阵
right=svd.components_
print(right.shape)
# 计算
print(np.dot(x,right[:,:].T))
(5, 10)
[[16.34513059 -4.8106942  -5.82035261 -0.90765052 -1.73020042][16.20910717 -1.61394637  6.43060373 -2.77069517 -1.27695179][16.36007388  3.50982968  1.07136253  4.50162336 -1.61760551][19.80727866 -3.17339531  0.55105757  1.28356345  3.26344521][14.51824224  7.59233143 -2.58588072 -2.70863565  0.744083  ]]

复原,可以直接得到原始的矩阵:

# sklearn可以直接复原
print(svd.inverse_transform(new_x))
[[4. 5. 5. 5. 9. 1. 4. 7. 3. 9.][7. 7. 3. 5. 2. 7. 3. 7. 8. 3.][3. 2. 5. 8. 4. 9. 4. 2. 6. 7.][7. 5. 2. 5. 8. 6. 5. 5. 9. 9.][5. 3. 4. 8. 9. 9. 1. 1. 1. 2.]]

2.使用numpy进行降维和复原

首先得到 u、s、vu、s、vu、s、v 三个矩阵

# 可以得到三个矩阵
u,s,v=np.linalg.svd(x)
print("u,s,v的形状:")
print("u:",u.shape)
print("s:",s.shape)
print("v:",v.shape)
u,s,v的形状:
u: (5, 5)
s: (5,)
v: (10, 10)

降维,利用 xxx 和 vvv 即可实现降维:

# 降维结果与sklearn基本一致,假设降维到n_com=5
print(np.dot(x,v[:n_com,:].T))
[[-16.34513059  -4.8106942    5.82035261  -0.90765052  -1.73020042][-16.20910717  -1.61394637  -6.43060373  -2.77069517  -1.27695179][-16.36007388   3.50982968  -1.07136253   4.50162336  -1.61760551][-19.80727866  -3.17339531  -0.55105757   1.28356345   3.26344521][-14.51824224   7.59233143   2.58588072  -2.70863565   0.744083  ]]

复原,需要依次取出 u、vu、vu、v 中对应的行或列,并按照 sss 矩阵中的权重值进行加权计算,可以得到原始的矩阵 xxx:

# m为原始行数,n为原始列数
m=5
n=10
# 将u、v、s三个矩阵进行运算,将结果累加到a中并返回
a=np.zeros([m,n])for i in range(0,n_com):# 依次取出u和v矩阵的对应数据,并reshape ui=u[:,i].reshape(m,1)vi=v[i].reshape(1,n)# 将其按照s的权重进行累加a+=s[i]*np.dot(ui,vi)# 结果与原始数据基本一致    
print(a)
[[4. 5. 5. 5. 9. 1. 4. 7. 3. 9.][7. 7. 3. 5. 2. 7. 3. 7. 8. 3.][3. 2. 5. 8. 4. 9. 4. 2. 6. 7.][7. 5. 2. 5. 8. 6. 5. 5. 9. 9.][5. 3. 4. 8. 9. 9. 1. 1. 1. 2.]]

相关内容