π Model Improvement Strategy
κΈ°μ‘΄ CNN λͺ¨λΈμ λ€μ μ λ΅μ ν΅ν©νμ¬ μ±λ₯μ ν₯μμν¨ κ΅¬μ‘°λ₯Ό ꡬνν¨:
- λ°μ΄ν° μ¦κ° μ μ©: λ€μν μ΄λ―Έμ§ λ³νμ ν΅ν΄ νμ΅ λ°μ΄ν° λ€μμ± ν보
- μ μ΄νμ΅ λμ : MobileNetV2μ μ¬μ νμ΅λ νΉμ§ μΆμΆκΈ° μ¬μ©
- Dropout λ° Dense λ μ΄μ΄ μΆκ°: μ€λ²νΌν λ°©μ§ λ° λͺ¨λΈ ννλ ₯ ν₯μ
- EarlyStopping, ModelCheckpoint μ μ©: κ³Όμ ν© λ°©μ§ λ° μ΅μ λͺ¨λΈ μ μ₯
- λ°μ΄ν° μ¦κ° κ°ν: νμ , μ΄λ, νλ/μΆμ, λ°μ λ± λ³΅ν©μ μ¦κ° μ μ©
π¬ HARIBO_Dataset Preparation
- 5κ°μ§ ν리보 μ €λ¦¬ μ’ λ₯(bear, cola, egg, heart, ring)λ₯Ό μ§μ 촬μνμ¬ μ΄λ―Έμ§ λ°μ΄ν°μ μμ±
- λ€μν κ°λΒ·μ‘°λͺ Β·λ°°κ²½μμ μμ§λ μ΄λ―Έμ§ μ΄ 500μ₯ (κ° ν΄λμ€λΉ 100μ₯ λ΄μΈ)
- κ΅¬κΈ λλΌμ΄λΈμ μ λ‘λ ν, Google Colab νκ²½μμ μ€μ΅μ©μΌλ‘ μ°λ
π‘ Code : CNN with Transfer Learning & Augmentation
import matplotlib.pyplot as plt
import numpy as np
import os
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras import models, layers
from tensorflow.keras.optimizers import RMSprop
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
# β
κ΅¬κΈ λλΌμ΄λΈ λ§μ΄νΈ
from google.colab import drive
drive.mount('/content/drive')
# β
κ²½λ‘ μ€μ
dataset_path = '/content/drive/MyDrive/haribo_dataset'
model_save_path = '/content/drive/MyDrive/haribo_model.h5'
# β
λ°μ΄ν° μ¦κ° μ€μ
datagen = ImageDataGenerator(
rescale=1./255, # ν½μ
κ°μ 0~1 λ²μλ‘ μ κ·ν
validation_split=0.2, # μ 체 λ°μ΄ν° μ€ 20%λ₯Ό κ²μ¦μ©μΌλ‘ μ¬μ©
rotation_range=90, # μ΅λ Β±90λ λ²μ λ΄μμ 무μμ νμ
width_shift_range=0.1, # μ 체 λλΉμ 10%λ§νΌ μ’μ° μ΄λ
height_shift_range=0.1, # μ 체 λμ΄μ 10%λ§νΌ μν μ΄λ
shear_range=0.1, # μ λ¨ λ³ν (μ΄λ―Έμ§λ₯Ό κΈ°μΈμ΄λ ν¨κ³Ό)
zoom_range=0.1, # 10% λ²μ λ΄ λ¬΄μμ νλ/μΆμ
horizontal_flip=True, # μ΄λ―Έμ§λ₯Ό μ’μ°λ‘ 무μμ λ°μ
fill_mode='nearest' # λ³ν ν μκΈ΄ λΉ μμμ κ°μ₯ κ°κΉμ΄ ν½μ
λ‘ μ±μ
)
# β
λ°μ΄ν° λ‘λ©
train_generator = datagen.flow_from_directory(
dataset_path,
target_size=(96, 96),
batch_size=32,
class_mode='categorical',
subset='training',
shuffle=True
)
val_generator = datagen.flow_from_directory(
dataset_path,
target_size=(96, 96),
batch_size=32,
class_mode='categorical',
subset='validation',
shuffle=True
)
# β
ν΄λμ€ μ΄λ¦ μλ μΆμΆ
class_names = list(train_generator.class_indices.keys())
print("ν΄λμ€ μΈλ±μ€:", train_generator.class_indices)
# β
MobileNetV2 κΈ°λ° λͺ¨λΈ ꡬμ±
base_model = MobileNetV2(input_shape=(96, 96, 3), include_top=False, weights='imagenet')
base_model.trainable = False
model = models.Sequential([
base_model,
layers.GlobalAveragePooling2D(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.5),
layers.Dense(len(class_names), activation='softmax') # ν΄λμ€ μ μλ λ°μ
])
model.compile(optimizer=Adam(learning_rate=1e-4),
loss='categorical_crossentropy',
metrics=['accuracy'])
# β
μ½λ°± μ€μ
early_stop = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)
checkpoint = ModelCheckpoint('best_model.h5', save_best_only=True)
# β
νμ΅ μ€ν
history = model.fit(
train_generator,
validation_data=val_generator,
epochs=50,
callbacks=[early_stop, checkpoint],
verbose=2
)
# β
κ²°κ³Ό μκ°ν
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(acc))
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and Validation Accuracy')
plt.legend()
plt.show()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()
# β
νμ΅ μ΄λ―Έμ§ μμ
x_batch, y_batch = next(train_generator)
plt.figure(figsize=(10, 10))
for i in range(25):
plt.subplot(5, 5, i + 1)
plt.xticks([]); plt.yticks([]); plt.grid(False)
plt.imshow(x_batch[i])
label_idx = np.argmax(y_batch[i])
plt.xlabel(class_names[label_idx])
plt.tight_layout()
plt.show()
# β
λͺ¨λΈ μ μ₯ (.h5 νμΌ)
model.save(model_save_path)
print(f"λͺ¨λΈμ΄ μ μ₯λμμ΅λλ€: {model_save_path}")
β Result : νμ΅ κ²°κ³Ό μκ°ν λ° μμΈ‘ νμΈ
π Summary
- MobileNetV2λ₯Ό κΈ°λ°μΌλ‘ ν μ μ΄νμ΅ λͺ¨λΈμ΄ μ μ λ°μ΄ν°μ μμλ μ’μ μ±λ₯μ 보μ
- μ€μκ° μμΈ‘ νκ²½μλ μ΅μ νλ λͺ¨λΈ κ΅¬μ‘°λ‘ μ ν κ°λ₯ (On-Device AI μ μ© κ°λ₯)
π» Real-Time Inference Setup on Terminal
π 1. λλ ν 리 ꡬμ±
mkdir haribo_cam_classifier
cd haribo_cam_classifier
π 2. κ°μνκ²½ μμ± λ° ν¨ν€μ§ μ€μΉ
python3 -m venv venv
source venv/bin/activate
pip install tensorflow opencv-python-headless numpy
π₯ 3. νμ΅ν λͺ¨λΈ(.h5)μ Google Driveμμ λ€μ΄λ‘λνμ¬ λ³΅μ¬
haribo_model.h5 νμΌμ Google Driveμμ λ€μ΄λ°μ haribo_cam_classifier
λλ ν 리μ μμΉμν΄
πΌοΈ 4. ν΄λμ€ μ΄λ¦ νμΌ μμ± (class_names.json)
["bear", "cola", "egg", "heart", "ring"]
π‘ 5. μ€μκ° λΆλ₯ μ½λ μμ± (predict_cam.py)
import cv2
import numpy as np
import tensorflow as tf
import json
# λͺ¨λΈκ³Ό ν΄λμ€ μ΄λ¦ λ‘λ
model = tf.keras.models.load_model('haribo_model.h5')
with open('class_names.json', 'r') as f:
class_names = json.load(f)
def preprocess(frame):
img = cv2.resize(frame, (96, 96))
img = img.astype('float32') / 255.0
return np.expand_dims(img, axis=0)
cap = cv2.VideoCapture(2)
if not cap.isOpened():
print("μΉ΄λ©λΌλ₯Ό μ΄ μ μμ΅λλ€.")
exit()
print("μ €λ¦¬ λΆλ₯ μμ! (Q ν€λ₯Ό λλ₯΄λ©΄ μ’
λ£)")
while True:
ret, frame = cap.read()
if not ret:
break
input_img = preprocess(frame)
pred = model.predict(input_img)
label = class_names[np.argmax(pred)]
# μμΈ‘ κ²°κ³Ό νλ©΄μ μΆλ ₯
cv2.putText(frame, f'Prediction: {label}', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.imshow('Haribo Classifier', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
π§© 6. OpenCV μ€μΉ (GUI μ§μ ν¬ν¨)
pip install opencv-python
βΆοΈ 7. μ€μκ° μμΈ‘ μ€ν
python3 predict_cam.py
β 8. κ²°κ³Ό μ 리
π¬ λͺ¨λΈ μμΈ‘μ μν 5κ° ν΄λμ€ ν리보 μν μ 체 μ΄λ―Έμ§
π§ͺ μμΈ‘ μμ: heart
π§ͺ μμΈ‘ μμ: ring
π§ͺ μμΈ‘ μμ: cola
π§ͺ μμΈ‘ μμ: egg
π§ͺ μμΈ‘ μμ: bear