Chapter3 Classification(包括Precision/Recall)

一、MNIST

用MNIST数据集做classification

from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784',version=1)
mnist.keys()

mnist keys的打印结果:

dict_keys(['data', 'target', 'frame', 'categories', 'feature_names', 'target_names', 'DESCR', 'details', 'url'])

X,y= mnist['data'],mnist['target']

X.shape
#(70000, 784)
y.shape
#(70000,)

有70,000图片,每个图片有784个features

这是因为每个图片大小是28*28(=784)个pixel,每张图片pixel的intensity是0-255

用matplotlib展示数据集里的其中的一张图片看:

把之前的X[0]拿出来,就是第一张图(?),因为拿出来的是一个特征向量,需要再reshape成28*28的array,plot成图片

import matplotlib as mpl
import matplotlib.pyplot as plt

some_digit=X[0]
some_digit_image = some_digit.reshape(28,28)

plt.imshow(some_digit_image, cmap=mpl.cm.binary)
plt.axis("off")
plt.show()

打印结果在chater3_img文件夹里

这个图片打印出来像5,那我们在y里面看看他的label是什么

print('label',y[0])
#label 5

目前打印出来的“5”是string,但是在机器学习里,我们希望它是一个数字numbers,把他转化成number:

import numpy as np
# y = y.astype(np.unit8)
y = y.astype(np.uint8)

#这里报错:AttributeError: module 'numpy' has no attribute 'unit8' —— 已经解决,打错了uint

在做任何事情之前,都要先把数据分成validation和test data set

不过MNIST已经帮你validation和test数据都分割好了,前60,000张图是training,最后10,000张图是test

X_train,X_test,y_trian,y_test = X[:60000],X[60000:],y[:60000],y[60000:]

X_train, X[:60000]就是前面60,000张图

T_test, X[60000:] 从60000张图以后的都是test

Y的train和test同理

二、TRAINING A BINARY CLASSIFIER

1. 首先找出所有都是“5”的手写数字5-detector

y_train_5 = (y_train == 5) #True for all 5
y_test_5 = (y_test == 5)

用 == 来对应每个值,如果是True,就会返回True,这里如果都是5,就会返回True

2.从这里开始使用SGD Classifier - Stochastic Gradient Descent 【有微积分知识】

SGD Classifier需要random state这个参数,他在训练过程中,依赖随机性

from sklearn.linear_model import SGDClassifier

sgd_clf = SGDClassifier(random_state=42)
sgd_clf.fit(X_train,y_train_5)

#use classifier to detect number 5
sgd_clf.predict([some_digit])

报错?我这里预测出来的结果是“3” 不是书上说的 “5”, 但是代码都是一样的,因为之前uint的错,所以一路到这里也错了

解决:报错是因为sgd_clf.fit(X_train,y_train)这一行应该写成sgd_clf.fit(X_train,y_train_5)

三、PERFORMANCE MEASURES

1. Cross-validation

Chapter有详细介绍:

在第二章里,scikit-learn的K-fold cross validation在以下代码里,就是随机把training set 分成10个不同的subsets(子集),把他们叫做folds。 然后电脑会训练和评估Decision Tree Model 10次, 每一次都找不同的fold去做评估evaluation,然后用剩下的9个fold做training 。 最终的结果是一个Array con-training the 10 evaluation scores(10个evaluation分数?)

from sklearn.model_selection import cross_val_score

scores = cross_val_score(tree_reg,housing_prepared,housing_labels,
            scoring='neg_mean_squared_error',cv=10)

tree_rmse_scores = np.sqrt(-scores)

因为sk-learn的cross validation是MSE的对立面(opposite),是一个负数,所以需要在作sqrt平方根的时候在前面加 - 号,变成正数

现在我们用cross_val_score去评估SGDClassifier,用K-fold cross validation把training set分成3个folds。

from sklearn.model_selection import cross_val_score
score_train = cross_val_score(sgd_clf,X_train,y_train,cv=3,scoring='accuracy')

print(score_train)
#[0.87365 0.85835 0.8689 ]

和书上不一样,书上是>93%的准确率,我这90%都没有

Implementing Cross-validation 不用cross_val_score这函数,自己写是这样的:

from sklearn.model_selection import StratifiedKFold
from sklearn.base import clone

skfolds = StratifiedKFold(n_splits=3, shuffle=True, random_state=42)

for train_index, test_index in skfolds.split(X_train, y_train_5):
    clone_clf = clone(sgd_clf)
    X_train_folds = X_train[train_index]
    y_train_folds = y_train_5[train_index]
    X_test_fold = X_train[test_index]
    y_test_fold = y_train_5[test_index]

    clone_clf.fit(X_train_folds, y_train_folds)
    y_pred = clone_clf.predict(X_test_fold)
    n_correct = sum(y_pred == y_test_fold)
    print(n_correct / len(y_pred))

