From edd72d6904b69ae266cf5067fddbb6487dc4d291 Mon Sep 17 00:00:00 2001 From: SRK Reddy Date: Wed, 6 Jun 2018 11:14:47 +0530 Subject: [PATCH] Mobilenet Support (Don't merge) - 1 --- nnvm/python/nnvm/top/nn.py | 8 +- nnvm/src/top/nn/convolution.cc | 11 +- tutorials/nnvm/from_tensorflow.py | 169 ++++++++++++++++------- tutorials/nnvm/imagenet_inference_gpu.py | 4 +- 4 files changed, 137 insertions(+), 55 deletions(-) diff --git a/nnvm/python/nnvm/top/nn.py b/nnvm/python/nnvm/top/nn.py index 4106ce2d68e1c..399b48ca37cea 100644 --- a/nnvm/python/nnvm/top/nn.py +++ b/nnvm/python/nnvm/top/nn.py @@ -116,13 +116,19 @@ def compute_conv2d(attrs, inputs, _): def schedule_conv2d(attrs, outs, target): """Schedule definition of conv2d""" groups = attrs.get_int("groups") + channels = attrs.get_int("channels") layout = attrs["layout"] with tvm.target.create(target): if groups == 1 and layout == "NCHW": return topi.generic.schedule_conv2d_nchw(outs) elif groups == 1 and layout == "NHWC": return topi.generic.schedule_conv2d_nhwc(outs) - return topi.generic.schedule_depthwise_conv2d_nchw(outs) + elif groups == channels and layout == "NCHW": + return topi.generic.schedule_depthwise_conv2d_nchw(outs) + elif groups == channels and layout == "NHWC": + return topi.generic.schedule_depthwise_conv2d_nhwc(outs) + else: + raise ValueError("No compatible schedule") @reg.register_alter_op_layout("conv2d") def alter_conv2d_layout(attrs, inputs, tinfos): diff --git a/nnvm/src/top/nn/convolution.cc b/nnvm/src/top/nn/convolution.cc index 169f1ed185b59..5ece46865b6e7 100644 --- a/nnvm/src/top/nn/convolution.cc +++ b/nnvm/src/top/nn/convolution.cc @@ -73,13 +73,22 @@ inline bool Conv2DInferShape(const nnvm::NodeAttrs& attrs, CHECK_EQ(param.channels % param.groups, 0U) << "output channels must divide group size"; + TShape wshape({param.channels / param.groups, dshape[1] / param.groups, param.kernel_size[0], param.kernel_size[1]}); wshape = ConvertLayout(wshape, kOIHW, kernel_layout); - wshape[kernel_layout.indexof('I')] *= param.groups; + // Depthwise + // NCHW : Expects weights in CNHW - Conversion is handled in frontend. + // NHWC : Original format (HWCN) + if (param.layout == "NHWC") { + wshape[kernel_layout.indexof('I')] *= param.groups; + } else { + wshape[0] *= param.groups; + } + NNVM_ASSIGN_INPUT_SHAPE(attrs, *in_shape, Conv2DParam::kWeight, wshape); if (param.use_bias) { diff --git a/tutorials/nnvm/from_tensorflow.py b/tutorials/nnvm/from_tensorflow.py index 850432bfdfe09..96c1d6e0f4f76 100644 --- a/tutorials/nnvm/from_tensorflow.py +++ b/tutorials/nnvm/from_tensorflow.py @@ -14,6 +14,7 @@ import tvm import numpy as np import os.path +import re # Tensorflow imports import tensorflow as tf @@ -21,12 +22,11 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import tensor_util -import nnvm.testing.tf -repo_base = 'https://github.com/dmlc/web-data/raw/master/tensorflow/models/InceptionV1/' +repo_base = 'https://github.com/srkreddy1238/dmlc_data/raw/master/models/tensorflow/InceptionV1/' img_name = 'elephant-299.jpg' image_url = os.path.join(repo_base, img_name) -model_name = 'classify_image_graph_def-with_shapes.pb' +model_name = '/home/srk/work/NN/TF/tensorflow/imagenet/model_with_output_shapes.pb' model_url = os.path.join(repo_base, model_name) map_proto = 'imagenet_2012_challenge_label_map_proto.pbtxt' map_proto_url = os.path.join(repo_base, map_proto) @@ -35,36 +35,10 @@ ###################################################################### -# Download processed tensorflow model -# ----------------------------------- -# In this section, we download a pretrained Tensorflow model and classify an image. -from mxnet.gluon.utils import download - -download(image_url, img_name) -download(model_url, model_name) -download(map_proto_url, map_proto) -download(lable_map_url, lable_map) - - -###################################################################### -# Creates graph from saved graph_def.pb. -# -------------------------------------- +# Some helper functions def _ProcessGraphDefParam(graph_def): - """Type-checks and possibly canonicalizes `graph_def`. - - Parameters - ---------- - graph_def : Obj - tensorflow graph definition. - - Returns - ------- - graph_def : Obj - tensorflow graph devinition - - """ - + """Type-checks and possibly canonicalizes `graph_def`.""" if not isinstance(graph_def, graph_pb2.GraphDef): # `graph_def` could be a dynamically-created message, so try a duck-typed # approach @@ -76,6 +50,84 @@ def _ProcessGraphDefParam(graph_def): raise TypeError('graph_def must be a GraphDef proto.') return graph_def +class NodeLookup(object): + """Converts integer node ID's to human readable labels.""" + + def __init__(self, + label_lookup_path=None, + uid_lookup_path=None): + if not label_lookup_path: + label_lookup_path = os.path.join( + "./", map_proto) + if not uid_lookup_path: + uid_lookup_path = os.path.join( + "./", lable_map) + self.node_lookup = self.load(label_lookup_path, uid_lookup_path) + + def load(self, label_lookup_path, uid_lookup_path): + """Loads a human readable English name for each softmax node. + + Args: + label_lookup_path: string UID to integer node ID. + uid_lookup_path: string UID to human-readable string. + + Returns: + dict from integer node ID to human-readable string. + """ + if not tf.gfile.Exists(uid_lookup_path): + tf.logging.fatal('File does not exist %s', uid_lookup_path) + if not tf.gfile.Exists(label_lookup_path): + tf.logging.fatal('File does not exist %s', label_lookup_path) + + # Loads mapping from string UID to human-readable string + proto_as_ascii_lines = tf.gfile.GFile(uid_lookup_path).readlines() + uid_to_human = {} + p = re.compile(r'[n\d]*[ \S,]*') + for line in proto_as_ascii_lines: + parsed_items = p.findall(line) + uid = parsed_items[0] + human_string = parsed_items[2] + uid_to_human[uid] = human_string + + # Loads mapping from string UID to integer node ID. + node_id_to_uid = {} + proto_as_ascii = tf.gfile.GFile(label_lookup_path).readlines() + for line in proto_as_ascii: + if line.startswith(' target_class:'): + target_class = int(line.split(': ')[1]) + if line.startswith(' target_class_string:'): + target_class_string = line.split(': ')[1] + node_id_to_uid[target_class] = target_class_string[1:-2] + + # Loads the final mapping of integer node ID to human-readable string + node_id_to_name = {} + for key, val in node_id_to_uid.items(): + if val not in uid_to_human: + tf.logging.fatal('Failed to locate: %s', val) + name = uid_to_human[val] + node_id_to_name[key] = name + + return node_id_to_name + + def id_to_string(self, node_id): + if node_id not in self.node_lookup: + return '' + return self.node_lookup[node_id] + +###################################################################### +# Download processed tensorflow model +# --------------------------------------------- +# In this section, we download a pretrained Tensorflow model and classify an image. +from mxnet.gluon.utils import download + +#download(image_url, img_name) +#download(model_url, model_name) +#download(map_proto_url, map_proto) +#download(lable_map_url, lable_map) + + +###################################################################### +# Creates graph from saved graph_def.pb. with tf.gfile.FastGFile(os.path.join( "./", model_name), 'rb') as f: graph_def = tf.GraphDef() @@ -86,35 +138,38 @@ def _ProcessGraphDefParam(graph_def): ###################################################################### # Decode image -# ------------ from PIL import Image -image = Image.open(img_name).resize((299, 299)) +image = Image.open(img_name).resize((224, 224)) def transform_image(image): - image = np.array(image) + image = np.array(image).astype('float32') + image = np.expand_dims(image, axis=0) return image x = transform_image(image) print('x', x.shape) +print('x type', x.dtype) ###################################################################### # Import the graph to NNVM -# ------------------------ +# ----------------- + +#print (graph_def) sym, params = nnvm.frontend.from_tensorflow(graph_def) ###################################################################### # Now compile the graph through NNVM import nnvm.compiler target = 'llvm' -shape_dict = {'DecodeJpeg/contents': x.shape} -dtype_dict = {'DecodeJpeg/contents': 'uint8'} +shape_dict = {'input': x.shape} +dtype_dict = {'input': x.dtype} graph, lib, params = nnvm.compiler.build(sym, target, shape_dict, dtype=dtype_dict, params=params) +print ("Build Completed\n") ###################################################################### # Save the compilation output. -# ---------------------------- """ lib.export_library("imagenet_tensorflow.so") with open("imagenet_tensorflow.json", "w") as fo: @@ -129,15 +184,15 @@ def transform_image(image): # Now, we would like to reproduce the same forward computation using TVM. from tvm.contrib import graph_runtime ctx = tvm.cpu(0) -dtype = 'uint8' +dtype = 'float32' m = graph_runtime.create(graph, lib, ctx) # set inputs -m.set_input('DecodeJpeg/contents', tvm.nd.array(x.astype(dtype))) +m.set_input('input', tvm.nd.array(x.astype(dtype))) m.set_input(**params) # execute m.run() # get outputs -tvm_output = m.get_output(0, tvm.nd.empty(((1, 1008)), 'float32')) +tvm_output = m.get_output(0, tvm.nd.empty(((1, 1001)), 'float32')) ###################################################################### @@ -146,9 +201,25 @@ def transform_image(image): predictions = tvm_output.asnumpy() predictions = np.squeeze(predictions) +print ("Predictions:", predictions) + +top_k = predictions.argsort()[-5:][::-1] +print ("TOP TVM:", top_k) + +tf_output = np.load("/home/srk/work/NN/TF/tensorflow/imagenet/tf_dump.txt.npy"); + +print ("TF: SHAPE:", tf_output.shape) +print ("TF:", tf_output) + +top_k = tf_output.argsort()[-5:][::-1] +print ("TOP TF:", top_k) + +np.testing.assert_allclose(tf_output, predictions, atol=1e-3, rtol=1e-3) + +exit(0) + # Creates node ID --> English string lookup. -node_lookup = nnvm.testing.tf.NodeLookup(label_lookup_path=os.path.join("./", map_proto), - uid_lookup_path=os.path.join("./", lable_map)) +node_lookup = NodeLookup() top_k = predictions.argsort()[-5:][::-1] for node_id in top_k: @@ -172,13 +243,10 @@ def create_graph(): def run_inference_on_image(image): """Runs inference on an image. - Parameters - ---------- - image: String - Image file name. + Args: + image: Image file name. - Returns - ------- + Returns: Nothing """ if not tf.gfile.Exists(image): @@ -196,8 +264,7 @@ def run_inference_on_image(image): predictions = np.squeeze(predictions) # Creates node ID --> English string lookup. - node_lookup = nnvm.testing.tf.NodeLookup(label_lookup_path=os.path.join("./", map_proto), - uid_lookup_path=os.path.join("./", lable_map)) + node_lookup = NodeLookup() top_k = predictions.argsort()[-5:][::-1] print ("===== TENSORFLOW RESULTS =======") diff --git a/tutorials/nnvm/imagenet_inference_gpu.py b/tutorials/nnvm/imagenet_inference_gpu.py index 09c90c2c54a58..e302334073b54 100644 --- a/tutorials/nnvm/imagenet_inference_gpu.py +++ b/tutorials/nnvm/imagenet_inference_gpu.py @@ -41,8 +41,8 @@ def tvm_callback_cuda_compile(code): # # In a typical workflow, we can get this pair from :any:`nnvm.frontend` # -target = "cuda" -ctx = tvm.gpu(0) +target = "llvm" +ctx = tvm.cpu(0) batch_size = 1 num_classes = 1000 image_shape = (3, 224, 224)