ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 8. CNN 코드 - MNIST, Cifar-10
    general ML, DL, NLP/딥러닝 2022. 4. 21. 03:58

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

    실제로 코드를 보면서 CNN이 어떻게 생겼는지를 살펴보려 합니다. 살펴볼 코드는 MNIST와 Cifar-10입니다.

     

    1. MNIST - 흑백 데이터 CNN

     

    (1) 중요사항 결정 및 데이터 로드

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 우선적으로 정해야 할 것들을 정합니다.
    num_classes = 10 # 종속변수 수 
    # input image dimensions
    img_rows, img_cols = 2828
     
     
    # 이후 데이터를 불러옵니다. 
    from tensorflow import keras
    from tensorflow.keras.datasets import mnist
    (x_train, y_train), (x_test, y_test) = mnist.load_data()
    # MNIST는 keras 자체에서 불러올 수 있습니다.
    cs

    우선적으로 종속변수(라벨) 수와 imput 이미지 차원을 정해줍니다. 

    이후에 데이터를 불러오는데, MNIST는 Keras에 내장되어 있어 엄청 쉽게 불러올 수 있습니다. MNIST는 기본적으로 28*28의 흑백 이미지입니다.

     

    (2) 데이터 형태 확인

    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
    28
    # 데이터 기초 정보 확인
    x_train.shape # 엠니스트는 28*28*1, 2차원 데이터 상태
    # 즉 depth 값이 없는 상태: (60000, 28, 28)
    x_train.shape[0# 훈련 데이터 개수: 위 튜플의 첫 번째 숫자이므로 60000개
     
     
     
    # 데이터 reshape
    # MNIST의 현재 상태인 28*28에 곱하기 1을 명시적으로 표시(60000, 28, 28, 1)
    # 처음에 결정한 img_rows, img_cols에 1을 추가
    x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1
    x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
     
    # 위에 맞추어 input_shape 정의: 처음에 결정한 img_rows, img_cols에 1을 추가
    input_shape = (img_rows, img_cols, 1)
     
    # 데이터 변경사항 확인
    print('x_train shape:', x_train.shape)
    print(x_train.shape[0], 'train samples')
    print(x_test.shape[0], 'test samples')
    """
    x_train shape: (60000, 28, 28, 1)
    60000 train samples
    10000 test samples
    """
     
    # 훈련 데이터 생김새 확인
    x_train[0].shape # (28, 28, 1)로 변경된 것을 확인할 수 있습니다.
    cs

    갓 불러온 훈련 데이터는 형태가 (60000, 28, 28)로 depth 값이 명시되지 않은 상태입니다.(test데이터도 마찬가지로 (10000, 28, 28)인 상태입니다.) 여기에 depth 값을 명시적으로 추가하고 초반에 지정한 img_row/cols와 조합하여 input_shape를 지정할 수 있습니다. 이를 위해 reshape을 사용합니다. 

    reshape을 통해서 훈련 데이터셋을 (60000, 28, 28, 1)로 만듭니다. 테스트 데이터셋에도 마찬가지로 변경을 가합니다. 자세히 풀어쓰자면 x_train.shape[0]은 60000, img_rows와 img_cols는 (1)에서 정의한 것과 같이 28과 28, 그리고 1이 추가된 형태를 x_train의 형태로 다시 만든다(reshape)는 뜻입니다.(코드 11~12줄에 해당합니다) 

    이렇게 변경한 데이터셋에 맞추어 input_shape도 정의해줍니다. MNIST 변경 사이즈에 맞추어 (28, 28, 1)이 되어야 합니다. 

    훈련 데이터를 다시금 확인해보면(28줄) (28, 28, 1) 원하는대로 depth를 추가한 상태로 변경되었음을 확인할 수 있습니다.

    MNIST가 처음이신 분들을 위해...
    - MNIST는 기본적으로 28*28 사이즈의 흑백 이미지입니다.
    - 흑백 이미지는 depth(채널) 수가 1입니다. 
    - 데이터셋을 갓 불러왔을 때에는 데이터 차원이 2차원이라 CNN에 바로 집어넣을 수 없습니다. 
    - 그렇기 때문에 MNIST를 3차원으로 변경시키는 한편, CNN에 집어넣으려면 input_shape도 MNIST의 형태와 맞추어야 합니다.

     

    (3) 이미지 normalizing & 라벨 원핫 벡터화

    MNIST 이미지 데이터를 출력해보면 0~255의 숫자가 28*28개 들어있습니다. 이를 255로 나눠줌으로써 0과 1로 normalizing을 하는 한편, 각 이미지의 라벨을 정수에서 0과 1로 구성된 원핫 벡터로 바꿉니다.

    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
    ## 이미지 normalizing & 라벨 one-hot vector화
     
    # 이미지 normalizing
    x_train = x_train.astype('float32')/255 # 0 ~ 1 사이의 값으로 normalization 하기
    x_test = x_test.astype('float32')/255
     
     
    # 라벨 원핫벡터화
    y_train[0:5# array([5, 0, 4, 1, 9], dtype=uint8)
    # 라벨은 0~9의 정수임을 확인 
     
    # 원핫벡터화를 위해 to_categorical을 불러옵니다.
    from tensorflow.keras.utils import to_categorical 
    y_train_one_hot = to_categorical(y_train) #정수로 표기된 라벨을 0, 1로 바꿉니다. 
    y_test_one_hot = to_categorical(y_test) #정수로 표기된 라벨을 0, 1로 바꿉니다. 
     
    # 잘 바뀌었는지 확인합니다.
    print(y_train[0:5])
    y_train_one_hot[0:5]
    """
    [5 0 4 1 9] # 정수 라벨을
    array([[0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
           [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
           [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]], dtype=float32) # 원핫 라벨로 변경
    """
    cs

     

    (4) 신경망 쌓기 - CNN 

    이미지 분류를 위한 CNN 신경망을 쌓습니다. CNN 신경망은 크게 convolution을 하는 단계와 classification을 하는 단계로 나뉩니다.

    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    # 신경망 쌓기
    from tensorflow.keras.models import Sequential
    from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten
    # Con2D는 필터가 적용되는 컨볼루션 네트워크를 만드는 클래스입니다.
     
     
    # Convolutional NN 단계입니다.
    model = Sequential() # 이 코드로 신경망 쌓기를 시작합니다. 
    # 입력층은 명시적으로 입력하지 않아도 돠나, 아래 첫 번째 은닉층에서 input_shape을 밝혀주어야 합니다.
    model.add(Conv2D(32, kernel_size=(33), activation='relu', input_shape=input_shape))
    """
    # 스트라이드는 default값으로 1, 즉 strides=(1, 1)로 지정됩니다. 
    # CNN에 filter를 적용하기 위해서 Conv2D 라고 하는 클래스를 사용했습니다.
    # Conv2D 클래스의 첫번째 객체는 필터 개수가 됩니다.
    # 필터 사이즈는 3*3입니다. kernel_size로 지정할 수 있습니다. 
    # 필터의 depth는 입력 이미지의 것으로 지정되므로, 따로 지정할 필요가 없습니다. 
    # 이렇게 설정한 하이퍼파라미터에 따라 Conv2D 클래스 별로 이미지에 서로 다른 32개의 필터를 한 번만 적용하게 됩니다
    # 활성화 함수를 relu로 지정했으므로, 각 필터는 relu를 활성화 함수로 사용합니다.
    """
    model.add(MaxPooling2D(pool_size=(22))) #풀링층
    """
    # strides의 기본값 => pool_size에 따라 결정됩니다.
    # 풀링 함수는 풀링 필터 크기를 지정해주어야 합니다: 2*2로 지정했습니다.
    # 스트라이드 값도 별도로 지정은 가능하나, 보통 풀링 필터 크기랑 동일합니다. 2가 됩니다.
    """
    model.add(MaxPooling2D(pool_size=(22))) # 풀링층
    """
    # strides의 기본값 => pool_size
    # 풀링 함수: 풀링 필터 크기 지정- 2*2
    # 스트라이드 값도 지정 가능: 풀링 필터는 보통 풀링 필터 크기랑 동일; 2
    """
     
    # classification 단계: 가래떡을 먼저 뽑은 후 분류합니다.ㅋㅋ
    model.add(Flatten()) # 3차원을 1차원으로 내립니다. 
    model.add(Dense(128, activation='relu')) # fully connected lyr
    # 덴스 레이어도 NN이기에 활성화 함수가 필요합니다. relu를 씁니다.
    model.add(Dense(num_classes, activation = 'softmax'))
    # 출력 노드가 10개(라벨 개수) 있는 출력층을 쌓고, 라벨별 확률을 리턴하는 softmax를 활성화함수로 씁니다.
     
    model.summary()
    cs

    CNN을 만들 때에는 Sequential 클래스를 사용하여 만들 수 있습니다. 이후 층을 하나씩 쌓아나갑니다. 층을 쌓는 것은 .add()로 가능합니다. convolution 단계에서는 (2)에서 맞춰 놓은 3차원 데이터로 연산이 이루어지나, classification 단계를 위해서는 1차원으로 내려야 합니다. 이를 위해 Flatten()을 사용합니다. 

    만들어진 모델과 파라미터 계산은 다음 그림과 같습니다.

    conv2d_1의 경우 들어오는 이미지의 채널(depth)이 32이므로 입력채널은 32, 출력되는 채널 개수는 32이므로 파란색 식과 같이 계산되는 것입니다. Flatten()이후에는 그냥 FFNN이므로 입력 노드 개수에 편향 개수 1을 더해 출력 노드 수와 곱합니다.  

    (5) 모델 compile 

    (주요 하이퍼파라미터: 옵티마이저, 학습률, 비용함수, 성능 평가 지표(metric))

    1
    2
    3
    4
    5
    # compile 단계
    from tensorflow.keras import optimizers
    model.compile(optimizer=optimizers.RMSprop(learning_rate=0.001),
                   loss='categorical_crossentropy',
                   metrics=['accuracy'])
    cs

    (4)에서 쌓아놓은 모델을 컴파일합니다. Keras는 컴파일을 해야 훈련이 가능합니다. 컴파일을 할 때에는 옵티마이저, 학습률, 비용함수, 평가지표를 정하게 됩니다. 분류에 라벨이 2개를 넘으므로 다진분류가 되어 categorical crossentropy를 사용합니다. 

     

    (6) 모델 학습

    (주요 하이퍼파라미터: 에폭수, mini-batch 수, 검증데이터셋 여부 & 비율)

    1
    2
    3
    # CNN을 훈련 데이터로 학습합니다.
    #history = model.fit(x_train, y_train_one_hot, epochs=10, batch_size=128)
    history = model.fit(x_train, y_train_one_hot, epochs=10, batch_size=128, validation_split=0.1)
    cs

     모델을 학습합니다. 학습 시에는 에폭 수와 배치 사이즈, 검증 데이터셋 사용 여부와 비율을 정하게 됩니다. 학습에 사용되는 fit 함수에는 x_train이라는 훈련 이미지 데이터셋과, y_train_one_hot이라는 x_train 이미지들의 라벨을 같이 학습합니다.

    만들어진 모델과 파라미터는 다음 사진과 같이 계산됩니다. 흑백과 달리 입력 채널 수가 3이 된 것을 확인할 수 있습니다. 

    (7) 훈련 추이 확인하기

    훈련 결과는 history 변수에 저장된 accuracy나 loss를 plot 함으로써 확인할 수 있습니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # 훈련 과정 plot: history에 저장된 내용을 바탕으로 합니다.
    import matplotlib.pyplot as plt
     
    # 훈련 중 loss 추이
    plt.plot(history.history['loss'])
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.legend(['train'])
    plt.show()
     
    # 훈련 중 accuracy 추이
    plt.plot(history.history['accuracy'])
    plt.xlabel('epoch')
    plt.ylabel('accuracy')
    plt.legend(['train'])
    plt.show()
     
    cs

     

    (8) 테스트 데이터셋으로 모델 성능 확인

    1
    2
    3
    4
    5
    6
    7
    # 테스트 데이터셋을 바탕으로 모델 성능 확인
    test_loss, test_acc = model.evaluate(x_test, y_test_one_hot)
    print('test_acc:', test_acc)
    """
    313/313 [==============================] - 2s 6ms/step - loss: 0.0549 - accuracy: 0.9868
    test_acc: 0.9868000149726868
    """
    cs

    테스트 데이터셋에 대해 성능을 확인하기 위해서는 evaluate 함수를 사용합니다. 훈련과 마찬가지로 x_test라는 테스트 데이터셋을 y_test_one_hot이라는 테스트 데이터의 라벨에 맞게 얼마나 잘 분류하는지를 측정합니다. 테스트 결과 정확도가 0.99에 육박하네요.

     

     

    2. Cifar-10 - 컬러 이미지 CNN

    (1) 패키지 불러오기 & 신경망 쌓기

    이번 신경망은 함수에 신경망을 쌓습니다. 쌓은 신경망은 build함수에 저장됩니다. 이 build함수는 인자로 input_shape 라벨 개수(classes)를 받습니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # 패키지를 불러옵니다
    import tensorflow as tf
    from tensorflow.keras import datasets, layers, models, optimizers
     
    # 쌓은 신경망을 함수로 정의합니다. 
    #define the convnet 
    # 3*3 필터 32개 사용
    # 풀링 필터 2*2
    # 풀링 스트라이드 2
    # 은닉 노드 512개 
    def build(input_shape, classes):
        model = models.Sequential() 
        model.add(layers.Conv2D(32, (33), activation='relu',
                            input_shape=input_shape))
        model.add(layers.MaxPooling2D(pool_size=(22)))
     
        model.add(layers.Flatten())
        model.add(layers.Dense(512, activation='relu'))
        model.add(layers.Dense(classes, activation='softmax'))
        return model
    cs

    Conv2D 레이어에는 3*3필터가 32개 쓰입니다. 풀링은 2*2 필터로 스트라이드 2로 진행됩니다. Flatten층 이후에는 은닉 노드 512개를 사용한 fully connected layer를 지나게 됩니다. 만들어진 모델은 다음과 같습니다.

    (2) 이미지 데이터 normalizing & 라벨 원핫 벡터화 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 이건 잘 모르겠습니다.
    import ssl
    ssl._create_default_https_context = ssl._create_unverified_context
     
    # 이미지 데이터 normalizing & 라벨 one-hot vector
    # data: shuffled and split between train and test sets
    (X_train, y_train), (X_test, y_test) = datasets.cifar10.load_data()
    # normalize
    X_train, X_test = X_train / 255.0, X_test / 255.0 # 노말라이제이션
    # convert to categorical
    # convert class vectors to binary class matrices
    y_train = tf.keras.utils.to_categorical(y_train) # 원핫인코딩
    y_test = tf.keras.utils.to_categorical(y_test)

    # 데이터 형식 확인
    X_train.shape #(50000, 32, 32, 3)
    cs

    MNIST와 마찬가지로 이미지를 정규화하고 라벨을 원핫벡터화 합니다.

    normalization을 거치게 되면 이미지의 원래 값은 0~255의 값에서 0~1사이의 값으로 변하게 됩니다. 데이터 형식이 (50000, 32, 32, 3)이 되었습니다. 특히 (32, 32, 3)은 input_shape의 값이 되어야 합니다.

     

    (3) 모델 완성 & 컴파일

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 정의해놓은 모델 함수에 인자를 넣어 모델을 완성합니다.
    model=build((32323), 10# 32개의 필터로 인해 뎁스가 32가 됨(30, 30, 32)
    model.summary()
     
    # 모델을 컴파일합니다. 비용함수, 옵티마이저, 학습률, 성능지표를 설정합니다.
    model.compile(loss='categorical_crossentropy', optimizer=tf.keras.optimizers.RMSprop(learning_rate=0.001), metrics=['accuracy'])
     
    # 모델을 학습시킵니다.
    # 학습한 결과는 history라는 변수에 저장하여 나중에 그래프를 그립니다. 
    history = model.fit(X_train, y_train, batch_size=128, epochs=10
    cs

    아까 (1)에서 정의한 모델 함수 build에 인자를 넣습니다. 인자는 input_shape와 클래스 수입니다. input_shape는 이미지 형태와 동일한 (32, 32, 3), 클래스 수, 즉 라벨 수는 10로 지정합니다. 이후 모델을 컴파일하고 학습을 시작합니다.

     

    (4) 훈련 중 loss, train accuracy 확인

    history에 저장된 훈련 결과를 가지고 그래프를 그립니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import matplotlib.pyplot as plt
     
    # 훈련 중 loss 추이 확인
    plt.plot(history.history['loss'])
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.legend(['loss'])
    plt.show()
     
    # 훈련 중 train accuracy 확인
    plt.plot(history.history['accuracy'])
    plt.xlabel('epoch')
    plt.ylabel('accuracy')
    plt.legend(['accuracy'])
    plt.show()
    cs

     

    (5) 훈련 결과를 바탕으로 평가 데이터 테스트하기

    평가는 evaluate 함수로 할 수 있습니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 훈련 결과를 바탕으로 테스트 데이터를 테스트합니다.
     
    score = model.evaluate(X_test, y_test)
    print("\nTest score:", score[0])
    print('Test accuracy:', score[1])
    """
    313/313 [==============================] - 3s 10ms/step - loss: 1.1619 - accuracy: 0.6623
     
    Test score: 1.16194748878479
    Test accuracy: 0.6622999906539917
    """
    cs

    컨볼루션 층이 1층인 단순한 CNN이기에 성능 자체는 낮게 나왔습니다.

     

    (6) 개별 데이터에 대해 평가하기 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 테스트 데이터의 2번째 이미지는 차입니다. 이미지를 plot하여 차를 확인합니다. 
    plt.imshow(X_test[1])
    plt.show()
     
    # 이 두번째 데이터에 대해 예측을 해봅니다. 
    import numpy as np
    np.set_printoptions(suppress=True, precision=10)
    model.predict(X_test[1:2]) # 9번째 라벨의 확률이 제일 큼을 알 수 있습니다.
    """
    array([[0.0008724507, 0.031893644 , 0.0000000073, 0.0000000035,
            0.0000000102, 0.0000000051, 0.0000000346, 0.0000000002,
            0.9671924   , 0.0000414316]], dtype=float32)
    """
    cs

    ㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇ

     

     

    'general ML, DL, NLP > 딥러닝' 카테고리의 다른 글

    10. 이미지 분류 사전학습모형  (0) 2022.04.21
    9. CNN 코드 - cats & dogs  (0) 2022.04.21
    7. CNN(Convolutional Neural Network)  (0) 2022.04.20
    6. Overfitting 과적합  (0) 2022.04.20
    5. FFNN(Feed Forward Neutral Network)  (0) 2022.04.20

    댓글

Designed by Tistory.