跳至内容

如何在 Python 中进行主成分分析(PCA)

了解 PCA 以及如何借助两个常用数据集(Breast Cancer 和 CIFAR-10)在无监督情况下从数据中提取信息。
已更新 2026年10月8日  · 15分钟 阅读

使用 AI 探索

ChatGPTClaudePerplexity

在线运行和编辑此教程中的代码

运行代码

主成分分析(PCA)是一种线性降维技术,它通过将高维空间投影到低维子空间来提取信息。如果您熟悉线性代数的语言,也可以说主成分分析是寻找协方差矩阵的特征向量,以识别数据中方差最大的方向。

关于 PCA 有一点很重要:它是一种无监督的降维技术,因此您可以根据数据点之间的相关性将相似的数据点聚类,而无需任何监督(或标签)。

说明:Features、Dimensions 和 Variables 都指的是同一件事,文中会交替使用。

列代表特征/属性/变量;行代表样本

PCA 可以用在哪里?

  • 数据可视化:处理任何与数据相关的问题,今天最大的挑战在于数据的体量以及定义该数据的变量/特征数量。要解决以数据为关键的问题,您需要进行广泛的数据探索,比如找出变量之间的相关性或理解部分变量的分布。考虑到数据沿着大量变量或维度分布,可视化会变得很有挑战,甚至几乎不可能。

    因此,PCA 可以通过将数据投影到更低的维度,帮助您在 2D 或 3D 空间中以肉眼进行可视化。

  • 加速机器学习(ML)算法:由于 PCA 的核心思想是降维,当您的数据有大量特征且机器学习算法学习过慢时,您可以借助 PCA 来加速训练和测试时间。

抽象来看,您从一个具有许多特征的数据集出发,通过从原始特征中选择少数几个主成分来简化该数据集。

什么是主成分?

主成分是 PCA 的关键;它们代表了数据“引擎盖下”的结构。通俗地说,当数据从高维空间投影到低维空间(假设为三维)时,这三个维度就是三个主成分,它们捕获(或承载)了数据中大部分的方差(信息)。

主成分同时具有方向和幅度。方向表示数据主要沿哪些主轴分布或在哪些方向上方差最大;幅度表示当数据投影到该轴上时,该主成分捕获的方差量。主成分是一条直线,第一主成分在数据中承载的方差最多。随后每个主成分都与前一个正交,且方差更小。如此,对于给定的 x 个相关变量在 y 个样本上的观测,您可以得到在同样 y 个样本上的 u 个不相关主成分。

之所以能从原始特征得到不相关的主成分,是因为相关特征会共同贡献于同一个主成分,从而将原始数据的特征压缩为不相关的主成分;每个主成分代表了一组不同的相关特征,并具有不同的变异程度。每个主成分都代表了从数据中捕获到的总变异的一个百分比。

在今天的教程中,我们将使用 PCA 来通过数据可视化获得洞见,并用 PCA 来加速机器学习算法。为完成这两个任务,您将使用两个著名数据集:Breast Cancer 和 CIFAR-10。前者是数值型数据集;后者是图像数据集。

理解数据

在继续加载数据之前,先了解并浏览一下您将要处理的数据会更好!

Breast Cancer

Breast Cancer 数据集是实值的多变量数据,包含两个类别,每个类别表示患者是否患有乳腺癌。两个类别分别为:恶性(malignant)和良性(benign)。

恶性类别有 212 个样本,良性类别有 357 个样本。

它包含 30 个在所有类别中共享的特征:半径、纹理、周长、面积、平滑度、分形维数等。

您可以从此处下载乳腺癌数据集,或者更简单的方式是借助 sklearn 库直接加载。

CIFAR - 10

CIFAR-10(加拿大高等研究院)数据集包含 60000 张 32x32x3 的彩色图像,共十个类别,每个类别有 6000 张图像。

该数据集由 50000 张训练图像和 10000 张测试图像组成。

数据集的类别包括:airplane、automobile、bird、cat、deer、dog、frog、horse、ship、truck。

您可以从此处下载 CIFAR 数据集,或者借助 Keras 等深度学习库按需加载。

使用 PCA 进行数据探索

现在您将加载并分析 Breast Cancer 和 CIFAR-10 数据集。到目前为止,您已经对这两个数据集的维度有了大致了解。

那么,让我们快速探索一下这两个数据集。

乳腺癌数据探索

先来探索一下 Breast Cancer 数据集。

您将使用 sklearn 的 datasets 模块并从中导入 Breast Cancer 数据集。

from sklearn.datasets import load_breast_cancer

load_breast_cancer 会同时返回标签和数据。获取数据请调用 .data,获取标签请调用 .target。

