PyDA Course
مبتدئ موجّه ⚡ +100 XP +15 نقطة لكل خطوة

اضبط نموذجًا لغويًا صغيرًا دقيقًا باستخدام Unsloth

Fine-tuningLLMs

في مسار Python 101 الصعب، بنيتَ نموذجًا لغويًا صغيرًا جدًا من الصفر تمامًا ، عدّ الكلمات، وجداول احتمالات الثنائيات (bigrams)، وأخذ العينات الموزون. يواصل هذا المشروع هذا الخيط بالضبط: بدلًا من بناء رياضيات نموذج لغوي من لا شيء، ستأخذ نموذجًا حقيقيًا مُدرَّبًا مسبقًا ومفتوح المصدر وتخصصه لمهمة من اختيارك عبر الضبط الدقيق (fine-tuning) ، تعديل أوزانه الحالية بكمية صغيرة من بياناتك الخاصة، باستخدام Unsloth، وهي مكتبة صُممت خصيصًا لجعل هذا سريعًا و(الأهم) مجانيًا.

هذا اختياري وغير مُقيَّم ، خيار جيد بمجرد إنهاء مسار Python 101 الصعب (النموذج اللغوي المبني من الصفر يمنحك الحدس الذي يبني عليه هذا المشروع). راجع مشاريع من العالم الحقيقي للاطلاع على القائمة الكاملة، التي تنمو باستمرار، بما في ذلك مشروع وكيل الذكاء الاصطناعي.

فرق صادق واحد عن مشروع وكيل الذكاء الاصطناعي

يعمل مشروع وكيل الذكاء الاصطناعي بالكامل على جهازك الخاص. لا يمكن لهذا المشروع أن يفعل ذلك بالكامل ، ضبط نموذج لغوي دقيقًا، حتى لو كان صغيرًا، يحتاج إلى وحدة معالجة رسومية (GPU)، ومعظم أجهزة الكمبيوتر المحمولة الشخصية لا تمتلك واحدة مناسبة لهذه المهمة. لذلك يقسّم هذا المشروع العمل: إعداد المشروع، وتحضير البيانات، وتشغيل نموذجك المكتمل يحدث محليًا باستخدام uv، تمامًا كما في مشروع وكيل الذكاء الاصطناعي؛ أما خطوة الضبط الدقيق نفسها فتعمل على وحدة معالجة رسومية مستضافة مجانًا (Google Colab أو Kaggle) بدلًا من ذلك. هذا ليس اختصارًا ، إنها الطريقة الصادقة والمعيارية للقيام بذلك دون إنفاق المال.

🎯 ما ستفعله

  1. تثبيت uv وإعداد مشروع محلي ، نفس الخطوة الأولى في كل مشروع.
  2. تحضير مجموعة بيانات صغيرة من الأمثلة تُظهر للنموذج السلوك الذي تريده أن يتعلمه.
  3. الحصول على وصول مجاني لوحدة معالجة رسومية عبر Google Colab أو Kaggle، واستخدام Unsloth لضبط نموذج مفتوح صغير (حوالي مليار معامل) دقيقًا باستخدام LoRA على مجموعة بياناتك.
  4. تنزيل النتيجة ، ملف “محوّل” (adapter) صغير، وليس نموذجًا جديدًا بالكامل ، وتشغيله محليًا لرؤية نموذجك المضبوط دقيقًا وهو يعمل.

أين تُشغّل هذا

محليًا باستخدام uv هو المسار الذي تتبعه خطوات هذا الدرس، والخيار الموصى به لخطوات الإعداد وحضّر البيانات والاستدلال المحلي (الخطوتان 1 و3) ، يشرح قسم الإعداد أدناه كيفية تثبيته. أما الخطوة 2، وهي الضبط الدقيق الفعلي، فتحتاج إلى وحدة معالجة رسومية وتعمل على دفتر Colab/Kaggle الرسمي الخاص بـ Unsloth في كل الأحوال (انظر الخطوة 2 أدناه)، لأن تلك الخطوة لا يمكن أن تحدث فعلًا على معظم أجهزة الكمبيوتر المحمولة.

إذا كنت تفضل تجربة خطوات حضّر البيانات المحلية والاستدلال في دفتر مستضاف بدلًا من استخدام uv، فهناك دفتر مصاحب لذلك بالضبط:

Open In Colab Open In Kaggle

