Наш сайт использует файлы cookie, чтобы улучшить работу сайта, повысить его эффективность и удобство
Настройки сookie
Файлы cookie, необходимые для правильной работы сайта, всегда разрешены.
Основные файлы cookie
Всегда Включено. Эти файлы cookie необходимы для использования веб-сайта и его функций. Их нельзя отключить. Они устанавливаются в ответ на ваши запросы, такие как настройка параметров конфиденциальности, вход в систему или заполнение форм.
Аналитические файлы cookie
Disabled
Эти файлы cookie нужны чтобы помочь нам понять, на сколько вам удобен наш веб-сайт и насколько эффективны наши маркетологи:) Смотрите список аналитических файлов cookie, которые мы используем.
Рекламные файлы cookie
Disabled
Эти файлы cookie предоставляют информацию рекламным компаниям с целью предоставления или ограничения персонализированной рекламы. Эта информация может быть передана другим рекламным компаниям. Смотрите список рекламных файлов cookie, которые мы используем.

Переобучение модели RuBERT для задачи сопоставления наименований товаров различных розничных сетей

блог о bi, №1 в рунете
В последние годы методы машинного обучения и, в частности, обработка естественного языка (NLP) стали важными инструментами в различных областях, от анализа данных до разработки интеллектуальных систем.
В своей работе мы столкнулись с задачей консолидации данных разных розничных сетей, основной проблемой которой было сопоставление наименований товаров. Современные торговые сети обладают в своем ассортименте десятками тысяч наименований товаров, которые имеют свою специфику в зависимости от поставщика, конкретной сети и менеджера, заполняющего карточку товара. В рамках решаемой задачи требовалось сопоставить несколько таких сетей. Выполнение такого сопоставления вручную – крайне трудоемкий процесс. При сравнении наименований или штрихкодов на уровне 100% совпадения, мы рисковали получить множество ошибок. В связи с этим было принято решение использовать алгоритмы машинного обучения для автоматизации процесса.
На верхнем уровне алгоритм состоит из нескольких частей:
  1. Предварительная обработка текстов: приведение к низкому регистру, сопоставление со словарем терминов, избавление от лишних символов.
  2. Легковесная модель, которая с высокой скоростью проходит по всем возможным парам наименований товаров из разных розничных сетей, и для каждой определяет процент схожести.
  3. Модель RuBERT, которая уточняет вероятность совпадения названий товаров. Эта часть алгоритма была дообучена на основе валидированных ошибок предыдущих предсказаний, что позволило улучшить её точность и адаптировать к специфике задач.
В этой статье мы рассмотрим организацию дообучения модели, описанную в пункте 3, а также результаты, которые были получены в ходе этого процесса.
  • RuBERT – это адаптация модели BERT для обработки текстов на русском языке. RuBERT эффективно использует трансформеры для задач обработки естественного языка (NLP) в контексте русского языка – это было основной причиной выбора такой модели.

Процесс переобучения модели

1. Загрузка данных для обучения

Первым шагом является извлечение данных из базы данных и их обработка. Наше решение интегрировано с корпоративным хранилищем данных Заказчика, данные для обучения хранятся в формате, который показан в таблице ниже. Для каждого наименования товара мы выбираем 10 примеров пар и к каждой из них добавляем признак совпадения 0 или 1 в соотношении 90% к 10%, который является целевым признаком. Список пар наименований для обучения формируется по итогам проверки результатов предыдущих предсказаний, отбираем ошибочные варианты и размечаем их.

Наименование сети 1

Наименование сети 2

Целевой признак

ЦИТРАМОН П-АВЕКСИМА ТАБЛ. N20, Производитель: Авексима+

Цитрамон П таб. №20, Производитель: Авексима ОАО/пр.Ирбитский ХФЗ ОАО

1

ЦИТРАМОН П ТАБЛ. N20, Производитель: УРАЛБИОФАРМ

Цитрамон П таб. №20, Производитель: Авексима ОАО/пр.Ирбитский ХФЗ ОАО

0

КОНТАКТНЫЕ ЛИНЗЫ OPTIMA FW 4 ШТ 8,4, -2,50 BAUSCH+LOMB, Производитель: БАУШ И ЛОМБ

Линзы контактные bausch+ lomb optima fw 8,7 -2,50 N4, Производитель: Бауш энд Ломб Инк

1

BAUSCH+ LOMB OPTIMA FW 8,7 N4/-3,50/ МЯГКИЕ КОНТАКТНЫЕ ЛИНЗЫ , Производитель: БАУШ И ЛОМБ

Линзы контактные bausch+ lomb optima fw 8,7 -2,50 N4, Производитель: Бауш энд Ломб Инк

0

КРЕМ Д/РУК УВЛАЖНЯЮЩИЙ COMFORTE (С ГЛИЦЕРИНОМ) ТУБА 50 МЛ Х1 , Производитель: ВЕТПРОМ

Comforte крем д/рук увлажняющий глицерин 50мл, Производитель: ВетПром АД

