segmentationの評価手法の一つであるmIoUが計算していることがよくわからなかったので絵にしてみました。
式1
$$
mIoU = \frac{1}{k} \sum_{i=1}^k \frac{N_{ii}}{\sum_{i=1}^k N_{ij} + \sum_{j=1}^k N_{ji} - N_{ii}}
$$
- $N_{ij}$: 正解クラスが$i$であるピクセルをモデルがクラスjに分類した数
- $k$: クラス数
まとめ
Accuracyに対するメリット
- クラスの偏りがあっても不当な評価値になりにくい
- 例) Accuracyではほとんどのピクセルが1になるようなサンプルでは、全ピクセルで1を出力するモデルでも評価が高くなってしまう。
- mIoUではざっくりクラスごとに評価してから平均するので、2のラベルの予測が全くできていないとほとんどのピクセルがが1であっても大きく評価値に影響する。
実装
import numpy as np
from sklearn.metrics import confusion_matrix
def mIoU(true: np.ndarray, pred: np.ndarray) -> float:
"""Calculate mIoU between 2 class label arrays
Parameters
----------
true : np.ndarray
A 2d class label array
pred : np.ndarray
A 2d class label array of the same shape of true
Return
------
float
mIoU
"""
c_matrix = confusion_matrix(true.ravel(), pred.ravel())
intersection = np.diag(c_matrix)
union = np.sum(c_matrix, axis=0) + np.sum(c_matrix, axis=1) - intersection
iou = intersection / union
miou = np.mean(iou)
return miou
# Test
true = np.zeros((5, 5))
true[1:3, 1:3] = 1
pred = np.zeros((5, 5))
pred[2:4, 2:4] = 1
assert mIoU(true, pred) == np.mean([1 / 7, (5 * 5 - 7) / (5 * 5 - 1)])
print("true")
print(true)
print("pred")
print(pred)
print("mIoU = %.2f" % mIoU(true, pred))
理解や実装に誤りありましたらご指摘いただけると助かります。
参考
- Intersection over Union (IoU) for object detection - PyImageSearch
- 5. 実践編: MRI画像のセグメンテーション — メディカルAI専門コース オンライン講義資料 documentation