Etc/Deep Learning

5장 합성곱 신경망Ⅰ(2) 실습- 전이학습- 특성 추출 기법(+ 텐서플로 허브)

z.zzz 2021. 8. 4. 17:19

1단계. 필요한 라이브러리 호출

2단계. 사전 훈련된 모델 내려받기

- 해당 예제에서 사용할 모델 : 사전 훈련된 ResNet50

model = ResNet50(include_top=True,
                          weights="imagenet",
                          input_tensor=None,
                          input_shape=None,
                          pooling=None,
                          classes=1000)

include_top : 네트워크(사전 훈련된 모델) 상단에 '완전연결층을 포함'할지의 여부를 지정(기본값: True)

                     //완전연결층 : 합성곱층, 풀링층을 거쳐 차원이 축소된 특성맵을 전달받음, 이미지를 3차원 벡터에서 1차원 벡터로 펼침

weights : 가중치

                   None(무작위 초기화)과 'imagenet'(ImageNet에서 사전 훈련된 값)을 지정 가능

input_tensor : 입력 데이터의 텐서(layers.Input()의 출력)

                        //텐서 : 데이터의 배열

input_shape : 입력 이미지에 대한 텐서 크기

pooling : 풀링(sub sampling, 특징 추출)에서 사용할 수 있는 값의 종류

               - None : 모델의 출력이 마지막 합성곱층

               - avg : 마지막 합성곱층에 글로벌 '평균' 풀링이 추가됨(나누는 과정X, 각 특성 맵에 대해 모든 값을 더하는 단순 합으로 대체)

               - max : 마지막 합성곱층에 글로벌 '최대' 풀링이 추가됨

classes : 이미지를 분류할 클래스의 선택적 수

                - weights로 'imagenet'사용하려면 classes 값이 1000이어야함

                  (다른 값을 사용하고 싶다면 None으로 지정)

 

ResNet50

 - 계층 50개로 구성된 합성곱 신경망

 - ImageNet 데이터베이스의 100만 개가 넘는 영상을 이용하여 훈련된 신경망으로, 전이 학습에 사용되도록 사전 훈련된 모델을 제공

 - 단점 : 입력 제약이 매우 크고, 충분한 메모리(RAM)가 없으면 학습 속도가 느릴 수 있다

 

2*단계. 내려받은 ResNet50 네트워크 구조 확인

✔ResNet50 네트워크는 유지, 여기에 추가 계층을 생성해 사용할 것

model.summary()

 

3단계. 사전훈련된 합성곱층의 가중치 고정, 시그모이드 활성화 함수가 포함된 밀집층(완전연결층) 추가

model.trainable = False
model = Sequential([model,
                           Dense(2, activation='sigmoid')])   #시그모이드 함수가 포함된 밀집층 추가
model.summary()

3단계 실행 결과

 

model.trainable : 훈련 가능/불가능(동결) 지정

Sequential() : 순차적으로 레이어층을 더해주는 모델.

                     각 레이어에 정확히 하나의 입력 텐서와 하나의 출력 텐서가 있는 일반 레이어 스택에 적합

+ Dense() : 밀집층, 입력과 출력을 연결

      - 첫번째 인자 : 출력 노드(뉴런)의 

      - activation : 활성화 함수를 설정. ( 'sigmoid' : 이진 분류 문제에서 출력 층에 주로 쓰임)

model.summary() 해석

     - Layer(type) : 레이어의 이름과 타입(알아서 레이어의 이름을 지정해줌)

     - Output Shape: (None, 2)이라는 뜻은 None개의 행과 2개의 아웃풋 값이 주어졌다는 것.

         ✔행이 None으로 지정되는 이유 : 데이터의 갯수는 계속해서 추가될 수 있기 때문에

                                                  딥러닝 모델에서는 주로 행을 무시하고 열의 shape을 맞추어주는 작업을 많이 수행함

     - Param: 파라미터의 수, 즉 각 입력노드와 출력노드에 대해 연결된 간선의 수

               인풋이 1개, 아웃풋이 5개인 경우, 1*5=5개의 간선이 존재

               인풋에 Bias(b) 노드가 추가된 경우, 입력값(1)+바이어스노드(1)=2 총 2개의 인풋 노드 ⇒ 파라미터의 갯수: 2*5 = 10개의 간선

 

4단계. 훈련에 사용될 환경 설정(활성화함수, 손실함수, 평가지표)

5단계. 모델 훈련(개와 고양이 이미지 데이터셋 이용)

train = ImageDataGenerator(rescale=1./255,
                                     rotation_range=10,
                                     width_shift_range=0.1,
                                     height_shift_range=0.1,
                                     shear_range=0.1,
                                     zoom_range=0.1)  ------ ①

train_generator = train.flow_from_directory(train_dir,
                                                       target_size=(image_height, image_width),
                                                       color_mode="rgb",
                                                       batch_size=BATCH_SIZE,
                                                       seed=1,
                                                       shuffle=True,
                                                       class_mode="categorical")  ------ ②

