【PaddlePaddle】模型的保存与使用

前言

在之前PaddleFluid的实例分析中,一直是训练过后,进行预测就结束了,没有进行模型的保存。这篇文章就讲解了如何在训练过程中保存模型,用于之后进行预测或者继续训练模型。本章会介绍三种保存模型的方法。一种方法是保存预测模型,是以后用来进行预测的模型;另外两种方法用来保存初始化模型,用来初始化模型继续训练。
接下就在代码中看看他们怎么用吧!我们将使用残差神经网络对经典的cifar数据集做一个10分类的工作(cifar数据集有100分类、10分类两个选择,我们这个网络使用的是cifar的10分类)。

模型搭建

导入相关的模块

#coding:utf-8
'''
created on February 19 15:06 2019

@author:lhy
'''

import os
import shutil
import paddle
import paddle.dataset.cifar as cifar
import paddle.fluid as fluid

定义一个残差神经网络,这是目前比较常用的网络,它可以通过增加网络深度达到提高识别率。不像传统的神经网络,对于传统的神经网络,当网络深度加深时,会出现损失精度(比如梯度消失等问题)。

#定义一个残差神经网络(ResNet)
def resnet_cifar(ipt,class_dim):
	#定义一个卷积+批标准化层
	def conv_bn_layer(input,ch_out,filter_size,stride,padding,act='relu',bias_attr=False):
		tmp=fluid.layers.conv2d(input=input,filter_size=filter_size,num_filters=ch_out,stride=stride,padding=padding,bias_attr=bias_attr)
		return fluid.layers.batch_norm(input=tmp,act=act)
	
	def shortcut(input,ch_in,ch_out,stride):
		if ch_in!=ch_out:
			return conv_bn_layer(input,ch_out,1,stride,0,None)
		else:
			return input
	#残差块
	def basicblock(input,ch_in,ch_out,stride):
		temp=conv_bn_layer(input,ch_out,3,stride,1)
		temp=conv_bn_layer(temp,ch_out,3,1,1,act=None,bias_attr=True)
		#如果输入层和输出层的大小不一样,就经过一个conv_bn,将输入层的大小变得和输出层一样
		short=shortcut(input,ch_in,ch_out,stride)
		#将两次卷积批标准化的结果和输入层相加,达到学习残差的目的
		return fluid.layers.elementwise_add(x=temp,y=short,act='relu')
	

	def layer_warp(block_func,input,ch_in,ch_out,count,stride):
		temp=block_func(input,ch_in,ch_out,stride)
		for i in range(1,count):
			temp=block_func(temp,ch_out,ch_out,1)
		return temp
	
	conv1=conv_bn_layer(ipt,ch_out=16,filter_size=3,stride=1,padding=1)
	res1=layer_warp(basicblock,conv1,16,16,5,1)
	res2=layer_warp(basicblock,res1,16,32,5,2)
	res3=layer_warp(basicblock,res2,32,64,5,2)
	pool=fluid.layers.pool2d(input=res3,pool_size=8,pool_type='avg',pool_stride=1)
	predict=fluid.layers.fc(input=pool,size=class_dim,act='softmax')
	return predict

定义数据层,使用的是cifar数据集,这个数据集的图片是宽高都是32的3通道彩色图片,所以定义的输入层的shape是[3,32,32]。

#使用的数据集是cifar数据集,这个数据集是32*32的图片,3通道,则输入层shape应该是[3,32,32]
image=fluid.layers.data(name='image',shape=[3,32,32],dtype='float32')
#label是每张图片对应的0-9的分类数字,是int类型的,所以shape=[1]
label=fluid.layers.data(name='label',shape=[1],dtype='int64')

获取残差神经网络的分类器,制定分类的大小为10分类,因为这个数据集有10个类别。

#获取残差神经网络的分类器
model=resnet_cifar(image,10)

定义交叉熵损失函数和平均准确率。其中交叉熵损失函数和计算准确率会选取输出model的10分类中最大的那个值的下标和label做对比。详情可以看PaddlePaddle官方的1.1版本的api:PaddlePaddle Fluid-1.1-api

