Transformers documentation

مساهمة نموذج قديم

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

مساهمة نموذج قديم

جرّب إضافة نماذج جديدة أولاً باستخدام نهج أكثر تكاملاً. هذا يجعل المساهمة بنموذج في Transformers أسهل بكثير!

تُسهم العديد من النماذج في Transformers من قِبل المطورين والباحثين. وبصفتنا مشروعًا مفتوح المصدر في المقام الأول، فإننا نستثمر في تمكين المجتمع من إضافة المزيد من النماذج بشكلٍ نشط ومستقل.

عند إضافة نموذج إلى Transformers، ستتعلم:

  • المزيد حول أفضل ممارسات المصادر المفتوحة
  • حول بنية النموذج
  • حول مبادئ تصميم Transformers
  • كيفية اختبار النماذج الكبيرة بكفاءة
  • كيفية استخدام أدوات بايثون مثل Black و Ruff لإنشاء أكواد نظيفة وسهلة القراءة

إنه عملية صعبة لكنها مثمرة.

ستأخذك هذه الدليل خطوة بخطوة في إضافة نموذج PyTorch جديد باسم BrandNewLlama إلى مكتبة Transformers. قبل أن تبدأ، من الجيد أن تتعرّف على المكتبة بشكل جيد.

نظرة عامة على Transformers

تُعد مكتبة Transformers مكتبة ذات توجهات واضحة، تتمتع بفلسفة وخيارات تصميم فريدة من نوعها. تساعدنا هذه الخيارات على توسيع نطاق Transformers والحفاظ عليها بشكل مستدام.

تعرف على المزيد حول مبادئ التصميم لدينا في وثيقة الفلسفة.

بعض هذه الخيارات التصميمية هي:

  • التركيب > الإفراط في التجريد
  • الكود المكرر ليس سيئًا دائمًا إذا كان يحسّن بشكل كبير من قابلية القراءة وسهولة الوصول
  • ملفات النموذج مكتفية ذاتياً، وكل أكواد النموذج اللازمة موجودة في ملف modeling_mymodel.py

تُعدّ هذه الاختيارات التصميمية مهمة لكل من يتفاعل مع النموذج. فهي أسهل في القراءة والفهم والتعديل.

تصف هذه الأقسام كيفية تفاعل فئات النموذج والإعدادات معًا، وأسلوب كتابة الكود في مكتبة Transformers.

النموذج والتكوين

تورث جميع نماذج Transformers من الفئة الأساسية PreTrainedModel وفئة PreTrainedConfig. وتُعدّ التهيئة هي المخطط الأساسي للنماذج.

لا يتجاوز مستوى التجريد في أي نموذج مستويين اثنين للحفاظ على قابلية قراءة الكود. النموذج الموضح هنا، BrandNewLlama، يرث من BrandNewLlamaPreTrainedModel و PreTrainedModel. من المهم أن يعتمد النموذج الجديد على PreTrainedModel فقط حتى يتمكن من استخدام طريقتي from_pretrained() و save_pretrained().

Other important functions like the forward method are defined in the modeling.py file.

يجب أن تستدعي رؤوس النماذج المحددة (مثل تصنيف التسلسل أو نمذجة اللغة) النموذج الأساسي في مسار الأمامي بدلاً من الوراثة منه، للحفاظ على مستوى التجريد منخفضاً.

تتطلب النماذج الجديدة إعداداً، على سبيل المثال BrandNewLlamaConfig، يُخزَّن كخاصية من خصائص PreTrainedModel.

model = BrandNewLlamaModel.from_pretrained("username/brand_new_llama")
model.config

يوفر PreTrainedConfig طريقتي from_pretrained() وsave_pretrained().

عند استخدامك PreTrainedModel.save_pretrained()، يتم استدعاء PreTrainedConfig.save_pretrained() تلقائيًا بحيث يتم حفظ النموذج والإعداد معًا.

يتم حفظ النموذج في ملف model.safetensors وحفظ الإعدادات في ملف config.json.

أسلوب الكتابة

يفضّل Transformers كودًا نظيفًا وسهل القراءة على أسلوب برمجي أكثر تجريدًا. وتشمل بعض اختيارات أسلوب الكود ما يلي:

  • يجب أن يكون الكود قابلاً للوصول للمستخدمين غير الناطقين بالإنجليزية. اختر أسماء متغيرات وصفية وتجنّب الاختصارات. على سبيل المثال، يُفضَّل استخدام “activation” بدلاً من “act”. يُنصح بشدة بعدم استخدام أسماء متغيرات من حرف واحد ما لم تكن فهارس داخل حلقة for.

  • يُفضَّل الكود الصريح - حتى لو كان أطول - على الكود الأقصر.

  • تجنّب إنشاء فئة فرعية من nn.Sequential. بدلاً من ذلك، أنشئ فئة فرعية من nn.Module حتى يمكن تصحيح الأخطاء في الكود بسرعة باستخدام عبارات الطباعة أو نقاط التوقف.

  • يجب أن تكون توقيعات الدوال مُعلَّمةً بأنواع. وإلا، فاستخدم أسماء متغيرات جيدة حتى تكون أكثر قابلية للفهم.

مشكلة في إضافة نموذج جديد

افتح مشكلة إضافة نموذج جديد لإضافة نموذج محدد.

استخدم علامة New model على GitHub للترشيح لعرض طلبات النماذج الموجودة وإضافتها.

