画像分類モデルの性能評価の計算(top-k accuracy)
画像分類の性能評価にはILSVRC 2012の検証データが使って計算したtop-1, top-5 accuracyという量が使われることが多いようです。top-k accuracyというのは、モデルが予測した分類のうち、確率の高いもの上位k位までに正解がある確率です。ここでは、VGGやMobileNetなどの画像分類モデルを想定しながら、top-k accuracyの計算方法について説明します。
使用できるGPU/CPUメモリに上限がある都合上、top-k accuracyはデータセットの画像をミニバッチに分けて正解数を計算し、最後に全画像数で割るようにして計算します。以降、以下の設定のもとで、各ミニバッチに対する正解数の計算方法をpytorchで説明します。
batch_size = 4 #ミニバッチサイズ
num_of_class = 10 #分類数
labels = [ str(v) for v in range(10)] #ラベル 以降の計算では使われません
k = 5 #top-k accuracyのk
num_of_class = 10
なのは、計算の途中経過を表示させやすくする都合だけの理由ですが、ImageNetから離れて、0から9の数字が1つだけ写った画像を分類していると考えても良いかもしれません。バッチサイズが異常に小さいのも同様の理由です。
batch_size
枚の画像データを入力として与えると、モデルは(batch_size, num_of_class)
の形状をした出力output
を返します。output[i][j]
にはi
番目の画像に数字j
が写っている確率が入っています。教師データtarget
は(batch_size,)
の形状をしており、target[i]
にはi
番目の画像のラベルのインデックスが入っています。
outputとtargetの生成
以上の設定のoutput
とtarget
を画像やモデルを使って生成するのは大変なので、次のようにランダムに生成します。
output = torch.rand((batch_size, num_of_class))
target = torch.randint(0, num_of_class, (batch_size,))
このときoutput
とtarget
は次のような値を取ります。
[
[0.1692, 0.1688, 0.8651, 0.0574, 0.8158, 0.3621, 0.6258, 0.2352, 0.6801, 0.9619],
[0.3163, 0.1547, 0.2093, 0.1790, 0.8335, 0.5940, 0.6706, 0.0315, 0.6133, 0.8345],
[0.1795, 0.8444, 0.8393, 0.5467, 0.8018, 0.2203, 0.4860, 0.7687, 0.8740, 0.7161],
[0.3900, 0.8673, 0.1001, 0.8594, 0.2875, 0.7443, 0.7490, 0.7478, 0.4602, 0.9961]
]
[0, 7, 4, 4]
outputから予測を計算する
output
の1軸方向で最も大きな確率のインデックスがモデルの予測になりますが、これとtarget
との一致する割合が(ミニバッチに対する)top-1 accuracyです。k
が1とは限らない場合は次のように、確率の上位k
個のインデックスを取得して計算します。
values, indices = output.topk(k, dim = 1, largest=True, sorted=True)
tensor.topk
はdim
方向について値の大きい順(largest=False
の場合は小さい順になる)にk
個だけとり、ソートした値values
とインデックスindices
を返すメソッドです。dimの向きですが、任意のtensor aに対して形式的にa[i_{0}][i_{1}][i_{2}][i_{3}]...
と書いたときのi_{s}
のs
が軸番号に一致します。
indices[i][j]
は落ち着いて考えると、i
番目の画像のj
番目に高い確率のインデックスだから、これを転置したindices.T[j][i]
はj
番目に高い確率で予想した、i
番目の画像のラベルのインデックスです。だからindices.T[0]
とtarget
がどれだけ一致するかを計算することで(このミニバッチにおける)top-1 accuracyを得ることができます。もっと一般的にindices.T[j]
とtarget
が一致する要素数を数えると、j
番目に高い確率で予想した分類の正解数が分かります。
このようにindices.T
はtop-k accuracyを計算に都合が良いのでprediction = indices.T
とします。prediction
の形状は(k, batch_size)
で、今の場合は次のような値になります。
[
[9, 9, 8, 9],
[2, 4, 1, 1],
[4, 6, 2, 3],
[8, 8, 4, 6],
[6, 5, 7, 7]
]
正解数を数える
prediction
の作り方からi != j
ならばpreciction[i][l] != prediction[j][l]
なので、k
番目までの予測の中に答えが入っている総数total
は、0 <= i and i <= k
の範囲でprediction[i] == target
の要素がTrue
になる数の総和になることが分かります。例えばk = 3
とすると、疑似コード的には次のようにして計算できます。
total = (prediction == [target, target, target]).astype(int).sum()
ここで登場した[target, target, target]
をpytorchの言葉書くと次のようになります。
targets = target.expand_as(prediction)
total
も次のように計算できます。
total = prediction.eq(targets) \
.reshape(-1) \
.float() \
.sum(0, keepdim=True) # => tensor([1.])
各ミニバッチのtotalの和を画像数で割ったものがtop-k accuracyです。以上の計算をまとめ、更にtop-1とtop-5を効率的に計算しているのが、pytorch/examplesのimagenetで定義されているaccuracy
関数です。
Discussion