PCA是一种降维算法,可以用来对数据进行压缩,或者降维后进行可视化显示。
把n维数据降维k维数据时,就是要找出合适的k个向量,把n维数据投射到这k个向量决定的线性空间中,最终使投射误差最小化。
衡量PCA算法的指标是数据还原率(与失真度互补),这个指标由(1 - (平均投射误差的平方除以所有样本到原点的平均距离))得到。如果数据还原率为99%,我们就可以说99%的数据真实性被保存下来了。
一般要选择一个偏差目标,在满足偏差目标的情况下最小化K。
计算K的过程就是把K从1开始,不断累加,直到偏差符合我们的要求。
计算偏差的过程可以通过求奇异值分解的S矩阵来简化。
PCA算法常常用来给监督学习算法加速。
一种错误的PCA使用方法:直接用来降维后防止过拟合。
这种方法一般都有效,可是防止过拟合的更好方法是增加正则化项。
不建议一开始就用PCA降维数据再训练。应该先使用原始数据训练,在确信需要PCA的时候才使用PCA
下面使用sklearn演示二维数据降为一维,然后再恢复回来。
from sklearn.decomposition import PCA
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import MinMaxScaler
import numpy as np
# 禁用科学计数法显示
np.set_printoptions(suppress=True)
# 使用缩放类预处理数据,然后后使用pipeline连接起来
def std_PCA(**argv):
scaler = MinMaxScaler()
pca = PCA(**argv)
pipeline = Pipeline([('scaler', scaler),
('pca', pca)])
return pipeline
A = np.array([[3, 2000],
[2, 3000],
[4, 5000],
[5, 8000],
[1, 2000]], dtype='float')
print("raw data:\n{}".format(A))
# 训练,降维目标由n_components指定
pca = std_PCA(n_components=1)
pca.fit(A)
# 降维转换
compress = pca.transform(A)
print("compress:\n{}".format(compress))
score = pca.score(A)
print('fit score:{}'.format(score))
# 恢复数据
restore = pca.inverse_transform(compress)
print("restore:\n{}".format(restore))
这里的pca是一个Pipeline实例,其逆运算inverse_transform()是逐级进行的,即先进行PCA还原,再执行预处理的逆运算。
代码里的score按照文档里的解释是所有样本的对数似然函数平均值,具体计算参考:
See. "Pattern Recognition and Machine Learning"
by C. Bishop, 12.2.1 p. 574
or http://www.miketipping.com/papers/met-mppca.pdf
上述代码的输出为:
raw data:
[[ 3. 2000.]
[ 2. 3000.]
[ 4. 5000.]
[ 5. 8000.]
[ 1. 2000.]]
compress:
[[-0.2452941 ]
[-0.29192442]
[ 0.29192442]
[ 0.82914294]
[-0.58384884]]
fit score:-0.15297041662134564
restore:
[[ 2.33563616 2916.95452015]
[ 2.20934082 2711.06794139]
[ 3.79065918 5288.93205861]
[ 5.2456822 7660.90959707]
[ 1.41868164 1422.13588278]]
将剑桥人脸数据集的图片(分辨率64X64),经过PCA降到140维,然后使用SVM分类。数据集里有40个人,每人10张,共400张图片。
SVM的参数使用GridSearchCV搜索,找到最优参数后,对测试集进行预测,然后使用classification_report统计在测试集上的预测结果。
ps:我感觉这就是个玩具,人脸角度和光线变化肯定会前列影响分类结果,连割脸的位置都有很大影响。SVM并不能使用人脸的一些结构化信息,只能用来演示基本的算法调用方法。搞人脸还是要专门的算法。
import numpy as np
import time
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC
from sklearn.metrics import classification_report
from sklearn.datasets import fetch_olivetti_faces
face_home = './att_faces'
faces = fetch_olivetti_faces(data_home=face_home)
X = faces.data
y = faces.target
print('########## face-dataset info ###########')
targets = np.unique(faces.target)
target_names = np.array(['c%d'% t for t in targets])
print('targets:{},\ntarget_names:{}\n'.format(targets, target_names))
n_targets = target_names.shape[0]
n_samples, h, w = faces.images.shape
print('Sample count:{}\nTarget count:{}'.format(n_samples, n_targets))
print('Image size:{}X{}\nDataset shape:{}\n'.format(w, h, X.shape))
# -------------------------------------
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=4)
print('########### PCA ############')
n_components = 140
start = time.clock()
pca = PCA(n_components=n_components, svd_solver='randomized', whiten=True).fit(X_train)
print('PCA Done in {0:.2f}s'.format(time.clock() - start))
print('Projecting input data for PCA')
start = time.clock()
X_train_pca = pca.transform(X_train)
X_test_pca = pca.transform(X_test)
print('transform Done in {0:.2f}s\n'.format(time.clock() - start))
print('############# SVM ##############')
print('Searching the best parameters for SVC ...')
param_grid = {'C':[1, 5, 10, 50, 100], 'gamma':[0.0001, 0.0005, 0.001, 0.005, 0.01]}
clf = GridSearchCV(SVC(kernel='rbf', class_weight='balanced'), param_grid, verbose=0, n_jobs=1)
clf = clf.fit(X_train_pca, y_train)
print("Best param: {0}\nbest score: {1}\n".format(clf.best_params_,
clf.best_score_))
print('############# Predict #################')
y_pre = clf.best_estimator_.predict(X_test_pca)
print(classification_report(y_test, y_pre, target_names=target_names))
最终的输出为:
########## face-dataset info ###########
targets:[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39],
target_names:['c0' 'c1' 'c2' 'c3' 'c4' 'c5' 'c6' 'c7' 'c8' 'c9' 'c10' 'c11' 'c12' 'c13'
'c14' 'c15' 'c16' 'c17' 'c18' 'c19' 'c20' 'c21' 'c22' 'c23' 'c24' 'c25'
'c26' 'c27' 'c28' 'c29' 'c30' 'c31' 'c32' 'c33' 'c34' 'c35' 'c36' 'c37'
'c38' 'c39']
Sample count:400
Target count:40
Image size:64X64
Dataset shape:(400, 4096)
########### PCA ############
PCA Done in 0.29s
Projecting input data for PCA
transform Done in 0.01s
############# SVM ##############
Searching the best parameters for SVC ...
Best param: {'C': 10, 'gamma': 0.001}
best score: 0.80625
############# Predict #################
precision recall f1-score support
c0 0.50 1.00 0.67 1
c1 1.00 0.67 0.80 3
c2 1.00 0.50 0.67 2
c3 1.00 1.00 1.00 1
c4 0.50 1.00 0.67 1
c5 1.00 1.00 1.00 1
c6 1.00 0.75 0.86 4
c7 1.00 1.00 1.00 2
c8 1.00 1.00 1.00 4
c9 1.00 1.00 1.00 2
c10 1.00 1.00 1.00 1
c11 1.00 1.00 1.00 4
c12 1.00 1.00 1.00 4
c13 1.00 1.00 1.00 1
c14 1.00 1.00 1.00 1
c15 0.75 1.00 0.86 3
c16 1.00 1.00 1.00 2
c17 1.00 1.00 1.00 2
c18 1.00 1.00 1.00 2
c19 1.00 1.00 1.00 1
c20 1.00 1.00 1.00 2
c21 0.75 1.00 0.86 3
c22 1.00 1.00 1.00 2
c23 1.00 1.00 1.00 3
c24 1.00 0.67 0.80 3
c25 1.00 1.00 1.00 2
c26 1.00 1.00 1.00 2
c27 1.00 1.00 1.00 2
c28 1.00 1.00 1.00 2
c29 1.00 1.00 1.00 3
c30 1.00 1.00 1.00 2
c31 1.00 1.00 1.00 2
c32 1.00 1.00 1.00 2
c33 1.00 1.00 1.00 3
c34 1.00 1.00 1.00 1
c35 1.00 1.00 1.00 2
c36 1.00 1.00 1.00 2
avg / total 0.97 0.95 0.95 80
平均准确率和召回率都在95%以上,看着还行啊。
试了下降维到40维然后svm分类,准确率达到了99%。看来不是特征越多越好呀。但也不是越少越好,降维到30,效果和140差不多了。