是什么决定了我的Python梯度下降算法是否会收敛?

时间:2016-04-23 02:08:13

标签: python algorithm linear-regression gradient-descent convergence

我在Python中实现了一个单变量线性回归模型,它使用梯度下降来找到最佳拟合线的截距和斜率(我使用的是梯度下降而不是直接计算截距和斜率的最佳值因为我最终想要推广到多元回归。)

我正在使用的数据如下。 sales是因变量(以美元计),temp是自变量(摄氏度)(想想冰淇淋销售与温度或类似情况)。

sales   temp
215     14.20
325     16.40
185     11.90
332     15.20
406     18.50
522     22.10
412     19.40
614     25.10
544     23.40
421     18.10
445     22.60
408     17.20

这是我的数据经过规范化后的数据:

sales        temp 
0.06993007  0.174242424
0.326340326 0.340909091
0           0
0.342657343 0.25
0.515151515 0.5
0.785547786 0.772727273
0.529137529 0.568181818
1           1
0.836829837 0.871212121
0.55011655  0.46969697
0.606060606 0.810606061
0.51981352  0.401515152

我的算法代码:

import numpy as np
import pandas as pd
from scipy import stats

class SLRegression(object):
    def __init__(self, learnrate = .01, tolerance = .000000001, max_iter = 10000):

        # Initialize learnrate, tolerance, and max_iter.
        self.learnrate = learnrate
        self.tolerance = tolerance
        self.max_iter = max_iter

    # Define the gradient descent algorithm.
    def fit(self, data):
        # data   :   array-like, shape = [m_observations, 2_columns] 

        # Initialize local variables.
        converged = False
        m = data.shape[0]

        # Track number of iterations.
        self.iter_ = 0

        # Initialize theta0 and theta1.
        self.theta0_ = 0
        self.theta1_ = 0

        # Compute the cost function.
        J = (1.0/(2.0*m)) * sum([(self.theta0_ + self.theta1_*data[i][1] - data[i][0])**2 for i in range(m)])
        print('J is: ', J)

        # Iterate over each point in data and update theta0 and theta1 on each pass.
        while not converged:
            diftemp0 = (1.0/m) * sum([(self.theta0_ + self.theta1_*data[i][1] - data[i][0]) for i in range(m)])
            diftemp1 = (1.0/m) * sum([(self.theta0_ + self.theta1_*data[i][1] - data[i][0]) * data[i][1] for i in range(m)])

            # Subtract the learnrate * partial derivative from theta0 and theta1.
            temp0 = self.theta0_ - (self.learnrate * diftemp0)
            temp1 = self.theta1_ - (self.learnrate * diftemp1)

            # Update theta0 and theta1.
            self.theta0_ = temp0
            self.theta1_ = temp1

            # Compute the updated cost function, given new theta0 and theta1.
            new_J = (1.0/(2.0*m)) * sum([(self.theta0_ + self.theta1_*data[i][1] - data[i][0])**2 for i in range(m)])
            print('New J is: %s') % (new_J)

            # Test for convergence.
            if abs(J - new_J) <= self.tolerance:
                converged = True
                print('Model converged after %s iterations!') % (self.iter_)

            # Set old cost equal to new cost and update iter.
            J = new_J
            self.iter_ += 1

            # Test whether we have hit max_iter.
            if self.iter_ == self.max_iter:
                converged = True
                print('Maximum iterations have been reached!')

        return self

    def point_forecast(self, x):
        # Given feature value x, returns the regression's predicted value for y.
        return self.theta0_ + self.theta1_ * x