该数据包含 569 个样本,每个样本有 30 个特征,并且每个样本都有一个关联的标签。该数据集中共有两个标签。

breast = load_breast_cancer()
breast_data = breast.data

来看一下数据的形状。

breast_data.shape
(569, 30)

尽管在本教程中您并不需要标签,但为了更好地理解,我们还是加载一下标签并查看其形状。

breast_labels = breast.target
breast_labels.shape
(569,)

现在您将导入 numpy,因为需要对 breast_labels 进行重塑,以便与 breast_data 连接,最终创建包含数据与标签的 DataFrame。

import numpy as np
labels = np.reshape(breast_labels,(569,1))

在对标签进行重塑后,您将沿第二个轴连接数据与标签,这意味着最终数组的形状将为 569 x 31。

final_breast_data = np.concatenate([breast_data,labels],axis=1)
final_breast_data.shape
(569, 31)

现在您将导入 pandas,创建最终数据的 DataFrame,以表格形式展示数据。

import pandas as pd
breast_dataset = pd.DataFrame(final_breast_data)

快速打印一下乳腺癌数据集中的特征!

features = breast.feature_names
features
array(['mean radius', 'mean texture', 'mean perimeter', 'mean area',
       'mean smoothness', 'mean compactness', 'mean concavity',
       'mean concave points', 'mean symmetry', 'mean fractal dimension',
       'radius error', 'texture error', 'perimeter error', 'area error',
       'smoothness error', 'compactness error', 'concavity error',
       'concave points error', 'symmetry error',
       'fractal dimension error', 'worst radius', 'worst texture',
       'worst perimeter', 'worst area', 'worst smoothness',
       'worst compactness', 'worst concavity', 'worst concave points',
       'worst symmetry', 'worst fractal dimension'], dtype='<U23')

注意在 features 数组中缺少 label 字段。因此,您需要手动将其添加到 features 数组中,因为稍后会将该数组用作 breast_dataset 数据框的列名。

features_labels = np.append(features,'label')

很好!现在将列名嵌入到 breast_dataset 数据框中。

breast_dataset.columns = features_labels

打印数据框的前几行。

breast_dataset.head()
mean radius mean texture mean perimeter mean area mean smoothness mean compactness mean concavity mean concave points mean symmetry mean fractal dimension ... worst texture worst perimeter worst area worst smoothness worst compactness worst concavity worst concave points worst symmetry worst fractal dimension label
0 17.99 10.38 122.80 1001.0 0.11840 0.27760 0.3001 0.14710 0.2419 0.07871 ... 17.33 184.60 2019.0 0.1622 0.6656 0.7119 0.2654 0.4601 0.11890 0.0
1 20.57 17.77 132.90 1326.0 0.08474 0.07864 0.0869 0.07017 0.1812 0.05667 ... 23.41 158.80 1956.0 0.1238 0.1866 0.2416 0.1860 0.2750 0.08902 0.0
2 19.69 21.25 130.00 1203.0 0.10960 0.15990 0.1974 0.12790 0.2069 0.05999 ... 25.53 152.50 1709.0 0.1444 0.4245 0.4504 0.2430 0.3613 0.08758 0.0
3 11.42 20.38 77.58 386.1 0.14250 0.28390 0.2414 0.10520 0.2597 0.09744 ... 26.50 98.87 567.7 0.2098 0.8663 0.6869 0.2575 0.6638 0.17300 0.0
4 20.29 14.34 135.10 1297.0 0.10030 0.13280 0.1980 0.10430 0.1809 0.05883 ... 16.67 152.20 1575.0 0.1374 0.2050 0.4000 0.1625 0.2364 0.07678 0.0

5 行 × 31 列

由于原始标签是 0,1 格式,您将使用 .replace 函数把标签改为 benign 和 malignant。使用 inplace=True 会直接修改 breast_dataset 数据框。

breast_dataset['label'].replace(0, 'Benign',inplace=True)
breast_dataset['label'].replace(1, 'Malignant',inplace=True)

打印 breast_dataset 的最后几行。

breast_dataset.tail()
  mean radius mean texture mean perimeter mean area mean smoothness mean compactness mean concavity mean concave points mean symmetry mean fractal dimension ... worst texture worst perimeter worst area worst smoothness worst compactness worst concavity worst concave points worst symmetry worst fractal dimension label
