代码改变世界

SciKit-Learn & TensorFlow 学习笔记(四)

2018-10-26 09:09  雄风狂飙  阅读(451)  评论(0)    收藏  举报

分类

1.获取MNIST

from sklearn.datasets import fetch_mldata
mnist = fetch_mldata('MNIST original',data_home='./datasets')
mnist

    {'COL_NAMES': ['label', 'data'],

 'DESCR': 'mldata.org dataset: mnist-original',
 '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),
 'target': array([0., 0., 0., ..., 9., 9., 9.])}
任意取一个数据,看一眼,看了第59004个数据,整理为28*28个像素,然后查看输出效果
%matplotlib inline
dt = mnist['data']
trgt = mnist['target']
import matplotlib
import matplotlib.pyplot as plt
plt.imshow(dt[59004].reshape(28,28),cmap=matplotlib.cm.binary,interpolation='nearest')
plt.axis('off')
plt.show()

  

2.训练一个简单的二元分类器

import numpy as np
from sklearn.linear_model import SGDClassifier
divLen = 60000
X_train,X_test,Y_train,Y_test = dt[:divLen],dt[divLen:],trgt[:divLen],trgt[divLen:]
Y_train = ( Y_train == 9 )
Y_test =  ( Y_test == 9 )
shuffle_index = np.random.permutation(divLen)
X_train,Y_train = X_train[shuffle_index],Y_train[shuffle_index]
#训练二元分类器
sgd_clf = SGDClassifier(random_state=42)
sgd_clf.fit(X_train,Y_train)
#进行预测
pre_result=sgd_clf.predict(X_test)
pre = ( pre_result == Y_test )

3.用coss-validation去计算准确性

先自己撸码实现:

from sklearn.model_selection import StratifiedKFold
from sklearn.linear_model import SGDClassifier

sgdCf = SGDClassifier()
skf = StratifiedKFold(n_splits=3,random_state=42)
for trainIndex,testIndex in skf.split(dt,trgt):
    trainX = dt[trainIndex] 
    trainY = trgt[trainIndex] 
    testX = dt[testIndex]
    testY = trgt[testIndex]
    
    sgdCf.fit(trainX,trainY)
    preditY = sgdCf.predict(testX)
    count = sum( preditY == testY )
    print('ratio==%f'%( count / len(preditY) ) )

  再用sklearn自带工具进行分析

from sklearn.model_selection import cross_val_score
cross_val_score(estimator = sgdCf,X = dt,y = trgt,cv = 3,scoring = 'accuracy')

4.混淆矩阵

使用混淆矩阵来确定算法预测的效果,混淆矩阵简述如下图,

                               预测

                      类A     类B

        类A        90        10

实际

       类B        15         85

 

上图表示将所有100个A预测为A的有90个,将A预测为B的有10个。

代码如下:

from sklearn.metrics import confusion_matrix
confusion_matrix(y_true = testY,y_pred = preditY)

5.F1计算公式

TP:将实际值正确分类为正值

FP:将实际值错误分类为正值

TN:将实际值正确分类为负值

FN:将实际值错误分类为负值

precision(准确率) = TP / ( TP + FP )

recall(召回率) = TP / ( TP + FN )

关于TP、FP等的概念,可以参考:

https://blog.csdn.net/abcjennifer/article/details/7359370

F1 = 2 / ( 1/precision + 1/recall ) = TP / ( TP +  ( FN + FP )/2 )

代码:

from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
print('precisionScore = %f'% precision_score(y_true = testY,y_pred = preditY) )
print('recallScore = %f'% recall_score(y_true = testY,y_pred = preditY) )
print('f1_score = %f'% f1_score(y_true = testY,y_pred = preditY) )

6.Precision/recall(准确率/回收率)取舍

以SGDClassfier为类,该分类器针对每一个实例,每次计算一个结果,如果这个结果大于门槛值,则是正类,否则是反类。假设存在一个门槛值,使得准确率是80%,回收率是67%,那么升高这个门槛值之后,有可能会导致准确率是100%,而回收率下降到50%。

 

结论就是,加大门槛值会导致,回收率变小,准确率变大;降低门槛值会导致,回收率变大,准确率变小。

可以定义函数将此进行绘制,绘制代码如下:

from sklearn.metrics import precision_recall_curve
import matplotlib.pyplot as plt

def plot_precision_recall_by_threshold(precision,recalls,thresholds):
    plt.plot(thresholds,precision[:-1],'b--',label='precision')
    plt.plot(thresholds,recalls[:-1],'g--',label='recall')
    plt.xlabel('thresholds')
    plt.ylabel('pre/recall')
    plt.legend(loc='upper left')
    plt.ylim([0,1])