الآن هو وقت مناسب للتعرف على BrandNewLlama. من المفيد قراءة ورقة بحثية حول النموذج لفهم تصميمه التقني وتنفيذه. لا يتعين عليك بالضرورة القلق كثيرًا حول التفاصيل النظرية. وبدلاً من ذلك، ركّز على التفاصيل العملية. استخدم الأسئلة أدناه لتوجيه قراءتك.

  • ما نوع النموذج BrandNewLlama؟ هل هو نموذج مُشفِّر (encoder) أو مُفكِّك (decoder) أو نموذج مُشفِّر-مُفكِّك (encoder-decoder)؟
  • ما هي المهام التي يمكن استخدام BrandNewLlama لأدائها؟
  • ما الذي يميّز BrandNewLlama عن النماذج الأخرى؟
  • ما هي النماذج الأكثر تشابهاً مع BrandNewLlama في مكتبة Transformers؟
  • ما هو مُجزِّئ الرموز (tokenizer) الذي يستخدمه BrandNewLlama؟

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

لكل مساهب أسلوبه الخاص ومسار عمله لإضافة النماذج إلى Transformers. كمثال، اطّلع على كيفية إضافة Gemma.

  • لا تعِد اختراع العجلة! خذ وقتك لاستكشاف النماذج والمقسّمين (tokenizers) الموجودة مسبقًا لترى ما يمكنك نسخه وإعادة استخدامه. Grep و ripgrep أدوات رائعة لهذا الغرض.
  • هذا تحدٍّ هندسي أكثر منه تحدٍّ علمي. ركّز على الجوانب العملية (مثل إعداد بيئة تصحيح أخطاء فعّالة) بدلاً من الجوانب النظرية للنموذج.
  • لا تتردد في طلب المساعدة! نحن هنا لدعمك. 🤗

بيئة التطوير

انقر على زر Fork في مستودع Transformers لإنشاء نسختك الخاصة للعمل عليها. انسخ المستودع إلى القرص المحلي لديك وأضف المستودع الأساسي كمصدر بعيد.

git clone https://github.com/[your Github handle]/transformers.git
cd transformers
git remote add upstream https://github.com/huggingface/transformers.git

أنشئ بيئة افتراضية وأجرِ تثبيتًا قابلًا للتعديل للمكتبة مع تبعيات “dev” أو تبعيات التطوير.

python -m venv .env
source .env/bin/activate
pip install -e ".[dev]"

نظرًا لعدد التبعيات الاختيارية المتزايدة في Transformers، قد يفشل هذا الأمر. في هذه الحالة، قم بتثبيت التبعيات الخاصة بـ “quality”. كما تأكد من أنك قمت بتثبيت إطار عمل للتعلم العميق.

pip install -e ".[quality]"

عد إلى المجلد الأب ثم استنسخ وثبّت مستودع BrandNewLlama الأصلي.

git clone https://github.com/org_that_created_brand_new_llama_org/brand_new_llama.git
cd brand_new_bert
pip install -e .

عد إلى نسخة Transformers الخاصة بك لبدء نقل BrandNewLlama.

cd transformers

تتوفر بيئتان محتملتان للتشخيص لتشغيل النموذج الأصلي، وهي دفتر ملاحظات (Google Colab أو Jupyter) أو سكربت بايثون محلي.

لا ننصح بإعداد بيئة GPU لتشغيل النموذج الأصلي لأن ذلك قد يكون مكلفًا. وبدلاً من ذلك، ابدأ بالعمل في بيئة CPU للتحقق من أن النموذج يعمل في Transformers. وبعد ذلك، يمكنك التحقق منه على GPU.

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

الوجه السلبي هو أنه إذا لم تكن معتادًا عليها، فقد يستغرق الأمر بعض الوقت حتى تعتاد عليها.

إذا كانت بنية النموذج مطابقة لبنية نموذج موجود مسبقًا، فتخطَّ إلى تحويل نقطة التفتيش، إذ يمكنك إعادة استخدام بنية النموذج الموجود.

شغّل الأمر أدناه لبدء استبيان جديد وإكماله ببعض المعلومات الأساسية حول النموذج الجديد. يُسرّع هذا الأمر العملية من خلال توليد بعض أكواد النموذج تلقائيًا، والتي ستحتاج إلى تكييفها.

transformers add-new-model-like

إنشاء طلب سحب

قبل أن تبدأ في تكييف الكود، أنشئ طلب سحب (pull request) لتتبع تقدمك والحصول على ملاحظات من فريق Transformers. اجعل عنوان طلب السحب [WIP] Add BrandNewLlama ليكون من الواضح أن هذا عمل قيد التنفيذ.

أنشئ فرعًا باسمًا وصفيًا من الفرع الرئيسي لديك.

git checkout -b add_brand_new_bert

التزم بالكود، ثم استخرج وأعد الأساس على الفرع الرئيسي.

git add .
git commit
git fetch upstream
git rebase upstream/main

ادفع أي تغييرات إلى فرعك وانقر على Compare & pull request لفتح طلب سحب على GitHub. افتح طلب السحب كـ draft للإشارة إلى أنه قيد العمل.

git push -u origin a-descriptive-name-for-my-changes

قم بتضمين أعضاء فريق Hugging Face المعنيين عن طريق إضافة مقابض GitHub الخاصة بهم في طلب الدمج لطرح الأسئلة، وتقديم الملاحظات والتعليقات، وإجراء المراجعات. وجّه أعضاء الفريق إلى الأجزاء المحددة من الكود التي تريد الإشارة إليها بالنقر على تبويب Files changed، ثم انقر على + الموجود على يسار رقم السطر لإضافة تعليق. عند حل سؤال أو مشكلة، انقر على Resolve للإشارة إلى أن المشكلة قد حُلّت. يحافظ هذا على تنظيم المحادثة ووضوحها.

تذكّر بالالتزام بدفع عملك بشكل دوري، وتحديث عملك مع الفرع الرئيسي الحالي.

git fetch upstream
git merge upstream/main

نقطة التفتيش الأصلية

خذ بعض الوقت للعمل على تنفيذ النموذج الأصلي أولاً لفهم كيفية عمله.

قد يكون ذلك صعبًا إذا كان مستودع النموذج الأصلي يفتقر إلى التوثيق أو إذا كان كود المشروع معقدًا. لكن يجب أن تستخدم هذا كدافع لتنفيذ النموذج في Transformers. فمساهمتك تجعله أكثر سهولة في الوصول إليه وأسهل استخدامًا للجميع!

تعرّف على المستودع الأصلي من خلال القيام بما يلي.

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

النقطة الأخيرة مهمة بشكل خاص لأنك ستحتاج إلى فهم شامل لما يحدث داخل النموذج الأصلي قبل أن تتمكن من إعادة تنفيذه في Transformers. لا تتردد في فتح مشاكل وطلبات سحب في المستودع الأصلي إذا واجهت أي مشكلات.

خطوة أولى جيدة هي تحميل نقطة تحقق مسبقة التدريب صغيرة ومحاولة إعادة إنتاج تمريرة أمامية واحدة باستخدام متجه أمثلة من الأعداد الصحيحة للمدخلات. على سبيل المثال، في الشيفرة الزائفة، قد يبدو الأمر على النحو التالي.

model = BrandNewLlamaModel.load_pretrained_checkpoint("/path/to/checkpoint/")
input_ids = [0, 4, 5, 2, 3, 7, 9]  # vector of input ids
original_output = model.generate(input_ids)

استكشاف الأخطاء وإصلاحها

إذا واجهت مشاكل، فستحتاج إلى اختيار إحدى استراتيجيات التصحيح التالية اعتمادًا على قاعدة أكواد النماذج الأصلية.

المكونات الفرعية
النموذج ومجزّئ النص

تعتمد هذه الاستراتيجية على تقسيم النموذج الأصلي إلى مكونات فرعية أصغر، كما هو الحال عندما يمكن تشغيل الكود بسهولة في الوضع العاجل (eager mode). وعلى الرغم من أن هذا النهج أكثر صعوبة، إلا أن له بعض المزايا.

  1. يكون من الأسهل لاحقًا مقارنة النموذج الأصلي بتنفيذك. يمكنك التحقق تلقائيًا من أن كل مكوّن فردي يطابق المكوّن المقابل له في تنفيذ Transformers. وهذا أفضل من الاعتماد على مقارنة بصرية مبنية على عبارات الطباعة.
  2. من الأسهل نقل المكونات الفردية بدلاً من نقل النموذج بأكمله.
  3. يكون فهم كيفية عمل النموذج أسهل عند تقسيمه إلى أجزاء أصغر.
  4. يكون من الأسهل منع الانحسارات في مرحلة لاحقة عند تغيير الكود بفضل الاختبارات المخصصة لكل مكوّن على حدة.

راجع فحوصات التكامل الخاصة بـ ELECTRA كمثال جيد على كيفية تفكيك النموذج إلى مكونات أصغر.

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

  1. معرفات الإدخال المُمررة إلى النموذج
  2. تضمينات الكلمات
  3. مدخل طبقة Transformer الأولى
  4. مخرجات طبقة Transformer الأولى
  5. مخرجات طبقات Transformer التالية n-1
  6. مخرجات النموذج بأكمله

يجب أن تكون المعرّفات المدخلة مجرد مصفوفة من الأعداد الصحيحة مثل input_ids = [0, 4, 4, 3, 2, 4, 1, 7, 19].

غالبًا ما تتكون مخرجات الطبقات من مصفوفات فاصلة عشرية متعددة الأبعاد.

[[
 [-0.1465, -0.6501,  0.1993,  ...,  0.1451,  0.3430,  0.6024],
 [-0.4417, -0.5920,  0.3450,  ..., -0.3062,  0.6182,  0.7132],
 [-0.5009, -0.7122,  0.4548,  ..., -0.3662,  0.6091,  0.7648],
 ...,
 [-0.5613, -0.6332,  0.4324,  ..., -0.3792,  0.7372,  0.9288],
 [-0.5416, -0.6345,  0.4180,  ..., -0.3564,  0.6992,  0.9191],
 [-0.5334, -0.6403,  0.4271,  ..., -0.3339,  0.6533,  0.8694]]],

يجب أن يكون لكل مخرجات نماذج Transformers دقة أو حد خطأ يقدر بـ 1e-3. وهذا يأخذ في الاعتبار أي اختلافات في المخرجات تنشأ عن استخدام إطار عمل مكتبة مختلف. قارن المخرجات الوسيطة للنموذج الأصلي بتنفيذ Transformers للتأكد من أنها متطابقة تقريبًا. إن وجود بيئة تصحيح أخطاء فعالة أمر بالغ الأهمية لهذه الخطوة.

إليك بعض النصائح لبناء بيئة تصحيح أخطاء فعّالة.

  • لتصحيح الأخطاء في النتائج الوسيطة، يعتمد الأمر على إطار عمل تعلّم الآلة الذي يستخدمه مستودع النموذج الأصلي. بالنسبة إلى PyTorch، يجب عليك كتابة سكربت لتفكيك النموذج الأصلي إلى مكوّنات فرعية أصغر لاسترجاع القيم الوسيطة.

  • يكون تصحيح الأخطاء أسرع عند استخدام نقطة تحقق مسبقة التدريب أصغر حجمًا مقارنةً بنقطة تحقق أكبر يستغرق فيها المرور الأمامي أكثر من 10 ثوانٍ. إذا لم تتوفر سوى نقاط تحقق كبيرة، أنشئ نموذجًا وهميًا بأوزان مهيأة عشوائيًا واحفظ تلك الأوزان للمقارنة مع تنفيذ مكتبة Transformers.

  • ابحث عن أسهل طريقة لاستدعاء مسار النموذج الأمامي. في المثالي، يجب أن يستدعي هذا الدالة (قد تُسمى predict أو evaluate أو forward أو __call__) مسار الأمام مرة واحدة فقط. فمن الأصعب تصحيح الأخطاء في دالة تستدعي مسار الأمام عدة مرات.

  • افصل الترميز (Tokenization) عن مسار التقدّم (Forward pass). حدّد المكان الذي تُحوَّل فيه مدخلات نصية إلى معرّفات المدخلات (Input ids) داخل مسار التقدّم، وابدأ من هناك. قد تحتاج إلى إنشاء سكربت صغير أو تعديل الكود الأصلي لإدخال معرّفات المدخلات مباشرةً بدلاً من النص المدخل.

  • تأكد من أن النموذج ليس في وضع التدريب. فقد يؤدي ذلك إلى إنتاج مخرجات عشوائية بسبب وجود طبقات dropout متعددة في النموذج. يجب أن يكون التمرير الأمامي في بيئة التصحيح التي تستخدمها حتمياً حتى لا تُستخدم طبقات dropout.

بمجرد أن تتمكن من تشغيل نقطة التفتيش الأصلية، تكون مستعدًا لبدء تكييف كود النموذج لمكتبة Transformers.

تكييف كود النموذج

يجب أن يكون الأمر transformers add-new-model-like قد أنشأ ملف نموذج وملف إعدادات.

  • src/transformers/models/brand_new_llama/modeling_brand_new_llama.py
  • src/transformers/models/brand_new_llama/configuration_brand_new_llama.py

يتميز الكود المولَّد تلقائيًا في ملف modeling.py بنفس البنية المعمارية الخاصة بـ Llama إذا أجبت بأنه نموذج مُفكِّك فقط، أو ستكون بنيته المعمارية مطابقة لتلك الخاصة بـ BART إذا أجبت بأنه نموذج مُرمِّز-مُفكِّك. الكود المولَّد ليس سوى نقطة انطلاق. بناءً على بحثك حول النموذج الجديد، ستحتاج إلى تنفيذ تلك التغييرات المحددة من خلال تكييف الكود المولَّد. وقد يشمل ذلك تغييرات في طبقة الانتباه الذاتي، وترتيب طبقة التطبيع، وما إلى ذلك.

تهيئة النموذج

في هذه المرحلة، لا يجب أن يكون كودك نظيفًا أو حتى صحيحًا بالكامل. من الكفاءة أن تنشئ مسودة أولى بسرعة ثم تحسّن عليها بشكل تكراري. الأهم هو أن يكون بإمكانك إنشاء نموذجك من Transformers. الأمر أدناه ينشئ نموذجًا من التهيئة بأوزان عشوائية، مما يتحقق من أن طريقة __init__ تعمل.

from transformers import BrandNewLlama, BrandNewLlamaConfig
model = BrandNewLlama(BrandNewLlamaConfig())

تحدث التهيئة العشوائية في طريقة _init_weights الخاصة بـ BrandNewLlamaPreTrainedModel. يتم تهيئة جميع الوحدات الفرعية (leaf modules) بناءً على متغيرات الإعداد. استخدم الأدوات المساعدة من وحدة transformers.initialization لضبط القيم، بدلاً من استدعاء العمليات المباشرة على الوزن أو على .data الخاص به.

from transformers import initialization as init

def _init_weights(self, module):
    """Initialize the weights"""
    if isinstance(module, nn.Linear):
        init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
        if module.bias is not None:
            init.zeros_(module.bias)
    elif isinstance(module, nn.Embedding):
        init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
        if module.padding_idx is not None:
            init.zeros_(module.weight[module.padding_idx])
    elif isinstance(module, nn.LayerNorm):
        init.zeros_(module.bias)
        init.ones_(module.weight)

يجب دائمًا تهيئة الأوزان من خلال أدوات المساعدة في transformers.initialization. العمليات التي تُنفَّذ في الموقع (In-place) مثل module.bias.zero_() أو أي شيء يمس module.weight.data يتجاوز _is_hf_initialized الذي يحدد المعاملات التي تم تحميلها بالفعل. يُنفّذ from_pretrained() الدالة _init_weights بعد تحميل نقطة التفتيش (checkpoint)، لذا فإن العمليات التي تُنفَّذ في الموقع تُعيد كتابة الأوزان المحمّلة بقيم عشوائية بصمت. يُفرض هذا على نماذج Transformers من خلال قاعدة TRF012.

أثناء from_pretrained()، يتم عادةً تهيئة النماذج على جهاز meta. يعيد المُحمِّل إنشاء المخازن المؤقتة غير الدائمة كمصفوفات فارغة، لذا فإن القيم المُسندة في __init__ لا تُحفظ. قم بتهيئة هذه المخازن المؤقتة في _init_weights حتى تمتلك القيم الصحيحة بعد التحميل. لمثال على ذلك، راجع كيفية تهيئة BERT لمعرّفات المواقع ومعرّفات أنواع الرموز.

