В современном мире люди получают множество писем, и в полный рост встает проблема с их классификацией и упорядочиванием почтового ящика. Инженер из США Андрей Куренков в своем блоге рассказал о том, как решил эту задачу с помощью нейронной сети. Мы решили осветить ход этого проекта и представляем вам первую часть рассказа.
Куренков пишет, что один из его любимых мини-проектов, EmailFiler, появился благодаря заданию на курсе «Введение в машинное обучение» в Технологическом Институте Джорджии. Задание состояло в том, чтобы взять какой-либо набор данных, применить к нему ряд контролируемых обучающихся алгоритмов и проанализировать результаты. Суть в том, что по желанию можно было использовать собственные данные. Разработчик так и поступил - экспортировал данные своего gmail для исследования возможностей машинного обучения в решении задачи сортировки электронной почты.
Куренков пишет, что давно понял, что электронные письма лучше всегда иметь под рукой на случай, если вдруг когда-нибудь понадобится к ним обратиться. С другой стороны, ему всегда хотелось свести количество непрочитанных входящих сообщений к нулю, какой бы безнадежной ни была эта идея. Поэтому много лет назад инженер начал сортировать электронные письма примерно по дюжине папок, и к моменту поступления на курсы машинного обучения, в этих папках накопились тысячи писем.
Так возникла идея проекта по созданию классификатора, который мог бы предложить единственную категорию для каждого входящего письма - так, чтобы отправлять письмо в соответствующую папку одним нажатием кнопки.
Категории (папки) и количество писем в каждой из них на момент старта проекта
В качестве входных данных выступали письма, в число выходных данных попадали категории писем. Сложность заключалась в том, что разработчику хотелось использовать тексты писем и метаданные, но понять, как превратить все это в пригодный для машинного обучения набор данных, было нелегко.
Любой, кто изучал обработку естественных языков, сразу предложит один простой подход - использование модели Bag of Words (Множество слов). Это один из простейших алгоритмов классификации текстов - найти N общих слов для всех текстов и составить таблицу бинарных признаков для каждого слова (признак равен 1, если заданный текст содержит слово, и 0 - если не содержит).
Куренков проделал это для группы слов, найденных во всех его письмах, а также для топ-20 отправителей писем (так как в некоторых случаях отправитель строго коррелирует с категорией письма; например, если отправитель -научный руководитель в университете, то категорией будет «Исследования») и для топ-5 доменов, с которых ему присылали письма (поскольку домены типа gatech.edu строго указывают на такие категории как, например, «Обучение»). Так, где-то после часа, потраченного на написание парсера электронной почты, он смог получать данные о своём ящике в формате csv (значения, разделённые запятыми).
Насколько хорошо все работало? Неплохо, но хотелось большего. Куренков говорит, что тогда увлекался фреймворком для машинного обучения Orange ML на Python, и, как и было положено по заданию, протестировал несколько алгоритмов на своем наборе данных. Выделялись два алгоритма - лучше всего показали себя деревья принятия решений, а хуже всего - нейронные сети:
Так с небольшим набором данных справились деревья принятия решений
А так - нейронные сети
Если внимательно взглянуть на эти графики из OpenOffice Calc, то можно увидеть, что лучший результат деревьев принятия решений на тесте - где-то 72%, а нейронных сетей - жалкие 65%. Ужас! Это, конечно, лучше, чем разложить всё наугад, учитывая, что там 11 категорий, но до отличного результата далеко.
Почему же результаты оказались такими удручающими? Дело в том, что признаки, полученные для набора данных, оказались весьма примитивны - простой поиск 500 наиболее часто встречающихся в письмах слов даст нам не так уж много полезных индикаторов, но много общеупотребительных конструкций, которые встречаются в английском, например «that» или «is». Инженер понял это и предпринял следующее: полностью убрал из списка слова из трёх и менее букв и написал немного кода, чтобы выбрать наиболее часто встречающиеся слова для каждой категории отдельно; но он все еще не представлял, как улучшить результат.
Попытка номер два
Спустя пару лет Куренков решил еще раз попытаться решить задачу - хоть сам проект в университете уже был давно сдан, хотелось улучшить результаты.
В этот раз инженер решил в качестве одного из основных инструментов применить Keras, потому как он написан на Python и также неплохо ладит с пакетами NumPy, pandas и scikit-learn и поддерживается библиотекой Theano.
К тому же получилось так, что у Keras есть в лёгком доступе несколько примеров, с которых можно начать работу, включая задачу классификации разнообразных текстов. И вот, что интересно - данный пример использует такие же функции, какие инженер использовал ранее. Он определяет 1000 наиболее часто встречающихся в документах слов, бинарные признаки и обучает нейронную сеть с одним скрытым слоем и dropout-регуляризацией предсказывать категорию заданного текста, основываясь исключительно на этих признаках.
Итак, первое, что приходит в голову: испробовать этот пример на собственных данных - посмотреть, улучшит ли работу Keras. К счастью, сохранился старый код для парсинга почтового ящика, а у Keras есть удобный класс Tokenizer для получения признаков текста. Поэтому можно легко создать набор данных в таком же формате, как и в примере, и получить новую информацию о текущем количестве электронных писем:
Using Theano backend. Label email count breakdown: Personal:440 Group work:150 Financial:118 Academic:1088 Professional:388 Group work/SolarJackets:1535 Personal/Programming:229 Professional/Research:1066 Professional/TA:1801 Sent:513 Unread:146 Professional/EPFL:234 Important:142 Professional/RISS:173 Total emails: 8023
Восемь тысяч писем никак не назовёшь большим набором данных, но всё же этого достаточно для того, чтобы немного позаниматься серьёзным машинным обучением. После перевода данных в нужный формат остаётся лишь посмотреть, работает ли обучение нейронной сети на этих данных. Пример с использованием Keras даёт легко сделать следующее:
85% точности! Это намного выше жалких 65% старой нейронной сети. Отлично.
Но-почему?
Старый код делал, в общем-то, следующее - определял наиболее часто встречающиеся слова, создавал бинарную матрицу признаков и обучал нейронную сеть с одним скрытым слоем. Может быть, всё из-за нового нейрона «relu», dropout-регуляризации и использования методов оптимизации, отличных от стохастического градиентного спуска? Поскольку признаки, найденные по старым данным, бинарные и представлены в виде матрицы, их очень легко превратить в набор данных для обучения этой нейронной сети. Итак, вот результаты:
Итак, старое решение по определению категорий электронных писем было неудачным. Возможно причиной стало сочетание сильных ограничений признаков (вручную определённые топовые отправители, домены и слова из каждой категории) и слишком маленького количества слов.
Пример, использующий Keras, просто отбирает 1000 наиболее часто встречающихся слов в большую матрицу без дополнительного фильтрования и передает её нейронной сети. Если сильно не ограничивать отбор признаков, из них могут быть выбраны более подходящие, что увеличивает общую точность работы алгоритма.
Причина или в этом или в наличии ошибок в коде: его изменение с целью сделать процесс отбора признаков менее ограничивающим приводит к повышению точности лишь до 70%. В любом случае, инженер выяснил, что можно было улучшить свой старый результат с использованием современной библиотеки машинного обучения.
Теперь возникал новый вопрос - можно ли добиться еще более высокой точности?