general ML, DL, NLP/딥러닝

9. CNN 코드 - cats & dogs

김아다만티움 2022. 4. 21. 20:43

*본 게시물은 22-1학기 연세대학교 일반대학원 딥러닝을이용한비정형데이터분석(이상엽 교수님) 수업 내용을 정리한 것입니다.

 

  이번 포스트에서는 개와 고양이를 구분하는 cats and dogs task를 수행한다고 가정합니다. 케라스에서 데이터셋을 다운받는 것이 아닌, 사용자가 가진 이미지를 사용하려면 이미지를 폴더에 따로 따로 담아주어야 합니다. 여기서는 라벨별로 따로 담아주었습니다. 

이미지 분류 시 라벨은 cat, dog 2가지가 됩니다

데이터를 위와 같이 정리한 후, 이전에 사용한 CNN 코드로 분류 task를 수행할 수 있습니다. 

 

(1) 첫 번째로 필요한 모든 프레임워크, 라이브러리 등을 import하는 의존성 준비 코드입니다.

1
2
3
4
5
6
7
8
9
# 의존성 준비
import tensorflow as tf
from tensorflow.keras import datasets, layers, models, optimizers
 
import math
import numpy as np
from glob import glob
from tensorflow.keras.preprocessing import image
import matplotlib.pyplot as plt
cs

 

(2) 두 번째 코드는 실질적으로 CNN을 사용하여 학습을 시키는 내용을 담고 있습니다. 이번 task는 저번과 다르게 모델을 build하는 'build 함수'를 지정(def)하여 수행하려고 합니다. 때문에 우선적으로 변수에 값을 저장하는 것과, 함수를 만드는 과정이 먼저 들어갑니다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 기본적 값을 변수에 저장하는 단계
# 채널의 수는 cats&dogs 사진 파일이 칼라이기에 3
# 이미지 크기를 모두 64*64로 통일할 것임(이미지 분류는 정사각형으로 양 변값이 동일해야 함)
# 클래스는 개/고양이 구분이므로 2개
 
IMG_CHANNELS = 3
IMG_ROWS = 64
IMG_COLS = 64
CLASSES = 2
 
 
# 함수 지정; define the convnet 
# 빌드에 모델 쌓기
# 은닉 노드 수는 32개로 설정
# 종속변수가 취할 수 있는 값은 2(라벨 수) 
def build(input_shape, classes):
    model = models.Sequential() 
    model.add(layers.Convolution2D(32, (33), activation='relu',
                        input_shape=input_shape))
    model.add(layers.MaxPooling2D(pool_size=(22)))
 
    model.add(layers.Flatten())
    model.add(layers.Dense(32, activation='relu'))
    model.add(layers.Dense(classes, activation='softmax'))
    return model
cs

 

