В предыдущем посте вы получили обзор интерпретируемости и различных объяснений, доступных в инструменте Интерпретировать текст.
В этом посте вы узнаете, как использовать один из объяснителей: Unified Information Explainer.

Unified Information Explainer может использоваться, когда требуется единое и понятное объяснение уровней преобразования, объединения и классификации конкретной модели глубокой обработки естественного языка (NLP).

Предварительной обработкой текста занимается объяснитель, предложения токенизируются BERT Tokenizer. Во время написания статьи разработчик должен предоставить Unified Information Explainer обученную или точно настроенную модель двунаправленных представлений кодировщика из трансформаторов (BERT) с образцами обученных данных. Поддержка рекуррентной нейронной сети (RNN) и долговременной краткосрочной памяти (LSTM) также будет реализована в будущем.

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

Настройка среды

Чтобы следовать руководству, установите Anaconda с Python 3.7 и откройте Anaconda Prompt. Это можно сделать на локальном компьютере или на виртуальной машине. Получите репозиторий, чтобы начать работу, и перейдите в созданную папку.

git clone https://github.com/interpretml/interpret-text.git
cd interpret-text

Следующим шагом является подготовка среды для включения всех необходимых пакетов.

For CPU:
python tools/generate_conda_files.py
conda env create -n interpret_cpu --file=interpret_cpu.yaml
conda activate interpret_cpu
For GPU:
python tools/generate_conda_files.py --gpu
conda env create -n interpret_gpu --file=interpret_gpu.yaml
conda activate interpret_gpu

Пакеты Python также должны быть установлены вместе с виджетом, который требуется для панели инструментов.

cd pythonpip install -e .
jupyter nbextension install interpret_text.experimental.widget --py --sys-prefix
jupyter nbextension enable interpret_text.experimental.widget --py --sys-prefix

И, наконец, установите записную книжку, в которой будет выполняться код Python, затем запустите записную книжку в своем любимом браузере.

cd..
pip install notebook
jupyter notebook

Окружение теперь должно выглядеть примерно так:

Реализация

Создайте новый блокнот с Python 3, нажав кнопку НОВИНКА в правом верхнем углу экрана. Пользователь должен увидеть среду, как на картинке ниже.

Среда Notebook и рабочий каталог определяются с помощью следующего фрагмента кода. Не забывайте использовать matplotlib, иначе некоторая графика может работать некорректно.

%matplotlib inline
import sys
import os
sys.path.append("../..")

Здесь определены некоторые переменные, которые будут использоваться для обучения. Можно уменьшить время выполнения, установив для параметра БЫСТРЫЙ ВЫПОЛНЕНИЕ значение True. Обратите внимание, что это повлияет на производительность модели. Кроме того, размер пакета можно установить в зависимости от конфигурации устройства, на котором будет выполняться обучение. Используйте функцию is_available () PyTorch.

QUICK_RUN = True
TRAIN_DATA_FRACTION = 1
TEST_DATA_FRACTION = 1
NUM_EPOCHS = 1
if QUICK_RUN:
    TRAIN_DATA_FRACTION = 0.001
    TEST_DATA_FRACTION = 0.001
    NUM_EPOCHS = 1
import torch
import torch.nn as nn
    
if torch.cuda.is_available():
    BATCH_SIZE = 1
else:
    BATCH_SIZE = 9

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

import pandas as pd
from notebooks.test_utils.utils_mnli import load_mnli_pandas_df
df = load_mnli_pandas_df("./temp", "train")
df = df[df["gold_label"]=="neutral"]

Вот фрагмент набора данных, показывающий метки (жанр) и функции (предложение1).

df[["genre", "sentence1"]].head()

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

df["genre"].value_counts()

Теперь видно, что данные вроде как сбалансированы, давайте воспользуемся этим для обучения. Теперь данные будут разделены с помощью функции train_test_split (), а LabelEncoder программы sklearn будет использоваться для предварительной обработки текста. Если QUICK RUN имеет значение True, можно передать только фрагмент всего набора данных, но это может снизить производительность модели.

from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
import numpy as np
#split data
df_train, df_test = train_test_split(df, train_size = 0.6, random_state=0)
df_train = df_train.reset_index(drop=True)
df_test = df_test.reset_index(drop=True)
if QUICK_RUN:
    df_train = df_train.sample(frac=TRAIN_DATA_FRACTION).reset_index(drop=True)
    df_test = df_test.sample(frac=TEST_DATA_FRACTION).reset_index(drop=True)
