from tensorflow import keras
(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()
/home/ubuntu/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:516: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. _np_qint8 = np.dtype([("qint8", np.int8, 1)]) /home/ubuntu/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:517: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. _np_quint8 = np.dtype([("quint8", np.uint8, 1)]) /home/ubuntu/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:518: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. _np_qint16 = np.dtype([("qint16", np.int16, 1)]) /home/ubuntu/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:519: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. _np_quint16 = np.dtype([("quint16", np.uint16, 1)]) /home/ubuntu/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:520: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. _np_qint32 = np.dtype([("qint32", np.int32, 1)]) /home/ubuntu/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:525: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. np_resource = np.dtype([("resource", np.ubyte, 1)]) /home/ubuntu/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:541: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. _np_qint8 = np.dtype([("qint8", np.int8, 1)]) /home/ubuntu/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:542: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. _np_quint8 = np.dtype([("quint8", np.uint8, 1)]) /home/ubuntu/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:543: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. _np_qint16 = np.dtype([("qint16", np.int16, 1)]) /home/ubuntu/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:544: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. _np_quint16 = np.dtype([("quint16", np.uint16, 1)]) /home/ubuntu/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:545: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. _np_qint32 = np.dtype([("qint32", np.int32, 1)]) /home/ubuntu/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:550: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. np_resource = np.dtype([("resource", np.ubyte, 1)])
그 다음 이미지의 픽셀값을 0 ~ 255 범위에서 0 ~ 1 사이로 변환하고, 28 x 28 크기의 2차원 배열을 784 크기의 1차원 배열로 펼친다. 마지막으로 사이킷런의 train_test_split() 함수로 훈련 세트와 검증 세트로 나눈다. 여기까지는 1절에서 했던 것과 같다. 다음 코드가 이해되지 않는다면 1절을 다시 학습하고 오길 바란다.
from sklearn.model_selection import train_test_split
train_scaled = train_input / 255.0
train_scaled = train_scaled.reshape(-1, 28 * 28)
train_scaled, val_scaled, train_target, val_target = train_test_split(train_scaled, train_target, test_size=0.2, random_state=42)
이제 인공 신경망 모델에 층을 2개에 추가해 보겠다. 1절을 만든 신경망 모델과 다른 점은 입력층과 출력층 사이에 밀집층이 추가된 것이다. 이렇게 입력층과 출력층 사이에 있는 모든 층을 은닉층 hidden layer이라고 부른다.
은닉층에는 주황색 원으로 활성화 함수가 표시되어 있다. 활성화 함수는 신경망 층의 선형 방정식의 계산 값에 적용하는 함수이다. 이전 절에서 출력층에 적용했던 소프트맥스 함수도 활성화 함수이다. 출력층에 적용하는 활성화 함수는 종류가 제한되어 있다. 이진 분류일 경우 시그모이드 함수를 사용하고 다중 분류일 경우 소프트맥스 함수를 사용한다. 이에 비해 은닉층의 활성화 함수는 비교적 자유롭다. 대표적으로 시그모이드 함수와 잠시 후에 볼 렐루 ReLU함수 등을 사용한다.
분류 문제를 위한 신경망의 출력층에는 시그모이드 함수나 소프트맥스 함수를 활성화 함수로 사용한다.
회귀를 위한 신경망의 출력층에서 어떤 활성화 함수를 사용하나?
- 분류 문제는 클래스에 대한 확률을 출력하기 위해 활성화 함수를 사용한다. 회귀의 출력은 임의의 어떤 숫자이므로 활성화 함수를 적용할 필요가 없다. 즉 출력층의 선형 방정식의 계산을 그대로 출력한다. 이렇게 하려면 Dense 층의 axtivation 매개변수에 아무런 값을 지정하지 않는다.
그런데 은닉층에 왜 활성화 함수를 적용할까? 은닉층에서 선형적인 산술 계산만 수행한다면 수행 역할이 없는 셈이다. 선형 계산을 적당하게 비선형적으로 비틀어 주어야 한다. 그래야 다음 층의 계산과 단순히 합쳐지지 않고 나름의 역할을 할 수 있다.
- 다른 책에서 보니 인공 신경망 그림에 활성화 함수가 없던데요?
- 인공 신경망을 그림으로 나타낼 때 활성화 함수를 생략하는 경우가 많은데 이는 절편과 마찬가지로 번거로움을 피하기 위해서 활성화 함수를 별개의 층으로 생각하지 않고 층에 포함되어 있다고 간주하기 때문이다. 그림에서 보이지는 않지만 모든 신경망의 은닉층에는 항상 활성화 함수가 있다.
많이 사용하는 활성화 함수 중 하나는 4장에서 배웠던 시그모이드 함수이다. 기억을 되살리기 위해 다시 한번 살펴보자. 이 함수는 뉴런의 출력 z값을 0과 1사이로 압축한다. 그럼 시그모이드 활성화 함수를 사용한 은닉층과 소프트맥스 함수를 사용한 출력층을 케라스의 Dense 클래스로 만들어 보자. 이전 절에서 언급했듯이 케라스에서 신경망의 첫 번째 층은 input_shape 매개변수로 입력의 크기를 꼭 지정해 주어야 한다.
dense1 = keras.layers.Dense(100, activation= 'sigmoid', input_shape=(784, ))
dense2 = keras.layers.Dense(10, activation = 'softmax')
WARNING:tensorflow:From /home/ubuntu/.local/lib/python3.6/site-packages/tensorflow/python/ops/init_ops.py:1251: calling VarianceScaling.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version. Instructions for updating: Call initializer instance with the dtype argument instead of passing it to the constructor
dense1이 은닉층이고 100개의 뉴런을 가진 밀집층이다. 활성화 함수를 'sigmoid'로 지정했고 input_shape 매개변수에서 입력의 크기를 (784,)로 지정했다. 은닉층의 뉴런 개수를 정하는데는 특별한 기준이 없다. 몇 개의 뉴런을 두어야 할지 판단하기 위해서는 상당한 경험이 필요하다.
여기에는 한 가지 제약 사항이 있다면 적어도 출력층의 뉴런보다는 많게 만들어야 한다. 클래스 10개에 대한 확률을 예측해야 하는데 이전 은닉층의 뉴런이 10개보다 적다면 부족한 정보가 전달될 것이다.
다음은 dense2는 출력층이다. 10개의 클래스를 분류하므로 10개의 뉴런을 두었고 활성화 함수는 소프트맥스 함수로 지정했다.
심층 신경망 만들기¶
이제 앞에서 만든 dense1과 dense2 객체를 Sequential 클래스에 추가하여 심층 신경망 deep neural network, DNN을 만들어 보겠다.
model = keras.Sequential([dense1, dense2])
Sequential 클래스의 객체를 만들 때 여러 개의 층을 추가하려면 이와 같이 dense1과 dense2를 리스트로 만들어 전달 한다. 여기서 주의할것은 출력층을 가장 마지막에 두어야 한다는 것이다. 이 리스트는 가장 처음 등장하는 은닉층에서 마지막 출력츠으이 순서로 나열해야 한다.
인공 신경망의 강력한 성능은 바로 이렇게 층을 추가하여 입력 데이터에 대해 연속적인 학습을 진행 하는 능력에서 나온다. 이 책의 앞 장에서 배운 선형 회귀, 로지스틱 회귀, 결정 트리 등 다른 머신러닝 알고리즘들과 대조 된다. 물론 2개 이상의 층을 추가할 수도 있다. 다음 장에서 더 복잡한 모델을 만들어 보겠다. 케라스는 모델의 summary() 메서드를 호출하면 층에 대한 유용한 정보를 얻을 수 있다.
model.summary()
Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense (Dense) (None, 100) 78500 _________________________________________________________________ dense_1 (Dense) (None, 10) 1010 ================================================================= Total params: 79,510 Trainable params: 79,510 Non-trainable params: 0 _________________________________________________________________
맨 첫 줄에 모델의 이름이 나온다. 그 다음 이 모델에 들어 있는 층이 순서대로 나열된다. 이 순서는 맨 처음 추가한 은닉층에서 출력층의 순서대로 나열 된다.
층마다 층 이름, 클래스, 출력 크기, 모델 파라미터 개수가 출력된다. 층을 만들 때 name 매개변수로 이름을 지정할 수 있다. 층 이름을 지정하지 않으면 케라스가 자동으로 'dense'라고 이름을 붙인다.
출력 크기를 보면 (None,100) 이다. 첫 번째 차원은 샘플의 개수를 나타낸다. 샘플 개수가 아직 정의되어 있지 않기 때문에 None이다. 왜 그럴까? 케라스 모델의 fit() 메서드에 훈련 데이터를 주입하면 이 데이터를 한 번에 모두 사용하징 ㅏㄶ고 잘게 나누어 여러 번에 걸쳐 경사 하강법 단계를 수행한다. 네 맞다. 바로 미니배치 경사 하강법을 사용하는 것이다.
케라스의 기본 미니배치 크기는 32개 이다. 이 값은 fit()메서드에서 batch_size 매개변수로 바꿀 수 있다. 따라서 샘플 개수를 고정하지 않고 어떤 배치 크기에도 유연하게 대응할 수 있도록 None으로 설정한다. 이렇게 신경망 층에 입력 되거나 출력되는 배열의 첫 번째 차원을 배치 차원이라고 부른다.
두 번째 100은 쉽다. 은닉층의 뉴런 개수를 100개로 두었으니 100개의 출력이 나온다. 즉 샘플마다 784개의 픽셀값이 은닉층을 통과하면서 100개의 특성으로 압축되었다.
마지막으로 모델 파라미터 개수가 출력된다. 이 층은 Dense 층이므로 입력 픽셀 784개와 100개의 모든 조합에 대한 가중치가 있다. 그리고 뉴런마다 1개의 절편이 있다.
두 번째 층의 출력 크기는 (NOne,10)이다. 배치 차원은 동일하게 None이고 출력 뉴런 개수가 10개이기 때문이다. 이 층의 모델 파라미터 개수는 몇 개 일까?
100개의 은닉층 뉴런과 10개의 출력층 뉴런이 모두 연결되고 출력츠으이 뉴런마다 하나의 절편이 있기 때문에 총 1,010개의 모델 파라미터가 있다. summary() 메서드의 마지막에는 총 모델 파라미터 개수와 훈련되는 파라미터 개수가 동일하게 79,510개로 나온다. 은닉층과 출력층의 파라미터 개수를 합친 값이다. 그 아래 훈련되지 않는 파라미터(None-trainable params)는 0으로 나온다. 간혹 경사 하강법으로 훈련되지 않는 파라미터를 가진 층이 있다. 이런 층의 파라미터 개수가 여기에 나타난다.
층을 추가하는 다른 방법¶
모델을 훈련하기 전에 Seqeuntial 클래스에 층을 추가하는 다른 방법을 알아본다. 앞에서는 Dense 클래스 객체 dense1, dense2 를 만들어 Sequential 클래스에 전달했다. 이 두 객체를 따로 저장하여 쓸 일이 없기 때문에 다음처럼 Sequential 클래스의 생성자 안에서 바로 Dense 클래스의 객체를 만드는 경우가 많다.
model = keras.Sequential([
keras.layers.Dense(100, activation='sigmoid', input_shape=(784,),
name='hidden'),
keras.layers.Dense(10, activation='softmax', name='output')],
name='패션 MNIST 모델')
이렇게 작업하면 추가되는 층을 한눈에 쉽게 알아보는 장점이 있다. 이전과 달리 이번에는 Sequential 클래스의 name 매개변수로 모델의 이름을 지정했다. 또 Dense 층의 name매개변수에 층의 이름을 'hidden'과 'output'으로 각각 지정했다. 모델의 이름과 달리 층의 이름은 반드시 영문이어야 한다. summary() 메서드의 출력에 이름이 잘 반영되는지 확인해 보자.
model.summary()
Model: "패션 MNIST 모델" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= hidden (Dense) (None, 100) 78500 _________________________________________________________________ output (Dense) (None, 10) 1010 ================================================================= Total params: 79,510 Trainable params: 79,510 Non-trainable params: 0 _________________________________________________________________
2개의 Dense 층이 이전과 동일하게 추가되었고 파라미터 개수도 같다. 바뀐 것은 모델 이름과층 이름이다. 여러 모델과 많은 층을 사용할 때 name 매개변수를 사용하면 구분하기 쉽다.
이 방법이 편리하지만 아주 많은 층을 추가하려면 Sequential 클래스 생성자가 매우 길어진다. 또 조건에 따라 층을 추가할 수도 없다. Sequential 클래스에서 층을 추가할 때 가장 널리 사용하는 방법은 모델의 add() 메서드이다.
이 방법은 다음처럼 Sequential 클래스의 객체를 만들고 이 객체의 add() 메서드를 호출하여 층을 추가한다.
model=keras.Sequential()
model.add(keras.layers.Dense(100, activation='sigmoid', input_shape=(784, )))
model.add(keras.layers.Dense(10, activation='softmax'))
여기에서도 Dense 클래스의 객체를 따로 변수에 담지 않고 바로 add()메서드로 전달한다. 이 방법은 한눈에 추가되는 층을 볼 수 있고 프로그램 실행시 동적으로 층을 선택하여 추가할 수 있다.
summary()메서드의 결과에서 층과 파라미터 개수는 당연히 동일하다.
model.summary()
Model: "sequential_1" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense_2 (Dense) (None, 100) 78500 _________________________________________________________________ dense_3 (Dense) (None, 10) 1010 ================================================================= Total params: 79,510 Trainable params: 79,510 Non-trainable params: 0 _________________________________________________________________
이제 모델을 훈련해 보자. compile() 메서드의 설정은 1절에서 했던 것과 동일하다. 여기에서도 5번의 에포크 동안 훈련해 보자.
model.compile(optimizer="adam",loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(train_scaled, train_target, epochs=5)
Epoch 1/5 48000/48000 [==============================] - 3s 54us/sample - loss: 0.5831 - acc: 0.8039 Epoch 2/5 48000/48000 [==============================] - 2s 51us/sample - loss: 0.4083 - acc: 0.8548 Epoch 3/5 48000/48000 [==============================] - 2s 51us/sample - loss: 0.3694 - acc: 0.8669 Epoch 4/5 48000/48000 [==============================] - 2s 51us/sample - loss: 0.3446 - acc: 0.8749 Epoch 5/5 48000/48000 [==============================] - 2s 51us/sample - loss: 0.3264 - acc: 0.8812
<tensorflow.python.keras.callbacks.History at 0x7ff59d488f28>
훈련 세트에 대한 성능을 보면 추가된 층이 성능을 향상시켰다는 것을 잘 알 수있다. 인공 신경망에 몇 개의 층을 추가하더라도 compile() 메서드와 fit()메서드의 사용법은 동일하다. 이것이 케라스 API의 장점이다. 피룡하면 여러 개의 층을 추가하고 실험해 보자.
다음에는 이미지 분류 문제에서 높은 성능을 낼 수 있는 활성화 함수에 대해 알아보겠다.
렐루 함수¶
초창기 인공 신경망의 은닉층에 많이 사용된 활성화 함수는 시그모이드 함수였다. 하지만 이 함수에는 단점이 있다. 이 함수의 오른쪽 왼쪽 끝으로 갈수록 그래프가 누워있기 때문에 올바른 출력을 만드는데 신속하게 대응하지 못한다.
특히 층이 많은 심층 신경망일수록 그 효과가 누적되어 학습을 더 어렵게 만든다. 이를 개선하기 위해 다른 종류의 활성화 함수가 제안되었다. 바로 렐루ReLU함수이다. 렐루 함수는 아주 간단하다. 입력이 양수일 경우 마치 활성화 함수가 없는 것처럼 그냥 입력을 통과시키고 음수일 경우에는 0으로 만든다.
렐루 함수는 max(0,z)와 같이 쓸 수 있다. 이 함수는 z가 0보다 크면 z를 출력하고 z가 0보다 작으면 0을 출력한다. 렐루 함수는 특히 이미지 처리에서 좋은 성능을 낸다고 알려져 있다. 은닉층의 활성화 함수에 시그모이드 함수 대신 렐루 함수를 적용하기 전에 케라스에서 제공하는 편리한 층 하나를 더 살펴보자.
패션 MNIST 데이터는 28 x 28 크기이기 때문에 인공 신경망에 주입하기 위해 넘파이 배열의 reshpae() 메서드를 사용해 1차원으로 펼쳤다. 직접 이렇게 1차원으로 펼쳐도 좋지만 케라스에서는 이를 위한 Flatten 층을 제공한다.
사실 Flatten 클래스느 배치 차원을 제외하고 나머지 입력 차원을 모두 일렬로 펼치는 역할만 한다. 입력에 곱해지는 가중치나 절편이 없다. 따라서 인공 신경망의 성능을 위해 기여하는 바는 없다. 하지만 Flatten 클래스를 층처럼 입력층과 은닉층 사이에 추가하기 때문에 이를 층이라 부른다. Flatten 층은 다음 코드처럼 입력층 바로 뒤에 추가한다.
model = keras.Sequential()
model.add(keras.layers.Flatten(input_shape=(28,28)))
model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dense(10, activation='softmax'))
첫 번째 Dense 층에 있던 input_shape 매개변수를 Flatten 층으로 옮겼다. 또 첫 번째 Dense 층의 활성화 함수를 'relu'로 바꾼 것을 눈여겨보자. 하지만 이 신경망을 깊이가 3인 신경망이라고 부르지 않는다. Flatten 클래스는 학습하는 층이 아니니까요. 모델의 summary() 메서드를 호출해 보면 이런 점을 더 확실히 알 수 있다.
model.summary()
Model: "sequential_2" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= flatten (Flatten) (None, 784) 0 _________________________________________________________________ dense_4 (Dense) (None, 100) 78500 _________________________________________________________________ dense_5 (Dense) (None, 10) 1010 ================================================================= Total params: 79,510 Trainable params: 79,510 Non-trainable params: 0 _________________________________________________________________
첫 번째 등장하는 Flatten 클래스에 포함된 모델 파라미터는 0개이다. 케라스의 Flatten 층을 신경망 모델에 추가하면 입력값의 차원을 짐작할 수 있는 것이 또 하나의 장점이다. 앞의 출력에서 784개의 입력이 첫 번째 은닉층에 전달된다는 것을 알 수 있다. 이는 이전에 만들었던 모델에서는 쉽게 눈치채기 어려웠다. 입력 데이터에 대한 전처리 과정을 가능한 모델에 포함시키는 것이 케라스 API의 철학 중 하나이다.
- 케라스 API는 입력 데이터에 대한 전처리 과정을 될 수 있으면 모델에 포함한다.
그럼 훈련 데이터를 다시 준비해서 모델을 훈련해 보겠다. 이 절의 서두에 있던 코드와 동일하지만 reshape()메서드를 적용하지 않는다.
(train_input, train_target),(test_input, test_target) = keras.datasets.fashion_mnist.load_data()
train_scaled = train_input / 255.0
train_scaled , val_scaled, train_target, val_target = train_test_split( train_scaled, train_target, test_size=0.2, random_state=42)
모델을 컴파일하고 훈련하는 것은 다음 코드처럼 이전과 동일하다.
model.compile(optimizer="adam",loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(train_scaled, train_target, epochs=5)
Epoch 1/5 48000/48000 [==============================] - 3s 54us/sample - loss: 0.5278 - acc: 0.8172 Epoch 2/5 48000/48000 [==============================] - 2s 52us/sample - loss: 0.3950 - acc: 0.8586 Epoch 3/5 48000/48000 [==============================] - 3s 53us/sample - loss: 0.3526 - acc: 0.8718 Epoch 4/5 48000/48000 [==============================] - 3s 52us/sample - loss: 0.3296 - acc: 0.8807 Epoch 5/5 48000/48000 [==============================] - 3s 52us/sample - loss: 0.3090 - acc: 0.8856
<tensorflow.python.keras.callbacks.History at 0x7ff5f421f6a0>
시그모이드 함수를 사용했을 때와 비교하면 성능이 조금 향상되었다. 크지 않지만 렐루 함수의 효과를 보았네요. 그럼 검증 세트에서의 성능도 확인해 보자.
model.evaluate(val_scaled, val_target)
12000/12000 [==============================] - 0s 27us/sample - loss: 0.3356 - acc: 0.8771
[0.3356366733709971, 0.87708336]
1절의 은닉층을 추가하지 않은 경우보다 몇 퍼센트 성능이 향상되었다. 지금까지는 모델을 5번의 에포크 동안 훈련했다. 이보다 더 훈련하지 않을 이유가 없다. 그전에 인공 신경망의 하이퍼파라미터에 대해 잠시 알아보고 이번 절을 마무리하겠다.
옵티마이저¶
3장에서 하이퍼파라미터 모델이 학습하지 않아 사람이 지정해 주어야 하는 파라미터라고 설명했다. 신경망에는 특히 하이퍼파라미터가 많다. 어떤 하이퍼파라미터가 있는지 먼저 이번 장에서 등장한 것들을 생각해보자.
이번 절에서는 은닉층을 하나 추가했다. 하지만 여러 개의 은닉층을 추가할 수도 있다. 추가할 은닉층의 개수는 모델이 학습하는 것이 아니라 우리가 지정해 주어야 할 하이퍼파라미터이다. 그럼 은닉츠으이 뉴런 개수도 하이퍼파라미터일까? 네, 맞다. 또 활성화 함수도 선택해야 할 하이퍼파라미터 중 하나이다. 이 장에서는 가장 기본적인 밀집층만 다루지만, 다른 종류의 층을 선택할 수도 있다.
케라스는 기본적으로 미니배치 경사 하강법을 사용하며 미니배치 개수는 32개이다. fit()메서드의 batch_size 매개변수에 이를 조정할 수 있으면 역시 하이퍼파라미터이다. 또한 fit()메서드의 epochs 매개변수도 하이퍼파라미터이다.! 반복 횟수에 따라 다른 모델이 만들어진다.
마지막으로 compile()메서드에서는 케라스의 기본 경사 하강법 알고리즘인 RMSprop을 사용했다. 케라스는 다양한 종류의 경사 하강법 알고리즘을 제공한다. 이들을 옵티마이저 optimizer라고 부른다. 역시 다른 옵티마이저를 테스트하지 않을 이유는 없다. 또한 RMSprop의 학습률 또한 조정할 하이퍼파라미터 중 하나이다.
와, 정말 많다 처음 부터 모델을 구성하고 각종 하이퍼파라미터의 최적값을 찾는 것은 어려운 작업이다. 여기서는 여러 가지 옵티마이저를 테스트해 보겠다. 가장 기본적인 옵티마이저는 확률적 경사 하강법인 SGD 이다. 이름이 SGD이지만 1개의 샘플을 뽑아서 훈련하지 않고 앞서 언급한 것처럼 기본적으로 미니배치를 사용한다.
SGD 옵티마이저를 사용하려면 compile()메서드의 optimizer 매개변수를 'sgd'로 지정한다.
model.compile(optimizer='sgd', loss='sparse_categorical_crossentropy',metrics=['accuracy'])
이 옵티마이저는 tensorflow.keras.optimizers 패키지 아래 SGD 클래스로 구현되어 있다. 'sgd' 문자열은 이 클래스의 기본 설정 매개변수로 생성한 객체와 동일하다. 즉 다음 코드는 위의 코드와 정확히 동일한다.
sgd = keras.optimizers.SGD()
model.compile(optimizer=sgd, loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
- sgd 와 'sgd'의 차이가 무엇인가?
- 원래 sgd = keras.optimizeers.SGD()처럼 SGD 클래스 객체를 만들어 사용해야 한다. 번거로움을 피하고자 'sgd'라 지정하면 자동으로 SGD 클래그 객체를 만들어 준다.
만약 SGD 클래스의 학습률 기본값이 0.01일때 이를 바꾸고 싶다면 다음과 같이 원하는 학습률을 learning_rate 매개변수에 지정하여 사용한다.
sgd = keras.optimizers.SGD(learning_rate=0.1)
SGD 외에도 다양한 옵티마이저들이 있다. 먼저 그림으로 많이 사용하는 옵티마이저들을 알아보자.
기본 경사 하강법 옵티마이저는 모두 SGD 클래스에서 제공한다. SGD 클래스의 momentum 매개변수의 기본값은 0이다. 이를 0보다 큰 값으로 지정하면 마치 이전의 그레이디언트를 가속도 처럼 사용하는 모멘텀 최적화 momentum optimization를 사용한다. 보통 momentum 매개변수는 0.9이상을 지정한다.
다음처럼 SGD 클래스의 nesterov 매개변수를 기본값 False 에서 True로 바꾸면 네스테로프 모멘텀 최적화nesterov momentum optimization를 사용한다.
sgd = keras.optimizers.SGD(momentum=0.9, nesterov=True)
네스테로프 모멘텀은 모멘텀 최적화를 2번 반복하여 구현한다. 대부분의 경우 네스테로프 모멘텀 최적화가 기본 확률적 경사 하강법보다 더 나은 성능을 제공한다.
모델이 최적점에 가까이 갈수록 확습률을 낮출 수 있다. 이렇게 하면 안정적이고 최적점에 수렴할 가능성이 높다. 이런 학습률을 적응적 학습률 adaptive learning rate라고 한다. 이런 방식들은 학습률 매개변수를 튜닝하는 수고들 덜 수 있는 것이 장점이다.
적응적 학습률을 사용하는 대표적인 옵티마이저는 Adagrad와 RMSprop이다. 각각 compile()메서드의 optimizer 매개변수에 'adagrad'와 'rmsprop'으로 지정할 수 있다. optimizer 매개변수의 기본값이 바로 'rmsprop'이다. 이 두 옵티마이저의 매개변수를 바꾸고 싶다면 SGD와 같이 Adagrad와 RMSprop 클래스 객체를 만들어 사용하면 된다.
adagrad = keras.optimizers.Adagrad()
model.compile(optimizer=adagrad, loss='sparse_categorical_crossentropy', metrics=['accuracy'])
RMSprop도 마찬가지이다.
rmsprop = keras.optimizers.RMSprop()
model.compile(optimizer=adagrad, loss='sparse_categorical_crossentropy', metrics=['accuracy'])
모멘텀 최적화와 RMSprop의 장점을 접목한 것이 Adam이다. Adam은 RMSprop과 함께 맨처음 시도해 볼 수 있는 좋은 알고리즘이다. Adam 클래스도 keras.optimizers 패키지 아래에 있다. 적응적 학습률을 사용하는 이 3개의 클래스는 learning_rate 매개변수의 기본값으로 모두 0.001을 사용한다.
여기에서는 Adam 클래스의 매개변수 기본값을 사용해 패션 MNIST 모델을 훈련해 보겠다. 먼저 모델을 다시 생성하자.
model = keras.Sequential()
model.add(keras.layers.Flatten(input_shape=(28,28)))
model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dense(10, activation='softmax'))
complie()메서드의 optimizer를 'adam'으로 설정하고 5번의 에포크 동안 훈련한다.
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
model.fit(train_scaled, train_target, epochs=5)
Epoch 1/5 48000/48000 [==============================] - 2s 52us/sample - loss: 0.5265 - acc: 0.8161 Epoch 2/5 48000/48000 [==============================] - 2s 50us/sample - loss: 0.3976 - acc: 0.8576 Epoch 3/5 48000/48000 [==============================] - 2s 50us/sample - loss: 0.3572 - acc: 0.8702 Epoch 4/5 48000/48000 [==============================] - 2s 51us/sample - loss: 0.3278 - acc: 0.8814 Epoch 5/5 48000/48000 [==============================] - 2s 51us/sample - loss: 0.3070 - acc: 0.8874
<tensorflow.python.keras.callbacks.History at 0x7ff5a83fd438>
이 출력 결과를 보면 기본 RMSprop을 사용했을 때와 거의 같은 성능을 보여준다. 마지막으로 검증 세트에서도 성능도 확인해 보자.
model.evaluate(val_scaled, val_target)
12000/12000 [==============================] - 0s 27us/sample - loss: 0.3298 - acc: 0.8799
[0.3297578009963036, 0.87991667]
환경마다 조금씩 차이가 있을 수 있지만 여기서는 기본 RMSprop보다 조금 나은 성능을 낸다.
케라스 API를 활용한 심층 신경망 - 문제해결 과정¶
이번 장에서 여러 개의 층을 추가하여 다층 인공 신경망을 만드는 방법을 터득했다. 특별히 이런 인공 신경망을 심층 신경망이라고 부른다. 또 케라스 API를 사용하여 층을 추가하는 여러가지 방법을 알아보았다.
케사르 모델의 정보를 요약해 주는 summary() 메서드를 사용해 보았다. 출력값의 의미를 이해하고 모델 파라미터 개수를 계산해 맞추어 보았다. 모델 파라미터 개수를 계산하는 과정은 모델을 올바르게 이해하고 있는지 확인하는 좋은 방법 중 하나이다.
은닉층에 적용한 시그모이드 활성화 함수 대신에 새로운 렐루 활성화 함수에 대해 배웠고 이를 적용해 약간의 성능을 향상시켰다. 또한 다양한 고급 경사 하강법 옵티마이저들을 적용하는 방법도 살펴 보았다. 케라스 API를 사용하면 이런 작업이 어렵지 않고 직관적으로 구성할 수 있다.
이번 절에서 인공 신경망에 조금 더 깊게 살펴보았다. 다음 절에서는 훈련한 인공 신경망 모델을 저장하고 복원하는 과정을 살펴보겠다. 이 과정에서 텐서플로와 케라스에서 제공하는 다양한 도구를 배울 수 있다.
- 출처: 혼자 공부하는 머신러닝 + 딥러닝
'혼자공부하는 머신러닝 + 딥러닝' 카테고리의 다른 글
Chapter08 이미지를 위한 인공 신경망 (0) | 2021.06.01 |
---|---|
Chapter07-3 신경망 모델 훈련 (0) | 2021.06.01 |
Chapter07-1 인공신경망 (0) | 2021.05.30 |
Chapter 06. 비지도 학습(주성분 분석) (0) | 2021.05.29 |
Chapter06. 비지도 학습 (군집알고리즘, k-평균) (0) | 2021.05.29 |