numpy最详细教程
使⽤Pytorch搭建模型的步骤
  本来是只⽤Tenorflow的,但是因为TF有些Numpy特性并不⽀持,⽐如对数组使⽤列表进⾏切⽚,所以只能转战Pytorch 了(pytorch是⽀持的)。还好Pytorch⽐较容易上⼿,⼏乎完美复制了Numpy的特性(但还有⼀些特性不⽀持),怪不得热度上升得这么快。
1  模型定义
  和TF很像,Pytorch也通过继承⽗类来搭建⾃定义模型,同样也是实现两个⽅法。在TF中是__init__()和call(),在Pytorch 中则是__init__()和forward()。功能类似,都分别是初始化模型内部结构和进⾏推理。其它功能⽐如计算loss和训练函数,你也可以继承在⾥⾯,当然这是可选的。下⾯搭建⼀个判别MNIST⼿写字的Demo,⾸先给出模型代码:
import numpy as np
import matplotlib.pyplot as plt
import torch
from torch import nn,optim
from torchsummary import summary
from keras.datasets import mnist
from keras.utils import to_categorical
device = torch.device('cuda') #——————1——————
class ModelTest(nn.Module):
def __init__(self,device):
super().__init__()
self.layer1 = nn.Sequential(nn.Flatten(),nn.Linear(28*28,512),nn.ReLU())#——————2——————
self.layer2 = nn.Sequential(nn.Linear(512,512),nn.ReLU())
self.layer3 = nn.Sequential(nn.Linear(512,512),nn.ReLU())
self.layer4 = nn.Sequential(nn.Linear(512,10),nn.Softmax())
<(device) #——————3——————
self.opt = optim.SGD(self.parameters(),lr=0.01)#——————4——————
def forward(self,inputs): #——————5——————
x = self.layer1(inputs)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
return x
def get_loss(self,true_labels,predicts):
loss = -true_labels * torch.log(predicts) #——————6——————
loss = an(loss)
return loss
def train(self,imgs,labels):
predicts = model(imgs)
loss = _loss(labels,predicts)
_grad()#——————7——————
loss.backward()#——————8——————
self.opt.step()#——————9——————
model = ModelTest(device)
summary(model,(1,28,28),3,device='cuda') #——————10——————
  #1:获取设备,以⽅便后⾯的模型与变量进⾏内存迁移,设备名只有两种:'cuda'和'cpu'。通常是在你有GPU的情况下需要这样显式进⾏设备的设置,从⽽在需要时,你可以将变量从主存迁移到显存中。如果没有GPU,不获取也没事,pytorch会默认将参数都保存在主存中。
  #2:模型中层的定义,可以使⽤Sequential将想要统⼀管理的层集中表⽰为⼀层。
  #3:在初始化中将模型参数迁移到GPU显存中,加速运算,当然你也可以在需要时在外部执⾏(device)进⾏迁移。
  #4:定义模型的优化器,和TF不同,pytorch需要在定义时就将需要梯度下降的参数传⼊,也就是其中的
self.parameters(),表⽰当前模型的所有参数。实际上你不⽤担⼼定义优化器和模型参数的顺序问题,因为self.parameters()的输出并不是模型参数的实例,⽽是整个模型参数对象的指针,所以即使你在定义优化器之后⼜定义了⼀个层,它依然能优化到。当然优化器你也可以在外部定义,传⼊model.parameters()即可。这⾥定义了⼀个随机梯度下降。
  #5:模型的前向传播,和TF的call()类似,定义好model()所执⾏的就是这个函数。
  #6:我将获取loss的函数集成在了模型中,这⾥计算的是真实标签和预测标签之间的交叉熵。
  #7/8/9:在TF中,参数梯度是保存在梯度带中的,⽽在pytorch中,参数梯度是各⾃集成在对应的参数中的,可以使⽤ad来查看。每次对loss执⾏backward(),pytorch都会将参与loss计算的所有可训练参数关于loss的梯度叠加进去(直
