机器学习-使用mnist数据集实践二元分类器训练

我们使用的mnist数据集,这是一组由高中生和美国人口普查局的员工手写的70000个数字图像。这个集合已经被研究如此之多,以此通常被称为机器学习的“hello world”。7万张图片,每个图片有784个特征,因为每个图像都是28×28像素,每个特征只代表一个像素的强弱,从0到255。

 

一. 训练二元分类器

首先我们来构建一个简化问题,只尝试识别一个数字,如数字5。这个“5的检测器”就是二元分类器的一个示例,能够区分两个类:5和非5.

前提准备:从scikit-learn中下载对应的数据集,准备对应的训练数据集和测试数据集

from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784',version=1,as_frame=False)
X,y = mnist.data,mnist.target
X_train,X_test,y_train,y_test = X[:60000],X[60000:],y[:60000],y[60000:]
some_digit = X[0]

  

1.首先为这个分类任务创建目标向量

y_train_5= (y_train==5)
y_test_5 = (y_test==5)

2.选择一个分类器并对其进行训练,一个好的起点是使用scikit-learn 的随机下降分类器即SGDClassifier。

#在整个训练集上训练
from sklearn.linear_model import SGDClassifier
sgd_clf = SGDClassifier(random_state=42)
sgd_clf.fit(X_train,y_train_5)
sgd_clf.predict([some_digit])

  

二、评估模型,即性能测量

1.使用交叉验证测量精度

1.1使用cross_val_score()函数来评估SGDClassifier模型,使用3个折叠的k折交叉验证

from sklearn.model_selection import cross_val_score
cross_val_score(sgd_clf,X_train,y_train_5,cv=3,scoring="accuracy")# 结果超过95%

即所有交叉验证折叠的精度都超过95%

1.2 我们再使用一个虚构分类器,它只对最频繁出现的类中的每个图像进行分类,在这种情况下训练的结果是阴性类(非5)

from sklearn.dummy import DummyClassifier
dummy_clf = DummyClassifier()
dummy_clf.fit(X_train,y_train_5)
print(any(dummy_clf.predict(X_train)))
cross_val_score(dummy_clf,X_train,y_train_5,cv=3,scoring="accuracy")# 结果超过90%

 这个模型的所有精度都超过了90%。为啥是90%?因为只有大约10%的图像是5,所以如果你总是猜测图像不是5,那么你就在90%的情况下都是正确的。

以上两个例子对比,说明了精度通常不是分类器的首选性能指标,尤其是当你处理不平衡的数据集时;

2.使用混淆矩阵评估分类器的性能
2.1 混淆矩阵的思想:计算所有A/B对的A类实例被分类为B类的次数。
计算混淆矩阵,首先需要有一组预测值,以便可以将它们与实际目标进行对比。混淆矩阵中的
每一个行代表一个实际类,每一列代表一个预测类

# 第一步:获取预测值:使用cross_val_predict执行k折交叉验证,但它不返回评估分数,而是返回对每个测试折叠进行预测的预测值
from sklearn.model_selection import cross_val_predict
y_train_pred = cross_val_predict(sgd_clf,X_train,y_train_5,cv=3)

#第二步:计算混淆矩阵:使用confusion_matrix函数,把目标类y_train_5和预测类y_train_pred传递进去 from sklearn.metrics import confusion_matrix, roc_auc_score cm = confusion_matrix(y_train_5,y_train_pred) print(cm)

该矩阵的第一行考虑非5(阴性类)图像:其中多少张被正确分类为非5(真阴性),而其余多少张被错误分类为5(假阳性)

第二行考虑5(阳性类)的图像:其中多少张被正确分类为非5(假阴性),而其余多少张被错误分类为5(真阳性)。

混淆矩阵提供了大量信息,但是可以使用更简洁的指标:分类器的精确率、召回率

2.2 计算精确率和召回率

将精准率和召回率组合成一个称为F1的分数的指标通常很方便,尤其当你需要一个指标来比较两个分类器时。F1分数是精准率和召回率的调和均值,其为低值赋予更多的权重。


 

计算精准率和召回率
from sklearn.metrics import  precision_score,recall_score,f1_score
precision_score(y_train_5,y_train_pred)
recall_score(y_train_5,y_train_pred)
#计算F1分数 f1_score(y_train_5,y_train_pred)

2.3 精准率/召回率权衡

提高精准率会较低召回率,反之亦然,如何兼顾两者?

对于每个实例,SGDClassifier会根据决策函数计算分数。如果该分数大于阈值,则将实例分配给阳性类,否则分配给阴性类。

#首先,调用decision_function返回预测的决策分数
y_scores = sgd_clf.decision_function([some_digit])
#然后根据分数使用想要的阈值进行预测
threshold = 0 #设置阈值为0
y_some_digit_pred = (y_scores>threshold) #输出为True

threshold = 3000 #设置阈值为3000
y_some_digit_pred = (y_scores>threshold) #输出为False

 以上例子证实提高阈值,会降低召回率。

如何决定使用哪个阈值呢?

 一种方法是绘制作为阈值函数的精确率和召回率图

# 首先,使用cross_val_predict函数获取训练集中所有实例的分数,但这次指定要返回决策的分数
y_scores = cross_val_predict(sgd_clf,X_train,y_train_5,cv=3,method="desicion_function")
# 然后,使用precision_recall_curve计算所有可能的阈值的精准率和召回率
from sklearn.metrics import precision_recall_curve

