해당 시리즈는 프로그래밍 언어 중 하나인 줄리아(Julia)로 딥러닝(Deep learning)을 구현하면서 원리를 설명합니다.
드랍아웃(Dropout)이란
드랍아웃은 인공신경망 훈련 과정을 최적화하기 위한 방법 중 하나이며, 오버피팅(overfitting)을 방지함으로써 모델의 정확도를 높여준다.
Note 오버피팅(overfitting)이란? 신경망을 학습하는 과정에서 훈련데이터에만 적합한 형태로 학습되는 현상을 오버피팅이라고 한다. 훈련데이터의 정확도는 거의 100%를 달성하는데 실제데이터에서는 일정 이상의 정확도에서 상승하지 않는 것이다. 이런 현상은 보통 훈련데이터를 너무 적게 사용한 경우 또는 모델 파라미터가 너무 많은 경우에 발생한다.
드랍아웃은 각각의 훈련데이터들이 결과값으로 연결되는 신호(엣지, Edge)를 일정한 퍼센트로 삭제함으로써 훈련데이터의 일부만 파라미터에 영향을 줄 수 있도록 조정하는 역할을 한다. 드랍아웃을 함수로 만들면 다음과 같다.
드랍아웃 구현
드랍아웃은 입력된 비율에 따라 몇몇의 신호값들을 0으로 반환한다. 이를 함수로 구현하면 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
using Random
function drop_out_single(input_size, rate) function changing_T_or_F_with_percentage(number, input_size, rate) temp_num = input_size * rate if number > temp_num return0 else return1 end end temp = shuffle(reshape(1:input_size, 1, input_size)) return changing_T_or_F_with_percentage.(temp, input_size, rate) end
위 함수는 신호값과 곱하는 마스크를 생성한다. 드랍아웃에서는 비율을 입력값으로 받아 입력된 신호값의 일부 위치를 무작위로 선정하고, 그 자리의 신호를 0으로 반환해야 하는데 이를 구현하기 위해 기술적으로 신호값과 곱해주는 마스크를 생성하는 것이다. 하지만 이는 데이터 하나의 형태(한 줄)에만 작동하는 함수이다. 우리는 지금까지 배치데이터(여러 줄)를 사용해왔기 때문에 각각의 모든 줄에 위의 드랍아웃을 적용하는 함수가 필요하다.
Note 배치데이터에서 드랍아웃의 작동원리 드랍아웃은 각각의 데이터들이 동일한 비율로 신호값이 제거되어야 한다. 다시 말해, 한 줄로 나열된 데이터를 여러 개 합쳐놓은 매트릭스 형태의 배치데이터에서는 한 줄마다 일정한 비율을 유지해주면서 신호값을 제거해야 한다. 그렇기에 한 줄씩 인덱스를 무작위로 선정하여 제거해주는 작업이 필수적이다. 만약 이를 고려하지 않고 배치데이터를 드랍아웃한다면, 이는 효과가 거의 없다.
1 2 3 4 5 6 7 8 9
function drop_out(input_size, hidden_size, rate) temp = drop_out_single(hidden_size, rate) temp_num = input_size - 1 for i = 1:temp_num temp_1 = drop_out_single(hidden_size, rate) temp = [temp; temp_1] end return temp end
위 함수는 drop_out_single()을 배치데이터에 사용할 수 있도록 변환한 것이다. 위 함수에서 사용된 input_size와 hidden_size는 배치데이터에서 행렬의 형상이다. 예를 들어 입력된 신호값이 $10 \times 784$의 행렬이라면 이는 $1 \times 784$ 데이터가 총 10개가 포함된 배치데이터이기에 drop_out_single()을 input_size만큼 반복하는 것이다.
신호값과 곱해줄 드랍아웃 마스크는 완성되었다. 이제는 신호값과 마스크를 곱해주는 함수를 생성해보자. 참고로 드랍아웃도 신호값을 제거하는 과정이기에 이후 역전파에서 같은 위치의 미분값이 제거되어야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
mutable struct Dropout mask end
function dropout_forward(dropout, x, dropout_ratio) if dropout_ratio < 1 dropout.mask = drop_out(size(x)[1],size(x)[2], dropout_ratio) return x .* dropout.mask else dropout_ratio = 1 return x .* (1.0 - dropout_ratio) end end
function dropout_backward(dropout, dout) return dout .* dropout.mask end
이제 드랍아웃을 위한 함수 구현은 모두 끝났다. 다음으로는 위 함수들을 이용하여 드랍아웃을 적용한 모델과 적용하지 않은 모델을 비교해보자.
신경망 모델 구현
신경망 모델은 지금까지 구현했던 MNIST데이터를 사용하는 2층 신경망 모델을 다시 사용할 것이다. 역전파 모델에 대한 정보는 해당 글에서 확인할 수 있다. 또한 모델 구성에 필요한 함수들은 깃허브에서 찾아볼 수 있다. 준비가 완료되었다면 본격적으로 구현해보자.
1 2 3 4 5
#훈련데이터 300개
train_x = train_x[1:300,:] train_y = train_y[1:300,:] t = t[1:300,:]
이번 구현에서는 오버피팅을 발생시키기 위해서 60000개인 train_x데이터 중에서 300개만을 사용하여 학습시킬 것이다.
모델 학습을 위한 변수들을 정의한다. 참고로 여기서 사용된 making_network()는 이전글인 가중치 초기값에서 사용했던 함수와 다르다. 역전파 알고리즘에서 사용했던 함수와 동일하다. 만약 새로운 making_network()를 사용하고 싶다면, 가중치와 편향 입력 부분을 아래와 같이 변경하면 된다.
1 2 3 4 5 6 7
# 층에 들어갈 가중치와 편향 입력 W = ["W1", "W2"] b = ["b1", "b2"] input_size = (1, 784) hidden_size = [(784,50),(50,10)]