Skip to content

Commit 32b09b5

Browse files
authored
Merge pull request #5776 from typhoonzero/update_refactor_dist_train_doc
Update design of dist train refactor
2 parents 6ece41e + e1cea8c commit 32b09b5

25 files changed

+117
-254
lines changed

doc/design/refactor/distributed_architecture.md renamed to doc/design/dist_refactor/distributed_architecture.md

Lines changed: 103 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -52,77 +52,121 @@ The IR for PaddlePaddle after refactoring is called a `Block`, it specifies the
5252

5353
The user can not directly specify the parameter update rule for the parameter server in the Python module, since the parameter server does not use the same computation definition as the trainer. Instead, the update rule is baked inside the parameter server. The user can not specify the update rule explicitly.
5454

55-
This could be fixed by making the parameter server run the same computation definition as the trainer (the user's Python module). For a detailed explanation, refer to this document -
56-
[Design Doc: Operation Graph Based Parameter Server](./parameter_server.md)
55+
This could be fixed by making the parameter server also run an IR, which can be different to the trainer side
56+
For a detailed explanation, refer to this document -
57+
[Design Doc: Parameter Server](./parameter_server.md)
5758

5859
## Distributed Training Architecture
5960

6061
The revamped distributed training architecture can address the above discussed limitations. Below is the illustration of how it does so:
6162

6263
<img src="src/distributed_architecture.png"/>
6364

64-
The major components in the architecture are: *PaddlePaddle Python*, *PaddlePaddle converter* and *PaddlePaddle runtime*.
65+
The major components are: *Python API*, *Distribute Transpiler* and *Remote Executor*.
6566

66-
### PaddlePaddle Python
67+
### Python API
6768

68-
PaddlePaddle Python is the Python library that user's Python code invokes, to read the data. build the neural network topology, start training, etc.
69+
Python API is the Python library that user's Python code invokes, to read the data, build the neural network topology, and start training, etc.
6970

7071
```Python
71-
paddle.init()
72-
input = paddle.op.recordIO("/home/data/mnist.recordio") # file stored on the cluster
73-
img, label = input[0], input[1]
74-
hidden = paddle.layer.fc(input=img, size=200, act=paddle.activation.Tanh())
75-
prediction = paddle.layer.fc(input=img, size=10, act=paddle.activation.Softmax())
76-
cost = paddle.layer.classification_cost(input=prediction, label=label)
77-
optimizer = paddle.optimizer.SGD(cost, learning_rate=0.01)
78-
session = paddle.session.NewRemote(num_trainer=3, num_ps=2, GPU_per_trainer=1)
79-
for i in range(1000):
80-
_, cost_val = session.eval(targets=[cost, optimizer])
81-
print cost_val
72+
images = fluid.layers.data(name='pixel', shape=[1, 28, 28], dtype='float32')
73+
label = fluid.layers.data(name='label', shape=[1], dtype='int64')
74+
...
75+
predict = fluid.layers.fc(input=conv_pool_2, size=10, act="softmax")
76+
cost = fluid.layers.cross_entropy(input=predict, label=label)
77+
avg_cost = fluid.layers.mean(x=cost)
78+
optimizer = fluid.optimizer.Adam(learning_rate=0.01)
79+
optimizer.minimize(avg_cost)
80+
81+
train_reader = paddle.batch(
82+
paddle.reader.shuffle(
83+
paddle.dataset.mnist.train(), buf_size=500),
84+
batch_size=BATCH_SIZE)
85+
86+
place = fluid.CPUPlace()
87+
exe = fluid.Executor(place)
88+
89+
for pass_id in range(10):
90+
for data in train_reader():
91+
loss, acc = exe.run(trainer_prog,
92+
feed=feeder.feed(data),
93+
fetch_list=[avg_cost])
8294
```
8395

84-
The above code is what a typical Python trainer code is, the neural network topology is built using the helper functions such as `paddle.layer.fc`. Training is done by calling `session.eval` iteratively.
85-
86-
#### session.eval
87-
88-
As shown in the graph, `session.eval` sends the IR and the evaluation inputs or targets to the PaddlePaddle cluster for evaluation.
89-
The targets can be any variable in the computation graph. When the target is say, the `optimizer` variable, the neural network will be optimized once. When the target is the `cost` variable, `session.eval` returns the cost value. Based on what the target is, an appropriate action is taken.
90-
91-
The Python `session` is a wrapper of the C++ `Session` class. For more information about `Session`, refer to this document - [Design Doc: Session](./session.md).
92-
93-
### PaddlePaddle Converter
94-
95-
The PaddlePaddle converter automatically converts the IR in the request (IR and evaluation inputs/targets) from PaddlePaddle Python to partitioned IRs and dispatches the new IRs and evaluation inputs/targets to different PaddlePaddle runtimes. Below are the steps that are followed :
96-
97-
1. Add a `feed` OP that feeds the eval inputs, and a `fetch` OP that fetches the eval targets to the IR.
98-
99-
2. Extract a new computation (sub)graph with the `feed` and `fetch` OPs as the boundary. The runtime does not need to run the OP that is not dependent on the `fetch` OP.
100-
101-
3. Optimize the computation graph.
102-
103-
4. Place the OPs in the graph onto different devices on different PaddlePaddle runtime according to a placement algorithm and the device constraints specified by the user.
104-
105-
5. Partition the graph according to runtime boundaries and add `send` / `recv` OP pair on the runtime boundaries.
96+
The code above is a typical local training program, the "Training Program" is built using helper functions such as
97+
`fluid.layer.fc`. The training is done by calling `Executor.run`
98+
iteratively.
99+
100+
For more details, the implementation of IR is [Program](../program.md), and `ProgramDesc` is the protobuf type.
101+
102+
[Executor](../executor.md) simply runs the `ProgramDesc`. For local training you generally use
103+
`Executor` to run the program locally. For any kind of distributed training, you can use
104+
`RemoteExecutor` to specify desired distributed training method with some optional arguments.
105+
106+
### Distributed Transpiler
107+
108+
The Distributed Transpiler automatically converts the IR (in protobuf format) to partitioned IRs. Then
109+
the Remote Executor dispatches the new IRs to Remote Executors across the cluster.
110+
Below are the steps that are followed :
111+
112+
1. User only need to change `Executor` to `RemoteExecutor` to change local program to distributed program.
113+
1. `RemoteExecutor` calls `Distributed Transpiler` to "transpile" user's program to several IRs representing a
114+
distributed training program:
115+
1. Parse configurations from `RemoteExecutor`.
116+
1. Determine the type of distributed program, can be DataParallelism, ModelParallelism or Streaming.
117+
1. Partition the `ProgramDesc` according to type and add `send` / `recv` OP pair on the boundaries. Take
118+
DataParallelism type for example, it removes the optimization operators and add a `send` OP to the
119+
"trainer" role, then add the optimization operators to the parameter server role within the `recv` OP.
120+
1. Dispatch the partitioned graph to different `RemoteExecutor` in the cluster.
121+
1. `RemoteExecutor` on each node run the received `ProgramDesc` utill the end.
122+
123+
124+
### RemoteExecutor
125+
126+
As shown in the graph, `RemoteExecutor.run` sends the IR to the cluster for Execution.
127+
You can also use parameter `fetch_list` to interactively fetch variable back to local for
128+
log printing.
129+
130+
The Python `RemoteExecutor` is derived from `Executor` class.
131+
132+
```python
133+
exe = RemoteExecutor(
134+
feed=feeder.feed(data),
135+
fetch_list=[avg_cost],
136+
job_desc=JobDesc(
137+
jobname,
138+
num_trainer,
139+
num_pserver,
140+
cpu_per_trainer,
141+
gpu_per_trainer,
142+
mem_per_trainer,
143+
cpu_per_pserver,
144+
mem_per_pserver
145+
))
146+
for data in train_reader():
147+
loss, acc = exe.run(trainer_prog,
148+
feed=feeder.feed(data),
149+
fetch_list=[avg_cost])
150+
```
106151

107-
6. Dispatch the partitioned graph to different PaddlePaddle runtimes.
152+
`JobDesc` object describe the distributed job resource specification to run on
153+
Cluster environment.
108154

109-
7. PaddlePaddle runtimes with the `fetch` OP reports evaluation results back to the converter, the converter reports the evaluation results back to the PaddlePaddle Python.
155+
<img src="src/remote_executor.png"/>
110156

111-
The output IRs will be cached to optimize the conversion latency.
157+
`RemoteExecutor.run` sends the `ProgramDesc` and
158+
[TrainingJob](https://github.com/PaddlePaddle/cloud/blob/develop/doc/autoscale/README.md#training-job-resource)
159+
to a server in the cluster which executes `RemoteExecutor.listen`. This server is responsible
160+
to start the final Kubernetes Jobs to run the different role of `ProgramDesc`.
112161

113162

114-
#### Placement Algorithm
163+
### Placement Algorithm
115164

116165
Our first implementation will only support "trainer-parameter server" placement: the parameters, initializers, and optimizers are all placed on the PaddlePaddle runtimes with the parameter server role. Everything else will be placed on the PaddlePaddle runtimes with the trainer role. This has the same functionality as the "trainer-parameter server" architecture of PaddlePaddle v0.10.0, but is more generic and flexible.
117166

118167
In the future, a more general placement algorithm should be implemented, which makes placements according to the input IR, and a model of device computation time and device communication time. Model parallelism requires the generic placement algorithm.
119168

120169

121-
### PaddlePaddle Runtime
122-
123-
The PaddlePaddle runtime owns multiple devices (e.g., CPUs, GPUs) and runs the IR. The runtime does not need to do OP placement since it is already done by the converter.
124-
125-
126170
### Local Training Architecture
127171

128172
The local training architecture will be the same as the distributed training architecture, the difference is that everything runs locally, and there is just one PaddlePaddle runtime:
@@ -132,9 +176,18 @@ The local training architecture will be the same as the distributed training arc
132176

133177
### Training Data
134178

135-
In PaddlePaddle v0.10.0, training data is typically read with a [data reader](../reader/README.md) from Python. This approach is no longer efficient when training in a distributed fashion since the Python process no longer runs on the same node with the trainer processes. The Python reader will need to read from the distributed filesystem (assuming it has the required access) and send to the trainers, doubling the network traffic.
136-
137-
When doing distributed training, the user can still use Python data reader: the training data are sent with `session.eval`. However this should be used for debugging purpose only. The users are encouraged to use the read data OPs.
179+
In PaddlePaddle v0.10.0, training data is typically read
180+
with [data reader](../reader/README.md) from Python. This approach is
181+
no longer efficient when training distributedly since the Python
182+
process no longer runs on the same node with the trainer processes,
183+
the Python reader will need to read from the distributed filesystem
184+
(assuming it has the access) and send to the trainers, doubling the
185+
network traffic.
186+
187+
When doing distributed training, the user can still use Python data
188+
reader: the training data are sent with `Executor.run`. However, should
189+
be used for debugging purpose only. The users are encouraged to use
190+
the read data OPs.
138191

139192

140193
## References:
File renamed without changes.

doc/design/refactor/parameter_server.md renamed to doc/design/dist_refactor/parameter_server.md

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Design Doc: Operation Graph Based Parameter Server
1+
# Design Doc: Parameter Server
22

33
## Abstract
44

@@ -10,7 +10,7 @@ different purposes.
1010
## Background
1111

1212
The previous implementations of the parameter server does not run a
13-
subgraph. parameter initialization, optimizer computation, network
13+
fluid sub-program. Parameter initialization, optimizer computation, network
1414
communication and checkpointing are implemented twice on both the
1515
trainer and the parameter server.
1616

@@ -23,18 +23,17 @@ server becomes a natural extension.
2323

2424
## Design
2525

26-
### Graph Converter
26+
### Distributed Transpiler
2727

28-
The *graph converter* converts the user-defined operation (OP) graph
29-
into subgraphs to be scheduled on different nodes with the following
28+
The *Distributed Transpiler* converts the user-defined fluid program
29+
into sub-programs to be scheduled on different nodes with the following
3030
steps:
3131

3232
1. OP placement: the OPs will be placed on different nodes according
3333
to heuristic that minimizes estimated total computation
3434
time. Currently we will use a simple heuristic that puts parameter
3535
varable on parameter server workers and everything else on trainer
3636
workers.
37-
3837
1. Add communication OPs to enable the communication between nodes.
3938

4039
We will need these OPs: *Send*, *Recv*, *Enqueue*, *Dequeue*.
@@ -48,8 +47,8 @@ After converting:
4847

4948
<img src="src/dist-graph.png" width="700"/>
5049

51-
1. The parameter variable W and it's optimizer subgraph are placed on the parameter server.
52-
1. Operators are added to the subgraphs.
50+
1. The parameter variable W and it's optimizer program are placed on the parameter server.
51+
1. Operators are added to the program.
5352
- *Send* sends data to the connected *Recv* operator. The
5453
scheduler on the receive node will only schedule *Recv* operator
5554
to run when the *Send* operator has ran (the *Send* OP will mark
@@ -64,39 +63,30 @@ After converting:
6463
### Benefits
6564

6665
- Model parallelism become easier to implement: it's an extension to
67-
the trainer - parameter server approach. we already have the
68-
communication OPs, but need to extend the graph converter's
69-
placement functionality.
70-
66+
the trainer - parameter server approach. We can have several "Transpilers"
67+
to achieve different goals.
7168
- User-defined optimizer is easier to add - user can now express it as
72-
a subgraph.
73-
69+
a sub-program.
7470
- No more duplication logic inside the trainer and the parameter
7571
server mentioned in the background section.
7672

7773
### Challenges
7874

79-
- It might be hard for the graph converter to cut a general graph
80-
(without any hint for which subgraph is the optimizer). We may need
81-
to label which subgraph inside the OP graph is the optimizer.
82-
8375
- It's important to balance the parameter shards of on multiple
8476
parameter server. If a single parameter is very big (some
8577
word-embedding, fully connected, softmax layer), we need to
8678
automatically partition the single parameter onto different
8779
parameter servers when possible (only element-wise optimizer depends
8880
on the parameter variable).
81+
- In the "Aync SGD" figure, the "W" variable on the parameter server
82+
could be read and wrote concurrently. See
83+
[here](https://github.com/PaddlePaddle/Paddle/pull/6394) for more
84+
details about concurrent program in fluid.
8985

9086
### Discussion
9187

92-
- In the "Aync SGD" figure, the "W" variable on the parameter server
93-
could be read and wrote concurrently, what is our locking strategy?
94-
E.g., each variable have a lock cpp method to be invoked by every
95-
OP, or, have a lock OP.
96-
9788
- Can the Enqueue OP be implemented under our current tensor design
9889
(puts the input tensor into the queue tensor)?
99-
10090
- *Dequeue* OP will have variable numbers of output (depends on the
10191
`min_count` attribute), does our current design support it? (similar
10292
question for the *Add* OP)
Binary file not shown.
189 KB
Loading

0 commit comments

Comments
 (0)