전이 학습(transfer learning) - 1
이제 거의 막바지에 도달했습니다. 열심히 해보자구요. 주말에 이 책을 끝내는 것이 목표입니다! (좀 많이 늦긴 했지만...)
전이학습(transfer learning)은 수만 장에 달하는 기존의 이미지에서 학습한 정보를 가져와 프로젝트에 활용하는 것입니다. 방대한 자료를 통해 미리 학습한 가중치 값을 가져와 프로젝트에 사용하는 방법으로 컴퓨터 비전, 자연어 처리 등에서 예측율을 높이고 있습니다. 많은 데이터가 필요한 딥러닝에서 데이터가 충분하지 않을 때 활용할 수 있는 방법이기도 합니다.
딥러닝은 데이터에 따라 방식이 달라집니다. 정답을 알려주는(레이블이 있는) 지도학습(supervised learning)과 정답을 알려주지 않는(레이블이 없는) 비지도 학습(upsupervised learning)이 있습니다.
앞서 해본 GAN이나 오토인코더는 정답을 주지 않고 했으므로 비지도 학습에 분류됩니다.
반면에 이번에 해볼 실습은 'MRI 뇌 사진을 보고 치매 환자의 뇌와 일반인의 뇌를 예측하는 것'으로 각 사진마다 치매나 일반인으로 클래스가 주어지므로 지도 학습이라고 할 수 있습니다. 이번에도 이미지를 보고 클래스를 분류하기 때문에 CNN을 사용하겠습니다.
데이터는 MRI 단면 이미지 280장으로 되어있고 치매 140장과 일반인 140장으로 구성되어 있습니다. 160개는 train 폴더, 120장은 test 폴더에 담겨있다고 합니다. 그 안에서 치매는 ad, 일반인은 normal 폴더에 넣어주었다고 합니다.
이번 글에서는 전이 학습을 하지 않은 이미지 학습을 진행해보겠습니다.
데이터를 mnist처럼 keras에서 가져오는 것이 아니기 때문에 flow_from_dirctory()로 데이터를 불러와야 합니다.
그리고 데이터 수도 늘려주겠습니다.
train_datagen = ImageDataGenerator(rescale=1./255, #이미지의 크기
horizontal_flip=True, # 수평 대칭 이미지 50%
width_shift_range=0.1, # 전체 크기의 15%에서 좌우 이동.
height_shift_range=0.1, # 위, 아래로 이동
#rotation_range=5, # 정해진 각도만큼 회전
#shear_range=0.7, # 좌표 하나 고정, 나머지 이동.
#zoom_range=1.2, # 확대 또는 축소.
#vertical_flip=True, # 수직 대칭 이미지
#fill_mode='nearest' # 빈 공간을 채우기 (nearest: 가장 비슷한 색)
)
rescale : 이미지 크기 바꾸기 (정규화 과정)
horizontal_flip, vertical_flip : 이미지를 수평, 수직으로 뒤집기
zoom_range : 정해진 범위 안에서 축소 또는 확대
width_shift_range, height_shift_range : 정해진 범위 안에서 수평 또는 수직으로 랜덤하게 평행 이동
rotation_range : 정해진 각도만큼 회전
shear_range : 좌표 하나 고정 후 나머지 이동
fill_mode : 빈공간 채우기
MRI 사진에서는 좌우 대칭이 필요없기 때문에 horizontal_flip만 사용하고 수직, 수평 이동을 사용하겠습니다. 필요없는 데이터로 학습시간을 늘릴 필요는 없으니까요.
반면 테스트셋은 과적합을 방지하기 위해 실제 정보 그대로 유지하는 것이 좋아 크기만 바꾸어 주겠습니다.
test_datagen = ImageDataGenerator(rescale=1./255)
이제 이미지를 불러오겠습니다.
train_generator = train_datagen.flow_from_directory(
'./data-ch20/train',
target_size=(150, 150),
batch_size=5,
class_mode='binary')
test_generator = test_datagen.flow_from_directory(
'./data-ch20/test',
target_size=(150, 150),
batch_size=5,
class_mode='binary')
CNN으로 모델을 만들어 주고
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150,150,3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.summary()
모델 실행 옵션과 조기중단을 설정해주고 오차를 저장합니다.
# 모델 컴파일
model.compile(loss='binary_crossentropy', optimizer=optimizers.Adam(learning_rate=0.0002), metrics=['accuracy'])
# 학습의 조기 중단
early_stopping_callback = EarlyStopping(monitor='val_loss', patience=5)
# 모델을 실행
history = model.fit(
train_generator,
epochs=100,
validation_data=test_generator,
validation_steps=10,
callbacks=[early_stopping_callback])
# 검증셋과 학습셋의 오차 저장
y_vloss = history.history['val_loss']
y_loss = history.history['loss']
최종 코드이고 실행해보면
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation, Dropout, Flatten, Conv2D, MaxPooling2D
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras import optimizers
import numpy as np
import matplotlib.pyplot as plt
#데이터 불러오기
!git clone https://github.com/taehojo/data-ch20.git
# 학습셋의 변형
train_datagen = ImageDataGenerator(rescale=1./255, #이미지의 크기
horizontal_flip=True, # 수평 대칭 이미지 50%
width_shift_range=0.1, # 전체 크기의 15%에서 좌우 이동.
height_shift_range=0.1, # 위, 아래로 이동
#rotation_range=5, # 정해진 각도만큼 회전
#shear_range=0.7, # 좌표 하나 고정, 나머지 이동.
#zoom_range=1.2, # 확대 또는 축소.
#vertical_flip=True, # 수직 대칭 이미지
#fill_mode='nearest' # 빈 공간을 채우기 (nearest: 가장 비슷한 색)
)
train_generator = train_datagen.flow_from_directory(
'./data-ch20/train',
target_size=(150, 150),
batch_size=5,
class_mode='binary')
# 테스트셋 이미지 부풀리기 과정을 진행 안함.
test_datagen = ImageDataGenerator(rescale=1./255)
test_generator = test_datagen.flow_from_directory(
'./data-ch20/test',
target_size=(150, 150),
batch_size=5,
class_mode='binary')
#CNN 적용
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150,150,3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.summary()
# 모델 컴파일
model.compile(loss='binary_crossentropy', optimizer=optimizers.Adam(learning_rate=0.0002), metrics=['accuracy'])
# 학습의 조기 중단
early_stopping_callback = EarlyStopping(monitor='val_loss', patience=5)
# 모델을 실행
history = model.fit(
train_generator,
epochs=100,
validation_data=test_generator,
validation_steps=10,
callbacks=[early_stopping_callback])
# 검증셋과 학습셋의 오차 저장
y_vloss = history.history['val_loss']
y_loss = history.history['loss']
# 그래프 표현
x_len = np.arange(len(y_loss))
plt.plot(x_len, y_vloss, marker='.', c="red", label='Testset_loss')
plt.plot(x_len, y_loss, marker='.', c="blue", label='Trainset_loss')
plt.legend(loc='upper right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()
이렇게 나옵니다. loss가 잘 줄어들고 있습니다.
참고: [모두의 딥러닝]