Классификация текста количеством более двух меток одна из самых сложных задач в машинном обучение. Как, правило сырые данные перед обучением модели требуют серьёзной обработки. Впервые с подобной задачей я столкнулся на своей работе – было необходимо обработать текст обратной связи посетителей портала с ограниченным количеством символов - около ста слов – сообщения могли состоять как из одного слова, так и из нескольких предложений. Количество классов – пять, от очень плохой «эмоциональной окраски» сообщения до очень хорошей. По причине NDA я не могу демонстрировать данные, которые предоставил клиент, поэтому специально для этой статьи я собрал собственный сет данных отзывов об отелях – стандартная история для классификации тональности. Мой «демонстрационный» сет данных содержит 163830 текстовых сообщений (желающие могут скачать по ссылке), как и у данных клиента, в сете присутствует сильный дисбаланс в классах, что очень ощутимо сказывается на итоговой точности предсказания. На порядок меньше отзывов с метками 1 и 3, и напротив крупный перевес представляет 5-ый класс.  Так же тексты весомо отличаются по своей длине, при средней длине отзыва 50 токенов присутствуют крупное кол-во, превышающее значения 100 токенов – встречаются тексты и более тысячи. Тестовые данные для чистоты эксперимента я выделил в отдельный фрейм размером 25000 строк – по 5000 сообщений на каждый класс. Моделируя реальную задачу, в тестовый набор я добавлял только сообщения с количеством токенов от 1 до 100. В этом исследование я хочу разобрать отдельный сегмент предобработки данных, а именно показать, насколько балансировка тренировочных данных может качественно повлиять на конечный результат, а также чуть глубже погрузиться в суть работы механизма взвешивания слов TF-IDF Первым делом определимся с необходимым количеством отзывов для каждого класса. Я посчитал что 30-35 тысяч отзывов будет оптимальным решением. Необходимо сгенерить 20к текстов для первого и для третьего класса, а 15 тысячами текстов для 5-го придётся пожертвовать. Существует множество способов генерации текста, в том числе с применением различных библиотек, я же реализую наиболее «дешевый» с точки зрения времени вычисления вариант, основанный на частоте использования биграмм тренировочного корпуса. В данной задаче для нас не имеет значение семантический аспект генерируемых предложений, так для взвешивания токенов будет использоваться метод TF-IDF. Далее в статье я подробней остановлюсь на этом методе, а здесь оставлю мой вариант скрипта, генерирующего текст для первого класса (очень плохие отзывы).  Первым делом напишем функцию (tokenize_sentences) разбиения текстов корпуса, которая возвращает список токенов, разделённых фиктивным токеном ('END_SENT_START') в конце каждого предложения.  На вход следующей функции поступает получившийся список токенов. Функция возвращает словарь с группированными биграммами: ключи – отдельные токены, в значение – список слов которые следуют за ними в корпусе с собственной частотой (  { 'отель': [('отличным', 4.116e-06), ('вобщем', 2.058e-06), …) Дальше код для самой генерации предложений. Скрипт выдаёт 4 предложения по 15 слов каждое, примерно следующего содержания :) 'советовали как и вся мебель в качестве вступления хочу от отеля не звонила на пользу выбора. округа говорит по часа видимо поэтому администрация обратит внимание что пробовать желания нет чайника ни сосисок. отнесен к сведенью, линию стали ждать пока мы были с сухариками, отфутболивают, вечерний рынок где то. чернышевской, привлекательными чем я уезжала из санатория а тут просто праздник но очень экономит особенно если.' Как, я упоминал ранее при данной задаче, для генерации семантика не имеет принципиального значения, важней частота появления слова в корпусе и именно на такой результат заточен алгоритм. И теперь, запамятовав вышесказанное, перейдём к следующей части работы. Сгенерировав недостающее количество текстов, следует привести к балансу длину самих текстов. Для чего? Что бы ответить на этот вопрос, вспомним как работает механизм взвешивания текста TF-IDF. Мера TF-IDF является произведением двух сомножителей. TF - частота слова - отношение числа вхождений некоторого слова к общему числу слов документа. Таким образом, оценивается важность слова в пределах отдельного документа. IDF - обратная частота документа - инверсия частоты, с которой некоторое слово встречается в документах коллекции. Учёт IDF уменьшает вес широкоупотребительных слов. Для каждого уникального слова в пределах конкретной коллекции документов существует только одно значение IDF. Большой вес в TF-IDF получат слова с высокой частотой в пределах конкретного документа и с низкой частотой употреблений в других документах. То есть, если мы имеем корпус с текстами с сильно различным количеством слов, мы рискуем получить завышенный показатель IDF если слово встречается только в маленьких текстах и наоборот если слово часто встречается много раз только в одном крупном тексте. Ещё раз по-другому Первый случай. Слово t встречается пять раз в тексте длинной 500 слов: знаменатель формулы IDF получит 1 бал и завысит показатель самого IDF, а следовательно, общий вес слова. Второй случай. Слово t встречается по одному разу в пяти текстах длинной 100 слов: знаменатель формулы IDF получит 5 балов и занизит показатель самого IDF, а следовательно, общий вес слова. Подводя черту, выходит, что, зная длину текстов тестовых данных, мы можем качественно повлиять на результат взвешивания слов, корректируя длину текстов для тренировочных данных. Если мы знаем, что в тестовой выборке сообщения длиной не более 100 слов, нам выгодней использовать веса, определённые во втором случае. Проверим гипотезу на практике Приведём токены к их леммам Пропустим наш корпус через функцию разбиения/склеивания текстов и получим новый корпус с длинами документов, в заданном тестовым набором, диапазонах – от одного до ста токенов. В функцию передаются аргументы: 1. сам корпус; 2. нижний порог слов в тексте; 3. верхний порог слов; 4.  нижний порог слов в текстах «под отсечение». В результате получим следующее распределение количества токенов в документах. Здесь следует сделать отступление. Если взглянуть на аналогичный график распределения слов для первого класса тестовой выборе (график ниже), мы увидим картину идеального нормального распределения. Следовательно, и распределение для тренировочной выборки я стремился сделать схожим – нормальным, но именно нормальное распределение в тренировочной выборке дало худший показатель точности, а самый оптимальный представлен на графике выше. Итак, мы получили корпуса со сбалансированным количеством слов, теперь отсечём лишнее – думаю 40к доков для каждого класса будет достаточно для демонстрации, затем обучим классификатор и сравним результат с результатами на разных этапах предобработки тренировочных данных. В связке с TF-IDF неплохо работает логистическая регрессия. Модель с загруженными начальными данными (не прошедшими предобработку) показала точность 0.51211. Классы сильно разбалансированы, это хорошо просматривается в показателях precision и recall.  Модель с добавлением сгенерированных данных в слабо представленных классах дала результат точности предсказания в 0,56897. Модель же с добавлением сгенерированных данных и балансировкой текстов по длине показала точность 0,64984. Уточнённые показатели так же выглядят приятней, да и прирост в точности в 8% при многоклассовой классификации довольно ощутимый результат. Спасибо за внимание! Буду рад Вашим замечанием, с удовольствием отвечу на вопросы в комментариях.   intern data analyst