あっぱれ日和、 - What a "Appare" day -

あっぱれ日和、 - What a "Appare" day -

通りすがりのしがない映画好きが綴る、モノづくりの記録や備忘録。そして、影響を受けた映画の記録。

機械学習初心者がDeep Learningを試してみる season CNN ~リベンジ編:第5話 改良、さらなる高みへ。Data Augmentation~

はじめに、

こんにちは!!前回の記事(以下)では、モデルの改良を行いました。
apao-m-appare99.hatenablog.com
成果の一つとしてData Augmentationを実施し、学習させたところ54.5%程の精度に上昇ました(その前は23%ほどでした。)。
今回はData Augumentationに焦点を当ててどのように精度が向上するかを試したいと思います。
今回の記事はただ画像を増やしてグラフがどうなるかを見るだけになります。(本記事は収穫度でいうと100点満点中10点ぐらいです。あまり読む価値ないかも。。。)
そして、今回も執筆するのはあくまでも機械学習ど素人のエンジニアもどきである私です。温かく見守ってください。
また間違っていたらご指摘してくださると助かります。

【目次】

画像データをさらに50%増やす。

前回の記事(上記リンク参照)で各カテゴリ毎に約200枚のあるデータセットを4倍に増やしました(各カテゴリ毎に約800枚)。その結果23%から54%に精度が増えました。倍以上に増えたんです。
つまり100%データを増価させて精度が倍以上になるなら、さらに50%増やせば、さらに1.5倍以上に精度が上がるのではと考えたわけです。(頭の悪い考え方)
さて、今回は回転とノイズを入れてみることにします。

回転を加える

回転を加えた画像を3種類用意します。60°から120°の値をランダムに3つ値を求め、それぞれ各角度分回転させた画像を生成します。
回転させる関数のコードは次のようになります。

def rotate_image(image):
    return transform.rotate(image, angle=random.randint(60, 120), resize=False, center=None)

そして各画像を回転させて対象フォルダに保存させます。以下がコードになります。

from keras.utils import np_utils
from sklearn.model_selection import train_test_split

from skimage import transform
import random

from PIL import Image
import numpy as np
import glob
import os

DATAPATH = "./data/src_augmentation/" 
folder = os.listdir(DATAPATH) 

dense_size  = len(folder)

for index, name in enumerate(folder):    
    files = glob.glob(DATAPATH + name + "/*.jpg") #ディレクトリごとに画像をまとめて格納
    for i, file in enumerate(files): # 1つのディレクトリの画像に対し、一つ一つ画像を見ていく
        for j in range(3):
            image = Image.open(file)
            image = image.convert("RGB")
            image = np.array(image, dtype=np.float32)
        
            image = rotate_image(image)
            image = Image.fromarray(np.uint8(image))
            #print(DATAPATH, name, i, file)
            image_path_separate = file.split('_') # ./data/src_augmentation/abyssinian\Abyssinian_1.jpgの1.jpgを抽出
            image_path_separate2 = image_path_separate[-1].split('.')
            image.save("./data/output2/" + name + "/" + name + "_" + image_path_separate2[0] + "_Rotate_" + str(j) + ".jpg")

出力例が以下、1枚目が元画像、2~4枚目が60°から120°の間のランダム値で回転させた画像。3枚目と4枚目がほとんど同じ画像ですが、一応別々の画像になります。

f:id:appare99:20200530195048j:plainf:id:appare99:20200603002728j:plainf:id:appare99:20200603002741j:plainf:id:appare99:20200603002749j:plain

ノイズを加える

ノイズはガウス雑音(ガウスノイズ)を加えることにします。
ガウス雑音は正規分布と等しい確率密度関数を持つ統計的雑音で、つまり、ノイズがとる値がガウス分布であるということ。(ウィキペディアより)
コードは以下のようになります。

def addGaussianNoise(src):
    row,col,ch= src.shape
    mean = 0
    var = 0.1
    sigma = 15
    gauss = np.random.normal(mean,sigma,(row,col,ch))
    gauss = gauss.reshape(row,col,ch)
    noisy = src + gauss

    return noisy

出力の際のコードは上記の回転の時とほぼ同じです。一応載せときます。

from keras.utils import np_utils
from sklearn.model_selection import train_test_split

