解决pytorch 模型复制的一些问题
直接使用
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生成器。
classMyDataset(Dataset): def__init__(self,data,labels): self.data=data self.labels=labels def__getitem__(self,index): returnself.data[index],self.labels[index] def__len__(self): returnlen(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)
模型定义
classNet(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) defforward(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) returnx defnum_flat_features(self,x): size=x.size()[1:]#除了batch_size之外的维度 num_features=1 forsinsize: num_features*=s returnnum_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,需要计算转换后的尺寸。
损失函数定义
importtorch.optimasoptim criterion=nn.CrossEntropyLoss() optimizer=optim.SGD(net.parameters(),lr=0.001,momentum=0.9)
模型定义完之后,意味着给出输入,就可以得到输出的结果。那么就来比较outputs和targets之间的区别,那么就需要用到损失函数来描述。
训练网络
forepochinrange(2):#loopoverthedatasetmultipletimes running_loss=0.0 fori,datainenumerate(trainloader,0): #gettheinputs;dataisalistof[inputs,labels] inputs,labels=data #zerotheparametergradients optimizer.zero_grad() #forward+backward+optimize outputs=net(inputs) loss=criterion(outputs,labels) loss.backward() optimizer.step() #printstatistics running_loss+=loss.item() ifi%2000==1999:#printevery2000mini-batches print('[%d,%5d]loss:%.3f'% (epoch+1,i+1,running_loss/2000)) running_loss=0.0 print('FinishedTraining')
以上的代码是官方教程中给出来的,我们要做的就是学习他的思路。
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:ExpectedobjectofscalartypeDoublebutgotscalartypeFloatforargument#2‘mat2'”等错误信息。
可以使用x.double(),y.float(),z.long()等方式转换成他要求的格式。
RuntimeError:multi-targetnotsupported。这个错误出现在损失函数那个地方,对于分类问题肯定是优先考虑交叉熵。
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-targetnotsupported,因为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) classA(): def__init__(self): self.list=[1,2,3] self.index=0 #def__getitem__(self,index): #returnself.list[i] #def__iter__(self): #returnself def__next__(self): #foriinrange(): ifself.index>=len(self.list): #raiseStopIteration self.index=self.index%len(self.list) result=self.list[self.index] self.index+=1 returnresult b=A() foriinrange(20): print(next(b))
以上为个人经验,希望能给大家一个参考,也希望大家多多支持毛票票。如有错误或未考虑完全的地方,望不吝赐教。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。