import hypothesis.strategies as st
import numpy as np
from caffe2.python import core
import caffe2.python.hypothesis_test_util as hu
import caffe2.python.serialized_test.serialized_test_util as serial
class TestCosineEmbeddingCriterion(serial.SerializedTestCase):
@serial.given(N=st.integers(min_value=10, max_value=20),
seed=st.integers(min_value=0, max_value=65535),
margin=st.floats(min_value=-0.5, max_value=0.5),
**hu.gcs)
def test_cosine_embedding_criterion(self, N, seed, margin, gc, dc):
np.random.seed(seed)
S = np.random.randn(N).astype(np.float32)
Y = np.random.choice([-1, 1], size=N).astype(np.int32)
op = core.CreateOperator(
"CosineEmbeddingCriterion", ["S", "Y"], ["output"],
margin=margin)
def ref_cec(S, Y):
result = (1 - S) * (Y == 1) + np.maximum(S - margin, 0) * (Y == -1)
return (result, )
# This checks the op implementation against a reference function in
# python.
self.assertReferenceChecks(gc, op, [S, Y], ref_cec)
# This checks the op implementation over multiple device options (e.g.
# CPU and CUDA). [0] means that the 0-th output is checked.
self.assertDeviceChecks(dc, op, [S, Y], [0])
# Now, since this operator's output has a "kink" around the margin
# value, we move the S vector away from the margin a little bit. This
# is a standard trick to avoid gradient check to fail on subgradient
# points.
S[np.abs(S - margin) < 0.1] += 0.2
# This checks the operator's gradient. the first 0 means that we are
# checking the gradient of the first input (S), and the second [0] means
# that the gradient check should initiate from the 0-th output.
self.assertGradientChecks(gc, op, [S, Y], 0, [0])
if __name__ == "__main__":
import unittest
unittest.main()