تغطي هذه الشارة الخطوات المحلية فقط (حضّر البيانات والاستدلال) ، أما خطوة الضبط الدقيق نفسها فما تزال تستخدم دفتر Unsloth الرسمي الخاص به، المُشار إليه بشكل منفصل في الخطوة 2.

الإعداد

uv أداة واحدة تحل محل السلسلة المعتادة “ثبّت Python، ثم ثبّت pip، ثم ثبّت أداة بيئة افتراضية، ثم ثبّت الحزم” ، يمكنها تثبيت وإدارة إصدارات Python بنفسها، إلى جانب تبعيات مشروعك.

macOS / Linux (الطرفية):

curl -LsSf https://astral.sh/uv/install.sh | sh

Windows (PowerShell):

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

أغلق طرفيتك وأعد فتحها، ثم تأكد من التثبيت:

uv --version

ثم أعدّ مشروعًا محليًا لخطوتَي حضّر البيانات والاستدلال (الأجزاء التي لا تحتاج إلى وحدة معالجة رسومية):

uv init finetune-llm
cd finetune-llm
uv add datasets huggingface_hub

الخطوة 1: حضّر مجموعة بيانات صغيرة

يُعلّم الضبط الدقيق النموذج سلوكًا محددًا، وليس حقائق جديدة من الصفر ، يعمل بشكل أفضل مع مجموعة أمثلة صغيرة ومركّزة وجيدة التنسيق، وليس كومة ضخمة من النص الخام. تنسيق شائع هو قائمة من أزواج تعليمات/استجابة. اختر مهمة ضيقة وشخصية ، بعض الأفكار: الإجابة على الأسئلة بنبرة أو شخصية محددة، اتباع تنسيق إخراج ثابت (مثلًا، الإجابة دائمًا بصيغة JSON صالحة)، أو تلخيص النص بالطريقة التي تفعلها أنت شخصيًا.

1.1 اكتب أمثلتك وابنِ الملف

👟 تلميح البداية : ابدأ من المثالين أدناه، وحافظ على الشكل الدقيق {"instruction": ..., "response": ...} لكل إدخال، ووسّع القائمة إلى 30-50 قبل الانتقال ، كائن JSON واحد لكل سطر (.jsonl)، وليس مصفوفة JSON واحدة:

# build_dataset.py
import json

examples = [
    {
        "instruction": "Summarize this course in one sentence.",
        "response": "A free, browser-based course teaching Python and pandas from first principles through a full data-analysis project.",
    },
    {
        "instruction": "Explain what a variable is, briefly.",
        "response": "A variable is a name that points to a value stored in memory, so you can refer to that value again by name instead of retyping it.",
    },
    # Add at least 30-50 more examples for the model to actually pick up a
    # pattern — a handful of examples is enough to see this code run, but not
    # enough to see a real behavior change once fine-tuned.
]

with open("dataset.jsonl", "w") as f:
    for example in examples:
        f.write(json.dumps(example) + "\n")

print(f"Wrote {len(examples)} examples to dataset.jsonl")
uv run python build_dataset.py

🎯 الناتج المتوقع : Wrote N examples to dataset.jsonl حيث يطابق N طول قائمتك، وفتح dataset.jsonl في محرر يُظهر كائن JSON صالحًا واحدًا لكل سطر ، لا مشاكل في الفواصل اللاحقة، لأن كل سطر يُكتب بشكل مستقل.

🩹 إذا لم يعمل : إذا كان dataset.jsonl يحتوي أسطرًا أقل من الأمثلة التي كتبتها، تحقق من وجود مفتاح مكرر ضال في أحد قواميسك (يحتفظ Python بصمت بالقيمة الأخيرة فقط للمفتاح المتكرر، وهذا لن يتعطل لكنه سيبدو خاطئًا). أبقِ القائمة قصيرة (2-3 أمثلة) فقط للتأكد من أن النص يعمل، ثم وسّعها إلى 30-50 قبل الخطوة 2 ، مجموعة بيانات من مثالين تمر عبر دفتر الضبط الدقيق بشكل جيد لكنها لن تُنتج تغيير سلوك مرئيًا بعد ذلك.

1.2 تحقق من مجموعة البيانات

