次回から「Gray Scale」で検索をお願いします!!

Grad-CAMをEfficeientNetで使用する

AIってブラックボックスと言われがち、、、
結果が出ても、AIがどう考えているか分からないと、説明性も不足してしまいます。

今回は、画像AIにおいて有名なEfficientNetの説明性を上げるツールとしてのGrad-CAMを実装していきます。

・EfficientNetを使っていて、出力の説明性を示したい
・Grad-CAMを使いたいが、どうコードを書けばいいかわからない
・Grad-CAMって何?

GradCAMとは

画像AIで一般的な処理としては、CNNである畳み込みニューラルネットワークがあります。
畳み込み処理とは、画像に対して数個のフィルターを適用することで、画像の特徴量を抽出していく処置となります。
Grad-CAMでは、この畳み込みの最終的な特徴マップを可視化することで、画像のどこにAIが注目しているかが分かるようになります。

EfficientNetとは

EffientNetですが、2019年にGoogleが発表したモデルとなります。
CNNは、層を増やせば増やすほど精度がよくなりそうですが、勾配消失に陥ったりと、層を増やすだけでは精度向上を満足できなくなってきます。
そこで、層を増やすという考えから、効率的なパラメータを持たせることで精度向上を図ろうとしたのがEfficientNetです。

EfficientNetはB0からB7までのモデルがあり、数字が大きくなるほど精度が良くなりますが、その分計算コストは大きくなります。
精度と計算コストのバランスをうまく取ることが必要です。

EfficientNetでの出力結果をGrad-CAMで可視化する

それでは、EfficientNetでの出力結果をGrad-CAMで可視化してみましょう!

ここで、一番のポイントは、「最終的な畳み込み層の特徴マップを、どう取り出すか?」という部分です。

使用するモデルによって記述は変わりますが、EfficientNetB0では下記のようにmodel.layer4.modules()という記述になります。

# Grad-CAM
target_layer = list(model.layer4.modules())[26]

実装コード

Efficientネットですが、下記リンクを参考にしています。

リンク

また、Grad-CAMは、下記リンクを参考にしています。

リンク

#importing required modules
import gdown
import zipfile
import numpy as np
from glob import glob
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
from torchsummary import summary
from torchvision import datasets, transforms as T
from efficientnet_pytorch import EfficientNet
import os
import torch.optim as optim
from PIL import ImageFile
from sklearn.metrics import accuracy_score
#try
from torch.nn import DataParallel
#Checking the availability of a GPU
use_cuda = torch.cuda.is_available()
use_cuda
#declaring batch size
batch_size = 32
batch_size = 32

pix_size = 224

#applying required transformations on the dataset
img_transforms = {
    'train':
    T.Compose([
        T.Resize(size=(pix_size,pix_size)),
        T.ToTensor(),
        T.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]),
        ]),
    
    'valid':
    T.Compose([
        T.Resize(size=(pix_size,pix_size)),
        T.ToTensor(),
        T.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
        ]),
    
    'test':
    T.Compose([
        T.Resize(size=(pix_size,pix_size)),
        T.ToTensor(),
        T.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
        ]),    
}

# creating Location of data: train, validation, test
data='./data/'
#data='./Hazelnut/'

train_path=os.path.join(data,'train')
valid_path=os.path.join(data,'valid')
test_path=os.path.join(data,'test')

train_file=datasets.ImageFolder(train_path,transform=img_transforms['train'])
valid_file=datasets.ImageFolder(valid_path,transform=img_transforms['valid'])
test_file=datasets.ImageFolder(test_path,transform=img_transforms['test'])

#creating loaders for the dataset
loaders_transfer={
    'train':torch.utils.data.DataLoader(train_file,batch_size,shuffle=True),
    'valid':torch.utils.data.DataLoader(valid_file,batch_size,shuffle=True),
    'test':torch.utils.data.DataLoader(test_file,batch_size,shuffle=True)
}
#importing the pretrained Efficientn\Net mode
model_transfer = EfficientNet.from_pretrained('efficientnet-b0')