قد يبدو مخطط التهيئة مختلفاً إذا كنت بحاجة إلى تكييفه مع نموذجك. يمكن لوحدة فرعية تحتوي على طريقة reset_parameters خاصة بها استدعاؤها مباشرة. على سبيل المثال، يهيّئ Wav2Vec2ForPreTraining طبقتي nn.Linear الأخيرتين في طبقاته الخطية. الاستدعاء آمن لأن from_pretrained() يعدّل دوال torch.nn.init الأساسية لتتوافق مع علم _is_hf_initialized أثناء تشغيله _init_weights، لذا لا يتم تجاوز الأوزان المحمّلة.

def _init_weights(self, module):
    """Initialize the weights"""
    if isinstance(module, Wav2Vec2ForPreTraining):
        module.project_hid.reset_parameters()
        module.project_q.reset_parameters()

تحويل نقاط التفتيش إلى Transformers

يجب تحويل نقطة التفتيش الأصلية إلى نقطة تفتيش متوافقة مع Transformers.

حاول البحث عن سكربت تحويل موجود لتُنسخه وتُكيّفه وتعيد استخدامه لنموذجك!

  • إذا كنت تنقل نموذجًا من TensorFlow إلى PyTorch، فقد يكون نقطة بداية جيدة هي سكربت التحويل الخاص بـ BERT.
  • إذا كنت تنقل نموذجًا من PyTorch إلى PyTorch، فقد يكون نقطة بداية جيدة هي سكربت التحويل الخاص بـ BART.

تأكد من أن جميع الأوزان المطلوبة قد تم تهيئتها، ثم اطبع جميع أوزان نقطة الحفظ التي لم تُستخدم في التهيئة للتأكد من أن النموذج تم تحويله بشكل صحيح.

قد تواجه عبارات خاطئة تتعلق بالأشكال أو إسناد الأسماء أثناء التحويل. يرجح أن يكون السبب هو وجود معاملات غير صحيحة في BrandNewLlamaConfig، أو معمارية غير مناسبة، أو خطأ برمجي في طريقة init في تطبيقك، أو أنك بحاجة إلى تبديل أبعاد أحد أوزان نقطة الحفظ.

استمر في تكرار العمل على قسم تكييف كود النموذج حتى يتم تحميل جميع أوزان نقطة الحفظ بشكل صحيح. بمجرد إمكانية تحميل نقطة حفظ في نموذجك، احفظها في مجلد. يجب أن يحتوي هذا المجلد على ملف model.safetensors وملف config.json.

model.save_pretrained("/path/to/converted/checkpoint/folder")

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

أوزان طبقات PyTorch وأسمائها

من المفيد إنشاء نموذج PyTorch أساسي لفهم كيفية تعريف أسماء الطبقات وتبديل الأوزان.

from torch import nn

class SimpleModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.dense = nn.Linear(10, 10)
        self.intermediate = nn.Linear(10, 10)
        self.layer_norm = nn.LayerNorm(10)

تُعرَّف أسماء طبقات PyTorch بواسطة اسم خاصية الفئة الخاصة بالطبقة (dense، intermediate، layer_norm). أنشئ كائنًا من SimpleModel لملء جميع الطبقات بأوزان عشوائية.

model = SimpleModel()
print(model)
SimpleModel(
  (dense): Linear(in_features=10, out_features=10, bias=True)
  (intermediate): Linear(in_features=10, out_features=10, bias=True)
  (layer_norm): LayerNorm((10,), eps=1e-05, elementwise_affine=True)
)

تُهيّأ قيم الأوزان لطبقة محددة بشكل عشوائي.

print(model.dense.weight.data)
tensor([[-0.0818,  0.2207, -0.0749, -0.0030,  0.0045, -0.1569, -0.1598,  0.0212,
         -0.2077,  0.2157],
        [ 0.1044,  0.0201,  0.0990,  0.2482,  0.3116,  0.2509,  0.2866, -0.2190,
          0.2166, -0.0212],
        [-0.2000,  0.1107, -0.1999, -0.3119,  0.1559,  0.0993,  0.1776, -0.1950,
         -0.1023, -0.0447],
        [-0.0888, -0.1092,  0.2281,  0.0336,  0.1817, -0.0115,  0.2096,  0.1415,
         -0.1876, -0.2467],
        [ 0.2208, -0.2352, -0.1426, -0.2636, -0.2889, -0.2061, -0.2849, -0.0465,
          0.2577,  0.0402],
        [ 0.1502,  0.2465,  0.2566,  0.0693,  0.2352, -0.0530,  0.1859, -0.0604,
          0.2132,  0.1680],
        [ 0.1733, -0.2407, -0.1721,  0.1484,  0.0358, -0.0633, -0.0721, -0.0090,
          0.2707, -0.2509],
        [-0.1173,  0.1561,  0.2945,  0.0595, -0.1996,  0.2988, -0.0802,  0.0407,
          0.1829, -0.1568],
        [-0.1164, -0.2228, -0.0403,  0.0428,  0.1339,  0.0047,  0.1967,  0.2923,
          0.0333, -0.0536],
        [-0.1492, -0.1616,  0.1057,  0.1950, -0.2807, -0.2710, -0.1586,  0.0739,
          0.2220,  0.2358]]).

في سكربت التحويل، يجب استبدال الأوزان العشوائية بالأوزان الدقيقة من الطبقة المقابلة في نقطة الفحص الأصلية.

