Skip to content

Commit ffd907a

Browse files
feat: clipiqa metric (#259)
* feat: add CLIP Image Quality Assessment metric and update dependencies * Introduced `CLIPImageQualityAssessment` to the TorchMetrics enumeration. * Updated `MetricResult` to support results as a list for IQA metrics. * Added a new test for the `clipiqa` metric to validate its functionality. * Included `piq` as a dependency in `pyproject.toml`. * fix: update pairwise metric handling in tests * Modified the test for `TorchMetricWrapper` to include `clipiqa` alongside `arniqa` for pairwise call types. * Adjusted assertions to ensure correct call type behavior for both metrics. --------- Co-authored-by: davidberenstein1957 <david.m.berenstein@gmail.com>
1 parent 9542bc9 commit ffd907a

4 files changed

Lines changed: 28 additions & 12 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ dependencies = [
115115
"hqq==0.2.6",
116116
"torchao",
117117
"llmcompressor",
118-
"gliner; python_version >= '3.10'"
118+
"gliner; python_version >= '3.10'",
119+
"piq",
119120

120121
]
121122

src/pruna/evaluation/metrics/metric_torch.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
StructuralSimilarityIndexMeasure,
3232
)
3333
from torchmetrics.image.arniqa import ARNIQA
34+
from torchmetrics.multimodal.clip_iqa import CLIPImageQualityAssessment
3435
from torchmetrics.multimodal.clip_score import CLIPScore
3536
from torchmetrics.text import Perplexity
3637
from torchvision import transforms
@@ -167,11 +168,7 @@ class TorchMetrics(Enum):
167168
"""
168169

169170
fid = (partial(FrechetInceptionDistance), fid_update, "gt_y")
170-
accuracy = (
171-
partial(Accuracy),
172-
None,
173-
"y_gt",
174-
)
171+
accuracy = (partial(Accuracy), None, "y_gt")
175172
perplexity = (partial(Perplexity), None, "y_gt")
176173
clip_score = (partial(CLIPScore), None, "y_x")
177174
precision = (partial(Precision), None, "y_gt")
@@ -180,6 +177,7 @@ class TorchMetrics(Enum):
180177
ssim = (partial(StructuralSimilarityIndexMeasure), ssim_update, "pairwise_y_gt")
181178
lpips = (partial(LearnedPerceptualImagePatchSimilarity), lpips_update, "pairwise_y_gt")
182179
arniqa = (partial(ARNIQA), arniqa_update, "y")
180+
clipiqa = (partial(CLIPImageQualityAssessment), None, "y")
183181

184182
def __init__(self, *args, **kwargs) -> None:
185183
self.tm = self.value[0]
@@ -341,10 +339,17 @@ def compute(self) -> Any:
341339
The computed metric value.
342340
"""
343341
result = self.metric.compute()
342+
343+
# Normally we have a single score for each metric for the entire dataset.
344+
# For IQA metrics we have a single score per image, so we need to convert the tensor to a list.
345+
if isinstance(result, Tensor):
346+
result_value = result.item() if result.numel() == 1 else result.tolist()
347+
else:
348+
result_value = result
344349
return MetricResult(
345350
self.metric_name,
346351
self.__dict__.copy(),
347-
result.item() if isinstance(result, Tensor) else result,
352+
result_value,
348353
)
349354

350355

src/pruna/evaluation/metrics/result.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from __future__ import annotations
1515

1616
from dataclasses import dataclass
17-
from typing import Any, Dict
17+
from typing import Any, Dict, List
1818

1919

2020
@dataclass
@@ -34,7 +34,7 @@ class MetricResult:
3434

3535
name: str
3636
params: Dict[str, Any]
37-
result: float | int
37+
result: float | int | List[float | int]
3838

3939
def __str__(self) -> str:
4040
"""

tests/evaluation/test_torch_metrics.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ def test_clip_score(dataloader_fixture: Any) -> None:
5252
score = metric.compute()
5353
assert score.result > 0.0 and score.result < 100.0
5454

55+
@pytest.mark.cpu
56+
@pytest.mark.parametrize("dataloader_fixture", ["LAION256"], indirect=True)
57+
def test_clipiqa(dataloader_fixture: Any) -> None:
58+
"""Test the clipiqa."""
59+
metric = TorchMetricWrapper("clipiqa")
60+
x, gt = next(iter(dataloader_fixture))
61+
metric.update(x, gt, gt)
62+
score = metric.compute()
63+
assert score.result > 0.0 and score.result < 1.0
64+
5565

5666
@pytest.mark.cpu
5767
@pytest.mark.parametrize("dataloader_fixture", ["ImageNet"], indirect=True)
@@ -79,14 +89,14 @@ def test_check_call_type(metric: str, call_type: str):
7989
kwargs = {}
8090
if metric in ['accuracy', 'recall', 'precision']:
8191
kwargs = {"task": "multiclass", "num_classes": 1000}
82-
if metric == "arniqa" and call_type == "pairwise":
92+
if metric in ["arniqa", "clipiqa"] and call_type == "pairwise":
8393
with pytest.raises(Exception):
8494
TorchMetricWrapper(metric, call_type=call_type, **kwargs)
8595
return
8696
metric = TorchMetricWrapper(metric, call_type=call_type, **kwargs)
87-
if call_type == "pairwise" and metric.metric_name != "arniqa":
97+
if call_type == "pairwise" and metric.metric_name not in ["arniqa", "clipiqa"]:
8898
assert metric.call_type.startswith("pairwise")
89-
elif metric.metric_name == "arniqa":
99+
elif metric.metric_name in ["arniqa", "clipiqa"]:
90100
assert metric.call_type == "y"
91101
else:
92102
assert not metric.call_type.startswith("pairwise")

0 commit comments

Comments
 (0)