페이지

2022년 7월 23일 토요일

3.1 신경망의 구조

 이전 장에서 본 것처럼 신경망 훈련에는 다음 요소들이 과련되어 있습니다.

1) 네트워크(또는 모델)를 구성하는 층

2) 입력 데이터와 그에 상응하는 타깃

3) 학습에 사용할 피드백 신호를 정의하는 손실 함수

4) 학습 진행 방식을 결정하는 옵티마이저

이들 간의 상호 작용을 그림 3-1에 나타냈습니다. 연속된 층으로 구성된 네트워크가 입력 데이터를 예측으로 매핑합니다. 손실 함수는 예측과 타깃을 비교하여 네트워크의 예측이 기댓값에 얼마나 잘 맞는지를 측정하는 손실 값을 만듭니다. 옵티마이저는 손실 값을 사용하여 네트워크 가중치를 업데이트 합니다. 층, 네트워크, 손실 함수, 옵티마이저에 대해 자세히 살펴보겠습니다.

2022년 7월 22일 금요일

3. 신경만 시작하기

 이 장에서는 신경망을 사요ㅕㅇ하여 실제 문제를 풀어 봅니다. 2장의 첫 번째 예제에서 얻은 지식을 정리하고, 신경망이 가장 많이 사용되는 세 종류의 문제인 이진 분류, 다중 분류, 스칼라 값을 예측하는 회귀에 배운 것을 적용해 봅니다.

또 2장에서 소개했던 층, 네트워크, 목적함수, 옵티마이저 같은 신경망의 핵심 구성 요소들을 자세히 살펴봅니다. 이 책에서 사용할 파이썬 딥러닝 라이브러리인 케라스(Keras)도 간략히 소개하며, 텐서플로, 케라스, GPU를 사용한 딥러닝을 실행하기 위해 컴퓨터를 셋팅합니다. 실전 문제를 해결하는데 신경망을 어떻게 사용하는지 세 가지 기본 예제로 자세히 살펴봅니다.

1) 영화 리뷰를 긍정 또는 부정으로 분류하기(이진 분류)

2) 신문 기사를 토픽으로 분류하기(다중 분류)

3) 부동산 데이터를 바탕으로 주택 가격을 예측하기(회귀)

이장이 끝에 다다르면 신경망으로 벡터 데이터를 사용한 분류나 회귀 같은 간단한 머신 러닝 문제를 해결할 수 있고, 4장에서는 머신러닝의 원리와 이론을 더 깊게 이해할 수 있을 것입니다.

2.6 요약

 1) 학습(Learning)은 훈련 데이터 샘플과 그에 상응하는 타깃이 주어졌을 때 손실 함수를 최소화하는 모델 파라미터의 조합을 찾는 것을 의미합니다.

2) 데이터 샘플과 타깃의 배치를 랜덤하게 뽑고 이 배치에서 손실에 대한 파라미터의 그ㄹ디언트를 계산함으로써 학습이 진행 됩니다. 네트워크의 파라미터는 그래디언트의 반대 방향으로 조금식(학습률에 의해 정의된 크기만큼) 움직입니다.

3) 전체 학습 과정은 신경망이 미분 가능한 텐서 연산으로 연결되어 있기 때문에 가능합니다. 현재 파라미터와 배치 데이터를 그래디언트 값에 매핑해 주는 그래디언트 함수를 구성하기 위해 미분의 연쇄 법칙을 사용합니다.

4) 이어지는 장에서 자주 보게 될 두 가지 핵심 개념은 손실과 옵티마이저입니다. 이 두가지는 네트워크에 데이터를 주입하기 전에 정의되어야 합니다.

5) 손실은 훈련하는 동안 최소화해야 할 양이므로 해결하려는 문제의 성공을 측정하는 데 사용합니다.

6) 욥티마이저는 손실에 대한 그래디언트가 파라미터를 업데이트하는 정확한 방식을 정의합니다. 예를 들어 RMSProp옵티마이저, 모멘텀을 사용한 SGD등입니다.

2.5 첫 번째 예제 다시 살펴보기

 이 장 끝부분에 다다랐습니다. 이제 신경망의 이면에 어떤 원리가 있는지 기초적인 내용을 이해했을 것입니다. 첫 번째 예제로 다시 돌아가서 이전 절에서 배웠던 내용을 이용하여 코드를 자세하게 리뷰합시다.

먼저 입력 데이터입니다.

(train_image, train_labels), (test_images, test_labels) = mnist.load_data()

train_images = train_images.reshape((60000, 28 * 28))

train_images = train_images.astype('float32')/255

