名古屋出身ソフトウェアエンジニアのブログ

Grad-CAM を画像分類二項ロジスティック回帰に適用する変形

公開:
更新:

仕事上必要になり、OpenAI CLIP によるエンコーディングを使って画像分類をするロジスティック回帰モデルを作成していました。

反応位置を見るために、Grad-CAM を適用できそうだなと考えていたのですが、Grad-CAM は One-Hot エンコーディングによる出力を想定しているように見えるので、スカラー値で出力をする二項のロジスティック回帰だと、どう計算すれば良いか自明ではありませんでした(少なくとも私には)。

Grad-CAM は正方向への出力寄与を抽出しますが、ロジスティック回帰の陰性出力(ラベルが 0)への寄与をどう処理するかを、特に考える必要があります。

結論としては、ラベル 1(陽性)を Grad-CAM のターゲットとする場合は、そのまま Grad-CAM を適用し、ラベル 0(陰性)をターゲットとする場合は、出力確率を反転させて勾配を計算してやることで、正しく Grad-CAM を適用できます。

ソフトマックス関数とロジスティック回帰の関係については、以前の記事で触れています。

CLIP とロジスティック回帰の接続

モデル自体は、CLIP でエンコードしておいたデータセットを使って scikit-learn でロジスティック回帰を学習し、以下のように scikit-learn で学習したモデルを PyTorch にインポートしています。

PyTorch における接続は、CLIP モデルからロジスティック回帰モデルに順伝播させるだけです。

scikit-learn のロジスティック回帰モデルを自動微分できるよう PyTorch モジュール化
scikit-learn で学習したロジスティック回帰モデルに対して、勾配を利用した操作を行いたかったので、PyTorch へのインポートをやってみました。

大体のコード

勾配計算のための PyTorch コードは以下のようになります。

outputs = model(image)
model.zero_grad()
if logistic:
    # 二項ロジスティック回帰(One-Hot でない)の場合
    loss = outputs[:, 0]
    if logistic_negative:
        # ラベル 0 に対しては、出力を反転させる
        loss = 1.0 - loss
else:
    # 通常の One-Hot エンコーディングによる分類器はこっち
    loss = outputs[:, class_idx]
loss.backward()

# ... この下で該当中間層の出力と勾配を取得する ...

中間層において、出力や勾配を取得する際は、register_forward_hook, register_full_backward_hook あたりの API が使えます。

適用例

その1: 犬と猫

ResNet-50 CLIP の Image Encoding に対して、犬猫分類をするロジスティック回帰です。

ResNet-50 CLIP の最終畳み込み層に Grad-CAM を適用しています。

犬さんとねこさんですねぇ

犬さんとねこさんですねぇ

猫のヒートマップ

猫のヒートマップ

犬のヒートマップ

犬のヒートマップ

一枚の画像に犬と猫両方が入っているので、ロジスティック回帰自体はうまくいかないですが、反応部分のスポッティングには成功しています。

その2: プリキュア

プリキュア StyleGAN の遺産データで作成した分類機に適用しました。学習元データは写真じゃないですが、思いのほかいい感じです。

こちらも ResNet-50 CLIP です。

キュアビューティ(陰ラベル)

キュアビューティ(陰ラベル)

キュアトゥインクル(陽ラベル)

キュアトゥインクル(陽ラベル)