Skip to content

Commit a76f61e

Browse files
[Fea] Support parallel jobs on multiple devices (#1159)
* support parallel jobs on multiple devices * Update user_guide.md
1 parent 5103736 commit a76f61e

File tree

3 files changed

+128
-83
lines changed

3 files changed

+128
-83
lines changed

docs/zh/user_guide.md

Lines changed: 103 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -96,84 +96,115 @@ TRAIN:
9696
9797
#### 1.1.3 自动化运行实验
9898
99-
如 [1.1.2 命令行方式配置参数](#112) 所述,可以通过在程序执行命令的末尾加上合适的参数来控制多组实验的运行配置,接下来以自动化执行四组实验为例,介绍如何利用 hydra 的 [multirun](https://hydra.cc/docs/1.0/tutorials/basic/running_your_app/multi-run/#internaldocs-banner) 功能,实现该目的。
99+
如 [1.1.2 命令行方式配置参数](#112) 所述,可以通过在程序执行命令的末尾加上合适的参数来控制多组实验的运行配置,接下来以自动化执行四组实验为例,介绍如何利用 hydra 的 [multirun](https://hydra.cc/docs/1.0/tutorials/basic/running_your_app/multi-run/#internaldocs-banner) 功能,实现该目的。
100100
101-
假设这四组实验围绕随机种子 `seed` 和训练轮数 `epochs` 进行配置,组合如下:
101+
假设这四组实验围绕随机种子 `seed` 和训练轮数 `epochs` 进行配置,组合如下:
102102
103-
| 实验编号 | seed | epochs |
104-
| :------- | :--- | :----- |
105-
| 1 | 42 | 10 |
106-
| 2 | 42 | 20 |
107-
| 3 | 1024 | 10 |
108-
| 4 | 1024 | 20 |
103+
| 实验编号 | seed | epochs |
104+
| :------- | :--- | :----- |
105+
| 1 | 42 | 10 |
106+
| 2 | 42 | 20 |
107+
| 3 | 1024 | 10 |
108+
| 4 | 1024 | 20 |
109109
110-
执行如下命令即可按顺序自动运行这 4 组实验。
110+
=== "串行实验"
111111
112-
``` sh title="$ python bracket.py {++-m seed=42,1024 TRAIN.epochs=10,20++}"
113-
[HYDRA] Launching 4 jobs locally
114-
[HYDRA] #0 : seed=42 TRAIN.epochs=10
115-
...
116-
[HYDRA] #1 : seed=42 TRAIN.epochs=20
117-
...
118-
[HYDRA] #2 : seed=1024 TRAIN.epochs=10
119-
...
120-
[HYDRA] #3 : seed=1024 TRAIN.epochs=20
121-
...
122-
```
112+
执行如下命令即可按顺序,以串行的方式自动运行这 4 组实验。
123113
124-
多组实验各自的参数文件、日志文件则保存在以不同参数组合为名称的子文件夹中,如下所示。
125-
126-
``` sh title="$ tree PaddleScience/examples/bracket/outputs_bracket/"
127-
PaddleScience/examples/bracket/outputs_bracket/
128-
└── 2023-10-14 # (1)
129-
└── 04-01-52 # (2)
130-
├── TRAIN.epochs=10,20,seed=42,1024 # multirun 总配置保存目录
131-
│ └── multirun.yaml # multirun 配置文件 (3)
132-
├── {==TRAIN.epochs=10,seed=1024==} # 实验编号3的保存目录
133-
│ ├── checkpoints
134-
│ │ ├── latest.pdeqn
135-
│ │ ├── latest.pdopt
136-
│ │ ├── latest.pdparams
137-
│ │ └── latest.pdstates
138-
│ ├── train.log
139-
│ └── visual
140-
│ └── epoch_0
141-
│ └── result_u_v_w_sigmas.vtu
142-
├── {==TRAIN.epochs=10,seed=42==} # 实验编号1的保存目录
143-
│ ├── checkpoints
144-
│ │ ├── latest.pdeqn
145-
│ │ ├── latest.pdopt
146-
│ │ ├── latest.pdparams
147-
│ │ └── latest.pdstates
148-
│ ├── train.log
149-
│ └── visual
150-
│ └── epoch_0
151-
│ └── result_u_v_w_sigmas.vtu
152-
├── {==TRAIN.epochs=20,seed=1024==} # 实验编号4的保存目录
153-
│ ├── checkpoints
154-
│ │ ├── latest.pdeqn
155-
│ │ ├── latest.pdopt
156-
│ │ ├── latest.pdparams
157-
│ │ └── latest.pdstates
158-
│ ├── train.log
159-
│ └── visual
160-
│ └── epoch_0
161-
│ └── result_u_v_w_sigmas.vtu
162-
└── {==TRAIN.epochs=20,seed=42==} # 实验编号2的保存目录
163-
├── checkpoints
164-
│ ├── latest.pdeqn
165-
│ ├── latest.pdopt
166-
│ ├── latest.pdparams
167-
│ └── latest.pdstates
168-
├── train.log
169-
└── visual
170-
└── epoch_0
171-
└── result_u_v_w_sigmas.vtu
172-
```
114+
``` sh title="$ python bracket.py {++-m seed=42,1024 TRAIN.epochs=10,20++}"
115+
[HYDRA] Launching 4 jobs locally
116+
[HYDRA] #0 : seed=42 TRAIN.epochs=10
117+
...
118+
[HYDRA] #1 : seed=42 TRAIN.epochs=20
119+
...
120+
[HYDRA] #2 : seed=1024 TRAIN.epochs=10
121+
...
122+
[HYDRA] #3 : seed=1024 TRAIN.epochs=20
123+
...
124+
```
125+
126+
多组实验各自的参数文件、日志文件则保存在以不同参数组合为名称的子文件夹中,如下所示。
127+
128+
``` sh title="$ tree PaddleScience/examples/bracket/outputs_bracket/"
129+
PaddleScience/examples/bracket/outputs_bracket/
130+
└── 2023-10-14 # (1)
131+
└── 04-01-52 # (2)
132+
├── TRAIN.epochs=10,20,seed=42,1024 # multirun 总配置保存目录
133+
│ └── multirun.yaml # multirun 配置文件 (3)
134+
├── {==TRAIN.epochs=10,seed=1024==} # 实验编号3的保存目录
135+
│ ├── checkpoints
136+
│ │ ├── latest.pdeqn
137+
│ │ ├── latest.pdopt
138+
│ │ ├── latest.pdparams
139+
│ │ └── latest.pdstates
140+
│ ├── train.log
141+
│ └── visual
142+
│ └── epoch_0
143+
│ └── result_u_v_w_sigmas.vtu
144+
├── {==TRAIN.epochs=10,seed=42==} # 实验编号1的保存目录
145+
│ ├── checkpoints
146+
│ │ ├── latest.pdeqn
147+
│ │ ├── latest.pdopt
148+
│ │ ├── latest.pdparams
149+
│ │ └── latest.pdstates
150+
│ ├── train.log
151+
│ └── visual
152+
│ └── epoch_0
153+
│ └── result_u_v_w_sigmas.vtu
154+
├── {==TRAIN.epochs=20,seed=1024==} # 实验编号4的保存目录
155+
│ ├── checkpoints
156+
│ │ ├── latest.pdeqn
157+
│ │ ├── latest.pdopt
158+
│ │ ├── latest.pdparams
159+
│ │ └── latest.pdstates
160+
│ ├── train.log
161+
│ └── visual
162+
│ └── epoch_0
163+
│ └── result_u_v_w_sigmas.vtu
164+
└── {==TRAIN.epochs=20,seed=42==} # 实验编号2的保存目录
165+
├── checkpoints
166+
│ ├── latest.pdeqn
167+
│ ├── latest.pdopt
168+
│ ├── latest.pdparams
169+
│ └── latest.pdstates
170+
├── train.log
171+
└── visual
172+
└── epoch_0
173+
└── result_u_v_w_sigmas.vtu
174+
```
175+
176+
1. 该文件夹是程序运行时根据日期自动创建得到,此处表示2023年10月14日
177+
2. 该文件夹是程序运行时根据运行时刻(世界标准时间,UTC)自动创建得到,此处表示04点01分52秒
178+
3. 该文件夹是 multirun 模式下额外产生一个总配置目录,主要用于保存 multirun.yaml,其内的 `hydra.overrides.task` 字段记录了用于组合出不同运行参数的原始配置。
179+
180+
=== "并行实验"
181+
182+
如果你的设备上有多个计算设备,则可以使用`hydra-joblib-launcher`插件实现并行实验,提高实验效率。
183+
184+
首先确认是否安装了`hydra-joblib-launcher`
185+
186+
``` sh
187+
pip install hydra-joblib-launcher --upgrade
188+
```
189+
190+
其次在你的运行配置 yaml 文件的开头位置,加入如下字段
191+
192+
``` yaml title="xxx.yaml" hl_lines="3"
193+
defaults:
194+
- ...
195+
- override hydra/launcher: joblib
196+
- _self_
197+
```
198+
199+
最后执行如下命令即可在 3,4,5,6 这四个设备上,一次并行运行 4 个任务。
200+
201+
``` sh
202+
{++CUDA_VISIBLE_DEVICES=3,4,6,7++} \
203+
python main_parallel.py -cn main_parallel -m seed=42,1024 TRAIN.epochs=10,20 \
204+
{++hydra.launcher.n_jobs=4++}"
205+
```
173206
174-
1. 该文件夹是程序运行时根据日期自动创建得到,此处表示2023年10月14日
175-
2. 该文件夹是程序运行时根据运行时刻(世界标准时间,UTC)自动创建得到,此处表示04点01分52秒
176-
3. 该文件夹是 multirun 模式下额外产生一个总配置目录,主要用于保存 multirun.yaml,其内的 `hydra.overrides.task` 字段记录了用于组合出不同运行参数的原始配置。
207+
注:设备数和并行任务数可以不相等,但建议单次并行的任务数小于等于设备数。
177208
178209
考虑到用户的阅读和学习成本,本章节只介绍了常用的实验方法,更多进阶用法请参考 [hydra官方教程](https://hydra.cc/docs/tutorials/basic/your_first_app/simple_cli/)。
179210

ppsci/geometry/geometry.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,6 @@ def boundary_normal(self, x):
112112
def uniform_points(self, n: int, boundary: bool = True) -> np.ndarray:
113113
"""Compute the equi-spaced points in the geometry.
114114
115-
Warnings:
116-
This function is not implemented, please use random_points instead.
117-
118115
Args:
119116
n (int): Number of points.
120117
boundary (bool): Include boundary points. Defaults to True.
@@ -379,9 +376,6 @@ def random_points(
379376
def uniform_boundary_points(self, n: int) -> np.ndarray:
380377
"""Compute the equi-spaced points on the boundary(not implemented).
381378
382-
Warnings:
383-
This function is not implemented, please use random_boundary_points instead.
384-
385379
Args:
386380
n (int): Number of points.
387381
@@ -429,11 +423,7 @@ def random_boundary_points(
429423
"""
430424

431425
def periodic_point(self, x: np.ndarray, component: int):
432-
"""Compute the periodic image of x(not implemented).
433-
434-
Warnings:
435-
This function is not implemented.
436-
"""
426+
"""Compute the periodic image of x(not implemented)."""
437427
raise NotImplementedError(f"{self}.periodic_point to be implemented")
438428

439429
def sdf_derivatives(self, x: np.ndarray, epsilon: float = 1e-4) -> np.ndarray:

ppsci/utils/callbacks.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
import importlib.util
1616
import inspect
1717
import sys
18+
import traceback
1819
from os import path as osp
1920
from typing import Any
2021

22+
from hydra.core.hydra_config import HydraConfig
2123
from hydra.experimental.callback import Callback
2224
from omegaconf import DictConfig
2325

@@ -102,6 +104,28 @@ def on_job_start(self, config: DictConfig, **kwargs: Any) -> None:
102104
if isinstance(full_cfg.device, str):
103105
paddle.device.set_device(full_cfg.device)
104106

107+
try:
108+
if "num" in HydraConfig.get().job:
109+
jobs_id = HydraConfig.get().job.num
110+
else:
111+
jobs_id = None
112+
if "n_jobs" in HydraConfig.get().launcher:
113+
parallel_jobs_num = HydraConfig.get().launcher.n_jobs
114+
else:
115+
parallel_jobs_num = None
116+
117+
if jobs_id and parallel_jobs_num:
118+
job_device_id = jobs_id % parallel_jobs_num
119+
device_type = paddle.get_device().split(":")[0]
120+
logger.message(
121+
f"Running job {jobs_id} on device {device_type}:{job_device_id}(logical device id)"
122+
)
123+
paddle.set_device(f"{device_type}:{job_device_id}")
124+
except Exception as e:
125+
print(e)
126+
traceback.print_exc()
127+
sys.exit(RUNTIME_EXIT_CODE)
128+
105129
# enable prim if specified
106130
if "prim" in full_cfg and bool(full_cfg.prim):
107131
# Mostly for compiler running with dy2st.

0 commit comments

Comments
 (0)