1.假设
一个房屋的出售价格y
(千元/平方米)由:
(1)房屋距市中心的距离(km) x1
(2)房屋的大小(平方米)x2
(3)房屋所拥有的房间数x3
(4)房屋的已使用时间x4
这四个因素决定,那么根据训练集确定五个变量θ1
θ2
θ3
θ4
θ0
,使得任一房屋的价格可由 y= θ0 + θ1*x1 + θ2*x2 + θ3*x3 + θ4*x4
预测。
2.分析:
要预测房屋价格,实际上是要对五个变量分别使用梯度下降,来找到使得误差函数J(θ)
最小的θ1
θ2
θ3
θ4
θ0
的组合。
这篇文章承接自我的上一篇有关梯度下降的文章:[初识:梯度下降算法 (Gradient Descent)]
在这篇博客中我通过对 x1
x2
(在这里应当对应的是 θ0
θ1
)两个变量进行梯度下降从而得到散点的最优直线拟合。在这里只是问题稍微复杂了一些,需要对五个变量进行梯度下降的处理,来得到最优的房价预测方程。
那么只需要根据这五个式子,就可以利用梯度下降算法得到最优的拟合方程。
然而,我在用代码实现了算法之后,却发现θ1
θ2
θ3
θ4
θ0
无法收敛,每次都运行到我指定的for循环最大次数后才退出。其时间消耗少则五分钟,多更至十多分钟,有时甚至根本无法得到结果(结果显示 not a number)。
但是,经过长时间的探索,终于发现问题竟出在了α
的取值上。α
又称学习速率,决定了每一次θ1
θ2
θ3
θ4
θ0
改变幅度的大小,受之前那个拟合散点程序的影响,我这次开始的时候将α
设置的较大(0.1或0.01),或许是因此导致了θ1
θ2
θ3
θ4
θ0
中一部分变量的发散,从而导致循环跳出条件无法满足。
将α
值调为0.0001后,五个变量得以迅速收敛,循环次数也由千万级别骤降至四百余次。
可以看到,过小的α
有时会导致计算次数增多,增加消耗,有时却能拯救一个无法收敛的程序。可见,对于不同的训练集,α
的取值相当灵活。
3.代码实现:
原理与上篇博客中的拟合散点大致相同
(1)给出训练集,学习速率,及有关变量
由于训练集数据过少,而且数据是自己编的,结论应该和实际没什么关系 :(
int Judge ; //用于判断收敛
//自己脑补了15组数字 :)
double Train_set_x1[15] = {0.1, 0.7, 3, 5, 10, 1, 1, 1, 1, 1.1, 2, 2, 2, 2, 2};
double Train_set_x2[15] = {1, 1, 1, 1.1, 1, 2.1, 2.1, 2, 1.5, 1.52, 3, 4, 5, 7};
double Train_set_x3[15] = {2, 2, 2, 2, 2, 2, 2, 3, 4, 3, 2, 2, 2, 2, 2 };
double Train_set_x4[15] = {1, 1, 1, 1, 1, 2, 1, 0.1, 0.1, 51, 1, 1, 1, 1, 1};
double Train_set_y[15] = {7, 5, 4, 3.9, 3.2, 4.5, 5.2, 5.9, 5.8, 4.1, 5.0, 5.5, 5.7, 5.9, 6.5};
int j;
double m = 15; //共十五组数据
double alpha = 0.0001;
double sum_0; //用于进行求和运算
double sum_1;
double sum_2;
double sum_3;
double sum_4;
double theta_0 = 1; //θ用theta代替
double theta_1 = 1;
double theta_2 = 1;
double theta_3 = 1;
double theta_4 = 1;
(2)进行梯度下降:
for (i = 0; i < 99999999; i++)
{
sum_0 = 0; //每次循环前初始化为0,用于计算和式
sum_1 = 0;
sum_2 = 0;
sum_3 = 0;
sum_4 = 0;
for (j = 0; j <= 14; j++) // for theta_0 先把和式全部计算出来
{
sum_0 += (theta_0 + theta_1*Train_set_x1[j] + theta_2*Train_set_x2[j] + theta_3*Train_set_x3[j] + theta_4 * Train_set_x4[j] - Train_set_y[j]);
}
for (k = 0; k <= 14; k++) // for theta_1
{
sum_1 += (theta_0 + theta_1*Train_set_x1[k] + theta_2*Train_set_x2[k] + theta_3*Train_set_x3[k] + theta_4*Train_set_x4[k] - Train_set_y[k])*Train_set_x1[k];
}
for (t = 0; t <= 14; t++) // for theta_2
{
sum_2 += (theta_0 + theta_1*Train_set_x1[t] + theta_2*Train_set_x2[t] + theta_3*Train_set_x3[t] + theta_4*Train_set_x4[t] - Train_set_y[t])*Train_set_x2[t];
}
for (n = 0; n <= 14; n++) // for theta_3
{
sum_3 += (theta_0 + theta_1*Train_set_x1[n] + theta_2*Train_set_x2[n] + theta_3*Train_set_x3[n] + theta_4*Train_set_x4[n] - Train_set_y[n])*Train_set_x3[n];
}
for (g = 0; g <= 14; g++) // for theta_4
{
sum_4 += (theta_0 + theta_1*Train_set_x1[g] + theta_2*Train_set_x2[g] + theta_3*Train_set_x3[g] + theta_4*Train_set_x4[g] - Train_set_y[g])*Train_set_x4[g];
}
temp_0 = theta_0 - alpha*(1 / m)*sum_0; //梯度下降算法核心
temp_1 = theta_1 - alpha*(1 / m)*sum_1;
temp_2 = theta_2 - alpha*(1 / m)*sum_2;
temp_3 = theta_3 - alpha*(1 / m)*sum_3;
temp_4 = theta_4 - alpha*(1 / m)*sum_4;
Judge = 0;
//判断五个变量是否都已经收敛
if (theta_0 == temp_0) Judge++;
if (theta_1 == temp_1) Judge++;
if (theta_2 == temp_2) Judge++;
if (theta_3 == temp_3) Judge++;
if (theta_4 == temp_4) Judge++;
if (Judge == 5) break;
theta_0 = temp_0; //更新五个变量的值
theta_1 = temp_1;
theta_2 = temp_2;
theta_3 = temp_3;
theta_4 = temp_4;
}
数据很大程度上影响了拟合的效果,不过就我的这些数据至少已经能展示出一般的规律了 :)
比如:距离市中心的距离与房价成反比; 房屋使用时间与房价成反比等等。
设想若数据量足够大,足够真实,那么应当可以得到一个很具有现实意义的预测函数。
与这个问题同理,还有许多问题都可以被梯度下降算法预测,只是未知量的个数问题。