SE_BOKUのまとめノート的ブログ

SE_BOKUが知ってること・勉強したこと・考えたことetc

CNNの認識精度向上に学習データ(画像)の水増しは効果あり/ニューラルネットワーク

f:id:arakan_no_boku:20190326212937j:plain

目次

学習データの水増し

学習データが少ないと(1カテゴリあたり40枚~60枚)、学習精度があがらない。

これは、実際に体験しました。。

arakan-pgm-ai.hatenablog.com

じゃあ・・ということで、学習データの水増しをやります。

 

水増しするのはCaltech101のカラー画像データ

Caltech101の画像データが、1カテゴリあたり40枚~60枚程度しかありません。

学習データにするには少なすぎます。 

なんとか1カテゴリ当たり、1000枚程度は欲しいものです。 

でも、同じ画像をコピーして数だけ増やしても意味がありません。

それでは、回転させたり、反転させたり、画像補正をかけたりといった変更を加えて、少しずつ異なる画像を生成して、水増ししてみたらどうだろう。

そういう作戦です。

Neural Network Consoleにも「ImageAugmentation」レイヤーがあって、入力画像にランダムな変化を加えることで、バリエーションを作って同様の効果を得ようとするものがありますが、今回は、そういうスマートや方法ではなく、力業で画像を増やしてやってみます。

手作業で水増しする方法は面倒すぎる

最初は、手作業でやろうかとも思いました。

例えば、Ralphaなんかでも、変換後のファイル名規則を指定して、一括でグレイスケール化や回転などの処理を行うことができますから、地道に繰り返していけば、画像の水増しはできますから。

でも、すぐに根気がつきました(笑)。

とりあえず、後のプログラム用にリサイズだけしておきます。

f:id:arakan_no_boku:20190329235110j:plain

カラー画像の水増しをするpythonプログラム

やっぱり、「横着者はプログラムを書け。」です。

pythonプログラムで一括処理をします。 

前のステップで使うRalphaでリサイズだけしている前提で作業してますので、プログラム中でリサイズはしていません。

ソースはこんな感じです。

# -*- coding: utf-8 -*-
import os
from PIL import Image, ImageFilter

def main():
    data_dir_path = u"./out/"
    data_dir_path_in = u"./in/"
    file_list = os.listdir(r'./in/')

    for file_name in file_list:
        root, ext = os.path.splitext(file_name)
        if ext == u'.png' or u'.jpeg' or u'.jpg':
            img = Image.open(data_dir_path_in + '/' + file_name)
            tmp = img.transpose(Image.FLIP_LEFT_RIGHT)
            tmp.save(data_dir_path + '/' + root +'_r01.jpg')
            tmp = img.transpose(Image.FLIP_TOP_BOTTOM)
            tmp.save(data_dir_path + '/' + root +'_r02.jpg')
            tmp = img.transpose(Image.ROTATE_90)
            tmp.save(data_dir_path + '/' + root +'_r03.jpg')
            tmp = img.transpose(Image.ROTATE_180)
            tmp.save(data_dir_path + '/' + root +'_r04.jpg')
            tmp = img.transpose(Image.ROTATE_270)
            tmp.save(data_dir_path + '/' + root +'_r05.jpg')
            tmp = img.rotate(15)
            tmp.save(data_dir_path + '/' + root +'_r06.jpg')
            tmp = img.rotate(30)
            tmp.save(data_dir_path + '/' + root +'_r07.jpg')
            tmp = img.rotate(45)
            tmp.save(data_dir_path + '/' + root +'_r08.jpg')
            tmp = img.rotate(60)
            tmp.save(data_dir_path + '/' + root +'_r09.jpg')
            tmp = img.rotate(75)
            tmp.save(data_dir_path + '/' + root +'_r10.jpg')
            tmp = img.rotate(105)
            tmp.save(data_dir_path + '/' + root +'_r11.jpg')
            tmp = img.rotate(120)
            tmp.save(data_dir_path + '/' + root +'_r12.jpg')
            tmp = img.rotate(135)
            tmp.save(data_dir_path + '/' + root +'_r13.jpg')
            tmp = img.rotate(150)
            tmp.save(data_dir_path + '/' + root +'_r14.jpg')
            tmp = img.rotate(165)
            tmp.save(data_dir_path + '/' + root +'_r15.jpg')
            tmp = img.rotate(195)
            tmp.save(data_dir_path + '/' + root +'_r16.jpg')
            tmp = img.rotate(210)
            tmp.save(data_dir_path + '/' + root +'_r17.jpg')
            tmp = img.rotate(225)
            tmp.save(data_dir_path + '/' + root +'_r18.jpg')
            tmp = img.rotate(240)
            tmp.save(data_dir_path + '/' + root +'_r19.jpg')
            tmp = img.rotate(255)
            tmp.save(data_dir_path + '/' + root +'_r20.jpg')
            tmp = img.rotate(285)
            tmp.save(data_dir_path + '/' + root +'_r21.jpg')
            tmp = img.rotate(300)
            tmp.save(data_dir_path + '/' + root +'_r22.jpg')
            tmp = img.rotate(315)
            tmp.save(data_dir_path + '/' + root +'_r23.jpg')
            tmp = img.rotate(330)
            tmp.save(data_dir_path + '/' + root +'_r24.jpg')
            tmp = img.rotate(345)
            tmp.save(data_dir_path + '/' + root +'_r25.jpg')
            tmp = img.filter(ImageFilter.FIND_EDGES)
            tmp.save(data_dir_path + '/' + root +'_r26.jpg')
            tmp = img.filter(ImageFilter.EDGE_ENHANCE)
            tmp.save(data_dir_path + '/' + root +'_r27.jpg')
            tmp = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
            tmp.save(data_dir_path + '/' + root +'_r28.jpg')
            tmp = img.filter(ImageFilter.UnsharpMask(radius=5, percent=150, threshold=2))
            tmp.save(data_dir_path + '/' + root +'_r29.jpg')
            tmp = img.filter(ImageFilter.UnsharpMask(radius=10, percent=200, threshold=5))
            tmp.save(data_dir_path + '/' + root +'_r30.jpg')

