VK Cloud

S3 для ML и Big Data: где хранить датасеты, фичи и веса моделей

10 августа 2026 г.
268267_007n7ek.jpg
Евгений Левашов
Автор статьи
_blog_head_121.png

Хранение данных для машинного обучения деградирует довольно незаметно. Обучение идёт, метрики растут, а через три месяца выясняется, что датасет v3 у двух дата-сайентистов различается на пару тысяч примеров, лучший чекпоинт лежит в /home/user/experiments на ноутбуке, который отдали в ремонт, и модель, ушедшую в продакшен, воспроизвести уже нечем.

Объектное хранилище закрывает эту дыру не за счёт того, что места больше. Количество объектов в бакете VK Object Storage не ограничено, объект грузится обычным запросом до 32 ГБ и составной загрузкой до 320 ТБ, доступ к любому объекту идёт по HTTP параллельно из любого числа обучающих воркеров, а инструменты ML-стека настраиваются на произвольный S3-эндпоинт. Российские команды уже собирают вокруг S3 общий слой хранения: датасеты лежат в бакете, оттуда их читает Spark и туда же пишет Trino.

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

Елизавета Белоконова2.png

Статья подготовлена вместе с экспертом

Елизавета Белоконова, менеджер продукта

Почему S3 стал стандартом для ML-данных

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

  • Объём. Датасет компьютерного зрения растёт от гигабайтов до сотен терабайт, и вместе с ним растут производные: распакованные копии, аугментации, кэш препроцессинга. Диск приходится выделять с запасом, который простаивает.
  • Характер доступа. Обучение читает большими последовательными кусками, разметка и инференс ходят точечно за отдельными объектами, мониторинг качества выгребает срезы за период. Один том под три профиля нагрузки подобрать сложно.
  • Форматы. Parquet для табличных выборок, HDF5 для многомерных массивов, .json для текстовых корпусов, .pt и .safetensors для весов. Это разные размеры объектов, разные сроки жизни и разные требования к скорости чтения.
  • Привязка к машине. Диск выделяется фиксированного размера и живёт вместе с одной ВМ. Как только обучающих воркеров становится больше одного, данные начинают копировать, а копии расходятся — именно так исчезает ответ на вопрос, на каких ровно данных обучалась модель v7.

Разница между дисками и объектным хранилищем:

Что требуется ML-контуру Диск виртуальной машины Объектное хранилище
Ёмкость выделяется заранее, фиксированным томом количество объектов в бакете не ограничено
Доступ одна машина, файловая система запрос по HTTP параллельно из многих воркеров
Размер одного объекта ограничен размером тома до 32 ГБ обычной загрузкой, до 320 ТБ составной
Неактуальные версии чистятся вручную удаляются по правилу жизненного цикла
Известные границы нагрузки зависят от типа диска 1 000 запросов в секунду, листинг 250 в секунду

Дело не только в объёме. Google в руководстве по MLOps выносит управление данными и моделями (data and model management) в отдельный процесс жизненного цикла — наравне с обучением и развёртыванием. То есть хранение данных для машинного обучения проектируют как отдельную подсистему, а не выбирают, куда положить файлы.

S3 для ML: три ключевых преимущества

Ёмкость не планируется заранее. Количество объектов в бакете не ограничено, а рамки известны заранее: 100 бакетов на проект, 1 000 запросов в секунду и отдельный лимит на листинг в 250 запросов в секунду. Операции тарифицируются по факту использования.

Параллельный доступ по HTTP. Адресация плоская: объект достаётся по ключу, без обхода дерева каталогов. Несколько GPU-воркеров читают один и тот же префикс одновременно, локальная копия датасета на каждой машине перестаёт быть обязательной.

Экосистема поверх S3 API. DVC, MLflow и Feast работают с S3-совместимым хранилищем как со стандартом де-факто, поэтому переезд между провайдерами сводится к смене эндпоинта и ключей. Обратная сторона — клиенты по умолчанию смотрят в сторону Amazon: для AWS CLI параметр --endpoint-url обязателен именно поэтому.

Структура бакетов: где хранить датасеты, фичи и веса

Ключ объекта состоит из необязательных префиксов и имени, разделитель между ними — символ /. Файловый менеджер рисует по этому разделителю дерево, но физической иерархии за ним нет: photos/myphoto1.jpg выглядит как файл в каталоге photos/, а на деле это один объект с длинным ключом. Отсюда важный вывод: раскладка в S3 — соглашение об именовании, и меняется оно только перезаливкой объектов, а не переименованием каталога.

Рабочая схема для ML-проекта:

text

ml-raw-data/ # неизменяемые исходники, только запись и чтение └── vision/retail-shelves/v3/2026-05-12/ ├── images/000001.jpg └── manifest.jsonl ml-processed/ # готовые выборки под обучение └── retail-shelves/v3/ ├── train/part-0000.parquet ├── val/part-0000.parquet └── test/part-0000.parquet ml-features/ # рассчитанные признаки └── retail/user_purchase_30d/dt=2026-07-22/part-0000.parquet ml-artifacts/ # веса, чекпоинты, метрики └── detector/model-v2/2026-07-22/commit-abc1234/ ├── weights.safetensors └── metrics.json ml-experiments/ # логи и конфиги прогонов └── detector/2026-07-22/run-8f31/ ├── config.yaml └── train.log

Пять верхних имён — это либо пять бакетов, либо пять префиксов одного бакета. Граница проходит по двум признакам. Первый — права доступа: разрешения выдаются на бакет и на объект, и разнести исходники «только чтение» и артефакты «запись из обучения» обычно проще бакетами. Второй — правила жизненного цикла: фильтр правила работает по одному префиксу ключа на правило (плюс по тегам), а самих правил в бакете не больше 50. Против чрезмерного дробления работает лимит в 100 бакетов на проект: бакет на каждый эксперимент упирается в него очень быстро.

Принципы нейминга объектов для воспроизводимости

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

  • версия датасета или модели (v3, model-v2);
  • дата в формате YYYY-MM-DD, а не 22.07.26;
  • хэш коммита кода, которым артефакт получен (commit-abc1234);
  • имя эксперимента или прогона для всего, что живёт под ml-experiments/.

Собранный ключ выглядит так:

text

ml-artifacts/detector/model-v2/2026-07-22/commit-abc1234/weights.safetensors

Три типичные ошибки:

  • Префикс с датой на первом уровне (2026-07-22/detector/...). Правило жизненного цикла и права доступа приходится заводить на каждую дату, потому что тип артефакта оказывается ниже по ключу.
  • Пробелы, кириллица и заглавные буквы в ключах. Ключ уезжает в командную строку, в конфиги и в URL, и кавычки с экранированием всплывают в самый неподходящий момент.
  • Бакет на эксперимент. Схема выглядит аккуратно ровно до сотого эксперимента, дальше упирается в лимит бакетов, а сводная выгрузка требует обхода всех бакетов подряд.

Схема именования связана ещё с одним решением, о котором обычно вспоминают поздно: класс хранения. В Object Storage он указывается при загрузке объекта (STANDARD для класса Hotbox, STANDARD_IA для Icebox) либо наследуется от бакета, если не указан явно. «Горячий» и «холодный» контуры разводятся на этапе проектирования префиксов, а не задним числом.

DVC: версионирование данных и моделей поверх S3

Git не тянет бинарники в десятки гигабайт, поэтому датасет в репозиторий не кладут. DVC (Data Version Control) решает эту проблему разделением: в Git уезжает небольшой метафайл .dvc с хэшем содержимого, сами данные — в объектное хранилище. Рабочий цикл короткий: dvc add считает хэш и создаёт метафайл, git add и git commit фиксируют его вместе с кодом, dvc push заливает данные в удалённое хранилище.

В результате коммит кода и состояние датасета связаны одним хэшем. Переключились на ветку трёхмесячной давности, выполнили dvc pull и получили ровно те файлы, на которых обучалась модель того периода. Без этого связка «код в Git, данные где-то в бакете» держится на дисциплине и комментариях в чате.

Проект активен: последний релиз на PyPI — 3.67.1 от 31 марта 2026 года. Релизы выходят нерегулярно, интервалы между ними доходят до нескольких месяцев, поэтому пиновать конкретный номер версии в командах не стоит.

Настройка DVC для S3 в VK Cloud

DVC работает с любым S3-совместимым хранилищем: адрес задаётся параметром endpointurl, остальные параметры S3 тоже принимаются, а их применимость зависит от платформы хранения. Для Object Storage в московском регионе эндпоинт — https://hb.ru-msk.vkcloud-storage.ru, код региона ru-msk; есть равнозначная короткая форма https://hb.vkcloud-storage.ru.

bash

# удалённое хранилище по умолчанию dvc remote add -d vkcloud s3://ml-raw-data/dvcstore dvc remote modify vkcloud endpointurl https://hb.ru-msk.vkcloud-storage.ru # ключи доступа: флаг --local пишет их в .dvc/config.local, # который игнорируется Git и не уезжает в репозиторий dvc remote modify --local vkcloud access_key_id <ACCESS_KEY> dvc remote modify --local vkcloud secret_access_key <SECRET_KEY>

Флаг --local здесь важен: документация DVC прямо рекомендует его, чтобы чувствительные данные попали в игнорируемый Git файл .dvc/config.local, а не утекли через историю репозитория. Если хранилище отдаёт временные учётные данные, для них есть параметр session_token.

Дальше цикл повторяется на каждой итерации датасета:

bash

dvc add data/retail-shelves/v3 git add data/retail-shelves/v3.dvc data/.gitignore git commit -m "Датасет retail-shelves v3" dvc push # на другой машине или в CI git checkout <commit> dvc pull

Проверять связку проще всего на пустом каталоге: пара dvc push и dvc pull, и если после dvc pull файлы не появились, причина почти всегда в эндпоинте или ключах, а не в самом DVC.

Границу инструмента стоит держать в голове с первого дня. DVC отвечает за соответствие коммита кода состоянию датасета и за файлы в удалённом хранилище. Он не ведёт историю метрик по прогонам, не сравнивает эксперименты в интерфейсе и не хранит реестр моделей со стадиями — это территория MLflow. Попытка вести учёт экспериментов через ветки и теги Git предсказуемо упирается в то, что сравнить двадцать прогонов по трём метрикам через git log нельзя.

Feature store: централизованное хранение фичей

По определению самого проекта Feast, feature store — это слой, который решает три задачи. Первая — training-serving skew, то есть расхождение между признаками, на которых модель обучалась, и признаками, которые она получает в продакшене; feature store даёт одинаковый доступ к ним и для обучения, и для низколатентного serving. Вторая — переиспользование признаков через единый слой доступа, который отделяет способ хранения фич от способа их извлечения. Третья — point-in-time correctness, то есть сборка обучающих выборок, корректных на момент времени, без подглядывания в будущее.

Конструктивно это две половины: offline store с исторической выборкой для обучения и online store для низколатентной подачи, плюс реестр, feature server, веб-интерфейс и CLI.

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

Границы проект очерчивает сам. Feast прямым списком пишет, чем feature store не является: не ETL- и не ELT-система, не оркестратор данных, не замена корпоративного хранилища, не инструмент версионирования датасетов и управления экспериментами, не система полного захвата lineage.

Feast + S3: offline feature store

Offline store хранит исторические значения признаков файлами в S3, обычно в Parquet, и по ним собирается обучающая выборка с корректностью на момент времени. Конфигурация проекта живёт в feature_store.yaml:

text

project: retail registry: s3://ml-features/registry.db provider: local offline_store: type: file

Значения registry: s3://… и offline_store.type: file совпадают с документацией Feast и её примерами. Путь к самим Parquet-файлам задаётся не здесь, а в источнике FileSource; для S3-совместимого хранилища туда же передаётся s3_endpoint_override. Версии проекта меняются часто: релиз v0.65.0 вышел 20.07.2026, предыдущий, v0.64.0, — 13.06.2026.

Практический вывод для небольшой команды: отдельный слой признаков окупается там, где есть онлайн-инференс и несколько потребителей одних и тех же фич. Если модель переобучается раз в месяц и отдаёт предсказания батчем, роль offline store спокойно играет тот же бакет с Parquet-выборками, а версионирование закрывает DVC.

Хранение данных для машинного обучения выигрывает от соседства с вычислениями: датасеты, признаки и веса — в Object Storage, обучение — на Cloud GPU в том же контуре, без выгрузки терабайтов между площадками. Характеристики сервиса, лимиты и условия подключения собраны на странице Object Storage.

MLflow + S3: управление весами и артефактами

MLflow разделяет хранение на две части: параметры, метрики и метаданные прогонов идут в базу данных tracking-сервера, артефакты (веса, графики, отчёты, конфиги) уезжают в отдельное хранилище. Чтобы артефакты MLflow писались не в Amazon, а в стороннее S3-совместимое хранилище, задаётся переменная окружения MLFLOW_S3_ENDPOINT_URL с адресом эндпоинта. Ключи доступа передаются стандартными переменными.

bash

export MLFLOW_S3_ENDPOINT_URL=https://hb.ru-msk.vkcloud-storage.ru export AWS_ACCESS_KEY_ID=<ACCESS_KEY> export AWS_SECRET_ACCESS_KEY=<SECRET_KEY>

python

import mlflow mlflow.set_tracking_uri("http://mlflow.internal:5000") mlflow.set_experiment("retail-shelves-detector") with mlflow.start_run(run_name="run-8f31"): mlflow.log_param("lr", 3e-4) mlflow.log_metric("map50", 0.71) mlflow.log_artifact("weights.safetensors", artifact_path="model")

Поднимать сервер самостоятельно не обязательно: MLflow есть в ML Platform VK Cloud как отдельный сервис — инстанс создаётся в личном кабинете и работает либо в связке с JupyterHub, либо в режиме Standalone. Для развёртывания моделей в JupyterHub предустановлена python-библиотека MLflow Deployment Client.

Одна ловушка ждёт тех, кто переезжает на третью мажорную версию. В MLflow 3 убраны MLflow Recipes и часть флейворов, появился отдельный объект LoggedModel со своими метриками и параметрами, а артефакты моделей больше не показываются на вкладке Artifacts страницы прогона: они переехали на страницу Logged Models. Жалоба «после обновления пропали веса» в большинстве случаев означает, что интерфейс смотрят не на той странице — объекты в бакете на месте.

safetensors vs pickle для весов моделей

Формат весов — вопрос не удобства, а доверия к содержимому бакета. Чекпоинт в pickle-формате, которым PyTorch сохраняет веса по умолчанию, способен исполнить произвольный код при загрузке. С версии PyTorch 2.6 torch.load идёт с weights_only=True по умолчанию и ограничивает распаковку тензорами, примитивами, словарями и явно разрешёнными типами. Дыра остаётся на практике: старые чекпоинты в безопасном режиме часто не читаются, загрузку повторяют с weights_only=False — и чужой файл из общего хранилища выполняет код в вашем окружении.

safetensors закрывает именно эту часть. Формат хранит сырые тензоры и небольшой JSON-заголовок, встроить исполняемый код в файл модели нельзя, отдельные тензоры читаются по имени, без загрузки всего чекпоинта в память, на Hugging Face Hub формат используется по умолчанию. Загрузка быстрее за счёт чтения тензоров по имени и отсутствия распаковки объектов Python. Публичных замеров с воспроизводимой методикой у формата нет, поэтому закладывать конкретный коэффициент ускорения в расчёты не стоит.

8 апреля 2026 года safetensors стал проектом PyTorch Foundation: Hugging Face передала формат фонду, где уже размещены PyTorch, vLLM, DeepSpeed, Ray и Helion. Для команд это сигнал, что безопасный формат весов перестал быть предпочтением отдельного вендора.

Границы у решения тоже есть. safetensors хранит тензоры, а не произвольные объекты Python, поэтому состояние оптимизатора, кастомные классы и часть пайплайнов всё равно сериализуются иначе; такие файлы стоит держать под отдельным префиксом с более узкими правами. И ещё: формат отвечает за отсутствие исполняемого кода, но не за подлинность. За вопрос «кто положил этот чекпоинт в бакет» отвечают права доступа и версионирование, а не расширение файла.

Lifecycle rules: экономия на хранении экспериментов

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

Простая арифметика показывает масштаб: 100 экспериментов, по 10 сохранённых чекпоинтов в каждом, по 1 ГБ на чекпоинт дают 1 ТБ, целиком состоящий из промежуточных состояний. Цифра иллюстративная, но порядок обычно такой же.

Отдельная статья роста — версии объектов. В бакете с включённым версионированием перезапись объекта не удаляет предыдущее состояние, а добавляет новое, и «очищенный» префикс продолжает занимать место старыми версиями.

Стратегия retention для ML-данных

Жизненный цикл в Object Storage делает ровно одно: автоматизированно удаляет объекты из бакета по заданным правилам. Настраиваются ID, Status, Filter, Expiration (варианты Days, ExpiredObjectDeleteMarker) и NoncurrentVersionExpiration для бакетов с версионированием; фильтр работает по префиксу ключа (один префикс на правило) и по тегам, которые объединяются оператором And. Правила применяются ежедневно и действуют как на объекты, добавленные в бакет с уже настроенным жизненным циклом, так и на объекты бакета, когда правило появилось позже.

Переноса объекта из класса в класс среди этих операций нет. Класс задаётся при загрузке или наследуется от бакета, поэтому «переложить в холодный класс» — это отдельное действие команды: объект копируется на себя с новым классом,

bash

aws s3 cp s3://<БАКЕТ>/<КЛЮЧ> s3://<БАКЕТ>/ --storage-class STANDARD_IA

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

Ориентир по срокам для типового ML-проекта:

Тип данных Срок хранения Что происходит дальше Чем реализуется
Сырые данные (ml-raw-data/) бессрочно остаются источником истины правил жизненного цикла нет
Обработанные выборки (ml-processed/) 6 месяцев в горячем классе перенос в холодный класс Icebox копирование объектов с классом STANDARD_IA
Промежуточные чекпоинты (ml-artifacts/*/checkpoints/) 30 дней удаление Expiration с параметром Days
Финальные веса продакшен-моделей бессрочно остаются вместе с метриками правил жизненного цикла нет
Логи экспериментов (ml-experiments/) 90 дней перенос в холодный класс либо удаление Expiration или ручной перенос
Неактуальные версии объектов 30 дней удаление старых версий NoncurrentVersionExpiration

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

С агрессивным TTL (time to live, срок жизни объекта) стоит быть осторожнее: удалённый чекпоинт означает, что дообучить модель с той же точки уже не выйдет. Разумный компромисс: чекпоинты промежуточных эпох чистятся по сроку, финальные веса и метрики живут вместе с кодом, а страховкой от случайного удаления служит версионирование бакета. Если объектное хранилище используется ещё и как цель для резервных копий, правила лучше развести по бакетам, чтобы retention ML-контура не задел бэкапы (про их отдельный контур — в разборе резервного копирования в облако).

Заключение

Проектирование сводится к четырём решениям, принятым до первой заливки данных. Схему префиксов продумывают первой: переименовать ключ задним числом означает перезалить объекты. Датасеты версионирует DVC, артефакты и веса ведёт MLflow, отдельный слой признаков подключают под онлайн-инференс, а не «на всякий случай». Веса складывают в safetensors. Счёт держат правила жизненного цикла, которые в Object Storage удаляют объекты по сроку, а смену класса хранения команда делает явной операцией. Собранное так хранение данных для машинного обучения перестаёт быть источником сюрпризов и становится обычной инженерной подсистемой с понятными границами.

Частые вопросы про хранение данных для машинного обучения

Зачем S3 вместо обычного диска для ML-данных?

Хранение данных для машинного обучения на диске виртуальной машины упирается в две вещи сразу. Диск выделяется фиксированного размера и привязан к одной машине, поэтому при нескольких обучающих воркерах датасет начинают копировать, а копии расходятся. В объектном хранилище количество объектов в бакете не ограничено, обычной загрузкой проходит объект до 32 ГБ, составной — до 320 ТБ, а доступ идёт параллельно по HTTP. Плюс инструменты ML-стека умеют писать туда напрямую, без промежуточных копий на локальных дисках.

Как хранить веса ML-модели в S3?

В формате safetensors, под ключом, который однозначно связывает веса с кодом и датасетом: например, ml-artifacts/detector/model-v2/2026-07-22/commit-abc1234/weights.safetensors. safetensors хранит сырые тензоры и JSON-заголовок, не исполняет код при загрузке и отдаёт отдельные тензоры по имени; в апреле 2026 года формат перешёл под управление PyTorch Foundation. Заливать веса удобнее через MLflow: он пишет артефакты в произвольное S3-совместимое хранилище, адрес которого задаётся переменной MLFLOW_S3_ENDPOINT_URL.

Что такое feature store и нужен ли он небольшой команде?

Feature store — слой доступа к признакам, который решает три задачи: расхождение признаков между обучением и продакшеном, переиспользование фич между командами и корректность выборки на момент времени; состоит из offline store и online store. Небольшой команде без онлайн-инференса он чаще не нужен: версионирование датасетов закрывает DVC, эксперименты — MLflow, а роль offline store играют Parquet-выборки в бакете. Проект Feast прямо пишет, что версионированием датасетов и управлением экспериментами он не занимается, поэтому подменять им эти слои не получится.

Оставьте заявку, чтобы получить консультацию

Наши специалисты свяжутся с вами в ближайшее время и ответят на все вопросы.

section-subscribe_2x.png

            Узнавайте о выходе новых статей в блоге первыми!

            Будем держать в курсе новостей и облачных трендов

            section-subscribe_2x.png
              section-subscribe_2x.png
              Теги: S3, big data, ML
              Ссылка скопирована
              Поделиться

              Почитать по теме

              _blog_head_172.png
              18 августа

              Как организовать хранение медицинских данных для ИИ с учетом требований российского законодательства

              _blog_head_131.png
              13 августа

              Объектное, файловое и блочное хранилище: в чём разница и что выбрать под задачу

              blog_head_39_69.png
              13 августа

              Горячее, холодное и архивное хранение в S3: классы и автоматизация lifecycle

              40+ готовых сервисов