mossling / src /upload_hf.py
recoilme's picture
src
3467e0f
Raw History Blame Contribute Delete
6.11 kB
#!/usr/bin/env python3
"""Загрузить локальный датасет в приватный репозиторий HF (большие объёмы, с возобновлением).
Зачем по коммиту на под-датасет: 37 ГБ и 165 файлов одним коммитом — это один длинный
запрос, который падает на таймауте и начинает всё заново. Здесь каждый `sub_*` уходит своим
коммитом через `run_as_future` (4 параллельно), а повторный запуск пропускает уже
залитые файлы — Xet (hf-xet) видит их на сервере и не передаёт заново.
Приватность: `--private` (по умолчанию True) — репозиторий создаётся приватным. ВАЖНО:
если репозиторий уже существует и он публичный, этот флаг его НЕ сделает приватным —
проверьте и пересоздайте вручную.
Запуск:
python3 src/upload_hf.py --src src/work/ds_testg_filtered --repo recoilme/ds_testg_filtered
nohup python3 src/upload_hf.py ... > /tmp/upload.log 2>&1 &
"""
import argparse
import os
import sys
def main():
p = argparse.ArgumentParser(description="Залить датасет в HF как приватный")
p.add_argument("--src", required=True, help="локальная папка с датасетом")
p.add_argument("--repo", required=True, help="репозиторий, напр. recoilme/ds_testg_filtered")
p.add_argument("--private", action="store_true", default=True)
p.add_argument("--workers", type=int, default=8, help="параллельных загрузок")
args = p.parse_args()
from huggingface_hub import HfApi
if not os.path.isdir(args.src):
raise SystemExit(f"нет папки {args.src}")
n_files = sum(len(f) for _, _, f in os.walk(args.src))
size = sum(os.path.getsize(os.path.join(r, f))
for r, _, fs in os.walk(args.src) for f in fs)
print(f"заливаю {args.src}: {n_files} файлов, {size / 2**30:.1f} ГиБ -> {args.repo}",
flush=True)
api = HfApi()
api.create_repo(repo_id=args.repo, repo_type="dataset", private=args.private,
exist_ok=True)
print(f"репозиторий готов: https://huggingface.co/datasets/{args.repo}", flush=True)
# README, чтобы у датасета было описание: как собран и чем отфильтрован
readme = os.path.join(args.src, "README.md")
if not os.path.exists(readme):
with open(readme, "w", encoding="utf-8") as f:
f.write(_readme(args.repo))
print("добавлен README.md", flush=True)
# по коммиту на под-датасет: обрыв не обнуляет прогресс
subs = sorted(os.path.join(r, d) for r, ds_, _ in os.walk(args.src)
for d in ds_ if d.startswith("sub_"))
groups = sorted({os.path.dirname(s) for s in subs})
units = groups if groups else [args.src]
print(f"коммитов будет {len(units)}", flush=True)
futures = []
for i, path in enumerate(units, 1):
rel = os.path.relpath(path, args.src)
print(f"[{i}/{len(units)}] -> {rel}", flush=True)
futures.append((rel, api.upload_folder(
folder_path=path, path_in_repo=rel if groups else "",
repo_id=args.repo, repo_type="dataset", run_as_future=True)))
if len(futures) >= args.workers: # дождаться пачки, чтобы не копить запросы
for name, f in futures:
f.result()
print(f" готово: {name}", flush=True)
futures = []
for name, f in futures:
f.result()
print(f" готово: {name}", flush=True)
print(f"ГОТОВО: https://huggingface.co/datasets/{args.repo}", flush=True)
def _readme(repo):
return f"""---
license: other
task_categories:
- text-to-image
tags:
- moss-image
- diffusers
- latents
- anime
private: true
---
# {repo.split('/')[-1]}
Закешированные латенты VAE и капшены для файнтюна Mossling (MOSS-Image, 30-блочный DiT).
Собран `src/make_dataset.py` из отфильтрованного набора, записан скриптом `src/filter_dataset.py`.
## Формат строки
| поле | тип | что это |
|---|---|---|
| `vae` | float16 `[128, H/16, W/16]` | латент VAE, упакованный 2x2 и bn-нормированный |
| `text` | str | капшен: теги gelbooru через запятую, без `*tags*` |
| `width`, `height` | int | размер кадра, из которого сделан латент |
| `aspect` | str | `1:1`, `3:4`, `4:3` ... |
| `source` | str | путь исходного файла |
Колонок `text_embeds`/`text_mask` нет: условие считается на лету энкодером Mossling
(`src/train.py --te`).
## Фильтрация
Из исходного набора отброшено 43.5% строк по тегам капшена: листы персонажа и ракурсы,
надписи и речь, монохром и скетч, несколько персонажей, низкое качество, обложки,
лишние конечности, обрезанный кадр, сцены без людей, капшены длиннее 30 тегов.
## Лицензия
Изображения — из открытого набора с тегами; права принадлежат их авторам.
Датасет собран для обучения и не является перераспространением оригиналов в исходном виде
(хранятся только латенты).
"""
if __name__ == "__main__":
main()