Azure Custom Vision を使ったシステムを作っています。
Azure Custom Vision を使うには、1つのタグに5つ以上の画像ファイルが必要です。
が、データ提供元から画像データが一つしかもらえなかったり、そもそも5ファイル程度では期待した精度が得られなかったりします。
こういう時、機械学習の世界では、学習用のデータを水増し(augmentation)することがよくあるようです。
機械学習のフレームワーク(Cognitive toolkit を含む)には、データの水増し機能が入っているそうですが、今回は Custom Vision を使いたいだけなので、CNTK は使わず、画像の水増しを行うライブラリを使ってみました。
Python ばっかりやなー。C# や JavaScript 製のも探してみたのですが Popular なものは見つからず。 Python はハンズオンを一度経験しただけの状態ですが、トライしてみます。
ツールは「imgaug」を使うことにしました。日本語の解説記事がありましたので。
たしか Python って 2.x と 3.x がどちらも生きてるんだよねえ、(macOSだけど)環境構築面倒そう。 ということで Docker を使うことにしました。Docker で動くようにしておけば、ゆくゆくはまるっと FaaS 化できるよね、という期待もあります。
Docker は version 18.09.0 が入っているのでこのままで。
まず Docker イメージを作るための Dockerfile
を記述します。
Dockerfile
FROM python:3
RUN pip install imgaug
RUN pip install opencv-python
RUN pip install imageio
python:3 をベースに imgaug と処理に必要なライブラリ各種をインストールしておきます。
そして Dockerfile
のあるディレクトリで
docker build . imgaug
を実行すると、 imgaug という名の Docker イメージが作成されます。(↓は docker images
で確認した様子)
REPOSITORY TAG IMAGE ID CREATED SIZE
imgaug latest 1fb215f98c9a About an hour ago 1.41GB
python 3 1e80caffd59e 3 weeks ago 923MB
次に imgaug で水増しを行う処理を Python で書きます。
work/
ディレクトリを作って、その中に run_aug.py
を作成することにしましょうか。
あ、今のところは docker 関係なく。ホストコンピュータの方で作業します。
run_aug.py
import imgaug as ia
from imgaug import augmenters as iaa
from matplotlib import pyplot as plt
import imageio
import glob
import os.path
def remove_glob(pathname):
for p in glob.glob(pathname, recursive=False):
if os.path.isfile(p):
os.remove(p)
def main(dir_in, dir_out):
# 出力先ディレクトリをクリーン
remove_glob(dir_out + '/*')
for filepath in glob.glob(dir_in +'/*'):
print('in: ' + filepath)
img = imageio.imread(filepath)
# ノイズ
noise(filepath, dir_out, img, [0.3, 0.4, 0.5])
# 加工後画像をファイルに保存する
def writeFile(filepath, dir_out, prefix, i, aug_img):
filename = os.path.basename(filepath) # /data_in/img.jpg -> img.jpg
root, ext = os.path.splitext(filename) # img.jpg -> (img, jpg)
outpath = dir_out + '/' + root + '_' + prefix + '_' + str(i) + ext
imageio.imwrite(outpath, aug_img)
print('out: ' + outpath)
# ノイズを入れる
def noise(filepath, dir_out, img, params):
i = 0
for d in params:
i = i + 1
# 画像に変換を適用する
augDropout = iaa.Dropout(p=d)
aug_img = augDropout.augment_image(img)
writeFile(filepath, dir_out, 'noise', i, aug_img)
dir = os.path.dirname(__file__) # 実行ファイルの場所
main(dir + '/images_in', dir + '/images_out')
これが C#er が初めて書いた Python のコードだw
ええと、 run_aug.py
のあるディレクトリに images_in/
、 images_out/
というディレクトリを作り、入力画像を images_in/
に入れます。
image_in/
内のファイルを列挙して、入力ファイルにノイズを入れます。 noise
関数に渡しているパラメータ [0.3, 0.4, 0.5]
はノイズの濃さを示しており、ノイズ薄い・中間・濃いの3つのファイルが images_out/
に出力されるという仕組みです。
run_aug.py
を実行するDocker 内で、Python で run_aug.py
を実行します。
work/
ディレクトリで、以下のコマンド一発です。
cd work
docker run --rm -v $(pwd):/temp imgaug python /temp/work/run_aug.py
一応解説。
$(pwd)
=現在のディレクトリを docker 内の /temp
ディレクトリにマップしますrun_aug.py
を実行します。完了すると、 images_out/
ディレクトリに、
sample_noise_1.jpg
sample_noise_2.jpg
sample_noise_3.jpg
の水増し画像ファイル群が出力されます。
これの完全版を github に公開しています。 ノイズ以外に「一部欠落」「回転」「移動」「剪断(shear)」を行っています。
完全版の実行結果
こうして水増しされた画像を、元画像とともに Custom Vision に投入して Train し、まずまず期待した検出が行えるようになりました。
水増しの加工の種類は imgaug にもたくさん用意されており、今回試したものが最適とは思えませんが、とりあえず検証を繰り返すための 「たたき台」 としては使えると思っています。
ひとつ疑問なのは、
「Custom Vision の Train の過程で、自動的にデータの水増し(augmentation)を行っているのではないか?」
ということです。特に根拠はなく、「CNTKには、データの水増し機能が入っている」という情報から勝手に思っているだけなのですが。
できれば機械学習そのものに対する学習はできるだけ避けてとおりたい(手が回らん)ので、簡単な手間で使用できる Custom Vision を始めとする各社の PaaS は、大変助かります。
Q:どのくらい勉強しなければならないのか?
— あめいぱわーにおまかせろ! (@amay077) 2016年5月25日
A:時間軸次第。2~3年先には機械学習もパターン化すると予想。その後にはアルゴリズムを選択する必用もなくなると思う。 #roomH #decode16
これ、2年前の de:code でスピーカーの方が話されていたことですが、だいぶその通りな世界になりつつあるなあという感じがします。