Gradient Descent

梯度下降

梯度下降法自动优化 𝑤𝑤𝑏𝑏

  1. 已经建立了一个预测 fw,b(x(i))f_{w,b}(x^{(i)}) 的线性模型:

fw,b(x(i))=wx(i)+bf_{w,b}(x^{(i)}) = wx^{(i)} + b
  1. 在线性回归中,利用输入的训练数据来拟合参数 𝑤𝑤 , 𝑏𝑏 ,最大限度地减小预测结果 fw,b(x(i))f_{w,b}(x^{(i)}) 与实际数据 y(i)y^{(i)} 之间的误差。这个度量称为 𝑐𝑜𝑠𝑡𝑐𝑜𝑠𝑡, 𝐽(𝑤,𝑏)𝐽(𝑤,𝑏). 在训练中,您需要衡量所有训练样本的成本 x(i),y(i)x^{(i)},y^{(i)}

J(w,b)=12mi=0m1(fw,b(x(i))y(i))2J(w,b) = \frac{1}{2m} \sum\limits_{i = 0}^{m-1} (f_{w,b}(x^{(i)}) - y^{(i)})^2
  1. 梯度下降被描述为

repeat until convergence:  {  w=wαJ(w,b)w  b=bαJ(w,b)b}\begin{align*} \text{repeat}&\text{ until convergence:} \; \lbrace \newline \; w &= w - \alpha \frac{\partial J(w,b)}{\partial w} \; \newline b &= b - \alpha \frac{\partial J(w,b)}{\partial b} \newline \rbrace \end{align*}
  1. 其中,参数 𝑤,𝑏 同时更新。 梯度定义如下

J(w,b)w=1mi=0m1(fw,b(x(i))y(i))x(i)J(w,b)b=1mi=0m1(fw,b(x(i))y(i))\begin{align} \frac{\partial J(w,b)}{\partial w} &= \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{w,b}(x^{(i)}) - y^{(i)})x^{(i)}\\ \frac{\partial J(w,b)}{\partial b} &= \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{w,b}(x^{(i)}) - y^{(i)}) \\ \end{align}

  1. 这里的同时是指在更新任何参数之前,先计算所有参数的偏导数

Implement Gradient Descent - 实施梯度下降

针对一个特征实施梯度下降算法。您需要三个函数

  • compute_gradient,执行上述公式 4

  • compute_cost执行上述公式 2

  • gradient_descent,利用计算梯度和计算成本

Conventions - 公约:

包含部分导数的 python 变量的命名也遵循这种模式, J(w,b)b\frac{\partial J(w,b)}{\partial b} 将是dj_db

w.r.t 是 with respect to 的缩写,意思是“相对与” or ”关于“,如 𝐽(𝑤𝑏)𝐽(𝑤𝑏) 的偏导数,相对于 𝑏𝑏

compute_gradient - 计算梯度

compute_gradient实现了上述 4,并返回 J(w,b)w\frac{\partial J(w,b)}{\partial w} , J(w,b)b\frac{\partial J(w,b)}{\partial b}。内嵌注释对操作进行了说明。

代码实现

import math, copy
import numpy as np
import matplotlib.pyplot as plt

# Load our data set
x_train = np.array([1.0, 2.0])   #features
y_train = np.array([300.0, 500.0])   #target value

#Function to calculate the cost
def compute_cost(x, y, w, b):
   
    m = x.shape[0] 
    cost = 0
    
    for i in range(m):
        f_wb = w * x[i] + b
        cost = cost + (f_wb - y[i])**2
    total_cost = 1 / (2 * m) * cost

    return total_cost

