使用Pytorch搭建模型的步骤


Posted in Python onNovember 16, 2020

本来是只用Tenorflow的,但是因为TF有些Numpy特性并不支持,比如对数组使用列表进行切片,所以只能转战Pytorch了(pytorch是支持的)。还好Pytorch比较容易上手,几乎完美复制了Numpy的特性(但还有一些特性不支持),怪不得热度上升得这么快。

1  模型定义

和TF很像,Pytorch也通过继承父类来搭建自定义模型,同样也是实现两个方法。在TF中是__init__()和call(),在Pytorch中则是__init__()和forward()。功能类似,都分别是初始化模型内部结构和进行推理。其它功能比如计算loss和训练函数,你也可以继承在里面,当然这是可选的。下面搭建一个判别MNIST手写字的Demo,首先给出模型代码:

import numpy as np
import matplotlib.pyplot as plt 
import torch 
from torch import nn,optim 
from torchsummary import summary 
from keras.datasets import mnist
from keras.utils import to_categorical
device = torch.device('cuda') #——————1——————
 
class ModelTest(nn.Module):
 def __init__(self,device):
  super().__init__() 
  self.layer1 = nn.Sequential(nn.Flatten(),nn.Linear(28*28,512),nn.ReLU())#——————2——————
  self.layer2 = nn.Sequential(nn.Linear(512,512),nn.ReLU()) 
  self.layer3 = nn.Sequential(nn.Linear(512,512),nn.ReLU())
  self.layer4 = nn.Sequential(nn.Linear(512,10),nn.Softmax()) 

  self.to(device) #——————3——————
  self.opt = optim.SGD(self.parameters(),lr=0.01)#——————4——————
 def forward(self,inputs): #——————5——————
  x = self.layer1(inputs)
  x = self.layer2(x)
  x = self.layer3(x)
  x = self.layer4(x)
  return x 
 def get_loss(self,true_labels,predicts): 
  loss = -true_labels * torch.log(predicts) #——————6——————
  loss = torch.mean(loss)
  return loss
 def train(self,imgs,labels): 
  predicts = model(imgs) 
  loss = self.get_loss(labels,predicts)
  self.opt.zero_grad()#——————7——————
  loss.backward()#——————8——————
  self.opt.step()#——————9——————
model = ModelTest(device)
summary(model,(1,28,28),3,device='cuda') #——————10——————

#1:获取设备,以方便后面的模型与变量进行内存迁移,设备名只有两种:'cuda'和'cpu'。通常是在你有GPU的情况下需要这样显式进行设备的设置,从而在需要时,你可以将变量从主存迁移到显存中。如果没有GPU,不获取也没事,pytorch会默认将参数都保存在主存中。

#2:模型中层的定义,可以使用Sequential将想要统一管理的层集中表示为一层。

#3:在初始化中将模型参数迁移到GPU显存中,加速运算,当然你也可以在需要时在外部执行model.to(device)进行迁移。

#4:定义模型的优化器,和TF不同,pytorch需要在定义时就将需要梯度下降的参数传入,也就是其中的self.parameters(),表示当前模型的所有参数。实际上你不用担心定义优化器和模型参数的顺序问题,因为self.parameters()的输出并不是模型参数的实例,而是整个模型参数对象的指针,所以即使你在定义优化器之后又定义了一个层,它依然能优化到。当然优化器你也可以在外部定义,传入model.parameters()即可。这里定义了一个随机梯度下降。

#5:模型的前向传播,和TF的call()类似,定义好model()所执行的就是这个函数。

#6:我将获取loss的函数集成在了模型中,这里计算的是真实标签和预测标签之间的交叉熵。

