#42 手書きの数字画像をPythonで形状認識できるの?

  Python  [公開]
icon Koichi.Muta が 2019/02/11 17:42 に投稿 ( icon Koichi.Muta が 2019/02/18 20:59 に編集 <更新履歴> )
  投稿を編集(サインイン)
  ストック
  アンケート回答

  目次

Pythonとは

 現在ではPythonは世界で最も人気のある言語として親しまれています。
人工知能(AI)や機械学習などの分野が近年ではホットな話題として多く取り上げられているため、一度は耳にしたことがある人は多いと思います。Python言語は最も人工知能を開発しやすい言語と言われています。その理由としてライブラリやモジュールが豊富に用意されており、Java言語などでインポートするように簡単に使うことができます。ライブラリやモジュールとはプログラムを組むときに自分がイチから組まなくても既に誰かが作った「プログラム」です。それを部品のように組み合わせて使うことで、簡単に高度なシステムの実装ができるのです。さらに、Pythonは初心者にも実装ミスを軽減できるように文法が工夫されており、短い文でプログラムを組むことが可能となっています。今回はそのPythonを使って誰でも数字の形状を認識させる画像認識の実装方法をご紹介いたします。
(2019/02/18/ 21:00に記事を編集しました)

①_目的

  1. コンピュータに0~9の数字が書かれた画像を読み込み、形状を認識してもらう。
  2. 数字の形状を認識するためのライブラリ・モジュールを知る
  3. 数字の形状を認識するための機械学習を間近で体験する

②_準備:インストールするもの

  • 言語
    • Python3.6
  • ツール
    • PyCharm CE
  • ライブラリ・モジュール
    • sklearn
    • numpy
    • PIL

③_準備:実装手順

3.1_インストール手順

3.1.1_Pythonのインストール

 Pythonの公式HPでインストールすることができます。バージョンで”2”と”3”が紹介されていますが、最新の”3”以降をインストールしてください。

3.1.2_PyCharm CEのインストール

 Pythonの統合開発環境で有名なものでPyCharmがあります。自分の言語学習やシステム開発関係の仕事でコーディングすることや単体試験、結合試験などに取り掛かる際の作業を効率よく行うことができますのでオススメです。
 公式HPに行くと「Professional」と「Community」版と2種類がありますが、無料版のCommunity版でも問題なく利用できますのでそちらをインストールすることをお勧めいたします。
image-20190129034039167.png

3.1.3_パッケージのインストール

 PythonやPyCharmをインストールした後は、ライブラリやモジュールを使うためにパッケージをインストールする作業に入ります。インストール方法はWindowsでは”コマンドライン”Macでは”ターミナル”で以下のコマンドを入力することで各PCにインストールされたPythonのフォルダに自動で追加することができます。
 今回利用するライブラリの説明を以下に簡単にまとめました。この3種類があれば数字の形状を機械学習して画像を取り込み、画像内の数字を認識する一連の動作を実現することができます。

・scikit-learn

 機械学習を実装させるときに利用します。今回は、数字の形状を認識するために利用します。
読み込んだ画像が0~9までの数字の内どれに当たるのか、ロジスティック回帰を用いて正解である数字の確立を求めていきます。
pip install scikit-learn
image-20190129033615379.png

・numpy

 数値計算に利用します。今回は数字の形状を学習するためのデータ分析として利用します。
pip install numpy
image-20190129033700370.png

・PIL

 画像を読み込むために利用します。
pip install Pillow
image-20190129033726984.png

3.2_実装

 一通りの準備ができたら早速プログラムを作っていきましょう。まずは、数字の形状を認識するためにコンピュータに学習させなければなりません。機械学習用のプログラムを作ります。

