動かざることバグの如し

近づきたいよ 君の理想に

脳死で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')