#损失函数和准确率
cost=fluid.layers.cross_entropy(input=model,label=label)
avg_cost=fluid.layers.mean(cost)
acc=fluid.layers.accuracy(input=model,label=label)

克隆出测试程序,用于以后测试使用。

test_program=fluid.default_main_program().clone(for_test=True)

定义优化方法(又是我最喜欢的Adam优化器,速度比梯度下降快)

#定义优化方法
optimizer=fluid.optimizer.AdamOptimizer(learning_rate=1e-3)
opts=optimizer.minimize(avg_cost)

获取训练和测试数据,在这里使用的是cifar数据集,cifar数据集有两种,一种是100类别的,一种是10类别的,在这里我们使用10类别的。因为这个数据集很大,所以下载cifar数据集所需要的时间会比较长。

#获取cifar数据集
train_reader=paddle.batch(cifar.train10(),batch_size=32)
test_reader=paddle.batch(cifar.test10(),batch_size=32)

创建执行器,头铁,还用CPU进行训练(实际上就是我的虚拟机没有CUDA)。并进行参数初始化。

#创建执行器,虚拟机不配用CUDA(实际上是我没得CUDA用
place=fluid.CPUPlace()
exe=fluid.Executor(place)
#参数初始化
exe.run(fluid.default_startup_program())

加载已经存在的模型并进行训练

创建执行器之后就可以用我们之前提到过的两种加载模型的方式,他们两个对应着两种方式保存模型,这两种方法随便用一种即可。而且这两种方式是用来初始化模型继续训练的的,而不是使用进行预测的那个模型。

参数模型:fluid.io.load_params()

fluid.io.load_params()是加载之前训练保存的参数模型,对应的保存接口是fluid.io.save_params()。当使用这个模型参数恢复网络参数时,可以这样写:

#加载之前的params_model模型,没有模型就不加载
save_path='models/params_model'
if os.path.exists(save_path):
	print("使用参数模型作为预训练模型")
	fluid.io.load_params(executor=exe,dirname=save_path)

持久化变量模型:fluid.io.load_persistables()

fluid.io.load_persistables()施加在之前训练保存的持久化变量模型,对应的保存接口是fluid.io.save_persistables()。当使用这个模型参数恢复网络时,可以这样写:

#加载之前的persistables_model模型,没有模型就不加载
save_path='models/persistables_model/'
if os.path.exists(save_path):
	print("使用持久化变量模型作为预训练模型")
	fluid.io.load_persistables(executor=exe,dirname=save_path)

以上二者都是保存的是初始化模型,所以在选择的时候,这两者可以随意选择,我比较喜欢前者,所以在最终的全部代码中,我会使用参数模型来保存模型参数。
而对于预测模型的存储和使用,文章后面就会提到。

开始训练模型:

#开始训练
for pass_id in range(10):
	for batch_id ,data in enumerate(train_reader()):
		train_cost,train_acc=exe.run(program=fluid.default_main_program(),feed=feeder.feed(data),fetch_list=[avg_cost,acc])
		
		#100batch打印一次
		if batch_id%100==0:
			print("Pass:%d,Batch:%d,Cost:%0.5f,Accuracy:%0.5f"%(pass_id,batch_id,train_cost[0],train_acc[0]))

	#测试
	test_accs=[]
	test_costs=[]
	for batch_id,data in enumerate(test_reader()):
		test_cost,test_acc=exe.run(program=test_program,feed=feeder.feed(data),fetch_list=[avg_cost,acc])
		test_accs.append(test_acc[0])
		test_costs.append(test_cost[0])
	#求测试平均值
	test_cost=(sum(test_costs)/len(test_costs))
	test_acc=(sum(test_accs)/len(test_accs))
	print('Test:%d,Cost:%0.5f,Accuracy:%0.5f'%(pass_id,test_cost,test_acc))

保存模型

