Уделение внимания автокорреляции в данных может помочь вам построить лучшие прогностические модели.

Во-первых, викторина.

В двух приведенных ниже примерах можно ли доверять заявленной точности?

  1. Шивраму приходит в голову блестящая идея предсказать частоту сердечных сокращений только по движениям iPhone. Он собирает синхронизированные по времени данные о движениях iPhone и частоте сердечных сокращений с часов Apple Watch от тысяч добровольных пользователей. Затем он разбивает данные случайным образом посекундно на наборы для обучения, проверки и тестирования. После того, как он доволен своей моделью, он сообщает, что может предсказать частоту сердечных сокращений по движениям iPhone с колоссальной точностью 98% на тестовом наборе!
  2. Абхилаш хочет использовать спутниковые снимки для поиска лесов. Он получает некоторые обучающие данные спутниковых изображений и нарисованных человеком геолокационных карт лесов. Затем он разбивает пиксели случайным образом на наборы для обучения, проверки и тестирования. После того, как он доволен своей моделью, он сообщает, что точность его теста составляет 99%!

Можно ли доверять заявленной точности в приведенных выше двух случаях?

NO!

В этой статье мы узнаем, почему их нет. Мы также изучим некоторые основные принципы предварительной обработки, которым можно следовать, чтобы избежать подобных ловушек в будущем.

Зачем заботиться, если данные i.i.d.?

Независимые и одинаково распределенные данные (i.i.d.) обладают множеством хороших свойств в прогностических настройках, когда знание одной точки данных ничего не говорит вам о другой точке данных. Когда мы разделяем данные для обучения модели, зная, являются ли данные i.i.d. является обязательным.

Разделение данных на наборы для обучения, проверки и тестирования — один из самых стандартных способов проверки производительности модели в условиях контролируемого обучения. Даже до того, как мы приступим к моделированию (которому уделяется почти все внимание в машинном обучении), не заботясь о восходящих процессах, таких как, откуда поступают данные, действительно ли они идентифицированы и как мы их разделяем, может иметь серьезные последствия для качества. прогнозов.

Это особенно важно, когда данные имеют высокую автокорреляцию. Автокорреляция между точками просто означает, что значение в точке похоже на значения вокруг нее. Возьмем, к примеру, температуру. Ожидается, что температура в любой момент будет аналогична температуре в предыдущую минуту. Таким образом, если мы хотим предсказать температуру, нам нужно проявлять особую осторожность при разделении данных. В частности, нам нужно убедиться, что между обучением, проверкой и наборами тестов нет утечки данных, которая может преувеличить производительность модели.

Насколько производительность модели может быть завышена из-за утечки информации?

Прочитав вышеизложенное, естественно задаться вопросом, достаточно ли это важная проблема, чтобы меня волновать? На примере данных с высокой степенью автокорреляции мы увидим, что ответ, безусловно, да! Мы разобьем пример на две части. Во-первых, мы случайным образом разделим данные на обучающий и проверочный наборы и добьемся очень высокой точности на проверочном наборе. Затем мы разделим данные, используя стратифицированную случайную выборку, что уменьшит утечку информации. Затем мы увидим, что та же модель имеет почти нулевую точность.

Интерактивный пример

Если вы хотите следовать этому примеру в интерактивном режиме, вы можете использовать эту записную книжку colab.

Давайте сначала импортируем соответствующие пакеты.

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import sklearn.model_selection
import sklearn.linear_model
import sklearn.ensemble

Давайте сделаем некоторые синтетические данные с высокой автокорреляцией в переменной отклика.

# number of examples in our data
n = int(100*2*np.pi)
# Seed for reproducebility
np.random.seed(4)
# make one feature (predictor)
x = np.arange(n)
# make one response (variable to predict) which has high autocorrelation. Use a
# sine wave.
y =  np.sin(x/n*7.1*np.pi)+np.random.normal(scale = 0.1, size = n)
# merge them into a dataframe to allow easy manipulation later
df = pd.DataFrame({"x":np.array(x), "y":np.array(y), "y_pred":np.nan})
# visualize the response versus feature
sns.set(style = "ticks", font_scale = 1.5)
sns.regplot(x="x",y="y",data=df)

Случайное разделение данных

Давайте случайным образом разделим данные на набор для обучения и проверки и посмотрим, насколько хорошо работает модель.

