pytorch/caffe2/python/operator_test/shape_inference_test.py
Hector Yuen 17ab3bd502 implement rowwise quantization for fp16 (#12382)
Summary:
Pull Request resolved: https://github.com/pytorch/pytorch/pull/12382

implement fp16-> (uint8 + scale and bias in fp32)

this is similar to fp32 rowwise quantization

we could have done scale and bias in fp16 but not too motivated since we are not saving much and those datatypes have to be converted to fp32 to process since x86 doesn't support half float operations anyways

Reviewed By: csummersea

Differential Revision: D10220463

fbshipit-source-id: 6c382026de881f03798c2e5fc43abfc80f84ea1f
2018-10-12 13:57:55 -07:00

628 lines
24 KiB
Python

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import numpy as np
import unittest
from caffe2.proto import caffe2_pb2
from caffe2.python import core, workspace, test_util, model_helper, brew, build
@unittest.skipIf(build.CAFFE2_NO_OPERATOR_SCHEMA,
'Built with CAFFE2_NO_OPERATOR_SCHEMA')
class TestShapeInference(test_util.TestCase):
def testShapeInferenceSimpleFC(self):
m = model_helper.ModelHelper(name="test_model")
brew.fc(m, "data", "fc1", dim_in=96, dim_out=32)
brew.fc(m, "fc1", "fc2", dim_in=32, dim_out=55)
for b in [0, 64]:
(shapes, types) = workspace.InferShapesAndTypes(
[m.param_init_net, m.net],
{'data': [b, 96]}
)
self.assertEquals(shapes['data'], [b, 96])
self.assertEquals(shapes['fc1_w'], [32, 96])
self.assertEquals(shapes['fc1_b'], [32])
self.assertEquals(shapes['fc1'], [b, 32])
self.assertEquals(shapes['fc2_w'], [55, 32])
self.assertEquals(shapes['fc2_b'], [55])
self.assertEquals(shapes['fc2'], [b, 55])
def testFCAxis2(self):
model = model_helper.ModelHelper(name="test_model")
model.net.FC(["x", "w", "b"], ["y"], axis=2)
workspace.FeedBlob("x", np.random.rand(4, 20, 36).astype(np.float32))
workspace.FeedBlob("w", np.random.rand(36, 36).astype(np.float32))
workspace.FeedBlob("b", np.random.rand(36,).astype(np.float32))
self.InferTensorRunAndCompare(model)
def testFCTransposed(self):
model = model_helper.ModelHelper(name="test_model")
model.net.FCTransposed(["x", "wt", "b"], ["y"])
workspace.FeedBlob("x", np.random.rand(20, 36).astype(np.float32))
workspace.FeedBlob("wt", np.random.rand(36, 48).astype(np.float32))
workspace.FeedBlob("b", np.random.rand(48,).astype(np.float32))
self.InferTensorRunAndCompare(model)
def testShapeInferenceSlice(self):
model = model_helper.ModelHelper(name="test_model")
model.net.Slice(["x"], ["y"], starts=[0, 0, 0, 0], ends=[-1, -1, -3, -1])
workspace.FeedBlob("x", np.random.rand(64, 1, 255, 384).astype(np.float32))
slice_starts = np.array([0, 0, 0, 0]).astype(np.int32)
slice_ends = np.array([-1, -1, -3, -1]).astype(np.int32)
slice_starts = model.net.GivenTensorIntFill(
[], shape=[4], values=slice_starts)
slice_ends = model.net.GivenTensorIntFill(
[], shape=[4], values=slice_ends)
model.net.Slice(["x2", slice_starts, slice_ends], ["y2"])
workspace.FeedBlob("x2", np.random.rand(64, 1, 255, 384).astype(np.float32))
self.InferTensorRunAndCompare(model, ["y2"])
def testShapeInferenceDistances(self):
model = model_helper.ModelHelper(name="test_model")
model.net.L1Distance(["x1", "y1"], "dl1_D1")
model.net.SquaredL2Distance(["x1", "y1"], "dl2_D1")
model.net.CosineSimilarity(["x1", "y1"], "dcos_D1")
model.net.DotProduct(["x1", "y1"], "ddot_D1")
model.net.DotProductWithPadding(["x1", "y1"], "ddotpad_D1")
model.net.L1Distance(["x2", "y2"], "dl1_D2")
model.net.SquaredL2Distance(["x2", "y2"], "dl2_D2")
model.net.CosineSimilarity(["x2", "y2"], "dcos_D2")
model.net.DotProduct(["x2", "y2"], "ddot_D2")
model.net.DotProductWithPadding(["x2", "z2"], "ddotpad_D2")
workspace.FeedBlob("x1", np.random.rand(10).astype(np.float32))
workspace.FeedBlob("y1", np.random.rand(10).astype(np.float32))
workspace.FeedBlob("x2", np.random.rand(10, 5).astype(np.float32))
workspace.FeedBlob("y2", np.random.rand(10, 5).astype(np.float32))
workspace.FeedBlob("z2", np.random.rand(10, 4).astype(np.float32))
self.InferTensorRunAndCompare(model)
def testShapeInferenceReduceBackFrontX(self):
model = model_helper.ModelHelper(name="test_model")
model.net.ReduceBackSum(["x"], ["x_back_sum"])
model.net.ReduceBackMean(["x"], ["x_back_mean"])
model.net.ReduceBackMax(["x"], ["x_back_max"])
model.net.ReduceFrontSum(["x"], ["x_front_sum"])
model.net.ReduceFrontMean(["x"], ["x_front_mean"])
model.net.ReduceFrontMax(["x"], ["x_front_max"])
workspace.FeedBlob("x", np.random.rand(10, 12, 18).astype(np.float32))
self.InferTensorRunAndCompare(model)
def testGather(self):
model = model_helper.ModelHelper(name="test_model")
model.net.Gather(["X", "idx"], "Y")
workspace.FeedBlob("X", np.random.rand(100, 4, 5).astype(np.float32))
workspace.FeedBlob("idx", np.array([[3, 18], [99, 4], [2, 5]]).astype(np.int32))
self.InferTensorRunAndCompare(model)
def testShapeInferenceConvNet(self):
model = model_helper.ModelHelper(name="convtest")
model.NHWC2NCHW("data", "data_nchw")
brew.conv(model, "data_nchw", 'conv1', 3, 64,
weight_init=("MSRAFill", {}), kernel=7,
stride=2, pad=3, no_bias=0)
brew.spatial_bn(model, 'conv1', 'conv1_spatbn_relu', 64, epsilon=1e-3, is_test=False)
brew.relu(model, 'conv1_spatbn_relu', 'conv1_spatbn_relu')
brew.max_pool(model, 'conv1_spatbn_relu', 'pool1', kernel=3, stride=2)
brew.fc(model, 'pool1', 'fc', dim_in=(64 * 56 * 56), dim_out=100)
brew.dropout(model, 'fc', 'fc_drop', is_test=False)
model.Sigmoid('fc_drop', 'fc_sigm')
brew.softmax(model, 'fc_sigm', 'softmax')
model.LabelCrossEntropy(['softmax', 'label'], 'xent')
loss = model.AveragedLoss('xent', 'loss')
model.AddGradientOperators([loss])
LR = model.param_init_net.ConstantFill(
[], 'LR', shape=[1], value=0.1
)
for param in model.GetParams():
param_grad = model.param_to_grad[param]
param_momentum = model.param_init_net.ConstantFill(
[param], param + '_momentum', value=0.0
)
model.net.MomentumSGDUpdate(
[param_grad, param_momentum, LR, param],
[param_grad, param_momentum, param],
)
workspace.FeedBlob(
"data",
np.random.rand(16, 227, 227, 3).astype(np.float32),
)
workspace.FeedBlob(
"label",
(100 * np.random.rand(16)).astype(np.int32),
)
workspace.FeedBlob(
"label",
(100 * np.random.rand(16)).astype(np.int32),
)
# Then do automatic comparison test: run the next once to
# initialize everything
self.InferTensorRunAndCompare(model)
def testShapeInferenceTranspose(self):
model = model_helper.ModelHelper(name="test_model")
workspace.FeedBlob(
"tensor",
np.random.rand(4, 2, 3, 3, 5).astype(np.float32)
)
# Testing with axes undefined
brew.transpose(
model,
["tensor"],
"transpose",
)
self.InferTensorRunAndCompare(model)
# Testing with axes defined
brew.transpose(
model,
["tensor"],
"transpose",
axes=np.random.permutation(5)
)
return self.InferTensorRunAndCompare(model)
def testShapeInferencePad(self):
model = model_helper.ModelHelper(name="padtest")
model.PadImage("data", 'padded', pad_t=100, pad_l=37, pad_b=28,
pad_r=20, mode="constant", order="NCHW")
workspace.FeedBlob(
"data",
np.random.rand(16, 3, 228, 228).astype(np.float32),
)
self.InferTensorRunAndCompare(model)
def testShapeInferenceTwoClass(self):
model = model_helper.ModelHelper(name="twoclass")
model.MakeTwoClass("v", "v2")
workspace.FeedBlob("v", np.random.rand(32).astype(np.float32))
self.InferTensorRunAndCompare(model)
def testShapeInferencePadZero(self):
model = model_helper.ModelHelper(name="padtest")
model.PadImage("data", 'padded', pad=0, mode="constant",
order="NCHW")
workspace.FeedBlob(
"data",
np.random.rand(16, 3, 228, 228).astype(np.float32),
)
self.InferTensorRunAndCompare(model)
def testShapeInferenceMatMul(self):
model = model_helper.ModelHelper(name="test_model")
model.MatMul(["x", "y"], "MatMul")
workspace.FeedBlob("x", np.random.rand(10, 5).astype(np.float32))
workspace.FeedBlob("y", np.random.rand(5, 10).astype(np.float32))
self.InferTensorRunAndCompare(model)
def testShapeInferenceSoftmaxWithLoss(self):
model = model_helper.ModelHelper(name="test_model")
model.SoftmaxWithLoss(
["logits", "labels"],
["softmax", "loss"],
)
# 2D Shape of [batch_size, num_classes]
workspace.FeedBlob(
"logits",
np.random.rand(4, 3).astype(np.float32),
)
# Shape of size batch_size with all values [0, num_classes)
workspace.FeedBlob(
"labels",
np.random.randint(low=0, high=3, size=(4, 1)).astype(np.int32),
)
self.InferTensorRunAndCompare(model)
# Testing with 1D labels arg
workspace.FeedBlob(
"logits",
np.random.rand(4, 3).astype(np.float32),
)
workspace.FeedBlob(
"labels",
np.random.randint(low=0, high=3, size=4).astype(np.int32),
)
self.InferTensorRunAndCompare(model)
# Testing with weight_tensor
model.SoftmaxWithLoss(
["logits", "labels", "weight_tensor"],
["softmax", "loss"],
)
workspace.FeedBlob(
"logits",
np.random.rand(4, 3).astype(np.float32),
)
workspace.FeedBlob(
"labels",
np.random.randint(low=0, high=3, size=4).astype(np.int32),
)
workspace.FeedBlob(
"weight_tensor",
np.random.rand(4).astype(np.float32),
)
self.InferTensorRunAndCompare(model)
# Test spatial model
model = model_helper.ModelHelper(name="test_model")
workspace.FeedBlob(
"img",
np.random.rand(32, 19, 33, 28).astype(np.float32)
)
workspace.FeedBlob(
"img_labels",
(np.random.rand(32, 33, 28) * 19).astype(np.int32)
)
model.SpatialSoftmaxWithLoss(
["img", "img_labels"],
["softmax_img", "loss"],
)
self.InferTensorRunAndCompare(model)
def testShapeInferenceIm2Col(self):
# Test with NCHW
model = model_helper.ModelHelper(name="test_model")
model.Im2Col("X", "Y", pad=1, kernel=4, dilation=2, stride=2,
order="NCHW")
workspace.FeedBlob(
"X",
np.random.rand(16, 3, 228, 228).astype(np.float32),
)
self.InferTensorRunAndCompare(model)
# Test with NHWC
model = model_helper.ModelHelper(name="test_model")
model.Im2Col("X", "Y", pad=1, kernel=4, dilation=2, stride=2,
order="NHWC")
workspace.FeedBlob(
"X",
np.random.rand(16, 228, 228, 3).astype(np.float32),
)
self.InferTensorRunAndCompare(model)
# Test with different width and height
model = model_helper.ModelHelper(name="test_model")
model.Im2Col("X", "Y", pad=1, kernel_h=8, kernel_w=4,
dilation=2, stride=2)
workspace.FeedBlob(
"X",
np.random.rand(16, 3, 228, 114).astype(np.float32),
)
self.InferTensorRunAndCompare(model)
def testShapeInferenceTile(self):
m = model_helper.ModelHelper(name="test_model")
workspace.FeedBlob(
"tensor",
np.random.rand(4, 2, 3, 3, 5).astype(np.float32)
)
# Testing with axes undefined
for i in range(0, 4):
m.net.Tile(
"tensor", "tiled_tensor_{}".format(i), tiles=5, axis=i)
self.InferTensorRunAndCompare(m)
def testShapeInferenceFlatten(self):
model = model_helper.ModelHelper(name="test_model")
model.FlattenToVec("X", "FlatVec")
model.FlattenToVec("empty", "EmptyFlatVec")
workspace.FeedBlob("X", np.random.rand(17, 5, 13).astype(np.float32))
workspace.FeedBlob("empty", np.random.rand(0, 2, 3).astype(np.float32))
self.InferTensorRunAndCompare(model)
# test Flatten with default axis (=1)
model = model_helper.ModelHelper(name="test_model")
model.Flatten("X", "Flat")
model.Flatten("empty", "EmptyFlat")
workspace.FeedBlob("X", np.random.rand(17, 5, 13).astype(np.float32))
workspace.FeedBlob("empty", np.random.rand(0, 2, 3).astype(np.float32))
self.InferTensorRunAndCompare(model)
# test Flatten with axis
model = model_helper.ModelHelper(name="test_model")
x = np.random.randn(17, 5, 13)
for axis in range(x.ndim + 1):
model.Flatten("x", "Flat", axis=axis)
workspace.FeedBlob("x", x)
self.InferTensorRunAndCompare(model)
empty = np.random.randn(0, 5, 13)
for axis in range(empty.ndim + 1):
model.Flatten("empty", "Flat", axis=axis)
workspace.FeedBlob("empty", empty)
self.InferTensorRunAndCompare(model)
def testShapeInferenceReshape(self):
model = model_helper.ModelHelper(name="test_model")
model.Reshape("X", ["Reshaped", "Old_Shape"], shape=[8, 0, -1, 2])
workspace.FeedBlob("X", np.random.rand(4, 26, 32).astype(np.float32))
self.InferTensorRunAndCompare(model)
def testShapeInferenceUnique(self):
for n in [0, 1]:
model = model_helper.ModelHelper(name="test_model")
model.Unique("X", ["Y"])
model.Unique("X", ["Z", "remap"])
workspace.FeedBlob("X", np.random.rand(n).astype(np.int64))
self.InferTensorRunAndCompare(model)
def testLengthsSum(self):
model = model_helper.ModelHelper(name="test_model")
model.LengthsSum(["X", "length"], ["sum"])
workspace.FeedBlob("X", np.random.rand(6, 32).astype(np.float32))
workspace.FeedBlob("length", np.array([1, 2, 3], dtype=np.int32))
self.InferTensorRunAndCompare(model)
def testLengthsPad(self):
model = model_helper.ModelHelper(name="test_model")
model.LengthsPad(
["X", "length"],
["X_padded"],
target_length=10,
padding_value=-1.0,
)
workspace.FeedBlob("X", np.random.rand(6, 32).astype(np.float32))
workspace.FeedBlob("length", np.array([1, 2, 3], dtype=np.int32))
self.InferTensorRunAndCompare(model)
def testConcat(self):
net = core.Net("concat")
net.Concat(["A", "B"], ["C", "splits"], axis=1)
net.Concat(["C", "D"], ["E"], order="NCHW")
net.Concat(["E", "F"], ["G"], add_axis=1, order="NHWC")
(shapes, types) = workspace.InferShapesAndTypes(
[net],
{
'A': [10, 12, 9, 10],
'B': [10, 9, 9, 10],
'D': [10, 2, 9, 10],
'F': [10, 23, 9, 10]
}
)
self.assertEqual(shapes['C'], [10, 21, 9, 10])
self.assertEqual(shapes['splits'], [2])
self.assertEqual(shapes['E'], [10, 23, 9, 10])
self.assertEqual(shapes['G'], [10, 23, 9, 2, 10])
def testConcatInt32(self):
net = core.Net("concat")
net.Concat(["A", "B"], ["C", "splits"], axis=1)
net.Concat(["C", "D"], ["E"], order="NCHW")
net.Concat(["E", "F"], ["G"], add_axis=1, order="NHWC")
(shapes, types) = workspace.InferShapesAndTypes(
[net],
blob_dimensions={
'A': [10, 12, 9, 10],
'B': [10, 9, 9, 10],
'D': [10, 2, 9, 10],
'F': [10, 23, 9, 10]
},
blob_types={
'A': core.DataType.INT32,
'B': core.DataType.INT32,
'D': core.DataType.INT32,
'F': core.DataType.INT32,
}
)
self.assertEqual(shapes['C'], [10, 21, 9, 10])
self.assertEqual(shapes['splits'], [2])
self.assertEqual(shapes['E'], [10, 23, 9, 10])
self.assertEqual(shapes['G'], [10, 23, 9, 2, 10])
self.assertEqual(types['C'], core.DataType.INT32)
self.assertEqual(types['splits'], core.DataType.INT32)
self.assertEqual(types['E'], core.DataType.INT32)
self.assertEqual(types['G'], core.DataType.INT32)
def testSqueeze(self):
net = core.Net("sq")
net.Squeeze(["data"], ["data_squeezed"], dims=[3, 1])
(shapes, types) = workspace.InferShapesAndTypes(
[net],
{'data': [64, 1, 96, 1, 4]}
)
self.assertEqual(shapes['data_squeezed'], [64, 96, 4])
def testCast(self):
model = model_helper.ModelHelper(name="test_model")
types = [
('bool', np.bool, caffe2_pb2.TensorProto.BOOL),
#('byte', None, caffe2_pb2.TensorProto.BYTE),
('int8', np.int8, caffe2_pb2.TensorProto.INT8),
('uint8', np.uint8, caffe2_pb2.TensorProto.UINT8),
('int16', np.int16, caffe2_pb2.TensorProto.INT16),
('uint16', np.uint16, caffe2_pb2.TensorProto.UINT16),
#('float16', np.float16, caffe2_pb2.TensorProto.FLOAT16),
('int32', np.int32, caffe2_pb2.TensorProto.INT32),
('float', np.float32, caffe2_pb2.TensorProto.FLOAT),
('int64', np.int64, caffe2_pb2.TensorProto.INT64),
('double', np.float64, caffe2_pb2.TensorProto.DOUBLE),
#('string', None, caffe2_pb2.TensorProto.STRING),
]
for (xstr, xnp, _) in types:
xname = 'X%s' % xstr
workspace.FeedBlob(xname, np.random.rand(1).astype(xnp))
for (ystr, _, yc2) in types:
yname = 'Y%s_to_%s' % (xstr, ystr)
model.Cast(xname, yname, to=yc2)
self.InferTensorRunAndCompare(model)
def testShapeInferenceRoiPool(self):
for is_test in [True, False]:
model = model_helper.ModelHelper(name="test_model")
outputs = ['Y'] if is_test else ['Y', 'argmaxes']
model.net.RoIPool(
['X', 'R'], outputs, pooled_h=4, pooled_w=5, is_test=is_test)
workspace.FeedBlob(
"X",
np.random.rand(100, 3, 4, 5).astype(np.float32))
workspace.FeedBlob(
"R",
np.random.rand(2, 5).astype(np.float32))
self.InferTensorRunAndCompare(model)
def testShapeInferencePow(self):
model = model_helper.ModelHelper(name="powtest")
model.Pow("x", 'y', exponent=-1.0)
workspace.FeedBlob('x', np.random.rand(1, 2, 3, 4).astype(np.float32))
self.InferTensorRunAndCompare(model)
def testInt8Conversion(self):
model = model_helper.ModelHelper(name="fp32_int8_conversion_test")
model.FloatToFused8BitRowwiseQuantized('x', 'x_8bit')
model.Fused8BitRowwiseQuantizedToFloat('x_8bit', 'x_recovered')
workspace.FeedBlob('x', np.random.rand(100, 150).astype(np.float32))
self.InferTensorRunAndCompare(model)
x = workspace.FetchBlob('x')
x_recovered = workspace.FetchBlob('x_recovered')
# TODO: find a tighter bound
assert(np.allclose(x, x_recovered, atol=1e-2))
def testHalfInt8Conversion(self):
model = model_helper.ModelHelper(name="fp16_int8_conversion_test")
model.HalfFloatToFused8BitRowwiseQuantized('x', 'x_8bit')
model.Fused8BitRowwiseQuantizedToHalfFloat('x_8bit', 'x_recovered')
workspace.FeedBlob('x', np.random.rand(100, 150).astype(np.float16))
self.InferTensorRunAndCompare(model)
x = workspace.FetchBlob('x')
x_recovered = workspace.FetchBlob('x_recovered')
# TODO: find a tighter bound
assert(np.allclose(x, x_recovered, atol=1e-2))
def testShapeOp(self):
model = model_helper.ModelHelper(name="shape_op_test")
model.Shape('x', 'y')
workspace.FeedBlob('x', np.random.rand(100, 150).astype(np.float32))
self.InferTensorRunAndCompare(model)
def InferTensorRunAndCompare(self, model, expected_uninferred_blobs=None):
'''
Runs shape inference, and then the model to check
that the inferred shapes agree with the actual ones
'expected_uninferred_blobs' is the list of blobs for which type and
shape cannot be inferred.
'''
(shapes, types) = workspace.InferShapesAndTypes(
[model.param_init_net, model.net],
)
# .. Create net
workspace.RunNetOnce(model.param_init_net)
workspace.CreateNet(model.net, True)
workspace.RunNet(model.Proto().name)
# ... and then check the shapes mismatch
correct_shapes = {}
correct_types = {}
for b in workspace.Blobs():
arr = workspace.FetchBlob(b)
correct_shapes[b] = arr.shape
if type(arr) is np.ndarray:
if arr.dtype == np.dtype('float32'):
correct_types[b] = caffe2_pb2.TensorProto.FLOAT
elif arr.dtype == np.dtype('int32'):
correct_types[b] = caffe2_pb2.TensorProto.INT32
# BYTE
# STRING
elif arr.dtype == np.dtype('bool'):
correct_types[b] = caffe2_pb2.TensorProto.BOOL
elif arr.dtype == np.dtype('uint8'):
correct_types[b] = caffe2_pb2.TensorProto.UINT8
elif arr.dtype == np.dtype('int8'):
correct_types[b] = caffe2_pb2.TensorProto.INT8
elif arr.dtype == np.dtype('uint16'):
correct_types[b] = caffe2_pb2.TensorProto.UINT16
elif arr.dtype == np.dtype('int16'):
correct_types[b] = caffe2_pb2.TensorProto.INT16
elif arr.dtype == np.dtype('int64'):
correct_types[b] = caffe2_pb2.TensorProto.INT64
elif arr.dtype == np.dtype('float16'):
correct_types[b] = caffe2_pb2.TensorProto.FLOAT16
elif arr.dtype == np.dtype('float64'):
correct_types[b] = caffe2_pb2.TensorProto.DOUBLE
else:
correct_types[b] = "unknown {}".format(arr.dtype)
else:
correct_types[b] = str(type(arr))
if expected_uninferred_blobs is None:
expected_uninferred_blobs = []
for b in correct_shapes:
# skip blobs for which shape couldn't be inferred
if b in expected_uninferred_blobs:
continue
self.assertTrue(
np.array_equal(
np.array(shapes[b]).astype(np.int32),
np.array(correct_shapes[b]).astype(np.int32)
),
"Shape {} mismatch: {} vs. correct {}".format(
b, shapes[b], correct_shapes[b]
)
)
self.assertFalse(
b not in types and b in correct_types,
"Type for {} not defined".format(b),
)
self.assertEqual(
types[b],
correct_types[b],
"Type {} mismatch: {} vs. {}".format(
b, types[b], correct_types[b],
)
)
if __name__ == "__main__":
unittest.main()