接相加)。所以如果我们没有叠加梯度的意愿的话,那就要在backward()之前先把之前的梯度删除。⼜因为我们前⾯已经把待训练的参数都传⼊了优化器,所以,对优化器使⽤zero_grad(),就能把所有待训练参数中已存在的梯度都清零。那么梯度叠加什么时候⽤到呢?⽐如批量梯度下降,当内存不够直接计算整个批量的梯度时,我们只能将批量分成⼀部分⼀部分来计算,每算⼀个部分得到loss就backward()⼀次,从⽽得到整个批量的梯度。梯度计算好后,再执⾏优化器的step(),优化器根据可训练参数的梯度对其执⾏⼀步优化。
  #10:使⽤torchsummary函数显⽰模型结构。奇怪为什么不把这个继承在torch⾥⾯,要重新安装⼀个torchsummary库。
2  训练及可视化
  接下来使⽤模型进⾏训练,因为pytorch⾃带的MNIST数据集并不好⽤,所以我使⽤的是Keras⾃带的,定义了⼀个获取数据的⽣成器。下⾯是完整的训练及绘图代码(50次迭代记录⼀次准确率):
import numpy as np
import matplotlib.pyplot as plt
import torch
from torch import nn,optim
from torchsummary import summary
from keras.datasets import mnist
from keras.utils import to_categorical
device = torch.device('cuda') #——————1——————
class ModelTest(nn.Module):
def __init__(self,device):
super().__init__()
self.layer1 = nn.Sequential(nn.Flatten(),nn.Linear(28*28,512),nn.ReLU())#——————2——————
self.layer2 = nn.Sequential(nn.Linear(512,512),nn.ReLU())
self.layer3 = nn.Sequential(nn.Linear(512,512),nn.ReLU())
self.layer4 = nn.Sequential(nn.Linear(512,10),nn.Softmax())
<(device) #——————3——————
self.opt = optim.SGD(self.parameters(),lr=0.01)#——————4——————
def forward(self,inputs): #——————5——————
x = self.layer1(inputs)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
return x
def get_loss(self,true_labels,predicts):
loss = -true_labels * torch.log(predicts) #——————6——————
loss = an(loss)
return loss
def train(self,imgs,labels):
predicts = model(imgs)
loss = _loss(labels,predicts)
_grad()#——————7——————
loss.backward()#——————8——————
self.opt.step()#——————9——————
def get_data(device,is_train = True, batch = 1024, num = 10000):
train_data,test_data = mnist.load_data()
if is_train:
imgs,labels = train_data
else:
imgs,labels = test_data
imgs = (imgs/255*2-1)[:,np.newaxis,...]
labels = to_categorical(labels,10)
imgs = sor(imgs,dtype=torch.float32).to(device)
labels = sor(labels,dtype=torch.float32).to(device)
i = 0
while(True):
i += batch
if i > num:
i = batch
yield imgs[i-batch:i],labels[i-batch:i]
train_dg = get_data(device, True,batch=4096,num=60000)
test_dg = get_data(device, False,batch=5000,num=10000)
model = ModelTest(device)
summary(model,(1,28,28),11,device='cuda')
ACCs = []
import time
start = time.time()
for j in range(20000):
#训练
imgs,labels = next(train_dg)
#验证
img,label = next(test_dg)
predicts = model(img)
acc = 1 - unt_nonzero(torch.argmax(predicts,axis=1) - torch.argmax(label,axis=1))/label.shape[0]
if j % 50 == 0:
t = time.time() - start
start = time.time()
ACCs.append(acc.cpu().numpy())
print(j,t,'ACC: ',acc)
#绘图
x = np.linspace(0,len(ACCs),len(ACCs))
plt.plot(x,ACCs)
  准确率变化图如下:
3  其它使⽤技巧
3.1  tensor与array
  需要注意的是,pytorch的tensor基于numpy的array,它们是共享内存的。也就是说,如果你把tensor直接插⼊⼀个列表,当你修改这个tensor时,列表中的这个tensor也会被修改;更容易被忽略的是,即使你⽤tensor.detach.numpy(),先将tensor 转换为array类型,再插⼊列表,当你修改原本的tensor时,列表中的这个array也依然会被修改。所以如果我们只是想保存tensor的值⽽不是整个对象,就要使⽤np.array(tensor)将tensor的值复制出来。
