ゼロから始める機械学習

機械学習についての個人メモ

Kaggle Carvana 画像セグメンテーションチャレンジ

Carvana Image Masking Challengeに参加しました。
画像セグメンテーションのコンペで、画像から車部分のみの領域を推定します。
結果はPublic 24位、Private 49位

f:id:fujisanx:20170928113843p:plain

参加時既にスコアは軒並み99.6に達しており、1280x1918のセグメンテーション結果を10万枚submitと処理時間のかかるコンペでした。

前処理

  • 1280x1918を縮小し640*960パディングあり (縮小し拡大で戻して、0.9999の精度をもつ一番低い解像度だった為)
  • 20%の学習データをvalidation用に

データ増強

  • 拡大縮小、縦横スライド、反転
  • HSVの強弱
  • ランダムガンマ補正
  • マスクを傷つけないRandom Erase

CNNの構築

Unetのkernelが序盤から高いスコアを出しており、Denseblockを採用したTiramisは高解像度だとメモリ不足で動かせなかったので、Unetを改良していくことにしました。 元の論文は2015年代なので、それ以降に良く使われるようになった構造でメモリの都合を見ながら自分なりに取り入れてみました。

  • Wide Residual Network なResdiual構造に変更
  • Squeeze-and-Excitation NetworksのSEblockの追加
  • Upsampling2DでconcatからConv2DTransposeでaddに変更

基本的には以上の要素を使い、亜種をいくつか試していました。
Tensorflow/Kerasで作成し、Githubにも置いています。

github.com

LBには99.72〜73があったので、シングルで99.71付近になるのを目指してましたが、結局わからず終盤に。 最終的にローカル検証とsubmitで最もスコアが良かった3つでアンサンブルと反転TTAを使用してsubmitしました。

たこ

  • Residual構造、SEblock、データ増強のRandom Eraseはどれも効果があった。
  • Public→Privateでスコアを大きく落としており偏っていた、時間節約でPublicLBで十分と思いone hold outでCV省略したのは良くなかった
  • モデルの基本が決まった段階でCVで出来たweightでアンサンブルを試して、1 foldの検証で最終スコアがある程度推測できるようにしておく方が良さそう
  • パラメーターやCNNモデルの微小な変更は殆ど差が出ないので、時間配分に応じて検証項目から外す
  • Tensorflow/Kerasでモデルを切り替えてpredict回すときは、モデル切り替え前にK.clear_session()しないと途中でGPUメモリ不足で落ちる現象があった

終わりに

似た画像である程度の解像度なら自動切り抜きも実用的になってきているのではと思わされるコンペでした。また、セグメンテーションは画像分類の前処理に入れても精度向上に寄与するので勉強できたのは良い機会でした。

今回のコンペはGPUの速度とメモリ不足に悩まされるコンペで、最後の2日間だけGCPの無料枠でP100も使いました。P100よりGTX1080tiの方が若干早いのが不思議でしたが、クラウドECCメモリ16GBなのでP100は魅力的です。
ただ、1日回すと7千円近いので気軽には使いにくいですね。

kaggleメダルは銀2銅1になりました、masterへの道は遠い。

自宅で使う機械学習/ディープラーニング用PCの構成

機械学習用のPC構成を最初の頃に悩んだので残します。主にKaggleやディープラーニングで遊べる用途です。
現在利用しているPCの構成は以下の通りです。

CPU core i5 7600
GPU GTX 1080ti
MEM 64GB
SSD  512GB SATA

CPU

一番悩む部分でした。多くの機械学習ライブラリがGPUに対応しているので、GPUにお金を注ぎ込むのも選択肢の一つです。
ディープラーニングは2コアあれば十分ですが、XGBoostやLightGBMはGPUの恩恵が少なく、多コアのRyzenが圧倒的なパフォーマンスです。
個人的にはGPUが十分であれば、Core i5でも我慢できるレベルです。 ※追記:コンペでCPUに頼る部分が多くなると、Core i5で後悔します。お金に余裕あればi7以上を買いましょう。

GPU

