コンピューターシステム株式会社

技術BLOG

Pythonで数字画像認識AIを作成してみた

言語 2021/08/13 松山担当

こんにちは。松山本社のTomoです。

今回はPythonで数字画像認識AIを作成してみましたので、サンプルコードを交えながらニューラルネットワークを用いたAIの基礎を紹介したいと思います。

はじめに

近年、AIは様々なところで利用されています。

例を挙げれば

  • 自動運転技術
  • 株価予測
  • 病気の診断
  • 翻訳
  • 不良品検出

とキリがありませんね。

これらのAIを作成するのに重要なのが機械学習となりますが、その中でも代表的なものにニューラルネットワークがあります。

ニューラルネットワークとは、機械学習の手法の一つで人間の脳の仕組みをコンピュータ上で再現することを目的に作られた数理モデルになります。

信号の受け渡しによりニューロンが記憶や学習することと同様なことをコンピュータ上で行うことで、問題解決するのに利用されています。

今回は代表的な機械学習であるニューラルネットワークを用いた数字画像認識AIを紹介したいと思います。

開発環境

今回使用するPCのスペックとツールのバージョンは以下になります。

OS Windows 10 Professional 20H2
CPU Intel(R) Core(TM) i5-4310M CPU @ 2.70GHz
メモリ 8GB
グラフィックス オンボード
Anaconda 2021.05 (64bit)
Python 3.88

基本的な数字画像認識AIのコード紹介

ここからは数字画像認識AIのコードについて紹介したいと思います。

今回、AIに対して機械学習を行わせたり、機械学習よって生成された学習済みモデルを評価したりする際にMNISTデータという手書き数字画像のデータセットを用います。

まずは、以下のコードを実行して、MNISTデータの一部を表示させてみましょう。

# 手書き数字のデータセット取得と手書きデータの表示
from sklearn.datasets import fetch_openml
import matplotlib.pyplot as plt
import math
import numpy as np

# MNISTデータを取得してnumpyの配列型に変換
mnist_x, mnist_y = fetch_openml('mnist_784', version=1, data_home="sklearn_MNIST_data", return_X_y=True)
list_mnist_x = np.array(mnist_x)
list_mnist_y = np.array(mnist_y)

# 全部のデータを表示すると時間がかかるので
# 最初の4データのみ表示
digits = list_mnist_x[0:4]

# 手書き数字(8×8ピクセル)を表示
plt.set_cmap("binary")
fig = plt.figure()
row_and_col = math.ceil(math.sqrt(len(digits)))

# 1データずつ取得して手書き数字データを表示
for i,item in enumerate(digits):
    ax = fig.add_subplot(row_and_col,row_and_col,i + 1) # 表示位置設定
    ax.imshow(digits[i].reshape(28,28)) # 手書き数字データを表示

プログラムを実行すると、以下のように手書き数字が表示されます。

次は実際に数字画像認識を行ってみましょう。

機械学習にscikit-learnライブラリのMLPClassifierクラスを使って、以下の図のようなニューラルネットワークを構築します。

サンプルプログラムは以下になります。

AIって難しいと思われがちですが、機械学習のライブラリを使うと基礎的なものであれば、20行程度のコードで作成できちゃうんです!

# 数字画像認識AI
import numpy as np
from sklearn.neural_network import MLPClassifier
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# MNISTデータを取得してnumpyの配列型に変換
mnist_x, mnist_y = fetch_openml('mnist_784', version=1, data_home="sklearn_MNIST_data", return_X_y=True)
list_mnist_x = np.array(mnist_x)
list_mnist_y = np.array(mnist_y)

# 訓練用データとテストデータに分ける
data_train, data_test, target_train, target_test = train_test_split(list_mnist_x, list_mnist_y, random_state=43)

# 学習効率を上げるため、値の範囲を0~255から0~1となるように変換する
data_train /= 255
data_test /= 255

# ニューラルネットワークによるクラス分類を行う
# MLPClassifierクラスを使って、隠れ層1(ノード数:100)で
# ニューラルネットワークを構築する
# 今回使用するマシンのスペックの考慮や少しでも変化が分かりやすくするために
# 学習回数の最大値(max_iter)を10としている
clf = MLPClassifier(hidden_layer_sizes=(100,), verbose=True, max_iter=10, random_state=43)

# 訓練用データを使って学習させる
clf.fit(data_train, target_train)

