samai-8b-M8 / PLAYBOOK.md
tchbcb's picture
M10 avoidance playbook (public copy)
e876cf0 verified
|
Raw History Blame Contribute Delete
7.03 kB

Samai-8B 训练管线避坑手册(PLAYBOOK)

2026-09-21 M10 灾后重建会话沉淀。每条坑都有真实事故背书, 配避坑代码模板。 配套工具库: samai_kit.py(远程 python 进程用, 正常函数作用域) ⚠️ 唯一例外区: 信封 payload 内联代码必须全平铺(见 §2)

§1 通道层: 信封协议

  • 信封 POST text/plain → /execute?respenc=b64url&timeout=N
  • 坑: 网关 ~180s 硬超时 → 同步信封只做 <120s 的活
  • 避坑: 长任务 = 信封只负责"写脚本 + Popen 分离点火", 然后轮询日志
    import subprocess
    subprocess.Popen(['bash', '/tmp/k8b/xxx_chain.sh'], start_new_session=True)
    print('FIRED')
    
  • 坑: exec 槽偶发静默失败 → 点火后必须 sleep 3 + ps 验证进程存在

§2 远程 exec 槽编码规范(最重要)

exec(code) 的 globals/locals 分离, 三个连招坑:

  1. def 函数体看不到顶层 import 和变量 → 函数内必须重新 import subprocess
  2. 顶层生成器表达式/列表推导也翻车(sorted(x for x in ...) 报 name not defined)
  3. 嵌套函数互相不可见(rstr 在 rval 里 undefined)

避坑三原则(内联 payload 专用):

  • 原则A: 全平铺, 不写 def; 复用逻辑用 for 循环 + 普通 if/append
  • 原则B: 每个循环体里用的模块, 在循环体前重新 import(或干脆顶层 import 且不用函数)
  • 原则C: 结构复杂 → 写进 .py 文件再 python3 /tmp/k8b/xxx.py(文件内代码是正常作用域, 可用 def/推导式)