564 21.56 22.39 142.00 1479.0 0.11100 0.11590 0.24390 0.13890 0.1726 0.05623 ... 26.40 166.10 2027.0 0.14100 0.21130 0.4107 0.2216 0.2060 0.07115 Benign
565 20.13 28.25 131.20 1261.0 0.09780 0.10340 0.14400 0.09791 0.1752 0.05533 ... 38.25 155.00 1731.0 0.11660 0.19220 0.3215 0.1628 0.2572 0.06637 Benign
566 16.60 28.08 108.30 858.1 0.08455 0.10230 0.09251 0.05302 0.1590 0.05648 ... 34.12 126.70 1124.0 0.11390 0.30940 0.3403 0.1418 0.2218 0.07820 Benign
567 20.60 29.33 140.10 1265.0 0.11780 0.27700 0.35140 0.15200 0.2397 0.07016 ... 39.42 184.60 1821.0 0.16500 0.86810 0.9387 0.2650 0.4087 0.12400 Benign
568 7.76 24.54 47.92 181.0 0.05263 0.04362 0.00000 0.00000 0.1587 0.05884 ... 30.37 59.16 268.6 0.08996 0.06444 0.0000 0.0000 0.2871 0.07039 Malignant

5 行 × 31 列

CIFAR - 10 数据探索

接下来,探索一下 CIFAR - 10 图像数据集。

您可以使用深度学习库 Keras 来加载 CIFAR - 10 数据集。

from keras.datasets import cifar10

导入后,使用 .load_data() 方法下载数据,它会将数据下载并存储在您的 Keras 目录中。下载所需时间取决于您的网络速度。

(x_train, y_train), (x_test, y_test) = cifar10.load_data()

上述代码返回训练与测试图像及其对应的标签。

快速打印一下训练与测试图像的形状。

print('Traning data shape:', x_train.shape)
print('Testing data shape:', x_test.shape)
Traning data shape: (50000, 32, 32, 3)
Testing data shape: (10000, 32, 32, 3)

也打印一下标签的形状。

y_train.shape,y_test.shape
((50000, 1), (10000, 1))

再来看一下标签的总数以及数据包含的各类类别。

# Find the unique numbers from the train labels
classes = np.unique(y_train)
nClasses = len(classes)
print('Total number of outputs : ', nClasses)
print('Output classes : ', classes)
Total number of outputs :  10
Output classes :  [0 1 2 3 4 5 6 7 8 9]

现在为了绘制 CIFAR-10 图像,导入 matplotlib,并使用 magic (%) 命令 %matplotlib inline 告诉 Jupyter Notebook 在笔记本中直接显示输出!

import matplotlib.pyplot as plt
%matplotlib inline

为更好地理解,我们创建一个字典,将类别名称与对应的类别标签关联起来。

label_dict = {
 0: 'airplane',
 1: 'automobile',
 2: 'bird',
 3: 'cat',
 4: 'deer',
 5: 'dog',
 6: 'frog',
 7: 'horse',
 8: 'ship',
 9: 'truck',
}
plt.figure(figsize=[5,5])

# Display the first image in training data
plt.subplot(121)
curr_img = np.reshape(x_train[0], (32,32,3))
plt.imshow(curr_img)
print(plt.title("(Label: " + str(label_dict[y_train[0][0]]) + ")"))

# Display the first image in testing data
plt.subplot(122)
curr_img = np.reshape(x_test[0],(32,32,3))
plt.imshow(curr_img)
print(plt.title("(Label: " + str(label_dict[y_test[0][0]]) + ")"))
Text(0.5, 1.0, '(Label: frog)')
Text(0.5, 1.0, '(Label: cat)')
模糊的青蛙和猫

尽管上面的两张图像较为模糊,您仍能大致看出第一张是青蛙,标签为 frog,而第二张是猫,标签为 cat。

使用 PCA 进行数据可视化

现在到了本教程中最令人兴奋的部分。正如您之前学到的,PCA 会将高维数据投影为低维主成分,现在是时候借助Python来进行可视化了!

可视化乳腺癌数据

  • 首先标准化数据,因为 PCA 的输出会受数据特征尺度的影响。

    在将数据输入任何机器学习算法之前进行归一化是常见做法。

    要应用归一化,您将从 sklearn 库导入 StandardScaler 模块,并仅选择在“数据探索”步骤中创建的 breast_dataset 中的特征。得到特征后,通过对特征数据执行 fit_transform 来进行缩放。

    在应用 StandardScaler 时,数据的每个特征应近似服从正态分布,以便将分布缩放为均值为 0、标准差为 1。

from sklearn.preprocessing import StandardScaler
x = breast_dataset.loc[:, features].values
x = StandardScaler().fit_transform(x) # normalizing the features
x.shape
(569, 30)

检查一下归一化后的数据是否均值为 0、标准差为 1。

np.mean(x),np.std(x)
(-6.826538293184326e-17, 1.0)

将归一化的特征借助 DataFrame 转换为表格格式。

feat_cols = ['feature'+str(i) for i in range(x.shape[1])]
normalised_breast = pd.DataFrame(x,columns=feat_cols)
normalised_breast.tail()
  feature0 feature1 feature2 feature3 feature4 feature5 feature6 feature7 feature8 feature9 ... feature20 feature21 feature22 feature23 feature24 feature25 feature26 feature27 feature28 feature29
564 2.110995 0.721473 2.060786 2.343856 1.041842 0.219060 1.947285 2.320965 -0.312589 -0.931027 ... 1.901185 0.117700 1.752563 2.015301 0.378365 -0.273318 0.664512 1.629151 -1.360158 -0.709091
565 1.704854 2.085134 1.615931 1.723842 0.102458 -0.017833 0.693043 1.263669 -0.217664 -1.058611 ... 1.536720 2.047399 1.421940 1.494959 -0.691230 -0.394820 0.236573 0.733827 -0.531855 -0.973978
566 0.702284 2.045574 0.672676 0.577953 -0.840484 -0.038680 0.046588 0.105777 -0.809117 -0.895587 ... 0.561361 1.374854 0.579001 0.427906 -0.809587 0.350735 0.326767 0.414069 -1.104549 -0.318409
567 1.838341 2.336457 1.982524 1.735218 1.525767 3.272144 3.296944 2.658866 2.137194 1.043695 ... 1.961239 2.237926 2.303601 1.653171 1.430427 3.904848 3.197605 2.289985 1.919083 2.219635
568 -1.808401 1.221792 -1.814389 -1.347789 -3.112085 -1.150752 -1.114873 -1.261820 -0.820070 -0.561032 ... -1.410893 0.764190 -1.432735 -1.075813 -1.859019 -1.207552 -1.305831 -1.745063 -0.048138 -0.751207

5 行 × 30 列

  • 接下来是关键部分,下面几行代码将把 30 维的乳腺癌数据投影到二维的主成分上。

    您将使用 sklearn 导入 PCA 模块,并在 PCA 方法中传入组件数量(n_components=2),最后对聚合数据调用 fit_transform。这里,组件数量代表您要将高维数据投影到的低维空间的维度。

from sklearn.decomposition import PCA
pca_breast = PCA(n_components=2)
principalComponents_breast = pca_breast.fit_transform(x)

接着,创建一个 DataFrame,包含所有 569 个样本的主成分值。

principal_breast_Df = pd.DataFrame(data = principalComponents_breast
             , columns = ['principal component 1', 'principal component 2'])
principal_breast_Df.tail()
  principal component 1 principal component 2
564 6.439315 -3.576817
565 3.793382 -3.584048
566 1.256179 -1.902297
567 10.374794 1.672010
568 -5.475243 -0.670637
  • 得到主成分后,您可以查看explained_variance_ratio。它会告诉您在将数据投影到低维子空间后,每个主成分所承载的信息量或方差。
print('Explained variability per principal component: {}'.format(pca_breast.explained_variance_ratio_))
Explained variability per principal component: [0.44272026 0.18971182]

从以上输出可以看到,principal component 1 承载了 44.2% 的信息,而 principal component 2 仅承载了 19% 的信息。另一个需要注意的点是,当把 30 维数据投影到二维时,损失了 36.8% 的信息。

让我们沿 principal component - 1 与 principal component - 2 轴绘制 569 个样本的可视化。这将有助于了解两个类别之间样本的分布情况。

plt.figure()
plt.figure(figsize=(10,10))
plt.xticks(fontsize=12)
plt.yticks(fontsize=14)
plt.xlabel('Principal Component - 1',fontsize=20)
plt.ylabel('Principal Component - 2',fontsize=20)
plt.title("Principal Component Analysis of Breast Cancer Dataset",fontsize=20)
targets = ['Benign', 'Malignant']
colors = ['r', 'g']
for target, color in zip(targets,colors):
    indicesToKeep = breast_dataset['label'] == target
    plt.scatter(principal_breast_Df.loc[indicesToKeep, 'principal component 1']
               , principal_breast_Df.loc[indicesToKeep, 'principal component 2'], c = color, s = 50)

plt.legend(targets,prop={'size': 15})
<matplotlib.legend.Legend at 0x14552a630>




<Figure size 432x288 with 0 Axes>
乳腺癌数据集的主成分分析

从上图可以看到,当将 benign 和 malignant 两个类别投影到二维空间后,在一定程度上可以线性可分。另一个观察是,与 malignant 类相比,benign 类的分布更为分散。

可视化 CIFAR - 10 数据

