第五轮:锁/续跑/超时/自动分支/R4 探针与 encode-once/控制台加固;阶段内断点续跑(gen 按条、prep 按 shard、训练与 APG 定时原子存档+体面停止);分歧以实现方意见为准

This commit is contained in:
Hark
2026-10-18 06:40:25 +08:00
committed by cbb
parent 3675fbbd9b
commit f40a40c56b
36 changed files with 1199 additions and 285 deletions
+2 -2
View File
@@ -45,9 +45,9 @@ def main():
so_side = torch.cat([p['side'] for _, p in po]); sn_side = torch.cat([p['side'] for _, p in pn])
det = torch.tensor([c in ('B', 'C') for c in cat])
# 2026-10-18:左右比例按全部 B/C 题的首次 J1 方向统计,不以到达为条件(APG 把一侧绕行全做失败时,到达子集比例可纹丝不动)
left = lambda sd, ok: (((sd > 0) & det).sum() / det.sum().clamp(min=1)).item()
left = lambda sd: (((sd > 0) & det).sum() / det.sum().clamp(min=1)).item() # side 为首次 J1 方向(closed_loop,第五轮改)
ok_o, ok_n = torch.tensor(so, dtype=torch.bool), torch.tensor(sn, dtype=torch.bool)
lr_shift = abs(left(sn_side, ok_n) - left(so_side, ok_o)) * 100
lr_shift = abs(left(sn_side) - left(so_side)) * 100
both = ok_o & ok_n
tro = torch.cat([p['travel'] for _, p in po]); trn = torch.cat([p['travel'] for _, p in pn])
travel_ratio = (trn[both].median() / tro[both].median().clamp(min=1e-6)).item() if both.any() else None
+21 -3
View File
@@ -3,6 +3,8 @@
# bash ml/mining/loop.sh [轮数=6] [每轮 APG 步数=1000] [每轮出题数=50000]
# 2026-10-18:APG 采样池只用本轮错题(此前累积所有历史轮,后期分布偏向老错题);老错题的保持由门禁"上轮修好仍对"负责。
# 统一 $PYTHON;门禁加 R7(左右比例 / 行程)。
# 2026-10-18 第五轮(审查 N-11 / 缺口 1):可续跑——已有 gate.json 的轮次直接跳过(读回 gain 与 fixed 链),
# 中途断在某轮的 APG 时用该轮 last.pt 续跑;current.pt 只在门禁通过时由 gate.py 更新,本身就是断点。
set -e
cd "$(dirname "$0")/../.."
PY=${PYTHON:-python}
@@ -13,14 +15,30 @@ mkdir -p ml/runs/mine
SAMPLES="ml/data/samples"; FIXED=""; small=0
for ((r=1; r<=ROUNDS; r++)); do
D=ml/runs/mine/r$r; mkdir -p $D
$PY -m ml.mining.mine_round --ckpt ml/runs/current.pt --seed $((1000+r)) --n $N --out $D
if [ -f $D/gate.json ]; then
gain=$($PY -c "import json;print(json.load(open('$D/gate.json'))['gain_pp'])")
echo "round $r 已完成(gain ${gain} pp),跳过"; FIXED="--fixed $D/gate_fixed.jsonl"
[ -s ml/data/e2/shard_e2_r$r.jsonl ] && SAMPLES="ml/data/samples ml/data/samples_e2"
if $PY -c "import sys;sys.exit(0 if $gain < 0.5 else 1)"; then small=$((small+1)); else small=0; fi
if [ $small -ge 2 ]; then echo "连续 2 轮提升 < 0.5 pp,停止"; break; fi
continue
fi
# 2026-10-18 断点续跑:挖错做完打 mined.ok;E2 老师补路径写 .part 续写、写完改名。重挖时清掉旧的补路径,免得和新题对不上
E2F=ml/data/e2/shard_e2_r$r.jsonl
if [ -f $D/mined.ok ]; then echo "round $r 挖错已完成,跳过"
else
rm -f $E2F $E2F.part
$PY -m ml.mining.mine_round --ckpt ml/runs/current.pt --seed $((1000+r)) --n $N --out $D
touch $D/mined.ok
fi
# E2(局部极小)请老师补少量路径(唯一保留的后训练老师用途)
if [ -s $D/e2_tasks.jsonl ]; then
node ml/gen/gen_dataset.cjs --tasks $D/e2_tasks.jsonl --seed $((5000+r)) --out ml/data/e2/shard_e2_r$r.jsonl
[ -f $E2F ] || { node ml/gen/gen_dataset.cjs --tasks $D/e2_tasks.jsonl --seed $((5000+r)) --out $E2F.part && mv $E2F.part $E2F; }
node ml/prep/build_dataset.cjs ml/data/e2/shard_e2_r$r.jsonl ml/data/samples_e2
SAMPLES="ml/data/samples ml/data/samples_e2"
fi
$PY -m ml.apg.apg_finetune --init ml/runs/current.pt --samples $SAMPLES --pool $D/pool.jsonl --out ml/runs/apg_r$r --steps $STEPS
res=""; [ -f ml/runs/apg_r$r/last.pt ] && res="--resume ml/runs/apg_r$r/last.pt"
$PY -m ml.apg.apg_finetune --init ml/runs/current.pt --samples $SAMPLES --pool $D/pool.jsonl --out ml/runs/apg_r$r --steps $STEPS $res
$PY -m ml.mining.gate --old ml/runs/current.pt --new ml/runs/apg_r$r/best.pt --tasks ml/runs/gate_tasks.jsonl --out $D/gate.json $FIXED
FIXED="--fixed $D/gate_fixed.jsonl"
gain=$($PY -c "import json;print(json.load(open('$D/gate.json'))['gain_pp'])")
+15 -3
View File
@@ -7,8 +7,19 @@ import torch
from ml.common.features import wrap180
def min_gap_chunked(kin, q, cap=0.5, chunk=512):
"""分块算最小间隙:随机位形每个约 2800 个幸存胶囊对、峰值约 1.5 MB/位形,一次 4 万位形会爆内存/显存(2026-10-18 实测出题 OOM),每块 512"""
def auto_chunk(kin, mb_per_q, lo=256, hi=8192):
"""2026-10-18 第五轮(审查 N-6):分块大小按设备空闲显存定,用掉其 1/4。
两处每位形峰值不同不是矛盾:kin.gaps 按 (胶囊对下限 + cap)² 筛候选对,cap=0.5 mm 时幸存约 2800 对(≈1.5 MB/位形),
column_gap 用 cap=40 mm,40 mm 内幸存的对多一个量级(≈20 MB/位形)。CPU 上取下限 256。"""
if kin.lo.device.type == 'cuda':
free = torch.cuda.mem_get_info(kin.lo.device)[0]
return int(max(lo, min(hi, free * 0.25 / (mb_per_q * 1e6))))
return lo
def min_gap_chunked(kin, q, cap=0.5, chunk=None):
"""分块算最小间隙:一次 4 万位形会爆内存/显存(2026-10-18 实测出题 OOM);块大小见 auto_chunk(≈1.5 MB/位形)"""
chunk = chunk or auto_chunk(kin, 1.5)
return torch.cat([kin.gaps(q[i:i + chunk], cap)['gm'].min(-1).values for i in range(0, q.shape[0], chunk)])
@@ -33,7 +44,8 @@ def straight_blocked(kin, a, b, n=24):
return (min_gap_chunked(kin, pts) <= 0).view(a.shape[0], n).any(-1)
def column_gap(kin, q, chunk=256):
def column_gap(kin, q, chunk=None):
chunk = chunk or auto_chunk(kin, 20.0) # cap=40 → ≈20 MB/位形
out = []
for s in range(0, q.shape[0], chunk): # 与 min_gap_chunked 一样分块防 OOM
gm = kin.gaps(q[s:s + chunk], 40)['gm']