专栏首页机器之心教程 | 经得住考验的「假图片」:用TensorFlow为神经网络生成对抗样本

教程 | 经得住考验的「假图片」:用TensorFlow为神经网络生成对抗样本

选自arXiv

作者:Anish Athalye

机器之心编译

参与:李泽南

用于识别图片中物体的神经网络可以被精心设计的对抗样本欺骗,而这些在人类看起来没有什么问题的图片是如何产生的呢?最近,来自 OpenAI 的研究者 Anish Athalye 等人撰文介绍了他们使用 TensorFlow 制作「假图片」的方法。

为神经网络加入对抗样本是一个简单而有意义的工作:仔细设计的「扰动」输入可以让神经网络受到分类任务上的挑战。这些对抗样本如果应用到现实世界中可能会导致安全问题,因此非常值得关注。

仅仅加入一些特殊的噪点,图像识别系统就会把大熊猫认作是长臂猿——而且是 99% 置信度。想象一下,如果无人驾驶汽车遇到了这种情况……

在本文中,我们将简要介绍用于合成对抗样本的算法,并将演示如何将其应用到 TensorFlow 中,构建稳固对抗样本的方法。

Jupyter notebook 可执行文件:http://www.anishathalye.com/media/2017/07/25/adversarial.ipynb

设置

我们选择攻击用 ImageNet 训练的 Inception v3 模型。在这一节里,我们会从 TF-slim 图像分类库中加载一个预训练的神经网络。

import tensorflow as tf
import tensorflow.contrib.slim as slim
import tensorflow.contrib.slim.nets as nets
tf.logging.set_verbosity(tf.logging.ERROR)
sess = tf.InteractiveSession()

首先,我们需要设置一张输入图片。我们使用 tf.Variable 而非 tf.placeholder 是因为我们会需要让数据可被训练,这样我们就可以在需要时继续输入。

image = tf.Variable(tf.zeros((299, 299, 3)))

随后,我们加载 Inception v3 模型。

def inception(image, reuse):
    preprocessed = tf.multiply(tf.subtract(tf.expand_dims(image, 0), 0.5), 2.0)
    arg_scope = nets.inception.inception_v3_arg_scope(weight_decay=0.0)
    with slim.arg_scope(arg_scope):
        logits, _ = nets.inception.inception_v3(
            preprocessed, 1001, is_training=False, reuse=reuse)
        logits = logits[:,1:] # ignore background class
        probs = tf.nn.softmax(logits) # probabilities
    return logits, probs

logits, probs = inception(image, reuse=False)

接下来,我们加载预训练权重。这种 Inception v3 模型的 top-5 正确率为 93.9%。

import tempfile
from urllib.request import urlretrieve
import tarfile
import os
data_dir = tempfile.mkdtemp()
inception_tarball, _ = urlretrieve(
    'http://download.tensorflow.org/models/inception_v3_2016_08_28.tar.gz')
tarfile.open(inception_tarball, 'r:gz').extractall(data_dir)
restore_vars = [
    var for var in tf.global_variables()
    if var.name.startswith('InceptionV3/')
]
saver = tf.train.Saver(restore_vars)
saver.restore(sess, os.path.join(data_dir, 'inception_v3.ckpt'))

接下来,我们编写代码来显示一张图片,对其进行分类,并显示分类结果。

import json
import matplotlib.pyplot as plt
imagenet_json, _ = urlretrieve(
    'http://www.anishathalye.com/media/2017/07/25/imagenet.json')
with open(imagenet_json) as f:
    imagenet_labels = json.load(f)
def classify(img, correct_class=None, target_class=None):
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 8))
    fig.sca(ax1)
    p = sess.run(probs, feed_dict={image: img})[0]
    ax1.imshow(img)
    fig.sca(ax1)

    topk = list(p.argsort()[-10:][::-1])
    topprobs = p[topk]
    barlist = ax2.bar(range(10), topprobs)
    if target_class in topk:
        barlist[topk.index(target_class)].set_color('r')
    if correct_class in topk:
        barlist[topk.index(correct_class)].set_color('g')
    plt.sca(ax2)
    plt.ylim([0, 1.1])
    plt.xticks(range(10),
               [imagenet_labels[i][:15] for i in topk],
               rotation='vertical')
    fig.subplots_adjust(bottom=0.2)
    plt.show()

