前言
上一章我们学习了机器学习的应用领域、定义以及算法的分类,这一章来学习最简单的一个机器学习算法——单变量线性回归算法,它是一种监督学习的算法,而且输出值是连续变化的值,因此是一种“回归”算法。
一、符号定义
我们通过吴老师课堂上给出的练习题中的例子来学习单变量线性回归算法。
在进行监督学习算法的时候,我们首先要有一个训练集,这里的训练集就是人们统计的50名不同年龄的儿童和他们的身高,如下图所示。
这里我们开发机器学习算法的目的就是找出身高与年龄的大致对应关系,从而可以预测特定年龄小孩的大致身高。
因此,这个训练集中,我们的输入值是年龄,输出值是身高。通常来讲,我们称输入变量为特征(feature),用
x
x
x表示,输出值定义为
y
y
y,这里的
x
x
x就是小孩的年龄了,因为只有这么一个输入变量,因此这个问题是一个单变量的问题,如果有多个输入变量的话可以用
x
1
,
x
2
,
x
3
⋯
x_1,x_2,x_3\cdots
x1,x2,x3⋯表示。我们用
m
m
m表示训练样本的数量,在本例中,
m
m
m就是小孩的数量,也就是
m
=
50
m=50
m=50。我们用符号(
x
(
i
)
x^{(i)}
x(i),
y
(
i
)
y^{(i)}
y(i))表示第
i
i
i个小孩的年龄和身高,
i
=
1
,
2
,
3
⋯
50
i=1,2,3\cdots50
i=1,2,3⋯50。
接下来,为了找出x和y之间的关系,我们把x和y画成散点图,如下所示
从数据点的分布可以看出,身高和年龄之间大致呈线性关系,因此假设身高与年龄之间的函数关系是一个线性函数。这个假设函数(hypothesis)我们将其命名为
h
h
h,即
y
=
h
(
x
)
y=h(x)
y=h(x),机器学习算法的目的就是找出
h
(
x
)
h(x)
h(x)的具体表达式。在本例中,我们认为假设函数是一条直线,于是有
h
(
x
)
=
θ
0
+
θ
1
x
h(x)=\theta_{0}+\theta_{1}x
h(x)=θ0+θ1x,其中
θ
1
\theta_{1}
θ1表示直线的斜率,
θ
0
\theta_{0}
θ0表示直线在
x
x
x轴上的截距。
这样,我们的研究目标就转化为寻找最优的
θ
0
\theta_{0}
θ0和
θ
1
\theta_{1}
θ1。等学习了后面的内容后,我们就会发现,监督学习算法的最终目的通常就是寻找最优的参数。
接下来,为了让我们的表达方法更具有普适性和简洁性,我们把变量都用向量的方式来表示。我们定义函数
h
(
x
)
h(x)
h(x)的参数为向量
θ
=
\mathbf{\theta}=
θ=
[
θ
0
θ
1
]
\left[\begin{matrix}\theta_{0}\\\theta_{1}\end{matrix}\right]
[θ0θ1],同时,我们将
x
x
x也写成向量的形式,为了让
x
x
x和
θ
\mathbf{\theta}
θ对应起来,我们给它增加一个常数1,变成:
x
=
\mathbf{x}=
x=
[
1
x
]
\left[\begin{matrix}1\\x\end{matrix}\right]
[1x],由于只有一个输出量,因此
y
y
y的向量形式为
y
=
\mathbf{y}=
y=
[
y
]
\left[\begin{matrix}y\end{matrix}\right]
[y]。这样,我们样本集的输入值就可以写成一个
2
×
m
2×m
2×m的矩阵:
[
1
1
⋯
x
(
1
)
x
(
2
)
⋯
]
\left[\begin{matrix}1&1&\cdots\\x^{(1)}&x^{(2)}&\cdots\end{matrix}\right]
[1x(1)1x(2)⋯⋯],输出值就可以写成一个
1
×
m
1×m
1×m的矩阵
[
y
(
1
)
y
(
2
)
⋯
]
\left[\begin{matrix}y^{(1)}&y^{(2)}&\cdots\end{matrix}\right]
[y(1)y(2)⋯]。这样一来我们的假设函数就可以用向量表示为
h
=
θ
T
×
x
h=\mathbf{\theta^{T}}\times\mathbf{x}
h=θT×x,这里的
×
\times
×是矩阵的乘积,我们也可以省略这个乘号,写成
h
=
θ
T
x
h=\mathbf{\theta^{T}}\mathbf{x}
h=θTx。这种表达方式对后面的多变量回归乃至逻辑回归都是适用的,同时也可以方便地转化为Matlab代码。因此,以后我们就用不带下标的字母
θ
\mathbf{\theta}
θ表示参数向量,用
θ
0
,
θ
1
⋯
\theta_{0},\theta_{1}\cdots
θ0,θ1⋯表示向量中的元素;用
x
\mathbf{x}
x表示输入向量,
x
j
x_j
xj表示第
j
j
j个输入变量,
x
j
(
i
)
x^{(i)}_j
xj(i)表示第
i
i
i个样本的第
j
j
j个输入变量;同样地,
y
\mathbf{y}
y表示输出向量,
y
j
y_j
yj表示第
j
j
j个输出量,
y
j
(
i
)
y^{(i)}_j
yj(i)表示第
i
i
i个样本的第
j
j
j个输出量
至此,我们就完成了单变量线性回归的准备工作了,下面开始考虑如何寻找最优的参数
θ
\mathbf{\theta}
θ。
二、代价函数
为了寻找最优的参数
θ
\mathbf{\theta}
θ,我们首先要确定一个标准,即什么样的参数是“优”的。直观地理解,对于每一个样本,我们的函数值
h
(
x
(
i
)
)
h(x^{(i)})
h(x(i))与实际的输出值
y
(
i
)
y^{(i)}
y(i)符合得越好,我们认为对应的参数越“好”。我们可以使用方差
[
h
(
x
(
i
)
)
−
y
(
i
)
]
2
[h(x^{(i)})-y^{(i)}]^{2}
[h(x(i))−y(i)]2来定量地描述这两个值的符合程度,方差越大,说明这两个值的差异越大,方差为零时这两个值完全相同。
综合考虑所有的样本,我们可以用所有样本的方差之和
J
=
∑
i
=
1
m
[
h
(
x
(
i
)
)
−
y
(
i
)
]
2
J=\sum\limits_{i=1}^m{[h(x^{(i)})-y^{(i)}]^{2}}
J=i=1∑m[h(x(i))−y(i)]2来表征函数
h
h
h与样本集整体的符合程度,
J
J
J越小,符合程度越高,我们称函数
J
J
J为函数
h
h
h的代价函数。
从函数
J
J
J的定义可以看出它的大小只与参数
θ
\mathbf{\theta}
θ有关,即
J
=
J
(
θ
)
J=J(\mathbf{\theta})
J=J(θ)。这样一来,我们的目标就再一次转化为寻找使代价函数值
J
J
J最小的参数
θ
\mathbf{\theta}
θ。
三、梯度下降算法
那么,如何寻找
J
J
J最小时的参数
θ
\mathbf{\theta}
θ呢?这就要用到梯度下降算法了。
梯度下降算法的思想很简单,
J
(
θ
0
,
θ
1
)
J(\theta_{0},\theta_{1})
J(θ0,θ1)实际上是一个关于
θ
0
和
θ
1
\theta_{0}和\theta_{1}
θ0和θ1的二维曲面,每一对
(
θ
0
,
θ
1
)
(\theta_{0},\theta_{1})
(θ0,θ1)都对应于曲面上的一个点,如下图所示:
我们先任意初始化一个
θ
\mathbf{\theta}
θ值,然后找出该点处
J
J
J下降最快的方向,将
θ
\mathbf{\theta}
θ向这个方向移动一小步,然后再重新寻找
J
J
J下降最快的方向,再向新的方向移动一小步,如此循环,直到
J
J
J达到最小值,这个过程中
θ
\mathbf{\theta}
θ的移动路径如上图中黑色四芒星所示。
而函数
J
J
J下降最快的方向其实就是函数
J
(
θ
0
,
θ
1
)
J(\theta_{0},\theta_{1})
J(θ0,θ1)的梯度的反方向。根据梯度的定义,函数
J
J
J的梯度为
∇
J
=
∂
J
∂
θ
0
θ
0
→
+
∂
J
∂
θ
1
θ
1
→
\nabla{J}=\frac{\partial{J}}{\partial{\theta_{0}}}\overrightarrow{\theta_{0}}+\frac{\partial{J}}{\partial{\theta_{1}}}\overrightarrow{\theta_{1}}
∇J=∂θ0∂Jθ0+∂θ1∂Jθ1,其中,
θ
0
→
\overrightarrow{\theta_{0}}
θ0和
θ
1
→
\overrightarrow{\theta_{1}}
θ1分别代表沿
θ
0
\theta_{0}
θ0和
θ
1
\theta_{1}
θ1正方向的单位向量,为了使
θ
\mathbf{\theta}
θ往
J
J
J下降最快的方向移动,应该使
θ
0
\theta_{0}
θ0和
θ
1
\theta_{1}
θ1分别减去
α
∗
∂
J
∂
θ
0
\alpha*\frac{\partial{J}}{\partial{\theta_{0}}}
α∗∂θ0∂J和
α
∗
∂
J
∂
θ
1
\alpha*\frac{\partial{J}}{\partial{\theta_{1}}}
α∗∂θ1∂J,其中
α
\alpha
α(>0)代表移动的步长。于是,梯度下降算法的计算过程如下所示:
Step1、初始化
θ
\mathbf{\theta}
θ:例如令
θ
0
=
θ
1
=
0
\theta_{0}=\theta_{1}=0
θ0=θ1=0;
Step2、计算当前
θ
\mathbf{\theta}
θ值对应的
J
J
J的梯度值:
∂
J
∂
θ
0
\frac{\partial{J}}{\partial{\theta_{0}}}
∂θ0∂J,
∂
J
∂
θ
1
\frac{\partial{J}}{\partial{\theta_{1}}}
∂θ1∂J;
Step3、更新
θ
\mathbf{\theta}
θ:
θ
0
:
=
θ
0
−
α
∗
∂
J
∂
θ
0
\theta_{0}:=\theta_{0}-\alpha*\frac{\partial{J}}{\partial{\theta_{0}}}
θ0:=θ0−α∗∂θ0∂J;
θ
1
:
=
θ
1
−
α
∗
∂
J
∂
θ
1
\theta_{1}:=\theta_{1}-\alpha*\frac{\partial{J}}{\partial{\theta_{1}}}
θ1:=θ1−α∗∂θ1∂J;
Step4、判断是否结束循环,若为否则回到Step2。
从以上过程可以看出,每一步的移动步长是由该点处
J
J
J的梯度大小和
α
\alpha
α共同决定的。在
α
\alpha
α大小不变的情况下,当
J
J
J离最小值较远时,对应的梯度值较大,
θ
\mathbf{\theta}
θ移动的较快;
J
J
J越接近最小值,
θ
\mathbf{\theta}
θ移动步长越小,最终逐渐收敛到
J
J
J最小处。
而第二步中偏导数
∂
J
∂
θ
0
\frac{\partial{J}}{\partial{\theta_{0}}}
∂θ0∂J,
∂
J
∂
θ
1
\frac{\partial{J}}{\partial{\theta_{1}}}
∂θ1∂J的值可以将
h
(
x
)
=
θ
0
+
θ
1
x
h(x)=\theta_{0}+\theta_{1}x
h(x)=θ0+θ1x代入
J
=
∑
i
=
1
m
[
h
(
x
(
i
)
)
−
y
(
i
)
]
2
J=\sum\limits_{i=1}^m{[h(x^{(i)})-y^{(i)}]^{2}}
J=i=1∑m[h(x(i))−y(i)]2中计算得出。根据偏导数的运算规则,可以得出:
∂
J
∂
θ
0
=
2
∑
i
=
1
m
[
h
(
x
(
i
)
)
−
y
(
i
)
]
\frac{\partial{J}}{\partial{\theta_{0}}}=2\sum\limits_{i=1}^m{[h(x^{(i)})-y^{(i)}]}
∂θ0∂J=2i=1∑m[h(x(i))−y(i)];
∂
J
∂
θ
1
=
2
∑
i
=
1
m
x
(
i
)
[
h
(
x
(
i
)
)
−
y
(
i
)
]
\frac{\partial{J}}{\partial{\theta_{1}}}=2\sum\limits_{i=1}^m{x^{(i)}[h(x^{(i)})-y^{(i)}]}
∂θ1∂J=2i=1∑mx(i)[h(x(i))−y(i)],实际执行时,会在前面乘上一个系数
1
2
m
\frac{1}{2m}
2m1,并不会影响最终的优化结果。
四、Matlab算法实现
接下来,用Matlab来实现以上过程,代码如下:
%导入训练集;
x=load('ex2x.dat');
y=load('ex2y.dat');
%画出训练集的散点图
figure,
subplot(121),
plot(x,y,'o');
ylabel('Height in meters');
xlabel('Age in years');
hold on;
%step1、初始化相关参数,注意:这里我们将x和y写成了m*2和m*1的向量形
%式,这样一来h=x*theta,与文中所述有所不同。
m=length(y);
x=[ones(m,1),x];
n=2;
theta=zeros(n,1);
alpha=0.07;
%开始单变量梯度下降循环,并实时显示J的变化
subplot(122);
for step=1:800
%先计算h-y的值,h-y是一个m×1的向量
h_y=x*theta-y;
%step2、3 计算梯度并更新θ的值,通过向量乘法实现样本的求和。
theta(1)=theta(1)-alpha/m*(h_y'*x(:,1));
theta(2)=theta(2)-alpha/m*(h_y'*x(:,2));
%计算并显示代价函数值
J=sum(h_y.^2)/m/2;
if mod(step,10)==9
plot(step,log10(J),'o');
axis([0,800,-inf,inf]);
ylabel('log(J)');
xlabel('Step');
hold on;
drawnow;
end
end
%显示最终的优化结果
subplot(121),
tempx=linspace(2,8,100);
tempy=theta(1)+tempx*theta(2);
plot(tempx,tempy);
%准备数据,画出代价函数曲面
Jval=zeros(100,100);
theta0=linspace(-3,3,100);
theta1=linspace(-1,1,100);
for xs=1:length(theta0)
for ys=1:length(theta1)
theta=[theta0(xs);theta1(ys)];
h_y=x*theta-y;
Jval(xs,ys)=sum(h_y.^2)/m/2;
end
end
figure,
surf(theta0,theta1,Jval');
axis([-3,3,-1,1]);
xlabel('\theta_0'); ylabel('\theta_1')
程序的运行结果如下图所示,红色的直线为最终的优化结果。
五、结语
以上Matlab代码中用到的数据和代码文件可以在这里下载,提取码m806
这一章主要介绍了单变量线性回归算法的原理和实现过程,定义了相关的符号和函数。随着后续的学习我们会发现,本章所学习的算法过程也是其它监督学习算法的大致实现过程,即定义特征量和假设函数、推导代价函数和偏导数、应用梯度下降算法寻找最优参数。
下一章介绍多变量的线性回归算法的原理和实现方法。