解决pytorch 模型复制的一些问题


Posted in Python onMarch 03, 2021

直接使用

model2=model1

会出现当更新model2时,model1的权重也会更新,这和自己的初始目的不同。

经评论指出可以使用:

model2=copy.deepcopy(model1)

来实现深拷贝,手上没有pytorch环境,具体还没测试过,谁测试过可以和我说下有没有用。

原方法:

所有要使用模型复制可以使用如下方法。

torch.save(model, "net_params.pkl")
model5=Cnn(3,10)
model5=torch.load('net_params.pkl')

这样编写不会影响原始模型的权重

补充:pytorch模型训练流程中遇到的一些坑(持续更新)

要训练一个模型,主要分成几个部分,如下。

数据预处理

入门的话肯定是拿 MNIST 手写数据集先练习。

pytorch 中有帮助我们制作数据生成器的模块,其中有 Dataset、TensorDataset、DataLoader 等类可以来创建数据入口。

之前在 tensorflow 中可以用 dataset.from_generator() 的形式,pytorch 中也类似,目前我了解到的有两种方法可以实现。

第一种就继承 pytorch 定义的 dataset,改写其中的方法即可。如下,就获得了一个 DataLoader 生成器。

class MyDataset(Dataset):
 def __init__(self, data, labels):
 self.data = data
 self.labels = labels
 def __getitem__(self, index):
 return self.data[index], self.labels[index]
 def __len__(self):
 return len(self.labels)
 
train_dataset = MyDataset(train_data, train_label)
train_loader = DataLoader(dataset = train_dataset,
 batch_size = 1,
 shuffle = True)

第二种就是转换,先把我们准备好的数据转化成 pytorch 的变量(或者是 Tensor),然后传入 TensorDataset,再构造 DataLoader。

X = torch.from_numpy(train_data).float()
Y = torch.from_numpy(train_label).float()
train_dataset = TensorDataset(X, Y)
 
train_loader = DataLoader(dataset = train_dataset,
 batch_size = 1,
 shuffle = True)
 #num_workers = 2)

模型定义

class Net(nn.Module):
 
 def __init__(self):
 super(Net, self).__init__()
 self.conv1 = nn.Conv2d(1, 6, 3)
 self.conv2 = nn.Conv2d(6 ,16, 3)
 
 self.fc1 = nn.Linear(400, 120)
 self.fc2 = nn.Linear(120, 84)
 self.fc3 = nn.Linear(84, 10)
 
 def forward(self, x):
 relu = F.relu(self.conv1(x))
 x = F.max_pool2d(relu, (2, 2))
 x = F.max_pool2d(F.relu(self.conv2(x)), 2)
 x = x.view(-1, self.num_flat_features(x))
 x = F.relu(self.fc1(x))
 x = F.relu(self.fc2(x))
 x = self.fc3(x)
 
 return x 
 def num_flat_features(self, x):
 size = x.size()[1:] #除了batch_size之外的维度
 num_features = 1
 for s in size:
 num_features *= s
 return num_features

训练模型那么肯定要先定义一个网络结构,如上定义一个前向传播网络。里面包含了卷积层、全连接层、最大池化层和 relu 非线性激活层(名字我自己取的)以及一个 view 展开,把一个多维的特征图平展成一维的。

其中nn.Conv2d(in_channels, out_channels, kernel_size),第一个参数是输入的深度,第二是输出的深度,第三是卷积核的尺寸。

F.max_pool2d(input, (pool_size, pool_size)),第二个参数是池话

nn.Linear(in_features, out_features)

x.view是平展的操作,不过实际上相当于 numpy 的 reshape,需要计算转换后的尺寸。

损失函数定义

import torch.optim as optim
 
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

模型定义完之后,意味着给出输入,就可以得到输出的结果。那么就来比较 outputs 和 targets 之间的区别,那么就需要用到损失函数来描述。

训练网络

for epoch in range(2): # loop over the dataset multiple times
 
 running_loss = 0.0
 for i, data in enumerate(trainloader, 0):
 # get the inputs; data is a list of [inputs, labels]
 inputs, labels = data
 
 # zero the parameter gradients
 optimizer.zero_grad()
 
 # forward + backward + optimize
 outputs = net(inputs)
 loss = criterion(outputs, labels)
 loss.backward()
 optimizer.step()
 
 # print statistics
 running_loss += loss.item()
 if i % 2000 == 1999: # print every 2000 mini-batches
  print('[%d, %5d] loss: %.3f' %
   (epoch + 1, i + 1, running_loss / 2000))
  running_loss = 0.0
 