3.2.1_"0〜9"までの数字の形を学習させる

 scikit-learnライブラリを用いて機械学習をしていきます。今回は学習データ数は10000に設定しました。そしてその学習結果が反映されているかを見るために学習データとは別に試験用データを1000に設定し、"0〜9"までの数字を1000回分を認識してもらい10個の配列に格納して認識結果を出力していきます。学習用データとして用意するのは、MNISTという数字が書かれた画像サンプルデータを収納しているサイトにアクセスして利用します。(MNISTと検索すると出てきます)また、学習した結果を「Learn.model」というデータとして記録するようにプログラムを組みました。その理由は、次にご紹介する自分自身が書いた数字をコンピュータに認識してもらうプログラムに学習データを読み込ませるためです。動作結果は「④_動作確認」に載せています。

from sklearn import datasets            # 主な用途:分類(糖尿病患者やボストン市住宅関係、数字の手書き文字など)
from sklearn import externals           # 予測モデルの構築
from sklearn import linear_model        # 線形回帰
from sklearn import model_selection     # データ分割

# 学習用データ数
TRAINING_DATA = 10000
# 試験用データ数
TEST_DATA = 1000
# 0〜9までの定数
ONE_TO_NINE = 10

# 重い処理のため、逐一進捗状況を出力させる
print('MNISTの取得中:', flush=True)

# MNIST のデータセットをダウンロード
mnist = datasets.fetch_mldata('MNIST original', data_home='.')

# dataは 28*28 = 784, targetは0~9を対象
data, label = mnist.data, mnist.target
print('取得完了')

# データの選択・データの分割
t = model_selection.train_test_split(data, label, train_size=TRAINING_DATA, test_size=TEST_DATA)

# 学習分のデータを代入
train_data, test_data, train_label, test_label = t

# 線形回帰 (a*a)+(b*b)・・・
model = linear_model.LogisticRegression().fit(train_data, train_label)

# データの予測を行う
predict = model.predict(test_data)

verification = [[0 for i in range(ONE_TO_NINE)] for j in range(ONE_TO_NINE)]
for i in range(TEST_DATA):
    verification[int(predict[i])][int(test_label[i])] += 1

# テストデータの合計から正解率%の割り出し
print("ーテスト結果ー\n正解率:", model.score(test_data, test_label)*100, '%')

# 出力した正解率の詳細(0〜9の手書き予測の分布)
print("--------------------------------------------------------------------------------")
print("対象数値     ", end='')
for i in range(ONE_TO_NINE):
    print("   ",[i],end='',)
print()
for i in range(ONE_TO_NINE):
    print("数値予測", [i], end='')
    for j in range(ONE_TO_NINE):
        print("{0:7d}".format(verification[i][j]), end='')
    print()

# 学習した内容をLearn.modelに保存
externals.joblib.dump(model, "Learn.model")

3.2.2_自分が書いた数字をコンピュータに認識してもらう

 上記で学習させたら次に「Learn.model」というデータを読み込み、任意の数字の形状をコンピュータに認識してもらいましょう。動作結果は「④_動作確認」に載せています。

import numpy
from PIL import Image
from sklearn import externals

# 画像サイズ
SIZE = 28
# 読み込む画像名
test_image = "./3.png"

# 予測モデルをシリアライズ・メモリ並列処理を実行
model = externals.joblib.load("Learn.model")

# 画像を読み込み、カラーをグレースケールに(convert("L"))
image = Image.open(test_image).convert("L")

# 読み込んだ画像の大きさを変換
image = image.resize((SIZE, SIZE))

# 配列の長さの設定・配列を一次元化
test_data = [numpy.array(image).flatten()]

# 機械学習から得たデータの予測を取得
predict = model.predict(test_data)
print("数値予測:{0}".format(int(predict[0])))

# 視覚化
for y in range(SIZE):
    for x in range(SIZE):
        # 左寄せ、中央寄せ
        print("{0:3d}".format(test_data[0][x+y*SIZE]), end='')
    print()

④_動作確認

 「3.2.1_"0〜9"までの数字の形を学習させる」で機械学習させた結果が以下のようになります。
