Saliency maps

Deep Inside Convolutional Networks: Visualising Image Classification Models and Saliency Maps

问题

这篇文章和ZFnet相似,旨在研究网络可视化的问题,根据分裂网络最后的向量来反推出最原始的图像,如果假设输入(input)是 I I I, 而输入图像对应的标签是 c c c, 而分类器的得分是 S c ( I ) S_c(I) Sc(I)(也就是第 c c c个分量),那么我们希望找到一个 I I I使得 S c ( I ) S_c(I) Sc(I)足够大,说明这个输入很有可能是这个类的:
a r g m a x I S c ( I ) , \mathrm{argmax}_I \quad S_c(I), argmaxISc(I),
不过,论文实际上是研究下面的问题:
a r g m a x I S c ( I ) − λ ∥ I ∥ 2 2 . \mathrm{argmax}_I \quad S_c(I) -\lambda \|I\|_2^2. argmaxISc(I)λI22.
其实就是加了一个正则化项,我想这应该是处于实际角度出发的,因为在处理图像的时候往往有一个Normlize的过程,所以如果 I I I太过“巨大”那肯定是不合适的——起码它都不能称为一个图像.

细节

变量

需要注意的是,上面的问题是关于 I I I,也就是图像来说的,如果有 k k k个类,那么理论上应该有 k k k张对应的图像(同一个 λ \lambda λ).

然后论文的结果是这样的:

在这里插入图片描述

我的结果是这样的(CIFAR10):
在这里插入图片描述
相差甚远, 是 λ = 0.1 \lambda =0.1 λ=0.1不合适?

S c ( I ) S_c(I) Sc(I)

需要一提的是,这个 S c ( I ) S_c(I) Sc(I)不是sigmoid后的值,而是之前的分数,作者是这么解释的,因为sigmoid:
P c = S c ∑ c exp ⁡ ( S c ) , P_c = \frac{S_c}{\sum_c \exp(S_c)}, Pc=cexp(Sc)Sc,
我们的目的是提高 S c S_c Sc,而如果是 P c P_c Pc, 那么我们可以通过降低别的 S c S_c Sc来间接提高 P c P_c Pc,而非提高 S c S_c Sc, 有点道理吧,试了一下,在原来的参数条件下几乎不学习了…

扩展

作者提到这个方案可以用于定位, 首先要说明的是,通过这种方法,我们可以“定位”(虽然可能是臆想)敏感地带.

输入一张图片,计算
∂ S c ( I ) ∂ I , \frac{\partial S_c(I)}{\partial I}, ISc(I),
结果是一个“矩阵”(张量?), 其中的元素的绝对值大小可以衡量对类别判断的重要,即越大越是敏感地带.

在这里插入图片描述
那个简单例子,感觉没能和好的说服我. 如果网络就是一个线性判别器,那么照此思路,其敏感程度就是权重,直观上这样似乎如此,但是感觉就像是抛开了数据本身…但的确是有道理的. 还有一个问题是,对于一张图片,如果它被误判了, 那么是选择其本身的标签,还是网络所判断的那个 c c c呢?
在我的实验中,二者似乎没有太大的差别.

回到定位的话题,计算出梯度的矩阵后,如果有 C C C个通道, C C C个通道的每个元素的绝对值的最大作为那个位置的敏感程度,如此,如果图片是 ( C , H , W ) (C, H, W) (C,H,W), 那么最后会得到一个 ( 1 , H , W ) (1, H, W) (1,H,W)的矩阵,其中的元素则反应了敏感程度.

但是,其中的敏感程度指示反应了物体所在的大概位置,作者说还要通过一种颜色的连续来更为细致地框定范围,那种技术我不知道,就简单地做个实验:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

细看,我觉得还是有那么点感觉的.

代码

I I I的时候,不知道怎么利用已有的梯度方法,就自己写了一个. 网络的测试成功率为60%,因为是一个比较简单的网络,大的网络实在难以下手.




import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt









class Net(nn.Module):

    def __init__(self, num):
        super(Net, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(3, 16, 4, 2), #3x32x32 --> 8x15x15
            nn.ReLU(),
            nn.MaxPool2d(2, 2), # 15 --> 7
            nn.Conv2d(16, 64, 3, 1, 1), #16x7x7 --> 64x7x7
            nn.ReLU(),
            nn.MaxPool2d(2, 1) #7-->6
         )
        self.dense = nn.Sequential(
            nn.Linear(64 * 6 * 6, 256),
            nn.ReLU(),
            nn.Linear(256, num)
        )

    def forward(self, x):
        x = self.conv(x)
        x = x.view(x.size(0), -1)
        out = self.dense(x)
        return out




class SGD:
    def __init__(self, lr=1e-3, momentum=0.9):
        self.v = 0
        self.lr = lr
        self.momentum = momentum

    def step(self, x, grad):
        self.v = self.momentum * self.v +  grad
        return x + self.lr * self.v



class Train:

    def __init__(self, trainset, num=10, lr=1e-4, momentum=0.9,loss_function=nn.CrossEntropyLoss()):
        self.net = Net(num)
        self.trainset = trainset
        self.criterion = loss_function
        self.opti = torch.optim.SGD(self.net.parameters(), lr=lr, momentum=momentum)

    def trainnet(self, iterations, path):
        running_loss = 0.0
        for epoch in range(iterations):
            for i, data in enumerate(self.trainset):
                imgs, labels = data
                output = self.net(imgs)
                loss = self.criterion(output, labels)
                self.opti.zero_grad()
                loss.backward()
                self.opti.step()
                running_loss += loss
                if i % 10 == 9:
                    print("[epoch: {} loss: {:.7f}]".format(
                        epoch,
                        running_loss / 10
                    ))
                    running_loss = 0.0
        torch.save(self.net.state_dict(), path)

    def loading(self, path):
        self.net.load_state_dict(torch.load(path))
        self.net.eval()

    def visual(self, iterations=100, digit=0, gamma=0.1, lr=1e-3, momentum=0.9):
        def criterion(out, x, digit, gamma=0.1):
            return out[0][digit] - gamma * torch.norm(x, 2) ** 2
        opti = SGD(lr, momentum)
        x = torch.zeros((1, 3, 32, 32), requires_grad=True, dtype=torch.float)
        for i in range(iterations):
            output = self.net(x)
            loss = criterion(output, x, digit, gamma)
            print(loss.item())
            loss.backward()
            x = torch.tensor(opti.step(x, x.grad), requires_grad=True)
        img = x[0].detach()
        img = img / 2 + 0.5
        img = img / torch.max(img.abs())
        img = np.transpose(img, (1, 2, 0))
        print(img[0])
        plt.imshow(img)
        plt.title(classes[digit])
        plt.show()
        return x

    def local(self, img, label):
        cimg = img.view(1, 3, 32, 32).detach()
        cimg.requires_grad = True
        output = self.net(cimg)
        print(output)
        print(label)
        s = output[0][label]
        s.backward()
        with torch.no_grad():
            grad = cimg.grad.data[0]
            graph = torch.max(torch.abs(grad), 0)[0]
            saliency = graph.detach().numpy()
        print(np.max(saliency))
        img = img.detach().numpy()
        img = img / 2 + 0.5
        img = np.transpose(img, (1, 2, 0))
        fig, ax = plt.subplots(1, 2)
        ax[0].set_title(classes[label])
        ax[0].imshow(img)
        ax[1].imshow(saliency, cmap=plt.cm.hot)
        plt.show()

    def testing(self, testloader):
        correct = 0
        total = 0
        with torch.no_grad():
            for data in testloader:
                images, labels = data
                outputs = self.net(images)
                _, predicted = torch.max(outputs.data, 1)
                total += labels.size(0)
                correct += (predicted == labels).sum().item()
        print('Accuracy of the network on the 10000 test images: %d %%' % (
                100 * correct / total))

root = "C:/Users/pkavs/1jupiterdata/data"

#准备训练集


trainset = torchvision.datasets.CIFAR10(root=root, train=True,
                                        download=False,
                                       transform=transforms.Compose(
                                           [transforms.ToTensor(),
                                            transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
                                       ))

train_loader = torch.utils.data.DataLoader(trainset, batch_size=64,
                                          shuffle=True, num_workers=0)


testset = torchvision.datasets.CIFAR10(root=root, train=False,
                                       download=False,
                                       transform=transforms.Compose(
                                           [transforms.ToTensor(),
                                            transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
                                       ))
testloader = torch.utils.data.DataLoader(testset, batch_size=64,
                                         shuffle=False, num_workers=0)



classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

path = root + "/visual1.pt"


test = Train(train_loader, lr=1e-4)
test.loading(path)
#test.testing(testloader) 60%


data = next(iter(train_loader))
imgs, labels = data
img = imgs[0]
label = labels[0]
test.local(img, label)


#test.visual(1000, digit=3)



  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这段程序是一个计算图像数据的显著性图并保存的过程。下面是对程序的详细解释: 1. 导入必要的库: ```python import torch import torchvision.datasets as datasets import torchvision.transforms as transforms from models import vgg11_bn from fullgrad import FullGrad, FullGradSimple from utils import NormalizeInverse, save_saliency_map import os ``` 2. 定义数据集和数据加载器: ```python dataset = 'image/' sampler_loader = torch.utils.data.DataLoader( datasets.ImageFolder(dataset, transform = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean = [0.485, 0.456, 0.406], std = [0.229, 0.224, 0.225]) ]))) ``` 这里使用了`datasets.ImageFolder`来加载数据集,`transform`参数指定了对图像进行的预处理操作,包括将图像大小调整为224x224,转换为张量和归一化。 3. 定义反归一化操作: ```python unnormalize = NormalizeInverse(mean = [0.485, 0.456, 0.406], std = [0.229, 0.224, 0.225]) ``` `NormalizeInverse`是一个自定义的类,用于将经过归一化处理的张量反转回原始图像。 4. 检查设备是否支持CUDA并定义模型: ```python device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = vgg11_bn(pretrained = True) model = model.to(device) ``` 这里使用`torch.cuda.is_available()`来检查设备是否支持CUDA,并将模型移动到相应的设备(GPU或CPU)。 5. 定义FullGrad和Simple FullGrad对象: ```python fullgrad = FullGrad(model) simple_fullgrad = FullGradSimple(model) ``` `FullGrad`和`FullGradSimple`是自定义的类,用于计算图像数据的显著性图。 6. 定义结果保存路径: ```python save_path = 'result' if os.path.isdir(save_path): os.mkdir(save_path) ``` 如果保存路径不存在,则创建一个新的。 7. 定义计算显著性图并保存的函数: ```python def compute_saliency_and_save(): for idx, (data, target) in enumerate(sampler_loader): data, target = data.to(device).requires_grad_(), target.to(device) cam = fullgrad.fullgrad(data) cam_simple = simple_fullgrad.fullgrad(data) for i in range(data.size(0)): filename = save_path + str((idx + 1) * (i+1)) filename_simple = filename + "_simple" image = unnormalize(data[i,:,:,:].cpu()) save_saliency_map(image, cam[i,:,:,:], filename + ".jpg") save_saliency_map(image, cam_simple[i,:,:,:], filename_simple + ".jpg") ``` 这个函数会遍历数据加载器中的每个数据,将其移动到设备上,并计算其对应的显著性图。然后,将图像、显著性图和保存路径传递给`save_saliency_map`函数以保存显著性图。 8. 主函数: ```python if __name__ == "__main__": compute_saliency_and_save() ``` 当脚本被直接执行时,会调用`compute_saliency_and_save`函数来计算和保存显著性图。 总结:这段程序用于计算图像数据的显著性图并保存,它使用了预训练的VGG-11模型和FullGrad算法。程序首先加载数据集并进行预处理,然后将模型移动到合适的设备上。接下来,使用FullGrad和Simple FullGrad算法计算每个图像的显著性图,并将结果保存到指定路径下。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值