اذهب إلى المحتوى

البحث في الموقع

المحتوى عن 'تعلم الآلة'.

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المحتوى


التصنيفات

  • الإدارة والقيادة
  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • السلوك التنظيمي في المؤسسات
  • عالم الأعمال
  • التجارة والتجارة الإلكترونية
  • نصائح وإرشادات
  • مقالات ريادة أعمال عامة

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • PHP
    • Laravel
    • ووردبريس
  • جافاسكربت
    • لغة TypeScript
    • Node.js
    • React
    • Vue.js
    • Angular
    • jQuery
    • Cordova
  • HTML
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • لغة C#‎
    • ‎.NET
    • منصة Xamarin
  • لغة C++‎
  • لغة C
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • لغة Rust
  • برمجة أندرويد
  • لغة R
  • الذكاء الاصطناعي
  • صناعة الألعاب
  • سير العمل
    • Git
  • الأنظمة والأنظمة المدمجة

التصنيفات

  • تصميم تجربة المستخدم UX
  • تصميم واجهة المستخدم UI
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب GIMP
    • كريتا Krita
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • مقالات DevOps عامة
  • خوادم
    • الويب HTTP
    • البريد الإلكتروني
    • قواعد البيانات
    • DNS
    • Samba
  • الحوسبة السحابية
    • Docker
  • إدارة الإعدادات والنشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
    • ريدهات (Red Hat)
  • خواديم ويندوز
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • شبكات
    • سيسكو (Cisco)

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح
  • مبادئ علم التسويق

التصنيفات

  • مقالات عمل حر عامة
  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • العمل الحر المهني
    • العمل بالترجمة
    • العمل كمساعد افتراضي
    • العمل بكتابة المحتوى

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
    • بريستاشوب
    • أوبن كارت
    • دروبال
  • الترجمة بمساعدة الحاسوب
    • omegaT
    • memoQ
    • Trados
    • Memsource
  • برامج تخطيط موارد المؤسسات ERP
    • تطبيقات أودو odoo
  • أنظمة تشغيل الحواسيب والهواتف
    • ويندوز
    • لينكس
  • مقالات عامة

التصنيفات

  • آخر التحديثات

أسئلة وأجوبة

  • الأقسام
    • أسئلة البرمجة
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات

التصنيفات

  • كتب ريادة الأعمال
  • كتب العمل الحر
  • كتب تسويق ومبيعات
  • كتب برمجة
  • كتب تصميم
  • كتب DevOps

ابحث في

ابحث عن


تاريخ الإنشاء

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


رشح النتائج حسب

تاريخ الانضمام

  • بداية

    نهاية


المجموعة


النبذة الشخصية