#7/8/9:在TF中,参数梯度是保存在梯度带中的,而在pytorch中,参数梯度是各自集成在对应的参数中的,可以使用tensor.grad来查看。每次对loss执行backward(),pytorch都会将参与loss计算的所有可训练参数关于loss的梯度叠加进去(直接相加)。所以如果我们没有叠加梯度的意愿的话,那就要在backward()之前先把之前的梯度删除。又因为我们前面已经把待训练的参数都传入了优化器,所以,对优化器使用zero_grad(),就能把所有待训练参数中已存在的梯度都清零。那么梯度叠加什么时候用到呢?比如批量梯度下降,当内存不够直接计算整个批量的梯度时,我们只能将批量分成一部分一部分来计算,每算一个部分得到loss就backward()一次,从而得到整个批量的梯度。梯度计算好后,再执行优化器的step(),优化器根据可训练参数的梯度对其执行一步优化。

#10:使用torchsummary函数显示模型结构。奇怪为什么不把这个继承在torch里面,要重新安装一个torchsummary库。

2  训练及可视化

接下来使用模型进行训练,因为pytorch自带的MNIST数据集并不好用,所以我使用的是Keras自带的,定义了一个获取数据的生成器。下面是完整的训练及绘图代码(50次迭代记录一次准确率):

import numpy as np
import matplotlib.pyplot as plt 
import torch 
from torch import nn,optim 
from torchsummary import summary 
from keras.datasets import mnist
from keras.utils import to_categorical
device = torch.device('cuda') #——————1——————
 
class ModelTest(nn.Module):
 def __init__(self,device):
  super().__init__() 
  self.layer1 = nn.Sequential(nn.Flatten(),nn.Linear(28*28,512),nn.ReLU())#——————2——————
  self.layer2 = nn.Sequential(nn.Linear(512,512),nn.ReLU()) 
  self.layer3 = nn.Sequential(nn.Linear(512,512),nn.ReLU())
  self.layer4 = nn.Sequential(nn.Linear(512,10),nn.Softmax()) 

  self.to(device) #——————3——————
  self.opt = optim.SGD(self.parameters(),lr=0.01)#——————4——————
 def forward(self,inputs): #——————5——————
  x = self.layer1(inputs)
  x = self.layer2(x)
  x = self.layer3(x)
  x = self.layer4(x)
  return x 
 def get_loss(self,true_labels,predicts): 
  loss = -true_labels * torch.log(predicts) #——————6——————
  loss = torch.mean(loss)
  return loss
 def train(self,imgs,labels): 
  predicts = model(imgs) 
  loss = self.get_loss(labels,predicts)
  self.opt.zero_grad()#——————7——————
  loss.backward()#——————8——————
  self.opt.step()#——————9——————
def get_data(device,is_train = True, batch = 1024, num = 10000):
 train_data,test_data = mnist.load_data()
 if is_train:
  imgs,labels = train_data
 else:
  imgs,labels = test_data 
 imgs = (imgs/255*2-1)[:,np.newaxis,...]
 labels = to_categorical(labels,10) 
 imgs = torch.tensor(imgs,dtype=torch.float32).to(device)
 labels = torch.tensor(labels,dtype=torch.float32).to(device)
 i = 0
 while(True):
  i += batch
  if i > num:
   i = batch 
  yield imgs[i-batch:i],labels[i-batch:i] 
train_dg = get_data(device, True,batch=4096,num=60000) 
test_dg = get_data(device, False,batch=5000,num=10000) 

model = ModelTest(device) 
summary(model,(1,28,28),11,device='cuda') 
ACCs = []
import time
start = time.time()
for j in range(20000):
 #训练
 imgs,labels = next(train_dg)
 model.train(imgs,labels)

 #验证
 img,label = next(test_dg)
 predicts = model(img) 
 acc = 1 - torch.count_nonzero(torch.argmax(predicts,axis=1) - torch.argmax(label,axis=1))/label.shape[0]
 if j % 50 == 0:
  t = time.time() - start
  start = time.time()
  ACCs.append(acc.cpu().numpy())
  print(j,t,'ACC: ',acc)
#绘图
x = np.linspace(0,len(ACCs),len(ACCs))
plt.plot(x,ACCs)

准确率变化图如下:

使用Pytorch搭建模型的步骤

3  其它使用技巧

3.1  tensor与array

