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)
噪音数据如下:

去噪后数据如下:

原始数据:

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