手寫數(shù)字圖像識別作為機器學(xué)習(xí)入門教程。本文分別使用KNN、SVM算法實現(xiàn)手寫數(shù)字識別后續(xù)教程繼續(xù)補充全連接神經(jīng)網(wǎng)絡(luò)和卷積神經(jīng)網(wǎng)絡(luò)的識別,對比不同算法的實現(xiàn)難度、訓(xùn)練耗時、準(zhǔn)確率等參數(shù)。
歡迎關(guān)注個人博客
數(shù)據(jù)
使用Google的手寫數(shù)字圖像,數(shù)據(jù)中包含55000的訓(xùn)練數(shù)據(jù),10000的測試數(shù)據(jù)和5000條驗證數(shù)據(jù),下載地址
標(biāo)記數(shù)據(jù)包含0-9一共10個數(shù)字,是一個多分類問題,詳細(xì)介紹可以參看MNIST 入門 。
KNN算法
最近最鄰算法,這是一種有監(jiān)督算法,思路非常簡單,簡單說就是“物以類聚”,通過對標(biāo)記數(shù)據(jù)進行訓(xùn)練以后,對于預(yù)測數(shù)據(jù),由最近K的鄰居投票,決定這個數(shù)據(jù)屬于哪個類,非常適合多分類問題,通常情況下準(zhǔn)確度非常好。這里使用sklearn包給出的knn接口
knn = KNeighborsClassifier(n_neighbors=10, n_jobs=8, leaf_size=10)
__init__(self, n_neighbors=5,
weights='uniform', algorithm='auto', leaf_size=30,
p=2, metric='minkowski', metric_params=None, n_jobs=1,
**kwargs):
附近10個neighbors,啟動8個線程訓(xùn)練,葉子大小控制為10,距離使用默認(rèn)的歐氏距離。訓(xùn)練數(shù)據(jù)只選用10000個樣本,準(zhǔn)確率達到0.9146,訓(xùn)練耗時0.546s
SVM算法
作為經(jīng)典機器學(xué)習(xí)算法,支持向量機具有很大的價值,其應(yīng)用面非常廣,可以適用于線性和非線性分類。但默認(rèn)是線性分類,需要選擇非線性核,非線性核將原來線性不可分的樣本,映射到更高維空間,變得線性可分。如何映射成線性可分,有一定難度,暫時不詳說。
支持向量機是一種二分類算法,通過構(gòu)建多種分類器,來支持多分類問題,常用的有ovo和ovr,本文依然使用sklearn包給出的接口。
svc = SVC(C=10)
def __init__(self, C=1.0, kernel='rbf', degree=3, gamma='auto',
coef0=0.0, shrinking=True, probability=False,
tol=1e-3, cache_size=200, class_weight=None,
verbose=False, max_iter=-1, decision_function_shape='ovr',
random_state=None):
其余參數(shù)使用默認(rèn)值,默認(rèn)非線性核rbf, 多分類采用ovr。訓(xùn)練數(shù)據(jù)只選用10000個樣本,準(zhǔn)確率達到0.9206,訓(xùn)練耗時19.226s
代碼分析
所有代碼可以移步github下載,這里主要介紹模型訓(xùn)練的接口
import os.path
from sklearn.externals import joblib
import logging
import time
from data import validation
logger = logging.getLogger(__file__)
def add_model(model_name, model=None):
"""
加載模型,如果存在直接返回,否則重新訓(xùn)練模型
:param model_name: 模型名稱
:param model: sk-learn 創(chuàng)建的模型對象
:return: model
"""
if os.path.exists(model_name):
logger.info("%s is exist, load ...", model_name)
return joblib.load(model_name)
if model is None:
logger.error("please set model")
return
from data import train, test
logger.info("start train model")
tic = time.time()
model.fit(train.images[:10000], train.labels[:10000]) # 10000 train
logger.info("%s cost %s to train model", model_name, time.time() - tic)
score = model.score(test.images[:5000], test.labels[:5000]) # 5000 test
logger.info("%s score is %s", model_name, score)
joblib.dump(model, model_name)
logger.info("save model as %s", model_name)
return model
def validation_model(model):
"""輸出模型的驗證數(shù)據(jù)"""
data, targets = validation.images[:10], validation.labels[:10] # 10 validation
predicts = model.predict(data)
for y_, y in zip(predicts, targets):
logger.info("預(yù)測值: %s 真實值:%s", y_, y)
add_model接口如果模型存在,則直接返回模型對象,如果不存在,則需要重新訓(xùn)練模型并保存。
主程序支持加載多個模型,但只能單線程的依次訓(xùn)練,有興趣可以改成多進程訓(xùn)練,最大程度的使用CPU。多個模型加載需要使用model模塊中的Model類啟動訓(xùn)練。
from api import add_model, validation_model
class Model:
def __init__(self):
self._model_name = []
self._model = []
def add(self, model_name, model):
self._model_name.append(model_name)
self._model.append(model)
@staticmethod
def _run_model(model_name, model):
model = add_model(model_name, model)
validation_model(model=model)
def run(self):
for _ in map(self._run_model, self._model_name, self._model):
pass
主函數(shù)中只需要定義好模型,然后通過model對象的add()方法添加,然后啟動run()方法,就可以依次訓(xùn)練模型。
from config import LOG_CONF
import logging
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from model import Model
logging.basicConfig(**LOG_CONF)
logger = logging.getLogger(__file__)
if __name__ == '__main__':
models = Model()
svc = SVC(C=10)
models.add(model_name='svc.model', model=svc) # 添加模型
knn = KNeighborsClassifier(n_neighbors=10, n_jobs=8, leaf_size=10)
models.add(model_name='knn.model', model=knn) # 添加模型
models.run() # 啟動