美文网首页
【深度学习DL】二、梯度下降

【深度学习DL】二、梯度下降

作者: ChiangCMBA | 来源:发表于2019-07-01 20:42 被阅读0次

    一、平方平均误差

    除了对数损失函数之外,还有很多其他误差函数都可以应用在神经网络中。平方平均误差就是其中一个。从名字可以看出,它表示预测值和标签值的差的平方的平均值

    二、学习权重

    你了解了如何使用感知器来构建 AND 和 XOR 运算,但它们的权重都是人为设定的。如果你要进行一个运算,例如预测大学录取结果,但你不知道正确的权重是什么,该怎么办?你要从样本中学习权重,然后用这些权重来做预测

    要了解我们将如何找到这些权重,可以从我们的目标开始考虑。我们想让网络做出的预测与真实值尽可能接近。为了能够衡量,我们需要有一个指标来了解预测有多差,也就是误差(error)。一个普遍的指标是误差平方和 sum of the squared errors (SSE)

    {E = \frac{1}{2}\sum_\mu \sum_j \left[ y^{\mu}_j - \hat{y} ^{\mu} _j \right]^2}

    这里 {\hat y }预测值{ y}真实值。一个是所有输出单元 j 的和,另一个是所有数据点{ \mu}的和。这里看上去很复杂,但你一旦理解了这些符号之后,你就能明白这是怎么回事了。

    首先是内部这个对 {j }的求和。变量 {j}代表网络输出单元。所以这个内部的求和是指对于每一个输出单元,计算预测值 {\hat y }与真实值 {y }之间的差的平方,再求和。

    另一个对 {\mu}的求和是针对所有的数据点。也就是说,对每一个数据点,计算其对应输出单元的方差和,然后把每个数据点的方差和加在一起。这就是你整个输出的总误差。

    SSE 是一个很好的选择有几个原因:误差的平方总是正的,对大误差的惩罚大于小误差。同时,它对数学运算也更友好

    回想神经网络的输出,也就是预测值,取决于权重
    {\hat{y}^{\mu}_j = f \left( \sum_i{ w_{ij} x^{\mu}_i }\right) }

    相应的,误差也取决于权重
    {E = \frac{1}{2}\sum_\mu \sum_j \left[ y^{\mu}_j - f \left( \sum_i{ w_{ij} x^{\mu}_i }\right) \right]^2}
    我们想让网络预测的误差尽可能小,权重是让我们能够实现这个目标的调节旋钮。我们的目的是寻找权重 {w_{ij}}使得误差平方 {E} 最小。通常来说神经网络通过梯度下降来实现这一点。

    三、梯度下降

    用梯度下降,我们通过多个小步骤来实现目标。在这个例子中,我们希望一步一步改变权重来减小误差。借用前面的比喻,误差就像是山,我们希望走到山下。下山最快的路应该是最陡峭的那个方向,因此我们也应该寻找能够使误差最小化的方向。我们可以通过计算误差平方的梯度来找到这个方向。

    梯度改变率或者斜度的另一个称呼。如果你需要回顾这个概念,可以看下可汗学院对这个问题的讲解

    要计算变化率,我们要转向微积分,具体来说是导数。一个函数 {f(x)}的导函数{f'(x)} 给到你的是{f(x)}{x}这一点的斜率。例如 {x^2}{x^2}的导数是 {f'(x) = 2x}。所以,在{x = 2}这个点斜率 {f'(2) = 4}。画出图来就是:

    梯度示例.png

    梯度就是对多变量函数导数的泛化。我们可以用微积分来寻找误差函数中任意一点的梯度,它与输入权重有关,下一节你可以看到如何推导梯度下降的步骤。

    下面我画了一个拥有两个输入的神经网络误差示例,相应的,它有两个权重。你可以将其看成一个地形图,同一条线代表相同的误差,较深的线对应较大的误差。

    每一步,你计算误差和梯度,然后用它们来决定如何改变权重。重复这个过程直到你最终找到接近误差函数最小值的权重,即中间的黑点。

    梯度下降到最小误差

    注意事项

    因为权重会走向梯度带它去的位置,它们有可能停留在误差小,但不是最小的地方。这个点被称作局部最低点。如果权重初始值有错,梯度下降可能会使得权重陷入局部最优,例如下图所示。

    梯度下降引向局部最低点

    有方法可以避免这一点,被称作 momentum.

    四、梯度下降的实现

    简单神经网络输出结果的过程
    1.误差平方和 :The sum of the squared errors(SSE)

    {E = \frac{1}{2}\sum_\mu \sum_j \left[ y^{\mu}_j - \hat{y} ^{\mu} _j \right]^2}
    {\hat{y}^{\mu}_j = f \left( \sum_i{ w_{ij} x^{\mu}_i }\right) }
    {E = \frac{1}{2}\sum_\mu \sum_j \left[ y^{\mu}_j - f \left( \sum_i{ w_{ij} x^{\mu}_i }\right) \right]^2}

    • {\mu}表示全体数据
    • {x}表示输入值
    • {y}表示目标值

    误差平方和用于衡量预测效果,值越高,预测效果越差;值越低,预测效果越好。从上面的公式可以看出权重{w}是误差函数的参数,因此,权重可以当作控制旋钮用来调整预测值,从而最终影响整体误差。我们的目标是求取能使误差最小化的权重值。

    2.计算梯度下降值

    {\Delta w = - gradient}

    3.更新权重

    {w_i = w_i +\Delta w}
    {\Delta w }

    权重delta w计算 权重delta w计算_多输出单元

    用梯度下降来更新权重的算法概述:
    权重步长设定为 0:{ \Delta w_i = 0}
    对训练数据中的每一条记录:
    通过网络做正向传播,计算输出 { \hat y = f(\sum_i w_i x_i) }
    计算输出单元的误差项(error term) { \delta = (y - \hat y) * f'(\sum_i w_i x_i)}
    更新权重步长{ \Delta w_i = \Delta w_i + \delta x_i}
    更新权重 { w_i = w_i + \eta \Delta w_i / m}。其中 { \eta} 是学习率,{ m} 是数据点个数。这里我们对权重步长做了平均,为的是降低训练数据中大的变化。
    重复 { e} 代表{ (epoch)}

    五、梯度下降:代码

    一个权重的更新可以这样计算:

    {\Delta w_i = \eta \, \delta x_i}

    这里 error term {\delta}是指

    {\delta = (y - \hat y) f'(h) = (y - \hat y) f'(\sum w_i x_i)}
    记住,上面公式中 {(y - \hat y)}是输出误差,激活函数{ f(h)}的导函数是{f'(h)} ,我们把这个导函数称做输出的梯度。

    # Defining the sigmoid function for activations 
    # 定义 sigmoid 激活函数
    def sigmoid(x):
        return 1/(1+np.exp(-x))
    
    # Derivative of the sigmoid function
    # 激活函数的导数
    def sigmoid_prime(x):
        return sigmoid(x) * (1 - sigmoid(x))
    
    # Input data
    # 输入数据
    x = np.array([0.1, 0.3])
    # Target
    # 目标
    y = 0.2
    # Input to output weights
    # 输入到输出的权重
    weights = np.array([-0.8, 0.5])
    
    # The learning rate, eta in the weight step equation
    # 权重更新的学习率
    learnrate = 0.5
    
    # the linear combination performed by the node (h in f(h) and f'(h))
    # 输入和权重的线性组合
    h = x[0]*weights[0] + x[1]*weights[1]
    # or h = np.dot(x, weights)
    
    # The neural network output (y-hat)
    # 神经网络输出
    nn_output = sigmoid(h)
    
    # output error (y - y-hat)
    # 输出误差
    error = y - nn_output
    
    # output gradient (f'(h))
    # 输出梯度
    output_grad = sigmoid_prime(h)
    
    # error term (lowercase delta)
    error_term = error * output_grad
    
    # Gradient descent step 
    # 梯度下降一步
    del_w = [ learnrate * error_term * x[0],
              learnrate * error_term * x[1]]
    # or del_w = learnrate * error_term * x
    
    

    六、反向传播

    如何让多层神经网络学习呢?我们已了解了使用梯度下降来更新权重,反向传播算法则是它的一个延伸。以一个两层神经网络为例,可以使用链式法则计算输入层-隐藏层权重的误差

    要使用梯度下降法更新隐藏层的权重,你需要知道各隐藏层节点的误差最终输出的影响。每层的输出是由两层间的权重决定的,两层之间产生的误差,按权重缩放后在网络中向前传播。既然我们知道输出误差,便可以用权重来反向传播到隐藏层

    例如,输出层每个输出节点{k}的误差是 {\delta^o_k}​ ,隐藏节点 {j}的误差即为输出误差乘以输出层-隐藏层间的权重矩阵(以及梯度)。

    隐藏节点 j 的误差

    然后,梯度下降与之前相同,只是用新的误差:


    隐藏节点 j 的梯度下降

    其中 {w_{ij}}是输入和隐藏层之间的权重, {x_{i}}是输入值。这个形式可以表示任意层数。权重更新步长等于步长乘以输出层误差再乘以该层的输入值。

    反向传播_隐藏节点的权重

    现在,你有了输出误差,{\delta_{output}},便可以反向传播这些误差了。{V_{in}} 是该层的输入,比如经过隐藏层激活函数的输出值。

    范例

    反向传播步骤:

    • 计算网络输出误差
    • 计算输出层误差项
    • 用反向传播计算隐藏层误差项
    • 计算反向传播误差的权重更新步长

    实现反向传播

    现在我们知道输出层的误差是
    {\delta_k = (y_k - \hat y_k) f'(a_k)}

    隐藏层误差是

    {\delta_j = \sum[w_{jk}\delta_k]f'(h_j)}

    现在我们只考虑一个简单神经网络,它只有一个隐藏层和一个输出节点。这是通过反向传播更新权重的算法概述:

    • 把每一层权重更新的初始步长设置为 0
      • 输入到隐藏层的权重更新是 {\Delta w_{ij} }
      • 隐藏层到输出层的权重更新是 {\Delta W_j = 0}
    • 对训练数据当中的每一个点
      • 让它正向通过网络,计算输出 {\hat y}
      • 计算输出节点的误差梯度 {\delta^o = (y - \hat y) f'(z)}这里 {z = \sum_j W_j a_j}是输出节点的输入。
      • 误差传播到隐藏层 {\delta^h_j = \delta^o W_j f'(h_j)}
      • 更新权重步长:
        • {\Delta W_j = \Delta W_j + \delta^o a_j}
        • {\Delta w_{ij} = \Delta w_{ij} + \delta^h_j a_i}
    • 更新权重, 其中 {\eta} 是学习率,{ m } 是数据点的数量:
      • {W_j = W_j + \eta \Delta W_j / m}
      • {w_{ij} = w_{ij} + \eta \Delta w_{ij} / m}
    • 重复这个过程 {e}代。
    import numpy as np
    from data_prep import features, targets, features_test, targets_test
    
    np.random.seed(21)
    
    def sigmoid(x):
        """
        Calculate sigmoid
        """
        return 1 / (1 + np.exp(-x))
    
    
    # Hyperparameters
    n_hidden = 2  # number of hidden units
    epochs = 900
    learnrate = 0.005
    
    n_records, n_features = features.shape
    last_loss = None
    # Initialize weights
    weights_input_hidden = np.random.normal(scale=1 / n_features ** .5,
                                            size=(n_features, n_hidden))
    weights_hidden_output = np.random.normal(scale=1 / n_features ** .5,
                                             size=n_hidden)
    
    for e in range(epochs):
        del_w_input_hidden = np.zeros(weights_input_hidden.shape)
        del_w_hidden_output = np.zeros(weights_hidden_output.shape)
        for x, y in zip(features.values, targets):
            ## Forward pass ##
            # TODO: Calculate the output
            #(输入层输入)*(输入层到隐藏层的权重)
            hidden_input = np.dot(x, weights_input_hidden)
            #通过隐藏层的激活函数计算(隐藏层输出)
            hidden_output = sigmoid(hidden_input)
    
            #将隐藏层的输出,作为(输出层的输入)*(隐藏层到输出层的权重),并通过输出层的激活函数求得
            #输出层的输出
            output = sigmoid(np.dot(hidden_output,
                                    weights_hidden_output))
    
            ## Backward pass ##
            # TODO: Calculate the network's prediction error
            error = y - output
    
            # TODO: Calculate error term for the output unit
            output_error_term = error * output * (1 - output)
    
            ## propagate errors to hidden layer
    
            # TODO: Calculate the hidden layer's contribution to the error
            #隐藏节点的误差即为(输出误差)乘以(输出层-隐藏层间的权重矩阵)
            hidden_error = np.dot(output_error_term, weights_hidden_output)
    
            # TODO: Calculate the error term for the hidden layer
            hidden_error_term = hidden_error * hidden_output * (1 - hidden_output)
    
            # TODO: Update the change in weights
            del_w_hidden_output += output_error_term * hidden_output
            del_w_input_hidden += hidden_error_term * x[:, None]
    
        # TODO: Update weights
        weights_input_hidden += learnrate * del_w_input_hidden / n_records
        weights_hidden_output += learnrate * del_w_hidden_output / n_records
    
        # Printing out the mean square error on the training set
        if e % (epochs / 10) == 0:
            hidden_output = sigmoid(np.dot(x, weights_input_hidden))
            out = sigmoid(np.dot(hidden_output,
                                 weights_hidden_output))
            loss = np.mean((out - targets) ** 2)
    
            if last_loss and last_loss < loss:
                print("Train loss: ", loss, "  WARNING - Loss Increasing")
            else:
                print("Train loss: ", loss)
            last_loss = loss
    
    # Calculate accuracy on test data
    hidden = sigmoid(np.dot(features_test, weights_input_hidden))
    out = sigmoid(np.dot(hidden, weights_hidden_output))
    predictions = out > 0.5
    accuracy = np.mean(predictions == targets_test)
    print("Prediction accuracy: {:.3f}".format(accuracy))
    
    
    

    相关文章

      网友评论

          本文标题:【深度学习DL】二、梯度下降

          本文链接:https://www.haomeiwen.com/subject/dyskcctx.html