# テストデータを使って数字画像認識を行う
predict = clf.predict(data_test)

# 正解率出力
print(clf.score(data_test, target_test))

実行すると、次のような結果が出力されます。

学習途中の進捗として損失(loss)が出力されます。損失とは正答値と予測値のずれの大きさを表します。

学習を繰り返すことによって、正答値と予測値のずれが小さくなっていくことが分かると思います。

そして、最後に正解率が割合で表示されますので、このサンプルプログラムでの正解率は97.07%となります。

認識精度向上のアプローチ

認識精度を上げる手法には、

  • 訓練用データの学習回数を増やす。
  • ニューラルネットワークの学習モデルを複雑にする。

があります。

それぞれの手法をサンプルコードを交えつつ、紹介していきます。

一つ目の訓練用データの学習回数を増やす手法はニューラルネットワークのパラメータであるmax_iterを増加させることで可能になります。

ただし、学習回数を増やしすぎると、後述の過学習になってしまい認識精度が落ちることがあるので注意が必要です。

# 数字画像認識AI(学習回数増)
import numpy as np
from sklearn.neural_network import MLPClassifier
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# MNISTデータを取得してnumpyの配列型に変換
mnist_x, mnist_y = fetch_openml('mnist_784', version=1, data_home="sklearn_MNIST_data", return_X_y=True)
list_mnist_x = np.array(mnist_x)
list_mnist_y = np.array(mnist_y)

# 訓練用データとテストデータに分ける
data_train, data_test, target_train, target_test = train_test_split(list_mnist_x, list_mnist_y, random_state=43)

# 学習効率を上げるため、値の範囲を0~255から0~1となるように変換する
data_train /= 255
data_test /= 255

# ニューラルネットワークによるクラス分類を行う
# MLPClassifierクラスを使って、隠れ層1(ノード数:100)で
# ニューラルネットワークを構築する。
# max_iterを変更して、学習回数を10から25に増やす
clf = MLPClassifier(hidden_layer_sizes=(100,), verbose=True, max_iter=25, random_state=43)

# 訓練用データを使って学習させる
clf.fit(data_train, target_train)

# テストデータを使って数字画像認識を行う
predict = clf.predict(data_test)

# 正解率出力
print(clf.score(data_test, target_test))

実行結果は以下になります。

正解率がさらに上がっていることが確認できますね。

二つ目のニューラルネットワークの学習モデルを複雑にする手法ですが、隠れ層の数を増やしたり、隠れ層に含まれるノード数を増やしたり等で複雑なニューラルネットワークを構築することになります。

ただし、こちらも学習モデルを複雑にしすぎると、後述の過学習になりやすくなり、認識精度が落ちることがあるので注意が必要です。

# 数字画像認識AI(学習モデルの複雑化)
import numpy as np
from sklearn.neural_network import MLPClassifier
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# MNISTデータを取得してnumpyの配列型に変換
mnist_x, mnist_y = fetch_openml('mnist_784', version=1, data_home="sklearn_MNIST_data", return_X_y=True)
list_mnist_x = np.array(mnist_x)
list_mnist_y = np.array(mnist_y)

# 訓練用データとテストデータに分ける
data_train, data_test, target_train, target_test = train_test_split(list_mnist_x, list_mnist_y, random_state=43)

# 学習効率を上げるため、値の範囲を0~255から0~1となるように変換する
data_train /= 255
data_test /= 255

# ニューラルネットワークによるクラス分類を行う
# ニューラルネットワークの構成を複雑にするため
# MLPClassifierクラスを使って、隠れ層(ノード数:100)を3層用意する構成で
# ニューラルネットワークを構築する。
clf = MLPClassifier(hidden_layer_sizes=(100, 100, 100), verbose=True, max_iter=10, random_state=43)

# 訓練用データを使って学習させる
clf.fit(data_train, target_train)

# テストデータを使って数字画像認識を行う
predict = clf.predict(data_test)

# 正解率出力
print(clf.score(data_test, target_test))

実行結果は以下になります。

こちらも正解率がさらに上がっていることが確認できますね。

AI実装時に起きやすい問題と改善策

AI実装時に起きやすい問題として挙げられるものに「過学習」があります。

訓練用データに対して高い認識精度を出しても、実際のデータで認識精度が落ちてしまえば意味がありませんね。

機械学習によって作成された学習モデルには汎化性能が求められます。

