来源 | Medium
编辑 | 代码医生团队
深度学习和神经网络的兴起为现代社会带来了各种机会和应用,例如对象检测和文本转语音。然而,尽管看似准确性很高,但神经网络(以及几乎所有机器学习模型)实际上都可能受到数据(即对抗性示例)的困扰,而这些数据是从原始训练样本中进行的非常轻微的操纵。实际上,过去的研究表明,只要您知道更改数据的“正确”方法,就可以迫使您的网络在数据上表现不佳,而这些数据在肉眼看来似乎并没有什么不同!这些对数据进行有意操纵以降低模型精度的方法称为对抗性攻击,而攻击与防御之战是机器学习领域中持续流行的研究主题。
本文将概述最简单但有效的攻击之一-快速梯度签名方法攻击-以及在PyTorch中通过对抗性训练实施和防御的方法。
对抗性例子和攻击的历史
对抗性示例可以定义为扰乱机器学习网络的输入或数据。这个想法是由伊恩等人提出的。在ICLR 2015会议的论文“解释和利用对抗性示例”中。虽然在此之前的出版物声称这些对抗性示例是由非线性和机器模型的过度拟合引起的,但伊恩等人。他们认为,由于架构的高度线性,神经网络实际上很容易受到这些示例的影响。诸如LSTM的模型和诸如ReLU的激活函数仍然经常以非常线性的方式运行,因此这些模型很容易被线性扰动所欺骗。然后,他提供了一种简单而快速的生成对抗性示例的单步方法:快速梯度符号法。
快速梯度符号法(FGSM)
图1. FGSM对熊猫图像的影响(Ian Et等人在其2015 ICLR论文中的照片)
快速梯度符号方法(FGSM)是一种白盒攻击,这意味着该攻击是基于给定的网络体系结构生成的。FGSM基于这样的思想,即正常网络遵循梯度下降以找到最低的损耗点,因此,如果我们遵循梯度的符号(与梯度下降的方向相反),则可以通过将少量的扰动。
因此,FGSM可以描述为以下数学表达式:
其中x'是扰动的x,它是通过将一个小的常数ε加到等于损耗J相对于x的梯度方向而生成的。图1是计算机视觉领域中FGSM攻击的经典图示。由于我们无法从视觉上识别出小于1%的图像变化,因此图像从具有中等置信度的正确分类变为具有较高置信度的错误分类。
这些简单的方法实际上可以欺骗深度神经网络,这一事实进一步证明了由于神经网络的线性,存在对抗性示例。
PyTorch中的FGSM
要在PyTorch中进行FGSM攻击,我们可以使用Ian Goodfellow和Nicolas Papernot提供并精心维护的CleverHans库。该库提供多种攻击和防御,并且今天已广泛用于基准测试。尽管大多数攻击是在Tensorflow中实施的,但他们最近也在PyTorch中发布了FGSM的代码。
可以使用以下命令下载并安装该库:
pip install git+https://github.com/tensorflow/cleverhans.git#egg=cleverhans
将使用简单的MNIST数据集来演示如何构建攻击。
创建模型和数据加载器
首先,必须为MNIST数据集创建一个普通的PyTorch模型和数据加载器。
为了演示,将构建一个简单的卷积网络,如下所示:
# Creating a simple network
class LeNet5(torch.nn.Module):
def __init__(self):
super(LeNet5, self).__init__()
self.conv1 = torch.nn.Conv2d(1, 6, 5, padding=2)
self.conv2 = torch.nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16*5*5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.max_pool2d(x, 2)
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, 2)
x = x.view(-1, 16*5*5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return F.log_softmax(x,dim=-1)
和数据加载器如下:
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('data', train=True, download=True,
transform=transforms.ToTensor()),
batch_size=batch_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(
datasets.MNIST('data', train=False, transform=transforms.ToTensor()),
batch_size=batch_size)
之后实现了普通转发方法来对网络进行常规数据训练:
def trainTorch(torch_model, train_loader, test_loader,
nb_epochs=NB_EPOCHS, batch_size=BATCH_SIZE, train_end=-1, test_end=-1, learning_rate=LEARNING_RATE, optimizer=None):
train_loss = []
total = 0
correct = 0
step = 0
for _epoch in range(nb_epochs):
for xs, ys in train_loader:
xs, ys = Variable(xs), Variable(ys)
if torch.cuda.is_available():
xs, ys = xs.cuda(), ys.cuda()
optimizer.zero_grad()
preds = torch_model(xs)
loss = F.nll_loss(preds, ys)
loss.backward() # calc gradients
train_loss.append(loss.data.item())
optimizer.step() # update gradients
preds_np = preds.cpu().detach().numpy()
correct += (np.argmax(preds_np, axis=1) == ys.cpu().detach().numpy()).sum()
total += train_loader.batch_size
step += 1
if total % 1000 == 0:
acc = float(correct) / total
print('[%s] Training accuracy: %.2f%%' % (step, acc * 100))
total = 0
correct = 0
通过将批次大小设置为128,将时期数设置为4,将学习率设置为0.001,网络在训练后成功地在MNIST数据集上实现了约98%的精度。
施加攻击
训练完网络之后,可以根据网络架构应用FGSM攻击。
为此,必须首先从CleverHans导入所需的功能:
from cleverhans.future.torch.attacks.fast_gradient_method import fast_gradient_method
这使可以调用fast_gradient_method()函数,该函数简单明了:给定模型,输入x,ε和范数(norm = np.inf,1或2),该函数输出扰动的x 。
然后,可以通过馈送被扰动的x而不是原始x来稍微改变原始正向函数,以如下方式测量结果:
def evalAdvAttack(fgsm_model=None, test_loader=None):
print("Evaluating single model results on adv data")
total = 0
correct = 0
fgsm_model.eval()
for xs, ys in test_loader:
if torch.cuda.is_available():
xs, ys = xs.cuda(), ys.cuda()
#pytorch fast gradient method
xs = fast_gradient_method(fgsm_model, xs, eps=0.1, norm=np.inf, clip_min=0., clip_max=1.)
# xs = fast_gradient_method(fgsm_model, xs, eps=0.1, norm=np.inf)
xs, ys = Variable(xs), Variable(ys)
preds1 = fgsm_model(xs)
preds_np1 = preds1.cpu().detach().numpy()
finalPred = np.argmax(preds_np1, axis=1)
correct += (finalPred == ys.cpu().detach().numpy()).sum()
total += test_loader.batch_size
acc = float(correct) / total
print('Adv accuracy: {:.3f}%'.format(acc * 100))
经过测试,上述攻击实际上可以迫使精度从98%急剧下降到4%左右,证明如果朝正确的方向进行小扰动实际上会导致网络性能非常差。
PyTorch的对抗训练
在Ian等人的同一篇论文中,提出了对抗训练的方法来对抗这些样本。简而言之,从训练集生成的对抗样本也包括在训练中。
通过将原始训练集和受扰训练集同时输入到体系结构中,可以很容易地将此概念实现到代码中。请注意,两种数据类型都应用于对抗训练,以防止原始数据集准确性下降。下面的代码是我对战训练的实现:
def advTrain(torch_model, train_loader, test_loader,
nb_epochs=NB_EPOCHS, batch_size=BATCH_SIZE, train_end=-1, test_end=-1, learning_rate=LEARNING_RATE):
optimizer = optim.Adam(torch_model.parameters(), lr=learning_rate)
train_loss = []
total = 0
correct = 0
totalAdv = 0
correctAdv = 0
step = 0
# breakstep = 0
for _epoch in range(nb_epochs):
for xs, ys in train_loader:
#Normal Training
xs, ys = Variable(xs), Variable(ys)
if torch.cuda.is_available():
xs, ys = xs.cuda(), ys.cuda()
optimizer.zero_grad()
preds = torch_model(xs)
loss = F.nll_loss(preds, ys)
loss.backward() # calc gradients
train_loss.append(loss.data.item())
optimizer.step() # update gradients
preds_np = preds.cpu().detach().numpy()
correct += (np.argmax(preds_np, axis=1) == ys.cpu().detach().numpy()).sum()
total += train_loader.batch_size
#Adversarial Training
xs = fast_gradient_method(torch_model, xs, eps=0.3, norm=np.inf, clip_min=0., clip_max=1.)
xs, ys = Variable(xs), Variable(ys)
if torch.cuda.is_available():
xs, ys = xs.cuda(), ys.cuda()
optimizer.zero_grad()
preds = torch_model(xs)
loss = F.nll_loss(preds, ys)
loss.backward() # calc gradients
train_loss.append(loss.data.item())
optimizer.step() # update gradients
preds_np = preds.cpu().detach().numpy()
correctAdv += (np.argmax(preds_np, axis=1) == ys.cpu().detach().numpy()).sum()
totalAdv += train_loader.batch_size
step += 1
if total % 1000 == 0:
acc = float(correct) / total
print('[%s] Clean Training accuracy: %.2f%%' % (step, acc * 100))
total = 0
correct = 0
accAdv = float(correctAdv) / totalAdv
print('[%s] Adv Training accuracy: %.2f%%' % (step, accAdv * 100))
totalAdv = 0
correctAdv = 0
请注意,网络从已经接受过干净数据训练的检查点开始。在对抗训练期间,将干净的示例和对抗的示例都馈送到网络中,以防止在进一步的训练期间降低干净数据的准确性。
使用相同的批次大小,时间段和学习率设置,实际上可以将对抗性示例的准确性提高到大约90%,同时保持原始数据的准确性。
对抗训练的问题
尽管上述示例说明了如何采用对抗训练来概括模型体系结构,但是一个主要问题是,它们仅对训练模型的特定类型的攻击有效。由于不同的攻击会产生不同的对抗示例,因此需要进一步研究和评估对抗训练方法,以实现更好的对抗防御。
结论
FGSM和对抗训练是最早的攻击和防御之一。C&W攻击和DeepFool等最近的攻击以及蒸馏等防御措施为未来的研究和调查打开了新的机会。本文将介绍对抗性攻击领域,完整代码也发布在Github中:
https://github.com/ttchengab/FGSMAttack