导语:
本次内容主要是针对上一讲中的分类模型进行调参,然后基于fetch_lfw_people数据集进行人脸识别实战。学习链接:
集成学习: EnsembleLearning项目-github.
1.分类模型调参
调参我们使用网格搜索和随机搜索。由于处理过程比较类似,这里只针对上一任务中“问题较大”的SVM模型进行调参,其他模型调参过程同理。
上一任务中,针对iris数据集,SVC使用默认参数,训练结果如下:
测试集得分: 0.9210526315789473
训练集得分: 0.9910714285714286
此时,测试集的得分与训练集得分有一定差距,判断存在轻微过拟合现象,下面我们分别用网格搜索和随机搜索对模型进行调优,看能否取得更好的效果。
1.网格搜索:
# 使用网格搜索进行超参数调优:
# 方式1:网格搜索GridSearchCV()
from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC
import time
start_time = time.time()
pipe_svc = make_pipeline(StandardScaler(),SVC(random_state=1))
param_range = [0.0001,0.001,0.01,0.1,1.0,10.0,100.0,1000.0]
param_grid = [{'svc__C':param_range,'svc__kernel':['linear']},{'svc__C':param_range,'svc__gamma':param_range,'svc__kernel':['rbf']}]
gs = GridSearchCV(estimator=pipe_svc,param_grid=param_grid,scoring='accuracy',cv=10,n_jobs=-1)
gs = gs.fit(X_train,y_train)
end_time = time.time()
print("网格搜索经历时间:%.3f S" % float(end_time-start_time))
print(gs.best_score_)
print(gs.best_params_)
print("测试集得分:",svc_iris.score(X_test,y_test))
print("训练集得分:",svc_iris.score(X_train,y_train))
网格搜索经历时间:0.670 S
0.9818181818181818
{‘svc__C’: 10.0, ‘svc__kernel’: ‘linear’}
测试集得分: 0.9473684210526315
训练集得分: 0.9821428571428571
2.随机搜索
# 方式2:随机网格搜索RandomizedSearchCV()
from sklearn.model_selection import RandomizedSearchCV
from sklearn.svm import SVC
import time
start_time = time.time()
pipe_svc = make_pipeline(StandardScaler(),SVC(random_state=1))
param_range = [0.0001,0.001,0.01,0.1,1.0,10.0,100.0,1000.0]
param_grid = [{'svc__C':param_range,'svc__kernel':['linear']},{'svc__C':param_range,'svc__gamma':param_range,'svc__kernel':['rbf']}]
# param_grid = [{'svc__C':param_range,'svc__kernel':['linear','rbf'],'svc__gamma':param_range}]
gs = RandomizedSearchCV(estimator=pipe_svc, param_distributions=param_grid,scoring='accuracy',cv=10,n_jobs=-1)
gs = gs.fit(X_train,y_train)
end_time = time.time()
print("随机网格搜索经历时间:%.3f S" % float(end_time-start_time))
print(gs.best_score_)
print(gs.best_params_)
print("测试集得分:",gs.score(X_test,y_test))
print("训练集得分:",gs.score(X_train,y_train))
随机网格搜索经历时间:0.156 S
0.9818181818181818
{‘svc__kernel’: ‘linear’, ‘svc__C’: 10.0}
测试集得分: 0.9473684210526315
训练集得分: 0.9821428571428571
结论:
两次搜索的得分一致,且都取得了比默认参数更好的效果:训练集得分下降了一点,但是测试集得分明显提高了0.026!这是一个比较好的结果,意味着我们此时得到的模型泛化性能更好。值得注意的是,在取得同样效果的情况下,随机搜索耗时0.156 S,而网格搜索耗时0.670 S,随机搜索足足快了4倍多!
接下来,我们看看SVC中的参数C对验证曲线的影响:
# 用验证曲线解决欠拟合和过拟合
from sklearn.model_selection import validation_curve
train_scores,test_scores = validation_curve(estimator=gs,X=X_train,y=y_train,param_name='estimator__svc__C',param_range=param_range,cv=10,n_jobs=-1)
train_mean = np.mean(train_scores,axis=1)
train_std = np.std(train_scores,axis=1)
test_mean = np.mean(test_scores,axis=1)
test_std = np.std(test_scores,axis=1)
plt.plot(param_range,train_mean,color='blue',marker='o',markersize=5,label='training accuracy')
plt.fill_between(param_range,train_mean+train_std,train_mean-train_std,alpha=0.15,color='blue')
plt.plot(param_range,test_mean,color='red',marker='s',markersize=5,label='validation accuracy')
plt.fill_between(param_range,test_mean+test_std,test_mean-test_std,alpha=0.15,color='red')
plt.xscale('log')
plt.xlabel("Parameter C")
plt.ylabel("Accuracy")
plt.legend(loc='lower right')
plt.ylim([0.8,1.02])
plt.show()
2.人脸识别实战
我们基于fetch_lfw_people数据集进行人脸识别实战,有关该数据集的详细解释,请参阅:
fetch_lfw_people人脸图像数据集
虽然用机器学习的方法处理图像比不上深度网络的精度,但却可以帮助我们理解以及从另一个角度进行思考。下面的这两个案例,均是使用PCA+SVM完成对人脸的分类,有兴趣地可以看一看:
https://blog.csdn.net/cwlseu/article/details/52356665
https://blog.csdn.net/jasonzhoujx/article/details/81905923
为了探索不同角度,我准备基于树模型,随机森林来尝试本次实战任务:
2.1 数据准备
导入数据集。由于原数据集比较庞大,且某些类别很少,全部处理涉及到稀疏性与样本不均衡的问题,为了简化问题,这里只挑选人脸至少在60张以上的类别组成数据集来完成任务:
#下载数据
from sklearn.datasets import fetch_lfw_people
faces = fetch_lfw_people(min_faces_per_person=60) #每个人至少有60个样本
print(faces.target_names)
print(faces.images.shape)
结果:
[‘Ariel Sharon’ ‘Colin Powell’ ‘Donald Rumsfeld’ ‘George W Bush’
‘Gerhard Schroeder’ ‘Hugo Chavez’ ‘Junichiro Koizumi’ ‘Tony Blair’]
(1348, 62, 47)
可以看出,我们共筛选了1348张图片,包含8个人。每张图片共有像素62X47,即每个标签共有2914个特征。
我们可以可视化一下我们的数据是什么样:
#画一些人脸,看看需要处理的数据
import matplotlib.pyplot as plt
import seaborn as sns;sns.set()
fig, ax = plt.subplots(3,5)
fig.subplots_adjust(left=0.0625, right=1.2, wspace=1)
for i, axi in enumerate(ax.flat):
axi.imshow(faces.images[i], cmap='bone')
axi.set(xticks=[], yticks=[], xlabel=faces.target_names[faces.target[i]])
2.2 模型特征提取
我们先对比一下决策树和随机森林在数据集上的效果:
import pandas as pd
import sklearn
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import classification_report
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
#特征选取
X = faces.data
y = faces.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)
# 使用决策树
dtc = DecisionTreeClassifier()
dtc.fit(X_train, y_train)
dt_predict = dtc.predict(X_test)
print("决策树测试集得分",dtc.score(X_test, y_test))
print(classification_report(y_test, dt_predict, target_names=faces.target_names))
# 使用随机森林
rfc = RandomForestClassifier()
rfc.fit(X_train, y_train)
rfc_y_predict = rfc.predict(X_test)
print("随机森林测试集得分",rfc.score(X_test, y_test))
print(classification_report(y_test, rfc_y_predict, target_names=faces.target_names))
结果:
从测试集的得分可以看出,随机森林有着明显的优势,从分类报告中可以清楚地看到每个类别的分类表现。下面我们选择随机森林作为基础模型。
重新划分数据集,25%用于测试,设置随机种子并保持数据划分不变。我们先基于全部数据,用随机森林进行训练,然后根据得到的随机森林特征重要性筛选特征:
#特征选取
X = faces.data
y = faces.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25,random_state=1) #设置随机种子,保证每次数据划分一样
#训练全部数据,作特征筛选
rfc = RandomForestClassifier()
rfc.fit(X, y)
#特征重要性分布,按大到小排列
import numpy as np
feat_labels = pd.DataFrame(X).columns[0:]
importances = rfc.feature_importances_
indices = np.argsort(importances)[::-1]
xl_list=[] #存储特征
yl_list=[] #存储对应的重要性数值
for f in range(X_train.shape[1]):
print("%2d) %-*s %f" % (f + 1, 30, feat_labels[indices[f]], importances[indices[f]]))
xl_list.append(feat_labels[indices[f]])
yl_list.append(importances[indices[f]])
上面的代码,我们得到了特征重要性从高到低的分布表格。为便于筛选特征和可视化,我们按照特征重要性从高到低,每次累加取10个特征,然后基于训练集数据,用随机森林完成训练,并获得交叉验证的平均得分,以此作为筛选依据。
from sklearn.model_selection import cross_val_score
features = []
scores_list = []
for i in range(10,len(xl_list),10):
clf = RandomForestClassifier()
scores = cross_val_score(clf, pd.DataFrame(X_train)[xl_list[:i]], y_train)
print(i,scores.mean())
features.append(i)
scores_list.append(scores.mean())
#可视化
plt.plot(features,scores_list,color='blue',markersize=5)
plt.xlabel("features")
plt.ylabel("score")
上图就是一个关于特征数量和得分之间的图了,可以看出,模型的得分并没有随着特征数的变多而不断提高,说明像素中存在很多冗余和噪音。通过索引我们可以发现,在特征数量为前380个时,取得最大分数0.6498317319416671。因此我们以这380个特征作为特征筛选的结果,瞬间就把2914维度降低到了380维度!
2.3 模型调参优化
我们使用降维后的数据,训练一下看看效果:
# 使用随机森林
rfc = RandomForestClassifier()
X_train380 = pd.DataFrame(X_train)[xl_list[:380]]
X_test380 = pd.DataFrame(X_test)[xl_list[:380]]
rfc.fit(X_train380, y_train)
rfc_y_predict = rfc.predict(X_test380)
print("随机森林测试集得分",rfc.score(X_test380, y_test))
print(classification_report(y_test, rfc_y_predict, target_names=faces.target_names))
跟之前相比,在维度大幅减小的情况下,准确率还提升了!
接下来,网格搜索调参。。。。