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

Bassel Alkhatib

الأعضاء
  • المساهمات

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

  • تاريخ آخر زيارة

كل منشورات العضو Bassel Alkhatib

  1. يُحدّد ملف بيان تطبيق الويب web app manifest مظهر وسلوك صفحات الويب التقدميّة PWA كما يجب أن يعرضها المتصفح بعد تثبيت التطبيق على سطح مكتب أو جوّال المستخدم، وهو عبارة عن ملف مكتوب بصيغة جيسون JSON. تدّعم معظم المتصفحات ملف بيان تطبيق الويب مثل Chrome و Edge و Firefox و UC Browser و Opera و Samsung أما المتصفح Safari فيوفر دعمًا جزئيًا فقط. إنشاء ملف بيان تطبيق الويب يُمكن تسمية ملف بيان الويب بأي اسم، إلا أن الشائع تسميته بـ manifest.json ووضعه في الجذر (المجلّد الأعلى لموقع الويب). تنص التوصيات القياسية على وجوب استخدام اللاحقة webmanifest لاسم الملف، إلا أن اللاحقة json يُمكن أن تُستخدم أيضًا لاسيما أنها مألوفة لجميع المطورين. نٌبين فيما يلي مثالًا عن محتوى بيان الويب: { "short_name": "Weather", "name": "Weather: Do I need an umbrella?", "icons": [ { "src": "/images/icons-192.png", "type": "image/png", "sizes": "192x192" }, { "src": "/images/icons-512.png", "type": "image/png", "sizes": "512x512" } ], "start_url": "/?source=pwa", "background_color": "#3367D6", "display": "standalone", "scope": "/", "theme_color": "#3367D6", "shortcuts": [ { "name": "How's weather today?", "short_name": "Today", "description": "View weather information for today", "url": "/today?source=pwa", "icons": [{ "src": "/images/today.png", "sizes": "192x192" }] }, { "name": "How's weather tomorrow?", "short_name": "Tomorrow", "description": "View weather information for tomorrow", "url": "/tomorrow?source=pwa", "icons": [{ "src": "/images/tomorrow.png", "sizes": "192x192" }] } ], "description": "Weather forecast information", "screenshots": [ { "src": "/images/screenshot1.png", "type": "image/png", "sizes": "540x720" }, { "src": "/images/screenshot2.jpg", "type": "image/jpg", "sizes": "540x720" } ] } أهم خصائص ملف بيان الويب سنشرح أهم الخصائص التي يحويها ملف بيان الويب والتي عليك ضبطها لتطبيقك. الاسم القصير short_name و/أو الاسم name يجب تضمين، على الأقل، إحدى الخاصيتين: الاسم القصير short_name أو الاسم name. وفي حال توفر كلتيهما يُستخدم الاسم القصير في شاشة المستخدم الرئيسية home screen وفي مُشغّل التطبيق launcher وفي أي مكان لا تتوفر فيه المساحة الكافية لعرض الاسم كاملًا؛ أما الاسم فيُستخدم عند تثبيت التطبيق. الأيقونات icons يُمكن تحديد مجموعة من الأيقونات ليعرضها المتصفح على الشاشة الرئيسية home screen وعلى مُشغّل التطبيق launcher وعلى مُبدّل المهام task switcher وعلى شاشة البداية splash screen وغيرها، بعد أن يقوم المستخدم بتثبيت صفحات الويب التقدميّة. تكون قيمة خاصية الأيقونات icons مصفوفة من العناصر، يحوي كل عنصر منها خاصية المصدر src وخاصية القياسات sizes وخاصية نوع الصورة type. يجب إضافة "purpose": "any maskable" إلى خاصية الأيقونة icon في حال الحاجة لاستخدام الأيقونات المقنّعة maskable icons والتي تُدعى أيضًا بالأيقونات المتكيفة في أندرويد. يتطلب استخدام المتصفح توفير أيقونات بقياس 192x192 بكسل وبقياس 512×512 بكسل على الأقل. إذا توفر هذان القياسان فقط سيقوم المتصفح Chrome بتحجيم الأيقونات لتتناسب مع الجهاز المستخدم. في حال أردت الوصول لكمال الإظهار pixel-perfection فعليك توفير أيقونات بقياسات من مضاعفات 48dp. عنوان بدء التطبيق start_url توجه الخاصية المطلوبة start_url المتصفح إلى عنوان أو رابط الصفحة التي يجب أن يبدأ منها التطبيق عند تشغيله، وتمنع التطبيق من البدء من الصفحة التي يتصفحها المستخدم عند إضافة التطبيق لشاشته الرئيسية. توجه الخاصية start_url المستخدم مباشرًة إلى التطبيق عوضًا عن صفحة الهبوط landing page لمنتج ما. يجب دومًا التفكير بما يريد المستخدم أن يقوم به حال فتح التطبيق ومن ثم وضعه في المكان المناسب. لون الخلفية backgroud_color تُستخدم خاصية لون الخلفية لشاشة البداية splash screen عند تشغيل التطبيق لأول مرة على الجوّال. العرض display يُمكن تخصيص واجهات المستخدم عند تشغيل التطبيق على المتصفح مثل إخفاء شريط العنوان للمتصفح Chrome، كما يُمكن أيضًا التشغيل بوضعية ملء الشاشة لحالة الألعاب مثلًا. يُبين الجدول التالي بعض قيم هذه الخاصية: ملء الشاشة fullscreen فتح تطبيق الويب دون أي واجهة متصفح متاحة للمستخدم وشغل مساحة العرض المتاحة بالكامل. مستقل standalone فتح تطبيق الويب ليبدو وكأنه تطبيق مستقل. يعمل التطبيق في نافذته الخاصة المنفصلة عن المتصفح والذي تُخفى عناصره القياسية مثل شريط العناوين. الحد الأدنى من الواجهة minimal-ui يشبه هذا الوضع الوضع المستقل السابق إلا أنه يوفر للمستخدم مجموعة صغيرة من عناصر التنقل (مثل الرجوع back وإعادة التحميل reload). متصفح browser يتيح هذا الوضع تجربة استخدام متصفح تمامًا. تجاوز العرض display_override تُحدّد خاصية العرض display في ملف بيان تطبيق الويب وضع العرض كما هو موضح أعلاه. ليس مطلوبًا من جميع المتصفحات أن توفر كل أوضاع العرض السابقة إلا أن عليها أن تدعم السلسلة الاحتياطية وفق المواصفات المحدّدة spec-defined fallback chain أي التسلسل: ("ملء الشاشة" fullscreen← "مستقل" standalone← "الحد الأدنى من الواجهة" minimal-ui← "المتصفح "browser) والتي تُحتّم على المتصفح في حال عدم إمكانية دعمه لوضع معين أن يوفر الوضع التالي له في السلسلة. يُمكن أن يؤدي هذا السلوك غير المرن لبعض المشاكل في حالات نادرة جدًا كعدم استطاعة المطور أن يطلب وضع الحد الأدنى minimal-ui دون العودة بشكل قسري إلى وضع المتصفح browser وذلك إذا كان وضع الحد الأدنى غير مدعوم أصلًا. يُمكن أن يؤدي هذا السلوك أيضًا إلى مشكلة عدم القدرة على تضمين أوضاع عرض جديدة متوافقة مع الإصدارات السابقة، مثلًا: لا يُمكن التوافق مع وضع التبويب tabbed application mode غير الموجود في السلسلة الاحتياطية. تحل خاصية "تجاوز العرض" display_override جميع هذه المشاكل حيث يعتمد المتصفح هذه الخاصية قبل خاصية العرض display. توضع قيمة هذه الخاصية على شكل تسلسل مرتب من السلاسل النصية وبحيث يُطبّق أول عرض منها مدعوم من قبل المتصفح، وفي حال عدم وجود أي عرض مدعوم يعود المتصفح إلى خاصية العرض display. تكون السلسلة الاحتياطية في المثال التالي كما يلي (مع ملاحظة أن تفاصيل الوضع window-control-overlay خارج إطار هذه المقالة): وضع تراكب عناصر النافذة window-control-overlay (أولًا ننظر إلى الخاصية display_override). وضع الحد الأدنى من الواجهة minimal-ui. الوضع المستقل standalone (إذا استُنفذت كل الأوضاع في display_override نعود إلى الخاصية display). وضع الحد الأدنى من الواجهة minimal-ui (نعود في النهاية للسلسلة الاحتياطية للخاصية display). وضع المتصفح browser. { "display_override": ["window-control-overlay", "minimal-ui"], "display": "standalone", } لا يقوم المتصفح باستخدام الخاصية display_override ما لم تكن الخاصية display موجودة. النطاق scope يُحدّد النطاق scope عناوين URLs الصفحات التي يعتبرها المتصفح واقعة ضمن التطبيق وتُستخدم لمعرفة وقت مغادرة المستخدم لهذا التطبيق. يتحكم النطاق ببنية هذه المحدِّدات والتي تتضمن جميع نقاط الدخول والخروج للتطبيق. بالطبع، يجب أن يكون رابط URL للبداية start_url ضمن محدِّدات النطاق هذه. تحذير: في حال نقر المستخدم لأحد روابط التطبيق الذي يُخرجه من النطاق، فسيُفتح الرابط للعرض ضمن نافذة صفحات الويب التقدميّة الحالية. إذا كان المطلوب فتح الرابط ضمن تبويب جديد للمتصفح فيجب إضافة "target="_blank لوسم الرابط <a>. تُفتح مثل هذه الروابط في أندرويد بتبويب مخصص للمتصفح Chrome Custom Tab. يجب أخذ الملاحظات التالية حول النطاق scope بعين الاعتبار: في حال عدم تحديد النطاق scope في ملف بيان الويب، فسيكون النطاق الافتراضي هو المجلد الذي يحوي ملف بيان الويب. يُمكن أن تُحدّد قيمة النطاق مسارًا نسبيًا (/..) أو أي مسار لمستوى أعلى (/) مما يسمح بتغطية أكبر للتنقلات في التطبيق. يجب أن يكون رابط URL للبداية start_url ضمن محدِّدات النطاق scope. يكون رابط URL للبداية start_url نسبي للمسار المُعرّف في النطاق scope. إذا ابتدأ start_url بالمحرف / فسيكون دومًا الجذر للبداية. لون النمط theme_color يُحدّد لون النمط theme_color لون شريط الأدوات، وقد يُستخدم في عرض التطبيق ضمن محوّل المهام task switchers. يجب أن يُطابق لون النمط اللون المحدّد في السمة meta في ترويسة الصفحة. بدءًا من نسخ المتصفحات Chromium 93 و Safari 15، يُمكن ضبط لون النمط باستخدام استعلام وسائط media query للخاصية media في السمة meta وباعتماد أول لون مطابق. يُمكن مثلًا تحديد لون ما للوضع الساطع وآخر للوضع الداكن. لايُمكن، حتى وقت كتابة هذه المقالة، تحديد هذا السلوك في ملف بيان الويب (يُمكنك العودة لمناقشة هذه المشكلة في w3c/manifest#975 GitHub issue) <meta name="theme-color" media="(prefers-color-scheme: light)" content="white"> <meta name="theme-color" media="(prefers-color-scheme: dark)" content="black"> الاختصارات shortcuts تسمح هذه الخاصية بتعريف مجموعة من الاختصارات للمهام الأساسية للتطبيق وهي عبارة عن مصفوفة يكون كل عنصر فيها من النوع "كائن اختصار" app shortcut. يُعرّف الكائن باستخدام قاموس من الأزواج مفتاح/قيمة ويجب أن يحوي على الأقل المفتاح name والمفتاح url. الوصف description تشرح هذه الخاصية الهدف من التطبيق. اللقطات screenshots تكون قيمة هذه الخاصية مصفوفة من كائنات الصور لبعض لقطات سيناريوهات الاستخدام الشائعة للتطبيق. يجب أن يتضمن كل كائن خاصية المصدر src والقياسات sizes و النوع type. يجب أن تحترم الصورة المعايير التالية في المتصفح Chrome: لا يقل قياس كل من العرض والارتفاع عن 320 بكسل ولا يزيد عن 3840 بكسل. لا يزيد البعد الأكبر عن 2.3 ضعف البعد الأقل. لجميع اللقطات نفس نسبة الطول إلى العرض aspect ratio. تنسيقات الصور JPEG أو PNG فقط. إضافة ملف بيان الويب إلى صفحات التطبيق يُمكن، بعد الانتهاء من إنشاء ملف بيان الويب، إضافة الوسم <link> لجميع صفحات الويب التقدميّة للتطبيق. مثلًا: <link rel="manifest" href="/manifest.json"> اختبار ملف البيان يُمكن استخدام جزء البيان Manifest في لوحة التطبيق Application من أدوات التطوير DevTools في المتصفح Chrome للتحقق من إعداد ملف البيان بشكل صحيح. يسمح جزء البيان Manifest بمعاينة معظم خصائص ملف البيان بشكل مرئي، ويُسهّل التحقق من تحميل جميع الصور بشكل صحيح. شاشة البدء على الجوال قد يستغرق تشغيل التطبيق، لا سيما التشغيل الأول، بعض الوقت. يعرض المتصفح شاشة البداية Splash screen حتى ظهور الواجهة الأولى للتطبيق منعًا من إظهار شاشة بيضاء قد توحي للمستخدم بوجود مشكلة ما في التطبيق. يقوم المتصفح بإنشاء شاشة البداية آليًا معتمدًا على خصائص ملف البيان ولا سيما: الاسم name. لون الخلفية background_color. الأيقونات icons. يجب أن يكون لون الخلفية background_color نفس لون صفحة التحميل لتوفير انتقال سلس من شاشة البداية إلى واجهة التطبيق الأولى. يختار المتصفح Chrome الأيقونة الأنسب لدقة الجهاز. يُمكن أن يكون توفير أيقونات بقياس 193 بكسل و512 بكسل كافيًا في معظم الحالات، إلا أن توفير أيقونات إضافية بقياسات مختلفة يوصل لكمال الإظهار بشكل عام. ترجمة -وبتصرف- للمقال Add a web app manifest للمؤلفين: Pete LePage و François Beaufort و Thomas Steiner. اقرأ أيضًا مدخل إلى تطبيقات الويب التقدمية PWA ما هي تطبيقات الويب التقدمية ولماذا يجب على المصممين الاهتمام بها؟ صيغة JSON وتوابعها في جافاسكربت صيغة JSON وXML في PHP
  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. قد يبدو الحكم على سهولة الوصول accessibility لتطبيق أو لموقع ويب وشموليته لكل المستخدمين بأنه مهمة صعبة. ولربما يتساءل البعض عن نقطة البداية لهذه المهمة، لا سيما هؤلاء الذين يتعاملون مع عملية التحقق من سهولة الوصول للمرة الأولى. بما أن العملية تعني التحقق من مجموعة متنوعة من الإمكانات فهذا يعني وجوب مراعاة مجموعة من المسائل المختلفة. نعرض في هذه المقالة آلية مقترحة تفصل هذه المسائل وتعمل عليها بشكل منطقي خطوة بخطوة للتحقق من سهولة الوصول لموقع ويب. البدء مع لوحة المفاتيح يُعدّ التنقل في الصفحة باستخدام لوحة المفاتيح فقط الوسيلة الأساسية للوصول لكل شيء على الشاشة من قبل المستخدمين الذين لا يُمكن لهم استعمال الفأرة (أو الذين يُحبذون عدم استعمالها). توجد شريحة واسعة من هذا النوع من المستخدمين مثل الأشخاص الذين يعانون من إعاقات حركية أو من الشلل أو المصابون بالإجهاد المتكرر (الناتج أحيانًا عن الاستخدام الطويل للفأرة)، إضافًة إلى مستخدمي قارئ الشاشة (ضعاف البصر مثلًا). يُعدّ توفير ترتيب تنقل منطقي عبر مفتاح تاب tab ومراعاة التركيز على العناصر بدقة لتوضيح مكان المؤشر الحالي أحد أهم أساسيات تحقيق سهولة الوصول باستخدام لوحة المفاتيح. النقاط الأساسية يجب أن تراعى النقاط الأساسية التالية: يُمكن البدء بالتنقل في الموقع باستخدام مفتاح الجدولة tab للتأكد من توفير ترتيب الجدولة المنطقي. يجب أن يكون ترتيب الجدولة موافقًا لترتيب العناصر في شجرة DOM. يُمكن العودة للمقالة التركيز على عناصر صفحة الويب لمراجعة القاعدة الأساسية في التركيز والتي تنص على أن أي عنصر تحكم يتفاعل المستخدم معه يجب التمكن من وضع التركيز عليه وإظهار مؤشر مرئي واضح للمستخدم عندها (مثلًا: إظهار حلقة تركيز focus ring). من الممارسات الشائعة تعطيل تنسيق الحدود الظاهرة على حواف العنصر عبر ضبط outline: none دون توفير أي تنسيقات بدلية لحدود حواف العنصر في CSS ولا تعد هذه من الممارسات الجيدة.anti-pattern. لن يتمكن مستخدم لوحة المفاتيح من التفاعل مع الصفحة إذا كان لا يستطيع رؤية موضع التركيز. يُمكن استخدام مكتبة برمجية مثل المكتبة what-input في حال الرغبة بالتمييز بين التركيز باستخدام الفأرة أو باستخدام لوحة المفاتيح. يجب إتاحة التركيز على العناصر المخصصة التفاعلية. مثلًا: في حال استخدام سكريبت جافا لتحويل مقطع إلى قائمة منسدلة جميلة فلن تكون قابلة للتركيز آليًا. يجب إسناد القيمة 0 لخاصية ترتيب الجدولة tabindex=0 لإتاحة التركيز على العنصر. يجب تجنب وضع قيم أكبر من الصفر للخاصية tabindex لأن ذلك سيجعلها في مقدمة ترتيب الجدولة وبغض النظر عن مكانها في شجرة DOM، مما يضع مستخدمي قارئ الشاشة الذين يميلون للتنقل بشكل متسلسل في حيرة والتباس. يجب تجنب وضع التركيز على العناصر التي لا يتفاعل المستخدم معها كالترويسات headings مثلًا. يلجأ بعض المطورين إلى إدراج خاصية ترتيب الجدولة tabindex في الترويسات ظنًا منهم أنها مهمة. يُعدّ هذا السلوك سلوكًا غير شائعًا مما يُقلل من كفاءة الاستخدام عمومًا. أما بالنسبة لمستخدمي قارئ الشاشة يكون التركيز على الترويسات غير ضروري لأن قارئ الشاشة يقرؤها. يجب التأكد من توجيه تركيز المستخدم إلى المحتوى الجديد المُضاف للصفحة كي يتمكن المستخدم من التفاعل معه. يُمكن مراجعة فقرة إدارة التركيز على مستوى الصفحة من المقالة المشار إليها سلفًا، والمعنونة بالتركيز على عناصر صفحة الويب لمزيد من الأمثلة. يجب تجنب قفل التركيز في أي نقطة. قد تؤدي عناصر الإكمال التلقائي autocomplete widgets لقفل التركيز باستخدام لوحة المفاتيح. يُمكن قفل التركيز في بعض الحالات المُحدّدة مثل إظهار نافذة شرطية modal والرغبة بمنع المستخدم من التعامل مع بقية عناصر الصفحة مع الاحتفاظ بإمكانية إغلاق هذه النافذة الشرطية باستخدام لوحة المفاتيح. إمكانية التركيز لا تعني إمكانية الاستخدام يجب على المطور عند إنشاء عنصر تحكم مُخصّص التأكد من وصول مستخدم لوحة المفاتيح لجميع وظائف هذا العنصر. المحتوى خارج الشاشة تحوي العديد من المواقع على محتوى مخفي موجود في شجرة DOM، كحالة الروابط الموجودة داخل قائمة تظهر استجابًة لفعل مستخدم ما responsive drawer menu أو زر داخل نافذة شرطية لم يأتِ بعد وجوب عرضه. يؤدي ترك هذه العناصر في شجرة DOM إلى إرباك مستخدم لوحة المفاتيح لا سيما أن قارئ الشاشة سيقرأ هذه العناصر كما لو أنها جزءًا من الصفحة. يُمكن مراجعة فقرة أهمية ترتيب العناصر في شجرة DOM من مقالة التركيز على عناصر صفحة الويب، للحصول على إرشادات التعامل مع هذه العناصر. اختبار الصفحة مع قارئ الشاشة يؤدي تحسين دعم لوحة المفاتيح إلى وضع أساسيات الخطوة التالية وهي التحقق من احتواء الصفحة على العناوين labels والدلالات المناسبة وعدم وجود أي عوائق أمام تنقل قارئ الشاشة ضمن الصفحة. النقاط الأساسية يجب أن تراعى النقاط الأساسية التالية: يجب التحقق من أن لجميع الصور الخاصية alt لتوفير النص البديل لها عند الحاجة. يُمكن استثناء الصور التي توضع بهدف العرض فقط ولا تكون جزءًا أساسيًا من المحتوى. يجب إسناد السلسلة الفارغة ""=alt لإعلام قارئ الشاشة بوجوب تخطي صورة. يجب التحقق من توفير عناوين labels لجميع عناصر التحكم. يُمكن أن يتطلب ذلك استخدام الخاصيتين aria-label و aria-labelledby للعناصر المخصصة. للمزيد من التفاصيل يُمكن العودة إلى العناوين والعلاقات في ARIA. يجب التحقق من أن لجميع عناصر التحكم الدور role المناسب وخصائص ARIA المطلوبة لضبط حالتها. مثلًا: يحتاج مربع الاختيار المخصص إلى الدور "role="checkbox وإلى الخاصية "aria-checked="true|false للإعلام عن حالة العنصر بشكل صحيح. يجب التحقق من التسلسل المنطقي للانتقال بين عناصر الصفحة. بما أن برامج قراءة الشاشة تنتقل في الصفحة وفق ترتيب شجرة DOM فستؤدي عملية استخدام أنماط CSS لتغيير أماكن توضع العناصر إلى الإعلان عن هذه العناصر وفق تسلسل غير منطقي. يجب على المطور وضع العناصر في شجرة DOM وفق ترتيب الإظهار الذي يرغبه. يجب عدم السماح بإخفاء أي قسم من أقسام الموقع أو حظر وصول قارئ الشاشة إليه بشكل دائم إذا كان دعم تنقل قارئ الشاشة في كل محتوى الصفحة مطلوبًا. يجب التأكد من إسناد القيمة true إلى الخاصية aria-hidden إذا كان المطلوب إخفاء محتوى ما عن قارئ الشاشة كأن يكون خارج الشاشة أو عبارة عن شيئٍ للعرض فقط. التعرف على قارئ شاشة واحد يُساعد في فهم المسائل والمشاكل قد يبدو تعلم استخدام قارئ الشاشة للوهلة الأولى أمرًا صعبًا، إلا أنه عملية سهلة في الواقع إذ يُمكن للمطورين التعامل معه باستخدام بضعة أوامر من لوحة المفاتيح. يُمكن استخدام قارئ الشاشة VoiceOver الذي يتوفر في أنظمة Mac OS.كما يُمكن لمستخدمي النظام Windows مشاهدة هذا الفيديو عن قارئ الشاشة NVDA والذي هو برنامج مفتوح المصدر مدعوم بالتبرعات. لا تمنع الخاصية aria-hidden التركيز باستخدام لوحة المفاتيح يجب أن نتذكر أن خصائص ARIA تؤثر على دلالات العناصر فقط وليس على سلوكها. يُمكن إخفاء عنصر عن قارئ الشاشة باستخدام hidden=true إلا أن هذا لن يغير من سلوك التركيز على العنصر. أما بالنسبة للمحتوى التفاعلي الموجود خارج الصفحة فنحتاج غالبًا للجمع بين aria-hidden=true و "tabindex=”-1 للتأكد من إزالة العنصر من تسلسل استخدام لوحة المفاتيح. تهدف السمة الجديدة المقترحة inert إلى تسهيل ذلك بالجمع بين سلوك كلتا الخاصيتين. الإشارة إلى حالة العناصر التفاعلية مثل الروابط والأزرار والهدف منها يُسهّل عرض التلميحات hints المرئية للعناصر التفاعل والتنقل في الموقع. تُدعى هذه التلميحات بالإمكانات affordances والتي تُساعد الأشخاص على تصفح الموقع باستخدام مجموعة متنوعة من الأجهزة. النقاط الأساسية تراعى النقاط الأساسية التالية: يجب أن تكون العناصر التفاعلية، مثل الروابط والأزرار، قابلة للتمييز بوضوح عن العناصر غير التفاعلية. إذ يصعُب على المستخدمين التنقل في موقع أو تطبيق عندما لا يتمكنون من معرفة فيما إذا كان العنصر قابلاً للنقر أم لا. هناك العديد من الطرق الصحيحة لتحقيق هذا الهدف. إحدى الممارسات الشائعة هي وضع خط تحت الروابط لتمييزها عن النص المحيط بها. على غرار متطلبات التركيز، تتطلب العناصر التفاعلية مثل الروابط والأزرار حالة حومان hover لمستخدمي الفأرة كي يدركوا أنهم يحركون الفأرة فوق شيء يُمكن النقر عليه. علاوًة على ذلك، يجب أن يكون العنصر التفاعلي مميزًا بنفسه. إذ لا يُساعد الاعتماد على حالة الحومان وحدها للإشارة إلى العناصر القابلة للنقر على الشاشات التي تعمل باللمس. الاستفادة من الترويسات والعلامات تُعطي العناوين Headings والعلامات landmarks بنية دلالية للصفحة، وتزيد من كفاءة التنقل لمستخدمي التقنية المساعدة بشكل كبير. أفاد العديد من مستخدمي برامج قراءة الشاشة أنهم عندما يصلون إلى صفحة غير مألوفة لأول مرة فإنهم يحاولون التنقل عن طريق الترويسات عادةً. وبالمثل، توفر برامج قراءة الشاشة أيضًا القدرة على الانتقال إلى علامات مهمة مثل <main> و <nav>. لذا فمن المهم دراسة كيفية استخدام بنية الصفحة لتوجيه تجربة الاستخدام. النقاط الأساسية تراعى النقاط الأساسية التالية: يجب استخدام التسلسل الهرمي للترويسات h1-h6 بالشكل المناسب والتفكير بالترويسات كأدوات لإنشاء مخطط outline الصفحة. يجب عدم ترك الترويسات بالأنماط المبنية مسبقًا لها بل يجب الآخذ بعين الاعتبار فيما لو كانت جميع الترويسات بنفس الحجم واستخدام المستوى المناسب دلاليًا للمستوى الأول والثاني والثالث. وفي النهاية استخدام أنماط CSS لجعل الترويسات تتماشى مع تصميم الصفحة. يجب استخدام عناصر العلامات landmarks والأدوار roles لتمكين المستخدمين من تجاوز المحتوى المكرر. توفر العديد من التقنيات المساعدة الاختصارات shortcuts اللازمة للانتقال إلى أقسام محدّدة من الصفحة مثل الأقسام المُعرّفة باستخدام العناصر <main> أو <nav> والتي تلعب دور علامات ضمنية. يُمكن استخدام خاصية الدور role في ARIA لتعريف المناطق بشكل صريح في الصفحة مثلًا: <"div role="search>. يُمكن العودة لفقرة الدلالات والتنقل في المحتوى للمزيد من الأمثلة. يجب على المطور تجنب استخدام الخاصية role مع القيمة application إلا في حال تمتعه بالخبرة السابقة في العمل معها. إذ يؤدي هذا الاستخدام إلى إعلام التقنية المساعدة بتعطيل جميع اختصاراتها shortcuts وتمرير جميع ضغطات المفاتيح إلى الصفحة، مما يعني أن مفاتيح قارئ الشاشة التي يستعملها المستخدمون عادةً للتنقل في الصفحة لن تعمل بعد الآن، وسيحتاج المطور إلى معالجة جميع أحداث لوحة المفاتيح بنفسه. استخدام قارئ الشاشة للتحقق السريع من الترويسات والعلامات توفر برامج قراءة الشاشة مثل VoiceOver و NVDA قائمة سياقية context menu تسمح بالوصول السريع للمناطق الهامة في الصفحة. يُمكن استخدام هذه القوائم للحصول على نظرة عامة سريعة على الصفحة وتحديد ما إذا كانت مستويات الترويسات مناسبة وما هي العلامات المستخدمة. راجع مقاطع الفيديو التعليمية هذه حول أساسيات VoiceOver و NVDA. أتمتة العملية يمكن أن يكون التحقق من سهولة الوصول أمرًا شاقًا وعرضة للخطأ. ولذا يكون من الأفضل أتمتة هذه العملية في نهاية المطاف. يمكن القيام بذلك من خلال استخدام ملحقات extensions المستعرض وأدوات اختبار سهولة الوصول باستخدام سطر الأوامر. النقاط الأساسية يمكنك اتباع الخطوات التالية: يُمكن التحقق من اجتياز الصفحة لجميع الاختبارات باستخدام إضافات المتصفح aXe أو WAVE. هذه الإضافات ليست سوى خيارين متاحين ويمكن أن تكون إضافة مفيدة لأي عملية اختبار يدوي حيث تستطيع التعرف على المشكلات الدقيقة بسرعة مثل فشل نسب التباين failing contrast ratios وخصائص ARIA المفقودة. يُمكن استخدام سطر الأوامر واستخدام ax-cli الذي يوفر نفس الوظائف التي يوفرها امتداد المتصفح aXe، ولكن يمكن تشغيله بسهولة من الطرفية. يُمكن تضمين مكتبة برمجية مثل axe-core لمجموعة الاختبار المؤتمتة وذلك لضمان تجانس الاختبارات وتكاملها لا سيما في بيئة عمل تكاملي مستمر. وهي المكتبة نفسها التي تدعم الامتداد aXe إلا أنها أسهل للاستخدام عن طريق سطر الأوامر. في حال استخدام إطار عمل أو مكتبة فيُمكن أن يُطرح السؤال: هل توفر أدوات سهولة الوصول الخاصة بها؟ تتضمن بعض الأمثلة استخدام المكتبة البرمجية protractor-accessibility-plugin في Angular و a11ysuite في Polymer و Web Components. يجب دومًا الاستفادة من الأدوات المتاحة كلما أمكن ذلك تجنبًا لإعادة اختراع الدولاب. استخدام الأداة Lighthouse عند تطوير صفحات الويب التقدميّة تُساعد الأداة Lighthouse في قياس أداء تطبيق الويب التقدمي وتستخدم أيضًا المكتبة ax-core لتشغيل مجموعة من اختبارات سهولة الوصول. يُمكّن استخدام هذه الأداة في تحديد حالات فشل الوصول والتي يُساهم إصلاحها في تحسين تجربة المستخدم الإجمالية للموقع. النتيجة يجب إدراج عمليات التحقق من سهولة الوصول كجزء أساسي في خطة التطوير ويجب إجراء هذه العمليات مبكرًا وبشكل دوري، مما يُساهم في تحسين تجارب الاستخدام للموقع، كما يجب أن لا ننسى أن الوصول السهل يعني تجربة استخدام جيدة! ترجمة -وبتصرف- للمقالة How To Do an Accessibility Review للمؤلف Rob Dodson. اقرأ أيضًا سهولة وصول جميع الزوار لمواقع وتطبيقات الويب النسخة الكاملة من كتاب نحو فهم أعمق لتقنيات HTML5
  6. شجعنا سابقًا على استخدام عناصر HTML الأساسية native لأنها توفر كل من إمكانية التركيز focus و دعم لوحة المفاتيح والدلالات المبنية مسبقًا built-in semantics. إلا أنه يوجد العديد من الحالات التي لا يكفي فيها استخدام عناصر HTML الأساسية مع نسق layout بسيط للوصول للمطلوب. مثلًا: لا يتوفر حاليًا عنصر HTML قياسي لإنشاء قائمة منبثقة pop-up menu رغم أنها شائعة الاستخدام. كما أنه لا يوجد عنصر HTML يوفر خاصية دلالية مثل "يحتاج المستخدم إلى معرفة ذلك بأسرع ما يمكن". نستكشف في هذه المقالة كيفية التعبير عن الدلالات التي لا تستطيع عناصر HTML التعبير عنها بمفردها وذلك بالاعتماد على مواصفات ARIA. مقدمة إلى ARIA تُعدّ المواصفات المُقدّمة من "مبادرة الوصول للويب لتطبيقات الإنترنت الغنية سهلة الوصول" Web Accessibility Initiative's Accessible Rich Internet Applications specification (WAI-ARIA أو فقط ARIA) مفيدة في الحالات التي لا يُمكن معالجتها باستخدام عناصر HTML الأساسية. تسمح هذه المواصفات بتحديد الخصائص التي تُعدّل طريقة ترجمة عنصر إلى شجرة الوصول accessibility tree. نبدأ أولًا بمثال بسيط. نستخدم في مقطع الشيفرة التالي عنصر قائمة li كنوع من مربع اختيار مخصص. يوفر الصف "checkbox" خصائص العنصر المرئية المطلوبة: <li tabindex="0" class="checkbox" checked> Receive promotional offers </li> لن يتمكن قارئ الشاشة من إعطاء أي إشارة للمستخدم بأن العنصر السابق يُعامل كمربع اختيار، وبذا لن يكون هذا العنصر واضحًا إلا للمستخدمين المبصرين أما بالنسبة لضعاف البصر (الذين يستخدمون قارئ الشاشة) فسيكون هذا العنصر مخفيًا تمامًا عنهم. تسمح خصائص ARIA بتوفير المعلومات الناقصة لهذا العنصر مما يُمكّن قارئ الشاشة من فهمه بشكل صحيح. تُبين الشيفرة التالية إضافة خاصية الدور role وخاصية حالة الاختيار aria-checked (من ARIA) للتصريح الواضح بأن العنصر هو مربع اختيار وحالته الافتراضية "مُحدّدًا". ستُضاف قائمة هذا العنصر إلى شجرة الوصول المستخدمة من قبل قارئ الشاشة الذي سيُعرّف هذا العنصر للمستخدم بشكل صحيح بعد الآن. <li tabindex="0" class="checkbox" role="checkbox" checked aria-checked="true"> Receive promotional offers </li> ملاحظة: نعرض خصائص ARIA لاحقًا. تُعدّل ARIA شجرة الوصول الناتجة عن شجرة DOM وفق ما يلي: تسمح ARIA بتعديل شجرة الوصول (جزئيًا أو كليًا) لأي عنصر في الصفحة، إلا أنها لا تزيد على أي من السلوكيات الأصيلة في العنصر. فهي، مثلًا، لن تُغيّر من إمكانية التركيز على العنصر ولن تُضيف له مستمع لحدث من لوحة المفاتيح. تبقى هذه الأمور من مهام المطور. يجب الانتباه جيدًا إلى أننا لا نحتاج لإعادة تعريف الدلالات الضمنية للعناصر. مثلًا: لا يحتاج مربع الاختيار القياسي <"input type="checkbox> لأن نُضيف له دور "role="checkbox ليٌعامله قارئ الشاشة بشكل صحيح. تجدر الإشارة أيضًا إلى أن لبعض عناصر HTML قيود على إضافة أدوار وخصائص ARIA إليها. مثلًا: لا يسمح عنصر مربع النص القياسي في HTML بإضافة أي دور أو خاصية عليه <"input type = "text>. يُمكن استعراض مواصفات ARIA من خلال ARIA in HTML spec. وسنعرض فيما يلي بعض الإمكانات الأخرى التي توفرها ARIA. ما الذي يمكن أن تفعله ARIA؟ يُمكن أن تُعدّل ARIA من دلالات العناصر أو تُضيف إليها دلالات جديدة غير موجودة في دلالاتها الأساسية كما عرضنا في مثال مربع الاختيار السابق. يُمكن لها أيضًا التعبير عن نماذج دلالات غير موجودة على الإطلاق في HTML، مثل قائمة menu أو تبويب في لوحة panel. تسمح ARIA غالبًا بإنشاء عناصر جديدة لواجهة المستخدم لا يُمكن بنائها باستخدام HTML القياسية. مثلًا، يُمكن أن تُضيف ARIA عنوان label إضافي لوضع نص توصيفي يُعامل فقط في واجهة برمجة التطبيقات للتقنية المساعدة. <button aria-label="screen reader only label"></button> يُمكن باستخدام ARIA التعبير عن العلاقات الدلالية بين العناصر التي تعمل على توسيع العلاقة القياسية أب/ابن، مثل شريط تمرير مخصص custom scrollbar يتحكم في منطقة معينة. <div role="scrollbar" aria-controls="main"></div> <div id="main"> . . . </div> يُمكن باستخدام ARIA جعل أجزاء من الصفحة "مباشر" live، بحيث تُعلّم التقنية المساعدة عند حدوث أي تغيير فيها فورًا . <div aria-live="polite"> <span>GOOG: $400</span> </div> من أهم الميزات المُقدّمة من ARIA هو مجموعة الأدوار التي يُمكن تعريفها للعناصر. إذ يسمح إسناد دور معين لعنصر بتعريف سلوك هذا العنصر مباشرًة. توفر ARIA مجموعة معينة من نماذج الأدوار التي يُمكن إسنادها لعناصر HTML. مثلًا: يُخبر استخدامنا للدور "role="checkbox التقنية المساعدة بأن سلوك هذا العنصر يجب أن يُماثل سلوك مربع اختيار checkbox. أي أنه يجب أن يكون له حالة الاختيار (مُحدّد أم لا)، وبأن هذه الحالة يُمكن قلبها باستخدام الفأرة أو شريط المسافة من لوحة المفاتيح (تمامًا مثل أي مربع اختيار من HTML). تلعب أحداث لوحة المفاتيح دورًا أساسيًا عند استخدام قارئ الشاشة، ولذا فمن المهم جدًا التأكد عند إنشاء عنصر مخصص من تطبيق خاصية الدور role في نفس مكان تطبيق خاصية ترتيب الجدولة tabindex مما يضمن ذهاب أحداث لوحة المفاتيح إلى المكان الصحيح وتطبيق الدور بدقة عندما يُصبح التركيز على عنصر. تُقدّم مواصفات ARIA مجموعة المفردات للقيم الممكنة لخاصية الدور ولخصائص ARIA التي يُمكن استخدامها مع الأدوار جنبًا إلى جنب بطريقة تدعمها المستعرضات والتقنيات المساعدة. نظرًا للعدد الكبير للمواصفات، يُمكن البدء من وثيقة ممارسات التأليف ARIA Authoring Practices document التي تعرض أفضل الممارسات لاستخدام أدوار وخصائص ARIA المتاحة. توفر ARIA أيضًا أدوارًا مميزة توسع الخيارات المتاحة في HTML5. يُمكن العودة إلى نماذج تصميم الأدوار Landmark Roles Design Patterns لمزيد من المعلومات. العناوين في ARIA توفر ARIA العديد من الآليات لإضافة عناوين labels أو وصف للعناصر. في الواقع، تُعدّ الخاصية aria-label الطريقة الوحيدة لإضافة مساعدة سهلة الوصول أو نص يصف للعنصر. نعرض فيما يلي الخصائص المستخدمة لإنشاء عناوين سهلة الوصول. عنوان aria-label ARIA تسمح الخاصية aria-label بتحديد سلسلة نصية لاستخدامها كعنوان سهل الوصول. تُلغي هذه الخاصية أي عنوان مُمكن آخر مُحدّد باستخدام طريقة أساسية كاستخدام العنصر label. مثلًا: إذا تضمن زر كل من خاصية النص text وخاصية aria-label فستُستخدم خاصية aria-label فقط. يُمكن استخدام الخاصية aria-label عندما يوفر العنصر نوعًا من المؤثرات المرئية لتوضيح الغرض من العنصر كوضع صورة مُعبّرة على زر عوضًا عن النص إلا أنه مازال ضروريًا توفير توضيح عن هذا العنصر لأي شخص غير قادر على إدراك المؤثر المرئي الموجود (مشاهدة الصورة الموضوعة على الزر). معنون باستخدام aria-labelledby تسمح الخاصية aria-labelledby بتحديد مُعرّف ID عنصر آخر في شجرة DOM كعنصر يوفر العنوان. يشبه هذا السلوك إلى حد كبير استخدام عنصر العنوان label، مع بعض الاختلافات الرئيسية: يُمكن استخدام aria-labelledby مع أي عنصر، وليس فقط مع العناصر القابلة للعنونة. يُشير عنصر العنوان label إلى الشيء الذي يُعنونه، تنعكس العلاقة في حالة aria-labelledby إذ أن الشيء المُعنون يشير إلى الشيء الذي يُعنونه. يُمكن ربط عنصر عنوان label واحد فقط بعنصر قابل للعنونة، أما الخاصية aria-labelledby فيُمكن أن تأخذ قائمة من المُعرّفات IDREFs لتكوين عنوان من عناصر متعدّدة. يكون العنوان النهائي حصيلة ضمّ نصوص العناصر وفق ترتيبها ضمن IDREFs. يُمكن استخدام aria-labelledby للإشارة إلى عناصر مخفية والتي لن تكون ضمن شجرة إمكانية الوصول. مثلًا: يُمكن إضافة امتداد span مخفي جانب العنصر المطلوب عنونته والإشارة إليه باستخدام aria-labelledby. لا تمنح الخاصية aria-labelledby سلوك النقر المألوف على عنصر العنوان label إذ أن ARIA لا يؤثر إلا على شجرة الوصول. يجب ملاحظة أن عنوان aria-labelledby له الأولوية الأولى أي أنه يتجاوز جميع مصادر العناوين الأخرى. مثلًا: في حال كان لعنصر كل من الخاصيتين aria-labelledby و aria-label أو كل من الخاصيتين aria-labelledby و label الأساسية من HTML، فسيكون العنوان المُعتمد هو العنوان المُحدّد من الخاصية aria-labelledby. العلاقات في ARIA تُنشئ الخصائص العلائقية علاقات Relationships دلالية بين عناصر الصفحة وذلك بغض النظر عن علاقتهم في شجرة DOM. مثلًا: الخاصية aria-labelledby هي مثال عن خاصية العلاقة "العنصر مُعنون بهذا العنصر". تُحدّد مواصفات ARIA ثمان خصائص علائقية [eight relationship attributes، ستٌ منها تُشير إلى عنصر أو أكثر على الصفحة وتُستخدم لإنشاء علاقات جديدة بين هذه العناصر: aria-activedescendant. aria-controls. aria-describedby. aria-labelledby. aria-owns. يكمن الفرق في كل حالة بمعنى العلاقة وكيفية عرضها للمستخدمين. التملك aria-owns تُعدّ خاصية التملك aria-owns من أكثر علاقات ARIA استخدامًا. تسمح هذه الخاصية بإعلام التقنية المساعدة بأن عنصر ما منفصل في شجرة DOM يجب أن يُعامل كابن للعنصر الحالي، أو أنه يجب إعادة ترتيب العناصر الأبناء بشكل مختلف. مثلًا: في حال وضع قائمة جزئية منبثقة pop-up sub-menu أمام القائمة الأب لها مع عدم إمكانية وضع هذه القائمة المنبثقة ابنًا للقائمة الأب في شجرة DOM لأن ذلك يؤدي إلى تغيير مظهر العرض المرئي المطلوب. يُمكن في هذه الحالة استخدام aria-owns لتقديم القائمة الفرعية المنبثقة كابن للقائمة الأب لقارئ الشاشة. الابن النشط aria-activedescendant تلعب الخاصية aria-activedescendant دور الوصل بين العناصر. مثلًا: تُعلّم هذه الخاصية التقنية المساعدة بأنه عند وصول التركيز على عنصر (الذي أسندنا قيمة لهذه الخاصية فيه) فيجب إعلام المستخدم بحصول التركيز على عنصر آخر. مثلًا: يُمكن أن يكون المطلوب عند استخدام مربع قائمة listbox ترك التركيز على حاوية مربع القائمة مع تحديث الخاصية aria-activedescendant إلى العنصر الحالي المُحدّد من القائمة مما يجعل هذا العنصر يظهر للتقنية المساعدة فيما لو كان العنصر الحاصل على التركيز. الوصف باستخدام aria-describedby توفر الخاصية aria-describedby وصفًا سهل الوصول بنفس الطريقة التي توفر بها الخاصية aria-labelledby العنوان. يُمكن لهذه الخاصية الإشارة إلى عناصر مخفية سواًء في شجرة الوصول أو في شجرة DOM. تُقدّم هذه الخاصية آلية مفيدة لتقديم شرحًا إضافيًا لكل من المستخدمين العاديين ولمستخدمي التقنية المساعدة. من الأمثلة الشهيرة على استخدام هذه الخاصية هي حالة حقل إدخال كلمة السر والمصحوب بنص يشرح متطلبات كلمة السر. على خلاف العنوان، يُمكن عرض هذا النص التوضيحي أم لا للمستخدم الذي يكون له الخيار في الوصول إليه أو قد يأتي بعد كل المعلومات الأخرى أو يُمكن استباقه بشيء آخر. مثلًا: إذا كان المستخدم يُدخل بعض المعلومات فيُمكن لهذه المعلومات أن تُظهر مرة أخرى مقاطعًة وصف العناصر. وبهذا يكون الوصف طريقة رائعة لتوصيل معلومات إضافية غير أساسية ولن يقف في طريق الحصول على معلومات أكثر أهمية مثل دور العنصر. المكان والحجم aria-posinset & aria-setsize تعمل الخصائص المتبقية معًا (والمختلفة قليلًا عن الخصائص السابقة). تُعرّف الخاصيتان aria-posinset (المكان في المجموعة) و aria-setsize (حجم المجموعة) العلاقة بين العناصر الأخوة في مجموعة، مثل حالة القائمة list. عند تعذر تحديد قياس المجموعة باستخدام العناصر الموجودة في شجرة DOM، مثل حالة التصيير الكسول lazy rendering المستخدمة عادًة لتجنب وجود قائمة ضخمة في شجرة DOM دفعة واحدة. تُحدّد الخاصية aria-setsize الحجم الفعلي للمجموعة، أما الخاصية aria-posinset فتُحدّد مكان العناصر في المجموعة. مثلًا: يُمكن في مجموعة تحوي 1000 عنصر من تعيين العنصر ذو الترتيب 857 ليكون الإظهار الأول في شجرة DOM واستخدام تقانات HTML الديناميكية للتأكد من تمكّن المستخدم من استكشاف كامل عناصر القائمة عند الحاجة. إظهار وإخفاء المحتوى تُعدّ عملية عرض الأجزاء المناسبة من الصفحة للتقنية المساعدة من الآليات المهمة في صقل تجربة مستخدمي التقنية المساعدة. يوجد العديد من الطرق للتأكد من عدم عرض جزء ما من شجرة DOM لواجهة برمجة تطبيقات شجرة الوصول. الإخفاء aria-hidden أولًا، لا يُضمّن في شجرة الوصول أي شيء مخفي بشكل صريح في شجرة DOM. وبالتالي، فإن أي نمط CSS يتعلق بالإخفاء مثل visibility: hidden أو display: none أو الخاصية hidden في HTML5 سيكون مخفيًا أيضًا لمستخدمي التقنية المساعدة. ومع ذلك، فإن أي عنصر لم يُعرض على الصفحة إلا أنه غير مخفي بشكل صريح سيبقى موجودًا في شجرة الوصول. من الطرق الشائعة تضمين "نص لقارئ الشاشة فقط" في عنصر موضوع خارج الشاشة بشكل مطلق absolute. .sr-only { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; } يُمكن، كما عرضنا سابقًا، توفير نص لقارئ الشاشة فقط باستخدام aria-label أو aria-labelledby أو aria-describedby مع الإشارة إلى عنصر مخفي، كما يُمكن العودة إلى مقالة منظمة WebAIM حول تقانات إخفاء العناصر Techniques for hiding text للمزيد من المعلومات عن توفير "نص لقارئ الشاشة فقط". توفر ARIA آلية لاستبعاد المحتوى غير المخفي باستخدام الخاصية aria-hidden والتي يؤدي تطبيقها على عنصر إلى حذفه مع جميع أبناءه من شجرة الوصول مع استثناء العناصر المشار إليها بإحدى الخاصيتين aria-labelledby و aria-describedby. <div class="deck"> <div class="slide" aria-hidden="true"> Sales Targets </div> <div class="slide"> Quarterly Sales </div> <div class="slide" aria-hidden="true"> Action Items </div> </div> مثلًا: يُمكن استخدام الخاصية aria-hidden في حال إنشاء واجهة مستخدم شرطية modal والتي تمنع المستخدم من الوصول إلى الصفحة الرئيسية. في مثل هذه الحالة يرى المستخدم المبصر نوعًا من التراكب شبه الشفاف والذي يُفهمه بأنه لا يستطيع الوصول إلى عناصر الصفحة الأساسية أما مستخدم قارئ الشاشة فيبقى قادرًا على الوصول إلى جميع أجزاء الصفحة. يُمكن هنا إضافًة لقفل لوحة المفاتيح التأكد من أن جميع أجزاء الصفحة المطلوب إخفائها عن المستخدم لها الخاصية aria-hidden. يٌمكن الآن بعد فهم أساسيات ARIA وكيف تتعامل مع دلالات عناصر HTML الأساسية وآلية استخدامها لإجراء تعديلات في شجرة إمكانية الوصول، الانتقال إلى مسألة إيصال المعلومات الهامة بشكل فوري للمستخدم. الجزء الحي aria-live تسمح الخاصية aria-live للمطور بتحديد جزء من الصفحة على أنه "حي" live بمعنى أنه يجب إعلام المستخدم بأي تحديث في هذا الجزء فورًا وبغض النظر عن موضع المستخدم في الصفحة، أي دون أن يقوم المستخدم باستكشاف هذا الجزء من الصفحة. عندما يكون لعنصر الخاصية aria-live، يُدعى الجزء من الصفحة الذي يحوي هذا العنصر وأبناءه بالمنطقة الحية أو الآنية live region. مثلًا: إذا كانت المنطقة الحية عبارة عن رسالة حالة تُظهر نتيجة إجراء ما للمستخدم، وكانت الرسالة هامة كفاية لجذب أنظار المستخدمين المبصرين فإنه من الضروري بمكان تنبيه مستخدم التقنية المساعدة إليها وذلك بضبط الخاصية aria-live. يُمكن مقارنة حالة هذا القسم div العادي: <div class="status">Your message has been sent.</div> مع نظيره "الآني": <div class="status" aria-live="polite">Your message has been sent.</div> يُمكن أن يكون للخاصية aria-live إحدى القيم الثلاث التالية: polite و assertive و off. القيمة aria-live="polite": تُعلّم هذه القيمة التقنية المساعدة لتنبيه المستخدم بالتغيير الحاصل حال انتهائه مما يعمل حاليًا. تُستخدم هذه القيمة عندما يكون هنالك أمرًا هامًا إلا أنه غير مستعجل جدًا وهي الحالة العامة لاستخدام aria-live. القيمة aria-live="assertive": تُعلّم هذه القيمة التقنية المساعدة لتنبيه المستخدم بالتغيير الحاصل فورًا وبغض النظر عما يقوم به حاليًا. تُستخدم هذه القيمة عندما يكون هنالك أمرًا هامًا ومستعجلًا مثل "حصل خطأ على الخادم ولم تُحفظ تعديلاتك، يُرجى إعادة تحميل الصفحة"، أو كتعديل على حقل إدخال نتيجة إجراء مستخدم كالنقر على أزرار عنصر تحديد خطوة stepper widget. القيمة aria-live="off": تُعلّم هذه القيمة التقنية المساعدة بإيقاف مقاطعات aria-live بشكل مؤقت. من الإرشادات الذكية للتأكد من أن المناطق المباشرة تعمل بشكل صحيح: أولًا، قد تُحدّد المنطقة المباشرة aria-live خلال التحميل الأول للصفحة. لا يُعدّ هذا الأمر قاعدة مؤكدة إلا أنه قد يكون هذا سبب مشكلة المنطقة المباشرة. ثانيًا، يُمكن أن يكون تعامل برامج قراءة الشاشة مختلفًا مع أنواع التحديثات الممكنة. مثلًا: يُمكن إطلاق تنبيه في بعض برامج قراءة الشاشة لمجرد قلب حالة عنصر من مخفي إلى ظاهر أو بالعكس باستخدام النمط hidden. يُمكن للخصائص الأخرى التي تعمل مع aria-live المساعدة في الضبط الدقيق لما يُعرض للمستخدم حال حصول تحديثات في المنطقة المباشرة. تُحدّد الخاصية aria-atomic، والتي تأخذ إحدى القيمتين true أو false (القيمة الافتراضية)، فيما إذا كان من الواجب اعتبار كامل المنطقة المباشرة عند الإعلام بالتحديثات أم لا. مثلًا: في حال استخدام عنصر التاريخ والذي يتألف من اليوم والشهر والسنة، فإن تغيير المستخدم لقيمة الشهر فقط يؤدي إلى إعادة قراءة التاريخ فيما لو كانت aria-atomic=true و aria-live=true. تُحدّد الخاصية aria-relevant أنواع التغييرات الواجب إعلام المستخدم بها. يوجد بعض الخيارات التي يُمكن استخدامها بشكل منفصل أو بشكل قائمة: الإضافات additions: تعني أن أي إضافة إلى المنطقة المباشرة يجب إعلام المستخدم بها. مثلًا: في حال إضافة امتداد span لسجل رسائل الحالة يُمكن أن يعني أنه من الأنسب إعلام المستخدم بهذا الامتداد الجديد (بافتراض أن قيمة aria-atomic هي false). النص text: تعني أن النص المُضاف لأي عقدة تابعة descendant node هو نصًا مناسبًا لإعلام المستخدم به. مثلًا: يُمكن عند تحديث الخاصية textContent لحقل نص مخصص إعادة قراءة النص للمستخدم. الحذف removals: تعني وجوب إعلام المستخدم بحذف أي نص أو عقدة تابعة. الكل all: تعني أن جميع التغييرات مناسبة لإعلام المستخدم بها. تكون القيمة الافتراضية للخاصية هي التسلسل additions text مما يعني أنه في حال عدم تحديد الخاصية aria-relevant فسيُعلم المستخدم بأي إضافة إلى العنصر وهو المطلوب في معظم الأحيان. أخيرًا، تطلب الخاصية aria-busy من التقنية المساعدة إهمال جميع التحديثات على العنصر بشكل مؤقت، مثلًا: خلال فترة التحميل يُمكن إسناد القيمة true لهذه الخاصية وعند الانتهاء نعاود إسناد القيمة false لها للعودة للوضع الطبيعي لقارئ الشاشة. ترجمة -وبتصرف- لمجموعة المقالات: Introduction to ARIA ARIA Labels and Relationships Hiding and Updating Content للمؤلفين: Meggin Kearney وDave Gash وAlice Boxhall. اقرأ أيضًا المقال السابق: الدلالات المضمنة لعناصر صفحة الويب ودورها في تعزيز سهولة الوصول سهولة وصول جميع الزوار لمواقع وتطبيقات الويب التركيز على عناصر صفحة الويب نحو فهم أعمق لتقنيات HTML5
  7. عرضنا في مقالة سابقة مسائل سهولة الوصول أو ما نطلق عليها بالشمولية (شمل مختلف المستخدمين والزائرين سواء كانت لديهم مشاكل وإعاقات أم لا) وإتاحة جميع عمليات التفاعل مع صفحات الموقع باستخدام لوحة المفاتيح فقط، لاسيما للأشخاص الذين لا يستخدمون الفأرة أو أجهزة التأشير لأسباب متعددة مثل الإعاقات الجسدية أو بسبب مشكلة تقنية أو لمجرد تفضيلاتهم الشخصية. لا يتطلب تحقيق هذه السهولة الكثير من الجهد والوقت فيما لو خُطّط لها بشكل صحيح منذ البداية. مع التنويه إلى أن تحقيق هذه السهولة يُفضي في نهاية المطاف إلى صفحات سهلة الوصول وجذابًة للمستخدمين. نبدأ أولًا، في هذه المقالة، بعرض بعض المعلومات الأساسية عن التقنية المساعدة assistive technology وهو المصطلح المُستخدم للإشارة إلى الأدوات المساعدة، كقارئ الشاشة مثلًا، للأشخاص الذين يعانون من إعاقات تمنعهم من الوصول للمعلومات بشكل اعتيادي. ننتقل بعد ذلك لعرض بعض تجارب الاستخدام العامة، ونتعمق بعدها في تجارب مستخدمي التقنيات المساعدة. وفي النهاية، نعرض كيفية استخدام لغة HTML بفعالية لتحقيق تجربة استخدام لهؤلاء المستخدمين وكيفية تداخل ذلك مع مسألة التركيز التي تعرضنا لها في مقالة سابقة. التقنية المساعدة يشمل مصطلح التقنية المساعدة جميع الأجهزة والبرمجيات والأدوات التي تُساعد أي شخص يعاني من إعاقة ما من إكمال مهمة معينة بنجاح. بشكل عام، يُمكن أن تكون الأداة شيئًا بسيطًا مثل عكاز المشي أو عدسة التكبير للقراءة، أو شيئًا ذو تقنية عالية مثل ذراع جسمال robot أو برنامج التعرف على الصور في هاتف ذكي. يُمكن أن تتضمن التقنية المساعدة شيئًا عامًا مثل تكبير/تصغير zoom المتصفح، أو خاصًا مثل وحدة تحكم مصممة خصيصًا للألعاب. كما يُمكن أن تكون جهازًا منفصلًا مثل شاشة برايل braille display (للكفيف) أو مُضمّنه بشكل كامل في برنامج ما مثل التحكم بالصوت. كما يُمكن أن تكون التقنية المساعدة مبنية مسبقًا built-in في نظام التشغيل، أو عبارة عن إضافة add-on ما مثل امتدادات المتصفح Chrome. لا يُمكن الفصل بين التقنية عمومًا والتقنية المساعدة بشكل واضح، إذ تهدف التقنية بجميع أشكالها، في النهاية، إلى مساعدة الناس للقيام بمهامهم. يُمكن لنفس التقنية أن تُعدّ أحيانًا ضمن فئة التقنية المساعدة وأحيانًا خارجها. مثلًا: كانت الآلة الحاسبة الناطقة للمكفوفين من أقدم المنتجات التجارية لتركيب الكلام. أما اليوم، فقد أصبح تركيب الكلام موجودًا في كل مكان: بدءًا من برمجيات توجيه الاتجاهات driving directions إلى المساعدين الافتراضيين virtual assistants. على النقيض من ذلك، نجد اليوم العديد من التقنيات العامة تُستخدم لأغراض مساعدة مثل استخدام ضعاف البصر زوم zoom كاميرا هواتفهم الذكية لإلقاء نظرة أفضل على شيء صغير في العالم الحقيقي. يجب أن يأخذ مطور صفحات الويب بعين الاعتبار مجموعة متنوعة من التقنيات الممكنة. إذ قد يتفاعل الأشخاص مع موقع الويب باستخدام قارئ الشاشة screen reader أو عارض برايل braille display أو مكبر الشاشة screen magnifier أو متحكم الصوت voice control أو جهاز تبديل switch device أو أي شكل آخر من أشكال التقنيات المساعدة والتي تُكيّف واجهات الصفحات الافتراضية لإنشاء واجهات مُخصصة يُمكن استخدامها من قبل هؤلاء الأشخاص مع اختلاف أدوات الوصول المستخدمة. تعتمد العديد من هذه التقنيات المساعدة على الدلالات المُعبّر عنها برمجيًا programmatically expressed semantics لإنشاء تجربة استخدام سهلة الوصول. نتطرق أولًا إلى الإمكانات affordances قبل شرح الدلالات المعبر عنها برمجيًا. الإمكانات الدالة على كيفية الاستخدام يُمكن لنا، في أغلب الأحيان، بمجرد النظر على شكل أداة أو جهاز أن نُكوّن فكرة عن عمل هذا الجهاز وكيفية استخدامه. يُساهم التصميم الجيد للإمكانات Affordances (المهام المعينة التي توفرها الأغراض المختلفة) في توضيح آلية استخدامها للعموم بسهولة. مثلًا يُدرك أي شخص بمجرد النظر لغلاية ماء أو إبريق شاي أن الإبريق يُحمل من مقبضه وليس من فوهته حتى لو كانت المرة الأولى التي يرى فيها إبريق الشاي. يُمكن تفسير ذلك بأن الإمكانات المُقدّمة من الإبريق تُشابه الإمكانات المُقدّمة من أغراض أخرى شبيهة به مثل أواني الري وأباريق المشروبات وأكواب القهوة وغيرها. بالطبع، يُمكنك مسك الإبريق من فوهته إلا أن تجارب المستخدم السابقة مع أغراض أخرى شبيهة بالإبريق تؤكد له بأن المقبض هو الخيار الأنسب. بشكل مماثل، تُعدّ إمكانات واجهات المستخدم البيانية الإجراءات التي يُمكن للمستخدم القيام بها، إلا أن هذه الإجراءات يُمكن أن تكون غامضة نظرًا لعدم وجود أي كائن مادي للتفاعل معه. تُصمّم واجهات المستخدم البيانية لتكون إمكاناتها واضحة دون أي لبس فيها باستخدام الأزرار buttons ومربعات الاختيار check boxes وأشرطة التمرير scrol bars بحيث يكون استخدامها واضحًا دون الحاجة لتدريب طويل للتعامل معها. مثلًا، يُمكن التعبير عن بعض عناصر التحكم الشائعة (إمكانات العناصر) كما يلي: أزرار الانتقاء Radio buttons "يُمكن انتقاء واحدًا من الخيارات المتاحة". مربع الاختيار Check box "يُمكن اختيار نعم أو لا من هذا الخيار". حقل نصي Text field "يُمكن كتابة أي نص في هذه المساحة". قائمة منسدلة Dropdown "يُمكن فتح هذا العنصر لعرض الخيارات المتاحة". يُمكن الوصول للاستنتاجات السابقة حول عناصر التحكم بمجرد رؤيتها، إلا أن السؤال المطروح هو: ما وضع الشخص غير القادر على رؤية هذه العناصر وبالتالي لا يستطيع إدراك إمكاناتها بديهيًا؟. يجب على المطور إذًا التأكد من التعبير عن المعلومات بمرونة كافية للوصول إليها باستخدام التقنية المساعدة والتي يُمكن لها إنشاء واجهة بديلة لتناسب احتياجات المستخدم الخاصة. يُدعى هذا العرض غير المرئي من إمكانات الاستخدام بالدلالات semantics. قارئ الشاشة يُعدّ قارئ الشاشة screen reader من أكثر التقنيات المساعدة شيوعًا، وهو عبارة عن برنامج يقرأ النص المكتوب على الشاشة بصوتٍ عالٍ مُولدٍ تلقائيًا مما يُمكّن الأشخاص ذوي الإعاقة البصرية من استخدام الحواسيب. يُمكن مشاهدة هذا الفيديو لفيكتور تساران (الكفيف ومدير البرنامج الفني في شركة Google) وهو يتصفح الويب باستخدام قارئ الشاشة المدعو VoiceOver في نظام التشغيل OS X. لمعايشة تجربة الكفيف الفعلية مع قارئ الشاشة، يُمكن القيام بالتمرين التالي: فتح صفحة الويب التالية (باستخدام المتصفح Chrome) ChromeVox lite demo page والتي هي عبارة عن صفحة بسيطة مكتوبة بلغة جافا سكريبت تُخفى النصوص فيها لمحاكاة تجربة ضعف البصر ولإرغام المستخدم على استخدام قارئ الشاشة. يُمكن استخدام لوحة التحكم الموجودة أسفل الشاشة للتحكم في قارئ الشاشة والذي يحتوي على الحد الأدنى من الوظائف الضرورية. يتم التنقل في المحتوى باستخدام الزرين السابق Previous والتالي Next، كما يُمكن النقر على أي شيء باستخدام الزر Click. تُستخدم الصفحة السابقة بعد تمكين ChromeVox lite لاستخدام قارئ الشاشة. وفي الحقيقة، يوفر قارئ الشاشة (أو أي تقنية مساعدة أخرى) تجربة استخدام بديلة كاملة اعتمادًا على الدلالات المُعبّر عنها برمجيًا. فعوضًا عن الواجهة المرئية، يوفر قارئ الشاشة واجهة مسموعة. يُعلَم قارئ الشاشة ببعض المعلومات حول كل عنصر من عناصر الواجهة. يجب أن نتوقع من قارئ شاشة مصمم جيدًا أن يُخبر المستخدم بجميع، أو على الأقل بمعظم، المعلومات التالية حول عناصر الواجهة: دور العنصر role أو نوعه type، إذا كان موصّفًا (يجب أن يكون). اسم العنصر name، إذا كان له اسم (يجب). قيمة العنصر value، إذا كان له قيمة (قد تكون أو لا تكون). حالة العنصر state، مثلًا: فيما إذا كان مُمكّنًا أو معطلاً (إن أمكن). يستطيع قارئ الشاشة إنشاء واجهة المستخدم البديلة لأن العناصر الأصلية native تحوي مسبقًا بيانات تعريف إمكانية الوصول. كما يستخدم المتصفح الشيفرة الأساسية native code لإظهار الواجهة الرسومية يستخدم قارئ الشاشة البيانات الوصفية metadata في عقد شجرة DOM لإنشاء إصدار سهل الوصول كما يُبين المثال التالي: شجرة الوصول لا نحتاج إلى إنشاء أي واجهة مرئية على الإطلاق في حال أردنا إنشاء واجهات لمستخدمي قارئ الشاشة فقط، وإنما يجب توفير المعلومات الكافية لقارئ الشاشة ليُنشئ الواجهة المسموعة الموافقة لاحتياجات هؤلاء المستخدمين. أي أنه يجب إنشاء نوع من واجهات برمجة التطبيقات API تصف بنية صفحة الويب على غرار DOM API مع معلومات وعقد أقل مما تحتاجه الواجهة المرئية. يُبين الشكل التالي مثالًا: يُظهر المثال السابق ما يُمكن أن يُقدّم المتصفح لقارئ الشاشة. يٌعدّل المتصفح شجرة DOM إلى شكل قابل للاستخدام من قبل التقنية المساعدة. ندعو الشجرة المُعدّلة بشجرة الوصول Accessibility Tree. يُمكن تشبيه شجرة الوصول بصفحة ويب قديمة من التسعينيات مع بضعة صور والكثير من الروابط مع حقل وزر كما يُظهر الشكل التالي: يُمكن التمعن في صفحة الشكل السابق لتخيل تجربة مماثلة لما سيحصل عليه قارئ الشاشة، ومع ملاحظة بساطة الصفحة إلا أنها تُشبه إلى حدّ كبير شجرة الوصول. تتفاعل معظم التقنيات المساعدة مع شجرة الوصول، ويكون التدفق شبيهًا بما يلي: يُقدّم التطبيق (المتصفح أو أي تطبيق آخر) إصدارًا دلاليًا من واجهة المستخدم إلى التقنية المساعدة عبر واجهة برمجة تطبيقات معينة API. تستخدم التقنية المساعدة المعلومات التي تصلها عبر واجهة برمجة التطبيقات لإنشاء عرضًا بديلًا لواجهة المستخدم. مثلًا: يُنشئ قارئ الشاشة واجهة يسمع فيها المستخدم تمثيلًا منطوقًا للتطبيق. يُمكن أن تسمح التقنية المساعدة للمستخدم بالتفاعل مع التطبيق بطرق مختلفة. مثلًا: توفر معظم قارئات الشاشة إمكانية محاكاة النقر بالفأرة أو بالإصبع. تُخبر التقنية المساعدة التطبيق برغبة المستخدم القيام بإجراء ما (مثل "النقر") عبر واجهة برمجة التطبيقات. يتحمل التطبيق بعد ذلك مسؤولية تفسير الإجراء بشكل مناسب ضمن واجهة المستخدم . أما مع متصفح الويب (منصة تشغيل تطبيق الويب)، فسيكون هنالك خطوة إضافية في كل اتجاه لأن المتصفح يحتاج لترجمة تطبيق الويب إلى شجرة وصول والتأكد من إطلاق الأحداث events المناسبة في جافا سكريبت والموافقة لأفعال المستخدم والتي تُمرر للمتصفح من التقنية المساعدة. يجب على مطور الويب التأكد من السلوك السابق (والذي هو من مسؤولية المتصفح) وتطوير صفحات ويب تستفيد من هذا السلوك لإنشاء تجارب استخدام سهلة الوصول. لتحقيق الهدف السابق (سهولة الوصول)، يجب التأكد من التعبير عن دلالات الصفحات بشكل صحيح مثل: التأكد من سهولة الوصول لأدوار roles وحالات states وخصائص properties جميع العناصر الهامة، مع تحديد أسماء هذه العناصر ووصفها. وبالتالي يُمكن للمتصفح بعد ذلك السماح للتقنية المساعدة بالوصول إلى هذه المعلومات لإنشاء تجربة مخصصة. الدلالات في عناصر لغة HTML الأساسية يُمكن للمتصفح تحويل شجرة DOM إلى شجرة الوصول لأن لمعظم عناصر HTML دلالة ضمنية. تستخدم شجرة DOM عناصر HTML الأساسية والتي تعمل بشكل قياسي موحد على مختلف المنصات. تُعالَج مسألة الوصول للعناصر الأساسية مثل الروابط والأزرار تلقائيًا. يُمكن الاستفادة من الدلالات المبنية مسبقًا built-in بكتابة تعليمات HTML التي تٌعبّر عن دلالات عناصر الصفحة. يُمكن أن يستخدم المطور أحيانًا عناصر شبيهة بالعناصر الأساسية إلا أنها ليست كذلك. مثلًا العنصر الشبيه بالزر في الصورة التالية ليس زرًا على الإطلاق: يُمكن إنشاء العنصر السابق في HTML بعدة طرق مثلًا: <div class="button-ish">Give me tacos</div> لن يتمكن قارئ الشاشة من التعرف على العنصر السابق (ليس زرًا) عند الوصول إليه. كما يجب إضافة خاصية ترتيب الجدولة tabindex للعنصر السابق ليتمكن مستخدمو لوحة المفاتيح من الوصول إليه إذ لا يُمكن الوصول إليه (كما هو مُصمم حاليًا) إلا عن طريق الفأرة. يُمكن تجاوز المشكلة السابقة باستخدام عنصر زر عادي عوضًا عن div. يوفر استخدام عنصر أصلي للمطور معالجة مسائل الوصول باستخدام لوحة المفاتيح إذ يُعالجها العنصر الأصلي بمفرده. لا يجب التردد في استخدام العناصر الأصلية بحجة مظهرها البسيط الأساسي إذ يُمكن للمطور إضافة المظهر المرئي المناسب لها مع حفاظها على دلالاتها الضمنية وسلوكها الاعتيادي. لاحظنا سابقًا أن قارئ الشاشة يُعلن عن دور العنصر واسمه وحالته وقيمته. يسمح استخدام الدلالات الصحيحة بالوصول إلى الدور والحالة والقيمة إلا أنه يجب التأكد من سهولة العثور على اسم العنصر. يوجد عمليًا نوعين من الأسماء: العناوين المرئية Visible labels والتي يربط المستخدمون من خلالها المعنى بالعنصر. البدائل النصية Text alternatives والتي تُستخدم عندما لا يكون هنالك حاجة لتسمية مرئية. لا نحتاج لعمل أي شيء مع العناصر النصية لأن محتواها النصي يُعرّف عنها، أما بالنسبة لبقية عناصر التحكم أو الإدخال أو العناصر ذات المحتوى المرئي، كالصور مثلًا، فيجب التأكد من وجود اسمها. عمليًا، يكون التحقق من الاسم على رأس قائمة التحقق من سهولة الوصول المعتمدّة من قبل المنظمة WebAIM (منظمة غير ربحية تُعنى بتقديم حلول سهولة الوصول منذ عام 1999). من الطرق المستخدمة اتباع التوصية: "يجب أن يكون لجميع عناصر الإدخال في النموذج عناوين نصية". يوجد طريقتين لربط عنوان label مع عنصر على النموذج مثل مربع الاختيار، تؤدي كل منهما إلى جعل نص التسمية هدفًا لنقر مربع الاختيار والذي يكون مساعدًا أيضًا لمستخدمي الفأرة أو شاشة اللمس. يُمكن لربط عنوان مع عنصر إما: وضع عنصر الإدخال داخل عنصر عنوان كما تُبين الشيفرة التالية: <label> <input type="checkbox">Receive promotional offers? </label> حيث يكون مظهر مربع الاختيار مع إمكانية النقر على العنوان أو على مربع الاختيار لاختيار/إزالة الاختيار من مربع الاختيار: أو: استخدام الخاصية for لعنصر العنوان مع إسناد مُعرّف مربع الاختيار لها كما توضح الشيفرة التالية: <input id="promo" type="checkbox"> <label for="promo">Receive promotional offers?</label> يُمكن لقارئ الشاشة إظهار المعلومات التالية عندما يكون مربع الاختيار معنونًا بشكل صحيح: العنصر هو مربع اختيار بحالة الاختيار ويُدعى "Receive promotional offers?‎" كما يُبين الشكل التالي: يُمكن استخدام قارئ الشاشة للعثور على التسميات غير المرتبطة مع العناصر بشكل صحيح عن طريق التنقل عبر الصفحة باستخدام مفتاح الجدولة tab والتحقق من الأدوار والحالات والأسماء المنطوقة. النصوص البديلة للصور تُعدّ الصور من أهم مكونات صفحات الويب، وهي بالطبع نقطة شائكة خاصًة للمستخدمين ضعاف البصر. ولذا يجب الآخذ بعين الاعتبار لدور الصورة في الصفحة عند تحديد النص البديل لها. لنعاين الصورة التالية مثلًا: <article> <h2>Study shows 9 out of 10 cats quietly judging their owners as they sleep</h2> <img src="imgs/160204193356-01-cat-500.jpg"> </article> تظهر صورة قطة في الصفحة في مقال عن سلوك القطط المعروف في حكمهم على الآخرين. يقوم قارئ الشاشة في هذا المثال بنطق اسم الصورة "/160204193356-01-cat-500.jpg" بشكل صحيح إلا أن هذا غير مفيد للمستخدم على الإطلاق. يُمكن استخدام الخاصية alt لتوفير نص بديل ذو معنى مفيد لهذه الصورة مثل "قطه تُحدّق في الفضاء بشكل متوعد" <img src="/160204193356-01-cat-500.jpg" alt="A cat staring menacingly off into space"> يُظهر قارئ الشاشة الوصف الموجز للصورة في الشريط الأسود VoiceOver، ويُمكن للمستخدم بعدها اختيار الانتقال للمقالة. يُمكن أن نشير إلى الملاحظتين التاليتين عن الخاصية alt: تسمح الخاصية alt بتحديد نص بسيط لعرضه في أي وقت لا تكون فيه الصورة متاحة، مثل حالة الفشل في تحميلها أو التعامل معها من قبل زاحف الويب crawler أو المرور عليها من قبل قارئ الشاشة. تختلف الخاصية alt عن خاصية العنوان title أو أي نوع من التسميات التوضيحية في أنها تُستخدم فقط عند تعذر توفير الصورة للمستخدم. تُعدّ كتابة نصًا بديلًا مفيدًا عملًا فنيًا إذ يجب أن يعكس النص مفهوم الصورة بشكل واضح وضمن سياق استخدامها. يُمكن مثلًا وضع النص البديل المناسب التالي لصورة شعار موقع الصفحة السابقة The Funion logo. <img class="logo" src="logo.jpg" alt="The Funion logo"> قد يكون إعطاء الشعار نصًا بديًلا بسيطًا مثل "الصفحة الرئيسية" أمرًا جاذبًا أكثر للمطور (لأن الشعار موضوع في الصفحة الرئيسة) إلا أن هذا قد يُربك كلًا من ضعاف البصر وجيدي النظر على وجه السواء. تؤدي القيمة "الصفحة الرئيسية" إلى إرباك مستخدم قارئ الشاشة الذي يريد تحديد موقع الشعار الموجود في رأس الصفحة، كذلك سيواجه المستخدم المبصر نفس السؤال: ماذا يؤدي النقر على الشعار؟ وبالمقابل، لا يكون مفيدًا دومًا وصف الصورة. لنأخذ مثلًا حالة صورة العدسة المكبرة التي توضع داخل زر البحث الذي له النص "بحث". بالتأكيد لو لم يكن النص موجودًا، لكان من المفيد إعطاء الخاصية alt للصورة القيمة "بحث" إلا أنه مع وجود النص سيقرأ قارئ الشاشة هذا النص بصوتٍ عالٍ وسيكون من التكرار قراءة قيمة الخاصية alt للصورة. يؤدي عدم وضع قيمة للخاصية alt للصورة إلى قراءة اسم الملف من قبل قارئ الشاشة في أغلب الأحيان، والذي يكون أمرًا مربكًا وغير مفيد. يُمكن اللجوء في مثل هكذا حالات إلى وضع قيمة فارغة للخاصية alt وبالتالي سيتخطى قارئ الشاشة الصورة بشكل كامل. <img src="magnifying-glass.jpg" alt=""> والخلاصة، يجب أن تحتوي جميع الصور على الخاصية alt، إلا أنه ليس بالضرورة أن تكون قيمتها نصًا. إذ يجب أن تحتوي الصور الهامة على نص بديل يصف الصورة بإيجاز، أما الصور الموضوعة بهدف الزخرفة فقط، فيجب وضع قيمة فارغة للخاصية alt أي alt=""‎ الدلالات والتنقل في المحتوى تعرفنا فيما سبق على الإمكانات والدلالات وكيفية استخدام التقنيات المساعدة لشجرة الوصول بهدف إنشاء تجربة استخدام بديلة لمستخدمي هذه التقنيات. يُمكن تحقيق سهولة الوصول بجهد بسيط في حال كتابة عناصر HTML بطريقة مُعبرّة عن دلالاتها إذ يوفر الكثير من عناصر HTML الدلالات والسلوك الداعم المطلوب بشكل مبني مسبقًا built-in. نعرض فيما يلي بعض الدلالات الأقل وضوحًا والتي تُعدّ هامة جدًا لمستخدمي قارئ الشاشة لا سيما مسائل التنقل في محتوى الصفحات. من السهولة معاينة صفحة مليئة بعناصر التحكم والعثور بسرعة على المطلوب إلا أنه سيكون من الأصعب قراءة محتوى الصفحات الزاخرة بالمحتوى مثل صفحات الويكيبيديا وصفحات الأخبار من الأعلى للأسفل، وهنا تبرز الحاجة الماسة لطرق تنقل في المحتوى بفعالية أكبر. يحمل مطوري الويب بعض الأفكار الخاطئة حول برامج قراءة الشاشة بأنها مملة وبطيئة وأن كل شيء موجود على الشاشة يجب أن يتمكن المستخدم من وضع التركيز عليه. هذا ليس هو الحال في الكثير من الأحيان. يعتمد مستخدمو قارئ الشاشة على قائمة الترويسات headings لتحديد موقع المعلومات في أغلب الأحيان. كما تمتلك معظم برامج قراءة الشاشة طرقًا سهلة لإيجاد قائمة ترويسات الصفحة وهي ميزة هامة تُدعى الدوّار rotor. نعرض فيما يلي كيفية استخدام العناوين في HTML بشكل فعّال لدعم هذه الميزة. استخدام العناوين بفعالية نُعيد التذكير أولًا بأن ترتيب العناصر في شجرة DOM أمرًا هامًا سواءً بالنسبة لترتيب التركيز focus أو بالنسبة لترتيب قارئ الشاشة. يُمكن باستخدام برامج قراءة الشاشة مثل VoiceOver و NVDA و JAWS و ChromeVox استنتاج أن قائمة الترويسات headings list تتبع ترتيب شجرة DOM وليس ترتيب ظهورها على الشاشة. يعود هذا الأمر إلى أن قارئ الشاشة يتعامل مع شجرة الوصول accessibility tree والتي هي بدورها ناتجة عن شجرة DOM أي أن الترتيب المُعتمد من قبل قارئ الشاشة هو ترتيب شجرة DOM في نهاية المطاف. يدّل ذلك على أن تنظيم بنية الترويسات المناسبة أصيح أمرًا هامًا وضروريًا أكثر مما مضى. توضع مستويات الترويسات heading levels، في الصفحات المُنظّمة جيدًا، بشكل هرمي لضمان علاقة أب/ابن بين أقسام المحتوى. تُشير توصيات WebAIM إلى هذه التقنية مرارًا وتكرارًا. نعرض فيما يلي بعض الروابط التي تُشير إلى: استخدام الترميز الدلالي لتعيين العناوين 1.3.1. استخدام العناوين كتقنية لتجاوز كتل المحتوى 2.4.1. بعض التفاصيل حول كتابة عناوين مفيدة 2.4.6. تحديد الأقسام الفردية من المحتوى باستخدام العناوين عندما يكون ذلك مناسبًا 2.4.10 ليس بالضرورة أن تكون كل العناوين مرئية على الشاشة. مثلًا: تضع صفحات الويكيبيديا بعض العناوين عمدًا خارج الشاشة وتُتيحهم فقط لقارئات الشاشة وللتقنيات المساعدة الأخرى. <style> .sr-only { position:absolute; left:-10000px; top:auto; width:1px; height:1px; overflow:hidden; } </style> <h2 class="sr-only">This heading is offscreen.</h2> يُمكن العودة لمقالة المنظمة WebAIM حول المحتوى خارج الشاشة للمزيد من المعلومات. يُمكن، في بعض التطبيقات المعقدة، أن تكون هذه الطريقة جيدة لاستيعاب العناوين عندما لا تتسع المساحة المتاحة لعرضها أو لا يكون لها دورًا هامًا في سياق العرض الحالي. تحذير: من المهم عدم المبالغة في استخدام هذه الطريقة (إخفاء العناوين). يجب الانتباه إلى أن مستخدمي التقنية المساعدة قد يكونوا قادرين أيضًا على رؤية الشاشة بأنفسهم، لذا فإن التركيز فقط على إنشاء محتوى يُناسب "قارئ الشاشة فقط" قد يؤدي في الواقع إلى تدهور تجربة الاستخدام لبعض المستخدمين، كما يُمكن أن يؤدي لكثير من الإرباك أثناء صيانة التطبيق من قبل المطور. خيارات التنقل الأخرى يوجد العديد من العناصر الأخرى التي يُمكن استخدامها للتنقل في الصفحة بما فيها الروابط links وعناصر النموذج controls والعلامات landmarks إضافًة إلى الترويسات. يُمكن لقارئ الصفحة استخدام ميزة الدوّار rotor (طريقة سهلة لتحديد قائمة الترويسات في صفحة) للوصول إلى قائمة روابط في الصفحة. قد تحتوي الصفحة، مثل صفحات الويكيبيديا، على الكثير من الروابط التي غالبًا ما يبحث المستخدم عن تعريف لمصطلح ما ضمنها. يؤدي هذا إلى الاقتصار على زيارة الروابط التي تحوي المصطلح بدلًا من كل ظهور لمصطلح على الصفحة. يُمكن أن تكون هذه الميزة مفيدة في حال عثور قارئ الشاشة على الرابط وكان لنص الرابط معنى واضح. نعرض فيما يلي بعض الممارسات السيئة والتي تُعطي روابط صعبة الوصول: استخدام الروابط <a> دون تحديد قيمة للخاصية href (لاسيما في التطبيقات ذات صفحة واحدة) مما يوقع قارئات الشاشة في مشاكل عديدة. استخدام الأزرار مع الروابط مما يجعل قارئ الشاشة يُعامل المحتوى كرابط ويُهمل وظيفة الزر. يجب في مثل هذه الحالة استبدال الرابط بزر حقيقي واستخدام تنسيق مناسب له. استخدام الصور كمحتوى للروابط. يُمكن في بعض الأحيان أن تكون مثل هذه الصور غير قابلة للاستخدام من قبل قارئ الشاشة. لضمان وصول التقنية المساعدة للرابط يجب التأكد من وضع الخاصية alt للصورة. يجب الانتباه لتوفير نصًا واضحًا يُعطي معلومات مفيدة حول المكان الذي ينقلنا الرابط إليه. مثلًا لا يوفر النص "معرفة المزيد" أو "انقر هنا" أي معلومات دلالية، على خلاف النصوص مثل "تعرّف على المزيد حول التصميم سريع الاستجابة" أو "مشاهدة البرنامج التعليمي" والتي تُساعد برامج قراءة الشاشة في توفير سياق مفيد حول الروابط. يُمكن للدوّار أيضًا استرداد قائمة التحكم في النموذج والتي تُمكّن القراء من البحث عن عناصر مُحدّدة والانتقال إليها مباشرًة. ترتكب برامج قارئات الشاشة العديد من أخطاء التهجئة أو النطق مثل قراءة رقم الهاتف كعدد صحيح كبير أو قراءة النص المكتوب بحروف كبيرة كاختصار أو قراءة اسم "Hsoub" مثل "saub". من حسن الحظ، يعتاد المستخدمون على مثل هذه الأخطاء ويألفونها ويأخذوها بالحسبان. يحاول بعض المطورين توفير النص مُهجًأ صوتيًا لقارئ الشاشة. مثلًا: لنأخذ التهجئة البسيطة التالية: "don't do it" والتي ستُفاقم المشكلة! مثلًا إذا كان المستخدم يستخدم شاشة برايل للعرض فستُكتب الكلمات بشكل غير صحيح مما سيؤدي لمزيد من الالتباس. بما أن قارئات الشاشة تقرأ الكلمات بصوت مرتفع يُمكن ترك الأمر لقارئ النص للتحكم في تجربته وقرار متى يكون ذلك ضروريًا. يُمكن للقارئ استخدام الدوّار rotor لمعاينة قائمة العلامات landmarks list لمساعدته في إيجاد المحتوى الأساسي ومجموعة علامات التنقل navigational landmarks التي توفرها عناصر العلامات في HTML. توفر HTML5 مجموعة من العناصر الجديدة التي يُمكن لها أن تُساعد في تحديد البنية الدلالية للصفحة بما فيها الترويسة header والتذييل footer والتنقل nav والمقال article والقسم section والجزء الرئيسي main والجزء الجانبي aside. توفر هذه العناصر أدلة على هيكلية الصفحة دون فرض أي تصميم مبني مسبقًا (وهو ما يجب فعله باستخدام CSS). تحل هذه العناصر الهيكلية الدلالية مكان كتل div المتعدّدة والمتكررة وتوفر طريقة وصفية أوضح للتعبير عن بنية الصفحة بشكل حدسي سواء للمطورين أو للمتصفحين. ترجمة -وبتصرف- لمجموعة المقالات: Introduction to Semantics The Accessibility Tree Text Alternatives for Images Semantics and Navigating Content للمؤلفين: Meggin Kearney وDave Gash وAlice Boxhall. اقرأ أيضًا تعلم البرمجة المدخل الشامل لتعلم علوم الحاسوب
  8. نُناقش مسألة التركيز Focus في القسم الأول من هذه المقالة التعليمية، كما نشرح آلية ترتيب عناصر شجرة DOM في القسم الثاني منها، ونُنهي المقالة بشرح استخدام فهرس الجدولة tabindex. مقدمة في التركيز نبدأ أولًا بشرح عملية التحكم بالتركيز على العناصر وآليات إدارته، وحيث يختص التركيز بعناصر التحكم (عناصر الإدخال مثل الحقول وصناديق التحقق والأزرار والروابط) التي تتلقى حاليًا الدخل من لوحة المفاتيح أو من الحافظة عند إجراء عملية لصق. يُعدّ التركيز نقطة الانطلاق الأولى لتعلم مسائل إمكانية الوصول لاسيما أننا جميعًا نتعامل مع لوحة المفاتيح بسهولة، كما سيستفيد جميع المستخدمين من نتائج إعداد التركيز المناسب. قد يعتمد المستخدمون الذين يعانون من إعاقات حركية، والتي تتراوح من التواء الرسغ إلى الشلل الدائم، على لوحة مفاتيح أو جهاز تبديل للتنقل في صفحة الويب، مما يستلزم توفير تجربة استخدام جيدة ووضع استراتيجية تركيز ملائمة لهم. تُساهم عملية التنقل في الموقع بسرعة في رفع انتاجية المستخدمين لاسيما المحترفين منهم والذين يستخدمون اختصارات لوحة المفاتيح بسهولة. وبالمحصلة، تضمن استراتيجية التركيز المُنفذّة جيدًا تمتع كل مستخدم بتجربة أفضل. سنرى لاحقًا أن الجهد الذي قد يبذله المطور ضروري للسماح لذوي الإعاقات المختلفة باستخدام الأدوات المساعدة كما أنه يُساعد في إغناء تجارب تعامل كل المستخدمين مع صفحات الويب المطورة. ما هو التركيز يُحدّد التركيز عنصر التحكم الحالي الذي يتلقى الإدخالات من لوحة المفاتيح ويعرض الأحرف التي يكتبها المستخدم، كما يستقبل هذا العنصر بيانات أي عملية لصق من الحافظة. يُميّز العنصر الحاصل على التركيز بواسطة إطار لوني حوله يعتمد نمطه على المستعرض أو على أسلوب التنسيق المُحدّد من قبل مطور صفحة الويب. يقوم المستعرض Chrome مثلًا بوضع إطار أزرق، بينما يقوم Firefox بوضع إطار متقطع. يتعامل بعض المستخدمين مع حاسوبهم حصرًا باستخدام لوحة المفاتيح وبالتالي يكون التركيز بالنسبة لهم أمرًا هامًا جدًا فهو وسيلتهم للوصول إلى أي كائن في صفحة الويب. تنص قائمة التحقق من مجموعة إمكانية الوصول Web AIM (اختصارًا إلى Web Accessibility In Mind) في الفقرة السابقة منها على أن جميع وظائف صفحة الويب يجب أن تكون ممكنة الوصول عن طريق لوحة المفاتيح (إلا في حالات خاصة لا يُمكن القيام بها باستخدام لوحة المفاتيح كالرسم اليدوي مثلًا). يُمكن نقل التركيز من عنصر لآخر باستخدام مفتاح الجدولة Tab أو التركيب Shift+Tab أو باستخدام مفاتيح الأسهم. يختلف هذا الأسلوب قليلًا في نظام التشغيل Mac OSX. يسمح لك المتصفح Chrome بالتنقل باستخدام المفتاح Tab بينما يجب استخدام Option+Tab في المتصفح Safari. يُمكن تغيير إعدادات نقل التركيز باستخدام لوحة المفاتيح عن طريق نافذة لوحة المفاتيح من تفضيلات النظام: يُدعى ترتيب نقل التركيز من عنصر لآخر للأمام أو للخلف بترتيب الجدولة، وهو من الأمور التي يجب على مطور صفحة الويب أن يأخذها بالحسبان فيتحقق من وجود ترتيب انتقال ملائم بين العناصر في الصفحة. تعريف إمكانية التركيز يُمكن التركيز على أي عنصر من عناصر HTML التي تسمح للمستخدم بالتفاعل معها (تغيير قيمها أو حالتها) مثل صناديق النصوص والأزرار والقوائم، وتدخل تلقائيًا في ترتيب الجدولة وتتفاعل مع أحداث لوحة المفاتيح دون أي تدخل من قبل مطور الصفحة. أما العناصر التي لا يتفاعل المستخدم معها مثل الفقرات والحاويات div فلا ينتقل التركيز عليها ولا تدخل في ترتيب الجدولة. تجربة التركيز نعرض في المثال التالي تقانات التركيز التي شرحناها سابقًا: افتح المستعرض Chrome وانتقل للرابط airline site mockup page واطلب تذكرة سفر باستخدام لوحة المفاتيح حصرًا (لا تتفاعل الصفحة بأي حال مع الفأرة). عليك ملء المعلومات التالية: اتجاه واحد للسفر oneway إلى مدينة Arrival ملبورن Melbourne تاريخ المغادرة Depart Date 12/10/2017 تاريخ العودة Return Data 23/10/2017 مقعد جانب النافذة Preferred seat type لا تطلب تلقي إشعارات العروض الترويجية Receive promotional offers بمجرد أن تنتهي من إتمام عملية إدخال البيانات دون أي خطأ ومن ثم نقر زر البحث، سيتم مسح البيانات وإعادة تهيئة نموذج إدخال جديد (قم بالتجربة المطلوبة ومن ثم أتمم القراءة). لنعاين الآن كيف يتفاعل نموذج الصفحة السابق مع إدخالات لوحة المفاتيح. يؤدي الضغط على مفتاح الجدولة Tab في البداية إلى إضاءة الروابط أعلى النموذج (الرحلات Flights، الفنادق Hotels، سيارات الأجرة Rental Cars)، وإذا استمريت بضغط مفتاح الجدولة مرارًا ستنتقل إلى مجموعة أزرار الانتقاء والتي عليك الآن استخدام مفاتيح الأسهم للتنقل فيما بينها (نوع الرحلة). اخرج من مجموعة أزرار الانتقاء باستخدام مفتاح الجدولة Tab لتنتقل إلى حقل الاسم ومن ثم إلى حقل العنوان للكتابة فيهما. عند وصولك للقائمة المنسدلة للمدن، يُمكنك اختيار عنصر منها عن طريق مفاتيح الأسهم أو عن طريق البدء بكتابة الأحرف الأولى من اسم المدينة ليتم ملء الحقل بالمدينة المناسبة. عند وصولك لقائمة نوع الكرسي يُمكن اختيار عنصر من القائمة باستخدام مفاتيح الأسهم أو بكتابة أحد الأحرف w أو a أو n لتحديد عنصر القائمة الموافق (لا تفضيل No preference، ممر Aile، نافذة Window). يُمكنك إلغاء تحديد صندوق التحقق (لطلب عدم تلقي إشعارات الحملات الترويجية) بالضغط على مفتاح المسافة عند وصول التركيز لصندوق التحقق. ضع أخيرًا التركيز على زر البحث Search وانقر المفتاح Enter لإرسال بيانات النموذج. يبدو أن التفاعل مع النموذج باستخدام لوحة المفاتيح فقط أسهل وأسرع من استخدام الفأرة ولوحة المفاتيح معًا (وضياع الوقت بالتنقل بينهما)، وهو أمر سهل التحقيق من قبل المطور لأن ما عليه سوى استخدام عناصر HTML الأساسية ولن يحتاج إلى كتابة أي شيفرة إضافية لإدارة عمليات التركيز. أهمية ترتيب العناصر في شجرة DOM يؤدي استخدام عناصر HTML الأساسية إلى الحصول على ترتيب انتقال تركيز ملائم إذ أن ترتيب الانتقال بين العناصر يكون وفق ترتيبها في نموذج كائن الوثيقة DOM. يكون ترتيب انتقال التركيز بين الأزرار في المثال التالي وفق ترتيبها في DOM أي الزر "I Should" أولًا ومن ثم الزر "Be Focused" و أخيرًا الزر "Last": <button>I Should</button> <button>Be Focused</button> <button>Last!</button> يجب الانتباه إلى بعض المشاكل التي قد تنجم عند استخدام تنسيق CSS لاسيما إذا كان ذلك يؤثر على أمكنة تموضع العناصر. فمثلًا إذا وضعت الخاصية (عائم إلى اليمين) للزر الأول "float: right" فسيظهر هذا الزر أقصى اليمين إلا أن ترتيبه في الجدولة سيبقى الأول لأنه الأول في ترتيب العناصر ضمن DOM. بالتأكيد، سيُسبب هذا إرباكًا للمستخدم إذ سيقفز بزر الجدولة Tab من الزر أقصى اليمين "I Should" إلى الزر أقصى اليسار "Be Focused" بعده. <button style="float: right">I Should</button> <button>Be Focused</button> <button>Last!</button> تنص قائمة التحقق من مجموعة إمكانية الوصول Web AIM اختصارًا Web Accessibility In Mind على أن الترتيب المرئي (ترتيب القراءة) يجب أن يكون متوافقًا مع ترتيب الجدولة كي لا نُربك المستخدم الذي يعتمد على لوحة المفاتيح. المحتوى غير الظاهر يجب الانتباه إلى انتقال التركيز على عناصر غير ظاهرة مما يُربك المستخدم الذي سيشعر عندها بفقدان أو ضياع مؤشر التركيز وسيبدأ بضغط مفتاح الجدولة مرارًا ليكتشف أن مؤشر التركيز يظهر ويختفي! يُمكن أن تظهر هذه المشكلة مثلًا عند استخدام لوحة تنقل جانبية متجاوبة responsive side navigation panel وعندها يجب أن نمنع التركيز من الانتقال للوحة عندما لا تكون ظاهرة، ونسمح له بالانتقال لها فقط عندما يكون المستخدم في حالة تفاعل معها. قد يتوجب عليك أحيانًا البحث عن موضع التركيز باستخدام خاصية العنصر النشط document.activeElement لمعرفة العنصر النشط حاليًا، ومن ثم إظهاره باستخدام display: none أو visibility: hidden أو إخفائه باستخدام display: block أو visibility: visible حسب الحالة. نؤكد أخيرًا على ضرورة قيام المطور بتجريب عملية التنقل مرارًا وتكرارًا في صفحته المطورة قبل نشرها للعموم ليتأكد من عدم وجود أي خلل في التركيز كاختفائه أو وجود ترتيب غير ملائم، وفي حال وجود أي خلل فيُمكن تصحيحه عن طريق إعادة ترتيب العناصر في نموذج كائن الوثيقة DOM أو عن طريق إخفاء العناصر عندما تكون غير ظاهرة في الشاشة ومن ثم إظهارها عندما يتفاعل المستخدم معها. استخدام فهرس الجدولة tabindex يُلائم الترتيب الافتراضي للجدولة والذي يُحدّده ترتيب العناصر في شجرةDOM السلوك المنطقي المطلوب للانتقال، إلا أنه قد تتطلب صفحتك المطورة تغيير هذا الترتيب الافتراضي. يُمكن القيام بذلك بتغيير مكان توضع العنصر في الوثيقة إلا أن ذلك قد لا يكون ممكنًا في بعض الحالات مما يجعلنا نتوجه لاستخدام سمة فهرس الجدولة tabindex في HTML. يُمكن تحديد ترتيب الجدولة لأي عنصر باستخدام السمة tabindex والتي تكون قيمتها عدد صحيح يُحدّد ترتيب العنصر. علاوةً على ذلك، يُمكن استخدام هذه السمة لإضافة كائن لا يتمتع بإمكانية التركيز (مثل الحاويات div) إلى ترتيب الجدولة، كما يُمكن حذف عناصر من ترتيب الجدولة. يؤدي إسناد القيم 0 إلى السمة tabindex لإضافة العنصر إلى الترتيب الطبيعي للجدولة أي يُمكن الانتقال له باستخدام مفتاح الجدولة Tab كما يُمكن وضع التركيز عليه برمجيًا باستخدام الطريقة focus. يُظهر المثال التالي عنصر مخصص يُمكن الانتقال له باستخدام المفتاح Tab: <custom-button tabindex="0">Press Tab to Focus Me!</custom-button> يؤدي إسناد القيم -1 إلى السمة tabindex إلى إزالة العنصر من الترتيب الطبيعي للجدولة أي لن يعود بالإمكان الانتقال له بمفتاح الجدولة Tab إلا أنه يُمكن وضع التركيز عليه برمجيًا باستخدام الطريقة focus. <button id="foo" tabindex="-1">I'm not keyboard focusable</button> <button onclick="foo.focus();">Focus my sibling</button> لا يُمكن الآن الانتقال للزر الأيسر بمفتاح الجدولة Tab وإنما يُمكن وضع التركيز عليه بالنقر على الزر الأيمن: يؤدي إسناد قيمة صحيحة أكبر من الصفر (مثلًا 5) إلى وضع العنصر الموافق في مقدمة ترتيب الجدولة. أما إذا وجد أكثر من عنصر لهم قيمة أكبر من الصفر فيتم الانتقال بدءًا من العنصر ذو القيمة الأدنى (أكبر من الصفر) إلى القيمة الأعلى التالية وهكذا. تُعدّ عملية إسناد قيم أكبر من الصفر مخالفة للنموذج النمطي الطبيعي anti-pattern. ليكن لدينا مثلًا: <button>I should be first</button> <button tabindex="6">And I should be second</button> <button tabindex="5">But I jumped to the front!</button> سيكون الزر الأخير (الأيمن) أول الأزرار في الجدولة ومن ثم الزر الثاني(الأوسط) وأخيرًا الزر الأول. بالطبع، لا معنى لتغيير ترتيب الجدولة للعناصر التي لا يتفاعل معها المستخدم مثل العناوين والصور. كما أنه من المستحسن تنظيم ترتيب الجدولة وفق ترتيب العناصر في DOM وعدم اللجوء لاستخدام tabindex إلا عند الضرورة القصوى مقتصرًا على عناصر التحكم التفاعلية مثل صناديق النص والأزرار والقوائم. لا تقلق بشأن مستخدمي قارئ الشاشة أن يفوتهم محتوى هامًا بسبب أن ليس له ترتيب جدولة tabindex، فحتى لو كان المحتوى مهمًا مثل الصور لا داع أن تضع له ترتيب جدولة لأن المستخدم لن يتفاعل معها أصلًا. ركّز على وضع عبارات توصيفية للصور في الخاصية Alt كي يستفيد منها مستخدمي قارئ الشاشة كما سنعرض لاحقًا. إدارة التركيز على مستوى الصفحة يُمكن أن يكون استخدام السمة ضروريًا في بعض الحالات. مثلًا: إذا احتوت صفحة على عدة أقسام لا تُعرض جميعها بنفس الوقت أي أن النقر على رابط قد يؤدي إلى تغيير المحتوى الظاهر دون إعادة تحميل الصفحة. يجب إذًا في هذه الحالة إسناد القيمة -1 لترتيب الجدولة لكل قسم كي لا ننتقل بمفتاح الجدولة Tab للقسم وإنما حصرًا باستخدام الطريقة focus. يحافظ هذا الأسلوب، المدعو بإدارة التركيز، على تزامن المحتوى المرئي مع سياق تفاعل المستخدم مع الصفحة. إدارة التركيز للعناصر المخصصة يُمكن أيضًا أن تحتاج لإدارة التركيز للتحكم بعنصر مخصص. مثلًا نعلم أنه يُمكن استخدام مفاتيح الأسهم لتحديد خيار من عنصر قائمة الاختيار الأساسية select. إذا أنشأت عنصرًا مخصصًا مشابه للقائمة، فسترغب بأن يكون له نفس أسلوب الاختيار لتراعي مستخدمي لوحة المفاتيح والذين يستخدمون الأسهم لاختيار عنصر من القائمة. نقوم في الشيفرة التالية باستخدام عنصر القائمة الأساسي مما يُعطي قائمة يُمكن التنقل والاختيار منها باستعمال مفاتيح الأسهم: <!-- يُمكن نقل التركيز باستخدام مفتاح الجدولة أو مفاتيح الأسهم--> <select> <option>Aisle seat</option> <option>Window seat</option> <option>No preference</option> </select> Aisle seat Window seat No preference قد لا يكون من السهل دائمًا معرفة سلوك لوحة المفاتيح الواجب تنفيذها مما يتوجب العودة إلى دليل تطوير تطبيقات الإنترنت الغنية سهلة الوصول Accessible Rich Internet Applications Authoring Practices التي تختصر إلى ARIA، لمعاينة قائمة أنواع عناصر التحكم وأحداث لوحة المفاتيح التي تتجاوب معها. نستخدم هذا الدليل لدعم لوحة المفاتيح في عنصر مخصص جديد. ليكن مثلًا العنصر المخصص Custom Elements الجديد التالي والذي يُماثل مجموعة أزرار الانتقاء إنما يُمكن أن نُخصص له مظهرًا وسلوكًا جديدين: <radio-group> <radio-button>Water</radio-button> <radio-button>Coffee</radio-button> <radio-button>Tea</radio-button> <radio-button>Cola</radio-button> <radio-button>Ginger Ale</radio-button> </radio-group> بمراجعة القسم الثاني من دليل ARIA لتحديد نوع التفاعل مع لوحة المفاتيح المطلوب، نجد ضمن قائمة نماذج التصميم المقترحة جدول مواصفات مجموعة أزرار الانتقاء characteristics table for radio groups والتي تُعدّ الأقرب للعنصر المخصص الذي نقوم بتطويره. يُبين هذا الجدول ضرورة تجاوب العنصر مع مفاتيح الأسهم أعلى/أسفل/يمين/يسار. لإضافة هذا التفاعل مع العنصر المخصص الجديد سنستخدم تقنية تُدعى ترتيب الجدولة المتنقل roving tabindex. تتمحور تقنية ترتيب الجدولة المتنقل على إسناد القيمة -1 لجميع العناصر ما عدا العنصر المُحدّد حاليًا: <radio-group> <radio-button tabindex="0">Water</radio-button> <radio-button tabindex="-1">Coffee</radio-button> <radio-button tabindex="-1">Tea</radio-button> <radio-button tabindex="-1">Cola</radio-button> <radio-button tabindex="-1">Ginger Ale</radio-button> </radio-group> يستخدم العنصر المخصص الجديد مستمع listener لمعرفة المفتاح الذي يضغطه المستخدم وعندها تُسندّ قيمة ترتيب الجدولة -1 إلى العنصر المُحدّد حاليًا ومن ثم تُسندّ القيمة 0 لترتيب الجدولة للعنصر الجديد الذي سيُحدّد باستخدام الطريقة focus. <radio-group> // Assuming the user pressed the down arrow, we'll focus the next available child <radio-button tabindex="-1">Water</radio-button> <radio-button tabindex="0">Coffee</radio-button> // call .focus() on this element <radio-button tabindex="-1">Tea</radio-button> <radio-button tabindex="-1">Cola</radio-button> <radio-button tabindex="-1">Ginger Ale</radio-button> </radio-group> عندما يصل المستخدم إلى الخيار الأخير (أو الأول، وفق الاتجاه الذي يتحرك به باستخدام مفاتيح الأسهم)، نعاود وضع التركيز على الخيار الأول (أو الأخير). يُمكن العودة إلى مستودع GitHub للحصول على الشيفرة المتاح للعموم وتجريب العنصر الجديد المخصص. النوافذ الشرطية وقفل لوحة المفاتيح يُمكن في بعض الأحيان، أثناء إدارة التركيز، الوقوع في ورطة لا يُمكن الخروج منها. من الأمثلة على ذلك حالة عنصر الإكمال التلقائي autocomplete الذي يحاول إدارة التركيز ومتابعة ترتيب الجدولة إلا أنه يمنع التركيز من الخروج منه حتى إكمال النص مما يؤدي إلى حجز لوحة المفاتيح والذي يُمكن أن يضايق المستخدم إلى حد كبير. تنص الفقرة 2.1.2 من دليل ARIA على أنه لا يجوز بأي حال من الأحوال حجز لوحة المفاتيح أو حصر التركيز في عنصر ما إذ أن المستخدم يجب أن يكون قادرًا على التنقل ضمن جميع عناصر الصفحة بحرية كاملة. يُمكن أن يكون هذا السلوك مطلوبًا في بعض الحالات مثل حالة النافذة الشرطية modal التي تمنع المستخدم من الوصول لعناصر الصفحة الخلفية. يُمكن وضع غطاء داكن شفاف لتغطية الصفحة الخلفية إلا أن ذلك لن يمنع خروج التركيز خارج النافذة الشرطية بشكل أكيد. يُمكن في مثل هذه الحالات تنفيذ حجز مؤقت للوحة المفاتيح لحصر التركيز ضمن النافذة الشرطية عندما تكون مفتوحة ومن ثم العودة للعنصر الذي كان عليه التركيز في الصفحة الخلفية عند إغلاق النافذة الشرطية. يوجد العديد من المقترحات لتسهيل المعالجة السابقة مثل استخدام العنصر الجديد <dialog> إلا أنه مازال غير متوافق مع بعض المتصفحات. يُمكن العودة للمقالة للتزود بمعلومات أكثر عن <dialog> كما يُمكن تفحص هذا المثال من مستودع GitHub. نعرض فيما يلي الخطوات الأساسية اللازمة لتنفيذ قفل لوحة المفاتيح بشكل مؤقت وذلك لحالة نافذة شرطية (حاوية div لبعض العناصر) مع حاوية ثانية للغطاء الداكن. استخدم التابع document.querySelector لحفظ مرجع لحاوية النافذة الشرطية ومرجع للحاوية الثانية. احفظ فور فتح النافذة الشرطية مرجع للعنصر الذي كان عليه التركيز في النافذة الخلفية (وبهذا ستتمكن من الرجوع إليه لاحقًا). استخدم مستمع لحدث ضغط مفتاح للأسفل keydown. يجب أيضًا استخدام مستمع لحدث النقر على الحاوية الثانية. احصر مجموعة العناصر القابلة للتركيز في النافذة الشرطية إذ سيلعب العنصر الأول والأخير دور الحارس للدوران ضمن مجموعة العناصر مع مفتاح الجدولة لضمان البقاء ضمن النافذة الشرطية. أظهر النافذة الشرطية وضع التركيز على أول عنصر فيها. انقل التركيز من عنصر لآخر للأمام عندما يقوم المستخدم بضغط مفتاح الجدولة Tab أو للخلف عندما يقوم المستخدم بضغط Shift+Tab مع مراعاة الدوران عند أول وآخر عنصر. أغلق النموذج حال ضغط المستخدم لمفتاح الهروب Esc (هذا أمر ضروري يسمح للمستخدم بإغلاق النافذة دون البحث عن زر الإغلاق ومفيد بالأخص للمستخدمين الذين لا يستعملون الفأرة). أخف النافذة الشرطية وحاوية الغطاء عند طلب الإغلاق وأعد التركيز على عنصر الصفحة الذي قمت بحفظ مرجع له. يُمكن، باتباع الخطوات السابقة، إدارة نافذة شرطية مريحة التعامل لجميع فئات المستخدمين. يُمكن الحصول على الشيفرة المتاح للجميع واختبار المثال كاملًا. ترجمة -وبتصرف- للمقالات الثلاث التالية: Introduction to Focus DOM Order Matters Using tabindex للمؤلفين الثلاثة: Meggin Kearney, Dave Gash, Rob Dodson. اقرأ أيضًا المقال السابق: سهولة وصول جميع الزوار لمواقع وتطبيقات الويب استخدام Vue.js للتعامل معDOM اكتشاف دعم المتصفحات لميزات HTML5 تعلم البرمجة المدخل الشامل لتعلم علوم الحاسوب p iframe { border: 1px solid #e7e5e3 !important; }
  9. نعرض في هذه المقالة أساسيات ومبادئ سهولة الوصول accessibility وذلك بالاعتماد على محتوى إحدى الدورات التدريبية في Udacity وفق الترتيب التالي: تعريف مسألة سهولة الوصول لاسيما في مسائل تطوير الويب. آليات تحقيق سهولة الوصول وجعل مواقع الويب شاملة الاستخدام usable من قبل الجميع. كيفية تحقيق سهولة الوصول بأقل جهد ممكن خلال مرحلة التطوير. استخدام الميزات التي توفرها لغة HTML لتحسين سهولة الوصول. عرض الآليات المتقدمة التي تسمح بالوصول لتفاعل سلس للمستخدمين مع صفحات الويب. تعرض هذه المقالة جميع الآليات المتاحة لتطوير مواقع ويب سهلة الاستخدام من قبل الجميع مما سيرفع، بالتأكيد، من مستواك في التطوير. لن يكون الأمر صعبًا إذ يُمكنك القيام ببعض الممارسات البسيطة أثناء استخدامك لغة HTML لتحسين سهولة الوصول (إضافًة لبعض التقانات المتقدمة والتي سنعرضها في هذه المقالة). ستصل، باتباعك للإرشادات المُقدّمة إلى واجهات سهلة الاستخدام لجميع شرائح المستخدمين بما فيهم ذوي الاحتياجات الخاصة. قد يوجد لدى الكثير من المطورين تصور أو فهم خاطئ لما يعنيه سهولة الوصول (شيء له علاقة بالعقود الحكومية أو صناديق التحقق أو قارئات الشاشات؟)، أو لربما يظن البعض أن سيكون عليهم الاختيار بين واجهات جميلة وجذابة وأخرى غير أنيقة إنما سهلة الوصول. بما أن الأمر ليس ذلك على الإطلاق فلنبدأ أولًا بشرح ماذا نقصد بسهولة الوصول وما سنتعلم في هذه المقالة. ما هي سهولة الوصول؟ نصف، عادًة، موقع ويب بأنه سهل الوصول عندما يتمكن جميع المستخدمين بمختلف شرائحهم من الوصول إليه واستثمار وظائفه بسهولة. من البساطة لك كمطور ويب أن تفترض أن جميع المستخدمين قادرين على التفاعل والتعامل مع موقعك بنفس الطريقة التي تقوم بها أنت بنفسك وذلك باستخدام لوحة المفاتيح أو الفأرة أو شاشة اللمس. يُمكن أن تكون فرضيتك هذه صحيحة مع الكثير من المستخدمين إلا أنه قد تبرز في العديد من الحالات مشاكل في التعامل مع الموقع تتراوح من المضايقات البسيطة إلى التوقف عن التصفح والاستخدام. يُعنى تسهيل الوصول بتفقد تجربة المستخدمين غير النمطيين والذين يُمكن أن يكون لهم أسلوب مختلف وغير متوقع في التواصل والتعامل مع محتوى الويب لاسيما الأشخاص الذين يعانون من مشاكل معينة أو إعاقات مختلفة. لا يعني تركيزنا في مسألة سهولة الوصول على الأشخاص الذين يعانون من إعاقات جسدية إهمال مشاكل الوصول التي قد تظهر عند أي مستخدم. فمن منا لم يعان من مشاكل في استخدام التطبيقات على جواله المحمول أو لم يتمكن من الوصول لقائمة معينة على حاسوبه اللوحي أو تفاجأ بظهور عبارة "هذا المحتوى غير متاح لك في منطقتك"؟ تُساعد معالجة مسائل سهولة الوصول بهذا المعنى الأوسع والأعم على تحسين تجربة جميع المستخدمين في التعامل مع المنتج البرمجي كما سنرى لاحقًا. لنبدأ مثلًا بإلقاء نظرة على المثال التالي: من الواضح أن الواجهة السابقة تعاني من المشاكل التالية: للنص المكتوب تباين منخفض وبالتالي سيصعُب على ضعاف النظر قراءته. يوجد مسافات كبيرة بين العناوين (على اليسار) والحقول (على اليمين) مما سيجعل عملية الربط بينهما شاقة لاسيما لمن يتعامل مع هذا النموذج على الجوال ويقوم بتكبير النموذج لمعاينته مما سيُضطره أيضًا للتحريك يمنى ويسرى. لا يرتبط عنوان صندوق التحقق معه (تذّكر التفاصيل Remember details) وبالتالي سيكون على المستخدم النقر حصرًا على صندوق التحقق الصغير عوضًا عن النقر على العنوان. علاوًة على ذلك، لن يتمكن المستخدم الكفيف مثلًا والذي يستخدم قارئ شاشة من الربط بين العنوان وصندوق التحقق. إذا أصلحنا هذه المشاكل كلها بتطبيق حلول سهولة الوصول التالية: زيادة تباين النصوص بجعله أغمق وتعديل التصميم لتقريب التسميات من حقولها وربط عنوان خانة الاختيار معها، نحصل على النموذج المُحسّن التالي: إذا كنت تفضل النموذج الثاني على الأول فأنت، بالتأكيد، فهمت الهدف من هذا الدليل الإرشادي! وتذكر أنه غالبًا ما يكون الأمر الذي يمنع البعض من الوصول والتعامل مع منتجك يُسبب إرباكًا كبيرًا للآخرين. إرشادات تطوير محتوى الويب سهل الوصول نتبع في هذا الدليل الإرشادي الإرشادات التي يُقدّمها الدليل Web Content Accessibility Guidelines (WCAG) 2.0 وهي مجموعة من التوصيات والممارسات الجيدة التي وضعها خبراء سهولة الوصول لحل مسائل "سهولة الوصول" بطريقة منهجية. يتمحور الدليل الإرشادي WCAG حول أربعة مبادئ يُطلق عليها غالبًا الاختصار POUR الأجنبي: قابل للإدراك Perceivable: هل يُمكن للمستخدم إدراك المحتوى؟ مع الآخذ بعين الاعتبار أن ما نُدركه بحاسة واحدة مثل البصر لا يعني، بالضرورة، أن جميع المستخدمين يُمكن لهم أن يدركوه. قابل للتشغيل Operable: هل يتمكن المستخدم من التعامل مع الواجهات والتنقل عبر المحتوى؟ مثلًا إذا كان أمرًا في الواجهة يتطلب تحريك الفأرة فلن يتمكن الأشخاص الذين لا يقدرون على استخدام الفأرة أو شاشات اللمس من القيام به. مفهوم Understandable: هل يفهم المستخدم الواجهات دون أي غموض أو التباس؟ مرن Robust: هل يُمكن عرض المحتوى على متصفحات مختلفة وباستخدام أدوات مساعده؟ نظرًا لضخامة الإرشادات العامة التي يُقدّمها دليل WCAG، قامت مجموعة سهولة الوصول على الويب WebAIM (اختصارًا إلى Web Accessibility In Mind) باستخلاص مجموعة الإرشادات الخاصة بمحتوى الويب ووضعها في قائمة واحدة تُعطي المختصر المفيد مع تسهيل العودة لتفصيلات WCAG إن لزم الأمر. سيُحقق التزامك بهذه الإرشادات تطوير محتوى ويب يتمتع بسهولةالوصول من قبل كافة شرائح المستخدمين. فهم تنوع المستخدمين من المفيد البدء أولًا بفهم احتياجات المستخدمين بمختلف شرائحهم والمشاكل التي قد تواجه البعض منهم عند تعاملهم مع محتوى الويب. للمزيد من التوضيح نعرض فيما يلي جلسة أسئلة/أجوبة مفيدة مع فيكتور تسارن Victor Tsaran مدير البرنامج الفني في شركة Google وهو شخص كفيف لا يرى شيئًا على الإطلاق. السؤال: ما هي طبيعة عملك في Google؟ الجواب: أعمل على التأكد من أن منتجات Google تلائم جميع المستخدمين بما فيهم ذوي الإعاقات مهما كان نوعها. السؤال: ما هي أنواع الإعاقات التي يعاني المستخدمون منها بشكل عام؟ الجواب: يأتي فقدان البصر في مقدمة الإعاقات التي قد تجعل من الصعوبة، وأحيانًا من المستحيل، التفاعل مع الكثير من مواقع الويب لاسيما أن العديد من تقانات الويب المتقدمة لا تأخذ بعين الاعتبار الأدوات المختلفة التي يستخدمها المكفوفون في تصفح محتوى الويب. وبشكل عام يُمكن تقسيم إعاقات المستخدمين إلى أربع مجموعات: البصرية والحركية والسمعية والإدراكية. السؤال: لنعرض هذه الإعاقات واحدًة تلو الأخرى ولنبدأ أولًا ببعض الأمثلة عن الإعاقة البصرية الجواب: تُقسم الإعاقات البصرية إلى عدة فئات أولها المستخدمون الكفيفون، مثلي، والذين يتوجب عليهم استخدام قارئ شاشة أو قارئ برايل أو تركيبة من الاثنين. إضافًة إلى الكفيفين الذين لا يرون على الإطلاق، هنالك عدد كبير من المستخدمين الذين يعانون من ضعف البصر والذين يكون لهم في الغالب نظارات طبية سميكة. يوجد الكثير من الحلول لهؤلاء الأشخاص مثل استخدام قارئ الشاشة أو قارئ برايل أو استخدام تقنية تحويل النص إلى كلام، كما يُمكن لهم تكبير الشاشة وزيادة التباين وتكبير حجم الخطوط وغيرها من التقانات المساعدة والتي ربما يقوم بها الأشخاص العاديون للحصول على مظهر أريح للعين. ومع ملاحظة أن النظر يضعف مع تقدم العمر وأنه في لحظات معينة يُمكن لأي شخص أن تحصل له مشاكل في البصر (بعد عملية ليزر جراحية في العين مثلاً أو بوجود أشعة الشمس القوية على شاطئ البحر مثلًا). أطلب من مطوري الويب أخذ هذا الأمر بعين العطف لمراعاة أصحاب هذه الإعاقات. كما أُذكّر أيضًا بأن 9% من الذكور و1% من الإناث لديهم مشاكل بصرية في تمييز الألوان (الأحمر مع الأخضر مثلًا أو الأصفر مع الأزرق) مما يتطلب من مطوري نماذج الويب مراعاة ذلك أيضًا. السؤال: ماذا عن الإعاقات الحركية؟ الجواب: يُمكن أن تتراوح الإعاقات الحركية من أشخاص لا يستطيعون الحركة أبدًا إلى أشخاص لا يُمكنهم تحريك الفأرة لسبب أو لآخر مثل الأشخاص الذين يعانون من إصابات الإجهاد المتكرر RSI مثلًا. يُمكن حسب درجة الإعاقة الحركية التفاعل مع الحاسوب باستخدام لوحة المفاتيح أو جهاز تبديل أو حتى جهاز متابعة العين كما يُمكن لمثل هؤلاء الأشخاص إعطاء الأوامر للحاسوب صوتيًا. على غرار ضعف البصر، يُمكن أن تكون الإعاقة الحركية مؤقتة كأن تكون راكبًا في قطار يهتز بقوة أو أنك كسرت يدك أو غيرها من الحالات. وبجميع الأحوال، يجب أن نلبي احتياجات ذوي الإعاقات الدائمة والمؤقتة. السؤال: لنتكلم الآن عن الإعاقات السمعية. الجواب: يتراوح الأشخاص في هذه المجموعة أيضًا من الأصم إلى ضعيف السمع. وعلى غرار البصر، يضعُف السمع مع التقدم بالعمر ويلجأ الأشخاص عادًة إلى وسائل تقوية السمع. يجب، أثناء تطوير الواجهات، التأكد من أننا لا نعتمد على الصوت فقط بل نوفر بدائل مثل التعليقات والنصوص. وكما هو الحال مع الإعاقات البصرية والحركية، فمن السهل حقًا تخيل موقف يستفيد فيه شخص تعمل أذنيه جيدًا من هذه البدائل أيضًا. ففي مكتب عمل يتشاركه العديدون، يُمكن حضور فلم فيديو بدون صوت إذا كان الحوار مكتوبًا على الفلم. السؤال: هل يُمكن أن تخبرنا قليلًا عن الإعاقات الإدراكية؟ الجواب: تختلف هذه الإعاقات حسب الحالة فهنالك حالات عسر القراءة وحالات التوحد وحالات التشتت وغيرها من الحالات التي يتطلب معظمها إيجاد تصميمات بسيطة لواجهات التفاعل مع المستخدم. بالطبع، يُمكن لهؤلاء الأشخاص استخدام وظائف التكبير/التصغير لتسهيل القراءة أو زيادة التركيز. بالتأكيد، إذا ابتكرنا ما يُمكن أن يجده الشخص الذي يعاني من ضعف إدراكي مفيدًا فسيكون ممتعًا ومفيدًا لجميع الآخرين. السؤال: هل يُمكن لك في النهاية تلخيص وجهة نظرك بخصوص سهولةالوصول؟ الجواب: إن تصميم وبناء منتجات تتوجه فقط للأشخاص الذين لا يعانون من أي مشكلة سيجعل جمهور هذا المنتج ضيقًا جدًا نظرًا لوجود الإعاقات، ولو بشكل جزئي أو مؤقت، عمليًا عند الجميع. قام فيكتور خلال هذه المقابلة بتصنيف الإعاقات ضمن أربع مجموعات: البصرية والحركية والسمعية والإدراكية، كما نوه إلى أن هذه الإعاقات يُمكن أن تكون ظرفية أو مؤقتة أو دائمة. يُعطي الجدول التالي بعض الأمثلة الواقعية لمختلف أنواع الإعاقات (لاحظ أن بعض الإعاقات قد تندرج في أكثر من تصنيف): 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; } ظرفية مؤقتة دائمة بصرية ارتجاج في المخ العمى حركية حمل طفل ذراع مكسور، إصابات الإجهاد المتكررة إصابات الإجهاد المتكررة سمعية ضجيج مكتب إدراكية ارتجاج في المخ من الأمثلة على إصابات الإجهاد المتكررة: متلازمة النفق الرسغي carpal tunnel syndrome، مرفق التنس tennis elbow، إصبع الزناد trigger finger. الخطوات الموالية عرضنا في هذه المقالة أساسيات سهولة الوصول: تعريف مسألة سهولة الوصول ومدى أهميتها. قوائم إرشادات تحقيق سهولة الوصول WCAG و WebAIM. الأنواع المختلفة من الإعاقات التي يجب مراعاتها أثناء تصميم محتوى الويب. نعرض في بقية هذا الدليل الإرشادي الجوانب العملية للوصول إلى موقع ويب ذو سهولة وصول جيدة وحيث سنُركّز العمل حول ثلاثة جوانب أساسية: التركيز Focus: نعرض كيفية الاستعاضة عن الفأرة بلوحة المفاتيح مما يسمح للمستخدمين الذين يعانون من إعاقات حركية من التعامل مع الواجهات المختلفة كما يضمن هذا الأمر تحقيق واجهات سهلة الاستخدام ولجميع المستخدمين. الدلالات Semantics: يجب أن نضمن أن واجهاتنا المطورة تعمل بشكل جيد مع أدوات الوصول المختلفة التي يُمكن أن تُستخدم من قبل ذوي الإعاقات. التصميم Styling: يجب أن يُحقق تصميم الوجهات سهولة الاستخدام إلى أكبر قدر ممكن عبر استخدام التقانات المناسبة. يحتاج كل محور من هذه المحاور إلى دورة تدريبية كاملة لذا لن نغطي كل المواضيع بشكل كامل وإنما سنزودك بنقاط البدء ونوجهك إلى العديد من المراجع المفيدة. ترجمة -وبتصرف- للمقال Accessibility للكتّاب الأربعة: Meggin Kearney, Dave Gash, Alice Boxhall, Rob Dodson. اقرأ أيضًا تعلم البرمجة المدخل الشامل لتعلم علوم الحاسوب نظرة عامة عن تطويرتطبيقات الأجهزة المحمولة عبر كوردوفا النسخة الكاملة لكتاب نحو فهم أعمق لتقنيات HTML5
  10. يُعَدّ التعلم المُعزز مجالًا فرعيًا من فروع نظرية التحكم، والتي تُعنى بالتحكم في الأنظمة التي تتغير بمرور الوقت؛ وتشمل عِدّة تطبيقاتٍ من بينها: السيارات ذاتية القيادة، وبعض أنواع الروبوتات مثل الروبوتات المُخصصة للألعاب. وسنستخدم في هذا المقال التعلم المعزز لبناء روبوتٍ لألعاب الفيديو المُخصصة لأجهزة آتاري 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 النسخة الكاملة من كتاب مدخل إلى الذكاء الاصطناعي وتعلم الآلة
×
×
  • أضف...