数字识别,从KNN,LR,SVM,RF到深度学习

@蜡笔小轩V

原文:http://blog.csdn.net/Dinosoft/article/details/50734539

之前看了很多入门的资料,如果现在让我来写写,我觉得我会选择”数字识别(digit recognizer)”作为例子,足够有趣,而且能说明很多问题。kaggle是个实践的好地方,python是门方便的语言,sklearn是个不错的库,文档很适合学习。那就用sklearn来实践一下机器学习,加深理解吧!

kaggle数据读取

import pandas as pdimport numpy as npimport timefrom sklearn.cross_validation import cross_val_score#read data dataset = pd.read_csv("./data/train.csv")
X_train = dataset.values[0:, 1:]
y_train = dataset.values[0:, 0]#for fast evaluationX_train_small = X_train[:10000, :]
y_train_small = y_train[:10000]


X_test = pd.read_csv("./data/test.csv").values

这个代码可以作为模版来用。基本的数据读取,切分X和y,切分小数据用于快速迭代。发现训练有些久,打印个时间看看。离线评估cross validation肯定也是要的。

pandas的dataframe当然是个好东西,完整去学太费时间了,建议先把几个常用的学起来就好了吧。

对于数字识别,从”人脑学习”的角度可能是先识别笔画,然后根据笔画构造出来的关键结构去识别。比如8是上下两个圈圈。如果没学过机器学习,可能就从这个思路开始想了。然后,我们来对比看看机器学习是怎么做的。

KNN

KNN在这里有个很直观intuition,跟哪个数字比较像,那就判断为哪个数字。虽然看上去有点土,但道理上完全讲得通!另辟蹊径,倒是一个挺赞的思路。KNN需要记录原始训练样本,有点死记硬背的味道(属于Non-Parametric Models)。说不定阿猫阿狗之类的动物就是用这种方法来”学习”的。

作为一名机器学习调包客,调参侠,sklearn这个库已经选好了,然后就是调参了。文档在KNeighborsClassifier。用来学习确实不错。

为了加速预测,除了在《统计学习方法》看到过的kd tree结构,这里还提到了ball_tree。加上暴力法,那就有三种方法了,即 {‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’}。这个只影响预测时间,不影响训练精确度。

看了文档 (现在知道我说sklearn文档好什么意思吧)发现除了指定k,我们还可以指定半径,不过我试了下,因为这里是高维的,我也选不好半径到底选多少,如果太小就会出现一些样本在半径内没近邻,挺麻烦的。还是用K吧。 先试了一下把K调大,以为判断的时候使用多一些样本,准确率会好转,结果发现居然下降了!仔细想想,K调大,那些越不像的样本也混进来了。这样不行,权重要降低点才行。加上weights=’distance’吧,确实有效!

另一个就是metric=’minkowski’,闵可夫斯基,听起来怪怪的,其实就是更一般的距离公式而已,p=2时是欧拉距离,p=1就是曼哈顿距离。默认是2,调成1发现降了,调成3上升了点(发现速度急速下降!?影响kd tree的构建了?)。貌似不好直观解释,而且这个距离计算,显然还会影响weights=’distance’。这里的距离更直观解释其实是”相似度”,怎样衡量图像相似?貌似不太好说。可能可以用上图像处理方面的技术,这里就不深究了。 代码

#knnfrom sklearn.neighbors import KNeighborsClassifier#begin timestart = time.clock()#progressingknn_clf=KNeighborsClassifier(n_neighbors=5, algorithm='kd_tree', weights='distance', p=3)
score = cross_val_score(knn_clf, X_train_small, y_train_small, cv=3)

print( score.mean() )#end timeelapsed = (time.clock() - start)
print("Time used:",int(elapsed), "s")#k=3#0.942300738697#0.946100822903 weights='distance'#0.950799888775 p=3#k=5#0.939899237556#0.94259888029#k=7#0.935395994386 #0.938997377902#k=9#0.933897851978

最后用全量数据训练,提交kaggle。代码模版

clf=knn_clf

start = time.clock()
clf.fit(X_train,y_train)
elapsed = (time.clock() - start)
print("Training Time used:",int(elapsed/60) , "min")

result=clf.predict(X_test)
result = np.c_[range(1,len(result)+1), result.astype(int)]
df_result = pd.DataFrame(result, columns=['ImageId', 'Label'])

df_result.to_csv('./results.knn.csv', index=False)#end timeelapsed = (time.clock() - start)
print("Test Time used:",int(elapsed/60) , "min")

提交成绩 0.96943 (‘Training Time used:’, 26s) (‘Test Time used:’, 1374s)

LR