valid = ImageDataGenerator(rescale=1.0/255.0)
valid_generator = valid.flow_from_directory(valid_dir,
                                                       target_size=(image_height, image_width),
                                                       color_mode="rgb",
                                                       batch_size=BATCH_SIZE,
                                                       seed=7,
                                                       shuffle=True,
                                                       class_mode="categorical")

history = model.fit(train_generator,
                        epochs=10,
                        validation_data = valid_generator,
                        verbose=2)  ------ ③

① ImageDataGenerator : 쉽게 데이터 전처리를 할 수 있음 + 데이터를 증가시킴

(augmentation, 기존 데이터에서 새로 생성 된 합성 데이터를 추가 ⇒ 정규화 역할을하며 기계 학습 모델을 학습할 때 과적합을 줄이는 데 도움줌)

   ⓐ rescale : 원본 영상은 0~255의 RGB 계수로 구성되는데, 1/255로 스케일링하여 0~1 범위로 변환

                  (원본 값은 모델을 효과적으로 학습시키기에 너무 높음)

                  Q. 왜 255로 나누는가?

                  A. 픽셀값이 0~255 사이의 값을 가진다할때, 0은 검은색, 255는 흰색, 1~254는 회색을 나타냄(grayscale 케이스)

                  Q. 왜 0~1범위의 값을 가져야하는가?

                  A. 머신러닝에서 scale이 큰 feature의 영향이 커지는걸 방지 + 극 값(local minima)에 빠질 확률을 감소시키며 학습속도 향상

   ⓑ rotation_range : 이미지 회전 범위.

                       rotation_range=10은 0~10도 범위 내에서 임의로 원본 이미지를 회전하겠다는 의미

   ⓒ width_shift_range, height_shift_range : 그림을 수평/수직으로 랜덤하게 평행 이동시키는 범위

                        값이 0.1, 전체 높이가 100일 경우, 0.1의 값을 적용하면서 10픽셀 내외로 이미지를 좌우/상하로 이동하겠다는 의미

   ⓓ shear_range : 원본 이미지를 임의로 변형(전단)시키는 범위(늘이기)

                         shear_range.0.1은 0.1라디안 내외로 시계 반대 방향으로 이미지를 변환시키겠다는 의미

shear_range : 이미지 전단

 

   ⓔ zoom_range : 임의 확대/축소 범위. zoom_range.0.1은 0.9에서 1.1배의 크기로 이미지를 변환시키겠다는 의미

zoom_range : 확대/축소

 


 

 flow_from_directory : 폴더 구조를 그대로 가져와서 ImageDataGenerator에 실제 데이터를 채워줌

   ⓐ 첫번째 파라미터 : 훈련 이미지 경로

   ⓑ target_size : 이미지 크기(모든 이미지에 적용)

   ⓒ color_mode : 이미지가 그레이스케일이면 'grayscale', 색상이 있으면 'rgb' 사용

   ⓓ batch_size : 한 배치당 generator에서 생성할 이미지 개수

   ⓔ seed : 이미지를 임의로 섞기 위한 랜덤 숫자

   ⓕ shuffle : 이미지를 섞어서 사용할지의 여부를 지정

   ⓖ class_mode : 예측할 클래스가 두 개뿐이면 'binary', 그렇지 않으면 'categorical'을 선택

 


 

③ 모델을 훈련시키기

   ⓐ 첫번째 파라미터 : 학습에 사용되는 데이터셋

   ⓑ validation_data : 검증 데이터셋 설정

   ③ verbose : 훈련의 진행과정을 보여줌 (0: 출력X, 1: 훈련 진행도 표시위한 진행 막대 표시, 2: 미니 배치마다 훈련 정보 출력)

 

6단계. 모델의 정확도를 시각화

plt.rcParams["font.family"] = font_family

accuracy = history.history['accuracy'] ------ ①
val_accuracy = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(accuracy))

plt.plot(epochs, accuracy, label="훈련 데이터셋")
plt.plot(epochs, val_accuracy, label="검증 데이터셋")
plt.legend()
plt.title('정확도')
plt.figure()

plt.plot(epochs, loss, label="훈련 데이터셋")
plt.plot(epochs, val_loss, label="검증 데이터셋")
plt.legend()
plt.title('오차')

 ⓐ plt.rcParams : 차트 figure의 기본 설정 지정

 ⓑ plt.legend() : 그래프에 범례를 표시

history 객체가 가진 정보(model.fit() 메서드에 대한 반환값으로 history 객체를 얻음)

    - accuracy: 매 에포크에 대한 훈련의 정확도

    - loss: 매 에포크에 대한 훈련의 손실값

    - val_accuracy: 매 에포크에 대한 검증의 정확도

    - val_loss: 매 에포크에 대한 검증의 손실값

 

 

훈련 데이터는 시간이 흐를 수록

정확도는 높아지고 오차는 낮아짐

그러나 검증 데이터는 시간이 흘러도 변동 X

=> 예측력이 좋지 못하다는 것을 의미

 

 

 

 

