在这一节,我们就利用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,效果显然不如使用原始数据好,因为在降维过程中丢失了大量的细节特征。
通过可视化展示主成分轴个数的选择对原始数据信息保留度的影响
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的强大性,它给了我们一个通过少量模型精度的牺牲来减少大量模型训练时长
将原数据降维至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()可以看到在二维空间中,数字之间的区分度还是比较明显的
首先导入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处理
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生成特征脸的步骤。
首先需要导入人脸数据集
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
