一、梯度下降
1.基本概念
梯度下降概念:梯度下降是求解无约束最优化问题最常用的方法,是人工智能基础算法之一。它通过迭代的思想一步步逼近目标函数,在每一次的过程中先求解当前位置的梯度向量(即求出当前函数的导数),并以负的梯度向量方向作为方向向量,求出下一次的函数(在该方向上函数下降速度最快),直到找到符合理想的极小值点为止。
梯度向量:函数在某点处变化最快速度最快,变化率最大的方向,变化率为梯度向量的模长。
梯度下降公式:
θ
i
=
θ
i
−
α
∂
∂
θ
i
J
(
θ
)
θ_i=θ_i−α\frac{∂}{∂θ_i}J(θ)
θi=θi−α∂θi∂J(θ)
2.直观解释
梯度下降求极小值点问题可以类比为人下山最短路线问题。
*左图为人下山在选择路线 *右图为下山的最短路线
问题描述:一个人要从山顶下山,他要如何选择下山路线以保证他下山的路线最短,最省力。
解题思路:要保证他下山路线最短,需要保证他下山的每一步是该处最斜的坡度。因为在此处坡度最斜的地方迈一小段的步长能获得最大的下山距离。且在下山时迈的步长越小,所走的整体下山路线越趋近于最短下山路线。因为朝最优方向行走的次数最多。
3.梯度下降求解步骤
设f(x1,x2,x3,…,xn)只有一个极小值点,初始给定参数X0 = (xn1,xn2,xn3,…,xn0),从该点开始如何找到该函数f(x)的极小值点?
方法:
(1) 首先自己按需求设定一个十分小的正数α(学习率,即步长),
ε
ε
ε(迭代结束的比较参数,称为阈值)。
(2) 求当前位置函数关于某一自变量的偏导数
f
′
(
x
m
0
)
=
∂
y
∂
x
m
(
x
m
0
)
,
m
=
1
或
2
或
3
或
.
.
.
或
n
f'(x_{m0})=\frac{∂y}{∂x_{m}}(x_{m0}),m=1或2或3或...或n
f′(xm0)=∂xm∂y(xm0),m=1或2或3或...或n
该处偏导数的值即为该点变化最大的斜率
(3) 修改当前函数。每一次下降后需要找到变化后的函数,以变化后的函数所在点为基准再次进行下降运算。
即
x
m
′
=
x
m
−
α
∂
y
∂
x
m
(
x
m
0
)
,
m
=
1
或
2
或
3
或
.
.
.
或
n
x'_{m}=x_{m}-α\frac{∂y}{∂x_{m}}(x_{m0}),m=1或2或3或...或n
xm′=xm−α∂xm∂y(xm0),m=1或2或3或...或n
(4) 比较函数变化值(与
ε
ε
ε对比),若变化值比
ε
ε
ε小结束循环,否则返回步骤(2)和步骤(3)继续循环,直至变化值比
ε
ε
ε小为止。
即判断
α
∂
y
∂
x
m
(
x
m
0
)
,
m
=
1
或
2
或
3
或
.
.
.
或
n
α\frac{∂y}{∂x_{m}}(x_{m0}),m=1或2或3或...或n
α∂xm∂y(xm0),m=1或2或3或...或n是否小于
ε
ε
ε
4.梯度下降法所存在的问题
①迭代次数多,需要大量运算
②在下降过程中可能会出现“之”字形下降
③直线搜索时可能会遇到一些问题
5.例题介绍
例如该题
(1)已知学习率η = 0.9,阈值ε = 0.01
(2)y’ = x - 2
y0’ = -6
(3)x0 = -4
x1 = x0 - η × y0’ = -4 + 5.4 = 1.4
(4)Δx = 5.4 > ε,返回步骤(2)
(以下不再标注步骤顺序)
y1’ = -0.6
x2 = x1 - η × y1’ = 1.94
Δx = 0.54 > 0.01,继续循环
y2’ = -0.06
x3 = x2 - η × y2’ = 1.994
Δx = 0.054 > 0.01,继续循环
y3’ = -0.006
x4 = x3 - η × y3’ = 1.9994
Δx = 0.0054 < 0.01,结束循环
即x = 1.9994为该函数的极小值点
y = x^2/2 - 2x =1.99999982
6.梯度下降三兄弟(BGD、SGD、MBGD)
①批量梯度下降法(Batch Gradient Descent)
批量梯度下降法每次都使用训练集中的所有样本更新参数。它得到的是一个全局最优解,但是每迭代一步,都要用到训练集所有的数据,如果 m 很大,那么迭代速度就会变得很慢。
优点:可以得出全局最优解。
缺点:样本数据集大时,训练速度慢。
#②随机梯度下降法(Stochastic Gradient Descent)
随机梯度下降法每次更新都从样本随机选择 1 组数据,因此随机梯度下降比批量梯度下降在计算量上会大大减少。SGD 有一个缺点是,其噪音较 BGD 要多,使得 SGD 并不是每次迭代都向着整体最优化方向。而且 SGD 因为每次都是使用一个样本进行迭代,因此最终求得的最优解往往不是全局最优解,而只是局部最优解。但是大的整体的方向是向全局最优解的,最终的结果往往是在全局最优解附近。
优点:训练速度较快。
缺点:过程杂乱,准确度下降。
#③小批量梯度下降法(Mini-batch Gradient Descent)
小批量梯度下降法对包含 n 个样本的数据集进行计算。综合了上述两种方法,既保证了训练速度快,又保证了准确度。
二、一元线性回归
1.线性回归概念
在回归分析中,如果只包含一个自变量和一个因变量可用一条直线近似表示,这种回归分析称为一元线性回归分析;如果回归分析中包含两个或两个以上的自变量,且因变量和自变量之间是线性关系,则称为多元线性回归分析。
含义: 一元线性回归分析其实就是从一堆训练集中去算出一条直线,是数据集到直线之间的距离差最小。
*此图为在众多数据集中找出一条一元线性回归方程,尽可能使数据点到直线的距离最小
2.线性回归分析的一般形式
一元线性回归分析:Y = a + bx
多元线性回归分析:Y = a + b1X1 + b2X2 + … + bnXn
渐进回归模型:
Y
=
a
+
b
e
−
r
X
Y=a+be^{-rX}
Y=a+be−rX
二次曲线回归模型:
Y
=
a
+
b
1
X
+
b
2
X
2
Y=a+b_{1}X+b_{2}X^{2}
Y=a+b1X+b2X2
双曲线模型:Y=a+\frac{b}
除此之外还有其他非线性回归分析形式,此处不再表示。
3.一元回归线性方程推导过程
设线性回归函数: h θ ( x ) = θ 0 + θ 1 x h_θ(x)=θ_0+θ_1x hθ(x)=θ0+θ1x构造损失函数(loss): J ( θ 0 , θ 1 ) = 1 2 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) 2 J(θ_{0},θ_{1})=\frac{1}{2m}\sum_{i=1}^{m}(h_{θ}(x^{(i)})-y^{(i)})^{2} J(θ0,θ1)=2m1∑i=1m(hθ(x(i))−y(i))2思路:通过梯度下降法不断更新 θ 0 θ_{0} θ0 和 θ 1 θ_{1} θ1,当损失函数的值特别小时,就得到了我们最终的函数模型。过程: J ( θ 0 , θ 1 ) = 1 2 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) 2 J(θ_{0},θ_{1})=\frac{1}{2m}\sum_{i=1}^{m}(h_{θ}(x^{(i)})-y^{(i)})^{2} J(θ0,θ1)=2m1i=1∑m(hθ(x(i))−y(i))2step1. 求导: ∂ ∂ θ 0 J ( θ 0 , θ 1 ) = 1 m ∑ i = 1 m ( h θ ( x ) ( i ) − y ( i ) ) \frac{∂}{∂θ_{0}}J(θ_{0},θ_{1})=\frac{1}{m}\sum_{i=1} ^{m}(h_{θ}(x)^{(i)}-y^{(i)}) ∂θ0∂J(θ0,θ1)=m1i=1∑m(hθ(x)(i)−y(i)) ∂ ∂ θ 1 J ( θ 0 , θ 1 ) = 1 m ∑ i = 1 m ( h θ ( x ) ( i ) − y ( i ) ) ⋅ x ( i ) \frac{∂}{∂θ_{1}}J(θ_{0},θ_{1})=\frac{1}{m}\sum_{i=1} ^{m}(h_{θ}(x)^{(i)}-y^{(i)})·x^{(i)} ∂θ1∂J(θ0,θ1)=m1i=1∑m(hθ(x)(i)−y(i))⋅x(i)step2. 更新 θ 0 θ_{0} θ0 和 θ 1 θ_{1} θ1: θ 0 = θ 0 − α ⋅ 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) θ_{0}=θ_{0}-α·\frac{1}{m}\sum_{i=1}^{m}(h_{θ}(x^{(i)})-y^{(i)}) θ0=θ0−α⋅m1i=1∑m(hθ(x(i))−y(i)) θ 1 = θ 1 − α ⋅ 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) ⋅ x ( i ) θ_{1}=θ_{1}-α·\frac{1}{m}\sum_{i=1}^{m}(h_{θ}(x^{(i)})-y^{(i)})·x^{(i)} θ1=θ1−α⋅m1i=1∑m(hθ(x(i))−y(i))⋅x(i)step3. 代入损失函数,求损失函数的值,若得到的值小于 ε ε ε(一般为 0.01 或者 0.001 这样的小数),退出;否则,返回 step1。
4.代码实现
以下为通过梯度下降法实现一元线性回归的代码,分别使用了上述的 3 种梯度下降法:
1.批量梯度下降:
下面展示 python代码
。
import matplotlib.pyplot as plt
import matplotlib
from math import pow
from random import uniform
x = [1,3,5,7,9,11,13]
y = [100,111,130,144,149,166,179]
#线性回归方程为 y = theta0+theta1*x
#参数定义
theta0 = uniform(0,2)
thetal = uniform(0,2)
alpha = 0.1
m = len(x)
count = 0
loss = []
for num in range(10000):
count += 1
diss = 0 #误差
deriv0 = 0 #对theta0 导数
deriv1 = 0 #对theta1 导数
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)
loss.append(diss)
#如果误差已经很小,可以退出循环
if diss <= 0.001:
break
print("本次迭代次数为:{}次,最终得到theta0={},theta1={}".format(count,theta0,theta1))
print("本次迭代得到的回归函数使:y={}+{}*x".dormat(theta0,theta1))
#画原始数据图和得到的线性回归函数图
matplotlib.rcParams['font.sans-serif'] = ['SiHei']
plt.plot(x,y,'bo',label='数据')
plt.plot(x,[theta0+theta1*x for x in x],label='线性回归函数')
plt.xlabel('x')
olt.ylabel('y')
plt.legend()
plt.show()
#画损失函数(误差)变化图
plt.scatter(range(count),loss)
plt.show()
输出结果:本次迭代次数为:10000 次,最终得到
t
h
e
t
a
0
=
96.86808644268262
theta0=96.86808644268262
theta0=96.86808644268262,
t
h
e
t
a
1
=
5.762687172041142
theta1=5.762687172041142
theta1=5.762687172041142本次迭代得到的回归函数是:
y
=
96.86808644268262
+
5.762687172041142
∗
x
y=96.86808644268262+5.762687172041142*x
y=96.86808644268262+5.762687172041142∗x
2.随机梯度下降
import matplotlib.pyplot as plt
import matplotlib
from math import pow
import random
x = [1,3,5,7,9,11,13]
y = [100,111,130,144,149,166,179]
#线性回归函数为 y=theta0+theta1*x
#参数定义
theta0 = random.uniform(0,2)#对 theata0 随机赋值
theta1 = random.uniform(0,2)#对 theata1 随机赋值
alpha = 0.1#学习率
m = len(x)
count = 0
loss = []
for num in range(10000):
count += 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 (θ):
rand_i = random.randrange(0,m)
diss = diss + (1/(2*m))*pow((theta0+theta1*x[rand_i]-y[rand_i]),2)
loss.append(diss)
#如果误差已经很小,则退出循环
if diss <= 0.001:
break
print("本次迭代次数为:{}次,最终得到theta0={},theta1={}".format(count,theta0,theta1))
print("本次迭代得到的回归函数是:y={}+{}*x".format(theta0,theta1))
#画原始数据图和得到的线性回归函数图
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
plt.plot(x,y,'bo',label='数据')
plt.plot(x,[theta0+theta1*x for x in x],label='线性回归函数')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()
#画损失函数(误差)变化图
plt.scatter(range(count),loss)
plt.show()
注意:因为是随机的,所以每次的结果会不一样。输出结果:本次迭代次数为:159 次,最终得到
t
h
e
t
a
0
=
94.02334615793364
theta0=94.02334615793364
theta0=94.02334615793364,
t
h
e
t
a
1
=
5.968813991236714
theta1=5.968813991236714
theta1=5.968813991236714本次迭代得到的回归函数是:
y
=
94.02334615793364
+
5.968813991236714
∗
x
y=94.02334615793364+5.968813991236714*x
y=94.02334615793364+5.968813991236714∗x
3.小批量梯度下降
import matplotlib.pyplot as plt
import matplotlib
from math import pow
import random
x = [1,3,5,7,9,11,13]
y = [100,111,130,144,149,166,179]
#目标函数为 y=theta0+theta1*x
#参数定义
theta0 = random.uniform(0,2)#对 theata0 随机赋值
theta1 = random.uniform(0,2)#对 theata1 随机赋值
alpha = 0.1#学习率
m = len(x)
count = 0
loss = []
for num in range(10000):
count += 1
diss = 0 #误差
deriv0 = 0 #对 theta0 导数
deriv1 = 0 #对 theta1 导数
#求导
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 (θ)
rand_ls = list(range(3))
for i in range(3):
rand_i = random.randrange(0,m)
rand_ls[i] = rand_i
for i in rand_ls:
diss = diss + (1/(2*m))*pow((theta0+theta1*x[i]-y[i]),2)
loss.append(diss)
#如果误差已经很小,则退出循环
if diss <= 0.001:
break
print("本次迭代次数为:{}次,最终得到theta0={},theta1={}".format(count,theta0,theta1))
print("本次迭代得到的回归函数是:y={}+{}*x".format(theta0,theta1))
#画原始数据图和得到的线性回归函数图
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
plt.plot(x,y,'bo',label='数据')
plt.plot(x,[theta0+theta1*x for x in x],label='线性回归函数')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()
#画损失函数(误差)变化图
plt.scatter(range(count),loss)
plt.show()
输出结果:本次迭代次数为:10000 次,最终得到 t h e t a 0 = 96.86808644268262 theta0=96.86808644268262 theta0=96.86808644268262, t h e t a 1 = 5.762687172041142 theta1=5.762687172041142 theta1=5.762687172041142本次迭代得到的回归函数是: y = 96.86808644268262 + 5.762687172041142 ∗ x y=96.86808644268262+5.762687172041142*x y=96.86808644268262+5.762687172041142∗x