print('Finished Training')

以上的代码是官方教程中给出来的,我们要做的就是学习他的思路。

1.首先是 epoch 的数量为 2,每个 epoch 都会历遍一次整个训练集。在每个 epoch 内累积统计 running_loss,每 2000 个 batch 数据计算一次损失的平均值,然后 print 再重新将 running_loss 置为 0。

2.然后分 mini-batch 进行训练,在每个计算每个 mini-batch 的损失之前,都会将优化器 optimizer 中的梯度清空,防止不同 mini-batch 的梯度被累加到一起。更新分成两步:第一步计算损失函数,然后把总的损失分配到各个层中,即 loss.backward(),然后就使用优化器更新权重,即 optimizer.step()。

保存模型

PATH = '...'
torch.save(net.state_dict(), PATH)

爬坑总结

总的来说流程就是上面那几步,但自己做的时候就遇到了挺多问题,最主要是对于其中张量传播过程中的要求不清楚,导致出了不少错误。

首先是输入的数据,pytorch 默认图片的 batch 数据的结构是(BATCH_SIZE, CHANNELS, IMG_H, IMG_W),所以要在生成数据时做一些调整,满足这种 BCHW 的规则。

会经常出现一些某个矩阵或者张量要求的数据,例如 “RuntimeError: Expected object of scalar type Double but got scalar type Float for argument #2 ‘mat2'” 等错误信息。

可以使用 x.double(),y.float(),z.long() 等方式转换成他要求的格式。

RuntimeError: multi-target not supported。这个错误出现在损失函数那个地方,对于分类问题肯定是优先考虑交叉熵。

criterion = nn.CrossEntropyLoss()
loss = criterion(outputs, labels.long())#报错的地方

当我batch-size=1时这个地方不会报错,但是当batch-size>1时就会报错。

查了别人的代码,大家基本都是和官方教程里面写的一样,使用官方的 mnist 数据接口,代码如下。一开始我是不愿意的,因为那样子意味着可能数据格式被封装起来看不见,但是自己折腾成本比较高,所以还是试了,真香!

train_dataset = datasets.MNIST(root='./data/',
    train=True,
    transform=transforms.ToTensor(),
    download=True)
train_loader = DataLoader(dataset = train_dataset,
  batch_size = 4,
  shuffle = True)

打印了一下从生成器中获得数据,看一下 size,发现果然和我自己写的不同。当 batch_size=4 时,数据 data.size() 都是4*1*28*28,这个是相同的;但是 labels.size() 是不同的,我写的是 one_hot 向量所以是 4*10,但它的是 4。

直接打印 labels 看看,果然,是单个指,例如 tensor([3, 2, 6, 2]) 这样。

不过模型的 outputs 依然是 4*10,看来是 nn.CrossEntropyLoss() 这个函数自己会做计算,所以他才会报错说 multi-target not supported,因为 lables.size() 不对,原本只有一个数字,但现在是10个数字,相当于被分配了10个属性,自然就报错啦。

所以稍微修改了自己写的生成器之后,就没问题了。

不过,如果想要更自由的调用数据,还是需要对对象进行一些方法的重载,使用 pytoch 定义的 DataLoader,用 enumerate,就会把所有的数据历遍一次,如果使用 iter() 得到一个可迭代对象之后 next(),并不可以像 tensorflow 那样子生成训练数据。

例如说,如果使用如上的形式,DataLoader 得到的是一个生成器,python 中的生成器对象主要有 __next__ 和 __iter__ 等魔术方法决定。

__iter__ 方法使得实例可以如下调用,可以得到一个可迭代对象,iterable,但是如果不加也没关系,因为更重要的是 __next__ 类方法。

如下自己写了 __next__ 方法之后就可以看到,原本会出现越界的现象不见了,可以循环的历遍数据,当然也可以想被注释的那部分一样,抛出 StopIteration 来终止。

a = A()
a_iter = iter(a)
class A():
 def __init__(self):
 self.list = [1,2,3]
 self.index = 0
 #def __getitem__(self, index):
 # return self.list[i]
 #def __iter__(self):
 # return self
 def __next__(self):
 #for i in range():
 if self.index >= len(self.list):
 #raise StopIteration 
 self.index = self.index%len(self.list)
 result = self.list[self.index]
 self.index += 1
 return result 
