PCA简介python的简单应用

发布于 2021-08-30  1670 次阅读


主成分分析

主成分分析是最为“简单粗暴”的一种数据降维方法,顾名思义,就是找到数据中最主要的方面,用这些方面来代替原始数据。

算法过程

  1. 对变量进行Z-Score标准化操作,消除变量间量纲不同造成的影响。
  2. 计算数据的协方差矩阵。
  3. 计算所得协方差矩阵的特征值(即主成分方差)和特征向量。
  4. 将所得特征值从大到小排序,并选择最大的k个特征值(即前k个主成分)对应的特征向量。具体k如何选择,需要计算前k个特征值的累计贡献率(碎石图比较不错)来决定。
  5. 将原始数据投影到所选取的k个特征向量组成的低维空间中,转化为新的样本。通过将原始数据乘以这k个特征向量即可得到。

优缺点

优点:

  • 计算方法简单,易于实现
  • 可消除原始变量之间的相关影响,因为主成分分析法在对原始数据变量进行变换后形成了彼此独立的主成分,而且实践证明变量间的相关程度越高,主成分分析效果越好。

缺点:

  • 主成分对数据含义的解释一般多少带有一点模糊性,不像原始变量的含义那么清楚、确切。
  • 方差晓得主成分也可能含有对样本差异的重要信息,丢弃后可能会对后续数据处理有不利影响。
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.datasets import load_iris

# 加载数据集
data = load_iris()
y = data.target
x = data.data

# 加载PCA算法,设置降维后主成分数目为2
pca = PCA(n_components=2)

# 得到新的样本点
reduced_x = pca.fit_transform(x)
red_x, red_y = [], []
blue_x, blue_y = [], []
green_x, green_y = [], []

for i in range(len(reduced_x)):
    if y[i] == 0:
        red_x.append(reduced_x[i][0])
        red_y.append(reduced_x[i][1])
    elif y[i] == 1:
        blue_x.append(reduced_x[i][0])
        blue_y.append(reduced_x[i][1])
    else:
        green_x.append(reduced_x[i][0])
        green_y.append(reduced_x[i][1])

# 结果可视化
plt.scatter(red_x, red_y, c='r', marker='o')
plt.scatter(blue_x, blue_y, c='b', marker='o')
plt.scatter(green_x, green_y, c='g', marker='o')
plt.show()

可以看到,将4维降到2维后,数据仍能够清晰地分成三类。这样不仅能削减数据的维度,降低分类任务的计算量,还能减少干扰,保证分类的质量。

注意

  • 原始数据中有多少个特征维度(变量),就会提取出多少个主成分。第一个主成分包含了原始数据中最多的信息,第二个主成分包含了第二多的信息,依次递减。主成分之间互相线性无关;
  • 选择保留多少个主成分,取决于前n个主成分的累计方差占比(累计贡献率)。一般来说,必须保证保留的前几个主成分的累计贡献率在较高水平(>85%);
  • 提取的主成分个数应明显少于原始特征的个数,否则降维带来的利可能还抵不过信息量损失所带来的弊。

示例demo:利用PCA进行mnist图像压缩

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_openml
from sklearn.decomposition import PCA

# 导入mnist数据集
mnist = fetch_openml('mnist_784')
x = mnist['data']
y = mnist['target']

# 调用PCA函数
pca = PCA()
pca.fit(x)

# 计算各个n下的累计贡献率。并计算累计贡献率第一次超过90%时的n
cumsum = np.cumsum(pca.explained_variance_ratio_)
d = np.argmax(cumsum >= 0.9) + 1 # argmax返回最大值所在的下标(从0开始)

# 绘制累计贡献率与主成分数目关系图
plt.plot(cumsum)
plt.ylim(0,1.1)
plt.xlabel('no. of principals', fontsize=12)
plt.ylabel('explained variance ratio', fontsize=12)
plt.show()
# 压缩时部分信息会有所丢失,图像清晰度也会随之降低。观察主成分数目为5,15,35,87时,图像的压缩效果。
Xr = []
for n in [5,15,35,87]:
    pca = PCA(n_components=n)
    X_reduced = pca.fit_transform(x) # 得到shape为(70000,n)的矩阵
    X_recovered = pca.inverse_transform(X_reduced) # 得到shape为(70000,784)的矩阵(降维后的标准化原始数据)
    Xr.append(X_recovered)

# 数据整理(将0-9每个数字取出来第一个进行观察其四个n下各图片的模糊度)
instances = []
for i in range(10):
    instances.append(x[y==str(i)].iloc[0])# y==i可以将所有标签为i的图片取出来,然后取第一张做研究标的
    for j in range(4):
        instances.append(Xr[j][y==str(i)][0])

# 绘制效果图
def plot_digits(instances):
    images = [instance.reshape(28,28) for instance in instances]
    row_images = []
    for row in range(5):
        rimages = images[row*5: (row+1)*5]
        row_images.append(np.concatenate(rimages, axis=1))# concatenate:连接
    image = np.concatenate(row_images, axis=0)
    plt.imshow(image, cmap=matplotlib.cm.binary)
    plt.axis('off')

plt.figure(figsize=(7, 4))
plt.subplot(121)
plot_digits(np.array(instances[:25]))
plt.subplot(122)
plot_digits(np.array(instances[25:]))
plt.show()

上面两幅图中第一列均为原始数据,第2、3、4、5列对应的主成分数目分别为5、15、35、87,可见当主成分数目为85时,图像已经相当清晰了(毕竟总共有784个特征)。