用于可视化 CIFAR-10 数据的以下代码与乳腺癌数据的 PCA 可视化非常相似。

  • 我们先快速检查 CIFAR-10 训练图像的最大值和最小值,并将像素归一化到 0 到 1(含)之间。
np.min(x_train),np.max(x_train)
(0.0, 1.0)
x_train = x_train/255.0
np.min(x_train),np.max(x_train)
(0.0, 0.00392156862745098)
x_train.shape
(50000, 32, 32, 3)

接下来,您将创建一个 DataFrame,以行列格式保存图像的像素值及其相应的标签。

在此之前,先把图像维度从三维重塑为一维(展平图像)。

x_train_flat = x_train.reshape(-1,3072)
feat_cols = ['pixel'+str(i) for i in range(x_train_flat.shape[1])]
df_cifar = pd.DataFrame(x_train_flat,columns=feat_cols)
df_cifar['label'] = y_train
print('Size of the dataframe: {}'.format(df_cifar.shape))
Size of the dataframe: (50000, 3073)

很好!数据框的大小正确,因为有 50,000 张训练图像,每张有 3072 个像素,外加一列标签,总计 3073 列。

PCA 将应用于除最后一列(每张图像的标签)之外的所有列。

df_cifar.head()
  pixel0 pixel1 pixel2 pixel3 pixel4 pixel5 pixel6 pixel7 pixel8 pixel9 ... pixel3063 pixel3064 pixel3065 pixel3066 pixel3067 pixel3068 pixel3069 pixel3070 pixel3071 label
0 0.231373 0.243137 0.247059 0.168627 0.180392 0.176471 0.196078 0.188235 0.168627 0.266667 ... 0.847059 0.721569 0.549020 0.592157 0.462745 0.329412 0.482353 0.360784 0.282353 6
1 0.603922 0.694118 0.733333 0.494118 0.537255 0.533333 0.411765 0.407843 0.372549 0.400000 ... 0.560784 0.521569 0.545098 0.560784 0.525490 0.556863 0.560784 0.521569 0.564706 9
2 1.000000 1.000000 1.000000 0.992157 0.992157 0.992157 0.992157 0.992157 0.992157 0.992157 ... 0.305882 0.333333 0.325490 0.309804 0.333333 0.325490 0.313725 0.337255 0.329412 9
3 0.109804 0.098039 0.039216 0.145098 0.133333 0.074510 0.149020 0.137255 0.078431 0.164706 ... 0.211765 0.184314 0.109804 0.247059 0.219608 0.145098 0.282353 0.254902 0.180392 4
4 0.666667 0.705882 0.776471 0.658824 0.698039 0.768627 0.694118 0.725490 0.796078 0.717647 ... 0.294118 0.309804 0.321569 0.278431 0.294118 0.305882 0.286275 0.301961 0.313725 1

5 行 × 3073 列

  • 接下来,创建 PCA 实例并将组件数量设为 2,对训练数据应用 fit_transform。由于有 50,000 个样本,这一步可能需要几秒钟。
pca_cifar = PCA(n_components=2)
principalComponents_cifar = pca_cifar.fit_transform(df_cifar.iloc[:,:-1])

然后将每张图像(共 50,000 张)的主成分从 numpy 数组转换为 pandas DataFrame。

principal_cifar_Df = pd.DataFrame(data = principalComponents_cifar
             , columns = ['principal component 1', 'principal component 2'])
principal_cifar_Df['y'] = y_train
principal_cifar_Df.head()
  principal component 1 principal component 2 y
0 -6.401018 2.729039 6
1 0.829783 -0.949943 9
2 7.730200 -11.522102 9
3 -10.347817 0.010738 4
4 -2.625651 -4.969240 1
  • 快速查看一下主成分所承载的信息量或方差。
print('Explained variability per principal component: {}'.format(pca_cifar.explained_variance_ratio_))
Explained variability per principal component: [0.2907663  0.11253144]

不错,考虑到数据从 3072 维被投影到只有两个主成分,主成分 1 和 2 仍保留了相当可观的信息量。

现在是时候在二维空间中可视化 CIFAR-10 数据了。请记住,该数据集中存在一定的语义类别重叠,这意味着青蛙与猫或鹿与狗可能在形状上略有相似;尤其是在二维空间投影时,它们之间的差异可能无法很好地被捕获。

import seaborn as sns
plt.figure(figsize=(16,10))
sns.scatterplot(
    x="principal component 1", y="principal component 2",
    hue="y",
    palette=sns.color_palette("hls", 10),
    data=principal_cifar_Df,
    legend="full",
    alpha=0.3
)
<matplotlib.axes._subplots.AxesSubplot at 0x12a5ba8d0>
主成分分析的变异

