# Make New Experiment

根据现有训练脚本，通过修改参数创建新的实验脚本。

## 核心原则

### 1. 禁止手写脚本内容
- **绝对禁止** 使用 Write 工具直接写入脚本内容
- **绝对禁止** 在回复中输出完整脚本让用户复制
- **必须使用** `cp` + `sed` 的方式从现有脚本派生

### 2. 为什么？
- 训练脚本通常 300+ 行，手写容易遗漏关键配置
- 现有脚本经过验证，包含正确的 SLURM 配置、信号处理、环境变量等
- `sed` 替换可追溯、可验证、可复现

---

## 工作流程

### Step 1: 理解用户需求

用户可能说：
- "创建一个 bd32 的 sharegpt4v 实验"
- "把学习率改成 1e-5"
- "用 j63_math 数据集跑一下"
- "加上 entropy loss"

提取关键变更：
| 参数类型 | 示例 |
|----------|------|
| 数据集 | `sharegpt4v_recipe`, `j63_math`, `j63_no_sft` |
| BD_SIZE | 4, 8, 16, 32 |
| LR | 1e-5, 1e-6, 5e-7 |
| 模式 | `block_causal`, `flexible`, `anneal` |
| 特殊 | `entropy_loss`, `fix_eos`, `llm_init` |

### Step 2: 找到最相似的基础脚本

```bash
# 列出所有训练脚本
ls chengyue_script/dvlm/training/multinode/mdm/sharegpt4v/sweep_flexible_constant_and_block_causal/*.sh

# 按关键词筛选
ls ... | grep bd16 | grep anneal | grep block_causal
```

**选择原则**：
1. 优先选择只需改 1-2 个参数的脚本
2. 优先选择同数据集的脚本
3. 优先选择同 bd_size 的脚本

### Step 3: 分析基础脚本的关键参数

```bash
# 提取关键配置行
grep -E "^(DATASET_PATH|BD_SIZE|LR|MAX_LENGTH|RUN_ID|SLURM.*array)=" base_script.sh
```

需要关注的参数：
```bash
# SLURM 配置
#SBATCH -J train_xxx                    # Job name
#SBATCH --array=1-10%1                  # 重提交次数 (sharegpt4v=10, j63=30)
#SBATCH -e workdir/xxx_error.log        # Error log
#SBATCH -o workdir/xxx_out.log          # Output log

# 训练参数
DATASET_PATH="${DATASET_PATH:-sharegpt4v_recipe}"
MAX_LENGTH="${MAX_LENGTH:-2048}"        # sharegpt4v=2048, j63=2500
BD_SIZE="${BD_SIZE:-16}"
LR="${LR:-1e-6}"
RUN_ID="${RUN_ID:-experiment_name}"

# 模式开关
ANNEAL_BLOCK_SIZE="${ANNEAL_BLOCK_SIZE:-1}"
USE_BLOCK_CAUSAL_MASK="${USE_BLOCK_CAUSAL_MASK:-1}"
FLEXIBLE_BD_SIZE="${FLEXIBLE_BD_SIZE:-0}"
```

### Step 4: 使用 diff 确认差异

```bash
# 对比两个相似脚本，理解命名规律
diff script_a.sh script_b.sh
```

### Step 5: 复制 + sed 创建新脚本

#### 5.1 复制基础脚本

```bash
cp chengyue_script/dvlm/training/multinode/mdm/sharegpt4v/sweep_flexible_constant_and_block_causal/train_BASE.sh \
   chengyue_script/dvlm/training/multinode/mdm/sharegpt4v/sweep_flexible_constant_and_block_causal/train_NEW.sh
```

#### 5.2 sed 替换

**重要**: 使用 `-i` 原地修改，多个 `-e` 链式替换

