# Классификация текста

Классификация текста — распространённая задача NLP, в которой тексту присваивается метка или класс. Крупнейшие компании используют классификацию текста в продакшене для широкого круга практических приложений. Одна из самых популярных форм классификации текста — анализ тональности, который присваивает последовательности текста метку, например 🙂 positive, 🙁 negative или 😐 neutral.

В этом руководстве показано, как:

1. Настройте [DistilBERT](https://huggingface.co/distilbert/distilbert-base-uncased) на датасете [IMDb](https://huggingface.co/datasets/stanfordnlp/imdb), чтобы определять, является ли отзыв о фильме положительным или отрицательным.
2. Используйте вашу дообученную модель для инференса.

Чтобы увидеть все архитектуры и контрольные точки, совместимые с этой задачей, рекомендуем обратиться к [странице задачи](https://huggingface.co/tasks/text-classification).

Прежде чем начать, убедитесь, что у вас установлены все необходимые библиотеки:

```bash
pip install transformers datasets evaluate accelerate
```

Мы рекомендуем вам войти в свой аккаунт Hugging Face, чтобы вы могли загрузить свою модель и поделиться ею с сообществом. При появлении запроса введите токен для входа:

```py
>>> from huggingface_hub import notebook_login

>>> notebook_login()
```

## Загрузка набора данных IMDb

Начните с загрузки набора данных IMDb из библиотеки 🤗 Datasets:

```py
>>> from datasets import load_dataset

>>> imdb = load_dataset("stanfordnlp/imdb")
```

Теперь рассмотрим пример:

```py
>>> imdb["test"][0]
{
    "label": 0,
    "text": "I love sci-fi and am willing to put up with a lot. Sci-fi movies/TV are usually underfunded, under-appreciated and misunderstood. I tried to like this, I really did, but it is to good TV sci-fi as Babylon 5 is to Star Trek (the original). Silly prosthetics, cheap cardboard sets, stilted dialogues, CG that doesn't match the background, and painfully one-dimensional characters cannot be overcome with a 'sci-fi' setting. (I'm sure there are those of you out there who think Babylon 5 is good sci-fi TV. It's not. It's clichéd and uninspiring.) While US viewers might like emotion and character development, sci-fi is a genre that does not take itself seriously (cf. Star Trek). It may treat important issues, yet not as a serious philosophy. It's really difficult to care about the characters here as they are not simply foolish, just missing a spark of life. Their actions and reactions are wooden and predictable, often painful to watch. The makers of Earth KNOW it's rubbish as they have to always say \"Gene Roddenberry's Earth...\" otherwise people would not continue watching. Roddenberry's ashes must be turning in their orbit as this dull, cheap, poorly edited (watching it without advert breaks really brings this home) trudging Trabant of a show lumbers into space. Spoiler. So, kill off a main character. And then bring him back as another actor. Jeeez! Dallas all over again.",
}
```

В этом наборе данных есть два поля:

- `text`: текст отзыва о фильме.
- `label`: значение, равное `0` для отрицательного отзыва или `1` для положительного отзыва.

## Предобработка

Следующий шаг — загрузить токенизатор DistilBERT для предобработки поля `text`:

```py
>>> from transformers import AutoTokenizer

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

Создайте функцию предобработки для токенизации `text` и обрезки последовательностей до максимальной длины входных данных DistilBERT:

```py
>>> def preprocess_function(examples):
...     return tokenizer(examples["text"], truncation=True)
```

Чтобы применить функцию предобработки ко всему набору данных, используйте функцию [map](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset.map) из 🤗 Datasets. Вы можете ускорить `map`, установив `batched=True`, чтобы обрабатывать несколько элементов набора данных одновременно:

```py
tokenized_imdb = imdb.map(preprocess_function, batched=True)
```

Теперь создайте батч примеров с помощью [DataCollatorWithPadding](/docs/transformers/main/ru/main_classes/data_collator#transformers.DataCollatorWithPadding). При сборке батча эффективнее *динамически дополнять* предложения до максимальной длины в рамках батча, чем дополнять весь датасет до максимальной длины.

```py
>>> from transformers import DataCollatorWithPadding

>>> data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
```

## Оценка

Включение метрики в процесс обучения часто помогает оценить производительность модели. Вы можете быстро загрузить метод оценки с помощью библиотеки 🤗 [Evaluate](https://huggingface.co/docs/evaluate/index). Для этой задачи загрузите метрику [accuracy](https://huggingface.co/spaces/evaluate-metric/accuracy) (см. [быстрый тур](https://huggingface.co/docs/evaluate/a_quick_tour) по 🤗 Evaluate, чтобы узнать больше о том, как загружать и вычислять метрику):

```py
>>> import evaluate

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

Затем создайте функцию, которая передаёт ваши предсказания и метки в [compute](https://huggingface.co/docs/evaluate/main/en/package_reference/main_classes#evaluate.EvaluationModule.compute) для вычисления точности:

```py
>>> import numpy as np

>>> def compute_metrics(eval_pred):
...     predictions, labels = eval_pred
...     predictions = np.argmax(predictions, axis=1)
...     return accuracy.compute(predictions=predictions, references=labels)
```

Ваша функция `compute_metrics` готова, и вы вернётесь к ней при настройке обучения.

## Обучение

Прежде чем начинать обучение модели, создайте сопоставление ожидаемых идентификаторов с их метками с помощью `id2label` и `label2id`:

```py
>>> id2label = {0: "NEGATIVE", 1: "POSITIVE"}
>>> label2id = {"NEGATIVE": 0, "POSITIVE": 1}
```

Если вы не знакомы с дообучением модели с помощью [Trainer](/docs/transformers/main/ru/main_classes/trainer#transformers.Trainer), загляните в базовое руководство [здесь](../training)!

Теперь вы готовы приступить к обучению модели! Загрузите DistilBERT с помощью [AutoModelForSequenceClassification](/docs/transformers/main/ru/model_doc/auto#transformers.AutoModelForSequenceClassification), указав количество ожидаемых меток и соответствия меток:

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

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

На этом этапе осталось выполнить только три шага:

1. Определите гиперпараметры обучения в [TrainingArguments](/docs/transformers/main/ru/main_classes/trainer#transformers.TrainingArguments). Единственный обязательный параметр — `output_dir`, который указывает, куда сохранять модель. Вы загрузите эту модель в Hub, установив `push_to_hub=True` (для загрузки модели необходимо войти в аккаунт Hugging Face). В конце каждой эпохи [Trainer](/docs/transformers/main/ru/main_classes/trainer#transformers.Trainer) будет оценивать точность и сохранять контрольную точку обучения.
2. Передайте аргументы обучения в [Trainer](/docs/transformers/main/ru/main_classes/trainer#transformers.Trainer) вместе с моделью, набором данных, токенизатором, data collator и функцией `compute_metrics`.
3. Вызовите [train()](/docs/transformers/main/ru/main_classes/trainer#transformers.Trainer.train), чтобы дообучить модель.

```py
>>> training_args = TrainingArguments(
...     output_dir="my_awesome_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_imdb["train"],
...     eval_dataset=tokenized_imdb["test"],
...     processing_class=tokenizer,
...     data_collator=data_collator,
...     compute_metrics=compute_metrics,
... )

>>> trainer.train()
```

[Trainer](/docs/transformers/main/ru/main_classes/trainer#transformers.Trainer) по умолчанию применяет динамическое выравнивание, если вы передаёте ему `tokenizer`. В этом случае явно указывать коллабор данных не нужно.

После завершения обучения используйте метод [push_to_hub()](/docs/transformers/main/ru/main_classes/trainer#transformers.Trainer.push_to_hub), чтобы опубликовать модель на Hub и дать всем возможность её использовать:

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

Более подробный пример дообучения модели для классификации текста можно найти в соответствующем [ноутбуке PyTorch](https://colab.research.google.com/github/huggingface/notebooks/blob/main/examples/text_classification.ipynb).

## Вывод

Отлично, теперь, когда вы дообучили модель, вы можете использовать её для инференса!

Подготовьте текст, для которого вы хотите выполнить инференс:

```py
>>> text = "This was a masterpiece. Not completely faithful to the books, but enthralling from beginning to end. Might be my favorite of the three."
```

Самый простой способ проверить вашу дообученную модель для инференса — использовать её в [pipeline()](/docs/transformers/main/ru/main_classes/pipelines#transformers.pipeline). Создайте экземпляр `pipeline` для анализа тональности с вашей моделью и передайте в него текст:

```py
>>> from transformers import pipeline

>>> classifier = pipeline("sentiment-analysis", model="stevhliu/my_awesome_model")
>>> classifier(text)
[{'label': 'POSITIVE', 'score': 0.9994940757751465}]
```

Вы также можете вручную воспроизвести результаты `pipeline`, если хотите:

Токенизируйте текст и верните тензоры PyTorch:

```py
>>> from transformers import AutoTokenizer

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

Передайте входные данные в модель и верните `logits`:

```py
>>> import torch
>>> from transformers import AutoModelForSequenceClassification

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

Получите класс с наибольшей вероятностью и используйте отображение `id2label` модели, чтобы преобразовать его в текстовую метку:

```py
>>> predicted_class_id = logits.argmax().item()
>>> model.config.id2label[predicted_class_id]
'POSITIVE'
```

