# Launch Experiment

启动训练实验，自动监控并修复常见错误，直到实验成功运行。

## 使用方法

```bash
/launch-experiment chengyue_script/dvlm/training/.../train_xxx.sh
/launch-experiment train_sharegpt4v_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision.sh
```

---

## 工作流程

### Phase 1: 预检查 (Pre-flight Checks)

在 sbatch 之前进行静态检查，避免明显错误：

```bash
# 1. 语法检查
bash -n script.sh

# 2. 检查关键参数一致性
grep -E "(RUN_ID|DATASET_PATH|BD_SIZE|LR)" script.sh

# 3. 检查文件名与 RUN_ID 是否匹配
# 文件名: train_xxx.sh 中的 xxx 应该与 RUN_ID 一致

# 4. 检查数据集是否存在 (如果可能)
# VILA registry 中是否有该数据集

# 5. 检查模型路径是否存在
grep "MODEL_PATH" script.sh
ls -d ${MODEL_PATH} 2>/dev/null
```

### Phase 2: 提交任务

```bash
sbatch script.sh
# 记录 job_id
```

### Phase 3: 监控循环 (Monitor Loop)

```bash
while true; do
    # 检查 job 状态
    squeue -j ${job_id} -h
    
    # 如果 job 不在队列中，检查是否成功
    if job_not_in_queue; then
        check_success_or_failure
    fi
    
    sleep 30
done
```

### Phase 4: 错误分类与自动修复

---

## 错误分类

### Category A: 可自动修复 (Auto-fixable)

这些错误可以通过 sed 修改脚本后重新提交：

| 错误类型 | 错误信息 | 自动修复方法 |
|----------|----------|--------------|
| **A1: 路径拼写错误** | `No such file or directory: xxx` | `sed -i 's/typo/correct/g'` |
| **A2: 变量名拼写** | `CONPLE_MASK` vs `COMPLEMENTARY_MASK` | `sed -i 's/CONPLE_MASK/COMPLEMENTARY_MASK/g'` |
| **A3: 缺少目录** | `mkdir: cannot create directory` | `mkdir -p ${dir}` |
| **A4: 权限问题** | `Permission denied` (workdir) | `chmod +x script.sh` 或 `mkdir -p workdir` |
| **A5: 环境变量缺失** | `conda: command not found` | 添加 `source ~/.bashrc` |
| **A6: Python 导入路径** | `ModuleNotFoundError: No module named 'xxx'` | 修复 PYTHONPATH |

### Category B: 需要用户确认 (User Confirmation Required)

这些错误需要用户决策：

| 错误类型 | 错误信息 | 提示用户 |
|----------|----------|----------|
| **B1: OOM** | `CUDA out of memory` | 建议减小 batch size 或启用 gradient checkpointing |
| **B2: 数据集不存在** | `Dataset xxx not found in registry` | 确认数据集名称 |
| **B3: 模型路径错误** | `Model path does not exist` | 确认模型路径 |
| **B4: NCCL 超时** | `NCCL timeout` / `ALLGATHER_BASE` | 检查多节点配置 |
| **B5: 学习率问题** | `loss is NaN` | 建议降低学习率 |

### Category C: 致命错误 (Fatal - Stop)

这些错误需要人工深入排查：

| 错误类型 | 错误信息 | 行动 |
|----------|----------|------|
| **C1: 代码 bug** | `AttributeError`, `TypeError` in model code | 停止，报告错误位置 |
| **C2: 配置冲突** | 逻辑错误 (如 flexible + block_causal 同时启用) | 停止，报告冲突 |
| **C3: 硬件故障** | `GPU has fallen off the bus` | 停止，建议换节点 |

---

## 自动修复流程

### 检测错误

```bash
# 1. 检查 SLURM 输出日志
error_log="workdir/${RUN_ID}_error.log"
out_log="workdir/${RUN_ID}_out.log"

# 2. 检查训练日志
train_log="log/${RUN_ID}/train_*.log"

# 3. 提取错误信息
grep -i "error\|failed\|exception\|traceback" ${error_log} ${train_log}
```

### 错误模式匹配

