Бонус: знакомство с Spark

Что это для вас значит?

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

Давайте поговорим о данных

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

Заявление об ограничении ответственности!

Пост будет большим. Не волнуйтесь, я не стану утомлять вас странными математическими выкладками, я сосредоточусь на поиске идей, и обещаю, это будет весело.

Возможно, вы захотите использовать colab, если вы используете весь набор данных. В зависимости от емкости вашего ноутбука.

Анализ данных с помощью PySpark

Не стесняйтесь переходить к следующему занятию. Если вы заинтересованы в том, чтобы начать работу с Spark и распределенными вычислениями, в общем, щелкните меня

Это просто, чтобы понять, как работает pyspark.

Моделирование будет означать использование техники машинного обучения для изучения данных о взаимосвязи между набором функций и тем, что мы надеемся предсказать. Прежде чем приступить к построению модели, мы должны узнать о функции стоимости. Это очень важная концепция, которая помогает создавать хорошие модели.

Стоимость / функция оценки

Как мы можем решить эту проблему? Хорошее место для начала рассуждений: допустим, мы создаем множество моделей, чтобы предвидеть нашу цель, как мы можем выбрать лучшую? Когда мы решаем это, наша цель - минимизировать / максимизировать это значение.

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

Поэтому для этой проблемы я бы предложил следующую метрику оценки: среднеквадратичная ошибка (MSE). Чтобы понять MSE, давайте определимся с терминологией:

  • 𝑦hat - это наше прогнозируемое значение для i-й точки данных
  • 𝑦 - это фактическое значение для i-й точки данных
  • 𝑛 - количество точек данных

Таким образом, MSE - это:

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

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

Это зависит от вашей функции потерь. Во многих случаях имеет смысл придавать больший вес точкам, более далеким от среднего, то есть отклонение на 10 более чем в два раза хуже, чем отклонение на 5. В таких случаях RMSE является более подходящей мерой ошибки. Если отставание на десять в два раза хуже, чем на пять, то более подходящим вариантом будет MAE.

Наша модель

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

Где j - количество имеющихся у нас предикторов, а значения β - наши коэффициенты, где β0 - точка пересечения. По сути, наша модель представляет собой линейную комбинацию наших предикторов с перехватом.

Теперь, когда у нас есть модель и функция стоимости, нашей задачей становится поиск значений β для нашей модели, которые минимизируют MSE для наших данных. Для линейной регрессии на самом деле существует решение в замкнутой форме, называемое нормальным уравнением. Однако мы собираемся использовать другую технику, которая более распространена в машинном обучении - градиентный спуск.

Градиентный спуск

Градиентный спуск - это метод, который мы позаимствовали из оптимизации. Это очень простой, но мощный алгоритм, который можно использовать для поиска минимума функции.

  1. Выберите случайное начальное значение
  2. Делайте шаги, пропорциональные отрицательному градиенту в текущей точке.
  3. Повторяйте, пока не сойдетесь

Этот метод найдет глобальный минимум, если функция выпуклая, в противном случае мы можем только доказать, что она найдет локальный минимум. Давайте посмотрим на пример.

Первый вопрос, на который нам нужно ответить: является ли функция стоимости выпуклой? Давайте взглянем:

То, что мы сделали выше, - это диапазон значений коэффициентов солености, и для каждого из них рассчитывается MSE по нашим данным (температура в качестве цели). Если мы затем построим их, мы получим приведенную выше кривую - выглядит довольно выпуклой! И на самом деле оказывается, что наша функция MSE с нашей моделью линейной регрессии всегда будет выпуклой! Это означает, что мы можем использовать градиентный спуск, чтобы найти оптимальные коэффициенты для нашей модели!

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

Градиенты

Градиенты - это просто частные производные по коэффициентам. Для каждого имеющегося у нас коэффициента нам нужно будет вычислить производную нашего MSE по этому коэффициенту. Давайте начнем!

Помните нашу функцию стоимости:

Теперь давайте расширим его для нашего простого примера с помощью перехватчика и единственной переменной Salnty:

Теперь, для производной этого по β0, мы получаем:

А для β1:

Реализацию можно найти в записной книжке.

Скорость обучения

Скорость обучения - это гиперпараметр, используемый для определения того, насколько далеко мы отходим от направления градиента. Как узнать, какое значение выбрать? Как правило, можно попробовать множество значений, и вот некоторые из них, которые, как мне кажется, были предложены Эндрю Нг: .001, .003, .01, .03, .1, .3, 1, 3

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