# retrieve matching layer weights with recursive algorithm
layer_name = "dense"
pretrained_weight = array_of_dense_layer

model_pointer = getattr(model, "dense")
model_pointer.weight.data = torch.from_numpy(pretrained_weight)

تحقق من أن الأوزان المُهيأة عشوائياً وأوزان نقطة الحفظ المُسبقة التدريب المقابلة لها لها الشكل والاسم المتطابقان. أضف عبارات assert للشكل وطبع أسماء أوزان نقطة الحفظ.

assert (
    model_pointer.weight.shape == pretrained_weight.shape
), f"Pointer shape of random weight {model_pointer.shape} and array shape of checkpoint weight {pretrained_weight.shape} mismatched"

logger.info(f"Initialize PyTorch weight {layer_name} from {pretrained_weight.name}")

عندما لا يتطابق الشكل أو الاسم، فقد تكون قد قمت بتعيين وزن نقطة التفتيش (checkpoint) غير الصحيح لطبقة تم تهيئتها عشوائيًا. قد يكون السبب وراء الشكل غير الصحيح هو أن معاملات BrandNewLlama لا تتطابق تمامًا مع معاملات النموذج الأصلي. لكن من الممكن أيضًا أن يتطلب تنفيذ طبقة PyTorch عكس الأوزان (transposed) أولاً.

تنفيذ المرور الأمامي

يجب تنفيذ المرور الأمامي (forward pass) تالياً إذا تم تحميل النموذج بشكل صحيح. فهو يستقبل بعض المدخلات ويعيد مخرجات النموذج.

model = BrandNewLlamaModel.from_pretrained("/path/to/converted/checkpoint/folder")
input_ids = [0, 4, 4, 3, 2, 4, 1, 7, 19]
output = model.generate(input_ids).last_hidden_states

لا تدعك الإحباط إذا لم يكن مسار التقدّم لديك مطابقًا تمامًا للمخرجات من النموذج الأصلي أو إذا أعاد خطأً. تأكد من أن مسار التقدّم لا يرمي أي أخطاء. وغالبًا ما يكون السبب في ذلك هو أن الأبعاد غير صحيحة أو لأن نوع البيانات المستخدم غير صحيح (torch.long بدلاً من torch.float32).

يجب أن تكون دقة مخرجاتك 1e-3. تأكد من أن أشكال المخرجات وقيم المخرجات متطابقة تماماً. تشمل الأسباب الشائعة لعدم تطابق المخرجات ما يلي:

  • لم تتم إضافة بعض الطبقات (طبقة التنشيط أو اتصال متبقٍ).
  • لا يتم ربط مصفوفة تضمين الكلمات.
  • تُستخدم تضمينات موضعية خاطئة لأن التنفيذ الأصلي يتضمن إزاحة.
  • يُطبَّق Dropout أثناء المرور الأمامي. صحّح هذا الخطأ عن طريق التأكد من أن model.training هو False ومرر self.training إلى torch.nn.functional.dropout.

قارن بين المرور الأمامي (forward pass) للنموذج الأصلي وتنفيذك للتحقق من وجود أي اختلافات. بشكل مثالي، قم بتشخيص الأخطاء وطباعة المخرجات الوسيطة لكلتا التنفيذات الخاصة بالمرور الأمامي لتحديد المكان الذي يختلف فيه التنفيذ الأصلي عن تنفيذك.

  1. تأكد من أن input_ids المُثبّتة يدويًا في كلا التنفيذين متطابقتان.
  2. تحقق من أن مخرجات التحويل الأول لـinput_ids (وهي عادةً تضمينات الكلمات) متطابقة، ثم تابع الفحص حتى الطبقة الأخيرة.

يجب أن يشير أي فرق بين التنفيذين إلى وجود خطأ في تنفيذك.

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

عندما تنتج كلتا التنفيذات نفس المخرجات، تحقق من أن المخرجات ضمن دقة 1e-3.

torch.allclose(original_output, output, atol=1e-3)

عادةً ما تكون هذه هي الأصعب في العملية. تهانينا إذا وصلت إلى هذه المرحلة!

وإذا واجهتَ صعوبةً أو تعثّرتَ في هذه الخطوة، فلا تتردد في طلب المساعدة على طلب الإرسال الخاص بك.

إضافة اختبارات النموذج

بينما يعمل النموذج، لا يزال عليك إضافة اختبارات للتأكد من توافقك مع Transformers. الاختبارات مهمة لأنها تساعد المستخدمين على فهم عملك من خلال الاطلاع على اختبارات محددة، ولأنها تمنع كسر نموذجك في المستقبل إذا أُجريت أي تغييرات.

يجب أن يكون Cookiecutter قد أضاف ملف اختبار لنموذجك. شغّل ملف الاختبار أدناه للتأكد من نجاح جميع الاختبارات الشائعة.

pytest tests/models/brand_new_llama/test_modeling_brand_new_llama.py

يجب إضافة اختبارات التكامل أولاً لأنها تخدم الغرض نفسه الذي خدمته سكربتات التصحيح التي استخدمتها سابقاً لتنفيذ النموذج الجديد في Transformers. وقد أضاف Cookiecutter قالباً لتلك اختبارات النموذج، BrandNewLlamaModelIntegrationTests، ويجب ملؤه. ولضمان نجاحه، نفّذ الأمر التالي.

macOS
Windows
RUN_SLOW=1 pytest -sv tests/models/brand_new_llama/test_modeling_brand_new_llama.py::BrandNewLlamaModelIntegrationTests

