На этом шаге мы рассмотрим работу сети на реальных данных.
Чтобы лучше понять, как нейронная сеть работает на реальных данных, давайте применим MLPClassifier к набору данных Breast Cancer. Мы начнем с параметров по умолчанию:
[In 12]: from sklearn.datasets import load_breast_cancer cancer = load_breast_cancer() print("Максимальные значения характеристик:\n{}".format(cancer.data.max(axis=0))) Максимальные значения характеристик: [2.811e+01 3.928e+01 1.885e+02 2.501e+03 1.634e-01 3.454e-01 4.268e-01 2.012e-01 3.040e-01 9.744e-02 2.873e+00 4.885e+00 2.198e+01 5.422e+02 3.113e-02 1.354e-01 3.960e-01 5.279e-02 7.895e-02 2.984e-02 3.604e+01 4.954e+01 2.512e+02 4.254e+03 2.226e-01 1.058e+00 1.252e+00 2.910e-01 6.638e-01 2.075e-01]
[In 13]: X_train, X_test, y_train, y_test = train_test_split( cancer.data, cancer.target, random_state=0) mlp = MLPClassifier(random_state=42) mlp.fit(X_train, y_train) print("Правильность на обучающем наборе: {:.2f}".format(mlp.score(X_train, y_train))) print("Правильность на тестовом наборе: {:.2f}".format(mlp.score(X_test, y_test))) Правильность на обучающем наборе: 0.94 Правильность на тестовом наборе: 0.92
MLP демонстрирует довольно неплохую правильность, однако не столь хорошую, если сравнивать с другими моделями. Как и в предыдущем примере с SVC, это, вероятно, обусловлено масштабом данных. Нейронные сети также требуют того, чтобы все входные признаки были измерены в одном и том же масштабе, в идеале они должны иметь среднее 0 и дисперсию 1. Мы должны отмасштабировать наши данные так, чтобы они отвечали этим требованиям. Опять же, мы будем делать это вручную, однако позже расскажем, как это делать автоматически с помощью StandardScaler.
[In 14]: # вычисляем среднее для каждого признака обучающего набора mean_on_train = X_train.mean(axis=0) # вычисляем стандартное отклонение для каждого признака обучающего набора std_on_train = X_train.std(axis=0) # вычитаем среднее и затем умножаем на обратную величину стандартного отклонения # mean=0 и std=1 X_train_scaled = (X_train - mean_on_train) / std_on_train # используем ТО ЖЕ САМОЕ преобразование (используем среднее и стандартное отклонение # обучающего набора) для тестового набора X_test_scaled = (X_test - mean_on_train) / std_on_train mlp = MLPClassifier(random_state=0) mlp.fit(X_train_scaled, y_train) print("Правильность на обучающем наборе: {:.3f}".format( mlp.score(X_train_scaled, y_train))) print("Правильность на тестовом наборе: {:.3f}".format( mlp.score(X_test_scaled, y_test))) Правильность на обучающем наборе: 0.991 Правильность на тестовом наборе: 0.965
После масштабирования результаты стали намного лучше и теперь уже вполне могут конкурировать с результатами остальных моделей. Впрочем, мы получили предупреждение о том, что достигнуто максимальное число итераций. Оно является неотъемлемой частью алгоритма adam и сообщает нам о том, что мы должны увеличить число итераций:
[In 15]: mlp = MLPClassifier(max_iter=1000, random_state=0) mlp.fit(X_train_scaled, y_train) print("Правильность на обучающем наборе: {:.3f}".format( mlp.score(X_train_scaled, y_train))) print("Правильность на тестовом наборе: {:.3f}".format( mlp.score(X_test_scaled, y_test))) Правильность на обучающем наборе: 1.000 Правильность на тестовом наборе: 0.972
Увеличение количества итераций незначительно повысило правильность на тестовом наборе. Тем не менее модель имеет достаточно высокое значение правильности. Поскольку существует определенный разрыв между правильностью на обучающем наборе и правильностью на тестовом наборе, мы можем попытаться уменьшить сложность модели, чтобы улучшить обобщающую способность. В данном случае мы увеличим параметр alpha (довольно сильно с 0.0001 до 1), чтобы применить к весам более строгую регуляризацию:
[In 16]: mlp = MLPClassifier(max_iter=1000, alpha=1, random_state=0) mlp.fit(X_train_scaled, y_train) print("Правильность на обучающем наборе: {:.3f}".format( mlp.score(X_train_scaled, y_train))) print("Правильность на тестовом наборе: {:.3f}".format( mlp.score(X_test_scaled, y_test))) Правильность на обучающем наборе: 0.988 Правильность на тестовом наборе: 0.972
Это дает правильность, сопоставимую с правильностью лучших моделей.
Несмотря на то что анализ нейронной сети возможен, он, как правило, гораздо сложнее анализа линейной модели или модели на основе дерева. Один из способов анализа нейронной сети заключается в том, чтобы исследовать веса модели. Образец такого анализа вы можете увидеть в галерее примеров scikit-learn. Применительно к набору данных Breast Cancer такой анализ может быть немного сложен. Следующий график (рисунок 1) показывает весовые коэффициенты, которые были вычислены при подключении входного слоя к первому скрытому слою. Строки в этом графике соответствуют 30 входным признакам, а столбцы - 100 скрытым элементам. Светлые цвета соответствуют высоким положительным значениям, в то время как темные цвета соответствуют отрицательным значениям:
plt.figure(figsize=(20, 5)) plt.imshow(mlp.coefs_[0], interpolation='none', cmap='viridis') plt.yticks(range(30), cancer.feature_names) plt.xlabel("Столбцы матрицы весов") plt.ylabel("Входная характеристика") plt.colorbar()
Рис.1. Теплокарта для весов первого слоя нейронной сети, обученной на наборе данных Breast Cancer (изображение кликабельно)
Один из возможных выводов, который мы можем сделать, заключается в том, что признаки с небольшими весами скрытых элементов "менее важны" в модели. Мы можем увидеть, что "mean smoothness" и "mean compactness" наряду с признаками, расположенными между "smothness error" и "fractal dimension error", имеют относительно низкие веса по сравнению с другими признаками. Это может означать, что эти признаки являются менее важными или, возможно, мы не преобразовали их таким способом, чтобы их могла использовать нейронная сеть.
Кроме того, мы можем визуализировать веса, соединяющие скрытый слой с выходным слоем, но их еще труднее интерпретировать.
Несмотря на то что для наболее распространенных архитектур нейронных сетей MLPClassifier и MLPRegressor предлагают легкий в использовании интерфейс, они представляют лишь небольшой набор возможных средств, позволяющих строить нейронные сети. Если вас интересует работа с более гибкими или более масштабными моделями, мы рекомендуем вам не ограничиваться возможностями библиотеки scikit-learn и обратиться к фантастическим по своими возможностями библиотекам глубокого обучения. Для пользователей Python наиболее устоявшимися являются keras, lasagna и tenzor-flow. lasagna построена на основе библиотеки theano, тогда как keras может использовать либо tensor-flow, либо theano. Эти библиотеки предлагают гораздо более гибкий интерфейс для построения нейронных сетей и обновляются в соответствии с последними достижениями в области глубокого обучения. Кроме того, все популярные библиотеки глубокого обучения позволяют использовать высокопроизводительные графические процессоры (GPU), которые в scikit-learn не поддерживаются. Использование графических процессоров позволяет ускорить вычисления от 10 до 100 раз, и они имеют важное значение для применения методов глубого обучения для крупномасштабных наборов данных.
Архив блокнота со всеми вычислениями, выполненными на 69-71 шагах, можно взять здесь.
На следующем шаге мы рассмотрим преимущества, недостатки и параметры.