3.2  ⾃定义层
  在TF中,⾃定义模型通常继承keras的Model,⽽⾃定义层则是继承layers.Layer,继承不同的⽗类通常会造成初学者的困扰。⽽在pytorch中,⾃定义层与⾃定义模型⼀样,都是继承nn.Module。Pytorch将层与模型都看成了模块,这很容易理解。的确,层与模型之间本来也没有什么明确的界限。并且定义⽅式与上⾯定义模型的⽅式⼀样,也是实现两个函数即可。代码⽰例如下:
import torch
from torch import nn
class ParaDeconv(nn.Module):#——————1——————
def __init__(self,in_n,out_n):
super().__init__()
self.w = nn.al(0,0.01,size = [in_n,out_n]),requires_grad=True)
self.b = nn.al(0,0.01,size = [out_n]),requires_grad=True)
def forward(self,inputs):
x = torch.matmul(inputs,self.w)
x = x + self.b
return x
layer = ParaDeconv(2,3)
y = s(100,2))#——————2——————
loss = torch.sum(y)#——————3——————
loss.backward()#——————4——————
for i in layer.parameters():#——————5——————
ad)#——————6——————
  #1:⾃定义⼀个全连接层。层中可训练参数的定义是使⽤nn.Parameter,如果直接使⽤sor是⽆法在#5中遍历到的。
  #2/3/4:输⼊并计算loss,然后反向传播计算参数梯度。
  #5/6:输出完成反向传播后层参数的梯度。
  以上定义的层可以和pytorch⾃带的层⼀样直接插⼊模型中使⽤。
3.3  保存/加载
3.3.1  保存/加载模型
  有两种⽅式,⼀种是保存模型的参数:
torch.save(model.state_dict(), PATH)        #保存
model.load_state_dict(torch.load(PATH),strict=True) #加载
  这种加载⽅式需要先定义模型,然后再加载参数。如果你定义的模型参数名与保存的参数对不上,就会出错。但如果把
strict修改成False,不严格匹配,它就会只匹配对应上的键值,不会因多出或缺少的参数⽽报错。
  另⼀种是直接保存模型:
torch.save(model, PATH) #保存
model = torch.load(PATH) #加载
  这种⽅式看似⽅便,实际上更容易出错。因为python不能保存整个模型的类,所以它只能保存定义类的代码⽂件位置,以在加载时获取类的结构。如果你改变了定义类的代码位置,就有可能因为不到类⽽出错。
3.3.2  保存训练点
  当你要保存某个训练阶段的状态,⽐如包含优化器参数、模型参数、训练迭代次数等,可以进⾏如下操作:
#保存训练点
torch.save({
'epoch': epoch,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'loss': loss
}, PATH)
#加载训练点
model = TheModelClass(*args, **kwargs)
optimizer = TheOptimizerClass(*args, **kwargs)
checkpoint = torch.load(PATH)
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
epoch = checkpoint['epoch']
loss = checkpoint['loss']
  和保存模型⼀样,也是使⽤torch.save()。它很灵活,可以保存字典,因此读取的时候也按照字典索引读取即可。当然要注意,并不是任何类型都能保存的,这⾥保存的四个类型分别是:
  1. int
  2. collections.OrderedDict
  3. collections.OrderedDict
  4. list
3.4  修改模型参数
  Pytorch没有提供额外的⽅式让我们修改模型参数,我们可以使⽤上⾯加载模型参数的⽅式来修改参数。对于某个参数,我们只要把键值和对应要修改的值放在字典中传⼊load_state_dict即可。如果没传⼊所有的参数,记得把strict设为False。⽰例如下:
model.load_state_dict({'weight':sor([0.])},strict=False) #修改模型参数
  参数名,也就是键值,和对应的参数shape可以通过model.state_dict()查看。
以上就是使⽤Pytorch搭建模型的步骤的详细内容,更多关于Pytorch搭建模型的资料请关注其它相关⽂章!