torch.optim에서 다루는 주요 내용에 대해서 살펴보자.
torch.optim 모듈은 1) 최적화 알고리즘과 2) 학습 스케쥴러를 사용할 수 있도록 지원하고 있다.
그리고 최적화 과정에서 사용되는 step, zero_grad 등도 알아두면 좋다.
최적화 알고리즘은 최적의 Loss를 찾기 위해서 learning rate와 moment를 고려해 Global minima를 찾아가기 위한 알고리즘이다.
최적화 알고리즘은 모델의 성능과 수렴 속도를 크게 좌우할 수 있다.
Global minima를 찾아가다가 Local minima에 빠지는 문제가 발생하거나 혹은 Plateau에 갇혀서 학습이 느려지는 문제가 발생할 수 있기 때문이다.
따라서, 학습 속도가 빠르면서도 최적의 Loss를 찾기 위해서 다양한 알고리즘이 연구되어 왔다.
torch.optim에서 제공하는 최적화 알고리즘의 파라미터로는 최적화가 필요한 모델의 파라미터가 입력되어야 하는데, iterable한 데이터 구조여야 한다. (list, dict 등)
SGD (Stochastic Gradient Descent)
가장 기본적인 확률적 경사하강법
전제 데이터 셋을 사용할 경우 학습이 느려지고, 메모리 문제가 발생할 수도 있어 무작위로 선택한 mini-batch를 사용하여 가중치를 업데이트 하도록 한다.
optimizer = torch.optim.SGD(model.parameters(), lr = 0.01)
Momentum SGD
SGD에 momentum을 추가하여 일종의 관성적인 방향성을 부여한 알고리즘
momentum은 이전에 계산된 gradient를 일정 비율로 사용하여 현재 기울기를 보정하는 방식이다.
SGD보다 수렴 속도가 빠르고, 진동을 줄이는 효과가 있다.
optimizer = torch.optim.SGD(model.parametrs(), lr = 0.01, momentum = 0.9)
Adagard
학습률을 각 매개변수에 대해 적응적으로 조정하여, 자주 업데이트되는 매개변수의 학습률을 줄임
희소 데이터 셋에서 유리함
optimizer = torch.optim.Adagrad(model.parameters(), lr = 0.01)
RMSProp
학습률을 각 파라미터에 대해 적응적으로 조정하여 학습을 안정화 시킴
Adagrad의 단점을 보완하여 보다 효과적으로 작동
optimizer = torch.optim.RMSprop(model.parameters(), lr = 0.01)
optimizer = torch.optim.Adam(model.parameters(), lr = 0.001)
앞서 다룬, Learning rate를 동적으로 조정하는 방식을 학습률 스케쥴링이라고 함
학습률 스케쥴링 역시도 최적화 알고리즘과 함께 모델의 성능과 수렴 속도를 크게 향상시키는 방법 중에 하나임
(실제로, 나도 대회에 나갔을 때 학습 스케쥴러 조정만으로 성능이 상승하여 100위권에서 30위권까지 한 번에 뛴 경험이 있음)
StepLR
일정한 epoch 간격마다 학습률을 감소시키는 스케쥴러
step_size 에포크 마다 학습률을 gamma 배로 감소시킴
scheduiler = torch.optim.lr_scheduler.StepLR(optimizer, step_size = 30, gamma = 0.1)
MultiSetpLR
지정된 리스트 내의 에포크마다 학습률을 감소 시킴
scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones = [30, 80], gamma = 0.1)
ExponentialLR
매 에포크마다 학습률을 gamma배로 지수적으로 감소시키는 스케쥴러
scheduler = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma = 0.9)
CosineAnnealingLR
학습률을 Cosine 함수 형태로 변화시키는 스케쥴러
T_max를 주기로 학습률이 최소값에 도달하며, 주기를 반복함
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max = 50)
ReduceLROnPlateau
성능이 더 향상되지 않을 때 학습률을 감소시키는 스케쥴러
주로 검증 성능이 개선되지 않을 때 학습률을 조정함
scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode = 'min', factor = 0.1, patience = 10)
CyclicLR
학습률을 주기적으로 변화시키는 스케쥴러
base_lr과 max_lr 사이에서 학습률을 주기적으로 변동시킴
scheduler = torch.optim.lr_scheduler.CyclicLR(optimizer, base_lr = 0.001, max_lr = 0.01, step_size = 2000)
OneCycleLR
한 번의 학습 주기 동안 학습률을 처음에는 증가시키고, 그 다음에는 감소시키는 스케쥴러
scheduler = torch.optim.lr_scheduler.OneCycleLR(optimizer, max_lr = 0.01, steps_per_epoch = len(train_loader), epochs = 10)
앞서 설명된 7개의 학습 스케쥴러 중에서 가장 성능이 좋다고 알려진 스케쥴러는 아래와 같다.
Optimizer 객체는 zero_grad라는 함수도 제공한다.
optimizer.zero_grad를 사용해서 역전파 직전에 그래디언트를 초기화하기 위해 사용한다.
만약 매 학습 스텝마다 이 optimizer.zero_grad()를 사용하여 그래디언트 초기화를 진행하지 않는다면, 이전 스텝에서 사용한 그래디언트에서부터 시작하여 잘못된 수치가 누적되고 가중치가 업데이트 되는 문제가 발생한다.
따라서, 모든 모델 파라미터의 그래디언트를 초기화 하여 올바른 그래디언트를 계산할 수 있도록 하기 위해 사용한다.
(※ 가중치를 초기화 하는 방법으로 사용하는 Xavier Initilization, He Initialziation과 그래디언트를 초기화한다는 개념은 서로 완전히 다른 내용이다. 내가 헷갈려서 적어놓음)
역전파 과정에서 계산된 그래디언트로
위와 같이 가중치 파라미터를 업데이트 해야 하는데, 이 가중치를 업데이트 하기 위해서 optimizer.step()을 사용한다.
순서대로 보자면,
1. optimizer.zero_grad()로 그래디언트를 초기화하고,
2. loss.backward()로 최종 손실함수에 대한 그래디언트를 역전파로 계산하고,
3. 역전파로 gradient를 계산한 결과 계산된 가중치 행렬을 업데이트 해야 한다.
optimizer.zero_grad()
loss.backward()
optimizer.step()
학습 에포크를 반복할 때 마다 위 세 가지 과정을 사용해야 그래디언트 초기화, 역전파, 가중치를 업데이트를 매 에포크마다 반복해야 하는 것이다.
만약, optimizer.step()으로 가중치 업데이트를 해주지 않으면 모델이 학습되지 않는다.