if __name__ == '__main__':
    main()

pythonのPILという画像操作ライブラリを使ってます。

ソースの内容の補足説明

簡単に解説します。

  • img.transpose(Image.FLIP_LEFT_RIGHT)は「左右反転」
  • img.transpose(Image.FLIP_TOP_BOTTOM)は「上下反転」
  • img.transpose(Image.ROTATE_90)は「90度回転」以下度数だけ変えてます。
  • img.rotate(15)は「15度回転」・・あとは、度数を変えているだけです。
  • img.filter(ImageFilter.EDGE_ENHANCE)はエッジ強調フィルターです。
  • img.filter(ImageFilter・・も同様に画像の画質に変化を加えてるだけです。

例えば、このソースのあるフォルダに「in」と[out」というフォルダを作り、「in」の下に以下のように画像ファイルをおいて実行します。

f:id:arakan_no_boku:20171104110033j:plain

そうすると、「out」フォルダにこんな感じで画像データができあがります。

f:id:arakan_no_boku:20171104110329j:plain

結局、この処理後にRalphaを使って、一括でグレースケール変換したものも追加したので、最終的には30x2=60倍に水増しして、枚数が10000枚を超えました。

ニューラルネットワークコンソールで試してみる

画像データをすべて水増ししたら、再度DATASETを作り直して、ニューラルネットワークコンソールで学習・評価をやり直してみます。 

 

DATASETの作り直し

DATASETの作り直し方については、以下を参考にしてください。

support.dl.sony.com

arakan-pgm-ai.hatenablog.com 

プロジェクトは前回と同じ

前回と同じプロジェクトを開きます。 

arakan-pgm-ai.hatenablog.com

DATASETは、今回生成したものに変更します。 

 

CONFIGタブ

あと、CONFIGタブも変更しておきます。 

データ量が増えたので、デフォルトのepochとbatchsizeに戻します。

f:id:arakan_no_boku:20171104114959j:plain

 

学習実行

さて学習します。 

時間は結構かかりました。 

”BOKU"のPCのスペックがしょぼい(5年前位に3万円ほどで買ったノートPC・・推して知るべしですね。)のもありますが、なんと学習に11時間ですよ(^_^;) 

その結果はこんな感じです。

f:id:arakan_no_boku:20171105000337j:plain

 

評価結果

それで評価した結果はこちら。

f:id:arakan_no_boku:20171105000313j:plain

96.27%は上出来です。 

なんせ、前回 約71%しか出なかったモデルはそのままで、学習データの水増しをしただけですからね。 

学習データ量が少ないときの画像水増しは学習精度の改善に効果はありそうです。

とりあえず、それが確認できたからよしとしときます。 

ではでは。