Files
Robot_Arm/ml/selftest.sh
T
cbb 8ad6b58fca ml: 显存预算按 19–20 GB 落地 + ort 本地副本入库 + R4 复跑(feat_dyn)通过
- 显存:APG batch 128→48(实测 d=192 下 128 无论 plans 60/150 都 OOM,崩前已分配 17.85 GiB;48/plans150=13.08、48/60=7.67);新增 ml/common/vram.py 每进程硬帽 VRAM_FRAC=0.78 并把 vram 峰值写进 train/APG 日志、监控页加「显存峰值」卡与按 20 GB 预算的显存条;smoke.json 补 apg 段并让自检带 --config。
- 自检修复:find_bash 兼容便携版 git 的 mingw64 布局(本机原返回 None → 网页「开始训练」起不动);selftest_proc.alive 读中文 tasklist 的 GBK 崩溃;rec 值非法 JSON 时静默丢项;gen_resume 判据剔除 plan_ms/cert.ms 两个墙钟字段(旧判据恒 false)。整轮重跑 7/8 通过、3.5 分钟,仅 train_term 为 Windows 真实限制(TERM 送不到原生 python)。
- vendor: onnxruntime-web 1.20.1 本地副本入库(约 33 MB),查看器与 R4/R9 探针不再依赖 npm/CDN;.gitignore 同步移除该目录。
- docs: 第八节 R4 复跑(feat_dyn 新模型):部署口径 P99 12.67 通过;WebGPU 首次实测 27.09(有界面窗口,adapter nvidia ampere);viewer_ref 随机模型 planP99 131 / starve 32.7% / lookaheadP99 170 / certFailWhy time 948 / tickGapMax 736,三条判读留给实现方。
2026-10-18 06:40:25 +08:00

