疑问:
optimizer的step为什么不能放在mini-batch那个循环之外,optimizer.step和loss.backward的区别;
解惑:
首先需要明确optimizer优化器的作用,形象地说,优化器就是需要根据网络反向传播的梯度信息来更新网络参数,以起到降低loss函数计数值的作用,这也是机器学习中最一般的方法论。
从优化器的作用出发,需要使得优化器能够起作用,主要需要两个东西:1.优化器需要知道当前的网络的参数空间,这也是为什么在训练文件中,正式开始训练之前需要将网络参数放到优化器里面,比如使用Pytorch的话总会出现类似如下的代码:
2. 需要知道反向传播的梯度信息,我们还是从代码入手,如下所示是pytorch中SGD优化算法的step()函数具体写法,具体SGD的写法放在参考部分。
1 def step(self, closure=None): 2 """Performs a single optimization step. 3 Arguments: 4 closure (callable, optional): A closure that reevaluates the model 5 and returns the loss. 6 """ 7 loss = None 8 if closure is not None: 9 loss = closure() 10 11 for group in self.param_groups: 12 weight_decay = group['weight_decay'] 13 momentum = group['momentum'] 14 dampening = group['dampening'] 15 nesterov = group['nesterov'] 16 17 for p in group['params']: 18 if p.grad is None: 19 continue 20 d_p = p.grad.data 21 if weight_decay != 0: 22 d_p.add_(weight_decay, p.data) 23 if momentum != 0: 24 param_state = self.state[p] 25 if 'momentum_buffer' not in param_state: 26 buf = param_state['momentum_buffer'] = d_p.clone() 27 else: 28 buf = param_state['momentum_buffer'] 29 buf.mul_(momentum).add_(1 - dampening, d_p) 30 if nesterov: 31 d_p = d_p.add(momentum, buf) 32 else: 33 d_p = buf 34 35 p.data.add_(-group['lr'], d_p) 36 37 return loss
从上面的代码可以看到step这个函数使用的是参数空间(param_groups)中的grad,也就是当前参数空间对应的梯度,这也就解释了为什么optimizer使用之前需要zero清零,因为如果不清零,那么使用的这个grad就同上一个Mini-batch有关,这不是我们需要的结果。再回过头来看,我们知道optimizer更新参数空间需要基于反向梯度,因为,当调用optimizer.step()的时候应当在loss.backward()之后。这也是经常会碰到的,代码示意如下:
loss.backward()在前
optimizer.step()在后。顺序不可颠倒。
那么为什么optimizer.step()需要放在每个batch训练中,而不是epoch训练中,这是因为mini-batch训练模式是嘉定每一个训练集就只有mini-batch大小,因此实际上可以将每一次Mini-batch看做是一次训练,一次训练更新一次参数空间,因为optimizer.step()放在每个Mini-batch中。
scheduler.step()按照pytorch的定义是用来更新优化器学习率的,一般是按照epoch为单位进行更换,即多少个epoch后更换一次学习率,因而scheduler.step()放在epoch这个大循环中。
Pytorch SGD代码:https://github.com/pytorch/pytorch/blob/cd9b27231b51633e76e28b6a34002ab83b0660fc/torch/optim/sgd.py#L63
原文连接:https://blog.csdn.net/xiaoxifei/article/details/87797935