Posted on

【S2お茶会】ニューラルネットで遊んでみる(6/4)

s2お茶会
今回は弊社の荒川が「ニューラルネットで遊んでみる」というテーマで話しました。

是非ともDeep Learningでお困りのことがあれば弊社にご依頼・お問い合わせください。

ニューラルネットで遊んでみる


ニューラルネットを使って、野鳥の画像を入力すると、その種類を推測させるということをもう何年もやっているのですが、ようやく勉強の成果が出てきました。今回はその話をしようと思います。

ニューラルネット


まず初めにニューラルネットが何なのかを説明します。

ニューラルネットの基本になるのがニューロンモデルです。
ニューロンモデルとは、神経細胞を模した多変数の引数を持つ関数のことです。
ただ、関数と言っても内部にいくつかのパラメータを持っていて、後から形を変えることができるようになっています。
全てが違う関数なので、引数に iがついて、区別できるようにしてあります。




このニューロンをたくさん用意して二次元状に束ねて層を作り、層の間を結線します。
一段目(左)のニューロンに入る線のことを入力と言い、段ごとに関数の値を計算してその値をどんどん次へと渡していき、最終段(右)で出力されます。これがニューラルネットです。




ニューラルネットは、入力に対して望ましい出力を教えてあげると、教えてあげたことを関数の形にフィードバックしていって段々と正解が出せるように成長していきます。この過程を「学習」とか「訓練」と言ったりします。この学習のフェイズを経なければニューラルネットは使い物になりません。

入力のところに鳥の写真を持ってきて、ビットマップの値(二次元配列)を渡します。それを計算していくということをやっているのですが、あらかじめ最終段で出力の各線に推測の結果の項目(この線はカワセミだとかキジバトだとか)の割り当てをしておきます。結果的に画像右側のような数値が出たとして、この中で一番大きい値がニューラルネットの答えだと考えるわけです。この場合だとコゲラですね。


Deep Learning


層の数が 4 層以上のニューラルネットを扱うことを Deep Learning(深層学習)と言います。
後でまた出てきますが、今回利用するニューラルネットの ResNet-50 は層が50層からになっていますが、精度が高ければ高いほど良いと皆考えるので、最近は数百層以上になるものも少なくありません。

先程の図を見てもらったら分かる通り、計算量が膨大です。
CPU は基本的に一つの演算器しかないため、どうしても計算をループして処理する必要があります。それに対してGPU は多数の演算器を並列に動かせるので、これをうまく使えば CPU でやるよりもずっと速く計算することができるのです。
なのでGPUをメインに使っていきます。

データ入力について


ある画像が何の画像なのか」これを教師データというのですが、Deep Learning には教師データが大量に必要です。データが少ないと正しい推測をしてくれません。今回使った ResNet-50 は100万枚を超える画像を使って学習させてあります。とは言っても我々が用意できる画像は実際のところ100万枚もありません。そこで、データが少ない場合でも対処できる方法というのがいくつかあります。


データの水増し(data augmentation)

入力画像をそのまま登録するだけじゃなくて、一部を拡大・縮小したり、反転させる、ノイズを加えるなどして画像を加工し、枚数を増やします。
意味がないんじゃないかと思われるかもしれませんが、ニューラルネットはデータが増えたと認識して学習してくれるようになっています。


転移学習(transfer learning)

別の画像で別の分類をするように作られた学習済みのニューラルネットがあったとして、それの出力層だけを付け替えて、最終段の出力だけを変数にして学習させるという手法です。


ファインチューニング(fine tuning)

既に学習済みのニューラルネットの出力層だけを付け替え、全ての層を対象にパラメータを調整する方法です。


転移学習とファインチューニングは学習させる対象の層が違うだけで、どちらも既存のネットワークを全然違うものにも応用してしまおうという強引な方法です。これが意外とうまくいきます。

何故うまくいくのか。脳の画像処理を考えてもらえばわかるのですが、何かを見た時、初めに網膜で光が電気信号に変換され、視神経を伝わって脳に入力されます。それが後頭部の一次視覚野に到達し、基本的な図形として認識されます。そこからは脳の前方に向かって信号処理が進んでいくのですが、より前方の領域で処理されるごとに徐々に複雑な形を認識していきます。
実は、ニューラルネットに学習させる時も同じ構造で認識されるようになっていて、入力に近い層(下位)ほど基本的な図形に反応するニューロンになっているのです。したがって、下位の層はある意味汎用的なため、他の目的にも使用することができます

ちなみに、Deep Learningには色々なプラットフォームが提案されており現在の主流は、


の二つなのですが、今回は Caffe の発展系である PyTorch を使いました。