需要注意的是,pytorch的tensor基于numpy的array,它们是共享内存的。也就是说,如果你把tensor直接插入一个列表,当你修改这个tensor时,列表中的这个tensor也会被修改;更容易被忽略的是,即使你用tensor.detach.numpy(),先将tensor转换为array类型,再插入列表,当你修改原本的tensor时,列表中的这个array也依然会被修改。所以如果我们只是想保存tensor的值而不是整个对象,就要使用np.array(tensor)将tensor的值复制出来。

3.2  自定义层

在TF中,自定义模型通常继承keras的Model,而自定义层则是继承layers.Layer,继承不同的父类通常会造成初学者的困扰。而在pytorch中,自定义层与自定义模型一样,都是继承nn.Module。Pytorch将层与模型都看成了模块,这很容易理解。的确,层与模型之间本来也没有什么明确的界限。并且定义方式与上面定义模型的方式一样,也是实现两个函数即可。代码示例如下:

import torch  
from torch import nn 

class ParaDeconv(nn.Module):#——————1——————
 def __init__(self,in_n,out_n):
  super().__init__() 
  self.w = nn.Parameter(torch.normal(0,0.01,size = [in_n,out_n]),requires_grad=True)
  self.b = nn.Parameter(torch.normal(0,0.01,size = [out_n]),requires_grad=True) 
 def forward(self,inputs):
  x = torch.matmul(inputs,self.w)
  x = x + self.b
  return x 
layer = ParaDeconv(2,3)
y = layer(torch.ones(100,2))#——————2——————
loss = torch.sum(y)#——————3——————
loss.backward()#——————4——————
for i in layer.parameters():#——————5——————
 print(i.grad)#——————6——————

#1:自定义一个全连接层。层中可训练参数的定义是使用nn.Parameter,如果直接使用torch.tensor是无法在#5中遍历到的。

#2/3/4:输入并计算loss,然后反向传播计算参数梯度。

#5/6:输出完成反向传播后层参数的梯度。

以上定义的层可以和pytorch自带的层一样直接插入模型中使用。

3.3  保存/加载

3.3.1  保存/加载模型

有两种方式,一种是保存模型的参数:

torch.save(model.state_dict(), PATH)         #保存  
model.load_state_dict(torch.load(PATH),strict=True) #加载

这种加载方式需要先定义模型,然后再加载参数。如果你定义的模型参数名与保存的参数对不上,就会出错。但如果把strict修改成False,不严格匹配,它就会只匹配对应上的键值,不会因多出或缺少的参数而报错。

另一种是直接保存模型:

torch.save(model, PATH) #保存
model = torch.load(PATH) #加载

这种方式看似方便,实际上更容易出错。因为python不能保存整个模型的类,所以它只能保存定义类的代码文件位置,以在加载时获取类的结构。如果你改变了定义类的代码位置,就有可能因为找不到类而出错。

3.3.2  保存训练点

当你要保存某个训练阶段的状态,比如包含优化器参数、模型参数、训练迭代次数等,可以进行如下操作:

#保存训练点
torch.save({
      'epoch': epoch,
      'model_state_dict': model.state_dict(),
      'optimizer_state_dict': optimizer.state_dict(),
      'loss': loss
      }, PATH)
#加载训练点
model = TheModelClass(*args, **kwargs)
optimizer = TheOptimizerClass(*args, **kwargs)

checkpoint = torch.load(PATH)

model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
epoch = checkpoint['epoch']
loss = checkpoint['loss']

和保存模型一样,也是使用torch.save()。它很灵活,可以保存字典,因此读取的时候也按照字典索引读取即可。当然要注意,并不是任何类型都能保存的,这里保存的四个类型分别是:

1. int

2. collections.OrderedDict

3. collections.OrderedDict

4. list 

3.4  修改模型参数

Pytorch没有提供额外的方式让我们修改模型参数,我们可以使用上面加载模型参数的方式来修改参数。对于某个参数,我们只要把键值和对应要修改的值放在字典中传入load_state_dict即可。如果没传入所有的参数,记得把strict设为False。示例如下:

model.load_state_dict({'weight':torch.tensor([0.])},strict=False) #修改模型参数

