8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Keras / CNN] 多クラス画像分類 --- ラーメンの味分類

Last updated at Posted at 2018-11-19

About

・CNNでラーメンの味分類
・多クラス分類(塩、しょうゆ、みそ)

Point!

二値分類とのコードの違いは何か? --- (見出し:"前処理、ジェネレーター"へ)
精度が上がらない理由       --- (見出し : "考察"へ)

コードの解説について

・多クラス分類特有の部分のみ解説
・二値分類と同じコードは過去記事で解説あり(赤ちゃんの表情分類)
https://qiita.com/Phoeboooo/items/2c7457d1bfba514e2dc8

データ

・train : 塩 -- 2167枚  しょうゆ -- 2301枚 みそ -- 2613枚
・validation : 塩 -- 360枚  しょうゆ -- 364枚 みそ -- 410枚
・test : 塩 -- 331枚  しょうゆ -- 338枚 みそ -- 409枚

モデル


from keras import layers
from keras import models


model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu',
                        input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Dropout(0.2))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Dropout(0.2))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Dropout(0.2))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Dropout(0.2))
model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dropout(0.2))
model.add(layers.Dense(3, activation='softmax'))

from keras import optimizers

model.compile(loss='categorical_crossentropy',
              optimizer=optimizers.RMSprop(lr=1e-4),
              metrics=['acc'])

Total params: 3,454,147
Trainable params: 3,454,147
Non-trainable params: 0

・もちろん上記のモデルがベストではないはず
・activationがsoftmax (二値分類では"sigmoid")
・最後のDense層のニューロン数がクラス数(3)

前処理・ジェネレーター


from keras.preprocessing.image import ImageDataGenerator

train_dir = 'Downloads/ramen/train'
validation_dir = 'Downloads/ramen/validation'

# rescaled by 1./255
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        train_dir,     # directory
        target_size=(150, 150),     # input shape (resized to 150x150)
        batch_size=20,
        class_mode='categorical')    # categorical labels

validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=20,
        class_mode='categorical')

・二値分類との違い
→ class_modeが"categorical" (二値分類では"binary")

Found 7081 images belonging to 3 classes.
Found 1134 images belonging to 3 classes.

学習


history = model.fit_generator(
      train_generator,
      steps_per_epoch=354,
      epochs=20,
      validation_data=validation_generator,
      validation_steps=56)

Epoch 1/20
354/354 [==============================] - 606s 2s/step - loss: 1.0235 - acc: 0.4818 - val_loss: 1.0156 - val_acc: 0.4875
Epoch 2/20
354/354 [==============================] - 554s 2s/step - loss: 0.9353 - acc: 0.5576 - val_loss: 0.9883 - val_acc: 0.5116
Epoch 3/20
354/354 [==============================] - 557s 2s/step - loss: 0.8976 - acc: 0.5866 - val_loss: 0.9701 - val_acc: 0.5304
.
.
.
Epoch 18/20
354/354 [==============================] - 549s 2s/step - loss: 0.5529 - acc: 0.7716 - val_loss: 0.6864 - val_acc: 0.7071
Epoch 19/20
354/354 [==============================] - 549s 2s/step - loss: 0.5524 - acc: 0.7720 - val_loss: 0.6935 - val_acc: 0.7089
Epoch 20/20
354/354 [==============================] - 581s 2s/step - loss: 0.5374 - acc: 0.7826 - val_loss: 0.7253 - val_acc: 0.7000

テスト


from keras.models import load_model
from keras.preprocessing.image import ImageDataGenerator
model = load_model('ramen')

test_dir = 'downloads/ramen/test'
test_datagen = ImageDataGenerator(rescale=1./255)
test_generator = test_datagen.flow_from_directory(
    test_dir,
    target_size=(150,150),
    batch_size=20,
    class_mode='categorical')

test_loss, test_acc = model.evaluate_generator(test_generator, steps=54)
print('test loss:', test_loss)
print('test acc:', test_acc)

Found 1078 images belonging to 3 classes.
test loss: 0.6364766670069579
test acc: 0.7346938798732793

モデルをシンプルにしたら・・・


model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu',
                        input_shape=(150, 150, 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(GlobalAveragePooling2D())
model.add(layers.Dense(32, activation='relu'))
model.add(layers.Dense(3, activation='softmax'))

Total params: 245,059
Trainable params: 245,059
Non-trainable params: 0

学習結果


Epoch 1/30
354/354 [==============================] - 564s 2s/step - loss: 0.6535 - acc: 0.7249 - val_loss: 0.6807 - val_acc: 0.7116
Epoch 2/30
354/354 [==============================] - 557s 2s/step - loss: 0.6487 - acc: 0.7261 - val_loss: 0.6944 - val_acc: 0.7018
Epoch 3/30
354/354 [==============================] - 636s 2s/step - loss: 0.6383 - acc: 0.7301 - val_loss: 0.7004 - val_acc: 0.7143
Epoch 4/30
354/354 [==============================] - 627s 2s/step - loss: 0.6417 - acc: 0.7271 - val_loss: 0.6961 - val_acc: 0.7018
Epoch 5/30
354/354 [==============================] - 538s 2s/step - loss: 0.6432 - acc: 0.7340 - val_loss: 0.7023 - val_acc: 0.7045

51~55エポック目での学習結果
70%ぐらいから精度が上がらない

考察

おそらく、分類の基準となったのはスープの色
しょうゆラーメンの中でも色に幅がある(塩、みそでも同様)
→ その幅を理解させるにはデータが少なすぎる

データの質、量が結果に大きく関わる
データの量と精度の比較検証については下記の記事で
https://qiita.com/Phoeboooo/items/f450cefc70fe1bf788c8

8
8
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?