(3) 위와 같이 앞으로 쓰일 변수에 값을 저장하는 것과 함수 지정이 끝났을 경우, X에 해당하는 것들을 건드려보겠습니다. 즉, 본격적으로 이미지들을 불러오고, 정규화하는 코드들을 작성합니다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# glob 확인
#glob는 파일의 이름을 읽어오는 역할을 합니다 
glob('cats_dogs/cats/*jpg'# cats_dogs라는 폴더의 하위 폴더인 cats 안에 있는 모든 jpg파일 이름을 보여줍니다.
 
 
#glob로 이미지를 모두 불러온 후 이미지 사이즈를 통일
images = [image.load_img(p, target_size=(IMG_ROWS, IMG_COLS))#이미지를 불러올때 target_size로 특정 사이즈로 불러옴
# 이 특정 사이즈는 위에서 변수 지정 시 64로 각각 지정했으므로 이미지 사이즈는 64*64가 됩니다
          for p in glob('cats_dogs/cats/*jpg'+ glob('cats_dogs/dogs/*jpg')] # 각 cats, dogs 폴더 내 이미지들을 p에 불러온 후
image_vector = np.asarray([image.img_to_array(img) for img in images]) # p에 저장된 이미지들을 aaray로 저장하는 코드입니다.
 
 
# 이미지 벡터화 확인
image_vector[0#숫자로 변환된 형태
# 해상도가 클수록 정보는 많으나 데이터 양이 많아 연산이 힘들어지므로 적절한 형태 필요
 
 
# 모든 이미지 개수, 사이즈, 채널 확인
image_vector.shape # (2000, 64, 64, 3)
 
 
# 첫 번째 이미지 사이즈, 채널 확인
 image_vector[0].shape # (64, 64, 3)
 
 
# 정규화한 이미지를 시각화
plt.imshow(image_vector[0]/255)
cs

정규화한 이미지는 다음과 같이 표출됩니다 :D

고양이 귀여워!

 

(4) 이제는 Y에 해당하는 종속변수(라벨)를 지정해야 합니다. 라벨을 생성하는 원리는 cats 폴더에서 불러온 이미지(첫 번째~ 1000 번째)에는 1을 부여하고, 뒤에 불러온 dogs 폴더 이미지에는 0을 부여하는 것입니다. 이에 따라 고양이 라벨은 1로, 강아지 라벨은 0으로 코딩됩니다.

1
2
3
4
5
# 종속 변수를 생성
y=[1* 1000 + [0* 1000
# 앞에 천개는 값이 1, 뒤에 천개는 값이 0
# 이미지를 불러왔을 떄 고양이 폴더부터 표출된 것을 확인(첫 번째 이미지가 고양이)
# 고양이를 1로 코딩, 강아지는 0으로 코딩 
cs

 

(5) X에 해당하는 이미지 파일도 불러오고, Y에 해당하는 라벨도 정해졌으면, 이제 이들을 훈련 데이터와 테스트 데이터로 나누어주는(split) 과정이 필요합니다. split 뒤에는 정규화를 하고 라벨을 붙여주는 과정도 들어갑니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 학습데이터와 평가데이터로 나눔
# 검증데이터는 지금 나누지 않고 훈련 중 validation_Split으로 처리
from sklearn.model_selection import train_test_split  
X_train, X_test, y_train, y_test = train_test_split(image_vector, y, test_size=0.20, random_state=0)
 
# 훈련 데이터 크기
X_train.shape # (1600, 64, 64, 3) # 2000개 이미지*0.8
 
 
# 이미지 정규화
X_train, X_test = X_train / 255, X_test / 255
 
 
# 라벨에 대해 원 핫 벡터 코딩하여 분류 task에 맞게 조정:convert to categorical
# convert class vectors to binary class matrices
y_train = tf.keras.utils.to_categorical(y_train, 2# 원 핫 벡터로 코딩
y_test = tf.keras.utils.to_categorical(y_test, 2)
 
 
# 라벨 확인
y_test[0# array([1., 0.], dtype=float32)
cs

 

(6) 이미지도 훈련/테스트로, 라벨도 훈련/테스트로 나누었으므로 이제 미리 지정해둔 CNN  모델 build함수를 사용하여 훈련시킵니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
########### 모델 함수 환기 #############
'''
#define the convnet 
# 빌드에 모델 쌓기
# 은닉 노드 수는 32개
# 종속변수가 취할 수 있는 값은 2 
def build(input_shape, classes):
    model = models.Sequential() 
    model.add(layers.Convolution2D(32, (3, 3), activation='relu',
                        input_shape=input_shape))
    model.add(layers.MaxPooling2D(pool_size=(2, 2)))
 
    model.add(layers.Flatten())
    model.add(layers.Dense(32, activation='relu'))
    model.add(layers.Dense(classes, activation='softmax'))
    return model
'''
#######################################
 
# 저장된 model 함수에 인자들을 넣어 모델 완성
model=build((IMG_ROWS, IMG_COLS, IMG_CHANNELS), CLASSES)
model.summary()
 
 
# 컴파일 후 훈련 시작
model.compile(loss='binary_crossentropy', optimizer=optimizers.RMSprop(learning_rate=0.001), metrics=['accuracy'])
history = model.fit(X_train, y_train, batch_size=128, epochs=50
cs

해당 build 함수로 모델을 쌓은 결과는 위의 사진과 같습니다.

 

(7) 훈련을 했으니 훈련 중 history에 저장된 loss값과 accuracy의 변화 추이도 살펴보아야 합니다 *^^* 이는 다음의 코드로 확인할 수 있습니다. 만일 validation을 수행했을 경우 plt.plot(history.history['val_loss'])를 추가하면 됩니다. accuracy도 마찬가지입니다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
import matplotlib.pyplot as plt
plt.plot(history.history['loss']) # 훈련 중 loss값 확인
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train'])
plt.show()
# 학습이 잘 안될 경우?: 에폭수를 늘리거나 cnn 모형을 복잡하게 하면 해결 가능 
 
plt.plot(history.history['accuracy'])# 훈련 중 acc.값 확인
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.legend(['train'])
plt.show()
cs

 

 

(8) 추이를 봤을 때는 꽤 훈련이 잘 된 듯합니다. 훈련이 잘 되었다는 것은 test 데이터도 잘 맞춘다는 뜻이므로 확인해보겠습니다.

1
2
3
4
5
6
7
score = model.evaluate(X_test, y_test, batch_size=128)
print("\nTest loss:", score[0])
print('Test accuracy:', score[1])
'''
Test loss: 0.8621721863746643
Test accuracy: 0.6850000023841858
'''
cs

accuracy는 놀랍게도 0.69입니다(....) 즉 단순한 CNN만으로는 cats & dogs task를 잘 수행해낼 수 없다는 뜻이 됩니다. 이렇듯 단순한 CNN만으로는 높은 성능을 기대하기가 어렵습니다. 그래서 사전학습모형(pre-trained model)을 사용하게 됩니다.