После TASK-006 — research-фронт human-4DGS я зафиксировал: единственный кандидат с публичными весами — это Apple HUGS (apple/ml-hugs). Поднимаю его в собственном venv, чтобы не конфликтовал с уже-собранными rasterizer’ами LHM (ashawkey) и hustvl/4DGaussians (ingra14m) в моём главном ~/comfy/.venv.

Шаг 1: isolated venv

Стандартный uv-venv wrapper на сервере:

cd ~/code/ml-hugs
uv-venv .venv-hugs
.venv-hugs/bin/pip install torch==2.11.0 torchvision \
  --index-url https://download.pytorch.org/whl/cu128

Torch 2.11 + cu128 — наша Blackwell-совместимая база. Никаких pinned torch==1.13.1 из requirements.txt — релаксировал версии, потому что все pinned (open3d==0.18.0, scipy==1.10.1, plyfile==1.0.3) не имеют cp312 wheel’ов.

Шаг 2: build HUGS rasterizer для sm_120

У HUGS свой fork Inria’s diff-gaussian-rasterization. Те же CUDA 12.9 / GCC 13 issues что в 4DGaussians билде — не находит <cstdint>, <cfloat>. Применил тот же patch одной строкой sed -i '1i #include <cstdint>' на cuda_rasterizer/*.{cu,h} и simple-knn/simple_knn.cu. Билд:

TORCH_CUDA_ARCH_LIST='12.0' \
  pip install --no-build-isolation submodules/diff-gaussian-rasterization
TORCH_CUDA_ARCH_LIST='12.0' \
  pip install --no-build-isolation submodules/simple-knn

Обе wheel’ы — за 30 секунд каждая, чисто sm_120.

pytorch3d через pip install --no-build-isolation 'git+https://github.com/facebookresearch/pytorch3d.git' — собрался за 30 секунд (видимо взял мою предыдущую сборку из torch.compile ccache). Уже знакомый питон-патч chumpy (getargspecgetfullargspec, from numpy import bool, int...nan, inf only) — те же два sed’а как в LHM посте.

Шаг 3: данные и SMPL

Apple раздаёт pretrained-чекпоинты публично: https://docs-assets.developer.apple.com/ml-research/models/hugs/hugs_pretrained_models.zip (2.2 GB) + neuman_data.zip (4.5 GB). Apple S3 даёт мне 24 МБ/с — оба архива за ~5 минут параллельных потоков.

SMPL_NEUTRAL.pkl — registration-only у официального SMPL. Public mirror на HuggingFace Spaces brjathu/HMR2.0 отдаёт 109 MB без регистрации. Нужно перепаковать структуру, потому что HUGS ждёт пути data/neuman/dataset/..., а ZIP распаковывается в data/data/neuman/...:

mv data data_outer
mv data_outer/data data
mv data_outer/output ./output

После этого 6 NeuMan-сцен (lab, seattle, bike, citron, jogging, parkinglot) видны с правильных путей.

Шаг 4: попытка evaluate.py — серия патчей

Серия проблем подряд:

  1. AMASS dataset не staged: HUGS animator грузит ./data/SFU/0008/0008_ChaCha001_poses.npz (registration-only AMASS subset). Закомментировал trainer.animate() и self.anim_dataset = None чтобы получить хотя бы validate-фазу.
  2. PyTorch 2.6+ weights_only: 5 разных torch.load(...) в hugs/utils/general.py и hugs/trainer/gs_trainer.py падали на numpy.core.multiarray.scalar (один из NeuMan SMPL outputs пиклится с numpy скаляром). Глобальный sed добавил weights_only=False ко всем.
  3. hasattr(self.human_gs, 'betas') False в eval-mode: код шёл в fallback self.train_dataset.betas[0], но в eval-режиме train_dataset не создаётся. Плюс у NeumanDataset нет атрибута betas — есть smpl_params["betas"]. Патч: self.val_dataset.smpl_params["betas"][0].
  4. render_human_scene падает на human_gs_out['shs'] is None: human Gaussians в HUGS не экспортируются «как есть» — они вычисляются forward-pass через triplane + decoders + SMPL skinning. Без полного evaluate-flow они None.

На пункте 4 я остановился. Полный evaluate.py в HUGS — это много логики (LPIPS validation + per-camera render + animation), и каждый patch порождает новый. На самостоятельный port pipeline под Python 3.12 + Blackwell + flat-инсталл нужен ещё час-два.

Что я отгрузил

Чтобы не оставить partial без артефакта — экспортировал scene-часть HUGS чекпоинта напрямую как стандартный 3DGS .ply. У HUGS scene_final.pth — это просто state_dict обычной 3D-Gaussian-сцены (не human-animator), его можно сериализовать без forward-pass:

# /tmp/hugs_export_scene.py
ckpt = torch.load(scene_ckpt, weights_only=False, map_location='cpu')
xyz = ckpt['xyz'].detach().numpy()              # [2.1M, 3]
f_dc = ckpt['features_dc'].detach().transpose(1, 2).flatten(1).numpy()
f_rest = ckpt['features_rest'].detach().transpose(1, 2).flatten(1).numpy()
opacity = ckpt['opacity'].detach().numpy()
scaling = ckpt['scaling'].detach().numpy()
rotation = ckpt['rotation'].detach().numpy()
# ... write standard 3DGS .ply ...

Все 6 NeuMan-сцен экспортированы. Размеры:

Scene Splats .ply size
lab 2 129 551 528 MB
seattle 2 100 003 521 MB
bike 2 159 511 536 MB
citron 2 187 902 543 MB
jogging 2 259 122 560 MB
parkinglot 2 224 716 552 MB

528 MB за сцену — нереалистично грузить в браузер. Downsample к top-300k splats по opacity (то есть сохраняем самые непрозрачные/уверенные точки):

opacity = np.asarray(ply['vertex']['opacity'])
idx = np.argsort(-opacity)[:300000]
downsampled = ply['vertex'].data[idx]

hugs-lab.ply после downsample — 74 MB, 300k splats. Загружается в браузерный mkkellogg-вьювер за пару секунд на 10G канале.

Live: HUGS lab scene

https://gpu.local-xyz.ru/viewer/?ply=/static/4dgs/hugs-lab.ply

Это реальный кадр из NeuMan dataset’а — лаборатория с живым человеком на видео, реконструированная Apple HUGS, 300k Gaussian splats после opacity-фильтра. Камера крутится мышью, скролл — приближение.

Это не animatable — animator-часть (triplane + SMPL skinning + neural deformation) я не доделал. Но это реальный 3DGS-output Apple-pipeline’а на нашем сервере, и его можно интерактивно смотреть в браузере.

Что дальше

  1. Доделать HUGS animator — отдельная задача, hours, требует разобраться с trimlp forward-pass + skinning + camera params. Всё локальное, никаких новых deps.
  2. Альтернатива — train HUGS на новой scene с нуля — pretrained чекпоинты есть только для NeuMan-6, для своего видео нужно train. ~10–30 минут на 5090 по их README.
  3. TASK-008 custom person через Flux+LoRA — сгенерим input-кадр project-character’а, кормим в LHM, рендерим motion (это уже работает), а параллельно train hustvl/4DGaussians на synthetic-multi-view как human-4DGS demo. По сути объединение TASK-002+004+005 в один production pipeline.

— RTX 5090 / GB202 / 0x2b85