#Freeze weights
for param in model_transfer.parameters():
    param.requires_grad = False
in_features = model_transfer._fc.in_features

#Defining Dense Top layers after the convolutional layers
model_transfer._fc = nn.Sequential(
    nn.BatchNorm1d(num_features=in_features),
    nn.Linear(in_features, 512),
    nn.ReLU(),
    nn.BatchNorm1d(512),
    nn.Linear(512, 128),
    nn.ReLU(),
    nn.Dropout(0.4),
    nn.Linear(128, 2),
    )
if use_cuda:
    model_transfer = model_transfer.cuda()

model_transfer    
# selecting loss function
criterion_transfer = nn.CrossEntropyLoss()

#using Adam classifier
optimizer_transfer = optim.Adam(model_transfer.parameters(), lr=0.0005)
ImageFile.LOAD_TRUNCATED_IMAGES = True

# Creating the function for training
def train(n_epochs, loaders, model, optimizer, criterion, use_cuda, save_path):
    """returns trained model"""
    # initialize tracker for minimum validation loss
    valid_loss_min = np.Inf 
    trainingloss = []
    validationloss = []
    validationAcc = []
    
    for epoch in range(1, n_epochs+1):
        # initialize the variables to monitor training and validation loss
        train_loss = 0.0
        valid_loss = 0.0
        correct = 0
        total = 0
        
        ###################
        # training the model #
        ###################
        model.train()
        for batch_idx, (data, target) in enumerate(loaders['train']):
            # move to GPU
            if use_cuda:
                data, target = data.cuda(), target.cuda()
          
            optimizer.zero_grad()
            output = model(data)
            #print(target.shape)
            #print(target)
            #print(output.shape)
            #print(output)
            loss = criterion(output, target)
            loss.backward()
            optimizer.step()
           
            #train_loss = train_loss + ((1 / (batch_idx + 1)) * (loss.data - train_loss))
            train_loss += loss.item()
        ######################    
        # validating the model #
        ######################
        model.eval()
        for batch_idx, (data, target) in enumerate(loaders['valid']):
            if use_cuda:
                data, target = data.cuda(), target.cuda()
            
            output = model(data)
            loss = criterion(output, target)
            
            ###改良
            predicted = output.max(1, keepdim=True)[1]
            labels = target.view_as(predicted)
            correct += predicted.eq(target).sum().item()
            total += target.size(0)      
            ###
            #valid_loss = valid_loss + ((1 / (batch_idx + 1)) * (loss.data - valid_loss))
            valid_loss += loss.item()
        
        train_loss = train_loss/len(train_file)
        valid_loss = valid_loss/len(valid_file)
        valid_acc = correct /len(valid_file)
        
        trainingloss.append(train_loss)
        validationloss.append(valid_loss)
        validationAcc.append(valid_acc)

        # printing training/validation statistics 
        print('Epoch: {} \tTraining Loss: {:.6f} \tValidation Loss: {:.6f}'.format(
            epoch, 
            train_loss,
            valid_loss,
            valid_acc
            ))
        
        ## saving the model if validation loss has decreased
        if valid_loss < valid_loss_min:
            torch.save(model.state_dict(), save_path)
            
            valid_loss_min = valid_loss
            
    # return trained model
    return model, trainingloss, validationloss, validationAcc
# training the model

n_epochs=10

model_transfer, train_loss, valid_loss, valid_acc = train(n_epochs, loaders_transfer, model_transfer, optimizer_transfer, criterion_transfer, use_cuda, 'model.pt')
#print(train_loss)
#print(valid_loss)
#print(valid_acc)

'''結果の表示'''
plt.plot(range(n_epochs), train_loss, 'r-', label='train_loss')
plt.plot(range(n_epochs), valid_loss, 'b-', label='val_loss')
plt.legend()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.figure()

plt.plot(range(n_epochs), valid_acc, 'g-', label='val_acc')
plt.legend()
plt.xlabel('epoch')
plt.ylabel('acc')

print('正解率:',valid_acc[-1], '%')
# Defining the test function

