지금까지의 학습 순서
-> Linear Regression
-> Logistic Regression(binary classification)
-> Multinomial Classification(정확도 92%)
-> 비정형 Data -> DNN 사용(정확도 95%)
-> 특징만 뽑아내서 학습 -> CNN 사용(정확도 98%)
-> 사용하는 DATA -> MNIST data 이용
오늘 진행할 내용
실사 이미지를 이용하여 학습시키기. 개와 고양이 분류
예제 데이터
: Dogs VS Cats 예제는 우리에게 주어진게 image file
25,000장 (dogs : 12,500장 / cats : 12,500장)
https://www.kaggle.com/c/dogs-vs-cats/data
작업 내용
1. 그림파일을 읽어들여서
2. JPG file을 RGB pixel값으로 decoding
3. 정규화 진행
4. file (CSV) 하나로 만들 예정(local에서 진행)
jupyter home/data/cats_dogs/train/
위 디렉토리에 개, 고양이 이미지파일 25000장을 위치시킨다.
conda activate data_env
conda install tqdm
conda install -c conda-forge ipywidgets
conda install -c conda-forge opencv
설치 끝났으면 jupyter notebook 입력해서 실행
# Dogs vs Cats (Kaggle) 분석하기
import numpy as np
import pandas as pd
from PIL import Image
import matplotlib.pyplot as plt
import random
import os
import cv2 as cv
from sklearn import utils
from tqdm.notebook import tqdm
# 파일 경로
train_dir = './data/cats_dogs/train/'
# img = 파일 이름
def labeling(img):
class_name = img.split('.')[0]
if class_name == 'cat': return 0
elif class_name == 'dog': return 1
# label data와 pixel data 담을 변수
x_data = []
t_data = []
# os.listdir(): 인자로 준 폴더 경로 안에 있는 모든 파일들 이름 리스트 ):
for img in tqdm(os.listdir(train_dir),
total=len(os.listdir(train_dir)),
position=0,
leave=True):
# 위에서 만든 labeling()을 통해 이름이 cat이면 0 / dog면 1 반환
label_data = labeling(img)
# 이미지 파일 경로 설정
path = os.path.join(train_dir, img)
# 이미지 파일 nd.array로 불러오기 (cv2.imread())
# 형태만 필요해서 흑백으로 불러오기 (cv2.IMREAD_GRAYSCALE)
# 이미지 pixel size 조정하기
img_data = cv.resize(cv.imread(path, cv.IMREAD_GRAYSCALE), (80, 80))
# 리스트 변수에 담아주기
t_data.append(label_data)
x_data.append(img_data.ravel()) # 이미지 shape이 2차원: (80,80)
# 이게 그대로 들어가면 차원 하나 더 있어서 결국 3차원이므로
# 1차원으로 바꿔주기: ravel()
# labeling data ==> DataFrame으로 만들기
t_df = pd.DataFrame({
'label': t_data
})
# 이미지 픽셀 data ==> DataFrame으로 만들기
x_df = pd.DataFrame(x_data)
# 2개 dataframe 합치기
df = pd.merge(t_df, x_df, left_index=True, right_index=True)
# utils.shuffle() ==> pandas dataframe 행을 shuffle 한다.
shuffled_df = utils.shuffle(df)
# DataFrame을 csv 파일로 저장하기
result = shuffled_df.to_csv('C:/python_ML/data/cat_dog/train.csv', index=False)
진행률이 100%여도 끝난 게 아니다. 100%는 금방 되는데, 시스템에서 메모리를 엄청 잡고 있으며(필자의 경우 7GB정도), 제대로 완료되면 설정한 위치에 파일이 하나 생성되고, 메모리를 반환해준다.(반환 후 파이썬은 700MB 정도)
위에서 만든 train_full.csv
파일을 구글 드라이브에 업로드
(파일명은 cat_dog_full.csv
로 변경)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Flatten, Dense, Conv2D, MaxPooling2D, Dropout
from tensorflow.keras.optimizers import Adam, RMSprop
import matplotlib.pyplot as plt
없음
# 1. Raw Data Loading
df = pd.read_csv('/content/drive/MyDrive/한국SW산업협회 교육-Java 과정/파이썬 실습/cat_dog_full.csv')
display(df.head(), df.shape) # (25000, 6401)
# 6401 에서 1 : 개인지 고양이인지 표기
# 2. 이미지 데이터(픽셀 정보), 라벨 데이터
label_data = df['label'].values
img_data = df.drop('label', axis=1, inplace=False).values # 2차원 ndarray
없음
# 3. 샘플 이미지 확인
plt.imshow(img_data[150:151].reshape(80,80), cmap='gray')
plt.show()
학습용 데이터를 70% 잡아주기
# 4. data split (train 7 : test 3)
x_data_train, x_data_test, t_data_train, t_data_test = \
train_test_split(img_data, label_data, test_size=0.3, random_state=0)
없음
# 5. 정규화 처리
scaler = MinMaxScaler()
scaler.fit(x_data_train)
x_data_train_norm = scaler.transform(x_data_train)
scaler.fit(x_data_test)
x_data_test_norm = scaler.transform(x_data_test)
없음
# Model 생성
# 학습 데이터 가공
model = Sequential()
model.add(Conv2D(filters=32,
kernel_size=(3,3),
activation='relu',
padding='same',
input_shape=(80,80,1)))
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(Conv2D(filters=128,
kernel_size=(3,3),
padding='same',
activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(filters=256,
kernel_size=(3,3),
padding='same',
activation='relu'))
model.add(Conv2D(filters=256,
kernel_size=(3,3),
padding='same',
activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
없음
# Input Layer
model.add(Flatten())
# Dropout Layer
model.add(Dropout(rate=0.5))
# Hidden Layer
model.add(Dense(units=256,
kernel_initializer='he_normal',
activation='relu'))
# Output Layer
model.add(Dense(units=1,
kernel_initializer='he_normal',
activation='sigmoid'))
# units=1
# 결과에는 이미지 하나 당 개인지 고양이인지 판단만 하면 된다.
print(model.summary())
오래 걸린다.
# 학습 실행
# Optimizer
model.compile(optimizer=RMSprop(learning_rate=1e-4),
loss='binary_crossentropy',
metrics=['accuracy'])
# Learning
history = model.fit(x_data_train_norm.reshape(-1,80,80,1),
t_data_train.reshape(-1,1),
epochs=100,
batch_size=100,
verbose=1,
validation_split=0.3)
# x_data_train_norm.reshape(-1,80,80,1)
# CNN에서는 입력이 무조건 4차원이어야 한다.
실행 후 약 1분쯤 지났을 때
너무 느리니까 리소스 런타임에서 하드웨어 가속기를 GPU로 변경하자.
GPU로 수행한 결과
# Evaluation
print(model.evaluate(x_data_test_norm.reshape(-1,80,80,1),
t_data_test.reshape(-1,1)))
# 결과 그래프
train_acc = history.history['accuracy']
train_loss = history.history['loss']
validation_acc = history.history['val_accuracy']
validation_loss = history.history['val_loss']
fig = plt.figure()
fig_1 = fig.add_subplot(1,2,1)
fig_2 = fig.add_subplot(1,2,2)
fig_1.plot(train_acc, color='b', label='training accuracy')
fig_1.plot(validation_acc, color='r', label='validation accuracy')
fig_1.set_title('Accuracy')
fig_1.legend()
fig_2.plot(train_loss, color='b', label='training loss')
fig_2.plot(validation_loss, color='r', label='validation loss')
fig_2.set_title('Loss')
fig_2.legend()
plt.tight_layout()
plt.show()
25000장의 학습 결과가 좋지 않다. CNN까지 사용했는데도. (결과정확도 : 86%)
빨간색 선이 어느 순간부터 다시 중가하고 있잖아. 과적합 문제가 생긴 거지.
실제 (현업)로 사용할 때는 위의 case보다 학습이 더 안 된다.
이를 실제 코드로 작성해서 실행해보자.(일부분만 사용해서 학습 진행하기)
파일명
230406-Cats_dogs_SMALL_CNN
# 일부 이미지 분리(총 4000개)
import os, shutil
original_dataset_dir = './data/cats_dogs/train'
## directory 생성 ##
base_dir = 'data/cat_dog_small'
os.mkdir(base_dir)
train_dir = os.path.join(base_dir,'train').replace('\\','/')
os.mkdir(train_dir)
validation_dir = os.path.join(base_dir,'validation').replace('\\','/')
os.mkdir(validation_dir)
test_dir = os.path.join(base_dir,'test').replace('\\','/')
os.mkdir(test_dir)
train_cats_dir = os.path.join(train_dir,'cats').replace('\\','/')
os.mkdir(train_cats_dir)
train_dogs_dir = os.path.join(train_dir,'dogs').replace('\\','/')
os.mkdir(train_dogs_dir)
validation_cats_dir = os.path.join(validation_dir,'cats').replace('\\','/')
os.mkdir(validation_cats_dir)
validation_dogs_dir = os.path.join(validation_dir,'dogs').replace('\\','/')
os.mkdir(validation_dogs_dir)
test_cats_dir = os.path.join(test_dir,'cats').replace('\\','/')
os.mkdir(test_cats_dir)
test_dogs_dir = os.path.join(test_dir,'dogs').replace('\\','/')
os.mkdir(test_dogs_dir)
## file 복사 ##
fnames = ['cat.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
src = os.path.join(original_dataset_dir,fname).replace('\\','/')
dst = os.path.join(train_cats_dir, fname).replace('\\','/')
shutil.copyfile(src,dst)
fnames = ['cat.{}.jpg'.format(i) for i in range(1000,1500)]
for fname in fnames:
src = os.path.join(original_dataset_dir,fname).replace('\\','/')
dst = os.path.join(validation_cats_dir, fname).replace('\\','/')
shutil.copyfile(src,dst)
fnames = ['cat.{}.jpg'.format(i) for i in range(1500,2000)]
for fname in fnames:
src = os.path.join(original_dataset_dir,fname).replace('\\','/')
dst = os.path.join(test_cats_dir, fname).replace('\\','/')
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).replace('\\','/')
dst = os.path.join(train_dogs_dir, fname).replace('\\','/')
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).replace('\\','/')
dst = os.path.join(validation_dogs_dir, fname).replace('\\','/')
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).replace('\\','/')
dst = os.path.join(test_dogs_dir, fname).replace('\\','/')
shutil.copyfile(src,dst)
import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D
from tensorflow.keras.layers import Flatten, Dense, Dropout
from tensorflow.keras.optimizers import Adam, RMSprop
from tensorflow.keras.preprocessing.image import ImageDataGenerator
train_dir = './data/cat_dog_small/train'
validation_dir = './data/cat_dog_small/validation'
없음
# ImageDataGenerator 생성
# 모든 이미지 데이터의 값을 1/255로 scaling(MinMaxScaling)
train_datagen = ImageDataGenerator(rescale=1/255)
validation_datagen = ImageDataGenerator(rescale=1/255)
train_generator = train_datagen.flow_from_directory(
train_dir, # target directory
classes=['cats', 'dogs'], # cats, dogs 순서로 label 0,1
# (생략하면 폴더순서로 label적용)
target_size=(150,150), # image size scaling
batch_size=20, # 한번에 20개의 이미지를 가져온다.
# label에 상관없이 가져온다.
class_mode='binary') # 고양이와 멍멍이만 존재하므로
# 2진 분류이기 때문에 binary
# 다중분류인 경우 'categorical'(기본값),
# 'sparse' 이용가능
# 오토인코더처럼 입력을 target으로 하는 경우 'input'
validation_generator = validation_datagen.flow_from_directory(
validation_dir,
classes=['cats', 'dogs'],
target_size=(150,150),
batch_size=20,
class_mode='binary')
model = Sequential()
model.add(Conv2D(filters=32,
kernel_size=(3,3),
activation='relu',
input_shape=(150,150,3)))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(filters=64,
kernel_size=(3,3),
activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(filters=128,
kernel_size=(3,3),
activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(filters=128,
kernel_size=(3,3),
activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(units=512,
activation='relu'))
model.add(Dense(units=1,
activation='sigmoid'))
print(model.summary())
model.compile(optimizer=RMSprop(learning_rate=1e-4),
loss='binary_crossentropy',
metrics=['accuracy'])
## keep in mind that by default, batch_size is 32 in model.fit()
## steps_per_epoch = len(X_train) // batch_size
history = model.fit(train_generator,
steps_per_epoch=100,
epochs=30,
validation_data=validation_generator,
validation_steps=50)
당연히 학습이 더 잘 안 됐다.
-> 그럼 어떻게 해야 할까?
해결방식 2가지
# data augmentation
from tensorflow.keras.preprocessing import image
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt
datagen = ImageDataGenerator(rotation_range=20,
# 지정된 각도 범위내에서 임의로 원본이미지를 회전
width_shift_range=0.1,
height_shift_range=0.1,
# 지정된 방향 이동 범위내에서 임의로 원본이미지를 이동.
# 수치는 전체 넓이의 비율(실수)
shear_range=0.1,
# 밀림 강도 범위내에서 임의로 원본이미지를 변형.
# 수치는 시계반대방향으로 밀림 강도를 라디안으로 표시
zoom_range=0.1,
# 지정된 확대/축소 범위내에서 임의로 원본이미지를 확대/축소.
# "1-수치"부터 "1+수치"사이 범위로 확대/축소
horizontal_flip=True,
vertical_flip=True,
# 수평, 수직방향으로 뒤집기.
fill_mode='nearest')
img = image.load_img('./data/cat_dog_small/train/cats/cat.3.jpg',
target_size=(150,150))
x = image.img_to_array(img) # (150,150,3)
x = x.reshape((1,) + x.shape) # (1,150,150,3)
fig = plt.figure(figsize=(10,10))
axs = []
for i in range(20):
axs.append(fig.add_subplot(4,5,i+1))
idx = 0
for batch in datagen.flow(x, batch_size=1):
imgplot = axs[idx].imshow(image.array_to_img(batch[0]))
idx += 1
if idx % 20 == 0:
break
fig.tight_layout()
plt.show()
import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D
from tensorflow.keras.layers import Flatten, Dense, Dropout
from tensorflow.keras.optimizers import Adam, RMSprop
from tensorflow.keras.preprocessing.image import ImageDataGenerator
train_dir = './data/cat_dog_small/train'
validation_dir = './data/cat_dog_small/validation'
없음
# ImageDataGenerator 생성 - 설정
# 모든 이미지 데이터의 값을 1/255로 scaling 하면서 augmentation
train_datagen = ImageDataGenerator(rescale=1/255,
rotation_range=40,
width_shift_range=0.1,
height_shift_range=0.1,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True)
# Validation data에 대해서는 당연히 증식을 사용하면 안된다.
validation_datagen = ImageDataGenerator(rescale=1/255)
없음
train_generator = train_datagen.flow_from_directory(
train_dir, # target directory
classes=['cats', 'dogs'], # cats, dogs 순서로 label 0,1
# (생략하면 폴더순서로 label적용)
target_size=(150,150), # image size scaling
batch_size=32, # 한번에 32개의 이미지를 가져온다.
# label에 상관없이 가져온다.
class_mode='binary') # 고양이와 멍멍이만 존재하므로
# 2진 분류이기 때문에 binary
# 다중분류인 경우 'categorical'(기본값),
# 'sparse' 이용가능
# 오토인코더처럼 입력을 target으로 하는 경우 'input'
validation_generator = validation_datagen.flow_from_directory(
validation_dir,
classes=['cats', 'dogs'],
target_size=(150,150),
batch_size=32,
class_mode='binary')
model = Sequential()
model.add(Conv2D(filters=32,
kernel_size=(3,3),
activation='relu',
input_shape=(150,150,3)))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(filters=64,
kernel_size=(3,3),
activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(filters=128,
kernel_size=(3,3),
activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(filters=128,
kernel_size=(3,3),
activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dropout(rate=0.5))
model.add(Dense(units=512,
activation='relu'))
model.add(Dense(units=1,
activation='sigmoid'))
print(model.summary())
model.compile(optimizer=RMSprop(learning_rate=1e-4),
loss='binary_crossentropy',
metrics=['accuracy'])
## keep in mind that by default, batch_size is 32 in model.fit()
## steps_per_epoch = len(X_train) // batch_size
## validation_steps = len(X_test) // batch_size
history = model.fit(train_generator,
steps_per_epoch=62,
epochs=100,
validation_data=validation_generator,
validation_steps=31)
커널이 터져버렸다... 메모리를 너무 많이 먹나..?
결과에서 None이 아니라 학습이 진행되면 정상.
데이터 경로만 적당히 변경한 후, 실행
실행 5분 후
한참 걸린다...
n분 후... (37분? 그 이상 걸린 것 같은데..?)