梯度下降
1.简介
梯度下降法是在机器学习和人工智能当中用来递归逼近最小偏差的模型。寻找极小值点,从负梯度方向寻找,当变化值小于一定阈值或完成一定次数时,梯度下降算法结束。
2.公式
θ = θ 0 − α ∗ ∂ f ( θ ) ∂ θ 0 \theta = \theta_{0} - \alpha*\frac{\partial f(\theta) }{\partial \theta_0} θ=θ0−α∗∂θ0∂f(θ)
其中 α \alpha α是步长,也叫作学习率,它决定了在梯度下降迭代时每一步前进的距离。当我们在向极小值点逼近的时候,我们沿着的是梯度的负方向。(梯度就是方向导数最大时的点)
2.1 步长的偏差
3.梯度下降法的步骤
假设函数
y
=
f
(
x
1
,
x
2
,
.
.
.
,
x
n
)
y = f (x_1,x_2,...,x_n)
y=f(x1,x2,...,xn) 只有一个极小点。
初始给定参数为
X
0
=
(
x
10
,
x
20
,
.
.
.
,
x
n
0
,
)
X_0 = (x_{10},x_{20},...,x_{n0},)
X0=(x10,x20,...,xn0,)。从这个点如何搜索才能找到原函数的极小值点?
方法:
①首先设定一个较小的正数
α
α
α,
ε
ε
ε;
②求当前位置出处的各个偏导数:
f
′
(
x
m
0
)
=
∂
y
∂
x
m
(
x
m
0
)
,
m
=
1
,
2
,
.
.
.
,
n
f'(x_{m0})=\frac{∂y}{∂x_{m}}(x_{m0}),m=1,2,...,n
f′(xm0)=∂xm∂y(xm0),m=1,2,...,n
③修改当前函数的参数值,公式如下:
x
m
′
=
x
m
−
α
∂
y
∂
x
m
(
x
m
0
)
,
m
=
1
,
2
,
.
.
.
,
n
x'_{m}=x_{m}-α\frac{∂y}{∂x_{m}}(x_{m0}),m=1,2,...,n
xm′=xm−α∂xm∂y(xm0),m=1,2,...,n
④如果参数变化量小于
ε
ε
ε,退出;否则返回第2步。
4.梯度下降的三种方法
批量梯度下降法(Batch Gradient Descent)
批量梯度下降法每次都使用训练集中的所有样本更新参数。它得到的是一个全局最优解,但是每迭代一步,都要用到训练集所有的数据,如果m很大,那么迭代速度就会变得很慢。
优点:可以得出全局最优解。
缺点:样本数据集大时,训练速度慢。
随机梯度下降法(Stochastic Gradient Descent)
随机梯度下降法每次更新都从样本随机选择1组数据,因此随机梯度下降比批量梯度下降在计算量上会大大减少。SGD有一个缺点是,其噪音较BGD要多,使得SGD并不是每次迭代都向着整体最优化方向。而且SGD因为每次都是使用一个样本进行迭代,因此最终求得的最优解往往不是全局最优解,而只是局部最优解。但是大的整体的方向是向全局最优解的,最终的结果往往是在全局最优解附近。
优点:训练速度较快。
缺点:过程杂乱,准确度下降。
小批量梯度下降法(Mini-batch Gradient Descent)
小批量梯度下降法对包含n个样本的数据集进行计算。综合了上述两种方法,既保证了训练速度快,又保证了准确度。
总结
随机梯度下降法的样例 < 小批量梯度下降法的样例数量 < 批量梯度下降法样例
5.举例 波士顿房价预测
5.1 代码
房屋价格与面积(数据在下面表格中):
序号 | 面积 | 价格 |
---|---|---|
1 | 150 | 6450 |
2 | 200 | 7450 |
3 | 250 | 8450 |
4 | 300 | 9450 |
5 | 350 | 11450 |
6 | 400 | 15450 |
7 | 600 | 18450 |
使用梯度下降求解线性回归(求
θ
0
θ_0
θ0,
θ
1
θ_1
θ1)
h
θ
(
x
)
=
θ
0
+
θ
1
x
h_θ(x)=θ_0+θ_1x
hθ(x)=θ0+θ1x
#房屋价格与面积
#序号:1 2 3 4 5 6 7
#面积:150 200 250 300 350 400 600
#价格:6450 7450 8450 9450 11450 15450 18450
import matplotlib.pyplot as plt
import matplotlib
from math import pow
from random import uniform
import random
x0 = [150,200,250,300,350,400,600]
y0 = [6450,7450,8450,9450,11450,15450,18450]
#为了方便计算,将所有数据缩小100倍
x = [1.50,2.00,2.50,3.00,3.50,4.00,6.00]
y = [64.50,74.50,84.50,94.50,114.50,154.50,184.50]
#线性回归函数为 y=theta0+theta1*x
#参数定义
theta0 = 0.1#对theata0赋值
theta1 = 0.1#对theata1赋值
alpha = 0.1#学习率
m = len(x)
count0 = 0
theta0_list = []
theta1_list = []
#使用批量梯度下降法
for num in range(10000):
count0 += 1
diss = 0 #误差
deriv0 = 0 #对theata0导数
deriv1 = 0 #对theata1导数
#求导
for i in range(m):
deriv0 += (theta0+theta1*x[i]-y[i])/m
deriv1 += ((theta0+theta1*x[i]-y[i])/m)*x[i]
#更新theta0和theta1
for i in range(m):
theta0 = theta0 - alpha*((theta0+theta1*x[i]-y[i])/m)
theta1 = theta1 - alpha*((theta0+theta1*x[i]-y[i])/m)*x[i]
#求损失函数J(θ)
for i in range(m):
diss = diss + (1/(2*m))*pow((theta0+theta1*x[i]-y[i]),2)
theta0_list.append(theta0*100)
theta1_list.append(theta1)
#如果误差已经很小,则退出循环
if diss <= 0.001:
break
theta0 = theta0*100#前面所有数据缩小了100倍,所以求出的theta0需要放大100倍,theta1不用变
#使用随机梯度下降法
theta2 = 0.1#对theata2赋值
theta3 = 0.1#对theata3赋值
count1 = 0
theta2_list = []
theta3_list = []
for num in range(10000):
count1 += 1
diss = 0 #误差
deriv2 = 0 #对theata2导数
deriv3 = 0 #对theata3导数
#求导
for i in range(m):
deriv2 += (theta2+theta3*x[i]-y[i])/m
deriv3 += ((theta2+theta3*x[i]-y[i])/m)*x[i]
#更新theta0和theta1
for i in range(m):
theta2 = theta2 - alpha*((theta2+theta3*x[i]-y[i])/m)
theta3 = theta3 - alpha*((theta2+theta3*x[i]-y[i])/m)*x[i]
#求损失函数J(θ)
rand_i = random.randrange(0,m)
diss = diss + (1/(2*m))*pow((theta2+theta3*x[rand_i]-y[rand_i]),2)
theta2_list.append(theta2*100)
theta3_list.append(theta3)
#如果误差已经很小,则退出循环
if diss <= 0.001:
break
theta2 = theta2*100
print("批量梯度下降最终得到theta0={},theta1={}".format(theta0,theta1))
print(" 得到的回归函数是:y={}+{}*x".format(theta0,theta1))
print("随机梯度下降最终得到theta0={},theta1={}".format(theta2,theta3))
print(" 得到的回归函数是:y={}+{}*x".format(theta2,theta3))
#画原始数据图和函数图
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
plt.plot(x0,y0,'bo',label='数据',color='black')
plt.plot(x0,[theta0+theta1*x for x in x0],label='批量梯度下降',color='red')
plt.plot(x0,[theta2+theta3*x for x in x0],label='随机梯度下降',color='blue')
plt.xlabel('x(面积)')
plt.ylabel('y(价格)')
plt.legend()
plt.show()
plt.scatter(range(count0),theta0_list,s=1)
plt.scatter(range(count0),theta1_list,s=1)
plt.xlabel('上方为theta0,下方为theta1')
plt.show()
plt.scatter(range(count1),theta2_list,s=3)
plt.scatter(range(count1),theta3_list,s=3)
plt.xlabel('上方为theta0,下方为theta1')
plt.show()
结果
由图可知,两者
θ
1
\theta_1
θ1 差距很大,根据如下图像可知,批量梯度下降更准确。
批量梯度下降时的
θ
0
\theta_0
θ0和
θ
1
\theta_1
θ1的变化:
随机梯度下降时的 θ 0 \theta_0 θ0和 θ 1 \theta_1 θ1的变化:
6.手工推导
用python实现简单一元函数梯度下降。
1.构建一个一元函数:y =0.5* (x-0.5)**2;
2.随机取一点,横坐标为
x
0
x_0
x0,设定
α
\alpha
α的参数值。
3.对这个点求导数,x =
x
0
x_0
x0-
α
\alpha
α*(
d
y
d
x
\frac{\mathrm{d} y}{\mathrm{d} x}
dxdy)。
4.重复第三步,设置迭代y的变化量小于
ξ
\xi
ξ,不在迭代。
5.设定初始参数:
x
0
x_0
x0 = 2;
α
\alpha
α = 0.5;
ξ
\xi
ξ = 0.1;迭代次数n = 0.
求导:
d
y
d
x
\frac{\mathrm{d} y}{\mathrm{d} x}
dxdy = 0.5 * 2 * (x-0.5)=x-0.5
x
1
x_1
x1 =
x
0
x_0
x0 -
α
\alpha
α *
d
y
d
x
0
\frac{\mathrm{d} y}{\mathrm{d} x_0}
dx0dy = 2 - 0.5 * (2-0.5) = 1.25;
x 0 x_0 x0 - x 1 x_1 x1 = 0.75;
x 2 x_2 x2 = x 1 x_1 x1 - α \alpha α * d y d x 1 \frac{\mathrm{d} y}{\mathrm{d} x_1} dx1dy = 1.25 - 0.5 * (1.25-0.5) = 0.875;
x 1 x_1 x1 - x 2 x_2 x2 = 0.375;
x 3 x_3 x3 = x 2 x_2 x2 - α \alpha α * d y d x 2 \frac{\mathrm{d} y}{\mathrm{d} x_2} dx2dy = 0.875 - 0.5 * (0.875-0.5) = 0.6875;
x 2 x_2 x2 - x 3 x_3 x3 = 0.1965;
x 4 x_4 x4 = x 3 x_3 x3 - α \alpha α * d y d x 3 \frac{\mathrm{d} y}{\mathrm{d} x_3} dx3dy = 0.6875-0.5*(0.6875-0.5) = 0.59375;
x
3
x_3
x3 -
x
4
x_4
x4 = 0.09375 <
ξ
\xi
ξ = 0.1
所以迭代次数n = 4
6.1实例代码
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import math
from mpl_toolkits.mplot3d import Axes3D
import warnings
# 解决中文显示问题
mpl.rcParams['font.sans-serif'] = [u'SimHei']
mpl.rcParams['axes.unicode_minus'] = False
%matplotlib inline
"""
原图像:
1、构建一个函数为 y = 0.5 * (x-0.5) ** 2 的图像。
2、随机生成X_0点,根据X_0点生成Y点。
3、画出图像。
"""
# 一维原始图像
# 原函数
def f1(x):
return 0.5 * (x - 0.5) ** 2
# 构建数据
X = np.arange(-4,4,0.05)
Y = np.array(list(map(lambda t: f1(t),X)))
# 作图
plt.figure(facecolor='w')
plt.plot(X,Y,'r-',linewidth=2)
plt.title(u'函数$y=0.5 * (x - 0.5)^2$')
plt.show()
# 导数
def h1(x):
return 0.5 * 2 * (x-0.5)
x = 2
alpha = 0.5
f_change = f1(x) # y的变化量
iter_num = 0 # 迭代次数
GD_X = [x] #保存梯度下降所经历的点
GD_Y = [f1(x)]
while(f_change > 1e-10) and iter_num<100:
tmp = x - alpha * h1(x)
f_change = np.abs(f1(x) - f1(tmp))
x = tmp
GD_X.append(x)
GD_Y.append(f1(x))
iter_num += 1
print(u"最终结果为:(%.5f,%.5f)"%(x,f1(x)))
print(u"迭代过程中X的取值,迭代次数:%d" % iter_num)
print(GD_X)
%matplotlib inline
plt.figure(facecolor='w')
plt.plot(X,Y,'r-',linewidth=2) #第三个参数是颜色和形状,red圈就是ro-,red直线就是r-
plt.plot(GD_X, GD_Y, 'bo-', linewidth=2)
plt.title(u'函数$ y = 0.5 * (x-0.25)^2$;\n学习率%.3f;最终解:(%.3f,%.3f),迭代次数:%d'%(alpha,x,f1(x),iter_num))