def compute_gradient(x, y, w, b): 
    """
    Computes the gradient for linear regression
    - 计算线性回归的梯度 
    Args 
    - 参数:
      x (ndarray (m,)): Data, m examples 
      y (ndarray (m,)): target values
      w,b (scalar)    : model parameters  
    Returns
      dj_dw (scalar): The gradient of the cost w.r.t. the parameters w
                      - 成本函数相对于参数w的梯度
      dj_db (scalar): The gradient of the cost w.r.t. the parameter b     
                      - 成本函数相对于参数b的梯度 
 
 w.r.t 是 with respect to 的缩写,意思是“相对与” or ”关于“                
     """
    
    # Number of training examples - 训练实例数量
    m = x.shape[0]    
    dj_dw = 0
    dj_db = 0
    
    for i in range(m):  
        f_wb = w * x[i] + b 
        dj_dw_i = (f_wb - y[i]) * x[i] 
        dj_db_i = f_wb - y[i] 
        dj_db += dj_db_i
        dj_dw += dj_dw_i 
    dj_dw = dj_dw / m 
    dj_db = dj_db / m 
        
    return dj_dw, dj_db
    
plt_gradients(x_train,y_train, compute_cost, compute_gradient)
plt.show()    

图中介绍了梯度下降法如何利用成本对某一点参数的偏导数来更新该参数

使用compute_gradient函数查找并绘制成本函数相对于一个参数的偏导数 𝑤0𝑤0

上图左侧显示的是 J(w,b)w\frac{\partial J(w,b)}{\partial w} 或成本曲线相对于 𝑤𝑤 三个点的斜率。图中右侧的导数为正,左侧的导数为负。由于 "碗形 "的原因,导数总是会导致梯度向底部下降,而底部的梯度为零

左图固定了 𝑏=100𝑏=100 . 梯度下降将利用 J(w,b)w\frac{\partial J(w,b)}{\partial w}J(w,b)b\frac{\partial J(w,b)}{\partial b} 更新参数。右侧的 "箭形图 "提供了一种查看两个参数梯度的方法。箭头大小反映了该点的梯度大小。箭头的方向和斜率反映了 J(w,b)w\frac{\partial J(w,b)}{\partial w}J(w,b)b\frac{\partial J(w,b)}{\partial b} 在该点的比率。 请注意,梯度点远离最小值。回顾上文公式 (3)。从 𝑤𝑤𝑏𝑏 的当前值中减去按比例缩放的梯度值。这将使参数向降低成本的方向移动

Gradient Descent - 梯度下降

既然可以计算梯度,就可以在下面的gradient_descent 中实现上文公式 3 所描述的梯度下降。实现的细节将在注释中描述。下面,你将利用这个函数在训练数据上找到 𝑤𝑤𝑏𝑏 的最佳值

