主成分分析
原理
通俗地讲,主成分分析(PCA)的原理是数据集旋转(可以是高维)后,使前面的特征对应的值的方差尽量大,只取前面若干个特征达到降维的目的。
方法:求出数据集的散度矩阵,求出它的特征值和特征向量,使用最大的K个(数量自定)特征值对应的特征向量作为基,使用矩阵乘法直接求出数据集变换后的结果。
参考链接
PCA csdn
PCA zhihu
刘建平大佬:SVD 顺便吹一波这位,资料很全推导很详细
作用
PCA和类似的降维算法有很大作用
- 数据降维:对高维数据进行降维,减轻机器学习的计算量。
- 数据可视化:高维的数据不利于人类的观察,故常常降维到二维或者三维
- 图像压缩:将一些眼睛难以察觉的细节去除,常见图像使用PCA的压缩率能够达到50%以上,而不损失大量细节。
- 数据降噪,包括图像降噪:噪声数据的方差一般比较小,而信号数据的方差一般比较大,所以PCA能够去除噪声。
python实现
由于PCA算法不依赖于数据集,无需进行训练等,故封装为一个函数。
#pca.py
import numpy as np
def pca(X:np.ndarray, K:int, debug:bool=False)->np.ndarray:
if np.shape(X)[1] <= K:
print("[-] dim too low");return
if K <= 0:
print("[-] K not valid");return
# centering the data
X_avg = np.average(X, axis=0)
X_centered = X - X_avg
# covariance matrix
cov_mat = X_centered @ X_centered.T
eigval, eigvec = np.linalg.eig(cov_mat)
eigval_target = eigval[0:K]
eigvec_target = eigvec[:, 0:K]
if debug : print(eigval, eigvec)
if debug : print(np.linalg.norm(np.abs(eigvec_target), axis=0))
# print(np.linalg.norm(eigvec/np.linalg.norm(eigvec, ord=2, axis=1), ord=2, axis=1))
# print(eigval_target, eigvec_target)
return np.array(cov_mat @ eigvec_target, dtype=np.float64)
测试上述代码,使用三维特征的数据集进行测试。
#main.py
import pca
import numpy as np
X = np.array(
[
[0., 0., 2.],
[1., 1., 3.],
[2., 2., 5.],
[-1., 0., 5.],
[4., 3., 2.]
])
print(pca.pca(X, 2))
测试结果如下所示
ComplexWarning: Casting complex values to real discards the imaginary part
return np.array(cov_mat @ eigvec_target, dtype=np.float64)
[[ 5.45643859 5.30087165]
[ 0.66197922 1.3243035 ]
[ -2.64791688 -5.29721401]
[ 13.72678804 -2.15574602]
[-17.19728897 0.82778488]]
可以看到对数据实现了降维
相关算法的应用
对图片进行压缩,原理见刘建平文
其中应用了SVD(奇异值分解),SVD也可以用于PCA降维
#svd.py
def image_compress_pca_svd(X:np.ndarray, K:int, debug:bool=False)->np.ndarray:
if len(np.shape(X)) != 2 : print("just support 2d data");return
u, sigma, vh = np.linalg.svd(X)
if debug:print(np.shape(sigma), "sigma shape")
if debug:print(np.shape(u), np.shape(sigma), np.shape(vh))
u[:, K:] = 0
vh[K:, :] = 0
s = np.zeros(np.shape(X), X.dtype)
for i in range(K):
s[i, i] = sigma[i]
return u @ s @ vh
利用图片进行测试
分别对三个通道进行压缩,之后进行还原,查看效果
#compress.py
import pca
import numpy as np
import matplotlib.pyplot as plt
img = plt.imread("what.jpg")
r = img[:, :, 0]
g = img[:, :, 1]
b = img[:, :, 2]
plt.imshow(img)
plt.show()
r1 = pca.image_compress_pca_svd(r, 400)
g1 = pca.image_compress_pca_svd(g, 400)
b1 = pca.image_compress_pca_svd(b, 400)
img1 = np.ndarray(np.shape(img), img.dtype)
img1[:, :, 0] = r1
img1[:, :, 1] = g1
img1[:, :, 2] = b1
# cv2.imshow("img1", img1)
plt.imshow(img1)
plt.show()
压缩前后图像
可以看出基本上还原了图片的效果而没有肉眼可见的损失
总结
- PCA降维算法是一种重要的数据处理方法
- SVD具有实际用途,是一种朴素的压缩方法,对于第二个维度的压缩,可以作为一种降维方法
- 实现起来也真方便:)