一、线性回归训练的代码与结果
(1)代码
import random
import numpy as np
import torch
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)
def linreg(X, w, b): # 本函数已保存在d2lzh包中方便以后使用
return torch.mm(X, w) + b
def squared_loss(y_hat, y): # 本函数已保存在pytorch_d2lzh包中方便以后使用
return (y_hat - y.view(y_hat.size())) ** 2 / 2
def sgd(params, lr, batch_size): # 本函数已保存在d2lzh_pytorch包中方便以后使用
for param in params:
param.data -= lr * param.grad / batch_size # 注意这里更改param时用的param.data
# print(param.grad.data)
if __name__ == '__main__':
num_inputs = 2
num_examples = 1000
true_w = [2, -3.4]
true_b = 4.2
features = torch.randn(num_examples, num_inputs)
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.float)
batch_size = 10
w = torch.tensor(np.random.normal(0, 0.01, (num_inputs, 1)), dtype=torch.float)
b = torch.zeros(1)
w.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True)
lr = 0.01
num_epochs = 5
net = linreg
loss = squared_loss
for epoch in range(num_epochs): # 训练模型一共需要num_epochs个迭代周期
# 在每一个迭代周期中,会使用训练数据集中所有样本一次(假设样本数能够被批量大小整除)。X
# 和y分别是小批量样本的特征和标签
for X, y in data_iter(batch_size, features, labels):
# with torch.no_grad():
# # 测试w代码块
# w1 = 0
# for i in range(10):
# w1 += (X[i, 0] * w[0] + b - y[i]) * X[i, 0]
# print('--------------')
# print(w1)
#
# # 测试b代码块
# b1 = 0
# for i in range(10):
# b1 += ((X[i, 0] * w[0] + X[i, 1] * w[1]) + b - y[i])
# print(b1)
l = loss(net(X, w, b), y).sum() # l是有关小批量X和y的损失
l.backward() # 小批量的损失对模型参数求梯度
sgd([w, b], lr, batch_size) # 使用小批量随机梯度下降迭代模型参数
# 不要忘了梯度清零
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)结果
epoch 1, loss 2.210815
epoch 2, loss 0.294341
epoch 3, loss 0.039363
epoch 4, loss 0.005334
epoch 5, loss 0.000763
二、w和b反向传播的计算过程
- 在上述程序中,
sgd([w, b], lr, batch_size)
这一步中,代入sgd函数中有w.data -= Ir * w.grad / batch_size
和b.data -= Ir * b.grad / batch_size
。
下面详解如何计算w.grad和b.grad:
-
w是一个2X1的矩阵,设为 。b是一个标量。X在每一次循环中是一个10X2的矩阵,设为 。y是一个1X10的矩阵,设为 。
-
l = loss(net(X, w, b), y).sum()
的计算公式: -
由 :
-
可得 :
-
利用链式法则,计算 关于 的偏微分:
-
计算 关于 的偏微分(其中 ):
-
关于 的偏微分计算同理。
-
计算 关于 的偏微分:
-
计算 关于 的偏微分:
三、对理论进行证明测试
- 根据上面的理论,分别手动计算了 和 反向传播的值,与w1.grad,b.grad相互对照,看结果是否一致。
(1)相关代码块
with torch.no_grad():
# 测试w代码块
w1 = 0
for i in range(10):
w1 += (X[i, 0] * w[0] + b - y[i]) * X[i, 0]
print('--------------')
print(w1)
# 测试b代码块
b1 = 0
for i in range(10):
b1 += ((X[i, 0] * w[0] + X[i, 1] * w[1]) + b - y[i])
print(b1)
(2)结果及分析
- 结果解读:第一个和第二个tensor是 和 反向传播的值。第三个tensor是w1.grad和w2.grad。第四个tensor是b.grad的值。
- 可以发现手动计算的 和 反向传播的值 7.8666 和 -60.6970 与w1.grad,b.grad的值7.8966,-60.6970一致( 有一点差别可能是由于pytorch在计算 时加入了一些扰动),推导完全正确!可喜可乐。
--------------
tensor([7.8666])
tensor([-60.6970])
tensor([[ 7.8966],
[51.1244]])
tensor([-60.6970])
--------------
tensor([-3.4083])
tensor([-12.8728])
tensor([[-3.3363],
[ 8.8743]])
tensor([-12.8728])
--------------
tensor([-10.8057])
tensor([-40.0288])
tensor([[-10.7679],
[ 48.8254]])
tensor([-40.0288])
(3)完整的测试代码
import random
import numpy as np
import torch
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)
def linreg(X, w, b): # 本函数已保存在d2lzh包中方便以后使用
return torch.mm(X, w) + b
def squared_loss(y_hat, y): # 本函数已保存在pytorch_d2lzh包中方便以后使用
return (y_hat - y.view(y_hat.size())) ** 2 / 2
def sgd(params, lr, batch_size): # 本函数已保存在d2lzh_pytorch包中方便以后使用
for param in params:
param.data -= lr * param.grad / batch_size # 注意这里更改param时用的param.data
print(param.grad.data)
if __name__ == '__main__':
num_inputs = 2
num_examples = 1000
true_w = [2, -3.4]
true_b = 4.2
features = torch.randn(num_examples, num_inputs)
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.float)
batch_size = 10
w = torch.tensor(np.random.normal(0, 0.01, (num_inputs, 1)), dtype=torch.float)
b = torch.zeros(1)
w.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True)
lr = 0.01
num_epochs = 5
net = linreg
loss = squared_loss
for epoch in range(num_epochs): # 训练模型一共需要num_epochs个迭代周期
# 在每一个迭代周期中,会使用训练数据集中所有样本一次(假设样本数能够被批量大小整除)。X
# 和y分别是小批量样本的特征和标签
for X, y in data_iter(batch_size, features, labels):
with torch.no_grad():
# 测试w代码块
w1 = 0
for i in range(10):
w1 += (X[i, 0] * w[0] + b - y[i]) * X[i, 0]
print('--------------')
print(w1)
# 测试b代码块
b1 = 0
for i in range(10):
b1 += ((X[i, 0] * w[0] + X[i, 1] * w[1]) + b - y[i])
print(b1)
l = loss(net(X, w, b), y).sum() # l是有关小批量X和y的损失
l.backward() # 小批量的损失对模型参数求梯度
sgd([w, b], lr, batch_size) # 使用小批量随机梯度下降迭代模型参数
# 不要忘了梯度清零
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()))
四、参考
CNN反向传播 - 文章对反向传播方向更新w和b的值讲解的很清晰
转载:https://blog.csdn.net/miracleoa/article/details/105616366
查看评论