优化算法-附各优化算法波士顿房价demo

目录

1 Mini-batch梯度下降

2 指数加权平均

3 动量梯度下降Momentum

4 AdaGrad算法

5 RMSprop

6 AdaDelta算法

7 Adam优化算法

7 学习率衰减

8 FTRL

9 波士顿房价预测demo


1 Mini-batch梯度下降

批量梯度下降的学习率可以大一点,因为梯度带1/m(所有样本的梯度的均值),随机梯度下降的学习率可以小一点,因为相当于一直累加梯度。随机梯度是对梯度的无偏估计。

\textbf{g}_{t} \leftarrow \bigtriangledown \textit{f}_{\ss_{t}}(\textit{\textbf{x}}_{t-1}) = \frac{1}{|\ss|}\sum_{i \in \ss_{t}} \bigtriangledown \textit{f}_{i}(\textit{\textbf{x}}_{t-1})

重复采样得到的小批量随机梯度也是对梯度的无偏估计。随机采样得到的梯度的方差在迭代过程中无法减小,因此在实际中,(小批量)随机梯度下降的学习率可以在迭代过程中自我衰减。如此一来,学习率和(小批量)随机梯度乘积的方差会减小。而梯度下降在迭代过程中一直使用目标函数的真实梯度,无须自我衰减学习率。

批量大小不影响随机梯度的期望,但会影响随机梯度的方差。批量大小越大,随机梯度的方差越小,引入的噪声也越小,训练也越稳定,因此可以设置较大的学习率。而批量大小较小时,需要设置较小的学习率,否则模型会不收敛。学习率通常要随着批量大小的增大而相应地增大。

2 指数加权平均

v_t = \beta v_{t-1} + (1-\beta)\theta_t

     

\beta较大时,相当于平均较多天数的数据,故会平坦一点,当然了由于平均了前些天的数据,只考虑了1-\beta的当日数据,故会有延迟。相当于平均1/(1-\beta)天的数据。

从左侧图可以看出,几乎考虑了之前所有天的数据,但是距离今天越远的数据所占权重越小,例如当\beta=0.9时,\beta^{10} \approx 1/e,10之前的数据占比很小,故相当于考虑了最近10天数据的加权平均。

偏差的修正,我们发现刚开始的几天,指数加权平均的值比较小,例如第一天,只有(1-\beta)\theta_1 \ll \theta_1,前几天一直有这种现象,为了使前几天更加准确,我们可以进行修正。当然不修正也行,只是前面的数据不太准确,熬过了前几天,后面的数据较不错了。

3 动量梯度下降Momentum

更新梯度的时候借助指数加权平均的思想,例如左图采用的是梯度下降法,可以看出y的梯度方向交替,采用梯度下降,若学习率过大可能导致y方向移动步伐过大,整个损失函数无法收敛,若学习率过小,更新过慢,若采用动量梯度下降更新方向更加一致,正负向相抵消。

使自变量更新的方向更加一致,降低发散的可能性,使得用梯度下降更新的较快的权重更新时较为平滑。

但是各个方向的学习率是一致的,还是会存在某些方向下降的过快,其他方向下降的过慢的现象。

指数加权平均

y_t = \gamma y_{t-1}+(1-\gamma)x_t

在实际中,常将y_t看作是最近1/(1-\gamma)个时间步的x_t值的加权平均,离当前时间步t越近的x_t值获得的权重越大(越接近1)。

 动量梯度下降法

\begin{aligned} &\boldsymbol{v}_t \leftarrow \gamma \boldsymbol{v}_{t-1 }+ \eta_t\boldsymbol{g}_t \\ &\boldsymbol{x}_t \leftarrow \boldsymbol{x}_{t-1 } - \boldsymbol{v}_t \end{aligned}

使自变量更新的方向更加一致,降低发散的可能性。但各个方向学习率一致,还是会存在有些方向下降的过快,其他方向下降的过慢。

\boldsymbol{v}_{t} \leftarrow \gamma \boldsymbol{v}_{t-1 }+ (1-\gamma)(\frac{\eta_t}{1-\gamma}\boldsymbol{g}_{t})

由指数加权平均理解动量梯度下降法,由指数加权移动平均的形式可得,速度变量\boldsymbol{v}_t实际上对序列\{\eta_{t-i}\boldsymbol{g}_{t-i}/(1-\gamma):i=0,\cdots,1/(1-\gamma)-1\}做了指数加权移动平均。相比小批量梯度下降,动量法在每个时间步的自变量更新量近似于将前者对应的最近1/(1-\gamma)个时间步的更新量做了指数加权移动平均后再除以(1-\gamma)\gamma变大只学习率可以相应变小。

4 AdaGrad算法

\begin{aligned} &\boldsymbol{s}_t \leftarrow \boldsymbol{s}_{t-1} + \boldsymbol{g}_t \odot \boldsymbol{g}_t \\ & \boldsymbol{x}_t = \boldsymbol{x}_{t-1} - \frac{\eta}{\sqrt{\boldsymbol{s}_t + \epsilon }} \odot \boldsymbol{g}_t \end{aligned}

AdaGrad算法会使用一个小批量的随机梯度\boldsymbol{g}_t按元素平方的累加变量\boldsymbol{s}_t,这里开方、除法和乘法都是按元素运算的,这些按元素运算使得目标函数自变量中每个元素都分别拥有自己的学习率。若目标函数有关自变量中某个元素的偏导一直都较大,那么该元素的学习率降下降较快;反之,若目标函数有关自变量中某个元素的偏导数一直较小,则该元素的学习率下降的较慢。

由于\boldsymbol{s}_t一直在累加按元素平方的梯度,自变量中每个元素的学习率在迭代过程中一直在降低(或不变)。所以,当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad算法在迭代后期由于学习率过小,可能很难找到一个有用的解。

5 RMSprop

\begin{aligned} &\boldsymbol{s}_t \leftarrow \gamma \boldsymbol{s}_{t-1} + (1-\gamma)\boldsymbol{g}_t \odot \boldsymbol{g}_t \\ & \boldsymbol{x}_t = \boldsymbol{x}_{t-1} - \frac{\eta}{\sqrt{\boldsymbol{s}_t + \epsilon }} \odot \boldsymbol{g}_t \end{aligned}

不同于AdaGrad算法里状态变量\boldsymbol{s}_t是截止时间步t所有小批量随机梯度\boldsymbol{g}_t按元素平方的累加,RMSprop算法借鉴了动量梯度下降和AdaGrad算法,将这些梯度按元素平方做指数加权移动平均,可以看作是对最近1/(1-\gamma)个时间步的小批量随机梯度平方项的加权平均。如此一来,自变量每个元素的学习率在迭代过程中就不再一直降低或不变。

例如w_1的梯度比较大,则s_{​{dw}_1}也比较大,在做更新的时候由于除上了s_{​{dw}_1},则其更新幅度也不是很大;w_2的梯度比较小,则s_{​{dw}_2}也比较小,在做更新的时候由于除上了s_{​{dw}_2},则其更新幅度也不会小太多。但是由于s_{​{dw}_2}一直是整的,且还在不停的累加,故后面的更新幅值会比较小,更新很慢,若刚开始不在最优值附近,可能很难达到最优值。

6 AdaDelta算法

针对AdaGrad算法在迭代后期可能较难找到有用的解做了改进,且其没有超参数。与RMSprop算法一样,也使用了小批量随机梯度\boldsymbol{g}_t按元素平方的指数加权移动平均变量\boldsymbol{s}_t。与RMSprop不同的是,AdaDelta算法还维护了一个额外的状态变量\triangle \boldsymbol{x}_t

