摘要

  • 主成分分析(Principal component analysis, PCA)是一种常用的无监督学习方法。这一方法利用正交变换,把由线性相关变量表示的观测数据,转换为少数几个由线性无关变量表示的数据,线性无关的变量称为主成分。
  • 主成分的个数通常小于原始变量的个数,所以主成分分析属于降维方法。主成分分析主要用于发现数据中的基本结构,即数据中变量之间的关系,是数据分析的有力工具,也用于其他机器学习方法的前处理。主成分分析属于多元统计分析的经典方法。

1 总体主成分分析

1.1 基本思想

统计分析中,数据的变量之间可能存在相关性,以致增加了分析的难度。于是,考虑由少数不相关的变量来代替相关的变量,用来表示数据,并且要求能够保留数据中的大部分信息。

主成分分析中,首先对给定数据进行规范化,使得数据每一变量的平均值为0,方差为1.之后对数据进行正交变换,原来由线性相关变量表示的数据,通过正交变换成若干个新变量表示的数据。新变量是可能的正交变换中变量的方差的和(信息保存)最大的,方差表示在新变量上信息的大小。将新变量依次称为第一主成分、第二主成分。这就是主成分的基本思想。通过主成分分析,可以利用主成分近似地表示原始数据,可理解为发现数据的“基本结构”;也可以把数据由少数主成分表示,可理解为对数据降维。

在数据总体上进行道的主成分分析称为总体主成分分析,在有限样本上进行的主成分分析称为样本主成分分析,总体主成分分析是样本主成分分析的基础。

1.2 总体主成分分析定义

假设 x = ( x 1 , x 2 , ⋯   , x m ) T x=(x_1,x_2,\cdots,x_m)^T x=(x1,x2,,xm)T是m维随机变量,其均值向量是 μ \mu μ
μ = E ( x ) = ( μ 1 , μ 2 , ⋯   , μ m ) T \mu=E(x)=(\mu_1,\mu_2,\cdots,\mu_m)^T μ=E(x)=(μ1,μ2,,μm)T
协方差矩阵是 Σ \Sigma Σ
Σ = c o v ( x , x ) = E [ ( x − μ ) ( x − μ ) T ] \Sigma=cov(x,x)=E[(x-\mu)(x-\mu)^T] Σ=cov(x,x)=E[(xμ)(xμ)T]

考虑由m维随机变量x到m维随机变量 y = ( y 1 , y 2 , ⋯   , y m ) T y=(y_1,y_2,\cdots,y_m)^T y=(y1,y2,,ym)T的线性变换
y i = a i T x = a 1 i x 1 + a 2 i x 2 + ⋯ + a m i x m y_i=a_i^Tx=a_{1i}x_1+a_{2i}x_2+\cdots+a_{mi}x_m yi=aiTx=a1ix1+a2ix2++amixm
如果满足下列条件:
(1)系数向量 a i T a_i^T aiT是单位向量,即 a i T a i = 1 , i = 1 , 2 , ⋯   , m a_i^Ta_i=1,i=1,2,\cdots,m aiTai=1,i=1,2,,m;
(2)变量 y i y_i yi y j y_j yj互不相关,即 c o v ( y i , y j ) = 0 ( i ≠ j ) cov(y_i,y_j)=0(i\neq j) cov(yi,yj)=0(i=j)
(3)变量 y 1 y_1 y1是x的所有线性变换中方差最大的; y 2 y_2 y2是与 y 1 y_1 y1不相关的x的所有线性变换中方差最大的;一般地, y i y_i yi是与 y 1 , y 2 , ⋯   , y i − 1 ( i = 1 , 2 , ⋯   , m ) y_1,y_2,\cdots,y_{i-1}(i=1,2,\cdots,m) y1,y2,,yi1(i=1,2,,m)都不相关的x的所有线性变换中方差最大的;这时分别称 y 1 , y 2 , ⋯   , y m y_1,y_2,\cdots,y_m y1,y2,,ym为x的第一主成分、第二主成分、、、第m主成分。

1.3 主成分的个数

主成分分析的主要目的就是降维,所以一般选择 k ( k ≪ m ) k(k \ll m) k(km)个主成分(线性无关变量)来代替m个原有变量(线性相关变量),使问题得以简化,并能保留原有变量的大部分信息。这里所说的信息是指原有变量的方差,

