セグメンテーションAIの評価指標:IoUとDice係数の算出アルゴリズム(Python)

セグメンテーション評価の「正解」:IoUとDice係数の数理的差異とPython実装の最適解

約15分で読めます
文字サイズ:
セグメンテーション評価の「正解」:IoUとDice係数の数理的差異とPython実装の最適解
目次

この記事の要点

  • IoUとDice係数の数理的背景と定義
  • セグメンテーションAI評価におけるAccuracy偏重の危険性
  • 実務で役立つPython(NumPy)による高速な実装方法

はじめに:その「精度99%」は本当に信頼できるのか

AIエンジニアとして製造業の外観検査などのプロジェクトに携わる中で、しばしば「数字の罠」が課題となることがあります。

「モデルの精度(Accuracy)が99%を超えました。これでリリース可能です」

モデルの評価レポートを確認する際、まず行うべきはその内訳の分析です。多くの場合、Accuracy(正解率)だけを追求したモデルは現場では実用的ではありません。なぜなら、検出すべき「欠陥」や「病変」は、画像全体のわずか数パーセント、あるいはそれ以下のピクセル領域にしか存在しないからです。

もし画像全体の99.5%が背景(正常部)で、残り0.5%が欠陥だった場合、AIが「すべてのピクセルは背景である」と判定しても、計算上のAccuracyは99.5%になります。数字上は優秀ですが、欠陥検出システムとしては価値ゼロです。これが、セグメンテーションタスクにおいてAccuracyが機能しない典型的な理由です。

AIエンジニアとして、工場のラインや医療現場で「見逃し」が許されないシステムを構築する際、「何を測るか(評価指標)」を決めることは、「何を解決したいか(ビジネス課題)」を定義することと同義であるという事実は重要です。実用的な精度と速度を両立するモデル設計を行うためには、適切な指標の選定が不可欠です。

本記事では、セグメンテーションの評価におけるデファクトスタンダードである「IoU(Intersection over Union)」と「Dice係数」について、アルゴリズムの原理から実装まで段階的に解説します。その数理的な性質の違い、実装上の注意点、そして損失関数として学習に組み込む際のアプローチまでを深く掘り下げ、現場で「使える」評価システムの構築を目指します。

なぜ「正解率(Accuracy)」では不十分なのか:不均衡データの罠

セグメンテーションは、画像の全ピクセルに対してクラス分類を行うタスクです。一見すると、通常の画像分類と同様に正解率(Accuracy)で評価できるように思えます。しかし、そこには「クラス不均衡(Class Imbalance)」という大きな落とし穴があります。

背景クラスが支配的な画像での評価パラドックス

具体的な数値で考えてみましょう。例えば、半導体ウェハーの欠陥検査を行うとします。入力画像は1000×1000ピクセル、つまり合計100万ピクセルです。その中に、わずか100ピクセル(10×10)の微細なキズがあるとします。

  • 全ピクセル数: 1,000,000
  • 背景ピクセル数: 999,900 (99.99%)
  • 欠陥ピクセル数: 100 (0.01%)

ここで、モデルAが「画像内に欠陥は一切ない(すべて背景)」と予測したとします。

  • 正解数: 999,900(背景のみ正解)
  • Accuracy: 999,900 / 1,000,000 = 99.99%

驚異的な高精度です。しかし、このモデルはキズを完全に見逃しており、検知システムとしての役割を全く果たしていません。これが「Accuracyのパラドックス」です。背景クラスが圧倒的に多いセグメンテーションタスクでは、多数派クラス(背景)の正解率が全体のスコアを支配してしまい、本当に検出したい少数派クラス(欠陥)の性能が隠蔽されてしまうのです。

ピクセル単位の評価がビジネス価値に直結する理由

ビジネスの現場では、「背景を正しく背景と言えること」よりも、「欠陥を正しく欠陥と言えること」、さらには「欠陥の形状や大きさを正確に捉えていること」が重要視されます。

