2回に分けてAIを使用した画像認識の解説を行いたいと思います。
画像認識といってもいくつかありますが、ここではCNNについての解説となります。
今回、CNNの処理の流れと概要、次回、TensorFlowを使い、今回の解説に沿ったプログラムを紹介します。

AIについての説明となると、微分だの回帰分析だのと理論を数式やグラフに置き換えたりする解説が多いですが、結局、我々プログラマがやりたいことは、画像に何が映っているかを判定するプログラムを作りたいだけです。
理論や数式を勉強したいわけではありません。
なので、今回はどういう手順で画像を判別しているかを手順で説明したいと思います。

画像認識の手順

画像認識の手順は以下のようになります。

AIについて調べるとよく見る単語が並んでます。
なんとなくわかるような気がしますが、具体的に何をしているのかわからないですよね。
以下で、それぞれの手順でどういったことが行われているか具体的に説明します。

これから説明するのは0~9の手書き文字のうち1つを読み込んで、それが0~9のどれかを判定する例です。
上記では4つしかありませんが、実際には結構長い手順になります。
これからひとつづつ説明しますが、最後まで読んだら全体像を見直してみると画像認識の全外的な概要がわかると思います。

画像の読み込み(入力⇒数値化)

以下の画像には手書きの数字が沢山ありますが、その中の左端の画像を判定するとします。
まず、判定する画像(赤い枠)を読み込み、ドットごとに数値化します。
通常の画像の場合、1ドットにRGB(赤緑青)の3つの数値が含まれますので、多くの画像認識ではグレースケール(白黒)で明暗だけの数値に変換します。
下の例では白を0、黒を1とし、小数点で明暗を数値化しています。

ノード

ニューラルネットワークの説明でよく用いられる図ですが、具体的に何のことかよくわからないですよね。
○がノードで、畳み込みで次のノードが決まってということですが、わかるようでわかりません。
具体的に説明しますと、今回の画像認識の場合、上で作成した各ドットを数値化した値が「ノード」ということになります。

畳み込み(特徴抽出)

いよいよ、AIの本丸、畳み込みです。
畳み込みは簡単に言うと、こうすることです。

先ほど、画像を1ドットずつ数値化しました。
それを画像の特徴を保ったままノード数を減らす(特徴を抽出する)ことを「畳み込み」といっています。
では、具体的にどうやっているのでしょうか?

CNNでは以下のような方法で畳み込みを行っています。
まず、3×3のフィルタ(F1)を用意します。
今回の場合、中の値は適当(乱数)です。本来は抽出対象の特徴がわかりやすいものを使うべきかもしれませんが、実際のCNNでもわざわざフィルタを手で準備することはないと思うので乱数で十分です。
このフィルタを画像のマップの端から順に掛け算していきます。
掛け算といっても配列の掛け算となりますので、図のような計算となります。
フィルタとマップの一部の掛け算した結果に重み(W1)として乱数をかけて特徴マップに設定します。

この処理を少しずつ、ずらしながら繰り返して特徴マップを完成させます。
反対側の端まで行ったときにフィルタがはみ出してしまう場合は、0や1で埋めて処理します。
こうすることで特徴を抽出した新しいマップ(特徴マップ)を作成することができます。

さらに畳み込みを繰り返すことで特徴を濃縮することができます。

プーリング

プーリングとは特徴マップの圧縮のことです。
畳み込みだけでマップを小さくするには限界があるため、畳み込みの合間に圧縮処理を行います。
ただ、いきなりこう圧縮してしまうと特徴が失われてしまうので、畳み込み⇒圧縮⇒畳み込み⇒圧縮というように繰り返すことで、特徴を失わずに圧縮することができます。

圧縮の方法はいろいろありますが、今回はMaxプーリングを行います。
他にも全体平均プーリングなどがあります。
処理は非常に単純です。
特定のサイズごとの最大値を使って新しいマップを作成します。
下の例では4×4のノードのうち最大値を新しいマップに登録しています。
これを、先ほどの畳み込みと同じように対象ノードをずらしながら処理を繰り返して新しいマップを完成させます。

出力(全結合)

畳み込みとプーリングによって、特徴の抽出と圧縮を十分に行ったら最後にすべてのノードを結合します。
結合は、今まで2次元配列だった値を1次元にします。
やり方は、単純に1行目の後ろに2行目、2行目の後ろに3行目をつなぎ合わせるだけです。
こうすることで1次元の特徴配列が出来ます。

今回の目的は画像(特徴)が0~9のどれかを判定するというものですので、最後は10個の値にまとめ、そのうち1の値がもっとも大きいという結果にならないといけません。
先ほどの手順で1次元の特徴配列が出来ましたが、これを10個の値に特徴を残したまま変換する必要がありますが、これを行列の内積を使用して実現します。

行列の内積は以下のような計算となります。

(参考)http://www.geisya.or.jp/~mwm48961/kou2/matrix2.html

このように、次元の違う行列の内積をとると次元を小さいほうの次元に集約することができます。
これを利用し、先ほどの1次元配列に10×1次元配列数の配列の内積をとることで1×10の配列を作ることができます。

誤差伝播法

ついに、10個の値になりました。
これで0~9の判定が出来るかというとできません。
お分かりと思いますが、フィルタや重み付けなど乱数を使用して機械的に作っただけなので、出来上がった値に規則性があるはずがありません。
まして、どの枠が1だということがわかるはずがないのです。

そこで、上の図のように、どの枠がどの数字かを人間が勝手に決めます。
そして、各枠の正しい値を人間が指定します。
その値とCNNで生成した値の差を損失とか誤差として、それが小さくなるように重み付けに調整していきます。
下の図のように、各層で乱数を使用していた重み付けを誤差が少なくなる方向(プラス/マイナス)に調整します。これを「誤差伝搬法」といいます。

再度、畳み込み⇒プーリング⇒出力を実施して誤差を算出し、これを何度も繰り返すことで、人間が指定した値に近づけることを学習といっているのです。
また、他の画像でも同じように重み付けを調整して、同じような結果が出るようにします。
満足のいく重み付けが設定出来たら、すべての処理をまとめて学習モデルとしてファイルにまとめます。
これを利用することで他の画像でも0~9を判定できるということになるのです。

参考サイト

http://codecrafthouse.jp/p/2018/01/knowledge-distillation/

https://products.sint.co.jp/aisia/blog/vol1-16

http://gagbot.net/machine-learning/ml4

https://products.sint.co.jp/aisia/blog/vol1-5

https://www.youtube.com/watch?v=r8bbe273vEs