صُمِّمت مكتبة المحوّلات Transformers المتخصصة في بناء نماذج الذكاء الاصطناعي من منصة Huggingface بحيث يمكن توسيعها بسهولة، وتُكتَب النماذج Models بالكامل في مجلد فرعي محدَّد من المستودع بدون تجريد أو إخفاء لأي من تفاصيل العمل، لذا يمكننا بسهولة نسخ أي ملف نموذج وتعديله وفقًا لاحتياجاتنا. وإذا أردنا كتابة نموذج Model جديد خاص بنا، فيمكن البدء بالنموذج من الصفر.
سنوضّح في هذا المقال كيفية كتابة نموذج مخصَّص وضبطه Configuration لنتمكّن من استخدامه بشكل يتوافق مع مكتبة المحوّلات Transformers، وسنوضّح كيفية مشاركته مع المجتمع مع شيفرته البرمجية ليتمكن أي شخص من استخدامه، حتى إن لم يكن موجودًا في مكتبة المحوّلات Transformers، حيث سنرى كيفية إضافة أو تعديل الوظائف التي يقدمها إطار العمل الافتراضي في مكتبة Transformers باستخدام أدوات برمجية كالخطافات Hooks والشيفرة البرمجية الخاصة بنا. سنستخدم في هذا المقال نموذج ResNet الذي هو جزء من المكتبة timm
ونعدّله ليعمل كجزء من مكتبة Transformers وسنغلفه ضمن النموذج
PreTrainedModel
الذي يعد أساس جميع النماذج في Transformers.
كتابة ضبط Configuration مخصص
عند إنشاء نموذج في مكتبة Transformers، يجب علينا أولاً إعداد كائن ضبط النموذج، فضبط النموذج هو كائن يحتوي على جميع المعلومات الضرورية لبناء النموذج، ولا يمكن للنموذج أن يأخذ إلا الكائن config
لتهيئته كما سنرى في القسم التالي، لذا يجب أن يكون هذا الكائن مكتملًا قدر الإمكان.
ملاحظة: لا حاجة لتمرير كل وسيط بشكل فردي عند إنشاء النموذج، حيث تتبع النماذج في مكتبة المحوّلات Transformers منهجية تمرير كائن واحد config
إلى التابع __init__
الخاص بالنموذج. بعد ذلك، يُمرَّر هذا الكائن بالكامل إلى الطبقات الفرعية للنموذج بدلاً من تقسيمه إلى عدة وسطاء. هذا يجعل الشيفرة البرمجية بسيطة ومنظمة من خلال الاحتفاظ بجميع الإعدادات في مكان واحد يسهل الوصول إليه، كما يساهم هذا النهج في تحسين قابلية إعادة استخدام الشيفرة البرمجية مع نماذج أخرى في مكتبة المُحوِّلات.
إنشاء كائن ضبط النموذج
سنأخذ في المثال التالي، بعض الإعدادات أو الوسطاء من الصنف ResNet
والتي نرغب في تعديلها. بعد ذلك، ستوفر عمليات الضبط المختلفة أنواعًا متنوعة من أصناف ResNet
المحتملة. ثم سنُخزّن هذه الوسطاء بعد التحقق من صحتها.
from transformers import PretrainedConfig from typing import List class ResnetConfig(PretrainedConfig): model_type = "resnet" def __init__( self, block_type="bottleneck", layers: List[int] = [3, 4, 6, 3], num_classes: int = 1000, input_channels: int = 3, cardinality: int = 1, base_width: int = 64, stem_width: int = 64, stem_type: str = "", avg_down: bool = False, **kwargs, ): if block_type not in ["basic", "bottleneck"]: raise ValueError(f"`block_type` must be 'basic' or bottleneck', got {block_type}.") if stem_type not in ["", "deep", "deep-tiered"]: raise ValueError(f"`stem_type` must be '', 'deep' or 'deep-tiered', got {stem_type}.") self.block_type = block_type self.layers = layers self.num_classes = num_classes self.input_channels = input_channels self.cardinality = cardinality self.base_width = base_width self.stem_width = stem_width self.stem_type = stem_type self.avg_down = avg_down super().__init__(**kwargs)
الأمور الثلاثة المهمة التي يجب تذكرها عند كتابة الضبط الخاص بنا هي كالتالي:
-
يجب أن يرث الصنف المخصص
ResnetConfig
من الصنف الأبPretrainedConfig
-
يجب أن يقبل التابع
__init__
من الصنف المخصص أي وسطاءkwargs
-
يجب تمرير هذه الوسطاء
kwargs
إلى الصنف الأب للتابع__init__
تعني الوراثة Inheritance التأكد من الحصول على جميع الوظائف من مكتبة المحوّلات Transformers، ويمثّل القيدان الآخران احتواء الصنف PretrainedConfig
على حقول أكثر من الحقول التي نضبطها، ويجب أن يقبل ضبطنا كافة هذه الحقول ثم تُرسَل إلى الصنف الأب عند إعادة تحميل الضبط باستخدام التابع from_pretrained
.
لا يُعَد تحديد السمة model_type
للضبط الخاص بنا بالقيمة model_type="resnet"
هنا إلزاميًا، إلا إذا أردنا تسجيل نموذجنا في الأصناف التلقائية Auto Classes كما سنوضح لاحقًا. يمكننا بعد ذلك إنشاء وحفظ الضبط الخاص بنا بسهولة كما نفعل مع أي ضبط نموذج آخر للمكتبة. لاحظ المثال التالي الذي يوضّح كيفية إنشاء الضبط resnet50d
وحفظه:
resnet50d_config = ResnetConfig(block_type="bottleneck", stem_width=32, stem_type="deep", avg_down=True) resnet50d_config.save_pretrained("custom-resnet")
سيؤدي هذا لحفظ ملف بالاسم config.json
ضمن المجلد custom-resnet
، يمكننا بعدها إعادة تحميل ملف الضبط الخاص باستخدام التابع from_pretrained
كما يلي:
resnet50d_config = ResnetConfig.from_pretrained("custom-resnet")
ويمكننا أيضًا استخدام أي تابع آخر من الصنف PretrainedConfig
مثل التابع push_to_hub()
لرفع الضبط الخاص بنا إلى المستودع Hub مباشرة.
كتابة نموذج مخصص
أصبح لدينا ضبط مخصص لنموذجنا ResNet
، ويمكننا الآن كتابة النموذج نفسه، حيث سنكتب نموذجين الأول يستخرج الميزات المخفية من مجموعة الصور مثل النموذج BertModel
، والثاني لتصنيف الصور وفق الفئات المختلفة مثل النموذج BertForSequenceClassification
.
لن نكتب نموذج كامل بل سنكتب فقط مغلِّف wrapper بسيط للنموذج للسهولة، سيكون بمثابة هيكل بسيط للنموذج يمكننا تمرير الإعدادات أو الضبط إليه. وقبل أن نكتب الصنف ResNet
أو النموذج نفسه، يجب أن نحدد أنواع الكتل في النموذج مثل basic أو bottleneck، ونحدد كيفية بناء هذه الكتل أو الطبقات في النموذج. بمجرد تحديد هذه الأمور، سنستخدم الضبط الذي حددناه سابقًا لتمرير هذه الإعدادات إلى الصنف ResNet
لإنشاء النموذج بناءً على هذه الإعدادات.
from transformers import PreTrainedModel from timm.models.resnet import BasicBlock, Bottleneck, ResNet from .configuration_resnet import ResnetConfig BLOCK_MAPPING = {"basic": BasicBlock, "bottleneck": Bottleneck} class ResnetModel(PreTrainedModel): config_class = ResnetConfig def __init__(self, config): super().__init__(config) block_layer = BLOCK_MAPPING[config.block_type] self.model = ResNet( block_layer, config.layers, num_classes=config.num_classes, in_chans=config.input_channels, cardinality=config.cardinality, base_width=config.base_width, stem_width=config.stem_width, stem_type=config.stem_type, avg_down=config.avg_down, ) def forward(self, tensor): return self.model.forward_features(tensor)
الآن، سنعدّل التابع forward
فقط بالنسبة للنموذج ResNet
المخصص لتصنيف الصور، فهذا التابع يتعامل مع البيانات المدخلة، ويحدد كيف تتم معالجتها عبر طبقات النموذج للحصول على النتيجة المطلوبة، سنجري التعديل كما يلي:
import torch class ResnetModelForImageClassification(PreTrainedModel): config_class = ResnetConfig def __init__(self, config): super().__init__(config) block_layer = BLOCK_MAPPING[config.block_type] self.model = ResNet( block_layer, config.layers, num_classes=config.num_classes, in_chans=config.input_channels, cardinality=config.cardinality, base_width=config.base_width, stem_width=config.stem_width, stem_type=config.stem_type, avg_down=config.avg_down, ) def forward(self, tensor, labels=None): logits = self.model(tensor) if labels is not None: loss = torch.nn.cross_entropy(logits, labels) return {"loss": loss, "logits": logits} return {"logits": logits}
نلاحظ في كلتا الحالتين كيف ورثنا الصنف PreTrainedModel
واستدعينا تهيئة الصنف الأب باستخدام الضبط config
كما يحدث عندما نكتب وحدة torch.nn.Module
عادية في PyTorch. ولا يُعَد السطر الذي يضبط config_class
إلزاميًا، إلا إذا أردنا تسجيل نموذجنا في الأصناف التلقائية Auto Classes أي عندما نرغب بأن نتيح لمنصة Hugging Face تحديد النموذج تلقائيًا بناءً على الضبط كما سنوضح لاحقًا.
ملاحظة: إذا كان نموذجنا مماثلًا لنموذج آخر موجود مسبقًا في المكتبة Transformers، فيمكن إعادة استخدام الضبط الخاص بهذا النموذج نفسه.
يمكن جعل نموذجنا يعيد أي مخرجات نريدها، ولكن ستؤدي إعادة قاموس Dictionary كما فعلنا مع الصنف ResnetModelForImageClassification
مع تضمين الخسارة عند تمرير التسميات التوضيحية Labels إلى جعل نموذجك قابلًا للاستخدام مباشرة في الصنف Trainer
. يُعدّ استخدام تنسيق خرج آخر جيدًا طالما أنك تخطط لاستخدام حلقة تدريب خاصة بك أو أي مكتبة أخرى للتدريب.
أصبح لدينا صنف النموذج الخاص بنا، فلننشئ الآن نموذجًا كما يلي:
resnet50d = ResnetModelForImageClassification(resnet50d_config)
يمكننا استخدام أي تابع من توابع الصنف PreTrainedModel
مثل التابع save_pretrained()
أو push_to_hub()
، حيث سنستخدم التابع الثاني في القسم التالي وسنرى كيفية دفع أوزان النموذج باستخدام الشيفرة البرمجية الخاصة بنموذجنا، ولكن لنحمّل أولًا بعض الأوزان المدرَّبة مسبقًا في نموذجنا.
يمكن أن ندرّب نموذجنا المخصّص على بياناتنا الخاصة في حالة استخدامه بشكل مخصص، ولكن سنستخدم في هذا المقال النسخة المدرَّبة مسبقًا من الضبط resnet50d
، وبما أن نموذجنا يحتوي على مغلِّف فقط، فسيكون من السهل نقل هذه الأوزان كما يلي:
import timm pretrained_model = timm.create_model("resnet50d", pretrained=True) resnet50d.model.load_state_dict(pretrained_model.state_dict())
لنوضّح الآن كيفية التأكد من حفظ شيفرة النموذج البرمجية عند تنفيذ التابع save_pretrained()
أو push_to_hub()
.
تسجيل النموذج في الأصناف التلقائية Auto Classes
إذا أردنا كتابة مكتبة توسّع المكتبة Transformers، فقد نرغب في توسيع الأصناف التلقائية لتضمين نموذجنا الخاص، ويختلف ذلك عن دفع الشيفرة البرمجية إلى المستودع Hub، إذ سيحتاج المستخدمون لاستيراد مكتبتنا هذه للحصول على النموذج المخصَّص على عكس تنزيل شيفرة النموذج البرمجية تلقائيًا من المستودع Hub.
إذا احتوى الضبط على السمة model_type
التي تختلف عن أنواع النماذج الموجودة مسبقًا واحتوت أصناف نموذجنا على سمات config_class
الصحيحة، فيمكن إضافتها إلى الأصناف التلقائية كما يلي:
from transformers import AutoConfig, AutoModel, AutoModelForImageClassification AutoConfig.register("resnet", ResnetConfig) AutoModel.register(ResnetConfig, ResnetModel) AutoModelForImageClassification.register(ResnetConfig, ResnetModelForImageClassification)
نلاحظ أن الوسيط الأول المُستخدَم عند تسجيل ضبطنا المخصص في الصنف التلقائي AutoConfig
يجب أن يتطابق مع السمة model_type
لضبطنا المخصص، ويجب أن يتطابق الوسيط الأول المُستخدَم عند تسجيل النماذج المخصَّصة في أي صنف نموذج تلقائي مع السمة config_class
لتلك النماذج.
إرسال الشيفرة البرمجية للمستودع
علينا التأكّد أولًا من تعريف نموذجنا الكامل في ملف بايثون .py
، حيث يمكن الاعتماد على الاستيراد النسبي لبعض الملفات الأخرى طالما أن جميع الملفات موجودة في المجلد نفسه، فالوحدات الفرعية لهذه الميزة غير مدعومة حتى الآن. سنعرّف في مثالنا ملف modeling_resnet.py
وملف configuration_resnet.py
في مجلد ضمن مجلد العمل الحالي resnet_model
، ويحتوي ملف الضبط على الشيفرة البرمجية الخاصة بالصنف ResnetConfig
، ويحتوي ملف النموذج على الشيفرة البرمجية الخاصة بالصنفين ResnetModel
و ResnetModelForImageClassification
.
. └── resnet_model ├── __init__.py ├── configuration_resnet.py └── modeling_resnet.py
يمكن أن يكون الملف __init__.py
فارغًا، لكنه موجود لتتمكّن لغة بايثون من اكتشاف إمكانية استخدام resnet_model
كوحدة Module مما يعني أنه يمكن استيراد المكونات والملفات من هذا المجلد في برامج بايثون أخرى.
ملاحظة1: إذا أردنا نسخ ملفات النموذج من المكتبة إلى مشروعنا الخاص، فيجب استبدال جميع تعليمات الاستيراد النسبية في أعلى الملف واستيرادها مباشرة من حزمة transformers
.
ملاحظة2: تُعدّ واجهة التطبيقات البرمجية API هذه تجريبية وقد تحتوي على بعض التغييرات في الإصدارات اللاحقة.
بإمكاننا إعادة استخدام أو إنشاء صنف فرعي لضبط أو لنموذج موجود مسبقًا، ويمكن مشاركة نموذجنا مع المجتمع من خلال استيراد نموذج وضبط ResNet
أولًا من الملفات التي أنشأناها كما يلي:
from resnet_model.configuration_resnet import ResnetConfig from resnet_model.modeling_resnet import ResnetModel, ResnetModelForImageClassification
بعد ذلك، علينا إخبار المكتبة بأننا نريد نسخ ملفات الشيفرة البرمجية لتلك الكائنات عند استخدام التابع save_pretrained
وتسجيلها بطريقة صحيحة في صنف تلقائي محدّد وخاصةً بالنسبة للنماذج، لذا ننفّذ التعليمات التالية:
ResnetConfig.register_for_auto_class() ResnetModel.register_for_auto_class("AutoModel") ResnetModelForImageClassification.register_for_auto_class("AutoModelForImageClassification")
نلاحظ أنه لا حاجة لتحديد صنف تلقائي للضبط Config، إذ يوجد صنف تلقائي واحد فقط له هو AutoConfig
، ولكن يختلف الأمر بالنسبة للنموذج Model، فالنماذج في مكتبة المحولات Transformers قد تُستَخدم في مهام مختلفة مثل توليد النصوص، أو الترجمة أو تصنيف الصور، لذا يتوجب علينا تحديد الصنف التلقائي المناسب بناءً على نوع النموذج والمهمة التي يؤديها.
عندما نريد جعل نموذجنا الخاص قابلاً للاستخدام في مكتبة Transformers وتسجيله ضمن النماذج التلقائية مثل AutoModel, AutoConfig، يجب استخدام التابعregister_for_auto_class()
لتسجيل النموذج بشكل صحيح، وإذا كنا نفضل استخدام الشيفرة البرمجية الموجودة على المستودع Hub من مستودع آخر، فلن تحتاج لاستدعاء هذا التابع. يمكننا تعديل الملف config.json
مباشرة باستخدام البنية التالية في الحالات التي يوجد فيها أكثر من صنف تلقائي:
"auto_map": { "AutoConfig": "<your-repo-name>--<config-name>", "AutoModel": "<your-repo-name>--<config-name>", "AutoModelFor<Task>": "<your-repo-name>--<config-name>", },
لننشئ بعد ذلك الضبط والنماذج كما فعلنا سابقًا:
resnet50d_config = ResnetConfig(block_type="bottleneck", stem_width=32, stem_type="deep", avg_down=True) resnet50d = ResnetModelForImageClassification(resnet50d_config) pretrained_model = timm.create_model("resnet50d", pretrained=True) resnet50d.model.load_state_dict(pretrained_model.state_dict())
لنتأكّد الآن من تسجيل الدخول لإرسال النموذج إلى المستودع Hub، لذا نشغّل الأمر التالي في الطرفية Terminal:
huggingface-cli login
أو نكتب من تطبيق المفكرة ما يلي:
from huggingface_hub import notebook_login notebook_login()
يمكن بعد ذلك رفع النموذج إلى فضاء الأسماء Namespace الخاص بحسابنا على Hugging Face كما يلي:
resnet50d.push_to_hub("custom-resnet50d")
ترفع التعليمة النموذج resnet50d
إلى المستودع Hugging Face Hub باسم custom-resnet50d
وتجعل النموذج متاحًا لاستخدامه مباشرة في المنصة Hugging Face. حيث تُنسَخ ملفات .py
للنموذج وللضبط بالإضافة إلى أوزان النموذج والضبط بتنسيق json في المجلد custom-resnet50d
وستُرفَع النتيجة للمستودع Hub، ويمكننا التحقق من النتيجة في مستودع النماذج على منصة Huggingface.
وللمزيد حول طريقة الدفع إلى المستودع Hub ننصح بمطالعة مقال مشاركة نموذج ذكاء اصطناعي على منصة Hugging Face .
استخدام نموذج مع شيفرة برمجية مخصصة
يمكن استخدام أي ضبط أو نموذج أو مرمِّز Tokenizer مع ملفات الشيفرة البرمجية المخصَّصة في مستودعها باستخدام الأصناف التلقائية والتابع from_pretrained
، حيث تُفحَص جميع الملفات والشيفرات البرمجية المرفوعة إلى المستودع Hub بحثًا عن البرامج الضارة، ولمزيد من التفاصيل يُنصَح بمطالعة توثيق أمان Hub، ويجب أيضًا مراجعة شيفرة النموذج والتحقق من كاتبها لتجنّب تنفيذ شيفرة برمجية ضارة.
سنضبط القيمة trust_remote_code=True
لاستخدام نموذج مع شيفرة برمجية مخصصة كما يلي:
from transformers import AutoModelForImageClassification model = AutoModelForImageClassification.from_pretrained("sgugger/custom-resnet50d", trust_remote_code=True)
يُفضَّل أيضًا تمرير قيمة تعمية الإيداع Commit Hash إلى سمة المراجعة revision
للتأكّد من أن كاتب النماذج لم يُحدّث الشيفرة البرمجية ببعض الأسطر الجديدة الضارة.
commit_hash = "ed94a7c6247d8aedce4647f00f20de6875b5b292" model = AutoModelForImageClassification.from_pretrained( "sgugger/custom-resnet50d", trust_remote_code=True, revision=commit_hash )
نلاحظ وجود زر لنسخ قيمة تعمية الإيداع commit hash يمكننا من خلاله نسخ التعديل بسهولةعند تصفح سجل الإيداعات الخاص بمستودع النماذج الموجود على Hugging Face Hub.
الخلاصة
شرحنا في مقال اليوم كيفية كتابة نموذج مخصَّص وضبطه وطريقة استخدامه في مكتبة المحوّلات Transformers، كما شرحنا كيفية مشاركته مع المجتمع على مستودع Hugging Face Hub ليتمكّن أي شخص من استخدامه.
ترجمة -وبتصرّف- للقسم Building custom models من توثيقات Hugging Face.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.