指数移动平均EDA
在深度学习中,经常会使用EMA(指数移动平均)这个方法对模型的参数做平均,以求提高测试指标并增加模型鲁棒。
EMA的定义
指数移动平均(Exponential Moving Average)也叫权重移动平均(Weighted Moving Average),是一种给予近期数据更高权重的平均方法。
假设我们有
- 普通的平均数:
- EMA:
,其中, 表示前 条的平均值 ( ), 是加权权重值 (一般设为0.9-0.999)。
Andrew Ng在Course 2 Improving Deep Neural Networks (opens new window)中讲到,EMA可以近似看成过去
普通的过去
$v_t =\frac{(n-1)\cdot v_{t-1}+\theta_t}{n} $
类比EMA,可以发现当
实际上,EMA计算时,过去
如果将这里的
$v_t = \alpha^n v_{t-n} + (1-\alpha)(\alpha^{n-1}\theta_{t-n+1}+ ... +\alpha^0\theta_t) $
其中,
在深度学习的优化中的EMA
上面讲的是广义的ema定义和计算方法,特别的,在深度学习的优化过程中,
EMA的偏差修正
实际使用中,如果令

理想的平均是绿色的,因为初始值为0,所以得到的是紫色的。
因此可以加一个偏差修正(bias correction):
$v_t = \frac{v_t}{1-\beta^t} $
显然,当t很大时,修正近似于1。
EMA为什么有效
网上大多数介绍EMA的博客,在介绍其为何有效的时候,只做了一些直觉上的解释,缺少严谨的推理,瓦砾在这补充一下,不喜欢看公式的读者可以跳过。
令第n时刻的模型权重(weights)为
令第n时刻EMA的影子权重为
代入上面
对比两式:
EMA对第i步的梯度下降的步长增加了权重系数
PyTorch实现
class EMA():
def __init__(self, model, decay):
self.model = model
self.decay = decay
self.shadow = {}
self.backup = {}
def register(self):
for name, param in self.model.named_parameters():
if param.requires_grad:
self.shadow[name] = param.data.clone()
def update(self):
for name, param in self.model.named_parameters():
if param.requires_grad:
assert name in self.shadow
new_average = (1.0 - self.decay) * param.data + self.decay * self.shadow[name]
self.shadow[name] = new_average.clone()
def apply_shadow(self):
for name, param in self.model.named_parameters():
if param.requires_grad:
assert name in self.shadow
self.backup[name] = param.data
param.data = self.shadow[name]
def restore(self):
for name, param in self.model.named_parameters():
if param.requires_grad:
assert name in self.backup
param.data = self.backup[name]
self.backup = {}
# 初始化
ema = EMA(model, 0.999)
ema.register()
# 训练过程中,更新完参数后,同步update shadow weights
def train():
optimizer.step()
ema.update()
# eval前,apply shadow weights;eval之后,恢复原来模型的参数
def evaluate():
ema.apply_shadow()
# evaluate
ema.restore()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47