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

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

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

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

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

Я использовал библиотеку Scrappy. С его помощью довольно просто написать веб-пауков, так как шаблон уже предоставлен. Чтобы начать новый проект:

Он автоматически создаст для вас структуру проекта.

Следующим шагом является получение правильного XPath для необходимых данных. XPath — это путь к требуемому узлу в XML-документе. Я использовал расширение «XPath helper», чтобы получить правильное значение. Чтобы получить XPath, щелкните правой кнопкой мыши элемент с данными, выберите «Копировать»-> «Копировать XPath».

Вы получите что-то вроде этого:

/html/body/app-root/div/div/rz-category/div/main/rz-catalog/div/div/section/rz-grid/ul/li[3]/rz-catalog-tile/app-goods-tile-default/div/div[2]/a[2]/span

Затем вставьте это значение в «помощник XPath». Вам нужно проверить правильность XPath и выбрать все необходимые элементы.

Как видите, он выбирает только один элемент на странице. Было бы полезно, если бы вы изменили его, чтобы выбрать все необходимые элементы. В нашем случае проблема в том, что он указывает определенные элементы в списке («li[3]»). Нам нужно удалить эту часть, и она выберет все необходимые элементы:

/html/body/app-root/div/div/rz-category/div/main/rz-catalog/div/div/section/rz-grid/ul/li/rz-catalog-tile/app-goods-tile-default/div/div[2]/a[2]/span

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

  1. Найти все категории
  2. Для каждой категории возьмите все подкатегории.
  3. Для каждой подкатегории соберите все товары со всех страниц.
  4. Для каждого продукта получите название, описание и все категории.

Вы можете найти мою реализацию там.

Исследовательский анализ данных

Давайте посмотрим на наши данные.

Первое, на что стоит обратить внимание — наши данные на русском языке. Итак, если мы используем какие-то библиотеки обработки текста или занимаемся трансферным обучением, он должен поддерживать этот язык.

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

У нас есть 80047 самых сложных категорий, и 682 000 продуктов из 780 000 имеют название предыдущей категории в самом важном названии. Главное отличие — имя бренда в самом сокровенном. Итак, учитывая малое количество товара на самую глубокую категорию, мы предскажем ей предыдущую категорию.

Полный блокнот доступен здесь.

Разделение данных

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

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

Полный скрипт можно посмотреть здесь.

Эксперименты

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

Универсальный кодировщик предложений (USE) — семейство моделей для кодирования предложений в векторы встраивания, которые специально нацелены на перенос обучения в другие задачи НЛП. Мы постараемся использовать ЕГЭ для базового и трансфертного обучения. Он возвращает 512 векторов фиксированного размера для текста.

Метрики

Мы будем использовать две метрики для определения производительности нашей модели: оценка F1 и разреженная категориальная точность.

Согласно документации, оценка F1 может быть интерпретирована как среднее гармоническое между точностью и полнотой, где оценка F1 достигает своего наилучшего значения при 1, а наихудшей оценки при 0. Относительный вклад точности и полноты в оценку F1 равны. Формула для оценки F1:

F1 = 2 * (precision * recall) / (precision + recall)

Оценка F1 хороша для несбалансированного набора данных, как в нашем случае.

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

Базовый уровень

Мы преобразуем все целевые категории и описания в их встраивание, используя ЕГЭ в качестве основы. Как упоминалось ранее, используется многоязычная модель.

Затем мы вычисляем косинусное сходство между каждым описанием и целевой категорией. Косинусное сходство измеряет сходство между двумя ненулевыми векторами пространства внутреннего произведения. Это дает ценную меру того, насколько похожи два документа с точки зрения их предмета. Итак, наш прогноз имеет максимальное значение косинусного сходства.

Мы сохранили наш прогноз в отдельном столбце «прогноз». Теперь пришло время измерить ошибку. Давайте посчитаем наш F1 Score:

Результат — 0,10, что не оптимально, но мы можем взять его за основу.

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

Полная реализация здесь.

Логистическая регрессия

Кроме ЕГЭ, существуют разные способы преобразования текста в числовые векторы: Bag of Words, Bag-of-n-Grams Tf-Idf vectorizer. Я использовал последний для своей модели.

TF-IDF расшифровывается как Term Frequency — Inverse Document Frequency. Это метод количественной оценки слов в наборе документов. Обычно мы подсчитываем баллы для каждого слова, чтобы обозначить его важность в документе и корпусе. Я рекомендую прочитать эту статью, чтобы углубиться в то, как это работает.

Чтобы настроить TF-IDF, нам нужно указать стоп-слова. Стоп-слова — это те слова, которые очень часто встречаются в документах, поэтому теряют свою репрезентативность. Мы будем использовать библиотеку NLTK для получения стоп-слов.

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

