cleaning code
This commit is contained in:
@@ -1,64 +0,0 @@
|
||||
ray:
|
||||
# Ray Job server address (head 容器内视角)
|
||||
address: "http://127.0.0.1:8265"
|
||||
|
||||
# 共享根路径(容器内统一 /private,对齐生产)
|
||||
shared_root: "/private"
|
||||
|
||||
# 强制 driver 落 worker(head 不跑训练)
|
||||
entrypoint_num_cpus: 1
|
||||
entrypoint_resources:
|
||||
worker_node: 1
|
||||
|
||||
# 所有 job 通用 runtime env
|
||||
runtime_env:
|
||||
env_vars:
|
||||
HF_ENDPOINT: "https://hf-mirror.com"
|
||||
PYTHONUNBUFFERED: "1"
|
||||
# v3.7: forbid HuggingFace Hub network access from Ray jobs (use cached snapshots).
|
||||
HF_HUB_OFFLINE: "1"
|
||||
# Unify cache dirs so `from_pretrained("org/name")` resolves from the same on-disk cache in offline mode.
|
||||
HF_HOME: "/private/hf"
|
||||
HUGGINGFACE_HUB_CACHE: "/private/hf/hub"
|
||||
TRANSFORMERS_CACHE: "/private/hf/hub"
|
||||
|
||||
# v3.0 先不支持 user code 执行
|
||||
user_code_path: "/private/user/code"
|
||||
|
||||
service:
|
||||
api:
|
||||
host: "0.0.0.0"
|
||||
port: 8080
|
||||
auth:
|
||||
token_env: "MVP_INTERNAL_TOKEN"
|
||||
sqlite:
|
||||
db_path: "/private/common/db/mvp.sqlite3"
|
||||
scheduler:
|
||||
tick_s: 5
|
||||
retry_interval_s: 60
|
||||
max_running_tasks: 1
|
||||
|
||||
tracking:
|
||||
wandb:
|
||||
enabled: true
|
||||
# For dev compose, recommend docker bridge gateway + host published port for stability.
|
||||
base_url: "http://172.22.0.1:8090"
|
||||
api_key_env: "WANDB_API_KEY"
|
||||
project_suffix: "_project"
|
||||
|
||||
data:
|
||||
user_root: "/private/users"
|
||||
sftpgo:
|
||||
enabled: true
|
||||
# Returned to users by GET /api/v2/me. For h1 E2E, usually connect to the host IP.
|
||||
host: "127.0.0.1"
|
||||
sftp_port: 2022
|
||||
# Admin API base should include /api/v2 (SFTPGo OpenAPI server base).
|
||||
# From head container, access SFTPGo by service name on the compose network.
|
||||
admin_api_base: "http://argus-sftpgo:8080/api/v2"
|
||||
admin_user: "admin"
|
||||
admin_password_env: "SFTPGO_ADMIN_PASSWORD"
|
||||
retention:
|
||||
jobs_trash_after_days: 3
|
||||
jobs_purge_after_days: 7
|
||||
janitor_interval_s: 3600
|
||||
@@ -1,118 +0,0 @@
|
||||
# MVP V1(Ray + VERL PPO)实验脚本
|
||||
|
||||
本目录用于在“宿主机 + Docker 容器”环境下,**用宿主机脚本(`docker exec`)**协调启动 Ray 集群,并通过 **`ray job submit`(在 head 提交)**跑通一次 `verl` 的 PPO 训练闭环(`total_epochs=1`),且数据/模型/日志/ckpt 都持久化到宿主机目录。
|
||||
|
||||
## 1. 运行环境与拓扑
|
||||
|
||||
### 1.1 依赖
|
||||
|
||||
- 宿主机:Linux
|
||||
- 必需工具:`docker`、`docker compose`(Compose v2 插件)、`git`
|
||||
- GPU:至少 8 张可用 GPU(索引 `0-7`),Docker 的 NVIDIA runtime 可用
|
||||
|
||||
### 1.2 集群拓扑(3 个容器)
|
||||
|
||||
- `mvp-ray-head`(Ray Head)
|
||||
- **不挂 GPU**(容器内 `nvidia-smi` 不可用)
|
||||
- `ray start --head --num-cpus=0 --num-gpus=0`:head 只做控制面,不参与计算调度
|
||||
- 暴露 dashboard:宿主机端口 `8265`
|
||||
- `mvp-ray-worker-0`(Ray Worker)
|
||||
- 4 GPU:`0,1,2,3`
|
||||
- `ray start ... --resources='{"worker_node": 100}'`
|
||||
- `mvp-ray-worker-1`(Ray Worker)
|
||||
- 4 GPU:`4,5,6,7`
|
||||
- `ray start ... --resources='{"worker_node": 100}'`
|
||||
|
||||
**关键点:driver 不在 head**
|
||||
|
||||
- 作业通过 head 提交:`ray job submit ...`
|
||||
- 通过 `--entrypoint-resources='{"worker_node": 1}'` 强制 entrypoint/driver 只能调度到 worker(head 没有该资源)
|
||||
|
||||
## 2. 持久化目录(宿主机 <-> 容器)
|
||||
|
||||
在宿主机项目根目录(运行脚本时的 `${PWD}`)下使用 `./shared` 做持久化根目录,并 bind mount 到容器内 `/mnt/shared`:
|
||||
|
||||
- 宿主机:`./shared`
|
||||
- 容器:`/mnt/shared`
|
||||
|
||||
主要内容:
|
||||
|
||||
- 数据集:`/mnt/shared/datasets/gsm8k/`
|
||||
- HF 缓存:`/mnt/shared/hf/`(脚本会设置 `HF_HOME`,并尽量幂等跳过重复下载)
|
||||
- 每个 Ray Job 的输出(按 submission id 分目录):
|
||||
- `/mnt/shared/jobs/<submission_id>/logs/`
|
||||
- `/mnt/shared/jobs/<submission_id>/checkpoints/`
|
||||
|
||||
## 3. 整体流程(代码逻辑)
|
||||
|
||||
脚本都在 `src/mvp/v1/scripts/`,整体顺序如下:
|
||||
|
||||
1) `00_prereq_check.sh`
|
||||
- 检查 `docker/docker compose/git`
|
||||
2) `05_ensure_verl_repo.sh`
|
||||
- 若项目根目录下没有 `./verl`,自动 `git clone https://github.com/volcengine/verl.git`
|
||||
3) `01_up.sh`
|
||||
- 创建持久化目录(`./shared/...`)
|
||||
- `docker compose up -d` 启动 3 个容器
|
||||
4) `10_install_verl_editable.sh`
|
||||
- 在 3 个容器内执行 `pip install -e /workspace/verl`(确保 `python -m verl...` 可用且代码与 `./verl` 同步)
|
||||
5) `20_start_head.sh`
|
||||
- 在 `mvp-ray-head` 内启动 Ray head(CPU=0、GPU=0)
|
||||
6) `21_start_workers.sh`
|
||||
- 在两个 worker 内启动 Ray worker 加入集群
|
||||
- 同时给 worker 打 `worker_node` 自定义资源标签
|
||||
7) `30_prepare_data_and_model.sh`
|
||||
- 数据集:若 `train.parquet/test.parquet` 已存在则跳过,否则生成
|
||||
- 模型:使用 HF cache(`HF_HOME=/mnt/shared/hf`),存在则跳过,不存在才下载
|
||||
8) `40_submit_ppo_epoch1.sh`
|
||||
- 在 head 容器里执行 `ray job submit`
|
||||
- 显式指定 `--submission-id=$SUBMISSION_ID`
|
||||
- 通过 `--entrypoint-resources='{"worker_node": 1}'` 强制 driver 在 worker
|
||||
- 训练参数:
|
||||
- `trainer.total_epochs=1`
|
||||
- `trainer.total_training_steps=29`(GSM8K 该配置下对应 29 steps)
|
||||
- `trainer.save_freq=10`(每 10 step 保存一次 checkpoint,避免磁盘爆炸)
|
||||
- `trainer.default_local_dir=/mnt/shared/jobs/$SUBMISSION_ID/checkpoints`
|
||||
- `hydra.run.dir=/mnt/shared/jobs/$SUBMISSION_ID/logs/hydra`
|
||||
9) `50_status.sh`
|
||||
- 打印 `ray status` / `ray job list` / `ray job status` / `ray job logs | tail`
|
||||
|
||||
## 4. 运行方法
|
||||
|
||||
### 4.1 一键执行
|
||||
|
||||
在项目根目录执行:
|
||||
|
||||
- `./src/mvp/v1/scripts/run_all.sh`
|
||||
|
||||
### 4.2 分步执行(推荐)
|
||||
|
||||
按顺序执行:
|
||||
|
||||
- `./src/mvp/v1/scripts/01_up.sh`
|
||||
- `./src/mvp/v1/scripts/10_install_verl_editable.sh`
|
||||
- `./src/mvp/v1/scripts/20_start_head.sh`
|
||||
- `./src/mvp/v1/scripts/21_start_workers.sh`
|
||||
- `./src/mvp/v1/scripts/30_prepare_data_and_model.sh`
|
||||
- `SUBMISSION_ID=ppo_h20_8g_$(date +%Y%m%d_%H%M%S) ./src/mvp/v1/scripts/40_submit_ppo_epoch1.sh`
|
||||
- `./src/mvp/v1/scripts/50_status.sh`
|
||||
|
||||
### 4.3 查看与停止
|
||||
|
||||
- Dashboard:`http://<宿主机IP>:8265`
|
||||
- 列出作业(在 head 容器内):
|
||||
- `docker exec mvp-ray-head bash -lc "ray job list --address=http://127.0.0.1:8265"`
|
||||
- 停止某个 submission id:
|
||||
- `docker exec mvp-ray-head bash -lc "ray job stop --address=http://127.0.0.1:8265 <submission_id>"`
|
||||
|
||||
### 4.4 清理
|
||||
|
||||
- 停止并删除容器:`./src/mvp/v1/scripts/02_down.sh`
|
||||
- 清理输出(谨慎,数据量可能很大):删除 `./shared/jobs/<submission_id>/`
|
||||
|
||||
## 5. 常见坑
|
||||
|
||||
- **不传 `--submission-id` 会导致“输出目录难以等于 submission id”**:因为 hydra/ckpt 目录需要在提交前确定。当前脚本会显式传 `--submission-id=$SUBMISSION_ID`,并使用同名目录。
|
||||
- **checkpoint 太大**:PPO 的 checkpoint 非常占空间。当前脚本默认 `save_freq=10`,如仍过大,可调大 `save_freq` 或减少保存内容/频率。
|
||||
|
||||
更多分步操作与验收标准见:`specs/mvp/v1_action.md`
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,86 +0,0 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
ray_head:
|
||||
image: verlai/verl:sgl055.latest
|
||||
container_name: mvp-ray-head
|
||||
command: sleep infinity
|
||||
ports:
|
||||
- "8265:8265"
|
||||
volumes:
|
||||
- ./verl:/workspace/verl
|
||||
- ./shared:/mnt/shared
|
||||
shm_size: "10g"
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
- SYS_PTRACE
|
||||
networks:
|
||||
- mvp-ray-net
|
||||
environment:
|
||||
HF_HOME: "/mnt/shared/hf"
|
||||
HUGGINGFACE_HUB_CACHE: "/mnt/shared/hf/hub"
|
||||
TRANSFORMERS_CACHE: "/mnt/shared/hf/transformers"
|
||||
HF_ENDPOINT: "https://hf-mirror.com"
|
||||
PYTHONUNBUFFERED: "1"
|
||||
|
||||
ray_worker_0:
|
||||
image: verlai/verl:sgl055.latest
|
||||
container_name: mvp-ray-worker-0
|
||||
command: sleep infinity
|
||||
volumes:
|
||||
- ./verl:/workspace/verl
|
||||
- ./shared:/mnt/shared
|
||||
shm_size: "10g"
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
- SYS_PTRACE
|
||||
networks:
|
||||
- mvp-ray-net
|
||||
runtime: nvidia
|
||||
environment:
|
||||
NVIDIA_VISIBLE_DEVICES: "0,1,2,3"
|
||||
NVIDIA_DRIVER_CAPABILITIES: "all"
|
||||
HF_HOME: "/mnt/shared/hf"
|
||||
HUGGINGFACE_HUB_CACHE: "/mnt/shared/hf/hub"
|
||||
TRANSFORMERS_CACHE: "/mnt/shared/hf/transformers"
|
||||
HF_ENDPOINT: "https://hf-mirror.com"
|
||||
PYTHONUNBUFFERED: "1"
|
||||
|
||||
ray_worker_1:
|
||||
image: verlai/verl:sgl055.latest
|
||||
container_name: mvp-ray-worker-1
|
||||
command: sleep infinity
|
||||
volumes:
|
||||
- ./verl:/workspace/verl
|
||||
- ./shared:/mnt/shared
|
||||
shm_size: "10g"
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
- SYS_PTRACE
|
||||
networks:
|
||||
- mvp-ray-net
|
||||
runtime: nvidia
|
||||
environment:
|
||||
NVIDIA_VISIBLE_DEVICES: "4,5,6,7"
|
||||
NVIDIA_DRIVER_CAPABILITIES: "all"
|
||||
HF_HOME: "/mnt/shared/hf"
|
||||
HUGGINGFACE_HUB_CACHE: "/mnt/shared/hf/hub"
|
||||
TRANSFORMERS_CACHE: "/mnt/shared/hf/transformers"
|
||||
HF_ENDPOINT: "https://hf-mirror.com"
|
||||
PYTHONUNBUFFERED: "1"
|
||||
|
||||
networks:
|
||||
mvp-ray-net:
|
||||
driver: bridge
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=lib.sh
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
require_cmd docker
|
||||
|
||||
require_cmd git
|
||||
|
||||
if ! docker compose version >/dev/null 2>&1; then
|
||||
echo "docker compose plugin not available; please install docker compose v2" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "OK: docker + docker compose + git"
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=lib.sh
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
"${SCRIPT_DIR}/00_prereq_check.sh"
|
||||
"${SCRIPT_DIR}/05_ensure_verl_repo.sh"
|
||||
|
||||
mkdir -p \
|
||||
"${ROOT_DIR}/shared/hf" \
|
||||
"${ROOT_DIR}/shared/datasets" \
|
||||
"${ROOT_DIR}/shared/jobs" \
|
||||
"${ROOT_DIR}/shared/outputs" \
|
||||
"${ROOT_DIR}/shared/ray"
|
||||
|
||||
dc up -d
|
||||
dc ps
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=lib.sh
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
dc down
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=lib.sh
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
VERL_DIR="${ROOT_DIR}/verl"
|
||||
|
||||
if [[ -d "${VERL_DIR}/.git" ]]; then
|
||||
echo "OK: verl repo exists: ${VERL_DIR}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "verl repo not found at ${VERL_DIR}; cloning..."
|
||||
rm -rf "${VERL_DIR}"
|
||||
git clone https://github.com/volcengine/verl.git "${VERL_DIR}"
|
||||
echo "OK: cloned verl -> ${VERL_DIR}"
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=lib.sh
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
install_one() {
|
||||
local name="$1"
|
||||
echo "[${name}] ensure verl importable"
|
||||
if ! dexec "${name}" python3 -c "import verl; print(verl.__file__)" >/dev/null 2>&1; then
|
||||
echo "[${name}] verl not importable; installing editable from /workspace/verl"
|
||||
dexec "${name}" bash -lc "pip install -e /workspace/verl"
|
||||
else
|
||||
echo "[${name}] verl import OK"
|
||||
fi
|
||||
}
|
||||
|
||||
install_one "${HEAD_CONTAINER}"
|
||||
install_one "${WORKER0_CONTAINER}"
|
||||
install_one "${WORKER1_CONTAINER}"
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=lib.sh
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
echo "[head] ray stop (ignore errors)"
|
||||
dexec "${HEAD_CONTAINER}" bash -lc "ray stop --force || true"
|
||||
|
||||
echo "[head] ray start (CPU=0, GPU=0 to prevent scheduling on head)"
|
||||
HEAD_IP="$(container_ip "${HEAD_CONTAINER}")"
|
||||
echo "[head] container ip: ${HEAD_IP}"
|
||||
dexec "${HEAD_CONTAINER}" bash -lc "ray start --head --node-ip-address=${HEAD_IP} --dashboard-host=0.0.0.0 --dashboard-port=8265 --port=6379 --num-cpus=0 --num-gpus=0"
|
||||
|
||||
echo "[head] ray status"
|
||||
dexec "${HEAD_CONTAINER}" bash -lc "ray status || true"
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=lib.sh
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
start_worker() {
|
||||
local name="$1"
|
||||
local node_ip
|
||||
node_ip="$(container_ip "${name}")"
|
||||
echo "[${name}] ray stop (ignore errors)"
|
||||
dexec "${name}" bash -lc "ray stop --force || true"
|
||||
|
||||
local head_ip
|
||||
head_ip="$(container_ip "${HEAD_CONTAINER}")"
|
||||
echo "[${name}] container ip: ${node_ip}"
|
||||
echo "[${name}] ray start -> join ${head_ip}:6379 (num_gpus=4, resources worker_node=100)"
|
||||
dexec "${name}" bash -lc "ray start --node-ip-address=${node_ip} --address=${head_ip}:6379 --num-gpus=4 --resources='{\"worker_node\": 100}'"
|
||||
}
|
||||
|
||||
start_worker "${WORKER0_CONTAINER}"
|
||||
start_worker "${WORKER1_CONTAINER}"
|
||||
|
||||
echo "[head] waiting for workers to register"
|
||||
for _ in $(seq 1 30); do
|
||||
if dexec "${HEAD_CONTAINER}" bash -lc "ray status" | grep -q "Active:"; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
dexec "${HEAD_CONTAINER}" bash -lc "ray status || true"
|
||||
@@ -1,37 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=lib.sh
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
DATA_DIR="/mnt/shared/datasets/gsm8k"
|
||||
MODEL_ID="Qwen/Qwen2.5-0.5B-Instruct"
|
||||
|
||||
echo "[head] prepare dataset (idempotent) -> ${DATA_DIR}"
|
||||
dexec "${HEAD_CONTAINER}" bash -lc "mkdir -p ${DATA_DIR} && if [[ -f ${DATA_DIR}/train.parquet && -f ${DATA_DIR}/test.parquet ]]; then echo 'dataset_exists: skip'; else python3 /workspace/verl/examples/data_preprocess/gsm8k.py --local_save_dir ${DATA_DIR}; fi"
|
||||
|
||||
echo "[head] ensure model cached to persistent HF_HOME (idempotent) -> ${MODEL_ID}"
|
||||
PY_CODE="$(cat <<'PY'
|
||||
import os
|
||||
|
||||
model_id = os.environ["MODEL_ID"]
|
||||
|
||||
hf_home = os.environ.get("HF_HOME", "/mnt/shared/hf")
|
||||
os.environ.setdefault("HF_HOME", hf_home)
|
||||
os.environ.setdefault("HUGGINGFACE_HUB_CACHE", os.path.join(hf_home, "hub"))
|
||||
os.environ.setdefault("TRANSFORMERS_CACHE", os.path.join(hf_home, "transformers"))
|
||||
|
||||
from huggingface_hub import snapshot_download
|
||||
|
||||
try:
|
||||
snapshot_download(repo_id=model_id, local_files_only=True)
|
||||
print("model_cache_exists: skip", model_id)
|
||||
except Exception:
|
||||
print("model_cache_missing: downloading", model_id)
|
||||
snapshot_download(repo_id=model_id)
|
||||
print("model_cached_ok:", model_id)
|
||||
PY
|
||||
)"
|
||||
|
||||
printf "%s\n" "${PY_CODE}" | dexec "${HEAD_CONTAINER}" bash -lc "MODEL_ID='${MODEL_ID}' python3 -"
|
||||
@@ -1,70 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=lib.sh
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
SUBMISSION_ID="${SUBMISSION_ID:-mvp_ppo_$(timestamp)_$RANDOM}"
|
||||
# 为了让“输出目录 = submission id”,默认把 JOB_TAG 也设成 SUBMISSION_ID(可手动覆盖)。
|
||||
JOB_TAG="${JOB_TAG:-${SUBMISSION_ID}}"
|
||||
JOB_DIR="/mnt/shared/jobs/${SUBMISSION_ID}"
|
||||
|
||||
MODEL_ID="Qwen/Qwen2.5-0.5B-Instruct"
|
||||
TRAIN_FILE="/mnt/shared/datasets/gsm8k/train.parquet"
|
||||
VAL_FILE="/mnt/shared/datasets/gsm8k/test.parquet"
|
||||
|
||||
echo "[head] create job dir: ${JOB_DIR}"
|
||||
dexec "${HEAD_CONTAINER}" bash -lc "mkdir -p ${JOB_DIR}/logs ${JOB_DIR}/checkpoints ${JOB_DIR}/config"
|
||||
|
||||
SUBMIT_CMD="python3 -m verl.trainer.main_ppo \
|
||||
data.train_files=${TRAIN_FILE} \
|
||||
data.val_files=${VAL_FILE} \
|
||||
data.train_batch_size=256 \
|
||||
data.max_prompt_length=512 \
|
||||
data.max_response_length=512 \
|
||||
actor_rollout_ref.model.path=${MODEL_ID} \
|
||||
actor_rollout_ref.actor.optim.lr=1e-6 \
|
||||
actor_rollout_ref.actor.ppo_mini_batch_size=64 \
|
||||
actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu=4 \
|
||||
actor_rollout_ref.rollout.name=sglang \
|
||||
actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu=8 \
|
||||
actor_rollout_ref.rollout.tensor_model_parallel_size=1 \
|
||||
actor_rollout_ref.rollout.gpu_memory_utilization=0.4 \
|
||||
actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpu=4 \
|
||||
critic.optim.lr=1e-5 \
|
||||
critic.model.path=${MODEL_ID} \
|
||||
critic.ppo_micro_batch_size_per_gpu=4 \
|
||||
algorithm.kl_ctrl.kl_coef=0.001 \
|
||||
trainer.logger=console \
|
||||
trainer.val_before_train=False \
|
||||
trainer.n_gpus_per_node=4 \
|
||||
trainer.nnodes=2 \
|
||||
trainer.save_freq=10 \
|
||||
trainer.test_freq=29 \
|
||||
trainer.total_epochs=1 \
|
||||
trainer.total_training_steps=29 \
|
||||
trainer.resume_mode=disable \
|
||||
trainer.default_local_dir=${JOB_DIR}/checkpoints \
|
||||
+ray_kwargs.ray_init.address=auto \
|
||||
hydra.run.dir=${JOB_DIR}/logs/hydra"
|
||||
|
||||
printf "%s\n" "${SUBMIT_CMD}" | dexec "${HEAD_CONTAINER}" bash -lc "cat > ${JOB_DIR}/config/submit_cmd.txt"
|
||||
|
||||
echo "[head] submit PPO via ray job submit (force driver on worker via entrypoint resources)"
|
||||
|
||||
SUBMIT_OUT="$(dexec "${HEAD_CONTAINER}" bash -lc "export HF_HOME=/mnt/shared/hf HUGGINGFACE_HUB_CACHE=/mnt/shared/hf/hub TRANSFORMERS_CACHE=/mnt/shared/hf/transformers HF_ENDPOINT=https://hf-mirror.com PYTHONUNBUFFERED=1; ray job submit --address=http://127.0.0.1:8265 --submission-id='${SUBMISSION_ID}' --entrypoint-num-cpus=1 --entrypoint-resources='{\"worker_node\": 1}' --runtime-env-json='{\"env_vars\":{\"HF_HOME\":\"/mnt/shared/hf\",\"HUGGINGFACE_HUB_CACHE\":\"/mnt/shared/hf/hub\",\"TRANSFORMERS_CACHE\":\"/mnt/shared/hf/transformers\",\"HF_ENDPOINT\":\"https://hf-mirror.com\",\"PYTHONUNBUFFERED\":\"1\"}}' --no-wait -- ${SUBMIT_CMD}")"
|
||||
|
||||
printf "%s\n" "${SUBMIT_OUT}"
|
||||
printf "%s\n" "${SUBMIT_OUT}" | dexec "${HEAD_CONTAINER}" bash -lc "cat > ${JOB_DIR}/logs/ray_job_submit.out"
|
||||
|
||||
PARSED_SUBMISSION_ID="$(printf "%s\n" "${SUBMIT_OUT}" | sed -r 's/\x1b\\[[0-9;]*m//g' | grep -Eo "raysubmit_[A-Za-z0-9_-]+" | head -n 1 || true)"
|
||||
if [[ -n "${PARSED_SUBMISSION_ID}" && "${PARSED_SUBMISSION_ID}" != "${SUBMISSION_ID}" ]]; then
|
||||
echo "WARN: submission id mismatch: expected=${SUBMISSION_ID} parsed=${PARSED_SUBMISSION_ID}" >&2
|
||||
fi
|
||||
|
||||
echo "${SUBMISSION_ID}" | dexec "${HEAD_CONTAINER}" bash -lc "cat > ${JOB_DIR}/config/ray_submission_id.txt"
|
||||
echo "ray submission id: ${SUBMISSION_ID}"
|
||||
|
||||
echo "submitted. track via Ray dashboard: http://<host>:8265 (driver should be scheduled on a worker due to entrypoint resources)"
|
||||
echo "job dir: ${JOB_DIR}"
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=lib.sh
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
echo "[head] ray status"
|
||||
dexec "${HEAD_CONTAINER}" bash -lc "ray status || true"
|
||||
|
||||
echo "[head] ray jobs list (optional)"
|
||||
dexec "${HEAD_CONTAINER}" bash -lc "ray job list --address=http://127.0.0.1:8265 || true"
|
||||
|
||||
LATEST_JOB_DIR="$(dexec "${HEAD_CONTAINER}" bash -lc "ls -1dt /mnt/shared/jobs/* 2>/dev/null | head -n 1 || true")"
|
||||
if [[ -n "${LATEST_JOB_DIR}" ]]; then
|
||||
echo "[host] latest job dir: ${LATEST_JOB_DIR}"
|
||||
echo "[host] ray submission id (if exists):"
|
||||
SUB_ID="$(dexec "${HEAD_CONTAINER}" bash -lc "cat ${LATEST_JOB_DIR}/config/ray_submission_id.txt 2>/dev/null || true")"
|
||||
echo "${SUB_ID}"
|
||||
if [[ -n "${SUB_ID}" ]]; then
|
||||
echo "[head] ray job status:"
|
||||
dexec "${HEAD_CONTAINER}" bash -lc "ray job status --address=http://127.0.0.1:8265 ${SUB_ID} || true"
|
||||
echo "[head] ray job logs (tail):"
|
||||
dexec "${HEAD_CONTAINER}" bash -lc "ray job logs --address=http://127.0.0.1:8265 ${SUB_ID} 2>/dev/null | tail -n 60 || true"
|
||||
fi
|
||||
fi
|
||||
@@ -1,48 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/../../../../" && pwd)"
|
||||
|
||||
COMPOSE_FILE="${ROOT_DIR}/src/mvp/v1/docker-compose.yaml"
|
||||
|
||||
HEAD_CONTAINER="mvp-ray-head"
|
||||
WORKER0_CONTAINER="mvp-ray-worker-0"
|
||||
WORKER1_CONTAINER="mvp-ray-worker-1"
|
||||
|
||||
dc() {
|
||||
docker compose --project-directory "${ROOT_DIR}" -f "${COMPOSE_FILE}" "$@"
|
||||
}
|
||||
|
||||
require_cmd() {
|
||||
local cmd="$1"
|
||||
command -v "${cmd}" >/dev/null 2>&1 || {
|
||||
echo "missing required command: ${cmd}" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
ensure_container_running() {
|
||||
local name="$1"
|
||||
if ! docker ps --format '{{.Names}}' | grep -qx "${name}"; then
|
||||
echo "container not running: ${name}" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
dexec() {
|
||||
local name="$1"
|
||||
shift
|
||||
ensure_container_running "${name}"
|
||||
docker exec -i "${name}" "$@"
|
||||
}
|
||||
|
||||
container_ip() {
|
||||
local name="$1"
|
||||
ensure_container_running "${name}"
|
||||
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${name}"
|
||||
}
|
||||
|
||||
timestamp() {
|
||||
date +"%Y%m%d_%H%M%S"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
"${SCRIPT_DIR}/01_up.sh"
|
||||
"${SCRIPT_DIR}/10_install_verl_editable.sh"
|
||||
"${SCRIPT_DIR}/20_start_head.sh"
|
||||
"${SCRIPT_DIR}/21_start_workers.sh"
|
||||
"${SCRIPT_DIR}/30_prepare_data_and_model.sh"
|
||||
"${SCRIPT_DIR}/40_submit_ppo_epoch1.sh"
|
||||
"${SCRIPT_DIR}/50_status.sh"
|
||||
|
||||
Reference in New Issue
Block a user