1. 程式人生 > 其它 >【kaggle實戰】從KNN,LR,SVM,RF到深度學習

【kaggle實戰】從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結果稍微差一點,因為只用了畫素的區域性資訊。當然了,模型的對比這裡只針對數字識別的問題,對於其他問題可能有不同的結果,要具體問題具體分析,結合模型特點,選取合適的模型。

發現實際動手一下,分析實驗結果,對模型的理解加深了。