from skimage import transform
import random

from PIL import Image
import numpy as np
import glob
import os
from skimage import util
import matplotlib.pyplot as plt
%matplotlib inline

DATAPATH = "./data/src_augmentation/" 
folder = os.listdir(DATAPATH) 

dense_size  = len(folder)


for index, name in enumerate(folder):    
    files = glob.glob(DATAPATH + name + "/*.jpg") #ディレクトリごとに画像をまとめて格納
    for i, file in enumerate(files): # 1つのディレクトリの画像に対し、一つ一つ画像を見ていく
        image = Image.open(file)
        image = image.convert("RGB")
        image = np.array(image, dtype=np.float32)
        
        image = addGaussianNoise(image)
        image = Image.fromarray(np.uint8(image))
        #print(DATAPATH, name, i, file)
        image_path_separate = file.split('_') # ./data/src_augmentation/abyssinian\Abyssinian_1.jpgの1.jpgを抽出
        image_path_separate2 = image_path_separate[-1].split('.')[f:id:appare99:20200606150010p:plain]
        image.save("./data/output/" + name + "/" + name + "_" + image_path_separate2[0] + "_Noise.jpg")

出力例が以下。左が元画像、右がノイズを加えた画像です。

f:id:appare99:20200530195048j:plainf:id:appare99:20200603003630j:plain

増加させたデータセットで学習させてみる(各カテゴリ毎約1200枚の画像)

では学習させていきます。学習のモデルは過去の記事(以下リンク参照)に用いたコードと同じものを使用しています。
apao-m-appare99.hatenablog.com
結果は以下のようになりました。
右が精度のプロット、左が損失の推移プロットになります。

f:id:appare99:20200606145453p:plainf:id:appare99:20200606145501p:plain
なんか、グラフが粗いですね。ノイズ画像が画像として認識を妨げている可能性がありますね。
一旦、ノイズ画像を除いた場合でも学習をさせました。その際の学習結果の推移プロットを載せてみます。
f:id:appare99:20200606145953p:plainf:id:appare99:20200606150010p:plain
なんか粗くなくなりましたね。ノイズのあった時のブレブレのグラフと比べて安定しています。ちなみにノイズ画像を入れて学習させた時の精度は 0.623でノイズ画像を入れずに学習させた時の精度は0.601であまり変わらなかったです。

一旦、まとめ

今回画像を50%増やしましたが54%から60%と、わずか6%しか上昇しませんでした。

画像データをさらに増やしてみる。

ここから精度は増えるのかという好奇心があったのでやってみました。
ここまでで

  • 上下反転
  • 左右反転
  • Cutout(画像の一部を塗りつぶす)
  • 画像回転(60°~120°)×3

の画像を作成し、1カテゴリあたりの画像を約200枚から約1400枚へと増加させました。
ここでさらに-60°~-120°の画像を3枚ずつ作成します。これにより1カテゴリあたり約2000枚の画像を学習させることになります。
ちなみに60°~-120°の画像を3枚ずつ作成した結果の例は以下のようになります。

f:id:appare99:20200606151541j:plainf:id:appare99:20200606151556j:plainf:id:appare99:20200606151603j:plain
1カテゴリあたり約2000枚の画像を学習した結果は以下のようになりました。
毎度のことながら右が精度のプロット、左が損失の推移プロットになります。
f:id:appare99:20200606151940p:plainf:id:appare99:20200606151959p:plain
また評価精度は0.382、評価損失は2.39となりました。
...増やせばいいってもんでもないですね。というかこんなすぐに上限に達してしまうとは。
限界ですね。今回はこの辺で終わりますか。

まとめ

今回はData Augmentationということで画像を増やすことでどんな結果になるかを見てみました。
前回の記事では学習データを増やすことで精度を30%程上げられたので、希望をもって挑みましたが60%止まりの精度で終わってしまいました。ここがこのデータにおけるData Augmentationの限界なのかもしれません。
次回以降は60%の精度を出した。

  • 上下反転
  • 左右反転
  • Cutout(画像の一部を塗りつぶす)
  • 画像回転(60°~120°)×3

のデータセットを用いていくことにします。
次回はCNNの別のモデルを構築する予定です。予定しているのはAlexnetです。