首页 > 解决方案 > 梯度下降的实现爆炸到无穷大?

问题描述

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

!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()

在此处输入图像描述

然后,我在一个简单的二次函数 (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()

我得到这张图

在此处输入图像描述

在我想要的任何一点上,它都是一个斜坡。为什么它在我的代码中不起作用?

标签: pythonlinear-regression

解决方案


现在我明白了,主要问题是学习率。学习率0.01太高。保持低于0.00035效果很好。关于0.0002工作得很好而且很快。我尝试用图形绘制事物,发现它产生了很大的不同。

0.00035使用1000 次迭代的学习率,这就是图表:

在此处输入图像描述

0.0002使用1000 次迭代的学习率,这就是图表:

在此处输入图像描述

使用学习率0.000410迭代次数,这就是图表:

在此处输入图像描述

它没有收敛到点,而是发散了。这就是为什么学习率很重要,任何大于 0.0004 的结果都会相同。

我花了很长时间才弄清楚。


推荐阅读