✅ قائمة التحقق

  • ✅ ملف dataset.jsonl موجود مع كائن JSON واحد لكل سطر، كلٌّ يحمل بالضبط instruction وresponse.
  • ✅ العدد المطبوع بواسطة النص يطابق عدد الأمثلة التي كتبتها فعلًا.
  • ✅ يحافظ كل إدخال على نفس الشكل {"instruction": ..., "response": ...} ، لا مفاتيح أو تنسيقات غير متسقة بين الإدخالات.

🤔 سؤال (أسئلة) سقراطي(ة)

يحذّر التلميح أعلاه من أن “50 مثالًا مكتوبًا بعناية ومتسقًا تُعلّم النموذج سلوكًا بشكل أكثر موثوقية بكثير من 500 مثال مهمَل أو غير متسق.” إذا أجاب اثنان من أمثلتك على نفس نوع السؤال بأسلوبين متعارضين، فما الذي يتعين على النموذج تعلمه ، ولماذا قد تؤدي إضافة المزيد من الأمثلة المتعارضة إلى تفاقم المشكلة بدلًا من تحسينها؟

الخطوة 2: اضبط دقيقًا باستخدام Unsloth على وحدة معالجة رسومية مجانية

هذه هي الخطوة التي تحتاج إلى وحدة معالجة رسومية. يوفر Unsloth دفاتر ملاحظات جاهزة للتشغيل مصممة خصيصًا لمستويات وحدة المعالجة الرسومية المجانية في Google Colab وKaggle ، لن تُثبّت أي شيء محليًا لهذا الجزء.

2.1 تأكد أولًا من أن الدفتر القياسي يعمل

👟 تلميح البداية : شغّل خلايا الدفتر من الأعلى إلى الأسفل بدون تعديل مرة واحدة، على بياناته النموذجية الخاصة، قبل أن تلمس أي شيء ، تأكيد أن الدفتر القياسي يدرّب ويُنتج محوّلًا أولًا يعزل “هل عمل Unsloth على الإطلاق” عن “هل نجح تبديل مجموعة بياناتي”، بنفس طريقة الاختبار بمدخلات صحيحة معروفة في أي مكان آخر في هذه الدورة.

  1. اذهب إلى صفحة دفاتر ملاحظات Unsloth وافتح أحد دفاتر Colab المناسبة للمبتدئين لنموذج صغير (حوالي مليار معامل ، صغير بما يكفي للضبط الدقيق السريع وللتنزيل والتشغيل فعليًا بعد ذلك). يُعد نموذج مفتوح بمليار معامل، مثل إصدار صغير من Llama أو Qwen، نقطة بداية معقولة ومدعومة جيدًا؛ تحقق من قائمة دفاتر Unsloth لمعرفة أي نموذج صغير لديه حاليًا قالب يعمل، حيث يتغير النموذج الأفضل دعمًا بمرور الوقت.
  2. شغّل خلايا الدفتر بالترتيب، من البداية إلى النهاية، دون تغيير أي شيء.

🎯 الناتج المتوقع : تطبع خلية التدريب في الدفتر رقم خسارة تدريب يتناقص بوضوح (لن يصل إلى الصفر، ولا ينبغي)، والانتهاء يُنتج مجلد محوّل مُنزَّل حجمه عشرات الميغابايتات، وليس غيغابايتات.

🩹 إذا لم يعمل : إذا بقيت خسارة التدريب ثابتة بدلًا من التناقص، فالدفتر لا يتدرب على الإطلاق ، أعد تشغيل خلايا تحميل البيانات والتدريب وراقب السجلات بدلًا من افتراض أن خلية طويلة مشغولة. إذا انقطع اتصال Colab في منتصف التشغيل، فهو دائمًا تقريبًا مهلة خمول من المستوى المجاني ، أعد الاتصال وأعد التشغيل من الأعلى؛ لا يوجد حفظ جزئي للاستئناف منه في دفاتر المبتدئين.

2.2 استبدل مجموعة بياناتك الخاصة

👟 تلميح البداية : الآن اربط عمل خطوتك 1: ارفع ملف dataset.jsonl الذي بنيته، ووجّه خلية تحميل البيانات في الدفتر إليه بدلًا من مجموعة البيانات النموذجية ، ثم شغّل الخلايا بالترتيب مرة أخرى.

# In the notebook's data-loading cell, point it at your dataset.jsonl
# instead of the notebook's bundled example dataset. Exactly which line
# changes depends on the notebook you opened — look for the cell that
# reads a .jsonl/.json dataset and give it your file's name/path.

