๐ŸงฉTensorflow Certification ์ทจ๋“ํ•˜๊ธฐ - part 5. ์‹ค์ „ (Rock-Paper-Scissors)

vincaยท2023๋…„ 1์›” 2์ผ
0

๐ŸŒ• AI/DL -Tenserflow Certification

๋ชฉ๋ก ๋ณด๊ธฐ
5/11
post-thumbnail

Rock-Paper-Scissors (๊ฐ€์œ„ ๋ฐ”์œ„ ๋ณด)

  • Convolution Neural Network (ํ•ฉ์„ฑ๊ณฑ ์‹ ๊ฒฝ๋ง)๋ฅผ ํ™œ์šฉํ•œ ์ด๋ฏธ์ง€ ๋ถ„๋ฅ˜ (Image Classification)

For this task you will build a classifier for Rock-Paper-Scissors
based on the rps dataset.
IMPORTANT: Your final layer should be as shown, do not change the
provided code, or the tests may fail
IMPORTANT: Images will be tested as 150x150 with 3 bytes of color depth
So ensure that your input layer is designed accordingly, or the tests
may fail.
NOTE THAT THIS IS UNLABELLED DATA.
You can use the ImageDataGenerator to automatically label it
and we have provided some starter code.

์ด ์ž‘์—…์—์„œ๋Š” Rock-Paper-Scissors์— ๋Œ€ํ•œ ๋ถ„๋ฅ˜๊ธฐ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
rps ๋ฐ์ดํ„ฐ ์…‹์„ ๊ธฐ๋ฐ˜์œผ๋กœํ•ฉ๋‹ˆ๋‹ค.
์ค‘์š” : ์ตœ์ข… ๋ ˆ์ด์–ด๋Š” ๊ทธ๋ฆผ๊ณผ ๊ฐ™์•„์•ผํ•ฉ๋‹ˆ๋‹ค.
์ค‘์š” : ์ด๋ฏธ์ง€๋Š” 3 ๋ฐ”์ดํŠธ 150x150์˜ ์ปฌ๋Ÿฌ์‚ฌ์ง„์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฉ๋‹ˆ๋‹ค.
๋”ฐ๋ผ์„œ ์ž…๋ ฅ ๋ ˆ์ด์–ด๊ฐ€ ๊ทธ์— ๋”ฐ๋ผ ์„ค๊ณ„๋˜์—ˆ๊ฑฐ๋‚˜ ํ…Œ์ŠคํŠธ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค.
ImageDataGenerator๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž๋™์œผ๋กœ ๋ ˆ์ด๋ธ”์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


Solution

์ˆœ์„œ ์š”์•ฝ

  1. import: ํ•„์š”ํ•œ ๋ชจ๋“ˆ import
  2. ์ „์ฒ˜๋ฆฌ: ํ•™์Šต์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  3. ๋ชจ๋ธ๋ง(model): ๋ชจ๋ธ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  4. ์ปดํŒŒ์ผ(compile): ๋ชจ๋ธ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  5. ํ•™์Šต (fit): ๋ชจ๋ธ์„ ํ•™์Šต์‹œํ‚ต๋‹ˆ๋‹ค.

1. import ํ•˜๊ธฐ

ํ•„์š”ํ•œ ๋ชจ๋“ˆ์„ import ํ•ฉ๋‹ˆ๋‹ค.

import urllib.request
import zipfile
import numpy as np
from IPython.display import Image

import tensorflow as tf
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dropout, Dense
from tensorflow.keras.models import Sequential
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.callbacks import ModelCheckpoint

2.1 ์ „์ฒ˜๋ฆฌ (Load dataset)

tensorflow-datasets๋ฅผ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.

๊ฐ€์œ„๋ฐ”์œ„๋ณด์— ๋Œ€ํ•œ ์†์˜ ์‚ฌ์ง„์„ ๊ฐ€์ง€๊ณ  ๊ฐ€์œ„์ธ์ง€, ๋ฐ”์œ„์ธ์ง€, ๋ณด์ž๊ธฐ์ธ์ง€ ๋ถ„๋ฅ˜ํ•˜๋Š” classification ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

