728x90
반응형
# 모델 저장하기

model.save('cats_and_dogs_small_1.h5') # 저장 이름 h5로 저장해야 한다.

작은 데이터셋에 딥러닝을 적용하기 위한 핵심적인 기술 두 가지

  1. 사전 훈련된 네트워크로 특성을 추출하는 것
  2. 사전 훈련된 네트워크를 세밀하게 튜닝하는 것

 

작은 데이터셋 문제에서 딥러닝의 타당성

  • 딥러닝의 근본적인 특징은 훈련 데이터에서 특성 공학의 수작업 없이 흥미로운 특성을 찾을 수 있는 것
  • 컨브넷은 지역적이고 평행 이동으로 변하지 않는 특성을 학습하기 때문에 지각에 관한 문제에서 매우 효율적으로 데이터를 사용한다.
    • 매우 작은 이미지 데이터셋에서 어떤 종류의 특성 공학을 사용하지 않고 컨브넷을 훈련해도 납득할 만한 결과를 만들 수 있다.
  • 딥러닝 모델은 태생적으로 매우 다목적이다.

 

데이터 내려받기

  • 원본 데이터셋 https://www.kaggle.com/c/dogs-vs-cats/data
  • 강아지 vs 고양이 데이터셋
  • 중간 정도의 해상도를 가진 JPEG 파일

 

훈련, 검증, 테스트 폴더로 이미지 복사하기

# 훈련, 검증, 테스트 폴더로 이미지 복사하기

import os, shutil

original_dataset_dir = './datasets/train/train' # 원본 데이터셋을 압축 해제한 디렉토리 경로

base_dir = './datasets/cats_and_dogs_small' # 소규모 데이터셋을 저장할 디렉터리
if os.path.exists(base_dir):  # 반복적인 실행을 위해 디렉토리를 삭제합니다.
    shutil.rmtree(base_dir)   
os.mkdir(base_dir) # 생성

# 훈련, 검증, 테스트 분할을 위한 디렉터리
train_dir = os.path.join(base_dir, 'train')
os.mkdir(train_dir)
validation_dir = os.path.join(base_dir, 'validation')
os.mkdir(validation_dir)
test_dir = os.path.join(base_dir, 'test')
os.mkdir(test_dir)

# 훈련용 고양이 사진 디렉터리
train_cats_dir = os.path.join(train_dir, 'cats')
os.mkdir(train_cats_dir)

# 훈련용 강아지 사진 디렉터리
train_dogs_dir = os.path.join(train_dir, 'dogs')
os.mkdir(train_dogs_dir)

# 검증용 고양이 사진 디렉터리
validation_cats_dir = os.path.join(validation_dir, 'cats')
os.mkdir(validation_cats_dir)

# 검증용 강아지 사진 디렉터리
validation_dogs_dir = os.path.join(validation_dir, 'dogs')
os.mkdir(validation_dogs_dir)

# 테스트용 고양이 사진 디렉터리
test_cats_dir = os.path.join(test_dir, 'cats')
os.mkdir(test_cats_dir)

# 테스트용 강아지 사진 디렉터리
test_dogs_dir = os.path.join(test_dir, 'dogs')
os.mkdir(test_dogs_dir)

# 처음 1,000개 고양이 이미지를 train_cats_dir에 복사

fnames = ['cat.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(train_cats_dir, fname)
    shutil.copyfile(src, dst)
    
fnames = ['cat.{}.jpg'.format(i) for i in range(1000,1500)] # validation_cats_dir에 복사
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_cats_dir, fname)
    shutil.copyfile(src, dst)
    
fnames = ['cat.{}.jpg'.format(i) for i in range(1500,2000)] # test_cats_dir 에 복사
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_cats_dir, fname)
    shutil.copyfile(src, dst)

fnames = ['dog.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(train_dogs_dir, fname)
    shutil.copyfile(src, dst)
    
fnames = ['dog.{}.jpg'.format(i) for i in range(1000,1500)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_dogs_dir, fname)
    shutil.copyfile(src, dst)

fnames = ['dog.{}.jpg'.format(i) for i in range(1500,2000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_dogs_dir, fname)
    shutil.copyfile(src, dst)
        
    

 

사진 개수 확인

# 각 분할 사진 개수 카운트

print('훈련용 고양이 이미지 전체 개수 : ', len(os.listdir(train_cats_dir)))
print('훈련용 강아지 이미지 전체 개수 : ', len(os.listdir(train_dogs_dir)))
print('검증용 고양이 이미지 전체 개수 : ', len(os.listdir(validation_cats_dir)))
print('검증용 강아지 이미지 전체 개수 : ', len(os.listdir(validation_dogs_dir)))
print('테스트용 고양이 이미지 전체 개수 : ', len(os.listdir(test_cats_dir)))
print('테스트용 강아지 이미지 전체 개수 : ', len(os.listdir(test_dogs_dir)))

[그림1] 사진 개수 확인

 

 

네트워크 구성하기

  • Conv2D(relu 함수)와 MaxPooling2D 층을 번갈아 쌓은 컨브넷을 만든다.
# 강아지 vs 고양이 분류를 위한 소규모 컨브넷 만들기
from tensorflow.keras import layers
from tensorflow.keras import models

model = models.Sequential()
model.add(layers.Conv2D(32, (3,3), activation='relu',
                       input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(64, (3,3), activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128, (3,3), activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128, (3,3), activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='softmax'))

model.summary()

[그림2] 특성 맵의 차원 확인

 

모델 훈련 설정

# 모델의 훈련 설정하기

from tensorflow.keras import optimizers

model.compile(loss = 'binary_crossentropy',
             optimizer = optimizers.RMSprop(lr=1e-4),
             metrics=['accuracy'])

 

 

데이터 전처리

  1. 사진 파일을 읽기
  2. JPEG 콘텐츠를 RGB 픽셀 값으로 디코딩
  3. 그다음 부동 소수 타입의 텐서로 변환
  4. 픽셀 값(0에서 255 사이)의 스케일을 [0, 1] 사이로 조정한다. (신경망은 작은 입력 값을 선호한다.)

 

케라스에는  keras.preprocessing.image에 이미지 처리를 위한 헬퍼 도구들이 있다.

ImageDataGenerator 클래스는 디크스에 있는 이미지 파일을 전처리된 배치 텐서로 자동으로 바꾸어 주는 파이썬 제너레이터(generator)를 만들어 준다.

 

# ImageDataGenerator를 사용해 디렉터리에서 이미지 읽기

from tensorflow.keras.preprocessing.image import ImageDataGenerator

train_datagen = ImageDataGenerator(rescale=1./255) # 모든 이미지를 1/255 스케일로 조정
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        train_dir, # 타깃 디렉터리
        target_size=(150,150), # 모든 이미지 150 x 150 크기로 조정
        batch_size = 20, 
        class_mode='binary') # binary_crossentropy 손실을 사용하기 떼문에 이진 테이블 필요

validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150,150),
        batch_size=20,
        class_mode='binary')

 

