[机器学习]三种梯度下降的代码实现
·
一.BGD全量梯度下降

全量,顾名思义就是,使用所有样本来进行计算梯度
前提知识:
y_hat = X*w,这是矩阵形式
举个简单例子:假设100个样本,每个样本2个特征,第0个特征的参数/权重就是b

梯度:
矩阵表达:这里注意不是(y_hat - y)X,而是XT(y_hat - y),这里的求和的意思就是要用上全部样本

注意:这里的X,是代码中的X_b,也就是把b看作第0个特征,且全为1
import numpy as np
import torch
#创建数据集
np.random.seed(1)
sample_size = 100
X1 = np.random.rand(sample_size,1) #第1个特征
X2 =np.random.rand(sample_size,1) #第二个特征
#这里我们等于w=[4,3,2]
y = 4 + 3*X1+2*X2 +np.random.randn(sample_size,1)
#将输入变为第一列特征x0=1
X_b = np.c_[np.ones((sample_size,1)),X1,X2]
#设置超参数
epoch = 1000
loss_f=torch.nn.MSELoss()
#随机w,w0=b
w = np.random.randn(3,1)
#后续可以根据迭代次数,动态调整,迭代次数变多,分母增加,学习率就降低==“步子先大,后小”
def learning_rate_schedule(t):
return 5/(500+t)
#学习
for i in range(epoch):
y_hat = X_b.dot(w) #预测值
lr = learning_rate_schedule(i) #得到学习率
loss_num=loss_f(torch.tensor(y_hat),torch.tensor(y))
gradients = X_b.T.dot(y_hat-y)
w = w-lr*gradients
if i %100==0:
print("epoch:{}".format(i),"loss:{}".format(loss_num))
print(w)
运行结果:

打印一下学习出来的w参数值,我们的实际值是[4,3,2] ,这就是我们进行1000次迭代学习出来的w,根据结果可以发现,从200个epoch的时候,损失几乎不变,所以我们只需要将epoch设置为300即可,这里和真实值有误差,样本数太少的原因

反思:
当我发现学习到的参数和实际w还不算很接近,我增加了样本数example_size=1000,发现:

貌似这里和我梯度计算相关,后续还需要学习来解决。
总结:
优点:每次更新都会朝着正确的方向进行,最后能保证收敛于极值点(凸函数,非凸函数可能会收敛于局部极值点)

缺点:每次学习时间过长,不能进行在线模型参数更新。
二.SGD随机梯度下降

这里对比于全量梯度下降,我们会发现没有求和符号,因为我们随机选用样本中的一个样本来计算梯度。
梯度:
![]()
矩阵表达:xiT(y_hat-y),Xi就是某一个样本

代码:
import numpy as np
import torch
#创建数据集
sample_size = 10000 #生成10000个样本
X1 = np.random.rand(sample_size,1) #第一个特征
X2 =np.random.rand(sample_size,1) #第二个特征
#标签值,w=[4,3,2]
y = 4 + 3*X1+2*X2 +np.random.randn(sample_size,1)
#公式中的X,是加上了第0个特征,也就是b,全为1
X_b = np.c_[np.ones((sample_size,1)),X1,X2]
#使用随机初始化
w = np.random.rand(3,1)
epoch =100
#随机梯度下降
for e in range(epoch):
arr = np.arange(sample_size) #[0.1,.....,sample_size-1]
np.random.shuffle(arr) #[5,2,1,....,x]
X_b=X_b[arr] #把样本打乱顺序
y = y[arr]
for i in range(sample_size):
xi = X_b[i:i+1]
yi = y[i:i+1]
yi_hat = xi.dot(w)
lr = learning_rate_schedule(epoch*sample_size+i)
gradients = xi.T.dot(yi_hat-yi)
w = w-lr*gradients
y_hat = X_b.dot(w)
loss_num=loss_f(torch.tensor(y_hat),torch.tensor(y))
if e %10==0:
print("epoch:{}".format(e),"loss:{}".format(loss_num))

最终w:

总结:
优点:学习速度较快
缺点:每次更新可能不会按照正确方向进行,但是这也可能跳出局部最优解

三.MBGD小批量梯度下降

取出部分样本进行计算梯度


代码
import numpy as np
import torch
#创建数据集
#10000个样本
sample_size = 10000
X1 = np.random.rand(sample_size,1) #第一个特征
X2 =np.random.rand(sample_size,1) #第二个特征
#w=[4,3,2] w[0]=b=4
y = 4 + 3*X1+2*X2 +np.random.randn(sample_size,1)
#拼接为第0个特征全为1的矩阵
X_b = np.c_[np.ones((sample_size,1)),X1,X2]
#初始化w
w = np.random.rand(3,1)
epoch =10000
batch_size=100
loss_f=torch.nn.MSELoss()
def learn_rate_schedule(t):
return 5/(500+t)
for e in range(int(epoch/batch_size)):
#双层for循环之间,每个轮次分批次的时候,打乱顺序
arr = np.arange(sample_size) #[0.1,.....,sample_size-1]
np.random.shuffle(arr) #[5,2,1,....,x]
X_b=X_b[arr] #把样本打乱顺序
y = y[arr]
for i in range(sample_size):
#随机一个批次的数据
X_batch = X_b[i:i+batch_size]
y_batch = y[i:i+batch_size]
y_batch_hat = X_batch.dot(w)
#计算梯度
lr = learn_rate_schedule(sample_size*e+i) #更新学习率
gradients = X_batch.T.dot(y_batch_hat-y_batch)
w = w-lr*gradients
最终w:

总结:
MBGD就是SGD和BGD的一个平衡。

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)