Хабрахабр

Выявление и классификация токсичных комментариев. Лекция в Яндексе

Во всех современных системах модерации используется либо краудсорсинг, либо уже ставшее классикой машинное обучение. На очередной тренировке по ML в Яндексе Константин Котик, Игорь Галицкий и Алексей Носков рассказали о своём участии в конкурсе по массовому выявлению оскорбительных комментариев. Конкурс проходил на платформе Kaggle.

— Всем привет! Меня зовут Константин Котик, я data scientist в компании «Кнопка жизни», студент физфака и Высшей школы бизнеса МГУ.
Сегодня мы с коллегами — Игорем Галицким и Алексеем Носковым — расскажем вам о соревновании Toxic Comment Classification Challenge, в котором наша команда DecisionGuys заняла 10 место среди 4551 команды.

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

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

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

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

Ссылка

К настоящему моменту эта группа разработала публичное API, которое может определять степень токсичности комментария, но текущие их модели по-прежнему совершают ошибки. И в этом соревновании нам, кегглерам, был брошен вызов построить модель, которая способна выявлять комментарии, содержащие угрозы, ненависть, оскорбления и тому подобное. Причем в идеале требовалось, чтобы эта модель была лучше, чем текущая модель для их API.

В качестве обучающей и тестовой выборок были предоставлены комментарии со страниц обсуждения статей Википедии. Имеем задачу обработки текста: выявить, а затем классифицировать комментарии. В трейне было порядка 160 тысяч комментариев, в тесте 154 тысячи.

Каждому комментарию соответствует шесть меток. Обучающая выборка была размечена следующим образом. И может быть так, что все метки нулевые, случай адекватного комментария. Метки принимают значение 1, если в комментарии есть данный тип токсичности, 0 — иначе. А может быть так, что один комментарий содержит несколько типов токсичности, сразу угрозу и непристойность.

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

Метрика качества — это усредненный по типам токсичности ROC AUC, то есть среднее арифметическое ROC AUC для каждого класса в отдельности.

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

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

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

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

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

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

Ссылка

Первое умное, что пришло мне в голову, это использовать векторное представление с помощью Doc2vec. Это Word2vec плюс вектор, учитывающий уникальность конкретного документа. В исходной статье этот вектор называется как параграф id.

Оценка качества проводилась на кросс-валидации по десяти фолдам, она была стратифицированной, и что важно отметить, логистическая регрессия обучалась по каждому классу, шесть задач классификации отдельно решалось. Далее по такому векторному представлению обучалась логистическая регрессия, где каждый документ представлялся 10 000-мерным вектором. И на выходе в итоге получалось распределение вероятностей по классам.

Мне вообще в оперативку не помещалась. Логистическая регрессия очень долго обучалась. По этой причине мы сразу отказались от использования Doc2vec в силу большого ожидания, хотя и можно было его улучшить на 1000, если комментарий с дополнительной предобработкой данных сделать. На мощностях Игоря, чтобы получить результат, как на слайде, где-то день потратили.

Они на вход последовательно получают слова, обновляя свое скрытое состояние после каждого слова. Более умное, что использовали мы и другие участники соревнования, это рекуррентные нейронные сети. Предсказать независимо наличие или отсутствие контекстного слова. Я с Игорем использовал рекуррентную сеть GRU по word embedding fastText, который особенен тем, что там решается множество независимых бинарных задач классификации.

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

Вы спросите, в чем состоял секрет успеха?

Подход нетворкинг нужно изобразить в виде ориентированного графа. Он состоял в блендинге, его было невроятно много, с стекинге, и в подходе нетворкинг.

Затем Павел Плесков в Slack канале ODS выразил желание, что он хочет объединиться с кем-нибудь из топ-200. В начале соревнования команда DecisionGuys состояла из двух человек. Игорь заметил его желание присоединиться, а я пригласил его в команду. Мы тогда как раз находились где-то на 157 месте, и Павел Плесков на 154, где-то по соседству. Игорь — Евгению. Затем к нам присоединился Андрей Литвинов, потом Павел пригласил грандмастера Алексея Носкова к нам в команду. И последним партнером нашей команды стал болгар Атанас Атанасов, и вот такой получился человеческий международный ансамбль.

Сейчас Игорь Галицкий расскажет, как он обучал gru, более подробно расскажет об идеях и подходах Павла Плескова, Андрея Литвинова и Атанаса Атанасова.

Игорь Галицкий:
— Я data scientist в компании Epoch8, и я расскажу о большинстве архитектур, которые мы использовали.

Все началось со стандартной didirectional gru с двумя слоями, все практически из команд ей пользовались, и в качестве embedding был fastText, функция активации EL.

Почему она нам дала такие неплохие результаты, с которыми мы довольно долго держались в топ-150? Тут особенно сказать нечего, простая архитектура, без изысков. Нужно было двигаться дальше. У нас была хорошая предобработка текста.

После блендинга с нашим это дало существенный прирост. У Павла был свой подход. До этого у нас был блендинг gru и модели на Doc2vec, она давала 61 LB.

Здесь изображено gru с attention, все параметры на слайде. Расскажу о подходах Атанаса Атанасова, он прямо энтузиаст всяких новых статей. Скор на слайде. У него было очень много реально крутых подходов, но он до последнего момента использовал свою предобработку, и весь профит нивелировался.

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

Ссылка

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

