IT Study/ML & DL

[DL/CNN] CNN 실습 (with. MNIST data)

짹짹체유 2023. 12. 22. 18:51

 

0. 데이터셋 확인

  • 사용 데이터: train 5000개, test 1000개
  • 이미지 데이터의 크기 28x28

 

  • 데이터 특징: 2차원의 0~255 픽셀 값으로 구성
    • CNN의 input data format은 3차원(가로, 세로, 채널)
    • => 전처리로 채널 차원을 추가해야한다

 

  • 9개의 이미지 시각화

 

  • 라벨 데이터

  • 0~9까지의 10개의 값으로 라벨링 되어있다
  • => 전처리 시, 원핫 인코딩 진행 필요

1. 데이터 전처리

1) x 데이터 채널차원 추가

numpy의 expand_dims 함수를 통해 채널 차원을 추가해준다

np.expand_dims (arr, axis)

arr: 차원을 확장할 배열

axis: 새로 추가될 차원의 위치(default: 0 -> 인덱스 0 앞에 새로운 차원을 추가하겠다)

 

 

실습에서는 axis에 -1을 넣어 맨 뒤에 새로운 차원을 추가하도록 했다

채널 차원이 추가되기 전에는 28x28의 2차원 행렬에서 채널 차원이 추가된 후에는 28x28x1의 3차원 행렬로 변환했다

 


 

2) 라벨 데이터 원핫인코딩

to_categorical 함수를 통해서 1차원의 배열로 원핫인코딩 작업을 진행했다

ex. 라벨 2: [0, 0, 1, 0, 0, 0, 0, 0, 0, 0] 로 변경

 

  • 전체 전처리 코드
def preprocess():
    (train_images, train_labels), (test_images, test_labels) = mnist.load_data()

    train_images, train_labels = train_images[:5000], train_labels[:5000]
    test_images, test_labels = test_images[:1000], test_labels[:1000]
    
    # 255 값의 이미지를 0~1 사이의 값으로 정규화
    train_images = train_images / 255
    test_images = test_images / 255

    # 채널차원 추가
    train_images = np.expand_dims(train_images, -1)
    test_images = np.expand_dims(test_images, -1)
    
    # 원핫인코딩 진행
    train_labels = to_categorical(train_labels, 10)
    test_labels = to_categorical(test_labels, 10)
    
    return train_images, test_images, train_labels, test_labels

 


2. 모델 생성

def CNN():
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Conv2D(filters=64, kernel_size=(3,3), activation='relu', padding='SAME', input_shape= (28,28,1)))
    model.add(tf.keras.layers.MaxPool2D(padding='SAME'))
    model.add(tf.keras.layers.Conv2D(filters=32, kernel_size=(3,3), activation='relu', padding='SAME'))
    model.add(tf.keras.layers.MaxPool2D(padding='SAME'))
    model.add(tf.keras.layers.Conv2D(filters=32, kernel_size=(3,3), activation='relu', padding='SAME'))
    model.add(tf.keras.layers.MaxPool2D(padding='SAME'))
    model.add(tf.keras.layers.Flatten())
    model.add(tf.keras.layers.Dense(32, activation='relu'))
    model.add(tf.keras.layers.Dense(64, activation='relu'))
    model.add(tf.keras.layers.Dense(10, activation='softmax'))
    
    return model

 

 

  • Conv2D layer
tf.keras.layers.Conv2D(filters=64, kernel_size=(3,3), activation='relu', padding='SAME', input_shape=(28,28,1))
  • filter(=kernel)는 이미지의 특징을 찾아내기 위한 행렬
    • 학교 수업에서는 필터를  '도장'으로 비유를 들어 학습했다. 나에겐 '도장'의 표현이 더 와닿았다.
    • " Input 데이터를 도장의 크기 만큼 도장을 찍으며 특징을 확인한다 "
  • kernel_size는 커널(필터)의 크기를 의미한다
  • activation 활성화 함수는 relu 함수를 적용해서 
  • padding="SAME" 옵션을 통해서 Feature Map의 크기가 줄어들지 않고 동일하도록 설정한다
  • input_shape 옵션은 입력층에서만 적용한다

 

  • MaxPool2D layer
tf.keras.layers.MaxPool2D(padding='SAME')
  • Max pooling는 Convolution 레이어의 출력 데이터를 입력 데이터로 받아서 크기를 줄이거나 특정 데이터의 특성을 강조한다
  • Max pooling 크기는 (2, 2)이기에 일반적으로는 Feature Map의 크기가 줄어든
  • padding="SAME" 옵션을 통해서 Feature Map의 크기가 줄어들지 않고 동일하도록 설정한다

 

  • Flatten layer
tf.keras.layers.Flatten()
  • CNN의 데이터 타입을 Fully Connected Neural Network의 형태로 변경하는 레이어이다

 

  • Dense layer
tf.keras.layers.Dense(32, activation='relu')
  • 32는 노드의 개수를 나타낸다
  • 활성화함수로는 relu 함수를 사용했다

 

  • Output layer
tf.keras.layers.Dense(10, activation='softmax')
  • 라벨 개수가 10개 이기 때문에 출력층 노드의 개수도 10개로 지정한다
  • 각 라벨에 대한 확률값을 나타내야하기 때문에 활성화함수로 softmax를 사용한다

 

 

※ 에러 발생

ValueError: A target array with shape (5000, 10) was passed for an output of shape (None, 14, 14, 10) while using as loss `categorical_crossentropy`. This loss expects targets to have the same shape as the output.

 

해결방법: Flatten layer을 추가해주자 에러가 해결

발생원인: 모델의 출력 형태와 레이블의 형태가 일치하지 않아서 발생한 것

Why?

Flatten layer 앞까지는 3차원의 형태였다. 그러나 결과는 10개의 라벨 중 하나의 값으로 출력되어야 한다. 따라서 1차원으로 평탄화해주는 작업이 필요하다.

 


3. 모델 학습

def main():
    
    train_images, test_images, train_labels, test_labels = preprocess()
    
    model = CNN()

    model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
    history = model.fit(train_images, train_labels, epochs=15, batch_size=128,
                        validation_data=(test_images, test_labels), verbose=2)
    print(model.summary)

 

 

1) 모델 컴파일하기

모델 구조를 선언하는 과정이다

model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
  • 옵티마이저는 adam 함수를 사용했다
  • 분류 문제이기 때문에 손실함수는 3개 이상의 범주형 데이터에 사용하는 categorical crossentropy를 사용했다
    • vs sparse_categorical_crossentropy는 원핫인코딩을 진행하지 않은 경우, 값이 정수 형태인 경우에 사용한다
  • 모델 결과는 정확도를 보고 싶어서 accuracy를 적용했다

 

2) 모델 학습하기

model.fit(train_images, train_labels, epochs=15, batch_size=128,
          validation_data=(test_images, test_labels), verbose=2)
  • epochs는 전체 데이터의 학습 횟수를 의미한다
  • batch_size는 데이터를 몇 개로 나누어서 학습할 것인지 의미한다
  • validation_data는 검증 데이터로 사용할 x와 y 데이터를 묶음으로 적용한다
  • verbose는 학습 과정을 출력하는 부분인데 기본값은 1이며 2는 진행바는 출력하지 않고 수치 정보들만 출력한다

 


4. 모델 평가

# 앞 함수에 이어서
    loss, test_acc = model.evaluate(test_images, test_labels)
    
    print('\nTest Loss : {:.4f} | Test Accuracy : {}'.format(loss, test_acc))

    result = model.predict(test_images)
    
    return history, result

 

 

1) 모델 평가하기

model.evaluate(test_images, test_labels)
  • evaluate 함수에 test x 데이터와 y 데이터를 넣는다

 

2) 모델 예측하기

model.predict(test_images)
  • 검증 데이터로 사용한 데이터를 넣는 것은 옳지 못하지만 간단한 실습이기에...!
  • predict 함수에는 예측할 x 데이터를 넣어준다
  • 답이 없는 예측 데이터이기 때문에 y 데이터는 당연히 넣지 않는 것!

 

 

반응형