Sansan Builders Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

歴史をたどってディープラーニングを学ぶ 第八回 データオーギュメンテーションを実装して学ぶ

こんにちは、ニューラルネット老人こと糟谷勇児です。

今回はデータオーギュメンテーション(Data Augmentation)を見ていきます。データオーギュメンテーションは学習データを人工的に変えて水増しして学習させる技法です。画像なら縮小や回転、左右反転、平行移動などが行われます。今回はニューラルネット自体をいじらないので簡単そうですね。(実際はだいぶハマった)

前回は、ソフトマックス層を実装しました。ソフトマックスはドラクエに例えるとチャモロという話をしましたが、データオーギュメンテーションはドラクエに例えると「ゲントの杖」ぐらいの期待感はあります。

というのも、今までの誤認識をした結果を見ると明らかに失敗しているものに傾向があるからです。

小さいもの
f:id:kasuya_ug:20200703094122j:plainf:id:kasuya_ug:20200703094124j:plainf:id:kasuya_ug:20200703094126j:plainf:id:kasuya_ug:20200703094130j:plainf:id:kasuya_ug:20200703094133j:plainf:id:kasuya_ug:20200703094136j:plain
下のほうにあるもの
f:id:kasuya_ug:20200703094302j:plainf:id:kasuya_ug:20200703094306j:plainf:id:kasuya_ug:20200703094309j:plainf:id:kasuya_ug:20200703094311j:plain

アップのもの
f:id:kasuya_ug:20200703094443j:plainf:id:kasuya_ug:20200703094446j:plainf:id:kasuya_ug:20200703094448j:plainf:id:kasuya_ug:20200703094451j:plainf:id:kasuya_ug:20200703094454j:plain

低コントラストなもの
f:id:kasuya_ug:20200703094625j:plainf:id:kasuya_ug:20200703094629j:plainf:id:kasuya_ug:20200703094632j:plainf:id:kasuya_ug:20200703094634j:plainf:id:kasuya_ug:20200703094637j:plain

これらを認識するのに、画像を拡大・縮小したり、平行移動したり、低コントラストにしたりした画像を増やしていくとよさそうです。


でもちょっともやもやしちゃう

私はずっと実務として画像処理をやってきたので、やっぱり人工的にデータを増やすのってなんかもやもやしちゃうんですよね。
小さい飛行機が認識できないとしたら、人工的に画像を作る前にやることあるだろみたいな。

例えば空港監視で使うために飛行機を見分ける画像処理だとしたら、どの程度離れた飛行機まで認識するのかお客さんと仕様を合わせて、結構離れたものまで認識するなら、そういうサンプルも採取する必要があるでしょう。遠いので小さく見えるのと、縮小して小さいのは、レンズのゆがみやノイズの問題があるので違う話です。また、一つのモデルにこだわらなくても小さいものを認識するモデルと、大きいものを認識するモデルに分けるとか、認識する側を拡大縮小して何回も認識処理をかけるとかいろいろあると思うんですよね。

まあでもそんなこと言ってると時代に取り残されるので、早速やっていきましょう。

人工的な学習画像の生成

今回は、ちょっと拡大したものとちょっと縮小したものを用意します。拡大のほうはいいですが、縮小のほうは画像がない部分が出てきてしまいます。ここはOpenCvのInpaintで補完してやることにします。
f:id:kasuya_ug:20200708213959p:plain
また、低コントラストのものも用意します。こちらはLab色空間に変換したのち、輝度成分(L平面)の標準偏差を取得し、ノーマライズした後、標準偏差が小さくなるようにして元のスケールに戻します。その後、RGB色空間に戻してやります。

今回は4500枚ずつ学習データ使っているのですが、全件に対してランダムに0.7~1.3倍までの画像を作ります。その後、4枚に1枚は低コントラスト処理をかけてみました。この辺の設計が難しいですね。人工画像をもとの画像と同じ数だけ増やすのがいいのか、半分ぐらいにしておくのがいいのか。増やせば増やすほど1エポックにかかる時間は増すのと、実際には認識対象も多くは普通のサイズの画像なのであまり小さいのや大きいのを増やしても逆に精度が下がってしまいます。
かといって、元画像の半分の数ということにすると、画像の中で拡大前と後で二回学習されるものとそうでないものが出てきます。
これが、バイアスにつながらないか心配になります。
この辺りはいろいろやって確かめていきたいですね。

