mxnet参数优化
lr-schedure 包含在optimizer之内
在init_optimizer的时候,get_updater
def get_updater(optimizer):
states = dict()
def updater(index, grad, weight):
if index not in states:
states[index] = optimizer.create_state(index, weight)
optimizer.update(index, weight, grad, states[index])
return updater
训练的时候:
for nbatch, data_batch in enumerate(train_dataiter):
mod.forward(data_batch)
mod.update_metric(metric, data_batch.label) #计算误差
mod.backward() #计算当前批次的梯度
mod.update() #更新参数,因为当前梯度只是少量样本的梯度,所以不能完全按照梯度更新。需要设置学习率等
- forward
- backward
- update
- updater(index,grad, weight) 里面调用optimizer的update函数
每个optimizer需要实现的是__init__, create_state, update。 以SGD为例子:
class SGD(Optimizer):
def __init__(self, momentum=0.0, **kwargs):
super(SGD, self).__init__(**kwargs)
self.momentum = momentum
def create_state(self, index, weight):
if self.momentum == 0.0:
return None
else:
return zeros(weight.shape, weight.context, dtype=weight.dtype)
def update(self, index, weight, grad, state):
assert(isinstance(weight, NDArray))
assert(isinstance(grad, NDArray))
lr = self._get_lr(index)
wd = self._get_wd(index)
self._update_count(index)
grad = grad * self.rescale_grad
if self.clip_gradient is not None:
grad = clip(grad, -self.clip_gradient, self.clip_gradient)
if state:
mom = state #注意,这里并没有复制。更改mom会使得state也产生,变化。达到记录上一次更新的目的。
mom[:] *= self.momentum
mom[:] += -lr * (grad + wd * weight)
weight[:] += mom
else:
assert self.momentum == 0.0
weight[:] += -lr * (grad + wd * weight)
finetune的实现方式
通过args_grad来控制是否要进行梯度计算。不论在module init中的fixed_param_names还是通过model中的bind。其实最终都是转化成symbol 的bind。而symbol的bind可以指定args_grad用于控制是否需要梯度。
sgd,momentum理论
不需要把所有数据都加载到内存一起计算。而且在数据有冗余的情况下能够更快的收敛。
SGD:
\[\theta=\theta-\eta\nabla_{\theta}J(\theta;x^{(i)};y^{(i)})\]SGD的缺点是容易收到噪声的影响。更新不稳定。引入Momentum模拟物体的动量,在一定程度上保持当前更新方向,并且根据当前的batch进行调整,从而确定最终的更新方向。momentum的取值在0-1之间。在训练刚开始时,梯度的变化较大,一般选择较小的动量0.3或者0.5。而到了训练后期,避免的当前批量的噪声数据造成过大的影响,一般选择较大的动量,例如0.9, 与此同时,也会不断的减少学习率,避免震荡。
Momentum: 动量
\[v_t={\gamma}v_{t-1}-\eta\nabla_{\theta}J(\theta)\] \[\theta=\theta-v_{t}\]