NetworkVisualization.ipynb
在之前的代码中,我们定义损失来表达模型的满意程度,然后计算每个参数的梯度,并使用梯度下降来最小化损失。
接下来会的代码则不一样:
- 有一个在ImageNet上预训练好的分类模型
- 用这个模型定义损失,用于表达对图像的满意程度
- 计算图像中的每个像素的梯度,使用梯度下降更新图像,使损失达到最小
这里将介绍三种图像生成的方法:
- 显著图:它能告诉我们,图像中哪些地方影响了分类;
- fooling image:修改图像像素,人眼中看到的图像没太大区别,预训练模型却会误分类;
- 类别可视化:生成一张图片去最大化特定类的分数,这能让我们了解神经网络把图片分为某一类时,比较重视什么。
预处理
import torch
from torch.autograd import Variable
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) # 默认画图大小
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'
预训练使用的数据是经过预处理的(减均值,除标准差),所以定义了一些函数来消除预处理。
def preprocess(img, size=224):
transform = T.Compose([
T.Resize(size),
T.ToTensor(),
# tolist(),将数组或者矩阵转换成列表
# SQUEEZENET_MEAN和SQUEEZENET_STD都是三维数组
T.Normalize(mean=SQUEEZENET_MEAN.tolist(),
std=SQUEEZENET_STD.tolist()),
T.Lambda(lambda x: x[None]),# 增加第0维
])
return transform(img)
def deprocess(img, should_rescale=True):
transform = T.Compose([
T.Lambda(lambda x: x[0]),
# 由于预处理是先减均值再除方差,现在顺序要反过来,写为一条语句的话顺序不对
T.Normalize(mean=[0, 0, 0], std=(1.0 / SQUEEZENET_STD).tolist()),
T.Normalize(mean=(-SQUEEZENET_MEAN).tolist(), std=[1, 1, 1]),
T.Lambda(rescale) if should_rescale else T.Lambda(lambda x: x),
T.ToPILImage(),
])
return transform(img)
# 归一化
def rescale(x):
low, high = x.min(), x.max()
x_rescaled = (x - low) / (high - low)
return x_rescaled
def blur_image(X, sigma=1):
X_np = X.cpu().clone().numpy()
# 分别在图像的长和宽进行一维高斯滤波
X_np = gaussian_filter1d(X_np, sigma, axis=2)
X_np = gaussian_filter1d(X_np, sigma, axis=3)
X.copy_(torch.Tensor(X_np).type_as(X))
return X
理论上可以使用任一预训练的网络来生成图片,但是cs231n选了SqueezeNet [1]。因为它达到了AlexNet的准确率的同时,参数量却显著减少(SqueezeNet是轻量级网络中的一个,后来还有ShuffleNet,MobileNet等等)。使用SqueezeNet而不是AlexNet或者VGG或者ResNet意味着可以使用CPU来生成图片。
[1]