読者です 読者をやめる 読者になる 読者になる

株式会社クイックのWebサービス開発blog

HAPPYなエンジニア&デザイナーのブログです

【機械学習】Python3 + scikit-learn で識別率99%の手書き数字の分類器を作った

こんにちは。 Ruby on Rails 、NodeJS、Meteor 等放浪し、現在はデータサイエンティストを目指している五所です。

大学では一応数学を専攻していました。

概要

機械学習への入り口として、よく題材にされるMNISTの手書き数字の分類器を作ってみました。 使ったのはPython3とscikit-learnというライブラリです。

結果としては、

  • トレーニングデータは60000字
  • テストデータは10000字
  • 正答率99.913%
  • トレーニングにかかった時間は5分程度

チューニングゼロでこのスコアが出たので、ライブラリは素晴らしいなあと思いました。

画像サイズは28*28ピクセルなので、784次元ベクトルを入力とした10クラス分類問題です。

他クラス分類問題はランダムフォレスト分類器を使いと良いと聞きかじったので、そうしています。

今回作ったファイルはこちらに置いておきました。

github.com

※追記 この記事を書いた時点では95%でしたが、ちゃんと測りなおしたら99%超えてました。

環境構築

作ったと言っても、実際にコーディングした時間はほんのわずかで、大部分は環境構築やデータの整形、情報収集でした。

具体的には下記をしました。

  • Python3 pip3 virtualenv のインストール
  • virtualenv で仮想環境構築
  • numpy scipy scikit-learn を構築した仮想環境にインストール
  • 手書き数字をダウンロードしてPythonから読み取れる形にする

動作環境はUbuntu 14.04で、Celeron2840N搭載の非力なChromebook上で動いています。 なるべくシステム全体にインストールしたくないので、virtualenvで仮想環境を作成します。

1. Python3 pip3 virtualenv のインストール

sudo apt-get install python3 python-dev python3-pip 
sudo pip install virtualenv

2. virtualenv で仮想環境構築

# プロジェクト作成
mkdir image-classify
cd image-classify
virtualenv -p python3 env
source env/bin/activate

3. numpy scipy scikit-learn を構築した仮想環境にインストール

# sudoで実行しない
pip install numpy
pip install scipy

# 先にライブラリを入れておく
sudo apt-get install gfortran liblapack-dev
pip install scikit-learn

4. 手書き数字をダウンロードしてPythonから読み取れる形にする

http://yann.lecun.com/exdb/mnist/から、下記データをダウンロードします。

  • train-images-idx3-ubyte.gz: training set images (9912422 bytes)
  • train-labels-idx1-ubyte.gz: training set labels (28881 bytes)
  • t10k-images-idx3-ubyte.gz: test set images (1648877 bytes)
  • t10k-labels-idx1-ubyte.gz: test set labels (4542 bytes)
# データを入れておく場所
mkdir data
cd data
wget http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz
wget http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz
wget http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz
wget http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz

ダウンロードした教師データを、ダンプファイルからテキストファイルに変換します。

gzip -dc train-images-idx3-ubyte.gz | od -An -v -tu1 -j16 -w784 | sed 's/^ *//' | tr -s ' ' > train-images.txt
gzip -dc train-labels-idx1-ubyte.gz | od -An -v -tu1 -j8 -w1 | tr -d ' ' > train-labels.txt
gzip -dc train-images-idx3-ubyte.gz | od -An -v -tu1 -j16 -w784 | sed 's/^ *//' | tr -s ' ' > test-images.txt
gzip -dc train-labels-idx1-ubyte.gz | od -An -v -tu1 -j8 -w1 | tr -d ' ' > test-labels.txt

これで下準備はおしまいです。

機械学習

  • データセットをテキストファイルから読み込み、numpyの配列に入れる
  • ランダムフォレスト分類器で学習
  • 最後にテストデータで試し、スコアを出力する
#!/usr/bin/env python

import numpy as np
from sklearn.ensemble import RandomForestClassifier

# learninig data
data_training = np.loadtxt('data/train-images.txt', delimiter=' ')
label_training = np.loadtxt('data/train-labels.txt')

# test data
data_test = np.loadtxt('data/test-images.txt', delimiter=' ')
label_test = np.loadtxt('data/test-labels.txt', delimiter=' ')

# learn
#estimator = LinearSVC(C=1.0)
estimator = RandomForestClassifier()
estimator.fit(data_training, label_training)

# test
print(estimator.score(data_test, label_test))

テストしてみる

python image-classify.py 
### 学習に5分程度 ###

#=> 0.999133333333

感想

機械学習を使えば、こんな感じのことができるんだなあというのが分かりました。

多次元ベクトルをNクラスに分類する問題に帰結できれば、色々と応用できそうですね。

次回以降、パラメータのチューニングや自然言語処理などをしていきたいと思います。

超絶参考にしたサイト

MNIST 手書き数字データを画像ファイルに変換する http://y-uti.hatenablog.jp/entry/2014/07/23/074845

scikit-learnで機械学習を試す SVM http://qiita.com/yasunori/items/8720c85e75b4679cae47

scikit-learnとgensimでニュース記事を分類する http://qiita.com/yasunori/items/31a23eb259482e4824e2

先人に感謝です。

お知らせ

弊社では、機械学習や統計分析等を使って面白いサービスを生み出すエンジニアを募集しています。

興味がありましたら、ぜひご一読下さい。

https://www.green-japan.com/job/27758