ОБНОВЛЕНИЕ: по состоянию на середину 2021 года этой статье уже около 2,5 лет!
Snowflake сильно изменилась за это время, включая введение Snowpark, который является лучшим способом перенести тяжелую пользовательскую обработку в свою двигатель.
Итак, если вы попали сюда из поисковой системы, то это потому, что историческая популярность выше, чем полезность контента 😁
Если вас интересует веселый эксперимент, в котором используется основанный на наборах подход к алгоритмам машинного обучения , продолжайте читать, и я надеюсь, что статья вам понравится.
Но если вы ищете лучший способ действительно запустить алгоритмы машинного обучения в движке Snowflake в производственной среде, есть много официальных партнеров Snowflake, которые изменили свои продукты, чтобы снизить переработку через Snowpark.

Если вы занимаетесь машинным обучением в Snowflake, у вас должен быть план по его внедрению! Еще одна вещь, которая изменилась после этой статьи, - это запуск стартапа под названием Omnata, который позволяет вам: 1) Подключить CRM напрямую к Snowflake для получения живых контекстных данных и 2) Перенести данные из Snowflake на ваш SaaS. Программы. Проверьте нас!

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

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

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

Должны ли мы?

Подходит ли движок базы данных для обучения модели?

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

Поскольку термин интеллектуальный анализ данных стал популярным в 1990-х годах, поставщики СУБД добавили функции статистического моделирования к своим диалектам SQL, например, IBM представила Intelligent Miner, Oracle представила Oracle Data Mining.

Затем, после бума .com, наборы данных выросли до такой степени, что мы стали называть их «большими», популярность Hadoop возросла как способ масштабирования обработки - за ним последовал Spark. Рабочие нагрузки интеллектуального анализа данных вышли за рамки традиционных СУБД.

Но так же, как многие объявили SQL мертвым, в 2010 году был выпущен Hive, и внезапно эти большие груды данных снова стали известны как хранилища данных. Перенесемся в 2019 год, и теперь все основные поставщики облачных услуг предоставляют некоторую форму интерфейса SQL для структурированных и полуструктурированных данных в своих объектных хранилищах.

В середине 2018 года Google анонсировал бета-версию BigQueryML, и теперь мы снова используем SQL как абстракцию по сравнению с машинным обучением, хотя и для гораздо больших наборов данных с гораздо большей (и эластичной) вычислительной мощностью. По состоянию на февраль 2019 года BigQueryML все еще находится в стадии бета-тестирования, в настоящее время он довольно прост и ограничен только линейной регрессией и логистической регрессией.

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

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

В снежинке

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

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

Чтобы свести к минимуму объем логики, которую мне нужно реализовать, я собираюсь выполнить упражнение дерево регрессии с использованием алгоритма ID3, главным образом для того, чтобы я мог повторно использовать существующие функции стандартного отклонения Snowflake при построении дерева.

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

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

Сценарий

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

Мы можем сохранить его в такой таблице:

create table bikes_hours(instant numeric, dteday date, season numeric, yr numeric, mnth numeric, hr numeric, holiday numeric, weekday numeric, workingday numeric, weathersit numeric, temp numeric, atemp numeric, hum numeric, windspeed numeric, casual numeric, registered numeric, cnt numeric)

и используйте инструмент командной строки SnowSql для загрузки файла csv.

Обучение

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

create or replace sequence ml_model_runs_sequence start = 1 increment = 1;
create or replace table ml_model_runs(run_id integer,
                                      table_name varchar(256),
                                      algorithm varchar(100),
                                      training_parameters variant,
                                      start_time timestamp, 
                                      end_time timestamp,
                                      model_object variant);

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

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

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

Наша хранимая процедура будет принимать четыре параметра: имя таблицы, целевая переменная (имя столбца, содержащего значения, которые мы хотим предсказать), имена столбцов, которые будут использоваться в качестве атрибутов в дереве, и параметры обучения.

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

Первый запрос дает нам:

1) Стандартное отклонение всех целевых значений от этого узла вниз, так как мы выберем ветвь, которая уменьшает это значение больше всего
2) Среднее значение всех целевых значений от этого узла вниз, как окончательное среднее используется в качестве предиктора, когда мы достигаем листа.
3) Коэффициент вариации, можно использовать, чтобы остановить построение, когда он становится слишком маленьким
4) Можно использовать количество целевых значений от этого узла вниз чтобы прекратить строительство, когда он становится слишком маленьким
5) Для каждой потенциальной ветви ниже (из списка оставшихся столбцов) - медианное значение. Это будет использоваться для разделения данных, если выбран этот атрибут.

