Первые шаги. Часть 1. Логистическая регрессия и "Титаник" - aeDeaf/physlearn GitHub Wiki
В первой части данного руководства мы познакомимся с классом NeuralNet и попробуем реализовать с его помощью нейронную сеть, которая будет решать задачу логистической регрессии (классификации данных), а именно, мы будем предсказывать, выживет ли пассажир на борту Титаника (основываясь на классе его каюты, поле и возрасте) или нет. Исходные данные в формате csv можно найти здесь.
Исходный код
from physlearn.NeuralNet.NeuralNet import NeuralNet
from physlearn.examples import Titanic
import matplotlib.pyplot as plt
%matplotlib inline
В первой строке мы импортируем класс NeuralNet, предоставляющий нам возможность работать с нейронными сетями на высоком уровне.
Во второй строке импортируется модуль Titanic, который подготовит данные для обучения.
Далее импортируется модуль matplotlib
, который отвечает за вывод графиков.
Последняя строка необходима только в том случае, если вы работаете в среде Jupyter Notebook. В противном случае, данная строка приведет только к ошибке исполнения, и никакого смысла в ней нет.
Далее, получим данные при помощи модуля Titanic:
(learn_data, learn_output), (cv_data, cv_output) = Titanic.create_datasets(0.2)
Для получения данных используется функция create_datasets
. Как легко заметить, данная функция возвращает два набора данных - learn и cv, которые представляют из себя обучающую и тестовую выборки. Единственный аргумент данной функции - это процент (если быть более точным - отношение) размера тестовой выборки к общему размеру набора данных. Стандартным является разбиение 80% / 20% - то есть обучение происходит на 80% данных, а проверка - на оставшихся 20%.
Далее, создается объект типа NeuralNet:
net = NeuralNet(-7, 7)
Параметрами конструктора класса NeuralNet являются два числа, которые представляют из себя нижнюю и верхнюю грань для генератора случайных чисел, который создает начальные матрицы весов нейронной сети.
Затем описывается вся архитектура нейронной сети:
net.add_input_layer(3)
net.add(5, net.sigmoid)
net.add(5, net.sigmoid)
net.add_output_layer(1, net.sigmoid)
Методы add_input_layer
и add_output_layer
добавляют входной и выходной слои нейронной сети соответственно. Метод add
добавляет скрытый слой. Во всех случаях первый параметр - это количество нейронов в слое, второй параметр (когда он присутствует) - функция активации слоя. Указывать функцию активации входного слоя нельзя (да не очень то и нужно).
В данном случае у нас три входных нейрона (поскольку предсказание ведется на основе трех параметров - класса каюты, пола и возраста), по пять нейронов в двух скрытых слоях, а так же один выходной нейрон (поскольку нам надо классифицировать все данные на два класса - выжил / не выжил, или, что тоже самое 1 / 0).
Далее необходимо "скомпиилировать" нашу нейронную сеть:
net.compile()
Метод compile
создает граф вычислений TensorFlow, соответствующий нейронной сети, описанной выше, устанавливает все необходимые внутренние переменные, и подготавливает к работе. Использование этого метода обязательно, без него нейронная сеть не будет работать, и при попытке вызова многих методов Вы получите ошибку.
Далее установим тип нашей нейронной сети:
net.set_train_type('logistic')
В данном случае, типом является logistic
- логистическая регрессия - то есть задачи классификации входных данных на один (или несколько) дискретных классов. Второй возможный вариант - это prediction
- то есть задача апроксимации нашей нейронной сетью какой-либо функции. Далее, мы будем использовать второй тип сети в одном из следующих разделов, когда будем апроксимировать синус нейронной сетью. Аналогично compile
, использование этого метода обязательно, и без укзания типа сети, работать ничего не будет.
Далее происходит обучение нейронной сети и вывод графика ценовой функции:
cost_list = net.train(learn_data, learn_output, 50, 100000, 0.001)
plt.plot(cost_list)
plt.show()
Для обучения используется стохастический градиентный спуск.
Первые два параметра - это данные обучающей выборки (входные данные и правильные ответы). Третий параметр - это размер подвыборки, которая на каждой итерации случайно (стохастически) выбирается из обучающих данных. Использование такого подхода позволяет частично решить одну из главных проблем обычного градиентного спуска, а именно, стохастический метод менее подвержен "сваливанию" в локальные экстремумы, а так же он работает быстрее, по сравнению с обычным.
Предпоследний параметр - это количество итераций обучения, а последний - скорость обучения.
Помимо этого, в данном участке кода происходит отрисовка графика зависимости ценовой функции от номера итерации. Конкретный вид графика зависит от начальных условий (которые случайны) но приблизительно он должен выглядеть
В случае работы в среде Jupyter Notebook строчку plt.show()
можно опустить.
Затем можно посчитать процент "правильных ответов" нашей нейронной сети на обучающих и тестовых данных:
ok_class = 0
output = net.run(learn_data)[0]
pred_clases = []
for item in output:
if item >= 0.5:
pred_clases.append(1)
else:
pred_clases.append(0)
for index, _ in enumerate(pred_clases):
if pred_clases[index] == learn_output[0][index]:
ok_class += 1
ok_percent = (ok_class / output.shape[0]) * 100
print('ok percent on learn data: ', ok_percent, '%')
ok percent on learn data: 81.08581436077058 %
ok_class = 0
output = net.run(cv_data)[0]
pred_clases = []
for item in output:
if item >= 0.5:
pred_clases.append(1)
else:
pred_clases.append(0)
for index, _ in enumerate(pred_clases):
if pred_clases[index] == cv_output[0][index]:
ok_class += 1
ok_percent = (ok_class / output.shape[0]) * 100
print('ok percent on cv data: ', ok_percent, '%')
ok percent on cv data: 77.62237762237763 %
Как можно видеть, процент правильных ответов на обоих выборках примерно совпадает, что говорит о правильном выборе архитектуры нейронной сети.
В качестве эксперимента можете попробовать увеличить количество нейронов в скрытых слоях. В результате этого, точность сети на обучающей выборке увеличиться (вплоть до 100%), но с тестовой выборкой произойдет ровно обратное - в итоге, наша нейронная сеть вместо того, что бы найти закономерности между входными и выходными данными, просто заучит их, из-за чего у нее появятся проблемы с тестовой выборкой.
На этом я бы хотел закончить первую часть введения. В следующей части мы познакомимся с новым классом NeuralNetPro, который предоставляет широкие возможности для использования сторонних оптимизаторв, и в качестве примера мы решим ту же задачу, но будем подбирать параметры сети при помощи метода Нелдера-Мида.