```bash
# A1: 路径拼写
if grep -q "No such file or directory" ${error_log}; then
    missing_path=$(grep "No such file" ${error_log} | sed 's/.*: //')
    # 尝试找到正确路径
fi

# A2: 变量名拼写 (已知的常见错误)
if grep -q "CONPLE_MASK\|conplemenrary_mask" ${error_log}; then
    sed -i 's/CONPLE_MASK/COMPLEMENTARY_MASK/g' script.sh
    sed -i 's/conplemenrary_mask/complementary_mask/g' script.sh
    # 重新提交
fi

# A6: Python 导入
if grep -q "ModuleNotFoundError: No module named" ${error_log}; then
    module=$(grep "No module named" ${error_log} | sed "s/.*'\(.*\)'.*/\1/")
    # 检查 PYTHONPATH 是否包含该模块
fi

# B1: OOM
if grep -q "CUDA out of memory\|OutOfMemoryError" ${error_log}; then
    echo "⚠️ OOM 错误，建议："
    echo "  1. 减小 BATCH: sed -i 's/BATCH:-1/BATCH:-1/g' (已经是1)"
    echo "  2. 增加 GRAD_ACC: sed -i 's/GRAD_ACC:-4/GRAD_ACC:-8/g'"
    echo "  3. 启用 gradient checkpointing (如果未启用)"
    # 需要用户确认
fi

# B4: NCCL 超时
if grep -q "NCCL\|ALLGATHER\|timeout" ${error_log}; then
    echo "⚠️ NCCL 超时，可能原因："
    echo "  1. 多节点 FSDP wrapping 不一致"
    echo "  2. 网络问题"
    echo "  3. 某个节点 GPU 故障"
    # 需要用户确认
fi

# C1: 代码 bug
if grep -q "AttributeError\|TypeError\|KeyError" ${error_log}; then
    echo "❌ 代码错误，需要人工排查："
    grep -A 5 "AttributeError\|TypeError\|KeyError" ${error_log}
    # 停止自动修复
fi
```

---

## 完整执行流程

```
┌─────────────────────────────────────────────────────────────┐
│                    /launch-experiment                        │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ Phase 1: Pre-flight Checks                                   │
│  - bash -n script.sh (语法检查)                              │
│  - 检查 RUN_ID 与文件名一致                                   │
│  - 检查关键路径存在                                           │
└─────────────────────────────────────────────────────────────┘
                              │
                    ┌─────────┴─────────┐
                    │ 检查通过?          │
                    └─────────┬─────────┘
                         Yes  │  No → 报告问题，尝试自动修复
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ Phase 2: Submit Job                                          │
│  - sbatch script.sh                                          │
│  - 记录 job_id                                               │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ Phase 3: Monitor Loop (每 30-60 秒检查一次)                   │
│  - squeue -j ${job_id}                                       │
│  - 检查日志文件                                               │
└─────────────────────────────────────────────────────────────┘
                              │
                    ┌─────────┴─────────┐
                    │ Job 状态?          │
                    └─────────┬─────────┘
                              │
          ┌───────────────────┼───────────────────┐
          │                   │                   │
          ▼                   ▼                   ▼
     [RUNNING]           [COMPLETED]          [FAILED]
          │                   │                   │
          │                   ▼                   ▼
          │            检查 checkpoint      Phase 4: 错误分类
          │               是否生成               │
          │                   │         ┌───────┴───────┐
          │                   │         │               │
          │                   ▼         ▼               ▼
          │              ✅ 成功!    [Cat A]        [Cat B/C]
          │                         可自动修复      需用户确认
          │                              │               │
          │                              ▼               ▼
          │                         sed 修复        报告问题
          │                         重新提交        等待指令
          │                              │
          └──────────────────────────────┘
                    (循环)
```

---

## 实际执行示例

### 用户请求
```
/launch-experiment train_sharegpt4v_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision.sh
```

### 执行过程

