前言
我正在学习吴恩达的机器学习课程,想在学习的过程中写一写博客。一是相当于做了笔记,总结一下机器学习的知识;二是在总结的过程中也会发现很多细节的问题可以让自己收获更多;三是分享到网上,或许对别人有所帮助,也或许我的错误之处被别人发现,也可以促使我改正。
一、线性回归模型
提供一定的数据作为训练集,训练集中有m个训练数据,假设训练集中每个样本为
(
x
,
y
)
\left( x,y \right)
(x,y),其中
x
=
(
x
1
,
x
2
,
⋯
,
x
n
)
x=\left( x_1,x_2,\cdots ,x_n \right)
x=(x1,x2,⋯,xn)为特征向量,
y
y
y为输出变量。我们将第
i
i
i个样本记为
(
x
(
i
)
,
y
(
i
)
)
\left( x^{(i)},y^{(i)} \right)
(x(i),y(i)),其中
x
(
i
)
=
(
x
1
(
i
)
,
x
2
(
i
)
,
⋯
,
x
n
(
i
)
)
x^{\left( i \right)}=\left( x_{1}^{\left( i \right)},x_{2}^{\left( i \right)},\cdots ,x_{n}^{\left( i \right)} \right)
x(i)=(x1(i),x2(i),⋯,xn(i))。我们记:
X
=
(
1
x
1
(
1
)
⋯
x
n
(
1
)
1
x
1
(
2
)
⋯
x
n
(
2
)
⋮
⋮
⋮
1
x
1
(
m
)
⋯
x
n
(
m
)
)
=
(
x
0
(
1
)
x
1
(
1
)
⋯
x
n
(
1
)
x
0
(
2
)
x
1
(
2
)
⋯
x
n
(
2
)
⋮
⋮
⋮
x
0
(
m
)
x
1
(
m
)
⋯
x
n
(
m
)
)
y
=
(
y
(
1
)
y
(
2
)
⋮
y
(
m
)
)
X=\left( \begin{matrix} 1& x_{1}^{\left( 1 \right)}& \cdots& x_{n}^{\left( 1 \right)}\\ 1& x_{1}^{\left( 2 \right)}& \cdots& x_{n}^{\left( 2 \right)}\\ \vdots& \vdots& & \vdots\\ 1& x_{1}^{\left( m \right)}& \cdots& x_{n}^{\left( m \right)}\\ \end{matrix} \right) =\left( \begin{matrix} x_{0}^{\left( 1 \right)}& x_{1}^{\left( 1 \right)}& \cdots& x_{n}^{\left( 1 \right)}\\ x_{0}^{\left( 2 \right)}& x_{1}^{\left( 2 \right)}& \cdots& x_{n}^{\left( 2 \right)}\\ \vdots& \vdots& & \vdots\\ x_{0}^{\left( m \right)}& x_{1}^{\left( m \right)}& \cdots& x_{n}^{\left( m \right)}\\ \end{matrix} \right) \\ \,\,y=\left( \begin{array}{c} y^{\left( 1 \right)}\\ y^{\left( 2 \right)}\\ \vdots\\ y^{\left( m \right)}\\ \end{array} \right)
X=⎝⎜⎜⎜⎜⎛11⋮1x1(1)x1(2)⋮x1(m)⋯⋯⋯xn(1)xn(2)⋮xn(m)⎠⎟⎟⎟⎟⎞=⎝⎜⎜⎜⎜⎛x0(1)x0(2)⋮x0(m)x1(1)x1(2)⋮x1(m)⋯⋯⋯xn(1)xn(2)⋮xn(m)⎠⎟⎟⎟⎟⎞y=⎝⎜⎜⎜⎛y(1)y(2)⋮y(m)⎠⎟⎟⎟⎞
(注意上文中两个
y
y
y的含义,虽然符号相同,但是含义不同)
为了下面的公式表示简单,我们重新定义 x = ( 1 , x 1 , x 2 , ⋯ , x m ) x=\left( 1,x_1,x_2,\cdots ,x_m \right) x=(1,x1,x2,⋯,xm)。
我们用这些数据来训练一个模型,用训练好的模型去预测新的数据。监督学习就是从训练集中“学习”,或称为“训练”,与人的学习类似,通过不断地学习,就可以知道这个特定问题的一些规律,然后就可以根据给定的特征向量 x x x预测 y y y。
假设特征向量和预测变量大致呈现出一种线性关系,即:
∃
θ
=
(
θ
0
,
θ
1
,
⋯
,
θ
n
)
T
∈
R
n
+
1
s
.
t
.
θ
T
x
≈
y
\exists \theta =\left( \theta _0,\theta _1,\cdots ,\theta _n \right) ^T\in \boldsymbol{R}^{\boldsymbol{n}+1}\,\, s.t. \ \ \ \theta ^Tx\approx y
∃θ=(θ0,θ1,⋯,θn)T∈Rn+1s.t. θTx≈y
这里可以令
h
θ
(
x
)
=
θ
T
x
h_{\theta}\left( x \right) =\theta ^Tx
hθ(x)=θTx,方便表示,同时也有利于和逻辑回归算法对比。
实际中很少有绝对的线性关系,所以上式使用约等于。但是当误差很小时,也能满足很多需求,我们的线性回归算法在学习时就是要想办法缩小以下误差:
∑
i
=
1
m
(
h
θ
(
x
(
i
)
)
−
y
(
i
)
)
2
\sum_{i=1}^m{\left( h_{\theta}\left( x^{\left( i \right)} \right) -y^{\left( i \right)} \right) ^2}
i=1∑m(hθ(x(i))−y(i))2
为了导函数比较简单,我们常常使用下式:
J
(
θ
)
=
1
2
∑
i
=
1
m
(
h
θ
(
x
(
i
)
)
−
y
(
i
)
)
2
J\left( \theta \right) =\frac{1}{2}\sum_{i=1}^m{\left( h_{\theta}\left( x^{\left( i \right)} \right) -y^{\left( i \right)} \right) ^2}
J(θ)=21i=1∑m(hθ(x(i))−y(i))2
我们把上式
J
(
θ
)
J(\theta)
J(θ)称为损失函数,使用矩阵的方式来表示就是:
J
(
θ
)
=
1
2
(
X
θ
−
y
)
T
(
X
θ
−
y
)
=
1
2
∥
X
θ
−
y
∥
2
2
J\left( \theta \right) =\frac{1}{2}\left( X\theta -y \right) ^T\left( X\theta -y \right) =\frac{1}{2}\left\| X\theta -y \right\| _{2}^{2}
J(θ)=21(Xθ−y)T(Xθ−y)=21∥Xθ−y∥22
我们的目标就是最小化这个损失函数,寻找使得损失函数最小的
θ
\theta
θ。
二、求解方法
1.正规方程法
损失函数是一个凸函数,直接对函数关于
θ
\theta
θ求导可得:
∂
J
(
θ
)
∂
θ
=
X
T
(
X
θ
−
y
)
\frac{\partial J\left( \theta \right)}{\partial \theta}=X^T\left( X\theta -y \right)
∂θ∂J(θ)=XT(Xθ−y)
令上述导数等于向量
0
0
0(
n
+
1
n+1
n+1维列向量):
∂
J
(
θ
)
∂
θ
=
X
T
(
X
θ
−
y
)
=
X
T
X
θ
−
X
T
y
=
0
\frac{\partial J\left( \theta \right)}{\partial \theta}=X^T\left( X\theta -y \right) =X^TX\theta -X^Ty=0
∂θ∂J(θ)=XT(Xθ−y)=XTXθ−XTy=0得出:
θ
=
(
X
T
X
)
−
1
X
T
y
\theta =\left( X^TX \right) ^{-1}X^Ty
θ=(XTX)−1XTy
这样就可以得出全局最优解了,这叫做正规方程解法。或许在使用这个公式时会遇到
X
T
X
X^TX
XTX不可逆的情况,这个时候通常是选择的特征中有线性相关性比较强的特征,这时可以尝试去寻找线性相关性强的特征,删掉一些特征大概率会解决这个问题。当然也可以使用
X
T
X
X^TX
XTX的伪逆来代替的逆,这样得到的答案也是可靠的。
这里顺便再计算一下偏导数: ∂ J ( θ ) ∂ θ k = ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x k ( i ) \frac{\partial J\left( \theta \right)}{\partial \theta _k}=\sum_{i=1}^m{\left( h_{\theta}\left( x^{\left( i \right)} \right) -y^{\left( i \right)} \right) x_{k}^{\left( i \right)}} ∂θk∂J(θ)=i=1∑m(hθ(x(i))−y(i))xk(i)
2.梯度下降算法
对应于正规方程的解法,还可以使用迭代解法去求解全局最优解。使用梯度下降法也可以寻找全局最优解。梯度的方向是函数在该点上升最快的方向,只要自变量向梯度的反方向移动,函数就会下降。该问题的损失函数
J
(
θ
)
J\left( \theta \right)
J(θ)的梯度如下:
∇
θ
J
(
θ
)
=
∂
J
(
θ
)
∂
θ
=
X
T
(
X
θ
−
y
)
\nabla _{\theta}J\left( \theta \right) =\frac{\partial J\left( \theta \right)}{\partial \theta}=X^T\left( X\theta -y \right)
∇θJ(θ)=∂θ∂J(θ)=XT(Xθ−y)
该问题的梯度下降法步骤如下:
- 初始化参数 θ \theta θ ;
- 不断执行下列操作: θ ⟵ θ − α X T ( X θ − y ) 或 θ k ⟵ θ k − α ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x k ( i ) ( k = 0 , 1 , ⋯ , n ) \theta \longleftarrow \theta -\alpha X^T\left( X\theta -y \right) \,\, \text{或} \\ \theta _k\longleftarrow \theta _k-\alpha \sum_{i=1}^m{\left( h_{\theta}\left( x^{\left( i \right)} \right) -y^{\left( i \right)} \right) x_{k}^{\left( i \right)}}\,\,\left( k=0,1,\cdots ,n \right) θ⟵θ−αXT(Xθ−y)或θk⟵θk−αi=1∑m(hθ(x(i))−y(i))xk(i)(k=0,1,⋯,n)直到 θ \theta θ的变化很小时停止。
这就是梯度下降法的大致步骤,步骤中 α \alpha α( α > 0 \alpha>0 α>0)称为学习率,控制 θ \theta θ沿着梯度的反方向移动的步长。学习率的取值要恰当,当学习率 α \alpha α太大,移动的步长就太大,最终可能导致 θ \theta θ在全局最优解附近反复横跳,甚至远离全局最优解;当学习率 α \alpha α太小,迭代次数通常也会增大,可能会导致收敛速度太慢。
梯度下降法看起来比正规方程法要复杂一些,因为正规方程只需要计算一下表达式就可以直接求出最优的 θ \theta θ,用代码实现正规方程算法也只需要一条计算的语句就可以求出最优的 θ \theta θ,而梯度下降算法需要考虑到学习率的问题,还要不断迭代,用代码实现起来也比正规方程稍微复杂。不过有时候梯度下降算法却有着优势。当样本的特征数目很大时,正规方程的求逆运算就会耗费较多的时间,这时候可能梯度下降训练速度比正规方程更快。通常特征数目大于10000时,正规方程的运算代价就会比较高,这时选择梯度下降较好。
上面的梯度下降算法在每次迭代时使用训练集中所有的数据进行计算,我们把它称为批量梯度下降法,但是当训练集样本数很多时,批量梯度下降法耗费的时间也会很多。这时我们可以对训练样本单独做梯度下降,这样会减少训练时间,我们把这样的方法称为随机梯度下降法。
一个样本
(
x
,
y
)
\left( x,y \right)
(x,y)的损失函数为:
j
(
θ
)
=
1
2
(
h
θ
(
x
)
−
y
)
2
j\left( \theta \right) =\frac{1}{2}\left( h_{\theta}\left( x \right) -y \right) ^2
j(θ)=21(hθ(x)−y)2梯度为:
∇
θ
j
(
θ
)
=
(
h
θ
(
x
)
−
y
)
x
\nabla _{\theta}j\left( \theta \right) =\left( h_{\theta}\left( x \right) -y \right) x
∇θj(θ)=(hθ(x)−y)x
因此该问题的随机梯度下降法步骤如下:
- 将训练集中的样本顺序打乱;
- 初始化参数 θ \theta θ ;
- 对于 i = 1 , 2 , ⋯ , m i=1,2,\cdots ,m i=1,2,⋯,m ,依次进行如下计算: θ ⟵ θ − α ( h θ ( x ( i ) ) − y ( i ) ) x ( i ) \theta \longleftarrow \theta -\alpha \left( h_{\theta}\left( x^{\left( i \right)} \right) -y^{\left( i \right)} \right) x^{\left( i \right)} θ⟵θ−α(hθ(x(i))−y(i))x(i)
- 重复上一步骤几次(通常 1 ∼ 10 1\sim10 1∼10 次)。
我之前没有想通随机梯度下降法和批量梯度下降法的区别,我现在想通了,是一个微小的问题被我察觉了。
我觉得下面这样的算法也是可以提高效率的,下面的算法和上面的算法主要区别在于:上面的算法一定可以用到所有样本点的信息,而下面的算法并不一定会用到所有样本点的信息:
-
初始化参数 θ \theta θ ;
-
随机选择一个样本点 ( x ( k ) , y ( k ) ) \left( x^{\left( k \right)},y^{\left( k \right)} \right) (x(k),y(k)) ,执行下列操作:
θ ⟵ θ − α ( h θ ( x ( k ) ) − y ( k ) ) x ( k ) \theta \longleftarrow \theta -\alpha \left( h_{\theta}\left( x^{\left( k \right)} \right) -y^{\left( k \right)} \right) x^{\left( k \right)} θ⟵θ−α(hθ(x(k))−y(k))x(k) -
重复上一步骤多次。
批量梯度下降法一定可以达到全局最优解,但是随机梯度下降法通常不能找到全局最优解,不过通常与全局最优解也比较接近,可能也会取得不错的效果。
三、概率解释
将线性回归算法的训练过程转化为了优化目标函数的过程,这样做还可以运用概率统计方面的知识去解释。
我们定义符号: ( x , y ) \left( x,y \right) (x,y)是一个随机变量,而训练集中的每个样本 ( x ( i ) , y ( i ) ) \left( x^{\left( i \right)},y^{\left( i \right)} \right) (x(i),y(i))是随机变量 ( x , y ) \left( x,y \right) (x,y)的取值。
我们的目的是寻找参数使得对于每组的样本 ( x ( i ) , y ( i ) ) \left( x^{\left( i \right)},y^{\left( i \right)} \right) (x(i),y(i)),都有 θ T x ( i ) \theta ^Tx^{\left( i \right)} θTx(i)与 y ( i ) y^{\left( i \right)} y(i)的差别不大,模型中我们使用了所有样本平方误差的总和来衡量差别不大,为什么不用绝对误差总和来衡量呢?为什么不用别的方式来衡量呢?下面就是答案。
对于每组样本,存在一个误差项
ε
(
i
)
\varepsilon ^{\left( i \right)}
ε(i),使得:
y
(
i
)
=
θ
T
x
(
i
)
+
ε
(
i
)
y^{\left( i \right)}=\theta ^Tx^{\left( i \right)}+\varepsilon ^{\left( i \right)}
y(i)=θTx(i)+ε(i)
根据中心极限定理,可以假设
ε
(
i
)
\varepsilon ^{\left( i \right)}
ε(i)是一个随机变量,并且
ε
(
i
)
∼
N
(
0
,
σ
2
)
\varepsilon ^{\left( i \right)}\sim N\left( 0,\sigma ^2 \right)
ε(i)∼N(0,σ2),那么对于给定的
x
(
i
)
x^{\left( i \right)}
x(i),
y
∼
N
(
θ
T
x
(
i
)
,
σ
2
)
y\sim N\left( \theta ^Tx^{(i)},\sigma ^2 \right)
y∼N(θTx(i),σ2),那么对于每组样本
(
x
(
i
)
,
y
(
i
)
)
\left( x^{\left( i \right)},y^{\left( i \right)} \right)
(x(i),y(i)),
y
y
y的概率密度函数取值为:
p
(
y
=
y
(
i
)
∣
x
=
x
(
i
)
;
θ
)
=
1
2
π
σ
exp
(
−
(
y
(
i
)
−
θ
T
x
(
i
)
)
2
2
σ
2
)
p\left( y=y^{\left( i \right)}|x=x^{\left( i \right)};\theta \right) =\frac{1}{\sqrt{2\pi}\sigma}\exp \left( -\frac{\left( y^{\left( i \right)}-\theta ^Tx^{\left( i \right)} \right) ^2}{2\sigma ^2} \right)
p(y=y(i)∣x=x(i);θ)=2πσ1exp(−2σ2(y(i)−θTx(i))2)
这样就可以运用极大似然估计了,似然函数:
L
(
θ
,
x
(
1
)
,
⋯
,
x
(
m
)
,
y
(
1
)
,
⋯
,
y
(
m
)
)
=
∏
i
=
1
m
p
(
y
=
y
(
i
)
∣
x
=
x
(
i
)
;
θ
)
=
∏
i
=
1
m
1
2
π
σ
exp
(
−
(
y
(
i
)
−
θ
T
x
(
i
)
)
2
2
σ
2
)
L\left( \theta ,x^{\left( 1 \right)},\cdots ,x^{\left( m \right)},y^{\left( 1 \right)},\cdots ,y^{\left( m \right)} \right) =\prod_{i=1}^m{p\left( y=y^{\left( i \right)}|x=x^{\left( i \right)};\theta \right)} \\ =\prod_{i=1}^m{\frac{1}{\sqrt{2\pi}\sigma}\exp \left( -\frac{\left( y^{\left( i \right)}-\theta ^Tx^{\left( i \right)} \right) ^2}{2\sigma ^2} \right)}
L(θ,x(1),⋯,x(m),y(1),⋯,y(m))=i=1∏mp(y=y(i)∣x=x(i);θ)=i=1∏m2πσ1exp(−2σ2(y(i)−θTx(i))2)
对数似然函数为:
ln
L
(
θ
)
=
m
ln
1
2
π
σ
−
1
2
σ
2
∑
i
=
1
m
(
y
(
i
)
−
θ
T
x
(
i
)
)
2
\ln L\left( \theta \right) =m\ln \frac{1}{\sqrt{2\pi}\sigma}-\frac{1}{2\sigma ^2}\sum_{i=1}^m{\left( y^{\left( i \right)}-\theta ^Tx^{\left( i \right)} \right) ^2}
lnL(θ)=mln2πσ1−2σ21i=1∑m(y(i)−θTx(i))2
最大化该对数似然函数,就等价于最小化下面的函数:
J
(
θ
)
=
1
2
∑
i
=
1
m
(
θ
T
x
(
i
)
−
y
(
i
)
)
2
J\left( \theta \right) =\frac{1}{2}\sum_{i=1}^m{\left( \theta ^Tx^{\left( i \right)}-y^{\left( i \right)} \right) ^2}
J(θ)=21i=1∑m(θTx(i)−y(i))2
这就是该问题的概率解释。
四、python代码
仿照sklearn库,写出线性回归类。
import numpy as np
class LR(object):
theta=0
def __Normal_Equation(self, X, y): #正规方程解法
self.theta=np.dot(np.dot(np.linalg.pinv(np.dot(X.T,X)),X.T),y)
def __Gradient_Descent(self, X, y): #梯度下降解法(批量)
alpha = 0.01 #学习率
iternum = 0 #记录迭代次数
while True:
tmp = self.theta-alpha*np.dot(X.T,np.dot(X,self.theta)-y)
if np.allclose(self.theta,tmp):
break
self.theta = tmp
iternum += 1
print("迭代次数:",iternum)
def __SGD(self, X ,y):
alpha = 0.1 #学习率
idxs = np.arange(X.shape[0])
np.random.shuffle(idxs)
X, y = X[idxs], y[idxs] # 打乱数据
for i in range(10): # 外层循环迭代10次看看效果
for k in range(X.shape[0]):
self.theta = self.theta - alpha*(np.dot(self.theta,X[k,:])-y[k])*X[k,:]
def fit(self, X_train, y_train, method = None):
X, y=np.insert(X_train, 0, 1, axis=1), y_train #这样写起来比较方便
self.theta = np.zeros(X.shape[1]) #初始化
if method == 'GD': #梯度下降解法
self.__Gradient_Descent(X, y)
elif method == 'NE': #正规方程解法
self.__Normal_Equation(X, y)
elif method == 'SGD': #随机梯度下降法
self.__SGD(X, y)
elif y.shape[0] <= 10000: #这个时候用正规方程
self.__Normal_Equation(X, y)
else: #数据量大,用梯度下降
self.__Gradient_Descent(X, y)
def predict(self, X): #预测
return np.dot(np.insert(X, 0, 1, axis=1), self.theta)
自己创造数据利用上述类来画出回归曲线。
import matplotlib.pyplot as plt
def Create():
return np.array([[1,2],[2,3.4],[1.5,3.2],[3.4,8],[2.5,5],[2.3,5.2]])
data=Create() #导入数据
plt.scatter(data[:,0],data[:,1]) #可视化数据
X=data[:,0:data.shape[1]-1]
y=data[:,-1] #数据中最后一列
np.random.seed(0)
reg = LR()
#正规方程解法
print("正规方程法:")
reg.fit(X, y) #数据量小,默认正规方程法
theta = reg.theta
print("参数theta:",theta)
x=np.linspace(np.min(data[:,0])-1,np.max(data[:,0])+1,2)
plt.plot(x,theta[1]*x+theta[0],color='green')
#梯度下降解法(批量梯度下降法)
print("批量梯度下降法:")
reg.fit(X, y, method = 'GD') #梯度下降法
theta = reg.theta
print("参数theta:",theta)
plt.plot(x,theta[1]*x+theta[0],color='red')
#随机梯度下降法
print("随机梯度下降法:")
reg.fit(X, y, method = 'SGD')
theta = reg.theta
print("参数theta:",theta)
plt.plot(x,theta[1]*x+theta[0],color='yellow')
plt.legend(['Normal Equation','Gradient Descent','SGD','data'])
plt.show()
结果如下:
正规方程法:
参数theta: [-0.74108602 2.4603556 ]
批量梯度下降法:
迭代次数: 1214
参数theta: [-0.73981544 2.45981353]
随机梯度下降法:
参数theta: [-0.25680749 2.20781238]
上图看不见正规方程的回归曲线是因为梯度下降的回归曲线将其挡住了。可以看到随机梯度下降法会较快得接近全局最优解,不过它往往不能收敛到全局最优。
总结
线性回归算法就是利用训练集中的样本学习出合适的参数 θ \theta θ,这样对于新的特征向量,我们可以输出 θ T x \theta^Tx θTx作为预测值。对于如何求解参数 θ \theta θ,我们介绍了两种方法,一种是直接求解的正规方程法,一种是迭代求解的梯度下降法,根据实际情况灵活选择求解方法。