pytorch
422 строки · 17.1 Кб
1
2
3
4
5
6import unittest7
8from hypothesis import given, assume, settings9import hypothesis.strategies as st10import numpy as np11import operator12
13from caffe2.proto import caffe2_pb214from caffe2.python import core, workspace15import caffe2.python.hypothesis_test_util as hu16import caffe2.python.serialized_test.serialized_test_util as serial17
18
19# TODO(jiayq): make them hypothesis tests for better coverage.
20class TestElementwiseBroadcast(serial.SerializedTestCase):21
22def __generate_test_cases(self, allow_broadcast_fastpath: bool):23"""24generates a set of test cases
25
26For each iteration, generates X, Y, args, X_out, Y_out
27where
28X, Y are test input tensors
29args is a dictionary of arguments to be passed to
30core.CreateOperator()
31X_out, Y_out are reshaped versions of X and Y
32which can be used to calculate the expected
33result with the operator to be tested
34"""
35# Set broadcast and no axis, i.e. broadcasting last dimensions.36X = np.random.rand(2, 3, 4, 5).astype(np.float32)37Y = np.random.rand(4, 5).astype(np.float32)38args = dict(broadcast=1, allow_broadcast_fastpath=allow_broadcast_fastpath)39yield X, Y, args, X, Y40
41# broadcasting intermediate dimensions42X = np.random.rand(2, 3, 4, 5).astype(np.float32)43Y = np.random.rand(3, 4).astype(np.float32)44args = dict(broadcast=1, axis=1, allow_broadcast_fastpath=allow_broadcast_fastpath)45yield X, Y, args, X, Y[:, :, np.newaxis]46
47# broadcasting the first dimension48X = np.random.rand(2, 3, 4, 5).astype(np.float32)49Y = np.random.rand(2).astype(np.float32)50args = dict(broadcast=1, axis=0, allow_broadcast_fastpath=allow_broadcast_fastpath)51yield X, Y, args, X, Y[:, np.newaxis, np.newaxis, np.newaxis]52
53# broadcasting with single elem dimensions at both ends54X = np.random.rand(2, 3, 4, 5).astype(np.float32)55Y = np.random.rand(1, 4, 1).astype(np.float32)56args = dict(broadcast=1, axis=1, allow_broadcast_fastpath=allow_broadcast_fastpath)57yield X, Y, args, X, Y58
59def __test_binary_op(60self, gc, dc, caffe2_op, op_function, allow_broadcast_fastpath: bool = False61):62"""63Args:
64caffe2_op: A string. Name of the caffe operator to test.
65op_function: an actual python operator (e.g. operator.add)
66path_prefix: A string. Optional param used to construct db name or path
67where checkpoint files are stored.
68"""
69
70for X, Y, op_args, X_out, Y_out in self.__generate_test_cases(allow_broadcast_fastpath):71op = core.CreateOperator(caffe2_op, ["X", "Y"], "out", **op_args)72workspace.FeedBlob("X", X)73workspace.FeedBlob("Y", Y)74workspace.RunOperatorOnce(op)75out = workspace.FetchBlob("out")76np.testing.assert_array_almost_equal(out, op_function(X_out, Y_out))77self.assertDeviceChecks(dc, op, [X, Y], [0])78self.assertGradientChecks(gc, op, [X, Y], 1, [0])79
80@given(allow_broadcast_fastpath=st.booleans(), **hu.gcs)81@settings(deadline=None)82def test_broadcast_Add(self, allow_broadcast_fastpath: bool, gc, dc):83self.__test_binary_op(84gc, dc, "Add", operator.add, allow_broadcast_fastpath=allow_broadcast_fastpath85)86
87@given(allow_broadcast_fastpath=st.booleans(), **hu.gcs)88@settings(deadline=None)89def test_broadcast_Mul(self, allow_broadcast_fastpath: bool, gc, dc):90self.__test_binary_op(91gc, dc, "Mul", operator.mul, allow_broadcast_fastpath=allow_broadcast_fastpath92)93
94@given(allow_broadcast_fastpath=st.booleans(), **hu.gcs)95@settings(deadline=None)96def test_broadcast_Sub(self, allow_broadcast_fastpath: bool, gc, dc):97self.__test_binary_op(98gc, dc, "Sub", operator.sub, allow_broadcast_fastpath=allow_broadcast_fastpath99)100
101@given(**hu.gcs)102@settings(deadline=None)103def test_broadcast_powt(self, gc, dc):104np.random.seed(101)105
106#operator107def powt_op(X, Y):108return [np.power(X, Y)]109
110#two gradients Y*X^(Y-1) and X^Y * ln(X)111def powt_grad(g_out, outputs, fwd_inputs):112[X, Y] = fwd_inputs113Z = outputs[0]114return ([Y * np.power(X, Y - 1), Z * np.log(X)] * g_out)115
116#1. Set broadcast and no axis, i.e. broadcasting last dimensions.117X = np.random.rand(2, 3, 4, 5).astype(np.float32) + 1.0118Y = np.random.rand(4, 5).astype(np.float32) + 2.0119
120#two gradients Y*X^(Y-1) and X^Y * ln(X)121#latter gradient is summed over 1 and 0 dims to account for broadcast122def powt_grad_broadcast(g_out, outputs, fwd_inputs):123[GX, GY] = powt_grad(g_out, outputs, fwd_inputs)124return ([GX, np.sum(np.sum(GY, 1), 0)])125
126op = core.CreateOperator("Pow", ["X", "Y"], "Z", broadcast=1)127self.assertReferenceChecks(device_option=gc,128op=op,129inputs=[X, Y],130reference=powt_op,131output_to_grad="Z",132grad_reference=powt_grad_broadcast)133
134#2. broadcasting intermediate dimensions135X = np.random.rand(2, 3, 4, 5).astype(np.float32) + 1.0136Y = np.random.rand(3, 4).astype(np.float32) + 2.0137
138#pow op with the latter array increased by one dim139def powt_op_axis1(X, Y):140return powt_op(X, Y[:, :, np.newaxis])141
142#two gradients Y*X^(Y-1) and X^Y * ln(X)143#latter gradient is summed over 3 and 0 dims to account for broadcast144def powt_grad_axis1(g_out, outputs, fwd_inputs):145[X, Y] = fwd_inputs146[GX, GY] = powt_grad(g_out, outputs, [X, Y[:, :, np.newaxis]])147return ([GX, np.sum(np.sum(GY, 3), 0)])148
149op = core.CreateOperator("Pow", ["X", "Y"], "Z", broadcast=1, axis=1)150self.assertReferenceChecks(device_option=gc,151op=op,152inputs=[X, Y],153reference=powt_op_axis1,154output_to_grad="Z",155grad_reference=powt_grad_axis1)156
157#3. broadcasting the first dimension158X = np.random.rand(2, 3, 4, 5).astype(np.float32) + 1.0159Y = np.random.rand(2).astype(np.float32) + 2.0160
161#pow op with the latter array increased by one dim162def powt_op_axis0(X, Y):163return powt_op(X, Y[:, np.newaxis, np.newaxis, np.newaxis])164
165#two gradients Y*X^(Y-1) and X^Y * ln(X)166#latter gradient is summed over 3, 2 and 1 dims to account for broadcast167def powt_grad_axis0(g_out, outputs, fwd_inputs):168[X, Y] = fwd_inputs169[GX, GY] = powt_grad(g_out,170outputs,171[X, Y[:, np.newaxis, np.newaxis, np.newaxis]])172return ([GX, np.sum(np.sum(np.sum(GY, 3), 2), 1)])173
174op = core.CreateOperator("Pow", ["X", "Y"], "Z", broadcast=1, axis=0)175self.assertReferenceChecks(device_option=gc,176op=op,177inputs=[X, Y],178reference=powt_op_axis0,179output_to_grad="Z",180grad_reference=powt_grad_axis0)181
182#4. broadcasting with single elem dimensions at both ends183X = np.random.rand(2, 3, 4, 5).astype(np.float32) + 1.0184Y = np.random.rand(1, 4, 1).astype(np.float32) + 2.0185
186#pow op with the latter array increased by one dim187def powt_op_mixed(X, Y):188return powt_op(X, Y[np.newaxis, :, :, :])189
190#two gradients Y*X^(Y-1) and X^Y * ln(X)191#latter gradient is summed over 0 and 1 dims to account for broadcast192def powt_grad_mixed(g_out, outputs, fwd_inputs):193[X, Y] = fwd_inputs194[GX, GY] = powt_grad(g_out, outputs, [X, Y[np.newaxis, :, :, :]])195return ([GX, np.reshape(np.sum(np.sum(np.sum(GY, 3), 1), 0),196(1, 4, 1))])197
198op = core.CreateOperator("Pow", ["X", "Y"], "Z", broadcast=1, axis=1)199self.assertReferenceChecks(device_option=gc,200op=op,201inputs=[X, Y],202reference=powt_op_mixed,203output_to_grad="Z",204grad_reference=powt_grad_mixed)205
206@given(allow_broadcast_fastpath=st.booleans(), **hu.gcs)207def test_broadcast_scalar(self, allow_broadcast_fastpath: bool, gc, dc):208# broadcasting constant209X = np.random.rand(2, 3, 4, 5).astype(np.float32)210Y = np.random.rand(1).astype(np.float32)211op = core.CreateOperator(212"Add", ["X", "Y"], "out", broadcast=1, allow_broadcast_fastpath=allow_broadcast_fastpath213)214workspace.FeedBlob("X", X)215workspace.FeedBlob("Y", Y)216workspace.RunOperatorOnce(op)217out = workspace.FetchBlob("out")218np.testing.assert_array_almost_equal(219out, X + Y)220self.assertDeviceChecks(dc, op, [X, Y], [0])221
222# broadcasting scalar223X = np.random.rand(1).astype(np.float32)224Y = np.random.rand(1).astype(np.float32).reshape([])225op = core.CreateOperator(226"Add", ["X", "Y"], "out", broadcast=1, allow_broadcast_fastpath=allow_broadcast_fastpath227)228workspace.FeedBlob("X", X)229workspace.FeedBlob("Y", Y)230workspace.RunOperatorOnce(op)231out = workspace.FetchBlob("out")232np.testing.assert_array_almost_equal(233out, X + Y)234self.assertDeviceChecks(dc, op, [X, Y], [0])235
236@given(allow_broadcast_fastpath=st.booleans(), **hu.gcs)237def test_semantic_broadcast(self, allow_broadcast_fastpath: bool, gc, dc):238# NCHW as default239X = np.random.rand(2, 3, 4, 5).astype(np.float32)240Y = np.random.rand(3).astype(np.float32)241op = core.CreateOperator(242"Add", ["X", "Y"], "out", broadcast=1, axis_str="C",243allow_broadcast_fastpath=allow_broadcast_fastpath)244workspace.FeedBlob("X", X)245workspace.FeedBlob("Y", Y)246workspace.RunOperatorOnce(op)247out = workspace.FetchBlob("out")248np.testing.assert_array_almost_equal(249out, X + Y[:, np.newaxis, np.newaxis])250self.assertDeviceChecks(dc, op, [X, Y], [0])251
252# NHWC253X = np.random.rand(2, 3, 4, 5).astype(np.float32)254Y = np.random.rand(5).astype(np.float32)255op = core.CreateOperator(256"Add", ["X", "Y"], "out", broadcast=1, axis_str="C", order="NHWC",257allow_broadcast_fastpath=allow_broadcast_fastpath)258workspace.FeedBlob("X", X)259workspace.FeedBlob("Y", Y)260workspace.RunOperatorOnce(op)261out = workspace.FetchBlob("out")262np.testing.assert_array_almost_equal(out, X + Y)263self.assertDeviceChecks(dc, op, [X, Y], [0])264
265@given(**hu.gcs)266def test_sum_reduce_empty_blob(self, gc, dc):267net = core.Net('test')268
269with core.DeviceScope(gc):270net.GivenTensorFill([], ["X"], values=[], shape=[2, 0, 5])271net.GivenTensorFill([], ["Y"], values=[], shape=[2, 0])272net.SumReduceLike(["X", "Y"], "out", axis=0)273workspace.RunNetOnce(net)274
275@given(**hu.gcs)276def test_sum_reduce(self, gc, dc):277# Set broadcast and no axis, i.e. broadcasting last dimensions.278X = np.random.rand(2, 3, 4, 5).astype(np.float32)279Y = np.random.rand(4, 5).astype(np.float32)280op = core.CreateOperator(281"SumReduceLike", ["X", "Y"], "out", broadcast=1)282workspace.FeedBlob("X", X)283workspace.FeedBlob("Y", Y)284workspace.RunOperatorOnce(op)285out = workspace.FetchBlob("out")286res = np.sum(X, axis=0)287res = np.sum(res, axis=0)288np.testing.assert_array_almost_equal(out, res)289self.assertDeviceChecks(dc, op, [X, Y], [0])290
291# Set broadcast and no axis, i.e. broadcasting last dimensions.292X = np.random.rand(2, 3, 4, 5).astype(np.float32)293Y = np.random.rand(2, 3).astype(np.float32)294op = core.CreateOperator(295"SumReduceLike", ["X", "Y"], "out", broadcast=1, axis=0)296workspace.FeedBlob("X", X)297workspace.FeedBlob("Y", Y)298workspace.RunOperatorOnce(op)299out = workspace.FetchBlob("out")300res = np.sum(X, axis=3)301res = np.sum(res, axis=2)302np.testing.assert_array_almost_equal(out, res, decimal=3)303self.assertDeviceChecks(dc, op, [X, Y], [0])304
305# broadcasting intermediate dimensions306X = np.random.rand(2, 3, 4, 5).astype(np.float32)307Y = np.random.rand(3, 4).astype(np.float32)308op = core.CreateOperator(309"SumReduceLike", ["X", "Y"], "out", broadcast=1, axis=1)310workspace.FeedBlob("X", X)311workspace.FeedBlob("Y", Y)312workspace.RunOperatorOnce(op)313out = workspace.FetchBlob("out")314res = np.sum(X, axis=0)315res = np.sum(res, axis=2)316np.testing.assert_array_almost_equal(out, res)317self.assertDeviceChecks(dc, op, [X, Y], [0])318
319# broadcasting intermediate dimensions320X = np.random.rand(2, 3, 4, 500).astype(np.float64)321Y = np.random.rand(1).astype(np.float64)322op = core.CreateOperator(323"SumReduceLike", ["X", "Y"], "out", broadcast=1)324workspace.FeedBlob("X", X)325workspace.FeedBlob("Y", Y)326workspace.RunOperatorOnce(op)327out = workspace.FetchBlob("out")328res = np.array(np.sum(X))329np.testing.assert_array_almost_equal(out, res, decimal=0)330
331# broadcasting with single elem dimensions at both ends332X = np.random.rand(2, 3, 4, 5).astype(np.float32)333Y = np.random.rand(1, 3, 4, 1).astype(np.float32)334op = core.CreateOperator(335"SumReduceLike", ["X", "Y"], "out", broadcast=1)336workspace.FeedBlob("X", X)337workspace.FeedBlob("Y", Y)338workspace.RunOperatorOnce(op)339out = workspace.FetchBlob("out")340res = np.sum(X, axis=0)341res = np.sum(res, axis=2).reshape(Y.shape)342np.testing.assert_array_almost_equal(out, res)343self.assertDeviceChecks(dc, op, [X, Y], [0])344
345# fp64 is not supported with the CUDA op346dc_cpu_only = [d for d in dc if d.device_type != caffe2_pb2.CUDA]347self.assertDeviceChecks(dc_cpu_only, op, [X, Y], [0])348
349@unittest.skipIf(not workspace.has_gpu_support, "No gpu support")350@given(**hu.gcs)351def test_sum_reduce_fp16(self, gc, dc):352assume(core.IsGPUDeviceType(gc.device_type))353
354# Set broadcast and no axis, i.e. broadcasting last dimensions.355X = np.random.rand(2, 3, 4, 5).astype(np.float16)356Y = np.random.rand(4, 5).astype(np.float16)357op = core.CreateOperator(358"SumReduceLike", ["X", "Y"], "out", broadcast=1, device_option=gc)359
360def ref_op(X, Y):361res = np.sum(X, axis=0)362res = np.sum(res, axis=0)363return [res]364
365self.assertReferenceChecks(366device_option=gc,367op=op,368inputs=[X, Y],369reference=ref_op,370threshold=1e-3)371
372# Set broadcast and no axis, i.e. broadcasting last dimensions.373X = np.random.rand(2, 3, 4, 5).astype(np.float16)374Y = np.random.rand(2, 3).astype(np.float16)375op = core.CreateOperator(376"SumReduceLike", ["X", "Y"], "out", broadcast=1, axis=0)377
378def ref_op(X, Y):379res = np.sum(X, axis=3)380res = np.sum(res, axis=2)381return [res]382
383self.assertReferenceChecks(384device_option=gc,385op=op,386inputs=[X, Y],387reference=ref_op,388threshold=1e-3)389
390# broadcasting intermediate dimensions391X = np.random.rand(2, 3, 4, 5).astype(np.float16)392Y = np.random.rand(3, 4).astype(np.float16)393op = core.CreateOperator(394"SumReduceLike", ["X", "Y"], "out", broadcast=1, axis=1)395
396def ref_op(X, Y):397res = np.sum(X, axis=0)398res = np.sum(res, axis=2)399return [res]400
401self.assertReferenceChecks(402device_option=gc,403op=op,404inputs=[X, Y],405reference=ref_op,406threshold=1e-3)407
408# broadcasting with single elem dimensions at both ends409X = np.random.rand(2, 3, 4, 5).astype(np.float16)410Y = np.random.rand(1, 3, 4, 1).astype(np.float16)411op = core.CreateOperator(412"SumReduceLike", ["X", "Y"], "out", broadcast=1)413
414def ref_op(X, Y):415res = np.sum(X, axis=0)416res = np.sum(res, axis=2)417return [res.reshape(Y.shape)]418
419self.assertReferenceChecks(420device_option=gc,421op=op,422inputs=[X, Y],423reference=ref_op,424threshold=1e-3)425
426if __name__ == "__main__":427unittest.main()428