ディープラーニングではGPUのメモリが足りないと動かないのでメモリ量が最優先です。
GTX1050tiから1080tiに変えましたが、処理時間は3.5倍程度の向上でした。

  • 安さ重視   GTX 1060 6GB
  • コスパ重視  GTX 1070 8GB
  • スペック重視 GTX 1080ti 11GB

複数GPUを刺すのは金額を気にする場合はやめた方が良いと思います。
複数刺してもメモリや速度は単純に倍にならず、CPU・マザーボード・電源・ケースも合わせて良いものにしないといけないからです。
GTX 1080ti *2に増設できるようにしていますが、勿体無かったかもと後悔中です。

メモリ

扱うデータ量によりますが、画像・音声・動画を処理する場合は多いほど楽です。
最低16GB、基本的には32GBあれば十分です。
64GBあるとKaggleの大量画像分類でもメモリ使用量を気にしなくて良くなります。

SSD

メモリに載せれるデータ量で殆ど処理できるのでSATAの256GB以上のSSDで十分です。
NVMe SSDSATA接続のSSDより高速ですが、PCIレーンを4使用するのでCPUもCore i5以上が必要です。

マザーボード

CPUに合わせてどれでも良いですがファンコントローラーがあるのが好きです。

電源

ブロンズ以上で価格が安すぎないものなら何でも良いと思います。
電気効率が良いのはゴールド以上で容量はピーク使用時の50%が目安みたいです。

ケース

ディープラーニングは長時間稼働させるので、十分冷却できるケースが良いです。
静音重視の古いAntec P100を流用していますが、空冷ファンで十分排熱ができれば室温31℃で長時間稼働してもGPU65℃前後で安定します。
GPUが75℃を超える場合はケースやファンを見直した方が良いです。

OS

Windows環境にも各ライブラリの対応が進んで今は不便なさそうですが、LinuxUbuntuで稼働させた方が使えるライブラリも多く、GPUメモリの空きが増えパフォーマンスは良いです。Windowsは別のSSDに入れて切り替えて使用しています。

雑感

AWSGCPなどクラウドでも機械学習はできますが、学習やKaggleなど個人用途ならPC購入した方が最終的には安くなると思います。
ゲームやVRもやりたい人、パーツを差し替えていけばデスクトップPCは長く使えるので1台あると便利です。

2017年 時点での予算規模

  安さ重視 コスパ重視 性能重視
CPU core i5 core i5 corei7/Ryzen
RAM 16GB 32GB 64GB
GPU GTX1060 GTX1070 GTX1080ti
予算目安 12万 15万 25万

Kaggle 画像分類、時間短縮と精度向上のメモ

Kaggle Planetに参加しました。結果は96位。
Discussionにない工夫をした点は天候に応じて特定のラベルの閾値を変更する程度で、 提出したのはVGG16, ResNet50, Xceptionの3CNNのアンサンブルでした。

f:id:fujisanx:20170727102609p:plain

時間短縮の為に

精度を競うと機械学習は実験的な側面もあり、いくらでも時間を溶かせます。
Kaggleのランキング上位のインタビューを見てもデータの特徴をうまく掴むことが大事だと思います。

  • ローカルでの検証環境を整える
  • データの整形・拡張の比較検証は軽量なCNNで
  • 時間のかかるCNNモデルは最後、その他の処理に注力する
  • 条件によってある程度決めれるパラメーターは固定してしまう
  • 学習は夜間や他のことをやってる時に
  • Discussionや過去の勝者ソリューションを見る

精度向上の為に

  • TTA 推論時のデータ拡張
  • アンサンブル 平均、投票など

コードを再利用しやすく

以下の構成に落ち着きました。

  • preprocess データのリサイズやクリーニングをしてnpzで保存
  • explore   データの調査
  • train    学習、1コードで学習からFスコアの結果を見れるように
  • predict   推論、アンサンブル用にnpzで保管

学習や推論は何度も実行するのでパラメーターを渡すのみにしました。

run_train(CNNname, freeze_layer, optimizer='Adam', fold_num=5, validation_no=1, dataset=npz_name)

