Repository: incubator-singa Updated Branches: refs/heads/master 63c6ae17c -> a39ed5ce5
SINGA-326 - Add Inception V4 for ImageNet classification adding inceptionv4 following https://github.com/tensorflow/models/blob/master/slim/nets/inception_v4.py convert params from tensorflow to pickle dictionary updated the padding configuration following Tensorflow for 'SAME' and 'VALID' Project: http://git-wip-us.apache.org/repos/asf/incubator-singa/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-singa/commit/2cdc1725 Tree: http://git-wip-us.apache.org/repos/asf/incubator-singa/tree/2cdc1725 Diff: http://git-wip-us.apache.org/repos/asf/incubator-singa/diff/2cdc1725 Branch: refs/heads/master Commit: 2cdc1725d7e8b1ec075994e46076f7a7a87e5795 Parents: 334c27d Author: wangwei <[email protected]> Authored: Tue Jun 27 17:28:37 2017 +0800 Committer: wangwei <[email protected]> Committed: Thu Jun 29 15:27:34 2017 +0800 ---------------------------------------------------------------------- examples/imagenet/googlenet/serve.py | 4 +- examples/imagenet/inceptionv4/README.md | 43 +++++ examples/imagenet/inceptionv4/convert.py | 117 ++++++++++++ examples/imagenet/inceptionv4/model.py | 263 ++++++++++++++++++++++++++ examples/imagenet/inceptionv4/serve.py | 121 ++++++++++++ python/singa/image_tool.py | 8 +- python/singa/layer.py | 71 ++++++- 7 files changed, 611 insertions(+), 16 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/2cdc1725/examples/imagenet/googlenet/serve.py ---------------------------------------------------------------------- diff --git a/examples/imagenet/googlenet/serve.py b/examples/imagenet/googlenet/serve.py index 57e005d..aee890d 100644 --- a/examples/imagenet/googlenet/serve.py +++ b/examples/imagenet/googlenet/serve.py @@ -139,7 +139,7 @@ def create_net(shape, weight_path='bvlc_googlenet.pickle'): # prob=net.add(Softmax('softmax')) net.load(weight_path, use_pickle=True) - print 'total num of params %d' % (len(net.param_names())) + print('total num of params %d' % (len(net.param_names()))) # SINGA and Caffe have different layout for the weight matrix of the dense # layer for key, val in zip(net.param_names(), net.param_values()): @@ -153,7 +153,7 @@ def create_net(shape, weight_path='bvlc_googlenet.pickle'): def serve(agent, use_cpu, parameter_file, topk=5): if use_cpu: - print 'running with cpu' + print('running with cpu') dev = device.get_default_device() layer.engine = 'singacpp' else: http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/2cdc1725/examples/imagenet/inceptionv4/README.md ---------------------------------------------------------------------- diff --git a/examples/imagenet/inceptionv4/README.md b/examples/imagenet/inceptionv4/README.md new file mode 100644 index 0000000..f129edc --- /dev/null +++ b/examples/imagenet/inceptionv4/README.md @@ -0,0 +1,43 @@ +--- +name: Inception V4 on ImageNet +SINGA version: 1.1.1 +SINGA commit: +parameter_url: https://s3-ap-southeast-1.amazonaws.com/dlfile/inception_v4.tar.gz +parameter_sha1: 5fdd6f5d8af8fd10e7321d9b38bb87ef14e80d56 +license: https://github.com/tensorflow/models/tree/master/slim +--- + +# Image Classification using Inception V4 + +In this example, we convert Inception V4 trained on Tensorflow to SINGA for image classification. + +## Instructions + +* Download the parameter checkpoint file + + $ wget + $ tar xvf inception_v4.tar.gz + +* Download [synset_word.txt](https://github.com/BVLC/caffe/blob/master/data/ilsvrc12/get_ilsvrc_aux.sh) file. + +* Run the program + + # use cpu + $ python serve.py -C & + # use gpu + $ python serve.py & + +* Submit images for classification + + $ curl -i -F [email protected] http://localhost:9999/api + $ curl -i -F [email protected] http://localhost:9999/api + $ curl -i -F [email protected] http://localhost:9999/api + +image1.jpg, image2.jpg and image3.jpg should be downloaded before executing the above commands. + +## Details + +We first extract the parameter values from [Tensorflow's checkpoint file](http://download.tensorflow.org/models/inception_v4_2016_09_09.tar.gz) into a pickle version. +After downloading and decompressing the checkpoint file, run the following script + + $ python convert.py --file_name=inception_v4.ckpt http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/2cdc1725/examples/imagenet/inceptionv4/convert.py ---------------------------------------------------------------------- diff --git a/examples/imagenet/inceptionv4/convert.py b/examples/imagenet/inceptionv4/convert.py new file mode 100644 index 0000000..e3f5adc --- /dev/null +++ b/examples/imagenet/inceptionv4/convert.py @@ -0,0 +1,117 @@ +# Copyright 2016 The TensorFlow 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. +# ============================================================================== +"""A simple script for inspect checkpoint files.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import argparse +import sys +import cPickle as pickle +import os + +import numpy as np +from tensorflow.python import pywrap_tensorflow +from tensorflow.python.platform import app +import model + + +FLAGS = None + + +def rename(name, suffix): + p = name.rfind('/') + if p == -1: + print('Bad name=%s' % name) + return name[0:p+1] + suffix + + +def convert(file_name): + net, _ = model.create_net() + params = {'SINGA_VERSION': 1101} + try: + reader = pywrap_tensorflow.NewCheckpointReader(file_name) + for pname, pval in zip(net.param_names(), net.param_values()): + if 'weight' in pname: + val = reader.get_tensor(rename(pname, 'weights')) + if 'Conv' in pname: + val = val.transpose((3, 2, 0, 1)) + val = val.reshape((val.shape[0], -1)) + elif 'bias' in pname: + val = reader.get_tensor(rename(pname, 'biases')) + elif 'mean' in pname: + val = reader.get_tensor(rename(pname, 'moving_mean')) + elif 'var' in pname: + val = reader.get_tensor(rename(pname, 'moving_variance')) + elif 'beta' in pname: + val= reader.get_tensor(pname) + elif 'gamma' in pname: + val = np.ones(pval.shape) + else: + print('not matched param %s' % pname) + assert val.shape == pval.shape, ('the shapes not match ', val.shape, pval.shape) + params[pname] = val.astype(np.float32) + print('converting:', pname, pval.shape) + var_to_shape_map = reader.get_variable_to_shape_map() + for key in var_to_shape_map: + if 'weights' in key: + key = rename(key, 'weight') + elif 'biases' in key: + key = rename(key, 'bias') + elif 'moving_mean' in key: + key = rename(key, 'mean') + elif 'moving_variance' in key: + key = rename(key, 'var') + if key not in params: + print('key=%s not in the net' % key) + ''' + for key in var_to_shape_map: + print("tensor_name: ", key, var_to_shape_map[key]) + ''' + with open(os.path.splitext(file_name)[0] + '.pickle', 'wb') as fd: + pickle.dump(params, fd) + except Exception as e: # pylint: disable=broad-except + print(str(e)) + if "corrupted compressed block contents" in str(e): + print("It's likely that your checkpoint file has been compressed " + "with SNAPPY.") + if ("Data loss" in str(e) and + (any([e in file_name for e in [".index", ".meta", ".data"]]))): + proposed_file = ".".join(file_name.split(".")[0:-1]) + v2_file_error_template = """ + It's likely that this is a V2 checkpoint and you need to provide the filename + *prefix*. Try removing the '.' and extension. Try: + inspect checkpoint --file_name = {}""" + print(v2_file_error_template.format(proposed_file)) + + + +def main(unused_argv): + if not FLAGS.file_name: + print("Usage: convert.py --file_name=checkpoint_file_name ") + sys.exit(1) + else: + convert(FLAGS.file_name) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.register("type", "bool", lambda v: v.lower() == "true") + parser.add_argument( + "--file_name", type=str, default="", help="Checkpoint filename. " + "Note, if using Checkpoint V2 format, file_name is the " + "shared prefix between all files in the checkpoint.") + FLAGS, unparsed = parser.parse_known_args() + app.run(main=main, argv=[sys.argv[0]] + unparsed) http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/2cdc1725/examples/imagenet/inceptionv4/model.py ---------------------------------------------------------------------- diff --git a/examples/imagenet/inceptionv4/model.py b/examples/imagenet/inceptionv4/model.py new file mode 100644 index 0000000..baab522 --- /dev/null +++ b/examples/imagenet/inceptionv4/model.py @@ -0,0 +1,263 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# ============================================================================= + + +""" +http://arxiv.org/abs/1602.07261. + + Inception-v4, Inception-ResNet and the Impact of Residual Connections + on Learning + Christian Szegedy, Sergey Ioffe, Vincent Vanhoucke, Alex Alemi + +Refer to +https://github.com/tensorflow/models/blob/master/slim/nets/inception_v4.py +""" + + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from singa.layer import Conv2D, Activation, MaxPooling2D, AvgPooling2D,\ + Split, Concat, Dropout, Flatten, Dense, BatchNormalization + +from singa import net as ffnet + +ffnet.verbose = True + +def conv2d(net, name, nb_filter, k, s=1, padding='SAME', src=None): + net.add(Conv2D(name, nb_filter, k, s, border_mode=padding, use_bias=False), src) + net.add(BatchNormalization('%s/BatchNorm' % name)) + return net.add(Activation(name+'/relu')) + + +def block_inception_a(name, net): + """Builds Inception-A block for Inception v4 network.""" + # By default use stride=1 and SAME padding + split = net.add(Split('%s/Split' % name, 4)) + br0 = conv2d(net, '%s/Branch_0/Conv2d_0a_1x1' % name, 96, 1, src=split) + conv2d(net, '%s/Branch_1/Conv2d_0a_1x1' % name, 64, 1, src=split) + br1 = conv2d(net, '%s/Branch_1/Conv2d_0b_3x3' % name, 96, 3) + conv2d(net, '%s/Branch_2/Conv2d_0a_1x1' % name, 64, 1, src=split) + conv2d(net, '%s/Branch_2/Conv2d_0b_3x3' % name, 96, 3) + br2 = conv2d(net, '%s/Branch_2/Conv2d_0c_3x3' % name, 96, 3) + net.add(AvgPooling2D('%s/Branch_3/AvgPool_0a_3x3' % name, 3, stride=1), split) + br3 = conv2d(net, '%s/Branch_3/Conv2d_0b_1x1' % name, 96, 1) + return net.add(Concat('%s/Concat' % name, 1), [br0, br1, br2, br3]) + + +def block_reduction_a(name, net): + """Builds Reduction-A block for Inception v4 network.""" + # By default use stride=1 and SAME padding + split = net.add(Split('%s/Split' % name, 3)) + br0 = conv2d(net, '%s/Branch_0/Conv2d_1a_3x3' % name, 384, 3, 2, padding='VALID', src=split) + conv2d(net, '%s/Branch_1/Conv2d_0a_1x1' % name, 192, 1, src=split) + conv2d(net, '%s/Branch_1/Conv2d_0b_3x3' % name, 224, 3) + br1 = conv2d(net, '%s/Branch_1/Conv2d_1a_3x3' % name, 256, 3, 2, padding='VALID') + br2 = net.add(MaxPooling2D('%s/Branch_2/MaxPool_1a_3x3' % name, 3, 2, border_mode='VALID'), split) + return net.add(Concat('%s/Concat' % name, 1), [br0, br1, br2]) + + +def block_inception_b(name, net): + """Builds Inception-B block for Inception v4 network.""" + # By default use stride=1 and SAME padding + split = net.add(Split('%s/Split' % name, 4)) + br0 = conv2d(net, '%s/Branch_0/Conv2d_0a_1x1' % name, 384, 1, src=split) + conv2d(net, '%s/Branch_1/Conv2d_0a_1x1' % name, 192, 1, src=split) + conv2d(net, '%s/Branch_1/Conv2d_0b_1x7' % name, 224, (1, 7)) + br1 = conv2d(net, '%s/Branch_1/Conv2d_0c_7x1' % name, 256, (7, 1)) + conv2d(net, '%s/Branch_2/Conv2d_0a_1x1' % name, 192, 1, src=split) + conv2d(net, '%s/Branch_2/Conv2d_0b_7x1' % name, 192, (7, 1)) + conv2d(net, '%s/Branch_2/Conv2d_0c_1x7' % name, 224, (1, 7)) + conv2d(net, '%s/Branch_2/Conv2d_0d_7x1' % name, 224, (7, 1)) + br2 = conv2d(net, '%s/Branch_2/Conv2d_0e_1x7' % name, 256, (1, 7)) + net.add(AvgPooling2D('%s/Branch_3/AvgPool_0a_3x3' % name, 3, 1), split) + br3 = conv2d(net, '%s/Branch_3/Conv2d_0b_1x1' % name, 128, 1) + return net.add(Concat('%s/Concat' % name, 1), [br0, br1, br2, br3]) + + +def block_reduction_b(name, net): + """Builds Reduction-B block for Inception v4 network.""" + # By default use stride=1 and SAME padding + split = net.add(Split('%s/Split', 3)) + conv2d(net, '%s/Branch_0/Conv2d_0a_1x1' % name, 192, 1, src=split) + br0 = conv2d(net, '%s/Branch_0/Conv2d_1a_3x3' % name, 192, 3, 2, padding='VALID') + conv2d(net, '%s/Branch_1/Conv2d_0a_1x1' % name, 256, 1, src=split) + conv2d(net, '%s/Branch_1/Conv2d_0b_1x7' % name, 256, (1, 7)) + conv2d(net, '%s/Branch_1/Conv2d_0c_7x1' % name, 320, (7, 1)) + br1 = conv2d(net, '%s/Branch_1/Conv2d_1a_3x3' % name, 320, 3, 2, padding='VALID') + br2 = net.add(MaxPooling2D('%s/Branch_2/MaxPool_1a_3x3' % name, 3, 2, border_mode='VALID'), split) + return net.add(Concat('%s/Concat' % name, 1), [br0, br1, br2]) + + +def block_inception_c(name, net): + """Builds Inception-C block for Inception v4 network.""" + # By default use stride=1 and SAME padding + split = net.add(Split('%s/Split' % name, 4)) + br0 = conv2d(net, '%s/Branch_0/Conv2d_0a_1x1' % name, 256, 1, src=split) + conv2d(net, '%s/Branch_1/Conv2d_0a_1x1' % name, 384, 1, src=split) + br1_split = net.add(Split('%s/Branch_1/Split' % name, 2)) + br1_0 = conv2d(net, '%s/Branch_1/Conv2d_0b_1x3' % name, 256, (1, 3), src=br1_split) + br1_1 = conv2d(net, '%s/Branch_1/Conv2d_0c_3x1' % name, 256, (3, 1), src=br1_split) + br1 = net.add(Concat('%s/Branch_1/Concat' % name, 1), [br1_0, br1_1]) + conv2d(net, '%s/Branch_2/Conv2d_0a_1x1' % name, 384, 1, src=split) + conv2d(net, '%s/Branch_2/Conv2d_0b_3x1' % name, 448, (3, 1)) + conv2d(net, '%s/Branch_2/Conv2d_0c_1x3' % name, 512, (1, 3)) + br2_split = net.add(Split('%s/Branch_2/Split' % name, 2)) + br2_0 = conv2d(net, '%s/Branch_2/Conv2d_0d_1x3' % name, 256, (1, 3), src=br2_split) + br2_1 = conv2d(net, '%s/Branch_2/Conv2d_0e_3x1' % name, 256, (3, 1), src=br2_split) + br2 = net.add(Concat('%s/Branch_2/Concat' % name, 1), [br2_0, br2_1]) + net.add(AvgPooling2D('%s/Branch_3/AvgPool_0a_3x3' % name, 3, 1), split) + br3 = conv2d(net, '%s/Branch_3/Conv2d_0b_1x1' % name, 256, 1) + return net.add(Concat('%s/Concat' % name, 1), [br0, br1, br2, br3]) + + +def inception_v4_base(name, sample_shape, final_endpoint='Mixed_7d', aux_name=None): + """Creates the Inception V4 network up to the given final endpoint. + + Args: + inputs: a 4-D tensor of size [batch_size, height, width, 3]. + final_endpoint: specifies the endpoint to construct the network up to. + It can be one of [ 'Conv2d_1a_3x3', 'Conv2d_2a_3x3', 'Conv2d_2b_3x3', + 'Mixed_3a', 'Mixed_4a', 'Mixed_5a', 'Mixed_5b', 'Mixed_5c', 'Mixed_5d', + 'Mixed_5e', 'Mixed_6a', 'Mixed_6b', 'Mixed_6c', 'Mixed_6d', 'Mixed_6e', + 'Mixed_6f', 'Mixed_6g', 'Mixed_6h', 'Mixed_7a', 'Mixed_7b', 'Mixed_7c', + 'Mixed_7d'] + + Returns: + logits: the logits outputs of the model. + end_points: the set of end_points from the inception model. + + Raises: + ValueError: if final_endpoint is not set to one of the predefined values, + """ + end_points = {} + net = ffnet.FeedForwardNet() + def add_and_check_final(name, lyr): + end_points[name] = lyr + return name == final_endpoint + + # 299 x 299 x 3 + net.add(Conv2D('%s/Conv2d_1a_3x3' % name, 32, 3, 2, border_mode='VALID', use_bias=False, input_sample_shape=sample_shape)) + net.add(BatchNormalization('%s/Conv2d_1a_3x3/BatchNorm' % name)) + net.add(Activation('%s/Conv2d_1a_3x3/relu' % name)) + # 149 x 149 x 32 + conv2d(net, '%s/Conv2d_2a_3x3' % name, 32, 3, padding='VALID') + # 147 x 147 x 32 + conv2d(net, '%s/Conv2d_2b_3x3' % name, 64, 3) + # 147 x 147 x 64 + s = net.add(Split('%s/Mixed_3a/Split' % name, 2)) + br0 = net.add(MaxPooling2D('%s/Mixed_3a/Branch_0/MaxPool_0a_3x3' % name, 3, 2, border_mode='VALID'), s) + br1 = conv2d(net, '%s/Mixed_3a/Branch_1/Conv2d_0a_3x3' % name, 96, 3, 2, padding='VALID', src=s) + net.add(Concat('%s/Mixed_3a/Concat' % name, 1), [br0, br1]) + + # 73 x 73 x 160 + s = net.add(Split('%s/Mixed_4a/Split' % name, 2)) + conv2d(net, '%s/Mixed_4a/Branch_0/Conv2d_0a_1x1' % name, 64, 1, src=s) + br0 = conv2d(net, '%s/Mixed_4a/Branch_0/Conv2d_1a_3x3' % name, 96, 3, padding='VALID') + conv2d(net, '%s/Mixed_4a/Branch_1/Conv2d_0a_1x1' % name, 64, 1, src=s) + conv2d(net, '%s/Mixed_4a/Branch_1/Conv2d_0b_1x7' % name, 64, (1, 7)) + conv2d(net, '%s/Mixed_4a/Branch_1/Conv2d_0c_7x1' % name, 64, (7, 1)) + br1 = conv2d(net, '%s/Mixed_4a/Branch_1/Conv2d_1a_3x3' % name, 96, 3, padding='VALID') + net.add(Concat('%s/Mixed_4a/Concat' % name, 1), [br0, br1]) + + # 71 x 71 x 192 + s = net.add(Split('%s/Mixed_5a/Split' % name, 2)) + br0 = conv2d(net, '%s/Mixed_5a/Branch_0/Conv2d_1a_3x3' % name, 192, 3, 2, padding='VALID', src=s) + br1 = net.add(MaxPooling2D('%s/Mixed_5a/Branch_1/MaxPool_1a_3x3' % name, 3, 2, border_mode='VALID'), s) + net.add(Concat('%s/Mixed_5a/Concat' % name, 1), [br0, br1]) + + # 35 x 35 x 384 + # 4 x Inception-A blocks + for idx in range(4): + block_scope = name + '/Mixed_5' + chr(ord('b') + idx) + lyr = block_inception_a(block_scope, net) + if add_and_check_final(block_scope, lyr): return net, lyr, end_points + + # 35 x 35 x 384 + # Reduction-A block + block_reduction_a(name + '/Mixed_6a', net) + + # 17 x 17 x 1024 + # 7 x Inception-B blocks + for idx in range(7): + block_scope = name + '/Mixed_6' + chr(ord('b') + idx) + lyr = block_inception_b(block_scope, net) + if add_and_check_final(block_scope, lyr): return net, lyr, end_points + if block_scope == aux_name: + end_points[aux_name] = net.add(Split('%s/Split' % block_scope, 2)) + + # 17 x 17 x 1024 + # Reduction-B block + block_reduction_b(name + '/Mixed_7a', net) + + # 8 x 8 x 1536 + # 3 x Inception-C blocks + for idx in range(3): + block_scope = name + '/Mixed_7' + chr(ord('b') + idx) + lyr = block_inception_c(block_scope, net) + if add_and_check_final(block_scope, lyr): return net, lyr, end_points + if block_scope == aux_name: + end_points[aux_name] = net.add(Split('%s/Split' % block_scope, 2)) + return net, lyr, end_points + + +def create_net(num_classes=1001, sample_shape=(3, 299, 299), is_training=True, dropout_keep_prob=0.8, create_aux_logits=True): + """Creates the Inception V4 model. + + Args: + num_classes: number of predicted classes. + is_training: whether is training or not. + dropout_keep_prob: float, the fraction to keep before final layer. + reuse: whether or not the network and its variables should be reused. To be + able to reuse 'scope' must be given. + create_aux_logits: Whether to include the auxiliary logits. + + Returns: + logits: the logits outputs of the model. + end_points: the set of end_points from the inception model. + """ + end_points = {} + name = 'InceptionV4' + if is_training and create_aux_logits: + aux_name = name + '/Mixed_6h' + else: + aux_name = None + net, last_layer, end_points = inception_v4_base(name, sample_shape, aux_name=aux_name) + # Auxiliary Head logits + if aux_name is not None: + # 17 x 17 x 1024 + aux_logits = end_points[aux_name] + net.add(AvgPooling2D('%s/AuxLogits/AvgPool_1a_5x5' % name, 5, stride=3, border_mode='VALID'), aux_logits) + t = conv2d(net, '%s/AuxLogits/Conv2d_1b_1x1' % name, 128, 1) + conv2d(net, '%s/AuxLogits/Conv2d_2a' % name, 768, t.get_output_sample_shape()[1:3], padding='VALID') + net.add(Flatten('%s/AuxLogits/flat' % name)) + end_points['AuxLogits'] = net.add(Dense('%s/AuxLogits/Aux_logits' % name, num_classes)) + + # Final pooling and prediction + # 8 x 8 x 1536 + net.add(AvgPooling2D('%s/Logits/AvgPool_1a' % name, last_layer.get_output_sample_shape()[1:3], border_mode='VALID'), last_layer) + # 1 x 1 x 1536 + net.add(Dropout('%s/Logits/Dropout_1b' % name, 1 - dropout_keep_prob)) + net.add(Flatten('%s/Logits/PreLogitsFlatten' % name)) + # 1536 + end_points['Logits'] = net.add(Dense('%s/Logits/Logits' % name, num_classes)) + return net, end_points + + +if __name__ == '__main__': + net, _ = create_net() http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/2cdc1725/examples/imagenet/inceptionv4/serve.py ---------------------------------------------------------------------- diff --git a/examples/imagenet/inceptionv4/serve.py b/examples/imagenet/inceptionv4/serve.py new file mode 100644 index 0000000..9ba099a --- /dev/null +++ b/examples/imagenet/inceptionv4/serve.py @@ -0,0 +1,121 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 model + +from singa import device +from singa import tensor +from singa import image_tool +from singa import layer +from rafiki.agent import Agent, MsgType + +import sys +import time +import traceback +from argparse import ArgumentParser +import numpy as np + + +def serve(agent, use_cpu, parameter_file, topk=5): + if use_cpu: + print('running with cpu') + dev = device.get_default_device() + layer.engine = 'singacpp' + else: + print("runing with gpu") + dev = device.create_cuda_gpu() + agent = agent + + print('Start intialization............') + net, _ = model.create_net(is_training=False) + net.load(parameter_file, use_pickle=True) + net.to_device(dev) + print('End intialization............') + + labels = np.loadtxt('synset_words.txt', str, delimiter='\t').tolist() + labels.insert(0, 'empty background') + while True: + key, val = agent.pull() + if key is None: + time.sleep(0.1) + continue + msg_type = MsgType.parse(key) + if msg_type.is_request(): + try: + response = "" + ratio = 0.875 + img = image_tool.load_img(val['image']) + height, width = img.size[0], img.size[1] + print(img.size) + crop_h, crop_w = int(height * ratio), int(width * ratio) + img = np.array(image_tool.crop(img, (crop_h, crop_w), 'center').resize((299, 299))).astype(np.float32) / float(255) + img -= 0.5 + img *= 2 + # img[:,:,[0,1,2]] = img[:,:,[2,1,0]] + img = img.transpose((2, 0, 1)) + images = np.expand_dims(img, axis=0) + x = tensor.from_numpy(images.astype(np.float32)) + x.to_device(dev) + y = net.predict(x) + prob = np.average(tensor.to_numpy(y), 0) + # sort and reverse + idx = np.argsort(-prob)[0:topk] + for i in idx: + response += "%s:%s<br/>" % (labels[i], prob[i]) + except: + traceback.print_exc() + response = "Sorry, system error during prediction." + agent.push(MsgType.kResponse, response) + elif MsgType.kCommandStop.equal(msg_type): + print('get stop command') + agent.push(MsgType.kStatus, "success") + break + else: + print('get unsupported message %s' % str(msg_type)) + agent.push(MsgType.kStatus, "Unknown command") + break + # while loop + print("server stop") + + +def main(): + try: + # Setup argument parser + parser = ArgumentParser(description="InceptionV4 for image classification") + parser.add_argument("-p", "--port", default=9999, help="listen port") + parser.add_argument("-C", "--use_cpu", action="store_true") + parser.add_argument("--parameter_file", default="inception_v4.pickle", + help="relative path") + + # Process arguments + args = parser.parse_args() + port = args.port + + # start to train + agent = Agent(port) + serve(agent, args.use_cpu, args.parameter_file) + agent.stop() + + except SystemExit: + return + except: + traceback.print_exc() + sys.stderr.write(" for help use --help \n\n") + return 2 + + +if __name__ == '__main__': + main() http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/2cdc1725/python/singa/image_tool.py ---------------------------------------------------------------------- diff --git a/python/singa/image_tool.py b/python/singa/image_tool.py index bbdd32e..7c9caeb 100644 --- a/python/singa/image_tool.py +++ b/python/singa/image_tool.py @@ -53,11 +53,11 @@ def crop(img, patch, position): and center. ''' if img.size[0] < patch[0]: - raise Exception( - 'img size[0] %d is smaller than patch[0]: %d' % (img[0], patch[0])) + raise Exception('img size[0] %d is smaller than patch[0]: %d' + % (img.size[0], patch[0])) if img.size[1] < patch[1]: - raise Exception( - 'img size[1] %d is smaller than patch[1]: %d' % (img[1], patch[1])) + raise Exception('img size[1] %d is smaller than patch[1]: %d' + % (img.size[1], patch[1])) if position == 'left_top': left, upper = 0, 0 http://git-wip-us.apache.org/repos/asf/incubator-singa/blob/2cdc1725/python/singa/layer.py ---------------------------------------------------------------------- diff --git a/python/singa/layer.py b/python/singa/layer.py index 4fe9983..026a766 100644 --- a/python/singa/layer.py +++ b/python/singa/layer.py @@ -340,7 +340,10 @@ class Conv2D(Layer): conf.num_output = nb_kernels conf.prefer = cudnn_prefer conf.workspace_byte_limit = workspace_byte_limit - conf = _set_kernel_stride_pad(conf, kernel, stride, border_mode, pad) + self.kernel = kernel + self.stride = stride + self.pad = pad + self.border_mode = border_mode conf.bias_term = use_bias # TODO(wangwei) enable data format for cpp code # conf.data_format = data_format @@ -365,6 +368,21 @@ class Conv2D(Layer): if input_sample_shape is not None: self.setup(input_sample_shape) + def setup(self, in_shape): + '''Set up the kernel, stride and padding; then call the C++ setup + function to create params and set some meta data. + + Args: + in_shapes is a tuple of int for the input sample shape + ''' + if self.has_setup: + return + _set_kernel_stride_pad(self.conf.convolution_conf, self.kernel, + self.stride, self.border_mode, self.pad, + in_shape) + self.layer.Setup(list(in_shape), self.conf.SerializeToString()) + self.has_setup = True + class Conv1D(Conv2D): """Construct a layer for 1D convolution. @@ -417,13 +435,30 @@ class Pooling2D(Layer): assert data_format == 'NCHW', 'Not supported data format: %s ' \ 'only "NCHW" is enabled currently' % (data_format) conf = self.conf.pooling_conf - conf = _set_kernel_stride_pad(conf, kernel, stride, border_mode, pad) conf.pool = mode + self.kernel = kernel + self.stride = stride + self.pad = pad + self.border_mode = border_mode _check_engine(engine, ['cudnn', 'singacpp', 'singacl']) self.layer = _create_layer(engine, 'Pooling') if input_sample_shape is not None: self.setup(input_sample_shape) + def setup(self, in_shape): + '''Set up the kernel, stride and padding; then call the C++ setup + function to create params and set some meta data. + + Args: + in_shapes is a tuple of int for the input sample shape + ''' + if self.has_setup: + return + _set_kernel_stride_pad(self.conf.pooling_conf, self.kernel, self.stride, + self.border_mode, self.pad, in_shape) + self.layer.Setup(list(in_shape), self.conf.SerializeToString()) + self.has_setup = True + class MaxPooling2D(Pooling2D): @@ -1144,8 +1179,20 @@ def _create_layer(eng, layer): return singa_wrap.CreateLayer(layer_type.lower()) -def _set_kernel_stride_pad(conf, kernel, stride, border_mode, pad): - """Private function called by Convolution2D and Pooling2D.""" +def _set_kernel_stride_pad(conf, kernel, stride, border_mode, pad, in_shape): + """Private function called by Convolution2D and Pooling2D. + + PyTorch: + http://pytorch.org/docs/nn.html#pooling-layers + floor for both conv and pooling + Caffe: + https://github.com/BVLC/caffe/issues/1318#issuecomment-59594323 + floor for conv and ceil for pooling + Tensorflow: https://www.tensorflow.org/api_guides/python/nn#Convolution + SAME outsize = ceil(insize/stride), + pad_h_w = max((outsize-1)*stride+k-insize, 0) + VALID same as pytorch + """ if isinstance(kernel, tuple): conf.kernel_h = kernel[0] conf.kernel_w = kernel[1] @@ -1162,16 +1209,20 @@ def _set_kernel_stride_pad(conf, kernel, stride, border_mode, pad): if pad is None: # TODO(wangwei) check the border mode if mode == 'same': - assert conf.kernel_h % 2 == 1 and conf.kernel_w % 2 == 1, \ - 'Must use odd kernel for mode="same", kernel is (%d, %d)' % ( - conf.kernel_h, conf.kernel_w) - pad = (conf.kernel_h / 2, conf.kernel_w / 2) + out_h = in_shape[1] / conf.stride_h + out_w = in_shape[2] / conf.stride_w + ph = max((out_h - 1) * conf.stride_h + conf.kernel_h - in_shape[1], + 0) + pw = max((out_w - 1) * conf.stride_w + conf.kernel_w - in_shape[2], + 0) + assert ph % 2 == 0 and pw % 2 == 0, 'ph=%d and pw=%d are not even' \ + % (ph, pw) + pad = (ph / 2, pw / 2) elif mode == 'valid': pad = (0, 0) else: assert False, ('Unsupported border_mode: %s. ' - 'Please use {"valid", "same"}' % border_mode) - assert isinstance(pad, tuple), 'pad should be a tuple' + 'Please use {"VALID", "SAME"}' % border_mode) if isinstance(pad, tuple): conf.pad_h = pad[0] conf.pad_w = pad[1]
