TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

リアルタイムにマンガの顔パーツ検出を行ってみた

以前に書いた以下の日記の内容を組み合わせて、リアルタイムにマンガの顔パーツ検出を行ってみました。

リアルタイムにマンガの顔検出することに何の意味があるかというと、特に意味はありません。。。
他の実用性のある応用を考えられそうですが、これはただのデモになります。

文章ではわかりにくいので動画にしてみました。


※途中(8秒あたり)から音楽が流れます。困る方はミュートしてください。


ソースコードGitHubで公開しました。試したい方はどうぞ。
github.com


学習データはアップできませんが、学習済みモデルを公開しました。

以下のようにして実行してください。

python realtime_facial_landmark.py model1000newgame

realtime_facial_landmark.pyの

video_capture = cv2.VideoCapture(1)

の行の、cv2.VideoCapture()の引数は、PCに接続されているカメラデバイスによって変更が必要です。

Surfaceのカメラでリアルタイムに顔器官検出を行う

以前日記に書いたdlibを使った顔器官検出Surfaceのカメラを使ってリアルタイムに行ってみました。

import sys

import dlib
import cv2

if len(sys.argv) != 2:
    print("realtime_shape_predictor.py model")
    exit()
model = sys.argv[1]

predictor = dlib.shape_predictor(model)
detector = dlib.get_frontal_face_detector()

video_capture = cv2.VideoCapture(1)

