【飞桨开发者说】张鑫(左),西安电子科技大学研二在读,软件设计师,cv爱好者。李文博(右),硕士研究生,研究方向为泛安防领域目标检测。
本项目适合以下人群:
已有PyTorch、TF模型却苦于没有算力运行的你
希望快速将PyTorch、TF工程迁移为PaddlePaddle的你
希望快速使用PaddlePaddle又不想重新训练模型的你
垂涎AI Studio的V100已久却不想花太多时间学习PaddlePaddle细节的你
将PyTorch模型转换为
PaddlePaddle模型
将PyTorch模型转换为PaddlePaddle模型需要先把PyTorch转换为onnx模型,然后转换为PaddlePaddle模型。
在实践下述代码前,你需要确保本地环境已安装以下依赖库:
torch
onnx
pip install onnx==1.6.0
pip install onnxruntime==1.0.0
PaddlePaddle >= 1.6.0
X2Paddle
git clone https://github.com/PaddlePaddle/X2Paddle.git
cd X2Paddle
git checkout develop
python setup.py install
本文所用PyTorch模型为nasnet-a_mobile ,通过迁移训练在Stanford Dogs数据集全集上训练20个epochs所得。
PyTorch模型定义文件,本文采用nasnet_mobile.py
PyTorch模型参数,本文中所用为nasnet_mobile.pkl
note:
上文所提两文件均在/home/aistudio目录下,读者可自行下载进行实验
如果你需要转换自己的PyTorch模型同样也需要提供模型定义文件和模型参数文件。
定义一个py文件名为trans.py,具体代码如下:
#coding: utf-8
import torch
#import torchvision
# 1.导入PyTorch模型定义
from nasnet_mobile import nasnetamobile
# 2.指定输入大小的shape
dummy_input = torch.randn(1, 3, 224, 224)
# 3. 构建PyTorch model
model = nasnetamobile(121,pretrained=False)
# 4. 载入模型参数
model.load_state_dict(torch.load('/home/aistudio/data/data23875/nasnet_mobile.pkl', map_location='cpu'))
# 5.导出onnx模型文件
torch.onnx.export(model, dummy_input, "nasnet.onnx",verbose=True)
note:如果你想转换自己的模型,在此需要修改,在本地终端中输入:
python trans.py
所转换的onnx模型nasnet.onnx将存放在当前目录。
在本地终端输入以下代码:
x2paddle --framework=onnx --model=nasnet.onnx --save_dir=pd_model
最终的PaddlePaddle模型存放在pd_model目录。
pd_model目录下有两个文件夹
inference_model 存放模型的网络结构和参数。
model_with_code 存放模型构建的代码model.py和模型参数。
下面我们用一张图片看看转换所得PaddlePaddle模型是否可以正常运行。
我们在AI Studio的环境上存放以下文件:
在目录/home/aistudio/下的n02085782_1039.jpg文件,这是 一张小狗的图片,类别标签为32。
在目录
/home/aistudio/pd_model/model_with_code下保存有转换所得Paddle模型的参数与模型定义。
下面我们开始构建Paddle程序,看看模型的推理结果是否如预期。
cd ./pd_model/
/home/aistudio/pd_model
tar = zipfile.ZipFile('/home/aistudio/pd_model/model_with_code_zip.zip','r')
tar.extractall()
cd ./model_with_code/
/home/aistudio/pd_model/model_with_code
import argparse
import functools
import numpy as np
import paddle.fluid as fluid
from model import x2paddle_net
use_gpu=True
######Attack graph
adv_program=fluid.Program()
#完成初始化
with fluid.program_guard(adv_program):
#设置为可以计算梯度
input_layer.stop_gradient=False
# model definition
inputs ,out_logits = x2paddle_net()
out = fluid.layers.softmax(out_logits[0])
place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace()
exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())
#记载模型参数
fluid.io.load_persistables(exe, "./")
#创建测试用评估模式
eval_program = adv_program.clone(for_test=True)
import cv2
#定义一个预处理图像的函数
def process_img(img_path="",image_shape=[3,224,224]):
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
img = cv2.imread(img_path)
img = cv2.resize(img,(image_shape[1],image_shape[2]))
#img = cv2.resize(img,(256,256))
#img = crop_image(img, image_shape[1], True)
#RBG img [224,224,3]->[3,224,224]
img = img[:, :, ::-1].astype('float32').transpose((2, 0, 1)) / 255
#img = img.astype('float32').transpose((2, 0, 1)) / 255
img_mean = np.array(mean).reshape((3, 1, 1))
img_std = np.array(std).reshape((3, 1, 1))
img -= img_mean
img /= img_std
img=img.astype('float32')
img=np.expand_dims(img, axis=0)
return img
#模型推理函数
def inference(img):
fetch_list = [out.name]
result = exe.run(eval_program,
fetch_list=fetch_list,
feed={inputs[0].name: img})
result = result[0][0]
pred_label = np.argmax(result)
pred_score = result[pred_label].copy()
return pred_label, pred_score
#将标签为32的图片进行预处理
img = process_img("/home/aistudio/n02085782_1039.jpg")
#用PaddlePaddle模型推理图片标签
pred_label, pred_score = inference(img)
print("预测图片{}的标签为{}".format("/home/aistudio/n02085782_1039.jpg",pred_label))
预测图片/home/aistudio/n02085782_1039.jpg的标签为32。可见模型可以如期推理出标签,那么我们的转换大功告成,接下来就可以在AI Studio平台愉快的用所转换的模型做各种下游任务了。
将TensorFlow模型转换
为PaddlePaddle模型
注:model.pb为TF训练好的模型,pb_model为转换为PaddlePaddle之后的文件。
在实践下述代码前,你需要确保本地环境满足以下依赖库:
TensorFlow1.14
PaddlePaddle1.8
pip install paddlepaddle -i https://mirror.baidu.com/pypi/simple
TensorFlow
conda install tensorflow ==1.1
PaddlePaddle >= 1.6.0
conda install paddlepaddle
X2Paddle
pip install x2paddle
2. 实验步骤:
首先训练TF网络,并保存成pb文件。本教程的主要目的是如何转换自己训练的TF模型到Paddle模型,所以只搭建了Lenet5这个最简单的网络。数据集为猫狗大战数据集,数据示例如下所示,相关数据已经制作成tfrecords格式。
注意 TensorFlow模型在导出时,只需要导出前向计算部分(即模型预测部分,不需要训练部分回传的网络结构)。
目前,X2Paddle中支持TF保存的pb模型,但是需要注意的是,在保存pb模型的时候,只需要导出前向计算部分(即模型预测部分,不需要训练部分回传的网络结构)。为了方便大家,模型保存的函数如下。
def freeze_model(sess, output_tensor_names, freeze_model_path):
out_graph = graph_util.convert_variables_to_constants(
sess, sess.graph.as_graph_def(), output_tensor_names)
with tf.gfile.GFile(freeze_model_path, 'wb') as f:
f.write(out_graph.SerializeToString())
print("freeze model saved in {}".format(freeze_model_path))
开启训练。因为是在CPU中进行计算,所以项目中设置了10次迭代,仅仅是对训练过程进行演示。
在终端中运行如下命令安装TF1.14:
pip install tensorflow==1.14 –i https://mirror.baidu.com/pypi/simple
执行如下命令开启训练过程:
!python work/X2Paddle_ISSUE/train.py
在本地终端输入以下代码将TF模型转换为PaddlePaddle模型:
x2paddle --framework=tensorflow --model=/home/aistudio/work/X2Paddle_ISSUE/save_model/model.pb --save_dir=/home/aistudio/pd_model
最终的转换出的PaddlePaddle模型将存放在pd_model目录中。
pd_model目录下有两个文件夹
inference_model 只存放了模型参数。
model_with_code 不仅存放了模型参数,还生成了模型定义。
下面我们用一张图片看看转换所得PaddlePaddle模型是否可以正常运行。
我们有以下文件:
work/X2Paddle_ISSUE/dog.jpg 一张小狗的图片,类别标签为1
/home/aistudio/pd_model/model_with_code 转换所得Paddle模型的参数与模型定义
为将图片以参数形式传入型,/home/aistudio/pd_model/model_with_code/model.py中需修改两处:
1)def x2paddle_net(): 修改为 def x2paddle_net(input):
2) x2paddle_input_1 = fluid.layers.data(dtype='float32', shape=[1, 3, 224, 224], name='x2paddle_input_1', append_batch_size=False)
修改为x2paddle_input_1 = input
下面展示了X2Paddle生成的网络结构定义函数,如果仔细看的话,我们能看出网络结构,但是这个代码确实不像是阳间的Paddle代码。
def x2paddle_net(input):
# Placeholder = fluid.layers.data(dtype='float32', shape=[1, 3, 32, 32], name='Placeholder', append_batch_size=False)
Placeholder = input
layer1_conv1_Variable_1 = fluid.layers.create_parameter(dtype='float32', shape=[64], name='layer1_conv1_Variable_1', default_initializer=Constant(0.0))
layer3_conv2_Variable_1 = fluid.layers.create_parameter(dtype='float32', shape=[128], name='layer3_conv2_Variable_1', default_initializer=Constant(0.0))
layer5_fc1_Variable = fluid.layers.create_parameter(dtype='float32', shape=[8192, 512], name='layer5_fc1_Variable', default_initializer=Constant(0.0))
layer5_fc1_Variable_1 = fluid.layers.create_parameter(dtype='float32', shape=[512], name='layer5_fc1_Variable_1', default_initializer=Constant(0.0))
layer5_fc1_dropout_rate = fluid.layers.create_parameter(dtype='float32', shape=[1], name='layer5_fc1_dropout_rate', default_initializer=Constant(0.5))
layer5_fc1_dropout_random_uniform_min = fluid.layers.create_parameter(dtype='float32', shape=[1], name='layer5_fc1_dropout_random_uniform_min', default_initializer=Constant(0.0))
layer5_fc1_dropout_random_uniform_max = fluid.layers.create_parameter(dtype='float32', shape=[1], name='layer5_fc1_dropout_random_uniform_max', default_initializer=Constant(1.0))
layer5_fc1_dropout_sub_x = fluid.layers.create_parameter(dtype='float32', shape=[1], name='layer5_fc1_dropout_sub_x', default_initializer=Constant(1.0))
layer5_fc1_dropout_truediv_x = fluid.layers.create_parameter(dtype='float32', shape=[1], name='layer5_fc1_dropout_truediv_x', default_initializer=Constant(1.0))
layer6_fc2_Variable = fluid.layers.create_parameter(dtype='float32', shape=[512, 2], name='layer6_fc2_Variable', default_initializer=Constant(0.0))
layer6_fc2_Variable_1 = fluid.layers.create_parameter(dtype='float32', shape=[2], name='layer6_fc2_Variable_1', default_initializer=Constant(0.0))
layer5_fc1_dropout_random_uniform_RandomUniform = fluid.layers.uniform_random(shape=[1, 512], min=0.0, max=0.9999)
layer5_fc1_dropout_random_uniform_sub = fluid.layers.elementwise_sub(x=layer5_fc1_dropout_random_uniform_max, y=layer5_fc1_dropout_random_uniform_min)
layer5_fc1_dropout_sub = fluid.layers.elementwise_sub(x=layer5_fc1_dropout_sub_x, y=layer5_fc1_dropout_rate)
layer1_conv1_Relu = fluid.layers.conv2d(Placeholder, bias_attr='layer1_conv1_Variable_1', param_attr='layer1_conv1_Variable', num_filters=64, filter_size=[5, 5], stride=[1, 1], dilation=[1, 1], padding='SAME', act='relu')
y_tmp = fluid.layers.expand(layer5_fc1_dropout_random_uniform_sub, expand_times=[512])
layer5_fc1_dropout_random_uniform_mul = fluid.layers.elementwise_mul(x=layer5_fc1_dropout_random_uniform_RandomUniform, y=y_tmp)
layer5_fc1_dropout_truediv = fluid.layers.elementwise_div(x=layer5_fc1_dropout_truediv_x, y=layer5_fc1_dropout_sub)
y_tmp = fluid.layers.expand(layer5_fc1_dropout_random_uniform_min, expand_times=[512])
layer5_fc1_dropout_random_uniform = fluid.layers.elementwise_add(x=layer5_fc1_dropout_random_uniform_mul, y=y_tmp)
layer5_fc1_dropout_GreaterEqual = fluid.layers.greater_equal(x=layer5_fc1_dropout_random_uniform, y=layer5_fc1_dropout_rate)
layer2_pool1_MaxPool = fluid.layers.pool2d(layer1_conv1_Relu, pool_size=[2, 2], pool_type='max', pool_padding='SAME', pool_stride=[2, 2])
layer5_fc1_dropout_Cast = fluid.layers.cast(layer5_fc1_dropout_GreaterEqual, dtype='float32')
layer3_conv2_Relu = fluid.layers.conv2d(layer2_pool1_MaxPool, bias_attr='layer3_conv2_Variable_1', param_attr='layer3_conv2_Variable', num_filters=128, filter_size=[5, 5], stride=[1, 1], dilation=[1, 1], padding='SAME', act='relu')
layer4_pool2_MaxPool = fluid.layers.pool2d(layer3_conv2_Relu, pool_size=[2, 2], pool_type='max', pool_padding='SAME', pool_stride=[2, 2])
layer4_pool2_Reshape = fluid.layers.transpose(layer4_pool2_MaxPool, perm=[0, 2, 3, 1])
layer4_pool2_Reshape = fluid.layers.reshape(layer4_pool2_Reshape, shape=[1, 8192])
layer5_fc1_MatMul = fluid.layers.matmul(x=layer4_pool2_Reshape, y=layer5_fc1_Variable, transpose_x=False, transpose_y=False)
layer5_fc1_add = fluid.layers.elementwise_add(x=layer5_fc1_MatMul, y=layer5_fc1_Variable_1)
layer5_fc1_Relu = fluid.layers.relu(layer5_fc1_add)
y_tmp = fluid.layers.expand(layer5_fc1_dropout_truediv, expand_times=[512])
layer5_fc1_dropout_mul = fluid.layers.elementwise_mul(x=layer5_fc1_Relu, y=y_tmp)
layer5_fc1_dropout_mul_1 = fluid.layers.elementwise_mul(x=layer5_fc1_dropout_mul, y=layer5_fc1_dropout_Cast)
layer6_fc2_MatMul = fluid.layers.matmul(x=layer5_fc1_dropout_mul_1, y=layer6_fc2_Variable, transpose_x=False, transpose_y=False)
layer6_fc2_add = fluid.layers.elementwise_add(x=layer6_fc2_MatMul, y=layer6_fc2_Variable_1)
return [Placeholder], [layer6_fc2_add]
下面我们开始构建Paddle程序,看看模型的推理结果是否如预期。预测用示例图像如下所示,在训练过程中,我们将cat的标签转换为0,dog的标签为1。
执行如下命令进行预测:
!python work/X2Paddle_ISSUE/test_paddle.py
最终预测图片work/X2Paddle_ISSUE/dog.jpg的标签为1。
如在使用过程中有问题,可加入飞桨官方QQ群交流:1108045677
如果您想详细了解更多飞桨的相关内容,请参阅以下文档。
官网地址:
https://www.paddlepaddle.org.cn
飞桨模型转换工具X2Paddle的项目地址:
GitHub:
https://github.com/PaddlePaddle/X2Paddle
Gitee:
https://gitee.com/paddlepaddle/X2Paddle
飞桨开源框架项目地址:
GitHub:
https://github.com/PaddlePaddle/Paddle
Gitee:
https://gitee.com/paddlepaddle/Paddle
END