배치 제너레이터 사용해 훈련

# 배치 제너레이터를 사용해 모델 훈련하기

history = model.fit_generator(
      train_generator,
      steps_per_epoch=100,
      epochs=30,
      validation_data=validation_generator,
      validation_steps=50)

 

모델 저장

model.save('cats_and_dogs_small_1.h5')

 

 

훈련 데이터와 검증 데이터에 대한 모델의 손실과 정확도 그래프

# 훈련의 정확도와 손실 그래프 그리기
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
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.figure()

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()

[그림3] 훈련 데이터와 검증 데이터에 대한 모델의 손실과 정확도 그래프

  • 훈련 샘플이 수 (2,000개)가 적기 때문에 과대적합은 가장 중요한 문제
  • 드롭아웃이나 가중치 감소(L2 규제)처럼 과대적합을 감소시킬 수 있는 여러 가지 기법들이 있지만
  • 컴퓨터 비전에 특화되어 있어서 딥러닝으로 이미지를 다룰 때 일반적으로 사용되는 데이터 증식을 시도

 

 

데이터 증식 사용

  • 과대적합은 학습할 샘플이 너무 적어 새로운 데이터에 일반화할 수 있는 모델을 훈련시킬 수 없기 때문에 발생한다.
  • 데이터 증식은 기존 훈련 샘플로부터 더 많은 훈련 데이터를 생성하는 방법이다.
    • 이 방법은 그럴듯한 이미지를 생성하도록 여러 가지 랜덤한 변환을 적용해 샘플을 늘린다.

 

ImageDataGenerator를 사용해 데이터 증식 설정

# ImageDataGenerator를 사용해 데이터 증식 설정하기

datagen = ImageDataGenerator(
    rotation_range=20,
    width_shift_range=0.1,
    height_shift_range=0.1,
    shear_range=0.1,
    zoom_range=0.1,
    horizontal_flip=True,
    fill_mode='nearest')

 

 

랜덤하게 증식된 훈련 이미지 그리기

# 랜덤하게 증식된 훈련 이미지 그리기

from tensorflow.keras.preprocessing import image

fnames = sorted([os.path.join(train_cats_dir, fname) for
                fname in os.listdir(train_cats_dir)])

img_path = fnames[3] # 증식할 이미지 선택

img = image.load_img(img_path, target_size = (150, 150)) # 이미지를 읽고 크기를 변경한다.

x = image.img_to_array(img) # (150, 150, 3) 크기의 넘파이 배열로 변환한다.
x = x.reshape((1,) + x.shape) # (1,150,150, 3) 크기로 변환한다.

i = 0
for batch in datagen.flow(x, batch_size = 1): # 랜덤하게 변환된 이미지 배치를 생성한다. 무한 반복되기 때문에 어느 지점에서 중지해야 한다.
    plt.figure(i)
    imgplot = plt.imshow(image.array_to_img(batch[0])) 
    i += 1
    if i % 4 == 0 :
        break

plt.show()

[그림4] 랜덤하게 증식된 훈련 이미지 그리기

 

  • 데이터 증식을 사용해 새로운 네트워크를 훈련시킬 때 네트워크에 같은 입력 데이터가 두 번 주입되지 않는다.
  • 하지만 적은 수의 원본 이미지에서 만들어졌기 때문에 상호 연관성이 크다.
  • 데이터 증식만으로는 과대적합을 제거하기 충분치 않을 수 있다.

 

드롭아웃을 추가한 컨브넷 정의

# 드롭아웃 층 추가

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu',
                        input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy',
              optimizer=optimizers.RMSprop(lr=1e-4),
              metrics=['acc'])

 

모델 저장

model.save('cats_and_dogs_small_2.h5')

 

그래프 그리기

acc = history.history['acc']
val_acc = history.history['val_acc']
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.figure()

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()

[그림5] 위 : 데이터 증식을 사용했을 때의 훈련 정확도와 검증 정확도 밑 : 데이터 증식을 사용했을 때 훈련 손실과 검증 손실

 

 

 

파이썬 제너레이터

  • 파이썬 제너레이터(generator)는 반복자(iterator)처럼 작동하는 객체로 for ... in  연산자에 사용할 수 있다.
  • 제너레이터는 yield 연산자를 사용해 만든다.
728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기