```bash
sed -i \
    -e 's/OLD_RUN_ID/NEW_RUN_ID/g' \
    -e 's/DATASET_PATH:-old_dataset/DATASET_PATH:-new_dataset/g' \
    -e 's/BD_SIZE:-16/BD_SIZE:-32/g' \
    -e 's/LR:-1e-6/LR:-1e-5/g' \
    -e 's/MAX_LENGTH:-2048/MAX_LENGTH:-2500/g' \
    -e 's/array=1-10%1/array=1-30%1/g' \
    -e 's/Old Description/New Description/g' \
    train_NEW.sh
```

#### 5.3 常用 sed 替换模式

| 变更类型 | sed 命令 |
|----------|----------|
| 改数据集 | `s/DATASET_PATH:-old/DATASET_PATH:-new/g` |
| 改 BD_SIZE | `s/BD_SIZE:-16/BD_SIZE:-32/g` |
| 改学习率 | `s/LR:-1e-6/LR:-1e-5/g` |
| 改 MAX_LENGTH | `s/MAX_LENGTH:-2048/MAX_LENGTH:-2500/g` |
| 改 SLURM array | `s/array=1-10%1/array=1-30%1/g` |
| 改实验名 (全局) | `s/old_exp_name/new_exp_name/g` |

### Step 6: 验证新脚本

```bash
# 1. 确认 diff 只有预期的变更
diff train_BASE.sh train_NEW.sh

# 2. 检查关键参数
grep -E "^(DATASET_PATH|BD_SIZE|LR|RUN_ID)=" train_NEW.sh

# 3. 检查文件名与 RUN_ID 一致
grep "RUN_ID=" train_NEW.sh
```

### Step 7: 输出总结

```markdown
## 新脚本创建完成

**新脚本路径**:
`chengyue_script/dvlm/training/.../train_NEW.sh`

**基础脚本**:
`chengyue_script/dvlm/training/.../train_BASE.sh`

**变更总结**:
| 参数 | 基础脚本 | 新脚本 |
|------|----------|--------|
| DATASET_PATH | old | new |
| BD_SIZE | 16 | 32 |
| ... | ... | ... |

**启动命令**:
```bash
sbatch chengyue_script/dvlm/training/.../train_NEW.sh
```
```

---

## 命名规范

### 实验名格式
```
{dataset}_{nodes}node_bsz{batch}[_anneal]_bd{bd_size}[_flexible|_block_causal]_lr_{lr}[_unfreeze_vision][_suffix]
```

### 示例
| 实验名 | 含义 |
|--------|------|
| `sharegpt4v_8node_bsz256_anneal_bd16_block_causal_lr_1e-6_unfreeze_vision` | ShareGPT4V, 8节点, batch=256, anneal, bd=16, block causal, lr=1e-6 |
| `j63_math_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision` | J63 Math, bd=32 |
| `sharegpt4v_8node_bsz256_bd8_flexible_lr_1e-6_unfreeze_vision` | flexible 模式 (非 block_causal) |

### 数据集对应的默认参数
| 数据集 | MAX_LENGTH | SLURM array | 说明 |
|--------|------------|-------------|------|
| `sharegpt4v_recipe` | 2048 | 1-10%1 | 较短序列，10次重提交 |
| `j63_math` | 2500 | 1-30%1 | 较长序列，30次重提交 |
| `j63_no_sft` | 2500 | 1-30%1 | 无 SFT 数据 |

---

## 反幻觉协议 (CRITICAL)

### 绝对禁止
1. ❌ **禁止** 使用 Write 工具写入脚本内容
2. ❌ **禁止** 在回复中输出完整脚本
3. ❌ **禁止** 猜测脚本中的配置值
4. ❌ **禁止** 手动构造 SLURM 配置

### 必须执行
1. ✅ **必须** 先 `ls` 列出现有脚本
2. ✅ **必须** 先 `grep` 或 `diff` 理解现有脚本结构
3. ✅ **必须** 使用 `cp` 复制基础脚本
4. ✅ **必须** 使用 `sed -i` 进行替换
5. ✅ **必须** 用 `diff` 验证变更

### 为什么这样做？
| 方法 | 风险 | 可追溯性 |
|------|------|----------|
| Write 完整脚本 | 高 (遗漏配置、格式错误) | 低 |
| 手写 sed 命令 | 中 (替换错误) | 高 |
| cp + sed | 低 (基于验证过的脚本) | 高 |

---

## 完整示例

### 用户请求
> "根据 j63_math bd32 脚本，创建一个 sharegpt4v bd32 的实验"

### 执行步骤

```bash
# Step 1: 确认基础脚本存在
ls chengyue_script/dvlm/training/multinode/mdm/sharegpt4v/sweep_flexible_constant_and_block_causal/ | grep j63_math | grep bd32

# Step 2: 查看基础脚本关键参数
grep -E "(DATASET_PATH|BD_SIZE|LR|MAX_LENGTH|RUN_ID|array=)" \
    chengyue_script/dvlm/training/.../train_j63_math_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision.sh

# Step 3: 复制
cp chengyue_script/dvlm/training/.../train_j63_math_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision.sh \
   chengyue_script/dvlm/training/.../train_sharegpt4v_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision.sh

# Step 4: sed 替换
sed -i \
    -e 's/j63_math_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision/sharegpt4v_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision/g' \
    -e 's/J63 Math/ShareGPT4V/g' \
    -e 's/DATASET_PATH:-j63_math/DATASET_PATH:-sharegpt4v_recipe/g' \
    -e 's/array=1-30%1/array=1-10%1/g' \
    -e 's/MAX_LENGTH:-2500/MAX_LENGTH:-2048/g' \
    chengyue_script/dvlm/training/.../train_sharegpt4v_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision.sh

# Step 5: 验证
diff chengyue_script/dvlm/training/.../train_j63_math_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision.sh \
     chengyue_script/dvlm/training/.../train_sharegpt4v_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision.sh
```

### 输出
```markdown
## 新脚本创建完成

**新脚本**: `train_sharegpt4v_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision.sh`
**基础脚本**: `train_j63_math_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision.sh`

| 参数 | j63_math (基础) | sharegpt4v (新) |
|------|-----------------|-----------------|
| DATASET_PATH | `j63_math` | `sharegpt4v_recipe` |
| MAX_LENGTH | 2500 | 2048 |
| SLURM array | 1-30%1 | 1-10%1 |

**保持不变**: BD_SIZE=32, LR=1e-6, ANNEAL=1, BLOCK_CAUSAL=1

**启动**: `sbatch .../train_sharegpt4v_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision.sh`
```

---

## 脚本目录结构

```
chengyue_script/dvlm/training/
├── multinode/
│   ├── mdm/
│   │   └── sharegpt4v/
│   │       └── sweep_flexible_constant_and_block_causal/
│   │           ├── train_sharegpt4v_8node_bsz256_anneal_bd16_block_causal_lr_1e-6_unfreeze_vision.sh
│   │           ├── train_sharegpt4v_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision.sh  # NEW
│   │           ├── train_j63_math_8node_bsz256_anneal_bd32_block_causal_lr_1e-6_unfreeze_vision.sh
│   │           └── ...
│   └── ar/
│       └── sharegpt4v/
│           └── train_sharegpt4v_ar_baseline_8node_bsz256_lr_1e-6.sh
└── single_node/
    └── ...
```

---

## 常见错误及修复

### 错误 1: sed 替换不完整
**症状**: RUN_ID 和文件名不匹配
**修复**: 使用 `s/old/new/g` 全局替换，确保所有出现都被替换

### 错误 2: 忘记改 SLURM 日志路径
**症状**: 日志写到错误的文件
**修复**: RUN_ID 全局替换会自动处理 `-e` 和 `-o` 路径

### 错误 3: 数据集和 MAX_LENGTH 不匹配
**症状**: OOM 或序列截断
**修复**: 参考数据集默认参数表

### 错误 4: 手写脚本遗漏信号处理
**症状**: SLURM 超时后无法正确保存 checkpoint
**修复**: 始终从现有脚本 cp，不要手写
