Home [혼공머신러닝 7-2] 심층 신경망
Post
Cancel

[혼공머신러닝 7-2] 심층 신경망

이번 장에서는 저번 장에서 배웠던 가장 기본적인 신경망에 층을 추가하거나, 활성화 함수를 변경하거나, 옵티마이저를 변경하는 것으로 더 복잡한 신경망을 만드는 것을 배웠습니다.

1. 층 추가하기

저번 장에서 만들었던 밀집층의 출력 수를 10에서 100으로 늘리고, 100개의 출력값을 10개의 출력값을 가지는 새 층에 넣는 방법으로 새 층을 추가할 수 있습니다.

1
2
3
4
dense1 = keras.layers.Dense(100, activation='sigmoid', input_shape=(784,)) # 은닉층, 출력값 100개는 스스로 판단해서 써야 함
dense2 = keras.layers.Dense(10, activation='softmax') # 출력층

model = keras.Sequential([dense1, dense2])

주의할 점은, 출력층의 경우에는 사용할 수 있는 활성화 함수가 제한적이라는 것입니다. 이 경우에는 전 장의 패선 mnist 데이터를 계속 사용하고 있으므로 다중 분류를 위해 softmax 함수를 사용했습니다. 은닉층에 사용할 수 있는 활성화 함수는 제한되지 않았습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
model.summary() # non-trainable params : 경사 하강법으로 훈련되지 않는 파라미터, total params : (784 * 100 + 100) + (100 * 10 + 10)

=>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
_________________________________________________________________

이처럼 summary 메서드를 사용하면 만든 모델의 정보를 확인할 수 있습니다. 각 층의 입력값이 None으로 적혀 있는 까닭은 모델이 기본적으로 미니배치 경사 하강법을 사용하기 때문입니다. 샘플 개수를 정의해놓지 않고 데이터를 나누어 경사 하강법을 수행합니다. Param의 수는 입력값 수 * 출력값 수 + 출력값으로 계산할 수 있는데 이는 각 기울기에 절편이 더해지기 때문입니다. Param들의 합이 toral params에 표시되며, non-trainable params는 경사 하강법으로 훈련되지 않는 파라미터의 개수입니다.

위에서는 층을 추가할 때 dense 변수를 직접 만들어 사용했지만 실제로 이 변수를 사용할 일은 없기 때문에 층을 축할 때는 다음과 같이 쓸 수도 있습니다.

1
2
3
4
model = keras.Sequential([
    keras.layers.Dense(100, activation='sigmoid', input_shape=(784,), name='hidden'),
    keras.layers.Dense(10, activation='softmax', name='output')
], name='패션 MNIST 모델') # 층 변수를 따로 사용할 일이 없으므로 모델 안에 직접 층을 넣음

그러나 이러한 작성 방식은 모델의 코드를 지나치게 길게 만드므로 일반적으로는 add 메서드를 사용한다고 합니다.

1
2
3
model = keras.Sequential() # add 메서드로 층을 추가하는 것이 일반적
model.add(keras.layers.Dense(100, activation='sigmoid', input_shape=(784,)))
model.add(keras.layers.Dense(10, activation='softmax'))

2. Relu

방금 은닉층에는 활성화 함수의 제한이 없다고 언급했는데, Relu는 사용할 수 있는 활성화 함수 중 하나입니다. 전 장에서 사용했었던 sigmoid 함수는 함수의 왼쪽 끝과 오른쪽 끝에서 값이 0와 1로 수렴하기 때문에 변화가 점점 느려집니다. 반면 Relu 함수는 입력값이 음수이면 0, 양수이면 x(원래 값 그대로)를 출력합니다.

1
2
3
4
model = keras.Sequential()
model.add(keras.layers.Flatten(input_shape=(28, 28)))
model.add(keras.layers.Dense(100, activation='relu')) # 시그모이드는 함수의 왼쪽 끝과 오른쪽 끝에서 값이 1과 0으로 수렴하므로 변화가 점점 느려짐
model.add(keras.layers.Dense(10, activation='softmax')) # 출력층에는 다중 분류일 경우 softmax로 고정 사용