§3 资产下载规范

  • 坑: 并发 curl -C - 写同一文件 → 文件交错损坏(本会话 M9 GGUF 膨胀到 9.37G) 避坑: 下载前 flock 或保证单实例; 见 samai_kit.dl()
  • 坑: 只需要大文件的局部(如 GGUF 里某段张量)→ 全量下载浪费 避坑: HTTP Range curl -r START-END(本会话 5.78G 文件只拉 151MB 片段) 前提: 精确计算字节区间并校验 size == end-start+1
  • 坑: tar 包内目录前缀(bin/llama-server 解压成 bin/bin/llama-server) 避坑: 解压后必须 test -x 目标二进制, 失败则 flatten mv inner/* outer/
  • 坑: snapshot_download 私仓 → repo_type='model'(没有 'private' 这个值)
  • 下载完必校验: size 断言 + sha256

§4 磁盘预算规范(57G 容器)

  • 事故: base(19.3G) + merged(18.5G) + bf16 GGUF(18.5G) 共存 = 爆盘
  • 避坑流程化:
    1. 每阶段点火脚本里 df -h / | tail -1 打印
    2. 中间产物用完即删: merge 目录 convert 完 rm -rf; f16 GGUF quantize 完 rm -f
    3. 大文件可用Range 拉, 不落全量
    4. 预算表先算再跑: 训练期 base19.3+ckpt1 ≈ 21G; 收尾期 base19.3+merged18.5+q4 5.5 峰值 44G(逐步删回 25G)
    5. 重建会话最优路径: 能拉产物就不重建过程(拉 5.5G GGUF 而不是重下 19.3G base 重训 1.5h)

§5 GGUF 手术规范(字节外科)

  • 头布局: magic(4) version(4) tensor_count(u64@8) kv_count(u64@16) 坑: patch 偏移写错一位 → kv_count 被改 → 'string length 2^58' 假错误
  • 坑: header_end 必须在"读完 KV + 全部 tensor infos"之后取 f.tell() (本会话把 KV 尾当 header 尾 → 411 条 info 被截断 → 'failed to read tensor info')
  • tensor info 结构: u64 name_len + name + u32 n_dims + u64 dims[nd] + u32 type + u64 offset
  • offset 是相对数据区起点(align32(header_end)); 追加 info 只需平移数据区起点
  • 张量数据区间连续时: 起止 = 数据区起点 + 首尾张量 offset/size
  • 避坑断言(每次手术必带):
    assert nt == 427 and names[-1] == 'blk.31.post_attention_norm.weight'
    assert os.path.getsize(OUT) == new_data + expect_end_rel
    
  • 手术后必 smoke: server 加载 + 1 题推理验证内容正确(不只 health OK)

§6 serving/评测规范

  • 坑(假 0.0% 元凶): llama.cpp 0.4.x 默认 reasoning 分离 — <think> 未闭合(截断)时 全文进 reasoning_content, content 为空 → 评测器提取不到 → pred=None 全 0 M8/M9 的"真实 HLE 0.0%"疑似此机制假 0
  • 双保险: ① server 加 --reasoning-format none ② eval 客户端拼接:
    m = d['choices'][0]['message']
    txt = (m.get('content') or '') + '\n' + (m.get('reasoning_content') or '')
    
  • 坑: 评测 max_tokens 不足 → think 不闭合 → 兜底行是垃圾 → 即使 format none 也低分 HLE 口径: max_tokens 1024 时 ~20% 题截断; 对照实验需 2048
  • 坑: 端口/进程混战(旧 server 活着 → 新 server 绑定失败 → health 误测旧实例 → 新 eval 打到旧 server) 避坑: 点火链开头 pkill -9 -f llama-server + ss -ltnp | grep PORT 验证 FREE → 起 → ps 断言唯一实例
  • health grep 宽松匹配 ok 即可, 但 smoke 必须验证输出内容(如 17*23=391)

§7 进程管理规范

  • 一切后台任务: nohup ... > log 2>&1 & + start_new_session=True(Popen)
  • 每条链独立日志文件, 轮询用 tail -c N(UTF-8 由 python decode(errors='replace') 处理)
  • 坑: tail -c 切断多字节 UTF-8 → 解码端 errors='replace'
  • 链内关键节点 echo 标记(HEALTH_OK / EVAL_RC / CHAIN_DONE)→ 轮询 grep 标记

§8 灾备规范

  • 产物尽早推仓(本会话 M9 产物靠上会话推仓才免于重建)
  • 里程碑即推: adapter 每epoch / GGUF+sha256 / 报告 json
  • 私仓 LFS 满仓只读 → 产物推公仓; 公私分工: 私仓=脚本文档, 公仓=权重+报告
  • 推送脚本自带重试(5 次) + 等待循环(产物未生成就位时轮询最多 100 分钟)
  • 重建优先级: 公仓产物 > 私仓脚本重跑 > 从头再来

§9 训练规范(M10 实证)

  • fp16=False 铁律: Trainer fp16 的 GradScaler(65536) 放大溢出 → grad_norm=nan → 永久跳步零学习 冻结 base 只训 LoRA 时根本不需要 fp16
  • nan 自杀保护: 前 4 步 3 次 loss=0/nan → 写 TRAIN_NAN + 停机(防毒 adapter 混入 final/)
  • 停机后重启必须先清 final/ TRAIN_DONE TRAIN_NAN 毒文件
  • QLoRA 环境: peft 0.19.1 与 torchao 0.10 冲突(dispatch ImportError)→ 卸 torchao
  • 数据前缀 mask: prefix = 非 assistant 消息的公共前缀 token 数 → labels 前缀置 -100
  • 自杀保护误触发复盘: run 自杀停机后脚本无早退检查 → 未训练 adapter 存成 final → 误 merge

§10 M11 前瞻(多模态 + computer use)

  • 训练: AutoModelForImageTextToText + AutoProcessor; LoRA regex .*language.*\.(q|k|v|o|gate|up|down)_proj 防误挂 ViT 的 q/k/v; vision tower + merger 显式 freeze
  • merge: ConditionalGeneration 全栈(保视觉塔); convert 双产物: f16 GGUF + --mmproj
  • serving: llama-server -m q4.gguf --mmproj mmproj.gguf
  • RFT: playwright 无头 chromium, DOM 断言自动判分; 截图 ≤1120px 长边控视觉 token 预算
  • 骨架四件套已上私仓: s8_m11_t4_train_v6.py / s8_m11_cu_env.py / s8_m11_merge_v3.py / s8_m11_serving_chain.sh