ดูบน 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
สร้างภาพปฏิปักษ์
การใช้วิธีการเครื่องหมายไล่ระดับอย่างรวดเร็ว
ขั้นตอนแรกคือการสร้างสิ่งรบกวนซึ่งจะใช้ในการบิดเบือนภาพต้นฉบับส่งผลให้เกิดภาพปฏิปักษ์ ดังที่กล่าวไว้ สำหรับงานนี้ การไล่ระดับจะถูกถ่ายโดยสัมพันธ์กับภาพ
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]
ลองใช้ค่าต่างๆ ของ 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])
ขั้นตอนถัดไป
ตอนนี้คุณรู้เกี่ยวกับการโจมตีของฝ่ายตรงข้ามแล้ว ลองใช้ชุดข้อมูลและสถาปัตยกรรมที่แตกต่างกัน คุณยังสร้างและฝึกโมเดลของคุณเองได้ แล้วพยายามหลอกด้วยวิธีเดียวกัน คุณยังสามารถลองดูว่าความเชื่อมั่นในการทำนายแตกต่างกันอย่างไรเมื่อคุณเปลี่ยนเอปซิลอน
แม้ว่าจะมีประสิทธิภาพ แต่การโจมตีที่แสดงในบทช่วยสอนนี้เป็นเพียงการเริ่มต้นของการวิจัยเกี่ยวกับการโจมตีของฝ่ายตรงข้าม และนับตั้งแต่นั้นเป็นต้นมาก็มีเอกสารหลายฉบับที่สร้างการโจมตีที่ทรงพลังมากขึ้น นอกจากการโจมตีจากฝ่ายตรงข้ามแล้ว การวิจัยยังนำไปสู่การสร้างการป้องกัน ซึ่งมุ่งสร้างโมเดลการเรียนรู้ของเครื่องที่แข็งแกร่ง คุณสามารถตรวจสอบ เอกสารสำรวจ นี้เพื่อดูรายการการโจมตีและการป้องกันของฝ่ายตรงข้ามที่ครอบคลุม
สำหรับการใช้งานการโจมตีและการป้องกันของฝ่ายตรงข้ามมากขึ้น คุณอาจต้องการดูตัวอย่างไลบรารี CleverHans ที่เป็นปฏิปักษ์