From 84e1c93ffbd6c43c84d229e5235e81159c5e8a25 Mon Sep 17 00:00:00 2001 From: Prianka Liz Kariat Date: Thu, 2 Feb 2023 17:22:56 +0530 Subject: [PATCH] Added MPPCosineSimilarity --- mediapipe/tasks/ios/components/utils/BUILD | 33 +++++++ .../utils/sources/MPPCosineSimilarity.h | 48 ++++++++++ .../utils/sources/MPPCosineSimilarity.mm | 89 +++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 mediapipe/tasks/ios/components/utils/BUILD create mode 100644 mediapipe/tasks/ios/components/utils/sources/MPPCosineSimilarity.h create mode 100644 mediapipe/tasks/ios/components/utils/sources/MPPCosineSimilarity.mm diff --git a/mediapipe/tasks/ios/components/utils/BUILD b/mediapipe/tasks/ios/components/utils/BUILD new file mode 100644 index 00000000..c9f82d1d --- /dev/null +++ b/mediapipe/tasks/ios/components/utils/BUILD @@ -0,0 +1,33 @@ +# Copyright 2023 The MediaPipe 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. + +package(default_visibility = ["//mediapipe/tasks:internal"]) + +licenses(["notice"]) + +objc_library( + name = "MPPCosineSimilarity", + srcs = ["sources/MPPCosineSimilarity.mm"], + hdrs = ["sources/MPPCosineSimilarity.h"], + copts = [ + "-ObjC++", + "-std=c++17", + "-x objective-c++", + ], + deps = [ + "//mediapipe/tasks/ios/common:MPPCommon", + "//mediapipe/tasks/ios/common/utils:MPPCommonUtils", + "//mediapipe/tasks/ios/components/containers:MPPEmbedding", + ] +) diff --git a/mediapipe/tasks/ios/components/utils/sources/MPPCosineSimilarity.h b/mediapipe/tasks/ios/components/utils/sources/MPPCosineSimilarity.h new file mode 100644 index 00000000..864baf16 --- /dev/null +++ b/mediapipe/tasks/ios/components/utils/sources/MPPCosineSimilarity.h @@ -0,0 +1,48 @@ +// Copyright 2022 The MediaPipe 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. + +#import + +#import "mediapipe/tasks/ios/components/containers/sources/MPPEmbedding.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Utility class for computing cosine similarity between `MPPEmbedding` objects. */ +NS_SWIFT_NAME(CosineSimilarity) + +@interface MPPCosineSimilarity : NSObject + +- (instancetype)init NS_UNAVAILABLE; + ++ (instancetype)new NS_UNAVAILABLE; + +/** Utility function to compute[cosine similarity](https://en.wikipedia.org/wiki/Cosine_similarity) + * between two `MPPEmbedding` objects. + * + * @param embedding1 One of the two `MPPEmbedding`s between whom cosine similarity is to be + * computed. + * @param embedding2 One of the two `MPPEmbedding`s between whom cosine similarity is to be + * computed. + * @param error An optional error parameter populated when there is an error in calculating cosine + * similarity between two embeddings. + * + * @return An `NSNumber` which holds the cosine similarity of type `double`. + */ ++ (nullable NSNumber *)computeBetweenEmbedding1:(MPPEmbedding *)embedding1 + andEmbedding2:(MPPEmbedding *)embedding2 + error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/mediapipe/tasks/ios/components/utils/sources/MPPCosineSimilarity.mm b/mediapipe/tasks/ios/components/utils/sources/MPPCosineSimilarity.mm new file mode 100644 index 00000000..dfbc54e0 --- /dev/null +++ b/mediapipe/tasks/ios/components/utils/sources/MPPCosineSimilarity.mm @@ -0,0 +1,89 @@ +// Copyright 2022 The MediaPipe 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. + +#import "mediapipe/tasks/ios/components/utils/sources/MPPCosineSimilarity.h" + +#import "mediapipe/tasks/ios/common/sources/MPPCommon.h" +#import "mediapipe/tasks/ios/common/utils/sources/MPPCommonUtils.h" + +#include + +@implementation MPPCosineSimilarity + ++ (nullable NSNumber *)computeBetweenVector1:(NSArray *)u + andVector2:(NSArray *)v + isFloat:(BOOL)isFloat + error:(NSError **)error { + if (u.count != v.count) { + [MPPCommonUtils + createCustomError:error + withCode:MPPTasksErrorCodeInvalidArgumentError + description:[NSString stringWithFormat:@"Cannot compute cosine similarity between " + @"embeddings of different sizes (%d vs %d)", + u.count, v.count]]; + return nil; + } + + __block double dotProduct = 0.0; + __block double normU = 0.0; + __block double normV = 0.0; + + [u enumerateObjectsUsingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) { + double uVal = 0.0; + double vVal = 0.0; + + if (isFloat) { + uVal = num.floatValue; + vVal = v[idx].floatValue; + } else { + uVal = num.charValue; + vVal = v[idx].charValue; + } + + dotProduct += uVal * vVal; + normU += uVal * uVal; + normV += vVal * vVal; + }]; + + return [NSNumber numberWithDouble:dotProduct / sqrt(normU * normV)]; +} + ++ (nullable NSNumber *)computeBetweenEmbedding1:(MPPEmbedding *)embedding1 + andEmbedding2:(MPPEmbedding *)embedding2 + error:(NSError **)error { + BOOL isFloat; + + if (embedding1.floatEmbedding && embedding2.floatEmbedding) { + return [MPPCosineSimilarity computeBetweenVector1:embedding1.floatEmbedding + andVector2:embedding2.floatEmbedding + isFloat:YES + error:error]; + } + + if (embedding1.quantizedEmbedding && embedding2.quantizedEmbedding) { + return [MPPCosineSimilarity computeBetweenVector1:embedding1.quantizedEmbedding + andVector2:embedding2.quantizedEmbedding + isFloat:NO + error:error]; + } + + [MPPCommonUtils + createCustomError:error + withCode:MPPTasksErrorCodeInvalidArgumentError + description: + @"Cannot compute cosine similarity between quantized and float embeddings."]; + return nil; +} + +@end