CNN_개_고양이_이진분류실습(모델링)

2024. 7. 9. 18:15Deep Learning

 

✅ 목표 설정

  1. 앞서 만든 NPZ 파일을 불러와서 사용해 보자
  2. CNN(합성곱 신경망)을 구현해 보자
  3. 신경망 성능 개선 및 전이 학습을 진행해 보자

# 드라이브 마운트

from google.colab import drive

drive.mount('/content/drive')

!pwd

%cd /content/drive/MyDrive/Colab Notebooks/Deep Learning

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# npz 파일 로딩

data = np.load('data/cats_dogs.npz')
data

# 데이터 분할

X_train = data['X_train']
X_test = data['X_test']
y_train = data['y_train']
y_test = data['y_test']
X_train.shape, X_test.shape, y_train.shape, y_test.shape

 

 

✅ CNN 모델링

  1. 모델 설계 및 구축
  2. 학습 및 평가 방법 설정
  3. 학습 및 시각화
  4. 모델 성능 평가
# 재료 import

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
from tensorflow.keras.optimizers import Adam
# 1. 모델 설계 및 구축

# 신경망 구현

# 뼈대 설정
model = Sequential()

# 특성 추출부 구현
model.add(Conv2D(filters=32,
                 kernel_size=(3,3),
                 padding = 'same', # padding 사용하겠다!
                 activation='relu',
                 input_shape=(224, 224, 3)))

model.add(MaxPooling2D(pool_size=(2,2)))

# ==========================================================

model.add(Conv2D(filters=64,
                 kernel_size=(3,3),
                 padding = 'same',
                 activation='relu'
                 ))

model.add(MaxPooling2D(pool_size=(2,2)))

# ==========================================================

model.add(Conv2D(filters=128,
                 kernel_size=(3,3),
                 padding = 'same',
                 activation='relu'
                 ))

model.add(MaxPooling2D(pool_size=(2,2)))

# ==========================================================

# 특성 추출부 구현 완료
# 전결합층 구현
model.add(Flatten())

model.add(Dense(units = 512, activation='relu'))
model.add(Dropout(0.35))

model.add(Dense(units = 256, activation='relu'))
model.add(Dropout(0.35))

model.add(Dense(units = 1, activation='sigmoid'))

# 전결합층 구현 완료
model.summary()

# 2. 학습 및 평가 방법 설정

model.compile(optimizer=Adam(learning_rate=0.001),
              loss='binary_crossentropy',
              metrics=['accuracy'])
# 학습 조기 중단 구현

f_early = EarlyStopping(monitor='val_loss',
                      patience=5)
# 3. 모델 학습

h = model.fit(X_train, y_train,
          epochs=100,
          batch_size=64,
          validation_split=0.2,
          callbacks=[f_early])
  • 📌 학습 중간에 표시되는 정확도와 검증 정확도를 확인한 결과 과대적합 발생!
  • 과대적합 제어 방법인 Dropout과 학습 조기 중단을 반영해도 과대적합 해소 X
  • 데이터가 모자라서 학습이 부족할 수 있다. -> 데이터를 증강해 보자.

✅ 데이터 증식

  • 부족한 데이터를 원본 데이터를 이용해서 증강 시켜주는 방법
  • 일반적으로 딥러닝 모델은 데이터가 많을수록 성능이 좋아진다
# 이미지 증식 진행 해보기

from tensorflow.keras.preprocessing.image import ImageDataGenerator

train_gen = ImageDataGenerator(
    rescale=1./255, # 데이터 스케일링, 현재는 연산을 통해 MinMax 스케일링 구현
    rotation_range=15, # 0 ~ 360도 사이의 각도 지정 -> 회전각
    width_shift_range=0.1, # 이미지를 좌우로 이동 10% 내외
    height_shift_range=0.1, # 이미지를 상하로 이동 10% 내외
    zoom_range=[0.8, 2.0], # 0.8배 또는 2배로 확대 및 축소
    shear_range=0.5, # 0.5 라디안 (호도) 내외 시계 방향으로 변형(약 28.6도)
    horizontal_flip=True, # 좌우 반전
    vertical_flip=True, # 상하 반전
    fill_mode='nearest' # 보간법 : 이미지 픽셀의 빈 공간을 채워주는 방법
)

test_gen = ImageDataGenerator(rescale=1./255)
# train / test 경로 잡아주기

train_dir = 'data/cats_and_dogs_filtered/train'
test_dir = 'data/cats_and_dogs_filtered/test'
# 이미지 증식 시작
# flow_from_directory() : 폴더의 경로 설정 및 옵션을 부여

train_generator = train_gen.flow_from_directory(
    train_dir, # 폴더 경로 지정
    target_size=(224, 224), # 변환할 이미지 사이즈
    batch_size=64, # 한 번에 변환 시킬 이미지 갯수
    class_mode='binary' # 라벨 번호 0번 부터 시작 / 폴더 알파벳 순서대로 읽어옴
)

