File size: 4,888 Bytes
fecdc11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/bin/bash
# Run native eSEN fine-tuning directly or submit it to Slurm.
set -euo pipefail

DEMO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ESEN_DIR="$(cd "$DEMO_DIR/.." && pwd)"
PARSER="$DEMO_DIR/_parse_config.py"
CONFIG=""
SUBMIT=false

while [[ $# -gt 0 ]]; do
    case "$1" in
        --config) CONFIG="$2"; shift 2 ;;
        --config=*) CONFIG="${1#*=}"; shift ;;
        --submit) SUBMIT=true; shift ;;
        -h|--help)
            echo "Usage: bash demo/run.sh --config configs/<name>.yaml"
            echo "Set launch.mode: submit in YAML to submit a Slurm job."
            exit 0
            ;;
        *) echo "Unknown argument: $1" >&2; exit 2 ;;
    esac
done

[[ -n "$CONFIG" ]] || { echo "Please specify --config configs/<name>.yaml" >&2; exit 2; }
[[ "$CONFIG" = /* ]] || CONFIG="$DEMO_DIR/$CONFIG"
[[ -f "$CONFIG" ]] || { echo "Config not found: $CONFIG" >&2; exit 2; }

if [[ -z "${CONDA_PREFIX:-}" ]]; then
    echo "Activate a OneScience MatChem conda environment before running this script." >&2
    exit 2
fi

export ESEN_REPO_DIR="$ESEN_DIR"
export ESEN_DATASET_DIR="${ESEN_DATASET_DIR:-$ESEN_DIR/datasets/oxides}"
export ONESCIENCE_ESEN_JD_PATH="${ONESCIENCE_ESEN_JD_PATH:-$ESEN_DIR/weight/Jd.pt}"

NAME="$(python3 "$PARSER" "$CONFIG" name)"
eval "$(python3 "$PARSER" "$CONFIG" launch)"
eval "$(python3 "$PARSER" "$CONFIG" slurm)"
ENV_EXPORTS="$(python3 "$PARSER" "$CONFIG" env)"

if [[ "$RUN_MODE" == "submit" ]]; then
    SUBMIT=true
fi

if ! $SUBMIT; then
    AUTO_SUBMIT_REASON=""
    if (( NODES > 1 )); then
        AUTO_SUBMIT_REASON="the config requests $NODES nodes"
    else
        AVAILABLE_GPUS="$(python3 -c 'import torch; print(torch.cuda.device_count() if torch.cuda.is_available() else 0)' 2>/dev/null || echo 0)"
        if (( AVAILABLE_GPUS < GPUS_PER_NODE )); then
            AUTO_SUBMIT_REASON="the config requests $GPUS_PER_NODE DCUs but only $AVAILABLE_GPUS are visible"
        fi
    fi
    if [[ -n "$AUTO_SUBMIT_REASON" ]]; then
        if ! command -v sbatch >/dev/null 2>&1; then
            echo "Cannot run locally: $AUTO_SUBMIT_REASON, and sbatch is unavailable." >&2
            exit 2
        fi
        echo "Local resources are insufficient: $AUTO_SUBMIT_REASON. Submitting to Slurm."
        SUBMIT=true
    fi
fi

TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
OUTPUT_ROOT="${ESEN_OUTPUT_ROOT:-$ESEN_DIR/outputs}"
OUTPUT_DIR="$OUTPUT_ROOT/${NAME}_${TIMESTAMP}"
mkdir -p "$OUTPUT_DIR/checkpoints"
cp "$CONFIG" "$OUTPUT_DIR/config.yaml"
FINETUNE_CONFIG="$OUTPUT_DIR/finetune.yaml"
python3 "$PARSER" "$CONFIG" finetune-config > "$FINETUNE_CONFIG"

if $SUBMIT; then
    SLURM_SCRIPT="$OUTPUT_DIR/submit.sh"
    cat > "$SLURM_SCRIPT" <<EOF
#!/bin/bash
#SBATCH --job-name=$NAME
#SBATCH --partition=$PARTITION
#SBATCH --nodes=$NODES
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=$CPUS_PER_TASK
#SBATCH --gres=dcu:$GPUS_PER_NODE
#SBATCH --time=$TIME
#SBATCH --output=$OUTPUT_DIR/slurm_%j.out
#SBATCH --error=$OUTPUT_DIR/slurm_%j.err
EOF

    cat >> "$SLURM_SCRIPT" <<EOF

set -euo pipefail
export ESEN_REPO_DIR="$ESEN_DIR"
export ESEN_DATASET_DIR="$ESEN_DATASET_DIR"
export ONESCIENCE_ESEN_JD_PATH="$ONESCIENCE_ESEN_JD_PATH"
export OMP_NUM_THREADS="$OMP_NUM_THREADS"
# A login/compute shell may carry a single-device selection into sbatch.
# Let Slurm expose all devices requested by this YAML.
if (( $GPUS_PER_NODE > 1 )); then
    unset HIP_VISIBLE_DEVICES
fi
$ENV_EXPORTS
cd "$OUTPUT_DIR"
EOF

    if (( NODES > 1 )); then
        cat >> "$SLURM_SCRIPT" <<EOF

nodes=(\$(scontrol show hostnames "\$SLURM_JOB_NODELIST"))
export MASTER_ADDR="\${nodes[0]}"
export MASTER_PORT=\$((20000 + SLURM_JOB_ID % 20000))
echo "eSEN multi-node DDP: nodes=\$SLURM_NNODES, devices/node=$GPUS_PER_NODE"
echo "MASTER_ADDR=\$MASTER_ADDR MASTER_PORT=\$MASTER_PORT"

srun --nodes="\$SLURM_NNODES" --ntasks="\$SLURM_NNODES" --ntasks-per-node=1 \\
  bash -c 'exec torchrun \\
    --nnodes=$NODES \\
    --node_rank="\${SLURM_NODEID}" \\
    --nproc_per_node=$GPUS_PER_NODE \\
    --rdzv_id="\${SLURM_JOB_ID}" \\
    --rdzv_backend=c10d \\
    --rdzv_endpoint="\${MASTER_ADDR}:\${MASTER_PORT}" \\
    "$ESEN_DIR/finetune.py" --config "$FINETUNE_CONFIG"'
EOF
    elif (( GPUS_PER_NODE > 1 )); then
        cat >> "$SLURM_SCRIPT" <<EOF

torchrun --standalone --nproc_per_node=$GPUS_PER_NODE \\
  "$ESEN_DIR/finetune.py" --config "$FINETUNE_CONFIG"
EOF
    else
        echo "python \"$ESEN_DIR/finetune.py\" --config \"$FINETUNE_CONFIG\"" >> "$SLURM_SCRIPT"
    fi

    chmod u+x "$SLURM_SCRIPT"
    echo "Submitting eSEN job: $SLURM_SCRIPT"
    sbatch "$SLURM_SCRIPT"
    exit 0
fi

eval "$ENV_EXPORTS"
cd "$OUTPUT_DIR"
if (( GPUS_PER_NODE > 1 )); then
    exec torchrun --standalone --nproc_per_node="$GPUS_PER_NODE" \
        "$ESEN_DIR/finetune.py" --config "$FINETUNE_CONFIG"
fi
exec python "$ESEN_DIR/finetune.py" --config "$FINETUNE_CONFIG"