机器学习系列笔记六:主成分分析PCA[下]

    技术2026-01-30  6

    机器学习系列笔记六:主成分分析PCA[下]

    文章目录

    机器学习系列笔记六:主成分分析PCA[下]scikit-learn中的PCA基本使用进阶操作对比实验设置合理的n_components通过PCA完成对数据的可视化测试MNIST数据集 使用PCA对数据进行降噪处理实验引入使用案例 PCA生成特征脸 总结参考 在上一节,我们自定义实现了PCA主成分分析法,并通过多个测试验证了算法的有效性,当然与scikit-learn或其他机器学习框架封装的PCA算法相比差的很远,但也足以让我们理解PCA的原理。

    在这一节,我们就利用scikit-learn框架中所提供的PCA来进行一系列的练习。学习利用框架所提供的工具来是实现主成分分析,并能用于实际的需求。

    scikit-learn中的PCA

    基本使用

    准备数据

    import numpy as np import matplotlib.pyplot as plt X = np.empty((100,2)) X[:,0] = np.random.uniform(0,100,size=100) X[:,1] = 0.75 * X[:,0] + 3 + np.random.normal(0,10,size=100)

    首先需要从sklearn.decomposition包中导入PCA工具类

    from sklearn.decomposition import PCA

    其实上一节中PCA的实现是参照着scikit-learn来的,所以使用起来和之前的代码差别不大

    pca = PCA(n_components=1) pca.fit(X) PCA(copy=True, iterated_power='auto', n_components=1, random_state=None, svd_solver='auto', tol=0.0, whiten=False) pca.components_ #求解的n_components个主成分 array([[-0.78373823, -0.62109128]])

    transform:根据求解的主成分对数据进行降维

    X_reduction = pca.transform(X) X_reduction.shape (100, 1)

    inverse_transform:根据求解的主成分将降维后的数据恢复为高维状态

    X_restore = pca.inverse_transform(X_reduction) X_restore.shape (100, 2)

    对比恢复后的数据与原始数据

    plt.scatter(X[:,0],X[:,1],color="b",alpha=0.5) plt.scatter(X_restore[:,0],X_restore[:,1],color="r",alpha=0.5) plt.show()

    进阶操作

    对比实验

    导入真实数据集

    import numpy as np import matplotlib.pyplot as plt from sklearn import datasets # 导入手写数据集 digits = datasets.load_digits() X = digits.data y = digits.target

    分割数据集为测试/训练组

    from sklearn.model_selection import train_test_split X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=666) X_train.shape (1347, 64) 对原始数据进行训练(KNN算法),看看识别率为多少 %%time from sklearn.neighbors import KNeighborsClassifier knn_clf = KNeighborsClassifier() knn_clf.fit(X_train,y_train) Wall time: 32.7 ms KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski', metric_params=None, n_jobs=None, n_neighbors=5, p=2, weights='uniform') knn_clf.score(X_test,y_test) 0.9866666666666667

    可以看到对原始数据不作PCA处理,所耗费的训练时间为17.9 ms,模型识别精度为0.9866666666666667

    对原始数据进行PCA降维处理,看看识别率为多少

    通过PCA.transform()将原始数据降维到2维

    from sklearn.decomposition import PCA pca = PCA(n_components=2) pca.fit(X_train) X_train_reduction = pca.transform(X_train) X_test_reduction = pca.transform(X_test)

    使用同样的KNN算法训练处理后的数据

    %%time knn_clf = KNeighborsClassifier() knn_clf.fit(X_train_reduction,y_train) Wall time: 1.49 ms KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski', metric_params=None, n_jobs=None, n_neighbors=5, p=2, weights='uniform') knn_clf.score(X_test_reduction,y_test) 0.6066666666666667

    可以看到对于通过PCA处理后的数据,KNN模型训练所耗费的训练时间为1.49 ms,将近比利用原始数据训练快了15倍!!!

    然而模型识别精度为0.6066666666666667,效果显然不如使用原始数据好,因为在降维过程中丢失了大量的细节特征。

    设置合理的n_components

    通过PCA.explained_variance_ratio_可以查看计算出的主成分轴分别解释原数据的百分比(保留的信息量) pca.explained_variance_ratio_ array([0.14566817, 0.13735469]) pca = PCA(n_components=X_train.shape[1]) pca.fit(X_train) pca.explained_variance_ratio_ array([1.45668166e-01, 1.37354688e-01, 1.17777287e-01, 8.49968861e-02, 5.86018996e-02, 5.11542945e-02, 4.26605279e-02, 3.60119663e-02, 3.41105814e-02, 3.05407804e-02, 2.42337671e-02, 2.28700570e-02, 1.80304649e-02, 1.79346003e-02, 1.45798298e-02, 1.42044841e-02, 1.29961033e-02, 1.26617002e-02, 1.01728635e-02, 9.09314698e-03, 8.85220461e-03, 7.73828332e-03, 7.60516219e-03, 7.11864860e-03, 6.85977267e-03, 5.76411920e-03, 5.71688020e-03, 5.08255707e-03, 4.89020776e-03, 4.34888085e-03, 3.72917505e-03, 3.57755036e-03, 3.26989470e-03, 3.14917937e-03, 3.09269839e-03, 2.87619649e-03, 2.50362666e-03, 2.25417403e-03, 2.20030857e-03, 1.98028746e-03, 1.88195578e-03, 1.52769283e-03, 1.42823692e-03, 1.38003340e-03, 1.17572392e-03, 1.07377463e-03, 9.55152460e-04, 9.00017642e-04, 5.79162563e-04, 3.82793717e-04, 2.38328586e-04, 8.40132221e-05, 5.60545588e-05, 5.48538930e-05, 1.08077650e-05, 4.01354717e-06, 1.23186515e-06, 1.05783059e-06, 6.06659094e-07, 5.86686040e-07, 1.71368535e-33, 7.44075955e-34, 7.44075955e-34, 7.15189459e-34])

    通过可视化展示主成分轴个数的选择对原始数据信息保留度的影响

    plt.plot([i for i in range(X_train.shape[1])], [np.sum(pca.explained_variance_ratio_[:i+1]) for i in range(X_train.shape[1])]) plt.ylabel('Original data information retention') plt.xlabel('the number of principle components be choosen') plt.show()

    如果我们不知道要将数据降到多少维,但是又需要降维后模型预测率至少要95%,可以通过在PCA构造方法中传入n_components=0.95来解决

    pca = PCA(0.95) pca.fit(X_train) PCA(copy=True, iterated_power='auto', n_components=0.95, random_state=None, svd_solver='auto', tol=0.0, whiten=False) pca.n_components_ 28

    这个结果说明对于原来的64维数据来说

    我们只需使用28维的数据就能解释原本数据95%以上的方差(信息)

    # 对原始数据降维64-->28 X_train_reduction = pca.transform(X_train) X_test_reduction = pca.transform(X_test) X_train_reduction.shape (1347, 28) %%time knn_clf = KNeighborsClassifier() knn_clf.fit(X_train_reduction,y_train) Wall time: 6.94 ms KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski', metric_params=None, n_jobs=None, n_neighbors=5, p=2, weights='uniform') knn_clf.score(X_test_reduction,y_test) 0.98

    可以看到,与使用包含全部信息的64维度特征相比,使用降维后的数据进行训练的时间是6.94 ms,提高了一倍多的训练速度

    而模型精度却只损失了0.9866666666666667-0.98 = 0.006666667

    可以看出PCA的强大性,它给了我们一个通过少量模型精度的牺牲来减少大量模型训练时长

    通过PCA完成对数据的可视化

    pca = PCA(n_components=2) pca.fit(X) X_reduction = pca.transform(X) X_reduction.shape (1797, 2)

    将原数据降维至2维后,我们就可以很方便地对其进行绘制了

    legend = [] for i in range(10): plt.scatter(X_reduction[y==i,0],X_reduction[y==i,1],alpha=0.8) legend.append(i) plt.legend(legend) plt.show()

    可以看到在二维空间中,数字之间的区分度还是比较明显的

    测试MNIST数据集

    首先导入fetch_mldata

    import numpy as np from sklearn.datasets import fetch_mldata

    通过fetch_mldata方法下载所需的数据集

    这里我发现无法下载成功,决定才用手动下载数据集并放在sciki_learn的对应目录中C:\Users\ASUS\scikit_learn_data

    链接:https://pan.baidu.com/s/13GA3FWzVi5mgoU0fqkmbNg 提取码:13h8

    # mnist = fetch_mldata("MNIST original") from sklearn.datasets.base import get_data_home print(get_data_home()) C:\Users\ASUS\scikit_learn_data mnist = fetch_mldata("MNIST original") mnist {'DESCR': 'mldata.org dataset: mnist-original', 'COL_NAMES': ['label', 'data'], 'target': array([0., 0., 0., ..., 9., 9., 9.]), 'data': array([[0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], ..., [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0]], dtype=uint8)} X,y = mnist['data'],mnist['target'] X.shape (70000, 784)

    MNIST数据集是帮我们把训练集和测试集分离好了的,所以可以直接调用

    X_train = np.array(X[:60000],dtype=float) y_train = np.array(y[:60000],dtype=float) X_test = np.array(X[60000:],dtype=float) y_test = np.array(y[60000:],dtype=float) X_train.shape (60000, 784) y_train.shape (60000,) X_test.shape (10000, 784) y_test.shape (10000,)

    使用KNN来对MNIST原始数据集进行识别

    from sklearn.neighbors import KNeighborsClassifier knn_clf = KNeighborsClassifier(n_jobs=-1) %time knn_clf.fit(X_train,y_train) Wall time: 15.6 s KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski', metric_params=None, n_jobs=-1, n_neighbors=5, p=2, weights='uniform') %time knn_clf.score(X_test,y_test) Wall time: 2min 23s 0.9688

    可以看到,在数据量比较大的时候,模型的预测花费了大量的时间,训练时间相对来说比较少是因为其内部做了大量的优化,比如对红黑树的转化等操作

    使用PCA进行降维

    from sklearn.decomposition import PCA pca = PCA(0.9) pca.fit(X_train) X_train_reduction = pca.transform(X_train) X_train_reduction.shape (60000, 87)

    可以看到,在保证样本信息保真度在0.9以上的前提下,通过PCA可以把样本的维度从784下降到87

    knn_clf2 = KNeighborsClassifier(n_jobs=-1) %time knn_clf2.fit(X_train_reduction,y_train) Wall time: 365 ms KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski', metric_params=None, n_jobs=-1, n_neighbors=5, p=2, weights='uniform') X_test_reduction = pca.transform(X_test) %time knn_clf2.score(X_test_reduction,y_test) Wall time: 17.3 s 0.9728

    使用PCA对数据进行降噪处理

    实验引入

    在前面的例子中,对自定义的数据进行了PCA处理

    import numpy as np import matplotlib.pyplot as plt X = np.empty((100, 2)) X[:,0] = np.random.uniform(0., 100., size=100) X[:,1] = 0.75 * X[:,0] + 3. + np.random.normal(0, 5, size=100)

    在这里定义的y=X[:,1],其中np.random.normal(0, 5, size=100)是我们人为添加的噪声

    我们不妨先看看如果没有噪声项的可视化结果

    X2 = np.empty((100, 2)) X2[:,0] = np.random.uniform(0., 100., size=100) X2[:,1] = 0.75 * X2[:,0] + 3. # 没有噪声 plt.scatter(X2[:,0],X2[:,1]) plt.title("no noise") plt.show()

    然后我们再看看加入了噪声后的可视化结果

    plt.scatter(X[:,0],X[:,1]) plt.title("with noise") plt.show()

    最后,我们将X(带噪声数据)进行PCA处理(进行两次变换),然后与X2(无噪声数据)进行对比

    from sklearn.decomposition import PCA pca = PCA(n_components=1) pca.fit(X) X_reduction = pca.transform(X) # 正变换 X_restore = pca.inverse_transform(X_reduction) # 逆变换 plt.scatter(X_restore[:,0],X_restore[:,1],color='r',alpha=0.8) plt.scatter(X2[:,0],X2[:,1],color='b',alpha=0.5) plt.scatter(X[:,0],X[:,1],color='r',alpha = 0.2) plt.legend(['data after PCA preocess ','data no noise','data with noise']) plt.show()

    可以看到通过PCA降维处理,然后再恢复可以将大量的噪声(不重要的特征)去除。

    所以有时候通过PCA降维后处理的数据来训练模型,预测结果:

    不仅时间上更快而且预测精度更高

    使用案例

    我们可以通过对手写数字进行PCA降噪处理来理解PCA降噪的强大

    首先导入手写数据集

    from sklearn import datasets digits = datasets.load_digits() X = digits.data y = digits.target

    为原始数据加入噪声

    noisy_digits = X + np.random.normal(0,4,size=X.shape)

    取出表示0~9的数据作为本次案例的样例

    example_digits = noisy_digits[y==0,:][:10] for num in range(1,10): example_digits = np.vstack([example_digits, noisy_digits[y==num,:][:10]]) example_digits.shape (100, 64)

    可视化加入噪声后的手写数字

    def plot_digits(data): fig, axes = plt.subplots(10, 10, figsize=(10, 10), subplot_kw={'xticks':[], 'yticks':[]}, gridspec_kw=dict(hspace=0.1, wspace=0.1)) for i, ax in enumerate(axes.flat): ax.imshow(data[i].reshape(8, 8), cmap='binary', interpolation='nearest', clim=(0, 16)) plt.show() plot_digits(example_digits)

    使用PCA对加入噪声后的数据example_digits进行降维去噪

    pca = PCA(0.5).fit(noisy_digits) pca.n_components_ 12 components = pca.transform(example_digits) filtered_digit = pca.inverse_transform(components) plot_digits(filtered_digit)

    可以看到,本次可视化的结果相比较上面的结果是要清晰很多的。

    PCA生成特征脸

    在上一节介绍PCA的时候,提到了PCA在图像处理方面有很大的应用面,其中比较典型的一个应用就是人脸识别,由于图片的维度非常大,一般都是上千个维度,所以在进行人脸识别模型训练之前一定要使用PCA进行降维处理,否则会造成人脸识别的低效。

    在这里就代码的形式给出PCA生成特征脸的步骤。

    首先需要导入人脸数据集

    import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import fetch_lfw_people faces = fetch_lfw_people()

    观察数据集

    faces.keys() dict_keys(['data', 'images', 'target', 'target_names', 'DESCR']) faces.target_names array(['AJ Cook', 'AJ Lamas', 'Aaron Eckhart', ..., 'Zumrati Juma', 'Zurab Tsereteli', 'Zydrunas Ilgauskas'], dtype='<U35') faces.images.shape (13233, 62, 47) faces.data.shape (13233, 2914)

    随机生成样本

    random_indexes = np.random.permutation(len(faces.data)) X = faces.data[random_indexes] example_faces = X[:36,:] example_faces.shape (36, 2914)

    可以看到我们只是随机去了36个样本,每个样本有2914个特征,这如果不进行降维处理,计算机要骂娘了

    样本可视化

    def plot_faces(faces): fig, axes = plt.subplots( 6, 6, figsize=(10, 10), subplot_kw={'xticks': [], 'yticks': []}, gridspec_kw=dict(hspace=0.1, wspace=0.1) ) for i, ax in enumerate(axes.flat): ax.imshow(faces[i].reshape(62, 47), cmap='bone') plt.show() plot_faces(example_faces)

    PCA处理,特征降维

    生成所有主成分

    %%time from sklearn.decomposition import PCA pca = PCA(svd_solver="randomized") # 没有指定n_components,默认生成n(样本特征数)个z=主成分 pca.fit(X) Wall time: 19.6 s PCA(copy=True, iterated_power='auto', n_components=None, random_state=None, svd_solver='randomized', tol=0.0, whiten=False) pca.components_.shape (2914, 2914)

    取出前36个主成分(等同于取出前36张脸)

    feature_face1 =pca.components_[:36,:] plot_faces(feature_face1)

    在该数据集中,有一些人的图片样本比较多而有一些人的图片样本比较少,为了获取拥有较多样本的人所对应的这些图片

    我们可以在调用fetch_lfw_people()的时候向里面传一个参数:

    min_faces_per_person=photo_num faces2 = fetch_lfw_people(min_faces_per_person=60) # 要求获取的数据中每个人至少拥有60张图片 faces2.data.shape (1348, 2914)

    可以看到只有1348张图片样本了

    faces2.target_names array(['Ariel Sharon', 'Colin Powell', 'Donald Rumsfeld', 'George W Bush', 'Gerhard Schroeder', 'Hugo Chavez', 'Junichiro Koizumi', 'Tony Blair'], dtype='<U17') len(faces2.target_names) 8

    可以看到只有8个人满足这样的条件

    使用KNN进行人脸识别

    X,y = faces['data'],faces['target'] train_index = int(0.8*len(X)) X_train = np.array(X[:train_index],dtype=float) y_train = np.array(y[:train_index],dtype=float) X_test = np.array(X[train_index:],dtype=float) y_test = np.array(y[train_index:],dtype=float) X_train.shape (10586, 2914) X_test.shape (2647, 2914) from sklearn.neighbors import KNeighborsClassifier knn_clf = KNeighborsClassifier(n_jobs=-1) from sklearn.decomposition import PCA pca = PCA(n_components=36) pca.fit(X_train) PCA(copy=True, iterated_power='auto', n_components=36, random_state=None, svd_solver='auto', tol=0.0, whiten=False) X_train_reduction = pca.transform(X_train) X_test_reduction = pca.transform(X_test) X_train_reduction.shape (10586, 36) X_test_reduction.shape (2647, 36) knn_clf.fit(X_train_reduction,y_train) KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski', metric_params=None, n_jobs=-1, n_neighbors=5, p=2, weights='uniform') knn_clf.score(X_test_reduction,y_test) 0.04722327162825841

    可以看到识别率是真的垃圾,但是不要灰心,因为在后面我们会专门利用卷积神经网络来处理图像的问题。

    总结

    参考

    liuyubobo:https://github.com/liuyubobobo/Play-with-Machine-Learning-Algorithms

    liuyubobo:https://coding.imooc.com/class/169.html

    莫烦Python:https://www.bilibili.com/video/BV1xW411Y7Qd?from=search&seid=11773783205368546023

    Processed: 0.024, SQL: 9