Добрый день, уважаемые читатели. Порой ко мне в голову приходят ненормальные мысли по поводу обучения нейросетей. И я решил — почему бы нам с вами не реализовать эти идеи и не оценить результат? :) Скорее всего такая задумка станет целым циклом статей, где мы будем пробовать различные «штуки».
Подпишись на группу Вконтакте и Телеграм-канал. Там еще больше полезного контента для программистов.
А на YouTube-канале ты найдешь обучающие видео по программированию. Подписывайся!
Идея на сегодня
Моя идея на сегодня: вместо последовательной модели реализовать модель с несколькими входами, каждый из которых принимает подмножество начальных признаков (эти подмножество пересекаются). Как считаете, приведёт ли это к улучшению обобщающей способности? Давайте проверим!
Загрузка данных
Для проверки этой идеи нам не стоит перезагружать своё железо большими вычислениями. Мы будем использовать BreastCancer
— этот датасет очень часто использовался в моих статьях, а также он входит в набор датасетов sklearn
.
Импортируем необходимые классы и функции:
In[1]: from tensorflow.python.keras.layers import Dense, Dropout, Input, Concatenate from tensorflow.python.keras.models import Model, Sequential from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split from sklearn.preprocessing import normalize
Также загрузим данные и проведём их нормализацию:
In[2]: data = load_breast_cancer() X = normalize(data.data * 10) y = data.target
Создание и сравнение моделей
Теперь создадим последовательную модель Keras
. Этот процесс уже описывался в этой статье (если вы не знаете, как это делать, то эта статья обязательна к прочтению).
In[3]: model = Sequential() model.add(Dense(64, activation='relu')) model.add(Dense(32, activation='relu')) model.add(Dropout(0.4)) model.add(Dense(32, activation='relu')) model.add(Dense(1, activation='sigmoid')) model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
Также разделим данные на обучающую и тестовую выборку:
In[4]: X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) X_train.shape, y_train.shape Out[4]: ((455, 30), (455,))
Сохраним историю обучения модели на обучающих данных. Её я использовать не буду, однако она полностью в вашем распоряжении :) ).
In[5]: history = model.fit(X_train, y_train, batch_size=128, epochs=100)
С помощью функционального API создадим модель с тремя входами (всё это описывалось также в этой статье).
In[6]: input_layer_1 = Input(shape=(15, )) input_layer_2 = Input(shape=(15, )) input_layer_3 = Input(shape=(15, )) layer_1 = Dense(32, activation='relu')(input_layer_1) layer_2 = Dense(32, activation='relu')(input_layer_2) layer_3 = Dense(32, activation='relu')(input_layer_3) concatenate = Concatenate(axis=1)([layer_1, layer_2, layer_3]) dropout = Dropout(0.4)(concatenate) layer_4 = Dense(32, activation='relu')(dropout) layer_5 = Dense(1, activation='sigmoid')(layer_4) model_ = Model(inputs=[input_layer_1, input_layer_2, input_layer_3], outputs=layer_5) model_.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
Итак, теперь самое интересное: разделим обучающую и тестовую выборки на три набора путём индексации (индексация массивов NumPy
происходит также, как и в Python
).
In[7]: input_1 = X_train[:, :15] input_2 = X_train[:, 5:20] input_3 = X_train[:, 14:-1] test_1 = X_test[:, :15] test_2 = X_test[:, 5:20] test_3 = X_test[:, 14:-1]
Почему именно такие индексы? Обратите внимание, мы указали shape=(15, )
в классе Input()
. А значит именно такой формы должен быть отдельный образец из выборок.
Сохраним историю обучения новой модели:
In[8]: history_ = model_.fit([input_1, input_2, input_3], y_train, batch_size=128, epochs=100)
А теперь сравним обобщающую способность обоих моделей на тестовых данных:
In[9]: model.evaluate(X_test, y_test)[1] 114/114 [==============================] - 0s 1ms/sample - loss: 0.3172 - acc: 0.8596 Out[9]: 0.8596491
In [10]: model_.evaluate([test_1, test_2, test_3], y_test)[1] 114/114 [==============================] - 0s 1ms/sample - loss: 0.4026 - acc: 0.8333 Out[10]: 0.8333333
Таким образом, обучив модели много раз, я пришёл к выводу, что сегодняшняя идея вряд ли приведёт к улучшению модели. Я предполагаю, что это связанно с потерей выделения связи между некоторыми признаками (например, между первым и последним).
Заключение
К сожалению, мы не добились успеха в этот раз. Однако, скорее всего, нам повезёт, и мы найдём «новый рецепт» глубокого обучения. Желаю вам всегда избегать локального минимума!
Документ на GitHub, который вы сможете просмотреть и редактировать на своё усмотрение находится по этой ссылочке.
Также рекомендую прочитать статью Функциональный API библиотеки Keras. А также подписывайтесь на группу ВКонтакте, Telegram и YouTube-канал. Там еще больше полезного и интересного для программистов.