728x90
반응형
CosineAnnealingWarmRestarts
- 코드 : https://github.com/pytorch/pytorch/blob/v1.1.0/torch/optim/lr_scheduler.py#L655
- CosineAnnealingWarmRestarts에 대하여 다루어 보겠습니다.
(개인적으로 이 스케쥴러는 아쉽게 구현이 되어있습니다. 왜냐하면 warmup start가 구현되어 있지 않고 learning rate 최댓값이 감소하는 방법이 구현되어 있지 않기 때문입니다. 따라서 아래 따로 구현한 Custom CosineAnnealingWarmRestarts을 사용하길 바랍니다.) - 사용할 파라미터는 optimizer 외에 T_0, T_mult 그리고 eta_min이 있습니다.
- T_0는 최초 주기값 입니다.
- T_mult는 주기가 반복되면서 최초 주기값에 비해 얼만큼 주기를 늘려나갈 것인지 스케일 값에 해당합니다.
- eta_min은 learning rate의 최소값에 해당합니다.
scheduler = CosineAnnealingWarmRestarts(optimizer, T_0=50, T_mult=2, eta_min=0.001)
- 위 코드 예제는 50 epoch 주기를 초깃값으로 가지되 반복될수록 주기를 2배씩 늘리는 방법입니다.
- 앞에서 언급하였듯이 warmup start나 max값이 감소되는 기능은 없습니다.
Custom CosineAnnealingWarmRestarts
- 이번에 다룰 스케쥴러는 많은 논문에서 사용 중이고 SGDR로 알려져 있으며 특히 bag of tricks for image classification에서 사용한 최적화 방법으로 좋은 성능을 보입니다.
논문 : SGDR, Stochastic Gradient Descent with Warm Restarts (https://arxiv.org/abs/1608.03983)
논문 : bag of tricks for image classification (https://arxiv.org/abs/1812.01187)
- 간단하게 설명드리면 앞에서 다룬 CosineAnnealingWarmRestarts에 warm up start와 max 값의 감소 기능이 추가된 형태입니다.
- 아래 코드는 Pytorch의 기존 CosineAnnealingWarmRestarts를 변경하여 사용되었습니다.
import math
from torch.optim.lr_scheduler import _LRScheduler
class CosineAnnealingWarmUpRestarts(_LRScheduler):
def __init__(self, optimizer, T_0, T_mult=1, eta_max=0.1, T_up=0, gamma=1., last_epoch=-1):
## 예외처리
#초기 주기값
if T_0 <= 0 or not isinstance(T_0, int):
raise ValueError("Expected positive integer T_0, but got {}".format(T_0))
# 주기마다 배수할 값
if T_mult < 1 or not isinstance(T_mult, int):
raise ValueError("Expected integer T_mult >= 1, but got {}".format(T_mult))
# Warm up 시 필요한 주기
if T_up < 0 or not isinstance(T_up, int):
raise ValueError("Expected positive integer T_up, but got {}".format(T_up))
self.T_0 = T_0
self.T_mult = T_mult
self.base_eta_max = eta_max # 초기 learning rate
self.eta_max = eta_max # 갱신될 learning rate
self.T_up = T_up
self.T_i = T_0
self.gamma = gamma
self.cycle = 0
self.T_cur = last_epoch
super(CosineAnnealingWarmUpRestarts, self).__init__(optimizer, last_epoch)
def get_lr(self):
if self.T_cur == -1:
return self.base_lrs # optimizer에서 가져온 base_lrs
elif self.T_cur < self.T_up:
return [(self.eta_max - base_lr)*self.T_cur/self.T_up + base_lr for base_lr in self.base_lrs]
else:
return [base_lr + (self.eta_max - base_lr) * (1 + math.cos(math.pi * (self.T_cur-self.T_up) / (self.T_i - self.T_up))) / 2
for base_lr in self.base_lrs]
def step(self, epoch=None):
if epoch is None:
epoch = self.last_epoch + 1
self.T_cur = self.T_cur + 1
if self.T_cur >= self.T_i:
self.cycle += 1
self.T_cur = self.T_cur - self.T_i # 초기화
self.T_i = (self.T_i - self.T_up) * self.T_mult + self.T_up # 주기 조절
# # TODO: 나중에 코드 분석
# else:
# if epoch >= self.T_0:
# # 배수가 1일 때
# if self.T_mult == 1:
# self.T_cur = epoch % self.T_0
# self.cycle = epoch // self.T_0
# # 배수가 2이상인 경우
# else:
# n = int(math.log((epoch / self.T_0 * (self.T_mult - 1) + 1), self.T_mult))
# self.cycle = n
# self.T_cur = epoch - self.T_0 * (self.T_mult ** n - 1) / (self.T_mult - 1)
# self.T_i = self.T_0 * self.T_mult ** (n)
# else:
# self.T_i = self.T_0
# self.T_cur = epoch
self.eta_max = self.base_eta_max * (self.gamma**self.cycle) # 최대 learning rate 조절
self.last_epoch = math.floor(epoch)
for param_group, lr in zip(self.optimizer.param_groups, self.get_lr()):
param_group['lr'] = lr
- 먼저 warm up을 위하여 optimizer에 입력되는 learning rate = 0 또는 0에 가까운 아주 작은 값을 입력합니다.
- 위 코드의 스케쥴러에서는 T_0, T_mult, eta_max 외에 T_up, gamma 값을 가집니다.
- T_0, T_mult의 사용법은 pytorch 공식 CosineAnnealingWarmUpRestarts와 동일합니다.
- eta_max는 learning rate의 최댓값을 뜻합니다.
- T_up은 Warm up 시 필요한 epoch 수를 지정하며 일반적으로 짧은 epoch 수를 지정합니다.
- gamma는 주기가 반복될수록 eta_max 곱해지는 스케일값 입니다.
Learning rate Warmup
요즘 코드들을 보고 있으면 Learning rate warm-up scheduler가 종종 보인다.
이는 논문 Bag of Tricks for Image Classification with Convolutional Neural Networks (2018)에 나온 학습 방법 중 하나이다.
논문 내용을 해석해보면
Training이 시작될 때, 모든 parameters들은 보통 random values(initialized)이므로,
최종 solution에서 멀리 떨어져 있다.
이 때, 너무 큰 learning rate를 사용하면 numerical instability가 발생할 수 있기에,
초기에 작은 learning rate를 사용하고, training과정이 안정되면 초기 learning rate로 전환하는 방법이다.
optimizer = optim.Adam(model.parameters(), lr = 0)
scheduler = CosineAnnealingWarmUpRestarts(optimizer, T_0=150, T_mult=1, eta_max=0.1, T_up=10, gamma=0.5)
- 먼저 위 그래프의 연두색 선은 50 epoch을 나타냅니다. 따라서 T_0=150 epoch의 초기 주기값 후에 다시 0으로 줄어들게 됩니다. 이 때, T_up=10 epoch 만에 learning rate는 0 → eta_max 까지 증가하게 되고 다음 주기에는 gamma=0.5만큼 곱해진 eta_max * gamma 만큼 warm up start 하여 learning rate가 증가하게 됩니다.
- 앞에서도 언급하였지만 주의할 점은 optimizer에서 시작할 learning rate를 일반적으로 사용하는 learning rate가 아닌 0에 가까운 아주 작은 값을 입력해야 합니다.
scheduler = CosineAnnealingWarmUpRestarts(optimizer, T_0=50, T_mult=2, eta_max=0.1, T_up=10, gamma=0.5)
- 이번 예제에는 T_mult=2가 적용되었습니다. 따라서 주기가 반복될수록 T_0 * T_mult 만큼 주기가 늘어나게 됩니다. 따라서 위 예제와 같이 주기가 반복할수록 주기가 2배씩 늘어나는 것을 볼 수 있습니다.
scheduler = CosineAnnealingWarmUpRestarts(optimizer, T_0=100, T_mult=1, eta_max=0.1, T_up=10, gamma=0.5)
- 만약 어떤 것을 사용해야 할 지 모르겠다면 마지막 Custom CosineAnnealingWarmUpRestarts을 선언 후 사용해 보시길 추천드립니다.
[코드 작성에 필요한 함수]
math.floor( )
floor 함수는 실수를 입력하면내림하여 정수를 반환하는 함수이다. floor은 바닥을 의미한다. 실수의 바닥의 수인 바로 아래 정수로 내림한다고 생각하면 기억하기가 좋다.
함수를 사용 할 때는 math 모듈을 import 하고서 math.floor(x) 형태로 사용한다. 괄호( ) 안의 수 x는 float타입의 소수점이 있는 실수를 입력한다. ceil 함수와 마찬가지로 정수를 입력하는 경우에는 정수를 그대로 반환한다.
math.floor 함수를 사용하면 반환되는 값은 정수인 int타입이다. 실수를 내림하는 코드를 작성한 사용 예시는 아래와 같다.
>>> math.floor(3.14)
3
>>> math.floor(-3.14)
-4
>>> math.floor(0.15)
0
>>> math.floor(-0.15)
-1
>>> math.floor(3) # 정수는 그대로 정수로 반환
3
>>> math.floor(-3)
3
코사인 함수 : math.cos(x)
>>> a = math.pi / 6
>>> # returning the value of cosine of pi / 6
>>> print ("The value of cosine of pi / 6 is : ", end ="")
>>> print (math.cos(a))
The value of cosine of pi/6 is : 0.8660254037844387
728x90
반응형
'딥러닝&머신러닝 > 파이토치 모델 구현' 카테고리의 다른 글
[파이토치] 파이토치로 CNN 모델을 구현해보자! (ResNet편) (0) | 2023.01.25 |
---|
댓글