# Use a helper to split data randomly into 5 folds. i.e., 4/5ths of the data is chosen *randomly* and put into the train set, while the rest is put into
# is chosen *randomly* and put into the train set, while the rest is put into
# the validation set.
kf = sklearn.model_selection.KFold(n_splits=5, shuffle=True, random_state=42)
# Use a random forest model with default parameters.
# The hyperaparameter of the model are not important for this example because we
# will use the same model twice- once with data split randomly and (later) with
# data split with stratification
reg = sklearn.ensemble.RandomForestRegressor()
# use k-1 folds to train. Predict on the kth fold and store in the dataframe
for fold, (train_index, test_index) in enumerate(kf.split(df)):
reg.fit(df.loc[train_index, "x"].values.reshape(-1, 1), df.loc[train_index, "y"])
df.loc[test_index, "y_pred"] = reg.predict(df.loc[test_index, "x"].values.reshape(-1, 1))
# visualize true y versus predicted y
fig, ax = plt.subplots(figsize = (5,5))
sns.kdeplot(
data=df, x="y_pred", y="y",
fill=True, thresh=0.3, levels=100, cmap="mako_r",ax=ax
)
ax.set_xlim(-2,2)
ax.set_ylim(-2,2)
ax.set_xlabel(r"y$_{\rm predicted}$")
ax.set_title("Exaggerated predictive ability\nassuming data is i.i.d.")
r2 = sklearn.metrics.r2_score(df.y, df.y_pred)
ax.annotate(f"R$^2$ = {r2:0.2f}", xy =(0.95,0.95), ha = "right", va = "top", xycoords = "axes fraction")
print(f"[INFO] Coefficient of determination of the model is {r2:0.2f}.")

Вау!! Мы достигли R2 97%! Похоже, наша модель отлично справляется с моделированием синусоидальной функции отклика.

Но… способна ли модель действительно понять функцию отклика между x и y? Или это просто интерполяция ближайшего соседа? Другими словами, не обманывает ли модель, запоминая обучающие данные и выводя значение y ближайшего обучающего примера? Давайте выясним, затруднив обман модели.

Стратифицированное разделение данных

Теперь, вместо того, чтобы разбивать данные случайным образом, мы разделим данные на 5 фрагментов по оси x (признак). Затем мы поместим 4 фрагмента в обучающие данные и 1 фрагмент в проверочный набор.

Стратифицируя данные по признаку, который автокоррелирован, мы учитываем не i.i.d. характер данных.

Посмотрим, имеет ли модель такую ​​же точность.

# How many chunks to split data in? 
nbins = 5
df["fold"] = pd.cut(df.x, bins = nbins, labels = range(nbins))
# Split the data into training and validation data based on the chunks.
# Train on 4 chunks, predict on the remaining chunk.
for fold in sorted(df.fold.unique()):
  train_index = df.loc[df.fold!=fold].index
  test_index = df.loc[df.fold==fold].index
  reg.fit(df.loc[train_index, "x"].values.reshape(-1, 1), df.loc[train_index, "y"])
  df.loc[test_index, "y_pred"] = reg.predict(df.loc[test_index, "x"].values.reshape(-1, 1))
# Visualize true y versus precited y.
fig, ax = plt.subplots(figsize = (5,5))
sns.kdeplot(
    data=df, x="y_pred", y="y",
    fill=True, thresh=0.3, levels=100, cmap="mako_r",ax=ax
)
ax.set_xlim(-2,2)
ax.set_ylim(-2,2)
ax.set_xlabel(r"y$_{\rm predicted}$")
ax.set_title("True predictive ability")
r2 = sklearn.metrics.r2_score(df.y, df.y_pred)
ax.annotate(f"R$^2$ = {r2:0.2f}", xy =(0.95,0.95), ha = "right", va = "top", xycoords = "axes fraction")
print(f"[INFO] Coefficient of determination of the model is {r2:0.2f}.")

Теперь мы видим, что наша модель имеет нижеслучайную производительность (примечание: интересно, как коэффициент детерминации может быть отрицательным? Подробнее здесь)! Это показывает, что наша исходная модель на самом деле не использовала x в качестве информативного предиктора для y, а только для нахождения ближайшего x из обучающего набора и выдачи соответствующего y. Таким образом, если мы не будем осторожны с автокорреляцией в наших данных, мы можем преувеличить производительность модели.

Хуже того, мы могли ошибочно сделать вывод о важности х и сделать несколько научных выводов. Принимая во внимание, что наша модель использовала x только для интерполяции/запоминания ответа. К сожалению, это не выдуманный пример. Эта статья показывает, что несколько работ в области наук о земле, пытающихся предсказать биомассу растительности (аналогично примеру Абхилаша в начале этой статьи), пронизаны этой проблемой.

Заключение

Разделение данных может иметь огромные последствия. Если есть какие-либо доказательства того, что данные являются автокоррелированными или, в более общем случае, не являются i.i.d., могут быть полезны послойное разбиение или другие методы декорреляции данных с использованием разложения сигнала. По крайней мере, визуализация ваших данных перед тем, как приступить к моделированию, может быть чрезвычайно полезной. Так что в следующий раз, когда вы встретите Шиврама, Абхилаша или кого-то еще, кто утверждает, что достиг очень высокой производительности моделирования после случайного разделения своих данных, вы хорошо подготовлены, чтобы помочь им разработать более совершенные прогностические модели без преувеличения производительности моделей.