解决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简单分割文件的方法
Jul 30 Python
详解Python 数据库 (sqlite3)应用
Dec 07 Python
python类的继承实例详解
Mar 30 Python
Pycharm学习教程(2) 代码风格
May 02 Python
利用python实现简单的邮件发送客户端示例
Dec 23 Python
详解Python 协程的详细用法使用和例子
Jun 15 Python
python中pika模块问题的深入探究
Oct 13 Python
详解Python3中ceil()函数用法
Feb 19 Python
Python实现FLV视频拼接功能
Jan 21 Python
如何实现更换Jupyter Notebook内核Python版本
May 18 Python
详解Go语言运用广度优先搜索走迷宫
Jun 23 Python
Python OpenCV实现图像模板匹配详解
Apr 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
phpMyAdmin 安装及问题总结
2009/05/28 PHP
php字符比较函数similar_text、strnatcmp与strcasecmp用法分析
2014/11/18 PHP
MacOS 安装 PHP的图片裁剪扩展Tclip
2015/03/25 PHP
php读取本地json文件的实例
2018/03/07 PHP
PhpStorm本地断点调试的方法步骤
2018/05/21 PHP
JavaScript 继承详解(三)
2009/07/13 Javascript
JavaScript 内置对象属性及方法集合
2010/07/04 Javascript
validator验证控件使用代码
2010/11/23 Javascript
编写简单的jQuery提示插件
2014/12/21 Javascript
封装了jQuery的Ajax请求全局配置
2015/02/05 Javascript
jQuery中通过ajax调用webservice传递数组参数的问题实例详解
2016/05/20 Javascript
jQuery实现的跨容器无缝拖动效果代码
2016/06/21 Javascript
mint-ui在vue中的使用示例
2018/04/05 Javascript
npm scripts 使用指南详解
2018/10/08 Javascript
webpack项目使用eslint建立代码规范实现
2019/05/16 Javascript
在vue中利用全局路由钩子给url统一添加公共参数的例子
2019/11/01 Javascript
使用Vue生成动态表单
2019/11/26 Javascript
python k-近邻算法实例分享
2014/06/11 Python
Python环境下搭建属于自己的pip源的教程
2016/05/05 Python
Python算法之求n个节点不同二叉树个数
2017/10/27 Python
python 简单搭建阻塞式单进程,多进程,多线程服务的实例
2017/11/01 Python
Python中安装easy_install的方法
2018/11/18 Python
python 利用pandas将arff文件转csv文件的方法
2019/02/12 Python
python pandas移动窗口函数rolling的用法
2020/02/29 Python
matplotlib实现数据实时刷新的示例代码
2021/01/05 Python
Pycharm 如何一键加引号的方法步骤
2021/02/05 Python
谷歌浏览器小字体处理方案即12px以下字体
2013/12/17 HTML / CSS
科沃斯机器人官网商城:Ecovacs
2016/08/29 全球购物
软件配置管理有什么好处
2015/04/15 面试题
思想品德课教学反思
2014/02/10 职场文书
负责人任命书范本
2014/06/04 职场文书
商场父亲节活动方案
2014/08/27 职场文书
四风对照检查材料范文
2014/09/27 职场文书
2014向国旗敬礼网上签名活动总结
2014/09/27 职场文书
2016年员工年度考核评语
2015/12/02 职场文书
Python中request的基本使用解决乱码问题
2022/04/12 Python