例えば、自動運転における道路領域のセグメンテーションを想像してください。道路(走行可能領域)を少しでも狭く認識すれば安全側に倒れますが、歩道や対向車線を道路と誤認(過検出)すれば事故に直結します。また、腫瘍の摘出手術支援AIであれば、腫瘍の境界線を1ミリでも外側に誤れば正常な臓器を傷つけ、内側に誤れば腫瘍を取り残すリスクになります。

つまり、単に「合っているピクセルの数」を数えるのではなく、「予測領域と正解領域がどれだけ重なっているか(集合の類似度)」を測る指標が必要不可欠なのです。ここで登場するのが、IoUとDice係数です。

IoUとDice係数がデファクトスタンダードである数学的背景

これら二つの指標が選ばれる理由は、どちらも「背景クラスの影響を除外(または軽減)して評価できる」点にあります。数式上、これらは予測された領域(Positive)と正解領域(True)の関係性のみに焦点を当て、True Negative(背景を正しく背景と予測した部分)が膨大であっても、スコアが不当に高くならないように設計されています。

次章では、これら二つの指標が具体的にどのように計算され、どのような違いがあるのかを、数理的な視点から解剖していきます。

IoU (Jaccard Index) と Dice係数 (F1 Score) の本質的理解

なぜ「正解率(Accuracy)」では不十分なのか:不均衡データの罠 - Section Image

IoUとDice係数は、どちらも「0から1の間」の値を取り、1に近いほど予測と正解が一致していることを示します。しかし、同じモデルを評価しても、この二つの数値は一致しません。一般的に、Dice係数の方がIoUよりも高い値が出ます。この性質を理解していないと、「Diceで0.9出たから優秀だ」と思っていたら、IoU基準では0.82程度しかなく、要件を満たせていなかったという事態に陥ります。

IoU(Intersection over Union)の定義とペナルティ特性

IoUは別名Jaccard Indexとも呼ばれ、集合論における「積集合(共通部分)と和集合の比率」です。

$ IoU = \frac{|A \cap B|}{|A \cup B|} = \frac{TP}{TP + FP + FN} $

ここで、

  • $A$: 正解領域(Ground Truth)
  • $B$: 予測領域(Prediction)
  • $TP$ (True Positive): 正解かつ予測もした領域(共通部分)
  • $FP$ (False Positive): 予測したが正解ではない領域(過検出)
  • $FN$ (False Negative): 正解だが予測しなかった領域(見逃し)

IoUの特徴は、分母に「和集合($A \cup B$)」を取る点です。予測が外れた部分($FP$や$FN$)が増えると、分母が大きくなり、分子($TP$)が変わらなくてもスコアは急激に低下します。つまり、IoUは誤りに対して非常に厳しいペナルティを与える指標と言えます。

Dice係数の定義と調和平均としての性質

一方、Dice係数(Sørensen-Dice coefficient)は、F1スコアと等価な指標であり、以下のように定義されます。

$ Dice = \frac{2 |A \cap B|}{|A| + |B|} = \frac{2TP}{2TP + FP + FN} $

分母は「正解領域の画素数と予測領域の画素数の単純な和」です。IoUと比較すると、分母における誤り($FP, FN$)の重みが相対的に小さくなっていることがわかります(IoUの分母は $TP+FP+FN$ ですが、Diceの分母を2で割って考えると $TP + 0.5(FP+FN)$ となり、誤りの重みが半分に見えます)。

Dice係数は、適合率(Precision)と再現率(Recall)の調和平均でもあります。調和平均は、極端に低い値に引きずられる性質があるため、PrecisionとRecallのバランスが取れていない場合(片方だけ極端に高い場合など)に値を低く抑える特性がありますが、IoUと比較すると「甘め」の数値が出る傾向にあります。

両者の数理的関係性と変換式

実は、IoUとDice係数の間には明確な代数的関係が存在します。一方の値が分かれば、もう一方を算出することが可能です。