Существуют также другие оптимизаторы градиентного спуска, которые более сложны и адаптируют скорость обучения за вас. Это также то, что вы можете делать самостоятельно, когда скорость обучения постепенно снижается. Подробнее о различных оптимизаторах можно прочитать здесь.

Когда прекратить итерацию?

В моем коде я просто выбираю запускать наш цикл 10 000 раз. Почему 10 000. Никакой реальной причины, кроме того, что я был уверен, что этого достаточно, чтобы сойтись. Обычно это не лучшая практика. Вот несколько лучших идей:

  1. Следите за своими затратами после каждого цикла, и когда они уменьшаются менее чем на некоторый допуск - скажем, 0,001 - останавливайтесь.
  2. Используйте набор для проверки и отслеживайте убытки, например, MSE. Когда он перестанет уменьшаться, остановитесь.

Нормализация данных

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

когда нормализовать данные?

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

когда стандартизировать данные?

Стандартизация предполагает, что ваши данные имеют распределение Гаусса (кривая колокола). Это не обязательно должно быть правдой, но этот метод более эффективен, если ваше распределение атрибутов гауссово. Стандартизация полезна, когда ваши данные имеют разные масштабы и алгоритм, который вы используете, делает предположения о том, что ваши данные имеют гауссовское распределение, например линейную регрессию, логистическую регрессию и линейный дискриминантный анализ.

Надежный скаляр (масштабирование до медианы и квантилей):

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

X_scaled = (X-X.среднее) / IQR

Другие виды градиентного спуска

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

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

Предположения нашей модели

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

Использование sklearn

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

Не забудьте масштабировать данные - очень важно!

Sklearn - отличный пакет. Он предоставляет множество моделей, и все они имеют функцию согласования и прогнозирования. Вы вызываете подгонку данных X и y для обучения модели и прогнозирования новых функций, чтобы получить прогнозируемое значение. Sklearn также предоставляет множество показателей, которые вы можете использовать для оценки, например, MSE. Здесь я вывожу корневую MSE (RMSE), потому что это возвращает нас к исходному масштабу нашей цели, который мне легче понять.

С нашим SGDRegressor tol сообщает модели, когда следует прекратить итерацию, а eta0 - это наша начальная скорость обучения.

Полиномиальные переменные

Если вы помните наш график Соленти против нашей цели (T_degC), там выглядело немного полиномиальное соотношение (это может быть связано также с масштабом графика). Линейная регрессия соответствует линейным отношениям, но если вы добавите полиномиальные функции, такие как Salnty², вы сможете установить более сложные отношения. Sklearn упрощает это:

Команда полиномиальных признаков сгенерировала новую матрицу признаков, состоящую из всех полиномиальных комбинаций признаков со степенью меньше или равной указанной степени (в нашем примере 2). Затем мы масштабировали эти данные и скормили их нашей модели. И мы получили немного улучшенную RMSE. Отлично! Однако обратите внимание, что эти результаты не очень верны, потому что они применимы к нашим данным тестирования. Позже в этом посте мы рассмотрим, как лучше оценить обобщаемость наших моделей.

Категориальные переменные

Линейная регрессия - одна из моделей, с которой нужно быть осторожным, когда у вас есть категориальные данные. Если у вас есть функция со значениями 1, 2 и 3, которые на самом деле означают «Мужской», «Женский», «Нет ответа». Вы не хотите передавать это модели таким образом, даже если это числа. Если бы вы это сделали, модель присвоила бы этому признаку коэффициент - возможно, 0,1. Это будет означать, что принадлежность к женщине увеличивает прогноз на 0,1, а отсутствие ответа - на 0,2. Но, возможно, женщина должна поднять балл на 1,2, а «Нет ответа» - всего на 0,001. Чтобы учесть это, вы должны преобразовать эти значения в фиктивные переменные, чтобы каждое значение могло иметь свой собственный вес. Если вы не уверены, что порядок ваших числовых значений является постоянным.

Интерпретация вашей модели

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

Введение в учебник по статам. Точки линейной регрессии:

Это просто вступление. Обратитесь к Введение в статистическое обучение для лучшего понимания.

