はじめに
本記事では今すぐに一度機械学習モデルを作ってみたいという方に向けて特に単純な機械学習モデルの作成方法を紹介することを目的としています。
本記事では機械学習の中でも教師あり学習についてのみ紹介します。
教師あり学習とはなにか
機械学習は教師あり学習と教師なし学習に大別できます。今回扱う教師あり学習とは問題と解答のセットで構成された教師データによって学習を行わせる方法です。
教師あり学習のより数学的な定義は、既知の問題
$$
\begin{align}
(x_i , y_i)
\end{align}
$$
というデータの組が$i=1,2, \cdots ,n$まで$n$個与えられたとき、未知の$x_{\mathrm{unknown}}$に対して対応する$y_{\mathrm{unknown}}$を予測することだと言えます。ここでは$x$を問題、$y$を解答と思ってみてください。機械学習の世界では$x$を説明変数、$y$を目的変数といいます。また、既知の問題と解答のデータセットのことを教師データといい、これがあるというのが教師あり学習の名前の由来です。
例えば画像を与えると写っているものが猫か犬かを見分けるような教師あり学習を考えてみましょう。この場合、教師データとして猫というラベルの付いた猫の画像(画像が説明変数、ラベルが目的変数)、犬というラベルの付いた犬の画像を十分な数用意する必要があります。そして、それらの教師データをモデルに読み込ませて学習(学習アルゴリズムの詳細はここでは触れません)を行い、最終的に未知の画像を与えたときに「これは猫だ」と識別できるようにさせるのです。
用意するもの
Pythonの実行環境を用意してください。特にノートブックの形式になっているものが扱いやすいと思います。すでに環境を用意してある方はお使いの環境で大丈夫です。
Pythonに触れたことのない方はGoogle Colabが最も手軽だと思います。Google Colabの導入は簡単です。お使いのGoogleアカウントのGoogle Driveから設定を行ってください。
XORを再現する
機械学習が本領を発揮するのは複雑な説明変数が与えられている際のクラスタリングですが、今回はとにかくモデルを作ることが目的なので、特に単純なXOR回路の結果を予測する機械学習モデルを作成していきます。論理回路の入力は$2 \times 2$の大きさを持つ二次元データ、出力は一次元データです。
XORは非常にシンプルですが、線形回帰では解けません。
①importしておくもの
TensorFlowと呼ばれるPythonの機械学習でよく使わるライブラリをあらかじめimportしておきます。Modelの作成にはKerasを使います。
import matplotlib.pyplot as plt
import numpy as np
from tensorflow import keras
from keras import layers
②教師データとテストデータ
x_train = x_test = np.array([[0, 0], [1, 0], [0, 1], [1, 1]])
y_train = y_test = np.array([0, 1, 1, 0])
教師データとテストデータ(今回は同じものですが一般には教師データとテストデータは重複していません)を用意します。シンプルなのでベタ書きです。
③Modelの設計
model = keras.models.Sequential()
Modelのオブジェクトの生成はシンプルです。ここからModelの具体的な設計を書いていきます。
# 入力層
model.add(layers.Dense(8, activation="relu", input_dim = 2))
# 全結合層
model.add(layers.Dense(8, activation="relu"))
# 出力層
model.add(layers.Dense(2, activation="softmax"))
入力層、全結合層、出力層を記述しました。入力データは二次元なのでinput_dim=2となります。全結合層の活性化関数には様々な選択肢があり、どれがいいという話は難しいのですが、計算量が少なく学習効率の高いReLU関数がおすすめです。出力層にはsoftmax関数を使います。
完成したModelの情報を表示したい時は次のように記述します。
model.summary()
今回の場合、Total params: 114と表示されると思います。これはModelのニューロン(厳密にはPerceptron)とそれらをつなぐ軸索の数の合計に対応しています。モデルの持つ変数(パラメータ)の数とも言いかえることができます。
④学習
作成したモデルに学習をさせます。記述はシンプルです。
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"])
history = model.fit(x_train, y_train, epochs=500, validation_data=(x_test, y_test))
test_loss, test_acc = model.evaluate(x_test, y_test)
ここでの重要な情報はオプティマイザにAdamを使っていること、500世代の学習を行っていることです。オプティマイザは他にも種類がありますが、現在最も人気があるのはAdamだろうと思います。
⑤テスト
本当に学習がうまく言ったかどうかを見るにはテストをしなければなりません。正答率と損失関数を表示してみましょう。これは厳密な言い方ではありませんが、損失関数とは予測と正解とのいわばズレを数値化したものです。したがって、学習がうまく進めば損失関数は減っていきます。
param =[["Accuracy", "accuracy", "val_accuracy"],["Loss", "loss", "val_loss"]]
plt.figure(figsize=(10, 5))
for i in range(2):
plt.subplot(1, 2, i + 1)
plt.title(f"{param[i][0]}")
plt.xlabel("Epoch")
plt.ylabel(param[i][2])
plt.plot(history.history[param[i][1]], "o-")
plt.plot(history.history[param[i][2]], "o-")
plt.legend(["train", "test"])
if i == 0:
plt.ylim([0, 1])
plt.show()
正答率が1になり、同時に損失関数の値が小さくなっていく様子がわかります。今回はテストデータと教師データが同じなので損失関数のグラフは重なっていますが、一般に重なるわけではありません。むしろこれは重ならないほうが普通です。
MNISTデータベースを使って手書き数字を判読する
すこしレベルアップして、ここからは手書き数字の判読に挑戦してみましょう。より複雑なモデルを扱っていきます。ここで用いるモデルは画像認識において威力を発揮する畳み込みニューラルネットワーク(CNN)と呼ばれるもので、中間層で畳み込みという操作を行います。
画像認識における畳み込みは画素をあえて下げるような層を通過させることに対応します。そう聞くと、むしろ精度が落ちそうですが、畳み込みによって文字情報のような画像の長距離秩序をモデルが学習できるようになります。
①importしておくもの
ここではMNISTという手書き数字のデータセットを使います。
import matplotlib.pyplot as plt
import numpy as np
import keras
from keras import layers
from keras.datasets import mnist
②教師データとテストデータ
教師データとテストデータを定義します。データセットには28px四方のモノクロ手書き数字とインデックスの組データが格納されています。教師データとテストデータは同じデータセットの異なるパートで、個々のデータそのものは重複していません。
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train / 255.0
x_test = x_test / 255.0
③Modelの設計
model = keras.Sequential()
# 2次元の入力を長さ784の1次元配列に変換(次元削減)
model.add(layers.Flatten(input_shape=(28, 28)))
# 28*28を14*14に圧縮
model.add(layers.Dense(128, activation='relu'))
# 数値なので出力は10種類
model.add(layers.Dense(10, activation='softmax'))
入力データは一辺28pxなのでinput_shape=(28, 28)と記述します。全結合層の活性化関数にはReLU関数を使います。出力層にはsoftmax関数を使います。入力層ではこの画像データを一次元配列に変換する次元削減という操作を行っています。
完成したModelの情報を表示したい時は次のように記述します。
model.summary()
今回の場合、Total params: 101,770と表示されると思います。先ほどのXORのモデルよりはるかにパラメータの数が多く、複雑なモデルであることが分かります。
④学習
作成したモデルに学習をさせます。
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
history = model.fit(x_train, y_train, epochs=10, validation_data=(x_test, y_test))
test_loss, test_acc = model.evaluate(x_test, y_test)
⑤テストⅠ
param =[["Accuracy", "accuracy", "val_accuracy"],["Loss", "loss", "val_loss"]]
plt.figure(figsize=(10, 5))
for i in range(2):
plt.subplot(1, 2, i + 1)
plt.title(f"{param[i][0]}")
plt.xlabel("Epoch")
plt.ylabel(param[i][2])
plt.plot(history.history[param[i][1]], "o-")
plt.plot(history.history[param[i][2]], "o-")
plt.legend(["train", "test"])
if i == 0:
plt.ylim([0, 1])
plt.show()
正答率が1に近づき、同時に損失関数の値が小さくなっていく様子がわかります。今回はテストデータと教師データが異なるので損失関数のグラフは重なっていません。教師データの損失関数が学習を重ねれば限りなく小さくなりますが、テストデータではどこかで頭打ちになります。
⑥テストⅡ
グラフだけ見てもなんとなくイメージがわかないと思うので、本当に手書き文字を判読できているのか、具体的なデータを抽出して表示してみました。
pre = model.predict(x_test)
fig = plt.figure(figsize=(12, 12))
for i in range(20):
plt.subplot(4, 5, i+1)
plt.xticks([])
plt.yticks([])
plt.imshow(x_test[i], cmap='Grays')
index = np.argmax(pre[i])
plt.title(f"{index}({pre[i][index]:.3f})")
plt.show()
結果はこのようになりました。
括弧内の数字はこのモデルが答えに対して持っている確信の程度を意味しています。数値が高いということはそれだけ自分の答えに自信を持っているということです。最終的な出力はsoftmax関数で離散的に、つまり一桁の整数に直されて出力されます。
数字を眺めると、かなり判読できている様子が分かります。ただ、どうやら一つだけ読み間違いをしているようですね……。
ソースコード
今回使用したソースコードはGithubにて公開しております。
まとめ
ここまで機械学習モデルの設計から、検証までをかなり大雑把ではありますが見てきました。XOR回路は機械学習でなくても簡単に作れるので、メリットは見えにくいかもしれません。しかし、ほとんど同じ方法で画像から数字を識別するようなモデルを作ることもできます。
数字を識別するときはモデルの入力を画像の大きさに合わせて増やし、中間層の深さを調整し、出力の種類を増やす必要があります。
機械学習の中でも中間層が深いモデルを使うものは特に深層学習(Deep Learning)と呼ばれています。また、画像認識では中間層で畳み込みと呼ばれる操作を行います。畳み込みを行ったニューラルネットワークを畳み込みニューラルネットワーク(CNN)といいます。
一見、中間層をひたすら大きくしていけば、いくらでもよいモデルを作ることができそうですが、それほど単純ではありません。モデルの設計を色々といじりながら実験を繰り返してみると面白いかも知れません。


