RNAQwen / scripts /49_push_hf.py
RomanCohort's picture
add scripts
acd280c verified
Raw History Blame Contribute Delete
6.22 kB
"""按目录分组推送到 HuggingFace:RomanCohort/RNAQwen
两个已经踩过的坑(都是我的设计错误,记下来):
1. `upload_folder`(一次性原子提交)—— 读完 130 MB 本地文件后网络 0 字节,9 分钟不动。
本地 ReadTransferCount 134.96 MB,WriteTransferCount 0.06 MB。挂住就全丢,且不可见。
2. 逐文件上传 —— 198 个文件 = 198 次 create_commit,**429 Too Many Requests**。
上传通道本身没问题(实测 65-82 KB/s),坏的是**提交次数**。
所以:**按目录分组,一次 commit 带一批文件**,总共 ~8 次提交。
429 单独处理:指数退避,必要时长时间等待。
用法:
python scripts/49_push_hf.py --list
python scripts/49_push_hf.py --dry
python scripts/49_push_hf.py --cooldown 120 # 先等 2 分钟再开始
"""
from __future__ import annotations
import argparse
import os
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SRC = ROOT / "_hf_upload"
REPO_ID = "RomanCohort/RNAQwen"
REPO_TYPE = "model"
SKIP_PARTS = {"_speedtest", "__pycache__"}
SKIP_NAMES = {"_probe.txt"}
def groups() -> list[tuple[str, list[Path]]]:
"""按目录分组 —— 每组一次 commit,把提交次数从 198 压到 ~8。"""
files = [p for p in SRC.rglob("*") if p.is_file()
and p.name not in SKIP_NAMES
and not (SKIP_PARTS & set(p.relative_to(SRC).parts))]
bucket: dict[str, list[Path]] = {}
for p in files:
rel = p.relative_to(SRC)
parts = rel.parts
if len(parts) == 1:
key = "根目录"
elif parts[0] == "scripts" and len(parts) > 2:
key = f"scripts/{parts[1]}" # scripts/_scratch 单独一组
elif parts[0] == "reports":
key = f"reports/{p.stat().st_size // 1_000_000}MB" # 报告按大小切,避免一次太多
elif parts[0] == "data" and len(parts) > 2:
key = f"data/{parts[1]}"
elif parts[0] == "data":
key = "data/大文件" if p.stat().st_size > 5_000_000 else "data/根"
else:
key = parts[0]
bucket.setdefault(key, []).append(p)
# 大文件单独成组(它们的 LFS 上传占绝大多数时间,单独一组便于重试)
out = []
for k, v in sorted(bucket.items()):
big = [p for p in v if p.stat().st_size > 5_000_000]
small = [p for p in v if p.stat().st_size <= 5_000_000]
if small:
out.append((k, sorted(small, key=lambda p: p.stat().st_size)))
for p in big:
out.append((f"{k}/{p.name}", [p]))
return out
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--repo", default=REPO_ID)
ap.add_argument("--list", action="store_true")
ap.add_argument("--dry", action="store_true")
ap.add_argument("--cooldown", type=int, default=0, help="开始前先等这么多秒(躲 429)")
ap.add_argument("--attempts", type=int, default=5)
args = ap.parse_args()
gs = groups()
total = sum(p.stat().st_size for _, v in gs for p in v)
print(f"[src] {len(gs)} 组 / {sum(len(v) for _,v in gs)} 文件 / {total/1024/1024:.1f} MB", flush=True)
for k, v in gs:
print(f" {k:<34} {len(v):>3} 文件 {sum(p.stat().st_size for p in v)/1024/1024:>7.2f} MB")
if args.list:
return 0
token = os.environ.get("HF_TOKEN") or None
from huggingface_hub import HfApi, CommitOperationAdd
api = HfApi(token=token)
try:
who = api.whoami()
print(f"[auth] {who.get('name')} ({who.get('type')})", flush=True)
except Exception as e:
print(f"[auth] 失败 {type(e).__name__}: {str(e)[:200]}")
return 1
have: dict[str, int] = {}
try:
info = api.repo_info(args.repo, repo_type=REPO_TYPE, files_metadata=True)
for s in (info.siblings or []):
have[s.rfilename] = getattr(s, "size", None) or -1
print(f"[repo] 远端已有 {len(have)} 个文件", flush=True)
except Exception:
api.create_repo(args.repo, repo_type=REPO_TYPE, exist_ok=True)
if args.dry:
print("[dry] 校验通过,未上传。")
return 0
if args.cooldown:
print(f"[cool] 等 {args.cooldown}s 躲 429 ...", flush=True)
time.sleep(args.cooldown)
ok = fail = 0
t_all = time.time()
for gi, (name, files) in enumerate(gs, 1):
todo = [p for p in files
if have.get(p.relative_to(SRC).as_posix()) != p.stat().st_size]
if not todo:
print(f"[{gi}/{len(gs)}] skip {name}", flush=True)
continue
ops = [CommitOperationAdd(path_in_repo=p.relative_to(SRC).as_posix(),
path_or_fileobj=str(p)) for p in todo]
wait = 20
for att in range(1, args.attempts + 1):
t0 = time.time()
try:
api.create_commit(repo_id=args.repo, repo_type=REPO_TYPE,
operations=ops, commit_message=f"add {name}")
dt = time.time() - t0
mb = sum(p.stat().st_size for p in todo) / 1024 / 1024
print(f"[{gi}/{len(gs)}] ok {name:<34} {len(todo):>3} 文件 {mb:>7.2f} MB "
f"{dt:>6.1f}s {mb*1024/dt:>6.1f} KB/s", flush=True)
ok += 1
break
except Exception as e:
msg = str(e)
is429 = "429" in msg
print(f"[{gi}/{len(gs)}] retry {att}/{args.attempts} {name} "
f"{'429 ' if is429 else ''}{type(e).__name__}", flush=True)
if att == args.attempts:
fail += 1
else:
print(f" 等 {wait}s ...", flush=True)
time.sleep(wait)
wait = min(wait * 3, 600) # 429 要等久一点
print(f"\n[done] 组成功 {ok} 失败 {fail} 用时 {(time.time()-t_all)/60:.1f} min", flush=True)
print(f" https://huggingface.co/{args.repo}", flush=True)
return 1 if fail else 0
if __name__ == "__main__":
raise SystemExit(main())