Потенциальные проблемы:

  • Нелинейность отношений между ответом и предиктором - графики остатков помогают нам это определить. yhat - графики y vs x. Если остаточный график указывает на наличие нелинейных связей в данных, тогда простой подход состоит в использовании нелинейных преобразований предикторов, таких как logX, √X и X².
  • Корреляция условий ошибки - если члены ошибки коррелированы, у нас может возникнуть необоснованное чувство уверенности в нашей модели. Почему могут возникать корреляции между ошибочными терминами? Такие корреляции часто возникают в контексте данных временных рядов.
  • Непостоянная дисперсия условий ошибки (гетероскедастичность) - когда вы обнаруживаете формы воронок на остаточных графиках. Когда вы сталкиваетесь с этой проблемой, одним из возможных решений является преобразование ответа Y с помощью вогнутой функции, такой как logY или √Y.
  • Выбросы - выбросы не имеют большого влияния на аппроксимацию методом наименьших квадратов. Они влияют на значения R² и RSE.
  • Точки высокого / низкого плеча - эти точки не являются выбросами, но остаются отличными от данных, близки к линии регрессии и влияют на аппроксимацию методом наименьших квадратов.
  • Коллинеарность - Коллинеарность относится к ситуации, в которой две или более переменных-предикторов тесно связаны друг с другом.

Во-первых, давайте взглянем на коэффициенты, которые изучила наша модель:

Что это за коэффициенты? Они представляют собой среднее изменение температуры на одну единицу изменения характеристики при сохранении других характеристик в модели постоянной (в нашем случае у нас есть только одна). Например, увеличение солености на единицу снижает нашу цель (температуру) на 2,096.

Это действительно здорово! Возможно, мы можем сказать, что если вы хотите повысить температуру, снижение солености может стать отправной точкой. Я говорю «может», потому что линейная регрессия рассматривает корреляции. По нашим данным, это определенно так, но это само по себе не означает, что эти особенности имеют причинно-следственную связь. Тем не менее, это может быть хорошим местом для поиска причинно-следственной связи, и она действительно представляет отношения, которые были замечены в данных.

Доверительные интервалы

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

Bootstrap - это случайная выборка с заменой наших данных, и эта выборка того же размера, что и исходные данные. Это способ создания нескольких представлений одних и тех же данных. Давайте создадим 1000 бутстрапов наших данных. После того, как мы закончили выборку, мы можем получить коэффициенты для каждого из этих наборов данных, введя их в модель.

Это очень мило! Теперь мы можем с большой уверенностью сказать, что фактический коэффициент на Соленти отрицательный и почти наверняка находится в диапазоне от -2,2 до -2,1.

Разделение на обучение / тестирование и перекрестная проверка

До этого момента мы тренировались на всех имеющихся данных. Это может иметь смысл, потому что мы хотим максимизировать полезность наших данных, используя как можно больше для обучения. С другой стороны, трудно оценить, насколько хорошо работает наша модель. Причина этого в том, что если мы просто рассчитаем нашу оценку MSE, используя данные, на которых была обучена модель, мы можем обнаружить, что мы представили данные, на которых она не была обучена, они работают довольно плохо.

Эта идея называется переобучением. По сути, когда модель работает намного лучше с данными, на которых она была обучена, чем с новыми данными, она переоснащена чем-то уникальным для данных обучения, которое не обобщает.

Другая сторона этого называется смещением. Модель имеет высокий уровень смещения, когда на самом деле она просто не подходит для данных. В этом случае MSE будет высокой как для обучающих данных, так и для данных, не видимых во время обучения.

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

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

Вы можете использовать train_test_split из sklearn.model_selection

Отлично! Теперь у нас есть RMSE как для данных обучения, так и для данных тестирования. И оба они довольно близки, что говорит о том, что у нас нет проблемы с переобучением. Хотя они оба низкие? Что предполагает высокую предвзятость.

Один из способов разобраться в этом - построить график обучения. Кривая обучения отображает нашу функцию ошибок (MSE) с различными объемами данных, используемых для обучения. Вот наш сюжет:

Вы можете видеть, что с менее чем 250 миллионами обучающих примеров обучающий MSE довольно хорош, а перекрестная проверка довольно плохая (мы еще не говорили о перекрестной проверке, так что теперь думайте об этом как о тестировании). Если бы у нас было только такое количество данных, это выглядело бы как проблема с высокой дисперсией. По мере увеличения количества данных мы начинаем улучшать оба наших результата, и они становятся очень близкими, что говорит о том, что у нас нет проблемы с высокой дисперсией.