이처럼 activation에 relu를 할당하면 활성화 함수를 relu로 변경할 수 있습니다. 일반적으로 sigmoid보다 더 좋은 성능을 냅니다.

추가로, Flatten 층은 2차원 이미지 데이터를 1차원 배열로 만들어주는 층이지만 파라미터를 포함하고 있지는 않아 신경망의 층을 이야기할 때 포함하지는 않습니다.

3. 옵티마이저

모델을 훈련할 때 사용하는 경사 하강법 알고리즘을 옵티마이저라고 합니다. 저희가 앞서 배웠던 가장 기본적인 미니배치 경사하강법은 SGD입니다.

1
model.compile(optimizer='sgd', loss='sparse_categorical_crossentropy', metrics='accuracy') # 기본 미니 배치 경사하강법을 사용

optimizer에 ‘sgd’를 할당하면 sgd 객체를 자동으로 생성해 줍니다. 직접 객체를 생성해 다음과 같이 쓸 수도 있습니다.

1
2
sgd = keras.optimizers.SGD() # 위 코드와 동일
model.compile(optimizer=sgd, loss='sparse_categorical_crossentropy', metrics='accuracy')

파라미터를 바꾸고 싶다면 직접 객체를 만들어 사용해야 합니다.

(1) 모멘텀 최적화

미니배치 경사 하강법 알고리즘을 개선하기 위한 방법으로 모멘텀 최적화가 있습니다.

1
sgd = keras.optimizers.SGD(momentum=0.9, nesterov=True) # 모멘텀 : 이전 그레이디언트를 가속도처럼 사용

모멘텀 최적화란 이전 그레이디언트를 가속도처럼 사용하는 기법입니다. 네스테로프 모멘텀 최적화는 모멘텀 최적화를 2번 구현하여 사용하는 것입니다.

(2) 적응적 학습률

adagrad와 rmsprop 알고리즘은 적응적 학습률을 사용하는 알고리즘입니다. 적응적 학습률이란 모델이 최적점에 가까이 갈수록 학습률을 낮추는 것입니다.

1
2
adagrad = keras.optimizers.Adagrad() # 모델이 최적점에 갈수록 학습률을 낮추는 적응적 학습률
model.compile(optimizer=adagrad, loss='sparse_categorical_crossentropy', metrics='accuracy')

RMSprop는 옵티마이저의 기본값입니다.

1
2
rmsprop = keras.optimizers.RMSprop() # 옵티마이저 매개변수의 기본값, 적응적 학습률 사용
model.compile(optimizer=rmsprop, loss='sparse_categorical_crossentropy', metrics='accuracy')

(3) adam

adam은 모멘텀 최적화와 적응적 학습률을 모두 사용하는 알고리즘입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics='accuracy') # 모멘텀과 적응적 학습률을 모두 사용

model.fit(train_scaled, train_target, epochs=5)
=>Epoch 1/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.5227 - accuracy: 0.8167
Epoch 2/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3909 - accuracy: 0.8605
Epoch 3/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3537 - accuracy: 0.8707
Epoch 4/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3285 - accuracy: 0.8794
Epoch 5/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3074 - accuracy: 0.8858
<keras.callbacks.History at 0x7f649c3cfd50>

model.evaluate(val_scaled, val_target) # 일반적으로 rmsprop보다 조금 나은 결과
=>375/375 [==============================] - 1s 2ms/step - loss: 0.3612 - accuracy: 0.8688
[0.36117008328437805, 0.8688333630561829]

일반적으로 기본값인 RMSprop보다 조금 더 나은 결과를 도출합니다.

공부한 내용의 전체 코드는 github에 작성해 두었습니다.

https://github.com/ynkim0/study/blob/main/7-2.ipynb

This post is licensed under CC BY 4.0 by the author.