https://www.kaggle.com/c/dogs-vs-cats/data에서 자료 내려받기!
- 디렉토리 설정하기
import os, shutil
original_dataset_dir = './datasets/cats_and_dogs/train'
original_dataset_dir
- 저장할 디렉터리 공간 만들기
# 소규모 데이터셋을 저장할 디렉터리
base_dir = './datasets/cats_and_dogs_small'
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)
## 다음 500개의 고양이 이미지를 validation_cats_dir 에 복사
fnames = ['cat.{}.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_cats_dir, fname)
shutil.copyfile(src, dst)
## 다음 500개의 고양이 이미지를 test_cats_dir에 복사
fnames = ['cat.{}.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_cats_dir, fname)
shutil.copyfile(src, dst)
## 처음 1,000개의 강아지 이미지를 train_dogs_dir에 복사
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)
## 다음 500개의 강아지 이미지를 validation_cats_dir 에 복사
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)
## 다음 500개의 강아지 이미지를 test_cats_dir에 복사
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)))
- 네트워크 구성하기
Conv2D와 MaxPooling2D층을 번갈아 쌓은 컨브넷을 만들것
이전보다 이미지가 크고 복잡한 문제이기 때문에 네트워크를 크게 만듦
Conv2D + MaxPooling2D 단계를 하나 더 추가함 이렇게 하면 네트워크 용량을 늘리고 Flatten층의 크기가 너무 커지지 않도록 특성 맵의 크기를 줄일 수 있음. 150x150 크기의 입력으로 시작해서 Flatten층 이전에 7x7크기의 특성맵으로 줄어듬
# 이진분류 문제이므로 네트워크는 하나의 유닛과 sigmoid활성화 함수로 끝남
from keras import layers
from 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='sigmoid'))
- 층 구성 확인해보기
model.summary()
- 모델 훈련 설정하기
from keras import optimizers
model.compile(loss='binary_crossentropy',
optimizer = optimizers.RMSprop(lr=1e-4),
metrics=['acc'])
- 데이터 전처리
JEPG인 데이터 파일을 네트워크에 주입하려면
1. 사진파일읽기
2. JPEG 콘텐츠를 RGB 픽셀값으로 디코딩
3. 부동소수 타입의 텐서로 변환
4. 픽셀값의 스케일을 [0,1]로 조정
케라스에는 위와같은 단계를 자동으로 처리하는 유틸리티가 있음
from keras.preprocessing.image import ImageDataGenerator
# 모든 이미지를 1/255로 스케일을 조정합니다
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
# 타깃 디렉터리
train_dir,
# 모든 이미지를 150 × 150 크기로 바꿉니다
target_size=(150, 150),
batch_size=20,
# binary_crossentropy 손실을 사용하기 때문에 이진 레이블이 필요합니다
class_mode='binary')
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.fit 하는 과정에서 알수없는 오류가 계속떴는데 일단은
PIL Image 오류가 뜨면
pip install pillow
해주면 되고 진짜 이상한 오류가 있었는데 그거는 아나콘다랑 주피터 모두 끄고 다시 재실행 하니까 됐다.
- 모델 저장하기
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()
데이터 증식하기
++ 코드 추가
- rotation_range : 랜덤하게 사진을 회전시킬 각도 범위
- width_shift_range와 height_shift_range는 사진을 수평과 수직으로 랜덤하게 평행이동
- shear_range 랜덤하게 전단변환 -> y축으로 살짝 돌리는?
- horizontal_flip 은 랜덤하게 이미지를 수평으로 뒤집음
- fill_mode는 회전이나 가로/세로 이동으로 인해 새롭게 생성해야 할 픽셀을 채울 전략
datagen = ImageDataGenerator(
rotation_range = 40,
width_shift_range=0.2,
height_shift_range = 0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')
- x = x.reshape((1,) + x.shape)
파이썬에서 튜플이나 리스트 2개를 더하면 하나의 튜플로 연결됨. flow() 메서드는 배치데이터를 기대하기 때문에 샘플 데이터에 배치 차원을 추가하여 4D 텐서로 만듦
from 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)
# (1,150,150,3) 크기로 변환
x = x.reshape((1,) + x.shape)
# flow() 메서드는 랜덤하게 변환된 이미지의 배치를 생성
# 무한반복되기 때문에 어느지점에서 중지
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()
- 증식 모델 다시 설정하기
기존의 Flatten() 아래에 새로운 코드 추가
model.add(layers.Dropout(0.5))
- 학습 사이즈도 변경
train_generator의 batch_size = 32 로 변경
validation_generator의 batch_size = 32 로 변경
증식했기 때문에 2000개 돌릴 때 돌리는걸 100번을 하겠다는 뜻 : 1번 돌릴 때 32개를 가져옴
즉, 20->32 12개는 저장 안되서 메모리 효율이 좋음
history = model.fit_generator(
train_generator,
steps_per_epoch=100,
epochs = 100,
validation_data=validation_generator,
validation_steps=50)
epochs를 100으로 했기때문에,,, 굉장히 많은 시간이 걸림...
* 실행결과
'딥러닝' 카테고리의 다른 글
0807 미세조정 fine-tuning (0) | 2019.08.07 |
---|---|
0807 사전훈련된컨브넷사용하기 - VGG16합성곱 (0) | 2019.08.07 |
0806 합성곱 신경망 (0) | 2019.08.06 |
0806 +) 가중치규제, 드롭아웃 (0) | 2019.08.06 |
0806 영화분류하기 - 과대적합, 과소적합 (GPU Tensorflow) (0) | 2019.08.06 |