Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【Hackathon No.17】为 Paddle 新增 paddle.nn.CosineEmbeddingLoss 和 paddle.nn.functional.cosine_embedding_loss API #41680

Merged
merged 36 commits into from Jun 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a2d6af5
add cosine embedding loss API
Patrick-Star125 Apr 12, 2022
ea0bd15
stay new version
Patrick-Star125 Apr 12, 2022
6cc16e7
new version
Patrick-Star125 Apr 12, 2022
33003a7
new version
Patrick-Star125 Apr 12, 2022
0854053
new version
Patrick-Star125 Apr 12, 2022
bde0cdc
set label to int32
Patrick-Star125 Apr 13, 2022
9352335
Merge remote-tracking branch 'refs/remotes/origin/develop' into develop
Patrick-Star125 Apr 17, 2022
5fb8bd1
new version
Patrick-Star125 Apr 17, 2022
0f08a10
new version-test
Patrick-Star125 Apr 17, 2022
999e94c
new version
Patrick-Star125 Apr 17, 2022
c080cfb
new version
Patrick-Star125 Apr 18, 2022
358260f
new version
Patrick-Star125 Apr 18, 2022
0e9a719
Merge branch 'PaddlePaddle:develop' into develop
Patrick-Star125 Apr 19, 2022
ac2bdda
Merge branch 'develop' of https://github.com/Patrick-Star125/Paddle i…
Patrick-Star125 Apr 19, 2022
8243ba8
new version
Patrick-Star125 Apr 19, 2022
d0174d5
new version
Patrick-Star125 Apr 20, 2022
87b7c3c
new version
Patrick-Star125 Apr 20, 2022
43eb546
new version
Patrick-Star125 Apr 21, 2022
f7cb133
new version
Patrick-Star125 Apr 21, 2022
f4d95db
new version
Patrick-Star125 Apr 24, 2022
6bcf254
new version
Patrick-Star125 Apr 25, 2022
a9046be
new version
Patrick-Star125 Apr 25, 2022
1868d0d
new version
Patrick-Star125 Apr 26, 2022
044022e
new version
Patrick-Star125 Apr 26, 2022
998ac5d
new version
Patrick-Star125 Apr 30, 2022
e684e0f
new version
Patrick-Star125 May 5, 2022
0bde96b
new version
Patrick-Star125 May 6, 2022
de6623f
aligning to Chinese document
Patrick-Star125 May 21, 2022
95217b8
add name parameter
Patrick-Star125 May 31, 2022
2c8c224
resolve conflicts
Patrick-Star125 May 31, 2022
53a4366
activate CI
Patrick-Star125 Jun 1, 2022
de8e7e6
Merge remote-tracking branch 'develop/develop' into develop
Patrick-Star125 Jun 1, 2022
f08ff19
fix format error
Patrick-Star125 Jun 2, 2022
ff13466
sync with Paddle
Patrick-Star125 Jun 5, 2022
34dc781
unit test code format
Patrick-Star125 Jun 6, 2022
f861de4
format code
Patrick-Star125 Jun 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
328 changes: 328 additions & 0 deletions python/paddle/fluid/tests/unittests/test_cosine_embedding_loss.py
@@ -0,0 +1,328 @@
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License

from __future__ import print_function

import paddle
import paddle.static as static
import numpy as np
import unittest


def cosine_embedding_loss(input1, input2, label, margin=0.5, reduction='mean'):
z = (input1 * input2).sum(axis=-1)
mag_square1 = np.square(input1).sum(axis=-1) + 10e-12
mag_square2 = np.square(input2).sum(axis=-1) + 10e-12
denom = np.sqrt(mag_square1 * mag_square2)
cos = z / denom
zeros = np.zeros_like(cos)
pos = 1 - cos
neg = np.clip(cos - margin, a_min=0, a_max=np.inf)
out_pos = np.where(label == 1, pos, zeros)
out_neg = np.where(label == -1, neg, zeros)
out = out_pos + out_neg
if reduction == 'none':
return out
if reduction == 'mean':
return np.mean(out)
elif reduction == 'sum':
return np.sum(out)


class TestFunctionCosineEmbeddingLoss(unittest.TestCase):

def setUp(self):
self.input1_np = np.random.random(size=(5, 3)).astype(np.float64)
self.input2_np = np.random.random(size=(5, 3)).astype(np.float64)
a = np.array([-1, -1, -1]).astype(np.int32)
b = np.array([1, 1]).astype(np.int32)
self.label_np = np.concatenate((a, b), axis=0)
np.random.shuffle(self.label_np)

def run_dynamic(self):
input1 = paddle.to_tensor(self.input1_np)
input2 = paddle.to_tensor(self.input2_np)
label = paddle.to_tensor(self.label_np)
dy_result = paddle.nn.functional.cosine_embedding_loss(input1,
input2,
label,
margin=0.5,
reduction='mean')
expected1 = cosine_embedding_loss(self.input1_np,
self.input2_np,
self.label_np,
margin=0.5,
reduction='mean')
self.assertTrue(np.allclose(dy_result.numpy(), expected1))
self.assertTrue(dy_result.shape, [1])