url = 'https://storage.googleapis.com/download.tensorflow.org/data/rps.zip'
urllib.request.urlretrieve(url, 'rps.zip')
local_zip = 'rps.zip'
zip_ref = zipfile.ZipFile(local_zip, 'r')
zip_ref.extractall('tmp/')
zip_ref.close()

2.2 ์ „์ฒ˜๋ฆฌ (Define Folder)

๋ฐ์ดํ„ฐ์…‹์˜ ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•ด ์ฃผ์„ธ์š” (root ํด๋”์˜ ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.)

# training dir
TRAINING_DIR = "tmp/rps/"

2.3 ์ „์ฒ˜๋ฆฌ (ImageDataGenerator)

  • rescale: ์ด๋ฏธ์ง€์˜ ํ”ฝ์…€ ๊ฐ’์„ ์กฐ์ •
  • rotation_range: ์ด๋ฏธ์ง€ ํšŒ์ „
  • width_shift_range: ๊ฐ€๋กœ ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋™
  • height_shift_range: ์„ธ๋กœ ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋™
  • shear_range: ์ด๋ฏธ์ง€ ๊ตด์ ˆ
  • zoom_range: ์ด๋ฏธ์ง€ ํ™•๋Œ€
  • horizontal_flip: ํšก ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋ฏธ์ง€ ๋ฐ˜์ „
  • fill_mode: ์ด๋ฏธ์ง€๋ฅผ ์ด๋™์ด๋‚˜ ๊ตด์ ˆ์‹œ์ผฐ์„ ๋•Œ ๋นˆ ํ”ฝ์…€ ๊ฐ’์— ๋Œ€ํ•˜์—ฌ ๊ฐ’์„ ์ฑ„์šฐ๋Š” ๋ฐฉ์‹
  • validation_split: validation set์˜ ๊ตฌ์„ฑ ๋น„์œจ
training_datagen = ImageDataGenerator(
    rescale=1. / 255,
    rotation_range=40,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest', 
    validation_split=0.2
    )

2.4 ์ „์ฒ˜๋ฆฌ (Make Generator)

ImageDataGenerator๋ฅผ ์ž˜ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ๋‹ค๋ฉด, flow_from_directory๋กœ ์ด๋ฏธ์ง€๋ฅผ ์–ด๋–ป๊ฒŒ ๊ณต๊ธ‰ํ•ด ์ค„ ๊ฒƒ์ธ๊ฐ€๋ฅผ ์ง€์ •ํ•ด ์ฃผ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

  • train / validation set ์ „์šฉ generator๋ฅผ ๋ณ„๋„๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  • batch_size๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. (32)
  • target_size๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. (150 x 150). ์ด๋ฏธ์ง€๋ฅผ ์•Œ์•„์„œ ํƒ€๊ฒŸ ์‚ฌ์ด์ฆˆ ๋งŒํผ ์ž˜๋ผ๋‚ด์–ด ๊ณต๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.
  • class_mode๋Š” ์ถœ๋ ฅ์ธต์˜ activation์ด softmax์ธ ๊ฒฝ์šฐ 'categorical', sigmoid์ธ ๊ฒฝ์šฐ 'binary'๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
  • subset์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. (training / validation)

training_generator์— ๋Œ€ํ•œ from_from_directory๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

training_generator = training_datagen.flow_from_directory(TRAINING_DIR, 
                                                          batch_size=32, 
                                                          target_size=(150, 150), 
                                                          class_mode='categorical', 
                                                          subset='training',
                                                         )

validation_generator์— ๋Œ€ํ•œ from_from_directory๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

validation_generator = training_datagen.flow_from_directory(TRAINING_DIR, 
                                                          batch_size=32, 
                                                          target_size=(150, 150), 
                                                          class_mode='categorical',
                                                          subset='validation', 
                                                         )

504 ๊ฐœ์˜ ์ด๋ฏธ์ง€๊ฐ€ ์ถœ๋ ฅ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์‹œ๊ฐํ™” ํ•ด๋ณด๊ธฐ

import matplotlib.pyplot as plt

class_map = {
    0: 'Paper',
    1: 'Rock', 
    2: 'Scissors'
}

print('์˜ค๋ฆฌ์ง€๋„ ์‚ฌ์ง„ ํŒŒ์ผ')

