| #!/bin/bash |
| |
| |
| |
| |
| |
| |
| |
| set -euo pipefail |
|
|
| |
| |
| |
| CONFIG="" |
| SUBMIT=false |
| DRY_RUN=false |
|
|
| while [[ $# -gt 0 ]]; do |
| case "$1" in |
| --config) |
| CONFIG="$2"; shift 2 ;; |
| --config=*) |
| CONFIG="${1#*=}"; shift ;; |
| --submit) |
| SUBMIT=true; shift ;; |
| --dry-run) |
| DRY_RUN=true; shift ;; |
| -h|--help) |
| echo "用法: bash run.sh --config <config.yaml> [--submit] [--dry-run]" |
| echo "" |
| echo "选项:" |
| echo " --config <file> YAML 配置文件路径 (必需)" |
| echo " --submit 生成 SLURM 脚本并提交作业" |
| echo " --dry-run 仅打印训练命令,不执行" |
| exit 0 ;; |
| *) |
| echo "[ERROR] 未知参数: $1" |
| exit 1 ;; |
| esac |
| done |
|
|
| if [ -z "$CONFIG" ]; then |
| echo "[ERROR] 请指定配置文件: bash run.sh --config configs/xxx.yaml" |
| exit 1 |
| fi |
|
|
| if [ ! -f "$CONFIG" ]; then |
| echo "[ERROR] 配置文件不存在: $CONFIG" |
| exit 1 |
| fi |
|
|
| |
| |
| |
| DEMO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| REPO_ROOT="$(cd "$DEMO_DIR/.." && pwd)" |
| CONFIG_ABS="$(cd "$(dirname "$CONFIG")" && pwd)/$(basename "$CONFIG")" |
| PARSE_PY="$DEMO_DIR/_parse_config.py" |
|
|
| |
| export ONESCIENCE_DATASETS_DIR="${ONESCIENCE_DATASETS_DIR:-$REPO_ROOT}" |
| |
| export ONESCIENCE_MODELS_DIR="${ONESCIENCE_MODELS_DIR:-$REPO_ROOT}" |
|
|
| |
| |
| |
| EXP_NAME=$(python3 "$PARSE_PY" "$CONFIG_ABS" name) |
| ENV_EXPORTS=$(python3 "$PARSE_PY" "$CONFIG_ABS" env) |
| ENV_ARGS=$(python3 "$PARSE_PY" "$CONFIG_ABS" env-args) |
| DATA_FILES=$(python3 "$PARSE_PY" "$CONFIG_ABS" data-files) |
|
|
| |
| |
| |
| TIMESTAMP=$(date +%Y%m%d_%H%M%S) |
| OUTPUT_DIR="$DEMO_DIR/outputs/${EXP_NAME}_${TIMESTAMP}" |
| HYDRA_CONFIG="$OUTPUT_DIR/hydra_config.yaml" |
|
|
| |
| if $DRY_RUN; then |
| DRY_HYDRA_TMP=$(mktemp -t "uma_hydra_XXXXXX.yaml") |
| HYDRA_CONFIG="$DRY_HYDRA_TMP" |
| python3 "$PARSE_PY" "$CONFIG_ABS" hydra-config > "$HYDRA_CONFIG" |
| else |
| mkdir -p "$OUTPUT_DIR" |
| cp "$CONFIG_ABS" "$OUTPUT_DIR/config.yaml" |
| python3 "$PARSE_PY" "$CONFIG_ABS" hydra-config > "$HYDRA_CONFIG" |
| |
| |
| |
| UMA_ROOT_DIR="$(cd "$DEMO_DIR/.." && pwd)" |
| if [ -d "$UMA_ROOT_DIR/models-json" ] && [ ! -e "$OUTPUT_DIR/models" ]; then |
| ln -s "$UMA_ROOT_DIR/models-json" "$OUTPUT_DIR/models" |
| fi |
| fi |
|
|
| |
| TRAIN_CMD=$(python3 "$PARSE_PY" "$CONFIG_ABS" command "$HYDRA_CONFIG") |
|
|
| |
| |
| |
| if $DRY_RUN; then |
| echo "=========================================" |
| echo "Dry-run: $EXP_NAME" |
| echo "Config: $CONFIG_ABS" |
| echo "Output: $OUTPUT_DIR (未创建)" |
| echo "Hydra: $HYDRA_CONFIG (临时)" |
| echo "=========================================" |
| echo "" |
| echo "# 环境变量:" |
| echo "$ENV_EXPORTS" |
| echo "" |
| echo "# 训练命令:" |
| echo "$TRAIN_CMD" |
| echo "" |
| echo "# env_setup.sh 参数: $ENV_ARGS" |
| echo "# 预检路径:" |
| echo "$DATA_FILES" | sed 's/^/# /' |
| echo "" |
| echo "# hydra_config.yaml 预览 (前 40 行):" |
| head -n 40 "$HYDRA_CONFIG" | sed 's/^/# /' |
| rm -f "$HYDRA_CONFIG" |
| exit 0 |
| fi |
|
|
| |
| |
| |
| if $SUBMIT; then |
| SLURM_VARS=$(python3 "$PARSE_PY" "$CONFIG_ABS" slurm) |
| eval "$SLURM_VARS" |
|
|
| SLURM_SCRIPT="$OUTPUT_DIR/submit.sh" |
|
|
| |
| sed -e "s|{{JOB_NAME}}|${JOB_NAME}|g" \ |
| -e "s|{{PARTITION}}|${PARTITION}|g" \ |
| -e "s|{{NODES}}|${NODES}|g" \ |
| -e "s|{{NTASKS_PER_NODE}}|${NTASKS_PER_NODE}|g" \ |
| -e "s|{{CPUS_PER_TASK}}|${CPUS_PER_TASK}|g" \ |
| -e "s|{{GPUS_PER_NODE}}|${GPUS_PER_NODE}|g" \ |
| -e "s|{{TIME}}|${TIME}|g" \ |
| "$DEMO_DIR/templates/slurm_header.template" > "$SLURM_SCRIPT" |
|
|
| |
| cat >> "$SLURM_SCRIPT" << 'SETUP_BLOCK' |
|
|
| |
| SETUP_BLOCK |
| cat >> "$SLURM_SCRIPT" << 'ENV_BLOCK' |
| set +u |
| if [ -n "${UMA_ENV_SCRIPT:-}" ] && [ -f "${UMA_ENV_SCRIPT}" ]; then |
| source "${UMA_ENV_SCRIPT}" |
| else |
| echo "[WARN] UMA_ENV_SCRIPT 未设置或文件不存在,跳过环境初始化。请自行确保 conda/matchem 环境已激活。" |
| fi |
| set -u |
| ENV_BLOCK |
|
|
| |
| echo "" >> "$SLURM_SCRIPT" |
| echo "# 预检" >> "$SLURM_SCRIPT" |
| DATA_ARGS="" |
| while IFS= read -r line; do |
| [ -z "$line" ] && continue |
| DATA_ARGS="$DATA_ARGS \"$line\"" |
| done <<< "$DATA_FILES" |
| echo "bash $DEMO_DIR/templates/preflight_check.sh $DATA_ARGS" >> "$SLURM_SCRIPT" |
|
|
| |
| echo "" >> "$SLURM_SCRIPT" |
| echo "# 工作目录" >> "$SLURM_SCRIPT" |
| echo "cd $OUTPUT_DIR" >> "$SLURM_SCRIPT" |
| echo "" >> "$SLURM_SCRIPT" |
| echo "# 环境变量" >> "$SLURM_SCRIPT" |
| echo "$ENV_EXPORTS" >> "$SLURM_SCRIPT" |
| echo "" >> "$SLURM_SCRIPT" |
| echo "# 将仓库根目录加入 PYTHONPATH,确保能 import 本地 model 包" >> "$SLURM_SCRIPT" |
| echo "export PYTHONPATH=\"$REPO_ROOT:\${PYTHONPATH:-}\"" >> "$SLURM_SCRIPT" |
| echo "" >> "$SLURM_SCRIPT" |
| echo "# 屏蔽 PyTorch NCCL C++ INFO 日志" >> "$SLURM_SCRIPT" |
| echo "export TORCH_CPP_LOG_LEVEL=WARNING" >> "$SLURM_SCRIPT" |
| echo "export NCCL_DEBUG=ERROR" >> "$SLURM_SCRIPT" |
| echo "export GLOG_minloglevel=1" >> "$SLURM_SCRIPT" |
| echo "" >> "$SLURM_SCRIPT" |
| echo "# AMD DCU: 避免 RCCL \"Missing HSA_FORCE_FINE_GRAIN_PCIE\" 警告" >> "$SLURM_SCRIPT" |
| echo "export HSA_FORCE_FINE_GRAIN_PCIE=1" >> "$SLURM_SCRIPT" |
| echo "" >> "$SLURM_SCRIPT" |
| echo "# UMA 依赖: 清理 rocblas tensile 路径, 避免拉到错版本" >> "$SLURM_SCRIPT" |
| echo "unset ROCBLAS_TENSILE_LIBPATH" >> "$SLURM_SCRIPT" |
|
|
| |
| if [ "$NODES" -gt 1 ]; then |
| cat >> "$SLURM_SCRIPT" << 'MULTI_NODE' |
|
|
| |
| nodes=$(scontrol show hostnames "$SLURM_JOB_NODELIST") |
| nodes_array=($nodes) |
| export MASTER_ADDR=${nodes_array[0]} |
| export MASTER_PORT=29504 |
| echo "SLURM_NNODES=$SLURM_NNODES" |
| echo "MASTER_ADDR=$MASTER_ADDR" |
| echo "MASTER_PORT=$MASTER_PORT" |
|
|
| |
| MULTI_NODE |
| echo "srun --nodes=\$SLURM_NNODES --ntasks=\$SLURM_NNODES \\" >> "$SLURM_SCRIPT" |
| |
| echo " $TRAIN_CMD" >> "$SLURM_SCRIPT" |
| else |
| echo "" >> "$SLURM_SCRIPT" |
| echo "# 训练命令" >> "$SLURM_SCRIPT" |
| echo "$TRAIN_CMD" >> "$SLURM_SCRIPT" |
| fi |
|
|
| echo "=========================================" |
| echo "SLURM 脚本已生成: $SLURM_SCRIPT" |
| echo "hydra 配置已生成: $HYDRA_CONFIG" |
| echo "配置快照已保存: $OUTPUT_DIR/config.yaml" |
| echo "=========================================" |
| echo "" |
| echo "提交作业..." |
| sbatch "$SLURM_SCRIPT" |
| exit 0 |
| fi |
|
|
| |
| |
| |
| echo "=========================================" |
| echo "实验: $EXP_NAME" |
| echo "配置: $CONFIG_ABS" |
| echo "输出: $OUTPUT_DIR" |
| echo "Hydra: $HYDRA_CONFIG" |
| echo "=========================================" |
|
|
| |
| set +u |
| if [ -n "${UMA_ENV_SCRIPT:-}" ] && [ -f "${UMA_ENV_SCRIPT}" ]; then |
| source "${UMA_ENV_SCRIPT}" |
| else |
| echo "[WARN] UMA_ENV_SCRIPT 未设置或文件不存在,跳过环境初始化。请自行确保 conda/matchem 环境已激活。" |
| fi |
| set -u |
|
|
| |
| DATA_ARGS="" |
| while IFS= read -r line; do |
| [ -z "$line" ] && continue |
| DATA_ARGS="$DATA_ARGS \"$line\"" |
| done <<< "$DATA_FILES" |
| eval "bash $DEMO_DIR/templates/preflight_check.sh $DATA_ARGS" |
|
|
| |
| eval "$ENV_EXPORTS" |
|
|
| |
| export PYTHONPATH="$REPO_ROOT:${PYTHONPATH:-}" |
|
|
| |
| UMA_JD_PATH="$REPO_ROOT/weight/Jd.pt" |
| if [ -f "$UMA_JD_PATH" ]; then |
| export ONESCIENCE_UMA_JD_PATH="$UMA_JD_PATH" |
| echo "[OK] 找到 Jd.pt: $UMA_JD_PATH" |
| else |
| echo "[WARN] 未找到 Jd.pt: $UMA_JD_PATH,如训练/推理报错请检查" |
| fi |
|
|
| |
| export TORCH_CPP_LOG_LEVEL=WARNING |
| export NCCL_DEBUG=ERROR |
| export GLOG_minloglevel=1 |
|
|
| |
| export HSA_FORCE_FINE_GRAIN_PCIE=1 |
| unset ROCBLAS_TENSILE_LIBPATH |
|
|
| |
| cd "$OUTPUT_DIR" |
|
|
| |
| |
| echo "=========================================" |
| echo "开始训练..." |
| echo "=========================================" |
|
|
| export PYTHONUNBUFFERED=1 |
| MERGED_LOG="${OUTPUT_DIR}/train_merged.out" |
| : > "${MERGED_LOG}" |
| echo "[LOG] merged realtime log: ${MERGED_LOG}" |
|
|
| set +e |
| eval "$TRAIN_CMD" 2>&1 | tee -a "${MERGED_LOG}" |
| TRAIN_EXIT=${PIPESTATUS[0]} |
| set -e |
|
|
| exit "${TRAIN_EXIT}" |
|
|
|
|