После 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 (getargspec → getfullargspec, 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 — серия патчей
Серия проблем подряд:
AMASS dataset не staged: HUGS animator грузит./data/SFU/0008/0008_ChaCha001_poses.npz(registration-only AMASS subset). Закомментировалtrainer.animate()иself.anim_dataset = Noneчтобы получить хотя бы validate-фазу.- 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ко всем. hasattr(self.human_gs, 'betas')False в eval-mode: код шёл в fallbackself.train_dataset.betas[0], но в eval-режимеtrain_datasetне создаётся. Плюс уNeumanDatasetнет атрибутаbetas— естьsmpl_params["betas"]. Патч:self.val_dataset.smpl_params["betas"][0].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’а на нашем сервере, и его можно интерактивно смотреть в браузере.
Что дальше
- Доделать HUGS animator — отдельная задача, hours, требует разобраться с trimlp forward-pass + skinning + camera params. Всё локальное, никаких новых deps.
- Альтернатива — train HUGS на новой scene с нуля — pretrained чекпоинты есть только для NeuMan-6, для своего видео нужно train. ~10–30 минут на 5090 по их README.
- 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