يجب اختبار جميع الميزات الفريدة لـ BrandNewLlama في اختبار منفصل تحت BrandNewLlamaModelTester/BrandNewLlamaModelTest. غالبًا ما يُغفل هذا الاختبار، لكنه بالغ الأهمية لأن:

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

تنفيذ مُجزِّئ

نوصي بإضافة مُقسِّم سريع (PreTrainedTokenizerFast) لمنح المستخدمين أفضل أداء. لا تتردد في الإشارة إلى @ArthurZucker أو @itazap في طلب الإرسال الخاص بك للحصول على مساعدة حول كيفية إضافة PreTrainedTokenizerFast.

بعد الانتهاء من النموذج، حان الوقت للتركيز على مُجزّئ النص. يجب أن يكون مُجزّئ النص مطابقًا أو مشابهًا جدًا لمُجزّئ نص موجود مسبقًا في Transformers.

ابحث عن ملف المُجزِّئ (tokenizer) الأصلي وحمله إلى تطبيقك. أنشئ سكربتًا في المستودع الأصلي يستقبل نصًا ويعيد input_ids. يجب أن يشبه الشيفرة الزائفة (pseudocode) الشيفرة أدناه.

input_str = "This is a long example input string containing special characters .$?-, numbers 2872 234 12 and words."
model = BrandNewLlamaModel.load_pretrained_checkpoint("/path/to/checkpoint/")
input_ids = model.tokenize(input_str)

قد تحتاج إلى البحث في المستودع الأصلي للعثور على دالة المُجزِّئ الصحيحة أو تعديل المُجزِّئ الموجود في نسختك من المستودع الأصلي بحيث يُرجع input_ids فقط. يجب أن يبدو سكربت المُجزِّئ الخاص بك مشابهاً لما يلي.

from transformers import BrandNewLlamaTokenizer

input_str = "This is a long example input string containing special characters .$?-, numbers 2872 234 12 and words."
tokenizer = BrandNewLlamaTokenizer.from_pretrained("/path/to/tokenizer/folder/")
input_ids = tokenizer(input_str).input_ids

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

تنفيذ معالج الصور

تستخدم معالجات الصور الآن معمارية مبنية على الخلفية (backend). الخلفية الافتراضية هي TorchvisionBackend، والتي تستخدم مكتبة torchvision ويمكنها تنفيذ معالجة الصور على وحدة معالجة الرسوميات (GPU). كما يتم توفير خلفية بديلة تعتمد على PIL/NumPy (PilBackend). يتم استيراد الخلفيتين من image_processing_backends. لا تتردد في الإشارة إلى @yonigozlan للحصول على المساعدة.

على الرغم من أن هذا المثال لا يتضمن معالج صور، فقد تحتاج إلى تنفيذ معالج صور إذا كانت نموذجك يتطلب مدخلات صور. يتولى معالج الصور مسؤولية تحويل الصور إلى صيغة مناسبة لنموذجك. قبل تنفيذ معالج صور جديد، تحقق مما إذا كان يمكن إعادة استخدام معالج صور موجود في مكتبة Transformers، حيث تشترك العديد من النماذج في تقنيات معالجة صور متشابهة. لاحظ أنه يمكنك أيضًا استخدام modular لمعالجات الصور لإعادة استخدام المكونات الموجودة.

إذا كنت بحاجة إلى تنفيذ معالج صور جديد، فإن لكل نموذج ملفَي معالج:

  • image_processing_<model>.py: المعالج الافتراضي المدعوم من torchvision (<Model>ImageProcessor)، والذي يرث من TorchvisionBackend. يحل هذا المعالج محل المعالج "السريع" القديم.
  • image_processing_pil_<model>.py: المعالج البديل باستخدام PIL/NumPy (<Model>ImageProcessorPil)، والذي يرث من PilBackend. يستبدل هذا المعالج القديم “البطيء”.

يحدد ملف torchvision backend أيضًا أي فئة kwargs مخصصة يستوردها ملف PIL. يستخدم كلا الملفين زخرفة @auto_docstring — لا تضف سطور توثيق يدوية للفئات. راجع IMAGE_PROCESSOR_REFACTORING_GUIDE.md للحصول على شرح خطوة بخطوة وأمثلة كاملة.

أضِف اختبارات لمعالج الصور في tests/models/your_model_name/test_image_processing_your_model_name.py. يجب أن تكون هذه الاختبارات مشابهة لتلك الخاصة بمعالجات الصور الأخرى، وأن تتحقق من أن معالج الصور يتعامل مع مدخلات الصور بشكل صحيح. إذا تضمن معالج الصور ميزات أو طرق معالجة فريدة، فتأكد من إضافة اختبارات محددة لها أيضاً.

تنفيذ المعالج

إذا كان نموذجك يقبل عدة أنماط إدخال، مثل النص والصور، فأنت بحاجة إلى إضافة مُعالِج. يركّز المُعالِج معالجة ما قبل المعالجة لأنماط الإدخال المختلفة قبل تمريرها إلى النموذج.

يجب أن يستدعي المعالج المعالجات الخاصة بكل نمط من الأنماط داخل دالة __call__ الخاصة به للتعامل مع كل نوع من المدخلات بشكل صحيح. تأكد من فحص المعالجات الموجودة في المكتبة لفهم بنيتها المتوقعة. يستخدم Transformers الاتفاق التالي في توقيع دالة __call__.

