From 0ab51800513133e558abac8b80440a7fdd3dda99 Mon Sep 17 00:00:00 2001 From: hi-sushanta Date: Thu, 26 Oct 2023 19:45:01 +0530 Subject: [PATCH 1/9] I added structural similarity index(SSIM) loss into the ivy framework --- ivy/functional/ivy/losses.py | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/ivy/functional/ivy/losses.py b/ivy/functional/ivy/losses.py index 64201cf60f566..45b996b382e62 100644 --- a/ivy/functional/ivy/losses.py +++ b/ivy/functional/ivy/losses.py @@ -379,3 +379,59 @@ def sparse_cross_entropy( return ivy.cross_entropy( true, pred, axis=axis, epsilon=epsilon, reduction=reduction, out=out ) + + +@handle_exceptions +@handle_nestable +@handle_array_like_without_promotion +@inputs_to_ivy_arrays +@handle_array_function +def ssim_loss( + pred : Union[ivy.Array, ivy.NativeArray], + ytrue : Union[ivy.Array, ivy.NativeArray]): + + """Calculates the Structural Similarity Index (SSIM) loss between two images. + + Args: + pred: A 3D image tensor of shape (batch_size, channels, height, width). + ytrue: A 3D image tensor of shape (batch_size, channels, height, width). + + Returns: + ------- + inv.array: The SSIM loss mesure similarity between the two images. + + Examples + -------- + With :class:`ivy.Array` input: + >>> import ivy + >>> x = ivy.ones((batch_size, channels, height, width)) + >>> y = ivy.zeros((batch_size, channels, height, width)) + >>> loss = ivy.ssim_loss(x, y) + >>> print(loss) + ivy.array(0.99989992) + + """ + + # Calculate the mean and variance of the two images. + mu_x = iv.avg_pool2d(pred,(3,3),(3,3),'SAME') + mu_y = iv.avg_pool2d(ytrue, (3,3),(3,3),'SAME') + + sigma_x2 = iv.avg_pool2d(pred * pred, (3,3),(3,3),'SAME') - mu_x * mu_x + sigma_y2 = iv.avg_pool2d(ytrue * ytrue, (3,3),(3,3),'SAME') - mu_y * mu_y + + sigma_xy = iv.avg_pool2d(pred * ytrue,(3,3),(3,3),'SAME') - mu_x * mu_y + + # Add small constants to avoid division by zero. + C1 = 0.01 ** 2 + C2 = 0.03 ** 2 + + # Calculate the SSIM index. + ssim = ((2 * mu_x * mu_y + C1) * (2 * sigma_xy + C2)) / ((mu_x ** 2 + mu_y ** 2 + C1) * (sigma_x2 + sigma_y2 + C2)) + + # Subtract 1 from the SSIM index to make the loss function compatible with other loss functions that are minimized when the difference between the predicted image and the ground truth image is zero. + ssim = 1 - ssim + + # Take the mean of the loss values for each image in the batch to get a single loss value that can be used to train the model. + loss = iv.mean(ssim) + + return loss \ No newline at end of file From 1ccc5f87e561d9cff338408e514c7ff1a87b2115 Mon Sep 17 00:00:00 2001 From: ivy-branch Date: Thu, 26 Oct 2023 14:27:16 +0000 Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=A4=96=20Lint=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ivy/functional/ivy/losses.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/ivy/functional/ivy/losses.py b/ivy/functional/ivy/losses.py index 45b996b382e62..261744d0d8c78 100644 --- a/ivy/functional/ivy/losses.py +++ b/ivy/functional/ivy/losses.py @@ -387,10 +387,10 @@ def sparse_cross_entropy( @inputs_to_ivy_arrays @handle_array_function def ssim_loss( - pred : Union[ivy.Array, ivy.NativeArray], - ytrue : Union[ivy.Array, ivy.NativeArray]): - - """Calculates the Structural Similarity Index (SSIM) loss between two images. + pred: Union[ivy.Array, ivy.NativeArray], ytrue: Union[ivy.Array, ivy.NativeArray] +): + """ + Calculates the Structural Similarity Index (SSIM) loss between two images. Args: pred: A 3D image tensor of shape (batch_size, channels, height, width). @@ -399,34 +399,35 @@ def ssim_loss( Returns: ------- inv.array: The SSIM loss mesure similarity between the two images. - + Examples -------- With :class:`ivy.Array` input: - >>> import ivy + >>> import ivy >>> x = ivy.ones((batch_size, channels, height, width)) >>> y = ivy.zeros((batch_size, channels, height, width)) >>> loss = ivy.ssim_loss(x, y) >>> print(loss) ivy.array(0.99989992) - """ - + # Calculate the mean and variance of the two images. - mu_x = iv.avg_pool2d(pred,(3,3),(3,3),'SAME') - mu_y = iv.avg_pool2d(ytrue, (3,3),(3,3),'SAME') + mu_x = iv.avg_pool2d(pred, (3, 3), (3, 3), "SAME") + mu_y = iv.avg_pool2d(ytrue, (3, 3), (3, 3), "SAME") - sigma_x2 = iv.avg_pool2d(pred * pred, (3,3),(3,3),'SAME') - mu_x * mu_x - sigma_y2 = iv.avg_pool2d(ytrue * ytrue, (3,3),(3,3),'SAME') - mu_y * mu_y + sigma_x2 = iv.avg_pool2d(pred * pred, (3, 3), (3, 3), "SAME") - mu_x * mu_x + sigma_y2 = iv.avg_pool2d(ytrue * ytrue, (3, 3), (3, 3), "SAME") - mu_y * mu_y - sigma_xy = iv.avg_pool2d(pred * ytrue,(3,3),(3,3),'SAME') - mu_x * mu_y + sigma_xy = iv.avg_pool2d(pred * ytrue, (3, 3), (3, 3), "SAME") - mu_x * mu_y # Add small constants to avoid division by zero. - C1 = 0.01 ** 2 - C2 = 0.03 ** 2 + C1 = 0.01**2 + C2 = 0.03**2 # Calculate the SSIM index. - ssim = ((2 * mu_x * mu_y + C1) * (2 * sigma_xy + C2)) / ((mu_x ** 2 + mu_y ** 2 + C1) * (sigma_x2 + sigma_y2 + C2)) + ssim = ((2 * mu_x * mu_y + C1) * (2 * sigma_xy + C2)) / ( + (mu_x**2 + mu_y**2 + C1) * (sigma_x2 + sigma_y2 + C2) + ) # Subtract 1 from the SSIM index to make the loss function compatible with other loss functions that are minimized when the difference between the predicted image and the ground truth image is zero. ssim = 1 - ssim @@ -434,4 +435,4 @@ def ssim_loss( # Take the mean of the loss values for each image in the batch to get a single loss value that can be used to train the model. loss = iv.mean(ssim) - return loss \ No newline at end of file + return loss From 1894e91ccf769f7744fb5847ff029bac6177f137 Mon Sep 17 00:00:00 2001 From: hi-sushanta Date: Thu, 26 Oct 2023 21:05:37 +0530 Subject: [PATCH 3/9] I replace iv to ivy --- ivy/functional/ivy/losses.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ivy/functional/ivy/losses.py b/ivy/functional/ivy/losses.py index 261744d0d8c78..d47ecddbed4b5 100644 --- a/ivy/functional/ivy/losses.py +++ b/ivy/functional/ivy/losses.py @@ -412,13 +412,13 @@ def ssim_loss( """ # Calculate the mean and variance of the two images. - mu_x = iv.avg_pool2d(pred, (3, 3), (3, 3), "SAME") - mu_y = iv.avg_pool2d(ytrue, (3, 3), (3, 3), "SAME") + mu_x = ivy.avg_pool2d(pred, (3, 3), (3, 3), "SAME") + mu_y = ivy.avg_pool2d(ytrue, (3, 3), (3, 3), "SAME") - sigma_x2 = iv.avg_pool2d(pred * pred, (3, 3), (3, 3), "SAME") - mu_x * mu_x - sigma_y2 = iv.avg_pool2d(ytrue * ytrue, (3, 3), (3, 3), "SAME") - mu_y * mu_y + sigma_x2 = ivy.avg_pool2d(pred * pred, (3, 3), (3, 3), "SAME") - mu_x * mu_x + sigma_y2 = ivy.avg_pool2d(ytrue * ytrue, (3, 3), (3, 3), "SAME") - mu_y * mu_y - sigma_xy = iv.avg_pool2d(pred * ytrue, (3, 3), (3, 3), "SAME") - mu_x * mu_y + sigma_xy = ivy.avg_pool2d(pred * ytrue, (3, 3), (3, 3), "SAME") - mu_x * mu_y # Add small constants to avoid division by zero. C1 = 0.01**2 @@ -433,6 +433,6 @@ def ssim_loss( ssim = 1 - ssim # Take the mean of the loss values for each image in the batch to get a single loss value that can be used to train the model. - loss = iv.mean(ssim) + loss = ivy.mean(ssim) return loss From e60d8ceab14886d4d3562799590696102f86e28d Mon Sep 17 00:00:00 2001 From: hi-sushanta Date: Thu, 26 Oct 2023 21:21:18 +0530 Subject: [PATCH 4/9] I am using the Black package to reformat my file and add return types to my functions. --- ivy/functional/ivy/losses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivy/functional/ivy/losses.py b/ivy/functional/ivy/losses.py index d47ecddbed4b5..42fdb878ed5b5 100644 --- a/ivy/functional/ivy/losses.py +++ b/ivy/functional/ivy/losses.py @@ -388,7 +388,7 @@ def sparse_cross_entropy( @handle_array_function def ssim_loss( pred: Union[ivy.Array, ivy.NativeArray], ytrue: Union[ivy.Array, ivy.NativeArray] -): +) -> ivy.Array: """ Calculates the Structural Similarity Index (SSIM) loss between two images. From 229f4bc9b0daff4faffbb293078a5fabb874dc57 Mon Sep 17 00:00:00 2001 From: hi-sushanta Date: Fri, 27 Oct 2023 06:32:55 +0530 Subject: [PATCH 5/9] Modify doc string and remove unnecessary comment --- ivy/functional/ivy/losses.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ivy/functional/ivy/losses.py b/ivy/functional/ivy/losses.py index 42fdb878ed5b5..3a06f73120938 100644 --- a/ivy/functional/ivy/losses.py +++ b/ivy/functional/ivy/losses.py @@ -390,13 +390,14 @@ def ssim_loss( pred: Union[ivy.Array, ivy.NativeArray], ytrue: Union[ivy.Array, ivy.NativeArray] ) -> ivy.Array: """ - Calculates the Structural Similarity Index (SSIM) loss between two images. + Calculate the Structural Similarity Index (SSIM) loss between two images. - Args: + Parameters + ---------- pred: A 3D image tensor of shape (batch_size, channels, height, width). ytrue: A 3D image tensor of shape (batch_size, channels, height, width). - Returns: + Returns ------- inv.array: The SSIM loss mesure similarity between the two images. @@ -410,7 +411,6 @@ def ssim_loss( >>> print(loss) ivy.array(0.99989992) """ - # Calculate the mean and variance of the two images. mu_x = ivy.avg_pool2d(pred, (3, 3), (3, 3), "SAME") mu_y = ivy.avg_pool2d(ytrue, (3, 3), (3, 3), "SAME") @@ -429,10 +429,8 @@ def ssim_loss( (mu_x**2 + mu_y**2 + C1) * (sigma_x2 + sigma_y2 + C2) ) - # Subtract 1 from the SSIM index to make the loss function compatible with other loss functions that are minimized when the difference between the predicted image and the ground truth image is zero. ssim = 1 - ssim - # Take the mean of the loss values for each image in the batch to get a single loss value that can be used to train the model. loss = ivy.mean(ssim) return loss From d14c287089060d1b168e171110d56a0faaabfd17 Mon Sep 17 00:00:00 2001 From: ivy-branch Date: Sun, 3 Mar 2024 13:31:58 +0000 Subject: [PATCH 6/9] =?UTF-8?q?=F0=9F=A4=96=20Lint=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ivy/functional/ivy/losses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ivy/functional/ivy/losses.py b/ivy/functional/ivy/losses.py index f850c5a98fbae..69f983740e89d 100644 --- a/ivy/functional/ivy/losses.py +++ b/ivy/functional/ivy/losses.py @@ -391,8 +391,8 @@ def sparse_cross_entropy( def ssim_loss( pred: Union[ivy.Array, ivy.NativeArray], ytrue: Union[ivy.Array, ivy.NativeArray] ) -> ivy.Array: - """ - Calculate the Structural Similarity Index (SSIM) loss between two images. + """Calculate the Structural Similarity Index (SSIM) loss between two + images. Parameters ---------- From 1160ec932cfb16b25767bf3239050f895145e0f9 Mon Sep 17 00:00:00 2001 From: hi-sushanta Date: Mon, 4 Mar 2024 19:46:03 +0530 Subject: [PATCH 7/9] I adding testing code --- .../test_functional/test_nn/test_losses.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/ivy_tests/test_ivy/test_functional/test_nn/test_losses.py b/ivy_tests/test_ivy/test_functional/test_nn/test_losses.py index bba4b09173af4..ad85d980a1aa3 100644 --- a/ivy_tests/test_ivy/test_functional/test_nn/test_losses.py +++ b/ivy_tests/test_ivy/test_functional/test_nn/test_losses.py @@ -203,3 +203,46 @@ def test_sparse_cross_entropy( epsilon=epsilon, reduction=reduction, ) + +@handle_test( + fn_tree="functional.ivy.ssim_loss", + dtype_and_p_pred=helpers.dtype_and_values( + available_dtypes=helpers.get_dtypes("float"), + min_value=-1, + max_value=1, + allow_inf=False, + min_num_dims=1, + max_num_dims=1, + min_dim_size=2, + shape=(5,), + ), + dtype_and_p_real=helpers.dtype_and_values( + available_dtypes=helpers.get_dtypes("float"), + min_value=-1, + max_value=1, + allow_inf=False, + min_num_dims=1, + max_num_dims=1, + min_dim_size=2, + shape=(5,), + ), +) + +def test_ssim_loss(dtype_and_p_pred,dtype_and_p_real,test_flags,backend_fw,fn_name,on_device): + + dtype_p_pred,p_pred = dtype_and_p_pred + dtype_p_real,p_real = dtype_and_p_real + + helpers.test_function( + input_dtypes = [dtype_p_pred,dtype_p_real], + test_flags = test_flags, + backend_to_test=backend_fw, + fn_name=fn_name, + on_device = on_device, + args=(p_pred,p_real), + kwargs={}, + rtol=1e-02, + atol=1e-02, + ) + + From 544d3bed484ea7eab711c719ef530de9c9f8d5fb Mon Sep 17 00:00:00 2001 From: ivy-branch Date: Mon, 4 Mar 2024 14:17:36 +0000 Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=A4=96=20Lint=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_functional/test_nn/test_losses.py | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/ivy_tests/test_ivy/test_functional/test_nn/test_losses.py b/ivy_tests/test_ivy/test_functional/test_nn/test_losses.py index ad85d980a1aa3..8530cff3e24d1 100644 --- a/ivy_tests/test_ivy/test_functional/test_nn/test_losses.py +++ b/ivy_tests/test_ivy/test_functional/test_nn/test_losses.py @@ -204,6 +204,7 @@ def test_sparse_cross_entropy( reduction=reduction, ) + @handle_test( fn_tree="functional.ivy.ssim_loss", dtype_and_p_pred=helpers.dtype_and_values( @@ -227,22 +228,20 @@ def test_sparse_cross_entropy( shape=(5,), ), ) +def test_ssim_loss( + dtype_and_p_pred, dtype_and_p_real, test_flags, backend_fw, fn_name, on_device +): + dtype_p_pred, p_pred = dtype_and_p_pred + dtype_p_real, p_real = dtype_and_p_real -def test_ssim_loss(dtype_and_p_pred,dtype_and_p_real,test_flags,backend_fw,fn_name,on_device): - - dtype_p_pred,p_pred = dtype_and_p_pred - dtype_p_real,p_real = dtype_and_p_real - helpers.test_function( - input_dtypes = [dtype_p_pred,dtype_p_real], - test_flags = test_flags, + input_dtypes=[dtype_p_pred, dtype_p_real], + test_flags=test_flags, backend_to_test=backend_fw, fn_name=fn_name, - on_device = on_device, - args=(p_pred,p_real), + on_device=on_device, + args=(p_pred, p_real), kwargs={}, rtol=1e-02, atol=1e-02, ) - - From cf32de1d83271fe8c6225c6cc6fd716d886a505c Mon Sep 17 00:00:00 2001 From: Sam-Armstrong Date: Sat, 13 Jul 2024 17:04:57 +0100 Subject: [PATCH 9/9] fix --- ivy/functional/ivy/losses.py | 53 ++++++++++--------- .../test_functional/test_nn/test_losses.py | 32 +++++------ 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/ivy/functional/ivy/losses.py b/ivy/functional/ivy/losses.py index 5a7fa3c84cd52..89820300d16df 100644 --- a/ivy/functional/ivy/losses.py +++ b/ivy/functional/ivy/losses.py @@ -389,50 +389,55 @@ def sparse_cross_entropy( @inputs_to_ivy_arrays @handle_array_function def ssim_loss( - pred: Union[ivy.Array, ivy.NativeArray], ytrue: Union[ivy.Array, ivy.NativeArray] + true: Union[ivy.Array, ivy.NativeArray], + pred: Union[ivy.Array, ivy.NativeArray], + out: Optional[ivy.Array] = None, ) -> ivy.Array: """Calculate the Structural Similarity Index (SSIM) loss between two images. Parameters ---------- - pred: A 3D image tensor of shape (batch_size, channels, height, width). - ytrue: A 3D image tensor of shape (batch_size, channels, height, width). + true: A 4D image array of shape (batch_size, channels, height, width). + pred: A 4D image array of shape (batch_size, channels, height, width). Returns ------- - inv.array: The SSIM loss mesure similarity between the two images. + ivy.Array: The SSIM loss measure similarity between the two images. Examples -------- With :class:`ivy.Array` input: >>> import ivy - >>> x = ivy.ones((batch_size, channels, height, width)) - >>> y = ivy.zeros((batch_size, channels, height, width)) - >>> loss = ivy.ssim_loss(x, y) - >>> print(loss) - ivy.array(0.99989992) + >>> x = ivy.ones((5, 3, 28, 28)) + >>> y = ivy.zeros((5, 3, 28, 28)) + >>> ivy.ssim_loss(x, y) + ivy.array(0.99989986) """ - # Calculate the mean and variance of the two images. - mu_x = ivy.avg_pool2d(pred, (3, 3), (3, 3), "SAME") - mu_y = ivy.avg_pool2d(ytrue, (3, 3), (3, 3), "SAME") - - sigma_x2 = ivy.avg_pool2d(pred * pred, (3, 3), (3, 3), "SAME") - mu_x * mu_x - sigma_y2 = ivy.avg_pool2d(ytrue * ytrue, (3, 3), (3, 3), "SAME") - mu_y * mu_y + # Constants for stability + C1 = 0.01 ** 2 + C2 = 0.03 ** 2 - sigma_xy = ivy.avg_pool2d(pred * ytrue, (3, 3), (3, 3), "SAME") - mu_x * mu_y + # Calculate the mean of the two images + mu_x = ivy.avg_pool2d(pred, (3, 3), (1, 1), "SAME") + mu_y = ivy.avg_pool2d(true, (3, 3), (1, 1), "SAME") - # Add small constants to avoid division by zero. - C1 = 0.01**2 - C2 = 0.03**2 + # Calculate variance and covariance + sigma_x2 = ivy.avg_pool2d(pred * pred, (3, 3), (1, 1), "SAME") - mu_x * mu_x + sigma_y2 = ivy.avg_pool2d(true * true, (3, 3), (1, 1), "SAME") - mu_y * mu_y + sigma_xy = ivy.avg_pool2d(pred * true, (3, 3), (1, 1), "SAME") - mu_x * mu_y - # Calculate the SSIM index. + # Calculate SSIM ssim = ((2 * mu_x * mu_y + C1) * (2 * sigma_xy + C2)) / ( - (mu_x**2 + mu_y**2 + C1) * (sigma_x2 + sigma_y2 + C2) + (mu_x ** 2 + mu_y ** 2 + C1) * (sigma_x2 + sigma_y2 + C2) ) - ssim = 1 - ssim + # Convert SSIM to loss + ssim_loss_value = 1 - ssim - loss = ivy.mean(ssim) + # Return mean SSIM loss + ret = ivy.mean(ssim_loss_value) - return loss + if ivy.exists(out): + ret = ivy.inplace_update(out, ret) + return ret diff --git a/ivy_tests/test_ivy/test_functional/test_nn/test_losses.py b/ivy_tests/test_ivy/test_functional/test_nn/test_losses.py index 8530cff3e24d1..92d4407a8acdc 100644 --- a/ivy_tests/test_ivy/test_functional/test_nn/test_losses.py +++ b/ivy_tests/test_ivy/test_functional/test_nn/test_losses.py @@ -207,41 +207,37 @@ def test_sparse_cross_entropy( @handle_test( fn_tree="functional.ivy.ssim_loss", - dtype_and_p_pred=helpers.dtype_and_values( + dtype_and_true=helpers.dtype_and_values( available_dtypes=helpers.get_dtypes("float"), min_value=-1, max_value=1, - allow_inf=False, - min_num_dims=1, - max_num_dims=1, + min_num_dims=4, + max_num_dims=4, min_dim_size=2, - shape=(5,), ), - dtype_and_p_real=helpers.dtype_and_values( + dtype_and_pred=helpers.dtype_and_values( available_dtypes=helpers.get_dtypes("float"), min_value=-1, max_value=1, - allow_inf=False, - min_num_dims=1, - max_num_dims=1, + min_num_dims=4, + max_num_dims=4, min_dim_size=2, - shape=(5,), ), ) def test_ssim_loss( - dtype_and_p_pred, dtype_and_p_real, test_flags, backend_fw, fn_name, on_device + dtype_and_true, dtype_and_pred, test_flags, backend_fw, fn_name, on_device ): - dtype_p_pred, p_pred = dtype_and_p_pred - dtype_p_real, p_real = dtype_and_p_real + true_dtype, true = dtype_and_true + pred_dtype, pred = dtype_and_pred helpers.test_function( - input_dtypes=[dtype_p_pred, dtype_p_real], + input_dtypes=pred_dtype + true_dtype, test_flags=test_flags, backend_to_test=backend_fw, fn_name=fn_name, on_device=on_device, - args=(p_pred, p_real), - kwargs={}, - rtol=1e-02, - atol=1e-02, + true=true[0], + pred=pred[0], + rtol_=1e-02, + atol_=1e-02, )