您可以通过反转模型并将输出层的权重可视化,如投影到输入层。
假设您有一个简单的线性分类器 NN,它有 784 个输入(MNIST 数字图像中的像素数)和 10 个输出(数字类的数量)——没有隐藏层。给定输入图像 x(一个 784 元素的列向量),输出层的激活 z 由 z = f(x) = Wx + b 给出,其中 W 是 10 x 784 权重矩阵,b 是 10 元素偏置向量。
你可以做一些代数并轻松地反转这个模型,计算x给定z:x = f^-1(z) = W^-1 (z - b)。现在假设您想查看4 类的最佳输入。这个类的目标输出是z = [0 0 0 0 1 0 0 0 0 0]^T;如果我们暂时忽略偏差,那么您只需要计算 W 的倒数的第 4 列(从 0 开始),一个 784 元素的列向量,将其重新排列成 28 x 28 图像,然后查看它。这是最佳输入,因为输出层激活与输入和该类的权重向量的点积成正比,因此与类4 的权重向量相同的输入向量将在输出处最大程度地激活该类层。
如果您向模型添加更多层和非线性,事情会变得更加复杂,但一般方法保持不变。给定模型的目标输出z*,您需要某种方法来计算最佳输入x*,但您只知道从输入到目标的(可能很复杂)前向映射z = f(x)。您可以将其视为优化问题:您正在尝试计算 x* = f^-1(z*) 并且您知道 f 和 z*。如果您对f 的了解允许您计算封闭形式的符号逆,那么您只需将z* 插入并得到x*。如果你不能做到这一点,你总是可以使用迭代优化过程来计算连续更好的近似值x1、x2、...、xn,给定初始猜测x0。这是一些使用scipy.optimize 执行此操作的 Python 伪代码:
import numpy as np
import scipy.optimize
# our forward model, paired layers of already-trained
# weights and biases.
weights = [np.array(...) ...]
biases = [np.array(...) ...]
def f(x):
for W, b in zip(weights, biases):
# relu activation.
x = np.clip(np.dot(W, x) + b, 0, np.inf)
return x
# set our sights on class #4.
zstar = np.array([0, 0, 0, 0, 1, 0, 0, 0, 0, 0])
# the loss we want to optimize: minimize difference
# between zstar and f(x).
def loss(x):
return abs(f(x) - zstar).sum()
x0 = np.zeros(784)
result = scipy.optimize.minimize(loss, x0)
顺便说一句,这个过程基本上是最近"Inceptionism" images from Google 的核心——优化过程试图确定复制复杂网络中特定隐藏层状态的输入像素。由于卷积等原因,在这种情况下它更复杂,但想法是相似的。