示例图片

我们加载示例图片,并确认它们已被正确分类。

import PIL
import numpy as np
img_path, _ = urlretrieve('http://www.anishathalye.com/media/2017/07/25/cat.jpg')
img_class = 281
img = PIL.Image.open(img_path)
big_dim = max(img.width, img.height)
wide = img.width > img.height
new_w = 299 if not wide else int(img.width * 299 / img.height)
new_h = 299 if wide else int(img.height * 299 / img.width)
img = img.resize((new_w, new_h)).crop((0, 0, 299, 299))
img = (np.asarray(img) / 255.0).astype(np.float32)
classify(img, correct_class=img_class)

对抗样本

给定图片 X,我们的神经网络输出 P(y|x)上的概率分布。当我们制作对抗性输入时,我们想找到一个 x ^,其中 logP(y ^ | x ^)对于目标标签 y ^是最大化的:这样,我们的输入就会被误分类为目标类别中。我们可以通过把自己约束在半径为ϵ的ℓ∞框中来确保 x ^看起来与原始的 x 看起来差不多,这就需要∥x−x^∥∞≤ϵ了。

在这个框架中,一个对抗性样本是约束优化问题的解,我们可以使用反向传播和预测梯度下降来求得它,这基本上是用于训练网络本身的相同技术。算法很简单:

我们首先将对抗样本初始化为 x^←x。然后重复以下过程直到收敛为止:

初始化

我们先从最简单的开始:写一个用于初始化的 TensorFlow op。

x = tf.placeholder(tf.float32, (299, 299, 3))

x_hat = image # our trainable adversarial input
assign_op = tf.assign(x_hat, x)

梯度下降

接下来,我们编写梯度下降步骤来最大化目标类的记录可能性(或等价地,最小化交叉熵)。

learning_rate = tf.placeholder(tf.float32, ())
y_hat = tf.placeholder(tf.int32, ())

labels = tf.one_hot(y_hat, 1000)
loss = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=[labels])
optim_step = tf.train.GradientDescentOptimizer(
    learning_rate).minimize(loss, var_list=[x_hat])

投影步骤

最后,我们编写投影步骤来使对抗样本和原始图片看起来更接近。此外,我们会将图片剪辑为 [0,1],以让图片保持有效。

epsilon = tf.placeholder(tf.float32, ())

below = x - epsilon
above = x + epsilon
projected = tf.clip_by_value(tf.clip_by_value(x_hat, below, above), 0, 1)
with tf.control_dependencies([projected]):
    project_step = tf.assign(x_hat, projected)

执行

我们已准备好合成一个对抗样本图了,我们准备选择「鳄梨酱」(ImageNet 分类 924)来作为它的错误分类。

demo_epsilon = 2.0/255.0 # a really small perturbation
demo_lr = 1e-1
demo_steps = 100
demo_target = 924 # "guacamole"

# initialization step
sess.run(assign_op, feed_dict={x: img})

# projected gradient descent
for i in range(demo_steps):
    # gradient descent step
    _, loss_value = sess.run(
        [optim_step, loss],
        feed_dict={learning_rate: demo_lr, y_hat: demo_target})
    # project step
    sess.run(project_step, feed_dict={x: img, epsilon: demo_epsilon})
    if (i+1) % 10 == 0:
        print('step %d, loss=%g' % (i+1, loss_value))


adv = x_hat.eval() # retrieve the adversarial example
step 10, loss=4.18923
step 20, loss=0.580237
step 30, loss=0.0322334
step 40, loss=0.0209522
step 50, loss=0.0159688
step 60, loss=0.0134457
step 70, loss=0.0117799
step 80, loss=0.0105757
step 90, loss=0.00962179
step 100, loss=0.00886694

这个对抗图片看起来和原图没什么区别,没有任何可见的人造痕迹。然而,它被明确地分类为「鳄梨酱」,而且置信度极高!

classify(adv, correct_class=img_class, target_class=demo_target)

稳固的对抗样本