test_images = test_images.reshape((10000, 28 * 28))

test_images = test_images.astype('float32')/255

입력 이미지의 데이터 타입은 float32로, 훈련 데이터는 (60000, 784)크기, 테스트 데이터는 (10000, 784) 크기의 넘파이 배열로 저장됩니다.

우리가 사용할 신경망입니다.

network = models.Sequential()

network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28, ))

network.add(layer.Dense(10, activation='softmax'))

이 네트워크는 2개의 Dense층이 연결되어 있고 각 층은 가중치 텐서를 포함하여 입력 데이터에 대한 몇 개의 간단한 텐서 연산을 적용합니다. 층의 속성인 가중치 텐서는 네트워크가 정보를 정하는 곳입니다.

이제 네트워크를 컴파일하는 단계입니다.

network.compile(optimizer='rmsprop', 

                    loss='categorical_crossentropy',

                    metrics=['accuracy'])

categorical_crossentropy는 손실 함수입니다. 가중치 텐서를 학습하기 위한 피드백 신호로 사용되면 훈련하는 동안 최소화됩니다. 미니 배치 확률적 경사 하강법을 통해 손실이 감소됩니다. 경사 하강법을 적용하는 구체적인 방식은 첫 번째 매개변수로 전달된 rmsprop 옵티마이저에 의해 결정됩니다.

마지막으로 훈련 반복입니다.

network.fit(train_images, train_balels, epochs=5, batch_size=128)

fit 메서드를 호출했을 때 다음과 같은 일이 일어납니다. 네트워크가 128개 샘플씩 미니 배치훈련 데이터를 다섯 번 반복합니다(전체 훈련 데이터에 수행되는 각 반복을 에포크(epoch)라고 합니다). 각 반복마다 네트워크가 배치에서 손실에 대한 가중치의 그래디언트를 계산하고 그에 맞추어 가중치를 업데이트합니다. 다섯 번의 에포크 동안 네트워크는 2,345버의 그래디언트 업데이트를 수행할 것입니다(에포크마다 465번). 아마 네트워크의 손실이 충분하게 낮아져서 높은 정확도로 손글씨 숫자를 구분할 수 있을 것 입니다. 여기까지 읽었다면 이미 신경망에 대해 많이 알았을 것입니다.

2.4.4 변화율 연결: 역전파 알고리즘

 앞의 알고리즘에서 함수가 미분 가능하기 때문에 변화율을 직접 계산할 수 있다고 잠시 가정했습니다. 실제로 신경망은 많은 텐서 연산으로 구성되어 있고 이 연산들의 변화율은 간단하며 이미 잘 알려져 있습니다. 3개의 텐서 연산 a, b, c와 가중치 행렬 w1, w2, w3로 구성된 네트워크 f를 예를 들어 보겠습니다.

f(W1, W2, W3) = a(W1, b(W2, c(W3)))

미적분에서 이렇게 연결된 함수는 연쇄 법칙(chain rule)이라 부르는 다음 항등식 f(g(x))' = f'(g(x)) * g'(x)를 사용하여 유도될 수 있습니다. 연쇄 법칙을 신경망의 그래디언트 계산에 적용하여 역전파(Backpropagation) 알고리즘(후진 모드 자동 미분(reverse-mode automatic differentiation)이라고 부릅니다)이 탄생되었습니다. 역전파는 최종 손실 값에서부터 시작합니다. 손실 값에 각 파라미터가 기여한 정도를 계산하기 위해 연쇄 법칙을 적용하여 최상위 층에서 하위 층까지 걱꾸로 진행됩니다.

요즘에는 그리고 향후 몇 년동안은 텐서플로처럼 기호 미분(symbolic differentiation)이 가능한 최신 프레임워크를 사용하여 신경망을 구현할 것입니다. 이 말은 변화율이 알려진 연산들로 연결되어 있으면(연쇄 법칙을 적용하여) 네트워크 파라미터와 그래디언트 값을 매핑하는 그래디언트 함수를 계산할 수 있다는 의미입니다. 기호 미분 덕택에 역전파 알고리즘을 직넙 구현할 필요가 전혀 없고 정확한 역전파 공식을 유도하느라 시간과 노력을 소모하지 않아도 됩니다. 그래디언트 기반의 최적화가 어떻게 작동하는지 잘 이해하는 것으로 충분합니다.

2.4.3 확률적 경사 하강법

 미분 가능한 함수가 주어지면 이론적으로 이 함수의 최솟값을 해석적으로 구할 수 있습니다. 함수의 최솟값은 변화율이 0인 지점입니다. 따라서 우리가 할 일은 변화율이 0이 되는 지점을 모두 찾고 이 중에서 어떤 포인트의 함수 값이 가장 작은지 확인하는 것입니다.

