ตัวอย่างที่เป็นปฏิปักษ์โดยใช้FGSM

ดูบน TensorFlow.org ทำงานใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดโน๊ตบุ๊ค

บทช่วยสอนนี้สร้าง ตัวอย่างที่เป็นปฏิปักษ์ โดยใช้การโจมตี Fast Gradient Signed Method (FGSM) ตามที่อธิบายไว้ใน การอธิบายและควบคุมตัวอย่างที่เป็นปฏิปักษ์ โดย Goodfellow et al นี่เป็นหนึ่งในการโจมตีครั้งแรกและเป็นที่นิยมมากที่สุดเพื่อหลอกเครือข่ายประสาทเทียม

ตัวอย่างที่เป็นปฏิปักษ์คืออะไร?

ตัวอย่างที่เป็นปฏิปักษ์คืออินพุตพิเศษที่สร้างขึ้นโดยมีวัตถุประสงค์เพื่อสร้างความสับสนให้กับโครงข่ายประสาทเทียม ส่งผลให้การจัดประเภทอินพุตที่กำหนดผิดพลาด อินพุตที่มีชื่อเสียงเหล่านี้ไม่สามารถแยกแยะได้ในสายตามนุษย์ แต่ทำให้เครือข่ายไม่สามารถระบุเนื้อหาของภาพได้ การโจมตีดังกล่าวมีหลายประเภท อย่างไรก็ตาม จุดเน้นอยู่ที่การโจมตีด้วยวิธีการไล่ระดับแบบเร็ว ซึ่งเป็นการโจมตีแบบ กล่องขาว ที่มีเป้าหมายเพื่อให้แน่ใจว่ามีการจัดประเภทที่ไม่ถูกต้อง การโจมตีกล่องขาวเป็นที่ที่ผู้โจมตีสามารถเข้าถึงแบบจำลองที่ถูกโจมตีได้อย่างสมบูรณ์ หนึ่งในตัวอย่างที่มีชื่อเสียงที่สุดของภาพปฏิปักษ์ที่แสดงด้านล่างนั้นนำมาจากบทความดังกล่าว

ตัวอย่างฝ่ายตรงข้าม

ที่นี่ เริ่มจากรูปภาพของแพนด้า ผู้โจมตีเพิ่มการรบกวนเล็กน้อย (การบิดเบือน) ให้กับภาพต้นฉบับ ซึ่งส่งผลให้นางแบบระบุรูปภาพนี้เป็นชะนีด้วยความมั่นใจสูง ขั้นตอนการเพิ่มการรบกวนเหล่านี้อธิบายไว้ด้านล่าง

วิธีเครื่องหมายไล่ระดับสีอย่างรวดเร็ว

วิธีเครื่องหมายไล่ระดับสีอย่างรวดเร็วทำงานโดยใช้การไล่ระดับสีของโครงข่ายประสาทเทียมเพื่อสร้างตัวอย่างที่เป็นปฏิปักษ์ สำหรับรูปภาพอินพุต วิธีนี้ใช้การไล่ระดับสีของการสูญเสียเทียบกับรูปภาพอินพุตเพื่อสร้างรูปภาพใหม่ที่เพิ่มการสูญเสียสูงสุด ภาพใหม่นี้เรียกว่าภาพปฏิปักษ์ สามารถสรุปได้โดยใช้นิพจน์ต่อไปนี้:

\[adv\_x = x + \epsilon*\text{sign}(\nabla_xJ(\theta, x, y))\]

ที่ไหน

  • adv_x : รูปภาพที่เป็นปฏิปักษ์
  • x : รูปภาพต้นฉบับที่ป้อน
  • y : ป้ายป้อนต้นฉบับ
  • \(\epsilon\) : ตัวคูณเพื่อให้แน่ใจว่าการรบกวนมีขนาดเล็ก
  • \(\theta\) : พารามิเตอร์โมเดล
  • \(J\) : ขาดทุน