# 方法一:绘制准确率和召回率与决策阈值图
# 图中准确率曲线比召回率曲线更曲折,因当提供阈值时,准确率有时会下降
precisions, recalls,thresholds = precision_recall_curve(y_train_5,y_scores)
plt.plot(thresholds,precisions[:-1],"b--",label="precision",linewidth=2)
plt.plot(thresholds,recalls[:-1],"g-",label="recall",linewidth=2)
plt.vlines(thresholds,0,1,"k",label="threshold")
plt.show()

image

 

方法二:直接绘制精确率与召回率的关系图

# 选择一个好的准确率/召回率权衡的另一种方法是直接绘制准确率和召回率关系图
plt.plot(recalls,precisions,linewidth = 2,label="precision/recall curve")
plt.show()

  

image

上图可看到精确率在80%左右时开始急剧下降,你可能希望在该下降之前选择精确率/召回率权衡---例如60%左右的召回率。

根据项目需求选择阈值:
• ​​高召回率需求​​(如疾病检测):选择召回率较高但精确率可接受的阈值。
• ​​高精确率需求​​(如推荐系统):选择精确率较高但召回率可接受的阈值。
• ​​平衡需求​​:选择F1值最大的阈值

假设决定以90%的精确率为目标,可在上图中找到需要的阈值,但是不是很准确。或者,可以搜索能提供至少90%精确率的最小阈值。

可以使用numpy数组的argmax()方法,返回最大值的第一个索引

# 1.搜索能提供至少90%准确率的最低阈值,可以使用numpy数组的argmax方法,返回最大值的第一个索引
idx_for_90_precision =(precisions>=0.9).argmax()
threshold_for_90_precision=thresholds[idx_for_90_precision]

# 2.在训练集上进行预测,不是调用分类器的predict方法
y_train_pre_90 = (y_scores>=threshold_for_90_precision)

# 3.检查预测的准确率和召回率
precision_score(y_train_5,y_train_pre_90) # 结果是90%
recall_score(y_train_5,y_train_pre_90) # 结果是48%

  太好了,现在就具有一个90%精确率的分类器啦!但是,你会发现创建你想要的几乎任何精确率的分类器都相当容易:只需设置足够高的阈值。

但是等等,如果召回率太低,高精确率分类器也不是很有用哦!

2.4 ROC曲线

ROC曲线是二元分类器的另外一种常用工具,它跟PR曲线(精确率/召回率)非常相似,绘制但是真阳性率与假阳性率。

要绘制ROC曲线

#首先使用函数计算各种阈值的TPR和FPR
from sklearn.metrics import roc_curve
fpr,tpr,threshold = roc_curve(y_train_5,y_scores)
# 使用matplotlib 绘制TPR和FPR的对比图;
#然后找到对应于90%准确率的点,需寻找所需阈值的索引
idx_for_threshold_at_90 =(thresholds<= threshold_for_90_precision).argmax()
tpr_90,fpr_90 = tpr[idx_for_threshold_at_90],fpr[idx_for_threshold_at_90]
plt.plot(fpr,tpr,linewith=2,label="roc curve")
plt.plot([0,1],[0,1],'k:',label="random classifier's roc curve")
plt.plot([fpr_90],[tpr_90],'ko',label="threshold for 90% precision")
plt.show()

image

 这里需要再次权衡:召回率越高,分类器产生的假阳性就越多。虚线表示纯随机分类器的ROC曲线,一个好的分类器会尽可能远离那条线。

比较分类器的一种方法是测量曲线下面的面积,即AUC.

完美分类器的ROC AUC等于1,而纯随机分类器的ROC AUC等于0.5
#估计ROC AUC的函数
from sklearn.metrics import roc_auc_score
roc_auc_score(y_train_5,y_scores) #结果输出96%

 

备注:由于ROC曲线与PR曲线非常相似,那么如何选择使用ROC曲线和 准确率/召回率曲线即PR曲线哪一个呢?
只要阳性类很少,或者你更关心假阳性而不是假阴性时,应该选择PR曲线;否则,选择ROC曲线。

现在我们再创建一个随机森林分类器,可以将其PR曲线和F1分数与SGD分类器进行比较:
# 使用随机森林分类器,可以将其PR曲线和F1分数与SGD分类器进行比较
from sklearn.ensemble import RandomForestClassifier
forest_clf = RandomForestClassifier(random_state=42)
# predict_proba 方法返回每个实例的类概率,可以只使用阳性类的概率作为分数
# 可以调用cross_val_predict函数来训练随机森林,使用交叉验证并使其预测每个图像的类概率
y_proba_forest = cross_val_predict(forest_clf,X_train,y_train_5,cv=3,method="predict_proba")
# 查看前两个图像的类概率
print(y_proba_forest[:2]) # 结果是第一张图89%的概率为阳性,第二张图99%的概率是阴性
y_scores_forest = y_proba_forest[:,1]
precisions_forest,recalls_forest,thresholds_forest = precision_recall_curve(y_train_5,y_scores_forest)
#绘制随机森林PR曲线 plt.plot(recalls_forest,precisions_forest,"b-",linewidth=2,label="Random forest")
#绘制SGD PR曲线 plt.plot(recalls,precisions,linewidth = 2,label="SGD") plt.show()

# 计算F1 score 和 AUC score y_train_pred_forest =y_scores_forest[:,1]>=0.5 f1_score(y_train_5,y_train_pred_forest) #结果是92% roc_auc_score(y_train_5,y_scores_forest) #结果是99.8%
#计算精确率和召回率分数 precision_score(y_train_5, y_train_pred_forest) #结果是99.1% recall_score(y_train_5, y_train_pred_forest) #结果是86.6%

image

 从上图可以看到随机森林分类器优于SGD分类器,因为它的PR曲线更接近右上角,并且具有更大的AUC。

 

posted @ 2026-03-05 18:28  sunshine_coast  阅读(1)  评论(0)    收藏  举报