1
КРЕМ Д/РУК ПИТАТЕЛЬНЫЙ COMFORTE (С АЛОЭ ВЕРА) ТУБА 50 МЛ, Производитель: ВЕТПРОМ

Comforte крем д/рук увлажняющий глицерин 50мл, Производитель: ВетПром АД

0
Видно, что данные достаточно разнородные: может отличаться производитель, формат дозировки (0,1г/100мг) или вкус. При этом наименования в семантическом смысле достаточно близки между собой.
Очень важно на первом этапе провести корректную обработку, остановимся на этом этапе подробнее.
Наиболее универсальным средством решения задачи является язык программирования Python, в нашем случае версии 3.9.5. Скрипт загрузки необходимых библиотек:
import pandas as pd
import pymorphy2
import nltk

# Загрузите необходимые ресурсы NLTK
#Модель для разбиения текста на предложения и слова.
nltk.download('punkt')
# Модель для определения частей речи в тексте.
nltk.download('averaged_perceptron_tagger')
from nltk.corpus import stopwords
# Загрузка списка стоп-слов, которые часто игнорируются при обработке текста.
nltk.download('stopwords')
russian_stopwords = stopwords.words("russian")
Наша задача касается медицинской сферы, то есть мы имеем дело с аптечными наименованиями, поэтому на первом этапе обработки привязываемся к ее специфике. Обрабатываем такие случаи так:
  1. Приводим все в нижний регистр, так как политика написания наименований у разных сетей отличается.
  2. Заменяем термины, которые имеют один смысл, но могут обозначаться по-разному у разных сетей, например kids > дет, р-р > раствор и прочие.
  3. Заменяем латинские буквы на кириллицу:Vitrum > Витрум.
  4. Обрабатываем знак №, так как чаще всего в аптечных наименованиях этот знак обозначает количество штук в упаковке.
  5. Убираем ненужные символы и пробелы.
Итоговая функция для первичной обработки выглядит следующим образом:
# Функция для обработки текста
def preprocess_text(text):
    # Приводим текст к нижнему регистру
    text = text.lower()

    # Замена "кидс" или "дет" на "детский"
    text = re.sub(r'\b(kids|кидс|дет|для детей)\b', 'детский', text)

    # Замена "р-р" на "раствор"
    text = re.sub(r'\bр-р\b', 'раствор', text.lower())

    # Замена "п/о" на "покрытые оболочкой"
    # text = re.sub(r'\bп/о\b', 'покрытые оболочкой', text)

    text = re.sub(r'\bкапс\b', 'капсула', text)
    text = re.sub(r'\bкиш\.?/?раст\b', 'кишечнорастворимый', text)
    text = re.sub(r'\bтаб\b', 'таблетка', text)

    # Словарь транслитерации
    translit_dict = {
        'v': 'в', 'i': 'и', 't': 'т',
        'r': 'р', 'u': 'у', 'm': 'м',
        'v': 'в', 't': 'т',
        'r': 'р', 'u': 'у', 'm': 'м',
    }

    # Заменяем латинские буквы на кириллицу
    for lat_char, cyr_char in translit_dict.items():
        text = text.replace(lat_char, cyr_char)

    # Замена "№" followed by digits to "digits шт"
    text = re.sub(r'№(\d+)', r'\1 шт', text)

    # Заменяем ненужные символы на пробелы, оставляя числа нетронутыми
    text = re.sub(r'[^\w\s,]', ' ', text)  # Заменяет все кроме букв, цифр, пробелов, запятых и точек на пробелы
    text = re.sub(r'(?<!\d)\,(?!\d)', ' ', text)  # Заменяет точки, не окруженные цифрами, на пробелы

    # Убираем лишние пробелы
    text = re.sub(r'\s+', ' ', text).strip()

    return text
Следующий этап обработки – это стемминг (приведение слов к их начальной форме) и удаление стоп-слов.
# Создаем объект морфологического анализатора
morph = pymorphy2.MorphAnalyzer()

# Определяем словарь исключений, которые не нужно нормализовать
exceptions = {
    "спрей": "спрей",
    # другие слова и их леммы
}

#Функция стемминга
def stem_text(text):
    words = text.split()
    stemmed_words = []
    for word in words:
        if word in exceptions:
            stemmed_words.append(exceptions[word])
        else:
            parsed_word = morph.parse(word)
            if parsed_word:
                stemmed_words.append(parsed_word[0].normal_form)
            else:
                stemmed_words.append(word)
    return ' '.join(stemmed_words)

#Функция удаления стоп-слов
def remove_stopwords(text):
       words = text.split()
       filtered_words = [word for word in words if word not in russian_stopwords]
       return " ".join(filtered_words)
После применения всех этапов обработки мы имеем очищенный текст с учетом всех особенностей торговой сети, который можно использовать для дальнейшей работы. На картинке ниже приведено несколько примеров наименований товаров до обработки (name) и после обработки (name_clean).

2. Загрузка модели и токенизатора