$ Dice = \frac{2 \times IoU}{1 + IoU} $

$ IoU = \frac{Dice}{2 - Dice} $

この変換式をグラフに描くとわかりますが、IoUが0から1へ増加するとき、Diceは常にIoU以上の値を取ります。例えば、IoUが0.5のとき、Dice係数は0.666... になります。

これは非常に重要なポイントです。論文やカタログスペックを読む際、「精度0.7」と書かれていても、それがIoUなのかDiceなのかで意味合いが全く異なります。IoU=0.7ならDice≈0.82に相当し、かなり高精度ですが、Dice=0.7ならIoU≈0.54であり、領域の半分程度しか重なっていないことになります。

どちらを選ぶべきか?ケース別選定ガイド

では、実務ではどちらを使うべきでしょうか。一般的な指針は以下の通りです。

  1. 厳密な形状一致が求められる場合(外観検査、自動運転)
    IoUを推奨します。IoUは「最悪のケース」に対して敏感です。わずかなズレや過検出も許容したくない場合、より厳しい指標であるIoUでモデルを評価し、その数値を改善していくアプローチが品質担保に繋がります。

  2. 不均衡データでの学習安定性を重視する場合(医療画像など)
    学習時の損失関数としてはDice(またはSoft Dice Loss)が好まれる傾向にあります。Dice係数は勾配がIoUよりもなだらかであり、学習が安定しやすいという特性があるためです。ただし、最終的な評価レポートにはIoUも併記すべきです。

  3. コンペティションや学術研究
    タスクのレギュレーションに従うのが大前提ですが、PASCAL VOCなどの主要なデータセットではmIoU (mean IoU) が標準指標となっています。

【Python実装】NumPyによる高速かつ正確な算出アルゴリズム

IoU (Jaccard Index) と Dice係数 (F1 Score) の本質的理解 - Section Image

理論を理解したところで、実装に移りましょう。ここで紹介するのは、実務で使える「高速」かつ「堅牢」なコードです。初心者が書きがちな二重forループによるピクセル走査は、高解像度画像や大量のデータセットに対しては計算時間がかかりすぎて実用的ではありません。NumPyのベクトル演算をフル活用し、精度とスピードのトレードオフを最適化します。

混同行列(Confusion Matrix)を用いた一括計算

セグメンテーションの評価は、本質的にはピクセルごとの分類問題です。したがって、全ピクセルに対する混同行列を作成すれば、そこからIoUやDiceを一括で計算できます。

import numpy as np

def compute_confusion_matrix(y_true, y_pred, num_classes):
    """
    高速に混同行列を計算する関数
    
    Args:
        y_true (np.array): 正解ラベル (H, W) または (N, H, W)
        y_pred (np.array): 予測ラベル (H, W) または (N, H, W)
        num_classes (int): クラス数
        
    Returns:
        np.array: 混同行列 (num_classes, num_classes)
    """
    # 1次元配列にフラット化
    y_true = y_true.flatten()
    y_pred = y_pred.flatten()
    
    # ラベルが範囲内にあるか確認(念のため)
    mask = (y_true >= 0) & (y_true < num_classes)
    
    # bincountを使って高速に集計
    # confusion matrixのインデックス: num_classes * true_label + pred_label
    cm = np.bincount(
        num_classes * y_true[mask] + y_pred[mask],
        minlength=num_classes2
    ).reshape(num_classes, num_classes)
    
    return cm

def compute_iou_from_cm(cm):
    """
    混同行列からIoUを計算
    """
    # TP: 対角成分
    tp = np.diag(cm)
    
    # FP: 列の和 - TP
    fp = cm.sum(axis=0) - tp
    
    # FN: 行の和 - TP
    fn = cm.sum(axis=1) - tp
    
    # IoU = TP / (TP + FP + FN)
    # ゼロ除算回避のための微小値(epsilon)
    epsilon = 1e-6
    denominator = tp + fp + fn
    iou = tp / (denominator + epsilon)
    
    return iou