现在,让我们看看更高级的例子。我们遵循论文《Synthesizing Robust Adversarial Examples》的方法来寻找一个强大的对抗性样本,希望寻找到这张猫图片的单一扰动,让它在某些转变后仍然保持「假分类」。我们可以选择任何可微分变换的分布,在本文中,我们将演示图片如何在旋转θ∈[−π/4,π/4] 后保持对抗性输入。

在开始前,我们先检查一下刚才的对抗性样本在旋转后还管不管用,让我们旋转θ=π/8。

ex_angle = np.pi/8

angle = tf.placeholder(tf.float32, ())
rotated_image = tf.contrib.image.rotate(image, angle)
rotated_example = rotated_image.eval(feed_dict={image: adv, angle: ex_angle})
classify(rotated_example, correct_class=img_class, target_class=demo_target)

看起来我们的原始对抗性样本不太管用了!

所以,如何让我们的对抗样本在旋转后仍有效果?给定变换 T 的分布,我们可以最大化 Et∼TlogP(y^∣t(x^)),使其服从∥x−x^∥∞≤ϵ。我们可以通过投影梯度下降来解决这个优化问题。注意,∇Et∼TlogP(y^∣t(x^)) 是 Et∼T∇logP(y^∣t(x^)) 并在每个梯度下降步骤中与样本近似。

与手动实现梯度采样不同,我们可以使用一个 trick 来让 TesnorFlow 为我们做到这点:我们可以对基于采样的梯度下降建模,作为随机分类器的集合中的梯度下降——在输入内容被分类前随机从分布和变化中采样。

num_samples = 10
average_loss = 0
for i in range(num_samples):
    rotated = tf.contrib.image.rotate(
        image, tf.random_uniform((), minval=-np.pi/4, maxval=np.pi/4))
    rotated_logits, _ = inception(rotated, reuse=True)
    average_loss += tf.nn.softmax_cross_entropy_with_logits(
        logits=rotated_logits, labels=labels) / num_samples

我们可以重新使用 assign_op 和 project_step 指令,尽管我们必须为这个新的目标写一个新的 optim_step。

optim_step = tf.train.GradientDescentOptimizer(
    learning_rate).minimize(average_loss, var_list=[x_hat])

最后,我们准备运行 PGD 来生成新的对抗输入。和刚才的例子一样,我们选择「鳄梨酱」来作为目标分类。

demo_epsilon = 8.0/255.0 # still a pretty small perturbation
demo_lr = 2e-1
demo_steps = 300
demo_target = 924 # "guacamole"

# initialization step
sess.run(assign_op, feed_dict={x: img})

# projected gradient descent
for i in range(demo_steps):
    # gradient descent step
    _, loss_value = sess.run(
        [optim_step, average_loss],
        feed_dict={learning_rate: demo_lr, y_hat: demo_target})
    # project step
    sess.run(project_step, feed_dict={x: img, epsilon: demo_epsilon})
    if (i+1) % 50 == 0:
        print('step %d, loss=%g' % (i+1, loss_value))


adv_robust = x_hat.eval() # retrieve the adversarial example
step 50, loss=0.0804289
step 100, loss=0.0270499
step 150, loss=0.00771527
step 200, loss=0.00350717
step 250, loss=0.00656128
step 300, loss=0.00226182

完成了,这种对抗图片被分类为「鳄梨酱」,置信度很高,即使是在被旋转后也没问题!

rotated_example = rotated_image.eval(feed_dict={image: adv_robust, angle: ex_angle})
classify(rotated_example, correct_class=img_class, target_class=demo_target)

让我们评估一下所有角度旋转后的图片「鲁棒性」,看看从 P(y^∣x^) 在θ∈[−π/4,π/4] 角度中的效果。

thetas = np.linspace(-np.pi/4, np.pi/4, 301)

p_naive = []
p_robust = []
for theta in thetas:
    rotated = rotated_image.eval(feed_dict={image: adv_robust, angle: theta})
    p_robust.append(probs.eval(feed_dict={image: rotated})[0][demo_target])

    rotated = rotated_image.eval(feed_dict={image: adv, angle: theta})
    p_naive.append(probs.eval(feed_dict={image: rotated})[0][demo_target])