\begin{aligned} &\boldsymbol{s}_t \leftarrow \rho \boldsymbol{s}_{t-1} + (1-\rho)\boldsymbol{g}_t \odot \boldsymbol{g}_t \\ &\boldsymbol{g}_t^{'} \leftarrow \sqrt{\frac{\triangle \boldsymbol{x }_{t-1}+\epsilon}{\boldsymbol{s}_t+\epsilon}} \odot \boldsymbol{g}_t \\ & \boldsymbol{x}_t = \boldsymbol{x}_{t-1} - \boldsymbol{g}_t^{'} \\ &\triangle \boldsymbol{x}_t \leftarrow \rho \triangle \boldsymbol{x }_{t-1} + (1-\rho)\boldsymbol{g}_t^{'} \odot \boldsymbol{g}_t^{'} \end{aligned}

7 Adam优化算法

其实就是结合了动量梯度下降法和RMSprop算法。v_t= (1-\beta_1)\sum_{i=1}^t\beta_1^{(t-i)}g_i

\begin{aligned} &\boldsymbol{v}_t \leftarrow \beta_1 \boldsymbol{v}_{t-1} + (1-\beta_1)\boldsymbol{g}_t \\ &\boldsymbol{s}_t \leftarrow \beta_2 \boldsymbol{s}_{t-1} + (1-\beta_2)\boldsymbol{g}_t \odot \boldsymbol{g}_t \\ & \boldsymbol{\hat{v}}_t \leftarrow \frac{\boldsymbol{v}_t}{1-\beta_1^t} \\ & \boldsymbol{\hat{s}}_t \leftarrow \frac{\boldsymbol{s}_t}{1-\beta_2^t} \\ &\boldsymbol{g}_t^{'} \leftarrow \frac{\eta \boldsymbol{\hat{v}}_t}{\sqrt{\boldsymbol{\hat{s}}_t}+\epsilon } \\ &\boldsymbol{x}_t \leftarrow \boldsymbol{x}_{t-1}- \boldsymbol{g}_t^{'}\end{aligned}

超参:

\eta:需调参

\beta_1:推荐值(0.9)

\beta_2:推荐值(0.999)

\epsilon:推荐值(10^{-8}),主要是为了数值稳定性,避免分母为0

7 学习率衰减

8 FTRL

发现了一篇讲解的很好,mark一下,在线学习 FTRL 方法的原理与实现 - 知乎

9 波士顿房价预测demo

from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
import numpy as np


# 1  数据下载
boston = load_boston()

# 2  划分训练集测试集
X_train, X_test, y_train, y_test = train_test_split(
    boston.data, boston.target, test_size=0.2)

# 3  归一化
mean, std = np.mean(X_train, axis=0), np.std(X_train, axis=0)
X_train = (X_train - mean) / std
X_test = (X_test - mean) / std

# 4  增广矩阵方便统一进行矩阵运算
X_b = np.hstack([np.ones(shape=(X_train.shape[0], 1)), X_train])
X_test_b = np.hstack([np.ones(shape=(X_test.shape[0], 1)), X_test])

# 5  损失函数
def J(X, y, w, reg=None):
    """
    X_train: [N, feas]
    y_train: (N, )
    w: [feas, ]
    """
    samples = X.shape[0]
    predict = X.dot(w)
    loss = np.sum(np.square(y - predict)) * 0.5 / samples
    if reg:
        loss += 0.5 * reg * np.sum(np.square(w)) / samples
    return loss

# 6  梯度计算
def gradient(X, y, w, reg):
    predict = X.dot(w)
    g = -(y - predict).dot(X) / X.shape[0]
    if reg:
        g += reg * w / X.shape[0]
    return g

# 7  训练
def train(X, y, n_iters, Opt, reg):
    for epoch in range(n_iters):
        if epoch % 10 == 0:
            print("epoch: {}, J: {}".format(epoch, round(J(X, y, Opt.w, None), 4)))
            print([round(item, 3) for item in list(Opt.w)])
        Opt.w_update(X, y, reg)

# 8  最优化算法
# 8.1  梯度下降
class GD:
    def __init__(self, feas, eta):
        self.w = np.zeros(shape=(feas))
        self.eta = eta

    def w_update(self, X, y, reg):
        g = gradient(X, y, self.w, reg)
        self.w -= self.eta * g