original_datagen = ImageDataGenerator(rescale=1./255)
original_generator = original_datagen.flow_from_directory(TRAINING_DIR, 
                                                          batch_size=128, 
                                                          target_size=(150, 150), 
                                                          class_mode='categorical'
                                                         )

for x, y in original_generator:
    print(x.shape, y.shape)
    print(y[0])
    
    fig, axes = plt.subplots(2, 5)
    fig.set_size_inches(15, 6)
    for i in range(10):
        axes[i//5, i%5].imshow(x[i])
        axes[i//5, i%5].set_title(class_map[y[i].argmax()], fontsize=15)
        axes[i//5, i%5].axis('off')
    plt.show()
    break
    
print('Augmentation ์ ์šฉํ•œ ์‚ฌ์ง„ ํŒŒ์ผ')
    
for x, y in training_generator:
    print(x.shape, y.shape)
    print(y[0])
    
    fig, axes = plt.subplots(2, 5)
    fig.set_size_inches(15, 6)
    for i in range(10):
        axes[i//5, i%5].imshow(x[i])
        axes[i//5, i%5].set_title(class_map[y[i].argmax()], fontsize=15)
        axes[i//5, i%5].axis('off')
    
    plt.show()
    break

Convolution Neural Network (CNN)

CNN - activation - Pooling ๊ณผ์ •์„ ํ†ตํ•ด ์ด๋ฏธ์ง€ ๋ถ€๋ถ„ ๋ถ€๋ถ„์˜ ์ฃผ์š”ํ•œ Feature ๋“ค์„ ์ถ”์ถœํ•ด ๋ƒ…๋‹ˆ๋‹ค.

CNN์„ ํ†ตํ•ด ์šฐ๋ฆฌ๋Š” ๋‹ค์–‘ํ•œ 1๊ฐœ์˜ ์ด๋ฏธ์ง€๋ฅผ filter๋ฅผ ๊ฑฐ์นœ ๋‹ค์ˆ˜์˜ ์ด๋ฏธ์ง€๋กœ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

filter์˜ ์‚ฌ์ด์ฆˆ๋Š” 3 X 3 ํ•„ํ„ฐ๋ฅผ ์ž์ฃผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค

๋˜ํ•œ, 3 X 3 ํ•„ํ„ฐ๋ฅผ ๊ฑฐ์นœ ์ด๋ฏธ์ง€์˜ ์‚ฌ์ด์ฆˆ๋Š” 2px ๋งŒํผ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ค„์–ด๋“ญ๋‹ˆ๋‹ค.

3. ๋ชจ๋ธ ์ •์˜ (Sequential)

์ด์ œ Modeling์„ ํ•  ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค.

model = Sequential([
    # Conv2D, MaxPooling2D ์กฐํ•ฉ์œผ๋กœ ์ธต์„ ์Œ“์Šต๋‹ˆ๋‹ค. ์ฒซ๋ฒˆ์งธ ์ž…๋ ฅ์ธต์˜ input_shape์€ (150, 150, 3)์œผ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
    Conv2D(64, (3, 3), activation='relu', input_shape=(150, 150, 3)),
    MaxPooling2D(2, 2), 
    Conv2D(64, (3, 3), activation='relu'),
    MaxPooling2D(2, 2), 
    Conv2D(128, (3, 3), activation='relu'),
    MaxPooling2D(2, 2), 
    Conv2D(128, (3, 3), activation='relu'),
    MaxPooling2D(2, 2), 
    # 2D -> 1D๋กœ ๋ณ€ํ™˜์„ ์œ„ํ•˜์—ฌ Flatten ํ•ฉ๋‹ˆ๋‹ค.
    Flatten(), 
    # ๊ณผ์ ํ•ฉ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•˜์—ฌ Dropout์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
    Dropout(0.5),
    Dense(512, activation='relu'),
    # Classification์„ ์œ„ํ•œ Softmax 
    # ์ถœ๋ ฅ์ธต์˜ ๊ฐฏ์ˆ˜๋Š” ํด๋ž˜์Šค์˜ ๊ฐฏ์ˆ˜์™€ ๋™์ผํ•˜๊ฒŒ ๋งž์ถฐ์ค๋‹ˆ๋‹ค (3๊ฐœ), activation๋„ ์žŠ์ง€๋งˆ์„ธ์š”!
    Dense(3, activation='softmax'),
])

๋ชจ๋ธ ๊ฒฐ๊ณผ ์š”์•ฝ

model.summary()

4. ์ปดํŒŒ์ผ (compile)

  1. optimizer๋Š” ๊ฐ€์žฅ ์ตœ์ ํ™”๊ฐ€ ์ž˜๋˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ธ 'adam'์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  2. loss์„ค์ •
  • ์ถœ๋ ฅ์ธต activation์ด sigmoid ์ธ ๊ฒฝ์šฐ: binary_crossentropy
  • ์ถœ๋ ฅ์ธต activation์ด softmax ์ธ ๊ฒฝ์šฐ:
    • ์›ํ•ซ์ธ์ฝ”๋”ฉ(O): categorical_crossentropy
    • ์›ํ•ซ์ธ์ฝ”๋”ฉ(X): sparse_categorical_crossentropy)
  1. ์ฐธ๊ณ : ImageDataGenerator๋Š” ์ž๋™์œผ๋กœ Label์„ ์›ํ•ซ์ธ์ฝ”๋”ฉ(one-hot encoding) ํ•ด์ค๋‹ˆ๋‹ค.
  2. metrics๋ฅผ 'acc' ํ˜น์€ 'accuracy'๋กœ ์ง€์ •ํ•˜๋ฉด, ํ•™์Šต์‹œ ์ •ํ™•๋„๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋ง ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc'])

ModelCheckpoint: ์ฒดํฌํฌ์ธํŠธ ์ƒ์„ฑ

val_loss ๊ธฐ์ค€์œผ๋กœ epoch ๋งˆ๋‹ค ์ตœ์ ์˜ ๋ชจ๋ธ์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•˜์—ฌ, ModelCheckpoint๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

  • checkpoint_path๋Š” ๋ชจ๋ธ์ด ์ €์žฅ๋  ํŒŒ์ผ ๋ช…์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • ModelCheckpoint์„ ์„ ์–ธํ•˜๊ณ , ์ ์ ˆํ•œ ์˜ต์…˜ ๊ฐ’์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
checkpoint_path = "tmp_checkpoint.ckpt"
checkpoint = ModelCheckpoint(filepath=checkpoint_path, 
                             save_weights_only=True, 
                             save_best_only=True, 
                             monitor='val_loss', 
                             verbose=1)

5. ํ•™์Šต (fit)

epochs=25
history = model.fit(train_data,
                    validation_data=(valid_data),
                    epochs=20,
                    callbacks=[checkpoint],
                   )

ํ•™์Šต ์™„๋ฃŒ ํ›„ Load Weights (ModelCheckpoint)

ํ•™์Šต์ด ์™„๋ฃŒ๋œ ํ›„์—๋Š” ๋ฐ˜๋“œ์‹œ load_weights๋ฅผ ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด, ์—ด์‹ฌํžˆ ModelCheckpoint๋ฅผ ๋งŒ๋“  ์˜๋ฏธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

# checkpoint ๋ฅผ ์ €์žฅํ•œ ํŒŒ์ผ๋ช…์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
model.load_weights(checkpoint_path)

์˜ค์ฐจ ๋ฐ ์ •ํ™•๋„ ์‹œ๊ฐํ™”

ํ•™์Šต Loss (์˜ค์ฐจ) / accuracy (์ •ํ™•๋„)์— ๋Œ€ํ•œ ์‹œ๊ฐํ™”

plt.figure(figsize=(12, 9))
plt.plot(np.arange(1, epochs+1), history.history['acc'])
plt.plot(np.arange(1, epochs+1), history.history['loss'])
plt.title('Acc / Loss', fontsize=20)
plt.xlabel('Epochs')
plt.ylabel('Acc / Loss')
plt.legend(['acc', 'loss'], fontsize=15)
plt.show()

๊ฒ€์ฆ์ด ํ•˜๊ณ ์‹ถ๋‹ค๋ฉด..

model.evaluate(x_valid, y_valid)
profile
๋ถ‰์€ ๋ฐฐ ์˜ค์ƒ‰ ๋”ฑ๋‹ค๊ตฌ๋ฆฌ ๊ฐœ๋ฐœ์ž ๐ŸฆƒCloud & DevOps

0๊ฐœ์˜ ๋Œ“๊ธ€