梯度下降
无约束最优化问题
指从一个问题的所有可能的备选方案中选择出依某种指标来说的是最优的解决方案。梯度下降就是用于解决无约束最优化问题的。
梯度下降的公式:
θ n + 1 = θ n − α ∗ g r a d i e n t 等价于 θ n + 1 = θ n − α ∗ α J ( θ ) α θ 等价于 W j n + 1 = w j n − η ∗ α J ( θ ) α θ \theta^{n+1}=\theta^n-\alpha*gradient\\ 等价于\\ \theta^{n+1}=\theta^n-\alpha*\frac{\alpha J(\theta)}{\alpha \theta}\\ 等价于\\ W^{n+1}_{j}=w^{n}_{j}-\eta*\frac{\alpha J(\theta)}{\alpha \theta} θn+1=θn−α∗gradient等价于θn+1=θn−α∗αθαJ(θ)等价于Wjn+1=wjn−η∗αθαJ(θ)
其中α和η表示的是学习率,也称为训练步幅,其不能太大也不能太小,太大可能会导致来回震荡,找不到全局最优,太小训练时间太长,导致训练速度慢,可能也找不到全局最优。
梯度下降的步骤
- ”瞎蒙“,随机生成θ(w1,w2,…,wn),期望μ=0,δ=1的正态分布。
- 求梯度g,梯度代表曲线上某点切线斜率,沿切线往下相当于沿着坡度最陡峭的方向下降。
- 如果 g>0,令θ下降变小,如果 g<0,则令θ上升变大。(可看上述公式)
- 判断是否收敛,如果收敛则跳出迭代,如果未收敛,则回到第二步,执行2-4步。(收敛判断准则:随着迭代进行,损失函数变化非常小甚至不改变,即认为达到收敛)
三种梯度下降的方法
方法 | 不同 |
---|---|
批量梯度下降(BGD) | 每次迭代使用所有样本进行梯度进行更新 |
小批量梯度下降(MBGD) | 每次迭代使用一部分样本进行梯度更新 |
随机梯度下降(SGD) | 每次迭代随机选择一个样本进行梯度更新 |
批量梯度下降迭代公式(BGD)
θ j n + 1 = θ j n − η ∗ 1 n ∑ i = 1 n ( h θ ( x i ) − y i ) x j i \theta^{n+1}_{j}=\theta^{n}_{j}-\eta*\frac{1}{n}\sum^{n}_{i=1}(h_{\theta}(x^{i})-y^{i})x_{j}^i θjn+1=θjn−η∗n1i=1∑n(hθ(xi)−yi)xji
其中**“j”表示特征数,i表示样本数,式子表示的是对某一个特定的特征j的待定系数进行计算。由于η与1/n是常量,因此可以化简成下列式子:
θ
j
n
+
1
=
θ
j
n
−
η
∗
∑
i
=
1
n
(
h
θ
(
x
i
)
−
y
i
)
x
j
i
\theta^{n+1}_{j}=\theta^{n}_{j}-\eta*\sum^{n}_{i=1}(h_{\theta}(x^{i})-y^{i})x_{j}^i
θjn+1=θjn−η∗i=1∑n(hθ(xi)−yi)xji
也可以利用矩阵写法写成下列式子,表示的是对全部系数**进行更新。
θ
n
+
1
=
θ
n
−
η
∗
X
T
(
X
θ
−
y
)
偏置项
x
0
i
=
1
,
相当于
y
=
k
x
+
b
中的
b
=
w
0
∗
1
\theta^{n+1}=\theta^{n}-\eta*X^T(X\theta -y)\\ 偏置项x_{0}^{i}=1,相当于y=kx+b中的b=w_0*1
θn+1=θn−η∗XT(Xθ−y)偏置项x0i=1,相当于y=kx+b中的b=w0∗1
优点:
- 一次迭代即对所有的样本进行计算,利用矩阵进行操作,实现了并行。
- 由全数据集确定的方向能更好的代表样本总体,更准确地朝向极值所在的方向。当目标函数为凸函数时,BGD一定可以得到全局最优值。
缺点:如果样本n很大,训练过程很慢,而且目标函数一般不是单调的凸函数。
import numpy as np
import matplotlib.pyplot as plt
# 生成测试数据
X = np.random.rand(100, 1)
w,b = np.random.randint(1,10,size = 2)
# 设置真实数据,增加一定的噪声,使得数据更真实
y = w * X + b + np.random.randn(100,1)
plt.scatter(X,y)
------------------------
# 设置偏置项b,即截距
X = np.concatenate([X,np.full(shape = (100,1),fill_value = 1)],axis = 1)
X
------------------------
# 批量梯度下降
epoches = 10000 # 循环次数
eta = 0.01 # 学习率
# 要求解的系数theta,首先进行随机赋值
theta = np.random.randn(2,1)
theta
------------------------
t0 = 5
t1 = 1000
# t:梯度下降的次数,逆时衰减
def learning_rate_shedule(t):
return t0/(t + t1)
------------------------
# BGD求解参数
count = 0 #梯度下降的次数
for i in range(epoches):
g = X.T.dot(X.dot(theta) - y) # 根据公式计算的梯度
eta = learning_rate_shedule(count)
theta = theta - eta * g
count += 1
print('真实的斜率和截距是:',w,b)
print('求解的的斜率和截距是:',theta)
随机梯度下降SGD迭代公式(小步快跑)
θ j n + 1 = θ j n − η ∗ ( h θ ( x i ) − y i ) x j \theta^{n+1}_{j}=\theta^{n}_{j}-\eta*(h_{\theta}(x^{i})-y^i)x_{j} θjn+1=θjn−η∗(hθ(xi)−yi)xj
优点:参数更新速度快。
缺点:准确度下降,可能会收敛到局部最优。
# 一元一次
import numpy as np
import matplotlib.pyplot as plt
# 生成测试数据
X = np.random.rand(100, 1)
w,b = np.random.randint(1,10,size = 2)
# 设置真实数据,增加一定的噪声,使得数据更真实
y = w * X + b + np.random.randn(100,1)
plt.scatter(X,y)
--------------------------------------
# 设置截距
X = np.concatenate([X,np.full_like(X,fill_value = 1)],axis = 1)
# SGD求解
epoches = 100
t0 = 5
t1 = 1000
# t:梯度下降的次数,逆时衰减
def learning_rate_shedule(t):
return t0/(t + t1)
# 斜率和截距两个未知数
theta = np.random.randn(2,1)
count = 0
for i in range(epoches):
index = np.arange(100)
np.random.shuffle(index) # 打乱下标顺序
# 花式索引
X = X[index]
y = y[index]
for j in range(100):
# 使得单个样本依然是一个矩阵,可以进行矩阵运算
X_j = X[[j]]
y_j = y[[j]]
g = X_j.T.dot(X_j.dot(theta) - y_j)
eta = learning_rate_shedule(count)
theta = theta - eta * g
count += 1
print('真实的斜率和截距是:',w,b)
print('求解的的斜率和截距是:',theta)
# 多元一次
import numpy as np
import matplotlib.pyplot as plt
X = np.random.rand(100, 5)
w = np.random.randint(1,10,size = (5,1))
b = np.random.randint(1,10,size = 1)
# 设置真实数据,增加一定的噪声,使得数据更真实
y = X.dot(w) + b + np.random.randn(100,1)
X = np.concatenate([X,np.full(shape=(100,1),fill_value = 1)],axis = 1)
epoches = 100
t0 = 5
t1 = 1000
# t:梯度下降的次数,逆时衰减
def learning_rate_shedule(t):
return t0/(t + t1)
# 斜率和截距两个未知数
theta = np.random.randn(6,1)
count = 0
for i in range(epoches):
index = np.arange(100)
np.random.shuffle(index) # 打乱下标顺序
# 花式索引
X = X[index]
y = y[index]
for j in range(100):
# 使得单个样本依然是一个矩阵,可以进行矩阵运算
X_j = X[[j]]
y_j = y[[j]]
g = X_j.T.dot(X_j.dot(theta) - y_j)
eta = learning_rate_shedule(count)
theta = theta - eta * g
count += 1
print('真实的斜率和截距是:',w,b)
print('求解的的斜率和截距是:',theta)
小批量梯度下降MBGD迭代公式
θ j n + 1 = θ j n − η ∗ 1 b a t c h _ s i z e ∑ i = 1 b a t c h _ s i z e ( h θ ( x i ) − y i ) x j i \theta^{n+1}_{j}=\theta^{n}_{j}-\eta*\frac{1}{batch\_size}\sum^{batch\_size}_{i=1}(h_{\theta}(x^i)-y^i)x_j^i θjn+1=θjn−η∗batch_size1i=1∑batch_size(hθ(xi)−yi)xji
更新速度和更新次数之间比较平衡。
# 一元一次
import numpy as np
import matplotlib.pyplot as plt
# 数据准备
X = np.random.rand(100, 1)
w,b = np.random.randint(1,10,size = 2)
# 设置真实数据,增加一定的噪声,使得数据更真实
y = w * X + b + np.random.randn(100,1)
X = np.c_[X,np.ones((100,1))]
# 学习率的逆时衰减
t0 = 5
t1 = 500
# t:梯度下降的次数,逆时衰减
def learning_rate_shedule(t):
return t0/(t + t1)
# 超参数
epoches = 1000 # 训练次数
n = 100 # 样本数量
batch_size = 16 # 小批量样本数量
num_batches = int(n / batch_size) # 小批量训练次数
theta = np.random.randn(2,1)
for i in range(epoches):
index = np.arange(100)
np.random.shuffle(index) # 打乱下标顺序
# 花式索引
X = X[index]
y = y[index]
for j in range(num_batches):
X_batch = X[i * batch_size: (i + 1) * batch_size]
y_batch = y[i * batch_size: (i + 1) * batch_size]
g = X_batch.T.dot(X_batch.dot(theta) - y_batch)
learning_rate = learning_rate_shedule(epoches * n + i)
theta = theta - learning_rate * g
print('初始的斜率与截距为:',w,b)
print('MBGD的斜率和截距为:',theta)
# 多元一次
import numpy as np
import matplotlib.pyplot as plt
# 数据准备
X = np.random.rand(100, 5)
w = np.random.randint(1,10,size = (5,1))
b = np.random.randint(1,10,size = 1)
# 设置真实数据,增加一定的噪声,使得数据更真实
y = X.dot(w) + b + np.random.randn(100,1)
X = np.c_[X,np.ones((100,1))]
# 学习率的逆时衰减
t0 = 1
t1 = 1000
# t:梯度下降的次数,逆时衰减
def learning_rate_shedule(t):
return t0/(t + t1)
# 超参数
epoches = 100 # 训练次数
n = 100 # 样本数量
batch_size = 16 # 小批量样本数量
num_batches = int(n / batch_size) # 小批量训练次数
theta = np.random.randn(6,1)
print('初始的斜率与截距为:',w,b)
for i in range(epoches):
index = np.arange(100)
np.random.shuffle(index) # 打乱下标顺序
# 花式索引
X = X[index]
y = y[index]
for j in range(num_batches):
X_batch = X[i * batch_size: (i + 1) * batch_size]
y_batch = y[i * batch_size: (i + 1) * batch_size]
g = X_batch.T.dot(X_batch.dot(theta) - y_batch)
learning_rate = learning_rate_shedule(epoches * n + i)
theta = theta - learning_rate * g
print('MBGD的斜率和截距为:',theta)