网上有很多分析svd分解原理的文章,例如下面的链接,
svd原理解释,本文主要介绍在sklearn和numpy中,如何进行svd分解以及如何复原,可以利用到图像压缩和复原等任务中。
原理
1.pca的主要过程
pca可以将 m ∗ n m*n m∗n的矩阵 A A A进行降维,需要先求出A的协方差矩阵 C = 1 m A T A C=\frac{1}{m}A^{T}A C=m1ATA,再求出 C C C的特征值和特征向量,并将前 k k k大的特征值对应的特征向量按列拼接,得到 P ∈ n ∗ k P \in n*k P∈n∗k,最后得到降维后的矩阵 A n e w = A P ∈ m ∗ k A_{new}=AP \in m*k Anew=AP∈m∗k
2.svd的主要过程
svd的主要作用就也是将一个 m ∗ n m*n m∗n的矩阵 A A A进行降维,与pca不同,svd可以将一个矩阵 A A A分解成 A = U S V T A=USV^{T} A=USVT的形式, U U U是m ∗ * ∗m的矩阵, V V V是n ∗ * ∗n的矩阵, S S S是m ∗ * ∗n的对角矩阵,进行降维时,只需要计算 A ∗ V T A*V^{T} A∗VT即可(需要取 V t V^{t} Vt的前k行,才可以降维成m ∗ * ∗ k的新矩阵)
代码实现
接下来就是具体实现了,主要对一个随机构造的矩阵 x x x,进行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 、 v u、s、v u、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)
降维,利用 x x x 和 v v v 即可实现降维:
# 降维结果与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 、 v u、v u、v 中对应的行或列,并按照 s s s 矩阵中的权重值进行加权计算,可以得到原始的矩阵 x x x:
# 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.]]