gdOpt = GD(X_b.shape[1], 0.1)
train(X_b, y_train, 100, gdOpt, 1)
print(J(X_test_b, y_test, gdOpt.w))

# 8.2  动量法
class Momentum:
    def __init__(self, feas, gamma, eta):
        self.w = np.zeros(shape=(feas))
        self.v = np.zeros(shape=(feas))
        self.gamma = gamma
        self.eta = eta

    def w_update(self, X, y, reg):
        g = gradient(X, y, self.w, reg)
        self.v = self.gamma * self.v + self.eta * g
        self.w -= self.v

MomentumOpt = Momentum(X_b.shape[1], 0.9, 0.2)
train(X_b, y_train, 100, MomentumOpt, 1)
J(X_test_b, y_test, MomentumOpt.w)

# 8.3  AdaGrad
class AdaGrad:
    def __init__(self, feas, eta):
        self.w = np.zeros(shape=(feas))
        self.s = np.zeros(shape=(feas))
        self.eta = eta
        self.epsilon = 1e-8

    def w_update(self, X, y, reg):
        g = gradient(X, y, self.w, reg)
        self.s += g * g
        self.w -= self.eta * (1. / np.sqrt(self.s + self.epsilon)) * g

AdaGradOpt = AdaGrad(X_b.shape[1], 2.0)
train(X_b, y_train, 1000, AdaGradOpt, 1)
J(X_test_b, y_test, AdaGradOpt.w)

# 8.4  RMSProp
class RMSProp:
    def __init__(self, feas, gamma, eta):
        self.w = np.zeros(shape=(feas))
        self.s = np.zeros(shape=(feas))
        self.gamma = gamma
        self.eta = eta
        self.epsilon = 1e-8

    def w_update(self, X, y, reg):
        g = gradient(X, y, self.w, reg)
        self.s = self.gamma * self.s + (1 - self.gamma) * g * g
        self.w -= self.eta * (1. / np.sqrt(self.s + self.epsilon)) * g

RMSPropOpt = RMSProp(X_b.shape[1], 0.95, 0.5)
train(X_b, y_train, 100, RMSPropOpt, 1)
J(X_test_b, y_test, RMSPropOpt.w)

# 8.5  Adadelta
class Adadelta:
    def __init__(self, feas, gamma):
        self.w = np.zeros(shape=(feas))
        self.s = np.zeros(shape=(feas))
        # 这边一直不是很理解,按照paper置0,下降的比较慢,所以我这边给了一个非0的初值
        # 后来又试了一下,delta初值为0,但是提高gamma的值也可以,大概0.99999速度还行
        self.delta = np.zeros(shape=(feas)) + 0.1
        self.gamma = gamma
        self.epsilon = 1e-5

    def w_update(self, X, y, reg):
        g = gradient(X, y, self.w, reg)
        self.s = self.gamma * self.s + (1 - self.gamma) * g * g
        g_trans = np.sqrt((self.delta + self.epsilon) / (self.s + self.epsilon)) * g
        self.w -= g_trans
        self.delta = self.gamma * self.delta + (1 - self.gamma) * g_trans * g_trans

AdadeltaOpt = Adadelta(X_b.shape[1], 0.1)
train(X_b, y_train, 100, AdadeltaOpt, 1)
J(X_test_b, y_test, AdadeltaOpt.w)

# 8.6  Adam
class Adam:
    def __init__(self, feas, gamma1, gamma2, eta):
        self.w = np.zeros(shape=(feas))
        self.s = np.zeros(shape=(feas))
        self.v = np.zeros(shape=(feas))
        self.gamma1 = gamma1
        self.gamma2 = gamma2
        self.eta = eta
        self.epsilon = 1e-8
        self.t = 1

    def w_update(self, X, y, reg):
        g = gradient(X, y, self.w, reg)
        self.v = self.gamma1 * self.v + (1 - self.gamma1) * g
        self.s = self.gamma2 * self.s + (1 - self.gamma2) * g * g

        v_trans = self.v / (1 - self.gamma1 ** self.t)
        s_trans = self.s / (1 - self.gamma2 ** self.t)
        self.w -= self.eta * v_trans / (np.sqrt(s_trans) + self.epsilon)
        self.t += 1

