2. 신경망 기본 구성 요소#

감사의 글

아래 내용은 프랑소와 숄레의 Deep Learning with Python(2판)의 소스코드 내용을 참고해서 작성되었습니다. 자료를 공개한 저자에게 진심어린 감사를 전합니다.

소스코드

여기서 언급되는 코드를 (구글 코랩) 신경망 구성 요소에서 직접 실행할 수 있다.

슬라이드

본문 내용을 요약한 슬라이드를 다운로드할 수 있다.

주요 내용

아래 요소들을 직관적으로 살펴본다.

  • 신경망 모델 구성, 훈련, 활용

  • 신경망 모델 훈련의 핵심 요소

2.1. 신경망 모델 기초 훈련법#

케라스 라이브러리를 이용하여 MNIST 손글씨 데이터셋을 대상으로 분류를 학습하는 신경망 모델을 구성, 훈련, 활용하는 방법을 소개한다.

2.1.1. 훈련셋 준비#

MNIST 데이터셋을 제공하는 많은 사이트가 있지만 여기서는 케라스 라이브러리가 자체로 제공하는 데이터셋을 활용한다.

from tensorflow.keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
  • 손글씨 숫자 인식 용도 데이터셋. 28x28 픽셀 크기의 사진 70,000개의 샘플로 구성 라벨: 0부터 9까지 10개의 클래스 중 하나

  • 훈련셋: 샘플 60,000개 (모델 훈련용)

    • train_images

    • train_labels

  • 테스트셋: 샘플 10,000개 (훈련된 모델 성능 테스트용)

    • test_images

    • test_labels

샘플, 타깃, 라벨, 예측값, 클래스

머신러닝 모델의 훈련에 사용되는 데이터셋과 관련된 기본 용어는 다음과 같다.

  • 샘플sample: 개별 데이터를 가리킴.

  • 타깃target과 라벨label

    • 타깃: 개별 샘플과 연관된 값이며, 샘플이 주어지면 머신러닝 모델이 맞춰야 하는 값임.

    • 라벨: 분류 과제의 경우 타깃 대신 라벨이라 부름.

  • 예측과 예측값: 개별 샘플에 대해 머신러닝 모델이 타깃에 가까운 값을 예측할 수록 좋은 성능의 모델임. 예측값은 모델이 입력 샘플들에 대해 예측한 값.

  • 클래스class: 분류 모델의 에측값으로 사용될 수 있는 라벨(타깃)들의 집합. 범주category라고도 함. 객체지향 프로그래밍 언어의 클래스 개념과 다름에 주의할 것.

2.1.2. 신경망 모델 지정#

다음과 같이 구성된 신경망 모델을 MNIST 분류 모델로 사용한다.

from tensorflow import keras
from tensorflow.keras import layers

model = keras.Sequential([
    layers.Dense(512, activation="relu"),
    layers.Dense(10, activation="softmax")
    ])

위 신경망 모델의 구조에 사용된 요소들은 다음과 같다.

  • Sequential 클래스

    • 1개 이상의 층을 순차적으로 연결하여 모델 객체를 생성하는 (파이썬) 클래스.

    • 앞으로 다른 방식으로 모델 객체를 생성하는 다양한 클래스를 접할 것임.

  • layer

    • 데이터가 입력되면 적절한 방식으로 변환 후 이어지는 층으로 전달함.

    • 여기서는 2개의 Dense 층 사용.

  • Dense

    • 입력 샘플의 모든 특성을 이용하여 층의 출력값을 생성함. 이런 방식으로 연결된 층들을 조밀하게 연결된densely connected 또는 완전하게 연결된fully-connected 층이라고 함. Dense 층은 항상 조밀하게 다음 층과 연결됨.

    • 첫째 Dense

      • 512개의 유닛 사용. 784개의 픽셀값으로부터 512개의 값을 생성. 즉, 한 장의 MNIST 손글씨 숫자 사진에 해당하는 길이가 784인 1차원 어레이가 입력되면 길이가 512인 1차원 어레이를 생성함.

      • 렐루Relu 함수: 활성화 함수로 사용됨. 생성된 512개의 값 중에서 음수는 모두 0으로 처리하는 함수.

    • 둘째 Dense

      • 10개의 유닛 사용. 입력된 512개의 값으로부터 10개의 값을 생성.

      • 소프트맥스Softmax 함수가 활성화 함수로 사용됨.

      • 계산된 10개의 값을 이용하여 0부터 9까지 10개의 범주 각각에 속할 확률을 계산함. 모든 확률의 합은 1.

  • 유닛unit

    • 생성된 값을 저장하는 장치.

    • 하나의 유닛에 하나의 값이 저장됨.

  • 활성화 함수activation function

    • 생성되어 유닛에 저장된 값을 이용하여 새로운 개수의 다른 값을 생성하는 함수.

    • 활성화 함수를 통과한 값이 다음 층으로 전달됨.