轮到万金油一样的LR上场。显然这里用LR并不是很合适,但可以看到结果也没有特别差。这里的LR直观解释就是评估每一个像素点,到底颜色深一点是偏向于目标数字,还是其他数字。但数字主要是靠结构来识别,但笔画深浅,轻微的平移,倾斜,字体变形都会影响LR,所以结果并不会特别好(后面提到的神经网络可以弥补这些不足)。代码里除以256只是为了方便调参C。

#LR also works!from sklearn.linear_model import LogisticRegression#begin timestart = time.clock()#progressinglr_clf=LogisticRegression(penalty='l2', solver ='lbfgs', multi_class='multinomial', max_iter=800,  C=0.2 )#lr_clf=LogisticRegression(penalty='l1', multi_class='ovr', max_iter=400,  C=4 )parameters = {'penalty':['l2'] , 'C':[2e-2, 4e-2,8e-2, 12e-2, 2e-1]}#parameters = {'penalty':['l1'] , 'C':[2e0,2e1, 2e2]}gs_clf =  GridSearchCV(lr_clf, parameters, n_jobs=1, verbose=True )

gs_clf.fit( X_train_small.astype('float')/256, y_train_small )

print()for params, mean_score, scores in gs_clf.grid_scores_:
    print("%0.3f (+/-%0.03f) for %r"  % (mean_score, scores.std() * 2, params))
print()#end timeelapsed = (time.clock() - start)
print("Time used:",elapsed)#可以打印模型参数出来看看#clf.coef_ [1,:]

小数据调参一些结果

0.870 (+/-0.004) for {‘penalty’: ‘l2’, ‘C’: 0.002} 0.900 (+/-0.005) for {‘penalty’: ‘l2’, ‘C’: 0.02} 0.905 (+/-0.001) for {‘penalty’: ‘l2’, ‘C’: 0.2} 0.890 (+/-0.003) for {‘penalty’: ‘l2’, ‘C’: 2.0} (‘Time used:’, 114.5217506956833) 0.900 (+/-0.005) for {‘penalty’: ‘l2’, ‘C’: 0.02} 0.904 (+/-0.006) for {‘penalty’: ‘l2’, ‘C’: 0.04} 0.908 (+/-0.005) for {‘penalty’: ‘l2’, ‘C’: 0.08} 0.908 (+/-0.005) for {‘penalty’: ‘l2’, ‘C’: 0.12} 0.905 (+/-0.001) for {‘penalty’: ‘l2’, ‘C’: 0.2}

最后选LR,max_iter=800, C=0.2,反正训练挺快,多迭代迭代。 成绩 0.92157

SVM

前面提到LR处理这种非线性的问题效果没有特别好;KNN虽然效果较好,但是训练很费时。SVM可以说很好地解决了前面两个问题:RBF核拟合非线性,support vector有点类似KNN的最近邻,但由于是在分类边界,更具”代表性”,比机械地选出最近邻效果更好。而且只保留support vector,预测速度快多了。

#svcfrom sklearn.svm import SVC,NuSVCfrom sklearn.grid_search import   GridSearchCV#begin timestart = time.clock()#progressingparameters = {'nu':(0.05, 0.02) , 'gamma':[3e-2, 2e-2, 1e-2]}

svc_clf=NuSVC(nu=0.1, kernel='rbf', verbose=True )
gs_clf =  GridSearchCV(svc_clf, parameters, n_jobs=1, verbose=True )

gs_clf.fit( X_train_small.astype('float')/256, y_train_small )

print()for params, mean_score, scores in gs_clf.grid_scores_:
    print("%0.3f (+/-%0.03f) for %r"  % (mean_score, scores.std() * 2, params))
print()#end timeelapsed = (time.clock() - start)
print("Time used:",elapsed)

调成n_jobs=2貌似有异常,只好乖乖用1了。

调参过程

Fitting 3 folds for each of 6 candidates, totalling 18 fits [LibSVM][LibSVM][LibSVM][LibSVM][LibSVM][LibSVM][LibSVM][LibSVM][LibSVM][LibSVM][LibSVM][LibSVM][LibSVM][LibSVM][LibSVM][LibSVM][LibSVM][LibSVM]LibSVM 0.968 (+/-0.001) for {‘nu’: 0.05, ‘gamma’: 0.03} 0.968 (+/-0.001) for {‘nu’: 0.02, ‘gamma’: 0.03} 0.967 (+/-0.003) for {‘nu’: 0.05, ‘gamma’: 0.02} 0.968 (+/-0.002) for {‘nu’: 0.02, ‘gamma’: 0.02} 0.961 (+/-0.002) for {‘nu’: 0.05, ‘gamma’: 0.01} 0.963 (+/-0.002) for {‘nu’: 0.02, ‘gamma’: 0.01} (‘Time used:’, 819.6633204167592)