打印结果

correct/pred: 0.9669
correct/pred: 0.91625
correct/pred: 0.96785

现在来看一个classifier "not-5" class,所有不是5的图片

from sklearn.base import BaseEstimator

class Never5Clasifier(BaseEstimator):
    def fit(self,X,y=None):
        return self
    def predict(sefl,X):
        return np.zeros(len(X),1,dtype=bool)

never_5_clf = Never5Clasifier()
cross_val_score(never_5_clf,X_trian,y_train,cv=3,scoring='accuracy')
print(cross_val_score(never_5_clf,X_trian,y_train,cv=3,scoring='accuracy'))

打印结果

[0.0997  0.0965  0.09995]
 

这里也和书上不一样,书上看NOT-5 class的准确率没有5-class高

2.Confusion Matrix

这是一个比上一个办法更好的,评估classifier的方法。

General idea 计算所有class A被当做class B的数量

from sklearn.model_selection import cross_val_predict
y_train_pred = cross_val_predict(sgd_clf,X_train,y_train_5,cv=3)

from sklearn.metrics import confusion_matrix
confusion_matrix(y_train_5,y_train_pred)
print(confusion_matrix(y_train_5,y_train_pred))

打印结果:

53892   687
1891  3530

每个confusion matrix的row-行都代表actual class,每个column-列都代表predicted class

第一行表示这个matrix认为non-5 images(不是5的图片)的数量(negative class)

53892是被正确归类为 non-5 image的,这些也叫true negatives

687是被错与归类于 5s-image(5的图片),也叫false positves

第二行是表示被认为是5(5s-image)的图片(positive class)

1891是被错误地归类于non-5 images的图片,也叫false negative

3530是被正确得归类于5的图片,也叫true positive

TrueFalse
Negative classTrue negative53893False negative1891
Positive classTrue positive3530False positive687

一个完美的分类会只有true positive和true negative,用这个代码试验,检测出来都是5的图片

从打印结果可以看出,右上角的false positive和左下角的false negative全部都是0,只有true positve 和true negative,也就是被正确归类为5-images和non-5 images的结果

y_train_perfect_predictions = y_train_5
perfect_matrix = confusion_matrix(y_train_5,y_train_perfect_predictions)
print('perfect matrix',perfect_matrix)
#perfect matrix 
[[54579     0]
 [    0  5421]]

The accuracy of the positive predictions, called precision of the classifier

positive结果预测的精确度,叫precision

precision =  TP / (TP + FP)

单独用precision并不准确,因为clasifie会ignore all but one positive instance——不知道怎么理解这句话

所以precision经常和recall一起用

Recall也是sensitiviry or the true positive rate(TPR): this is the ratio of positive instances that are correctly detected by the classifier

被classifier正确检测到的正确例子

recall = TP / (TP + FN) 

3. Precision and Recall

用sklearn来算:

from sklearn.metrics import precision_score,recall_score
precision_score = precision_score(y_train_5,y_train_pred)
recall_score = recall_score(y_train_5,y_train_pred)

print('precison:',precision_score)
print('recall:',recall_score)

手动计算:

precision =  TP / (TP + FP) = 3530 / (3530+ 687) = 3530/4217 = 0.8370879772350012

recall = TP / (TP + FN) = 3530 / (3530 + 1891) = 3530/5421 = 0.651171370595831

打印结果:

precison: 0.8370879772350012
recall: 0.6511713705958311

解释:

如果机器识别图片是【5】,那他的正确率是precision 83.7%,此外,他只能检测到65.1%的【5】

When it claims an image represents a 5, it is correct only 83.7% of the time. Moreover, it only detects 65% of the 5s.

我们还习惯性把precision、recall 和F1 score联系在一起。

F1 score 是harmonic mean of precision and recall (调和平均数)

from sklearn.metrics import f1_score
F1_Score = f1_score(y_train_5,y_train_pred)
print('F1 score:',F1_Score)

手动计算

F1 = TP / [TP+(FN+FP)/2] = 3530 / [3530+(687+1891)/2]

= 3530 / [3530 + 2575/2] = 3530 / [3530 +1289] = 3530/4819 = 0.7325171197343847

打印结果:F1 score: 0.7325171197343846

总的来说,准确率低的原因就产将那些看起来像5(只是像,实际并不是5)的预测成了5;而召回率低的原因是把那些看起来不像5(实际上是5,只是可能那个5写得比较丑)预测成不是5。

4. Precision/Recall Trade-off

y_scores = sgd_clf.decision_function([some_digit])
print('y scores:',y_scores)

threshold = 0
y_some_digit_pred = (y_scores > threshold)

threshold = 8000
y_some_digit_pred = (y_scores > threshold)
print(y_some_digit_pred)

y_scores = cross_val_predict(sgd_clf,X_train,y_train_5, cv=3, method='decision_function')


from sklearn.metrics import precision_recall_curve
precisions, recalls, thresholds = precision_recall_curve(y_train_5,y_scores)

5. ROC Curve

Receiver operating characteristic - ROC Curve 是另外一个被用作binary classifier的工具

ROC 展示True positive rate -TPR  和False positive rate -FPR

FPR: negative 的例子被错误的分类为 positTPOve的数量

from sklearn.metrics import roc_curve
fpr,tpr, thresholds = roc_curve(y_train_5,y_scores)

#plt
def plot_roc_curve(fpr, tpr, label=None):
    plt.plot(fpr, tpr, linewidth=2, label=label)
    plt.plot([0, 1], [0, 1], 'k--') # dashed diagonal
    plt.axis([0, 1, 0, 1])                                    # Not shown in the book
    plt.xlabel('False Positive Rate (Fall-Out)', fontsize=16) # Not shown
    plt.ylabel('True Positive Rate (Recall)', fontsize=16)    # Not shown
    plt.grid(True)                                            # Not shown

plt.figure(figsize=(8, 6))                                    # Not shown
plot_roc_curve(fpr, tpr)
fpr_90 = fpr[np.argmax(tpr >= recall_90_precision)]           # Not shown
plt.plot([fpr_90, fpr_90], [0., recall_90_precision], "r:")   # Not shown
plt.plot([0.0, fpr_90], [recall_90_precision, recall_90_precision], "r:")  # Not shown
plt.plot([fpr_90], [recall_90_precision], "ro")               # Not shown
#save_fig("roc_curve_plot")                                    # Not shown
plt.show()

结果表明,TPR越高,就有越多的FALSE POSITVE(FPR)越高

一个好的分类器的 ROC 曲线应该尽可能远离这条线(即向左上角方向靠拢)。

另外一个评估classifier的方法是计算AUC: the area under the curve

from sklearn.metrics import roc_auc_score
auc = roc_auc_score(y_train_5,y_scores)
print('AUC SCORE:',auc)

AUC SCORE: 0.9604938554008616

现在trian一个Random Forest的 Classifier,拿去和SGD的Classifier做比较

from sklearn.ensemble import RandomForestClassifier
forest_clf = RandomForestClassifier(random_state=42)
y_probas_forest = cross_val_predict(forest_clf,X_train,y_train_5,cv=3,method='predict_proba')

y_scores_forest = y_probas_forest[:1]
fpr_forest,tpr_forest, thresholds_forest = roc_curve(y_train_5,y_scores_forest)

recall_for_forest = tpr_forest[np.argmax(fpr_forest >= fpr_90)]

打印出AUC SCORE-RANDOM FOREST:0.9983436731328145

这个分数比之前的要高

三、Multiclass Classification

multiclass classifiers also called multinomial classifiers

有的algorithm可以做多分类:

Logistic Regression classifier

Random Forest classifier

naive Bayes classifier

有的只能做binary分类:

SGD classifier

Support Vector Machine classifier

做多分类的方法:

1)One-verses-the rest(OVR) / One-verses-all

在MNIST里面,把数据分成10个class,从0-9,训练10个binary classifier,每个classifier都代表一个数字。

在做决定的时候,从每个classifier中找到分数最高的那一个

当你想对某张图片进行分类的时候,让每一个分类器对这个图片进行分类,选出决策分数最高的那个分类器。

2)One-verses-one(OvO)

如果有N个class,那就训练 N * (N-1) / 2 个classifier

在MNIST里面,从0-9每一对数字都训练一个classifier,比如0和1,0和2……以此类推一直到9,一共是45个

10 * (10-1) / 2 = 10*9 / 2 = 45 

1. 用OvO

from sklearn.svm import SVC
svm_clf = SVC()
svm_clf.fix(X_train,y_train)
svm_prediction = svm_clf.predict([some_digit])

some_digit_scores = svm_clf.decision_function([some_digit])
print(some_digit_scores)

print(np.argmax(some_digit_scores))
print('svm class:', svm_clf.classes_)
print(svm_clf.classes_[5])

注意这里在fix函数里使用的是y_train而不是y_train_5,表示训练了0-9个过滤器

他训练了45个binary分类器,得到图片的decision scores,然后选择分数最高的那个

select the class that won the most duels

上面的代码在训练集上训练了一个SGDClassifier。在幕后,Scikit-Learn 实际上训练了 10 个二分类器,每个分类器都产到一张图片的决策数值,选择数值最高的那个类。为了证明这是真实的,调用decision_function()方法。不是返回每个样例的一个数值,而是返回 10 个数值,一个数值对应于一个类。
————————————————
版权声明:本文为CSDN博主「别致的SmallSix」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sinat_41942180/article/details/104864888

打印结果:

[[ 1.72501977  2.72809088  7.2510018   8.3076379  -0.31087254  9.3132482
   1.70975103  2.76765202  6.23049537  4.84771048]]

最高数值是对应于的位置索引值: 5
svm class: [0 1 2 3 4 5 6 7 8 9]
class: 5

用OvR的方法

from sklearn.multiclass import OneVsOneClassifier
ovr_clf = OneVsOneClassifier(SVC())
ovr_clf.fit(X_train,y_train)
ovr_clf.predict([some_digit])
print(len(ovr_clf.estimators_))

sgd_clf.fit(X_train,y_train)
sgd_clf.predict([some_digit])

sgd_clf.decision_function([some_digit])

result = cross_val_score(sgd_clf,X_train,y_train,cv=3,scoring='accuracy')
print(result)

len_ovr 10 用OneVerseRest需要拿出0-9个数字的所有binary过滤器,就是10

result: [0.87365 0.85835 0.8689 ]——这分数不像书里有90%+

四、Error Analysis

要得到confusion matrix,先用cross_val_prediction拿到预测,再用confusion_matrix得到

y_trian_pred = cross_val_predict(sgd_clf,X_train_scaled,y_train,cv=3)
conf_mx = confusion_matrix(y_train,y_trian_pred)
print(conf_mx)

#plt
plt.mashow(conf_mx,cmap=plt.cm.gray)
plt.show()

打印出来的confusion matrix都是数字,用图片看更直观

看图可以知道5s-image更加深,比起其他图片,这意味着:

1)有更少的5s-image在数据集里

或者

2)这个过滤器在perform数字5的时候,没有其他数字那么好

分析办法:

首先,你需要将混淆矩阵中的每个值除以相应类别中的图像数量,这样你就可以比较错误率,而不是错误的绝对数字。

you need to divide each value in the confusion matrix by the number of images in the corresponding class so that you can compare error rates instead of absolute numbers of errors

然后,在矩阵在对角线上填上0,只保留误差。

row_sums = conf_mx.sum(axis=1,keepdims=True)
norm_conf_mx = conf_mx / row_sums

np.fill_diagonal(norm_conf_mx,0)
plt.matshow(norm_conf_mx,camp=plt.cm.gray)
plt.show()

现在可以清楚看出分类器制造出来的各类误差。记住:行代表实际类别,列代表预测的类别。第 8、9 列相当亮,这告诉你许多图片被误分成数字 8 或者数字 9。相似的,第 8、9 行也相当亮,告诉你数字 8、数字 9 经常被误以为是其他数字。相反,一些行相当黑,比如第一行:这意味着大部分的数字 1 被正确分类(一些被误分类为数字 8 )。留意到误差图不是严格对称的。举例子,比起将数字 8 误分类为数字 5 的数量,有更多的数字 5 被误分类为数字 8。
————————————————
版权声明:本文为CSDN博主「别致的SmallSix」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sinat_41942180/article/details/104864888

五、Multilabel Classification

比如有3个人在照片上,希望过滤器能标出这三个人是Alice、Bob、Charlie,他的输出是[1,0,1],就意味着Alice是,Bob不是,Charlie是

from sklearn.neighbors import KNeighborsClassifier
y_train_large = (y_train>=7)
y_train_odd = (y_train % 2 == 1)
y_multilabel = np.c_[y_trian_large,y_train_odd]

knn_clf = KNeighborsClassifier()
knn_clf.fit(X_train,y_multilabel)

y_train_large = (y_train>=7) 查看是不是>7的数
y_train_odd = (y_train % 2 == 1) 查看是不是奇数

下面的代码计算全部标签的平均 F1 值:

knn_clf.predict([some_digit])
y_trian_knnn_pred = cross_val_predict(knn_clf,X_train,y_multilabel,cv=3)
f1_score_knn = f1_score(y_multilabel,y_trian_knnn_pred,average='macro')
print('F1 KNN:',f1_score_knn)

average='macro' 是所有标签有着同等的重要性,如果说有的特别重要,有的不是,那就改成average='weighted'

F1 KNN: 0.976410265560605

这个分数还不错

六、Mutioutput Classification

多输出过滤器,这个输出可以有多于2的结果

noise = np.random.randint(0,100,len(X_train),784)
X_train_mod = X_train + noise
noise = np.random.randint(0,100,len(X_test),784)
X_test_mod = X_test + noise
y_train_mod = X_train
y_test_mod = X_test

knn_clf.fit(X_train_mod,y_train_mod)
clean_digit = knn_clf.predict([X_test_mod[some_digit]])
plot_digit(clean_digit)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值