谈到线性回归,我们脑海中总是会浮现出这样一幅画面:
没错,这个是线性回归最简单的情形。横轴表示自变量x,纵轴表示应变量y,我们试图找到一个直线,使得图上的所有点,到直线的垂直距离的平方和最小。还是用数学表示来得准确些。我们假设直线的方程为。每个点在直线上的值就是。因此,所有点的垂直距离平方和可以表示为:
这里的是列向量,也是列向量。上面的公式不仅适用于二维的情形,对于N维的情况,上式也同样成立。此时有和成立。
线性回归的目标是找出一个向量使得的值最小。你既可以把看成是多元函数求极值的问题,这样我们就可以采用梯度下降的方法来求解线性回归问题了。同样地,如果把向量简单地看成是一个变量,通过矩阵的偏微分运算,对对求偏导,令结果等于0,则可以直接解出最优的,这种方法也叫做正规方程法。我们详细讨论下这两种不同的解法。
正规方程法
首先,我们把成本函数改写成矩阵的形式:
接下来,我们把平方展开,得到:
然后,我们对向量求偏导,这里涉及到了一些矩阵微分的一些运算法则,以后有时间我再研究下矩阵微分方面的知识。现在我们简单地当成是普通变量的求导,于是我们得到如下结果:
令,我们得到,其中,。把它们的定义代入上式得到:。当且仅当的逆存在时,我们得到的解为:
上式中的也称为伪逆,记作。最小二乘法只有在的情况下才适用。对于或者的情况,可能是奇异矩阵。这样线性回归的结果就会出现过拟合。岭回归可以解决传统线性回归出现的问题,准备在之后的博客中讨论下岭回归算法。
我们可以用python和numpy快速地实现线性回归的正规方程的算法。代码如下所示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# -*- coding: utf-8 -*-
import numpy as np
import pylab as plt
def psudoInverse(X):
inv=np.linalg.inv(np.dot(X.T,X))
psudoInv=np.dot(inv,X.T)
return psudoInv
def linearRregression(theta,X,Y):
psudoInv=psudoInverse(X)
return np.dot(psudoInv,Y)
#prepare data
x=np.arange(1,11,0.5)
theta0=2
theta1=3
y=theta0+theta1*x+np.random.randn(len(x))
#train linear regression
bias=np.ones(len(x))
X=np.vstack((bias,x)).T
theta=np.zeros(2)
theta=linearRregression(theta,X,y)
plt.scatter(x,y)
plt.plot(x,theta[0]+theta[1]*x)
plt.xlim(0,11)
plt.show()
在命令行下运行上面的程序,得到如下图的结果。
梯度下降法
如果把看成是多变量的函数时,我们可以采用梯度下降算法计算最优的参数向量,使得成本函数最小。梯度下降算法的思路很直观,每次更新我们都沿着每个维度的梯度下降的方向更新参数,直到算法收敛。每个参数的更新规则如下:
需要注意的是,我们需要同时更新。也就是说,在每次更新时,必须用上一次迭代时得到的,当所有的在这次迭代更新完成后,得到新的向量,并用于下一次的迭代,直到算法收敛。
我们同样用python简单地实现了下梯度下降算法。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import numpy as np
import pylab as plt
def hypothesis(theta,X):
return np.dot(X,theta)
def costfunction(theta,X,y):
hyp=hypothesis(theta,X)
m=X.shape[0]
cost=sum((hyp-y)**2)/(2*m)
return cost
def gradientDescent(theta,X,y,alpha=0.02,iteration=100):
thetatemp=np.zeros_like(theta)
m=X.shape[0]
for i in range(iteration):
cost=costfunction(theta,X,y)
print("iter %d: %f" %(i,cost))
diff=hypothesis(theta,X)-y
for i in range(len(theta)):
derivative=sum(diff*X[:,i])/m
#print derivative
thetatemp[i]=theta[i]-alpha*derivative
theta=thetatemp
return theta
# prepare data
x=np.arange(1,11,0.5)
theta0=3
theta1=2
y=theta0+theta1*x+np.random.randn(len(x))
# train a linear odel
bias=np.ones(len(x))
X=np.vstack((bias,x)).T
theta=np.zeros(2)
theta=gradientDescent(theta,X,y,iteration=200)
plt.scatter(x,y)
plt.plot(x,theta[0]+theta[1]*x)
plt.xlim(0,11)
plt.show()
梯度下降算法中学习率的选择非常重要,通常太大的学习率会导致算法不收敛,使得成本函数越来越大。而太小的学习率则导致收敛比较慢。我们一般根据经验和实际尝试来选择合适的。
最后,我们在terminal上运行上面的程序:python linearRegression.py,得到如下图的结果。
方法对比
对比维度 | 正规方程法 | 梯度下降法 |
---|---|---|
参数调优 | 没有参数需要调优 | 需要调整学习率 |
增量训练 | 不能增量训练 | 批量梯度下降可以从前一次训练的参数作为初始值,进行增量训练 |
训练样本维度 | 当特征维度大于10万时,矩阵求逆的运行非常慢 | 可以适用于特征维度很大的训练 |
迭代 | 一次计算即可 | 需要多次迭代 |
最后,当样本特征维数大于样本数时,或者当样本中存在很多线性相关的样本时,可能不存在。此时正规方程法就失效了。但是我们仍然可以用梯度下降算法来计算。
参考
- 机器学习的基石,第九讲。
https://class.coursera.org/ntumlone-002/lecture
。