Как использовать эту мощную базу данных NoSQL для онлайн-вывода

Зачем рассматривать DynamoDB?

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

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

AWS DynamoDB — это чрезвычайно масштабируемая управляемая база данных NoSQL. Как и в других базах данных NoSQL, согласованность в ней заменяется доступностью, что делает ее идеальным хранилищем данных, на основе которого можно построить предсказатель машинного обучения с малой задержкой. Цель этой публикации — продемонстрировать некоторые шаблоны проектирования, с помощью которых вы можете создавать производственные рабочие процессы, достаточно гибкие для обработки распространенных шаблонов доступа ML.

Основные сведения об индексах в DynamoDB

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

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

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

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

Вывод для одного клиента с использованием функции временного ряда

Представьте, что у вас есть модель, которая предсказывает, совершит ли клиент покупку, учитывая их последние 30 дней истории. Ваш продакт-менеджер просит вас использовать эту модель, чтобы определить, должен ли рекламный баннер отображаться на сайте во время браузера (почему бы и нет, я не PM).

Как DynamoDB может поддерживать этот конвейер прогнозирования?

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

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

Как можно эффективно воспроизвести эту настройку в DynamoDB? Модель прогнозирует на уровне клиента, используя функции для этого клиента, поэтому естественным (и эффективным) местом для начала является разделение данных по customer_id. DynamoDB будет постоянно хранить данные для каждого идентификатора клиента; в каждом разделе первичного ключа мы хотим эффективно получать покупки, соответствующие нашему прогнозу. Локальный вторичный индекс позволяет нам сделать именно это.

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

Расширение этого до нескольких прогнозов одновременно

Все идет нормально. Мы можем обратиться к одной конечной точке API с помощью идентификатора клиента и получить доступ к прогнозу этого клиента с низкой задержкой. На самом деле, ваш PM настолько доволен эффективностью рекламной акции, что хочет расширить ее до рассылки по электронной почте. Она хочет, чтобы каждая рассылка была нацелена на клиентов, которые могут совершить покупку в течение следующих 30 дней, сегментированных по клиентам, совершившим покупки в определенных категориях. Одна рассылка по электронной почте может быть нацелена на покупателей ювелирных украшений, а другая — на причудливые рубашки (здесь).

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

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

Приведенный ниже цикл прогнозирования реализует описанную выше стратегию. Простой запрос к GSI быстро возвращает все покупки в таблице в категории, указанной параметром API category_name. Мы дедуплицируем возвращенные идентификаторы клиентов, а затем используем их в запросе BatchGetItem (который сокращает время передачи данных по сети), чтобы получить все покупки для этих клиентов, используя первичный ключ и локальный вторичный индекс. Затем маршалинг и прогнозирование функций могут полностью выполняться в памяти.

Заключение

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

Разработка функций в SQL более интуитивно понятна и менее многословна, поэтому используйте ее там, где это возможно. Но модель DynamoDB с малой задержкой, в конечном счете, согласованная, делает ее привлекательным вариантом для онлайн-логического вывода большого объема.