-
المساهمات
23 -
تاريخ الانضمام
-
تاريخ آخر زيارة
نوع المحتوى
ريادة الأعمال
البرمجة
التصميم
DevOps
التسويق والمبيعات
العمل الحر
البرامج والتطبيقات
آخر التحديثات
قصص نجاح
أسئلة وأجوبة
كتب
دورات
كل منشورات العضو رشا سعد
-
قد تحتاج في سكريتات باش للتعامل مع مئات المتغيرات التي يدخلها المستخدم وفي هذه الحالة لن يكون من المناسب أن تنشئ هذه المتغيرات يدويًا، وهنا يأتي دور المصفوفات Arrays الحل المنقذ في مثل هذه الحالات، سنشرح في هذا المقال أساسيات التعامل مع المصفوفات في باش وتعديلها بكفاءة. أنشئ مصفوفتك الأولى في باش لنفترض أنك تحتاج لكتابة سكربت بسيط لتحديث الطابع الزمني timestamp لخمس ملفات مختلفة، والطابع الزمنية هي مجموعة محارف وأرقام تدل على تاريخ ووقت إجراء تعديل معين على الملف. لننشئ السكربت timestamp.sh الخاص بهذه العملية أولًا بطريقة المتغيرات دون استخدام المصفوفات، فسيكون على الشكل التالي: #!/bin/bash file1="f1.txt" file2="f2.txt" file3="f3.txt" file4="f4.txt" file5="f5.txt" touch $file1 touch $file2 touch $file3 touch $file4 touch $file5 لننشئه الآن باستخدام المصفوفات ونلاحظ الفرق، سنخزن في المصفوفة أسماء الملفات الخمسة عوضًا عن تعريف خمس متغيرات تقابل أسماء الملفات الخمسة، وفيما يلي الصيغة العامة لتعريف المصفوفات في باش: array_name=(value1 value2 value3 … ) لنطبق الصيغة على حالتنا، فستكون مصفوفة أسماء الملفات وفق التالي: files=("f1.txt" "f2.txt" "f3.txt" "f4.txt" "f5.txt") السكربت الآن أوضح وأكثر كفاءة وأقرب لمعايير الكود النظيف، فقد استبدلنا خمسة متغيرات بمصفوفة واحدة فقط. الوصول لعناصر مصفوفات باش تبدأ فهرسة مصفوفات باش بالصفر 0 ويستخدم الدليل n-1 للوصول إلى العنصر n من المصفوفة. فإذا رغبنا بإظهار العنصر الثاني في المصفوفة فسنكتب التالي: echo ${files[1]} وللوصول للعنصر الثالث: echo ${files[2]} وهكذا لبقية العناصر. لنجرب أمرًا آخر، ألقِ نظرة على السكربت التالي reverse.sh الذي سيظهر كامل عناصر مصفوفتك بترتيب عكسي من العنصر الأخير إلى الأول: #!/bin/bash files=("f1.txt" "f2.txt" "f3.txt" "f4.txt" "f5.txt") echo ${files[4]} echo ${files[3]} echo ${files[2]} echo ${files[1]} echo ${files[0]} وستحصل بتنفيذه على الخرج التالي: سنتعلم لاحقًا كيف نظهر عناصر المصفوفة باستخدام الحلقات Loops، فتكرار الأمر echo للغرض نفسه عددً كبيرًا من المرات ليس الطريقة الأفضل لإظهار عناصر المصفوفة. يمكننا أيضًا إظهار عناصر المصفوفة دفعة واحدة في سطرٍ واحد كما في الأمر التالي: echo ${files[*]} f1.txt f2.txt f3.txt f4.txt f5.txt ويساعدك الأمر التالي على إظهار عدد عناصر المصفوفة الذي يسمى اصطلاحًا حجم المصفوفة size of array: echo ${#files[@]} 5 وبوسعك تغيير قيمة أي عنصر من عناصر المصفوفة بسهولة، ألقِ نظرة على السطر أدناه إذ نغير فيه قيمة العنصر الأول إلى القيمة "a.txt": files[0]="a.txt" إضافة عناصر جديدة إلى مصفوفة باش لننشئ مثلًا المصفوفة التالية التي تتضمن أسماء أشهر توزيعات لينكس: distros=("Ubuntu" "Red Hat" "Fedora") تحتوي المصفوفة الحالية على ثلاثة عناصر، ويمكنك إضافة عناصر أخرى إلى نهايتها باستخدام المعامل =+، دعنا نضيف مثلًا توزيعة Kali بكتابة: distros+=("Kali") تحتوي المصفوفة الآن أربعة عناصر، وتبين الصورة أدناه السكربت بعد إضافة العنصر الأخير: حذف عناصر من مصفوفة باش لننشئ مصفوفة تتضمن الأعداد من 1 إلى 5: num=(1 2 3 4 5) يمكنك إظهار كافة عناصر المصفوفة كما يلي: echo ${num[*]} 1 2 3 4 5 لنفترض أننا نرغب بحذف العنصر الثالث منها، فسنستخدم الأمر unset وفق التالي: unset num[2] أظهر الآن كافة عناصر المصفوفة كما يلي: echo ${num[*]} 1 2 4 5 ولاحظ حذف العنصر الثالث. يمكنك أيضًا حذف المصفوفة بالطريقة نفسها بكتابة الأمر أدناه: unset num تبين الصورة أدناه تنفيذ سكربت يتضمن كل ما تعلمناه في هذه الفقرة: إنشاء مصفوفة هجينة بأنواع مختلفة من البيانات تتميز باش عن الكثير من لغات البرمجة بقدرتها على إنشاء مصفوفات هجينة hybrid arrays تحتوي أنواعًا مختلفة من البيانات، مثلًا أعداد صحيحة وسلاسل نصية وغيرها كما في سكربت باش التالي باسم user.sh: #!/bin/bash user=("john" 122 "sudo,developers" "bash") echo "User Name: ${user[0]}" echo "User ID: ${user[1]}" echo "User Groups: ${user[2]}" echo "User Shell: ${user[3]}" لاحظ أن مصفوفة المستخدمين السابقة تتضمن أربع عناصر هي: العنصر "john" نوعه سلسلة نصية String العنصر 122 نوعه عدد صحيح Integer العنصر "sudo,developers" نوعه سلسلة نصية String العنصر "bash" نوعه سلسلة نصية String سيكون خرج السكربت على النحو التالي: الخلاصة نصل بذلك لنهاية مقال المصفوفات في لغة باش Bash الذي تعرفنا فيه على المصفوفات وكيفية استخدامها لتنظيم البيانات بكفاءة، وتعلمنا كيفية إنشاء المصفوفات والوصول لعناصرها وعكس ترتيبها وتعديلها وإضافة وحذف العناصر لها، وتعرفنا على طريقة إلى إنشاء مصفوفات هجينة تحتوي على أنواع بيانات مختلفة. ترجمة -وبتصرف- للمقال Using Arrays in Bash. المقال السابق: تمرير الوسطاء إلى سكربت باش Bash طريقة التعامل مع المتغيرات وتمرير الوسطاء لسكربت باش مدخل إلى صدفة باش Bash الحصول على مدخلات من لوحة المفاتيح وإجراء العمليات الحسابية في سكربتات الصدفة الأخطاء الشائعة التي تحدث عند كتابة سكربتات الصدفة
-
نشرح في هذا المقال نشر تطبيق روبي أون ريلز Ruby on Rails على خادم أوبنتو خطوة بخطوة بدايةً من اختيار خادم الاستضافة وتثبيت الاعتماديات dependencies عليه، ووصولًا إلى إعداد خادم NGINX لاستقبال طلبات التطبيق وبناء قاعدة بيانات التطبيق ونشرها على خادم الاستضافة بمساعدة Capistrano وهي أداة مفتوحة المصدر مخصصة لأتمتة عمليات النشر. أولًا: إعداد خادم الإنتاج سنشرح بداية أبرز معايير اختيار الخادم الافتراضي الخاص VPS الذي سيعمل عليه تطبيق Rails فخيارات الاستضافة كثيرة وعليك أن تعرف الفروقات فيما بينها وتعتمد ما يناسب احتياجات تطبيقك سواء من ناحية الحجم أو المواصفات أو الأمان وغيرها. اختيار مزود الاستضافة هناك العديد من مزودي خدمات الاستضافة ولكل منهم سلبياته وإيجابياته، ويُقصد بمزود خدمة الاستضافة الجهة التي تمتلك خوادم موجودة في مركز بيانات datacenter وتؤجرها للراغبين باستخدامها بمقابل مادي يدفعونه شهريًا. إن استئجار الخوادم الموجودة في مراكز البيانات أمر ضروري عند نشر التطبيقات في بيئة الإنتاج فهو يحميك من المشكلات الطارئة المتعلقة بالتشغيل وأعطال التجهيزات العتادية لأن المزود يؤمن حلولًا لها، كما يوفر لتطبيقك اتصالًا فائق السرعة بالإنترنت وهو أمر مفيد تشغيل التطبيقات المقدمة للعملاء عبر الإنترنت. إذا كنت تحتاج تحكمًا كبيرًا ببيئة تطبيقك فاستبعد مزودي خدمات الاستضافة المُدارة managed hosting providers أي شركات الاستضافة التي تتكفل بإدارة البنية التحتية التي استأجرتها كاملة لأنها لا تمنحك تحكمًا كافيًا ببيئتك وتفتقر عادةً إلى تثبيت آخر التحديثات. كيف أحدد مواصفات الخادم الذي أحتاجه لتطبيق ريلز؟ تحتاج تطبيقات إطار العمل Rails وتطبيقات روبي عمومًا إلى سعة ذاكرة وصول عشوائي RAM كبيرة، فسعة الذاكرة RAM معيار أساسي عليك التركيز عليه عند اختيار الخادم، وتذكر أنك ستحتاج لتثبيت مكونات أخرى على الخادم ستأخذ حصتها من RAM أيضًا، ومنها قاعدة بيانات التطبيق مثل: MongoDB أو PostgreSQL أو Redis التي تعمل في الخلفية. فإذا كنت تنشر تطبيقك الأول فإن خادمًا بذاكرة RAM حجمها 2 جيجابايت مناسب ومقبول التكلفة، وبالرغم من إمكانية البدء بذاكرة 1 جيجابايت لكنها لن تلبي طلبك على الأغلب وسرعان ما ستنفذ عند تجميع الأصول compiling assets وتجهيز الملفات أثناء النشر إلى بيئة الإنتاج. كما ستحتاج لزيادة سعة RAM على الخادم في الحالات التي تستخدم فيها اعتماديات كبيرة مع تطبيقك مثل خدمات البحث أو غيرها حسب ما تتطلبه هذه الاعتماديات إضافةً إلى احتياجات لتطبيقك، فخدمة بحث مثل ElasticSearch على سبيل المثال تحتاج لوحدها إلى 4 جيجابايت، لذا يلجأ البعض لتشغيلها على خادم منفصل عن خادم تطبيقات روبي وريلز لتسهيل توسعة الموارد المحجوزة للخدمتين (أي التطبيق والبحث) بمعزل عن بعضهما. إنشاء الخادم سنعرض هنا كمثال طريقة إنشاء الخادم على منصة الاستضافة DigitalOcean علمًا أن العملية متقاربة في معظم المنصات، افتح المنصة وانتقل إلى صفحة إنشاء قطرة Droplet ثم تابع الخطوات. ملاحظة: Droplet مصطلح خاص بمنصة DigitalOcean يشير إلى وحدة افتراضية تشبه الخادم الافتراضي Virtual Server يمكن للمستخدمين إنشاؤها لتشغيل المواقع والتطبيقات وقواعد بيانات ويمكن تخصيص مواردها المختلفة كالمعالج والذاكرة ومساحة التخزين. الخطوة 1: اختر نظام تشغيل الخادم اخترنا هنا نظام أوبنتو 20.24، وهو نظام تشغيل يتمتع بدعم فني طويل الأمد LTS أي أنه سيتلقى تحديثات أمنية أكثر من المعتاد وعلى مدى سنوات طويلة، وهذا أمر مهم عند نشر التطبيقات في بيئة الإنتاج. توضح الصورة التالية خيار الخادم أوبنتو 20.24 من القائمة المنسدلة. الخطوة 2: حدد حجم الخادم ومواصفاته بعد اختيار نظام التشغيل الخاص بخادم الاستضافة ستظهر أمامك نافذة لتحدد حجم الخادم ومواصفاته، كما توضح الصورة التالية. في حال لم تكن متأكدًا من الحجم المناسب لتطبيقك، يمكنك اختيارحجم RAM لتكون 2 جيجابايت، وتغيير لاحقًا الحجم زيادة أو نقصانًا حسب احتياجات تطبيقك، وهنا تكمن ميزة الخوادم الافتراضية مقارنة بالخوادم الفيزيائية إذ يمكنك تغيير مواصفاتها بسهولة في أي لحظة بزيادة سعة الذاكرة RAM أو إنقاصها وكذلك الأمر بالنسبة لوحدات المعالجة المركزية CPU ووحدات التخزين وغيرها. الخطوة 3: اختر المنطقة الجغرافية الخطوة التالية هي تحديد منطقة الخادم server region أي المكان الجغرافي الذي يتواجد فيه مركز البيانات الذي سيعمل عليه خادمك الافتراضي، اختر المنطقة الأقرب لمكان تواجد مستخدمي تطبيقك أو مكان تواجدك كمدير للتطبيق والخادم. الخطوة 4: ضبط الخيارات الإضافية يوجد عدد من الخيارات الأخرى يمكنك ضبطها حسب احتياجات عملك إذا رغبت بذلك، ومنها: 1. الشبكات الخاصة Private Networking: يفيدك إعداد الشبكات الخاصة إذا كان خادمك يحتاج للتخاطب مع خوادم أخرى، كالحالة التي يكون فيها خادم قاعدة البيانات منفصلًا عن خادم التطبيق. 2. البروتوكول IPv6: بتفعيل هذا الخيار يمكنك إعطاء خادمك عنوان IPv6. 3. المراقبة Monitoring: يعطيك بعض المقاييس التقريبية التي تفيدك في قياس نسب استخدام الخادم. 4. النسخ الاحتياطي Backups: يعني تفعيل هذا الخيار أخذ صورة image أو نسخة كاملة عن خادمك يمكنك استعادته منها عند حدوث أي طارئ، لكن هذه النسخ الكاملة لا تُشَغَّل بفترات متقاربة لذا تُعدّ النسخ الاحتياطية الساعية لقاعدة بيانات التطبيق أو Hourly database backups أفضل منها في معظم الحالات. الخطوة 5: أنشئ خادمك بعد ضبط كافة الخيارات السابقة، اضغط على زر الإنشاء Create وستُنشئ DigitalOcean خادمك الجديد خلال دقيقة تقريبًا. بعدها توجه إلى بريدك الإلكتروني بمجرد إتمام عملية الإنشاء لتستلم كلمة مرور الخادم. يمكنك الآن الاتصال بالخادم من حاسوبك المحلي Local Machine بواسطة عنوان IP الخاص به ومستخدم الجذر root الذي استلمته عبر البريد الإلكتروني، كما في المثال التالي طبعًا مع استبدال 1.2.3.4 بعنوان IP لخادمك: ssh root@1.2.3.4 الخطوة 6: أنشئ مستخدم النشر أنشئ مستخدمًا لعملية النشر باسم deploy مثلًا بأذونات محدودة، واعتمد عليه في تشغيل برمجياتك على الخادم في بيئة الإنتاج بدلًا من تشغيلها من حساب المستخدم الجذر، يقلل ذلك من خطر حصول المخترقين على التحكم الكامل بخادمك في حال نجح باختراقه. إذًا في أثناء دخولك الأول إلى الخادم باستخدام حساب الجذر root أنشئ مستخدمًا جديدًا وامنحه امتيازات sudo كما يلي: root@1.2.3.4 adduser deploy adduser deploy sudo exit أضف بعدها مفتاح SSH إلى الخادم لتسريع الدخول إليه، سنستخدم في ذلك الأداة ssh-copy-id، علمًا أنها لا تتوفر افتراضيًا على أجهزة ماك فإذا كان جهازك المحلي من نوع ماك ثبتها باستخدام homebrew وفق الأمر brew install ssh-copy-id ثم نفذ التالي: ssh-copy-id root@1.2.3.4 ssh-copy-id deploy@1.2.3.4 يمكنك الآن تسجيل الدخول إلى الخادم بحساب المستخدم root أو المستخدم deploy ومن دون الحاجة لكتابة كلمة المرور لأن تفعيل مفتاح SSH يغنيك عنها. لنفتح الآن جلسة اتصال SSH بواسطة مستخدم النشر deploy وهو المستخدم الذي سننفذ بواسطته كافة الخطوات التالية في المقال، علمًا أنك لن تُطالب بإدخال كلمة مروره كما ذكرنا: ssh deploy@1.2.3.4 ثانيًا: تثبيت روبي على الخادم يشبه تثبيت روبي في بيئة الإنتاج تثبيته في بيئة التطوير مع مزيد من التدقيق في تثبيت جميع اعتماديات لينكس الضرورية لضمان تصريف compile روبي بطريقة صحيحة، ويساعدك استخدام مدير إصدارات روبي Ruby version manager على نشر الإصدارات الجديدة بسهولة ومن دون أي تعديل في ملفات الإعداد config files. الخطوة الأولى هي تثبيت الاعتمادات الضرورية لتصريف كل من روبي وإطار العمل ريلز، وأهمها ما يلزم لعمل المكتبة Webpacker مع ريلز لذا سنبدأ بإضافة مستودعات Yarn و Node.js إلى نظامنا لنتمكن من تثبيتها. ثم سنثبت Redis لنستطيع استخدام ActionCable مع مقابس الويب WebSocket في بيئة الإنتاج، كما قد يرغب البعض باستخدام Redis بصفته مخزن تخبئة cashing لخادم الإنتاج. لنطبق ذلك عمليًّا، تأكد أولًا من تسجيل دخولك بالمستخدم deploy ثم اكتب الأوامر التالية: deploy@1.2.3.4 # إضافة مستودع Node.js curl -sL https://deb.nodesource.com/setup_lts.x | sudo -E bash - # إضافة مستودع Yarn curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list sudo add-apt-repository ppa:chris-lea/redis-server # تحديث الحزم من المستودعين المضافين أعلاه sudo apt-get update # تثبيت الاعتماديات المطلوبة لتصريف روبي من المستودعين المضافين sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev software-properties-common libffi-dev dirmngr gnupg apt-transport-https ca-certificates redis-server redis-tools nodejs yarn يمكننا الآن تثبيت روبي على الخادم بعد إتمام تثبيت الاعتماديات وقد استخدمنا في هذا المقال الإصدار 3.3.1 من روبي لكن تستطيع اختيار الإصدار الذي تفضله. كما سبق وذكرنا سنستخدم مدير الإصدارات rbenv لتثبيت روبي، فهو أسهل في التعامل والترقية وأيضًا يوفر لك عددًا من المكونات الإضافية المفيدة لضبط متغيرات بيئة الإنتاج. نَفِّذْ الأوامر التالية: deploy@1.2.3.4 git clone https://github.com/rbenv/rbenv.git ~/.rbenv echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc echo 'eval "$(rbenv init -)"' >> ~/.bashrc git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc git clone https://github.com/rbenv/rbenv-vars.git ~/.rbenv/plugins/rbenv-vars exec $SHELL rbenv install 3.3.1 rbenv global 3.3.1 ruby -v # ruby 3.3.1 نثبت الآن مُجَمِّع الوحدات Bundler (وهو أداة تستخدم في مشاريع الويب لتنظيم الوحدات والاعتماديات وتوليد الأصول): deploy@1.2.3.4 # يثبت الأمر التالي الإصدار الأخير من Bundler، والإصدار الأحدث عند إعداد المقال هو 2.x gem install bundler # أو نفذ الأمر التالي إذا رغبت بتثبيت إصدار أقدم مثل 1.x gem install bundler -v 1.17.3 # يعطيك الأمر التالي رقم إصدار Bundler ويساعدك بالنتيجة على التأكد من صحة تثبيته bundle -v # Bundler version 2.0 إذا حصلت على رسالة خطأ مفادها عدم العثور على Bundler، نفذ الأمر rbenv rehash وكرر المحاولة. ثالثًا: إعداد خادم الويب NGINX والوحدة Passenger يتطلب العمل في بيئة الإنتاج وجود خادم ويب مثل NGINX أمام ريلز لاستقبال طلبات HTTP والتعامل مع شهادات SSL وملفات التطبيق الثابتة static files بطريقة أسرع من روبي، وعلى أرض الواقع من غير المنطقي طرح تطبيقك للمستخدمين من دون خادم ويب. اعتمدنا في مثالنا على NGINX والوحدة Passenger، إذ سيستقبل NGINX طلبات HTTP الواردة إلى خادم التطبيق، ويحولها إلى Passenger الذي سيشغل تطبيقك. اكتب الأوامر التالية لديك لإضافة مستودع Passenger وتثبيته على الخادم: deploy@1.2.3.4 sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7 sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger $(lsb_release -cs) main > /etc/apt/sources.list.d/passenger.list' sudo apt-get update sudo apt-get install -y nginx-extras libnginx-mod-http-passenger if [ ! -f /etc/nginx/modules-enabled/50-mod-http-passenger.conf ]; then sudo ln -s /usr/share/nginx/modules-available/mod-http-passenger.load /etc/nginx/modules-enabled/50-mod-http-passenger.conf ; fi sudo ls /etc/nginx/conf.d/mod-http-passenger.conf ثم افتح ملف إعدادات Passenger باستخدام أي محرر نصوص تفضله مثل nano أو vim كما يلي: deploy@1.2.3.4 # تحرير الملف باستخدام nano sudo nano /etc/nginx/conf.d/mod-http-passenger.conf # تحرير الملف باستخدام vim sudo vim /etc/nginx/conf.d/mod-http-passenger.conf عَدِّل السطر الذي يحتوي القيمة passenger_ruby ضمن الملف ليصبح وفق التالي، وذلك لتوجيه Passenger إلى الإصدار المطلوب من روبي: passenger_ruby /home/deploy/.rbenv/shims/ruby; احفظ التغييرات على الملف وأغلقه. ثم شغل NGINX كما يلي: deploy@1.2.3.4 sudo service nginx start يمكنك التأكد من صحة تشغيل NGINX بفتح عنوان IP العام للخادم في متصفح الإنترنت لديك، وستحصل على رسالة الترحيب التالية "Welcome to NGINX". والآن احذف ملف الإعدادات الافتراضي لخادم NGINX، وأضف ملفًا خاصًا لتطبيقك: deploy@1.2.3.4 sudo rm /etc/nginx/sites-enabled/default # تحرير الملف باستخدام nano sudo nano /etc/nginx/sites-enabled/myapp # تحرير الملف باستخدام VIM sudo vim /etc/nginx/sites-enabled/myapp عدل على الملف ليصبح وفق الآتي، واستبدل myapp باسم تطبيقك، علمًا أننا سنستخدم الملف نفسه لاحقًا لتحديد المجلد deploy_to الخاص بالمكتبة Capistrano: server { listen 80; listen [::]:80; server_name _; root /home/deploy/myapp/current/public; passenger_enabled on; passenger_app_env production; location /cable { passenger_app_group_name myapp_websocket; passenger_force_max_concurrent_requests_per_process 0; } # Allow uploads up to 100MB in size client_max_body_size 100m; location ~ ^/(assets|packs) { expires max; gzip_static on; } } احفظ التغييرات على الملف وأغلقه، ثم أعد تحميل NGINX كما يلي لتطبيق التغييرات: deploy@1.2.3.4 sudo service nginx reload رابعًا: إنشاء قاعدة البيانات سنعرض طريقتين لإنشاء قاعدة بيانات التطبيق، الأولى باستخدام PostgreSQL وهي الخيار الأفضل في بيئة الإنتاج، والثانية باستخدام MySQL، اتبع الأسلوب الذي يناسبك. إنشاء قاعدة بيانات PostgreSQL ثبت في البداية خادم Postgres مع المكتبة libpq التي تسمح بتصريف gem pg وهي مكتبة روبي المخصصة للتعامل مع PostgreSQL. للمزيد يمكنك مشاهدة الفيديو التالي: سينشأ مع التثبيت مستخدم لينكس يدعى postgres يملك الصلاحيات الكاملة على قاعدة البيانات، يمكنك استخدامه لإنشاء مستخدم قاعدة بيانات خاص بتطبيقك، اخترنا له الاسم deploy في مثالنا. ثم أنشئ قاعدة بيانات التطبيق، تدعى في مثالنا myapp، وتأكد أن المستخدم deploy هو مالك قاعدة البيانات، وفق الأوامر التالية: deploy@1.2.3.4 sudo apt-get install postgresql postgresql-contrib libpq-dev sudo su - postgres createuser --pwprompt deploy createdb -O deploy myapp exit والآن يمكنك الاتصال بقاعدة البيانات الجديدة وفق التالي: psql -U deploy -W -h 127.0.0.1 -d myapp استخدم 127.0.0.1 بدلًا من localhost عند الاتصال بقاعدة البيانات. إنشاء قاعدة بيانات MySQL ستحتاج لتثبيت نسختي الخادم server والعميل client من MySQL لإنشاء قاعدة بيانات التطبيق وتصريف روبي gem عبر الواحهة mysql2 (يساعدك الاطلاع على المقال كيفية تثبيت MySQL على أوبونتو 18.04). وبعد التثبيت اكتب الأوامر التالية: deploy@1.2.3.4 sudo apt-get install mysql-server mysql-client libmysqlclient-dev sudo mysql_secure_installation # هذا الأمر مخصص لفتح واجهة سطر الأوامر لنظام MySQL لننشئ المستخدم وقاعدة البيانات mysql -u root -p بعد تنفيذ الأوامر السابقة ستفتح واجهة سطر الأوامر لنظام MySQL وبواسطتها يمكنك إنشاء قاعدة بيانات تطبيقك ومستخدم قاعدة بيانات مخصص لإدارتها ويملك الصلاحيات الكاملة عليها، ويستحسن أن تلتزم بإنشاء مستخدم قاعدة بيانات خاص للتطبيق فهذا الإجراء يُعدّ أكثر آمانًا. عندما تقرأ الأوامر الموجودة أسفل الفقرة سيتبادر لذهنك أننا ننشئ مستخدمين اثنين على قاعدة البيانات لكن ذلك يرجع لاختلاف طريقة تعامل MySQL مع الاتصالات المحلية localhost والبعيدة التي تجري عبر الشبكة أي عبر عنوان IP، وبالأوامر التي كتبناها هنا قد سمحنا بنوعي الاتصال. وانتبه قبل التنفيذ لضرورة استبدال الأسماء المذكورة بما يناسب تطبيقك كما يلي: استبدل myapp باسم قاعدة بياناتك، وهي تماثل اسم التطبيق عادةً. استبدل omeFancyPassword123 بكلمة مرورك. استبدل deploy باسم مستخدم قاعدة البيانات الذي تختاره. deploy@1.2.3.4 CREATE DATABASE IF NOT EXISTS myapp; CREATE USER IF NOT EXISTS 'deploy'@'localhost' IDENTIFIED BY '$omeFancyPassword123'; CREATE USER IF NOT EXISTS 'deploy'@'%' IDENTIFIED BY '$omeFancyPassword123'; GRANT ALL PRIVILEGES ON myapp.* TO 'deploy'@'localhost'; GRANT ALL PRIVILEGES ON myapp.* TO 'deploy'@'%'; FLUSH PRIVILEGES; \q خامسًا: النشر باستخدام Capistrano بعد إتمام تهيئة الخادم وأدواته حان الوقت لتحميل الشيفرة البرمجية الخاصة بالتطبيق إلى مرحلة الإنتاج. وهنا يأتي دور المكتبة Capistrano التي تساعدك على إنشاء نسخ من مستودع تطبيقك في مرحلة الإنتاج، وعلى إنشاء إصداراته الجديدة لاحقًا بكل سهولة. إعداد Capistrano ثَبِّت Capistrano ضمن تطبيق ريلز على حاسوبك المحلي (أي حيث طورت التطبيق وحيث توجد شيفرته البرمجية). ثم أضف جواهر روبي أو gems التالية إلى الملف Gemfile كما يلي: gem 'capistrano', '~> 3.11' gem 'capistrano-rails', '~> 1.4' gem 'capistrano-passenger', '~> 0.2.0' gem 'capistrano-rbenv', '~> 2.1', '>= 2.1.4' ثم نَفِّذ الأوامر التالية من حاسوبك المحلي بهدف تثبيت gems السابقة وتثبيت ملفات الإعدادات الخاصة بالمكتبة Capistrano: bundle cap install STAGES=production وستنشئ بعدها الملفات التالية: Capfile. config/deploy.rb. config/deploy/production.rb. افتح في البداية الملف Capfile وأضِف إليه الأسطر التالية: require 'capistrano/rails' require 'capistrano/passenger' require 'capistrano/rbenv' set :rbenv_type, :user set :rbenv_ruby, '3.3.1' ثم افتح الملف config/deploy.rb لتُعرِّف تطبيقك ضمنه، وتحصل على التفاصيل الخاصة بمستودع التطبيق: set :application, "myapp" set :repo_url, "git@github.com:username/myapp.git" # Deploy to the user's home directory set :deploy_to, "/home/deploy/#{fetch :application}" append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', '.bundle', 'public/system', 'public/uploads' # Only keep the last 5 releases to save disk space set :keep_releases, 5 # Optionally, you can symlink your database.yml and/or secrets.yml file from the shared directory during deploy # This is useful if you don't want to use ENV variables # append :linked_files, 'config/database.yml', 'config/secrets.yml' وأخيرًا عَدِّل على الملف config/deploy/production.rb لإضافة عنوان IP العام للخادم إلى عمليات النشر، وذلك وفق التالي، ولا تنسَ استبدال 1.2.3.4 بعنوان خادمك: server '1.2.3.4', user: 'deploy', roles: %w{app db web} يتبقى لنا الخطوة الأخيرة قبل النشر وهي إضافة متغيرات البيئة إلى خادم الإنتاج، لذا افتح جلسة اتصال SSH مع الخادم من حاسوبك المحلي: ssh deploy@1.2.3.4 ونفذ التالي: mkdir /home/deploy/myapp nano /home/deploy/myapp/.rbenv-vars ثم أضف متغيرات البيئة المناسبة لحالتك من بين التالي إلى هذا الملف: # For Postgres DATABASE_URL=postgresql://deploy:PASSWORD@127.0.0.1/myapp # For MySQL DATABASE_URL=mysql2://deploy:$omeFancyPassword123@localhost/myapp RAILS_MASTER_KEY=ohai SECRET_KEY_BASE=1234567890 STRIPE_PUBLIC_KEY=x STRIPE_PRIVATE_KEY=y # etc... احفظ التغييرات على الملف، وستُحَمَّل متغيرات البيئة المذكورة هنا تلقائيًا إلى الخادم في كل مرة تُشَغِّل فيها أوامر روبي داخل مجلد التطبيق على الخادم. يفيدك هذا الأسلوب في تخصيص متغيرات بيئة مستقلة لكل تطبيق تنشره على الخادم. يمكنك الآن نشر التطبيق على خادم الإنتاج، بتنفيذ الأمر التالي من حاسوبك المحلي: cap production deploy اكتب عنوان IP الخادم في متصفح الإنترنت وإذا كانت كافة إعداداتك صحيحة فستظهر أمامك واجهة تطبيق ريلز Rails الذي نشرته. وإذا لم تحصل على واجهة ريلز، فيمكنك تتبع الخطأ بمراجعة ملفات تسجيل الأحداث logs من جلسة اتصال SSH مع الخادم كما يلي: deploy@1.2.3.4 # لعرض ملفات تسجيل الأحداث الخاصة بريلز less /home/deploy/myapp/current/log/production.log # لعرض ملفات تسجيل الأحداث الخاصة بخادم NGINX و Passenger sudo less /var/log/nginx/error.log تنجم أغلب الأخطاء عن وجود خلل في أحد متغيرات البيئة أو ملفات الإعدادات التي جهزناها لمرحلة الإنتاج، وبمجرد وصولك للخطأ وتصحيحه يمكنك إعادة تشغيل تطبيقك أو إعادة نشره ثم التحقق مجددًا من المتصفح لمعرفة النتيجة. سادسًا: توصيات إضافية إلى جانب جودة التطبيق الذي طورته بواسطة روبي أو غيرها وصحة نشره على الخادم، فإن متطلبات العمل في البيئة الحقيقية تفرض عليك الاهتمام بجوانب أخرى مثل: تركيب شهادة SSL على الخادم من خدمة مجانية مثل LetsEncrypt أو غيرها لحماية البيانات المتبادلة مع التطبيق، يساعدك في ذلك مقال تنصيب شهادة SSL مجانية عبر خدمة Let's encrypt على خادوم لينكس على أكاديمية حسوب. تفعيل النسخ الاحتياطي الساعي Hourly Backups أو النسخ الاحتياطي الدوري عمومًا الذي ينسخ بياناتك إلى وحدة تخزين خارجية مثل S3، فهو يخفف من مخاطر ضياع البيانات ويساعدك على استعادتها في حال تعرض خادمك أو تطبيقك لأي عطل أو حادث طارئ. اتباع خطة لتدوير ملفات تسجيل الأحداث logs حتى لا تملأ مساحة التخزين على الخادم دون أن تنتبه لها. اتخاذ التدابير الأمنية الضرورية لحماية الخادم من الهجمات السيبرانية، يفيدك في ذلك مقال 7 تدابير أمنية لحماية خواديمك والمقالات الأخرى الموجودة في قسم حماية على أكاديمية حسوب. ترجمة -وبتصرف- لمقال Deploy Ruby On Rails:Ubuntu 24.04 Noble Numbat in 2024 من موقع Go Rails. اقرأ أيضًا كيفية نشر تطبيق Express وتوسيعه باستخدام إضافة MemCachier من منصة تطبيقات DigitalOcean نشر تطبيقات Flask باستخدام PythonAnywhere نشر التطبيقات وتوزيعها وفق نهج التسليم المستمر فيديو: نشر تطبيق React.js ذو واجهات خلفية Node.js على منصة Heroku
-
يعرض المقال الثالث من سلسلة باش كيفية تمرير الوسطاء arguments إلى سكربتات الصدفة باش، وإضافةً للوسطاء سنتعرف على بعض المتغيرات الخاصة في صدفة باش. تعلمنا في المقال السابق كيف نستخدم المتغيرات لكتابة سكربتات باش عامة وديناميكية تتجاوب مع أنواع مختلفة من البيانات ومدخلات المستخدم، وسنتعلم هنا تمرير الوسطاء إلى سكربتات باش ضمن سطر الأوامر. تمرير وسيط إلى سكربت باش يحتسب السكربت count_lines.sh التالي عدد الأسطر الموجودة في أي ملف تزوده باسمه: #!/bin/bash echo -n "Please enter a filename: " read filename nlines=$(wc -l < $filename) echo "There are $nlines lines in $filename" يمكنك الرجوع للمقال الأول لتعرف كيف تُنشئ سكربت باش وتحوله لملف تنفيذي. لنفترض أننا نود حساب عدد الأسطر الموجودة في الملف etc/passwd/ فستكون نتيجة تنفيذ السكربت كما في الصورة التالية: يمكننا تسهيل العملية بتمرير اسم الملف للسكربت بصفته وسيط سطر أوامر يكتب معه في نافذة سطر الأوامر عند التشغيل كما يلي: ./count_lines.sh /etc/passwd يتطلب ذلك تعديلًا على السكربت نستخدم فيه المتغير 1$ الذي يشير إلى الوسيط الأول الذي سيتلقاه السكربت من سطر الأوامر (والذي يسمى المُحَدِّد الموضعي). لنستبدل إذًا المتغير filename الخاص باسم الملف بالمتغير 1$ في كامل السكربت count_lines.sh السابق، ليصبح كما يلي: #!/bin/bash nlines=$(wc -l < $1) echo "There are $nlines lines in $1" استغنينا بهذه الطريقة عن الأمر read والأمر echo الأول، فأصبح السكربت أقصر. يمكنك الآن تجربة السكربت على ملفات أخرى كما في المثال التالي: ./count_lines.sh /etc/group There are 73 lines in /etc/group تمرير عدة وسطاء إلى سكربت باش يمكنك تمرير أكثر من وسيط لسكربتات باش، وفق الصيغة التالية: script.sh arg1 arg2 arg3 … يشير المتغير 2$ للوسيط الثاني، والمتغير 3$ للوسيط الثالث وهكذا. أما المتغير 0$ فيشير إلى اسم السكربت، وهو أحد المتغيرات الخاصة في صدفة باش. لنُعدّل الآن السكربت السابق كما يلي ليقبل أكثر من ملف، ويحسب عدد الأسطر في كل واحد منها: #!/bin/bash n1=$(wc -l < $1) n2=$(wc -l < $2) n3=$(wc -l < $3) echo "There are $n1 lines in $1" echo "There are $n2 lines in $2" echo "There are $n3 lines in $3" يمكنك الآن تشغيل السكربت، وتمرير ثلاثة ملفات نصية له، لتحصل على أعداد الأسطر فيها كما يلي: إذا فقد أعطانا السكربت عدد الأسطر في كل ملف من الملفات الثلاثة، وبالترتيب نفسه المكتوبة به، فترتيب تمرير الوسطاء للسكربت مهم لضمان دقة التنفيذ. أبدِع في استخدام وسطاء باش يساعدك استخدام وسطاء باش على تبسيط أوامر لينكس الطويلة أو المعقدة التي تحتاج لضبط الكثير من الخيارات، إذ يمكنك تحويلها لسكربتات باش بسيطة، وتشغيلها بعد تزويدها بالوسطاء. ألقِ نظرة على سكربت باش find.sh التالي لتوضيح الأمر: #!/bin/bash find / -iname $1 2> /dev/null يساعدك هذا السكربت في العثور على الملفات، وستجد استخدامه أسهل من كتابة أمر البحث الطويل في كل مرة تحتاج فيها لإيجاد ملف، فقط مرر اسم الملف الذي تبحث عنه بصفته وسيطًا للسكربت وسيعرض لك موقعه. وبالطريقة نفسها يمكنك تحويل أي أمر طويل ومعقد من أوامر لينكس إلى سكربت باش سهل الاستخدام. أما التعليمة: 2> /dev/null الواردة في السكربت فتحجب رسائل الخطأ مثل (لا يمكن الوصول للملف أو غيرها) من الظهور على الشاشة. أشهر المتغيرات الخاصة في باش توفر لك صدفة باش مجموعة من المتغيرات الخاصة المُضمنة فيها، وتُعدّ مفيدة جدًا في أي سكربت. يتضمن الجدول أشهرها: المتغير الخاص الوصف $0 اسم السكربت $1, $2…$n وسطاء سطر الأوامر $$ مُعرّف العملية process id للصدفة shell الحالية #$ العدد الكلي للوسطاء المُمَرَّة إلى السكربت @$ قيم جميع الوسطاء المُمَرَّة إلى السكربت ?$ حالة الخروج لآخر أمر مُنَفذ !$ مُعرّف العملية لآخر أمر مُنَفذ يعطيك السكربت Variables.sh التالي مثالًا عمليًّا بسيطًا عن المتغيرات الخاصة: #!/bin/bash echo "Name of the script: $0" echo "Total number of arguments: $#" echo "Values of all the arguments: $@" مرر عدد من الوسطاء التجريبية، ثم شغل السكربت، ولاحظ النتائج: وفي الختام ننصحك بالتدرب أكثر على استخدام وسطاء باش حتى تتقن استخدامها بشكل جيد، وندعوك لمتابعةبقية المقالات في هذه السلسلة للتعرف أكثر على لغة باش واحترافها. ترجمة -وبتصرف- للمقال Passing Arguments to Bash Scripts لصاحبه Kabary. اقرأ أيضًا المقال السابق: المتغيرات في سكربتات الصدفة باش مفهوم واصفات الملفات File Descriptors وارتباطها بعملية التجريد في أنظمة التشغيل أنشئ برنامجك النصي الأول على صدفة باش Bash احترف الأمر ls في لينكس
-
المتغيرات Variables جزءٌ أساسي من أي مشروع برمجي، لا بدّ أنك تعاملت معها من قبل، وإن كنت لا تملك معرفةً مسبقة بها، فيمكنك تشبيهها بحاويات التخزين فهي تُخَزِّن أجزاءً من المعلومات قد تتغير قيمها مع الزمن. ويتناول مقالنا الثاني من سلسلة باش Bash للمبتدئين طريقة استخدام المتغيرات في كتابة سكربتات الصدفة باش bash shell scripts، تابع معنا أمثلة المقال لتتعلم استعمال المتغيرات لكتابة سكربتات باش. استخدام المتغيرات في سكربتات الصدفة باش تعلمنا في المقال السابق كيفية إنشاء سكربت باش بسيط يعرض عبارة " hello world"، اسمه "hello.sh" ويتضمن المحتوى التالي: #! /bin/bash echo 'Hello, World!' لنحاول تطوير هذا السكربت البسيط ليتظهر اسم المستخدم في عبارة الترحيب، سنستخدم لذلك المتغيرات والأمر read القادر على استلام المدخلات من المستخدمين، يمكنك مطالعة هذا المقال للحصول على معلومات أكثر عن read، والآن افتح الملف "hello.sh" وعدّل محتواه ليصبح كما يلي: #! /bin/bash echo "What's your name, stranger?" read name echo "Hello, $name" إذا شغلت السكربت سيُطالبك بإدخال اسمك ثم سيرحب بك بالاسم الذي زودته به، وفق التالي: abhishek@handbook:~/scripts$ ./hello.sh What's your name, stranger? Elliot Hello, Elliot إذًا سيسألك البرنامج عن اسمك، ثم تزوده به، وبعدها سيظهر اسمك في عبارة الترحيب. وهذا ملخص لما نفذناه حتى الآن: الشرح التفصيلي للسكربت السابق لنراجع نص السكربت سطرًا سطرًا مع توضيح دلالة كل سطر. يتضمن السطر الأول رمز التوجيه shebang ليوضح لمُفسر الأوامر أننا سنستخدم الصدفة باش لتنفيذ السكربت. #!/bin/bash وفي السطر الثاني أظهرنا العبارة التالية التي تطلب من المستخدم إدخال اسمه: echo "What's your name, stranger?" ونفذنا ذلك باستخدام أمر بسيط هو echo الذي يعرض العبارة المكتوبة بعده على شاشة الطرفية. ثم السطر الثالث، وهو الجزء الأهم الذي أحدث تطورًا في السكربت: read name استخدمنا فيه الأمر read لنقل التحكم من السكربت إلى المستخدم ليستطيع إدخال اسمه وتخزينه في متغير يدعى name. وفي السطر الأخير يستخدم السكربت المتغير name ويرحب بالمستخدم باسمه: echo “Hello, $name” تُكتب إشارة "$" قبل اسم المتغير للحصول على القيمة المخزنة فيه، أو لو كتبت name فقط في السطر السابق بدلًا من name$ فتظهر العبارة "Hello, name" عوضًا عن الترحيب بالمستخدم باسمه (الذي هو قيمة المتغير). ملاحظة: تعرف إشارة "$" في سكربتات باش بأنها معامل تحصيل dereference operator لتحصيل قيمة معينة من موقعها في الذاكرة. إنشاء متغيرات بأنواع بيانات مختلفة يمكن إنشاءمتغيرات بأنواع بيانات مختلفة مثل الأعداد والمحارف والسلاسل النصية، وتستخدم إشارة المساواة "=" لإنشاء المتغيرات وإسناد القيم الافتراضية لها، ففي السطر التالي مثلًا أنشأنا متغيرًا يدعى age وأسندنا له القيمة 27. age=27 يمكنك تغيير قيمة المتغير لاحقًا بقدر ما تريد، فالسطر التالي مثلًا يغيرها من 27 إلى 3: age=3 تحمل المتغيرات أنواعًا مختلفة من البيانات، مثل الأعداد الصحيحة، والمحارف، والسلاسل النصية، وباش لا تحدد الأنواع بشكل صارم كما في بعض لغات البرمجة الأخرى، فلا حاجة لتحديد نوع المتغير مسبقًا ويمكنك وضع أي نوع ضمن المتغير. ألقِ نظرة على الأمثلة التالية: letter=’c’ color=’blue’ year=2020 المتغيرات الثابتة في سكربت باش المتغير الثابت constant variable هو متغير ذو قيمة ثابتة لا تتغير أبدًا، تُنشِئه بواسطة الأمر readonly كما يلي: readonly PI=3.14159 أنشأ الأمر السابق متغيرًا ثابتًا يدعى PI يحمل القيمة 3.14159، وإذا حاولت تغيير قيمته فلن تتمكن من ذلك وستحصل على الخطأ التالي: bash: PI: readonly variable إذًا المتغيرات الثابتة هي متغيرات للقراءة فقط، يمكنك قراءة قيمتها فقط، ولا تستطيع تغييرها أبدًا بعد إنشائها. تعويض الأوامر Command substitutions تعويض الأوامر Command substitutions هو تخزين نتيجة أمر في متغير، ويُعدّ من أبرز مميزات البرمجة النصية باستخدام صدفة باش. والأمر date الخاص بإظهار التاريخ الحالي من أشهر الأمثلة على تعويض الأوامر، ألقِ نظرة على السطر التالي: TODAY=$(date) حسب السطر السابق يُخَزَّن خرج الأمر date في المتغير TODAY، ولاحظ أن الأمر المستخدم لتعويض الأوامر يُكتب بين قوسين هلاليين ويُسبَق بإشارة الدولار "$" على يساره. تبين الصورة أدناه كيف أخذ المتغير TODAY قيمة خرج الأمر date: يمكنك أيضًا كتابة تعويض الأوامر بوضع الأمر بين علامتي اقتباس مائلة للخلف back quotes وفق التالي، بدلًا من وضعه بين قوسين مع إشارة الدولار: TODAY=`date` لكننا ننصحك بعدم استخدام هذه الطريقة في كتابة تعويض الأوامر لأنها قديمة ولم تعد مستخدمة، احرص دائمًا على استخدام الطريقة الحديثة ذات الصيغة التالية: variable=$(command) مثال عملي على تعويض الأوامر في آخر تحديث أجريناه على السكربت "hello.sh" كنا نطلب من المستخدم إدخال اسمه ليستخدمه البرنامج في رسالة الترحيب. أما الآن -وبالاعتماد على تعويض الأوامر- لن نسأل المستخدم عن اسمه بل سنستعين بأمر خاص يدعى whoami يعطينا اسم المستخدم الحالي. عدّل محتوى الملف "hello.sh" ليصبح كما يلي: #! /bin/bash echo "Hello, $(whoami)" لاحظ الاختصار الذي طرأ على حجم السكربت فقد أصبح يقتصر على سطرين فقط، شغّل السكربت الآن: ./hello.sh سيؤكد لك الخرج نجاح العملية، فسيظهر اسم المستخدم الحالي في رسالة الترحيب، وتلخص هذه الصورة ما طبقناه هنا: وصلنا إلى نهاية المقال نأمل أنه كان مفيدًا ووضح لك طريقة استخدام المتغيرات في سكربتات باش، حاول تطبيق أمثلة أخرى لتزيد مهاراتك في العمل مع المتغيرات، وتابع مقالنا التالي لتتعرف على كيفية تمرير الوسطاء لسكربت باش. ترجمة -وبتصرف- للمقال Understanding Variables in Bash Shell Scripting. اقرأ أيضًا المقال السابق: أنشئ برنامجك النصي الأول على صدفة باش Bash مدخل إلى صدفة باش Bash دليل ميَسَّر لكتابة سكربتات Shell مدخل إلى كتابة سكربتات الصدفة الحصول على مدخلات من لوحة المفاتيح وإجراء العمليات الحسابية في سكربتات الصدفة (Shell Scripts)
-
تعلمنا في المقالات السابقة من سلسلة Pygame التي تشرح طريقة بناء لعبة من الصفر بلغة بايثون3 ووحدة الألعاب Pygame، وكيف نضيف إليها الشخصيات سواء شخصيات الأبطال أو أعداء، ونحركهم بالقفز والركض ورمي المقذوفات مثل الكرات النارية وغيرها، وسنعرض في هذا المقال المتمم للسلسلة طريقة إضافة مؤثرات صوتية تناسب أحداث اللعبة تُشَغَّل في أثناء القتال أو القفز أو جمع الجوائز أو غير ذلك، لكن دعنا في البداية نذكرك بمقالات السلسلة بالترتيب: بناء لعبة نرد بسيطة بلغة بايثون. بناء لعبة رسومية باستخدام بايثون ووحدة الألعاب PyGame. إضافة لاعب إلى اللعبة المطورة باستخدام بايثون و Pygame. تحريك شخصية اللعبة باستخدام PyGame. إضافة شخصية العدو للعبة. إضافة المنصات إلى لعبة بايثون باستخدام الوحدة Pygame محاكاة أثر الجاذبية في لعبة بايثون. إضافة خاصية القفز والركض إلى لعبة بايثون. إضافة الجوائز إلى اللعبة المطورة بلغة بايثون تسجيل نتائج اللعبة المطورة بلغة بايثون وعرضها على الشاشة. إضافة آليات القذف إلى اللعبة المطورة بلغة بايثون. إضافة آليات القذف إلى اللعبة المطورة بلغة بايثون. إضافة المؤثرات الصوتية إلى اللعبة المطورة بلغة بايثون ومكتبة Pygame. توفر المكتبة Pygame طريقةً سهلة لإضافة المؤثرات الصوتية إلى ألعاب الفيديو المطورة بلغة بايثون، وذلك اعتمادًا على وحدة خاصة تسمى mixer module تتيح لك تشغيل صوت واحد أو أكثر حسب طلبك، فيمكنك مثلًا تشغيل موسيقى خلفية background music بالتزامن مع صوت بطل اللعبة وهو يقاتل أو يقفز أو يجمع الجوائز. لن يتضمن هذا المقال تعديلات مباشرةً على الشيفرة البرمجية للعبة التي عملنا عليها خلال السلسلة (كما في فعلنا المقالات السابقة) لكننا سنقدم لك أمثلة للتعليمات المتعلقة بالوحدة mixer وسنشرح لك بالتفصيل كيفية الاستفادة منها ودمجها في لعبتك بخطوات متسلسلة. تشغيل الوحدة mixer اكتب في البداية التعليمة الخاصة بتشغيل الوحدة mixer في قسم الإعدادات setup ضمن شيفرة اللعبة، طبعًا يمكنك جمعها مع في كتلة واحدة مع التعليمات المشابهة لها مثل تعليمة تشغيل pygame وتشغيل pygame.font لتصبح كما يلي: pygame.init() pygame.font.init() pygame.mixer.init() # أضف هذا السطر ملاحظة: يمكنك الحصول على شيفرة اللعبة بشكلها النهائي من المقال الثاني عشر من السلسلة إضافة آليات القذف إلى اللعبة المطورة بلغة بايثون. الحصول على الملفات الصوتية اللعبة خطوتك التالية هي تحديد الأصوات التي تود استخدامها في اللعبة وتوفيرها محليًّا على حاسوبك، فاستخدام الأصوات في اللعبة المطورة بلغة بايثون يتطلب وجودها كملفات على الحاسوب المحلي تمامًا مثل الخطوط والرسوم. إذًا بعد تأمين ملفات الصوت عليك وضعها في حزمة واحدة مع ملفات اللعبة حتى يحصل عليها كل من يلعب بلعبتك. لنبدأ بإنشاء مجلد خاص لحفظ ملفات الصوت ضمن المجلد الرئيسي للعبة إلى جانب مجلدي الصور والخطوط، ولنسميه sound كما يلي: s = 'sound' يتوفر العديد من الملفات الصوتية على الإنترنت لكن قد لا يسمح لك باستخدامها جميعًا بسبب حقوق الملكية، لذا ابحث عن الملفات الصوتية مفتوحة المصدر أو المنشورة تحت رخصة المشاع الإبداعي Creative Commons واستخدمها في لعبتك، وهذه بعض المصادر التي تتيح لك تحميل ملفات الصوت مجانًا وبطريقة قانونية: يحتوي Freesound على ملفات لمختلف أنواع المؤثرات الصوتية. يستضيف موقع Incompetech مجموعة واسعة من الموسيقى المناسبة لتكون موسيقى خلفية للألعاب. يوفر Open Game Art ملفاتٍ متنوعة من المؤثرات الصوتية والموسيقى. لكن احرص دائمًا على قراءة شروط الاستخدام قبل تحميل أي ملف صوتي مجاني واعتماده في لعبتك، إذ يشترط بعض المؤلفون الموسيقيون ومصممو الصوت أن تُنسب الملفات الصوتية إليهم ويذكر أنهم أصحاب الفضل في إنشائها عندما يستخدمها الآخرون مجانًا، وبكل الأحوال يُعدّ ذكر اسم صاحب الملف الصوتي تصرفًا جيدًا وأخلاقيًا لمطوري الألعاب، فهم في نهاية الأمر قد تعبوا على ملفاتهم الصوتية بالطريقة نفسها التي تعبت فيها لتطوير لعبة أو تطبيقك. فأين يُذكر اسم صاحب الملف الصوتي إذًا؟ يُنشأ عادةً ملفٌ نصيٌّ خاص في مجلد اللعبة الرئيسي يسمى CREDIT وتُكتب فيه الملفات الصوتية المستخدمة في اللعبة مع مصادرها. قد يرغب البعض بتأليف مؤثراتهم الصوتية الخاصة، فإذا كنت منهم يمكنك استخدام أدوات Linux Multimedia Studio، أو LMMS ، فهو برنامج مجاني ومفتوح المصدر يساعد على لإنتاج وتحرير الموسيقى وتوليد التأثيرات الصوتية، كما أنه سهل الاستخدام ومتوافق مع معظم المنصات الأساسية، ويوفر لك العديد من الأصوات لتبدأ منها، فضلًا عن أنه يسمح لك بتصدير الملفات الصوتية بتنسيق Ogg Vorbis مفتوح المصدر الذي يسمى اختصارًا OGG. يمكنك معرفة المزيد عن المشاريع والبرمجيات مفتوحة المصدر بمشاهدة هذا الفيديو: إضافة الملفات الصوتية إلى Pygame الآن بعد أن وجدت المؤثرات الصوتية المناسبة للعبتك ستُحَمِّلها غالبًا بصيغة ملفات مضغوطة tar أو zip لذا أول ما سنفعله هو فك ضغطها، ونقل الملفات الصوتية الناتجة إلى المجلد sound الموجود ضمن مجلد اللعبة الرئيسي. انظر بعدها إلى أسماء الملفات الصوتية فإذا وجدتها معقدة أو تتضمن العديد من المحارف الخاصة، أَعِدّْ تسميتها واختر لها أسماءً بسيطة يمكنك استخدامها بسهولة ضمن الشيفرة البرمجية. تعتمد معظم ألعاب الفيديو ملفاتٍ صوتية بصيغة OGG لأنها تجمع بين الجودة العالية وصِغَر حجم الملف، فإذا كانت الملفات الصوتية التي اخترتها للعبتك بصيغة MP3 أو WAVE أو FLAC أو غيرها، احرص على تحويلها إلى صيغة OGG باستعمال أدوات مثل fre:ac و Miro لتضمن توافقيةً أعلى وحجمًا أصغر عند تحميل اللعبة. لنفترض على سبيل المثال أن الملف الصوتي الذي حَمَّلته يدعى ouch.ogg. سننشئ متغيرًا خاصًا لتمثيله في قسم الإعدادات setup ضمن شيفرة اللعبة، ليكن مثلًا المتغير ouch كما يلي: ouch = pygame.mixer.Sound(os.path.join(s, 'ouch.ogg')) تشغيل الأصوات ضمن اللعبة الآن كل ما عليك فعله لتشغيل الصوت ضمن اللعبة هو استدعاء المتغير السابق عندما تحتاجه، فعلى سبيل المثال إذا رغبت بتشغيل الصوت OUCH عندما يصطدم بطلك بأحد الأعداء فستكتب الحلقة التالية: for enemy in enemy_hit_list: pygame.mixer.Sound.play(ouch) score -= 1 وبالطريقة نفسها يمكنك إنشاء مؤثرات صوتية لمختلف أنواع الأحداث في اللعبة، مثل: القفز، وجمع الجوائز، ورمي المقذوفات، والاصطدام بالأشياء… إلخ. إضافة موسيقى خلفية للعبة تساعدك الدالة music (إحدى دوال الوحدة mixer في Pygame) على تشغيل موسيقى أو مؤثرات جوية مثل صوت هواء أو غيره في خلفية background اللعبة، وذلك بخطوتين: أولًا تحميل الملف الصوتي بكتابة الأمر التالي في قسم الإعدادات setup من شيفرة اللعبة: music = pygame.mixer.music.load(os.path.join(s, 'music.ogg')) ثم تشغيل الدالة music كما يلي: pygame.mixer.music.play(-1) تعني القيمة 1- أن الدالة ستعمل إلى ما لا نهاية من دون توقف وهذه سمة الموسيقى الخلفية، لكن يمكنك استخدام أي عدد آخر بدءًا من 0 وما فوق لتحديد عدد المرات التي ستعمل فيها الدالة music قبل أن تتوقف. طوّر اللعبة ولا تتوقف هنا لا تتوقف عند ما تعلمناه في هذه السلسلة عن Pygame جرب إضافة المزيد من الأصوات والمراحل والمؤثرات الحركية إلى لعبتك، تعلَّم المزيد عنها فهي تضفي النكهة على لعبتك وتساهم في جعلها مفضلة لدى المستخدمين. ترجمة -وبتصرف- لمقال Add sound to your Python game لصاحبه Seth Kenlon. اقرأ أيضًا المقال السابق: إضافة آليات القذف إلى اللعبة المطورة بلغة بايثون مطور الألعاب: من هو وما هي مهامه تعرف على أشهر لغات برمجة الألعاب الأدوات المستخدمة في بناء الواجهات الرسومية في بايثون البرمجة باستخدام لغة بايثون في تطبيقات راسبيري باي تعرف على مجالات وتطبيقات لغة بايثون
-
كتابة سكربتات باش Bash Scripting مهارة أساسية لا غنى عنها لكل مدير نظام ومهندس DevOps، فإذا كنت ترغب بتطوير مهاراتك في هذا المجال سواءً كنت مبتدئًا أو متمرسًا في نظام لينكس فإن هذه السلسلة التي تشرح Bash للمبتدئين ستلبي طلبك، بما تقدمه من أمثلة وتطبيقات عملية على أساسيات باش بدايةً من إنشاء السكريبت وحتى أتمتة تنفيذه على الخادم، وتتضمن هذه السلسلة المقالات التالية: أنشئ برنامجك النصي الأول على صدفة باش Bash. المتغيرات في سكربتات الصدفة باش Bash. تمرير الوسطاء إلى سكريبت باش Bash. استخدام المصفوفات في باش Bash. استخدام المعاملات الحسابية في سكربتات باش Bash. عمليات السلاسل النصية في باش Bash. الجمل الشرطية If و Else و Case في باش Bash. الحلقات في باش Bash. استخدام الدوال في باش Bash. أتمتة المهام باستخدام باش Bash. هذا هو المقال الأول في هذه السلسلة، إذ ستتعرف فيه على كيفية إنشاء سكريبت باش bash script يفيدك في أتمتة المهام الروتينية على الخادم، إذ كثيرًا ما نجد أنفسنا ننفذ المهام نفسها مرارًا وتكرارًا، بداية من النسخ الاحتياطي للمجلدات، وتنظيف الملفات المؤقتة، وحتى استنساخ cloning قواعد البيانات. سننشئ معًا سكريبت باش بسيط ونقوم بتشغيله، ونستعرض بعض الأساسيات التي ينبغي لك معرفتها عن كتابة سكربتات الصدفة Shell عمومًا. إنشاء سكريبت Shell وتشغيله أنشئ في البداية مجلدًا جديدًا اسمه "scripts" سنُخَزِّن فيه جميع السكربتات التي سننشئها في أثناء تطبيق أمثلة المقال، ثم انتقل للعمل ضمنه، بكتابة التالي: mkdir scripts cd scripts أنشئ ضمن المجلد السابق ملفًا نصيًّا باسم "hello.sh" باستخدام الأمر cat وفق التالي، أو أنشئه بأي طريقة أخرى تفضلها: cat > hello.sh يمكنك الآن الكتابة ضمن الملف من الطرفية terminal مباشرةً فاكتب السطر التالي: echo 'Hello, World!' ثم اضغط على Ctrl+D لحفظ التغييرات على الملف، والخروج من الأمر cat. تستطيع الكتابة ضمن الملف بالطريقة التي تناسبك باستخدام محررات النصوص العاملة في الطرفية مثلًا وأبرزها Vim و Emacs و Nano، أو محررات النصوص ذات الواجهة الرسومية نحو Gedit إذا كنت تستخدم إحدى بيئات سطح المكتب لنظام لينكس. يعرض الأمر echo العبارة "Hello World" المكتوبة بعده على الشاشة، وهدفنا هنا تشغيل echo على أنه سكريبت shell بدلًا من تشغيله بالطريقة العادية أي بكتابته ضمن الطرفية. بعد إنشاء الملف "hello.sh" سنحوله إلى ملف تنفيذي باستخدام الأمر chmod، كما يلي: chmod u+x hello.sh يمكنك معرفة المزيد عن chmod وغيره من أوامر لينكس الشهيرة بمطالعة المقال مرجع إلى أشهر أوامر لينكس. والآن لنشغّل السكريبت بكتابة الأمر "bash" قبل اسم الملف "hello.sh"، وفق التالي: bash hello.sh ستظهر العبارة !Hello, World أمامك على الشاشة مشيرةً لنجاح تنفيذ السكربت. ألقِ نظرة على الصورة أدناه فهي تتضمن ملخصًا للأوامر التي نفذناها حتى الآن. تحويل سكريبت Shell إلى سكريبت Bash يخلط البعض بين shell و bash، وهما مرتبطان بالفعل، لكن Shell أعَمّ من باش. فكلمة باش Bash اختصار للعبارة الإنجليزية "Bourne-Again shell"، وهي واحدة من أشهر أنواع الصدفات Shells المتاحة في لينكس. أما الصدفة shell فهي مُفَسِّر interpreter لسطر الأوامر يستقبل الأوامر المدخلة من المستخدم ويُشغلها، وله عدة أنواع. فأنت إذًا تستخدم الصدفة shell في كل مرة تكتب فيها أوامر لينكس، وعندما تفتح الطرفية على حاسوبك فأنت فعليَّا تشغل الصدفة الافتراضية لنظام لينكس الذي تستعمله. وباش هو الصدفة الافتراضية لمعظم توزيعات لينكس، لذا يستخدم في معظم الأحيان مرادفًا للصدفة shell. يوجد تشابه كبير في قواعد كتابة السكربتات بين أنواع الصدفات المختلفة، ولكنها مع ذلك تتباين في بعض الأحيان، فعلى سبيل المثال تبدأ فهرسة المصفوفات من "1" في صدفة Zsh بينما تبدأ من "0" في صدفة باش، وبالتالي فأي سكريبت مكتوب لصدفة Zsh ويتضمن مصفوفات، لن يعمل بطريقة صحيحة في صدفة باش. وهنا يأتي دور شيبانج shebang وهو السطر الذي تبدأ به كل سكربتات باش، فهو يوضح للمُفَسِّر أن السكريبت مكتوب للصدفة باش وليس لغيرها. السطر Shebang في بداية كل سكريبت يُقصد بسطر shebang العبارة bin/bash/ !# التي تكتب في السطر الأول من كل سكريبت باش، ويدعوه البعض hashbang لأنه يبدأ بالمحرفين هاش "#" hash وبان "!" ban. لاحظ كيف سيبدو السكريبت الذي أنشأناه قبل قليل بعد إضافة هذا السطر: #! /bin/bash echo 'Hello, World!' إذًا يخبر السطر bin/bash/ !# نظام التشغيل بنوع الصدفة أو المُفَسِّر الذي تود أن تستخدمه لتشغيل السكربت، فبعد إضافة هذا السطر إلى ملفنا السابق "hello.sh" سيعمل مباشرة بواسطة باش دون الحاجة لكتابة كلمة "bash'' قبل اسم الملف عند استدعائه كما فعلنا سابقًا. انظر الصورة أدناه: تشغيل السكريبت من أي مجلد بإضافة مساره للمتغير PATH إذا دققت في الصورة السابقة ستجد أننا استخدمنا البادئة /. قبل اسم السكريبت المُراد تشغيله للدلالة على مساره (فهو موجود في مجلد العمل الحالي)، وفي حال حذفت البادئة فستحصل على خطأ مثل التالي: abhishek@handbook:~/scripts$ hello.sh hello.sh: command not found فقد بَدَا اسم الملف للصدفة باش على أنه أمر برمجي يدعى hello.sh، وبدأ باش يبحث عن مساره بين المسارات المحددة في المتغير PATH، فمسارات حفظ جميع الأوامر التي تُشغلها الصدفات تخزن في هذا المتغير. يمكنك استعراض محتويات المتغير PATH باستخدام الأمر echo وفق ما يلي: echo $PATH /home/user/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin تفصل النقطتان الرأسيتان ":" بين المسارات الموجودة ضمن PATH والتي تفحصها صدفات shell عند تنفيذ أي أمر. يمكنك تشغيل أوامر لينكس مثل: echo و cat وغيرها من أي مجلد على الخادم لأن أماكن وجود ملفاتها التنفيذية معروفة للنظام فهي مُخَزَّنِة ضمن مجلدات bin، وجميع مجلدات bin مذكورة في المتغير PATH كما رأينا في خرج التعليمة السابقة، والمسارات الموجودة في PATH هي الأماكن التي يبحث فيها النظام عن الملف التنفيذي لأي أمر تطلب تشغيله. خلاصة القول إذا أردت تشغيل سكريبت باش الخاص بك من أي مجلد في نظام التشغيل كما لو أنه أمرٌ أساسي من أوامر النظام فأضف مسار وجوده إلى المتغير PATH، وفق الخطوات التالية. أولًا حدد مسار حفظ السكريبت بدقة، يمكنك استخدام الأمر pwd إذا كان السكريبت موجود في مجلد عملك الحالي: pwd بعد أن تحصل على المسار (وهو المجلد "scripts" في مثالنا)، استخدم الأمر export لإضافته إلى المتغير PATH كما يلي: export PATH=$PATH:/home/user/scripts ملاحظة: بعد إضافة المجلد scripts إلى نهاية متغير PATH، فهذا يعني أن النظام سيبحث في المجلدات القياسية أولاً قبل أن يبحث في المجلد scripts. إذا كان لديك نسخ من نفس البرنامج في عدة دلائل، فإن هذا الترتيب يضمن أن النسخة الموجودة في المجلدات القياسية، بمعنى سيفحص نظام التشغيل المجلدات التي تتواجد مساراتها في المتغير PATH بالترتيب، وبالتالي فهو يبحث أولًا في المجلدات القياسية لحفظ السكربتات، ثم يأتي إلى المجلدات المخصصة التي أضافها المستخدم. شغّل الآن السكريبت بكتابة اسمه مباشرةً في الطرفية مثل أي أمر من أوامر لينكس العادية ولاحظ النتيجة، وفق التالي: abhishek@handbook:~/scripts$ hello.sh Hello, World! وهذا ملخص للأوامر التي نفذناها هنا: تهانينا، لقد أنجزت السكريبت الأول لك في باش، تابع معنا بقية مقالات السلسلة لنتعلم أكثر عن متغيرات الصدفة، ونجرب معًا أمثلة متنوعة أخرى عن باش bash وطريقة التعامل معها بكفاءة. ترجمة -وبتصرف- للمقال Create and Run Your First Bash Shell Script. اقرأ أيضًا مدخل إلى صدفة باش Bash دليل ميَسَّر لكتابة سكربتات Shell الأخطاء الشائعة التي تحدث عند كتابة سكربتات الصدفة (Shell Scripts) تعديل سكربتات الصدفة (Shell Scripts) الموجودة على حاسوبك
-
كوبرنيتس Kubernetes هو نظام تنسيق حاويات شهير ومفتوح المصدر، يستخدم لأتمتة نشر البرامج وتوسيعها وإدارتها، ويزداد الاعتماد عليه في الشركات والمؤسسات يومًا بعد يوم لتسهيل عمليات التوسعة الأفقية horizontal scaling لموارد الخادم، ويُقصد بها إضافة المزيد من الخوادم حسب الحاجة لزيادة الموارد المتاحة لتطبيقك، مثل: الحجوم التخزينية وقدرة المعالجة وغيرها. يمكنك الحصول على حلول Kubernetes السحابية من مزودي خدمات سحابية متعددين ولكل خدمة مميزات خاصة في الإدارة وغيرها. يتطلب العمل مع كوبرنيتس Kubernetes استخدامًا مكثفًا للموارد الحاسوبية، لأنه يعمل في نظام عنقودي يتكون من عدة خوادم، ويُشكل ذلك عبئًا إضافيًا على المطورين وخصوصًا في مرحلة ما قبل الإنتاج، فسيحتاجون الكثير من الموارد لتطوير مكدس Kubernetes تجريبي واختباره قبل النشر في البيئة الفعلية، لذا أنشأ مطورو Kubernetes مشروعًا مساعدًا مخصص لهذا الغرض يدعى minikube، الذي يعمل مع بيئات تشغيل الحاويات مثل دوكر Docker وغيره، ويستطيع محاكاة عنقود Kubernetes كامل على آلة واحدة فقط هي حاسوبك الشخصي مثلًا. فما هو minikube المحاكي الشهير لنظام Kubernetes؟ وكيف يستخدم لاختبار إعدادات Kubernetes قبل نشرها في بيئة الإنتاج؟ وما هي لوحة معلومات Kubernetes المضمنة فيه؟ سيجيبك المقال عن هذه الأسئلة، ويعطيك طريقة تثبيته على حاسوبك المحلي أو خادمك البعيد، ثم سنعمل معًا على نشر تطبيق تجريبي بسيط، ونحاول الوصول إليه عبر minikube، وفي الختام سنتعرف على طريقة استخدام Minikube مع عناقيد Kubernetes البعيدة بواسطة ملفات تعريف الإعدادات configuration profiles. متطلبات العمل ستحتاج المتطلبات الأولية التالية لتطبيق خطوات العمل المذكورة في المقال: فهم أساسيات Kubernetes، تفيدك مطالعة مقال تعرّف على كوبرنيتس Kubernetes لتكوين فكرة جيدة عن أبرز مفاهيم Kubernetes ومكوناته. تثبيت بيئة تشغيل الحاويات Docker على حاسوبك الذي ستعمل منه، إذ سنُشغل منها minikube. إذا كنت تستخدم نظام تشغيل لينكس، فستساعدك الخطوات الواردة في مقال كيفية تثبيت دوكر واستخدامه على دبيان، ولسهولة العمل احرص على تنفيذ الخطوة المتعلقة بضبط الإعدادات اللازمة لتشغيل Docker بدون الحاجة لكتابة sudo في بداية كل أمر. أما إذا كنت تعتمد نظام تشغيل ويندوز أو ماك فيمكنك الاستعانة بتوثيقات Docker الرسمية لإتمام عملية التثبيت. مدير الحزم Homebrew، يمكنك الاسترشاد بالخطوات الواردة في هذا المقال على DigitalOcean لتثبيته على نظام تشغيل ماك، أو بمقال لتثبيته على لينكس، وفي حال كنت تستخدم نظام ويندوز فتستطيع تثبيته باستخدام WSL نظام ويندوز الفرعي لنظام لينكس . توفير الموارد الحاسوبية اللازمة للبيئة التي ستُثَبِّت فيها Minikube، وهي بالحد الأدنى: وحدتي معالجة مركزية 2CPUs، وذاكرة مخبئية 2GB RAM، ومساحة تخزينية على القرص الصلب بسعة 20GB. الخطوة 1: تثبيت Minikube وتشغيله ثبّت minikube بواسطة مدير الحزم Homebrew كما يلي: $ brew install minikube وستحصل على خرج يشبه التالي، يبين لك نجاح التثبيت: … ==> Installing minikube ==> Pouring minikube--1.25.2.x86_64_linux.bottle.tar.gz ==> Caveats Bash completion has been installed to: /home/sammy/.linuxbrew/etc/bash_completion.d ==> Summary 🍺 /home/sammy/.linuxbrew/Cellar/minikube/1.25.2: 9 files, 70.0MB … ملاحظة: يتطلب تثبيت minikube على نظام ويندوز الانتباه لبعض التفاصيل المهمة: يعمل minikube مع WSL2 (وهي النسخة المتوفرة من WSL لتاريخ نشر المقال)، وينبغي تهيئته ليستخدم Docker واجهةً خلفية backend بدلًا من واجهته الخلفية الافتراضية. لذا بعد تثبيت Docker احرص على تفعيل ميزة دعم WSL2 باتباع إرشادات توثيقات Docker الخاصة بالموضوع ثم ثبت minikube ونفذ الأمر minikube config set driver docker. اكتب الآن الأمر start وفق التالي لبدء تشغيل minikube، وسينشأ بداخله آليًّا عنقود Kubernetes محلي بأحدث إصدار مستقر متوفر، ويتضمن عدة حاويات Docker: $ minikube start سيتطلب التشغيل بعض الوقت، وستحصل في نهايته على الخرج التالي، مع تجهيز الأداة kubectl لتستخدمها للاتصال مع العنقود cluster، كما يوضح السطر الأخير من الخرج: 👍 Starting control plane node minikube in cluster minikube 🚜 Pulling base image ... 💾 Downloading Kubernetes v1.23.1 preload ... > preloaded-images-k8s-v16-v1...: 504.42 MiB / 504.42 MiB 100.00% 81.31 Mi > gcr.io/k8s-minikube/kicbase: 378.98 MiB / 378.98 MiB 100.00% 31.21 MiB p 🔥 Creating docker container (CPUs=2, Memory=1987MB) ... 🐳 Preparing Kubernetes v1.23.1 on Docker 20.10.12 ... ▪ kubelet.housekeeping-interval=5m ▪ Generating certificates and keys ... ▪ Booting up control plane ... ▪ Configuring RBAC rules ... 🔎 Verifying Kubernetes components... ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5 🌟 Enabled addons: default-storageclass, storage-provisioner 🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default ملاحظة: يمكنك اختيار إصدار Kubernetes الذي يناسبك لأسباب تتعلق بالتوافقية أو غيرها، بدلًا من الاعتماد على الإصدار الافتراضي الذي يوفره minikube، وذلك بكتابة رقم الإصدار المطلوب بعد الأمر minikube start بهذا الشكل kubernetes-version v.1.2.3--. يسمح لك تثبيت minikube بواسطة مدير الحزم Homebrew بالعمل مباشرةً مع kubectl الأداة الأساسية لإدارة عناقيد Kubernetes باستخدام سطر الأوامر، وبالتالي يمكنك كتابة الأمر kubectl get كما يلي لاستعراض جميع pods العاملة في العنقود بالطريقة نفسها المتبعة مع عناقيد Kubernetes العادية: $ kubectl get pods -A يعرض الوسيط A- كافة pods العاملة في جميع مساحات الأسماء namespaces الموجودة ضمن العنقود، ألقِ نظرة على شكل الخرج للأمر السابق: NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-64897985d-ttwl9 1/1 Running 0 46s kube-system etcd-minikube 1/1 Running 0 57s kube-system kube-apiserver-minikube 1/1 Running 0 61s kube-system kube-controller-manager-minikube 1/1 Running 0 57s kube-system kube-proxy-ddtgd 1/1 Running 0 46s kube-system kube-scheduler-minikube 1/1 Running 0 57s kube-system storage-provisioner 1/1 Running 1 (14s ago) 54s لديك الآن عنقود Kubernetes محلي، تستطيع إدارته باستخدام أدوات Kubernetes المألوفة مثل kubectl، وسنعرض في الفقرات القادمة وظائف إضافية يوفرها لك minikube لمراقبة عناقيد Kubernetes وإدارتها والتعديل عليها. الخطوة 2: الوصول إلى لوحة معلومات Kubernetes يوفر minikube لمستخدميه وصولًا سهلًا للوحة معلومات النظام Kubernetes Dashboard، التي يمكنك استخدامها لمراقبة سلامة العنقود ولنشر التطبيقات يدويًا ولغيرها من أعمال الإدارة. وفور تثبيت minikube محليًا على حاسوبك تستطيع الوصول إلى لوحة معلومات Kubernetes بكتابة الأمر minikube dashboard: $ minikube dashboard سيُشغّل هذا الأمر لوحة المعلومات آليًّا، ويفتح منفذًا port خاصًا داخل Kubernets يوجه حركة البيانات إلى العنقود، ثم يعرض رابط اللوحة الذي يشير إلى رقم المنفذ مباشرةً أمامك في متصفح الويب، كما في الصورة التالية. يؤدي تشغيل لوحة المعلومات إلى تعطيل النافذة الطرفية terminal التي كَتَبّتَ أمر التشغيل فيها، فلا يمكنك كتابة أوامر أخرى ضمنها، لذا يلجأ المستخدمون إلى تشغيل لوحة المعلومات في نافذة طرفية أخرى غير التي يعملون عليها. يمكنك إيقاف هذه العملية المُعطِّلة وغيرها من العمليات المشابهة بالضغط على Ctrl+C. أما إذا كنت تستخدم minikube على خادم بعيد، فأضِف الوسيط url-- إلى الأمر minikube dashboard السابق، وسيعطيك في الخرج رابط URL الخاص بلوحة المعلومات، بدلًا من فتحه مباشرة في المتصفح. ألقِ نظرة على الأمر التالي الخاص بتشغيل اللوحة للخادم البعيد: $ minikube dashboard --url وسيكون الخرج كما يلي: 🤔 Verifying dashboard health ... 🚀 Launching proxy ... 🤔 Verifying proxy health ... http://127.0.0.1:34197/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ يختلف رقم المنفذ الذي يفتحه minikube للوحة المعلومات من نظامٍ إلى آخر، ستلاحظ أن رقمه على حاسوبك مختلفٌ عن رقمه هنا في هذا المثال. تمنع إعدادات الأمان الافتراضية لنظام Kubernetes الوصول إلى عنوان URL هذا من الأجهزة البعيدة لحمايته، لذا ينبغي عليك بدايةً إعداد قناة SSH آمنة مع الخادم قبل فتحه. اكتب إذاً الأمر التالي مع الراية L- لفتح قناة ssh بين الحاسوب المحلي والخادم البعيد، واكتب ضمنه رقم منفذ لوحة المعلومات الظاهر في الخرج السابق، وعنوان IP لخادمك البعيد ليصبح بهذه الصيغة: $ ssh -L 34197:127.0.0.1:34197 sammy@your_server_ip يمكنك بعد ذلك الدخول إلى لوحة المعلومات باستخدام الرابط: http://127.0.0.1:34197/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/. يمكنك معرفة المزيد عن تقنية الاتصال الآمن SSH بمطالعة الفيديو التالي: الآن بعد أن اختبرنا التعامل مع minikube مثل أي عنقود Kubernetes كامل عن طريق لوحة المعلومات، سننتقل للخطوة التالية، ونحاول نشر تطبيق تجريبي بسيط في هذا العنقود لنتأكد من عمله كما هو بالطريقة المرجوة منه. الخطوة 3: نشر تطبيق تجريبي واختباره يمكنك استخدام الأمر kubectl لنشر تطبيق تجريبي في عنقود Minikube. اكتب مثلًا الأمر التالي الذي سيؤدي إلى نشر تطبيق Kubernetes تجريبي متاح للاختبارات من شركة جوجل يدعى hello-app. $ kubectl create deployment web --image=gcr.io/google-samples/hello-app:1.0 يُنشئ هذا الأمر عملية نشر deployment داخل العنقود تدعى web، وتُبنى انطلاقًا من صورة بعيدة تسمى hello-app موجودة في سجل حاويات جوجل المسمى gcr.io. سنُعَرِّف الآن عملية النشر web بصفتها خدمة من خدمات Kubernetes، ونحدد منفذًا ثابتًا للاتصال معه بكتابة المحددين port=8080-- و type=NodePort--، وفق التالي: $ kubectl expose deployment web --type=NodePort --port=8080 يمكنك معرفة المزيد عن خدمات Kubernetes وكيفية الاتصال معها بمطالعة مقال تعرّف على كوبرنيتس Kubernetes. لنتحقق فيما إذا كانت الخدمة تعمل أم لا؟ بواسطة الأمر kubectl get service مع كتابة اسم الخدمة بعده، كما يلي: $ kubectl get service web ستحصل على خرج يشبه الخرج التالي، مع اختلاف في أرقام المنافذ لأن NodePort توزع أرقام المنافذ عشوائيًا على خدمات Kubernetes: NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE web NodePort 10.109.254.242 <none> 8080:31534/TCP 10s يمكننا الآن استخدام minikube للحصول على عنوان URL المتاح من خارج الحاوية، يسمح لك هذا العنوان بالاتصال مع خدمة التطبيق hello-app العاملة على المنفذ 8080 داخل العنقود. إذا كنت تستخدم Minikube على حاسوبك المحلي، فلست بحاجة لإعادة توجيه حركة البيانات من منفذ لآخر كما سنذكر لاحقًا، فقط نفذ الأمر minikube service web --url التالي، وستحصل على عنوان URL لتطبيقك التجريبي: $ minikube service web --url وسيكون الخرج عنوان URL مثل التالي: http://192.168.49.2:31534 اختبر عنوان URL بواسطة crul، وهو أحد أشهر برامج سطر الأوامر command line المستخدمة لإرسال أنواع مختلفة من طلبات الويب، يستخدم للتحقق من إمكانية عمل عناوين URL ضمن المتصفح في ظروف مناسبة، لذا ننصحك بفحص الروابط باستخدام crul دائمًا قبل تجربتها في المتصفح، وذلك وفق التالي: $ curl http://192.168.49.2:31534 يبين لك الخرج التالي نجاح العملية: Hello, world! Version: 1.0.0 Hostname: web-746c8679d4-j92tb يمكنك الآن استعراض عنوان URL هذا في المتصفح مباشرةً إذا كانت تستخدم minikube محليًا، وستحصل على النص السابق غير المنسق نفسه الذي حصلت عليه بتعليمة crul. أما إذا كنت تعمل على جهاز بعيد، استخدم اتصال SSH كما في الخطوة 2، ثم استعرض العنوان في المتصفح. أصبحت لديك الأساسيات اللازمة لنشر تطبيق عبر minikube فما ينطبق على التطبيق التجريبي البسيط الذي نشرناه يتنطبق نفسه على المشاريع الأكبر حجمًا، والتخصيص الإضافي الذي ستحتاجه فيها يتعلق بنظام Kubernetes وليس بوظائف minikube. سنتعلم في خطوتنا التالية طريقة استخدام بعض أدوات Minikube المدمجة لتغيير بعض الإعدادات الافتراضية للعنقود. الخطوة 4: إدارة نظام ملفات Minikube وموارده يوفر لك minikube عددًا من الأوامر الخاصة بتعديل إعدادات العنقود، فمثلًا يمكنك استخدام الأمر minikube config لتعديل الذاكرة المتوفرة للعنقود وفق التالي، علمًا أن الذاكرة هنا تقدر بالميجابايت MB وبالتالي يقابل الأمر minikube config 4096 توفير ذاكر بسعة 4GB لعنقودك: $ minikube config set memory 4096 ستحصل على الخرج التالي: ❗ These changes will take effect upon a minikube delete and then a minikube start يشير الخرج السابق إلى أن تعديل الذاكرة يتطلب إعادة نشر العنقود ليأخذ مفعوله. ملاحظة: لا تتطلب جميع التعديلات على الموارد إعادة نشر عنقود Kubernetes لتأخذ مفعولها، ففي بيئات الإنتاج يسري تغيير الذاكرة مباشرةً بدون إعادة نشر، لكن مع ذلك حاول أن لا تجري الكثير من التعديلات على عناقيد Kubernetes قيد التشغيل اعتمادًا على ملفات الإعدادات فقط بدون إجراء إعادة نشر، حاول الالتزام بإعادة النشر بعد كل تعديل على الموارد. تتضمن إعادة النشر مرحلتين هما minikube delete و minikube start، اكتب أولًا الأمر: $ minikube delete وستحصل على الخرج التالي: 🔥 Deleting "minikube" in docker ... 🔥 Deleting container "minikube" ... 🔥 Removing /home/sammy/.minikube/machines/minikube ... 💀 Removed all traces of the "minikube" cluster. ثم اكتب الأمر: $ minikube start يتيح لك minikube وصل أي مجلد من نظام ملفاتك المحلي الموجود على حاسوبك إلى داخل العنقود وصلًا مؤقتًا باستخدام الأمر minikube mount. أما كيفية كتابة الأمر mount قواعديًا فهي على الشكل التالي: local_path:minikube_host_path. يرمز local_path إلى مسار المجلد المحلي الذي تريد إيصاله إلى داخل العنقود، ويشير الجزء الآخر أي minikube_host_path إلى الموقع أو المجلد داخل VM أو داخل حاوية Minikube الذي تود أن تصل منه إلى ملفاتك. ألقِ نظرة على الأمر التالي الذي يوصل المجلد الأساسي الخاص بك home directory إلى المجلد host/ داخل عنقود minikube: $ minikube mount $HOME:/host وستحصل على الخرج التالي الذي يؤكد لك نجاح العملية: 📁 Mounting host path /home/sammy into VM as /host ... ▪ Mount type: ▪ User ID: docker ▪ Group ID: docker ▪ Version: 9p2000.L ▪ Message Size: 262144 ▪ Options: map[] ▪ Bind Address: 192.168.49.1:43605 🚀 Userspace file server: ufs starting ✅ Successfully mounted /home/sammy to /host 📌 NOTE: This process must stay alive for the mount to be accessible ... تفيدك هذه الطريقة في الحالات التي تحتاج فيها للحفاظ على مدخلات أو مخرجات ثابتة لعملك مع العنقود، مثل عمليات تسجيل الأحداث logging لعنقود minikube. تعطل هذه العملية نافذة الطرفية فلا يعود بإمكانك كتابة الأوامر فيها، يمكنك الخروج منها بالضغط على Ctrl+C، تمامًا كما فعلنا في حالة فتح المنفذ الخاصة بلوحة المعلومات التي ذكرناها في الخطوة 2. سنتعلم في الخطوة التالية كيف تتنقل بكفاءة بين minikube وعنقود Kubernetes كامل موجود على خادم بعيد. التعامل مع أكثر من عنقود Kubernetes يستطيع minikube التعامل مع عناقيد Kubernetes محلية متعددة في الوقت نفسه، فيُهيئ لكل عنقود ملف تعريف profile خاص به. فقد تحتاج في بعض الحالات للتعامل مع إصدارات مختلفة من عناقيد Kubernetes لإجراء اختبار معين مثلًا، فيمكنك عندها التبديل بين هذه الإصدارات باستخدام الراية p- أو profile--. وإذا كنت ستعمل مع عنقود معين لفترة طويلة أو أكثر من بقية العناقيد، فيمكنك تعيينه ليكون ملف التعريف الافتراضي في minikube بواسطة الأمر minikube profile، بدلًا من تحديده بعد الراية profile-- في كل أمر تنفذه. لنُشغّل الآن Minikube مع ملف تعريفي جديد بتنفيذ الأمر minikube start مع الراية p-، وفق التالي: $ minikube start -p new-profile اضبط الآن هذا الملف التعريفي الجديد ليكون هو الملف الفعال أو الافتراضي في Minikube بكتابة الأمر minikube profile كما يلي: $ minikube profile new-profile سيظهر لك هذا الخرج: ✅ minikube profile was successfully set to new-profile يمكنك معرفة الملف التعريفي الحالي الذي تستخدمه بواسطة الأمر get profile كما يلي: $ minikube config get profile وسيعيد لك الخرج اسم الملف التعريفي الحالي وهو في مثالنا: new-profile يُنشئ minikube ملفات الإعدادات لمنظومتك، ويخزنها هذه الملفات في مكانها الافتراضي المعروف للأداة kubectl ولغيرها من أدوات Kubernetes لتتمكن من الوصول إليها، ذلك سواء كنت تستخدم ملف تعريفي واحد أو عدة ملفات تعريفية، فمثلًا في كل مرة تنفذ فيها الأمر kubectl get nodes لاستعراض بيانات العقد nodes لعنقود minikube ستُحلل kubectl ملفات الإعدادات وتعطيك النتيجة، ألقِ نظرة على الأمر أدناه: $ kubectl get nodes وسيبين الخرج العقد الموجودة وهي عقدة واحدة فقط في حالتنا كما يلي: NAME STATUS ROLES AGE VERSION minikube Ready control-plane,master 3h18m v1.23.1 يمكنك اختيار أي ملف إعدادات تريده وإسناده للمُحَدِد kubeconfig لتقرأه kubectl عند بدء تشغيلها بدلًا من الملف الافتراضي الموجود في المجلد kube/confg. /~، وعندها ستستخدم بيانات اعتماد العنقود المذكورة في هذا الملف عوضًا عن تلك الموجودة في الملف الافتراضي. لنفترض أن لديك ملف إعدادات اسمه remote-kubeconfig.yaml مثلًا لعنقود Kubernetes آخر غير عنقودك في Minikube، وتريد استخراج العقد الموجودة فيه، فستكتب حينئذ الأمر التالي: $ kubectl --kubeconfig=remote-kubeconfig.yaml get nodes وستحصل في الخرج على العقد الموجودة في ذلك العنقود، والتي تعمل عن بعد خارج Minikube الخاص بك، وسيبدو الخرج كما يلي: NAME STATUS ROLES AGE VERSION pool-xr6rvqbox-uha8f Ready <none> 2d2h v1.21.9 pool-xr6rvqbox-uha8m Ready <none> 2d2h v1.21.9 pool-xr6rvqbox-uha8q Ready <none> 2d2h v1.21.9 صُمم Kubernetes في الأساس للعمل مع ملف إعدادات واحد لكل عنقود، يُمَرَر للأوامر مثل kubectl وغيره عند التشغيل، ومع ذلك يمكنك دمج عدة ملفات إعدادات مع بعضها، إلّا أنها ليست الطريقة المفضلة ولا تُعدّ ضرورية إذ سيضعب عليك بعدها تتبع أفضل الممارسات الموصى بها للعمل مع Kubernetes. ننصحك أيضًا بالتَعَرُّف على Krew مدير الحزم الخاص بإضافات Kubectl plugins. الخلاصة وضح هذا المقال كيفية تثبيت Minikube محليًا على الحاسوب الشخصي، واستخدام لوحة معلومات Kubernetes المُضمنة لمراقبة التطبيقات ونشرها، مع الإضاءة على الفكرة الأهم، وهي إمكانية العمل على نشر مثيل instance تجريبي للتطبيق واختباره محليًا ضمن minikube بالتزامن مع وجود مثيل Kubernetes بعيد نصل إليه بواسطة ملفات تعريف Minikube والراية kubectl --kubeconfig. خلاصة القول: يساعدك minikube على اختبار إعدادات Kubernetes وتقييمها محليًا قبل نشرها الفعلي، لتحدد كيف ومتى تصبح جاهزًا لنشر Kubernetes في بيئة الإنتاج. ترجمة -وبتصرف- للمقال How To Use minikube for Local Kubernetes Development and Testing لصاحبه Alex Garnett. اقرأ أيضًا تعلم أساسيات Kubernetes نشر التطبيقات وتوزيعها وفق نهج التسليم المستمر نظام كوبيرنتس Kubernetes وكيفية عمله أبرز المفاهيم التي يجب عليك الإلمام بها عن الحاويات
-
نناقش في مقال اليوم النماذج اللغوية الكبيرة (LLMs) وهي صاحبة الدور الرئيسي في توليد النصوص، فهي تتكون من نماذج ذكاء اصطناعي كبيرة من نوع المحولات transformer، ومُدَرَّبة مُسبقًا على مهمة التنبؤ بالكلمة التالية (أو الـtoken التالي) من أي مُوجه يعطى لها، فهي إذًا تتنبأ بكلمات فردية بمقدار كلمة واحدة في كل مرة، لذا فإن توليد الجمل الكاملة سيحتاج تقنيةً أوسع تسمى توليد الانحدار الذاتي autoregressive generation. ويُعرَّف توليد الانحدار الذاتي بأنه إجراءٌ استدلالي متكرر مع الزمن، يستدعي نموذج LLM مراتٍ متكررة وفي كل مرة يُمرر له المخرجات التي وَلَّدها في المرة السابقة كمدخلات وهكذا، وبطبيعة الحالة يحتاح إلى مدخلات ابتدائية نقدمها له ليستخدمها في الاستدعاء الأول للنموذج، وتوفر مكتبة المحولات Transformers تابعًا خاصًا لهذا الغرض هو generate()، يعمل جميع النماذج ذات الإمكانات التوليدية generative. نسعى في هذا المقال لتحقيق ثلاثة أهداف رئيسية: شرح كيفية توليد نص باستخدام نموذج لغوي كبير LLM. الإضاءة على بعض المخاطر الشائعة لتتجنبها. اقتراح بعض المصادر التي ستساعدك على تحقيق أقصى استفادة ممكنة من نماذج LLMs. تأكد في البداية من تثبيت المكتبات الضرورية للعمل قبل أن نبدأ بالتفاصيل، وذلك وفق التالي: pip install transformers bitsandbytes>=0.39.0 -q توليد النص يأخذ النموذج اللغوي المُدَرَّب على النمذجة اللغوية السببية سلسلة من الرموز النصية كمدخلات inputs ويرجع بناءً عليها التوزع الاحتمالي للرمز التالي المتوقع، ألقِ نظرة على الصورة التوضيحية التالية: أما عن كيفية اختيار الرمز التالي من هذا التوزع الاحتمالي الناتج، فهي تختلف حسب الحالة، فقد تكون بسيطةً تتمثل بانتقاء الرمز الأكثر احتمالية من ضمن رموز التوزع الاحتمالي، أو معقدة لدرجة نحتاج معها لتطبيق عشرات التحويلات قبل الاختيار، وبصرف النظر عن طريقة الاختيار فإننا سنحصل بعد هذه المرحلة على رمز جديد نستخدمه في التكرار التالي أو الاستدعاء التالي للنموذج كما هو موضح في الصورة التالية لتوليد الانحدار الذاتي: تستمر عملية توليد الكلمات حتى نصل إلى أحد شروط التوقف التي يُحددها النموذج، ويتعلم النموذج متى ينبغي أن يرجع رمز نهاية السلسلة (EOS) الذي يُعدّ الإنهاء المثالي لعملية توليد النص، وفي حال لم يرجع النموذج هذا الرمز فسيظل العمل مستمرًا لحين الوصول إلى الحد الأقصى المسموح به من الرموز. إذًا فلديك أمرين مهمين ينبغي أن تهتم بهما ليعمل نموذجك التوليدي بالطريقة المرجوة، الأمر الأول هو كيفية اختيار الرمز التالي من بين رموز التوزع الاحتمالي، والأمر الثاني هو تحديد شرط إنهاء التوليد، تُضبط هذه الإعدادات في ملف إعدادات التوليد GenerationConfig الخاص بكل نموذج توليدي، يُحَمَّل هذا الملف مع النموذج وهو يتضنت معاملاتٍ افتراضية مناسبة له. لنبدأ الآن بالتطبيق العملي. حَمِّل أولًا النموذج كما يلي: >>> from transformers import AutoModelForCausalLM >>> model = AutoModelForCausalLM.from_pretrained( "mistralai/Mistral-7B-v0.1", device_map="auto", load_in_4bit=True ) استخدمنا في الاستدعاء from_pretrained السابق معاملين device_map و load_in_4bit: يضمن المعامل device_map أن حمل النموذج سيتوزع تلقائيًا على وحدات GPU المتاحة. يساعد المعامل load_in_4bit على تقليل استخدام الموارد الحاسوبية إلى أقصى حد ممكن عبر تطبيق التكميم الديناميكي 4 بت. توجد طرق أخرى عديدة لتهيئة نموذج LLM عند تحميله، عرضنا إحداها في الأمر السابق، وهي أساسية وبسيطة. نحتاج الآن لمعالجة النص معالجةً مسبقة قبل إدخاله للنموذج وذلك باستخدام المُرَمِّز Tokenizer وهو نوع المعالجة المناسب للنصوص كما تعلمنا في مقال المعالجة المُسبقة للبيانات قبل تمريرها لنماذج الذكاء الاصطناعي: >>> from transformers import AutoTokenizer >>> tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-v0.1", padding_side="left") >>> model_inputs = tokenizer(["A list of colors: red, blue"], return_tensors="pt").to("cuda") سيُخَزَّن خرج المُرَمِّز (وهو عبارة عن النص المُرَمَّز وقناع الانتباه attention mask) في المتغير model_inputs، ويوصى عادةً بتمرير بيانات قناع الانتباه ما أمكن ذلك للحصول على أفضل النتائج، رغم أن التابع generate() سيسعى لتخمين قيمة قناع الانتباه عند عدم تمريره. ملاحظة: قناع الانتباه attention mask هو أداة تساعد النموذج اللغوي على معرفة الأجزاء المهمة في النص الذي يعالجه وتجاهل الأجزاء غير المهمة كرموز الحشو التي تُضاف لجعل طول النصوص موحدًا. يوجه هذا القناع النموذج ليركز فقط على الكلمات الفعلية في النص ويهمل الرموز لا تعني شيئًا للحصول على نتائج دقيقة. بعد انتهاء الترميز يُستدعى التابع generate() الذي سيُرجع الرموز tokens المتنبأ بها، والتي ستتحول إلى نص قبل إظهارها في خرج النموذج. >>> generated_ids = model.generate(**model_inputs) >>> tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] 'A list of colors: red, blue, green, yellow, orange, purple, pink,' ننوه أخيرًا إلى أنك لست مضطرًا لتمرير مدخلاتك إلى النموذج على شكل جمل مفردة، جملة واحدة في كل مرة، إذ يمكنك تجميع أكثر من جملة وتمريرها بهيئة دفعات batches مع استخدام الحشو padding لجعلها متساوية الطول، كما في المثال التالي، يزيد هذا الأسلوب من إنتاجية النموذج ويقلل الزمن والذاكرة المستهلكين: >>> tokenizer.pad_token = tokenizer.eos_token # لا تملك معظم النماذج اللغوية الكبيرة رمزًا للحشو افتراضيًا >>> model_inputs = tokenizer( ["A list of colors: red, blue", "Portugal is"], return_tensors="pt", padding=True ).to("cuda") >>> generated_ids = model.generate(**model_inputs) >>> tokenizer.batch_decode(generated_ids, skip_special_tokens=True) ['A list of colors: red, blue, green, yellow, orange, purple, pink,', 'Portugal is a country in southwestern Europe, on the Iber'] إذًا ببضع أسطر برمجية فقط استفدنا من أحد النماذج اللغوية الكبيرة LLM واستطعنا توليد نصوص مكلمة للجمل التي أعطيناها للنموذج. بعض المشكلات المحتمل وقوعها لا تناسب القيم الافتراضية لمعاملات النماذج التوليدية جميع المهام فلكل مشروع خصوصيته، والاعتماد عليها قد لا يعطينا نتائج مرضية في العديد من حالات الاستخدام، سنعرض لك أشهرها مع طرق تجنبها: لنبدأ أولًا بتحميل النموذج والمُرَمِّز ثم نتابع بقية أجزاء الشيفرة ضمن الأمثلة تباعًا: >>> from transformers import AutoModelForCausalLM, AutoTokenizer >>> tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-v0.1") >>> tokenizer.pad_token = tokenizer.eos_token # لا تمتلك معظم النماذج اللغوية الكبيرة رمزًا للحشو افتراضيًا >>> model = AutoModelForCausalLM.from_pretrained( "mistralai/Mistral-7B-v0.1", device_map="auto", load_in_4bit=True ) توليد خرج أطول أو أقصر من اللازم يُرجع التابع generate عشرين tokens كحد أقصى افتراضيًا، طالما أننا لم نحدد ما يخالف ذلك في ملف إعدادات التوليد Generation-config، ولكن ما ينبغي الانتباه له أن نماذج LLMs (وخاصة النماذج من نوع decoder models مثل GPT و CTRL) تُرجع مُوجَّه الدخل input prompt أيضًا مع كل خرج تعطيه، لذا ننصحك بتحديد الحد الأقصى لعدد الـ tokens الناتجة يدويًا، وعدم الاعتماد على الحد الافتراضي، وذلك ضمن المتغير max_new_tokens المرافق لاستدعاء generate، ألقِ نظرة على المثال التالي ولاحظ الفرق في الخرج بين الحالتين، الحالة الافتراضية، وحالة تحديد العدد الأقصى للرموز الناتجة: >>> model_inputs = tokenizer(["A sequence of numbers: 1, 2"], >>> return_tensors="pt").to("cuda") # الحالة الافتراضية الحد الأقصى لعدد الرموز الناتجة هو 20 رمز >>> generated_ids = model.generate(**model_inputs) >>> tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] 'A sequence of numbers: 1, 2, 3, 4, 5' # عند ضبط قيمة المتغير الذي سيحدد العدد الأقصى للرموز الناتجة >>> generated_ids = model.generate(**model_inputs, max_new_tokens=50) >>> tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] 'A sequence of numbers: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,' نمط توليد غير مناسب افتراضيًا يختار التابع generate الرمز الأكثر احتمالية من بين الرموز الناتجة في كل تكرار ما لم نحدد طريقة مغايرة للاختيار في ملف إعدادات التوليد GenerationConfig، يسمى هذا الأسلوب الافتراضي فك التشفير الشره greedy decoding، وهو لا يناسب المهام الإبداعية التي تستفيد من بعض العينات، مثل: بناء روبوت دردشة لمحادثة العملاء أو كتابة مقال متخصص، لكنه من ناحية أخرى يعمل جيدًا مع المهام المستندة إلى المدخلات، نحو: التفريغ الصوتي والترجمة وغيره، لذا اضبط المتغير على القيمة do_sample=True في المهام الإبداعية، كما يبين المثال التالي الذي يتضمن ثلاث حالات: >>> # استخدم هذا السطر إذا رغبت بالتكرار الكامل >>> from transformers import set_seed >>> set_seed(42) >>> model_inputs = tokenizer(["I am a cat."], return_tensors="pt").to("cuda") >>> # LLM + فك التشفير الشره = مخرجات متكررة ومملة >>> generated_ids = model.generate(**model_inputs) >>> tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] 'I am a cat. I am a cat. I am a cat. I am a cat' >>> # مع تفعيل أخذ العينات، تصبح المخرجات أكثر إبداعًا >>> generated_ids = model.generate(**model_inputs, do_sample=True) >>> tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] 'I am a cat. Specifically, I am an indoor-only cat. I' الحشو في الجانب الخاطئ ذكرنا سابقًا أنك عندما تقوم بإدخال جمل أو نصوص ذات أطوال مختلفة للنموذج، فقد تحتاج إلى جعل هذه المدخلات بطول موحد ليتمكن النموذج من معالجتها بشكل صحيح من خلال إضافة رموز الحشو (padding tokens) التي تجعل جميع المدخلات بطول متساوٍ. لكن النماذج اللغوية الكبيرة LLMs هي بنى لفك التشفير فقط decoder-only فهي تكرر الإجراءات نفسها على مدخلاتك، لكنها غير مُدَرَّبة على الاستمرار بالتكرار على رموز الحشو، لذا عندما تكون مدخلاتك مختلفة الأطوال وتحتاج للحشو لتصبح بطول موحد، فاحرص على إضافة رموز الحشو على الجانب الأيسر left-padded (أي قبل بداية النص الحقيقي) ليعمل التوليد بطريقة سليمة، وتأكد من تمرير قناع الانتباه attention mask للتابع generate حتى لا تترك الأمر للتخمين: >>> # المُرَمِّز المستخدم هنا يحشو الرموز على الجانب الأيمن افتراضيًا، والسلسلة النصية الأولى هي >>> # السلسلة الأقصر والتي تحتاج لحشو، وعند حشوها على الجانب الأيمن سيفشل النموذج التوليدي في التنبؤ بمنطقية >>> model_inputs = tokenizer( ["1, 2, 3", "A, B, C, D, E"], padding=True, return_tensors="pt" ).to("cuda") >>> generated_ids = model.generate(**model_inputs) >>> tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] '1, 2, 33333333333' >>> # لاحظ الفرق عند تعديل الحشو ليصبح على الجانب الأيسر >>> tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-v0.1", padding_side="left") >>> tokenizer.pad_token = tokenizer.eos_token # Most LLMs don't have a pad token by default >>> model_inputs = tokenizer( ["1, 2, 3", "A, B, C, D, E"], padding=True, return_tensors="pt" ).to("cuda") >>> generated_ids = model.generate(**model_inputs) >>> tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] '1, 2, 3, 4, 5, 6,' مُوجَّهات خاطئة يتراجع أداء بعض النماذج عندما لا نمرر لها مُوجَّهات input prompt بالتنسيق الصحيح الذي يناسبها، يمكنك الحصول على مزيد من المعلومات عن طبيعة الدخل المتوقع للنماذج مع كل مهمة بالاطلاع على دليل المُوجَّهات في نماذج LLMs على منصة Hugging Face، ألقِ نظرة على المثال التالي الذي عن استخدام نموذج LLM للدردشة باستخدام قوالب الدردشة: >>> tokenizer = AutoTokenizer.from_pretrained("HuggingFaceH4/zephyr-7b-alpha") >>> model = AutoModelForCausalLM.from_pretrained( "HuggingFaceH4/zephyr-7b-alpha", device_map="auto", load_in_4bit=True ) >>> set_seed(0) >>> prompt = """How many helicopters can a human eat in one sitting? Reply as a thug.""" >>> model_inputs = tokenizer([prompt], return_tensors="pt").to("cuda") >>> input_length = model_inputs.input_ids.shape[1] >>> generated_ids = model.generate(**model_inputs, max_new_tokens=20) >>> print(tokenizer.batch_decode(generated_ids[:, input_length:], skip_special_tokens=True)[0]) "I'm not a thug, but i can tell you that a human cannot eat" >>> # لم يتبع النموذج تعليماتنا هنا فهو لم يرد على السؤال كما ينبغي أن يرد أي شخص عنيف >>> # سنقدم الآن دخلًا أفضل يناسب النموذج باستخدام قوالب الدردشة، ونرى الفرق النتيجة >>> set_seed(0) >>> messages = [ { "role": "system", "content": "You are a friendly chatbot who always responds in the style of a thug", }, {"role": "user", "content": "How many helicopters can a human eat in one sitting?"}, ] >>> model_inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt").to("cuda") >>> input_length = model_inputs.shape[1] >>> generated_ids = model.generate(model_inputs, do_sample=True, max_new_tokens=20) >>> print(tokenizer.batch_decode(generated_ids[:, input_length:], skip_special_tokens=True)[0]) 'None, you thug. How bout you try to focus on more useful questions?' >>> # كما تلاحظ فقد تغير أسلوب الرد واتبع تعليماتنا بطريقة أفضل فكان رده أقرب للأسلوب المطلوب مصادر مفيدة للاستفادة من نماذج LLMs ستحتاج لتعميق معرفتك بالنماذج اللغوية الكبيرة (LLMs) إذا رغبت بتحقيق أقصى استفادة منها، وإليك بعض الأدلة المفيدة من منصة Hugging Face المتخصصة في المجال: أدلة الاستخدام المتقدم للتوليد Generating دليل استراتيجيات توليد النصوص باستخدام الذكاء الاصطناعي الذي يساعدك في تعلم كيفية التحكم بتوابع توليد مختلفة، وضبط مخرجاتها، وملفات الإعدادات الخاصة بها. دليل لاستخدام قوالب الدردشة مع نماذج LLMs. دليل LLM prompting يتضمن الأساسيات وأفضل الممارسات في كتابة المُوجَّهات. توثيقات واجهة برمجة التطبيقات API لكل من ملف إعدادات التوليد GenerationConfig و التابع ()generate و الأصناف clasess المرتبطة مع المعالجة المسبقة والعديد من الأمثلة التوضيحية. أشهر نماذج LLMs النماذج مفتوحة المصدر التي تُركِّز على الجودة Open LLM Leaderboard. النماذج التي تهتم بالإنتاجية Open LLM-Perf Leaderboard. أدلة حول تحسين السرعة والإنتاجية وتقليل استخدام الذاكرة دليل تحسين السرعة والذاكرة في نماذج LLMs. دليل التكميم Quantization باستخدام تقنيات مثل bitsandbytes و autogptq، لتخفيض متطلبات استخدام الذواكر. مكتبات مرتبطة بالنماذج اللغوية الكبيرة المكتبة text-generation-inference، وهي بمثابة خادم إنتاج جاهز للعمل مع نماذج LLMs. المكتبة optimum، وهي امتداد لمكتبة المحوّلات Transformers تساعدك في تحسين استخدام مكونات الحاسوب وموارده. كما تساعدك دورة الذكاء الاصطناعي من أكاديمية حسوب في فهم طريقة التعامل مع النماذج اللغوية الكبيرة LLMs وربط الذكاء الاصطناعي مع تطبيقاتك المختلفة، كما يمكنك الحصول على معلومات مفيدة من دروس ومقالات قسم الذكاء الاصطناعي على أكاديمية حسوب. دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن الخلاصة وصلنا إلى ختام المقال وقد عرضنا فيه طريقة استخدام النماذج اللغوية الكبيرة LLMs لتوليد النصوص الطويلة تلقائيًا من نص بسيط مُدخل، مع بيان بعض المخاطر الشائعة التي تعترض مستخدميها وكيفية تجنبها، بالإضافة لتعداد أشهر نماذج LLM، وبعض المصادر الموثوقة لمن يريد تعلمها بتعمقٍ أكبر. ترجمة -وبتصرف- لقسم Generation with LLMs من توثيقات Hugging Face. اقرأ أيضًا المقال السابق: استخدام وكلاء مكتبة المحولات Transformers Agents في الذكاء الاصطناعي التوليدي تدريب المًكيَّفات PEFT Adapters بدل تدريب نماذج الذكاء الاصطناعي بالكامل بناء تطبيق بايثون يجيب على أسئلة ملف PDF باستخدام الذكاء الاصطناعي تطوير تطبيق 'اختبرني' باستخدام ChatGPT ولغة جافاسكربت مع Node.js مصطلحات الذكاء الاصطناعي للمبتدئين
-
نعرفكم في مقال اليوم على كيفية استخدام مكتبة المحولات Transformers في مهام الذكاء الاصطناعي التوليدي مثل توليد الصور أو تحويل النص إلى كلام أو الإجابة عن أسئلة متعلقة بالمستندات حيث يوفر الإصدار v4.29.0 من مكتبة المحوّلات Transformers واجهة برمجة تطبيقات API اختبارية لعناصر مفيدة في الذكاء الاصطناعي التوليدي هي الوكلاء Agents والأدوات Tools، وسنناقشها بالتفصيل في فقراتنا التالية. يمكنك العمل مع هذه الواجهة البرمجية API من رابط مخصص لها على جوجل كولاب Google Colab، وهي تُركز على مهام فهم اللغة الطبيعية (اللغة البشرية)، فيعمل الوكيل agent على تفسير أوامر اللغة الطبيعية المعُطاة له مستخدمًا مجموعة من الأدوات الأساسية التي توفرها المكتبة؛ تحتاج لأن تبدأ عملك بتصميم الوكيل وتحديد أدواته المناسبة لمشروعك، ويمكنك في أي لحظة توسيع نطاق التصميم ليشمل أدواتٍ إضافية أنشأها وشاركها مطورون آخرون على مجتمع Hugging Face. سنعرض بعضًا من الأمثلة عمّا يمكنك تحقيقه باستخدام هذه الواجهة البرمجية API، وستلاحظ قوتها في المهام متعددة الأنماط multimodal، لذا سنبدأ أمثلتنا بتجربتها في مهام توليد الصور وقراءة النصوص صوتيًا. ألقِ نظرة على الأمر التالي فهو يوجه طلبًا مكتوبًا باللغة الإنكليزية للوكيل Agent ويطلب منه وضع تسمية توضيحية أو عنوان للصورة المعطاة له: agent.run("Caption the following image", image=image) لاحظ أن دخل الوكيل صورة والخرج تسمية توضيحية تناسبها: الدخل الخرج A beaver is swimming in the water الآن لنطلب من الوكيل Agent قراءة التسمية التوضيحية السابقة، سنمررها له ضمن text: agent.run("Read the following text out loud", text=text) وسيكون الدخل والخرج كما يلي: الدخل الخرج A beaver is swimming in the water ملف صوتي يقرأ النص باللغة الطبيعية وهي في مثالنا الإنجليزية tts_example.wav لنجرب أيضًا تمرير مستند document للوكيل Agent ونطرح عليه سؤالًا عن مضمون المستند: agent.run( "In the following `document`, where will the TRRF Scientific Advisory Council Meeting take place?", document=document, ) في هذه الحالة سيكون الدخل والخرج كما يلي: الدخل الخرج صورة المستند نص يتضمن الإجابة عن السؤال المستنتجة من المستند أكمل معنا قراءة المقال لتعرف الأساسيات، ثم ادخل إلى بيئة Google Colab الخاصة بالمكتبة وجرب بعض المهام بنفسك حتى تتقن التعامل معها. مقدمة سريعة الخطوة الأولى هي إدراج الوكيل الذي نود استخدامه بتعليمة agent.run، والوكيل هو نموذج لغوي كبير (LLM) يمكنك الحصول عليه من مصادر متنوعة، فمكتبة المحوّلات Transformers مثلًا تدعم النماذج اللغوية الخاصة بمنصات openAI و BigCode و OpenAssistant؛ تتمتع نماذج openAI بكفاءة أعلى من البقية لكنها بالمقابل غير مجانية إذ يتطلب استخدام واجهتها البرمجية مفتاحًا خاصًا "openAI API key"، أما نماذج منصتي BigCode و OpenAssistant فالوصول إليها متاح مجانًا عبر منصة Hugging Face يمكنك استخدامها بسهولة. لنُثَبِّت في البداية امتداد مكتبة المحوّلات الخاص بالوكلاء agents قبل تثبيت بقية الاعتماديات: pip install transformers[agents] تعتمد خطوتنا التالية على نوع المنصة التي سنستخدم نماذجها. إذا رغبت باعتماد نماذج OpenAI فثَبِّت الاعتمادية openai لتتمكن من استخدامها وإنشاء المثيل OpenAiAgent ليكون وكيلًا: pip install openai يمكنك الآن إدراج الوكيل OpenAiAgent كما يلي: from transformers import OpenAiAgent agent = OpenAiAgent(model="text-davinci-003", api_key="<your_api_key>") أما إذا رغبت باستخدام نماذج BigCode أو OpenAssistant، فعليك في البداية تسجيل الدخول إلى منصة Hugging Face لتحصل على صلاحية الوصول إلى واجهة API الاستدلالية التي توفرها Hugging Face لهذه النماذج: from huggingface_hub import login login("<YOUR_TOKEN>") وبعدها أدرج الوكيل agent: from transformers import HfAgent # Starcoder agent = HfAgent("https://api-inference.huggingface.co/models/bigcode/starcoder") # StarcoderBase # agent = HfAgent("https://api-inference.huggingface.co/models/bigcode/starcoderbase") # OpenAssistant # agent = HfAgent(url_endpoint="https://api-inference.huggingface.co/models/OpenAssistant/oasst-sft-4-pythia-12b-epoch-3.5") استخدمنا هنا واجهة API المجانية (حاليًا) التي توفرها Hugging Face للوصول إلى النماذج اللغوية الكبيرة، لكن يمكنك بالطبع استخدام أي واجهة API أخرى متوفرة لديك وعندها ستستبدل عنوان URL الذي استخدمناه بعنوان URL لنقطة الاتصال endpoint المتوفرة لديك. ملاحظة: تعمل النماذج المجانية لكل من OpenAssistant و StarCoder جيدًا في المهام البسيطة لكن أداءها يتراجع للأسف كلما ازداد تعقيد المهام الموكلة لها، لذا ننصحك باستخدام نماذج OpenAI في المشاريع الكبيرة فبالرغم من كونها غير مفتوحة المصدر لكنها مناسبة. لننتقل إلى الخطوة التالية، ونتعرف عن قرب على واجهتي API المتوفرتين لدينا الآن. التنفيذ الإفرادي باستخدام التابع run يستخدم التنفيذ الإفرادي single execution التابع ()run مع الوكيل agent كما في المثال التالي الذي يطلب من الوكيل توليد صورة تتضمن أنهارًا وبحيرات: agent.run("Draw me a picture of rivers and lakes.") أرجع الوكيل الصورة التالية: سيُحَدِدْ الوكيل agent تلقائيًا الأداة tool أو الأدوات المناسبة للمهمة التي طلبنا تنفيذها، ويعمل على تشغيلها بالطريقة الصحيحة، علمًا أنه يستطيع تنفيذ أكثر من مهمة في آن واحد، لكن لا ننصحك بذلك فكلما ازداد تعقيد الأوامر الموجهة له ازدادت احتمالات الفشل. وهذا مثالٌ آخر لتوليد صورة تتضمن بحرًا ثم إضافة جزيرة عليها: agent.run("Draw me a picture of the sea then transform the picture to add an island") وستكون الصورة التي ستحصل عليها كالتالي: يمكنك تشغيل ()run لمهام مختلفة عدة مرات متتالية، فكل تعليمة ()run مستقلة تمامًا عن غيرها من التعليمات. تُعدّ دقة مطالبات الدخل prompts الموجهة للوكيل مهمة جدًا في الحصول على أفضل النتائج، فالاختلافات الصغيرة فيها تصنع فرقًا فالوكيل agent في نهاية الأمر مجرد نموذج لغوي كبير يستجيب للتوجيهات التي تُعطى له. كما يوفر الوكيل خياراتٍ مفيدة أخرى عبر المتغيرات التي يقبلها، فتستطيع مثلًا تمرير كائنات غير نصية له كتمرير الصور، أو توجيهه لحفظ حالة التنفيذ الأخيرة التي وصل إليها وتحديثها بإضافة عناصر جديدة، لنفترض أننا نريد من الوكيل أخذ صورة الأنهار والبحيرات التي ولدها في مثالنا السابق وإضافة جزيرة عليها، فستكون الأوامر وفق التالي: picture = agent.run("Generate a picture of rivers and lakes.") updated_picture = agent.run("Transform the image in `picture` to add an island to it.", picture=picture) تفيدك هذه الحالة عندما لا يكون النموذج قادرًا على فهم طلبك بوضوح أو عاجزًا عن مزج عدة أدوات معًا، كما في المثال التالي: > agent.run("Draw me the picture of a capybara swimming in the sea") قد يُفسِّر الوكيل هذا الطلب بطريقتين: توليد صورة من النص text-to-image والنص هو: "ارسم لي صورة لحيوان كابيبارا يسبح في الماء". توليد صورة من النص text-to-image والنص هو: "ارسم لي صورة لحيوان كابيبارا"، ثم "استخدام أداة تحويل الصور image-transformation لجعل حيوان الكابيبارا يسبح". يمكنك حسم الموضوع وفرض السيناريو الأول بتمرير موجه prompt صريحة كمتغير للوكيل تتضمن بوضوح ما تريد وجوده في الصورة كما يلي: agent.run("Draw me a picture of the `prompt`", prompt="a capybara swimming in the sea") التنفيذ المستند إلى الدردشة باستخدام التابع chat يعمل الوكيل agent بأسلوب الدردشة النصية chat-based بواسطة التابع ()chat كما في المثال التالي: سنطالبه أولًا بتوليد صورة أنهار وبحيرات: agent.chat("Generate a picture of rivers and lakes") سيولد لنا الصورة التالية: ثم سنطلب منه تحويل الصورة وإضافة عنصر إليها كما يلي: agent.chat("Transform the picture so that there is a rock in there") يساعدك هذا الأسلوب في الاختبارات وتنفيذ التعليمات الفردية التي تحافظ فيها على الحالة الأخيرة للتنفيذ عبر مجموعة من التعليمات، لكنه ليس الأسلوب الأمثل للتعليمات المعقدة، التي يناسبها التنفيذ المعتمد على ()run. يقبل التنفيذ المعتمد على الدردشة تمرير وسيط غير نصي أو موجهات Prompts محددة للتابع ()chat. التنفيذ عن بعد Remote execution أنشأت منصة Hugging Face -باستخدام نقاط الاتصال الاستدلالية inference endpoints- مُنَفِّذْين عن بعد remote executors للعديد من الأدوات الافتراضية التي يستخدمها الوكلاء agents، ستجدها معطلة حاليًا في هذا الإصدار لكننا ذكرناها هنا لتعرف أن الإمكانية متوفرة، وأن باستطاعتك إنشاء أدوات مخصصة لاحتياجات مشروعك وتخزينها في مستودع بعيد ثم استدعائها للتنفيذ عن بعد، اقرأ دليل إنشاء الأدوات المخصصة لتعرف أكثر عن الموضوع. الوكلاء Agents والأدوات Tools بعد هذه المقدمة السريعة سنُعَرِّف الآن الوكلاء Agents والأدوات Tools، لكن اطّلع أولًا على المخطط التوضيحي التالي: الوكلاء Agents الوكيل agent هو نموذج لغوي كبير (LLM) نوجهه بواسطة مُوجّهات prompts ليستخدم مجموعة من الأدوات tools التي نحددها له بغرض تنفيذ المهمة المطلوبة. تُعَدُ النماذج اللغوية الكبيرة (LLMs) مناسبة لتوليد شيفرات برمجية صغيرة، لذا تستفيد الواجهة البرمجية API من هذه الميزة وتطلب من النموذج توليد شيفرة برمجية تتضمن التعليمات اللازمة لتنفيذ مهمة محددة مع استخدام أدوات معينة نَصِفُها له، فيأخذ الوكيل المهمة والوصف المقدم للأدوات، ويكمل المطلوب بإنشاء تعليمات متوافقة مع مدخلات الأدوات ومخرجاتها كما هو موضح في المخطط السابق. الأدوات Tools تتكون الأداة tool من دالة واحدة لها وظيفة محددة؛ وتتميز كل أداة باسم ووصف، نستخدم وصف الأدوات في المُوجِّهات prompt الموجهة للوكيل agent، فيوضح له الوصف كيف يستفيد من كل أداة في تنفيذ المهمة المطلوبة. يستخدم الوكيل agent الأدوات tools بدلًا من خطوط الأنابيب pipelines بالرغم من تشابه المفهومين لأنه يقدم أداءً أفضل في كتابة الشيفرة البرمجية مع الأدوات المحددة والصغيرة، فالأدوات تتخصص بمهمة واحدة بسيطة، بينما تجمع خطوط الأنابيب مهام متعددة في آنٍ واحد. تنفيذ الشيفرة البرمجية بعد توليد الوكيل الشيفرة البرمجية تُنَفَّذْ على المدخلات مباشرةً باستخدام مُفَسِّر أوامر بايثون المحدود والخاص في منصة Hugging Face، قد لا تطمئن لهذه الطريقة أو تظنها غير آمنة فالأوامر الناتجة عن الوكيل تذهب للمُفَسِّر مباشرةً وتُنَفَّذْ دون أن تراها. لكن ما يخفف مخاطرها أن التنفيذ يقتصر على الأدوات التي تستدعيها بنفسك فقط ، ولن تُنفذ أي تعليمات أخرى غيرها، لذا فهو يُعدّ آمنًا نوعًا ما، طالما أنك تلتزم باستخدام الأدوات التي تؤمنها منصة Hugging Face، فالمنصة تعتمد عددًا من الاحتياطات الأمنية فهي تمنع عمليات استخراج السمات من الشيفرات البرمجية أو استيرادها وتحميك من الهجمات الشهيرة. وإذا رغبت بحماية أكبر فيمكنك استخدام أسلوب التنفيذ المعتمد على التابع ()run مع الوسيط return_code=True الذي سيعرض لك الشيفرة البرمجية التي وَلَّدها الوكيل لتتفحصها وتقرر بنفسك فيما إذا ما كنت تود تنفيذها أم لا. أما حالات توقف التنفيذ غير المتوقعة التي قد تصادفك، فهي ترجع عادةً لسببين: إما تنفيذك عملية غير مصرح بها، أو وجود خطأ ما في الشيفرة التي أنتجها الوكيل. الأدوات التي توفرها مكتبة Transformers هذه قائمة بأشهر الأدوات المدمجة في مكتبة المحوّلات Transformers والمستخدمة لتشغيل الوكلاء agents: الإجابة عن أسئلة حول محتوى المستند Document question answering: نمرر للنموذج مستندًا (ملف PDF مثلًا) بصيغة صورة ويستخدم الأداة للإجابة عن أي سؤال تطرحه حول محتوى المستند، ومن أمثلته النموذج (Donut). الإجابة عن أسئلة حول النص Text question answering: تكون المدخلات هنا نص طويل وسؤال، والمخرجات هي الإجابة عن هذا السؤال من داخل النص، تعمل مع النموذج (Flan-T5). وضع تسمية توضيحية للصورة Unconditional image captioning: وضع تسميةً توضيحية للصور المدخلة، مثل النموذج (BLIP). الإجابة عن أسئلة حول الصورة Image question answering: نمرر صورة لتتم الإجابة عن السؤال المطروح عنها، (VILT). تجزئة الصور Image segmentation: المدخلات في هذه الحالة: صورة، وموجه prompt بتجزئة الصورة، ويكون الخرج قناعًا لتجزئة الصورة حسب المعيار المحدد في الموجه prompt، ومن أمثلتها النموذج (CLIPSeg) تحويل الكلام إلى نص Speech to text: نمرر ملفًا صوتيًا لشخص يتكلم، ويجري تحويله إلى نص، يناسبه مثلًا النموذج (Whisper). تحول النص إلى كلام Text to speech: العملية المعاكسة، الدخل هنا هو نص ويُحوّل إلى كلام، تعمل مع النموذج (SpeechT5). تصنيف النصوص بدون تدريب مسبق Zero-shot text classification: نمرر للنموذج نصًا ومجموعة تسميات توضيحية labels لم تكن موجودة ضمن البيانات التي تَدَرَّبَ عليها سابقًا، وينبغي له تحديد التسمية التوضيحية الأكثر توافقًا مع النص، مثل النموذج (BART). تلخيص النصوص Text summarization: تلخيص النص الطويل ببضع جمل فقط، مثل النموذج (BART). الترجمة Translation: ترجمة النص المُعطى إلى اللغة المحددة، مثل النموذج (NLLB). هذه الأدوات مدمجة في مكتبة المحولات Transformers ويمكنك استدعائها واستخدامها يدويًا خارج الإطار السابق كما في المثال التالي: from transformers import load_tool tool = load_tool("text-to-speech") audio = tool("This is a text to speech tool") الأدوات المخصصة بالرغم من توفر العديد من الأدوات الجاهزة في منصة Hugging Face إلّا أن الإمكانية متاحة أمامك لإنشاء أدوات مخصصة لاحتياجات عملك ومشاركتها على المنصة بسرعة وبسهولة. بعد الانتهاء من برمجة الأداة ارفع شيفرتها البرمجية إلى المنصة أو إلى مستودع النموذج الخاص بك، وستتمكن بعدها من استخدامها مع أي وكيل agent، وهذه بعض الأمثلة عن أدوات مخصصة موجودة في قسم huggingface-tools على منصة Hugging Face وهي مستقلة عن مكتبة Transformers: تحميل النصوص Text downloader: تحميل نص من عنوان URL معين. تحويل النص إلى صورة Text to image: توليد صورة بناءً على النص المُدخل في الموجه prompt، وذلك باستخدام نموذج الذكاء الاصطناعي stable diffusion. تحويل الصور Image transformation: تكون المدخلات صورة مبدئية، وموجه prompt بتعديلها، ويجري تعديل الصورة بناءً على الموجه prompt، وذلك باستخدام تعليمات pix2pix stable diffusion. تحويل النص إلى فيديو Text to video: يُنشئ مقطع فيديو صغير بناءً على الموجه prompt المقدم له، وذلك بالاستفادة من damo-vilab. بالمناسبة إن أداة تحويل النص إلى صورة text-to-image التي استخدمناها في أمثلة المقال هي أداة بعيدة مستضافة على منصة Hugging Face على الرابط huggingface-tools/text-to-image، وستجد العديد من الأدوات الأخرى المستضافة هناك. يمكنك الاطلاع على دليل إنشاء الأدوات المخصصة لتتعرف أكثر على طريقة إنشاء الأدوات ومشاركتها، علمًا أن الوكلاء agents يتمتعون تلقائيًا بصلاحية الوصول إلى كافة الأدوات الموجودة على Hugging Face. توليد الشيفرة البرمجية Code generation استخدمنا في أمثلتنا السابقة الوكلاء agents لتنفيذ الإجراءات دون أي تدخل منا، فالوكيل يوّلِد الشيفرة البرمجية ثم تُنَفَّذ هذه الشيفرة باستخدام مُفَسِّر بايثون المحدود، وبالتالي إذا رغبت باستخدام الشيفرة البرمجية الموَّلدة بأي طريقة أخرى، يمكنك ذلك عبر مطالبة الوكيل بإرجاع الشيفرة البرمجية الناتجة جنبًا إلى جنب مع تعريف الأداة المستخدمة وعمليات الاستيراد imports الدقيقة. فالتعليمة المبينة أدناه على سبيل المثال: agent.run("Draw me a picture of rivers and lakes", return_code=True) تُرجع الشيفرة التالية التي تستطيع تعديلها وتنفيذها يدويًا حسب ما تريد: from transformers import load_tool image_generator = load_tool("huggingface-tools/text-to-image") image = image_generator(prompt="rivers and lakes") الخلاصة تعرفنا في هذا المقال على الوكلاء agents والأدوات tools والأساليب المختلفة لاستخدامها في تحويل أوامر اللغة الطبيعية إلى تعليمات برمجية لتوليد الصور والنصوص والفيديوهات أو لترجمتها أو تلخيصها أو تحويلها حالة إلى أخرى، وتعلمنا أيضًا أننا أحرار في طريقة الاستفادة من هذه التعليمات البرمجية الناتجة، فيمكننا تركها لتُنَفَّذْ تلقائيًا أو استعراضها وتنفيذها يدويًا أو حتى استخدامها في أغراضٍ أخرى في برامج أوسع مثلًا أو غير ذلك. وفي الختام ندعوك لمتابعة مقالنا التالي حول توليد النصوص باستخدام النماذج اللغوية الكبيرة LLMs لتعزز معرفتك أكثر بهذا المجال الحيوي. ترجمة -وبتصرف- لقسم Transformers Agents من منصة Hugging Face. اقرأ أيضًا المقال السابق: تدريب المًكيَّفات PEFT Adapters بدل تدريب نماذج الذكاء الاصطناعي بالكامل بناء تطبيق بايثون يجيب على أسئلة ملف PDF باستخدام الذكاء الاصطناعي تطوير تطبيق 'اختبرني' باستخدام ChatGPT ولغة جافاسكربت مع Node.js مصطلحات الذكاء الاصطناعي للمبتدئين
-
تساهم مشاركة نماذج الذكاء الاصطناعي مع الآخرين في نشر المعرفة، وتوفير الوقت والموارد على الراغبين باستثمارها وبالأخص على المستخدمين الجدد، إذا كنت تتابع معنا هذه السلسلة عن مكتبة المحولات Transformers مفتوحة المصدر من منصة Hugging Face فقد تعلمت كيفية صقل Fine-Tune نموذج ذكاء اصطناعي مُدَرَّبْ مُسبقًا باستخدام كل من PyTorch و Keras وأيضًا كيفية تسريع تدريبه بواسطة التدريب الموزع والمكتبة Accelerate، وستتعلم الآن كيف تشاركه مع المجتمع. سواء كنت قد دَرَّبتَ نموذجك من الصفر أو استخدمت نموذجًا مُسبق التدريب وصقلته fine-tune على بيانات محددة فلديك طريقتين لمشاركته على مستودع نماذج Hugging Face المسمى Model Hub: رفع الملفات إلى المستودع برمجيًا. رفع الملفات إلى المستودع بطريقة السحب والإفلات من واجهة الويب للمستودع. ميزات مستودع النماذج Model Hub يتمتع كل مستودع repository في مستودع النماذج Model Hub بكافة المميزات الخاصة بمستودعات GitHub التقليدية، مثل: توفر نظام التحكم بالإصدارات versioning، وسجل تتبع التعديلات commit history، وإمكانية العرض المرئي للاختلافات بين الإصدارات visualize differences وغيرها. يعتمد نظام التحكم بالإصدار versioning المُضَمَّنْ في مستودع النماذج على كل من git و git-lfs، فتستطيع التعامل مع النموذج على أنه مستودع مستقل، يزيد ذلك من قدرتك على التوسع، ويمنحك تحكمًا أكبر بإمكانية الوصول للنماذج، كما يسمح نظام التحكم بالإصدار بالمراجعات revisions أي تثبيت إصدار محدد من النموذج وتمييزه بعلامةٍ خاصة قد تكون رمزًا commit hash أو وسمًا tag أو فرعًا من المستودع branch. بالنتيجة يمكنك تحديد إصدار النموذج الذي تريده باستخدام المعامل revision: model = AutoModel.from_pretrained( "julien-c/EsperBERTo-small", revision="v2.0.1" # tag name, or branch name, or commit hash ) تُعَدَّل ملفات المستودع بسهولة، ويمكنك مشاهدة التعديلات التي طرأت عليعا والاختلافات التي أحدثتها في النموذج بمرونة تامة، كما في الصورة التالية: إعداد متطلبات العمل تحتاج إلى بيانات الاعتماد الخاصة بك على منصة Hugging Face قبل مشاركة أي نموذج على مستودع النماذج، إذا كنت تملك وصولًا لنافذة الطرفية terminal، فاكتب الأمر التالي ضمن البيئة الافتراضية التي أنشأناها عند تثبيت مكتبة المُحوّلات Transformers وبمجرد تنفيذه ستُخَزَّن بيانات اعتمادك المتمثلة برمز الدخول access token في مجلد الذاكرة المخبئية الخاص بمنصة Hugging Face (وهو المجلد /~/.cache افتراضيًا): huggingface-cli login أما إذا كنت تستخدم دفترًا للملاحظات notebook عبر الويب مثل: Jupyter أو Colaboratory فاحرص في البداية على تثبيت المكتبة huggingface_hub التي تسمح لك بالتفاعل البرمجي مع مستودع النماذج: pip install huggingface_hub ثم استخدم notebook_login كما يلي لتسجيل الدخول إلى مستودع النماذج، واتبع بعدها الخطوات الخاصة بتوليد رمز الدخول token لتستخدمه: >>> from huggingface_hub import notebook_login >>> notebook_login() تحويل النموذج ليعمل مع أي إطار عمل ننصحك بإنشاء نقطتي تحقق checkpoints لنموذجك قبل رفعه إلى المستودع واحدة باستخدام إطار العمل PyTorch وأخرى باستخدام TensorFlow، يُسَهِّل هذا الأمر استفادة الآخرين من النموذج بصرف النظر عن إطار العمل الذي يستخدمونه. ونشير هنا إلى المستخدمين سيظلون قادرين على تحميل نموذجك بأي إطار عمل يرغبون به حتى إذا تجاوزت هذه الخطوة ولم تحوّل نموذجك، لكنهم سيعانون عندها من بعض البطء في التحميل، لأن المحوّلات Transformers ستُحَوّل النموذج إلى صيغة تناسب إطار عملهم في أثناء التحميل فيزداد الوقت المستغرق له. أما تحويل نقطة التحقق الخاصة بنموذجك من إطار العمل الذي تعتمده إلى إطار عمل آخر فيُعَدُّ إجراءً سهلًا سنتعلمه معًا، لكن تأكد في البداية من تثبيت إطاري العمل باي تورش PyTorch وتنسرفلو TensorFlow (يمكنك مراجعة مقال تثبيت مكتبة المحوّلات Transformers من هذه السلسلة على أكاديمية حسوب لتتعرف على خطوات التثبيت)، ثم ابحث عن نموذج مهمتك في إطار العمل الآخر الذي ستحول نموذجك إليه لتتأكد أنه متوافق معه. تحويل نقطة التحقق من TensorFlow إلى PyTorch عبر ضبط قيمة المعامل from_tf=True وفق التالي: >>> pt_model = DistilBertForSequenceClassification.from_pretrained("path/to/awesome-name-you-picked", from_tf=True) >>> pt_model.save_pretrained("path/to/awesome-name-you-picked") تحويل نقطة التحقق من PyTorch إلى TensorFlow عبر ضبط قيمة المعامل from_pt=True كما يلي: >>> tf_model = TFDistilBertForSequenceClassification.from_pretrained("path/to/awesome-name-you-picked", from_pt=True) ثم احفظ نموذج TensorFlow الجديد بنقطة تحققه الجديدة: >>> tf_model.save_pretrained("path/to/awesome-name-you-picked") وإذا كان النموذج مدعومًا في Flax يمكنك تحويله من PyTorch إلى Flax وفق التالي: >>> flax_model = FlaxDistilBertForSequenceClassification.from_pretrained( "path/to/awesome-name-you-picked", from_pt=True ) رفع النموذج إلى المستودع في أثناء التدريب يُعدّ رفع النماذج ومشاركتها على المستودع عملًا بسيطًا يماثل إضافة معامل أو رد نداء callback أو غيره من الخيارات المألوفة، فهو في نهاية الأمر أحد معاملات التدريب التي نُحددها ضمن الصنف TrainingArguments كما تعلمنا في مقال صقل Fine-Tune نموذج ذكاء اصطناعي مُدَرَّبْ مُسبقًا فكل ما عليك فعله هو ضبط المعامل Push_to_hub=True في الصنف TrainingArguments كما في الخطوات التالية، وسيُرفَع نموذجك مباشرةً إلى المستودع: >>> training_args = TrainingArguments(output_dir="my-awesome-model", push_to_hub=True) نُمَرِّر هذه الخيارات الآن إلى المُدَرِّب Trainer: trainer = Trainer( model=model, args=training_args, train_dataset=small_train_dataset, eval_dataset=small_eval_dataset, compute_metrics=compute_metrics, ) وبعد الانتهاء من صقل fine-tune النموذج نستدعي الدالة call push_to_hub() بواسطة المُدَرِّب Trainer لرفعه إلى مستودع النماذج، وستُضيف مكتبة المحوّلات Transformers تلقائيًا معلومات النموذج إلى بطاقة الوصف الخاصة به، وهي تتضمن مثلًا: المعاملات الفائقة hyperparameters للنموذج، ونتائج تدريبه، وإصدارات إطار العمل التي يعمل عليها وما إلى ذلك: trainer.push_to_hub() وفي إطار العمل TensorFlow يمكنك مشاركة النماذج على المستودع بواسطة الدالة PushToHubCallback مع إضافة الخيارات التالية: مجلد خرج النموذج. المُرَمِّز tokenizer. مُعَرِّف النموذج على المستودع hub_model_id وهو يتكون من اسم المستخدم مالك النموذج username واسم النموذج. وستكون الأوامر كما يلي: >>> from transformers import PushToHubCallback >>> push_to_hub_callback = PushToHubCallback( output_dir="./your_model_save_path", tokenizer=tokenizer, hub_model_id="your-username/my-awesome-model" ) ثم أضف رد نداء callback للدالة fit وفق التالي وسترفع المحوّلات Transformers نموذجك إلى المستودع: >>> model.fit(tf_train_dataset, validation_data=tf_validation_dataset, epochs=3, callbacks=push_to_hub_callback) استخدام الدالة push_to_hub يمكنك بدلًا من استخدام الطريقة السابقة استدعاء الدالة push_to_hub وتمرير اسم النموذج المطلوب رفعه إلى المستودع. حَدِّدْ اسم النموذج ضمن push_to_hub وفق التالي: >>> pt_model.push_to_hub("my-awesome-model") سينشأ الآن مستودعًا repository للنموذج على مستودع النماذج لمنصة Hugging Face يكون تحت اسمك، ويُسمى باسم النموذج وهو في مثالنا my-awesome-model، ويمكن للمستخدمين الآخرين بعد ذلك تحميله بواسطة الدالة from_pretrained كما يلي: >>> from transformers import AutoModel >>> model = AutoModel.from_pretrained("your_username/my-awesome-model") أما إذا كنت تنتمي لمؤسسة لديها حساب على Hugging Face وتريد رفع نموذجك تحت اسم المؤسسة فأضف مُعَرِّف مستودع المؤسسة repo_id مع اسم النموذج وفق التالي: >>> pt_model.push_to_hub("my-awesome-org/my-awesome-model") تساعدك الدالة Push_to_hub على رفع ملفات أخرى إلى مستودع النموذج نحو المُرَمِّز tokenizer مثلًا: >>> tokenizer.push_to_hub("my-awesome-model") أو نسخة TensorFlow من نموذج PyTorch المصقول fine-tuned كما يلي أو غير ذلك: >>> tf_model.push_to_hub("my-awesome-model") الآن عندما تفتح حسابك على منصة Hugging Face سترى المستودع الجديد الذي أنشأته، وبالضغط على تبويب الملفات Files ستستعرض جميع الملفات التي حَمَّلتها إلى المستودع. شرحنا هنا الأساسيات فقط، ولمزيد من المعلومات حول إنشاء الملفات ورفعها على مستودع Hugging Face راجع التوثيقات ذات الصلة على المنصة. تحميل النماذج من واجهة الويب الخاصة بالمستودع إذا كنت تفضل العمل مع الواجهات الرسومية بدلًا من سطر الأوامر فيمكنك استخدام واجهة مستودع Hugging Face الموجودة على الرابط huggingface.co/new لإنشاء المستودع الجديد ورفع النماذج إليه كما في الصورة التالية: بعدها أدخل المعلومات التالية المطلوبة في الصورة أعلاه وهي كالتالي: حَدِّدْ مالك المستودع owner، سواء كنت أنت بصفتك الشخصية أو أي مؤسسة تنتمي إليها. اختر اسمًا مميزًا لنموذجك، والذي سيكون اسمًا للمستودع أيضًا. حَدِّدْ خصوصية مستودعك عامًا أم خاصًا. اختر رخصة استخدام نموذجك. اضغط بعدها على تبويب الملفات Files ثم زر أضف ملف Add file وحَمِّل ملفك الجديد إلى المستودع بطريقة السحب والإفلات، وأخيرًا أضف رسالة التثبيت commit message، ألقِ نظرة على الصورة التوضيحية التالية: إضافة بطاقة وصف للنموذج Model Card تساعد البطاقات مستخدمي النماذج على معرفة مزايا كل نموذج وإمكاناته والاعتبارات الأخلاقية لاستخدامه وغير ذلك من التفاصيل، لذا احرص على إضافة بطاقة خاصة لنموذجك لترشد كل من يود الاستفادة منه أو لتعود إليها عند الحاجة؛ تُعَرَّفْ بطاقة وصف النموذج model card بواسطة الملف README.md ويمكنك إنجاز الأمر بطريقتين: إنشاء الملف README.md وتحميله يدويًا. الضغط على زر تحرير بطاقة النموذج Edit model card من الواجهة الرسومية لمستودعك. ألق نظرة على بطاقة وصف النموذج DistilBert على سبيل المثال، واطَّلِع على المعلومات التي تتضمنها عادةً بطاقات النماذج، كما يفيدك أيضًا استعراض قسم التوثيقات الخاص ببطاقات النماذج على منصة Hugging Face لتعرف المزيد عن الخيارات المرتبطة بالملف README.md مثل البصمة الكربونية للنموذج model’s carbon footprint (التي تقيس استهلاك الطاقة المستخدمة في تشغيل الحواسيب اللازمة لتدريب النموذج، أو اختباره وتشغيله)، وبعض الأمثلة عن الأدوات الملحقة به widget examples. الخلاصة تعلمنا في هذا المقال كيفية مشاركة نماذج الذكاء الاصطناعي على مستودع Hugging Face بعدة طرق برمجيًا وعبر واجهة الويب الرسومية لمستودع النماذج، برمجيًا لدينا طريقتين استخدام الدالة push_to_hub أو إضافة المعامل Push_to_hub=True إلى معاملات التدريب، أما عبر واجهة الويب فيمكنك إنشاء حسابك على المنصة ورفع نموذجك ببضع نقرات بسيطة، والنقطة الأهم أنك تستطيع تحويل النموذج إلى أطر العمل المختلفة قبل مشاركته على المستودع، وذلك بخطوات بسيطة عرضناها في المقال، فتُسهل على الآخرين تحميله بالصيغة التي يفضلونها مثل: PyTorch أو TensorFlow أو Flax، ولا تنسَ إضافة بطاقة وصف دقيقة للنموذج توضح للراغبين بتحميله إمكاناته وخصائصه وشروط استخدامه. ترجمة -وبتصرف- لقسم Share a model من توثيقات Hugging Face. اقرأ أيضًا المقال السابق: تدريب المًكيَّفات PEFT Adapters بدل تدريب نماذج الذكاء الاصطناعي بالكامل. تعرف على أهم مشاريع الذكاء الاصطناعي مكتبات وأطر عمل الذكاء الاصطناعي: القوة الكامنة خلف الأنظمة الذكية المعالجة المُسبقة للبيانات قبل تمريرها لنماذج الذكاء الاصطناعي
-
نناقش في مقال اليوم طريقة استخدام توابع PEFT أو ما يعرف باسم توابع المعاملات الفعالة لصقل النماذج Parameter-Efficient Fine Tuning methods، وهي نوعٌ خاص من التوابع يُلغي أو يجمًد فعالية المعاملات الأصلية للنماذج المُدَرَّبة مسبقًا أثناء التدريب، ويستبدلها بمجموعة أصغر من المعاملات الخاصة بالمُكيّفات adapters ليجري تدريبها على المهمة المطلوبة بدلًا من تدريب النموذج كاملًا. توفر هذه الطريقة استخدام الذاكرة والموارد الحاسوبية المختلفة بشكل كبير لأن المكيّفات adapters المُدَرَّبة باستعمال توابع PEFT صغيرة الحجم مقارنة بالنماذج الكاملة، وهذا يجعلها أسهل في التحميل والتخزين والمشاركة، وهي تعطي النتائج نفسها التي نحصل عليها بتدريب كامل النموذج. ما هي المكيفات Adapters؟ المكيفات adapters هي بدائل خفيفة الوزن لتحسين نماذج الذكاء الاصطناعي المدربة مسبقًا pre-trained والمصقولة fine-tuned، فبدلاً من ضبط النموذج بالكامل، تُضاف المكيفات adapters بهيئة تعديلات صغيرة بين طبقات النموذج بعد مراحل معينة، وعند التدريب تُجَمَّدْ جميع أوزان النموذج وتُحدَّث أوزان المكيفات فقط وهو ما يؤدي إلى توفير كبير في الموارد الحاسوبية وتسريع التدريب وتحسين الأداء. ألقِ نظرة على الصورة التالية مثلًا فهي مأخوذة من مستودع النماذج، ولاحظ الفرق بين حجم مكيف النموذج OPTForCausalLM وبين حجم النموذج نفسه، إذ يبلغ حجم المكيف adapter حوالي 6 ميجا بايت بينما يصل حجم النموذج الأساسي إلى 700 ميجا. كما سيفيدك الاطلاع على PEFT إذا كنت مهتمًا بمعرفة المزيد من التفاصيل عنها. إعداد بيئة العمل ابدأ بتثبيت المكتبة PEFT من خلال الأمر التالي: pip install peft وإذا رغبت باستثمار مزاياها الحديثة أولًا بأول ثَبِّتها من المصدر الأساسي كما يلي: pip install git+https://github.com/huggingface/peft.git توافق نماذج PEFT مع مكتبة المحوّلات Transformers تدعم مكتبة المحوّلات Transformers عددًا من توابع PEFT تلقائيًا، فيمكنك تحميل أوزانها weights بسهولة سواء كانت مخزنة محليًّا أو في مستودع النماذج، ويمكن تدريبها وتشغيلها ببضع أسطر برمجية فقط، وهذه أبرزها: Low Rank Adapters. IA3. AdaLoRA. أما إذا رغبت باستخدام توابع أخرى مع مكتبة المحولات Transformers مثل: Prompt Learning و Prompt tuning فراجع توثيقات PEFT. تحميل PEFT Adapter يساعدك الصنف AutoModelFor وأصناف أخرى مشابهة على تحميل نماذج PEFT adapter من مكتبة المحوّلات Transformers واستخدامها في مشروعك، لكن تأكد من وجود الملف adapter_config.json وأوزان adapter في مجلدك المحلي أو المستودع البعيد Hub كما هو موضوح في الصورة السابقة. على سبيل المثال يمكنك تحميل نموذج PEFT adapter لمهمة النمذجة اللغوية السببية causal language modeling التي تمكنك من التنبؤ بالكلمة التالية في سلسلة نصية معينة بناءً على الكلمات السابقة باتباع التالي: حدد المُعَرِّف الخاص بنموذج PEFT الذي تريده (أي PEFT model id). مَرِّرْ هذا المُعَرِّف إلى الصنف AutoModelForCausalLM. from transformers import AutoModelForCausalLM, AutoTokenizer peft_model_id = "ybelkada/opt-350m-lora" model = AutoModelForCausalLM.from_pretrained(peft_model_id) يمكنك تحميل PEFT adapter أيضًا باستخدام الصنف AutoModelFor والصنف OPTForCausalLM والصنف LlamaForCausalLM كما يمكنك تحميله أيضًا بواسطة التابع load_adapter كما يلي: from transformers import AutoModelForCausalLM, AutoTokenizer model_id = "facebook/opt-350m" peft_model_id = "ybelkada/opt-350m-lora" model = AutoModelForCausalLM.from_pretrained(model_id) model.load_adapter(peft_model_id) التحميل بصيغة 8 بت أو 4 بت يمكنك الاستفادة من مكتبة bitsandbytes لتقليل استهلاك الذاكرة عن طريق تحميل النماذج باستخدام أنواع بيانات 8 بت و 4 بت، يساعدك هذا على توفير الذاكرة وتسريع تحميل النماذج الكبيرة، حيث يمكنك استخدام المعامل الذي تريده سواء كان load_in_8bit أو load_in_4bit ضمن محددات الدالة from_pretrained() لتختار طبيعة تحميل نموذجك، ويمكنك أيضًا ضبط المعامل device_map="auto" لتوزيع حمل النموذج بكفاءة على الموارد الحاسوبية المتاحة لك، وذلك وفق التالي: from transformers import AutoModelForCausalLM, AutoTokenizer peft_model_id = "ybelkada/opt-350m-lora" model = AutoModelForCausalLM.from_pretrained(peft_model_id, device_map="auto", load_in_8bit=True) تحميل adapter جديد يمكنك تعزيز إمكانيات نموذج الذكاء الاصطناعي من خلال تحميل مُكيَّف adapter إضافي لأي نموذج يحتوي على مُكيَّف adapter سابق لكن يشترط أن يكون من نفس نوع المُكيَّف الحالي الموجود مسبقًا في النموذج، كأن يكونا كلاهما من النوع LoRA مثلًا، وتُنجز ذلك باستخدام ~peft.PeftModel.add_adapter كما في المثال التالي: هذه الشيفرة الأصلية لتحميل مُكيَّف LoRA adapter لنموذج الذكاء الاصطناعي: from transformers import AutoModelForCausalLM, OPTForCausalLM, AutoTokenizer from peft import LoraConfig model_id = "facebook/opt-350m" model = AutoModelForCausalLM.from_pretrained(model_id) lora_config = LoraConfig( target_modules=["q_proj", "k_proj"], init_lora_weights=False ) model.add_adapter(lora_config, adapter_name="adapter_1") والآن سنضيف إليها السطر التالي لتحميل مُكيَّف LoRA adapter إضافي: # إضافة adapter جديد بنفس الإعدادات تمامًا model.add_adapter(lora_config, adapter_name="adapter_2") ثم سنستخدم ~peft.PeftModel.set_adapter لنُحدد أي adapter هو المعتمد في النموذج: # استخدام adapter_1 model.set_adapter("adapter_1") output = model.generate(**inputs) print(tokenizer.decode(output_disabled[0], skip_special_tokens=True)) # استخدام adapter_2 model.set_adapter("adapter_2") output_enabled = model.generate(**inputs) print(tokenizer.decode(output_enabled[0], skip_special_tokens=True)) طريقة تفعيل adapter معين أو إلغاء تفعيله يبين المثال التالي طريقة تفعيل adapter module: from transformers import AutoModelForCausalLM, OPTForCausalLM, AutoTokenizer from peft import PeftConfig model_id = "facebook/opt-350m" adapter_model_id = "ybelkada/opt-350m-lora" tokenizer = AutoTokenizer.from_pretrained(model_id) text = "Hello" inputs = tokenizer(text, return_tensors="pt") model = AutoModelForCausalLM.from_pretrained(model_id) peft_config = PeftConfig.from_pretrained(adapter_model_id) # تهيئة الأوزان العشوائية peft_config.init_lora_weights = False model.add_adapter(peft_config) model.enable_adapters() output = model.generate(**inputs) وهذه طريقة إلغاء التفعيل: model.disable_adapters() output = model.generate(**inputs) تدريب PEFT adapter يمكنك تدريب PEFT adapters على المهمة التي تريدها باستخدام صنف المُدَرِّب Trainer ببضع أوامر برمجية فقط، ألقِ نظرة على المثال التالي لتدريب LoRA adapter: 1. حدد ضمن إعدادات adapter المهمة التي تود تدريبه عليها، والمعاملات الفائقة hyperparameters الخاصة به وفق التالي، (يمكنك التَعرُّف على المعاملات الفائقة للنوع LoRA adapter واستخداماتها باستعراض تفاصيل ~peft.LoraConfig😞 from peft import LoraConfig peft_config = LoraConfig( lora_alpha=16, lora_dropout=0.1, r=64, bias="none", task_type="CAUSAL_LM", ) 2. أضِفْ adapter إلى النموذج: model.add_adapter(peft_config) 3. مَرِّرْ النموذج إلى المُدَرِّب: trainer = Trainer(model=model, ...) trainer.train() وفي النهاية احفظ adapter بعد التدريب وحَمِّله مجددًا كما يلي: model.save_pretrained(save_dir) model = AutoModelForCausalLM.from_pretrained(save_dir) زيادة طبقات إضافية قابلة للتدريب إلى PEFT adapter مَرِّرْ modules_to_save ضمن إعدادات PEFT لصقل fine-tune أي مُكيَّفات تود إضافتها إلى نموذجك الذي يحتوي في الأساس على adapters أخرى، كما في المثال التالي الذي يبين طريقة صقل lm_head فوق نموذج يحتوي في الأساس على LoRA adapter: from transformers import AutoModelForCausalLM, OPTForCausalLM, AutoTokenizer from peft import LoraConfig model_id = "facebook/opt-350m" model = AutoModelForCausalLM.from_pretrained(model_id) lora_config = LoraConfig( target_modules=["q_proj", "k_proj"], modules_to_save=["lm_head"], ) model.add_adapter(lora_config) الخلاصة تعرفنا في هذا المقال على المكيفات adapters وهي وحدات صغيرة تعوض عن تدريب نماذج الذكاء الاصطناعي الكاملة فتُحسِّن الأداء، وتعلمنا كيفية تحميل PEFT Adapters وإضافتها إلى نماذج مكتبة المحولات Transformers وتدريبها، وأيضًا تعرفنا على طريقة إضافة أكثر من مكيف adapter إلى النموذج لتحسين كفاءته بشرط أن تكون جميعها من نوعٍ واحد، مع إمكانية تفعيلها أو إلغاء تفعيلها حسب الحاجة. ترجمة -وبتصرف- لقسم Load adapters with PEFT من توثيقات Hugging Face. اقرأ أيضًا المقال السابق: استخدام التدريب الموزع ومكتبة Accelerate لتسريع تدريب نماذج الذكاء الاصطناعي مكتبات وأطر عمل الذكاء الاصطناعي: القوة الكامنة خلف الأنظمة الذكية خوارزميات الذكاء الاصطناعي تدريب نموذج ذكاء اصطناعي على تلخيص النصوص باستخدام سكربتات Transformers
-
كوبرنيتس Kubernetes هو نظام فعّال ومفتوح المصدر لإدارة التطبيقات المغلفة داخل الحاويات والمبنية في بيئة عنقودية (Clustered environment)، طوّرته في البداية شركة جوجل ثم حظي بدعم مؤسسة CNCF أو Cloud Native Computing Foundation، وهدفه الأساسي توفير طرق مرنة لإدارة الخدمات والمكونات الموزعة في بنية تحتية متنوعة التقنيات. فإذا كنت تتساءل ما هو Kubernetes؟ وما مكوناته الأساسية وما المشكلات التي يحلّها؟ وأي نموذج يعتمد في نشر التطبيقات وتوسعتها؟ فسيُجيبك هذا المقال على كل تساؤلاتك تلك. ما هو Kubernetes؟ كوبرنيتس Kubernetes في الأساس هو نظام لتشغيل التطبيقات المغلفة في الحاويات وتنسيقها ضمن بيئة عمل مكونة من عدة خوادم. وقد صُممت منصة Kubernetes لإدارة دورة حياة التطبيقات والخدمات المغلفة في الحاويات إدارةً كاملة وبطرق فعالة تجعلها قابلة للتنبؤ، وسهلة التوسعة، وعالية التوافرية. يُمَكِّنُك كوبرنيتس Kubernetes من تحديد كيفية تشغيل تطبيقاتك، وطريقة تفاعلها مع التطبيقات الأخرى ومع المصادر الخارجية، ويمنحك القدرة على توسيع مواردها الحاسوبية أو إنقاصها حسب الحاجة، ويسهل إجراء التحديثات التدريجية rolling updates بسلاسة دون انقطاع الخدمة، وتبديل حركة مرور البيانات بين إصداراتها المختلفة لاختبار الميزات الجديدة أو للتراجع عن عمليات النشر المصحوبة بالأخطاء أو غير ذلك. وتأتي إمكاناته هذه مما يوفره لمستخدميه من واجهات استخدام، ومنصة قوية قابلة للبناء حسب تفضيلاتهم، ومختلف الأساسيات التي يحتاجونها ليُنشئوا بيئات تطبيقاتهم كما يريدون بمرونة وموثوقية عالية. ولمزيد من المعلومات حول تقنية كوبرنتيس Kubernetes والاطلاع على أكثر الأسئلة شيوعًا حولها تتصحك بمطالعة الفيديو التالي: بنية Kubernetes سنتعرّف على بنية Kubernetes وطريقة تصميمها وتنظيمها لنفهم إمكاناته الكبيرة التي يوفرها للمطورين. يشبه Kubernetes نظامًا مكوّنًا من عدة طبقات، وتُجَرِّد كل طبقة منه التعقيدات الموجودة في الطبقات الأدنى منها. سبيل المثال تتضمن كائنات Pod (التي تمثل أصغر وحدة في Kubernetes والتي سنشرحها بعد قليل) عدة حاويات، وتشير الخدمة (Service) إلى عدة كائنات Pod وتوفر نقطة وصول واحدة إليها وقِس على ذلك، فيخفف هذا التجريد على المطوّر كمية التفاصيل التي عليه التعامل معها، فنحن هنا نتعامل مع مفاهيم منطقية دون أن نربطها بآلة فيزيائية أو افتراضية معينة. يضم Kubernetes في قاعدته عنقودًا cluster من الآلات الفيزيائية أو الافتراضية (VMs) يجمعها مع بعضها شبكة اتصال مشتركة، ويُعدّ هذا العنقود المنصة الفيزيائية التي تُضبَط فيها جميع مكونات Kubernetes، وإمكاناته، وأحمال العمل workloads، ويُقصد بأحمال العمل هنا تطبيقك الذي ستُشغله ضمن الحاوية سواء كان تطبيق ويب أو أي نوع آخر من التطبيقات. تتمتع كل آلة ضمن العنقود بدورٍ خاص في نظام Kubernetes. ويعمل خادم واحد (أو مجموعة خوادم في عمليات النشر التي تتطلب توافرية عالية) دور الخادم الرئيسي master server، فيكون بمثابة بوابة العنقود أو عقله المُدبِّر، ويؤدي مهامًا مثل: إتاحة واجهة برمجة التطبيقات Kubernetes API للمستخدمين والعملاء، والتحقق من سلامة الخوادم الأخرى وتقسيم العمل عليها (بعملية تسمى "الجدولة scheduling")، بالإضافة إلى تنظيم الاتصال بين مكونات العنقود (أو ما يُعرف بتنسيق الحاويات container orchestration). إذًا فالخادم الرئيسي هو نقطة الاتصال الأساسية مع عنقود Kubernetes والمسؤول عن معظم أعماله المركزية. تدعى الآلات الأخرى في العنقود عقدًا nodes: وهي مجموعة خوادم مسؤولة عن استقبال أحمال العمل workloads وتشغيلها باستخدام الموارد الحاسوبية المتوفرة لها المحلية والخارجية. وتُجَهَّز كل عقدة ببرنامج لتشغيل الحاويات مثل دوكر Docker أو روكيت rkt، إذ إن Kubernetes يُشغِّل التطبيقات والخدمات ضمن حاويات containers ليؤمن لها المستويات المرجوة من العزل والمرونة والإدارة الفعالة، وتتلقى العقدة تعليمات العمل الخاصة بها من الخادم الرئيسي، فتُنشئ بناءً عليها حاوياتٍ جديدة أو تُدمِّر حاوياتٍ أخرى، وتُعدِّل قواعد المرور عبر الشبكة لتُوجّه حركة البيانات حسب المطلوب. تعمل التطبيقات والخدمات على العنقود وهي ضمن الحاويات (كما ذكرنا أعلاه)، وتتأكد المكونات الأساسية باستمرار من توافق الحالة المرغوبة للتطبيقات مع الحالة الفعلية للعنقود. أما المستخدمون فيتفاعلون مع العنقود عبر الاتصال مع خادم Kubernetes API الرئيسي مباشرةً أو بواسطة تطبيقات العملاء clients والمكتبات البرمجية. والخطوة الأولى التي يبدأ بها تشغيل أي تطبيق أو خدمة هي إعداد خطة تصريحية declarative plan بصيغة JSON أو YAML تتضمن ما ينبغي إنشاؤه لتشغيل التطبيق، وكيف سيُدّار. ثم يُحلل الخادم الرئيسي هذه الخطة، ويحدد أنسب الطرق لتشغيلها على البنية التحتية المتوفرة له، وذلك عبر فحص المتطلبات اللازمة والحالة الحالية للنظام والموازنة بينهما. وتمثل مجموعة التطبيقات التي خصصها المستخدم لتعمل وفق خطة محددة الطبقة الأخيرة في نظام Kubernetes. يجعل هذا النهج التصريحي من إدارة التطبيقات المعقدة أكثر سهولة وقابلية للتوسع ويسمح للمطورين بالتركيز على وصف ما يريدونه، بينما يتولى كوبرنيتس Kubernetes تفاصيل التنفيذ. مكونات الخادم الرئيسي Master Server يتحكم الخادم الرئيسي بعناقيد Kubernetes، ويؤمن نقطة اتصال رئيسية لمدراء الأنظمة administrators والمستخدمين، ويوفر العديد من الأنظمة لإدارة العقد العاملة worker nods الأقل منه تطورًا على نطاق العنقود. بالعموم، تؤدي مكونات الخادم الرئيسي مجموعةً من الوظائف أبرزها: قبول طلبات المستخدمين، وتحديد أفضل الطرق لجدولة حاويات أحمال العمل workload containers، وإجراء المصادقة بين العملاء والعقد، وضبط قواعد التوجيه والوصول الشبكي على مستوى العنقود، وإدارة مهام التوسعة والتحقق من سلامة النظام. تُثبَّت مكونات الخادم الرئيسي (التي سنذكرها تاليًا) على خادم واحد أو على عدة خوادم حسب الحالة. المَخزَّن العام للإعدادات etcd يُعدّ المَخزَّن العام للإعدادات واحدًا من أهم المكونات الضرورية لعمل عنقود Kubernetes، يوفره مشروع etcd الذي أنشأه فريق تطوير CoreOS (وهو نظام التشغيل الموجه للحاويات ويمكنك معرفة المزيد عنه بقراءة مقدّمة إلى مكونات نظام CoreOS). و etcd هو نظام لتخزين ملفات الإعدادت وإدارتها بشكل موزع، وهو عبارة عن مَخزَنَ قيم-مفاتيح موزّع على المستوى العام لنظام Kubernetes، ويتصف بأنه خفيف في استخدام الموارد، ويمكن تهيئته لتمرير الإعدادات بين عدة عقد. إذ يُخزِّن Kubernetes بيانات الإعدادات التي يُسمح لكل عقدة بالوصول إليها في مَخزَّن etcd. وتستخدم مكونات Kubernetes هذه البيانات لاكتشاف الخدمة المطلوبة، وتهيئة نفسها للعمل حسب المعطيات الجديدة، وتساهم أيضًا في الحفاظ على حالة العنقود بواسطة ميزات، مثل: اختيار القائد leader election، والقفل الموزع distributed locking وغيرها. وتجري عملية تعيين القيم أو استردادها بواسطة واجهة برمجة تطبيقات HTTP/JSON API بسيطة تجعل العملية سلسة وواضحة. يمكن بناء etcd على خادم رئيسي واحد أو جعله موزعًا على عدة آلات في بعض بيئات الإنتاج، والشرط الوحيد هو توفر اتصال شبكي يصله بجميع آلات العنقود. خادم kube-apiserver API خادم API من أهم الخدمات المحورية على الخادم الرئيسي لعنقود Kubernetes، ويُعدّ نقطة الإدارة الرئيسية للعنقود بأكمله فهو يسمح للمستخدم بإعداد أحمال العمل والوحدات التنظيمية، والتحقق من توافق تفاصيل الخدمة على الحاويات المنشورة مع البيانات الموجودة في مخزَّن etcd. يمكنك النظر إلى خادم API مثل جسر يصل المكونات ببعضها، ويعمل للحفاظ على سلامة العنقود، ولنقل المعلومات والأوامر عبره. يطبق هذا الخادم واجهة برمجة تطبيقات من نوع RESTful API، وتضع شهرتها الواسعة العديد من المكتبات والأدوات في أيدي مستخدمي Kubernetes لتيسير عليهم التخاطب مع خادم API. يتوفر لخادم kube-apiserver عميلٌ افتراضي يدعى kubectl يتيح لك الاتصال مع عنقود Kubernetes من حاسبك المحلي. مدير التحكم kube-controller-manager مدير التحكم controller manager هو خدمة عامة تتولى عدة مسؤوليات ضمن العنقود، أولها إدارة المتحكمات المختلفة التي تنظم حالة العنقود، وإدارة دورات حياة حمل العمل workload، بالإضافة لبعض المهام الروتينية الأخرى. فعلى سبيل المثال: يفحص متحكم النظائر replication controller عدد النظائر المحددة لكائن pod معين (أي عدد تكراراته أو النسخ المماثلة له)، ويتأكد أن هذا العدد مطابقٌ للعدد الفعلي المنشور حاليًا على العنقود. تُكتب تفاصيل العمليات جميعها في etcd، ويراقب مدير التحكم التغييرات التي تطرأ على المعلومات ضمنه بواسطة خادم API. عندما يحدث أي تغيير، يقرأ المتحكم المعلومات الجديدة، ويُطبق الإجراءات اللازمة للوصول إلى الحالة المطلوبة، فقد يعمل مثلًا على توسيع نطاق التطبيق أو تقليصه، أو على ضبط نقاط الوصول الخاصة به endpoints أو غير ذلك. المُجَدّوِل kube-scheduler الجدولة هي ربط أحمال العمل بالعقد؛ فتحدد خدمة المُجَدّوِل ما ستُنفذه كل عقدة. تقرأ الخدمة متطلبات تشغيل كل حمل عمل، وتُحلل مواصفات بيئة البنية التحتية الحالية المتوفرة، وبناءً على ذلك تقرر أي عقد أو مجموعة عقد هي المقبولة لتنفيذ العمل. يتتبع المُجَدّوِل الإمكانات المتاحة على كل مضيف، ليضمن أن أحمال العمل لم تُجَدول على العقد بطريقة خاطئة تتعارض مع الموارد المتاحة. وبالتالي يعرف المُجَدّوِل في كل لحظة الموارد الإجمالية المتوفرة والموارد المخصصة حاليًّا لأحمال العمل. مدير التحكم السحابي cloud-controller-manager نظام Kubernetes قابلٌ للنشر في بيئات متعددة، ويمكنه التعامل مع خدمات البنى التحتية لمزودين مختلفين، فيأخذ معلومات عن حالة الموارد الحاسوبية التي توفرها للعنقود ويديرها حسب متطلباته. ولكونه يتعامل مع تمثيلات عامة للموارد الحاسوبية مثل: موازنات الحمل Load Balancers والحجوم التخزينية وغيرها، فإنه يحتاج لربط map هذه التمثيلات بالموارد الفعلية التي يوفرها له مزودو الخدمات السحابية المتعددون. وهنا تبرز أهمية مدير التحكم السحابي، إذ يسمح لنظام Kubernetes بالتخاطب مع مزودي خدمات مختلفين في المزايا والإمكانات وواجهات API، مع الحفاظ على بنيته الداخلية عامة نوعًا ما بالرغم من تنوع الخدمات السحابية التي يتعامل معها. يعطي ذلك Kubernetes القدرة على تحديث معلومات الحالة لديه بناءً على المعلومات التي يوفرها له المزود السحابي، ويستخدمها لضبط الموارد السحابية المخصصة له لتلبي متغيرات العمل، فقد يُنشئ مثلًا خدمات سحابية إضافية عندما تزداد احتياجات العمل المطلوبة من العنقود. مكونات خادم العقدة Node Server العقد Nodes في Kubernetes هي الخوادم التي تعمل عليها حاويات التطبيق؛ تتطلب العقد بعض الضروريات لتؤدي أعمالها في التخاطب مع مكونات الخادم الرئيسي، وضبط إعدادات الاتصال الشبكي للحاويات، وتشغيل أحمال العمل التي يكلفها بها الخادم الرئيسي. بيئة تشغيل الحاوية Container Runtime بيئة تشغيل الحاوية Container Runtime من أهم مكونات العقدة، ودوكر Docker هو من أشهر بيئات التشغيل المعتمدة، لكنه ليس الوحيد، إذ يوجد بدائل له أخرى مثل rkt المطُور من قبل CoreOS وكان يُعتبر بديلاً أكثر أماناً وقابلية للتحكم مقارنة بدوكر و runc وهو مكون منخفض المستوى يستخدمه دوكر نفسه لتنفيذ الحاويات. تتولى أدوات بيئة تشغيل الحاوية عمليات بدء تشغيل الحاويات وإدارتها، والحاويات Containers هي تطبيقات خفيفة مغلفة في بيئة تشغيل معزولة، وتُعدّ المكون الأساسي في وحدات عمل عنقود Kubernetes، فتشتمل كل وحدة عمل على حاوية واحدة ينبغي نشرها أو أكثر حسب متطلبات المشروع. وعندما ترد أحمال العمل إلى العنقود تعمل بيئة تشغيل الحاوية على تشغيل الحاويات المطلوبة لتنفيذ العمل. نقطة اتصال العقدة kubelet نقطة اتصال العقدة kubelet هي صلة الوصل التي تربط كل عقدة ببقية أجزاء العنقود. وهي خدمة صغيرة تنقل المعلومات ذهابًا وإيابًا بين العقدة وخدمات مستوى التحكم على الخادم الرئيسي، وتتخاطب أيضًا مع مخزن الإعدادات etcd لقراءة بيانات العمل منه أو كتابة بيانات جديدة فيه. تتواصل kubelet مع مكونات الخادم الرئيسي لإتمام عملية المصادقة بين العقدة والعنقود، وللحصول على الأوامر وتفاصيل الأعمال المُسندة إليها، والتي تردها بصيغة بيان manifest أو خطة تحدد حمل العمل المطلوب ومعطيات parameters التشغيل. تحافظ kubelet على حالة العمل على العقدة، وتتحكم ببيئة تشغيل الحاوية لإنشاء الحاويات أو تدميرها حسب الحاجة. الوكيل kube-proxy يستخدم الوكيل لإدارة الشبكات الفرعية داخل المضيف، وجعل الخدمات متاحة بين المكونات، وهو خدمة صغيرة تدعى kube-proxy توجد على كل عقدة، توجه الطلبات الواردة إليها إلى الحاوية المسؤولة عن تنفيذها، وتجري نوعًا مبسطًا من موازنة الحمل Load Balancing، وتضمن أن سلوك الشبكة قابلًا للتنبؤ به، وأنها متاحة أو معزولة حيث ينبغي ذلك. كائنات Kubernetes وأحمال العمل يستخدم Kubernetes طبقات تجريد abstraction إضافية فوق الحاويات -التي تُعدّ الآلية الرئيسية لنشر التطبيقات المغلفة containerized applications- توفر هذه التجريدات للمستخدمين ميزات المرونة والتوسع وإدارة دورة الحياة، فبدلًا من تعاملهم مع الحاويات مباشرةً يتفاعلون مع المثيلات instances التي تتألف من مجموعة مكونات أولية يوفرها نموذج عمل كائن Kubernetes، سنعرض في هذه الفقرة كائنات Kubernetes وكيفية استخدامها للتعامل مع أحمال الحمل. الكائنات Pods الكائن Pod هو أصغر وحدة تنظيمية يتعامل معها عنقود Kubernetes، تتكون من حاوية أو مجموعة حاويات، فعند الإعداد لا نخصص الحاويات على مضيف معين، إنما نشير إليها بكائن Pod الذي تنتمي إليه. تمثل الحاويات الموجودة في Pod واحد تطبيقًا واحدًا أي أنها تعمل معًا بارتباطٍ وثيق، فتتشارك دورة الحياة، وتُجدّوَل أعمالها على عقدة واحدة، وتُدار بصفتها وحدة واحدة، وتتقاسم بيئة التشغيل والموارد مثل: المساحات التخزينية وعناوين IP. فِكِّر دائمًا بالكائن Pod على أنه تطبيق واحد متجانس بالرغم من تعدد حاوياته، لتفهم كيف يتعامل معه العنقود، ويُدّير موارده، ويُجدّول مهامه. يحتوي كل Pod على حاوية رئيسية لا غنى عنها تلبي الهدف العام من حمل العمل workload، وقد يحتوي حاويات أخرى اختيارية تؤدي مهامًا مساعدة حسب احتياجات مشروعك، تتضمن الحاويات المساعدة عادةً برامج مستقلة تعمل وتُدار فيها باستقلالية لكنها ترتبط بالمشروع بطريقة أو بأخرى، لذا توضع معه في كائن Pod واحد. على سبيل المثال: قد يتضمن Pod معين حاويةً رئيسية لتشغيل خادم التطبيق، وحاويةً مساعدة تتحسس لأي تغييرات تحدث على مستودع خارجي متعلق به، وتسحب الملفات المرتبطة منه إلى نظام الملفات المشترك داخل Pod. لا يُنصح عادةً بالتوسع الأفقي على مستوى كائنات Pods إذ تترك هذه المهمة عادة لكائنات أخرى بمستوى أعلى. لا يُدّير المستخدمون عمومًا كائنات Pods بأنفسهم، فهي لا توفر لهم المميزات التي غالبًا ما يحتاجونها لإدارة تطبيقاتهم، مثل: الإدارة المتقدمة لدورة حياة التطبيق أو إدارة عمليات التوسعة Scaling، وبالمقابل تشجعهم بنية Kubernetes على استخدام كائنات بمستوى أعلى، تستخدم بدورها كائنات Pods أو قوالب Pods لتأدية الوظائف. متحكمات النظائر ومجموعات النظائر يعمل معظم من يستخدم Kubernetes مع نظائر متعددة للكائنات Pods مطابقة لها تمامًا، بدلًا من التعامل مع Pods مفردة. تُنشئ هذه النظائر انطلاقًا من قوالب Pod، وتوسعها أفقيًا متحكماتٌ خاصة تدعى متحكمات النظائر Replication Controllers ومجموعات النظائر Replication Sets. متحكم النظائر replication controller هو أحد كائنات Kubernetes، مسؤول عن تحديد قالب Pod الذي ستُبنى النظائر انطلاقًا منه، وأيضًا عن تحديد معطيات التحكم التي تُمَكِّنهُ من إدارة التوسعة الأفقية لنظائر Pod فيزيد عدد النظائر قيد التشغيل أو ينقصها حسب الحاجة. تمثل هذه العملية طريقةً سهلة لموازنة الحمل وتحسين التوافرية availability داخل نظام Kubernetes، فالقالب الذي ستنشئ منه النظائر الجديدة، والذي يشبه إلى حدٍ كبير تعريف للكائن Pod، يكون مضمنًا embedded في إعدادات متحكم النظائر. يتأكد متحكم النظائر باستمرار من تطابق عدد Pods المنشورة فعليًّا على العنقود مع العدد المطلوب والمحدد في إعدادته. وفي حال فشل أي Pod أو مضيف، سيستجيب المتحكم على الفور، ويبدأ بتشغيل Pods جديدة لتعويض النقص. أما إذا طرأ أي تغيير على عدد النظائر المحدد في إعداداته بالزيادة أو النقصان فسيُشغِّل المتحكم حاويات جديدة أو يدمر الحاويات الموجودة أصلًا ليصل للعدد المطلوب. يؤدي متحكم النظائر أيضًا مهمة التحديثات التدريجية rolling updates فيَنقُل مجموعة Pods إلى الإصدار الجديد بالتتابع واحدًا تلو الآخر حتى لا يسبب انقطاع كلي للخدمة فيؤثر على توافرية التطبيق. مجموعات النظائر Replication sets تتمتع بتصميم مشابه لتصميم متحكم النظائر، مع مرونة أكبر في آلية تعريف المتحكم لكائنات Pods التي سيُدِّيرها. يتسع نطاق الاعتماد على مجموعات النظائر على حساب متحكمات النظائر، بسبب إمكاناتها الكبيرة في اختيار النظائر، لكنها بالمقابل قاصرة عن إجراء التحديثات التدريجية للتطبيق وترقية واجهاته الخلفية إلى إصدار جديد كما تفعل متحكمات النظائر، فقد صممت مجموعات النظائر لتعمل في وحدات تنظيمية أخرى أعلى منها مستوى تؤدي هذه المهمة. تمامًا مثل Pods، تُعدّ متحكمات النظائر ومجموعات النظائر وحداتٍ أولية في نظام Kubernetes وناردًا ما يتعامل معها المستخدم العادي، رغم أنها توفر لتطبيقاته التوسعة الأفقية والوثوقية في العمل، إلَا أنها تفتقر لمميزات إدارة دورات الحياة بطريقة سلسة يفضلها المستخدمون والموجودة في مكونات Kubernetes الأكثر تعقيدًا. النشر Deployments يُعدّ النشر Deployments واحدًا من أشهر أحمال العمل التي تُنشئ وتُدار مباشرةً على Kubernetes. تستخدم عمليات النشر مجموعات النظائر بوصفها كتلة بناء أو مكون أساسي تُبنى منه، وهذا ما يمنح مجموعات النظائر المرونة التي تفتقدها في إدارة دورة الحياة. للوهلة الأولى قد تبدو لك عمليات النشر deployments المكونة من اجتماع عدد من مجموعات النظائر مشابهة في وظائفها لمتحكمات النظائر، إلّا أنها في الواقع تعالج العديد من نقاط ضعفها في إطلاق التحديثات. فعند استخدام المتحكمات ينبغي عليك إعداد خطة لمتحكم النظائر الجديد الذي سيحل محل المتحكم الحالي، وستكون أعمال كثيرة مثل: سجلات تتبع الأحداث، واستعادة التطبيق في حال طرأ عطل في الشبكة، والتراجع عن التحديث rolling back، عملياتٍ صعبة التنفيذ أو تلقى مسؤوليتها على المستخدم. إذًا فالنشر Deployments هو كائن عالي المستوى، ومصمم لتسهيل إدارة دورة حياة نظائر Pods، وهو أكثر الكائنات التي ستعمل معها مباشرةً في Kubernetes، إذ يمكنك تعديله بسهولة حسب ما تريد فكل ما يتطلبه الأمر تعديل إعداداته، وبعدها سيضبط Kubernetes مجموعات النظائر replica sets بناءً على إعداداتك، ويدير الانتقالات بين إصدارات التطبيق مع تفعيل اختياري لميزات مثل: سجل تتبع الأحداث والتراجع الآلي عن التحديث وغيرها. مجموعات الحالة Stateful sets مجموعات الحالة Stateful sets هي pod تحكم متخصصة نوعًأ ما، تضمن ترتيب طلبات إنشاء Pods في مجموعة الحالة، وأن يمتلك كل Pod في هذه المجموعة معرفًا فريدًا خاصًا به. وتوفر للمستخدمين تحكمًا دقيقًا في المشاريع ذات المتطلبات الخاصة سواءً على صعيد النشر أو الاستقرار الشبكي أو وجود البيانات الدائمة Persistent data التي تبقى متاحة لوقت طويل دون تغيير. فغالبًا ما ترتبط مجموعات الحالة بالتطبيقات الموجهة للبيانات، مثل قواعد البيانات التي تتطلب وصولًا دائمًا لوحدات التخزين نفسها في جميع الظروف حتى إذا أُعِيدَت جدولتها على عقدة جديدة. توفر مجموعات الحالة مُعرِّفًا شبكيًا ثابتًا لكل Pod، هو اسمٌ عددي فريد يشير إلى Pod بعينها دائمًا حتى إذا انتقلت إلى عقدة أخرى. والأمر نفسه مع وحدات التخزين الدائمة التي تنتقل مع Pod إن أُعِيدَت جدولتها لسبب ما. علماً أن وحدات التخزين الدائمة هذه تبقى موجودة ولا تحذف تلقائيًا مع حذف Pod لمنع الفقدان غير المقصود للبيانات. تنفذ مجموعات الحالة العمليات المناطة بها في أثناء النشر أو ضبط التوسعة وفقًا للمُعرِّف الفريد، ويعزز ذلك إمكانية التنبؤ والتحكم بترتيب التنفيذ، ويُعدّ مفيدًا في بعض المشاريع ذات الطبيعة الخاصة. مجموعات برامج التشغيل الخلفية Daemon Sets مجموعات البرامج الخفية Daemon sets هي نموذج متخصص آخر من متحكمات pod التي تُشَغِّل نسخة من pod معين على كل عقدة (أو مجموعة عقد مترابطة) في العنقود. وتبرز فائدتها عند نشر Pods الخدمية لعقد Kubernetes مثل Pods الصيانة وغيرها. فعلى سبيل المثال تُعدّ خدمات نحو: جمع ملفات تسجيل الأحداث وإعادة توجيهها، وتنظيم المقاييس، وتشغيل الخدمات التي تزيد من إمكانات العقد وغيرها، حالات شائعة للاستفادة من مجموعات البرامج الخفية. ولا تخضع مجموعات البرامج الخفية لقيود الجدولة المفروضة على pods العادية والتي تربطها بمضيفين معينين، ذلك أنها تقدم خدمات أساسية ومطلوبة في كامل نظام Kubernetes حتى **على الخادم الرئيسي، الذي يُعدّ محظورًا على المجدول بسبب مسؤولياته الخاصة، إلّا أن مجموعات البرامج الخلفية تتجاوز هذه القيود لتتأكد من سلامة تشغيل الخدمات الرئيسية. المهام Jobs والمهام الدورية Cron Jobs تتصف معظم أحمال العمل التي أوردناها في المقال بدورة حياة طويلة الأمد تشبه دورة حياة الخدمة، لكن Kubernetes يستخدم أيضًا نوعًا آخر من أحمال العمل يسمى المهام jobs لتأمين سير عمل أشبه بالمأمورية المؤقتة task-based تخرج فيه الحاوية من الخدمة بعد مضي بعض الوقت وانتهاء مهمتها. تساعدك كائنات المهام Jobs على تنفيذ العمليات التي لا تتطلب تشغيل خدمات دائمة، مثل: العمليات الدفعية batch أو العمليات التي تُجرى لمرة واحدة. تُبنى المهام الدورية cron jobs على المهام jobs. وهي تماثل جدولة المهام بتقنية cron في لينكس وغيره من أنظمة يونيكس المشابهة التي تُنَفَذ بموجبها السكربتات وفقًا لجدول زمني محدد، إذ توفر المهام الدورية في Kubernetes واجهة لتشغيل المهام بواسطة المجدول (مكون الخادم الرئيسي الذي ذكرناه سابقًا). تستخدم المهام الدورية لضبط تنفيذ مهمة معينة بوقت محدد في المستقبل، سواء لمرة واحدة أو لتكرار تنفيذها بانتظام. ويمكنك النظر إلى المهام الدورية في Kubernetes على أنها تطبيق لمهام cron التقليدية ولكن على منصة كاملة هي العنقود بدلًا من نظام تشغيل منفرد. مكونات أخرى لنظام Kubernetes يوفر Kubernetes عددًا من التجريدات إضافةً إلى أحمال العمل تساعدك على إدارة تطبيقاتك، والتحكم بالشبكات، وتأمين الاستمرارية وغير ذلك، سنناقش بعضًا منها أدناه: خدمات Kubernetes استخدمنا مصطلح "خدمة service" بالمعنى التقليدي كما في أنظمة يونيكس، أي للدلالة على العمليات طويلة الأمد، والتي غالبًا ما تتصل بالشبكة، وتستجيب للطلبات. إنما في Kubernetes تعمل الخدمة بصفتها موازن حمل أساسي داخلي ومرسال لوحدات pods. وتجمع الخدمة مجموعات منطقية من pods التي تؤدي الوظيفة نفسها، وتقدمها بوصفها كيان واحد. يمكنك مثلًا نشر خدمة لتتبع الحاويات الخلفية من نوع معين وتوجيه الطلبات إليها. علمًا أن كل ما يحتاج المستهلكون الداخليون لمعرفته عن الخدمة هو نقطة النهاية الثابتة التي توفرها فقط دون أي تفاصيل أخرى، يتيح لك هذا التجريد توسيع نطاق وحدات العمل العاملة في الخلفية أو استبدالها حسب الحاجة، وسيبقى عنوان IP المخصص للخدمة ثابتًا بصرف النظر عن أي تغيير يطرأ على pods المرتبطة به. يكسبك نشر الخدمات قابلية الاكتشاف ويبسيط تصميم حاوياتك. تفيدك الخدمات في الحالات التي تحتاج فيها للوصول إلى pods تطبيق آخر أو للاتصال مع مستهلكين خارجيين. فعلى سبيل المثال: إذا كان لتطبيقك عددٌ من pods العاملة على خوادم الويب ويتصل بها العملاء عن طريق الإنترنت، فإن بناء خدمة خاصة بهذه الحالة سيوفر لك التجريد اللازم. وهذا مثال آخر: لنفترض أن خوادم الويب في مشروعك بحاجة لتخزين البيانات واستردادها في قاعدة بيانات معينة، فيمكنك تأمين وصولها إلى pods قاعدة البيانات عبر بناء خدمة داخلية تتكفل بالأمر. خدمات Kubernetes متاحة افتراضيًا داخل العنقود بعنوان IP الخاص بها، وإذا أردت طلبها من الخارج فيمكنك تحقيق ذلك بعدة طرق، تتمثل إحداها بفتح منفذ ثابت static port خاص بالخدمة على واجهة الشبكة الخارجية لكل عقدة، ثم توجيه حركة المرور الواردة إليه تلقائيًا إلى عنوان IP الداخلي للخدمة، وبالتالي إلى pods المناسبة. تسمى هذه الطريقة NodePort. أو يمكنك استخدام خدمة خاصة من نوع موازن حمل LoadBalancer تُنشئ لك موازن حمل خارجي يوجه حركة البيانات نحو الخدمة الداخلية المقصودة، تُنَفَذ هذه الطريقة باستخدام تكامل موازن الحمل الخاص بمزود Kubernetes السحابي، إذ ينشئ مدير التحكم السحابي الموارد اللازمة ويربطها بعناوين خدمة موازن الحمل التي تشير لخدمتنا الداخلية المقصودة. وحدات التخزين ووحدات التخزين الدائمة تُعدّ مشاركة البيانات بين أكثر من حاوية مشاركةً موثوقة، والحفاظ عليها وضمان توفرها بين عمليات إعادة تشغيل الحاويات، تحدّيًا كبيرًا في العديد من بيئات الحاويات. وتوفر بعض بيئات تشغيل الحاويات آليات خاصة ترتبط وحدات التخزين بالحاوية، وتحافظ عليها بعد انتهاء عمر الحاوية، لكن هذه الآليات تفتقد غالبًا للمرونة في التنفيذ العملي. يستخدم Kubernetes تجريدًا خاصًا لمعالجة هذا التحدّي يدعى وحدات التخزين volumes، يسمح للحاويات المغلفة في pod واحد بمشاركة البيانات فيما بينها، والإبقاء عليها متاحة لحين انتهاء عمل pod، وحتى إذا فشلت حاوية معينة أو توقفت لسبب من الأسباب فالبيانات المشتركة ستبقى قابلة للوصول طالما أن pod موجود، وعند إنهاء عمله يُحذف المجلد المشترك. يُسهّل ذلك مشاركة الملفات بين عناصر pod الواحد دون الحاجة لأي آليات خارجية معقدة، لكنه لا يُعدّ حلًا مناسبًا للبيانات الدائمة التي لا تتعلق بعمر pod بل تستمر بعدها. لذا يوفر Kubernetes تجريدًا أقوى لتخزين البيانات الدائمة، يسمى وحدات التخزين الدائمة Persistent volumes، لا يرتبط بدورة حياة pod، ويسمح لمديري النظام بإعداد موارد التخزين على مستوى العنقود ليتمكن المستخدمون من حجزها لكائنات pod التي يُشغِّلونها حسب تفضيلاتهم. ويحدد كل مستخدم سياسة الاسترداد الخاصة به، التي تقرر فيما إذا كانت وحدة التخزين وبياناتها ستُحذف يدويًا من قبله أم آليًّا عند انتهاء عمر pod. يخفف استخدام وحدات التخزين الدائمة من المخاطر المترتبطة بحالات فشل العقدة، ويساعدك على زيادة مساحات التخزين المخصصة لصالح مشروعك عمّا هو متاحٌ له محليًّا على المضيف. التعليقات التوضيحية Annotations والتسميات التوضيحية Labels التسميات التوضيحية label هي تجريد تنظيمي في Kubernetes ينظم المفاهيم الأخرى في النظام ويوضحها، وهي علاماتٌ دلالية ترفق بكائنات Kubernetes لوسمها بوسمٍ خاص يشير لانتمائها لمجموعة معينة، أي أنها تُستَخدم لتصنيف الكائنات ولاستهداف مثيلات مختلفة لأغراض الإدارة أو التوجيه أو غيرها، فمثلًا تعتمد الخدمات على التسميات التوضيحية لمعرفة pods الواجهة الخلفية التي ينبغي توجيه الطلبات إليها، وكذلك الأمر مع كائنات التحكم التي تستعمل التسميات التوضيحية لتحديد pods التي ستعمل عليها. تُوضع التسميات التوضيحية بهيئة أزواج مفتاح-قيمة، ويمكن أن تحظى الوحدة التنظيمية عدة لصائق، لكنها لا تحمل أبدًا إلّا بقيمة واحدة لكل مفتاح. ومفتاح "الاسم name" هو أشهر المفاتيح، ويُعدّ مُعَرِّفًا عامًا متعدد الأغراض، يمكنك أيضًا تصنيف الكائنات وفق معايير أخرى حسب احتياجات عملك، مثل: مرحلة التطوير، وإمكانية الوصول العام، وإصدار التطبيق… إلخ. تشبه **التعليقات التوضيحية Annotations ** التسميات التوضيحية لكنها تترك لك الحرية في إرفاق أي تعليق تريده ضمن خانة القيمة للدلالة على الكائن ما دون الالتزام بمعايير محددة، في حين تفرض عليك التسميات التوضيحية قواعد معينة في كتابة المعلومات الدلالية لتجري مطابقتها عند اختيار pod. باختصار تساعدك التعليقات التوضيحية في إضافة بيانات تعريفية غنية للكائن لكنها لا تفيدك في تصنيفه أو تحديده لأمرٍ معين. الخلاصة يتيح Kubernetes لمستخدميه تشغيل مشاريعهم المغلفة في الحاويات على منصة قوية وعالية التجريد، وبطريقة تكفل لهم قابلية التوسع والتوافرية العالية. وعندما تبدأ بالتعرف عليه ستبدو لك بنيته معقدة، ويصعب التعامل معها، لكن ما أن تفهم أساسياتها وكيفية توافق عناصرها معًا ستشعر بمرونتها وفائدتها الكبيرة للمشاريع مفتوحة المصدر والتطوير السحابي الأصلي، وبمزيد من التدريب ستصبح قادرًا على استثمار ميزاتها المتنوعة لتشغيل أحمال العمل وإدارتها على نطاق واسع، ولإنشاء تطبيقاتك السحابية الأصلية. ترجمة -وبتصرف- للمقال ?What is Kubernetes لصاحبه Justin Ellingwood. اقرأ أيضًا تعلم أساسيات Kubernetes نشر تطبيق جانغو آمن وقابل للتوسيع باستخدام كوبيرنتس Kubernetes مدخل إلى Kubernetes ما الفرق بين دوكر Docker وكوبيرنيتيس Kubernetes؟
-
يُعدّ التدريب الموزع أو التدريب على التوازي استراتيجيةً ناجحة في تدريب نماذج الذكاء الاصطناعي كبيرة الحجم على أجهزة حاسوبية محدودة الموارد، ويزداد الاعتماد عليها يومًا بعد يوم مع ازدياد أحجام النماذج المطروحة، وفي هذا المجال توفر منصة Hugging Face لمستخدميها مكتبةً متخصصة تدعى Accelerate تُسَهِّل عليهم تدريب نماذج مكتبة المحولات Transformers تدريبًا موزعًا بأنواعٍ مختلفة من الإعدادات مثل استخدام عدة وحدات معالجة رسومية (GPU) سواء كانت موجودة على الجهاز نفسه أو موزعة على عدة أجهزة، فكيف نطبق هذا الأسلوب عمليًّا على حلقات تدريب PyTorch الأصلي للاستفادة منه في تدريب نماذج الذكاء الاصطناعي في البيئات الحاسوبية ذات الموارد الموزعة؟ إعداد بيئة العمل ابدأ أولًا بتثبيت المكتبة Accelerate كما يلي: pip install accelerate ثم استورد كائن المُسَرِّع Accelerator وأنشئ واحدًا في مشروعك، وسيكتشف المُسَرِّع نوعية الإعدادات الموزعة المتوفرة لديك ويهيئ لك المكونات الضرورية للتدريب اعتمادًا عليها، ولا حاجة لتخزين النموذج على جهازك: >>> from accelerate import Accelerator >>> accelerator = Accelerator() التحضير لعملية التسريع Accelerate خطوتنا التالية هي تمرير كافة الكائنات المرتبطة بالتدريب إلى تابع التحضير accelerator.prepare والتي تشمل مُحَمِّلات البيانات DataLoaders لكل من مرحلتي التدريب training والتقييم evaluation بالإضافة إلى النموذج model والمُحَسِّن optimizer على النحو التالي: train_dataloader, eval_dataloader, model, optimizer = accelerator.prepare( train_dataloader, eval_dataloader, model, optimizer ) ملاحظة: إن مهمة التابع prepare هنا هي تجهيز جميع مكونات التدريب للعمل في بيئة موزعة، وضمان توافق جميع المكونات مع بعضها البعض. استبدال الدالة Backward وأخيرًا علينا استبدل الدالة loss.backward() المعتادة في حلقة التدريب بدالة Backward الخاصة بمكتبة Accelerate لضمان حساب التدرجات الخلفية (backward pass) بشكل صحيح على الأجهزة الموزعة على النحو التالي: for epoch in range(num_epochs): for batch in train_dataloader: outputs = model(**batch) loss = outputs.loss accelerator.backward(loss) optimizer.step() lr_scheduler.step() optimizer.zero_grad() progress_bar.update(1) إذًا يمكنك تحويل حلقة التدريب العادية إلى حلقة تدريب موزعة بإضافة أربعة أوامر برمجية فقط إلى حلقة التدريب (ميَّزناها هنا بإشارة "+")، وبحذف أمرين فقط (ميَّزناهما بإشارة "-"): + from accelerate import Accelerator from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler + accelerator = Accelerator() model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2) optimizer = AdamW(model.parameters(), lr=3e-5) - device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") - model.to(device) + train_dataloader, eval_dataloader, model, optimizer = accelerator.prepare( + train_dataloader, eval_dataloader, model, optimizer + ) num_epochs = 3 num_training_steps = num_epochs * len(train_dataloader) lr_scheduler = get_scheduler( "linear", optimizer=optimizer, num_warmup_steps=0, num_training_steps=num_training_steps ) progress_bar = tqdm(range(num_training_steps)) model.train() for epoch in range(num_epochs): for batch in train_dataloader: - batch = {k: v.to(device) for k, v in batch.items()} outputs = model(**batch) loss = outputs.loss - loss.backward() + accelerator.backward(loss) optimizer.step() lr_scheduler.step() optimizer.zero_grad() progress_bar.update(1) التدريب بعد إضافة الأسطر البرمجية السابقة لحلقة التدريب يمكنك البدء بتدريب نموذجك إما بتشغيل السكريبت لديك أو بكتابته في Colaboratory notebook أو أي دفتر ملاحظات مشابه. التدريب باستخدام السكربت إذا كنت تُدَرِّب نموذجك اعتمادًا على سكريبت، فاكتب الأمر التالي لإنشاء ملف الإعدادات وحفظه: accelerate config ثم باشر بالتدريب بكتابة الأمر التالي: accelerate launch train.py التدريب باستخدام دفتر الملاحظات Notebook يمكنك تشغيل مكتبة Accelerate داخل دفتر ملاحظات مثل Colaboratory notebook أو غيره إذا كنت ترغب باستخدام وحدات TPU (أي وحدات معالجة الموتر أو التنسور tensor) الخاصة بخدمة Colaboratory، لكن عليك في هذه الحالة وضع جميع تعليمات التدريب في دالة واحدة وتمريرها إلى notebook_launcher كما يلي: >>> from accelerate import notebook_launcher >>> notebook_launcher(training_function) الخلاصة تعرفنا في مقال اليوم على الخطوات الأساسية لاستخدام المكتبة Accelerate في تسريع تدريب نماذج الذكاء الاصطناعي باستخدام التدريب الموزع وهي طريقة مفيدة لتدريب نماذج الذكاء الاصطناعي الكبيرة على أجهزة محدودة الموارد، ويمكنك التعمق أكثر في خياراتها المختلفة بمطالعة توثيقاتها الرسمية. ترجمة -وبتصرف- لقسم Distributed training with Accelerate من توثيقات Hugging Face. اقرأ أيضًا المقال السابق: تدريب نموذج ذكاء اصطناعي على تلخيص النصوص باستخدام سكربتات Transformers أدوات برمجة نماذج تعلم الآلة مكتبات وأطر عمل الذكاء الاصطناعي: القوة الكامنة خلف الأنظمة الذكية كيفية تهيئة تطبيق المفكرة Jupyter Notebook للعمل مع لغة البرمجة Python
-
تزخر مكتبة المحوّلات Transformers بالسكربتات التوضيحية والأمثلة التي تشرح تدريب نماذج الذكاء الاصطناعي على مختلف المهام بأطر العمل الشهيرة بايتورش PyTorch وتنسرفلو TensorFlow و JAX/Flax، وترتبط هذه الأمثلة ببعض المشاريع البحثية والمساهمات القديمة لمجتمع Hugging Face، لكن لن تعمل هذه الأمثلة بدقة تامة في مشروعك من دون بعض التعديلات فهي في نهاية الأمر لا تخضع للصيانة والتحديث الدوري ومعظمها لا يتوافق مع الإصدارات الحديثة من مكتبة المحوّلات، لذا ستحتاج لتكييفها مع مشروعك. وبعد سحب الملف من GitHub يمكنك متابعة قراءة هذا المقال لتتعلم كيفية الاستفادة من السكربتات التدريبية الموجود على منصة Hugging Face وتكييفها لمشروعك، اخترنا هنا سكريبت بسيط لمهمة تلخيص النصوص وسنعرض طريقة التعامل معه في كل من إطاري العمل PyTorch و TensorFlow. إعداد بيئة العمل الخطوة الأولى لتشغيل أحدث إصدار من السكربتات التوضيحية بنجاح هي تثبيت مكتبة المحوّلات Transformers من المصدر في بيئة افتراضية جديدة كما يلي: git clone https://github.com/huggingface/transformers cd transformers pip install . وإذا أردت استخدام أمثلة تناسب الإصدارات القديمة من مكتبة المحوّلات Transformers مثل الإصدار v.4.5.1 يمكنك الاطلاع على الرابط وبنفس الطريقة للإصدارات الأخرى. ثم بَدِّل نسحتك من المكتبة إلى الإصدار الذي اخترته حسب المثال وليكن فرضًا الإصدار v3.5.1: git checkout tags/v3.5.1 بعد ضبط الإصدار الصحيح للمكتبة، انتقل إلى مجلد المثال الذي اخترته وثَبِّت متطلباته كما يلي: pip install -r requirements.txt تشغيل السكربت التوضيحي يبدأ السكربت بتحميل مجموعة بيانات dataset من مكتبة مجموعات البيانات Datasets ومعالجتها معالجةً مسبقة، ويستخدم بعدها المُدَرِّب Trainer لضبط مجموعة البيانات على بنية مناسبة لمهمة التلخيص summarization. يبين المثال التالي كيفية صقل النموذج T5-small وتدريبه بدقة fine-tuning على مجموعة البيانات CNN/DailyMail، لاحظ وجود الوسيط source_prefix وهو أحد متطلبات للنموذج T5 ويتيح له معرفة طبيعة المهمة التي سيستخدم لأجلها أي التلخيص في حالتنا، لنعرض السكربت الآن بإطاري العمل: أولًا باستخدام إطار العمل Pytorch: python examples/pytorch/summarization/run_summarization.py \ --model_name_or_path google-t5/t5-small \ --do_train \ --do_eval \ --dataset_name cnn_dailymail \ --dataset_config "3.0.0" \ --source_prefix "summarize: " \ --output_dir /tmp/tst-summarization \ --per_device_train_batch_size=4 \ --per_device_eval_batch_size=4 \ --overwrite_output_dir \ --predict_with_generate ثانيًا باستخدام إطار العمل TensorFlow: python examples/tensorflow/summarization/run_summarization.py \ --model_name_or_path google-t5/t5-small \ --dataset_name cnn_dailymail \ --dataset_config "3.0.0" \ --output_dir /tmp/tst-summarization \ --per_device_train_batch_size 8 \ --per_device_eval_batch_size 16 \ --num_train_epochs 3 \ --do_train \ --do_eval التدريب الموزع والدقة المختلطة يدعم المُدَرِّب ميزة التدريب الموزع distributed training أي توزيع التدريب على أكثر من وحدة معالجة، وميزة الدقة المختلطة mixed precision التي تتيح إمكانية العمل على 32 بت أو 16 بت للتسريع، يمكنك تفعيل الميزتين ضمن السكربت بضبط قيم الوسيطين التاليين: أضف الوسيط fp16 لتفعيل ميزة الدقة المختلطة. حدد عدد وحدات المعالجة الرسومية (GPUs) التي تريد استخدامها ضمن الوسيط nproc_per_node. torchrun \ --nproc_per_node 8 pytorch/summarization/run_summarization.py \ --fp16 \ --model_name_or_path google-t5/t5-small \ --do_train \ --do_eval \ --dataset_name cnn_dailymail \ --dataset_config "3.0.0" \ --source_prefix "summarize: " \ --output_dir /tmp/tst-summarization \ --per_device_train_batch_size=4 \ --per_device_eval_batch_size=4 \ --overwrite_output_dir \ --predict_with_generate أما إطار TensorFlow فيستخدم التدريب الموزع افتراضيًا باستراتيجية خاصة تدعى MirroredStrategy ويوزع العمل على كافة وحدات GPU المتوفرة للتدريب من دون الحاجة لضبط أي وسيط إضافي بهذا الخصوص ضمن السكربت. تشغيل السكربت باستخدام وحدات TPU إن تسريع الأداء هو الهدف الكامن وراء تصميم وحدات معالجة المُوترات أو التنسورات Tensors التي تدعى اختصارًا TPUs، ويساعدك مترجم التعلُّم العميق XLA compiler على استخدامها مع إطار العمل PyTorch (يمكنك معرفة المزيد عنها بمطالعة هذا الرابط). الآن افتح السكربت xla_spawn.py وحدد عدد نوى معالجة المُوترات TPU cors التي تود استخدامها بذكرها ضمن الوسيط num_cores وفق التالي: python xla_spawn.py --num_cores 8 \ summarization/run_summarization.py \ --model_name_or_path google-t5/t5-small \ --do_train \ --do_eval \ --dataset_name cnn_dailymail \ --dataset_config "3.0.0" \ --source_prefix "summarize: " \ --output_dir /tmp/tst-summarization \ --per_device_train_batch_size=4 \ --per_device_eval_batch_size=4 \ --overwrite_output_dir \ --predict_with_generate أما إطار العمل TensorFlow فيستخدم استراتيجيةً اسمها TPUStrategy لتدريب النماذج اعتمادًا على وحدات TPU، ويتعين عليك تحديد اسم المورد الحاسوبي resource الذي ستستخدمه ضمن الوسيط tpu في السكربت كما يلي: python run_summarization.py \ --tpu name_of_tpu_resource \ --model_name_or_path google-t5/t5-small \ --dataset_name cnn_dailymail \ --dataset_config "3.0.0" \ --output_dir /tmp/tst-summarization \ --per_device_train_batch_size 8 \ --per_device_eval_batch_size 16 \ --num_train_epochs 3 \ --do_train \ --do_eval تشغيل السكربت مع المكتبة Accelerate مكتبة Accelerate هي مكتبة خاصة بإطار العمل بايتورش PyTorch فقط، حيث توفر أسلوبًا موحدًا لتدريب نموذج الذكاء الاصطناعي نفسه بإعدادات مختلفة (سواء بالاعتماد على وحدة المعالجة المركزية CPU فقط، أو على أكثر من وحدة معالجة رسومية GPU، أو على وحدات معالجة الموترات TPU) مع الحفاظ على وضوح حلقة التدريب، لكن في البداية ينبغي لنا التأكد من تثبيت المكتبة Accelerate: تنويه: تتطور مكتبة Accelerate باستمرار لذا احرص على تثبيتها من مستودعها على git. pip install git+https://github.com/huggingface/accelerate سنستخدم الآن السكربت run_summarization_no_trainer.py الذي يتضمن الإعدادات الخاصة باستخدام مكتبة Accelerate بدلًا من السكربت السابق run_summarization.py، يمكنك تمييز السكربتات الخاصة بمكتبة Accelerate بوجود الملف Task_no_trainer.py ضمن مجلداتها. ابدأ بتنفيذ الأمر التالي الخاص بإنشاء ملف الإعدادات وحفظه: accelerate config ثم اختبر ملف الإعدادات للتأكد من سلامته: accelerate test أصبحنا جاهزين لبدء التدريب: accelerate launch run_summarization_no_trainer.py \ --model_name_or_path google-t5/t5-small \ --dataset_name cnn_dailymail \ --dataset_config "3.0.0" \ --source_prefix "summarize: " \ --output_dir ~/tmp/tst-summarization استخدام مجموعة بيانات مخصصة مع سكربت التلخيص يمكنك استخدام مجموعة بيانات مخصصة مع سكربت التلخيص التوضيحي الذي اخترناه هنا، بشرط أن تكون بصيغة CSV أو JSON وستحتاج أيضًا لضبط بعض الوسطاء الإضافية لتتناسب مع مجموعة بياناتك وفق التالي: يحدد كل من الملف Train_file وملف validation_file مسار حفظ ملفات التدريب والتحقق على التوالي. يشير text_columnd إلى الدخل أو النص المطلوب تلخيصه. يَدُّل على الخرج ٍSummary_column على الخرج أو النص الهدف بعد التلخيص. وستكون الصيغة النهائية لسكربت التلخيص مع استخدام مجموعة بيانات مخصصة على النحو التالي: python examples/pytorch/summarization/run_summarization.py \ --model_name_or_path google-t5/t5-small \ --do_train \ --do_eval \ --train_file path_to_csv_or_jsonlines_file \ --validation_file path_to_csv_or_jsonlines_file \ --text_column text_column_name \ --summary_column summary_column_name \ --source_prefix "summarize: " \ --output_dir /tmp/tst-summarization \ --overwrite_output_dir \ --per_device_train_batch_size=4 \ --per_device_eval_batch_size=4 \ --predict_with_generate اختبار السكربت على عينة من البيانات يُعدّ اختبار السكربتات على عينة صغيرة من مجموعة البيانات أسلوبًا ناجحًا للتأكد من صحتها وفعاليتها قبل تطبيقها على كامل البيانات، فتجريب السكربت على dataset كاملة يستغرق وقتًا طويلًا ربما ساعات قبل حصولك على النتيجة، ويساعدك الوسطاء الثلاثة الموجودين أدناه على اقتطاع جزء من مجموعة البيانات حسب قيمة كل وسيط ليُنَفَذ السكربت على جزء العينات المقتطع فقط من مجموعة البيانات بدلًا من تنفيذه على كامل المجموعة: max_train_samples. max_eval_samples. max_predict_samples. python examples/pytorch/summarization/run_summarization.py \ --model_name_or_path google-t5/t5-small \ --max_train_samples 50 \ --max_eval_samples 50 \ --max_predict_samples 50 \ --do_train \ --do_eval \ --dataset_name cnn_dailymail \ --dataset_config "3.0.0" \ --source_prefix "summarize: " \ --output_dir /tmp/tst-summarization \ --per_device_train_batch_size=4 \ --per_device_eval_batch_size=4 \ --overwrite_output_dir \ --predict_with_generate ملاحظة: لا تتيح جميع السكربتات التوضيحية الموجودة في مكتبة المحوّلات إمكانية استخدام الوسيط max_predict_samples لذا ننصحك بالتحقق أولًا من توفر هذه الإمكانية في السكربت بإضافة الوسيط h- وفق التالي: examples/pytorch/summarization/run_summarization.py -h استئناف التدريب من نقطة تحقق سابقة قد يتوقف تدريب النموذج لأسباب غير متوقعة؛ هل سبق أن تعرضت لذلك؟ يساعدك خيار استئناف التدريب من نقطة تحقق checkpoint سابقة على متابعة تدريب نموذجك من حيث توقف دون الحاجة لإعادته من البداية، ويوجد طريقتان لإجراء ذلك: سنستخدم في الطريقة الأولى الوسيط output_dir Previous_output_dir لاستئناف التدريب من أحدث نقطة تحقق متوفرة ومخزنة في المجلد output_dir، لكن علينا حذف الأمر overwrite_output_dir من السكربت في هذه الحالة: python examples/pytorch/summarization/run_summarization.py --model_name_or_path google-t5/t5-small \ --do_train \ --do_eval \ --dataset_name cnn_dailymail \ --dataset_config "3.0.0" \ --source_prefix "summarize: " \ --output_dir /tmp/tst-summarization \ --per_device_train_batch_size=4 \ --per_device_eval_batch_size=4 \ --output_dir previous_output_dir \ --predict_with_generate وفي الطريقة الثانية سيُستأنف التدريب انطلاقًا من نقطة التحقق الموجودة في مجلد محدد وذلك باستخدام الوسيط resume_from_checkpoint path_to_specific_checkpoint وفق التالي: python examples/pytorch/summarization/run_summarization.py --model_name_or_path google-t5/t5-small \ --do_train \ --do_eval \ --dataset_name cnn_dailymail \ --dataset_config "3.0.0" \ --source_prefix "summarize: " \ --output_dir /tmp/tst-summarization \ --per_device_train_batch_size=4 \ --per_device_eval_batch_size=4 \ --overwrite_output_dir \ --resume_from_checkpoint path_to_specific_checkpoint \ --predict_with_generate شارك نموذجك يمكنك أن ترفع السكربتات التوضيحية لنموذجك النهائي إلى مستودع النماذج Model Hub على منصة Hugging Face بعد انتهائك من استخدام السكربت، لكن عليك أولًا تسجيل حساب على المنصة وفق التالي: huggingface-cli login ثم إضافة الوسيط Push_to_hub إلى السكربت، يؤدي ذلك إلى إنشاء مستودع خاص بالمستخدم على Hugging Face يُسمى باسم الدخول الخاص به (أي username) مع اسم المجلد المُحدد في put_dir. يمكنك تغيير آلية التسمية واختيار الاسم الذي تريده لمستودعك باستخدام الوسيط Push_to_hub_model_id وسيُدرج تلقائيًا تحت المساحة الاسمية namespace المخصصة لك على المنصة. يبين المثال التالي طريقة تحميل النموذج إلى مستودعٍ محدد: python examples/pytorch/summarization/run_summarization.py --model_name_or_path google-t5/t5-small \ --do_train \ --do_eval \ --dataset_name cnn_dailymail \ --dataset_config "3.0.0" \ --source_prefix "summarize: " \ --push_to_hub \ --push_to_hub_model_id finetuned-t5-cnn_dailymail \ --output_dir /tmp/tst-summarization \ --per_device_train_batch_size=4 \ --per_device_eval_batch_size=4 \ --overwrite_output_dir \ --predict_with_generate الخلاصة شرحنا في مقال اليوم كيفية تدريب نموذج ذكاء اصطناعي على مهمة تلخيص النصوص باستخدام مكتبة المحولات Transformers وشرحنا خطوات إعداد بيئة العمل بداية من تثبيت هذه المكتبة من المصدر، ثم تشغيل سكربت تلخيص نصوص بسيط باستخدام إطاري عمل PyTorch و TensorFlow، كما وضحنا تقنيات تدريب وتسريع وتكييف السكربت ومشاركة النموذج المدرب على منصة Hugging Face. ترجمة -وبتصرف- لقسم Train with a script من توثيقات Hugging Face. اقرأ أيضًا المقال السابق: طريقة الصقل Fine-Tune لنموذج ذكاء اصطناعي مُدَرَّبْ مُسبقًا المعالجة المُسبقة للبيانات قبل تمريرها لنماذج الذكاء الاصطناعي تلخيص النصوص باستخدام الذكاء الاصطناعي أهم مشاريع عملية عن الذكاء الاصطناعي مصطلحات الذكاء الاصطناعي للمبتدئين
-
يُعرّف سجل دوكر Docker Registry بأنه تطبيق يُدّير عمليات تخزين صور حاويات Docker وتسليمها للمطورين، حيث تكون صور الحاويات متاحة أمامهم في سجلٍ مركزي واحد ومُضمّن فيها جميع المكونات الضرورية لعملها، وفي هذا استثمارٌ كبير لوقت المطوّر، إذ تكفل صور دوكر بيئة تشغيل مماثلة لمتطلباته عبر المحاكاة الافتراضية، وبهذا يمكن للمطور سحب الصورة التي يحتاجها من السجل مع كل ما يلزم وتنزيلها بهيئة مضغوطة، بدلًا من تنزيل الاعتماديات والحزم واحدة واحدة وتثبيتها داخل الحاوية في كل مرة، وبالمثل أيضًا يستطيع المطوّر أتمتة عمليات نشر الصور على السجل باستخدام أدوات التكامل المستمر (CI) مثل TravisCI أو غيرها لتحديث صوره باستمرار في مراحل التطوير والإنتاج. قد تكون سجلات Docker عامة أو خاصة ولعل Docker Hub هو أبرز مثال على سجلات دوكر العامة، فهو مجاني ومتاح للجميع ويمكنك تخصيص صورك حسب احتياجات عملك واستضافتها عليه، وإذا رغبت بمستوى أعلى من السرية والخصوصية فيمكنك استخدام سجل خاص بك فهو الخيار الأفضل للتطبيقات مغلقة المصدر، إذ تتضمن الصور عادةً جميع التعليمات البرمجية اللازمة لعمل التطبيق، ويبقيها السجل الخاص في متناول مجموعة محددة من الأشخاص فقط. سنشرح لك في هذا المقال كيفية إعداد سجلك الخاص لصور حاويات Docker، وطريقة تأمينه، واستخدام كل من Docker Compose لضبط إعدادات التحكم بتشغيل الحاويات، وخادم Nginx لتوجيه حركة مرور البيانات القادمة من الإنترنت إلى حاوية Docker قيد التشغيل. وستمتلك في النهاية المعرفة الكافية لرفع صورة Docker إلى سجلك الخاص، وسحب الصور بأمان من خادمٍ بعيد. متطلبات العمل ستحتاج لتوفير المتطلبات التالية لتتابع معنا سير العمل خطوة بخطوة: خادمين مثبت عليهما نظام تشغيل أوبنتو (الإصدار 22.04 )، ويمكنك اتباع هذا الدليل لتجهيزهما، حيث يتضمن هذا الدليل طريقة إنشاء مستخدم جديد غير مستخدم الجذر لكنه يتمتع بصلاحيات sudo بالإضافة إلى آلية إعداد جدار الحماية للخادم، احرص على تنفيذ هذه الخطوات إذ سيستضيف أحد الخادمين سجل Docker الخاص بك وسنسميه الخادم المضيف host، وسيكون الخادم الآخر عميلًا client يستخدم السجل. تثبيت Docker على الخادمين، تساعدك الخطوتان 1 و 2 من مقال كيفية تثبيت دوكر واستخدامه على دبيان في تنفيذ المطلوب فتثبيت Docker على دبيان يشبه تثبيته على أوبنتو. اضبط بعد ذلك الإعدادات التالية على الخادم المضيف: ثبّت عليه دوكر كومبوزر Docker Compose مستعينًا بالخطوات الواردة في مقال تثبيت الأداة دوكر كومبوز Docker Compose واستخدامها ضمن نظام لينكس أوبونتو. ثبّت عليه إنجن إكس Nginx بإتباع الإرشادات الواردة في مقال كيفية تثبيت Nginx على أوبونتو 18.04. أمّن حماية الخادم Nginx الموجود على المضيف بشهادات مصدقة من Let’s Encrypt مثلًا لحماية سجل Docker الخاص الذي تنشؤه، اطلّع على مقال كيف تؤمّن خادم ويب NGINX على أوبنتو 16.04 على أكاديمية حسوب أو مقال تأمين خادم NGINX باستخدام Let’s Encrypt على DigitalOcean لإنجاز هذه الخطوة. وتأكد من توجيه حركة مرور البيانات الواردة إلى تطبيقك من HTTP إلى HTTPS. احجز اسم نطاق لخادمك المضيف لسجل Docker، واسم النطاق المعتمد في المقال هو your_domain، وتوفير اسم النطاق ضروري قبل البدء بإعدادات خدمة Let’s Encrypt. الخطوة 1: تثبيت سجل Docker وإعداده يفيدك تشغيل Docker من سطر الأوامر في بداية التشغيل وعند اختبار الحاويات، لكن استمرارك باستخدامه من سطر الأوامر لن يكون عمليًّا مع تقدم سير العمل، وبالأخص في عمليات النشر واسعة النطاق أو تلك التي تتطلب التحكم بعدة حاويات تعمل معًا على التوازي. لذا يستخدم المطورون أداة Docker Compose، حيث تكتب ملف yml. واحد لكل حاوية يتضمن إعداداتها والمعلومات التي تحتاجها للتواصل مع بقية الحاويات. ويمكنك استخدام تعليمة docker compose لتطبيق الأوامر دفعةً واحدة على جميع الأجزاء المكوّنة لتطبيقك والتحكم بها على أنها مجموعة. تُستخدم أداة Docker Compose أيضًا لإدارة سجل دوكر ومكوناته فهو تطبيق في نهاية الأمر ويتكون من عدة أجزاء. لتشغيل مثيل instance لسجل Docker على Docker Compose عليك إعداد الملف docker-compose.yml لتعريف السجل، وتوفير مساحة تخزينية له على القرص الصلب ليُخزّن البيانات. أنشئ في البداية مجلدًا اسمه docker-registry على الخادم المضيف وفق التالي، إذ سنُخزّن فيه إعدادات السجل: $ mkdir ~/docker-registry انتقل إلى المجلد الجديد بكتابة التالي: $ cd ~/docker-registry وأنشئ بداخله مجلدًا فرعيًّا باسم data، وفق الأمر التالي حيث سيُخزّن السجل صور الحاويات بداخله: $ mkdir data أنشئ أيضًا ملفًا نصيًّا باسم docker-compose.yml باستعمال محرر نصوص مثل نانو كما يلي: $ nano docker-compose.yml واكتب ضمنه المعلومات التالية، التي تُعرّف المثيل الأساسي لسجل Docker: version: '3' services: registry: image: registry:latest ports: - "5000:5000" environment: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data volumes: - ./data:/data لنشرح المعلومات السابقة، عرّفنا في البداية خدمةً جديدة باسم registry، وحددنا الصورة التي ستُبنى انطلاقًا منها وهي registry، أما الكلمة latest فتعني أنك تطلب استخدام أحدث إصدار متوفر من الصورة، وفي القسم التالي وجهّنا المنفذ port رقم 5000 على الخادم المضيف إلى المنفذ 5000 على الحاوية، وذلك يعني أن الطلبات الواردة إلى المنفذ 5000 على الخادم سترسل مباشرةً إلى السجل. أما في قسم بيئة العمل environment فقد أسندنا القيمة data/ للمتغير REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY وهو المسؤول عن تحديد المجلد المخصص لتخزين بيانات السجل. وفي القسم volumes وصلنا المجلد data/ على نظام ملفات الخادم المضيف بالمجلد data/ داخل الحاوية، الذي يُعدّ بمثابة معبر فقط إذ ستُخزّن البيانات فعليًّا على الخادم المضيف. احفظ التغييرات على الملف، وأغلقه وشغّل الآن الإعدادات بتنفيذ الأمر التالي: $ docker compose up سيبدأ تحميل حاوية السجل مع اعتمادياتها، وستصبح في وضع التشغيل، ثم ستحصل على خرج يشبه التالي: [+] Running 2/2 ⠿ Network docker-registry_default Created 0.1s ⠿ Container docker-registry-registry-1 Created 0.1s Attaching to docker-registry-registry-1 docker-registry-registry-1 | time="2024-01-19T14:31:20.40444638Z" level=warning msg="No HTTP secret provided - generated random secret. This may cause problems with uploads if multiple registries are behind a load-balancer. To provide a shared secret, fill in http.secret in the configuration file or set the REGISTRY_HTTP_SECRET environment variable." go.version=go1.16.15 instance.id=4fb8d420-eaf8-4a69-b740-bdc94fa52d91 service=registry version="v2.8.1+unknown" docker-registry-registry-1 | time="2024-01-19T14:31:20.404960549Z" level=info msg="redis not configured" go.version=go1.16.15 instance.id=4fb8d420-eaf8-4a69-b740-bdc94fa52d91 service=registry version="v2.8.1+unknown" docker-registry-registry-1 | time="2024-01-19T14:31:20.412312462Z" level=info msg="using inmemory blob descriptor cache" go.version=go1.16.15 instance.id=4fb8d420-eaf8-4a69-b740-bdc94fa52d91 service=registry version="v2.8.1+unknown" docker-registry-registry-1 | time="2024-01-19T14:31:20.412803878Z" level=info msg="Starting upload purge in 52m0s" go.version=go1.16.15 instance.id=4fb8d420-eaf8-4a69-b740-bdc94fa52d91 service=registry version="v2.8.1+unknown" docker-registry-registry-1 | time="2024-01-19T14:31:20.41296431Z" level=info msg="listening on [::]:5000" go.version=go1.16.15 instance.id=4fb8d420-eaf8-4a69-b740-bdc94fa52d91 service=registry version="v2.8.1+unknown" ... يتضمن الخرج السابق رسالة تحذير مفادها عدم توفر اتصال HTTP آمن No HTTP secret provided، لا تقلق سنعالجها في الفقرات القادمة. إذا دققت في السطر الأخير، ستجده يعلمك بإتمام عملية التشغيل بنجاح، وبأن السجل جاهز لاستقبال الطلبات على المنفذ 5000. يمكنك الآن الضغط على CTRL+C لإيقاف التنفيذ. إذًا فقد أنشأنا في هذه الخطوة إعدادات Docker Compose التي شغّلت سجل Docker على المنفذ 5000، وسنعمل في الخطوات التالية على استعراضه باسم النطاق المخصص له، وعلى ضبط إعدادات المصادقة Authentication للتحكم بصلاحية الوصول إليه. الخطوة 2: ضبط إعدادات التوجيه لمنفذ Nginx ذكرنا في بداية المقال أن تفعيل بروتوكول HTTPS على اسم نطاقك هو أحد متطلبات العمل الأولية، وسنعمل الآن على توجيه حركة مرور البيانات من اسم النطاق إلى حاوية السجل لنضمن أن الوصول لسجل Docker سيجري عبر اتصالٍ آمن. لابد أنك جهزت الملف etc/nginx/sites-available/your_domain/ أثناء إعدادك لخادم Nginx، إذ يحتوى هذا الملف على قسمٍ خاص بإعدادات الخادم، افتحه بواسطة أي محرر نصوص وفق التالي، لنجري عليه بعض التعديلات: $ sudo nano /etc/nginx/sites-available/your_domain ابحث ضمنه عن القسم المسمى location: ... location / { ... } ... المطلوب في حالتنا أمران: توجيه حركة مرور البيانات إلى المنفذ 5000 الذي يتلقى السجل عبره الطلبات، وإضافة ترويسات headers للطلبات الموجهة إلى السجل تحتوي معلوماتٍ إضافية عنها يضيفها الخادم. استبدل محتوى القسم location بالتالي لتنفيذهما: ... location / { # Do not allow connections from docker 1.5 and earlier # docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) { return 404; } proxy_pass http://localhost:5000; proxy_set_header Host $http_host; # required for docker client's sake proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 900; } ... تتأكد الجملة if من توفر عدة شروط قبل أن تسمح للطلب بالمرور إلى السجل، فتتحقق من وكيل المستخدم صاحب الطلب User Agent، ومن كون إصدار Docker الذي يستعمله أعلى من 1.5، ومن أنه ليس تطبيق مبرمج بلغة Go ويسعى للوصول إلى السجل. يمكنك معرفة المزيد عن ترويسة nginx بمراجعة دليل إعداد Ngin لسجل Docker من توثيقات Docker الرسمية. احفظ التغييرات على الملف، وأعِد تشغيل Nginx بكتابة التعليمة التالية، حتى تأخذ التغييرات مفعولها: $ sudo systemctl restart nginx إذا حصلت على أي رسالة خطأ تفيد بعدم نجاح عملية إعادة التشغيل، فتحقق مجددًا من صحة التعديلات التي أجريتها على الملف. سنشغل السجل الآن لنتأكد من توجيه Nginx الطلبات الواردة إلى حاوية السجل، اكتب الأمر التالي: $ docker compose up استعرض العنوان التالي في متصفحك، والذي يتضمن اسم النطاق يليه v2 نقطة الوصول endpoint: https://your_domain/v2 سيعرض لك المتصفح كائن JSON فارغ على الشكل: وستحصل في الطرفية على الخرج التالي: docker-registry-registry-1 | time="2024-01-19T14:32:50.082396361Z" level=info msg="response completed" go.version=go1.16.15 http.request.host=your_domain http.request.id=779fe265-1a7c-4a15-8ae4-eeb5fc35de98 http.request.method=GET http.request.remoteaddr=87.116.166.89 http.request.uri="/v2" http.request.useragent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" http.response.contenttype="text/html; charset=utf-8" http.response.duration="162.546µs" http.response.status=301 http.response.written=39 docker-registry-registry-1 | 172.19.0.1 - - [19/Nov/2022:14:32:50 +0000] "GET /v2 HTTP/1.0" 301 39 "" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" docker-registry-registry-1 | 172.19.0.1 - - [19/Nov/2022:14:32:50 +0000] "GET /v2/ HTTP/1.0" 200 2 "" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" docker-registry-registry-1 | time="2024-01-19T14:32:50.132472674Z" level=info msg="response completed" go.version=go1.16.15 http.request.host=your_domain http.request.id=0ffb17f0-c2a0-49d6-94f3-af046cfb96e5 http.request.method=GET http.request.remoteaddr=87.116.166.89 http.request.uri="/v2/" http.request.useragent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" http.response.contenttype="application/json; charset=utf-8" http.response.duration=2.429608ms http.response.status=200 http.response.written=2 يخبرك السطر الأخير أن الطلب GET قد وصل إلى نقطة الوصول /v2/ المذكورة في العنوان الذي طلبته من المتصفح، واستقبلته حاوية السجل (بفضل إعدادات التوجيه التي عملنا عليها) وأرسلت استجابة بكائن JSON {} مع رمز الاستجابة 200 الذي يشير لنجاح العملية. اضغط الآن CTRL+C لإيقاف التنفيذ. أنهينا بذلك إعدادات التوجيه وننتقل لإعدادات تأمين السجل. الخطوة 3: ضبط إعدادات المصادقة Authentication يتيح Nginx لمستخدمه إعداد آلية مصادقة Authentication باسم مستخدم وكلمة مرور لتقييد الوصول إلى مواقعهم المستضافة عليه وذلك بإنشاء ملف مصادقة htpasswd وكتابة أسماء المستخدمين المسموح له بالوصول إليه مع كلمات مرورهم. سنستخدم هذه الآلية هنا لحماية سجل Docker. يمكنك الحصول على الأداة htpasswd بتثبيت الحزمة apache2-utils وفق الأمر التالي: $ sudo apt install apache2-utils -y سننشئ الآن المجلد docker-registry/auth/~ لتخزين ملف المصادقة الذي يتضمن بيانات الاعتماد، وفق ما يلي: $ mkdir ~/docker-registry/auth انتقل للمجلد الجديد بكتابة التالي: $ cd ~/docker-registry/auth نفذّ الأمر المبين أدناه لإنشاء المستخدم الأول، واستبدل العبارة username باسم المستخدم الفعلي، واحرص على كتابة الراية B- فهي مسؤولة عن تفعيل خاصية التشفير bcrypt التي تشترطها Docker: $ htpasswd -Bc registry.password username سيُطلب منك إدخال كلمة المرور الخاصة بهذا المستخدم، أدخلها بدقة. وستخزن بيانات الاعتماد هذه التي أدخلتها في registry.password. ملاحظة: أعِدّ تنفيذ الأمر السابق بدون الراية c- لإضافة مستخدمين آخرين إلى الملف وفق التالي: $ htpasswd -B registry.password username إذ تشير الراية c- إلى إنشاء ملف جديد، وتعني إزالتها التعديل على الملف الحالي (أي إضافة مستخدمين جدد). عدّل الآن الملف docker-compose.yml ليستخدم Docker ملف بيانات الاعتماد -الذي أنشأناه- لإجراء المصادقة مع المستخدمين للتحقق من هوياتهم. افتح أولًا الملف بكتابة التالي: $ nano ~/docker-registry/docker-compose.yml أضف الأجزاء المتعلقة بالمصادقة إلى محتواه، ليصبح كما يلي: version: '3' services: registry: image: registry:latest ports: - "5000:5000" environment: REGISTRY_AUTH: htpasswd REGISTRY_AUTH_HTPASSWD_REALM: Registry REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data volumes: - ./auth:/auth - ./data:/data أضفت بهذه التعديلات بعض المتغيرات إلى متغيرات البيئة لفرض استخدام المصادقة مع بروتوكول HTTP، ولتوفير مسار الملف htpasswd. فقد حدد المتغير REGISTRY_AUTH الذي يحمل القيمة htpasswd مخطط المصادقة المستخدم أو authentication scheme، وأُسندت القيمة التي تدل على مسار ملف المصادقة إلى المتغير REGISTRY_AUTH_HTPASSWD_PATH، أما المتغير REGISTRY_AUTH_HTPASSWD_REALM فيوضح نطاق تنفيذ المصادقة htpasswd. وفي السطر ما قبل الأخير وصلت المجلد auth/. إلى داخل حاوية السجل ليكون متاحًا ضمنها. احفظ التغييرات على الملف وأغلقه. ودعنا نتأكد من استخدام السجل لإجراء المصادقة. توجه في البداية إلى مجلد السجل الأساسي بكتابة الأمر: $ cd ~/docker-registry شغّل السجل بتنفيذ ما يلي: $ docker compose up حدّث الصفحة في متصفح الويب إذا كان ما يزال مفتوحًا لديك، أو اطلب مجددًا اسم النطاق الذي حددته للسجل، ولاحظ الفرق. سيطلب منك في هذه المرة اسم مستخدم وكلمة مرور. أدخل البيانات الصحيحة وستحصل على الخرج السابق نفسه، كائن JSON فارغ كما يلي، وهو ما يشير لصحة التنفيذ: نجحت إذا عملية المصادقة وسُمح لك بالوصول للسجل بعد إدخال بيانات الاعتماد الصحيحة. يمكنك الخروج بالضغط على CTRL+C في نافذة الطرفية. خطوتنا التالية هي تحويل السجل إلى خدمة تعمل في الخلفية، وتقلع تلقائيًا، مع الإبقاء على المرونة التي تسمح لنا بإعادة تشغيلها. الخطوة 4: بدء تشغيل سجل Docker بصفته خدمة يعني تحويل سجل Docker إلى خدمة ضبط بعض الإعدادات في Docker Compose لإبقاء حاوية السجل في وضع التشغيل دائمًا، فتُقلع تلقائيًا مع إقلاع نظام التشغيل، ويُعاد تشغيلها بعد أي عطل. اكتب الأمر التالي، وافتح الملف docker-compose.yml لنجري عليه بعض التعديلات: $ nano docker-compose.yml ابحث ضمن الملف عن قسم السجل المسمى registry، واكتب تحته السطر التالي: ... registry: restart: always ... يعني ضبط المحدد restart على القيمة always أن حاوية السجل سيُعاد تشغيلها دائمًا بعد أي طارئ يسبب إيقافها. احفظ الآن التغييرات على الملف، وأغلقه لننتقل إلى الإجراء التالي. اكتب الأمر المبين أدناه لبدء تشغيل حاوية السجل بصفتها عملية تعمل في الخلفية background process، وذلك بتمرير الراية d-: $ docker compose up -d يمكنك إغلاق جلسة SSH والاطمئنان بأن حاوية السجل لن تتوقف فهي الآن تعمل في الخلفية. ستتناول الخطوة التالية زيادة حجم الملفات التي يُسمح برفعها على خادم Nginx ليناسب حجوم صور الحاويات التي ستحفظ على السجل. الخطوة 5: زيادة حجم الملفات المسموح رفعها على Nginx الحجم الأعظمي المسموح رفعه على خادم Nginx افتراضيًا هو 1m أي 1 ميجا بايت للملف الواحد، ويُعدّ صغيرًا نسبيًا موازنةً بحجوم صور الحاويات، لذا يتحتم علينا تغيره قبل البدء برفع الصور إلى السجل. يمكنك تغييره بتعديل قيمته في ملف إعدادات Nginx الموجود في المسار etc/nginx/nginx.conf/. افتح ملف إعدادات Nginx بكتابة الأمر التالي: $ sudo nano /etc/nginx/nginx.conf أضف السطر التالي إلى القسم http ضمنه: ... http { client_max_body_size 16384m; ... } ... زدنا بهذا التعديل الحجم الأعظمي للملف المسموح برفعه إلى 16 جيجا بايت، وذلك بضبط قيمة المحدد client_max_body_size على 16384m. احفظ التغييرات على الملف وأغلقه. أعِدّ تشغيل الخادم Nginx لتأخذ التغييرات مفعولها، وفق التالي: $ sudo systemctl restart nginx يمكنك الآن رفع الصور إلى السجلات بدون أي أخطاء تتعلق بالحجم من Nginx. الخطوة 6: نشر صور الحاويات على سجل Docker الخاص أصبح خادم السجل قادرًا على استيعاب الملفات كبيرة الحجم، لذا سنجرب نشر صورة تجريبية عليه، فإذا لم يتوفر لديك أي صورة لرفعها، يمكنك تحميل صورة أوبنتو من Docker Hub (سجل Docker العام) لتجرب نشرها على هذا السجل. افتح جلسة طرفية جديدة على الخادم العميل، ونفذّ الأمر المبين أدناه لتحميل صورة الحاوية ubuntu وتشغيلها (تذكر أننا طلبنا وجود خادمين ضمن المتطلبات الأولية خادم مضيف وخادم عميل): $ docker run -t -i ubuntu /bin/bash تمنحك الرايتان t- و i- واجهة صدفة shell تفاعلية لتُنفذ بواسطتها الأوامر داخل حاوية أوبنتو. أنشئ الآن ملفًا يدعى SUCCESS داخل حاوية أوبنتو وفق التالي: root@f7e13d5464d1:/# touch /SUCCESS أنشأنا هذا الملف داخل الحاوية كنوع من التخصيص لتمييزها عن غيرها، فيمكننا لاحقًا استعراضه للتأكد من استخدامنا الحاوية الصحيحة. اخرج من صدفة الحاوية بكتابة التالي: root@f7e13d5464d1:/# exit أنشئ الآن صورة عن هذه الحاوية بعد تخصيصها، بكتابة الأمر التالي: $ docker commit $(docker ps -lq) test-image أصبح لديك صورة عن حاوية أوبنتو المخصصة محفوظة محليًا على خادمك، سجِّل دخول إلى سجل Docker وفق التالي لنحاول نشرها عليه: $ docker login https://your_domain سيُطلب منك إدخال اسم مستخدم وكلمة مرور، أدخل بيانات أحد المستخدمين الذين أنشأتهم في الخطوة 3 قبل قليل، وستحصل على الخرج التالي: Login Succeeded بعد تسجيل الدخول بنجاح عدّل تسمية صورة الحاوية كما يلي: $ docker tag test-image your_domain/test-image انشرها الآن على سجلك بكتابة الأمر التالي: $ docker push your_domain/test-image وستحصل على خرج يشبه ما يلي يؤكد لك نجاح العملية: Using default tag: latest The push refers to a repository [your_domain/test-image] 1cf9c9034825: Pushed f4a670ac65b6: Pushed latest: digest: sha256:95112d0af51e5470d74ead77932954baca3053e04d201ac4639bdf46d5cd515b size: 736 إذًا فقد نشرنا صورة حاوية Docker على السجل الخاص، وتأكدنا من فعالية عملية المصادقة، إذ لم نتمكن من الوصول للسجل والنشر عليه بدون إدخال اسم مستخدم وكلمة مرور صحيحين، لنختبر الآن سحب الصور من السجل. سحب الصور من سجل Docker الخاص سنحاول سحب الصورة نفسها التي نشرتها على السجل في الخطوة السابقة. اكتب الأمر التالي، وسجل دخول إلى سجل Docker من الخادم الرئيسي باستخدام بيانات المستخدمين الذين أنشأتهم سابقًا: $ docker login https://your_domain جرّب سحب الصورة test-image من السجل كما يلي: $ docker pull your_domain/test-image حمَّل Docker الآن هذه الصورة إلى خادمك المحلي، شغّل حاوية جديدة باستخدامها عبر كتابة ما يلي: $ docker run -it your_domain/test-image /bin/bash يوفر لنا هذا الأمر صدفة shell تفاعلية مع الحاوية المُشغّلة. اكتب ضمنها الأمر التالي لنستعرض نظام ملفاتها: root@f7e13d5464d1:/# ls لاحظ الخرج التالي، إنه يتضمن الملف SUCCESS الذي أنشأناه قبلًا لتمييز صورة الحاوية قبل نشرها على السجل، وهذا يؤكد أن الحاوية المُشغّلة حاليًّا مبنية على الصورة نفسها المسحوبة من السجل: root@f7e13d5464d1:/# SUCCESS bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var اخرج الآن من صدفة الحاوية بكتابة الأمر التالي: $ exit أنهينا بذلك إنشاء سجل Docker خاص وآمن لتخزين صور الحاويات التي تخصصها حسب احتياجاتك، وجربنا معًا نشر صورة تجريبية عليه وسحبها منه إلى الخادم المحلي. الخلاصة يساعدك هذا المقال التعليمي على إنشاء سجلك الخاص لحفظ صور حاويات Docker ونشر الصور عليه بمرونة وأمان، ويمكنك أيضًا الاستفادة من بعض الأدوات الخاصة بالتكامل المستمر لأتمتة عمليات النشر عليه. وتذكر دائمًا أن اعتمادك على حاويات Docker في سير عملك يعني أن الصور التي تتضمن الشيفرات البرمجية لتطبيقاتك ستعمل دائمًا بالصورة المطلوبة نفسها على أي جهاز وفي أي بيئة العمل سواءً في مرحلة التطوير أو الإنتاج. لمزيد من المعلومات عن حاويات Docker وتفاصيل التعامل معها، وطرق كتابة ملفات Docker، ننصحك بالاطلاع على مقالات قسم Docker باللغة العربية على أكاديمية حسوب أو على توثيقات Docker الرسمية. ترجمة -وبتصرف- للمقال How To Set Up a Private Docker Registry on Ubuntu 22.04 لصاحبيه Young Kim و Savic. اقرأ أيضًا ما هي تقنية Docker؟ استخدام الأمر docker exec في حاويات Docker مدخل إلى دوكر Docker تثبيت الأداة دوكر كومبوز Docker Compose واستخدامها ضمن نظام لينكس أوبونتو
-
لاشك أن استخدام نماذج الذكاء الاصطناعي المُدَرَّبة مُسبقًا pretrained model يقلل الوقت والجهد والتكاليف اللازمة لتدريب هذه النماذج من الصفر، فضلًا عن إتاحة الفرصة أمامك لاستخدام أحدث النماذج المتوفرة على منصات متخصصة مثل تلك التي توفرها مكتبة المُحوّلات Transformers من منصة Hugging Face، لذا يلجأ مهندسو الذكاء الاصطناعي لاستخدام النماذج المُدَرَّبة مُسبقًا في كثير من الحالات ويعمدون إلى صقلها أو معايرتها Fine Tuning بدقة وتدريبها على بيانات محددة تناسب أهدافهم، إذ يعني صقل النماذج fine-tuning أخذ نموذج تعلم آلي مدرب مسبقًا ومواصلة تدريبه على مجموعة بيانات أصغر وأكثر تخصصًا للحفاظ على قدرات النموذج المدرب وتكييفه ليناسب استخدامات محددة ويعطي تنبؤاتٍ دقيقة، كما سنطرح بعض الأمثلة التوضيحية على تدريب النماذج باستخدام كل من التقنيات التالية: مُدَرِّب مكتبة المحوّلات Transformers Trainer. إطار العمل تنسرفلو TensorFlow مع كيراس Keras. إطار العمل بايتورش PyTorch لوحده. تحضير مجموعة بيانات التدريب قبل أن نبدأ بصقل النموذج fine-tune سنُحَمِّل مجموعة بيانات Dataset ونُحَضِّر بياناتها كما تعلمنا في المقال السابق المعالجة المُسبقة للبيانات قبل تمريرها لنماذج الذكاء الاصطناعي. اخترنا في هذا المقال مجموعة البيانات Yelp Reviews: >>> from datasets import load_dataset >>> dataset = load_dataset("yelp_review_full") >>> dataset["train"][100] {'label': 0, 'text': 'My expectations for McDonalds are t rarely high. But for one to still fail so spectacularly...that takes something special!\\nThe cashier took my friends\'s order, then promptly ignored me. I had to force myself in front of a cashier who opened his register to wait on the person BEHIND me. I waited over five minutes for a gigantic order that included precisely one kid\'s meal. After watching two people who ordered after me be handed their food, I asked where mine was. The manager started yelling at the cashiers for \\"serving off their orders\\" when they didn\'t have their food. But neither cashier was anywhere near those controls, and the manager was the one serving food to customers and clearing the boards.\\nThe manager was rude when giving me my order. She didn\'t make sure that I had everything ON MY RECEIPT, and never even had the decency to apologize that I felt I was getting poor service.\\nI\'ve eaten at various McDonalds restaurants for over 30 years. I\'ve worked at more than one location. I expect bad days, bad moods, and the occasional mistake. But I have yet to have a decent experience at this store. It will remain a place I avoid unless someone in my party needs to avoid illness from low blood sugar.'} وبما أن مجموعة بياناتنا نصية لذا سنحتاج مُرَمِّزًا tokenizer مناسبًا للنموذج لمعالجتها كما تعلمنا في مقالات السلسلة، تتضمن هذه المعالجة أساليب الحشو والاقتطاع لتوحيد أطوال السلاسل النصية، وسنستخدم دالةً تدعى map لتسريع المعالجة التحضيرية للبيانات وتطبيقها على كامل مجموعة البيانات dataset وفق التالي: >>> from transformers import AutoTokenizer >>> tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-cased") >>> def tokenize_function(examples): return tokenizer(examples["text"], padding="max_length", truncation=True) >>> tokenized_datasets = dataset.map(tokenize_function, batched=True) للسهولة وتسريع العمل يمكنك أخذ جزء من مجموعة البيانات فقط بدلًا من العمل معها كاملةً كما يلي: >>> small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(1000)) >>> small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(1000)) تدريب نموذج ذكاء اصطناعي باستخدام PyTorch Trainer إن المُدَرِّبTrainer هو أحد أصناف مكتبة المحوّلات Transformers حيث يستخدم لتدريب نماذج المكتبة، ويوفر عليك أعباء إنشاء حلقة تدريب خاصة بمشروعك من الصفر، ويتمتع هذا الصنف بواجهة برمجية API متنوعة الخيارات وتؤمن مزايا تدريبية واسعة، مثل: تسجيل الأحداث logging، والتدرج التراكمي gradient accumulation، والدقة المختلطة mixed precision. سنبدأ عملنا بتحميل النموذج وفق الأوامر التالية مع تحديد عدد التسميات التوضيحية labels المتوقعة من البيانات المُدخَلة، وإذا قرأت بطاقة وصف مجموعة البيانات التي حضرناها Yelp Review ستجد أن عدد التسميات labels فيها هو 5: >>> from transformers import AutoModelForSequenceClassification >>> model = AutoModelForSequenceClassification.from_pretrained("google-bert/bert-base-cased", num_labels=5) ملاحة: عندما تُنَفِذ الأوامر السابقة ستواجه تحذيرًا مفاده أن بعض الأوزان المُدَرَّبة مسبقًا في النموذج لن تُسْتَخْدَمْ، وبعضها ستُعاد تهيئته عشوائيًا، يُعدّ هذا التحذير طبيعيًا ولا يستوجب القلق إذ سيُهمَل رأس النموذج BERT المُدَرَّب مسبقًا ويُستَبْدَل برأس تصنيف لا على التعيين، ثم يٌدَرَّب الرأس الجديد على تصنيف السلاسل وتنتقل إليه تلقائيًا كل المعرفة التي اكتسبها النموذج المدرب مسبقًا فيستفيد منها، علمًا أن رأس النموذج model head هو الجزء المسؤول عن معالجة مهمة معينة مثل التصنيف أو الترجمة ويستخدم لتحديد نتائجها. المعاملات الفائقة للنموذج hyperparameters سننشئ صنفًا لوسطاء التدريب TrainingArguments يتضمن كافة المعاملات الفائقة التي يمكننا ضبطها بالإضافة إلى الرايات flags الخاصة بتفعيل خيارات التدريب المختلفة، سنستعمل هنا المعاملات الافتراضية لكن يمكنك استعراض جميع المعاملات الفائقة وتجريبها لتصل إلى الإعدادات الملائمة لحالتك. ملاحظة: المعاملات الفائقة للنموذج هي المعاملات التي نحددها قبل بدء عملية تدريب النموذج وتتحكم في كيفية تعلم النموذج. لا تنسَ أن تحدد مكان حفظ نقاط التحقق checkpoints الناتجة عن التدريب: >>> from transformers import TrainingArguments >>> training_args = TrainingArguments(output_dir="test_trainer") التقييم لا يعطي المُدَرِّب Trainer في الأحوال الطبيعية مؤشراتٍ عن أداء النماذج في أثناء التدريب، فإذا رغبت بالحصول على تقييمٍ لنموذجك، ينبغي أن تمرر دالة خاصة بهذا الأمر تحسب مؤشرات الأداء وترجع لك تقريرًا بتقييم النموذج، وفي هذا المجال توفر مكتبة التقييم Evaluate دالةً بسيطة تدعى accuracy يمكنك تحميلها باستخدام evaluate.load كما في المثال التالي (طالع هذه الجولة السريعة في مكتبة التقييم لمزيدٍ من المعلومات): >>> import numpy as np >>> import evaluate >>> metric = evaluate.load("accuracy") استدعِ الدالة compute مع metric وفق التالي لحساب دقة التنبؤات الناتجة عن نموذجك، ولأن نماذج مكتبة المحوّلات Transformers تضع مخرجاتها في السمة logits (كما تعلمنا سابقًا في مقال جولة سريعة للبدء مع مكتبة المحوّلات Transformers) فينبغي لنا في البداية تحويل logits الناتجة عن النموذج إلى تنبؤات predictions ثم تمريرها لدالة حساب الدقة: >>> def compute_metrics(eval_pred): logits, labels = eval_pred predictions = np.argmax(logits, axis=-1) return metric.compute(predictions=predictions, references=labels) والآن أعطِ القيمة "epoch" للمعامل evaluation_strategy من وسطاء التدريب TrainingArguments لمراقبة أداء نموذجك أثناء التدريب فهذا الخيار سيعطيك تقييمًا في نهاية كل دورة تدريبية epoch للنموذج: >>> from transformers import TrainingArguments, Trainer >>> training_args = TrainingArguments(output_dir="test_trainer", evaluation_strategy="epoch") المُدَرِّب Trainer لننشئ الآن كائن المُدَرِّب Trainer باستخدام جميع الإعدادات السابقة وهي: النموذج الذي اخترناه، ووسطاء التدريب، ومجموعة بيانات التدريب، ودالة التقييم وذلك وفق الأمر التالي: >>> trainer = Trainer( model=model, args=training_args, train_dataset=small_train_dataset, eval_dataset=small_eval_dataset, compute_metrics=compute_metrics, ) ثم دَرِّب نموذجك باستخدامه كما يلي: >>> trainer.train() تدريب نموذج TensorFlow باستخدام Keras نناقش هنا تدريب نماذج من مكتبة Transformers باستخدام إطار العمل تنسرفلو TensorFlow وكيراس Keras API، حيث أن كيراس هو إطار عمل سهل ومفتوح المصدر يسمح بإنشاء شبكات عصبية معقدة بتعليمات قليلة، بدأ مشروعًا مستقلًا ثم اندمج مع TensorFlow (يمكنك معرفة المزيد بمطالعة قسم الذكاء الاصطناعي على أكاديمية حسوب وخاصةً المقال التعريفي مكتبات وأطر عمل الذكاء الاصطناعي). تحويل البيانات إلى صيغة تناسب كيراس Keras يتطلب تدريب نماذج Transformers باستخدام Keras API تحميل مجموعة بيانات بصيغة تتوافق مع كيراس Keras، وإحدى الطرق السهلة لذلك هي تحويل البيانات التدريبية إلى مصفوفات NumPy ثم تمريرها له، تناسب هذه الطريقة مجموعات البيانات صغيرة الحجم وهي ما سنجربه بدايةً قبل الانتقال إلى طرق أكثر تعقيدًا. لنُحمِّل في البداية مجموعة بيانات، وقد اخترنا هنا المجموعة CoLA dataset من GLUE benchmark وهي مجموعة بيانات بسيطة تناسب تصنيف النصوص الثنائية binary text، وسنأخذ منها القسم المخصص للتدريب فقط: from datasets import load_dataset dataset = load_dataset("glue", "cola") dataset = dataset["train"] # أخذنا من مجموعة البيانات القسم الخاص بالتدريب فقط سنُحَمِّل بعد ذلك مُرمِّزًا tokenizer يناسب النموذج ونستخدمه لترميز بيانات الندريب وتحويلها إلى مصفوفات NumPy، ولكن في مثالنا البيانات ثنائية بسيطة وتسمياتها التوضيحية labels هي مجموعة أصفار وواحدات فيمكننا تحويلها إلى مصفوفة NumPy مباشرةً دون ترميز: from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-cased") tokenized_data = tokenizer(dataset["sentence"], return_tensors="np", padding=True) # يرجع المُرَمِّز دفعات مُرَمَّزة من البيانات، حوّلناها هنا إلى قاموس يناسب Keras tokenized_data = dict(tokenized_data) labels = np.array(dataset["label"]) # هذه البيانات في الأساس مصفوفة أصفار وواحدات وفي المرحة الأخيرة سنُحَمِّل النموذج ونخضعه لعملية تصريف compile ثم ملائمة fit، وننوه هنا أن كل نموذج في مكتبة المحوّلات يتضمن دالةً افتراضية لحساب الخسارة loss function تناسب المهمة التي يُستَخدم النموذج لأجلها، فلست بحاجة لضبط أي خيارات بهذا الخصوص: from transformers import TFAutoModelForSequenceClassification from tensorflow.keras.optimizers import Adam # تحميل النموذج وتصريفه model = TFAutoModelForSequenceClassification.from_pretrained("google-bert/bert-base-cased") # غالبًا ما تكون معدلات التَعَلُّم المنخفضة هي الأنسب لصقل نماذج مكتبة المحوّلات المُدَرَّبة مُسبقًا model.compile(optimizer=Adam(3e-5)) # لاحظ عدم وجود أي وسيط يتعلق بدالة حساب الخسارة فهي افتراضية model.fit(tokenized_data, labels) ملاحظة: تختار نماذج Hugging Face تلقائيًا دوال الخسارة المناسبة لبُنيتها ومهامها، لذا لن تضطر لتمرير الوسيط الخاص بحساب الخسارة عند تصريف نموذجك باستخدام compile() لكن الخيار يبقى لك ففي حال لم ترغب باستخدام دالة الخسارة الافتراضية يمكنك حسابها بنفسك. يعمل أسلوب الترميز السابق جيدًا مع مجموعات البيانات صغيرة الحجم لكنه لا يُعدّ عمليًّا أبدًا مع مجموعات البيانات الكبيرة بل وسيؤدي إلى إبطاء عملية التدريب، يعود ذلك لسببين: الأول أن مصفوفتي الرموز والتسميات التوضيحية ستكونان كبيرتي الحجم وتحميلهما بالكامل إلى الذاكرة يُعدّ مشكلة، والسبب الثاني أن مكتبة Numpy لا تستطيع التعامل مع المصفوفات غير منظمة الأطوال المعروفة باسم jagged arrays يعني ذلك أنك ستضطر إلى حشو العناصر القصيرة في المصفوفة بالأصفار لتصبح جميعها بطول موحد يساوي أطول عنصر في المصفوفة، وهذا أيضًا سيُبَطئ التدريب. تحميل مجموعة بيانات بصيغة tf.data.Dataset يمكنك تحميل مجموعة بياناتك بصيغة tf.data.Dataset بدلًا من اتباع الطريقة السابقة والمخاطرة بإبطاء التدريب، سنقترح عليك طريقتين لإنجاز الأمر، وتستطيع إنشاء خط أنابيبك الخاص tf.data يدويًا إذا رغبت بذلك: prepare_tf_dataset(): تعتمد هذه الطريقة على طبيعة نموذجك لذا تُعدّ الطريقة الأنسب والموصى بها في معظم الحالات، فهي تفحص مواصفات النموذج وتتعرف تلقائيًا على أعمدة مجموعة البيانات المتوافقة معه أي التي يمكن استخدامها كمدخلات للنموذج، وتتجاهل الأعمدة غير المتوافقة فتُنشئ بذلك مجموعة بيانات أبسط وأفضل أداءً. to_tf_dataset: طريقة منخفضة المستوى low-level فهي تتحكم بالتفاصيل الدقيقة لطريقة إنشاء مجموعة بيانات التدريب، فتُمَكِّنك من تحديد الأعمدة columns وتسمياتها التوضيحية label_cols التي تود تضمينها في مجموعة البيانات. لنبدأ بالطريقة الأولى prepare_tf_dataset()، ولكن قبل تطبيقها ينبغي لنا ترميز البيانات وإدخال مُخرجات المُرَمِّز بهيئة أعمدة إلى مجموعة البيانات dataset كما يلي: def tokenize_dataset(data): # سنُدخِل مفاتيح القاموس الناتج هنا إلى مجموعة البيانات بصفتها أعمدة return tokenizer(data["text"]) dataset = dataset.map(tokenize_dataset) تُخَزَّن مجموعات بيانات Hugging Face على القرص الصلب افتراضيًا، فلن تسبب ضغطًا على استخدام الذاكرة على حاسوبك، وبمجرد إضافة الأعمدة السابقة تستطيع الحصول على الدفعات batches من مجموعة البيانات، وإضافة رموز الحشو إلى كل دفعة وهو ما يقلل عدد رموز الحشو المطلوبة في كل مرة مقارنةً بحشو مجموعة البيانات كاملةً. tf_dataset = model.prepare_tf_dataset(dataset["train"], batch_size=16, shuffle=True, tokenizer=tokenizer) مررنا إلى Preparation_tf_dataset في التعليمات السابقة وسيطًا خاصًا هو tokenizer يحدد المُرَمِّز الذي سنستخدمه لحشو الدفعات المُحَمَّلة بطريقة صحيحة، لكن يمكنك الاستغناء عنه إذا كانت العينات في مجموعة بياناتك متساوية الطول ولا تحتاج لأي حشو، أو استبداله بوسيطٍ آخر نحو Collate_fn إذا كنت ترغب بتدريب النموذج على حالات أعقد، مثل نمذجة اللغة المقنعة masked language modelling أي إخفاء بعض الرموز ليتنبأ النموذج بالكلمات من السياق أو غيرها من الحالات، تستطيع تحدد نوعية المعالجة التحضيرية التي تريدها للدفعات قبل تمريرها للنموذج، يساعدك الاطلاع على بعض الأمثلة والملاحظات المتعلقة بالموضوع من منصة Hugging Face لتطبيق ذلك عمليًّا. والآن بعد إنشاء مجموعة بيانات tf.data.Dataset يتبقى لنا الخطوة الأخيرة وهي تصريف النموذج compile وملائمته fit وفق التالي: model.compile(optimizer=Adam(3e-5)) # No loss argument! model.fit(tf_dataset) تدريب نموذج ذكاء اصطناعي باستخدام Native PyTorch يساعدك المُدَرِّب Trainer على تدريب نموذجك وضبطه بتعليمة واحدة فقط ويغنيك عن إنشاء حلقة التدريب من الصفر، لكن بعض المستخدمين يفضلون عدم الاعتماد على المُدَرِّب وإنشاء حلقات تدريبهم الخاصة بأنفسهم لتدريب نماذج مكتبة المحوّلات Transformers، فإذا كنت أحدهم يمكنك إجراء ذلك باستخدام إطار العمل PyTorch لوحده أي native PyTorch وفق الخطوات التالية، لكن في البداية ننصحك بتفريغ الذاكرة المؤقتة على جهازك أو دفتر ملاحظاتك notebook بإعادة تشغيله أو بتنفيذ هذه الأوامر: del model del trainer torch.cuda.empty_cache() تتضمن الخطوات التالية المعالجة التي سنجريها يدويًا على البيانات المُرَمَّزة tokenized_dataset لتحضيرها للتدريب. 1. تخَلَّص من عمود النص text لأن النموذج لا يقبل النصوص الخام مدخلاتٍ له: >>> tokenized_datasets = tokenized_datasets.remove_columns(["text"]) 2. عَدِّل اسم العمود label إلى labels ليتوافق مع اسم الوسيط الذي يقبله النموذج: >>> tokenized_datasets = tokenized_datasets.rename_column("label", "labels") 3. اضبط تنسيق مجموعة البيانات لتُرجِع PyTorch tensors بدلًا من القوائم المعتادة: >>> tokenized_datasets.set_format("torch") 4. ثم أنشئ مجموعة بيانات مُصَغَّرة من مجموعة البيانات الكاملة كما فعلنا في الفقرات السابقة لتسريع عملية صقل النموذج fine-tuning: >>> small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(1000)) >>> small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(1000)) مُحَمِّل البيانات DataLoader أنشئ مُحَمِّل بيانات DataLoader لمجموعات بيانات التدريب والاختبار لتٌنَفِّذ العمليات التكرارية على دفعات البيانات: >>> from torch.utils.data import DataLoader >>> train_dataloader = DataLoader(small_train_dataset, shuffle=True, batch_size=8) >>> eval_dataloader = DataLoader(small_eval_dataset, batch_size=8) ثم حَمِّل النموذج وحَدِّد عدد التسميات labels المتوقعة له: >>> from transformers import AutoModelForSequenceClassification >>> model = AutoModelForSequenceClassification.from_pretrained("google-bert/bert-base-cased", num_labels=5) المُحَسِّنْ Optimizer ومُجَدّوِل معدل التعلُّم learning rate scheduler سننشئ مُحَسِّنْ Optimizer ومُجَدّوِل معدل التعلُّم learning rate scheduler لتدريب النموذج وضبطه، اخترنا هنا المُحَسِّنْ AdamW من PyTorch: >>> from torch.optim import AdamW >>> optimizer = AdamW(model.parameters(), lr=5e-5) ثم مُجَدّوِل معدل التعلُّم باستخدام المُجَدّوِل الافتراضي للمُدَّرِب Trainer: >>> from transformers import get_scheduler >>> num_epochs = 3 >>> num_training_steps = num_epochs * len(train_dataloader) >>> lr_scheduler = get_scheduler( name="linear", optimizer=optimizer, num_warmup_steps=0, num_training_steps=num_training_steps ) وأخيرًا حَدِّدْ قيمة المعامل device لتكون "cpu" إذا كان لديك وحدة معالجة رسوميات (GPU) ورغبت باستخدامها للتدريب، فمن دونها سيستغرق التدريب مدةً طويلة في حال الاعتماد على وحدة المعالجة المركزية (CPU) لوحدها وقد تصل المدة لساعات بدلًا من بضع دقائق: >>> import torch >>> device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") >>> model.to(device) أصبحنا جاهزين الآن للتدريب. حلقة التدريب Training loop إليك نموذجًا للشيفرة البرمجية الخاصة بحلقة التدريب، ويمكنك استخدام المكتبة tqdm لإضافة شريط خاص bar يعرض لك تقدم مراحل التدريب لتتبع سير العملية: >>> from tqdm.auto import tqdm >>> progress_bar = tqdm(range(num_training_steps)) >>> model.train() for epoch in range(num_epochs): for batch in train_dataloader: batch = {k: v.to(device) for k, v in batch.items()} outputs = model(**batch) loss = outputs.loss loss.backward() optimizer.step() lr_scheduler.step() optimizer.zero_grad() progress_bar.update(1) التقييم Evaluate يختلف أسلوب تقييم أداء النموذج في حلقة التدريب المخصصة عنه في صنف المُدَرِّب Trainer، فبدلًا من حساب مؤشرات الأداء وتصدير التقارير عنها في نهاية كل دورة تدربية epoch سيجري التقييم هنا في نهاية التدريب إذ سنُجَمع كافة الدفعات باستخدم add_batch ونحسب مؤشرات الأداء. >>> import evaluate >>> metric = evaluate.load("accuracy") >>> model.eval() for batch in eval_dataloader: batch = {k: v.to(device) for k, v in batch.items()} with torch.no_grad(): outputs = model(**batch) logits = outputs.logits predictions = torch.argmax(logits, dim=-1) metric.add_batch(predictions=predictions, references=batch["labels"]) >>> metric.compute() الخلاصة تغرفنا في مقال اليوم على تقنية الصقل fine-tuning لتدريب نماذج الذكاء الاصطناعي المُدَرَّبة مسبقًا وتحسين أدائها على مهام معينة باستخدام بيانات محددة، ووضحنا الفوائد الرئيسية لاستخدام نماذج مُدَرَّبة مسبقًا وكيفية تحميل وتحضير البيانات لها وتدريبها وضبطها وتقييم أدائها من خلال استخدام مكتبات وأطر عمل متنوعة تساعدنا في تنفيذ مهام الصقل مثل Transformers و TensorFlow و Keras و PyTorch كي نحسن من كفاءة وأداء هذه النماذج في مشاريعنا الخاصة. ترجمة -وبتصرف- لقسم Fine-tune a pretrained model من توثيقات Hugging Face. اقرأ أيضًا المقال السابق: المعالجة المُسبقة للبيانات قبل تمريرها لنماذج الذكاء الاصطناعي تعرف على إطار عمل باي تورش PyTorch وأهميته لتطبيقات الذكاء الاصطناعي كيفية بناء شبكة عصبية لترجمة لغة الإشارة إلى اللغة الإنجليزية تعرف على أفضل دورات الذكاء الاصطناعي
-
تحتاج جميع أنواع البيانات مهما كان نوعها سواء كانت نصوصًا أو صورًا أو ملفات صوتية أو غير ذلك إلى معالجة مسبقة أو معالجة تحضيرية قبل تمريرها لنماذج الذكاء الاصطناعي حتى تتدرب عليها، نطلق على هذه العملية اسم "data Preprocessing" إذ تتحول هذه البيانات بعد معالجتها إلى دفعاتٍ من التنسورات tensors التي تمثل بنى ملائمة لتمثيل البيانات تتوافق مع النموذج ويمكنه التعامل معها، وفي هذا المجال توفر مكتبة المحولات Transformers مجموعةً واسعة من أصناف المعالجة تُسّهِل عليك تجهيز بياناتك للنموذج، وهو ما سنتعلمه في مقال اليوم إذ سنُجري معالجة مسبقة لأنواع البيانات التالية: النصوص: سنستخدم المُرَمِّزات Tokenizer لمعالجة النص وتحويله إلى سلسلة رموز tokens، ثم تمثيلها عدديًا وتجميعها على هيئة tensors. الكلام والصوت: سنعتمد على مستخرج الميزات Feature extractor لاستخراج الميزات المتسلسلة من الأمواج الصوتية وتحويلها إلى tensors. الصور: سنتعامل مع معالج الصور ImageProcessor ونُمَرِر له الصور المطلوبة قبل إدخالها للنموذج فيُحولها إلى tensors. الأنماط المتعددة Multimodal: نستخدم في هذه الحالة معالجًا Processor يجمع بين وظيفة المُرَمِّز ووظيفة معالج الصور أو بين وظيفة المُرَمِّز ومستخرج الميزات حسب أنماط البيانات المستخدمة في مشروعك وهل هي تتضمن نصًا وصوتًا، أو نصًا وصورة، أو غير ذلك. ملاحظة: ننصحك باستخدام المعالج التلقائي AutoProcessor فهو يساعدك بصورة كبيرة ويختار لك دومًا دائمًا صنف المعالج المناسب لنموذجك سواء كان مُرَمِّز أو معالج صور أو مستخرج ميزات أو معالج للأنماط المتعددة. قبل البدء بأمثلتنا العملية سنُثَبِّتْ مجموعة البيانات Datasets لنتمكن من تحميل مجموعات بيانات تجريبية لعملنا من خلال كتابة التعليمة التالية: pip install datasets معالجة اللغة الطبيعية المُرَمِّز tokenizer هو الأداة الرئيسية لمعالجة النصوص اللغوية قبل تمريرها للنموذج، إذ يُقَسٍّمُها إلى رموز tokens وفقًا لقواعد خاصة، ثم يحوّل هذه الرموز إلى أعداد ثم إلى تنسورات tensors تمثل المدخلات المقبولة للنموذج، وستُمَرَر أية بيانات إضافية لاحقًا للنموذج له عبر المُرَمِّز. ملاحظة: إذا كنت تخطط لاستخدام أحد النماذج المُدَّربة مسبقًا بدلًا من تدريب نموذجك من الصفر، فاحرص على معالجة بياناتك بالمُرَمِّز نفسه الذي تَدّرَبَ عليه النموذج الجاهز قبل تمريرها له، فهذا يعني أن يُقَسَّم النص الخاص بك بالطريقة نفسها التي قُسِّمَت بها بيانات تدريب النموذج، وأن يُرَمَّز برموزه نفسها (التي يشار إليها بالمفردات vocab). لنبدأ بالتطبيق العملي، حَمِّلْ في البداية مُرَمِّزًا tokenizer يناسب النموذج الذي اخترته، ويتضمن المفردات vocab التي تَدَّرَبَ عليها سابقًا، وذلك بواسطة التابع AutoTokenizer.from_pretrained() وفق التالي: >>> from transformers import AutoTokenizer >>> tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-cased") ثم مَرر النص للمُرَمِّز وستحصل على الخرج التالي: >>> encoded_input = tokenizer("Do not meddle in the affairs of wizards, for they are subtle and quick to anger.") >>> print(encoded_input) {'input_ids': [101, 2079, 2025, 19960, 10362, 1999, 1996, 3821, 1997, 16657, 1010, 2005, 2027, 2024, 11259, 1998, 4248, 2000, 4963, 1012, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]} يعطي المُرَمِّز في خرجه قاموسًا dictionary يتضمن ثلاثة أنواع من العناصر هي: input_ids: الدلالات العددية المقابلة لكل رمز من رموز الجملة أي لكل token. attention_mask: يشير إلى الرموز المهمة التي ينبغي الانتباه لها. token_type_ids: تبين السلسلة التي ينتمي إليها كل رمز، وهي مفيدة عندما تمرر أكثر من سلسلة للمُرَمِّز. يمكنك فك ترميز الخرج السابق لاستعادة النص الأصلي كما يلي: >>> tokenizer.decode(encoded_input["input_ids"]) '[CLS] Do not meddle in the affairs of wizards, for they are subtle and quick to anger. [SEP]' لاحظ أنك استعدت النص مع رمزين إضافيين هما CLS و SEP واحد في مقدمة الجملة، وواحد في نهايتها أضافهما المُرَمِّز، قد لا تتطلب جميع النماذج وجود هذين الرمزين لكنَّ المُرَمِّز يُضيفهما تلقائيًا، علمًا أن الرمز CLS هو اختصار لكلمة المُصَنِّف classifier والرمز SEP اختصار لكلمة الفاصل separator. أما إذا رغبت بمعالجة عدة جمل في آنٍ واحد، فمَرِرها للمُرَمِّز بهيئة قائمة list كما في المثال التالي: >>> batch_sentences = [ "But what about second breakfast?", "Don't think he knows about second breakfast, Pip.", "What about elevensies?", ] >>> encoded_inputs = tokenizer(batch_sentences) >>> print(encoded_inputs) {'input_ids': [[101, 1252, 1184, 1164, 1248, 6462, 136, 102], [101, 1790, 112, 189, 1341, 1119, 3520, 1164, 1248, 6462, 117, 21902, 1643, 119, 102], [101, 1327, 1164, 5450, 23434, 136, 102]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1]]} الحشو Pad يشترط النموذج أن تكون جميع tensors المدخلة إليه بنفس الطول أي بنفس عدد المحارف، وهذا الأمر لا يمكن تحقيقه من دون معالجة النصوص، فالجمل النصية التي نتعامل معها مختلفة الأطوال في معظم الحالات إن لم يكن في جميعها، لذا نلجأ للحشو pad في تطبيقات معالجة اللغة الطبيعية أي إضافة رموز خاصة للجمل القصيرة تسمى (رموز الحشو padding token) لزيادة طولها حتى تتساوى مع الجمل الطويلة فنحصل على طول موحد للمدخلات. اضبط المعامل padding على القيمة True لتُفَعِّل ميزة الحشو في برنامجك كما في المثال التالي: >>> batch_sentences = [ "But what about second breakfast?", "Don't think he knows about second breakfast, Pip.", "What about elevensies?", ] >>> encoded_input = tokenizer(batch_sentences, padding=True) >>> print(encoded_input) {'input_ids': [[101, 1252, 1184, 1164, 1248, 6462, 136, 102, 0, 0, 0, 0, 0, 0, 0], [101, 1790, 112, 189, 1341, 1119, 3520, 1164, 1248, 6462, 117, 21902, 1643, 119, 102], [101, 1327, 1164, 5450, 23434, 136, 102, 0, 0, 0, 0, 0, 0, 0, 0]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]]} كما تلاحظ فإن الجملة الثانية في المثال السابق هي الأطول، لذا أضفنا عددًا من الأصفار لكل من الجملة الأولى والثالثة حتى تتساوى معها بالطول. الاقتطاع Truncation الاقتطاع Truncation هي الحالة المعاكسة للحشو، فقد تصادف في بعض الأحيان سلاسل طويلة جدًا أكبر من الحد المسموح به في النماذج فلا يستطيع النموذج التعامل معها، نلجأ في الحالة لاقتطاع جزء من السلسلة حتى تصبح أقصر. يمكنك استخدام هذه الخاصية بضبط قيمة المعامل truncation على True ليقطتع المُرَمِّز من طول السلسلة المدخلة حتى تتناسب مع الحد الأقصى للطول الذي يقبله النموذج: >>> batch_sentences = [ "But what about second breakfast?", "Don't think he knows about second breakfast, Pip.", "What about elevensies?", ] >>> encoded_input = tokenizer(batch_sentences, padding=True, truncation=True) >>> print(encoded_input) {'input_ids': [[101, 1252, 1184, 1164, 1248, 6462, 136, 102, 0, 0, 0, 0, 0, 0, 0], [101, 1790, 112, 189, 1341, 1119, 3520, 1164, 1248, 6462, 117, 21902, 1643, 119, 102], [101, 1327, 1164, 5450, 23434, 136, 102, 0, 0, 0, 0, 0, 0, 0, 0]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]]} يمكنك مطالعة دليل الحشو والاقتطاع على منصة Hugging Face لمعرفة المزيد حول وسطاء arguments هاتين العمليتين. بناء التنسورات tensors إن عناصر التنسورات tensors هي المخرجات النهائية التي نريدها من المُرَمِّز ففي نهاية الأمر هي المدخلات الوحيدة التي يقبلها النموذج، يمكنك الحصول عليها بضبط قيمة المعامل return_tensors حسب إطار عمل الذكاء الاصطناعي الذي تعتمده. إذا كنت تستخدم إطار العمل بايتورش Pytorch، اضبط قيمة المعامل return_tensors على pt وفق التالي: >>> batch_sentences = [ "But what about second breakfast?", "Don't think he knows about second breakfast, Pip.", "What about elevensies?", ] >>> encoded_input = tokenizer(batch_sentences, padding=True, truncation=True, return_tensors="pt") >>> print(encoded_input) {'input_ids': tensor([[101, 1252, 1184, 1164, 1248, 6462, 136, 102, 0, 0, 0, 0, 0, 0, 0], [101, 1790, 112, 189, 1341, 1119, 3520, 1164, 1248, 6462, 117, 21902, 1643, 119, 102], [101, 1327, 1164, 5450, 23434, 136, 102, 0, 0, 0, 0, 0, 0, 0, 0]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]])} وإذا كنت تستخدم إطار العمل TensorFlowK، اضبط قيمة المعامل return_tensors على tf كما في المثال التالي: >>> batch_sentences = [ "But what about second breakfast?", "Don't think he knows about second breakfast, Pip.", "What about elevensies?", ] >>> encoded_input = tokenizer(batch_sentences, padding=True, truncation=True, return_tensors="tf") >>> print(encoded_input) {'input_ids': <tf.Tensor: shape=(2, 9), dtype=int32, numpy= array([[101, 1252, 1184, 1164, 1248, 6462, 136, 102, 0, 0, 0, 0, 0, 0, 0], [101, 1790, 112, 189, 1341, 1119, 3520, 1164, 1248, 6462, 117, 21902, 1643, 119, 102], [101, 1327, 1164, 5450, 23434, 136, 102, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=int32)>, 'token_type_ids': <tf.Tensor: shape=(2, 9), dtype=int32, numpy= array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=int32)>, 'attention_mask': <tf.Tensor: shape=(2, 9), dtype=int32, numpy= array([[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=int32)>} ملاحظة: تختلف خطوط الأنابيب في قبولها لوسطاء المُرَمِّز عند استدعائها ()__call__، فعلى سبيل المثال يدعم خط الأنابيب text-2-text-generation الوسيط truncation فقط، بينما يسمح خط الأنابيب text-generation بتمرير ثلاثة وسطاء هي text-generation و truncation و add_special_tokens، أما في خط الأنابيب fill-mask فتُمرر وسطاء المُرَمِّز بهيئة قاموس ضمن الوسيط tokenizer_kwargs. معالجة البيانات الصوتية إن أداة المعالجة في المهام الصوتية هي مُستَخرِج الميزات feature extractor حيث تستخدمه لمعالجة البيانات الصوتية الخام وإعدادها قبل إدخالها للنموذج، فيعمل على استخراج الميزات منها ثم تحوّيلها إلى tensors. سنبدأ أولًا بتحميل مجموعة بيانات صوتية مثل MInDS-14 dataset كما يلي: >>> from datasets import load_dataset, Audio >>> dataset = load_dataset("PolyAI/minds14", name="en-US", split="train") اطلب العنصر الأول من العمود audio من مجموعة البيانات لتأخذ لمحة عن تنسيق الدخل الذي نتعامل معه، علمًا أنه بمجرد استدعاء عمود audio سيتحمل الملف الصوتي تلقائيًا ويُعاد أخذ العينات منه: >>> dataset[0]["audio"] {'array': array([ 0. , 0.00024414, -0.00024414, ..., -0.00024414, 0. , 0. ], dtype=float32), 'path': '/root/.cache/huggingface/datasets/downloads/extracted/f14948e0e84be638dd7943ac36518a4cf3324e8b7aa331c5ab11541518e9368c/en-US~JOINT_ACCOUNT/602ba55abb1e6d0fbce92065.wav', 'sampling_rate': 8000} يتضمن الخرج السابق ثلاثة عناصر: array: المصفوفة هي الإشارة الصوتية المُحَمَّلة للكلام، والتي سيُعاد أخذ العينات منها وتشكيلها بهيئة مصفوفة أحادية البعد. path: يُشير إلى مسار تخزين الملف الصوتي. sampling_rate: معدل أخذ العينات، وهو عدد العينات المأخوذة من الإشارة الصوتية في الثانية. استُخدِمَ النموذج Wav2Vec2 في هذا المقال، وإذا قرأت توصيفه ستجد أن معدل أخذ العينات في البيانات الصوتية التي تَدَّرَب عليها هو 16KHz، وبالتالي لضمان سلامة التطبيق بنبغي أن نستخدم المعدل نفسه في البيانات الصوتية التي سنمررها للنموذج، لذا تفقد دائمًا معدل أخذ العينات في بياناتك الصوتية فإذا كان مختلفًا عن معدل النموذج فينبغي لك إعادة أخذ العينات منها وتسمى هذه العملية resample ليصبح لها نفس معدل النموذج، وهذه هي الخطوة الأولى التي سنطبقها تاليًا: 1. استخدم التابع cast_column الخاص بمجموعات البيانات Datasets لرفع معدل أخذ العينات في مجموعتنا إلى 16KHz: >>> dataset = dataset.cast_column("audio", Audio(sampling_rate=16_000)) 2. استدعِ العمود audio من مجموعة البيانات ليُعاد أخذ العينات منه وفق المعدل الجديد وتشكيل الملف الصوتي: >>> dataset[0]["audio"] {'array': array([ 2.3443763e-05, 2.1729663e-04, 2.2145823e-04, ..., 3.8356509e-05, -7.3497440e-06, -2.1754686e-05], dtype=float32), 'path': '/root/.cache/huggingface/datasets/downloads/extracted/f14948e0e84be638dd7943ac36518a4cf3324e8b7aa331c5ab11541518e9368c/en-US~JOINT_ACCOUNT/602ba55abb1e6d0fbce92065.wav', 'sampling_rate': 16000} والآن سنُحمّل مستخرج الميزات لتسوية الملف الصوتي normalize، ومعالجته بالحشو أو الاقتطاع وتحضيره قبل إدخاله للنموذج، ففي معالجة النصوص كنا نضيف أصفارًا "0" إلى السلاسل النصية القصيرة لزيادة طولها، وهنا أيضًا سنضيف أصفارًا "0" إلى المصفوفة الصوتية array فهي لا تؤثر على معناها لأنها تفُسَّر بصفتها لحظات صامتة. حمّل إذًا مُستَخرِج الميزات المناسب لنموذجك بواسطة AutoFeatureExtractor.from_pretrained() كما يلي: >>> from transformers import AutoFeatureExtractor >>> feature_extractor = AutoFeatureExtractor.from_pretrained("facebook/wav2vec2-base") ثم مَرر المصفوفة الصوتية array إلى مستخرج المميزات مع ضبط قيمة الوسيط sampling_rate على معدل أخذ العينات المرغوب لضمان تصحيح أي أخطاء قد تحدث: >>> audio_input = [dataset[0]["audio"]["array"]] >>> feature_extractor(audio_input, sampling_rate=16000) {'input_values': [array([ 3.8106556e-04, 2.7506407e-03, 2.8015103e-03, ..., 5.6335266e-04, 4.6588284e-06, -1.7142107e-04], dtype=float32)]} يمكنك تطبيق الحشو والاقتطاع هنا للتعامل مع السلاسل المتغيرة كما طبقناه مع المُرَمِّز tokenizer، ألقِ نظرة على طول العينتين الصوتيتين أدناه: >>> dataset[0]["audio"]["array"].shape (173398,) >>> dataset[1]["audio"]["array"].shape (106496,) أنشئ الدالة preprocess_function التالية لمعالجة مجموعة البيانات حتى تصبح جميع العينات الصوتية بطولٍ واحد، كل ما عليك هو تحديد الحد الأقصى لطول العينة، وسيعمل مستخرج الميزات على حشو السلاسل أو اقتطاعها لتصل للطول المطلوب: >>> def preprocess_function(examples): audio_arrays = [x["array"] for x in examples["audio"]] inputs = feature_extractor( audio_arrays, sampling_rate=16000, padding=True, max_length=100000, truncation=True, ) return inputs ثم طبِّق الدالة preprocess_function على أول بضع عينات من مجموعة البيانات: >>> processed_dataset = preprocess_function(dataset[:5]) أصبحت جميع العينات الآن بالطول نفسه الذي يماثل الحد الأقصى لطول العينة، ويمكننا تمريرها للنموذج: >>> processed_dataset["input_values"][0].shape (100000,) >>> processed_dataset["input_values"][1].shape (100000,) الرؤية الحاسوبية يستخدم معالج الصور image processor في مشاريع الرؤية الحاسوبية لتجهيز الصور قبل إدخالها للنموذج؛ وتتكون معالجة الصور من عدة مراحل تتحول الصور في نهايتها إلى مدخلات يقبلها النموذج، ومن المراحل على سبيل المثال: تغيير الحجم resizing، والتسوية normalizing، وتصحيح القناة اللونية color channel correction، وأخيرًا تحويل الصور إلى tensors. إن تحسين الصور أو تعزير الصور image augmentation هو الأسلوب الأكثر استخدامًا في المعالجة المسبقة للصور، وهذه أبرز الفوائد المرجوة من كليهما: تعمل ميزة تعزيز الصور image augmentation على تعديل الصور بطريقة تساعدك على الحد من الفرط في التخصيص أي المبالغة في ملائمة البيانات المدخلة للبيانات التي تدرب عليها النموذج، يزيد ذلك من واقعية النموذج ويُحَسِّن أدائه، يمكنك أن تبدع في تعزيز بياناتك وإنشاء نسخ عنها، اضبط السطوع والألوان مثلًا، أو اقتطع من الصور، أو عدّل تدويرها، أو غيّر حجمها بالتكبير أو التصغير أو غير ذلك، ولكن حافظ دائمًا على معناها. تضمن المعالجة المسبقة للصور image preprocessing مطابقة صورك لتنسيق الدخل الذي يقبله النموذج، فالصور المُمَرَّرة لنموذج الرؤية الحاسوبية ينبغي أن تُعالج بالطريقة نفسها التي عولجت فيها الصور التي تَدَّرَبَ عليها النموذج عند تدريبه وضبطه وتسمى عملية الصقل fine-tune. ملاحظة: احرص على استخدام معالج الصور ImageProcessor المرتبط بالنموذج الذي اخترته، ولجهة تعزيز الصور فيمكنك استخدام أي مكتبة تريدها لإنجاز ذلك. لنتعلم معًا كيف نستخدم معالج الصور على مجموعة بيانات كاملة، حمّل في البداية مجموعة البيانات food101 dataset وفق الأمر التالي، ويمكنك الاطلاع على دليل تحميل مجموعات البيانات من Hugging Face لمزيد من المعلومات عن طريقة تحميلها: ملاحظة: سنستخدم هنا المعامل split لأخذ عينة بسيطة من مجموعة البيانات لأنها كبيرة الحجم. >>> from datasets import load_dataset >>> dataset = load_dataset("food101", split="train[:100]") والآن لنأخذ الصورة الأولى من مجموعة البيانات باستخدام الخاصية image كما يلي: >>> dataset[0]["image"] حمّل بعدها معالج الصور المناسب لنموذجك باستخدام التابع AutoImageProcessor.from_pretrained() كما يلي: >>> from transformers import AutoImageProcessor >>> image_processor = AutoImageProcessor.from_pretrained("google/vit-base-patch16-224") يمكننا الآن إضافة بعض الصور باستخدام تعزيز الصور، تستطيع اختيار المكتبة التي تريدها لتطبيق ذلك، مثل Albumentations أو Kornia notebooks، أما في هذا المقال فقد استُخدِمَتْ الوحدة torchvision من مكتبة المحولات transforms وفق الخطوات التالية: سنستخدم اثنين من التحويلات transforms لتعديل الصور في مثالنا، التحويل الأول RandomResizedCrop لتغيير حجم الصور، والثاني ColorJitter لتغيير خصائصها مثل السطوع والتشبع، وسنستخدم الصنف Compose لدمج التحويلين معًا، علمًا أننا نستطيع استخلاص متطلبات الحجم الذي يقبله النموذج من خصائص image_processor إذ تشترط بعض النماذج قياساتٍ محددة للارتفاع والعرض، ويقتصر بعضها الآخر على تحديد أقصر حافة shortest_edge فقط. >>> from torchvision.transforms import RandomResizedCrop, ColorJitter, Compose >>> size = ( image_processor.size["shortest_edge"] if "shortest_edge" in image_processor.size else (image_processor.size["height"], image_processor.size["width"]) ) >>> _transforms = Compose([RandomResizedCrop(size), ColorJitter(brightness=0.5, hue=0.5)]) سننشئ دالةً تجمع بين وظيفتي معالجة الصور وتعزير الصور (التي أجريناها في الخطوة السابقة)، تُطَبَّق هذه الدالة على دفعات الصور التي سنمررها لها وتعطينا في النهاية pixel_values التي تُعدّ مدخلات النموذج، إذ إن المرحلة الثانية من الدالة تتضمن استخدام معالج الصور، وهو يتكفل بتسوية الصور وتحويلها إلى tensors مناسبة للنموذج، ألقِ نظرة على الأوامر التالية لتتضح لك الفكرة: >>> def transforms(examples): images = [_transforms(img.convert("RGB")) for img in examples["image"]] examples["pixel_values"] = image_processor(images, do_resize=False, return_tensors="pt")["pixel_values"] return examples لابُدّ أنك لاحظت أننا ضبطنا المعامل الخاص بتعديل حجم الصور في معالج الصور على القيمة do_resize=False، لأننا لا نحتاجه هنا فقد أنجزنا تعديل حجم الصور بالفعل في مرحلة تعزيز الصور (أي في الخطوة الأولى) مستفيدين من خاصية الحجم في معالج الصور image_processor لمعرفة حدود الحجم المقبولة في النموذج، أما إذا لم تعدل حجم الصور في مرحلة تعزيز الصور، فاترك المعامل do_resize على قيمته الافتراضية وسيُعَدِّل معالج الصور أحجام صورك بما يتناسب مع متطلبات النموذج. وبالمقابل إذا رغبت بإجراء تسوية normalize للصور ضمن مرحلة تعزيز الصور فاستخدم عندها الخاصيتين image_processor.image_mean و image_processor.image_std. استخدم الآن datasets.set_transform لتسريع نشر التحويلات السابقة transforms على مجموعة البيانات: >>> dataset.set_transform(transforms) استدعِ الصورة الأولى من مجموعة البيانات ولاحظ كيف تغيرت فقد أضاف إليها التحويل قيم pixel_values، وبذلك أصبحت مجموعة البيانات dataset جاهزة لإدخالها إلى النموذج: dataset[0].keys() ألقِ نظرة على الصورة بعد التعديل، فقد اقتطع جزءٌ عشوائيٌ منها، وتغيرت خصائصها اللونية. >>> import numpy as np >>> import matplotlib.pyplot as plt >>> img = dataset[0]["pixel_values"] >>> plt.imshow(img.permute(1, 2, 0)) ملاحظة:لا يقتصر عمل معالج الصور ImageProcessor على المعالجة المُسبقة أو التحضيرية للبيانات قبل إدخالها للنموذج، بل يستخدم أيضًا في المعالجة اللاحقة post processing فيعالج المخرجات الأولية لنموذج الذكاء الاصطناعي ويحولها إلى تنبؤات لها معنى مثل المربعات المحيطة bounding boxes وخرائط التقسيم segmentation maps. يُعدّ هذا مفيدًا في مهام مثل التَعرُّف على الكائنات object detection، والتجزئة الدلالية للصور semantic segmentation أي تقسيم الصورة إلى أجزاء بحيث يعبر كل جزء عن صنف معينة، وتجزئة المثيل instance segmentation وهي تشابه التجزئة الدلالية ولكن بالإضافة إلى تحديد الأصناف، تفصل المثيلات الفردية داخل نفس الصنف، وتجزئة panoptic segmentation الشاملة التي تنتج خرائط تقسيم شاملة تحدد كل جزء من الصورة وتوضح الصنف أو الكائن الذي ينتمي إليه. الحشو Pad تطبقُ بعض النماذج عملية تعزيز الصور في أثناء التدريب، مثل نموذج DETR المخصص للتعرف على الكائنات، وتسبب هذه الحالة اختلافًا في أحجام الصور ضمن الدفعة الواحدة batch، يتعامل DETR مع هذه الحالة باستخدام تابع الحشو image_processor.pad() من الصنف DetrImageProcessor مع وضع قناع "pixel_mask" يظهر أي البيكسلات هي بيكسلات الحشو، بالإضافة إلى تعريف الدالة collate_fn وفق التالي لتجميع الصور: >>> def collate_fn(batch): pixel_values = [item["pixel_values"] for item in batch] encoding = image_processor.pad(pixel_values, return_tensors="pt") labels = [item["labels"] for item in batch] batch = {} batch["pixel_values"] = encoding["pixel_values"] batch["pixel_mask"] = encoding["pixel_mask"] batch["labels"] = labels return batch الأنماط المتعددة يُستَخدم المعالج processor لتحضير البيانات متعددة الأنماط multimodal قبل إدخالها لنماذج الذكاء الاصطناعي، ويجمع المعالج الواحد أكثر من وظيفة معالجة من الوظائف التي تعرفنا عليها، مثل المُرَمِّز ومُستَخرِج الميزات أو غير ذلك حسب مزيج الأنماط الموجود في بياناتك نصوص أو صور أو صوت. حمِّل مثلًا مجموعة البيانات LJ Speech متعددة الأنماط وطبق المثال التالي لتتعلم طريقة التَعَرُّف التلقائي على الكلام (ASR) فيها: >>> from datasets import load_dataset >>> lj_speech = load_dataset("lj_speech", split="train") نُرَكِز اهتمامنا على الصوت audio والنص text في مهام التَعَرُّف التلقائي على الكلام (ASR) لذا سنبدأ بإزالة الأعمدة الأخرى من مجموعة البيانات وفق التالي: >>> lj_speech = lj_speech.map(remove_columns=["file", "id", "normalized_text"]) لنستعرض الآن عمودي الصوت والنص: >>> lj_speech[0]["audio"] {'array': array([-7.3242188e-04, -7.6293945e-04, -6.4086914e-04, ..., 7.3242188e-04, 2.1362305e-04, 6.1035156e-05], dtype=float32), 'path': '/root/.cache/huggingface/datasets/downloads/extracted/917ece08c95cf0c4115e45294e3cd0dee724a1165b7fc11798369308a465bd26/LJSpeech-1.1/wavs/LJ001-0001.wav', 'sampling_rate': 22050} >>> lj_speech[0]["text"] 'Printing, in the only sense with which we are at present concerned, differs from most if not from all the arts and crafts represented in the Exhibition' كما تعلمنا في الفقرات السابقة ينبغي أن يتطابق معدل أخذ العينات في بياناتنا الصوتية المدخلة للنموذج مع معدل أخذ العينات في البيانات الصوتية التي تَدَرَّبَ عليها النموذج سابقًا، لذا سنجري عملية إعادة أخذ للعينات resample في بياناتنا وفق التالي: >>> lj_speech = lj_speech.cast_column("audio", Audio(sampling_rate=16_000)) يمكننا الآن تحميل المعالج المناسب لحالتنا باستخدام AutoProcessor.from_pretrained(): >>> from transformers import AutoProcessor >>> processor = AutoProcessor.from_pretrained("facebook/wav2vec2-base-960h") يتبقى لنا خطوتان قبل تمرير البيانات للنموذج: إنشاء الدالة prepare_dataset التالية التي ستُعَالِج البيانات الصوتية الموجودة في array (أحد عناصر الخرج لمستخرج الميزات كما تعلمنا في فقرة الصوتيات) وتُحَوّلها إلى input_values، وأيضًا تُرَمِّز tokenize النص text ليتحول إلى labels، وبذلك نكون قد جهزّنا مُدخلات النموذج: >>> def prepare_dataset(example): audio = example["audio"] example.update(processor(audio=audio["array"], text=example["text"], sampling_rate=16000)) return example 2. تطبيق الدالة على مجموعة البيانات dataset، سنُطّبِّقها هنا على عينة منها: >>> prepare_dataset(lj_speech[0]) إذًا فقد جَهَّز المعالج بياناتنا للنموذج والتي تتكون من input_values و labels، وضبط معدل أخذ العينات على 16KHz، وبالتالي يمكننا الآن تمرير البيانات إلى النموذج. الخلاصة المعالجة المسبقة للبيانات قبل تمريرها لنماذج الذكاء الاصطناعي خطوة أساسية لا غنى عنها في أي مشروع، وتختلف أساليب المعالجة حسب نوع بياناتك صوت أو صورة أو نص أو غيرها، لكنها تنتهي دائمًا بتحويل البيانات الخام إلى tensors يقبلها النموذج، ولا تنسَ أن الأصناف التلقائية في مكتبة Transformers مثل: AutoTokenizer و AutoProcessor و AutoImageProcessor و AutoFeatureExtractor تساعدك على اختيار المعالج المسبق المناسب لنموذجك، وهذا مهمٌ جدًا عند استخدامك النماذج المدربة مسبقًا إذ ينبغي تُعالج بيانات مشروعك بالمعايير نفسها التي عولجت بها البيانات التي تَدَرَّبَ عليها النموذج قبلًا فتستخدم المفردات vocab نفسها مثلًا في مهام معالجة النص، ومعدل أخذ العينات نفسه والمهام الصوتية وما إلى ذلك. ترجمة -وبتصرف- لقسم Preprocess من توثيقات Hugging Face. اقرأ أيضًا المقال السابق: تحميل النماذج المُدّربة مسبقًا باستخدام الأصناف التلقائية AutoClasses في مكتبة Transformers استخدام خطوط الأنابيب Pipelines للاستدلال في تطبيقات الذكاء الاصطناعي أفضل دورات الذكاء الاصطناعي مصطلحات الذكاء الاصطناعي للمبتدئين
-
تعد دوكر Docker واحدة من الأدوات المشهورة والمستخدمة على نطاق واسع لتغليف التطبيقات ضمن حاويات containerization، وهي تساعد المطورين على إنشاء وإدارة حاويات لينكس المحمولة والمتسقة فيما بينها إذ توفر دوكر بيئة معزولة لتشغيل التطبيقات مما يسهل نقلها وتشغيلها عبر مختلف البيئات دون مشاكل التوافق. ويحتاج المطورون عند تطوير الحاويات ونشرها إلى أوامر برمجية تعينهم على مراقبة حالتها في أثناء التشغيل أو حل مشكلاتها، وهذا ما يوفره الأمر docker exec الذي يسمح لك بتشغيل البرامج في حاويات Docker مشغلة مسبقًا. سنوضح في مقال اليوم ما هو الأمر docker exec؟ وما هي خياراته؟ وكيف تستخدمه لتنفيذ الأوامر في الحاويات قيد التشغيل وطريقة الحصول على واجهة صدفة shell تفاعلية في داخل الحاوية، كما سنعرفك على أبرز الأخطاء التي قد تظهر لك عند تنفيذ هذا الأمر وكيفية التعامل معها وحلها. متطلبات العمل يتطلب تطبيق خطوات المقال وجود Docker مُثبتًا على حاسوبك أو خادمك الذي ستعمل منه، ومستخدمًا يمتلك صلاحيات تشغيل الأمر docker، وإذا احتجت لتشغيل docker بصلاحية الجذر root احرص على إضافة sudo قبل كتابتك الأوامر الواردة هنا. يمكنك الاطلاع على مقال كيفية تثبيت دوكر واستخدامه على دبيان لمعرفة خطوات تثبيت Docker، وستجد فيه فقرةً خاصة عن كيفية استخدام Docker دون كتابة sudo في بداية كل أمر في حال فضلت عدم كتابتها. تشغيل حاوية تجريبية يُستخدم الأمر docker exec كما ذكرنا سابقًا مع الحاويات قيد التشغيل، لذا إن لم يكن لديك حاوية تعمل أنشئ واحدة تجريبية وشغّلها باستخدام الأمر docker run وفق التالي: $ docker run -d --name container-name alpine watch "date >> /var/log/date.log" يُنشئ هذا الأمر حاويةً جديدة بالاعتماد صورة image لتوزيعة Alpine الرسمية الموجود في مستودعات دوكر. إذ إن Alpine Linux من أشهر توزيعات لينكس المستخدمة مع الحاويات بسبب خفتها في استخدام الموارد وصغر حجمها. تشير الراية d- إلى فصل الحاوية عن نافذة الطرفية، فتعمل الحاوية في الخلفية، أما name container-name-- فيُحدد اسم الحاوية، سُميّت الحاوية هنا container-name، اكتب الاسم الذي تريده أو يمكنك عدم كتابة أي اسم وترك دوكر Docker يعطي الحاوية اسمًا عشوائيًا فريدًا. ويأتي بعد اسم الحاوية اسم الصورة التي ستُنشئ الحاوية انطلاقًا منها، واسمها في حالتنا alpine. وبعدها اكتب الأمر الذي تريد تشغيله داخل الحاوية، وهو في مثالنا "watch "date >> /var/log/date.log. افتراضيًا يكرر الأمر watch تشغيل الأمر المكتوب بعده أي "date >> /var/log/date.log" كل ثانيتين، ويعرض الأمر date التاريخ والوقت الحاليين على الخادم، كما يلي: Fri Jan 26 14:57:05 UTC 2024 أما الجزء /var/log/date.log/ << فيعني أن نتيجة تنفيذ الأمر date ستُخزن في ملف نصي يدعى /var/log/date.log/، وبالتالي سيُكتب سطرٌ جديد كل ثانيتين في هذا الملف، وبعد عدة ثواني سيبدو محتواه على الشكل التالي: Fri Jan 26 15:00:26 UTC 2024 Fri Jan 26 15:00:28 UTC 2024 Fri Jan 26 15:00:30 UTC 2024 Fri Jan 26 15:00:32 UTC 2024 Fri Jan 26 15:00:34 UTC 2024 سنعرض في الفقرة التالية طريقة اكتشاف أسماء الحاويات المشغلة على خادم معين، لتستفيد منها في الحالات التي تستهدف فيها العمل على حاوية لا تعرف اسمها بدقة. اكتشاف اسم حاوية Docker بما أن الأمر docker exec يعمل مع الحاويات قيد التشغيل، لذا من المهم معرفة اسم الحاوية أو المعرّف الخاص بها Container ID لتمريره له، ويمكنك معرفة أسماء الحاويات ومعلوماتٍ أخرى عنها بواسطة الأمر docker ps كما يلي: $ docker ps يعرض لك هذا الأمر أسماء الحاويات التي تعمل على الخادم، مع معلوماتٍ عامة عنها، مثل: صورة الحاوية، والأمر الذي تنفذه الآن، وحالة تشغيلها وغيرها، ألقِ نظرة على خرج الأمر docker ps: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 76aded7112d4 alpine "watch 'date >> /var…" 11 seconds ago Up 10 seconds container-name ستلاحظ في الخرج السابق وجود اسم الحاوية والمُعرّف الخاص بها، وهما ينوبان عن بعضهما، فيمكنك تمرير أي واحد منهما للأمر docker exec للتعامل مع الحاوية. وإذا رغبت بتغيير اسم الحاوية مثلًا، فاستخدم الأمر docker rename، وفق التالي: $ docker rename container-name new-name الآن بعد أن تعلمنا كيف نحصل على اسم الحاوية، سنعرض بعضًا من الأمثلة العملية على طريقة استخدام docker exec لتنفيذ الأوامر في حاوية قيد التشغيل. تشغيل صدفة Sell تفاعلية في حاوية Docker تحتاج في كثير من الحالات لتشغيل صدفة Shell تفاعلية داخل حاوية Docker لاستعراض نظام ملفاتها مثلًا أو لتقصي أخطاء بعض العمليات ضمنها وتصحيحها في أثناء التنفيذ أو لغير ذلك من الأسباب، يوفر لك docker exec هذه الإمكانية عبر استخدامه مع الرايتين i- و t-. تُبقي الراية i- إمكانية إدخال البيانات إلى الحاوية متاحة، بينما تُنشئ الراية t- طرفيةً وهمية pseudo-terminal ترتبط بالصدفة shell، ويمكنك استخدام الرايتين معًا في أمرٍ واحد وفق التالي: $ docker exec -it container-name sh ينتج عن الأمر السابق تشغيل الصدفة Shell أو sh في الحاوية المحددة، وفتح مِحث أوامر Shell prompt اعتيادي لاستخدامه. وتستطيع الخروج من الحاوية بكتابة exit ثم الضغط على ENTER. / # exit إذا احتوت صورة الحاوية التي تستخدمها صدفةً Shell متقدمة مثل باش Bash، فيمكنك العمل معها باستبدال الرمز sh في الأمر السابق بالرمز bash. تشغيل أوامر غير تفاعلية في حاوية Docker يمكنك استخدام docker exec بلا رايات Flags، لتنفيذ الأوامر التي لا تتطلب تفاعلًا مع حاويات Docker قيد التشغيل، ألقِ نظرة على المثال التالي: $ docker exec container-name tail /var/log/date.log يُشغّل السطر السابق الأمر tail /var/log/date.log في الحاوية المسماة container-name ثم يعرض لك النتائج، إذ يُظهِر الأمر tail آخر عشر أسطر من ملف الخرج على الشاشة، والذي يبدو مثل التالي: Mon Jan 29 14:39:33 UTC 2024 Mon Jan 29 14:39:35 UTC 2024 Mon Jan 29 14:39:37 UTC 2024 Mon Jan 29 14:39:39 UTC 2024 Mon Jan 29 14:39:41 UTC 2024 Mon Jan 29 14:39:43 UTC 2024 Mon Jan 29 14:39:45 UTC 2024 Mon Jan 29 14:39:47 UTC 2024 Mon Jan 29 14:39:49 UTC 2024 Mon Jan 29 14:39:51 UTC 2024 لاحظ أن ما نفذناه هنا يشبه فتح صدفة Shell تفاعلية (كما في المثال السابق docker exec -it container-name sh) ثم استدعاء الأمر tail /var/log/date.log لعرض النتائج، فقد حصلنا في الحالتين على الخرج نفسه، ولكننا في الحالة الأولى (أي التفاعلية) فتحنا الصدفة Shell، ثم نفذنا الأمر، وبعدها أغلقنا الصدفة، أما هنا فتمت العملية بأمرٍ واحد فقط وبدون الحاجة لفتح طرفية وهمية. تنفيذ الأوامر في مجلد بديل ضمن الحاوية Docker يمكنك تحديد مجلد العمل أو المجلد الذي تود تنفيذ الأوامر فيه ضمن الحاوية بكتابة مساره بعد الراية workdir-- كما في المثال التالي: $ docker exec --workdir /tmp container-name pwd حددنا هنا المجلد tmp/ على أنه مجلد العمل، وسيُنفذ ضمنه الأمر pwd، ووظيفة pwd عرض اسم مجلد العمل الحالي، وستحصل بذلك على الخرج التالي: /tmp الذي يؤكد أن tmp/ هو مجلد العمل الحالي الذي نُفِّذ فيه الأمر لأنه ظهر في خرج pwd. تشغيل الأوامر بصفة مستخدم آخر في حاوية Docker استخدم الراية user-- إذا رغبت بتنفيذ أمرٍ ما ضمن الحاوية بصفتك مستخدمًا آخر أي بصلاحياته والمحددات الخاصة به، كما في المثال التالي: $ docker exec --user guest container-name whoami يُنفّذ السطر السابق الأمر whoami بصفة المستخدم guest، وبما أن الأمر whoami يستعمل عادةً لمعرفة هوية المستخدم الحالي فستحصل على هذا الخرج: guest كما تلاحظ فقد أكدّ خرج الأمر whoami أن guest هو المستخدم المُنَفِذ. تمرير المتغيرات إلى داخل حاوية Docker تساعدك الراية e- على تمرير قيم متغيرات البيئة environment variables إلى داخل الحاوية لتُستخدم عند تنفيذ الأمر، ألقِ نظرة على المثال التالي: $ docker exec -e TEST=sammy container-name env يُسنِد هذا الأمر القيمة sammy إلى المتغير TEST ثم يُشغّل الأمر env في داخل الحاوية، علمًا أن وظيفة الأمر env هي عرض كافة متغيرات البيئة أمامك على الشاشة، مثل التالي: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=76aded7112d4 TEST=sammy HOME=/root لاحظ أن المتغير TEST أصبح يحمل القيمة sammy بالفعل. أما إذا احتجت لتمرير عدة متغيرات فاكتبهم بالترتيب واكتب الراية e- قبل كل متغير منهم، وفق ما يلي: $ docker exec -e TEST=sammy -e ENVIRONMENT=prod container-name env تستطيع أيضًا تمرير كافة متغيرات البيئة التي تحتاجها إلى الحاوية ضمن ملفٍ نصي يحتوي على قيمها، وذلك بواسطة الراية env-file--. لكن أول ما عليك فعله تجهيز ملف المتغيرات، لذا افتح أي محرر نصوص تفضله مثل نانو nano أو غيره، وأنشئ بواسطته ملفًا جديدًا كما يلي: $ nano .env اعتمدنا env. اسمًا لملف متغيرات البيئة، لأنه الاسم المتعارف عليه لهذا النوع من الملفات، والذي يعتمده معظم المطورين لتسمية ملفاتهم الخاصة بإدارة معلومات التطبيق الخارجة عن عمليات التحكم بالإصدار. عرّف الآن متغيرات بيئتك بكتابتها ضمن الملف بهيئة ثنائيات اسم وقيمة KEY=value، وبمعدل متغير واحد في كل سطر، كما في المثال أدناه: TEST=sammy ENVIRONMENT=prod احفظ التغييرات على الملف، وأغلقه، لننتقل للخطوة التالية. إذا كنت تستخدم محرر النصوص نانو nano، فاضغط على CTRL+O ثم ENTER لحفظ التغييرات، وبعدها اضغط على CTRL+X للخروج. نَفِّذ الآن الأمر docker exec بعد كتابة اسم ملف التغيرات بدقة بعد الراية env-file-- كما يلي: $ docker exec --env-file .env container-name env وسيظهر أمامك الخرج التالي: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=76aded7112d4 TEST=sammy ENVIRONMENT=prod HOME=/root لاحظ القيم التي حددناها للمتغيرين إنها مضبوطة تمامًا كما كتبناها ملف المتغيرات. يمكنك أيضًا تمرير عدة ملفات لمتغيرات البيئة مع تكرار كتابة الراية env-file-- قبل كل واحد منها، وإذا حصل أي تعارض بينها في قيم المتغيرات، فإن الملف اللاحق سيكون ذا أولوية أعلى من الملفات التي تسبقه. أخطاء شائعة ستواجه بعض الأخطاء الشائعة عند تعاملك مع الأمر docker exec، سنذكر أبرزها، انظر مثلًا الخطأ التالي: Error: No such container: container-name يعني الخطأ No such container أن الحاوية التي تطلبها غير موجودة، أو أنك أخطأت في كتابة اسمها، يمكنك استخدام الأمر docker ps لتحري أسباب الخطأ والتحقق من صحة اسم الحاوية التي تريدها فهو يعرض أسماء جميع الحاويات المُشغلة على الخادم. وهذا خطأ آخر: Error response from daemon: Container 2a94aae70ea5dc92a12e30b13d0613dd6ca5919174d73e62e29cb0f79db6e4ab is not running تعني رسالة not running أن الحاوية موجودة ولكنها متوقفة عن العمل، يمكنك تشغيلها بالأمر docker start container-name. أما الخطأ الأخير الذي نعرضه فهو: Error response from daemon: Container container-name is paused, unpause the container before exec يشرح الخطأ نفسه بدقة، فالرسالة Container is paused تعني أن الحاوية موقفة عن العمل مؤقتًا، يمكنك إزالة الإيقاف المؤقت وإعادة تشغيلها باستخدام الأمر docker unpause container-name. الخلاصة عرضنا في مقال اليوم طريقة تنفيذ الأوامر في حاويات Docker قيد التشغيل، ووضحنا أهم الخيارات المتاحة لتستخدمها حسب احتياجات عملك، ويمكنك معرفة المزيد عن هذه الحاويات والتعامل معها بالإطلاع على مقالات قسم Docker على أكاديمية حسوب. ترجمة -وبتصرف- للمقال How To Use docker exec to Run Commands in a Docker Container لصاحبه Brian Boucheron. اقرأ أيضًا مدخل إلى دوكر Docker أساسيات تنسيق الحاويات ما هي تقنية Docker؟ تثبيت الأداة دوكر كومبوز واستخدامها ضمن نظام أوبونتو
-
في ظل التنوع الكبير للبنى الموجودة في مكتبة المحوّلات Transformers قد يصعب عليك إنشاء بنية مناسبة لنقطة التحقق من النموذج checkpoint الخاصة بك وهنا يأتي دور الأصناف التلقائية AutoClasses التي توفرها مكتبة Transformers التي حرص مطوروها على جعلها مكتبةً مرنة وسهلة الاستخدام حيث تستنتج هذه الأصناف التلقائية البنية المناسبة لنقطة تحقق معينة وتُحمّلها لك تلقائيًا. فأنت تستطيع بواسطة الدالة from_pretrained() في مكتبة Transformers تحميل نموذج مُدَّرب مسبقًا لأي بنية تريدها بسرعة وسهولة ومن دون الحاجة لتدريبه من الصفر وهدر الوقت والموارد، ويمكننا القول أن إنتاج هذا النوع من الشيفرات البرمجية المعزول كليًّا عن نقاط التحقق checkpoint-agnostic code والتي يمكن استخدامها مع نماذج مختلفة دون تغيير يساعد المطورين جدًا، فالشيفرة التي تعمل مع نقطة تحقق معينة ستعمل مع أي نقطة تحقق أخرى حتى لو كانت مختلفة بالبنية طالما الشيفرة مُدربة سابقًا على مهمة مماثلة. سنتطرق للنقاط التالية في هذا المقال: تحميل مُرَمِّز مُدَّرب مسبقًا pretrained tokenizer. تحميل معالج صور مُدَّرب مسبقًا pretrained image processor. تحميل مُستخرج ميزات مُدَّرب مسبقًا pretrained feature extractor. تحميل معالج مُدَّرب مسبقًا pretrained processor. تحميل نموذج مُدَّرب مسبقًا pretrained model. تحميل نموذج ليكون backbone. المُرَمِّز التلقائي AutoTokenizer تبدأ معظم مهام معالجة اللغة الطبيعية (NLP) بمُرَمِّز AutoTokenizer يحول المُدخَلات إلى تنسيق مناسب للنموذج حتى يستطيع معالجتها. علينا أولًا تحميل المُرَمِّز المناسب للنموذج باستخدام الدالة AutoTokenizer.from_pretrained(): >>> from transformers import AutoTokenizer >>> tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-uncased") ثم تُرَمِّزْ المدخلات باستخدامه كما يلي: >>> sequence = "In a hole in the ground there lived a hobbit." >>> print(tokenizer(sequence)) {'input_ids': [101, 1999, 1037, 4920, 1999, 1996, 2598, 2045, 2973, 1037, 7570, 10322, 4183, 1012, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]} معالج الصور التلقائي AutoImageProcessor يعد معالج الصور AutoImageProcessor أداة مهمة في مكتبة Transformers تسهل عملية تحضير الصور للنماذج المختصة بالرؤية الحاسوبية، مما يجعل استخدام هذه النماذج أكثر سهولة وفعالية. تشبه وظيفة معالج الصور وظيفة المُرَمِّز AutoTokenizer فهو يُحَضِّر البيانات قبل إدخالها للنموذج، لكنه يستخدم في مهام الرؤية الحاسوبية فيكفل معالجة الصور المدخلة بالتنسيق الصحيح الملائم للنموذج، كما في المثال التالي: >>> from transformers import AutoImageProcessor >>> image_processor = AutoImageProcessor.from_pretrained("google/vit-base-patch16-224") AutoBackbone AutoBackbone هو أداة قوية تسمح باستخدام نماذج معقدة مثل Swin Transformer بطريقة مرنة، حيث يمكن استخراج ميزات الصور على مستويات مختلفة من التفاصيل حسب متطلبات المهمة التي نقوم بها. ألقِ نظرة على الصورة التالية في البداية التي تبين نموذج Backbone الخاص بمحوّل Swin Transformer متعدد المراحل لاستخراج ميزات الصور: يساعدك AutoBackbone على استخدام النماذج المُدّربة مسبقًا بصفتها بنى أساسية Backbones مكونة من عدة مراحل تعطيك خرائط الميزات feature maps لبياناتك بطريقة تدريجية على عدة مراحل حتى تصل إلى أفضل نتيجة، ويمكنك تحديد المرحلة التي تريد استخراج الخرائط عندها بواسطة معاملات الدالة from_pretrained() إذ ينبغي لك تحديد معامل واحد على الأقل من المعاملات التالية: المعامل out_indices: يشير إلى فهرس index الطبقة التي ستستخرج خريطة الميزات منها. المعامل out_features: يدل على اسم الطبقة التي ستستخرج خريطة الميزات منها. وإذا استخدمت المعاملين معًا فاحرص على التوافق بينهما، أما في الحالات التي لا تُمرر فيها أي معامل فستعبر بياناتك جميع مراحل Backbone وتحصل على خريطة الميزة من المرحلة الأخيرة. يبين المثال التالي كيفية الحصول على خريطة الميزات من المرحلة الأولى لنموذج Swin backbone عبر ضبط قيمة المعامل out_indices=(1,)، لاحظ الصورة ثم التعليمات البرمجية المرتبطة: >>> from transformers import AutoImageProcessor, AutoBackbone >>> import torch >>> from PIL import Image >>> import requests >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg" >>> image = Image.open(requests.get(url, stream=True).raw) >>> processor = AutoImageProcessor.from_pretrained("microsoft/swin-tiny-patch4-window7-224") >>> model = AutoBackbone.from_pretrained("microsoft/swin-tiny-patch4-window7-224", out_indices=(1,)) >>> inputs = processor(image, return_tensors="pt") >>> outputs = model(**inputs) >>> feature_maps = outputs.feature_maps يمكنك الآن الوصول إلى الكائن feature_maps الخاص بخرائط الميزات وفق التالي: >>> list(feature_maps[0].shape) [1, 96, 56, 56] مُسْتَخْرِج الميزات التلقائي AutoFeatureExtractor يستخدم مُسْتَخْرِج الميزات في معالجة الصوتيات ويمكنك من معالجة الملفات الصوتية بالتنسيق الصحيح قبل إدخالها للنموذج، وفيما يلي طريقة تحميله بواسطة الدالة AutoFeatureExtractor.from_pretrained(): >>> from transformers import AutoProcessor >>> processor = AutoProcessor.from_pretrained("microsoft/layoutlmv2-base-uncased") النموذج التلقائي AutoModel تتيح لك أصناف النماذج التلقائية AutoModel تحميل نموذج جاهز مُدَّرب مسبقًا يناسب المهمة التي تعمل عليها، وتختلف هذه الأصناف classes حسب إطار العمل الذي تستخدمه، ففي إطار عمل بايتورشPytorch تكون من نوع AutoModelFor (ويمكنك الاطلاع على كافة المهام التي تدعمها أصناف التلقائية بمراجعة القسم الخاص بها على Hugging Face)، تستطيع مثلًا يمكنك تحميل نموذج ملائم لتصنيف السلاسل sequence classification بواسطة AutoModelForSequenceClassification.from_pretrained() كما يلي: يمكنك استخدام نقطة التحقق نفسها لمهمة مختلفة: >>> from transformers import AutoModelForTokenClassification >>> model = AutoModelForTokenClassification.from_pretrained("distilbert/distilbert-base-uncased") ملاحظة: في نماذج PyTorch تُحَمّل النماذج بواسطة التابع from_pretrained() المعتمدة على torch.load() التي تستخدم بدورها الوحدة pickle من بايثون وهي غير آمنة، لذا ننصحك بعدم تحميل أي نماذج مجهولة المصدر أو غير موثوقة حتى لا تَضُّرْ بمشروعك، واحرص دائمًا على تحميلها من منصات معروفة مثل النماذج العامة الموجودة في مستودع Hugging Face فهي أقل خطرًا إذ تفحصها المنصة عند كل commit لضمان خلوها من البرمجيات الخبيثة، ويمكنك الاطلاع على توثيقات المستودع لمعرفة أفضل الممارسات في هذا المجال مثل استخدام GPG في التحقق من أن عمليات commit في النموذج موقعة من طرف موثوق وغيرها من طرق الحماية. بالمقابل تُعدّ نقاط تحقق checkpoints لكل من إطاري TensorFlow و Flax محمية من هذا الخطر، ويمكنك الاستفادة من ذلك وتجاوز المخاطر السابقة بتحميل هذه النماذج ضمن بنى PyTorch أي بتحويلها إلى نماذج PyTorch باستخدام from_tf و from_flax بالصيغة kwargs مع from_pretrained. توجد أصناف تلقائية مشابهة في إطار العمل TensorFlow وتتيح أيضًا تحميل نماذج مُدَّربة مسبقًا لمهمة محددة تختارها، تسمى أصناف TFAutoModelFor (يمكنك الاطلاع على كافة مهام المعالجة التي تدعمها)، يبين المثال التالي طريقة استخدام TFAutoModelForSequenceClassification.from_pretrained() لتحميل نموذج مناسب لتصنيف السلاسل: >>> from transformers import TFAutoModelForSequenceClassification model = >>> TFAutoModelForSequenceClassification.from_pretrained("distilbert/distilbert-base-uncased") تستطيع بسهولة إعادة استخدام نقطة التحقق التي وصلت إليها لتحميل بنية تناسب مهمة أخرى مختلفة كما في المثال التالي: >>> from transformers import TFAutoModelForTokenClassification >>> model = TFAutoModelForTokenClassification.from_pretrained("distilbert/distilbert-base-uncased") ختامًا يوصى باستخدام صنف المُرَمِّز التلقائي AutoTokenizer وأصناف النموذج التلقائي AutoModelFor و TFAutoModelFor (حسب إطار العمل الذي تعتمده) لتحميل مثيلات النماذج المُدَّربة مُسبقًا لمشروعك فهي تضمن استخدامك البنية الصحيحة والمناسبة لمهمتك في كل مرة. تابع معنا مقالات السلسلة واقرأ مقالنا التالي عن المعالجة المسبقة للبيانات قبل تمريرها لنماذج الذكاء الاصطناعي لتتعلم كيف تعالج مجموعات بياناتك وتضبطها بدقة قبل تمريرها إلى النموذج وذلك عبر تقنيات المعالجة المختلفة، مثل المُرَمِّز، ومعالج الصور، ومستخرج الميزات، ومعالج الأنماط المتعددة. ترجمة -وبتصرف- لقسم Load pretrained instances with an AutoClass من توثيقات Hugging Face. اقرأ أيضًا المقال السابق: استخدام خطوط الأنابيب Pipelines للاستدلال في تطبيقات الذكاء الاصطناعي من هو مهندس الذكاء الاصطناعي وما أبرز مهامه مقدمة إلى الذكاء الاصطناعي التوليدي Generative AI مكتبات وأطر عمل الذكاء الاصطناعي: القوة الكامنة خلف الأنظمة الذكية
-
الاستضافة السحابية Cloud Hosting هي التقنية التي تتيح لك استخدام الخوادم الافتراضية حسب طلبك عبر الإنترنت، إذ يمكنك إنشاؤها أو تعديلها أو حذفها وفق ما تريد. تُستضاف الخوادم السحابية على خوادم مادية موجودة لدى مزود الخدمة، وتأخذ منها الموارد الحاسوبية التي تحتاجها من وحدات معالجة وذواكر ومساحات تخزين وغيرها، وبإمكانك تثبيت نظم التشغيل والبرمجيات التي تختارها على الخوادم السحابية. تستخدم الاستضافة السحابية عمومًا لاستضافة مواقع الويب أو التطبيقات الموزعة المعتمدة على الويب أو أي خدمات أخرى مشابهة. يتناول المقال المفاهيم الأساسية للاستضافة السحابية، ويشرح آلية عمل الحوسبة الافتراضية Virtualization مع عرضٍ لأهم مكوناتها، وأخيرًا مقارنة موجزة بين الاستضافة السحابية والاستضافات الأخرى. ما هي السحابة؟ السحابة مصطلح شائع يشير إلى الخوادم المتاحة للاستخدام العام عبر الإنترنت، وذلك إما باستئجارها استئجارًا مدفوعًا أو استخدامها بوصفها جزءًا من برنامج أو منصة خدمية. تتنوع الخدمات السحابية التي تستطيع استخدامها ومنها مثلًا: استضافة مواقع الويب، واستضافة الملفات ومشاركتها، واستضافة البرامج الموزعة وغير ذلك. تشير السحابة أيضًا إلى الحوسبة السحابية Cloud Computing، وهي التقنية التي تتيح لك توسيع نطاق المهمة التي تنفذها لتشمل خوادم متعددة، فبدلًا من تنفيذها على خادم واحد بمواصفات عالية توزع المهمة على مجموعة خوادم أصغر تسمى العقد nodes. كيف تعمل الحوسبة الافتراضية؟ تتضمن بيئة الاستضافة السحابية مكونين رئيسيين: الخوادم الافتراضية التي تستضيف التطبيقات ومواقع الويب، والمضيفين الماديين المسؤولين عن إدارة الخوادم الافتراضية، وتتمثل الحوسبة الافتراضية في العلاقة بينهما، وهي التي تمنح الاستضافة السحابية مرونتها وقابليتها للتوسع مميزةً إياها عن الاستضافات الأخرى. الخوادم الافتراضية Virtual Servers الخادم الافتراضي الخاص Virtual private server أواختصارًأ VPS هو الأسلوب الأكثر انتشارًا للاستضافة السحابية، وخادم VPS هو خادم افتراضي يكافئ في عمله الحاسوب المادي، وله نظام تشغيله الخاص وعملياته الخاصة، ورغم أن الخوادم الافتراضية الموجودة على المضيف نفسه تتشارك في موارده الفيزيائية إلّا أن عمليات كل خادم افتراضي تبقى مستقلة تمامًا عن عمليات الخوادم الافتراضية الأخرى، فلا تتأثر أو تؤثر بها. تُنشر الخوادم الافتراضية وتُدار بواسطة برنامج خاص يسمى المراقب Hypervisor ويكون موجودًا على المضيف الفيزيائي، وبواسطته تُثبت نظم التشغيل على الخوادم الافتراضية. عمليًّا يعادل استخدام الخادم الافتراضي استخدام خادم مادي مخصص فلن تشعر بفرق بينهما، رغم أن الخادم الافتراضي يتشارك موارد مضيفه مع الخوادم الافتراضية الأخرى التي تقاسمه الاستضافة. المضيفون Hosts المُضيف هو الخادم الفيزيائي أو المادي الذي يستضيف الخادم الافتراضي، ويزوده بالموارد الحاسوبية اللازمة لعمله. يستخدم المُضيف طبقة برمجية تدعى برنامج المراقب Hypervisor لإدارة الموارد الحاسوبية ونشرها ومنحها للخوادم الافتراضية الموجودة تحت سلطته. يستخدم البعض مصطلح Hypervisor للإشارة إلى المضيفين الفيزيائيين المّثبت عليهم برامج المراقب مع خوادمهم الافتراضية. يخصص المضيف كل خادم افتراضي بذاكرة، ووحدات معالجة مركزية CPU، واتصالًا شبكيًا بمجرد تشغيله، ويعمل المراقب على جدولة العمليات بين CPU الافتراضية ونوى المعالجة الحقيقية في مهمة مستمرة وعالية الدقة فقد تستخدم خوادم افتراضية متعددة نوى المعالجة الحقيقية نفسها. يوجد عدة أنواع لبرنامج المراقب، تختلف عن بعضها بطريقة جدولة العمليات ومشاركة الموارد. المراقبون Hypervisors يتوفر لمضيفي السحابة اليوم عددٌ قليل من برامج Hypervisor الشائعة، ولكل منها أساليبه الخاصة في الحوسبة الافتراضية، لكن مهما بلغت الاختلافات بينها فجميع برمجيات المراقب في نهاية الأمر تؤدي دورًا جوهريًا واحدًا، هو تأمين الأدوات التي يحتاجها المضيف لنشر الخوادم الافتراضية، وصيانتها، ونقلها، وحذفها حسب الطلب. KVM، تختصر العبارة Kernel-Based Virtual Machine أو الأجهزة الافتراضية المعتمدة على نواة لينكس. تمامًا كما يوحي الاسم فهي بنية تحتية للحوسبة الافتراضية مدمجة في نواة نظام لينكس، وبمجرد تفعيلها يتحول الخادم المثبت عليه لينكس إلى Hypervisor، ويمكنه استضافة الخوادم الافتراضية مباشرةً بدون أي إضافات أخرى. تختلف KVM عن بقية برامج Hypervisor بأنها لا تحتاج إلى القيام بعملية إنشاء أو محاكاة لمكونات نواة خاصة بالاستضافة الافتراضية. يمكنك التعرف عليها عن قرب بمراجعة المقال أساسيّات الحوسبة الافتراضيّة Virtualization باستخدام KVM على Red Hat Enterprise Linux. Xen، من أشهر برامج Hypervisor، يعتمد على نواته المصغرة الخاصة لتأمين الأدوات اللازمة لدعم الخوادم الافتراضية بدون الحاجة لإجراء أي تعديل على نواة المضيف كما في KVM، يدعم Xen طريقتين مختلفتين للحوسبة الافتراضية: تدعى الطريقة الأولى الحوسبة شبه الافتراضية Paravirtualization، ليست حوسبة افتراضية كاملة لأنها لا تجري محاكاةً للعتاد الصلب إنما فقط تعديلات على نظم تشغيل الخوادم الافتراضية لتتمكن من التخاطب معه بكفاءة، أما الطريقة الثانية تسمى الحوسبة الافتراضية بمساعدة العتاد الصلب أو HVM فإنها تستخدم ميزات خاصة موجودة في بعض أنواع العتاد الصلب لمحاكاة الخادم الافتراضي بدون أي تعديل على نظام التشغيل. لاقت HVM انتشارًا واسعًا حوالي العام 2006 وخصوصًا مع توفر المعالجات الاستهلاكية عالية الأداء التي سمحت لمعظم الحواسيب المكتبية والمحمولة بتحقيق الأداء نفسه عند تشغيلها أجهزة افتراضية أو حاويات معتمدة على النوى المصغرة مثل حاويات دوكر. ESXi، موجه للمؤسسات، طورته شركة VMware. ميزته الأساسية أنه لا يتطلب وجود نظام تشغيل على المضيف لأن ESXi نفسه يؤدي دور نظام التشغيل، يسمى هذا النوع من المراقبين بالنمط الأول type one، ويعمل بكفاءة عالية بسبب عدم وجود أي برمجية وسيطة بين العتاد الصلب والخوادم الافتراضية. Hyper-V، من أشهر تقنيات الحوسبة الافتراضية المستخدمة مع خوادم ويندوز، تجده بين خدمات النظام في ويندوز سيرفر ومعظم نسخ الويندوز الحديثة، يتوفر أيضًا بوصفه خادمًا مستقلًا يمكنك العمل معه مباشرةً بدون نظام تشغيل آخر. يمكنك قراءة المقال تطبيق Hyper-V في ويندوز 10 المتضمن خطوات إرشادية لتثبيت Hyper-V. لماذا أختار الاستضافة السحابية؟ تتبلور مميزات الحوسبة الافتراضية في بيئات الاستضافة السحابية، فيسهل في هذه البيئات بناء الخوادم الافتراضية، وإعدادها، وتخصيصها بالموارد حسب الطلب، ويمكنك مع مرور الوقت زيادة الموارد المخصصة أو إنقاصها حسب تغيرات عملك، أضف إلى ذلك ما يقدمه لك بعض مضيفي السحابة من توافرية عالية للخدمة، فيعملون على نقل الخادم الافتراضي من مراقب إلى آخر عند الحاجة بأدنى فترة ممكنة من التوقف عن العمل أو حتى دون أي توقف، ويؤمنون في بعض الحالات بديلًا عن الخادم الافتراضي يماثله تمامًا لينوب عنه في حال فشلت إحدى العقد. التخصيص يفضل المطورون غالبًا استخدام الخوادم الافتراضية الخاصة VPS لما توفره لهم من تحكم كامل ببيئتهم الافتراضية. فيحصلون افتراضيًا على حسابات الجذر ذات الصلاحيات الكاملة أو sudo، ليتمكنوا من تثبيت البرامج التي يحتاجونها أو التعديل عليها حسب طلباتهم. تبدأ أريحية التخصيص لديهم من حرية اختيار نظام التشغيل، إذ تقبل معظم برمجيات المراقب تثبيت أي نظام تشغيل على الخادم الضيف بدون قيود، بدءًا من النظم مفتوحة المصدر مثل لينكس و BSD إلى نظم المملوكة مثل ويندوز، وبعد اعتماد نظام التشغيل يستطيع كل مطور تثبيت البرمجيات التي يحتاجها بالتتابع ليصل للبنية الحاسوبية التي يحتاجها على الخادم السحابي، والتي قد تتضمن خادم ويب مثلًا أو قاعدة بيانات أو تطبيقًا جاهزًا للتوزيع أو غير ذلك. التوسعة تتميز الخوادم السحابية بمرونتها وقابليتها للتوسع أفقيًا وعموديًا، وهو أمرٌ تتفوق فيه الاستضافة السحابية على غيرها من الاستضافات، فمعظم الاستضافات الأخرى تقبل التوسعة إما أفقيًا أو عموديًا، ويرجع ذلك إلى البيئة الافتراضية المبني عليها الخادم السحابي، فموارده في نهاية الأمر جزءٌ من مجموعة موارد مادية أوسع بكثير، يمكنك نقلها إلى مراقبين آخرين أو إنشاء تكرارات عنها لديهم حسب الحاجة. التوسع الأفقي، أو scaling out هو زيادة عدد العقد في النظام العنقودي، قد يعني ذلك زيادة عدد خوادم الويب لتحسين إدارة حركة مرور البيانات إلى تطبيقك، أو إضافة خوادم لمناطق معينة لتسريع الاستجابة، أو زيادة عدد العاملين workers في قاعدة البيانات لرفع سرعة نقل البيانات أو غير ذلك. التوسع العمودي، أو scaling up يُقصد به ترقية خادم واحد، وزيادة موارده ليتمتع بمواصفات أعلى، بزيادة الذواكر المخصصة له أو نوى المعالجة المركزية أو غير ذلك، توفر هذه الموارد المحسنة البيئة اللازمة لتشغيل مثيلات البرامج الإضافية بكفاءة على الخادم، على سبيل المثال: إذا وسعت عدد عمال قاعدة البيانات، فإن ترقية الخادم عموديًا تعطيهم الموارد اللازمة للعمل بكفاءة أعلى. بقي التوسع العمودي لفترة طويلة الطريقة السائدة لرفع مواصفات الاستضافة السحابية، ثم تغير الأمر مع تقديم المزودين عروضًا اقتصادية للتوسعة الأفقية. بالنتيجة يستطيع المطورون توسعة استضافاتهم السحابية، إما أفقيًا بزيادة عدد عقد VPS التي يستخدمونها، أو عموديًا ترقية مواصفات الخادم، أو القيام بالأمرين معًا حسب احتياجات عملهم. الخلاصة نأمل أنك أخذت فكرةً جيدة عن آلية عمل الاستضافة السحابية، والعلاقة بين الخوادم الافتراضية والمراقب Hypervisor المسؤول عن إدارتها، وتبينت خصائص الاستضافة السحابية التي تميزها عن غيرها من الاستضافات، لتتمكن من اختيار الاستضافة المناسبة لحالتك. ترجمة -وبتصرف- للمقال An Introduction to Cloud Hosting لصاحبه Josh Barnett. اقرأ أيضًا المقال السابق: نظرة عامة على الحوسبة السحابية مقدمة إلى الخوادم السحابية مفهوم السحابة Cloud كل ما تود معرفته عن السحابة الهجينة Hybrid Cloud تعلم الحوسبة السحابيّة: المتطلبات الأساسيّة، وكيف تصبح مهندس حوسبة سحابيّة
-
نشرح في هذا المقال التعامل مع خطوط الأنابيب pipeline() التي توفر طريقة سهلة وموحدة للتعامل مع نماذج الذكاء الاصطناعي الموجودة في مستودع Hugging Face إذ يمكنك استخدامها للاستدلال inference وتوقع معلومات جديدة بناءً على المعلومات المتوفرة بأي أسلوب معالجة، سواء كان يعتمد على فهم اللغة language أو فهم الكلام speech أو الرؤية الحاسوبية computer vision أو الأنماط المتعددة multimodal. فمع خطوط الأنابيب المتاحة في مكتبة transformers لن تحتاج معرفةً قوية بالنماذج المخصصة لنمط معين ولا بطريقة كتابة شيفراتها البرمجية فهي ستسهل عليك المهمة، وهذا ما نعرضه في هذا المقال إذ سنتناول النقاط التالية: كيفية استخدام خطوط الأنابيب للاستدلال. طريقة استخدام مُرَمِّز tokenizer محدد أو نموذج model محدد. آلية استخدام خطوط الأنابيب لمشاريع معالجة الصوتيات والرؤية والأنماط المتعددة multimodal التي تتضمن التعامل مع بيانات متعددة الأنماط مثل الأصوات والصور معًا. استخدام خطوط الأنابيب Pipeline يوجد خط أنابيب pipeline() خاص بكل مهمة، إلّا أن استخدام خط الأنابيب العام general pipeline (وهو تجريد يضم خطوط الأنابيب الخاصة بجميع المهام) يوفر طريقةً أسهل وأكثر مرونة، إذ يُحَمِّل نموذجًا افتراضيًا وصنف معالجة مسبقة للاستدلال مناسبين لطبيعة مشروعك. يبين مثالنا التالي طريقة استخدام خط الأنابيب للتَعَرُّف التلقائي على الكلام (ASR) أي تحويله إلى نص، لتنفيذ هذا التطبيق حاول تطبيق الخطوات في بيئتك التجريبية: أنشئ خط الأنابيب pipeline() وحدد المهمة التي تريدها وهي هنا التعرف التلقائي على الكلام: >>> from transformers import pipeline >>> transcriber = pipeline(task="automatic-speech-recognition") 2. مَرِّر مُدخَلَاتِك input إلى خط الأنابيب، واحصل على النتيجة، في مثالنا المُدخَل هو الملف الصوتي المطلوب التَعَرُّف عليه وتحويله إلى نص: >>> transcriber("https://huggingface.co/datasets/Narsil/asr_dummy/resolve/main/mlk.flac") {'text': 'I HAVE A DREAM BUT ONE DAY THIS NATION WILL RISE UP LIVE UP THE TRUE MEANING OF ITS TREES'} إذا لم تكن النتيجة مرضية كأن لا يعطيك النص مثلًا المعنى كاملًا، أو يحوي كلمات في غير سياقها أو غير ذلك، فابحث عندها عن نموذج أقوى للتعامل مع المهمة، يمكنك الدخول مستودع Hugging Face مثلًا واستعراض نماذج التَعَرُّف على الكلام الأكثر تحميلًا ضمنه وتجريبها. ومنها على سبيل المثال النموذج Whisper large-v2 من منصة OpenAI فهو أحدث من النموذج الافتراضي Wav2Vec2 المستخدم أعلاه بسنتين، وقد جرى تدريبه على كمية بيانات أكثر بعشر أضعاف فهو بالتأكيد سيتفوق عليه في دقة التَعَرُّف على الكلام وسيعطيك نتائج أدق، هذا فضلًا عن قدرته على التنبؤ بعلامات الترقيم وحالة الحروف صغيرة أم كبيرة، ولا تتوفر هاتان الميزتان في Wav2Vec2. لنُعِدْ التنفيذ باستخدام النموذج Whisper large-v2 ونلاحظ الفرق: >>> transcriber = pipeline(model="openai/whisper-large-v2") >>> transcriber("https://huggingface.co/datasets/Narsil/asr_dummy/resolve/main/mlk.flac") {'text': ' I have a dream that one day this nation will rise up and live out the true meaning of its creed.'} يبدو النص الآن أدق في إيصال المعنى وصياغته متماسكة أكثر من النص الأول، يمكنك معرفة المزيد عن النماذج المناسبة لمعالجة الملفات الصوتية بالاطلاع على القسم التعليمي الخاص بالصوتيات منصة Hugging Face، ويمكنك تجربة النماذج الموجودة في مستودع المنصة على عينات بسيطة ومقارنة النتائج لاختيار النموذج المناسب لك، وإن لم تجد ما يلائم مشروعك دَرِِّبْ نموذجك الخاص؛ لمزيدٍ من المعلومات طالع مقال [كيفية تدريب نموذج ذكاء اصطناعي مُدَّرب مسبقًا]() من هذه السلسلة على أكاديمية حسوب. أما إذا كان لديك عدة مُدخَلاَت inputs أي عدة ملفات صوتية تحتاج لتحويلها إلى نصوص كما في مثالنا أعلاه فيمكنك تمريرها لخط الأنابيب بهيئة قائمة وفق التالي: transcriber( [ "https://huggingface.co/datasets/Narsil/asr_dummy/resolve/main/mlk.flac", "https://huggingface.co/datasets/Narsil/asr_dummy/resolve/main/1.flac", ] ) تُعدّ خطوط الأنابيب طريقةً مرنة لتجريب النماذج، فالتبديل من نموذج لآخر إجراء سهل ولن يشكل أي عبءٍ عليك، وفضلًا عن ذلك فهي تستخدم مع مجموعات البيانات الكبيرة dataset وخوادم الويب webserver. معاملات خطوط الأنابيب هناك نوعان من معاملات parameters خطوط الأنابيب pipeline() نوعان عامة وخاصة؛ توجد المعاملات العامة في جميع أنواع خطوط الأنابيب، بينما تختص المعاملات الخاصة بمهمة محددة مثل معالجة اللغة الطبيعية أو الرؤية الحاسوبية أو التعرُّف على الكلام أو غيرها، ويمكنك تعريف المعاملات في أي قسم من برنامجك كما في المثال التالي: transcriber = pipeline(model="openai/whisper-large-v2", my_parameter=1) out = transcriber(...) # يعتمد هذا السطر القيمة 1 للمعامل out = transcriber(..., my_parameter=2) # تعدلت قيمة المعامل هنا وأصبحت 2 out = transcriber(...) # الآن بدون كتابة قيمة جديدة للمعامل سيأخذ من جديد القيمة الابتدائية 1 سنعرض بمزيد من التفصيل ثلاثة من أشهر المعاملات: معامل الجهاز Device يشير المعامل Device إلى الجهاز المستخدم لتشغيل نموذج الذكاء الاصطناعي سواء وحدة المعالجة المركزية (CPU) أو وحدة معالجة الرسومات (GPU).إذا ضبطت قيمة هذا المعامل على device=n فإن خط الأنابيب سيُحَمّل النموذج على الجهاز الذي حددته بالقيمة "n". ألقِ نظرة على الأمر التالي لضبط قيمة المعامل device علمًا أنه ينطبق على إطار عمل PyTorch و Tensorflow: transcriber = pipeline(model="openai/whisper-large-v2", device=0) أما إذا كنت تعتمد نموذجًا كبير الحجم ويحتاج إلى أكثر من وحدة معالجة رسومية (GPU) وكنت تستخدم PyTorch، فيمكنك عندها ضبط قيمة المعامل على device_map="auto" لتجري عملية توزيع أوتوماتيكية لأحمال النموذج على مكونات الجهاز، مع الانتباه لأن استخدام المعامل device_map مشروط بوجود مكتبة التسريع Accelerate: pip install --upgrade accelerate إذًا سيوزع السطر التالي أحمال النموذج على معالجات الجهاز: transcriber = pipeline(model="openai/whisper-large-v2", device_map="auto") وننوه هنا إلى أن تمرير قيمة المتغير device_map="auto" يغنيك عن كتابة device=device فوجودهما معًا في الشيفرة قد يسبب سلوكيات غير متوقعة في برنامجك. معامل حجم الدفعة Batch size لا تقُسِّم خطوط الأنابيب بيانات الاستدلال إلى دفعات batching افتراضيًا، فإذا أردت تقسيمها اضبط الإعدادات اللازمة لذلك، واعلم أن هذه العملية لا تؤدي دائمًا إلى تسريع الأداء فربما تسبب البطء في بعض الحالات، لذا يترك خيار التقسيم من عدمه حسب حالة الاستخدام. يبين المثال التالي طريقة استخدامها: transcriber = pipeline(model="openai/whisper-large-v2", device=0, batch_size=2) audio_filenames = [f"https://huggingface.co/datasets/Narsil/asr_dummy/resolve/main/{i}.flac" for i in range(1, 5)] texts = transcriber(audio_filenames) بموجب التعليمات السابقة: سيُطَبَّق خط الأنابيب على الملفات الصوتية الأربعة المقدمة له، لكنه سيُمررها للنموذج على دفعتين ملفين في كل دفعة، (النموذج في مثالنا موجود على وحدة GPU والتقسيم إلى دفعات يُعدّ مفيدًا في هذه الحالة). لا تتطلب عملية التقسيم كتابة أي تعليمات برمجية إضافية فكل ما عليك هو تحديد قيمة المعامل، مع ضرورة التنويه إلى أن الخرج سيكون نفسه في الحالتين سواء قَسَّمْتَ بياناتك المستخدمة للاستدلال أو لم تُقَسِّمْها فالهدف من التقسيم هو تسريع أداء خط الأنابيب فقط دون أي تأثير على التنبؤات. الغاية الأساسية كما ذكرنا تحسين الأداء لكنك قد ستصادف بعض العناصر كبيرة الحجم (الملفات الصوتية الطويلة مثلًا) والتي لا يمكن للنماذج معالجتها دون تقسيمها إلى أجزاء فستضطر عندها لاستخدام التقسيم بصرف النظر عن الأداء، وهنا يأتي دور خطوط الأنابيب إذ تخفف عنك بعض تعقيدات العملية وتقسم هذه العناصر نيابةً عنك. معاملات خاصة بنوع معين من المهمات يتوفر لكل مهمة معاملات خاصة تعطيها المرونة وتساعدك على التحكم بخياراتها الإضافية لصالح مشروعك، فعلى سبيل المثال يتضمن أسلوب المعالجة Transformers.AutomaticSpeechRecognitionPipeline.call() معاملًا خاصًا يدعى return_timestamps يُعدّ معاملًا واعدًا في إضافة النصوص التوضيحية لمقاطع الفيديو subtitling videos: >>> transcriber = pipeline(model="openai/whisper-large-v2", return_timestamps=True) >>> transcriber("https://huggingface.co/datasets/Narsil/asr_dummy/resolve/main/mlk.flac") {'text': ' I have a dream that one day this nation will rise up and live out the true meaning of its creed.', 'chunks': [{'timestamp': (0.0, 11.88), 'text': ' I have a dream that one day this nation will rise up and live out the true meaning of its'}, {'timestamp': (11.88, 12.38), 'text': ' creed.'}]} إذا دققت في خرج الأمر السابق ستلاحظ أن النموذج قد استنتج النص من الملف الصوتي وأيضًا وقت النطق بكل جملة 'timestamp'. لذا ننصحك بمراجعة توصيف الواجهة البرمجية API لكل مهمة أو أسلوب معالجة حتى تتعرف أكثر على معاملاتها وتتمكن من استثمار مزاياها على أكمل وجه، فعلى سبيل المثال تحتوي المهمة AutomaticSpeechRecognitionPipeline على معامل مفيد للتعامل مع الملفات الصوتية الطويلة يدعى chunk_length_s يفيدك في ترجمة الأفلام أو مقاطع الفيديو التي تصل مدتها إلى ساعة مثلًا والتي لا يستطيع النموذج التعامل معها بخياراته الاعتيادية: >>> transcriber = pipeline(model="openai/whisper-large-v2", chunk_length_s=30, return_timestamps=True) >>> transcriber("https://huggingface.co/datasets/sanchit-gandhi/librispeech_long/resolve/main/audio.wav") {'text': " Chapter 16. I might have told you of the beginning of this liaison in a few lines, but I wanted you to see every step by which we came. I, too, agree to whatever Marguerite wished, Marguerite to be unable to live apart from me. It was the day after the evening... و في سعيها للتطوير تشجع Hugging Face المستخدمين على طلب المعاملات التي يحتاجونها ولا يتمكنون من إيجادها. استخدام خط الأنابيب على مجموعة بيانات يمكنك تشغيل خط الأنابيب pipeline للاستدلال على مجموعة بيانات كبيرة dataset وليس مجرد قائمة بالمدخلات، وأسهل الطرق الموصى بها لإنجاز ذلك هي استخدام مُكَرِّر iterator كما في المثال التالي: def data(): for i in range(1000): yield f"My example {i}" pipe = pipeline(model="openai-community/gpt2", device=0) generated_characters = 0 for out in pipe(data()): generated_characters += len(out[0]["generated_text"]) يعمل المُكَرِّر data() ويُرجِع نتيجة في كل تكرار؛ ويُميّز خط الأنابيب مباشرةً أن المُدخَل الوارد إليه قابل للتكرار iterable أي يمكن الحصول على بياناته بطريقة متكررة أو متتالية، فيبدأ بجلب البيانات بالتزامن مع استمراره في معالجة الموجود في وحدة GPU (ويستخدم في ذلك موازن البيانات DataLoader ضمنًا)، تتميز هذه الطريقة بكونها لا تتطلب حجز ذاكرة لكامل مجموعة البيانات، وتساعدك على تغذية GPU بالبيانات بأسرع ما يمكن. يُعدّ تقسيم البيانات إلى دفعات (بضبط المعامل batch_size) مفيدًا في هذه الحالة ويؤدي إلى تحسين الأداء. ويمكنك تنفيذ التكرار على مجموعة بيانات بتحميل إحدى مجموعات البيانات Datasets الموجودة في مستودعات Hugging Face وتقسيمها ثم استخدام الأداة المساعدة KeyDataset ضمن حلقة التكرار للحصول على الخرج المطلوب: # KeyDataset أداة مساعدة ترجع لك الخرج الذي يهمك من مجموعة البيانات from transformers.pipelines.pt_utils import KeyDataset from datasets import load_dataset pipe = pipeline(model="hf-internal-testing/tiny-random-wav2vec2", device=0) dataset = load_dataset("hf-internal-testing/librispeech_asr_dummy", "clean", split="validation[:10]") for out in pipe(KeyDataset(dataset, "audio")): print(out) ملاحظة: بطريقة مشابهة لاستخدامها مع مجموعات البيانات، يمكنك استخدام خطوط الأنابيب مع خوادم الويب في مشاريع ذات طبيعة خاصة، يمكنك الاطلاع على هذا الدليل من توثيقات Hugging Face لمزيد من المعلومات. استخدام خط الأنابيب مع الرؤية الحاسوبية عمليًا يشبه استخدام خطوط الأنابيب في مشاريع الرؤية الحاسوبية استخدامها في أمثلتنا السابقة. حَدِّد مهمتك ثم مرر الصورة إلى المُصَنِّف classifier، طبعًا يمكنك تمرير الصورة بهيئة رابط link أو مسار محلي path يشير إليها، أو صورة مشفرة بترميز base64. لنفترض مثلًا أن مهمتنا هي اكتشاف السلالة التي تنتمي إليها القطة في الصورة التالية: فستكون الشيفرة البرمجية كما يلي: >>> from transformers import pipeline >>> vision_classifier = pipeline(model="google/vit-base-patch16-224") >>> preds = vision_classifier( images="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg" ) >>> preds = [{"score": round(pred["score"], 4), "label": pred["label"]} for pred in preds] >>> Preds [{'score': 0.4335, 'label': 'lynx, catamount'}, {'score': 0.0348, 'label': 'cougar, puma, catamount, mountain lion, painter, panther, Felis concolor'}, {'score': 0.0324, 'label': 'snow leopard, ounce, Panthera uncia'}, {'score': 0.0239, 'label': 'Egyptian cat'}, {'score': 0.0229, 'label': 'tiger cat'}] استخدام خط الأنابيب مع النصوص يمكنك بآلية مشابهة استخدام خط الأنابيب لمهام معالجة اللغة الطبيعية (NLP) كما في المثال التالي: >>> from transformers import pipeline # النموذج المستخدم من نوع `zero-shot-classification` # يمكنك اختيار التسميات التوضيحية التي تريدها لتصنيف النص >>> classifier = pipeline(model="facebook/bart-large-mnli") >>> classifier( "I have a problem with my iphone that needs to be resolved asap!!", candidate_labels=["urgent", "not urgent", "phone", "tablet", "computer"], ) {'sequence': 'I have a problem with my iphone that needs to be resolved asap!!', 'labels': ['urgent', 'phone', 'computer', 'not urgent', 'tablet'], 'scores': [0.504, 0.479, 0.013, 0.003, 0.002]} استخدام خط الأنابيب مع الأنماط المتعددة يدعم خط الأنابيب التعامل مع أكثر من أسلوب معالجة في المهمة الواحدة، وهو ما يسمى بالمعالجة متعددة الأنماط Multimodal، ومن أمثلتها الإجابة عن الأسئلة المرئية (VQA) فهي تجمع بين معالجة النص والصورة، تمرر في هذه الحالة الصورة المطلوبة بهيئة رابط URL أو مسار تخزين ونص السؤال الذي تود طرحه عنها. ألقِ نظرة على المثال التالي، نسأل هنا عن رقم الفاتورة المكتوب في صورة الفاتورة الموجودة هنا: >>> from transformers import pipeline >>> vqa = pipeline(model="impira/layoutlm-document-qa") >>> vqa( image="https://huggingface.co/spaces/impira/docquery/resolve/2359223c1837a7587402bda0f2643382a6eefeab/invoice.png", question="What is the invoice number?", ) [{'score': 0.42515, 'answer': 'us-001', 'start': 16, 'end': 16}] لتنفيذ المثال السابق تحتاج لتثبيت الأداة pytesseract الخاصة بالتَعَرُّف على المستندات الممسوحة ضوئيًا (OCR) في بايثون طبعًا بالإضافة لمكتبة المحوّلات Transformers، وهذه أوامر تثبيتها: >> sudo apt install -y tesseract-ocr >> pip install pytesseract استخدام خط الأنابيب مع النماذج الكبيرة ومكتبة التسريع تُسهّل مكتبة التسريع accelerate استخدام خطوط الأنابيب مع النماذج الكبيرة large models، ويمكنك تثبيتها بالأمر pip install accelerate. حمّل نموذجك مع ضبط المعامل device_map="auto" ثم اتبع الأوامر التالية، اخترنا في هذا المقال النموذج "facebook/opt-1.3b". # pip install accelerate import torch from transformers import pipeline pipe = pipeline(model="facebook/opt-1.3b", torch_dtype=torch.bfloat16, device_map="auto") output = pipe("This is a cool example!", do_sample=True, top_p=0.95) وإذا أردت تمرير نماذج 8-bit فاحرص على تثبيت المكتبة bitsandbytes وتمرير الوسيط load_in_8bit=True كما في المثال التالي: # pip install accelerate bitsandbytes import torch from transformers import pipeline pipe = pipeline(model="facebook/opt-1.3b", device_map="auto", model_kwargs={"load_in_8bit": True}) output = pipe("This is a cool example!", do_sample=True, top_p=0.95) الآن يمكنك أخذ نقطة التحقق checkpoint هذه وتبديلها بأي نموذج يناسب النماذج الكبيرة من منصة Hugging Face مثل النموذج اللغوي متعدد اللغات BLOOM. ترجمة -وبتصرف- لقسم Pipelines for inference من توثيقات Hugging Face. اقرأ أيضًا المقال السابق: تثبيت مكتبة المحوّلات Transformers تعرف على مكتبة المحوّلات Transformers من منصة Hugging Face تعرف على أفضل دورات الذكاء الاصطناعي تعرف على إطار عمل باي تورش PyTorch وأهميته لتطبيقات الذكاء الاصطناعي
-
تعرفنا في مقال سابق على مكتبة المحوّلات Transformers التي توفرها منصة Hugging Face واستعرضنا أبرز مميزاتها وتوافقها مع أي مكتبة تعلُّم عميق تستخدمها؛ وسنتعلم في هذا المقال كيفية تثبيت هذه المكتبة على حاسوبك المحلي وخطوات ضبط متغيرات الذاكرة المخبئية cache المتعلقة بها، والإعدادات اللازمة للتعامل مع المكتبة دون الحاجةللاتصال بالإنترنت. هذه المكتبة مختبرة على إصدارات مختلفة من لغة بايثون من الإصدار 3.6 وأعلى، وإطار العمل باي تورش PyTorch من الإصدار 1.1.0 وأعلى، والإطار تنسرفلو TensorFlow من الإصدار 2 وأعلى، بالإضافة إلى مكتبة Flax. وسنعرض في مقال اليوم تعليمات تثبيتها مع كل من: إطار العمل PyTorch. إطار العمل TensorFlow 2.0. مكتبة الشبكات العصبية Flax. اتبع التعليمات التي تناسبك من بينها. التثبيت بواسطة مدير الحزم pip سنبدأ عملنا بإنشاء البيئة الافتراضية للمشروع إذ ينبغي أن تثبت المكتبة Transformers ضمن بيئة افتراضية، وتساعدك بيئات بايثون الافتراضية على إدارة مشاريعك البرمجية المختلفة بمرونة وتجنب المشكلات التي قد تحدث بسبب تعارض الاعتماديات الخاصة بكل مشروع. يمكنك معرفة المزيد عن كيفية تثبيت بايثون وإعداد بيئته الافتراضية على أشهر نظم التشغيل بمساعدة هذا الدليل باللغة العربية على أكاديمية حسوب. أنشئ بيئةً افتراضية في مجلد مشروعك وفق التالي: python -m venv .env ثم فعّل البيئة الافتراضية، فإذا كنت تستخدم نظام تشغيل لينكس أو ماك اكتب هذا الأمر: source .env/bin/activate وإذا كنت تستخدم نظام التشغيل ويندوز فيمكنك تفعيلها بكتابة ما يلي: .env/Scripts/activate وبذلك تكون جاهزًا لتثبيت المكتبة من خلال التعليمة التالية: pip install transformers وفي حالة الأجهزة التي تعتمد في معالجتها على وحدة المعالجة المركزية (CPU) فقط فيمكنك تثبيت مكتبة التعلم العميق ومكتبة المحولات transformers في سطرٍ واحد، مثل السطر التالي الذي يبين طريقة تثبيت PyTorch و Transformers: pip install 'transformers[torch]' والآن لتثبيت Transformers و TensorFlow 2.0 نكتب: pip install 'transformers[tf-cpu]' وأخيرًا لتثبيت Transformers مع Flax نكتب الأمر التالي: pip install 'transformers[flax]' يمكنك التأكد من صحة تثبيت المكتبة transformers بتنفيذ السطر التالي الذي ينبغي أن يُحمّل نموذج تعلّم آلي مدرب مسبقًا pretrained model: python -c "from transformers import pipeline; print(pipeline('sentiment-analysis')('we love you'))" ولنعرض بعدها قاموس النموذج الذي حمّلناه والمتضمن مفتاحي التسمية التوضيحية label ومجموع النقاط score: [{'label': 'POSITIVE', 'score': 0.9998704791069031}] دورة الذكاء الاصطناعي احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة. اشترك الآن تثبيت المكتبة Transformers من المصدر الأساسي يمكنك تثبيت مكتبة المحولات من مصدرها الأساسي (أي من مستودع منصة Hugging Face على github) بكتابة التالي: pip install git+https://github.com/huggingface/transformers بهذه الطريقة ستثبت الإصدار الأساسي من المكتبة main version وليس أحدث إصدار مستقر متوفر stable version، والفرق بينهما أن استخدام الإصدار الأساسي يبقيك على اطلاع بأحدث تطورات المكتبة أولًا بأول حتى إن لم تكن تحديثات مستقرة، فمثلًا إذا ظهر خطأ معين في الإصدار الرسمي للمكتبة فيمكن للجهة المطورة لها أن تصلحه إطلاق الإصدار الجديد وبذلك ستستفيد من الإصلاح قبل طرح الإصدار الجديد المستقر. تأكد الآن من صحة تثبيت المكتبة بتنفيذ الأمر التالي: python -c "from transformers import pipeline; print(pipeline('sentiment-analysis')('I love you'))" تثبيت المكتبة تثبيًا قابلًا للتعديل تحتاج هذا النوع من التثبيت القابل للتعديل Editable install في حالتين هما: عندما تستخدم الإصدار الأساسي main version من الشيفرة المصدرية. إذا أردت المساهمة في مشروع مكتبة المحوّلات واختبار تعديلات الشيفرات البرمجية. لنبدأ أولًا باستنساخ مستودع المكتبة وتثبيتها كما يلي: git clone https://github.com/huggingface/transformers.git cd transformers pip install -e . ستعمل هذه الأوامر على وصل المجلد الذي استنسخت مستودع المكتبة ضمنه مع مسارات مكتبة بايثون التي تستخدمها، أي ستبحث بايثون عند تنفيذ الأوامر في هذا المجلد إضافةً إلى مجلدات المكتبة الاعتيادية التي تبحث فيها عادةً، فعلى سبيل المثال إذا كانت حزم بايثون مثبتة في المجلد /~/anaconda3/envs/main/lib/python3.7/site-packages فيجري البحث عن الأوامر ضمنه وضمن المجلد /~/transformers الذي استنسخت فيه المستودع. والآن حَدِّث ما استنسخته إلى أحدث إصدار من المكتبة وفق التالي: cd ~/transformers/ git pull ستكتشف البيئة الافتراضية لبايثون الإصدار الأساسي من المكتبة الذي ثبتناه وتبدأ بالتعامل معه بعد إعادة التشغيل. التثبيت باستخدام conda يمكنك تثبيت المكتبة باستخدام conda بدلًا من pip كما يلي: conda install conda-forge::transformers ضبط الذاكرة المخبئية Cache setup يُشير المتغير TRANSFORMERS_CACHE، وهو أحد متغيرات البيئة للصدفة shell، إلى المجلد المحلي الذي ستُخَزَّن فيه النماذج المدربة مسبقًا التي ستُحمِّلها، وفي الوضع الافتراضي يكون هذا المجلد هو ~/.cache/huggingface/hub في لينكس، ويكون C:\Users\username\.cache\huggingface\hub في ويندوز، لكن يمكنك تغييره بتعديل قيم متغيرات البيئة للصدفة shell الواردة أدناه مع مراعاة ترتيبها فهي مرتبة حسب الأولوية: المتغير HUGGINGFACE_HUB_CACHE أو TRANSFORMERS_CACHE. المتغير HF_HOME. المتغير XDG_CACHE_HOME + /huggingface. العمل من دون اتصال بالإنترنت Offline Mode يمكنك تشغيل مكتبة المحوّلات في بيئة محمية بجدار ناري أو غير متصلة بالإنترنت بالاعتماد على الملفات المخزنة محليًا على جهازك، وذلك بإعطاء القيمة "1" للمتغير TRANSFORMERS_OFFLINE. تستطيع أيضًا أن توفر لمشروعك مجموعات بيانات Datasets محلية لاستخدامها في التدريب من دون اتصال بالإنترنت عبر ضبط قيمة المتغير HF_DATASETS_OFFLINE=1. كما في المثال التالي: HF_DATASETS_OFFLINE=1 TRANSFORMERS_OFFLINE=1 \ python examples/pytorch/translation/run_translation.py --model_name_or_path google-t5/t5-small --dataset_name wmt16 --dataset_config ro-en ... سيعمل هذا السكربت بلا تأخير أو انتظار لأنه لن يحاول الاتصال بالإنترنت لتحميل النموذج من مستودع Hugging Face بل سيستخدم الملفات المحلية فقط. تستطيع أيضًا إيقاف تحميل النماذج من المستودع وتحميلها من المواقع المحلية فقط عن طريق ضبط قيمة المعامل local_files_only على True في كل مرة تستدعي فيها from_pretrained() وفق التالي: from transformers import T5Model model = T5Model.from_pretrained("./path/to/local/directory", local_files_only=True) جلب النماذج والمُرمِزَّات لاستخدامها في حالة العمل دون اتصال بالإنترنت يُقصد بجلب fetch النماذج والمُرمِزَّات تحميلها مسبقًا من مستودعات المكتبة Transformers، وحفظها على جهازك، ثم استخدامها لاحقًا من دون اتصال بالإنترنت عبر الإشارة إلى مسارها المحلي عند كتابة التعليمات البرمجية، وذلك بثلاث طرق: الطريقة الأولى: حمّل الملفات التي تريدها من واجهة المستخدم لمستودع النماذج بالضغط على أيقونة السهم النازل الموجودة بجوار كل ملف كما في الصورة التالية: الطريقة الثانية: استخدم PreTrainedModel.from_pretrained() و PreTrainedModel.save_pretrained() وفق التسلسل التالي: 1. حمّل مسبقًا الملفات اللازمة لمشروعك بواسطة PreTrainedModel.from_pretrained() كما يلي: >>> from transformers import AutoTokenizer, AutoModelForSeq2SeqLM >>> tokenizer = AutoTokenizer.from_pretrained("bigscience/T0_3B") >>> model = AutoModelForSeq2SeqLM.from_pretrained("bigscience/T0_3B") 2. احفظ ملفاتك في مجلد معين على حاسوبك باستخدام PreTrainedModel.save_pretrained(): >>> tokenizer.save_pretrained("./your/path/bigscience_t0") >>> model.save_pretrained("./your/path/bigscience_t0") 3. عندما تكون في وضع العمل من دون اتصال بالإنترنت أعد تحميل الملفات من المجلد الذي حفظته الملفات به بواسطة التعليمة PreTrainedModel.from_pretrained(): >>> tokenizer = AutoTokenizer.from_pretrained("./your/path/bigscience_t0") >>> model = AutoModel.from_pretrained("./your/path/bigscience_t0") الطريقة الثالثة: حمّل الملفات برمجيًا باستخدام مكتبة huggingface_hub كما يلي: 1. ثَبّت المكتبة huggingface_hub في بيئتك الافتراضية: python -m pip install huggingface_hub 2. استخدم الدالة hf_hub_download لتحميل الملف الذي تريده إلى المجلد المحلي المحدد، فالأمر التالي على سبيل المثال يُحَمِّل الملف config.json من النموذج "T0" إلى المجلد الذي تحدده: >>> from huggingface_hub import hf_hub_download >>> hf_hub_download(repo_id="bigscience/T0_3B", filename="config.json", cache_dir="./your/path/bigscience_t0") الآن بمجرد الانتهاء من تحميل ملفات النماذج وتخزينها محليًّا يصبح بإمكانك استخدامها عبر كتابة مسار المجلد الذي حفظت فيه ضمن التعليمات البرمجية كما في المثال التالي: >>> from transformers import AutoConfig >>> config = AutoConfig.from_pretrained("./your/path/bigscience_t0/config.json") ننهي عند هذه النقطة خطوات تثبيت المكتبة huggingface_hubوضبط إعداداتها، ولمزيد من التعمق ندعوك لمتابعة السلسلة وقراءة مقالنا التالي الذي سنتعرف فيه على طريقة استخدام خطوط الأنابيب pipeline لتبسيط التعامل مع نماذج الذكاء الاصطناعي التي توفرها Hugging Face. ترجمة -وبتصرف- لقسم Installation من توثيقات Hugging Face. اقرأ أيضًا المقال السابق: جولة سريعة للبدء مع مكتبة المحوّلات Transformers من هو مهندس الذكاء الاصطناعي وما أبرز مهامه لغات برمجة الذكاء الاصطناعي مكتبات وأطر عمل الذكاء الاصطناعي: القوة الكامنة خلف الأنظمة الذكية
-
إذا كنت مطوّرًا أو مستخدمًا دائمًا للذكاء الاصطناعي فجولتنا في مقال اليوم ستساعدك على الاستفادة من مكتبة المحولات Transformers، وتعرض لك آلية استخدام خطوط الأنابيب pipeline() في الاستدلال وتوقع معلومات جديدة inference، وكيفية تحميل نموذج مدرب مسبقًا ومعالج مدرب مسبقًا يناسبان مشروعك بواسطة الأصناف التلقائية Auto Classes، بالإضافة إلى طريقة تدريب النموذج بإطار عمل PyTorch أو TensorFlow. تأكد في البداية من تثبيت المكتبات التالية لتتابع معنا خطوات العمل: !pip install transformers datasets وثبت بعدها إطار عمل التعلُّم الآلي الذي تفضله. فإذا اخترت Pytorch اكتب السطر التالي لتثبيته: pip install torch وإذا كنت تفضل TensorFlow ثَبّته بكتابة ما يلي: pip install tensorflow خطوط الأنابيب Pipeline خط الأنابيب pipeline() هو أسهل وأسرع الطرق المتاحة لاستخدام نموذج تعلّم آلي مدرب مسبقًا في عملية الاستدلال، ويمكنك الاستفادة منه في مجالات متنوعة لتنفيذ مهام تتعلق بمعالجة اللغة الطبيعية Natural Language Processing أو الصوتيات Audio أو الرؤية الحاسوبية Computer Vision أو الأنماط المتعددة Modalities، ويبين الجدول التالي أبرزها: المهمة الوصف أسلوب المعالجة المُعرّف Pipeline identifier تصنيف نص وضع تسمية توضيحية label لجزء محدد من النص معالجة اللغة الطبيعية (NLP) pipeline(task=“sentiment-analysis”) توليد نص توليد نص بناءً على موجه promote معين يُعطى للنموذج معالجة اللغة الطبيعية (NLP) pipeline(task=“text-generation”) التلخيص إنشاء ملخص لجزء من نص أو مستند معالجة اللغة الطبيعية (NLP) pipeline(task=“summarization”) تصنيف الصور وضع تسمية توضيحية لصورة الرؤية الحاسوبية pipeline(task=“image-classification”) تجزئة الصور وضع تسمية توضيحية لكل جزء أو بيكسل من الصورة (يدعم التجزئة الدلالية للصور تجزئة المثيل) الرؤية الحاسوبية pipeline(task=“image-segmentation”) التعرّف على الكائنات التنبؤ بالمربعات المحيطة بالكائنات في صورة معينة وتصنيفها الرؤية الحاسوبية pipeline(task=“object-detection”) تصنيف الصوتيات وضع تسمية توضيحية للبيانات الصوتية صوتيات pipeline(task=“audio-classification”) التعرّف التلقائي على الكلام تحويل الكلام إلى نص مكتوب صوتيات pipeline(task=“automatic-speech-recognition”) الإجابة على الأسئلة مرئيًا الإجابة على سؤال حول الصورة، يُعطى النموذج سؤال وصورة أنماط متعددة Multimodal pipeline(task=“vqa”) الإجابة على الأسئلة المتعلقة بالمستندات الإجابة على سؤال حول المستند، يُعطى النموذج سؤال ومستند أنماط متعددة Multimodal pipeline(task=“document-question-answering”) عَنْوَنْة الصور إعطاء عنوان مناسب للصورة أنماط متعددة Multimodal pipeline(task=“image-to-text”) والآن بعد أن ثبتنا إطار العمل سننشئ مثيلًا instance لخط الأنابيب pipeline() ونحدد المهمة التي نريد استخدامه من أجلها، سنستخدمه في هذا المقال لتحليل المشاعر "sentiment-analysis"، وذلك بكتابة الأوامر التالية: >>> from transformers import pipeline >>> classifier = pipeline("sentiment-analysis") بموجب التعليمات السابقة سيُحَمِّل خط الأنابيب نموذج افتراضي مدرب مسبقًا pretrained model ومُرَمِّزْ tokenizer مناسبين لمهمة تحليل المشاعر، ويمكنك بعدها استخدام المُصَنِّف classifier الذي عرّفناه على النص الذي تود تحليل المشاعر ضمنه لتحصل على النتيجة، وفق التالي: >>> classifier("We are very happy to show you the ? Transformers library.") [{'label': 'POSITIVE', 'score': 0.9998}] إذا كان لديك أكثر من مُدْخَل input أي أكثر من نص، فمَرِرهم لخط الأنابيب pipeline() بهيئة قائمة ليرجع لك قائمة بالقواميس dictionaries كما يلي: >>> results = classifier(["We are very happy to show you the ? Transformers library.", "We hope you don't hate it."]) >>> for result in results: print(f"label: {result['label']}, with score: {round(result['score'], 4)}") label: POSITIVE, with score: 0.9998 label: NEGATIVE, with score: 0.5309 يمكن لخط الأنابيب أيضًا التعامل مع مجموعة بيانات كاملة dataset بدلًا من بضع مدخلات فقط، كما في المثال التالي الذي يبين استخدام pipeline() لمهمة التعرّف على الكلام "automatic-speech-recognition": >>> import torch >>> from transformers import pipeline >>> speech_recognizer = pipeline("automatic-speech-recognition", model="facebook/wav2vec2-base-960h") وسنُحمّل مجموعة بيانات صوتية audio dataset لنُطَبَّق عليها خط الأنابيب، لتكن مثلًا المجموعة MInDS-14 وفق التالي (يمكنك معرفة المزيد عن التعامل مع مجموعات البيانات بمراجعة القسم الخاص بها على منصة Hugging Face): >>> from datasets import load_dataset, Audio >>> dataset = load_dataset("PolyAI/minds14", name="en-US", split="train") عند التعامل مع الصوتيات ينبغي الانتباه لكون معدّل أخذ العينات sampling rate الخاص بمجموعة البيانات الصوتية MInDS-14 التي حملناها يطابق معدّل أخذ العينات الذي تَدرَّب عليه النموذج facebook/wav2vec2-base-960h وفق التالي: >>> dataset = dataset.cast_column("audio", Audio(sampling_rate=speech_recognizer.feature_extractor.sampling_rate)) الآن بمجرد استدعاء العمود "audio" وحسب التعليمات التالية ستُحَمَّلْ الملفات الصوتية من مجموعة البيانات ويُعاد أخذ العينات منها resampled تلقائيًا بما يتناسب مع معدّل أخذ العينات في النموذج، ثم ستُشكل مصفوفات الأشكال الموجية الأولية raw waveform arrays من العينات الأربع الأولى وتُمرر بهيئة قائمة إلى خط الأنابيب: >>> result = speech_recognizer(dataset[:4]["audio"]) >>> print([d["text"] for d in result]) ['I WOULD LIKE TO SET UP A JOINT ACCOUNT WITH MY PARTNER HOW DO I PROCEED WITH DOING THAT', "FONDERING HOW I'D SET UP A JOIN TO HELL T WITH MY WIFE AND WHERE THE AP MIGHT BE", "I I'D LIKE TOY SET UP A JOINT ACCOUNT WITH MY PARTNER I'M NOT SEEING THE OPTION TO DO IT ON THE APSO I CALLED IN TO GET SOME HELP CAN I JUST DO IT OVER THE PHONE WITH YOU AND GIVE YOU THE INFORMATION OR SHOULD I DO IT IN THE AP AN I'M MISSING SOMETHING UQUETTE HAD PREFERRED TO JUST DO IT OVER THE PHONE OF POSSIBLE THINGS", 'HOW DO I FURN A JOINA COUT'] أما عندما تتعامل مع مجموعات البيانات الكبيرة أي مع المدخلات الكبيرة (مثل التعامل بيانات الكلام speech أو الرؤية vision) فستحتاج مولدًا generator بدلًا من القائمة السابقة لتمرير البيانات إلى خطوط الأنابيب حتى تتحمل جميع المدخلات إلى الذاكرة. يمكنك معرفة المزيد عن الموضوع بالاطلاع على واجهة API لخطوط الأنابيب. تطبيق عملي ثاني باستخدام نموذج آخر ومُرَمِّز آخر تتكيف خطوط الأنابيب مع أي نموذج موجود في مستودع Hugging Face، يعطيك هذا مرونةً كبيرة في حالات الاستخدام المختلفة، لنفترض مثلًا أنك ترغب بتحليل المشاعر في نصوص مكتوبة باللغة الفرنسية، فيمكنك عندها استخدام الوسوم وفلاتر التصفية الموجودة في المستودع لتصل إلى النموذج المناسب لاحتياجك، فإذا جربت البحث عن نماذج تدعم اللغة الفرنسية سيكون نموذج BERT model المتعدد اللغات أحد الخيارات واسمه الدقيق هو: >>> model_name = "nlptown/bert-base-multilingual-uncased-sentiment" إذا كنت تعتمد إطار العمل Pytorch، فاستخدم الصنف التلقائي AutoModelForSequenceClassification الذي يناسب تصنيف النصوص، والمُرمِّز التلقائي AutoTokenizer من أجل تحميل النموذج المدرب مسبقًا الذي حددناه والمُرمِّز المرتبط به وفق التالي: >>> from transformers import AutoTokenizer, AutoModelForSequenceClassification >>> model = AutoModelForSequenceClassification.from_pretrained(model_name) >>> tokenizer = AutoTokenizer.from_pretrained(model_name) أما إذا كنت تعتمد الإطار TensorFlow، فإن الصنف التلقائي الخاص بتحميل النموذج عندها سيكون TFAutoModelForSequenceClassification والمُرمِّز المناسب هو AutoTokenizer كما يلي: >>> from transformers import AutoTokenizer, TFAutoModelForSequenceClassification >>> model = TFAutoModelForSequenceClassification.from_pretrained(model_name) >>> tokenizer = AutoTokenizer.from_pretrained(model_name) والآن لنطبق المُصَنِف classifier على النص الذي نود تحليل المشاعر ضمنه بعد كتابة النموذج والمُرمِّز اللذين اخترناهما ضمن محددات خط الأنابيب ()pipeline وفق التالي: >>> classifier = pipeline("sentiment-analysis", model=model, tokenizer=tokenizer) >>> classifier("Nous sommes très heureux de vous présenter la bibliothèque ? Transformers.") [{'label': '5 stars', 'score': 0.7273}] إذا لم تجد نموذجًا جاهزًا يناسب مشروعك في مستودع المنصة فينبغي لك إعادة تدريب أحد النماذج المدربة مسبقًا على بياناتك، يمكنك الاطلاع على مقال [تدريب نموذج مُعدّ مسبقًا على بيانات محددة]() من هذه السلسلة لمساعدتك بالمهمة، وتستطيع بعدها مشاركة النموذج على منصة Hagging Face باتباع الخطوات الواردة في مقال [مشاركة النماذج على منصة Hagging Face](). الصنف التلقائي AutoClass الأصناف التلقائية في حقيقة الأمر هي صاحبة الدور الرئيسي في تشغيل خطوط الأنابيب ()pipeline كما في مثالنا السابق عندما استخدمنا الصنفين التلقائيين AutoModelForSequenceClassification و AutoTokenizer، وإذا أردنا تعريف الصنف التلقائي AutoClass فهو اختصار يُرجع للمستخدم بنية النموذج الذي زوده باسمه أو مساره تلقائيًا، لذا فكل ما عليك فعله هو اختيار الصنف التلقائي AutoClass وصنف المعالج المُسبق preprocessing class المناسبين لمشروعك. لنرجع للمثال السابق ونحاول استخدام Autocalss للحصول على النتائج من خطوط الأنابيب. المُرمِّز التلقائي AutoTokenizer المُرمِّز هو المسؤول عن المعالجة التحضيرية للنص ليتحول إلى مصفوفة من الأعداد تكون بمثابة المدخلات للنموذج. وتخضع عملية الترميز لمجموعة قواعد مثل كيفية تقسيم split الكلمة وعلى أي مستوى تُقسّم الكلمات وغير ذلك (يمكنك معرفة المزيد عن الموضوع بمطالعة ملخص المُرمِّزات)، وتذكر دائمًا أن عليك إنشاء مثيل للمُرمِّز باسم النموذج نفسه للتأكد أنك تستخدم قواعد الترميز نفسها التي تدرب عليها النموذج مسبقًا. يتضمن الأمر التالي طريقة تحميل المُرَمِّز باستخدام AutoTokenizer: >>> from transformers import AutoTokenizer >>> model_name = "nlptown/bert-base-multilingual-uncased-sentiment" >>> tokenizer = AutoTokenizer.from_pretrained(model_name) والآن يمكنك تمرير النص الذي تريد ترميزه للمُرَمِّز وفق التالي: >>> encoding = tokenizer("We are very happy to show you the ? Transformers library.") >>> print(encoding) {'input_ids': [101, 11312, 10320, 12495, 19308, 10114, 11391, 10855, 10103, 100, 58263, 13299, 119, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]} يُرجع المُرَمِّز قاموسًا يتضمن: input_ids: تمثيلات عددية للرموز tokens. attention_mask: تبين أي الرموز تقابل أجزاء النص المهمة التي ينبغي الانتباه لها والأخذ بها وأيُّها لا. يتيح المُرَمِّز أيضًا إمكانية التعامل مع قائمة بالمدخلات inputs بدلًا من مدخل واحد، وإجراء عمليات الحشو والاقتطاع لزيادة أو إنقاص طول السلاسل النصية المدخلة للوصول إلى دفعات batches أو قطع بطول موحد. وذلك وفق التالي في Pytorch: pt_batch = tokenizer( ["We are very happy to show you the ? Transformers library.", "We hope you don't hate it."], padding=True, truncation=True, max_length=512, return_tensors="pt", ) وفي TensorFlow: tf_batch = tokenizer( ["We are very happy to show you the ? Transformers library.", "We hope you don't hate it."], padding=True, truncation=True, max_length=512, return_tensors="tf", ) النموذج التلقائي AutoModel توفر مكتبة المحولات Transformers طريقةً سهلة وموحدة لتحميل مثيلات النماذج المدربة مسبقًا، تشبه طريقة استخدام AutoTokenizer الذي تعاملنا معه سابقًا لكن عليك هنا تحديد AutoModel المناسب للمهمة التي تنفذها، مثل AutoModelForSequenceClassification الملائم لتصنيف النصوص في إطار العمل Pytorch: >>> from transformers import AutoModelForSequenceClassification >>> model_name = "nlptown/bert-base-multilingual-uncased-sentiment" >>> pt_model = AutoModelForSequenceClassification.from_pretrained(model_name) لنمرر الآن قاموس الدفعات أو قطع المدخلات pt_batch التي عالجناها قبل قليل إلى النموذج التلقائي AutoModel لكن بعد فك ضغطه unpack بإضافة رمز النجمتين ** وفق التالي: >>> pt_outputs = pt_model(**pt_batch) يضع النموذج المخرجات النهائية التي تنتج بعد كل التنشيطات activations في السمة logits، ثم نطبق الدالة softmax على السمة logits للحصول على الاحتمالات المتنبأ بها وفق التالي: >>> from torch import nn >>> pt_predictions = nn.functional.softmax(pt_outputs.logits, dim=-1) >>> print(pt_predictions) tensor([[0.0021, 0.0018, 0.0115, 0.2121, 0.7725], [0.2084, 0.1826, 0.1969, 0.1755, 0.2365]], grad_fn=<SoftmaxBackward0>) أما في حالة استخدامك لإطار العمل TensorFlow فإن النموذج التلقائي سيُسمى عندها TFAutoModel ويمكنك استخدامه بالطريقة نفسها التي تُحمّل بها AutoTokenizer، وأيضًا عليك هنا تحديد النموذج التلقائي TFAutoModel المناسب لمشروعك، في مثالنا سنستخدم TFAutoModelForSequenceClassification لأنه مخصص لتصنيف النصوص: >>> from transformers import TFAutoModelForSequenceClassification >>> model_name = "nlptown/bert-base-multilingual-uncased-sentiment" >>> tf_model = TFAutoModelForSequenceClassification.from_pretrained(model_name) مرر الآن مصفوفة tensors التي تتضمن قطع المدخلات التي عالجناها سابقًا إلى النموذج كما هي (أي بدون الرمز ** مثل ما فعلنا في تعليمات Pytorch): >>> tf_outputs = tf_model(tf_batch) ثم نطبق الدالة softmax على مخرجات النموذج النهائية الموجودة في السمة logits وفق التالي: >>> import tensorflow as tf >>> tf_predictions = tf.nn.softmax(tf_outputs.logits, axis=-1) >>> tf_predictions حفظ النموذج يمكنك حفظ النموذج مع المُرَمِّز الخاص به بعد الانتهاء من تدريبه على بيانات مشروعك باستخدام PreTrainedModel.save_pretrained() في إطار العمل Pytorch وفق التالي: >>> pt_save_directory = "./pt_save_pretrained" >>> tokenizer.save_pretrained(pt_save_directory) >>> pt_model.save_pretrained(pt_save_directory) وعندما تحتاجه مجددًا تستطيع تحميله بواسطة PreTrainedModel.from_pretrained() كما يلي: >>> pt_model = AutoModelForSequenceClassification.from_pretrained("./pt_save_pretrained") أما في إطار العمل TensorFlow فيمكنك حفظ النموذج باستخدام TFPreTrainedModel.save_pretrained(): >>> tf_save_directory = "./tf_save_pretrained" >>> tokenizer.save_pretrained(tf_save_directory) >>> tf_model.save_pretrained(tf_save_directory) وعندما تريد استخدامه ثانيةً استعمل TFPreTrainedModel.from_pretrained() وفق التالي: >>> tf_model = TFAutoModelForSequenceClassification.from_pretrained("./tf_save_pretrained") تُعدّ ميزة تحويل النماذج من PyTorch إلى TensorFlow وبالعكس واحدة من أبرز مميزات المكتبة Transormers، وذلك باستخدام المعاملين from_pt و from_tf فيمكنك حفظ النموذج ثم تحميله بالصيغة المناسبة لإطار العمل الذي تفضله، وهذا مثال عملي: أولًا إطار العمل PyTorch: >>> from transformers import AutoModel >>> tokenizer = AutoTokenizer.from_pretrained(tf_save_directory) >>> pt_model = AutoModelForSequenceClassification.from_pretrained(tf_save_directory, from_tf=True) ثانيًا إطار العمل TensorFlow: >>> from transformers import TFAutoModel >>> tokenizer = AutoTokenizer.from_pretrained(pt_save_directory) >>> tf_model = TFAutoModelForSequenceClassification.from_pretrained(pt_save_directory, from_pt=True) بناء نموذج تعلّم آلي مخصص يمكنك بناء نموذج مخصص لحالتك بتعديل صنف إعدادات النموذج configuration class وتحديد السمات التي تريدها، مثل: عدد الطبقات المخفية hidden layers ورؤوس الانتباه attention heads، فعندما تبني نموذجك من الصفر استنادًا إلى صنف إعدادات مخصص فإن سمات النموذج ستأخذ في البداية محددات عشوائية ويمكنك ضبطها فيما وتحقيق الاستفادة المتوقعة منه بعد بتدريب النموذج على البيانات المناسبة لمشروعك. لنبدأ بالتطبيق العملي: سنستدعي أولًا الصنف AutoConfig ثم نُحمّل النموذج المُعدّ مسبقًا الذي نريد تخصيصه حسب حالتنا، ونذكر السمة التي نود تعديلها ضمن الدالة AutoConfig.from_pretrained() كما في المثال التالي الذي يبين طريقة تغيير سمة رؤوس الانتباه: >>> from transformers import AutoConfig >>> my_config = AutoConfig.from_pretrained("distilbert/distilbert-base-uncased", n_heads=12) والخطوة التالية هي بناء النموذج انطلاقًا من الإعدادات المخصصة التي ضبطناها أعلاه وباستخدام AutoModel.from_config() في Pytorch، و TFAutoModel.from_config() في TensorFlow. إذا كنت تستخدم إطار العمل Pytorch فيمكنك تنفيذها وفق التالي: >>> from transformers import AutoModel >>> my_model = AutoModel.from_config(my_config) وإذا كنت تعتمد TensorFlow وفق التالي: >>> from transformers import TFAutoModel >>> my_model = TFAutoModel.from_config(my_config) المُدَرِّبْ Trainer- حلقة التدريب المحسنة في PyTorch جميع نماذج مكتبة المحولات Transformers المتوافقة مع PyTorch هي نماذج torch.nn.Module قياسية، يمكنك استخدامها في أي حلقة تدريب training loop نموذجية تريدها؛ يمكنك أيضًا برمجة حلقة تدريبك الخاصة وفق متطلبات عملك، ومع ذلك توفر لك المكتبة صنفًا خاصًا بتدريب حلقات PyTorch يدعى صنف المُدَرِّب Trainer class ويتضمن حلقة التدريب الاعتيادية ووظائف إضافية أخرى مثل: التدريب الموزع distributed training والتدريب مختلط الدقة mixed precision وغيرها. وفي أثناء العمل ستمرر للمُدَرِّب عدة وسطاء حسب احتياجات مشروعك، لنبدأ بالخطوات العملية: 1. حمّل في البداية نموذجًا قياسيًا: >>> from transformers import AutoModelForSequenceClassification >>> model = AutoModelForSequenceClassification.from_pretrained("distilbert/distilbert-base-uncased") 2. حَدِّدْ قيم وسطاء التدريب مثل: معدّل التدريب learning rate، والحجم الموحد لقطع المدخلات التي يعالجها النموذج دفعة واحدة batch size، وعدد دورات التدريب number of epochs أي عدد تكرارات مجموعة البيانات التي سيتدرب عليها النموذج، وإن لم تحدد قيمًّا للوسطاء فستُستخدم القيم الافتراضية: >>> from transformers import TrainingArguments >>> training_args = TrainingArguments( output_dir="path/to/save/folder/", learning_rate=2e-5, per_device_train_batch_size=8, per_device_eval_batch_size=8, num_train_epochs=2, ) 3. حَدِّدْ صنف المعالجة المسبقة الذي تحتاجه، مثل: المُرمِّز tokenizer أو معالج الصور mage processor أو مستخرج المميزات feature extractor أو المعالج processor: >>> from transformers import AutoTokenizer >>> tokenizer = AutoTokenizer.from_pretrained("distilbert/distilbert-base-uncased") 4. حمّل مجموعة البيانات dataset المناسبة: >>> from datasets import load_dataset >>> dataset = load_dataset("rotten_tomatoes") # doctest: +IGNORE_RESULT 5. أنشئ دالة لترميز مجموعة البيانات: >>> def tokenize_dataset(dataset): return tokenizer(dataset["text"]) ثم طبّقها على كامل مجموعة البيانات باستعمال map وفق التالي: >>> dataset = dataset.map(tokenize_dataset, batched=True) 6. استخدم DataCollatorWithPadding لإنشاء قطع من الأمثلة من مجموعة البيانات: >>> from transformers import DataCollatorWithPadding >>> data_collator = DataCollatorWithPadding(tokenizer=tokenizer) والآن اجمع كل هذه الأصناف في شيفرة المُدَرِّب trainer كما يلي: >>> from transformers import Trainer >>> trainer = Trainer( model=model, args=training_args, train_dataset=dataset["train"], eval_dataset=dataset["test"], tokenizer=tokenizer, data_collator=data_collator, ) # doctest: +SKIP وبعد ذلك يمكنك استدعاء المُدَرِّب train() ليبدأ عملية التدريب وفق التالي: >>> trainer.train() يمكنك التحكم بسلوك حلقة التدريب بطريقتين: أولًا عبر إنشاء أصناف فرعية subclassing لمنهجيات التدريب المضمنة داخل المُدَرِّب Trainer؛ يساعدك ذلك على تخصيص ميزات مثل: دالة الخسارة loss function، والمُحَسِّن optimizer، والمُجَدّوِل scheduler حسب متطلبات عملك، يمكنك الاطلاع على قسم المُدَرِّب من توثيقات Hugging Face لمزيد من المعلومات عن منهجيات التدريب القابلة للتصنيف الفرعي subclassing. وثانيًا باستخدام الاستدعاءات Callbacks لتحقيق التكامل مع المكتبات الأخرى ولتفحص عمل حلقة التدريب ومتابعة تقدمها أو إيقاف التدريب في مرحلة معينة، مع العلم أن الاستدعاءات لا تتيح لك إجراء أي تعديل على بنية الحلقة لتخصيص ميزات مثل دالة الخسارة أو غيره فمثل هذه الأعمال محصورة فقط بالطريقة الأولى أي بإنشاء الأصناف الفرعية. تدريب نموذج في TensorFlow جميع نماذج المكتبة المتوافقة مع TensorFlow هي نماذج tf.keras.Model قياسية، يمكنك تدريبها باستخدام واجهة برمجة التطبيقات Keras API، وتوفر مكتبة المحولات منهجيةً تدعى prepare_tf_dataset() تسهل عليك تحميل مجموعات البيانات التي ستعتمدها بصيغة tf.data.Dataset والشروع بتدريبها باستخدام منهجيات Keras مثل compile و fit، لنبدأ بالتطبيق العملي: 1. حمّل نموذجًا قياسيًا: >>> from transformers import TFAutoModelForSequenceClassification >>> model = TFAutoModelForSequenceClassification.from_pretrained("distilbert/distilbert-base-uncased") 2. اختر صنف المعالجة المسبقة الذي تحتاجه، مثل: المُرمِّز tokenizer أو معالج الصور mage processor أو مستخرج المميزات feature extractor أو المعالج processor: >>> from transformers import AutoTokenizer >>> tokenizer = AutoTokenizer.from_pretrained("distilbert/distilbert-base-uncased") 3. أنشئ دالة لترميز مجموعة البيانات: >>> def tokenize_dataset(dataset): return tokenizer(dataset["text"]) # doctest: +SKIP طبّق الترميز على كامل مجموعة البيانات باستعمال map، ثم مرر المُرَمِّز ومجموعة البيانات إلى prepare_tf_dataset() وفق التالي، ويمكنك في هذه المرحلة تغيير حجم الدفعات أو قطع المدخلات batch size أو خلط بيانات مجموعة البيانات إذا رغبت بذلك: >>> dataset = dataset.map(tokenize_dataset) # doctest: +SKIP >>> tf_dataset = model.prepare_tf_dataset( dataset["train"], batch_size=16, shuffle=True, tokenizer=tokenizer ) # doctest: +SKIP استدعِ compile و fit لبدء عملية التدريب، وننوه هنا إلى أن جميع نماذج مكتبة المحولات تتضمن دالة افتراضية لحساب الخسارة تتعلق بطبيعة المهمة التي تعمل عليها، يمكنك الاعتماد عليها افتراضيًا إلّا إذا رغبت بعكس ذلك: >>> from tensorflow.keras.optimizers import Adam >>> model.compile(optimizer=Adam(3e-5)) # No loss argument! >>> model.fit(tf_dataset) # doctest: +SKIP الخلاصة وصلنا إلى ختام جولتنا السريعة في مكتبة Transformers، تابع مقالنا القادم تثبيت مكتبة المحولات Transformers لتتعلم كيفية تثبيتها والتعامل معها باستخدام كل من PyTorch و TensorFlow و Flax. ترجمة -وبتصرف- لقسم Quick tour من توثيقات Hugging Face. اقرأ أيضًا المقال السابق: تعرف على مكتبة المحوّلات Transformers من منصة Hugging Face تعرف على أفضل دورات الذكاء الاصطناعي تعرف على إطار عمل باي تورش PyTorch وأهميته لتطبيقات الذكاء الاصطناعي أساسيات الذكاء الاصطناعي: دليل المبتدئين
-
إن تثبيت نظام التشغيل هو نقطة الانطلاق في إعداد أي خادم سحابي، وقد سيطرت نظم تشغيل ويندوز و UNIX على بيئات الخوادم في بعض المجالات التجارية لفترة طويلة سابقة، أما اليوم فالجميع تقريبًا يستخدم نظام التشغيل لينكس لأنه مجاني في المقام الأول، وإن احتاج لتراخيص فتراخيصه مرنة مقارنة بغيره، بالإضافة لكونه يتمتع بدعم وشعبية كبيرة في الأوساط التقنية، وخاصة في حوسبة الخوادم server computing. تتنوع توزيعات لينكس ولكل منها مزاياه الخاصة، وبيئاته المفضلة للعمل، وبعض التوزيعات مشمول بخدمات الدعم الفني والصيانة من مزودي الخدمات التجاريين، بينما لا يخضع بعضها الآخر لهذا النوع من الدعم. فما هي أشهر توزيعات لينكس؟ ما الفروق بينها؟ وكيف تختار التوزيعة الأنسب لبناء خادمك السحابي؟ نظرة عامة على التوزيعات أوبنتو Ubuntu، من أشهر توزيعات لينكس للخوادم والحواسيب الشخصية، تُطرح إصدارات أوبنتو جديدة كل شهرين، أما إصدارات الدعم الفني طويل الأمد فتصدر كل سنتين، وتدوم لخمس سنوات، وستجد أن معظم المحتوى التعليمي المنتشر عن لينكس يتعلق بأوبنتو بسبب شعبيته الكبيرة، ويسجل هذا الدعم الواسع نقطة مهمة لصالح أوبنتو على حساب توزيعات لينكس الأخرى. ديبيان Debian، هو الأساس الذي يرتكز عليه أوبنتو، وبالتالي تُحدِد قراراته التصميمية طبيعة إصدارات أوبنتو، ويستخدم ديبيان لاحقة أوبنتو نفسها أي deb. وأيضًا مدير الحزم نفسه apt، لكن لا يتمتع ديبيان بشعبية كبيرة بين المستخدمين في خوادم الإنتاج، بسبب طابعه التقليدي وافتقاره للدعم التجاري الكافي. ومع ذلك، يفضله العديد من المستخدمين بسبب سهولة نقله من منصة إلى أخرى، ولكونه المنبع الأساسي الذي تفرعت عنه الكثير من توزيعات لينكس العاملة على مختلف المنصات، ومن بينها راسبيان Raspbian الذي يعد نظام التشغيل الرسمي والشهير لجهاز راسبيري باي Raspberry Pi وهو حاسوب صغير الحجم ومنخفض التكلفة يستخدم لأغراض متعددة كالتعليم والتطوير والهوايات التقنية. ريدهات لينكس للمؤسسات Red Hat Enterprise Linux، واختصاره RHEL، وهو أكثر توزيعات لينكس شعبية، ويتمتع بدعمٍ تجاري، وعلى عكس التوزيعات المستندة إلى ديبيان، فإن ريدهات يستخدم حزمًا من نوع rpm.، ومدير حزم يدعى dnf، جنبًا إلى جنب مع الأدوات والحلول التي يوفرها ضمن ضمن نظامه البيئي Red Hat Ecosystem، يتطلب استخدام ريدهات اشتراكًا مدفوعًا للحصول على خدمات الدعم الفني، وهذا أمر إلزامي تفرضه شروط الترخيص. روكي لينكس Rocky Linux، يستند روكي لينكس إلى توزيعة ريدهات، بالطريقة نفسها التي يستند فيها أوبنتو إلى ديبيان، روكي مجاني على عكس RHEL لذا يُعدّ خيارًا ممتازًا للمستخدمين الذين اعتادوا على استخدام ريدهات لكنهم غير قادرين أو راغبين بدفع رسوم مالية، تُطلق إصدارات روكي لينكس بتوقيت متزامن مع إصدارات ريدهات، حتى التوثيقات مشتركة بينهما.وقد كانت توزيعة CentOS سابقَا تلعب الدور نفسه الذي يقوم به توزيعة روكي حاليًا، ولكن تغير نموذج إصدارها مما دفع المستخدمين للبحث عن بديل مجاني ومستقر كالذي يقدمه روكي. فيدورا لينكس Fedora Linux، يستخدم في بيئات سطح المكتب وفي الخودام، ويرتبط فيدورا بتوزيعة ريدهات، فهو موطن التطوير الفعلي لها، تختبر فيه جميع حلول ريدهات قبل نشرها في مشاريع ريدهات، ومن بينها بيئة سطح المكتب Gnome التي يستخدمها أوبنتو وغيره. آرتش لينكس Arch Linux، من أشهر توزيعات لينكس الخاصة ببيئات سطح المكتب، لا ينتمي لعائلة ديبيان أو ريدهات، فله حزمه وأدواته خاصة. ولا يعدّ خيارًا مثاليًا لخوادم الإنتاج لكونه لا يعتمد نظام الإصدارات، فالمستخدم يعمل دائمًا على أحدث الحزم المتوفرة، لكنه بالمقابل يوفر توثيقًا جيدًا لمستخدميه، ويعطي مرونة كبيرة للضلعين بالتعامل معه. ألبين لينكس Alpine Linux، من أبسط توزيعات لينكس وأصغرها حجمًا، وهو لا يتضمن معظم الأدوات التي اعتدنا وجودها افتراضيًا في لينكس. تمحورت العديد من توزيعات لينكس تاريخيًا حول هذا الهدف لكن ألبين يبقى الأشهر بينها. وهو يستخدم بكثرة في بناء النماذج الأولية للحاويات، وخاصةً في عمليات النشر الحديثة المعتمدة على الحاويات مثل Docker، فعندما يحتاج تطبيقك لنظام تشغيل مصغر يعمل افتراضيًا ضمن الحاوية فألبين هو الخيار الأمثل. تقلصت الفروق بين توزيعات لينكس مع الزمن، فقد اختلفت كثيرًا في بداية انطلاقتها وخاصة بأمور أساسية مثل: نظام التهيئة init system، ومدير النوافذ، والمكتبات المستخدمة وغير ذلك، أما اليوم تعتمد معظمها الأساسيات نفسها تقريبًا، مثل نظام التمهيد systemd الموحد، وغيره من الأدوات المشابهة. اختيار التوزيعة المناسبة عرضنا في الفقرة السابقة سبعة من أشهر توزيعات لينكس، وتوجد توزيعات غيرها بالتأكيد، لكن معرفتك بهذه التوزيعات مبدئيًا وبالاختلافات بينها ستساعدك على تكوين فكرة عامة عن معايير اختيار التوزيعة، والتي نلخصها بالنقاط التالية: هل تستخدم منصة أو نظامًا بيئيًا ما يعتمد كليًا على توزيعة معينة، مثل ديبيان أو ريدهات يحتم عليك التوافق معها؟ هل تطوّر برمجياتك للعمل في البيئة السحابية أم في بيئة سطح المكتب أم ربما للحاويات؟ هل تفرض عليك متطلبات العمل استخدام أحدث الحزم المتوفرة دائمًا بصرف النظر عن أي شيء؟ أم العكس والأولوية عندك لاستخدام الحزم المختبرة جيدًا ومستقرة الأداء؟ بإجابتك عن الأسئلة ستجد أن اختيار التوزيعة المناسبة يرجع لمتطلبات عملك وتفضيلاتك الشخصية، فعلى سبيل المثال إذا كنت تعمل في بيئة سحابية، ولا تربطك أي متطلبات إنتاجية بمنصة محددة نحو النظام البيئي لريدهات أو غيره، فإن أوبنتو عندها يُعدّ خيارًا جيدًا وشائع الاستخدام تستطيع الاعتماد عليه. وستجد في هذا الرابط على سبيل المثال لا الحصر حزمًا لتوزيعة أوبنتو 22.04، ويمكنك البحث عن مستودعات أخرى على الإنترنت تستضيف حزمًا لتوزيعات لينكس التي تختارها. إدارة الحزم تختلف معظم توزيعات لينكس اختلافًا ملحوظًا في طريقة تعاملها مع حزم الطرف الثالث أي الحزم التي لا تتبع لمستودعاتها الأساسية، سواء في الإنشاء أو الاكتشاف أو التثبيت. ستجد أن توزيعات ريدهات وفيدورا وروكي لينكس لا تستخدم حزمًا خارجية إلّا فيما ندر، فمعظم اعتمادها على الحزم الرسمية، بسبب سياساتها الداخلية الحازمة التي توالي اهتمامًا كبيرًا بتطوير المنتج، وأشهر EPEL أو الحزم الإضافية لنظام لينكس للمؤسسات، وهي مجموعة من الحزم المختارة بعناية لتناسب بيئة الشركات، فعندما تتعامل مع النظام البيئي لريدهات ستجده يميّز الحزم المدعومة تجاريًا عن غير المدعومة، فإذا أخذت أي من الحزم الخارجية الشائعة على أوبنتو مثلًا، وحاولت تثبيتها على ريدهات سيُطلب منك إعداد حزم EPEL أولًا. تعتمد كميات الحزم المتوفرة في مستودعات توزيعتك الرسمية وضرورة استخدامك لها على أمرين أساسيين هما: درجة السيطرة المفروضة على التوزيعة من الشركة الأم، ومسؤوليات الصيانة الموكلة إلى المشرفين التجاريين عليها أو مزودي الخدمة. ومع ذلك تنتشر الحزم الخارجية أو حزم الطرف الثالث على نطاق واسع بين مستخدمي لينكس، ويتمتع الكثير منها بثقتهم، لكن هذه الحزم بالمقابل لا تخضع للصيانة والدعم الفني من الجهات المشرفة على التوزيعات والحزم الرسمية. تتيح توزيعة أوبنتو للمستخدمين الأفراد إنشاء أرشيفاتهم الخاصة من الحزم الشخصية أو ما يسمى PPAs، التي تتضمن حزم طرف ثالث سبق لهم تثبيتها ويعرضونها للآخرين للاستفادة منها وتثبيتها، تُعدّ هذه النقطة مرونة كبيرة من أوبنتو لكنها قد تضع المستخدم في إشكاليات ناجمة عن التعارض بين بعض حزم PPA المختلفة، وخاصةً إذا لجأ إلى تثبيت أعداد كبيرة منها بالتزامن مع بعضها، فلكل حزمة من ديبيان وأوبنتو متطلباتها الخاصة. يفرض ذلك على مسؤولي PPA لتوزيعة أوبنتو التأكد دائمًا من مطابقتهم لآخر التحديثات الحاصلة على المستودع الأصلي أو مستودع المنبع upstream لتلافي مشكلات عدم التوافق. أما توزيعة آرتش لينكس فتحتوي مستودعًا وحيدًا فقط يسمى مستودع مستخدم آرتش أو AUR، ويضم كافة الحزم المقدمة للمستخدم، ربما تفتقر هذه الطريقة للتنظيم لكنها بالمقابل تتيح لمستخدمي التوزيعة استعمال العشرات من حزم الطرف الثالث. يمكنك أيضًا تبسيط عملية إدارة الحزم عبر تثبيت برمجيات مخصصة لهذا الغرض، مثل: Homebrew و Docker، استعرض مثلًا هذا الدليل العربي لتثبيت دوكر على فيدورا، قد لا تتمتع عمليات النشر المعتمدة على الحاويات بالفعالية المنشودة لجهة استخدامية القرص الصلب، والموارد اللازمة للتثبيت، وهذا ما يعزز انتشار ألبين لينكس المصغرة في هذه البيئات، لكن البرمجيات المعبأة في الحاويات سهلة النقل من توزيعة لأخرى، ولا تفرض عليك إصدارات معينة فكل متطلباتها موجودة بداخلها. أما النقطة الأخيرة التي يجدر بك أخذها في الحسبان أنك لن تحظى بتحديثات أوتوماتيكية لحزمك ما لم تستخدم الحزم الخاصة بالتوزيعة. الخلاصة تعرفنا في مقال اليوم على أهم المعايير المتعلقة باختيار توزيعة لينكس لخادمك السحابي، رغم أن الاعتماد الواسع على الحاويات مثل دوكر وغيرها، قد خفف تأثير توزيعة نظام تشغيل على نوعية البرامج الممكن تشغيلها على الخادم، ومع ذلك يظل اختيار توزيعة لينكس واحدًا من أهم الاعتبارات التي عليك الانتباه لها في رغبت بالحصول على الدعم الفني لنظامك، أو تجهيز بيئتك لمرحلة الإنتاج دون أي مخاوف. ترجمة -وبتصرف- للمقال How to Choose a Linux Distribution لصاحبه Alex Garnett. اقرأ أيضًا ما هو نظام لينكس ولماذا توجد 100 توزيعة منه؟ تعرف على نظام لينكس Linux وأبرز مميزاته وعيوبه 10 طرق لتجربة نظام التشغيل لينكس Linux واستكشافه أساسيات إدارة حزم لينكس apt ،yum ،dnf ،pkg
-
تقدم لنا الواجهات الرسومية أو GUI تجربةً بصرية بديهية، وسهلة الاستخدام للتعامل مع الحواسيب والهواتف الذكية، والأجهزة اللوحية، فتجدنا ننتقل بين التطبيقات وصفحات الويب والمستندات والصور بنقرات بسيطة على الأيقونات والقوائم المنسدلة وبقية عناصر سطح المكتب التي توفرها نظم التشغيل المختلفة، مثل: ويندوز وماك ولينكس. وعلى الرغم من سهولة استخدامها من قبل المستخدم العادي إلّا أن واجهات المستخدم الرسومية تبقى قاصرة نوعًا ما عن منحنا التحكم الإداري الكامل بالأجهزة بالكيفية التي نريدها، فقد تمنعنا من عمليات التثبيت أو التعديل أو الحذف لبعض البرامج والميزات والملفات، وهنا يبرز دور واجهة سطر الأوامر. يشار لها بالرمز CLI وهو اختصار command-line interface، ويُعدّ إتقانها والتعود على استخدامها عبر الطرفية Terminal من أهم طرق التنقل بين حاسوبك الشخصي والخوادم السحابية، بغرض الوصول إليها والتحكم بها عن بعد بدون الحاجة للواجهات الرسومية. توجد الواجهات الطرفية (التي تكتب ضمنها الأوامر) في معظم نظم التشغيل الحاسوبية، وتتوفر لها محاكيات خاصة بهيئة تطبيقات تُثبّت على الهواتف الذكية والأجهزة اللوحية. وهي تمنح المستخدمين تحكمًا أكبر بأجهزتهم عبر تعزيز صلاحيات الوصول المتاحة لمدير النظام administrator، وزيادة قدرة المستخدم على تخصيص بيئات العمل، وأتمتة العمليات التي يحتاجها، وإعطائه إمكانية الوصول إلى الأجهزة البعيدة، مثل الخوادم السحابية. إذا كنت من مستخدمي واجهة سطر الأوامر الجدد، فسيعطيك هذا التمهيد فكرةً جيدة عن أساسيات استخدامها عبر الطرفية، وستكون في نهايته قادرًا على تطبيقها مع لينكس وماك، سواء على حاسوبك الشخصي أو على الخوادم. لمعلومات أكثر يمكنك أيضًا الاطلاع على مقال مقدمة إلى طرفية لينكس، ومقال دليل استخدام سطر الأوامر في عملية تطوير الويب من طرف العميل. فهم النافذة الطرفية عندما تستخدم حاسوبًا أو أي هاتف محمول لأول مرة عبر الواجهات الرسومية، ستبدأ رحلة التعرف عليه باستعراض التطبيقات الموجودة، وأماكن توضع الأشياء، لتتمكن من تخصيصه حسب احتياجاتك، والحالة نفسها لاستكشافه مع الطرفية، وهو ما سنتعرف عليه تباعًا ضمن المقال. الطرفية المستخدمة هنا هي طرفية أوبنتو 20.04، إحدى أشهر توزيعات لينكس، المبني أساسًا على يونيكس، فإذا كان نظام التشغيل على حاسوبك هو لينكس أو ماك يمكنك تطبيق الأمثلة الواردة هنا بحرفيتها، فطرفية النظامين مشابهة لطرفية أوبنتو المعتمدة في المقال، وكليهما مبني على يونيكس. وإذا كنت تستخدم ويندوز يمكنك الاستعانة بمقال تثبيت نظام لينكس داخل نظام ويندوز في بيئة وهمية لتهيئة بيئة لينكس على حاسوبك نفسه. افتح الآن الطرفية على حاسوبك أو استخدام أحد المواقع التي توفر طرفيات تفاعلية للتجربة مثل Glitch.com، وستحصل على نافذة شبيهة بهذه الصورة. لاحظ رمز الدولار $ والمؤشر الوامض هنا ستبدأ الكتابة. تدّل الرموز الموجودة في نهاية محث الأوامر prompt على مستوى مستخدم الطرفية، وفق التالي، وهي تنطبق على معظم نظم التشغيل المعتمدة على يونيكس أو التي تسمى nix*: رمز الدولار $ للمستخدم العادي أي أنك سجلت دخول بحساب مستخدم عادي. رمز المربع# للمستخدم صاحب أعلى الامتيازات. يُعرف المستخدم ذو الرمز # بأنه مستخدم الجذر Root، ويُعدّ مستخدمًا مميزًا أو مدير النظام الافتراضي. ستجد الرمز $ في جميع الأمثلة هنا فقد طبقت بواسطة مستخدم عادي، لكنه يستطيع الحصول على امتيازات الجذر باستعمال الأمر sudo. افتح الطرفية على حاسوبك لنبدأ بالأمثلة العملية. التآلف مع المجلدات الأوامر التي نكتبها في سطر الأوامر هي تعليمات تخبر حاسوبك بما تريد تنفيذه، تكتب الأمر، ثم تضغط على زر ENTER أو RETURN ليبدأ التنفيذ. اكتب مثلًا الأمر التالي في الطرفية، واضغط ENTER: $ pwd نفذ الأمر، وستحصل على الخرج التالي: /home/sammy يرمز الأمر pwd إلى مجلد العمل الحالي، وهو اختصار للعبارة present working directory، ويساعدك على معرفة مكان وجودك الحالي ضمن نظام الملفات أي المجلد الذي تُنفذ منه الأوامر. مجلد العمل الحالي في مثالنا هو home/sammy/، وsammy هو اسم المستخدم، قد يكون أي اسم آخر، فإذا سجلت دخولًا بحساب مستخدم الجذر root صاحب أعلى الامتيازات، فسيكون مجلد العمل الحالي root/، وعلى حاسوبك الشخصي سيسمى المجلد باسم المستخدم الذي تستعمله، غالبًا ما يطلق مالك الحاسوب اسمه على المستخدم فتجد المجلد باسمه أيضًا. مجلد العمل الحالي فارغ الآن، لذا سننشئ ضمنه مجلدًا جديدًا اسمه الملفات أو files مثلًا، نخزن فيه الملفات التي ستطبق عليها أمثلة المقال. تُنشَأ المجلدات بواسطة الأمر mkdir، وهو اختصار لعبارة make directory، ثم نكتب اسم المجلد الذي نود إنشائه بعد الأمر mkdir، يلعب اسم المجلد هنا دور الوسيط، لأنه يمرر القيمة المدخلة إلى الأمر حتى يأخذ بها عند التنفيذ. يشبه الوسيط المفعول به في القواعد النحوية، فهو يمثل ما يتصرف به الفعل أو الأمر البرمجي في حالتنا. اكتب السطر التالي في الطرفية لديك، يبدأ السطر بالأمر mkdir، ويليه الوسيط files وهو اسم المجلد الذي ننشئه: $ mkdir files اضغط Enter ونفذ الأمر، وفور ظهور المؤشر الوامض في السطر التالي سيكون مجلدك الجديد قد أُنشئ، وتكون جاهزًا لكتابة الأوامر التالية، لن تحصل على خرج يخبرك بإتمام عملية الإنشاء، فقط المؤشر الوامض. عرض محتويات المجلدات وفهم صلاحيات العمل عليها سنستخدم الأمر ls المستوحى من كلمة list، لإظهار محتويات مجلد العمل الحالي (حيث أنشأنا مجلدنا الجديد)، يمكنك بهذه الطريقة التثبت من صحة إنشائه بما أنك لم تحصل على خرج صريح يؤكد ذلك. $ ls انظر الخرج، أنه يؤكد وجود المجلد files: files يعطيك هذا الأمر معلومات عامة عن محتويات مجلد العمل الحالي، وتستطيع الحصول على معلومات تفصيلية أكثر باستعمال الرايات Flags. تكتب الرايات في لينكس بشكل شرطة - متبوعة بحرف. تُمرِر الرايات للأمر الأساسي خياراتٍ إضافية أو وسطاء إضافيين. فعلى سبيل المثال يمكنك إضافة الراية l- للأمر السابق ليظهر خرجه بتنسيق قائمة طويلة فيها معلومات مفصلة عن محتويات المجلد. جرب كتابة الأمر مع الراية كما يلي: $ ls -l اضغط زر ENTER للتنفيذ، وستحصل على الخرج الآتي: total 4 drwxr-xr-x 2 sammy sammy 4096 Nov 13 18:06 files يشير السطر الأول من الخرج إلى عدد الكتل blocks المحجوزة للمجلد في ذاكرة الحاسوب، عددها هنا 4، أما السطر الثاني فيتضمن صلاحيات المستخدم على المجلد وبعض التفاصيل الأخرى. يمكنك أيضًا استعمال الراية h- أو human-readable-- لتحصل على خرج أكثر مقروئية، فيظهر الحجم المخصص للمجلد في الذاكرة مقدرًا بالكيلو بايت مثلًا أي بصيغة أسهل للقراءة والفهم، كما هو موضح أدناه. ستلاحظ عند تعاملك مع الرايات: أن الشرطة الوحيدة - تشير إلى راية يُعبر عنها بحرف وحيد، والشرطتان -- إلى الرايات التي تكتب بالكلمات، وتستخدم بعض الرايات الصيغتين فقد تكتب بحرف وحيد أو بالكلمات، يمكنك أيضًا كتابة أمر متعدد الرايات أو متعدد الوسطاء بتجميع رموز عدة رايات معًا مثل lh-. انظر الأمرين أدناه لهما المعنى نفسه تمامًا، لكن كل منهما مكتوب بطريقة: $ ls -lh $ ls -l --human-readable سيعطي كلاهما الخرج نفسه، وهو مشابه لما حصلنا عليه سابقًا مع فارقٍ وحيد، إذ سيظهر الحجم بتنسيق سهل القراءة بسبب الراية الخاصة بالمقروئية: total 4.0K drwxr-xr-x 2 sammy sammy 4.0K Nov 13 18:06 files يبين الخرج السابق مجموعة معلومات عن المجلد، مثل: حجمه في الذاكرة 4K، وصلاحيات المستخدمين عليه وغير ذلك، انظر الجدول التالي للتوضيح: نوع الملف الصلاحيات عدد الوصلات اسم المالك مجموعة العمل حجم الملف تاريخ آخر تعديل اسم الملف d rwxr-xr-x 2 sammy sammy 4.0K Nov 13 18:06 files يتضمن الجدول سطرًا واحدًا فقط، لأننا نملك مجلدًا واحدًا files في مجلد العمل الحالي home/sammy/، ولو كان لدينا عدة مجلدات أو ملفات كنت ستجد سطورًا بعددها. لنوضح الآن الرموز الموجودة في بداية السطر الثاني من الخرج: المحرف الوصف d أي مجلد أو directory، والمجلد في علم الحاسوب هو العنصر التنظيمي الذي يحتوي عدة ملفات، وإذا لم يكن العنصر مجلدًا فستجد شرطة - بدل الحرف d r تعني صلاحية القراءة read، وتسمح للمستخدم بفتح الملف وقراءته، أو باستعراض محتويات المجلد w صلاحية الكتابة write، تتيح تعديل محتويات الملف، وإضافة ملفات إلى المجلد أو حذف بعض من ملفاته أو تغيير أسمائها x صلاحية تنفيذ ملف execute أو تشغيله كبرنامج، ومن جهة المجلدات تعني الصلاحية الدخول إلى المجلد والوصول إلى ملفاته حاول الآن قراءة الخرج السابق مجددًا، فلنبدأ بسلسلة المحارف drwx، يعني المحرف الأول d أن العنصر الموصوف هو مجلد، فلو كان نوعًا آخر، لبدأت السلسلة بالشرطة - بدلًا منه على هذا الشكل rwx-، أما المحارف الثلاثة التالية مخصصة لأذونات مالك المجلد، وتعني أن المالك لديه صلاحيات القراءة والكتابة والتنفيذ على مجلده المجلد files، وإن استبدل أي محرف منها بالشرطة - فسيفقد المالك الصلاحية التي يمثلها هذا المحرف. وتمثل المحارف الثلاثة التي تليها صلاحيات مجموعة العمل، وهي r-x في حالتنا، وتعني أن لدى المستخدمين المنتمين للمجموعة صلاحيات القراءة والتنفيذ على هذا المجلد، لكن لا يمكنهم التعديل عليه أبدًا فلا يملكون صلاحية الكتابة، إذ إن المحرف w مستبدل بالشرطة -. أما المحارف الثلاثة الأخيرة r-x فيقصد بها صلاحيات مجموعات العمل الأخرى على المجلد، وكما ترى تنحصر صلاحياتهم في القراءة والتنفيذ، ولا يملكون صلاحية الكتابة على المجلد. يشير الرقم 2 (الذي يلي سلسلة المحارف) إلى عدد روابط المجلد، تشبه الروابط Links في لينكس الاختصارات التي تسهل على المستخدمين الوصول إلى نظام الملفات. وعند تنشئ أي ملف أو مجلد سيبني له لينكس تلقائيًا رابطين، رابط مطلق، ورابط ذاتي المرجع يتيح الوصول إليه عبر مسار نسبي، سيتضح لك معنى النسبي والمطلق في القسم التالي من المقال. ويمكنك معرفة المزيد عن روابط لينكس بمراجعة المقال دليل المستخدم للروابط في نظام ملفات لينكس. ستجد بعد الرقم 2 كلمة sammy مكررة مرتين، تشير الأولى منها إلى اسم مالك المجلد، وقد تعرفنا على صلاحياته قبل قليل وهيrwx، أما لماذا sammy هو المالك فالسبب أننا أنشأنا المجلد files من حسابه. يفيدك دائمًا معرفة اسم المستخدم مالك الملفات في بيئة العمل الفعلية. وتدّل كلمة sammy الثانية على اسم مجموعة العمل المستفيدة من المجلد files أي التي تملك حق الوصول إليه، وصلاحياتها كما رأينا r-x، يتطابق اسم المستخدم هنا مع اسم مجموعة العمل، لكنه ليس أمرًا إلزاميًا، فستصادف في بيئات العمل الفعلية مجموعات عمل مختلفة، مثل: مجموعة الموظفين العاديين وقد يكون اسمها staff، ومجموعة مدراء النظام باسم admin مثلًا أو غير ذلك. أما بقية المعلومات الموجودة في الخرج فهي: الحجم المخصص للمجلد في الذاكرة، وقدره 4 كيلو بايت، بالإضافة إلى تاريخ آخر تعديل أجري عليه قبل تنفيذ التعليمة. أصبح لدينا الآن أرضية ننطلق منها لقسمنا التالي المتعلق بالتنقل عبر نظام الملفات. التنقل ضمن نظام الملفات تعلمت كيف تحدد موقعك الحالي في نظام الملفات، وكيف تنشئ مجلدًا جديدًا، وتستعرض محتوياته، وتحدد صلاحيات الوصول إليه والتحكم به. سنجرب الآن التنقل بين مجلدات نظام الملفات، أنشأنا المجلد files في المجلد الأساسي للمستخدم sammy وهو home/sammy/، فما طريقة الانتقال من home/sammy/ إلى home/sammy/files/؟ يمكنك الانتقال باستعمال الأمر cd، وهو اختصار للعبارة change directory التي تعني تغيير المجلد، اكتب إذًا السطر التالي في واجهة الطرفية لديك: $ cd files لن تحصل على رسالة أو خرج يؤكد لك صحة تنفيذ الأمر، وأنك في المسار home/sammy/files/، كما هو الحال مع أمر إنشاء المجلد في القسم السابق، ستتلقى فقط المؤشر الوامض، لذا استعمل الأمر pwd لتتأكد من ذلك: $ pwd سيظهر الخرج التالي: /home/sammy/files يؤكد الخرج السابق وجودك في المجلد home/sammy/files/ الذي يقع ضمن مجلد المستخدم home/sammy/، تبدو هذه المسارات المفصولة بالشرطات المائلة مألوفة بالنسبة لك؟ ملاحظتك صحيحة لأنها تشبه عناوين URL لمواقع الويب، فهذه المواقع في نهاية الأمر تتوضع ضمن مجلدات على خوادم الويب. يساعدك الأمر / cd على الوصول إلى المجلد الرئيسي للخادم من أي مكان في نظام الملفات: $ cd / نفذ الآن الأمر التالي لتستعرض محتويات المجلد الرئيسي: $ ls ستحصل على هذا الخرج: bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run s sbin srv sys tmp usr var هذه هي محتويات المجلد الرئيسي / لنظام لينكس، ويسمى أيضًا مجلد الجذر، وهو يختلف تمامًا عن مستخدم الجذر الافتراضي root، ويتمتع بأهمية كبيرة في نظام لينكس إذ يتضمن كل المجلدات الضرورية لعمل نظام التشغيل، فعلى سبيل المثال: يحتوي المجلد الفرعي sys على نواة النظام kernel، ومعلومات النظام عن نظام الملفات الافتراضي، يمكنك معرفة المزيد عن محتويات مجلد الجذر بمراجعة توثيقات لينكس الرسمية. لاحظ أن المجلد home/ الخاص بالمستخدمين موجودٌ هنا أيضًا، ويمكنك الانتقال إليه من مجلد الجذر / ثم الانتقال إلى المجلد files، أو استخدام الأمر cd والانتقال إلى files مباشرةً من مجلد الجذر / وفق التالي: $ cd /home/sammy/files نفذ الآن الأمر pwd وستحصل على home/sammy/files/ في الخرج. سنعرض الآن المسارات في لينكس، مسار الملف File Path هو توصيف لمكان وجود الملف أو المجلد على حاسوبك أو على الخادم، المسارات نوعان نسبي ومطلق، النسبي هو الذي يحدد موقع الملف أو المجلد الهدف نسبةً إلى مجلد العمل الحالي، يشبه طريقة انتقالنا إلى داخل المجلد /files عندما كنا في مجلد العمل الحالي home/sammy/. أما المسار المطلق فهو موقع الملف انطلاقًا من مجلد الجذر، كما فعلنا في الأمر أعلاه باستخدام home/sammy/files/، فقد بدأنا بمجلد الجذر /، ثم استدعينا home/sammy/، وبعدها /files الموجود بداخله، إذًأ فالمسار المطلق يبدأ دائمًا بإشارة الجذر /. النقط dot أيضًا هي أسلوب للتنقل عبر المسارات النسبية في لينكس. تشير النقطة الوحيدة . إلى مجلد العمل الحالي، والنقطتان .. إلى المجلد الأب، فمثلًا لو كنت في المجلد home/sammy/files/ يمكنك استخدام النقطتين للوصول إلى المجلد الأب home/sammy/، وفق التالي: $ cd .. يمكنك اختبار صحة التنفيذ باستخدام pwd لعرض مجلد العمل الحالي، ستحصل عندها على home/sammy/، وتستطيع أيضًا تنفيذ ls لعرض المحتويات فسيظهر لك المجلد files. يوجد رمزٌ آخر يفيدك في التنقل هو الرمز ~، ينقلك مباشرةً من أي مكان في نظام الملفات إلى المجلد الأساسي home للمستخدم الذي سجلت دخولًا باسمه، في مثالنا المسخدم هو sammy والمجلد الأساسي له يدعى home/sammy/، على حاسوبك سيكون باسم المستخدم الذي تنفذ الأوامر من حسابه. جرب الأمر التالي على الطرفية لديك، وسيأخذك بنقلة واحدة إلى المجلد الأساسي لمستخدمك: $ cd ~ سنتعلم في القسم التالي كيفية التعامل مع الملفات النصية. إنشاء الملفات النصية وتعديلها تأكد في البداية من وجدوك في المجلد /files، لديك طريقتين للقيام بذلك، إما الاستعلام عن مسار العمل الحالي بواسطة الأمر pwd، أو الانتقال مباشرة إلى المجلد بواسطة مساره المطلق وفق التالي: $ cd /home/sammy/files سننشئ الآن ملفًا نصيًا بلاحقة txt. وهي لاحقة قياسية يقرأها نظام التشغيل بدون أي برامج إضافية كما هو الحال مع ملفات doc. مثلًا. والنصوص المكتوبة ضمن الملفات من نوع txt. هي نصوص غير منسقة، يمكن استخدامها بسهولة داخل نافذة سطر الأوامر، أو عند التعامل مع النصوص برمجيًا، مثل: التحليل الآلي للنص، أو سحب المعلومات من نص معين، أو غير ذلك. سننشئ الملف بكتابة الأمر touch، المخصص لإنشاء الملفات النصية وتعديلها، ثم تمرير اسم الملف النصي بصفته وسيطًا له، وفق التالي: $ touch ocean.txt بمجرد الضغط على زر ENTER ستنتقل لسطر جديد في نافذة سطر الأوامر بدون أي رسالة تؤكد إنشاء الملف النصي، لذا يمكنك استعراض محتويات المجلد /files للتأكد من وجود الملف بداخله. $ ls وسيكون الخرج: ocean.txt أنشأنا إذًا الملف النصي ocean.txt لكنه فارغ، ولو أردنا إنشاء ملف نصي مجهز ببعض الكلمات فسيفيدنا في ذلك الأمر echo المخصص لعرض السلاسل النصية في نافذة سطر أوامر لينكس. يعمل الأمر echo على ترديد ما تكتبه بعده في نافذة سطر الأوامر. لنجرب ذلك بالعبارة المعتادة !Hello, World: $ echo Hello, World! وهذا هو الخرج: Hello, World! يأتي اسم echo أو الصدى من إيكو حورية الجبل التي تردد ما يقال لها فقط دون أي قدرة على حديث نابع من نفسها، حسب الأسطورة اليونانية القديمة في كتاب التحولات لأوفيد، والأمر echo هنا يكرر ما يكتب بعده فقط دون اجتهاد، وفي مثالنا أظهر العبارة !Hello, World كما هي في نافذة سطر الأوامر. أما لو رغبت باستخدامه لتخزين هذه العبارة أو غيرها في ملف نصي ما، فيتعين عليك كتابته بهذه الطريقة: $ echo "Sammy the Shark" > sammy.txt يبدأ السطر السابق بالأمر echo، يليه العبارة النصية التي نريد كتابتها في الملف، وبعده سهم إعادة التوجيه <، ثم اسم الملف النصي الجديد sammy.txt. تفحص الآن وجود الملف sammy.txt بواسطة الأمر ls وفق التالي: $ ls وسيكون الخرج: ocean.txt sammy.txt يظهر الخرج السابق وجود ملفين في المجلد home/sammy/files/، يمكنك استعراض الملف sammy.txt للتأكد من وجود العبارة النصية التي خزنتها فيه، وذلك باستخدام الأمر cat، وهو من أشهر أوامر لينكس، ويستعمل لعرض مضمون الملفات النصية ووظائف أخرى تتعلق بها: $ cat sammy.txt نفذ الأمر وستحصل على الخرج التالي: Sammy the Shark جرب استخدام cat مع الملف ocean.txt، لن تحصل على شيء لأنه فارغ من أي محتوى، أضف إليه أي عبارة تريدها بواسطة الأمر echo كما شرحنا سابقًا، سنضيف مثلًا اقتباسًا شهيرًا للكاتبة زورا نيل هيرستون، انظر للأمر التالي: $ echo "Some people could look at a mud puddle and see an ocean with ships." > ocean.txt استخدم cat معه مجددًا ولاحظ الفرق في الخرج: Some people could look at a mud puddle and see an ocean with ships. سنتعرف الآن على طريقة تعديل محتوى الملف النصي، ستحتاج لهذا العمل محرر نصوص يعمل مع سطر الأوامر، الخيارات متنوعة أبرزها: vim و Emacs و nano. سنستخدم هنا nano. يمكنك استدعائه بالأمر nano وتمرير اسم الملف الذي تريده تعديله بصفته وسيطًا له، انظر التالي: $ nano sammy.txt سيفتح الآن الملف sammy.txt، وتجد فيه المحتوى التالي: Sammy the Shark حرك المؤشرة باستعمال أسهم لوحة المفاتيح لتصل إلى نهاية العبارة الموجودة في الملف، واكتب ما تريده بعدها. لنفترض أنك أضفت التعديلات التالية إلى الملف: Sammy the Shark Hello, I am Sammy. I am studying computer science. Nice to meet you! حان الآن موعد الحفظ، فكيف تحفظ التعديلات؟ لاحظ الكلمات الموجودة في أسفل النافذة الطرفية لديك، تبدو مثل هذا: ^G Get Help ^O WriteOut ^R Read File ^Y Prev Page ^K Cut Text ^C Cur Pos ^X Exit ^J Justify ^W Where Is ^V Next Page ^U UnCut Text ^T To Spell هي تشرح نفسها نوعًا ما، فبجانب كل رمز كلمة توضح وظيفته. أو ما ينبغي معرفته أن الرمز ^ يشير لزر CTRL أو Control على لوحة المفاتيح. وما دام أننا أنهينا التعديلات سنضغط CTRL مع حرف X معًا لنحصل على مفعول Exit، ونغلق الملف، وانتبه لكتابة الحرف x حرف صغير أي بدون استخدام الزر Shift. CTRL x ستجدها في التوثيقات مكتوبة بصيغة CTRL + X أو Ctrl+x، وهي تعني الضغط على الزرين في الوقت نفسه. اضغط الزرين الآن، وستحصل مباشرةً على الرسالة التالية: Save modified buffer? Y Yes N No ^C Cancel اضغط الزر Y لحفظ التغييرات. Y وسيؤكد لك النظام عملية الحفظ بإظهار الخرج التالي: File Name to Write: sammy.txt تتضمن الرسالة خيارات إضافية مثل الضغط على CTRL + C للإلغاء، لكن إذا كنت واثقًا من رغبتك بالخروج من الملف اضغط على ENTER لحفظ التغييرات على الملف والخروج منه. لنفترض على سبيل المثال أنك تريد إنشاء عدد من الملفات النصية لطلبة أكاديمية حسوب، ملف لكل طالب، وسامي sammy هو أحد الطلاب، ستنشئ في البداية مجلدًا جديدًا يدعى الطلاب students مثلًا ضمن المجلد /files، ثم تنقل الملف sammy.txt إليه. أولًا تعليمة إنشاء المجلد: $ mkdir students والآن تعليمة النقل بواسطة الأمر mv (المستوحى من كلمة move)، يأخذ هذا الأمر وسيطين، الوسيط الأول هو الملف المراد نقله، والوسيط الثاني هو موقعه الجديد، انظر التعليمتين التاليتين فلهما النتيجة نفسها: $ mv sammy.txt students $ mv sammy.txt students/sammy.txt تعمل mv في التعليمة الأولى على نقل الملف مع الحفاظ على اسمه، أما في الثانية فيمكنك تغيير اسم الملف عند نقله إذا رغبت بذلك، تستطيع مثلًا كتابتها على الشكل التالي: mv sammy.txt students/sammy-the-shark.txt إذا استعرضت محتويات المجلد /files باستخدام الأمر ls، ستجد ضمنه الملف ocean.txt، والمجلد الجديد students، انتقل إلى المجلد /students، وفق الآتي: $ cd students أنت الآن ضمن المجلد /students، فلنفترض أنك تحتاج لاستخدام الملف sammy.txt على أنه قالب يسهل عليك إنشاء ملفات بقية الطلاب، فأول ما يتبادر إلى ذهنك هو نسخ الملف sammy.txt إلى عدة نسخ ثم التعديل عليها حسب بيانات كل طالب، تجري عملية النسخ باستخدام الأمرcp وهو اختصار لكلمة copy. وعلى غرار mv فإنه يأخذ وسيطين، الوسيط الأول اسم الملف الأصلي أي المطلوب نسخه، والوسيط الثاني اسم الملف الجديد، انظر التعليمة التالية لإنشاء نسخة من ملف الطالب سامي sammy.txt وتسميتها باسم جديد لتمثل طالب آخر أليكس alex.txt مثلًا: $ cp sammy.txt alex.txt افتح الآن الملف alex.txt باستخدام محرر النصوص، ستجد محتواه مطابق تمامًا لمحتوى الملف sammy.txt، عدّل البيانات التي تحتاج لتعديلها، ليصبح مثلًا كما هو مبين أدناه، ونود لفت انتباهك إلى قدرتك على حذف سطر كامل من الملف بخطوة واحدة، عبر الضغط على CTRL + K معًا: Alex the Leafy Seadragon Hello, I am Alex. I am studying oceanography. Nice to meet you! أغلق الملف الجديد الآن واحفظ التغييرات، بالضغط على CTRL + x ثم Y ثم ENTER. حاول إنشاء ملفات نصية أخرى للتدريب، وعندما تصبح جاهزًا انتقل معنا للقسم التالي لنتعرف على أوامر ومميزات جديدة. الإكمال التلقائي وسجل الأوامر المحفوظة توفر معظم واجهات سطر الأوامر مميزات، مثل: الإكمال التلقائي للأوامر، وإعادة استخدام الأوامر السابقة، تسهل عمل المستخدمين عليها، وتسرّع وتيرته. جرب بنفسك، حاول مثلًا كتابة الأمر cat مع الأحرف الأولى من أحد الملفات التي عملنا عليها نحو cat sa قبل أن تكمل اسم الملف sammy.txt بالكامل، اضغط على زر TAB من لوحة المفاتيح ولاحظ ميزة الإكمال التلقائي، فالطرفية ستكمل اسم الملف تلقائيًا وتظهره لك دون أن تكمل أنت كتابته، كما هو مبين أدناه: $ cat sammy.txt والآن إذا ضغطت ENTER ستنفذ التعليمة بطريقة طبيعية، ويظهر محتوى الملف النصي. حاول بعدها الضغط على السهم الأعلى UP من أسهم التمرير في لوحة المفاتيح، وشاهد ما سيظهر، ستجد أن الطرفية تعرض عليك آخر أمر نفذته، اضغط السهم UP بوتيرة أسرع وسترى الأوامر التي نفذتها مؤخرًا، يمكنك بذلك الوصول إلى الأمر الذي تريد إعادة تنفيذه دون كتابته من جديد. تستطيع أيضًا استعراض كافة الأوامر المستخدمة سابقًا في هذه الجلسة، باستخدام الأمر history: $ history ستحصل في الخرج على سجل يتضمن جميع الأوامر المحفوظة، مثل التالي: 1 pwd 2 mkdir files 3 ls 4 ls -l ... تفيدك هذه الاختصارات في رفع إنتاجيتك عند العمل مع واجهة سطر الأوامر. العمل مع الملفات عبر الويب تتيح لك واجهة سطر الأوامر، الموجودة على حاسوب متصل بالإنترنت، الوصول إلى جميع الموارد المتاحة على الويب، يمكنك مثلًا: الاتصال بخادم سحابي عن طريقها إذا كنت تعرف بيانات الاعتماد الخاصة به، أو إدارة البنية التحتية السحابية لشركتك، أو القيام بأنشطتك الخاصة بتطوير تطبيقات الويب أو غير ذلك. وما دام أنك تعلمت طريقة التعامل مع الملفات النصية، فستكون خطوتنا الأولى التعرف على كيفية سحب ملف نصي من الويب ليكون متاحًا على حاسوبك الشخصي. انتقل للمجلد /files: $ cd /home/sammy/files سنستخدم الآن الأمر crul، والذي يرمز إلى client URL، لجلب البيانات من الويب إلى طرفيتك. رفعنا -يقول كاتب المقال- ملف نصيًا لغرض التجربة يتضمن مقطعًا قصيرًا من رواية على خادم سحابي، وسنستخدم عنوان URL الخاص به في تعليمة crul التالية: $ curl https://assets.digitalocean.com/articles/command-line-intro/verne_twenty-thousand-leagues.txt ما إن تضغط على زر ENTER ستتلقى محتوى الملف النصي في واجهة الطرفية، وهو في حالتنا مقطعًا من رواية، وفق التالي: "You like the sea, Captain?" "Yes; I love it! The sea is everything. It covers seven tenths of the terrestrial globe. ... "Captain Nemo," said I to my host, who had just thrown himself on one of the divans, "this is a library which would do honor to more than one of the continental palaces, and I am absolutely astounded when I consider that it can follow you to the bottom of the seas." ظهر محتوى الملف النصي على واجهتك الطرفية، لكنه في الواقع لم يخزن على حاسوبك المحلي حتى الآن، يمكنك التأكد من ذلك بواسطة الأمر ls، فما طريقة تخزينه إذًا؟ الحل هو استخدام الخيار أو الراية O- مع الأمر curl، وعندها سيُخَزَّن الملف محليًا باسم الملف البعيد نفسه. $ curl -O https://assets.digitalocean.com/articles/command-line-intro/verne_twenty-thousand-leagues.txt وستحصل على رسالة في واجهة الطرفية، تخبرك بنجاح عملية التنزيل، تشبه صيغتها التالي: % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 2671 100 2671 0 0 68487 0 --:--:-- --:--:-- --:--:-- 68487 يمكنك أيضًا تنزيل الملف من الويب وحفظه على الحاسب باسم مختلف عن اسمه الأصلي، بالاسم jules.txt مثلًا، وذلك بتمرير الاسم الجديد بصفة وسيط إضافي للأمر crul مع الخيار o-، وفق التالي: $ curl -o jules.txt https://assets.digitalocean.com/articles/command-line-intro/verne_twenty-thousand-leagues.txt تستطيع الآن عرض الملف بواسطة cat، أو تحرير عبر nano. يفيدك أيضًا مراجعة المقال دليل استخدام سطر الأوامر في عملية تطوير الويب من طرف العميل لمزيد من الأمثلة من المعلومات المفيدة. يتناول القسم الأخير من المقال طرق حذف الملفات وإنهاء تنفيذ الأوامر. حذف الملفات وإنهاء تنفيذ الأوامر ستتعرض بالتأكيد لحالات تحتاج فيها لحذف ملف أو مجلد، أو لإنهاء تنفيذ أمر معين، أو الخروج من تطبيق تستخدمه، سنتعلم ذلك. بالعودة إلى مثال طلاب الأكاديمية، فلنفترض أنك تحتاج لحذف ملفات بعض الطلاب لأنهم تخرجوا مثلًا. تأكد أولًا من وجودك في المجلد /students، يمكنك تنفيذ الأمر التالي للانتقال إليه: $ cd /home/sammy/files/students نفذ الآن الأمر ls لاستعراض محتويات المجلد، وبما أننا أنشأنا عددًا من الملفات مسبقًا، فستحصل على الخرج التالي: alex.txt drew.txt jamie.txt jesse.txt sammy.txt taylor.txt يمكنك حذف الملفات المفردة من مجلد الطلاب /students باستخدام الأمر rm. لنحذف مثلًا الملف sammy.txt وفق الآتي: $ rm sammy.txt استعرض مجددًا محتويات المجلد باستخدام ls، ولاحظ عدم وجود الملف sammy.txt بينها: alex.txt drew.txt jamie.txt jesse.txt taylor.txt إذا رغبت بحذف المجلد /students كاملًا مع كافة محتوياته، فستحتاج للأمر rmdir الذي يختصر العبارة remove directory. انتقل في البداية إلى المجلد الأب files ونفذ أمر حذف المجلد /students من هناك، فلا يمكنك حذف أي مجلد طالما أنك تعمل ضمنه. $ cd .. نحن الآن في المجلد /home/sammy/files/، وحاول حذف مجلد الطلاب students من هنا باستخدام rmdir، وفق التالي: $ rmdir students وستحصل على رسالة الخطأ التالية في الخرج: rmdir: failed to remove 'students': Directory not empty تفيد الرسالة بعدم إمكانية حذف المجلد لأنه غير فارغ، يمكنك بالتأكيد معالجة الموضوع بحذف الملفات الفردية من المجلد students واحدًا تلو الآخر، ثم حذف المجلد، لكنها ليست بالطريقة الأمثل وخاصة إذا كان عدد الملفات كبير. بالمناسبة يمكنك إنشاء مجلد فارغ ومحاولة حذفه باستخدام الأمر rmdir والتأكد من فعاليته مع المجلدات الفارغة. أما حذف المجلدات الممتلئة، فيتطلب استخدام الأمر rm مع الراية أو الخيار r-، ترمز هذه الراية إلى الكلمة الإنكليزية recursion أو العودية التي تصف الدوال أو العمليات الحاسوبية التي تستدعي نفسها ذاتيًا مرارًا وتكرارًا، فبالتالي عند استخدامها مع أمر الحذف سيُحذَف العنصر مع كل محتوياته. اكتب إذًا الأمر التالي، ومرر اسم المجلد students بعد أمر الحذف: $ rm -r students تأكد من صحة حذف المجلد /students مع جميع ملفاته، بتنفيذ الأمر ls. أنهيت أعمالك في الطرفية سواء كنت تعمل على حاسوبك الشخصي أو تتصل بخادم بعيد وتريد الخروج منها؟ اكتب إذًا الأمر exit وفي حال كنت متيقنًا من كل شيء اضغط على زر ENTER، فمجرد مغادرتك لجلسة العمل لن تتمكن من استعادتها مجددًا. $ exit ستحصل على الخرج التالي بعد الضغط على زر إنتر، ليؤكد لك إنتهاء جلسة العمل وخروجك منها. Session ended الخلاصة قدم المقال بداية تعريفية لكل راغب ببدء العمل مع الواجهة الطرفية وسطر الأوامر، سواء لإدارة الحواسيب أو الخوادم السحابية البعيدة. يوجد العديد من المقالات المفيدة في هذا المجال على أكاديمية حسوب، استعرض قسم لينكس وقسم الحوسبة السحابية، واقرأ ما يناسبك منها. ترجمة -وبتصرف- للمقال A Linux Command Line Primer لصاحبته Lisa Tagliaferri. اقرأ أيضًا ما هو سطر الأوامر؟ مرجع إلى أشهر أوامر لينكس التعامل مع واجهة سطر الأوامر (CLI) في PHP إعداد البيئة وواجهة سطر الأوامر في بايثون