После завершения обработки данных можно перейти к работе с моделью. Мы загружаем предварительно обученную модель RuBERT и соответствующий токенизатор. Для этого воспользовались библиотекой Hugging Face и скачали локально наиболее подходящую модель rubert-base-cased.
  • Токенизатор – это инструмент, который разбивает текст на токены (меньшие единицы, такие как слова или подслова). Он необходим для преобразования текстовых данных в числовые форматы, которые могут быть поняты моделью.
AutoTokenizer – это класс из библиотеки Hugging Face, который автоматически выбирает подходящий токенизатор для конкретной модели.
import os
import torch
from datasets import Dataset
from transformers import AutoTokenizer, AutoModelForSequenceClassification, Trainer, TrainingArguments

model_input_path = '/path/to/your/model'
tokenizer = AutoTokenizer.from_pretrained(model_input_path)
model = AutoModelForSequenceClassification.from_pretrained(model_input_path, num_labels=2)

3. Токенизация

Для обучения мы использовали около 40 000 пар наименований, данную выборку разделили на обучающую и тестовую в соотношении 90% к 10% таким образом, чтобы в разных наборах данных не было пересекающихся наименований.
Для токенизации использовали следующую функцию:
def tokenize_function(examples):
    return tokenizer(examples['text1'], examples['text2'],
                     truncation=True, padding='max_length', max_length=200)

tokenized_train = dataset_train.map(tokenize_function, batched=True)

4. Настройка параметров обучения

Затем мы настраиваем параметры обучения для тренера. В нашем случае оптимальными оказались следующие параметры:
training_args = TrainingArguments(
    output_dir='/path/to/output',
    evaluation_strategy='epoch',
    save_strategy='epoch',
    learning_rate=2e-5,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=16,
    num_train_epochs=3,
    weight_decay=0.01,
    warmup_ratio=0.1,
    logging_steps=100
)
  • output_dir– путь к директории, где будут сохранены результаты обучения, такие как модель и логи;
  • evaluation_strategy – указывает, как часто проводить оценку модели. В данном случае оценка будет проводиться по окончании каждой эпохи. Эпоха – это один полный проход через всю обучающую выборку;
  • save_strategy – определяет, когда сохранять модель. Здесь также используется стратегия сохранения по окончании каждой эпохи;
  • learning_rate – скорость обучения определяет, насколько быстро модель обновляет свои параметры в процессе обучения;
  • per_device_train_batch_size – размер батча (количество примеров, обрабатываемых одновременно) для обучения на каждом устройстве. В данном случае размер батча составляет 16;
  • per_device_eval_batch_size – размер батча для оценки модели на каждом устройстве. Также установлен на 16;
  • num_train_epochs – количество полных проходов по обучающему набору данных. В данном случае модель будет обучаться в течение 3 эпох;
  • weight_decay – коэффициент регуляризации, который помогает предотвратить переобучение модели. Значение 0.01 означает, что 1% весов будет уменьшаться на каждой итерации;
  • warmup_ratio – доля эпох, в течение которых скорость обучения будет постепенно увеличиваться от нуля до заданного значения. Здесь указано 10%, что означает, что первые 10% эпох будут использоваться для разогрева;
  • logging_steps – частота, с которой будут записываться логи (например, значения потерь, метрики) в процессе обучения. В данном случае логирование будет происходить каждые 100 шагов.

5. Обучение модели

С помощью класса Trainer мы запускаем процесс обучения модели и сохраняем ее в указанной директории. Это класс упрощает процесс обучения и оценки моделей. Он предоставляет множество удобных функций для настройки, логирования и управления процессом обучения.
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_train,
    eval_dataset=tokenized_eval
)
trainer.train()

model_path = "/path/to/your/model"
trainer.save_model(model_path)
tokenizer.save_pretrained(model_path)
train_dataset – этот параметр указывает на обучающий набор данных, который должен быть токенизирован. tokenized_train должен быть объектом типа Dataset, который содержит входные данные и соответствующие метки.
eval_dataset – здесь мы передаем набор данных для оценки (валидации). Он также должен быть токенизирован и иметь тот же формат, что и обучающий набор данных.

6. Оценка результатов и экспорт предсказаний

После обучения модели на основном датасете размером примерно 36 000 пар наименований провели тестирование полученной модели на оставшейся тестовой выборке, ее размер 4158 пар.
Пример результата тестирования для пяти наименований показан на рисунке ниже. Здесь:
  • text1 – наименование товара, с которым необходимо сопоставить исходное наименование,
  • text2 – исходное наименование,
  • y_true – признак корректной связки, размеченный в данных,
  • y_pred – признак корректной связки, предсказанный моделью.
Во всей тестовой выборке содержалась информация о 660 исходных наименованиях товаров, по результатам анализа выяснили, что для 90% наименований товаров пары были определены правильно. До проведения дообучения все эти связки определялись неверно, то есть периодическое дообучение модели на ошибочных данных способно сделать модель лучше применимой к задаче определения схожих наименований товаров.

Заключение

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