В этой статье мы настроим предварительно обученную модель BERT на наших «мультимодальных» данных для выполнения мультиклассовой классификации счетов по категориям.

  1. понимание бизнеса
  2. Подготовка рабочей среды
  3. Понимание данных
  4. Что такое мультимодальные трансформаторы?
  5. Подготовка данных
  6. Моделирование
  7. Результаты оценки

1. Деловое понимание

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

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

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

Хотя категоризация расходов является важной задачей, выполнение ее вручную может оказаться настоящим бременем и пустой тратой времени и ресурсов. Та же проблема возникает, когда нам нужно извлечь данные из счетов-фактур, такие как дата, сумма TTC, налоги и продавец… Однако с недавним прогрессом в моделях глубокого обучения, таких как Transformers, становится проще, чем когда-либо, настраивать большой язык. модели для удовлетворения конкретных потребностей бизнеса. Все, что вам нужно, это высококачественные размеченные данные для обучения модели.

Для извлечения счета-фактуры нам нужно найти инструмент аннотаций, который предлагает аннотацию OCR для анализа текста и ограничивающей рамки из счетов-фактур и позволяет использовать собственные метки. К счастью, я нашел инструмент под названием UBIAI, который позволит вам напрямую маркировать ваши счета, а также обучать модели глубокого обучения, такие как LayoutLM, для автоматического извлечения информации из изображения счета (как показано на рисунке ниже).

В то время как упомянутая выше информация (Дата, Сумма_TTC, налоги…) явно указана в счете-фактуре, и мы используем инструмент аннотирования UBIAI для их извлечения, информация о категории не упоминается напрямую в счете-фактуре и, следовательно, должна быть выведена (вычтена). ) из данных, которые мы извлекли с помощью инструмента UBIAI.

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

2. Подготовка рабочей среды

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

Мы использовали Google Colab в качестве веб-IDE для Python, это бесплатно, поэтому создайте новую записную книжку, чтобы следовать за нами самым простым способом.

Далее нам нужно импортировать библиотеки, которые мы будем использовать:

import numpy as np
import pandas as pd
from sklearn import preprocessing
from sklearn.metrics import f1_score, matthews_corrcoef
!pip install multimodal-transformers
from transformers import AutoTokenizer, AutoConfig, Trainer, EvalPrediction, set_seed
from transformers.training_args import TrainingArguments
from multimodal_transformers.data import load_data_from_folder
from multimodal_transformers.model import TabularConfig
from multimodal_transformers.model import AutoModelWithTabular

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

3. Понимание данных

Как упоминалось выше, категория — это часть информации, которую мы будем выводить (выводить) из нашей текущей информации о счете:

df = pd.read_csv('/content/categorized ocr_annotated_invoices.csv')
df.head()

df.describe()

df.info()

Как мы видим, наши данные представляют собой набор счетов-фактур, похожих на счета-фактуры, обрабатываемые инструментом аннотирования UBIAI (мы выбрали для работы только несколько столбцов), и они представлены в табличном формате; каждая строка представляет счет-фактуру (наблюдение). Каждый столбец представляет функцию (предикторы), такую ​​как Amount_TTC, VAT_TVA, Seller_ID и Invoice_Description. Данные помечены, а целевой переменной является Category_ID.

Таким образом, мы будем прогнозировать Category_ID на основе других функций. Давайте подсчитаем количество различных категорий, представленных в нашем наборе данных:

number_categories = df.Category_ID.nunique()
print("We have ", number_categories, " classes, so it's a Multiclass Classification")

› У нас 36 классов, так что это мультиклассовая классификация.

4. Мультимодальные трансформаторы

Модели на основе преобразователей, использующие неструктурированные текстовые данные, настолько эффективны, хорошо обсуждаются и широко используются. Однако в нашем случае у нас есть текстовые данные, представленные в столбце Invoice_Description, а также ценные структурированные данные. Для функций со структурированными данными у нас есть Seller_ID, VAT_TVA и Amount_TTC, и каждая из этих функций предоставляет информацию, которую не может предоставить отдельная функция.