dy_result = paddle.nn.functional.cosine_embedding_loss(input1,
input2,
label,
margin=0.5,
reduction='sum')
expected2 = cosine_embedding_loss(self.input1_np,
self.input2_np,
self.label_np,
margin=0.5,
reduction='sum')

self.assertTrue(np.allclose(dy_result.numpy(), expected2))
self.assertTrue(dy_result.shape, [1])

dy_result = paddle.nn.functional.cosine_embedding_loss(input1,
input2,
label,
margin=0.5,
reduction='none')
expected3 = cosine_embedding_loss(self.input1_np,
self.input2_np,
self.label_np,
margin=0.5,
reduction='none')

self.assertTrue(np.allclose(dy_result.numpy(), expected3))
self.assertTrue(dy_result.shape, [5])

def run_static(self, use_gpu=False):
input1 = static.data(name='input1', shape=[5, 3], dtype='float64')
input2 = static.data(name='input2', shape=[5, 3], dtype='float64')
label = static.data(name='label', shape=[5], dtype='int32')
result0 = paddle.nn.functional.cosine_embedding_loss(input1,
input2,
label,
margin=0.5,
reduction='none')
result1 = paddle.nn.functional.cosine_embedding_loss(input1,
input2,
label,
margin=0.5,
reduction='sum')
result2 = paddle.nn.functional.cosine_embedding_loss(input1,
input2,
label,
margin=0.5,
reduction='mean')

place = paddle.CUDAPlace(0) if use_gpu else paddle.CPUPlace()
exe = static.Executor(place)
exe.run(static.default_startup_program())
static_result = exe.run(feed={
"input1": self.input1_np,
"input2": self.input2_np,
"label": self.label_np
},
fetch_list=[result0, result1, result2])
expected = cosine_embedding_loss(self.input1_np,
self.input2_np,
self.label_np,
margin=0.5,
reduction='none')

self.assertTrue(np.allclose(static_result[0], expected))
expected = cosine_embedding_loss(self.input1_np,
self.input2_np,
self.label_np,
margin=0.5,
reduction='sum')

self.assertTrue(np.allclose(static_result[1], expected))
expected = cosine_embedding_loss(self.input1_np,
self.input2_np,
self.label_np,
margin=0.5,
reduction='mean')

self.assertTrue(np.allclose(static_result[2], expected))

def test_cpu(self):
paddle.disable_static(place=paddle.CPUPlace())
self.run_dynamic()
paddle.enable_static()

with static.program_guard(static.Program()):
self.run_static()

def test_gpu(self):
if not paddle.is_compiled_with_cuda():
return

paddle.disable_static(place=paddle.CUDAPlace(0))
self.run_dynamic()
paddle.enable_static()

with static.program_guard(static.Program()):
self.run_static(use_gpu=True)

def test_errors(self):
paddle.disable_static()
input1 = paddle.to_tensor(self.input1_np)
input2 = paddle.to_tensor(self.input2_np)
label = paddle.to_tensor(self.label_np)

def test_label_shape_error():
label = paddle.to_tensor(
np.random.randint(low=0, high=2, size=(2, 3)))
paddle.nn.functional.cosine_embedding_loss(input1,
input2,
label,
margin=0.5,
reduction='mean')

self.assertRaises(ValueError, test_label_shape_error)

def test_input_different_shape_error():
input1 = paddle.to_tensor(self.input1_np[0])
label = paddle.to_tensor(np.ndarray([1]))
paddle.nn.functional.cosine_embedding_loss(input1,
input2,
label,
margin=0.5,
reduction='mean')

self.assertRaises(ValueError, test_input_different_shape_error)

def test_input_shape2D_error():
input1 = paddle.to_tensor(
np.random.random(size=(2, 3, 4)).astype(np.float64))
input2 = paddle.to_tensor(
np.random.random(size=(2, 3, 4)).astype(np.float64))
paddle.nn.functional.cosine_embedding_loss(input1,
input2,
label,
margin=0.5,
reduction='mean')

self.assertRaises(ValueError, test_input_shape2D_error)

def test_label_value_error():
label = paddle.to_tensor(np.ndarray([-1, -2]))
paddle.nn.functional.cosine_embedding_loss(input1,
input2,
label,
margin=0.5,
reduction='mean')

self.assertRaises(ValueError, test_label_value_error)

def test_input_type_error():
input1 = paddle.to_tensor(self.input1_np.astype(np.int64))
paddle.nn.functional.cosine_embedding_loss(input1,
input2,
label,
margin=0.5,
reduction='mean')

self.assertRaises(ValueError, test_input_type_error)

def test_label_type_error():
label = paddle.to_tensor(self.label_np.astype(np.int16))
paddle.nn.functional.cosine_embedding_loss(input1,
input2,
label,
margin=0.5,
reduction='mean')

self.assertRaises(ValueError, test_label_type_error)


class TestClassCosineEmbeddingLoss(unittest.TestCase):

