解决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 相关文章推荐
Python写的一个简单DNS服务器实例
Jun 04 Python
Python多线程结合队列下载百度音乐的方法
Jul 27 Python
Python Requests 基础入门
Apr 07 Python
python opencv 直方图反向投影的方法
Feb 24 Python
Pycharm之快速定位到某行快捷键的方法
Jan 20 Python
ipython和python区别详解
Jun 26 Python
Django+zTree构建组织架构树的方法
Aug 21 Python
Python使用PyQt5/PySide2编写一个极简的音乐播放器功能
Feb 07 Python
Django 5种类型Session使用方法解析
Apr 29 Python
Python selenium爬虫实现定时任务过程解析
Jun 08 Python
基于Python3读写INI配置文件过程解析
Jul 23 Python
手把手教你如何用Pycharm2020.1.1配置远程连接的详细步骤
Aug 07 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
main.php
2006/12/09 PHP
php 读取shell管道传输过来的内容
2010/03/01 PHP
php中通过curl检测页面是否被百度收录
2013/09/27 PHP
PHP APC缓存配置、使用详解
2014/03/06 PHP
php中Session的生成机制、回收机制和存储机制探究
2014/08/19 PHP
php递归json类实例
2014/12/02 PHP
Zend Framework教程之Application用法实例详解
2016/03/14 PHP
thinkphp5引入公共部分header、footer的方法详解
2018/09/14 PHP
Jquery弹出窗口插件 LeanModal的使用方法
2012/03/10 Javascript
jquery实现浮动的侧栏实例
2015/06/25 Javascript
jQuery+HTML5美女瀑布流布局实现方法
2015/09/21 Javascript
Web开发中客户端的跳转与服务器端的跳转的区别
2017/03/05 Javascript
JS字符串false转boolean的方法(推荐)
2017/03/08 Javascript
Vue项目中quill-editor带样式编辑器的使用方法
2017/08/08 Javascript
nodejs基于express实现文件上传的方法
2018/03/19 NodeJs
在create-react-app中使用sass的方法示例
2018/10/01 Javascript
vue + element-ui的分页问题实现
2018/12/17 Javascript
python根据路径导入模块的方法
2014/09/30 Python
浅谈python中的正则表达式(re模块)
2017/10/17 Python
Python 查看list中是否含有某元素的方法
2018/06/27 Python
Django添加feeds功能的示例
2018/08/07 Python
python实现递归查找某个路径下所有文件中的中文字符
2019/08/31 Python
python 实现视频 图像帧提取
2019/12/10 Python
TensorFlow梯度求解tf.gradients实例
2020/02/04 Python
Pytorch 使用不同版本的cuda的方法步骤
2020/04/02 Python
Python实现Word表格转成Excel表格的示例代码
2020/04/16 Python
python爬虫把url链接编码成gbk2312格式过程解析
2020/06/08 Python
python 中的命名空间,你真的了解吗?
2020/08/19 Python
美国复古街头服饰精品店:Need Supply Co.
2017/02/22 全球购物
澳大利亚家具和家居用品购物网站:Zanui
2018/12/29 全球购物
莫斯科的韩国化妆品店:Sifo
2019/12/04 全球购物
模特职业生涯规划范文
2014/02/26 职场文书
安全责任书怎么写
2014/07/28 职场文书
倡议书格式
2014/08/30 职场文书
司法局群众路线教育实践活动开展情况总结
2014/10/25 职场文书
2014年项目经理工作总结
2014/11/24 职场文书