AdamOpt = Adam(X_b.shape[1], 0.9, 0.999, 0.2)
train(X_b, y_train, 200, AdamOpt, 1)
J(X_test_b, y_test, AdamOpt.w)

# 9  Online

def gradient_online(X, y, w):
    predict = X.dot(w)
    g = -(y - predict).dot(X) / X.shape[0]
    return g

def train_online(n_iters, Opt):
    feas, samples = X_b.shape[1], X_b.shape[0]
    for epoch in range(n_iters):
        if epoch % 10 == 0:
            print("epoch: {}, J: {}".format(epoch, round(J(X_b, y_train, Opt.w, None), 4)))
            print([round(item, 3) for item in list(Opt.w)])
        for i, (x, y) in enumerate(zip(X_b, y_train)):
            Opt.w_update(x.reshape((-1,feas)), np.array([y]))

# 9.1  FOBOS

class FOBOS(object):
    def __init__(self, feas, lam):
        self.w = np.zeros(shape=(feas))
        self.lam = lam
        self.t = 1
        
    def w_update(self, x, y):
        eta_t = 1. / np.sqrt(self.t)
        eta_t_plus = 1. / np.sqrt(self.t + 0.5)
        g = gradient_online(x, y, self.w)
        w = self.w - eta_t * g
        self.w = np.sign(w) * np.where(np.abs(w) < eta_t_plus * self.lam, 
                 0., np.abs(w) - eta_t * self.lam )
        self.t += 1

FOBOSOpt = FOBOS(X_b.shape[1], 1.)
train_online(100, FOBOSOpt)

# 9.2  RDA

class RDA(object):
    def __init__(self, feas, lam, gamma):
        self.w = np.zeros(shape=(feas))
        self.g = np.zeros(shape=(feas))
        self.lam = lam
        self.gamma = gamma
        self.t = 1
        
    def w_update(self, x, y):
        g = gradient_online(x, y, self.w)
        self.g = 1. * (self.t - 1) / self.t * self.g + 1. / self.t * g

        self.w = np.where(np.abs(self.g) < self.lam, 0.,
                          -1.0 * np.sqrt(self.t) / self.gamma * (self.g - self.lam * np.sign(self.g)))
        self.t += 1

RDAOpt = RDA(X_b.shape[1], 0.1, 1.)
train_online(100, RDAOpt)

# 9.3  FTRL
class FTRL(object):
    def __init__(self, feas, alpha, beta, lam1, lam2):
        self.w = np.zeros(shape=(feas))
        self.q = np.zeros(shape=(feas))
        self.z = np.zeros(shape=(feas))
        self.lam1 = lam1
        self.lam2 = lam2
        self.alpha = alpha
        self.beta = beta
        
    def w_update(self, x, y):
        self.w = np.where(np.abs(self.z) < self.lam1, 0.,
                          -(self.z - self.lam1 *np.sign(self.z)) / 
                          (self.lam2 + (self.beta + np.sqrt(self.q)/ self.alpha))
                         )

        g = gradient_online(x, y, self.w)
        sigma = (np.sqrt(self.q + g * g) - np.sqrt(self.q)) / self.alpha
        self.q += g * g
        self.z = self.z + g - sigma * self.w

FTRLOpt = FTRL(X_b.shape[1], 1., 1, 0.5, 0.5) # alpha / beta 可以看作学习率的体现
train_online(100, FTRLOpt)

# 10  看一下直接用均值预测的效果
np.mean(np.square(y_test - np.mean(y_train, axis=0)))

参考

动手学深度学习

吴恩达深度学习微专业课程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值