repeat until convergence:  {  w=wαJ(w,b)w  b=bαJ(w,b)b}\begin{align*} \text{repeat}&\text{ until convergence:} \; \lbrace \newline \; w &= w - \alpha \frac{\partial J(w,b)}{\partial w} \; \newline b &= b - \alpha \frac{\partial J(w,b)}{\partial b} \newline \rbrace \end{align*}
def gradient_descent(x, y, w_in, b_in, alpha, num_iters, cost_function, gradient_function): 
    """
    执行梯度下降以拟合 w、b.以学习率 alpha 计算 num_iters 个梯度步长,更新 w,b
    
    Args:
      x (ndarray (m,))  : Data, m examples 
      y (ndarray (m,))  : target values
      w_in,b_in (scalar): initial values of model parameters - 模型参数的初始值 
      alpha (float):     Learning rate - 学习率
      num_iters (int):   number of iterations to run gradient descent - 梯度下降的迭代次数
      cost_function:     function to call to produce cost - 函数来产生成本
      gradient_function: function to call to produce gradient - 函数来产生梯度
      
    Returns:
      w (scalar): Updated value of parameter after running gradient descent - 梯度下降后的参数更新值
      b (scalar): Updated value of parameter after running gradient descent - 梯度下降后的参数更新值
      J_history (List): History of cost values
      p_history (list): History of parameters [w,b] 

      """
    
    # An array to store cost J and w's at each iteration primarily for graphing later
    # 一个数组,用于存储每次迭代的成本 J 和 w,主要用于以后的图形绘制
    J_history = []
    p_history = []
    b = b_in
    w = w_in
    
    for i in range(num_iters):
        # Calculate the gradient and update the parameters using gradient_function
        # 使用 gradient_function 计算梯度并更新参数
        dj_dw, dj_db = gradient_function(x, y, w , b)     

        # Update Parameters using equation (3) above
        # 使用上述公式 (3) 更新参数
        b = b - alpha * dj_db                            
        w = w - alpha * dj_dw                            

        # Save cost J at each iteration
        # 每次迭代都保存成本 J
        if i<100000:      # prevent resource exhaustion - 防止资源耗尽
            J_history.append( cost_function(x, y, w , b))
            p_history.append([w,b])
        # Print cost every at intervals 10 times or as many iterations if < 10
        # 每间隔 10 次打印一次cost,或者如果小于 10 次,则打印所有迭代次数
        if i% math.ceil(num_iters/10) == 0:
            print(f"Iteration {i:4}: Cost {J_history[-1]:0.2e} ",
                  f"dj_dw: {dj_dw: 0.3e}, dj_db: {dj_db: 0.3e}  ",
                  f"w: {w: 0.3e}, b:{b: 0.5e}")
 
    return w, b, J_history, p_history #return w and J,w history for graphing - 返回用于绘图的 w 和 J,w 的历史记录
    
# initialize parameters - 初始化参数
w_init = 0
b_init = 0
# some gradient descent settings - 一些梯度下降的设置
iterations = 10000
tmp_alpha = 1.0e-2
# run gradient descent - 运行梯度下降
w_final, b_final, J_hist, p_hist = gradient_descent(x_train ,y_train, w_init, b_init, tmp_alpha, 
                                                    iterations, compute_cost, compute_gradient)
print(f"(w,b) found by gradient descent: ({w_final:8.4f},{b_final:8.4f})")
Iteration    0: Cost 7.93e+04  dj_dw: -6.500e+02, dj_db: -4.000e+02   w:  6.500e+00, b: 4.00000e+00
Iteration 1000: Cost 3.41e+00  dj_dw: -3.712e-01, dj_db:  6.007e-01   w:  1.949e+02, b: 1.08228e+02
Iteration 2000: Cost 7.93e-01  dj_dw: -1.789e-01, dj_db:  2.895e-01   w:  1.975e+02, b: 1.03966e+02
Iteration 3000: Cost 1.84e-01  dj_dw: -8.625e-02, dj_db:  1.396e-01   w:  1.988e+02, b: 1.01912e+02
Iteration 4000: Cost 4.28e-02  dj_dw: -4.158e-02, dj_db:  6.727e-02   w:  1.994e+02, b: 1.00922e+02
Iteration 5000: Cost 9.95e-03  dj_dw: -2.004e-02, dj_db:  3.243e-02   w:  1.997e+02, b: 1.00444e+02
Iteration 6000: Cost 2.31e-03  dj_dw: -9.660e-03, dj_db:  1.563e-02   w:  1.999e+02, b: 1.00214e+02
Iteration 7000: Cost 5.37e-04  dj_dw: -4.657e-03, dj_db:  7.535e-03   w:  1.999e+02, b: 1.00103e+02
Iteration 8000: Cost 1.25e-04  dj_dw: -2.245e-03, dj_db:  3.632e-03   w:  2.000e+02, b: 1.00050e+02
Iteration 9000: Cost 2.90e-05  dj_dw: -1.082e-03, dj_db:  1.751e-03   w:  2.000e+02, b: 1.00024e+02
(w,b) found by gradient descent: (199.9929,100.0116)

