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

تصنيف الرموز

تُخصِّص تصنيف الرموز (Token classification) تسميةً لكل رمز فردي في الجملة. من أكثر مهام تصنيف الرموز شيوعاً هو التعرف على الكيانات المسمّاة (NER). يحاول NER إيجاد تسمية لكل كيان في الجملة، مثل شخص أو موقع أو منظمة.

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

  1. قم بتدريب DistilBERT على مجموعة بيانات WNUT 17 للكشف عن كيانات جديدة.
  2. استخدم النموذج المُحسَّن لديك للاستدلال.

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

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

pip install transformers datasets evaluate seqeval

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

>>> from huggingface_hub import notebook_login

>>> notebook_login()

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

ابدأ بتحميل مجموعة بيانات WNUT 17 من مكتبة 🤗 Datasets:

>>> from datasets import load_dataset

>>> wnut = load_dataset("flaitenberger/wnut_17")

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

>>> wnut["train"][0]
{'id': '0',
 'ner_tags': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0],
 'tokens': ['@paulwalk', 'It', "'s", 'the', 'view', 'from', 'where', 'I', "'m", 'living', 'for', 'two', 'weeks', '.', 'Empire', 'State', 'Building', '=', 'ESB', '.', 'Pretty', 'bad', 'storm', 'here', 'last', 'evening', '.']
}

يمثل كل رقم في ner_tags كيانًا. قم بتحويل الأرقام إلى أسماء تسمياتها لمعرفة ما هي الكيانات:

>>> label_list = wnut["train"].features[f"ner_tags"].feature.names
>>> label_list
[
    "O",
    "B-corporation",
    "I-corporation",
    "B-creative-work",
    "I-creative-work",
    "B-group",
    "I-group",
    "B-location",
    "I-location",
    "B-person",
    "I-person",
    "B-product",
    "I-product",
]

الحرف الذي يسبق كل ner_tag يشير إلى موضع الرمز ضمن الكيان:

  • B- يشير إلى بداية الكيان.
  • يشير I- إلى أن الرمز (token) مضمن داخل الكيان نفسه (على سبيل المثال، رمز State هو جزء من كيان مثل Empire State Building).
  • 0 يشير إلى أن الرمز لا يتوافق مع أي كيان.

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

الخطوة التالية هي تحميل مُجزِّئ DistilBERT لمعالجة حقل tokens مسبقاً:

>>> from transformers import AutoTokenizer

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

كما رأيت في حقل tokens في المثال أعلاه، يبدو أن الإدخال تم تقطيعه مسبقًا. لكن الإدخال لم يتم تقطيعه بعد، وستحتاج إلى تعيين is_split_into_words=True لتقطيع الكلمات إلى مقاطع فرعية. على سبيل المثال:

>>> example = wnut["train"][0]
>>> tokenized_input = tokenizer(example["tokens"], is_split_into_words=True)
>>> tokens = tokenizer.convert_ids_to_tokens(tokenized_input["input_ids"])
>>> tokens
['[CLS]', '@', 'paul', '##walk', 'it', "'", 's', 'the', 'view', 'from', 'where', 'i', "'", 'm', 'living', 'for', 'two', 'weeks', '.', 'empire', 'state', 'building', '=', 'es', '##b', '.', 'pretty', 'bad', 'storm', 'here', 'last', 'evening', '.', '[SEP]']

ومع ذلك، يضيف هذا بعض الرموز الخاصة [CLS] و[SEP]، ويؤدي تقسيم الكلمات الفرعية إلى عدم تطابق بين المدخلات والتسميات. فقد يتم تقسيم كلمة واحدة تتوافق مع تسمية واحدة إلى كلمتين فرعيتين. ستحتاج إلى إعادة محاذاة الرموز والتسميات من خلال:

  1. ربط جميع الرموز بالكلمة المقابلة لها باستخدام طريقة word_ids.
  2. تعيين التسمية -100 للرموز الخاصة [CLS] و[SEP] حتى يتم تجاهلها بواسطة دالة الخسارة في PyTorch (انظر CrossEntropyLoss).
  3. تعيين التسمية لأول رمز فقط من كلمة معينة. تعيين -100 لباقي الرموز الفرعية من نفس الكلمة.

إليك كيفية إنشاء دالة لإعادة محاذاة الرموز والوسوم، وقصّ التسلسلات بحيث لا تتجاوز الحد الأقصى لطول الإدخال في DistilBERT:

>>> def tokenize_and_align_labels(examples):
...     tokenized_inputs = tokenizer(examples["tokens"], truncation=True, is_split_into_words=True)

...     labels = []
...     for i, label in enumerate(examples[f"ner_tags"]):
...         word_ids = tokenized_inputs.word_ids(batch_index=i)  # Map tokens to their respective word.
...         previous_word_idx = None
...         label_ids = []
...         for word_idx in word_ids:  # Set the special tokens to -100.
...             if word_idx is None:
...                 label_ids.append(-100)
...             elif word_idx != previous_word_idx:  # Only label the first token of a given word.
...                 label_ids.append(label[word_idx])
...             else:
...                 label_ids.append(-100)
...             previous_word_idx = word_idx
...         labels.append(label_ids)

...     tokenized_inputs["labels"] = labels
...     return tokenized_inputs

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

>>> tokenized_wnut = wnut.map(tokenize_and_align_labels, batched=True)

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

>>> from transformers import DataCollatorForTokenClassification

