【kaggle实战】从KNN,LR,SVM,RF到深度学习

时间:2022-05-04
本文章向大家介绍【kaggle实战】从KNN,LR,SVM,RF到深度学习,主要内容包括kaggle数据读取、KNN、LR、SVM、Random Forest、Deep Learning、总结、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

@蜡笔小轩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结果稍微差一点,因为只用了像素的局部信息。当然了,模型的对比这里只针对数字识别的问题,对于其他问题可能有不同的结果,要具体问题具体分析,结合模型特点,选取合适的模型。

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