从上图可以看出,主成分确实捕获到了一些变异,因为当沿两个主成分轴投影时,点分布呈现出一定结构。属于同一类别的点彼此更接近,而语义上差异较大的点(或图像)彼此距离更远。

使用 PCA 加速深度学习训练

在本教程的最后一部分,您将学习如何使用 PCA 来加速深度学习模型的训练过程。

说明:要了解本节将使用的一些基础术语,欢迎查看这篇教程。

首先,让我们对训练与测试图像进行归一化。如果您还记得,训练图像已在 PCA 可视化部分完成归一化,因此这里只需对测试图像归一化。我们来快速完成!

x_test = x_test/255.0
x_test = x_test.reshape(-1,32,32,3)

对测试数据进行重塑。

x_test_flat = x_test.reshape(-1,3072)

接下来,实例化 PCA 模型。

此处,您也可以指定希望 PCA 捕获的方差量。我们将 0.9 作为参数传给 PCA 模型,这表示 PCA 将保留 90% 的方差,并使用达到 90% 方差所需的组件数量。

注意,此前您传入的是 n_components 参数,然后再去查看这两个组件捕获了多少方差。而这里我们明确指定了希望 PCA 捕获的方差量,因此 n_components 将根据该方差参数而变化。

如果不传入任何方差参数,则组件数量将等于数据的原始维度。

pca = PCA(0.9)

随后您会在训练图像上拟合 PCA 实例。

pca.fit(x_train_flat)
PCA(copy=True, iterated_power='auto', n_components=0.9, random_state=None,
  svd_solver='auto', tol=0.0, whiten=False)

现在来看一下,PCA 为了捕获 0.9 的方差使用了多少个 n_components。

pca.n_components_
99

从以上输出可以看到,为了达到 90% 的方差,维度从原始的 3072 降至 99 个主成分。

最后,您将在训练集与测试集上分别应用 transform,利用 fit 方法得到的参数生成转换后的数据集。

train_img_pca = pca.transform(x_train_flat)
test_img_pca = pca.transform(x_test_flat)

接着,快速导入运行深度学习模型所需的库。

from keras.models import Sequential
from keras.layers import Dense
from keras.utils import np_utils
from keras.optimizers import RMSprop

现在,将训练与测试标签转换为 one-hot 编码向量。

y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)

定义模型的 epoch 数、类别数与批大小。

batch_size = 128
num_classes = 10
epochs = 20

接下来,定义您的 Sequential 模型!

model = Sequential()
model.add(Dense(1024, activation='relu', input_shape=(99,)))
model.add(Dense(1024, activation='relu'))
model.add(Dense(512, activation='relu'))
model.add(Dense(256, activation='relu'))
model.add(Dense(num_classes, activation='softmax'))

打印模型摘要。

model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 1024)              102400    
_________________________________________________________________
dense_2 (Dense)              (None, 1024)              1049600   
_________________________________________________________________
dense_3 (Dense)              (None, 512)               524800    
_________________________________________________________________
dense_4 (Dense)              (None, 256)               131328    
_________________________________________________________________
dense_5 (Dense)              (None, 10)                2570      
=================================================================
Total params: 1,810,698
Trainable params: 1,810,698
Non-trainable params: 0
_________________________________________________________________

最后,开始编译并训练模型!

model.compile(loss='categorical_crossentropy',
              optimizer=RMSprop(),
              metrics=['accuracy'])

history = model.fit(train_img_pca, y_train,batch_size=batch_size,epochs=epochs,verbose=1,
                    validation_data=(test_img_pca, y_test))
