@csyhhu: 感谢您的杰出工作。
我对您的MetaQuant技术很感兴趣,仔细研读了您的论文和代码。我的理解,您的核心思想是要解决量化网络在梯度反传时量化函数不可导问题。解决的办法是:反向传播时,通过meta-net获得meta-grad,再结合calibration,从而获得更加准确的梯度。在训练过程中,每一次迭代的过程主要分为4步 (以dorefa和MultiFC为例):
步骤1. 对于非首次迭代,使用上次迭代产生的layer.quantized_grad和layer.pre_quantized_weight来生成meta-grad
步骤2. 执行base-net前向
步骤3. 计算base-net损失,执行base-net的反向传播
步骤4. 对于非首次迭代,a) 使用步骤1生成的meta-grad,并结合layer.calibration,更新weight梯度, b)对weight梯度执行refinement, c)用refinement后的梯度更新base-net参数。
我仔细研究了您的代码实现,代码可以正常运行,Cifar10上获得了89%的准确率。但是,我也发现了几处不太匹配的地方,分析如下。如果是我理解不当,还请您不吝赐教,非常感谢!
第一次迭代过程:
因为是第1次迭代,所以会跳过上述步骤1和步骤4,只执行步骤2和步骤3。此时,base-net完成了一次前向和反向的过程。前向过程中,由于Function_STE.apply的使用,反向结束后,layer.weight.grad保存的将是STE的结果。并且,第一次迭代结束时,不立即更新参数,从而在第二次迭代开始时base-net参数仍然是第一次迭代时的参数。
第二次迭代过程(依序执行步骤1~4):
执行步骤1:计算meta-grad:layer.pre_quantized_weight和layer.quantized_grad中分别对应于第一次迭代前向过程中保存的量化前权重和反向过程中回传的梯度,根据此两个值,计算并返回meta-grad。
执行步骤2:在base-net的前向过程中,首先根据layer.weight计算layer.calibration(该layer.calibration将用于后续的步骤4), 接着计算layer.calibrated_grad, 然后更新layer.meta_weight. 问题是,layer.meta_weight的实际计算使用的是self.weight - lr * self.weight.grad, (尽管代码中使用的是 self.weight - lr * (self.calibrated_grads + (self.weight.grad.data - self.calibrated_grads.data)) ), 而此时self.weight.grad中存储的是STE的梯度,并不是meta-grad。因此,第二次迭代过程中使用的layer.meta_weight是基于STE的,而不是基于meta-grad的。这是第1个与论文不匹配的地方。
执行步骤3:正常执行,没有问题。
执行步骤4:使用步骤1得到的meta-grad和第一次迭代的layer.calibration来更新layer.weight.grad,并对梯度执行refinement,最后更新网络参数。
第2次迭代结束后,网络参数对应于第一次迭代的结果,并且是基于meta-grad的。这也就是您说的“延迟更新”。然而,这带来了第2个不匹配的地方:网络存储的权重参数是基于meta-grad的,而第2次迭代过程中实际使用的meta-weight是基于STE的。
第3次迭代过程:
执行步骤1:此时,layer.pre_quantized_weight和layer.quantized_grad中分别对应于第二次迭代前向过程中保存的量化前权重和反向过程中回传的梯度,根据此两个值,计算并返回meta-grad。
执行步骤2:首先根据layer.weight计算layer.calibration,但此时的layer.weight是已经基于meta-grad更新了的参数,并不对应于第二次迭代过程中实际使用的基于STE的meta-weight,因此,这里的校准操作并不严格匹配于第二次迭代的实际情况。基于同样的原因,第3次迭代过程中用来更新layer.meta_weight的layer.weight也不对应于第二次迭代过程中实际使用的layer.meta-weight.
以上列出了我的一些分析,请指教,非常感谢!
@csyhhu: 感谢您的杰出工作。
我对您的MetaQuant技术很感兴趣,仔细研读了您的论文和代码。我的理解,您的核心思想是要解决量化网络在梯度反传时量化函数不可导问题。解决的办法是:反向传播时,通过meta-net获得meta-grad,再结合calibration,从而获得更加准确的梯度。在训练过程中,每一次迭代的过程主要分为4步 (以dorefa和MultiFC为例):
步骤1. 对于非首次迭代,使用上次迭代产生的layer.quantized_grad和layer.pre_quantized_weight来生成meta-grad
步骤2. 执行base-net前向
步骤3. 计算base-net损失,执行base-net的反向传播
步骤4. 对于非首次迭代,a) 使用步骤1生成的meta-grad,并结合layer.calibration,更新weight梯度, b)对weight梯度执行refinement, c)用refinement后的梯度更新base-net参数。
我仔细研究了您的代码实现,代码可以正常运行,Cifar10上获得了89%的准确率。但是,我也发现了几处不太匹配的地方,分析如下。如果是我理解不当,还请您不吝赐教,非常感谢!
第一次迭代过程:
因为是第1次迭代,所以会跳过上述步骤1和步骤4,只执行步骤2和步骤3。此时,base-net完成了一次前向和反向的过程。前向过程中,由于Function_STE.apply的使用,反向结束后,layer.weight.grad保存的将是STE的结果。并且,第一次迭代结束时,不立即更新参数,从而在第二次迭代开始时base-net参数仍然是第一次迭代时的参数。
第二次迭代过程(依序执行步骤1~4):
执行步骤1:计算meta-grad:layer.pre_quantized_weight和layer.quantized_grad中分别对应于第一次迭代前向过程中保存的量化前权重和反向过程中回传的梯度,根据此两个值,计算并返回meta-grad。
执行步骤2:在base-net的前向过程中,首先根据layer.weight计算layer.calibration(该layer.calibration将用于后续的步骤4), 接着计算layer.calibrated_grad, 然后更新layer.meta_weight. 问题是,layer.meta_weight的实际计算使用的是self.weight - lr * self.weight.grad, (尽管代码中使用的是 self.weight - lr * (self.calibrated_grads + (self.weight.grad.data - self.calibrated_grads.data)) ), 而此时self.weight.grad中存储的是STE的梯度,并不是meta-grad。因此,第二次迭代过程中使用的layer.meta_weight是基于STE的,而不是基于meta-grad的。这是第1个与论文不匹配的地方。
执行步骤3:正常执行,没有问题。
执行步骤4:使用步骤1得到的meta-grad和第一次迭代的layer.calibration来更新layer.weight.grad,并对梯度执行refinement,最后更新网络参数。
第2次迭代结束后,网络参数对应于第一次迭代的结果,并且是基于meta-grad的。这也就是您说的“延迟更新”。然而,这带来了第2个不匹配的地方:网络存储的权重参数是基于meta-grad的,而第2次迭代过程中实际使用的meta-weight是基于STE的。
第3次迭代过程:
执行步骤1:此时,layer.pre_quantized_weight和layer.quantized_grad中分别对应于第二次迭代前向过程中保存的量化前权重和反向过程中回传的梯度,根据此两个值,计算并返回meta-grad。
执行步骤2:首先根据layer.weight计算layer.calibration,但此时的layer.weight是已经基于meta-grad更新了的参数,并不对应于第二次迭代过程中实际使用的基于STE的meta-weight,因此,这里的校准操作并不严格匹配于第二次迭代的实际情况。基于同样的原因,第3次迭代过程中用来更新layer.meta_weight的layer.weight也不对应于第二次迭代过程中实际使用的layer.meta-weight.
以上列出了我的一些分析,请指教,非常感谢!