縦に並ぶ「数値予測[n]」はコンピュータが試験用データ1000個分を分析した内訳です。そして、横に並ぶ「[0]〜[9]」は今回分析された実際の数字です。今回の学習では85.9%の数字を正確に分析できたようです。試験用データは1000個ですので、859個を正確に分析してくれたことになります。最も正解数が多かった数字は「1」ですね。一本線という他の数字にはない特徴があるため、コンピュータは認識しやすかったと考察できます。反対に「8」は曲線が他の数字よりも特徴的なため、他の数字と混合しやすかったと言えます。個人的に気になるのは「7」という数字が「9」という数字と誤認した数が多いことです。画像サンプルが悪かったのか、学習させるデータ数が数字によってバラツキがあるせいなのか不明ですが、少し不思議な結果になりました。

image-20190211132238841.png

 「3.2.2_自分が書いた数字をコンピュータに認識してもらう」で認識してもらう数字はiPadのメモ帳で「3」と書いてスクリーンショットで撮った以下の画像になります。

image-20190211133416830.png

 そして認識結果がこちら、「数値予測」が実際にコンピュータが画像から認識した数字になります。さらに「3」という画像がどのように読み込まれたかを人間が視覚的にわかるように"0〜255"までの数字で画像内の数字を表現しています。色が黒に近いほど"0"に近く、白に近いほど"255"に近づきます。今回読み込んだ「3」という数値は正確に認識できたことが以下の画像からわかります。

image-20190211132328383.png

⑤_まとめ

 数字の形状を認識してもらうには多くのデータを用意して学習しなければなりません。今回は10000個の手書きの数字の形状を学習させましたが、それでも形状の似ている数字では誤認してしまうなど精度の問題は課題として残っています。
 Pythonはライブラリやモジュールが豊富に用意されているため、誰でも簡単に高度なコーディングを行うことができます。システム開発などの「ものづくり」でモジュールを利用すれば、自身が創造したいものを実現することも比較的容易にできます。今回ご紹介したライブラリ「scikit-learn」では、数字の形状を認識してもらう機械学習以外に様々な用途で使用されています。ご興味がございましたら是非、お調べ下さいませ。

⑥参考

・画像サンプルデータ取得場所
http://yann.lecun.com/exdb/mnist/
・【機械学習初心者向け】ロジスティック回帰で手書き文字認識【機械学習の実装】
https://blog.aidemy.net/entry/2017/07/11/214635


 コメント
[登録] 2019/02/18 19:16 [パンダさん]
[更新] 2019/02/19 9:59 [パンダさん]
icon

初回投稿がこのクオリティって。。。
おそろしい新人が来ましたね。

Mutaさんありがとう。いい記事だと思います。
内容のコメントは他の方に譲るとして、
体裁をマークダウン駆使して見やすくしてほしいかな。

見出しとか段落分けとか強調とか。
ソースは画像よりもマークダウン記法を用いた方が良い。
あと記事自体にタグ付けとくと検索するとき助かる。

Qiitaの記事みたく読みやすくなるからいろいろやってみて。


 いいね! × 1  
非表示
[登録] 2019/02/18 20:18 [Koichi.Muta]
icon

ご指摘ありがとうございます。
初めてのことで不慣れなことも多くご不便をおかけいたしました。
後日、少しずつ記事を改良してまいります。
最後に読んでいただきありがとうございました。


 いいね! × 0  
非表示
[登録] 2019/02/18 21:07 [Koichi.Muta]
icon

記事を編集いたしました。
マークダウンは使いこなすとかなり便利ですね。。。
だいぶ見やすくなったと思います。


 いいね! × 0  
非表示
[登録] 2019/02/19 9:28 [katano]
[更新] 2019/02/19 12:58 [katano]
icon

見た瞬間、更新したなーとわかるくらい見やすくなりました。仕事が早い!とても良いです! :blush:


 いいね! × 0  
非表示



 コメント追加