在训练过程中就可以保存模型,这里介绍全部三种模型的保存,一般使用fluid.io.save_inference_model()函数保存的模型之后用于预测,是预测模型。使用fluid.io.save_params()或者fluid.io.save_persistables()函数保存的模型用于初始化模型,进行训练。

保存预测模型

fluid.io.save_inference_model()函数一般用来保存预测模型,用于以后预测图像。通过这个方式保存的模型,之后看来使用是非常方便的,具体怎样使用可以阅读下面的预测部分。使用方法如下:

#保存预测模型,方便以后预测
save_path='models/infer_model/'
#删除旧的模型文件
shutil.rmtree(save_path,ignore_errors=True)
#创建预测模型文件目录
os.makedirs(save_path)
#保存预测模型,值得注意的是,这个需要把输入层的name记录下来以便以后使用输入层的name进行feed
#想要预测的内容也要存入模型,以便以后使用fetch_list进行预测
fluid.io.save_inference_model(save_path,feeded_var_names=[image.name],target_vars=[model],executor=exe)

保存参数模型

保存参数模型,以便以后初始化模型,继续训练。

#保存参数模型
save_path='model/params_model/'
#删除旧的模型文件
shutil.rmtree(save_path,ignore_errors=True)
#创建保持模型文件目录
os.makedirs(save_path)
#保存模型参数
fluid.io.save_params(executor=exe,dirname=save_path)

保存持久化变量模型

保存持久化变量模型,便于以后初始化模型,继续训练。

# 保存持久化变量模型
save_path = 'models/persistables_model/'
# 删除旧的模型文件
shutil.rmtree(save_path, ignore_errors=True)
# 创建保持模型文件目录
os.makedirs(save_path)
# 保存持久化变量模型
fluid.io.save_persistables(executor=exe, dirname=save_path)

全部代码

#coding:utf-8
'''
created on February 19 15:06 2019

@author:lhy
'''

#使用fluid.io.save_params保存模型
import os
import shutil
import paddle
import paddle.dataset.cifar as cifar
import paddle.fluid as fluid

#定义一个残差神经网络(ResNet)
def resnet_cifar(ipt,class_dim):
	#定义一个卷积+批标准化层
	def conv_bn_layer(input,ch_out,filter_size,stride,padding,act='relu',bias_attr=False):
		tmp=fluid.layers.conv2d(input=input,filter_size=filter_size,num_filters=ch_out,stride=stride,padding=padding,bias_attr=bias_attr)
		return fluid.layers.batch_norm(input=tmp,act=act)
	
	def shortcut(input,ch_in,ch_out,stride):
		if ch_in!=ch_out:
			return conv_bn_layer(input,ch_out,1,stride,0,None)
		else:
			return input
	#残差块
	def basicblock(input,ch_in,ch_out,stride):
		temp=conv_bn_layer(input,ch_out,3,stride,1)
		temp=conv_bn_layer(temp,ch_out,3,1,1,act=None,bias_attr=True)
		short=shortcut(input,ch_in,ch_out,stride)
		return fluid.layers.elementwise_add(x=temp,y=short,act='relu')
	

	def layer_warp(block_func,input,ch_in,ch_out,count,stride):
		temp=block_func(input,ch_in,ch_out,stride)
		for i in range(1,count):
			temp=block_func(temp,ch_out,ch_out,1)
		return temp
	
	conv1=conv_bn_layer(ipt,ch_out=16,filter_size=3,stride=1,padding=1)
	res1=layer_warp(basicblock,conv1,16,16,5,1)
	res2=layer_warp(basicblock,res1,16,32,5,2)
	res3=layer_warp(basicblock,res2,32,64,5,2)
	pool=fluid.layers.pool2d(input=res3,pool_size=8,pool_type='avg',pool_stride=1)
	predict=fluid.layers.fc(input=pool,size=class_dim,act='softmax')
	return predict

#使用的数据集是cifar数据集,这个数据集是32*32的图片,3通道,则输入层shape应该是[3,32,32]
image=fluid.layers.data(name='image',shape=[3,32,32],dtype='float32')
label=fluid.layers.data(name='label',shape=[1],dtype='int64')