选nu:0.02, gamma:0.02吧。

svm就是训练慢,预测快。等着无聊,训练过程打印的参数意思可以自己google

optimization finished, #iter = 1456 C = 2.065921 obj = 160.316989, rho = 0.340949 nSV = 599, nBSV = 15

训练时间还能接受

[LibSVM](‘Training Time used:’, 6, ‘min’) (‘Test Time used:’, 12, ‘min’)

成绩 0.98286!

Random Forest

接下来试试集成学习的方法。直观理解,跟LR一样,也是根据每个像素来判断,不过由于底层是树形结构,可以学习到非线性的边界。理论上效果应该比LR会好一些。

from sklearn.ensemble import RandomForestClassifier#begin timestart = time.clock()#progressingparameters = {'criterion':['gini','entropy'] , 'max_features':['auto', 12, 100]}

rf_clf=RandomForestClassifier(n_estimators=400, n_jobs=4, verbose=1)
gs_clf =  GridSearchCV(rf_clf, parameters, n_jobs=1, verbose=True )

gs_clf.fit( X_train_small.astype('int'), y_train_small )

print()for params, mean_score, scores in gs_clf.grid_scores_:
    print("%0.3f (+/-%0.03f) for %r"  % (mean_score, scores.std() * 2, params))
print()#end timeelapsed = (time.clock() - start)
print("Time used:",elapsed)

0.946 (+/-0.002) for {‘max_features’: ‘auto’, ‘criterion’: ‘gini’} 0.945 (+/-0.001) for {‘max_features’: 12, ‘criterion’: ‘gini’} 0.943 (+/-0.005) for {‘max_features’: 100, ‘criterion’: ‘gini’} 0.944 (+/-0.004) for {‘max_features’: ‘auto’, ‘criterion’: ‘entropy’} 0.944 (+/-0.006) for {‘max_features’: 12, ‘criterion’: ‘entropy’} 0.942 (+/-0.007) for {‘max_features’: 100, ‘criterion’: ‘entropy’} () (‘Time used:’, 342.1534636337892) 0.946 (+/-0.005) for {‘max_features’: ‘auto’, ‘criterion’: ‘gini’, ‘max_depth’: None} 0.889 (+/-0.004) for {‘max_features’: ‘auto’, ‘criterion’: ‘gini’, ‘max_depth’: 6} 0.945 (+/-0.004) for {‘max_features’: ‘auto’, ‘criterion’: ‘gini’, ‘max_depth’: 18} 0.946 (+/-0.004) for {‘max_features’: ‘auto’, ‘criterion’: ‘gini’, ‘max_depth’: 32} () (‘Test Time used:’, 1, ‘min’)

貌似都差不多啊。那用默认参数提交吧。 忘记截图了。补一个,0.96714

Deep Learning

这种图像问题,目前最强还是要靠神经网络。学了UFLDL,然后看了theano,发现就是个符号计算和Automatic Differentiation库,然后代码能编译成cuda。然后keras在theano上抽象一层,实现了神经网络的一些通用结构,大概就是这样吧。怎么构建神经网络和调参,目前没啥经验,直接拷一个demo代码来跑跑吧。

