# تصنيف الرموز

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

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

1. قم بتدريب [DistilBERT](https://huggingface.co/distilbert/distilbert-base-uncased) على مجموعة بيانات [WNUT 17](https://huggingface.co/datasets/flaitenberger/wnut_17) للكشف عن كيانات جديدة.
2. استخدم النموذج المُحسَّن لديك للاستدلال.

للاطلاع على جميع البنى المعمارية ونقاط التفتيش المتوافقة مع هذه المهمة، ننصح بالاطلاع على [صفحة المهمة](https://huggingface.co/tasks/token-classification).

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

```bash
pip install transformers datasets evaluate seqeval
```

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

```py
>>> from huggingface_hub import notebook_login

>>> notebook_login()
```

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

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

```py
>>> from datasets import load_dataset

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

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

```py
>>> 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` كيانًا. قم بتحويل الأرقام إلى أسماء تسمياتها لمعرفة ما هي الكيانات:

```py
>>> 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` مسبقاً:

```py
>>> from transformers import AutoTokenizer

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

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

```py
>>> 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]`، كما أن تقسيم الكلمات الفرعية (subword tokenization) يُحدث عدم تطابق بين المدخلات والتسميات. فقد يتم تقسيم كلمة واحدة تتوافق مع تسمية واحدة إلى كلمتين فرعيتين. ستحتاج إلى إعادة محاذاة الرموز والتسميات من خلال:

1. ربط جميع الرموز بالكلمة المقابلة لها باستخدام طريقة [word_ids](/docs/transformers/main/ar/main_classes/tokenizer#transformers.BatchEncoding.word_ids).
2. تعيين التسمية `-100` للرموز الخاصة `[CLS]` و`[SEP]` حتى يتم تجاهلها بواسطة دالة الخسارة في PyTorch (انظر [CrossEntropyLoss](https://pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html)).
3. تعيين التسمية لأول رمز فقط من كلمة معينة. تعيين `-100` لباقي الرموز الفرعية من نفس الكلمة.

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

```py
>>> 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](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset.map) في 🤗 Datasets. يمكنك تسريع دالة `map` عن طريق تعيين `batched=True` لمعالجة عدة عناصر من مجموعة البيانات في وقت واحد:

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

الآن أنشئ دفعة من الأمثلة باستخدام [DataCollatorWithPadding](/docs/transformers/main/ar/main_classes/data_collator#transformers.DataCollatorWithPadding). من الكفاءة أن *نملأ الجمل ديناميكيًا* حتى أطول طول في الدفعة أثناء التجميع، بدلاً من ملء مجموعة البيانات بأكملها حتى الطول الأقصى.

```py
>>> from transformers import DataCollatorForTokenClassification

>>> data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer)
```

## تقييم

غالباً ما يكون تضمين مقياس أثناء التدريب مفيداً لتقييم أداء نموذجك. يمكنك تحميل طريقة تقييم بسرعة باستخدام مكتبة 🤗 [Evaluate](https://huggingface.co/docs/evaluate/index). لهذه المهمة، حمّل إطار عمل [seqeval](https://huggingface.co/spaces/evaluate-metric/seqeval) (راجع [الجولة السريعة](https://huggingface.co/docs/evaluate/a_quick_tour) في 🤗 Evaluate لمعرفة المزيد حول كيفية تحميل مقياس وحسابه). في الواقع، ينتج Seqeval عدة درجات: الدقة (precision)، والاستدعاء (recall)، وF1، والدقة (accuracy).

```py
>>> import evaluate

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

احصل على تسميات NER أولاً، ثم أنشئ دالة تمرّر تنبؤاتك الصحيحة وتسمياتك الحقيقية إلى [compute](https://huggingface.co/docs/evaluate/main/en/package_reference/main_classes#evaluate.EvaluationModule.compute) لحساب الدرجات:

```py
>>> 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`:

```py
>>> 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](/docs/transformers/main/ar/main_classes/trainer#transformers.Trainer)، فاطّلع على الدرس الأساسي [هنا](../training)!

أنت مستعد الآن لبدء تدريب نموذجك! حمّل DistilBERT باستخدام [AutoModelForTokenClassification](/docs/transformers/main/ar/model_doc/auto#transformers.AutoModelForTokenClassification) مع عدد التسميات المتوقعة، وتعيينات التسميات:

```py
>>> from transformers import AutoModelForTokenClassification, TrainingArguments, Trainer

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

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

1. حدد معاملات التدريب الفائقة في [TrainingArguments](/docs/transformers/main/ar/main_classes/trainer#transformers.TrainingArguments). المعامل الوحيد الإلزامي هو `output_dir` الذي يحدد مكان حفظ النموذج. ستقوم بدفع هذا النموذج إلى Hub من خلال تعيين `push_to_hub=True` (يتعين عليك تسجيل الدخول إلى Hugging Face لرفع النموذج). في نهاية كل حقبة، سيقوم [Trainer](/docs/transformers/main/ar/main_classes/trainer#transformers.Trainer) بتقييم درجات seqeval وحفظ نقطة الفحص للتدريب.
2. مرّر حُجج التدريب إلى [Trainer](/docs/transformers/main/ar/main_classes/trainer#transformers.Trainer) مع النموذج، ومجموعة البيانات، ومُجزّئ الرموز، ومُجمِّع البيانات، ودالة `compute_metrics`.
3. استدعِ [train()](/docs/transformers/main/ar/main_classes/trainer#transformers.Trainer.train) لضبط نموذجك.

```py
>>> 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()](/docs/transformers/main/ar/main_classes/trainer#transformers.Trainer.push_to_hub) ليتمكن الجميع من استخدام نموذجك:

```py
>>> trainer.push_to_hub()
```

لمثال أكثر تفصيلاً حول كيفية ضبط نموذج لتصنيف الرموز، راجع [دفتر PyTorch](https://colab.research.google.com/github/huggingface/notebooks/blob/main/examples/token_classification.ipynb) المقابل.

## الاستدلال

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

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

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

أبسط طريقة لتجربة نموذجك المُحسَّن للاستدلال هي استخدامه في [pipeline()](/docs/transformers/main/ar/main_classes/pipelines#transformers.pipeline). أنشئ كائن `pipeline` لتحديد الكيانات (NER) باستخدام نموذجك، ثم مرر النص إليه:

```py
>>> 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:

```py
>>> from transformers import AutoTokenizer

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

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

```py
>>> from transformers import AutoModelForTokenClassification

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

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

```py
>>> 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']
```