print(label.shape)

#获取残差神经网络的分类器,指定是10分类,这个数据集有10个类别
model=resnet_cifar(image,10)

print(model.shape)

#损失函数和准确率
cost=fluid.layers.cross_entropy(input=model,label=label)
avg_cost=fluid.layers.mean(cost)
acc=fluid.layers.accuracy(input=model,label=label)

#克隆测试程序
test_program=fluid.default_main_program().clone(for_test=True)

#定义优化方法
optimizer=fluid.optimizer.AdamOptimizer(learning_rate=1e-3)
opts=optimizer.minimize(avg_cost)

#获取cifar数据集,cifar数据集有两种,一种是100类别,一种是10类别,这里使用10类别,一组取出32组
train_reader=paddle.batch(cifar.train10(),batch_size=32)
test_reader=paddle.batch(cifar.test10(),batch_size=32)

#创建执行器,虚拟机不配用CUDA(实际上是我没得CUDA用
place=fluid.CPUPlace()
exe=fluid.Executor(place)
#参数初始化
exe.run(fluid.default_startup_program())

#加载之前的params_model模型,没有模型就不加载
save_path='models/params_model'
if os.path.exists(save_path):
	print("使用参数模型作为预训练模型")
	fluid.io.load_params(executor=exe,dirname=save_path)

#定义输入数据维度
feeder=fluid.DataFeeder(place=place,feed_list=[image,label])

#开始训练
for pass_id in range(10):
	for batch_id ,data in enumerate(train_reader()):
		train_cost,train_acc=exe.run(program=fluid.default_main_program(),feed=feeder.feed(data),fetch_list=[avg_cost,acc])
		
		#100batch打印一次
		if batch_id%100==0:
			print("Pass:%d,Batch:%d,Cost:%0.5f,Accuracy:%0.5f"%(pass_id,batch_id,train_cost[0],train_acc[0]))

	#测试
	test_accs=[]
	test_costs=[]
	for batch_id,data in enumerate(test_reader()):
		test_cost,test_acc=exe.run(program=test_program,feed=feeder.feed(data),fetch_list=[avg_cost,acc])
		test_accs.append(test_acc[0])
		test_costs.append(test_cost[0])
	#求测试平均值
	test_cost=(sum(test_costs)/len(test_costs))
	test_acc=(sum(test_accs)/len(test_accs))
	print('Test:%d,Cost:%0.5f,Accuracy:%0.5f'%(pass_id,test_cost,test_acc))

#保存模型,一般使用fluid.io.save_inference_model()函数保存的模型之后用于预测,是预测模型。
#使用fluid.io.save_params()或者fluid.io.save_persistables()函数保存的模型用于初始化模型,进行训练。

#保存预测模型,方便以后预测
save_path='models/infer_model/'
#删除旧的模型文件
shutil.rmtree(save_path,ignore_errors=True)
#创建预测模型文件目录
os.makedirs(save_path)
#保存预测模型
fluid.io.save_inference_model(save_path,feeded_var_names=[image.name],target_vars=[model],executor=exe)


#保存参数模型
save_path='models/params_model/'
#删除旧的模型文件
shutil.rmtree(save_path,ignore_errors=True)
#创建保持模型文件目录
os.makedirs(save_path)
#保存模型参数
fluid.io.save_params(executor=exe,dirname=save_path)

以上代码效果

由于一共有10个pass,每个pass又有1500+的batch,输出信息太多,所以这里只展示最后一个Pass的输出:

Pass:9,Batch:0,Cost:0.23369,Accuracy:0.87500
Pass:9,Batch:100,Cost:0.13673,Accuracy:0.93750
Pass:9,Batch:200,Cost:0.14569,Accuracy:0.96875
Pass:9,Batch:300,Cost:0.11853,Accuracy:1.00000
Pass:9,Batch:400,Cost:0.10782,Accuracy:1.00000
Pass:9,Batch:500,Cost:0.10297,Accuracy:0.93750
Pass:9,Batch:600,Cost:0.09028,Accuracy:0.96875
Pass:9,Batch:700,Cost:0.13088,Accuracy:0.93750
Pass:9,Batch:800,Cost:0.46306,Accuracy:0.90625
Pass:9,Batch:900,Cost:0.15785,Accuracy:0.93750
Pass:9,Batch:1000,Cost:0.11927,Accuracy:0.93750
Pass:9,Batch:1100,Cost:0.12313,Accuracy:0.93750
Pass:9,Batch:1200,Cost:0.32262,Accuracy:0.84375
Pass:9,Batch:1300,Cost:0.20035,Accuracy:0.93750
Pass:9,Batch:1400,Cost:0.06530,Accuracy:1.00000
Pass:9,Batch:1500,Cost:0.23624,Accuracy:0.93750
Test:9,Cost:0.83426,Accuracy:0.78235

从测试集测试结果来看。。。有点过拟合了。。。
模型保存在models文件夹下,如下图所示:
在这里插入图片描述
模型中是这样存储参数的:
在这里插入图片描述
网络越复杂,参数越多,生成的模型文件越多。

预测

在训练的时候,我们使用fluid.io.save_inference_model接口保存了预测模型,通过以下程序,我们知道用这个接口保存的模型使用起来有多简单,其中很大一个改变就是不需要前向传播获得分类器了。

导入相关模块:

import paddle.fluid as fluid
from PIL import Image
import numpy as np

创建执行器:

#创建执行器
place=fluid.CPUPlace()
exe=fluid.Executor(place)
exe.run(fluid.default_startup_program())

重点:加载模型,通过fluid.io.load_inference_model()函数可以轻松获得这个模型存储的预测程序,输入参数的名称,以及分类器的输出。

save_path='models/infer_model/'
#从模型中得到预测程序,输入数据名称列表和分类器
[infer_program,feeded_var_names,target_var]=fluid.io.load_inference_model(dirname=save_path,executor=exe)

预处理图像,将图片统一大小,修改图像的存储顺序和通道顺序,并转化成一个numpy数组:

#定义一个图像预处理的函数
def load_image(file):
	im=Image.open(file)
	im=im.resize((32,32),Image.ANTIALIAS)
	im=np.array(im).astype(np.float32)
	#PIL打开图片存储顺序为H(高度),W(宽度),C(通道数)
	#但是PaddlePaddle要求数据的顺序是CHW,需要调整顺序
	im=im.transpose((2,0,1))
	#cifar训练的图片通道顺序为B(蓝),G(绿),R(红)
	#但是PIL打开的图片是默认的RGB,所以要交换通道
	im=im[(2,1,0),:,:]
	im=im/255.0#归一化
	im=np.expand_dims(im,axis=0)
	return im

获取数据并进行预测,不需要再输入一个模拟的标签了,因为在保存模型的时候,已经对标签进行了修剪,去掉了这部分不必要的输入。
输入一张猫的图片:
在这里插入图片描述

#获取图片数据
img=load_image('cat.jpeg')

#执行预测
result=exe.run(program=infer_program,feed={feed_var_names[0]:img},fetch_list=target_var)

执行预测程序之后,获得一个10分类的数组,表示每种分类的概率,我们获得最大值的下标来确定该类的名称:

#获得值最大的元素对应的下标
lab=np.argmax(result[0][0])

names=['飞机','汽车','鸟','猫','鹿','狗','青蛙','马','船','卡车']

print("预测的标签是:%d,名称是:%s,概率是:%f"%(lab,names[lab],result[0][0][lab]))

输出结果:

预测的标签是:3,名称是:猫,概率是:0.951282

可以看到模型很成功的识别出了猫。

代码参考:夜雨飘零1—PaddlePaddle系列课程
非常感谢这位大佬!!!

发布了61 篇原创文章 · 获赞 16 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41427568/article/details/87735085
今日推荐