動かざることバグの如し

近づきたいよ 君の理想に

脳死でCNNによる画像分類 on TensorFlowするメモ

Ver 2

from keras.models import Sequential
from keras.layers import Conv2D, Dense, MaxPool2D, Flatten, Dropout
from tensorflow.keras import optimizers
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import datetime

# バッチサイズ
batch_size = 32
# 画像分類したい数
num_classes = 3
# エポック数
epochs = 30
train_dir = 'images/train'
validation_dir = 'images/test'

# 画像の解像度
# image_height, image_weight = 1280, 720
image_height, image_weight = 384, 216

# CNN
model = Sequential()
model.add(Conv2D(32, (3, 3), activation='relu',
                        input_shape=(image_height, image_weight, 3)))
model.add(MaxPool2D((2, 2)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPool2D((2, 2)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPool2D((2, 2)))
model.add(Conv2D(128, (3, 3), activation='relu'))
model.add(MaxPool2D((2, 2)))
model.add(Flatten())
model.add(Dropout(0.5))
model.add(Dense(512, activation='relu')) # 値が多いほうが精度はいいが計算量は増える
model.add(Dense(num_classes, activation='softmax')) # 多分類の場合softmax

model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['acc'])

# 画像のRGBの値域が0-255であるのを0-1に正規化
train_datagen = ImageDataGenerator(
    rescale=1./255,
    rotation_range=20,
    width_shift_range=10,
    height_shift_range=10,
    brightness_range=[0.7, 2]
)
validation_datagen = ImageDataGenerator(
    rescale=1./255,
    rotation_range=20,
    width_shift_range=10,
    height_shift_range=10,
    brightness_range=[0.7, 2]    
)
train_generator = train_datagen.flow_from_directory(
    train_dir,
    target_size=(image_height, image_weight),
    batch_size=batch_size,
    class_mode='categorical'
)

validation_generator = validation_datagen.flow_from_directory(
    validation_dir,
    target_size=(image_height, image_weight),
    batch_size=batch_size,
    class_mode='categorical'
)

# log_dir="works/logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M")
# tensorboard_callback = tf.keras.callbacks.TensorBoard(
#     log_dir=log_dir,
#     histogram_freq=1,
#     write_grads=True,
#     write_graph=False)

from keras.callbacks import ModelCheckpoint
mc = ModelCheckpoint(
    filepath='best.h5',
    monitor="val_loss",
    verbose=1,
    save_weights_only=False,
    mode='min',
    save_best_only=True,
    period=1
)

train_step_size = train_generator.n // train_generator.batch_size
validation_step_size = validation_generator.n // validation_generator.batch_size

model.fit_generator(
      train_generator,
      steps_per_epoch=train_step_size,
      epochs=epochs,
      validation_data=validation_generator,
      validation_steps=validation_step_size,
      verbose=1,
      callbacks=[mc]
)

Ver 1

データは Fruits 360 dataset | Kaggle

# kerasでやろうとしていたがどうもtensorflowに吸収されたっぽい
# 参考: https://book.mynavi.jp/manatee/detail/id=79420
# よって tensorflow.keras と奇妙に見えるがこれで正しい
import tensorflow as tf
from tensorflow.keras import layers
from tensorflow.keras import models
from tensorflow.keras import optimizers
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import datetime

# GPUのメモリを使い切らないようにするおまじない
# ちなみにTF2.xではエラーになるので注意
from keras.backend.tensorflow_backend import set_session
config = tf.ConfigProto(
    gpu_options=tf.GPUOptions(
        visible_device_list="0",
        allow_growth=True
    )
)
set_session(tf.Session(config=config))
# バッチサイズ
batch_size = 32
# 画像分類したい数
num_classes = 103
# エポック数
epochs = 20

# 画像の解像度 100x100pxとなる
img_rows, img_cols = 100, 100

# CNN
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu',
                        input_shape=(img_rows, img_cols, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(512, activation='relu'))
# 最後のsoftmaxは多分類の場合
model.add(layers.Dense(num_classes, activation='softmax'))

model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['acc'])

# the data, split between train and test sets
train_dir = 'works/data/fruits-360/Training'
validation_dir = 'works/data/fruits-360/Test'
# rescaleで正規化
train_datagen = ImageDataGenerator(rescale=1./255)
validation_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
    train_dir,
    target_size=(img_rows, img_cols),
    batch_size=batch_size,
    class_mode='categorical'
)

