指数移动平均(EMA)

1. 定义

EMA(Exponential Moving Average,指数移动平均)是一种加权平均方法,它给予最近的数据点更高的权重。

在深度学习训练中,我们维护一个“影子参数”,在每次网络参数更新之后,使用EMA对影子参数更新(注:影子参数不会参与训练,EMA并不会对训练中的模型参数产生影响)。通过这样的操作,我们最终得到的“影子参数”是所有时刻参数的加权平均值,且越往后的影子参数所占权重越大,这样可以增加模型的鲁棒性和稳定性。
v t = β v t − 1 + ( 1 − β ) θ t (1) v_{t} = \beta v_{t-1} + (1 - \beta)\theta_{t}\tag{1} vt=βvt1+(1β)θt(1)
其中, v t v_{t} vt 是前 t t t 时刻的“影子参数”的加权平均值, v t − 1 v_{t-1} vt1 是前 t − 1 t-1 t1 的“影子参数”加权平均值, θ t \theta_{t} θt t t t 时刻的网络参数, β \beta β 是加权权重值, 一般取 [ 0.9 , 0.9999 ] [0.9,0.9999] [0.9,0.9999]

另外,根据吴恩达老师的课程改善深层神经网络:超参数调试、正则化以及优化所讲,可以近似认为当前影子参数是最近的 n = 1 / ( 1 − β ) n = 1/(1-\beta) n=1/(1β) 个影子参数的均值。这是因为可以计算出对于 v t v_{t} vt n = 1 / ( 1 − β ) n = 1/(1-\beta) n=1/(1β) 时刻之前的平均值 v t − n v_{t-n} vtn 所占权重被衰减到了 1 / e 1/e 1/e 。如下:
v t = β v t − 1 + ( 1 − β ) θ t = β n v t − n + ( 1 − β ) ∑ i = 0 n − 1 β i θ t − i (2) \begin{align*} v_{t} &=\beta v_{t-1} + (1 - \beta)\theta_{t} \\ &=\beta^{n}v_{t-n} + (1-\beta)\sum^{n-1}_{i=0}\beta^{i}\theta_{t-i}\\ \end{align*} \tag{2} vt=βvt1+(1β)θt=βnvtn+(1β)i=0n1βiθti(2)
其中,当 β ∈ [ 0.9 , 0.9999 ] \beta\in[0.9, 0.9999] β[0.9,0.9999]时, β n = β 1 1 − β ≈ 1 / e \beta^{n}=\beta^{\frac{1}{1-\beta}}\approx1/e βn=β1β11/e。这可以说明此时 n n n 时刻之前的均值在当前参数所占权重已经很少。(不过个人感觉取 n = 10 / ( 1 − β ) n = 10 / (1- \beta) n=10/(1β) 是不是更能说明问题?)

2. Pytroch实现以及使用

1. 实现

class EMA:  
  
    def __init__(self, model, decay=0.999, correction=False):  
  
        self.model = model  
        self.decay = decay  
        self.shadow = {} # 存储影子参数  
        self.backup = {} # 存储网络参数,用于restore  
  
        # 初始化  
        for name, param in model.named_parameters():  
            if param.requires_grad:  
                self.shadow[name] = param.data.clone()  
  
        self.correction = correction  # 是否进行偏差修正
        if self.correction:  
            self.step = 0 # 记录步数  
  
    # 每轮网络参数更新之后更新影子参数  
    def update(self):  
  
        if self.correction:  
            self.step += 1  
        for name, param in self.model.named_parameters():  
            if param.requires_grad:  
                assert name in self.shadow  
                self.shadow[name] = self.decay * self.shadow[name] + (1 - self.decay) * param.data.clone()  
  
    # 推理之前,将影子参数应用于网络  
    def apply_shadow(self):  
  
        for name, param in self.model.named_parameters():  
            if param.requires_grad:  
                assert name in self.shadow  
                correction_factor = 1  
                if self.correction:  
                    correction_factor = 1 - self.decay**self.step  
                self.backup[name] = param.data.clone()  
                param.data.copy_(self.shadow[name] / correction_factor)  
  
    # 推理之后,将网络参数恢复  
    def restore(self):  
  
        for name, param in self.model.named_parameters():  
            if param.requires_grad:  
                assert name in self.backup  
                param.data.copy_(self.backup[name])  
        self.backup = {}

2. 使用

  1. 模型训练开始之前进行初始化
ema = EMA(model, decay=0.999)
  1. 每轮训练结束进行更新
def train(...):
	...
	for epoch in range(num_epochs):
		for batch in train_loader:
			...
			optimizer.step()
			ema.update()
  1. 评估前进行应用,评估完之后恢复参数
def evaluate(...):
	...
	ema.apply_shadow()
	...
	ema.restore()

参考

  1. 【炼丹技巧】指数移动平均(EMA)的原理及PyTorch实现
  2. Exponential Moving Average of Weights in Deep Learning: Dynamics and Benefits
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

下雨了啊!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值