def setUp(self): class Model(chainer.Chain): def __init__(self): super(Model, self).__init__() def __call__(self, x1, x2): return F.concat((x1, x2)) self.model = Model() self.x1 = input_generator.increasing(2, 5) self.x2 = input_generator.increasing(2, 4)
def setUp(self): class Model(chainer.Chain): def __init__(self): super(Model, self).__init__() with self.init_scope(): self.prelu = L.PReLU() def __call__(self, x, y, z): return F.relu(x) + self.prelu(y) * z self.model = Model() self.ins = (input_generator.increasing(1, 5), input_generator.increasing(1, 5), input_generator.increasing(1, 5)) self.fn = 'MultiInputs.onnx'
def test_output(self): class Model(chainer.Chain): def __init__(self, value): super().__init__() self.value = value @as_funcnode('NumpyFull') def full(self, xs, value=0): # not support `def full(self, xs_shape, value=0)` # wrapped function node cannot handle shape directly yet. return np.full(xs.array.shape, value, dtype=np.float32) def __call__(self, xs): return F.sigmoid(self.full(xs, value=self.value)) model = Model(value=5) x = input_generator.increasing(2, 3, 4) def numpy_full_converter(params): gb = onnx_helper.GraphBuilder() output = gb.op('Shape', params.input_names) value = onnx.helper.make_tensor( 'value', onnx.TensorProto.FLOAT, [1], [params.func.value]) gb.op_output_named( 'ConstantOfShape', [output], params.output_names, value=value) return gb.nodes() addon_converters = {'NumpyFull': numpy_full_converter} self.expect( model, x, skip_opset_version=[7, 8], external_converters=addon_converters)
def test_get_item_error(slices): model = chainer.Sequential( lambda x: F.get_item(x, slices=slices)) x = input_generator.increasing(2, 3, 4) with pytest.raises(ValueError): export(model, x)
def test_output(self, name, slices): name = 'get_item_' + name model = chainer.Sequential(lambda x: F.get_item(x, slices=slices)) x = input_generator.increasing(2, 3, 4) self.expect(model, x, name=name, expected_num_initializers=0)
def test_output(self): model = chainer.Sequential( F.where ) cond = np.array([[1, 0, 0], [0, 1, 0]], dtype=np.bool) x = input_generator.increasing(2, 3) y = np.zeros((2, 3), np.float32) self.expect(model, (cond, x, y), skip_opset_version=[7, 8])
def setUp(self): class Model(chainer.Chain): def __call__(self, x): gamma = np.ones(x.shape[1:], dtype=x.dtype) beta = np.zeros(x.shape[1:], dtype=x.dtype) return F.batch_normalization(x, gamma, beta) self.model = Model() self.x = input_generator.increasing(2, 5)
def test_output(self): attr = None is_deco = False if self.func_kind == 'list': def input_converter(xs): return ([xs[0], xs[1]],), {} def target_func(xs): return xs[0].array + xs[1].array elif self.func_kind == 'list_kwargs': def input_converter(xs): return (), {'xs': [xs[0], xs[1]]} def target_func(xs=None): assert xs is not None return xs[0].array + xs[1].array elif self.func_kind == 'var_with_deco': def input_converter(xs): return (xs,), {} @as_funcnode('AddConstant', rename_attributes=[('b', 'value')]) def target_func(x, b=0.01): return x.array + b is_deco = True elif self.func_kind == 'var_kwargs': def input_converter(xs): return (), {'x': xs, 'value': 0.02} def target_func(x=None, value=0.01): assert x is not None return x.array + value else: assert self.func_kind == 'var' def input_converter(xs): return (xs, 0.01), {} def target_func(x, value): return x.array + value attr = [(1, 'value')] model = self.get_model(target_func, input_converter) x = input_generator.increasing(*self.in_shape) if not is_deco: model.fn = fake_as_funcnode( model.fn, self.op_type, rename_attributes=attr) name = 'replace_func_' + self.func_kind self.expect(model, x, name=name)
def test_grad_error(self): model = self.get_model() # this alternative function does not return chainer.Variable # backward propagation will fail model.half = fake_as_funcnode( lambda xs, value=0.5: xs.array * value, 'MulConstant') x = input_generator.increasing(2, 5) with pytest.raises(ValueError): self.expect(model, x, output_grad=True)
def setUp(self): class Model(chainer.Chain): def __init__(self, ops): super(Model, self).__init__() self.ops = ops def __call__(self, a, b, c): if not isinstance(a, chainer.Variable): a = chainer.Variable(a) if not isinstance(b, chainer.Variable): b = chainer.Variable(b) if not isinstance(c, chainer.Variable): c = chainer.Variable(c) return eval(self.ops) self.model = Model(self.ops) a = chainer.Variable(input_generator.increasing(2, 3)) b = chainer.Variable(input_generator.increasing(2, 3) * 0.3) c = chainer.Variable(input_generator.increasing(2, 3) * 0.7) self.x = (a, b, c)
def get_x(self, in_type=None): base_x = (input_generator.increasing(1, 5), input_generator.increasing(1, 5) * 1.1, input_generator.increasing(1, 5) * 1.2) names = ['x', 'y', 'z'] if in_type is None: return base_x elif in_type == 'list': return list(base_x) elif in_type == 'variable': return tuple(chainer.Variable(v) for v in base_x) elif in_type == 'variable_list': return [chainer.Variable(v) for v in base_x] elif in_type == 'dict': return {names[i]: v for i, v in enumerate(base_x)} elif in_type == 'variable_dict': return { names[i]: chainer.Variable(v) for i, v in enumerate(base_x) }
def setUp(self): class Model(chainer.Chain): def __call__(self, x, gamma, beta, mean, var): return F.fixed_batch_normalization(x, gamma, beta, mean, var) self.model = Model() self.x = input_generator.increasing(2, 5) self.mean = self.x.mean(axis=0) self.var = self.x.var(axis=0) self.gamma = np.ones_like(self.mean, dtype=self.x.dtype) self.beta = np.zeros_like(self.mean, dtype=self.x.dtype)
def test_output(self): class Model(chainer.Chain): def forward(self, x, t): return F.select_item(x, t) model = Model() x = input_generator.increasing(3, 5) t = np.array([4, 1, 0], dtype=np.int32) self.expect(model, (x, t), expected_num_initializers=0, skip_opset_version=list(range(1, 9)))
def test_output(self): class Model(chainer.Chain): def __init__(self): super(Model, self).__init__() def __call__(self, *xs): return F.transpose_sequence(xs) model = Model() xs = [input_generator.increasing(*shape) for shape in self.in_shapes] self.expect(model, xs, name=self.name)
def test_get_item_slice_step(self, name, slices): skip_opsets = tuple(range(7, 11)) name = 'get_item_' + name model = chainer.Sequential(lambda x: F.get_item(x, slices=slices)) x = input_generator.increasing(2, 3, 4) self.expect(model, x, name=name, expected_num_initializers=0, skip_opset_version=skip_opsets)
def test_export_external_converters_custom_op(tmpdir, domain, version): path = str(tmpdir) class Dummy(chainer.FunctionNode): def forward_cpu(self, inputs): self.x = inputs[0] return np.ones_like(inputs[0]), def backward(self, indexes, grad_outputs): return chainer.Variable(np.zeros_like(self.x)), def dummy_function(x): return Dummy().apply((x, ))[0] model = chainer.Sequential(dummy_function) x = input_generator.increasing(2, 5) def custom_converter(params): return onnx_helper.make_node('Dummy', params.input_names, params.output_names, domain=domain), addon_converters = {'Dummy': custom_converter} # warnings list # 1. `external_converter` is experimental feature # 2. `return_named_inout` which is used internally is experimental feature expected_warning_num = 2 external_opset_imports = {} if domain is not None: external_opset_imports[domain] = version # 3. `external_opset_imports` is experimental feature expected_warning_num += 1 if not onnx_helper.is_support_non_standard_domain(): # 4. ValidationError is ignored expected_warning_num += 1 else: # 3. ValidationError is ignored expected_warning_num += 1 with warnings.catch_warnings(record=True) as w: export_testcase(model, x, path, external_converters=addon_converters, external_opset_imports=external_opset_imports) assert len(w) == expected_warning_num output_path = os.path.join(path, 'test_data_set_0', 'output_0.pb') assert os.path.isfile(output_path) output = onnx.numpy_helper.to_array(onnx.load_tensor(output_path)) expected_output = np.ones_like(x) np.testing.assert_allclose(output, expected_output, rtol=1e-5, atol=1e-5)
def setUp(self): class Model(chainer.Chain): def __init__(self, **kwargs): super(Model, self).__init__() with self.init_scope(): self.bn = L.BatchNormalization(5, **kwargs) def __call__(self, x): return self.bn(x) self.model = Model(**self.kwargs) self.x = input_generator.increasing(2, 5)
def setUp(self): class Model(chainer.Chain): def __init__(self): super(Model, self).__init__() with self.init_scope(): self.prelu = L.PReLU() def __call__(self, x): return self.prelu(x) self.model = Model() self.x = input_generator.increasing(2, 5)
def test_output(self, tmpdir): # first, make expected gradients to temp directory expected_result_path = str(tmpdir) model = self.get_model() x = input_generator.increasing(2, 5) export_testcase(model, x, expected_result_path, output_grad=True) data_set_name = 'test_data_set_0' expected_gradients = [ os.path.join(expected_result_path, data_set_name, 'gradient_{}.pb').format(i) for i in range(2) ] assert all([os.path.isfile(path) for path in expected_gradients]) # model.half returns chainer.Variable and enabled backward # regardless using replacing model.half = fake_as_funcnode(model.half, 'MulConstant') x = input_generator.increasing(2, 5) def gradient_check(model, path): actual_gradients = [ os.path.join(path, data_set_name, 'gradient_{}.pb').format(i) for i in range(2) ] assert all([os.path.isfile(path) for path in actual_gradients]) def load_tensor(path): tensor = onnx.load_tensor(path) return onnx.numpy_helper.to_array(tensor) for e_path, a_path in zip(expected_gradients, actual_gradients): expected = load_tensor(e_path) actual = load_tensor(a_path) np.testing.assert_allclose(expected, actual) self.expect(model, x, output_grad=True, custom_model_test_func=gradient_check)
def test_output(self): n_layers = self.n_layers dropout_ratio = 0.0 batch_size = 3 input_size = 4 hidden_size = 5 seq_length = 6 class Model(chainer.Chain): def __init__(self): super().__init__() def __call__(self, hx, ws1, ws2, ws3, bs, xs): ws = [F.separate(ws1) + F.separate(ws2)] if n_layers > 1: ws.extend([F.separate(w) for w in F.separate(ws3)]) bs = [F.separate(b) for b in F.separate(bs)] xs = F.separate(xs) hy, ys = F.n_step_gru(n_layers, dropout_ratio, hx, ws, bs, xs) return hy, F.stack(ys, axis=0) model = Model() hx = input_generator.increasing(n_layers, batch_size, hidden_size) ws1 = input_generator.increasing(3, hidden_size, input_size) ws2 = input_generator.increasing(3, hidden_size, hidden_size) ws3 = input_generator.increasing(n_layers - 1, 6, hidden_size, hidden_size) bs = input_generator.increasing(n_layers, 6, hidden_size) xs = input_generator.increasing(seq_length, batch_size, input_size) self.expect(model, (hx, ws1, ws2, ws3, bs, xs))
def setUp(self): class Model(chainer.Chain): def __init__(self, ops, args, input_argname): super(Model, self).__init__() self.ops = getattr(F, ops) self.args = args self.input_argname = input_argname def __call__(self, x): self.args[self.input_argname] = x return self.ops(**self.args) self.model = Model(self.ops, self.args, self.input_argname) self.x = input_generator.increasing(*self.input_shape)
def test_export_external_converters_custom_op(tmpdir, domain, version): path = str(tmpdir) class Dummy(chainer.FunctionNode): def forward_cpu(self, inputs): self.x = inputs[0] return np.ones_like(inputs[0]), def backward(self, indexes, grad_outputs): return chainer.Variable(np.zeros_like(self.x)), def dummy_function(x): return Dummy().apply((x, ))[0] model = chainer.Sequential(dummy_function) x = input_generator.increasing(2, 5) def custom_converter(params): return onnx_helper.make_node('Dummy', params.input_names, params.output_names, domain=domain), addon_converters = {'Dummy': custom_converter} external_opset_imports = {} is_set_domain = domain is not None if is_set_domain: external_opset_imports[domain] = version if is_set_domain and onnx_helper.is_support_non_standard_domain(): export_testcase(model, x, path, external_converters=addon_converters, external_opset_imports=external_opset_imports) else: with testing.assert_warns(UserWarning): export_testcase(model, x, path, external_converters=addon_converters, external_opset_imports=external_opset_imports) output_path = os.path.join(path, 'test_data_set_0', 'output_0.pb') assert os.path.isfile(output_path) output = onnx.numpy_helper.to_array(onnx.load_tensor(output_path)) expected_output = np.ones_like(x) np.testing.assert_allclose(output, expected_output, rtol=1e-5, atol=1e-5)
def setUp(self): class Model(chainer.Chain): def __init__(self, ops, args): super(Model, self).__init__() self.ops = ops self.args = args def __call__(self, x): return self.ops(x, **self.args) ops = getattr(F, self.name) args = {} if hasattr(self, 'args'): args = self.args self.model = Model(ops, args) self.x = input_generator.increasing(2, 5, 3)
def setUp(self): class Model(chainer.Chain): def __init__(self, link, args, kwargs): super(Model, self).__init__() with self.init_scope(): self.l1 = link(*args, **kwargs) def __call__(self, x): return self.l1(x) self.model = Model(self.link, self.args, self.kwargs) if self.link is L.EmbedID: self.x = np.random.randint(0, self.args[0], size=self.in_shape) self.x = self.x.astype(self.in_type) else: self.x = input_generator.increasing(*self.in_shape, dtype=self.in_type)
def test_fake_as_funcnode_without_replace(): class Model(chainer.Chain): def _init__(self): super().__init__() def add(self, xs, value=0.01): return xs.array + value def __call__(self, xs): return F.sigmoid(self.add(xs)) model = Model() x = input_generator.increasing(3, 4) with pytest.raises(onnx.checker.ValidationError): export(model, x)
def test_replace_func_collection_return(tmpdir, return_type): path = str(tmpdir) class Model(chainer.Chain): def __init__(self, return_type): super().__init__() self.return_type = return_type def tiled_array(self, xs, n=5): if self.return_type == 'list': return [xs.array * i for i in range(1, 1+n)] else: assert self.return_type == 'dict' return {str(i): xs.array * i for i in range(1, 1+n)} def __call__(self, xs): return self.tiled_array(xs) model = Model(return_type) x = input_generator.increasing(1, 5) with warnings.catch_warnings(record=True): model.tiled_array = fake_as_funcnode(model.tiled_array, 'xTiledArray') def tiled_array_converter(params): return onnx_helper.make_node( 'xTiledArray', params.input_names, params.output_names), addon_converters = {'xTiledArray': tiled_array_converter} with testing.assert_warns(UserWarning): export_testcase(model, x, path, external_converters=addon_converters) model_filepath = os.path.join(path, 'model.onnx') assert os.path.isfile(model_filepath) onnx_model = onnx.load(model_filepath) node_names = [n.name for n in onnx_model.graph.node] assert len(node_names) == 1 assert node_names[0] == 'xTiledArray_0' output_names = [n.name for n in onnx_model.graph.output] assert len(output_names) == 5 for i, name in enumerate(output_names): assert name == 'xTiledArray_0_{:d}'.format(i)
def test_output(self): class Model(chainer.Chain): def __init__(self, ops, kwargs): super(Model, self).__init__() self.ops = getattr(F, ops) self.kwargs = kwargs def __call__(self, *xs): return self.ops(xs, **self.kwargs) model = Model(ops=self.ops, kwargs=self.kwargs) if hasattr(self, 'inputs'): xs = [np.array(value, dtype=np.float32) for value in self.inputs] else: xs = [input_generator.increasing(*shape) for shape in self.in_shapes] self.expect(model, xs, name=self.name)
def test_output(self): from onnx_chainer.replace_func import as_funcnode class Model(chainer.Chain): def __init__(self): super().__init__() @as_funcnode('Shape') def shape(self, x): # ONNX Shape operator constrains to return int64 type return np.array(x.shape, dtype=np.int64) def forward(self, x): # use shape method instead of x.shape to connect graph. return self.shape(x) model = Model() x = input_generator.increasing(3, 4, 5) self.expect(model, (x,))
def test_fake_as_funcnode_without_replace(): class Model(chainer.Chain): def _init__(self): super().__init__() def add(self, xs, value=0.01): return xs.array + value def __call__(self, xs): return F.sigmoid(self.add(xs)) model = Model() x = input_generator.increasing(3, 4) onnx_model = export(model, x) sigmoid_nodes = [ node for node in onnx_model.graph.node if node.op_type == 'Sigmoid'] assert len(sigmoid_nodes) == 1 # sigmoid node should be expected to connect with input # but the connection is cut because `add` method takes array. assert not sigmoid_nodes[0].input[0] == 'Input_0'
def test_fake_as_funcnode_keep_structure(tmpdir): path = str(tmpdir) class Model(chainer.Chain): def __init__(self): super().__init__() def f(self, x): return {'a': (x, x+1), 'b': [x+2, x+3, x+4]} def __call__(self, x): ret = self.f(x) return ret['a'][0] + ret['b'][1] model = Model() x = input_generator.increasing(2, 3) with warnings.catch_warnings(record=True): model.f = fake_as_funcnode(model.f, 'xF') def f_converter(params): return onnx_helper.make_node( 'xF', params.input_names, params.output_names), addon_converters = {'xF': f_converter} with testing.assert_warns(UserWarning): export_testcase(model, x, path, external_converters=addon_converters) export_testcase(model, x, ".", external_converters=addon_converters) model_filepath = os.path.join(path, 'model.onnx') assert os.path.isfile(model_filepath) onnx_model = onnx.load(model_filepath) node_names = [n.name for n in onnx_model.graph.node] assert len(node_names) == 2 assert node_names[0] == 'xF_0' assert len(onnx_model.graph.node[0].output) == 5 assert len(onnx_model.graph.output) == 1