# Run the algorithm on a data set.
if __name__ == '__main__':
    # Load in the .csv file.
    data = np.squeeze(np.array(pd.read_csv('sales_normalized.csv')))

    # Create a regression model with the default learning rate, tolerance, and maximum number of iterations.
    slregression = SLRegression()

    # Call the fit function and pass in the data.
    slregression.fit(data)

    # Print out the results.
    print('After %s iterations, the model converged on Theta0 = %s and Theta1 = %s.') % (slregression.iter_, slregression.theta0_, slregression.theta1_)
    # Compare our model to scipy linregress model.
    slope, intercept, r_value, p_value, slope_std_error = stats.linregress(data[:,1], data[:,0])
    print('Scipy linear regression gives intercept: %s and slope = %s.') % (intercept, slope)

    # Test the model with a point forecast.
    print('As an example, our algorithm gives y = %s given x = .87.') % (slregression.point_forecast(.87)) # Should be about .83.
    print('The true y-value for x = .87 is about .8368.')

我无法准确理解允许算法收敛的内容与完全错误的返回值。给定learnrate = .01tolerance = .0000000001max_iter = 10000,结合规范化数据,我可以得到梯度下降算法来收敛。但是,当我使用非标准化数据时,最小的我可以在没有返回NaN的算法的情况下使学习率为.005。这会将成本函数的变化从迭代变为迭代,直到614左右,但我不能让它变得更低。

这种类型的算法肯定要求标准化数据,如果是这样,为什么?另外,考虑到算法需要标准化值,以非标准化形式获取小说x-value并将其插入点预测的最佳方法是什么?例如,如果我打算将这个算法交付给客户端,那么他们就可以对自己进行预测(我不是,但为了争论......),我不希望他们只是简单地插上在未规范化的x-value

总而言之,使用tolerancemax_iterlearnrate,大部分时间都会给我带来非收敛的结果。这是正常的,还是我的算法中存在导致此问题的缺陷?

1 个答案:

答案 0 :(得分:1)

  

给定学习率= .01,容差= .0000000001和max_iter = 10000,结合归一化数据,我可以得到梯度下降算法收敛。但是,当我使用非标准化数据时,最小的I可以在不返回NaN的算法的情况下提高学习率.005

您可以根据自己的算法设置算法。

数据的标准化使得最佳拟合的y轴截距 0.0。否则,你可以从起始猜测中获得数千个单位的y截距,并且在你真正开始优化部分之前你必须在那里跋涉。

  

这种类型的算法肯定要求标准化数据,如果是,为什么?

不,绝对没有,但是如果你没有规范化,你应该更智能地选择一个起点(你从(m,b)=(0,0)开始)。如果您没有规范化数据,那么您的学习量也可能太小,而且与您的容忍度相同。

  

此外,考虑到算法需要标准化值,以非标准化形式采用新的x值并将其插入点预测的最佳方法是什么?

应用您对原始数据应用的任何转换,以将规范化数据转换为新的x值。 (规范化的代码超出了您的显示范围)。如果此测试点落在原始数据的(minx,maxx)范围内,则一旦转换,它应该落在0 <= x <= 1之内。一旦有了这个标准化的测试点,将其插入到你的θ方程中一行(记住,你的thet是m,b是一个直线方程的y截距形式)。

  

总而言之,在大多数情况下,使用公差,max_iter和learnrate会给我带来非收敛的结果。

对于格式正确的问题,如果您实际上发散,通常意味着您的步长太大。尝试降低它。

如果它在达到最大迭代次数之前根本没有收敛,那可能是一些问题:

  • 您的步长太小,
  • 你的容忍度太小,
  • 您的最大迭代次数太小,
  • 你的出发点选择不当

在您的情况下,使用非标准化数据导致您的起点(0,0)非常偏离(非标准化数据的(m,b)大约是(-159,30),而(m,b)你的标准化数据是(0.10,0.79)),因此大多数(如果不是所有的)迭代都被用于到达感兴趣的区域。

这样做的问题在于,通过增加步长以更快地到达感兴趣的区域,也可以使它更容易更少 - 一旦到达那里就能找到收敛。

为了解释这一点,一些梯度下降算法具有动态步长(或学习率),因此在开始时采取较大步骤,而在接近收敛时采用较小步骤。

在整个算法中保留theta对的历史记录,然后绘制它们也可能会有所帮助。您将能够立即看到使用标准化和非标准化输入数据之间的差异。