机器学习中常用梯度下降法
写在前面
最近在上机器学习的讨论班,今天讲到梯度下降法,又去翻看了吴恩达老师机器学习课程的相关视频,记录下学习笔记。
梯度下降算法就可以用爬山去解释的非常典型的一个例子。 设想,我们现在在一座风景优美的山上,天快黑了,我们该怎样选择下山路径,以最快的速度下山到山脚下。我们站在所在的位置往四周看,发现有一个方向最陡(负梯度方向),沿着这个方向走一步,能让我们的海拔降低最多,所以我们就沿着这个方向走一步。 然后在现在站的位置上,再看四周,找一个最陡的方向走一步,以此一步一步往山下走。这样我们能以最快的速度到达山脚(局部最小解)。这就是梯度下降法的思想。
梯度下降算法
看了一些资料,现在在机器学习方向,常用梯度下降法有三种形式:批量梯度下降法(Batch Gradient Descent),随机梯度下降法(Stochastic Gradient Descent),小批量梯度下降法(Mini-Batch Gradient Descent)。我们以线性回归的目标函数为例来分别讲解下三种形式的梯度下降方法。设有数据集 D = { ( x i , y i ) , i = 1 , 2 , ⋯ , m } D=\{(x_i,y_i),i=1,2,\cdots,m\} D={(xi,yi),i=1,2,⋯,m}, 其中 x i = ( x i ( 1 ) , x i ( 2 ) , ⋯ , x i ( d ) ) T , y i ∈ R x_i=(x^{(1)}_i,x^{(2)}_i,\cdots,x^{(d)}_i)^T, y_i\in \mathbb{R} xi=(xi(1),xi(2),⋯,xi(d))T,yi∈R, 要求解以下最小化误差函数
θ
∗
=
arg
min
J
(
θ
)
\theta^*=\arg \min J(\theta)
θ∗=argminJ(θ)
其中
J
(
θ
)
=
1
2
m
∑
i
=
1
m
(
θ
T
x
i
−
y
i
)
2
J(\theta)=\frac{1}{2m}\sum^m_{i=1}\left(\theta^Tx_i-y_i\right)^2
J(θ)=2m1i=1∑m(θTxi−yi)2
1.批量梯度下降法(BGD)
J
(
θ
)
J(\theta)
J(θ)的梯度为
∇
J
(
θ
)
=
1
m
∑
i
=
1
m
(
θ
T
x
i
−
y
i
)
x
i
\nabla J(\theta)=\frac{1}{m}\sum^m_{i=1}(\theta^{T}x_i-y_i)x_i
∇J(θ)=m1i=1∑m(θTxi−yi)xi选择合适的步长
η
\eta
η,可以得到BGD如下:
BGD算法
Step1:选择合适的初始值 θ 0 \theta_0 θ0和误差容忍度 ϵ \epsilon ϵ,学习率 η \eta η,
Step2:对 k = 1 , 2 , ⋯ k=1,2,\cdots k=1,2,⋯
θ k + 1 = θ k + η m ∑ i = 1 m ( y i − θ T x i ) x i \hskip3em\theta_{k+1}=\theta_k+\frac{\eta}{m}\sum^m_{i=1}(y_i-\theta^{T}x_i)x_i θk+1=θk+mη∑i=1m(yi−θTxi)xi
\hskip3em 如果 ∣ J ( θ k + 1 ) − J ( θ k ) ∣ < ϵ |J(\theta_{k+1})-J(\theta_k)| < \epsilon ∣J(θk+1)−J(θk)∣<ϵ, 停止迭代
Step3:输出 θ k + 1 \theta_{k+1} θk+1为所求最小值点。
2.随机梯度下降法(SGD)
用批量梯度下降法每次更新参数 θ \theta θ的时候,需要对所有的训练样本进行求和,当训练数据量很大的时候,计算量会非常大。 为了解决这个困难,随机梯度下降法被提出。 SGD的基本思路是,先将训练样本随机打乱重新排好,然后每次更新参数 θ \theta θ时,依次选取其中的一个样本,使其误差函数下降。第 k k k个样本的误差函数
J
k
(
θ
)
=
1
2
(
θ
T
x
i
−
y
i
)
2
J_k(\theta)=\frac{1}{2}\left(\theta^Tx_i-y_i\right)^2
Jk(θ)=21(θTxi−yi)2
梯度为
∇
J
k
(
θ
)
=
(
θ
T
x
i
−
y
i
)
x
i
\nabla J_k(\theta)=(\theta^{T}x_i-y_i)x_i
∇Jk(θ)=(θTxi−yi)xi。选择合适的步长
η
\eta
η,可以得到SGD如下:
SGD算法
Step1:选择合适的初始值 θ 0 \theta_0 θ0和误差容忍度 ϵ \epsilon ϵ,学习率 η \eta η,
Step2:将训练样本 ( x i , y i ) , i = 12 , ⋯ , m (x_i,y_i),i=12,\cdots,m (xi,yi),i=12,⋯,m,打乱重新排序。打乱之后的训练样本仍记为 ( x i , y i ) , i = 12 , ⋯ , m (x_i,y_i),i=12,\cdots,m (xi,yi),i=12,⋯,m
Step3:do{ //一般重复10次左右
\hskip4em for i = 1 , 2 , ⋯ , m i=1,2,\cdots, m i=1,2,⋯,m,
\hskip5em θ k + 1 = θ k + η ( y i − θ T x i ) x i \theta_{k+1}=\theta_k+\eta(y_i-\theta^{T}x_i)x_i θk+1=θk+η(yi−θTxi)xi
\hskip4em 如果 ∣ J ( θ k + 1 ) − J ( θ k ) ∣ < ϵ |J(\theta_{k+1})-J(\theta_k)| < \epsilon ∣J(θk+1)−J(θk)∣<ϵ, 停止迭代
\hskip3em }
Step4:输出 θ k + 1 \theta_{k+1} θk+1为所求最小值点。
3.小批量梯度下降法(MGD)
结合BGD和SGD算法,产生了小批量梯度下降法,其思路为:选取适当的batchsize,利用训练样本中batchsize个样本来更新参数 θ \theta θ,即每次更新参数 θ \theta θ时,使batchsize个样本的误差下降。
MGD算法
Step1:选择合适的初始值 θ 0 \theta_0 θ0和误差容忍度 ϵ \epsilon ϵ,学习率 η \eta η和batchsize, 令 n b = M b s nb=\frac{M}{bs} nb=bsM, t = 0 t=0 t=0
Step2:for j = 1 , 2 , ⋯ , n b j=1,2,\cdots, nb j=1,2,⋯,nb
\hskip4em θ k + 1 = θ k + η b s ∑ i = t t + b s ( y i − θ T x i ) x i \theta_{k+1}=\theta_k+\frac{\eta}{bs}\sum^{t+bs}_{i=t}(y_i-\theta^{T}x_i)x_i θk+1=θk+bsη∑i=tt+bs(yi−θTxi)xi
\hskip4em t=t+bs
\hskip4em 如果 ∣ J ( θ k + 1 ) − J ( θ k ) ∣ < ϵ |J(\theta_{k+1})-J(\theta_k)| < \epsilon ∣J(θk+1)−J(θk)∣<ϵ, 停止迭代
Step4:输出 θ k + 1 \theta_{k+1} θk+1为所求最小值点。
算例
由于熬夜感冒了,先实现BGD。给定数据集如图所示,对这组数据做线性回归。
线性回归所得为下图红色直线
代码实现:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
def getData():
fpath=r'E:\\Li\\python\\database\\data_linear.csv'
data=pd.read_csv(fpath,header=None,names=['x','y'])
x = np.array(data.x)
y = np.array(data.y)
return x,y
def loss(x,y,w):
m = x.shape[0]
v=y-np.dot(x,w)
return np.dot(v.T,v)/(2*m)
def BGD(x, y, step_size=0.0001, max_iter_count=10000, tol = 1e-6):
m = x.shape[0]
d = (len(x.shape)>1 and [x.shape[1]] or [1])[0]
x = np.concatenate(([x], [np.ones(m)]), axis=0).T
w = np.ones((x.shape[1],))
J0 = loss(x,y,w)
n_iter = 0
while n_iter < max_iter_count:
n_iter += 1
c = y - np.dot(x,w)
vtmp = np.zeros((d+1,m))
for i in range(m):
vtmp[:,i] = c[i] * x[i,:]
dw = np.sum(vtmp,axis=1)
dw = step_size * dw /m
w += dw
J1 = loss(x,y,w)
if abs(J1-J0) < tol: break
J0 = J1
return w,n_iter
if __name__ == '__main__':
x, y = getData()
plt.scatter(x,y)
w,n_iter=BGD(x,y)
x1 = np.linspace(min(x),max(x), 100)
x2 = np.ones((100,))
xi = np.concatenate(([x1], [x2]), axis=0).T
yi = np.dot(xi, w)
plt.plot(x1,yi,'r-')