注意上面印出的梯度下降过程的一些特征

  • 如图所描述,成本一开始很大,然后迅速下降

  • 偏导数dj_dwdj_db也会变小,起初会很快变小,然后会越来越慢。如图所描述,当过程接近 "碗底 "时,由于此时的导数值较小,过程的进展就会减慢

  • 虽然学习率 alpha 保持不变,但学习进度会减慢

Cost versus iterations of gradient descent - 成本与梯度下降迭代的关系

成本与迭代次数的关系图是衡量梯度下降进展的有用指标。在成功的运行过程中,代价总是会降低的。成本的变化起初非常迅速,因此用不同于最终下降的比例来绘制最初的下降曲线是非常有用的。在下面的图中,请注意坐标轴上的成本比例和迭代步长

现在,已经找到了 𝑤𝑤𝑏𝑏 这两个参数的最佳值,现在可以使用模型预测房屋价值。预测值与相同房屋的训练值几乎相同。此外,预测值以外的值与预期值一致。

Increased Learning Rate - 提高学习率

在图中,公式 (3) 中学习率 𝛼𝛼 的适当值。 𝛼𝛼 越大,梯度下降收敛到解决方案的速度就越快。但是,如果学习率过大,梯度下降过程就会发散。上面是一个很好收敛的解的例子

试着增加 𝛼𝛼 的值

# initialize parameters
w_init = 0
b_init = 0
# set alpha to a large value
iterations = 10
tmp_alpha = 8.0e-1
# run gradient descent
w_final, b_final, J_hist, p_hist = gradient_descent(x_train ,y_train, w_init, b_init, tmp_alpha, 
                                                    iterations, compute_cost, compute_gradient)
Iteration    0: Cost 2.58e+05  dj_dw: -6.500e+02, dj_db: -4.000e+02   w:  5.200e+02, b: 3.20000e+02
Iteration    1: Cost 7.82e+05  dj_dw:  1.130e+03, dj_db:  7.000e+02   w: -3.840e+02, b:-2.40000e+02
Iteration    2: Cost 2.37e+06  dj_dw: -1.970e+03, dj_db: -1.216e+03   w:  1.192e+03, b: 7.32800e+02
Iteration    3: Cost 7.19e+06  dj_dw:  3.429e+03, dj_db:  2.121e+03   w: -1.551e+03, b:-9.63840e+02
Iteration    4: Cost 2.18e+07  dj_dw: -5.974e+03, dj_db: -3.691e+03   w:  3.228e+03, b: 1.98886e+03
Iteration    5: Cost 6.62e+07  dj_dw:  1.040e+04, dj_db:  6.431e+03   w: -5.095e+03, b:-3.15579e+03
Iteration    6: Cost 2.01e+08  dj_dw: -1.812e+04, dj_db: -1.120e+04   w:  9.402e+03, b: 5.80237e+03
Iteration    7: Cost 6.09e+08  dj_dw:  3.156e+04, dj_db:  1.950e+04   w: -1.584e+04, b:-9.80139e+03
Iteration    8: Cost 1.85e+09  dj_dw: -5.496e+04, dj_db: -3.397e+04   w:  2.813e+04, b: 1.73730e+04
Iteration    9: Cost 5.60e+09  dj_dw:  9.572e+04, dj_db:  5.916e+04   w: -4.845e+04, b:-2.99567e+04

上图中, 𝑤𝑤𝑏𝑏 在正负值之间来回跳动,每次迭代的绝对值都在增加。此外,每次迭代 J(w,b)w\frac{\partial J(w,b)}{\partial w} 的符号都会发生变化,成本不降反升。这清楚地表明,学习率过大,解决方案正在发散。 下图说明

上图左侧显示了 𝑤𝑤 在梯度下降最初几步的进展情况。 𝑤𝑤 从正值到负值摆动,成本快速增长。梯度下降同时在 𝑤𝑤𝑏𝑏 上运行,因此需要右侧的三维图才能看清全貌

Last updated

Was this helpful?