Як робот вчиться на власних демонстраціях, не завантажуючи терабайти

AWS і Hugging Face з'єднали єдиний цикл агента: він записує епізоди роботи маніпулятора, кладе їх у змінне сховище, передає фрагменти безпосередньо на GPU і повертає отриману контрольну точку назад на ту саму руку. Датасет ніколи не виходить за формат LeRobot і не покидає Hub, тому добу збору коштує лише байти, що дійсно змінилися.

Роботизована рука піднімає червоний куб. Камери спостерігають, суглоби рухаються, і кожен кадр потрапляє до каталогу з фрагментами Parquet і MP4.

Цей каталог — датасет LeRobot. На Hub вже перебуває понад 90 000 таких наборів від більше ніж 8 000 видавців. Новим є те, що відбувається далі: ті самі фрагменти синхронізуються у сховище Hugging Face Storage Bucket — змінний, без версійного контролю, об'єктний сховище на основі Xet, оголошене у березні 2026 року. Сховище лежить поруч з вашими репозиторіями датасетів у тому ж просторі імен hf:// і використовує той самий CLI hf, який у вас вже є.

Сховище — робочий шар, а не архів

Версійні репозиторії перетворюють кожне дописування на фіксацію і зберігають усі ревізії. Збір даних потребує протилежного: місця, куди можна записати байти і перезаписати їх на місці. Сховища роблять саме те. Потрібно налаштувати жодних IAM-ролей. Жодних правил CORS. Жодного сервісу завантаження, що вимагає обслуговування. Ваш агент записує LeRobotDataset у тому ж форматі, що й LeRobot на «залізі», а потім викликає sync_dataset_to_bucket("/tmp/cube_pick", "my-org/robot-fave"). Синхронізація пише у hf://buckets/{bucket}/{run_id}, де run_id за замовчуванням дорівнює назві каталогу датасету. Та сама можливість є в DatasetRecorder.sync_to_bucket і stop_recording(bucket=...), якщо ви керуєте відкритим рекордером безпосередньо.

Для публікації артефакту ви все ще викликаєте push_to_hub(). Обидва тримають однаковий формат. Сховище — це шар, у який ви пишете протягом дня; версійний репозиторій отримує лише ті версії, які ви вирішили опублікувати.

Вилучення дублікатів на рівні байтів змінює економіку

Направте дві фіксовані камери на руку, що очищує один і той самий стіл вісім годин. Більшість записаного — пікселі, які у вас вже є: однакове освітлення, однаковий корпус, однаковий фон через тисячі епізодів. У версійному репозиторії зміна одного кадру у відеофрагменті на кілька гігабайт змушує завантажувати файл повністю.

Сховища базуються на Xet, який вилучає дублікати при завантаженні на рівні байтів за допомогою розбиття за вмістом. Межі частин слідують за даними, тому вставка кількох байтів змінює лише ту частину, куди вони потрапили, а не зсуває всі межі після неї. За власними вимірюваннями Hugging Face, таке розбиття зменшує обсяг переданих даних при завантаженні приблизно в чотири рази по всьому Hub. На Enterprise-планах оплата йде за обсяг після вилучення дублікатів.

хні тести сховищ показують це на прикладі одного файлу. Початкове завантаження 500 МБ. Зміна 1 % байтів і повторне завантаження перемістило 5,5 МБ. Зміна 5 % — 27,5 МБ. Зміна 10 % — 55 МБ. Без частинного вилучення дублікатів перезапис об'єкта означає відправку всіх його байтів знову, незалежно від того, чи змінилися вони.

Рекордер Strands Robots використовує розкладку фрагментів LeRobot. Епізоди йдуть у фрагменти Parquet (data/chunk-000/file-000.parquet) і per-камерні фрагменти MP4 (videos/observation.images.front/chunk-000/file-000.mp4), переходячи до нового файлу лише тоді, коли поточний наповнюється за замовчуваннями LeRobot: 100 МБ для даних Parquet і 200 МБ для відео MP4. Синхронізація після дня запису завантажує лише нові хвостові фрагменти плюс один частково заповнений фрагмент, що зріс, а не весь датасет. Синхронізуйте те саме сховище завтра — Xet сам подбає про вилучення дублікатів.

Потокове читання перетворює датасет на torch-ітерований об'єкт

Для навчання ви вказуєте GPU на свій датасет. Спочатку завантажте його — і ці GPU будуть простаївати, поки не скопіюються сотні гігабайт. Потокове читання безпосередньо з Hub працює завдяки розкладці фрагментів: пакет стає кількома діапазонами байтів над великими фрагментами, а не тисячами дрібних запитів.

StreamingLeRobotDataset LeRobot перетворює це на готовий до використання torch-ітерований об'єкт. Strands Robots надає його через stream_dataset():

reader = sim.stream_dataset(
    "my-org/robot-fave/cube_pick",
    repo_type="bucket",
    shuffle=False,
    max_num_shards=1,
    buffer_size=1
)
print(reader.num_episodes, reader.num_frames, reader.fps)
for frame in reader:
    frame["observation.images.front"]  # (3, H, W) тензор, декодований на льоту
    frame["observation.state"]         # вектор суглобів з Parquet-фрагмента
    frame["action"]
    break

На локальний диск потрапляє лише невелика тека meta/ зі схемою, статистикою та індексом епізодів. Кадри камер декодуються з віддалених MP4-фрагментів під час ітерації; стан і дія приходять з Parquet-фрагментів. Цей цикл читає по одному кадру, що підходить для перегляду епізоду. Щоб навчати, передайте ридер у DataLoader і ітеруйте пакети. Потоковий датасет перемішує внутрішньо через обмежений буферний резервуар, тому декодування відео паралелізується між робочими процесами.

Якщо ви не хочете писати цикл взагалі, власний тренер LeRobot

Джерела, за якими написано статтю

  • "Hugging Face blog
    https://huggingface.co/blog/amazon/strands-lerobot-streaming-data-loop"

Читайте також