手写数字图像识别作为机器学习入门教程。本文分别使用KNN、SVM算法实现手写数字识别后续教程继续补充全连接神经网络和卷积神经网络的识别,对比不同算法的实现难度、训练耗时、准确率等参数。
欢迎关注个人博客
数据
使用Google的手写数字图像,数据中包含55000的训练数据,10000的测试数据和5000条验证数据,下载地址
标记数据包含0-9一共10个数字,是一个多分类问题,详细介绍可以参看MNIST 入门 。
KNN算法
最近最邻算法,这是一种有监督算法,思路非常简单,简单说就是“物以类聚”,通过对标记数据进行训练以后,对于预测数据,由最近K的邻居投票,决定这个数据属于哪个类,非常适合多分类问题,通常情况下准确度非常好。这里使用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个线程训练,叶子大小控制为10,距离使用默认的欧氏距离。训练数据只选用10000个样本,准确率达到0.9146,训练耗时0.546s
SVM算法
作为经典机器学习算法,支持向量机具有很大的价值,其应用面非常广,可以适用于线性和非线性分类。但默认是线性分类,需要选择非线性核,非线性核将原来线性不可分的样本,映射到更高维空间,变得线性可分。如何映射成线性可分,有一定难度,暂时不详说。
支持向量机是一种二分类算法,通过构建多种分类器,来支持多分类问题,常用的有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):
其余参数使用默认值,默认非线性核rbf
, 多分类采用ovr
。训练数据只选用10000个样本,准确率达到0.9206,训练耗时19.226s
代码分析
所有代码可以移步github下载,这里主要介绍模型训练的接口
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):
"""
加载模型,如果存在直接返回,否则重新训练模型
:param model_name: 模型名称
:param model: sk-learn 创建的模型对象
: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):
"""输出模型的验证数据"""
data, targets = validation.images[:10], validation.labels[:10] # 10 validation
predicts = model.predict(data)
for y_, y in zip(predicts, targets):
logger.info("预测值: %s 真实值:%s", y_, y)
add_model
接口如果模型存在,则直接返回模型对象,如果不存在,则需要重新训练模型并保存。
主程序支持加载多个模型,但只能单线程的依次训练,有兴趣可以改成多进程训练,最大程度的使用CPU。多个模型加载需要使用model
模块中的Model
类启动训练。
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
主函数中只需要定义好模型,然后通过model
对象的add()
方法添加,然后启动run()
方法,就可以依次训练模型。
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() # 启动