81 lines
6.3 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 本机自检(2026-10-18 第七轮,遗留 C-5 / C-6:Windows 生命周期与断点续跑只在沙箱验证过)。约 5–10 分钟。
# 在仓库旁的副本(默认 ../<仓库名>_selftest)里做,不碰本仓库数据。结果写本仓库 ml/runs/selftest.json,每项 pass=true/false:
# gen_resume 生成 6 条一口气 vs 先生成 3 条、把最后一行截掉一半、再续到 6 条 → 两份文件逐字节相同
# prep_rerun 打包两次,第二次「待打包 0 个」
# train_stop 训练 --stop-at 20 → rc 143、last.pt 第 20 步;续训到 40 步 rc 0
# train_stopfile 训练中途写 ml/runs/STOP → 60 s 内 rc 143 且 last.pt 已存(控制台「停止」走的就是这条路)
# train_term Git Bash 的 timeout 送 TERM 给训练 → 是否来得及存 last.pt(Windows 原生 python 未必收得到,结果决定控制台是否只能靠 STOP 文件)
# apg_resume APG --stop-at 5 → rc 143;--resume 续跑 rc 0
# tree_killed / survive_parent_exit 控制台的进程树停止、关窗口后子树存活(ml/selftest_proc.py)
set -o pipefail
cd "$(dirname "$0")/.."
SRC=$(pwd); PY=${PYTHON:-python}; export PYTHONUTF8=1
D=${SELFTEST_DIR:-$(dirname "$SRC")/$(basename "$SRC")_selftest}
rm -rf "$D"; mkdir -p "$D"
git ls-files -z | tar --null -T - -cf - | (cd "$D" && tar xf -)
[ -d ml/node_modules ] && cp -r ml/node_modules "$D/ml/node_modules"
cd "$D"; mkdir -p ml/runs ml/data
R=ml/runs/selftest_items.jsonl; : > $R
rec() { $PY -c "import json,sys
try: v=json.loads(sys.argv[2])
except Exception: v=False # 值不是合法 JSON 时记 false,不能让整项静默消失(第七轮实测:'none' 抛异常 → 两项没进结果)
print(json.dumps({'item':sys.argv[1],'pass':v,'detail':sys.argv[3]},ensure_ascii=False))" "$1" "$2" "$3" >> $R; echo "[selftest] $1 → $2 $3"; }
CFGS=ml/configs/smoke.json
# 1 生成续写
node ml/gen/gen_dataset.cjs --n 6 --seed 11 --out ml/data/a.jsonl 2>/dev/null
node ml/gen/gen_dataset.cjs --n 3 --seed 11 --out ml/data/shard_0011.jsonl.part 2>/dev/null
sz=$(wc -c < ml/data/shard_0011.jsonl.part); head -c $((sz - 40)) ml/data/shard_0011.jsonl.part > t && mv t ml/data/shard_0011.jsonl.part
node ml/gen/gen_dataset.cjs --n 6 --seed 11 --out ml/data/shard_0011.jsonl.part 2>ml/runs/st_gen.log; mv ml/data/shard_0011.jsonl.part ml/data/shard_0011.jsonl
gd=$($PY -c "import json,sys
f=lambda p:[json.dumps({**r,'plan_ms':0,'cert':{**r.get('cert',{}),'ms':0}},sort_keys=True) for r in (json.loads(l) for l in open(p,encoding='utf-8') if l.strip())]
a,b=f(sys.argv[1]),f(sys.argv[2]);print('OK' if a==b else '不同 %d/%d 条'%(sum(x!=y for x,y in zip(a,b))+abs(len(a)-len(b)),max(len(a),len(b))))" ml/data/a.jsonl ml/data/shard_0011.jsonl)
if [ "$gd" = OK ]; then rec gen_resume true "$(grep -o '续写[^,]*' ml/runs/st_gen.log | head -1)(逐条一致;不含 plan_ms/cert.ms 两个墙钟字段,第七轮实测 cmp -s 恒不等)"
else rec gen_resume false "续写与一口气生成不同:$gd"; fi
rm -f ml/data/a.jsonl
node ml/gen/gen_dataset.cjs --n 40 --seed 12 --out ml/data/shard_0012.jsonl 2>/dev/null # 训练自检用的小数据
# 2 打包二次运行
bash ml/prep/run_prep.sh 4 >/dev/null 2>&1; bash ml/prep/run_prep.sh 4 > ml/runs/st_prep.log 2>&1
grep -q "待打包 0 个" ml/runs/st_prep.log && rec prep_rerun true "" || rec prep_rerun false "$(head -c 200 ml/runs/st_prep.log)"
T="--samples ml/data/samples --no-eval --config $CFGS --steps 40"
step_of() { $PY -c "import torch;print(torch.load('$1',map_location='cpu',weights_only=False)['step'])" 2>/dev/null; }
# 3 --stop-at
$PY -m ml.train $T --out ml/runs/st_a --stop-at 20 > ml/runs/st_a.log 2>&1; rc=$?
s=$(step_of ml/runs/st_a/last.pt)
$PY -m ml.train $T --out ml/runs/st_a --resume ml/runs/st_a/last.pt >> ml/runs/st_a.log 2>&1; rc2=$?
[ $rc = 143 ] && [ "$s" = 20 ] && [ $rc2 = 0 ] && rec train_stop true "rc 143 / 第 $s 步 / 续训 rc 0" || rec train_stop false "rc=$rc step=$s 续训rc=$rc2"
# 4 STOP 文件
rm -f ml/runs/STOP
$PY -m ml.train --samples ml/data/samples --no-eval --config $CFGS --steps 1000000 --out ml/runs/st_b > ml/runs/st_b.log 2>&1 & pid=$!
sleep 25; touch ml/runs/STOP; t0=$(date +%s)
wait $pid; rc=$?; dt=$(( $(date +%s) - t0 )); rm -f ml/runs/STOP
[ $rc = 143 ] && [ -f ml/runs/st_b/last.pt ] && rec train_stopfile true "写 STOP 后 ${dt}s 退出,第 $(step_of ml/runs/st_b/last.pt) 步" || rec train_stopfile false "rc=$rc ${dt}s"
# 5 timeout TERM
if command -v timeout >/dev/null; then
timeout -k 30 25 $PY -m ml.train --samples ml/data/samples --no-eval --config $CFGS --steps 1000000 --out ml/runs/st_c > ml/runs/st_c.log 2>&1; rc=$?
grep -q "收到停止请求" ml/runs/st_c.log && rec train_term true "rc=$rc,TERM 送达并存档" || rec train_term false "rc=$rc,TERM 没送到 python(Windows 原生进程常见)→ 阶段超时只能硬杀,进度靠每 10 分钟存档"
else rec train_term false "没有 timeout 命令"; fi
# 6 APG 续跑
$PY -m ml.apg.apg_finetune --config $CFGS --init ml/runs/st_a/last.pt --samples ml/data/samples --out ml/runs/st_apg --steps 10 --stop-at 5 > ml/runs/st_apg.log 2>&1; rc=$?
$PY -m ml.apg.apg_finetune --config $CFGS --init ml/runs/st_a/last.pt --samples ml/data/samples --out ml/runs/st_apg --steps 10 --resume ml/runs/st_apg/last.pt >> ml/runs/st_apg.log 2>&1; rc2=$?
[ $rc = 143 ] && [ $rc2 = 0 ] && rec apg_resume true "" || rec apg_resume false "rc=$rc 续跑rc=$rc2(日志 $D/ml/runs/st_apg.log)"
# 7 进程树 / 关窗口
pj=$($PY ml/selftest_proc.py 2>ml/runs/st_proc.err | tail -1)
echo "$pj" > ml/runs/st_proc.json
k=$($PY -c "import json,sys
try: d=json.loads(sys.argv[1])
except Exception: d={}
print(json.dumps(d.get('tree_killed')), json.dumps(d.get('survive_parent_exit')))" "$pj" 2>/dev/null || echo "null null")
rec tree_killed ${k% *} "$pj" ; rec survive_parent_exit ${k#* } ""
mkdir -p "$SRC/ml/runs"
$PY - "$R" "$SRC/ml/runs/selftest.json" "$D" <<'PY'
import json, sys
items = []
for l in open(sys.argv[1], encoding='utf-8'):
try: items.append(json.loads(l))
except Exception: items.append({'item': 'parse', 'pass': False, 'detail': l.strip()[:200]})
out = {'all_pass': all(i['pass'] is True for i in items), 'items': items, 'dir': sys.argv[3]}
json.dump(out, open(sys.argv[2], 'w', encoding='utf-8'), indent=1, ensure_ascii=False)
print('[selftest] 全部通过' if out['all_pass'] else '[selftest] 有未通过项,见 ' + sys.argv[2])
PY