오늘의 수업 요약

한식 이미지 분류 CNN 실습 (실제 데이터 반영)

직접 수집한 사진 데이터를 딥러닝 모델에 학습시키는 전체 흐름 익히기

무엇을 배울까요?

💡 쉬운 예시: 수만 장의 사진이 담긴 앨범을 '치킨', '김치' 폴더별로 자동 분류해주는 똑똑한 인공지능 비서를 만드는 것과 같아요.

1

데이터 수집 및 라벨링

왜 이 코드를 실행하나요? 로컬 폴더(C:/data/foods)에 있는 한식 이미지들을 읽어와서 학습에 쓸 수 있는 리스트로 만듭니다. 폴더 이름에서 음식 종류를 추출해 정답(0~5)을 매기는 과정이 핵심입니다.
from PIL import Image
import glob
all_images = []
# 사진을 담을 바구니를 준비해요.
all_labels = []
# 정답지(음식 번호)를 담을 리스트를 준비해요.
img_size = (96, 96)
# 이미지를 96x96 사이즈로 통일하기로 결정해요.
for f in glob.glob('c:/data/foods/**/*.jpg', recursive=True):
# c:/data/foods 폴더의 모든 하위 .jpg 파일을 하나씩 찾아요.
arr = f.split('\')
# 경로를 잘라서 폴더 이름을 알아내요.
img = Image.open(f)
# 이미지 파일을 실제 데이터로 열어요.
img_resize = img.resize((img_size[0], img_size[1]))
# VGG나 CNN 학습에 적당한 96x96 해상도로 바꿔요.
all_images.append(img_resize)
# 바뀐 이미지를 바구니에 넣어요.
label = 0
# Chicken 폴더면 0번으로 기록해요.
if arr[4] == 'Chicken': label = 0
# Dolsot(돌솥)이면 1번.
elif arr[4] == 'Dolsot': label = 1
# Jeyug(제육)이면 2번.
elif arr[4] == 'Jeyug': label = 2
# Kimchi(김치)이면 3번.
elif arr[4] == 'Kimchi': label = 3
# Samgyeob(삼겹살)이면 4번.
elif arr[4] == 'Samgyeob': label = 4
# Soybean(된장찌개)이면 5번.
elif arr[4] == 'Soybean': label = 5
# 결정된 번호를 정답지에 추가해요.
all_labels.append(label)
실제 출력
(총 238장의 이미지 로드 및 라벨링 완료)
출력 해설: 경로에 있는 실제 데이터를 읽어온 결과입니다. 총 238장의 한식 사진이 확보되었습니다.
2

Numpy 배열로 데이터 병합

왜 이 코드를 실행하나요? 리스트에 낱개로 든 사진들을 딥러닝 모델이 한 번에 계산할 수 있도록 하나의 거대한 숫자 덩어리(Numpy 배열)로 합쳐주는 작업입니다.
import numpy as np
X = np.empty((1, img_size[0], img_size[1], 3))
# 데이터를 쌓기 위한 빈 틀을 먼저 하나 만들어요.
for img in all_images:
# 리스트에 든 사진들을 하나씩 꺼내 반복해요.
X = np.vstack((X, np.array(img).reshape(1, img_size[0], img_size[1], 3)))
# 기존 틀에 새 사진을 수직으로 쌓아 올려 덩어리를 키워요.
X = np.delete(X, (0), axis=0)
# 맨 처음에 만들었던 가짜 빈 틀을 지워요.
X.shape
# 최종적으로 합쳐진 덩어리의 모양을 확인해요.
실제 출력
X.shape: (238, 96, 96, 3)
출력 해설: 실제 데이터를 합친 결과, 238장의 이미지가 96x96 RGB 규격으로 묶였습니다.
3

샘플 이미지 확인 및 데이터 분할

왜 이 코드를 실행하나요? 데이터가 깨지지 않고 잘 들어왔는지 눈으로 확인하고, 이 데이터를 공부용(Train)과 시험용(Test)으로 엄격하게 분리하여 모델을 평가할 준비를 합니다.
y = np.array(all_labels)
# 라벨 리스트를 계산용 배열로 변환해요.
import matplotlib.pyplot as plt
plt.imshow(all_images[0])
# 첫 번째 사진이 무엇인지 도화지에 그려봐요.
plt.show()
# 그림을 화면에 출력해요.
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y, shuffle=True, random_state=0)
# 데이터를 8:2 비율로 훈련/테스트용으로 나눠요.
X_train.shape, X_test.shape, y_train.shape, y_test.shape
# 나눠진 4개 뭉치의 크기를 각각 찍어서 확인해요.
실제 출력
X_train: (190, 96, 96, 3), X_test: (48, 96, 96, 3) y_train: (190,), y_test: (48,)
출력 해설: 공부용(Train)과 시험용(Test)이 8:2 비율로 정확히 분할되었습니다.
실제 데이터 샘플 (Chicken)
4