산경망에 적용하면 가장 작은 손실 함수의 값을 만드는 가중치의 조합을 해석적으로 찾는 것을 의미합니다. 이는 식 gradient(f)(W) = 0 을 풀면 해결됩니다. 이 식은 N개의 변수로 이루어진 다항식입니다. 여기에서 N은 네트워크의 가중치 개수입니다. N = 2나 N = 3인 식을 푸는 것은 가능하지만 실제 신경망에서는 파라미터의 개수가 수천 개보다 적은 경우가 거의 없고 종종 수천만개가 되기 때문에 해석적으로 해결하는 것이 어렵습니다.

그 대신 앞서 2.4절에서 설명한 알고리즘 네 단계를 사용할 수 있습니다. 랜덤한 배치 데이터에서 현재 손실 값을 토대로 하여 조금씩 파라미터를 수정하는 것입니다. 미분 가능한 함수를 가지고 있으므로 그래디언트를 계산하여 단계 4를 효율적으로 구현할 수 있습니다. 그래디언튿의 반대방향으로 가중치를 업데이트하면 손실이 매번 조금씩 감소할 것입니다.

1) 훈련 샘플 배치 X와 이에 상응하는 타깃 y를 추출합니다.

2) x로 네트워크를 실행하고 예측 y_pred를 구합니다.

3) 이 배치에서 y_pred와 y사이의 오차를 측정하여 네트워크의 손실을 계산합니다.

4) 네트워크의 파라미터에 대한 손실 함수의 그래디언트를 계산합니다(역방향 패스(backward pass)).

5) 그래디언트의 반대 방향으로 파라미터를 조금 이동시킵니다. 예를 들어 W -= step * gradient 처럼 하면 배치에 대한 손실이 조금 감소할 것입니다.

아주 쉽네요! 방금 전에 이야기한 것이 미니 배치 확률적 경사 하강법(mini-batch stochastic gradient descent)(미니 배치 SGD)입니다. 확률적(stochastic)이란 단어는 각 배치 데이터가 무작위로 선택된다는 의미입니다(확률적이란 것은 무작위(random)하다는 것의 과학적 표현입니다). 네트워크의 파라미터와 훈련 샘플이 하나일 때 이 과정을 나타냈습니다.

step값을 적절히 고르는 것이 중요합니다. 이 값이 너무 작으면 곡선을 따라 내려가는 데 너무 많은 반복이 필요하고 지역 최솟값(local minimum)에 갇힐 수 있습니다. step이 너무 크면 손실 함수 곡선에서 완전히 임의의 위치로 이동시킬 수 있습니다.

미니 배치 SGD알고리즘의 한 가지 변종은 반복마다 하나의 샘플과 하나의 타깃을 뽑는 것이다. 이것이 (미니 배치 SGD와 반대로) 진정한(true) SGD입니다. 다른 한편으로 극단적인 반대의 경우를 생각해 보면 가용한 모든 데이터를 사용하여 반복을 실행할 수 있습니다. 이를 배치 SGD (batch SGD)라고 합니다.더 정확하게 업데이트되지만 더 많은 비요이 듭니다 극단적인 두가지 방법이 효율적인 절충안은 적절한 크기의 미니 배치를 사용하는 것입니다.

그러럼 2-11은 1D파라미터 공간에서 경사 하강법을 설명하고 있지만 실제로는 매우 고차원 공간에서 경사 하강법을 사용하게 됩니다. 신경망에 있는 각각의 가중치 값은 이 공간에서 하나의 독립된 차원이고 수만 또는 수백만 개가 될 수도 있습니다. 손실 함수의 표면을 좀 더 쉽게 이해하기 위해 2D 손실 함수의 표면을 따라 진행하는 경사 하강법을 시각화해 볼 수 있습니다. 하지만 신경망이 훈련되는 실제 과정을 시각화하기는 어렵습니다. 사람이 이해할 수 있도록 1,000,000차원의 공간을 표현하는 것이 불가능하기 때문입니다. 그렇기 때문에 저차원 표현으로 얻는 직관이 실전과 항상 맞지는 않는다는 것을 유념해야 합니다. 이는 딥러닝 연구 분야에서 오랫동안 여러 이슈를 일으키는 근원이었습니다.

