البحث في الموقع
المحتوى عن 'شبكة عصبية'.
-
تُستخدم الشبكات العصبية كوسيلةٍ للتعلم العميق، فهي واحدةٌ من المجالات الفرعية العديدة لطُرق تطبيق الذكاء الصناعي. وقد اقْتُرحت لأول مرةٍ منذ حوالي 70 عامًا في محاولةٍ لمحاكاة طريقة عمل دماغ الإنسان، إلا أنها أبسط بكثيرٍ من الخلايا العصبية الحقيقة، إذ أن كلّ خليةٍ اصطناعيةٍ مرتبطةٌ بعِدة طبقاتٍ، ولكلّ واحدةٍ منها وزنٌ مُعينٌ يُعبر عن أهمية هذه الطبقة، وذلك لتحديد كيفية استجابة الخلية العصبية عند نشْر البيانات عبرها، وبينما كانت الشبكات العصبية سابقًا محدودةً في عدد الخلايا العصبية التي تستطيع محاكاتها في ذلك الوقت -وهو الأمر الذي انعكس بدوره على تعقيد عملية التعلم التي يمكننا تحقيقها-، إلا أنه في السنوات الأخيرة، ونظرًا للتقدم الكبير في تطوّر الأجهزة، استطعنا أخيرًا بناء شبكاتٍ عصبيةٍ عميقةٍ جدًا، وتدريبها على مجموعات بياناتٍ هائلةٍ وضخمة كذلك، مما أدّى إلى تحقيق قفزاتٍ نوعية في تطور الذكاء الاصطناعي وتحديدًا تعلم الآلة. سمحت تلك القفزات النوعية للآلات بمُقاربة قدرات البشر، بل وتجاوزتها في أداء بعض المهام المحدودة. ومن بين هذه المهام، قدرتها على التعرف على الكائنات، فعلى الرغم من أن الآلات كانت غير قادرة تاريخيًا على منافسة قوة الرؤية البشرية، إلا أنّ التطورات الحديثة في التعلم العميق جعلت من الممكن بناء شبكاتٍ عصبيةٍ باستطاعتها التعرف على الكائنات والوجوه والنصوص، بل وحتى العواطف! سنُطبق في هذا المقال قِسمًا فرعيًا صغيرًا من طُرق التعرف على الكائنات، وتحديدًا التعرف على الأرقام المكتوبة بخط اليد، وذلك باستخدام مكتبة TensorFlow، وهي مكتبة بايثون مفتوحة المصدر التي طُوّرت في مختبرات غوغل Google Brain لأبحاث التعلم العميق، كما أنها من أشهر المكتبات الحالية في التعلم العميق، وسنأخذ صورًا مكتوبٌ عليها الأرقام بخط اليد من الرقم 0 وحتى الرقم 9، وسنبني شبكةً عصبيةً وندربها لكي تتعرف على التصنيف المناسب لكل رقمٍ معروضٍ في الصورة وتتنبأ به، ثم تَنسْبه لصنفٍ من أصناف الأرقام الموجودة. سنفترض إلمامك بمصطلحات ومفاهيم تعلم الآلة، مثل التدريب والاختبار والميزات والأصناف والتحسين والتقييم. لهذا لن تحتاج لخبرةٍ سابقةٍ في مجال التعلم العميق التطبيقي أو بمكتبة TensorFlow، ولمتابعة وفَهم هذا المقال جيدًا ننصحك أولًا بالاطلاع على: المفاهيم الأساسية لتعلم الآلة. المتطلبات الرئيسية لإكمال هذا المقال ستحتاج بيئةً برمجيةً للغة بايثون الإصدار 3.8 سواءً كان محليًا أو بعيدًا. ويجب أن تتضمن هذه البيئة البرمجية مدير الحِزم pip لتثبيت الحِزم، ومُنشئ البيئات الافتراضية venv لإنشاء بيئاتٍ افتراضيةٍ. 1. إعداد المشروع ستحتاج أولًا لتثبيت بعض التبعيات، وذلك لإنشاء مساحة عملٍ للاحتفاظ بملفاتنا قبل أن نتمكن من تطوير برنامج التعرف على الصور، وسنستخدم بيئة بايثون 3.8 الافتراضية لإدارة التبعيات الخاصة بمشروعنا. سَنُنشئ مجلدًا جديدًا خاصًا بمشروعنا وسندخل إليه هكذا: mkdir tensorflow-demo cd tensorflow-demo سننفذّ الأوامر التالية لإنشاء البيئة الافتراضية: python -m venv tensorflow-demo ومن ثم الامر التالي في Linux لتنشيط البيئة الافتراضية: source tensorflow-demo/bin/activate أما في Windows، فيكون أمر التنشيط: "tensorflow-demo/Scripts/activate.bat" بعد ذلك، سنُثبتّ المكتبات التي سنستخدمها. سنستخدم إصداراتٍ محددةٍ من هذه المكتبات، من خلال إنشاء ملف requirements.txt في مجلد المشروع، وسيُحدِّد هذا الملف المتطلبات والإصدارات التي سنحتاج إليها. نفتح الملف requirements.txt في محرر النصوص، ونُضيف الأسطر التالية، وذلك لتحديد المكتبات التي نريدها وإصداراتها: keras==2.6.0 numpy==1.19.5 Pillow==8.4.0 scikit-learn==1.0 scipy==1.7.1 sklearn==0.0 tensorflow==2.6.0 سنحفظ التغييرات التي طرأت على الملف وسنخرج من محرر النصوص، ثم سنُثَبت هذه المكتبات بالأمر التالي: (tensorflow-demo) $ pip install -r requirements.txt بعد تثبيتنا لهذه التبعيات، سنُصبح جاهزين لبدء العمل على مشروعنا. 2. استيراد مجموعة بيانات MNIST تُسمى مجموعة البيانات التي سنستخدمها، بمجموعة بيانات MNIST، وهي مجموعةٌ كلاسيكيةٌ في مجتمع مُطوري تعلم الآلة، وتتكون من صورٍ لأرقامٍ مكتوبةٍ بخط اليد، بحجم 28×28 بكسل. ونستعرض فيما يلي بعض الأمثلة للأرقام المُتضمنة فيها: لاحظ أنه ينبغي أن نستخدم ملفًا واحدًا لجميع أعمالنا في هذا المقال، ولنُنشئ برنامج بايثون يتعامل مع مجموعة البيانات هذه، سننشئ ملفًا جديدًا باسم main.py، وسنفتح هذا الملف بأي محرر شيفرات لدينا -مثل VS code- وسنُضيف هذه الأسطر البرمجية لاستيراد المكتبات اللازمة: import tensorflow as tf import numpy as np from sklearn.preprocessing import OneHotEncoder #مكتبة معالجة الصور from PIL import Image #التوافقية مع إصدار سابق tf.compat.v1.disable_v2_behavior() وسنضيف أيضًا هذه الأسطر من الشيفرات البرمجية لملفك لاستيراد مجموعة بيانات MNIST وذلك باختيار صور التدريب المتاحة من Tensorflow ومن ثم نُنزلها ونقسمها إلى جزئين: الأول للتدريب والثاني للاختبار: #اختيار بيانات التدريب mnist = tf.keras.datasets.mnist #تنزيل بيانات التدريب والاختبار (x_train, y_train), (x_test, y_test) = mnist.load_data() #طباعة عدد بيانات التدريب والاختبار print (len(x_train)) print (len(x_test)) #تحجيم البيانات بين 0 و 1 x_train, x_test = x_train / 255.0, x_test / 255.0 #الترميز الأحادي النشط y_train = [[i] for i in y_train] y_test = [[i] for i in y_test] enc = OneHotEncoder(sparse=True) enc.fit(y_train) y_train = enc.transform(y_train) y_test = enc.transform(y_test) تقوم الدالة 'mnist.load_data' بتنزيل البيانات وتقسيمها إلى مجموعتين واحدة للتدريب (60000 صورة) والمجموعة الثانية للاختبار (10000 صورة). وعند قراءة البيانات سنستخدم الترميز الأحادي النشط One-Hot Encoding لتمثيل التصنيفات للصور. حيث يَستَخدم الترميز الأحادي النشط One-Hot Encoding متجهًا vector مُكونٌ من قيمٍ ثنائيةٍ لتمثيل القيم الرقمية أو الصنفية. ونظرًا لأن أصنافنا مخصصةٌ لتمثيل الأرقام من 0 إلى 9، فإن المتجه سيحتوي على 10 قيمٍ، واحدةٌ لكلّ رقمٍ ممكنٍ. وتُسنَد إحدى هذه القيم بوضع القيمة 1، وذلك لتمثيل الرقم في هذا المؤشر للمتجه، كما ستُسنَد القيم الباقية بالقيمة 0. فمثلًا، سيُمثلُ الرقم 3 من خلال المتجه هكذا: [0 ،0 ،0 ،1 ،0 ،0 ،0 ،0 ،0 ،0]. وسنلاحظ وجود القيمة 1 في الفهرس 3، لذلك فإن المتجه سيُمثِلُ الرقم 3. ولتمثيل الصور الفعلية والتي تكون بحجم 28x28 بكسل، يتوجب علينا تسويتها في المتجه 1D بحجم 784 بكسل، وهو ناتج ضرب 28×28. وسنخزن هذه البكسلات والتي ستُشكل الصورة لاحقًا، وذلك في قيمٍ تتراوح بين 0 و255، حيث ستحدّد هذه القيم تدرج اللون الرمادي للبكسل، وستُعرَض صورنا باللونين الأبيض والأسود فقط. لذلك سيُمثلُ البكسل الأسود بالقيمة 255، والبكسل الأبيض بالقيمة 0، وذلك مع التدرجات المختلفة للون الرمادي بينهم. والآن بعد استيرادنا للبيانات، حان الوقت للتفكير في كيفية بناء الشبكة العصبية. 3. تحديد بنية الشبكة العصبية يُشير مصطلح بنية الشبكة العصبية لعناصرٍ متنوعةٍ، مثل عدد الطبقات في الشبكة وعدد الوِحدات في كلّ طبقةٍ، كما يشير إلى كيفية توصيل هذه الوِحدات بين الطبقات المختلفة. ونظرًا لأن الشبكات العصبية مستوحاةٌ من كيفية عمل الدماغ البشري، فسنستخدم مصطلح الوِحدة ليُمثّل ما يُمكن تسميته بيولوجيًا بالخلايا العصبية. تأخذ الوِحدات بعض القيم من الوِحدات السابقة مثل مُدخلاتٍ لها، حيث تتشابه مع الخلايا العصبية التي تُمرر إشاراتها حول الدماغ، ثمّ تُجري عمليةً حسابيةً، وتُمرر القيمة الجديدة مثل مُخرجاتٍ إلى وِحداتٍ أخرى، وهكذا. تُوضع هذه الوِحدات على شكل طبقاتٍ متراكبةٍ فوق بعضها البعض مشكّلةً الشبكة العصبية، بحيث يمكن للشبكة أن تتألف كحدٍ أدنى من طبقتين، طبقةٌ لإدخال القيم، وطبقةٌ أخرى لإخراج القيم. يُستخدم مصطلح الطبقة المخفية لجميع الطبقات الموجودة بين طبقات المُدخلات وطبقات المُخرجات الخارجية، أي أن تلك الطبقات تكون مخفيةً عن العالم الحقيقي. تحقق البُنى المختلفة للشبكة نتائجًا مختلفةً عن بعضها البعض، ويمكن اتخاذ الأداء مثل معيارٍ للحكم على هذه البُنى المختلفة، كما يمكن اتخاذ عناصر أخرى معيارًا للحكم، مثل الوسطاء والبيانات ومدة التدريب. سنضيف هذه الأسطر البرمجية التالية لملفك، وذلك لتخزين عدد الوِحدات المُخصصة لكلّ طبقةٍ ووضعها في متغيّراتٍ عامةٍ. وهذه الطريقة ستسمح لنا بتغيير بِنية الشبكة بمكانٍ واحدٍ، وفي نهاية هذا المقال يمكنك اختبار مدى تأثير الأعداد المختلفة من الطبقات والوِحدات على نتائج نموذجنا: n_input = 784 # input layer (28x28 pixels) n_hidden1 = 512 # 1st hidden layer n_hidden2 = 256 # 2nd hidden layer n_hidden3 = 128 # 3rd hidden layer n_output = 10 # output layer (0-9 digits) يُوضح الرسم البياني التالي تصورًا للبِنية التي صمّمناها، مع توصيل كلّ طبقةٍ بالطبقات المحيطة بها توصيلًا كاملًا: ويرتبط مصطلح الشبكة العصبية العميقة Deep Neural Network بعدد الطبقات المخفية، وعادةً ما تُشير كلمة السطحية في مصطلح الشبكة العصبية السطحية إلى وجود طبقةٍ مخفيةٍ واحدةٍ، بينما تُشير كلمة العميقة إلى وجود طبقاتٍ مخفيةٍ متعددةٍ. ونظريًا إذا أُعطِيت الشبكة العصبية السطحية ما يكفي من بياناتٍ للتدريب، فيجب أن تَقدِر على تمثيل أي وظيفةٍ يمكن للشبكة العصبية العميقة أن تؤدّيها. ولكن من ناحية الفعالية الحسابية، فغالبًا ما يكون نتائج استخدام شبكةٍ عصبيةٍ عميقةٍ ذات حجمٍ صغيرٍ أفضل من النتائج التي تُعطيها الشبكة العصبية السطحية ذات العدد الكبير من الوِحدات المخفية، وذلك عند تأديتهم لنفس المَهمة. كما أن الشبكات العصبية السطحية غالبًا ما تواجه مشكلة فَرط التخصيص Overfitting، إذ يكون هدف الشبكة الأساسي هو حفظ بيانات التدريب التي شاهدتها، ولكنها لن تستطيع تعميم المعرفة التي اكتسبتها على البيانات الجديدة، وهذا هو السبب في كون استخدام الشبكات العصبية العميقة أكثر شيوعًا، إذ أنها تسمح للطبقات المتعددة الموجودة بين البيانات المُدخلة الأولية والبيانات المُصنفة الناتجة، بتعلم الميزات على مستوياتٍ متنوعةٍ، مما يُعزز قدرة الشبكة على التعلم وتعميم الفكرة. ومن العناصر الأخرى للشبكة العصبية التي يجب تعريفها هنا هي الوسطاء الفائقة Hyperparameters، فعلى عكس الوسطاء العادية التي تُحدث قيمها أثناء عملية التدريب، سنُسند قيم الوسطاء الفائقة في البداية وسنثبتها طوال العملية. أسنِد المتغيّرات بالقيم التالية في ملفك: learning_rate = 1e-4 n_iterations = 1000 batch_size = 128 dropout = 0.5 يمثل معدل التعلم Learning Rate مدى تعديل الوسطاء في كلّ خطوةٍ من عملية التعلّم، إذ تُعَد هذه التعديلات مكونًا رئيسيًا للتدريب، فبعد كلّ عملية مرور ٍعبر الشبكة، سنضبط أوزان الطبقات قليلًا لأهمية ذلك في محاولةٍ لتقليل الخسارة، حيث يمكن لمعدل التعلم المرتفع أن يتحقق بسرعة، ولكن يمكن كذلك أن تتجاوز القيم المثلى عند تحديثها في كلّ مرة. يشير مصطلح عدد التكرارات Number Of Iterations إلى عدد مرات مرورنا على خطوة التدريب، ويشير حجم الدفعة Batch Size لعدد أمثلة التدريب التي نستخدمها في كل خطوة، كما ويمثل المتغير dropout الموضع الذي نحذف عنده بعضًا من الوِحدات عشوائيًا. وسنستخدم المتغير dropout في الطبقة النهائية المخفية لإعطاء كلّ وِحدة من الوحدات احتمالًا بنسبة 50٪ للتخلص منها في كلّ خطوة تدريبٍ، وهذا سيساعد على منع ظهور مشكلة فرط التخصيص Overfitting. حددنا الآن بِنية شبكتنا العصبية والوسطاء الفائقة التي ستُؤثر على عملية التعلّم، والخطوة التالية هي بناء الشبكة مثل مخططٍ بيانيٍ من خلال مكتبة TensorFlow. 4. بناء مخطط بياني من خلال مكتبة TensorFlow لبناء شبكتنا، لابد لنا من إعداد الشبكة مثل مخططٍ بيانيٍ حسابي من خلال مكتبة TensorFlow لتنفيذه. والمفهوم الأساسي لمكتبة TensorFlow هو tensor، وهو بنية بياناتٍ مشابهةٍ لبِنية المصفوفة Array، أو القائمة List. وهذا المتغير سيهيَأ ويُعالَج عند مروره عبر المخطط البياني للشبكة عبر عملية التعلّم. وسنبدأ بتحديد ثلاثة متغيراتٍ tensors من نوع placeholders، وهو نوع tensor تُسندُ قيمته لاحقًا. والآن سنضيف الشيفرة البرمجية التالية إلى الملف الذي نعمل عليه: X = tf.compat.v1.placeholder("float", [None, n_input]) Y = tf.compat.v1.placeholder("float", [None, n_output]) keep_prob = tf.compat.v1.placeholder(tf.float32) إنّ الوسيط الوحيد الذي يتوجب علينا تحديده عند التعريف هو حجم البيانات التي سنُسندها لاحقًا، وبالنسبة للمتغير X سنستخدم شكل [None، 784]، إذ ستمثل القيمة None كميةً غير محددةٍ، وسنُسند كميةً غير محددةٍ من الصور ذات حجم 784 بكسل. بحيث يصبح شكل المتغير Y هو [None، 10]، وستمثل None عددًا غير محددٍ من التصنيفات الناتجة، مع وجود 10 أصنافٍ محتملةٍ. وسنستخدم في المتغير keep_prob tensor من نوع placeholders للتحكم في معدل dropout، وسنجعله من نوع placeholders وذلك لجعله متغيرًا من نوعٍ قابلٍ للتعديل، بدلًا من كونه متغيرًا من نوعٍ غير قابلٍ للتعديل immutable variable، وذلك لأننا نريد استخدام نفس tensor التدريب عند إسناد dropout بالقيمة 0.5، ونفس tensor الاختبار عند إسناد dropout بالقيمة 1.0. والوسطاء التي ستُحدث قيمها الشبكة العصبية في عملية التدريب هي القيم الخاصة بوزن كلّ طبقةٍ، والتي تُعبر عن الأهمية وقيم التحيز bias values، لذلك سنحتاج لإسنادهم بقيمٍ إبتدائيةٍ بدلًا من قيمٍ فارغةٍ. وهذه القيم هي الأساس الذي ستبدأ الشبكة رحلة التعلم انطلاقًا منها، إذ ستُستخدم في تفعيل دوال الشبكة العصبية، والتي تُمثِلُ قوة الاتصالات بين الوِحدات. ونظرًا لاستمرار تحسين القيم أثناء عملية التدريب، يمكننا ضبطها حاليًا بالقيمة 0. لاحظ أن القيمة الأولية في الواقع لها تأثيرٌ كبيرٌ على الدقة النهائية للنموذج. وسنستخدم التوزيع الاحتمالي الطبيعي المنقطع Truncated normal distribution لتوليد قيمٍ عشوائيةٍ لأوزان الطبقات، بحيث يكونون قريبين من الصفر حتى يتمكنوا من التعديل إما باتجاهٍ إيجابيٍ أو سلبيٍ، كما يكونون مختلفين قليلًا، وذلك ليُنتِجوا أخطاءً مختلفةً، وبهذه الطريقة سنضمن بأن يتعلم النموذج شيئًا مفيدًا. والآن سنضيف هذه الأسطر البرمجية التالية لملفنا الذي نعمل عليه: weights = { 'w1': tf.Variable(tf.random.truncated_normal([n_input, n_hidden1], stddev=0.1)), 'w2': tf.Variable(tf.random.truncated_normal([n_hidden1, n_hidden2], stddev=0.1)), 'w3': tf.Variable(tf.random.truncated_normal([n_hidden2, n_hidden3], stddev=0.1)), 'out': tf.Variable(tf.random.truncated_normal([n_hidden3, n_output], stddev=0.1)), } بالنسبة للتحيز Bais، سنستخدم قيمةً ثابتةً صغيرةً لضمان تنشيط جميع tensors المراحل الأولية، وبالتالي المساهمة في الانتشار. وستُخزن الأوزان وجميع tensors التحيزات في objects قواميس Dictionary لسهولة الوصول إليها. أضف هذه الشيفرة البرمجية للملف الذي نعمل عليه وذلك لتعريف التحيز وقِيَمه: biases = { 'b1': tf.Variable(tf.constant(0.1, shape=[n_hidden1])), 'b2': tf.Variable(tf.constant(0.1, shape=[n_hidden2])), 'b3': tf.Variable(tf.constant(0.1, shape=[n_hidden3])), 'out': tf.Variable(tf.constant(0.1, shape=[n_output])) } والآن جهّز طبقات الشبكة العصبية من خلال تعريف العمليات التي ستتعامل مع tensors المرحلة الحالية. وأضف هذه الشيفرة البرمجية للملف الذي نعمل عليه: layer_1 = tf.add(tf.matmul(X, weights['w1']), biases['b1']) layer_2 = tf.add(tf.matmul(layer_1, weights['w2']), biases['b2']) layer_3 = tf.add(tf.matmul(layer_2, weights['w3']), biases['b3']) layer_drop = tf.nn.dropout(layer_3, keep_prob) output_layer = tf.matmul(layer_3, weights['out']) + biases['out'] ستنفذ كلّ طبقةٍ مخفيةٍ عملية ضربٍ للمصفوفة على نتائج الطبقة التي سبقتها وعلى أوزان الطبقة الحالية، وسيُضاف التحيز لهذه القيم. في الطبقة المخفية الأخيرة، سنُطبِّق عملية التسرب dropout بالقيمة 0.5 للمتغير Keep_prob الخاص بنا. الخطوة الأخيرة في بناء المخطط البياني، هي تحديد دالة الخسارة التي نريد تحسينها. والاختيار الشائع لدالة الخسارة في المكتبة البرمجية TensorFlow هو الانتروبي المشترك Joint Antropy، والمعروف كذلك باسم فقدان السجل log-loss، وهو الذي يُحدد الفرق بين التوزيعين الاحتماليين لكلً من التنبؤات والتصنيف. ويمكن أن تكون قيمة الانتروبي المشترك 0، وذلك في أفضل الأحوال عند التصنيف المثالي، وذلك مع انعدام الخسارة تمامًا. سنحتاج كذلك إلى اختيار خوارزمية التحسين المناسبة، والتي سنستخدمها لتقليل الناتج من دالة الخسارة. وتُسمى هذه العملية بعملية تحسين الانحدار التدريجي، وهي طريقةٌ شائعةٌ للعثور على الحد الأدنى للدالة، من خلال اتخاذ خطواتٍ تكراريةٍ على طول التدرج في الاتجاه السلبي التنازلي. وهناك العديد من الخيارات لخوارزميات تحسين الانحدار التدريجي المُطبقة في المكتبة البرمجية TensorFlow، إلا أننا سنستخدم في هذا المقال خوارزمية المُحسِّن أدم Adam optimizer، الذي يعمتد على عملية تحسين الانحدار التدريجي باستخدام الزخم أو كمية الحركة Momentum، وذلك بتسريع عملية التنعيم من خلال حساب متوسط ٍمُرجَّحٍ بكثرة للتدرجات، واستخدام ذلك في التعديلات مما يؤدي لتقاربٍ أسرع. وسنضيف هذه الشيفرة للملف الذي نعمل عليه: cross_entropy = tf.reduce_mean( tf.nn.softmax_cross_entropy_with_logits( labels=Y, logits=output_layer )) train_step = tf.compat.v1.train.AdamOptimizer(1e-4).minimize(cross_entropy) عرّفنا حتى الآن الشبكة وبنيناها باستخدام المكتبة البرمجية TensorFlow، والخطوة التالية هي إرسال البيانات عبر المخطط البياني لتدريبها، ومن ثَمّ اختبارها للتحقق فيما إن كانت تعلمت شيئًا بالفعل أم لا. 5. التدريب والاختبار تتضمن عملية التدريب تغذية المخطط البياني للشبكة بمجموعة بيانات التدريب، وتحسين نتيجة دالة الخسارة، إذ أن في كلّ مرةٍ تمر فيها الشبكة عبر مجموعةٍ إضافيةٍ من صور التدريب، فستُحدثُ الوسطاء لتقليل الخسارة، وذلك بهدف تحسين دقة التنبؤ للأرقامٍ؛ أما عملية الاختبار، فتتضمن تشغيل مجموعة بيانات الاختبار الخاصة بنا عبر المخطط البياني المدرّب، كما ستتتبع عدد الصور التي صح التنبؤ بها، حتى نحسب الدقة جيدًا. قبل البدء في عملية التدريب، سوف نحدد دالة تقييم الدقة لكي نتمكن من طباعتها على مجموعاتٍ صغيرةٍ من البيانات أثناء التدريب. هذه البيانات المطبوعة ستسمح لنا بالتحقق من انخفاض الخسارة وزيادة الدقة، وذلك بدءًا من المرور الأول عبر المخطط البياني، وحتى المرور الأخير؛ كما ستسمح لنا بتتبّع ما إذا نفذنا عمليات مرور ٍكافيةً عبر المخطط البياني للوصول لنتيجةٍ مناسبةٍ ومثاليةٍ أم لا: correct_pred = tf.equal(tf.argmax(output_layer, 1), tf.argmax(Y, 1)) accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) سنستخدم الدالة arg_max في المتغيّر right_pred للموازنة بين الصور التي صَحَ توقُّعها، وذلك بالنظر لقيمة التنبؤات output_layer والتصنيفات Y، وسنستخدم الدالة equal لإعادة هذه النتائج مثل قائمةٍ مؤلفةٍ من قيمٍ بوليانيةٍ. ويمكننا بعد ذلك تحويل هذه القائمة للنوع float، وذلك لحساب المتوسط للحصول على درجة الدقة الإجمالية. الآن نحن جاهزون لتهيئة الجلسة لتشغيل المخطط البياني، إذ سنرسل للشبكة أمثلة التدريب الخاصة بنا، وبمجرد انتهاء التدريب، سنرسل أمثلة اختبارٍ جديدةٍ عبر المخطط البياني نفسه لتحديد دقة النموذج. أضف هذه الشيفرة للملف الذي نعمل عليه: init = tf.compat.v1.global_variables_initializer() sess = tf.compat.v1.Session() sess.run(init) إن جوهر عملية التدريب في التعلم العميق هو تحسين ناتج دالة الخسارة. ونحن هنا سنهدف إلى تقليل الفرق بين التصنيفات المُتوقعة للصور والتصنيفات الحقيقية لها. وستتضمن هذه العملية أربع خطواتٍ تتكرر لعددٍ محددٍ من مرات المرور عبر المخطط البياني، وهي: دفع القيم إلى الأمام عبر الشبكة. حساب الخسارة. دفع القيم للخلف عبر الشبكة. تحديث الوسطاء. ففي كلّ خطوة تدريب، سنعدِّل الوسطاء قليلًا في محاولةٍ لتقليل نتائج دالة الخسارة. وفي الخطوة التالية مع تَقدُّم عملية التعلّم، يجب أن نشاهد انخفاضًا في الخسارة، حيث سنوقِف التدريب في النهاية، وسنستخدم الشبكة مثل نموذجٍ لاختبار بياناتنا الجديدة. سنضيف هذه الشيفرة البرمجية للملف الذي نعمل عليه: # التدريب على دفعات صغيرة for i in range(n_iterations): startbatch = (i*batch_size) % len(x_train) endbatch = ((i+1)*batch_size) % len(x_train) batch_x = np.array(x_train[startbatch:endbatch]) batch_x = batch_x.reshape(batch_size, -1) batch_y = y_train[startbatch:endbatch].toarray() if batch_x.shape != (128, 784): continue sess.run(train_step, feed_dict={ X: batch_x, Y: (batch_y), keep_prob: dropout }) # طباعة الخسارة والدقة لكل دفعة صغيرة if i % 100 == 0: minibatch_loss, minibatch_accuracy = sess.run( [cross_entropy, accuracy], feed_dict={X: batch_x, Y: batch_y, keep_prob: 1.0} ) print( "Iteration", str(i), "\t| Loss =", str(minibatch_loss), "\t| Accuracy =", str(minibatch_accuracy) ) بعد 100 عملية مرورٍ لكلّ خطوة تدريبٍ والتي أرسلنا فيها مجموعةً صغيرةً من الصور عبر الشبكة، سنطبع نتائج دالة الخسارة والدقة لتلك الدفعة. وينبغي ألّا نتوقع هنا انخفاض معدل الخسارة وزيادة الدقة، لأن القيم لكل دفعةٍ صغيرةٍ. إذ أن النتائج ليست للنموذج بأكمله. فنحن نستخدم مجموعاتٍ صغيرةٍ من الصور بدلًا من إرسال كلّ صورةٍ بمفردها، وذلك لتسريع عملية التدريب والسماح للشبكة برؤية عددٍ من الأمثلة المختلفة قبل تحديث الوسطاء. وبمجرد اكتمال التدريب، يمكننا تشغيل الجلسة على الصور المخصصة للاختبار. وهذه المرة سنستخدم القيمة 1.0 مثل مُعدلِ تَسرّب dropout للمتغيّر Keep_prob، وذلك للتأكد من أن جميع الوِحدات نشطةٌ في عملية الاختبار. أضف هذه الشيفرة البرمجية للملف الذي نعمل عليه: إعداد صور الاختبار كمتجهات أحادية طول كل منها 28*28 # x_test = x_test.reshape(-1,784) test_accuracy = sess.run(accuracy, feed_dict={X: x_test, Y: y_test.toarray(), keep_prob: 1.0}) print("\nAccuracy on test set:", test_accuracy) والآن سنشغّل برنامجنا، لنعرف مدى دقة شبكتنا العصبية في التعرف على الأرقام المكتوبة بخط اليد. وسنحفظ التغييرات في الملف main.py الذي نعمل عليه. نَفِّذ الأمر التالي في الوِحدة الطرفية لتنفيذ الشيفرة البرمجية: (tensorflow-demo) $ python main.py سترى نتيجةً مشابهةً لما يلي، ويمكن أن تختلف قليلًا نتائج الخسارة والدقة الفردية: Iteration 0 | Loss = 3.67079 | Accuracy = 0.140625 Iteration 100 | Loss = 0.492122 | Accuracy = 0.84375 Iteration 200 | Loss = 0.421595 | Accuracy = 0.882812 Iteration 300 | Loss = 0.307726 | Accuracy = 0.921875 Iteration 400 | Loss = 0.392948 | Accuracy = 0.882812 Iteration 500 | Loss = 0.371461 | Accuracy = 0.90625 Iteration 600 | Loss = 0.378425 | Accuracy = 0.882812 Iteration 700 | Loss = 0.338605 | Accuracy = 0.914062 Iteration 800 | Loss = 0.379697 | Accuracy = 0.875 Iteration 900 | Loss = 0.444303 | Accuracy = 0.90625 Accuracy on test set: 0.9206 ولمحاولة تحسين دقة نموذجنا، أو لمعرفة المزيد حول تأثير ضبط الوسطاء الفائقة hyperparameters، يمكننا تغييرها لاختبار تأثيرها المنعكس على معدّل التعلم وعتبة التسرب Dropout Threshold، وكذا حجم الدفعة من الصور في كمية الأمثلة وعدد مرات المرور عبر المخطط، كما يمكننا كذلك تغيير عدد الوِحدات في طبقاتنا المخفية وتغيير عدد الطبقات المخفية نفسها، وذلك لنرى كيف ستؤثر بنية الشبكة العصبية على النموذج سواءً بزيادة دقته أو بتخفيضها. وللتأكد من أن الشبكة تتعرف جيدًا على الصور المكتوبة بخط اليد، فسنختبرها على صورةٍ خاصةٍ بنا، فإذا كنت تعمل على جهازك المحلي وترغب في استخدام صورٍ من جهازك، يمكنك استخدام أي محرر رسوماتٍ لإنشاء صورة بأبعاد 28x28 بكسل لأي رقمٍ تريده. مثلًا: نزل الصورة وانقلها إلى مجلد المشروع (تأكد أنها باسم test_image.png أو غير اسمها في الشيفرة) ثم أضف في نهاية الملف main.py هذا السطر البرمجي التالي لتحميل صورة الاختبار للرقم المكتوب بخط اليد: img = np.array(Image.open("test_image.png").convert('L')).ravel() إن الدالة open من مكتبة الصور Image تحمّل صورة الاختبار مثل مصفوفةٍ رباعية الأبعاد 4D، حيث تحتوي على قنوات الألوان الثلاث الرئيسية RGB بالإضافة إلى الشفافية، ولكن هذا ليس نفس التمثيل الذي استخدمناه سابقًا عند القراءة من مجموعة البيانات باستخدام المكتبة البرمجية TensorFlow، لذلك سنحتاج للقيام ببعض المهام الإضافية ليتناسب تنسيق هذه الصور مع التنسيق الذي سبق واعتمدناه في الخوارزمية. سنستخدم الدالة convert مع الوسيط L لتقليل تمثيل 4D RGBA إلى قناة لونٍ رماديةٍ واحدةٍ، وسنُخزنها على هيئة مصفوفة numpy. وسنستدعي ravel لتسوية المصفوفة. الآن بعد أن صححّنا بنية معلومات الصورة، يمكننا تشغيل الجلسة بنفس الطريقة السابقة، ولكن هذه المرة سنُرسل صورةً واحدةً فقط للاختبار. وسنضيف الشيفرة التالية للملف لاختبار الصورة وطباعة التصنيف الناتج، هكذا: prediction = sess.run(tf.argmax(output_layer, 1), feed_dict={X: [img]}) print ("Prediction for test image:", np.squeeze(prediction)) وتُستدعى الدالة np.squeeze على المتغير prediction ليُعِيد عددًا صحيحًا وفريدًا إلى المصفوفة. وسيتضح من الناتج أن الشبكة العصبية قد تعرفت على الصورة كرقم 4، هكذا: Prediction for test image: 4 يمكنك الآن تجربة عملية اختبار الشبكة باستخدام صورٍ أكثر تعقيدًا مثل الأرقام المتشابهة مع الأرقام الأخرى، أو أرقامٍ مكتوبةٍ بخطٍ سيئٍ أو حتى خاطئةٍ، وذلك لمعرفة وقياس مدى نجاحها. الخلاصة في هذا المقال، نجحنا في تدريب شبكةٍ عصبيةٍ لتصنيف مجموعة بياناتٍ MNIST بدقةٍ تصل إلى 92٪، واختبارها على صورةٍ خاصةٍ بنا، مع العلم بأنه قد تحققت نسبة أعلى في الأبحاث العلمية الحديثة وكانت حوالي 99٪ لنفس الفكرة، وذلك باستخدام بُنىً مختلفةً لشبكةٍ عصبيةٍ ذات تعقيدٍ أكبر، بحيث تتضمن طبقات تلافيفية. وتستخدم تلك الشبكات بِنيةً ثنائية الأبعاد للصورة لتمثيل المحتويات تمثيلًا أفضل من تمثيل نموذجنا السابق، إذ أن نموذجنا يُسوّي كلّ البكسلات في متجهٍ واحدٍ مكوّنٍ من 784 وِحدة. ويمكنك قراءة المزيد على الموقع الرسمي للمكتبة TensorFlow، والاطلاع على الأوراق البحثية التي تُفصِّل أدق النتائج على موقع MNIST. والآن بعد أن تعرفنا على كيفية بناء شبكةٍ عصبيةٍ وتدريبها، يمكنك تجربة هذا التطبيق واستخدامه على بياناتك الخاصة، أو اختبارها على مجموعات بياناتٍ شائعةٍ مختلفةٍ عن تلك التي استخدمناها مثل: مجموعة البيانات من غوغل أو مجموعة البيانات من CIFAR-10، وذلك للتعرف على صورٍ أكثر عموميةٍ وشموليةٍ. ترجمة -وبتصرف- للفصل How To Build a Neural Network to Recognize Handwritten Digits with TensorFlow من كتاب Python Machine Learning Projects لكاتبته Ellie Birbeck يُمكن تنزيل الكود كاملًا من Recognize-Handwritten-Numbers-MNIST-master. اقرأ أيضًا إعداد شبكة عصبية صنعية وتدريبها للتعرف على الوجوه النسخة الكاملة من كتاب مدخل إلى الذكاء الاصطناعي وتعلم الآلة النسخة العربية الكاملة من كتاب البرمجة بلغة بايثون
-
الذكاء الصنعي وخوارزميات تعلم الآلة AI/ML تتفق جميع التقنيات والخوارزميات التي تندرج تحت مسمى الذكاء الصنعي Artificial Intelligence وتعلم الآلة Machine Learning باختلاف أطيافها على البدء بتحليل البيانات الخام والبحث فيها (بطرق خوارزمية ممنهجة) لاستكشاف ما تحتويه من أنماط وعلاقات في داخلها وبين كياناتها. إن معرفة مثل تلك الأنماط والعلاقات ستساعدنا على بناء نماذج قادرة على استقراء الجوهر العام المشترك الذي يجمع ويصنف تلك الكتل من البيانات على الرغم من الاختلافات في التفاصيل الهامشية والتي قد تكون كبيرة ظاهريا ومربكة في عين من لا يتمتع بالخبرة في مجال الدراسة. لقد أثبتت مثل هذه النظم فعاليتها في حوسبة العديد من المهام التي كان يعتقد حتى وقت قريب أنها عصية على عالم البرمجيات، ساعد على ذلك الطفرة التي شهدتها عتاديات الحواسيب في العقد الأخير والتقدم الهائل في إمكانيات الحوسبة المتوازية لوحدات المعالجة الرسومية GPU، وكذلك توافر حلول الحوسبة السحابية لشريحة أكبر من فرق التطوير البرمجية التي كان يصعب عليها في الماضي الحصول على حواسيب فائقة بسهولة. إننا نشهد في الفترة الحالية انتقالا وتحولا جذريا في مفاهيم علم البرمجة مقارنة بما سبق وبما تعلمناه أو استخدمناه خلال القرن الماضي (كالبرمجة الوظيفية ومخططات سير العمل، أو حتى البرمجة الغرضية التوجه بكائناتها وطرائقها)، فالمبرمج هو من كان يضع منطق العمل وخوارزميته ليحدد أسلوب معالجة المدخلات وتوليد المخرجات، وقد امتازت فترة ثمانينيات وتسعينيات القرن الماضي بوفرة القدرة الحاسوبية مقارنة بكمية البيانات المتوافرة للمعالجة أصلا، لكن ما نشهده الآن في القرن الحادي والعشرين قد قلب هذه المعادلة رأسا على عقب، فقد أصبح لدينا فائض كبير من البيانات التي تجمع من كل حدب وصوب على مدار الساعة بدءا من جوالاتنا (التي ما عادت وسيلة للاتصال فقط) وصولا إلى الأقمار الاصطناعية في مداراتها حول الأرض. إن هذا المستوى العالي من الأتمتة في حياتنا ومجتمعاتنا نتج عنه كم هائل من البيانات التي تجاوزت بمراحل القدرة على تحليلها واستخلاص المعلومات منها بالطرق التقليدية، وهو ما أعاد فتح الأبواب مجددا لتقنيات الذكاء الصنعي التي كان لها مجد سابق منتصف القرن الماضي لكنه زال بعد أن عجزت القدرات العتادية في ذلك الحين عن مواكبة شطحات المتنورين من رواد هذا الحقل من العلوم، لكننا اليوم وبتضافر جهود الباحثين في مجالات شتى مثل علم البيانات والإحصاء والحواسيب بلا شك، أصبحنا قادرين على تنفيذ ما كانوا يتحدثون عنه ضمن طيف واسع من التطبيقات ربما بات يتجاوز أقصى طموحاتهم. الشبكات العصبية Neural Networks في جزء من لوحة رافائييل الجصيّة "مدرسة أثينا" تم رسم أفلاطون وأرسطو بشكل يعبّر عن نظريّة كل منهما في المعرفة، فأرسطو يومئ نحو الأرض فيما يشير أفلاطون بإصبعه نحو السماء، حيث كان ينظر أرسطو إلى الطبيعة بحثا عن إجابات في حين أن أفلاطون يبحث عن المثالي. وها نحن ننتقل في عالم البرمجة من فلسفة أفلاطون التي كنا نتّبعها في أدوات بناء تطبيقاتنا من لغات برمجة وما تحويه من متغيرات وتوابع وعبارات شرطية وحلقات وسواها، إلى فلسفة أرسطو حيث نبحث في كيفية عمل أدمغتنا ونحاول محاكاتها في بنيتها وطريقة عملها، وهكذا تماما ولد مجال الشبكات العصبية ضمن علوم الذكاء الصنعي. حيث ابتدأت المحاكاة على مستوى الخلية العصبية (العصبون)، فالعصبونات هي خلايا تتألف من جسم مركزي يتضمن نواة الخلية ويمتد منه استطالة وحيدة تكوّن ليف عصبي طويل يقوم مقام السلك الناقل للإشارة الخارجة من الخلية عند نهايته تفرعات كثيرة تنتهي بعقد مشبكية صغيرة، أما في الطرف الآخر من جسم الخلية العصبية فتبرز تفرّعات تخرج في كافة الاتجاهات والتي عن طريقها ترد إلى جسم الخلية البيانات الداخلة، ولابد للإشارات لكي تمر من عصبون إلى آخر أن تجتاز الفجوة الضيقة ما بين عقدة المشبك والجسم أو التفرّع للعصبون التالي. تتلقى الخلية العصبية عدة تنبيهات من الخلايا المجاورة تؤدي إلى شحنها، فإذا وصلت تلك الشحنة إلى عتبة معينة ينبثق كمون كهربائي عند قاعدة المحور وينتشر دفعة واحدة على طوله. لا تستجيب العصبونات لمختلف التنبيهات بشكل متشابه، فلكل منبه درجة أهمية تزيد أو تنقص، كما أن خرج العصبون لا يمتاز بالتدرج، فإما أن تكون هناك نبضة عصبية تنتشر عبر المحور إلى الخلايا المجاورة أو لن يكون هنالك شيء على الإطلاق. طبعا هذا الوصف لبنية العصبون وطريقة عمله فيه تبسيط شديد لحقيقة الأمر، لكن هذا المستوى من الشرح يفي بالغرض. على الرغم من أننا عندما نحاكي عمل العصبون (بفرض أننا نعلم طريقة عمله تماما) لن نحصل على عصبون حقيقي، لكننا سنحصل على معالجة حقيقية للمعلومات كما لو كان العصبون هو من قام بها، وهذا هو بيت القصيد. إن التمثيل الرياضي للعصبون يفترض أن لدينا n إشارة دخل سنرمز إليها بالمقادير X1, X2, …, Xn يرتبط كل منها بوزن أو تثقيل للتعبير عن دور المشابك العصبية كون العصبونات لا تستجيب بشكل متشابه لمختلف التنبيهات كما اتفقنا سابقا. سنرمز إلى تلك الأوزان بالمقادير W1, W2, …, Wn والتي قد تكون قيما موجبة أو حتى سالبة تكبر أو تصغر بحسب طبيعة ودور إشارة الدخل المرتبطة بها سواء كانت محفّزة أم مثبّطة وإلى أي قدر هي كذلك. وهكذا يمكننا التعبير عن مجمل الدخل الآتي إلى العصبون المفرد بالشكل الرياضي التالي: من جهة أخرى، لتمثيل إشارة خرج العصبون نحتاج إلى دالة رياضية تستطيع توصيف عمل قانون الكل أو لا شيء تبعا لعتبة معيّنة، وهناك عدّة خيارات رياضية شائعة قد يتم تفضيل إحداها على الأخرى بحسب طبيعة البيانات التي نتعامل معها نذكر منها على سبيل المثال لا الحصر: Sigmoid (للقيم الثنائية 0/1 أو نعم/لا أو حتى ذكر/أنثى)، TanH (للفئات أو التصنيفات المتقطعة مثل الأعراق: عربي، أوروبي، أفريقي، آسيوي، هندي، الخ.)، وكذلك ReLU (للقيم المتصلة كالعمر مثلا). يوضح الشكل التالي الصيغة الرياضية والتمثيل البياني الذي يظهر العلاقة فيما بين الدخل والخرج لكل منها: بعد أن قمنا بتوصيف العصبون بشكل رياضي مبسّط، علينا الانتقال للحديث عن بنية الشبكات التي تنتظم بها تلك العصبونات وطرق ارتباطها بعضها ببعض، سنتناول بالشرح شكلا واحدا من أشكال بناء الشبكات العصبية، لكن ذلك لا يعني عدم وجود معماريات أخرى لبناء تلك الشبكات. سنفترض أن العصبونات تنتظم في طبقات ولكل طبقة عدد من العصبونات بحيث أن خرج أي عصبون من هذه الطبقة يتم إيصاله إلى كل عصبونات الطبقة التالية. يمكن تمييز الطبقة الأولى على أنـها طبقة الإدخال والتي تتلقى بياناتها من الوسط الخارجي للشبكة العصبية، كذلك نعرّف طبقة الإخراج على أنـها الطبقة الأخيرة والتي ترسل ناتج معالجة البيانات ثانية إلى الوسط الخارجي، في حين يسمّى كل ما عدى ذلك من طبقات بين هاتين الطبقتين بالطبقات الخفية. كما نلاحظ فإن عدد العصبونات في كل من طبقتي الإدخال والإخراج محدد بحسب طبيعة الوظيفة المناطة بالشبكة العصبية (مثلا عدد العنصورات Pixels الإجمالي في الصورة لكل قناة لونية RGB في الدخل، وعدد الفئات المراد تصنيفها في الخرج)، أمّا عدد الطبقات الخفية وعدد العصبونات في كل منها فلا توجد قواعد ضابطة وواضحة لذلك. مكتبة TensorFlow من Google مكتبة TensorFlow هي منصة متكاملة لتدريب وبناء تطبيقات الذكاء الصنعي وتعلم الآلة بالاعتماد على تقنية الشبكات العصبية طورتها شركة Google باستخدام لغة Python ونشرتها تحت ترخيص البرمجيات الحرة والمفتوحة المصدر، وهي تعد في الوقت الراهن واحدة من أكثر المكتبات شهرة واستخداما في هذا المجال (رغم أنها ليست الوحيدة قطعا)، وذلك نظرا لغزارة وتنوع المصادر والأدوات المتوافرة لها والتي تتيح للباحثين القدرة على بناء واستخدام تطبيقات الذكاء الصنعي في أعمالهم. للمزيد حول TensorFlow يمكنكم الإطلاع على الموقع الرسمي لها على الرابط التالي: https://www.tensorflow.org دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن مكنز TensorFlow Hub للنماذج إحدى أهم التحديات في عالم الذكاء الصنعي بشكل خاص، وعلوم البيانات بشكل عام، هي القدرة على إعادة استخدام ما سبق وما توصل إليه فريق تطوير آخر سابقًا، لذا قدمت Google هذا المكنز لوضع طريقة معيارية في مشاركة نماذج الشبكات العصبية بحيث تتضمن كافة المعلومات المطلوبة لإعادة استخدامها سواء كانت بنية الشبكة العصبية ذاتها (من حيث عدد الطبقات، ونوعها، وعدد العصبونات في كل منها، ونوع الروابط فيما بينها، الخ.)، إضافة إلى قيم الوسطاء والأوزان المختلفة في تلك الشبكة العصبية بعد إتمام عملية تدريبها. يمكن أخذ هذه النماذج وإعادة استخدامها في مهام مختلفة أو حتى إعادة تدريبها بسهولة مستخدمين تقنية تدعى نقل التعلم والتي سنتحدث عنها لاحقا في هذا الدرس. لمزيد من المعلومات حول هذا المكنز يمكنكم زيارة الموقع الرسمي له على الرابط التالي: https://www.tensorflow.org/hub. نموذج MobileNet V2 للرؤية الحاسوبية تم تصميم هذه العائلة من نماذج الشبكات العصبية المخصصة لمهام الرؤية الحاسوبية عامة الأغراض مثل التصنيف وتحديد الأجزاء والعناصر في الصورة وسواها من الوظائف مع مراعاة المصادر المحدودة التي قد تكون متوافرة على الأجهزة المحمولة. إن القدرة على تشغيل تطبيقات التعلم العميق على أجهزة الجوال الشخصية ستحسن من تجربة المستخدم نظرا لأنها ستكون متاحة في أي زمان ومكان بغض النظر عن الحاجة إلى الاتصال بمصادر خارجية على الإنترنت، وهو ما سيترافق مع فوائد إضافية لجهة الأمان والخصوصية والاقتصاد في استهلاك الطاقة. لمزيد من المعلومات يمكنك الإطلاع على الرابط المدرج ضمن قسم المراجع1. تقنية نقل التعلم Transfer Learning إن النماذج الحديثة للتعرف على الصور تحتوي على الملايين من الوسطاء (من أوزان للروابط ما بين الآلاف من العصبونات المرصوفة في العشرات من الطبقات الخفية) والتي يتطلب تدريبها من الصفر كما كبيرا من بيانات التدريب من جهة، والكثير من الطاقة الحاسوبية من جهة أخرى (تقدّر بالمئات من ساعات الحساب على وحدات المعالجة الرسومية GPU أو حتى أكثر بكثير). إن تقنية نقل التعلم تعتمد على حيلة ذكية لاختصار كم المصادر الكبير الذي نحتاجه لتطوير نموذج رؤية حاسوبية جديد تخصص مهاراته في التعرف على نمط مختلف من الصور (مثلا صور الأشعة السينية لتشخيص وجود أورام سرطانية محتملة عوض التعرف على الأنواع المختلفة من الأزهار البرية). تقوم الفكرة على استبدال الطبقة الأخيرة فقط من شبكة عصبية سبق وأن تم تدريبها بشكل جيد على تصنيف الصور ولو لغايات مختلفة، مستفيدين بذلك من كم المهارات التي اكتسبتها بنية الطبقات الخفية في ذلك النموذج، ابتداء من التعرف على أنماط النسج والأشكال وترابط الأجزاء وعلاقات الألوان وغيرها مما هو مشترك بالعموم بين كافة نظم الرؤية الحاسوبية، والتي يمكن إعادة استخدامها ومشاركتها بين النماذج المختلفة. وحدها الطبقة الأخيرة فقط الخاصة بالأصناف تتغير بتغير الغاية والهدف من النموذج الذي يجري تطويره وتخصيصه، لذا هي وحدها التي سيتم تدريبها فعليا عند استخدام تقنية نقل التعلم هذه. لمزيد من المعلومات حول هذه التقنية في التعليم يمكنكم الإطلاع على رابط ورقة البحث العلمي المدرجة ضمن قسم المراجع أدناه2. تجهيز بيئة العمل سنستخدم منصة Docker المخصصة لتطوير ونشر وإدارة التطبيقات باستخدام فكرة الحاويات وذلك لتسهيل بناء بيئة العمل لدينا من أجل تنفيذ التطبيق العملي في هذه الجلسة، حيث أن الحاويات هي عبارة عن حزم تنفيذية خفيفة وقائمة بذاتها لتطبيق ما، تحوي كل المكتبات وملفات الإمداد والاعتماديات والأجزاء الأخرى الضرورية ليعمل التطبيق ضمن بيئة معزولة، هذا عدى عن أنها خفيفة لأنها لا تتطلب حملا إضافيا كالأجهزة الافتراضية كونها تعمل ضمن نواة النظام المضيف مباشرة دون الحاجة إلى نظام ضيف، وبذلك نزيل عن كاهلنا في هذه المرحلة أي تعقيدات تختص بالتنصيب والربط والإعداد لمختلف مكونات بيئة التطوير الخاصة بمكتبة TensorFlow وهي مهمة ليست باليسيرة على المبتدئ، لذا عليك القيام بتثبيت Docker على حاسوبك الشخصي من الموقع الرسمي https://www.docker.com/ قبل الانتقال إلى الخطوة التالية. اطلع على مقال «التعامل مع حاويات Docker» في قسم دوكر في أكاديمية حسوب. تطبيق عملي يقوم بتحديد جنس الشخص من صورة وجهه بداية نقوم بتنصيب TensorFlow على Docker ضمن حاوية تحت تسمية hsoub-ft، قد تتطلب هذه الخطوة بعض الوقت نظرا لكون حجم صورة الحاوية التي سيتم سحبها وتنزيلها عبر الإنترنت يتجاوز 1GB: docker pull tensorflow/tensorflow docker run --name hsoub-tf -it -d tensorflow/tensorflow:latest بعد ذلك نقوم بالدخول إلى سطر الأوامر ضمن الحاوية ونقوم بتنصيب الإصدار 2.0 على الأقل من مكتبة TensorFlow وكذلك الإصدار 0.6 على الأقل من نموذج تصنيف الصور3 الذي سنستخدمه والمستضاف في مكنز TensorFlow Hub، و بعد إتمام هذه الخطوات نخرج باستخدام الأمر exit في سطر الأوامر للعودة إلى الجهاز المضيف: docker exec -it hsoub-tf bash pip install "tensorflow~=2.0" pip install "tensorflow-hub[make_image_classifier]~=0.6" exit الخطوة التالية هي تحضير بيانات التدريب وذلك من خلال الحصول على الصور التي سيتم تدريب الشبكة عليها، في مثالنا هذا استخدمنا مجموعة جزئية تتكون من حوالي 2000 صورة مقسمة إلى فئتين ذكور وإناث وهي مقتطعة من مجموعة بيانات أكبر تدعى UTKFace4 المتاحة للاستخدامات غير التجارية والتي تتضمن بالأساس ما يزيد عن 20 ألف صورة وجه. تم اختيار الصور التي سوف نستخدمها في عملية التدريب بحيث تكون متوازنة من حيث الجنس (أي أن عدد الصور الخاصة بالذكور يساوي تقريبا عددها للإناث) والعرقيات (أي تقارب عدد الأشخاص من ذوي الأصول الأوروبية والأفريقية والهندية الخ.) وذلك لضمان عدم التحيز، إضافة إلى أن الصور تخص أشخاصا تتراوح أعمارهم ما بين 20 إلى 40 سنة. للمتابعة عليك تحميل الملف المضغوط المرفق مع هذا المحتوى، ثم قم بفك ضغطه على حاسوبك وستحصل على مجلد باسم training داخله ثلاث مجلدات هي images و output وكذلك test. ستلاحظ ضمن مجلد images أن هنالك مجلد فرعي لك تصنيف تريد من شبكتك العصبية أن تتعرف عليه (في حالتنا هذه هناك مجلدان فقط بتسمية Male و Female) داخل كل منهما مجموعة الصور التي تنتمي إلى ذلك التصنيف. من جهة أخرى فإن مجلد output هو فارغ حاليا لكنه المكان الذي سيتم فيه حفظ الشبكة العصبية بعد إتمام عملية تدريبها (أي حيث سنخزن النموذج الناتج)، أخيرا ستجد في المجلد الثالث test شيفرة برمجية بسيطة لاختبار النموذج الناتج وبعض الصور التي لم يسبق له أن رآها من قبل (أي أنها لم تكن موجودة أصلا ضمن صور وبيانات التدريب). نحن بحاجة إلى نقل كل هذه المجلدات وما فيها من ملفات إلى داخل الحاوية hsoub-tf وذلك باستخدام الأمر التالي على الجهاز المضيف: docker cp .\training hsoub-tf:/training الآن نستطيع الانتقال مجددا إلى سطر الأوامر ضمن الحاوية باستخدام الأمر التالي: docker exec -it hsoub-tf bash وبعد ذلك يمكننا بدء عملية التدريب باستخدام الأمر التالي: make_image_classifier \ --image_dir training/images dir \ --tfhub_module https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4 \ --image_size 224 \ --saved_model_dir training/output/model \ --labels_output_file training/output/class_labels.txt \ --tflite_output_file training/output/mobile_model.tflite إن وسطاء الأمر التنفيذي السابق تحدد المسار الذي يحتوي على مجلدات الصور التي سيتم استخدامها في عملية التدريب (أي مسار المجلد ضمن الحاوية وليس على الجهاز المضيف)، وكذلك مصدر النموذج الأصلي الذي ستطبق عليه تقنية نقل التعلم باستبدال طبقته الأخيرة، بعد ذلك نحدد الأبعاد المطلوبة للصور ليتم تحجيمها تلقائيا لتلائم هذه الأبعاد (فهذا محدد ومرتبط بطبقة الإدخال في نموذج الشبكة العصبية المستخدمة)، في حالتنا نحن نستخدم نموذج MobileNet V2 والذي يفترض أبعاد الصورة هي 224 عنصورة/بيكسل للطول والعرض، بعد ذلك نحدد المكان الذي سيتم فيه حفظ النموذج المدرّب الناتج، وكذلك أين سيتم حفظ التسميات المرافقة لعصبونات الخرج من شبكتنا العصبية بالترتيب، وأخيرا أين سيتم تصدير هذا النموذج بصيغة tflite المحزومة والموضبة لتطبيقات الجوال. في نهاية عملية التدريب (والتي قد تطول تبعا لعدد الصور من جهة، ولسرعة الحاسوب الذي تتم عليه عملية التدريب من جهة أخرى)، سنجد أننا حصلنا على نموذج شبكة عصبية يستطيع تمييز جنس الشخص من صورته بدقة تتعدى 80% وذلك بمجهود بسيط جدا دون الحاجة إلى عمليات ضبط ومعايرة فائقة التخصص لوسطاء بناء وتدريب الشبكة العصبية، ودون الحاجة كذلك إلى ساعات وساعات من التدريب ولا إلى آلاف وآلاف من الصورة للتدرب عليها! إنها بحق نتيجة مرضية لأوائل مغامراتك مع الشبكات العصبية، لكنها مجرد البداية. ها قد حان الوقت لتجربة النموذج الذي قمنا بتدريبه على صور لم يسبق له أن رآها من قبل، للقيام بذلك سوف نستخدم برنامج معد مسبقا لهذه الغاية5 وهو مكتوب بلغة Python وموجود داخل المجلد training/test صحبة بضعة صور كأمثلة للتجريب (ويمكن أن تستخدم صورك الخاصة في هذه المرحلة)، حيث ستتم عملية الاختبار لكل صورة من خلال الأمر التالي (وفيه نشير إلى مسار الشبكة العصبية التي سيتم تحميلها واستخدامها، والملف النصي الذي يتضمن اسم التصنيف الخاص بكل عصبون خرج ضمن هذه الشبكة بالترتيب، وأخيرا مسار الصورة المراد تصنيفها): python training/test/label_image.py \ --input_mean 0 --input_std 255 \ --model_file training/output/mobile_model.tflite \ --label_file training/output/class_labels.txt \ --image training/test/Ahmed_Zewail.jpg بعد إتمام تدريبنا واختبارنا للشبكة العصبية حان الوقت للخروج من بيئة التدريب هذه باستخدام الأمر exit للعودة إلى سطر الأوامر على الجهاز المضيف، ومن ثم سنرغب طبعا باستخراج النموذج الذي قمنا بتعليمه خلال هذه الجلسة من داخل حاوية Docker وهو ما نستطيع القيام به باستخدام الأمر التالي: docker cp hsoub-tf:/training/output .\training نقاط تستحق التأمل قد يرى البعض في تقنيات الذكاء الصنعي حلا لجميع المشكلات، لكنها في حقيقة الأمر لا تأتي خالية من المخاطر التي قد ترقى إلى حد اعتبارها نقاط ضعف أو عيوب يجب أن تؤخذ بعين الاعتبار، نذكر منها: باعتبار أن النماذج في الشبكات العصبية تبنى انطلاقا من بيانات التدريب، لذا فإنها غير قادرة على حل أي مشاكل كانت موجودة أصلا في تلك البيانات كالأخطاء أو الانحياز أو عدم التكامل والشمول لمختلف الحالات المراد التعامل معها. يصعب في الشبكات العصبية تفسير المنطق الذي استخدمه النموذج لإعطاء إجابته، فالتعامل معه أقرب ما يكون للصندوق الأسود الذي لا نعلم كمستخدمين آلية عمله الدقيقة في الداخل، بل نؤمن بأن ما نحصل عليه من إجابات هو أفضل المتاح، ولهذه الفكرة تداعياتها الفلسفية والاجتماعية التي تصعّب من استخدام هكذا أدوات في الحالات التي تتطلب تقديم تبرير (مثلا عند تطبيق عقوبة أو حرمان من مكافأة). تطوير نماذج الشبكات العصبية يتطلب قدرا كبيرا من بيانات التدريب المشروحة والمجهزة بشكل جيد وملائم، وهو ما قد يعني الكثير من الجهد لتوفير مثل هكذا مصادر للتدريب. عملية تدريب الشبكات العصبية هي عملية متطلّبة من ناحية قدرات المعالجة الحسابية المتوفّرة للعتاد المستخدم، وذلك على عكس عملية استخدامها في التطبيقات عقب إتمام التدريب، لذا قد يجد المطور في الحوسبة السحابية فرصا متاحة بميزانيات في المتناول. إن بناء النماذج باستخدام خوارزميات تعلم الآلة يتطلب مطورين أذكياء وموهوبين، فعلى الرغم من أن تطبيق المثال الذي قمنا بعرضه يبدو سهلا ويسيرا، إلا أن نظرة أكثر تعمقا ستكشف لك الكثير من التفاصيل التي بحاجة إلى معايرة وضبط، منها على سبيل المثال لا الحصر: معمارية الشبكة، وطريقة توصيف تابع تفعيل العصبونات فيها، وعدد طبقاتها الخفية، وعدد العصبونات في كل منها، وطريقة ربطها بعضها ببعض، وكيفية حساب الخطأ ما بين النتيجة المحسوبة والمطلوبة، ومعدل سرعة التعلم، وعدد مرات تكرار عرض الأمثلة على الشبكة العصبية أثناء التدريب، وغيرها الكثير. إن انتقاء التوليفة الأكثر ملائمة لجملة معايير الضبط هذه يؤثر بشكل حاسم على جودة ودقة النموذج الناتج. مراجع أجنبية للاستزادة MobileNetV2: The Next Generation of On-Device Computer Vision Networks DeCAF: A Deep Convolutional Activation Feature for Generic Visual Recognition Making your own TensorFlow model for image classification UTKFace: Large Scale Face Dataset TensorFlow Lite Python image classification demo اقرأ أيضًا تعلم الذكاء الاصطناعي الذكاء الاصطناعي: دليلك الشامل المقال التالي: إعداد شبكة عصبية صنعية وتدريبها للتعرف على الوجوه الذكاء الاصطناعي: مراحل البدء والتطور والأسس التي نشأ عليها
-
- 2
-
- تعرف على الوجوه
- شبكة عصبية
-
(و 2 أكثر)
موسوم في:
-
بنية الشبكات العصبية الصنعية تتكون الشبكات العصبية الصنعية من عناصر أولية بسيطة من حيث طريقتها في معالجة البيانات تدعى العصبونات، ويمكن تمثيل أي من تلك العصبونات رياضيا -وفق مفاهيم البرمجة غرضية التوجه- على شكل كائن برمجي Object يتضمن كل نسخة (Instance) منه على مصفوفة من أوزان الدخل يساوي حجمها عدد إشارات الدخل الواردة لهذا العصبون والتي قد تتراوح ما بين العشرات والآلاف تبعا لبنية الشبكة العصبية وهيكليتها. إن معالجة العصبون الواحد للإشارات الواردة بسيطة من الناحية الرياضية، حيث يتم ضرب كل إشارة دخل بقيمة الوزن المقابل لها ومن ثم تجمع كافة النواتج لنحصل على مقدار كلي يمثل درجة استثارة العصبون أو شحنته الإجمالية، ومن ثم يرسل هذا الرقم إلى تابع التفعيل، وهو ببساطة علاقة رياضية تحاكي إلى حد ما سلوك العصبونات الحقيقية فيما يخص قانون الكل أو لاشيء مع بعض التصرف لاعتبارات تتعلق بخوارزميات التعليم المستخدمة (إمكانية حساب المشتق الرياضي كما في حالة تابع Sigmoid) أو لاعتبارات براغماتية عملية (سهولة البرمجة وسرعة التنفيذ كما في حالة التابع ReLU) أو حتى لاعتبارات تتعلق بطبيعة البيانات المعالجة (كشيوع استخدام التابع TanH عند التعامل مع بيانات الفئات والأصناف). في نهاية المطاف تستخدم قيمة الاستثارة الإجمالية للعصبون كدخل لتابع التفعيل x هذا، وبتطبيق العلاقة الرياضية الخاصة بتابع التفعيل المحدد للعصبون نحسب قيمة y المقابلة والتي تمثل الآن خرج هذا العصبون. تتألف الشبكات العصبية من بضع عشرات أو مئات أو حتى الآلاف من تلك الوحدات البنائية الأساسية والتي تنتظم في معمارية على شكل طبقات، حيث يحدد لكل طبقة عدد العصبونات التي تتضمنها، ونوع تابع التفعيل المستخدم لعصبونات تلك الطبقة (أي أن تابع التفعيل قد يختلف من طبقة إلى أخرى لكنه عادة ما يكون ذاته لجميع عصبونات الطبقة الواحدة)، وطريقة ارتباطها بالطبقة التي تسبقها والتي تليها (هل هو ربط كامل كأن تصل إشارة دخل واردة من كل عصبون موجود في الطبقة السابقة، أم هو جزئي يأخذ دخله من عصبونات الطبقة السابقة المجاورة لموضعه الحيزي كما هو حال الشبكات العصبية الصنعية التي تحاكي الباحات البصرية في الدماغ بغية التعرف على الأنماط في الصور). الفرق ما بين تدريب واستخدام الشبكات العصبية الصنعية إن تدريب الشبكات العصبية الصنعية يقصد به إيجاد القيم الأنسب لكافة أوزان الدخل لعصبونات الشبكة بحيث تكون قادرة على إعطاء الإجابات الصحيحة بأقل هامش خطأ في مخرجاتها وذلك للمدخلات المعروضة عليها، في حين يقع على عاتق المطور فن تحديد المعمارية الملائمة من حيث عدد الطبقات ونوع تابع تفعيل كل منها وعدد عصبوناته وطريقة ربطها مع بعضها البعض. قد تكون مهمة تدريب الشبكات العصبية الصنعية تحديا حسابيا هائلا تتطلب كما كبيرا من المعالجات الرياضية والتي قد تستغرق زمنا طويلا حتى لو أجريت على حواسيب فائقة، لكن بمجرد الوصول إلى الحل وإيجاد قيم الأوزان الملائمة التي تعطي نتائج مرضية عند معالجتها للبيانات، تصبح مسألة الاستخدام والتطبيق في منتهى البساطة، فكل ما عليك القيام به هو تمرير وحيد عبر طبقات الشبكة يتم خلاله إجراء بضع عمليات ضرب وجمع ومن ثم تطبيق تابع رياضي ما (تابع التفعيل)، وهكذا تمرر القيم من طبقة إلى أخرى وصولا إلى طبقة الخرج النهائية لتظهر من خلالها الإجابة. كيفية حفظ وتصدير الشبكات العصبية الصنعية بعد إتمام عملية تدريب الشبكة العصبية الصنعية والرضى عن نتائجها، كل ما نحتاج إليه هو حفظ بنيتها من حيث عدد الطبقات، وعدد العصبونات في كل طبقة، وطريقة ارتباط عصبونات الطبقات المختلفة بعضها ببعض، وكذلك تابع التفعيل المستخدم في كل طبقة، إضافة إلى قيم الأوزان لكل عصبون على حدة. هذا كل مافي الأمر! فإن حصلت على هذه الأرقام مجددا فأنت قادر على إعادة بناء واستخدام تلك الشبكة العصبية الصنعية بشكلها النهائي بعد أن اكتملت عملية تدريبها. عادة ما تقوم مكتبة TensorFlow بحفظ نموذج الشبكة العصبية الصنعية بكافة وسطائها وقيمها ضمن مجلد خاص بذلك خلال عملية التدريب والذي يمكن قراءته واستعادة بنية شبكته العصبية باستخدام لغة Python (ستجد ضمنه ملف بامتداد *.pb إضافة إلى مجلدين فرعيين هما variables وassets)، لكن هذه الصيغة من الحفظ ملائمة فقط لبيئة التطوير ولا تصلح للتطبيق العملي. عوضا عن ذلك أتاحت Google مجموعة من الصيغ المعيارية المناسبة للتطبيقات المختلفة من أهمها صيغة *.tfjs الموجهة للمبرمجين بلغة JavaScript ذائعة الصيت وواسعة الانتشار في عالم الويب سواء باستخدامها على طرف المستعرض أو حتى على طرف المخدم من خلال تقنية Node.js، كذلك لدينا صيغة *.tflite والمصممة أصلا لتعمل بموارد محدودة من سرعة معالجة ومساحة ذاكرة وذلك بهدف استخدامها على أجهزة الجوال أو إنترنت الأشياء. لا يقتصر الأمر على ذلك فحسب، لكن حزم الشبكات العصبية الصنعية وإعدادها للاستخدام العملي قد يتضمن بعضا من المقايضة بهدف تصغير حجمها وزيادة سرعة عملها ولو كان ذلك على حساب انخفاض طفيف ومقبول في دقة نتائجها، إحدى تلك الخوارزميات على سبيل المثال تقوم بإزالة الروابط التي تقترب قيم أوزانها من الصفر باعتبارها لا تساهم بشكل كبير في مقدار الاستثارة الكلية للعصبون المرتبطة به، في المقابل نكون قد خفضنا عدد الأوزان التي نحن بحاجة إلى حفظها وكذلك قللنا الزمن الكلي اللازم للحساب كون عدد المدخلات أصبح أقل. مكتبة Face-API كما هو واضح من المقدمة السابقة، فنحن لن نقوم هنا ببناء وتدريب شبكة عصبية صنعية من الصفر، لكننا في المقابل سوف نستعرض كيفية استخدام شبكة عصبية صنعية سبق وأن تم تدريبها وذلك ضمن تطبيقنا الخاص. لهذه الغاية أود تعريفكم بمكتبة face-api.js والتي تم بناؤها لأغراض التعرف على الوجوه وتمييزها ضمن بيئة المتصفح إنطلاقا من مكتبة TensorFlow.js، وهي بذلك تقدم مجموعة من نماذج الشبكات العصبية الصنعية المدربة والجاهزة لطيف متنوع من التطبيقات يمكنك الإطلاع عليها من خلال النقر على الرابط التالي لصفحة التوثيق الخاص بهذه المكتبة: https://justadudewhohacks.github.io/face-api.js/docs/ إن المثال الذي سوف نبنيه فيما يلي سيقوم باستخدام كاميرا حاسوبك أو جوالك من خلال صفحة ويب ستقوم ببرمجتها بنفسك لكي يأخذ صورة المستخدم ويقدّر منها عمره وجنسه. تخيل لو أننا نتحدث عن صفحة تسجيل مستخدم جديد في تطبيقك المستقبلي، سيكون من اللطيف والذكي في آن معا لو استطاع التطبيق تحديد القيم الافتراضية للعمر والجنس بناء على صورة المستخدم بحيث تكون قريبة إلى الواقع بطريقة تحسن من تجربة المستخدم. تم بناء نموذج التعرف على العمر والجنس باستخدام شبكة عصبية صنعية متعددة المهام توظف فيها طبقة استخلاص سمات الوجه لتقدير العمر وكذلك لتصنيف الجنس، يبلغ حجم حزمة النموذج بالإجمال قرابة 420kb، وقد تم تدريبها واختبارها باستخدام قواعد بيانات متعددة لصور الوجوه الموسومة بالعمر والجنس الحقيقيين (منها على سبيل المثال موسوعة Wikipedia وقاعدة IMDB للأفلام والممثلين)، وتبلغ دقتها 95% في تحديد الجنس، وهامش خطئها في تحديد العمر يقارب 4 سنوات ونصف. استخدام الشبكات العصبية في نافذة متصفحك لنقم بداية بإنشاء مجلد جديد وليكن اسمه face-api للمشروع الذي سنعمل عليه اليوم ضمن مخدم الويب المحلي الذي لديك لتجربة تطبيقات الويب التي تعمل عليها (أي ضمن المجلد C:\xampp\htdocs على سبيل المثال في حال كنت تستخدم مخدم XAMPP)، سنضيف داخل ذلك المجلد الرئيسي مجلدين فرعيين باسم js وكذلك models، ومن ثم نقوم بنسخ الملفات التي نحتاجها من مكتبة Face-API الأصلية على الشكل التالي: نُنزِّل الصيغة المصغرة/المضغوطة من المكتبة ذاتها (أي الملف "face-api.min.js") ونسخها إلى داخل المجلد الفرعي js في مشروعنا، يمكن الحصول على ذلك الملف من العنوان التالي: https://github.com/justadudewhohacks/face-api.js/tree/master/dist نُنَزِّل نماذج الشبكات العصبية الصنعية التي سنستخدمها، حيث يوجد ملفين لكل نموذج شبكة عصبية أحدهما ينتهي إسمه بكلمة "shard1" تحفظ فيه قيم أوزان الشبكة، أما الآخر فهو ملف بصيغة json يصف بنية الشبكة حتى يتم قراءة وتحميل قيم الأوزان السابقة بشكل سليم. في مثالنا الحالي سنحتاج إلى استخدام شبكتين عصبيتين هما "age_gender_model" و "tiny_face_detector_model"، أي أننا بحاجة إلى تنزيل أربع ملفات ومن ثم نسخها إلى داخل مجلد models في مشروعنا. يمكنك الحصول على تلك الملفات من العنوان التالي: https://github.com/justadudewhohacks/face-api.js/tree/master/weights بعد ذلك سنُنشِئ ملفين جديدين أحدهما سندعوه index.html سنضعه في المجلد الرئيسي للمشروع، أما الآخر فسندعوه main.js ونضعه ضمن المجلد الفرعي js المخصص لشيفرات لغة JavaScript البرمجية. سنقوم بداية بتنقيح محتوى صفحة HTML والتي سنجعلها بأبسط شكل ممكن بغرض التركيز على العناصر الأساسية التي نهتم بها: <html> <head> <title>Face-API Example</title> </head> <body> </body> </html> ضمن قسم body سنقوم بإضافة عنصر div فيه عنصر video لعرض الفيديو الذي تلتقطه كاميرا حاسوبك ندعوه webcam، ويتم ضبطها لتعمل تلقائيا ضمن النمط الصامت بمجرد تحميل الصفحة: <div class="container"> <video id="webcam" height="360" width="360" autoplay muted></video> </div> ثم نضيف حقل إدخال input مخصص للعمر ندعوه age: Age (years): <input id="age" size="3"><br /> بعد ذلك نضيف مجموعة أزرار اختيار من نوع radio ندعوها gender فيها اختيارين اثنين هما male وfemale: <input id="male" type="radio" name="gender" value="male">Male<br /> <input id="female" type="radio" name="gender" value="female">Female<br /> أخيرا وبعد اكتمال تحميل وعرض كافة عناصر الصفحة، نقوم بتضمين ملفات جافاسكريبت في نهاية محتويات الصفحة حتى نكون متأكدين من وصولها برمجيا لكافة العناصر ضمن تلك الصفحة: <script src="js/face-api.min.js"></script> <script src="js/main.js"></script> ملاحظة هامة: بسبب إعدادات الأمان فإن معظم متصفحات الويب تعرض رسالة للمستخدم تبيّن رغبة الصفحة في الوصول إلى الكاميرا وتطلب الإذن للسماح لها بذلك. هذا كل ما هو مطلوب فعليا ضمن صفحة HTML الخاصة بهذا المثال، أما الآن فعلينا الانتقال إلى لكتابة شيفرات لغة جافاسكريبت التي ستقوم بتحميل نموذج الشبكة العصبية الصنعية، وتمرير صور الكاميرا إليه، ومن ثم قراءة الخرج الناتج عن الشبكة العصبية الصنعية وعرضه ضمن عناصر الإدخال المقابلة في صفحة HTML السابقة. لذلك سنبدأ بكتابة محتوى الملف main.js من الشيفرات البرمجية مع شرح مختصر لكل قسم منها، فالسطر الأول يقوم بتعيين اسم الثابت video للإشارة إلى العنصر المدعوة webcam ضمن صفحة HTML السابقة: const video = document.getElementById("webcam"); بعد ذلك نقوم بتحميل نموذجي الشبكتين العصبيتين الخاصتين بالتعرف على الوجوه ضمن الصورة وتحديد الجنس وتقدير العمر من خلال سمات تلك الوجوه وذلك باستخدام آلية الوعود Promise.all، ومن بعد إتمام ذلك يتم استدعاء التابع startVideo الذي سنقوم بتعريفه لاحقا: Promise.all([ faceapi.nets.tinyFaceDetector.loadFromUri("models"), faceapi.nets.ageGenderNet.loadFromUri("models") ]).then(startVideo); يقوم هذا التابع بقراءة دفق إشارة الفيديو من الكاميرا وعرضها ضمن الكائن video، فإن حدث خطأ يتم كتابة رسالة الخطأ ضمن console.error في المستعرض: function startVideo() { navigator.getUserMedia( { video: {} }, stream => (video.srcObject = stream), err => console.error(err) ); } ومن ثم نضيف حدث يقوم بالإنصات لما يتم عرضه ضمن عنصر video بحيث يستدعي بشكل غير متزامن async (أي دون الحاجة لانتظار الإجابة حتى يتابع عرض الفيديو) وذلك كل ثانية (حيث تم ضبط الفترة الزمنية setInterval لتكون قيمتها 1000 ميلي ثانية). يقوم هذا الاستدعاء الدوري المتكرر بتعيين قيمة الكائن detection بواسطة مكتبة faceapi حيث تحدد جميع الوجوه الموجودة ضمن كائن video بالاستعانة بنموذج TinyFaceDetector مستخدمين الإعدادات الافتراضية، ومن ثم يمرر الناتج (أي الوجوه التي تم التعرف عليها في الصورة)، إلى الشبكة العصبية الأخرى التي تتعرف على العمر والجنس: video.addEventListener("playing", () => { setInterval(async () => { const detections = await faceapi .detectAllFaces(video, new faceapi.TinyFaceDetectorOptions()) .withAgeAndGender(); إن العبارة الشرطية التالية تتأكد إن كان الكائن detections موجودا أصلا (تذكر أن تعيين قيمة هذا الكائن تتم من خلال استدعاء غير متزامن وبالتالي يمكن أن تكون القيمة غير موجودة في البداية ريثما تصل إجابة ذلك الاستدعاء). ليس هذا فحسب، بل يجب أن يتضمن على الأقل وجه واحد على الأقل. فإن تحقق هذين الشرطين يتم قراءة الجنس المتوقع للوجه الأول detections[0].gender وتعيينه لعنصر الإدخال المقابل له، وكذلك قراءة العمر المتوقع لذات الوجه الأول detections[0].age وتقريبه إلى أقرب عدد صحيح باستخدام التابع Math.round ومن ثم إسناده كقيمة لعنصر الإدخال المدعو age ضمن صفحة HTML السابقة: if (detections && Object.keys(detections).length > 0) { document.getElementById(detections[0].gender).checked = true; document.getElementById("age").value = Math.round(detections[0].age); } }, 1000); }); هذا كل شيء! تستطيع الآن تجربة هذا التطبيق البسيط من خلال عرض الصفحة على نافذة المستعرض مستخدمين عنوانها على مخدم الويب المحلي وليكن على سبيل المثال: http://localhost/face-api نقاط تستحق التأمل من منا لا تغريه كم الإسقاطات التي تطرحها تقنية الشبكات العصبية الصنعية على مفهوم البرمجة ككل؟ إنها بذور جيل جديد مختلف جذريا عن كل ما سبق وأن اعتدنا عليه، فرغم أنها تعتمد على وحدات بسيطة في صميمها هي العصبونات، والتي قمنا بوصف آلية عملها بطريقة خوارزمية محددة تماما وواضحة، إلا أننا مع نمو شبكاتنا في الحجم والتعقيد استبدلنا الثقة بالأمل، والتحليل بالتجريب. لنأخذ على سبيل المثال نموذج اللغة العربية، فحتى نستطيع تشكيل أي جملة نستخدم علم النحو والإعراب الذي تعلمناه في مدارسنا، ومن خلال قواعده نحدد بناء الجملة وعناصرها كالفعل والفاعل أو المبتدأ والخبر وسواها من القواعد التي تساعدنا على ضبط أواخر الكلمات. هذه هي تماما الطريقة التي نبرمج بها حواسيبنا حاليا (منذ أيام تشارلز بابيج وحاسوبه الميكانيكي قبل مئتي عام)، فنحن نضع القواعد الصارمة لتدفق البيانات ونترجمها إلى عبارات شرطية وحلقات وسواها من بنى التحكم في عدد منتهي من الخطوات ندعوها بالخوارزمية. لكن لحظة، هل هذه هي الطريقة الصحيحة فعلا التي تجري بها الأمور؟ هل هذا ما نفعله حقيقة عندما يلتبس علينا تشكيل كلمة ما في نص نقرأه أو جملة نتحدثها؟ أغلب الظن ستكون إجابتك هي النفي! فمعظمنا لا يعرب الجملة في رأسه ليضبط أواخر الكلمات، بل نجرب الاحتمالات المختلفة ونرى أيها يملك الوقع الأكثر تجانسا وانسجاما في نفسنا (على سبيل المثال: هل أباك/أبوك/أبيك موجود؟). إنها ببساطة ما ندعوه بالسليقة اللغوية، وهي التي تفسر قدرة الطفل الصغير على التكلم بلغة صحيحة حتى قبل معرفته بوجود علم النحو والإعراب أصلا، فهو يتعلم مما يسمع وينسج على منواله، فكلما كانت الأمثلة كثيرة وصحيحة ازداد اتقانه للغة (ولهذا السبب كان العرب يرسلون أبنائهم إلى البادية لسلامة اللغة من اللحن الأعجمي هناك). إن التعلم من خلال الأمثلة هو ما تقوم به تماما الشبكات العصبية الصنعية التي نقوم بتطويرها، إنها تقوم ببناء خبرتها وسليقتها وفراستها الخاصة، في نهاية المطاف يمتلك كل من المبتدئ والخبير القواعد ذاتها، لكن عين الخبير رأت الكثير من الأمثلة التي صقلت بها خبرتها، حتى وصلت من التعقيد إلى درجة قد يصعب معها التفسير رغم قوة الإحساس ووضوحه في ذهنه. وهكذا ينتقل دور المبرمج رويدا رويدا من وضع القواعد وبنى التحكم لبرمجياته إلى ما بات يدعى اليوم بعلم البيانات وهندسة عملية استخلاص المزايا والخصائص المؤثرة في عملية التعلم من تلك البيانات، حيث أن دقة وجودة النموذج الناتج تعتمد بشكل حاسم على تكامل أمثلة التعليم وشمولها وصحتها، وأصبح امتلاك مثل هكذا مكانز رقمية موصّفة بشكل دقيق ومبنية بطريقة هيكلية سهلة الولوج والاستعلام يعادل قطع ما يتجاوز نصف الطريق إلى الوصول لعالم الذكاء الصنعي التطبيقي وقطف ثماره. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن مراجع للاستزادة مكتبة TensorFlow مكتبة face-api.js Model optimization اقرأ أيضًا المقال السابق: تصنيف الصور والتعرف على الوجه في مجال الذكاء الاصطناعي المفاهيم الأساسية لتعلم الآلة