欺骗神经网络
2015 年 6 月 18 日
首先简要介绍一下欺骗神经网络的原理:
输入数据微小的改变,可能大幅度地影响输出。
神经网络中几乎到处都有这种机会。例如 sigmoid 函数,自变量在 0 附近的微小改变可以引发函数值很大的变化。一个全连接层,各个节点都变动一个微小数值,没准就能齐心协力把输出的 argmax 改变掉,让分类器作出错误的判断。
CTF 中也经常出现这类「指鹿为马」形式的问题:给出一个分类器(我遇到过神经网络和 k-nearest)的所有参数,然后给出一个样本 $x$,它被模型正确地识别为 $y$. 选手的任务是小幅度地修改 $x$,使得模型将之分类成 $y ^ \prime$. 这里可能要求 $x ^ \prime$ 与 $x$ 的距离小于某个值,例如求出 $x$ 与 $ x ^ \prime$ 的每个像素点之间的距离(RGB8,各个通道之差取绝对值求和),要求像素距离总和小于 $10 ^ 5$.
我们之所以可以欺骗神经网络,是因为神经网络缺乏泛化能力。一般来看,过拟合越严重,我们越能轻易地(指 $x ^ \prime$ 与 $x$ 距离很近)修改输入向量,来让神经网络输出我们想要的值。
作为演示,我们接下来实现一个神经网络,它识别 MNIST 数据集的手写数字。然后我们随便找一张图像,将其修改一番,使得我们人类仍然能够正确识别,而神经网络给出错误判断。
输入数据微小的改变,可能大幅度地影响输出。
神经网络中几乎到处都有这种机会。例如 sigmoid 函数,自变量在 0 附近的微小改变可以引发函数值很大的变化。一个全连接层,各个节点都变动一个微小数值,没准就能齐心协力把输出的 argmax 改变掉,让分类器作出错误的判断。
CTF 中也经常出现这类「指鹿为马」形式的问题:给出一个分类器(我遇到过神经网络和 k-nearest)的所有参数,然后给出一个样本 $x$,它被模型正确地识别为 $y$. 选手的任务是小幅度地修改 $x$,使得模型将之分类成 $y ^ \prime$. 这里可能要求 $x ^ \prime$ 与 $x$ 的距离小于某个值,例如求出 $x$ 与 $ x ^ \prime$ 的每个像素点之间的距离(RGB8,各个通道之差取绝对值求和),要求像素距离总和小于 $10 ^ 5$.
我们之所以可以欺骗神经网络,是因为神经网络缺乏泛化能力。一般来看,过拟合越严重,我们越能轻易地(指 $x ^ \prime$ 与 $x$ 距离很近)修改输入向量,来让神经网络输出我们想要的值。
作为演示,我们接下来实现一个神经网络,它识别 MNIST 数据集的手写数字。然后我们随便找一张图像,将其修改一番,使得我们人类仍然能够正确识别,而神经网络给出错误判断。
实现神经网络
作为 PyTorch 玩家,我们当然用 PyTorch 来完成此项工作。先把包导入进来:
import torch import torchvision import torch.nn as nn import torchvision.transforms as transforms import torch.nn.functional as F import numpy as np import matplotlib.pyplot as plt
然后导入数据集。 torchvision
库可以帮助我们快速导入 MNIST.
trans_to_tensor = transforms.Compose([ transforms.ToTensor() ]) data_train = torchvision.datasets.MNIST( './data', train=True, transform=trans_to_tensor, download=True) data_test = torchvision.datasets.MNIST( './data', train=False, transform=trans_to_tensor, download=True) data_train, data_test ''' (Dataset MNIST Number of datapoints: 60000 Root location: ./data Split: Train StandardTransform Transform: Compose( ToTensor() ), Dataset MNIST Number of datapoints: 10000 Root location: ./data Split: Test StandardTransform Transform: Compose( ToTensor() )) '''