Это одна из лучших на private была, достаточно глубокая сеть, показала хорошие результаты. Также Bi-GRU с attention блоком.

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

Ссылка

Это была самая глубокая сеть. Как сказал Атанас, он просто хотел что-то большое и интересное обучить. Обычная сверточная сетка, которая училась на текстовых фичах, результаты ничего особенного.

Ссылка

Это достаточно интересный новый подход, в 2017 году была статья на эту тему, она использовалась для ImageNet, и там позволила улучшить на 25% предыдущий результат. Ее основная фишка в том, что параллельно сверточному блоку пускается маленький слой, который учит веса для каждой свертки в этом блоке. Она дала очень крутой подход, несмотря на обрезание предложений.

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

Ссылка

Здесь архитектура AC-BLSTM. Суть в том, что если нижнее разделение на две части, помимо attention, это умный пуллинг, а параллельно идет еще обычный, и это все конкретизируется. Тоже неплохие результаты.

Помимо самих моделей, добавлял какие-то фичи текстовые, обычно длина, количество больших букв, количество плохих слов, количество символов, это все добавлял. И Атанас весь свой зоопарк моделей, потом это классно смешивал. 9867. Кросс-валидация из пяти фолдов, и получались отличные результаты, на private LB 0.

Ссылка

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

Он обучал очень много public kernel, стакал их как не в себе, и это реально дало очень классные результаты. Хотелось сказать про подход другого нашего коллеги, Андрея c ником Лаоль в ODS. Можно было это все не делать, а просто взять кучу public kernel различных, даже на tf-idf есть, gru всякие конволюционные.

Ссылка

У него один из лучших подходов был, с которым мы долго держались в топ-15, пока к нам не присоединился Алексей и Атанас, он сблендил блендинг и стакинг этого всего. А также очень крутой момент, который, как я понял, ни одна из команд не использовала, мы потом еще делали фичи из результатов API организаторов. Про это дальше расскажет Алексей.

Расскажу про подход, который я использовал, и чем мы это завершили. Алексей Носков:
— Привет.

Первый, который в основном вначале работал — обучил какое-то количество моделей, посмотрел ошибки на кросс-валидации, на каких примерах оно делает очевидные ошибки, и исправил исходя из этого препроцессинг, потому что там как раз понятнее, как их пофиксить. У меня все было достаточно просто: 10 фолдов кросс-валидации, модели, предуобученные на различных векторах с различным препроцессингом, чтобы у них был побольше diversity в ансамбле, немного аугментация и два цикла разработки.

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

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

Ссылка

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

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

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

Первая ссылка, вторая ссылка

Интересным отличием были модели, обученные на BPE. Есть SentencePiece — токенайзер Google, который позволяет разбить на токены, в которых не будет UNK вообще. Ограниченный словарь, в котором любая строка разобьется на какие-то токены. Если в реальном тексте количество слов больше, чем целевой размер словаря, они начинают разбиваться на более мелкие кусочки, и получается промежуточный подход между character level и word level моделями.

Для алгоритма BPE в сети было достаточно легко найти запредтрейненные эмбеддинги, и с некоторым фиксированным словарем — у меня как раз хорошо заработал словарь 50к — можно было еще обучить модели, которые давали неплохой (неразборчиво — прим. Там используется два основных алгоритма построения: BPE и Unigram. ред.), чуть-чуть похуже, чем обычные на fastText, зато они были очень слабо скоррелированы со всеми остальными и давали хороший буст.

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

Просто скидывать их все в какой-то стекер? У меня было большое количество моделей. Обучал на этом LightGBM, багал 20 запусков с различными семплами, докидывал немного мета-фич аналогично тому, что делал Атанас, и в конце это стало наконец работать, давать некоторый буст поверх простого усреднения. Работало не очень хорошо, он переобучался, но поскольку модели представляли из себя группы, которые были достаточно сильно скоррелированы, я их просто объединял в эти группы, внутри каждой группы усреднял и получал 5–7 групп очень похожих моделей, от которых в качестве фич для следующего уровня использовал усредненные значения.

Ссылка

Больше всего в итоге добавило API, которое нашел Андрей и которое содержит сходный набор лейблов. Модели для них организаторы построили изначально. Поскольку он изначально отличался, то участники его не использовали, нельзя было его просто сопоставить с теми, которые нам необходимо было предсказать. Зато если это закидывалось в качестве мета-фич в хорошо работающий стекинг, то давало замечательный буст, особенно на классе TOXIC, который, судя по всему, был наиболее сложным на лидерборде, и позволило нам на несколько мест подпрыгнуть в конце, буквально в последний день.

Работало уж очень подозрительно хорошо, поэтому мы выбрали два сабмишна по следующему принципу: один — бленд моделей без API, который был получен до этого, плюс стекинг с метафичами из API. Поскольку мы обнаружили, что у нас так хорошо работает стекинг и API, то перед финальным сабмишном немного сомневались в том, насколько хорошо это перенесется на private. Здесь у меня перепутаны отметки. Тут получилось 0,9880 на паблике и 0,9874 на private.

Обошлось, не слетели, и в итоге с результатом 0,9876 на private мы получили десятую позицию. И второй — бленд моделей без API, без использования стекинга и без использования LightGBM, поскольку были опасения, что это какое-то небольшое переобучение под паблик, и мы можем с этим сильно слететь. Всё.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть