Нейросеть за пять минут! Is it real?!

Добрый день, уважаемые читатели. Сатирический заголовок говорит о том, что сегодняшней темой станут нейронные сети, а точнее «алгоритм» их обучения. Ведь обучить нейросеть не значит создать класс модели и вызвать метод fit (sklearn) или заполнить её слоями (keras). Это значит предобработать данные, подобрать гиперпараметры, сохранить её для дальнейшего использования и многое другое. Сегодня мы и сделаем это, исключив подбор гиперпараметров модели, это я доверяю вам, чтобы добавить интерактива.

Будет полезным:

Подпишись на группу Вконтакте и Телеграм-канал. Там еще больше полезного контента для программистов.

А на моем YouTube-канале ты найдешь обучающие видео по программированию. Подписывайся!

Импорт

В качестве примера мы будем использовать обычную реализацию нейросети из sklearn.

In[1]:
import pandas as pd
import pandas_profiling as pprf
import numpy as np

from sklearn.externals import joblib
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import normaliz

Команды для установки пакетов:

pip install -U scikit-learn
pip install pandas
pip install numpy
pip install pandas_profiling

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

Предобработка (preprocessing) данных

In[2]:
path = r'E:\datasets\mlcourse\adult_data.csv'
df = pd.read_csv(path)
df.head()

Загрузим данные с помощью pandas, в частности функции read_csv.

Нам необходимо получше «узнать» датасет, чтобы продолжить с ним работу. В этом нам поможет профилирование.

In[3]:
profiler = pprf.ProfileReport(df)
profiler.to_file(r'C:\profiling.html')

Мы создали отчет по нашей таблице и загрузили его в отдельный файл. Не буду раскрывать карты и демонстрировать, что находится в нём, но могу сказать, что вы будете приятно удивлены, если ранее не сталкивались с pandas_profiling.

У нас довольно много категориальных данных, которые мы будем кодировать с помощью One-Hot encoding.

Однако столбец education-num является кодированием меток из столбца education, потому он нам не нужен.

In[4]:
df = df.drop('education-num', axis=1)
df.head()

Также нам необходимо закодировать наш target — столбец salary. Для этого воспользуемся методом map класса Series. Он принимает словарь типа:

<value : coding>

Метод возвращает новый объект Series, потому следующая наша команда выглядит так:

In[5]:
df['salary'] = df['salary'].map({'<=50K': 0, '>50K': 1})
df.head()

По привычке, в качестве Out в Jupyter Notebook я выдаю первые 5 строк таблицы, дабы посмотреть, корректно ли сработала та или иная конструкция.

Далее нам необходимо закодировать категориальные переменные. Мы не будем делать это вручную: за нас это сделает функция get_dummies из библиотеки pandas.

In[6]:
df = pd.get_dummies(df)
df.values.shape

Out[6]:
(32561, 108)

Итого у нас получилось 108 столбцов — не так много, как могло казаться ранее. Теперь проверим информацию о таблице, чтобы точно убедиться, что всё верно.

In[7]:
df.info()

Out[7]:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 32561 entries, 0 to 32560
Columns: 108 entries, age to native-country_Yugoslavia
dtypes: int64(6), uint8(102)
memory usage: 4.7 MB

Отлично, мы имеем все столбцы в числовом виде, однако это ещё не всё.

Нам необходимо нормализировать данные: именно в таком виде лучше передавать числовые значения

Отделим нашу цель и образцы из общего набора:

In[8]:
y = df['salary']
del df['salary']
X = normalize(df.values)
X.shape, y.shape

Out[8]:
((32561, 107), (32561,))

Теперь же разделим данные на тренировочные и тестовые с помощью train_test_split:

In[9]:
X_train, X_test, y_train, y_test = train_test_split(X, 
                                                    y, 
                                                    test_size=0.2)
X_train.shape

Out[9]:
(26048, 107)

26 тысячи тренировочных образцов мы получили в итоге. Теперь начнём создавать наш классификатор.

Нейросеть (neural network)

In[10]:
nntw = MLPClassifier(activation='logistic', 
                     hidden_layer_sizes=100,
                     alpha=0.0001,
                     learning_rate_init=0.2,
                     shuffle=True,
                     max_iter=300)

100 скрытых слоёв, сигмоидальная функция активации, штраф L2-регуляризации в 0.0001, перемешивание и т.д. указываем при создании экземпляра класса. Далее обучаем модель:

In[11]:
nntw.fit(X_train, y_train)
nntw.score(X_test, y_test), nntw.score(X_train, y_train)

Out[11]:
(0.7868877629356671, 0.7953777641277642)

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

Недавно от меня была статья про консервацию объектов Python с помощью pickle и json. Однако сегодня мы будем использовать joblib — подмодуль sklearn для сохранения модели в бинарный файл и её загрузки в объект Python.

Программный интерфейс joblib, который нам необходим, такой же, как и у pickle:

In[12]:
with open(r'C:\model.pkl', 'wb') as f:
  joblib.dump(nntw, f) 
In[13]:
with open(r'C:\model.pkl', 'rb') as f:
  nntw = joblib.load(f)

nntw  

Out[13]:
MLPClassifier(activation='logistic', alpha=0.0001, batch_size='auto',
       beta_1=0.9, beta_2=0.999, early_stopping=False, epsilon=1e-08,
       hidden_layer_sizes=100, learning_rate='constant',
       learning_rate_init=0.2, max_iter=300, momentum=0.9,
       nesterovs_momentum=True, power_t=0.5, random_state=None,
       shuffle=True, solver='adam', tol=0.0001,validation_fraction=0.1,
       verbose=False, warm_start=False)

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

In[14]:
nntw.score(X_test, y_test), nntw.score(X_train, y_train)

Out[14]:
(0.7868877629356671, 0.7953777641277642)

Отлично, всё сходится.

Заключение

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

Документ .ipynb для полного его разбора вы можете найти здесь.

Также рекомендую прочитать статью Начальные функции NumPy. Подпишитесь на группу ВКонтакте, Telegram и YouTube-канал. Там еще больше полезного и интересного для разработчиков.