参数名,也就是键值,和对应的参数shape可以通过model.state_dict()查看。

以上就是使用Pytorch搭建模型的步骤的详细内容,更多关于Pytorch搭建模型的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
在Python中使用判断语句和循环的教程
Apr 25 Python
matplotlib在python上绘制3D散点图实例详解
Dec 09 Python
Python字典,函数,全局变量代码解析
Dec 18 Python
python通过百度地图API获取某地址的经纬度详解
Jan 28 Python
在cmder下安装ipython以及环境的搭建
Oct 19 Python
在python中使用xlrd获取合并单元格的方法
Dec 26 Python
django 类视图的使用方法详解
Jul 24 Python
爬虫代理池Python3WebSpider源代码测试过程解析
Dec 20 Python
在python中使用nohup命令说明
Apr 16 Python
pycharm无法安装第三方库的问题及解决方法以scrapy为例(图解)
May 09 Python
解决阿里云邮件发送不能使用25端口问题
Aug 07 Python
python 制作磁力搜索工具
Mar 04 Python
Python图像读写方法对比
Nov 16 #Python
python3中编码获取网页的实例方法
Nov 16 #Python
Python3中小括号()、中括号[]、花括号{}的区别详解
Nov 15 #Python
Python根据URL地址下载文件并保存至对应目录的实现
Nov 15 #Python
python re的findall和finditer的区别详解
Nov 15 #Python
Python获取android设备cpu和内存占用情况
Nov 15 #Python
Python __slots__的使用方法
Nov 15 #Python
You might like
Linux Apache PHP Oracle 安装配置(具体操作步骤)
2013/06/17 PHP
PHP中foreach()用法汇总
2015/07/02 PHP
Symfony模板的快捷变量用法实例
2016/03/17 PHP
PHP微信支付结果通知与回调策略分析
2019/01/10 PHP
Extjs学习笔记之七 布局
2010/01/08 Javascript
File, FileReader 和 Ajax 文件上传实例分析(php)
2011/04/27 Javascript
gridview生成时如何去掉style属性中的border-collapse
2014/09/30 Javascript
Javascript this 关键字 详解
2014/10/22 Javascript
javascript实现简易计算器的代码
2016/05/31 Javascript
如何实现json数据可视化详解
2016/11/24 Javascript
jquery实现轮播图效果
2017/02/13 Javascript
基于jquery实现的tab选项卡功能示例【附源码下载】
2019/06/10 jQuery
bootstrap 日期控件 datepicker被弹出框dialog覆盖的解决办法
2019/07/09 Javascript
JS实现骰子3D旋转效果
2019/10/24 Javascript
python读写ini配置文件方法实例分析
2015/06/30 Python
python 根据pid杀死相应进程的方法
2017/01/16 Python
Python三级菜单的实例
2017/09/13 Python
python Flask实现restful api service
2017/12/04 Python
Python实现的rsa加密算法详解
2018/01/24 Python
Python微信公众号开发平台
2018/01/25 Python
浅谈Python2、Python3相对路径、绝对路径导入方法
2018/06/22 Python
python实现手机销售管理系统
2019/03/19 Python
PyTorch预训练的实现
2019/09/18 Python
简单瞅瞅Python vars()内置函数的实现
2019/09/27 Python
大学教师年终总结的自我评价
2013/10/29 职场文书
《母亲的恩情》教学反思
2014/02/13 职场文书
《广玉兰》教学反思
2014/04/14 职场文书
北京奥运会主题口号
2014/06/13 职场文书
信仰心得体会
2014/09/05 职场文书
红领巾心向党演讲稿
2014/09/10 职场文书
2014年行政部工作总结
2014/11/19 职场文书
建党伟业观后感
2015/06/01 职场文书
2016新年晚会开场白
2015/12/03 职场文书
运动会班级口号霸气押韵
2015/12/24 职场文书
《詹天佑》教学反思
2016/02/20 职场文书
uwsgi+nginx代理Django无法访问静态资源的解决
2021/05/10 Servers