Main Content

このページの翻訳は最新ではありません。ここをクリックして、英語の最新版を参照してください。

クラス活性化マッピングを使用したネットワークの予測の調査

この例では、クラス活性化マッピング (CAM) を使用して、深層畳み込みニューラル ネットワークによるイメージ分類の予測を調査し説明する方法を示します。

深層学習ネットワークは、ネットワークに何を学習させ、ネットワークへの入力のどの部分がネットワークの予測の要因になったかを理解する方法が提供されないため、"ブラックボックス" のように考えられることがよくあります。多くの場合、モデルが失敗して誤った予測が与えられても、警告や説明は一切なく完了します。クラス活性化マッピング [1] は、畳み込みニューラル ネットワークの予測についての視覚的な説明を取得するために使用できる手法の 1 つです。誤っていて妥当でないように見える予測でも、相応の理由がある可能性があります。クラス活性化マッピングを使用すると、入力イメージの特定の部分がネットワークを "混乱" させ、誤った予測を行う原因になっていないかどうかを確認できます。

クラス活性化マッピングを使用して学習セットのバイアスを識別し、モデルの精度を向上させることが可能です。ネットワークが誤った特徴を基に予測しているとわかった場合は、より適切なデータを収集してネットワークをさらにロバストにすることができます。たとえば、猫と犬のイメージを区別するようにネットワークに学習させると仮定します。ネットワークは学習セットに対しては高精度ですが、実世界の例に対しては性能が劣ります。学習の例にクラス活性化マッピングを使用することで、ネットワークがイメージ内の犬や猫ではなく、背景を基に予測していることがわかりました。次に、猫の写真は背景がすべて赤く、犬の写真は背景がすべて緑であり、学習中に背景の色をネットワークに学習させてしまったことに気付きます。そのため、このバイアスをもたない新しいデータを収集することができます。

この例のクラス活性化マップでは、入力イメージのどの領域が予測クラス mouse に最も寄与しているかがわかります。赤い領域が最も寄与しています。

事前学習済みのネットワークと Web カメラの読み込み

イメージ分類用に事前学習済みの畳み込みニューラル ネットワークを読み込みます。SqueezeNet、GoogLeNet、ResNet-18、および MobileNet-v2 は比較的高速なネットワークです。SqueezeNet は最も高速なネットワークで、クラス活性化マッピングの解像度は他のネットワークの 4 倍です。AlexNet、VGG-16、および VGG-19 のようにネットワークの最後に複数の全結合層をもつネットワークでは、クラス活性化マッピングを使用できません。

netName = "squeezenet";
net = eval(netName);

webcam オブジェクトを作成して Web カメラに接続します。

camera = webcam;

イメージの入力サイズとネットワークの出力クラスを抽出します。この例の終わりに定義されている補助関数 activationLayerName は、活性化を抽出する層の名前を返します。この層はネットワークの最後の畳み込み層に続く ReLU 層です。

inputSize = net.Layers(1).InputSize(1:2);
classes = net.Layers(end).Classes;
layerName = activationLayerName(netName);

クラス活性化マッピングの表示

Figure を作成してループでクラス活性化マッピングを実行します。ループの実行を終了するには、Figure を閉じます。

h = figure('Units','normalized','Position',[0.05 0.05 0.9 0.8],'Visible','on');

while ishandle(h)

Web カメラを使用してスナップショットを取得します。イメージをサイズ変更して短辺の長さ (ここではイメージの高さ) をネットワークの入力イメージ サイズと等しくします。サイズを変更する際、イメージの縦横比を維持します。イメージのサイズは拡大したり縮小したりできます。イメージを拡大すると最終的なクラス活性化マップの解像度が上がりますが、予測全体の精度が下がる原因になる可能性があります。

ネットワークの最後の畳み込み層に続く ReLU 層で、サイズ変更されたイメージの活性化を計算します。

    im = snapshot(camera);
    imResized = imresize(im,[inputSize(1), NaN]);
    imageActivations = activations(net,imResized,layerName);

特定クラスのクラス活性化マップは、最終の畳み込み層に続く ReLU 層の活性化マップで、各活性化がクラスの最終的なスコアにどれだけ寄与するかに応じて重み付けされます。これらの重みは、そのクラスにおけるネットワークの最終全結合層の重みに等しくなります。SqueezeNet には最終の全結合層がありません。代わりに、最後の畳み込み層に続く ReLU 層の出力は、既にクラス活性化マップになっています。

