线性回归的梯度下降实现问题

时间:2016-12-14 18:36:59

标签: python machine-learning least-squares gradient-descent

我正在机器学习/线性回归上this Coursera class。以下是他们如何描述用于求解估计的OLS系数的梯度下降算法:

enter image description here

因此,他们使用w作为系数,H作为设计矩阵(或他们称之为的特征),y作为因变量。并且它们的收敛标准通常是RSS的梯度小于容差epsilon的范数;也就是说,他们对“未融合”的定义是:

enter image description here

我无法使此算法收敛,并且想知道我是否在实现中忽略了某些内容。下面是代码。请注意,我还通过statsmodels regression library运行了我在其中使用的样本数据集(df),只是为了看到回归可以收敛并获得与之相关的系数值。确实如此,他们是:

Intercept    4.344435
x1           4.387702
x2           0.450958

这是我的实施。在每次迭代中,它打印RSS梯度的范数:

import numpy as np
import numpy.linalg as LA
import pandas as pd
from pandas import DataFrame

# First define the grad function: grad(RSS) = -2H'(y-Hw)
def grad_rss(df, var_name_y, var_names_h, w):
    # Set up feature matrix H
    H = DataFrame({"Intercept" : [1 for i in range(0,len(df))]})
    for var_name_h in var_names_h:
        H[var_name_h] = df[var_name_h]

    # Set up y vector
    y = df[var_name_y]

    # Calculate the gradient of the RSS:  -2H'(y - Hw)
    result = -2 * np.transpose(H.values) @ (y.values - H.values @ w)

    return result

def ols_gradient_descent(df, var_name_y, var_names_h, epsilon = 0.0001, eta = 0.05):
    # Set all initial w values to 0.0001 (not related to our choice of epsilon)
    w = np.array([0.0001 for i in range(0, len(var_names_h) + 1)])

    # Iteration counter
    t = 0

    # Basic algorithm: keep subtracting eta * grad(RSS) from w until
    # ||grad(RSS)|| < epsilon.
    while True:
        t = t + 1

        grad = grad_rss(df, var_name_y, var_names_h, w)
        norm_grad = LA.norm(grad)

        if norm_grad < epsilon:
            break
        else:
            print("{} : {}".format(t, norm_grad))
            w = w - eta * grad

            if t > 10:
                raise Exception ("Failed to converge")

    return w

# ########################################## 

df = DataFrame({
        "y" : [20,40,60,80,100] ,
        "x1" : [1,5,7,9,11] ,
        "x2" : [23,29,60,85,99]         
    })

# Run
ols_gradient_descent(df, "y", ["x1", "x2"])

不幸的是,这并没有收敛,实际上会打印出每次迭代都会爆炸的规范:

1 : 44114.31506051333
2 : 98203544.03067812
3 : 218612547944.95386
4 : 486657040646682.9
5 : 1.083355358314664e+18
6 : 2.411675439503567e+21
7 : 5.368670935963926e+24
8 : 1.1951287949674022e+28
9 : 2.660496151835357e+31
10 : 5.922574875391406e+34
11 : 1.3184342751414824e+38
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
......
Exception: Failed to converge

如果我增加了足够的最大迭代次数,它就不会收敛,而只会向无穷大扩散。

这里是否存在实施错误,或者我是否在课堂笔记中误解了解释?

更新了w / Answer

正如@Kant建议的那样,eta需要在每次迭代时更新。该课程本身有一些示例公式,但没有一个有助于融合。 This section of the Wikipedia page about gradient descent提到Barzilai-Borwein approach是更新eta的好方法。我实现了它并改变了我的代码,以便在每次迭代时用它更新eta,并且回归成功收敛。下面是我对公式的维基百科版本到回归中使用的变量的翻译,以及实现它的代码。同样,此代码在原始ols_gradient_descent的循环中调用,以更新eta

enter image description here

def eta_t (w_t, w_t_minus_1, grad_t, grad_t_minus_1):
    delta_w = w_t - w_t_minus_1
    delta_grad = grad_t - grad_t_minus_1

    eta_t = (delta_w.T @ delta_grad) / (LA.norm(delta_grad))**2

    return eta_t

1 个答案:

答案 0 :(得分:3)

尝试减少eta的值。如果η太高,梯度下降会发散。