Этот график больше похож на проблему с большим смещением, поскольку две наши кривые очень близки и сглаживаются. Однако трудно сказать наверняка, потому что, возможно, мы только что достигли наилучшего возможного MSE. В таком случае это не будет большой проблемой смещения. Это было бы проблемой только в том случае, если бы наши кривые выровнялись с MSE выше оптимального. В реальной жизни вы не знаете, какова оптимальная MSE, поэтому вам нужно немного теоретизировать, думаете ли вы, что уменьшение смещения улучшит ваш результат - или просто попробуйте!

Устранение проблем с высоким смещением / высокой дисперсией

Итак, теперь, когда вы диагностировали проблему смещения или дисперсии, как вы их исправить?

Для высокой дисперсии:

  • Получите больше данных для обучения
  • Попробуйте меньший набор функций
  • Попробуйте менее сложную модель
  • Добавить регуляризацию

Для высокого смещения:

  • Попробуйте добавить функции
  • Попробуйте более сложную модель

Перекрестная проверка и настройка гиперпараметров

Ранее мы упоминали эту фразу: перекрестная проверка. Давай поговорим об этом сейчас. На данный момент мы узнали, что неплохо разделить данные на наборы для обучения и тестирования, чтобы лучше понять, насколько хорошо модель работает на самом деле. Это замечательно, но представьте, что мы хотим протестировать несколько разных моделей или протестировать разные параметры нашей модели - например, другую скорость обучения или толерантность. Как бы нам решить, какая модель или какой параметр лучше? Будем ли мы обучать все на обучающих данных и тестировать все на наших тестовых данных? Надеюсь, вы понимаете, что это не имеет смысла, потому что тогда мы, по сути, оказались бы на том же месте, что и раньше, без возможности проверить, насколько хорошо мы справляемся с никогда ранее не полученными данными. Итак - мы хотим сохранить наш набор для тестирования незапятнанным в том смысле, что в идеальном мире мы будем запускать наши тесты на нем только после того, как мы проведем все наши эксперименты и думаем, что нашли самую лучшую модель.

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

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

На практике вместо создания единого набора для проверки мы часто используем перекрестную проверку в k-кратном размере. Это означает, что мы выбираем значение k, скажем 3. Затем мы берем наши обучающие данные и разделяем их на 3 части. Мы случайным образом выбираем 2 складки для тренировки, а затем используем оставшиеся для тестирования. Затем мы повторяем это еще 2 раза, всего 3 раза, так что все наблюдения используются как для обучения, так и для проверки, а каждое наблюдение используется для проверки только один раз. Затем мы усредняем все три оценки (в нашем случае MSE), чтобы получить оценку для конкретной модели. Затем мы можем повторить этот процесс для нескольких моделей, чтобы найти лучшую. Хорошее видео по этому поводу

Этот процесс очень прост со sklearn:

Здесь мы фактически использовали рандомизированный поиск, который обычно лучше, чем поиск по всем возможным значениям. Часто вы хотите попробовать много разных параметров для множества разных регуляторов, и поиск по сетке по всему неэффективен. Обычно вы хотите использовать рандомизированный поиск, как мы делали выше. Хотя, поскольку у нас было только небольшое количество значений, мы вынудили его использовать поиск по сетке, установив n_iter_search равным количеству значений, которые мы хотели попробовать.

Мы также установили cv = 3, чтобы иметь 3 складки и использовали отрицательную MSE, потому что функции CV в sklearn пытаются максимизировать значение.

Подробнее о случайном поиске и поиске по сетке можно узнать здесь:

Кроме того, у sklearn есть много других функций CV, которые полезны, особенно если вы хотите протестировать разные модели с одинаковыми складками. Вот некоторая документация

Регуляризация

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

  • Регуляризация L1 (лассо): вы добавляете сумму абсолютных значений коэффициентов к функции стоимости. Этот метод может принудительно обнулить коэффициенты, что затем может быть средством выбора функции.
  • Регуляризация L2 (Ridge): вы добавляете сумму квадратов значений коэффициентов к функции стоимости.
  • Эластичная сетка: вы добавляете и то, и другое и выбираете, как утяжелить их

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

Sklearn упрощает это:

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

Код: Github

Если вам понадобится помощь, вы можете связаться с нами: LinkedIn

Использованная литература:

Практическое машинное обучение от Джерона Орелиена