このように訓練用データに最適化してしまうことを過学習と呼びます。

これまでに紹介したサンプルコードでは学習の進捗を表示してきましたが、ここで表示されるlossの値が学習を繰り返すことで0に近づくほど訓練用データに最適化していることになります。

実際に過学習が起きている状態を確認しましょう。

# 数字画像認識AI(学習回数増・過学習未対策)
import numpy as np
from sklearn.neural_network import MLPClassifier
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# MNISTデータを取得してnumpyの配列型に変換
mnist_x, mnist_y = fetch_openml('mnist_784', version=1, data_home="sklearn_MNIST_data", return_X_y=True)
list_mnist_x = np.array(mnist_x)
list_mnist_y = np.array(mnist_y)

# 訓練用データとテストデータに分ける
data_train, data_test, target_train, target_test = train_test_split(list_mnist_x, list_mnist_y, random_state=43)

# 学習効率を上げるため、値の範囲を0~255から0~1となるように変換する
data_train /= 255
data_test /= 255

# ニューラルネットワークによるクラス分類を行う
# MLPClassifierクラスを使って、隠れ層1(ノード数:100)で
# ニューラルネットワークを構築する。
# max_iterを変更して、学習回数を10から50に増やす
clf = MLPClassifier(hidden_layer_sizes=(100,), verbose=True, max_iter=50, random_state=43)

# 訓練用データを使って学習させる
clf.fit(data_train, target_train)

# テストデータを使って数字画像認識を行う
predict = clf.predict(data_test)

# 正解率出力
print(clf.score(data_test, target_test))

実行結果は以下になります。

lossの値が0に近づいていることから訓練用データへの最適化が進んでおり、正解率が下がっていることも確認できます。

過学習を防止する手法の一つにWeightDecayがあります。

ニューラルネットワークの学習の過程で正答値と予測値のずれが小さくなるように各種パラメータを

調整しますが、そのパラメータの一つであるWeightの値が大きくなるのを抑制することで

訓練用データに最適化するのを防止する手法になります。

MLPClassifierクラスの設定でalphaの値を増加させると、Weightの値が大きくなるのを抑制することが出来ます。

以下のWeightDecayの手法を利用したコードを実行して、対策の有無による違いを確認しましょう。

# 数字画像認識AI(学習回数増・過学習対策)
import numpy as np
from sklearn.neural_network import MLPClassifier
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# MNISTデータを取得してnumpyの配列型に変換
mnist_x, mnist_y = fetch_openml('mnist_784', version=1, data_home="sklearn_MNIST_data", return_X_y=True)
list_mnist_x = np.array(mnist_x)
list_mnist_y = np.array(mnist_y)

# 訓練用データとテストデータに分ける
data_train, data_test, target_train, target_test = train_test_split(list_mnist_x, list_mnist_y, random_state=43)

# 学習効率を上げるため、値の範囲を0~255から0~1となるように変換する
data_train /= 255
data_test /= 255

# ニューラルネットワークによるクラス分類を行う
# MLPClassifierクラスを使って、隠れ層1(ノード数:100)で
# ニューラルネットワークを構築する。
# max_iterを変更して、学習回数を10から50に増やす
# alphaをデフォルト値の0.0001から0.01に変更する
clf = MLPClassifier(hidden_layer_sizes=(100,), verbose=True, max_iter=50, random_state=43, alpha=0.01)

# 訓練用データを使って学習させる
clf.fit(data_train, target_train)

# テストデータを使って数字画像認識を行う
predict = clf.predict(data_test)

# 正解率出力
print(clf.score(data_test, target_test))

実行結果は以下になります。

lossの値が対策なしと比べると大きくなっており、正解率が悪くなる現象も起きていませんね。

使用する機械学習ライブラリによっては利用できない場合があるかもしれませんが、他にも過学習の防止には

  • Dropout
  • Batch Nomalization

といった手法がありますので、興味がある方は調べていただければと思います。

最後に

ここまでAIの基礎として数字画像認識AIを作成しましたが、機械学習ライブラリが充実してきたこともあり、AIの作成はそれほど難しくないと分かったと思います。

また、画像認識以外のAIついても考え方は同じなので、興味がある方は他の分野のAIについても調べてみたり、実際にAIを作成してみてはいかがでしょうか?

以上で終わりとなります。

最後までお付き合いいただき、ありがとうございました。