Julia
Дом, который построил Джет
Нейронная сеть для распознавания имен на Julia (Flux.jl)
Доброго времени суток,
хотел бы поделиться своей небольшой работой в области машинного обучения. Данный материал не стоит рассматривать как гайд или урок, так как я близко не являюсь датасаентистом, просто подвернулся интересная задача, и я решил использовать нейронные сети для ее решения.
Суть задачи: есть у нас около 112 тысяч никнеймов. Кто-то пишет свое реальное имя правильно, кто-то пишет, но не правильно, кто-то решил написать какое-либо слово, кто-то - абсурдный никнейм по типу аувау324. И вот нам нужно узнать, сколько в данном списке имен Влад, сколько - Саша, и сколько "мусорных" имен.
Решить данную задачу можно было и без использования ML, но:
а) это было бы долго и много ручной работы
б) хотелось поиграться с нейронками
Решая данную задачу, я во многом полагался на данную интересную статью на Хабре.
Во время решения донного задания я допустил несколько критических ошибок, которые повлияли на конечный результат (ошибки рассмотрю по ходу объяснения алгоритма решения).
Алгоритм решения:
1) Подготовка данных
Первым делом нужно было подготовить данные к манипуляциям. Чем меньше лишних данных и чем меньше форм этих данных - тем лучше справятся алгоритмы машинного обучения.
Важно понимать, что свести все данные к одной форме и убрать n-нное количество информации - не одно и тоже. Можно сравнить данный процесс со сжатием без потерь - информация не удаляется, изменяется лишь ее форма.
Подготовил данные я следующим образом:
1) Преобразовал все слова в lowercase.
2) Нормализировал UTF символы (например, перевел ó в о).
3) Удалил смайлики и прочий мусор из имен.
4) Удалил имена, которые имели 2 символа или меньше.
Важный момент(!) - так как в базе мы имели имена на английском (например Julia), транслитом (например Yulia) и кириллицей (Юлия\Юлія), я не додумался перевести все имена, допустим, на латинские буквы, что повлияло на сложность вычислений и на точность нейронной сети. Это важный момент - все данные должны быть в одной форме.
2) Составления словаря
Если вы знакомы с нейронными сетями, то знаете, что у нейронной сети есть входной слой, размер которого определен на этапе "компиляции" тела нейронной сети и не может быть изменен в ходе выполнения алгоритма. Плюс, нам нужно представить слова как набор чисел, с которыми сможет удобно работать наша нейронка.
Для решения данного пункта я воспользовался материалом с Хабровской статьи, и создал словарь для моего набора слов.
Создавал я словарь следующим образом:
1) Каждое слово делил на би-граммы (если в слове было нечетное количество букв - то на би-граммы и уни-граммы).
2) Далее, если данной би-граммы\уни-граммы еще нет в словаре - записывал ее в словарь. На выходе у меня получился словарь из 2371 би-грамм\уни-грамм без повторений.
Почему я использовал би-граммы, а не уни-граммы? Мой алгоритм не предусматривал определения, например, количества данной буквы в слове, и поэтому нейронная сеть не смогла бы отличить слово "Ана" от "Анна", и вот почему...
3) Векторизация словаря
Теперь нам нужно превратить наш словарь и слова, которые мы будем запихивать в нейронку, в цифры.
Для начала векторизируем словарь:
1) Так как наш словарь имеет 2871 элемент, закодируем каждое "слово" из словаря в 2371-элементный вектор. Для примера, имеем словарь ["a", "b", "c"] - тогда буква "а" будет иметь вектор [1, 0, 0] относительно словаря, "b" - [0, 1, 0], "c" - [0, 0, 1]. И теперь, если мы захотим векторизировать слово "abbca" относительно такого словаря, то у нас получится вектор [1, 1, 1]. И теперь понимаем, почему данный алгоритм не смог бы различаться некоторые слова, если бы мы использовали уни-граммы - для него что слово "aaabbbbccc", что "abc", имеют одинаковый вектор [1, 1, 1].
Таким Макаром мы также установили количество входных нейронов для нашей сети - 2371, так как каждое наше слово теперь будет кодироваться относительно словаря в 2371-размерный вектор и на каждый нейрон будет подаваться 1 или 0.
4) Создание нейронной сети
Самый спорный момент, так как я почти не разбираюсь в проектировании нейронных сетей и ML в целом, так что я эмпирическим путем узнал, что моя видеокарта способна с адекватной скоростью тренировать нейронку с одним входным слоем, одним скрытым на 1840 нейронов, один скрытый на 700 нейронов, ну и один выходной слой (кстати, размер выходного слоя у нас зависит от количества классов, которые вы хотите иметь. Я хотел, что бы нейронка смогла различить 105 разных имен, поэтом размер выходного слоя у меня соответственный). Такая нейронная сеть давала мне около 75% точности после 100 эпох обучения.
5) Подготовка данных
Вручную мною было классифицировано 2000 имен. По ходу ручной классификации было найдено 105 уникальных имени (класс №1 означал "мусорное имя"). На этих именах я тренировал и тестировал нейронную сеть.
Вот, в принципе, и вся история. Надеюсь, данная информация поможет кому-то не допустить ошибок, которые допустил я, или в качестве примера для построения более сложного алгоритма. Весь код проекта можно найти на тут.
Удачи, никогда не забывайте, что программирование - это не знания языка программирования, а в первую очередь умение правильно управлять данными. А еще проектирование не помешает :)