คุณสมบัติที่น่าสนใจที่นี่คือข้อเท็จจริงที่ว่าการไล่ระดับนั้นสัมพันธ์กับภาพที่ป้อนเข้า สิ่งนี้เกิดขึ้นเพราะมีวัตถุประสงค์เพื่อสร้างภาพที่สูญเสียมากที่สุด วิธีที่จะทำให้สำเร็จคือค้นหาว่าแต่ละพิกเซลในภาพมีส่วนทำให้เกิดค่าที่สูญเสียไปมากน้อยเพียงใด และเพิ่มการรบกวนตามนั้น การทำงานนี้ค่อนข้างเร็วเพราะง่ายต่อการค้นหาว่าแต่ละพิกเซลอินพุตมีส่วนทำให้เกิดการสูญเสียอย่างไรโดยใช้กฎลูกโซ่และค้นหาการไล่ระดับสีที่ต้องการ ดังนั้น การไล่ระดับสีจะถูกถ่ายโดยสัมพันธ์กับภาพ นอกจากนี้ เนื่องจากโมเดลไม่ได้รับการฝึกอบรมอีกต่อไป (ดังนั้นจึงไม่ใช้การไล่ระดับสีเมื่อเทียบกับตัวแปรที่ฝึกได้ เช่น พารามิเตอร์ของโมเดล) ดังนั้นพารามิเตอร์ของโมเดลจึงคงที่ เป้าหมายเดียวคือการหลอกนางแบบที่ได้รับการฝึกฝนมาแล้ว

มาลองหลอกโมเดลที่ได้รับการฝึกมาแล้วกัน ในบทช่วยสอนนี้ โมเดลนี้เป็นรุ่น MobileNetV2 ซึ่งได้รับการฝึกอบรมล่วงหน้าบน ImageNet

import tensorflow as tf
import matplotlib as mpl
import matplotlib.pyplot as plt

mpl.rcParams['figure.figsize'] = (8, 8)
mpl.rcParams['axes.grid'] = False

มาโหลดโมเดล MobileNetV2 ที่ฝึกไว้ล่วงหน้าและชื่อคลาส ImageNet กัน

pretrained_model = tf.keras.applications.MobileNetV2(include_top=True,
                                                     weights='imagenet')
pretrained_model.trainable = False

# ImageNet labels
decode_predictions = tf.keras.applications.mobilenet_v2.decode_predictions
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet_v2/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_224.h5
14540800/14536120 [==============================] - 0s 0us/step
14548992/14536120 [==============================] - 0s 0us/step
# Helper function to preprocess the image so that it can be inputted in MobileNetV2
def preprocess(image):
  image = tf.cast(image, tf.float32)
  image = tf.image.resize(image, (224, 224))
  image = tf.keras.applications.mobilenet_v2.preprocess_input(image)
  image = image[None, ...]
  return image

# Helper function to extract labels from probability vector
def get_imagenet_label(probs):
  return decode_predictions(probs, top=1)[0][0]

ภาพต้นฉบับ

ลองใช้ภาพตัวอย่างของ Labrador Retriever โดย Mirko CC-BY-SA 3.0 จาก Wikimedia Common และสร้างตัวอย่างที่เป็นปฏิปักษ์จากภาพดังกล่าว ขั้นตอนแรกคือการประมวลผลล่วงหน้าเพื่อให้สามารถป้อนเป็นอินพุตไปยังรุ่น MobileNetV2

image_path = tf.keras.utils.get_file('YellowLabradorLooking_new.jpg', 'https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg')
image_raw = tf.io.read_file(image_path)
image = tf.image.decode_image(image_raw)

image = preprocess(image)
image_probs = pretrained_model.predict(image)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg
90112/83281 [================================] - 0s 0us/step
98304/83281 [===================================] - 0s 0us/step

มาชมภาพกันเลยครับ

plt.figure()
plt.imshow(image[0] * 0.5 + 0.5)  # To change [-1, 1] to [0,1]
_, image_class, class_confidence = get_imagenet_label(image_probs)
plt.title('{} : {:.2f}% Confidence'.format(image_class, class_confidence*100))
plt.show()
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/imagenet_class_index.json
40960/35363 [==================================] - 0s 0us/step
49152/35363 [=========================================] - 0s 0us/step

png

สร้างภาพปฏิปักษ์

การใช้วิธีการเครื่องหมายไล่ระดับอย่างรวดเร็ว

ขั้นตอนแรกคือการสร้างสิ่งรบกวนซึ่งจะใช้ในการบิดเบือนภาพต้นฉบับส่งผลให้เกิดภาพปฏิปักษ์ ดังที่กล่าวไว้ สำหรับงานนี้ การไล่ระดับจะถูกถ่ายโดยสัมพันธ์กับภาพ

loss_object = tf.keras.losses.CategoricalCrossentropy()

def create_adversarial_pattern(input_image, input_label):
  with tf.GradientTape() as tape:
    tape.watch(input_image)
    prediction = pretrained_model(input_image)
    loss = loss_object(input_label, prediction)

  # Get the gradients of the loss w.r.t to the input image.
  gradient = tape.gradient(loss, input_image)
  # Get the sign of the gradients to create the perturbation
  signed_grad = tf.sign(gradient)
  return signed_grad

นอกจากนี้ยังสามารถเห็นภาพการรบกวนที่เกิดขึ้นได้

# Get the input label of the image.
labrador_retriever_index = 208
label = tf.one_hot(labrador_retriever_index, image_probs.shape[-1])
label = tf.reshape(label, (1, image_probs.shape[-1]))

perturbations = create_adversarial_pattern(image, label)
plt.imshow(perturbations[0] * 0.5 + 0.5);  # To change [-1, 1] to [0,1]

png

ลองใช้ค่าต่างๆ ของ epsilon และดูผลลัพธ์ที่ได้ คุณจะสังเกตได้ว่าเมื่อค่าของ epsilon เพิ่มขึ้น การหลอกเครือข่ายจะง่ายขึ้น อย่างไรก็ตาม นี่เป็นการประนีประนอมซึ่งส่งผลให้ความยุ่งยากสามารถระบุตัวตนได้มากขึ้น

def display_images(image, description):
  _, label, confidence = get_imagenet_label(pretrained_model.predict(image))
  plt.figure()
  plt.imshow(image[0]*0.5+0.5)
  plt.title('{} \n {} : {:.2f}% Confidence'.format(description,
                                                   label, confidence*100))
  plt.show()
epsilons = [0, 0.01, 0.1, 0.15]
descriptions = [('Epsilon = {:0.3f}'.format(eps) if eps else 'Input')
                for eps in epsilons]

for i, eps in enumerate(epsilons):
  adv_x = image + eps*perturbations
  adv_x = tf.clip_by_value(adv_x, -1, 1)
  display_images(adv_x, descriptions[i])

png

png

png

png

ขั้นตอนถัดไป

ตอนนี้คุณรู้เกี่ยวกับการโจมตีของฝ่ายตรงข้ามแล้ว ลองใช้ชุดข้อมูลและสถาปัตยกรรมที่แตกต่างกัน คุณยังสร้างและฝึกโมเดลของคุณเองได้ แล้วพยายามหลอกด้วยวิธีเดียวกัน คุณยังสามารถลองดูว่าความเชื่อมั่นในการทำนายแตกต่างกันอย่างไรเมื่อคุณเปลี่ยนเอปซิลอน

แม้ว่าจะมีประสิทธิภาพ แต่การโจมตีที่แสดงในบทช่วยสอนนี้เป็นเพียงการเริ่มต้นของการวิจัยเกี่ยวกับการโจมตีของฝ่ายตรงข้าม และนับตั้งแต่นั้นเป็นต้นมาก็มีเอกสารหลายฉบับที่สร้างการโจมตีที่ทรงพลังมากขึ้น นอกจากการโจมตีจากฝ่ายตรงข้ามแล้ว การวิจัยยังนำไปสู่การสร้างการป้องกัน ซึ่งมุ่งสร้างโมเดลการเรียนรู้ของเครื่องที่แข็งแกร่ง คุณสามารถตรวจสอบ เอกสารสำรวจ นี้เพื่อดูรายการการโจมตีและการป้องกันของฝ่ายตรงข้ามที่ครอบคลุม

สำหรับการใช้งานการโจมตีและการป้องกันของฝ่ายตรงข้ามมากขึ้น คุณอาจต้องการดูตัวอย่างไลบรารี CleverHans ที่เป็นปฏิปักษ์