def test(loaders, model, criterion, use_cuda):

    # monitoring test loss and accuracy
    test_loss = 0.
    correct = 0.
    total = 0.
    preds = []
    targets = []

    model.eval()
    for batch_idx, (data, target) in enumerate(loaders['test']):
        # moving to GPU
        if use_cuda:
            data, target = data.cuda(), target.cuda()
        # forward pass
        output = model(data)
        print(output)
        # calculate the loss
        loss = criterion(output, target)
        # updating average test loss 
        test_loss = test_loss + ((1 / (batch_idx + 1)) * (loss.data - test_loss))
        # converting the output probabilities to predicted class
        pred = output.data.max(1, keepdim=True)[1]
        preds.append(pred)
        targets.append(target)
        # compare predictions
        correct += np.sum(np.squeeze(pred.eq(target.data.view_as(pred))).cpu().numpy())
        total += data.size(0)
    
    return preds, targets

# calling test function
preds, targets = test(loaders_transfer, model_transfer, criterion_transfer, use_cuda)
#converting the tensor object to a list for metric functions

preds2, targets2 = [],[]

for i in preds:
  for j in range(len(i)):
    preds2.append(i.cpu().numpy()[j])
for i in targets:
  for j in range(len(i)):
    targets2.append(i.cpu().numpy()[j])

#Computing the accuracy
acc = accuracy_score(targets2, preds2)
print("Accuracy: ", acc)
from sklearn.metrics import confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt


cm = confusion_matrix(targets2, preds2)

print(cm)
#A = Negative, B = Positive
#          Predicted
#          A    B
#Actual A  TN   FP
#       B  FN   TP

sns.heatmap(cm, annot=True, cmap='Blues')
plt.savefig('./sklearn_confusion_matrix.png')
# Basic Modules
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import glob
import re
import os
from PIL import *

# PyTorch Modules
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision
from torchvision import transforms, datasets
import torchvision.transforms as transforms
from torch.utils.data.dataset import Subset
import torchvision.models as models
import torch.optim as optim
from torchvision.utils import make_grid, save_image

# Grad-CAM
from gradcam.utils import visualize_cam
from gradcam import GradCAM, GradCAMpp


device = torch.device("cuda:0" if torch.cuda.is_available()  else "cpu")
#model = models.densenet161(pretrained=True)
model = models.resnet50(pretrained=False, num_classes=2)
in_features = model.fc.in_features
model.fc = nn.Linear(in_features, 2)
#model.fc = nn.Linear(2048,5)
#model = torch.nn.DataParallel(model).to(device)
model.to(device)
model.eval()
#model.load_state_dict(torch.load('trained_model.pt'))
model.load_state_dict(torch.load('model_metric.pth'), strict=False)

# Grad-CAM
target_layer = list(model.layer4.modules())[26]
#target_layer = model.module.features
gradcam = GradCAM(model, target_layer)
gradcam_pp = GradCAMpp(model, target_layer)

images = []
# あるラベルの検証用データセットを呼び出してる想定

#for path in glob.glob("{}/label1/*".format(config['dataset'])):
#for path in glob.glob('./data/test/covid/*'):
#for path in glob.glob('./Hazelnut/test/crack/*'):
for path in glob.glob('./Hazelnut/test/good/*'):
    img = Image.open(path)
    torch_img = transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor()
    ])(img).to(device)
    normed_torch_img = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])(torch_img)[None]  
    mask, _ = gradcam(normed_torch_img)
    heatmap, result = visualize_cam(mask, torch_img)

    mask_pp, _ = gradcam_pp(normed_torch_img)
    heatmap_pp, result_pp = visualize_cam(mask_pp, torch_img)

    #images.extend([torch_img.cpu(), heatmap, heatmap_pp, result, result_pp])
    images.extend([torch_img.cpu(),heatmap, result])
    
#grid_image = make_grid(images, nrow=5)
grid_image = make_grid(images, nrow=3)

# 結果の表示
transforms.ToPILImage()(grid_image)
最新情報をチェックしよう!

プログラミングの最新記事8件