尝试模拟scikitlearn的LinearRegression时无法使线性回归收敛

问题描述 投票:1回答:1

因此,为了更好地理解线性回归的数据科学主题,我一直在尝试重新创建scikitlearn的LinearRegression模块在后台进行的工作。我遇到的问题是,当我开始对坡度进行梯度下降并使用数据进行截距时,无论使用何种步长或下降迭代,我都无法获得斜率和截距值的收敛。我试图找到两者之间的线性关系的数据是NBA FG% and NBA W/L% which can be found here(仅约250行数据,但我认为在pastebin中共享会更容易...)。您可以使用以下方法重新创建图形:数据的初始图形:

import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

def graph1(axis = []):
    x = FG_pct
    y = W_L_pct
    plt.scatter(x, y)

    plt.title('NBA FG% vs. Win%')
    plt.xlabel('FG pct (%)')
    plt.ylabel('Win pct (%)')
    if len(axis) > 1:
        plt.axis(axis)
    plt.legend()

看起来像这样(减去颜色:):

enter image description here

[两个变量之间有一个非常明显的关系,您基本上可以对最合适的线进行一个很好的猜测(我的猜测是斜率为5,截距为-1.75)。

我使用的梯度下降方程式是通过对斜率和截距采用损失函数的导数得出的,它们是:

def get_b_gradient(x_pts, y_pts, m, b):
    N = len(x_pts)
    tot = 0

    for x, y in zip(x_pts, y_pts):
        tot += y - (m*x + b)

    gradient = (-2/N)*tot
    return gradient

def get_m_gradient(x_pts, y_pts, m, b):
    N = len(x_pts)
    tot = 0

    for x, y in zip(x_pts, y_pts):
        tot += x * (y - (m*x + b))

    gradient = (-2/N)*tot
    return gradient

def get_step(x_pts, y_pts, m, b, learning_rate):
    init_b = get_b_gradient(x_pts, y_pts, m, b)
    init_m = get_m_gradient(x_pts, y_pts, m, b)

    final_b = b - (init_b*learning_rate)
    final_m = m - (init_m*learning_rate)

    return final_m, final_b

def gradient_descent(x_pts, y_pts, m, b, learning_rate, num_iterations):
    for i in range(num_iterations):
        m, b = get_step(x_pts, y_pts, m, b, learning_rate)
    return m, b

得到这些后,只需找到正确的迭代次数和学习率就可以使斜率和截距收敛到最佳值。由于我不确定找到这些值的系统方法,因此我只想尝试将不同的数量级输入到gradient_descent函数中:

# 1000 iterations, learning rate of 0.1, and initial slope and intercept guess of 0
m, b = gradient_descent(df['FG%'], df['W/L%'], 0, 0, 0.1, 1000)

您可以跟踪坡度的收敛并使用诸如此类的图形进行截距:

def convergence_graph(iterations, learning_rate, m, b):
    plt.subplot(1, 2, 1)
    for i in range(iterations):
        plt.scatter(i,b, color='orange')
        plt.title('convergence of b')
        m, b = get_step(df['FG%'], df['W/L%'], m, b, learning_rate)

    plt.subplot(1, 2, 2)
    for i in range(iterations):
        plt.scatter(i,m, color='blue')
        plt.title('convergence of m')
        m, b = get_step(df['FG%'], df['W/L%'], m, b, learning_rate)

这确实是问题显而易见的地方。使用与以前相同的迭代(1000)和learning_rate(0.1),您将看到一个如下图:

enter image description here

我会说这些图的线性意味着它仍在收敛,因此答案是提高学习率,但无论我为学习率选择什么数量级(一直到数百万)图表仍然保持线性并且永远不会收敛。我还尝试以较低的学习速度进行操作,并且弄乱了迭代次数……什么也没有。最终,我决定将其放入sklearn中,看它是否会遇到麻烦:

FG_pct = np.array(FG_pct)
FG_pct = FG_pct.reshape(-1, 1)

line_fitter = LinearRegression().fit(FG_pct, W_L_pct)

win_loss_predict = line_fitter.predict(FG_pct)

没问题:

enter image description here

所以这变得相当长,对此我感到抱歉。我没有任何要询问数据科学的人,也没有教授在附近,所以我想我会把它扔在这里。最终,我不确定问题是否出在以下方面:1)我的梯度下降方程式或2)我找到合适的学习率和迭代次数的方法。如果有人能指出正在发生的事情,为什么坡度和截距没有收敛,以及我做错了什么,将不胜感激!

python machine-learning scikit-learn data-science linear-regression
1个回答
3
投票

我建议从数据科学材料介绍这些主题的方式中退后一步。线性回归,梯度下降。这些不是数据科学主题。这些是统计概念。我将开始浏览介绍统计资料。您所掌握的几乎所有内容都会有一章介绍普通线性回归(OLS)。

梯度下降是牛顿方法的更复杂版本,用于寻找零。我强烈建议您查看该算法。如果您对演算有一个很好的了解,这很容易实现,听起来像您可能做的那样。如果您确实要研究它,请注意没有“学习率”。这个词使我感到g嘴。在大约十年前的“数据科学”时代,它被称为步长。

步长对于收敛速度至关重要。但是,如果太大,您很可能永远不会收敛。假设步长为10,而导数(单变量情况)为0.1。您的猜测将增加1。但是,如果最小值与当前的猜测仅相隔0.25个单位怎么办?恭喜。您的解决方案刚刚得到解决。您可以整日跳出最小值,却永远找不到(我怀疑这可能是您的代码中正在发生的事情)。许多算法使用的是减小步长。通常与迭代次数成正比。例如,在第j次迭代中,步长可能是10 / j。随着迭代的发展,这也存在可以通过稳定值和步长形状的附加边界来解决的问题。

您实际上想做的事真的很棒。有太多的人在“做数据科学”,他们不了解杰克的真实情况。不利的一面是,这不是一条容易采取的途径。我鼓励你继续前进!这很值得。但是您需要认识到您已经跳入了深渊。有一些更简单的算法可以使您受益匪浅,并在以后为更高级的东西打下基础。

编辑:更直接的答案

因此,代码中唯一需要更改的是渐变。在两个梯度计算中更改

gradient = (-2/N)*tot

to

gradient = (-2)*tot

梯度的分母中没有N。一些推导可能显示出这种方式,但这可能是因为它们推导了封闭形式的解决方案,并将整个事物设置为零。

看来您的参数变得疯狂的原因是您的步长太大。使用这一更改,它返回了params:

m, b = gradient_descent(FG_pct, W_L_pct, 6, -1, 0.003, 10000)
m = 6.465
b = -2.44

[我认为在您的示例中,您使用0, 0的初始猜测来植入算法。好的初步猜测可能会带来很大的不同。

闭式替代这是使用封闭形式的示例。无需搜索即可产生确切答案。

from matplotlib.pyplot import plot, scatter
import numpy as np

Y = np.array(W_L_pct)
X = np.array([np.ones(len(FG_pct)), FG_pct]).reshape(2, 270).T

A = np.linalg.inv(np.matmul(X.T, X))
B = np.matmul(X.T, Y)

beta = np.matmul(A, B)
m, b = beta[1], beta[0]
print(m, b)
r = np.arange(0.4, 0.52, 0.01)
scatter(FG_pct, Y)
plot(r, m * r + b)
© www.soinside.com 2019 - 2024. All rights reserved.