Мы называем эти разные способы восприятия данных (неструктурированный текст, структурированные числовые данные…) модальностями. Объединение информации из нескольких модальностей для составления прогноза называется «мультимодальным слиянием».

Для этого мы решили использовать пакет multimodal-transformers, который, говоря простым языком, сочетает в себе вывод модели преобразователя на текстовые данные СО структурированными данными в категориальных и числовых признаках.

5. Подготовка данных

Теперь, когда мы знакомы с нашим бизнес-кейсом, свойствами наших данных и соответствующим инструментом, мы можем начать работу над подготовкой набора данных.

Мы определяем словарь column_info, в котором мы укажем, какие столбцы содержат текстовые данные, числовые данные, категориальные данные и целевую переменную:

column_info = {
'text_cols': ['Invoice_Description'],
'num_cols': ['VAT_TVA', 'Amount_TTC'],
'cat_cols': ['Seller_ID'],
'label_col': 'Category_ID',
# 'label_list': list(df.Category_ID.unique( ))
}

Затем мы должны закодировать столбец метки Category_ID как преобразователи. Trainer не работает со столбцом качественной метки и ожидает, что столбец метки будет содержать целые числа от 0 до количества классов -1:

encoder = preprocessing.LabelEncoder( )
df["Category_ID"] = encoder.fit_transform(df['Category_ID']).astype(int)

Примечание. Существует несколько способов кодирования объектов, здесь мы использовали sklearn.preprocessing.LabelEncoder (! Имейте в виду, что если вы собираетесь кодировать объект без метки с помощью этого метода, вы должны быть осторожны с возможностью создания неправильного кода. порядковое значение в измерении, поэтому вам может понадобиться использовать sklearn.preprocessing.OneHotEncoder).

Чтобы построить надежную модель, мы не должны использовать один и тот же набор данных как для обучения модели, так и для оценки, поэтому нам нужно разделить наши данные на наборы для обучения, проверки и тестирования:

train_df, validation_df, test_df = np.split(df.sample(frac = 1), [int(.8 * len(df)), int(.9 * len(df))])
print('TRAIN DATA is ~80% :', len(train_df))
print('VALIDATION DATA is ~10% :', len(validation_df))
print('TEST DATA is ~10% :', len(test_df))
train_df.to_csv('train.csv')
validation_df.to_csv('val.csv')
test_df.to_csv('test.csv')

Примечание. Мы сохранили 3 набора в файлах csv, имена файлов должны быть такими, как train.csv, test.csv и val.csv, иначе функция multimodal_transformers.data.load_data_from_folder не сможет загрузить их.

Затем мы загружаем наши наборы данных в TorchTabularTextDataset, который будет включать текстовые входные данные для преобразователей HuggingFace, а также указанные нами столбцы категориальных и числовых признаков. Для этого мы должны сначала загрузить наш токенизатор HuggingFace:

pretrained_model_name = 'bert-base-uncased'
tokenizer = AutoTokenizer.from_pretrained(pretrained_model_name)
train_dataset, validation_dataset, test_dataset = load_data_from_folder(folder_path = '.',
                       text_cols = column_info['text_cols'],
                       tokenizer = tokenizer,
                       label_col = column_info['label_col'],
                       # label_list = column_info['label_list'],
                       categorical_cols = column_info['cat_cols'],
                       numerical_cols = column_info['num_cols'],
                       sep_text_token_str = tokenizer.sep_token,
)

Мы решили использовать предварительно обученную базовую модель BERT (без кейса), в зависимости от ваших требований вы можете выбрать другую предварительно обученную модель здесь.

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

6. Моделирование

Теперь следующее, что нужно сделать, это загрузить в наш преобразователь табличную модель.

Во-первых, мы указываем наши табличные конфигурации в объекте TabularConfig, в котором мы также указываем, как мы хотим объединить табличные функции с текстовыми функциями, и мы будем использовать метод взвешенной суммы.

Во-вторых, мы определяем эту конфигурацию как переменную-член tabular_config объекта BertConfig преобразователя HuggingFace.