تم العثور على 9 نتائج

  1. تعلم الآلة machine learning هو أحد الدعائم الأساسية لتكنولوجيا المعلومات على مدار العقدين الماضيين، وبات اليوم جزءًا أساسيًا من حياتنا اليومية. فمع توفر كميات متزايدة من البيانات أصبح هناك سببٌ وجيهٌ للاعتقاد بأن التحليل الذكي للبيانات سيكون أكثر انتشارًا وأهميةً كمكون أساسي للتقدم التكنولوجي. في المقالة السابقة قدمنا دليلًا شاملًا لبدء تعلم الذكاء الاصطناعي وفي هذه المقالة والتي تعتبر جزءًا من سلسلة مقالات عن الذكاء الصناعي بدأناها مع مقال مقدمة شاملة عن الذكاء الاصطناعي نُضيء لك الطريق ونقدم لك دليلًا شاملًا للبدء بتعلم وفهم تعلم الآلة والتعرف على أهم المصطلحات المرتبطة بهذا التخصص. ملاحظة: إن لم يكن لديك معرفة مسبقة بمجال الذكاء الصناعي وفروعه وأساسياته، سيكون من الأفضل أن تقرأ مقال "مقدمة شاملة عن الذكاء" من هذه السلسلة على الأقل التي أشرت إليها للتو. ما المقصود بتعلم الآلة؟ تعلم الآلة Machine learning أو ما يعرف اختصارًا ML هو مصطلح تقني يعني استخدام مجموعة من التقنيات والأدوات التي تساعد أجهزة الحاسوب والآلات الذكية عمومًا على التعلم والتكيف من تلقاء نفسها. لقد نشأ علم التعلم الآلي عندما بدأ علماء الحاسوب بطرح الأسئلة التالية: نحن البشر نتعلم من التجارب السابقة أما الآلات فهي تفعل ما نمليه عليها فقط فهل يمكن أن نتمكن من تدريب هذه الآلات كي تتعلم من البيانات والخبرات السابقة وتحاكي طريقة تفكيرنا وتتمكن من التعلم والفهم والاستنتاج دون تدخلنا؟ هل يمكن للحواسيب أن تفعل ما نفعله وبالطريقة التي نريدها، وأن تتعلم من تلقاء نفسه كيفية أداء مهمة محددة؟ هل يمكن للحواسيب والآلات أن تفاجئنا وتتعلم من خلال البيانات من تلقاء نفسه بدلًا من قيام المبرمجين بصياغة قواعد معالجة البيانات لها بشكل يدوي؟ كل هذه التساؤلات فتحت الباب أمام نموذج برمجة بديل عن أسلوب البرمجة الكلاسيكية التي يُدخل فيها البشر القواعد ضمن برامج حاسوبية ويحددون بدقة البيانات التي يجب معالجتها وفقًا لهذه القواعد ويكون الخرج إجابات محددة ناتجة عن عمليات المعالجة. في نموذج البرمجة الجديد هذا الذي سمي تعلم الآلة أصبح بمقدور البشر إدخال البيانات للحاسوب بالإضافة إلى الإجابات المتوقعة وفقًا للبيانات، ويكون الخرج هو القواعد التي تم استنتاجها بشكل برنامج أو ما يُسمى بالنموذج model ثم يمكن بعد ذلك تطبيق هذه القواعد على البيانات الجديدة لإنتاج الإجابات. بمعنى آخر نظام التعلم الآلي يُدرّب بدلًا من برمجته صراحةً، حيث تقدّم له العديد من عينات البيانات المتعلقة بالمهمة المطلوبة، ليكتشف بنية إحصائية في هذه العينات تربط المدخلات بالمخرجات (البيانات بالإجابات) وتسمح في النهاية للنظام بالوصول إلى قواعد لأتمتة المهمة. على سبيل المثال إذا عرضنا على الحاسوب مجموعات ثنائية من الأرقام كالتالي (2 , 4) و (3 , 6) و (4 , 9) ثم طلبنا منه بناء على هذه المعطيات أن يتنبأ بالرقم الذي يجب أن يأتي مع الرقم 5 فسوف يحتاج في بداية الأمر إلى إيجاد المنطق بين مجموعات البيانات التي مررناها له وتطبيق نفس المنطق للتنبؤ بالرقم الجديد، عملية العثور على هذا المنطق يسمى تعلم الآلة وبعد إيجاد هذا المنطق سنتمكن من تطبيقه للتنبؤ بكل رقم جديد. و إذا كنت ترغب بأتمتة عملية تخمين أسعار المنازل -يمكنك إعطاء نظام التعلم الآلي مجموعة من العينات التي تمثّل مواصفات المنازل (بيانات) مع أسعارها (إجابات)، وسيتعلم النظام القواعد الإحصائية اللازمة لربط المواصفات بالأسعار. على الرغم من أن التعلم الآلي بدأ في الازدهار بدءًا من التسعينيات، إلا أنه سرعان ما أصبح المجال الفرعي الأكثر شعبية والأكثر نجاحًا في الذكاء الصناعي. هل ترغب في استخدام ذكاء الآلة في أعمالك؟ وظّف مهندس ذكاء اصطناعي خبير لتحقيق أهدافك عبر مستقل أضف مشروعك الآن تعريف تعلم الآلة؟ هناك العديد من التعاريف الخاصة بتعلم الآلة نذكر منها أهم تعريفين: تعريف آرثر صامويل 1959: "تعلم الآلة هو المجال الدراسي الذي يمنح أجهزة الحاسب القدرة على التعلم دون برمجتها صراحةً من قبل البشر" تعريف توم ميتشل: "تعلم الآلة هو دراسة خوارزميات الحاسوب التي تحسن أدائها تلقائيًا من خلال التجربة ويقال أَنَّ برنامجا حاسوبيًا يتعلَّم من الخبرة E التي تخص بمجموعة من المهام T بالنسبة إِلى مقياس الأداء P إذا تحسن أداءه على إنجاز المهام T بعد اكتساب الخبرة E بالمقدار P" على سبيل المثال في مجال لعب الشطرنج تكون الخبرة E هي عدد مرات اللعب ضد الحاسب و T هي مهمة لعب الشطرنج ضد الحاسب والمعيار P هو فوز/خسارة الحاسب. ويمكننا القول بتعريف بسيط أن تعلم الآلة مجال من مجالات الذكاء الصناعي يحاول بناء آلات قادرة على التعلّم من تلقاء نفسها. الفرق بين الذكاء الاصطناعي وتعلم الآلة يميل معظم الناس إلى استخدام مصطلحي الذكاء الاصطناعي وتعلم الآلة كمرادفين ولا يعرفون الفرق، إلا أن هذين المصطلحين هما في الواقع مفهومان مختلفان، فتعلم الآلة هو في الواقع جزء من الذكاء الاصطناعي أو أحد أنواع الذكاء الاصطناعي ويمكن القول أن الذكاء الاصطناعي هو مجال واسع من الموضوعات، ويشكل تعلم الآلة جزء صغير من هذه الموضوعات. يشير الذكاء الاصطناعي إلى القدرة العامة لأجهزة الحاسب على محاكاة الفكر البشري وأداء المهام في بيئات العالم الحقيقي، بينما يشير التعلم الآلي إلى التقنيات والخوارزميات التي تمكّن الأنظمة من تحديد طريقة لتعلّم الأنماط المخفيّة في البيانات واتخاذ القرارات وتحسين نفسها من خلال التجربة والبيانات. أهمية تعلم الآلة قد تتساءل عن أهمية مجال تعلم الآلة في عصرنا الحالي وللإجابة على هذا السؤال يمكننا تقسيم الإجابة إلى شقين الأول أهمية تعلم الآلة بالنسبة للمؤسسات وتطور الحياة عمومًا، والثاني أهميته بالنسبة لبقية فروع الذكاء الصناعي و الفروع ذات الصلة. بالنسبة للشق الأول، فالبيانات هي شريان الحياة لجميع قطاعات الأعمال وقد باتت القرارات التي تعتمد على البيانات تصنع الفارق وتشكل الفصل بين مواكبة المنافسة أو التخلف عن الركب. التعلم الآلي هو مفتاح إطلاق العنان لقيمة بيانات الشركات والعملاء واتخاذ القرارات التي تجعل الشركة في صدارة المنافسة. إذًا يعتبر التعلم الآلي مهمًا لأنه يمنح المؤسسات بمختلف أنواعها وجهة نظر حول اتجاهات وسلوك العملاء وأنماط تشغيل الأعمال فضلًا عن دعم عملية تطوير منتجات جديدة كما أنها تساعد في أمور أخرى مثل اكتشاف الأمراض وتوقعات الطقس …إلخ. وتستخدم العديد من الشركات الرائدة اليوم مثل فيسبوك Facebook وجوجل Google وأوبر Uber التعلم الآلي كجزء أساسي من عملياتها. بالنسبة للشق الثاني، ففروع الذكاء الصناعي الأخرى مثل معالجة اللغات الطبيعية أو الفروع ذات الصلة كالرؤية الحاسوبية لم تبصر النور إلا بعد التطورات الأخيرة التي شهدها تعلم الآلة الذي شكَّل ركيزة أساسية في تطور معظم فروع الذكاء الصناعي. على سبيل المثال تعد تطبيقات التعرف على الكائنات أو تتبعها أو تصنيف الأشياء قضايا شائعة في الرؤية الحاسوبية، وهي تعتمد بشكل شبه مطلق على خوارزميات التعلم العميق والأمر ذاته ينطبق على معالجة اللغات الطبيعية. البيانات وتعلم الآلة البيانات هي وقود التعلم الآلي، فبدون البيانات ستكون خوارزميات التعلم الآلي جائعة وضعيفة الأداء وستفشل في حل أي مشكلة بشكل صحيح. وبعض خوارزميات التعلم الآلي مثل خوارزميات التعلم العميق لا تشبع، وكلما زودتها بالبيانات ونوّعتها لها كلما ازدادت قوتها وقدراتها وتحسن أداؤها. وتجدر الإشارة هنا لأن الأمر لا يتعلق بزيادة حجم البيانات وكميتها فقط بل يتعلق كذلك بدقة البيانات وجودتها وتنوع حالات استخدامها للحصول على نتائج صحيحة ودقيقة. وفيما يلي نوضح أهم النقاط الأساسية المتعلقة بالبيانات والمرتبطة بالتعلم الآلي: هناك نوعان أساسيان للبيانات المستخدمة في التعلم الآلي: بيانات مُسماة labeled وغير مسماة unlabeled على سبيل المثال إذا كان المطلوب هو التنبؤ بعمر شخص ما فإن العمر هنا يعد من البيانات المسماة في حين أن البيانات غير المسماة لا تحتوي على أي سمة مميزة. تسمى خوارزميات تعلم الآلة التي تتعامل مع مجموعة بيانات مسماة خوارزميات التعلم الخاضع للإشراف في حين تسمى خوارزميات تعلم الآلة التي تستخدم مجموعة بيانات غير مسماة خوارزميات التعلم غير الخاضع للإشراف وسنشرح المزيد عن هذه الأنواع من التعلم الآلي في فقرة لاحقة. عادةً ما تكون البيانات المستخدمة في التعلم الآلي عددية Numerical أو فئوية Categorical. تتضمن البيانات العددية القيم التي يمكن ترتيبها وقياسها مثل العمر أو الدخل، وتتضمن البيانات الفئوية القيم التي تمثل الفئات، مثل الجنس أو نوع الفاكهة. يمكن تقسيم البيانات إلى مجموعات تدريب Training ومجموعات مراقبة أو تحقق Validation ومجموعات اختبار Testing حيث تُستخدم مجموعة بيانات التدريب لتدريب النموذج، وتُستخدم مجموعة المراقبة لمراقبة سير عملية التدريب وتعلّم النموذج وأخيرًا تُستخدم مجموعة الاختبار للتقييم النهائي لأداء النموذج. تعد المعالجة المسبقة للبيانات خطوة مهمة في تعلم الآلة حيث يتم من خلالها تنظيف البيانات ومعالجة القيم المفقودة والشاذة وهندسة الميزات. هناك فرق بين البيانات والمعلومات والمعرفة، فالبيانات يمكن أن تكون أي حقيقة أو قيمة أو نص أو صوت أو صورة غير معالجة لم تُفسّر أو تحلل أما المعلومات فهي البيانات بعد تفسيرها ومعالجتها. أي أنها شكل متطور للبيانات ذو فائدة ومعنى أكبر والمعرفة هي المرحلة الأكثر تطورًا والمتمثلة بالوعي والفهم والإدراك للمعلومات. تطبيقات تعلم الآلة للتعلم الآلي تطبيقات في جميع أنواع الصناعات وفي حياتنا اليومية عمومًا، يتضمن ذلك التصنيع والتجارة بالتجزئة والرعاية الصحية وعلوم الحياة والسفر والضيافة والخدمات المالية والطاقة والمواد الأولية والمرافق. بما أن تعلم الآلة يندرج تحت مظلة الذكاء الاصطناعي، فإن تطبيقات تعلم الآلة تدخل ضمن أي تطبيق ذكاء اصطناعي وقد تحدثنا عن تطبيقات الذكاء الاصطناعي في مقال تطبيقات الذكاء الاصطناعي لذا يمكنك الرجوع إليه. تشمل بعض حالات الاستخدام لمجال تعلم الآلة: التعرّف على الصور التعرف على الكلام التصنيف والتنبؤ الآلات والروبوتات الذكية -السيارات ذاتية القيادة التجارة الإلكترونية الأمان واكتشاف التزوير أو الاحتيال -الترجمة الآلية والخدمات المالية …إلخ. يُستخدم التعلم الآلي في العديد من المجالات الأخرى والتي قد لا يسعنا ذكر جميعها هنا. كما أن معظم تطبيقات الذكاء الصناعي اليوم تعتمد بشكل كامل على خوارزميات تعلم الآلة. طور أعمالك مع حلول الذكاء الاصطناعي المبتكرة اسبق منافسيك نحو المستقبل وحقق أهدافك بالاستعانة بقوة الذكاء الاصطناعي اطلب خدمات الذكاء الاصطناعي الآن في الفيديو التالي ستتعرف على المزيد من المعلومات حول معنى تعلم الآلة وأهدافها والمكونات الرئيسية لأي نظام معتمد على تعلم الآلة وأمثلة منوعة على التطبيقات المستخدمة في هذا المجال. أنواع تعلم الآلة ينقسم تعلم الآلة إلى أربعة أنواع أساسية أو مجالات أساسية بناءً على نوع البيانات المستخدمة وأسلوب التعلم المتبع وهذه الأنواع هي: التعلم الخاضع للإشراف Supervised learning التعلم غير الخاضع للإشراف UnSupervised learning التعلم شبه الخاضع للإشراف Semi-Supervised Learning التعلم المُعزز Reinforcement Learning يحاكي كل نوع من هذه الأنواع الطرق والأساليب المختلفة التي تتخذ بها نحن البشر قرارتنا وإليك شرحًا مفصلًا لكل نوع من بين هذه الأنواع. 1. التعلم الخاضع للإشراف Supervised learning هو أحد أكثر أنواع التعلم الآلي شيوعًا ونجاحًا. في هذا النوع تُعطى الخوارزمية مجموعةً من البيانات بالإضافة إلى الخرج الصحيح لها. أي أن الخوارزمية تُدرّب على مجموعةً بيانات مُسماة. مبدأ عمل الخوارزميات التي تنتهج هذا النهج هو: الحصول على مجموعة بيانات مُسماة، ثم تدريب النموذج على إيجاد علاقة أو دالة function تربط بين المدخلات والمخرجات. إذًا يمكننا القول أن هذا النهج يُستخدم عندما نريد بناء نموذج يتنبأ (كلمة يتنبأ هنا تشمل التصنيف أيضًا) بنتيجة معينة في ضوء مدخلات معينة ويتطلب تعلم الآلة الخاضع للإشراف جهدًا بشريًا لبناء مجموعة التدريب المناسبة. هناك نوعان رئيسيان من مشكلات التعلم الآلي الخاضعة للإشراف هما التصنيف Classification والتوقع Regression. يكون الهدف في حالة التصنيف هو التنبؤ بتسمية فئة من قائمة محددة مسبقًا من الفئات. المثال الذي ورد في الفقرة السابقة هو خير مثال على ذلك، حيث نُدرّب الخوارزمية على التمييز بين فئتين أو نوعين من الفاكهة هما البرتقال والتفاح. التصنيف بدوره يُقسم إلى نوعين أساسيين هما: التصنيف الثنائي Binary Classification والتصنيف المتعدد Multiclass-Classification. يحدث التصنيف الثنائي عندما يكون لدينا فئتين من البيانات كما في مثال البرتقال والتفاح، مثال آخر هو تصنيف رسائل البريد الإلكتروني على أنها بريد عشوائي أو بريد حقيقي أما التصنيف المتعدد فهو يحدث عندما يكون لدينا أكثر من فئتين (مثلًا لو كان المطلوب جعل النموذج سالف الذكر يميز بين 3 أنواع من الفاكهة هي البرتقال والتفاح والموز. بالنسبة لمهام التوقع، فإن الهدف يكون توقع قيمة من مجال غير محدد وخير مثال على ذلك هو توقع أسعار المنازل أو توقع الدخل السنوي للفرد بناءًا على مستواه التعليمي وعمره ومكان إقامته فسعر المنازل أو الدخل السنوي يمكن أن تكون أي قيمة ضمن مجال ما. 2. التعلم غير الخاضع للإشراف UnSupervised learning في هذا النوع من أنواع تعلم الآلة تُعطى الخوارزمية مجموعةً من البيانات بدون الخرج الصحيح لها، أي أن الخوارزمية تُدرّب على مجموعةً بيانات غير مُسماة. مبدأ عمل الخوارزميات التي تنتهج هذا النهج هو: الحصول على مجموعة بيانات غير مُسماة، ثم تدريب النموذج على إيجاد علاقات مخفيّة بين هذه المُدخلات. هناك نوعان أساسيين من الخوارزميات التي تنتهج هذا النهج هما خوارزميات التحويل Transformation وخوارزميات العنقدة أو التجميع Clustering. تُنشئ خوارزميات التحويل تمثيلًا جديدًا للبيانات قد يكون من الأسهل على البشر أو خوارزميات التعلم الآلي الأخرى فهمه مقارنةً بالتمثيل الأصلي للبيانات. بالنسبة للنوع الآخر المتمثل بخوارزميات العنقدة، فهي تعتمد على تقسيم البيانات إلى مجموعات مميزة من العناصر المتشابهة. خذ مثلًا عملية تحميل الصور على أحد مواقع التواصل الاجتماعي كمثال، هنا قد يرغب الموقع في تجميع الصور التي تُظهر الشخص نفسه مع بعضها بغية تنظيم صورك إلا أن الموقع لا يعرف من يظهر في الصور ولا يعرف عدد الأشخاص المختلفين الذين يظهرون في مجموعة الصور خاصتك. تتمثل الطريقة المنطقية لحل المشكلة في استخراج كل الوجوه وتقسيمها إلى مجموعات من الوجوه المتشابهة، وبعدها يتم وضع كل صورة جديدة تحملها في المجموعة الأكثر شبهًا لها ومن أشهر خوارزميات العنقدة نذكر خوارزمية K-means. التعلم شبه الخاضع للإشراف Semi-Supervised Learning تحدثنا سابقًا عن أهمية البيانات، وأنّه كلما زاد حجم البيانات التي نُدرّب النموذج عليها تزداد قوته، ولاسيما بالنسبة خوارزميات التعلم العميق التي تنتهج النهج الخاضع للإشراف والتي تعتمد على البيانات المُسماة. نشأ التعلم شبه الخاضع للإشراف من السؤال التالي: ماذا لو لم يكن لدينا ما يكفي من البيانات المُسماة لخوارزميتنا التي تنتهج المنهج الخاضع للإشراف؟ قد يخطر ببالك في هذه الحالة محاولة تسمية البيانات يدويًا لكن هذا ليس مضيعة للوقت فقط لكنه أيضًا عرضة للعديد من أنواع الأخطاء البشرية الحل البديل الجديد هو استخدام التعلّم شبه الخاضع للإشراف وهو مزيج من التعلم الخاضع للإشراف والتعلم غير الخاضع للإشراف فهو يستخدم البيانات غير المسماة (والتي تكون كثيرة) والبيانات المسماة (والتي تكون قليلة) معًا، لإنشاء تقنيات تعلم أفضل من تلك التي تُنشأ باستخدام البيانات غير المسماة فقط أو المسماة فقط. التعلم المعزز Reinforcement Learning في هذا النوع من تعلم الآلة ابتكر المطورون طريقة لمكافأة السلوكيات المرغوبة ومعاقبة السلوكيات السلبية للآلة حيث يعتمد التعلم المُعزز على التعلم من خلال التفاعل مع البيئة أي أن الخوارزمية أو الروبوت أو البرنامج والذي يسمى في هذه الحالة الوكيل "agent" يتعلم من من عواقب أفعاله بدلًا من تعليمه صراحةً. فالوكيل يختار أفعاله وقرارته المستقبلية على أساس تجاربه السابقة (الاستغلال) وأيضًا من خلال الخيارات الجديدة (الاستكشاف) أي أن التعلم يرتكز على مبدأ التجربة والخطأ ويتعلق الأمر باتخاذ الإجراءات المناسبة لزيادة المكافأة إلى أقصى حد في موقف معين. يتطلب التعلم المعزز عددًا كبيرًا من التفاعلات بين الوكيل والبيئة لجمع البيانات للتدريب ويستخدم في مجال الألعاب حيث تعد الألعاب من أبرز مجالات استخدام التعلم المعزز فهو قادر على تحقيق أداء خارق في العديد من الألعاب مثل لعبة Pac-Man. ومن الأمثلة على استخدامه هو تدريب السيارات على التوقف باستخدام نظام القيادة الآلي حيث يتم تعليم حاسوب السيارة أو الوكيل على الوقوف في المكان الصحيح من خلال قراءة بيانات أجهزة استشعار مثل الكاميرات ونظام تحديد المواقع GPS واتخاذ الإجراءات المناسبة مثل الضغط على الفرامل وتغيير الاتجاه ويحتاج الوكيل بالطبع إلى إيقاف السيارة مرارًا وتكرارًا باستخدام التجربة والخطأ ويتم تقديم إشارة مكافأة لتقييم جودة التجربة وتعزيز عملية التعلم. نماذج تعلم الآلة لقد وصل التعلم العميق وهو نوع متقدم من أنواع تعلم الآلة إلى مستوى من الاهتمام العام والاستثمار الصناعي لم يسبق له مثيل في تاريخ مجال علوم الحاسوب ولكنه ليس أول شكل أو نموذج ناجح للتعلم الآلي ويمكننا القول إن معظم خوارزميات التعلم الآلي المستخدمة في الصناعة هذه الأيام -ليست خوارزميات التعلم العميق. يوفر مجال تعلم الآلة العديد من النماذج machine learning models أشهرها التعلم العميق لكن في الواقع لا يعد التعلم العميق دائمًا الأداة المناسبة، ففي بعض الأحيان لا توجد بيانات كافية ليكون التعلم العميق قابلاً للتطبيق، ويفضل أن تُحلّ المشكلة بطريقة أفضل من خلال خوارزمية مختلفة وفيما يلي نوضح أهم أنواع طرق تعلم الآلة: النمذجة الاحتمالية Probabilistic modeling أساليب النواة Kernel Methods أشجار القرار Decision Trees والغابات العشوائية Random Forests الشبكات العصبية Neural Networks التعلم العميق Deep Learning لنتعرف على قرب على كل نموذج من هذه النماذج وأشهر استخداماته وخوارزمياته. النمذجة الاحتمالية Probabilistic modeling تعتمد النمذجة الاحتمالية على تطبيق مبادئ الإحصاء في تحليل البيانات. لقد كانت أحد أقدم أشكال التعلم الآلي، والتي لا تزال مستخدمة حتى يومنا هذا واحدة من أشهر الخوارزميات في هذه الفئة هي خوارزمية بايز الساذج Naive Bayes. أساليب النواة Kernel Methods عندما بدأت الشبكات العصبية تكتسب شهرة بين الباحثين في التسعينيات ظهر نهج جديد للتعلم الآلي سمي أساليب النواة وسرعان ما أدى لتراجع شعبية الشبكات العصبية، وأساليب النواة هي مجموعة من خوارزميات التصنيف، وأشهرها خوارزمية الآلة متجه الدعم (SVM). أشجار القرار Decision Trees والغابات العشوائية Random Forests هي إحدى طرق النمذجة التنبؤية التي تُستخدم في الإحصاء واستخراج البيانات وتعلم الآلة وهي عبارة عن هياكل شبيهة بالمخططات التدفقية تتيح لك تصنيف نقاط البيانات المُدخلة أو التنبؤ بقيم المخرجات بناءًا على البيانات المُدخلة مثلًا تعطيها مواصفات المنزل، فتتوقع السعر وقد تم تفضيلها على أساليب النواة. الشبكات العصبية Neural Networks حتى عام 2010 تقريبًا، كانت الشبكات العصبية مُهمّشة إلى أن بدأ عدد من الباحثين الذين كانوا ما يزالون يؤمنون بالشبكات العصبية في تحقيق نتائج مهمة باستخدامها، وبذلك بدأت التطورات على الشبكات العصبية تتسارع وتفرض هيمنتها في معظم مهام تعلم الآلة. التعلم العميق Deep Learning السبب الرئيسي وراء انتشار التعلم العميق وتطوره بسرعه هو أنه قدّم أداءً أفضل في العديد من المشكلات واختصر العديد من الخطوات المعقدة التي كانت موجودة في الأساليب السابقة، والتعلم العميق اليوم هو الأسلوب الأنجح في جميع المهام الحسية أو الإدراكية مثل مهام الرؤية الحاسوبية ومعالجة اللغات الطبيعية NLP. وللتعرف على المزيد من الأساليب المتطورة لخوارزميات وأدوات التعلم الآلي يمكنك إلقاء نظرة على مسابقات التعلم الآلي على موقع Kaggle نظرًا لبيئتها شديدة التنافس (بعض المسابقات لديها آلاف المشاركين وجوائز بملايين الدولارات) وللتنوع الكبير في مشكلات التعلم الآلي التي تغطيها. لغة بايثون وتعلم الآلة تعد لغة البرمجة بايثون اللغة الأكثر شهرة في مجال تعلم الآلة لعدد كبير من الأسباب فهي لغة سهلة التعلم والفهم و ولها شعبية كبيرة وتستخدم من قبل عدد كبير من الأشخاص والمؤسسات وتملك العديد من المكتبات وأطر العمل التي تختص في مجال الذكاء الصنعي وتعلم الآلة فيما يلي سنكتب كود تعلم آلة في بايثون، حيث سنستخدم خوارزمية أشجار القرار لتوقع نوع الفاكهة (برتقال أو تفاح) بناءًا على وزنها ونسيجها. from sklearn import tree # الخطوة 1 # الخطوة 2 X = [[140, 1], [130, 1], [150, 0], [170, 0]] y = [0, 0, 1, 1] # 0: apple, 1: orange # الخطوة 3 clf = tree.DecisionTreeClassifier() clf = clf.fit(X, y) # الخطوة 4 prediction = clf.predict([[160, 0]]) print(prediction) # النتيجة: 1 شرح تنفيذ الشيفرة: الخطوة الأولى: هي استيراد المكتبة ساي كيت ليرن sklearn التي تحتوي العديد من خوارزميات تعلم الآلة الجاهزة. الخطوة الثانية: هي تحديد بيانات التدريب. هنا سيكون لدينا 4 عينات، كل عينة تُمثّل نقطة بيانات الخطوة الثالثة: هي تدريب خوارزمية أشجار القرار على البيانات السابقة الهدف هنا هو جعل الخوارزمية قادرة على التنبؤ بنوع الفاكهة بعد أن ندربها على البيانات السابقة. الخطوة الرابعة: بعد الخطوة 3 نكون انتهينا من تدريب النموذج، أي أنه أصبح جاهزًا للاستخدام الفعلي. لذا نعطيه عينة بيانات، ونطلب منه توقع نوع الفاكهة. إذا كنت مهتمًا بإتقان تعلم الآلة باستخدام لغة البرمجة بايثون يمكنك البدء مع دورة الذكاء الاصطناعي AI التي توفرها أكاديمية حسوب والتي تبدأ معك من الصفر ولا تتطلب منك أي خبرة مسبقة فهي تبدأ معك بتعلم كل ما تحتاجه بلغة بايثون وتطوير تطبيقات تستفيد من دمج مختلف نماذج LLMs الحديثة مثل GPT من OpenAI و LLaMA 2 من Meta لتطوير تطبيقات ذكية بسهولة وكفاءة كما تشرح لك تحليل البيانات Data Analysis وتمثيلها مرئيًا، ومفاهيم تعلم الآلة Machine Learning والتعلم العميق Deep Learning وغيرها من خلال التركيز على الشرح العملي المعد من قبل مجموعة من المطورين المُختصين، الذين يُرافقونك طيلة الدورة وحتى بعد الدورة للإجابة على أي سؤال أو مشكلة تواجهك كما تضمن لك الدورة دخول السوق والحصول على فرصة عمل بعد تخرجك من الدورة خلال ستة أشهر. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن مصادر تعليمية لتعلم الآلة بالإضافة إلى دورة بايثون الشاملة التي ذكرناها للتو والتي تعد من أفضل الدورات العربية لتعلم بايثون وتعلم الآلة، توفر توفر لك أكاديمية حسوب أيضًا مجموعة من المقالات والدروس التعليمية ومنشورات الكتب العربية التي تعد من أفضل مصادر تعلم الآلة وتعلم البرمجة وعلوم الحاسوب نذكر لك منها: كتاب البرمجة بلغة بايثون كتاب مدخل إلى الذكاء الاصطناعي وتعلم الآلة كتاب عشرة مشاريع عملية عن الذكاء الاصطناعي سلسلة برمجة الذكاء الاصطناعي المفاهيم الأساسية لتعلم الآلة نظرة سريعة على مجال تعلم الآلة أدوات برمجة نماذج تعلم الآلة وننصحك بتفصّح ومتابعة قسم مقالات الذكاء الاصطناعي في أكاديمية لقراءة المقالات الجديدة المنشورة حول الذكاء الاصطناعي وتعلم الآلة وما يتعلق بهما. أفضل مكتبات تعلم الآلة في بايثون إليك قائمة بأهم المكتبات التي قد تحتاجها في تعلّم الآلة (جميعها تعمل في لغة بايثون): نمباي Numpy: من أفضل مكتبات بايثون في مجال تعلم الآلة والذكاء الصناعي فهي مكتبة قوية وشائعة الاستخدام تسهل معالجة المصفوفات والتعامل معها وتسهل عمليات معالجة البيانات والعمليات الإحصائية عليها. سي باي Scipy: توفر هذه المكتبة مجموعة من الحزم المخصصة للاستخدام في مجال للحوسبة العلمية والهندسة والتقنية وتحتوي على وحدات مختلفة للجبر الخطي والتكامل والإحصاءات. ساي كيت ليرن Scikit-learn: اختصارًا Sklearn وهي مكتبة فعالة توفر العديد من أدوات وخوارزميات تعلم الآلة. باندا Pandas: هي مكتبة تستخدم لمعالجة البيانات وتحليلها وتسهل عمليات تنظيم البيانات وفهرستها ووضعها ضمن جداول وإجراء العمليات عليها. ماتبلوتليب Matplotlib: هي مكتبة بايثون خاصة بتصميم الرسوميات والمخططات البيانية وإنشاء رسومات تفاعلية وتصديرها إلى تنسيقات مختلفة. إضافة للعديد من أطر العمل القوية مثل ثيانو Theano وتنسرفلو TensorFlow وكيراس Keras وباي تورش PyTorch التي تسهل بناء نماذج الشبكات العصبية وتطبيق خوارزميات التعلم العميق. مستقبل تعلم الآلة بالرغم من أن خوارزميات تعلم الآلة كانت موجودة منذ عقود إلا أنها اكتسبت شعبية أكبر مع تنامي الذكاء الاصطناعي فمعظم تطبيقات الذكاء الصناعي المتقدمة التي نراها اليوم هي تطبيقات تعمل باستخدام خوارزميات تعلم الآلة (لاسيما التعلم العميق) بدءًا من خوارزميات التوصية التي نراها في موجز أخبار فيسبوك وغيرها من شبكات التواصل الاجتماعي والتي تعتمد على اهتماماتنا وعاداتنا في القراءة وصولًا السيارات ذاتية القيادة مثل سيارات تسلا Tsla ومعالجة اللغات الطبيعية مثل تطبيق الدردشة ChatGPT. كما تندمج الحلول التي يقدمها تعلم الآلة يومًا بعد يوم في العمليات الأساسية للشركات واستخدامات حياتنا اليومية مثل الترجمة الآلية للغات والتشخيص الطبي البحث الصوتي والبحث عن أقصر الطرق للوصول إلى وجهة ما وغيرها الكثير من التطبيقات ومن المتوقع أن ينمو السوق العالمي للتعلم الآلي من 8.43 مليار دولار أمريكي في عام 2019 إلى 117.19 مليار دولار أمريكي بحلول عام 2027 وفقًا لتوقع Fortune Business Insights. وقد بدأت العديد من الشركات بالفعل في استخدام خوارزميات التعلم الآلي نظرًا لقدرتها على إجراء تنبؤات وقرارات تجارية أكثر دقة ففي عام 2020 تم رصد 3.1 مليار دولار لتمويل شركات تعلم الآلة، وذلك لقدرة تعلم الآلة على إحداث تغييرات كبيرة في الصناعات. فيما يلي بعض التوقعات لمستقبل التعلم الآلي: يمكن للحوسبة الكمية أن تحدد مستقبل تعلم الآلة: والحوسبة الكمومية هي أحد المجالات التي لديها القدرة على تعزيز قدرات التعلم الآلي وهي تسمح بأداء عمليات متعددة الحالات في وقت واحد، مما يتيح معالجة أسرع للبيانات ففي عام 2019 أجرى معالج كمي خاص بشركة جوجل Google مهمة في 200 ثانية من شأنها أن تستغرق 10000 عام لإكمالها على أفضل حاسب عملاق في العالم! قد يتمكن برنامج AutoML من تسهيل عملية تطوير نماذج تعلم الآلة من البداية إلى النهاية: يعمل AutoML الذي طورته جوجل على أتمتة عملية بناء وتطبيق خوارزميات التعلم الآلي وهو يسمح لأي شخص أو شركة تجارية بتطبيق نماذج وتقنيات التعلم الآلي المعقدة وتخصيصها وفق احتياجات أعمالهم خلال دقائق دون أن يكون خبيرًا في التعلم الآلي. دخول تعلم الآلة بقوة في عملية التصنيع: لا زالت الشركات المصنعة ما زالت في المراحل الأولى فقط من تبني التعلم الآلي. ففي عام 2020 استفادت 9٪ فقط من الشركات المشاركة في استطلاع رأي حول استخدام الذكاء الاصطناعي في عملياتهم التجارية، لكن مع التطورات المستمرة التي نشهدها في مجال تعلم الآلة يتوقع أن تزداد هذه النسبة بشكل كبير ويصبح الاعتماد على استخدام تقنيات تعلم الآلة والروبوتات في أماكن التصنيع أكثر بكثير في المستقبل القريب. السيارات والمركبات ذاتية القيادة: تعمل شركات مثل تسلا وهوندا باستمرار على إنتاج هذا النوع من السيارات مستفيدة من تقنيات التعلم الآلي، وقد قدمت هذه الشركات بالفعل سيارات مؤتمتة جزئيًّا لكن لا تزال السيارات ذاتية القيادة قيد التطوير ولم تصل إلى الشكل النهائي. الخلاصة قدمنا من خلال هذه المقالة دليلًا شاملًا عن تعلم الآلة وبدأنا بتوضيح المفهوم نفسه وأنواع التعلم الآلي وتطبيقاته والفروقات بينها والفرق بين التعلم الآلي والذكاء الاصطناعي، كما تعرفنا على أهمية لغة البرمجة بايثون في مجال التعلم الآلي وسردنا أهم المكتبات وأطر العمل التي توفرها في مجال التعلم الآلي وقدّمنا مثلًا برمجيًا لتطبيق خوارزمية الانحدار اللوجستي كما سردنا أهم المصادر والمراجع لتعلم مجال الذكاء الاصطناعي عمومًا وتعلم الآلة خصوصًا ثم ألقينا نظرة أخيرًا على مستقل مجال تعلم الآلة. نرجو أن تكون هذه المقالة قد قدمت لك فهمًا جيّدًا لمجال تعلم الآلة ووضحت لك المفاهيم الأساسية لتنطلق في رحلة تعلم الآلة والانتقال إلى مواضيع وتطبيقات عملية حتى دخول سوق العمل.
  2. يوجه النقد لنماذج تعلّم الآلة غالبًا بأنها صناديق سوداء نُدخَل فيها البيانات من جهة للحصول على أجوبة دقيقة في أغلب الأحيان، ومن جهة أخرى دون أي تفسير واضح لكيفية الحصول على الجواب. نعاين في هذا الجزء الثالث من بناء نموذج تعلّم آلة في بايثون نموذج التعلّم المطوّر لمحاولة فهم كيفية وصوله للتنبؤ الدقيق، وما يُمكن تعلمه حول مسألتنا المطروحة (التنبؤ بمعامل نجمة الطاقة) من هذا النموذج. وسنختم بمناقشة جزءٍ مهمٍ في مشاريع تعلّم الآلة وهو توثيق العمل وعرض النتائج بوضوح. عرضنا في الجزء الأول مسألة تنظيف البيانات وتحليلها الاستكشافي، ومن ثم آليات هندسة الميزات لاختيار المناسب منها؛ أما في الجزء الثاني فشرحنا آلية احتساب القيم الناقصة وعرضنا الشيفرة اللازمة لإنشاء نماذج تعلّم، كما وازنا بين عدة نماذج تعلّم ممكنة وصولًا إلى اختيار النموذج الأمثل لمسألتنا. وبهدف تحسين أداء النموذج المختار عالجنا مسألة معايرة المعاملات الفائقة للنموذج باستخدام البحث العشوائي مع التقويم المتقاطع. وفي النهاية حسبنا أداء النموذج مع بيانات الاختبار. ننصح الجميع بتحميل الشيفرة ومعاينتها ومشاركتها، فهي متاحة للعموم. نُذكّر بأننا نعمل على تطوير نموذج تعلّم آلة من نمط موجه عبر الانحدار، وذلك باستخدام بيانات الطاقة لمباني نيويورك المتاحة للعموم بهدف التنبؤ بمعامل نجمة الطاقة للمبنى، ووصلنا في النهاية إلى بناء نموذج تعلّم من نمط الانحدار المعزز بالتدرج مع متوسط خطأ مطلق يساوي إلى 9.1 نقطة، بحيث يتراوح معامل نجمة الطاقة بين 1 و100 نقطة. تفسير وفهم النموذج يُعدّ الانحدار المعزز بالتدرج من النماذج المعقدة، إلا أنه يتموضع في منتصف مقياس إمكانية تفسير النموذج نظرًا لاحتوائه على أشجار القرار القابلة للشرح والتفسير. سننظر في الطرق الثلاثة التالية لمحاولة فهم آلية التنبؤ: أهمية الميزات. معاينة شجرة قرار واحدة. التفسيرات المحلية المحايدة للنموذج. تختص الطريقة الأولى والثانية بأشجار القرار؛ أما الطريقة الثالثة فهي طريقة عامة يُمكن استخدامها مع أي نموذج، وهي حزمة برمجية جديدة وخطوة مهمة نحو فهم آلية التنبؤ في مسائل تعلّم الآلة. أهمية الميزات تُحسب أهمية ميزة ما بمدى تأثيرها في الوصول إلى القيمة الهدف. وهنا لن نخوض في حسابات الأهمية المعقدة مثل حساب متوسط الانخفاض في عدم النقاء أو حساب انخفاض الخطأ عند تضمين الميزة، إذ توفر مكتبات Scikit-Learn إمكانية حساب أهمية كل ميزة لأي نموذج متعلّم يعتمد على الأشجار. تحسب الشيفرة التالية أهمية الميزات باستخدام model.feature_importances، حيث model هو النموذج المطور، ثم نضع الأهميات المحسوبة في إطار بيانات بهدف رسم العشر الأوائل الأكثر أهمية: import pandas as pd # model النموذج المطور importances = model.feature_importances_ # train_features إطار بيانات المزايا feature_list = list(train_features.columns) # وضع أهميات المزايا في إطار بيانات feature_results = pd.DataFrame({'feature': feature_list, 'importance': importances}) # إظهار الميزات العشر الأكثر أهمية feature_results = feature_results.sort_values('importance', ascending = False).reset_index(drop=True) feature_results.head(10) يُظهِر الشكل بوضوح أن أهم ميزتين (تُشكّلان لوحدهما حوالي 66% من الأهمية الكلية هما كثافة استخدام الطاقة Site EUI Energy Use Intensity، وكثافة الكهرباء وفق الطقس Weather Normalized Site Electricity Intensity. تنخفض أهمية الميزات الأخرى بعد هاتين الميزتين مما يعني أننا لسنا مضطرين لاستخدام كل الميزات في النموذج (64 ميزة) للوصول إلى دقة كبيرة، وقد اخترنا عمليًا على سبيل التجريب عشر ميزات فقط وقمنا بإعادة بناء النموذج إلا أن دقته لم تكن جيدةً. وبهذا تعرّفنا السريع عن أهم الميزات المؤثرة في معامل نجمة الطاقة دون الاضطرار للغوص كثيرًا في الخلفية النظرية لهذا الموضوع. معاينة شجرة قرار وحيدة تُعَد أشجار القرار من أبسط الأشياء القابلة للفهم بخلاف الانحدار المعزز بالتدرج، حيث نستخدم في الشيفرة التالية التابع export_graphviz من Scikit-Learn لمعاينة أي شجرة من غابة الأشجار، إذ نستخرج أولًا شجرةً من غابة الأشجار ثم نحفظها في ملف نقطي dot. from sklearn import tree # استخراج الشجرة (105) single_tree = model.estimators_[105][0] # حفظ الشجرة في ملف نقطي tree.export_graphviz(single_tree, out_file = 'images/tree.dot', feature_names = feature_list) نستخدم برمجية المعاينة من Graphviz لحفظ الصورة بصيغة png وذلك باستخدام التعليمة cmd التالية: dot -Tpng images/tree.dot -o images/tree.png ويكون الناتج عبارة عن شجرة كاملة: لا يُمكن تفحص الشجرة السابقة جيدًا على الرغم من أن عمقها 6 فقط لذا سنعيد توليد الشجرة بعمق 2 عن طريق تعديل معامل العمق للتابع export_graphviz: تحوي كل عقدة من الشجرة المعلومات التالية: اختبار منطقي لقيمة ميزة ما، حيث تُحدّد نتيجة هذا الاختبار المنطقي الاتجاه التالي في الشجرة نزولًا يمينًا أو يسارًا. mse قياس الخطأ في العقدة. samples عدد الأمثلة في العقدة. value تقدير القيمة الهدف عند هذه العقدة. بالطبع لن يكون في أوراق الشجرة إلا الخطأ وتقدير القيمة الهدف. تتنبأ شجرة القرار بالنتيجة كما يلي: تبدأ من العقدة الأولى (الجذر) وتتحرك نحو الأسفل حتى تصل لورقة من أوراق الشجرة، ويُحدّد الاختبار المنطقي نعم/لا في كل عقدة اتجاه الحركة يمينًا أو يسارًا. فمثلًا، تسأل عقدة الشكل السابق عما إذا كانت قيمة ميزة كثافة الطاقة EUI أقل من 68.95 أم لا؟ فإذا كان الاختبار صحيحًا ينتقل للعقدة الأدنى اليسرى، وإلا للعقدة الأدنى اليمنى، وهكذا حتى الوصول لورقة فتكون القيمة فيها هي قيمة التنبؤ النهائي. وإذا حوت الورقة على عدد من الأمثلة فسيكون لهم جميعًا نفس قيمة التنبؤ. وبالطبع فكلما نزلنا في الشجرة ينقص الخطأ المرتكب في التنبؤ لأننا ننعم عمليًا الأمثلة أكثر فأكثر، إلا أن زيادة العمق قد توقعنا في مشكلة فرط التخصيص ولن تتمكن الشجرة عندها من تعميم الأمثلة. عايرنا المعاملات الفائقة للنموذج في المقالة السابقة والتي تتحكم في شجرة القرار الناتجة، مثل العمق الأعظم للشجرة وعدد الأمثلة الأصغر المطلوب في ورقة، وهما معاملان يلعبان دورًا كبيرًا في المقايضة الملائمة/عدم الملائمة الفائضة. تسمح معاينة شجرة القرار لنا بفهم تأثير هذه المعاملات الفائقة عمليًا، وعلى الرغم من أن معاينة جميع الأشجار أمرًا صعبًا، إلا أنه يكفي لنا معاينة شجرة واحدة فقط لنتمكن من فهم آلية التنبؤ التي ينجزها النموذج. وتبدو هذه الطريقة المعتمدة على مخطط انسيابي مشابهةً لما يفعله الإنسان عند اتخاذ قراراته بالإجابة على سؤال حول قيمة معينة في كل مرة. تراكب أشجار القرار المعتمدة على مجموعات يبَيّن تنبؤات العديد من أشجار القرار الفردية من أجل إنشاء نموذج أكثر دقة مع تباين أقل، وتميل مجموعات الأشجار عامةً إلى أن تكون دقيقةً للغاية، كما أنها سهلة الشرح. التفسيرات المحلية المحايدة للنموذج LIME تهدف هذه الأداة الجديدة إلى شرح تنبؤ وحيد لأي نموذج، وذلك بإنشاء تقريب محلي للنموذج يكون قريبًا من المثال المدروس باستخدام نموذج بسيط مثل الانحدار الخطي (للحصول على تفاصيل أكثر يُمكن مراجعة الملف). سنستخدم LIME لتفحص تنبؤ خاطئ ينجزه نموذجنا المطور، وللحصول على تنبؤ خاطئ نستخرج المثال الذي يكون معامل الخطأ المطلق له أكبر ما يُمكن: from sklearn.ensemble import GradientBoostingRegressor # إنشاء النموذج مع المعاملات الفائقة الأمثل model = GradientBoostingRegressor(loss='lad', max_depth=5, max_features=None, min_samples_leaf=6, min_samples_split=6, n_estimators=800, random_state=42) # ملائمة واختبار النموذج model.fit(X, y) model_pred = model.predict(X_test) # إيجاد الأخطاء residuals = abs(model_pred - y_test) # استخراج أكثر تنبؤ خاطئ wrong = X_test[np.argmax(residuals), :] print('Prediction: %0.4f' % np.argmax(residuals)) print('Actual Value: %0.4f' % y_test[np.argmax(residuals)]) يكون الناتج: Prediction: 12.8615 Actual Value: 100.0000 تُنشئ الشيفرة التالية كائن شرح مع تمرير المعاملات التالية له: بيانات التدريب والنمط وعناوين بيانات التدريب وأسماء الميزات. import lime # إنشاء كائن شرح explainer = lime.lime_tabular.LimeTabularExplainer(training_data = X, mode = 'regression', training_labels = y, feature_names = feature_list) # شرح أسوء تنبؤ exp = explainer.explain_instance(data_row = wrong, predict_fn = model.predict) # رسم شرح التنبؤ exp.as_pyplot_figure(); ويكون الناتج ما يلي: يُمكن تفسير الشكل كما يلي: يُظهِر المحور y الميزات التي أوصلت للنتيجة مع شريط أخضر في حال كانت قيمة الميزة تؤثر إيجابًا على القيمة الهدف؛ أما إذا كان تأثير قيمة الميزة سلبًيا فيكون لون الشريط أحمر. مثلًا: تؤدي قيمة الميزة الأولى والتي هي أكبر من 95.90 إلى طرح حوالي 40 من القيمة الهدف؛ أما الميزة الثانية والتي قيمتها أقل من 3.80 فتجمع 10 للتنبؤ، حيث تكون قيمة التنبؤ النهائية حاصل جمع كل هذه القيم الموجبة والسالبة. يُمكن الحصول على نظرة أخرى لنفس المعلومات باستخدام الطريقة show_in_notebook: # إظهار الشروحات exp.show_in_notebook() يُبين الشكل آلية المحاكمة التي ينجزها النموذج وصولًا للهدف من خلال عرض تأثير كل ميزة على التنبؤ (يسار الشكل)، وعرض القيم الفعلية (يمين الشكل). للأسف، يُظهر المثال تنبؤ النموذج للقيمة الهدف بحوالي 12 بينما القيمة الفعلية هي 100؛ إلا أنه بتفحص قيم الميزات يتبين أن قيمة معامل كثافة الطاقة مرتفعة نسبيًا، مما يعني أن معامل نجمة الطاقة يجب أن يكون منخفضًا لأن الارتباط بين كثافة الطاقة ومعامل نجمة الطاقة هو ارتباط سلبي كما عرضنا سابقًا. في مثل هذه الحالة يجب البحث عن سبب إعطاء المبنى معاملًا مرتفعًا على الرغم من أن كثافة الطاقة مرتفعة. وهنا قد تبدو أدوات الشرح المتوفرة غير كاملة، إلا أنها وفي جميع الأحوال ساعدتنا على فهم النموذج المتعلّم، مما يسمح لنا باتخاذ قرارات أفضل. توثيق العمل وإعداد تقارير النتائج يغفل الكثيرون في المشاريع التقنية عن ضرورة توثيق العمل وعرض النتائج بوضوح، حيث يُمكن أن يُهمَل عملنا ولو كان رائعًا في حال لم نعرض النتائج بطريقة مناسبة. يهدف التوثيق إلى وضع جميع الشروحات والتعليقات التي تُمكّن الآخرين من قراءة الشيفرة المكتوبة وفهمها وإعادة بنائها بسرعة عند الحاجة، إذ يُعدّ استخدام أدوات مثل Jupyter Notebooks والتي تسمح بوضع الشروحات المطلوبة أمرًا أساسيًا كي نتمكن نحن أولًا، ومن ثم الآخرين بمراجعة الشيفرة وتعديلها إن لزم ولو بعد أشهر من كتابتها. تسمح بعض الإضافات للأداة السابقة بإخفاء الشيفرة في التقرير النهائي، إذ قد لا يرغب البعض بمشاهدة قطع بايثون في كل فقرة. يجد كاتب المقال الأصلي صعوبةً في تلخيص العمل المنجز وإخفاء التفاصيل، إلا أنه يوجز العمل كله في النقطتين التاليتين: يُمكن بناء نموذج تعلّم للتنبؤ بمعامل نجمة الطاقة بالاعتماد على بيانات الطاقة لمدينة نيويورك مع خطأ ممكن بحوالي 9.1 نقطة. تلعب الميزتان التاليتان الدور الأكبر في تحديد معامل نجمة الطاقة: كثافة استخدام الطاقة Site EUI' Energy Use Intensity` وكثافة الكهرباء وفق الطقس Weather Normalized Site Electricity Intensity. أُنجز هذا المشروع بصفة اختبار لكاتب المقال لقبوله في وظيفة في شركة ناشئة، كما طُلب منه عرضُ عمله واستنتاجاته في النهاية، ولذا فقد طور ملفًا من النمط Jupyter Notebook لتسليمه للشركة، وعوضًا عن تحويل الملف إلى pdf فورًا استخدم Latex لتحويله إلى ملف من النمط tex، ومن ثم تحريره باستخدام texStudio قبل توليد الملف النهائي بصيغة pdf. علمًا أنه يُمكن توليد الصيغة pdf من Jupyter مباشرةً وتحريرها لإجراء بعض التحسينات عليها. وفي نهاية المطاف تلعب مهارة عرض النتائج دورًا أساسيًا في إبراز أهمية العمل ونتائجه التي يُمكن أن تُبنى القرارات وفقها. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن النتائج استعرضنا في هذه السلسلة من المقالات الثلاثة خطوات بناء مشروع تعلّم آلة كامل من البداية إلى النهاية، حيث بدأنا بتنظيف البيانات ثم انتقلنا إلى بناء النموذج ومن ثم لمحاولة فهم نموذج التعلّم. نُعيد التذكير أخيًرا بخطوات بناء نموذج تعلّم الآلة: تنظيف البيانات وتنسيقها. تحليل البيانات الاستكشافي. هندسة الميزات والاختيار منها. موازنة نماذج تعلّم الآلة باستخدام مقياس للأداء. معايرة المعاملات الفائقة لنموذج التعلّم. تقويم النموذج الأمثل مع بيانات الاختبار. تفسير نتائج النموذج إلى أقصى حد ممكن. استخلاص النتائج وتوثيق العمل. قد تختلف هذه الخطوات وفق المسألة المطروحة طبعًا، وغالبًا ما يكون العمل في مسائل تعلّم الآلة تكراريًا وليس تسلسليًا، أي يُمكن أن نعود لخطوات سابقة دومًا. نأمل أن توفر لك هذه السلسلة من المقالات الثلاثة دليلًا جيدًا يُساعدك أثناء معالجتك لمشاريع تعلّم الآلة المستقبلية، كما نتمنى أن تكون هذه السلسلة قد منحتك الثقة لتكون قادرًا على تنفيذ مشاريع تعلّم الآلة الخاصة بك. وتذكر بأن أحدًا لا يعمل بمفرده وأنه يوجد الكثير من المجموعات الداعمة والتي يُمكن طلب المساعدة منها. ننصحك بالرجوع إلى كتاب مدخل إلى الذكاء الاصطناعي وتعلم الآلة من أكاديمية حسوب. ترجمة -وبتصرف- للمقال A Complete Machine Learning Project Walk-Through in Python: Part Three لكاتبه Will Koehrsen. اقرأ أيضًا خطوات تنفيذ مشروع عن تعلم الآلة في بايثون: الجزء الأول دوال الكثافة الاحتمالية في بايثون المرجع الشامل إلى تعلم لغة بايثون
  3. نعرض في هذه السلسلة من المقالات تسلسل خطوات تنفيذ مسألة حقيقية في تعلّم الآلة بهدف تسليط الضوء على أهم الصعوبات التي يُمكنها مواجهة المبرمج وكيفية التغلب عليها. شرحنا في المقالة السابقة الخطوات الثلاثة الأولى في مسألة تعلّم الآلة، وهي تنظيف البيانات أولًا، ومن ثم إجراء التحليل الاستكشافي لها بهدف اختيار الميزات المناسبة للمسألة، وأخيرًا تحديد خط الأساس الذي سنقيّم أداء نموذج التعلّم وفقه. نتابع في هذه المقالة عرض إنشاء عدة نماذج تعلّم مختلفة باستخدام بايثون، وذلك بهدف موازنة هذه النماذج لاختيار الأفضل منها، ومن ثم آلية ضبط ومعايرة المعاملات الفائقة للنموذج المختار بشكل أمثلي. وأخيرًا كيفية تقويم النموذج النهائي باستخدام بيانات الاختبار المتوفرة. يُمكن تنزيل واستخدام ومشاركة الشيفرة المتاحة للعموم من مستودع github. اختيار النموذج وتقييمه نُذكّر بأننا نريد بناء نموذج يتنبأ بمعامل نجمة الطاقة Energy Star Score لمدينة نيويوركk مع التركيز على دقة النتائج وقابلية تفسيرها وذلك باستخدام بيانات الطاقة المتاحة للعموم. وبهذا تكون المسألة المطروحة مسألة تعلّم آلة من نمط موجه عبر الانحدار كما عرضنا سابقًا. كي لا نغوص في الموازنات الكثيرة بين نماذج تعلّم الآلة المعروفة ونتمعن في مخططات موازنة النماذج سنجرب العديد منها عمليًا لاختيار الأمثل. لنتذكر أن تعلّم الآلة مازال حقلًا تجريبيًا، وقد يكون من المستحيل معرفة النموذج الأمثل مسبقًا. نبدأ عادةً بتجربة نموذج بسيط قابل للتفسير مثل نموذج الانحدار الخطي linear regression، وفي حال كانت دقته غير مقبولة، فسننتقل لنماذج أكثر تعقيدًا. يُبين الشكل التالي (المرسوم بشكل تجريبي) العلاقة بين الدقة accuracy وقابلية التفسير interpretability. سنجرب النماذج الخمسة التالية: الانحدار الخطي Linear Regression. انحدار أقرب الجيران K-Nearest Neighbors Regression. انحدار الغابة العشوائية Random Forest Regression. الانحدار المعزز بالتدرج Gradient Boosted Regression. انحدار آلة متجهة الدعم Support Vector Machine Regression. لن نغوص في الخلفية النظرية لهذه النماذج بل سنُركز على برمجتها وتقويمها. يُمكن العودة للمزيد من التفاصيل للمقال العربي المفاهيم الأساسية لتعلم الآلة، وللمرجع المجاني من Introduction to Statistical Learning، وللكتاب Hands-On Machine Learning with Scikit-Learn and TensorFlow، لأخذ معرفة أكثر حول ذلك. احتساب القيم الناقصة حذفنا في مرحلة تنظيف البيانات الأعمدة التي يتجاوز فيها عدد القيم الناقصة 50% من قيم العمود. وسيتعين علينا الآن ملء القيم الناقصة المتبقية لأن جميع خوارزميات التعلّم لا تعمل بوجود قيم ناقصة. لنبدأ أولًا بعرض كل البيانات باستخدام الشيفرة التالية: import pandas as pd import numpy as np # قراءة البيانات ووضعها في إطار بيانات train_features = pd.read_csv('data/training_features.csv') test_features = pd.read_csv('data/testing_features.csv') train_labels = pd.read_csv('data/training_labels.csv') test_labels = pd.read_csv('data/testing_labels.csv') Training Feature Size: (6622, 64) Testing Feature Size: (2839, 64) Training Labels Size: (6622, 1) Testing Labels Size: (2839, 1) والذي يُظهر: لاحظ أن كل قيمة "ليست رقمًا" NaN تُمثّل قيمةً ناقصة، وهنا سنستبدل ببساطة كل قيمة ناقصة بالقيمة الأوسط median لعمودها. يُمكن العودة للمرجع لمعاينة طرق أخرى لاستبدال القيم الناقصة. نُنشئ في الشيفرة التالية كائنًا Scikit-Learn من النمط Imputer مع إسناد استراتيجيته إلى القيمة الأوسط، ونُدرّب بعدها هذا الكائن باستخدام بيانات التدريب عن طريق تابع الملاءمة imputer.fit، ومن ثم نستخدمه لملء القيم الناقصة في كل من بيانات التدريب والاختبار باستخدام تابع التحويل imputer.transform. لاحظ أن القيم الناقصة في بيانات الاختبار تُستبدل بالقيمة الأوسط لبيانات التدريب. نستخدم هذه الطريقة في الحساب لتجنب الوقوع في فخ تسرب بيانات الاختبار test data leakage, وبهذا لا تختلط بيانات الاختبار مع بيانات التدريب أبدًا. # إنشاء كائن الحساب مع استراتيجية القيمة الأوسط imputer = Imputer(strategy='median') # الملاءمة مع ميزات التدريب imputer.fit(train_features) # حساب كل من بيانات التدريب والاختبار X = imputer.transform(train_features) X_test = imputer.transform(test_features) Missing values in training features: 0 Missing values in testing features: 0 يؤدي تنفيذ الشيفرة السابقة إلى الحصول على ميزات لها قيم حقيقية منتهية وبدون أي نقص فيها. تحجيم الميزات نعني بتحجيم الميزات الإجرائية العامة التي تُغير مجال قيم الميزة، وهي عملية ضرورية لأن الميزات تأتي عادةً بوحدات مختلفة، وبالتالي ستتوزع قيمها على مجالات مختلفة، تتأثر بعض النماذج كثيرًا ولاسيما تلك التي تحسب المسافة بين الأمثلة بمجالات هذه القيم، مثل نموذج آلة متجهة الدعم support vector machine ونموذج أقرب الجيران، بينما لا تتطلب بعض النماذج بالضرورة تحجيم الميزات، مثل نموذج الانحدار الخطي linear regression ونموذج الغابة العشوائية random forest. وتكون هذه الخطوة ضروريةً في جميع الأحوال عند الحاجة لموازنة نماذج التعلّم المختلفة. نفعّل من أجل إرجاع قيم أي ميزة إلى المجال [0-1] من أجل كل قيمة في ميزة بطرح أصغر قيمة للميزة من هذه القيمة، ومن ثم القسمة على ناتج طرح أعلى قيمة للميزة من أصغر قيمة للميزة (مجال الميزة). تّدعى هذه العملية أحيانًا بالتسوية normalization أو التوحيد standardization. يُمكن لنا برمجة العملية الحسابية السابقة طبعًا بسهولة، إلا أننا نستخدم الكائن MinMaxScaler في Scikit-Learn والذي ينجزها، وتُطابق شيفرة التحجيم شيفرة الحساب السابق مع استبدال scaler بـ imputer. سنؤكد على أننا نُدرّب باستخدام بيانات التدريب فقط ونحسب لكل البيانات بما فيها بيانات الاختبار كما نوّه سابقًا. # إنشاء كائن التحجيم مع المجال 0 إلى 1 scaler = MinMaxScaler(feature_range=(0, 1)) # الملاءمة مع بيانات التدريب scaler.fit(X) # تحويل كل من بيانات التدريب والاختبار X = scaler.transform(X) X_test = scaler.transform(X_test) تكون لكل ميزة بعد تنفيذ هذه الشيفرة قيمة صغرى هي 0 وقيمة عظمى هي 1. يُستحسن فهم كل من عملية احتساب القيم الناقصة وعملية تحجيم الميزات جيدًا لأنهما موجودتان عمليًا في أي مشروع تعلّم آلة. تنفيذ نماذج تعلم الآلة باستخدام Scikit-Learn يُعدّ إنشاء النماذج وتدريبها ومن ثم استخدامها للتنبؤ أمرًا يسيرًا بعد أن أنهينا مراحل تنظيف البيانات وإعدادها. نستخدم المكتبة Scikit-Learn في بايثون والتي يتوفر لها توثيق ممتاز وطريقة بناء موحدة لكل نماذج التعلّم، مما يسمح بتنفيذ العديد من النماذج بسرعة. تُبين الشيفرة التالية آلية إنشاء نموذج الانحدار المعزز بالتدرج Gradient Boosted Regression وتدريبه باستخدام تابع الملاءمة fit، ومن ثم اختباره باستخدام تابع التنبؤ predict. from sklearn.ensemble import GradientBoostingRegressor # إنشاء النموذج gradient_boosted = GradientBoostingRegressor() # ملاءمة النموذج مع بيانات التدريب gradient_boosted.fit(X, y) # التنبؤ باستخدام بيانات الاختبار predictions = gradient_boosted.predict(X_test) # تقويم النموذج mae = np.mean(abs(predictions - y_test)) print('Gradient Boosted Performance on the test set: MAE = %0.4f' % mae) حيث يكون الناتج: Gradient Boosted Performance on the test set: MAE = 10.0132 لاحظ أن كل عملية من العمليات الأساسية (إنشاء وتدريب واختبار) تأخذ سطرًا واحدًا فقط، وبالتالي لبناء بقية النماذج يكفي تغيير اسم النموذج المطلوب في الشيفرة. يُبين الشكل التالي نتائج النماذج المختبرة: تُثبت الأرقام في الشكل أن جميع نماذج تعلّم الآلة قابلة للتطبيق في مسألتنا لأنها جميعها تتميز بمتوسط قيم خطأ مطلق أصغر من خط الأساس المُحدّد لمسألتنا (وهو 24.5) والذي حُسب باستخدام القيمة الأوسط للهدف (معامل نجمة الطاقة). لاحظ تقارب متوسط الخطأ المطلق لكل من نموذج الانحدار المعزز MAE=10.013 ونموذج الغابة العشوائية MAE=10.014. قد لا تكون الموازنة في هذه المرحلة عادلةً بين النماذج لاسيما بالنسبة لنموذج آلة متجهة الدعم support vector machine لأننا تركنا القيم الافتراضية لمعاملات النماذج دون أي معايرة أو ضبط. معايرة المعاملات الفائقة وصولا لنموذج أمثلي يُمكن الوصول لنموذج أمثلي بمعايرة معاملاته الفائقة وفق معطيات المشروع. لنُبين أولًا الفرق بين المعاملات الفائقة لنموذج والمعاملات الأخرى له: المعاملات الفائقة hyperparameters: هي إعدادات خوارزمية التعلّم قبل التدريب (والتي وضعها مصممو الخوارزمية) مثل عدد الجيران في نموذج أقرب الجيران أو عدد الأشجار في نموذج الغابة العشوائية. المعاملات parameters: هي المعاملات التي يتعلّمها النموذج أثناء التدريب مثل أوزان نموذج الانحدار الخطي. تؤثر عملية معايرة المعاملات الفائقة على أداء النموذج لاسيما لجهة التوزان المطلوب بين مشكلة قلة التخصيص underfitting ومشكلة فرط التخصيص overfitting، واللتان تؤديان إلى نموذج غير قادر على تعميم أمثلة التدريب، وبالتالي لن يتمكن من التنبؤ مع معطيات جديدة. ويُمكن العودة للرابط من أكاديمية حسوب للمزيد من التفصيل حول هاتين المشكلتين. تظهر مشكلة قلة التخصيص عندما لا يكون للنموذج درجات حرية كافية ليتعلّم الربط بين الميزات والهدف، وبالتالي يكون له انحياز كبير نحو قيم معينة للهدف. يُمكن تصحيح قلة التخصيص بجعل النموذج أكثر تعقيدًا، بينما تظهر مشكلة فرط التخصيص عندما يخزن النموذج بيانات التدريب، فيكون له بالتالي تباين كبير، والذي يُمكن تصحيحه بالحد من تعقيد النموذج باستخدام التسوية regularization. تكمن المشكلة في معايرة المعاملات الفائقة بأن قيمها المثلى تختلف من مسألة لأخرى، وبالتالي تُعَد الطريقة الوحيدة للوصول لهذه القيم المثلى هي تجريب قيم مختلفة مع كل مجموعة بيانات تدريب جديدة. يحاول الكثيرون مثل مخبر Epistasis الوصول للقيم المثلى باستخدام خوارزميات مناسبة مثل الخوارزميات الجينية، إذ يوفر Scikit-Learn لحسن الحظ العديد من الطرق لتقويم المعاملات الفائقة وبالتالي سنعتمد في مشروعنا عليها دون تعقيد الأمور أكثر. البحث العشوائي مع التقييم المتقاطع تُدعى الطريقة التي سنستخدمها في إيجاد القيم المثلى بالبحث العشوائي مع التقويم المتقاطع random search with cross validation: البحث العشوائي: نُعرّف شبكة grid من قيم المعاملات الفائقة، ومن ثم نختار تركيبات مختلفة منها عشوائيًا، أي أننا لا نختار كل التركيبات الممكنة مثل سلوك البحث الشبكي (يكون أداء البحث العشوائي لحسن الحظ قريب من البحث الشبكي مع تخفيض كبير في الزمن اللازم). التقييم المتقاطع: وهو الطريقة المستخدمة لتقويم مجموعة قيم محدّدة للمعاملات الفائقة، عوضًا عن تقسيم البيانات إلى بيانات للتدريب وبيانات للتقويم، مما يُخفّض من البيانات التي يُمكن لنا استخدامها للتدريب، كما نستخدم التقويم المتقاطع مع عدد محدّد من الحاويات K-Fold. إذ تُقسم بيانات التدريب إلى عدد K من الحاويات ومن ثم ننكرر ما يلي K مرة: في كل مرة ندرب النموذج مع بيانات K-1 حاوية، ومن ثم تقويمه مع بيانات الحاوية K. حيث يكون مقياس الأداء النهائي هو متوسط الخطأ لكل التكرارات. يوضح الشكل التالي فكرة التقويم المتقاطع من أجل K=5: يُمكن تلخيص خطوات البحث العشوائي مع التقويم المتقاطع كما يلي: إعداد شبكة من المعاملات الفائقة. اختيار مجموعة عشوائية من تركيبات قيم المعاملات الفائقة. إنشاء نموذج مع قيم المعاملات المختارة. تقويم النموذج باستخدام التقويم المتقاطع. اختيار تركيب قيم المعاملات ذو الأداء الأفضل. لن نبرمج هذه الخطوات طبعًا لأن الكائن RandomizedSearchCV في Scikit-Learn ينجز كل ذلك. طرق التعزيز المتدرج مرة أخرى يُعدّ نموذج الانحدار المعزز بالتدرج Gradient Boosted Regression (المستخدم في حالتنا) من طرق المجموعات، بمعنىً أنه يدرب مجموعةً من المتدربين الضعفاء (أشجار قرار) تسلسليًا، وبحيث أن كل متدرب يستفيد من أخطاء المتدرب السابق، بخلاف نموذج الغابة العشوائية والذي يدرب مجموعةً من المتدربين الضعفاء على التوازي ومن ثم يتنبأ عن طريق الانتخاب بينهم. تصدرت طرقُ التعزيز طرقَ تعلّم الآلة في السنوات الأخيرة لاسيما بعد فوزها في العديد من مسابقات التعلّم، إذ تستخدم طريقة التعزيز المتدرجِ النزولَ المتدرجَ Gradient Descent لتخفيض تابع الكلفة عن طريق تدريب المتدربين واحدًا تلو الآخر، بحيث يستفيد كل متدرب من أخطاء المتدرب السابق. تسبق بعض المكتبات أداء المكتبة Scikit-Learn المستخدمة في مشروعنا مثل XGBoost، إلا أننا سنحافظ على استخدامها مع بياناتنا الصغيرة نسبيًا نظرًا لدقتها الواضحة. معايرة المعاملات الفائقة في نموذج الانحدار المعزز بالتدريج يُمكن العودة لتوثيق Scikit-Learn لتفاصيل المعاملات الفائقة، والتي سنجد القيم الأمثل لها: loss تابع الخسارة والذي يجب تخفيضه. n_estimators عدد المتدربين الضعفاء (أشجار القرار). max_depth العمق الأعظم لشجرة القرار. min_samples_leaf العدد الأصغر للأمثلة في ورقة شجرة قرار. min_samples_split العدد الأصغر المطلوب لتقسيم عقدة في شجرة قرار. max_features العدد الأعظم للميزات المطلوب لتقسيم عقدة في شجرة قرار. قد لا نجد أحدًا يفهم كيفية تأثير هذه المعاملات على بعضها البعض، ولابدّ من التجريب للوصول إلى القيم المثلى لها. نبني في الشيفرة التالية شبكةً من قيم المعاملات الفائقة، حيث نُنشئ كائن RandomizedSearchCV ونبحث باستخدام 4 حاويات للتقويم المتقاطع مع 25 تركيبةً مختلفةً لقيم المعاملات الفائقة: # تابع الخسارة المطلوب تخفيضه loss = ['ls', 'lad', 'huber'] # عدد أشجار القرار المستخدمة n_estimators = [100, 500, 900, 1100, 1500] # العمق الأعظم لكل شجرة max_depth = [2, 3, 5, 10, 15] # عدد الأمثلة الأصغر لكل ورقة min_samples_leaf = [1, 2, 4, 6, 8] # عدد الأمثلة الأصغر لتقسيم عقدة min_samples_split = [2, 4, 6, 10] # العدد الأعظم للميزات المستخدمة لتقسيم عقدة max_features = ['auto', 'sqrt', 'log2', None] # تعريف شبكة المعاملات الفائقة للبحث فيها hyperparameter_grid = {'loss': loss, 'n_estimators': n_estimators, 'max_depth': max_depth, 'min_samples_leaf': min_samples_leaf, 'min_samples_split': min_samples_split, 'max_features': max_features} # إنشاء نموذج معايرة المعاملات الفائقة model = GradientBoostingRegressor(random_state = 42) # إعداد البحث العشوائي مع 4 حاويات للتقويم المتقاطع random_cv = RandomizedSearchCV(estimator=model, param_distributions=hyperparameter_grid, cv=4, n_iter=25, scoring = 'neg_mean_absolute_error', n_jobs = -1, verbose = 1, return_train_score = True, random_state=42) # الملاءمة مع بيانات التدريب random_cv.fit(X, y) نعاين الكائن RandomizedSearchCV بعد الانتهاء من البحث لنعرف النموذج الأمثل: # إيجاد أفضل تركيبة ممكنة من القيم random_cv.best_estimator_ GradientBoostingRegressor(loss='lad', max_depth=5, max_features=None, min_samples_leaf=6, min_samples_split=6, n_estimators=500) يُمكن لنا استخدام هذه النتائج لإنجاز البحث الشبكي باستخدام معاملات للشبكة قريبة من هذه المعاملات المثلى. لن يؤدي المزيد من البحث والمعايرة إلى تحسين ملحوظ على الأرجح، وذلك لأن عملية هندسة الميزات التي عملنا عليها في البداية كان لها التأثير الأكبر في تحسين نتائج النموذج، إذ يطبق في الحقيقة أيضًا قانون تناقص العوائد law of diminishing في تعلّم الآلة والذي يقول: لقد أعطت هندسة الميزات الأثر الأكبر في تحسين النموذج ولن تؤدي معايرة المعاملات الفائقة إلا للقليل من التحسن. يُمكن تجربة تغيير عدد المتدربين مثلًا (أشجار القرار)، مع الحفاظ على بقية قيم المعاملات الفائقة ثابتةً مما يسمح لنا بمعاينة تأثير هذا المعامل. يُمكن الاطلاع على الشيفرة الموافقة والتي تعطي النتائج التالية: لاحظ أنه كلما ازداد عدد الأشجار يقل الخطأ سواءً في بيانات التدريب أو في بيانات الاختبار، كما يتناقص الخطأ في التدريب بسرعة أكبر من الخطأ في الاختبار. لكن يبدو أن نموذجنا ذو فرط تخصيص overfitting، حيث أن له أداءً ممتازًا مع بيانات التدريب لا يصل له مع بيانات الاختبار. نتوقع دائمًا بعض الانخفاض في الأداء مع بيانات الاختبار عن بيانات التدريب (لا تنسى أن النموذج يرى كل بيانات التدريب)، لكن وجود فارق كبير في الأداء بين التدريب والاختبار يعني مشكلة فرط التخصيص، والتي يُمكن حلها بزيادة بيانات التدريب أو تخفيض تعقيد النموذج عبر المعاملات الفائقة. نحافظ على قيم المعاملات الفائقة كما هي ونترك للقارئ محاولة البحث عن حل لمشكلة فرط التخصيص. نعتمد من أجل النموذج النهائي القيمة 800 لمعامل عدد الأشجار لأنها تُعطي أقل خطأ نتيجة التقويم المتقاطع. التقويم باستخدام بيانات الاختبار سنستخدم بيانات الاختبار والتي أخفيناها تمامًا عن نموذج التعلّم أثناء مرحلة التدريب، إذ سيُعطي اختبار النموذج طبعًا مع هذه البيانات مؤشرًا لأداء النموذج مع البيانات الحقيقية لاحقًا. تحسب الشيفرة التالية متوسط الخطأ المطلق (معيار الأداء) لكل من النموذج الأولي والنموذج النهائي الذي حصلنا عليه بعد معايرة المعاملات الفائقة: # التنبؤ باستخدام النموذج الأولي والنهائي default_pred = default_model.predict(X_test) final_pred = final_model.predict(X_test) Default model performance on the test set: MAE = 10.0118. Final model performance on the test set: MAE = 9.0446. أدت معايرة المعاملات الفائقة إلى تحسين الأداء بنسبة 10% تقريبًا وهي نسبة قد تكون مهمةً في التطبيقات الحقيقية رغم الوقت الذي خصصناه لتنفيذها. يُمكن معايرة الوقت الذي يستغرقه تدريب النموذج باستخدام التعليمة السحرية ‎%timeit في محيط التطوير المستخدم Jupyter Notebooks: %%timeit -n 1 -r 5 default_model.fit(X, y) ويكون ناتج الشيفرة مثلًا: 1.09 s ± 153 ms per loop (mean ± std. dev. of 5 runs, 1 loop each) تُظهر النتيجة أن تدريب النموذج الأولي يحتاج لحوالي ثانية واحدة فقط وهو زمن معقول جدًا؛ أما النموذج النهائي فهو ليس بهذه السرعة إذ يستغرق حوالي 12 ثانيةً: %%timeit -n 1 -r 5 final_model.fit(X, y) حيث يكون ناتج الشيفرة مثلًا: 12.1 s ± 1.33 s per loop (mean ± std. dev. of 5 runs, 1 loop each) يُعزز ذلك مبدأ المقايضة trade-offs العام في تعلّم الآلة، إذ نسعى دومًا لمقايضة الدقة مع قابلية التفسير وانحراف النتائج مع تباينها، والدقة مع زمن التنفيذ وغيرها. ربما يبدو 12 ضعفًا رقمًا كبيرًا نسبيًا إلا أنه ليس بهذا السوء مطلقًا. يُمكننا الآن وبعد حصولنا على التنبؤات النهائية فحص فيما إذا يوجد أي انحراف أو مشكلة ما فيها. يُبين الشكل التالي (على اليسار) مخطط الكثافة لتوقعات النموذج (الأزرق) مع القيم الحقيقية (الأحمر)؛ أما على اليمين فيَظهر المدرج التكراري لقيمة الخطأ المرتكب. يُبين مخطط الكثافة أن توقعات النموذج تتبع تقريبًا توزع القيم الحقيقية على الرغم من أن قمة كثافة التوقعات قريبة من القيمة الأوسط لقيم التدريب (66)، بينما تكون قمة كثافة القيم الحقيقية أقرب للقيمة العظمى (100)؛ أما المدرج التكراري فيُظهر توزعًا طبيعيًا تقريبًا للخطأ المرتكب مع ملاحظة وجود قيم سالبة للخطأ مما يعني توقعات أقل من الحقيقة. كما سنُلقي في المقالة التالية ضوءًا أكبر على النتائج. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن النتائج تابعنا في هذه المقالة استعراض العديد من الخطوات في معالجة مسألة تعلّم الآلة: احتساب القيم الناقصة وتحجيم الميزات. تقويم وموازنة عدة نماذج تعلّم. معايرة المعاملات الفائقة باستخدام البحث العشوائي الشبكي والتقويم المتقاطع. تقويم النموذج الأفضل مع بيانات الاختبار. تُبرهن النتائج على إمكانية استخدام البيانات المتوفرة لبناء نموذج تعلّم الآلة بهدف توقع معامل نجمة الطاقة للمباني، إذ يتنبأ نموذج التعلّم من نمط الانحدار المعزز بالتدرج بمعامل نجمة الطاقة مع خطأ من مرتبة 9.1 نقطةً بالنسبة للقيمة الحقيقية في بيانات الاختبار. وقد ساعدت عملية معايرة المعاملات الفائقة رغم الوقت المستغرق لإنجازنا لها لتحسين أداء النموذج، وهي واحدة من المقايضات التي ننجزها في مسائل تعلّم الآلة. سنحاول في المقالة التالية فهم النموذج المتعلّم الدقيق الذي توصلنا له أكثر لاسيما آلية قيامه بالتنبؤات، كما سنحاول تحديد العوامل المؤثرة في معامل نجمة الطاقة. فمع العلم بأن النموذج المتعلّم الذي توصلنا إليه يبدو دقيقًا، إلا أننا نريد أن نفهم آلية وصوله لنتائجه. ترجمة -وبتصرف- للمقال A Complete Machine Learning Project Walk-Through in Python: Part Two لكاتبه Will Koehrsen. اقرأ أيضًا دوال الكثافة الاحتمالية في بايثون المرجع الشامل إلى تعلم لغة بايثون
  4. قد تظن عندما تتابع دورةً تدريبيةً أو تقرأ كتابًا في علم البيانات، أنك تملكت من المطلوب لتنفيذ مشروعًا كاملًا في تعلّم الآلة، لكن لن تكون لديك الخبرة الكافية بالضرورة لتكامل جميع أجزاء ومراحل تطوير نظام تعلّم آلة لحل مسألة حقيقية على أرض الواقع. ربما يُشكّل تنفيذك المشروع الأول تحديًا كبيرًا لكن إتمامك له بنجاح سيمنحك الثقة والخبرة اللازمتين لتنفيذ مشاريع لاحقة بعدها. تعرض هذه السلسلة من المقالات آلية تسلسل خطوات معالجة مسائل تعلّم الآلة عبر القيام بتنفيذ مشروع متكامل مع بيانات حقيقية. نعمل وفق المنهجية العامة المتبعة عادةً في مسائل تعلّم الآلة خطوةً بخطوة وفق ما يلي: تنظيف البيانات وتنسيقها. استكشاف وتحليل البيانات. هندسة الميزات واختيار المناسب منها. اعتماد مقاييس الأداء وموازنة نماذج التعلّم وفقها. ضبط قيم المعاملات الفائقة لنموذج التعلّم الأفضل. تقويم النموذج الأفضل باستخدام مجموعة بيانات الاختبار. تفسير وشرح نتائج النموذج. عرض الاستنتاجات وتوثيق العمل. نُنفذ فيما يلي كل خطوة من الخطوات السابقة مع بيان تكامل كل خطوة مع الخطوة التالية لها، حيث نعرض في هذه المقالة الخطوات الثلاث الأولى ونتابع في مقالات لاحقة الخطوات المتبقية، ويُمكن الحصول على الشيفرة الكاملة للمشروع من هذا المستودع. تعريف المسألة قبل البدء بالعمل البرمجي لابدّ من فهم المسألة ومعاينة البيانات المتاحة. نحلل في هذا المشروع ومعالجة بيانات الطاقة لمدينة نيويورك والمنشورة للعموم. يهدف المشروع إلى تحليل بيانات الطاقة للوصول إلى نموذج متعلّم يُمكنه التنبؤ بمعامل نجمة الطاقة Energy Star Score الذي يعتمده برنامج نجمة الطاقة وهو برنامج تديره وكالة حماية البيئة الأمريكية ووزارة الطاقة الأمريكية لتعزيز كفاءة الطاقة. كما يجب في النهاية تفسير النتائج لمعرفة العوامل التي يُمكنها التأثير في هذا المعامل. تحوي البيانات المتاحة قيم معامل نجمة الطاقة، مما يعني أن المسألة المطروحة هي مسألة تعلّم آلة من نمط موجه عبر الانحدار supervised regression machine learning: تعليم موجه Supervised: تحوي البيانات المتاحة الميزات والنتيجة معًا، لذلك تدريب نموذج يتعلّم الربط بين هذه الميزات والنتيجة هو المطلوب في هذا النمط. عبر الانحدار Regression: لأن المعامل المطلوب هو قيمة رقمية حقيقية. نهدف إلى بناء نموذج تعلّم دقيق يُمكنه التنبؤ بقيمة قريبة من القيمة الحقيقية لمعامل نجمة الطاقة، ويجب أن تكون نتائج النموذج المبني قابلةً للتفسير والفهم أيضًا. توجّه هذه الأهداف قراراتنا المتخَذة لاحقًا أثناء تحليلنا البيانات وبناء النموذج. تنظيف البيانات تُعَد عمليات تنظيف البيانات وتنسيقها في البداية من العمليات الأساسية، إذ أن بيانات العالم الحقيقة غالبًا ما تكون ناقصةً وتحوي العديد من القيم المتطرفة (بخلاف مجموعات بيانات التدريب المُعدّة لأهداف تعليمية مثل mtcars وiris). حيث تكون بيانات العالم الحقيقية في فوضى كبيرة، مما يستلزم تنظيفها وإعادة تشكيلها قبل البدء بتحليلها. قد تبدو مهمة تنظيف البيانات غير ممتعة إلا أنها ضرورية في جميع مسائل العالم الحقيقي. نبدأ أولًا بتحميل البيانات ضمن إطار من البيانات DataFrame من مكتبة Pandas ومن ثم عرضها: import pandas as pd import numpy as np # قراءة البيانات وتحميلها ضمن إطار من البيانات data = pd.read_csv('data/Energy_and_Water_Data_Disclosure_for_Local_Law_84_2017__Data_for_Calendar_Year_2016_.csv') # إظهار الجزء الأعلى من إطار البيانات data.head() يظهر لنا أولًا: يُظهِر الشكل مجموعةً جزئيةً من البيانات والتي تحوي 60 عمودًا، مما يطرح المسألة الأولى التالية: نريد التنبؤ بمعامل نجمة الطاقة Energy Star Score، إلا أننا لا نعرف أي عمود من الأعمدة يوافق هذا المعامل. قد تكون عدم معرفة معاني الأعمدة في بعض المسائل غير مهمة ويُمكن بناء نماذج دقيقة إلا أنه في حالتنا، وبما أن المطلوب تفسير النتائج فيجب فهم معاني بعض الأعمدة على الأقل. لم يُرد كاتب المقال عندما كُلّف بالعمل على هذا المشروع، سؤال أحد عن معاني الأعمدة، إلا أنه عندما تمعن في اسم الملف: قرر البحث عن القانون المدعو بـ Local Law 84، مما قاده إلى هذه الصفحة التي تفرض على جميع مباني مدينة نيويورك اعتبارًا من حجم معين، تقديم تقرير عن استهلاك الطاقة فيها. وبمزيد من البحث توصل إلى تعريفات جميع الأعمدة. يجب بدء العمل بتأن وروية دائمًا وعدم نسيان أي أمر قد يكون هامًا مثل معاينة اسم الملف، إذ لا نحتاج عمليًا لدراسة جميع الأعمدة، إلا أنه يجب أن نفهم معامل نجمة الطاقة والموصّف على أساس نسبة مئوية (رقم صحيح بين 1 و100) لتقييم استهلاك الطاقة في سنة معينة، والذي يُقدّر لكل بناء في مدينة نيويورك، وهو مقياس نسبي يُستخدم لموازنة كفاءة الطاقة في المباني. بعد حل المسألة الأولى نلتفت إلى المسألة الثانية وهي مشكلة القيم الناقصة، إذ تحوي البيانات المتاحة عبارة غير متوفر Not Available في الكثير من الخلايا التي لا تُعرَف قيمتها، ستُجبِر هذه القيمة النصية بايثون على تخزين العمود (ولو كان عمودًا رقميًا) كائن مثلًا object وذلك لأن مكتبة Pandas تعد جميع قيم العمود نصية بمجرد وجود قيمة نصية واحدة ضمن هذا العمود، ويُمكن معاينة نوع بيانات الأعمدة باستخدام طريقة إطار البيانات dataframe.info: # معاينة بيانات الأعمدة والقيم غير الناقصة data.info() ويكون الناتج: تُخزّن الكثير من الأعمدة الرقمية مثل كائن object مشابه للمساحة بالأقدام المربعة ft2، مما يستلزم تحويلها إلى رقم حقيقي float كي نستطيع إنجاز عمليات التحليل المطلوبة والتي لا يُمكن تطبيقها على السلاسل النصية. سنعرض فيما يلي شيفرة بايثون التي تحول السلسلة النصية Not Available إلى القيمة np.nan "ليس رقمًا" not a number والتي يُمكن معاملتها مثل الأرقام ومن ثم تحويل العمود الموافق إلى نمط البيانات عدد حقيقي float: # استبدال القيم الناقصة data = data.replace({'Not Available': np.nan}) # المرور على الأعمدة عمودًا عمودًا for col in list(data.columns): # اختيار الأعمدة التي يجب أن تكون رقميةً if ('ft²' in col or 'kBtu' in col or 'Metric Tons CO2e' in col or 'kWh' in col or 'therms' in col or 'gal' in col or 'Score' in col): # تحويل نمط البيانات إلى عدد data[col] = data[col].astype(float) بعد الانتهاء من تعديل الأعمدة المناسبة إلى أرقام، ننتقل لمعاينة البيانات. البيانات الناقصة والمتطرفة من المشاكل الشائعة أيضًا وجود العديد من القيم الناقصة في البيانات لأسباب عديدة. يجب حذف هذه القيم أو إيجاد طريقة لملئها بقيم متوقعة قبل بناء نموذج التعلّم، لنبدأ أولًا بتحديد حجم هذه المشكلة لكل عمود (يُمكن العودة لرابط المشروع من أجل الحصول على الشيفرة). نُظهر الجدول التالي والذي يحسب نسبة القيم الناقصة لكل عمود باستخدام الشيفرة الموجودة في هذا السؤال من موقع Stack Overflow. يجب توخي الحذر عند حذف عمود يحوي نسبةً كبيرةً من القيم الناقصة إذ قد يكون مفيدًا لنموذج التعلّم المطلوب. تعتمد العتبة والتي من فوقها نحذف العمود على المسألة المطروحة (تجد في هذا الرابط مناقشة لمسألة العتبة) وفي مشروعنا سنحذف أي عمود يحوي أكثر من 50% قيمًا ناقصةً. نحذف القيم المتطرفة أيضًا في هذه المرحلة والتي يُمكن أن تظهر في البيانات نتيجة أخطاء طباعية أو أخطاء في الوحدات units المستخدمة، لكن يجب الملاحظة أنها في بعض الأحيان قد تكون صحيحةً إلا أنها بعيدةً كثيرًا عن باقي القيم. سنحذف القيم بالاعتماد على تعريف القيم المتطرفة البعيدة التالي: أقل من الربع الأول – 3 * الانحراف الربيعي. فوق الربع الثالث + 3 * الانحراف الربيعي. يُمكن العودة للمشروع للاطلاع على شيفرة حذف الأعمدة والقيم المتطرفة. في نهاية هذه المرحلة، بقي لدينا أكثر من 11000 مبنىً مع 49 ميزةً. تحليل البيانات الاستكشافي بعد الانتهاء من المرحلة السابقة الضرورية جدًا رغم صعوبتها بعض الشيء، يُمكن لنا الانتقال إلى مرحلة تحليل البيانات الاستكشافي، والذي نعني به تطبيق بعض الحسابات الاحصائية ورسم بعض المخططات بهدف إيجاد الاتجاهات العامة trends والمتطرفات anomalies والأنماط patterns والعلاقات relationships الموجودة ضمن البيانات. وباختصار يُعدّ الهدف من هذه المرحلة هو استكشاف المعلومات المُضمّنة في البيانات والتي يُمكن لها لعب دورًا مهمًا في توجيه خياراتنا عند بناء نماذج التعلّم، مثلًا: من هي الميزات الأكثر ارتباطًا مع الهدف؟ نعمل عادةً هذه الخطوة بشكل تدريجي، أي نبدأ من نظرة عامة كلية قد تقودنا في بعض الأحيان إلى التركيز على بيانات محدّدة معينة. رسم متغير وحيد هدفنا التنبؤ بمعامل نجمة الطاقة (ندعوه 'score' في بياناتنا)، لذلك من الطبيعي البدء باستكشاف توزع هذا المعامل، وبالطبع فأسهل شيء يُمكن اللجوء له هو رسم المدرج التكراري Histogram، والذي يسمح بمعاينة توزع متغير لاسيما أنه يُمكن إنجاز ذلك بسهولة باستخدام المكتبة matplotlib كما يلي: import matplotlib.pyplot as plt # المدرج التكراري لمعامل نجمة الطاقة plt.style.use('fivethirtyeight') plt.hist(data['score'].dropna(), bins = 100, edgecolor = 'k') plt.xlabel('Score'); plt.ylabel('Number of Buildings') plt.title('Energy Star Score Distribution') ويكون الناتج ما يلي: يُظهِر هذا المدرج التكراري الملاحظة التالية المثيرة للانتباه: بالرغم من أن مُعامل نجمة الطاقة هو نسبة مئوية، أي يُفترض تشكيل توزيعًا منتظمًا تقريبًا، إذ يوجد اختلاف كبير بين عدد الأبنية ذات معامل يساوي 100 وعدد الأبنية ذات معامل يساوي 1، أي أنه كلما كان معامل نجمة الطاقة كبيرًا كانت كفاءة الطاقة أفضل. إذا عُدنا إلى تعريف معامل نجمة الطاقة فسنجد أنه تقدير نسبي يُعطيه الأشخاص مما يُفسّر الظاهرة السابقة، لربما كان الطلب من سكان الأبنية تقدير استهلاكهم من الطاقة يشابه الطلب من طلاب صف تقويم أنفسهم في امتحان ما، وبالنتيجة فإننا نعتقد أن هذا المعيار غير موضوعي لقياس كفاءة الطاقة في الأبنية. يُمكن التحري عن السبب في اختلاف قيم المعيار بين الأبنية عن طريق إيجاد الميزات المشتركة بين الأبنية التي لها تقريبًا نفس قيم المعيار؛ إلا أننا لن نفعل ذلك لأن مهمتنا هي التنبؤ بقيم هذا المعيار وليس إيجاد طرق أفضل لتقويم طاقة الأبنية. سنضع في تقريرنا النهائي ملاحظاتنا عن التوزيع غير المنتظم إلا أننا سنركز على مسألة التنبؤ. إيجاد العلاقات تهدف مرحلة تحليل البيانات الاستكشافية أساسًا إلى إيجاد العلاقات الكامنة بين الميزات وبين المعامل الهدف، حيث تكون الميزات المترابطة مع الهدف مفيدةً جدًا لنموذج التعلّم، لأنه يُمكن استخدامها في عملية التنبؤ بالهدف. يُمكن رسم مخطط الكثافة لاستكشاف تأثير متغير فئوي (يأخذ مجموعةً مُحدّدةً من القيم) على الهدف وذلك باستخدام المكتبة seaborn، ويُمكن النظر لمخطط الكثافة مثل تنعيم للمدرج التكراري لأنه يُظهر توزع متغير وحيد أيضًا. يُمكن تلوين مخطط الكثافة حسب الفئة لنرى كيف يؤثر المتغير الفئوي على التوزيع، حيث ترسم الشيفرة التالية مخططات الكثافة لمعامل الطاقة ملون حسب نوع المبنى (يقتصر على أنواع المباني التي تحتوي على أكثر من 100 سطر بيانات): # إنشاء قائمة من الأبنية التي لها أكثر من 10 قياس types = data.dropna(subset=['score']) types = types['Largest Property Use Type'].value_counts() types = list(types[types.values > 100].index) # رسم توزيع المعامل وفق فئة البناء figsize(12, 10) # رسم كل بناء for b_type in types: # اختيار فئة البناء subset = data[data['Largest Property Use Type'] == b_type] # رسم مخطط الكثافة لمعامل الطاقة sns.kdeplot(subset['score'].dropna(), label = b_type, shade = False, alpha = 0.8); # عنونة المخطط plt.xlabel('Energy Star Score', size = 20); plt.ylabel('Density', size = 20); plt.title('Density Plot of Energy Star Scores by Building Type', size = 28); ويكون الناتج مخططات الكثافة حسب نوع البناء (الأزرق للمنازل السكنية، والأحمر للمكاتب، أما الأصفر فللفنادق، بينما الأخضر فالمستودعات غير المبردّة): تُبرهن المخططات أعلاه على أن نوع المبنى يؤثر كثيرًا في معامل نجمة الطاقة، إذ يكون لمباني المكاتب معاملات عالية بينما تأخذ الفنادق القيم الأدنى، مما يستلزم تضمين نوع المبنى في نموذج التعلّم لأنه يؤثر بوضوح على الهدف بصفة متغير فئوي، وسيتوجب علينا استخدام ترميز ساخن واحد one-hot. يُمكن تكرار العمل لدراسة العلاقة بين حي السكن ومعامل الطاقة حيث ينتج لدينا: تُبرهن المخططات أعلاه عدم وجود تأثير كبير للحي على معامل نجمة الطاقة، ومع ذلك سنضمن الحي في نموذجنا نظرًا لوجود بعض الاختلافات الطفيفة بين الأحياء. يُمكن استخدام معامل الارتباط لبيرسون Pearson لتحديد نوع العلاقات بين المتغيرات: القيمة 1 تعني علاقة تناسب طردي. القيمة -1 تعني تناسب عكسي. أما القيمة 0 تعني عدم وجود أي علاقة. يُبين الشكل التالي بعض قيم معامل الارتباط لبيرسون: لا يكشف معامل الارتباط العلاقات غير الخطية وعلى الرغم من ذلك، فهو يبقى في البداية طريقةً جيدةً لاستكشاف ترابط المتغيرات مع بعضها البعض، يُمكن بسهولة إيجاد معاملات الارتباط باستخدام المكتبة Pandas: # إيجاد قيم جميع الارتباطات وترتيبها تصاعديًا correlations_data = data.corr()['score'].sort_values() يُبين الشكل التالي علاقات الارتباط الناتجة السلبية (على اليسار، تناسب عكسي) والإيجابية (على اليمين، تناسب طردي): نلاحظ وجود العديد من الارتباطات السلبية (العكسية) بين الميزات ومعامل نجمة الطاقة الهدف. لنأخذ مثلًا الميزة EUI والتي هي كثافة استخدام الطاقة في المبنى أي الطاقة المستهلكة للمبنى مقسومًا على مساحة المبنى، بالطبع كلما كان هذا المقدار أصغر كانت الكفاءة أكبر (والتي يُعبّر عنها مقياس نجمة الطاقة). رسم مخطط متغيرين لمعاينة العلاقة بين متغيرين مستمرين (قيم رقمية حقيقية) يُمكن اللجوء إلى المخططات من النمط المبعثر scatterplots، كما يُمكن إضافة معلومات أخرى مثل المتغيرات الفئوية لتلوين نقاط المخطط، حيث يُبين المخطط التالي العلاقة بين كثافة استخدام الطاقة EUI ومعامل نجمة الطاقة ملونة حسب نوع البناء: يُظهر هذا المخطط علاقةً عكسيةً بين كثافة استخدام الطاقة ومعامل نجمة الطاقة، فكلما انخفضت الكثافة ارتفع معامل نجمة الطاقة (يكون معامل الترابط حوالي -0.7). أخيرًا، لنرسم مخطط باريس Paris Plot، والذي يوفر أداةً ممتازةً لاستكشاف البيانات، إذ يُمكّننا من معاينة العلاقات بين عدة أزواج من المتغيرات إضافًة إلى توزيعات متغيرات وحيدة. سنستخدم مكتبة المعاينة seaborn والتابع PairGrid لرسم مخطط باريس وحيث نضع في المثلث العلوي المخططات المتناثرة وعلى القطر المدرجات التكرارية، بينما نضع مخططات كثافة النواة ثنائية الأبعاد 2D kernel density مع معاملات الترابط في المثلث السفلي. # تحديد أعمدة المخطط plot_data = features[['score', 'Site EUI (kBtu/ft²)', 'Weather Normalized Source EUI (kBtu/ft²)', 'log_Total GHG Emissions (Metric Tons CO2e)']] # استبدال اللانهاية بليس رقمًا plot_data = plot_data.replace({np.inf: np.nan, -np.inf: np.nan}) # إعادة تسمية الأعمدة plot_data = plot_data.rename(columns = {'Site EUI (kBtu/ft²)': 'Site EUI', 'Weather Normalized Source EUI (kBtu/ft²)': 'Weather Norm EUI', 'log_Total GHG Emissions (Metric Tons CO2e)': 'log GHG Emissions'}) # حذف القيم غير الرقمية plot_data = plot_data.dropna() # تابع حساب معامل الارتباط بين عمودين def corr_func(x, y, **kwargs): r = np.corrcoef(x, y)[0][1] ax = plt.gca() ax.annotate("r = {:.2f}".format(r), xy=(.2, .8), xycoords=ax.transAxes, size = 20) # إنشاء كائن الشبكة grid = sns.PairGrid(data = plot_data, size = 3) # الأعلى هو المخطط المبعثر grid.map_upper(plt.scatter, color = 'red', alpha = 0.6) # القطر هو المدرج التكراري grid.map_diag(plt.hist, color = 'red', edgecolor = 'black') # الأسفل هو مخطط الارتباط والكثافة grid.map_lower(corr_func) grid.map_lower(sns.kdeplot, cmap = plt.cm.Reds) # العنوان الكلي للمخطط plt.suptitle('Pairs Plot of Energy Data', size = 36, y = 1.02) فنحصل على المخططات التالية: ننظر لتقاطع سطر المتغير الأول مع عمود المتغير الثاني لمعاينة التفاعل بين متغيرين، مثلًا: لمعاينة الارتباط بين كثافة الكهرباء وفق الطقس Weather Norm EUI والمعامل الهدف score، ننظر إلى تقاطع سطر Weather Norm EUI مع عمود score لنجد معامل ارتباط قيمته -0.67 أي أن العلاقة بينهما عكسيةً. يُمكن أن يُساعد هذا المخطط إضافًة لمظهر المخطط السابق الرائع، في تحديد الميزات المفيدة في عملية النمذجة. هندسة الميزات والاختيار تختصر عملية هندسة الميزات وحسن اختيار المناسب منها الوقت اللازم لمعالجة مسائل تعلّم الآلة. لنبدأ أولًا بتعريف هاتين المهمتين: هندسة الميزات Feature engineering: وهي عملية استخراج أو إنشاء ميزات جديدة من البيانات الأساسية المتاحة، قد يستلزم ذلك إجراء بعض التحويلات على المتغيرات طبعًا، مثل إيجاد مربع القيمة أو اللوغاريتم الطبيعي لها، أو ترميز المتغيرات الفئوية لتُصبح قابلةً للاستخدام في النموذج. يُمكن النظر لهندسة الميزات عمومًا على أنها عملية إنشاء ميزات جديدة. اختيار الميزات Feature selection: وهو عملية اختيار الميزات الأنسب للنموذج، حيث نحذف عادةً الميزات غير المرتبطة مع الهدف كي نُساعد النموذج على التعميم الأفضل للبيانات الجديدة وللحصول على نموذج قابل للشرح والفهم. يُمكن النظر لاختيار الميزات بشكل عام على أنها عملية حذف الميزات غير المرتبطة مع الهدف والإبقاء على الميزات المهمة فقط. يتعلّم نموذج تعلّم الآلة فقط من البيانات التي نُقدّمها له، لذا يُعدّ التأكد من أن البيانات تتضمن جميع الميزات الهامة أمرًا أساسيًا، وإذا لم نغذي النموذج بالبيانات الصحيحة، فإننا نعدّه ليفشل ولا يجب أن نتوقع منه أن يتعلّم عمليًا. ننجز بمشروعنا في مرحلة هندسة الميزات بما يلي: ترميز المتغيرات الفئوية باستخدام ترميز ساخن واحد (ميزة الحي السكني وميزة نوع استخدام الملكية). إضافة اللوغاريتم الطبيعي للمتغيرات الرقمية. يلزم استخدام هذا الترميز one-hot لتضمين متغير فئوي categorical variables في النموذج، إذ لا تفهم خوارزمية تعلّم الآلة بأن نوع البناء مكتبًا مثلًا، إذ يجب استخدام 1 إذا كان نوع البناء مكتبًا وإلا 0. يُمكن أن تساهم إضافة ميزات جديدة ناتجة عن تطبيق بعض التوابع الرياضية على الميزات الرقمية في مساعدة النموذج على تعلّم العلاقات غير الخطية الموجودة ضمن البيانات، ومن الممارسات الشائعة حساب الجذر التربيعي أو القوة من مرتبة معينة أو اللوغاريتم الطبيعي، والتي تعتمد عادةً على التجربة أو المعرفة بمجال المسألة، ونضيف في مشروعنا اللوغاريتم الطبيعي لجميع الميزات الرقمية. تختار الشيفرة التالية الميزات الرقمية ومن ثم يوجد اللوغاريتم الطبيعي لها ثم يختار الميزتين الفئويتين ويرمزهما ثم يدمج الكل، قد يبدو أن هذا يتطلب الكثير من الجهد لكن استخدام المكتبة Pandas يُسهّل العمل: # نسخ البيانات الأولية features = data.copy() # اختيار الأعمدة الرقمية numeric_subset = data.select_dtypes('number') # إنشاء أعمدة جديدة للوغاريتم الطبيعي للأعمدة الرقمية for col in numeric_subset.columns: # تجاوز عمود معامل الطاقة if col == 'score': next else: numeric_subset['log_' + col] = np.log(numeric_subset[col]) # اختيار الأعمدة الفئوية categorical_subset = data[['Borough', 'Largest Property Use Type']] # ترميز واحد ساخن categorical_subset = pd.get_dummies(categorical_subset) # وصل إطاري العمل الناتجين # اضبط قيمة العمود على 1 لإنشاء عمود ربط features = pd.concat([numeric_subset, categorical_subset], axis = 1) بعد هذه المعالجة سيكون لدينا أكثر من 11000 مبنى مع 110 عمود (ميزة)، حيث لن تكون كل هذه الميزات مفيدةً في عملية التنبؤ بمعامل الطاقة لذا نحذف بعضها. اختيار الميزات ترتبط العديد من الميزات مع بعضها البعض بصورة وثيقة مما يعني حصول تكرار عمليًا، إذ يُبين المخطط التالي مثلًا ترابط ميزة كثافة الطاقة Site EUI مع ميزة كثافة الكهرباء Weather Normalized Site EUI، وبقيمة ترابط عالية جدًا 0.997. نحذف عادةً الميزات المترابطة مع بعضها كثيرًا تُدعى علاقة خطية متداخلة collinear، مما يُساعد على الحصول على نموذج تعلّم عام قابل للتفسير. حيث نشير هنا إلى الارتباط بين الميزات مع بعضها البعض وليس إلى ارتباط الميزات مع الهدف. توجد العديد من الطرق لحساب علاقات التداخل الخطية بين الميزات، حيث يُمكن استخدام عامل تضخم التباين variance inflation factor. سنستخدم في هذا المشروع معامل الارتباط لإيجاد الميزات المترابطة ونحذف ميزةً من أجل كل ميزتين إذا كان معدل الارتباط بينهما أكبر من 0.6، حيث يمكنك الحصول على الشيفرة أيضًا. لاحظ أن القيمة المعتمدة 0.6 هي قيمة تجريبية اختيرت بعد العديد من التجارب، لنتذكر أن تعلّم الآلة هو حقل تجريبي وغالبًا ما نجرب عدة تجارب ومحاولات لاختيار الأفضل. أخيرًا وبعد مرحلة اختيار الميزات تبقى لدينا 64 عمودًا وهدفًا وحيدًا. # حذف الأعمدة ذات القيم غير الرقمية features = features.dropna(axis=1, how = 'all') print(features.shape) (11319, 65) إنشاء خط الأساس بعد أن أنهينا مرحلة تنظيف البيانات وتحليل البيانات الاستكشافي وهندسة الميزات، الآن يجب تحديد خط الأساس المبدئي Baseline قبل البدء بعملية بناء النموذج، وهو عبارة عن تخمين يُستخدم للحكم فيما إذا كانت نتائج نموذج التعلّم مقبولةً أم لا. يُمكن في مسائل الانحدار اعتماد القيمة الأوسط median للهدف والمحسوبة لبيانات اختبار الخط الذي يجب لنموذج التعلّم تجاوزه وهو عمليًا هدف سهل الوصول. نستخدم متوسط الخطأ المطلق MAE mean absolute error، والذي يقيس متوسط الفروقات بالقيمة المطلقة بين تنبؤ النموذج والقيم الحقيقية. توجد العديد من المقاييس في الواقع إلا أنه يُمكن اتباع نصيحة اعتماد مقياس واحد دومًا إضافةً إلى أن حساب متوسط الخطأ المطلق سهل وقابل للشرح والتفسير. قبل حساب خط الأساس يجب تقسيم البيانات إلى مجموعتين هما مجموعة التدريب ومجموعة الاختبار: مجموعة التدريب: وهي مجموعة البيانات التي نُقدّمها للنموذج ليتعلّم منها، حيث تتألف من مجموعة الميزات إضافةً إلى القيمة الهدف (معامل الطاقة في حالتنا). مجموعة الاختبار: تُستخدم هذه المجموعة لتقويم كفاءة نموذج التعلّم حيث نختبر النموذج المدرب مع هذه البيانات، ومن ثم نوازن جواب النموذج مع الجواب المعروف لدينا في هذه البيانات، مما يسمح لنا بحساب قيمة متوسط الخطأ المطلق. نقسم البيانات إلى 70% للتدريب و30% للاختبار: # تقسيم البيانات إلى 70% للتدريب و30% للاختبار X, X_test, y, y_test = train_test_split(features, targets, test_size = 0.3, random_state = 42) يُمكن لنا الآن حساب خط الأساس: # تابع حساب متوسط الخطأ المطلق def mae(y_true, y_pred): return np.mean(abs(y_true - y_pred)) baseline_guess = np.median(y) print('The baseline guess is a score of %0.2f' % baseline_guess) print("Baseline Performance on the test set: MAE = %0.4f" % mae(y_test, baseline_guess)) والذي يُعطي: The baseline guess is a score of 66.00 Baseline Performance on the test set: MAE = 24.5164 مما يعني أن الخطأ من مرتبة 25% وهي عتبة سهلة التجاوز. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن النتائج استعرضنا في هذه المقالة الخطوات الثلاث الأولى لمعالجة مسألة تعلّم الآلة، بعد تحديد المشكلة نفعل ما يلي: تنظيف وتنسيق البيانات الخام المتاحة. تحليل استكشافي للبيانات بهدف التعرّف على البيانات. تطوير مجموعةً من الميزات للاستخدام في نموذج التعلّم. وفي النهاية، أجرينا عملية تحديد خط الأساس الذي سيسمح لنا بقبول نموذج التعلّم أو رفضه. نعرض في المقالة الثانية كيفية استخدام Scikit-Learn لتقييم نماذج التعلّم واختيار الأفضل منها، إضافةً إلى آليات معايرة المعاملات الفائقة للنموذج للوصول إلى نموذج أمثلي، وتعرض المقالة الثالثة كيفية تفسير النموذج وعرض النتائج. ترجمة -وبتصرف- للمقال A Complete Machine Learning Project Walk-Through in Python: Part One لكاتبه Will Koehrsen. اقرأ أيضًا دوال الكثافة الاحتمالية في بايثون المرجع الشامل إلى تعلم لغة بايثون
  5. تُعدّ مسألة تصنيف الشخصيات بالاعتماد على تغريداتهم على وسائل التواصل الاجتماعي من المسائل المهمة مثلًا يُمكن أن يكون من المفيد تحديد الأشخاص الذين يكتبون تحليلات مهمة في مجالات الأعمال أو الاقتصاد أو الرياضة وغيرها مما يتيح للآخرين معرفة تقييمات أو آراء الأشخاص المغردين واتخاذ الإجراء المناسب. نعرض في هذه المقالة استخدام تقنيات التعلم العميق في تصنيف الشخصيات المغردة بنصوص مكتوبة باللغة العربية وباللهجة السعودية بمعنى أن اللغة المستخدمة ليست بالضرورة اللغة العربية الفصحى بل يُمكن أن تدخل فيها ألفاظ عامية يستخدمها المغردون عادةً. لتصنيف شخص ما، سنلجأ أولًا إلى تصنيف تغريداته (أي إسناد كل تغريده إلى صف معين)، ومن ثم تصنيفه بناًء على نتائج تصنيف تغريداته (الصفوف ذات التواتر الكبر). بيانات التدريب تحوي مجموعة البيانات المتوفرة حوالي 26500 تغريدة لمجموعة متنوعة من المغردين المشهورين في المملكة العربية السعودية. جُمّعت هذه التغريدات عن طريق مجموعة من الطلاب الجامعيين وذلك من موقع تويتر Twitter. بالطبع، لا يوجد تصنيف متوفر لهذه التغريدات على تويتر. تجدها موجودة هي وبقية ملفات هذا الفصل في الملف المضغوط المرفق. تصنيف بيانات التدريب يتطلب استخدام خوارزميات تعلم الآلة (خوارزميات تصنيف التغريدات في حالتنا) توفر بيانات للتدريب أي مجموعة من التغريدات مُصنفّة مُسبقًا إلى مجموعة من الصفوف، ويُمكن، كما في حالتنا، اللجوء إلى الطرق اليدوية أي الطلب من مجموعة من الأشخاص قراءة النصوص وتصنيفها وهو حل يصلح في حال كان عدد النصوص صغيرًا نسبيًا، ويتميز هذا الحل بالدقة العالية لأن الأشخاص تُدرك، بشكل عام، معاني النصوص من خلال خبرتها اللغوية المُكتسبة وتُصنّف النصوص بشكل صحيح غالبًا. طلبنا من الطلاب المشاركين في مشروعنا تصنيف كل تغريدة إلى واحدة من المواضيع (الصفوف) التالية: table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } الصف بالعربي English الرياضة Sports الأعمال Business التكنولوجيا Technology المجتمع Social السياسة Politics الأخبار News الاقتصاد Economy نُعطي فيما يلي أمثلة عن هذه التغريدات (بعد تصنيفها): "الحمد لله مباراة الدوري وهدف غالي يشابه هدف القناص مباراة الفتح الرائد الهلال" (الرياضة). "قريبا جدا… تتابع السوق السعودي وشركاته والأمريكي وشركاته والعملات والكربتو النفط والذهب بمنصة واحدة مباشرة وتقدر ت…" (الأعمال). "فايروس خطير جدا يستهدف مستخدمي نظام الاندرويد قادر يسرق بيانات حساسه الصور وتسجيل صوت المستخدم والوصول لمعلوماتهم وسرقة…" (التكنولوجيا) "أالسلام عليكم ورحمة الله وبركاتة اللهم فرج يفرج همه ويبارك ماله ويفتح أبواب الخيروال…" (المجتمع) "مبادرون لأجل فلسطين.. تكريم كويتي لرافضي التطبيع في الفعاليات الرياضية والأدبية" (السياسة) "الخط العربي ومخاوف التقنية في الزمن الرقمي.. تجربة عالمية أم خروج عن تقاليد الفن العريق؟" (الأخبار) "معلومة فنية (٢)للشراء المضاربي ١- اقفال السوق ٥ دقايق اذا كانت شمعة ايجابية للحصول ربح مضاربي يوم ٢-عند…" (الاقتصاد). نعرض في هذه المقالة كيفية بناء مُصنّف حاسوبي آلي يُصنّف أي تغريدة عربية إلى أحد الصفوف السبعة السابقة ومن ثم تصنيف الشخص المغرد لواحد أو أكثر من هذه الصفوف (الأكثر تواترًا). المعالجة الأولية للنصوص تتميز التغريدات، بشكل عام، باحتوائها على مجموعة من الأمور غير المهمة في مسألة التصنيف مثل الروابط Links والوسوم Hashtags والرموز التعبيرية Emojis وغيرها. يُبين الشكل التالي مثالًا عن التغريدات المُجمّعة: تهدف المعالجة الأولية إلى الحصول على الكلمات المهمة فقط من النصوص وذلك عن طريق تنفيذ بعض العمليات اللغوية عليها. لتكن لدينا مثلًا الجملة التالية: "أنا أحب الذهاب إلى الحديقة ?، كل يوم 9 صباحاً، مع رفاقي هؤلاء! @toto "، سنقوم بتنفيذ العمليات التالية: حذف الروابط والوسوم والرموز التعبيرية: يكون ناتج الجملة السابقة: "أنا أحب الذهاب إلى الحديقة، كل يوم 9 صباحاً، مع رفاقي هؤلاء!" حذف إشارات الترقيم المختلفة كالفواصل وإشارات الاستفهام وغيرها: يكون ناتج الجملة السابقة: "أنا أحب الذهاب إلى الحديقة كل يوم 9 صباحاً مع رفاقي هؤلاء" حذف الأرقام الواردة في النص: يكون ناتج الجملة السابقة: "أنا أحب الذهاب إلى الحديقة كل يوم صباحاً مع رفاقي هؤلاء" حذف كلمات التوقف stop words وهي الكلمات التي تتكرر كثيرًا في النصوص ولا تؤثر في معانيها كأحرف الجر (من، إلى، …) والضمائر (أنا، هو، …) وغيرها فيكون ناتج الجملة السابقة: " أحب الذهاب الحديقة يوم صباحاً رفاقي" تجذيع الكلمات stemming أي إرجاع الكلمات المتشابهة إلى كلمة واحدة (جذع) مما يُساهم في إنقاص عدد الكلمات الكلية المختلفة في النصوص، ومطابقة الكلمات المتشابهة مع بعضها البعض. مثلًا: يكون للكلمات الأربع: (رائع، رايع، رائعون، رائعين) نفس الجذع المشترك: (رايع) فيكون ناتج الجملة السابقة: " احب ذهاب حديق يوم صباح رفاق" إعداد المشروع يحتاج تنفذ شيفرات هذه المقالة بيئةً برمجيةً للغة بايثون الإصدار 3.8. ويجب أن تتضمن هذه البيئة البرمجية مدير الحِزم pip لتثبيت الحِزم، ومُنشئ البيئات الافتراضية venv لإنشاء بيئاتٍ افتراضيةٍ. نستخدم محرر الشيفرات البرمجية Jupyter Notebooks، وهو مفيد جدًا لتجربة وتشغيل الأمثلة الخاصة بتَعَلّم الآلة بطريقةٍ تفاعليةٍ، حيث نستطيع من خلاله تشغيل كتلًا صغيرةً من الشيفرات البرمجية ورؤية النتائج بسرعة، مما يُسهّل علينا اختبار الشيفرات البرمجية وتصحيحها. نحتاج أولًا لتثبيت بعض التبعيات، وذلك لإنشاء مساحة عمل للاحتفاظ بملفاتنا قبل أن نتمكن من تطوير برنامجنا. نُنشئ مجلدًا جديدًا خاصًا بمشروعنا وندخل إليه هكذا: mkdir arc cd arc نُنفذّ الأمر التالي لإنشاء البيئة الافتراضية: python -m venv arc ومن ثم الأمر التالي في Linux لتنشيط البيئة الافتراضية: source arc/bin/activate أما في Windows، فيكون أمر التنشيط: "arc/Scripts/activate.bat" نستخدم إصداراتٍ محددةٍ من المكتبات اللازمة، من خلال إنشاء ملف requirements.txt في مجلد المشروع، وسيُحدِّد هذا الملف المتطلبات والإصدارات التي سنحتاج إليها. نفتح الملف requirements.txt في محرر النصوص، ونُضيف الأسطر التالية، وذلك لتحديد المكتبات التي نريدها وإصداراتها: absl-py==1.0.0 asttokens==2.0.5 astunparse==1.6.3 backcall==0.2.0 cachetools==5.1.0 certifi==2021.10.8 charset-normalizer==2.0.12 click==8.1.3 colorama==0.4.4 cycler==0.11.0 debugpy==1.6.0 decorator==5.1.1 entrypoints==0.4 executing==0.8.3 flatbuffers==1.12 fonttools==4.33.3 gast==0.4.0 google-auth==2.6.6 google-auth-oauthlib==0.4.6 google-pasta==0.2.0 grpcio==1.46.1 h5py==3.6.0 idna==3.3 imbalanced-learn==0.9.1 imblearn==0.0 importlib-metadata==4.11.3 ipykernel==6.13.0 ipython==8.3.0 jedi==0.18.1 joblib==1.1.0 jupyter-client==7.3.1 jupyter-core==4.10.0 keras==2.6.0 Keras-Applications==1.0.8 Keras-Preprocessing==1.1.2 kiwisolver==1.4.2 libclang==14.0.1 Markdown==3.3.7 matplotlib==3.5.2 matplotlib-inline==0.1.3 nest-asyncio==1.5.5 nltk==3.7 numpy==1.22.3 oauthlib==3.2.0 opt-einsum==3.3.0 packaging==21.3 pandas==1.4.2 parso==0.8.3 pickleshare==0.7.5 Pillow==9.1.1 prompt-toolkit==3.0.29 protobuf==3.20.1 psutil==5.9.0 pure-eval==0.2.2 PyArabic==0.6.14 pyasn1==0.4.8 pyasn1-modules==0.2.8 Pygments==2.12.0 pyparsing==3.0.9 python-dateutil==2.8.2 pytz==2022.1 #pywin32==304 pyzmq==22.3.0 regex==2022.4.24 requests==2.27.1 requests-oauthlib==1.3.1 rsa==4.8 scikit-learn==1.1.0 scipy==1.8.0 seaborn==0.11.2 six==1.16.0 sklearn==0.0 snowballstemmer==2.2.0 stack-data==0.2.0 tensorboard==2.9.0 tensorboard-data-server==0.6.1 tensorboard-plugin-wit==1.8.1 tensorflow==2.9.0 tensorflow-estimator==2.9.0 tensorflow-io-gcs-filesystem==0.26.0 termcolor==1.1.0 threadpoolctl==3.1.0 tornado==6.1 tqdm==4.64.0 traitlets==5.2.1.post0 typing-extensions==4.2.0 urllib3==1.26.9 wcwidth==0.2.5 Werkzeug==2.1.2 wrapt==1.14.1 zipp==3.8.0 نحفظ التغييرات التي طرأت على الملف ونخرج من محرر النصوص، ثم نُثَبت هذه المكتبات بالأمر التالي: (arc) $ pip install -r requirements.txt بعد تثبيتنا لهذه التبعيات، نُصبح جاهزين لبدء العمل على مشروعنا. كتابة الشيفرة البرمجية نُشغّل محرر الشيفرات البرمجية Jupyter Notebook بمجرد اكتمال عملية التثبيت هكذا: (arc) $ jupyter notebook ثم نُنشئ ملفًا جديدًا في داخل المحرر ونُسمّه باسم ac مثلًا. تحميل البيانات نبدأ أولًا بتحميل التغريدات من الملف tweets.csv ضمن إطار من البيانات DataFrame من مكتبة Pandas ومن ثم عرض بعضها: import pandas as pd # قراءة التغريدات وتحميلها ضمن إطار من البيانات tweets = pd.read_csv('tweets.csv',encoding = "utf-8") # إظهار الجزء الأعلى من إطار البيانات tweets.head() يظهر لنا أوائل التغريدات: يُمكن استخدام خاصية الشكل shape لمعرفة عدد الصفوف والأعمدة للبيانات المُحمّلة: print('Data size:', tweets.shape) يكون الناتج: Data size: (26748, 2) المعالجة الأولية للنصوص نستخدم فيما يلي بعض الخدمات التي توفرها المكتبة nltk لمعالجة اللغات الطبيعية كتوفير قائمة كلمات التوقف باللغة العربية (حوالي 700 كلمة) واستخراج الوحدات tokens من النصوص. كما نستخدم مجذع الكلمات العربية من مكتبة snowballstemmer. كما نستخدم التعابير النمطية regular expressions للتعرف على الرموز التعبيرية والوسوم والروابط وحذفها. # مكتبة السلاسل النصية import string # مكتبة التعابير النظامية import re # مكتبة معالجة اللغات الطبيعية import nltk #nltk.download('punkt') #nltk.download('stopwords') # مكتبة كلمات التوقف from nltk.corpus import stopwords # مكتبة استخراج الوحدات from nltk.tokenize import word_tokenize # مكتبة المجذع العربي from snowballstemmer import stemmer ar_stemmer = stemmer("arabic") # دالة حذف المحارف غير اللازمة def remove_chars(text, del_chars): translator = str.maketrans('', '', del_chars) return text.translate(translator) # دالة حذف المحارف المكررة def remove_repeating_char(text): return re.sub(r'(.)\1{2,}', r'\1', text) # دالة تنظيف التغريدات def clean_tweet(tweet): stop_words = stopwords.words('arabic') # محارف الرموز التعبيرية emoj = re.compile("[" u"\U0001F600-\U0001F64F" u"\U0001F300-\U0001F5FF" u"\U0001F680-\U0001F6FF" u"\U0001F1E0-\U0001F1FF" u"\U00002500-\U00002BEF" u"\U00002702-\U000027B0" u"\U00002702-\U000027B0" u"\U000024C2-\U0001F251" u"\U0001f926-\U0001f937" u"\U00010000-\U0010ffff" u"\u2640-\u2642" u"\u2600-\u2B55" u"\u200d" u"\u23cf" u"\u23e9" u"\u231a" u"\ufe0f" u"\u3030" "]+", re.UNICODE) tweet = str(tweet) # حذف @ وما يتبعها tweet = re.sub("@[^\s]+","",tweet) tweet = re.sub("RT","",tweet) # حذف الروابط tweet = re.sub(r"(?:\@|http?\://|https?\://|www)\S+", "", tweet) # حذف الرموز التعبيرية tweet = re.sub(emoj, '', tweet) # حذف كلمات التوقف tweet = ' '.join(word for word in tweet.split() if word not in stop_words) # حذف الإشارات # tweet = tweet.replace("#", "").replace("_", " ") # حذف الأرقام tweet = re.sub(r'[0-9]+', '', tweet) # حذف المحارف غير اللازمة # علامات الترقيم العربية arabic_punctuations = '''`÷×؛<>_()*&^%][ـ،/:"؟.,'{}~¦+|!”…“–ـ''' # علامات الترقيم الإنجليزية english_punctuations = string.punctuation # دمج علامات الترقيم العربية والانكليزية punctuations_list = arabic_punctuations + english_punctuations tweet = remove_chars(tweet, punctuations_list) # حذف المحارف المكررة tweet = remove_repeating_char(tweet) # استبدال الأسطر الجديدة بفراغات tweet = tweet.replace('\n', ' ') # حذف الفراغات الزائدة من اليمين واليسار tweet = tweet.strip(' ') return tweet # دالة تقسيم النص إلى مجموعة من الوحدات def tokenizingText(text): tokens_list = word_tokenize(text) return tokens_list # دالة حذف كلمات التوقف def filteringText(tokens_list): # قائمة كلمات التوقف العربية listStopwords = set(stopwords.words('arabic')) filtered = [] for txt in tokens_list: if txt not in listStopwords: filtered.append(txt) tokens_list = filtered return tokens_list # دالة التجذيع def stemmingText(tokens_list): tokens_list = [ar_stemmer.stemWord(word) for word in tokens_list] return tokens_list # دالة دمج قائمة من الكلمات في جملة def toSentence(words_list): sentence = ' '.join(word for word in words_list) return sentence شرح الدوال السابقة: الدالة clean_tweet تحذف الرموز التعبيرية والروابط والوسوم والأرقام وعلامات الترقيم العربية والإنكليزية من النص وذلك باستخدام التعابير النظامية الموافقة. الدالة remove_repeating_char تحذف المحارف المكررة والتي قد يستخدمها كاتب التغريدة. الدالة tokenizingText تعمل على تجزئة النص إلى قائمة من الوحدات tokens. الدالة filteringText تحذف كلمات التوقف من قائمة الوحدات. الدالة stemmingText تعمل على تجذيع كلمات قائمة الوحدات المتبقية. يُبين المثال التالي نتيجة استدعاء كل دالة من الدوال السابقة: # مثال text= "أنا أحب الذهاب إلى الحديقة ?، كل يوم 9 صباحاً، مع رفاقي هؤلاء! @toto " print(text) text=clean_tweet(text) print(text) tokens_list=tokenizingText(text) print(tokens_list) tokens_list=filteringText(tokens_list) print(tokens_list) tokens_list=stemmingText(tokens_list) print(tokens_list) يكون ناتج التنفيذ: تعرض الشيفرة التالية التصريح عن الدالة process_tweet والتي تقوم أولًا بتنظيف التغريدات وذلك باستدعاء الدالة clean_tweet، ومن ثم تحويل التغريدات إلى وحدات وذلك باستدعاء الدالة tokenizingText، وأخيرًا تجذيع الوحدات عن طريق الدالة stemmingText. ومن ثم تنفيذ دالة المعالجة الأولية على جميع التغريدات: # دالة معالجة التغريدات def process_tweet(tweet): # تنظيف التغريدة tweet=clean_tweet(tweet) # التحويل إلى وحدات tweet=tokenizingText(tweet) # التجذيع tweet=stemmingText(tweet) return tweet # المعالجة الأولية للتغريدات tweets['tweet'] = tweets['tweet'].apply(process_tweet) موازنة الصفوف تتطلب معظم خوارزميات التصنيف توفر بيانات للتدريب وبصفوف متوازنة، بمعنى أن يكون عدد أسطر البيانات من كل صف متساوية تقريبًا تجنبًا لانحياز المُصنف إلى الصف ذي العدد الأكبر من الأسطر في بيانات التدريب. من المفيد إذًا أولًا معاينة توزع أعداد الصفوف. نستخدم في الشيفرة البرمجية التالية مكتبات الرسم اللازمة لرسم أشرطة تُمثّل أعداد الصفوف: # مكتبات الرسم import matplotlib.pyplot as plt import seaborn as sns # حجم الرسم plt.figure(figsize=(12, 6)) # رسم عدد كل صف sns.countplot(data=tweets, y='topic'); plt.title('Topics Distribution', fontsize=18) plt.show() يُبين الشكل الناتج عدم توازن الصفوف على الإطلاق: نستخدم في الشيفرة التالية الصنف RandomOverSampler من المكتبة imblearn.over_sampling والذي يقوم باختيار أسطر عشوائية من الصفوف القليلة العدد ويُكررها في مجموعة البيانات. # استيراد مكتبة الموازنة from imblearn.over_sampling import RandomOverSampler # إنشاء كائن من الصنف oversample = RandomOverSampler() # توليد سطر عشوائي tweets = tweets.sample(frac=1) # توليد الأسطر الجديدة tweets, Y = oversample.fit_resample(tweets, tweets.topic) نعيد رسم أعداد الصفوف بعد الموازنة: # إعادة رسم أعداد الصفوف # بعد الموازنة plt.figure(figsize=(12, 6)) sns.countplot(data=tweets, y='topic'); plt.title('Topics Distribution After OverSampling', fontsize=18) plt.show() يُبين الشكل الناتج توازن الصفوف بشكل كامل: ترميز الصفوف لا تقبل بنى تعلم الآلة النصوص كمدخلات لها، تقوم الشيفرة التالية باستخدام الصنف LabelEncoder من المكتبة sklearn.preprocessing لترميز الصفوف باستخدام الأرقام. نطبع في النهاية أسماء الصفوف والترميز المقابل لها: from sklearn.preprocessing import LabelEncoder # ترميز الصفوف le_topics = LabelEncoder() tweets['topic'] = tweets[['topic']].apply(le_topics.fit_transform) classes = le_topics.classes_ # الصفوف n_classes = len(classes) # عدد الصفوف print("No. of classes:", n_classes) print("Classes:", classes) print("Coding: ", le_topics.transform(classes)) يكون الإظهار: No. of classes: 7 Classes: ['Business' 'Economy' 'News' 'Politics' 'Social' 'Sports' 'Technology'] Coding: [0 1 2 3 4 5 6] تحويل النصوص إلى أشعة رقمية لا تقبل بنى تعلم الآلة النصوص كمدخلات لها، بل تحتاج إلى أشعة رقمية كمدخلات. نستخدم الشيفرة التالية لتحويل الشعاع النصي لكل تغريدة tweet_preprocessed إلى شعاع رقمي: from keras.preprocessing.text import Tokenizer from keras.preprocessing.sequence import pad_sequences # نركيب جمل التغريدات من المفردات المعالجة sentences = tweets['tweet'].apply(toSentence) print(sentences[6]) # عدد الكلمات الأعظمي ذات التواتر الأكبر # التي ستُستخدم max_words = 5000 # الطول الأعظمي لشعاع الأرقام max_len = 50 # التصريح عن المجزئ # مع تحديد عدد الكلمات التي ستبقى # بالاعتماد على تواترها tokenizer = Tokenizer(num_words=max_words ) # ملائمة المجزئ لنصوص التغريدات tokenizer.fit_on_texts(sentences) # تحويل النص إلى قائمة من الأرقام S = tokenizer.texts_to_sequences(sentences) print(S[0]) # توحيد أطوال الأشعة X = pad_sequences(S, maxlen=max_len) print(X[0]) X.shape نجد من الشيفرة أن المتغير max_words يُحدّد عدد الكلمات الأعظمي التي سيتم الاحتفاظ بها حيث يُحسب تواتر كل كلمة في كل النصوص ومن ثم تُرتب حسب تواترها (المرتبة الأولى للكلمة ذات التواتر الأكبر). ستُهمل الكلمات ذات المرتبة أكبر من max_words، و يُحدّد المتغير max_len طول الشعاع الرقمي النهائي فإذا كان موافقًا لنص أقل من max_len تُضاف أصفار للشعاع حتى يُصبح طوله مساويًا إلى max_len. أما إذا كان طوله أكبر يُقتطع جزءًا منه ليُصبح طوله مساويًا إلى max_len. بينما تقوم الدالة fit_on_texts بملائمة المُجزء tokenizer لنصوص جمل التغريدات sentences.values أي حساب تواتر الكلمات والاحتفاظ بالكلمات ذات التواتر أكبر أو يساوي max_words. نطبع في الشيفرة السابقة، بهدف التوضيح، ناتج كل مرحلة. اخترنا مثلًا شعاع التغريدة 6 بعد المعالجة: تكون نتيجة تحويل الشعاع السابق النصي إلى شعاع من الأرقام: [1839, 1375, 2751, 315, 975, 1420, 1839, 3945, 436, 794, 919, 445, 1290, 312, 258] وبعد عملية توحيد الطول يكون الشعاع الرقمي النهائي الناتج: [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1839 1375 2751 315 975 1420 1839 3945 436 794 919 445 1290 312 258] تجهيز دخل وخرج الشبكة العصبية تعرض الشيفرة التالية حساب شعاع الخرج أولًا وذلك بإسناد العمود topic من إطار البيانات، والذي يحوي الترميز الرقمي للصفوف إلى المتغير y. نستخدم الدالة train_test_split لتقسيم البيانات المتاحة إلى 80% منها لعملية التدريب و20% لعملية الاختبار وحساب مقاييس الأداء: # توليد شعاع الخرج y = tweets['topic'] # مكنبة تقسيم البيانات إلى تدريب واختبار from sklearn.model_selection import train_test_split # تقسيم البيانات إلى تدريب واختبار X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0) print(X_train.shape, y_train.shape) print(X_test.shape, y_test.shape) نطبع في الشيفرة السابقة حجوم أشعة الدخل والخرج للتدريب وللاختبار: (30520, 50) (30520,) (7630, 50) (7630,) نموذج الشبكة العصبية المتعلم تُعدّ المكتبة Keras من أهم مكتبات بايثون التي توفر بناء شبكات عصبية لمسائل التعلم الآلي. تعرض الشيفرة التالية التصريح عن دالة بناء نموذج التعلّم create_model مع إعطاء جميع المعاملات المترفعة قيمًا ابتدائية: # تضمين النموذج التسلسلي from keras.models import Sequential # تضمين الطبقات اللازمة from keras.layers import Embedding, Dense, LSTM # دوال التحسين from tensorflow.keras.optimizers import Adam, RMSprop # التصريح عن دالة إنشاء نموذج التعلم # مع إعطاء قيم أولية للمعاملات المترفعة def create_model(embed_dim = 32, hidden_unit = 16, dropout_rate = 0.2, optimizers = RMSprop, learning_rate = 0.001): # التصريح عن نموذج تسلسلي model = Sequential() # طبقة التضمين model.add(Embedding(input_dim = max_words, output_dim = embed_dim, input_length = MAX_LENGTH)) # LSTM model.add(LSTM(units = hidden_unit ,dropout=dropout_rate)) # الطبقة الأخيرة model.add(Dense(units = len(classes), activation = 'softmax')) # يناء النموذج model.compile(loss = 'sparse_categorical_crossentropy', optimizer = optimizers(learning_rate = learning_rate), metrics = ['accuracy']) # طباعة ملخص النموذج print(model.summary()) return model نستخدم من أجل مسألتنا نموذج شبكة عصبية تسلسلي يتألف من ثلاث طبقات هي: طبقة التضمين Embedding وطبقة LSTM (شبكة ذات ذاكرة طويلة المدى) والطبقة الكثيفة Dense. الطبقة الأولى: طبقة التضمين Embedding نستخدم هذه الطبقة لتوليد ترميز مكثف للكلمات dense word encoding مما يُساهم في تحسين عملية التعلم. نطلب تحويل الشعاع الذي طوله input_length (في حالتنا 50) والذي يحوي قيم ضمن المجال input_dim (من 1 إلى 5000 في مثالنا) إلى شعاع من القيم ضمن المجال output_dim (مثلًا 32 قيمة). الطبقة الثانية: شبكة ذات ذاكرة طويلة المدى Long Short-Term Memory يُحدّد المعامل المترفع units عدد الوحدات المخفية لهذه الطبقة. يُساهم المعامل dropout في معايرة الشبكة خلال التدريب حيث يقوم بإيقاف تشغيل الوحدات المخفية بشكل عشوائي أثناء التدريب، وبهذه الطريقة لا تعتمد الشبكة بنسبة 100٪ على جميع الخلايا العصبية الخاصة بها. وبدلاً من ذلك، تُجبر نفسها على العثور على أنماط أكثر أهمية في البيانات من أجل زيادة المقياس الذي تحاول تحسينه (الدقة مثلًا). الطبقة الثالثة: الكثيفة Dense يُحدّد المعامل units حجم الخرج لهذه الطبقة (7 في حالتنا: عدد الصفوف) ويُبين الشكل التالي ملخص النموذج: معايرة المعاملات الفائقة للوصول لنموذج أمثلي يمكن الوصول لنموذج تعلم أمثلي بمعايرة معاملاته الفائقة وفق معطيات المشروع. لنُبين أولًا الفرق بين المعاملات الفائقة لنموذج والمعاملات الأخرى له: المعاملات الفائقة hyperparameters: هي إعدادات خوارزمية التعلّم قبل التدريب (والتي وضعها مصممو الخوارزمية). المعاملات parameters: هي المعاملات التي يتعلّمها النموذج أثناء التدريب مثل أوزان الشبكة العصبية. تؤثر عملية معايرة المعاملات الفائقة على أداء النموذج لاسيما لجهة التوزان المطلوب بين مشكلة قلة التخصيص underfitting ومشكلة فرط التخصيص overfitting واللتان تؤديان إلى نموذج غير قادر على تعميم أمثلة التدريب وبالتالي لن يتمكن من التصنيف مع معطيات جديدة (يُمكن العودة لمقال تعلم الآلة: التحديات الرئيسية وكيفية التوسع في المجال من أكاديمية حسوب للمزيد من التفصيل حول هاتين المشكلتين). تظهر مشكلة قلة التخصيص عندما لا يكون للنموذج درجات حرية كافية ليتعلّم الربط بين الميزات والهدف، وبالتالي يكون له انحياز كبير Bias–variance tradeoff نحو قيم معينة للهدف. يُمكن تصحيح قلة التخصيص بجعل النموذج أكثر تعقيدًا. أما مشكلة فرط التخصيص فتظهر عندما يقوم النموذج بتخزين بيانات التدريب فيكون له بالتالي تباين كبير والذي يُمكن تصحيحه بالحد من تعقيد النموذج باستخدام التسوية regularization. تكمن المشكلة في معايرة المعاملات الفائقة بأن قيمها المثلى تختلف من مسألة لأخرى! وبالتالي، فإن الطريقة الوحيدة للوصول لهذه القيم المثلى هي تجريب قيم مختلفة مع كل مجموعة بيانات تدريب جديدة. يوفر Scikit-Learn العديد من الطرق لتقويم المعاملات الفائقة وبالتالي سنعتمد في مشروعنا عليها دون أن نُعقّد الأمور أكثر. يُمكن العودة إلى مقال تحليل المشاعر في اللغة العربية باستخدام التعلم العميق فقرة "البحث الشبكي مع التقييم المتقاطع" لشرح طريقة حسابنا لأفضل القيم للمعاملات المترفعة. بناء نموذج التعلم النهائي نستخدم الدالة KerasClassifier من scikit لبناء المُصنف مع الدالة السابقة create_model: # مكتبة التصنيف from keras.wrappers.scikit_learn import KerasClassifier # إنشاء النموذج مع قيم المعاملات المترفعة الأمثلية model = KerasClassifier(build_fn = create_model, # معاملات النموذج dropout_rate = 0.2, embed_dim = 32, hidden_unit = 64, optimizers = Adam, learning_rate = 0.001, # معاملات التدريب epochs=10, batch_size=256, # نسبة بيانات التقييم validation_split = 0.1) # ملائمة النموذج مع بيانات التدريب model_prediction = model.fit(X_train, y_train) يُمكن الآن رسم منحني الدقة accuracy لكل من بيانات التدريب والتقييم (لاحظ أننا في الشيفرة السابقة احتفاظنا بـ 10% من بيانات التدريب للتقييم): # معاينة دقة النموذج # التدريب والتقييم fig, ax = plt.subplots(figsize = (10, 4)) ax.plot(model_prediction.history['accuracy'], label = 'train accuracy') ax.plot(model_prediction.history['val_accuracy'], label = 'val accuracy') ax.set_title('Model Accuracy') ax.set_xlabel('Epoch') ax.set_ylabel('Accuracy') ax.legend(loc = 'upper left') plt.show() يكون للمنحي الشكل التالي: حساب مقاييس الأداء يُمكن الآن حساب مقاييس الأداء المعروفة في مسائل التصنيف (الصحة Accuracy، الدقة Precision، الاستذكار Recall، المقياس F1) للنموذج المتعلم باستخدام الشيفرة التالية: # مقاييس الأداء # مقياس الصحة from sklearn.metrics import accuracy_score # مقياس الدقة from sklearn.metrics import precision_score # مقياس الاستذكار from sklearn.metrics import recall_score # f1 from sklearn.metrics import f1_score # مصفوفة الارتباك from sklearn.metrics import confusion_matrix # تصنيف بيانات الاختبار y_pred = model.predict(X_test) # حساب مقاييس الأداء accuracy = accuracy_score(y_test, y_pred) precision=precision_score(y_test, y_pred , average='weighted') recall= recall_score(y_test, y_pred, zero_division=1, average='weighted') f1= f1_score(y_test, y_pred, zero_division=1, average='weighted') print('Model Accuracy on Test Data:', accuracy*100) print('Model Precision on Test Data:', precision*100) print('Model Recall on Test Data:', recall*100) print('Model F1 on Test Data:', f1*100) confusion_matrix(y_test, y_pred) تكون النتائج: Model Accuracy on Test Data: 90.90 Model Precision on Test Data: 90.88 Model Recall on Test Data: 90.90 Model F1 on Test Data: 90.86 array([[ 964, 22, 24, 57, 16, 12, 19], [ 18, 1029, 26, 20, 15, 9, 8], [ 13, 18, 1033, 19, 5, 8, 3], [ 45, 19, 52, 871, 59, 12, 10], [ 11, 25, 18, 38, 990, 11, 3], [ 3, 10, 13, 3, 15, 986, 3], [ 4, 10, 8, 2, 0, 8, 1063]], dtype=int64) لاحظ ارتفاع قيم جميع المقاييس مما يعني جودة المُصنف. يُمكن رسم مصفوفة الارتباك confusion matrix بشكل أوضح باستخدام المكتبة seaborn: # رسم مصفوفة الارتباك import seaborn as sns sns.set(style = 'whitegrid') fig, ax = plt.subplots(figsize = (8,6)) sns.heatmap(confusion_matrix(y_true = y_test, y_pred = y_pred), fmt = 'g', annot = True) ax.xaxis.set_label_position('top') ax.xaxis.set_ticks_position('top') ax.set_xlabel('Prediction', fontsize = 14) ax.set_ylabel('Actual', fontsize = 14) plt.show() مما يُظهر المخطط التالي: يُمكن حساب بعض مقاييس الأداء الأخرى المُستخدمة في حالة وجود أكثر من صف في المسألة (Micro, Macro, Weighted): # مقاييس الأداء في حالة أكثر من صفين print('\nAccuracy: {:.2f}\n'.format(accuracy_score(y_test, y_pred))) print('Micro Precision: {:.2f}'.format(precision_score(y_test, y_pred, average='micro'))) print('Micro Recall: {:.2f}'.format(recall_score(y_test, y_pred, average='micro'))) print('Micro F1-score: {:.2f}\n'.format(f1_score(y_test, y_pred, average='micro'))) print('Macro Precision: {:.2f}'.format(precision_score(y_test, y_pred, average='macro'))) print('Macro Recall: {:.2f}'.format(recall_score(y_test, y_pred, average='macro'))) print('Macro F1-score: {:.2f}\n'.format(f1_score(y_test, y_pred, average='macro'))) print('Weighted Precision: {:.2f}'.format(precision_score(y_test, y_pred, average='weighted'))) print('Weighted Recall: {:.2f}'.format(recall_score(y_test, y_pred, average='weighted'))) print('Weighted F1-score: {:.2f}'.format(f1_score(y_test, y_pred, average='weighted'))) # تقرير التصنيف from sklearn.metrics import classification_report print('\nClassification Report\n') print(classification_report(y_test, y_pred, target_names=classes)) مما يُعطي الناتج التالي: لاحظ ارتفاع جميع المقاييس مما يعني جودة المُصنف. تصنيف الأشخاص يُمكن الآن تصنيف الأشخاص وذلك وفق تصنيفات تغريداتهم. تقوم الشيفرة التالية بتصنيف تغريدة واحدة، فيكون دخل الدالة classify_tweet سلسلة نصية نضعها أولًا في متجهة تحوي النص. نستدعي دالة تحويل النص إلى أرقام tokenizer.texts_to_sequences ومن ثم توحيد طول المتجهة الرقمية الناتجة وذلك باستدعاء الدالة pad_sequences. # دالة تصنيف تغريدة def classify_tweet(tweet): # تحويل شعاع الكلمات إلى جملة tweet = toSentence(tweet) # وضع الجملة في شعاع ar=[] ar.append(tweet) # تحويل النص إلى قائمة من الأرقام seq = tokenizer.texts_to_sequences(ar) # توحيد طول المتجهة الرقمية pseq = pad_sequences(seq, maxlen=max_len) # استدعاء دالة التنبؤ للنموذج pred = model.predict(pseq) return pred تقوم الدالة classify_person بتصنيف شخص، ويكون معامل الدالة اسم الشخص (بفرض أن ملف تغريداته يحمل نفس الاسم مع اللاحقة csv)، إذ تعمل الدالة أولًا على تحميل تغريدات الشخص في إطار بيانات df. وأخيرًا تكون دالة تصنيف الشخص classify_person هي: # دالة تصنيف الشخص def classify_person(person_name): # تحميل تغريدات الشخص # في إطار بيانات path = person_name + '.csv' df = pd.read_csv(path) # إنشاء قاموس لعد # التغريدات من كل صف classes_count=dict() # إعطاء قيم ابتدائيه 0 for i in range(len(classes)): key=classes[i] classes_count[key]=0 # الحد الأدنى لطول التغريدة min_tweet_len=5 total=0 for _, row in df.iterrows(): tweet=row['tweet'] # تنظيف التغريدة processed_tweet=process_tweet(tweet) if len(processed_tweet)>min_tweet_len: # تصنيف التغريدة c= classify_tweet(processed_tweet) # إيجاد اسم الصف من رمزه topic=le_topics.inverse_transform(c)[0] # إضافة 1 للصف الموافق classes_count[topic]=classes_count[topic]+1 total=total+1 # ترتيب الصفوف وفق العدد # تنازلياً sorted_classes = sorted(classes_count, key=classes_count.get,reverse=True) # القاموس النهائي sorted_classes_cleaned = {} min_display=total/25 # إهمال الصفوف ذات العدد الصغير for w in sorted_classes: if classes_count[w]>min_display: sorted_classes_cleaned[w] = classes_count[w] # طباعة النتائج print(sorted_classes_cleaned) n=0 for key, value in sorted_classes_cleaned.items(): n=n+value print(person_name, "is classified as :") for key, value in sorted_classes_cleaned.items(): print(key, "(", "{:.2f}".format((value/n)*100) , "%)") # رسم فطيرة أعداد الصفوف x = sorted_classes_cleaned.keys() y = sorted_classes_cleaned.values() import matplotlib.pyplot as plt # pie plt.figure(figsize=(9,9)); plt.title(person_name, fontdict = {'fontsize':20}) plt.pie(y, labels = x,autopct='%1.1f%%') plt.show() # مثال classify_person("salem") نجد من الشيفرة السابقة، تُنشئ الدالة القاموس classes_count والذي يكون مفتاحه اسم الصف وقيمته ستكون عدد التغريدات من هذا الصف (تُعطي أولًا قيمة ابتدائية 0 لكل قيم القاموس). يُحدّد المتغير min_tweet_len الطول الأدنى للتغريدة لتؤخذ بعين الاعتبار (لا تحمل التغريدات الصغيرة جدًا معاني بل تكون على الأغلب عبارات مجاملة وترحيب). تدور الدالة على تغريدات الشخص وتستدعي من أجل كل تغريدة دالة المعالجة الأولية للتغريدة process_tweet ثم دالة التصنيف السابقة classify_tweet. يكون ناتج التصنيف رقم (ترميز الصف) ولذا تستدعي الدالة inverse_transform للحصول على اسم الصف ثم نزيد عدد الصف الموافق في القاموس بـ 1. يحسب المتغير total العدد الكلي للتغريدات التي تم أخذها بعين الاعتبار، وتُستخدم الدالة المتغير sorted لترتيب القاموس تنازليا (المعامل reverse=True) وفق العدد. نضع في المتغير min_display مثلًا القيمة total/25 وذلك بهدف عدم إظهار صفوف الشخص قليلة التواتر في تغريداته، ونضع أخيرًا في القاموس sorted_classes_cleaned الصفوف الأكثر تواترًا ونطبع النتائج ونرسم فطيرة تُمثل النسب المئوية لتغريدات الشخص. تكون النتائج مثلًا: الخلاصة عرضنا في هذه المقالة خطوات بناء نموذج تعلّم لتصنيف الأشخاص وفق تغريداتهم. يُمكن تجربة المثال كاملًا من موقع Google Colab من الرابط أو من الملف المرفق data.zip. اقرأ أيضًا تحليل المشاعر في النصوص العربية باستخدام التعلم العميق دليل المبتدئين لفهم أساسيات التعلم العميق خطوات تنفيذ مشروع عن تعلم الآلة في بايثون: الجزء الأول data.zip
  6. سننفذ في هذا المقال خوارزميةً بسيطةً لتعلم الآلة بلغة بايثون Python باستخدام مكتبة Scikit-learn، وهذه المكتبة ما هي إلا أداةٌ لتطبيق تعلّم الآلة بلغة البايثون، كما سنستخدم المُصنّف Naive Bayes (NB) مع قاعدة بياناتٍ حقيقية لمعلومات ورم سرطان الثدي، والذي سيتنبأ إذا ما كان الورم خبيثًا أم حميدًا. وفي نهاية هذا المقال ستعرف خطوات وكيفية إنشاء نموذج تنبؤي خاص بك لتَعَلّم الآلة بلغة بايثون. المتطلبات الرئيسية قبل البدء بهذا المقال لا بد من تجهيز البيئة المناسبة، وسنستخدم محرر الشيفرات البرمجية Jupyter Notebooks، وهو مفيد جدًا لتجربة وتشغيل الأمثلة الخاصة بتَعَلّم الآلة بطريقةٍ تفاعليةٍ، حيث تستطيع من خلاله تشغيل كتلًا صغيرةً من الشيفرات البرمجية ورؤية النتائج بسرعة، مما سيسهل علينا اختبار الشيفرات البرمجية وتصحيحها. يُمكنك فتح متصفح الويب والذهاب لموقع المحرر الرسمي jupyter على الوِيب لبدء العمل بسرعة، ومن ثمّ انقر فوق "جرّب المحرر التقليدي Try Classic Notebook"، وستنتقل بعدها لملفٍ جديدٍ بداخل محرر Jupyter Notebooks التفاعلي، وبذلك تجهّز نفسك لكتابة الشيفرة البرمجية بلغة البايثون. إذا رغبت بمزيدٍ من المعلومات حول محرر الشيفرات البرمجية Jupyter Notebooks وكيفيّة إعداد بيئته الخاصة لكتابة شيفرة بايثون، فيمكنك الاطلاع على: كيفية تهيئة تطبيق المفكرة jupyter notebook للعمل مع لغة البرمجة python. 1. إعداد المشروع ستحتاج أولًا لتثبيت بعض التبعيات، وذلك لإنشاء مساحة عملٍ للاحتفاظ بملفاتنا قبل أن نتمكن من تطوير برنامج التعرف على الصور، وسنستخدم بيئة بايثون 3.8 الافتراضية لإدارة التبعيات الخاصة بمشروعنا. سَنُنشئ مجلدًا جديدًا خاصًا بمشروعنا وسندخل إليه هكذا: mkdir cancer-demo cd cancer-demo سننفذّ الأمر التالي لإعداد البيئة الافتراضية: python -m venv cancer-demo سننفذّ الأمر التالي لتشغيل البيئة الافتراضية في Linux: source cancer-demo/bin/activate أما في Windows: "cancer-demo/Scripts/activate.bat" سنستخدم إصداراتٍ محددةٍ من هذه المكتبات، من خلال إنشاء ملف requirements.txt في مجلد المشروع، وسيُحدِّد هذا الملف المتطلبات والإصدارات التي سنحتاج إليها. سنفتح الملف requirements.txt في محرر النصوص، وسنُضيف الأسطر البرمجية التالية، وذلك لتحديد المكتبات التي نريدها وإصداراتها: jupyter==1.0.0 scikit-learn==1.0 سنحفظ التغييرات التي طرأت على الملف وسنخرج من محرر النصوص، ثم سنُثَبت هذه المكتبات بالأمر التالي: (cancer-demo) $ pip install -r requirements.txt بعد تثبيتنا لهذه التبعيات، سنُصبح جاهزين لبدء العمل على مشروعنا. شغّل محرر الشيفرات البرمجية Jupyter Notebook بمجرد اكتمال عملية التثبيت. هكذا: (cancer-demo) $ jupyter notebook أنشئ ملفًا جديدًا في داخل المحرر بالضغط على الزر new واختيار python 3 (ipykernal)‎ وسمه باسم ML Tutorial مثلًا، حيث ستكون في الخلية الأولى للملف عملية استيراد الوِحدة (أو المكتبة) البرمجية scikit-learn (لمزيد من المعلومات حول طريقة استيراد وحدة برمجية في لغة بايثون يمكنك الاطلاع على كيفية استيراد الوحدات في بايثون 3 سبق وأن ناقشنا فيه هذه الفكرة بالتفصيل): import sklearn يجب أن يبدو الملف الخاص بك شبيهًا بالملف التالي: والآن بعد استيرادنا للمكتبة بنجاح، سنبدأ العمل مع مجموعة البيانات لبناء نموذج تعلّم الآلة الخاص بنا. 2. استيراد مجموعة بيانات Scikit-Learn’s مجموعة البيانات التي سنتعامل معها في هذا المقال هي قاعدة بيانات الخاصة بتشخيص مرض سرطان الثدي في ولاية ويسكونسن الأمريكية. تتضمن هذه المجموعة من البيانات معلوماتٍ مختلفةٍ حول أورام سرطان الثدي، بالإضافة إلى تصنيفات الأورام سواءً كانت خبيثةً أم حميدةً. كما تحتوي على 569 حالة (أو للدقة بيانات 569 ورمًا)، كما تتضمن معلومات عن 30 ميزة لكلّ ورم، مثل: نصف قطر الورم ونسيجه ونعومته ومساحته. سنبني نموذجًا لتعلّم الآلة من مجموعة البيانات السابقة باستخدام معلومات الورم فقط للتنبؤ فيما إذا كان الورم خبيثًا أم حميدًا. يُثَبت مع مكتبة Scikit-learn مجموعات بياناتٍ مختلفةٍ افتراضيًا، ويُمكننا استيرادها لتُصبح متاحةً للاستخدام في بيئتنا مباشرةً، لنفعل ذلك: from sklearn.datasets import load_breast_cancer # Load dataset data = load_breast_cancer() سيُمثَل المتغير data ككائنٍ في البايثون، والذي سيعمل مثل عمل القاموس الذي هو نوعٌ مُضمَّنٌ في بايثون، بحيث يربط مفاتيحًا بقيمٍ على هيئة أزواجٍ، وستُؤخذ بالحسبان مفاتيح القاموس، وهي أسماء الحقول المُصنّفة target_names، والقيم الفعلية لها target، وأسماء الميّزات feature_names، والقيم الفعلية لهذه الميزات data. تُعَد الميّزات جزءًا مهمًا من أي مصنّف، إذ تُمثّل هذه الميزات خصائص مهمةً تصف طبيعة البيانات، كما ستساعدنا في عملية التنبؤ بحالة الورم (ورم الخبيث malignant tumor أو ورم حميد benign tumor)، ومن الميّزات المُفيدة المحتملة في مجموعة بياناتنا هذه، هي حجم الورم ونصف قطره ونسيجه. أنشئ في الملف نفسه بعد ذلك متغيرات جديدةً لكلّ مجموعةٍ مهمةٍ من هذه المعلومات وأسند لها البيانات: # تنظيم بياناتنا label_names = data['target_names'] labels = data['target'] feature_names = data['feature_names'] features = data['data'] والآن أصبحت لدينا قوائم لكلّ مجموعةٍ من المعلومات، ولفَهم مجموعة البيانات الخاصة بنا فهمًا صحيحًا ودقيقًا، سنُلقي نظرةً عليها من خلال طباعة حقول الصنف مثل طباعة أول عينةٍ من البيانات، وأسماء ميّزاتها، وقيمها هكذا: # الاطلاع على بياناتنا print(label_names) print(labels[0]) print(feature_names[0]) print(features[0]) إن نفذّت هذه الشيفرة بطريقةٍ صحيحةٍ فسترى النتائج التالية: نُلاحظ من الصورة أن أسماء الأصناف الخاصة بنا ستكون خبيث malignant وحميد benign (أي أن الورم سيكون إما خبيثًا أو حميدًا)، والمرتبطة بقيم ثنائية وهي إما 0 أو 1، إذ يُمثّل الرقم 0 أورامًا خبيثة ويُمثّل الرقم 1 أورامًا حميدة، لذا فإن أول مثالٍ للبيانات الموجودة لدينا هو ورمٌ خبيثٌ نصف قطره 1.79900000e+01. والآن بعد تأكدنا من تحميل بياناتنا تحميلًا صحيحًا في بيئة التنفيذ، سنبدأ العمل مع بياناتنا لبناء مصنّف باستخدام طُرق تعلّم الآلة. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن 3. تنظيم البيانات في مجموعات ينبغي عليك دائمًا اختبار النموذج على البيانات غير المرئية، وذلك لتقييم مدى جودة أداء المُصنّف، لهذا قسّم البيانات الخاصة بك إلى جزئين قبل بناء النموذج، بحيث تكون هناك مجموعةٌ للتدريب ومجموعةٌ للاختبار. تستطيع استخدام المجموعة المخصصة للتدريب من أجل تدريب وتقييم النموذج أثناء مرحلة التطوير. حيث ستمنحك منهجية تنبؤات هذا النموذج المُدرّب على المجموعة المخصصة للاختبار غير المرئية، فكرةً دقيقةً عن أداء النموذج وقوته. لحسن الحظ، لدى المكتبة Scikit-learn دالة تُدعى train_test_split()‎، والتي ستقسمُ بياناتك لهذه المجموعات. ولكن يجب أن تستورد هذه الدالة أولًا ومن ثَمّ تستخدمها لتقسيم البيانات: from sklearn.model_selection import train_test_split # تقسيم بياناتنا train, test, train_labels, test_labels = train_test_split(features, labels, test_size=0.33, random_state=42) ستُقسّمُ هذه الدّالة البيانات بطريقةٍ عشوائيةٍ باستخدام الوسيط test_size. في مثالنا لدينا الآن مجموعةً مخصصةً للاختبار test تُمثّل 33٪ من مجموعة البيانات الأصلية، وسيُشكّل الجزء المتبقي من البيانات المجموعة المخصصة للتدريب train. كما لدينا حقولٌ مخصصةٌ لكلٍ من المتغيرات، سواء أكانت مُخصّصةً للاختبار أو للتدريب، أي train_labels وtest_labels. لنُدرّب الآن نموذجنا الأول. 4. بناء النموذج وتقييمه هناك العديد من النماذج المُخَصصة لتعلّم الآلة، ولكلّ نموذجٍ منها نقاط قوةٍ وضعفٍ. في هذا المقال، سنُركّز على خوارزمية بسيطةٍ تؤدي عادةً أداءً جيدًا في مهام التصنيف الثنائية، وهي خوارزمية بايز Naive Bayes (NB). أولًا، سنستورد الوِحدة البرمجية GaussianNB ثم نُهَيّئ النموذج باستخدام الدالة GaussianNB()‎، بعدها سنُدرّب النموذج من خلال مُلاءمته مع البيانات باستخدام الدالة gnb.fit()‎: from sklearn.naive_bayes import GaussianNB # تهيئة المصنّف خاصتنا gnb = GaussianNB() # تدريب المصنّف model = gnb.fit(train, train_labels) بعد أن نُدّرب النموذج سنستخدمه للتنبؤ على المجموعة المخصصة للاختبار، وسننفذ ذلك من خلال الدّالة predict()‎، والتي ستُعيد مجموعةً من التنبؤات لكلّ نسخة بياناتٍ في المجموعة المخصصة للاختبار، ثم نطبع تنبؤاتنا لِفَهم ما حدده هذا النموذج. استخدِم الدالة predict()‎ مع مجموعة البيانات المخصصة للاختبار test واطبع النتائج: # بناء التوقعات preds = gnb.predict(test) print(preds) عند تنفيذك للشيفرة البرمجية تنفيذًا صحيحًا سترى النتائج التالية: فكما ترى، أعادت الدالة predict()‎ مصفوفةً ثُنائية القيم إما 0 أو 1، حيث تُمثل القيم المتوقعة لصنف الورم (خبيث أم حميد). والآن بعد أن حصلنا على توقعاتنا، لِنُقيِّم مدى جودة أداء هذا المُصنّف. 5. تقييم دقة النموذج نُقّيم دقة القيم المتوقَّعة لنموذجنا باستخدام مصفوفة التصنيفات الناتجة للأصناف الحقيقية التي لدينا، وذلك من خلال موازنة المصفوفتين test_labels وpreds باستخدام الدالة accuracy_score()‎ التابعة للمكتبة Scikit-learn، وذلك لتحديد دِقة المُصنّف. from sklearn.metrics import accuracy_score # تقييم الدقة print(accuracy_score(test_labels, preds)) سترى النتائج التالية: كما ترى في النتيجة، فإن المُصنّف NB دقيقٌ بنسبة 94.15٪. وهذا يعني أن المُصنِّف قادرٌ على التنبؤ الصحيح فيما إذا كان الورم خبيثًا أو حميدًا بنسبة 94.15٪ من الحالات الكُليّة. كما تُشير هذه النتائج إلى أن مجموعة الميّزات المُكونة من 30 ميزة هي مؤشراتٍ جيدةٍ لصنف الورم. بهذا تكون قد نجحت في إنشاء مصنِّفك الأول الذي يعتمد في عمله على طرق تعلّم الآلة، والآن لنعد تنظيم الشيفرة البرمجية بوضع جميع عمليات الاستيراد في أعلى الملف، إذ يجب أن تبدو النسخة النهائية من الشيفرة البرمجية خاصتك شبيةً بهذه الشيفرة: from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split from sklearn.naive_bayes import GaussianNB from sklearn.metrics import accuracy_score # تحميل البيانات data = load_breast_cancer() # تنظيم البيانات label_names = data['target_names'] labels = data['target'] feature_names = data['feature_names'] features = data['data'] # الاطلاع على البيانات print(label_names) print('Class label = ', labels[0]) print(feature_names) print(features[0]) # تقسيم البيانات train, test, train_labels, test_labels = train_test_split(features, labels, test_size=0.33, random_state=42) # تهيئة المصنّف gnb = GaussianNB() # تدريب المصنّف model = gnb.fit(train, train_labels) # بناء التوقعات preds = gnb.predict(test) print(preds) # تقييم الدقة print(accuracy_score(test_labels, preds)) والآن بإمكانك إكمال العمل على الشيفرة البرمجية، وتحسين عمل المُصنّف وتوسيعه، وكذا تجربة هذا المصنّف مع مجموعاتٍ فرعيةٍ مختلفةٍ من الميزات، أو حتى تجربة خوارزمياتٍ مختلفةٍ تمامًا. تستطيع الاطلاع على الموقع الرسمي لمكتبة Scikit-Learn لمزيدٍ من الأفكار حول تطبيق تعلّم الآلة مع البيانات لبناء شيءٍ مفيدٍ. الخلاصة لقد تعلمنا في هذا المقال كيفية إنشاء مُصنّف بالاعتماد على تعلّم الآلة بلغة بايثون باستخدام المكتبة Scikit-learn، والآن بإمكانك تحميل البيانات في بيئةٍ برمجيةٍ وتنظيمها وتدريبها، وكذا التنبؤ بأشياء بناءً عليها، وتقييم دِقّة المُصنّفات الناتجة. نتمنى أن تُساعدك هذه الخطوات في تسهيل طريقة العمل مع بياناتك الخاصة بلغة بايثون. ترجمة -وبتصرف- للفصل How To Build a Machine Learning Classifier in Python with Scikit-learn من كتاب Python Machine Learning Projects لكاتبه Michelle Morales. اقرأ أيضًا النسخة الكاملة لكتاب مدخل إلى الذكاء الاصطناعي وتعلم الآلة المرجع الشامل إلى تعلم لغة بايثون المفاهيم الأساسية لتعلم الآلة بناء شبكة عصبية للتعرف على الأرقام المكتوبة بخط اليد باستخدام مكتبة TensorFlow
  7. يُعَدّ التعلم المُعزز مجالًا فرعيًا من فروع نظرية التحكم، والتي تُعنى بالتحكم في الأنظمة التي تتغير بمرور الوقت؛ وتشمل عِدّة تطبيقاتٍ من بينها: السيارات ذاتية القيادة، وبعض أنواع الروبوتات مثل الروبوتات المُخصصة للألعاب. وسنستخدم في هذا المقال التعلم المعزز لبناء روبوتٍ لألعاب الفيديو المُخصصة لأجهزة آتاري Atari، حيث لن يُمنح هذا الروبوت إمكانية الوصول إلى المعلومات الداخلية للعبة، وإنما سُيمنح إمكانية الوصول لنتائج اللعبة المعروضة على الشاشة، وكذا المكافآت الناتجة عن هذه اللعبة فقط؛ أي أنه سيرى ما يراه أي شخصٍ سيلعبُ هذه اللعبة. في مجال تعلّم الآلة، يُعرَّف الروبوت Bot بأنه الوكيل Agent، وسيكون الوكيل بمثابة لاعبٍ في النظام، بحيث يعمل وفقًا لدالة اتخاذ القرار، وهدفنا الأساسي هو تطوير روبوتاتٍ ذكيةٍ من خلال تعليمهم وإمدادهم بقدراتٍ قويةٍ تقدر على اتخاذ القرار. نعرض، في هذا الدرس، كيفية تدريب روبوت باستخدام التعلم المعزز العميق وفق النموذج الحُر Deep Q-learning للعبة غزاة الفضاء Space Invaders، وهي لعبةٌ مخصصةٌ لجهاز أتاري أركايد Atari Arcade الكلاسيكي. فهم التعلم المعزز يكون هدف اللاعب في أي لعبة هو زيادة درجاته، وسنشير في هذا الدرس لنتيجة اللاعب على أنها مكافأته، ولتعظيم المكافأة يجب على اللاعب أن يكون قادرًا على تحسين قدراته في اتخاذ القرارات، وبصيغةٍ أخرى فلسفية، القرار هو عملية النظر إلى اللعبة، أو مراقبة حالة اللعبة، واختيار فعلٍ معينٍ مناسبٍ لهذه الحالة. يُمكن بشكل عام نمذجة روبوت لاعب في بيئة لعب بسلسلة من الحالات State(ح S) والأفعال Actions (أ A) والمكافآت Rewards (م R): ح.أ.م.ح.أ.م.ح.أ.م… أو بالانكليزية: S,A,R,S,A,R,… يهدف التعلم إلى اختيار الفعل الأنسب من مجموعة من الأفعال المُمكنة في حالة ما والذي يُمكن أن يؤدي إلى مكافأة كبيرة في النهاية. ليس من الضروري الحصول على مكافأة جيدة في الحالة التالية، بمعنى أن لا يكون الهدف محلي (مكافأة جيدة فورًا) بل أن يكون الهدف استراتيجي (ربح اللعبة مع مكافأة كبيرة في نهاية اللعبة. أي أننا سنضع في حُسباننا ما يلي: عند مشاهدة العديد من الملاحظات حول حالات الروبوت وأفعاله ومكافآته، يمكن للمرء تقدير المكافأة المناسبة لكلّ حالةٍ وفعل معينٍ وذلك حسب النهاية التي آلت إليها اللعبة (أو مرحلة متقدمة منها). تُعَد لعبة غزاة الفضاء Space Invaders من الألعاب ذات المكافأة المتأخرة؛ أي أن اللاعب يكافَأ عندما يفجّر أحد الأعداء وليس بمجرد إطلاقه للنار فقط، ومع ذلك فإن قرار اللاعب بإطلاق النار هو الدافع الصحيح للحصول على المكافأة، لذا بطريقةٍ أو بأخرى، يجب أن نتعلم منح الحالة المرتبطة بفعل الإطلاق مكافأةً إيجابيةً. المتطلبات الرئيسية لإكمال هذا الدرس بنجاحٍ، ستحتاج إلى تجهيز بيئة العمل المكونة من: حاسوب يعمل بنظام أوبونتو 18.04، وبذاكرة وصولٍ عشوائيٍ RAM لا تقِل عن 1 غيغابايت، ويجب أن يحتوي الخادم على مستخدمٍ غير مسؤولٍ non-root user وبصلاحيات sudo المُهيّأة مسبقًا، وجدار حمايةٍ مهيئٍ باستخدام UFW. وللمزيد، يمكنك الاطلاع على كيفية التهيئة الأولية لخادم أوبونتو 18.04 أو حاسوب يعمل بنظام ويندوز 10 . تثبيت بايثون 3 وإعداد بيئته البرمجية، وبإمكانك معرفة كيفية تثبيت بايثون 3 وإعداد بيئة برمجية على أوبنتو 18.04. وإذا كنت تستخدم جهازًا محليًا، فيمكنك تثبيت بايثون 3 محليًا خطوةً بخطوة، باطلاعك على كيفية تثبيت بايثون 3 وإعداد بيئته البرمجية على ويندوز 10. سنحتاج في هذا المشروع إلى المكتبات الأساسية التالية: Gym: وهي مكتبة بايثون تجعل الألعاب المختلفة متاحةً للأبحاث، وكذا جميع التبعيات الخاصة بالألعاب المخصصة لأجهزة أتاري، والتي طورتها شركة OpenAI. حيث تقدم مكتبة Gym معاييرًا عامة لكلّ لعبةٍ، وذلك لتمكننا من تقييم أداء الروبوتات والخوارزميات المختلفة بطريقةٍ موحدةٍ. Tensorflow: وهي مكتبة التعلم العميق المقدمة من غوغل، تتيح لنا إجراء العمليات الحسابية بطريقةٍ أكثر كفاءةً عن ذي قبل، حيث تؤدي ذلك من خلال بناء دوال رياضيةً باستخدام التجريدات الخاصة بمكتبة Tensorflow تحديدًا ، كما تعمل حصريًا على وِحدة المعالجة الرسومية GPU الخاصة بك. NumPy: وهي مكتبة الجبر الخطي. لإكمال هذا الدرس ستحتاج بيئةً برمجيةً للغة بايثون الإصدار 3.8 سواءً كان محليًا أو بعيدًا. ويجب أن تتضمن هذه البيئة البرمجية مدير الحِزم pip لتثبيت الحِزم، ومُنشئ البيئات الافتراضية venv لإنشاء بيئاتٍ افتراضيةٍ. قبل البدء بهذا المقال لا بد من تجهيز البيئة المناسبة، وسنستخدم محرر الشيفرات البرمجية Jupyter Notebooks، وهو مفيد جدًا لتجربة وتشغيل الأمثلة الخاصة بتَعَلّم الآلة بطريقةٍ تفاعليةٍ، حيث تستطيع من خلاله تشغيل كتلًا صغيرةً من الشيفرات البرمجية ورؤية النتائج بسرعة، مما سيسهل علينا اختبار الشيفرات البرمجية وتصحيحها. يُمكنك فتح متصفح الويب والذهاب لموقع المحرر الرسمي jupyter على الوِيب لبدء العمل بسرعة، ومن ثمّ انقر فوق "جرّب المحرر التقليدي Try Classic Notebook"، وستنتقل بعدها لملفٍ جديدٍ بداخل محرر Jupyter Notebooks التفاعلي، وبذلك تجهّز نفسك لكتابة الشيفرة البرمجية بلغة البايثون. إذا رغبت بمزيدٍ من المعلومات حول محرر الشيفرات البرمجية Jupyter Notebooks وكيفيّة إعداد بيئته الخاصة لكتابة شيفرة بايثون، فيمكنك الاطلاع على: كيفية تهيئة تطبيق المفكرة jupyter notebook للعمل مع لغة البرمجة python. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن إعداد المشروع ستحتاج أولًا لتثبيت بعض التبعيات، وذلك لإنشاء مساحة عملٍ للاحتفاظ بملفاتنا قبل أن نتمكن من تطوير برنامج التعرف على الصور، وسنستخدم بيئة بايثون 3.8 الافتراضية لإدارة التبعيات الخاصة بمشروعنا. سَنُنشئ مجلدًا جديدًا خاصًا بمشروعنا وسندخل إليه هكذا: mkdir qlearning-demo cd qlearning-demo سننفذّ الأمر التالي لإنشاء البيئة الافتراضية: python -m venv qlearning-demo ومن ثم الأمر التالي في Linux لتنشيط البيئة الافتراضية: source qlearning-demo/bin/activate أما في Windows، فيكون أمر التنشيط: "qlearning-demo/Scripts/activate.bat" سنستخدم إصداراتٍ محددةٍ من هذه المكتبات، من خلال إنشاء ملف requirements.txt في مجلد المشروع، وسيُحدِّد هذا الملف المتطلبات والإصدارات التي سنحتاج إليها. نفتح الملف requirements.txt في محرر النصوص، ونُضيف الأسطر التالية، وذلك لتحديد المكتبات التي نريدها وإصداراتها: tensorflow==2.6.0 numpy==1.19.5 cmake==3.21.4 tf-slim==1.1.0 atari-py==0.2.6 gym==0.19.0 matplotlib==3.5.0 PyVirtualDisplay==2.2 jupyter keras==2.6.* gym[all] PyVirtualDisplay سنحفظ التغييرات التي طرأت على الملف وسنخرج من محرر النصوص، ثم سنُثَبت هذه المكتبات بالأمر التالي: (qlearning-demo) $ pip install -r requirements.txt بعد تثبيتنا لهذه التبعيات، سنُصبح جاهزين لبدء العمل على مشروعنا. كتابة الشيفرة البرمجية شغّل محرر الشيفرات البرمجية Jupyter Notebook بمجرد اكتمال عملية التثبيت. هكذا: (qlearning-demo) $ jupyter notebook أنشئ ملفًا جديدًا في داخل المحرر بالضغط على الزر new واختيار python 3 (ipykernal)‎ وسمه باسم main مثلًا، حيث ستكون في الخلية الأولى للملف عملية استيراد الوِحدات (أو المكتبات) البرمجية اللازمة. (لمزيد من المعلومات حول طريقة استيراد وحدة برمجية في لغة بايثون يمكنك الاطلاع على كيفية استيراد الوحدات في بايثون 3 سبق وأن ناقشنا فيه هذه الفكرة بالتفصيل). نقوم أولًا بتثبيت مكتبة مساعدة في نمذجة الحركات الفيزيائية: تثبيت محرك المحاكاة الفيزيائية # !curl https://mujoco.org/download/mujoco210-linux-x86_64.tar.gz --output mujoco210-linux-x86_64.tar.gz !tar -xf mujoco210-linux-x86_64.tar.gz import os إضافة متغير يدل على مسار المحرك# os.environ['MUJOCO_PY_MUJOCO_PATH'] = "/content/mujoco210" ثم نقوم باستيراد جميع المكتبات اللازمة: # مكتبة العمليات الرياضية import numpy as np # مكتبات الألعاب import gym from gym import logger as gymlogger from gym.wrappers import Monitor # مكتبات التعلم العميق import tensorflow as tf import tf_slim # مكتبة للرسوميات import matplotlib.pyplot as plt %matplotlib inline # تفعيل توافقية نسخ مكتبة التعلم العميق tf.compat.v1.disable_eager_execution() # مكتبة لاستخدام القائمة مفتوحة الطرفين from collections import deque, Counter # مكتبة للمسارات import glob # مكتبة دخل-خرج import io # مكتبة ترميز 64 import base64 # مكتبة عرض HTML from IPython.display import HTML نُنشئ بيئة لعبة غزاة الفضاء SpaceInvaders-v0 باستخدام الدالة gym.make. نستخدم الخاصية action_space.n لبيئة اللعبة لمعرفة عدد الأفعال الممكنة في اللعبة، ونستخدم الدالة get_action_meanings للحصول على أسماء هذه الأفعال الممكنة: # تهيئة بيئة عمل اللعبة env = gym.make("SpaceInvaders-v0") # معاينة عدد الأفعال الممكنة n_outputs = env.action_space.n print(n_outputs) # معاينة أسماء الأفعال الممكنة print(env.get_action_meanings()) يُبين ناتج التنفيذ عدد الأفعال الممكنة في اللعبة وأسمائها: 6 ['NOOP', 'FIRE', 'RIGHT', 'LEFT', 'RIGHTFIRE', 'LEFTFIRE'] يُمكن طباعة شاشة اللعبة الأولية وذلك بعد استدعاء دالة التهيئة الأولية لبيئة اللعبة reset: # التهيئة الأولية state = env.reset() # معاينة الوضع الأولي للعبة plt.imshow(state) مما يُعطي صورة شاشة اللعبة التالية: نستخدم فيما يلي الدالة الأساسية env.step، والتي تُعيد القيم التالية: الحالة state: وهي الحالة الجديدة للعبة بعد تطبيق فعلٍ معينٍ. المكافأة reward: وهي الزيادة في الدرجة المترتبة عن فعلٍ معينٍ. وفي نمط الألعاب المعتمدة على النقاط، يكون هدف اللاعب هو تعظيم المكافأة الإجمالية، بزيادة مجموع النقاط لأقصى درجةٍ ممكنةٍ. حالة انتهاء اللعبة done: تأخذ القيمة true عندما يخسر اللاعب ويفقد جميع الفرص المتاحة له. المعلومات info: وهي المعلومات الجانبية، وسنتجاوز شرحها حاليًا. نشرح مبدأ اللعبة عبر الشيفرة التالية والتي تدخل في حلقة تُبقي اللعبة مستمرة إلى أن يخسر اللاعب بموته. تختار الدالة sample أحد الأفعال الستة الممكنة بشكل عشوائي. يؤدي تنفيذ الفعل (باستخدام الدالة step) إلى الانتقال إلى حالة جديدة مع مكافأة ممكنة في بعض الحالات. تنتهي اللعبة بموت اللاعب (done=true). env.reset() # المكافأة الإجمالية episode_reward = 0 while True: # اختيار فعل بشكل عشوائي action = env.action_space.sample() # تنفيذ الفعل # الانتقال لحالة جديدة state, reward, done, info = env.step(action) if (reward>0): # طباعة القيم في حال مكافأة أكبر من الصفر print(env.get_action_meanings()[action]," ==> ", reward, " ,(",episode_reward,")" ) # إضافة مكافأة الفعل للمكافأة الإجمالية episode_reward += reward # اختبار نهاية اللعبة if done: # طباعة المكافأة النهائية print('Reward: %s' % episode_reward) break # معاينة الوضع النهائي للعبة plt.imshow(state) plt.show() يكون الخرج مثلًا: FIRE ==> 5.0 ,( 0.0 ) NOOP ==> 5.0 ,( 5.0 ) RIGHTFIRE ==> 10.0 ,( 10.0 ) RIGHT ==> 15.0 ,( 20.0 ) RIGHT ==> 20.0 ,( 35.0 ) FIRE ==> 25.0 ,( 55.0 ) RIGHT ==> 10.0 ,( 80.0 ) RIGHTFIRE ==> 15.0 ,( 90.0 ) Reward: 105.0 كما تُبين واجهة اللعبة: نحتاج إلى إجراء معالجة أولية لصور اللعبة (والتي ستكون لاحقًا دخلًا لشبكات التعلم العصبية)، ولذا نُعرّف الدالة التالية والتي تقتطع الجزء الهام من صورة اللعبة وتُنفّذ بعض عمليات المعالجة عليها: def preprocess_state(state): # تحجيم الصورة واقتطاع الجزء الهام منها img = state[25:201:2, ::2] # تحويل الصورة إلى درجات الرمادي img = img.mean(axis=2) color = np.array([210, 164, 74]).mean() # تحسين التباين في الصورة img[img==color] = 0 # تطبيع القيم من -1 إلى +1 img = (img - 128) / 128 - 1 return img.reshape(88,80) نستدعي الدالة السابقة مثلًا على صورة بداية اللعبة لنعاين ناتج المعالجة: # التهيئة الأولية state = env.reset() # معاينة الصورة بعد عمليات التهيئة state_preprocessed = preprocess_state(state).reshape(88,80) print(state.shape) print(state_preprocessed.shape) plt.imshow(state_preprocessed) plt.show() يكون الخرج: (210, 160, 3) (88, 80) والصورة المُعالجة: نُعرّف فيما يلي الدالة stack_frames والتي تُجهز قائمة من الصور عددها (4 مثلًا)، نخزن العدد ضمن المتغير stack_size. تحوي القائمة نفس صورة البداية مكررة العدد نفسه stack_size مرة عند بداية كل حلقة episode لعب. لاحظ أننا نستخدم البنية الخاصة في بايثون المدعوة deque (قائمة مفتوحة الطرفين) والتي يُمكن لنا الإضافة عليها والحذف منها وذلك من طرفيها. # نستخدم 4 صور متعاقبة لملاحقة الحركة stack_size = 4 # تهيئة قائمة مفتوحة الطرفين # تحوي أربعة صور ذات قيم 0 stacked_frames = deque([np.zeros((88,80), dtype=np.int) for i in range(stack_size)], maxlen=stack_size) def stack_frames(stacked_frames, state, is_new_episode): # المعالجة الأولية للصورة frame = preprocess_state(state) # حلقة جديدة if is_new_episode: # مسح الصور السابقة المكدّسة stacked_frames = deque([np.zeros((88,80), dtype=np.int) for i in range(stack_size)], maxlen=stack_size) # تكرار الصورة المعالجة في كل حلقة جديدة من اللعبة for i in range(stack_size): stacked_frames.append(frame) # تكديس الصور stacked_state = np.stack(stacked_frames, axis=2) else: # بما أن القائمة مفتوحة الطرفين تضيف من أقصى اليمين # نُحضر الصورة أقصى اليمين frame=stacked_frames[-1] # إضافة الصورة stacked_frames.append(frame) # إنشاء الحالة stacked_state = np.stack(stacked_frames, axis=2) return stacked_state, stacked_frames نُعرّف شبكة عصبية مؤلفة من ست طبقات: ثلاثة طبقات تلافيفية، تليها طبقة مسطحة flatten، ومن ثم طبقة موصولة بشكل كامل fully connected، تليها طبقة الخرج. def q_network(X, name_scope): # تهيئة الطبقات initializer = tf.compat.v1.keras.initializers.VarianceScaling(scale=2.0) with tf.compat.v1.variable_scope(name_scope) as scope: # تهيئة الطبقات التلافيفية layer_1 = tf_slim.conv2d(X, num_outputs=32, kernel_size=(8,8), stride=4, padding='SAME', weights_initializer=initializer) tf.compat.v1.summary.histogram('layer_1',layer_1) layer_2 = tf_slim.conv2d(layer_1, num_outputs=64, kernel_size=(4,4), stride=2, padding='SAME', weights_initializer=initializer) tf.compat.v1.summary.histogram('layer_2',layer_2) layer_3 = tf_slim.conv2d(layer_2, num_outputs=64, kernel_size=(3,3), stride=1, padding='SAME', weights_initializer=initializer) tf.compat.v1.summary.histogram('layer_3',layer_3) # تسطيح نتيجة الطبقة الثالثة قبل تمريرها إلى الطبقة # التالية الموصولة بشكل كامل flat = tf_slim.flatten(layer_3) # إدراج الطبقة الموصولة بشكل كامل fc = tf_slim.fully_connected(flat, num_outputs=128, weights_initializer=initializer) tf.compat.v1.summary.histogram('fc',fc) # إضافة طبقة الخرج النهائية output = tf_slim.fully_connected(fc, num_outputs=n_outputs, activation_fn=None, weights_initializer=initializer) tf.compat.v1.summary.histogram('output',output) # تخزين معاملات الشبكة كأوزان vars = {v.name[len(scope.name):]: v for v in tf.compat.v1.get_collection(key=tf.compat.v1.GraphKeys.TRAINABLE_VARIABLES, scope=scope.name)} # إرجاع كل من المتغيرات والخرج #Return both variables and outputs together return vars, output نُعرّف الدالة epsilon_greedy لتنفيذ السياسة المدعوة بسياسة إبسلون الشرهة epsilon greedy policy والتي تختار أفضل فعل باحتمال "1 - إبسلون" أو أي فعل آخر عشوائي باحتمال إبسلون. لاحظ أننا نُخفّض قيمة إبسلون مع مرور الوقت (زيادة عدد الخطوات) كي تستخدم هذه السياسة الأفعال الجيدة فقط. epsilon = 0.5 eps_min = 0.05 eps_max = 1.0 eps_decay_steps = 500000 def epsilon_greedy(action, step): p = np.random.random(1).squeeze() # متحهة أحادية epsilon = max(eps_min, eps_max - (eps_max-eps_min) * step/eps_decay_steps) if p < epsilon: return np.random.randint(n_outputs) else: return action نهيئ فيما يلي ذاكرة تخزين مؤقت من النمط قائمة مفتوحة الطرفين deque لنضع فيها معلومات اللعب من الشكل SARSA. نُخزّن كل خبرة الروبوت أي (الحالة، الفعل، المكافأة) state, action, rewards في الذاكرة المؤقتة وهي الخبرة المكتسبة المفيدة عند إعادة اللعب experience replay buffer ونقوم لاحقًا بأخذ عينات منها y-values لتوليد القيم التي سنُدرب الشبكة عليها: buffer_len = 20000 # نستخدم لذاكرة التخزين المؤقت قائمة مفتوحة الطرفين exp_buffer = deque(maxlen=buffer_len) نُعرّف الدالة sample_memories التي تنتقي مجموعة عشوائية من تجارب التدريب: # اختيار مجموعة عشوائية من تجارب التدريب # بطول حجم دفعة التدريب def sample_memories(batch_size): perm_batch = np.random.permutation(len(exp_buffer))[:batch_size] mem = np.array(exp_buffer)[perm_batch] return mem[:,0], mem[:,1], mem[:,2], mem[:,3], mem[:,4] نُعرّف فيما يلي جميع المعاملات المُترفعة hyperparameters للشبكة مع قيمها. اخترنا هذه القيم بعد بناًء على الخبرة في الشبكات العصبية وبعد العديد من التجارب. num_episodes = 2000 batch_size = 48 input_shape = (None, 88, 80, 1) learning_rate = 0.001 # التعديل لملائمة الصور المكدّسة X_shape = (None, 88, 80, 4) discount_factor = 0.97 global_step = 0 copy_steps = 100 steps_train = 4 start_steps = 2000 نُعرّف حاوية الدخل X: tf.compat.v1.reset_default_graph() # تعريف حاوية دخل الشبكة العصبية أي حالة اللعبة X = tf.compat.v1.placeholder(tf.float32, shape=X_shape) # تعريف متغير بولياني لقلب حالة التدريب in_training_mode = tf.compat.v1.placeholder(tf.bool) نبني فيما يلي شبكتين: الشبكة الأولية والشبكة الهدف مما يسمح بتوليد البيانات والتدريب بشكل متزامن: # بناء الشبكة التي تولد جميع قيم التعلم لجميع الأفعال في الحالة mainQ, mainQ_outputs = q_network(X, 'mainQ') # بشكل مشابهة نبني الشبكة الهدف لقيم التعلم targetQ, targetQ_outputs = q_network(X, 'targetQ') # تعريف حاوية قيم الأفعال X_action = tf.compat.v1.placeholder(tf.int32, shape=(None,)) Q_action = tf.reduce_sum(input_tensor=targetQ_outputs * tf.one_hot(X_action, n_outputs), axis=-1, keepdims=True) # نسخ قيم معاملات الشبكة إلى الشبكة الهدف copy_op = [tf.compat.v1.assign(main_name, targetQ[var_name]) for var_name, main_name in mainQ.items()] copy_target_to_main = tf.group(*copy_op) نُعرّف فيما يلي الخرج ونقوم بحساب الخسارة: # تعريف حاوبة الخرج y = tf.compat.v1.placeholder(tf.float32, shape=(None,1)) # حساب الخسارة والتي هي الفرق بين القيمة الحقيقية والقيمة المتوقعة loss = tf.reduce_mean(input_tensor=tf.square(y - Q_action)) # تحسين الخسارة optimizer = tf.compat.v1.train.AdamOptimizer(learning_rate) training_op = optimizer.minimize(loss) نبدأ الآن جلسة Tensorflow ونُشغّل النموذج model: نُجري أولًا المعالجة الأولية لصورة شاشة اللعب ومن ثم نُمرر الناتج (الحالة s) إلى الشبكة العصبية DQN والتي تُعيد جميع قيم التعلم Q-values. نختار أحد الأفعال a باستخدام سياسة إبسلون الجشعة. نُنفذ الفعل a على الحالة s وننتقل إلى حالة جديدة snew مع حصولنا على مكافأة (تكون الحالة الجديدة هي الصورة المعالجة لشاشة اللعب التالية) نُخزّن هذا الانتقال في الذاكرة المؤقتة على الشكل ,a,r,snew> ننتقي بعض الانتقالات من الذاكرة العشوائية ونحسب الخسارة. نُعدّل معاملات الشبكة لتخفيض الخسارة. ننسخ أوزان شبكة التدريب إلى الشبكة الفعلية. نكرر الخطوات السابقة عددًا من المرات (num_episodes). with tf.compat.v1.Session() as sess: init = tf.compat.v1.global_variables_initializer() init.run() # من أجل كل دورة history = [] for i in range(num_episodes): done = False obs = env.reset() epoch = 0 episodic_reward = 0 actions_counter = Counter() episodic_loss = [] # تكديس الصور في الخطوة الأولى obs,stacked_frames= stack_frames(stacked_frames,obs,True) # الدوران طالما لم نصل للحالة النهائية while not done: # توليد البيانات باستخدام الشبكة غير المدربة # إدخال صورة اللعبة والحصول على قيم التعلم # من أجل كل فعل actions = mainQ_outputs.eval(feed_dict={X:[obs], in_training_mode:False}) # اختيار الفعل action = np.argmax(actions, axis=-1) actions_counter[str(action)] += 1 # استخدام سياسة ابسلون الشرهة لاختيار الفعل action = epsilon_greedy(action, global_step) # تنفيذ الفعل # والانتقال للحالة التالية وحساب المكافأة next_obs, reward, done, _ = env.step(action) # تكديس مابين الدورات next_obs, stacked_frames = stack_frames(stacked_frames, next_obs, False) # تخزين الانتقال كتجربة في # الذاكرة المؤقتة لإعادة اللعب exp_buffer.append([obs, action, next_obs, reward, done]) # تدريب الشبكة من الذاكرة المؤقتة لإعادة اللعب بعد عدة خطوات معينة if global_step % steps_train == 0 and global_step > start_steps: # تحوي الذاكرة المؤقتة لإعادة اللعب # كل ما تمت معالجته وتكديسه # mem[:,0], mem[:,1], mem[:,2], mem[:,3], mem[:,4] o_obs, o_act, o_next_obs, o_rew, o_done = sample_memories(batch_size) # الحالات o_obs = [x for x in o_obs] # الحالات التالية o_next_obs = [x for x in o_next_obs] # الأفعال التالية next_act = mainQ_outputs.eval(feed_dict={X:o_next_obs, in_training_mode:False}) # المكافآت y_batch = o_rew + discount_factor * np.max(next_act, axis=-1) * (1-o_done) train_loss, _ = sess.run([loss, training_op], feed_dict={X:o_obs, y:np.expand_dims(y_batch, axis=-1), X_action:o_act, in_training_mode:True}) episodic_loss.append(train_loss) # نسخ أوزان الشبكة الرئيسية إلى الشبكة الهدف if (global_step+1) % copy_steps == 0 and global_step > start_steps: copy_target_to_main.run() obs = next_obs epoch += 1 global_step += 1 episodic_reward += reward next_obs=np.zeros(obs.shape) exp_buffer.append([obs, action, next_obs, reward, done]) obs= env.reset() obs,stacked_frames= stack_frames(stacked_frames,obs,True) history.append(episodic_reward) print('Epochs per episode:', epoch, 'Episode Reward:', episodic_reward,"Episode number:", len(history)) نرسم المكافآت وفق الحلقات: plt.plot(history) plt.show() مما يُعطي مثلًا: يُمكن في النهاية معاينة أداء الروبوت وتسجيل فيديو للعبة: # تسجيل فيديو بهدف التقييم gymlogger.set_level(40) def show_video(): # مسار ملف الفيديو mp4list = glob.glob('video/*.mp4') # التأكد من وجود الفيديو if len(mp4list) > 0: mp4 = mp4list[0] # فتح ملف الفيديو video = io.open(mp4, 'r+b').read() # التحويل لكود 64 encoded = base64.b64encode(video) # عرض الفيديو في حالة حاسوب محلي # HTML بتوليد ipythondisplay.display(HTML(data='''<video alt="test" autoplay loop controls style="height: 400px;"> <source src="data:video/mp4;base64,{0}" type="video/mp4" /> </video>'''.format(encoded.decode('ascii')))) else: print("Could not find video") def wrap_env(env): env = Monitor(env, './video', force=True) return env # تقييم النموذج environment = wrap_env(gym.make('SpaceInvaders-v0')) done = False observation = environment.reset() new_observation = observation prev_input = None with tf.compat.v1.Session() as sess: init.run() observation, stacked_frames = stack_frames(stacked_frames, observation, True) while True: # الحصول على قيم التعلم actions = mainQ_outputs.eval(feed_dict={X:[observation], in_training_mode:False}) # الحصول على الفعل action = np.argmax(actions, axis=-1) actions_counter[str(action)] += 1 # اختيار الفعل باستخدام سياسة إبسلون الشرهة action = epsilon_greedy(action, global_step) # يجب إضافة التعليمة التالية على حاسوب محلي # environment.render() new_observation, stacked_frames = stack_frames(stacked_frames, new_observation, False) observation = new_observation # تنفيذ الفعل و الانتقال للخطوة التالية new_observation, reward, done, _ = environment.step(action) if done: break environment.close() يُمكن أن يكون الفيديو مثلًا: الخلاصة بنينا في هذا الدرس روبوتًا يلعب لعبة غزو الفضاء وذلك باستخدام التعلم المعزز العميق وفق النموذج الحُر Deep Q-learnin. السؤال الطبيعي الذي سيخطر ببالنا هو، هل يمكننا بناء روبوتاتٍ لألعابٍ أكثر تعقيدًا مثل لعبة StarCraft 2؟ فكما اتضح لنا، إن هذا سؤالٌ بحثيٌ معلقٌ ومدعومٌ بأدواتٍ مفتوحة المصدر من شركاتٍ كبيرةٍ مثل غوغل Google وديب مايند DeepMind وشركة بليزارد Blizzard. فإذا كانت هذه المشاكل تهمك فعلًا، فيمكنك الاطلاع على المشاكل الحالية التي يواجهونها. يُمكن تجربة المثال كاملًا من موقع Google Colab من الرابط. المصادر Optimized Space Invaders using Deep Q-learning: An Implementation in Tensorflow 2.0 Getting Started with Gym https://gym.openai.com/envs/SpaceInvaders-v0/ Python Machine Learning Projects. اقرأ أيضًا بناء شبكة عصبية للتعرف على الأرقام المكتوبة بخط اليد باستخدام مكتبة TensorFlow بناء مصنف بالاعتماد على طرق تعلم الآلة بلغة بايثون باستخدام مكتبة Scikit-Learn النسخة الكاملة من كتاب مدخل إلى الذكاء الاصطناعي وتعلم الآلة
  8. تُعدّ مسألة تقييم نماذج تعلم الآلة، أي حساب مجموعة من مقاييس تقييم الأداء والتي تُبرهن على وصول النموذج المُتعلم إلى درجة جيدة من التعلم وبحيث يُمكن الاعتماد عليه واستخدامه في مسألة ما، أمرًا أساسيًا في مسائل تعلم الآلة. تسمح هذه المقاييس، في نهاية المطاف، بالمقارنة بين مختلف نماذج التعلم المُمكن استخدامها (والتي يولد كل منها باستخدام خوارزمية ما معينة)، مما يسمح باختيار الأنسب منها. نعرض في هذه المقالة مجموعة المقاييس الأكثر استخدامًا لتقييم نماذج تعلم التصنيف classification وتقييم نماذج الانحدار regression ومن ثم نعرض كيفية المقارنة بين عدة نماذج تعلم ممكنة واختيار الأفضل منها. مقاييس تقييم نماذج تعلم التصنيف تهدف نماذج تعلم التصنيف إلى بناء مُصنف يُمكن استخدامه لتصنيف غرض ما إلى صف مُعين مثلًا ليكن لدينا مسألة تعلم تصنيف صورة إلى كلب أو قط، وبفرض أن لدينا عشرة صور لها قيم الصفوف التالية (كلب أم قط): Actual values = ['cat', 'dog', 'cat', 'dog', 'cat', 'cat', 'dog', 'cat, 'dog', 'cat'] وبفرض أن نموذج تصنيف أعطى التصنيفات التالية لنفس الصور (ندعوها عادًة تنبؤ نموذج التعلم): Predicted values = ['cat', 'cat', 'cat', 'dog', 'cat', 'cat', 'dog', 'dog', 'dog', 'dog'] من الواضح أن نموذج التصنيف أصاب في بعض الحالات وأخطأ في البعض الآخر. والسؤال المطروح هنا بشكل أساسي: ما كفاءة (تقييم) هذا النموذج؟ مصفوفة الارتباك confusion matrix وهي عبارة عن جدول يُستخدم لبيان كفاءة نموذج تعلم التصنيف، إذ يعرض عدد حالات الصواب والخطأ المُمكنة المختلفة. يُبين الجدول التالي مثلًا الحالات المختلفة في المثال السابق (ندعو، للتبسيط، صف الكلب بالموجب وصف القط بالسالب): table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } تنبؤ نموذج التصنيف القيمة الحقيقية: كلب (موجب) القيمة الحقيقية: قط (سالب) كلب (موجب) 3 1 قط (سالب) 2 4 يُبين الجدول السابق أن نموذج التعلم أصاب في 3 حالات (الحقيقة: كلب / التنبؤ: كلب) + 4 حالات (الحقيقة: قط / التنبؤ: قط) = 7 حالات. وأخطأ في حالتين (الحقيقة: كلب / التنبؤ: قط) + حالة (الحقيقة: قط / التنبؤ: كلب) = 3 حالات. تنبؤ نموذج التصنيف القيمة الحقيقية: (موجب) القيمة الحقيقية: (سالب) موجب عدد الحالات الصحيحة الموجبة عدد الحالات الخاطئة الموجبة سالب عدد الحالات الخاطئة السالبة عدد الحالات الصحيحة السالبة الحالات الصحيحة الموجبة True Positive الحالات الصحيحة الموجبة True Positive وتختصر إلى TP وهي الحالات التي يكون من أجلها تنبؤ النموذج موجب والقيمة الحقيقة موجب (تنبؤ صحيح). الحالات الصحيحة السالبة True Negative الحالات الصحيحة السالبة True Negative وتختصر إلى TN وهي الحالات التي يكون من أجلها تنبؤ النموذج سالب والقيمة الحقيقة سالب (تنبؤ صحيح). الحالات الخاطئة الموجبة False Positive (FP) الحالات الخاطئة الموجبة False Positive وتختصر إلى FP وهي الحالات التي يكون من أجلها تنبؤ النموذج موجب والقيمة الحقيقة سالب (تنبؤ خاطئ). الحالات الخاطئة السالبة False Negative (FN) الحالات الخاطئة السالبة False Negative وتختصر إلى FN وهي الحالات التي يكون من أجلها تنبؤ النموذج سالب والقيمة الحقيقة موجب (تنبؤ خاطئ). مقياس الصحة Accuracy مقياس الصحة Accuracy وهو نسبة التصنيفات الصحيحة من العدد الكلي للأمثلة: ????????= (??+??)/(??+??+??+??) * 100 تكون الصحة في مثالنا السابق: ????????= (3+4)/10 * 100 = 70% مقياس الدقة Precision مقياس الدقة Precision هو نسبة التصنيفات الصحيحة للأمثلة الموجبة على العدد الكلي للأمثلة التي صُنّفت موجبة: ?????????= ??/(??+??) * 100 تكون الدقة في مثالنا السابق: ?????????= 3/(3+1) * 100 = 75% مقياس الاستذكار Recall مقياس الاستذكار Recall هو نسبة التصنيفات الصحيحة للأمثلة الموجبة على العدد الكلي للأمثلة الموجبة (يُدعى هذا المقياس أيضًا بالحساسية Sensitivity أو نسبة الموجب الصحيح True Positive Rate): ??????= ??/(??+??) * 100 يكون الاستذكار في مثالنا السابق: ??????= 3/(3+2) * 100 = 60% المقياس F1 المقياس F1 وهو مقياس يوازن بين الدقة والاستذكار في قيمة واحدة: ?1−?????= (2∗?????????∗??????)/(?????????+??????) يكون F1 في مثالنا السابق: ?1−?????= (2∗75∗60)/(75+60) = 66.66% الخصوصية Specificity الخصوصية Specificity هو نسبة التصنيفات الخاطئة للأمثلة الموجبة على العدد الكلي للأمثلة السالبة (يُدعى هذا المقياس أيضًا بنسبة الموجب الخاطئ False Positive Rate): ???= ??/(??+??) * 100 تكون الخصوصية في مثالنا السابق: ???= 1/(1+4) * 100 = 20% حساب مقاييس الأداء في بايثون توفر المكتبة sklearn.metrics في بايثون إمكانية حساب كل المقاييس السابقة، كما تُبين الشيفرة البرمجية التالية: # تقييم نماذج التصنيف from sklearn.metrics import confusion_matrix from sklearn.metrics import accuracy_score from sklearn.metrics import precision_score from sklearn.metrics import recall_score from sklearn.metrics import f1_score # الصفوف actual = ['cat', 'dog', 'cat', 'dog', 'cat', 'cat', 'dog', 'cat', 'dog', 'cat'] # التنيؤ predicted = ['cat', 'cat', 'cat', 'dog', 'cat', 'cat', 'dog', 'dog', 'dog', 'dog'] # مصفوفة الارتباك cf_matrix = confusion_matrix(actual , predicted, labels=["dog", "cat"] ) print ('Confusion Matrix :') print (cf_matrix) # مقاييس الأداء print ('Accuracy Score :{:.2f}'.format(accuracy_score(actual, predicted)*100)) print ('Precision Score :{:.2f}'.format(precision_score(actual, predicted, pos_label='dog')*100)) print ('Recall Score :{:.2f}'.format(recall_score(actual, predicted, pos_label='dog')*100)) print ('F1 Score :{:.2f}'.format(f1_score(actual, predicted, pos_label='dog')*100)) print ('Specificity :{:.2f}'.format(cf_matrix[0,1]/(cf_matrix[0,1]+cf_matrix[1,1])*100)) نستخدم في الشيفرة السابقة كل من الدوال التالية والتي يكون لها معاملين هما قائمة القيم الحقيقية وقائمة قيم التنبؤ: الوصف الدالة حساب مصفوفة الارتباك confusion_matrix حساب الصحة accuracy_score حساب الدقة precision_score حساب الاستذكار recall_score حساب F1 f1_score تكون نتائج مثالنا: Confusion Matrix : [[3 1] [2 4]] Accuracy Score :70.00 Precision Score :60.00 Recall Score :75.00 F1 Score :66.67 يُمكن أيضًا رسم مصفوفة الارتباك باستخدام المكتبة seaborn كما يلي: import seaborn as sns sns.heatmap(cf_matrix, annot=True) مما يُظهر الشكل التالي: خصائص المُستقبل التشغيلية ROC خصائص المُستقبل التشغيلية ROC -اختصار Receiver Operating Characteristic- هو منحني يُبين كفاءة نموذج التصنيف في قدرته على الفصل بين الصفوف الموجبة والسالبة. كي يكون نموذج تصنيف ممتازًا، يجب أن يصل إلى الزاوية العليا اليسارية أي أن تكون نسبة الصفوف الموجبة الصحيحة (الاستذكار) TPR أقرب للواحد، ونسبة الصفوف الموجبة الخاطئة (الخصوصية) FPR أقرب للصفر. يُبين الشكل التالي منحنيات ROC مختلفة ممكنة. كلما اقتربنا من أعلى اليسار كان المُصنّف أفضل: المساحة تحت المنحني AUC كلما كانت المساحة AUC -اختصار Area Under the Curve- تحت منحني ROC أكبر (أقرب من الواحد)، كان المُصنّف أفضل (لأن ذلك يعني أن المنحني أقرب للأعلى يسارًا). يُمكن رسم منحني ROC في بايثون وحساب المساحة تحت المنحني AUC كما تُبين الشيفرة البرمجية التالية لمثالنا السابق: # مكتبة الترميز from sklearn.preprocessing import LabelEncoder # الصفوف actual = ['cat', 'dog', 'cat', 'dog', 'cat', 'cat', 'dog', 'cat', 'dog', 'cat'] # التنيؤ predicted = ['cat', 'cat', 'cat', 'dog', 'cat', 'cat', 'dog', 'dog', 'dog', 'dog'] # ترميز الصفوف le = LabelEncoder() # ترميز الصفوف كأرقام actual=le.fit_transform(actual) # ترميز التنبؤ كأرقام predicted = le.fit_transform(predicted) # مكتبة المقاييس اللازمة from sklearn import metrics # مكتبة الرسم import matplotlib.pyplot as plt # حساب المقاييس fpr, tpr, _ = metrics.roc_curve(actual, predicted) auc = metrics.roc_auc_score(actual, predicted) # رسم المنحني plt.plot(fpr,tpr,label="AUC="+str(auc)) plt.ylabel('True Positive Rate') plt.xlabel('False Positive Rate') plt.legend(loc=4) plt.show() لاحظ استخدام الصف LabelEncoder من المكتبة sklearn.preprocessing لترميز الصفوف كأرقام. يكون الإظهار: لاحظ أن المساحة تحت المنحني تُساوي إلى 0.75 مما يعني أن المُصنف جيد نسبيًا. تقييم نماذج الانحدار نُذكّر أولًا بأن الهدف من نماذج الانحدار هو التنبؤ بقيمة رقمية y انطلاقًا من قيمة (أو مجموعة من القيم) x. ليكن لدينا مثلًا جدول بيانات التدريب التالي والذي يُعطي عدد الأعطال لآلة وفق عمر الآلة بالسنوات: Failures Age 15 10 30 20 40 40 55 50 75 70 90 90 يُبين الشكل التالي هذه النقاط على محور العمر Age والأعطال Failures: يُعدّ الانحدار الخطي linear regression من أكثر أنواع الانحدار استخدامًا نظرًا لبساطته في كل من التعلم والتنبؤ، فيُمكن مثلًا أن يتلائم fit الانحدار الخطي مع البيانات السابقة ليولد المستقيم التالي: يُمكن حساب التنبؤ Predictions لكل من قيم الجدول السابق باستخدام معادلة المستقيم الناتجة عن نموذج الانحدار، مما يُعطي: Failures Age Predictions 15 10 26 30 20 32 40 40 44 55 50 50 75 70 62 90 90 74 يُمكن الآن قياس مدى ملائمة المستقيم للبيانات بحساب المسافة بين النقاط الأساسية والمستقيم، وذلك بحساب الأخطاء Errors الحاصلة أي الفروقات بين القيم الأساسية و قيم التنبؤ، كما يُبين الجدول التالي: Errors Predictions Failures Age 11 26 15 10 2 32 30 20 4 44 40 40 -5 50 55 50 -13 62 75 70 -16 74 90 90 يُبين الشكل التالي هذه الفروقات أيضًا: تُستخدم المقاييس التالية لتقييم أداء الانحدار: الجذر التربيعي لمتوسطات مربعات الأخطاء Root Mean Square Error (ويختصر إلى RMSE) متوسط الأخطاء بالقيمة المطلقة Mean Absolute Error (ويختصر إلى MAE) الجذر التربيعي لمتوسطات مربعات الأخطاء RMSE تُستخدم عملية التربيع أولًا للتخلص من الإشارة السالبة للأخطاء: Errors2 Errors Predictions Failutres Age 121 11 26 15 10 4 2 32 30 20 16 4 44 40 40 25 -5 50 55 50 169 -13 62 75 70 256 -16 74 90 90 ومن ثم نحسب متوسط مربعات الأخطاء، مما يُعطي 89.5. وأخيًرا نجذر المتوسط السابق فيكون الناتج 9.9 قيمة المقياس RMSE أي الجذر التربيعي لمتوسطات مربعات الأخطاء Root Mean Square Error. بالطبع، كلما كان هذا العدد صغيرًا فهذا يعني أن الملائمة أفضل (الحالة المثالية هي الصفر). متوسط الأخطاء بالقيمة المطلقة MAE \Errors\ Errors Predictions Failutres Age 11 11 26 15 10 2 2 32 30 20 4 4 44 40 40 5 -5 50 55 50 13 -13 62 75 70 16 -16 74 90 90 ومن ثم نحسب المتوسط الحسابي لها، مما يُعطي: 8.5 في مثالنا وهو مقياس MAE أي متوسط الأخطاء بالقيمة المطلقة Mean Absolute Error. يُمكن حساب هذه المقاييس في بايثون باستخدام الدوال الموافقة من المكتبة sklearn.metrics، كما تُبين الشيفرة البرمجية التالية والتي نستخدم فيها بيانات مثالنا السابق: from sklearn.metrics import mean_absolute_error from sklearn.metrics import mean_squared_error from math import sqrt y_true = [15, 30, 40, 55, 75, 90] y_pred = [26, 32, 44, 50, 62, 74] print ('MAE :{:.2f}'.format(mean_absolute_error(y_true, y_pred))) print ('RMSE :{:.2f}'.format(sqrt(mean_squared_error (y_true, y_pred)))) يكون الناتج: RMSE :9.92 MAE :8.50 تقييم نماذج التعلم باستخدام طريقة التقسيم العشوائي Hold-out method لبناء نموذج مُتعلم، يجب توفير مجموعة من البيانات dataset لتنفيذ خوارزمية التعلم عليها أولًا، ومن ثم تقييم نموذج التعلم الناتج. يجب لتقييم نموذج التعلم، بشكل حيادي، أن نمرر له مجموعة من البيانات التي لم يرها خلال تدريبه وذلك تفاديًا لحصولنا على مقاييس تقييم ممتازة مزيفة نتيجة وقوع نموذج التعلم في مطب مشكلة فرط التخصيص overfitting، أي أن النموذج تعلم بشكل جيد على الأمثلة المُمره له فقط، وهو غير قادر على تعميم التعلم ليتعامل مع أمثلة جديدة بشكل جيد. تُعدّ طريقة التقسيم العشوائي من أبسط الطرق المستخدمة لتقييم نماذج التعلم والتي تُقسّم البيانات المتاحة إلى قسمين بشكل عشوائي: ندعو القسم الأول ببيانات التدريب training data (عادًة حوالي 80% من البيانات)، والقسم الآخر المتبقي (حوالي 20%) ببينات الاختبار testing data. نعمد إلى تدريب وبناء نموذج التعلم باستخدام بيانات التدريب فقط، ومن ثم تقييم النموذج الناتج باستخدام بيانات الاختبار. توفر المكتبة sklearn في بايثون كل ما يلزم للقيام بذلك كما تٌبين الشيفرة البرمجية التالية والتي نبني فيها نموذج تصنيف باستخدام مُصنّف بايز Bayes وذلك على مجموعة بيانات تصنيف أزهار السوسن iris المتاحة من المكتبة نفسها. # مجموعة بيانات # أزهار السوسن from sklearn.datasets import load_iris # مكتبة تقسيم البيانات إلى تدريب واختبار from sklearn.model_selection import train_test_split # مكتبة مُصنف بايز from sklearn.naive_bayes import GaussianNB # مكتبات مقاييس التقييم from sklearn.metrics import accuracy_score from sklearn.metrics import precision_score from sklearn.metrics import recall_score from sklearn.metrics import f1_score # تحميل البيانات X, y = load_iris(return_X_y=True) # تقسيم البيانات إلى تدريب و اختبار X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) # بناء مصنف بايز gnb = GaussianNB() # الملائمة على بيانات التدريب # والتنبؤ على بيانات الاختبار y_pred = gnb.fit(X_train, y_train).predict(X_test) # حساب مقاييس التقييم # بمقارنة بيانات الاختبار # مع تنبؤ المصنف print ('Accuracy Score :{:.2f}'.format(accuracy_score(y_test, y_pred)*100)) print ('Precision Score :{:.2f}'.format(precision_score(y_test, y_pred, average='macro')*100)) print ('Recall Score ::{:.2f}'.format(recall_score(y_test, y_pred, average='macro')*100)) print ('F1 Score ::{:.2f}'.format(f1_score(y_test, y_pred, average='macro')*100)) لاحظ أن الدالة train_test_split والتي نُمرر لها كل من: X: مصفوفة ثنائية يكون كل عنصر فيها مصفوفة من 4 عناصر (طول وعرض كل من السبال والبتلات لزهرة السوسن). y: مصفوفة أحادية (تصنيف الزهرة الموافق) test_size: النسبة المئوية لبيانات الاختبار من البيانات الكليه (20% افتراضيًا) تُعيد: X_train: بيانات التدريب المختارة عشوائيًا من X. X_test: بيانات الاختبار المختارة عشوائيًا من X. y_train: صفوف بيانات التدريب. y_test: صفوف بيانات الاختبار. يُمكن تنفيذ الشيفرة لمعاينة قيم مقاييس الأداء: Accuracy Score :93.33 Precision Score :92.59 Recall Score :93.94 F1 Score :92.50 يجب الانتباه إلى أن هذه القيم هي قيم استرشادية، بمعنى أنها ستختلف عند كل تنفيذ للشيفرة البرمجية إذ أن حسابها يعتمد في كل مرة على مجموعة مختلفة من بيانات الاختبار (إذ أن هذه البيانات تُختار كل مرة بشكل عشوائي) مثلًا يُعطي تنفيذ ثاني لنفس الشيفرة السابقة النتائج التالية: Accuracy Score :90.00 Precision Score :92.31 Recall Score :92.86 F1 Score :91.65 تقييم نماذج التعلم باستخدام طريقة التقييم المتقاطع Cross Validation method تهدف هذه الطريقة بشكل أساسي إلى استخدام كل البيانات للتدريب (بخلاف الطريقة السابقة والتي تستخدم قسمًا منها فقط)، كما أن قيم مقاييس التقييم ستكون نفسها من أجل كل تنفيذ للشيفرة (بخلاف الطريقة السابقة كما عرضنا أعلاه). عوضًا عن تقسيم البيانات إلى بيانات للتدريب وبيانات للاختبار (التقييم) مما يُخفّض من البيانات التي يُمكن لنا استخدامها للتدريب، نستخدم التقويم المتقاطع مع عدد محدّد من الحاويات K-Fold. تُقسم بيانات التدريب إلى عدد K من الحاويات ومن ثم نقوم بتكرار ما يلي K مرة: في كل مرة i نقوم بتدريب النموذج مع بيانات K-1 حاوية (كل الحاويات ما عدا الحاوية i) ومن ثم تقييمه مع بيانات الحاوية i. في النهاية، يكون مقياس الأداء النهائي هو متوسط مقياس التقييم لكل التكرارات (i:1..K). توفر المكتبة sklearn في بايثون الدالة cross_val_score لتنفيذ التقييم المتقاطع مع تحديد عدد الحاويات المطلوب، كما تُبين الشيفرة البرمجية التالية والتي نحسب فيها مقياس الدقة مثلًا: # مجموعة بيانات # أزهار السوسن from sklearn.datasets import load_iris # مكتبة تقسيم البيانات إلى تدريب واختبار from sklearn.model_selection import train_test_split # مكتبة مُصنف بايز from sklearn.naive_bayes import GaussianNB # مكتبة التقييم المتقاطع from sklearn.model_selection import cross_val_score # مكتبات مقاييس التقييم from sklearn.metrics import accuracy_score from sklearn.metrics import precision_score from sklearn.metrics import recall_score from sklearn.metrics import f1_score # تحميل البيانات X, y = load_iris(return_X_y=True) # تقسيم البيانات إلى تدريب و اختبار X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) # بناء مصنف بايز gnb = GaussianNB() # حساب مقياس الصحة scores = cross_val_score(gnb, X, y, cv=5, scoring="accuracy") print(scores) # حساب المتوسط meanScore = scores.mean() print(meanScore * 100) يكون ناتج التنفيذ (في كل مرة): [0.93333333 0.96666667 0.93333333 0.93333333 1. ] 95.33333333333334 اختيار نموذج التعلم الأفضل يُمكن استخدام طريقة التقويم المتقاطع السابقة لتقييم مجموعة من نماذج التعلم. نُعرّف في الشيفرة البرمجية التالية الدالة cv_comparison_classification والتي نُمرر لها مجموعة من نماذج التعلم models والبيانات كلها (X,y) وعدد الحاويات cv، وتُعيد هذه الدالة إطار بيانات يُظهر مقياس الدقة لكل نموذج. تدور حلقة for في الدالة على النماذج المُمرره، وتستدعي من أجل كل نموذج دالة التقويم المتقاطع من المكتبة sklearn.model والتي تُعيد قائمة بقيم مقياس الصحة (كل عنصر في القائمة هو قيمة مقياس الصحة من أجل حاوية اختبار ما). نحسب متوسط mean القائمة السابقة، ونضيف عمودًا جديدًا إلى إطار البيانات يكون اسمه اسم النموذج والقيمة في الخلية الموافقة للسطر الوحيد في إطار البيانات (والمفهرس بـ Accuracy) قيمة مقياس الصحة. # مكتبة أطر البيانات import pandas as pd # مكتبة التقييم المتقاطع from sklearn.model_selection import cross_val_score # دالة لمقارنة مجموعة من النماذج def cv_comparison_classification(models, X, y, cv): # تهيئة إطار بيانات لمقاييس التقييم cv_df = pd.DataFrame() # الدوران على النماذج # تطبيق التقييم المتقاطع for model in models: # حساب مقياس الصحة لكل حاوية acc = cross_val_score(model, X, y,scoring='accuracy', cv=cv) # حساب متوسط الصحة للنموذج acc_avg = round(acc.mean(), 4) # كتابة النتيجة في إطار البيانات cv_df[str(model)] = [ acc_avg] cv_df.index = ['Accuracy'] return cv_df نستدعي في الشيفرة البرمجية التالية الدالة السابقة للمقارنة بين ثلاثة نماذج للتصنيف: مصنف بايز GaussianNB ومصنف أشجار القرار DecisionTreeClassifier ومصنف أقرب الجيران KNeighborsClassifier. نستخدم بيانات تصنيف أزهار السوسن المتاحة من sklearn.datasets لتنفيذ نماذج التعلم عليها. # مجموعة بيانات # أزهار السوسن from sklearn.datasets import load_iris # مصنف بايز from sklearn.naive_bayes import GaussianNB # مصنف شجرة القرار from sklearn.tree import DecisionTreeClassifier # مصنف أقرب الجيران from sklearn.neighbors import KNeighborsClassifier # تحميل البيانات X, y = load_iris (return_X_y=True) # إنشاء متغيرات النماذج mlr_g = GaussianNB() mlr_d = DecisionTreeClassifier() mlr_k = KNeighborsClassifier() # وضع النماذج في قائمة models = [mlr_g, mlr_d, mlr_k] # استدعاء دالة المقارنة comp_df = cv_comparison_classification(models, X, y, 4) # إظهار إطار البيانات للمقارنه print(comp_df) يُظهر تنفيذ الشيفرة السابقة إطار البيانات التالي والذي يسمح لنا بمعاينة مقياس الصحة لكل نموذج مما يسمح لنا باختيار الأنسب منها: الخلاصة عرضنا في هذه المقالة مقاييس تقييم نماذج التعلم المختلفة وكيفية اختيار النموذج الأفضل منها لمسألة معينة. يُمكن تجربة جميع أمثلة المقالة من موقع Google Colab من الرابط أو من الملف المرفق. الملف المرفق eval.zip. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن اقرأ أيضًا المفاهيم الأساسية لتعلم الآلة تعلم الآلة: التحديات الرئيسية وكيفية التوسع في المجال النسخة الكاملة لكتاب مدخل إلى الذكاء الاصطناعي وتعلم الآلة
  9. تُعدّ مسألة استكشاف قواعد الترابط في مبيعات المتاجر من المسائل المهمة جدًا لأصحاب المتاجر الإلكترونية، إذ يسمح إيجاد هذه القواعد بإظهار توصيات recommandations الشراء المناسبة للزبائن مما يساهم في زيادة مبيعات المتجر مثلًا لو عرفنا أن معظم الزبائن تشتري السلعة B مع السلعة A دومًا، فسيكون من المناسب إظهار توصية بشراء السلعة B لكل زبون يطلب شراء السلعة A مما يُحقق، في نهاية المطاف، رضى الزبون وزيادة أرباح المتجر. نعرض في هذه المقالة استخدام تقنيات تعلم الآلة لإيجاد قواعد الترابط انطلاقًا من حركات الشراء السابقة للمتجر. بيانات التدريب نستخدم بيانات عمليات الشراء لأحد متاجر مبيعات التجزئة والمتاحة على الرابط أو يمكنك تنزيلها من الملف data.zip في المقال. تأتي هذه البيانات في الملف Groceries.csv المرفق والذي يحوي حوالي 10000 صف. يحوي كل صف بيانات عربة تسوق واحدة أي مجموعة من السلع التي اشتراها أحد الزبائن معًا كما يُبين الشكل التالي: أساسيات في التنقيب عن قواعد الترابط نعرض فيما يلي بعض التعاريف الأساسية في مسألة التنقيب عن قواعد الترابط. التنقيب عن قواعد الترابط تُعرّف مسألة التنقيب عن قواعد الترابط كما يلي: بفرض أن لدينا مجموعة من الإجراءات transactions، يتألف كل إجراء من مجموعة من العناصر items. يكون المطلوب إيجاد جميع الترابطات correlations بين ظهور مجموعة جزئية من العناصر مع مجموعة جزئية أخرى. يُبين الشكل التالي مثالًا توضيحيًا: نستخدم فيما يلي المثال التالي التعليمي والذي يحوي 5 إجراءات: table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } الإجراء Transaction خبز، حليب Bread,Milk خبز، فوط، عصير، بيض Bread, Diaper, Juice, Eggs حليب، فوط، عصير، كولا Milk, Diaper, Juice, Coke خبز، حليب، فوط، عصير Bread, Milk, Diaper, Juice خبز، حليب، فوط، كولا Bread, Milk, Diaper, Coke نُعطي أولًا أهم التعاريف الأساسية: مجموعة عناصر itemset وهي مجموعة من العناصر مثلًا: { Milk , Bread , Diaper} (فوط أطفال، خبز، حليب) كما ندون k-itemset للدلالة على مجموعة عناصر تحوي k عنصر. عدد الدعم support count لمجموعة من العناصر وهو عدد مرات تواتر (ظهور) مجموعة من العناصر في الإجراءات مثلًا يكون عدد الدعم في مثالنا التعليمي لمجموعة العناصر السابقة: sc({Milk, Bread, Diaper}) = 2 الدعم support لمجموعة من العناصر وهو النسبة المئوية لظهور مجموعة من العناصر في الإجراءات، مثلًا يكون عدد الدعم في مثالنا التعليمي لمجموعة العناصر سابقة الذكر: s({Milk, Bread, Diaper}) = (2/5)*100 = 40% الحد الأدنى للدعم minimum support وهو حد أدنى تجريبي للدعم (يُمكن أن يكون رقم أو نسبة مئوية) نُحدّده لخوارزميات التنقيب عن القواعد. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن مجموعة عناصر متواترة frequent itemset نقول عن مجموعة عناصر أنها متواترة إذا كان دعم المجموعة أكبر أو يساوي الحد الأدنى للدعم. قاعدة ترابط association rule وهي اقتضاء من الشكل: {X} => {Y} وحيث X و Y هي مجموعات من العناصر (لا تحوي، بالطبع، عناصر مشتركة) مثلاً: {Milk, Diaper} => {Bread} الدعم لقاعدة ترابط association rule support يكون الدعم لقاعدة ترابط X=>Y هو الدعم لاجتماع العناصر X و Y معًا. أي نسبة الإجراءات التي تحوي X و Y معًا من عدد الإجراءات الكلي: الثقة في قاعدة ترابط association rule confidence وهو الاحتمال الشرطي لظهور مجموعة العناصر Y في إجراء يحوي X، أي عمليًا احتمال (الثقة) أن تظهر مجموعة العناصر Y في عربة تسوق تحوي العناصر X. مثلًا يكون الدعم لمجموعة العناصر {Milk, Diaper, Bread} في المثال السابق مساويًا 40% لأن هذه العناصر ظهرت مع بعضها البعض مرتين في الإجراءات الخمسة الكلية. يُمكن من هذه العناصر الثلاثة {Milk, Diaper, Bread} توليد مجموعة من القواعد المختلفة (يكفي أن نوزع هذه العناصر على الطرف اليساري واليمني بكل الطرق الممكنة لنحصل على جميع القواعد). يُمكن أن يكون لكل قاعدة معامل ثقة مختلف كما تُبين الأمثلة التالية (المحسوبة من مثالنا التعليمي): {Milk, Diaper} => {Bread} (s=0.4, c=0.67) {Milk, Bread} => {Diaper} (s=0.4, c= 0.67) {Diaper, Bread} => {Milk} (s=0.4, c=0.67) {Bread} => {Milk, Diaper} (s=0.4, c=0.50) {Diaper} => {Milk, Bread} (s=0.4, c=0.5) {Milk} => {Diaper, Bread} (s=0.4, c=0.5) نعرض في هذه المقالة كيفية استخراج قواعد الترابط التي تحقق دعم وثقة معينين. إعداد المشروع يحتاج تنفذ شيفرات هذه المقالة بيئةً برمجيةً للغة بايثون الإصدار 3.8. ويجب أن تتضمن هذه البيئة البرمجية مدير الحِزم pip لتثبيت الحِزم، ومُنشئ البيئات الافتراضية venv لإنشاء بيئاتٍ افتراضيةٍ. نستخدم محرر الشيفرات البرمجية Jupyter Notebooks، وهو مفيد جدًا لتجربة وتشغيل الأمثلة الخاصة بتَعَلّم الآلة بطريقةٍ تفاعليةٍ، حيث نستطيع من خلاله تشغيل كتلًا صغيرةً من الشيفرات البرمجية ورؤية النتائج بسرعة، مما يُسهّل علينا اختبار الشيفرات البرمجية وتصحيحها. نحتاج أولًا لتثبيت بعض التبعيات، وذلك لإنشاء مساحة عملٍ للاحتفاظ بملفاتنا قبل أن نتمكن من تطوير برنامجنا. نُنشئ مجلدًا جديدًا خاصًا بمشروعنا وندخل إليه هكذا: mkdir asoc cd asoc نُنفذّ الأمر التالي لإنشاء البيئة الافتراضية: python -m venv asoc ومن ثم الأمر التالي في Linux لتنشيط البيئة الافتراضية: source asoc/bin/activate أما في Windows، فيكون أمر التنشيط: "asoc/Scripts/activate.bat" نستخدم إصداراتٍ مُحددةٍ من المكتبات اللازمة، من خلال إنشاء ملف requirements.txt في مجلد المشروع، وسيُحدِّد هذا الملف المتطلبات والإصدارات التي سنحتاج إليها. نفتح الملف requirements.txt في محرر النصوص، ونُضيف الأسطر التالية، وذلك لتحديد المكتبات التي نريدها وإصداراتها: asttokens==2.0.5 backcall==0.2.0 colorama==0.4.4 cycler==0.11.0 debugpy==1.6.0 decorator==5.1.1 entrypoints==0.4 executing==0.8.3 fonttools==4.33.3 ipykernel==6.13.0 ipython==8.4.0 jedi==0.18.1 joblib==1.1.0 jupyter-client==7.3.1 jupyter-core==4.10.0 kiwisolver==1.4.2 matplotlib==3.5.2 matplotlib-inline==0.1.3 mlxtend==0.20.0 nest-asyncio==1.5.5 numpy==1.22.4 packaging==21.3 pandas==1.4.2 parso==0.8.3 pickleshare==0.7.5 Pillow==9.1.1 prompt-toolkit==3.0.29 psutil==5.9.1 pure-eval==0.2.2 Pygments==2.12.0 pyparsing==3.0.9 python-dateutil==2.8.2 pytz==2022.1 pywin32==304 pyzmq==23.0.0 scikit-learn==1.1.1 scipy==1.8.1 seaborn==0.11.2 six==1.16.0 stack-data==0.2.0 threadpoolctl==3.1.0 tornado==6.1 traitlets==5.2.2.post1 wcwidth==0.2.5 نحفظ التغييرات التي طرأت على الملف ونخرج من محرر النصوص، ثم نُثَبت هذه المكتبات بالأمر التالي: (asoc) $ pip install -r requirements.txt بعد تثبيتنا لهذه التبعيات، نُصبح جاهزين لبدء العمل على مشروعنا. كتابة الشيفرة البرمجية نُشغّل محرر الشيفرات البرمجية Jupyter Notebook بمجرد اكتمال عملية التثبيت هكذا: (asoc) $ jupyter notebook ثم نُنشئ ملفًا جديدًا في داخل المحرر ونُسمّه باسم asc مثلًا. توليد قواعد الترابط نعرض في الشيفرة التالية توليد قواعد الترابط للمثال السابق بهدف التعرف على المكتبات اللازمة وآلية استخدامها. نضع إجراءات المثال السابق أولًا في مصفوفة ثنائية وبحيث يكون كل عنصر منها هو مصفوفة من عناصر إجراء واحد. نحتاج أولًا إلى توليد إطار بيانات dataframe وبحيث تكون رؤوس الأعمدة هي العناصر وقيم الخلايا هي إما True في حال وجود العنصر في صف row الإجراء الموافق أو False في حال عدم وجوده. نستخدم الصنف TransactionEncoder من المكتبة mlxtend.preprocessing للوصول إلى ذلك. # الإجراءات transactions = [['Bread', 'Milk'], ['Bread', 'Diaper', 'Juice', 'Eggs'], ['Milk', 'Diaper', 'Juice', 'Coke' ], ['Bread', 'Milk', 'Diaper', 'Juice'], ['Bread', 'Milk', 'Diaper', 'Coke']] # مكتبة ترميز الإجراءات from mlxtend.preprocessing import TransactionEncoder # إنشاء غرض من الصف te = TransactionEncoder() # ملائمة المرمز مع البيانات te_model = te.fit(transactions) # تحويل الإجراءات rows=te_model.transform(transactions) # استيراد مكتبة إطار البيانات import pandas as pd # بناء إطار بيانات الإجراءات df = pd.DataFrame(rows, columns=te_model.columns_) print(df) وبالنتيجة يكون لدينا إطار بيانات الإجراءات التالي: لاحظ مثلًا أن الصف الأول من إطار البيانات يوافق الإجراء الأول: {Bread, Milk}. نستدعي في الشيفرة التالية الدالة apriori من المكتبة mlxtend.frequent_patterns والتي تحسب العناصر المتواترة في إطار البيانات السابق (df) وفق حد أدنى معين للدعم min_support (يساوي 40% في مثالنا). يكون ناتج تطبيق هذه الدالة إطار بيانات frequent_itemsets ذي عمودين: مجموعة العناصر المتواترة itemsets والدعم support. نُضيف عمود محسوب جديد لإطار البيانات الناتج يحسب طول كل مجموعة عناصر length. # مكتبة خوارزمية إيجاد العناصر المتواترة from mlxtend.frequent_patterns import apriori # توليد المجموعات المتواترة مع تحديد الحد الأدنى للدعم frequent_itemsets = apriori(df, min_support=0.4, use_colnames=True) # حساب أطوال مجموعات العناصر frequent_itemsets['length'] = frequent_itemsets['itemsets'].apply(lambda x: len(x)) print(frequent_itemsets) تُظهر الطباعة دعم وطول كل مجموعة من مجموعات العناصر المتواترة: يُمكن الآن توليد قواعد الترابط باستخدام إطار بيانات العناصر المتواترة frequent_itemsets السابق، كما تُبين الشيفرة التالية. نستخدم الدالة association_rules من المكتبة mlxtend.frequent_patterns مع تحديد الحد الأدنى للثقة min_threshold (لنأخذ القيمة 60% في مثالنا). # مكتبة خوارزمية إيجاد قواعد الترابط from mlxtend.frequent_patterns import association_rules # توليد القواعد مع تحديد الحد الأدنى للثقة rules = association_rules(frequent_itemsets,metric="confidence",min_threshold=0.6) # الترتيب التنازلي وفق معامل الثقة rules = rules.sort_values(['confidence'], ascending =[False]) print(rules) يُبين الشكل التالي قواعد الترابط الناتجة: يُمكن استخدام رسم الإحداثيات المتوازية parallel_coordinates من المكتبة pandas.plotting لرسم مشاهدة توضيحية للقواعد السابقة. from matplotlib import pyplot as plt from pandas.plotting import parallel_coordinates # دالة تحويل القواعد إلى إحداثيات def rules_to_coordinates(rules): rules['antecedent'] = rules['antecedents'].apply(lambda antecedent: list(antecedent)[0]) rules['consequent'] = rules['consequents'].apply(lambda consequent: list(consequent)[0]) rules['rule'] = rules.index return rules[['antecedent','consequent','rule']] # توليد الإحداثيات المتوازية coords = rules_to_coordinates(rules) # توليد رسم الإحداثيات المتوازية plt.figure(figsize=(4,8)) parallel_coordinates(coords, 'rule') plt.grid(True) plt.show() يكون الرسم البياني الناتج: يُبين الرسم ارتباطات العناصر، وذلك برسم خط بين العنصر من الجهة اليسرى وبين نهاية الخط الأفقي للعنصر الآخر المرتبط معه من الجهة اليمنى، مثلًا يرتبط الحليب Milk مع كل من الخبز Bread و الفوط Diaper. تحميل بيانات المتجر نبدأ أولًا بتحميل بيانات المتجر من الملف Groceries.csv ضمن إطار من البيانات DataFrame من مكتبة Pandas ومن ثم عرض بعضها: # تحميل بيانات المتجر df = pd.read_csv('Groceries.csv',header=None) df.head() يظهر لنا أوائل صفوف الملف: نلاحظ وجود قيم كثيرة فارغة NaN وذلك لأن عدد العناصر في كل صف غير متساوي. نحذف في الشفرة التالية القيم الفارغة، ثم نُنشئ مصفوفة الإجراءات والتي هي مصفوفة يكون كل عنصر منها مصفوفة من عناصر صف واحد من إطار البيانات: # حذف القيم الفارغة # وإنشاء مصفوفة transactions = df.T.apply(lambda x: x.dropna().tolist()).tolist() print(transactions[1:10])) يُبين الشكل التالي مثلًا العناصر العشرة الأولى من مصفوفة الإجراءات الناتجة: نستخدم في الشيفرة التالية مُرمز الإجراءات للحصول على إطار بيانات الإجراءات: # إنشاء غرض من الصف te = TransactionEncoder() # ملائمة المرمز مع البيانات te_model = te.fit(transactions) # تحويل الإجراءات rows=te_model.transform(transactions) # بناء إطار بيانات الإجراءات df = pd.DataFrame(rows, columns=te_model.columns_) print(df.shape) مما يُعطي: (9835, 169) لاحظ أن عدد أعمدة إطار البيانات الناتج هو 169 عمودًا مما يعني وجود 169 عنصرًا مختلفًا فقط في الإجراءات البالغ عددها 9835 إجراء. نستدعي في الشيفرة التالية الدالة apriori والتي تحسب العناصر المتواترة في إطار البيانات df وفق حد أدنى معين للدعم min_support يساوي 0.5% (هو رقم تجريبي حصلنا عليه بتكرار توليد العناصر المتواترة وقواعد الترابط حتى الوصول لقواعد ترابط عددها محدود نسبيًا). يكون ناتج تطبيق هذه الدالة إطار بيانات frequent_itemsets ذي عمودين: مجموعة العناصر المتواترة itemsets والدعم support. نُضيف عمود محسوب جديد لإطار البيانات الناتج يحسب طول length كل مجموعة عناصر. # توليد المجموعات المتواترة مع تحديد الحد الأدنى للدعم frequent_itemsets = apriori(df, min_support=0.005, use_colnames=True) # حساب أطوال مجموعات العناصر frequent_itemsets['length'] = frequent_itemsets['itemsets'].apply(lambda x: len(x)) print(frequent_itemsets) تُبين النتائج أن لدينا 1001 مجموعة من العناصر المتواترة المُحققة للحد الأدنى للدعم ويتراوح طولها بين 1 و 4: يُمكن الآن توليد قواعد الترابط باستخدام إطار بيانات العناصر المتواترة السابق كما تُبين الشيفرة التالية. نستخدم الدالة association_rules مع تحديد الحد الأدنى للثقة 55% (رقم تجريبي حصلنا عليه بعد عدة محاولات لتوليد قواعد الترابط حتى وصلنا لمجموعة معقولة من القواعد). # توليد القواعد مع تحديد الحد الأدنى للثقة rules = association_rules(frequent_itemsets,metric="confidence",min_threshold=0.55) # الترتيب التنازلي وفق معامل الثقة rules = rules.sort_values(['confidence'], ascending =[False]) print(rules) يُبين الشكل التالي قواعد الترابط الناتجة (حوالي 50 قاعدة): يُمكن استخدام رسم الإحداثيات المتوازية لرسم مشاهدة توضيحية للقواعد السابقة: from pandas.plotting import parallel_coordinates # دالة تحويل القواعد إلى إحداثيات def rules_to_coordinates(rules): rules['antecedent'] = rules['antecedents'].apply(lambda antecedent: list(antecedent)[0]) rules['consequent'] = rules['consequents'].apply(lambda consequent: list(consequent)[0]) rules['rule'] = rules.index return rules[['antecedent','consequent','rule']] # توليد الإحداثيات المتوازية coords = rules_to_coordinates(rules) # توليد رسم الإحداثيات المتوازية plt.figure(figsize=(4,8)) parallel_coordinates(coords, 'rule') plt.legend([]) plt.grid(True) plt.show() يكون الرسم البياني الناتج: الخلاصة عرضنا في هذه المقالة خطوات بناء إيجاد قواعد الترابط بين مبيعات العناصر في المتاجر. يُمكن تجربة المثال كاملًا من موقع Google Colab من الرابط أو تنزيله من الملف المرفق. الملف المرفق: data (1).zip اقرأ أيضًا المفاهيم الأساسية لتعلم الآلة تعلم الآلة: التحديات الرئيسية وكيفية التوسع في المجال خطوات تنفيذ مشروع عن تعلم الآلة في بايثون: الجزء الأول النسخة الكاملة لكتاب مدخل إلى الذكاء الاصطناعي وتعلم الآلة
×
×
  • أضف...