Pytorch和Tensorflow在相同数据规模规模下的降维SVD(Singular Value Decompositionm)算法中的运算速度对比
上一篇文章讲了如何在Pytorch和Tensorflow中使用PCA降维算法,这篇文章就来讲一下降维的另一种算法SVD算法。
那么既然有PCA那我们为什么要用SVD呢?
对于奇异值,它跟我们特征分解中的特征值类似,在奇异值矩阵中也是按照从大到小排列,而且奇异值的减少特别的快,在很多情况下,前10%甚至1%的奇异值的和就占了全部的奇异值之和的99%以上的比例。也就是说,我们也可以用最大的几个的奇异值和对应的左右奇异向量来近似描述矩阵。正是由于这个性质,SVD可以用于PCA降维,来做数据压缩和去噪。
PCA和SVD的区别
- SVD可以获取另一个方向上的主成分,而PCA只能获得单个方向上的主成分。
- 第二,PCA在计算机中运算量很大,而SVD更加容易得到结果。
- 通过SVD可以得到PCA相同的结果,但是SVD通常比直接使用PCA更稳定。因为PCA需要计算 X T X X^TX XTX 的值,对于某些矩阵,求协方差时很可能会丢失一些精度。
SVD算法的基本原理
我们在上一篇文章中了解到特征值与特征向量,其中
A
=
P
B
P
T
A=PBP^T
A=PBPT,P为A的特征向量组成的矩阵。那么我们可以假设A也可以被分解为
A
=
U
∑
V
T
A=U \sum V^T
A=U∑VT,其中矩阵A是一个m×n的矩阵,U是一个m×m的矩阵,Σ是一个m×n的矩阵,除了主对角线上的元素以外全为0,主对角线上的每个元素都称为奇异值,V是一个n×n的矩阵.
U
T
U
=
E
,
V
T
V
=
E
U^TU=E,V^TV=E
UTU=E,VTV=E(也有的书将E写为I)。
U
U
U被称为左奇异矩阵,
V
V
V被称为右奇异矩阵。
那么我们如何求要的U和V呢?
假设
A
=
U
∑
V
T
A=U \sum V^T
A=U∑VT那么
A
A
T
=
U
∑
V
T
V
∑
U
T
=
A
A
T
=
U
∑
2
U
T
AA^T=U \sum V^TV\sum U^T=AA^T=U {\sum }^2 U^T
AAT=U∑VTV∑UT=AAT=U∑2UT其中
V
T
V
=
E
V^TV=E
VTV=E,所以U就是
A
A
T
AA^T
AAT的特征向量,同理
A
T
A
=
V
∑
U
T
U
∑
V
T
=
V
∑
2
V
T
A^TA=V \sum U^TU\sum V^T=V{\sum }^2V^T
ATA=V∑UTU∑VT=V∑2VT其中
U
T
U
=
E
U^TU=E
UTU=E,所以V就是
A
T
A
A^TA
ATA的特征向量。
左奇异矩阵可以用于行数的压缩。
右奇异矩阵可以用于列数即特征维度的压缩,也就是我们的PCA降维。
代码实现(使用的CPU为锐龙R5-3500U)
iris数据集下
Pytorch下
from sklearn import datasets
import torch
import numpy as np
import matplotlib.pyplot as plt
import time
def pca_svd(data,k=2):
#将数据转化为tensor
X=torch.from_numpy(data)
#print(X)
#去中心化 避免数据过度损失
X_mean= torch.mean(X,0)
X=X-X_mean.expand_as(X)
#print(X)
#svd计算
U,S,V = torch.svd(X)
#print(S)
#print(V[:,:k])
return torch.mm(X,V[:,:k])
#数据准备
iris = datasets.load_iris()
X= iris.data
Y= iris.target
start = time.perf_counter()
X_PCA = pca_svd(X)
elapsed = (time.perf_counter()-start)
print(f'time use :{elapsed}')
pca = X_PCA.numpy()
#print(pca)
#可视化
plt.figure()
color=['red','green','blue']
for i, target_name in enumerate(iris.target_names):
plt.scatter(pca[Y==i,0],pca[Y==i,1],label = target_name, color = color[i])
plt.legend()
plt.title('svd')
plt.show
Tensorflow下
import tensorflow.compat.v1 as tf
# 使用Eager Execution动态图机制
tf.enable_eager_execution()
import numpy as np
from sklearn import datasets
import matplotlib.pyplot as plt
import time
def pca(x,dim = 2):
'''
x:输入矩阵
dim:降维之后的维度数
'''
with tf.name_scope("SVD"):#创建一个参数名空间
m,n= tf.to_float(x.get_shape()[0]),tf.to_int32(x.get_shape()[1])
assert not tf.assert_less(dim,n)
mean= tf.reduce_mean(x,0)
# 去中心化 防止数据过分逼近大的值 而忽略小的值
x_new = x - mean
#print(x_new)
# 计算奇异值
s, u, v = tf.linalg.svd(x_new)
pca = tf.matmul(x_new,v[:,:dim])
#print(v[:,:dim])
#print(v)
return pca
#数据准备
iris = datasets.load_iris(return_X_y=False)
start = time.perf_counter()
pca_data = tf.constant(np.reshape(iris.data,(iris.data.shape[0],-1)),dtype=tf.float32)
pca_data = pca(pca_data,dim=2)
elapsed = (time.perf_counter()-start)
print(f'time use :{elapsed}')
#print(pca_data)
#可视化
Y= iris.target
pca = pca_data.numpy()
plt.figure()
color=['red','green','blue']
for i, target_name in enumerate(iris.target_names):
plt.scatter(pca[Y==i,0],pca[Y==i,1],label = target_name, color = color[i])
plt.legend()
plt.title('svd')
plt.show
自定义10000*1000数据下
pytorch下
from sklearn import datasets
import torch
import numpy as np
import matplotlib.pyplot as plt
import time
def pca_svd(data,k=2):
#将数据转化为tensor
X=torch.from_numpy(data)
#print(X)
#去中心化 避免数据过度损失
X_mean= torch.mean(X,0)
X=X-X_mean.expand_as(X)
#print(X)
#svd计算
U,S,V = torch.svd(X)
#print(S)
#print(V[:,:k])
return torch.mm(X,V[:,:k])
#数据准备
n0=2*np.random.randn(5000,1000)+1
n1=-2*np.random.randn(5000,1000)-1
n=np.vstack((n0,n1))
start = time.perf_counter()
pca_data = pca_svd(n,k=2)
elapsed = (time.perf_counter()-start)
print(f'time use :{elapsed}')
Tensorflow下
import tensorflow.compat.v1 as tf
# 使用Eager Execution动态图机制
tf.enable_eager_execution()
import numpy as np
from sklearn import datasets
import matplotlib.pyplot as plt
import time
def pca(x,dim = 2):
'''
x:输入矩阵
dim:降维之后的维度数
'''
with tf.name_scope("SVD"):#创建一个参数名空间
m,n= tf.to_float(x.get_shape()[0]),tf.to_int32(x.get_shape()[1])
assert not tf.assert_less(dim,n)
mean= tf.reduce_mean(x,0)
# 去中心化 防止数据过分逼近大的值 而忽略小的值
x_new = x - mean
#print(x_new)
# 计算奇异值
s, u, v = tf.linalg.svd(x_new)
pca = tf.matmul(x_new,v[:,:dim])
#print(v[:,:dim])
#print(v)
return pca
#数据准备
n0=2*np.random.randn(5000,1000)+1
n1=-2*np.random.randn(5000,1000)-1
n=np.vstack((n0,n1))
start = time.perf_counter()
pca_data = tf.constant(np.reshape(n,(n.shape[0],-1)),dtype=tf.float32)
pca_data = pca_svd(pca_data,dim=2)
elapsed = (time.perf_counter()-start)
print(f'time use :{elapsed}')
从对比中可以看出在执行SVD时,pytorch比tensorflow快很多。同时我们观察下图pca与svd运行的相同数据集可以看出SVD的分类效果更好: