Сравнение форматов хранения и методов сериализации в изображениях для нейросетей: анализ эффективности TFRecord, HDF5 и LMDB

При обучении моделей на датасетах от 100 ГБ и выше время простоя GPU из-за медленного ввода-вывода (I/O bound) может достигать 30-40% от общего времени итерации. Переход от хранения тысяч отдельных JPEG-файлов к бинарным форматам сериализации сокращает время загрузки данных в 5-15 раз за счет минимизации системных вызовов open() и seek().

Проблема «миллиона мелких файлов»

Хранение изображений в виде отдельных файлов в файловой системе (ext4, NTFS) создает критическую нагрузку на inode и замедляет чтение из-за фрагментации. При размере одного изображения 50-200 КБ операционная система тратит больше времени на поиск файла в дереве директорий, чем на само чтение данных. В результате throughput падает до 20-50 МБ/с даже на NVMe SSD, хотя физический лимит накопителя превышает 3000 МБ/с.

Мини-кейс: при переходе с структуры /train/class_N/image_M.jpg на единый бинарный контейнер скорость подачи данных в DataLoader PyTorch выросла с 12 до 140 итераций в секунду на датасете в 1.2 млн изображений. Это позволило сократить время эпохи с 8 часов до 45 минут.

Экспертный вывод: Любой датасет с количеством файлов более 100 000 должен быть сериализован в единый формат, иначе вы платите за аренду GPU, который простаивает в ожидании данных.

TFRecord: стандарт для линейного стриминга

TFRecord реализует последовательное хранение данных в виде бинарных строк (tf.train.Example). Основное преимущество — идеальная совместимость с tf.data.Dataset, что позволяет использовать префетчинг и параллельное чтение. Однако TFRecord лишен возможности произвольного доступа (random access): чтобы прочитать 100-й пример, нужно пропустить первые 99. Это делает его непригодным для некоторых методов активного обучения или специфического сэмплирования.

Технический нюанс: использование GZIP-сжатия внутри TFRecord снижает объем на диске на 20-30%, но увеличивает нагрузку на CPU при распаковке, что может создать новое «бутылочное горлышко» при использовании очень быстрых GPU (например, H100). Оптимальный размер одного файла .tfrecord — от 100 до 200 МБ.

Экспертный вывод: TFRecord — лучший выбор для огромных датасетов, которые читаются строго линейно (shuffled sequential), особенно в экосистеме TensorFlow.

HDF5: иерархическая структура и Random Access

HDF5 (Hierarchical Data Format) работает как «файловая система внутри файла», позволяя обращаться к любому тензору по индексу за константное время O(1). Это критично при реализации сложных стратегий сэмплирования или когда требуется частый доступ к метаданным. Однако HDF5 имеет серьезный недостаток — проблемы с многопоточным чтением (Global Interpreter Lock в Python и внутренние блокировки библиотеки), что часто ограничивает масштабируемость при использовании большого количества worker-ов в DataLoader.

Пример: в задачах медицинской сегментации (КТ/МРТ), где один объект может весить 50-100 МБ, HDF5 позволяет эффективно хранить многомерные массивы без потери структуры. При попытке использовать более 8 параллельных потоков чтения из одного .h5 файла часто наблюдается деградация производительности из-за конкуренции за доступ к файлу.

Экспертный вывод: Выбирайте HDF5 для тяжелых объектов (3D-сканы, видео) и сценариев, где необходим мгновенный доступ к произвольному элементу, но ограничьте количество worker-ов.

LMDB: максимальная скорость через Memory Mapping

LMDB (Lightning Memory-Mapped Database) — это B+ дерево, которое отображает базу данных напрямую в виртуальную память процесса (mmap). Это исключает лишнее копирование данных между ядром ОС и пользовательским пространством. Скорость чтения из LMDB в 2-4 раза выше, чем у HDF5, при сохранении возможности произвольного доступа. Это стандарт де-факто для тяжелых моделей компьютерного зрения (например, в архитектурах типа YOLO или при обучении на ImageNet).

Подводный камень: LMDB требует предварительного выдерования места (map_size). Если указать слишком малый размер, запись прервется ошибкой MDB_MAP_FULL; если слишком большой — в некоторых старых ОС это может привести к избыточному потреблению виртуальной памяти. Типичный размер map_size для датасета в 500 ГБ — около 600 ГБ.

Экспертный вывод: LMDB — самый производительный формат для Random Access. Если ваша задача — максимальный throughput при любом типе сэмплирования, используйте его.

Сравнительный анализ и метрики эффективности

Сравнение форматов показывает, что выбор зависит от баланса между скоростью чтения и гибкостью доступа. В таблице ниже приведены ориентировочные показатели для датасета из 1 млн изображений (512x512 px):

  • Raw JPEG: I/O Speed ~30-70 МБ/с, CPU Load — высокая (постоянный open/close).
  • TFRecord: I/O Speed ~400-800 МБ/с, CPU Load — низкая (линейный поток).
  • HDF5: I/O Speed ~200-500 МБ/с, CPU Load — средняя (блокировки при многопоточности).
  • LMDB: I/O Speed ~600-1200 МБ/с, CPU Load — минимальная (mmap).

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

Экспертный вывод: Для максимального ускорения обучения на GPU используйте связку LMDB + NVMe SSD, что практически полностью нивелирует задержки ввода-вывода.

Вывод

Мой вердикт: забудьте о хранении данных в виде отдельных файлов, если их больше 100к. Для линейного обучения в TensorFlow — только TFRecord. Для PyTorch и задач с произвольным доступом — однозначно LMDB, так как mmap дает недостижимый для HDF5 прирост скорости. Избегайте HDF5 в высокопараллельных пайплайнах из-за проблем с блокировками. Начинайте с LMDB: это даст самый ощутимый прирост FPS при обучении без усложнения архитектуры данных.