なぜこの実装が良いのか:
np.bincountを使用することで、Pythonレベルのループを完全に排除しています。これはC言語レベルで最適化された操作であり、数百万ピクセルの画像であってもミリ秒オーダーで処理が完了します。また、混同行列を一度作ってしまえば、Accuracy、Precision、Recall、Diceなども全てここから計算コストほぼゼロで導出できます。

多クラスセグメンテーションへの拡張(Macro vs Micro)

クラスが複数ある場合(例:背景、人、車、信号)、評価指標をどうまとめるかが問題になります。

  • mIoU (Mean IoU): クラスごとのIoUを計算し、その単純平均を取る方法(Macro Average的アプローチ)。クラス間のサンプル数の偏りを無視して、全てのクラスを平等に扱います。小さな物体を見逃さないためにはこちらが重要です。
  • Frequency Weighted IoU: クラスの出現頻度(ピクセル数)で重み付けして平均を取る方法。背景クラスなどの支配的なクラスの影響を受けやすいため、精度が高く見えがちです。注意が必要です。
def compute_miou(y_true, y_pred, num_classes, ignore_background=False):
    cm = compute_confusion_matrix(y_true, y_pred, num_classes)
    iou_per_class = compute_iou_from_cm(cm)
    
    if ignore_background:
        # クラス0を背景として除外する場合
        iou_per_class = iou_per_class[1:]
        
    # NaN(正解にも予測にも存在しなかったクラス)を除外して平均
    valid_mask = np.isfinite(iou_per_class)
    miou = np.mean(iou_per_class[valid_mask])
    
    return miou, iou_per_class

ゼロ除算を防ぐスムージング項(Smoothing)の実装テクニック

実装コードにある epsilon = 1e-6 は非常に重要です。正解にも予測にもそのクラスが存在しない場合、分母が0になります。このとき、プログラムをクラッシュさせたり NaN を発生させたりしないために微小値を加えます。

また、スムージングにはもう一つの役割があります。損失関数として使用する場合、smooth = 1.0 などを分子と分母の両方に加えることがあります(Laplace smoothing)。これにより、非常に小さな領域に対する過敏な反応を抑え、学習を安定させる効果があります。

損失関数(Loss Function)としての活用と微分可能性

損失関数(Loss Function)としての活用と微分可能性 - Section Image 3

ここまで評価指標としてのIoU/Diceについて解説してきましたが、これらをニューラルネットワークの学習をガイドする「損失関数」として使いたいという要望は自然です。AccuracyではなくIoUを最大化したいなら、IoUそのものを目的関数にすべきだからです。

しかし、通常のIoUやDice計算は、ピクセルごとの判定(0か1か)に基づく離散的な操作であり、そのままでは微分不可能です。バックプロパゲーションを行うためには、これを微分可能な形にする必要があります。

評価指標をそのまま学習に使う:Dice LossとIoU Loss

ここで登場するのが「Soft Dice」や「Soft IoU」という考え方です。モデルの出力(SigmoidやSoftmaxを通した確率値)を、0/1に二値化せずにそのまま計算式に用います。

Soft Dice Loss:
$ L_{Dice} = 1 - \frac{2 \sum_{i} p_i g_i + \epsilon}{\sum_{i} p_i^2 + \sum_{i} g_i^2 + \epsilon} $

ここで、$p_i$ は画素 $i$ が対象クラスである予測確率、$g_i$ は正解ラベル(0 or 1)です。確率値を用いることで、関数は滑らかになり、微分が可能になります。

Cross Entropyとの組み合わせ戦略(Combo Loss)

実務的なモデル構築において、単独のDice Lossだけを使うことはあまり推奨されません。Dice Lossは勾配が不安定になる可能性があり、学習初期になかなか収束しないケースがあるからです。

