본문 바로가기
AI & 데이터분석

[AI 모델링] Image 데이터 모델링 예제

by 잼들리 2025. 2. 14.

(Image) Cats&Dogs 분류 예측

  • 데이터: 개 고양이 이미지 데이터
  • 목표: 개 고양이 이미지를 분류하는 최고 성능의 모델 만들기
  • 달성 목표: Accuracy 90% 이상
  • 제출 형식: 모델 파일, 정답 csv 파일

0. 라이브러리 임포트 및 데이터 다운로드

# 필요한 라이브러리들을 임포트
import os
from glob import glob
from PIL import Image

import numpy as np
import matplotlib.pyplot as plt

from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report

import tensorflow as tf
from tensorflow.keras.models import Sequential, load_model
from tensorflow.keras.layers import Conv2D, Dropout, Flatten, Dense, MaxPooling2D, Rescaling, GlobalAveragePooling2D
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
from tensorflow.keras.optimizers import Adam

from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras import Input
from tensorflow.keras import Model

from keras.utils import to_categorical
from tensorflow.keras.preprocessing import image_dataset_from_directory
# 데이터셋 다운로드 및 압축 해제
!wget cat_and_dogs.zip
!unzip -q cats_and_dogs.zip
# cats_and_dogs 디렉토리 내용 확인
!ls -l cats_and_dogs

 

A. 이미지 데이터셋 생성 + CNN 모델 구현

1. 데이터 전처리 및 탐색

# 학습 및 검증용 이미지 데이터 경로 설정
train_cat_path = 'cats_and_dogs/train/cats/'
train_dog_path = 'cats_and_dogs/train/dogs/'
validation_cat_path = 'cats_and_dogs/validation/cats/'
validation_dog_path = 'cats_and_dogs/validation/dogs/'
# 고양이와 강아지 이미지 파일 목록 불러오기
train_cat_file = os.listdir(train_cat_path)
train_dog_file = os.listdir(train_dog_path)
validation_cat_file = os.listdir(validation_cat_path)
validation_dog_file = os.listdir(validation_dog_path)

train_cat_file[:2], train_dog_file[:2]
# 고양이와 강아지 이미지 데이터 시각화
for img_file in train_cat_file[:2]:
  img = Image.open(train_cat_path+img_file)
  plt.title(img_file+': cat')
  plt.imshow(img)
  plt.show()

for img_file in train_dog_file[:2]:
  img = Image.open(train_dog_path+img_file)
  plt.title(img_file+": dog")
  plt.imshow(img)
  plt.show()
# 이미지와 라벨을 저장하는 함수 정의
def save_img(path, label):
  files = glob(path+'*jpg')  # jpg 파일 목록 가져오기
  for img_file in files:
    img = Image.open(img_file).resize((128,128))  # 이미지 크기 조정
    img = np.array(img)/255.  # 이미지 정규화 (0-1 사이 값으로)
    img_list.append(img)  # 이미지 리스트에 추가
    label_list.append(label)  # 라벨 리스트에 추가
    
# 빈 리스트 초기화
img_list, label_list = [], []

# 각 경로에서 이미지 로드 (고양이=0, 강아지=1)
save_img(train_cat_path, 0)
save_img(train_dog_path, 1)
save_img(validation_cat_path, 0)
save_img(validation_dog_path, 1)

# 리스트를 numpy 배열로 변환
img_list_arr = np.array(img_list)
label_list_arr = np.array(label_list)

# 배열 형태 확인
img_list_arr.shape, label_list_arr.shape

3. 데이터셋 분할

# 데이터를 학습용(70%)과 테스트용(30%)으로 분할
x_train, x_test, y_train, y_test = train_test_split(
		img_list_arr, label_list_arr, 
		test_size=0.3, 
		shuffle=True, 
		stratify=label_list_arr, 
		random_state=41
)

4. 기본 CNN 모델 구현

# 모델 학습을 위한 하이퍼파라미터 설정
num_epochs = 10
batch_size = 32
learning_rate = 0.001
dropout_rate = 0.5
input_shape = (128, 128, 3)
# 간단한 CNN 모델 구성: Conv2D 레이어 2개와 Dense 레이어 2개로 구성
model = Sequential()
model.add(Conv2D(32, kernel_size=(5,5), strides=(1,1), padding='same', activation='relu', input_shape=input_shape))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2)))
model.add(Conv2D(64, (3,3), activation='relu', padding='same'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.2))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.3))
model.add(Dense(2, activation='softmax'))
# 모델 컴파일: Adam 옵티마이저와 손실 함수, 평가 지표 설정
model.compile(
	optimizer=Adam(learning_rate), 
	loss='sparse_categorical_crossentropy', 
	metrics=['accuracy']
)
model.summary()
# Early Stopping: 검증 손실이 5번 연속으로 개선되지 않으면 학습 중단
es = EarlyStopping(
	monitor='val_loss', 
	verbose=1, 
	patience=5, 
	mode='min'
)

# ModelCheckpoint: 가장 좋은 성능의 모델을 저장
cp = ModelCheckpoint(
	'best_model.keras', 
	monitor='val_loss', 
	verbose=1, 
	save_best_only=True
)
# 모델 학습: 지정된 에폭 동안 학습을 진행하고 검증 데이터로 성능 평가
history = model.fit(
    x_train, y_train,
    epochs=num_epochs,
    batch_size=batch_size,
    validation_data=(x_test, y_test),
    callbacks=[es, cp]
)

5. 모델 성능 평가

# 저장된 최고 성능의 모델을 불러와서 테스트 데이터로 평가
best_model = load_model('best_model.keras')
best_model.evaluate(x_test, y_test)

B. image_dataset_from_directory + 사전학습 모델 활용

1. 데이터셋 준비

input_shape = (128, 128, 3)
batch_size = 32
num_classes = 5
# 이미지 패스 지정
train_img_path ='/content/cats_and_dogs/train'
valid_img_path ='/content/cats_and_dogs/validation'
# image_dataset_from_directory로 이미지 데이터셋 생성
# 폴더 구조를 기반으로 라벨링, 배치 처리, 데이터 섞기 수행

# 학습용 데이터셋 생성
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
    directory=train_img_path,      # 학습 이미지가 있는 경로
    label_mode="categorical",      # 라벨 형식 (원핫인코딩)
    batch_size=batch_size,         # 배치 크기
    image_size=(128, 128),         # 이미지 크기 지정
    seed=42,                       # 랜덤 시드
    shuffle=True,                  # 데이터 섞기
)

# 테스트용 데이터셋 생성
test_ds = tf.keras.preprocessing.image_dataset_from_directory(
    directory=valid_img_path,      # 검증 이미지가 있는 경로
    label_mode="categorical",      # 라벨 형식 (원핫인코딩)
    batch_size=batch_size,         # 배치 크기
    image_size=(128, 128),         # 이미지 크기 지정
    seed=42,                       # 랜덤 시드
    shuffle=False,                 # 데이터 순서 유지
)
# Class 이름 확인
train_ds.class_names
batch_img, batch_label = next(iter(train_ds))
batch_img.shape, batch_label.shape

2. 사전학습 모델 준비

# 사전학습된 모델 종류 확인
dir(tf.keras.applications)
# MobileNetV2 모델을 기본 모델로 사용

base_model = tf.keras.applications.MobileNetV2(
	input_shape=(128, 128, 3), # input_shape: 입력 이미지 크기 (128x128, RGB)
	weights='imagenet', # weights='imagenet': ImageNet으로 사전학습된 가중치 사용
	include_top=False # include_top=False: 분류기 층 제외하고 특징 추출기만 사용
)
base_model.summary()

3. MobileNetV2 모델 Fine-tuning 및 학습

# MobileNet V2 베이스 모델의 가중치를 고정하여 학습되지 않도록 설정
# 이렇게 하면 사전학습된 가중치를 그대로 사용하게 됨
base_model.trainable = False
# 모델 구조 정의
inputs = tf.keras.Input(shape=(128, 128, 3))  # 입력층: 128x128 RGB 이미지

# 이미지 전처리: 픽셀값을 -1~1 범위로 정규화
x = tf.keras.layers.Rescaling(1./127.5, offset=-1)(inputs)

# MobileNetV2 기본 모델 통과 (특징 추출)
x = base_model(x, training=False)

# 특징맵을 1차원으로 변환 (7x7x1280 -> 1280)
x = tf.keras.layers.GlobalAveragePooling2D()(x)

# 출력층: 2개 클래스(고양이/강아지) 분류
output = tf.keras.layers.Dense(2, activation='softmax')(x)

# 최종 모델 생성
model = tf.keras.Model(inputs=inputs, outputs=output)
model.summary()
# 모델 컴파일 설정
model.compile(
    optimizer=Adam(learning_rate),  # 최적화 알고리즘 설정
    loss='categorical_crossentropy', # 손실 함수: 다중 분류용
    metrics=['accuracy'] # 평가 지표: 정확도
)
# Early Stopping: 검증 손실이 3번 연속 개선되지 않으면 학습 중단
es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=3)

# ModelCheckpoint: 가장 좋은 성능의 모델을 저장
checkpoint_path = "my_checkpoint.keras"
checkpoint = ModelCheckpoint(
	filepath=checkpoint_path,
	save_best_only=True,  # 최고 성능 모델만 저장
	monitor='val_loss',   # 검증 손실 기준으로 모니터링
	verbose=1            # 저장 과정 출력
)
# MobileNetV2 모델로 전이학습 수행 (정확도 97% 달성)
# 데이터셋: image_dataset_from_directory 함수로 생성
# 학습 설정: 10 에폭, 배치 크기 32개

# 모델 학습 실행
history = model.fit(
    train_ds,                # 학습용 데이터셋
    validation_data=test_ds, # 검증용 데이터셋
    epochs=2,               # 학습 반복 횟수
    callbacks=[es, checkpoint] # 콜백 함수: 조기종료, 체크포인트 저장
)

4. 모델 성능 평가

# 저장된 최고 성능의 모델을 불러오기
my_model = load_model('./my_checkpoint.keras')

# 테스트 데이터셋으로 모델 성능 평가 (손실값과 정확도 출력)
my_model.evaluate(test_ds)

5. 정답파일 저장

# ID 초기화
id_counter = 1
data = []

# test_ds에서 배치별로 처리
for batch_x, batch_y in test_ds:
    # 모델 예측 수행
    pred_probs = my_model.predict(batch_x)

    # 예측 확률값을 정수형 라벨로 변환
    pred_labels = np.argmax(pred_probs, axis=1)

    # 클래스 이름 가져오기
    class_names = test_ds.class_names
    pred_classes = [class_names[label] for label in pred_labels]

    # ID 추가
    for label in pred_classes:
        data.append({"id": id_counter, "label": label})
        id_counter += 1

# 데이터프레임 생성
df = pd.DataFrame(data)

# CSV 파일로 저장 (엑셀에서 한글 깨짐 방지)
df.to_csv("result.csv", index=False, encoding="utf-8-sig")
# 샘플 이미지 확인
batch_img, batch_label = next(iter(train_ds))

i = 0
for batch_img, batch_label in train_ds.take(1):
  if i == 0 :
    print(batch_img[i].shape)
    plt.imshow(batch_img[i]/255)
  i = i + 1

댓글