newsmode
search
Меню
arrow_back Назад

Feature Stores: A Hierarchy of Needs

auto_awesomeКраткое саммари

Юджин Янь разбирает, что такое feature store, и предлагает рассматривать его возможности как иерархию потребностей по аналогии с пирамидой Маслоу. В основании — доступ к признакам (поиск, переиспользование, прозрачность), затем serving в продакшене с высокой пропускной способностью (как у DoorDash с 10+ млн QPS на Redis), целостность данных (минимизация train-serve skew и point-in-time correctness), удобство использования (единые API для офлайн и онлайн) и автопилот (автоматический backfill, мониторинг качества данных). Автор анализирует подходы Uber (Palette, Michelangelo), Airbnb (Zipline), Netflix (distributed time-travel), GoJek (Feast), Monzo Bank и DoorDash. Для старта Янь рекомендует open-source решение Feast, покрывающее базовые уровни доступа и serving. Не каждой организации нужны все уровни, но первые два-три обычно дают наибольшую отдачу.

Feature Stores: иерархия потребностей

[ teardown machinelearning engineering 🔥 ] · 19 мин чтения

Feature stores в последнее время привлекают много внимания. В декабре 2020 года Amazon Web Services выпустила SageMaker Feature Store. В прошлом месяце компания Splice Machine, платформа для больших данных, тоже запустила собственное feature store. Издание Datanami даже назвало 2021 год годом feature store (цитируя сооснователя Tecton.ai).

По нашему опыту, управление признаками — одно из самых серьёзных узких мест при выводе ML-моделей в продакшен. — Uber

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

Что такое feature store? Это зависит от того, кого вы спросите. В одних статьях это определяют просто как «центральное место для хранения подготовленных признаков». В других говорят, что он помогает «создать признаки один раз и использовать их где угодно» или «разворачивать модели в 100 раз быстрее». Определений много, и я думаю, дело в том, что чем является feature store, зависит от того, что вам нужно.

Чтобы лучше разобраться с feature stores и потребностями, которые они закрывают, я изучил технические блоги компаний. Неудивительно, что я начал систематизировать возможности feature store (вот так каламбур) в виде иерархии потребностей (вдохновлённой Маслоу). Сначала мы рассмотрим общий обзор иерархии потребностей feature store, а затем обсудим, как компании удовлетворяют эти потребности на каждом уровне.

Эта статья теперь доступна на китайском благодаря Yik San!

Иерархия потребностей feature store

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

Maslow's hierarchy of needs, represented as a pyramid with the more basic needs at the bottom

Иерархия потребностей Маслоу в виде пирамиды, с более базовыми потребностями в основании. (источник)

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

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

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

Потребности в уважении: они основаны на способностях или достижениях и делятся на две категории. «Низшая» версия — потребность в уважении со стороны других: статус, признание, репутация. «Высшая» версия — потребность в самоуважении, мастерстве и достижениях.

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

Я думаю, что feature stores тоже можно организовать как иерархию потребностей. Некоторые потребности более насущны, чем другие (например, доступ к данным, serving признаков в продакшене). Эти базовые потребности должны быть удовлетворены до того, как имеет смысл думать о более высоких уровнях.

Иерархия потребностей Feature Store

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

Раньше ML-практики в Airbnb тратили примерно 60% своего времени на сбор данных и написание трансформаций для ML-задач. — Airbnb

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

Data scientists обычно реализовывали признаки в изолированных контурах, а затем передавали свой код команде дата-инженеров для повторной реализации production-ready пайплайнов. Этот процесс может добавлять к проекту месяцы и требует сложной координации между командами. — GoJek

Когда нижние уровни закрыты, можно переходить к потребностям целостности. Здесь главная задача — минимизировать train-serve skew. Это означает обеспечение согласованности между признаками во время обучения и признаками, подаваемыми на вход модели в продакшене. Другая частая потребность — point-in-time correctness (она же time travel). Она гарантирует, что исторические признаки и метки, используемые в офлайн-обучении и оценке, не содержат утечек данных.

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

Дальше идут потребности удобства. Feature store должен быть простым и быстрым в использовании, чтобы дата-практики его приняли и получили прирост продуктивности. Сюда входят простые интуитивные API, которые можно использовать как для обучения, так и для serving, а также интерактивность для ускорения разработки и отладки.

Но помните: мы платформа, а не инженерная команда… Как нам дать пользователям инструмент, который вернёт им эту мощь, чтобы они могли без нашей помощи делать всё это самостоятельно? — Uber

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

Backfilling — серьёзное узкое место, когда вы итеративно дорабатываете идеи для тренировочного набора. Эффективное вычисление новых обучающих наборов сильно ускоряет работу data scientist. — Airbnb

Не всем организациям нужны все уровни потребностей, хотя большинство команд получат пользу от первых двух уровней (доступ и serving) и части третьего (минимизация train-serve skew). Глубина каждой потребности тоже может варьироваться. Организации с меньшим числом онлайн ML use-cases могут иметь менее требовательные нужды по serving по сравнению с DoorDash (миллионы запросов в секунду). Модели и признаки, обновляющиеся в течение дня, могут иметь более сложные требования к point-in-time correctness.

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

Части иерархии потребностей feature store позаимствованы из модели эволюции продукта в книге The Innovator's Dilemma. Её также называют buying hierarchy по имени создателей, Windermere Associates из Сан-Франциско, Калифорния.

Competition evolves along Functionality, Reliability, Convenience, and Price

Конкуренция эволюционирует по осям функциональности, надёжности, удобства и цены

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

Надёжность: когда два или более продукта правдоподобно закрывают спрос на функциональность, клиенты выбирают по надёжности. Может ли продукт стабильно обеспечивать одно и то же качество?

Удобство: когда несколько продуктов более чем удовлетворяют требования рынка к надёжности, клиенты выбирают по удобству. Что проще и/или быстрее?

Цена: наконец, когда несколько продуктов полностью удовлетворяют спрос на удобство, конкуренция смещается к цене. Что дешевле?

Доступ: сокращение дублирования и поощрение переиспользования

Что происходит, если доступ к информации о признаках и данным затруднён? Вот типичный опыт нескольких компаний:

Разные команды по нескольку раз создавали один и тот же признак, иногда до 10 разных версий одного и того же. Строились дублирующиеся пайплайны для концептуально похожих признаков, что приводило к лишним усилиям и расходам ресурсов (compute, storage). Сложно получать согласованные результаты, потому что модели использовали разные версии одного признака. Как следствие — более медленные итерации и разработка у команд data science и ML.

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

Чтобы решить эту проблему, GoJek построила Feast — feature store, который служит интерфейсом между дата-инженерами и сайентистами с одной стороны и ML-практиками с другой. Дата-инженеры и сайентисты создают признаки и контрибьютят их в feature store. Затем ML-практики потребляют эти готовые признаки, экономя время за счёт того, что им не приходится создавать свои.

(Не уверен, как я отношусь к такому приросту продуктивности, учитывая моё предпочтение, чтобы data scientists работали более end-to-end. Тем не менее, это разделение между дата-инжинирингом и ML могло быть артефактом структуры дата-команды GoJek: команда дата-инженеров находилась преимущественно в Индии, а ML-команда — преимущественно в Сингапуре. Так что Feast выступил интерфейсом для совместной работы.)

Аналогично, Uber построила свой feature store Palette, чтобы поощрять обмен признаками — и их переиспользование — в одном месте. Разные команды Uber могут контрибьютить признаки в Palette и использовать их оттуда. Для Uber это сократило дублирование работы, сделало результаты ML более согласованными и ускорило ML-процесс.

Чтобы признаки было легко находить и использовать, нужны и аспекты data discoverability (хотя можно утверждать, что это потребность удобства). Я не встретил много обсуждений data discoverability в контексте feature stores, но подозреваю, что они будут очень похожи на то, что я ранее писал об open-source платформах для data discovery.

На этом уровне feature store в основном выполняет роль хранилища, пусть и очень полезного, содержащего краудсорсенные/курируемые признаки, которые ML-практики могут просто подключать. Тем не менее, это не сильно отличается от хранилища данных. Что отличает feature stores от хранилищ данных — это способность закрывать второй уровень иерархии потребностей, serving.

Serving: использование признаков в реальном времени

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

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

В Monzo Bank признаки были доступны в аналитическом стеке, который использовался для обучения, но не в продакшен-стеке, который использовался для serving. Чтобы это решить, они применили lean-подход, автоматизировав синхронизацию признаков из аналитического хранилища (BigQuery) в продакшен-хранилище (Cassandra).

Сначала к SQL-запросам, создающим таблицы признаков в аналитическом стеке, добавляются теги. Эти таблицы обновляются с дневным/часовым интервалом. Go-сервис feature store проверяет схему этих таблиц (например, присутствуют ли обязательные столбцы вроде subject_type и subject_id?). Наконец, cron-задача проверяет обновлённые таблицы признаков и синхронизирует их из BigQuery в Cassandra через staging-зону в Google Cloud Storage.

Palette от Uber использует похожий подход с двумя хранилищами. Офлайн-хранилище (Hive) сохраняет снимки признаков и в основном используется для обучения. Онлайн-хранилище (Cassandra) обслуживает те же признаки в реальном времени. Признаки, отсутствующие в Cassandra, генерируются онлайн через Flink и затем сохраняются в Cassandra. Данные между двумя хранилищами синхронизированы. Новые признаки, добавленные в Hive, автоматически копируются в Cassandra; признаки реального времени, добавленные в Cassandra, ETL-ятся в Hive.

Создание батчевых (слева) и реал-тайм (справа) признаков и синхронизация между хранилищами (источник)

DoorDash довела закрытие потребностей serving до крайности со своим Gigascale feature store. Вот их жёсткие технические требования:

Хранит миллиарды записей в персистентном масштабируемом хранилище: у DoorDash миллионы сущностей (пользователи, мерчанты, блюда), что даёт миллиарды пар «признак-значение». Масштабируется до миллионов запросов в секунду (QPS): feature store используется во множестве задач, включая ранжирование магазинов, в котором десятки признаков и более 1 млн предсказаний в секунду. С учётом всех моделей в продакшене feature store получает до 10+ млн QPS. Обновляется ежедневно через быстрые батчевые записи: большинство признаков обновляется раз в сутки, а признаки реального времени (например, среднее время доставки магазина за скользящее 20-минутное окно) обновляются равномерно в течение дня.

Чтобы построить этот feature store, DoorDash прогнала бенчмарки нескольких key-value хранилищ — Redis, Cassandra, CockroachDB, ScyllaDB и YugabyteDB — и в итоге остановилась на Redis. Подробности процесса бенчмаркинга и оптимизации Redis есть в их превосходной статье.

Другой подход — вычислять признаки в реальном времени. Так Alibaba обслуживает рекомендации реального времени: их Alibaba Basic Feature Server считает статистические признаки по пользовательским взаимодействиям (клики, лайки, покупки) в реальном времени. Эти признаки затем используются для генерации кандидатов. Подробнее о real-time recsys — в предыдущей статье.

Целостность: создание корректных офлайн- и онлайн-признаков

Когда потребности serving закрыты, переходим к потребностям целостности. Тут две основные боли:

Сложность создания point-in-time корректных признаков для симуляции продакшена; ошибка тут приводит к утечкам данных. Несогласованность между признаками, используемыми при обучении и при serving, что приводит к субоптимальной работе модели в продакшене.

Чтобы решить первую проблему, Netflix построил распределённый time-travel. Он делает снимки офлайн- и онлайн-данных. Эти снимки покрывают разные контексты: тип подписки, устройство, время суток и т. д.

Создание снимков из офлайн-хранилищ и онлайн-микросервисов (источник)

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

Аналогичный процесс снимает онлайн-данные с сотен микросервисов. Эти микросервисы предоставляют такие данные, как история просмотров, персонализированные очереди просмотра и предсказанные оценки. Spark параллелизует вызовы Prana, который забирает данные из разных микросервисов. Снимки также хранятся в S3 в формате Parquet.

Чтобы решить вторую боль — train-serve skew — GoJek взяла Apache Beam в качестве пайплайна обработки данных. Данные из батч- и стрим-источников (BigQuery, Kafka) принимаются через Beam в офлайн- и онлайн-хранилища (BigQuery, Redis). Единый API (для доступа к историческим и онлайн-данным) минимизирует необходимость переписывать пайплайны признаков под среду serving и тем самым случайно вносить train-serve skew.

Feast от GoJek принимает признаки через Apache Beam (источник)

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

Netflix использует одни и те же encoders для офлайн- и онлайн-генерации признаков (источник)

Мы также видели, как Uber подошёл к этому, держа офлайн- (Hive) и онлайн- (Cassandra) хранилища признаков синхронизированными. Новые признаки в любом из хранилищ копируются в другое, что обеспечивает согласованность между данными для обучения и serving.

Также нужен мониторинг, чтобы обеспечить целостность признаков:

Когда признаки обновлялись в последний раз? Корректны ли схемы? Сместилось ли распределение данных? Выполняем ли мы требования к serving (пропускная способность, задержка)?

Чтобы data scientists могли визуально проверять признаки на целостность, Zipline от Airbnb включает UI, показывающий распределение признаков, корреляции между признаками и метками и выполняющий кластерный анализ (правда, не уверен, на чём именно этот кластерный анализ).

Аналогично, Uber построил Data Quality Monitor. Он ежедневно даёт пользователям оценки качества данных, а также алерты при обнаружении аномалий. Работает он так:

Считаются метрики по признакам (среднее, медиана, максимум, минимум для числовых; число уникальных и пропущенных значений для категориальных). Затем строится многомерный временной ряд по этим метрикам (и применяется PCA для извлечения главных компонент). Наконец, по главным компонентам строится временной ряд. Если текущее измерение не совпадает с прогнозом на один шаг вперёд, оно классифицируется как аномалия.

Оценки качества данных во времени и моменты инцидентов (источник)

Удобство: сделать использование максимально простым

Я не нашёл много материалов про удобство (и про боли с ним) в контексте feature stores. Тем не менее, как практикам, нам важно, чтобы инструменты и платформы были простыми в использовании (например, PyTorch против TensorFlow).

Лучший пример, который я нашёл, — от GoJek. Они предоставляют единые SDK на Python, Java и Go для упрощения получения признаков как из офлайн-, так и из онлайн-хранилищ. API позволяет писать практически идентичный код для get_batch_features() и get_online_features(), упрощая переход от офлайн- к онлайн-feature engineering.

customer_features = ['credit_score', 'balance', 'total_purchases', 'last_active'] historical_features_df = feast.get_historical_features(customer_ids, customer_features) model = ml.fit(historical_features_df) # pseudo code online_features = feast.get_online_features(customer_ids, customer_features) prediction = model.predict(online_features)

Для time-travel Netflix предоставляет простые API, чтобы data scientists могли создавать point-in-time корректные признаки и метки. Вот пример получения снимка истории просмотров для фильма OUTATIME.

val snapshot = new SnapshotDataManager(sqlContext) .withTimestamp(1445470140000L) .withContextID(OUTATIME) .getViewingHistory

Опираясь на этот снимок, пользователи могут выполнять time-travel и создавать признаки для обучения и валидации, указав следующее:

Context: где, когда и как используется модель (страна, устройство, профиль участника, фильм, время); ключевой компонент тут — время. Items: элементы для скоринга/ранжирования (фильмы, рекомендательные slates, поисковые сущности). Labels: целевые значения для supervised learning (клик, просмотр, минуты просмотра). Для unsupervised learning не требуются. Feature encoder: как объединить данные контекста и элемента в признаки, например feature-crosses (страна-фильм, userID-фильм) и другие комбинации.

Uber также привёл подробный пример того, как они расширили Spark Transformer (нет, не тот NLP-трансформер) и построили предметно-ориентированный язык (DSL) для получения и трансформации признаков. Это сделано через две абстракции:

Transformer: несмотря на название, в основном используется для получения признаков. Estimator: не для обучения моделей, как у Spark's Estimator, а в основном для создания признаков.

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

tx_p1 = PaletteTransformer([ "@palette:restaurant:realtime_feature:nMeal:r_id", "@palette:restaurant:batch_feature:preptime:r_id", "@palette:restaurant:property:lat:r_id", "@palette:restaurant:property:log:r_id", ])

По широте и долготе через estimator можно получить ID региона ресторана.

es_dsl1 = DSLEstimator(lambdas=[ ["region_id", "regionId(@palette:restaurant:property:lat:r_id, @palette:restaurant:property:log:r_id)"] ])

По ID региона можно получить признаки того, насколько регион загружен.

tx_p2 = PaletteTransformer([ "@palette:region:service_feature:nBusy:region_id" ])

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

es_dsl2 = DSLEstimator(lambdas=[ ["prepTime": nFill(nVal("@palette:restaurant:batch_feature:prepTime:r_id"), avg("@palette:restaurant:batch_featuer:prepTime:r_id"))], ["nMeal": nVal("@palette:restaurant:realtime_feature:nMean:r_id")], ["nOrder": nVal("@basis:nOrder")], ["nBusy": nVal("@palette:region:service_feature:nBusy:region_id")] ])

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

Автопилот: автоматизация всего, что можно

На вершине — потребности автопилота. Если их не закрыть, data scientists будут тратить время на рутинную ручную работу — это не блокер для ML в продакшене, но может уменьшить трудозатраты на разработку и операционные расходы. Хотя некоторые компании рассказывали, как закрывают эти потребности, в индустрии это (пока) не сильно распространено.

Airbnb заметили, что backfill — узкое место, когда их data scientists итеративно ставят ML-эксперименты. Поэтому Zipline включает функцию автоматического backfill признаков. Предусмотрен простой UI, где data scientists могут определить новые признаки, указать даты начала и конца, а также сколько процессов параллелить под backfill. Эти признаки затем добавляются в существующий обучающий набор через Airflow-пайплайн.

UI Airbnb для backfill и Airflow DAG, который он создаёт (источник)

Другие способы закрыть потребности автопилота:

Metacat от Netflix даёт метрики для анализа стоимости и занимаемого места. Это упрощает управление таблицами признаков (и удаление неиспользуемых), экономя расходы. Data Quality Monitor от Uber с автоматическим обнаружением аномалий и уведомлениями по метрикам качества данных, а также ежедневными оценками качества. Эксперименты Uber с автоматическим отбором признаков. Идея — позволить пользователям искать признаки по меткам, которые они пытаются предсказать. Palette затем предложит признаки, которые коррелируют с метками или влияют на них.

Заключение: всё зависит от того, что вам нужно

Надеюсь, теперь понятнее, что такое feature store. Если мы начинаем с нуля, возможно, всё, что нам нужно — это доступ и serving, плюс щепотка целостности. Если строим своё в big tech, то удобство и автопилот стоит учитывать достаточно рано. Что-то упустил? Пишите @eugeneyan!

Хотите попробовать feature store? Feast — отличный вариант. Он закрывает потребности доступа и serving, а его согласованный API позволяет использовать похожий код для обучения и serving. И самое главное — он open-source (читай: бесплатный). Дайте знать, как пойдёт!

Что я узнал о feature stores как иерархии потребностей• Доступ: информация, данные• Serving: реальное время в проде• Целостность: корректные train/serve данные• Удобство: легко и быстро использовать• Автопилот: автоматизировать всё подрядЧто-то упустил? https://t.co/gkax6cXhe4— Eugene Yan (@eugeneyan) 24 февраля 2021

Источники

Спасибо Yang Xinyi, Willem Pienaar, David Golden, Shawn Wang и Sky Chin за чтение черновиков.

Если статья показалась полезной, пожалуйста, цитируйте её так:

Yan, Ziyou. (Feb 2021). Feature Stores: A Hierarchy of Needs. eugeneyan.com. https://eugeneyan.com/writing/feature-stores/.

или

@article{yan2021feature, title = {Feature Stores: A Hierarchy of Needs}, author = {Yan, Ziyou}, journal = {eugeneyan.com}, year = {2021}, month = {Feb}, url = {https://eugeneyan.com/writing/feature-stores/} }



К 11 800+ читателям присоединяйтесь и получайте обновления по machine learning, RecSys, LLM и инженерии.