Рубрики
Без рубрики

Использование машинного обучения для прогнозирования погоды: Часть 3

Автор оригинала: 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()
Потеря обучения (SSE)

Круто! Из приведенной выше диаграммы видно, что после всех этих итераций я не переоснастил модель, потому что потери оценки никогда не демонстрируют значительного изменения направления в сторону увеличения значения. Теперь я могу спокойно перейти к составлению прогнозов на основе моего оставшегося тестового набора данных и оценить, как модель предсказывает средние погодные температуры.

Как и два других метода регрессора, которые я продемонстрировал , метод 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 . Попутно я описал, в общем смысле, теорию нейронных сетей, как они обучаются, и важность осознания опасности переоснащения модели в процессе.

Чтобы продемонстрировать этот процесс построения нейронных сетей, я построил модель, способную предсказать среднюю температуру на следующий день на основе числовых характеристик, собранных в первой статье этой серии. Тем не менее, я хотел бы воспользоваться моментом, чтобы прояснить свои намерения в отношении этой серии. Моя основная цель состояла не в том, чтобы на самом деле построить современные модели прогнозирования ни в статье о линейной регрессии, ни в текущей статье о нейронных сетях, но мои цели состояли в том, чтобы выполнить следующее:

  1. Продемонстрируйте общий процесс выполнения аналитического проекта (машинное обучение, наука о данных, что угодно…) из сбора данных, обработки данных, исследовательского анализа данных, выбора модели, построения модели и оценки модели.
  2. Продемонстрируйте, как выбрать значимые функции, которые не нарушают ключевых допущений метода линейной регрессии, используя две популярные библиотеки Python, StatsModels и Scikit Learn.
  3. Продемонстрируйте, как использовать высокоуровневый API TensorFlow, и дайте некоторое представление о том, что происходит под всеми этими слоями абстракции.
  4. Обсудите проблемы, связанные с чрезмерной подгонкой модели.
  5. Объясните, как важно экспериментировать с несколькими типами моделей, чтобы наилучшим образом решить проблему.

Спасибо, что прочитали. Я надеюсь, что вам понравилась эта серия так же, как и мне, и, как всегда, я приветствую комментарии и критику.