>>> data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer)

تقييم

غالباً ما يكون تضمين مقياس أثناء التدريب مفيداً لتقييم أداء نموذجك. يمكنك تحميل طريقة تقييم بسرعة باستخدام مكتبة 🤗 Evaluate. لهذه المهمة، حمّل إطار عمل seqeval (راجع الجولة السريعة في 🤗 Evaluate لمعرفة المزيد حول كيفية تحميل مقياس وحسابه). في الواقع، ينتج Seqeval عدة درجات: الدقة (precision)، والاستدعاء (recall)، وF1، والدقة (accuracy).

>>> import evaluate

>>> seqeval = evaluate.load("seqeval")

احصل على تسميات NER أولاً، ثم أنشئ دالة تمرّر تنبؤاتك الصحيحة وتسمياتك الحقيقية إلى compute لحساب الدرجات:

>>> import numpy as np

>>> labels = [label_list[i] for i in example[f"ner_tags"]]


>>> def compute_metrics(p):
...     predictions, labels = p
...     predictions = np.argmax(predictions, axis=2)

...     true_predictions = [
...         [label_list[p] for (p, l) in zip(prediction, label) if l != -100]
...         for prediction, label in zip(predictions, labels)
...     ]
...     true_labels = [
...         [label_list[l] for (p, l) in zip(prediction, label) if l != -100]
...         for prediction, label in zip(predictions, labels)
...     ]

...     results = seqeval.compute(predictions=true_predictions, references=true_labels)
...     return {
...         "precision": results["overall_precision"],
...         "recall": results["overall_recall"],
...         "f1": results["overall_f1"],
...         "accuracy": results["overall_accuracy"],
...     }

دالة compute_metrics الخاصة بك جاهزة الآن، وستعود إليها عند إعداد التدريب.

تدريب

قبل أن تبدأ في تدريب نموذجك، أنشئ خريطة للـ ids المتوقعة إلى تسمياتها باستخدام id2label وlabel2id:

>>> id2label = {
...     0: "O",
...     1: "B-corporation",
...     2: "I-corporation",
...     3: "B-creative-work",
...     4: "I-creative-work",
...     5: "B-group",
...     6: "I-group",
...     7: "B-location",
...     8: "I-location",
...     9: "B-person",
...     10: "I-person",
...     11: "B-product",
...     12: "I-product",
... }
>>> label2id = {
...     "O": 0,
...     "B-corporation": 1,
...     "I-corporation": 2,
...     "B-creative-work": 3,
...     "I-creative-work": 4,
...     "B-group": 5,
...     "I-group": 6,
...     "B-location": 7,
...     "I-location": 8,
...     "B-person": 9,
...     "I-person": 10,
...     "B-product": 11,
...     "I-product": 12,
... }

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

أنت مستعد الآن لبدء تدريب نموذجك! حمّل DistilBERT باستخدام AutoModelForTokenClassification مع عدد التسميات المتوقعة، وتعيينات التسميات:

>>> from transformers import AutoModelForTokenClassification, TrainingArguments, Trainer

>>> model = AutoModelForTokenClassification.from_pretrained(
...     "distilbert/distilbert-base-uncased", num_labels=13, id2label=id2label, label2id=label2id
... )

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

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

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

>>> trainer.train()

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

>>> trainer.push_to_hub()

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

الاستدلال

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

احصل على نص تريد تشغيل الاستدلال عليه:

>>> text = "The Golden State Warriors are an American professional basketball team based in San Francisco."

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

>>> from transformers import pipeline

>>> classifier = pipeline("ner", model="stevhliu/my_awesome_wnut_model")
>>> classifier(text)
[{'entity': 'B-location',
  'score': 0.42658573,
  'index': 2,
  'word': 'golden',
  'start': 4,
  'end': 10},
 {'entity': 'I-location',
  'score': 0.35856336,
  'index': 3,
  'word': 'state',
  'start': 11,
  'end': 16},
 {'entity': 'B-group',
  'score': 0.3064001,
  'index': 4,
  'word': 'warriors',
  'start': 17,
  'end': 25},
 {'entity': 'B-location',
  'score': 0.65523505,
  'index': 13,
  'word': 'san',
  'start': 80,
  'end': 83},
 {'entity': 'B-location',
  'score': 0.4668663,
  'index': 14,
  'word': 'francisco',
  'start': 84,
  'end': 93}]

يمكنك أيضًا إعادة إنتاج نتائج pipeline يدويًا إذا رغبت في ذلك:

قم بتجزئة النص وإرجاع متجهات PyTorch:

>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("stevhliu/my_awesome_wnut_model")
>>> inputs = tokenizer(text, return_tensors="pt")

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

>>> from transformers import AutoModelForTokenClassification

>>> model = AutoModelForTokenClassification.from_pretrained("stevhliu/my_awesome_wnut_model")
>>> with torch.no_grad():
...     logits = model(**inputs).logits

احصل على الفئة ذات أعلى احتمال، واستخدم خرائط id2label الخاصة بالنموذج لتحويلها إلى تسمية نصية:

>>> predictions = torch.argmax(logits, dim=2)
>>> predicted_token_class = [model.config.id2label[t.item()] for t in predictions[0]]
>>> predicted_token_class
['O',
 'O',
 'B-location',
 'I-location',
 'B-group',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'O',
 'B-location',
 'B-location',
 'O',
 'O']
Update on GitHub