القائمة الرئيسية

الصفحات

 




تقنية الحلم العميق (DeepDream)


تقنية الحلم العميق

(DeepDream)


المهندس حسن فنجان عداي

 

مقدمة:

بسم إلله الرحمن الرحيم

نتطرق في هذه المقالة إلى تقنية مشهورة تسمى تقنية الحلم العميق (Deep Dream),  وهي تقنية لعمل تعديلات على الصور لجعلها غير واقعية شبيهة بتلك التي تشاهد في الأحلام, حيث تتم الإستعانة بالمرشحات (filters) المدربة والتي تعود إلى أحد النماذج المدربة (model) . ويمكن تشبيه هذه التقنية بمن ينظر إلى السحب في الجو ويحاول أن يتخيل منها أشكال لأشخاص أو كائنات أو أشياء أخرى معتاد عليها.


الحلم العميق

(DeepDream) :

تم إصدار هذه التقنية لأول مرة بواسطة شركة (Google) في صيف عام (2015) ، كتطبيق مكتوب باستخدام مكتبة التعلم العميق  (Caffe), حيث كان هذا قبل عدة أشهر من الإصدار الأول  للمكتبة المشهورة في التعلم العميق (Tensorflow), وقد أخذت هذه التقنية شهرةً كبيرة وخاصة عن طريق الإنترنت لما تحققه من تعديلات مدهشة على الصور حيث تم إعتبارها كأحد أنواع الفنون الجميلة.

تتشابه خوارزمية (DeepDream) تقريبًا مع تقنية ال (Guided backpropagation) التي سبق وأن قمنا بشرحها وتوضيحها في الجزء الرابع من المقالة الخاصة في موضوع إستعادة الصور (Image Retrieval) والتي تتكون من تشغيل شبكة افتراضية في الاتجاه المعاكس, حيث يتم حساب دالة الفقدان (Loss Function)  لمصفوفات الخصائص المناظرة للمرشحات المقصودة, بعدها يتم حساب الميل التصاعدي (Gradient Ascent) لدالة الفقدان بالنسبة للصورة المدخلة إلى النموذج المدرب (Input Image).

تستخدم قيمة الميل هذه في عملية التحديث لكل بكسل من بكسلات الصورة المدخلة, حيث يتم تكرار هذه العملية لعدة مرات إلى أن تصل قيمة دالة الفقدان إلى الحد المناسب أي تصل الزيادة في قيم مخارج الوحدات (units) المكونة لمصفوفة الخصائص المقصودة (feature map)  إلى أعلى قيمــــــــة ممكنـة.

يستخدم (DeepDream) هذه الفكرة نفسها ، مع بعض الاختلافات البسيطة:

1 - مع (DeepDream) نحاول  زيادة تنشيط الطبقات بأكملها بدلاً من تنشيط مرشح معـين ، حيث تكون دالة الفقدان (loss function) هي عبارة عن مجموع لنسب معينة من دوال الفقدان لكل مصفوفة خصائص (feature map) للطبقات المنتخبة من النموذج.

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

3 - يتم إنشاء أحجام مختلفة لنفس الصورة تسمى (Octaves) ، مما يحسن من جودة التصور, حيث يتم تطبيق نفس الخوارزمية على الجميع, ثم يصار إلى جمع الصور بعد التعديل.

ولتوضيح هذا الموضوع بشكل افضل سوف نقوم بإنشاء تطبيق يقوم بإستيراد النموذج المشهور (InceptionV3) وإجتزاء بعض الطبقات منه لأجل إنشاء نموذج مصغر ويكون مدرب على مجموعة البيانات (ImageNet). 

ولنبدأ الآن بكتابة الكود لهذا التطبيق: 

شرح الكود:

الأسطر من (1) إلى (4): لجلب المكتبات التي نحتاجها في هذا التطبيق.


الأسطر من (2) إلى (8): لإنشاء دالتين سوف نستخدهما لاحقاً, الأولى لغرض إعادة معالجة الصور قبل عرضها من خلال إجراء بعض التحسينات على قيم البكسلات للصورة وكذلك تغيير نوع البيانات إلى (uint8).

أما الدالة الثانية فتستخدم لإظهار الصور.

السطر رقم (2): لتحميل بيانات الصورة من الملف (my_image.jpg) وإيداعها في المتغير (original_img). علماً بأن هذا الملف قد وضعناه بالمجلد الخاص بالتطبيق.

السطر رقم (3): لتغيير حجم الصورة إلى صورة صغيرة بحجم (500 x 500).

السطر رقم (4): لتحويل بيانات الصورة إلى مصفوفة (array) من نوع (Numpy) لكي يسهل التعامل معها لاحقاً.

السطر رقم (5): لإستدعاء الدالة (show) لأجل عرض الصورة الأصلية لمقارنتها لاحقاً بالأخرى المتولدة بعد تطبيق تقنية ال (Deep dream).  وبعد تنفيذ هذا الكود تظهر لنا الصورة التالية:





السطر رقم (1): لإنشاء النموذج المدرب نوع (InceptionV3) الخالي من طبقة الإخراج (output layer), وهذا النموذج مدرب على مجموعة البيانات المشهورة (ImageNet), وقد إخترنا هذا النموذج لأنه مشابه للنموذج الذي تم إستخدامه في تنفيذ هذه التقنية لأول مرة. إنَّ تنفيذ هذا الكود سوف يأخذ بعض الوقت لأجل القيام بتحميل الأوزان لهذا النموذج من المصدر.

السطر رقم (6): لإختيار مجموعتان من طبقات هذا النموذج هما (mixed3 and mixed5). علماً بأنَّ الطبقات الأولى تقوم بإستخلاص الأشكال الهندسية مثل الدوائر والخطوط الأفقية والعمودية وكذلك الحواف, أما الطبقات الأكثر عمقاً فتقوم بإستخلاص أشكال الكائنات وكذلك الوجوه وغير ذلك من الأشكال الكاملة.

السطر رقم (7): لإنشاء قائمة (list) بمخارج (outputs)  الطبقات المنتخبة.

السطر رقم (8): لإنشاء نموذج مصغر ذات مخرجين (2 outputs) لأننا إخترنا طبقتين لتطبيق الخوارزمية. علماً بأنَّ هذا النموذج يمثل جزء منتخب من النموذج الأصلي, بمعنى أنه مدرب على مجموعة الصور (ImageNet).


الأسطر من (1) إلى (8): لإنشاء دالة تقوم بحساب قيمة مايسمى الفقد (loss) الخاص بجميع مصفوفات الخصائص (feature maps) وللطبقتين معاً والذي سوف نحتاجه لاحقاً في عملية إجراء التحديثات على بيانات الصورة المدخلة إلى النموذج.

السطر رقم (2): لإضافة بعد رابع للصورة المدخلة ويكون موقعه في (axis=0) أي الأول من اليسار.

السطر رقم (3): لإستخراج جميع قيم مخارج النيورونات لل (feature maps) وللطبقتين المنتخبتين معاً.

السطر رقم (5): لإنشاء حلقة تكرارية تتكرر بعدد الطبقات المنتخبة وفي هذا التطبيق هما طبقتان فقط.

السطر رقم (6): حساب قيمة الفقد لكل طبقة, وتستخدم الدالة (reduce_mean) والتي تقوم بحساب المتوسط لقيم جميع النيورونات لأحد الطبقات في كل مرة.

السطر رقم (7): لوضع قيمة الفقد (loss) للطبقتين في مصفوفة (list).

السطر رقم (8): للقيام بعملية جمع لقيمتي الفقد الخاصتين بالطبقتين وجعلهما قيمةً واحدة والتي تمثل القيمة الراجعة للدالة.

شرح الكود:

الأسطر من (1) إلى (13): إنشاء دالة تقوم بتنفيذ جزء من الخوارزمية الخاصة بال (Deep dream).

السطر رقم (1): تستخدم هذه الجملة من الكود لتسريع الأداء وذلك بتحويل العمليات (operations) إلى رسوم (graphs) وذلك لتخطي التقنية المسماة بالتنفيذ الحريص (Eager execution) التي تتميز بها مكتبة (Tensorflow) الإصدار الثاني. يفضل في هذا النوع من الدوال إستخدام المصفوفات من نوع (tensorflow) وليس من نوع (numpy).

السطر رقم (2): الإعلان عن الدالة (my_deep_dream) والتي تحتاج إلى أربعة معاملات هي:

أ – بيانات الصورة بعد إجراء المعالجات الضرورية عليها لكي تكون مناسبة كمعامل للدالة (calc_loss) الخاصة بحساب قيمة الفقد (loss) والتي مرت علينا.

ب – النموذج المدرب الذي قمنا بإنشائه مسبقاً وهو (dreem_model).

ج – عدد الخطوات التي يتم فيها إجراء عملية التحديث على بيانات الصورة.

د – النسبة من قيمة المشتقة (gradient) التي تضاف على بيانات الصورة لأجل التحديث, وتسمى هذه النسبة حجم الخطوة (step_size).

السطر رقم (4): للإعلان عن ثابت نوع (tensor) ليكون متوافقاً مع هذا النوع من الدوال كما ذكرنا.

السطر رقم (5): لعمل حلقة تكرارية بعدد الخطوات (steps) التي سوف نمررها للدالة عند إستدعائها لاحقاً.

السطر رقم (6): إستدعاء الدالة (GradientTape) التي تقوم بمراقبة وتسجيل قيم المتغيرات الواقعة تحتها, وفي حالتنا تقوم بتسجيل مايحدث لقيم ال(loss)  وكذلك أضفنا لها (img) عن طريق الدالة (watch).

السطر رقم (8): حساب قيمة الفقد (loss) عن طريق إستدعاء الدالة (calc_loss) حيث نمرر لها بيانات الصورة المراد إجراء التحديث عليها وكذلك نمرر لها النموذج الذي أنشأناه.

السطر رقم (9): لحساب قيمة المشتقة (gradient) لقيمة الفقد (loss) بالنسبة لكل بكسل من بكسلات الصورة.

السطر رقم (10): إجراء عملية تطبيع (Normalization)على قيمة المشتقة (gradients) قبل إستخدامها في عملية تحديث بيانات الصورة.

السطر رقم (11): إجراء عملية التحديث على بيانات الصورة.

السطر رقم (12): إجراء معالجة لبيانات الصورة لتكون صالحة للعرض.


شرح الكود:

الأسطر من (1) إلى (21): لإنشاء دالة تقوم بتنفيذ ماتبقى من مراحل الخوارزمية.

السطر رقم (2): للقيام بتهيئة بيانات الصورة وجعلها متوافقة مع مايحتاجه النموذج من الأبعاد المناسبة للصورة (shapes) وكذلك نوع البيانات (data type).

السطر رقم (3): لتحويل بيانات الصورة إلى مصفوفة من نوع (tensor) لكي تكون متاوفقة مع الدالة (my_deep_dream) وذلك عند إستدعاءها وتمرير هذه البيانات إليها.

السطر رقم (4): لتحويل قيمة الثابت (step_size) إلى قيمة من نوع (tensor) لنفس السبب السابق.

السطر رقم (5): للإعلان عن عداد لحساب خطوات التحديث للصورة لكي يتم عرضه مع الصور لاحقاً.

السطر رقم (6): لإنشاء حلقة تكرارية تتكرر بعدد الخطوات الكلية التي نمررها عند إستدعاء هذه الدالة, حيث تنتهي عندما تصبح قيمة (steps = 0).

السطر رقم (7): لإنشاء جملة شرطية لإختبار قيمة الخطوات (steps) بعد كل عملية تكرار فإذا كانت لاتزال قيمتها أكبر من (100) فتعطى قيمة للمتغير (run_steps) بمقدار (100) لكي يتم طرحه لاحقاً من الخطوات (steps) المتبقية.

الأسطر من (13) إلى (18): لإستدعاء الدالة (my_deep_dream) وتمرير المعاملات (parameters) التي تحتاجها, وهي أربعة معاملات, من أجل الحصول على قيمة (loss) الفقد وكذلك بيانات الصورة بعد التحديث (img) وذلك لأجل العرض (Display), حيث نلاحظ أننا نعطي إلى الدالة عدد الخطوات دائماً (100) خطوة.

السطر رقم (19): لأجل مسح الصورة السابقة للإستعداد لعرض الصورة اللاحقة.