def __call__(
    self,
    images: ImageInput = None,
    text: Union[TextInput, PreTokenizedInput, list[TextInput], list[PreTokenizedInput]] = None,
    audio=None,
    videos=None,
    **kwargs: Unpack[YourModelProcessorKwargs],
) -> BatchFeature:
    ...

YourModelProcessorKwargs هو TypedDict يتضمن جميع معاملات المعالجة النموذجية وأي معاملات إضافية قد يتطلبها معالج محدد.

أضِف اختبارات للمعالج في tests/models/your_model_name/test_processor_your_model_name.py. يجب أن تكون هذه الاختبارات مشابهة لتلك الخاصة بالمعالجات الأخرى، وأن تتحقق من أن المعالج يتعامل بشكل صحيح مع الأنماط المختلفة.

اختبارات التكامل

الآن وبعد أن حصلت على النموذج ومُقسِّم النص، أضف اختبارات تكامل من الطرف إلى الطرف للنموذج ومُقسِّم النص في tests/models/brand_new_llama/test_modeling_brand_new_llama.py.

يجب أن يوفّر الاختبار مثالاً نصياً إلى نص ذي معنى لإظهار أن النموذج يعمل كما هو متوقع. على سبيل المثال، يمكنك تضمين زوج ترجمة من مصدر إلى هدف، أو زوج مقال إلى ملخص، أو زوج سؤال إلى إجابة.

إذا لم يتم ضبط نقطة التفتيش (checkpoint) على مهمة لاحقة، فإن اختبارات النموذج كافية.

أخيرًا، حاول التأكد من إمكانية تشغيل اختباراتك على وحدة معالجة رسومية (GPU) عن طريق إضافة عبارات .to(self.device) إلى المتجهات الداخلية للنماذج. إذا لم يكن لديك وصول إلى وحدة معالجة رسومية، يمكننا الاعتناء بذلك نيابةً عنك.

إضافة التوثيق

لا يكون نموذجك مفيدًا إلا إذا عرف المستخدمون كيفية استخدامه. ولهذا السبب من المهم إضافة التوثيق وسلاسل التوثيق (docstrings). أضاف Cookiecutter ملف قالب، docs/source/model_doc/brand_new_llama.md، يمكنك ملؤه بالمعلومات المتعلقة بنموذجك.

عادةً ما يكون هذا أول تفاعل للمستخدم مع النموذج، لذا يجب أن تكون التوثيقات واضحة ومختصرة. وغالبًا ما يكون من المفيد جدًا إضافة أمثلة على كيفية استخدام النموذج.

تأكد من إضافة توثيق الدوال (docstrings) إلى src/transformers/models/brand_new_llama/modeling_brand_new_llama.py وأن يتضمن جميع المدخلات والمخرجات اللازمة. راجع دليلنا لكتابة التوثيق وتوثيق الدوال.

إعادة هيكلة

حان الوقت لتنظيم الأمور والتأكد من أن نمط الكود متسق مع بقية المكتبة. نفّذ الأمر التالي لإصلاح الأنماط غير الصحيحة تلقائيًا.

make style

للتحقق من أن نمط الكود يجتاز فحوصات الجودة، نفّذ الأمر أدناه.

make check-repo

قد توجد اختبارات أو فحوصات أخرى فاشلة في طلب الإرسال الخاص بك، مثل غياب سلسلة التوثيق أو تسمية غير صحيحة، وذلك بسبب اختبارات التصميم الصارمة في Transformers. يمكننا مساعدتك في هذه المشكلات إذا واجهت صعوبة في حلها.

بعد التأكد من أن الكود يعمل بشكل صحيح، قد ترغب في إعادة هيكلة الكود لجعله أكثر قابلية للقراءة أو أنظف.

الرفع إلى المستودع

قم بتحويل جميع نقاط التفتيش (checkpoints) ورفعها إلى Hub. أضف بطاقة نموذج (model card) لتوفير مزيد من الشفافية والسياق حول النموذج. يجب أن تُبرز بطاقة النموذج خصائص محددة لنقطة التفتيش، وكيفية تدريب النموذج، وأمثلة على الأكواد توضح كيفية استخدامه.

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

يجب عليك أيضاً الاستشارة مع فريق Transformers لتحديد اسم مناسب للنموذج، والحصول على صلاحيات الوصول المطلوبة لرفع النموذج.

استخدم طريقة push_to_hub() لرفع النموذج.

brand_new_bert.push_to_hub("brand_new_llama")

راجع دليل المشاركة لمزيد من المعلومات حول رفع النماذج إلى Hub.

دمج نموذجك

أنت مستعد أخيراً لدمج طلب السحب الخاص بك وإضافة النموذج إلى Transformers بشكل رسمي! تأكد من أن جميع الاختبارات ناجحة وأن جميع التعليقات والملاحظات قد تم التعامل معها.

تهانينا على إضافة نموذج جديد إلى Transformers! 🥳

هذا مساهمة ذات أهمية كبيرة. عملك يجعل Transformers أكثر سهولة في الوصول للمطورين والباحثين حول العالم. يجب أن تفخر بمساهمتك وتشارك إنجازك مع المجتمع!

انظر أيضاً

  • قواعد بنية النموذج — قواعد ثابتة تُفرض على جميع ملفات modeling_*.py وconfiguration_*.py. نفّذ make typing للتحقق منها قبل فتح طلب دمج.
  • فحوصات طلبات الدمج — مرجع كامل للفحوصات التي يجريها نظام التكامل المستمر على طلب الدمج الخاص بك وكيفية اجتيازها.
Update on GitHub