🎯 الناتج المتوقع : تُظهر خلية التدريب مجددًا خسارة متناقصة، ويمكنك التأكد من معاينة البيانات المحمّلة في الدفتر أنها تقرأ الآن إدخالات مجموعة بياناتك dataset.jsonl (أزواج تعليماتك/استجاباتك)، وليس مجموعة العرض التوضيحي القياسية.

🩹 إذا لم يعمل : أكثر الفشل الصامت شيوعًا هو أن خلية تحميل البيانات ما تزال تقرأ مجموعة البيانات النموذجية الأصلية للدفتر ، تبدو هذه الخطوة وكأنها “نجحت” دون التدريب على بياناتك على الإطلاق. تحقق من أن معاينة البيانات المحمّلة تحتوي بوضوح على أمثلتك الدقيقة قبل الخلية التدريبية الطويلة. تتبع خسارة التدريب الثابتة عادةً إلى نفس سبب مجموعة البيانات الخاطئة.

تحقق من التوثيق الحالي قبل أن تبدأ

النموذج المحدد، والدفتر المحدد، وواجهة برمجة تطبيقات Unsloth نفسها، كلها تتغير بسرعة ، أسرع من معظم البرمجيات، لأن هذه أداة تُطوَّر بنشاط وقريبة من مجال البحث. قبل تشغيل أي شيء، افتح توثيق Unsloth الحالي واستخدم الدفتر والنموذج اللذين يوصي بهما حاليًا للمبتدئين، بدلًا من افتراض أن تفاصيل العام الماضي لا تزال سارية.

تستخدم خطوة الضبط الدقيق الأساسية LoRA (Low-Rank Adaptation): بدلًا من تحديث مليارات معاملات النموذج (بطيء، يحتاج ذاكرة كبيرة)، يُجمّد LoRA النموذج الأصلي ويدرّب زوجًا من المصفوفات المنخفضة الرتبة تُضاف فوقه ، رياضيًا، إذا كانت مصفوفة الأوزان الأصلية هي WW، يتعلم LoRA تحديثًا منخفض الرتبة ΔW=BA\Delta W = BA (حيث BB و AA مصفوفتان أصغر بكثير) ويستخدم W+ΔWW + \Delta W في وقت الاستدلال. هذه هي نفس فكرة تقريب مصفوفة كبيرة بمصفوفة أقل بُعدًا ، مفهوم من الجبر الخطي لديك خلفية فيه بالفعل ، مُطبَّق لجعل الضبط الدقيق رخيصًا بما يكفي للعمل على وحدة معالجة رسومية مجانية. بمجرد انتهاء التدريب، يحفظ الدفتر نتيجتك كـ محوّل (adapter) صغير ، فقط مصفوفتَي AA و BB، عادةً عشرات الميغابايتات، وليس نسخة من عدة غيغابايتات من النموذج بأكمله. نزّل مجلد المحوّل هذا إلى جهازك.

2.3 تحقق من الضبط الدقيق

✅ قائمة التحقق

  • ✅ عملت خلية التدريب على dataset.jsonl الخاص بك (مؤكَّد عبر معاينة البيانات المحمّلة)، وليس على بيانات العرض التوضيحي للدفتر.
  • ✅ تناقصت خسارة التدريب بوضوح أثناء التشغيل.
  • ✅ نزّلت مجلد المحوّل الناتج إلى جهازك ، عشرات الميغابايتات، وليس غيغابايتات.
  • ✅ تعرف النموذج الأساسي الدقيق الذي ضبط هذا الدفتر دقيقًا، لأن الخطوة 3 تحتاج إلى ذلك المعرف الدقيق.

🤔 سؤال (أسئلة) سقراطي(ة)

لماذا ملف المحوّل عشرات الميغابايتات فقط بينما النموذج الكامل الذي يضبطه غيغابايتات؟ إذا خزّن LoRA مصفوفتَي الرتبة المنخفضة AA و BB فقط بدلًا من إعادة حفظ النموذج الأساسي كاملاً، فماذا سيحدث لو حاولت استخدام ذلك المحوّل المُنزَّل على نموذج مختلف عن النموذج الذي دُرِّب ضده؟

الخطوة 3: شغّل نموذجك المضبوط دقيقًا محليًا

ارجع إلى جهازك الخاص، وحمّل النموذج الأساسي بالإضافة إلى المحوّل الذي نزّلته وجرّبه.

