Caffe2 - Python API
A deep learning, cross platform ML framework
test_onnxifi.py
1 from __future__ import absolute_import
2 from __future__ import division
3 from __future__ import print_function
4 from __future__ import unicode_literals
5 
6 import json
7 import numpy as np
8 import os
9 import time
10 import unittest
11 
12 import onnx
13 import onnx.defs
14 from onnx.backend.base import namedtupledict
15 from onnx.helper import make_node, make_graph, make_tensor, make_tensor_value_info, make_model
16 from caffe2.proto import caffe2_pb2
17 from caffe2.python import core, workspace
18 from caffe2.python.models.download import downloadFromURLToFile, getURLFromName, deleteDirectory
19 from caffe2.python.onnx.onnxifi import onnxifi_caffe2_net
20 from caffe2.python.onnx.tests.test_utils import TestCase
21 
22 ONNXIFI_DATATYPE_FLOAT32 = 1
23 
24 
25 def _print_net(net):
26  for i in net.external_input:
27  print("Input: {}".format(i))
28  for i in net.external_output:
29  print("Output: {}".format(i))
30  for op in net.op:
31  print("Op {}".format(op.type))
32  for x in op.input:
33  print(" input: {}".format(x))
34  for y in op.output:
35  print(" output: {}".format(y))
36 
37 
39  @unittest.skip("Need ONNXIFI backend support")
40  def test_relu_graph(self):
41  batch_size = 1
42  X = np.random.randn(batch_size, 1, 3, 2).astype(np.float32)
43  graph_def = make_graph(
44  [make_node("Relu", ["X"], ["Y"])],
45  name="test",
46  inputs=[make_tensor_value_info("X", onnx.TensorProto.FLOAT,
47  [batch_size, 1, 3, 2])],
48  outputs=[make_tensor_value_info("Y", onnx.TensorProto.FLOAT,
49  [batch_size, 1, 3, 2])])
50  model_def = make_model(graph_def, producer_name='relu-test')
51  op = core.CreateOperator(
52  "Onnxifi",
53  ["X"],
54  ["Y"],
55  onnx_model=model_def.SerializeToString(),
56  input_names=["X"],
57  output_names=["Y"],
58  output_shape_hint_0=[ONNXIFI_DATATYPE_FLOAT32, batch_size, 1, 3, 2])
59  workspace.FeedBlob("X", X)
60  workspace.RunOperatorOnce(op)
61  Y = workspace.FetchBlob("Y")
62  np.testing.assert_almost_equal(Y, np.maximum(X, 0))
63 
64  @unittest.skip("Need ONNXIFI backend support")
65  def test_conv_graph(self):
66  X = np.array([[[[0., 1., 2., 3., 4.], # (1, 1, 5, 5) input tensor
67  [5., 6., 7., 8., 9.],
68  [10., 11., 12., 13., 14.],
69  [15., 16., 17., 18., 19.],
70  [20., 21., 22., 23., 24.]]]]).astype(np.float32)
71  W = np.array([[[[1., 1., 1.], # (1, 1, 3, 3) tensor for convolution weights
72  [1., 1., 1.],
73  [1., 1., 1.]]]]).astype(np.float32)
74  Y_without_padding = np.array([[[[54., 63., 72.], # (1, 1, 3, 3) output tensor
75  [99., 108., 117.],
76  [144., 153., 162.]]]]).astype(np.float32)
77  graph_def = make_graph(
78  [make_node(
79  'Conv',
80  inputs=['X', 'W'],
81  outputs=['Y'],
82  kernel_shape=[3, 3],
83  # Default values for other attributes: strides=[1, 1], dilations=[1, 1], groups=1
84  pads=[0, 0, 0, 0],
85  )],
86  name="test",
87  inputs=[make_tensor_value_info("X", onnx.TensorProto.FLOAT, [1, 1, 5, 5]),
88  make_tensor_value_info("W", onnx.TensorProto.FLOAT, [1, 1, 3, 3]),
89  ],
90  outputs=[make_tensor_value_info("Y", onnx.TensorProto.FLOAT,
91  [1, 1, 3, 3])])
92  model_def = make_model(graph_def, producer_name='conv-test')
93  # We intentional rewrite the input/output name so test that the
94  # input/output binding of c2 op is positional
95  op = core.CreateOperator(
96  "Onnxifi",
97  ["X0"],
98  ["Y0"],
99  onnx_model=model_def.SerializeToString(),
100  initializers=["W", "W0"],
101  input_names=["X"],
102  output_names=["Y"],
103  output_shape_hint_0=[ONNXIFI_DATATYPE_FLOAT32, 1, 1, 3, 3])
104  workspace.FeedBlob("X0", X)
105  workspace.FeedBlob("W0", W)
106  workspace.RunOperatorOnce(op)
107  Y = workspace.FetchBlob("Y0")
108  np.testing.assert_almost_equal(Y, Y_without_padding)
109 
110 
112  def _model_dir(self, model):
113  caffe2_home = os.path.expanduser(os.getenv('CAFFE2_HOME', '~/.caffe2'))
114  models_dir = os.getenv('CAFFE2_MODELS', os.path.join(caffe2_home, 'models'))
115  return os.path.join(models_dir, model)
116 
117  def _download(self, model):
118  model_dir = self._model_dir(model)
119  assert not os.path.exists(model_dir)
120  os.makedirs(model_dir)
121  for f in ['predict_net.pb', 'init_net.pb', 'value_info.json']:
122  url = getURLFromName(model, f)
123  dest = os.path.join(model_dir, f)
124  try:
125  try:
126  downloadFromURLToFile(url, dest,
127  show_progress=False)
128  except TypeError:
129  # show_progress not supported prior to
130  # Caffe2 78c014e752a374d905ecfb465d44fa16e02a28f1
131  # (Sep 17, 2017)
132  downloadFromURLToFile(url, dest)
133  except Exception as e:
134  print("Abort: {reason}".format(reason=e))
135  print("Cleaning up...")
136  deleteDirectory(model_dir)
137  exit(1)
138 
139  # TODO: we need to modulize this function
140  def _get_c2_model(self, model_name):
141  model_dir = self._model_dir(model_name)
142  if not os.path.exists(model_dir):
143  self._download(model_name)
144  c2_predict_pb = os.path.join(model_dir, 'predict_net.pb')
145  c2_predict_net = caffe2_pb2.NetDef()
146  with open(c2_predict_pb, 'rb') as f:
147  c2_predict_net.ParseFromString(f.read())
148  c2_predict_net.name = model_name
149 
150  c2_init_pb = os.path.join(model_dir, 'init_net.pb')
151  c2_init_net = caffe2_pb2.NetDef()
152  with open(c2_init_pb, 'rb') as f:
153  c2_init_net.ParseFromString(f.read())
154  c2_init_net.name = model_name + '_init'
155 
156  with open(os.path.join(model_dir, 'value_info.json')) as f:
157  value_info = json.load(f)
158 
159  return c2_init_net, c2_predict_net, value_info
160 
161  def _add_head_tail(self, pred_net, new_head, new_tail):
162  orig_head = pred_net.external_input[0]
163  orig_tail = pred_net.external_output[0]
164 
165  # Add head
166  head = caffe2_pb2.OperatorDef()
167  head.type = "Copy"
168  head.input.append(new_head)
169  head.output.append(orig_head)
170  dummy = caffe2_pb2.NetDef()
171  dummy.op.extend(pred_net.op)
172  del pred_net.op[:]
173  pred_net.op.extend([head])
174  pred_net.op.extend(dummy.op)
175  pred_net.external_input[0] = new_head
176 
177  # Add tail
178  tail = caffe2_pb2.OperatorDef()
179  tail.type = "Copy"
180  tail.input.append(orig_tail)
181  tail.output.append(new_tail)
182  pred_net.op.extend([tail])
183  pred_net.external_output[0] = new_tail
184 
185  @unittest.skip("Need ONNXIFI backend support")
186  def test_resnet50_core(self):
187  N = 1
188  repeat = 1
189  print("Batch size: {}, repeat inference {} times".format(N, repeat))
190  init_net, pred_net, _ = self._get_c2_model('resnet50')
191  self._add_head_tail(pred_net, 'real_data', 'real_softmax')
192  input_blob_dims = (N, 3, 224, 224)
193  input_name = "real_data"
194 
195  device_option = core.DeviceOption(caffe2_pb2.CPU, 0)
196  init_net.device_option.CopyFrom(device_option)
197  pred_net.device_option.CopyFrom(device_option)
198  for op in pred_net.op:
199  op.device_option.CopyFrom(device_option)
200  net_outputs = pred_net.external_output
201  Y_c2 = None
202  data = np.random.randn(*input_blob_dims).astype(np.float32)
203  c2_time = 1
204  workspace.SwitchWorkspace("onnxifi_test", True)
205  with core.DeviceScope(device_option):
206  workspace.FeedBlob(input_name, data)
207  workspace.RunNetOnce(init_net)
208  workspace.CreateNet(pred_net)
209  start = time.time()
210  for _ in range(repeat):
211  workspace.RunNet(pred_net.name)
212  end = time.time()
213  c2_time = end - start
214  output_values = [workspace.FetchBlob(name) for name in net_outputs]
215  Y_c2 = namedtupledict('Outputs', net_outputs)(*output_values)
216  workspace.ResetWorkspace()
217 
218  # Fill the workspace with the weights
219  with core.DeviceScope(device_option):
220  workspace.RunNetOnce(init_net)
221 
222  # Cut the graph
223  start = time.time()
224  pred_net_cut = onnxifi_caffe2_net(pred_net,
225  {input_name: input_blob_dims},
226  infer_shapes=True)
227  del init_net, pred_net
228  #_print_net(pred_net_cut)
229 
230  Y_trt = None
231  input_name = pred_net_cut.external_input[0]
232  print("C2 runtime: {}s".format(c2_time))
233  with core.DeviceScope(device_option):
234  workspace.FeedBlob(input_name, data)
235  workspace.CreateNet(pred_net_cut)
236  end = time.time()
237  print("Conversion time: {:.2f}s".format(end - start))
238 
239  start = time.time()
240  for _ in range(repeat):
241  workspace.RunNet(pred_net_cut.name)
242  end = time.time()
243  trt_time = end - start
244  print("Onnxifi runtime: {}s, improvement: {}%".format(trt_time, (c2_time - trt_time) / c2_time * 100))
245  output_values = [workspace.FetchBlob(name) for name in net_outputs]
246  Y_trt = namedtupledict('Outputs', net_outputs)(*output_values)
247  np.testing.assert_allclose(Y_c2, Y_trt, rtol=1e-3)
248 
249 
def _add_head_tail(self, pred_net, new_head, new_tail)