2.1.3. 신경망 모델 컴파일#

지정된 신경망 모델을 훈련시키기 위해서 옵티마이저, 손실 함수, 성능 평가 지표를 설정하는 컴파일 과정을 실행해야 한다.

model.compile(optimizer="rmsprop",
              loss="sparse_categorical_crossentropy",
              metrics=["accuracy"])

위 컴파일 과정에 사용된 요소들은 다음과 같다.

  • optimizer

    • 경사하강법(역전파) 업무를 처리하는 옵티마이저 지정.

    • 여기서는 rmsprop 옵티마이저 사용.

    • 앞으로 다양한 옵티마이저를 접할 것임.

  • loss

    • 손실 함수loss function 지정.

    • 손실 함수: 모델 훈련하는 동안 모델의 성능을 손실값으로 측정. 손실값이 작을 수록 좋음.

  • metrics

    • 훈련과 테스트 과정을 모니터링 할 때 사용되는 한 개 이상의 평가 지표metric를 포함하는 리스트로 지정.

    • 손실 함수값, 정확도 등 모델의 종류에 따라 다양한 평가 지표를 사용할 수 있음.

    • 분류 모델의 경우 일반적으로 정확도accuracy를 평가지표로 포함시킴.

2.1.4. 데이터 전처리#

머신러닝 모델에 따라 입력값이 적절한 형식을 갖춰야 한다. 앞서 두 개의 Dense 층과 Sequential 클래스로 지정된 모델의 입력값은 1차원 어레이 형식을 갖춰야 한다.

그런데 MNIST 데이터 샘플의 경우 0부터 255 사이의 8비트 정수(uint8)로 이루어진 (28, 28) 모양의 2차원 어레이로 표현되었다. 이를 1차원 어레이로 변환하기 위해 (28*28, ) 모양의 1차원 어레이로 변환한다. 또한 어레이의 각 항목을 0부터 1 사이의 32비트 부동소수점(float32)으로 변환한다. 이는 머신러닝 모델이 일반적으로 정수가 아닌 부동소수점 계산을 사용하기 때문이다.

train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype("float32") / 255   # 0과 1사이의 값
test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype("float32") / 255     # 0과 1사이의 값

2.1.5. 모델 훈련#

모델 훈련은 컴파일된 모델의 fit() 메소드를 호출하면 된다. MNIST 모델의 경우 지도 학습 모델이기에 입력 데이터셋과 타깃 데이터셋을 각각 첫째와 둘째 인자로 사용한다.

model.fit(train_images, train_labels, epochs=5, batch_size=128)
  • 첫째 인자: 훈련 데이터셋

  • 둘째 인자: 훈련 라벨셋

  • epoths: 에포크. 전체 훈련 세트 대상 반복 훈련 횟수.

  • batch_size: 배치 크기. 배치 크기만큼의 훈련 데이터셋로 훈련할 때 마다 가중치 업데이트.

모델의 훈련 과정 동안 에포크가 끝날 때마다 평균 손실값과 평균 정확도를 계산하여 다음과 같이 출력한다.

Epoch 1/5
469/469 [==============================] - 5s 4ms/step - loss: 0.2551 - accuracy: 0.9263
Epoch 2/5
469/469 [==============================] - 2s 4ms/step - loss: 0.1044 - accuracy: 0.9693
Epoch 3/5
469/469 [==============================] - 2s 3ms/step - loss: 0.0683 - accuracy: 0.9793
Epoch 4/5
469/469 [==============================] - 2s 4ms/step - loss: 0.0504 - accuracy: 0.9847
Epoch 5/5
469/469 [==============================] - 2s 3ms/step - loss: 0.0378 - accuracy: 0.9885

2.1.6. 가중치 활용 모델 예측값 계산#

입력 데이터 샘플 하나가 신경망 모델를 통해 최종 출력값으로 변환되는 과정을 묘사하면 다음과 같다.

  • 손글씨 데이터 샘플 입력

    • 위 사진에서는 8을 가리키는 사진 샘플이 입력값으로 사용됨.

    • 784 개의 픽셀값으로 구성된 1차원 어레이로 변환

  • 첫째 Dense

    • 입력된 784개의 픽셀값을 이용하여 512개의 값 생성.

    • relu() 활성화 함수로 인해 음수는 모두 0으로 처리됨.

  • 둘째 Dense

    • 첫째 Dense 층에서 생성된 512개의 값을 이용하여 10개의 값 생성.

    • softmax() 활성화 함수로 인해 모두 0과 1사이의 값으로 변환됨. 모든 값의 합이 1이 되며, 각각의 범주에 속할 확률을 가리킴.