Вверху дерева это выглядит так:

но по мере того, как дерево становится все шире и шире, оно начинает выглядеть так:

Второй запрос состоит из двух частей:

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

Оба запроса используют предложения WHERE для фильтрации до их позиции в дереве.

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

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

ближе к низу, поскольку он оценивает все возможные разбиения для каждой ветки, это выглядит так:

Из общего количества доступных атрибутов (9), используя параметры конфигурации, я ограничил оценку 5 из них за раз.

При первом разбиении план запроса для второго запроса выглядит следующим образом (значения для разбиения каждого столбца были предоставлены первым запросом):

и к тому времени, когда вы перейдете к некоторой приличной глубине, план запроса начнет выглядеть так:

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

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

Во-первых, давайте добавим в набор данных столбец оценки:

alter table bikes_hours add column score numeric

затем, используя функцию примера Snowflake, разделите исходную таблицу на обучение (90%):

create temporary table bikes_hours_training as
select * from bikes_hours sample (90)

… И оставшиеся 10% для тестирования (10%):

create temporary table bikes_hours_testing as
select * from bikes_hours where instant not in (select instant from bikes_hours_training)

Теперь мы вызываем процедуру Decision_tree_train для прогнозирования столбца «CASUAL» (количество случайных гонщиков в час) с использованием 9 столбцов.

call decision_tree_train('bikes_hours_training', 'CASUAL', 'season,hr,weekday,workingday,temp,atemp,weathersit,hum,holiday');

Вот как выглядят полученные модели в таблице ml_model_runs:

Вот фрагмент объекта модели:

Подсчет очков

Чтобы сгенерировать прогноз для записи базы данных, нам нужен способ навигации вниз по дереву, используя значения в его столбце.

Первоначально я думал, что для этого идеально подойдет табличная функция, определяемая пользователем (UDTF) (код здесь), поскольку она должна использовать параллелизм, который вы получаете от движка Snowflake. Я построил тот, который детализировал модель (как объект javascript), пока не достиг листа. На практике ограничение по времени UDF (около минуты?), Казалось, накапливалось над столом, и я слишком часто его нажимал.

Затем я понял, что дерево решений можно легко преобразовать в гигантский оператор CASE и запустить его как простой SQL. Итак, я написал хранимую процедуру (код здесь), которая сгенерировала оператор case и выполнила запрос UPDATE для выбранной таблицы, поместив предсказанное значение в один из ее столбцов.

Итак, мы запускаем его, передавая объект json дерева решений и имя столбца, в который нужно ввести оценку:

call decision_tree_score('bikes_hours_testing',(select model_object from ml_model_runs where run_id=1),'SCORE');

Оценка

Распространенный способ оценки модели - Среднеквадратичная ошибка. В Snowflake это можно сделать так:

select sqrt(sum(square(casual-score))/count(*) ) from bikes_hours_testing

Результат 25.419340847

Учитывая диапазон значений от 0 до 367, это не слишком затруднительно.

Представление

Обучение

Одна из замечательных особенностей Snowflake заключается в том, что отзывы о производительности запросов встроены прямо в продукт. Поэтому после запуска процесса обучения я просто переключаюсь на вкладку «История», чтобы увидеть профиль.

Я использовал 9 атрибутов, но ограничил обучение следующим образом:

  • не более 5 атрибутов для одновременного сравнения
  • коэффициент вариации нижний предел 10

и это дало двоичное дерево с 33 листьями.

Всего на обучение на среднем кластере ушло 128 секунд.

В корневом узле первый запрос (сводка по каждому узлу на каждой глубине) занял около 200 мс и постепенно увеличивался до 10 секунд на конечной глубине 10.

Второй запрос (оценка кандидатов на разделение ветвей для каждого узла на каждой глубине) начался с 1,3 секунды, постепенно увеличился до 23,8 секунды на глубине 7, затем сократился до 14,8 секунды на конечной глубине 10 (это потому, что ветви завершаются, чтобы дерево не разветвлялось полностью вниз).

Подсчет очков

Подсчет очков был, конечно, почти мгновенным. Передача оператора CASE в механизм запросов Snowflake (даже такой большой) хорошо оптимизирован.

Заключение

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

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

Что дальше?

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

Но что еще более важно, построение дерева решений формирует основу для наиболее популярных алгоритмов машинного обучения для структурированных данных (случайные леса, XGBoost), поэтому не должно быть никаких причин, по которым этот подход не может быть расширен.

Другими частями головоломки, которые могут позволить специалисту по обработке данных выиграть соревнование Kaggle полностью в Snowflake, являются:

Функциональная инженерия

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

Отбор проб

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