我用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); //一些可视化代码 }}
现在的问题是,如果学习率大于大约0.000001,梯度下降后的成本函数值会高于之前。也就是说,算法在反向工作。线很快就形成了通过原点的直线,但实际上需要数百万次迭代才能达到合理拟合的线。
当学习率为0.01时,经过六次迭代后的输出是:(其中difference是costAfter-costBefore)
Cost before 102901.945312, cost after 517539430400.000000, difference 517539332096.000000Cost before 517539430400.000000, cost after 3131945127824588800.000000, difference 3131944578068774912.000000Cost before 3131945127824588800.000000, cost after 18953312418560698826620928.000000, difference 18953308959796185006080000.000000Cost before 18953312418560698826620928.000000, cost after 114697949347691988409089177681920.000000, difference 114697930004878874575022382383104.000000Cost before 114697949347691988409089177681920.000000, cost after inf, difference infCost before inf, cost after inf, difference nan
在这个例子中,theta被设置为零,学习率设置为0.000001,迭代次数为800万次!可视化代码每10万次迭代后更新一次图表。
创建数据点的函数如下:
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) )); }}
简而言之,如果我的学习率过高,梯度下降算法实际上会反向运行并趋向于无穷大,而如果降低到它实际收敛到最小值的点,所需的迭代次数就高得不可接受。
我在核心算法中是否遗漏了什么或犯了错误?
回答:
看起来一切都在按预期运行,但您在选择合理的学习率方面遇到了问题。这并不是一个完全简单的问题,有许多方法,从预定义的逐步降低学习率的计划(例如参见这篇论文)到自适应方法,如AdaGrad或AdaDelta。
对于您使用固定学习率的普通实现,您应该通过在将数据输入梯度下降算法之前将其标准化为零均值和单位标准差来简化您的工作。这样您就可以更容易地推理学习率。然后您可以相应地重新调整您的预测。