while True:
    ret, frame = video_capture.read()

    dets = detector(frame, 1)
    for d in dets:
        # Get the landmarks/parts for the face in box d.
        shape = predictor(frame, d)
        # Draw the face landmarks on the screen.
        for i in range(shape.num_parts):
            p = shape.part(i)
            cv2.circle(frame, (p.x, p.y), 3, (0, 0, 255), 1)
            
    cv2.imshow('Video', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

video_capture.release()
cv2.destroyAllWindows()

OpenCVを使うと簡単にカメラから画像を取り込めます。(詳細はこちらの日記を参照)
取り込んだ画像に対してdlibで顔器官検出を行って、取り込んだ画像に検出点(ランドマーク)を重ねて表示しています。

このような表示になります。
f:id:TadaoYamaoka:20170221220153j:plain

本をカメラの前にかざしています。(自分は写したくないので、適当に人物が写っている本にしましたm(__)m)

Surfaceで実行すると2秒に一回くらいしか更新されません。
もう少し速いPCじゃないとリアルタイムとは呼べませんね。

SurfaceのカメラをPythonからリアルタイムに使う

SurfaceのカメラをPythonからリアルタイムに使うには、OpenCVを利用して以下のように記述する。

import cv2

video_capture = cv2.VideoCapture(1)

while True:
    ret, frame = video_capture.read()
    cv2.imshow('Video', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

video_capture.release()
cv2.destroyAllWindows()

これでリアルタイムにカメラの映像を取り込むことができる。
終了するには、キーボードの「Q」を押下する。

cv2.VideoCapture()の引数によって、

0 背面カメラ
1 前面カメラ

となる。

なお、Jupyter QtConsoleなどで、

%matplotlib inline

を実行していると、cv2.imshowが動作しないため、

%matplotlib

でデフォルトに戻す必要がある。

写真を線画化する

マンガの顔パーツ検出を試みているが、マンガの顔画像と検出点がセットになった教師データを大量に入手することができない。

自分で入力したデータで検出精度の実験を行って、ディープラーニングを使うことで、単一のコミックの画像であればかなりの精度で検出できることが検証できた。
しかし、他のコミックに対する汎化能力は低い。

ニューラルネットワークの構成をシンプルなものにしているので、教師データは少なくてすむはずなので、一つのコミックあたりの画像数を少なめにして、できるだけ多くのコミックの画像の検出点を手入力していくのが一つのアプローチだと思っている。

一方、より深いネットワーク構成で学習させることで、検出の精度を高めることができるはずだ。
そのためには、より多くの教師データが必要になる。

そこで、写真の顔器官検出の教師データが流用できないかと考えている。
写真の顔器官検出の教師データは、dlibのface_landmark_detection.pyのコメントに記載されているiBUG 300-Wのデータセットが入手可能である。

写真を線画化することでマンガの教師データとして使用できるかもしれない。
やってみなと分からないが、とりあえず線画化を行ってみた。


写真の線画化は、PhotoShopなどの画像処理ソフトに機能があるが、バッチ処理を行いたいためPythonで扱えるライブラリがないか探してみた。
OpenCVに線画化そのものの機能はないようだが、OpenCVで演算をしてやることで線画化ができるようだ。

www.mathgram.xyz

元はPaintsChainerの作者のコメント欄に記載されていた方法のようだ。さらに元をたどれば、画像編集ソフトを使って行われていたテクニック(参考)のようだ。

同じ方法で試してみた。

import cv2
import numpy as np

gray = cv2.imread('r:/indoor_295.png', cv2.IMREAD_GRAYSCALE)
neiborhood24 = np.array([[1, 1, 1, 1, 1],
  [1, 1, 1, 1, 1],
  [1, 1, 1, 1, 1],
  [1, 1, 1, 1, 1],
  [1, 1, 1, 1, 1]],
  np.uint8)
dilated = cv2.dilate(gray, neiborhood24, iterations=1)
diff = cv2.absdiff(dilated, gray)
contour = 255 - diff
cv2.imwrite('r:/contour.png', contour)
変換元の写真

f:id:TadaoYamaoka:20170219165201p:plain

線画化

f:id:TadaoYamaoka:20170219165302p:plain

一部の線は強調されているが、はっきり線画化して欲しい顔が全体的にうっすらしている。
口は歯だけ線がはっきりして口の輪郭が分かりにくい。
これでは教師データとして使えなさそうである。

ガンマ補正

ガンマ補正を掛けて明るくしてから線画化した後、逆のガンマ補正をして元に戻してみる。
線画化は白領域を膨張(近傍の最大値をとる処理)させて元画像との差をとっているので、線画化前に白っぽくすることで線がくっきりする。
ガンマ補正はこちらのページを参考にした。

def gamma_compensation(img, gamma):
    lookUpTable = np.zeros((256, 1), dtype = 'uint8')
    for i in range(256):
	    lookUpTable[i][0] = 255 * pow(float(i) / 255, 1.0 / gamma)
    return cv2.LUT(img, lookUpTable)

img_gamma = gamma_compensation(gray, 2.0)
dilated2 = cv2.dilate(img_gamma, neiborhood24, iterations=1)
diff2 = cv2.absdiff(dilated2, img_gamma)
contour2_tmp = 255 - diff2
contour2 = gamma_compensation(contour2_tmp, 0.5)
cv2.imwrite('r:/contour2.png', contour2)
変換後画像(ガンマ値=2.0→0.5)

f:id:TadaoYamaoka:20170219170228p:plain

全体的に線がくっきりして、口の輪郭もはっきりするようになった。
これでも、灰色で濃淡があるため、完全に線画にはなっていない。

なお、ガンマ値2.0と0.5のガンマ補正をグラフにすると以下のようになる。
f:id:TadaoYamaoka:20170219182202p:plain


階調化するため、ガンマ値を極端に大きくしてみた。

変換後画像(ガンマ値=100.0→0.01)

f:id:TadaoYamaoka:20170219172312p:plain

濃淡は減っているが、線になってほしくない箇所が線になってしまった。

これならガンマ値=2.0の方がよさそうである。

コントラスト調整

今度は別の方法で薄い灰色の部分なくすために、コントラスト調整を行ってみる。

ガンマ値2.0でガンマ補正してから線画化した画像を、ガンマ値0.25で濃い目の線にした後、コントラスト調整で薄い部分を白に、線の部分を黒に近づける。

def contrast_adjustment(img, a, b):
    lookUpTable = np.zeros((256, 1), dtype = 'uint8')
    for i in range(256):
	    lookUpTable[i][0] = 255.0 / (1+math.exp(-a*(i-b)/255))
    return cv2.LUT(img, lookUpTable)

contour2 = gamma_compensation(contour2_tmp, 0.25)
contour3 = contrast_adjustment(contour2, 10.0, 128)
cv2.imwrite('r:/contour3.png', contour3)
コントラスト調整後

f:id:TadaoYamaoka:20170219203335p:plain

線が強調され、薄い灰色がなくなっている。
だいぶ線画に近づいた気がする。

なお、コントラスト調整のルックアップテーブルをグラフ化すると以下のようになる。
f:id:TadaoYamaoka:20170219203906p:plain

ガンマ値とコントラスト調整のパラメータは、画像によって最適な値が異なる。
他の画像で試すと別の値に調整しないとダメかもしれない。

コントラスト調整のみ

ガンマ補正を掛けずに、コントラスト調整のみして線画化してみる。

img_contrast = contrast_adjustment(gray, 10.0, 128)
dilated4 = cv2.dilate(img_contrast, neiborhood24, iterations=1)
diff4 = cv2.absdiff(dilated4, img_contrast)
contour4 = 255 - diff4
cv2.imwrite('r:/contour4.png', contour4)

f:id:TadaoYamaoka:20170219222045p:plain

先にコントラスト調整をするとうまくいかないようだ。

写真の自然な線画化は難しい。

とりあえず使えるかわからないが、この線画化した画像を教師データに使用して試してみようと思う。

TensorFlowをWindowsにインストール

以前の日記で公式バイナリをインストールする方法を書いていましたが、バージョン1.0がリリースされたことで内容が古くなったので、書き直します。

公式のページを見ればわかる内容なので、あまり価値のある内容ではないですが。


前提ソフトウェアとして、以下のソフトウェアをインストールしておきます。

※cuDNN 6は現時点で対応していないので、5.1をインストールする必要があります。

インストール済みの古いTensorFlowをアンインストール

古いTensorFlowをインストール済みの場合は、アンインストールします。

pip uninstall tensorflow-gpu

正式版のインストール

pip install --upgrade tensorflow-gpu

インストールは以上です。
簡単すぎて記事にする意味ないです(´・ω・`)

上記はGPUを使用する場合のインストール方法です。CPUのみの場合は、tensorflow-gpuの部分がtensorflowになります。

※ 2017/3/17 追記
TensorFlowを入れなおした際に、

Cannot remove entries from nonexistent file c:\anaconda3\lib\site-packages\easy-install.pth

というエラーが表示されるようになりました。
setuptoolsのバージョンが古いと起きるようなので、先に、

pip install --upgrade setuptools

を実行することで解消しました。

バージョン確認

1.0がインストールされているか、バージョンを確認します。

pythonコマンドでインタープリタを起動するか、Jupyter QtConsleなどで以下のスクリプトを実行します。

import tensorflow as tf
tf.__version__
'1.0.0'

と表示されれば1.0がインストールされています。

MNISTサンプルの実行

最初にやることと言えば、MNISTサンプルの実行です。

公式のチュートリアルにMNISTのpythonスクリプトがあります。

まずチュートリアルを含むGitHubリポジトリを取得します。

適当な作業ディレクトリで、

git clone https://github.com/tensorflow/tensorflow.git

MNISTサンプルのあるディレクトリに移動します。

cd tensorflow\tensorflow\examples\tutorials\mnist

MNISTの学習を実行します。

python mnist_softmax.py

成功すれば以下のように表示されます。

I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\stream_executor\dso_loader.cc:135] successfully opened CUDA library cublas64_80.dll locally
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\stream_executor\dso_loader.cc:135] successfully opened CUDA library cudnn64_5.dll locally
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\stream_executor\dso_loader.cc:135] successfully opened CUDA library cufft64_80.dll locally
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\stream_executor\dso_loader.cc:135] successfully opened CUDA library nvcuda.dll locally
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\stream_executor\dso_loader.cc:135] successfully opened CUDA library curand64_80.dll locally
Extracting /tmp/tensorflow/mnist/input_data\train-images-idx3-ubyte.gz
Extracting /tmp/tensorflow/mnist/input_data\train-labels-idx1-ubyte.gz
Extracting /tmp/tensorflow/mnist/input_data\t10k-images-idx3-ubyte.gz
Extracting /tmp/tensorflow/mnist/input_data\t10k-labels-idx1-ubyte.gz
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "BestSplits" device_type: "CPU"') for unknown op: BestSplits
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "CountExtremelyRandomStats" device_type: "CPU"') for unknown op: CountExtremelyRandomStats
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "FinishedNodes" device_type: "CPU"') for unknown op: FinishedNodes
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "GrowTree" device_type: "CPU"') for unknown op: GrowTree
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "ReinterpretStringToFloat" device_type: "CPU"') for unknown op: ReinterpretStringToFloat
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "SampleInputs" device_type: "CPU"') for unknown op: SampleInputs
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "ScatterAddNdim" device_type: "CPU"') for unknown op: ScatterAddNdim
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "TopNInsert" device_type: "CPU"') for unknown op: TopNInsert
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "TopNRemove" device_type: "CPU"') for unknown op: TopNRemove
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "TreePredictions" device_type: "CPU"') for unknown op: TreePredictions
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "UpdateFertileSlots" device_type: "CPU"') for unknown op: UpdateFertileSlots
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\common_runtime\gpu\gpu_device.cc:885] Found device 0 with properties:
name: GeForce GTX 1080
major: 6 minor: 1 memoryClockRate (GHz) 1.8225
pciBusID 0000:01:00.0
Total memory: 8.00GiB
Free memory: 6.63GiB
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\common_runtime\gpu\gpu_device.cc:906] DMA: 0
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\common_runtime\gpu\gpu_device.cc:916] 0:   Y
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\common_runtime\gpu\gpu_device.cc:975] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 1080, pci bus id: 0000:01:00.0)
0.9172

末尾の行に表示される数値がTest accuracyです。


mnist_with_summaries.pyを使えば実行結果が可視化されます。

python mnist_with_summaries.py

実行すると、cupti64_80.dllがないというエラーが表示される場合があります。

I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\stream_executor\dso_loader.cc:126] Couldn't open CUDA library cupti64_80.dll
F c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\platform\default\gpu\cupti_wrapper.cc:59] Check failed: ::tensorflow::Status::OK() == (::tensorflow::Env::Default()->GetSymbolFromLibrary( GetDsoHandle(), kName, &f)) (OK vs. Not found: cuptiActivityRegisterCallbacks not found)could not find cuptiActivityRegisterCallbacksin libcupti DSO

PATHの設定が必要なので、「C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\extras\CUPTI\libx64」を環境変数PATHに追加します。

環境変数を変更した場合は、コマンドプロンプトを起動し直します。再度実行すると以下のように表示されます。

(略)
Accuracy at step 900: 0.9642
Accuracy at step 910: 0.9653
Accuracy at step 920: 0.9676
Accuracy at step 930: 0.9653
Accuracy at step 940: 0.9648
Accuracy at step 950: 0.9678
Accuracy at step 960: 0.9667
Accuracy at step 970: 0.9663
Accuracy at step 980: 0.9664
Accuracy at step 990: 0.9663
Adding run metadata for 999

畳み込みニューラルネットワーク(CNN)の学習

先ほどの例のmnist_softmax.pyは、1層のパーセプトロンで、ぜんぜんDeepではないので、次に畳み込みニューラルネットワーク(CNN)を学習します。

CNNのモデルは別のリポジトリにあるので、GitHubから取得します。

適当な作業ディレクトリで以下のコマンドを実行します。

git clone https://github.com/tensorflow/models.git

MNISTサンプルのあるディレクトリに移動します。

cd models\tutorials\image\mnist

MINSTサンプル(CNN版)を実行します。

python convolutional.py

成功すれば以下のように結果が表示されます。

I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\stream_executor\dso_loader.cc:135] successfully opened CUDA library cublas64_80.dll locally
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\stream_executor\dso_loader.cc:135] successfully opened CUDA library cudnn64_5.dll locally
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\stream_executor\dso_loader.cc:135] successfully opened CUDA library cufft64_80.dll locally
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\stream_executor\dso_loader.cc:135] successfully opened CUDA library nvcuda.dll locally
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\stream_executor\dso_loader.cc:135] successfully opened CUDA library curand64_80.dll locally
Successfully downloaded train-images-idx3-ubyte.gz 9912422 bytes.
Successfully downloaded train-labels-idx1-ubyte.gz 28881 bytes.
Successfully downloaded t10k-images-idx3-ubyte.gz 1648877 bytes.
Successfully downloaded t10k-labels-idx1-ubyte.gz 4542 bytes.
Extracting data\train-images-idx3-ubyte.gz
Extracting data\train-labels-idx1-ubyte.gz
Extracting data\t10k-images-idx3-ubyte.gz
Extracting data\t10k-labels-idx1-ubyte.gz
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "BestSplits" device_type: "CPU"') for unknown op: BestSplits
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "CountExtremelyRandomStats" device_type: "CPU"') for unknown op: CountExtremelyRandomStats
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "FinishedNodes" device_type: "CPU"') for unknown op: FinishedNodes
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "GrowTree" device_type: "CPU"') for unknown op: GrowTree
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "ReinterpretStringToFloat" device_type: "CPU"') for unknown op: ReinterpretStringToFloat
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "SampleInputs" device_type: "CPU"') for unknown op: SampleInputs
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "ScatterAddNdim" device_type: "CPU"') for unknown op: ScatterAddNdim
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "TopNInsert" device_type: "CPU"') for unknown op: TopNInsert
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "TopNRemove" device_type: "CPU"') for unknown op: TopNRemove
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "TreePredictions" device_type: "CPU"') for unknown op: TreePredictions
E c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "UpdateFertileSlots" device_type: "CPU"') for unknown op: UpdateFertileSlots
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\common_runtime\gpu\gpu_device.cc:885] Found device 0 with properties:
name: GeForce GTX 1080
major: 6 minor: 1 memoryClockRate (GHz) 1.8225
pciBusID 0000:01:00.0
Total memory: 8.00GiB
Free memory: 6.63GiB
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\common_runtime\gpu\gpu_device.cc:906] DMA: 0
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\common_runtime\gpu\gpu_device.cc:916] 0:   Y
I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\common_runtime\gpu\gpu_device.cc:975] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 1080, pci bus id: 0000:01:00.0)
Initialized!
Step 0 (epoch 0.00), 16.4 ms
Minibatch loss: 8.334, learning rate: 0.010000
Minibatch error: 85.9%
Validation error: 84.6%
Step 100 (epoch 0.12), 5.5 ms
Minibatch loss: 3.248, learning rate: 0.010000
Minibatch error: 4.7%
Validation error: 7.4%

(略)

Step 8500 (epoch 9.89), 5.5 ms
Minibatch loss: 1.624, learning rate: 0.006302
Minibatch error: 1.6%
Validation error: 0.9%
Test error: 0.7%

Test errorが0.7%で、パーセプトロン版のTest accuracy 0.9172はTest errorにすると8.3%くらいなので、CNN版は高い精度で分類ができています。

デブサミ2017 【DevBooks】で同人誌を出します

デブサミ2017 【DevBooks】で、Andirogueというサークルで頒布する同人誌に記事を寄稿しました。

以前の日記で書いていたAlphaGoに関する記事です。

1ページ目の内容はこんな感じ。
f:id:TadaoYamaoka:20170210074351p:plain

サークルのメンバが書いた技術的な内容からゆるふわな記事まであります。
よろしければお立ち寄りください。

DCNNによるマンガキャラクターの顔パーツ検出(追試3)

前回に続き、学習のハイパーパラメータの影響を考察する。

今回は、ミニバッチサイズを変更した場合の影響について調べた。

ニューラルネットワークの学習は、通常ミニバッチ単位で更新を行う。
ミニバッチのサイズは、大きくすると勾配のばらつきが安定するため、学習係数を大きくすることができる。
また、GPUはメモリの許す限り1度に多くの行列を処理した方が効率がよい。(CPUの場合はこの限りではない。)

ただし、ミニバッチサイズをN倍にしても学習係数はN倍まで大きくはできない。

ここでは、適切なミニバッチサイズはどれくらいか調べてみた。

前回まではミニバッチサイズを16として、学習アルゴリズムにRMSpropを使用し、lossが発散しないできるだけ大きな学習率としてlr=0.0001で学習を行った。

学習係数を固定

まず、学習係数を固定して、ミニバッチサイズのみ変化させた結果、以下の通りとなった。

f:id:TadaoYamaoka:20170209223157p:plain

ミニバッチサイズを変更した分、1エポックのイテレーション数を増減させて、1エポックあたりのサンプル数が変わらないようにしている。

エポック単位では学習の速度は、ミニバッチサイズが小さいほうが速く収束している。
ミニバッチサイズが4では、test lossのばらつきが大きくなっており、直前のデータの影響を大きく受けている。

ミニバッチサイズを変えるとエポックあたりの処理時間も変わる。横軸の単位を実時間に変えた場合は、以下のようになる。

f:id:TadaoYamaoka:20170209223714p:plain

実時間あたりでは、エポックあたりよりミニバッチサイズごとの学習速度の差が小さくなっている。
それでも、ミニバッチサイズが小さい方が速く収束している。

学習係数を調整

次に、学習係数を調整を行った場合のミニバッチサイズによる学習速度について比較した。
ミニバッチサイズが4と8の場合は省略して、ミニバッチサイズを32にした場合と、64にした場合について比較する。
ミニバッチサイズを32にした場合、学習係数をlr=0.0002にしても学習できた。
ミニバッチサイズを64にした場合、学習係数はlr=0.00025までしか上げられなかった。それ以上にするとlossの減りが逆に遅くなる。

エポックあたりのlossは以下の通りとなった。

f:id:TadaoYamaoka:20170209234910p:plain


ミニバッチサイズ16と32の差は学習係数を固定した場合より差が小さくなっている。
ミニバッチサイズ64は16、32と比べてだいぶ学習速度が遅い。


実時間あたりのlossを比較した結果は以下の通りとなる。

f:id:TadaoYamaoka:20170209235355p:plain

ミニバッチサイズ16と32ほぼ同じ結果であるが、train lossはミニバッチサイズが16の方が収束が速く、test lossはわずかにミニバッチサイズが32の方が安定しているように見える。
ミニバッチサイズ64は、train、testともに16、32より学習速度が遅い。

まとめ

以上の結果から、ミニバッチサイズは小さすぎると直前のデータ影響を受けやすくlossが安定しなくなる。
ミニバッチサイズを大きくすると学習係数を大きくできるが、実時間あたりの学習速度は必ずしも速くなるわけではない。

今回の結果からは、ミニバッチサイズは小さ過ぎないサイズ(8以上)であればよく、大きくするメリットはあまりなく、大きくしすぎる(64以上)と学習速度が遅くなるということが言える。
前回まで使用していたミニバッチサイズの16は、他の論文を参考にしたものであったが、それが適切なサイズであることが確かめられた。