未验证 提交 36ca3876 编写于 作者: Q qingqing01 提交者: GitHub

Merge pull request #761 from walloollaw/add-examples-for-caffe2fluid

Add examples for caffe2fluid
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
This tool is used to convert a Caffe model to Fluid model This tool is used to convert a Caffe model to Fluid model
### Howto ### Howto
1, Prepare caffepb.py in ./proto, two options provided 1, Prepare caffepb.py in ./proto if your python has no 'pycaffe' module, two options provided here:
1) generate it from caffe.proto using protoc 1) generate it from caffe.proto using protoc
bash ./proto/compile.sh bash ./proto/compile.sh
...@@ -12,14 +13,24 @@ This tool is used to convert a Caffe model to Fluid model ...@@ -12,14 +13,24 @@ This tool is used to convert a Caffe model to Fluid model
2, Convert the caffe model using 'convert.py' which will generate a python script and a weight(in .npy) file 2, Convert the caffe model using 'convert.py' which will generate a python script and a weight(in .npy) file
3, Use the converted model to predict 3, Use the converted model to predict
see more detail info in 'tests/lenet/README.md'
see more detail info in 'examples/xxx'
### Supported models ### Tested models
- Lenet on mnist dataset - Lenet on mnist dataset
- ResNets:(ResNet-50, ResNet-101, ResNet-152) - ResNets:(ResNet-50, ResNet-101, ResNet-152)
model addrs:(https://onedrive.live.com/?authkey=%21AAFW2-FVoxeVRck&id=4006CBB8476FF777%2117887&cid=4006CBB8476FF777) model addr: `https://onedrive.live.com/?authkey=%21AAFW2-FVoxeVRck&id=4006CBB8476FF777%2117887&cid=4006CBB8476FF777`_
- GoogleNet:
model addr: `https://gist.github.com/jimmie33/7ea9f8ac0da259866b854460f4526034`_
- VGG:
model addr: `https://gist.github.com/ksimonyan/211839e770f7b538e2d8`_
- AlexNet:
model addr: `https://github.com/BVLC/caffe/tree/master/models/bvlc_alexnet`_
### Notes ### Notes
Some of this code come from here: https://github.com/ethereon/caffe-tensorflow Some of this code come from here: https://github.com/ethereon/caffe-tensorflow
...@@ -4,8 +4,8 @@ import os ...@@ -4,8 +4,8 @@ import os
import sys import sys
import numpy as np import numpy as np
import argparse import argparse
from kaffe import KaffeError, print_stderr
from kaffe import KaffeError, print_stderr
from kaffe.paddle import Transformer from kaffe.paddle import Transformer
...@@ -47,6 +47,8 @@ def convert(def_path, caffemodel_path, data_output_path, code_output_path, ...@@ -47,6 +47,8 @@ def convert(def_path, caffemodel_path, data_output_path, code_output_path,
except KaffeError as err: except KaffeError as err:
fatal_error('Error encountered: {}'.format(err)) fatal_error('Error encountered: {}'.format(err))
return 0
def main(): def main():
""" main """ main
...@@ -64,9 +66,10 @@ def main(): ...@@ -64,9 +66,10 @@ def main():
help='The phase to convert: test (default) or train') help='The phase to convert: test (default) or train')
args = parser.parse_args() args = parser.parse_args()
validate_arguments(args) validate_arguments(args)
convert(args.def_path, args.caffemodel, args.data_output_path, return convert(args.def_path, args.caffemodel, args.data_output_path,
args.code_output_path, args.phase) args.code_output_path, args.phase)
if __name__ == '__main__': if __name__ == '__main__':
main() ret = main()
sys.exit(ret)
a demo to show converting caffe models on 'imagenet' using caffe2fluid
---
# How to use
1. prepare python environment
2. download caffe model to "models.caffe/xxx" which contains "xxx.caffemodel" and "xxx.prototxt"
3. run the tool
eg: bash ./run.sh resnet50 ./models.caffe/resnet50 ./models/resnet50
#!/bin/env python
#function:
# a demo to show how to use the converted model genereated by caffe2fluid
#
#notes:
# only support imagenet data
import os
import sys
import inspect
import numpy as np
import paddle.v2 as paddle
import paddle.v2.fluid as fluid
def load_data(imgfile, shape):
h, w = shape[1:]
from PIL import Image
im = Image.open(imgfile)
# The storage order of the loaded image is W(widht),
# H(height), C(channel). PaddlePaddle requires
# the CHW order, so transpose them.
im = im.resize((w, h), Image.ANTIALIAS)
im = np.array(im).astype(np.float32)
im = im.transpose((2, 0, 1)) # CHW
im = im[(2, 1, 0), :, :] # BGR
# The mean to be subtracted from each image.
# By default, the per-channel ImageNet mean.
mean = np.array([104., 117., 124.], dtype=np.float32)
mean = mean.reshape([3, 1, 1])
im = im - mean
return im.reshape([1] + shape)
def build_model(net_file, net_name):
print('build model with net_file[%s] and net_name[%s]' %
(net_file, net_name))
net_path = os.path.dirname(net_file)
module_name = os.path.basename(net_file).rstrip('.py')
if net_path not in sys.path:
sys.path.insert(0, net_path)
try:
m = __import__(module_name, fromlist=[net_name])
MyNet = getattr(m, net_name)
except Exception as e:
print('failed to load module[%s]' % (module_name))
print(e)
return None
input_name = 'data'
input_shape = MyNet.input_shapes()[input_name]
images = fluid.layers.data(name='image', shape=input_shape, dtype='float32')
#label = fluid.layers.data(name='label', shape=[1], dtype='int64')
net = MyNet({input_name: images})
input_shape = MyNet.input_shapes()[input_name]
return net, input_shape
def dump_results(results, names, root):
if os.path.exists(root) is False:
os.path.mkdir(root)
for i in range(len(names)):
n = names[i]
res = results[i]
filename = os.path.join(root, n)
np.save(filename + '.npy', res)
def infer(net_file, net_name, model_file, imgfile, debug=False):
""" do inference using a model which consist 'xxx.py' and 'xxx.npy'
"""
#1, build model
net, input_shape = build_model(net_file, net_name)
prediction = net.get_output()
#2, load weights for this model
place = fluid.CPUPlace()
exe = fluid.Executor(place)
startup_program = fluid.default_startup_program()
exe.run(startup_program)
if model_file.find('.npy') > 0:
net.load(data_path=model_file, exe=exe, place=place)
else:
net.load(data_path=model_file, exe=exe)
#3, test this model
test_program = fluid.default_main_program().clone()
fetch_list_var = []
fetch_list_name = []
if debug is False:
fetch_list_var.append(prediction)
else:
for k, v in net.layers.items():
fetch_list_var.append(v)
fetch_list_name.append(k)
np_images = load_data(imgfile, input_shape)
results = exe.run(program=test_program,
feed={'image': np_images},
fetch_list=fetch_list_var)
if debug is True:
dump_path = 'results.layers'
dump_results(results, fetch_list_name, dump_path)
print('all results dumped to [%s]' % (dump_path))
else:
result = results[0]
print('predicted class:', np.argmax(result))
if __name__ == "__main__":
""" maybe more convenient to use 'run.sh' to call this tool
"""
net_file = 'models/resnet50/resnet50.py'
weight_file = 'models/resnet50/resnet50.npy'
imgfile = 'data/65.jpeg'
net_name = 'ResNet50'
argc = len(sys.argv)
if argc == 5:
net_file = sys.argv[1]
weight_file = sys.argv[2]
imgfile = sys.argv[3]
net_name = sys.argv[4]
elif argc > 1:
print('usage:')
print('\tpython %s [net_file] [weight_file] [imgfile] [net_name]' %
(sys.argv[0]))
print('\teg:python %s %s %s %s %s' % (sys.argv[0], net_file,
weight_file, imgfile, net_name))
sys.exit(1)
infer(net_file, net_name, weight_file, imgfile)
#!/bin/bash
#function:
# a tool used to:
# 1, convert a caffe model
# 2, do inference using this model
#
#usage:
# bash run.sh resnet50 ./models.caffe/resnet50 ./models/resnet50
#
#set -x
if [[ $# -lt 3 ]];then
echo "usage:"
echo " bash $0 [model_name] [cf_model_path] [pd_model_path] [only_convert]"
echo " eg: bash $0 resnet50 ./models.caffe/resnet50 ./models/resnet50"
exit 1
else
model_name=$1
cf_model_path=$2
pd_model_path=$3
only_convert=$4
fi
proto_file=$cf_model_path/${model_name}.prototxt
caffemodel_file=$cf_model_path/${model_name}.caffemodel
weight_file=$pd_model_path/${model_name}.npy
net_file=$pd_model_path/${model_name}.py
if [[ ! -e $proto_file ]];then
echo "not found prototxt[$proto_file]"
exit 1
fi
if [[ ! -e $caffemodel_file ]];then
echo "not found caffemodel[$caffemodel_file]"
exit 1
fi
if [[ ! -e $pd_model_path ]];then
mkdir $pd_model_path
fi
PYTHON=`which cfpython`
if [[ -z $PYTHON ]];then
PYTHON=`which python`
fi
$PYTHON ../../convert.py \
$proto_file \
--caffemodel $caffemodel_file \
--data-output-path $weight_file\
--code-output-path $net_file
ret=$?
if [[ $ret -ne 0 ]];then
echo "failed to convert caffe model[$cf_model_path]"
exit $ret
else
echo "succeed to convert caffe model[$cf_model_path] to fluid model[$pd_model_path]"
fi
if [[ -z $only_convert ]];then
PYTHON=`which pdpython`
if [[ -z $PYTHON ]];then
PYTHON=`which python`
fi
imgfile="data/65.jpeg"
net_name=`grep "name" $proto_file | head -n1 | perl -ne 'if(/\"([^\"]+)\"/){ print $1."\n";}'`
$PYTHON ./infer.py $net_file $weight_file $imgfile $net_name
ret=$?
fi
exit $ret
a demo to show converting caffe model on 'mnist' using caffe2fluid
---
# How to use
1. prepare python environment
2. download caffe model to "models.caffe/lenet" which contains "lenet.caffemodel" and "lenet.prototxt"
3. run the tool
eg: bash ./run.sh lenet ./models.caffe/lenet ./models/lenet
...@@ -4,12 +4,12 @@ ...@@ -4,12 +4,12 @@
# demo to show how to use converted model using caffe2fluid # demo to show how to use converted model using caffe2fluid
# #
import sys
import os
import numpy as np import numpy as np
import paddle.v2 as paddle import paddle.v2 as paddle
import paddle.v2.fluid as fluid import paddle.v2.fluid as fluid
from lenet import LeNet as MyNet
def test_model(exe, test_program, fetch_list, test_reader, feeder): def test_model(exe, test_program, fetch_list, test_reader, feeder):
acc_set = [] acc_set = []
...@@ -24,10 +24,15 @@ def test_model(exe, test_program, fetch_list, test_reader, feeder): ...@@ -24,10 +24,15 @@ def test_model(exe, test_program, fetch_list, test_reader, feeder):
return float(acc_val) return float(acc_val)
def main(model_path): def evaluate(net_file, model_file):
""" main """ main
""" """
print('load fluid model in %s' % (model_path)) #1, build model
net_path = os.path.dirname(net_file)
if net_path not in sys.path:
sys.path.insert(0, net_path)
from lenet import LeNet as MyNet
with_gpu = False with_gpu = False
paddle.init(use_gpu=with_gpu) paddle.init(use_gpu=with_gpu)
...@@ -45,10 +50,10 @@ def main(model_path): ...@@ -45,10 +50,10 @@ def main(model_path):
exe.run(fluid.default_startup_program()) exe.run(fluid.default_startup_program())
#2, load weights #2, load weights
if model_path.find('.npy') > 0: if model_file.find('.npy') > 0:
net.load(data_path=model_path, exe=exe, place=place) net.load(data_path=model_file, exe=exe, place=place)
else: else:
net.load(data_path=model_path, exe=exe) net.load(data_path=model_file, exe=exe)
#3, test this model #3, test this model
test_program = fluid.default_main_program().clone() test_program = fluid.default_main_program().clone()
...@@ -65,10 +70,17 @@ def main(model_path): ...@@ -65,10 +70,17 @@ def main(model_path):
if __name__ == "__main__": if __name__ == "__main__":
import sys net_file = 'models/lenet/lenet.py'
if len(sys.argv) == 2: weight_file = 'models/lenet/lenet.npy'
fluid_model_path = sys.argv[1]
else: argc = len(sys.argv)
fluid_model_path = './model.fluid' if argc == 3:
net_file = sys.argv[1]
main(fluid_model_path) weight_file = sys.argv[2]
elif argc > 1:
print('usage:')
print('\tpython %s [net_file] [weight_file]' % (sys.argv[0]))
print('\teg:python %s %s %s %s' % (sys.argv[0], net_file, weight_file))
sys.exit(1)
evaluate(net_file, weight_file)
#!/bin/bash
#function:
# a tool used to:
# 1, convert a caffe model
# 2, do inference using this model
#
#usage:
# bash run.sh lenet ./models.caffe/lenet ./models/lenet
#
#set -x
if [[ $# -lt 3 ]];then
echo "usage:"
echo " bash $0 [model_name] [cf_model_path] [pd_model_path] [only_convert]"
echo " eg: bash $0 lenet ./models.caffe/lenet ./models/lenet"
exit 1
else
model_name=$1
cf_model_path=$2
pd_model_path=$3
no_eval=$4
fi
proto_file=$cf_model_path/${model_name}.prototxt
caffemodel_file=$cf_model_path/${model_name}.caffemodel
weight_file=$pd_model_path/${model_name}.npy
net_file=$pd_model_path/${model_name}.py
if [[ ! -e $proto_file ]];then
echo "not found prototxt[$proto_file]"
exit 1
fi
if [[ ! -e $caffemodel_file ]];then
echo "not found caffemodel[$caffemodel_file]"
exit 1
fi
if [[ ! -e $pd_model_path ]];then
mkdir $pd_model_path
fi
PYTHON=`which cfpython`
if [[ -z $PYTHON ]];then
PYTHON=`which python`
fi
$PYTHON ../../convert.py \
$proto_file \
--caffemodel $caffemodel_file \
--data-output-path $weight_file\
--code-output-path $net_file
ret=$?
if [[ $ret -ne 0 ]];then
echo "failed to convert caffe model[$cf_model_path]"
exit $ret
else
echo "succeed to convert caffe model[$cf_model_path] to fluid model[$pd_model_path]"
fi
if [[ -z $only_convert ]];then
PYTHON=`which pdpython`
if [[ -z $PYTHON ]];then
PYTHON=`which python`
fi
net_name=`grep "name" $proto_file | head -n1 | perl -ne 'if(/\"([^\"]+)\"/){ print $1."\n";}'`
if [[ $net_name != "LeNet" ]];then
echo "only support LeNet"
exit 1
fi
$PYTHON ./evaluate.py $net_file $weight_file
ret=$?
fi
exit $ret
...@@ -54,7 +54,6 @@ def show_fallback_warning(): ...@@ -54,7 +54,6 @@ def show_fallback_warning():
WARNING: PyCaffe not found! WARNING: PyCaffe not found!
Falling back to a pure protocol buffer implementation. Falling back to a pure protocol buffer implementation.
* Conversions will be drastically slower. * Conversions will be drastically slower.
* This backend is UNTESTED!
------------------------------------------------------------ ------------------------------------------------------------
''' '''
......
...@@ -175,6 +175,7 @@ class GraphBuilder(object): ...@@ -175,6 +175,7 @@ class GraphBuilder(object):
kind = NodeKind.map_raw_kind(layer.type) kind = NodeKind.map_raw_kind(layer.type)
if kind is None: if kind is None:
raise KaffeError('Unknown layer type encountered: %s' % layer.type) raise KaffeError('Unknown layer type encountered: %s' % layer.type)
# We want to use the layer's top names (the "output" names), rather than the # We want to use the layer's top names (the "output" names), rather than the
# name attribute, which is more of readability thing than a functional one. # name attribute, which is more of readability thing than a functional one.
# Other layers will refer to a node by its "top name". # Other layers will refer to a node by its "top name".
...@@ -235,6 +236,7 @@ class GraphBuilder(object): ...@@ -235,6 +236,7 @@ class GraphBuilder(object):
node.add_parent(parent_node) node.add_parent(parent_node)
if len(layer.top) > 1: if len(layer.top) > 1:
raise KaffeError('Multiple top nodes are not supported.') raise KaffeError('Multiple top nodes are not supported.')
for output_name in layer.top: for output_name in layer.top:
if output_name == layer.name: if output_name == layer.name:
# Output is named the same as the node. No further action required. # Output is named the same as the node. No further action required.
......
...@@ -51,16 +51,78 @@ LAYER_DESCRIPTORS = { ...@@ -51,16 +51,78 @@ LAYER_DESCRIPTORS = {
'Threshold': shape_identity, 'Threshold': shape_identity,
} }
LAYER_TYPES = LAYER_DESCRIPTORS.keys() # layer types in 'V1LayerParameter'
# (v1layertype name, enum value, mapped to layer type)
v1_layertypes = [
('ABSVAL', 35),
('ACCURACY', 1),
('ARGMAX', 30),
('BNLL', 2),
('CONCAT', 3),
('CONVOLUTION', 4),
('DATA', 5),
('DECONVOLUTION', 39),
('DROPOUT', 6),
('ELTWISE', 25),
('EXP', 38),
('FLATTEN', 8),
('IM2COL', 11),
('INNERPRODUCT', 14),
('LRN', 15),
('MEMORYDATA', 29),
('MULTINOMIALLOGISTICLOSS', 16),
('MVN', 34),
('POOLING', 17),
('POWER', 26),
('RELU', 18),
('SIGMOID', 19),
('SIGMOIDCROSSENTROPYLOSS', 27),
('SILENCE', 36),
('SOFTMAX', 20),
('SPLIT', 22),
('SLICE', 33),
('TANH', 23),
('WINDOWDATA', 24),
('THRESHOLD', 31),
]
LAYER_TYPES = LAYER_DESCRIPTORS.keys()
LayerType = type('LayerType', (), {t: t for t in LAYER_TYPES}) LayerType = type('LayerType', (), {t: t for t in LAYER_TYPES})
#map the layer name in V1 to standard name
V1_LAYER_MAP = {'_not_init_': True}
def get_v1_layer_map():
global V1_LAYER_MAP
if '_not_init_' not in V1_LAYER_MAP:
return V1_LAYER_MAP
else:
del V1_LAYER_MAP['_not_init_']
name2layer = {}
for n in LAYER_TYPES:
name2layer[n.upper()] = n
for l in v1_layertypes:
n, v = l
if n in name2layer and v not in V1_LAYER_MAP:
V1_LAYER_MAP[v] = name2layer[n]
else:
raise KaffeError('not found v1 layer type %s' % n)
return V1_LAYER_MAP
class NodeKind(LayerType): class NodeKind(LayerType):
@staticmethod @staticmethod
def map_raw_kind(kind): def map_raw_kind(kind):
if kind in LAYER_TYPES: if kind in LAYER_TYPES:
return kind return kind
v1_layers = get_v1_layer_map()
if kind in v1_layers:
return v1_layers[kind]
else:
return None return None
@staticmethod @staticmethod
......
...@@ -27,6 +27,9 @@ def layer(op): ...@@ -27,6 +27,9 @@ def layer(op):
self.layers[name] = layer_output self.layers[name] = layer_output
# This output is now the input for the next layer. # This output is now the input for the next layer.
self.feed(layer_output) self.feed(layer_output)
#print('output shape of %s:' % (name))
#print layer_output.shape
# Return self for chained calls. # Return self for chained calls.
return self return self
...@@ -158,41 +161,64 @@ class Network(object): ...@@ -158,41 +161,64 @@ class Network(object):
output = fluid.layers.relu(x=input) output = fluid.layers.relu(x=input)
return output return output
@layer def _adjust_pad_if_needed(self, i_hw, k_hw, s_hw, p_hw):
def max_pool(self, input, k_h, k_w, s_h, s_w, name, padding=None): #adjust the padding if needed
if padding is None: i_h, i_w = i_hw
padding = [0, 0] k_h, k_w = k_hw
s_h, s_w = s_hw
p_h, p_w = p_hw
def is_consistent(i, k, s, p):
o = i + 2 * p - k
if o % s == 0:
return True
else:
return False
real_p_h = 0
real_p_w = 0
if is_consistent(i_h, k_h, s_h, p_h) is False:
real_p_h = int(k_h / 2)
if is_consistent(i_w, k_w, s_w, p_w) is False:
real_p_w = int(k_w / 2)
return [real_p_h, real_p_w]
def pool(self, pool_type, input, k_h, k_w, s_h, s_w, name, padding):
# Get the number of channels in the input # Get the number of channels in the input
h_i, w_i = input.shape[2:] in_hw = input.shape[2:]
fluid = import_fluid() k_hw = [k_h, k_w]
output = fluid.layers.pool2d( s_hw = [s_h, s_w]
input=input,
pool_size=[k_h, k_w],
pool_stride=[s_h, s_w],
pool_padding=padding,
pool_type='max')
return output
@layer
def avg_pool(self, input, k_h, k_w, s_h, s_w, name, padding=None):
if padding is None: if padding is None:
padding = [0, 0] #fix bug about the difference between conv and pool
#more info: https://github.com/BVLC/caffe/issues/1318
padding = self._adjust_pad_if_needed(in_hw, k_hw, s_hw, [0, 0])
# Get the number of channels in the input
h_i, w_i = input.shape[2:]
fluid = import_fluid() fluid = import_fluid()
output = fluid.layers.pool2d( output = fluid.layers.pool2d(
input=input, input=input,
pool_size=[k_h, k_w], pool_size=k_hw,
pool_stride=[s_h, s_w], pool_stride=s_hw,
pool_padding=padding, pool_padding=padding,
pool_type='avg') pool_type=pool_type)
return output return output
@layer
def max_pool(self, input, k_h, k_w, s_h, s_w, name, padding=None):
return self.pool('max', input, k_h, k_w, s_h, s_w, name, padding)
@layer
def avg_pool(self, input, k_h, k_w, s_h, s_w, name, padding=None):
return self.pool('avg', input, k_h, k_w, s_h, s_w, name, padding)
@layer @layer
def lrn(self, input, radius, alpha, beta, name, bias=1.0): def lrn(self, input, radius, alpha, beta, name, bias=1.0):
raise Exception('lrn() not implemented yet') fluid = import_fluid()
output = fluid.layers.lrn(input=input, \
n=radius, k=bias, alpha=alpha, beta=beta, name=name)
return output
@layer @layer
def concat(self, inputs, axis, name): def concat(self, inputs, axis, name):
...@@ -228,7 +254,7 @@ class Network(object): ...@@ -228,7 +254,7 @@ class Network(object):
@layer @layer
def softmax(self, input, name): def softmax(self, input, name):
fluid = import_fluid() fluid = import_fluid()
output = fluid.layers.softmax(x=input, name=name) output = fluid.layers.softmax(input)
return output return output
@layer @layer
...@@ -256,5 +282,8 @@ class Network(object): ...@@ -256,5 +282,8 @@ class Network(object):
return output return output
@layer @layer
def dropout(self, input, keep_prob, name): def dropout(self, input, drop_prob, name, is_test=True):
raise Exception('dropout() not implemented yet') fluid = import_fluid()
output = fluid.layers.dropout(
input, dropout_prob=drop_prob, is_test=is_test, name=name)
return output
...@@ -132,8 +132,7 @@ class TensorFlowMapper(NodeMapper): ...@@ -132,8 +132,7 @@ class TensorFlowMapper(NodeMapper):
# just scales by alpha (as does Krizhevsky's paper). # just scales by alpha (as does Krizhevsky's paper).
# We'll account for that here. # We'll account for that here.
alpha = params.alpha / float(params.local_size) alpha = params.alpha / float(params.local_size)
return TensorFlowNode('lrn', return TensorFlowNode('lrn', params.local_size, alpha, params.beta)
int(params.local_size / 2), alpha, params.beta)
def map_concat(self, node): def map_concat(self, node):
return TensorFlowNode('concat', node.parameters.axis) return TensorFlowNode('concat', node.parameters.axis)
...@@ -191,22 +190,33 @@ class TensorFlowEmitter(object): ...@@ -191,22 +190,33 @@ class TensorFlowEmitter(object):
def emit_setup_def(self): def emit_setup_def(self):
return self.statement('def setup(self):') return self.statement('def setup(self):')
def emit_convert_def(self, input_nodes): def emit_shape_def(self, input_nodes):
def data_layer_def(name, shape, dtype=None): self.outdent()
if dtype is None: func_def = self.statement('@classmethod')
dtype = 'float32' func_def += self.statement('def input_shapes(cls):')
self.indent()
layer_var = name + '_layer' input_shapes = {}
shape = [str(s) for s in shape[1:]] for n in input_nodes:
layer_def = '%s = fluid.layers.data(name="%s", shape=[%s], dtype="%s")'\ name = n.name
% (layer_var, name, ','.join(shape), dtype) output_shape = n.output_shape
return layer_var, layer_def shape = [str(s) for s in output_shape[1:]]
input_shapes[name] = ', '.join(shape)
input_shapes = ['"%s": [%s]' % (n, l) for n, l in input_shapes.items()]
shape_str = ','.join(input_shapes)
func_def += self.statement('return {%s}' % (shape_str))
return '\n\n' + func_def
def emit_convert_def(self, input_nodes):
codes = [] codes = []
inputs = {} inputs = {}
codes.append('shapes = cls.input_shapes()')
for n in input_nodes: for n in input_nodes:
name = n.name name = n.name
layer_var, layer_def = data_layer_def(n.name, n.output_shape) layer_var = name + '_layer'
layer_def = '%s = fluid.layers.data(name="%s", shape=shapes["%s"],'\
' dtype="float32")' % (layer_var, name, name)
#layer_var, layer_def = data_layer_def(n.name, n.output_shape)
codes.append(layer_def) codes.append(layer_def)
inputs[name] = layer_var inputs[name] = layer_var
...@@ -229,7 +239,7 @@ class TensorFlowEmitter(object): ...@@ -229,7 +239,7 @@ class TensorFlowEmitter(object):
func_def += self.statement('import paddle.v2.fluid as fluid') func_def += self.statement('import paddle.v2.fluid as fluid')
for l in codes: for l in codes:
func_def += self.statement(l) func_def += self.statement(l)
return '\n\n' + func_def return '\n' + func_def
def emit_main_def(self, name): def emit_main_def(self, name):
if name is None: if name is None:
...@@ -273,6 +283,7 @@ class TensorFlowEmitter(object): ...@@ -273,6 +283,7 @@ class TensorFlowEmitter(object):
b += self.emit_node(node) b += self.emit_node(node)
blocks.append(b[:-1]) blocks.append(b[:-1])
s = s + '\n\n'.join(blocks) s = s + '\n\n'.join(blocks)
s += self.emit_shape_def(input_nodes)
s += self.emit_convert_def(input_nodes) s += self.emit_convert_def(input_nodes)
s += self.emit_main_def(name) s += self.emit_main_def(name)
return s return s
......
### Convert lenet model from caffe format into paddle format(fluid api)
### Howto
1, Prepare your caffepb.py
2, Download a lenet caffe-model
lenet_iter_10000.caffemodel
download address: https://github.com/ethereon/caffe-tensorflow/raw/master/examples/mnist/lenet_iter_10000.caffemodel
md5: cbec75c1c374b6c1981c4a1eb024ae01
lenet.prototxt
download address: https://raw.githubusercontent.com/BVLC/caffe/master/examples/mnist/lenet.prototxt
md5: 27384af843338ab90b00c8d1c81de7d5
2, Convert this model(make sure caffepb.py is ready in ../../proto)
convert to npy format
bash ./convert.sh lenet.prototxt lenet.caffemodel lenet.py lenet.npy
save to fluid format(optional)
bash ./convert.sh lenet.prototxt lenet.caffemodel lenet.py lenet.npy && python ./lenet.py ./lenet.npy ./fluid.model
4, Use this new model(paddle installed in this python)
use fluid format
python ./predict.py ./fluid.model
use npy format
python ./predict.py ./lenet.npy
#!/bin/bash
#function:
# convert a caffe model
# eg:
# bash ./convert.sh ./model.caffe/lenet.prototxt ./model.caffe/lenet.caffemodel lenet.py lenet.npy
if [[ $# -ne 4 ]];then
echo "usage:"
echo " bash $0 [PROTOTXT] [CAFFEMODEL] [PY_NAME] [WEIGHT_NAME]"
echo " eg: bash $0 lenet.prototxt lenet.caffemodel lenet.py lenet.npy"
exit 1
fi
WORK_ROOT=$(dirname `readlink -f ${BASH_SOURCE[0]}`)
if [[ -z $PYTHON ]];then
PYTHON=`which python`
fi
PROTOTXT=$1
CAFFEMODEL=$2
PY_NAME=$3
WEIGHT_NAME=$4
CONVERTER_PY="$WORK_ROOT/../../convert.py"
$PYTHON $CONVERTER_PY $PROTOTXT --caffemodel $CAFFEMODEL --code-output-path=$PY_NAME --data-output-path=$WEIGHT_NAME
ret=$?
if [[ $ret -eq 0 ]];then
echo "succeed to convert caffe model[$CAFFEMODEL, $PROTOTXT] to paddle model[$PY_NAME, $WEIGHT_NAME]"
else
echo "failed to convert caffe model[$CAFFEMODEL, $PROTOTXT]"
fi
exit $ret
### generated by caffe2fluid, your net is in class "LeNet" ###
import math
import os
import numpy as np
def import_fluid():
import paddle.v2.fluid as fluid
return fluid
def layer(op):
'''Decorator for composable network layers.'''
def layer_decorated(self, *args, **kwargs):
# Automatically set a name if not provided.
name = kwargs.setdefault('name', self.get_unique_name(op.__name__))
# Figure out the layer inputs.
if len(self.terminals) == 0:
raise RuntimeError('No input variables found for layer %s.' % name)
elif len(self.terminals) == 1:
layer_input = self.terminals[0]
else:
layer_input = list(self.terminals)
# Perform the operation and get the output.
layer_output = op(self, layer_input, *args, **kwargs)
# Add to layer LUT.
self.layers[name] = layer_output
# This output is now the input for the next layer.
self.feed(layer_output)
# Return self for chained calls.
return self
return layer_decorated
class Network(object):
def __init__(self, inputs, trainable=True):
# The input nodes for this network
self.inputs = inputs
# The current list of terminal nodes
self.terminals = []
# Mapping from layer names to layers
self.layers = dict(inputs)
# If true, the resulting variables are set as trainable
self.trainable = trainable
# Switch variable for dropout
self.paddle_env = None
self.setup()
def setup(self):
'''Construct the network. '''
raise NotImplementedError('Must be implemented by the subclass.')
def load(self, data_path, exe=None, place=None, ignore_missing=False):
'''Load network weights.
data_path: The path to the numpy-serialized network weights
ignore_missing: If true, serialized weights for missing layers are ignored.
'''
fluid = import_fluid()
#load fluid mode directly
if os.path.isdir(data_path):
assert (exe is not None), \
'must provide a executor to load fluid model'
fluid.io.load_persistables_if_exist(executor=exe, dirname=data_path)
return True
#load model from a npy file
if exe is None or place is None:
if self.paddle_env is None:
place = fluid.CPUPlace()
exe = fluid.Executor(place)
self.paddle_env = {'place': place, 'exe': exe}
exe = exe.run(fluid.default_startup_program())
else:
place = self.paddle_env['place']
exe = self.paddle_env['exe']
data_dict = np.load(data_path).item()
for op_name in data_dict:
layer = self.layers[op_name]
for param_name, data in data_dict[op_name].iteritems():
try:
name = '%s_%s' % (op_name, param_name)
v = fluid.global_scope().find_var(name)
w = v.get_tensor()
w.set(data, place)
except ValueError:
if not ignore_missing:
raise
return True
def feed(self, *args):
'''Set the input(s) for the next operation by replacing the terminal nodes.
The arguments can be either layer names or the actual layers.
'''
assert len(args) != 0
self.terminals = []
for fed_layer in args:
if isinstance(fed_layer, basestring):
try:
fed_layer = self.layers[fed_layer]
except KeyError:
raise KeyError('Unknown layer name fed: %s' % fed_layer)
self.terminals.append(fed_layer)
return self
def get_output(self):
'''Returns the current network output.'''
return self.terminals[-1]
def get_unique_name(self, prefix):
'''Returns an index-suffixed unique name for the given prefix.
This is used for auto-generating layer names based on the type-prefix.
'''
ident = sum(t.startswith(prefix) for t, _ in self.layers.items()) + 1
return '%s_%d' % (prefix, ident)
@layer
def conv(self,
input,
k_h,
k_w,
c_o,
s_h,
s_w,
name,
relu=True,
padding=None,
group=1,
biased=True):
if padding is None:
padding = [0, 0]
# Get the number of channels in the input
c_i, h_i, w_i = input.shape[1:]
# Verify that the grouping parameter is valid
assert c_i % group == 0
assert c_o % group == 0
fluid = import_fluid()
prefix = name + '_'
output = fluid.layers.conv2d(
input=input,
filter_size=[k_h, k_w],
num_filters=c_o,
stride=[s_h, s_w],
padding=padding,
groups=group,
param_attr=fluid.ParamAttr(name=prefix + "weights"),
bias_attr=fluid.ParamAttr(name=prefix + "biases"),
act="relu" if relu is True else None)
return output
@layer
def relu(self, input, name):
fluid = import_fluid()
output = fluid.layers.relu(x=input)
return output
@layer
def max_pool(self, input, k_h, k_w, s_h, s_w, name, padding=None):
if padding is None:
padding = [0, 0]
# Get the number of channels in the input
h_i, w_i = input.shape[2:]
fluid = import_fluid()
output = fluid.layers.pool2d(
input=input,
pool_size=[k_h, k_w],
pool_stride=[s_h, s_w],
pool_padding=padding,
pool_type='max')
return output
@layer
def avg_pool(self, input, k_h, k_w, s_h, s_w, name, padding=None):
if padding is None:
padding = [0, 0]
# Get the number of channels in the input
h_i, w_i = input.shape[2:]
fluid = import_fluid()
output = fluid.layers.pool2d(
input=input,
pool_size=[k_h, k_w],
pool_stride=[s_h, s_w],
pool_padding=padding,
pool_type='avg')
return output
@layer
def lrn(self, input, radius, alpha, beta, name, bias=1.0):
raise Exception('lrn() not implemented yet')
@layer
def concat(self, inputs, axis, name):
fluid = import_fluid()
output = fluid.layers.concat(input=inputs, axis=axis)
return output
@layer
def add(self, inputs, name):
fluid = import_fluid()
output = inputs[0]
for i in inputs[1:]:
output = fluid.layers.elementwise_add(x=output, y=i)
return output
@layer
def fc(self, input, num_out, name, relu=True, act=None):
fluid = import_fluid()
if act is None:
act = 'relu' if relu is True else None
prefix = name + '_'
output = fluid.layers.fc(
name=name,
input=input,
size=num_out,
act=act,
param_attr=fluid.ParamAttr(name=prefix + 'weights'),
bias_attr=fluid.ParamAttr(name=prefix + 'biases'))
return output
@layer
def softmax(self, input, name):
fluid = import_fluid()
output = fluid.layers.softmax(x=input, name=name)
return output
@layer
def batch_normalization(self, input, name, scale_offset=True, relu=False):
# NOTE: Currently, only inference is supported
fluid = import_fluid()
prefix = name + '_'
param_attr = None if scale_offset is False else fluid.ParamAttr(
name=prefix + 'scale')
bias_attr = None if scale_offset is False else fluid.ParamAttr(
name=prefix + 'offset')
mean_name = prefix + 'mean'
variance_name = prefix + 'variance'
output = fluid.layers.batch_norm(
name=name,
input=input,
is_test=True,
param_attr=param_attr,
bias_attr=bias_attr,
moving_mean_name=mean_name,
moving_variance_name=variance_name,
epsilon=1e-5,
act='relu' if relu is True else None)
return output
@layer
def dropout(self, input, keep_prob, name):
raise Exception('dropout() not implemented yet')
class LeNet(Network):
def setup(self):
self.feed('data')
self.conv(5, 5, 20, 1, 1, relu=False, name='conv1')
self.max_pool(2, 2, 2, 2, name='pool1')
self.conv(5, 5, 50, 1, 1, relu=False, name='conv2')
self.max_pool(2, 2, 2, 2, name='pool2')
self.fc(500, name='ip1')
self.fc(10, relu=False, name='ip2')
self.softmax(name='prob')
@classmethod
def convert(cls, npy_model, fluid_path):
import paddle.v2.fluid as fluid
data_layer = fluid.layers.data(
name="data", shape=[1, 28, 28], dtype="float32")
feed_data = {"data": data_layer}
net = cls(feed_data)
place = fluid.CPUPlace()
exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())
net.load(data_path=npy_model, exe=exe, place=place)
fluid.io.save_persistables(executor=exe, dirname=fluid_path)
if __name__ == "__main__":
#usage: python xxxnet.py xxx.npy ./model
import sys
npy_weight = sys.argv[1]
fluid_model = sys.argv[2]
LeNet.convert(npy_weight, fluid_model)
exit(0)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册