pre , rec , thresholds = precision_recall_curve( trgt ,preditScore)
plot_precision_recall_by_threshold( precision = pre , recalls = rec ,thresholds = thresholds )

  其图如下:

7.ROC曲线(receiver operating characteristic)

类似于上面的precision/recall曲线,ROC曲线是tpr/fpr。ROC曲线的中文名称是受试者工作特征曲线,详情了解:https://baike.baidu.com/item/ROC%E6%9B%B2%E7%BA%BF/775606?fr=aladdin

TPR = TP /( TP + FN )

FPR = FP /  ( FP + TN )

from sklearn.metrics import roc_curve
import matplotlib.pyplot as plt

def plot_roc(fpr,tpr,label=None):
    plt.plot(fpr,tpr,linewidth=2,label=label)
    plt.plot([0,1],[0,1],'k--')
    plt.axis([0,1,0,1])
    
    plt.xlabel('false positive rate')
    plt.ylabel('true positive rate')


fpr , tpr , thresholds = roc_curve( trgt ,preditScore)
plot_roc( fpr = fpr , tpr = tpr  )

 

 

结论:tpr(也就是recall越大),fpr也越大,这就需要做一个权衡了。虚线表示一个纯随机的分类器,好的分类器应该离它越远越好。

另一个衡量分类器好坏的工具叫做AUC(the area under the curve),就是fpr/tpr下面的面积。一个完美的分类器的AUC是1.图上虚线表示的分类器的AUC是0.5.

from sklearn.metrics import roc_auc_score
roc_auc_score(y_true=trgt,y_score=preditScore)

8.到底是precision/recall还是ROC?

 经验告诉我们,如果你的正类较少或者你更关注FP而不是FN,则选择precision/recall,否则请选择ROC。

 9.多元分类器

多元分类器,顾名思义,可以区分多个类型。一些算法可以进行多元分类(随机森林分类器或者朴素贝叶斯分类器),而另一些则是天生的二元分类器(比如支持向量机或者线性分类器)。但是有一些技术可以使二元分类器达到多元分类器的效果。常用的有两种方法,分别是OvA(One vs All)和OvO(One vs One)。

       OVA是指训练n个分类器,每一个分类器可以区分该子类,比如MINST可以建立10个分类器,分别区分0,1,2,。。。,9,然后选择那个得分最高的分类器,便是某个实例的分类结果。

      OvO是指对所有的类型,两两分队,比如MINST有10个数字需要区分,那么就需要 10*9/2 = 45个子分类器,需要这么多的分类器!最后我们寻找赢得其他分类器最多的分类器,就是我们的实例的最终分类结果。OvO的好处是每次只需要训练部分数据。

 scikit-learn会默认使用OVA而不是OVO(支持向量机除外)

下面的代码使用SGDClassifier去训练多元分类器,并获得相应的分数,最终输出的分类结果肯定是得分最高者。

from sklearn.linear_model import SGDClassifier
sgdClf = SGDClassifier(random_state=42)
sgdClf.fit(X=dt,y=trgt)
print( sgdClf.predict( [dt[1000]] ))
print(sgdClf.decision_function([dt[1000]]))
print( sgdClf.classes_ )

  输出结果如下:

[0.]
[[   62693.74962297  -764548.09998436  -182583.18911765  -145500.23300188
   -590935.87246975  -162982.3977424  -1079859.865758    -585952.31933479
   -281134.11284017  -494607.71508837]]
[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
很明显第一个“0分类器”的分数最高,因此结果就是0.
另外,要想执意使用OVO或者OVA,可以使用OneVsOneClassifier和OneVsRestClassifier.
from sklearn.multiclass import OneVsOneClassifier
from sklearn.linear_model import SGDClassifier
OVOClf = OneVsOneClassifier( SGDClassifier(random_state = 42) )
OVOClf.fit( X=dt,y=trgt )
OVOClf.predict([dt[1000]])

  而如果你使用随机森林,则无需使用OVO或者OVA,因为随机森林会自动进行多元分类:

from sklearn.ensemble import RandomForestClassifier
#warning太多,屏蔽掉

import warnings
warnings.filterwarnings('ignore')
randomForestClf = RandomForestClassifier()
randomForestClf.fit( X=dt,y=trgt )
print( randomForestClf.predict([dt[30000]]) )
print( randomForestClf.predict_proba([dt[30000]]) )

  最后来看一下效果,SGDClassifier的效果:

from sklearn.model_selection import cross_val_score
import warnings

warnings.filterwarnings('ignore')
cross_val_score( sgdClf,dt,trgt,cv=3,scoring='accuracy'  )

  array([0.88442749, 0.86696953, 0.89121769])

OneVsOneClassifier,使用SGDClassifier的效果:
from sklearn.model_selection import cross_val_score
import warnings

warnings.filterwarnings('ignore')
cross_val_score( OVOClf,dt,trgt,cv=3,scoring='accuracy'  )

  array([0.92423723, 0.91557022, 0.91813467])

随机森林的效果:

from sklearn.model_selection import cross_val_score
import warnings

warnings.filterwarnings('ignore')
cross_val_score( randomForestClf,dt,trgt,cv=3,scoring='accuracy'  )

  array([0.93940692, 0.93948485, 0.94895204])

以下是使用OneVsAllClassifier的效果:

from sklearn.multiclass import OneVsRestClassifier
from sklearn.linear_model import SGDClassifier
import warnings
warnings.filterwarnings('ignore')
OVAClf = OneVsRestClassifier( SGDClassifier(random_state = 42) )
cross_val_score( OVAClf,dt,trgt,cv=3,scoring='accuracy'  )

  可以看的出来,SGDClassifier默认就是使用OneVsAllClassifier的,而且OneVsAllClassifier的效果确实不如OneVsOneClassifier的效果。

10.错误分析

      如果我们已经训练好了模型,但是并没有取得满意的效果,那么我们可能需要进一步优化、调参,那么对错误的分析就至关重要,这样才可以对症下药。

     先使用cross_val_predict进行预测,之后使用confusion_matrix计算出来混淆矩阵,最后使用matshow展现出来:

from sklearn.model_selection import cross_val_predict
from sklearn.metrics import confusion_matrix
predData = cross_val_predict(sgdCf,dt,trgt,cv=3)
cfm = confusion_matrix(trgt,predData)
plt.matshow(cfm,cmap=plt.cm.gray_r)#原书使用的是gray,但是我觉得效果并不好,而gray_r的效果会更好一些
plt.show()

  

 从上图容易看到,8容易和1、3、5、9发生混淆。我们单独使用8来训练,可以看到如下效果:

from sklearn.model_selection import cross_val_score
import warnings
trgt8 = ( trgt == 8)
warnings.filterwarnings('ignore')
cross_val_score( sgdClf,dt,trgt8,cv=3,scoring='accuracy'  )

  array([0.73227908, 0.71233875, 0.88046972])

from sklearn.model_selection import cross_val_score
import warnings
trgt8 = ( trgt == 5)
warnings.filterwarnings('ignore')
cross_val_score( sgdClf,dt,trgt8,cv=3,scoring='accuracy'  )

  array([0.9583869 , 0.9438992 , 0.94381348])

       很明显,使用同一个分类器,8的分类效果比5的分类效果要差很多。

11.多标签分类器(Multilabel Classification)

      前面说的都是一个实例最终被分为一个标签(不管是待选种类有多少),而Multilabel Classification是将实例分为多个标签。比如人脸识别系统中,待识别人员有A、B、C,而一张图片中存在A、B,输出结果是(1、1、0),这种分类器就是多标签分类器。

from sklearn.neighbors import KNeighborsClassifier
import numpy as np
yLarge = ( trgt >= 7 )
yOdder = ( trgt %2 == 1 )
yMul = np.c_[yLarge,yOdder]
#训练
knnClf = KNeighborsClassifier()
knnClf.fit(X = dt,y = yMul)
#评估
knnClf.predict(dt[[10908]])

12.多输出分类器(Multioutput Classification)

这是一种多标签分类器,而且每一个标签都可以由多种分类标识。英文是multioutput-multiclass classification,简称为multioutput classification。

举例说明一下,给MNIST的每一个输入都加上一个随机数,生成带噪音的数据,训练的分类器的输入是带噪音的数据,输出是去噪后的数据。

from numpy.random import randint
SHOLDER = 6000
dt_train = dt[:SHOLDER]
dt_test = dt[SHOLDER:]
trgt_train = dt[:SHOLDER]
drgt_test = dt[SHOLDER:]

noise = randint(0,100,( len(dt_train),784) )
noise_test = randint(0,100,( len(dt_test),784) )

dt_train_mod = dt_train + noise
dt_test_mod = dt_test + noise_test

knnClf.fit( dt_train_mod,dt_train )
relt = knnClf.predict( dt_test_mod[[234]] )

  绘图函数如下:

def drawImg(x):
    %matplotlib inline
    import matplotlib
    import matplotlib.pyplot as plt
    plt.imshow(x.reshape(28,28),cmap=matplotlib.cm.binary,interpolation='nearest')
    plt.axis('off')
    plt.show()

  利用绘图函数看一下带噪音的数据和去噪后的数据:

#带噪音数据
drawImg(dt_test_mod[[234]])
#去噪后数据
drawImg(relt)

  噪音数据如下:

去噪后数据如下:

原始数据:

原始数据和去噪后的数据,差距还是有一些的,但整体上看起来还算可以。对此,暂不研究了。