Добрый день, уважаемые читатели. Темой нашей сегодняшней статьи станет объединение библиотек Keras и Sklearn.
Подпишись на группу Вконтакте и Телеграм-канал. Там еще больше полезного контента для программистов.А на YouTube-канале ты найдешь обучающие видео по программированию. Подписывайся!
Действительно, в поисках лучшей архитектуры сети мы много раз обучаем несколько вариантов модели на определённом наборе данных, а затем сравниваем их обобщающую способность. Сегодня я предлагаю автоматизировать эту процедуру, использовав пользовательский класс и GridSearchCV
.
Для тех, кто не знает, как работает сеточный поиск и конвейеры в sklearn, советую ознакомиться с моей прошлой статьей.
Импорт модулей и загрузка данных
Начнём наш сегодняшний эксперимент с импортирования всех необходимых функций и классов.
In[1]: # Импортирование необходимых функций и классов import numpy as np from sklearn.datasets import load_breast_cancer from sklearn.pipeline import Pipeline from sklearn.model_selection import GridSearchCV from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from tensorflow.keras.wrappers.scikit_learn import KerasClassifier from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Dropout from tensorflow.keras.utils import plot_model from PIL import Image
Не буду здесь объяснять назначение каждого импорта, я буду использовать все функции постепенно.
В качестве набора данных я предпочёл Breast Cancer. Этот датасет можно считать классическим в сфере классификации. Набор содержит 569 наблюдений, каждое из которых содержит 30 признаков.
In[2]: # Загрузка данных dataset = load_breast_cancer() X = np.array(dataset.data) y = np.array(dataset.target) X.shape, y.shape ((569, 30), (569,))
Разделим набор на обучающие и тестовые данные. Для этого используем такой инструмент, как функцию train_test_split
из модуля model_selection
библиотеки sklearn. В моём случае тестовый набор будет составлять 20% от общего.
In[3]: # Разделение данных на тренировочные/тестовые X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
Пользовательский класс для построения модели
Немного забегая вперёд, скажу, что GridSearchCV
использует подстановку гиперпараметров в классы моделей для дальнейшего их обучения. Однако использование класса Sequential()
не очень подходит под эту задачу. Поэтому мы реализуем свой класс, в котором построим модель и приведём её к программному интерфейсу sklearn.
# Класс кастомного классификатора class ModelBuilder(object): # Конструктор класса def __init__(self): pass
Я объявляю класс и также описываю, что его конструктор не принимает никаких аргументов, а также ничего не выполняет.
# Метод, используемый GridSearchCV # для установки параметров def set_params(self, layers=[], optimizer='rmsprop', loss='binary_crossentropy', metric=['accuracy'], epochs=200, batch_size=5): # Установка параметров в качестве полей класса self.__layers = layers self.__optimizer = optimizer self.__loss = loss self.__metric = metric self.__epochs = epochs self.__batch_size = batch_size # Преобразования модели в классификатор sklearn self.model = KerasClassifier(build_fn=self.__build, epochs=epochs, batch_size=batch_size)
Класс GridSearchCV
использует метод set_params(**params)
для подстановки параметров.
Сюда я буду передавать список слоёв сети, тип оптимизатора, меру измерения ошибки, метрику оценивая, кол-во эпох и размер мини-пакета для обучения.
С помощью Keras класса KerasClassifier()
я преобразовываю нашу сеть, реализованную в классе Sequential()
, так, чтобы я мог использовать модель в различных операциях sklearn
. Конструктор класса принимает функцию, которая должна возвращать саму нейронную сеть, кол-во эпох и размер батча.
# Функция для постройки модели def __build(self): model = Sequential(self.__layers) model.compile(optimizer=self.__optimizer, loss=self.__loss, metrics=self.__metric) return model
Также мы переопределяем методы fit()
, predict()
и score()
, т.к. их использует алгоритм сеточного поиска.
# Адаптирование модели def fit(self, X, y): return self.model.fit(x=X, y=y) # Предсказание модели def predict(self, X): return self.model.predict(X) # Вычисление точности предсказания def score(self, X, y): return self.model.score(X, y)
Примечание. Полное описание тела класса содержится в документе, ссылку на который вы сможете найти в конце статьи.
Сеточный поиск
Так выглядит словарь со всеми значениями параметров нашего класса:
In[5]: # Различные наборы слоёв params = { 'clf__layers': [[Dense(256, activation='relu', input_shape=(30, )), Dense(256, activation='relu'), Dropout(0.4), Dense(1, activation='sigmoid')], [Dense(64, activation='relu', input_shape=(30, )), Dropout(0.4), Dense(64, activation='relu'), Dense(1, activation='sigmoid')], [Dense(16, activation='relu', input_shape=(30, )), Dropout(0.4), Dense(16, activation='relu'), Dense(16, activation='relu'), Dropout(0.4), Dense(1, activation='sigmoid')] ], 'clf__optimizer': ['rmsprop'], 'clf__loss': ['binary_crossentropy'], 'clf__metric': [['accuracy']], 'clf__epochs': [25, 50] }
Позже я рассмотрю инструмент, с помощью которого вы сможете визуализировать все три предложенные мною архитектуры.
Т.к. нейронные сети «требуют» масштабирования признаков, я создал конвейер с транформером, который будет проводить операцию стандартизации.
In[6]: # Инициализация трансформера scaler = StandardScaler() # Инициализация классификатора clf = ModelBuilder() # Создание конвеера pipeline = Pipeline([('scaler', scaler), ('clf', clf)])
Далее я создал экземпляр класса, отвечающего за сеточный поиск.
In[7]: # Построение класса, выполняющего сеточный поиск grid_search = GridSearchCV(pipeline, params)
И сразу запустил поиск на тренировочных данных:
In[8]: # Алгоритм обучает множество моделей на тренировочных данных grid_search.fit(X_train, y_train)
Результат
Этот самый класс сохраняет лучшие параметры, а также лучшую модель, основываясь на тренировочных данных.
In[9]: # Лучшие "гиперпараметры", найдены поиском params = grid_search.best_params_ layers = params['clf__layers'] metric = params['clf__metric'] optimizer = params['clf__optimizer'] epochs = params['clf__epochs'] loss = params['clf__loss']
Теперь, основываясь на этих параметрах, я создад экземпляр класса Sequential()
с такой архитектурой и параметра компилирования.
In[10]: # Построение модели, основываясь на лучшие "гиперпараметры" model = Sequential(layers) model.compile(optimizer=optimizer, loss=loss, metrics=metric) # Визуализация архитектуры plot_model(model, to_file='C:\model.png')
С помощью функции plot_model()
(прежде чем использовать её, установите PyDot — pip/conda install pydot
) я сохраню изображение, которое будет содержать отображение архитектуры нашей сети.
С помощью Pillow.Image выводится изображение на экран:
In[11]: # Вывод изображения img = Image.open("model.png") img

Прекрасно, с этим разобрались. В качестве завершения, стоит сравнить точность классификации лучшей модели на тренировочных и обучающих данных.
In[12]: model = grid_search.best_estimator_ train_score = model.score(X_train, y_train) test_score = model.score(X_test, y_test) print('Score on train data is {0}'.format(train_score)) print('Score on test data is {0}'.format(test_score)) Score on train data is 0.995604395866394 Score on test data is 0.9824561476707458
Заключение
Сегодня я продемонстрировал способ автоматизировать поиск нужного кол-ва слоёв в нейронной сети, связав sklearn и Keras. Надеюсь, эта статья была для вас полезна. Желаю вам низких ошибок и надёжных опорных векторов.
Документ с расширением .ipynb, который вы сможете просмотреть и изменить на своё усмотрение, находится по ссылке.
Кроме того, рекомендую прочитать статью Обработка естественного языка с Python — NLP. А также подписывайтесь на группу ВКонтакте, Telegram и YouTube-канал. Там еще больше полезного и интересного для программистов.