Пошаговое руководство, чтобы лучше понять, на что ориентируется ваша модель классификации текста при прогнозировании.

Введение:

В этом проекте мы будем изучать использование библиотеки PyTorch под названием Captum для создания инструмента интерпретируемости, применяемого к модели классификации анализа настроений. Это позволит нам визуализировать, какие части текста больше всего повлияли на вывод.

В первой части этого поста мы поговорим о модели классификации текста и данных, которые использовались для ее построения. Во второй части мы более подробно рассмотрим часть интерпретируемости с помощью Captum.

Код, чтобы попробовать это самостоятельно в блокноте Colab или GitHub, доступен в конце поста.

Во-первых, мы построим классическую модель анализа настроений, используя обзоры продуктов.

Данные:

Мы будем основывать наш подход на наборе данных отзывов, большей частью написанных на английском языке. Доступно здесь: Data Link и Data License

Данные содержат около шести миллионов отзывов, где каждый отзыв связан с текстом и рейтингом от 1 до 5 звезд.

Вот пример обзора:

Рейтинг: ⭐️⭐️⭐️⭐️⭐️
Текст:

That was a great book  !!!

Разделим отзывы на три группы:

  • Положительный: 4 или 5 ⭐️
  • Нейтрально: 3 ⭐️
  • Отрицательно: 1 или 2 ⭐️

Конвейер предсказания настроений:

Сначала мы разбиваем текст на токены, а затем на индексы токенов.

Например:

I loved that movie !!

Преобразуется в:

['[CLS]', 'i', 'loved', 'that', 'movie', '!', '!', '[SEP]']

А затем к:

[1, 76, 8459, 7923, 8332, 30, 30, 2]

Затем эти индексы сопоставляются с векторами в слое встраивания, а позиционные вложения добавляются поверх этого.

Эта последовательность векторов затем подается на кодер преобразователя.

Существует действительно хороший учебник о преобразовательной части конвейера классификации настроений здесь, я повторно использовал часть этого кода как есть для встраивания слов или позиционного встраивания.

Псевдокод модели выглядит так!

class Transformer(pl.LightningModule):
    def __init__(
        self,
        ...
    ):
        super().__init__()

        ...

        self.embeddings = TokenEmbedding(...)

        self.pos_encoder = PositionalEncoding(...)

        encoder_layer = torch.nn.TransformerEncoderLayer(
            ...)

        self.encoder = torch.nn.TransformerEncoder(
            ...
        )

        self.linear = torch.nn.Linear(channels, self.n_outputs)

        self.do = nn.Dropout(p=self.dropout)

    def encode(self, x):

        x = self.embeddings(x)
        x = self.pos_encoder(x)

        x = self.encoder(x)

        x = x[:, 0, :]

        return x

    def forward(self, x):
        x = self.do(self.encode(x))
        x = self.linear(x)

        return x

Это в основном все для части модели, довольно классическая для конвейера классификации текста.

Интерпретируемость:

Мы будем использовать библиотеку Captum, которая является библиотекой интерпретируемости для PyTorch. Мы будем использовать интегрированный градиентный подход, который я уже реализовал с нуля в одном из моих предыдущих проектов для Computer Vision:



Идея этого подхода заключается в том, что мы назначаем «атрибуцию» каждой из входных функций. Хитрость в случае НЛП заключается в том, что вместо самих токенов мы будем использовать их векторы слов.

Мы можем выполнять эту операцию только для одного класса за раз. Здесь мы выберем положительный класс, чтобы мы могли видеть, какая часть предложения увеличивает или уменьшает оценку «положительности».

Вот как это сделать с помощью Captum:

  • Во-первых, вам нужно получить слой внедрения вашей модели, model.embeddings.embedding в данном случае.
  • Во-вторых, нам нужно определить базовую линию. Здесь это будет предложение со всеми токенами заполнения: [CLS], [PAD], [PAD], …, [SEP]
  • Затем мы можем применить интегрированный градиентный подход:
tokenized = tokenize(text)
tokens_idx = tokenized.ids[:MAX_LEN]
x = torch.tensor([tokens_idx], dtype=torch.long)
ref = torch.tensor(...)

x = x.to(device)
ref = ref.to(device)

base_class = 2

lig = LayerIntegratedGradients(
    model,
    model.embeddings.embedding,
)

attributions_ig, delta = lig.attribute(
    x, ref, n_steps=500, return_convergence_delta=True, target=base_class
)

Давайте посмотрим на несколько примеров того, как это выглядит:

Мы можем видеть, какие слова или группы слов увеличили оценку «позитивности» зеленым цветом, а какие уменьшили ее — красным.

Заключение:

В этом посте мы увидели, как мы можем добавить некоторую интерпретируемость в наш конвейер классификации текста. Это действительно может быть полезно для лучшего понимания того, как наша модель делает прогнозы, либо для улучшения подхода, либо для добавления прозрачности в модель NLP черного ящика.

Вы можете попробовать это сами, используя блокнот colab или на GitHub, вы найдете обученную модель и код!

Блокнот: https://colab.research.google.com/drive/1SKitCFjbiZ3k7eL3UfMuKOISKmD_b9bc?authuser=3#scrollTo=9jqg0n0v0fDE

GitHub: https://github.com/CVxTz/interpretable_nlp