画像分類モデルの性能評価の計算(top-k accuracy)

3 min読了の目安(約3200字TECH技術記事

画像分類の性能評価には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の生成

以上の設定のoutputtargetを画像やモデルを使って生成するのは大変なので、次のようにランダムに生成します。

output = torch.rand((batch_size, num_of_class))
target = torch.randint(0, num_of_class, (batch_size,))

このときoutputtargetは次のような値を取ります。

output
[
  [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]
]
target
[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.topkdim方向について値の大きい順(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)で、今の場合は次のような値になります。

prediction
[
  [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関数です。