# encode labels
label_encoder = LabelEncoder()
labels_train = label_encoder.fit_transform(df_train["genre"])
labels_test = label_encoder.transform(df_test["genre"])
num_labels = len(np.unique(labels_train))
print("Number of unique labels: {}".format(num_labels))
print("Number of training examples: {}".format(df_train.shape[0]))
print("Number of testing examples: {}".format(df_test.shape[0]))

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

from interpret_text.experimental.common.utils_bert import Language, Tokenizer
BERT_CACHE_DIR = "./temp"
LANGUAGE = Language.ENGLISH
tokenizer = Tokenizer(LANGUAGE, to_lower=True, cache_dir=BERT_CACHE_DIR)
tokens_train = tokenizer.tokenize(list(df_train["sentence1"]))
tokens_test = tokenizer.tokenize(list(df_test["sentence1"]))

В следующих двух строках выполняется некоторая дополнительная предварительная обработка, такая как определение начала и конца предложения, длина токена (MAX_LEN: измените это, если вы хотите уменьшить длину процесса обучения). Более подробную информацию о реализации BERT можно найти здесь.

MAX_LEN = 150
tokens_train, mask_train, _ = tokenizer.preprocess_classification_tokens(tokens_train, MAX_LEN)
tokens_test, mask_test, _ = tokenizer.preprocess_classification_tokens(tokens_test, MAX_LEN)

Следующим шагом будет загрузка предварительно обученной модели BERT с использованием классификатора последовательностей с указанием количества меток и языка.

from interpret_text.experimental.common.utils_bert import BERTSequenceClassifier
classifier = BERTSequenceClassifier(language=LANGUAGE, num_labels=num_labels, cache_dir=BERT_CACHE_DIR)

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

from interpret_text.experimental.common.timer import Timer
with Timer() as t:
    classifier.fit(token_ids=tokens_train,
                    input_mask=mask_train,
                    labels=labels_train,    
                    num_epochs=NUM_EPOCHS,
                    batch_size=BATCH_SIZE,    
                    verbose=True)
print("[Training time: {:.3f} hrs]".format(t.interval / 3600))

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

preds = classifier.predict(token_ids=tokens_test, 
                           input_mask=mask_test, 
                           batch_size=512)

Пришло время оценить модель и проверить точность, точность, отзывчивость и оценку F1 для каждой метки. Чтобы понять эти меры, полезно знать, как работает матрица путаницы, поскольку по этим параметрам мы можем вычислить точность, точность, отзывчивость и оценку F1. Точность показывает общее количество правильно спрогнозированных наблюдений из всех наблюдений. Точность показывает правильно предсказанные положительные наблюдения из всех положительных наблюдений. Отзыв рассчитывается на основе правильно предсказанных положительных наблюдений, когда это действительно положительный класс. Наконец, оценка F1 рассчитывается на основе запоминания и точности, это среднее значение этих двух показателей с весом.

from sklearn.metrics import classification_report, accuracy_score
import json
report = classification_report(labels_test, preds, target_names=label_encoder.classes_, output_dict=True) 
accuracy = accuracy_score(labels_test, preds)
print("accuracy: {}".format(accuracy))
print(json.dumps(report, indent=4, sort_keys=True))

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

device = torch.device("cpu" if not torch.cuda.is_available() else "cuda")
classifier.model.to(device)
for param in classifier.model.parameters():
    param.requires_grad = False
classifier.model.eval()

Результат должен выглядеть примерно так, как на следующем рисунке.

Чтобы получить объяснение модели, Unified Information Explainer необходимо инициализировать, передав модель, набор данных, устройство CUDA и целевой уровень BERT.

from interpret_text.experimental.unified_information import UnifiedInformationExplainer
interpreter_unified = UnifiedInformationExplainer(model=classifier.model, 
                                 train_dataset=list(df_train["sentence1"]), 
                                 device=device, 
                                 target_layer=14, 
                                 classes=label_encoder.classes_)

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

idx = 10
text = df_test["sentence1"][idx]
true_label = df_test["genre"][idx]
predicted_label = label_encoder.inverse_transform([preds[idx]])
print(text, true_label, predicted_label)
explanation_unified = interpreter_unified.explain_local(text, true_label)

Этот результат на самом деле не дает выдающейся информации, но можно визуализировать, какое слово влияет на результаты прогноза, а какие - нет.

Щиток приборов

Это интерактивный виджет, который показывает, влияет ли конкретное слово на предсказание (положительные характеристики) или нет (отрицательные характеристики). Чтобы создать и просмотреть эту панель мониторинга, запустите следующий код.

from interpret_text.experimental.widget import ExplanationDashboard
ExplanationDashboard(explanation_unified)

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

Заключение

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

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

Продолжите эксперименты с другими объяснителями:

Вклад:

GitHub: Interpret-Text