クラス活性化マップはどのような出力クラスに対しても生成できます。たとえば、ネットワークが誤って分類した場合、真のクラスと予測クラスのクラス活性化マップを比較できます。この例では、スコアが最も高い予測クラスのクラス活性化マップを生成します。

    scores = squeeze(mean(imageActivations,[1 2]));
    
    if netName ~= "squeezenet"
        fcWeights = net.Layers(end-2).Weights;
        fcBias = net.Layers(end-2).Bias;
        scores =  fcWeights*scores + fcBias;
        
        [~,classIds] = maxk(scores,3);
        
        weightVector = shiftdim(fcWeights(classIds(1),:),-1);
        classActivationMap = sum(imageActivations.*weightVector,3);
    else
        [~,classIds] = maxk(scores,3);
        classActivationMap = imageActivations(:,:,classIds(1));
    end
    

最上位クラスのラベルと最終的な正規化されたクラス スコアを計算します。

    scores = exp(scores)/sum(exp(scores));     
    maxScores = scores(classIds);
    labels = classes(classIds);

クラス活性化マップをプロットします。最初のサブプロットに元のイメージを表示します。2 番目のサブプロットに、この例の終わりに定義されている補助関数 CAMshow を使用して、元のイメージのグレースケール バージョンを暗くしてその上にクラス活性化マップを重ねて表示します。上位 3 つの予測ラベルを予測スコアと共に表示します。

    subplot(1,2,1)
    imshow(im)
    
    subplot(1,2,2)
    CAMshow(im,classActivationMap)
    title(string(labels) + ", " + string(maxScores));
    
    drawnow
    
end

webcam オブジェクトをクリアします。

clear camera

マップの例

ネットワークはイメージ内のオブジェクトをローファー (靴の一種) として正しく識別しています。右のイメージのクラス活性化マップは、入力イメージの各領域による、予測クラス Loafer への寄与を示しています。赤い領域が最も寄与しています。ネットワークは靴全体を基に分類を行いますが、最も強い入力は赤い領域、つまり靴のつま先と履き口から得られます。

ネットワークは以下のイメージをマウスとして分類します。クラス活性化マップからわかるように、予測はイメージ内のマウスだけでなく、キーボードにも基づいています。学習セットにはキーボードの隣にマウスがあるイメージが多く含まれる傾向にあるため、キーボードを含むイメージはマウスを含む傾向にあることがネットワークにより予測されます。

ネットワークは、以下のコーヒー カップのイメージをバックルとして分類します。クラス活性化マップからわかるように、イメージであまりに多くのオブジェクトが重なっているため、ネットワークがイメージを誤分類します。ネットワークは、コーヒー カップではなく腕時計のリストバンドを検出して、これに焦点を当てています。

補助関数

CAMshow(im,CAM) は、イメージ im のグレースケール バージョンを暗くしたものに、クラス活性化マップ CAM を重ね合わせます。この関数はクラス活性化マップを im のサイズに変更し、正規化し、下からしきい値処理を行って、jet カラーマップを使用して可視化します。

function CAMshow(im,CAM)
imSize = size(im);
CAM = imresize(CAM,imSize(1:2));
CAM = normalizeImage(CAM);
CAM(CAM<0.2) = 0;
cmap = jet(255).*linspace(0,1,255)';
CAM = ind2rgb(uint8(CAM*255),cmap)*255;

combinedImage = double(rgb2gray(im))/2 + CAM;
combinedImage = normalizeImage(combinedImage)*255;
imshow(uint8(combinedImage));
end

function N = normalizeImage(I)
minimum = min(I(:));
maximum = max(I(:));
N = (I-minimum)/(maximum-minimum);
end

function layerName = activationLayerName(netName)

if netName == "squeezenet"
    layerName = 'relu_conv10';
elseif netName == "googlenet"
    layerName = 'inception_5b-output';
elseif netName == "resnet18"
    layerName = 'res5b_relu';
elseif netName == "mobilenetv2"
    layerName = 'out_relu';
end

end

参照

[1] Zhou, Bolei, Aditya Khosla, Agata Lapedriza, Aude Oliva, and Antonio Torralba. "Learning deep features for discriminative localization." In Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition, pp. 2921-2929. 2016.

参考

|

関連するトピック