3.1 اكتب وشغّل نص الاستدلال

👟 تلميح البداية : حمّل النموذج الأساسي والرمز (tokenizer) أولًا باستخدام AutoModelForCausalLM/AutoTokenizer، تمامًا كما هو مسمّى في دفتر الخطوة 2، ثم غلّف النموذج الأساسي بـ PeftModel.from_pretrained(base_model, adapter_path) ، هذا هو السطر الوحيد الذي يطبّق فعليًا ضبطك الدقيق في الأعلى:

uv add transformers peft torch --extra-index-url https://download.pytorch.org/whl/cpu
# infer.py
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel

base_model_name = "unsloth/<the-base-model-you-fine-tuned>"  # match Step 2's notebook
adapter_path = "./my-adapter"  # the folder you downloaded from Colab

tokenizer = AutoTokenizer.from_pretrained(base_model_name)
base_model = AutoModelForCausalLM.from_pretrained(base_model_name)
model = PeftModel.from_pretrained(base_model, adapter_path)

prompt = "Summarize this course in one sentence."
inputs = tokenizer(prompt, return_tensors="pt")
output = model.generate(**inputs, max_new_tokens=80)
print(tokenizer.decode(output[0], skip_special_tokens=True))
uv run python infer.py

تشغيل نموذج بحجم مليار معامل تقريبًا على المعالج (CPU) بطيء (توقع ثوانٍ حقيقية، وليس أجزاء من الثانية، لكل استجابة) لكنه يعمل ، هذا جهازك الخاص يُشغّل فعليًا نموذجًا لغويًا مضبوطًا دقيقًا، بدون مفتاح API، وبدون الحاجة إلى اتصال بالإنترنت بمجرد تنزيل ملفات النموذج.

🎯 الناتج المتوقع : استجابة مولّدة تميل بشكل ملحوظ نحو أسلوب أو تنسيق أمثلتك التدريبية ، ليست صياغة مطابقة، لكنها تحول مرئي عن الشكل الذي سيجيب به النموذج الأساسي غير المضبوط على نفس المطالبة.

🩹 إذا لم يعمل : يشير خطأ size mismatch/config عند تحميل المحوّل دائمًا تقريبًا إلى أن base_model_name لا يطابق بدقة النموذج الأساسي الذي ضبطه دفتر الخطوة 2 فعلًا ، تحقق من خلية تحميل النموذج في الدفتر نفسها بحثًا عن المعرف الدقيق وانسخه حرفيًا. إذا بدت الاستجابة لا يمكن تمييزها عن نموذج غير مدرب، أعد التحقق من أن adapter_path يشير إلى المجلد الذي نزّلته (وليس عنصرًا نائبًا فارغًا) وأن الخطوة 2 درّبت فعلًا على بياناتك، وليس على مجموعة العرض التوضيحي للدفتر.

3.2 تحقق من الناتج المضبوط دقيقًا

✅ قائمة التحقق

  • ✅ يكتمل uv run python infer.py ويطبع استجابة، وليس تتبّع خطأ (traceback).
  • ✅ تُظهر الاستجابة تحولًا مرئيًا نحو أسلوب أو تنسيق أمثلتك التدريبية، مقارنة بسلوك النموذج الأساسي الافتراضي.
  • ✅ يطابق معرف النموذج الأساسي في infer.py النموذج الذي ضبطه دفتر الخطوة 2 دقيقًا، بالضبط.

🤔 سؤال (أسئلة) سقراطي(ة)

يجب أن تُظهر الاستجابة السلوك الذي درّبت عليه، لكن لا تُعيد إنتاج أي مثال حرفيًا. قارن ناتجك ببياناتك التدريبية: هل يحفظ النموذج ويردد مثالًا مخزّنًا، أم يعمّم نمط التعليمات/الاستجابة الأساسي؟ كيف تميّز بين الاثنين، وأيهما تفضل أن تمتلك؟

