Adding Peter's Swish Op ULP analysis. (#42573)
Summary:
Pull Request resolved: https://github.com/pytorch/pytorch/pull/42573
* Generate the ULP png files for different ranges.
Test Plan: test_op_ulp_error.py
Reviewed By: hyuen
Differential Revision: D22938572
fbshipit-source-id: 6374bef6d44c38e1141030d44029dee99112cd18
diff --git a/caffe2/contrib/fakelowp/test/test_op_nnpi_fp16.py b/caffe2/contrib/fakelowp/test/test_op_nnpi_fp16.py
index 36577f7..f9615c5 100644
--- a/caffe2/contrib/fakelowp/test/test_op_nnpi_fp16.py
+++ b/caffe2/contrib/fakelowp/test/test_op_nnpi_fp16.py
@@ -3,22 +3,17 @@
from __future__ import print_function
from __future__ import unicode_literals
-import ctypes
import numpy as np
-import os
import caffe2.python.fakelowp.init_shared_libs # noqa
-
-from hypothesis import given, settings
+from hypothesis import given
from hypothesis import strategies as st
-
-
from caffe2.proto import caffe2_pb2
-from caffe2.python import dyndep
from caffe2.python import core
from caffe2.python import workspace
from caffe2.python.onnx.onnxifi import onnxifi_caffe2_net
from caffe2.python.fakelowp.test_utils import print_test_debug_info
+from caffe2.python.oss.fakelowp.test_utils import compute_ulp_error
import caffe2.python.serialized_test.serialized_test_util as serial
core.GlobalInit(["caffe2", "--caffe2_log_level=-3", "--glow_global_fp16=1"])
@@ -119,12 +114,9 @@
class UnaryOpTest(serial.SerializedTestCase):
- def _test_unary_op(self, opname, value, rtol=1e-5, atol=1e-8):
+ def _test_unary_op(self, opname, X, rtol=1e-5, atol=1e-8):
workspace.ResetWorkspace()
- n = 1
- m = 10001
- X = np.linspace(-value, value, num=m, dtype=np.float32)
pred_net = caffe2_pb2.NetDef()
pred_net.name = "pred"
pred_net.external_input.append("X")
@@ -147,7 +139,7 @@
)
print("REF NET = {}".format(ref_net))
- shape_hints = {"X": (n, m)}
+ shape_hints = {"X": X.shape}
pred_net_onnxified = onnxifi_caffe2_net(pred_net,
shape_hints,
debug=True,
@@ -167,8 +159,6 @@
workspace.RunNet(ref_net.name)
Y_c2 = workspace.FetchBlob('Y')
-
-
if not np.allclose(Y_c2, Y_glow, rtol=atol, atol=atol):
diff = np.abs(Y_c2 - Y_glow)
np.save('/tmp/' + opname + 'diff', diff)
@@ -181,19 +171,42 @@
})
assert(0)
+ return Y_glow
+
+ def _test_op_w_ulp_error(self, opname, regions, atol=0, err_threshold=2):
+ ulp_err = 0
+ for x0, x1 in regions:
+ X = np.linspace(x0, x1, num=1025, dtype=np.float16).astype(np.float32)
+ Y_glow = self._test_unary_op(opname, X, atol=atol)
+ region_err = compute_ulp_error(opname, X, Y_glow)
+ ulp_err = max(np.max(np.abs(region_err)), ulp_err)
+ if (ulp_err > err_threshold):
+ print(r'{} Op detected ulp_err={}'.format(opname, ulp_err))
+ assert(0)
+
# These tests doesn't need to run multiple times given that it is a
# linear sweep and it is deterministic.
# Once hypothesis.testing version is updated, we can re-enable
# testing with different hypothesis examples.
def test_sigmoid(self):
- self._test_unary_op("Sigmoid", value=20)
+ opname = "Sigmoid"
+ regions = [[-8., -4.], [-4., -2.], [-2., -1.], [-1., -.5], [-.5, -.25],
+ [-.25, .25], [.25, .5], [.5, 1.], [1., 2.], [2., 4.],
+ [4., 8.]]
+ self._test_op_w_ulp_error(opname, regions, atol=0, err_threshold=2.5)
# These tests doesn't need to run multiple times given that it is a
# linear sweep and it is deterministic.
# Once hypothesis.testing version is updated, we can re-enable
# testing with different hypothesis examples.
def test_tanh(self):
- self._test_unary_op("Tanh", value=20)
+ opname = "Tanh"
+ regions = [[2.**(-9), 2.**(-8)], [2.**(-8), 2.**(-7)],
+ [2.**(-7), 2.**(-6)], [2.**(-6), 2.**(-5)],
+ [2.**(-5), 2.**(-4)], [2.**(-4), 2.**(-3)],
+ [2.**(-3), 2.**(-2)], [2.**(-2), 2.**(-1)],
+ [2.**(-1), 1.], [1., 2.], [2., 4.], [4., 8.]]
+ self._test_op_w_ulp_error(opname, regions, atol=0, err_threshold=2)
# These tests doesn't need to run multiple times given that it is a
# linear sweep and it is deterministic.
@@ -201,7 +214,10 @@
# testing with different hypothesis examples.
# TODO: move atol to 1e-8 once we get a non-lowered swish implementation
def test_swish(self):
- self._test_unary_op("Swish", value=20, atol=0.008)
+ opname = "Swish"
+ regions = [[-20.5, -11.], [-11., -8.], [-8., -1.], [-1., -0.1],
+ [-1. / 8., 1. / 8.], [1. / 8, 5.], [5., 8.]]
+ self._test_op_w_ulp_error(opname, regions, atol=0.008, err_threshold=384)
# These tests doesn't need to run multiple times given that it is a
# linear sweep and it is deterministic.
@@ -328,6 +344,6 @@
if not np.allclose(Y_c2, Y_glow):
diff = np.abs((Y_glow - Y_c2) / (Y_c2 + kEpsilon))
print_test_debug_info("Relu", {
- "seed":seed, "X": X,
+ "seed": seed, "X": X,
"Y_glow": Y_glow, "Y_c2": Y_c2, "diff": diff})
assert(0)
diff --git a/caffe2/python/fakelowp/test_utils.py b/caffe2/python/fakelowp/test_utils.py
index 275289b..75e4422 100644
--- a/caffe2/python/fakelowp/test_utils.py
+++ b/caffe2/python/fakelowp/test_utils.py
@@ -6,7 +6,6 @@
import sys
import numpy as np
-
def print_test_debug_info(testname, items_dict):
filename = "debug_operator_onnxifi_" + testname + ".txt"
np.set_printoptions(threshold=sys.maxsize)
@@ -16,7 +15,6 @@
f.write("{}\n".format(key))
f.write("{}\n".format(value))
-
def print_net(net):
for i in net.external_input:
print("Input: {}".format(i))
@@ -28,3 +26,40 @@
print(" input: {}".format(x))
for y in op.output:
print(" output: {}".format(y))
+
+def _sigmoid(x):
+ return 1. / (1. + np.exp(np.float64(-x)))
+
+def _tanh(x):
+ return np.tanh(np.float64(x))
+
+def _swish(x):
+ return np.float64(x) * _sigmoid(x)
+
+def _gelu_by_sigmoid(x):
+ return np.float64(x) / (1. + np.exp(np.float64(x) * 1.702))
+
+
+def _acc_func(opname, x):
+ if opname == "Swish":
+ return _swish(x)
+ elif opname == "Sigmoid":
+ return _sigmoid(x)
+ elif opname == "Tanh":
+ return _tanh(x)
+ elif opname == "Gelu":
+ return _gelu_by_sigmoid(x)
+ else:
+ return x
+
+def _get_ulp16(x):
+ abs_x = np.abs(x)
+ mask = (abs_x > 2.**(-14))
+ abs_x = mask * abs_x + (1 - mask) * 2.**(-14)
+ k = np.floor(np.log2(abs_x))
+ return 2.**(k - 10)
+
+def compute_ulp_error(opname, xvec, y_nnpi):
+ y_acc = _acc_func(opname, np.float64(xvec))
+ scale = 1. / _get_ulp16(y_acc)
+ return (y_nnpi - y_acc) * scale