خدعة الشبكة العصبية للمبتدئين

الصورة


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


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


| Home --| KerasModel.h5 --| Task.txt --| ZeroSource.bmp 

المعلومات التالية كانت في ملف Task.txt:


 Now it is time for a final boss! http://51.15.100.188:36491/predict You have a mode and an image. To get a ticket, you need to change an image so that it is identified as "1". curl -X POST -F image=@ZeroSource.bmp 'http://51.15.100.188:36491/predict'. (don't forget about normalization (/255) ^_^) 

للحصول على التذكرة المطلوبة ، طلب من المهاجم تحويل ZeroSource.bmp:


الصورة


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


وبالطبع ، فإن التلميح الرئيسي لهذه المهمة هو ملف الطراز KerasModel.h5 (هذا الملف يساعد المهاجم في نقل الهجوم إلى الطائرة WhiteBox ، نظرًا لأن الشبكة العصبية وجميع البيانات المرتبطة به يمكن الوصول إليه). يحتوي اسم الملف فورًا على تلميح - اسم الإطار الذي تم تطبيق الشبكة العصبية عليه.


كان مع هذه الملاحظات التمهيدية أن مجموعة المشاركين حول حل المهمة:


  • نموذج الشبكة العصبية مكتوبة في Keras.
  • القدرة على إرسال صورة إلى الخادم باستخدام حليقة.
  • الصورة الأصلية التي تحتاج إلى تغيير.

على جانب الخادم ، كان الفحص بسيطًا قدر الإمكان:


  1. يجب أن تكون الصورة بالحجم الصحيح - 28 × 28 بكسل.
  2. في هذه الصورة ، يجب أن يعود النموذج 1.
  3. يجب أن يكون الفرق بين الصورة الأولية لـ ZeroSource.bmp والصورة المرسلة إلى الخادم أقل من k بواسطة مقياس MSE (خطأ قياسي).

لذلك دعونا نبدأ.


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


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


 import keras import numpy as np from PIL import Image import foolbox 

ماذا نرى هنا؟


  1. Keras هو الإطار الذي تتم فيه كتابة الشبكة العصبية ، والتي سنخدعها.
  2. NumPy هي مكتبة ستسمح لنا بالعمل مع المتجهات بكفاءة.
  3. PIL هي أداة للعمل مع الصور.
  4. FoolBox هي مكتبة لتوليد أمثلة عدائية.

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


 model = keras.models.load_model("KerasModel.h5") #   model.summary() #     model.input #    ,        

في الإخراج ، نحصل على ما يلي:


 Layer (type) Output Shape Param # ================================================================= conv2d_1 (Conv2D) (None, 26, 26, 32) 320 _________________________________________________________________ conv2d_2 (Conv2D) (None, 26, 26, 64) 18496 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 13, 13, 64) 0 _________________________________________________________________ dropout_1 (Dropout) (None, 13, 13, 64) 0 _________________________________________________________________ conv2d_3 (Conv2D) (None, 13, 13, 64) 36928 _________________________________________________________________ conv2d_4 (Conv2D) (None, 13, 13, 128) 73856 _________________________________________________________________ max_pooling2d_2 (MaxPooling2 (None, 6, 6, 128) 0 _________________________________________________________________ flatten_1 (Flatten) (None, 4608) 0 _________________________________________________________________ dense_1 (Dense) (None, 256) 1179904 _________________________________________________________________ dense_2 (Dense) (None, 10) 2570 ================================================================= Total params: 1,312,074 Trainable params: 1,312,074 Non-trainable params: 0 _________________________________________________________________ <tf.Tensor 'conv2d_1_input_1:0' shape=(?, 28, 28, 1) dtype=float32> 

ما المعلومات التي يمكنني الحصول عليها من هنا؟


  1. يقبل طراز الإدخال (طبقة conv2d_1) كائن البعد؟ X28x28x1 ، حيث "؟" - عدد الأشياء ؛ إذا كانت الصورة واحدة ، فسيكون البعد 1 × 28 × 28 × 1. والصورة عبارة عن صفيف ثلاثي الأبعاد ، حيث يكون البعد واحدًا 1. أي ، يتم تقديم الصورة كجدول قيم من 0 إلى 255.
  2. عند إخراج النموذج (طبقة dense_2) ، يتم الحصول على متجه البعد 10.

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


 img = Image.open("ZeroSource.bmp") #   img = np.array(img) #     numpy.array img = img.astype('float32') #      float img /= 255 #  

يمكننا الآن إرسال الصورة إلى النموذج الذي تم تحميله ومعرفة النتيجة التي ينتجها:


 model.predict(img.reshape(1,28,28,1)) #   predict            

في الإخراج نحصل على المعلومات التالية


 array([[1.0000000e+00, 4.2309660e-19, 3.1170484e-15, 6.2545637e-18, 1.4199094e-16, 6.3990816e-13, 6.9493417e-10, 2.8936278e-12, 8.9440377e-14, 1.6340098e-12]], dtype=float32) 

يجدر شرح ماهية هذا المتجه: في الواقع ، هذا هو توزيع الاحتمالات ، أي أن كل رقم يمثل احتمالًا للفئة 0،1،2 ... ، 9. مجموع جميع الأرقام في المتجه هو 1. في هذه الحالة ، يمكن ملاحظة أن النموذج واثق من أن صورة الإدخال تمثل الفئة 0 مع احتمال 100 ٪.


إذا قمنا بتصوير هذا على الرسم البياني ، فسنحصل على ما يلي:


الصورة


الثقة المطلقة.


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


الصورة


من المقبول عمومًا تحديد فئة النموذج بواسطة فهرس الحد الأقصى لعدد المتجهات. وهذا يعني أن النموذج يمكنه نظريًا اختيار فئة ذات احتمال يزيد عن 10٪. ولكن قد يختلف هذا المنطق اعتمادًا على منطق القرار الموصوف من قبل المطورين.


الآن دعنا ننتقل إلى الجزء الأكثر إثارة للاهتمام - الهجمات العدائية.


أولاً ، للعمل مع نموذج في مكتبة FoolBox ، يجب عليك ترجمة النموذج إلى تدوين Foolbox. يمكنك القيام بذلك بهذه الطريقة:


 fmodel = foolbox.models.KerasModel(model,bounds=(0,1)) #  bounds ,       ,        255,        0-1. 

بعد ذلك ، يمكنك اختبار هجمات مختلفة. لنبدأ بالأكثر شعبية - ختان الإناث:


Fgsm

 attack = foolbox.attacks.FGSM(fmodel) #    FGSM     adversarial = attack(img.reshape(28,28,1),0) #  ,  adversarial  probs = model.predict(adversarial.reshape(1,28,28,1)) #     print(probs) #    print(np.argmax(probs)) #      

سيكون إخراج الشبكة العصبية على النحو التالي


 [4.8592144e-01 2.5432981e-14 5.7048566e-13 1.6787202e-14 1.6875961e-11 1.2974949e-07 5.1407838e-01 3.9819957e-12 1.9827724e-09 5.7383300e-12] 6 

والصورة الناتجة:


الصورة


لذلك ، الآن مع احتمال أكثر من 50 ٪ 0 تم الاعتراف بأنه 6. جيد بالفعل. ومع ذلك ، ما زلنا نريد الحصول على 1 ، ومستوى الضوضاء ليست مؤثرة للغاية. تبدو الصورة غير معقولة حقًا. المزيد عن هذا في وقت لاحق. في هذه الأثناء ، دعونا نحاول فقط الاستهزاء بالهجمات. فجأة ، ما زلنا نحصل على 1.


هجوم L-BFGS

 attack = foolbox.attacks.LBFGSAttack(fmodel) adversarial = attack(img.reshape(28,28,1),0) probs = model.predict(adversarial.reshape(1,28,28,1)) print(probs) print(np.argmax(probs)) 

الخلاصة:


 [4.7782943e-01, 1.9682934e-10, 1.0285517e-06, 3.2558936e-10, 6.5797998e-05, 4.0495447e-06, 2.5545436e-04, 3.4730587e-02, 5.5223148e-07, 4.8711312e-01] 9 

الصورة:


الصورة


مرة أخرى بواسطة. الآن لدينا 0 معترف بها 9 مع احتمال ~ 49 ٪. ومع ذلك ، فإن الضوضاء أقل بكثير.


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


في هذه الوحدة ، يمكنك تحديد معيار للهجوم ، إذا كان يدعمهم. على وجه التحديد ، نحن مهتمون بمعايير اثنين:


  1. TargetClass - يجعل ذلك في متجه توزيعات الاحتمال ، للعنصر الذي يحتوي على رقم k أقصى الاحتمالات.
  2. TargetClassProbability - يجعله بحيث يكون في عنصر متجه توزيعات الاحتمال وجود عنصر برقم k له احتمال p على الأقل.

لنجرب كليهما:


L-BFGS + TargetClass

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


 attack = foolbox.attacks.LBFGSAttack(fmodel,foolbox.criteria.TargetClass(1))#    ,     ,    TargetClass,  ,      ,      adversarial = attack(img.reshape(28,28,1),0) probs = model.predict(adversarial.reshape(1,28,28,1)) print(probs) print(np.argmax(probs)) 

الخلاصة:


 [3.2620126e-01 3.2813528e-01 8.5446298e-02 8.1292394e-04 1.1273423e-03 2.4886258e-02 3.3904776e-02 1.9947644e-01 8.2347924e-07 8.5878673e-06] 1 

الصورة:


الصورة


كما يتضح من الاستنتاج ، تدعي شبكتنا العصبية الآن أنها 1 مع احتمال بنسبة 32.8 ٪ ، في حين أن احتمال 0 قريب من هذه القيمة قدر الإمكان وهو 32.6 ٪. لقد فعلنا ذلك! من حيث المبدأ ، هذا بالفعل يكفي لإكمال المهمة. لكن سنذهب أبعد من ذلك ونحاول الحصول على احتمال 1 فوق 0.5.


L-BFGS + TargetClassProbability

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


 attack = foolbox.attacks.LBFGSAttack(fmodel,foolbox.criteria.TargetClassProbability(1,0.5)) adversarial = attack(img.reshape(28,28,1),0) probs = model.predict(adversarial.reshape(1,28,28,1)) print(probs) print(np.argmax(probs)) 

الخلاصة:


 [4.2620126e-01 5.0013528e-01 9.5413298e-02 8.1292394e-04 1.1273423e-03 2.4886258e-02 3.3904776e-02 1.9947644e-01 8.2347924e-07 8.5878673e-06] 

الصيحة! لقد نجحنا في الحصول على مثال خصم ، حيث يكون الاحتمال 1 لشبكتنا العصبية أعلى من 50 ٪! عظيم! الآن لنقم بإلغاء الطابع الطبيعي (أعد الصورة إلى تنسيق 0-255) واحفظها.


البرنامج النصي النهائي هو كما يلي:


 import keras from PIL import Image import numpy as np import foolbox from foolbox.criteria import TargetClassProbability import scipy.misc model = keras.models.load_model("KerasModel.h5") img = Image.open("ZeroSource.bmp") img = np.array(img.getdata()) img = img.astype('float32') img = img /255. img = img.reshape(28,28,1) fmodel = foolbox.models.KerasModel(model,bounds=(0,1)) attack = foolbox.attacks.LBFGSAttack(fmodel,criterion=TargetClassProbability(1 ,p=.5)) adversarial = attack(img[:,:,::-1], 0) adversarial = adversarial * 255 adversarial = adversarial.astype('int') scipy.misc.toimage(adversarial.reshape(28,28)).save('AdversarialExampleZero.bmp') 

والصورة النهائية هي كما يلي:


الصورة .


النتائج

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


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


شكرا لاهتمامكم!

Source: https://habr.com/ru/post/ar443164/


All Articles