梯度下降的实现爆炸到无穷大?

时间:2021-07-13 04:54:34

标签: python linear-regression

这就是我为线性回归生成训练数据的方式。

!pip install grapher, numpy

from grapher import Grapher
import matplotlib.pyplot as plt
import numpy as np

# Secret: y = 3x + 4

# x, y = [float(row[0]) for row in rows], [float(row[5]) for row in rows]

x, y = [a for a in range(-20, 20)], [3*a + 4 for a in range(-20, 20)]


g = Grapher(['3*x + 4'], title="y = 3x+4")
plt.scatter(x, y)
g.plot()

enter image description here

然后,我在一个简单的二次函数 (x - 7)^2 上尝试了梯度下降

def n(x):
    return (x-7)**2

cur_x = 0
lr = 0.001
ittr = 10000
n = 0
prev_x = -1
max_precision = 0.0000001
precision = 1

while n < ittr and precision > max_precision:
    prev_x = cur_x

    cur_x = cur_x - lr * (2*(cur_x - 7))
    precision = abs(prev_x - cur_x)

    n+=1
    if n%100 == 0:
        print(n, ':')
        print(cur_x)
        print()

print(cur_x)

这很完美。

然后我做了一个线性回归类来实现同样的事情。

class LinearRegression:
    def __init__(self, X, Y):
        self.X = X
        self.Y = Y
        self.m = 1
        self.c = 0
        self.learning_rate = 0.01
        self.max_precision = 0.000001
        self.itter = 10000

    def h(self, x, m, c):
        return m * x + c

    def J(self, m, c):
        loss = 0
        for x in self.X:
            loss += (self.h(x, m, c) - self.Y[self.X.index(x)])**2

        return loss/2

    def calc_loss(self):
        return self.J(self.m, self.c)

    def guess_answer(self, step=1):
        losses = []
        mcvalues = []
        for m in np.arange(-10, 10, step):
            for c in np.arange(-10, 10, step):
                mcvalues.append((m, c))
                losses.append(self.J(m, c))

        minloss = sorted(losses)[0]
        return mcvalues[losses.index(minloss)]

    def gradient_decent(self):
        print('Orignal: ', self.m, self.c)

        nm = 0
        nc = 0

        prev_m = 0
        perv_c = -1

        mprecision = 1
        cprecision = 1

        while nm < self.itter and mprecision > self.max_precision:
            prev_m = self.m
            nm += 1

            self.m = self.m - self.learning_rate * sum([(self.h(x, self.m, self.c) - self.Y[self.X.index(x)])*x for x in self.X])
            mprecision = abs(self.m - prev_m)

        return self.m, self.c

    def graph_loss(self):
        plt.scatter(0, self.J(0))
        print(self.J(0))
        plt.plot(self.X, [self.J(x) for x in self.X])

    def check_loss(self):
        plt.plot([m for m in range(-20, 20)], [self.J(m, 0) for m in range(-20, 20)])
        x1 = 10
        y1 = self.J(x1, 0)
        l = sum([(self.h(x, x1, self.c) - self.Y[self.X.index(x)])*x for x in self.X])
        print(l)
        plt.plot([m for m in range(-20, 20)], [(l*(m - x1)) + y1 for m in range(-20, 20)])
        plt.scatter([x1], [y1])

LinearRegression(x, y).gradient_decent()

输出是

Orignal:  1 0
(nan, 0)

然后我尝试绘制我的损失函数 (J(m, c)) 并尝试使用它的导数来查看它是否真的给出了斜率。我怀疑我搞砸了我的 d(J(m, c))/dm

运行LinearRegression(x, y).check_loss()

我得到这张图

enter image description here

无论我希望它是什么,它都是一个斜坡。为什么它在我的代码中不起作用?

1 个答案:

答案 0 :(得分:0)

现在我明白了,主要问题在于学习率。 0.01 的学习率太高。保持低于 0.00035 效果很好。关于 0.0002 运行良好且快速。我尝试将事物绘制成图形,发现它产生了很大的不同。

在学习率为 0.00035 和 1000 次迭代的情况下,这是图表, enter image description here

使用 0.0002 的学习率和 1000 次迭代,这是图表, enter image description here

学习率为 0.0004 并且只有 10 次迭代,这是图表, enter image description here

不是收敛到重点,而是发散。这就是为什么学习率很重要,并且任何大于 0.0004 的值都会导致相同的结果。

我花了很长时间才弄明白。