Автор оригинала: Adam McQuistan.
Это последняя статья об использовании машинного обучения в Python для прогнозирования средней температуры на основе метеорологических данных, полученных из Weather Underground , как описано в части первой этой серии.
Темой этой заключительной статьи будет построение регрессора нейронной сети с использованием библиотеки Google Open Source TensorFlow . Общее введение в TensorFlow, а также обсуждение методов установки см. в превосходном посте Михаила Павлоски TensorFlow Neural Network Tutorial .
Темы, которые я буду освещать в этой статье, включают:
- Понимание Теории Искусственных Нейронных Сетей
- API высокоуровневой оценки TensorFlow
- Построение регрессора DNN для прогнозирования погоды
Понимание Теории Искусственных Нейронных Сетей
В предыдущей статье ( часть 2 ) я описал процесс построения линейной регрессионной модели, почтенного метода машинного обучения, лежащего в основе многих других, для прогнозирования среднесуточной температуры в Линкольне, штат Небраска. Линейные регрессионные модели чрезвычайно мощны и использовались для численных, а также категориальных прогнозов задолго до того, как был введен термин “машинное обучение”. Однако этот метод имеет некоторые критические замечания, в основном связанные с его ребристым предположением о линейной зависимости между зависимой переменной и независимой переменной(переменными).
В области науки о данных и машинного обучения существует бесчисленное множество других алгоритмов, которые преодолевают это предположение о линейности. Одним из наиболее популярных направлений в последние годы стало применение нейронных сетей к широкому спектру задач машинного обучения. Нейронные сети обладают мощным способом использования методов обучения, основанных как на линейных, так и на нелинейных операциях.
Нейронные сети вдохновляются биологическими нейронами в мозге, которые работают в сложной сети взаимодействий для передачи, сбора и изучения информации, основанной на истории уже собранной информации. Вычислительные нейронные сети, которые нас интересуют, подобны нейронам мозга в том, что они представляют собой набор нейронов (узлов), которые принимают входные сигналы (числовые величины), обрабатывают входные данные и передают обработанные сигналы другим нижестоящим агентам в сети. Обработка сигналов в виде числовых величин, проходящих через нейронную сеть, является очень мощной функцией, которая не ограничивается линейными отношениями.
В этой серии я сосредоточился на конкретном типе машинного обучения , называемом контролируемое обучение , что просто означает, что обучаемые модели строятся с использованием данных, которые имеют известные целевые результаты, которые модель пытается научиться предсказывать. Кроме того, тип производимых предсказаний-это числовые реальные значения, что означает, что мы имеем дело с алгоритмами регрессора предсказания.
Графически нейронная сеть, подобная той, что описана в этой статье, показана на рисунке ниже.
Нейронная сеть, изображенная выше, содержит входной слой в крайнем левом углу, представляющий два объекта, x1 и x2, которые питают нейронную сеть. Эти два признака поступают в нейронную сеть, которая обрабатывается и передается через два слоя нейронов, которые называются скрытыми слоями. Это изображение показывает два скрытых слоя, каждый из которых содержит три нейрона (узла). Затем сигнал выходит из нейронной сети и агрегируется на выходном слое как единое числовое прогнозируемое значение.
Позвольте мне воспользоваться моментом, чтобы объяснить значение стрелок, обозначающих данные, обрабатываемые от узла к узлу через слои. Каждая стрелка представляет собой математическое преобразование значения, начиная с основания стрелки, которое затем умножается на вес, специфичный для этого пути. Таким образом, каждому узлу в слое будет передано значение. Затем суммируются все значения, сходящиеся в узле. Именно эта совокупность умножения на веса и суммирования продуктов определяет линейные операции нейронной сети, о которых я упоминал ранее.
После суммирования в каждом узле к сумме применяется специальная нелинейная функция, которая изображена на рисунке выше как Fn(…) . Эта специальная функция, которая вводит нелинейные характеристики в нейронную сеть, называется активационной функцией . Именно эта нелинейная характеристика, вызванная активационными функциями, дает многослойным нейронным сетям их мощь. Если бы не нелинейность, добавленная к процессу, то все слои эффективно просто алгебраически объединялись бы в одну постоянную операцию, состоящую в умножении входных данных на некоторое плоское значение коэффициента (то есть линейную модель).
Ладно, все это прекрасно и денди, но я надеюсь, что в глубине души ты задаешься вопросом… Хорошо, Адам, но как это переводится в алгоритм обучения? Ну, самый прямой ответ на этот вопрос состоит в том, чтобы оценить сделанные прогнозы, выход модели “y”, до фактических ожидаемых значений (целевых показателей) и сделать ряд корректировок весов таким образом, чтобы повысить общую точность прогнозирования.
В мире алгоритмов регрессорного машинного обучения точность оценивается с помощью функции стоимости (она же “потеря” или “цель”), а именно суммы квадратов ошибок (SSE). Обратите внимание, что я обобщил это утверждение на весь континуум машинного обучения, а не только на нейронные сети. В предыдущей статье Обычный алгоритм наименьших квадратов достиг именно этого, он нашел комбинации коэффициентов, которые минимизировали сумму квадратов ошибок (то есть наименьших квадратов).
Наш регрессор нейронной сети будет делать то же самое. Он будет перебирать обучающие данные, подаваемые в значениях признаков, вычислять функцию затрат (используя SSE) и вносить коррективы в веса таким образом, чтобы минимизировать функцию затрат. Этот процесс итеративного проталкивания функций через алгоритм и оценки того, как корректировать веса на основе функции затрат, по сути, является тем, что известно как оптимизация модели.
Алгоритмы оптимизации моделей очень важны при построении надежных нейронных сетей. Поскольку примеры подаются через сетевую архитектуру (то есть ширину и глубину), а затем оцениваются по функции затрат, веса корректируются. Говорят, что модели “обучаются”, когда функция оптимизатора определяет, что корректировка веса была произведена таким образом, чтобы не улучшить (снизить) функцию затрат, которая регистрируется оптимизатором, чтобы он снова не корректировал вес в этом направлении.
API высокоуровневой оценки TensorFlow
Библиотека Google TensorFlow состоит из нескольких API, самым популярным из которых является Core API, который дает пользователю низкоуровневый набор инструментов для определения и обучения практически любого алгоритма машинного обучения с использованием символьных операций. Это называется ядром тензорного потока. В то время как TensorFlow Core-это удивительный API с обширными возможностями применения, я сосредоточусь на более новом, более высоком уровне API, разработанном командой TensorFlow, который в совокупности называется API Estimator.
Команда TensorFlow разработала API Estimator, чтобы сделать библиотеку более доступной для повседневного разработчика. Этот высокоуровневый API предоставляет общий интерфейс для обучения(...)
моделей, оценки(...)
моделей и прогнозирования(...)
результатов неизвестных случаев, подобных (и под влиянием) популярной библиотеке Sci-Kit Learn, которая реализуется путем реализации общего интерфейса для различных алгоритмов. Кроме того, в API высокого уровня встроена масса лучших практик машинного обучения, абстракций и возможностей масштабирования.
Все это благо машинного обучения приносит набор инструментов, реализованных в базовом классе оценщиков, а также несколько предварительно законсервированных типов моделей, которые снижают барьер входа для использования TensorFlow, чтобы его можно было применить к множеству повседневных проблем (или возможностей). Абстрагируясь от многих повседневных и ручных аспектов таких вещей, как написание обучающих циклов или работа с сессиями, разработчик может сосредоточиться на более важных вещах, таких как быстрая попытка нескольких моделей и архитектур моделей найти ту, которая лучше всего соответствует их потребностям.
В этой статье я опишу, как использовать один из очень мощных глубоких нейронных сетевых оценщиков, DNNRegressor
.
Построение регрессора DNN для прогнозирования погоды
Позвольте мне начать с импорта нескольких различных библиотек, которые я буду использовать для построения модели:
import pandas as pd import numpy as np import tensorflow as tf from sklearn.metrics import explained_variance_score, \ mean_absolute_error, \ median_absolute_error from sklearn.model_selection import train_test_split
Теперь давайте возьмем в руки эти данные и еще раз взглянем на них, чтобы ознакомиться с ними. Я поместил весь код и данные в свое репо GitHub здесь , чтобы читатели могли следить за ним.
# read in the csv data into a pandas data frame and set the date as the index df = pd.read_csv('end-part2_df.csv').set_index('date') # execute the describe() function and transpose the output so that it doesn't overflow the width of the screen df.describe().T
5.0 | 15.0 | 22.00 | 32.00 | -17.0 | 10.971591 | 13.129388 | 997.0 | средняя температура |
11.0 | 22.0 | 29.00 | 38.00 | -12.0 | 11.577275 | 19.509529 | 997.0 | maxtemp |
-2.0 | 7.0 | 16.00 | 26.00 | -27.0 | 10.957267 | 6.438315 | 997.0 | минтемп |
5.0 | 15.0 | 22.00 | 32.00 | -17.0 | 10.984613 | 13.109328 | 997.0 | среднее значение temp_1 |
5.0 | 14.0 | 22.00 | 32.00 | -17.0 | 11.001106 | 13.088265 | 997.0 | среднее значение temp_2 |
5.0 | 14.0 | 22.00 | 32.00 | -17.0 | 11.017312 | 13.066199 | 997.0 | среднее значение temp_3 |
-2.0 | 7.0 | 16.00 | 24.00 | -22.0 | 10.596265 | 6.440321 | 997.0 | meandewptm_1 |
-2.0 | 7.0 | 16.00 | 24.00 | -22.0 | 10.606550 | 6.420261 | 997.0 | meandewptm_2 |
-2.0 | 7.0 | 16.00 | 24.00 | -22.0 | 10.619083 | 6.393180 | 997.0 | meandewptm_3 |
1011.0 | 1016.0 | 1021.00 | 1040.00 | 989.0 | 7.582453 | 1016.139418 | 997.0 | среднее давление m_1 |
1011.0 | 1016.0 | 1021.00 | 1040.00 | 989.0 | 7.584185 | 1016.142427 | 997.0 | среднее давление m_2 |
1011.0 | 1016.0 | 1021.00 | 1040.00 | 989.0 | 7.586988 | 1016.151454 | 997.0 | среднее давление m_3 |
83.0 | 90.0 | 93.00 | 100.00 | 47.0 | 9.280627 | 88.107322 | 997.0 | max humidity_1 |
83.0 | 90.0 | 93.00 | 100.00 | 47.0 | 9.280152 | 88.106319 | 997.0 | max humidity_2 |
83.0 | 90.0 | 93.00 | 100.00 | 47.0 | 9.276775 | 88.093280 | 997.0 | max humidity_3 |
35.0 | 45.0 | 56.00 | 92.00 | 9.0 | 16.108517 | 46.025075 | 997.0 | min humidity_1 |
35.0 | 45.0 | 56.00 | 92.00 | 9.0 | 16.105530 | 46.021063 | 997.0 | min humidity_2 |
35.0 | 45.0 | 56.00 | 92.00 | 9.0 | 16.047081 | 45.984955 | 997.0 | min humidity_3 |
11.0 | 22.0 | 29.00 | 38.00 | -12.0 | 11.588542 | 19.489468 | 997.0 | макс. темп_1 |
11.0 | 22.0 | 29.00 | 38.00 | -12.0 | 11.603318 | 19.471414 | 997.0 | макс. темп_2 |
11.0 | 22.0 | 29.00 | 38.00 | -12.0 | 11.616412 | 19.455366 | 997.0 | макс. темп_3 |
-2.0 | 7.0 | 16.00 | 26.00 | -27.0 | 10.974433 | 6.417252 | 997.0 | min temp_1 |
-2.0 | 7.0 | 16.00 | 26.00 | -27.0 | 10.988954 | 6.394183 | 997.0 | min temp_2 |
-2.0 | 7.0 | 16.00 | 26.00 | -27.0 | 11.003451 | 6.367101 | 997.0 | min temp_3 |
1.0 | 11.0 | 18.00 | 26.00 | -18.0 | 10.160778 | 9.378134 | 997.0 | maxdepth_1 |
1.0 | 11.0 | 18.00 | 26.00 | -18.0 | 10.171790 | 9.359077 | 997.0 | maxdepth_2 |
1.0 | 11.0 | 18.00 | 26.00 | -18.0 | 10.180521 | 9.336008 | 997.0 | maxdepth_3 |
-6.0 | 4.0 | 13.00 | 22.00 | -28.0 | 11.225411 | 3.251755 | 997.0 | mindepth_1 |
-6.0 | 4.0 | 13.00 | 22.00 | -28.0 | 11.235718 | 3.229689 | 997.0 | mindepth_2 |
-6.0 | 4.0 | 13.00 | 22.00 | -28.0 | 11.251536 | 3.198596 | 997.0 | mindewptm_3 |
1015.0 | 1019.0 | 1024.00 | 1055.00 | 993.0 | 7.755590 | 1019.913741 | 997.0 | максимальное давление_1 |
1015.0 | 1019.0 | 1024.00 | 1055.00 | 993.0 | 7.757705 | 1019.917753 | 997.0 | максимальное давление_2 |
1015.0 | 1019.0 | 1024.00 | 1055.00 | 993.0 | 7.757805 | 1019.927783 | 997.0 | максимальное давление_3 |
1008.0 | 1012.0 | 1017.00 | 1035.00 | 956.0 | 7.885743 | 1012.317954 | 997.0 | минимальное давление_1 |
1008.0 | 1012.0 | 1017.00 | 1035.00 | 956.0 | 7.886681 | 1012.319960 | 997.0 | минимальное давление_2 |
1008.0 | 1012.0 | 1017.00 | 1035.00 | 956.0 | 7.889511 | 1012.326981 | 997.0 | минимальное давление_3 |
0.0 | 0.0 | 0.25 | 95.76 | 0.0 | 8.428058 | 2.593180 | 997.0 | precip_1 |
0.0 | 0.0 | 0.25 | 95.76 | 0.0 | 8.428058 | 2.593180 | 997.0 | precip_2 |
0.0 | 0.0 | 0.25 | 95.76 | 0.0 | 8.410223 | 2.573049 | 997.0 | precip_3 |
# execute the info() function df.info()
Index: 997 entries, 2015-01-04 to 2017-09-27 Data columns (total 39 columns): meantempm 997 non-null int64 maxtempm 997 non-null int64 mintempm 997 non-null int64 meantempm_1 997 non-null float64 meantempm_2 997 non-null float64 meantempm_3 997 non-null float64 meandewptm_1 997 non-null float64 meandewptm_2 997 non-null float64 meandewptm_3 997 non-null float64 meanpressurem_1 997 non-null float64 meanpressurem_2 997 non-null float64 meanpressurem_3 997 non-null float64 maxhumidity_1 997 non-null float64 maxhumidity_2 997 non-null float64 maxhumidity_3 997 non-null float64 minhumidity_1 997 non-null float64 minhumidity_2 997 non-null float64 minhumidity_3 997 non-null float64 maxtempm_1 997 non-null float64 maxtempm_2 997 non-null float64 maxtempm_3 997 non-null float64 mintempm_1 997 non-null float64 mintempm_2 997 non-null float64 mintempm_3 997 non-null float64 maxdewptm_1 997 non-null float64 maxdewptm_2 997 non-null float64 maxdewptm_3 997 non-null float64 mindewptm_1 997 non-null float64 mindewptm_2 997 non-null float64 mindewptm_3 997 non-null float64 maxpressurem_1 997 non-null float64 maxpressurem_2 997 non-null float64 maxpressurem_3 997 non-null float64 minpressurem_1 997 non-null float64 minpressurem_2 997 non-null float64 minpressurem_3 997 non-null float64 precipm_1 997 non-null float64 precipm_2 997 non-null float64 precipm_3 997 non-null float64 dtypes: float64(36), int64(3) memory usage: 311.6+ KB
Обратите внимание, что у нас есть чуть менее 1000 записей метеорологических данных и что все эти характеристики носят числовой характер. Кроме того, из-за нашей напряженной работы в первой статье все записи являются полными в том смысле, что в них не пропущены (нет ненулевых значений) какие-либо значения.
Теперь я удалю столбцы “mintempm” и “maxtempm”, поскольку они не имеют никакого значения для того, чтобы помочь нам предсказать средние средние температуры. Мы пытаемся предсказать будущее, поэтому у нас, очевидно, нет данных о будущем. Я также отделю функции ( X
) от целей ( y
).
# First drop the maxtempm and mintempm from the dataframe df = df.drop(['mintempm', 'maxtempm'], axis=1) # X will be a pandas dataframe of all columns except meantempm X = df[[col for col in df.columns if col != 'meantempm']] # y will be a pandas series of the meantempm y = df['meantempm']
Как и во всех контролируемых приложениях машинного обучения, я буду делить свой набор данных на обучающие и тестовые наборы. Однако, чтобы лучше объяснить итеративный процесс обучения этой нейронной сети, я буду использовать дополнительный набор данных, который буду называть “набором проверки”. Для обучающего набора я буду использовать 80 процентов данных, а для набора тестирования и проверки они будут составлять по 10% от оставшихся данных.
Чтобы разделить эти данные, я снова буду использовать SciKit Learn train_test_split(...)
.
# split data into training set and a temporary set using sklearn.model_selection.traing_test_split X_train, X_tmp, y_train, y_tmp = train_test_split(X, y, test_size=0.2, random_state=23)
# take the remaining 20% of data in X_tmp, y_tmp and split them evenly X_test, X_val, y_test, y_val = train_test_split(X_tmp, y_tmp, test_size=0.5, random_state=23) X_train.shape, X_test.shape, X_val.shape print("Training instances {}, Training features {}".format(X_train.shape[0], X_train.shape[1])) print("Validation instances {}, Validation features {}".format(X_val.shape[0], X_val.shape[1])) print("Testing instances {}, Testing features {}".format(X_test.shape[0], X_test.shape[1]))
Training instances 797, Training features 36 Validation instances 100, Validation features 36 Testing instances 100, Testing features 36
Первым шагом при построении нейросетевой модели является создание экземпляра tf.estimator.DNNRegressor(...)
класс. Конструктор класса имеет несколько параметров, но я сосредоточусь на следующем:
feature_columns
: Структура типа списка, содержащая определение имени и типов данных для объектов, вводимых в модельhidden_units
: Список-подобная структура, содержащая определение ширины и глубины числа нейронной сетиoptimizer
: Экземпляр подклассаtf.Optimizer
, который оптимизирует вес модели во время обучения; по умолчанию используется оптимизатор AdaGrad.activation_fn
: Функция активации, используемая для введения нелинейности в сеть на каждом уровне; по умолчанию используется .model_dir
: Каталог, который будет создан, который будет содержать метаданные и другие сохраненные контрольные точки для модели
Я начну с определения списка числовых столбцов объектов. Для этого я использую функцию tf.feature_column.numeric_column ()
, которая возвращает экземпляр Feature Column
для числовых объектов с непрерывным значением.
feature_cols = [tf.feature_column.numeric_column(col) for col in X.columns]
С помощью определенных столбцов функций я теперь могу создать экземпляр класса DNNRegressor
и сохранить его в переменной regressor. Я уточняю, что мне нужна нейронная сеть, имеющая два слоя глубиной, где оба слоя имеют ширину 50 узлов. Я также указываю, что хочу, чтобы данные моей модели хранились в каталоге с именем tf_wx_model .
regressor = tf.estimator.DNNRegressor(feature_columns=feature_cols, hidden_units=[50, 50], model_dir='tf_wx_model')
INFO:tensorflow:Using default config. INFO:tensorflow:Using config: {'_tf_random_seed': 1, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_model_dir': 'tf_wx_model', '_log_step_count_steps': 100, '_keep_checkpoint_every_n_hours': 10000, '_save_summary_steps': 100, '_keep_checkpoint_max': 5, '_session_config': None}
Следующее, что я хочу сделать, – это определить повторно используемую функцию, которая обычно называется “функцией ввода”, которую я буду называть wx_input_fn(...)
. Эта функция будет использоваться для подачи данных в мою нейронную сеть на этапах обучения и тестирования. Существует много различных способов построения входных функций, но я буду описывать, как определить и использовать одну из них на основе tf.estimator.inputs.pandas_input_fn(...)
поскольку мои данные находятся в структурах данных pandas.
def wx_input_fn(X, y=None, num_epochs=None, shuffle=True, batch_size=400): return tf.estimator.inputs.pandas_input_fn(x=X, y=y, num_epochs=num_epochs, shuffle=shuffle, batch_size=batch_size)
Обратите внимание, что эта функция wx_input_fn(...)
принимает один обязательный и четыре необязательных параметра, которые затем передаются входной функции TensorFlow специально для данных pandas, которые возвращаются. Это очень мощная функция API TensorFlow (а также Python и других языков, которые рассматривают функции как граждан первого класса).
Параметры функции определяются следующим образом:
X
: Входные функции, которые будут подаваться в один из трех методов интерфейсаDNNRegressor
(train
,evaluate
иpredict
)y
: Целевые значенияX
, которые являются необязательными и не будут предоставлены вызовуpredict
num_epochs
: Необязательный параметр. Эпоха возникает, когда алгоритм выполняется по всему набору данных один раз.shuffle
: Необязательный параметр, указывающий, следует ли случайным образом выбирать пакет (подмножество) набора данных при каждом выполнении алгоритмаbatch_size
: Количество выборок, включаемых при каждом выполнении алгоритма.
Теперь, когда наша входная функция определена, мы можем обучать нашу нейронную сеть на нашем обучающем наборе данных. Для читателей, знакомых с высокоуровневым API TensorFlow, вы, вероятно, заметите, что я немного нетрадиционно отношусь к тому, как я тренирую свою модель. То есть, по крайней мере, с точки зрения текущих учебных пособий на веб-сайте TensorFlow и других учебных пособий в Интернете.
Обычно вы увидите что-то вроде следующего, когда тренируете одну из этих предварительно законсервированных моделей API высокого уровня.
regressor.train(input_fn=input_fn(training_data, num_epochs=None, shuffle=True), steps=some_large_number) ..... lots of log info ....
Затем автор сразу же приступит к демонстрации функции evaluate(...)
и едва ли намекнет на описание того, что она делает или почему существует эта строка кода.
regressor.evaluate(input_fn=input_fn(eval_data, num_epochs=1, shuffle=False), steps=1) ..... less log info ....
И после этого они сразу же перейдут к выполнению функции predict (...)
, предполагая, что с обученной моделью все идеально.
predictions = regressor.predict(input_fn=input_fn(pred_data, num_epochs=1, shuffle=False), steps=1)
Для новичка ML, читающего этот тип учебника, я съеживаюсь. В этих трех строках кода содержится гораздо больше мыслей, которые требуют большего внимания. Это, я чувствую, единственный недостаток наличия API высокого уровня – становится очень легко собрать модель вместе, не понимая ключевых моментов. Я надеюсь дать разумное объяснение того, как обучить и оценить эту нейронную сеть таким образом, чтобы свести к минимуму риск резкого несоответствия или переоснащения этой модели обучающим данным.
Итак, без дальнейших задержек позвольте мне определить простой цикл обучения, чтобы обучить модель на обучающих данных и периодически оценивать ее на оценочных данных.
evaluations = [] STEPS = 400 for i in range(100): regressor.train(input_fn=wx_input_fn(X_train, y=y_train), steps=STEPS) evaluations.append(regressor.evaluate(input_fn=wx_input_fn(X_val, y_val, num_epochs=1, shuffle=False)))
INFO:tensorflow:Create CheckpointSaverHook. INFO:tensorflow:Saving checkpoints for 1 into tf_wx_model/model.ckpt. INFO:tensorflow:step = 1, loss = 1.11335e+07 INFO:tensorflow:global_step/sec: 75.7886 INFO:tensorflow:step = 101, loss = 36981.3 (1.321 sec) INFO:tensorflow:global_step/sec: 85.0322 ... A WHOLE LOT OF LOG OUTPUT ... INFO:tensorflow:step = 39901, loss = 5205.02 (1.233 sec) INFO:tensorflow:Saving checkpoints for 40000 into tf_wx_model/model.ckpt. INFO:tensorflow:Loss for final step: 4557.79. INFO:tensorflow:Starting evaluation at 2017-12-05-13:48:43 INFO:tensorflow:Restoring parameters from tf_wx_model/model.ckpt-40000 INFO:tensorflow:Evaluation [1/1] INFO:tensorflow:Finished evaluation at 2017-12-05-13:48:43 INFO:tensorflow:Saving dict for global step 40000: average_loss = 10.2416, global_step = 40000, loss = 1024.16 INFO:tensorflow:Starting evaluation at 2017-12-05-13:48:43 INFO:tensorflow:Restoring parameters from tf_wx_model/model.ckpt-40000 INFO:tensorflow:Finished evaluation at 2017-12-05-13:48:43 INFO:tensorflow:Saving dict for global step 40000: average_loss = 10.2416, global_step = 40000, loss = 1024.16
Описанный выше цикл повторяется 100 раз. В теле цикла я вызываю метод train(...)
объекта-регрессора, передавая ему мой многоразовый wx_input_fn (...)
, который, в свою очередь, передает мой набор обучающих функций и целей. Я намеренно оставил параметры по умолчанию num_epochs
равными None
, что в основном говорит: “Мне все равно, сколько раз вы проходите через обучающий набор, просто продолжайте тренировать алгоритм против каждого значения по умолчанию batch_size
400″ (примерно половина размера обучающего набора). Я также оставил параметр shuffle
равным его значению по умолчанию True
, чтобы во время обучения данные выбирались случайным образом, чтобы избежать каких-либо последовательных связей в данных. Последний параметр метода train (...)
– это steps
, который я установил на 400, что означает, что обучающий набор будет дозироваться 400 раз за цикл.
Это дает мне хорошее время, чтобы объяснить более конкретным числовым способом, что такое эпоха. Вспомним из приведенных выше примеров, что эпоха наступает, когда все записи обучающего набора пропускаются через нейронную сеть для обучения ровно один раз. Итак, если в нашем обучающем наборе около 800 (точнее, 797) записей и каждая партия выбирает 400, то для каждых двух партий мы завершили одну эпоху. Таким образом, если мы переберем обучающий набор для 100 итераций по 400 шагов каждая с размером пакета 400 (одна половина эпохи на пакет), то получим:
(100 x 400 / 2) = 20,000 epochs
Теперь вы можете задаться вопросом, почему я выполнил и evaluate(...)
метод для каждой итерации цикла и захватил его вывод в список. Сначала позвольте мне объяснить, что происходит каждый раз, когда срабатывает метод train (...)
. Он выбирает случайную партию обучающих записей и проталкивает их через сеть до тех пор, пока не будет сделано предсказание, и для каждой записи вычисляется функция потерь. Затем на основе вычисленных потерь веса корректируются в соответствии с логикой оптимизатора, который довольно хорошо справляется с корректировкой в направлении, уменьшающем общие потери для следующей итерации. Эти значения потерь, как правило, пока скорость обучения достаточно мала, со временем уменьшаются с каждой итерацией или шагом.
Однако после некоторого количества этих итераций обучения на вес начинают влиять не только общие тенденции в данных, но и неинформативный шум, наследуемый практически во всех реальных данных. В этот момент сеть находится под чрезмерным влиянием особенностей обучающих данных и становится неспособной обобщать прогнозы относительно общей совокупности данных (то есть данных, которые она еще не видела).
Это связано с проблемой, о которой я упоминал ранее, когда многие другие учебные пособия по высокоуровневому API TensorFlow не оправдали ожиданий. Очень важно периодически прерываться во время обучения и оценивать, как модель обобщается на оценочный или валидационный набор данных. Давайте на минутку посмотрим, что возвращает функция evaluate (...)
, посмотрев на результат оценки первой итерации цикла.
evaluations[0]
{'average_loss': 31.116383, 'global_step': 400, 'loss': 3111.6382}
Как вы можете видеть, он выводит среднюю потерю (среднеквадратичную ошибку) и общую потерю (сумму квадратов ошибок) для шага обучения, который для этого является 400-м шагом. То, что вы обычно видите в хорошо обученной сети, – это тенденция, когда потери как в обучении, так и в оценке более или менее постоянно снижаются параллельно. Однако в переоснащенной модели в какой-то момент времени, фактически в тот момент, когда начинает происходить переоснащение, обучающий набор валидации перестанет видеть сокращение выходных данных своего метода evaluate (...)
. Именно здесь вы хотите прекратить дальнейшее обучение модели, предпочтительно прямо перед тем, как произойдет это изменение.
Теперь, когда у нас есть набор оценок для каждой из итераций, давайте построим их как функцию шагов обучения, чтобы убедиться, что мы не переобучили нашу модель. Для этого я буду использовать простую точечную диаграмму из модуля matplotlib pyplot
.
import matplotlib.pyplot as plt %matplotlib inline # manually set the parameters of the figure to and appropriate size plt.rcParams['figure.figsize'] = [14, 10] loss_values = [ev['loss'] for ev in evaluations] training_steps = [ev['global_step'] for ev in evaluations] plt.scatter(x=training_steps, y=loss_values) plt.xlabel('Training steps (Epochs = steps / 2)') plt.ylabel('Loss (SSE)') plt.show()
Круто! Из приведенной выше диаграммы видно, что после всех этих итераций я не переоснастил модель, потому что потери оценки никогда не демонстрируют значительного изменения направления в сторону увеличения значения. Теперь я могу спокойно перейти к составлению прогнозов на основе моего оставшегося тестового набора данных и оценить, как модель предсказывает средние погодные температуры.
Как и два других метода регрессора, которые я продемонстрировал , метод predict(...)
требует input_fn
, который я передам с помощью многоразового использования wx_input_fn (...)
, передавая ему тестовый набор данных, указывая, что num_epochs
должен быть одним и shuffle
должен быть ложным, чтобы он последовательно подавал все данные для тестирования.
Затем я делаю некоторое форматирование итерабельных диктов, возвращаемых из метода predict (...)
, так что у меня есть массив numpy предсказаний. Затем я использую массив предсказаний с методами sklearn explained_variance_score(...)
, mean_absolute_error (...)
и median_absolute_error(...)
для измерения того , насколько хорошо предсказания выполнялись по отношению к известным целям y_test
. Это говорит разработчику, каковы прогностические возможности модели.
pred = regressor.predict(input_fn=wx_input_fn(X_test, num_epochs=1, shuffle=False)) predictions = np.array([p['predictions'][0] for p in pred]) print("The Explained Variance: %.2f" % explained_variance_score( y_test, predictions)) print("The Mean Absolute Error: %.2f degrees Celcius" % mean_absolute_error( y_test, predictions)) print("The Median Absolute Error: %.2f degrees Celcius" % median_absolute_error( y_test, predictions))
INFO:tensorflow:Restoring parameters from tf_wx_model/model.ckpt-40000 The Explained Variance: 0.88 The Mean Absolute Error: 3.11 degrees Celcius The Median Absolute Error: 2.51 degrees Celcius
Я использовал те же показатели, что и в предыдущей статье, посвященной методу линейной регрессии, так что мы можем не только оценить эту модель, но и сравнить их. Как вы можете видеть, эти две модели работали совершенно одинаково, а более простая модель линейной регрессии была немного лучше. Однако проницательный практик наверняка провел бы несколько экспериментов, варьируя гиперпараметры (скорость обучения, ширину и глубину) этой нейронной сети, чтобы немного настроить ее, но в целом это, вероятно, довольно близко к оптимальной модели.
Это поднимает вопрос, заслуживающий упоминания, что редко бывает и определенно не рекомендуется просто полагаться на одну модель или самую последнюю горячую тему в сообществе машинного обучения. Нет двух одинаковых наборов данных, и ни одна модель не является королем. Единственный способ определить лучшую модель-это действительно опробовать их. Затем, как только вы определили лучшую модель, есть и другие компромиссы, которые необходимо учитывать, такие как интерпретируемость.
Ресурсы
Хотите изучить инструменты, методы машинного обучения и анализа данных, используемые в этом учебнике? Вот несколько замечательных ресурсов, которые помогут вам начать работу:
Вывод
В этой статье показано, как использовать высокоуровневый API TensorFlow для предварительно законсервированного подкласса оценщика DNNRegressor
. Попутно я описал, в общем смысле, теорию нейронных сетей, как они обучаются, и важность осознания опасности переоснащения модели в процессе.
Чтобы продемонстрировать этот процесс построения нейронных сетей, я построил модель, способную предсказать среднюю температуру на следующий день на основе числовых характеристик, собранных в первой статье этой серии. Тем не менее, я хотел бы воспользоваться моментом, чтобы прояснить свои намерения в отношении этой серии. Моя основная цель состояла не в том, чтобы на самом деле построить современные модели прогнозирования ни в статье о линейной регрессии, ни в текущей статье о нейронных сетях, но мои цели состояли в том, чтобы выполнить следующее:
- Продемонстрируйте общий процесс выполнения аналитического проекта (машинное обучение, наука о данных, что угодно…) из сбора данных, обработки данных, исследовательского анализа данных, выбора модели, построения модели и оценки модели.
- Продемонстрируйте, как выбрать значимые функции, которые не нарушают ключевых допущений метода линейной регрессии, используя две популярные библиотеки Python, StatsModels и Scikit Learn.
- Продемонстрируйте, как использовать высокоуровневый API TensorFlow, и дайте некоторое представление о том, что происходит под всеми этими слоями абстракции.
- Обсудите проблемы, связанные с чрезмерной подгонкой модели.
- Объясните, как важно экспериментировать с несколькими типами моделей, чтобы наилучшим образом решить проблему.
Спасибо, что прочитали. Я надеюсь, что вам понравилась эта серия так же, как и мне, и, как всегда, я приветствую комментарии и критику.