가중치 행렬

위 그림에서 유닛과 유닛을 있는 모든 선엔 가중치weight가 관여한다. 아래 그림은 위 모델을 단순화 시켜서 첫째 층과 둘째 층 모두 3개의 유닛으로 구성되었을 때 둘째 층에서 첫째 층에서 넘어온 특성들을 변환시키는 과정에 관여하는 가중치들을 한눈에 보여준다.

하나의 샘플에 대한 데이터 변환의 구체적인 계산은 다음과 같다.

위 변환식을 가중치들로 이루어진 행렬과 입력값의 연산으로 표현하면 다음과 같다.

2.2. 신경망 모델 훈련의 핵심 요소#

이전 그림들은 모두 하나의 입력 샘플이 변환되는 과정을 보여준다. 하지만 입력 데이터의 변환은 실제로는 배치 단위로 이루어지며 이 과정을 스텝이라 부른다.

2.2.1. 훈련 스텝과 에포크#

배치 크기

배치에 포함된 모든 샘플에 대해 동시에 데이터 변환을 실행한다. 예를 들어, 배치 크기가 128이면, 위 MNIST 손글씨 이미지를 분류하는 모델은 128x784 모양의 2차원 어레이를 입력값으로 사용한다.

훈련 스텝과 경사하강법

각각의 층에서 입력된 배치는 아래 행렬 연산을 통해 변환된다. 아래 식에서 W는 가중치 행렬을, X 는 배치 데이터셋, b는 편향 벡터를 가리킨다.

softmax(W X + b)

가중치 행렬과 편향 벡터는 모델의 훈련이 시작될 때 무작위로 초기화된다. 그런 다음 입력된 배치에 대한 모델의 최종 예측값과 타깃과의 오차를 이용하여 타깃에 보다 가까운 예측값을 계산하도록 가중치 행렬과 편향 벡터를 업데이트한다. 업데이트를 어떻게 진행할까를 판단하기 위해 경사하강법이 적용된다. 이렇게 입력된 배치의 예측값을 계산하고 보다 정확한 예측값을 생성할 수 있도록 가중치 행렬과 편향 벡터를 업데이트하는 과정이 훈련 스텝이다.

정리하면, 입력된 하나의 배치에 대한 훈련 스텝은 다음 과정으로 이루어진다.

  • 입력된 배치 데이터셋을 변환하여 모델의 예측값 생성

  • 예측값과 타깃 사이의 오차 정보를 이용하여 보다 정확한 예측값을 생성할 수 있도록 가중치 행렬과 편향 벡터 조정

에포크

에포크는 전체 훈련셋을 지정된 크기의 배치 묶음으로 나눈 후 각각의 배치에 대한 모든 스텝을 한 번 완료하는 과정을 가리킨다. 하나의 에포크 동안 스텝이 여러 번 실행된다. 예를 들어, 앞서 배치 크기가 128이고, 훈련 데이터셋의 크기가 6만이었기에 하나의 에포크 동안 총 469번의 스텝이 실행된다. 이유는 60000 = 28 * 468 + 96 이기에 128개로 묶인 468개의 묶음 각각에 대해 스텝이 진행되고 나머지 96개를 묶은 배치에 대한 스텝이 한 번 더 진행되기 때문이다.

딥러닝 모델이 사용하는 신경망의 훈련은 입력 데이터를 배치 단위로 여러 층을 통과시키면서 변환시키는 과정을 최적의 예측값을 만들 때까지 여러 에포크를 거치면서 진행한다.

2.2.2. 훈련 스텝의 핵심 요소#

훈련 스텝 핵심 요소는 다음과 같다.

  • 가중치

  • 순전파

  • 손실 함수

  • 역전파

  • 경사하강법

  • 옵티마이저

  • 훈련 루프

가중치와 순전파

신경망의 각 층에 사용되는 데이터 변환 알고리즘 알고리즘은 가중치weight라 불리는 파라미터parameter에 의해 결정된다.

층에 데이터가 입력되면 변환 알고리즘에 의해 변환된 데이터가 다음 층으로 전달된다 (아래 그림 참고). 이와 같이 입력값을 가중치와 조합하는 과정을 여러 층에서 수행하여 최종 결과물인 예측값prediction을 생성하는 과정을 순전파forward pass라고 한다.

머신러닝 모델의 훈련은 바로 각 층에서 필요한 적절한 가중치를 찾는 과정을 가리킨다. 경우에 따라서 수 십만 수 백만, 수 천만 개 까지의 매우 많은 수의 적절한 가중치와 편향 파라미터를 학습해야 하는 훈련 과정이 매우 어렵거나 심지어 불가능한 과제로 판명되기도 한다.

신경망 모델의 가중치 파라미터