假设x是m维随机变量,其协方差矩阵是 Σ \Sigma Σ Σ \Sigma Σ的特征值分别是 λ 1 ≥ λ 2 ≥ ⋯ λ m ≥ 0 \lambda_1 \ge \lambda_2 \ge \cdots \lambda_m \ge 0 λ1λ2λm0,特征值对应的单位特征向量是 a 1 , a 2 , ⋯   , a m a_1,a_2,\cdots,a_m a1,a2,,am,则x的第i主成分可以写作
y i = a i T x = ∑ k = 1 m a k i x k , i = 1 , 2 , ⋯   , m y_i=a_i^Tx=\sum_{k=1}^ma_{ki}x_k,i=1,2,\cdots,m yi=aiTx=k=1makixk,i=1,2,,m
并且,x的第i主成分的方差是协方差矩阵 Σ \Sigma Σ的第i个特征值,即
v a r ( y i ) = a i T Σ a i = λ i var(y_i)=a_i^T\Sigma a_i=\lambda_i var(yi)=aiTΣai=λi

1.4 规范化变量的总体主成分分析

在实际问题中,不同变量可能有不同的量纲,直接求主成分有时会产生不合理的结果。为了消除这个影响,常常对各个随机变量实施规范化,使其均值为0,方差为1.

x = ( x 1 , x 2 , ⋯   , x m ) T x=(x_1,x_2,\cdots,x_m)^T x=(x1,x2,,xm)T为m维随机变量, x i x_i xi为第i个随机变量, i = 1 , 2 , ⋯   , m i=1,2,\cdots,m i=1,2,,m,令
x i ∗ = x i − E ( x i ) ( v a r ( x i ) ) , i = 1 , 2 , ⋯   , m x_i^*={{x_i-E(x_i)}\over{\sqrt(var(x_i))}},i=1,2,\cdots,m xi=( var(xi))xiE(xi),i=1,2,,m
其中, E ( x i ) , v a r ( x i ) E(x_i),var(x_i) E(xi),var(xi)分别是随机变量 x i x_i xi的均值和方差,这时 x i ∗ x_i^* xi就是 x i x_i xi的规范化随机变量。

显然,规范化随机变量的协方差矩阵就是相关矩阵R。主成分分析通常在规范化随机变量的协方差矩阵即相关矩阵上进行。

2 样本主成分分析

在实际问题中,需要在观测数据上进行主成分分析,这就是样本主成分分析。样本主成分也和总体主成分具有相同的性质。

2.1 样本主成分的定义和性质

给定样本矩阵X。样本第一主成分 y 1 = a 1 T x y_1=a_1^Tx y1=a1Tx是在 a 1 T a 1 = 1 a_1^Ta_1=1 a1Ta1=1条件下,使得 a 1 T x j ( j = 1 , 2 , ⋯   , n ) a_1^Tx_j(j=1,2,\cdots,n) a1Txj(j=1,2,,n)的样本方差 a 1 S a 1 a_1Sa_1 a1Sa1最大的x的线性变换;

样本第二主成分 y 2 = a 2 T x y_2=a_2^Tx y2=a2Tx是在 a 2 T a 2 = 1 a_2^Ta_2=1 a2Ta2=1 a 2 T x j a_2^Tx_j a2Txj a 1 T x j ( j = 1 , 2 , ⋯   , n ) a_1^Tx_j(j=1,2,\cdots,n) a1Txj(j=1,2,,n)的样本协方差 a 1 T S a 2 = 0 a_1^TSa_2=0 a1TSa2=0条件下,使得 a 2 T x j ( j = 1 , 2 , ⋯   , n ) a_2^Tx_j(j=1,2,\cdots,n) a2Txj(j=1,2,,n)的样本方差 a 2 T S a 2 a_2^TSa_2 a2TSa2最大的x的线性变换。

2.2 主成分分析方法

主成分分析方法主要有两种,可以通过相关矩阵的特征值分解或样本矩阵的奇异值分解进行。

2.2.1 相关矩阵的特征值分解

相关矩阵的特征值分解算法