jupyter notebookを使用していますが、結果も1ファイルに残り楽です。
コードに変更を行う場合は複製してコードを残し、アンサンブル用にタイムスタンプを付けたweightsとnpzを生成します。

github.com

結果

メダルを2枚取得でき無事Kaggle expertになりました。
Kaggleへの参加は実データに向き合う練習になり、KernelやDiscussionを読むのも非常に勉強になります。
Kaggle masterへの道は遠い。

画像認識で参考にした書籍

深層学習

bookclub.kodansha.co.jp

画像認識

www.kspub.co.jp

Raspberry Pi でディープラーニング環境構築

ディープラーニングの推論専用の環境を構築していきます。
* ラズパイはCPUが弱くメモリも1GBしかないので、DNNの訓練用の環境は別途必要です。

既にsshで接続できる環境が整っているのを前提としています。

zero-ai.hatenablog.com

今回設定するもの
- Python3環境
- Tensorflow
- OpenCV

ビルド中はCPUが高負荷になるので風を当てた方が良いです。

パッケージインストー

必要になるものをインストー

sudo apt-get install git cmake build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev libpng-dev

Tensorflow

Pythonで手軽に利用したいので今回は以下を利用
GitHub - samjabrahams/tensorflow-on-raspberry-pi: TensorFlow for Raspberry Pi

C++で利用する場合は公式のソースからビルドします。
github.com

cd tensorflow
tensorflow/contrib/makefile/download_dependencies.sh
sudo apt-get install -y autoconf automake libtool gcc-4.8 g++-4.8
cd tensorflow/contrib/makefile/downloads/protobuf/
./autogen.sh
./configure
make
sudo make install
sudo ldconfig  # refresh shared library cache
make -f tensorflow/contrib/makefile/Makefile HOST_OS=PI TARGET=PI OPTFLAGS="-Os" CXX=g++-4.8

OpenCV

git clone https://github.com/Itseez/opencv.git
cd opencv
mkdir build
cd build
cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D INSTALL_PYTHON_EXAMPLES=ON \
-D BUILD_EXAMPLES=ON ..
make -j4
sudo make install
sudo ldconfig

以上で完了です。

Raspberry piのサーバー向けRaspbian Jessie Liteセットアップ

Raspberry Piは広く使われており、Webにある古い情報のセットアップとは若干変わっていたので残します。

Mac環境でのRaspberry Pi CUI版のセットアップとなります。
デスクトップ版と比べて軽量ですが、サーバ向けでGUI環境はありません。
- RASPBIAN JESSIE LITE
- Version:July 2017
- Release date:2017-07-05
- Kernel version:4.9

Raspbian OSの準備

イメージファイルは公式より入手

www.raspberrypi.org

ディスクユーティリティでFATでSDカードをフォーマット
アンマウントしてイメージをSDカードに書き込む

sudo diskutil unmount /dev/disk2s1
sudo dd bs=1m if=2017-07-05-raspbian-jessie-lite.img of=/dev/rdisk2

Raspberry pi の設定

HDMIとキーボードを接続し、USB電源を刺すとランプが点灯し自動で起動します。
*電源は推奨2.5Vです、一般のスマホ充電器や100円均一のは1Vが多いので確認

初期ユーザー:pi
パスワード:raspberry

起動後に下記を実行し、ラズベリーパイの設定画面を表示

sudo raspi-config
  1. Change User Passwordでパスワードの変更
  2. Localisation Optionsでkeybordの設定
  3. Interfacing OptionsでSSHを有効
  4. Advanced OptionsでA1 Expand FilesystemでSDカード全体を使えるように

Finishして再起動

無線LANの設定とIP固定

まずは無線LANの接続設定

sudo vi /etc/wpa_supplicant/wpa_supplicant.conf

以下を追記

network={
  ssid="Your SSID"
  psk="Your Password"
}

続いてIPアドレスの設定

sudo vi /etc/dhcpcd.conf
interface wlan0
static ip_address=192.168.xx.xx
static routers=192.168.xx.xx
static domain_name_servers=192.168.xx.xx

あとは再起動し

sudo reboot

再起動後に正常に設定できたか確認し完了です。

ip addr

あとは各種アップデートも行っておきます。

