目录
前言
参考邱锡鹏老师的《神经网络与深度学习》(俗称蒲公英书),记录一下笔记.一些核心的代码参考李沐的《动手学深度学习》。
一、梯度下降
def bgd(params,lr):
for param in params:
param.data -= lr*param.grad/1000
二、随机梯度下降
每次迭代只从训练集中取出一个样本更新梯度
def sgd(params,lr):
for param in params:
param.data -= lr * param.grad
三、小批量梯度下降
四、Adam算法
Adam算法可以看做是动量法与RMSprop的结合。
Adam算法的最终更新参数的方式为:
def Adam(params,b1,b2,lr,t,mt=0,vt=0,min_num=1e-8):
for param in params:
mt=b1*mt+(1-b1)*param.grad
vt=b2*vt+(1-b2)*param.grad**2
mt_e=mt/(1-b1**(t+1))
vt_e=vt/(1-b2**(t+1))
param_delata=-lr*mt_e/torch.sqrt(vt_e+min_num)
param.data+=param_delata
mt=0
vt=0
五、总代码
1.构建数据集
import torch
from matplotlib import pyplot as plt
import numpy as np
import random
#制作训练需要的数据 y=w1*x1+w2*x2+b
num_inputs = 2
num_examples = 1000
true_w = [2, -3.4]
true_b = 4.2
features = torch.randn(num_examples, num_inputs,dtype=torch.float32)
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()),dtype=torch.float32)
在小批量梯度下降算法中,需要将总体样本分割为多个batch。
# def data_iter(batch_size, features, labels):
# num_examples = len(features)
# indices = list(range(num_examples))
# random.shuffle(indices) # 样本的读取顺序是随机的
# for i in range(0, num_examples, batch_size):
# j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)]) # 最后一次可能不足一个batch
# yield features.index_select(0, j), labels.index_select(0, j)
#loss = squared_loss
#batch_size = 10
# for epoch in range(num_epochs): # 训练模型一共需要num_epochs个迭代周期
# # 在每一个迭代周期中,会使用训练数据集中所有样本一次(假设样本数能够被批量大小整除)。X
# # 和y分别是小批量样本的特征和标签
# for X, y in data_iter(batch_size, features, labels):
# l = loss(net(X, w, b), y).sum() # l是有关小批量X和y的损失
# l.backward() # 小批量的损失对模型参数求梯度
# sgd([w, b], lr) # 使用随机梯度下降迭代模型参数
#
# # 不要忘了梯度清零
# w.grad.data.zero_()
# b.grad.data.zero_()
# train_l = loss(net(features, w, b), labels)
# print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item()))
2.初始化需要训练的参数
w = torch.tensor(np.random.normal(0, 0.01, (num_inputs, 1)), dtype=torch.float32,requires_grad=True)
b = torch.zeros(1, dtype=torch.float32,requires_grad=True)
3.定义模型与损失函数
def linreg(X, w, b):
return torch.mm(X, w) + b
def squared_loss(y_hat, y):
return (y_hat - y.view(y_hat.size())) ** 2 / 2
4.训练
定义不同的优化方法
def mini_bgd(params, lr, batch_size): # 本函数已保存在d2lzh_pytorch包中方便以后使用
for param in params:
param.data -= lr * param.grad / batch_size # 注意这里更改param时用的param.data
def bgd(params,lr):
for param in params:
param.data -= lr*param.grad/1000
def sgd(params,lr):
for param in params:
param.data -= lr * param.grad
def Adam(params,b1,b2,lr,t,mt=0,vt=0,min_num=1e-8):
for param in params:
mt=b1*mt+(1-b1)*param.grad
vt=b2*vt+(1-b2)*param.grad**2
mt_e=mt/(1-b1**(t+1))
vt_e=vt/(1-b2**(t+1))
param_delata=-lr*mt_e/torch.sqrt(vt_e+min_num)
param.data+=param_delata
mt=0
vt=0
训练
lr = 0.03
num_epochs = 5000
b1=0.9
b2=0.99
lr=0.001
for epoch in range(num_epochs):
X = features
y = labels
prediction=linreg(X, w, b)
loss = squared_loss(prediction, y).sum() # l是有关小批量X和y的损失
print(f'epoch:{epoch},loss:{loss.item()}')
loss.backward() # 小批量的损失对模型参数求梯度
Adam([w, b],b1,b2,lr,epoch) # 使用随机梯度下降迭代模型参数
w.grad.data.zero_()
b.grad.data.zero_()
'''sgd'''
# for epoch in range(num_epochs):
# random_num=np.random.randint(0,num_examples)
# X=features[random_num].unsqueeze(0)
# y=labels[random_num].unsqueeze(0)
# prediction = linreg(X, w, b)
# loss = squared_loss(prediction, y).sum() # l是有关小批量X和y的损失
# print(loss.item())
# loss.backward() # 小批量的损失对模型参数求梯度
# sgd([w, b], lr) # 使用随机梯度下降迭代模型参数
# w.grad.data.zero_()
# b.grad.data.zero_()
'''bgd'''
# for epoch in range(num_epochs): # 训练模型一共需要num_epochs个迭代周期
# X = features
# y = labels
# prediction=linreg(X, w, b)
# loss = squared_loss(prediction, y).sum() # l是有关小批量X和y的损失
# print(loss.item())
# loss.backward() # 小批量的损失对模型参数求梯度
# bgd([w, b], lr) # 使用随机梯度下降迭代模型参数
# w.grad.data.zero_()
# b.grad.data.zero_()
# #train_l = squared_loss(linreg(features, w, b), labels)
# #print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item()))
print(true_w, '\n', w)
print(true_b, '\n', b)
总结
任何复杂的神经网络,其复杂的反向传播过程,最核心的一环就是参数梯度的求解,如果参数的梯度已知就可以根据优化算法更新参数