7단계. 훈련된 모델의 예측

class_names = ['cat', 'dog'] #개와 고양이에 대한 클래스 2개
validation, label_batch = next(iter(valid_generator))     //validation: 이미지 배열, label_batch : 정답 레이블 배열
prediction_values = model.predict_classes(validation)  //이미지 배열의 예측값

fig = plt.figure(figsize=(12, 8))
fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)

for i in range(8):
      ax = fig.add_subplot(2, 4, i+1, xticks=[], yticks=[])
      ax.imshow(validation[i,:], cmap=plt.cm.gray_r, interpolation='nearest')

      /* 예측값이 정답과 같으면 노란색, 다르면 빨간색으로 예측값 출력*/
      if prediction_values[i] == np.argmax(label_batch[i]):      //prediction_values는 0 아니면 1의 값을 가짐
            ax.text(3, 17, class_names[prediction_values[i]], color='yellow', fontsize=14)
      else:
            ax.text(3, 17, class_names[prediction_values[i]], color='red', fontsize=14)

(Q.valid_generator 첫 인덱스(?)는 이미지가 아닌지)

iter() : 전달된 데이터의 반복자를 꺼내 반환

       · iterable 객체 - 반복 가능한 객체

       · 대표적으로 iterable한 타입 - list, dict, set, str, bytes, tuple, range

next() : 반복자를 입력으로 받아 그 반복자가 다음에 출력해야 할 요소를 반환

predict_classes : label 제공     //predict는 probability

     · predict 의 경우 [[0.22520512] [0.9520419 ] [0.9672848 ] [0.02690617]]

     · predict_classes 의 경우 [[0] [1] [1] [0]]

subplots_adjust : 배치되어 있는 subplot들의 위치를 모두 조절

     · left, bottom= 0 : 경계에 둠

     · right, top= 1 : 또 다른 경계에 둠

     · hspace, wspace : subplot이 여러 개일때 subplot간의 간격을 의미

interpolation : 보간법(픽셀들의 축 위치 간격을 보정하여 이미지가 자연스러운 모양으로 보일 수 있게 하는 방법)

      *  'nearest'는 가장 고해상도인 보간법

imshow 첫 파라미터 : 유사 배열 or PLI(Python Imaging Library)

argmax : 배열에서 가장 큰 값의 인덱스 값을 반환

+ 틱(눈금) 없애기(틱이 기본적으로 약간의 텍스트 공백을 차지하기 때문에 제거)

   → plt.xticks([])   plt.yticks([])

+ 컬러맵 목록

   https://scipy-cookbook.readthedocs.io/items/Matplotlib_Show_colormaps.html 

 

 

7단계 수행 결과

 


텐서플로 허브 사용

텐서플로 허브

 - 사전 훈련된 모델을 이용하는 방법 중 하나

 - 일반화된 문제들에 대해 모델의 재사용성을 극대화하하려고 구글에서 공개한 API

 

1단계. 필요한 라이브러리 호출 및 ResNet50 내려받기

import tensorflow_hub as hub
model = tf.keras.Sequential([       

    /* ResNet50의 합성곱층의 가중치 고정 */
    hub.KerasLayer("https://tfhub.dev/google/imagenet/resnet_v2_152/feature_vector/4",
                       input_shape=(224,224,3),
                       trainable=False),

    /* 사전 훈련된 모델을 가져와서 밀집층 추가(완전연결층, 소프트맥스 활성화 함수가 추가된) */
    tf.keras.layers.Dense(2, activation='softmax') 
])

계층을 고정. 분류를 위해 새로운 계층을 추가

 

 

2단계. 훈련과 검증 데이터셋을 충분히 확보하기 위해 데이터 확장(augmentation)

이후 단계는 앞의 예제와 동일함

 

텐서플로 허브를 이용한 경우의 정확도와 손실

 

 

시간이 흐름에 따라

정확도는 높아지고

오차는 낮아짐

⇒ 훈련이 잘 됐음

 

 

 

 

 

개와 고양이에 대한 예측 결과

옳은 예측은 노란색 글씨, 틀린 예측은 빨간색 글씨로 예측값이 출력됨

 

 

 


참고 자료 
3단계 : https://ebbnflow.tistory.com/124 

ImageDataGenerator 사진 출처 : https://ichi.pro/ko/keras-mich-tensorflowleul-sayonghan-imiji-deiteo-jeungdae-tamsaeg-184813206747204

predict_classes : https://mmresult.tistory.com/entry/predict-%EC%99%80-predictclass-%EC%99%80%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90

subplot_adjust : https://frhyme.github.io/python-lib/img_savefig_%EA%B3%B5%EB%B0%B1%EC%A0%9C%EA%B1%B0/

interpolation : https://bentist.tistory.com/23

imshow() :https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.imshow.html

argmax() : https://www.delftstack.com/ko/api/numpy/python-numpy-argmax/

정규화 이유 : https://realblack0.github.io/2020/03/29/normalization-standardization-regularization.html