Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ def update(self, annotation, prediction):
def evaluate(self, annotations, predictions):
tp_tn = np.add(self.tp, self.tn, dtype=float)
per_class = np.divide(tp_tn, self.counter, out=np.zeros_like(tp_tn, dtype=float), where=self.counter != 0)
if not self.calculate_average:
return per_class
average = np.sum(tp_tn) / np.sum(self.counter)

return [*per_class, average]
Expand Down Expand Up @@ -230,9 +232,11 @@ def update(self, annotation, prediction):
def evaluate(self, annotations, predictions):
precisions = self.precision.evaluate(annotations, predictions)
recalls = self.recall.evaluate(annotations, predictions)
# precision and recall share this config, so they only end with an average if calculate_average is set
num_classes = len(self.labels)

precision_add = np.add(precisions[:-1], recalls[:-1], dtype=float)
precision_multiply = np.multiply(precisions[:-1], recalls[:-1], dtype=float)
precision_add = np.add(precisions[:num_classes], recalls[:num_classes], dtype=float)
precision_multiply = np.multiply(precisions[:num_classes], recalls[:num_classes], dtype=float)

per_class = 2 * np.divide(
precision_multiply, precision_add, out=np.zeros_like(precision_multiply, dtype=float),
Expand Down
61 changes: 61 additions & 0 deletions tools/accuracy_checker/tests/test_multilabel_metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""
Copyright (c) 2026 Intel Corporation

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 pytest
from accuracy_checker.metrics.multilabel_recognition import F1Score, MultiLabelAccuracy
from accuracy_checker.representation import MultiLabelRecognitionAnnotation, MultiLabelRecognitionPrediction
from .common import DummyDataset


LABEL_MAP = {0: 'a', 1: 'b', 2: 'c'}


def evaluate(metric_cls, config):
metric = metric_cls(config, DummyDataset(label_map=LABEL_MAP))
annotations = [
MultiLabelRecognitionAnnotation('img1', [1, 0, 1]),
MultiLabelRecognitionAnnotation('img2', [0, 1, 1]),
]
predictions = [
MultiLabelRecognitionPrediction('img1', [1, 1, 1]),
MultiLabelRecognitionPrediction('img2', [0, 1, 0]),
]
for annotation, prediction in zip(annotations, predictions):
metric.update(annotation, prediction)
return list(metric.evaluate(annotations, predictions)), metric.meta['names']


class TestMultiLabelMetrics:
def test_f1_score_with_average(self):
result, names = evaluate(F1Score, {'type': 'f1-score'})
assert names == ['a', 'b', 'c', 'average']
assert result == pytest.approx([1.0, 2 / 3, 2 / 3, 0.75])

def test_f1_score_without_average_keeps_all_classes(self):
result, names = evaluate(F1Score, {'type': 'f1-score', 'calculate_average': False})
assert names == ['a', 'b', 'c']
assert result == pytest.approx([1.0, 2 / 3, 2 / 3])

def test_multi_accuracy_with_average(self):
result, names = evaluate(MultiLabelAccuracy, {'type': 'multi_accuracy'})
assert names == ['a', 'b', 'c', 'average']
assert result == pytest.approx([1.0, 0.5, 0.5, 2 / 3])

def test_multi_accuracy_without_average(self):
result, names = evaluate(MultiLabelAccuracy, {'type': 'multi_accuracy', 'calculate_average': False})
assert names == ['a', 'b', 'c']
assert result == pytest.approx([1.0, 0.5, 0.5])