핸즈 온 머신러닝 (Hands-On Machine Learning with Scikit-Learn & TensorFlow) / 오렐리앙 제론 지음 , 박해선 옮김
을 읽고, 추후 기억을 되살릴 수 있게끔 나만의 방법으로 내용을 리뷰한다. 따라서 리뷰의 내용 별 비중이 주관적일 수 있다.
챕터 11. 심층 신경망 훈련
10장에 비해서 훨씬 더 깊은 심층 신경망을 훈련시킬 때의 문제들을 살펴보고 해결 방법을 공부한다.
11.1 그래디언트 소실과 폭주 문제
알고리즘이 하위층(입력층)으로 진행됨에 따라 그래디언트가 점점 작아진다면 그래디언트 소실 현상이라고 부르고, 점점 커져서 발산한다면 그래디언트 폭주라고 부른다. 과거 많이 사용되던 로지스틱 활성화 함수와, 평균이 0이고 표준편차가 1인 정규분포 초기화 방법의 조합은 출력층으로 갈수록 분산을 크게 만들었고, 로지스틱 활성화 함수의 양 끝단은 그래디언트가 0으로 수렴하기 때문에 이런 문제를 만들었다.
11.1.1 세이비어 초기화와 He 초기화
글로럿과 벤지오는 이 문제를 크게 완화시키는 초기화 방법을 제안했다. 위의 문제가 생기지 않기 위해서는 각 층의 출력에 대한 분산이 입력에 대한 분산과 같아야하며, 그래디언트 분산도 마찬가지이다. 경험적으로 이 문제를 해결해주는 초기화 방법은 평균이 0이고 표준편차가 sqrt(2 / (n_input + n_output))인 정규 분포로 만드는 것이다. 그 외에 활성화 함수 별 분포는 Xavier initialization, Glorot initialization 을 검색하여 알 수 있다. tensorflow에서는 기본적으로 세이비어 균등분포 초기화를 사용하며 variance_scaling_initializer() 함수를 사용하면 ReLU 활성화함수를 위한 He 초기화 방법으로 바꿀 수 있다.
11.1.2 수렴하지 않는 활성화 함수
ReLU 함수에는 dying ReLU 문제가 있는데 가중치 합이 음수가 되면 0을 출력하게 되고 이 때 그래디언트가 0이므로 뉴런이 다시 살아나기 힘들다. 이 문제를 해결하기 위해 LeakyReLU (z) = max (az, z)를 사용하거나 a를 무작위로 결정하는 RReLU (Randomized leaky ReLU), a가 학습되는 PReLU (parametric leaky ReLU) 등이 있다. LeakyReLU가 ReLU보다 주로 높은 성능을 보이며 RReLU의 랜덤성은 과대적합을 막는 규제의 역할을 하고 PReLU는 파라미터가 추가되어 과대적합의 위험이 있다.
혹은 z<0일 때 지수함수 형태를 보이는 ELU(exponential linear unit)은 모든 ReLU 변종의 성능을 앞질렀는데, 활성화 함수의 평균 출력이 0에 가까워지며 그래디언트가 0이 아니므로 죽은 뉴런을 만들지 않고, 모든 구간에서 매끄러워 경사 하강법의 속도를 높여주는 장점이 있다. 하지만 계산이 느리므로 테스트 시에는 느리다는 단점이 있다.
일반적으로 ELU > LeakyReLU > ReLU > tanh > 로지스틱을 사용하면 된다. tf에서 elu를 사용하고 싶다면 tf.nn.elu 함수를 사용하면 된다.
11.1.3 배치 정규화
초기화와 활성화 함수를 잘 정하더라도 훈련 과정에서 문제가 일어날 수 있으므로, 표준편차를 제한시키는 배치 정규화 기법을 적용할 수 있다. 이는 단순하게 입력 데이터의 평균과 표준편차를 정규화 한 뒤, 새로운 파라미터로 스케일을 조정한다. 새로운 파라미터 또한 학습한다. 테스트 할 때에는 평균과 표준편차를 계산할 미니배치가 없으니 입력 데이터가 아닌 훈련 데이터를 기반으로 스케일을 조정한다. 따라서 배치 정규화된 층마다 (평균, 표준편차, 새로운 평균, 새로운 표준편차)를 학습한다.
이는 로지스틱 활성화 함수 같이 수렴되는 활성화 함수를 사용하더라도 그래디언트 소실 문제를 크게 막아주며 큰 학습률을 사용할 수 있어 학습 속도를 크게 개선시켰다. 그러나 이는 모델의 복잡도를 키우고 시간을 느리게 하므로 적용 가능한 모델인지 판단해야한다.
텐서플로에서 배치 정규화를 사용하려면 layers.batch_normalization() 함수를 사용하면 된다. 이 때 momentum을 지정하여 지수 감소 이동 평균으로 파라미터를 계산한다. 모든 층에 함수를 적용하려면 중복되므로 파이썬의 partial() 함수를 사용하여 매개변수의 기본값을 지정할 수 있다.
그래프를 구성한 후, 훈련할 때에는 training placeholder를 True로 설정하고 UPDATE_OPS collection에서 이동 평균을 구하는 연산자를 뽑아내서 run 해주어야 한다.
11.1.4 그래디언트 클리핑
그래디언트 폭주 문제를 줄이기 위해 임곗값을 넘는 그래디언트를 단순히 잘라내는 Gradient Clipping 방법을 사용하여도 된다. tensorflow의 clip_by_value를 사용하여 정의하면 된다.
11.2 미리 훈련된 층 재사용하기
큰 규모의 DNN의 속도를 빠르게 하기 위해 비슷한 유형의 문제를 처리한 신경망을 찾아보고 하위층을 재사용하는 것이 좋다. 이를 transfer learning 이라고 한다.
11.2.1 텐서플로 모델 재사용하기
Saver의 import_meta_graph 를 통해 다른 모델을 불러오고, get_operation_by_name()과 get_tensor_by_name()을 이용하여 훈련 대상을 직접 지정해야 한다. 이 때, tensor는 포인터와 같은 개념이므로 같은 연산을 가리키는 텐서들은 :0, :1, :2 등이 뒤에 붙으며 구분된다. 따라서 연산 이름 뒤에 :0을 붙여주어야 한다.
미리 훈련된 모델에 대한 설명이 부족하면 그래프를 직접 뒤져야 하는데, get_operations() method를 활용하면 된다. 연산에 명확한 이름을 사용하고 문서화 해놓는 방법에는 collection이 있다. add_to_collection 으로 만들며 get_collection으로 불러온다.
다른 모델을 불러와, Collection을 토대로 하위층을 불러오고 모든 변수를 초기화 한 후 하위층의 parameter를 복원하고 상위층을 훈련하면 된다.
11.2.2 다른 프레임워크의 모델 재사용하기
다른 프레임워크의 모델을 재사용하는 것은 번거로운 작업이다. 파라미터들을 어떻게든 불러와서 모델을 직접 만들고 Assign handle을 구하여 대입해야 한다. 변수 이름에 /Assign이 붙은 Assign operation이 자동으로 정의되어 있으므로 이를 사용하면 된다.
11.2.3 신경망의 하위층을 학습에서 제외하기
하위층의 변수를 제외하고 훈련시킬 변수 목록을 optimizer에 var_list로 전달하면 하위층을 학습하지 않는다. 훈련되는 동안 변하지 않는 layer를 frozen layer라고 한다.
11.2.4 동결된 층 캐싱하기
동결된 층은 변하지 않기 때문에 출력을 캐싱하면 더 빨라질 것이다. 직접 데이터를 미리 돌려서, 출력값을 저장한 후 배치를 만들어 상위층을 훈련하면 된다. 상위층의 입력값에 feed_dict로 값을 전달하면 하위층을 학습하지 않을 것이다.
11.2.5 상위층을 변경, 삭제, 대체하기
원본 모델의 모든 층을 동결 시킨 후 상위층을 동결 해제하여 학습시키거나 (변경), 아예 제거하고 남은 은닉층 중 일부를 동결하여 학습시키거나 (삭제), 상위층의 구성을 변경하거나 추가하여 학습시킬 수 있다 (대체).
11.2.6 모델 저장소
여러 모델 저장소를 확인하여 자신이 학습하고자 하는 모델과 유사한 모델을 찾을 수 있다. 물론 본인의 모델들을 잘 정리해서 추후 사용하는 것도 중요하다.
예시 : (https://github.com/tensorflow/models)
11.2.7 비지도 사전훈련
레이블 된 훈련 데이터를 모으기 힘들다면 비지도 사전훈련을 진행할 수 있다. Restricted Boltzmann Machines나 오토 인코더 같은 비지도 특성 추출 알고리즘을 사용하여 초기 신경망을 만들고 역전파 알고리즘으로 파라미터를 튜닝한다. 시간이 오래 걸리고 번거로운 작업이지만 종종 잘 작동한다.
11.2.8 보조 작업으로 사전훈련
마지막 선택사항은 보조 작업에 신경망을 훈련시켜 하위층을 재사용하는 것이다. 예를 들어 얼굴 인식 시스템의 데이터가 별로 없다면 인터넷에서 무작위로 많은 인물 이미지를 수집한 후 두 개의 다른 이미지가 같은 사람인지 다른 사람인지 감지하는 신경망을 학습하여 하위층을 사용할 수 있다.
혹은, 각 훈련 샘플에 대해 샘플의 점수를 출력하도록 한 후 좋은 샘플의 점수가 나쁜 샘플의 점수보다 일정 마진 이상 크게 만드는 비용함수를 사용하는 방법을 max margin learning 이라고 한다.
11.3 고속 옵티마이저
아주 큰 심층 신경망에 대해서 경사 하강법 옵티마이저 대신 더 빠른 옵티마이저를 적용할 수 있다.
11.3.1 모멘텀 최적화
Momentum optimization은 항상 기울기만큼 이동하는 것보다 공이 빗면을 구르듯 가속하면 더 빨리 최저점에 도달할 수 있다는 개념으로 그래디언트가 가속도처럼 적용된다. 기존의 momentum vector m에 대해 b를 곱한 후 학습률 * 그래디언트를 더하여 파라미터를 이동한다. 그래디언트가 일정하다면 원래 그래디언트의 1/(1-b) 배가 종단속도가 된다. 이 기법은 스케일이 다른 입력값에 대해서도 비교적 빨라지며 local optima를 건너뛰는데에도 도움이 된다. GradientDescentOptimizer 대신 MomentumOptimizer를 사용하면 된다.
11.3.2 네스테로프 가속 경사
Nesterov Momentum optimization은 모멘텀 최적화의 변종으로, 현재 위치가 아니라 모멘텀의 방향으로 조금 앞서서 그래디언트를 계산하는 것이다. MomentumOptimizer에 user_nesterov=True를 설정하면 된다.
11.3.3 AdaGrad
AdaGrad 알고리즘은 가파른 차원을 따라 그래디언트 벡터의 스케일을 감소시키는 것이다. 벡터의 스케일을 학습하는 벡터 s에 대해 각 그래디언트 원소의 제곱을 더하고, 학습률에서 각 원소에 sqrt (s + eps)를 나누어주면 각 원소의 크기가 클수록 학습률이 작아진다. 이를 adaptive learning rate라고 부른다. AdagradOptimizer를 사용하면 되지만, 심층 신경망에는 너무 일찍 멈춰버리는 경향이 있어 사용하면 안된다.
11.3.4 RMSProp
RMSProp 알고리즘은 AdaGrad 알고리즘에서 s를 관성을 가지고 학습하는 것이다. RMSPropOptimizer class를 사용하면 된다. 아주 간단한 문제를 제외하고는 AdaGrad보다 대부분 성능이 좋다.
11.3.5 Adam 최적화
모멘텀 최적화와 RMSProp을 합친 adaptive moment estimation (Adam)은 momentum을 그래디언트에 따라 이동 평균내고, s를 AdaGrad 방법과 같이 이동평균내며 m / sqrt(s + eps)만큼 학습한다.
구체적으로는
m : b_1 m + (1 - b_1) grad
s : b_2 s + (1 - b_2) grad^2
m' : m / (1 - b_1 ^ t)
s' : s / (1 - b_2 ^ t)
theta : theta - learning_rate * m' / sqrt (s' + eps)
m'과 s'을 정의하는 이유는 초기에 값을 증폭시키기 위해서이다. 주로 b_1은 0.9, b_2는 0.999로 초기화한다.
tf의 AdamOptimizer를 사용하면 된다. 하지만, 이 알고리즘은 좋은 줄 알았으나 일부 데이터셋에서 나쁜 결과를 만드므로 사용을 보류하는 것이 좋을 수도 있다.
지금까지의 최적화 기법은 1차 편미분에 의존한다. 2차 편미분을 기반으로 한 뛰어난 알고리즘들도 있으나 이는 계산 복잡도가 O(n^2)이 되므로 너무 느리다.
희소 모델을 강력하게 훈련하고 싶다면 FTRL과 l1규제를 사용하면 된다. (FTRLOptimizer : FTRL-Proximal)
11.3.6 학습률 스케줄링
처음에는 빠르게 학습하고 나중에는 천천히 학습하는 것이 도움이 된다. 이를 위해 여러 가지 학습 스케줄 전략이 있다.
- 미리 정해둔 학습률을 특정 시점이후에 적용하(학습률을 정하기 위해 여러 시도를 해봐야 한다는 단점)
- 매 N스텝 마다 검증 오차를 측정하고 오차가 줄어들지 않는 경우 학습률 감소
- 반복 횟수에 따라 지수적으로 학습률 감소 (감소율 튜닝 필요)
- 다항식을 분모로 학습률 감소 (지수 기반 보다 느리게 감소)
주로 지수 기반 스케줄링을 사용한다. tf.train.exponential_decay를 이용하여 학습률을 직접 적용하고, 이 떄 사용되는 global_step은 학습에서 제외하여야 한다.
AdaGrad, RMSProp, Adam 최적화는 학습률을 알아서 감소시켜준다.
11.4 과대적합을 피하기 위한 규제 방법
파라미터가 많으므로 과대적합 될 위험이 있다. 따라서 여러 규제를 적용하여야 한다.
11.4.1 조기 종료
일정한 간격으로 Validation Data로 평가하여 성능이 떨어지려고 하면 중단하는 조기 종료 방법을 사용할 수 있다.
11.4.2 l1과 l2 규제
선형 회귀에 대해 했던 것 처럼 l1이나 l2규제를 적용하여 (일반적으로 bias는 제외) 제약을 가할 수 있다. layer를 선언할 때에 kernel_regularizer에 l1_regularizer(), l2_regularizer(), l1_l2_regularizer() 함수 등을 적용할 수 있다. 이 때 REGULARIZATION_LOSSES collections에서 연산들을 불러와 기존의 loss에 더해주어 새로운 loss를 정의하여야 한다.
11.4.3 드롭아웃
매 훈련 스텝에서 각 뉴런은 무시 될 확률 p를 가진다. 주로 p는 0.5를 사용한다. p를 드롭아웃 비율이라고 한다. 훈련 이후에는 드롭아웃을 적용하지 않는다. 이는 특징을 특정 뉴런에만 맡기지 않고 임의의 뉴런에 맡길 수 있으므로 일반적으로 좋은 성능을 가지게 된다. 매번 다른 신경망에 학습하는 효과가 있으므로 앙상블과 같은 효과를 얻을 수도 있다. 대신 테스트하는 동안 한 뉴런이 훈련때보다 평균적으로 1/(1-p)배 많은 입력 뉴런과 연결되므로 보존확률 (1-p)으로 계수를 나누어줄 필요가 있다.
11.4.4 맥스-노름 규제
널리 사용되는 다른 규제기법은 max-norm regularization이다. 각각의 뉴런에 대해 입력의 연결 가중치 w의 2-norm이 r이하가 되도록 제한한다. 일반적으로 w를 클리핑한다. clip_by_norm() 함수를 사용할 수 있다.
11.4.5 데이터 증식
기존의 데이터에서 새로운 데이터를 생성해 훈련 세트를 늘리는데, 단순한 백색소음은 도움이 되지 않는다. 예를 들어 이미지를 약간 회전하거나 약간 이동하거나 명암을 약간 다르게 한다면 많은 명암, 각도, 이동에 대해 학습할 수 있을 것이다.
11.5 실용적 가이드라인
주로 활용할 수 있는 가이드 라인.
[He 초기화, ELU 활성화 함수, 배치 정규화, 드롭아웃, 네스테로프 가속 경사, 학습률 스케줄링 없음]
'개발 인생 > ML' 카테고리의 다른 글
핸즈 온 머신러닝 :: 13. 합성곱 신경망 (0) | 2020.01.27 |
---|---|
핸즈 온 머신러닝 :: 12. 다중 머신과 장치를 위한 분산 텐서플로 (0) | 2020.01.24 |
핸즈 온 머신러닝 :: 10. 인공 신경망 소개 (0) | 2020.01.19 |
핸즈 온 머신러닝 :: 9. 텐서플로 시작하기 (0) | 2020.01.18 |
핸즈 온 머신러닝 :: 8. 차원 축소 (0) | 2020.01.18 |