ベストプラクティスは、画素ごとの分類精度を高めるCross Entropy Lossと、領域の重なりを最大化するDice Lossを組み合わせることです。

$ L_{total} = \alpha L_{CE} + (1 - \alpha) L_{Dice} $

このハイブリッドな損失関数(Combo Lossなどと呼ばれます)を使用することで、ピクセル単位の正しさと、全体的な形状の一致度の両方をバランスよく最適化できます。実務の現場では、この組み合わせによりmIoUが向上し、境界線のアーティファクトが減少する結果が報告されています。

評価結果のレポートと品質保証(QA)プロセスへの統合

最後に、算出した数値をどう解釈し、プロジェクトの意思決定に活かすかについて述べます。データから仮説を立て、実験で検証するサイクルを回す上で、評価結果の正しい解釈は欠かせません。

指標の「閾値」をどう設定するか

「IoUがいくつなら合格か」という疑問が生じることがよくあります。正解はタスクによりますが、一般的な目安(PASCAL VOCなどのコンペ基準)としては以下の通りです。

  • mIoU > 0.5: 最低限の重なりがある(Good)。
  • mIoU > 0.7: 実用レベルで良好(Very Good)。
  • mIoU > 0.9: 人間レベル、あるいはそれ以上(Excellent)。

ただし、医療や精密検査では0.5では不十分です。0.85〜0.9以上が求められることもあります。重要なのは、プロジェクト開始時に「人間がアノテーションしたデータ同士のIoU」を測定しておくこと**です。人間同士でもIoUは0.8〜0.9程度になることが多いと考えられます。これがそのタスクにおける「理論上の上限値」となり、非現実的な目標設定を防ぐことができます。

ワーストケース分析によるモデルの弱点特定

平均値(mIoU)だけで判断するのは危険です。mIoUが0.8でも、特定の条件下で0.1になるケースが含まれているかもしれません。

QAプロセスでは、必ずIoUのヒストグラムを作成してください。そして、IoUが低い下位5%の画像を抽出し、目視確認します。「暗い画像で弱い」「重なり合った物体に弱い」「小さな物体が見えていない」といった具体的な弱点が見えてきます。この分析結果を次のデータ収集やオーグメンテーション戦略にフィードバックするサイクルが重要です。

CI/CDパイプラインへの自動評価組み込み例

一般的なベストプラクティスとして推奨されるのは、モデルの学習が終わるたびに、テストデータセットに対する評価スクリプトを自動実行し、以下のレポートを出力するパイプラインを構築することです。

  1. クラス別IoU / Diceスコア一覧
  2. 推論速度(FPS)
  3. ワーストケース画像(Top 5)と正解・予測のオーバーレイ表示

これをGitHubのプルリクエストや実験管理ツール(MLflowなど)に自動投稿させることで、チーム全体が「数値」と「実際の見え方」の両方に基づいて客観的な議論ができるようになります。

まとめ:定量的な「ものさし」がAIの信頼を作る

セグメンテーションAIの開発において、正解率(Accuracy)の高さに安住することは、砂上の楼閣を築くようなものです。不均衡データという現実に対し、IoUやDice係数といった適切な「ものさし」を持つこと、そしてそれを正しく実装し運用することだけが、現場で信頼されるAIを生み出します。

今回解説したPython実装や損失関数のテクニックは、実用的な精度と速度を両立するモデル設計においてすぐに活用できるものです。まずは評価スクリプトを見直し、計算コストの最適化を図るところから始めることを推奨します。

AI技術は日進月歩ですが、データから仮説を立て、実験で検証するサイクルを回す上で、こうした評価の基礎理論は不可欠です。より深い実装テクニックや、最新のセグメンテーションモデル(Segment Anything Modelなど)の評価事例については、様々な情報源から入手し、継続的な改善に役立ててください。

セグメンテーション評価の「正解」:IoUとDice係数の数理的差異とPython実装の最適解 - Conclusion Image

コメント

コメントは1週間で消えます
コメントを読み込み中...