또 업데이트할 다음 가중치를 계산할 때 현재 그래디언트 값만 보지 않고 이전에 업데이트된 가중치를 여러 가지 다른 방식으로 고려하는 SGD 변종이 많습니다. 예를 들어 모멘텀을 사용한 SGD, Adagrad, RMSProp 등입니다. 이런 변종들을 모두 최적화 방법(optimization method)또는 옵티마이저라고 부릅니다. 특히 여러 변종들에서 사용하는 모멘텀(momentum)개념은 아주 중요합니다. 모멘텀은 SGD에 있는 2개의 문제점인 수렴 속도와 지역 최솟값을 해결합니다. 네트워크의 파라미터 하나에 대한 손실 값의 곡선을 보여 줍니다.

그림에서 볼 수 있듯이 어떤 파라민터 값에서는 지역 최솟값에 도달합니다. 그 지점 근처에서는 왼쪽으로 이동해도 손실이 증가하고, 오른쪽으로 이동해도 손실이 증가합니다. 대상 파라미터가 작은 학습률을 가진 SGD로 최적화 되었다면 최적화 과정이 전역 최솟값으로 향하지 못하고 지역 최솟값에 갇히게 될 것입니다.

물리학에서 영감을 얻는 모멘텀을 사용하여 이 문제를 피할 수 있습니다. 여기에서 최적화 과정을 손실 곡선 위로 작은 공을 굴리는 것으로 생각하면 쉽게 이해할 수 있습니다. 모멘텀이 충분하면 공이 골짜기에 갇히지 않고 전역 최솟값에 도달할 것입니다. 모멘텀은 현재 기울기 값(현재 가속도)뿐만 아니라 (과거의 가속도로 인한) 현재 속도를 함깨 고려하여 각 단계에서 공을 움직입니다. 실전에 적용할 때는 현재 그래디언트 값뿐만 아니라 이전에 업데이트한 파라미터에 기초하여 파라미터 w를 업데이트합니다. 다음은 단순한 구현 예입니다.

past_velocity = 0.

momentum = 0.1................모멘텀 상수

while loss > 0.01: ........................... 최적화 반복 루프

    w, loss, gradient = get_current_parameters()

    velocity = momentum * past_velocity - learning_Rate *( gradient

    w = w + mementum * velocity - learning_rate * gradient

    past_velocity = velocity

    update_parameter(w)

2.4.2 텐서 연산의 변화율: 그래디언트

 그래디언트는 턴서 연산의 변화율입니다. 이는 다차원 입력, 즉 턴서를 입력으로 받는 함수에 변화율 개볌을 확장시킨 것입니다.

입력 벡터 x, 행렬  W, 타킷 y와 손실 함수 loss가 있다고 가정합시다. W를 사용하여 타깃의 예측 y_pred를 계산하고 손실, 즉 타깃 예측 y_pred와 타깃 y 사이의 오차를 계산할 수 있습니다.

y_pred = dot(W, x)

loss_value = loss(y_red, y)

입력 데이터 x와 y가 고정되어 있다면 이 함수는 W를 손실 값에 매핑하는 함수로 볼 수 있습니다.

loss_value = f(w)

W 의 현재 값을 W0이라고 합시다. 포인트 W0에서 f의 변화율은 W와 같은 크기의 텐서인 gradient(f)(W0) 입니다. 이 텐서의 각 원소 gradient(f)(W0)[i, j]는 W0[i, j]를 변경했을 때 loss_value가 바뀌는 방향과 크기를 나타냅니다. 다시 말해 텐서 gradient(f)(W0)가 W0에서 함수 f(W)가 W0에서 함수 f(W) = loss_value의 그래디언트입니다.

앞서 함수 f(x)의 변화율 하나는 곡선 f의 기울기로 해석할 수 있다는 것을 보았습니다. 비슷하게 gradient(f)(W0)는 W0에서 f(W)의 기울기를 나타내는 텐서로 해석할 수 있습니다.

그렇기 때문에 함수 f(x)에 대해서는 변화율의 반대 방향으로 x를 조금 움직이면 f(x)의 값을 감소시킬 수 있습니다. 동일한 방식을 적용하면 함수 f(W)의 입장에서는 그래디언트의 반대 방향으로 W를 움직이면 f(W)의 값을 줄일 수 있습니다. 예를 들어 W1 = W0 - step * gradient(f)(W0)입니다(step은 스케일 조정하기 위한 작은 값입니다). 이 말은 기울기가 작아지는 곡면의 낮은 위치로 이동된다는 의미입니다. gradient(f)(W0)는 W0에 아주 가까이 있을때 기울기를 근사한 것이므로 W0에서 너무 크게 벗어나지 않기 위해 스케일링 비율 step이 필요합니다.