لقد كنت أبحث في تنفيذ SGD + Momentum في PyTorch ولاحظت شيئًا مختلفًا قليلاً عن كيفية وصف الحزم (والأوراق) الأخرى لها. في الوقت الحالي ، دعونا نركز فقط على الزخم (الكلاسيكي) وليس نسخة نيستيروف.
في وقت كتابة هذا التقرير ، كان التنفيذ كما يلي:
""
إذا كان الزخم! = 0:
param_state = self.state [p]
إذا لم يكن "boomum_buffer" في param_state:
buf = param_state ['momentum_buffer'] = d_p.clone ()
آخر:
buf = param_state ['momentum_buffer']
buf.mul_ (الزخم). add_ (1 - الترطيب ، d_p)
إذا نيستيروف:
d_p = d_p.add (الزخم ، buf)
آخر:
d_p = بوف
p.data.add_(-group['lr'], d_p)
Mathematically, if we denote the momentum buffer by `v` and assume that `dampening=0`, at every iteration, the buffer is updated as `v = m*v + g` and the step is `∆x = lr * v`. Notice that the learning rate `lr` hits the momentum term `v` as well as the gradient. To me, this is different from what classical momentum is, and also differs from how other packages implement SGD+M.
Let us contrast this with the Sutskever et. al. paper and other commonly used pacakges such as Lasagne, Keras, Neon, etc.
## [Sutskever et. al.](http://www.jmlr.org/proceedings/papers/v28/sutskever13.pdf)
The snippet of the relevant section is pasted below.
![Sutskever et. al.](http://i.imgur.com/QJelodE.png)
Retaining the syntax from above, the algorithm updates `v` as `v = m*v - lr * g` with the step `∆x = v`. So, the learning rate `lr` only hits the gradient. It does not (explicitly) influence the effect of the momentum term which is in contrast with PyTorch's implementation.
# [Lasagne](https://github.com/Lasagne/Lasagne/blob/master/lasagne/updates.py#L217)
Lasagne employs the same rule as suggested in Sutskever for momentum.
for param in params:
value = param.get_value(borrow=True)
velocity = theano.shared(np.zeros(value.shape, dtype=value.dtype),
broadcastable=param.broadcastable)
x = momentum * velocity + updates[param]
updates[velocity] = x - param
# [Keras](https://github.com/fchollet/keras/blob/master/keras/optimizers.py#L141)
Same for Keras:
for p, g, m in zip(params, grads, moments):
v = self.momentum * m - lr * g # velocity
self.updates.append(K.update(m, v))
if self.nesterov:
new_p = p + self.momentum * v - lr * g
else:
new_p = p + v
# [Neon](https://github.com/NervanaSystems/neon/blob/master/neon/optimizers/optimizer.py#L520)
and Neon.
velocity[:] = self.momentum_coef * velocity - lrate * grad
# Nesterov accelerated gradient (NAG) is implemented the same
# as in torch's "sgd.lua". It's a reformulation of Sutskever's
# NAG equation found in "On the importance of initialization
# and momentum in deep learning".
if self.nesterov:
param[:] = param + self.momentum_coef * velocity -\
lrate * grad
else:
param[:] = param + velocity
""
هل التفاوت صحيح أم أني أفتقد شيئًا مهمًا؟
الفرق بين التطبيقين ليس ضئيلًا وخاصةً عندما يتم تقليل lr
على طول الطريق. إذا كانت المطالبة الخاصة بي صحيحة ، فربما يمكننا تحديث المرجع (لست متأكدًا من ذلك) أو تضمين الإصدار أعلاه في رمز SGD (يمكنني تناول هذا الأمر إذا لزم الأمر)؟
بالنسبة لمعدل التعلم الثابت ، فإن الصيغتين متكافئتان. يتم اختيار صيغة Torch لأن حجم الخطوة يتناسب طرديًا مع معدل التعلم. هذا يعني أنك إذا قمت بتقليل معدل التعلم ، فإن حجم الخطوة يتناقص على الفور ، وليس بعد عدد من التكرارات ، وهو ما تريده عمومًا.
أنا موافق. كان قلقي الوحيد هو أنه ، نظرًا لأن المرجع الخاص بهذه الطريقة هو ورقة Sutskever ولا توجد وثائق لشرح الاختلاف ، فقد يكون التنفيذ الحالي بمثابة "مسدود" بالنسبة للأشخاص الذين ينتقلون إلى PyTorch من أطر أخرى.
keskarnitish @ إذا أرسلت العلاقات العامة بإضافة ملاحظة إلى المستندات ، يسعدني الدمج.
التعليق الأكثر فائدة
بالنسبة لمعدل التعلم الثابت ، فإن الصيغتين متكافئتان. يتم اختيار صيغة Torch لأن حجم الخطوة يتناسب طرديًا مع معدل التعلم. هذا يعني أنك إذا قمت بتقليل معدل التعلم ، فإن حجم الخطوة يتناقص على الفور ، وليس بعد عدد من التكرارات ، وهو ما تريده عمومًا.