提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
提示:这里可以添加本文要记录的大概内容:
初学神经网络,对于反向传播的理解一直不是很深刻,但读到一篇帖子并编程跑了一下后忽然豁然开朗,故记录在此
提示:以下是本篇文章正文内容,下面案例可供参考
一、反向传播程序实例
具体可参考这篇文章:https://blog.csdn.net/baidu_38797690/article/details/122180655
本文也是在阅读这篇文章后有所感想
二、传播过程
损失函数构成的计算图如下所示:(转载上文链接)
本质上是损失函数LOSS为因变量,x1 w1 w2 Y为自变量的函数,所以这四个变量被称为叶子变量,即独立变量。(具体了解叶子节点可参考这篇文章:https://zhuanlan.zhihu.com/p/85506092)
在神经网路前向传播的过程中,会构建一个计算图即上图,计算图包括了网络中所有变量,并根据变量是否独立标注是否为叶子变量。前向传播并计算完损失函数后,进行反向传播,计算每个叶子变量关于LOSS函数的梯度,而计算每个叶子变量的时候就用到了链式法则。
1.程序
代码如下(示例):
import torch
x1 = torch.tensor([2, 3, 4, 5], dtype=torch.float, requires_grad=True)
print(x1)
w1 = torch.ones(4, requires_grad=True)
print(w1)
x2 = x1 * w1
w2 = torch.tensor([4, 5, 6, 7], dtype=torch.float, requires_grad=True)
y = x2 * w2
print(y)
Y = torch.ones(4, requires_grad=True)
print(Y)
L = (Y - y).mean()
print(L)
L.backward()
print(w2.grad)
print(w1.grad)
print(x2.is_leaf)
2.输出结果
代码如下(示例):
tensor([2., 3., 4., 5.], requires_grad=True)
tensor([1., 1., 1., 1.], requires_grad=True)
tensor([ 8., 15., 24., 35.], grad_fn=<MulBackward0>)
tensor([1., 1., 1., 1.], requires_grad=True)
tensor(-19.5000, grad_fn=<MeanBackward0>)
tensor([-0.5000, -0.7500, -1.0000, -1.2500])
tensor([-2.0000, -3.7500, -6.0000, -8.7500])
False
3.分析
L = (Y - y).mean即对Y和y的所有分量求和相加再除以4,(1-8+1-15+1-24+1-35)/4 = -19.5。所以L = 1/4(Y - x1w1w2),故对w2的求导为-1/4(x1w1),与输出结果相符。如果求x2.grad是会报错的,因为x2不是叶子变量。
神经网络中的权重偏置参数均为叶子变量,因为它们均是人为创建的,故反向传播时,如果你将这些变量的require_grad设置为Ture,那么反向传播时候就会计算这些叶子变量的梯度,从而让我们在更新参数时候使用这些梯度。(require_grad参数相关问题参考这个链接:https://blog.csdn.net/sazass/article/details/116668755)
4.神经网络线性回归模型
这是我在听沐神讲课是从他那里copy的,加了很多注释,贴在这里给大家做个参考。
import random
import torch
from d2l import torch as d2l
# 人工构造数据集
def synthetic_data(w, b, num_examples):
# 生成y=Xw+b+噪声
# torch.normal返回一个张量
X = torch.normal(0, 1, (num_examples, len(w))) # x是均值为0方差为1的随机数,行和列分别为num和w,num是数据的数量,len为数据的长度,则1000个1*2的数据
# 张量乘法
y = torch.matmul(X, w) + b
y += torch.normal(0, 0.01, y.shape)
return X, y.reshape((-1, 1)) # -1是自动计算行数,列数定为1.此时y已经是1000*1的张量
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000) # 注意,[features中的每一行都包含一个二维数据样本, labels中的每一行都包含一维标签值(一个标量)]
# 定义一个data_iter函数, 该函数接收批量大小、特征矩阵和标签向量作为输入,生成大小为batch_size的小批量]。 每个小批量包含一组特征和标签。
# 包含yield的函数是一个生成器,第一次调用时遇到yield会停止,再一次调用是不会从头运行,而是从yield处继续运行。
# 此程序是为了生成一个batchsize大小的批量数据,每次调用都生成一批数据,不需要每次都从第一句开始。故第一次调用时遇到yield会停止,返回一批数据
# 第二次调用继续执行date_iter中的for循环,返回另一批数据
def data_iter(batch_size, features, labels):
num_examples = len(features) # 返回数组的行数,即数据数量
indices = list(range(num_examples)) # range(5)返回0-4的数字,步长为1,list(),返回数组的索引,indices数据类型是list
# 这些样本是随机读取的,没有特定的顺序
# indices 本来是0-999的数构成的一维数组且按照从小到大排列,打乱后还是一维数组但是没有排列顺序
random.shuffle(indices)
for i in range(0, num_examples, batch_size): # range(初值, 终值, 步长)
batch_indices = torch.tensor(
indices[i: min(i + batch_size, num_examples)]) # 生成1*10的张量batchindices,张量里的数值是10个一组取自indices
yield features[batch_indices], labels[batch_indices] # 将batchindices中的数值作为索引,取出对应的features值形成一个10*2的tensor
# 初始化模型参数
w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True) # requires_grad设置为ture, 反向传播时就会自动求导
batch_size = 10
def linreg(X, w, b): #@save
"""线性回归模型"""
return torch.matmul(X, w) + b
def squared_loss(y_hat, y): #@save
"""均方损失"""
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
def sgd(params, lr): #@save
"""小批量随机梯度下降"""
with torch.no_grad(): # 即在with模块下,所有计算所得tensor都不需要求导,即使w, b 的grad被设置为ture,但w和b作为参数输入sgd的计算过程中
# w和b也不会被求导
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
lr = 0.06
num_epochs = 5
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
for X, y in data_iter(batch_size, features, labels): # X为10*2的张量,y为10*1的张量
# net输出为10*1张量
l = loss(net(X, w, b), y) # X和y的小批量损失
# 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
# 并以此计算关于[w,b]的梯度
l.sum().backward() # sum就相当于loss函数中的求和符号
sgd([w, b], lr) # 使用参数的梯度更新参数
with torch.no_grad():
train_l = loss(net(features, w, b), labels)
print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
输出结果为:
epoch 1, loss 0.000131
epoch 2, loss 0.000049
epoch 3, loss 0.000050
epoch 4, loss 0.000050
epoch 5, loss 0.000050
总结
线性回归模型的建立过程:
1.首先生成数据集:定义w和b,生成均值为0,方差为0.01的输入数据大小为(num,len(w)),根据表达式y = wx +b生成y。则将y和x分别赋值给features和labels,作为数据集的特征和标签。这里一共有1000个2维的特征向量,标签即一个1000维的特征向量。
2.构建数据生成器:可以将数据集按照指定的批量大小输出。
3.初始化要优化的参数:即权重系数和偏置系数
4.构建线性回归模型:即y = wx + b。
5.构建损失函数的形式为均方损失
6.构建梯度下降的形式是小批量梯度下降
7.初始化超参数比如学习率,批量大小等
8.开始训练:第一个for循环是用于设置重复遍历所有数据集的次数。第二个for循环是设置在一次遍历中,每次取批量大小的数据进行训练。具体训练过程为首先将features和初始化的w和b放入网络中计算出预测输出,然后将预测输出和实际输出放到损失函数里面计算损失值。再计算损失函数关于参数的偏导数,通过随机梯度下降法优化参数。遍历完一次所有数据后,用此时的参数计算输出,再比较预测和实际的差值求平均后将值打印出来这样我们可以知道每次遍历后损失函数的平均值。
网络结构如下图: