如何将PyTorch、TensorFlow模型转换为PaddlePaddle模型

【飞桨开发者说】张鑫(左),西安电子科技大学研二在读,软件设计师,cv爱好者。李文博(右),硕士研究生,研究方向为泛安防领域目标检测。


本文手把手教你使用X2Paddle将PyTorch、TensorFlow模型转换为PaddlePaddle模型,并提供了PaddlePaddle模型的使用实例。

本项目适合以下人群:

  • 已有PyTorch、TF模型却苦于没有算力运行的你

  • 希望快速将PyTorch、TF工程迁移为PaddlePaddle的你

  • 希望快速使用PaddlePaddle又不想重新训练模型的你

  • 垂涎AI Studio的V100已久却不想花太多时间学习PaddlePaddle细节的你


将PyTorch模型转换为

PaddlePaddle模型

将PyTorch模型转换为PaddlePaddle模型需要先把PyTorch转换为onnx模型,然后转换为PaddlePaddle模型。

1. 安装依赖库:

在实践下述代码前,你需要确保本地环境已安装以下依赖库:

  • 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


2.  实验环境:

本文所用PyTorch模型为nasnet-a_mobile ,通过迁移训练在Stanford Dogs数据集全集上训练20个epochs所得。

  • PyTorch模型定义文件,本文采用nasnet_mobile.py

  • PyTorch模型参数,本文中所用为nasnet_mobile.pkl

note:

  1. 上文所提两文件均在/home/aistudio目录下,读者可自行下载进行实验

  2. 如果你需要转换自己的PyTorch模型同样也需要提供模型定义文件和模型参数文件。

3. 实验步骤:


3.1 PyTorch模型转换为onnx模型

定义一个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将存放在当前目录。

3.2 将onnx模型转换为PaddlePaddle模型

在本地终端输入以下代码:

x2paddle --framework=onnx --model=nasnet.onnx --save_dir=pd_model

最终的PaddlePaddle模型存放在pd_model目录。

pd_model目录下有两个文件夹

  • inference_model 存放模型的网络结构和参数。

  • model_with_code 存放模型构建的代码model.py和模型参数。


4. 转换所得PaddlePaddle模型应用示例

下面我们用一张图片看看转换所得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之后的文件。


1. 安装依赖库:

在实践下述代码前,你需要确保本地环境满足以下依赖库:

  • 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 不仅存放了模型参数,还生成了模型定义。


3. 转换所得PaddlePaddle模型应用示例

下面我们用一张图片看看转换所得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

已标记关键词 清除标记
相关推荐