السطر رقم (20): لعرض إحدى الصور بعد كل (100) خطوة من التحديث من خلال الدالة (show), وكذلك معالجتها قبل العرض من خلال الدالة (deprocess) لإرجاعها إلى الحالة الطبيعية.

السطر رقم (25): لعرض الصورة النهائية بعد إكتمال جميع الخطوات (steps).


طريقة لتحسين أداء الخوارزمية:

عند تنفيذ الكود من خلال إستدعاء الدالة (run_deep_simple) نلاحظ أنَّ الأشكال والأنماط التي أضيفت إلى الصورة الأصلية تظهر كما لو أنها تحدث جميعها بنفس التفاصيل ولو قمنا بزيادة عدد الخطوات (steps) يحدث ضوضاء (Noise) في الصورة وللتخلص من هذا الضوضاء تنخفض دقة الصورة.

لهذه الأسباب لجأ الباحثون إلى عملية تسمى (Octave) لتحسين أداء هذه الخوارزمية, وتتلخص هذه العملية بأن نقوم بإنشاء نماذج ذات أحجام (sizes) متعددة تبدأ من أحجام أصغر من الصورة الأصلية تمثل أجزاء من حجم الصورة الأصلية وكذلك أحجام أكبر من حجم الصورة الأصلية تمثل مضاعفات لحجم الصورة الأصلية.

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


شرح الكود:

السطر رقم (1): لتعيين القيمة التي نستخدها في عملية إنشاء الأحجام المختلفة للصورة المدخلة إلى الخوارزمية.

السطر رقم (2): لتحويل بيانات الصورة إلى مصفوفة من نوع (numpy) ومن ثم تحويلها إلى (tensor) لكي نستطيع التحكم في حجمها  لاحقاً.

السطر رقم (3): لإنتزاع حجم الصورة الأصلي (الطول و العرض).

السطر رقم (4): لتحويل قياسات الحجم المنتزع في الخطوة السابقة إلى بيانات نوع (float32) بعد أن كان من نوع (integer) وذلك لأننا سوف نقوم بحساب أحجام للصور تكون بعض الأحيان فيها كسور.

السطر رقم (5): لإنشاء حلقة تكرارية بقدر عدد الصور المراد إنشاءها.

السطر رقم (6): حساب حجم جديد للصورة المراد إنشاءها وتحويله الى بيانات نوع (int32).

السطر رقم (7): لإنشاء صورة ذات حجم جديد تبعاً للحجم الذي حصلنا عليه من الخطوة السابقة.

السطر رقم (8): لإستدعاء الدالة (run_deep_dream_simple) في كل تكرار وتمرير الصورة التي نحصل عليها في هذا التكرار وكذلك بقية المعاملات (parameters), وذلك لغرض الحصول على الصورة التي تم إجراء التحديثات عليها لكي يتم إستخدامها في التكرار اللاحق للحصول على الصورة ذات حجم جديد آخر وهكذا يتم إستدعاء هذه الدالة على عدد التكرار في الحلقة.

السطر رقم (9): لمسح الصورة السابقة لتحل محلها الصورة اللاحقة.

السطر رقم (10): إنَّ الصورة التي نحصل عليها من التكرار الأخير حجمها يعدل أضعاف الصورة الأصلية لذا تستوجب الحالة أن نعيد هذه الصورة إلى نفس حجم الصورة الأصلية.

السطر رقم (11): لتحويل نوع بيانات الصورة إلى (uint8).

السطر رقم (12): لعرض الصورة التي أجريت عليها كافة التحديثات.

وبعد تنفيذ الكود الخاص بهذه الخوارزمية نحصل على الصورة التالية:




وبهذا نكون قد إنتهينا من هذه المقالة التي قمنا فيها بشرح وتوضيح أحد المواضيع المشهورة والذي يعتبر أحد تطبيقات التعلم العميق (Deep Learning), ألا وهو الموضوع المسمى الحلم العميق (Deep Dream), حيث قمنا بشرح الأساس النظري الذي يبتني عليه هذا الموضوع وكذلك قمنا بإنشاء وتنفيذ أحد التطبيقات لزيادة الفائدة والتوضيح.

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

 

المهندس حسن فنجان عداي.







هل اعجبك الموضوع :

تعليقات

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

    https://pypi.org/project/deep4dream/

    ردحذف

إرسال تعليق