sudo rpi-update
sudo apt-get update
sudo apt-get upgrade

TensorflowでKerasのコードを書く

Tensorflow 1.2でKerasのコードがほぼそのまま動くようになりました。

Module: tf.contrib.keras  |  TensorFlow

import部分を変えるだけで動作します。

例)ResNet50の転移学習でのkaggle犬猫画像分類

Tensorflowで完結するので、Android, iOS, Raspberry Piへの展開が楽になりそうです。
便利ですね。

画像データの切り抜きをCNNとopenCVで自動化する

概要

画像分類では認識したい対象だけのクリーンな画像が欲しいですが、提供データには不要なオブジェクトが多く写ります。また、分類対象が小さくしか写っていないこともあります。
大量の画像データを手動で切り抜くには時間がかかるので、必要部分のみ切り抜く処理をCNNとopenCVで教師データありで自動化します。

目的

Kaggleのintel子宮頚部癌スクリーニングコンペに参加し、22位でした。
Intel & MobileODT Cervical Cancer Screening | Kaggle

f:id:fujisanx:20170703205728p:plain

このコンペでは4000pxを超える高解像度のデータが8000枚ほど提供されました。
画像は専用の医療機器で撮ったものからデジカメで撮ったようなものまで、対象の形状や色も違い、医療器具の映り込みもありました。
CNNでの画像分類はメモリの制約上、224pxや299pxなど縮小したものを利用します。
認識したい箇所が小さい画像はそのまま縮小すると特徴が失われてしまうので、高解像度の画像から認識したい箇所を切り抜き特徴を残したいところです。
※最初にそのまま縮小した画像で、転移学習のResNet50で分類した時は良いスコアは得れませんでした。

検討した他の手法

Kaggleカーネルでは、教師なしでセグメンテーションをする方法が紹介されていましたが、実際に適応するとあまり満足する結果ではありませんでした。
以下のカーネルで確認できますが、必要でない医療器具が残ったり、必要な皮膚部分が除去される場合があります。

http://ggle.com/chattob/cervix-segmentation-gmm

その他scikit-imageにある教師なしセグメンテーションで閾値分割や前景の検出など

http://scikit-image.org/docs/dev/auto_examples/

満足な結果が出なかったので教師なしでのセグメンテーションを諦め、教師ありで切り抜くことにしました。

CNNによるセグメンテーション既存手法

既にRCNNやSSDなどの複数オブジェクト検出、FCNによるピクセル単位でセグメンテーションするネットワークが考案されています。
今回は医療画像の為、流用できる教師データはなさそうでした。
手動で教師データを作成する場合、時間のかかるピクセル単位の教師データ作成は時間がかかるので、切り抜きたい座標を得れる単純なもので対応したいところです。

また、kaggle公式ブログの過去コンペでも既に使われている手法だったので、教師データ作成に時間をかける価値はありそうでした。 blog.kaggle.com

ResNetを利用した単一オブジェクトの切り抜き領域の推測

切り抜きたい対象は画像につき1つで、領域はCNNに必要な正方形で十分です。
今回はシンプルにResNetで特徴を抽出して始点のx,yとlengthを推測することにしました。

一度縮小した画像でx,y,lengthを求め、元画像と縮小画像の比率に応じてopenCVで切り抜けば完了です。

結果

まず200件の教師データを手動で作成し、残りのデータに推論をかけてチェックしながらズレを手修正、チェック済みのデータで再学習の繰り返しで対応しました。
初期の200件の学習でもそれなりの結果が得れ、1割程度の手修正。1000枚の学習以降は2000枚チェックしても殆ど修正はしませんでした。

このことから以下の条件を満たす場合にはある程度有効となりそうです。

  • 教師なしの手法で切り抜きができない
  • 流用できるセグメンテーションデータがない
  • 切り抜き対象が画像内に必ず一つ
  • ピクセル単位のセグメンテーションデータの作成は面倒で、座標指定の作業ならまだ許せる場合

最終的に3000枚の教師データの切り抜き精度で十分だったので画像を処理し、複数のCNNのアンサンブルで22位を取得できました。