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

استخدام خطوط الأنابيب Pipelines للاستدلال في تطبيقات الذكاء الاصطناعي


رشا سعد

نشرح في هذا المقال التعامل مع خطوط الأنابيب pipeline()‎ التي توفر طريقة سهلة وموحدة للتعامل مع نماذج الذكاء الاصطناعي الموجودة في مستودع Hugging Face إذ يمكنك استخدامها للاستدلال inference وتوقع معلومات جديدة بناءً على المعلومات المتوفرة بأي أسلوب معالجة، سواء كان يعتمد على فهم اللغة language أو فهم الكلام speech أو الرؤية الحاسوبية computer vision أو الأنماط المتعددة multimodal.

فمع خطوط الأنابيب المتاحة في مكتبة transformers لن تحتاج معرفةً قوية بالنماذج المخصصة لنمط معين ولا بطريقة كتابة شيفراتها البرمجية فهي ستسهل عليك المهمة، وهذا ما نعرضه في هذا المقال إذ سنتناول النقاط التالية:

  • كيفية استخدام خطوط الأنابيب للاستدلال.

  • طريقة استخدام مُرَمِّز tokenizer محدد أو نموذج model محدد.

  • آلية استخدام خطوط الأنابيب لمشاريع معالجة الصوتيات والرؤية والأنماط المتعددة multimodal التي تتضمن التعامل مع بيانات متعددة الأنماط مثل الأصوات والصور معًا.

اقتباس

يمكنك الاطلاع على جميع المهام التي تدعمها خطوط الأنابيب بالاطلاع على القسم الخاص بها من توثيقات منصة Hugging Face.

استخدام خطوط الأنابيب Pipeline

يوجد خط أنابيب pipeline()‎ خاص بكل مهمة، إلّا أن استخدام خط الأنابيب العام general pipeline (وهو تجريد يضم خطوط الأنابيب الخاصة بجميع المهام) يوفر طريقةً أسهل وأكثر مرونة، إذ يُحَمِّل نموذجًا افتراضيًا وصنف معالجة مسبقة للاستدلال مناسبين لطبيعة مشروعك.

يبين مثالنا التالي طريقة استخدام خط الأنابيب للتَعَرُّف التلقائي على الكلام (ASR) أي تحويله إلى نص، لتنفيذ هذا التطبيق حاول تطبيق الخطوات في بيئتك التجريبية:

  1. أنشئ خط الأنابيب 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. لنفترض مثلًا أن مهمتنا هي اكتشاف السلالة التي تنتمي إليها القطة في الصورة التالية:

img01 pipeline vision

فستكون الشيفرة البرمجية كما يلي:

>>> 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.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...