신경망 모델의 가중치 파라미터는 가중치 행렬과 편향 벡터를 모두 포함하는 것으로 이해해야 한다.

손실 함수

모델의 손실 함수loss function는 모델의 최종 출력결과인 예측값과 실제 타깃target이 얼마나 다른지를 측정한다. 손실 함수를 목적 함수objective function 또는 비용 함수cost function라고도 부른다. 훈련된 모델에 대한 손실 함수의 값인 손실 점수loss score는 낮을 수록 훈련이 잘되었다고 평가한다.

역전파, 경사하강법, 옵티마이저

역전파backpropagation경사하강법gradient descent에 기초하여 훈련 중인 모델의 손실값을 최대한 낮추는 방향으로 각 층의 가중치를 조절하는 과정을 가리킨다. 역전파는 옵티마이저optimizer에 의해 실행되며 딥러닝 모델 훈련 알고리즘의 핵심이다.

모델 훈련이 시작될 때 가중치는 임의로 초기화된다. 이후 손실 점수가 보다 작아지는 방향으로 조금씩 업데이트하는 역전파를 반복 실행하면서 손실값이 점차 낮아져서 최종적으로 최소 손실값을 갖도록 하는 가중치를 학습해 간다.

훈련 루프

신경망 모델의 훈련 루프training loop는 “순전파-손실값 계산-역전파”로 구성된 순환과정, 즉 하나의 스텝이 작동하는 과정을 가리킨다. 모델의 훈련은 최소 손실값을 갖도록 하는 가중치를 찾을 때까지 훈련 루프를 반복하는 방식으로 진행된다.

2.3. 학습된 모델의 활용과 평가#

2.3.1. 모델 활용#

훈련된 모델을 이용하여 훈련에 사용되지 않은 손글씨 숫자 사진 10장에 대한 예측값을 predict() 메서드로 확인하는 방식은 다음과 같다. predict() 메서드의 입력값이 하나의 샘플이 아닌 여러 개의 샘플에 대해 동시에 계산될 수 있음에 주의한다. 즉, 하나의 샘플에 대한 예측값을 계산하고자 하더라도 무조건 하나의 샘플 데이터로 구성된 2차원 어레이가 입력값으로 사용된다.

test_digits = test_images[0:10]
predictions = model.predict(test_digits)

출력값으로 각 사진에 대한 예측값으로 구성된 2차원 어레이가 계산된다. 각 항목은 각 입력값으로 사용된 손글씨 사진이 각 범주에 속할 확률을 갖는 길이가 10인 1차원 어레이가 된다. 예를 들어 첫째 사진에 대한 예측값은 다음과 같다.

>>> predictions[0]
array([5.6115879e-10, 6.5201892e-11, 3.8620074e-06, 2.0421362e-04,
       2.3715735e-13, 1.0822280e-08, 3.6126845e-15, 9.9979085e-01,
       2.0998414e-08, 1.0214288e-06], dtype=float32)
>>> predictions[0].argmax() 
7 
>>> predictions[0][7] 
0.99999106

위 예측값의 7번 인덱스의 값이 0.998 정도로 가장 높으며, 이는 0번 사진 입력 샘플이 숫자 7을 담고 있을 확률이 거의 100% 라고 예측했음을 의미한다. 실제로도 0번 사진은 숫자 7을 담고 있어서 이 경우는 정확하게 예측되었다.

>>> test_labels[0] 
7

2.3.2. 모델 성능 평가#

훈련에 사용되지 않은 테스트셋 전체에 대한 성능 평가를 위해 evaluate() 메서드를 테스트셋과 테스트셋의 라벨셋을 인자로 해서 호출한다.

>>> test_loss, test_acc = model.evaluate(test_images, test_labels)
313/313 [==============================] - 1s 3ms/step - loss: 0.0635 - accuracy: 0.9811
>>> print(f"test_acc: {test_acc}")
test_acc: 0.9811000227928162

evaluate() 메서드의 반환값 계산은 훈련 과정과 동일하게 배치 단위로 손실값과 앞서 모델을 컴파일할 때 지정한 정확도를 계산한 다음에 최종적으로 손실값과 정확도의 평균값을 반환한다. 배치 크기는 32가 기본값으로 사용되기에 총 313(10,000/32=312.5)번의 스텝이 진행되었다.

테스트 세트에 대한 정확도는 98.11% 이며 훈련 세트에 대한 정확도인 98.85% 보다 조금 낮다. 이는 모델이 훈련 세트에 대해 약간의 과대 적합overfitting이 발생했음을 의미한다. 과대적합과 과대적합을 해결하는 다양한 방법에 대해서는 나중에 보다 자세히 다룬다.

2.4. 연습문제#

  1. (실습) 신경망 기본 구성 요소