Вы можете контролировать размер словаря с помощью параметров «max_df» и «min_df». «max_df» используется для удаления терминов, которые появляются слишком часто, также известных как «стоп-слова, специфичные для корпуса». «min_df» используется для устранения терминов, которые встречаются слишком редко.

После обучения модель F1 Score на тестовых данных равна 0,67. Намного лучше, чем базовый уровень, да? Тем не менее, после проверки фактических баллов по категориям большинство категорий набрали более 0,71 балла, но в результате некоторые категории получили 0 баллов. Важно проверить, в чем причина. В некоторых категориях слишком мало продуктов, но чтобы узнать, что происходит с другими — , мы можем использовать библиотеку ELI5 и TextExplainer.

ELI5 — это пакет Python, который помогает отлаживать классификаторы машинного обучения и объяснять их прогнозы. eli5.lime.TextExplainer помогает отладить предсказание — проверить, что было важно в документе для принятия этого решения.

Существует два основных способа взглянуть на модель классификации:

  1. проверить параметры модели и попытаться выяснить, как модель работает в глобальном масштабе;
  2. проверить индивидуальный прогноз модели, попытаться выяснить, почему модель принимает решение, которое она делает.

Для проверки индивидуального прогноза модели ELI5 предоставляет функцию eli5.show_prediction():

Результат выглядит следующим образом:

Для проверки параметров модели ELI5 предоставляет eli5.explain_weights() функцию:

Эти функции могут помочь понять, почему точный прогноз был ошибочным.

Как мы можем улучшить результаты логистической регрессии?

Мы можем использовать текстовую лемматизацию и выделение корней для наших данных.

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

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

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

Вложения на уровне символов имеют некоторые преимущества перед вложениями на уровне слов, например:

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

И последнее, что, как мне кажется, может улучшить производительность — очистка текста. Например, с помощью библиотеки Spacy Python мы можем очистить наши тексты, чтобы в них были только существительные, местоимения и прилагательные.

Здесь вы можете найти блокнот для логистической регрессии.

Обучение переносу универсального кодировщика предложений

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

Краткое описание модели:

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

Эта модель имеет точность проверки 0,77 с 25 эпохами.

Полный блокнот смотрите здесь.

Классификация нулевого выстрела

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

У Hugging Face есть собственный ZeroShotClassificationPipeline — конвейер нулевой классификации на основе NLI с использованием ModelForSequenceClassification, обученного задачам NLI (вывод естественного языка).

Я использовал многоязычную модель Roberta с этим пайплайном, код доступен здесь.

Было бы лучше, если бы у вас было много памяти, чтобы выполнить классификацию нулевого выстрела со многими классами. В моем случае я получил OOM даже с экземпляром GPU Colab и 18 ГБ памяти. Итак, для этого примера я предсказал только основную категорию, в которой всего 16 классов. В целом он показывает неплохие результаты, но мне не удалось измерить производительность на всем тестовом наборе данных.

Вывод моделей

Существуют разные способы создания пользовательского интерфейса поверх вашей модели: вы можете использовать API и интегрировать его с вашим интерфейсом, создавать чат-ботов или использовать различные инструменты, доступные в Интернете. Я выбираю Streamlit, чтобы создать пользовательский интерфейс для вывода модели.

Streamlit — это платформа приложений с открытым исходным кодом для машинного обучения. Главный плюс для меня — весь UI описан на Python. Никакого HTML, настройки CSS — ненавижу эту часть :)

Одной из проблем для меня было то, как загрузить свои модели. Варианты заключались в том, чтобы сохранить его на AWS S3, загрузить и сохранить на Github или Google Disk. Мои модели были довольно большими — 400 МБ, 1,2 ГБ, и я решил использовать Git LFS и хранить модели вместе с исходным кодом.

Создать приложение Streamlit довольно просто: установите Streamlit, создайте точку входа, используйте документы или различные примеры и создайте пользовательский интерфейс. Вы можете запустить приложение локально или использовать его на сайте Streamlit (бесплатно!).

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

Проверьте исходный код здесь.

Несколько советов по развертыванию приложения на сервере Streamlit:

  1. Создайте файл requirements.txt со всеми зависимостями.
  2. Не загружайте все модели сразу, чтобы избежать ошибки «Недостаточно памяти».
  3. Не забудьте указать секреты, если вы используете какие-либо

Если при развертывании или использовании приложения возникнут ошибки — консоль со всеми доступными логами.

Краткое содержание

Было интересно попробовать разные подходы к классификации текста. Модель на основе преобразователя показала лучшие результаты, чем классика, такая как логистическая регрессия. Хотя готовый к производству результат не был достигнут, точность многоязычной модели довольно высока. И, конечно же, всегда есть место для доработок и доработок!

Вы можете проверить весь исходный код здесь.

Я надеюсь, что эта статья была полезной для вас. Следуйте за мной, чтобы узнать больше о больших данных, машинном обучении и облачных вычислениях!