| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | """PyTorch optimization for OpenAI GPT model.""" |
| |
|
| | import math |
| | import torch |
| | from torch.optim import Optimizer |
| | from torch.optim.optimizer import required |
| | from torch.nn.utils import clip_grad_norm_ |
| | import logging |
| |
|
| | logger = logging.getLogger(__name__) |
| |
|
| | def warmup_cosine(x, warmup=0.002): |
| | if x < warmup: |
| | return x/warmup |
| | x_ = (x - warmup) / (1 - warmup) |
| | return 0.5 * (1. + math.cos(math.pi * x_)) |
| |
|
| | def warmup_constant(x, warmup=0.002): |
| | """ Linearly increases learning rate over `warmup`*`t_total` (as provided to OpenAIAdam) training steps. |
| | Learning rate is 1. afterwards. """ |
| | if x < warmup: |
| | return x/warmup |
| | return 1.0 |
| |
|
| | def warmup_linear(x, warmup=0.002): |
| | """ Specifies a triangular learning rate schedule where peak is reached at `warmup`*`t_total`-th (as provided to OpenAIAdam) training step. |
| | After `t_total`-th training step, learning rate is zero. """ |
| | if x < warmup: |
| | return x/warmup |
| | return max((x-1.)/(warmup-1.), 0) |
| |
|
| | SCHEDULES = { |
| | 'warmup_cosine':warmup_cosine, |
| | 'warmup_constant':warmup_constant, |
| | 'warmup_linear':warmup_linear, |
| | } |
| |
|
| |
|
| | class OpenAIAdam(Optimizer): |
| | """Implements Open AI version of Adam algorithm with weight decay fix. |
| | """ |
| | def __init__(self, params, lr=required, schedule='warmup_linear', warmup=-1, t_total=-1, |
| | b1=0.9, b2=0.999, e=1e-8, weight_decay=0, |
| | vector_l2=False, max_grad_norm=-1, **kwargs): |
| | if lr is not required and lr < 0.0: |
| | raise ValueError("Invalid learning rate: {} - should be >= 0.0".format(lr)) |
| | if schedule not in SCHEDULES: |
| | raise ValueError("Invalid schedule parameter: {}".format(schedule)) |
| | if not 0.0 <= warmup < 1.0 and not warmup == -1: |
| | raise ValueError("Invalid warmup: {} - should be in [0.0, 1.0[ or -1".format(warmup)) |
| | if not 0.0 <= b1 < 1.0: |
| | raise ValueError("Invalid b1 parameter: {}".format(b1)) |
| | if not 0.0 <= b2 < 1.0: |
| | raise ValueError("Invalid b2 parameter: {}".format(b2)) |
| | if not e >= 0.0: |
| | raise ValueError("Invalid epsilon value: {}".format(e)) |
| | defaults = dict(lr=lr, schedule=schedule, warmup=warmup, t_total=t_total, |
| | b1=b1, b2=b2, e=e, weight_decay=weight_decay, vector_l2=vector_l2, |
| | max_grad_norm=max_grad_norm) |
| | super(OpenAIAdam, self).__init__(params, defaults) |
| |
|
| | def get_lr(self): |
| | lr = [] |
| | for group in self.param_groups: |
| | for p in group['params']: |
| | state = self.state[p] |
| | if len(state) == 0: |
| | return [0] |
| | if group['t_total'] != -1: |
| | schedule_fct = SCHEDULES[group['schedule']] |
| | lr_scheduled = group['lr'] * schedule_fct(state['step']/group['t_total'], group['warmup']) |
| | else: |
| | lr_scheduled = group['lr'] |
| | lr.append(lr_scheduled) |
| | return lr |
| |
|
| | def step(self, closure=None): |
| | """Performs a single optimization step. |
| | |
| | Arguments: |
| | closure (callable, optional): A closure that reevaluates the model |
| | and returns the loss. |
| | """ |
| | loss = None |
| | if closure is not None: |
| | loss = closure() |
| |
|
| | warned_for_t_total = False |
| |
|
| | 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] |
| |
|
| | |
| | if len(state) == 0: |
| | state['step'] = 0 |
| | |
| | state['exp_avg'] = torch.zeros_like(p.data) |
| | |
| | state['exp_avg_sq'] = torch.zeros_like(p.data) |
| |
|
| | exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq'] |
| | beta1, beta2 = group['b1'], group['b2'] |
| |
|
| | state['step'] += 1 |
| |
|
| | |
| | if group['max_grad_norm'] > 0: |
| | clip_grad_norm_(p, group['max_grad_norm']) |
| |
|
| | |
| | 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['e']) |
| |
|
| | bias_correction1 = 1 - beta1 ** state['step'] |
| | bias_correction2 = 1 - beta2 ** state['step'] |
| |
|
| | if group['t_total'] != -1: |
| | schedule_fct = SCHEDULES[group['schedule']] |
| | progress = state['step']/group['t_total'] |
| | lr_scheduled = group['lr'] * schedule_fct(progress, group['warmup']) |
| | |
| | if group['schedule'] == "warmup_linear" and progress > 1. and not warned_for_t_total: |
| | logger.warning( |
| | "Training beyond specified 't_total' steps with schedule '{}'. Learning rate set to {}. " |
| | "Please set 't_total' of {} correctly.".format(group['schedule'], lr_scheduled, self.__class__.__name__)) |
| | warned_for_t_total = True |
| | |
| | else: |
| | lr_scheduled = group['lr'] |
| |
|
| | step_size = lr_scheduled * math.sqrt(bias_correction2) / bias_correction1 |
| |
|
| | p.data.addcdiv_(-step_size, exp_avg, denom) |
| |
|
| | |
| | if (len(p.size()) > 1 or group['vector_l2']) and group['weight_decay'] > 0: |
| | p.data.add_(-lr_scheduled * group['weight_decay'], p.data) |
| |
|
| | return loss |
| |
|