```markdown
## Phase 1: Pre-flight Checks

✅ 语法检查通过: `bash -n script.sh`
✅ RUN_ID 与文件名一致: `sharegpt4v_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision`
✅ 模型路径存在: `/lustre/.../Qwen2.5-VL-3B-Instruct-mdm`
✅ 数据集已注册: `sharegpt4v_recipe`

## Phase 2: Submit

```bash
sbatch chengyue_script/dvlm/training/.../train_sharegpt4v_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision.sh
```
Job ID: 12345678

## Phase 3: Monitoring

| 时间 | 状态 | 备注 |
|------|------|------|
| 00:00 | PENDING | 等待资源 |
| 02:30 | RUNNING | 已分配 8 节点 |
| 05:00 | RUNNING | 训练中... |

## 监控命令

```bash
# 查看 job 状态
squeue -j 12345678

# 查看输出日志
tail -f workdir/train_sharegpt4v_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision_out.log

# 查看错误日志
tail -f workdir/train_sharegpt4v_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision_error.log

# 查看训练日志
tail -f log/sharegpt4v_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision/train_*.log
```
```

### 如果遇到错误

```markdown
## ⚠️ Job Failed - 自动诊断

**错误类型**: A2 (变量名拼写)
**错误信息**: `unrecognized arguments: --conplemenrary_mask`

**自动修复**:
```bash
sed -i 's/conplemenrary_mask/complementary_mask/g' script.sh
```

**重新提交**: 
```bash
sbatch script.sh
```
New Job ID: 12345679

---

## ⚠️ Job Failed Again - 需要用户确认

**错误类型**: B1 (OOM)
**错误信息**: `CUDA out of memory. Tried to allocate 2.5 GiB`

**建议选项**:
1. 增加 gradient accumulation: `sed -i 's/GRAD_ACC:-4/GRAD_ACC:-8/g'`
2. 减小 MAX_LENGTH: `sed -i 's/MAX_LENGTH:-2048/MAX_LENGTH:-1536/g'`
3. 检查是否启用了 gradient checkpointing

请选择 (1/2/3) 或输入其他修复方案:
```

---

## 已知的常见拼写错误

这些错误在现有脚本中出现过，可以自动修复：

| 错误 | 正确 | 出现位置 |
|------|------|----------|
| `CONPLE_MASK` | `COMPLEMENTARY_MASK` | 变量定义 |
| `conplemenrary_mask` | `complementary_mask` | torchrun 参数 |
| `sharegpt4v_sft` | `sharegpt4v_recipe` | 数据集名 (视情况) |

---

## 成功标志

实验成功运行的标志：

1. **Job 状态为 RUNNING 超过 5 分钟**
2. **训练日志出现**: `Step X/Y, Loss: Z.ZZ`
3. **Checkpoint 目录创建**: `output_models/${RUN_ID}/checkpoint-*`
4. **无 ERROR/FAILED 关键词** 在日志中

```bash
# 检查成功
ls output_models/${RUN_ID}/checkpoint-* 2>/dev/null && echo "✅ Checkpoint 已生成"
grep "Step.*Loss" log/${RUN_ID}/train_*.log | tail -3
```

---

## 输出格式

### 成功启动
```
## ✅ 实验启动成功

**实验名**: sharegpt4v_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision
**Job ID**: 12345678
**状态**: RUNNING (已运行 10 分钟)

**监控命令**:
```bash
squeue -j 12345678
tail -f log/sharegpt4v_.../train_*.log
```

**预计输出**:
- Checkpoint: `output_models/sharegpt4v_.../checkpoint-*`
- 评测结果: 训练完成后自动提交
```

### 失败需要人工介入
```
## ❌ 实验启动失败 - 需要人工介入

**错误类型**: C1 (代码 bug)
**错误信息**:
```
AttributeError: 'NoneType' object has no attribute 'shape'
  File "examples/finetune_qwenvl.py", line 234, in forward
```

**建议**: 检查模型代码 `examples/finetune_qwenvl.py:234`
```

---

## 限制

1. **不能无限循环**: 最多自动重试 3 次
2. **不修改核心代码**: 只修改脚本配置，不改 Python 代码
3. **需要用户在线**: Category B 错误需要用户确认
4. **日志依赖**: 需要能访问 SLURM 日志和训练日志