b = A() 
for i in range(20):
 print(next(b))

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。如有错误或未考虑完全的地方,望不吝赐教。

Python 相关文章推荐
在Docker上部署Python的Flask框架的教程
Apr 08 Python
详解在Python程序中使用Cookie的教程
Apr 30 Python
Python中列表、字典、元组数据结构的简单学习笔记
Mar 20 Python
Python实现简易端口扫描器代码实例
Mar 15 Python
TF-IDF算法解析与Python实现方法详解
Nov 16 Python
python机器学习之神经网络(三)
Dec 20 Python
详解python的argpare和click模块小结
Mar 31 Python
使用虚拟环境打包python为exe 文件的方法
Aug 29 Python
python flask中动态URL规则详解
Nov 22 Python
windows上彻底删除jupyter notebook的实现
Apr 13 Python
Pytorch mask-rcnn 实现细节分享
Jun 24 Python
django教程如何自学
Jul 31 Python
Pytorch模型迁移和迁移学习,导入部分模型参数的操作
Mar 03 #Python
pytorch 实现L2和L1正则化regularization的操作
Mar 03 #Python
Pytorch自定义Dataset和DataLoader去除不存在和空数据的操作
Mar 03 #Python
python爬取youtube视频的示例代码
Mar 03 #Python
pytorch Dataset,DataLoader产生自定义的训练数据案例
Mar 03 #Python
解决pytorch 数据类型报错的问题
Mar 03 #Python
python反编译教程之2048小游戏实例
Mar 03 #Python
You might like
phpmyadmin 3.4 空密码登录的实现方法
2010/05/29 PHP
php实现的发送带附件邮件类实例
2014/09/22 PHP
PHP中的命名空间相关概念浅析
2015/01/22 PHP
PHP中rename()函数的妙用讲解
2019/02/28 PHP
Avengerls vs Newbee BO3 第一场2.18
2021/03/10 DOTA
IE6浏览器下resize事件被执行了多次解决方法
2012/12/11 Javascript
如何使Chrome控制台支持多行js模式——意外发现
2013/06/13 Javascript
javascript解决IE6下hover问题的方法
2015/07/28 Javascript
javascript:void(0)是什么意思及href=#与href=javascriptvoid(0)的区别
2015/11/13 Javascript
JavaScript添加随滚动条滚动窗体的方法
2016/02/23 Javascript
JQuery EasyUI的使用
2016/02/24 Javascript
js图片上传前预览功能(兼容所有浏览器)
2016/08/24 Javascript
基于vue实现网站前台的权限管理(前后端分离实践)
2018/01/13 Javascript
JS实现图片旋转动画效果封装与使用示例
2018/07/09 Javascript
ligerUI的ligerDialog关闭刷新的方法
2019/09/27 Javascript
基于JS实现计算24点算法代码实例解析
2020/07/23 Javascript
vue.js封装switch开关组件的操作
2020/10/26 Javascript
Python 调用VC++的动态链接库(DLL)
2008/09/06 Python
Django rest framework工具包简单用法示例
2018/07/20 Python
python的faker库用法
2019/11/28 Python
Python函数式编程实例详解
2020/01/17 Python
python实现单张图像拼接与批量图片拼接
2020/03/23 Python
Python yield生成器和return对比代码实例
2020/04/20 Python
为什么相对PHP黑python的更少
2020/06/21 Python
python实现取余操作的简单实例
2020/08/16 Python
全球酒店预订网站:Hotels.com
2016/08/10 全球购物
美国眼镜网站:EyeBuyDirect
2017/04/13 全球购物
美国气象仪器、花园装饰和墙壁艺术商店:Wind & Weather
2019/05/29 全球购物
Yummie官方网站:塑身衣和衣柜必需品
2019/10/29 全球购物
公司管理建议书范文
2014/03/12 职场文书
结婚喜宴主持词
2014/03/14 职场文书
利群广告词
2014/03/20 职场文书
党支部党的群众路线对照检查材料
2014/09/24 职场文书
2014年学校教学工作总结
2014/12/06 职场文书
市场营销计划书范文
2015/01/16 职场文书
毕业纪念册寄语大全
2015/02/26 职场文书