هجوم بكسل واحد. أو كيفية خداع شبكة عصبية

دعونا نتعرف على إحدى الهجمات على الشبكة العصبية ، مما يؤدي إلى أخطاء التصنيف بأقل قدر من التأثيرات الخارجية. تخيل للحظة أن الشبكة العصبية هي أنت. وفي الوقت الحالي ، أثناء شرب فنجان من القهوة العطرية ، يمكنك تصنيف صور القطط بدقة تزيد عن 90 بالمائة دون حتى الشك في أن "هجوم البكسل الواحد" حول "قططك" إلى شاحنات.

والآن سنتوقف مؤقتًا ، وننقل القهوة جانبًا ، ونستورد جميع المكتبات التي نحتاجها ونرى كيف تعمل هجمات البكسل هذه.

الغرض من هذا الهجوم هو جعل الخوارزمية (الشبكة العصبية) تعطي إجابة غير صحيحة. أدناه سنرى هذا مع العديد من النماذج المختلفة للشبكات العصبية التلافيفية. باستخدام إحدى طرق التحسين الرياضي متعدد الأبعاد - التطور التفاضلي ، نجد بكسلًا خاصًا يمكنه تغيير الصورة بحيث تبدأ الشبكة العصبية في تصنيف هذه الصورة بشكل غير صحيح (على الرغم من حقيقة أن الخوارزمية في وقت سابق "تعرفت" على نفس الصورة بشكل صحيح وبدقة عالية).

إستيراد المكتبات:

# Python Libraries
%matplotlib inline
import pickle
import numpy as np
import pandas as pd
import matplotlib
from keras.datasets import cifar10
from keras import backend as K

# Custom Networks
from networks.lenet import LeNet
from networks.pure_cnn import PureCnn
from networks.network_in_network import NetworkInNetwork
from networks.resnet import ResNet
from networks.densenet import DenseNet
from networks.wide_resnet import WideResNet
from networks.capsnet import CapsNet

# Helper functions
from differential_evolution import differential_evolution
import helper

matplotlib.style.use('ggplot')

لتجربتنا ، سنقوم بتحميل مجموعة بيانات CIFAR-10 التي تحتوي على صور من العالم الحقيقي مقسمة إلى 10 فئات.

(x_train, y_train), (x_test, y_test) = cifar10.load_data()

class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']

دعونا نلقي نظرة على أي صورة من خلال فهرسها. على سبيل المثال ، هنا على هذا الحصان.

image_id = 99 # Image index in the test set
helper.plot_image(x_test[image_id])



سيتعين علينا البحث عن البكسل القوي جدًا الذي يمكنه تغيير استجابة الشبكة العصبية ، مما يعني أن الوقت قد حان لكتابة وظيفة لتغيير بكسل واحد أو أكثر من الصورة.

