Veblen34's picture
Upload folder using huggingface_hub
cd6775d verified
Raw
History Blame Contribute Delete
10.4 kB
# Copyright (c) 2019-present, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the license found in the
# LICENSE file in the root directory of this source tree.
#
import re
import math
import inspect
import torch
from torch import optim
class Adam(optim.Optimizer):
"""
Same as https://github.com/pytorch/pytorch/blob/master/torch/optim/adam.py,
without amsgrad, with step in a tensor, and states initialization in __init__.
It was important to add `.item()` in `state['step'].item()`.
"""
def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0):
if not 0.0 <= lr:
raise ValueError("Invalid learning rate: {}".format(lr))
if not 0.0 <= eps:
raise ValueError("Invalid epsilon value: {}".format(eps))
if not 0.0 <= betas[0] < 1.0:
raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0]))
if not 0.0 <= betas[1] < 1.0:
raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1]))
defaults = dict(lr=lr, betas=betas, eps=eps, weight_decay=weight_decay)
super().__init__(params, defaults)
for group in self.param_groups:
for p in group['params']:
state = self.state[p]
state['step'] = 0 # torch.zeros(1)
state['exp_avg'] = torch.zeros_like(p.data)
state['exp_avg_sq'] = torch.zeros_like(p.data)
def __setstate__(self, state):
super().__setstate__(state)
def step(self, closure=None):
"""
Step.
"""
loss = None
if closure is not None:
loss = closure()
for group in self.param_groups:
for p in group['params']:
if p.grad is None:
continue
grad = p.grad.data
if grad.is_sparse:
raise RuntimeError('Adam does not support sparse gradients, please consider SparseAdam instead')
state = self.state[p]
exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq']
beta1, beta2 = group['betas']
state['step'] += 1
# if group['weight_decay'] != 0:
# grad.add_(group['weight_decay'], p.data)
# Decay the first and second moment running average coefficient
exp_avg.mul_(beta1).add_(1 - beta1, grad)
exp_avg_sq.mul_(beta2).addcmul_(1 - beta2, grad, grad)
denom = exp_avg_sq.sqrt().add_(group['eps'])
# denom = exp_avg_sq.sqrt().clamp_(min=group['eps'])
bias_correction1 = 1 - beta1 ** state['step'] # .item()
bias_correction2 = 1 - beta2 ** state['step'] # .item()
step_size = group['lr'] * math.sqrt(bias_correction2) / bias_correction1
if group['weight_decay'] != 0:
p.data.add_(-group['weight_decay'] * group['lr'], p.data)
p.data.addcdiv_(-step_size, exp_avg, denom)
return loss
class AdamInverseSqrtWithWarmup(Adam):
"""
Decay the LR based on the inverse square root of the update number.
We also support a warmup phase where we linearly increase the learning rate
from some initial learning rate (`warmup-init-lr`) until the configured
learning rate (`lr`). Thereafter we decay proportional to the number of
updates, with a decay factor set to align with the configured learning rate.
During warmup:
lrs = torch.linspace(warmup_init_lr, lr, warmup_updates)
lr = lrs[update_num]
After warmup:
lr = decay_factor / sqrt(update_num)
where
decay_factor = lr * sqrt(warmup_updates)
"""
def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8,
weight_decay=0, warmup_updates=4000, warmup_init_lr=1e-7,
exp_factor=0.5):
super().__init__(
params,
lr=warmup_init_lr,
betas=betas,
eps=eps,
weight_decay=weight_decay,
)
# linearly warmup for the first warmup_updates
self.warmup_updates = warmup_updates
self.warmup_init_lr = warmup_init_lr
warmup_end_lr = lr
self.lr_step = (warmup_end_lr - warmup_init_lr) / warmup_updates
# then, decay prop. to the inverse square root of the update number
self.exp_factor = exp_factor
self.decay_factor = warmup_end_lr * warmup_updates ** self.exp_factor
# total number of updates
for param_group in self.param_groups:
param_group['num_updates'] = 0
def get_lr_for_step(self, num_updates):
if num_updates < self.warmup_updates:
return self.warmup_init_lr + num_updates * self.lr_step
else:
return self.decay_factor * (num_updates ** -self.exp_factor)
def step(self, closure=None):
super().step(closure)
for param_group in self.param_groups:
param_group['num_updates'] += 1
param_group['lr'] = self.get_lr_for_step(param_group['num_updates'])
class AdamCosineWithWarmup(Adam):
"""
Assign LR based on a cyclical schedule that follows the cosine function.
See https://arxiv.org/pdf/1608.03983.pdf for details.
We also support a warmup phase where we linearly increase the learning rate
from some initial learning rate (``--warmup-init-lr``) until the configured
learning rate (``--lr``).
During warmup::
lrs = torch.linspace(args.warmup_init_lr, args.lr, args.warmup_updates)
lr = lrs[update_num]
After warmup::
lr = lr_min + 0.5*(lr_max - lr_min)*(1 + cos(t_curr / t_i))
where ``t_curr`` is current percentage of updates within the current period
range and ``t_i`` is the current period range, which is scaled by ``t_mul``
after every iteration.
"""
def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8,
weight_decay=0, warmup_updates=4000, warmup_init_lr=1e-7,
min_lr=1e-9, init_period=1000000, period_mult=1, lr_shrink=0.75):
super().__init__(
params,
lr=warmup_init_lr,
betas=betas,
eps=eps,
weight_decay=weight_decay,
)
# linearly warmup for the first warmup_updates
self.warmup_updates = warmup_updates
self.warmup_init_lr = warmup_init_lr
warmup_end_lr = lr
self.lr_step = (warmup_end_lr - warmup_init_lr) / warmup_updates
# then, apply cosine scheduler
self.min_lr = min_lr
self.max_lr = lr
self.period = init_period
self.period_mult = period_mult
self.lr_shrink = lr_shrink
# total number of updates
for param_group in self.param_groups:
param_group['num_updates'] = 0
def get_lr_for_step(self, num_updates):
if num_updates < self.warmup_updates:
return self.warmup_init_lr + num_updates * self.lr_step
else:
t = num_updates - self.warmup_updates
if self.period_mult == 1:
pid = math.floor(t / self.period)
t_i = self.period
t_curr = t - (self.period * pid)
else:
pid = math.floor(math.log(1 - t / self.period * (1 - self.period_mult), self.period_mult))
t_i = self.period * (self.period_mult ** pid)
t_curr = t - (1 - self.period_mult ** pid) / (1 - self.period_mult) * self.period
lr_shrink = self.lr_shrink ** pid
min_lr = self.min_lr * lr_shrink
max_lr = self.max_lr * lr_shrink
return min_lr + 0.5 * (max_lr - min_lr) * (1 + math.cos(math.pi * t_curr / t_i))
def step(self, closure=None):
super().step(closure)
for param_group in self.param_groups:
param_group['num_updates'] += 1
param_group['lr'] = self.get_lr_for_step(param_group['num_updates'])
def get_optimizer(parameters, s):
"""
Parse optimizer parameters.
Input should be of the form:
- "sgd,lr=0.01"
- "adagrad,lr=0.1,lr_decay=0.05"
"""
if "," in s:
method = s[:s.find(',')]
optim_params = {}
for x in s[s.find(',') + 1:].split(','):
split = x.split('=')
assert len(split) == 2
assert re.match("^[+-]?(\d+(\.\d*)?|\.\d+)$", split[1]) is not None
optim_params[split[0]] = float(split[1])
else:
method = s
optim_params = {}
if method == 'adadelta':
optim_fn = optim.Adadelta
elif method == 'adagrad':
optim_fn = optim.Adagrad
elif method == 'adam':
optim_fn = Adam
optim_params['betas'] = (optim_params.get('beta1', 0.9), optim_params.get('beta2', 0.999))
optim_params.pop('beta1', None)
optim_params.pop('beta2', None)
elif method == 'adam_inverse_sqrt':
optim_fn = AdamInverseSqrtWithWarmup
optim_params['betas'] = (optim_params.get('beta1', 0.9), optim_params.get('beta2', 0.999))
optim_params.pop('beta1', None)
optim_params.pop('beta2', None)
elif method == 'adam_cosine':
optim_fn = AdamCosineWithWarmup
optim_params['betas'] = (optim_params.get('beta1', 0.9), optim_params.get('beta2', 0.999))
optim_params.pop('beta1', None)
optim_params.pop('beta2', None)
elif method == 'adamax':
optim_fn = optim.Adamax
elif method == 'asgd':
optim_fn = optim.ASGD
elif method == 'rmsprop':
optim_fn = optim.RMSprop
elif method == 'rprop':
optim_fn = optim.Rprop
elif method == 'sgd':
optim_fn = optim.SGD
assert 'lr' in optim_params
else:
raise Exception('Unknown optimization method: "%s"' % method)
# check that we give good parameters to the optimizer
expected_args = inspect.getargspec(optim_fn.__init__)[0]
assert expected_args[:2] == ['self', 'params']
if not all(k in expected_args[2:] for k in optim_params.keys()):
raise Exception('Unexpected parameters: expected "%s", got "%s"' % (
str(expected_args[2:]), str(optim_params.keys())))
return optim_fn(parameters, **optim_params)