赞
踩
计算图和autograd是十分强大的工具,可以定义复杂的操作并且自动求导,然而对于大规模的网络,autograd太底层(太low),在构建网络的过程中,我们经常要考虑将计算安排成层,其中一些可学习的参数,将会在学习的过程中进行优化。
TensorFlow中有类似的Keras,TensorFlow-Slim和TFLearn这种封装了底层计算图的高度抽象的接口,使得构建网络变得十分方便。
在PyTorch中,nn包也完成了同样的功能。其中定义了一组大致等价于层的模块。一个模块接受输入的tensor,计算输出的tensor,而且还保存了一些内部状态(如:需要学习的tensor参数)nn也定义了一组损失函数(loss function)用来训练神经网络。
用nn实现两层网络
# -*- coding: utf-8 -*- """ Created on Mon Nov 1 15:30:03 2021 @author: Lenovo """ # 用nn包实现两层网络 import torch # n是批量大小,d_in是输入维度 # h是隐藏的维度,d_out是输出维度 n,d_in,h,d_out=64,1000,100,10 # 创建随机输入和输出数据——随机tensor x=torch.randn(n,d_in) y=torch.randn(n,d_out) # 使用nn包将model定义为一系列层,nn.Sequential是包含其他模块的模块,并按顺序应用这些模块产生输出。 # 每个线性模块使用线性函数从输入计算输出,并保存其内部的权重和【偏差张量 # 在构造model之后,使用.to()方法将其移动到所需要的设备 model=torch.nn.Sequential( torch.nn.Linear(d_in, h), torch.nn.ReLU(), torch.nn.Linear(h, d_out), ) # nn包还包含常用的损失函数的定义:在这种情况下,我们将使用平均平方误差(MSE)作为我们的损失函数 loss_fn=torch.nn.MSELoss(reduction="sum")# 设置reduction=sum,表示我们计算的是平方误差的和,而不是其他统计量 # 这与之前手工计算损失的例子保持一致,但是在实践过程中,通常使用reduction=elementwise_mean作为损失较为常见 learning_rate=1e-4 for i in range(500): # 前向传播:通过向模型传入x计算预测的y # 模块对象重载了__call__运算符,所以可以像函数那样调用,这么做相当于向模块传入了一个tensor,然后他返回了一个输出tensor y_pred=model(x) # 计算并打印损失值,传递包含y的预测值和真实值的张量,损失函数返回包含损失的tensor loss=loss_fn(y_pred,y) print(i,loss.item()) # 反向传播之前将梯度置零 model.zero_grad() # 反向传播:计算模型的损失对所有学习参数的导数(梯度),在内部,每个模块的参数存储在requires_grad=True的tensor中, # 这个调用将计算模型中所有可学习参数的梯度 loss.backward() # 使用梯度下降更新权重,每个参数都是tensor,可以像之前那样得到他们的数值和梯度 with torch.no_grad(): for param in model.parameters(): param-=learning_rate*param.grad
运行结果
…
截止目前,我们已经通过手动改变包含科学系参数的张量来更新模型的权重,对于随机梯度下降(stochastic gradient descent——SGD)等简单的优化算法来说,这不是很大的负担,但是在实践中,我们经常使用的是AdaGrad,RMSProp,Adam等复杂的优化器来训练神经网络。
将上述代码做细微调整即可
import torch # n是批量大小,d_in是输入维度 # h是隐藏的维度,d_out是输出维度 n,d_in,h,d_out=64,1000,100,10 # 创建随机输入和输出数据——随机tensor x=torch.randn(n,d_in) y=torch.randn(n,d_out) # 使用nn包将model定义为一系列层,nn.Sequential是包含其他模块的模块,并按顺序应用这些模块产生输出。 # 每个线性模块使用线性函数从输入计算输出,并保存其内部的权重和【偏差张量 # 在构造model之后,使用.to()方法将其移动到所需要的设备 model=torch.nn.Sequential( torch.nn.Linear(d_in, h), torch.nn.ReLU(), torch.nn.Linear(h, d_out), ) # nn包还包含常用的损失函数的定义:在这种情况下,我们将使用平均平方误差(MSE)作为我们的损失函数 loss_fn=torch.nn.MSELoss(reduction="sum")# 设置reduction=sum,表示我们计算的是平方误差的和,而不是其他统计量 # 这与之前手工计算损失的例子保持一致,但是在实践过程中,通常使用reduction=elementwise_mean作为损失较为常见 # 使用optim包定义优化器(optimizer),optimizer将会为我们更新模型的权重 # 此处使用Adam优化方法,optim包还包含了许多别的优化算法 # Adam构造函数的第一个参数告诉优化器应该更新哪些tensor learning_rate=1e-4 optimizer=torch.optim.Adam(model.parameters(),lr=learning_rate) for i in range(500): # 前向传播:通过向模型传入x计算预测的y y_pred=model(x) # 计算并打印损失值,传递包含y的预测值和真实值的张量,损失函数返回包含损失的tensor loss=loss_fn(y_pred,y) print(i,loss.item()) # 反向传播之前,使用optimizer将他要更新的张量的梯度置零——这些tensor是模型可学习的权重 optimizer.zero_grad() # 反向传播:根据模型的参数计算loss值 loss.backward() # 调用optimizer的step函数将参数更新 optimizer.step()
运行结果
…
有时候需要指定比以上使用的模块序列更复杂的model,对于这种情况,可以通过继承nn.Module并定义forward函数,这个forward函数可以使用其他模块或者其他的自动求导运算来接受输入tensor,产生输出tensor
用自定义Module子类构建两层网络(在上述代码的基础上,增加自定义的module并调用)
#%% import torch # 自定义nn模块,重写__init__函数和forward函数 class TwoLayerNet(torch.nn.Module): def __init__(self,d_in,h,d_out): # 在构造函数中,实例化两个nn.Linear模块,并将他们作为成员变量 super(TwoLayerNet,self).__init__() self.linear1=torch.nn.Linear(d_in,h) self.linear2=torch.nn.Linear(h, d_out) def forward(self,x): # 在前向传播的函数中,接收一个输入tensor,也必须返回一个输出tensor,可以使用构造函数中定义的模块以及张量上的任意操作 h_relu=self.linear1(x).clamp(min=0) y_pred=self.linear2(h_relu) return y_pred # n是批量大小,d_in是输入维度 # h是隐藏的维度,d_out是输出维度 n,d_in,h,d_out=64,1000,100,10 # 创建随机输入和输出数据——随机tensor x=torch.randn(n,d_in) y=torch.randn(n,d_out) # 通过上面定义的类构建模型 model=TwoLayerNet(d_in, h, d_out) # 构造损失函数和优化器,SGD函数中对model.parameters()调用将包含模型的一部分,即两个Linear模块的可学习参数 loss_fn=torch.nn.MSELoss(reduction="sum")# 设置reduction=sum,表示我们计算的是平方误差的和,而不是其他统计量 learning_rate=1e-4 optimizer=torch.optim.SGD(model.parameters(),lr=learning_rate) for i in range(500): # 前向传播:通过向模型传入x计算预测的y y_pred=model(x) # 计算并打印损失值,传递包含y的预测值和真实值的张量,损失函数返回包含损失的tensor loss=loss_fn(y_pred,y) print(i,loss.item()) # 反向传播之前,使用optimizer将他要更新的张量的梯度置零——这些tensor是模型可学习的权重 optimizer.zero_grad() # 反向传播:根据模型的参数计算loss值 loss.backward() # 调用optimizer的step函数将参数更新 optimizer.step()
运行结果
…
使用Python中的控制流实现循环,并且可以通过在定义转发时多次重用同一个模块来实现最内层之间的权重共享。构建这样一个模型:一个全连接的ReLU网络,在每一次前向传播的时候,他的隐藏层的层数为随机1到4之间的数,这样可以多次重用相同的权重来计算
使用定义的Module实现
#%% import torch import random # 自定义nn模块,重写__init__函数和forward函数 class DynamicNet(torch.nn.Module): def __init__(self,d_in,h,d_out): # 在构造函数中,实例化三个nn.Linear模块,他们在前向传播时候使用 super(DynamicNet,self).__init__() self.input_linear=torch.nn.Linear(d_in,h) self.middle_linear=torch.nn.Linear(h,h) self.output_linear=torch.nn.Linear(h, d_out) def forward(self,x): # 对于前向传播,随机选择0,1,2,3,并多次重用计算隐藏层的middle_linear # 由于每个前向传播构建一个动态计算图,在定义模型的前向传播时使用常规控制流运算符(循环语句和条件语句) # 在定义图形时多次重用同一个模块是安全的 h_relu=self.input_linear(x).clamp(min=0) for i in range(random.randint(0,3)): h_relu=self.middle_linear(h_relu).clamp(min=0) y_pred=self.output_linear(h_relu) return y_pred # n是批量大小,d_in是输入维度 # h是隐藏的维度,d_out是输出维度 n,d_in,h,d_out=64,1000,100,10 # 创建随机输入和输出数据——随机tensor x=torch.randn(n,d_in) y=torch.randn(n,d_out) # 通过上面定义的类构建模型 model=DynamicNet(d_in, h, d_out) # 构造损失函数和优化器 # 此处使用SGD训练是有困难的,使用momentum方法 loss_fn=torch.nn.MSELoss(reduction="sum")# 设置reduction=sum,表示我们计算的是平方误差的和,而不是其他统计量 learning_rate=1e-4 optimizer=torch.optim.SGD(model.parameters(),lr=learning_rate,momentum=0.9) for i in range(500): # 前向传播:通过向模型传入x计算预测的y y_pred=model(x) # 计算并打印损失值,传递包含y的预测值和真实值的张量,损失函数返回包含损失的tensor loss=loss_fn(y_pred,y) print(i,loss.item()) # 反向传播之前,使用optimizer将他要更新的张量的梯度置零——这些tensor是模型可学习的权重 optimizer.zero_grad() # 反向传播:根据模型的参数计算loss值 loss.backward() # 调用optimizer的step函数将参数更新 optimizer.step()
运行结果
…
今日告一段落,晚上看看文献去跑步啦,不想看文献的话回来继续写。跑步提上日程!跑完直接回宿舍啦~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。