-
Notifications
You must be signed in to change notification settings - Fork 0
Operation description
ИИ (искусственный интеллект) - свойство машин, компьютерных программ и систем выполнять интеллектуальные и творческие функции человека, находить способы решения задач, уметь делать выводы и принимать решения.
Искусственная нейронная сеть - математическая модель, а также её программное или аппаратное воплощение, построенная по принципу организации и функционирования биологических нейронных сетей - сетей нервных клеток живого организма.
Нейронные сети не программируются в привычном смысле этого слова, они обучаются. Возможность обучения - одно из главных преимуществ нейронных сетей перед традиционными алгоритмами. Технически обучение заключается в нахождении коэффициентов связей между нейронами (весами). В процессе обучения нейронная сеть способна выявлять сложные зависимости между входными данными и выходными, а также выполнять обобщение. Это значит, что в случае успешного обучения сеть сможет вернуть верный результат на основании данных, которые отсутствовали в обучающей выборке
Машинное обучение является одним из направлений искусственного интеллекта. Основной принцип заключается в том, что машины получают данные и «обучаются» на них
Глубокое обучение является подмножеством машинного обучения. Оно использует некоторые методы машинного обучения для решения реальных задач, используя нейронные сетей, которые могут имитировать человеческое принятие решений. Глубокое обучение требует огромных массивов данных для обучения. Это объясняется тем, что существует огромное количество параметров, которые необходимо настроить для алгоритмов обучения, чтобы избежать ложных срабатываний. Например, алгоритму глубокого обучения может быть дано указание «узнать», как выглядит кошка. Чтобы произвести обучение, потребуется огромное количество изображений для того, чтобы научиться различать мельчайшие детали, которые позволяют отличить кошку от гепарда, пантеры или лисицы.
Этот проект основан на глубоком обучении с применением нейронных сетей.
Проект использует рекуррентную нейронную сеть с LSTM нейронами. В качестве библиотеки выбран keras, являющийся надстройкой над tensorflow.
ВОПРОС: Почему keras, а не чистый tensorflow?
ОТВЕТ: Keras позволяет более удобно использовать функционал tensorflow. Чистый tensorflow довольно низкоуровневый, следовательно, не всегда удобен в использовании и требует больше лишнего кода. Keras является высокоуровневой надстройкой над tensorflow.
Для тренировки нейросети используется русскоязычный корпус коротких текстов RuTweetCorp. Ссылка: https://study.mokoron.com/
Для загрузки данных используется модуль utils/load_data.py
. Данные загружаются и перемешиваются, используя методы сторонней библиотеки pandas. В результате своей работы он возвращает 4 массива: тренировочный, тестовый, ответы на тренировочный, ответы на тестовый. Тестовые массивы потребуются для оценки точности предсказания уже после тренировки (они не будут использоваться в момент тренировки).
Тренировочные и тестовые массивы содержат только тексты, массивы с ответами содержат 1
(положительный окрас) либо 0
(отрицательный окрас).
Словарь потребуется в дальнейшем для перевода текстов в последовательности чисел.
Модуль utils/tokenizer.py
позволяет создать словарь наиболее часто использующихся слов в тех самых корпусах текстов. По умолчанию размер словаря равен 100.000 самых часто использующихся слов. Но перед тем, как загонять слово в словарь, извлекается его основа (стемма). Для получения основы слова используется внешняя библиотека snowballstemmer. Подробно об алгоритме стемминга: https://ru.wikipedia.org/wiki/%D0%A1%D1%82%D0%B5%D0%BC%D0%BC%D0%B8%D0%BD%D0%B3
ВОПРОС: Зачем именно наиболее часто используемые слова?
ОТВЕТ: Представим ситуацию, что в наших текстах огромное количество разных слов, например, 500.000. Причем последние 400.000 из них используются не чаще 10 раз во всех текстах. В лучшем случае обучение на таком массиве данных принесет тот же процент точности, что и на ограниченном отсортированном массиве. В худшем случае точность предсказания может упасть, поскольку нейросети будет недостаточно такого количества слов, для того чтобы запомнить, в каком случае редко использующееся слово положительное, а в каком отрицательное. Поэтому сначала сортируем массив в порядке от наиболее к менее часто использующимся словам, затем обрезаем его, скажем, на 100.000 первых слов.
Так выглядит часть словаря:
Поскольку нейронная сеть не может напрямую работать со строками, сначала их нужно преобразовать в числа. Для этого потребуется словарь, созданный выше. Каждое слово в каждом тексте заменяется на индекс этого слова в словаре. Если в словаре нет этого слова, оно просто пропускается.
Для примера возьмем предложение "Только сегодня был еще". После преобразования получаем следующее: 31 32 23 26
Основа слова "только" - "тольк". Находится в словаре под номером 31
Основа слова "сегодня" - "сегодн". Находится в словаре под номером 32
и так далее.
Вот еще несколько примеров
>>> tokenizer.texts_to_sequences(['Только сегодня был еще'])
[[31, 32, 23, 26]]
>>> tokenizer.texts_to_sequences(['Летом я хорошо отдохнул'])
[[141, 2, 51, 3703]]
>>> tokenizer.texts_to_sequences(['На прошлой неделе мы писали контрольную работу. Я получил за нее двойку'])
[[5, 256, 145, 45, 205, 720, 131, 2, 159, 27, 1, 4521]]
Заполнение текстов - значит добавление нулей к нашим последовательностям чисел.
Обрезание текстов - значит выбрасывание лишней части текста.
Другая проблема заключается в том, что последовательности должны быть одинаковых длин.
И хотя рекуррентная нейронная сеть может принимать последовательности произвольных длин, библиотеке tensorflow нужны последовательности одинаковых длин для правильной работы. Такая у нее особенность.
ВОПРОС: Почему так?
ОТВЕТ: Если попытаться создать Embedding слой, не указав фиксированную длину входных последовательностей, выпадет ошибка. В документации это объясняется тем, что у нас идет Dense слой после всех остальных, и без фиксированной длины входных данных невозможно посчитать его выходную форму.
Есть два способа обрезания и заполнения: с начала и с конца. Но в нашем случае и обрезание и вставка нулей производится с начале последовательностей.
ВОПРОС: Почему нули вставляем в начало, а не в конец?
ОТВЕТ: У LSTM нейронов есть так называемое "внутреннее состояние". Если мы начнем загонять в сеть сначала нули, то по сути это ничего не будет означать. Как только нули закончатся, сеть поймет, что теперь начинается последовательность. Однако если мы вставим нули в конец и начнем загонять такие последовательности в сеть, после того, как последовательность закончится, и в сеть начнут поступать кучи нулей, это может разрушить "внутреннее состояние" сети.
После подготовки данных запускается процесс тренировки. Нейросеть содержит 4 слоя: Embedding, 2xLSTM, Dense
Представим, что есть два одинаковых по смыслу предложения. "У меня был хороший день" и "У меня был отличный день". Человек сразу поймет, что у этих двух предложений смысл одинаковый, однако компьютер - нет.
Представим эти два предложения в виде последовательностей.
0 - У
1 - меня
2 - был
3 - хороший
4 - день
5 - отличный
[0, 1, 2, 3, 4]
[0, 1, 2, 5, 4]
Посмотрев на две эти последовательности, можно ли определить, что они имеют одинаковый смысл? Ведь 3 и 5 - разные числа. Поэтому довольно сложно будет определить тональность, ведь эти слова различны. Embedding слой решает эту проблему, составляя под каждое слово свой вектор, и для схожих слов будут схожие вектора. Короче этот слой разбивает слова, схожие друг с другом, на группы, чтобы мы знали, какие слова схожи с какими.
Embedding также позволяет сократить необходимый размер датасета для обучения, обучаясь различать схожие по смыслу слова. Если бы сеть не понимала, что у нескольких слов близкое значение, нам бы потребовалось колоссальное количество данных для обучения
Обучение происходит следующим образом: например, сеть увидела предложение "у меня был хороший день" с положительным окрасом. Ок. Затем она видит предложение "у меня был отличный день", аналогично, с положительным окрасом. Затем она "сдвигает" слово "хороший" ближе к слову "отличный". И наоборот, если бы она встретила предложение "у меня был ужасный день", она бы "сдвинула" слово "ужасный" очень далеко от "хороший" и "отличный", так как окрас уже отрицательный.
Более подробно: http://colah.github.io/posts/2014-07-NLP-RNNs-Representations/ (раздел Word Embeddings)
Более понятно, чем по ссылке ниже, описать вряд ли смогу.
http://colah.github.io/posts/2015-08-Understanding-LSTMs/
Самый обычный слой (в данном случае с одним нейроном), который обрабатывает входной вектор, прогоняет его через сигмоидальную функцию, для того чтобы выдать окончательное значение в диапазоне (0; 1)
Функция активации нужна для определения выходного значения нейрона в зависимости от результата взвешенной суммы входного вектора (выход Dense слоя)
В нашем случае выбрана сигмоидальная функция активации.
ВОПРОС: Почему именно такая?
ОТВЕТ: Можно использовать любую функцию активации (даже написать свою собственную), но в данном случае выбрана сигмоида, поскольку она лучше всего подходит для задачи классификации.
Функция потерь используется для расчета ошибки между нужными и полученными ответами. Наша цель - уменьшить эту ошибку.
Во время тренировки нейросеть будет предсказывать вероятность того, положительный ли пост (для каждого поста). И как нам узнать, насколько хорошо или плохо сеть сделала предсказание? В этом нам поможет функция потерь. Она должна выдать высокие значения для плохих предсказаний и низкие для хороших.
Выходные значения (финальные предсказания) должны быть от 0 до 1, поэтому используем функцию потерь "бинарная кросс-энтропия" (двоичная), которая как раз предназначена для расчета ошибки у значений от 0 до 1 (задача бинарной классификации).
Оптимизатор - это алгоритм, благодаря которому и происходит обучение нейронной сети, используя градиенты, которые получаются при обратном распространении ошибки.
Алгоритм для стандарной feedforward нейросети.
При инициализации нейросети веса задаются рандомно (маленькие значения).
Обратное распространение ошибки используется для правильного расчета градиентов (значений, которые используются для настройки весов).
Идея: настроить все веса нейронной сети так, чтобы получить желаемый результат. Допустим, мы хотели получить на выходе единицу - нам нужно настроить все веса так, чтобы получилось значение, близкое к единице.
Алгоритм:
- Прогнать данные напрямую и получить результат (как при обычной работе нейросети)
- Рассчитать ошибку, используя функцию потерь
- Разворачиваемся и идем обратно (от выходного слоя к входному), передавая ошибку, рассчитывая градиенты для каждого веса у каждого нейрона на каждом слое. Используем оптимизатор для каждого рассчитанного градиента, затем подстраиваем вес и смещение.
- Повторяем процедуру для каждого набора данных.
Для обучения рекуррентных нейросетей используется backpropagation through time (на русском нет названия, дословно - "обратное распространение через время"). Оно практически идентично с обычным обратным распространением, называется "через время", потому что, грубо говоря, оно происходит обратно во времени (если понять, как работет RNN/LSTM ячейка, можно понять, почему называется "через время").
В отличие от обычного backpropagation, здесь нам нужна общая ошибка - это сумма всех ошибок на каждом временном шаге. Временной шаг - это одно слово из предложения. Аналогично, нам нужна сумма градиентов (с каждого временного шага). Для расчета градиентов используется алгоритм тот же, что и для обычного backpropagation.