test_generator = test_gen.flow_from_directory(
    test_dir, # 폴더 경로 지정
    target_size=(224, 224), # 변환할 이미지 사이즈
    batch_size=10, # 한 번에 변환 시킬 이미지 갯수
    class_mode='binary' # 라벨 번호 0번 부터 시작 / 폴더 알파벳 순서대로 읽어옴
)

# 라벨링 결과 확인

print(train_generator.class_indices)
print(test_generator.class_indices)

# 뼈대 설정
model2 = Sequential()

# 특성 추출부 구현

# conv2D_1 layer
# filter 갯수 32 / 패딩 사용 / 필터 사이즈 : (3,3) / 활성화 함수 relu / input_shape 지정
model2.add(Conv2D(filters = 32,
                  kernel_size = (3,3),
                  activation = 'relu',
                  input_shape = (224, 224, 3),
                  padding = 'same'))

# MaxPooling layer
# pool_size = (2,2)
model2.add(MaxPooling2D(pool_size = (2,2)))

# conv2D_2 layer
# filter 갯수 64 / 패딩 사용 / 필터 사이즈 : (3,3) / 활성화 함수 relu
model2.add(Conv2D(filters = 64,
                  kernel_size = (3,3),
                  activation = 'relu',
                  padding = 'same'))

# MaxPooling layer
# pool_size = (2,2)
model2.add(MaxPooling2D(pool_size = (2,2)))

# conv2D_3 layer
# filter 갯수 128 / 패딩 사용 / 필터 사이즈 : (3,3) / 활성화 함수 relu
model2.add(Conv2D(filters = 128,
                  kernel_size = (3,3),
                  activation = 'relu',
                  padding = 'same'))

# MaxPooling layer
# pool_size = (2,2)
model2.add(MaxPooling2D(pool_size = (2,2)))

# 전결합층 구현
# 데이터를 1차원으로 펴주는 층
model2.add(Flatten())

# 뉴런의 갯수 64 / 활성화 함수 : 렐루
model2.add(Dense(units = 64, activation = 'relu'))

# 드롭아웃 추가(비워두기)
model2.add(Dropout(0.35))

# 출력층 설정 (이진분류에 맞는 출력층 설정)
model2.add(Dense(units = 1, activation = 'sigmoid'))

model2.summary()

model2.compile(optimizer=Adam(learning_rate=0.001),
              loss='binary_crossentropy',
              metrics=['accuracy'])
# 학습 조기 중단 구현
# 이미지 증식을 진행 했을 때는 학습이 들쭉날쭉하게 진행이 된다.
# 학습 횟수와 기회를 늘려서 학습을 오래 시켜줄 필요가 있다.

f_early = EarlyStopping(monitor='val_accuracy',
                        patience=20)

epochs = 100

h2 = model2.fit_generator(generator = train_generator,
                    epochs=epochs,
                    validation_data=test_generator,
                    callbacks=[f_early])

 

 

✅ 전이학습

  • 기존에 학습이 잘 된 모델을 이용하는 방법
  • 전이학습에는 특성 추출 방식과 미세 조정 방식이 있음

# vgg16모델 import

from tensorflow.keras.applications import VGG16
vgg16 = VGG16(
    include_top=False, # 불러온 모델의 MLP 분류기를 쓸건가?
    weights='imagenet', # imgenet에서 사용했던 1000개 클래스에 대응하는 가중치를 사용하겠다(사전 학습 가중치 사용)
    input_shape=(224, 224, 3) # 이미지 사이즈
)

vgg16.summary()

# 특성 추출부에 MLP를 결합해주자
# 우리가 실습할 부분은 미세 조정 방식
# 동결 시킬 층을 확인하기 위해 불러온 VGG16 모델의 층 이름을 확인해보자

for layer in vgg16.layers:
  print(layer.name)

# 신경망 설계
# 뼈대 설정

transfer_model = Sequential()

# 미세 조정 방식 적용
# 분류기와 맞닿은 block5_conv3 층만 학습이 가능하도록 설정

for layer in vgg16.layers :
    if layer.name == "lock5_conv3" :
        layer.trainable = True
    else :
        layer.trainable = False

# 모델 구현
transfer_model.add(vgg16)

transfer_model.add(Flatten())

transfer_model.add(Dense(units = 128, activation = 'relu'))

# 출력층
transfer_model.add(Dense(units = 1, activation = 'sigmoid'))

 

# 모델 컴파일

transfer_model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])

 

# 모델 학습

transfer_model.fit(X_train, y_train,
                    epochs=10,
                    validation_split=0.3)

 

 

transfer_model.evaluate(X_test, y_test)

 

✅ 주의점

  • 한번 학습 가능하도록 설정된 층은 가중치가 변경
  • 만약 동결층을 바꿔서 다시 학습시키고 싶다면 처음부터 모델을 새롭게 import 해와야 한다.