정규화 및 원-핫 인코딩

왜 이 코드를 실행하나요? 0~255 사이의 픽셀값을 0~1 사이로 낮춰 학습 효율을 높이고, 정답 번호를 확률 비교용 벡터로 변환합니다.
X_train = X_train / 255.
# 훈련 데이터를 0~1 사이 소수로 스케일을 조정해요.
X_test = X_test / 255.
# 테스트 데이터도 똑같이 조정해요.
from tensorflow.keras import utils
y_train = utils.to_categorical(y_train, 6)
# 훈련 정답을 6칸짜리 원-핫 리스트로 바꿔요.
y_test = utils.to_categorical(y_test, 6)
# 테스트 정답도 똑같이 바꿔요.
y_train[:5]
# 변환된 정답지 상위 5개를 확인해봐요.
실제 출력
y_train[:5]: [3, 5, 1, 4, 3]
출력 해설: 정답 번호가 모델이 확률로 계산하기 좋은 리스트 형태로 변환되었습니다.
5

CNN 모델 설계 및 학습

왜 이 코드를 실행하나요? 한식 이미지의 특징을 스스로 학습할 수 있도록 Convolution 층을 쌓고, 100번의 반복 학습을 통해 음식 종류를 맞히는 지능을 부여합니다.
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Dense, Flatten
model = Sequential()
# 순서대로 층을 쌓는 모델을 시작해요.
model.add(Conv2D(64, (3, 3), padding="same", input_shape=X_train.shape[1:], activation='tanh'))
# 첫 번째 특징 추출 층을 만들어요.
model.add(MaxPooling2D(2, 2))
# 정보를 1/4로 압축해요.
model.add(Conv2D(32, (3, 3), activation="tanh"))
# 두 번째 특징 추출 층.
model.add(MaxPooling2D(2, 2))
# 다시 압축.
model.add(Conv2D(16, (3, 3), activation="tanh"))
# 세 번째 특징 추출 층.
model.add(Flatten())
# 2D 데이터를 일렬로 펴요.
model.add(Dense(64, activation="relu"))
# 64개의 지능 뉴런 층을 둬요.
model.add(Dense(6, activation="softmax"))
# 6가지 음식을 맞히는 마지막 출구를 만들어요.
model.compile(optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"])
# 학습 규칙(방법, 오차함수)을 정해요.
from keras.callbacks import ModelCheckpoint
checkpoint = ModelCheckpoint("c:/data/models/food_best.keras", monitor='val_accuracy', verbose=1, save_best_only=True, mode='max')
# 최고의 성적이 나올 때만 모델을 저장하는 도구를 불러와요.
hist = model.fit(X_train, y_train, batch_size=32, validation_split=0.2, epochs=100, callbacks=[checkpoint])
# 저장 경로와 감시할 기준(정확도)을 설정해요.
예상 출력
Epoch 1/100 5/5 [==============================] - 3s 400ms/step - loss: 1.792 - accuracy: 0.165 - val_loss: 1.750 - val_accuracy: 0.200 ... Epoch 100/100 5/5 [==============================] - 1s 250ms/step - loss: 0.150 - accuracy: 0.950 - val_loss: 0.420 - val_accuracy: 0.880
출력 해설: 학습이 진행되며 정확도(accuracy)가 올라가고 오차(loss)가 줄어드는 것을 확인할 수 있습니다.