在本笔记本中,我们将探索使用图像梯度生成新图像。
这里我们要做一些稍微不同的事情。我们将从一个卷积神经网络模型开始,该模型已经经过了对ImageNet数据集进行图像分类的预训练。我们将使用这个模型来定义一个损失函数,该函数量化我们当前对图像的不满意,然后使用反向传播来计算这个损失相对于图像像素的梯度。然后我们将保持模型不变,并对图像进行梯度下降,以合成新的图像,使损失最小化。
在本笔记本中,我们将探索三种图像生成技术:
显著性映射:显著性映射是一种快速的方法,可以告诉哪些部分的图像影响了网络的分类决策。
欺骗图像:我们可以干扰输入图像,使其在人类看来是一样的,但会被预先训练的网络错误分类。
类可视化:我们可以合成一幅图像,使特定类的分类分数最大化;这可以给我们一些感觉,当网络对这类图像进行分类时,它在寻找什么。
ln[1]:
import torch
import torchvision
import torchvision.transforms as T
import random
import numpy as np
from scipy.ndimage.filters import gaussian_filter1d
import matplotlib.pyplot as plt
from cs231n.image_utils import SQUEEZENET_MEAN, SQUEEZENET_STD
from PIL import Image
#%matplotlib inline
plt.rcParams['figure.figsize'] = (10.0, 8.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'
Pretrained模型
对于我们所有的图像生成实验,我们将从一个卷积神经网络开始,该神经网络经过预先训练,可以在ImageNet上进行图像分类。在这里,我们可以使用任何模型,但为了实现这个任务,我们将使用SqueezeNet,它的精确度与AlexNet相当,但参数计数和计算复杂性显著降低。
SqeezeNet在ImageNet上实现了和AlexNet相同的正确率,但是只使用了1/50的参数。更进一步,使用模型压缩技术,可以将SqueezeNet压缩到0.5MB,这是AlexNet的1/510
一个Fire模块包括: 一个squeeze层 (只有1x1 卷积), 将其放入一个具有1x1 和3x3 卷积组合的expand层
使用SqueezeNet而不是AlexNet或VGG或ResNet意味着我们可以轻松地在CPU上执行所有图像生成实验。
ln[2]:
# Download and load the pretrained SqueezeNet model.
model = torchvision.models.squeezenet1_1(pretrained=True)
# We don't want to train the model, so tell PyTorch not to compute gradients
# with respect to model parameters.
for param in model.parameters():
param.requires_grad = False
# you may see warning regarding initialization deprecated, that's fine, please continue to next steps
加载一些ImageNet图像
我们提供了一些来自ImageNet ILSVRC 2012分类数据集验证集的示例图像。要下载这些映像,请进入cs231n/datasets/并运行get_imagenet_val.sh。
由于它们来自验证集,我们的预训练模型在训练期间没有看到这些图像。
运行以下单元格以可视化其中一些图像,以及它们的ground-truth标签。
ln[3]:
from cs231n.data_utils import load_imagenet_val
X, y, class_names = load_imagenet_val(num=5)
plt.figure(figsize=(12, 6))
for i in range(5):
plt.subplot(1, 5, i + 1)
plt.imshow(X[i])
plt.title(class_names[y[i]])
plt.axis('off')
plt.gcf().tight_layout()
Saliency Maps
Saliency Maps告诉我们图像中的每个像素对该图像分类评分的影响程度。为了计算它,我们计算对应于正确类(这是一个标量)的非归一化分数的梯度相对于图像的像素。如果图像有形状(3,H, W),那么这个梯度也会有形状(3,H, W);对于图像中的每个像素,这个梯度告诉我们,如果像素变化很小,分类评分将发生多大的变化。为了计算显著性图,我们取梯度的绝对值,然后取3个输入通道上的最大值;最终的Saliency Maps因此具有形状(H, W),并且是非负的。
提示:PyTorch gather 方法
记得在作业1中你需要从矩阵的每一行中选择一个元素;如果s是一个numpy数组(N,C)和y是一个numpy数组(N,)其中0<=y[i]<C,那么s[np.arange(N), y)是一个numpy数组(N,),使用y中的下标从s中的每个元素中选择一个元素
在PyTorch中,您可以使用gather()方法执行相同的操作。如果s是形状(N, C)的PyTorch张量,y是形状(N,)的PyTorch张量,其长度范围为0 <= y[i] < C,则
s.gather (1,y.view(-1,1)) .squeeze () 是一个形状为(N,)的PyTorch张量,使用y中的下标从s中的每一行元素中选择一个元素
运行以下单元格以查看示例。
ln[4]:
# Example of using gather to select one entry from each row in PyTorch
def gather_example():
N, C = 4, 5
s = torch.randn(N, C)
y = torch.LongTensor([1, 2, 1, 3])
print(s)
print(y)
print(s.gather(1, y.view(-1, 1)).squeeze())
gather_example()
在cs231n/net_visualization_py实现compute_saliency_maps函数
def compute_saliency_maps(X, y, model):
"""
Compute a class saliency map using the model for images X and labels y.
Input:
- X: Input images; Tensor of shape (N, 3, H, W)
- y: Labels for X; LongTensor of shape (N,)
- model: A pretrained CNN that will be used to compute the saliency map.
Returns:
- saliency: A Tensor of shape (N, H, W) giving the saliency maps for the input
images.
"""
# Make sure the model is in "test" mode
model.eval()
# Make input tensor require gradient
X.requires_grad_()
saliency = None
##############################################################################
# TODO: Implement this function. Perform a forward and backward pass through #
# the model to compute the gradient of the correct class score with respect #
# to each input image. You first want to compute the loss over the correct #