未验证 提交 75d0eb30 编写于 作者: J Jason 提交者: GitHub

Merge pull request #4 from jiangjiajun/master

2018.03.14更新
...@@ -4,203 +4,87 @@ ...@@ -4,203 +4,87 @@
tensorflow2fluid支持将训练好的TensorFlow模型转换为PaddlePaddle模型,包括基于PaddlePaddle实现的模型前向计算网络python代码,以及PaddlePaddle可加载的模型参数文件。 tensorflow2fluid支持将训练好的TensorFlow模型转换为PaddlePaddle模型,包括基于PaddlePaddle实现的模型前向计算网络python代码,以及PaddlePaddle可加载的模型参数文件。
> <a href="#环境依赖">`环境依赖`</a> > <a href="#环境依赖">`环境依赖`</a>
> <a href="#安装说明">`安装说明`</a>
> <a href="#使用方法">`使用方法`</a> > <a href="#使用方法">`使用方法`</a>
> <a href="#开发介绍">`开发介绍`</a> > <a href="#测试模型">`测试模型`</a>
> <a href="#对比实验">`对比实验`</a>
**我们计划专门梳理出指南文档,对比TensorFlow与PaddlePaddle的差异,帮助TensorFlow用户快速上手PaddlePaddle的使用,文档后续会整理在doc目录下,欢迎有需求的同学关注!** **我们计划专门梳理出指南文档,对比TensorFlow与PaddlePaddle的差异,帮助TensorFlow用户快速上手PaddlePaddle的使用,文档后续会整理在doc目录下,欢迎有需求的同学关注!**
## 环境依赖 ## 依赖环境
> python = 2.7 工具开发过程中,我们在如下环境配置中测试模型转换
> tensorflow >= 1.12.0 > python == 2.7 or 3.6
> 注:tensorflow2fluid的运行不依赖于paddlepaddle,但测试转换后的模型所需的PaddlePaddle须为1.2.0或更新版本 > tensorflow == 1.12.0
<a id="安装说明"> > paddlepaddle == 1.3.0
## 安装说明
```
# 如果没有安装paddlepaddle和tensorflow环境
pip install paddlepaddle
pip install tensorflow
git clone https://github.com/PaddlePaddle/X2Paddle.git
cd X2Paddle/tensorflow2fluid
python setup.py install
```
<a id="使用方法"> <a id="使用方法">
## 使用方法 ## 使用方法
### 转换模型
> 1. 目前支持转换的模型格式包括checkpoint保存的模型、将参数序列化到网络结构的pb格式模型 ```
> 2. 模型转换后,在输入同样的数据前提下,检查模型转换前后的diff,一般结果最大diff数量级不大于1e-04 python src/convert.py --pb_file tf_model.pb \
--in_nodes inputs \
### 转换示例 --output_nodes outputs \
--input_shape None,224,224,3 \
下面示例中,将vgg_16模型转换至paddlepaddle模型 --input_format NHWC \
--use_cuda True \
--save_dir translated_paddle_model
``` ```
# 下载预训练的vgg_16模型参数 ### 加载模型并预测
wget http://download.tensorflow.org/models/vgg_16_2016_08_28.tar.gz 本目录下提供了[model_loader.py](tf2fluid/model_loader.py),可以辅助用户简单的加载模型和预测,和dump模型,用户可直接参考其实现
tar xzvf vgg_16_2016_08_28.tar.gz
``` python
# 将模型转存为checkpoint格式模型 # coding:utf-8
python demo/export_to_checkpoint.py --model vgg_16 --ckpt_file vgg_16.ckpt --save_dir vgg_checkpoint # 代码运行目录 X2Paddle/tensorflow2fluid
import sys
# 转换模型 import tf2fluid.model_loader as ml
tf2fluid --meta_file vgg_checkpoint/model.meta \
--ckpt_dir vgg_checkpoint/ \ # 加载模型
--in_nodes inputs \ model = ml.ModelLoader("translated_paddle_model", use_cuda=True)
--input_shape None,224,224,3 \
--output_nodes vgg_16/fc8/squeezed \ # 随机生成数据用于模型预测
--save_dir paddle_vgg # 注意Paddle CV模型输入格式为NCHW !!!
data = numpy.random.rand(5, 3, 224, 224).astype('float32')
results = model.inference(feed_dict={model.inputs[0]:data})
# 返回的results为list,元素为np.array
for res in results:
print(res.shape)
``` ```
### 参数说明 使用转换后的模型主要注意,**模型转换后,计算结果与原模型存在一定精度的diff,因此务必检查模型转换前后,在输入同样的数据前提下,diff是否符合预期**
### 序列化模型结构
tensorflow2fluid转换后的模型结构以python代码定义形式供用户直观阅读或修改,如若需要将模型结构和参数均序列化存储,可以上面的示例代码中,调用如下代码即可,序列化的模型结构和参数如何加载可见PaddlePaddle使用文档中的[加载预测模型](http://www.paddlepaddle.org/documentation/docs/zh/1.3/api_guides/low_level/inference.html#id4)
``` python
model.save_inference_model("new_model_dir")
```
### 参数说明
|tf2fluid参数|说明| |tf2fluid参数|说明|
|------------------|-----------------------------------------------| |-----------|-----------------------------------------------|
|meta_file|TensorFlow模型序列化后保存的meta文件| |meta_file|TensorFlow模型序列化后保存的meta文件|
|ckpt_dir|TensorFlow模型保存checkpoint目录| |ckpt_dir|TensorFlow模型保存checkpoint目录|
|pb_file|Tensorflow保存的pb格式模型| |pb_file|Tensorflow保存的pb格式模型|
|in_nodes|输入tensor名,多个输入时以空格分隔| |in_nodes|输入tensor名,多个输入时以空格分隔|
|input_shape|输入tensor的shape(batch维度以None表示),shape之间以空格分隔,shape内各维度以逗号分隔,须与input_nodes对应| |input_shape|输入tensor的shape(batch维度以None表示),shape之间以空格分隔,shape内各维度以逗号分隔|
|input_format|输入数据格式,NHWC/NCHW/OTHER|
|output_nodes|输出tensor名,多个输出时以空格分隔| |output_nodes|输出tensor名,多个输出时以空格分隔|
|use_cuda|转换过程中是否使用GPU,默认True|
|save_dir|转换后的模型保存路径| |save_dir|转换后的模型保存路径|
目前支持tensorflow保存的checkpoint模型和将参数及模型结构序列化存储的pb模型,前者须指定meta_file和ckpt_file,后者则指定pb_file 目前支持tensorflow保存的checkpoint模型和将参数及模型结构序列化存储的pb模型,前者须指定meta_file和ckpt_dir,后者则指定pb_file
### 转换后模型文件说明
### 转换后模型文件说明
文件|作用 文件|作用
:------------------:|:-----------------------------------------------: :------------------:|:-----------------------------------------------:
my_model.py|基于PaddlePaddle实现的模型网络结构python代码 mymodel.py|基于PaddlePaddle实现的模型网络结构python代码
ref_name.txt|my_model.py中各tensor与原TensorFlow模型中的tensor对应关系 ref_name.info|my_model.py中各tensor与原TensorFlow模型中的tensor对应关系
const_\*/params_\*|转换后的模型参数文件 const_\*/params_\*|转换后的模型参数文件
save_var.list|模型载入过程中的变量list save_var.list|模型载入过程中的变量list
### 加载转换后的模型
加载转换后的模型主要注意以下三点
> 1. `import`模型结构,模型结构代码定义在my_model.py中
> 2. 注意原模型中输出与转换后模型输出名的映射关系,参考ref_name.txt
> 3. 模型需要加载的参数列表为save_var.list
仍然以上面转换后的vgg_16为例,下面通过示例展示如何加载模型,并进行预测
**【重要】代码中须注意,PaddlePaddle的图像输入为NCHW格式, 卷积的kernel形状为[filter_num, in_channel, height, width], 卷积输出形状为[batch, filter_num, height, width],这三点与tensorflow默认情况均不同**
```
#coding:utf-8
# paddle_vgg为转换后模型存储路径
from paddle_vgg.mymodel import KitModel
import paddle.fluid as fluid
import numpy
def model_initialize():
# 构建模型结构,并初始化参数
result = KitModel()
exe = fluid.Executor(fluid.CPUPlace())
exe.run(fluid.default_startup_program())
# 根据save_var.list列表,加载模型参数
var_list = list()
global_block = fluid.default_main_program().global_block()
with open('paddle_vgg/save_var.list') as f:
for line in f:
try:
# 过滤部分不需要加载的参数(OP配置参数)
var = global_block.var(line.strip())
var_list.append(var)
except:
pass
fluid.io.load_vars(exe, 'paddle_vgg', vars=var_list)
prog = fluid.default_main_program()
return exe, prog, result
def test_case(exe, prog, result):
# 测试随机数据输入
numpy.random.seed(13)
img_data = numpy.random.rand(1, 224, 224, 3)
# tf中输入为NHWC,PaddlePaddle则为NCHW,需transpose
img_data = numpy.transpose(img_data, (0, 3, 1, 2))
# input_0为输入数据的张量名,张量名和数据类型须与my_model.py中定义一致
r, = exe.run(fluid.default_main_program(),
feed={'input_0':numpy.array(img_data, dtype='float32')},
fetch_list=[result])
# 调用save_inference_model可将模型结构(当前以代码形式保存)和参数均序列化保存
# 保存后的模型可使用load_inference_model加载
# http://www.paddlepaddle.org/documentation/docs/zh/1.2/api_cn/api_guides/low_level/inference.html#api-guide-inference
fluid.io.save_inference_model("./paddle_model", ["input_0"], [result], exe)
if __name__ == "__main__":
exe, prog, result = model_initialize()
test_case(exe, prog, result)
```
<a id="开发介绍">
## 开发介绍
tensorflow2fluid在模型转换过程中,以tensorflow计算图中的节点为粒度,遍历图中的节点,并将每个节点所对应的OP转换为基于PaddlePaddle实现的python网络结构代码。
> 模型中所使用的代码,一般而言并不能直接能过模型训练时所使用的tensorflow代码中就能完全看出来。比如在python模型代码中所使用到的`tf.contrib.layers.fully_connected`就涉及到如下OP
|TensorFlow OP名|说明|
|:-----------------:|:----------------------------------------:|
|VariableV2|用于创建变量weights和bias|
|MatMul|输入与weights乘法操作|
|BiasAdd|输入值在Matmul后,再与bias相加|
|Relu|输出最后需要通过的激活函数操作|
|Idenitity|计算过程中的变量复制操作|
目前支持转换OP如文档最末附表所示,需要注意的是,**在实现转换过程中,代码转换基于各OP常见的使用情况**,此外,并非所有OP都需要转成PaddlePaddle对应的代码实现,如Identity,switch等OP,在实际转换过程中,都直接将输出表示为输入即可。
| TensorFlow OP | Python Api | TensorFlow OP | Python Api |
| ------------------- | ---------- | ---------------------- | ---------- |
| VariableV2 | 1 | placeholderwithdefault | 17 |
| Identity | 2 | switch | 18 |
| Placeholder | 3 | merge | 19 |
| Const | 4 | MaxPool | 20 |
| Conv2D | 5 | Squeeze | 21 |
| BiasAdd | 6 | Add | 22 |
| Relu | 7 | Mean | 23 |
| Conv2dBackpropInput | 8 | DepthwiseConv2dNative | 24 |
| FusedBatchNorm | 9 | Pad | 25 |
| ConcatV2 | 10 | StridedSlice | 26 |
| AvgPool | 11 | ResizeNearestNeighbor | 27 |
| Rsqrt | 12 | Maximum | 28 |
| Mul | 13 | Minimum | 9 |
| Sub | 14 | Sigmoid | 30 |
| Shape | 15 | Pack | 31 |
| Reshape | 16 | | |
tensorflow2fluid仍在持续开发阶段中,也非常欢迎用户贡献自己的代码,或者通过issue的方式提出建议和需求。
<a id="对比实验">
## 对比实验
我们在tensorflow-models上测试了数个模型的前向预测过程,通过输入1000个随机数据在原模型和转换后的模型上进行预测,得到的平均diff大小如下表所示(-表示待统计)
**注意:模型在预测时,设定参数is_training=False,去除dropout层的影响**
Model|Pre-trained Model|Average Diff|Max Diff
:--------------:|:----------------------------------------------:|:-----------------:|:-----------------:
[vgg_16](https://github.com/tensorflow/models/blob/master/research/slim/nets/vgg.py)|[vgg_16_2016_08_28.tar.gz](http://download.tensorflow.org/models/vgg_16_2016_08_28.tar.gz)|1.7e-07|1.9e-06
[vgg_19](https://github.com/tensorflow/models/blob/master/research/slim/nets/vgg.py)|[vgg_19_2016_08_28.tar.gz](http://download.tensorflow.org/models/vgg_19_2016_08_28.tar.gz)|1.7e-07|1.7e-06
[resnet_v1_50](https://github.com/tensorflow/models/blob/master/research/slim/nets/resnet_v1.py)|[resnet_v1_50_2016_08_28.tar.gz](http://download.tensorflow.org/models/resnet_v1_50_2016_08_28.tar.gz)|2.0e-09|1.1e-06
[resnet_v1_101](https://github.com/tensorflow/models/blob/master/research/slim/nets/resnet_v1.py)|[resnet_v1_101_2016_08_28.tar.gz](http://download.tensorflow.org/models/resnet_v1_101_2016_08_28.tar.gz)|1.8e-09|4.7e-07
[alexnet_v2](https://github.com/tensorflow/models/blob/master/research/slim/nets/alexnet.py)|-|1.7e-07|1.0e-06|
[inception_v3](https://github.com/tensorflow/models/blob/master/research/slim/nets/inception_v3.py)|[inception_v3_2016_08_28.tar.gz](http://download.tensorflow.org/models/inception_v3_2016_08_28.tar.gz)|1.6e-04|1.9e-05
## Link ## Link
本目录下部分代码参考了MMdnn-Tensorflow,对此表示感谢! 本目录下部分代码参考了MMdnn-Tensorflow,对此表示感谢!
......
import sys
import math
val1 = map(float, open(sys.argv[1]).read().strip().split('\n'))
val2 = map(float, open(sys.argv[2]).read().strip().split('\n'))
if len(val1) != len(val2):
raise Exception("Not Same Length")
max_diff = 0
avg_diff = 0
for i in range(len(val1)):
diff = math.fabs(val1[i] - val2[i])
if diff > max_diff:
max_diff = diff
avg_diff += diff
avg_diff /= len(val1)
print("max_diff: {}\tavg_diff: {}".format(max_diff, avg_diff))
# coding:utf-8
import sys
sys.path.append("..")
from paddle_resnet_v1_101.mymodel import KitModel
import paddle.fluid as fluid
import numpy
use_cuda = True
def model_initialize():
# 构建模型结构,并初始化参数
result = KitModel()
if use_cuda:
exe = fluid.Executor(fluid.CUDAPlace(0))
else:
exe = fluid.Executor(fluid.CPUPlace())
exe.run(fluid.default_startup_program())
# 根据save_var.list列表,加载模型参数
var_list = list()
global_block = fluid.default_main_program().global_block()
with open('../paddle_resnet_v1_101/save_var.list') as f:
for line in f:
try:
# 过滤部分不需要加载的参数(OP配置参数)
var = global_block.var(line.strip())
var_list.append(var)
except:
pass
fluid.io.load_vars(exe, '../paddle_resnet_v1_101', vars=var_list)
prog = fluid.default_main_program()
return exe, prog, result
def test_case(exe, prog, result):
# 测试随机数据输入
numpy.random.seed(13)
img_data = numpy.random.rand(1000, 224, 224, 3)
# tf中输入为NHWC,PaddlePaddle则为NCHW,需transpose
img_data = numpy.transpose(img_data, (0, 3, 1, 2))
# input_0为输入数据的张量名,张量名和数据类型须与my_model.py中定义一致
for i in range(0, 50):
r, = exe.run(
fluid.default_main_program(),
feed={
'input_0':
numpy.array(img_data[i * 20:i * 20 + 20], dtype='float32')
},
fetch_list=[result])
r = r.flatten()
files = open('fluid_resnet_v1_101.result', 'a+')
for i in range(0, r.shape[0]):
files.write(str(r[i]) + '\n')
files.close()
# 调用save_inference_model可将模型结构(当前以代码形式保存)和参数均序列化保存
# 保存后的模型可使用load_inference_model加载
# http://www.paddlepaddle.org/documentation/docs/zh/1.2/api_cn/api_guides/low_level/inference.html#api-guide-inference
# fluid.io.save_inference_model("./paddle_model", ["input_0"], [result], exe)
if __name__ == "__main__":
exe, prog, result = model_initialize()
test_case(exe, prog, result)
# coding:utf-8
import sys
sys.path.append("..")
from paddle_resnet_v1_50.mymodel import KitModel
import paddle.fluid as fluid
import numpy
use_cuda = True
def model_initialize():
# 构建模型结构,并初始化参数
result = KitModel()
if use_cuda:
exe = fluid.Executor(fluid.CUDAPlace(0))
else:
exe = fluid.Executor(fluid.CPUPlace())
exe.run(fluid.default_startup_program())
# 根据save_var.list列表,加载模型参数
var_list = list()
global_block = fluid.default_main_program().global_block()
with open('../paddle_resnet_v1_50/save_var.list') as f:
for line in f:
try:
# 过滤部分不需要加载的参数(OP配置参数)
var = global_block.var(line.strip())
var_list.append(var)
except:
pass
fluid.io.load_vars(exe, '../paddle_resnet_v1_50', vars=var_list)
prog = fluid.default_main_program()
return exe, prog, result
def test_case(exe, prog, result):
# 测试随机数据输入
numpy.random.seed(13)
img_data = numpy.random.rand(1000, 224, 224, 3)
# tf中输入为NHWC,PaddlePaddle则为NCHW,需transpose
img_data = numpy.transpose(img_data, (0, 3, 1, 2))
# input_0为输入数据的张量名,张量名和数据类型须与my_model.py中定义一致
for i in range(0, 50):
r, = exe.run(
fluid.default_main_program(),
feed={
'input_0':
numpy.array(img_data[i * 20:i * 20 + 20], dtype='float32')
},
fetch_list=[result])
r = r.flatten()
files = open('fluid_resnet_v1_50.result', 'a+')
for i in range(0, r.shape[0]):
files.write(str(r[i]) + '\n')
files.close()
# 调用save_inference_model可将模型结构(当前以代码形式保存)和参数均序列化保存
# 保存后的模型可使用load_inference_model加载
# http://www.paddlepaddle.org/documentation/docs/zh/1.2/api_cn/api_guides/low_level/inference.html#api-guide-inference
# fluid.io.save_inference_model("./paddle_model", ["input_0"], [result], exe)
if __name__ == "__main__":
exe, prog, result = model_initialize()
test_case(exe, prog, result)
rm -rf fluid_vgg_19.result
python vgg_19_infer.py
echo "paddle fluid vgg_19 model"
python diff.py fluid_vgg_19.result tf_vgg_19.result
rm -rf fluid_vgg_16.result
python vgg_16_infer.py
echo "paddle fluid vgg_16 model"
python diff.py fluid_vgg_16.result tf_vgg_16.result
rm -rf fluid_resnet_v1_50.result
python resnet_v1_50_infer.py
echo "paddle fluid resnet_v1_50 model"
python diff.py fluid_resnet_v1_50.result tf_resnet_v1_50.result
rm -rf fluid_resnet_v1_101.result
python resnet_v1_101_infer.py
echo "paddle fluid resnet_v1_101 model"
python diff.py fluid_resnet_v1_101.result tf_resnet_v1_101.result
# coding:utf-8
import sys
sys.path.append("..")
from paddle_vgg_16.mymodel import KitModel
import paddle.fluid as fluid
import numpy
use_cuda = True
def model_initialize():
# 构建模型结构,并初始化参数
result = KitModel()
if use_cuda:
exe = fluid.Executor(fluid.CUDAPlace(0))
else:
exe = fluid.Executor(fluid.CPUPlace())
exe.run(fluid.default_startup_program())
# 根据save_var.list列表,加载模型参数
var_list = list()
global_block = fluid.default_main_program().global_block()
with open('../paddle_vgg_16/save_var.list') as f:
for line in f:
try:
# 过滤部分不需要加载的参数(OP配置参数)
var = global_block.var(line.strip())
var_list.append(var)
except:
pass
fluid.io.load_vars(exe, '../paddle_vgg_16', vars=var_list)
prog = fluid.default_main_program()
return exe, prog, result
def test_case(exe, prog, result):
# 测试随机数据输入
numpy.random.seed(13)
img_data = numpy.random.rand(1000, 224, 224, 3)
# tf中输入为NHWC,PaddlePaddle则为NCHW,需transpose
img_data = numpy.transpose(img_data, (0, 3, 1, 2))
# input_0为输入数据的张量名,张量名和数据类型须与my_model.py中定义一致
for i in range(0, 50):
r, = exe.run(
fluid.default_main_program(),
feed={
'input_0':
numpy.array(img_data[i * 20:i * 20 + 20], dtype='float32')
},
fetch_list=[result])
r = r.flatten()
files = open('fluid_vgg_16.result', 'a+')
for i in range(0, r.shape[0]):
files.write(str(r[i]) + '\n')
files.close()
# 调用save_inference_model可将模型结构(当前以代码形式保存)和参数均序列化保存
# 保存后的模型可使用load_inference_model加载
# http://www.paddlepaddle.org/documentation/docs/zh/1.2/api_cn/api_guides/low_level/inference.html#api-guide-inference
# fluid.io.save_inference_model("./paddle_model", ["input_0"], [result], exe)
if __name__ == "__main__":
exe, prog, result = model_initialize()
test_case(exe, prog, result)
# coding:utf-8
import sys
sys.path.append("..")
from paddle_vgg_19.mymodel import KitModel
import paddle.fluid as fluid
import numpy
use_cuda = True
def model_initialize():
# 构建模型结构,并初始化参数
result = KitModel()
if use_cuda:
exe = fluid.Executor(fluid.CUDAPlace(0))
else:
exe = fluid.Executor(fluid.CPUPlace())
exe.run(fluid.default_startup_program())
# 根据save_var.list列表,加载模型参数
var_list = list()
global_block = fluid.default_main_program().global_block()
with open('../paddle_vgg_19/save_var.list') as f:
for line in f:
try:
# 过滤部分不需要加载的参数(OP配置参数)
var = global_block.var(line.strip())
var_list.append(var)
except:
pass
fluid.io.load_vars(exe, '../paddle_vgg_19', vars=var_list)
prog = fluid.default_main_program()
return exe, prog, result
def test_case(exe, prog, result):
# 测试随机数据输入
numpy.random.seed(13)
img_data = numpy.random.rand(1000, 224, 224, 3)
# tf中输入为NHWC,PaddlePaddle则为NCHW,需transpose
img_data = numpy.transpose(img_data, (0, 3, 1, 2))
# input_0为输入数据的张量名,张量名和数据类型须与my_model.py中定义一致
for i in range(0, 50):
r, = exe.run(
fluid.default_main_program(),
feed={
'input_0':
numpy.array(img_data[i * 20:i * 20 + 20], dtype='float32')
},
fetch_list=[result])
r = r.flatten()
files = open('fluid_vgg_19.result', 'a+')
for i in range(0, r.shape[0]):
files.write(str(r[i]) + '\n')
files.close()
# 调用save_inference_model可将模型结构(当前以代码形式保存)和参数均序列化保存
# 保存后的模型可使用load_inference_model加载
# http://www.paddlepaddle.org/documentation/docs/zh/1.2/api_cn/api_guides/low_level/inference.html#api-guide-inference
# fluid.io.save_inference_model("./paddle_model", ["input_0"], [result], exe)
if __name__ == "__main__":
exe, prog, result = model_initialize()
test_case(exe, prog, result)
from tensorflow.contrib.slim.nets import inception
from tensorflow.contrib.slim.nets import vgg as vgg
from tensorflow.contrib.slim.nets import resnet_v1 as resnet_v1
import tensorflow.contrib.slim as slim
import tensorflow as tf
from six import text_type as _text_type
def inception_v3(ckpt_file):
def get_tuned_variables():
CHECKPOINT_EXCLUDE_SCOPES = 'InceptionV3/Logits,InceptionV3/AuxLogits'
exclusions = [
scope.strip() for scope in CHECKPOINT_EXCLUDE_SCOPES.split(',')
]
variables_to_restore = []
for var in slim.get_model_variables():
excluded = False
for exclusion in exclusions:
if var.op.name.startswith(exclusion):
excluded = True
break
if not excluded:
variables_to_restore.append(var)
return variables_to_restore
img_size = inception.inception_v3.default_image_size
img = tf.placeholder(
tf.float32, shape=[None, img_size, img_size, 3], name='inputs')
with slim.arg_scope(inception.inception_v3_arg_scope()):
logits, _ = inception.inception_v3(
img, num_classes=1000, is_training=False)
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
load_model = tf.contrib.slim.assign_from_checkpoint_fn(
ckpt_file, get_tuned_variables(), ignore_missing_vars=True)
load_model(sess)
return sess
def resnet_v1_50(ckpt_file):
img_size = resnet_v1.resnet_v1.default_image_size
img = tf.placeholder(
tf.float32, shape=[None, img_size, img_size, 3], name='inputs')
with slim.arg_scope(resnet_v1.resnet_arg_scope()):
net, endpoint = resnet_v1.resnet_v1_50(
img, num_classes=1000, is_training=False)
sess = tf.Session()
load_model = tf.contrib.slim.assign_from_checkpoint_fn(
ckpt_file, tf.contrib.slim.get_model_variables("resnet_v1_50"))
load_model(sess)
return sess
def resnet_v1_101(ckpt_file):
img_size = resnet_v1.resnet_v1.default_image_size
img = tf.placeholder(
tf.float32, shape=[None, img_size, img_size, 3], name='inputs')
with slim.arg_scope(resnet_v1.resnet_arg_scope()):
net, endpoint = resnet_v1.resnet_v1_101(
img, num_classes=1000, is_training=False)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
load_model = tf.contrib.slim.assign_from_checkpoint_fn(
ckpt_file, tf.contrib.slim.get_model_variables("resnet_v1_101"))
load_model(sess)
return sess
def vgg_16(ckpt_file):
img_size = vgg.vgg_16.default_image_size
inputs = tf.placeholder(
tf.float32, shape=[None, img_size, img_size, 3], name="inputs")
logits, endpoint = vgg.vgg_16(inputs, num_classes=1000, is_training=False)
sess = tf.Session()
load_model = tf.contrib.slim.assign_from_checkpoint_fn(
ckpt_file, tf.contrib.slim.get_model_variables("vgg_16"))
load_model(sess)
return sess
def vgg_19(ckpt_file):
img_size = vgg.vgg_19.default_image_size
inputs = tf.placeholder(
tf.float32, shape=[None, img_size, img_size, 3], name="inputs")
logits, endpoint = vgg.vgg_19(inputs, num_classes=1000, is_training=False)
sess = tf.Session()
load_model = tf.contrib.slim.assign_from_checkpoint_fn(
ckpt_file, tf.contrib.slim.get_model_variables("vgg_19"))
load_model(sess)
return sess
def save_checkpoint(sess, save_dir):
saver = tf.train.Saver()
saver.save(sess, save_dir + "/model")
def get_parser():
import argparse
parser = argparse.ArgumentParser()
parser.add_argument(
"--model",
"-m",
type=_text_type,
default=None,
help="inception_v3/resnet_v1_50/resnet_v1_101/vgg_16/vgg_19")
parser.add_argument(
"--ckpt_file",
"-c",
type=_text_type,
default=None,
help="parameters ckpt file")
parser.add_argument(
"--save_dir", "-s", type=_text_type, default=None, help="model path")
return parser
if __name__ == "__main__":
parser = get_parser()
args = parser.parse_args()
sess = None
if args.model is None or args.save_dir is None or args.ckpt_file is None:
raise Exception("--model, --ckpt_file and --save_dir are needed")
if args.model == "inception_v3":
sess = inception_v3(args.ckpt_file)
elif args.model == "resnet_v1_50":
sess = resnet_v1_50(args.ckpt_file)
elif args.model == "resnet_v1_101":
sess = resnet_v1_101(args.ckpt_file)
elif args.model == "vgg_16":
sess = vgg_16(args.ckpt_file)
elif args.model == "vgg_19":
sess = vgg_19(args.ckpt_file)
else:
raise Exception(
"Only support inception_v3/resnet_v1_50/resnet_v1_101/vgg_16/vgg_19"
)
save_checkpoint(sess, args.save_dir)
export CUDA_VISIBLE_DEVICES=-1
wget http://download.tensorflow.org/models/resnet_v1_101_2016_08_28.tar.gz
tar xzvf resnet_v1_101_2016_08_28.tar.gz
python export_to_checkpoint.py --model resnet_v1_101 --ckpt_file resnet_v1_101.ckpt --save_dir resnet_v1_101_checkpoint
rm resnet_v1_101_2016_08_28.tar.gz resnet_v1_101.ckpt
tf2fluid --meta_file resnet_v1_101_checkpoint/model.meta \
--ckpt_dir resnet_v1_101_checkpoint \
--in_nodes inputs \
--input_shape None,224,224,3 \
--output_nodes resnet_v1_101/predictions/Softmax \
--save_dir paddle_resnet_v1_101
export CUDA_VISIBLE_DEVICES=-1
wget http://download.tensorflow.org/models/resnet_v1_50_2016_08_28.tar.gz
tar xzvf resnet_v1_50_2016_08_28.tar.gz
python export_to_checkpoint.py --model resnet_v1_50 --ckpt_file resnet_v1_50.ckpt --save_dir resnet_v1_50_checkpoint
rm resnet_v1_50_2016_08_28.tar.gz resnet_v1_50.ckpt
tf2fluid --meta_file resnet_v1_50_checkpoint/model.meta \
--ckpt_dir resnet_v1_50_checkpoint \
--in_nodes inputs \
--input_shape None,224,224,3 \
--output_nodes resnet_v1_50/predictions/Softmax \
--save_dir paddle_resnet_v1_50
export CUDA_VISIBLE_DEVICES=-1
wget http://download.tensorflow.org/models/vgg_16_2016_08_28.tar.gz
tar xzvf vgg_16_2016_08_28.tar.gz
python export_to_checkpoint.py --model vgg_16 --ckpt_file vgg_16.ckpt --save_dir vgg_16_checkpoint
rm vgg_16_2016_08_28.tar.gz vgg_16.ckpt
tf2fluid --meta_file vgg_16_checkpoint/model.meta \
--ckpt_dir vgg_16_checkpoint \
--in_nodes inputs \
--input_shape None,224,224,3 \
--output_nodes vgg_16/fc8/squeezed \
--save_dir paddle_vgg_16
export CUDA_VISIBLE_DEVICES=-1
wget http://download.tensorflow.org/models/vgg_19_2016_08_28.tar.gz
tar xzvf vgg_19_2016_08_28.tar.gz
python export_to_checkpoint.py --model vgg_19 --ckpt_file vgg_19.ckpt --save_dir vgg_19_checkpoint
rm vgg_19_2016_08_28.tar.gz vgg_19.ckpt
tf2fluid --meta_file vgg_19_checkpoint/model.meta \
--ckpt_dir vgg_19_checkpoint \
--in_nodes inputs \
--input_shape None,224,224,3 \
--output_nodes vgg_19/fc8/squeezed \
--save_dir paddle_vgg_19
from __future__ import absolute_import
from setuptools import setup, find_packages
from io import open
setup(
name='tensorflow2fluid',
version='0.0.1',
description='Translate TensorFlow Model to PaddlePaddle',
url='http://paddlepaddle.org',
author = 'PaddlePaddle Development Group',
author_email='paddle-dev@baidu.com',
license='Apache 2',
packages=find_packages(),
install_requires=[],
entry_points={
'console_scripts': [
'tf2fluid = src.convert:_main',
],
},
)
from paddle_emitter import PaddleEmitter
from tensorflow_parser import TensorflowCkptParser
from tensorflow_parser import TensorflowPbParser
from six import text_type as _text_type
import argparse
import sys
import os
def _get_parser():
parser = argparse.ArgumentParser()
parser.add_argument("--meta_file", "-m", type=_text_type, default=None, help="meta file path for checkpoint format")
parser.add_argument("--ckpt_dir", "-c", type=_text_type, default=None, help="checkpoint directory")
parser.add_argument("--pb_file", "-p", type=_text_type, default=None, help="pb model file path")
parser.add_argument("--in_nodes", "-i", type=_text_type, nargs="+", default=None, help="input nodes name")
parser.add_argument("--input_shape", "-is", type=_text_type, nargs="+", default=None, help="input tensor shape")
parser.add_argument("--output_nodes", "-o", type=_text_type, nargs="+", default=None, help="output nodes name")
parser.add_argument("--save_dir", "-s", type=_text_type, default=None, help="path to save transformed paddle model")
parser.add_argument("--version", "-v", action="version", version="tensorflow2fluid version=0.0.1 Release @2019.01.28")
return parser
def _convert(args):
if args.meta_file is None and args.pb_file is None:
raise Exception("Need to define --meta_file or --pb_file")
assert args.in_nodes is not None
assert args.output_nodes is not None
assert args.input_shape is not None
assert args.save_dir is not None
if os.path.exists(args.save_dir):
sys.stderr.write("save_dir already exists, change to a new path\n")
return
os.makedirs(args.save_dir)
input_shape = list()
for shape_str in args.input_shape:
items = shape_str.split(',')
for i in range(len(items)):
if items[i] != "None":
items[i] = int(items[i])
else:
items[i] = None
input_shape.append(items)
sys.stderr.write("\nLoading tensorflow model......\n")
if args.meta_file is not None:
parser = TensorflowCkptParser(args.meta_file, args.ckpt_dir, args.output_nodes, input_shape, args.in_nodes)
else:
parser = TensorflowPbParser(args.pb_file, args.output_nodes, input_shape, args.in_nodes)
sys.stderr.write("Tensorflow model loaded!\n")
emitter = PaddleEmitter(parser, args.save_dir)
emitter.run()
open(args.save_dir+"/__init__.py", "w").close()
def _main():
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
parser = _get_parser()
args = parser.parse_args()
_convert(args)
if __name__ == "__main__":
_main()
此差异已折叠。
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import tensorflow
from tensorflow_graph import TensorflowGraph
from tensorflow.python.framework import tensor_util
from tensorflow.python.tools import strip_unused_lib
from tensorflow.python.framework import dtypes
class TensorflowCkptParser(object):
def __init__(self,
meta_file,
checkpoint_file,
dest_nodes,
input_shape=None,
in_nodes=None):
graph_def = None
self.weights = None
with tensorflow.Session() as sess:
if meta_file is None:
raise Exception("meta_file must be provided")
new_saver = tensorflow.train.import_meta_graph(meta_file)
if checkpoint_file is not None:
self.weights = dict()
new_saver.restore(
sess, tensorflow.train.latest_checkpoint(checkpoint_file))
for var in tensorflow.global_variables():
value = var.eval(sess)
self.weights[var.name.split(':')[0]] = value
graph_def, ver = tensorflow.get_default_graph()._as_graph_def(
add_shapes=True)
# self.sess = sess
if in_nodes is not None and input_shape is not None:
graph_def = strip_unused_lib.strip_unused(
input_graph_def=graph_def,
input_node_names=in_nodes,
output_node_names=dest_nodes,
placeholder_type_enum=dtypes.float32.as_datatype_enum)
self.tf_graph = TensorflowGraph(graph_def)
else:
raise Exception('in_nodes and output_nodes need be provided')
self.tf_graph.build()
class TensorflowPbParser(object):
def __init__(self, pb_file, dest_nodes, input_shape=None, in_nodes=None):
with open(pb_file) as f:
serialized = f.read()
tensorflow.reset_default_graph()
original_graph_def = tensorflow.GraphDef()
original_graph_def.ParseFromString(serialized)
# tensorflow.import_graph_def(origin_graph_def, name="")
# self.sess = tensorflow.Session(graph=tf.get_default_graph())
# self.sess.run(tensorflow.global_variables_initializer())
original_graph_def = strip_unused_lib.strip_unused(
input_graph_def=original_graph_def,
input_node_names=in_nodes,
output_node_names=dest_nodes,
placeholder_type_enum=dtypes.float32.as_datatype_enum)
graph_def = tensorflow.GraphDef()
graph_def.ParseFromString(original_graph_def.SerializeToString())
in_type_list = dict()
for node in graph_def.node:
if node.name in in_nodes:
in_type_list[node.name] = node.attr['dtype'].type
input_shape = list(input_shape)
if not isinstance(input_shape[0], list):
input_shape = [input_shape]
input_map = dict()
for i in range(len(input_shape)):
if in_type_list[in_nodes[i]] == 1 or in_type_list[
in_nodes[i]] == 0:
dtype = tensorflow.float32
x = tensorflow.placeholder(dtype, shape=input_shape[i])
elif in_type_list[in_nodes[i]] == 3:
dtype = tensorflow.int32
x = tensorflow.placehoder(dtype, shape=input_shape[i])
else:
raise Exception(
"Unexpected dtype for input, only support float32 and int32 now"
)
input_map[in_nodes[i] + ":0"] = x
tensorflow.import_graph_def(graph_def, name="", input_map=input_map)
graph_def = tensorflow.get_default_graph()._as_graph_def(
add_shapes=True)[0]
node = graph_def.node[0]
self.tf_graph = TensorflowGraph(graph_def)
self.tf_graph.build()
self.weights = dict()
for node in graph_def.node:
if node.op.lower() == "const":
try:
node.attr['value'].tensor.tensor_content
weight = tensor_util.MakeNdarray(node.attr['value'].tensor)
self.weights[node.name] = weight
except:
continue
from paddle_emitter import PaddleEmitter
from tensorflow_parser import TensorflowCkptParser
from tensorflow_parser import TensorflowPbParser
class Transformer(object):
def __init__(self, meta_file, ckpt_file, out_nodes, in_shape, in_nodes, save_dir):
self.parser = TensorflowCkptParser(meta_file, ckpt_file, out_nodes,
in_shape, in_nodes)
self.emitter = PaddleEmitter(self.parser, save_dir)
def transform_code(self):
codes = self.emitter.run()
def run(self):
self.transform_code()
class PbTransformer(object):
def __init__(self, pb_file, out_nodes, in_shape, in_nodes, save_dir):
self.parser = TensorflowPbParser(pb_file, out_nodes, in_shape, in_nodes)
self.emitter = PaddleEmitter(self.parser, save_dir)
node = self.parser.tf_graph.tf_graph.node[0]
def transform_code(self):
codes = self.emitter.run()
def run(self):
self.transform_code()
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from paddle_emitter import PaddleEmitter
from tensorflow_parser import TensorflowCkptParser
from tensorflow_parser import TensorflowPbParser
from six import text_type as _text_type
from utils import *
import argparse
import logging
import os
logging.basicConfig(level=logging.DEBUG)
def _get_parser():
parser = argparse.ArgumentParser()
parser.add_argument(
"--meta_file",
"-m",
type=_text_type,
default=None,
help="meta file path for checkpoint format")
parser.add_argument(
"--ckpt_dir",
"-c",
type=_text_type,
default=None,
help="checkpoint directory")
parser.add_argument(
"--pb_file",
"-p",
type=_text_type,
default=None,
help="pb model file path")
parser.add_argument(
"--in_nodes",
"-i",
type=_text_type,
nargs="+",
default=None,
help="input nodes name")
parser.add_argument(
"--input_shape",
"-is",
type=_text_type,
nargs="+",
default=None,
help="input tensor shape")
parser.add_argument(
"--output_nodes",
"-o",
type=_text_type,
nargs="+",
default=None,
help="output nodes name")
parser.add_argument(
"--save_dir",
"-s",
type=_text_type,
default=None,
help="path to save transformed paddle model")
parser.add_argument(
"--input_format",
"-sf",
type=_text_type,
default=None,
help="input data format(NHWC/NCHW or OTHER)")
parser.add_argument(
"--use_cuda",
"-u",
type=_text_type,
default="True",
help="True for use gpu")
return parser
def run(args):
if args.meta_file is None and args.pb_file is None:
raise Exception("Need to define --meta_file or --pb_file")
if args.input_format is None:
raise Exception("Input format need to be defined(NHWC, NCHW or OTHER)")
assert args.use_cuda == "True" or args.use_cuda == "False"
if args.use_cuda == "False":
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
if args.input_format == "NHWC":
input_format = NHWC
elif args.input_format == "NCHW":
input_format = NCHW
elif args.input_format == "OTHER":
input_format = OTHER
else:
raise Exception("Can not identify input format(NHWC/NCHW/OTHER)")
assert args.in_nodes is not None
assert args.output_nodes is not None
assert args.input_shape is not None
assert args.save_dir is not None
if not os.path.exists(args.save_dir):
os.makedirs(args.save_dir)
input_shape = list()
for shape_str in args.input_shape:
items = shape_str.split(',')
for i in range(len(items)):
if items[i] != "None":
items[i] = int(items[i])
else:
items[i] = None
input_shape.append(items)
logging.info("Loading tensorflow model...")
if args.meta_file is not None:
parser = TensorflowCkptParser(args.meta_file, args.ckpt_dir,
args.output_nodes, input_shape,
args.in_nodes, input_format)
else:
parser = TensorflowPbParser(args.pb_file, args.output_nodes,
input_shape, args.in_nodes, input_format)
logging.info("Tensorflow model loaded!")
emitter = PaddleEmitter(parser, args.save_dir)
emitter.run()
open(args.save_dir + "/__init__.py", "w").close()
def _main():
parser = _get_parser()
args = parser.parse_args()
run(args)
if __name__ == "__main__":
_main()
...@@ -12,16 +12,21 @@ ...@@ -12,16 +12,21 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from name_generator import NameGenerator from utils import *
import collections
class GraphNode(object): class GraphNode(object):
def __init__(self, layer): def __init__(self, layer, layer_name=None):
self.inputs = list() self.inputs = list()
self.outputs = list() self.outputs = list()
self.layer = layer self.layer = layer
self.ref_name = None self.ref_name = None
self.output_name = None self.output_name = None
if layer_name is not None:
self.layer_name = layer_name
else:
self.layer_name = layer.name
def __hash__(self): def __hash__(self):
return hash(self.layer.name) return hash(self.layer.name)
...@@ -34,14 +39,14 @@ class GraphNode(object): ...@@ -34,14 +39,14 @@ class GraphNode(object):
class Graph(object): class Graph(object):
def __init__(self, model): def __init__(self, model):
self.node_map = dict() self.node_map = collections.OrderedDict()
self.input_nodes = list() self.input_nodes = list()
self.output_nodes = list() self.output_nodes = list()
self.topological_sort = list() self.topological_sort = list()
self.model = model self.model = model
self.name_generator = NameGenerator() self.name_generator = NameGenerator()
def build(self): def build(self, input_format):
self._make_input_nodes() self._make_input_nodes()
self._make_output_nodes() self._make_output_nodes()
self._get_topological_sort() self._get_topological_sort()
...@@ -78,9 +83,26 @@ class Graph(object): ...@@ -78,9 +83,26 @@ class Graph(object):
for node_name in self.topological_sort: for node_name in self.topological_sort:
node = self.node_map[node_name] node = self.node_map[node_name]
ref_name = self.name_generator.get_name(node) ref_name = self.name_generator.get_name(node)
if node.layer_type == 'split' or node.layer_type == 'splitv':
index = '0'
if len(node_name.split(':')) == 2:
index = node_name.split(':')[-1]
ref_name += '[{}]'.format(index)
self.node_map[node.layer.name].ref_name = ref_name self.node_map[node.layer.name].ref_name = ref_name
self.node_map[node.layer.name].output_name = ref_name.split('[')[0] self.node_map[node.layer.name].output_name = ref_name.split('[')[0]
for node_name, node in self.node_map.items():
ref_name = self.name_generator.get_name(node)
if node.layer_type == 'split' or node.layer_type == 'splitv':
index = '0'
if len(node_name.split(':')) == 2:
index = node_name.split(':')[-1]
ref_name += '[{}]'.format(index)
self.node_map[node_name].ref_name = ref_name
self.node_map[node_name].output_name = ref_name.split('[')[0]
def get_node(self, name): def get_node(self, name):
if name not in self.node_map: if name not in self.node_map:
raise Exception("Graph doesn't have node [%s]." % name) raise Exception("Graph doesn't have node [%s]." % name)
...@@ -88,9 +110,8 @@ class Graph(object): ...@@ -88,9 +110,8 @@ class Graph(object):
return self.node_map[name] return self.node_map[name]
def _make_connection(self, src, dst): def _make_connection(self, src, dst):
if src.layer_name == dst.layer_name or src.layer_name not in self.node_map or dst.layer_name not in self.node_map: if src.layer_name == dst.layer_name or src.layer_name not in \
self.node_map or dst.layer_name not in self.node_map:
raise Exception('Warning: Node not exist or there is a self-loop') raise Exception('Warning: Node not exist or there is a self-loop')
if src not in self.node_map[dst.layer_name].inputs: self.node_map[dst.layer_name].inputs.append(src)
self.node_map[dst.layer_name].inputs.append(src) self.node_map[src.layer_name].outputs.append(dst)
if dst not in self.node_map[src.layer_name].outputs:
self.node_map[src.layer_name].outputs.append(dst)
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import paddle.fluid as fluid
import sys
class ModelLoader(object):
def __init__(self, model_dir, use_cuda=False):
sys.path.append(model_dir)
mymodel = __import__("mymodel")
self.model = mymodel.Model()
self.model.build()
self.inputs = self.model.inputs
self.outputs = self.model.outputs
if use_cuda:
self.exe = fluid.Executor(fluid.CUDAPlace(0))
else:
self.exe = fluid.Executor(fluid.CPUPlace())
self.exe.run(fluid.default_startup_program())
var_list = list()
global_block = fluid.default_main_program().global_block()
with open(model_dir + "/save_var.list") as f:
for line in f:
try:
var = global_block.var(line.strip())
var_list.append(var)
except:
pass
fluid.io.load_vars(self.exe, model_dir, vars=var_list)
self.program = fluid.default_main_program()
def save_inference_model(self, save_dir):
fluid.io.save_inference_model(save_dir, self.model.inputs,
self.model.outputs, self.exe)
def inference(self, feed_dict):
result = self.exe.run(
self.program, feed=feed_dict, fetch_list=self.model.outputs)
return result
此差异已折叠。
...@@ -14,21 +14,36 @@ ...@@ -14,21 +14,36 @@
from graph import GraphNode, Graph from graph import GraphNode, Graph
from tensorflow.core.framework import attr_value_pb2 from tensorflow.core.framework import attr_value_pb2
from utils import *
class TensorflowGraphNode(GraphNode): class TensorflowGraphNode(GraphNode):
def __init__(self, layer): dtype_map = {1: "float32", 3: "int32", 9: "int64"}
super(TensorflowGraphNode, self).__init__(layer)
def __init__(self, layer, input_format, layer_name=None):
super(TensorflowGraphNode, self).__init__(layer, layer_name)
self.codes = list() self.codes = list()
self.data_format = 'NCHW' self.code = FluidCode()
self.ref_as_const = 0
self.data_format = input_format
@property @property
def layer_type(self): def layer_type(self):
return self.layer.op.lower() return self.layer.op.lower()
@property @property
def layer_name(self): def shape_dim_size(self):
return self.layer.name shape = self.layer.attr['_output_shapes']
return len(shape.list.shape[0].dim)
@property
def dtype(self):
dtype = self.get_attr("dtype")
if dtype in self.dtype_map:
dtype = self.dtype_map[dtype]
else:
raise Exception("Unknow dtype: {}".format(dtype))
return dtype
def get_attr(self, name, default_value=None): def get_attr(self, name, default_value=None):
if name in self.layer.attr: if name in self.layer.attr:
...@@ -36,33 +51,58 @@ class TensorflowGraphNode(GraphNode): ...@@ -36,33 +51,58 @@ class TensorflowGraphNode(GraphNode):
field = attr.WhichOneof('value') field = attr.WhichOneof('value')
val = getattr(attr, field) if field else default_value val = getattr(attr, field) if field else default_value
if isinstance(val, attr_value_pb2.AttrValue.ListValue): if isinstance(val, attr_value_pb2.AttrValue.ListValue):
return list(val.ListFields()[0][1]) result = list(val.ListFields()[0][1])
for i in range(len(result)):
if isinstance(result[i], int):
result[i] = int(result[i])
try:
if isinstance(result[i], long):
result[i] = int(result[i])
except:
pass
return result
else: else:
return val.decode('utf-8') if isinstance(val, bytes) else val return val if isinstance(val, bytes) else val
else: else:
return default_value return default_value
def clear_code(self):
self.code.clear()
class TensorflowGraph(Graph): class TensorflowGraph(Graph):
useless_type = ['identity', 'placeholderwithdefault', 'switch', 'merge']
def __init__(self, tf_graph): def __init__(self, tf_graph):
super(TensorflowGraph, self).__init__(tf_graph) super(TensorflowGraph, self).__init__(tf_graph)
self.tf_graph = tf_graph self.tf_graph = tf_graph
def build(self): def build(self, input_format):
skip_node = set(['const']) skip_node = set(['const'])
for i, layer in enumerate(self.tf_graph.node): for i, layer in enumerate(self.tf_graph.node):
self.node_map[layer.name] = TensorflowGraphNode(layer) self.node_map[layer.name] = TensorflowGraphNode(
layer, input_format)
for i, layer in enumerate(self.tf_graph.node): for i, layer in enumerate(self.tf_graph.node):
if layer.op.lower() in skip_node: if layer.op.lower() in skip_node:
continue continue
for pred in layer.input: for pred in layer.input:
if pred not in self.node_map and pred.split( if pred not in self.node_map and pred.split(
':')[0] in self.node_map: ':')[0] in self.node_map:
node = self.node_map[pred.split(':')[0]] pred_node = self.node_map[pred.split(':')[0]]
if node.layer_type == "switch": if pred_node.layer_type == "switch":
self._make_connection(node, self.node_map[layer.name]) self._make_connection(pred_node,
self.node_map[layer.name])
elif pred_node.layer_type == "split" or \
pred_node.layer_type == "splitv":
self.node_map[pred] = TensorflowGraphNode(
pred_node.layer, input_format, pred)
self._make_connection(self.node_map[pred],
self.node_map[layer.name])
self._make_connection(pred_node, self.node_map[pred])
else: else:
raise Exception("Need to fix here") raise Exception("Unsupported situation(name:[{}], \
OP[{}])".format(node.layer_name, node.layer_type))
elif pred in self.node_map: elif pred in self.node_map:
self._make_connection(self.node_map[pred], self._make_connection(self.node_map[pred],
...@@ -70,38 +110,26 @@ class TensorflowGraph(Graph): ...@@ -70,38 +110,26 @@ class TensorflowGraph(Graph):
else: else:
raise Exception("input: {} not in node_map".format(pred)) raise Exception("input: {} not in node_map".format(pred))
super(TensorflowGraph, self).build() super(TensorflowGraph, self).build(input_format)
self._remove_useless_nodes() self._process_useless_nodes()
self._check_dataformat() self._check_dataformat(input_format)
def _check_dataformat(self): def _check_dataformat(self, input_format):
ss = list() for i in range(len(self.topological_sort)):
for i in range(0, len(self.topological_sort)):
current_node = self.node_map[self.topological_sort[i]] current_node = self.node_map[self.topological_sort[i]]
if 'data_format' in current_node.layer.attr: if 'data_format'.encode() in current_node.layer.attr:
s = current_node.layer.attr['data_format'].s s = current_node.layer.attr['data_format'].s
if s != 'NHWC' and s != 'NCHW': if s != NHWC and s != NCHW:
raise Exception('Unkown dataformat {}'.format(s)) raise Exception('Unkown dataformat {}'.format(s))
ss.append(s) self.set_data_format(current_node, s)
if len(set(ss)) > 1: def _process_useless_nodes(self):
raise Exception("Two type of dataformat exist in this model")
if len(set(ss)) == 0:
return
for k, v in self.node_map.items():
self.node_map[k].data_format = ss[0]
def _remove_useless_nodes(self):
useless_type = set(
['identity', 'placeholderwithdefault', 'switch', 'merge'])
remove_index = list() remove_index = list()
for i in range(0, len(self.topological_sort)): for i in range(len(self.topological_sort)):
name = self.topological_sort[i] name = self.topological_sort[i]
current_node = self.node_map[name] current_node = self.node_map[name]
if current_node.layer_type in useless_type: if current_node.layer_type in self.useless_type:
input = current_node.inputs[0] input = current_node.inputs[0]
for node in current_node.outputs: for node in current_node.outputs:
for k in range(0, len(node.inputs)): for k in range(0, len(node.inputs)):
...@@ -118,5 +146,15 @@ class TensorflowGraph(Graph): ...@@ -118,5 +146,15 @@ class TensorflowGraph(Graph):
remove_index.append(i) remove_index.append(i)
remove_index.sort(reverse=True) remove_index.sort(reverse=True)
for i in range(0, len(remove_index)): for i in range(len(remove_index)):
del self.topological_sort[remove_index[i]] del self.topological_sort[remove_index[i]]
def set_data_format(self, node, data_format):
assert data_format == 'NHWC'.encode() or data_format == 'NCHW'.encode()
if node.data_format == data_format:
return
node.data_format = data_format
if len(node.outputs) == 0:
return
for output in node.outputs:
self.set_data_format(output, data_format)
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import tensorflow as tf
from tensorflow_graph import TensorflowGraph
from tensorflow.python.framework import tensor_util
from tensorflow.python.tools import strip_unused_lib
from tensorflow.python.framework import dtypes
import numpy
class TensorflowCkptParser(object):
def __init__(self,
meta_file,
checkpoint_file,
dest_nodes,
input_shape=None,
in_nodes=None,
input_format="NCHW".encode()):
graph_def = None
self.weights = None
self.inputs = in_nodes
self.outputs = dest_nodes
sess = tf.Session()
if meta_file is None:
raise Exception("meta_file must be provided")
new_saver = tf.train.import_meta_graph(meta_file)
if checkpoint_file is not None:
self.weights = dict()
new_saver.restore(sess,
tf.train.latest_checkpoint(checkpoint_file))
for var in tf.global_variables():
value = var.eval(sess)
self.weights[var.name.split(':')[0]] = value
self.infer = ModelInfer(sess)
graph_def, ver = tf.get_default_graph()._as_graph_def(add_shapes=True)
if in_nodes is not None and input_shape is not None:
graph_def = strip_unused_lib.strip_unused(
input_graph_def=graph_def,
input_node_names=in_nodes,
output_node_names=dest_nodes,
placeholder_type_enum=dtypes.float32.as_datatype_enum)
for node in graph_def.node:
if node.name in in_nodes:
index = in_nodes.index(node.name)
shape = [tf.Dimension(x) for x in input_shape[index]]
shape_proto = tf.TensorShape(shape).as_proto()
node.attr['_output_shapes'].list.shape.pop()
node.attr['_output_shapes'].list.shape.extend(
[shape_proto])
self.infer.gen_sample_data(node.name, input_shape[index])
self.tf_graph = TensorflowGraph(graph_def)
else:
raise Exception('in_nodes and output_nodes need be provided')
self.tf_graph.build(input_format)
class TensorflowPbParser(object):
def __init__(self,
pb_file,
dest_nodes,
input_shape=None,
in_nodes=None,
input_format="NCHW".encode()):
with open(pb_file, 'rb') as f:
serialized = f.read()
tf.reset_default_graph()
original_graph_def = tf.GraphDef()
original_graph_def.ParseFromString(serialized)
self.inputs = list()
self.outputs = dest_nodes
sess = tf.Session(graph=tf.get_default_graph())
sess.run(tf.global_variables_initializer())
self.infer = ModelInfer(sess)
original_graph_def = strip_unused_lib.strip_unused(
input_graph_def=original_graph_def,
input_node_names=in_nodes,
output_node_names=dest_nodes,
placeholder_type_enum=dtypes.float32.as_datatype_enum)
graph_def = tf.GraphDef()
graph_def.ParseFromString(original_graph_def.SerializeToString())
in_type_list = dict()
for node in graph_def.node:
if node.name in in_nodes:
in_type_list[node.name] = node.attr['dtype'].type
input_shape = list(input_shape)
if not isinstance(input_shape[0], list):
input_shape = [input_shape]
input_map = dict()
for i in range(len(input_shape)):
if in_type_list[in_nodes[i]] == 1 or in_type_list[
in_nodes[i]] == 0:
dtype = tf.float32
x = tf.placeholder(dtype, shape=input_shape[i])
elif in_type_list[in_nodes[i]] == 3:
dtype = tf.int32
x = tf.placehoder(dtype, shape=input_shape[i])
else:
raise Exception("Unexpected dtype for input, only support " \
"float32 and int32 now")
input_map[in_nodes[i] + ":0"] = x
self.inputs.append(x.name.split(':')[0])
self.infer.gen_sample_data(x.name, input_shape[i])
tf.import_graph_def(graph_def, name="", input_map=input_map)
graph_def = tf.get_default_graph()._as_graph_def(add_shapes=True)[0]
self.tf_graph = TensorflowGraph(graph_def)
self.tf_graph.build(input_format)
self.weights = dict()
for node in graph_def.node:
if node.op.lower() == "const":
try:
node.attr['value'].tensor.tensor_content
weight = tensor_util.MakeNdarray(node.attr['value'].tensor)
self.weights[node.name] = weight
except:
continue
class ModelInfer(object):
""" Trick method for tensorflow2fluid
There are some Operators in PaddlePaddle not support
tensor as parameter, like reshape/transpose, Because these
parameters should be fixed in PaddlePaddle. So we
provide 'ModelInfer' here to solove this problem.
"""
def __init__(self, sess):
self.sess = sess
self.inputs_sample_data = dict()
def gen_sample_data(self, tensor_name, shape):
self.inputs_sample_data[tensor_name] = list()
if shape[0] is None or shape[0] < 0:
for i in range(1, 4):
data = numpy.random.random_sample([i] + shape[1:])
self.inputs_sample_data[tensor_name].append(data)
else:
for i in range(1, 4):
data = numpy.random.random_sample(shape)
self.inputs_sample_data[tensor_name].append(data)
def get_shape_tensor(self, layer, output_shape=None):
""" return value of shape parameter
return value of shape parameter which are tensor type
in tensorflow model
"""
tensor_name = layer.name
if len(tensor_name.split(':')) < 2:
tensor_name = tensor_name + ':0'
output_tensor = self.sess.graph.get_tensor_by_name(tensor_name)
tensor_values = []
for i in range(0, 3):
inputs_tensors = dict()
for name, values in self.inputs_sample_data.items():
if len(name.split(':')) < 2:
name = name + ':0'
tensor = self.sess.graph.get_tensor_by_name(name)
inputs_tensors[tensor] = values[i]
r, = self.sess.run([output_tensor], inputs_tensors)
tensor_values.append(r.flatten())
compare01 = (tensor_values[0] == tensor_values[1])
compare12 = (tensor_values[1] == tensor_values[2])
if compare01.all() and compare12.all():
return tensor_values[0]
if (compare01 == compare12).all():
index = numpy.argwhere(compare01 == False).flatten()
if index.shape[0] != 1:
raise Exception("There's not only one unstable dimension")
tensor_values[0][index[0]] = -1
index = numpy.argwhere(tensor_values[0] < 0).flatten()
if index.shape[0] > 2:
raise Exception("There's more than two values less than zero")
if index.shape[0] == 2:
if output_shape is None:
raise Exception("Need output_shape parameter, " \
"get_shape_tensor(tensor_name, output_shape)")
tensor_values[0][index[1]] = output_shape[index[1]]
return tensor_values[0]
else:
raise Exception("Can not infer a stable shape tensor value")
def get_tensor_shape(self, layer):
shape = layer.attr['_output_shapes'].list.shape[0]
shape = numpy.array([dim.size for dim in shape.dim])
if numpy.argwhere(shape < 0).shape[0] <= 1:
return shape
tensor_name = layer.name
if len(tensor_name.split(':')) < 2:
tensor_name = tensor_name + ':0'
output_tensor = self.sess.graph.get_tensor_by_name(tensor_name)
shapes = []
for i in range(0, 3):
inputs_tensors = dict()
for name, values in self.inputs_sample_data.items():
if len(name.split(':')) < 2:
name = name + ':0'
tensor = self.sess.graph.get_tensor_by_name(name)
inputs_tensors[tensor] = values[i]
r, = self.sess.run([output_tensor], inputs_tensors)
shapes.append(numpy.array(r.shape))
compare01 = (shapes[0] == shapes[1])
compare12 = (shapes[1] == shapes[2])
if compare01.all() and compare12.all():
return shapes[0]
if (compare01 == compare12).all():
index = numpy.argwhere(compare01 == False).flatten()
if index.shape[0] != 1:
raise Exception("There's not only one unstable dimension")
if index[0] != 0:
raise Exception("Batch size not in the first dimension")
shapes[0][0] = -1
return shapes[0]
else:
raise Exception("Can not infer a stable tensor shape, failed!")
def get_const_tensor_value(self, layer):
tensor_name = layer.name
if len(tensor_name.split(':')) < 2:
tensor_name = tensor_name + ':0'
output_tensor = self.sess.graph.get_tensor_by_name(tensor_name)
result = []
for i in range(0, 3):
inputs_tensors = dict()
for name, values in self.inputs_sample_data.items():
if len(name.split(':')) < 2:
name = name + ':0'
tensor = self.sess.graph.get_tensor_by_name(name)
inputs_tensors[tensor] = values[i]
r, = self.sess.run([output_tensor], inputs_tensors)
result.append(r)
compare01 = (result[0] == result[1])
compare12 = (result[1] == result[2])
if compare01.all() and compare12.all():
return result[0]
else:
raise Exception("Can not infer a stable constant tensor value")
...@@ -12,11 +12,16 @@ ...@@ -12,11 +12,16 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
NHWC = 'NHWC'.encode()
NCHW = 'NCHW'.encode()
OTHER = 'OTHER'.encode()
SAME = 'SAME'.encode()
VALID = 'VALID'.encode()
class NameGenerator(object): class NameGenerator(object):
def __init__(self): def __init__(self):
self.param_index = 0 self.param_index = 0
self.input_index = 0
self.net_index = 0 self.net_index = 0
self.const_index = 0 self.const_index = 0
self.names = dict() self.names = dict()
...@@ -32,8 +37,7 @@ class NameGenerator(object): ...@@ -32,8 +37,7 @@ class NameGenerator(object):
ref_name = "param_" + str(self.param_index) ref_name = "param_" + str(self.param_index)
self.param_index += 1 self.param_index += 1
elif op_name == "placeholder": elif op_name == "placeholder":
ref_name = "input_" + str(self.input_index) ref_name = node.layer.name
self.input_index += 1
elif op_name == "const": elif op_name == "const":
ref_name = "const_" + str(self.const_index) ref_name = "const_" + str(self.const_index)
self.const_index += 1 self.const_index += 1
...@@ -44,3 +48,68 @@ class NameGenerator(object): ...@@ -44,3 +48,68 @@ class NameGenerator(object):
self.net_index += 1 self.net_index += 1
self.names[node.layer.name] = ref_name self.names[node.layer.name] = ref_name
return ref_name return ref_name
class LayerCode(object):
def __init__(self):
self.op = None
self.param_attr = dict()
self.input = None
self.output = None
self.str_code = None
def get_str_code(self):
if self.str_code is not None:
return self.str_code
layer_code0 = ""
if self.output is not None:
layer_code0 = layer_code0 + self.output + " = "
layer_code0 += "layers."
layer_code1 = self.op + "("
if self.input is not None:
layer_code1 = layer_code1 + self.input + ", "
layer_code2 = ""
for k, v in self.param_attr.items():
layer_code2 = layer_code2 + k + "=" + "{}".format(v) + ", "
layer_code2 = layer_code2.strip(", ")
layer_code = (
layer_code0 + layer_code1 + layer_code2).strip(", ") + ")"
return layer_code
class FluidCode(object):
def __init__(self):
self.codes = list()
def add_layer(self, op, input, output, param_attr=None):
if param_attr is None:
param_attr = dict()
layer_code = LayerCode()
layer_code.op = op
layer_code.input = input
layer_code.output = output
layer_code.param_attr = param_attr
self.codes.append(layer_code)
def add_str(self, str_code):
layer_code = LayerCode()
layer_code.str_code = str_code
self.codes.append(layer_code)
def clear(self):
self.codes = list()
def gen_codes(self):
res = list()
if len(self.codes) == 0:
return []
for code in self.codes:
if isinstance(code, LayerCode):
res.append(code.get_str_code())
else:
raise Exception("Unexcept situation!")
return res
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册