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

أحمد عصام النجار

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

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

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

4 متابعين

المعلومات الشخصية

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

آخر الزوار

لوحة آخر الزوار معطلة ولن تظهر للأعضاء

إنجازات أحمد عصام النجار

عضو مساهم

عضو مساهم (2/3)

6

السمعة بالموقع

  1. يحتوي حقل تعلم الآلة على الكثير من الفروع والتقنيات والخوارزميات تبعًا لاهتمامات الأفراد والشركات، ولذلك فإنه من الضروري معرفة المسار الأنسب لك لتعلم ما عليك تعلّمه وما عليك تركه، فمن المستحيل الجمع بين مسارات تعلم الآلة المختلفة مرةً واحدةً. مسارات تعلم الآلة يمكن تقسيم تلك المسارات إلى ثلاثة، أولها مشاريع بيانات الأعمال التجارية أو يسمى ذكاء الأعمال business intelligence وثانيهما السلك الأكاديمي وثالثهما المشاريع الهندسية، وسنسرد الثلاثة والفرق بينهم في الفقرات الآتية. تخصص ذكاء الأعمال التجارية في تعلم الآلة في هذا النوع من المشاريع يعمل غالبًا محللو البيانات data analysts وعلماء البيانات data scientists على استخدام البيانات الضخمة لفهم اتجاهات وأداء الشركات، إذ تُستخدَم تلك التحليلات للتنبؤ بأفضل القرارات الواجب اتخاذها والتي قد تشكِّل صعودًا في سلم أهداف الشركات. من الأفضل في ذلك المسار أن يكون محلل البيانات على دراية كبيرة بعدد من الخوارزميات المفيدة لعمله وكيف تعمل كل خوارزمية حتى يستطيع الاختيار بينهم بناءً على المشكلة والمشروع الذي يعمل عليه. يمكنك معرفة المزيد بالرجوع إلى مقال الدليل الشامل إلى تحليل البيانات فمجال تحليل البيانات كبير وواسع جدًا. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن التخصص الأكاديمي في تعلم الآلة قد يكون الفرد في هذا المسار طالبًا أو باحثًا، وفي كلا الحالتين يُركَّز على الوجه الأكاديمي والرياضي في تعلم الآلة غالبًا، ففي حال كونك طالبًا، فستجد على الأغلب أنّ التركيز كله يصب في جانب الرياضيات من الخوارزميات، ومع أنه يوجد بعض الجامعات التي تدرِّس جوانب تطبيقيةً أيضًا، إلا أنها ليست مؤهلةً على الأغلب لسوق العمل. أما في حالة كونك باحثًا في علوم الحاسوب أو تعلم الآلة، فسيرتكز الأمر دائمًا على قراءة الأبحاث والمجلات العلمية ومحاولة تطوير الخوارزميات رياضيًا ثم محاولة تطبيق تلك الأبحاث؛ ولذلك إذا اتخذت هذا المسار فإنه لمن الضروري دراسة الخوارزميات بصورة دقيقة للبحث في تطويرها واستخدامها، وأعني هنا دراسة كيف تعمل الخوارزميات ولماذا تعمل. مع ذلك، فإنك قد تكون باحثًا أو طالبًا في مجال الفيزياء أو الكيمياء أو الأحياء أو غيرها من العلوم الأخرى غير المرتبطة بالرياضيات والإحصاء علوم الحاسوب، ففي تلك الحالة قد تحتاج إلى برمجة تعلم الآلة لصناعة نماذج قادرة على مساعدتك في أبحاثك. فكما أوردنا من قبل، نماذج تعلم الآلة مفيدة في كافة المجالات البحثية، إذ يستخدمها علماء الفيزياء والفلك في اكتشاف الكواكب وتحليل الصور، ويستخدمها علماء الأحياء كذلك وغيرهم، وإنه لمن الأفضل في ذلك الحال عدم التوغل كثيرًا في الرياضيات، بل يكفيك فهم أبرز الخوارزميات التي من المرجح أن تحل مشكلتك، ثم البدء في تطبيقها في مشروعك البحثي، ثم اختيار الخوارزمية المناسبة فعلًا لمشروعك بناءً على التجربة والخطأ وبناءً على فهمك للبيانات والمشروع والمشكلة التي تريد حلها. ما أعنيه أنه في كل تلك الحالات والمسارات الأكاديمية عدا كونك باحثًا متخصصًا في تعلم الآلة، فإن فهم كيف تعمل الخوارزمية التي تستخدِمها هو الجانب الضروري؛ أما لماذا تعمل الخوارزمية، فليس ضروريًا. التطبيق في المشاريع الهندسية أعني هنا أنك قد تكون مبرمجًا أو مطورًا أو مهندس تعلم آلة وتعمل في إحدى الشركات أو تعمل على تطوير مشروع ما، وفي كل حالة من تلك الحالات، فإنه من الضروري اختيار مجموعة الخوارزميات المناسبة لتخصص مشاريعك. قد تعمل مثلًا في إحدى الشركات المتخصصة فقط في الرؤية الحاسوبية Computer Vision، ففي ذلك الحال من الضروري أن تبدأ رحلتك بالبحث عن الخوارزميات التي تعمل بصورة جيدة في ذلك التخصص فقط ثم دراسة كل خوارزمية نظريًا وتطبيقها العملي، وكذلك قد تعمل في إحدى الشركات المتخصصة في معالجة اللغات الطبيعية natural language processing اختصارًا NLP، ففي تلك الحالة أيضًا عليك اختيار الخوارزميات المهمة المعروف عنها العمل في هذا التخصص، ثم دراسة تلك الخوارزميات نظريًا وتطبيقيًا. وقد تكون على سبيل المثال مبرمجًا وتعمل على مشروع نظام مبيعات أو ما شابه، ففي ذلك الحال قد تريد إضافة بعض الميزات للنظام مثل توقع مبيعات الشهر القادم أو ما شابه، ففي ذلك المسار قد يهمك فقط فهم أساسيات تعلم الآلة وبعض أشهر الخوارزميات لحل مشكلات التوقع والتصنيف وما شابه، فحتى الجانب الرياضياتي في ذلك المسار لن يكون على قدر كبير من الأهمية. كيفية تنفيذ مشاريع تعلم الآلة عمليًا لأننا نهدف في هذه السلسلة برمجة الذكاء الاصطناعي إلى تعريفك بحقل تعلم الآلة من الناحية النظرية والناحية العملية والتطبيقية، فسنشرح في الفقرات الآتية الخطوات التطبيقية لعملية بناء نموذج تعلم آلة في أي مشروع من المشاريع بغض النظر عن التخصص أو الخوارزمية، وذلك لأن كثيرًا من طلبة هذا الحقل غالبًا يضيعون بين المعلومات النظرية والتطبيقية المتفرقة دون تكوين صورة عامة للخطوات التطبيقية. يمكن تقسيم خطوات مشاريع تعلم الآلة التطبيقية إلى خمس مراحل: تعريف المشكلة والمشروع تحضير وتهيئة البيانات فحص واختيار وضبط الخوارزميات تحسين النتائج تقديم النتائج تتسم تلك الخطوات بالمرونة، ومع ذلك فتلك الخطوات هي فقط الخطوط العريضة التي قد يندرج أسفل كل خطوة منها العديد من الخطوات الفرعية الأخرى التي قد يحتاج إليها في بعض المشاريع دونًا عن غيرها. واتسام تلك الخطوات بالمرونة يكمن في استطاعتنا الرجوع في أي خطوة من تلك الخطوات إلى الخطوة التي قبلها للقيام ببعض التعديلات والتغييرات، وذلك يتضح في الخطوة الرابعة خصيصًا والتي تهتم بتحسين نتائج النموذج، ولتحسين تلك النتائج سيتوجب الرجوع إلى الخطوة الثانية والثالثة، حيث أن تهيئة البيانات تلعب دورًا في تحسين نتائج النموذج، وكذلك الخوارزمية المختارة أيضًا. 1. تعريف المشكلة والمشروع ذكرنا في بداية مقال نظرة سريعة على مجال تعلم الآلة تعريف عالم الحاسوب توم ميتشل Tom Mitchell لتعلم الآلة، وكان كما يلي: بناءً على ذلك يمكننا تحديد ما ترمز إليه تلك الرموز الموجودة في التعريف كما يلي: الرمز E: يرمز هنا إلى البيانات، إذ أننا نمثِّل كل الخبرات التي نلقمها للخوارزمية على صورة بيانات، وتلك البيانات كلها يتم تشكيلها على صورة مصفوفات ومتجهات رياضية كما وصفنا من قبل. الرمز T: وهي المهمة المراد إجراؤها، وقد سردنا بعضًا من تلك المهام من قبل مثل توقع سعر عقار ما أو تصنيف عملية تحويل أموال …إلخ. الرمز P: يرمز إلى المعيار الذي على أساسه نستطيع تحديد نجاح المشروع من عدمه مثل قولنا أنّ معيار نجاح المشروع هو توقع أسعار العقارات بصورة صحيحة في 90٪ من الحالات على الأقل. يجب تحديد معنى كل رمز من تلك الرموز في المشروع في هذه الخطوة، فيرمز الرمز E مثلًا كما بيّنا إلى الخبرة وهي البيانات في مشاريع تعلم الآلة، وتلك البيانات يجب أن تتوفر قبل البدء في المشروع، ويجب أن تكون كافية كميًا للمشروع، ثم يجب بعد ذلك تحديد النقاط الأولية التي تخدم المشروع وتحجم أهدافه، فعلى سبيل المثال عند العمل على مشروع لتوقع سعر العقارات في منطقة ما، قد تكون لدينا عدة نقاط أولية أولها أنّ حساب سعر المتر المربع هو المهم توقعه، وبالتالي بناءً على تلك النقطة فإننا سنحتاج ضرورةً إلى تلك المعلومة في عنصر الخبرة E أي البيانات، أي سنحتاج إلى بعد في البيانات يحتوي على سعر المتر المربع لكل عقار ليتم تدريب النموذج عليه. من النقاط الأخرى الممكن إضافتها في مثل هذا المشروع هو أنّ الأسعار الحديثة فقط هي التي تحمل الأهمية الكبرى في تدريب النموذج، أي أننا لا نريد تدريب النموذج على أسعار العقارات المسعرة منذ عشرين عامًا مثلًا، وإنما نريد تدريب النموذج على الأسعار الحديثة فقط، وبناءً على تلك النقطة فإننا سنحتاج ضرورةً إلى معرفة تاريخ التسعير في كل عينة من البيانات وتنقية مجموعة البيانات عبر حذف العينات ذات التاريخ القديم. وكذلك يمكن إضافة العديد من النقاط الأولية للمشروع والتي على أساسها تجري بعض التعديلات على عنصر الخبرة E وأيضًا العناصر الأخرى على الأرجح، لذلك من الضروري كتابة تلك النقاط على الورق لتشكِّل نقطة انطلاق وخريطة طريق مبدئية للمشروع، وفي تلك المرحلة، من الضروري التفكير بعمق في عنصر الخبرة، فربما يكون ذلك العنصر مفقودًا أو غير متاح بالنسبة إليك، وبالتالي فإنه لمن المفيد بدايةً وقبل كل أمر التفكير في آلية تجميع البيانات المراد العمل بها في ذلك المشروع. ولذلك، فإن من أهم التخصصات التي تخدم تخصص برمجة نماذج تعلم الآلة هو تخصص استخراج البيانات من الإنترنت web scraping، وذلك لأنّ تلك المهارة تعنى بكتابة برامج تعمل على تجميع البيانات تلقائيًا من مواقع الإنترنت، فمن المهم التفكير في عنصر البيانات جيدًا قبل البدء في باقي الخطوات التطبيقية. يمكن كذلك في تلك المرحلة التفكير في الصورة النهائية للمنتج، إذ أنه من المفيد صناعة نموذج مبدئي prototype لترى المنتج النهائي المراد تحقيقه في نهاية المطاف عمومًا. 2. تحضير وتهيئة البيانات عند توفر البيانات المناسبة للمشروع نبدأ أولًا بمعالجة تلك البيانات، إذ يلزم إجراء بعض العمليات على البيانات على الأغلب في المشاريع الحقيقية بناءً على الرؤية العامة للمشروع؛ لتُمرَّر تلك البيانات بعد تحضيرها إلى النموذج. تنقسم هذه الخطوة باختصار إلى ثلاث خطوات فرعية وهي اختيار البيانات ومعالجة البيانات وتحويل البيانات، وكلها خطوات اختيارية، أي تختلف تلك المراحل تبعًا للمشروع وتبعًا لجاهزية البيانات لتمرر للنموذج. اختيار البيانات في ذلك الطور تُختار بعض الأبعاد وتُحذَف بعضها بناءً على الرؤية العامة للمشروع وعن الجودة المراد تحقيقها، إذ أن قاعدة الأكثر أفضل ليست صحيحةً في كل الحالات، فحذف بعض الأبعاد -أي الأعمدة- من بعض مجموعات البيانات يوفِّر جودةً أفضل في بعض النماذج، وأيضًا قد يكون من الأفضل حذف بعض العينات من البيانات لأنها عينات شاذة تؤثِّر سلبًا على جودة النموذج. معالجة البيانات في الطور السابق وبعد حذف بعض الأبعاد المؤثرة سلبًا والغير مهمة، تأتي مرحلة المعالجة والتي تُنقَّح فيها العينات أو تُنسَّق بعض البيانات الموجودة في العينات، وقد يتم في ذلك الطور تقسيم البيانات لأخذ عدد بسيط فقط من العينات في أول الأمر لاختبار النموذج المبدئي قبل تدريب نموذج تعلم الآلة على كل العينات، إذ يعني العدد الأكبر من العينات وقتًا أطول في تدريب النموذج واستهلاك لموارد الحاسوب بصورة أكبر، …إلخ. تحويل البيانات في ذلك الطور تُجهَّز البيانات بصورة نهائية لتمريرها إلى النموذج، وقد تعود إلى تلك النقطة عدة مرات بناءً على نتائج النموذج المبدئي، إذ قد تحتاج إلى إعادة تحجيم بعض الأبعاد scaling أو تجميع بعضها aggregation أو تجزيء بعضها decomposition …إلخ، وذلك عبر هندسة الميزات feature engineering والتي تعني تحويل البيانات إلى الصورة الأمثل للنموذج للحصول على أعلى دقة في نهاية المطاف. 3. فحص واختيار وضبط الخوارزميات تتماشى هذه المرحلة مع ما قبلها وما بعدها على أغلب الأحوال، فهي تعنى باختيار وضبط الخوارزميات، فكما أوردنا أنّ خوارزميات تعلم الآلة والتعلم العميق كثيرة للغاية، ويعمل الباحثون في أكبر شركات وجامعات العالم على تطوير تلك الخوارزميات يومًا بعد يوم، لذلك تتزايد الخيارات تزايدًا ملحوظًا كل عام. في هذه المرحلة من الضروري تحديد الخوارزميات المنوط بها حل المشكلة المعرفة من قبلك في أولى الخطوات المذكورة، ففي حال برمجة نموذج يسعى مثلًا إلى توقع قيمة مستمرة continuous value مثل توقع قيمة عقار في إحدى المناطق السكنية، فسيقع حل تلك المشكلة تحت خوارزميات الانحدار مثل انحدار ريدج ridge regression وانحدار المربعات الصغرى الاعتيادية ordinary least squares regression، وبالتالي نستطيع في تلك المرحلة ولتلك المشكلة بالتحديد اختيار هاتين الخوارزميتين لتجربتهما، ثم ليقع اختيارنا على إحداهما في نهاية المطاف بناءً على معدل الدقة لكل منهما. يؤثر اختيار أكبر عدد ممكن من الخوارزميات المتاحة لحل المشكلة المحددة مسبقًا في كثير من الأحيان، ولا يضرنا اختبار عدد كبير من الخوارزميات إذا كانت كلها في نهاية المطاف تخدم الهدف المرجو، فقد تجد خوارزميةً أفضل من الأخرى تبعًا لكل حالة؛ ولإجراء ذلك رجحنا كما في الخطوة السابقة اقتطاع جزء من البيانات بعد تهيئتها لتشكيل عدد بسيط من العينات المعدة للتمرير إلى الخوارزميات لتجربة النموذج المبدئي، فتستطيع تجربة أكبر عدد من الخوارزميات في أقل فترة ممكنة عبر ذلك الاقتطاع، لأن كثرة العينات تزيد بصورة كبيرة مرحلة تدريب الخوارزمية على البيانات؛ ولذلك فإنه في تلك المرحلة من الضروري ألا تحتوي البيانات على عدد كبير من العينات لتجربة أكبر عدد ممكن من الخوارزميات. لتحديد أفضل خوارزمية من تلك الخوارزميات التي وقع اختيارك عليها يلزم اختيار تقنية من تقنيات قياس معدل الدقة، ولا يعوزنا القيام بذلك بأنفسنا، لوجود عدد كبير من تلك التقنيات والطرق بالفعل لقياس معدل الدقة لكل خوارزمية من خوارزميات تعلم الآلة والتي على أساسها يمكن اختيار الخوارزمية المثلى للمشروع. يوجد عدد من التقنيات والطرق المعنية بقياس معدل دقة الخوارزميات وأبسطها على الإطلاق تقسيم مجموعة البيانات الممررة إلى الخوارزمية إلى قسمين، إذ يمثل القسم الأول 80% من البيانات غالبًا ويمثل القسم الثاني 20%، بحيث يُستخدَم القسم الأول في تدريب الخوارزمية والقسم الثاني في اختبار النموذج بعد التدريب لمعرفة معدل الدقة، فقد نمرر مثلًا معظم العينات لتدريب الخوارزمية في مشروع توقع أسعار العقارات في إحدى المناطق، بحيث تحتوي تلك العينات على كل الأبعاد والتي من ضمنها المخرج المراد توقعه وهو السعر، ثم لاختبار النموذج تُمرَّر العينات المتبقية -أي عدد 20% من العينات- إلى وسيلة قياس معدل الدقة دون بعد المخرج -أي دون سعر كل عينة- لتتوقع الخوارزمية ذلك المخرج ثم مقارنة مخرج الخوارزمية مع المخرج الحقيقي في كل عينة. من التقنيات المشابهة لقياس أداء الخوارزميات هي تقنية التقييم المتقاطع cross validation والتي تعتمد على فكرة تقسيم العينات إلى مجموعات متساوية في العدد بحيث تدعى كل مجموعة طية fold، إذ تدرب الخوارزمية على كافة الطيات ما عدا طية واحدة تترَك للنهاية لقياس معدل الدقة بالوسيلة ذاتها في الطريقة السابقة، بحيث تعامل الطية الأخيرة معاملة عينات الاختبار في الطريقة البسيطة أعلاه، أي طريقة 80% للتدريب و 20% للاختبار. تُكرَّر العملية أعلاه مع تغيير الطيات في كل مرة، إذ تُدرَّب وتختبر الخوارزمية عدد الطيات، فإذا قُسِّمت مجموعة البيانات مثلًا إلى ثلاث طيات، فسيُدرَّب ويختبر النموذج ثلاث مرات كما يلي: طية 1 و 2 للتدريب، والطية 3 للاختبار. طية 1 و 3 للتدريب، والطية 2 للاختبار. طية 2 و 3 للتدريب، والطية 1 للاختبار. يُحسَب متوسط معدل الدقة في كل عملية اختبار في نهاية هذه العملية لينتج معدل الدقة العام للنموذج، والذي بناءً عليه تُعرَف دقة أداء الخوارزمية على بيانات المشروع وعلى المشكلة المعرَّفة والمراد حلها في المشروع. 4. تحسين النتائج نعود في هذه المرحلة مرارًا إلى المرحلة الثانية والثالثة لمحاولة استخلاص أفضل دقة ممكنة، فنعود مرارًا إلى هندسة الميزات حيث قد نحتاج إلى بضع تعديلات هنا وهناك لتحسين معدل الدقة، وكذلك قد نحتاج إلى ضبط إعدادات الخوارزمية المختارة لتحسين معدل الدقة، إذ تتضمن الكثير من الخوارزميات عددًا من المعاملات الأولية التي تتحكم بقدر ما في آلية عملها وبالتالي في معدل دقة نتائجها. يمكن التفكير في معاملات الخوارزميات على أساس أبعاد على صورة رسم بياني، إذ يمثِّل كل بعد محورًا في الرسم، وقيمة كل بعد ما هي إلا نقطة في الرسم، والهدف من ذلك هو إيجاد أفضل النقاط لكل بعد على الرسم بحيث ينتج أعلى معدل دقة من القيم المثلى لكل بعد في ذلك الرسم عند التلاقي، ويمكن إضافة بُعد معدل الدقة إلى تلك الأبعاد بأن نحاول زعزعة وتحريك نقاط كل بعد من الأبعاد غير بعد معدل الدقة لنرى تأثير تلك التغيرات على معدل الدقة؛ وبذلك نحاول الوصول إلى القيم المثلى. قد نستخدِم في بعض الحالات في هذه المرحلة مجموعة خوارزميات ensemble methods للجمع بين نتائج خوارزميتين أو أكثر، إذ تتدرب كل خوارزمية على حدة وتُختبَر كل واحدة اختبارًا منفصلًا، ثم يُجمَع بين توقعات الخوارزميات في مخرج واحد عبر إحدى خوارزميات التجميع، ونزعم أنّ ذلك قد يفضي إلى معدل دقة أعلى من استخدام خوارزمية واحدة فقط في بعض الأحيان. 5. نشر النتائج تبدأ المرحلة الأخيرة في المشروع وهي نشر النتائج deployment وتختلف تبعًا لنوع المشروع بعد الوصول إلى القيم المثلى في ضبط الخوارزمية المختارة، وبعد تدريب النموذج والوصول إلى أفضل معدل دقة ممكن. قد يُنشر أحيانًا عبر الإنترنت على موقع خاص أو على هيئة تطبيق للهواتف أو على هيئة تطبيق سطح مكتب أو قد تنشَر الشيفرة فقط لأهداف تعليمية أو أكاديمية أو تباع لأهداف تجارية، والخلاصة يتحدد الشكل النهائي للبرنامج عبر الخطوة الأولى من الخطوات التي أشرنا إليها أي يُنشَر المشروع وفقًا للهدف الذي انطلق منه. اقرأ أيضًا المقال السابق: أدوات برمجة نماذج تعلم الآلة خطوات تنفيذ مشروع عن تعلم الآلة في بايثون تقييم واختيار نماذج تعلم الآلة
  2. هنالك بضعة أدوات يجب توافرها لك في كونك مبرمج أو مهندس تعلم آلة للبدء في برمجة النماذج، وللتسهيل وللتوضيح سنسرد تلك الأدوات بصورة منظمة حتى يسهل عليك قدر الإمكان جمع وربط الأفكار التي قد تكون مشتتة ومشوشة في عقلك أثناء القراءة في موضوع مثل تعلم الآلة. تتوافر أمور عدة في كل مجال من مجالات صناعة البرمجيات للبدء في العمل، فصناعة برامج الإنترنت تتطلب بدورها وبصورة ضرورية لغة لهيكلة الصفحات هي HTML ولغة لتنسيقها هي CSS ولغة لبرمجة الأنظمة الخلفية مثل لغة PHP أو Python، وبصورة اختيارية لغة تفاعلية للواجهة الأمامية مثل لغة JavaScript، والصورة التي تربط تلك الأدوات واللغات ببعضها البعض، هي ما يدعى بمجال هندسة الويب Web Engineering، ونحن نحاول توضيح الأمر ذاته، عبر ذكر تلك الأدوات الرئيسة والاختيارية في تعلم الآلة والتي من شأنها مساعدتك في ربط الأفكار المتعلقة بتعلم الآلة وبرمجتها. الأدوات الأساسية الواجب توافرها للبدء في برمجة أي نموذج من نماذج تعلم الآلة هي البيانات والبنية التحتية التقنية والخوارزميات، وسنتحدث في الفقرات الآتية عن كل نقطة من تلك النقاط بشيء من التفصيل. العلاقة بين البيانات ومجال تعلم الآلة الأداة الأولى المهمة في تعلم الآلة هي البيانات data، وهي أهم أداة يجب توافرها في نماذج تعلم الآلة، ويتم تمثيل البيانات على هيئة مصفوفات رياضية أثناء وقبل تمريرها إلى خوارزميات تعلم الآلة، ومن السهل تمثيل المصفوفات على هيئة جداول من البيانات، بحيث تحتوي على صفوف وأعمدة، ويكون كل صف عينةً من البيانات تتكون من عدة نقاط، وكل عمود هو خاصية من خواص مجموعة البيانات، إذ تمثل كل خاصية بُعدًا إضافيًا للبيانات، بحيث يكون عدد أبعاد البيانات هو عدد الأعمدة الممثلة للبيانات في صورة جدول، ولمزيد من التوضيح لنأخذ بيانات موظفي شركة ما على سبيل المثال لتجريد تلك المصطلحات السالفة من اللغط. لدينا في الجدول التالي ثلاث عينات وأربعة أبعاد، وكل خلية من خلايا الجدول هي ما يدعى بنقطة بيانات Data Point . الراتب سنوات الخبرة مستوى التعليم العمر 4000 0 1 20 6000 2 2 25 10000 10 3 35 كما نرى تحتوي البيانات على ثلاث عينات ونعني هنا ثلاثة موظفين من موظفي الشركة، وكل عينة من تلك العينات لها أربعة أبعاد أو خواص وهي العمر ومستوى التعليم والخبرة والراتب، وما نعنيه بنقطة البيانات هي خلية من خلايا الجدول، أي بعد العينات، إذ يمثل راتب العينة الأخيرة مثلًا نقطة بيانات وقيمتها 10000، كما تمثل عدد سنوات خبرة العينة الأولى نقطة بيانات أخرى وقيمتها صفر وهكذا. الجدير بالذكر أنّ البعد له أسماء كثيرة قد تسبب لغطًا كبيرًا للمتعلمين، وإن من بينِ أسمائه بعدًا Dimension ومتغيرًا Variable وسمة أو خاصية Attribute وميزة Feature، ولذلك من الضروري عند دراسة أيّ مرجع من مراجع تعلم الآلة وعلوم البيانات أن تعلم أنّ تلك المسميات كلها تشير إلى الشيء ذاته وهو عمود في البيانات بعد تمثيلها على هيئة جدول. جدول البيانات بأكمله في الرياضيات وفي برمجة تعلم الآلة ما هو إلا مصفوفة Matrix ذات بعد يساوي عدد الأعمدة، وكل عمود في الجدول ما هو إلا متجه Vector يحتوي على جزء من المصفوفة، فالمتجهات هي مصفوفات لكنها ذات بعد واحد، أي خاصية واحدة فقط، فسنوات الخبرة للثلاث عينات في الجدول السابق مثلًا هي متجه يحتوي على ثلاثة صفوف وعمود واحد، الصف الأول صفر والثاني اثنان والثالث عشرة، وكذلك الأمر بالنسبة لمستوى التعليم والعمر والراتب، وهكذا. الجدول الآتي هو متجه العمر على سبيل المثال: العمر 20 25 35 والجدول التالي هو متجه سنوات الخبرة: سنوات الخبرة 0 2 10 أما الجدول التالي فهو متجه الراتب: الراتب 4000 6000 10000 يُعَدّ ذلك أمرًا أساسيًا فهمه في البيانات، ففهم البيانات هو أول وأهم الخطوات في سبيل برمجة تعلم الآلة، إذ تُقضَى أكثر أوقات مهندسِي تعلم الآلة في فهم البيانات وتنقيحها قبل إجراء مهمات برمجة نموذج تعلم الآلة، وتستخدِم كل مهمة من مهام تعلم الآلة ذلك النمط من تمثيل البيانات وحتى المعقدة منها مثل النماذج التي تتعامل مع الصور وغيرها حتى وإذا كانت غير واضحة بصورة جلية وبسيطة للمبرمج، ففي التعامل مع الصور تُحوَّل كل صورة إلى مصفوفة من البكسلات Pixels وهكذا. الجدير بالذكر أنه في التعلم تحت إشراف تُقسَّم المتغيرات أو الأبعاد في البيانات إلى قسمين، بحيث يحتوي القسم الأول على المتغيرات المستقلة Independent Variables وهي تلك المتغيرات التي تعمل على أساس مدخلات للنموذج، والقسم الثاني يحتوي على المتغير التابع Dependant Variable وهو الذي يعمل على أساس خرج في النموذج. لفهم ذلك لنأخذ بيانات الموظفين السابقة مثالًا ولنقل أننا نريد بناءً على تلك البيانات بناء نموذج تعلم آلة يتوقع راتب الموظف بناءً على عمره ومستوى تعليمه وسنوات خبرته، فتلك المتغيرات هي ما ندعوها بالمتغيرات المستقلة لأن الخرج يعتمد عليها لا العكس وهي على هيئة مصفوفة، وفي مثالنا هذا تتكون المصفوفة من ثلاثة أعمدة وثلاثة صفوف؛ أما المتغير التابع فهو متجه الراتب في ذلك المثال، وهو مصفوفة ذات عمود واحد وثلاثة صفوف، وتكون المتغيرات المستقلة كما يلي: سنوات الخبرة مستوى التعليم العمر 0 1 20 2 2 25 10 3 35 ويكون المتغير التابع كما يلي: الراتب 4000 6000 10000 لتدريب نموذج يتوقع راتب الموظفين بناءً على تلك البيانات، فإننا نُمرر مصفوفة المتغيرات المستقلة ومتجه المتغير التابع إلى خوارزمية النموذج ليبني العلاقة بين تلك المتغيرات للوصول إلى القيم المثلى للثوابت الموجودة في المعادلة الجبرية، التي تربط بين المتغيرات المستقلة والمتغير التابع، ثم بعد ذلك نستطيع استخدام تلك المعادلة مع تلك الثوابت التي وصلت الخوارزمية إليها بعد التدريب ليُتوقَّع راتب أيّ موظف جديد. البنية التحتية التقنية الأداة الثانية في برمجة تعلم الآلة هي البنية التحتية التقنية، وما نعنيه هنا هو مجموعة الأدوات التقنية الواجب توفرها للبدء في التطبيق العملي في كل مراحله، ويشمل ذلك ما تحتاجه لمعالجة البيانات وتنقيحها وما تحتاجه لرسم البيانات لمساعدتك على فهمها بصورة أفضل وما تحتاجه في معالجة المصفوفات والمتجهات وإجراء العمليات عليهما، …إلخ. لغات البرمجة المستخدمة في تعلم الآلة إنّ أول ما نحتاجه في البنية التحتية التقنية هي لغة البرمجة ومع أنّ العمل في مجال تعلم الآلة ممكن باستخدام أيّ لغة من اللغات، إلا أن بعض اللغات قد تدعم العمل على نماذج تعلم الآلة وعلى معالجة البيانات بصورة أفضل وأسهل، ومن ذلك المنطلق وقع اختيارنا على لغة بايثون التي أوردنا شرحها في الفصل الثالث من السلسلة، لكن بايثون ليست اللغة الوحيدة مع ذلك فإن من الاختيارات الأخرى الأكثر شهرةً للتعامل مع البيانات وتعلم الآلة هي لغة R ولغة Matlab ولغة Octave ولغة C++‎ ولغة JavaScript وغيرها. مع ذلك فإنه حتى تاريخ كتابة هذا الفصل لاحظت تفضيل لغة بايثون و R وماتلاب Matlab و Octave على باقي لغات البرمجة من قِبَل مبرمجِي تعلم الآلة وذلك لتوفر مكتبات مساعدة غير متاحة في الكثير من اللغات الأخرى، ولكن بالطبع أكثر لغة مستخدَمة في معالجة البيانات وتعلم الآلة هي بايثون بلا أدنى شك، والتي من أسباب شيوعها هي توفر مكتبات أخرى تساعد على كشط وجمع البيانات من الإنترنت بصورة آلية لاستخدامها في المشاريع. أيضًا، تُعَد لغة بايثون لغةً عامةً لا تقتصر على مجال بعينه، وإنما تُستخدَم في برمجة الويب وغيرها من المجالات، إذ ستفضِّل شركات برمجيات الويب حتمًا استخدام بايثون لبرمجة نماذج تعلم الآلة في مشروعاتها بدلًا من اللغات الأخرى وذلك لأنها هي ذاتها اللغة التي يمكن عن طريقها برمجة تطبيقات ويب بسلاسة. تُعَدّ لغة R لغةً مجانيةً ومفتوحة المصدر مثل بايثون، ولكنها مبنية من أجل العمليات الرياضية بصورة أساسية مثل تحليل البيانات والعمليات الإحصائية، إذ تحتوي افتراضيًا على كثير من الدوال التي تؤدي تلك المهام، كما أنها تدعم الكثير من مهام تعلم الآلة؛ أما ماتلاب وأُكتاف Octave، فهما منافستان للغة R من حيث التخصص، ولكن ماتلاب هي لغة تجارية غير مجانية وهي متميزة إلى حد كبير في حل المعادلات الجبرية على وجه الخصوص، ومن ميزاتها أيضًا أنها لغة سهلة التعلم، وهي واسعة الانتشار في الهندسة الكهربائية والهندسة الكيميائية والهندسة المدنية وهندسة الطيران. مع ذلك/ عادةً لا يستخدِم الكثير من علماء الحاسوب ومهندسو البرمجيات لغة ماتلاب سوى في الأوساط الأكاديمية، فهي لا تستخدَم في الأعمال بصورة كبيرة، لذلك فإنّ العدد الكبير للمساقات الأكاديمية الموجودة على الإنترنت والتي تستخدِم ماتلاب ليس مؤشرًا على وسع انتشار ماتلاب في وسط الأعمال؛ أما لغة Octave فهي ببساطة النسخة المجانية من ماتلاب والتي طُوِّرت عن طريق مساهمات المبرمجين والمطورين حول العالم لتوفير بديل مجاني ومفتوح المصدر لماتلاب. ناقشنا في الفصل الثالث خيارات كتابة لغة بايثون إما على حاسوبك الخاص أو عبر خدمة Google Colab، وهي تماثل ميزات خدمة أخرى يستخدِمها الكثير من المبرمجين تدعى Jupyter Notebook، وهي خدمة لها واجهة يتم الولوج فيها عبر المتصفح، إذ تُثبَّت على الحاسوب ثم تُشغَّل عبر أمر في الطرفية Terminal أو سطر الأوامر Command Prompt، لكن لكون Google Colab أبسط في التعامل وكونها خدمةً سحابيةً لا تحتاج إلى تثبيت أية مكتبات، فقد فضلنا الإحالة إليها بدلًا من شرح Jupyter Notebook مثل باقي المراجع الأجنبية. المكتبات البرمجية في تعلم الآلة يأخذنا هذا إلى الحديث عن المكتبات المهمة في بايثون للتعامل مع علوم البيانات وتعلم الآلة، إذ يجب استخدام مكتبة Numpy للتعامل مع المصفوفات والمتجهات، وللتعامل مع أمور قراءة ملفات البيانات مثل ملفات Excel و CSV، فإن استخدام مكتبة مثل Pandas يُعَدّ أمرًا مهمًا أيضًا، ولتوفير الوقت والجهد في كتابة خوارزميات تعلم الآلة من الصفر يمكن استخدام مكتبة مثل Scikit Learn التي تحتوي على كثير من خوارزميات تعلم الآلة افتراضيًا، إذ يمكن استدعاء واستخدام الدوال المدمجة في المكتبة لإجراء الكثير من الأمور. أما بالنسبة لأمور تمثيل البيانات ورسمها، فيمكن استخدام المكتبات الأكثر شيوعًا في بايثون وهما Matplotlib و Seaborn، وقد يظهر لتلك المكتبات مكتبات أخرى منافسة لتكون هي الأكثر شيوعًا واستخدامًا في السنوات القادمة. بغض النظر عن مستوى النموذج الذي تبرمجه، فإن من أكبر الضروريات أثناء عملك هو تمثيل البيانات عبر أدوات التصور والإظهار Visualization، إذ يعطي تصور البيانات القدرة على فهمها بصورة أكبر، فعن طريقها تستطيع تحديد ما إذا كان لديك بعض العينات الشاذة التي تؤثِّر على النتائج تأثيرًا سلبيًا، وتستطيع أيضًا تحديد وجود مشكلات أخرى، كما تستطيع تحديد أفضل الخوارزميات المناسبة للعمل على البيانات المتاحة، وتستطيع أيضًا تمثيل نتائج الخوارزميات على البيانات، …إلخ. ذكرنا أن أكثر المكتبات شيوعًا في لغة بايثون لتمثيل البيانات هي Matplotlib و Seaborn، ولكن مع ذلك فقد يظهر لتلك المكتبات مكتبات أخرى منافسة لتكون هي الأكثر شيوعًا واستخدامًا في السنوات القادمة. تحدثنا في الفصل السابق عن التعلم العميق وأنه أحد فروع تعلم الآلة، وفكرة التعلم العميق هي بناء شبكات عصبية Neural Networks تحاكي تلك الموجودة في دماغنا البشري، وإن للتعلم العميق مكتبات واسعة الاستخدام في بايثون والتي توفر خوارزميات ودوال مهمة، لتسهيل عملية بناء الشبكات العصبية بدلًا من بنائِها من الصفر، وأشهر تلك المكتبات هي تلك التي طُوِّرت في Google وهي مكتبة TensorFlow، وقد أصدرت Google نسخةً منها للغة JavaScript مؤخرًا، ومن مكتبات التعلم العميق مكتبة PyTorch و Theano و Caffe و Chainer، وهي كلها مكتبات متاحة للغة بايثون. توجد أيضًا مكتبة Keras وهي من أوسع المكتبات استخدامًا في بايثون، إذ تستخدِم إحدى مكتبات التعلم العميق الأخرى المتوفرة في بايثون مع تبسيط كبير لدوالها ووحداتها، فيمكن استخدام Keras مع محرك خلفي آخر مثل Tensorflow أو Theano وغيرهما، ومع ذلك فإنه توجد مكتبات أخرى للغات الأخرى مثل مكتبة DL4J للغة جافا، ومكتبة Microsoft Cognitive Toolkit والتي تتوفر لعدة لغات وهي: بايثون وC#‎ و ++C، كما أنه يمكن إدماجها مع اللغات الأخرى عبر لغتها الخاصة والتي تدعى Brain Script. يظهر الرسم [4-4] رسمًا بيانيًا من منصة Kaggle تشير إلى أنّ أكثر المكتبات استخدامًا هي مكتبة Sci-kit Learn التي أشرنا إليها؛ أما تلك المنصة فهي أكبر منصة على الإنترنت تجمع علماء البيانات ومهندسِي تعلم الآلة. أما الرسم [4-5] فهو رسم بياني آخر من المنصة نفسها يشير إلى أنّ أكثر اللغات التي يستخدمها أعضاؤها يوميًا هي لغة بايثون. يدخل في نطاق البنية التحتية التقنية أمور أخرى مثل المعدات، وذلك لأن المشروعات الضخمة لا يمكن برمجة نماذجها عبر الحاسوب الشخصي بإمكانياته البسيطة، فعند التعامل مع البيانات الضخمة التي قد تتمثل في عدد من التيرابايت -وهي وحدات قياس سعة تخزين الحاسوب وهي تساوي ألف جيجابايت- أو البيتابايت -أي مليون جيجابايت- إذ سيصبح استخدام خدمات الحوسبة السحابية والحوسبة الموزعة أمرًا لازمًا لأن تدريب مثل تلك النماذج سيتطلب قدرات حاسوبية كبيرة. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن الخوارزميات المستخدمة في تعلم الآلة الخوارزميات هي ثالث الأدوات في تعلم الآلة، وهي أصل الموضوع ولبه، إذ أن تعلم الآلة كما أوردنا ما هو إلا خوارزميات للتعلم من البيانات وخوارزميات لقياس دقة النماذج، …إلخ، ومن الممكن تطبيق تلك الخوارزميات عبر كتابتها من الصفر بأي لغة من لغات البرمجة، لكن الأمر قد يكون معقدًا خاصةً للمبتدئين وإجراء ذلك ليس ضروريًا على الإطلاق، فكما ترى من الفقرة السابقة أنه توجد العديد من المكتبات الجاهزة للاستخدام والتي تحتوي على دوال مدمجة بها أبرز الخوارزميات التي قد تستخدِمها في حياتك المهنية في هذا الحقل. تُعَدّ خوارزميات تعلم الآلة كثيرةً ومختلفةً، فبعضها متخصص في حل مشكلات التعلم تحت إشراف وبعضها في التعلم دون إشراف وبعضها للتعلم المعزز وللتعلم العميق، …إلخ، وكل خوارزمية من تلك الخوارزميات لها اسم تدعى به، وقد يتخصص مهندس الذكاء الاصطناعي في أحد الفروع دونًا عن غيره، وفي تلك الحالة قد يدرس خوارزميات معينة تُستخدَم في حقله وتخصصه فقط ولا تستخدَم في غيره، ومن تلك التخصصات على سبيل المثال لا الحصر الرؤية الحاسوبية Computer Vision ومعالجة اللغات الطبيعية Natural Language Processing. تصنَّف الخوارزميات عادةً حسب طريقة عملها، وبالرغم من كون القائمة التالية ليست دقيقةً بصورة مطلقة لأن الكثير من تلك الخوارزميات يمكن تصنيفها إلى صنفين أو أكثر من التصنيفات، إلا أنها قد تعطيك نبذةً عن أشهر الخوارزميات التي قد تراها كثيرًا في مراجع تعلم الآلة، وبالطبع كل تلك الخوارزميات لن تُغطَّى في هذه السلسة ولكن نظرة على تلك الأسماء قد توفر لك جهدًا عند مراجعتك للمراجع الأجنبية. 1. خوارزميات الانحدار Regression Algorithms هي خوارزميات تصنع نماذج تربط بين المتغيرات، والتي تُراجَع دقتها باستمرار عبر تقنيات وطرق قياس الخطأ، وهي خوارزميات تعتمد في الأساس وبصورة كبيرة على علم الإحصاء، وفيما يلي بعض أشهر خوارزميات الانحدار: الانحدار الخطي Linear Regression الانحدار اللوجستي Logistic Regression الانحدار الخطوي Stepwise Regression خطوط الانحدار التكيفية متعددة المتغيرات Multivariate Adaptive Regression Splines واختصارًا MARS انحدار المربعات الصغرى الاعتيادية Ordinary Least Squares Regression واختصارًا OLSR تجانس المخطط المبعثر المقدر محليًا Locally Estimated Scatterplot Smoothing واختصارًا LOESS 2. الخوارزميات المبنية على النماذج Instance-based Algorithms يهدف هذا النوع من الخوارزميات إلى حل مشكلات اتخاذ القرار بناءً على الأمثلة التي تعلمها النموذج، وتلك الأمثلة بالطبع ضرورية لتدريب النموذج، عادةً يبني ذلك النوع من الخوارزميات قاعدة بيانات تحتوي على أمثلة موجودة مسبقًا مع ما اتُخِذ من قرار ثم تحاول الخوارزمية موازنة البيانات الجديدة مع ما تملكه في قاعدة البيانات لتوقع القرار الأنسب، وفيما يلي أشهر خوارزميات هذا النوع: التمثيل الكمي لمتجه التعلم Learning Vector Quantization واختصارًا LVQ الجار الأقرب K-nearest neighbor واختصارًا KNN الخرائط ذاتية التنظيم Self-Organized Maps واختصارًا SOM دعم آلات المتجهات Support Vector Machines واختصارًا SVM التعلم الموزون محليًا Locally-Weighted Learning واختصارًا LWL 3. خوارزميات التنظيم Regularization Algorithms يضم هذا النوع بعض الخوارزميات الإضافية والتي تضاف عادةً إلى خوارزميات الانحدار، وتلك الخوارزميات تقوِّم النماذج بناءً على مستوى بساطتها وتعقيدها، إذ تفضِّل النماذج الأبسط، وفيما يلي قائمة بأكثر تلك الخوارزميات شيوعًا: انحدار ريدج Ridge Regression أقل انكماش مطلق ومعامل الاختيار Least Absolute Shrinkage and Selection Operator واختصارًا LASSO الشبكة المرنة Elastic Net انحدار الزاوية الصغرى Least-Angle Regression واختصارًا LARS 4. خوارزميات شجرة القرار Decision Tree يبني هذا النوع من الخوارزميات نموذج اتخاذ قرارات بناءً على قيم خواص البيانات، فالأمر فيه أشبه بشجرة، إذ تُتَتَبع قيم الخواص حتى الوصول إلى قرار ما، ويُستخدَم هذا النوع من الخوارزميات للتدرب على البيانات الخاصة بمهام التصنيف ومهام التوقع/الانحدار، كما تتميز تلك الخوارزميات بالسرعة والدقة العالية في معظم الأحيان، وفيما يلي بعض من تلك الخوارزميات: خوارزمية M5 قرعة القرار Decision Stump أشجار القرار المشروط Conditional Decision Trees شجرة التصنيف والانحدار Classification and Regression Tree واختصارًا CART ثنائي التفرع التكراري 3 أو Iterative Dichotomiser 3 واختصارًا ID3 كشف التفاعل التلقائي لمربع تشي Chi-squared Automatic Interaction Detection واختصارًا CHAID 5. خوارزميات مبرهنة بايز Bayesian Algorithms هذا النوع من الخوارزميات هو الذي يستخدِم مبرهنة بايز Bayes’ Theorem بصورة صريحة لحل مشكلات التصنيف والتوقع / الانحدار، وفيما يلي أكثر خوارزميات هذا النوع انتشارًا: خوارزمية Naive Bayes خوارزمية Gaussian Naive Bayes خوارزمية Naive Bayes متعددة الحدود Multinomial Naive Bayes الشبكة البايزية Bayesian Network واختصارًا BN مقدِرات متوسطات الاعتماد الواحد Averaged One-Dependence Estimators واختصارًا AODE شبكة المعتقدات البايزية Bayesian Belief Network واختصارًا BBN 6. خوارزميات العنقدة Clustering Algorithms يحاول هذا النوع من الخوارزميات تصنيف وتجميع البيانات المتشابهة إلى أقصى حد لتوفير فهم أفضل للبيانات والخصائص المشتركة بين العينات، وفيما يلي أشهر خوارزميات هذا النوع: خوارزمية k-Medians خوارزمية k-Means تعظيم التوقع Expectation Maximisation واختصارًا EM العنقدة الهرمية Hierarchical Clustering 7. خوارزميات تعلم قواعد الترابط Association Rule Learning Algorithms يحاول هذا النوع من الخوارزميات استخراج القواعد التي تصف العلاقات بين المتغيرات الموجودة في البيانات، إذ يمكن أن تظهر القواعد علاقات ذات فائدة كبيرة في البيانات ذات الأبعاد الكثيرة والتي قد تكون مفيدةً بصورة كبيرة في المشروعات التجارية، وفيما يلي أكثر تلك الخوارزميات شيوعًا: خوارزمية ابيروري Apriori algorithm خوارزمية إيكلات Eclat algorithm 8. خوارزميات الشبكة العصبية الاصطناعية Artificial Neural Network Algorithms هذا النوع من الخوارزميات مستلهم من الشبكات العصبية الحيوية الموجودة في دماغ الإنسان، وهي خوارزميات مستخدَمة بكثرة لحل مشاكل التصنيف والتوقع، ولكن ذلك النوع هو بالأحرى مجال فرعي من مجالات تعلم الآلة؛ وذلك لكثرة عدد الخوارزميات واختلافها تبعًا لمشاكلها وتطبيقاتها، وفيما يلي أشهر خوارزميات هذا النوع: خوارزمية بيرسبترون Perceptron الانتشار الخلفي Back-Propagation شبكة هوبفيلد Hopfield Network الهبوط التدريجي العشوائي Stochastic Gradient Descent خوارزمية بيرسبترون متعددة الطبقات Multilayer Perceptrons واختصارًا MLP شبكة وظيفة الأساس الشعاعي Radial Basis Function Network واختصارًا RBFN 9. خوارزميات التعلم العميق Deep Learning Algorithms لقد فرقنا بين خوارزميات التعلم العميق وخوارزميات الشبكات العصبية لكون خوارزميات التعلم العميق هي النسخة الأحدث والمطوَّرة من خوارزميات الشبكات العصبية، فذلك النوع من الخوارزميات يعنى بتطوير الشبكات العصبية الأكثر تعقيدًا من نظيراتها الأخرى، فهي تعمل على تطوير شبكات معقدة تتعامل مع بيانات ضخمة ومعقدة مثل الصور والصوتيات والنصوص والمقاطع المرئية ونحوها، وفيما يلي مجموعة من أشهر خوارزميات التعلم العميق: الشبكات العصبية التلافيفية Convolutional Neural Network واختصارًا CNN شبكات الذاكرة طويلة-قصيرة المدى Long Short-Term Memory Networks واختصارًا LSTMs المشفرات الآلية المرصوصة Stacked Auto-Encoders الشبكات العصبية المتكررة Recurrent Neural Networks واختصارًا RNN شبكة المعتقد العميق Deep Belief Networks واختصارًا DBN آلة بولتزمان العميقة Deep Boltzmann Machine واختصارًا DBM 10. خوارزميات تقليص الأبعاد Dimensionality Reduction Algorithms تقلِّص هذه الخوارزميات أبعاد البيانات لمحاولة تصنيف وتجميع البيانات المتشابهة تمامًا مثل خوارزميات العنقدة، وتلك الخوارزميات مفيدة في فهم البيانات بصورة أفضل حتى يُعدَّل على البيانات قبل استخدامها مرةً أخرى في خوارزميات أخرى، كما تستخدَم لحل مشاكل التصنيف أو التوقع، وفيما يلي قائمة بأوسع خوارزميات هذا النوع استخدامًا: تحليل المكون الرئيسي Principal Component Analysis واختصارًا PCA انحدار المكون الرئيسي Principal Component Regression واختصارًا PCR انحدار المربعات الصغرى الجزئي Partial Least Squares Regression واختصارًا PLSR رسم خرائط سامون Sammon Mapping مطاردة الإسقاط /التنبؤ Projection Pursuit التحجيم متعدد الأبعاد Multidimensional Scaling واختصارًا MDS تحليل التمييز الخطي Linear Discriminant Analysis واختصارًا LDA تحليل التمييز الخليط Mixture Discriminant Analysis واختصارًا MDA تحليل التمييز المرن Flexible Discriminant Analysis واختصارًا FDA تحليل التمييز التربيعي Quadratic Discriminant Analysis واختصارًا QDA 11. خوارزميات الفرقة/المجموعة الواحدة Ensemble Algorithms تجمع هذه الخوارزميات نتائج نماذج مختلفة ضعيفة في نتيجة واحدة، إذ يُدرَّب كل نموذج عليها بصورة مستقلة ثم تجمع هذه الخوارزمية تلك التوقعات لتكوين توقع واحد جديد شامل، وفيما يلي قائمة من أبرز هذا النوع من الخوارزميات: التعزيز Boosting خوارزمية تجميع Bootstrap وهي بالإنجليزية Bootstrap aggregating أو تسمى في كثير من الأحوال بالتعبئة Bagging خوارزمية AdaBoost الغابة العشوائية Random Forest التعميم المرصوص/المكدس Stacked Generalization ويدعى أحيانًا التراص Stacking خوارزمية المتوسط الموزون Weighted Average وأحيانًا تدعى المزج Blending أشجار الانحدار المعزز بالتدرج Gradient Boosted Regression Trees والذي يدعى أحيانًا GBRT اختصارًا في بعض المراجع آلات تعزيز التدرج Gradient Boosting Machines وتدعى أحيانًا GBM اختصارًا. وبالرغم من وجود الكثير من الخوارزميات الأخرى، إلا أن ذلك يغطي معظم ما يراه مهندس تعلم الآلة من الخوارزميات في عمله اليومي في وقتنا الحاضر، وأغلب الظن أنّ القوائم السابقة ستزيد وتتغير بصورة كبيرة خلال السنوات القادمة، إذ أنّ الأعمال البحثية في كثير من جامعات وشركات العالم قائمة وفي تنافس شديد ومتسارع. إليك الصورة التالية التي تجمع كل خوارزميات تعلم الآلة في مكان واحد لتبقى لك مرجعًا: اقرأ أيضًا الفصل السابق: نظرة سريعة على مجال تعلم الآلة المفاهيم الأساسية لتعلم الآلة أدوات برمجة نماذج تعلم الآلة خطوات تنفيذ مشروع عن تعلم الآلة في بايثون: الجزء الأول
  3. بدأنا في هذه السلسلة برمجة الذكاء الاصطناعي تعريف الذكاء الاصطناعي وارتابط البرمجة والذكاء الاصطناعي ثم تحدثنا عن البرمجة وأساسيات البرمجة بلغة بايثون، وسنكمل في هذا المقال الحديث بالتفصيل عن تخصص تعلم الآلة تحديدًا الذي يندرج ضمن الذكاء الاصطناعي. تعريف تعلم الآلة Machine Learning عرَّف البروفسور توم ميتشل Tom Mitchell -وهو عالم حاسوب أمريكي وبروفيسور في جامعة Carnegie Mellon في الولايات المتحدة- تعلم الآلة بالتعريف التالي: لا بأس إذا بدا ذلك التعريف غامضًا قليلًا، إذ سنشرحه أثناء الحديث عن الخطوات التطبيقية وعملياتها في برمجة تعلم الآلة، كما سيُعرَّف كل رمز من تلك الرموز المذكورة في التعريف بشيء من التفصيل لاحقًا لنرى موقع تلك الرموز من الجانب التطبيقي. إنّ نماذج تعلم الآلة ما هي إلا خوارزميات بُنيت على قواعد الجبر الخطي والتفاضل والتكامل وعلم الإحصاء؛ ولذلك فإنّ فروع تلك الرياضيات مهمة لدارسي تعلم الآلة ليُؤهلوا لفهم أفضل لما يحدث وراء الأحداث ولفهم ما تفعله الخوارزميات، وبالطبع ستكون محاولة فهم لماذا تعمل تلك الخوارزميات بالأمر الشاق جدًا، وذلك تخصص باحثي الذكاء الاصطناعي القائمين باختراع وتطوير تلك الخوارزميات؛ أما نحن المبرمجين فما نريده لا يزيد عن فهم آلية عمل تلك الخوارزميات. يُعَدّ تعلم الآلة Machine Learning فرعًا من فروع علم الذكاء الاصطناعي، وبالأحرى هو إحدى طرق صناعة الذكاء من بين طرق متعددة وهو الأكثر شيوعًا في العقد الحالي مع أنّ الأبحاث في ذلك العلم كانت قد بدأت منذ القرن الماضي بالفعل، لكن لم تر تلك الخوارزميات النور لعدم وجود إمكانيات حاسوبية في ذلك الوقت تتيح تنفيذ تلك الخوارزميات، فالحواسيب لم تكن قادرة على تنفيذ هذه المهام، كما أنّ الأمر يتطلب الكثير والكثير من البيانات التي لم تكن لتتوفر بالصورة المطلوبة في ذاك الوقت. يدعى ذلك العلم في وسط الرياضيات باسم التعلم الإحصائي Statistical Learning أو التحليلات التنبؤية Predictive Analytics، ويسعى ذلك العلم ببساطة إلى استخراج المعلومات من البيانات السابقة ليتنبأ معلومات أخرى بصورة دقيقة لم يتعرف عليها من قبل. نرى تطبيقات تعلم الآلة واضحةً في حياتنا اليومية على الإنترنت، إذ تُستخدَم خوارزميات تعلم الآلة في اليوتيوب ليرجِّح المقاطع أثناء تصفحك لمشاهدتها، كما أنّ أمازون تستخدِمها لترجيح المنتجات؛ أما فيسبوك فيستخدِمها في تحديد وجهك ووجه أصدقائك في صوركم معًا، فعندما تتصفح أحد تلك المواقع فستجد على الأرجح في كثير من أجزائها مهامًا تستخدِم فيها إحدى خوارزميات تعلم الآلة، ولكن بعيدًا عن تلك المواقع والمجالات الترفيهية والتجارية، فإنّ لمجال تعلم الآلة أثر عظيم في المجالات العلمية الأخرى، إذ يُستخدَم في تحليل سلاسل الحمض النووي وفي فهم النجوم وإيجاد الكواكب البعيدة وتشخيص الأمراض وإيجاد الأدوية والكثير من الأمور الأخرى. لا يعني ذلك بالضرورة أنّ كتابة أيّ برنامج تعلم آلة هو حصرًا للمشروعات الكبيرة المؤثرة في العالم، فيمكن الاستفادة من برامج تعلم الآلة على المستوى البسيط، إذ تستخدِم الكثير من الشركات الصغيرة والمتوسطة نماذج تعلم الآلة في اتخاذ القرارات المستقبلية الصحيحة لأعمالها، فلتلك النماذج استخدامات كثيرة منها البسيط ومنها المعقد والكبير. حتى هذا اليوم معظم التطبيقات الذكية التي تُصنَع دون تعلم الآلة تدعى بالأنظمة الخبيرة، وهي برامج تُستخدَم فيها قواعد اتخاذ القرارات، وهي تُستخدم في معالجة البيانات ومدخلات المستخدِم ثم محاولة كتابة تعليمات شرطية تؤدي إلى ناتج ما، كما تحتاج تلك النظم إلى معرفة دقيقة للغاية بالمهمة المطلوبة، لكن لن يكون البرنامج دقيقًا إلى حد كبير في كثير من الأحوال، كما أنه ستُعاد كتابة أجزاء كبيرة من البرنامج أو حتى إعادة كتابة البرنامج كليةً لإجراء أيّ تعديل بسيط على المنطق الأساسي في البرنامج. لن تنجح الأنظمة الخبيرة في القيام بالمهام المطلوبة في كثير من الأحوال، فلا يمكن على سبيل المثال لمثل تلك الأنظمة إجراء مهمات متعلقة بالصور مثل تحديد الوجه أو تحديد الكائنات الموجودة في الصور؛ وذلك لأنه يكاد يكون من المستحيل على البشر التوصل إلى آليات وشروط محددة يستطيع كتابتها للحاسوب ليحدد بها خصائص الوجه أو تحديد خصائص كائنات معينة في الصور، إذ أنّ طريقة معالجة البشر للصور في الدماغ تختلف عن الحاسوب، فمشكلة تحديد الوجوه البشرية في الصور تلك ظلت غير محلولة حتى عام 2001م، إذ جرى صناعة نموذج تعلم آلة يستخدم الكثير من صور الوجوه البشرية لتحديد الخصائص التي يمكن عن طريقها تحديد الوجه البشري في الصور. كما أوردنا فإن تعلم الآلة هو تخصص فرعي من تخصصات الذكاء الاصطناعي، ولكن حتى تعلم الآلة ينقسم إلى تخصصات فرعية أخرى مثل التعلم العميق Deep Learning وهو المختص بحل المشكلات والتطبيقات المعقدة عن طريق محاكاة شبكة عصبية من الدماغ البشري ويعمل ذلك الفرع على حل مشكلات مثل تطبيقات التعرف على الوجوه في الصور وغيرها. يعتمد تعلم الآلة اعتمادًا كبيرًا على البيانات الضخمة Big Data وجودة تلك البيانات؛ ولذلك يتداخل تخصص تعلم الآلة مع تخصص آخر يدعى علم البيانات Data Science، والحدود بين العلمين غير واضحة بصورة دقيقة، إلا أننا نستطيع القول بأن تعلم الآلة يعتمد على علم البيانات وليس العكس، إذ يستطيع عالم البيانات التعامل مع البيانات الكبيرة وتحسين جودتها عبر الأساليب المدروسة مسبقًا حتى تكون البيانات جاهزةً بالفعل لتدريب نموذج تعلم الآلة المراد صنعه، وذلك عادةً ما يفعله مهندس تعلم الآلة، إذ أنه في كثير من الأحوال وخاصةً في الشركات الصغيرة لا يوجد متخصص في علوم البيانات لتحسينها قبل إرسال البيانات إلى مهندس تعلم الآلة للتدريب النموذج، ولا بأس إذا بدت الأمور غير واضحة لك حتى الآن. توضح الصورة التالية العلاقة بين الذكاء الاصطناعي Artificial Intelligence وتعلم الآلة Machine Learning والتعلم العميق Deep Learning. أما الصورة التالية، فستستطيع من خلالها فهم الترابط والتداخل بين علم البيانات Data Science وعلوم الذكاء الاصطناعي المختلفة. عرَّف أرثر صامويل Arthur Samuel في عام 1959م تعلم الآلة بأنه حقل الدراسة الذي يعطي للحواسيب القدرة على التعلم دون أن تكون مبرمجةً صراحةً، ونجد أنّ ذلك التعريف معبر للغاية، وقد ظل ذلك التعريف مستخدمًا حتى اليوم، ولكن مع ذلك فإنه قد يُفهم التعريف بصورة خاطئة عند كثير من غير المتخصصين، إذ أنّ أرثر لم يقصد أنه بإمكان الآلات اتخاذ القرارات بدون برمجة مسبقة على الإطلاق لأن ذلك لم يحصل، إذ أنّ مجال تعلم الآلة يعتمد اعتمادًا كبيرًا على البرمجة، ولكن يقصد بذلك أنّ الآلة تستطيع التعلم من البيانات لاستنباط واستنتاج أنماط من خلال علم الإحصاء وخوارزميات الرياضيات المكتوبة بلغة البرمجة لتستطيع من خلالها التوقع أو أداء مهمة ما بناءً على برمجة مسبقة، ولكنها ليست برمجة صريحة بالمعنى التقليدي الذي يحدد مخرجات البرامج بصورة نوعية وكمية. كان أرثر صامويل من أوائل مَن استخدم مصطلح تعلم الآلة Machine Learning وذلك في الورقة البحثية التي نشرها في العام نفسه 1959 والذي عرف فيه المصطلح بعنوان Some Studies in Machine Learning Using the Game of Checkers، وهو يحاول في تلك الورقة إثبات أنه بإمكان الآلات تعلم لعب الألعاب وبمستوى أفضل من مبرمج الآلة نفسه. الفرق بين البرمجة الاعتيادية وبرمجة تعلم الآلة تختلف برمجة الحاسوب الاعتيادية وبرمجة نماذج تعلم الآلة في أوجه كثيرة سنناقشها في هذا القسم. البيانات الضخمة Big Data لا تحتاج البرمجة الاعتيادية في أغلب الأحوال إلى بيانات ضخمة لصناعة تطبيقاتها، فبالرغم من كونها قد تخزِّن تلك البيانات الضخمة واستخدامها، إلا أنها ليست ضرورية، ولكن تحتاج برمجة تعلم الآلة إلى البيانات الضخمة بصورة أساسية حسب ما يراد برمجته، فمثلًا في حال برمجة نموذج تعلم آلة -أو بالأحرى نموذج تعلم عميق- يحدِّد الوجوه البشرية في الصور، فسنحتاج إلى كثير من الصور التي تحتوي على وجوه بشرية حصرًا وبصورة واضحة حتى يتعلم منها النموذج خصائص الوجه البشري، أو مثلًا إذا أردنا برمجة نموذج تعلم آلة يتوقع قيمة سعر منزل في منطقة ما، فسنحتاج إلى بيانات تحتوي على خصائص العقارات وأسعارها في تلك المنطقة لنمرر بعدها تلك البيانات للنموذج وليتعلم منها خصائص كل عقار وسعره حتى يستطيع بعد ذلك توقع سعر أيّ عقار في تلك المنطقة بناءً على خصائصه حتى ولو لم يكن ذلك العقار من ضمن العقارات التي مررناها إليه مسبقًا، وبناءً على ذلك فإنّ الحاجة إلى البيانات الكبيرة في نماذج تعلم الآلة ضروري وأساسي للغاية، كما أنّ تحسين جودة تلك البيانات أيضًا ضروري. الدقة وآلية العمل تُمرَّر مدخلات المستخدِم في البرمجة الاعتيادية، ثم إجراء عمليات محددة مسبقًا من قبل البرنامج ثم إرجاع الخرج إلى المستخدِم مرةً أخرى؛ أما في تعلم الآلة فالأمر يختلف قليلًا، إذ تُستقبَل في كثير من الأحيان مدخلات من المستخدِم فعلًا، مع ذلك فإنه لا تُجرى عمليات محددة مسبقًا من قِبَل البرنامج، وإنما تُمرَّر المدخلات إلى الخوارزمية -أي النموذج- الذي دُرِّب من قبل حتى يتوقع الخرج الذي سيعاد مرةً أخرى إلى المستخدِم، ولنأخذ المثال السابق للعقارات لفهم ذلك بصورة واضح. لا يسعنا سوى استخدام قواعد اتخاذ القرارات لتحديد سعر العقار في البرمجة الاعتيادية، فإذا طلبنا مثلًا من المستخدِم إدخال عدد غرف العقار ومساحته بالمتر المربع، فكل ما يسعنا فعله هو كما يلي: إذا كان عدد الغرف أكبر من غرفتين والمساحة أكبر من 100 متر مربع، فسيكون السعر 100 ألف دولار أمريكي، فيما عدا ذلك فسيكون السعر 80 ألف دولار أمريكي، وبذلك فإنه لمن المستحيل أن يُعيد البرنامج أية قيمة سوى 100 ألف أو 80 ألف فقط، وهي القيم المبرمَجة بالفعل، ولكن مع ذلك فيمكننا تعقيد المثال بصورة أكبر حتى يُحسَب سعر أساسي تبعًا للمتر المربع ثم إجراء بعض العمليات الحسابية بناءً على عدد الغرف مثلًا، لكن في النهاية سيكون سعر المتر عددًا محددًا بالفعل من مجموعة أعداد محدَّدة من قبل المبرمِج. أما في الواقع، يختلف سعر المتر المربع من شارع إلى آخر ومن مبنى إلى آخر، في حين أن الأمر يختلف في نماذج تعلم الآلة لأننا في تلك الحالة ننشئ نموذجًا بناءً على إحدى الخوارزميات التي نختارها تبعًا للمشكلة المراد حلها، ثم ندرِّب تلك الخوارزمية على بيانات العقارات في تلك المنطقة، إذ ستحتوي تلك البيانات على مساحة وعدد غرف كل عقار ثم سعر كل عقار، بعد ذلك نختبر النموذج لمعرفة مدى دقة توقعه لأسعار العقارات عند إدخال بيانات عقار آخر، أي المساحة وعدد الغرف. الحدود النوعية كما أوردنا من قبل في هذا المقال أنه توجد الكثير من المشكلات التي من المستحيل واقعيًا تجسيدها عبر البرمجة التقليدية مثل مشكلة التعرف على الوجوه في الصور ومشكلة الترجمة من لغة إلى أخرى بصورة دقيقة ومشكلة التعرف على الصوت وتحويله إلى نص والعكس؛ أما في تعلم الآلة فالأمر مختلف، إذ أنّ تلك المهام التي تُعَدّ مستحيلةً في البرمجة العادية، أصبحت الآن متواجدة تواجدًا فعليًا في حياتنا اليومية، ويضيف الباحثون كل عام نماذج وخوارزميات جديدةً لحل مشكلات جديدة لم تحل من قبل أو خوارزميات لحل مشاكل موجودة لكن بجودة أعلى عبر تعلم الآلة حتى وصلنا اليوم إلى نماذج وتطبيقات مبهرة مثل تطبيق الدردشة ChatGPT وتطبيقات تصميم الصور بناءً على كتابة وصف لها مثل نموذج DALL-E. لنأخذ تلك الأوقات التي نتصفح فيها اليوتيوب مثالًا لتوضيح أكثر، فقد تشاهد في أحيان كثيرة مقاطعًا رياضيةً لدوري كرة القدم الإسباني ودوري كرة القدم الإنجليزي، وستلاحظ في تلك الحالة أن الموقع يرشِّح لك دائمًا ذلك النوع من المقاطع في كل فرصة، مع ذلك فلم يتخذ مبرمِجي الموقع ذلك القرار صراحةً، إذ أنهم لم يكتبوا في برمجية الموقع تعليمة مثل أنه إذا شاهد المستخدِم ذلك المقطع رقم 42 وذاك المقطع رقم 55، فاعرض له المقطع 43 و56 ، وإنما استخدموا إحدى خوارزميات تعلم الآلة التي ترشِّح المقاطع المشابهة لتلك المقاطع التي تشاهدها باستمرار بناءً على ما تقضيه من وقت في مشاهدتها وبناءً على إعجاباتك على المقاطع وعلى تعليقاتك، …إلخ. وبذلك يتضح لنا جليًا مقصد أرثر صامويل في تعريفه لتعلم الآلة. أقسام تعلم الآلة تنقسم نماذج تعلم الآلة إلى عدة أقسام تبعًا لطريقة عملها، إذ يوجد التعلم تحت إشراف Supervised Learning والتعلم دون إشراف Unsupervised Learning والتعلم المعزز Reinforcement Learning. التعلم تحت إشراف Supervised Learning أول قسم من أقسام تعلم الآلة هو التعلم تحت إشراف Supervised Learning ، ويُعرَف أيضًا بالتعلم الاستقرائي Inductive Learning، وهو من أشهر أقسام تعلم الآلة وأكثرها استخدامًا وأيسرها في الفهم والبرمجة، فالتعلم تحت إشراف عمومًا أسهل كثيرًا من التعلم دون إشراف Unsupervised Learning لأنه يعتمد على الاستقراء المبني على معرفتنا المسبقة بجميع خواص النموذج وعلى المدخلات والمخرجات المرادة. تُدرَّس الفكرة الأساسية في التعلم الاستقرائي بالفعل في مناهج الرياضيات في المرحلة الثانوية عادةً، والفكرة في الأمر كله إيجاد أفضل خط يمر بين نقاط عدة بين المدخلات ومخرجاتها لتوقع مخرجات لمدخلات جديدة، وبرغم كون الأمر يبدو مبهمًا، إلا أنه في غاية البساطة وسيتضح ذلك لاحقًا. يعمل التعلم الاستقرائي رياضيًا على دالة تستقبل مدخلات تُدخَل في معادلة جبرية لاستخراج الناتج المتوقع، وتلك المعادلة تضرب المدخلات وتجمعها مع ثوابت أخرى تمثِّل أفضل قيم ممكنة لتوقع الناتج الأقرب للواقع، وتلك الدالة تدعى دالة الفرضية Hypothesis Function وتدعى أيضًا بالمتوقِّع Predictor، فهدف النموذج في النهاية هو تعديل ثوابت دالة الفرضية لهدف تقريب ناتجها مع الناتج الحقيقي، ولمحاولة تبسيط المعنى بغض النظر عن دقة المثال، فلنفترض وجود الدالة الآتية: f(x) = x + 1 في حال كان المتغير x يساوي 1 فإن الدالة ستنتج 2، وفي حال كان المتغير x يساوي 2 فإن الدالة ستنتج 3، …إلخ، ثم بعد ذلك لنفترض أننا نحاول الإتيان بفرضية ناتجها مقارب جدًا إلى ناتج الدالة أعلاه، إذ توجد عدة فرضيات محتملة مثل: f(x) = x + 0.9 f(x) = x + 0.5 f(x) = x + 0.99 عند محاولة استخدام الفرضية الأولى في حالة كان المتغير x يساوي 1 فسيكون الناتج 1.9، وهو يقارب الناتج الحقيقي حال كون x يساوي 1 في الدالة، إذ كان الناتج الحقيقي للدالة هو 2 وناتج الفرضية 1.9، وهما ناتجان متقاربان للغاية، كما يبدو الأمر أكثر تقاربًا عند استخدام الفرضية الثالثة، ففي حال كان المتغير x يساوي 1 فسيكون الناتج 1.99، وهو أقرب للناتج الحقيقي من 1.9؛ وبناءً على ذلك نستنتج أنّ الفرضية الثالثة أفضل من الفرضية الأولى. في حين أن الفرضية الثانية هي الأقل دقة، فعند تطبيقها حال كون المتغير x يحمل القيمة 1 فإن الناتج سيكون 1.5، وهو الأبعد عن الناتج الحقيقي من الفرضية الأولى والفرضية الثالثة. إذًا الفرق بين الفرضيات الثلاثة والتي تمثِّل دورًا مفصليًا في جودة الناتج هو العدد الرقم الأخير، وهو في حالة التعلم الاستقرائي أحد الثوابت Constants في المعادلة والتي نحاول الإتيان بأفضل قيمة ممكنة له حتى تنتج دالة الفرضية ناتجًا مقاربًا للحقيقة بأكبر قدر ممكن، في مثالنا السابق اخترنا الفرضية الثالثة والتي تمثل أعلى دقة، وبالتالي يمكن تجربة دالة تلك الفرضية على مدخلات أخرى، فإذا كان المتغير x يساوي 2 مثلًا، فسيكون ناتج الفرضية 2.99 وهو قريب جدًا من ناتج الدالة الأصلية. إنّ محاولة الوصول إلى أفضل قيم للثوابت في دالة الفرضية هي ما تكون عن طريق جزء التعلم Learning في التعلم الاستقرائي، وتوجد تقنيات وطرق رياضية وخوارزميات مختلفة للوصول إلى القيم المثلى لتلك الثوابت في علم التعلم الإحصائي Statistical Learning الذي أحلنا إليه مسبقًا، ولكون التعلم الاستقرائي أو التعلم تحت إشراف هو أسهل أنواع نماذج تعلم الآلة وأكثرها شيوعًا، ففي هذه السلسلة نحاول التركيز بصورة كبيرة على ذلك النوع من التعلم، وبالرغم من كون الأمر قد يبدو لك معقدًا ومبهمًا فإن ذلك طبيعي للغاية، إذ نهدف من هذا الفصل إلى تعريف بعض المبادئ فقط؛ أما شرح تلك النقاط فسيكون في الفصول القادمة. سنورد ثلاثة أمثلة على التعلم تحت إشراف وهي تحديد نوع السرطان في الصور الطبية إذا كان خبيثًا أو حميدًا، وتحديد إذا كانت عملية تحويل الاموال سليمةً أم مشبوهةً، وقراءة أرقام مكتوبة بخط اليد في القسم التالي، مع أنه توجد أمثلة أبسط على ذلك النوع من التعلم أيضًا، فمثلًا يمكننا محاولة استقراء راتب موظف عن طريق عدد سنين خبرته، إذ يكون عدد سنين الخبرة هو الدخل الوحيد لدينا في ذلك المثال وهو يمثل قيمة المتغير x في الدالة؛ أما راتب الموظف فهو ناتج الدالة. أمثلة أخرى من قبيل استقراء سعر شقة أو عقار في المناطق، أو تصنيف الرسائل البريدية إن كانت سليمة أم مزعجة. الجدير بالذكر هنا أنه يوجد نوعان رئيسيان من مشاكل التعلم تحت إشراف أولهما هو التوقع الخطي أو ما يدعى بالانحدار الخطي Linear Regression، وهي المشاكل التي يتم فيها توقع قيمة مستمرة Continuous Value، أي قيم غير محددة مثل ثمن عقار أو راتب موظف، وثانيهما التصنيف Classification، أو ما يدعى بالتوقع اللوجيستي أو الانحدار اللوجيستي Logistic Regression، وهي المشاكل التي تقرَّب فيها القيم المتوقعة إلى تصنيف محدد، مثل تصنيف الرسائل البريدية إذا كانت صحيحةً أو مزعجةً، أو تصنيف عمليات التحويل المالية إذا كانت سليمةً أو مشبوهةً، أو تصنيف صورة ما إذا كانت تحتوي على قطة أو كلب، أو تصنيف صورة ما إذا كانت تحتوي على وجه بشري أو لا. التعلم دون إشراف Unsupervised Learning القسم الثاني من أقسام تعلم الآلة هو التعلم دون إشراف Unsupervised Learning وهو أكثر تعقيدًا من التعلم تحت إشراف؛ وذلك لكون المعطيات لا تكون واضحةً تمامًا لنا في هذا النوع من التعلم، فنحن نسعى في ذلك النوع من التعلم إلى إيجاد أنماط مشتركة بين البيانات لتُصنَّف بشكل أو بآخر. سُمِّي ذلك النوع بالتعلم دون إشراف لعدم وجود ناتج حقيقي نقييم ونعلِّم النموذج حتى يصل إلى أقرب قيمة له، ففي الأمثلة الواردة في التعلم تحت إشراف كانت لدينا دالة تحتوي على معادلة تنتج قيمة، وتلك الدالة تنتظر معاملًا أو متغيرًا يدعى x، وعندما يساوي ذلك المتغير 1 فإن ناتج الدالة يكون 2، وبذلك يصبح الرقم 2 هو الناتج الحقيقي المراد الوصول إلى أقرب قيمة له عن طريق دالة الفرضية، إذ كان لدينا في التعلم تحت إشراف مدخلات ومخرجات يراد توقعها؛ أما في التعلم دون إشراف فلا توجد مخرجات أصلًا، أي لا يوجد الناتج 2، فنحن فقط لدينا مدخلات مختلفة مثل x عندما تساوي 1 وx عندما تساوي 2 وx عندما تساوي 3 …إلخ، وبالرغم من كون تلك الأمثلة ليست دقيقةً للغاية، إلا أنها فقط لمحاولة إيضاح فكرة عامة عن التعلم دون إشراف والفرق بينه وبين التعلم تحت إشراف. يُعَدّ كل من تحويل مجموعة البيانات Dataset Transformation وخوارزميات التجميع Clustering Algorithms تقنيتَين من تقنيات التعلم دون إشراف؛ أما الأولى فهي عملية تسعى إلى تحويل البيانات التي يُدرَّب النموذج عليها حتى تكون في صورة أكثر إفادة وسهولة في التعامل، وقد يتم ذلك حتى يستطيع المبرمج فهم البيانات بشكل أفضل، أو حتى يتم تهيئة البيانات لخوارزمية تعلم آلة أخرى، ومن أهم تطبيقات ذلك النوع من الخوارزميات -أي خوارزميات تحويل هيئة مجموعة البيانات- هو تقليص الأبعاد Dimensionality Reduction، فقد تحتوي مجموعة البيانات على أبعاد كثيرة مختلفة يصعب التعامل معها، والبعد في علوم البيانات وتعلم الآلة يشير إلى الميزة أو الخاصية في البيانات والتي أشرنا إليها عند الحديث عن هندسة الميزات أو هندسة الخواص Feature Engineering، وهي ببساطة العمود إذا رسمنا البيانات على هيئة جدول، ففي حال كان لدينا مجموعة بيانات لموظفي شركة ما على سبيل المثال، وكانت تلك البيانات تحتوي على العمر والاسم وتاريخ الميلاد والمؤهل الدراسي لكل موظف، فإن كل خاصية من تلك الخواص هي بُعد من الناحية التقنية والرياضية، أي أن الاسم بُعد والعمر بُعد وتاريخ الميلاد بُعد والمؤهل الدراسي بُعد، …إلخ. إن هدف تحويل مجموعة البيانات هو تقليل عدد الخواص في البيانات عبر تعلم الأنماط المشتركة، وإنّ لتلك التقنية استخدامات عدة من أبرزها تقليل عدد الأبعاد إلى بعدين فقط لتسهيل عملية تصوير البيانات في رسوم بيانية، وهناك أيضًا بعض التطبيقات الأخرى مثل استخراج كلمة مفتاحية واحدة تعبِّر عن كل مقالة من مجموعة من المقالات بناءً على محتواها؛ أما خوارزميات التجميع فهي تهدف إلى إيجاد البيانات المتشابهة والمختلفة للتمييز بين كل مجموعة من البيانات، فيمكن مثلًا تقسيم مجموعة من الصور حسب الأشخاص الموجودين فيها، إذ تُقسَّم مجموعة من الصور لوجود الشخص 1 فيها ومجموعة أخرى من الصور لوجود الشخص 2 فيها، وعلى الرغم من أنّ الخوارزمية لا تعرف هذا ولا ذاك، ولكنها تقسِّم مجموعة البيانات إلى مجموعات أصغر بناءً على التشابهات والاختلافات الموجودة في كل صورة. التعلم شبه الخاضع للإشراف Semi-supervised Learning التعلم شبه الخاضع للإشراف Semi-supervised Learning هو مزيج بين القسمين السابقين، لذلك قد لا يُذكر في بعض الكتب والمراجع، وهو عن التعامل مع البيانات التي يحتوي بعضها وليس كلها على مخرجات، فقد يكون لدينا مثلًا بيانات لموظفي شركة ما ويوجد لدينا خواص مثل الاسم والعمر والمؤهل الدراسي لكل موظف، ومع ذلك فإن بعض هؤلاء الموظفين لدينا عنهم معلومات إضافية مثل الراتب، وبذلك أصبح لدينا معلومات إضافية لبعض الموظفين. أحد الأمثلة التطبيقية على ذلك النوع هو خدمات رفع الصور مثل خدمة Google Photos، إذ أنه بعد رفع صورك مع أفراد العائلة، ستتمكًن الخدمة من معرفة أن الشخص س ظهر في الصورة 1 و3 و6؛ أما الشخص ع ظهر في الصورة 2 و3 و4، وهذا الجزء دون إشراف، ولكن قد تتمكن الخدمة من معرفة أنّ الشخص س، هو في الحقيقة اسمه زيد، ولكنها لا تعرف اسم الشخص ع، إذ لا توجد لديها تلك المعلومة، وتستخدِِم الخدمة تلك المعلومة الإضافية لتوفر لمستخدميها خدمة البحث في الصور عن طريق الأسماء. التعلم المعزز Reinforcement Learning يُعَدّ التعلم المعزز أو التعلم التعزيزي Reinforcement Learning أكثر أنواع تعلم الآلة طموحًا وأكثرها صعوبةً وتعقيدًا بالطبع وهو وحش مختلف تمامًا عن باقي الأنواع، إذ في التعلم المعزز يُصنَع نظام تعلم يدعى بالوكيل Agent، وفي هذا الإطار يلاحظ الوكيل ما حوله في البيئة ثم يتخذ قرارًا بإجراء فعل ما، ويحصل بناءً على ذلك الفعل على نقاط مكافأة Rewards أو على نقاط عقوبة Penalties، وعن طريق تلك النقاط يجب على الوكيل تحديد الاستراتيجية المثلى للتعامل مع المواقف واتخاذ القرار الصحيح، وتلك الاستراتيجية التي يطورها الوكيل تدعى سياسة العمل Policy، والأمر هنا كما تلاحظ أشبه بتعليم طفل صغير وما يجب أن يفعله وما لا يجب أن يفعله عن طريق نظام الثواب والعقاب. تُعَدّ طريقة تطوير سياسة العمل تلك هي أنواع الخوارزميات في التعلم المعزز وتدعى Policy-Based Algorithms أي الخوارزميات القائمة على السياسة، ولكن يوجد أنواع أخرى مثل القائمة على القيمة Value-Based والقائمة على النموذج Model-Based، …إلخ، والأمثلة التطبيقية على هذا النوع من التعلم شتى، إذ أنّ الكثير من الروبوتات مبرمجة عن طريق ذلك النوع من نماذج تعلم الآلة لتتعلم كيفية المشي، ومثال آخر هو برنامج ألفا جو Alpha Go الذي تصدر الأخبار عام 2016م حين هزم بطل العالم في لعبة جو اللاعب لي سيدول Lee Sedol، وهذا البرنامج قد صُنِع من قِبَل شركة Deep Mind وهي شركة بريطانية متخصصة في أبحاث الذكاء الاصطناعي تأسست عام 2010م وقد استحوذت عليها شركة جوجل عام 2014م والتي بدورها تغير اسمها إلى Alphabet عام 2015م، لتصبح شركة جوجل شركات Alphabet بجانب Deep Mind. تعلَّم برنامج ألفا جو لعب تلك اللعبة عن طريق التعلم المعزز، إذ حلل البرنامج ملايين الاحتمالات في اللعبة ثم لعب ضد نفسه مرارًا وتكرارًا عبر نظام نقاط الثواب والعقاب، حتى أنه طوَّر السياسة المناسبة Policy والتي استخدمها في اللعب ضد بطل العالم في اللعبة، ونذكر هنا أن البرنامج أثناء اللعب ضد بطل العالم لم يكن في تلك المرحلة في إطار التعلم، وإنما كان في إطار استخدام السياسة التي طورها أثناء تعلمه السابق لمباراته ضد بطل العالم. ودونًا عن باقي أقسام تعلم الآلة، فإن التعلم المعزز لا يحتاج بصورة ضرورية إلى بيانات مسبقة للتعلم منها، وإنما يتعلم من خلال خبرته الشخصية عبر الثواب والعقاب والتجربة والخطأ كما أوردنا، والهدف النهائي لهذا النموذج هو تعظيم نقاط المكافأة على المدى الطويل، ونلاحظ أيضًا من أهم الاختلافات بين التعلم المعزز وباقي أقسام خوارزميات تعلم الآلة أنّ التعلم المعزز يقوم بالتعلم باستمرار دون توقف، في حين أنّ نماذج التعلم تحت إشراف والتعلم دون إشراف تصل إلى نقطة محددة بعد التعلم من البيانات الممررة إليها لتصل إلى ذروة ما تستطيع الوصول إليه من معدل الدقة، وعند تلك النقطة يقف النموذج عن التطور والتعلم. إنّ من أفضل طرق تخيل أمر التعلم المعزز هو تخيل لعبة حاسوبية يلعبها الوكيل Agent، إذ أنه لديه مهمة طويلة المدى مع مجموعة من الأفعال التي من الممكن أن يتخذها عند الحاجة، ولكنه عند القيام بالفعل الخاطئ، سيُخصَم من نقاطه في اللعبة، في حين أنّ أداء الفعل الصحيح سيزيد من نقاطه، وبذلك هو يقوم بتطوير وتصقيل مهاراته في اللعبة واستراتيجيته في ممارستها يومًا بعد يوم بناءً على البيانات المسبقة التي تمثلها الخبرة التي اكتسبها أثناء لعبها آلافًا من المرات، ومن التقنيات الخاصة بالتعلم المعزز هي مقايضة الاستكشاف والاستغلال Exploration and Exploitation Tradeoff وعمليات قرار ماركوف Markov Decision Processes أو MDPs اختصارًا في بعض المراجع، بالإضافة إلى تعلم كيو Q-Learning، والتعلم المعزز العميق Deep Reinforcement Learning. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن مشاكل يحلها تعلم الآلة سنتعرف الآن على التطبيقات التي يمكن حلها تبعًا لكل قسم من أقسام تعلم الآلة التي أشرنا إليها للتو. تحديد نوع السرطان إن كان خبيثا أو حميدا يمكن لنماذج تعلم الآلة بناءً على الصورة الطبية تحديد ما إذا كان يوجد سرطان خبيث أو حميد، وستحتاج لإجراء ذلك إلى عدد كبير من الصور الطبية مع تحديد كل صورة كون السرطان الموجود فيها خبيثًا أم حميدًا، وقد تحتاج لذلك إلى رأي طبيب متخصص، بعد تمرير تلك الصور مع نوع السرطان في كل صورة إلى نموذج تعلم الآلة، فسيكون النموذج قادرًا على استقبال صورة طبية جديدة تمامًا ليحدِّد نوع السرطان بناءً على ما تعلمه من قبل في النماذج التي مررتها إليه، ويندرج ذلك النوع من النماذج تحت تصنيف التعلم تحت إشراف. تحديد ما إن كانت التحويلات الائتمانية صحيحة أم مشبوهة سنحتاج في ذلك النموذج إلى بيانات فيها الكثير من عمليات تحويلات الأموال عبر البطاقات الائتمانية مع إدخال قيمة تدل على ناتج عملية التحويل إذا كانت عمليةً صحيحةً أم مشبوهةً، وبعد تدريب النموذج على تلك البيانات، سيستطيع النموذج استقبال بيانات عملية تحويل جديدة ثم تحديد ما إذا كانت العملية مشبوهةً أم صحيحةً بناءً على خبرته السابقة التي تعلمها من البيانات. قراءة أرقام مكتوبة بخط اليد نحتاج إلى كثير من الصور لتدريب ذلك النموذج، والتي تحتوي على أرقام مكتوبة بخط اليد، مع إدخال الرقم الموجود في كل صورة ضمن البيانات ليتم التدريب، إذ يصبح لدى النموذج خبرةً بقدر كاف لاستقبال صور جديدة لا يعرف الأرقام الموجودة فيها ليستخرجها هو، ارجع مثلًا إلى مقال بناء شبكة عصبية للتعرف على الأرقام المكتوبة بخط اليد باستخدام مكتبة TensorFlow لتطلع على مثال عملي. نلاحظ أن المشترك بين التطبيقات الثلاثة السابقة هو وجود مدخلات ومخرجات في البيانات الممررة إلى النموذج، ففي المثال الأول نحن لا نقوم فقط بتمرير العديد من الصور الطبية، بل نقوم بتحديد ما إن كان السرطان الموجود فيها حميدًا أم خبيثًا حتى يتعلم النموذج أنّ الصورة 1 فيها سرطان خبيث، والصورة 2 فيها سرطان حميد، والصورة 3 فيها سرطان حميد، …إلخ، وبعد التدريب تصبح لدينا إمكانية تمرير الصورة الطبية فقط دون تحديد نوع السرطان، إذ سيفعله النموذج بالفعل بناءً على ما تعلمه مسبقًا من الصور والمخرجات التي مررتها إليه. وكذلك الأمر في النموذج الثاني، إذ لا نمرر بيانات عمليات التحويل فقط، وإنما نمرر معلومة إضافية مع كل عملية تحويل -وهو الخرج- وهي إذا كانت تلك العملية عمليةً سليمة أم عمليةً مشبوهةً ليُدرَّب النموذج عليها وليتعرف على أكبر عدد ممكن من العمليات الصحيحة والعمليات المشبوهة، وبعد مرحلة التدريب تلك نستطيع تمرير بيانات عملية تحويل جديدة كليًا -دون خرجها- لم يُدرَّب النموذج عليها، حتى يحدد النموذج ما إذا كانت العملية سليمةً أم مشبوهةً بناءً على ما تدرب عليه مسبقًا، وكذلك الأمر في المثال الثالث. يدعى ذلك النوع من النماذج التي نمتلك فيها المدخلات والمخرجات المراد توقعها بعد ذلك من النموذج بالتعليم تحت إشراف، ونلاحظ أنه بالرغم من كون التطبيقات الثلاثة تحت تصنيف واحد، إلا أنّ أمر جمع وتنقية تلك البيانات اللازمة لتدريب النموذج تتباين للغاية، إذ سيكون من الصعب جمع مثل تلك البيانات في المثال الأول، كما أنه لتحسين جودة البيانات وتنقيتها فإنه يجب استخدام خبير طبي في مثل تلك الصور، لكن الأمر يختلف في نموذج عمليات التحويلات المالية، إذ قد تكون تلك البيانات متاحةً من قِبَل بعض الشركات بالرغم من كونها بيانات غاية في الدقة والحساسية بسبب خصوصيتها؛ أما المثال الثالث فإنه بسيط للغاية ولا يتطلب متخصصين، إذًا أمر تطبيقات تعلم الآلة يختلف كثيرًا باختلاف مجال التطبيق، حتى وإن تشابهت في نوع الخوارزميات، وفيما يلي أمثلة على تطبيقات أخرى. تحديد المواضيع التي تتحدث عنها المقالات لنقل أنه لديك عدد كبير من المقالات المكتوبة التي تريد استخراج المواضيع المهمة التي تتحدث عنها كل مقالة من تلك المقالات، في تلك الحالة أنت فقط لديك المدخلات -أي نصوص المقالات- ولا يوجد لديك فكرة واضحة عن المخرجات، إذ لا تعرف ما هي تلك المواضيع ولا تعرف كم عدد الموضوعات الممكن إيجادها. ارجع إلى مقال تصنيف الشخصيات بالاعتماد على تغريداتهم العربية باستخدام التعلم العميق للاطلاع على مثال عملي مشابه. تقسيم العملاء حسب تفضيلاتهم الشرائية نريد في هذا النموذج تقسيم العملاء حسب تفضيلاتهم الشرائية، وفي تلك الحالة نحن نملك بيانات العملاء، لكننا مع ذلك لا نعرف ما هي تلك التقسيمات وما عددها، لا نعلم إذا كان لدينا مَن يفضِّل شراء الأدوات المنزلية أو من يفضِّل شراء الألعاب أو كليهما؛ ولذلك لا توجد لدينا مخرجات واضحة لهذا النموذج، وارجع إلى مقال استخدام خوارزميات العنقدة لتجزئة عملاء متجر إلكتروني للاطلاع على مثال عملي. نلاحظ في كلا التطبيقين السابقين أنه لدينا المدخلات ولا نمتلك مخرجات واضحة لا من حيث النوع ولا من حيث الكم؛ وهذه التطبيقات تندرج تحت قسم التعلم دون إشراف، لكن في كلتا الحالتين لاحظ أنه علينا الحصول على بيانات المدخلات وهي القاسم المشترك بين هذا وذاك، وفي كل الأحوال من الجيد تنظيم البيانات أو على الأقل تخيلها على صورة جدول، بحيث يحتوي كل صف على بيانات عينة كاملة، وكل عمود هو خاصية Property أو ميزة Feature، ففي مثال تحديد ما إذا كانت عمليات التحويل المالية صحيحةً أم مشبوهةً مثلًا، يمكننا تخيل كل صف على أنه عملية تحويل، وقد يكون لدينا عدة أعمدة مثل عمود يحتوي على بلد التحويل وعمود يحتوي على تاريخ التحويل، …إلخ. أهمية فهم وتحسين البيانات يعتمد تعلم الآلة بجميع نماذجه وخوارزمياته بصورة أساسية على البيانات بغض النظر إذا كانت مدخلات ومخرجات أو حتى مدخلات فقط؛ ولذلك تُعَدّ جودة البيانات وفهمها من أهم الأمور، إذ سيتعين عليك في بعض الأحيان إضافة خاصية أو حذف خاصية من البيانات، وهو ما يدعى بهندسة الخواص أو هندسة الميزات Feature Engineering أو Feature Extraction، وأحيان أخرى ستدمج بعض الخواص معًا بطريقة ما، وكل تلك الأمور نجدها في علم التعلم الإحصائي Statistical Learning الذي تم الإحالة إليه من قبل. تُعَدّ مرحلة فهم البيانات وتنقيتها وتحسينها من أهم المراحل، لأنه بناءً على ذلك تستطيع كمهندس تعلم آلة اختيار الخوارزمية المناسبة للبيانات من خوارزميات تعلم الآلة المتاحة والتي ستوفِّر أفضل دقة ممكنة تبعًا للمهمة التي تريد إجراءها من خلال النموذج الذي بُرمِج، كما أنّ جودة البيانات تلعب دورًا كبيرًا في معدل الدقة Accuracy Rate للنموذج، إذ يمكننا عن طريق تحسين جودة البيانات الوصول إلى دقة 99٪ بدلًا من دقة 97٪، ونستطيع الوصول إلى دقة 97٪ بدلًا من 80٪ عن طريق تعديل قيم معامِلات الخوارزمية المختارة Algorithm Parameters، ولذلك فإن فهمك للبيانات هو أول خطوة على طريق اختيار الخوارزمية وقيم المعاملات. يُعَدّ الجزء الخاص ببرمجة نماذج تعلم الآلة مجرد نقطة من صورة أكبر، وهي المشكلة المراد حلها، ولذلك من الضروري جدًا بقاء تلك الصورة الكبيرة في ذهنك دائمًا عند برمجة نموذج، فبناءً على تلك الصورة الكبيرة ستستطيع تحديد ما إذا كنت تمتلك البيانات الصحيحة أم لا، وتلك هي أول خطوة من الخطوات، ثم بعد ذلك ستستطيع تحديد ما يجب فعله أثناء مرحلة هندسة الخواص عبر تنقية وتحسين جودة البيانات، ثم بناءً على تلك الصورة ستستطيع تحديد الخوارزمية الصحيحة للتطبيق الذي تعمل عليه. اقرأ أيضًا المقال السابق: مختصر البرمجة كائنية التوجه OOP وتطبيقها في بايثون تحليل المشاعر في النصوص العربية باستخدام التعلم العميق الذكاء البشري مقابل الذكاء الاصطناعي المفاهيم الأساسية لتعلم الآلة
  4. تُعَدّ البرمجة كائنية التوجه Object Oriented Programming -أو OOP اختصارًا- نمطًا من أنماط البرمجة التي يُكتَب فيها البرنامج على صورة كائنات تحتوي على خاصيات ومهام -أي دوال- فيمكن مثلًا التفكير في السيارة على أساس كائن، إذ يكون لون السيارة ولون إطاراتها واسم الشركة المصنعة للسيارة خاصيات لها؛ أما مهمة حركة السيارة ومهمة إنقاص سرعة السيارة ومهمة زيادة سرعة السيارة، فهي دوال. كما يمكن التفكير أيضًا في الأقلام على أنها كائنات، يكون لون القلم واسم الشركة المصنعة له خاصيات؛ أما مهمة الكتابة فهي دالة، كذلك الأشخاص يمكن التفكير فيهم من وجهة نظر البرمجة الكائنية على أساس كائنات لها خاصيات ودوال، إذ يكون اسم الشخص ولون بشرته وطول قامته خاصيات؛ أما مهمة المشي ومهمة الجري ومهمة الأكل فهي دوال، وكذلك مهمة النوم والجلوس والعمل، …إلخ فهي كلها دوال موجودة في الكائن. ليست كل لغات البرمجة تدعم نمط البرمجة كائنية التوجه، إذ لا يتوفر هذا النمط في لغات مثل C، ومع ذلك فإنّ معظم لغات البرمجة المستخدَمة في هذا العقد تدعم البرمجة الكائنية، لكن العمل بذلك النمط ليس فرضًا أثناء عملك باللغات التي تدعم ذلك النمط مثل بايثون؛ إذ يوجد على سبيل المثال نمط آخر يدعى بالبرمجة الإجرائية Procedural Programing والتي تنفِّذ البرنامج على صورة كتل من الشيفرات تمثِّل كل واحدة منها خطوةً من خطوات تنفيذ البرنامج، وهو ما استخدمناه بالفعل في الأمثلة. مفهوم الصنف class والكائن object إنّ أنواع البيانات البدائية الموجودة بصورة مدمجة في بايثون هي نصوص وأعداد وقوائم، …إلخ، ولكن توفِّر البرمجة الكائنية بناء هيكل بيانات جديد خاص ببرنامجك، إذ تُنشأ الكائنات باستخدام صنف Class يحَّدد له مسبقًا الخاصيات والدوال، ويمكن التفكير مثلًا في الشخص أحمد على أنه كائن من الصنف إنسان، ولذلك يجب عند إنشاء أي كائن تحديد صنفه أولًا، كما يمكن عدّ ذلك الصنف نوعًا جديدًا من هياكل البيانات المصمم من قبلك خصيصًا لبرنامجك، إذ تُخزَّن تلك الكائنات في النهاية في متغيرات، فيصبح بذلك نوع البيانات المخزن في المتغير هو صنف الكائن المخزن به. وبناءً على ذلك فإنَ الأصناف Classes لا تؤدي حقًا أيّ مهمة، فهي في الواقع نوع جديد من هيكلة البيانات الذي يعرِّفه المبرمج لتحديد الخاصيات والدوال الموجودة في كائنات تلك الأصناف، لكنها بدورها لا تحمل قيمة لأي خاصية ولا تنفِّذ أيّ دالة، فهي أشبه بمخطط فقط أو فكرة مجردة. سيحمل الصنف إنسان في مثالنا السابق خاصيات مثل الاسم والعمر ولون البشرة ولون الشعر ولكنه لا يحمل قيمةّ للاسم مثل أحمد أو محمد، ولا يحمل قيمة للعمر مثل 14 أو 40 عامًا، ولا غيرها من الخاصيات وإنما يعرِّف الصنف تلك الخاصيات فقط لكي تُسنَد قيم لتلك الخاصيات عند إنشاء كائن من ذلك الصنف، وكذلك الأمر في الدوال، إذ يحمل الصنف نفسه شيفرة تلك الدوال، لكنه ينفِّذها بذاته إلا عند استدعاء تلك الدوال لتنفيذها من قِبَل كائنات الصنف. أما الكائنات Objects أو تُدعى في بعض الأحيان نسخ Instances، فهي ليست أفكارًا أو مخططات، وإنما هي نسخة واقعية تحمل قيمًا في خاصياتها وتحمل دوالًا جاهزةً للاستخدام أخذتها نسخةً من الصنف، إذ يكون الكائن في حالة الصنف إنسان شخصًا حقيقيًا، يحمل قيمًا للخصائص التي يوفرها الصنف ويستطيع تنفيذ المهام، لذلك فإنه غالبًا عندما يُنشَأ كائن جديد، فإنه تُسنَد قيم لخاصياته الأساسية أثناء الإنشاء. إنشاء صنف في بايثون لإنشاء صنف جديد في بايثون، نستطيع استخدام الكلمة المفتاحية class متبوعةً باسم الصنف الذي من المفضَّل أن يبدأ بحرف كبير وليس شرطًا، ثم نبدأ كتلةً تحتوي على الخاصيات والدوال بالشكل التالي: class Human: pass عرَّفنا في المثال السابق صنفًا جديدًا باسم Human، ولا تحمل كتلته أيّ تعليمات حاليًا لذلك أضفنا الكلمة المفتاحية pass التي ذكرناها سابقًا بأنها تُستَعمل في حالة عدم وجود أيّ تعليمات في كتلة الشيفرة في حلقات التكرار وقواعد اتخاذ القرارات، وكذلك تستخدَم في أيّ كتلة شيفرة فارغة في أيّ قاعدة من قواعد اللغة. كما أوردنا فإنّ الأصناف تحتوي على خاصيات ودوال، وبالتالي تكون أولى الخطوات عند تعريف صنف جديد هي تحديد الخاصيات، إذ يمكننا في ذلك المثال إضافة خاصيات مثل الاسم والعمر بتعريف متغيرات عادية في كتلة الصنف كما في المثال التالي: class Human: name = 'Adam' age = 30 نلاحظ أننا كتبنا قيمًا لتلك الخاصيات على الرغم من قولنا السابق بأن الأصناف لا تحمل أيّ قيم حقيقية، وذلك لا يتعارض مع ذاك لأن تلك القيم لا يحملها الصنف ذاته، وإنما تحملها كائنات ذلك الصنف افتراضيًا في حال لم تُحدَّد قيمة لكل خاصية من تلك الخاصيات للكائن الجديد، لكن لكي يتسنى لنا تغيير تلك القيم عند إنشاء كائن جديد من ذلك الصنف، فإنه من الضروري استخدام دالة __init__ والتي هي إحدى الدوال الرئيسة في أيّ صنف؛ لأنها تُنفَّذ افتراضيًا عند إنشاء أيّ كائن جديد من الصنف. يأخذنا ذلك للحديث عن الركن الثاني من أركان كتابة الأصناف وهو الدوال، فقد علمنا مسبقًا أن تعريف الخاصيات في الأصناف هي تمامًا مثل تعريف المتغيرات في أيّ جزء من أجزاء البرنامج؛ أما تعريف الدوال في الأصناف، فهو شبيه بقدر كبير لتعريفه في أيّ جزء آخر لكن مع فارق بسيط، وذلك أنه افتراضيًا عند تعريف دالة جديدة بداخل صنف ما، فستُمرَّر نسخة من الكائن على أساس أول معامل في الدالة؛ ولذلك سيكون لدينا دائمًا معاملًا موجودًا بالفعل في الدالة حتى ولو لم تتطلب الدالة أية معاملات على الإطلاق. يلعب ذلك المعامِل في الحقيقة دورًا مركزيًا في أيّ دالة، إذ يمكِّنك من استدعاء أيّ دالة أخرى بداخل الكائن -أي الصنف- وأيضًا من استدعاء وتغيير قيم أيّ خاصية من الخاصيات في الكائن، وبناءً على ذلك فإننا سنستخدِم الدالة __init__ لتمرير قيم خاصيات الكائن عند إنشائه، والتي تُنفَّذ افتراضيًا عند الإنشاء لكي يتم تمرير قيم الخاصيات، ثم إعادة تعيينها في الكائن بأول معامل افتراضي تحدثنا عنه، وسنكتب في المثال التالي تلك الدالة، بحيث نكتب اسمًا للمعامل الأول والذي يفضَّل أن يكون self، ثم نبدأ في إضافة معامِلات لكل خاصية من خاصيات الكائن لتُعيَّن فيه. class Human: name = 'Adam' age = 30 def __init__(self, name, age): self.name = name self.age = age برغم أننا أردنا تمرير قيمتين فقط للخاصيتين الموجودتين في التصنيف، إلا أنّ الدالة كما ذكرنا لها معامل ثالث يُحرَّر افتراضيًا على أساس أول معامل في الدالة، وذلك المعامل هو الكائن نفسه، إذ يعني الكائن هنا النسخة التي يُعمَل عليها من الصنف، وبالتالي سيتيح ذلك المعامِل لنا الوصول إلى خاصيات ودوال الصنف، كما سنستطيع استخدام القيم المُمرَّرة من باقي معاملات الدالة __init__ وإسنادها إلى خاصيات الكائن. إنشاء الكائنات والتعامل معها عند إنشاء كائن جديد من صنف تمرَّر معامِلات دالة الإنشاء __init__، إذ تُنفَّذ تلك الدالة كما ذكرنا عند إنشاء كائن جديد، كما يُسنَد الكائن الجديد في متغير، إذ أنه يُعَد نوعًا جديدًا من أنواع البيانات الذي عرَّفته أنت بنفسك عند إنشاء صنف جديد. ahmed = Human('Ahmed', 23) print (ahmed.name); print (ahmed.age) >> Ahmed >> 23 حسنًا، ما حدث لا يزيد عن إنشاء متغير جديد يحمل الاسم ahmed من الصنف الذي كتبناه مسبقًا Human، والذي تحتاج دالة __init__ فيه إلى معاملين هما الاسم والعمر واللذين مرِّرا بين الأقواس بعد اسم الصنف في المثال السابق. نستطيع الآن بعد تنفيذ دالة __init__ التنبؤ بكون خاصية الاسم تحمل الاسم الذي مررناه، وخاصية العمر تحمل العدد الذي مررناه أيضًا، إذ أسندنا في دالة __init__ تلك المعاملات بالفعل إلى خاصيات الكائن، وبناءً على ذلك، فعند محاولة طباعة خاصية الاسم أو خاصية العمر من الكائن، فسنفترض أن تلك الخاصيات تحمل القيم التي مررناها في أول الأمر، ما لم تُستدعى دالة أخرى أو تُغيَّر خاصية من الخاصيات بصورة مباشرة أو غير مباشرة، فالأمر لا يحتاج إلى دالة بصورة ضرورية حتى تُعدَّل خاصية من خاصيات الكائن، إذ أنه يمكننا التلاعب بقيم الخاصيات بصورة مباشرة من خلال المتغير الذي يحمل الكائن كما في المثال التالي: ahmed = Human('Ahmed', 23) ahmed.age += 1 print (ahmed.age) >> 24 استخدمنا في المثال السابق أحد عوامل الإسناد لتغيير قيمة خاصية من خاصيات الكائن حتى بعد تعريفه وإسناده في دالة __init__؛ ولذلك فنحن لا نحتاج دائمًا إلى دالة كي يتسنى لنا تغيير قيمة خاصية من الخاصيات؛ أما الدوال فهي كما أوردنا تُعرَّف تمامًا مثل دالة __init__ وتُستدعى عند الحاجة من خلال المتغير الذي يحمل الكائن، إذ يمكننا مثلًا تعريف دالة في الصنف تطبع كلمة المشي أو أمر من هذا القبيل للتجربة. class Human: name = 'Adam' def __init__(self, name): self.name = name def walk(self): print (self.name + ' is walking now ..') ahmed = Human('Ahmed') ahmed.walk() >> Ahmed is walking now .. نلاحظ أنه على الرغم من أنّ الدالة walk لا تطلب أيّ معامل، إلا أنه يمرَّر أول معامل افتراضيًا أسميناه self، والذي نستطيع من خلاله استدعاء أي دالة أو خاصية من الكائن، وذلك ما فعلناه لطباعة اسم الكائن عند طباعة الجملة. وراثة الأصناف وإنشاء أصناف فرعية يمكننا إنشاء أصناف فرعية من أصناف منشأة بالفعل، إذ يمكننا إنشاء صنف جديد يُدعى Programmer يرث بصورة تلقائية خواص ودوال الصنف Human، فالأمر أشبه بإنشاء صنف للمبرمجين الذين هم في الأساس من صنف الإنسان، كذلك الأمر في أيّ صنف آخر، إذ نستطيع مثلًا إنشاء صنف للقطط يرث صنف الحيوانات، وإنشاء صنف للسيارات الرياضية يرث صنف السيارات عممرمًا، إذ تُعَدّ فائدة إنشاء أصناف فرعية هي إنشاء صنف يرث جميع الخواص والدوال من أبيه وكما أنّ ذلك لا يتعارض مع كوننا نستطيع إعادة كتابة بضع دوال أو بضع خواص في الصنف الفرعي الجديد، وتلك العملية في مجملها تُدعى بالوراثة Inheritance. لنكتب مثالًا يحتوي على صنف Programmer الذي يرث الصنف Human مع إضافة خاصية اللغة language التي تحمل بدورها اسم لغة البرمجة المتخصص فيها المبرمج، ثم سنعدِّل على دالة __init__ لاستقبال قيمة للخاصية الجديدة وهي اللغة، وبذلك نكون قد أنشأنا صنفًا فرعيًا وراثيًا، وأضفنا خاصيةً له، ثم عدّلنا على دالة من دوال الصنف الأب: class Human: name = 'Adam' def __init__(self, name): self.name = name def walk(self): print (self.name + ' is walking now ..') class Programmer(Human): language = 'PHP' def __init__(self, name, language): self.name = name; self.language = language ahmed = Programmer('Ahmed', 'Python') print (ahmed.language); ahmed.walk() >> Python >> Ahmed is walking now .. توجد الكثير والكثير من التفاصيل الأخرى الخاصة بالبرمجة كائنية التوجه في بايثون، إلا أن ذلك القدر يكفي لفهم الأفكار الأساسية في الأمر برمته، وذلك لأن التوسع في البرمجة كائنية التوجه لن يكون ذا فائدة كبيرة لأننا لن نستخدِم ذلك النمط في أمثلة نماذج تعلم الآلة في الكتاب، لكننا مع ذلك نوصي وبشدة بالتوسع في أمر البرمجة كائنية التوجه، إذ تستخدِمها كل التطبيقات الحديثة، فهي أمر لا بد منه عند التخصص في أيّ عمل يخص كتابة البرمجيات لذا نوصي بقراءة المقالات المقترحة في قسم اقرأ أيضًا. اقرأ أيضًا المقال السابق: تعرف على أهم الدوال المدمجة في لغة بايثون دليلك إلى أنواع لغات البرمجة مفهوم البرمجة كائنية التوجه
  5. تعرفنا في المقال السابق على مفهوم الدوال وكيف يمكن للمبرمج تعريف دوال مخصصة ولكن لا يحتاج المبرمج إلى تعريف كل الدوال بنفسه من الصفر بل توجد الكثير من الدوال المدمجة بالفعل في اللغة افتراضيًا تسمى Built-in Functions أي دوال مدمَجة، وأفضل الأمثلة عليها هي دالة الطباعة print الموجودة افتراضيًا في بايثون، وسنذكر في هذا الفصل أهم هذه الدوال وأمثلة على استخدامها. دوال التعامل مع الأوقات والأزمنة في بايثون يُتعامَل مع الأوقات والتواريخ في كثير من برامج الحاسوب بصورة ضرورية، فلا غنى عن التعامل مع التواريخ والأوقات في برمجة الكثير من التطبيقات، إذ تعتمد الكثير من التطبيقات عليها، فهاتفك مثلًا يحتوي على تطبيقات تستخدِمها يوميًا مثل تطبيق المنبه، والذي يعتمد بصورة أساسية على التعامل مع التواريخ والأوقات، كما توجد الكثير من المهام التي من الممكن استخدام دوال التعامل مع الأوقات فيها، فمثلًا قد نودّ متابعة وتسجيل عدد الثواني التي يستخدِمها الحاسوب في تنفيذ كتلة شيفرة معيّنة لقياس سرعة تنفيذ البرنامج. توجد في بايثون بعض الوحدات Modules التي تُستخدَم عند التعامل مع الأوقات والتواريخ، سنبدأ أولًا بوحدة الوقت التي تُدعى time وهي تحتوي على الكثير من الدوال المفيدة للتعامل مع الوقت وسنذكر بعضًا منها. ملاحظة: يُحسَب في بايثون وفي كثير من لغات البرمجة وقت الثانية الحالية حسب فترة بُعدها بالثواني عن الساعة 12 صباحًا يوم 1 من شهر 1 عام 1970م، وهو نظام متعارف عليه، إذ يُعَدّ ذلك التاريخ نقطة انطلاق ما يُدعى بوقت يونكس Unix أو Unix Epoch. دالة time يمكننا تحديد رقم الثانية الحالية باستخدام الدالة time الموجودة في الوحدة time، إذ سنطبع في المثال التالي الوقت الحالي كما يلي: import time print (time.time()) >> 1592952455.39395 ستلاحظ في كل مرة يُنفَّذ المثال أعلاه أن العدد يزيد بفرق عدد الثواني بين التنفيذ الثاني والتنفيذ الأول، والجدير بالذكر أنّ الثانية في تلك القاعدة يُطلق عليها بالإنجليزية لقب Tick، وحسب تلك القاعدة فإنه يعبَّر عن التاريخ حسب عدد الثواني، إذ يمثِّل العدد الموجب عدد ثواني المارة على تاريخ 1/1/1970؛ أما العدد السالب، فيُمثِّل عدد الثواني المطلوبة للوصول إلى وقت يونكس أي تاريخ 1 شهر 1 عام 1970م في حال تعاملنا مع تاريخ قديم، ولفهم ما نعنيه يمكن تخيُّل تاريخ وقت يونكس هو نقطة الصفر بحيث تمثِّل القيمة الموجبة ما بعد ذلك التاريخ؛ أما القيمة السالبة، فتمثِّل ما قبل ذلك التاريخ كما في الصورة التالية: إضافة: ستكون أيّ قيمة في وقتنا الحاضر وفي المستقبل قيمةً موجبةً، لكن مع ذلك فإنّ الأرقام الصحيحة التي تعبِّر عن عدد الثواني محدودةً في أنظمة التشغيل، وبالتالي توجد مشكلة تدعى مشكلة عام 2038، إذ أنه في تمام يوم الثلاثاء الموافق للتاسع عشر من شهر 1 من ذلك العام ستتوقف الأنظمة عن العمل بذلك النظام لأنه لن تستوعب العدد الكبير من الثواني آنذاك. دالة localtime يمكننا في بايثون معرفة التاريخ الحالي عبر دالة localtime وفي المثال الآتي نوضِّح كيفية استخدامها. import time print (time.localtime()); >> time.struct_time(tm_year=2020, tm_mon=6, tm_mday=24, tm_hour=23, tm_min=23, tm_sec=55, tm_wday=2, tm_yday=176, tm_isdst=0) نلاحظ أنّ الدالة تُنتج صنف time.struct_time، ويمكننا استخراج بيانات التاريخ وطباعتها من الدالة السابقة عبر معاملة الكائن الناتج عن الدالة كونه صنفًا، إذ يكون العنصر الأول هو العام، والعنصر الثاني هو رقم الشهر، والعنصر الثالث هو رقم اليوم، والعنصر الرابع هو الساعة، والعنصر الخامس هو الدقيقة، والعنصر السادس هو الثواني، والعنصر السابع هو رقم اليوم في الأسبوع، والعنصر الثامن هو رقم اليوم في السنة، ولطباعة العام يجب طباعة العنصر الأول كما يلي: print (time.localtime()[0]); >> 2020 ولطباعة الشهر مثلًا يجب طباعة العنصر الثاني كما يلي: print (time.localtime()[1]); >> 6 وهكذا. دالة asctime تكوّن هذه الدالة التاريخ بصورة مقروءة وتتطلب معاملًا واحدًا ومن الممكن أن يكون من نوع time.struct_time، لذلك نستطيع ببساطة تمرير ناتج دالة localtime على أساس معامِل في هذه الدالة، ففي المثال الآتي نطبع التاريخ والوقت الحاليين بصورة مقروءة باستخدام الدالتين السابقتين: import time print (time.asctime(time.localtime())); >> Wed Jun 24 23:41:30 2020 دالة sleep توقف الدالة sleep عمل البرنامج لبرهة من الزمن تُحدَّد بعدد من الثواني تُمرَّر على أساس معامِل وحيد في الدالة، ففي المثال التالي سيُطبَع الاسم Adam في بداية تشغيل البرنامج ثم سيتوقف البرنامج عشرة ثوانٍ ثم يطبع الاسم Noah: import time print ('Adam') time.sleep(10) print ('Noah') >> Adam >> Noah دوال التعامل مع التواريخ في بايثون سنتعرف على بعض الدوال البسيطة والمفيدة في التعامل مع التواريخ. دالة month تطبع التقويم الميلادي لشهر محدد في سنة محددة عبر المعامِلات التي تستقبلها الدالة، فالمعامل الأول المطلوب في الدالة هو العام؛ أما المعامل الثاني فهو رقم الشهر، إذ يمكننا عبر المثال التالي طباعة تقويم شهر 1 من عام 2019م: import calendar print (calendar.month(2019, 1)) >> January 2019 Mo Tu We Th Fr Sa Su 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 دالة today توجد في بايثون وحدة datetime والتي تحتوي على الكثير من الخصائص والدوال منها الدالة today. نستطيع استخدام الدالة today لطباعة تاريخ اليوم، ففي المثال الآتي نستورد الصنف date من الوحدة datetime ثم نستخدِم دالة today الموجودة بداخلها. from datetime import date print (date.today()) >> 2020-06-25 دالة isleap تتحقق هذه الدالة من كون السنة المحدَّدة في معاملها سنةً كبيسةً أم لا، إذ نتحقق في المثال التالي من سنة 2019: import calendar print (calendar.isleap(2019)) >> False الإجابة كما نرى في المثال هي خطأ False وبالتالي هي ليست سنةً كبيسةً، ومن المُمكن استخدام تلك الدالة في الجمل الشرطية، إذ تستطيع في تلك الحالة تنفيذ كتلة شيفرة معينة في السنة الكبيسة دونًا عن السنوات الأخرى أو العكس. دالة leapdays تحسب هذه الدالة عدد الأيام الكبيسة في نطاق عدد سنوات يُحدَّد عبر تمرير معاملين فيها بحيث يحتوي المعامِل الأول على عام بداية النطاق؛ أما المعامل الثاني فيحتوي على عام نهاية النطاق، فإذا أردنا مثلًا معرفة عدد الأيام الكبيسة منذ عام 1990م حتى عام 2019م، فسيكون كما في المثال التالي: import calendar print (calendar.leapdays(1990, 2019)) >> 7 توجد في كل من الوحدات time و calendar و datetime السالف ذكرها الكثير من الدوال والخصائص الأخرى التي لا يتسع الفصل لذكرها، ويمكنك الرجوع إلى توثيقها في موسوعة حسوب لمزيد من التفاصيل. دوال التعامل مع الملفات في بايثون تُعَدّ معرفة الدوال والأمور الخاصة بالتعامل مع الملفات في بايثون من الموضوعات الضرورية أيضًا، إذ يصبح من الضروري قراءة محتويات الملفات التي تحتوي على بيانات المشروع أثناء برمجة الكثير من المشاريع وأهمها مشاريع تعلم الآلة. ملاحظة: لا تُستخدَم دوال التعامل مع الملفات المدمجة غالبًا بصورة افتراضية في بايثون في مشاريع تعلُّم الآلة، وذلك لأن معظم ملفات تلك المشروعات هي ملفات بصيغة CSV أو ما شابه، وهي ملفات من الأفضل قراءة محتواها عبر مكتبات متخصصة لذلك النوع، ولكن مع ذلك فإنه من المُستحسن معرفة بعض تلك الدوال المدمجة وإمكاناتها، إذ أنها قد تكون مفيدةً في بعض البرامج. الدالة read قبل القراءة من ملف أو الكتابة فيه، يجب استخدام الدالة open الأساسية المستخدَمة في فتح الملفات، وهي دالة تطلب معاملَين اثنين، الأول هو مسار الملف، والثاني هو ما يُدعى بوضع الوصول Access Mode، وهو قيمة مكونة من رمز مأخوذ من مجموعة رموز محددة كل منها يعبِّر عن وضع وصول معيّن، إذ تحدِّد أوضاع الوصول ما يمكن فعله وما لا يمكن فعله عند فتحك ملفًا ما، فقد تتيح بعض لك أوضاع الوصول قراءة المحتويات فقط، وأخرى تتيح لك القراءة من والكتابة في الملف وأخرى تتيح لك كتابة محتوى فقط دون قراءة للمحتوى، إذ تختلف أوضاع الكتابة، فتكتب بعض الأوضاع المحتوى من أول الملف، في حين تكتب أوضاع أخرى المحتوى في نهاية الملف، بالإضافة إلى الكثير من التفاصيل الأخرى التي يعبِّر عنها كل رمز من الرموز. لنبدأ بمثال بسيط، يمكننا إنشاء ملف نصي بسيط بجوار الملف الرئيسي للبرنامج باسم file.txt، ولنكتب شيفرة مهمتها قراءة محتوى الملف وطباعته، ولتحقيق ذلك لا نحتاج إلى استيراد أية مكتبات لأن الدالة مدمجة بالفعل. file = open ('file.txt') print (file.read()) >> The file content استخدمنا في المثال السابق الدالة open لقراءة الملف دون تمرير معامل ثان لها وهو معامل الوصول بوضع القراءة افتراضيًا، وقد أنشأت بذلك كائنًا يحتوي على الكثير من الدوال، منها الدالة التي تتيح قراء المحتوى النصي وهي الدالة read. الدالة write كما يمكننا أيضًا كتابة محتوًى في الملف بتغيير رمز وضع الوصول إلى w، ثم استخدام الدالة write لإدخال نص وحفظه في الملف كما يلي: file = open ('file.txt', 'w') file.write('New file content') ستُحذَف جميع محتويات الملف النصي بعد تنفيذ ذلك البرنامج، ويُغيَّر بالمعامل الأول في دالة write، والجدير بالذكر أنّ الرمز w يتيح الكتابة في الملفات فقط ولا يتيح قراءة المحتويات. الدالة close من الأفضل إغلاق المتغير الذي يحتوي على الملف بعد إنهاء المهمات المُراد تنفيذها على الملفات لكي تُلغى أيّة تعديلات غير منتهية ومن ثم إغلاق الملف، إذ لن يكون التعديل على الملف متاحًا بعد إغلاقه إلا إذا استُخدِمت الدالة open مرةً أخرى لفتح الملف، ويمكن إغلاق الملف باستخدام الدالة close الموجودة في متغير الملف كما في المثال التالي: file.close() الدالتان remove و rename توجد دالتان مهمتان هما دالة حذف الملفات remove ودالة إعادة تسمية الملفات rename، لكنهما متاحتان فقط بوجود مكتبة مستقلة للتعامل مع أنظمة التشغيل، وهي مكتبة os المُسماة بذلك اختصارا لكلمة Operating System، إذ يكون المعامل الوحيد للدالة الأولى التي تتيح حذف الملفات هو اسم الملف أو مساره بداخل المجلد الموجود فيه البرنامج؛ أما الدالة الثانية التي تتيح إعادة تسمية الملفات، فستطلب معاملين هما اسم الملف الحالي المُراد تعديله واسم الملف الجديد، إذ سنعيد في المثال التالي تسمية ملف موجود بجوار البرنامج. import os os.rename ('file.txt', 'file_renamed.txt') سيتغير بذلك اسم الملف بجوار البرنامج من file.txt إلى file_renamed.txt بدون طباعة أيّ كلمة عند تشغيل البرنامج. ملاحظة: من الضروري دائمًا معاملة صيغة الملف على أساس جزء من الاسم عند التعامل مع الملفات في بايثون وفي معظم لغات البرمجة. يوضِّح المثال التالي حذف الملف من الحاسوب تمامًا: import os os.remove ('file_renamed.txt') دوال التعامل مع النصوص في بايثون النص أو السلسلة النصية string في لغة بايثون عبارة عن سلسلة متتالية من الأحرف الموضوعة ضمن علامتي اقتباس مفردتين أو مزدوجتين ومن أهم الدوال المستخدمة للتعامل مع السلاسل النصية في بايثون نذكر: دالة str السلسلة النصية كما ذكرنا ما هي إلا سلسلة من الأحرف المتتالية ويمكنك الوصول إلى كل حرف منها باستخدام الدالة str [n] حيث يعبر n هنا عن موضع أو فهرس الحرف الذي تريد الوصول له، علمًا بأن فهرسة السلاسل النصية في بايثون تبدأ بالصفر أي أن فهرس الحرف الأول في السلسلة النصية يساوي صفر. لنأخذ مثالًا لتوضيح الأمر، سننشئ متغير يعبر عن السلسلة Python String وسنصل للحرفين الأول والثاني من السلسلة من خلال كتابة str[0] و str[1] كما يلي str = "Python String" print(str[0]) # P print(str[1]) # y ملاحظة: إذا كنت مررت قيمة سالبة للفهرس فإن بايثون تبدأ العد من نهاية السلسلة وتعيد لك الحرف الموافق وتبدأ الفهرسة في هذه الحالة بالقيمة 1- كما هو موضح في المثال التالي: str = "Python String" print(str[-1]) # g print(str[-4]) # r ويمكن الحصول على سلسلة فرعية من سلسلة نصية ما من خلال تمرير مجال من القيم للدالة على سبيل المثال للحصول على أول 6 أحرف من السلسلة النصية "Python String" نكتب الكود التالي: str = "Python String" print (str[0:6]) #Python كما تلاحظ من المثال أعلاه ستتضمن السلسلة الفرعية الناتج عن الأحرف من الدليل رقم 0 لغاية الدليل 5 وتستبعد الحرف ذو الدليل 6. ويمكنك حذف قيمة فهرس البداية ليتم تعيينها افتراضيًا على الصفر أو حذف قيمة فهرس النهاية ليتم تعيينها افتراضيًا على طول السلسلة الكامل. str = "Python String" print (str[:6]) # Python print (str[7:]) # String دالة len تستعمل الدالة len للحصول على طول السلسلة النصية أي عدد حروفها، على سبيل المثال الشيفرة التالية تعيد القيمة 13 str = "Python String" str_len = len(str) print(str_len) دوال التعامل مع القوائم والمصفوفات في بايثون تعد كل من القوائم lists والمصفوفات arrays بنى بيانات في بايثون قادرة على تخزين عناصر متعددة في متغير واحد. والفرق الأساسي بين القوائم والمصفوفات هو أن القائمة تتكون من عناصر تنتمي إلى أنواع بيانات مختلفة في حين تتكون المصفوفة من عناصر تنتمي إلى نفس نوع البيانات. توفر بايثون الكثير من الدوال التي تتعامل مع القوائم والدوال وفيما يلي نتعرف على أهم هذه الدوال: دالة append تضيف الدالة append عنصرًا واحدًا إلى نهاية القائمة، يمكن أن يكون العنصر نفسه قائمة، لاحظ الأمثلة التالية fruits = ['apple', 'banana', 'cherry'] vegetables = ["cabbage", "tomato", "cucumber"] fruits.append("orange") print(fruits) #['apple', 'banana', 'cherry', 'orange'] fruits.append(vegetables) print(fruits) #['apple', 'banana', 'cherry', 'orange', ['cabbage', 'tomato', 'cucumber']] دالة clear الدالة clear دالة بسيطة لا تحتاج لأي وسطاء وتقوم بحذف كل العناصر من القائمة وتعيد قائمة فارغة. fruits = ["apple", "banana", "cherry"] fruits.clear() print(fruits) #[] دالة count تعيد الدالة count عدد المرات التي ورد فيها عنصر ما في القائمة، على سبيل المثال لإعادة عدد مرات ورود اسم معين في قائمة من الأسماء أو رقم محدد في سلسلة أرقام نكتب الكود التالي names = ['Ali','OLA','Ahmad','Ali','Mohammed','Ali',] num=names.count("Ali") print(num) #3 points = [1, 3, 2, 9, 7, 8, 9, 3, 1] x = points.count(3) print(x)#2 دالة extend تلحق الدالة extend قائمة جديدة أو أي بنية معطيات أخرى مكونة من عدة عناصر إلى نهاية القائمة الحالية fruits = ['apple', 'banana', 'cherry'] vegetables = ["cabbage", "tomato", "cucumber"] points = (1, 4, 5, 9) fruits.extend(vegetables) print(fruits) #['apple', 'banana', 'cherry', 'cabbage', 'tomato', 'cucumber'] دالة index تعيد الدالة index فهرس العنصر في القائمة كل ما عليك هو تمرير قيمة العنصر الذي تريد البحث عنه في السلسلة وستعيد لك الدالة دليل أو ظهور له ضمن القائمة، وتذكر أن فهرس أول عنصر في القائمة هو 0. لاحظ المثال التالي: names = ['Myriam ', 'Ali', 'Mohammed', 'Ahmad','Salah'] i=names.index('Ahmad') print(i) #3 دالة insert تقوم الدالة insert بإضافة عنصر إلى موضع محدد في القائمة على سبيل المثال إذا كان لدينا قائمة بأسماء وعلامات الطلاب ونسينا إضافة علامة الطالب الثاني في القائمة يمكن استخدام الدالة لإضافتها ببساطة كما يلي students = ['Ali', 90, 'Ahmad', 'Salah', 80] students.insert(3, 76) print(students) #['Ali', 90, 'Ahmad', 76, 'Salah', 80] دالة pop تحذف الدالة pop عنصر له فهرس محدد من القائمة ويمكن أن تعيد لنا العنصر المحذوف كما في المثال التالي fruits = ['apple', 'banana', 'cherry'] x = fruits.pop(1) print(fruits) #['apple', 'cherry'] print(x) #banana دالة remove تحذف الدالة عنصر له فيمة محددة من القائمة وفي حال تكررت القيمة أكثر من مرة في القائمة ستحذف الدالة أول ورود للقيمة وتترك القيم الباقية. names = ['Ali','OLA','Ahmad','Ali','Mohammed','Ali',] names.remove("Ali") print(names) #['OLA', 'Ahmad', 'Ali', 'Mohammed', 'Ali'] دالة reverse تعكس الدالة ترتيب عناصر القائمة numbers = [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ] numbers.reverse() print(numbers) #[10, 9, 8, 7, 6, 5, 4, 3, 2, 1] دالة sort ترتب الدالة sort عناصر القائمة وفق التسلسل الأبجدي وهي تفرز العناصر تصاعديًا بشكل افتراضي كما يمكن تمرير وسيط اختياري reverse وإعطاؤه القيمة True لجعل الدالة تغير طريقة عملها وتفرز عناصر القائمة تنازليًا على سبيل المثال لترتيب مجموعة من الأسماء أبجديًا نكتب الشيفرة التالية names = ['OLA', 'Ahmad', 'Mohammed'] names.sort() print(names) # ['Ahmad', 'Mohammed', 'OLA'] names.sort(reverse=True) print(names) # ['OLA', 'Mohammed', 'Ahmad'] ويمكن كذلك تغيير طريقة الترتيب من خلال تمرير قيمة مفتاح key يحدد المعيار الذي تود فرز العناصر وفقه، على سبيل المثال لترتيب الأسماء حسب طولها أو عدد أحرفها من الأقل للأكثر سنعرف دالة تحسب طول السلسلة النصية ثم سنمررها كقيمة مفتاحية لدالة الترتيب كما يلي: def myFunc(e): return len(e) names = ['Ahmad', 'Ali', 'Mohammed'] names.sort(key=myFunc) print(names) #['Ali', 'Ahmad', 'Mohammed'] هذه كانت نبذة سريعة عن أهم الدوال الأساسية في الدوال المدمجة في لغة بايثون، ويمكنك التعرف على مزيد من التفاصيل حول الدوال المدمجة في لغة بايثون بالاطلاع على توثيق لغة بايثون باللغة العربية الذي وفرته لك موسوعة حسوب. اقرأ أيضًا المقال السابق: مفهوم الدوال Functions في البرمجة تعرف على الدوال (Functions) في بايثون الدوال الرياضية المضمنة في بايثون 3 كيفية تعريف الدوال في بايثون 3
  6. تُعَدّ الدوال Functions في لغات البرمجة كتلًا منظمةً من الشيفرة تؤدي مهامًا محددةً، ويمكن إعادة استخدامها في أيّ مكان في البرنامج، كما توفِّر للمبرمج طريقةً أفضل لتنظيم كتابته للبرامج، بحيث لا يُضطر إلى إعادة كتابة مهمة واحدة أكثر من مرة، وسنتعرف في هذا المقال على مفهوم الدوال وأنواعها وكيفية كتابة دوال مخصصة في لغة بايثون وتنفيذها. تعريف دالة function تُعرَّف الدالة في لغة بايثون باستخدام الكلمة المفتاحية def -وهي اختصار كلمة definition أي تعريف- متبوعةً باسم الدالة والتي تخضع لقواعد المعرِّفات في بايثون التي ذكرناها سابقًا في مقال أساسيات البرمجة في لغة بايثون، ويلي اسم الدالة قوسان تُعرَّف معامِلات الدالة بداخلهما -والتي سنشرح أنواعها فيما بعد- ثم يُتبَع ذلك بالنقطتين :، وأسفل ذلك تُكتَب كتلة الشيفرة -أي جسم الدالة- التي ستُنفَّذ عند استدعاء الدالة. قد تُنتِج الدالة قيمةً ما وتعيدها مكان استدعاء الدالة باستخدام الكلمة المفتاحية return التي تعني بالعربية أَعِدْ، إذ تُستخدَم بعض الدوال لمعالجة بعض البيانات وتنفيذ بعض العمليات عليها مثل العمليات الحسابية، كما أنّ استخدام الكلمة المفتاحية return ليس شرطًا، إذ يمكن ألا تعيد الدالة أيّ قيم على الإطلاق، ومع ذلك فإنه عند إعادة قيمة من الدالة، فإنه لا توجد أنواع محدَّدة من البيانات التي يمكن إعادتها. من المتعارف عليه أثناء كتابة جسم الدالة الاحتفاظ بأول سطر لكتابة تعليق يشرح ويبيّن مهمة الدالة، وهو ليس بأمر ضروري، لكن يُفضَّل تطبيقه للحفاظ على الشكل العام للبرنامج، وبناءً على ما شرحناه، فإنه من المُمكن صياغة قالب تعريف الدوال كما في المثال التالي: def function_name( arguments ): "function's comment" function code return [expression] يمكننا البدء في تعريف دالة بمثال عن كتابة دالة تحسب مساحة الدوائر لتُستدعى من أجل حساب مساحة أية دائرة باختلاف نصف قطرها، ولكتابة تلك الدالة يجب معرفة قانون حساب مساحة الدائرة وهوπ*r2 ‎ بحيث تكون π هي قيمة ثابتة تساوي 3.141592654؛ أما r2 فهي مربع نصف قطر الدائرة، وهي المتغير المطلوب للدالة، إذ تختلف كل دائرة في نصف قطرها، في حين تبقى π ثابتةً لا تتغير، وبناءً على سنطبق العملية ثم نعيد الناتج من الدالة كما يلي: def circle_area (radius): r_squared = radius ** 2 pi = 3.141592654 area = pi * r_squared return area عرَّفنا في الدالة السابقة متغير r_squared الذي يحتوي على قيمة مربع نصف القطر؛ ولذلك كانت قيمته ناتج عملية حسابية استُخدِم فيها عامل الأُس ** لتربيع نصف القطر، بعد ذلك عرَّفنا متغير يحمل قيمة π لتُستخدَم تلك القيمة لاحقًا، ثم عرَّفنا متغير area الذي سيحمل قيمة مساحة الدائرة، وذلك بضرب يتم قيمة π في مربع نصف القطر، وكلاهما قد جرى تعيينهما في إحدى المتغيرات بالفعل، إذًا بات المتغير area في نهاية الدالة يحمل قيمة مساحة الدائرة، لذلك استخدِمَت الكلمة المفتاحية return لإرجاع المتغير -أي قيمته- في السطر الأخير. استدعاء دالة لا تستطيع تلك الدالة وأي دالة نعرفها تنفيذ أيّ شيء دون استدعائها، وذلك لأن الدالة تم تعريفها وكتابة كتلتها، لكنها في الحقيقة لم يتم استدعاؤها لحساب أي شيء. سنكتب في المثال الآتي بضع تعليمات لاستدعاء تلك الدالة وتمرير قيمة نصف قطر لتُحسَب مساحة الدائرة وطباعة القيمة، ولفعل ذلك سنعرِّف متغيرًا يحمل قيمة نصف قطر افتراضية أولًا ثم نستدعي الدالة. r = 10 print (circle_area(r)) >> 314.1592654 معاملات الدوال تُعَدّ المعاملات Parameters وسيلة للتخاطب مع الدوال وتمرير قيم إليها اثناء استدعائها وهي عبارة عن متغيرات لها أسماء مثل غيرها من المتغيرات تُعرَّف بداخل الأقواس بتحديد اسم لكل معامِل، ويمكن استخدام تلك الأسماء داخل جسم الدالة والتعامل معها مثل أيّ متغير من المتغيرات، لكن لا يمكن استخدام تلك المعاملات إلا بداخل جسم الدالة فقط، وتمرَّر قيمها عند استدعاء الدالة لاستخدامها، كما من الممكن أن تحتوي الدالة على الكثير من المعاملات، أو ألا تحتوي على أية معاملات على الإطلاق. يمكن تمرير المعاملات المطلوبة في الدوال بنفس ترتيبها في حالة وجود أكثر من معامل، ويمكننا لمزيد من التوضيح لعملية استدعاء الدوال وإسناد قيم المعاملات تخيل وجود دالة تُدعى example ولديها ثلاثة معاملات هي a و b و c كما في المثال التالي: def example(a, b, c): return a * b * c يمكن إسناد قيمة المعاملات الثلاثة بالترتيب عند استدعاء الدالة كما في المثال التالي: example(1, 2, 3) ستُسنَد القيمة العددية 1 إلى المعامل a والقيمة العددية 2 إلى المعامل b والقيمة العددية 3 إلى المعامل c، لكن يمكن إسناد تلك القيم بدون استخدام ذلك الترتيب بذكر أسماء المعاملات وهو ما يسمى بالمعاملات المسماة Keyword Arguments كما في المثال التالي: example(c = 3, b = 2, a = 1) لاحظ أنه أسندنا قيمةً إلى المعامل c أولًا على الرغم من كونه آخر معامل في الدالة لأننا استخدمنا أسماء المعاملات لإسناد القيم، ففي تلك الحالة نستطيع تعيين قيم المعاملات دون التقيد بترتيب المعاملات. يمكننا استكمال مثال دالة حساب مساحة الدالة بتطوير البرنامج قليلًا، إذ بدلًا من طباعة مساحة دائرة ناتجة عن نصف قطر محدد وثابت في البرنامج، فسنستقبِل قيمة نصف القطر تلك من المستخدِم عن طريق دالة input التي شرحناها سابقًا حيث سنمرر لها معاملًا يمثل رسالة تُطبَع للمستخدِم ليُطلَب منه إدخال قيمة عددية لنصف قطر الدائرة الذي يريد حساب مساحتها: r_str = input ('Please enter R value: ') r = float(r_str) print (circle_area(radius=r)) لاحظ أننا حولنا القيمة التي أخذناها من المستخدم إلى قيمة عددية وذلك لأن خرج دالة input دائمًا قيمًا نصيةً وبالتالي يجب تحويل نوع البيانات في المتغير إلى نوع عددي في حالتنا عن طريق الدالة int، أو إلى النوع العددي العشري Float عن طريق الدالة float، ومن الأفضل بالطبع استخدام الدالة float لأن العدد العشري قد يكون عددًا صحيحًا؛ أما في حالة استخدام الدالة int، فإنه عند التحويل إلى عدد صحيح، فسينتِج المفسر خطأً لاحتواء النص على عدد عشري ولا يمكن تحويله إلى عدد صحيح إذا أدخل المستخدِم قيمًا عشرية. ملاحظة: يُطلَق على المعاملات في الدوال بالإنجليزية Parameters أو وسائط Arguments، والفارق غالبًا ما يكون أن الأولى تُستعمل عند الحديث عن المعاملات التي تمرَّر عمرمًا؛ أما الثانية فتُستعمَل عند الحديث عن المعاملات أثناء تعريف الدالة وجسمها، وهو فارق اصطلاحي بسيط، ففي الغالب عندما يتحدث المبرمجون عن المعاملات يتم ذكر أحد المصطلحين في أيّ حال من الأحوال دون مشكلة. المعاملات الافتراضية يمكن إضافة معامِلات لها قيم افتراضية، عندها يكون تمرير قيمة للمعامل غير ضروري أو اختياري عند استدعائه، ويكون ذلك عبر كتابة اسم المعامِل ثم استخدام عامل الإسناد = ثم كتابة القيمة الافتراضية للمعامِل. في المثال التالي كتبنا دالةً لضرب عددين ببعضها إذ يُضاف كل عدد على أساس معامِل في الدالة مع صياغة المعامل الثاني بحيث يكون معامِلًا اختياريًا له قيمةً افتراضيةً ولتكن 1، ففي تلك الحالة في عدم تمرير قيمة للمعامِل الاختياري عند الاستدعاء، فستُنفَّذ الدالة مع تعويض قيمة المعامل الثاني بالقيمة الافتراضية 1. def multiply (x, y = 1): return x * y print (multiple(5)) >> 5 نلاحظ أنه لم نمرِّر المعامِل الثاني، وبما أنه يحمل قيمةً افتراضيةً هي العدد 1، فكانت النتيجة في ضرب العدد 5 بالعدد 1. المعاملات الديناميكية ‎*args من الميزات المفيدة في بايثون وجود إمكانية إضافة عدد غير محدَّد من المعامِلات في الدوال عند استدعائها، وتُضاف تلك الميزة عبر إضافة رمز النجمة * عند تعريف الدالة وقبل كتابة اسم المعامِل الذي سيحمل جميل القيم التي ستمرَّر عند استدعاء الدالة مرتبةً حسبما تم تمريره عند الاستدعاء. في المثال التالي استقبلنا عددًا غير محدود من القيم، ثم استخدمنا حلقة التكرار for، من أجل جمع كل القيم وإعادة الناتج من الدالة. def addition (*args): total = 0 for number in args: total += number return total print ( addition(5, 6, 7, 8, 14) ) >> 40 كما يمكن إضافة عدد محدد من المعاملات في الدالة أولًا، ثم إضافة معامل يمثل بقية المعاملات إن وجدت آخر المعاملات حصرًا ولا نضيفه قبلها، وإليك مثال بتعديل الدالة السابقة مثلًا بحيث تجمع كل المعاملات الإضافية الزائدة عن المعامل x ثم تضرب الناتج فيه: def addition (x, *args): total = 0 for number in args: total += number return x * total print ( addition(2, 5, 6, 7, 8, 14) ) >> 80 ملاحظة: في حالة استخدام ميزة المعاملات الديناميكية في إحدى الدوال، لا يمكن تمرير قيم المعاملات عبر تحديد أسمائها بل يجب تمرير المعاملات حسب ترتيب تعريفها في الدالة. كما أن من الميزات الأخرى في بايثون إمكانية إرجاع أكثر من قيمة من الدالة باستخدام الكلمة المفتاحية return ثم الفصل بين القيم المُراد إعادتها بالفاصلة، وعندئذ تُعيد الدالة قائمة تحتوي على القيم بالترتيب نفسه، إذ يمكننا مثلًا التعديل على المثال السابق لإعادة قيمتين كما يلي: def addition(x, *args): total = 0 for number in args: total += number return x * total, total result = addition(2, 5, 6, 7, 8, 14) print ( result[0], result[1] ) >> 80 40 المعاملات الديناميكية المسماة ‎**kwargs قد تحتاج أحيانًا إلى تمرير عدة معاملات مسماة دون معرفة عددها إلى الدالة والتعامل معها باسمها بدلًا من أن تكون ممرَّرة بالترتيب وعشوائيًا إلى الدالة، لذا يمكن يمكن في هذه الحالة استعمال مفهوم المعاملات الديناميكية المسماة باستخدام ** قبل اسم المعامل في تعريف الدالة، ويصبح نوع المعامل آنذاك قاموسًا ويمكن الوصول إلى عناصره بسهولة، إليك مثال: def print_name(**kid): print("His last name is " + kid["lname"]) print_name(fname = "Jamil", mname= "Ahmad", lname = "Alomar") الدوال مجهولة الاسم الدوال مجهولة الاسم Anonymous Functions وتُدعى أيضًا دوال لامدا Lambda Functions، وهي دوال لا تُعرَّف بالكلمة المفتاحية def وإنما تستخدم الكلمة المفتاحية lambda، وتُعرَّف بالشكل التالي بحيث توضع معاملات الدالة مكان args والتعليمة الوحيدة في الدالة مكان statement: lambda args : statement توجد الكثير من الاختلافات بينها وبين الدوال التي يعرِّفها المستخدِم، ونستطيع تلخيص تلك الاختلافات في نقطتين مهمتين قبل الانتقال إلى شرح هذا النوع من الدوال وحالات استخداماتها: لا تستطيع الدوال المجهولة إعادة أكثر من قيمة واحدة. لا تستطيع الدوال المجهولة احتواء أكثر من تعليمة واحدة. بالنسبة للنقطة الأخيرة فهي مهمة جدًا، ففي حين يمكن للدوال التي يعرفها المستخدِم أن يحتوي جسمها على عدد غير محدود من التعليمات والأسطر، إذ تُستخدَم الدوال المجهولة غالبًا لتنفيذ عملية بسيطة مثل العمليات الحسابية لإعادة قيمة ما. نعرِّف في المثال التالي دالةً مجهولة الاسم بسيطةً مهمتها الوحيدة هي جمع رقمين: addition = lambda x, y : x + y نلاحظ أنه قد أسنِدت الدالة إلى متغير addition، إذ يكون اسم المتغير هو نفسه اسم الدالة حتى نستطيع استدعاء الدالة عند الحاجة، وبذلك نستطيع استدعاء الدالة مع تمرير المعاملات المطلوبة x و y للحصول على ناتج الدالة بالشكل التالي: print (addition(1, 2)) >> 3 نطاق المتغيرات Variables Scope يجب هنا معرفة نطاق المتغيرات عند التعامل مع الدوال، فبناءً على معرفتك بها، تستطيع تحديد المتغيرات التي يمكنك استخدامها داخل الدالة، والمتغيرات التي يمكنك استخدامها في باقي البرنامج، فليست كل المتغيرات متاحة للوصول في كل الأماكن. يمكن تقسيم نطاقات المتغيرات إلى نطاقين هما نطاق محلي Local Scope ونطاق عام Global Scope؛ أما قاعدة نطاقات المتغيرات فهي بسيطة للغاية، فعند تعريف متغير داخل دالة ما، فسيصبح ضمن النطاق المحلي للدالة، ولا يمكن الوصول إليه من خارجها، في حين أنّ المتغيرات المعرَّفة خارج الدالة تقع ضمن النطاق العام، ويمكن الوصول إليها من أيّ مكان، ومن داخل أي كتلة أو دالة، وبالتالي يمكن استدعاء المتغيرات المعرَّفة خارج الدالة من داخلها، بينما لا يمكن استدعاء المتغيرات المعرَّفة داخل الدالة من خارجها، وبناءً على ذلك سيعمل المثال التالي دون مشكلة، إذ تحاول الدالة طباعة متغير يقع تحت النطاق العام: x = 'Hello' def example(): print (x) example() >> Hello أما المثال التالي فلن يعمل أبدًا، إذ يحاول البرنامج خارج الدالة طباعة متغير معرَّف داخل النطاق المحلي للدالة: def example(): x = 'Hello' print (x) >> NameError: name 'x' is not defined ولذلك يجب دائمًا مراعاة نطاقات المتغيرات أثناء كتابة البرامج. الوحدات Modules تُعَدّ الوحدات طريقةً لتنظيم الشيفرة وتجميعها في ملفات منفصلة، إذ يوفِّر ذلك التنظيم سهولةً في التعامل مع ملفات البرنامج، وسهولةً في قراءة الشيفرة والتعليمات، وتكون الوحدة بشكلها البسيط ملفًا بصيغة py يحتوي على أصناف أو دوال ومتغيرات تُستدعى من ملفات أخرى في البرنامج لاستخدامها مثل وجود وحدة منفصلة يُكتب فيها دوال تجري عمليات حسابية مختلفة مثل دالة لحساب مساحة الدوائر، وأخرى لحساب مساحة المثلثات، …إلخ. ثم تُستدعى تلك الوحدة من أيّ ملف يُراد فيها تنفيذ تلك المهام التي جرى تعريفها وكتابتها مسبقًا في إحدى تلك الدوال. تُنشأ الوحدات بإنشاء ملف بايثون منفصل في المجلد نفسه الذي يحتوي على مشروعك، فإذا كان برنامجك الذي تكتبه حاليًا موجودًا في ملف program.py مثلًا، فبإمكانك بجوار ذلك الملف إنشاء وحدة من خلال إنشاء ملف آخر باسم الوحدة، إذ نستطيع في مثالنا أعلاه تسمية الوحدة باسم calculations.py، بحيث تكون الكلمة calculations هي اسم الوحدة؛ أما py فهي صيغة كل الملفات التي تحتوي على شيفرة بايثون، وبعد ذلك نستطيع استيراد تلك الوحدة من الملف الرئيسي لبرنامجك باستخدام الكلمة المفتاحية import متبوعة باسم الوحدة، وعندئذ يُتاح لك الوصول إلى متغيرات ودوال تلك الوحدة في ملفك. لنضف الدالة التي كتبناها سابقًا لحساب مساحة الدائرة في تلك الوحدة الجديدة، لكن مع تعديل بسيط وهو أننا سنعرِّف المتغير الذي يحمل قيمة π في نطاق المتغيرات العام في ملف الوحدة كما يلي: pi = 3.141592654 def circle_area (radius): r_squared = radius ** 2 area = pi * r_squared return area أما في ملف برنامجنا الرئيسي الآن، فسنستطيع البدء في استيراد الوحدة، ثم استخدام دالة حساب مساحة الدائرة والوصول إلى المتغير الذي يحمل قيمة π مباشرةً، إذ نستطيع مثلًا استقبال قيمة نصف القطر من المستخدِم أثناء تشغيل البرنامج، ثم استدعاء الدالة من الوحدة مباشرةً وطباعة النتيجة للمستخدِم كما في المثال الآتي: import calculations r = input ('Please enter R value: ') r = float (r) print (calculations.circle_area(r)) كما يمكن الوصول إلى متغيرات الوحدة مثل المتغير pi الذي يحمل قيمة π كما في المثال التالي: import calculations print (calculations.pi) >> 3.141592654 من الميزات المفيدة التي تقدمها الوحدات في بايثون أنه بالإمكان استيراد متغير أو دالة معينة من الوحدة، عبر استخدام الكلمة المفتاحية from متبوعةً باسم الوحدة، ثم استخدام أمر الاستيراد import متبوعًا باسم المتغير أو الدالة المُراد استيرادها كما في المثال التالي: from calculations import pi print (pi) >> 3.141592654 الجدير بالذكر أنه عند محاولة استيراد إحدى الوحدات أو المكتبات، فسيُجري مفسر بايثون بضع محاولات لتحديد مكان الوحدة المطلوبة، وتلك المحاولات مرتبة كما يلي: البحث عن اسم الوحدة في الملفات الموجودة في المجلد نفسه الذي يحتوي على البرنامج. إذا لم يستطع إيجاد الوحدة، فسيبحث في جميع مسارات المجلدات المخزنة في متغير البيئة PYTHONPATH. إذا لم يستطع إيجاد الوحدة، فسيبحث في المسار الرئيسي المثبَّت فيه لغة بايثون، والذي يحتوي عادةً على المكتبات التي ثبِّتت على الحاسوب لتُستخدَم في جميع برامج بايثون. ومن أهم المعلومات الواجب ذكرها، قبل الانتهاء من الحديث عن الوحدات، أنه يوجد اختلاف بين الوحدات وبين المكتبات على الرغم من كون طريقة الاستيراد لكلاهما متشابهة، إذ تستورَد المكتبة كاملًا عن طريق الكلمة المفتاحية الخاصة بالاستيراد، ويستورَد جزء من المكتبة بالكلمة المفتاحية from، لكن قد تحتوي المكتبة على العديد من الوحدات والملفات، فغالبًا تكون المكتبة تطبيقًا يحتوي على العديد من الوحدات، التي بدورها تحتوي على تصنيفات ودوال ومتغيرات، وتكون تلك المكتبة في مجلد منفصل. يتطلب إنشاء مكتبة إنشاء مجلد يحتوي على ملف فارغ باسم init__.py__ ليُعرَّف المجلد على أساس مكتبة بايثون، ثم يُنشأ أيّ عدد من الوحدات -أي الملفات- لتُستورَد بعد ذلك من خلال استيراد اسم المكتبة، ولمزيد من التوضيح فإنه يمكن إنشاء مكتبة -أي مجلد- باسم calculations من أجل المثال الذي أوردناه لدالة حساب مساحة الدائرة، ثم ننشئ ملفًا فارغًا باسم init__.py__ ثم ننشئ ملفًا باسم circle.py يحتوي على محتوى الوحدة نفسها التي كتبناها للتو وبعد ذلك تكون قد حوِّلت الوحدة calculations إلى مكتبة يمكن استيراد إحدى وحداتها كما في المثال التالي: from calculations import circle print (circle.pi) >> 3.141592654 نلاحظ أننا استوردنا الوحدة circle من المكتبة ولم نستورِد المكتبة كلها، ونستطيع التفصيل بصورة أكبر من ذلك حتى، إذ نستطيع استيراد المتغير π فقط من داخل الوحدة كما في المثال التالي: from calculations.circle import pi print (pi) >> 3.141592654 وبناءً على ذلك يوجد في بايثون قدر كبير من الديناميكية عن نظيراتها من اللغات، إذ أنه كما أوردنا توجد كلمات مفتاحية تستخدَم بطرق مختلفة، كما أن ديناميكية تعريف المتغيرات وأنواع البيانات فيها أيضًا ميزة للغاية، وتوفِّر مزيدًا من التسهيلات للمبرمجين ليكون التركيز الرئيسي للمبرمج هو كتابة مهمات ودوال برنامجه، في حين يتعامل مفسر اللغة مع الحاسوب والذاكرة. للمزيد من التفاصيل حول هذه النقطة، ننصحك بقراءة مقال البرمجة باستخدام الوحدات. خاتمة إلى هنا، تعرفنا على مفهوم الدوال وكيفية تعريف دالة في لغة بايثون وهي الدوال المخصصة التي يحتاج إليها المبرمج في شيفرته ولكن عمومًا هنالك الكثير من الدوال الجاهزة المكتوبة مسبقًا توفرها أي لغة برمجة ومنها لغة بايثون تسهل على المبرمج وتجنبه من كتابة دوال مكررة في كل برنامج وهو ما سنتعرف عليه في المقال التالي. اقرأ أيضًا المقال السابق: اتخاذ القرار: العبارات الشرطية والحلقات التكرارية في البرمجة كيفية استخدام args* و kwargs** في بايثون 3 الدوال الرياضية المضمنة في بايثون 3 كيفية تعريف الدوال في بايثون 3 أهم 8 مكتبات بلغة البايثون تستخدم في المشاريع الصغيرة
  7. يُعَدّ اتخاذ القرار أحد المواضيع الأهم في هندسة البرمجيات، إذ يُستخدَم بصورة دائمة وضرورية في أيّ برنامج، في حين تُستخدَم جمل اتخاذ القرار لطرح سؤال ما، عبر المعامِلات التي تُستخدَم للموازنة التي ذكرناها في المقال السابق أنواع البيانات والعمليات الأساسية في لغة بايثون من هذه السلسلة ومن ثم اتخاذ قرار بتنفيذ أسطر معيّنة بناءً على تحقق شرط من عدمه. العبارات الشرطية يوضِّح المخطط السابق آلية عمل البرنامج عند وجود جملة شرطية فيه فقد افترضنا في المخطط السابق وجود برنامج فيه جملة شرطية واحدة فإذا كانت نعم أو True، فستُنفَّذ كتلة شيفرة معيّنة ثم يُنهى البرنامج؛ أما في حالة الإجابة بلا أو False، فستُنفَّذ كتلة شيفرة أخرى ثم يُنهى البرنامج، فالمُراد فهمه عند تلك النقطة، هو أنّ اتخاذ القرار في هذه الحالة يعني تنفيذ كتلة شيفرة وتجاهل كتلة شيفرة أخرى بناءً على شرط. يمكن كتابة الجمل الشرطية في بايثون ومحاكاة المخطط السابق عبر تعليمة if بالشكل التالي: x = 5 if x == 5: print ('X equals five') else: print ("X doesn't equal five") عرّفنا في المثال السابق متغيرًا يحتوي على القيمة العددية 5، ثم طرحنا سؤالًا في السطر الذي يليه على اللغة وهو: هل المتغير x يساوي العدد 5؟ بالطبع الإجابة نعم، وبناءً على ذلك ستُنفِّذ كتلة الشيفرة المندرِجة تحت هذا الشرط عند تحققه، أي ستُطبع X equals five، وقد استخدمنا في المثال السابق قاعدة if else، وهي ما يوضحها المخطط السابق إذ توجد كتلتان من الشيفرة، بحيث تُنفَّذ إحداهما فقط بناءً على نتيجة الجملة الشرطية. سنوضح في المخطط الآتي فسنوضح استخدام قاعدة if فقط، بحيث تُنفَّذ كتلة شيفرة عند تحقق الشرط، ولا يُنفَّذ أيّ شيء عند عدم تحققه. المثال المكافئ للمخطط بلغة بايثون يُكتب كما يلي: x = 5 if x > 3: print ("X is bigger than 3") يكون المتغير x في هذه الحالة أكبر من الرقم بالطبع، وبالتالي ستُنفَّذ كتلة الشيفرة المندرجة تحت هذا الشرط، وتُطبَع العبارة X is bigger than 3؛ أما في المثال التالي فسنغيِّر معامِل الموازنة في الجملة الشرطية، وبالتالي فلن تُنفَّذ أيّ كتلة شيفرة لعدم استخدام قاعدة if else. x = 5 if x <= 3: print ("X is less than 3 or equals to 3") ما حدث في المثال يوضحه المخطط المخطط التالي الذي يعرض مسار تنفيذ البرنامج بالسهم الأسود العريض. أوردنا في الصفحات القليلة الماضية استخدام قاعدة if وقاعدة if else، لكن ذلك ليس نهاية الأمر في اتخاذ القرار، إذ توجد قاعدة أكثر ديناميكيةً وقوةً، كما تُستخدَم لوضع سلسلة من الشروط، إذ يتحقق المفسر من الشرط إذا لم يتحقق الشرط السابق له، وهي قاعدة if elif أو if elif else. نحاكي في المخطط السابق خريطة تدفق برنامج بسيط يحتوي على ثلاث جمل شرطية، أولها كُتبت باستخدام if، وثانيها وثالثها كُتِب باستخدام elif، كما من الضروري معرفة التالي: يجب أن تبدأ الجمل الشرطية بجملة if أولًا. لا حدود لجُمل elif، إذ يمكن أن يمتد المخطط السابق إلى عدد غير محدود من الجمل الشرطية الأخرى. يمكن إضافة جملة else مرةً واحدةً فـي الجمل الشرطية، وهي تُستخدَم كآخر حل بعد عدم تحقق جميع الشروط السابقة لها، أي if و elif. فيما يلي محاكاة للمخطط السابق في بايثون، إذ سيسلك التنفيذ مسارًا يتحقق فيه من أول شرط فيجده خاطئًا، وبعدها يتجه إلى الشرط الثاني الموجود في elif فيجده خاطئًا أيضًا، ثم يتجه إلى الشرط الثالث فيجده صحيحًا، وعندها ينفِّذ كتلة الشيفرة الموجودة بداخله. name = "Adam" if name == "Noah": print ("The name is Noah") elif name == "Jacob": print ("The name is Jacob") elif name == "Adam": print ("The name is Adam") >> The name is Adam نضيف إلى ذلك إمكانية استخدام جملة else في نهاية الشيفرة كي تُنفَّذ شيفرة ما إضافية في حالة عدم تحقق أيّ شرط من الشروط التي وُضعت، لذا نستطيع تغيير المثال السابق بتعديل قيمة المتغير كما يلي: name = "Yusuf" if name == "Noah": print ("The name is Noah") elif name == "Jacob": print ("The name is Jacob") elif name == "Adam": print ("The name is Adam") else: print ("We don't know the name") >> We don't know the name بناءً على كل ما سبق، فإنه يمكن إضافة عدد غير محدود من جمل التحقق أو الشروط باستخدام ما ذُكِر من قواعد، كما يمكن كتابة تلك الشروط بصورة متداخلة أيضًا، وتُعَدّ تلك التعابير الشرطية conditional expression مهمةً للغاية، وتُستخدَم في كل البرامج تقريبًا، إذ أنه لا استغناء عن التحقق من المدخَلات والبيانات في كل البرامج الذكية. حلقات التكرار تُعَدّ حلقات التكرار Loops إحدى أهم الميزات المستخدَمة بكثرة في كل لغات البرمجة، إذ قد يود المبرمج في كثير من الأحيان تكرار كتلة شيفرة معيّنة لمدة معيّنة قد تكون عددًا معيّنًا من المرات، أو إلى حين تحقق شرط معيّن، وذلك ما توفره لنا حلقات التكرار في بايثون وفي كثير من اللغات البرمجية الأخرى، كما تُستخدَم استخدامًا واسعًا في كثير من الخوارزميات وبالطبع في معظم البرامج. نوضِّح في المخطط السابق آلية عمل البرنامج عندما يقابل حلقة تكرار في البرنامج، إذ يردّ المفسر على الجملة الشرطية أو السؤال، وبناءً على الإجابة إما يتخطى كتلة الشيفرة المُرمز إليها على يمين المخطط، أو يُنفِّذ كتلة الشيفرة، ثم يردّ على الجملة الشرطية مرةً أخرى، ليرى إذا كانت نتيجة الجملة الشرطية قد اختلفت في تلك المرة، وهكذا إلى أن تصبح إجابة الجملة الشرطية خطأ False، فيتخطى المفسر كتلة الشيفرة تلك المرة، ويستكمل البرنامج إذا كانت هناك تعليمات أخرى أو ينهي العمل. توجد طريقتان لاستعمال الحلقات التكرارية في بايثون، الأولى هي باستعمال تعليمة While والثانية باستعمال تعليمة For، إذ تُستخدَم الأولى لتكرار تنفيذ كتلة شيفرة ما بناءً على جملة شرطية كما وضَّحنا من خلال المخطط السابق؛ أما القاعدة الثانية فتكرِّر تنفيذ كتلة شيفرة معيّنة ولعدد معيّن من المرات. حلقة While تُستخدَم هذه الحلقة كما أشرنا في تكرار كتلة شيفرة بناءً على جملة شرطية، وما دامت الجملة الشرطية مُحققةً، فسيُكرَّر تنفيذ كتلة الشيفرة إلى حين عدم تحقق الجملة الشرطية، ويكون بناء جُمل قاعدة حلقة While كما في المثال التالي: while expression: statements تحل الجملة الشرطية التي قد تحتوي على شرط واحد أو أكثر محل التعبير expression ثم تحل كتلة الشيفرة المُراد تنفيذها محل التعليمات statements، ففي المثال التالي نعرِّف متغيرًا عدديًا ثم نطبع الرقم طالما أنه أصغر من 9، مع زيادة قيمة المتغير بعدد واحد صحيح في كل مرة تُنفَّذ فيها كتلة الشيفرة. x = 0 while x < 9: print (x) x += 1 ومن الجدير بالملاحظة أنه يمكن اختصار تركيب الجملة الخاصة بحلقة While إذا كانت كتلة الشيفرة التي تحتويها عبارةً عن سطر واحد كما يلي: x = 0 while x < 9: print (x); x += 1 print ('Done') يختصر المثال أعلاه المثال الذي يسبقه تمامًا، مع إضافة طباعة كلمة Done بعد الانتهاء من الحلقة، وذلك بالطبع لأن جملة طباعة كلمة Done تأتي في مستوى حلقة While نفسه، ولذلك هي لا تندرج تحت حلقة While، وإنما تليها بناءً على قاعدة السطور والمسافات السابق ذكرها. سيطبع المثال السابق الأعداد من 0 إلى 8، إذ أنه عندما تساوي قيمة المتغير x الرقم 9، فسيصبح الشرط في الحلقة خاطئًا، وبالتالي لن تُنفَّذ كتلة الشيفرة. توجد ما تُدعى بالحلقة اللانهائية Infinite Loop، وذلك يحدث عندما لا يتحول التعبير الشرطي في حلقة While إلى False أبدًا، لتُنفَّذ كتلة الشيفرة إلى الأبد، لذلك فإن آخر سطر في كتلة الشيفرة في المثال السابق، عندما نزيد قيمة المتغير x، هي خطوة ضرورية لكي لا تُنفَّذ كتلة الشيفرة إلى الأبد، لذلك في معظم الأحيان يجب أن تحتوي كتلة الشيفرة بداخل حلقة While على تغيير ما، يؤثِّر على إحدى المتغيرات الموجودة في التعبير الشرطي في الحلقة، وهنالك بالطبع استخدامات جيدة ومفيدة للحلقات اللانهائية في بعض البرامج خصوصًا في الألعاب، إذ تتطلب بعض التطبيقات تنفيذ تعليمات تنفيذًا مستمرًا ودائمًا. حلقة For تُنفِّذ حلقة For كما أوردنا كتلة الشيفرة المندرجة بداخلها عددًا معيّنًا من المرات، ويُحدَّد ذلك العدد من خلال عدد عناصر السلسلة الموضوعة في تعبير الحلقة، وقد تكون السلسلة صفًا أو قائمةً أو مجموعةً، ويكون بناء جملة قاعدة حلقة For كما يلي: for variable in sequence: statements يحل اسم المتغير الذي يحتوي على قيمة العنصر الحالي أثناء دوران الحلقة محل variable في حين يحل اسم المتغير الذي يحتوي على السلسلة محل sequence والذي قد يكون قائمةً، أو مجموعةًً، أو صفًا؛ أما statements فيحل محلها كتلة الشيفرة المُراد تكرار تنفيذها. نوضِّح في المخطط التالي مسار عمل المفسر أثناء العمل على حلقة For، إذ نكرِّر تنفيذ كتلة الشيفرة بناءً على عدد العناصر الموجودة في قائمة تحتوي على أسماء أشخاص. ويوضح المثال التالي تنفيذ المخطط بلغة بايثون: names = ['Adam', 'Noah'] for name in names: print (name) عرَّفنا قائمةً تحتوي على اسمَي Adam و Noah، ثم استخدمنا معها حلقة For، وفي تلك الحالة ستُنفَّذ كتلة الشيفرة بداخل الحلقة مرتين فقط، وذلك لأنّ السلسلة التي استُخدِمت هي قائمة تحتوي على عنصرين اثنين فقط، ففي المرة الأولى ستستطيع كتلة الشيفرة بداخل الحلقة الوصول إلى العنصر الأول في السلسلة -أي الاسم Adam- ثم في المرة الثانية ستستطيع كتلة الشيفرة بداخل الحلقة الوصول إلى العنصر الثاني في السلسلة -أي الاسم Noah- وتتوقف لعدم وجود عناصر أخرى في السلسلة. لاحظ أنه يمكن استخدام النصوص في حلقات التكرار عبر قاعدة for، إذ أنّ كل محرف في النص هو عنصر مستقل بداخل السلسلة النصية، ففي المثال نطبع كل حرف من محارف كلمة Hello بصورة مفرَدة باستخدام حلقة for. for letter in 'hello': print (letter) >> h >> e >> l >> l >> o يمكن أيضًا إضافة حلقات تكرار داخل حلقات أخرى، أو إضافة جمل شرطية داخل حلقات التكرار، أو إضافة جمل شرطية داخل جمل شرطية أخرى إلى ما لا نهاية، فلا يوجد ما يمنع أن تحتوي كتلة الشيفرة الموجودة داخل إحدى القواعد، على قاعدة أخرى وكتل أخرى بأيّ عدد. الدالة range تُستعمل الدالة range عادة مع حلقات التكرار for والتي تعني بالعربية نِطَاق أو مَدَى لتنفيذ كتلة الشيفرة عددًا معيّنًا من المرات عبر سلسلة من الأعداد التي يُحدَّد العدد الأدنى والعدد الأقصى فيها، إليك المثال التالي: for number in range(1, 6): print (number) سيطبع المثال بناءً على ذلك الأرقام من 1 إلى 5 بالتتالي ولاحظ أن العدد الأقصى لا يدخل ضمن المجال. ويمكن استخدام معامل ثالث اختياري للدالة يُسمى معامل الخطوة يحدد مقدار الزيادة بين كل عنصر والذي يمكن أن يكون أي رقم صحيح، سواء كان موجبًا أو سالبًا، إليك المثال التالي: for number in range(1, 6, 2): print (number) سيطبع المثال أعلاه الأرقام من 1 و3 و5 التعليمة else يمكن استخدام الكلمة المفتاحية else في حلقتَي While وFor، إذ تُكتَب بعد كتلة الشيفرة المُنفَّذة على مستوى تعبير الحلقة نفسه، ثم إدماج كتلة شيفرة تُنفَّذ عند انتهاء تنفيذ الحلقة، فحتى إذا نُفِّذت كتلة الشيفرة الموجودة بداخل الحلقة مرةً أو ثلاث مرات أو أيّ عدد من المرات، أو لم تُنفَّذ على الإطلاق، فستُنفَّذ دائمًا كتلة الشيفرة التابعة للكلمة المفتاحية else في النهاية، كما في الأمثلة الآتية: for number in []: print (number) else: print ('Done') >> Done يتخطى مسار التنفيذ حلقة التكرار في المثال السابق لعدم وجود عناصر في السلسلة المُزوَّدة، لكنه مع ذلك ينفِّذ كتلة الشيفرة المندرجة بداخل الكلمة المفتاحية else أما في المثال التالي فتُنفَّذ حلقة التكرار مع تعليمة else: for number in [1, 2]: print (number) else: print ('Done') >> 1 >> 2 >> Done التوقف والتخطي في حلقات التكرار تُعَدّ الكلمات break وcontinue وpass من أهم الكلمات المفتاحية الأخرى المُستخدَمة في حلقات التكرار، فكل واحدة منها يمكن استخدامها في حلقة While أو حلقة For على حد سواء. تُستخدَم أولًا كلمة break في الخروج من الحلقة كلها واستكمال باقي البرنامج، وغالبًا ما يتم ذلك عبر وضع جملة شرطية أولًا، إذ أنه عند تحقق ذلك الشرط، يخرج مسار التنفيذ من الحلقة تمامًا، ويستكمل تنفيذ باقي البرنامج، فإذا وُجدت حلقة for تعمل على قائمة تحتوي على ثلاثة أسماء مثلًا، فسنستطيع طباعة أول عنصر فقط من خلال التحقق من قيمة العنصر الحالي في الحلقة، فإذا كان لا يساوي ما نريد طباعته فسنخرج من الحلقة كاملةً كما في المثال التالي الذي يطبع أول عنصر فقط وهو Adam، وعند وجود أيّ قيمة أخرى، فسنخرج من الحلقة. x = ['Adam', 'Noah', 'Jacob'] for name in x: if name != 'Adam': break print (name) >> Adam كما يمكن إيضاح مسار تنفيذ المثال السابق عبر المخطط الآتي: نلاحظ أنّ قيمة المتغير name ستحمل القيمة Adam في أول دورة، وعندما يُتحقَّق من الجملة الشرطية هل الاسم لا يساوي آدم؟ فستكون الإجابة بالطبع لا، وستُنفَّذ عملية طباعة الاسم؛ أما في الدورة الثانية، فسيحمل المتغير القيمة Noah، وبالتالي سيتحقق الشرط أي يصبح True، وبالتالي ستُنفَّذ كتلة الشيفرة المندرجة تحت الشرط مباشرةً، وتلك الكتلة تحتوي على تعليمة واحدة وهي break، والتي تعني إنهاء الحلقة بجميع دوراتها واستكمال باقي البرنامج. أما الكلمة المفتاحية continue، فتُستخدَم لتخطي الدورة الحالية والبدء في الدورة التالية مباشرةً، بمعنى أنها تقوم بإنهاء الدورة الحالية، وتخطي باقي التعليمات الموجودة في كتلة الشيفرة الخاصة بحلقة التكرار، على عكس break التي تنهي حلقة التكرار تمامًا، لينتقل مسار التنفيذ لما بعد قاعدة For أو While، ونوضِّح في المخطط التالي آلية عمل الكلمة المفتاحية continue والتي غالبًا ما تُنفَّذ من خلال جملة شرطية. نكتب المخطط السابق بلغة بايثون بالشكل التالي: x = ['Adam', 'Noah', 'Jacob'] for name in x: if name == 'Adam': continue print (name) >> Noah >> Jacob لدينا قائمةً تحتوي على ثلاثة أسماء، إذ نهدف من البرنامج إلى طباعة أيّ اسم في القائمة غير اسم آدم Adam، فلذلك كتبنا جملةً شرطيةً بداخل حلقة التكرار for، إذ تطرح الجملة الشرطية على المفسر سؤالًا وهو: هل الاسم الحالي يساوي آدم؟ ستكون الإجابة في أول دورة صحيحةً True، ولذلك سينفِّذ المفسر التعليمات المندرجة أسفل الشرط، إذ توجد تعليمة وحيدة مندرجة أسفل هذا الشرط وهي الكلمة المفتاحية continue، والتي تخبر مفسر اللغة كما ذكرنا ببساطة، بعدم استكمال الدورة الحالية والذهاب مباشرةً إلى الدورة التالية، أي العنصر التالي في القائمة وهكذا سيُطبَع الاسمان Noah و Jacob فقط. يمكن استخدام الكلمتان المفتاحيتان السابق ذكرهما break وcontinue في حلقات while وحلقات for على حد سواء، وفيما يلي مثال على استخدام continue في حلقة while. سنطبع الأعداد الزوجية فقط بين الصفر والعشرة ونتخطى الأرقام الفردية، كما يمكننا التحقق من كون الرقم فرديًا أو زوجيًا بصورة بسيطة في الرياضيات عبر ناتج باقي القسمة على 2، إليك المثال: x = 0 while x <= 10: if x % 2 != 0: x += 1 continue print (x) x += 1 تعرفت الآن على كيفية التحكم في مسار تنفيذ البرنامج وآلية اتخاذ القرار أثناء التنفيذ لذا جرب الآن كتابة برامج وتدرب بها على ما تعلمته، وسننتقل في المقال التالي إلى الحديث عن الدوال في لغة بايثون. اقرأ أيضًا المقال السابق: أنواع البيانات والعمليات الأساسية في لغة بايثون الحلقات التكرارية في البرمجة مقدمة في البرمجة الشرطية
  8. استعرضنا في المقال السابق البرمجة بلغة بايثون لمحةً عن لغة بايثون وكيفية تثبيتها وكتابة تعليمات فيها، وسنكمل في هذا المقال تعلم بايثون والتعرف عليها أكثر بالتعرف على المتغيرات وأنواع البيانات بالإضافة إلى أنواع العمليات التي يمكن تطبيقها على المتغيرات. المتغيرات تمثِّل المتغيرات في أيّ لغة برمجية أماكن في ذاكرة الحاسوب مخصصةً لتخزين البيانات، وذلك يعني أنك عندما تعرِّف متغيرًا ما حسب قواعد المعرِّفات التي ذكرناها في المقال السابق، فأنت بذلك تحجز مكانًا في الذاكرة لتخزين ذلك المتغير وما يحتويه. تعريف المتغيرات يمكن تعريف متغير في بايثون بسهولة كبيرة وذلك بذكر اسم المتغير وإسناد قيمة له بالشكل التالي: x = 10; print(x) y = 10.5; print(y) z = "Word"; print(z) عرَّفنا تلك المتغيرات في المثال السابق وأسندنا لها قيمة مباشرةً لتحجز بايثون لتلك المتغيرات مساحة في ذاكرة الحاسوب، أولها المتغير x من نوع بيانات الأعداد الصحيحة Integer، وثانيها المتغير y من نوع بيانات الأعداد العشرية Float؛ أما ثالثها فهو المتغير z من نوع نص أو سلسلة نصية String، وعلى الرغم من اختلاف أنواع تلك المتغيرات، إلا أننا لم نحدِّد تلك الأنواع مسبقًا، فقد عيّنا البيانات في المتغير فقط بعد تحديد اسم المتغير مباشرةً، ويمكن تغيير نوع البيانات في المتغير ببساطة وبدون أدنى مشكلة، وذلك ليس هو الحال في كل اللغات. x = 10; print(x) x = "word"; print(x) فعند تعريف متغير جديد في كثير من لغات البرمجة، يلتزم المبرمِج بتعريف نوع البيانات التي ستُخزَّن في المتغير، وبناءً على ذلك تحدِّد اللغة مكانًا له في الذاكرة ولا يمكنك عادةً في تلك اللغات تغيير نوع البيانات التي قيدت بها المتغير لأن اللغة تفحص المتغير ونوعه أثناء تنفيذ كامل الشيفرة، فإذا عرَّفت متغير ما على سبيل المثال وحدَّدت نوع البيانات بأنه عدد عشري، فلا يمكن بعد ذلك تخزين نص في ذلك المتغير -أي في ذلك المكان في الذاكرة- وذلك النوع من اللغات يُطلق عليه لقب Statically Typed مثل Fortran و Java وC؛ أما بايثون فتُعَدّ من اللغات الديناميكية، أي لا تتطلب تحديد نوع المتغير صراحةً، إذ يستطيع المتغير حواية أيّ نوع من أنواع البيانات وتغييرها لاحقًا، ولمزيد من التفاصيل حول هذه النقطة، ارجع إلى مقال لغات البرمجة قسم أنواع لغات البرمجة. ومن ديناميكية لغة بايثون وجود إمكانية تعريف وتعيين قيمة لعدة متغيرات في التعليمة نفسها، ففي المثال التالي نسند رقم إلى ثلاثة متغيرات في الوقت نفسه. x = y = z = 1 سيُطبَع الرقم 1 عند طباعة أيّ متغير من تلك المتغيرات لأنها تحمل البيانات نفسها، كما أنه من الممكن تعريف أكثر من متغير وتعيين بيانات مختلفة من أنواع متطابقة أو مختلفة لكل متغير في السطر نفسه كما في المثال التالي: x, y, z = 10, "Hello", "World" print(x) print(y) print(z) >> 10 >> Hello >> World حذف المتغيرات يمكنك في أيّ وقت حذف متغير أو أكثر من ذاكرة الحاسوب في بايثون وفي العديد من لغات البرمجة الأخرى، فتلك الخطوة مهمة في العديد من البرامج والتطبيقات لترشيد استهلاك ذاكرة الحاسوب خاصةً في التطبيقات الكبيرة والمعقَّدة والتي تحتوي على الكثير من البيانات والعمليات الحسابية مثل تطبيقات تعلُّم الآلة، إذ يُحذَف المتغير في بايثون عن طريق الكلمة المفتاحية del ثم اتباعها بمسافة وكتابة اسمه كما في المثال التالي وعندها لا يمكن استخدام المتغير بعد حذفه: x = 1 print (x) del x أنواع البيانات في لغة بايثون تختلف أنواع البيانات التي تدعمها لغة بايثون، حيث تدعم البيانات النصية، وهي البيانات التي تحتوي على أحرف ونصوص Strings، والبيانات العددية سواءً كانت أعدادًا صحيحةً Integers أو أعدادًا عشرية Floats، أو أعدادًا عقدية Complex Numbers، كما توجد أنواع أخرى مثل الصفوف Tuples والقواميس Dictionaries والقوائم Lists. النصوص Strings توجد بعض المعلومات القيمة فيما يتعلق بالتعامل مع النصوص في بايثون، إذ توفِّر بايثون بعض العمليات الأساسية للتعامل مع النصوص وطباعتها والوصول إلى الأحرف والمقاطع داخل النص، حيث يمكن للمبرمج التعامل مع المتغير الذي يحتوي على نص مثل تعامله مع المصفوفات في اللغات الأخرى، وإذا كانت لديك خلفية في الرياضيات، فإنّ المصفوفات في البرمجة شبيهة بقدر كبير للمصفوفات في الرياضيات، كما توفِّر لغات البرمجة أساليب مختلفةً للتعامل مع تلك المصفوفات. يمكن التعامل مع المتغيرات النصية في بايثون على أساس مصفوفة ذات بُعد واحد، إذ يمثِّل كل حرف قيمةً بذاتها داخل المصفوفة، فيمكن التفكير في النص التالي Hello World!‎ على أنه مصفوفة يُربَط كل حرف أو مسافة فيه بعدد، بحيث يبدأ الترقيم في المصفوفات وغيرها من أنواع البيانات المشابهة في معظم لغات البرمجة بالرقم صفر وليس واحد، أي كما يلي: 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; } 11 10 9 8 7 6 5 4 3 2 1 0 ! d l r o W o l l e H يمكننا طباعة المحرف بناءً على الجدول بكتابة اسم المتغير الذي يحتوي على النص ثم كتابة رقمه بين علامَتي الأقواس المعقوفة []، فلطباعة المحرف r مثلًا نستخدِم الرقم 8، ولطباعة المحرف H من النص السابق يمكن ذلك كما في الشيفرة التالية: x = "Hello World!" print(x[0]) >> H يمكن الطباعة من محرف إلى آخر عن طريق كتابة رمز : بين رقم محرف البداية وعدد المحارف المطلوبة، فلطباعة كلمة Hello مثلًا، فسنبدأ بصفر ثم نعُد خمسة محارف من H إلى O، ولذلك نستطيع طباعة الكلمة عبر الشيفرة الآتي: x = "Hello World!" print (x[0:5]) >> Hello كما يمكن الطباعة من حرف محدد إلى نهاية النص عن طريق إضافة رمز النقطتين السابق ذكره وعدم إلحاقه بأيّ عدد. x = "Hello World!" print (x[0:]) >> Hello World! print (x[6:]) >> World! يمكن أيضًا طباعة النص مرتين باستخدام علامة الضرب الرياضية. x = "Hello World! " print (x * 2) >> Hello World! Hello World! القوائم Lists تتوفر في بايثون ما يُدعى بالقوائم Lists، وهي تشبه إلى حد كبير المصفوفات في لغات البرمجة الأخرى، فببساطة يمكن تعريف القائمة في بايثون بأنها متغير يحتوي على مصفوفة من البيانات والتي من الممكن أن تكون من أنواع مختلفة، كما من الممكن التعامل معها كما أوردنا في التعامل مع النصوص على أساس مصفوفة، إذ تُعيَّن قيمة متغير القائمة عبر كتابة القيم بصورة متتابعة يليها فاصلة , بين الأقواس المعقوفة []، وإليك المثال التالي: x = ['abc', 'efg', 100, 'ilm', 10.54] print (x[0]) >> abc print (x) >> ['abc', 'efg', 100, 'ilm', 10.54] print (x[1:3]) >> ['efg', 100] print (x[1:]) >> ['efg', 100, 'ilm', 10.54] print (x * 2) >> ['abc', 'efg', 100, 'ilm', 10.54, 'abc', 'efg', 100, 'ilm', 10.54] y = ['xyz'] print (x + y) >> ['abc', 'efg', 100, 'ilm', 10.54, 'xyz'] كما نرى في المثال السابق أننا نستطيع الجمع بين قائمتين عبر علامة الجمع، كما نستطيع مضاعفة القائمة وتكرار قيمها عبر علامة الضرب. الصفوف Tuples تشبه الصفوف Tuples في بايثون القوائم إلى حد كبير، إذ تحتوي على مجموعة من القيم بصورة متتابعة ويلي كل منها فاصلة، لكنها تُضمَّن بين أقواس عادية ( ) على عكس القوائم، وتملك ميزات القوائم نفسها كما في المثال التالي: x = ('abc', 'efg', 100, 'ilm', 10.54) print (x[0]) >> abc print (x) >> ('abc', 'efg', 100, 'ilm', 10.54) print (x[1:3]) >> ('efg', 100) print (x[1:]) >> ('efg', 100, 'ilm', 10.54) print (x * 2) >> ('abc', 'efg', 100, 'ilm', 10.54, 'abc', 'efg', 100, 'ilm', 10.54) y = ('xyz', 'abc') print (x + y) >> ('abc', 'efg', 100, 'ilm', 10.54, 'xyz', 'abc') توجد اختلافات بين القوائم والصفوف نلخصها في النقطتين الآتيتين. أولًا، عند إنشاء صف يحتوي على عنصر واحد، فسيُختصَر ذلك تلقائيًا في بايثون إلى نوع ذلك العنصر بعكس القوائم، إذ أنه إذا عُرِّف صف يحتوي على عنصر واحد وليكن نصًا، فسيُخزَّن في الذاكرة على أنه نص وليس صفًا؛ لذا أضفنا عنصرين عند تعيين قيمة للمتغير y في الشيفرة السابقة، كي يُخزَّن المتغير y على أساس صف وليس على أساس نص، ولكي يُتعامَل معه على أساس صف، وليتمكن مفسر اللغة من تنفيذ العملية الأخيرة وهي جمع صفين. ثانيًا، لا يمكن تغيير قيمة عنصر من عناصر الصفوف، في حين يمكن تغيير قيمة عنصر من عناصر القوائم كما يلي: x = ['hello', 'world'] x[1] = 'there' print (x) >> ['hello', 'there'] أما عند محاولة تنفيذ التعليمات نفسها على الصفوف، فلن يقبل المفسر تغيير قيمة العنصر، إذ يجب بقاء قيمة العناصر في الصفوف ثابتةً، فمن الممكن تغيير المتغير كله، لكن لا يمكن تغيير قيمة عنصر واحد من داخل الصف مباشرةً، فعند أداء تلك المحاولة، فسيطبع مفسر اللغة الخطأ التالي: TypeError: 'tuple' object does not support item assignment. ما يعني أنّ الصفوف لا تدعم تعيين قيم للعناصر. القواميس Dictionary يشبه القاموس Dictionary في بايثون جدول Hash أي Hash-table وهو إحدى هياكل البيانات في علوم الحاسوب بحيث تُربَط القيم بمفتاح ومن ثم البحث عن القيم بسرعة كبيرة إذ أنها تعمل على أساس مصفوفة تحتوي على أزواج من المفاتيح والقيم، بحيث يوجد مفتاح ما لكل قيمة يتيح الوصول إليها، وتُعرَّف القواميس عبر الأقواس المعقوصة { }، كما أنّ القواميس بدورها غير مرتبة، بعكس الصفوف والقوائم التي تُرتَّب فيها مفاتيح كل عنصر بالترتيب الرقمي بدءًا من الصفر أي يعيِّن المبرمج مفاتيح القواميس والتي من الممكن أن تكون نصيةً أو عدديةً بدون ترتيب. x = {'key': 'value', '2ndKey': '2ndValue'} print (x['key']) >> 'value' يمكن للمفاتيح أن تكون نصيةً أو عددية مثل الصفوف والقوائم، أي كما يلي: x = {'key': 'value', 2: '2ndValue'} print (x[2]) >> '2ndValue' كما يمكن تغيير قيمة أحد عناصر القاموس مثل القوائم كما يلي: x = {'key': 'value', 2: '2ndValue'} x['key'] = "Hello" print (x) >> {'key': 'Hello', 2: '2ndValue'} ويمكن استخلاص مفاتيح القاموس أو قيمه عبر التوابع keys وvalues كما يلي: x = {'key': 'value', 2: '2ndValue'} print (x.keys()) >> dict_keys(['key', 2]) print (x.values()) >> dict_values(['value', '2ndValue']) المجموعات Sets تشبه المجموعات Sets والتي هي هياكل البيانات في بايثون إلى حد كبير الصفوف، لكنها تُكتب باستخدام الأقواس المعقوصة مثل القواميس، إذ نعرِّف في المثال التالي مجموعةً تحتوي على بضعة أسماء ثم نطبع عناصر المجموعة في السطر الثاني: names = {'Adam', 'Noah'} print (names) قد يختلف ترتيب العناصر عند طباعة المجموعة لأن المجموعات في الأساس لا ترتِّب العناصر؛ لذلك قد يختلف ترتيب عناصر المجموعة في كل مرة يُشغَّل فيها البرنامج، أيضًا من الضروري معرفة أنّ المجموعات لا تُذكَر كثيرًا في الدورات ومساقات التعليم، إذ أنها محدودة للغاية في استخداماتها، ولا يمكن طباعة عنصر معيّن منها إلا بطرق معقدة وهي لا تستخدَم بصورة شائعة. تحويل أنواع البيانات في لغة بايثون توفِّر بايثون الكثير من الدوال التي يمكن استخدامها لتحويل أنواع البيانات إلى أنواع أخرى، فأحيانًا قد تضطر إلى تحويل سلسلة نصية إلى عدد إذ أنه من الممكن وجود أعداد مكتوبة على صورة سلسلة نصية مثل: x = '2' لا يمكنك في تلك الحالة إجراء عمليات حسابية على المتغير x، إذ أنه قد يحمل قيمةً نصيةً بالنسبة للمفسر بما أنّ الرقم متضمِّن بين علامتَي تنصيص، كما يمكنك التأكد من نوع المتغير باستخدام الدالة type. print (type(x)) >> <class 'str'> تُعَدّ كلمة str اختصارًا لكلمة String أي سلسلة نصية، وبالتالي ستفشل محاولات تنفيذ أيّ عملية حسابية على المتغير، إذ ستتسبب عمليتَي القسمة والطرح في ظهور رسالة خطأ؛ أما عمليتَي الجمع والضرب فستنتجان نتائج غير متوقعة، إذ أنه سيُتعامَل مع المتغير كما يُتعامَل مع النصوص، وبالتالي ستفضي عملية الضرب إلى تكرار النص عدد مرات يساوي العدد المضروب فيه، أما عملية الجمع فستلصق شقَّي الجمع على أساس نصوص وليس على أساس أعداد. x * 2 >> '22' x + '4' >> '24' لاحظ أنه عند محاولة الجمع قمنا بتضمين الرقم بين علامتي تنصيص، لكي يكون طرفي الجمع نصَّين بما أنّ المتغير x يحتوي على نص، فلا يمكن جمع نوعين مختلفين من البيانات، فإما نصوصًا معًا، أو أعدادًا معًا. تُقرأ الأعداد في الكثير من الحالات على أساس نصوص، ولذلك تُعَدّ دوال تحويل أنواع البيانات من الميزات المهمة في لغة بايثون وغيرها من لغات البرمجة، وسنسرد فيما يلي أهم دوال تحويل البيانات في بايثون مع الأمثلة. التحويل إلى عدد صحيح int يمكن تحويل البيانات مثل النصوص إلى رقم صحيح باستخدام الدالة int التي هي اختصار لكلمة Integer، أي عدد صحيح، ففي المثال الآتي حوِّل المتغير النصي x إلى عدد صحيح، كما يمكن تنفيذ عمليات حسابية عليه. x = '2' x = int(x) print (type(x)) >> <class 'int'> print (x * 2) >> 4 التحويل إلى عدد عشري float يمكن تحويل البيانات مثل النصوص إلى عدد عشري باستخدام الدالة float، ففي المثال حوِّل نص إلى عدد عشري، كما يمكن تنفيذ عمليات حسابية عليه. x = '2.5' x = float(x) print (type(x)) >> <class 'float'> print (x * 2) >> 5.0 التحويل إلى نص str يمكن تحويل البيانات مثل الأعداد الصحيحة والعشرية إلى نصوص عبر الدالة str، ففي المثال التالي حُوِّل عدد عشري إلى نص. x = 1.5 x = str(x) print (type(x)) >> <class 'str'> التحويل إلى صف Tuple يمكن تحويل بعض أنواع البيانات لتكون صفًا Tuple باستخدام الدالة tuple، ومن تلك الأنواع النصوص والقواميس والقوائم، ولكن لا يمكن تحويل الأعداد إلى صفوف. يحوَّل كل حرف أو مسافة في النصوص إلى عنصر في الصف؛ أما القواميس فتُحوَّل كل المفاتيح فيها إلى عناصر في الصف، في حين تتحول القوائم إلى صفوف بالترتيب نفسه وبالعناصر نفسها، ونحوِّل في المثال التالي نصًا إلى صف ثم نطبعه لنرى أنه قد حوِّل المتغير إلى صف ذي عنصر واحد. x = '1'; x = tuple(x); print (x) >> ('1',) التحويل إلى قائمة List يمكن لبعض أنواع البيانات أن يتم تحويلها، إلى قائمة عبر الدالة list ، تمامًا كحال دالة التحويل إلى صف، يمكن تحويل الصفوف والقواميس والنصوص إلى قوائم. تعمل دالة التحويل إلى قائمة، كحال دالة التحويل إلى صف، فعند تحويل صف إلى قائمة، يتم تحويل العناصر كلها بنفس الترتيب، أما في حالة تحويل نص إلى قائمة، يتم تحويل كل حرف أو مسافة في النص، إلى عنصر في القائمة، أما في حالة تحويل القواميس إلى قائمة، فيتم تحويل مفاتيح القاموس إلى عناصر في القائمة. نُحوِّل في المثال الآتي نص إلى قائمة، ومن ثم نطبعه. x = 'Hello'; x = list(x); print(x) >> ['H', 'e', 'l', 'l', 'o'] التحويل إلى قاموس Dictionary على عكس باقي الدوال السابق ذكرها حيث يمكن التحويل من نوع بيانات إلى قائمة أو صف، لا يمكن تحويل نوع آخر إلى قاموس بشكل تلقائي من اللغة، حيث أن القاموس يحتاج إلى مفاتيح وقيم لكل مفتاح، وهي بيانات غير متوفرة في باقي أنواع البيانات التي أوردتها، حتى يتم التحويل منها إلى قاموس. مع ذلك يمكنك إنشاء قاموس جديد عبر الدالة dict، وهي طريقة أخرى لإنشاء القواميس في بايثون، ويمكنك في أي من الطريقتين القيام بنسخ قيم متغيرات أخرى أثناء إنشاء القاموس. في المثال الآتي نقوم بتعريف متغير جديد يحمل قيمة نصية، ومن ثم سنقوم بإنشاء قاموس بالطريقة التقليدية التي أوردتها من قبل، باستخدام المتغير النصي، ثم نقوم بفعل الأمر نفسه مجددًا عبر دالة dict. x = 'value'; y = {'key': x, 'a': 'b'}; print (y) >> {'key': 'value', 'a': 'b'} z = dict(key = x, a = 'b'); print (z) >> {'key': 'value', 'a': 'b'} العمليات والعوامل في لغة بايثون تتوفر في بايثون وفي العديد من لغات البرمجة الأخرى العديد من العوامل المفيدة التي تساعدنا على إجراء مختلف العمليات مثل العمليات الرياضية والحسابية أو عمليات التعريف والإسناد وسمي عامل من عوامل لأنه يجري عملية على عدة مُعامَلات تُستخدم معه مثل عملية الجمع 5+5 تسمى الإشارة + بعامل والعدد 5 بالمُعامَل. ويوجد في بايثون سبعة أنواع مختلفة من العوامل نستعرضها كما يلي: العوامل الرياضية أو الحسابية Arithmetic Operators تهدف العوامل الحسابية من اسمها إلى تنفيذ العمليات الحسابية من خلال شيفرة بايثون، فعلامات الجمع + والطرح - والقسمة / والضرب * كلها من العوامل الحسابية، ونذكر أيضًا عامل باقي القسمة وهو علامة النسبة المئوية %، وقد أوردتُ أمثلةً على جميع تلك العوامل في بداية المقال. نضيف إلى تلك العوامل عاملَين آخرين هما العامل الأسي Exponent Operator وعامل القسمة الصحيحة Floor Division. أما العامل الأسي ** فيقوم بعملية ضرب أُسي على الأرقام، فمثلًا 5 أُس 4 تعني ضرب الرقم 5 في نفسه 4 مرات، وينتج ذلك الرقم 625. أما مهمة عامل القسمة الصحيحة //، هي عملية القسمة العادية مع إنتاج الرقم الصحيح فقط من الناتج دونًا عن الأرقام والكسور العشرية، فمثلًا ناتج قسمة 26 على 5 هو 5.2، لكن في القسمة الصحيحة ينتج ذلك الرقم 5 فقط ويتم حذف 0.2 من الناتج. x = 5 print (x ** 4) >> 625 y = 26 print (y // 5) >> 5 معوامل الموازنة Comparison Operators تُستخدم تلك العوامل للموازنة بين القيم والمتغيرات، إذ يكون الأمر أشبه بطرح سؤال ما على لغة بايثون مثل هل قيمة المتغير كذا تساوي قيمة المتغير كذا؟ أو هل قيمة المتغير كذا لا تساوي قيمة المتغير كذا؟ فتلك الأسئلة من الضروري سؤالها للغة في كثير من البرامج لأنه بناءً على الإجابات سيُتَّخذ قرار بتنفيذ كتلة شيفرة ما، كما أنّ عوامل الموازنة تتشابه في كثير من لغات البرمجة وتكون في بايثون كما يلي: العامل الاستخدام مثال == هل القيمة التي على يسار العامل تساوي القيمة التي على يمينه؟ x == y =! هل القيمة التي على يسار العامل لا تساوي القيمة التي على يمينه؟ x != y < هل القيمة التي على يسار العامل أكبر من القيمة التي على يمينه؟ x > y > هل القيمة التي على يسار العامل أصغر من القيمة التي على يمينه؟ x < y =< هل القيمة التي على يسار العامل أكبر أو تساوي القيمة التي على يمينه؟ x >= y => هل القيمة التي على يسار العامل أصغر أو تساوي القيمة التي على يمينه؟ x <= y وبناءً على السابق فإنه إذا وُجد متغيران يحملان قيمًا متساويةً، فسيكون المثال الأول في عامل التساوي صحيحًا True؛ أما إذا كان المتغيران يحملان قيمًا مختلفةً، فسيكون المثال خاطئًا False، وكذلك الحال في العوامل الأخرى، وقد تبدو تلك العوامل مبهمةً قليلًا، إلا أنها تملك استخدامات مهمةً، إذ سيتضح ذلك أثناء دراسة جزء اتخاذ القرار. x = 1; y = 1 print (x == y) >> True print (x != y) >> False المعوامل المنطقية Logical Operators تُستخدَم العوامل المنطقية لتركيب أسئلة منطقية أكثر تعقيدًا من خلال عوامل الموازنة السابق ذكرها مثلًا، فبافتراض وجود أربعة متغيرات وهي x و y و z و v، وإذا سألنا السؤال التالي: هل قيمة المتغير x تساوي قيمة المتغير y أو قيمة المتغير z تساوي قيمة المتغير v؟ يتكون السؤال المذكور من سؤالين فرعيين يفصل بينهما كلمة أو وهي عامل منطقي في البرمجة، وهنا تُعالَج الأسئلة الفرعية، ثم يُتخَذ القرار بناءً على ذلك العامل المنطقي. توجد ثلاثة عوامل منطقية هي AND و OR و NOT بصورة أساسية، وكل منها يتعامل مع الجمل بطرق مختلفة، فالعامل الأول والثاني يعالجان أسئلةً معقدةً تتكون من أكثر من سؤال فرعي؛ أما الثالث فيتعامل مع جملة محدَّدة بحيث ينفي نتيجتها المنطقية أي يُحوِّل الصح True إلى خطأ False. وفيما يلي استخدامات العوامل المنطقية: And: إذا كانت جميع الجمل أو الأسئلة الفرعية صحيحةً، فستنتُج إجابة صحيحة True، وإذا كانت أيّ من الأسئلة الفرعية خاطئةً، فسينتُج خطأ False. x = 1; y = 1 x == 1 and y == 1 # >> True or: إذا كانت أي من الجمل صحيحةً، فستنتُج إجابة صحيحة حتى إذا كانت باقي الجمل غير صحيحة x = 1; y =1 x == 2 or y == 1 # >> True Not: يعكس القيمة المنطقية الناتجة. x = 1 not x == 1 # >> False عوامل العضوية Membership Operators وهي إحدى العوامل المستخدَمة في الموازنة، أو بمعنى أبسط المستخدَمة في طرح أسئلة يُبنَى عليها اتخاذ قرار ما في البرنامج، ويوجد في بايثون عاملان فقط تحت ذلك التصنيف وهما عامل الوجود in وعامل عدم الوجود not in، إذ يستخدَم العامل الأول للتحقق من وجود قيمة ما بداخل سلسلة من القيم؛ أما الثاني فيستخدَم للتحقق من عدم وجود القيمة في سلسلة ما. تعني سلسلة القيم هنا القوائم والصفوف والمفاتيح الموجودة في القواميس، بمعنى أنه يمكننا التحقق من وجود قيمة معينة بداخل قائمة أو صف، أو من وجود قيمة معينة مثل المفاتيح الموجودة في قاموس، وفيما يلي ثلاثة أمثلة على التحقق من وجود قيمة باستخدام العامل الأول: x = [5, 6, 7] print (5 in x) >> True y = (5, 6, 7) print (5 in y) >> True z = {5: 'x', 6: 'y', 7: 'z'} print (5 in z) >> True أما العامل الثاني فيتحقق من عدم وجود القيمة في السلسلة، وفيما يلي الأمثلة السابقة نفسها ولكن باستخدام العامل الثاني: x = [5, 6, 7] print (5 not in x) >> False y = (5, 6, 7) print (5 not in y) >> False z = {5: 'x', 6: 'y', 7: 'z'} print (5 not in z) >> False عوامل الهوية Identity Operators وهي إحدى الأنواع المستخدَمة في الموازنة، إذ توازن بين شيئين للتحقق من تطابقهما تمامًا، ويوجد عاملان فقط تحت هذا التصنيف وهما عامل is وعامل نفيه is not، إذ يستخدَم العامل الأول للتحقق من تطابق كائنين؛ أما الثاني فيستخدَم للتحقق من عدم تطابق كائنين، وفيما يلي مثال على ذلك: x = 5 print (x is 5) >> True print (x is not 5) >> False عوامل الإسناد Assignment Operators يوجد عامل إسناد واحد بصورة أساسية في بايثون وفي معظم لغات البرمجة وهو =، إذ يُستخدَم لإسناد قيمة إلى متغير، فإذا كان لدينا متغير ما يدعى x وقيمته الرقم 1، فنستطيع كما يلي إجراء عملية جمع على المتغير ثم إسناد ناتج العملية إلى المتغير. x = 1 x = x + 1 print (x) >> 2 توجد بعض العوامل الأخرى التي تجري عمليةً ما على قيمة المتغير وتسند قيمةً جديدةً من ناتج تلك العملية، أي هي اختصار لعمليتين، بحيث تكون الأولى هي تنفيذ عملية رياضية على قيمة المتغير، وتكون الثانية إسناد نتيجة تلك العملية الرياضية على أساس قيمة جديدة للمتغير، لذلك فتوجد في بايثون وفي لغات البرمجة الأخرى عوامل إسناد تختصر تلك التعليمات كما يلي: x = 1 x += 1 print (x) >> 2 نذكر في الجدول التالي تلك العوامل مع استخداماتها: العامل الاستخدام = يسند القيمة الموجودة على يمينه كما هي إلى المتغير الموجود على يساره إذا كان المتغير موجودًا بالفعل، أو ينشئ المتغير في الذاكرة إذا لم يكن موجودًا ثم يسند إليه القيمة الموجودة على يمينه كما هي. =+ يضيف القيمة الموجودة على يمينه إلى قيمة المتغير الموجود على يساره. =- يطرح القيمة الموجودة على يمينه من قيمة المتغير الموجود على يساره ثم يسند الناتج إلى المتغير. =* يضرب قيمة المتغير الموجود على يساره بالقيمة الموجودة على يمينه ثم يسند الناتج إلى المتغير. =/ يقسِّم قيمة المتغير الموجود على يساره على القيمة الموجودة على يمينه ثم يسند الناتج إلى المتغير. =% يُوجِد باقي قسمة قيمة المتغير الموجود على يساره على القيمة الموجودة على يمينه ثم يسند الناتج إلى المتغير. =** يُجري حسابًا أسيًا على قيمة المتغير، إذ يكون الاْس هو القيمة الموجودة على يمينه، ثم يسند الناتج إلى المتغير. =// يُجري عملية قسمة صحيحة على قيمة المتغير من اليسار على القيمة الموجودة على يمينه، ثم يسند الناتج إلى المتغير. نترك لك تجربة تلك العوامل وبقية العوامل الأخرى أيضًا كتطبيق على ما تعلمته. اقرأ أيضًا المقال السابق: أساسيات البرمجة بلغة بايثون أنواع البيانات التعامل مع الصفوف، المجموعات والقواميس في بايثون فهم أنواع البيانات في بايثون 3
  9. سنبدأ في هذا المقال من سلسلة برمجة الذكاء الاصطناعي في تعلم أساسيات لغة بايثون، وهي من أهم لغات البرمجة على الإطلاق المستخدمة في مجال الذكاء الاصطناعي، ولكنها ليست لذلك فقط؛ إذ تُستعمَل لغة بايثون في كثير من المجالات الأخرى مثل برمجة المواقع وبرامج سطح المكتب وأنظمة التشغيل وغيرها. قبل البدء في أساسيات لغة بايثون: ما هي لغة البرمجة بايثون؟ تعني بايثون في اللغة الإنجليزية نوعًا من الثعابين الكبيرة، لكنها لم تُسمى لذلك بل سُمِّيَت بهذا الاسم تيمنًا ببرنامج ترفيهي قدَّمته قناة BBC يحمل اسم Monty Python’s Flying Circus، وذلك بعد ما شاهده مخترع اللغة. وتُعَدّ بايثون لغةً برمجيةً عامةً، أي تستطيع استخدامها في مجالات عدة على عكس بعض اللغات الأخرى التي تتخصص في مجال ما دونًا عن الآخر، كما تُعَدّ لغةً بسيطةً ومتطورةً للغاية، بالإضافة إلى أنها تدعم البرمجة كائنية التوجه Object Oriented Programming أو OOP اختصارًا. صُنِعت بايثون بواسطة الهولندي جايدو فان روسم Guido van Rossum، وأُصدِرت في شهر 11 من عام 1994م بعدما عُمِل عليها في معهد الأبحاث القومي للرياضيات وعلوم الحاسوب في هولندا، في حين نُشر الإصدار الثاني من بايثون في عام 2000م؛ أما الإصدار الثالث، فقد نُشِر في عام 2008م وهو الإصدار المستخدم حاليًا من بايثون لأن الإصدار الثاني السابق قد توقف دعمه وتطويره. خصائص لغة بايثون تُعَدّ لغة بايثون لغةً مفتوحة المصدر تجد شيفرتها المصدرية على موقع GitHub، وبالتالي يستطيع أيّ مبرمج المشاركة في تطوير اللغة. وبحسب موقع جيت هاب GitHub، فقد شارك أكثر من 1200 شخص حول العالم في تطويرها، كما تُعرَف بايثون بسهولة تراكيب الجمل فيها Syntax التي تشبه تركيب اللغة الإنجليزية بنسبة كبيرة، وهي لغة مهمة جدًا للطلبة والباحثين والمهنيين على حد سواء في الكثير من النطاقات العلمية والعملية. وتتميز بايثون بالميزات التالية: لغة برمجة مفسرة Interpreted تدعم البرمجة الكائنية مناسبة للمبتدئين مدعومة بالكثير من المكتبات لغة مجانية ومفتوحة المصدر تستخدم في العديد من التخصصات لغة برمجة مفسرة Interpreted أي أنها تُنفَّذ مباشرةً مثل لغة PHP ولغة Perl، ولا تحتاج إلى تصريف كامل الشيفرة أولًا ثم تنفيذها مثل لغة C، فلا يتطلب الأمر تحويل الشيفرة الخاصة بك إلى شيفرة ثنائية تفهمها الآلة أولًا لتبدأ عملية التنفيذ، وهذا قد يميز شيفرات بايثون في سرعة تنفيذها أثناء البرمجة. وفي تلك النقطة بالتحديد قد يختلف بعض المبرمجين، فالبرغم من تصنيف بايثون أنها لغة مفسَّرة، إلا أنّ الشيفرة تُصرَّف compiled أولًا ليستطيع المُفسِّر فهمها قبل تنفيذها، لذلك قد تجد بعض النقاشات المتباينة حول بايثون لمحاولة تصنيفها تصنيفًا دقيقًا. يذهب بعض المبرمجين إلى القول بأنّ لغة بايثون لها طريقتها الخاصة في تلك النقطة، فالأمر معقَّد ولا نريد الخوض فيه. تدعم البرمجة الكائنية تدعم بايثون نمطًا يُدعى الكائنية في البرمجة Object-oriented programming (أو تدعى الشيئية أحيانًا)، وهو نمط شهير ومهم، إذ تُكتَب الشيفرة ويُتعامَل مع أجزاء التطبيق على أساس الكائنات Objects، وهو نمط غير مدعوم في بعض اللغات القديمة مثل لغة C، كما أنها تدعم البرمجة الوظيفية Functional والهيكلية Structured وغيرها. أساسيات لغة بايثون مناسبة للمبتدئين تُعَد بايثون مناسبةً جدًا للمبتدئين، حيث أنَّ صياغة الجمل فيها بسيطة للغاية، ولا يتطلب الأمر الدخول في تفاصيل كثيرة عند كتابتها؛ لذلك فهي سهلة التعلم والقراءة. مدعومة بالكثير من المكتبات يمكنك في بايثون إيجاد مكتبات بسيطة الاستخدام تستطيع بها برمجة تطبيقات معقدة جدًا، فهي لغة لديها أرشيف واسع من المكتبات في كافة المجالات تقريبًا. لغة مجانية ومفتوحة المصدر توجد بعض لغات البرمجة غير المجانية، أي أنك مُطالَب بدفع الأموال لشركة ما، كي تستطيع استخدام تلك اللغة، كما أنَه لا يمكنك ولا يمكن لأيّ شخص آخر إصلاح مشكلة ما في اللغة أو أن يطوِّر ميزةً أو خاصيةً جديدةً في اللغة، فالشيفرة المصدرية في تلك الحالة تقع تحت أيدي الشركة المصنعة فقط، وهي الوحيدة التي يحق لها تطوير اللغة أو إصلاح مشاكلها أو إصدار نسخ جديدة منها. أما في بايثون والعديد من اللغات المجانية المفتوحة المصدر، فالأمر مختلف إذ عمِل على على لغة بايثون أكثر من 1200 شخص حول العالم، فالشيفرة المصدرية للغة موجودة ومتاحة على موقع GitHub، ويمكن لأيّ شخص له الخبرة والمعرفة الكافية أن يطوِّر أو يعدِّل ميزةً ما، كما يستطيع مجتمع مبرمِجي بايثون على الإنترنت من المشاركة بآرائهم في تطوير اللغة، فالأمر بالجميع وللجميع، وهي لغة مجانية بالكامل تستطيع استخدامها في أيّ مشروع خيري أو تجاري، بدون أية مشاكل قانونية على الإطلاق. تستخدم في العديد من التخصصات لا يقتصر الأمر أبدًا على برمجة الذكاء الاصطناعي وتعلّم الآلة، ولا يقتصر على برمجة مواقع الويب أيضًا، إذ تُعَدّ بايثون من أكثر لغات البرمجة انتشارًا وتوغلًا في العديد من المجالات في حياتنا اليومية، وفي التخصصات العلمية والأبحاث ومعامل ومختبرات الجامعات حول العالم، لذلك بتعلُُّمك للغة بايثون فإنّ الأمر لا يقتصر على فرصة عمل في مجال الذكاء الاصطناعي فحسب، وإنما تستطيع استخدام معرفتك وخبرتك في بايثون في مجالات أخرى تفتح عليك أبواب دخل إضافية. تُشتهر بايثون أيضًا في استخدامها في برمجة المواقع وتطبيقات سطح المكتب وبرمجة برامج تجارية عبر بايثون مثل أودوو Odoo الذي يُعَدّ أشهرها وله متخصصين وشركات تجارية تعتمد كليةً على استخدامه. كما أنَّ لغة بايثون كما أوردنا تدخل في الكثير من مجالات البحث العلمي، فهي من أكثر اللغات التي تحتوي على مكتبات تهدف إلى خدمة مجالات البحث العلمي والرياضيات من الذكاء الاصطناعي حتى التغير المناخي وتُعَد دراسة لغة بايثون أمرًا أساسيًا بالنسبة لمبرمجي الذكاء الاصطناعي، لذلك سنبدأ الآن في دراسة أساسيات اللغة حتى نكون على قدر من المعرفة المطلوبة لنبدأ في دراسة وتطبيق الخوارزميات الأساسية في تعلُّم الآلة. لذلك فإن السهل البدء بتعلم أساسيات بايثون والبدء في عالم البرمجة بسهولة. تثبيت لغة بايثون أول ما نبدأ به في تعلم أساسيات لغة Python ولكي تستطيع العمل بلغة بايثون، يجب عليك أولًا تثبيت البرنامج الذي يفهم اللغة ثم ينفذها، وذلك لكي يستطيع حاسوبك التعرف على الأوامر التي تكتبها لتعمل عليه بصورة صحيحة، لذلك من الضروري أن تكون أول خطوة نقوم بها هي تثبيت لغة بايثون على حاسوبك. يختلف أمر تثبيت اللغة باختلاف نظام التشغيل، فإذا كان حاسوبك مثلًا يعمل على نظام لينكس Linux، فعلى الأرجح أنّ حاسوبك مثبَّت عليه بالفعل لغة بايثون، وللتأكد من ذلك يمكنك فتح الطرفية Terminal ثم كتابة الأمر الآتي: >> python --version أول الأمر التالي بالنسبة للإصدار الثالث: >> python3 --version إذا كانت بايثون مثبتةً بالفعل على حاسوبك، فسيظهر لك رقم الإصدار المثبَّت، والجدير بالذكر أنه يجب أن يكون الإصدار المثبت لديك هو الإصدار الثالث، وبالتالي يجب بدء رقم الإصدار بالرقم 3؛ أما إذا لم تكن اللغة مثبتةً على حاسوبك، فيمكنك ذلك عبر تنفيذ الأمر الآتي في الطرفية Terminal على لينكس. >> sudo apt-get install python3.6 أما إذا كنت مستخدِمًا لنظام التشغيل ماك macOS بمختلف إصداراته، فعلى الأغلب أيضًا أنّ لغة بايثون مثبتة بالفعل على حاسوبك، وتستطيع اختبار ذلك عبر الأمر السابق ذكره بخصوص نظام لينكس، فإذا لم تكن اللغة مثبتةً، فيمكنك ببساطة تثبيتها مثل أيّ برنامج أخر عن طريق الموقع الرسمي للغة /Python. بعد تثبيت اللغة بالطرق الموضحة أعلاه في نظامي لينكس وماكينتوش، فمن المحتمل ألا يعمل أمر التحقق من الإصدار وألا يكون جاهزًا للعمل بعد، إذ أنه قد لا يُتعرَّف على برنامج بايثون عندما تُنفِّذ الأمر التالي: >> python --version ولحل ذلك يجب تنفيذ الأمر التالي في الطرفية Terminal: >> export PYTHONPATH=/usr/local/bin/python3.6 مع تغيير كلمة python3.6 لأنها قد تختلف حسب الإصدار الذي ثبَّته؛ لذلك يجب التحقق من ذلك المسار على حاسوبك أولًا لترى أيّ الإصدارات يجب استدعاؤها في الأمر السابق. أما في حالة مستخدمي نظام الويندوز، فالأمر بسيط للغاية، إذ تستطيع تنزيل برنامج اللغة من الموقع الرسمي السالف ذكره، ثم تثبيته مثل أيّ برنامج آخر على حاسوبك دون تعقيدات قد لا يعمل كذلك أمر التحقق من إصدار اللغة بصورة تلقائية بعد التثبيت، ولحل ذلك ببساطة يمكنك فتح موجِّه الأوامر Command Prompt في ويندوز ثم تنفيذ الأمر الآتي: >> %path%;C:\Python مع الأخذ في الحسبان إمكانية تغيير النص C:\Python إذا كنت قد تثبَّت اللغة في مسار آخر على حاسوبك أثناء عملية التثبيت. استعمال بايثون مع خدمة Google Colab ضمن أساسيات لغة بايثون وبالرغم من سهولة عملية تثبيت بايثون على حاسوبك، فإنه ليس من الضروري فعلًا فعل تلك الخطوات السالف ذكرها، فقد أصدرت شركة جوجل مؤخرًا ما يُدعى Google Colaboratory عبر موقع الأبحاث الخاص بها \colab.research، وبالتالي تستطيع ببساطة استخدام تلك الخاصية بإنشاء ذلك النوع من الملفات على خدمة Google Drive الموجودة مجانيًا لأي عنوان بريد إلكتروني مُسجَّل على Gmail، بعدها يمكنك البدء في كتابة وتنفيذ شيفرة البايثون الخاصة بك عبر الإنترنت دون الحاجة إلى الدخول في الكثير من التعقيدات والمشاكل التقنية أثناء تعلمك، أو حتى أثناء عملك في برمجة الذكاء الاصطناعي. كما تحتوى تلك الخدمة تلقائيًا على معظم وأهم مكتبات بايثون المستخدَمة في مجال الذكاء الاصطناعي عامةً ومجال تعلّم الآلة خاصةً، فنجد مثلًا تلقائيًا في تلك الخدمة أنّ مكتبات متخصصة في الرياضيات مثل Numpy، ومكتبات متخصصة في رسم البيانات مثل Matplotlib ومكتبات متخصصة في خوارزميات تعلّم الآلة مثل Keras، ومكتبات متخصصة في التعلّم العميق والشبكات العصبية مثل Tensorflow …إلخ مثبتة ومتاحة للاستخدام مباشرةً. أُصدِرت الخدمة أساسًا للتسهيل على العاملين في مجال برمجة الذكاء الاصطناعي، وبالأخص تعلُّم الآلة ليستطيع المبرمج مشاركة الشيفرة المصدرية الخاصة به مع نتائج هذه الشيفرة والملاحظات مع شركائه في العمل أو أي شخص آخر، وهي خدمة سحابية بالكامل، أي أنها تعمل عبر الإنترنت ولا تحتاج إلى أي متطلبات أو إمكانيات في حاسوبك، فكل ما تحتاجه لاستخدام الخدمة هي وصلة الإنترنت وعنوان بريد إلكتروني من Gmail. أفضِّل شخصيًا استخدام تلك الخدمة أثناء التعلم لأنها بسيطة وسهلة، وتحتوي تلقائيًا على الكثير من مكتبات بايثون الخاصة بالذكاء الاصطناعي التي قد يكون تثبيت بعضها عملًا شاقًا إذا حدث خطأ ما أثناء التثبيت والسبب الآخر الذي يدفعني إلى التوصية باستخدام تلك الخدمة بشدة، هو عدم امتلاك بعض أجهزة الحاسوب للإمكانيات اللازمة لتشغيل نماذج تعلّم الآلة، إذ تحتاج بعض الخوارزميات إلى ذاكرة عشوائية RAM كبيرة ليُدرَّب النموذج تدريبًا صحيحًا، وذلك اعتمادًا على حجم البيانات المتدفقة إلى النموذج. ولمزيد من التفاصيل، ارجع إلى مقال دليل استخدام Google Colab. برنامجك الأول في لغة بايثون من أساسيات لغة بايثون أنك تستطيع البدء في كتابة برنامجك الأول بعدة من الطرق، أولها عبر كتابة الشيفرة مباشرةً في الطرفية Terminal في لينكس وماك وموجِّه الأوامر Command Prompt في ويندوز؛ وثانيها، كتابة البرنامج في ملف أو عدة ملفات منفصلة، ثم تشغيلها عبر البرامج السابق ذكرها؛ أما ثالثها فتكون عن طريق خدمة Google Colab التي ذكرناها سابقًا. أما كتابة الشيفرة مباشرة في الطرفية وموجه الأوامر، فالخطوات لذلك بسيطة، وكل ما عليك فعله بعد فتح أحد تلك البرامج حسب نظام التشغيل الخاص بحاسوبك هو تنفيذ الأمر الآتي: >> python أو الأمر التالي بالنسبة للإصدار 3 من بايثون: >> python3 تستطيع بعد ذلك كتابة أوامر بايثون مباشرةً وتنفيذها، كما يمكنك الخروج من هذه الشاشة بعد ذلك عبر كتابة الأمر الآتي: >> quit() وأما كتابة الشيفرة في ملف منفصل، فتستطيع إنشاء ملف جديد في أيّ مكان في حاسوبك وتسميته بأيّ اسم تريده، كما يفضَّل أن يكون الاسم معبِّرًا، فالتسمية البسيطة والمعبِّرة من الأمور المهمة. احفظ بعد ذلك الملف بصيغة py -وهي صيغة اختصارية لكلمة Python-، فإذا كان مثلًا اسم الملف Program، فستكون التسمية الكاملة للملف Program.py، ثم اذهب إلى المكان المخزن فيه الملف غبر الطرفية أو موجِّه الأوامر، وبعدها نفِّذ الأمر التالي: >> python Program.py والطريقة الثالثة، تستطيع ببساطة استخدام Google Colab من خدمة Google Drive ومن ثم إنشاء ملف جديد من نوع Google Colaboratory، ثم البدء في إضافة الفقرات، فيمكنك إضافة شيفرات بايثون في ذلك الملف بجانب نصوص عادية وبذلك تستطيع كتابة ملاحظات وتعليقات بين أسطر الشيفرات ويمكنك في الوقت نفسه تنفيذ الشيفرات ككل أو أجزاء محددة. سنبدأ بأمر الطباعة لكتابة أول برنامج بايثون خاص بك، فوظيفة أمر الطباعة في بايثون وفي كثير من اللغات الأخرى، هي طباعة نص على شاشة المستخدِم عند تشغيل البرنامج، وقد ذكرنا ذلك الأمر سابقًا عند الحديث عن الخوارزميات في البرمجة، فعند كتابة الأمر print والذي يعني اطبع بالإنجليزية، فيجب إلحاقه بقوسين، ومن ثم فتح علامات تنصيص بداخل القوسين تحتوى على الجملة التي نريد طباعتها. print ("Hello World") قد يُستخدَم الأمر print لطباعة النصوص على الشاشة أو لطباعة الأرقام، فالأمر سيان في حالة الطباعة، ولكن لا يجب إضافة علامات تنصيص في حالة طباعة الأعداد. print (3) >> 3 كما أنه من الممكن طباعة نتيجة عملية رياضية بسيطة مثل عمليات الجمع والطرح كما في الأمثلة الآتية: print (3+3) >> 6 print (6-4) >> 2 يمكن أيضًا طباعة عمليات الضرب والقسمة وباقي القسمة، إذ يمكنك عبر كتابة الشيفرة التالية طباعة حاصل ضرب عددين مثلًا: print (6*6) >> 36 والأمر بسيط كذلك لطباعة حاصل قسمة عددين: print (49/7) >> 7.0 أما حالة طباعة باقي القسمة، فسنستخدِم رمز النسبة المئوية %: print (50%7) >> 1 كما يمكنك طباعة أيّ عدد من النصوص باستخدام علامة الزائد + كما في المثال الآتي: print ("Hello " + "World!") >> Hello World! إذا أردنا تضمين قيمة عددية داخل نص، فيجب فيجب تحويل العدد إلى نص لأنّ الأعداد في بايثون وفي عدد من اللغات الأخرى عمومًا هي نوع من أنواع البيانات، لذلك يجب استخدام دالة str في هذه الحالة لتحويل العدد إلى نص لتستطيع لغة بايثون التعامل معها على أساس نص. print ("Hello "+str(2)+"nd "+"World!") >> Hello 2nd World! نستطيع الاستغناء عن دالة str إذا أدرجنا الأعداد داخل علامات التنصيص، إذ ستَعُدّ بايثون الأعداد داخل علامات التنصيص نصًا عاديًا، كما سنتعرف على الدوال بصورة أكبر في الصفحات القادمة. print ("Hello " + "2nd " + "World!") >> Hello 2nd World! المعرفات في بايثون تعد المعرِّفات Identifiers من أساسيات البايثون وهي الكلمات التي تستطيع من خلالها تعريف اسم متغير Variable أو ثابت Constant أو دالة Function أو صنف Class أو وحدة Module، ونختصر ذلك كله الآن بكلمة المعرِّفات فقط للدلالة على أيّ منهم، فتلك المتغيرات والثوابت والدوال والأصناف والوحدات، كلها مواضيع وسنتعرف عليها بالتفصيل في هذا الكتاب. تخضع تسمية المعرفات إلى قواعد محدَّدة لا يُمكن الخروج عنها في بايثون؛ لأنه عند الخروج عن إحدى القواعد سيطبع مفسر بايثون رسالة خطأ عند محاولتك تشغيل البرنامج، وتلك القواعد كما يلي: يجب أن تبدأ بحرف إنجليزي أو شَرطة سفلية Underscore _ ولا يُمكنها البدء برقم أبدًا. لا يمكنها احتواء رموز مثل % أو $ أو & …إلخ، ولكن يمكنها احتواء الأرقام. ألا تكون مطابقةً لأيّ كلمة من الكلمات المفتاحية في بايثون. الكلمات المفتاحية في بايثون هي كلمات تُستخدَم في أصل اللغة، بمعنى أنّ تلك الكلمات يقرؤها مفسر لغة بايثون لإجراء مهمة ما، فكلمة مثل print كما علمنا من قبل تؤدي مهمة طباعة نص أو عدد على الشاشة، لذلك لا نستطيع تعريف متغير أو ثابت أو دالة بهذا الاسم، وتكون جميع الكلمات المفتاحية في لغة بايثون كما يلي: 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; } and exec not as finally or assert for Pass break from print class global raise continue if return def import try del in while elif is with else lambda yield except والجدير بالذكر أنّ جميع تلك الكلمات المفتاحية لا تحتوي على أي حرف كبير، وبما أنّ لغة بايثون لغة حساسة لحالة الأحرف، فمن تسمية أيّ معرِّف بتلك الكلمات في حالة تغيير حالة الأحرف، بمعنى أنه لا يُمكنك تسمية متغير باسم print، لكن يمكنك تسمية متغير باسم Print أو PRINT لأنه بالنسبة لبايثون، تكون الكلمات الثلاثة السابقة مختلفةً تمامًا عن بعضها، وبناءً على ذلك، فإذا أردت تسمية متغير أو دالة أو صنف في بايثون، فيجب عليك اتباع القواعد السابقة. كما أنّ هناك أيضًا في بايثون بعض التوصيات أثناء التسمية، وهي ليست قواعدًا يجب اتباعها بل هي أقرب إلى العُرف، ولكن من الأفضل اتباعها حتى تكون الشيفرة المصدرية في أفضل صورة ممكنة، وتلك التوصيات هي كما يلي: اسم الصنف من المفضل أن يبدأ بحرف كبير مثل Mouse وليس mouse. جميع المعرِّفات الأخرى مثل المتغيرات والدوال من الأفضل أن تبدأ بحرف صغير. إذا عرَّفت متغيرًا ما خاصًا، ولا تريد أن يُستخدَم في أيّ مكان آخر في البرنامج، فمن المفضل بدء اسم المتغير بشرطة سفلية واحدة أو شرطتين إذا كان المتغير خاصًا جدًا. السطور والمسافات لدى بايثون نوعًا فريدًا من القواعد عندما يتعلق الأمر بالأسطر والمسافات وتنظيم الشيفرة، إذ تستخدِم معظم لغات البرمجة الأخرى الأقواس المعقوصة { } لكتابة كتلة من الشيفرات، لكن الأمر في بايثون مختلف قليلًا، إذ تُنظَّم كتل الشيفرات باستخدام المسافات، وهي عادةً مسافة جدولة Tab، أو فراغين أو أربعة فراغات spaces، فنكتب كتلةً من الشيفرات لتُنفَّذ عند تحقق شرط معين بالشكل التالي: if something_happens: do_this() else: do_that() نقول للمفسر في المثال السابق الوهمي إذا حدث هذا الأمر، افعل هذا؛ وإذ لم يحدث، افعل ذاك، فالأمر بسيط للغاية، المهم دقق بالمسافات وكيف أن do_this تدخل ضمن الشرط if أما do_that فتدخل ضمن else. وبالمثل، عند كتابة كتلة من الشيفرة داخل أيّ قاعدة شرطية أو غيرها من القواعد، يجب عليك البدء في كتابة أسطر الشيفرة الخاصة بالكتلة بعد مسافة تفصل بينها وبين أول السطر، أو بمعنى أدق تفصل بينها وبين كتلة الشيفرة التي تسبقها، انظر مثلًا: count = 10 if count >= 10: if count <= 20: print ("Count is between 10 and 20") else: print ("Count is larger than 20") else: print ("Count is less than 10") وإليك المثال التالي لموازنة طريقة بايثون تلك مع اللغات الأخرى مثل لغة جافاسكربت: let count = 10; if (count <= 20) { if (count <= 20) { console.log("Count is between 10 and 20"); } else { console.log("Count is larger than 20"); } } else { console.log("Count is less than 10"); } تضمَّن كتل الشيفرة في هذا المثال بداخل القواعد الشرطية داخل أقواس معقوصة بغض النظر عن المسافة بين كل سطر وبدايته، المثال السابق يكافئ: let count = 10; if (count <= 20) { if (count <= 20) { console.log("Count is between 10 and 20"); } else { console.log("Count is larger than 20"); } } else { console.log("Count is less than 10"); } المثالان السابقان متطابقان تمامًا ويعملان بلا مشكلة؛ أما في بايثون، فتخضع الشيفرة إلى قاعدة مسافات الأسطر تلك، ولا يُمكن كسر تلك القاعدة، كما أنّ كافة مبرمجي بايثون يحبون قاعدة المسافات لأنها تُجبِر المبرمجين على كتابة شيفرة بسيطة ومنظمة وسهلة القراءة. ومن أساسيات البايثون وأهم القواعد الخاصة بالأسطر في بايثون، نجد قاعدة استكمال الأسطر، فإذا كنت تكتب سطرًا ما وكان هذا السطر طويلًا للغاية وتريد تقسيمه إلى سطرين، فستستطيع في بايثون تحقيق ذلك عبر استخدام الرمز \، فإذا كتبت سطرًا يجمع رقمَين مثلًا ثم يقسم أحدهما على الآخر، وكنت تريد تجزئة هذا السطر إلى سطرين مختلفين، فيمكنك فعل ذلك كما في المثال التالي: 30 + 6 / 6 >> 31.0 30 + 6 \ / 6 >> 31.0 كتبنا في السطر الأول عملية الجمع مع عملية القسمة في سطر واحد مباشرةً وأنتج ذلك العدد 31، أما في المثال الثاني كتبنا عملية الجمع في السطر الأول فقط، ثم ألحقنا عملية الجمع بالرمز \، واستكملنا السطر التالي العمليات بكتابة عملية القسمة مباشرةً، وأنتج ذلك في النهاية العدد نفسه، لكن توجد مع ذلك حالة خاصة لأيّ سطر في بايثون يحتوي على إحدى الأقواس بمختلف أنواعها، مثل [] أو {} أو ()، إذ يمكن كتابة تلك الأسطر على سطرين أو أكثر دون استخدام الرمز السابق ذكره، فالسطر التالي مثلًا يعمل بدون مشكلة حتى مع عدم استخدامنا للرمز. items = ['item_1', 'item_2', 'item_3', 'item_4', 'item_5', 'item_6'] علامات التنصيص توجد اختلافات بسيطة بين علامات التنصيص في لغة بايثون مثل أي لغة برمجة أخرى، إذ يمكنك في بايثون تضمين أيّ نص بين علامتَي تنصيص مُفرَدة ' أو علامتي تنصيص مزدوجة " أو علامتي تنصيص ثلاثية """ أو ''' لكن من أهم قواعد استخدام علامات التنصيص في بايثون هي إنهاء النص بعلامة التنصيص نفسها المُستخدَمة في البداية، فلا يُمكن استخدام علامة تنصيص مُفرَدة في بداية النص ثم استخدام علامة تنصيص مزدوجة في نهايته، وإنما يجب تطابق العلامة في البداية والنهاية للنص الواحد، وإليك أمثلةً على ذلك في أوامر الطباعة: print ("Hello World!") print ('Hello World!') print ("""Hello World!""") أما بالنسبة لعلامات التنصيص الثلاثية، فتُستخدَم في الغالب لامتلاكها ميزةً غير موجودة في العلامات المفرَدة والمزدوجة، إذ تُعَدّ قادرةً على معالجة النصوص التي تتكون من أكثر من سطر، في حين أنّ علامات التنصيص المُفردة والمزدوجة يجب احتوائها على نص مكوَّن من سطر واحد لا أكثر، فلا يمكنك مثلًا طباعة سطرين متتالين عبر علامات التنصيص المزدوجة، أي لا يمكنك تنفيذ المثال التالي في بايثون بصورة صحيحة: print ("Hello World!") لكن يمكنك تنفيذ ذلك الأمر عند استخدام علامات التنصيص الثلاثية كما يلي: print ("""Hello World!""") >> Hello World! التعليقات في أساسيات لغة بايثون ستحتاج في كثير من الأوقات أثناء كتابتك أو عملك على برنامج ما، إلى كتابة بعض الملاحظات على بعض الأسطر، فقد تكون تلك الملاحظات موجهةً لتذكيرك بكيفية عمل هذه الكتلة من الشيفرة، أو لتذكيرك بأمر ما تريد استكماله في هذه الأسطر في وقت لاحق، وتوفر بايثون مثلها مثل بقية لغات البرمجة إمكانية كتابة التعليقات عبر استعمال الرمز # والذي سيؤدي إلى تجاهل ما يليه حتى آخر السطر، انظر مثلًا الشيفرة التالية: # هذا تعليق print ("Hello, Python!") # تعليق آخر >> Hello, Python! كما ترى، فإنّ المثال السابق يعمل عملًا عاديًا وكأنه مكتوب بالشكل التالي دون تعليقات: print ("Hello, Python!") >> Hello, Python! فإذا كتبت أيّ سطر برمجي بعد رمز # فسيتجاهله مفسر بايثون تمامًا كما لو أنه غير موجود؛ وذلك لأن التعليقات هي في الأساس جمل من اللغة الطبيعية البشرية التي يستخدمها المبرمج للتعليق وكتابة الملاحظات بين تعليمات البرنامج. كما أنه في بايثون توجد طريقة أخرى لكتابة التعليقات في أسطر عدة، وذلك باستخدام علامات التنصيص الثلاثية، ومن مميزات هذه الطريقة أنها قد تُستخدم في توثيق الدوال وغير ذلك، كما أن هنالك أدوات تستخلص تلك التعليقات لتوليد توثيق لشيفرة البرنامج ووظائفه. """This is a multi line comment. It's wonderful how easy is Python language!""" print ("Hello World!") تُستخدَم التعليقات استخدامًا كبيرًا أثناء برمجة التطبيقات المعقَّدة، فغير أنها تُستخدَم للتعليق على الشيفرة، فإنها قد تكون مفيدةً بصورة كبيرة في تعطيل وتفعيل بعض الأسطر في الشيفرة أثناء عملك على حل مشكلة ما، مثل نسخ سطر ما ووضعه بداخل التعليق حتى يتجاهله البرنامج، ولكي أحتفظ بذلك السطر لاستخدامه لاحقًا. تعليمات متعددة في سطر واحد تنتهي التعليمة البرمجية في السطر في معظم لغات البرمجة مثل لغة PHP أو جافاسكربت JavaScript عن طريق كتابة رمز الفاصلة المنقوطة ; مما يسمح بكتابة أكثر من تعليمة برمجية في سطر واحد؛ أما في بايثون، فينتهي السطر عن طريق بداية سطر جديد، ولكن مع ذلك تدعم بايثون تلك الميزة أيضًا، إذ يمكنك إنهاء التعليمة البرمجية عبر الرمز ; وهو بالإنجليزية Semicolon، وهو أمر اختياري وليس إجباريًا كما في اللغات الأخرى، وبذلك تستطيع كتابة أكثر من تعليمة برمجية في سطر واحد مثل اللغات الأخرى. print ("Hello"); print("World") >> Hello >> World اقرأ أيضًا المقال السابق: البرمجة والخوارزميات والذكاء الاصطناعي المرجع الشامل إلى تعلم لغة بايثون النسخة العربية الكاملة من كتاب البرمجة بلغة بايثون
  10. تعرفنا في المقال السابق على مفهوم الذكاء الاصطناعي وناقشنا فكرة خطر الذكاء الاصطناعي ونكمل في هذه السلسلة برمجة الذكاء الاصطناعي الحديث عن البرمجة والتعرف عليها وعلى الخوارزميات المرتبطة بها ونبحث علاقتهما بالذكاء الاصطناعي. ما هي البرمجة؟ إذا كنت تتوقع زيارة صديق لك، فقد تكتب له عنوانك بالكامل، وإذا كان العنوان صعب الوصول، فمن الممكن أن تكتب العنوان له على صورة مجموعة من التعليمات للوصول إلى منزلك مثل: عندما تصل إلى ناصية الشارع، اتجه إلى اليمين امش عشر خطوات، ثم اتجه إلى اليمين مرةً أخرى اُدخل أول منزل من جهة اليسار اصعد إلى الدور الثاني فالبرمجة هي الشيء نفسه تمامًا مع الحاسوب، فبدلًا من الخطوات السابقة، تُكتب تعبيرات مشابهة تخاطب الحاسوب بلغة برمجة لتنفيذ مهمة مشابهة بدلًا من صديقك السابق. تُعَدّ لغات البرمجة مجموعةً من المفردات والقواعد اللغوية التي تمثِّل لغةً وسيطةً للتخاطب مع الحاسوب وأمره بتنفيذ تعليمات وأمور محدَّدة، فلا الحاسوب يفهم لغة البشر، ولا البشر يفهمون لغة الحاسوب، إذ لا تخرج لغة الحاسوب في الأصل خارج حدود الصفر والواحد، وهي الشحنة الكهربائية الموجبة والشحنة الكهربائية السالبة، لكن لا أحد يستطيع التعامل مباشرةً مع تلك اللغة؛ لذا كانت هنالك حاجة ملحة لوجود لغة وسيطة يفهمها كلا البشر والآلة؛ ونتيجةً لذلك فقد اُخترعت لغات البرمجة، وهي نوعان منخفضة المستوى وعالية المستوى. إذا أردنا أمر الحاسوب بالأمر "افعل كذا" بعبارة أخرى، فسنحتاج إلى لغة مشتركة بيننا وبينه ليفهم ما نبتغيه، وهنا يأتي دور لغات البرمجة، إذ يمكنك عَدّ لغات البرمجة بأنها الوسيط بين المبرمج والحاسوب، فوحدة المعالجة المركزية للحاسوب CPU غير قادرة على التحدث بأية لغة في الأصل غير لغتها لغة الآلة، وفيما يلي نموذج من تعليمة تعريف متغير يحوي القيمة 97 في لغة الآلة: 10110000 01100001 كان على المبرمجين كتابة برامج مباشرة بلغة الآلة عندما اختُرِعت أجهزة الحاسوب لأول مرة، وهو أمر صعب للغاية ويستغرق وقتًا طويلًا، فكيفية تنظيم هذه التعليمات هو أمر خارج نطاق هذه السلسلة، ولكن من المثير للاهتمام ملاحظة أمرين، أولًا هو أن تكون لغة الآلة كلها تسلسلًا من 1 و0 فقط، إذ يُطلق على كل رقم 0 أو 1 برقم ثنائي binary digit أو اختصارًا بت Bit، كما يختلف عدد وحدات البت Bit أو الأرقام الثنائية التي تكوِّن أمرًا واحدًا، فبعض وحدات المعالجة المركزية CPU مثلًا تعالِج تعليمات يبلغ طولها دائمًا ‎32 ‎‎‎Bit‎، في حين تحتوي بعض وحدات المعالجة المركزية الأخرى على تعليمات يمكن أن يكون طولها متغيرًا؛ أما ثانيًا، فتُفسَّر كل مجموعة من الوحدات من وحدة المعالجة المركزية للحاسوب لأداء أمر دقيق جدًا فقط مثل موازنة عددين، أو تخزين عدد أو نص في الذاكرة …إلخ. الجدير بالذكر أنّ كل شيفرة تنفيذية أو ثنائية -نسبة إلى الأرقام الثنائية- تعمل على وحدة معالجة مركزية ما ولا تعمل على غيره إلا إن كانت المعالجات تشترك بتعليمات التنفيذ نفسها منخفضة المستوى، ما يعني أنه ليعمل برنامجك المكتوب بلغة الآلة على حاسوب معالجه مختلفة، من المرجح أن يتطلب الأمر مجهودًا مضنيًا، وإعادة كتابة أجزاء كبيرة من الشيفرة لملاءمة حزمة التعليمات التي تفهمها وحدة المعالجة المركزية للحاسوب الآخر. ونظرًا لصعوبة قراءة لغة الآلة وفهمها لأنها مكتوبة بأرقام ثنائية فقط أصفار وواحدات، فقد اختُرِِعت لغة التجميع Assembly Language، بحيث تُحدَّد كل تعليمة باختصار قصير بدلًا من مجموعة من الأرقام الثنائية، كما يمكن استخدام الأسماء والأرقام الأخرى، وإليك التعليمة السابقة المذكورة أعلاه بلغة الآلة مكتوبة بلغة التجميع: mov al, 061h يجعل هذا من لغة التجميع أسهل بكثير للقراءة والكتابة من لغة الآلة، ومع ذلك لا يمكن لوحدة المعالجة المركزية فهم لغة التجميع مباشرةً، حيث يجب ترجمتها إلى لغة الآلة بدلًا من ذلك وقبل تمكِّن الحاسوب من تنفيذه، وذلك باستخدام برنامج يسمى المُجمِّع Assembler، فالبرامج المكتوبة بلغات التجميع تكون سريعةً للغاية غالبًا، ولا تزال لغة التجميع مستخدَمةً إلى اليوم عندما تكون السرعة مطلوبةً ومهمةً في برمجة بعض البرامج. تملك لغة التجميع مع ذلك بعض السلبيات، أولًا: لا تزال لغة التجميع تتطلب الكثير من التعليمات للقيام بمهام بسيطة، في حين أن التعليمات الفردية نفسها قابلة للقراءة إلى حد ما، إلا أنّ فهم ما يفعله البرنامج بأكمله يمكن أن يمثِّل تحديًا يشبه إلى حد ما محاولة فهم جملة من خلال النظر إلى كل حرف على حدة؛ أما ثانيًا، فلغة التجميع لا تزال غير متكيّفة، فمن المحتمل ألا يعمل برنامج مكتوب بلغة التجميع لوحدة معالجة مركزية معينة على وحدات معالجة مركزية أخرى، إذ يجب إعادة كتابة البرنامج أو تعديله على نطاق واسع. طُوِّرت لغات برمجة جديدة مثل C ولغة C++‎ ولغة PascalK ولاحقًا لغات مثل جافا Java وجافاسكربت Javascript وبيرل Perl وبايثون Python لمعالجة مشكلات قابلية القراءة وإمكانية النقل والتنفيذ على وحدات معالجة مركزية مختلفة، حيث تسمى هذه اللغات بلغات عالية المستوى، وذلك لأنها مصممة للسماح للمبرمج بكتابة البرامج دون الحاجة إلى القلق بشأن نوع الحاسوب الذي سيُشغَّل البرنامج عليه، فتُكتب التعليمة السابقة بلغة C ومثلها في لغة بايثون: a = 97 يجب تحويل البرامج المكتوبة بلغات عالية المستوى إلى لغة يمكن للحاسوب فهمها قبل تشغيل البرنامج مثل البرامج المكتوبة بلغة التجميع والآلة، وهناك طريقتان أساسيتان للقيام بذلك هما التصريف Compiling والتفسير Interpreting، فالمصرِّف Compiler هو برنامج يقرأ الشيفرة المصدرية وينتج برنامجًا منفصلًا قابلًا للتنفيذ يمكن تشغيله بعد ذلك بمجرد تحويل الشيفرة الخاصة بك إلى ملف قابل للتنفيذ، إذ لا تحتاج إلى المترجِم لتشغيل البرنامج، كما كانت برامج المصرِّفات Compilers رديئةً في البداية وتنتج برامجًا بطيئةً للغاية، ولكن على مر السنين أصبحت برامج المترجِمات سريعةً جدًا. تستطيع التعرُّف في الصورة السابقة على خطوات إنتاج وتشغيل البرامج المُبرمَجة بلغات عالية المستوى والتي تستخدِم هذا النظام؛ أما المفسِّر Interpreter فهو برنامج ينفِّذ التعليمات الموجودة في الشيفرة البرمجية مباشرةً دون الحاجة إلى تجميعها في ملف تنفيذي أولًا، فبرامج التفسير غالبًا أكثر مرونةً من برامج المصرِّفات لكنها أقل كفاءة عند تشغيل البرامج، وذلك لأن عملية التفسير تحتاج إلى تنفيذ في كل مرة يُشغَّل فيها البرنامج، وهذا يعني أنّ المفسِّر مطلوب في كل مرة يُشغَّل فيها البرنامج. يوضِّح الشكل السابق خطوات إنتاج وتشغيل البرامج المُبرمَجة بلغات عالية المستوى التي تستخدِم نظام التحويل للغات منخفضة المستوى. علاقة البرمجة بالذكاء الاصطناعي وتعلم الآلة لا تزيد نماذج الذكاء الاصطناعي وتعلّم الآلة عن كونها خوارزميات رياضية تستطيع كتابتها والتعبير عنها على الورق، لكن عندما يتعلق الأمر بتجربة تلك الخوارزميات على أرض الواقع، فإنّ البرمجة هي الوسيلة لتطبيق تلك الخوارزميات، فالمبرمج عادةً هو شخص مهمته صناعة برامج تحل مشكلةً ما بكتابة شيفرة تتكون من عدة خوارزميات، وقد لا يتطلب الأمر خوارزميةً ذكيةً في أغلب الأحيان، ولكن قد يتطلب العمل في بعض البرامج تنفيذ خوارزمية ذكية للتوقع كما يتوقع الإنسان مثلًا أو لأي مهمة أخرى تتطلب ذكاءً بشريًا، إذ يحتاج المبرمج في تلك الحالة إلى خوارزمية ذكاء اصطناعي للتوقُّع مثل الإنسان ليستطيع ترجمة رموزها الرياضية إلى شيفرة برمجية. الجدير بالذكر أنّ الكثير من الشركات الناشئة التي تدَّعي أنها مدعومة ببرامج وخوارزميات ذكاء اصطناعي مبهرة ليست كذلك بالفعل، ولكنها فقط تدعي ذلك للحصول على التمويل ولأغراض تسويقية كما بيَّنا من قبل عن شركة Hanson Robotics مثلًا، وذلك بناءً على دراسة أجرتها شركة MMC Ventures، فغالبًا ما تكون خوارزميات الذكاء الاصطناعي مجموعةً من الخطوات، وكل خطوة فيها مجموعة من المعادلات الرياضية على بعض المدخلات التي تأتي من الحاسوب أو من البرنامج، وتختلف المعادلات كثيرًا في فحواها وهدفها، وبالتالي تُعَدّ البرمجة حلقة الوصل بين باحثي الذكاء الاصطناعي وبين تطبيق واختبار خوارزمياتهم ومعادلاتهم الرياضية على أرض الواقع. الخوارزميات في علوم الحاسوب والبرمجة الخوارزميات كما من اسمها مشتقة من اسم العالم المسلم أبو جعفر محمد بن موسى الخوارزمي، وهو من مدينة خوارزم المشتقة منها كنيته، وخوارزم حاليًا مكان مقسم بين ثلاث دول وهي: أوزباكستان، وكازاخستان، وتركمانستان. يعتبر من أوائل علماء الرياضيات المسلمين، حيث ساهمت أعماله بدور كبير في تقدم الرياضيات في عصره. اتصل بالخليفة العباسي المأمون، وعمل في بيت الحكمة في بغداد، وكسب ثقة الخليفة إذ ولاه المأمون بيت الحكمة، كما عهد إليه برسم خارطة للأرض عمل فيها أكثر من سبعين جغرافيًا. قبل وفاته في عام 850م/232هـ المُقدَّر كان الخوارزمي قد ترك العديد من المؤلفات في علوم الرياضيات والفلك والجغرافيا، ومن أهمها: كتاب المختصر في حساب الجبر، والمقابلة الذي يعد أهم كتبه. تُرجمت كتبه بعد ذلك من قبل علماء أوروبا، وتم ترجمة بعض المصطلحات التي كانت شبيهة بشكل كبير للعربية، مثل كلمة الجبر التي تُرجمت إلى Algebra. يُعَدّ البرنامج الحاسوبي سلسلةً من التعليمات المكتوبة باستخدام لغة البرمجة لأداء مهمة محدَّدة بوساطة الحاسوب، ويوجد مصطلحان مهمان يجب إيضاحهما هما سلسلة التعليمات instructions ولغة البرمجة programming language، ففي المثال الذي ذكرناه في بداية هذا المقال الذي يقول إذا كنت تنتظر صديقًا لك ليزورك، فإنّ التعليمات التي ذكرناها من الممكن كتابتها على الصورة التالية: عندما تصل إلى ناصية الشارع: اتجه إلى اليمين امش عشر خطوات اتجه إلى اليمين مرةً أخرى اُدخل أول منزل من جهة اليسار اصعد إلى الدور الثاني تُعَدّ سلسلة التعليمات أعلاه في الواقع برنامجًا بشريًا مكتوبًا باللغة العربية، والذي يرشِد إلى كيفية الوصول إلى بيتك من نقطة بداية معينة، كما يمكن كتابة تلك السلسلة باللغة الإسبانية أو الهندية أو الإنجليزية أو أيّ لغة بشرية أخرى بشرط معرفة الشخص الذي يسعى للحصول على التوجيه أيًا من تلك اللغات، وكذلك هو الأمر في الحاسوب، فالبرنامج الحاسوبي هو مجموعة من التعليمات المكتوبة بلغة برمجية ينفِّذها الحاسوب، وفيما يلي مثال عن تعليمة مكتوبة بلغة بايثون، وهي أمر للحاسوب بطباعة جملة Hello World على الشاشة. print("Hello World") يمكن أن يتكون البرنامج الحاسوبي من سطر أو تعليمة واحدة إلى ملايين من السطور؛ أما سلسلة التعليمات المكتوبة بلغة برمجة معينة هي ما تُدعى الشيفرة المصدرية للبرنامج source code؛ أما من وجهة نظرالبرمجة الخوارزمية، فهي مجموعة من الخطوات لحل مشكلة ما، ويُعبِّر عنه على أساس مجموعة محدودة من التعليمات، كما أنّ كل تلك التعليمات يمكن تطبيقها والتعبير عنها بلغة البرمجة، كما لا ترتبط الخوارزمية بلغة برمجة معينة، فالخوارزمية هي خطوات مجرَّدة يمكن حتى كتابتها باللغة البشرية ومن ثم تطبيقها بأيّ لغة برمجة مع الأخذ في الحسبان بأنّ كلمة الخوارزمية في البرمجة لها بعض الشروط وهي: الخلو من الغموض: من المهم أن تكون كل خطوة أو كل تعليمة من تعليمات الخوارزمية واضحةً وضوحًا كبيرًا ولا تحمل أكثر من معنى مثل قولنا في تعليمة ما "اجمع س مع ص"، لا أن نقول في إحدى التعليمات "جرب تقريبًا جمع أو طرح س مع ص". المخرجات: من المهم أن تُنتج الخوارزمية على الأقل مخرجًا واحدًا نتيجةً واحدةً، فخوارزمية للتوقع مثلًا يجب عليها إنتاج ناتج ما لعملية التوقع في النهاية حتمًا. منتهية في نقطة ما: من الضروري انتهاء عمل الخوارزمية بعد إنتاج المخرجات المطلوبة، فلا خوارزمية تعمل بصورة مستمرة إلى الأبد. الجدوى/إمكانية التطبيق: يجب أن تعمل الخوارزمية بناءً على الموارد المتاحة، بمعنى أنه لا يمكننا تطبيق خوارزمية تتطلب ذاكرةً تخزينيةً لا يُمكن إنتاجها بالفعل مثل طلب تخزين عدد ذرات الكون. الاستقلالية: من الضروري أن تكون الخوارزمية مستقلة عن لغات البرمجة، أي يمكن تطبيقها بأيّ لغة برمجة، فلا يمكن مثلًا أن تعتمد الخوارزمية على مكتبة جاهزة للاستخدام موجودة في إحدى لغات البرمجة، إذ يؤدي ذلك إلى الاعتماد اعتمادًا كليًا على تلك المكتبة، فلا يمكن للخوارزمية أن تعمل على أية لغة برمجة أخرى. تتكون الخوارزمية من خطوات عدة كما ذكرنا، وتتكون كل خطوة من تعليمة واضحة، كما توجد طرق محدَّدة لتقييم أداء الخوارزمية وتحليلها، وتلك الخطوات مهمة للغاية في حالة خوارزميات الذكاء الاصطناعي، لأن خوارزميات الذكاء الاصطناعي غالبًا ما تطلب في كل تعليمة عددًا ضخمًا جدًا من العمليات الرياضية التي تستهلك الكثير من موارد الحاسوب، ولذلك يلجأ الكثير من مبرمجي تعلّم الآلة والذكاء الاصطناعي إلى استئجار حاسوب فائق الإمكانات عبر الانترنت لتشغيل الخوارزميات، لذلك يُعَدّ تقييم وتحليل أداء الخوارزمية أمرًا مهمًا للباحثين. توجد طريقتان لتحليل الخوارزميات وهما تحليل نظري مسبَق يفترض جميع العوامل ويفترض أداء الخوارزمية بناءً على الموارد المتاحة في الحاسوب، وتحليل متقدِّم وهو تحليل تجريبي، فبعد تنفيذ الخوارزمية على حاسوب معيَّن بإمكانات معينة، تُجمَّع بيانات حقيقية في تلك الحالة مثل الوقت الذي مرَّ منذ بدء تشغيل الخوارزمية والمساحة المستخدَمة من الذاكرة من قبلها أيضًا، كما يتم حساب وتحليل أداء الخوارزمية حسب عاملين رئيسيين كما ذكرنا وهما الوقت المُستهلك في تنفيذ الخوارزمية على الحاسوب ومساحة ذاكرة الوصول العشوائية RAM القصوى المطلوبة لتعمل الخوارزمية بكفاءة. يهمنا مما سبق فهم أنّ خوارزميات الذكاء الاصطناعي مثلها مثل باقي الخوارزميات، فهي في الأساس مستقلة عن لغات البرمجة، وهي خوارزميات لديها مدخلات Inputs وتنتهي إلى استخراج مخرجات أو نتائج Outputs، كما تتكون كل تعليمة من تعليمات الخوارزمية من خطوات واضحة ويكون معظمها عمليات رياضية في الغالب، ويمكن تنفيذ جميعها بأيّ لغة برمجة، ومن الجدير بالذكر أنه بالرغم من إمكانية تطبيق أيّ خوارزمية بأيّ لغة برمجة، إلا أنّ ذلك يتطلب من المبرمج فهمًا دقيقًا وعميقًا لكل خطوة من خطوات الخوارزمية ليستطيع تطبيقها حسب اللغة التي يجيدها، لكن يتطلب ذلك من المبرمج فهمًا عميقًا للرياضيات المستخدَمة في معادلات الذكاء الاصطناعي أيضًا لكي يستطيع تطبيقها بلغة البرمجة التي يجيدها. وليس كذلك هو الحال في جميع لغات البرمجة، إذ طوَّر مبرمجون كثر من مجتمع بايثون حول العالم مكتبات جاهزة للاستخدام تحتوي على دوال Functions جاهزة للاستدعاء فيها الكثير من خوارزميات تعلّم الآلة، أي أنه يمكن لمبرمج بايثون استخدام تلك المكتبات بدلًا من كتابة الخوارزميات بنفسه، لأن ذلك يتطلب كما أوردنا فهمًا عميقًا لكل خطوة من خطوات الخوارزمية التي تحتوي على الكثير من الرياضيات المعقَّدة، لذلك تستطيع تلك المكتبات توفير لك الكثير من المجهود في معظم الأحيان لكي تستطيع استخدام خوارزميات الذكاء الاصطناعي بسهولة دون الدخول في تفاصيل ما يحصل فيما وراء الأحداث. مع ذلك تملك تلك الطريقة عيوبًا لا تخفى على أحد، فالمبرمج إذا لم يكن مدركًا إدراكًا قويًا لما يحدث وراء الأحداث -أي في الخوارزمية وخطواتها-، وقد تكون نتائج الخوارزمية غير مرضية له على الإطلاق، فخوارزميات الذكاء الاصطناعي وتعلّم الآلة عادةً لها الكثير من المتغيرات التي تتحكم تحكمًا كبيرًا في دقة عمل الخوارزمية ودقة استنتاجاتها، لذلك ينصح الخبراء المبرمجين والمطورين بدراسة الخوارزميات دراسةً عميقةً، ودراسة الرياضيات المطلوبة لذلك على قدر المستطاع حتى يتمكن المبرمج من فهم تلك المتغيرات التي تلعب دورًا مفصليًا في دقة برنامجه. لماذا تعلم البرمجة مهم تُعَدّ البرمجة حلقة الوصل بين باحثي الذكاء الاصطناعي وبين تطبيق أبحاثهم على أرض الواقع كما أوردنا، لذلك يلعب المبرمجون دورًا مفصليًا في مجال الذكاء الاصطناعي، كما أنّ البرمجة من أفضل المهن من النواحي المستقبلية، فلا يخفى على أحد التحوّل التقني السريع الذي يحدث حول العالم، فالتقنية المعتمدة على الحواسيب والحوسبة اليوم هي عصب حياتنا ودخلت في عمق تفاصيل يومياتنا ولا يخفى ذلك على الناس، فمهن البرمجة أصبحت من أكثر المهن طلبًا، وستصبح أكثر طلبًا بمرور الزمن بالطبع. اقرأ أيضًا المقال السابق: الذكاء البشري مقابل الذكاء الاصطناعي تعلم البرمجة تعلم الذكاء الاصطناعي فوائد البرمجة المدخل الشامل لتعلم علوم الحاسوب النسخة الكاملة من كتاب مدخل إلى الذكاء الاصطناعي وتعلم الآلة
  11. يصف الدكتور أندرو Andrew N G عالم الحاسوب في جامعة ستانفورد بالولايات المتحدة نماذج تعلُّم الآلة Machine Learning بأنها كهرباء العصر الحديث، وتعلم الآلة هو نوع من خوارزميات الذكاء الاصطناعي المستنبطة من الرياضيات، والتي بدأت بالانتشار والرواج في العقدين الماضيين، ومع أنّ صفة كهرباء العصر الحديث تلك قد يجدها البعض مبالغًا فيها قليلًا كون كثير من تلك الخوارزميات معروفة منذ زمن، إلا أنها تشكِّل بالفعل ثورةً علميةً، في حين يقول بروفيسور علوم الحاسوب الألماني فولفجانج ارتل Wolfgang Ertel في كتابه مقدمة إلى الذكاء الاصطناعي introduction to artificial intelligence: "إنَّ تقنيات الذكاء الاصطناعي لها هدف محدد وهو فهم الذكاء وبناء أنظمة ذكية مماثلة، مع ذلك فإنَّ الطرق والأساليب لهذا الهدف ليست محددةً أو قطعيةً بعد؛ ولذلك أصبح الذكاء الاصطناعي يتكون من عدة تخصصات فرعية اليوم". يثير مصطلح الذكاء الاصطناعي المشاعر، إذ يُعَدّ الذكاء خاصيةً يتميز بها البشر دونًا عن باقي الكائنات، فما هو الذكاء؟ كيف نستطيع قياس الذكاء؟ وكيف يعمل العقل البشري؟ كل تلك اسئلة ذات معنىً وفائدة لمهندسي الذكاء الاصطناعي وعلماء الحاسوب، لكنها ليست سهلة الإجابة ولا قطعيةً كما قد يظن العوام، كما يصعب إعطاء الذكاء الاصطناعي تعريفًا كاملًا دقيقًا، ومع ذلك أول من وضع تعريف له كان جون مكارثي John McCarthy في عام 1955م، حيث قال أنّ الهدف من الذكاء الاصطناعي هو تطوير آلات تتصرف كما لو كانت ذكيةً، لكن يعتقد بعض العلماء بأنَ ذلك التعريف ليس دقيقًا للغاية، ويوصِّف البعض ذلك المصطلح بأنه ببساطة عبارة عن صنع نماذج حسابية للسلوك البشري، فإننا نؤمن بأنّ البشر أذكياء وبناءً على ذلك نفهم نماذج الذكاء، وما علينا سوى فهم تلك السلوكيات ومحاكاتها باستخدام الرياضيات ثم تطبيقها على الحاسوب. من الطرق الأخرى للوصول إلى المبتغى نفسه هو صناعة نماذج حسابية لعمليات التفكير البشري وليس السلوك، إذ أنه لا يكفي أن تبرمِج حاسوبًا يحاكي سلوك البشر، وإنما يجب عليك التفكير مثلهم عن طريق البحث في علم الأعصاب الإدراكي Cognitive Neuroscience، وغالبًا ما تكون استراتيجية البحث قائمةً على تجارب تكشف عما يحدث بداخل رؤوس الناس ومن ثم تطبيق نماذج حسابية تعكس تلك العمليات؛ أما الطريقة الأخيرة في هذا العقد فتكون عن طريق محاكاة ما يجري داخل خلية عصبية واحدة أو ما يُدعى بالشبكة العصبية أو العصبون Neuron، ويبدو ذلك منطقيًا لمعرفتنا بأنَ المخ البشري يتكون من خلايا عصبية، ولكن مع ذلك فهل بالفعل محاكاة الخلايا العصبية تعني محاكاة الذكاء البشري؟ وهل يعني ذلك محاكاة العقل البشري؟ أم فقط الدماغ؟ أم كلاهما؟ لا توجد إجابة قطعية عن تلك التساؤلات حاليًا لدى العلماء. لدى الكثير من العلماء في مختلف الجامعات تعريفهم الخاص بهم عندما يتعلق الأمر بالذكاء الاصطناعي، إذ تتشابه كلها في المعنى وتختلف في الألفاظ، فمثلًا يُعرَّف لغويًا في معجم أوكسفورد للغة الإنجليزية بأنه نظرية وتطوير أنظمة الحاسوب القادرة على أداء المهام التي تتطلب عادةً الذكاء البشري مثل الإدراك البصري والتعرف على الكلام واتخاذ القرار والترجمة بين اللغات، في حين يقول بروفسور أرتل في كتابه Introduction to artificial intelligence (مقدمة إلى الذكاء الاصطناعي) أنَ أفضل تعريف وُضِع حتى الآن للذكاء الاصطناعي هو التعريف الذي ذكرته الدكتورة إيلين ريتش elaine rich بقولها أنَ الذكاء الاصطناعي هو دراسة كيفية جعل أجهزة الحاسوب تقوم بأشياء يكون الإنسان فيها أفضل في الوقت الحالي، وقد شخَّص هذا التعريف ما كان يفعله باحثو الذكاء الاصطناعي خلال الخمسين عامًا الماضية وحتى لسنوات كثيرة قادمة، أي سيبقى هذا التعريف فعالًا كونه واسع النطاق. تتمثَّل قوة الأنظمة الحاسوبية في نقاط عدة، من ضمنها التفوق على البشر في إمكانية تطبيق آلاف العمليات الحسابية المعقدة في ثوان معدودة، ومع ذلك يتفوق الإنسان على الحاسوب بفارق شاسع في أشياء أخرى، فعند دخول الشخص مثلًا إلى غرفة ما أو مكان ما لم يدخله من قبل، فسيتعرَّف على ما يحيطه من أشياء في أجزاء من الثانية، ثم سيتمكن من اتخاذ القرارات فورًا أو التفكير في خطة ما للقيام بأمور عدة بصورة منظَّمة، فحتى الآن تُعَدّ مثل تلك المهام -وبناءً على تعريف أي لين ريتش- هدفًا لباحثي الذكاء الاصطناعي، وبناءً على هذا التعريف لا يمكن بناء الأنظمة الحاسوبية الذكية بدون الذكاء والمنطق الإنساني، فهي مرهونة بالذكاء البشري، فلذلك يُعَدّ علم الأعصاب ذا أهمية كبيرة للذكاء الاصطناعي، كما تُعَدّ القدرة على التكيف أيضًا من أقوى نقاط القوة في الذكاء البشري، فنحن قادرون على التكيف مع الظروف البيئية المختلفة وتغيير سلوكنا وفقًا لذلك من خلال التعلّم لأنّ قدرتنا على التعلّم المستمر مذهلة، وبالتالي فإنَّ تعلّم الآلة وفقًا لتعريف ريتش هو من أهم المجالات الفرعية للذكاء الاصطناعي كونه يعتمد على محاكاة عملية التعلم البشرية. تُعَدّ نصف برامج الحاسوب الحالية ذكيةً بشكل أو بآخر عمومًا، ولا تستطيع العامة في بعض الأوقات التمييز بين تطبيقات الذكاء الاصطناعي والتطبيقات الاعتيادية، فالتطبيقات الاعتيادية رغم أننا نصِفها بالذكية، إلا أنها لا تندرج فعليًا تحت تصنيف الذكاء الاصطناعي، إذ يختلف الأمر كثيرًا عند كتابة برنامج محادثة آلية مثلًا لمساعدة العملاء عند استخدام خوارزميات الذكاء الاصطناعي أو استخدام التعليمات البرمجية الشرطية العادية، ففي حال استخدام التعليمات الشرطية العادية، فسيسلك البرنامج طريقًا واضحًا مرسومًا له يتكون من بضعة أسئلة وإجابات محددة، فإذا ذكر المستخدِم كلمة "الدفع" مثلًا، فاعرض صفحة الدفع، وإذا ذكر كلمة "الخروج"، فسجِّل خروج المستخدِم؛ أما إذا ذكر كلمة "المدفوعات" مثلًا، فلن يكون الحاسوب قادرًا على فهم الأمر لأن مطوِّر البرنامج لم يذكر له تلك الكلمة المفتاحية بالتحديد؛ أي يتكون البرنامج الاعتيادي من مجموعة أوامر لا تنتج ذكاءً ولا تستطيع التصرف في خارج حدودها من الناحية الكمية والنوعية، لذلك من أهم سمات الذكاء الاصطناعي هو محاكاة الذكاء البشري في أمور مثل التعلم والاستنتاج ورد الفعل واتخاذ قرارات لم تكن مبرمجة في الآلة بصورة مباشرة، ومع ذلك فإنَّ تلك القرارات في نهاية الأمر لها حدود نوعية وكمية دائمًا. يحاول الباحثون دائمًا فهم السلوك البشري عمومًا لمحاولة صياغة أو محاكاة ذلك على الحاسوب، إذ كوِّنت بعض من تلك الأفكار والمبادئ في مجال الشبكات العصبية Neural Networks المستمد من علوم المخ والأعصاب، ويُعَدّ هذا نموذجًا واحدًا من نماذج البحث العلمي في الذكاء الاصطناعي، ومن النماذج الأخرى في البحث توجد ما تُسمى بالنظم هدفية التوجه goal-oriented systems، والتي تبدأ بتحديد المشكلة التي نحاول حلها ثم محاولة إيجاد الحل الأمثل لتلك المشكلة عمومًا وبغض النظر عن آلية حل البشر لمثل تلك المشاكل، وبناءً على ذلك كله فإن المهام في الذكاء الاصطناعي متعددة، والحلول والطرق أيضًا متعددة ومختلفة مثل الطب، حيث تملك الأمراض طرق معالجة وخطوات مداواة مختلفة، وكذلك الذكاء الاصطناعي، فهو يقدِّم لوحةً كبيرةً من مختلف الحلول للكثير من التطبيقات مثل صيدلية تحتوي على دواء خاص لكل مرض؛ أما العلوم المعرفية Cognitive Science فهي علوم مكرسة للبحث في تفكير الإنسان، وهي على مستوى أعلى من علوم المخ والأعصاب، أي لا تبحث بالضرورة في تفاعلات الدماغ المادية، كما تُعَدّ من العلوم المهمة أيضًا التي تقدِّم حلولًا ونماذج فعالة للذكاء الاصطناعي. توجد الكثير من الأسئلة الفلسفية الخاصة بالذكاء الاصطناعي التي تطرأ عندما نحاول محاكاة تفكير الإنسان أو محاكاة دماغه، فيرى بعض الفلاسفة أنَّ الوعي والعقل مرتبطان بالدماغ، وبعضهم يستشكلون ذلك؛ أما البتُّ بتطابق العقل والمخ فهو استكشالي إلى حد كبير، فلم يستطع العلم التجريبي الولوج في أمور عدة حتى الآن مثل النية والضمير والوعي والتفكير في الأخلاق وغير ذلك، وبناءً على ذلك فإننا نستطيع تفهُّم تسخيف المسخِّفين لفكرة الإنسان الآلي المدمِّر للبشرية التي تصر السينما العالمية عليها، ففكرة الإنسان الآلي الواعي فكرة خيالية لا وجود لها سوى في سيناريوهات أفلام هوليوود، وذلك لأننا بعيدون جدًا عن تحقيق ذلك حتى الآن، ومع ذلك فهي أفكار تُطرَح من قِبل الفلاسفة بالفعل لأن فكرة الذكاء دون وعي مشكلة إلى حد مفهوم. ظاهرة تأثير الذكاء الاصطناعي ما يُمكن تصنيفه واعتبار كونه ذكاءً اصطناعيًا ليس بالأمر الواضح الجلي في معظم الأوقات، حيث يتغيّر تفسير مصطلح الذكاء الاصطناعي على الأغلب بمرور الوقت والسنوات، فمنذ عدة عقود كان يَعُدّ البشر ما تفعله الآلات في يومنا هذا ذكاءً اصطناعيًا متقدمًا، ولكن عند بلوغنا لما نحن فيه فسنشعر نحن البشر بأنَّ ذلك لم يكن حقًا ذكيّا، حيث يُدعى ذلك بتأثير الذكاء الاصطناعي Artificial Intelligence Effect. ففي عام 1997م مثلًا عندما فاز الحاسوب Deep Blue الذي صنعته شركة IBM على بطل العالم في الشطرنج جاري كاسباروف Garry Kasparov، كانت هنالك أصوات تصدح بأنَّ ذلك لم يكن حقًا ذكاءً، وإنما كان ما يُدعى بالقوة الغاشمة Brute Force، وهي تقنية لحل المشكلات عبر تجربة كل الاحتمالات، والتحقق من مآلات كل احتمال، كما يظهر ذلك التأثير جليًا عبر تاريخ تطور الذكاء الاصطناعي وتطبيقاته، ولذلك يقول دوجلاس هوفستادتر Douglas Hofstadter -هو عالم أمريكي متخصص في الفيزياء وعلوم الإدراك- أنّ الذكاء الاصطناعي هو ما لم يُحقَّق بعد. مختصر تاريخ الذكاء الاصطناعي نسرد في الجدول التالي بعضًا من العناوين المؤرِّخة للذكاء الاصطناعي من بداية القرن العشرين حتى بداية القرن الحالي. 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; } العام الحدث 1937م أظهر الآن تورنج Alan Turing حدود ذكاء الآلات عبر مشكلة التوقف The Halting Problem، وهي مشكلة تحديد ما إذا سينتهي البرنامج الحاسوبي أو سيعمل إلى الأبد، كما أثبت استحالة وجود خوارزمية عامة لحل تلك المشكلة. 1943م صاغ وارن مكولتش Warren MuCulloch وولتر بيتس Walter Pitts نموذج شبكة عصبية صنعية أو عصبون صنعي Artificial Neuron. 1950م وضع الآن تورنج تعريفًا لذكاء الآلة عبر ما عُرِف باختبار تورنج Turing Test، وكتب عن تعلم الآلة والخوارزميات الجينية. 1951م استطاع مارفين مينسكي Marvin Minsky تطوير آلة شبكة عصبية عبر ٣٠٠ أنبوبًا مفرَّغًا حاكى ٤٠ عصبونًا. 1955م بنى آرثر صامويل Arthur Samuel من شركة IBM برنامجًا للعب الشطرنج. 1956م نظَّم جون مكارثي John Mccarthy ندوةً في كلية دارتموث Dartmouth في الولايات المتحدة، وقدَّم لأول مرة مفهوم الذكاء الاصطناعي. 1958م اخترع مكارثي في معهد ماساشوستس للتقنية لغة البرمجة LISP. 1961م اخترع كل من Herbert A. Simon وJ. C. Shaw وAllen Newell أداةً اسمها حل المشكلات العامة General Problem Solver، حيث تستطيع حل بعض المشكلات التي يمكن وصفها رياضيًا على صورة معادلات مثل لعبة أو أحجية برج هانوي، وهي لعبة يمكن وصفها بالرياضيات؛ ومع ذلك فهي أداة لا تستطيع حل أيّ مشكلة من مشكلات العالم الحقيقي. 1963م أسس مكارثي مختبر الذكاء الاصطناعي في جامعة ستانفورد. 1966م أسس جوزيف فايزنباوم Joseph Weizenbaum برنامج Eliza الذي حاور الناس لأول مرة عبر اللغات الطبيعية بعد عمل دام سنتين في معهد ماساشوستس للتقنية. 1969م أظهر مارفين مينسكي Marvin Minsky وسيمور بابرت Seymour Papert في كتابهما Perceptrons شبكةً عصبونيةً بسيطةً جدًا يمكن التعبير عنها بالدوال الخطيّة. 1972م اخترع العالم الفرنسي آلان كولميرور Alain Colmerauer لغة برمجة المنطق PROLOG. 1976م طور شورتليف Shortliffe وبوكانان Buchanan برنامج MYCIN، وهو نظام خبير لتشخيص الأمراض المعدية وقادر على التعامل مع عدم اليقين. 1986م نشر تيرينس سيجنوفسكي Terrence J. Sejnowski وتشارلز روزنبرج Charles R. Rosenberg ورقةً بحثيةً في معهد Salk للدراسات البيولوجية عن نظام Nettalk الذي يستطيع التعلُّم ليقرأ النصوص. 1993م مبادرة RoboCup العالمية لبناء روبوتات مستقلة للعب كرة القدم. 1997م يهزم حاسوب الشطرنج DeepBlue من IBM بطل العالم في الشطرنج غاري كاسباروف Garry Kasparov، وحدوث أول مسابقة RoboCup دولية في اليابان. هل الذكاء الاصطناعي خطر؟ يكمن خطر الذكاء الاصطناعي في الواقع ومن وجهة نظر الكثيرين في نقطتين أولهما خطر التفرد التكنولوجي، وثانيهما خطر الخصوصية والاقتصاد؛ أما أولهما فهي فكرة غريبة بعض الشيء، وهي أن تُصنَّع خوارزميات قادرة على تطوير نفسها بنفسها إلى ما لا نهاية، وفي الحقيقة فإنَّ الفكرة نفسها فيها إشكالية كبيرة لا مساحة للخوض فيها، ولقب التفرد مأخوذ من تفرد الجاذبية، وهي الظاهرة التي تحدث في مركز الثقوب السوداء، كما هي نقطة أحادية البعد كثيفة بلا حدود، حيث تبدأ قوانين الفيزياء كما نفهمها في الانهيار تمامًا، فليس لدينا رؤية واضحة ولا أية معرفة لما يحدث في أفق الثقوب السوداء، وبالمثل فإنَّ صناعة خوارزمية تطوِّر نفسها بنفسها إلى ما لا نهاية أمر لا نعرف عن حقيقته شيء. أما بعيدًا عن ذلك الخطر الوجودي، فلا يتوقف الأمر عند هذا الحد، فبرامج الذكاء الاصطناعي عمومًا تمثِّل تحديًا وخطرًا على الخصوصية وخطرًا على الاقتصاد الإنساني والكرامة الإنسانية كما تُوصَف في بعض المقالات، ويتجلى خطر الخصوصية وضوحًا في بعض البرامج التي تستخدِمها الشركات في قراءة الرسائل البريدية للمستخدِمين لفهمها واستنباط أنماط وإحصائيات منها، كما تتمثل أيضًا في برامج التحدث التي تسجِّل الصوتيات وفهم اللغات الطبيعية البشرية، إذ تحاول كافة الشركات الحصول على أكبر قدر من البيانات حتى ولو كان ذلك على حساب خصوصية مستخدِميها. أما الخطر الاقتصادي فقد بدا واضحًا منذ الثورة الصناعية والثورة التقنية التي بدأ فيها رجال الأعمال باستبدال العامل الآلي بالعامل البشري دون النظر إلى مخاطر هذا الاستبدال على المستقبل البعيد من إضعاف القوة الشرائية للكثير من الناس الذين هم في الأساس المستهلكون الحقيقيون لمنتجات تلك المصانع وخدمات تلك الشركات، فبدأنا نسمع بمطاعم لا تعمل فيها سوى الآلات، ومحلات تجارية ذكية مدعومة ببرامج وأجهزة في كل ركن من أركانها لتحل محل العمال، وفي حقيقة الأمر أنَّ معظم تلك النظم في الشركات والمصانع لا تحتاج أصلًا إلى ذكاء اصطناعي بالمعنى العلمي، فمعظم تلك الحالات عبارة عن آلات مبرمجة خصيصًا لهدف ومهمة محدَّدة. لمن المُهين من وجهة نظر الكثيرين أن نرى يومًا ما استبدال الآلات والبرامج الذكية منها وغير الذكية بوظائف مرموقة مثل المحاماة التي تبدو من تلك المهن المستهدَف غزوها من قبل مبرمجي الذكاء الاصطناعي، وكذلك من المُهين رؤية طبيب آلي وجراح آلي وقاضي آلي وشُرطي آلي في المستقبل كما تُعَدّ احتمالية استبدال تطبيقات الذكاء الاصطناعي ببعض المهن عاليةً جدًا بحلول عام 2035م بحسب تقرير شبكة الإذاعة البريطانية BBC المُعَد سنة 2015م، فاحتمالية استبدال مهنة النادل 90% والمحاسب القانوني بنسبة 95% وعامل الاستقبال بنسبة 96% وسائق سيارات الأجرة بنسبة 57%. يحذِّر عدد لا بأس به من رجال الأعمال والمبتكرين مثل أيلون ماسك مرارًا وتكرارًا من الأخطار المحتمَلة للذكاء الاصطناعي مع إدراكهم التام بأنَّ ما توصَّل إليه البشر حتى الآن لا يشكل أي خطر حقيقي ولا حتى في المستقبل القريب، ومع ذلك فإنَّ أيلون ماسك يحذِّر من خطوات العلماء وشغفهم للوصول إلى تلك النقطة التي لا يعتقد هو أنهم يدركون كافة جوانبها، في حين يعتقد البعض مثل ستيفن هوكينج أنه إذا اكتسب الذكاء الاصطناعي المتقدِّم يومًا ما القدرة على إعادة تصميم نفسه بمعدل متزايد، فقد يؤدي ذلك إلى انفجار ذكائي لا يمكن إيقافه حتى الانقراض التام للإنسان، ومع أنّ أيلون ماسك نفسه يصف الذكاء الاصطناعي بأنه أكبر خطر وجودي للبشرية، إلا أنه أسس شركة OpenAI على صورة منظمة غير ربحية مع شركاء آخرين حتى يتمكن من تركيز أبحاث المنظمة على تطوير ذكاء اصطناعي إيجابي لا يمثِّل خطرًا على الإنسان. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن إشكالية الوعي في الذكاء الاصطناعي ما نستفيدهُ من النقاشات الفلسفية والعلمية الأكاديمية هو أنَّ الذكاء البشري الذي يُعَدّ جزءًا ركيزيًا في العقل البشري من الصعب جدًا فهمه فهمًا كاملًا من قِبَل الأكاديميين والعلماء؛ لذلك وبالرغم من بدء المجال البحثي في الذكاء الاصطناعي منذ منتصف القرن الماضي إلا أنَّ التطور يُعَدّ محدودًا، وعمومًا حتى في ذروة تقدُّم المجال البحثي وذروة تقدُّم الذكاء الاصطناعي، فكل الخوارزميات في نهاية الأمر تتعلق بأساليب وظيفية تتمحور حول القيام بمهمة واحدة معينة فقط. تستطيع طبعًا برمجة تطبيق شامل فيه أكثر من برنامج حاسوبي لأداء العديد من المهام في الوقت نفسه مثل برنامج لقراءة المدخلات الصوتية، بحيث يستطيع الحاسوب فهم ما يتحدث به غيره ليعالج ذلك على أساس مدخلات في برنامجه، وبرنامج آخر لقراءة تلك المدخلات لصياغة نص للرد، وبرنامج آخر لتحويل نص الرد إلى صوت مرةً أخرى لتشغيله، وبذلك نكون قد صنعنا تطبيقًا مناسبًا لإنسان آلي يستطيع سماع بعض الجمل لتحويلها إلى نصوص، لتعالَج رياضيًا لاستخراج وصياغة مخرجات، ولكن مع ذلك فلا يستطيع الإنسان الآلي بأي حال من الأحوال إنتاج ذكاء في مهمات أخرى وبخوارزميات أخرى غير تلك الخوارزميات التي أُدخلت إليه من قِبَل المبرمج لفعل أمر معيّن مثل معالجة نص ما أو معالجة مدخلات مطلوبة لتوقع نتيجة ما، حتى مع أساليب الذكاء الاصطناعي الحالية فإنتاج ذكاء في مهام أخرى يمكن أن يتطلب وعيًا أو نوعًا من الإرادة. بُرمِجت برامج للإنسان الآلي في مثالنا السابق تحاكي تلك الوظائف الموجودة في جسم الإنسان مثل برنامج لاستقبال الأصوات وبرنامج لترجمة الأصوات إلى لغة الآلة وبرنامج تحليل النصوص …إلخ، لكن في حالة عُطل أي برنامج من تلك المنظومة التي تربط بعضها بعضًا، فهو حكم بالموت على المنظومة بكاملها؛ أما في حالة الإنسان، فعُطل حاسة ما في كل الأحوال لا يمُيت باقي حواسه ولا يؤثر ذلك على وعيه، ومنذ آلاف السنين يتحدث الفلاسفة عن الوعي، ولا يكاد فيلسوف يوافق الآخر على مفهوم فلسفي صحيح للوعي، فما بالك أصلًا بافتراض وجود حس للوعي يستطيع العلماء محاكاتها رياضيًا لإنتاج الوعي حاسوبيًا؟ إذ لم يستطع باحثو الذكاء الاصطناعي حتى تاريخ كتابة هذا الكتاب فهم الوعي البشري بعد تطور مجال الذكاء الاصطناعي وخاصةً تعلّم الآلة وبالأخص أيضًا التعلّم العميق Deep Learning والشبكات العصبية التي تحاكي الخلايا العصبونية في المخ، رغم أننا الآن فعليًا نستطيع محاكاة خلية من الدماغ عبر ما يُدعى ببرمجة الشبكات العصبية، لكن لا يزيد الأمر عن خوارزميات رياضية مستخدَمة لتوقُّع مخرجات في مَهمة معينة في تعلم الآلة ومثلها خوارزميات تحاكي خليةً واحدةً من خلايا المخ. أحد الفوارق بين النماذج التي أقصدها هنا بالواعية وغير الواعية هو أنَّ غير الواعية لديها حدود كمية ونوعية دائمًا كما هو الأمر في نماذج الذكاء الاصطناعي، فمن أهم النماذج التي سأذكرها في الكتاب مثلًا نماذج التصنيف أو المُصنِّفات التي هي خوارزمية تعلّم آلة تستطيع من خلالها إنتاج تصنيفات فقط حسبما بُرمجَت عليه، فقد يستخدمها الإنسان مثلًا في مهام مثل تصنيف الرسائل البريدية لتكون مزعجةً أم لا، أو تصنيف نوع سرطان خبيث أم حميد، لكن لا تستطيع الخوارزمية بأيّ حال من الأحوال فهم أو معالجة أمر لم تبرمَج عليه، فإذا كان لدينا برنامج يستطيع تحليل صور الأشعة الطبية لتحديد نوع السرطان الموجود فيها، فسيفترض النموذج دائمًا أنَّ مُدخلاته أشعة طبية، فلن يدرك البرنامج إذا أدخلتَ صورتك الشخصية للنموذج أنَّ تلك الصورة دخيلة عليه إذا لم يُبرمَج على ذلك، وبالتالي سيخبرك البرنامج بتوقعه لنوع السرطان بناءً على تلك الصورة غير الصحيحة أصلًا، فهو في النهاية محدود الأهداف والقدرة، حيث بُرمِج وصُنع لهدف ثابت ومحدد وهو توقُّع تصنيف السرطان حسب خوارزمية مستنبَطة من علم الإحصاء والجبر الخطي والتفاضل والتكامل وليس لديه وعي لذاته. يقول بين جورتزل Ben Goertzel وهو أحد علماء شركة Hanson Robotics المصنعة للروبوت صوفيا الشهير أنَّ الروبوت يضايق المتخصصين لأنه يعطي للعامة إحساسًا بأننا بالفعل توصَّلنا لذكاء اصطناعي مشابه لذكاء الإنسان، وهذا بالطبع ليس صحيحًا أبدًا، فبالرغم من كون جورتزل من المتفائلين باستطاعة البشر يومًا ما خلال العقدين القادمين صناعة ذكاء اصطناعي عام AGI اختصارًا لمصطلح Artificial General Intelligence، إلا أنه يعترف بأنَّ الروبوت صوفيا ما هي إلا برنامج دردشة مدهش. اقرأ أيضًا تعلم الذكاء الاصطناعي الذكاء الاصطناعي: أهم الإنجازات والاختراعات وكيف أثرت في حياتنا اليومية بناء شبكة عصبية للتعرف على الأرقام المكتوبة بخط اليد باستخدام مكتبة TensorFlow إعداد شبكة عصبية صنعية وتدريبها للتعرف على الوجوه النسخة الكاملة من كتاب مدخل إلى الذكاء الاصطناعي وتعلم الآلة
×
×
  • أضف...