名前からも何となく想像できるように PyTorch は Python をベースに作られています。
Python 由来で主要なライブラリは C / C++ / Fortran で書かれており処理が速く、さらに Jupyter Notebook 上でコードの編集と評価が対話的に行え、試行錯誤するのに非常に便利です。

ニューラルネットを使ってみる


PyTorch を使ってニューラルネットを構築する前に使用する学習済みモデルを選びます。
こちらのモデルの比較表は、左に行くほど精度が高く、上に行くほど処理が重いというグラフなのですが、今回はコストパフォーマンスの良さそうな ResNet-50 という50層からなる学習済みのデータを使用しました。
これを出発点に鳥を認識させてみましょう。


(何故鳥なのかは、私が鳥の写真を撮るのが趣味でたくさんのデータを保有しているから都合がいいというだけです。)


6,310枚の画像に、それぞれ何の鳥なのかというラベルを全部で77種類つけて教師データを作成しました。




ニューラルネットの構築については PyTorch のチュートリアルを参照してください。ほとんど例そのままで簡単に構築が可能です。

下のコードもほぼチュートリアルから持ってきたものですが、1 行目で学習済みの ResNet-50 をロードして、2 行目で最終層を取り出し、5 行目で最終層を77種類の出力を持つレイヤーに入れ替えています。
これを実行すると、50層分の計算と学習が行われます。




計算するとグラフのような学習結果が出ました。
横軸のエポックは、用意したデータを全て使って学習をした回数を表しています。25回データを参照したということですね。
青のライン学習の結果の正答率オレンジのラインニューラルネットにとって初めて見る画像に対しての正答率を表しています。後者の正答率を伸ばしたいのですがなかなかうまくいきません。

これを計算するのに大体90分かかりました。




こうして学習が完了したニューラルネットに、ネットから適当に拾ってきたドバトの画像を渡して推測させてみました。下がその結果です。




第一候補がキジバトになっていてドバトは二番目です。グラフの正答率を見たら 95% を超えているのに思ったよりも精度が良くありません。どうすればもっと精度が上がるのか。色々試してみました。

まず試したのは画像のクリッピングです。
デジカメで撮った写真を学習に使っているので、アスペクト比が 3 : 2 になっているのですが、ニューラルネットに入力するのは 1 : 1 のものじゃないといけないという制約があります。そこで全ての画像から鳥の部分だけを 1 : 1 の正方形で取り出すということをやってみました。6,310 枚の画像をダブルチェックまでやったので実質 12,620 枚分クリッピングしたのですが、本当に大変でした…。しかし効果があったのかどうかは微妙なところです…。

あとはひたすら学習率をいじってみたり、提案されている最適化アルゴリズムを試してみたりと試行錯誤したのですが、どうやっても 95.36% という壁が越えられません。

そうして最後にたどり着いたのがメビウス変換です。

メビウス変換と試行錯誤


色々調べたところ、どうやら Deep Learning の重鎮であるスタンフォード大学のアンドリュー・ン氏がメビウス変換はニューラルネットの正答率に効果的だと論文に書いていることがわかりました。そんな人が言っているのなら間違いないと思って試してみることに。

幸いなことに、その論文の結果を PyTorch で使えるようにしたルーチンが github に公開されていたので一部修正して使わせてもらうことにしました。

メビウス変換とは、複素数の単純な有理変換です。これを画像に適用すると、回転させたり、歪ませたり、拡大したり、と色々なパターンの変換となります。これをランダムに選択して、データの水増しをします

ところがメビウス変換と Python の都合上、GPU は使えず CPU での計算しかできなかったため460分もかかってしまったうえ、どうしても正答率が 95.33% までしかいきません。しかも ResNet-50 ではいい数値が出なかったため EfficientNet という別のモデルを使うことに。




ただここで、学習時の正答率の上がり具合が 99.12% までしか上がっていないということに着目しました。Data Augmentation の手法を変更すればもっと伸び代があるかもしれない。とりあえず一旦メビウス変換を使うのはやめて、EfficientNet はそのままで、Data Augmentation の手法を変更し、継続して学習を続けさせることにしました。

その結果なんと、 正答率が 95.83% に!
元の 95.36% からほとんど数値は変化していないように思うかもしれませんが、数値以上に性能に随分差が出ています。



試しにさっきのドバトと同じ画像を今のニューラルネットに入力すると、99.95% ドバトだと推測する結果が出ました。学習データの少ない鳥の画像を入力しても非常に高い数値で正解が。ようやく質的にいいものが出来上がりました。





試行錯誤の末にやっと感覚がわかってきたので、仕事として今後、Deep Learning を使ったものを受けていければと思っています。その時は是非、弊社にご依頼・お問い合わせください。