引言
通过12.梯度下降法的具体解析------举足轻重的模型优化算法-CSDN博客的学习,我们已经了解到了梯度下降法的整体流程与不同分类。归根结底,我们最终是要使用代码实现梯度下降法。
通过阅读本篇博客,你可以:
1.知晓轮次和批次的定义
2.使用代码实现不同的梯度下降法
一、轮次和批次
1.轮次的定义
**轮次(epoch)**指的是整个训练数据集被完整使用一次的过程。一个轮次就是指所有的训练样本训练模型一次。在这里就表明了我们使用梯度下降法中模型遍历整个训练样本的次数。
2.轮次在梯度下降法中的应用
通常情况下,一个训练过程会设置多个轮次。例如,如果我们设定进行10个轮次,模型就会遍历整个训练集10次,每次都尝试优化参数以降低损失函数。
3.批次的定义
**批次(batch)**是指在一次参数更新中使用的样本数量。在训练过程中,批次可以根据数据集的大小和内存的限制进行调整。一个批次通常只包括训练集的一部分。
4.批次在梯度下降法中的应用
在梯度下降法中,批次指我们已有的训练集数据较多的时候,一轮要学习太多的数据,那就把一轮此的数据分成多个批次,一批一批的学习。
二、全量梯度下降的代码实现
1.创建数据集
python
import numpy as np
#创建数据集
X = np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
X_b = np.c_[np.ones((100, 1)), X]
我们导入numpy模块,使用其中的 random 方法创建数据集 和 ,由于 的存在,我们需要拼接一列全为1的数据到矩阵上去,最后就形成了数据集 X_b 。(代码中 , )
2.设置超参数
python
# 创建超参数
learning_rate = 0.0001
n_iterations = 10000
我们通常将学习率设置为0.001,0.0001等较为小的数字。通过较大的迭代次数保证函数能够收敛。
3.初始化theta
python
#初始化theta,W0,...Wn,标准正太分布创建W
theta = np.random.randn(2, 1)
由于数据集中只有 一个维度,再算上截距项 。我们就可以使用 random 方法随机创建一个两行一列的 作为梯度下降的参数。
4.进行参数更新迭代
python
#进行迭代
for _ in range(n_iterations):
# 求梯度,计算gradient
gradients = X_b.T.dot(X_b.dot(theta) - y)
# 应用梯度下降法的公式调整theta值 theta_t+1 = theta_t - η * gradient
theta = theta - learning_rate * gradients
我们开始进行参数更新的迭代,每次迭代中,我们要计算梯度的大小和更新参数。由梯度计算公式 可以得出代码中梯度计算的实现(由于同行同列的矩阵运算不能直接相乘,所以我们使用 X_b 的转置相乘)。由梯度下降法公式 可以得出代码中的参数更新的实现。(公式的原理请参考12.梯度下降法的具体解析------举足轻重的模型优化算法-CSDN博客)
5.整体代码
python
import numpy as np
# 创建数据集X,y
X = np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
X_b = np.c_[np.ones((100, 1)), X]
# 创建超参数
learning_rate = 0.0001
n_iterations = 10000
# 初始化theta,W0,...Wn,标准正太分布创建W
theta = np.random.randn(2, 1)
print(theta)
# 判断是否收敛,一般不会设定阈值,而是直接采用设置相对大的迭代次数保证收敛
for _ in range(n_iterations):
# 求梯度,计算gradient
gradients = X_b.T.dot(X_b.dot(theta) - y)
# 应用梯度下降法的公式调整theta值 theta_t+1 = theta_t - η * gradient
theta = theta - learning_rate * gradients
print(theta)
三、随机梯度下降的代码实现
1.创建数据集
python
import numpy as np
# 创建数据集X,y
X = 2 * np.random.rand(100, 1)
y = 5 + 3 * X + np.random.randn(100, 1)
X_b = np.c_[np.ones((100, 1)), X]
创建数据集这边的代码实现原理与全量梯度下降一模一样。
2.设置超参数
python
# 设置超参数
n_epochs = 10000
m = 100
learning_rate = 0.001
同样地,我们仍将轮次设置为10000。不同地是,随机梯度下降每个批次只使用一个随机的样本,所以我们要使用 来代表总样本数100,以代表每个轮次下批次的总数。
3.初始化theta
python
theta = np.random.randn(2, 1)
与全量梯度下降一致。
4.进行参数更新迭代
python
# 每一个轮次
for epoch in range(n_epochs):
# 在每个轮次开始之前分彼此迭代之前打乱数据索引顺序
arr = np.arange(len(X_b))
np.random.shuffle(arr)
X_b = X_b[arr]
y = y[arr]
# 每一个批次
for i in range(m):
xi = X_b[i: i + 1]
yi = y[i: i + 1]
# 求梯度
gradients = xi.T.dot(xi.dot(theta) - yi)
theta = theta - learning_rate * gradients
随机梯度下降除了每个轮次需要遍历,还要对每个批次进行遍历。那么这里就会产生一个值得思考的问题:"我们有什么方法在每一个轮次遍历到所有样本的情况下还保持随机性呢?"。在提出这个问题之后,我想到了一种解决方案,就是在批次遍历之前,使用 random.shuffle方法将数组的索引进行随机打乱,在每个批次遍历的时候按照打乱索引的顺序进行遍历。这样就可以取巧的解决这个问题。
5.整体代码
python
import numpy as np
# 创建数据集X,y
X = 2 * np.random.rand(100, 1)
y = 5 + 3 * X + np.random.randn(100, 1)
X_b = np.c_[np.ones((100, 1)), X]
# 设置超参数
n_epochs = 10000
m = 100
learning_rate = 0.001
# 初始化theta
theta = np.random.randn(2, 1)
# 每一个轮次
for epoch in range(n_epochs):
# 在每个轮次开始之前分彼此迭代之前打乱数据索引顺序
arr = np.arange(len(X_b))
np.random.shuffle(arr)
X_b = X_b[arr]
y = y[arr]
# 每一个批次
for i in range(m):
xi = X_b[i: i + 1]
yi = y[i: i + 1]
# 求梯度
gradients = xi.T.dot(xi.dot(theta) - yi)
theta = theta - learning_rate * gradients
print(theta)
四、小批量梯度下降的代码实现
1.创建数据集
python
import numpy as np
# 设置数据集X,y
X = 3 * np.random.rand(100, 1)
y = 10 + 4 * X + np.random.randn(100, 1)
X_b = np.c_[np.ones((100, 1)), X]
大同小异,不再赘述。
2.设置超参数
python
# 设置超参数
n_epochs = 10000
m = 100
batch_size = 10
num_batches = int(m / batch_size)
learning_rate = 0.001
小批量梯度下降要设置每个批次中批量的大小,这里我们将其设置为 。那么每个轮次中需要批次遍历的次数就是 。使用 的原因是如果无法正好整除,那么我们可以舍弃掉一些多余的样本。这样处理减少了一个多余批次的消耗,并且少量样本的减少并不会对结果产生影响。
3.初始化theta
python
# 初始化theta
theta = np.random.randn(2, 1)
4.进行参数更新迭代
python
# 每个轮次
for epoch in range(n_epochs):
arr = np.arange(len(X_b))
np.random.shuffle(arr)
X_b = X_b[arr]
y = y[arr]
# 每个批次
for i in range(num_batches):
x_batch = X_b[i*batch_size:i*batch_size + batch_size]
y_batch = y[i*batch_size:i*batch_size + batch_size]
# 求梯度
gradients = x_batch.T.dot(x_batch.dot(theta) - y_batch)
theta = theta - learning_rate * gradients
小批量梯度下降中的参数更新迭代其实与随机梯度下降很类似。改变的只有批次遍历数量和每个批次中数据量的大小与索引。
5.整体代码
python
import numpy as np
# 设置数据集X,y
X = 3 * np.random.rand(100, 1)
y = 10 + 4 * X + np.random.randn(100, 1)
X_b = np.c_[np.ones((100, 1)), X]
# 设置超参数
n_epochs = 10000
m = 100
batch_size = 10
num_batches = int(m / batch_size)
learning_rate = 0.001
# 初始化theta
theta = np.random.randn(2, 1)
# 每个轮次
for epoch in range(n_epochs):
arr = np.arange(len(X_b))
np.random.shuffle(arr)
X_b = X_b[arr]
y = y[arr]
# 每个批次
for i in range(num_batches):
x_batch = X_b[i*batch_size:i*batch_size + batch_size]
y_batch = y[i*batch_size:i*batch_size + batch_size]
# 求梯度
gradients = x_batch.T.dot(x_batch.dot(theta) - y_batch)
theta = theta - learning_rate * gradients
print(theta)
总结
本篇博客重点讲解了三种梯度下降法的不同代码实现。希望可以对大家起到作用,谢谢。
关注我,内容持续更新(后续内容在作者专栏《从零基础到AI算法工程师》)!!!