#DL  modified from keras's example'''Train a simple convnet on the MNIST dataset.
Run on GPU: THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 python mnist_cnn.py
Get to 99.25% test accuracy after 12 epochs (there is still a lot of margin for parameter tuning).
16 seconds per epoch on a GRID K520 GPU.
'''from __future__ import print_functionimport numpy as np
np.random.seed(1337)  # for reproducibility#from keras.datasets import mnistfrom keras.models import Sequentialfrom keras.layers.core import Dense, Dropout, Activation, Flattenfrom keras.layers.convolutional import Convolution2D, MaxPooling2Dfrom keras.utils import np_utils

batch_size = 128nb_classes = 10nb_epoch = 60# input image dimensionsimg_rows, img_cols = 28, 28# number of convolutional filters to usenb_filters = 32# size of pooling area for max poolingnb_pool = 2# convolution kernel sizenb_conv = 3#(X_train, y_train), (X_test, y_test) = mnist.load_data()X_train = X_train.reshape(X_train.shape[0], 1, img_rows, img_cols)
X_train = X_train.astype('float32')

X_test = X_test.reshape(X_test.shape[0], 1, img_rows, img_cols)
X_test = X_test.astype('float32')

X_test /=255X_train /= 255print('X_train shape:', X_train.shape)
print(X_train.shape[0], 'train samples')# convert class vectors to binary class matricesY_train = np_utils.to_categorical(y_train, nb_classes)#Y_test = np_utils.to_categorical(y_test, nb_classes)model = Sequential()

model.add(Convolution2D(nb_filters, nb_conv, nb_conv,
                        border_mode='valid',
                        input_shape=(1, img_rows, img_cols)))
model.add(Activation('relu'))
model.add(Convolution2D(nb_filters, nb_conv, nb_conv))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(nb_pool, nb_pool)))
model.add(Dropout(0.25))

model.add(Flatten())
model.add(Dense(128))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(nb_classes))
model.add(Activation('softmax'))

model.compile(loss='categorical_crossentropy', optimizer='adadelta')

model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epoch,
          show_accuracy=True, verbose=1 )

test_result=model.predict_classes( X_test, batch_size=128, verbose=1)
result = np.c_[range(1,len(test_result)+1), test_result.astype(int)]
df_result = pd.DataFrame(result[:,0:2], columns=['ImageId', 'Label'])

df_result.to_csv('./results.dl.csv', index=False)

刚开始没开启GPU,一个epoch要800s,开启GPU之后就只要40s了! 发现用GPU的话,CPU占用率是降低了,但是CPU温度高了?!不知是因为导热铜片把GPU的热量导过来,还是CPU跟GPU通过总线交换数据也会发热??

因为不会调参,只好暂时傻逼地增加epoch次数了,发现是有提高了,最后试了60提交

果然是大杀器 0.99114!

总结

LR线性模型显然最弱。神经网络处理这种图像问题确实目前是最强的。svm的support vector在这里起到作用非常明显,准确地找出了最具区分度的“特征图像”。RF有点像非线性问题的万金油,这里默认参数已经很可以了。只比KNN结果稍微差一点,因为只用了像素的局部信息。当然了,模型的对比这里只针对数字识别的问题,对于其他问题可能有不同的结果,要具体问题具体分析,结合模型特点,选取合适的模型。

发现实际动手一下,分析实验结果,对模型的理解加深了。

原文发布于微信公众号 - 大数据挖掘DT数据分析(datadw)

原文发表时间:2016-06-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Petrichor的专栏

tensorflow: 损失函数(Losses Functions) 探究

易得 l2_loss( t, name=None ) 等同于 output = sum(t ** 2) / 2

801
来自专栏目标检测和深度学习

教程 | 如何使用DeepFake实现视频换脸

1692
来自专栏Fred Liang

gg 小组种子杯初赛报告

队员: 柳泓鑫 梁志博 洪志远 AUC: 0.7566 2017年10月1日 Github:https://github.com/ver217/seedc...

642
来自专栏机器之心

教程 | 如何使用DeepFake实现视频换脸

3773
来自专栏生信技能树

【r<-ROC|包】分析与可视化ROC——plotROC、pROC

在【r<-绘图|ROC】ROC的计算与绘制这篇文章中我讲了ROC曲线的本质以及如何计算和绘制ROC曲线。注意,我这里谈到的ROC并未曾涉及机器学习模型的拟合与预...

1352
来自专栏新智元

谷歌开源图像分类工具TF-Slim,定义TensorFlow复杂模型

【新智元导读】谷歌今天宣布开源 TensorFlow 高级软件包 TF-Slim,能使用户快速准确地定义复杂模型,尤其是图像分类任务。这不由让人想起 Faceb...

3336
来自专栏人工智能

从论文到测试:Facebook Detectron开源项目初探

机器之心专栏 作者:陈惠婵 从 RCNN 到 Faster RCNN,再到最近的 FPN 和获得 ICCV Best Paper 的 Mask RCNN,深度学...

30210
来自专栏开源FPGA

基于FPGA的均值滤波算法的实现

  前面实现了基于FPGA的彩色图像转灰度处理,减小了图像的体积,但是其中还是存在许多噪声,会影响图像的边缘检测,所以这一篇就要消除这些噪声,基于灰度图像进行图...

2546
来自专栏深度学习自然语言处理

如何用简单易懂的例子解释隐马尔可夫模型?(入门篇)

因为文章总共超过5W字,所以我分为两部分,今天这是第一部分,先自己大致了解下什么是HMM,明天将会是具体的通俗公式讲解。加油,每天进步一丢丢O.O

784
来自专栏专知

机器翻译新时代:Facebook 开源无监督机器翻译模型和大规模训练语料

【导读】基于深度学习的机器翻译往往需要数量非常庞大的平行语料,这一前提使得当前最先进的技术无法被有效地用于那些平行语料比较匮乏的语言之间。为了解决这一问题,Fa...

42611

扫码关注云+社区