def perturb_image(xs, img):
    # If this function is passed just one perturbation vector,
    # pack it in a list to keep the computation the same
    if xs.ndim < 2:
        xs = np.array([xs])
    
    # Copy the image n == len(xs) times so that we can 
    # create n new perturbed images
    tile = [len(xs)] + [1]*(xs.ndim+1)
    imgs = np.tile(img, tile)
    
    # Make sure to floor the members of xs as int types
    xs = xs.astype(int)
    
    for x,img in zip(xs, imgs):
        # Split x into an array of 5-tuples (perturbation pixels)
        # i.e., [[x,y,r,g,b], ...]
        pixels = np.split(x, len(x) // 5)
        for pixel in pixels:
            # At each pixel's x,y position, assign its rgb value
            x_pos, y_pos, *rgb = pixel
            img[x_pos, y_pos] = rgb
    
    return imgs

تحقق من ذلك ؟! قم بتغيير بكسل واحد من حصاننا بإحداثيات (16 ، 16) إلى اللون الأصفر.

image_id = 99 # Image index in the test set
pixel = np.array([16, 16, 255, 255, 0]) # pixel = x,y,r,g,b
image_perturbed = perturb_image(pixel, x_test[image_id])[0]

helper.plot_image(image_perturbed)



لإثبات الهجوم ، تحتاج إلى تنزيل نماذج مدربة مسبقًا من الشبكات العصبية على مجموعة بيانات CIFAR-10. سنستخدم نموذجين lenet و resnet ، ولكن يمكنك استخدام نماذج أخرى لتجاربك من خلال إلغاء تعليق الأسطر المقابلة من التعليمات البرمجية.

lenet = LeNet()
resnet = ResNet()

models = [lenet, resnet]

بعد تحميل النماذج ، من الضروري تقييم صور الاختبار لكل نموذج للتأكد من أننا نهاجم فقط الصور المصنفة بشكل صحيح. يعرض الرمز أدناه دقة وعدد المعلمات لكل نموذج.

network_stats, correct_imgs = helper.evaluate_models(models, x_test, y_test)

correct_imgs = pd.DataFrame(correct_imgs, columns=['name', 'img', 'label', 'confidence', 'pred'])

network_stats = pd.DataFrame(network_stats, columns=['name', 'accuracy', 'param_count'])

network_stats
Evaluating lenet
Evaluating resnet

Out[11]:


	name        accuracy    param_count
0      lenet        0.748       62006
1      resnet       0.9231      470218


يمكن تقسيم جميع هذه الهجمات إلى فئتين: WhiteBox و BlackBox. الفرق بينهما هو أنه في الحالة الأولى ، نعرف جميعًا بشكل موثوق به عن الخوارزمية ، النموذج الذي نتعامل معه. في حالة BlackBox ، كل ما نحتاجه هو الإدخال (الصورة) والإخراج (احتمالات التعيين في إحدى الفئات). يشير هجوم بكسل واحد إلى BlackBox.

في هذه المقالة ، نعتبر خيارين لمهاجمة بكسل واحد: غير مستهدف وموجه. في الحالة الأولى ، لن يهم على الإطلاق أي فئة ستنتمي الشبكة العصبية لقطتنا ، والأهم من ذلك ، ليس لفئة القطط. الهجوم المستهدف قابل للتطبيق عندما نريد أن تصبح قطتنا شاحنة وفقط شاحنة.

ولكن كيف يمكن العثور على وحدات البكسل نفسها التي سيؤدي تغييرها إلى تغيير في فئة الصورة؟ كيفية العثور على بكسل عن طريق تغيير هجوم بكسل واحد ممكن ونجاح؟ دعونا نحاول صياغة هذه المشكلة كمشكلة تحسين ، ولكن فقط بكلمات بسيطة للغاية: من خلال هجوم غير مستهدف ، يجب علينا تقليل الثقة في الفئة المطلوبة ، ومع الهدف ، زيادة الثقة في الفئة المستهدفة.

عند تنفيذ مثل هذه الهجمات ، من الصعب تحسين الوظيفة باستخدام التدرج. يجب استخدام خوارزمية تحسين لا تعتمد على سلاسة الوظيفة.

تذكر أنه في تجربتنا نستخدم مجموعة بيانات CIFAR-10 ، التي تحتوي على صور من العالم الحقيقي ، بحجم 32 × 32 بكسل ، مقسمة إلى 10 فئات. وهذا يعني أن لدينا قيمًا صحيحة منفصلة من 0 إلى 31 وكثافات لونية من 0 إلى 255 ، ولا يُتوقع أن تكون الوظيفة سلسة ، بل متعرجة ، كما هو موضح أدناه:



لهذا السبب نستخدم خوارزمية التطور التفاضلي.

ولكن بالعودة إلى الكود وكتابة دالة ترجع احتمالية موثوقية النموذج. إذا كانت الفئة المستهدفة صحيحة ، فنحن نريد تقليل هذه الوظيفة حتى يكون النموذج متأكدًا من فئة أخرى (وهذا غير صحيح).

def predict_classes(xs, img, target_class, model, minimize=True):
    # Perturb the image with the given pixel(s) x and get the prediction of the model
    imgs_perturbed = perturb_image(xs, img)
    predictions = model.predict(imgs_perturbed)[:,target_class]
    # This function should always be minimized, so return its complement if needed
    return predictions if minimize else 1 - predictions

image_id = 384
pixel = np.array([16, 13,  25, 48, 156])
model = resnet

true_class = y_test[image_id, 0]
prior_confidence = model.predict_one(x_test[image_id])[true_class]
confidence = predict_classes(pixel, x_test[image_id], true_class, model)[0]

print('Confidence in true class', class_names[true_class], 'is', confidence)
print('Prior confidence was', prior_confidence)
helper.plot_image(perturb_image(pixel, x_test[image_id])[0])

Confidence in true class bird is 0.00018887444
Prior confidence was 0.70661753



سنحتاج إلى الوظيفة التالية لتأكيد معيار نجاح الهجوم ، وسوف يعود صحيحًا عندما يكون التغيير كافيًا لخداع النموذج.

def attack_success(x, img, target_class, model, targeted_attack=False, verbose=False):
    # Perturb the image with the given pixel(s) and get the prediction of the model
    attack_image = perturb_image(x, img)
    confidence = model.predict(attack_image)[0]
    predicted_class = np.argmax(confidence)
    
    # If the prediction is what we want (misclassification or 
    # targeted classification), return True
    if verbose:
        print('Confidence:', confidence[target_class])
    if ((targeted_attack and predicted_class == target_class) or
        (not targeted_attack and predicted_class != target_class)):
        return True
    # NOTE: return None otherwise (not False), due to how Scipy handles its callback function

دعونا نلقي نظرة على عمل دالة معيار النجاح. من أجل التظاهر ، نفترض هجومًا غير مستهدف.

image_id = 541
pixel = np.array([17, 18, 185, 36, 215])
model = resnet

true_class = y_test[image_id, 0]
prior_confidence = model.predict_one(x_test[image_id])[true_class]
success = attack_success(pixel, x_test[image_id], true_class, model, verbose=True)

print('Prior confidence', prior_confidence)
print('Attack success:', success == True)
helper.plot_image(perturb_image(pixel, x_test[image_id])[0])
Confidence: 0.07460087
Prior confidence 0.50054216
Attack success: True



حان الوقت لجمع جميع الألغاز في صورة واحدة. سنستخدم تعديل صغير لتنفيذ التطور التفاضلي في Scipy.

def attack(img_id, model, target=None, pixel_count=1, 
           maxiter=75, popsize=400, verbose=False):
    # Change the target class based on whether this is a targeted attack or not
    targeted_attack = target is not None
    target_class = target if targeted_attack else y_test[img_id, 0]
    
    # Define bounds for a flat vector of x,y,r,g,b values
    # For more pixels, repeat this layout
    bounds = [(0,32), (0,32), (0,256), (0,256), (0,256)] * pixel_count
    
    # Population multiplier, in terms of the size of the perturbation vector x
    popmul = max(1, popsize // len(bounds))
    
    # Format the predict/callback functions for the differential evolution algorithm
    def predict_fn(xs):
        return predict_classes(xs, x_test[img_id], target_class, 
                               model, target is None)
    
    def callback_fn(x, convergence):
        return attack_success(x, x_test[img_id], target_class, 
                              model, targeted_attack, verbose)
    
    # Call Scipy's Implementation of Differential Evolution
    attack_result = differential_evolution(
        predict_fn, bounds, maxiter=maxiter, popsize=popmul,
        recombination=1, atol=-1, callback=callback_fn, polish=False)

    # Calculate some useful statistics to return from this function
    attack_image = perturb_image(attack_result.x, x_test[img_id])[0]
    prior_probs = model.predict_one(x_test[img_id])
    predicted_probs = model.predict_one(attack_image)
    predicted_class = np.argmax(predicted_probs)
    actual_class = y_test[img_id, 0]
    success = predicted_class != actual_class
    cdiff = prior_probs[actual_class] - predicted_probs[actual_class]

    # Show the best attempt at a solution (successful or not)
    helper.plot_image(attack_image, actual_class, class_names, predicted_class)

    return [model.name, pixel_count, img_id, actual_class, predicted_class, success, cdiff, prior_probs, predicted_probs, attack_result.x]

حان الوقت لمشاركة نتائج الدراسة (الهجوم) ومعرفة كيف سيؤدي تغيير بكسل واحد فقط إلى تحويل الضفدع إلى كلب ، والقط إلى ضفدع ، وسيارة إلى طائرة. ولكن كلما سمح بتغيير المزيد من نقاط الصورة ، زاد احتمال حدوث هجوم ناجح على أي صورة.



شرح هجوم ناجح على صورة ضفدع باستخدام نموذج resnet. يجب أن نرى الثقة في التراجع الحقيقي في الفصل بعد عدة تكرارات.

image_id = 102
pixels = 1 # Number of pixels to attack
model = resnet

_ = attack(image_id, model, pixel_count=pixels, verbose=True)

Confidence: 0.9938618
Confidence: 0.77454716
Confidence: 0.77454716
Confidence: 0.77454716
Confidence: 0.77454716
Confidence: 0.77454716
Confidence: 0.53226393
Confidence: 0.53226393
Confidence: 0.53226393
Confidence: 0.53226393
Confidence: 0.4211318



كانت هذه أمثلة على هجوم غير مستهدف ، والآن سنقوم بهجوم مستهدف ونختار إلى أي فئة نود أن يقوم النموذج بتصنيف الصورة. المهمة أكثر تعقيدًا بكثير من المهمة السابقة ، لأننا سنجعل الشبكة العصبية تصنف صورة السفينة كسيارة ، والحصان كقط.



أدناه سنحاول الحصول على lenet لتصنيف صورة السفينة كسيارة.

image_id = 108
target_class = 1 # Integer in range 0-9
pixels = 3
model = lenet

print('Attacking with target', class_names[target_class])
_ = attack(image_id, model, target_class, pixel_count=pixels, verbose=True)
Attacking with target automobile
Confidence: 0.044409167
Confidence: 0.044409167
Confidence: 0.044409167
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.081972085
Confidence: 0.081972085
Confidence: 0.081972085
Confidence: 0.081972085
Confidence: 0.1537778
Confidence: 0.1537778
Confidence: 0.1537778
Confidence: 0.22246778
Confidence: 0.23916133
Confidence: 0.25238588
Confidence: 0.25238588
Confidence: 0.25238588
Confidence: 0.44560355
Confidence: 0.44560355
Confidence: 0.44560355
Confidence: 0.5711696



بعد التعامل مع حالات فردية من الهجمات ، سنقوم بجمع الإحصاءات باستخدام بنية الشبكات العصبية التلافيفية ResNet ، من خلال كل نموذج ، وتغيير 1 أو 3 أو 5 بكسل لكل صورة. في هذه المقالة ، نعرض الاستنتاجات النهائية دون إزعاج القارئ للتعرف على كل تكرار ، حيث يستغرق الأمر الكثير من الوقت والموارد الحاسوبية.

def attack_all(models, samples=500, pixels=(1,3,5), targeted=False, 
               maxiter=75, popsize=400, verbose=False):
    results = []
    for model in models:
        model_results = []
        valid_imgs = correct_imgs[correct_imgs.name == model.name].img
        img_samples = np.random.choice(valid_imgs, samples, replace=False)
        
        for pixel_count in pixels:
            for i, img_id in enumerate(img_samples):
                print('\n', model.name, '- image', img_id, '-', i+1, '/', len(img_samples))
                targets = [None] if not targeted else range(10)
                
                for target in targets:
                    if targeted:
                        print('Attacking with target', class_names[target])
                        if target == y_test[img, 0]:
                            continue
                    result = attack(img_id, model, target, pixel_count, 
                                    maxiter=maxiter, popsize=popsize, 
                                    verbose=verbose)
                    model_results.append(result)
                    
        results += model_results
        helper.checkpoint(results, targeted)
    return results

untargeted = attack_all(models, samples=100, targeted=False)

targeted = attack_all(models, samples=10, targeted=False)

لاختبار إمكانية تشويه سمعة الشبكة ، تم تطوير خوارزمية وتم قياس تأثيرها على الجودة المتوقعة لحل التعرف على الأنماط.

دعنا نرى النتائج النهائية.

untargeted, targeted = helper.load_results()

columns = ['model', 'pixels', 'image', 'true', 'predicted', 'success', 'cdiff', 'prior_probs', 'predicted_probs', 'perturbation']

untargeted_results = pd.DataFrame(untargeted, columns=columns)
targeted_results = pd.DataFrame(targeted, columns=columns)

يوضح الجدول أدناه أنه باستخدام الشبكة العصبية ResNet بدقة 0.9231 ، وتغيير عدة بكسل من الصورة ، حصلنا على نسبة جيدة جدًا من الصور التي تمت مهاجمتها بنجاح (attack_success_rate).

helper.attack_stats(targeted_results, models, network_stats)
Out[26]:
	model	accuracy   pixels	attack_success_rate
0	resnet	0.9231	    1	        0.144444
1	resnet	0.9231	    3	        0.211111
2	resnet	0.9231	    5	        0.222222

helper.attack_stats(untargeted_results, models, network_stats)
Out[27]:
	model	accuracy   pixels	attack_success_rate
0	resnet	0.9231	   1	        0.34
1	resnet	0.9231	   3	        0.79
2	resnet	0.9231	   5	        0.79

في تجاربك ، أنت حر في استخدام بنى أخرى للشبكات العصبية الاصطناعية ، حيث يوجد حاليًا العديد منها.



لقد غطت الشبكات العصبية العالم الحديث بخيوط غير مرئية. لفترة طويلة ، تم اختراع الخدمات حيث ، باستخدام الذكاء الاصطناعي (AI) ، يتلقى المستخدمون صورًا تمت معالجتها تشبه أسلوبًا فنيًا لعمل الفنانين العظماء ، واليوم يمكن للخوارزميات نفسها رسم الصور وإنشاء روائع موسيقية وكتابة الكتب وحتى النصوص للأفلام.

مجالات مثل رؤية الكمبيوتر ، والتعرف على الوجه ، والمركبات غير المأهولة ، وتشخيص الأمراض - اتخاذ قرارات مهمة ولا يحق لها ارتكاب الأخطاء ، والتدخل في تشغيل الخوارزميات سيؤدي إلى عواقب وخيمة.

هجوم بكسل واحد هو طريقة واحدة لتزييف الهجمات. لاختبار إمكانية تشويه سمعة الشبكة ، تم تطوير خوارزمية وتم قياس تأثيرها على الجودة المتوقعة لحل التعرف على الأنماط. أظهرت النتائج أن بنيات الشبكات العصبية التلافيفية المستخدمة معرضة لخوارزمية هجوم بكسل واحد مدربة خصيصًا ، والتي تحل محل بكسل واحد ، لتشويه سمعة خوارزمية التعرف.

وقد أعدته المادة الكسندر Andronic و Adrey شيرني-تكاتش كجزء من التدريب في Data4 .

All Articles