> For the complete documentation index, see [llms.txt](https://hecongqing.gitbook.io/tfnotes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hecongqing.gitbook.io/tfnotes/di-san-zhang-tensorflow-ji-chu-pian-ii/3.3-you-hua-qi-ji-zi-ding-yi-you-hua-qi.md).

# 3.3 优化器及自定义优化器

## Adam算法

[Adam算法](https://arxiv.org/pdf/1412.6980.pdf)在RMSProp算法基础上对小批量随机梯度也做了指数加权移动平均。下面我们来介绍这个算法。

## 1. 算法

Adam算法使用了动量变量$$\boldsymbol{v}\_t$$和RMSProp算法中小批量随机梯度按元素平方的指数加权移动平均变量$$\boldsymbol{s}\_t$$，并在时间步0将它们中每个元素初始化为0。给定超参数$$0 \leq \beta\_1 < 1$$（算法作者建议设为0.9），时间步$$t$$的动量变量$$\boldsymbol{v}\_t$$即小批量随机梯度$$\boldsymbol{g}\_t$$的指数加权移动平均：

$$
\boldsymbol{v}*t \leftarrow \beta\_1 \boldsymbol{v}*{t-1} + (1 - \beta\_1) \boldsymbol{g}\_t.
$$

和RMSProp算法中一样，给定超参数$$0 \leq \beta\_2 < 1$（算法作者建议设为0.999）， 将小批量随机梯度按元素平方后的项$\boldsymbol{g}\_t \odot \boldsymbol{g}\_t$做指数加权移动平均得到$\boldsymbol{s}\_t$：

$$
\boldsymbol{s}*t \leftarrow \beta\_2 \boldsymbol{s}*{t-1} + (1 - \beta\_2) \boldsymbol{g}\_t \odot \boldsymbol{g}\_t.
$$

由于我们将$$\boldsymbol{v}\_0$$和$$\boldsymbol{s}\_0$$中的元素都初始化为0， 在时间步$$t$$我们得到$$\boldsymbol{v}*t = (1-\beta\_1) \sum*{i=1}^t \beta\_1^{t-i} \boldsymbol{g}*i$$。将过去各时间步小批量随机梯度的权值相加，得到 $$(1-\beta\_1) \sum*{i=1}^t \beta\_1^{t-i} = 1 - \beta\_1^t$$。需要注意的是，当$$t$$较小时，过去各时间步小批量随机梯度权值之和会较小。例如，当$$\beta\_1 = 0.9$$时，$$\boldsymbol{v}\_1 = 0.1\boldsymbol{g}\_1$$。为了消除这样的影响，对于任意时间步$$t$$，我们可以将$$\boldsymbol{v}\_t$$再除以$$1 - \beta\_1^t$$，从而使过去各时间步小批量随机梯度权值之和为1。这也叫作偏差修正。在Adam算法中，我们对变量$$\boldsymbol{v}\_t$$和$$\boldsymbol{s}\_t$$均作偏差修正：

$$
\hat{\boldsymbol{v}}\_t \leftarrow \frac{\boldsymbol{v}\_t}{1 - \beta\_1^t},
$$

$$
\hat{\boldsymbol{s}}\_t \leftarrow \frac{\boldsymbol{s}\_t}{1 - \beta\_2^t}.
$$

接下来，Adam算法使用以上偏差修正后的变量$$\hat{\boldsymbol{v}}\_t$$和$$\hat{\boldsymbol{s}}\_t$$，将模型参数中每个元素的学习率通过按元素运算重新调整：

$$
\boldsymbol{g}\_t' \leftarrow \frac{\eta \hat{\boldsymbol{v}}\_t}{\sqrt{\hat{\boldsymbol{s}}\_t} + \epsilon},
$$

其中$$\eta$$是学习率，$$\epsilon$$是为了维持数值稳定性而添加的常数，如$$10^{-8}$$。和AdaGrad算法、RMSProp算法以及AdaDelta算法一样，目标函数自变量中每个元素都分别拥有自己的学习率。最后，使用$$\boldsymbol{g}\_t'$$迭代自变量：

$$
\boldsymbol{x}*t \leftarrow \boldsymbol{x}*{t-1} - \boldsymbol{g}\_t'.
$$

原始论文中实现：

![](https://742994238-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M8iCebT5iJWyvavkCMO%2Fsync%2F95112ea33ba4b976e422b10d7b9a23f1ac6332f8.png?generation=1600238214043411\&alt=media)

## 2. 简单实现

基于Python实现Adam优化器：

```python
class Adam:
    def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.iter = 0
        self.m = None
        self.v = None

    def update(self, params, grads):
        if self.m is None:
            self.m, self.v = {}, {}
            for key, val in params.items():
                self.m[key] = np.zeros_like(val)
                self.v[key] = np.zeros_like(val)

        self.iter += 1
        lr_t = self.lr * np.sqrt(1.0 - self.beta2**self.iter) / (1.0 - self.beta1**self.iter)

        for key in params.keys():
            self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
            self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key])

            params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)
```

TODO：

<https://github.com/OverLordGoldDragon/keras-adamw/blob/master/keras_adamw/optimizers_v2.py>

实验
