线性回归梯度下降表现差

6
我已经在C ++中实现了一个简单的线性回归(目前只有单变量),以帮助我理解这些概念。我相当确定关键算法是正确的,但我的性能很差。
这是实际执行梯度下降的方法:
void LinearRegression::BatchGradientDescent(std::vector<std::pair<int,int>> & data,float& theta1,float& theta2)
{

    float weight = (1.0f/static_cast<float>(data.size()));
    float theta1Res = 0.0f;
    float theta2Res = 0.0f;

    for(auto p: data)
    {

        float cost = Hypothesis(p.first,theta1,theta2) - p.second;
        theta1Res += cost;
        theta2Res += cost*p.first;
    }   

    theta1 = theta1 - (m_LearningRate*weight* theta1Res);
    theta2 = theta2 - (m_LearningRate*weight* theta2Res);
}

其他关键功能如下:

float LinearRegression::Hypothesis(float x,float theta1,float theta2) const
{
    return theta1 + x*theta2;
}


float LinearRegression::CostFunction(std::vector<std::pair<int,int>> & data,
                                     float theta1,
                                     float theta2) const
{ 
    float error = 0.0f;
    for(auto p: data)
    {

        float prediction = (Hypothesis(p.first,theta1,theta2) - p.second) ;
        error += prediction*prediction;
    }

    error *= 1.0f/(data.size()*2.0f);
    return error;
}

void LinearRegression::Regress(std::vector<std::pair<int,int>> & data)
{
    for(unsigned int itr = 0; itr < MAX_ITERATIONS; ++itr)
    {
       BatchGradientDescent(data,m_Theta1,m_Theta2);
       //Some visualisation code
    }
}

现在的问题是,如果学习率大于约0.000001,则梯度下降后成本函数的值比之前高。也就是说,算法反而起反作用。尽管该线很快形成了经过原点的直线,但实际上需要数百万次迭代才能得出一个合理的拟合线。
使用0.01的学习率,在六次迭代后,输出结果为:(其中差异为costAfter-costBefore)
Cost before 102901.945312, cost after 517539430400.000000, difference 517539332096.000000
Cost before 517539430400.000000, cost after 3131945127824588800.000000, difference 3131944578068774912.000000
Cost before 3131945127824588800.000000, cost after 18953312418560698826620928.000000, difference 18953308959796185006080000.000000
Cost before 18953312418560698826620928.000000, cost after 114697949347691988409089177681920.000000, difference 114697930004878874575022382383104.000000
Cost before 114697949347691988409089177681920.000000, cost after inf, difference inf
Cost before inf, cost after inf, difference nan

在这个例子中,theta被设置为零,学习率为0.000001,迭代次数为8,000,000!可视化代码仅在每100,000次迭代后更新图形。
创建数据点的函数:

enter image description here

static void SetupRegressionData(std::vector<std::pair<int,int>> & data)
{
    srand (time(NULL));

    for(int x = 50; x < 750; x += 3)
    {
        data.push_back(std::pair<int,int>(x+(rand() % 100), 400 + (rand() % 100) ));
    }
}

简而言之,如果我的学习率太高,梯度下降算法实际上会反向运行并趋于无限大;如果将其降低到实际收敛于极小值的程度,则需要进行的迭代次数是无法接受的。

在核心算法中,我有没有漏掉什么或犯了错误?


你有算法的快速参考吗?这可能更容易找到问题。此外,根据中间结果,每次迭代后成本实际上会增加,我认为一定有什么问题。 - TimeString
该算法取自于斯坦福机器学习课程讲座链接,以及其他几个视频,但它是相当标准的。成本在每次迭代后仅在学习率过高时增加(我认为这是错误的),如果学习率较低,则会缓慢减少。 - Davors72
另一件事是,我猜在 CostFunction() 中你需要在返回结果前取绝对值。 - TimeString
我还没有观看你获取算法的视频,但是像你描述的梯度下降算法本身表现很差。当你接近最小值时,梯度趋近于零,这会导致算法在接近最小值时减速。Numerical Recipes对最小化算法有一个合理的讨论(如果你关心效率,这也不是你实现最小二乘的方式,但我假设这只是个测试案例)。 - David Roundy
1个回答

6

看起来一切都按预期运行,但是您在选择合理的学习率时遇到了问题。这不是完全琐碎的问题,有许多方法可以解决,从预定义的逐步降低学习率的计划(参见例如这篇论文)到自适应方法,例如AdaGrad或AdaDelta。

对于您使用固定学习率的香草实现,您应该在将数据馈送到梯度下降算法之前将其归一化为零均值和单位标准差,以使您更轻松地推理学习率。然后您只需要相应地重新调整您的预测即可。


谢谢!变量的归一化效果非常好,我尝试了不同的学习率和迭代次数,结果正如我所期望的那样。 - Davors72

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接