0. 背景
手寫(xiě)數(shù)字識(shí)別是機(jī)器學(xué)習(xí)領(lǐng)域最基本的入門(mén)內(nèi)容,圖像識(shí)別要做到應(yīng)用級(jí)別,實(shí)際是非常復(fù)雜的,目前業(yè)內(nèi)主要還是以深度學(xué)習(xí)為主。
這里簡(jiǎn)單實(shí)現(xiàn)了幾個(gè)不同機(jī)器學(xué)習(xí)算法的數(shù)字識(shí)別。
都是些很基礎(chǔ)的東西,主要作為入門(mén)了解下常用算法的調(diào)參類(lèi)型和簡(jiǎn)單效果。
1. 數(shù)據(jù)源
- 代碼:https://github.com/polegithub/digital-mnist-learning
- 數(shù)據(jù)源:
在路徑digital-minist-data下面,
訓(xùn)練數(shù)據(jù)是28*28的圖片,標(biāo)簽為對(duì)應(yīng)的數(shù)字:0~9.
2. 不同算法對(duì)比
2.1 Logistic Regression 模型
2.1.1 代碼
Logistic回歸是最基本的線(xiàn)性分類(lèi)模型,也可以用于圖像識(shí)別,sklearn的代碼如下:
"""
lbfgs + l2
"""
parameters = {'penalty': ['l2'], 'C': [2e-2, 4e-2, 8e-2, 12e-2, 2e-1]}
lr_clf = LogisticRegression(
penalty='l2', solver='lbfgs', multi_class='multinomial', max_iter=800, C=0.2)
"""
liblinear + l1
"""
parameters = {'penalty': ['l1'], 'C': [2e0, 2e1, 2e2]}
lr_clf=LogisticRegression(penalty='l1', multi_class='ovr', max_iter=800, C=4 )
2.1.2 liblinear + L1測(cè)試結(jié)果
樣本數(shù):1000
grid_scores_:
mean score | scores.std() * 2 | params
0.826 | (+/-0.035) | {'C': 2.0, 'penalty': 'l1'}
0.820 | (+/-0.050) | {'C': 200.0, 'penalty': 'l1'}
0.819 | (+/-0.031) | {'C': 20.0, 'penalty': 'l1'}
超參數(shù)結(jié)論:
- C越大,均方差越大
- 不同的 C 對(duì) mean score 差異不大
- 1000的樣本數(shù)太少,僅供參考
2.1.3 lbfgs + L2測(cè)試結(jié)果
樣本數(shù):1000
grid_scores_:
mean score | scores.std() * 2 | params
0.850 | (+/-0.036) | {'C': 0.12, 'penalty': 'l2'}
0.848 | (+/-0.034) | {'C': 0.08, 'penalty': 'l2'}
0.848 | (+/-0.034) | {'C': 0.2, 'penalty': 'l2'}
0.844 | (+/-0.045) | {'C': 0.04, 'penalty': 'l2'}
0.839 | (+/-0.055) | {'C': 0.02, 'penalty': 'l2'}
超參數(shù)結(jié)論:
- 整理來(lái)看,無(wú)太大差異,相對(duì)來(lái)說(shuō),C 的取值 0.12 表現(xiàn)稍微好一點(diǎn)
- 1000的樣本數(shù)太少,僅供參考
2.2 KNN 近鄰模型
2.2.1 代碼
K最近鄰(kNN,k-NearestNeighbor)分類(lèi)算法也是很基本的算法。識(shí)別圖像的算法有{'auto', 'ball_tree', 'kd_tree', 'brute'} ,本質(zhì)是通過(guò)計(jì)算距離來(lái)計(jì)算矩陣之間的相似度。 sklearn的代碼如下:
knn_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)
2.2.2 測(cè)試結(jié)果
樣本數(shù):1000
grid_scores_:
mean score | scores.std() * 2 | params
0.850 | (+/-0.024) | {'algorithm': 'kd_tree', 'n_neighbors': 3}
0.850 | (+/-0.024) | {'algorithm': 'ball_tree', 'n_neighbors': 3}
0.846 | (+/-0.008) | {'algorithm': 'kd_tree', 'n_neighbors': 7}
0.846 | (+/-0.008) | {'algorithm': 'ball_tree', 'n_neighbors': 7}
0.843 | (+/-0.033) | {'algorithm': 'kd_tree', 'n_neighbors': 5}
0.843 | (+/-0.033) | {'algorithm': 'ball_tree', 'n_neighbors': 5}
0.832 | (+/-0.020) | {'algorithm': 'kd_tree', 'n_neighbors': 9}
0.832 | (+/-0.020) | {'algorithm': 'ball_tree', 'n_neighbors': 9}
超參數(shù)結(jié)論:
- 不同算法 (kd_tree / ball_tree) 對(duì)結(jié)果無(wú)影響,ball_tree 只是優(yōu)化了維度災(zāi)難的問(wèn)題
- n_neighbors 目前結(jié)果來(lái)看,3 的 mean score最佳,但是 7 的均方差最小。
- 1000的樣本數(shù)太少,僅供參考
2.3 RandomForest 隨機(jī)森林模型
2.3.1 代碼
parameters = {'criterion': ['gini', 'entropy'],
'max_features': ['auto', 12, 100]}
rf_clf = RandomForestClassifier(n_estimators=400, n_jobs=4, verbose=1)
2.3.2 測(cè)試結(jié)果
樣本數(shù):1000
grid_scores_:
mean score | scores.std() * 2 | params
0.877 | (+/-0.020) | {'criterion': 'gini', 'max_features': 12}
0.876 | (+/-0.023) | {'criterion': 'entropy', 'max_features': 12}
0.875 | (+/-0.025) | {'criterion': 'gini', 'max_features': 'auto'}
0.871 | (+/-0.045) | {'criterion': 'gini', 'max_features': 100}
0.869 | (+/-0.034) | {'criterion': 'entropy', 'max_features': 100}
0.866 | (+/-0.025) | {'criterion': 'entropy', 'max_features': 'auto'}
超參數(shù)結(jié)論:
- max_features 目前最佳為 12
- gini 略?xún)?yōu)于 entropy, 但并不明顯, 其實(shí)各項(xiàng)參數(shù)的結(jié)果都比較接近。
- 1000的樣本數(shù)太少,僅供參考
2.4 SVM 支持向量機(jī)模型
支持向量機(jī)SVM是通過(guò)構(gòu)建決策面實(shí)現(xiàn)分類(lèi)的模型,決策面構(gòu)建的前提是選取的樣本點(diǎn)(支持向量)距離決策面的距離最大。具體在求解最大距離的時(shí)候,是通過(guò)拉格朗日乘子法進(jìn)行計(jì)算的。
同時(shí)由于很多數(shù)據(jù)并不是線(xiàn)性的,所以在求解距離的時(shí)候會(huì)引入一個(gè)松弛變量, 這個(gè)
求和后的參數(shù)是C,C被稱(chēng)作復(fù)雜性參數(shù),表達(dá)了對(duì)錯(cuò)誤的容忍度:
- 默認(rèn)為1,C較大表示對(duì)錯(cuò)誤的容忍度越低。
- 一般需要通過(guò)交叉驗(yàn)證來(lái)選擇一個(gè)合適的C
- 一般來(lái)說(shuō),如果噪音點(diǎn)較多時(shí),C需要小一些。
scikit-learn中SVM的算法庫(kù)分為兩類(lèi),一類(lèi)是分類(lèi)的算法庫(kù),包括SVC, NuSVC,和LinearSVC 3個(gè)類(lèi)。另一類(lèi)是回歸算法庫(kù),包括SVR, NuSVR,和LinearSVR 3個(gè)類(lèi)。
關(guān)于NuSVC的說(shuō)明:
nuSVC使用了nu這個(gè)等價(jià)的參數(shù)控制錯(cuò)誤率,就沒(méi)有使用C,為什么我們nuSVR仍然有這個(gè)參數(shù)呢,不是重復(fù)了嗎?這里的原因在回歸模型里面,我們除了懲罰系數(shù)C還有還有一個(gè)距離誤差??來(lái)控制損失度量,因此僅僅一個(gè)nu不能等同于C.也就是說(shuō)回歸錯(cuò)誤率是懲罰系數(shù)C和距離誤差??共同作用的結(jié)果。
來(lái)源:https://www.cnblogs.com/pinard/p/6117515.html
2.4.1 代碼
# nuSVC
parameters = {'nu': (0.5, 0.02, 0.01), 'gamma': [0.02, 0.01,'auto'],'kernel': ['rbf','sigmoid']}
svc_clf = NuSVC(nu=0.1, kernel='rbf', verbose=0)
# SVC
parameters = {'gamma': (0.05, 0.02, 'auto'), 'C': [10, 100, 1.0], 'kernel': ['rbf','sigmoid']}
svc_clf = SVC(gamma=0.02)
2.4.2 NuSVC 測(cè)試結(jié)果
樣本數(shù):1000
grid_scores_:
mean score | scores.std() * 2 | params
0.902 | (+/-0.017) | {'gamma': 0.02, 'kernel': 'rbf', 'nu': 0.01}
0.901 | (+/-0.016) | {'gamma': 0.02, 'kernel': 'rbf', 'nu': 0.02}
0.896 | (+/-0.027) | {'gamma': 0.01, 'kernel': 'rbf', 'nu': 0.02}
0.896 | (+/-0.027) | {'gamma': 0.01, 'kernel': 'rbf', 'nu': 0.01}
0.888 | (+/-0.040) | {'gamma': 0.02, 'kernel': 'rbf', 'nu': 0.5}
0.879 | (+/-0.031) | {'gamma': 0.01, 'kernel': 'rbf', 'nu': 0.5}
0.874 | (+/-0.024) | {'gamma': 'auto', 'kernel': 'rbf', 'nu': 0.02}
0.872 | (+/-0.019) | {'gamma': 'auto', 'kernel': 'rbf', 'nu': 0.01}
0.859 | (+/-0.041) | {'gamma': 'auto', 'kernel': 'rbf', 'nu': 0.5}
0.857 | (+/-0.032) | {'gamma': 'auto', 'kernel': 'sigmoid', 'nu': 0.02}
0.856 | (+/-0.042) | {'gamma': 'auto', 'kernel': 'sigmoid', 'nu': 0.5}
超參數(shù)結(jié)論:
- rbf 優(yōu)于 sigmoid
- gamma: auto的效果并不好, 結(jié)果來(lái)看最佳為 0.02
- nu為 0.5 時(shí)效果并不好,0.01 和 0.02 時(shí)無(wú)明顯優(yōu)勢(shì),可以考慮加入 0.05 對(duì)比測(cè)試
- 1000的樣本數(shù)太少,僅供參考
2.4.3 SVC 測(cè)試結(jié)果
樣本數(shù):1000
grid_scores_:
mean score | scores.std() * 2 | params
0.901 | (+/-0.016) | {'C': 10, 'gamma': 0.02, 'kernel': 'rbf'}
0.901 | (+/-0.016) | {'C': 50, 'gamma': 0.02, 'kernel': 'rbf'}
0.894 | (+/-0.031) | {'C': 1.0, 'gamma': 0.02, 'kernel': 'rbf'}
0.883 | (+/-0.026) | {'C': 10, 'gamma': 'auto', 'kernel': 'rbf'}
0.874 | (+/-0.026) | {'C': 50, 'gamma': 'auto', 'kernel': 'rbf'}
0.870 | (+/-0.033) | {'C': 50, 'gamma': 'auto', 'kernel': 'sigmoid'}
0.866 | (+/-0.020) | {'C': 10, 'gamma': 'auto', 'kernel': 'sigmoid'}
0.864 | (+/-0.019) | {'C': 10, 'gamma': 0.05, 'kernel': 'rbf'}
0.864 | (+/-0.019) | {'C': 50, 'gamma': 0.05, 'kernel': 'rbf'}
0.856 | (+/-0.014) | {'C': 1.0, 'gamma': 0.05, 'kernel': 'rbf'}
0.822 | (+/-0.057) | {'C': 1.0, 'gamma': 'auto', 'kernel': 'rbf'}
0.754 | (+/-0.053) | {'C': 1.0, 'gamma': 0.02, 'kernel': 'sigmoid'}
超參數(shù)結(jié)論:
- rbf 優(yōu)于 sigmoid
- gamma: auto的效果并不好, 結(jié)果來(lái)看score最佳為 0.02
- C取10和50甚至100,對(duì)mean score無(wú)明顯影響,但C取1的時(shí)候,均方差偏大。
- 1000的樣本數(shù)太少,僅供參考
2.5 CNN 卷積神經(jīng)網(wǎng)絡(luò)模型
CNN在圖像識(shí)別中曾經(jīng)放光彩,毫無(wú)疑問(wèn)相比前面的幾個(gè)算法,必然是準(zhǔn)確率更高的。數(shù)字識(shí)別在很多框架中作為基礎(chǔ)example,所以這里不再進(jìn)一步展開(kāi),直接用了keras的一個(gè)例子,源碼見(jiàn)這里:mnist_cnn.py
2.5.1 代碼
from __future__ import print_function
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K
...
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
activation='relu',
input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss=keras.losses.categorical_crossentropy,
optimizer=keras.optimizers.Adadelta(),
metrics=['accuracy'])
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_data=(x_test, y_test))
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
2.5.2 測(cè)試結(jié)論
3840/60000 [>.............................] - ETA: 1:36 - loss: 1.3122 - acc: 0.5766
3968/60000 [>.............................] - ETA: 1:35 - loss: 1.2842 - acc: 0.5862
4224/60000 [=>............................] - ETA: 1:34 - loss: 1.2694 - acc: 0.5909
4480/60000 [=>............................] - ETA: 1:34 - loss: 1.2335 - acc: 0.6029
25472/60000 [===========>..................] - ETA: 56s - loss: 0.4524 - acc: 0.8605
...
59520/60000 [============================>.] - ETA: 0s - loss: 0.0462 - acc: 0.9860
59904/60000 [============================>.] - ETA: 0s - loss: 0.0461 - acc: 0.9860
60000/60000 [==============================] - 98s 2ms/step - loss: 0.0461 - acc: 0.9860 -
val_loss: 0.0280 - val_acc: 0.9908
2.6 GBDT
梯度提升決策樹(shù),GBDT(Gradient Boosting Decision Tree) 又叫 MART(Multiple Additive Regression Tree),是一種迭代的決策樹(shù)算法,該算法由多棵決策樹(shù)組成,所有樹(shù)的結(jié)論累加起來(lái)做最終答案。它在被提出之初就和SVM一起被認(rèn)為是泛化能力較強(qiáng)的算法。
2.6.1 代碼
parameters = {'loss': ['deviance'],
'max_depth':[3,10,2],
'learning_rate': [1,0.1,0.05]}
rf_clf = GradientBoostingClassifier(n_estimators=100)
gs_clf = GridSearchCV(rf_clf, parameters, n_jobs=1, verbose=True)
gs_clf.fit(X_train_small.astype('float')/256, y_train_small)
print_grid_mean(gs_clf.grid_scores_)
2.6.2 測(cè)試結(jié)果(樣本數(shù):1000)
grid_scores_:
mean score | scores.std() * 2 | params
0.842 | (+/-0.050) | {'learning_rate': 0.1, 'loss': 'deviance', 'max_depth': 3}
0.828 | (+/-0.054) | {'learning_rate': 0.05, 'loss': 'deviance', 'max_depth': 3}
0.816 | (+/-0.075) | {'learning_rate': 0.1, 'loss': 'deviance', 'max_depth': 2}
0.803 | (+/-0.063) | {'learning_rate': 0.05, 'loss': 'deviance', 'max_depth': 2}
0.748 | (+/-0.043) | {'learning_rate': 0.1, 'loss': 'deviance', 'max_depth': 10}
0.741 | (+/-0.045) | {'learning_rate': 0.05, 'loss': 'deviance', 'max_depth': 10}
0.714 | (+/-0.018) | {'learning_rate': 1, 'loss': 'deviance', 'max_depth': 3}
0.710 | (+/-0.038) | {'learning_rate': 1, 'loss': 'deviance', 'max_depth': 10}
0.704 | (+/-0.050) | {'learning_rate': 1, 'loss': 'deviance', 'max_depth': 2}
結(jié)論來(lái)看,基本默認(rèn)參數(shù)的效果就是最好的。
源碼默認(rèn)learning_rate為0.1,max_depth為3。 損失函數(shù)loss有{'deviance', 'exponential'} 2種,但是如果用exponential的話(huà),會(huì)報(bào)錯(cuò):
ExponentialLoss requires 2 classes
是exponential的一個(gè)bug, 參見(jiàn) StackOverFlow:why-scikit-gradientboostingclassifier...,所以loss直接選了deviance.
2.7 XGBoost
XGBoost是一個(gè)開(kāi)源軟件庫(kù),為C ++,Java,Python,R,和Julia提供了漸變?cè)鰪?qiáng)框架。 它適用于Linux,Windows,MacOS。從項(xiàng)目描述來(lái)看,它旨在提供一個(gè)“可擴(kuò)展,便攜式和分布式的梯度提升(GBM,GBRT,GBDT)庫(kù)”.
一種可擴(kuò)展性的端到端基于樹(shù)的boosting系統(tǒng),這個(gè)系統(tǒng)可以處理稀疏性數(shù)據(jù),通過(guò)分布式加權(quán)直方圖算法去近似學(xué)習(xí)樹(shù),這個(gè)系統(tǒng)也提供基于緩存的加速模式、數(shù)據(jù)壓縮、分片功能。
2.7.1 代碼
import xgboost as xgb
import time
from sklearn.grid_search import GridSearchCV
parameters = {'objective':['binary:logistic'],
'learning_rate': [0.05,0.08], #so called `eta` value
'max_depth': [4,5,6],
'n_estimators': [5,20], #number of trees, change it to 1000 for better results
}
xgb_model = xgb.XGBClassifier()
# xgb_model.fit(X_train_small, y_train_small)
gs_clf = GridSearchCV(xgb_model, parameters, n_jobs=1, verbose=True)
gs_clf.fit(X_train_small.astype('float')/256, y_train_small)
print_grid_mean(gs_clf.grid_scores_)
2.7.2 測(cè)試結(jié)果(樣本數(shù):1000)
grid_scores_:
mean score | scores.std() * 2 | params
0.817 | (+/-0.052) | {'learning_rate': 0.08, 'max_depth': 4, 'n_estimators': 20, 'objective': 'binary:logistic'}
0.817 | (+/-0.035) | {'learning_rate': 0.08, 'max_depth': 5, 'n_estimators': 20, 'objective': 'binary:logistic'}
0.816 | (+/-0.043) | {'learning_rate': 0.05, 'max_depth': 6, 'n_estimators': 20, 'objective': 'binary:logistic'}
0.815 | (+/-0.029) | {'learning_rate': 0.08, 'max_depth': 6, 'n_estimators': 20, 'objective': 'binary:logistic'}
0.813 | (+/-0.038) | {'learning_rate': 0.05, 'max_depth': 5, 'n_estimators': 20, 'objective': 'binary:logistic'}
0.810 | (+/-0.052) | {'learning_rate': 0.05, 'max_depth': 4, 'n_estimators': 20, 'objective': 'binary:logistic'}
0.771 | (+/-0.058) | {'learning_rate': 0.08, 'max_depth': 6, 'n_estimators': 5, 'objective': 'binary:logistic'}
0.770 | (+/-0.053) | {'learning_rate': 0.08, 'max_depth': 5, 'n_estimators': 5, 'objective': 'binary:logistic'}
0.760 | (+/-0.073) | {'learning_rate': 0.08, 'max_depth': 4, 'n_estimators': 5, 'objective': 'binary:logistic'}
超參數(shù)結(jié)論:
-
n_estimators在這里的影響最為直接,n_estimators為5的時(shí)候,score大概在0.75~0.77.但n_estimators為20之后,score直接上到0.8以上。 - 相同
n_estimators的情況下,學(xué)習(xí)率(learning_rate)在0.08比0.05效果更好一些。 -
max_depth目前4、5、6未看到很明顯的差距。
3. 總結(jié)
以上所有的源碼都在這里:digital-mnist-learning
具體的參數(shù)結(jié)論都在每個(gè)算法各自的測(cè)試結(jié)果里,整體橫向?qū)Ρ?,除了CNN效果最好,其他幾個(gè)算法中SVM的效果是僅次于CNN的。在神經(jīng)網(wǎng)絡(luò)出現(xiàn)之前,SVM一直是每年論文投遞的熱門(mén)領(lǐng)域,但知道deep learning的出現(xiàn),準(zhǔn)確率大幅上升之后,SVM的使用者大大減少。很多學(xué)者的研究方向轉(zhuǎn)向的RNN,CNN,LSTM以及各種新模型等。誠(chéng)然,神經(jīng)網(wǎng)絡(luò)的效果的顯著的,但是很多基礎(chǔ)的算法還是需要掌握和了解,因?yàn)檎鎸?shí)的場(chǎng)景中,并不是每個(gè)公司都有足夠量級(jí)的數(shù)據(jù)供deep learning去訓(xùn)練。