针对 m × n m\times n m×n样本矩 X X X,求样本相关矩阵
R = 1 n − 1 X X T R={1\over{n-1}}XX^T R=n11XXT
再求样本相关矩阵的k个特征值和对应的单位特征向量,构成正交矩阵
V = ( v 1 , v 2 , ⋯   , v k ) V=(v_1,v_2,\cdots,v_k) V=(v1,v2,,vk)
V V V的每一列对应一个主成分,得到 k × n k\times n k×n样本主成分矩阵
Y = V T X Y=V^TX Y=VTX

2.2.2 样本矩阵的奇异值分解

矩阵X的奇异值分解算法

针对 m × n m\times n m×n样本矩阵 X X X
X ′ = 1 ( n − 1 ) X T X^{'}={1\over{\sqrt(n-1)}}X^T X=( n1)1XT
对矩阵 X ′ X^{'} X进行截断奇异值分解,保留k个奇异值、奇异向量,得到
X ′ = U S V T X^{'}=USV^T X=USVT
V的每一列对应一个主成分,得到 k × n k\times n k×n样本主成分矩阵Y
Y = V T X Y=V^TX Y=VTX

3 主成分分析应用

3.1 数据降维

  • 数据压缩:从二维数据压缩到一维数据

在这里插入图片描述

  • 数据压缩:从三维数据压缩到二维数据

在这里插入图片描述

  • 降维分析:找到数据最重要的方向,即方差最大的方向

在这里插入图片描述

  • 降维分析:第一个主成分就是从数据差异性最大(方差最大)的地方提取出来的,第二个主成分则来自于数据差异次大的方向,并且要与第一个主成分方向正交。

在这里插入图片描述

  • PCA不是线性回归

在这里插入图片描述

3.2 PCA算法流程

  1. 数据预处理:中心化 X − X ˉ X-\bar X XXˉ
  2. 求样本的协方差矩阵 1 m X X T {1\over m}XX^T m1XXT
  3. 对协方差矩阵 1 m X X T {1\over m}XX^T m1XXT做特征值分解
  4. 选出最大的K个特征值对应的K个特征向量
  5. 将原始数据投影到选取的特征向量上
  6. 输出投影后的数据集

方差描述一个数据的离散程度。
v a r ( X ) = ∑ i = 1 n ( X i − X ˉ ) ( X i − X ˉ ) n − 1 var(X)={{\sum_{i=1}^n(X_i-\bar X)(X_i-\bar X)}\over{n-1}} var(X)=n1i=1n(XiXˉ)(XiXˉ)

协方差描述两个数据的相关性,接近1就是正相关,接近-1就是负相关,接近0就是不相关。
c o v ( X , Y ) = ∑ i = 1 n ( X i − X ˉ ) ( Y i − Y ˉ ) n − 1 cov(X,Y)={{\sum_{i=1}^n(X_i-\bar X)(Y_i-\bar Y)}\over{n-1}} cov(X,Y)=n1i=1n(XiXˉ)(YiYˉ)

协方差只能处理二维问题,对于多维数据自然需要计算多个协方差,可以使用矩阵来组织这些数据。协方差是一个对称矩阵,而且对角线线是各个维度的方差。

二维数据
C = ( c o v ( x , x ) c o v ( x , y ) c o v ( y , x ) c o v ( y , y ) ) C= \begin{pmatrix} cov(x,x) & cov(x,y) \\ cov(y,x) & cov(y,y) \\ \end{pmatrix} C=(cov(x,x)cov(y,x)cov(x,y)cov(y,y))

三维数据
C = ( c o v ( x , x ) c o v ( x , y ) c o v ( x , z ) c o v ( y , x ) c o v ( y , y ) c o v ( y , z ) c o v ( z , x ) c o v ( z , y ) c o v ( z , z ) ) C= \begin{pmatrix} cov(x,x) & cov(x,y) & cov(x,z) \\ cov(y,x) & cov(y,y) & cov(y,z) \\ cov(z,x) & cov(z,y) & cov(z,z) \\ \end{pmatrix} C=cov(x,x)cov(y,x)cov(z,x)cov(x,y)cov(y,y)cov(z,y)cov(x,z)cov(y,z)cov(z,z)

3.3 特征值与特征向量

通过数据集的协方差矩阵及其特征值分析,可以得到协方差矩阵的特征向量和特征值,需要保留K个维度的特征就选取最大的K个特征值。

3.4 Python实现PCA降维

原始数据

在这里插入图片描述

完整程序

import numpy as np
import matplotlib.pyplot as plt

# 载入数据
data = np.genfromtxt('./dataset/data.csv', delimiter=',', dtype=np.float, skip_header=1)
x_data = data[:, 0]
y_data = data[:, 1]
plt.scatter(x_data, y_data)
plt.show()


# 数据中心化
def zeroMean(dataMat):
    # 按列求平均,即各个特征的平均
    meanVal = np.mean(dataMat, axis=0)
    newData = dataMat - meanVal
    return newData, meanVal


newData, meanVal = zeroMean(data)
# np.cov用来求协方差矩阵,参数rowvar=0说明数据一行代表一个样本
covMat = np.cov(newData, rowvar=0)
print('协方差矩阵:', covMat)

# 求矩阵的特征值和特征向量
eigVals, eigVects = np.linalg.eig(np.mat(covMat))
print('特征值:\n', eigVals)
print('特征向量:\n', eigVects)

# 对特征值从小到大排序
eigValIndice = np.argsort(eigVals)
print('eigValIndice:', eigValIndice)

top = 1
# 最大的top个特征值的下标
n_eigValIndice = eigValIndice[-1:-(top + 1):-1]
print("n_eigValIndice:", n_eigValIndice)

# 最大的n个特征值对应的特征向量
n_eigVect = eigVects[:, n_eigValIndice]
print("n_eigVect:", n_eigVect)

# 低维特征空间的数据
lowDDataMat = newData * n_eigVect
print("lowDDataMat:\n", lowDDataMat)

# 利用低维度数据重构原始数据
reconMat = (lowDDataMat * n_eigVect.T) + meanVal
print("reconMat:\n", reconMat)

# 载入数据
data = np.genfromtxt('./dataset/data.csv', delimiter=',', skip_header=1)
x_data = data[:, 0]
y_data = data[:, 1]
plt.scatter(x_data, y_data)

# 重构数据
x_data = np.array(reconMat)[:, 0]
y_data = np.array(reconMat)[:, 1]
plt.scatter(x_data, y_data, c='r')
plt.show()
print(type(reconMat))
print(reconMat.shape)
print(type(np.array(reconMat)))
print(np.array(reconMat).shape)

蓝色为原始数据,红色为PCA降维数据

在这里插入图片描述

3.5 手写数字识别降维及可视化

from sklearn.neural_network import MLPClassifier
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix
import numpy as np
import matplotlib.pyplot as plt

digits = load_digits()  # 载入数据
x_data = digits.data  # 数据
print('x_data.shape:', x_data.shape)
y_data = digits.target  # 标签
print('y_data.shape:', y_data.shape)

# 分割数据:0.75训练集,0.25测试集
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data)

# 建立多层全连接神经网络,第一层100个神经元,第二层50个神经元
mlp = MLPClassifier(hidden_layer_sizes=(100, 50), max_iter=500)
print(mlp)
mlp.fit(x_train, y_train)
score = mlp.score(x_train, y_train)
print(score)


# 数据中心化
def zeroMean(dataMat):
    # 按列求平均,即各个特征的平均
    meanVal = np.mean(dataMat, axis=0)
    newData = dataMat - meanVal
    return newData, meanVal


def pca(dataMat, top):
    newData, meanVal = zeroMean(dataMat)  # 数据中心化
    covMat = np.cov(newData, rowvar=0)  # 求协方差矩阵,每一行代表一个样本
    eigVals, eigVects = np.linalg.eig(np.mat(covMat))  # 求矩阵的特征值和特征向量
    eigValIndice = np.argsort(eigVals)  # 对特征值从小到大排序
    n_eigValIndice = eigValIndice[-1:-(top + 1):-1]  # 最大的n个特征值的下标
    n_eigVect = eigVects[:, n_eigValIndice]  # 最大的n个特征值对应的特征向量
    lowDDataMat = newData * n_eigVect  # 低维特征空间的数据
    reconMat = (lowDDataMat * n_eigVect.T) + meanVal  # 利用低维度数据来重构原始数据
    # 返回低维特征空间的数据和重构的矩阵
    return lowDDataMat, reconMat


def pca_64_to_2():
    # 从64维下降到2维
    lowDDataMat, reconMat = pca(x_data, 2)

    # 重构的数据
    x = np.array(lowDDataMat)[:, 0]
    y = np.array(lowDDataMat)[:, 1]
    plt.scatter(x, y, c='r')
    plt.show()

    # 重构的数据
    predictions = mlp.predict(x_data)
    x = np.array(lowDDataMat)[:, 0]
    y = np.array(lowDDataMat)[:, 1]
    plt.scatter(x, y, c=predictions)
    plt.show()


def pca_64_to_3():
    # 从64维下降到3维
    lowDDataMat, reconMat = pca(x_data, 3)

    # 重构的数据
    from mpl_toolkits.mplot3d import Axes3D

    x = np.array(lowDDataMat)[:, 0]
    y = np.array(lowDDataMat)[:, 1]
    z = np.array(lowDDataMat)[:, 2]
    ax = plt.figure().add_subplot(111, projection='3d')
    ax.scatter(x, y, z, c=y_data, s=10)
    plt.show()


if __name__ == '__main__':
    pca_64_to_3()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4 数据降维在图像识别领域的应用

4.1 导入模块

"""
1.导入模块
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC  # 向量机
# 主成分分析,主要用于数据降维
from sklearn.decomposition import PCA
# 用于切割训练数据集和样本数据
from sklearn.model_selection import train_test_split
# 手写数字识别数据集
from sklearn.datasets import load_digits

4.2 生成训练数据和测试数据

"""
2.生成训练数据和测试数据
"""
digits = load_digits()  # 载入数据
x_data = digits.data  # 数据
print('x_data:', x_data.shape)  # x_data: (1797, 64)
y_data = digits.target  # 标签
print('y_data:', y_data.shape)  # y_data: (1797,)

# 分割数据:0.8训练集,0.2测试集
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=0.2)
print('train:', x_train.shape, y_train.shape)  # train: (1437, 64) (1437,)
print('test:', x_test.shape, y_test.shape)  # test: (360, 64) (360,)

展示数据

def show_img():
    img = x_data[1, :]
    img = img.reshape(8, 8)
    label = y_data[1]
    print(img.shape) # (8, 8)
    plt.imshow(img)
    plt.title('label:' + str(label))
    plt.show()

在这里插入图片描述

4.3 对数据进行降维处理

"""
3.对数据进行降维处理
"""
# PCA用于数据降维,减少运算时间,避免过拟合
# n_components参数设置需要保留特征的数量,如果是小数,则表示保留特征的比例

# 3.1 创建PCA对象
pca = PCA(n_components=32, whiten=True)

# 3.2 使用PCA训练数据
pca.fit(x_train, y_train)

# 3.3 对训练数据进行降维
x_train_pca = pca.transform(x_train)
x_test_pca = pca.transform(x_test)
# 由原来的64个特征降为现在的32个特征
print('x_train_pca:', x_train_pca.shape)  # (1437, 32)
print('x_test_pca:', x_test_pca.shape)  # (360, 32)

4.4 创建学习模型

"""
4.创建学习模型
"""
svc_pca = SVC(kernel='rbf')
svc = SVC(kernel='rbf')

4.5 使用降维后的数据进行模型训练

"""
5.使用降维后的数据进行模型训练
"""
svc_pca.fit(x_train_pca, y_train)
# 模型执行降维后数据的评分
score_train_pca = svc_pca.score(x_train_pca, y_train)
score_test_pca = svc_pca.score(x_test_pca, y_test)
print('score_train_pca:', score_train_pca)  # 1.0
print('score_test_pca:', score_test_pca)  # 0.9888

svc.fit(x_train, y_train)
# 模型不执行降维后数据的评分
score_train = svc.score(x_train, y_train)
score_test = svc.score(x_test, y_test)
print('score_train:', score_train)  # 0.9951
print('score_test:', score_test)  # 0.9655

4.6 预测结果

"""
6.预测结果
"""
y_pre_svc_pca = svc_pca.predict(x_test_pca)

4.7 展示结果

"""
7.展示结果
"""
# 展示前100个测试样本数据
samples = x_test[:100]
y_pre = y_pre_svc_pca[:100]

plt.figure(figsize=(12, 18))
for i in range(100):
    plt.subplot(10, 10, i + 1)
    plt.imshow(samples[i].reshape(8, 8), cmap='gray')
    title = 'True:' + str(y_test[i]) + '\nSVC:' + str(y_pre[i])
    plt.title(title)
    plt.xticks([])
    plt.yticks([])
plt.show()

在这里插入图片描述

5 PCA降维应用:高维数据可视化

  • 在数据预处理和特征工程部分,有一种重要的特征选择方法:方差过滤。如果一个特征的方差很小,则意味着这个特征上很可能有着大量取值都相同,比如90%都是1,只有10%是0,甚至100%都是1,那么这个特征的取值对样本而言就没有区分度,这样的特征几乎不包含任何有效信息。
  • 从方差的这种应用可以推断出,如果一个特征的方差很大,则说明这个特征上带有大量的信息。因此在降维中,PCA使用的信息量衡量指标就是样本方差,又称为解释性方差。如果某一特征的方差越大,该特征所带的信息量就越多。

5.1 鸢尾花数据降维

  • 基于Sklearn中PCA方法对鸢尾花数据集进行可视化
  • 鸢尾花数据集共有150个样本,分为三个类别,每个类别各50个样本;在每个样本中,有四个特征标示样本特征,根据这四个特征对鸢尾花进行分类。
# 高维数据的可视化
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
import pandas as pd

iris = load_iris()
x = iris.data  # 特征矩阵
y = iris.target  # 标签(三种鸢尾花的类别)
print('x:', x.shape)  # (150, 4)
print('y:', y.shape)  # (150,)
  • 实例化PCA对象:pca = PCA(n_components=2),参数n_components表示指定要下降到多少维
pca = PCA(n_components=2)  # 保留两个特征
x_dec = pca.fit_transform(x)  # 拟合并导出新的特征矩阵
print('x_dec:', x_dec.shape)  # (150, 2)
  • 原始的鸢尾花数据集是四维的,基于PCA方法进行降维到2维后,就可以在二维平面进行数据的可视化
plt.scatter(x_dec[y == 0, 0], x_dec[y == 0, 1], label=iris.target_names[0])
plt.scatter(x_dec[y == 1, 0], x_dec[y == 1, 1], label=iris.target_names[1])
plt.scatter(x_dec[y == 2, 0], x_dec[y == 2, 1], label=iris.target_names[2])
plt.title("PCA OF IRIS")
plt.legend()
plt.show()
  • 显然,簇的分布是很明显的。此时,不论使用SVM、KNN或随机森林都不容易分错,准确率基本都在95%以上,所以即使将数据从四维降到二维,对模型的分类效果影响甚微。

在这里插入图片描述

5.2 PCA方法属性

5.2.1 pca.explained_variance_

  • explained_variance_表示降维后新特征上所带的信息量的大小,即可解释方差的大小
# 探索降维后的数据
# 表示降维后新特征上所带的信息量的大小,也就是可解释方差的大小
explained_var = pca.explained_variance_
print('explained_var:', explained_var)  # [4.22824171 0.24267075]

5.2.2 pca.explained_variance_ratio_

  • explained_variance_ratio_表示降维后,每个特征向量所包含的信息量,占原始数据总信息量的百分比
# 表示降维后每个新特征向量所包含的信息量,占原始数据总信息量的百分比
# 又称做可解释性方差贡献率
explained_var_radio = pca.explained_variance_ratio_
# 由输出结果可见,大部分信息都集中在第一个特征上 [0.92461872 0.05306648]
# 0.92461872+0.05306648=0.9776852,表明主成分分析之后保留了97.76%的原始数据特征
print('explained_var_radio:', explained_var_radio)

5.2.3 n_components

  • 对于参数n_components,有多种类型取值,可以是整数,表示降维之后特征的数量;
pca = PCA(n_components=2)  # 保留两个特征
x_dec = pca.fit_transform(x)  # 拟合并导出新的特征矩阵
print('x_dec:', x_dec.shape)  # (150, 2)
  • 可以是[0,1]之间的小数,表示希望保留的信息量的百分比;
# 按照信息量占比选择超参数
pca = PCA(n_components=0.95)
x_radio = pca.fit_transform(x)
print(x_radio.shape)  # (150, 2)
print(pca.explained_variance_)  # [4.22824171 0.24267075]
print(pca.explained_variance_ratio_)  # [0.92461872 0.05306648]
  • 可以是字符类型mle,表示极大似然估计自选超参数,此时会自动选择整数值;
# 极大似然估计自选超参数,选择的新特征数量是3
pca = PCA(n_components='mle')
x_mle = pca.fit_transform(x)
print('x_mle:', x_mle.shape)  # (150, 3)
print(pca.explained_variance_)  # [4.22824171 0.24267075 0.0782095 ]
print(pca.explained_variance_ratio_)  # [0.92461872 0.05306648 0.01710261]
  • 可以直接使用PCA(k)的形式,甚至可以不填,默认是min(x.shape),由于一般特征数量小于样本数量,所以结果通常是特征数量。
# n_components不填,默认是min(x.shape),通常是特征数量
pca = PCA()
x_def = pca.fit_transform(x)
print('x_def:', x_def.shape)  # (150, 4)
print(pca.explained_variance_)  # [4.22824171 0.24267075 0.0782095  0.02383509]
print(pca.explained_variance_ratio_)  # [0.92461872 0.05306648 0.01710261 0.00521218]
  • 容易看出来第一个特征(向量)几乎包含所有的原始数据信息。如果原始数据维数较多,可以发现前几个特征几乎包含了所有的元素信息,所以通常取前K个特征就可以了。

5.3 主成分分析(PCA)与奇异值分解(SVD)

机器学习理论《统计学习方法》学习笔记:第十五章 奇异值分解

使用PCA进行降维的时候需要计算协方差矩阵 X X T XX^T XXT,其中 X = ( x i j ) m × n X=(x_{ij})_{m\times n} X=(xij)m×n表示特征矩阵,这个矩阵的计算量是非常大的,Sklearn中会使用SVD来帮助计算,大致的流程如下:

在这里插入图片描述

# SVD(奇异值分解)在fit中进行,分解结果除V(k,n)以外,其他的都被舍弃
# V(k,n)被保存在components_属性值中
pca = PCA(2)
pca.fit(x)
print('components_:', pca.components_)
print('shape:', pca.components_.shape)

输出结果

components_: [[ 0.36138659 -0.08452251  0.85667061  0.3582892 ]
 [ 0.65658877  0.73016143 -0.17337266 -0.07548102]]
shape: (2, 4)

6 PCA人脸数据降维与还原

6.1 导入人脸识别数据

#人脸识别案例
from sklearn.datasets import fetch_lfw_people
from sklearn.decomposition import PCA
import numpy as np
import matplotlib.pyplot as plt

faces = fetch_lfw_people()
faces.data.shape,faces.images.shape  #62*47=2924,像素 前者是2维数组后者是3维数组

输出:((13233, 2914), (13233, 62, 47))

figs,objs = plt.subplots(
3,5 # 3行5列
,figsize=(8,5) # 每个人脸图像的大小为8*5
,subplot_kw={"xticks":[],"yticks":[]} #不显示坐标轴
)
for i,obj in enumerate(objs.flat):#或者直接使用faces.images[i]
    obj.imshow(faces.data[i].reshape(62,47),cmap="gray")

在这里插入图片描述

6.2 利用PCA对数据降维

现在我们进行降维,使用fit后得到的矩阵V来画图

#提取新特征空间   降维
x = faces.data
pca = PCA(200).fit(x)
V = pca.components_
#V.shape   #输出(200, 2914)  k=200,n=2914
figs,objs = plt.subplots(2,5,figsize=(8,5),subplot_kw={"xticks":[],"yticks":[]})
for i,obj in enumerate(objs.flat):
    obj.imshow(V[i,:].reshape(62,47),cmap="gray")

在这里插入图片描述
比起降维前的数据,新特征空间可视化后的人脸非常模糊,这是因为原始数据还没有被映射到特征空间中。但是可以看出,整体比较亮的图片,获取的信息较多,整体比较暗的图片,却只能看见黑漆漆的一块。在比较亮的图片中,眼睛,鼻子,嘴巴,都相对清晰,脸的轮廓,头发之类的比较模糊。

这说明,新特征空间里的特征向量们,大部分是"五官"和"亮度"相关的向量,所以新特征向量上的信息肯定大部分是由原数据中和"五官"和"亮度"相关的特征中提取出来的。到这里,我们通过可视化新特征空间V,解释了一部分降维后的特征:虽然显示出来的数字看着不知所云,但画出来的图表示,这些特征是和”五官“以及”亮度“有关的。这也再次证明了,PCA能够将原始数据集中重要的数据进行聚集

6.3 pca.inverse_transform

pca.inverse_transform接口可以将归一化、标准化甚至做过哑变量的特征矩阵,还原到原始数据中的特征矩阵。

#人脸识别案例
from sklearn.decomposition import PCA
from sklearn.datasets import fetch_lfw_people
import numpy as np
import matplotlib.pyplot as plt

faces = fetch_lfw_people()
x = faces.data
pca = PCA(200)
x_face = pca.fit_transform(x)
print(x_face.shape) # (13233, 200)
x_inverse = pca.inverse_transform(x_face)
print(x_inverse.shape) # (13233, 2914)

figs, objs = plt.subplots(2,10,figsize=(10,2.5),subplot_kw={"xticks":[],"yticks":[]})
for i in range(10):
    objs[0,i].imshow(x[i].reshape(62,47),cmap="gray")
    objs[1,i].imshow(x_inverse[i].reshape(62,47),cmap="gray")

在这里插入图片描述

  • 对比一下,一个是原始数据得到的人脸,一个是降维后使用接口inverse_transform还原回去的数据,得到的人脸是非常相似的,虽然后者有点模糊。

  • 这说明,inverse_transform并没有实现数据的完全逆转。这是因为,在降维的时候,部分信息已经被舍弃了,X_face中往往不会包含原数据100%的信息,所以在逆转的时候,即便维度升高,原数据中已经被舍弃的信息也不可能再回来了。

  • 所以,降维不是完全可逆的,但是同时也说明了,使用200维度,的确保留了原数据的大部分信息,所以图像看起来,才会和原数据高度相似,只是稍稍模糊罢了。

7 使用PCA进行降噪

  • 降维的目的之一就是希望抛弃对模型带来负面影响的特征,而且含有效信息的特征的方差应该是远大于噪声的,所以相比于噪声,有效的特征所带来的信息应该不会在PCA过程中被大量抛弃。
  • inverse_transform能够在不恢复原始数据的情况下,将降维后的数据返回到原本的高维空间,即能够实现保证维度,但去掉方差很小特征所带来的信息。利用inverse_transform这个性质,能够实现噪音过滤。
# 用PCA做噪声过滤
from sklearn.datasets import load_digits
dig=load_digits()
dig.data.shape,dig.images.shape

((1797, 64), (1797, 8, 8))

# 定义画图的方法
%matplotlib inline
import matplotlib.pyplot as plt
def draw_img(data):
    figs,objs=plt.subplots(2,10,figsize=(10,2.5),subplot_kw={'xticks':[],'yticks':[]})
    for i,obj in enumerate(objs.flat):
        obj.imshow(data[i].reshape(8,8),cmap='binary')
draw_img(dig.data)
plt.show()

在这里插入图片描述
给数据加上噪声

# 在指定数据集中,随机抽取服从正太分布的数据
# 两个参数,分别是指定的数据集和抽取出来的正太分布的方差
import numpy as np
noisy=np.random.normal(dig.data,2)
draw_img(noisy)

在这里插入图片描述
对含有噪声的数据进行降维,然后还原

# 先降维,再还原
from sklearn.decomposition import PCA
pca=PCA(n_components=0.9)
x_dr=pca.fit_transform(noisy)
x_inverse=pca.inverse_transform(x_dr)
draw_img(x_inverse)

在这里插入图片描述

参考文献

  1. 李航《统计学习方法》第二版
  2. https://www.bilibili.com/video/BV1Rt411q7WJ
  3. https://blog.csdn.net/weixin_38748717/article/details/78847452
  4. https://blog.csdn.net/qq_37334135/article/details/87026462
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