ニューラルネットの並列化

さて、ここで問題が出てきました。カラー化したり、ソフトマックスの導入でバッチサイズが小さいと学習がうまくいかなくなったり、少しずつ学習に時間がかかるようになってきて、今回2倍の時間がかかるようになったので、並列化して速くしないといけないくなってきました。

今回、ニューラルネットは状態を持ったクラスとして作っているので並列化はなかなか難しいです。
今回は複数の同じニューラルネットを用意し、バッチごとに並列化して学習して統合するようにしました。

学習のバッチサイズが256だとすると、それを16分割して16個のニューラルネットにそれぞれ学習して、更新する重みを統合します。統合したニューラルネットをまた16個コピーして学習することを繰り返します。
f:id:kasuya_ug:20200707222222p:plain
これで、4コアあるPCで3倍以上の速度になりました。

実験

今回も実際にやっていきます。
ネットワーク構造は前回に引き続き以下のような感じです。


  1. 5*5のコンボリューション層 フィルタ数15
  2. 3*3のマックスプーリング層 ストライド2
  3. 5*5のコンボリューション層 フィルタ数10
  4. 3*3のマックスプーリング層 ストライド2
  5. 全結合層 ReLU ニューロン数 50
  6. 全結合層 ReLU ニューロン数 30
  7. 全結合層 ソフトマックス ニューロン数 2


水増ししてできた画像はこんな感じ。左が元画像、右がサイズを変更した画像です。


f:id:kasuya_ug:20200707215112j:plainf:id:kasuya_ug:20200707215116j:plainf:id:kasuya_ug:20200707215119j:plainf:id:kasuya_ug:20200707215122j:plainf:id:kasuya_ug:20200707215125j:plainf:id:kasuya_ug:20200707215127j:plainf:id:kasuya_ug:20200707215130j:plainf:id:kasuya_ug:20200707215133j:plainf:id:kasuya_ug:20200707215136j:plainf:id:kasuya_ug:20200707215139j:plainf:id:kasuya_ug:20200707215142j:plainf:id:kasuya_ug:20200707215145j:plainf:id:kasuya_ug:20200707215148j:plainf:id:kasuya_ug:20200707215151j:plain
こちらが縮小画像

f:id:kasuya_ug:20200707215321j:plainf:id:kasuya_ug:20200707215323j:plainf:id:kasuya_ug:20200707215326j:plainf:id:kasuya_ug:20200707215328j:plainf:id:kasuya_ug:20200707215330j:plainf:id:kasuya_ug:20200707215334j:plainf:id:kasuya_ug:20200707215337j:plainf:id:kasuya_ug:20200707215340j:plainf:id:kasuya_ug:20200707215343j:plainf:id:kasuya_ug:20200707215345j:plainf:id:kasuya_ug:20200707215348j:plainf:id:kasuya_ug:20200707215351j:plainf:id:kasuya_ug:20200707215354j:plainf:id:kasuya_ug:20200707215357j:plain
こちらが拡大画像


これを学習した結果、ちょっと精度が上がりました。
前回88%まで行きましたが、今回は88.5%まで行きました。

そんなに簡単じゃないなーという話

実はこの結果を得るまでに左右反転を試したのですが精度変わらず、x軸y軸を反転させるのも試したのですが逆に精度が下がってしまいました。

その後いろいろ試したんですが90%を超えるような学習はできませんでした。
これ以上は結構難しいかもしれない・・・

もしかしたらもっと踏み込んで分析していく必要があるかもしれません。
例えば、船も飛行機も一律小さくしていましたが、小さいものは飛行機が多いんですよね。
飛行機は移動速度が速いので撮りたいと思ってもズームするのが難しいからでしょうか。
単純に空を飛んでいるから遠いのか。
なので飛行機は小さくするけど船はしないなどの工夫はあるかもしれません。

あるいはそもそも結構小さいデータも学習データに入っていてもう加味された上で今の結果なのかもしれません。
まあ、あんまり画像の分析によりすぎるとディープラーニングを学ぶ本題から外れそうなので今後の課題としておきます。

次回は

次回は気を取り直してついにAlexNetに挑戦していきたいと思います。
一筋縄ではいかないと思いますが頑張ってみます。



▼本シリーズのほかの記事はこちら

buildersbox.corp-sansan.com

© Sansan, Inc.