WARNING:tensorflow:From /Users/adityasharma/blog/lib/python3.7/site-packages/keras/backend/tensorflow_backend.py:2704: calling reduce_sum (from tensorflow.python.ops.math_ops) with keep_dims is deprecated and will be removed in a future version.
Instructions for updating:
keep_dims is deprecated, use keepdims instead
WARNING:tensorflow:From /Users/adityasharma/blog/lib/python3.7/site-packages/keras/backend/tensorflow_backend.py:1257: calling reduce_mean (from tensorflow.python.ops.math_ops) with keep_dims is deprecated and will be removed in a future version.
Instructions for updating:
keep_dims is deprecated, use keepdims instead
Train on 50000 samples, validate on 10000 samples
Epoch 1/20
50000/50000 [==============================] - 7s - loss: 1.9032 - acc: 0.2962 - val_loss: 1.6925 - val_acc: 0.3875
Epoch 2/20
50000/50000 [==============================] - 7s - loss: 1.6480 - acc: 0.4055 - val_loss: 1.5313 - val_acc: 0.4412
Epoch 3/20
50000/50000 [==============================] - 7s - loss: 1.5205 - acc: 0.4534 - val_loss: 1.4609 - val_acc: 0.4695
Epoch 4/20
50000/50000 [==============================] - 7s - loss: 1.4322 - acc: 0.4849 - val_loss: 1.6164 - val_acc: 0.4503
Epoch 5/20
50000/50000 [==============================] - 7s - loss: 1.3621 - acc: 0.5120 - val_loss: 1.3626 - val_acc: 0.5081
Epoch 6/20
50000/50000 [==============================] - 7s - loss: 1.2995 - acc: 0.5330 - val_loss: 1.4100 - val_acc: 0.4940
Epoch 7/20
50000/50000 [==============================] - 7s - loss: 1.2473 - acc: 0.5529 - val_loss: 1.3589 - val_acc: 0.5251
Epoch 8/20
50000/50000 [==============================] - 7s - loss: 1.2010 - acc: 0.5669 - val_loss: 1.3315 - val_acc: 0.5232
Epoch 9/20
50000/50000 [==============================] - 7s - loss: 1.1524 - acc: 0.5868 - val_loss: 1.3903 - val_acc: 0.5197
Epoch 10/20
50000/50000 [==============================] - 7s - loss: 1.1134 - acc: 0.6013 - val_loss: 1.2722 - val_acc: 0.5499
Epoch 11/20
50000/50000 [==============================] - 7s - loss: 1.0691 - acc: 0.6160 - val_loss: 1.5911 - val_acc: 0.4768
Epoch 12/20
50000/50000 [==============================] - 7s - loss: 1.0325 - acc: 0.6289 - val_loss: 1.2515 - val_acc: 0.5602
Epoch 13/20
50000/50000 [==============================] - 7s - loss: 0.9977 - acc: 0.6420 - val_loss: 1.5678 - val_acc: 0.4914
Epoch 14/20
50000/50000 [==============================] - 8s - loss: 0.9567 - acc: 0.6567 - val_loss: 1.3525 - val_acc: 0.5418
Epoch 15/20
50000/50000 [==============================] - 9s - loss: 0.9158 - acc: 0.6713 - val_loss: 1.3525 - val_acc: 0.5540
Epoch 16/20
50000/50000 [==============================] - 10s - loss: 0.8948 - acc: 0.6816 - val_loss: 1.5633 - val_acc: 0.5156
Epoch 17/20
50000/50000 [==============================] - 9s - loss: 0.8690 - acc: 0.6903 - val_loss: 1.6980 - val_acc: 0.5084
Epoch 18/20
50000/50000 [==============================] - 9s - loss: 0.8586 - acc: 0.7002 - val_loss: 1.6325 - val_acc: 0.5247
Epoch 19/20
50000/50000 [==============================] - 8s - loss: 0.9367 - acc: 0.6853 - val_loss: 1.8253 - val_acc: 0.5165
Epoch 20/20
50000/50000 [==============================] - 8s - loss: 2.3761 - acc: 0.5971 - val_loss: 6.0192 - val_acc: 0.4409

从以上输出可以看到,每个 epoch 的训练时间在 CPU 上仅为 7 秒。该模型在训练数据上表现尚可,达到了 70% 的准确率,而在测试数据上仅达到 56% 的准确率。这意味着它对训练数据发生了过拟合。不过请记住,数据已从 3072 维投影到 99 维,尽管如此它仍取得了不错的表现!

最后,我们来看一下在原始数据集上训练同样的深度学习模型所需的时间,以及它能达到的准确率。

model = Sequential()
model.add(Dense(1024, activation='relu', input_shape=(3072,)))
model.add(Dense(1024, activation='relu'))
model.add(Dense(512, activation='relu'))
model.add(Dense(256, activation='relu'))
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss='categorical_crossentropy',
              optimizer=RMSprop(),
              metrics=['accuracy'])

history = model.fit(x_train_flat, y_train,batch_size=batch_size,epochs=epochs,verbose=1,
                    validation_data=(x_test_flat, y_test))
