解决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中实现从目录中过滤出指定文件类型的文件
Feb 02 Python
Python通过90行代码搭建一个音乐搜索工具
Jul 29 Python
Python使用Paramiko模块编写脚本进行远程服务器操作
May 05 Python
Python中进程和线程的区别详解
Oct 29 Python
python使用tcp实现局域网内文件传输
Mar 20 Python
Linux下python3.6.1环境配置教程
Sep 26 Python
python爬取网易云音乐评论
Nov 16 Python
几行Python代码爬取3000+上市公司的信息
Jan 24 Python
Python利用lxml模块爬取豆瓣读书排行榜的方法与分析
Apr 15 Python
pyqt5 使用cv2 显示图片,摄像头的实例
Jun 27 Python
Numpy对数组的操作:创建、变形(升降维等)、计算、取值、复制、分割、合并
Aug 28 Python
Python数据分析之pandas函数详解
Apr 21 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
一个可以找出源代码中所有中文的工具
2006/10/25 PHP
PHP测试程序运行时间的类
2012/02/05 PHP
mac下Apache + MySql + PHP搭建网站开发环境
2014/06/02 PHP
9个比较实用的php代码片段
2016/03/15 PHP
不同浏览器对回车提交表单的处理办法
2010/02/13 Javascript
jQuery 中使用JSON的实现代码
2011/12/01 Javascript
js去除重复字符串两种实现方法
2013/01/09 Javascript
jquery数组封装使用方法分享(jquery数组遍历)
2014/03/25 Javascript
js+html5实现canvas绘制圆形图案的方法
2015/06/05 Javascript
jQuery实现延迟跳转的方法
2015/06/05 Javascript
jQuery多级手风琴菜单实例讲解
2015/10/22 Javascript
javascript倒计时效果实现
2015/11/12 Javascript
谷歌Chrome浏览器扩展程序开发小记
2016/01/06 Javascript
BootstrapTable请求数据时设置超时(timeout)的方法
2017/01/22 Javascript
js+html5实现复制文字按钮
2017/07/15 Javascript
nodejs异步编程基础之回调函数用法分析
2018/12/26 NodeJs
微信小程序 弹窗输入组件的实现解析
2019/08/12 Javascript
浅谈Three.js截图并下载的大坑
2019/11/01 Javascript
javascript代码实现简易计算器
2021/01/25 Javascript
[14:19]2018年度COSER大赛-完美盛典
2018/12/16 DOTA
python实现telnet客户端的方法
2015/04/15 Python
Python中datetime常用时间处理方法
2015/06/15 Python
深入理解Python3 内置函数大全
2017/11/23 Python
Python将多个excel文件合并为一个文件
2018/01/03 Python
解决pyecharts在jupyter notebook中使用报错问题
2020/04/23 Python
解决python super()调用多重继承函数的问题
2019/06/26 Python
python实现PCA降维的示例详解
2020/02/24 Python
美国肌肉和力量商店:Muscle & Strength
2019/06/22 全球购物
大学学年自我鉴定
2013/10/28 职场文书
班组长安全职责
2014/01/05 职场文书
打造高效课堂实施方案
2014/03/22 职场文书
2015年基层党支部工作总结
2015/05/21 职场文书
让子弹飞观后感
2015/06/11 职场文书
刘胡兰观后感
2015/06/16 职场文书
安全教育主题班会总结
2015/08/14 职场文书
MySQL 慢查询日志深入理解
2021/04/22 MySQL