def setUp(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里主要覆盖了input: float64, label: int32的输入类型,需要补充下设计支持的所有其他类型的测试用例。此外覆盖的shape可以多样化一些,保证正确性。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

再补充一个shape 为(D,)的测试吧

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已修改

self.input1_np = np.random.random(size=(10, 3)).astype(np.float32)
self.input2_np = np.random.random(size=(10, 3)).astype(np.float32)
a = np.array([-1, -1, -1, -1, -1]).astype(np.int64)
b = np.array([1, 1, 1, 1, 1]).astype(np.int64)
self.label_np = np.concatenate((a, b), axis=0)
np.random.shuffle(self.label_np)
self.input1_np_1D = np.random.random(size=10).astype(np.float32)
self.input2_np_1D = np.random.random(size=10).astype(np.float32)
self.label_np_1D = np.array([1]).astype(np.int64)

def run_dynamic(self):
input1 = paddle.to_tensor(self.input1_np)
input2 = paddle.to_tensor(self.input2_np)
label = paddle.to_tensor(self.label_np)
CosineEmbeddingLoss = paddle.nn.CosineEmbeddingLoss(margin=0.5,
reduction='mean')
dy_result = CosineEmbeddingLoss(input1, input2, label)
expected1 = cosine_embedding_loss(self.input1_np,
self.input2_np,
self.label_np,
margin=0.5,
reduction='mean')
self.assertTrue(np.allclose(dy_result.numpy(), expected1))
self.assertTrue(dy_result.shape, [1])

input1_1D = paddle.to_tensor(self.input1_np_1D)
input2_1D = paddle.to_tensor(self.input2_np_1D)
label_1D = paddle.to_tensor(self.label_np_1D)
dy_result = CosineEmbeddingLoss(input1_1D, input2_1D, label_1D)
expected2 = cosine_embedding_loss(self.input1_np_1D,
self.input2_np_1D,
self.label_np_1D,
margin=0.5,
reduction='mean')
self.assertTrue(np.allclose(dy_result.numpy(), expected2))

def run_static(self):
input1 = static.data(name='input1', shape=[10, 3], dtype='float32')
input2 = static.data(name='input2', shape=[10, 3], dtype='float32')
label = static.data(name='label', shape=[10], dtype='int64')
CosineEmbeddingLoss = paddle.nn.CosineEmbeddingLoss(margin=0.5,
reduction='mean')
result = CosineEmbeddingLoss(input1, input2, label)

place = paddle.CPUPlace()
exe = static.Executor(place)
exe.run(static.default_startup_program())
static_result = exe.run(feed={
"input1": self.input1_np,
"input2": self.input2_np,
"label": self.label_np
},
fetch_list=[result])
expected = cosine_embedding_loss(self.input1_np,
self.input2_np,
self.label_np,
margin=0.5,
reduction='mean')

self.assertTrue(np.allclose(static_result[0], expected))

def test_cpu(self):
paddle.disable_static(place=paddle.CPUPlace())
self.run_dynamic()
paddle.enable_static()

with static.program_guard(static.Program()):
self.run_static()

def test_errors(self):

def test_margin_error():
CosineEmbeddingLoss = paddle.nn.CosineEmbeddingLoss(
margin=2, reduction='mean')

self.assertRaises(ValueError, test_margin_error)

def test_reduction_error():
CosineEmbeddingLoss = paddle.nn.CosineEmbeddingLoss(
margin=2, reduction='reduce_mean')

self.assertRaises(ValueError, test_reduction_error)


if __name__ == "__main__":
unittest.main()
2 changes: 2 additions & 0 deletions python/paddle/nn/__init__.py
Expand Up @@ -107,6 +107,7 @@
from .layer.loss import CTCLoss # noqa: F401
from .layer.loss import SmoothL1Loss # noqa: F401
from .layer.loss import HingeEmbeddingLoss # noqa: F401
from .layer.loss import CosineEmbeddingLoss # noqa: F401
from .layer.norm import BatchNorm # noqa: F401
from .layer.norm import SyncBatchNorm # noqa: F401
from .layer.norm import GroupNorm # noqa: F401
Expand Down Expand Up @@ -311,5 +312,6 @@ def weight_norm(*args):
'MaxUnPool3D',
'HingeEmbeddingLoss',
'Identity',
'CosineEmbeddingLoss',
'RReLU',
]
2 changes: 2 additions & 0 deletions python/paddle/nn/functional/__init__.py
Expand Up @@ -90,6 +90,7 @@
from .loss import square_error_cost # noqa: F401
from .loss import ctc_loss # noqa: F401
from .loss import hinge_embedding_loss # noqa: F401
from .loss import cosine_embedding_loss # noqa: F401
from .norm import batch_norm # noqa: F401
from .norm import instance_norm # noqa: F401
from .norm import layer_norm # noqa: F401
Expand Down Expand Up @@ -229,5 +230,6 @@
'class_center_sample',
'sparse_attention',
'fold',
'cosine_embedding_loss',
'rrelu',
]