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

نمذجة اللغة المقنّعة

يتنبأ نمذجة اللغة المقنّعة (Masked language modeling) بالرمز المقنّع في تسلسل، ويمكن للنموذج أن ينتبه إلى الرموز باتجاهين. وهذا يعني أن النموذج لديه وصول كامل إلى الرموز الموجودة على اليسار واليمين. نمذجة اللغة المقنّعة ممتازة للمهام التي تتطلب فهماً سياقياً جيداً للتسلسل بأكمله. BERT هو مثال على نموذج لغة مقنّع.

ستُظهر لك هذه الدليل كيفية:

  1. قم بضبط DistilRoBERTa على مجموعة r/askscience الفرعية من مجموعة بيانات ELI5.
  2. استخدم النموذج المُحسَّن لديك للاستدلال.

للاطلاع على جميع البنى المعمارية ونقاط التفتيش المتوافقة مع هذه المهمة، ننصح بالتحقق من صفحة المهمة

قبل أن تبدأ، تأكد من تثبيت جميع المكتبات اللازمة:

pip install transformers datasets evaluate

ننصحك بتسجيل الدخول إلى حسابك على Hugging Face حتى تتمكن من رفع نموذجك ومشاركته مع المجتمع. عند الطلب، أدخل رمزك (token) لتسجيل الدخول:

>>> from huggingface_hub import notebook_login

>>> notebook_login()

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

ابدأ بتحميل أول 5000 مثال من مجموعة بيانات ELI5-Category باستخدام مكتبة 🤗 Datasets. سيمنحك هذا فرصة للتجربة والتأكد من عمل كل شيء بشكل صحيح قبل قضاء مزيد من الوقت في التدريب على المجموعة الكاملة.

>>> from datasets import load_dataset

>>> eli5 = load_dataset("dany0407/eli5_category", split="train[:5000]")

قسّم مجموعة train من مجموعة البيانات إلى مجموعة تدريب ومجموعة اختبار باستخدام طريقة train_test_split:

>>> eli5 = eli5.train_test_split(test_size=0.2)

ثم اطّلع على مثال:

>>> eli5["train"][0]
{'q_id': '7h191n',
 'title': 'What does the tax bill that was passed today mean? How will it affect Americans in each tax bracket?',
 'selftext': '',
 'category': 'Economics',
 'subreddit': 'explainlikeimfive',
 'answers': {'a_id': ['dqnds8l', 'dqnd1jl', 'dqng3i1', 'dqnku5x'],
  'text': ["The tax bill is 500 pages long and there were a lot of changes still going on right to the end. It's not just an adjustment to the income tax brackets, it's a whole bunch of changes. As such there is no good answer to your question. The big take aways are: - Big reduction in corporate income tax rate will make large companies very happy. - Pass through rate change will make certain styles of business (law firms, hedge funds) extremely happy - Income tax changes are moderate, and are set to expire (though it's the kind of thing that might just always get re-applied without being made permanent) - People in high tax states (California, New York) lose out, and many of them will end up with their taxes raised.",
   'None yet. It has to be reconciled with a vastly different house bill and then passed again.',
   'Also: does this apply to 2017 taxes? Or does it start with 2018 taxes?',
   'This article explains both the House and senate bills, including the proposed changes to your income taxes based on your income level. URL_0'],
  'score': [21, 19, 5, 3],
  'text_urls': [[],
   [],
   [],
   ['https://www.investopedia.com/news/trumps-tax-reform-what-can-be-done/']]},
 'title_urls': ['url'],
 'selftext_urls': ['url']}

قد يبدو هذا كثيرًا، لكنك مهتم فعليًا بحقل text فقط. ما هو مثير للاهتمام في مهام نمذجة اللغة أنك لا تحتاج إلى تسميات (أي أنها مهمة غير مُشرَّفة)، لأن الكلمة التالية هي التسمية.

المعالجة المسبقة

لنمذجة اللغة المقنّعة، الخطوة التالية هي تحميل مُجزّئ DistilRoBERTa لمعالجة الحقل الفرعي text:

>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("distilbert/distilroberta-base")

ستلاحظ من المثال أعلاه أن حقل text هو في الواقع متداخل داخل answers. وهذا يعني أنك ستحتاج إلى استخراج الحقل الفرعي text من بنيته المتداخلة باستخدام طريقة flatten:

>>> eli5 = eli5.flatten()
>>> eli5["train"][0]
{'q_id': '7h191n',
 'title': 'What does the tax bill that was passed today mean? How will it affect Americans in each tax bracket?',
 'selftext': '',
 'category': 'Economics',
 'subreddit': 'explainlikeimfive',
 'answers.a_id': ['dqnds8l', 'dqnd1jl', 'dqng3i1', 'dqnku5x'],
 'answers.text': ["The tax bill is 500 pages long and there were a lot of changes still going on right to the end. It's not just an adjustment to the income tax brackets, it's a whole bunch of changes. As such there is no good answer to your question. The big take aways are: - Big reduction in corporate income tax rate will make large companies very happy. - Pass through rate change will make certain styles of business (law firms, hedge funds) extremely happy - Income tax changes are moderate, and are set to expire (though it's the kind of thing that might just always get re-applied without being made permanent) - People in high tax states (California, New York) lose out, and many of them will end up with their taxes raised.",
  'None yet. It has to be reconciled with a vastly different house bill and then passed again.',
  'Also: does this apply to 2017 taxes? Or does it start with 2018 taxes?',
  'This article explains both the House and senate bills, including the proposed changes to your income taxes based on your income level. URL_0'],
 'answers.score': [21, 19, 5, 3],
 'answers.text_urls': [[],
  [],
  [],
  ['https://www.investopedia.com/news/trumps-tax-reform-what-can-be-done/']],
 'title_urls': ['url'],
 'selftext_urls': ['url']}

أصبح كل حقل فرعي عمودًا مستقلًا كما يشير بادئة answers، وأصبح الحقل text قائمة. بدلاً من تحويل كل جملة إلى رموز بشكل منفصل، قم بتحويل القائمة إلى نص حتى تتمكن من تحويلها إلى رموز معًا.

إليك أول دالة للمعالجة المسبقة لدمج قائمة النصوص لكل مثال وتحويل النتيجة إلى رموز:

>>> def preprocess_function(examples):
...     return tokenizer([" ".join(x) for x in examples["answers.text"]])

لتطبيق دالة المعالجة المسبقة هذه على مجموعة البيانات بأكملها، استخدم طريقة map في 🤗 Datasets. يمكنك تسريع دالة map عن طريق تعيين batched=True لمعالجة عدة عناصر من مجموعة البيانات في وقت واحد، وزيادة عدد العمليات باستخدام num_proc. احذف أي أعمدة لا تحتاج إليها:

>>> tokenized_eli5 = eli5.map(
...     preprocess_function,
...     batched=True,
...     num_proc=4,
...     remove_columns=eli5["train"].column_names,
... )

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

يمكنك الآن استخدام دالة معالجة مسبقة ثانية ل

  • دمج جميع التسلسلات
  • قسّم التسلسلات المدمجة إلى مقاطع أقصر تُعرَّف بواسطة block_size، والتي يجب أن تكون أقصر من الحد الأقصى لطول الإدخال وفي الوقت نفسه قصيرة بما يكفي لتناسب ذاكرة وحدة معالجة الرسوميات (GPU) لديك.
>>> block_size = 128


>>> def group_texts(examples):
...     # Concatenate all texts.
...     concatenated_examples = {k: sum(examples[k], []) for k in examples.keys()}
...     total_length = len(concatenated_examples[list(examples.keys())[0]])
...     # We drop the small remainder, we could add padding if the model supported it instead of this drop, you can
...     # customize this part to your needs.
...     if total_length >= block_size:
...         total_length = (total_length // block_size) * block_size
...     # Split by chunks of block_size.
...     result = {
...         k: [t[i : i + block_size] for i in range(0, total_length, block_size)]
...         for k, t in concatenated_examples.items()
...     }
...     return result

طبّق دالة group_texts على كامل مجموعة البيانات:

>>> lm_dataset = tokenized_eli5.map(group_texts, batched=True, num_proc=4)

الآن أنشئ دفعة من الأمثلة باستخدام DataCollatorForLanguageModeling. من الكفاءة أن نملأ الجمل ديناميكياً حتى أطول طول في الدفعة أثناء التجميع، بدلاً من ملء مجموعة البيانات بأكملها حتى الطول الأقصى.

استخدم رمز نهاية التسلسل كرمز حشو، وحدد mlm_probability لإخفاء الرموز عشوائياً في كل مرة تتكرر فيها على البيانات:

>>> from transformers import DataCollatorForLanguageModeling

>>> tokenizer.pad_token = tokenizer.eos_token
>>> data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm_probability=0.15)

تدريب

إذا لم تكن على دراية بتدريب نموذج باستخدام Trainer، فاطّلع على الدرس الأساسي هنا!

أنت مستعد الآن لبدء تدريب نموذجك! حمّل DistilRoBERTa باستخدام AutoModelForMaskedLM:

>>> from transformers import AutoModelForMaskedLM

>>> model = AutoModelForMaskedLM.from_pretrained("distilbert/distilroberta-base")

في هذه المرحلة، تبقى ثلاث خطوات فقط:

  1. حدّد معاملات التدريب الفائقة في TrainingArguments. المعامل الوحيد الإلزامي هو output_dir الذي يحدد مكان حفظ النموذج. ستقوم بدفع هذا النموذج إلى Hub عن طريق تعيين push_to_hub=True (يتعين عليك تسجيل الدخول إلى Hugging Face لرفع النموذج).
  2. مرّر حُجج التدريب إلى Trainer مع النموذج ومجموعات البيانات ومُجمِّع البيانات.
  3. استدعِ train() لضبط نموذجك.
>>> training_args = TrainingArguments(
...     output_dir="my_awesome_eli5_mlm_model",
...     eval_strategy="epoch",
...     learning_rate=2e-5,
...     num_train_epochs=3,
...     weight_decay=0.01,
...     push_to_hub=True,
... )

>>> trainer = Trainer(
...     model=model,
...     args=training_args,
...     train_dataset=lm_dataset["train"],
...     eval_dataset=lm_dataset["test"],
...     data_collator=data_collator,
...     processing_class=tokenizer,
... )

>>> trainer.train()

بمجرد اكتمال التدريب، استخدم طريقة evaluate() لتقييم نموذجك والحصول على قيمة الحيرة (perplexity) الخاصة به:

>>> import math

>>> eval_results = trainer.evaluate()
>>> print(f"Perplexity: {math.exp(eval_results['eval_loss']):.2f}")
Perplexity: 8.76

ثم شارك نموذجك على Hub باستخدام طريقة push_to_hub() ليتمكن الجميع من استخدام نموذجك:

>>> trainer.push_to_hub()

لمثال أكثر تفصيلاً حول كيفية ضبط نموذج لغوي مقنّع، راجع دفتر PyTorch المقابل.

الاستدلال

رائع، والآن بعد أن قمت بضبط نموذج، يمكنك استخدامه للاستدلال!

فكّر في نصّ تريد أن يملأ النموذج الفراغ فيه، واستخدم الرمز الخاص <mask> للإشارة إلى موضع الفراغ:

>>> text = "The Milky Way is a <mask> galaxy."

أبسط طريقة لتجربة نموذجك المُحسَّن للاستدلال هي استخدامه في pipeline(). أنشئ كائن pipeline لمهمة fill-mask باستخدام نموذجك، ثم مرر النص إليه. وإذا رغبت، يمكنك استخدام معامل top_k لتحديد عدد التنبؤات التي يجب إرجاعها:

>>> from transformers import pipeline

>>> mask_filler = pipeline("fill-mask", "username/my_awesome_eli5_mlm_model")
>>> mask_filler(text, top_k=3)
[{'score': 0.5150994658470154,
  'token': 21300,
  'token_str': ' spiral',
  'sequence': 'The Milky Way is a spiral galaxy.'},
 {'score': 0.07087188959121704,
  'token': 2232,
  'token_str': ' massive',
  'sequence': 'The Milky Way is a massive galaxy.'},
 {'score': 0.06434620916843414,
  'token': 650,
  'token_str': ' small',
  'sequence': 'The Milky Way is a small galaxy.'}]

قم بتجزئة النص وإرجاع input_ids كمُتجهات PyTorch. كما ستحتاج إلى تحديد موضع رمز <mask>:

>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("username/my_awesome_eli5_mlm_model")
>>> inputs = tokenizer(text, return_tensors="pt")
>>> mask_token_index = torch.where(inputs["input_ids"] == tokenizer.mask_token_id)[1]

مرّر مدخلاتك إلى النموذج وأعد logits للرمز المقنّع:

>>> from transformers import AutoModelForMaskedLM

>>> model = AutoModelForMaskedLM.from_pretrained("username/my_awesome_eli5_mlm_model")
>>> logits = model(**inputs).logits
>>> mask_token_logits = logits[0, mask_token_index, :]

ثم أعد الرموز الثلاثة المقنّعة ذات أعلى احتمال وطبعها:

>>> top_3_tokens = torch.topk(mask_token_logits, 3, dim=1).indices[0].tolist()

>>> for token in top_3_tokens:
...     print(text.replace(tokenizer.mask_token, tokenizer.decode([token])))
The Milky Way is a spiral galaxy.
The Milky Way is a massive galaxy.
The Milky Way is a small galaxy.
Update on GitHub