上文回顾,目前已经揭晓了l2正则化以及如何画图,这一章主要是为了重构代码,代码断点重续、增加动态图转静态图的操作,这两块将合并起来一起写,不多说,先上代码
network.py
from paddle.nn.layer import Conv2D,MaxPool2D,Linear
import paddle.nn.functional as F
class MNIST(paddle.nn.Layer):
def __init__(self):
super(MNIST,self).__init__()
nn.initializer.set_global_initializer(nn.initializer.Uniform(),nn.initializer.Constant())
# 卷积层
self.conv1=Conv2D(in_channels=1,out_channels=20,kernel_size=5,padding=2)
# 池化层
self.max_pool1=MaxPool2D(kernel_size=2,stride=2)
# 卷积层
self.conv2=Conv2D(in_channels=20,out_channels=20,kernel_size=5,padding=2)
# 池化层
self.max_pool2=MaxPool2D(kernel_size=2,stride=2)
#线性层
self.fc=Linear(in_features=980,out_features=10)
# 新增一个装饰器,使得动态图网络结构在静态图模式下运行
@paddle.jit.to_static
def forward(self,inputs):
x=self.conv1(inputs)
x=F.relu()
x=self.max_pool1(x)
x=self.conv2(x)
x=F.relu()
x=self.max_pool2(x)
x=paddle.reshape(x,[x.shape[0],980])
x=self.fc(x)
return x
动态图转静态图是对网络的forward函数进行操作,其目的是为后续部署做好模型转换的准备
trainer.py
这里的设计思路是,首先这个Trainer接收的是模型的保存路径,模型初始化以及优化器等,然后这个类包含以下功能:
import paddle
import paddle.nn.functional as F
import os
class Trainer(object):
def __init__(self,model_path,model,optimizer):
self.model_path=model_path
self.model=model
self.optimizer=optimizer
def save(self):
paddle.save(self.model.state_dict(),self.model_path)
def norm_img(img):
batch_size=img.shape[0]
img=img/127.5-1
img=paddle.reshape(img,[batch_size,784])
return img
def train_step(self,data):
images=self.norm_img(data[0]).astype('float32')
labels=data[1].astype('int64')
predicts=self.model(images)
loss=F.cross_entropy(predicts,labels)
avg_loss=paddle.mean(loss)
avg_loss.backward()
self.optimizer.step()
self.optimizer.clear_grad()
return avg_loss
def train_epoch(self,datasets,epoch):
self.model.train()
for batch_id,data in enumerate(datasets()):
loss=self.train_step(data)
if batch_id % 500 ==0:
print('epoch_id:{}, batch_id: {} loss is :{}'.format(epoch,batch_id,loss.numpy()))
def train(self,train_datasets,start_epoch,end_epoch,save_path):
if not os.path.exists(save_path):
os.makedirs(save_path)
for i in range(start_epoch,end_epoch):
self.train_epoch(train_datasets,i)
paddle.save(self.optimizer.state_dict(),'./{}/mnist_epoch{}'.format(save_path,i)+".pdopt"
paddle.save(self.model.state_dict(),'./{}/mnist_epoch{}',format(save_path,i)+".pdparams")
self.save()
测试代码
import paddle
use_gpu=True
paddle.set_device("gpu:0") if use_gpu else paddle.set_device("cpu")
import warnings
warnings.filterwarnings('ignore')
paddle.seed(1024)
epochs=3
BATCH_SIZE=32
model_path='./mnist_pdparams'
paddle.vision.set_image_backend('cv2')
train_dataset=paddle.io.DataLoader(paddle.vision.datasets.MNIST(mode='train'),batch_size=16,shuffle=True)
model=MNIST()
total_steps=(int(50000//BATCH_SIZE)+1)*epochs
lr=paddle.optimizer.lr.PolynomialDecay(learning_rate=lr,parameters=model.parameters())
opt=paddle.optimizer.Momentum(learning_rate=lr,parameters=model.parameters())
trainer=Trainer(model_path=model_path,model=model,optimizer=opt)
trainer.train(train_datasets=train_loader,start_epoch=0,end_epoch=epochs,save_path='checkpoint')
然后训练完成后,会生成这些断点模型
然后这里如果要进行断点训练,可进行如下操作
import paddle
import warnings
warnings.filterwarnings('ignore')
paddle.seed(1024)
epochs=3
BATCH_SIZE=32
total_steps=(int(50000//BATCH_SIZE)+1)*epochs
lr=paddle.optimizer.lr.PolynomialDecay(learning_rate=lr,parameters=model.parameters())
opt=paddle.optimizer.Momentum(learning_rate=lr,parameters=model.parameters())
'''导入断点权重'''
param_dict=paddle.load('./checkpoint/mnist_epoch0.pdparams')
opt_dict=paddle.load('./checkpoint/mnist_epoch0.pdopt')
model.set_state_dict(param_dict)
opt.set_state_dict(opt_dict)
trainer=Trainer(model_path=model_path,model=model,optimizer=opt)
trainer.train(train_datasets=train_loader,start_epoch=0,end_epoch=epochs,save_path='checkpoint')
在推理&部署场景中,需要同时保存推理模型的结构和参数,但是动态图是即时执行即时得到结果,并不会记录模型的结构信息。动态图在保存推理模型时,需要先将动态图模型转换为静态图写法,编译得到对应的模型结构再保存,而飞桨框架2.0版本推出paddle.jit.save和paddle.jit.load接口,无需重新实现静态图网络结构,直接实现动态图模型转成静态图模型格式。
然后这里如果要进行动态转静态,然后进行推理可以进行如下两步
model=MNIST()
state_dict=paddle.load('./mnist.pdparams')
model.set_state_dict(state_dict) # 将训练好的参数读取到网络中
model.eval()
paddle.jit.save(layer=model,path='inference/mnist',input_spec=[InputSpec(shape=[None,784],dtype='float32')])
这是时候,会自动生成inference文件夹,然后把模型存的这里
具体包括三种文件:保存模型结构的*.pdmodel文件;保存推理用参数的*.pdiparams文件和保存兼容变量信息的*.pdiparams.info文件,这几个文件后缀均为paddle.jit.save保存时默认使用的文件后缀。
2. 推理
import paddle
import numpy as np
import paddle.nn.functional as F
def norm_img(img):
batch_size=img.shape[0]
img=img/127.5-1
img=paddle.reshape(img,[batch_size,784])
return img
paddle.vision.set_image_backend('cv2')
mnist_test=paddle.vision.datasets.MNIST(mode='test')
test_image,label=mnist_test[1]
print('The label of readed image is:',label)
test_image=paddle.reshape(paddle.to_tensor(test_image),[1,784])
test_image=norm_img(test_image)
loaded_model=paddle.jit.load('./inference/mnist')
preds=loaded_model(test_image)
pred_label=paddle.argmax(preds)
print('The predicted label is:',pred_label.numpy())
结果:
The predicted label is: [2]
The label of readed image is: [2]