⚠️ المآزق الشائعة

  • أمثلة قليلة جدًا أو غير متسقة جدًا. مجموعة بيانات من 5 أمثلة، أو 50 مثالًا تُجيب كل منها على أسئلة متشابهة بشكل مختلف، لا تُعطي النموذج شيئًا موثوقًا للتعميم منه ، ستحصل على شيء قريب من النموذج الأساسي غير المضبوط.
  • نسيان استبدال مجموعة بياناتك الخاصة فعليًا. من السهل تشغيل دفتر من البداية إلى النهاية على مجموعة بياناته النموذجية واستنتاج “لقد نجح الأمر” دون التدريب فعليًا على بياناتك الخاصة ، تأكد دائمًا أن خلية تحميل البيانات تقرأ dataset.jsonl، وليس ملف العرض التوضيحي الأصلي للدفتر.
  • محاولة الضبط الدقيق محليًا على وحدة معالجة رسومية لجهاز محمول (أو بدون وحدة معالجة رسومية) بدلًا من استخدام الوحدة المستضافة مجانًا. حتى نموذج “صغير” بمليار معامل يحتاج ذاكرة وحدة معالجة رسومية حقيقية للتدريب بكفاءة ، يوجد المستوى المجاني في Colab/Kaggle خصيصًا حتى لا تحتاج إلى واحدة خاصة بك.
  • الخلط بين النموذج الأساسي بين الخطوتين 3 و4. المحوّل الذي نزّلته منطقي فقط عند تحميله فوق النموذج الأساسي بالضبط الذي دُرِّب عليه ، تحميله على نموذج مختلف (حتى لو كان بنفس الاسم تقريبًا) سيؤدي إما إلى خطأ أو إنتاج كلام لا معنى له بصمت.

ما بنيته للتو

لم تُدرِّب نموذجًا لغويًا من الصفر ، هذا ما مرّرك به مسار Python 101 الصعب بالفعل، بالطريقة الصادقة والمبنية على المبادئ الأولى. هنا، أخذتَ نموذجًا حقيقيًا مُدرَّبًا مسبقًا وخصصته: نفس الفكرة الأساسية (نموذج يُشكّل سلوكه بواسطة البيانات) ولكن بمقياس ومستوى قدرة لا يمكن لأي شيء مبني من الصفر في متصفح أن يصل إليه، باستخدام تقنية (LoRA) صُممت خصيصًا لجعل ذلك ميسور التكلفة على أجهزة مجانية.

إلى أين تذهب من هنا

  • جرّب مهمة مختلفة حقًا لضبطك الدقيق التالي ، تنسيق إخراج ثابت، أو نبرة محددة، أو مجال ضيق (مثلًا، الإجابة فقط على أسئلة حول موضوع واحد) يميل إلى إظهار فروقات “قبل/بعد” أوضح وأكثر إقناعًا من تغيير واسع وعام الغرض.
  • اقرأ توثيق Unsloth نفسه حول التكميم (quantization) ، تستخدم دفاتر المستوى المجاني بالفعل تكميمًا بـ4 بتات لجعل التدريب يناسب ذاكرة وحدة المعالجة الرسومية المحدودة؛ يستحق فهم ما يضحّي به ذلك (كمية صغيرة من الدقة) مقابل ما يوفره (استيعاب نموذج لا يناسب الذاكرة بخلاف ذلك) قبل الاعتماد عليه لأي شيء يتجاوز مشروع دورة دراسية.
  • قارن هذا بـمشروع وكيل الذكاء الاصطناعي: يغيّر ذلك المشروع سلوك النموذج بمنحه أدوات وتعليمات وقت الطلب (بدون أي تدريب)؛ بينما يغيّر هذا المشروع أوزان النموذج الفعلية مسبقًا. كلاهما منهجان حقيقيان وحاليان للبناء بنماذج لغوية ، معرفة متى تلجأ إلى أحدهما دون الآخر أمر مفيد حقًا أن تكون قد شعرت به مباشرة، لا أن تكون قد قرأت عنه فقط.

شارك مشروعك مع الصف

بنيتَ شيئًا تفخر به؟ examples/student-projects/ معرض لمشاريع ختامية قدّمها طلاب آخرون ، وملف README الخاص به يحتوي على دليل كامل وسهل للمبتدئين لإضافة مشروعك عبر طلب سحب (pull request)، حتى لو لم تستخدم git من قبل: عمل fork للمستودع، وإنشاء فرع، وتثبيت (commit) ملفاتك، وفتح طلب السحب، خطوة بخطوة. لا يُفترض وجود أي خبرة سابقة بـ git.

مرحبًا بك في كتابة Python خارج المتصفح. 🎓

أكمل كل خطوة ثم حدد المشروع كمكتمل لجمع نقاطه.