efficientnetで学習させてみる

efficientnetのコードはこちらで公式に公開されている

tpu/models/official/efficientnet at 8462d083dd89489a79e3200bcc8d4063bf362186 · tensorflow/tpu · GitHub

これを使ってcolaboratory上で学習を試してみたのでメモ

データセット準備

以下のスクリプトを使えば画像をtf_record化してgcsへuploadできる

https://github.com/tensorflow/tpu/blob/7e0ad3aabe888ce25599c5d9cbd804e99714f060/tools/datasets/imagenet_to_gcs.py

このスクリプトで想定しているdatasetのディレクトリ構造はこういう形。trainはlabel名のディレクトリ毎に分かれているが、validationはすべてのファイルが直下に入っているので注意。

dataset
├── train
│   ├── labelA
│   │   └── IMG_20170309_023302.JPEG
│   └── labelB
│       └── IMG_20170401_102412.JPEG
└── validation
    ├── IMG_20170923_172045.JPEG
    └── IMG_20170924_095022.JPEG

ちなみに画像名は拡張子が.JPEGになってればOK(以下で拡張子を直に指定しているので) 手元の画像の拡張子が.jpgとかだったら、ここを書き換えるだけでちゃんと動く

https://github.com/tensorflow/tpu/blob/7e0ad3aabe888ce25599c5d9cbd804e99714f060/tools/datasets/imagenet_to_gcs.py#L375

また、validation直下のファイル名を辞書順にsortして並べたsynset_labels.txtというファイルが必要なため以下のコマンドで作成

$ ls dataset/validation | sort > dataset/synset_labels.txt

スクリプトの実行コマンドはこちら どうやらpython2系で動かすことを想定しているらしくpython3.6で動かしたらエラーが出たため、dockerを使ってpython2系で動かした

$ cd <path/to/tpu repo>
$ docker run -v `pwd`:/root -w /root -it tensorflow/tensorflow:1.14.0 bash -c "python /root/tools/dataset/imagenet_to_gcs.py  --raw_data_dir /root/dataset --local_scratch_dir /root/tf_records --nogcs_upload" 
$ gsutil -m cp -r tf_records/train/train-*  gs://<bucket name>/dataset/tf_records/
$ gsutil -m cp -r tf_records/validation/validation-*  gs://<bucket name>/dataset/tf_records/

上記を実行するとtf_recordsというディレクトリができてその中に元の画像をtf_records化したものが生成される また、dockerだとgcloudの設定がめんどそうだったので、--nogcs_uploadを指定して一旦ローカルに生成してからgsutilを使ってuploadするようにした(gsutil -mで並列にuploadした方が早いというもある)

imagenet2012の場合150Gを1024に分けてtf_record化しているので、一つあたり150M程度の大きさになる。 自前のデータセットが小さいものである場合、一つあたりのサイズが小さくなりすぎるかもしれないので、その場合は以下の設定を変えて調整

https://github.com/tensorflow/tpu/blob/7e0ad3aabe888ce25599c5d9cbd804e99714f060/tools/datasets/imagenet_to_gcs.py#L85

ちなみに私は一つあたり10M程度でやってみたが特に問題なかった。

学習

学習のエントリーポイントはこちらのスクリプト

https://github.com/tensorflow/tpu/blob/8462d083dd89489a79e3200bcc8d4063bf362186/models/official/efficientnet/main.py

google公式であるためもちろんtpuで学習できる。 デフォルトの設定値はmain.pyの引数設定に書いてあるもので、imagenet2012のデータセットを350epochまわすようになっている模様

https://github.com/tensorflow/tpu/blob/7e0ad3aabe888ce25599c5d9cbd804e99714f060/models/official/efficientnet/main.py#L105,L154

以下で学習開始できるが、自分のデータセットの大きさに合わせてstep数などを調整する

# GCSへのアクセス権取得
from google.colab import auth
auth.authenticate_user()

# 学習用環境準備
!git clone https://github.com/tensorflow/tpu.git
!export PYTHONPATH="$PYTHONPATH:/content/tpu/models"
%cd tpu/models/official/efficientnet/
!git checkout 7e0ad3aabe888ce25599c5d9cbd804e99714f060 # 私が試したときのcommit

# 学習実行
TPU_ADDRESS="grpc://"+os.environ["COLAB_TPU_ADDR"]
!python main.py \
  --tpu=$TPU_ADDRESS \
  --model_name='efficientnet-b0' \
  --skip_host_call=true \
  --data_dir=gs://<bucket name>/dataset/tf_records \     # uploadしたtf_recordsが存在するディレクトリ
  --model_dir=gs://<bucket name>/dataset/model \         # これは学習時の出力ディレクトリの設定なので任意の場所でOK
  --num_label_classes=100 --num_eval_images=10000 --num_train_images=100000 --train_batch_size=2048 --eval_batch_size=1024  --train_steps=17090 --steps_per_eval=488 --iterations_per_loop=244

上記の設定は

  • 100クラス
  • trainingデータ10万件
  • validationデータ1万件
  • 合計350 epoch、10 epoch毎にvalidation

の場合のもので、計算は

  • train_steps = 100000 / 2048 * 350
  • steps_per_eval = train_steps / 35

iteration_per_loopはcheckpointの保存間隔の設定で今回は5epoch毎にしてある

steps_per_evalとiteration_per_loopはなるべく大きい値にしておく方が早い(tpuの処理を中断する回数が減るため)

ちなみに10GB程度のデータセットで試してみたところ、学習時間は7時間程度だった

loss確認

loss、accuracyの確認にはtensorboardを使う。出力されたファイルの中にtensorboardのファイルがあり、そこにlossとtop1, top5 accuracyの推移が保存される。colaboratory上であれば以下のようにして出力できる。

# driveをmount
from google.colab import drive
drive.mount('/gdrive')

# GCSからdriveへファイルをコピー
!gsutil -m cp -r  gs://<bucket_name>//dataset/model/eval "/gdrive/My Drive/"

# tensorboardの表示
%load_ext tensorboard
%tensorboard --logdir "/gdrive/My Drive/eval/"