Train on 50000 samples, validate on 10000 samples
Epoch 1/20
50000/50000 [==============================] - 23s - loss: 2.0657 - acc: 0.2200 - val_loss: 2.0277 - val_acc: 0.2485
Epoch 2/20
50000/50000 [==============================] - 22s - loss: 1.8727 - acc: 0.3166 - val_loss: 1.8428 - val_acc: 0.3215
Epoch 3/20
50000/50000 [==============================] - 22s - loss: 1.7801 - acc: 0.3526 - val_loss: 1.7657 - val_acc: 0.3605
Epoch 4/20
50000/50000 [==============================] - 22s - loss: 1.7141 - acc: 0.3796 - val_loss: 1.6345 - val_acc: 0.4132
Epoch 5/20
50000/50000 [==============================] - 22s - loss: 1.6566 - acc: 0.4001 - val_loss: 1.6384 - val_acc: 0.4076
Epoch 6/20
50000/50000 [==============================] - 22s - loss: 1.6083 - acc: 0.4209 - val_loss: 1.7507 - val_acc: 0.3574
Epoch 7/20
50000/50000 [==============================] - 22s - loss: 1.5626 - acc: 0.4374 - val_loss: 1.7125 - val_acc: 0.4010
Epoch 8/20
50000/50000 [==============================] - 22s - loss: 1.5252 - acc: 0.4486 - val_loss: 1.5914 - val_acc: 0.4321
Epoch 9/20
50000/50000 [==============================] - 24s - loss: 1.4924 - acc: 0.4620 - val_loss: 1.5352 - val_acc: 0.4616
Epoch 10/20
50000/50000 [==============================] - 25s - loss: 1.4627 - acc: 0.4728 - val_loss: 1.4561 - val_acc: 0.4798
Epoch 11/20
50000/50000 [==============================] - 24s - loss: 1.4349 - acc: 0.4820 - val_loss: 1.5044 - val_acc: 0.4723
Epoch 12/20
50000/50000 [==============================] - 24s - loss: 1.4120 - acc: 0.4919 - val_loss: 1.4740 - val_acc: 0.4790
Epoch 13/20
50000/50000 [==============================] - 23s - loss: 1.3913 - acc: 0.4981 - val_loss: 1.4430 - val_acc: 0.4891
Epoch 14/20
50000/50000 [==============================] - 27s - loss: 1.3678 - acc: 0.5098 - val_loss: 1.4323 - val_acc: 0.4888
Epoch 15/20
50000/50000 [==============================] - 27s - loss: 1.3508 - acc: 0.5148 - val_loss: 1.6179 - val_acc: 0.4372
Epoch 16/20
50000/50000 [==============================] - 25s - loss: 1.3443 - acc: 0.5167 - val_loss: 1.5868 - val_acc: 0.4656
Epoch 17/20
50000/50000 [==============================] - 25s - loss: 1.3734 - acc: 0.5101 - val_loss: 1.4756 - val_acc: 0.4913
Epoch 18/20
50000/50000 [==============================] - 26s - loss: 5.5126 - acc: 0.3591 - val_loss: 5.7580 - val_acc: 0.3084
Epoch 19/20
50000/50000 [==============================] - 27s - loss: 5.6346 - acc: 0.3395 - val_loss: 3.7362 - val_acc: 0.3402
Epoch 20/20
50000/50000 [==============================] - 26s - loss: 6.4199 - acc: 0.3030 - val_loss: 13.9429 - val_acc: 0.1326

瞧!从以上输出可以清楚地看到,在 CPU 上每个 epoch 的训练时间约为 23 秒,几乎是基于 PCA 输出训练的模型的三倍。

此外,无论是训练准确率还是测试准确率,都低于以 99 个主成分作为输入所得到的准确率。

因此,通过在训练数据上应用 PCA,您不仅可以更快地训练深度学习算法,而且与使用原始训练数据训练的模型相比,它还在测试数据上获得了更好的准确率。

继续深入!

恭喜您完成了本教程。

本教程对 Python 中的 PCA 做了精彩而全面的介绍,兼顾了理论与实践。

如果您想更深入地了解降维技术,可以阅读关于 t-distributed Stochastic Neighbor Embedding(常称为 tSNE)的资料,这是一种非线性的概率降维技术。

如果您想进一步学习像 PCA 这样的无监督学习技术,欢迎参加 DataCamp 的 Unsupervised Learning in Python 课程。

延伸阅读:

常见问题解答

因子分析与主成分分析有什么区别?

因子分析(FA)与主成分分析(PCA)都是用于降维的技术,但目标不同。PCA 通过将数据转换为一组新的不相关变量(按解释方差大小排序的主成分)来保留数据的总体变异性。相反,FA 旨在通过用少量潜在因子来建模数据,从而识别观测变量之间的潜在关系,这些潜在因子解释了变量之间的相关性。

什么是主成分分析?

主成分分析是寻找数据协方差矩阵的特征向量,将其投影到由主成分(具有最大特征值者)定义的低维空间的过程。

主题
Python
数据可视化
机器学习

进一步了解 Python

课程

Python 入门

4 小时
7M
只需 4 小时就能掌握 Python 数据分析基础知识。 本在线课程将介绍 Python 接口并探索常用包。
查看详情Right Arrow
开始课程
查看更多Right Arrow