validation_generator = validation_datagen.flow_from_directory(
    validation_dir,
    target_size=(img_rows, img_cols),
    batch_size=batch_size,
    class_mode='categorical'
)

log_dir="works/logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M")
tensorboard_callback = tf.keras.callbacks.TensorBoard(
    log_dir=log_dir,
    histogram_freq=1,
    write_grads=True,
    write_graph=False)

train_step_size = train_generator.n // train_generator.batch_size
validation_step_size = validation_generator.n // validation_generator.batch_size

model.fit_generator(
      train_generator,
      steps_per_epoch=train_step_size,
      epochs=epochs,
      validation_data=validation_generator,
      validation_steps=validation_step_size,
      verbose=1,
      callbacks=[tensorboard_callback]
      )

model.save('fruit_1.h5')

runtime=nvidiaを有効してdocker buildする

環境

  • docker
  • nvidia/cuda:10.0-runtime-ubuntu18.04

やりたいこと

docker build時にも --runtime=nvidiaしたい!!!!!!!

周知の通り、nvidiaはDockerに対してランタイムという形でCUDA環境を提供している。つまり、nvidia-dockerをインストールすると、Dockerのランタイム機能であたかもそのDocker Image内にCUDAがインストールされているかのように扱うことができる。

例えば以下の例

docker run --runtime=nvidia --rm nvidia/cuda:10.0-base nvidia-smi

--runtime=nvidiaをつけるだけ つよい

が、build時には何故かruntimeを指定することができない

なんでや

方法

現在のところ、デフォルトランタイムというオプションがあって、それをnvidia指定にするしかない

指定する方法は以下

/etc/docker/daemon.json にてdefault-runtimeを追記するだけ

{
    "default-runtime": "nvidia",
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}

でDockerサービスを再起動

# systemctl restart docker

確認

Default RuntimeがnvidiaになっていればOK

docker info 2> /dev/null | grep -i runtime
Runtimes: nvidia runc
Default Runtime: nvidia

これでruntime=nvidiaを指定しなくても自動的にnvidiaが有効になる。

# 以下も叩けるはず
docker run --rm nvidia/cuda:10.0-base nvidia-smi

当然全Dockerに対して有効になってしまうので、諸刃の剣な気がする。やっぱりdocker build時に指定できるようになってほしい。。。

TensorFlowでTensorBoardを使う MNIST版

環境

  • Python 3.6
  • TensorFlow 1.13
    • 今年中にはTensorFlow2.xが出るのだろうか、、、

やりたいこと

TensorBoardを試してみたかった。が、Qiitaをかいつまんで実行してもうまくいかない。。。

そこでMNISTの最小のコードを使ってサンプルを作る。

TensorBoardなしのMNISTコード

公式サイトのチュートリアルTOPページに載ってたやつそのまんま

import tensorflow as tf
mnist = tf.keras.datasets.mnist

(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(input_shape=(28, 28)),
  tf.keras.layers.Dense(512, activation=tf.nn.relu),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=5)
model.evaluate(x_test, y_test)

とくにコケる要素はないと思う

TensorBoardありのMNISTコード

import tensorflow as tf
import datetime
mnist = tf.keras.datasets.mnist

(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(input_shape=(28, 28)),
  tf.keras.layers.Dense(512, activation=tf.nn.relu),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

#### ここから追記
log_dir="logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M")
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1)
#### ここまで追記

model.fit(x=x_train, 
          y=y_train, 
          epochs=5, 
          validation_data=(x_test, y_test), 
          callbacks=[tensorboard_callback]) ## 追記

model.evaluate(x_test, y_test)

変更点をいくつか

後述用のコールバック関数を定義 kerasがコールバック関数を持ってるのでそれを使う histogram_freq=1は必須 log_dirは日付とかにしといたほうがあとでごちゃごちゃにならないので吉

log_dir="logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M")
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1)

ここで先のコールバックを渡す。配列なのが注意。

model.fit(x=x_train, 
          y=y_train, 
          epochs=5, 
          validation_data=(x_test, y_test), 
          callbacks=[tensorboard_callback])

あとこのコードではkerasもtensorboardも別途インストールはいらなかった

実行

TensorBoardを実行する

tensorboard --logdir logs

ローカルホスト以外のサーバーから実行した場合は--host 0.0.0.0 を付ける必要がある。

次はkerasと組み合わせて、、