Как только мы определили model_config, мы можем загрузить модель, используя метод multimodal_transformers.model.AutoModelWithTabular.from_pretrained HuggingFace.

tabular_config = 
TabularConfig(
        num_labels = number_categories,
        cat_feat_dim = train_dataset.cat_feats.shape[1],
        numerical_feat_dim = train_dataset.numerical_feats.shape[1],
        combine_feat_method =     'weighted_feature_sum_on_transformer_cat_and_numerical_feats'
)

model_config = AutoConfig.from_pretrained('bert-base-uncased')
model_config.tabular_config = tabular_config

model = AutoModelWithTabular.from_pretrained('bert-base-uncased', config = model_config)

После загрузки модели и перед ее обучением на наших данных нам необходимо определить некоторые соответствующие метрики оценки, чтобы обеспечить представление о производительности модели, поэтому мы определяем функцию для возврата точности, оценки F1 и коэффициента корреляции Мэтью:

def calculate_classification_metrics(p: EvalPrediction):
  predicted_labels = np.argmax(p.predictions, axis = 1)
  expected_labels = p.label_ids
  accuracy = (predicted_labels == expected_labels).mean( )
  f1 = f1_score(y_true = expected_labels, y_pred = predicted_labels, average = 'micro')
  eval_result = {
         "acc": accuracy,
         "f1": f1,
         "mcc": matthews_corrcoef(expected_labels, predicted_labels)
  }
  return eval_result

На данный момент нам нужно просто обучить нашу модель, поэтому осталось всего три шага 🎉:

  1. Определите гиперпараметры обучения в TrainingArguments.
  2. Передайте наши обучающие аргументы в Trainer вместе с нашими наборами данных, моделью и функцией оценки.
  3. Вызовите train() для точной настройки нашей модели.
training_args = TrainingArguments(output_dir = "./UBIAI/model_out",
                                  logging_dir = "./UBIAI/run_logs",
                                  overwrite_output_dir = True,
                                  do_train = True,
                                  do_eval = True,
                                  per_device_train_batch_size = 32,
                                  num_train_epochs = 1,
                                  evaluate_during_training = True,
                                  logging_steps = 5,
                                  eval_steps = 54)
set_seed(training_args.seed)
trainer = Trainer(model = model,
                 args = training_args,
                 train_dataset = train_dataset,
                 eval_dataset = validation_dataset,
                 compute_metrics = calculate_classification_metrics)

trainer.train( )

7. Оценка

После обучения модели давайте посмотрим на метрики проверки:

# Load the TensorBoard notebook extension
%load_ext tensorboard
%tensorboard --logdir ./UBIAI/run_logs --port=6006

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

# Save our model
trainer.save_model ("./UBIAI/model_out")

# Load it
model = AutoModelWithTabular.from_pretrained("./UBIAI/model_out", local_files_only=True)
trainer = Trainer(model=model)

# Predict with it
y_predicted = [list(i).index(max(i)) for i in trainer.predict(test_dataset).predictions]
print("➡️Predicted categories for the test data set:", y_predicted)
# Evaluate the prediction
precision = sum(1 for x,y in zip(y_predicted,test_dataset.labels) if x == y) / float(len(y_predicted))
print("🥳 Our model is able to predict the invoice category with an accuracy score of", round(precision*100), "%")

Примечание: давайте напомним о целях наших 3 наборов данных:

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

- validation_dataset используется для проверки производительности нашей модели во время обучения.

- test_dataset используется для тестирования модели после завершения обучения.

Заключение

Работая с аннотированными счетами, которые содержат как структурированные данные (Amount_TTC…), так и неструктурированные данные (Invoice_Description), мы смогли загрузить преобразователь с табличной моделью, которая использует преимущества как текстовых, так и структурированных данных, мы точно настроили предварительно обученную модель. на наших данных, и мы смогли предсказать категорию счетов с точностью ~ 95%.

Ссылка на блокнот Colab. (Все неисходные визуальные эффекты взяты отсюда)

Если вам интересно понять, как извлекать структурированную информацию (дата, сумма_TTC…) из неструктурированных данных (изображений, документов…), рекомендуем прочитать эту статью от UBIAI.