robust_line, = plt.plot(thetas, p_robust, color='b', linewidth=2, label='robust')
naive_line, = plt.plot(thetas, p_naive, color='r', linewidth=2, label='naive')
plt.ylim([0, 1.05])
plt.xlabel('rotation angle')
plt.ylabel('target class probability')
plt.legend(handles=[robust_line, naive_line], loc='lower right')
plt.show()

如图所示,效果非常棒!

论文:Synthesizing Robust Adversarial Examples

论文链接:https://arxiv.org/abs/1707.07397

神经网络容易受到对抗性样本的影响:一点点精巧的扰动信息就可以让网络完全错判。然而,一些研究证明了这些对抗性样本在面对小小的转换之后就会失效:例如,放大图片就会让图像分类重新回归正确。这为我们带来了新的课题:在实践中,是否存在对抗性样本?因为在现实世界中,图像的远近和视角总是在不断变化的。

本论文证明了对抗性样本可以是鲁棒的——能够经受住各种变换的考验。我们的新方法可以让一张图片在经历所有变换之后仍然保持错误分类。实验证明,我们不能依赖缩放、转换和旋转来解决对抗性样本的问题。

原文链接:http://www.anishathalye.com/2017/07/25/synthesizing-adversarial-examples/?utm_source=mybridge&utm_medium=blog&utm_campaign=read_more

本文为机器之心编译,转载请联系本公众号获得授权。

本文分享自微信公众号 - 机器之心(almosthuman2014)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-08-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 强化学习如何使用内在动机?

    「内在动机」 (Intrinsic Motivation) 这一概念最初是在心理学中提出并发展起来的。由于其在制造开放式学习机器和机器人方面的潜力,这一概念正日...

    机器之心
  • 纽约客特稿 | 把癌症诊断交给机器,医疗服务会更好吗?

    选自Newyorker 作者:Siddhartha Mukherjee 机器之心编译 参与:侯韵楚、Rick R、微胖、吴攀、蒋思源 深度学习系统变得越强大,它...

    机器之心
  • 扒出了3867篇论文中的3万个基准测试结果,他们发现追求SOTA其实没什么意义

    在基准数据集上比较模型性能是人工智能领域衡量和推动研究进展的重要方式之一。研究者通常基于模型在基准数据集上的一个或一组性能指标进行评估,虽然这样可以快速进行比较...

    机器之心
  • 2018-11-23 数据引擎排名,2018年11月数据库趋势图

    实时数据 https://db-engines.com/en/ranking_trend

    Albert陈凯
  • 之所以被裁也许是少了这些东西 [每日前端夜话(0x0A)]

    人们总是认为我懂的很多。 这种想法并不那么差劲,所以我对此并不排斥。 (不过有少数人尽管知识渊博,但他们往往遭受相反的偏见,这很糟糕。)

    疯狂的技术宅
  • GAN 的 keras 实现

    本文结构: 什么是 GAN? 优点? keras 例子? ---- 什么是 GAN? GAN,全称为 Generative Adversarial Nets,直...

    杨熹
  • C++像Go一样的并发与闭包

    并发性是程序的一种属性,其中两个或多个任务可以同时进行。并行性是一个运行时属性,其中两个或多个任务同时执行。通过并发性,为程序定义一个适当的结构。并发可以使用并...

    公众号guangcity
  • 干货 | 数据科学和机器学习面试问题集锦

    在过去的几个月里,我面试了许多公司涉及数据科学和机器学习的初级职位。介绍一下我自己,以让你对我有一定了解。我还有最后几个月就研究生毕业,我的硕士是机器学习和计算...

    AI科技评论
  • 比特币分叉在即,你会被重放攻击吗?

    前几天BIP91被锁定,大家以为比特币不会分叉了,没想到这几天杀出来一个比特现金Bitcoin Cash(前身是Bitcoin ABC),忽悠了一些矿池的算力来...

    申龙斌
  • Nilearn学习笔记1-Nilearn库介绍

    nilearn是一个将机器学习、模式识别、多变量分析等技术应用于神经影像数据的应用中,能完成多体素模式分析(MVPA:mutli-voxel pattern a...

    锦小年

扫码关注云+社区

领取腾讯云代金券