Init a cos similarity operator
| 4930 | |
| 4931 | |
| 4932 | class CosSim(Operator): |
| 4933 | """ |
| 4934 | Init a cos similarity operator |
| 4935 | """ |
| 4936 | |
| 4937 | def __init__(self): |
| 4938 | super(CosSim, self).__init__() |
| 4939 | |
| 4940 | @classmethod |
| 4941 | def dot(cls, a, b): |
| 4942 | """ |
| 4943 | dot multiply |
| 4944 | Args: |
| 4945 | a (CTensor): 2d input tensor. |
| 4946 | b (CTensor): 2d input tensor. |
| 4947 | Returns: |
| 4948 | CTensor: the output CTensor. |
| 4949 | """ |
| 4950 | batch_size = a.shape()[0] |
| 4951 | ret = [] |
| 4952 | for indice in range(batch_size): |
| 4953 | tmp_a = singa.SliceOn(a, indice, indice + 1, 0) # 1 * d |
| 4954 | tmp_b = singa.SliceOn(b, indice, indice + 1, 0) # 1 * d |
| 4955 | tmp_b = singa.DefaultTranspose(tmp_b) |
| 4956 | tmp_tensor = singa.Mult(tmp_a, tmp_b) # 1 * d * d * 1 |
| 4957 | ret.append(tmp_tensor) |
| 4958 | ret = singa.VecTensor(ret) |
| 4959 | ret = singa.ConcatOn(ret, 0) # b * 1 |
| 4960 | return singa.Reshape(ret, [ret.shape()[0]]) # b |
| 4961 | |
| 4962 | def forward(self, a, b): |
| 4963 | """ |
| 4964 | forward of CosSim |
| 4965 | Args: |
| 4966 | a (CTensor): input tensor. |
| 4967 | b (CTensor): input tensor. |
| 4968 | Returns: |
| 4969 | the output CTensor. |
| 4970 | """ |
| 4971 | ad = CosSim.dot(a, a) |
| 4972 | bd = CosSim.dot(b, b) |
| 4973 | ap = singa.PowFloat(ad, 0.5) |
| 4974 | bp = singa.PowFloat(bd, 0.5) |
| 4975 | ret = singa.__div__(CosSim.dot(a, b), singa.__mul__(ap, bp)) |
| 4976 | if training: |
| 4977 | self.cache = (a, b, ad, bd, ap, bp, ret) |
| 4978 | return ret |
| 4979 | |
| 4980 | def backward(self, dy): |
| 4981 | """ |
| 4982 | backward of CosSim |
| 4983 | follow https://math.stackexchange.com/a/1923705 |
| 4984 | Args: |
| 4985 | dy (CTensor): gradient tensor. |
| 4986 | Return: |
| 4987 | the gradient tensor over input tensor. |
| 4988 | """ |
| 4989 | a, b, ad, bd, ap, bp, ret = self.cache |