未验证 提交 aa81b0f1 编写于 作者: B Bo Zhou 提交者: GitHub

Add Chinese docs (#301)

* add docs

* add docs

* typo

* Update Overview.md

* update docs

* yapf

* yapf
上级 8e9eec6c
......@@ -3,7 +3,7 @@
</p>
[English](./README.md) | 简体中文
[**文档**](https://parl.readthedocs.io/en/stable/index.html)
[**文档**](https://parl.readthedocs.io/en/stable/index.html)| [**中文文档**](./docs/zh_CN/Overview.md)
> PARL 是一个高性能、灵活的强化学习框架。
# 特点
......
......@@ -3,7 +3,7 @@
</p>
English | [简体中文](./README.cn.md)
[**Documentation**](https://parl.readthedocs.io/en/stable/index.html)
[**Documentation**](https://parl.readthedocs.io/en/stable/index.html) | [**中文文档**](./docs/zh_CN/Overview.md)
> PARL is a flexible and high-efficient reinforcement learning framework.
......
文件已添加
<p align="center">
<img src="../../.github/PARL-logo.png" width="500"/>
<img src="../images/bar.png"/>
</p>
<br>**PARL**是一个主打高性能、稳定复现、轻量级的强化学习框架。<br>
## 使用场景
- 想要在**实际任务中**尝试使用强化学习解决问题
- 想快速调研下**不同强化学习算法**在同一个问题上的效果
- 强化学习算法训练速度太慢,想搭建**分布式**强化学习训练平台
- python的GIL全局锁限制了多线程加速,想**加速python代码**
## PARL文档全览
<table>
<tbody>
<tr align="center" valign="bottom">
<td>
</td>
<td>
<b>构建智能体(基础)</b>
<img src="../images/bar.png"/>
</td>
<td>
<b>开源算法库</b>
<img src="../images/bar.png"/>
</td>
<td>
<b>并行训练(进阶)</b>
<img src="../images/bar.png"/>
</td>
</tr>
</tr>
<tr valign="top">
<td align="center" valign="middle">
</td>
<td>
<ul>
<li><b>教程</b></li>
<ul>
<li><a href="tutorial/quick_start.md">入门:解决cartpole问题</a></li>
<li><a href="tutorial/module.md">子模块说明</a></li>
<li><a href="tutorial/param.md">模型参数管理</a></li>
<li><a href="tutorial/summary.md">绘制训练曲线</a></li>
</ul>
</ul>
</td>
<td align="left" >
<ul>
<li><b>前沿算法</b></li>
<ul>
<li><a href="https://github.com/PaddlePaddle/PARL/tree/CN_docs/examples/MADDPG">MADDPG</a></li>
<li><a href="https://github.com/PaddlePaddle/PARL/blob/CN_docs/examples/ES">ES</a></li>
<li><a href="https://github.com/PaddlePaddle/PARL/blob/CN_docs/examples/SAC">SAC</a></li>
<li><a href="https://github.com/PaddlePaddle/PARL/blob/CN_docs/examples/TD3">TD3</a></li>
</ul>
<li><b>经典算法</b></li>
<ul>
<li><a href="https://github.com/PaddlePaddle/PARL/blob/CN_docs/examples/QuickStart">PolicyGradient</a></li>
<li><a href="https://github.com/PaddlePaddle/PARL/blob/CN_docs/examples/DQN">DQN</a></li>
<li><a href="https://github.com/PaddlePaddle/PARL/blob/CN_docs/examples/DDPG">DDPG</a></li>
<li><a href="https://github.com/PaddlePaddle/PARL/blob/CN_docs/examples/PPO">PPO</a></li>
</ul>
<li><b>并行算法</b></li>
<ul>
<li><a href="https://github.com/PaddlePaddle/PARL/blob/CN_docs/examples/A2C">A2C</a></li>
<li><a href="https://github.com/PaddlePaddle/PARL/tree/CN_docs/examples/IMPALA">IMPALA</a></li>
</ul>
</ul>
</td>
<td>
<ul>
<li><b>教程</b></li>
<ul><li><a href="xparl/introduction.md">XPARL并行介绍</a></li>
<li><a href="xparl/tutorial.md">使用教程</a></li>
<li><a href="xparl/example.md">加速案例</a></li>
<li><a href="xparl/debug.md">如何debug</a></li>
</ul>
</td>
</tr>
</tbody>
</table>
## **安装**
### **安装**
PARL 支持并在 Ubuntu >= 16.04, macOS >= 10.14.1, 和 Windows 10通过了测试。 目前在Windows上**仅支持**python3.5+以上的版本,要求是64位的python。
```shell
pip install parl --upgrade
```
如果遇到网络问题导致的下载较慢,建议使用清华源解决:
```shell
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple parl --upgrade
```
如果想试试最新代码,可从源代码安装。
```shell
git clone https://github.com/PaddlePaddle/PARL
cd PARL
pip install .
```
如果遇到网络问题导致的下载较慢,建议使用清华源解决(参考上面的命令)。<br>
遇到git clone如果较慢的问题,建议使用我们托管在国内码云平台的仓库。
```shell
git clone https://gitee.com/paddlepaddle/PARL.git
```
### **关于并行**
如果只是想使用PARL的并行功能的话,是无需安装任何深度学习框架的。
## 贡献
本项目欢迎任何贡献和建议。 大多数贡献都需要你同意参与者许可协议(CLA),来声明你有权,并实际上授予我们有权使用你的贡献。
### 代码贡献规范
- 代码风格规范<br>
PARL使用yapf工具进行代码风格的统一,使用方法如下:
```shell
pip install yapf==0.24.0
yapf -i modified_file.py
```
- 持续集成测试<br>
当增加代码时候,需要增加测试代码覆盖所添加的代码,测试代码得放在相关代码文件的`tests`文件夹下,以`_test.py`结尾(这样持续集成测试会自动拉取代码跑)。附:[测试代码示例](../../parl/tests/import_test.py)
## 反馈
- 在 GitHub 上[提交问题](https://github.com/PaddlePaddle/PARL/issues)
# **教程:子模块说明**
<p align="center">
<img src="../../../.github/abstractions.png" width="300"/>
</p>
在上一个教程中,我们快速地展示了如果通过PARL的三个基础模块:Model Algorithm, Agent 来搭建智能体和环境进行交互的。在这个教程中,我们将详细介绍每个模块的具体定位,以及使用规范。
## Model
- 定义:`Model` 用来定义前向(Forward)网络,这通常是一个策略网络(Policy Network)或者一个值函数网络(Value Function),输入是当前环境状态(State)。
- **⚠️注意事项**:用户得要继承`parl.Model`这个类来构建自己的Model。
- 需要实现的函数:
- forward: 根据在初始化函数中声明的计算层来搭建前向网络。
- 备注:在PARL中,实现强化学习常用的target很方便的,直接通过deepcopy即可。
- 示例:
```python
import parl
from parl import layers
class CartpoleModel(parl.Model):
def __init__(self):
self.fc1 = layers.fc(size=10, act='tanh')
self.fc2 = layers.fc(size=2, act='softmax')
def forward(self, obs):
out = self.fc1(obs)
out = self.fc2(out)
return out
if __name__ == '__main__:
model = CartpoleModel()
target_model = deepcopy.copy(model)
```
## Algorithm
- 定义:`Algorithm` 定义了具体的算法来更新前向网络(Model),也就是通过定义损失函数来更新Model。一个Algorithm包含至少一个Model。
- **⚠️注意事项**:一般不自己开发,推荐直接import 仓库中已经实现好的算法。
- 需要实现的函数(`开发新算法才需要`):
- learn: 根据训练数据(观测量和输入的reward),定义损失函数,用于更新Model中的参数。
- predict: 根据当前的观测量,给出动作概率或者Q函数的预估值。
- 示例:
```python
model = CartpoleModel()
alg = parl.algorithms.PolicyGradient(model, lr=1e-3)
```
## Agent
- 定义:`Agent` 负责算法与环境的交互,在交互过程中把生成的数据提供给Algorithm来更新模型(Model),数据的预处理流程也一般定义在这里。
- **⚠️注意事项**:需要继承`parl.Agent`来使用,要在构造函数中调用父类的构造函数。
- 需要实现的函数:
- build_program: 定义paddle的program。用户通常在这里定义两个program:一个用于训练(在learn函数中调用),一个用于预测(用于sample、predict函数中)注意⚠️:这个函数会自动被调用,用户无需关注。
- learn: 根据输入的训练数据,更新模型参数。
- predict: 根据输入的观测量,返回要执行的动作(action)
- sample: 根据输入的观测量,返回要执行的动作,这个一般是添加了噪声的,用于探索用的。
- 示例:
```python
class CartpoleAgent(parl.Agent):
def __init__(self, algorithm, obs_dim, act_dim):
self.obs_dim = obs_dim
self.act_dim = act_dim
super(CartpoleAgent, self).__init__(algorithm)
def build_program(self):
# 这里我们定义了两个program。这个函数会被PARL自动调用,用户只需在这里定义program就好。
self.pred_program = fluid.Program()
self.train_program = fluid.Program()
#
with fluid.program_guard(self.pred_program):
obs = layers.data(
name='obs', shape=[self.obs_dim], dtype='float32')
self.act_prob = self.alg.predict(obs)
with fluid.program_guard(self.train_program):
obs = layers.data(
name='obs', shape=[self.obs_dim], dtype='float32')
act = layers.data(name='act', shape=[1], dtype='int64')
reward = layers.data(name='reward', shape=[], dtype='float32')
self.cost = self.alg.learn(obs, act, reward)
def learn(self, obs, act, reward):
# 在这里对数据进行预处理,同时准备好数据feed给上面定义的program
act = np.expand_dims(act, axis=-1)
feed = {
'obs': obs.astype('float32'),
'act': act.astype('int64'),
'reward': reward.astype('float32')
}
cost = self.fluid_executor.run(
self.train_program, feed=feed, fetch_list=[self.cost])[0]
return cost
def predict(self, obs):
# 在这里对数据进行预处理,同时准备好数据feed给上面定义的program
obs = np.expand_dims(obs, axis=0)
act_prob = self.fluid_executor.run(
self.pred_program,
feed={'obs': obs.astype('float32')},
fetch_list=[self.act_prob])[0]
act_prob = np.squeeze(act_prob, axis=0)
act = np.argmax(act_prob)
return act
def sample(self, obs):
# 输入观测量,返回用于探索的action
obs = np.expand_dims(obs, axis=0)
act_prob = self.fluid_executor.run(
self.pred_program,
feed={'obs': obs.astype('float32')},
fetch_list=[self.act_prob])[0]
# 按照概率采样,输出探索性的动作
act_prob = np.squeeze(act_prob, axis=0)
act = np.random.choice(range(self.act_dim), p=act_prob)
return act
if __name__ == '__main__':
model = CartpoleModel()
alg = parl.algorithms.PolicyGradient(model, lr=1e-3)
agent = CartpoleAgent(alg, obs_dim=4, act_dim=2)
```
# **教程:模型参数管理**
场景1: 在训练过程中,我们有时候需要把训练好的模型参数保存到本地,用于后续的部署或者评估。
当用户构建好agent之后,可以直接通过agent的相关接口来完成参数的存储。
```python
agent = AtariAgent()
# 保存参数到 ./model.ckpt
agent.save('./model.ckpt')
# 恢复参数到这个agent上
agent.restore('./model.ckpt')
```
场景2: 并行训练过程中,经常需要把最新的模型参数同步到另一台服务器上,这时候,需要把模型参数拿到内存中,然后再赋值给另一台机器上的agent(actor)。
```python
#--------------Agent---------------
weights = agent.get_weights()
#--------------Remote Actor--------------
actor.set_weights(weights)
```
# **教程:使用PARL解决Cartpole问题**
本教程会使用 [示例](https://github.com/PaddlePaddle/PARL/tree/develop/examples/QuickStart)中的代码来解释任何通过PARL构建智能体解决经典的Cartpole问题。
本教程的目标:
- 熟悉PARL构建智能体过程中需要用到的子模块。
## CartPole 介绍
CartPole又叫倒立摆。小车上放了一根杆,杆会因重力而倒下。为了不让杆倒下,我们要通过移动小车,来保持其是直立的。如下图所示。 在每一个时间步,模型的输入是一个4维的向量,表示当前小车和杆的状态,模型输出的信号用于控制小车往左或者右移动。当杆没有倒下的时候,每个时间步,环境会给1分的奖励;当杆倒下后,环境不会给任何的奖励,游戏结束。
<p align="center">
<img src="../../../examples/QuickStart/performance.gif" width="300"/>
</p>
## Model
**Model** 主要定义前向网络,这通常是一个策略网络(Policy Network)或者一个值函数网络(Value Function),输入是当前环境状态(State)。
首先,我们构建一个包含2个全连接层的前向网络。
```python
import parl
from parl import layers
class CartpoleModel(parl.Model):
def __init__(self, act_dim):
act_dim = act_dim
hid1_size = act_dim * 10
self.fc1 = layers.fc(size=hid1_size, act='tanh')
self.fc2 = layers.fc(size=act_dim, act='softmax')
def forward(self, obs):
out = self.fc1(obs)
out = self.fc2(out)
return out
```
定义前向网络的主要三个步骤:
- 继承`parl.Model`
- 构造函数`__init__`中声明要用到的中间层
-`forward`函数中搭建网络
以上,我们现在构造函数中声明了两个全连接层以及其激活函数,然后在`forward`函数中定义了网络的前向计算方式:输入一个状态,然后经过两层FC,最后得到的是每个action的概率预测。
## Algorithm
**Algorithm** 定义了具体的算法来更新前向网络(Model),也就是通过定义损失函数来更新Model。一个Algorithm包含至少一个Model。在这个教程中,我们将使用经典的PolicyGradient算法来解决问题。由于PARL仓库已经实现了这个算法,我们只需要直接import来使用即可。
```python
model = CartpoleModel(act_dim=2)
algorithm = parl.algorithms.PolicyGradient(model, lr=1e-3)
```
在实例化了Model之后,我们把它传给algorithm。
## Agent
**Agent** 负责算法与环境的交互,在交互过程中把生成的数据提供给Algorithm来更新模型(Model),数据的预处理流程也一般定义在这里。
我们得要继承`parl.Agent`这个类来实现自己的Agent,下面先把Agent的代码抛出来,再按照函数解释:
```python
class CartpoleAgent(parl.Agent):
def __init__(self, algorithm, obs_dim, act_dim):
self.obs_dim = obs_dim
self.act_dim = act_dim
super(CartpoleAgent, self).__init__(algorithm)
def build_program(self):
self.pred_program = fluid.Program()
self.train_program = fluid.Program()
with fluid.program_guard(self.pred_program):
obs = layers.data(
name='obs', shape=[self.obs_dim], dtype='float32')
self.act_prob = self.alg.predict(obs)
with fluid.program_guard(self.train_program):
obs = layers.data(
name='obs', shape=[self.obs_dim], dtype='float32')
act = layers.data(name='act', shape=[1], dtype='int64')
reward = layers.data(name='reward', shape=[], dtype='float32')
self.cost = self.alg.learn(obs, act, reward)
def sample(self, obs):
obs = np.expand_dims(obs, axis=0)
act_prob = self.fluid_executor.run(
self.pred_program,
feed={'obs': obs.astype('float32')},
fetch_list=[self.act_prob])[0]
act_prob = np.squeeze(act_prob, axis=0)
act = np.random.choice(range(self.act_dim), p=act_prob)
return act
def predict(self, obs):
obs = np.expand_dims(obs, axis=0)
act_prob = self.fluid_executor.run(
self.pred_program,
feed={'obs': obs.astype('float32')},
fetch_list=[self.act_prob])[0]
act_prob = np.squeeze(act_prob, axis=0)
act = np.argmax(act_prob)
return act
def learn(self, obs, act, reward):
act = np.expand_dims(act, axis=-1)
feed = {
'obs': obs.astype('float32'),
'act': act.astype('int64'),
'reward': reward.astype('float32')
}
cost = self.fluid_executor.run(
self.train_program, feed=feed, fetch_list=[self.cost])[0]
return cost
```
一般情况下,用户必须实现以下几个函数:
- 构造函数:
把algorithm传进来,以及相关的环境参数(用户自定义的)。需要注意的是,这里必须得要初始化父类:super(CartpoleAgent, self).\_\_init\_\_(algorithm)。
- build_program: 定义paddle里面的program。通常需要定义两个program:一个用于训练,一个用于预测。
- predict: 根据输入返回预测动作(action)。
- sample:根据输入返回动作(action),带探索的动作。
- learn: 输入训练数据,更新算法。
## 开始训练
首先,我们来定一个智能体。逐步定义model|algorithm|agent,然后得到一个可以和环境进行交互的智能体。
```python
model = CartpoleModel(act_dim=2)
alg = parl.algorithms.PolicyGradient(model, lr=1e-3)
agent = CartpoleAgent(alg, obs_dim=OBS_DIM, act_dim=2)
```
然后我们用这个agent和环境进行交互,训练模型,1000个episode之后,agent就可以很好地解决Cartpole问题,拿到满分(200)。
```python
def run_episode(env, agent, train_or_test='train'):
obs_list, action_list, reward_list = [], [], []
obs = env.reset()
while True:
obs_list.append(obs)
if train_or_test == 'train':
action = agent.sample(obs)
else:
action = agent.predict(obs)
action_list.append(action)
obs, reward, done, info = env.step(action)
reward_list.append(reward)
if done:
break
return obs_list, action_list, reward_list
env = gym.make("CartPole-v0")
for i in range(1000):
obs_list, action_list, reward_list = run_episode(env, agent)
if i % 10 == 0:
logger.info("Episode {}, Reward Sum {}.".format(i, sum(reward_list)))
batch_obs = np.array(obs_list)
batch_action = np.array(action_list)
batch_reward = calc_discount_norm_reward(reward_list, GAMMA)
agent.learn(batch_obs, batch_action, batch_reward)
if (i + 1) % 100 == 0:
_, _, reward_list = run_episode(env, agent, train_or_test='test')
total_reward = np.sum(reward_list)
logger.info('Test reward: {}'.format(total_reward))
```
## 总结
<img src="../../../examples/QuickStart/performance.gif" width="300"/><img src="../../images/quickstart.png" width="300"/>
在这个教程,我们展示了如何一步步地构建强化学习智能体,用于解决经典的Cartpole问题。完整的训练代码可以在这个[文件夹](https://github.com/PaddlePaddle/PARL/tree/develop/examples/QuickStart)中找到。赶紧执行下面的代码运行尝试下吧:)
```shell
# Install dependencies
pip install paddlepaddle
pip install gym
git clone https://github.com/PaddlePaddle/PARL.git
cd PARL
pip install .
# Train model
cd examples/QuickStart/
python train.py
```
# **教程:绘制训练曲线**
PARL集成了tensorboardX作为可视化的工具。工具导入方法:
`from parl.utils import summary`
### 折线图接口:add_scalar
summary.add_scalar(tag, scalar_value, global_step=None)
常用的参数
- tag (string) – Data identifier
- scalar_value (float or string/blobname) – Value to save
- global_step (int) – Global step value to record
例子:
```python
from parl.utils import summary
x = range(100)
for i in x:
summary.add_scalar('y=2x', i * 2, i)
```
预期结果:
<img src="../../tutorial/add_scalar.jpg" width="300"/>
### 柱形接口:add_scalar
summary.add_scalar(tag, scalar_value, global_step=None)
常用的参数
- tag (string) – Data identifier
- scalar_value (float or string/blobname) – Value to save
- global_step (int) – Global step value to record
例子:
```python
from parl.utils import summary
import numpy as np
for i in range(10):
x = np.random.random(1000)
summary.add_histogram('distribution centers', x + i, i)
```
预期结果:
<img src="../../tutorial/add_histogram.jpg" width="300"/>
### 修改数据保存路径接口
默认的数据保存路径是:`./train_log`, summary的数据路径和logger绑定到一起的,所以直接修改logger的保存路径即可:
```python
from parl.utils import logger
logger.set_dir('./train_log/exp1')
```
# **如何在xparl中debug**
经过并行修饰符修饰的类,并没有在本地运行,而是跑在了集群上,相应地,我们也没法在本机上看到打印的log,比如之前的代码。
```python
import parl
@parl.remote_class
class Actor(object):
def hello_world(self):
print("Hello world.")
def add(self, a, b):
return a + b
# 连接到集群(master节点)
parl.connect("localhost:6006")
actor = Actor()
actor.hello_world()# 因为计算是放在集群中执行,所以这里不会打印信息
```
这种情况下,我们应该怎么debug,定位问题呢?
这里推荐两个方案:
- 注释并行修饰符
先不在集群上跑并行,而是在本地跑起来,根据输出的日志debug,调试通过后再增加并行修饰符。但是这种方法在静态图的神经网络框架中可能会引发静态图重复定义的问题,所以在用paddle或者tensorflow的时候不建议采用这种方法。
- 根据xparl的日志服务查看
在本地脚本连接到xparl集群之后,xparl会在程序中输出logserver的地址,通过浏览器访问这个网站即可实时查看每个并行任务的对应输出。
<img src="./log_server.png" width="500"/>
# **示例:通过XPARL 实现线性加速运算**
<p align="center">
<img src="../../parallel_training/poster.png" width="300"/>
</p>
这个教程展示了如何通过并行修饰符`@parl.remote_class`,使用python的**多线程**也能够实现并行计算。
众所周知,python 的多线程并发性能并不好,很难达到传统的编程语言比如C++或者JAVA这种加速效果,主要的原因是python 有全局锁(GIL)的限制,使得其最多只能运用单核来记性运算。
## 串行运算
下面我们通过一个简单的例子来看下GIL对于python的影响。首先,我们跑下这段代码:
```python
class A(object):
def run(self):
ans = 0
for i in range(100000000):
ans += i
a = A()
for _ in range(5):
a.run()
```
这段代码需要17.46秒的时间来计算5次的从1累加到1亿。
## 多线程计算
接下来我们通过python的原生多线程库改造下上面的代码,让它可以多线程跑起来。
```python
import threading
class A(object):
def run(self):
ans = 0
for i in range(100000000):
ans += i
threads = []
for _ in range(5):
a = A()
th = threading.Thread(target=a.run)
th.start()
threads.append(th)
for th in threads:
th.join()
```
运行这段代码之后,居然需要**41.35秒**,比刚才的串行运算速度更慢。主要的原因是GIL限制了python只能单核运算,使用了多线程运算之后,触发了多线程竞争CPU的问题,反而延长了计算时间。
## PARL
```python
import threading
import parl
#这增加一行
@parl.remote_class
class A(object):
def run(self):
ans = 0
for i in range(100000000):
ans += i
threads = []
#这增加一行
parl.connect("localhost:6006")
for _ in range(5):
a = A()
th = threading.Thread(target=a.run)
th.start()
threads.append(th)
for th in threads:
th.join()
```
这段代码只需要**4.3秒**就能跑完!PARL在这里做的改动只有两行代码,但是我们却看到了运算速度的极大提升,具体的效果对比可以看下图。
<p align="center">
<img src="../../parallel_training/elapsed_time.jpg" width="500"/>
</p>
# **PARL并行核心介绍: XPARL**
随着强化学习能够解决的问题变得复杂,算法对于训练数据的需求也变得更大,为了提升算法的训练速度,PARL借鉴了[Pyro4](https://github.com/irmen/Pyro4)的设计理念,提出了更为高效实用的并行接口。
### 简单易用
通过一个简单的修饰符`@parl.remote_class`,用户就可以很简单地实现并行计算,无需关注繁琐的多进程通讯以及网络通讯,也不受Python多线程GIL锁的限制。
### 高性能
`@parl.remote_class` 可以让我们实现真正意义上的多线程并发计算(堪比C++的多线程)。正如下图a所示,python原生的多线程加速表现很糟糕(由于全局锁GIL的存在),但是我们可以看到,PARL的并行可以线性地减少运行时间,从而提升并发效率。
### Web 页面监控集群信息
在多机并行计算的时候,PARL在启动集群的时候提供了web服务,用户可以通过这个页面查看每台机器上的内存、CPU使用率等,同时也可以查看每个任务占用了多少集群资源。
### 全框架兼容
PARL的并行可以兼容目前市场上的任何深度学习框架,比如tensorflow、pytorch、mxnet等。通过增加并行修饰符`@parl.remote_class`,用户就可以把他们之前的代码转换成并行代码。
# 为什么用PARL
## 高吞吐量、高并发
PARL在实现底层的并行计算时,是通过端到端的这种网络传输,也就是在进行并发任务时,没有额外的网络损耗。这种并行设计,相比于RLlib需要通过Redis进行数据中转,PARL在同样的时间内,有更高的数据吞吐量。根据我们之前做的实验对比,运行同样的IMPALA算法,在同样的机器上,PARL的并行性能是更优秀的。
## 自动分发本地文件
市面上的并行框架大部分得要用户手动同步文件才可以跑起并行代码,比如配置文件得要手动或者通过命令分发到不同机器,parl可以自动分发当前目录下的代码文件,实现无缝的多机并行。
<img src="../../parallel_training/comparison.png" width="500"/>
# **使用教程**
---
## 配置命令
这个教程将会演示如何搭建一个集群。
搭建一个PARL集群,可以通过执行下面两个`xparl`命令:
### 启动集群
```bash
xparl start --port 6006
```
这个命令会启动一个主节点(master)来管理集群的计算资源,同时会把本地机器的CPU资源加入到集群中。命令中的6006端口只是作为示例,你可以修改成任何有效的端口。
### 加入其它机器资源
> 注意:如果你只有单台机器,可以忽略这部分教程。
如果你想加入更多的CPU计算资源到集群中,可以在其他机器上运行下面命令:
```bash
xparl connect --address [MASTER_ADDRESS]:6006
```
它会启动一个工作节点(worker),并把当前机器的CPU资源加入到该master对应的集群。worker默认会把所有的CPU资源加入到集群中,如果你需要指定worker可使用的CPU数量,可以在上述命令上加入选项`--cpu_num [CPU_NUM]` (例如:----cpu_num 10)。
注意:启动集群后,你可以在任何时候和任何机器上,通过执行`xparl connect`命令把更多CPU资源加入到集群中。
## 示例
这里我们给出了一个示例来演示如何通过`@parl.remote_class`来进行并行计算。
```python
import parl
@parl.remote_class
class Actor(object):
def hello_world(self):
print("Hello world.")
def add(self, a, b):
return a + b
# 连接到集群(master节点)
parl.connect("localhost:6006")
actor = Actor()
actor.hello_world()# 因为计算是放在集群中执行,所以这里不会打印信息
actor.add(1, 2) # 返回 3
```
## 关闭集群
在master机器上运行`xparl stop`命令即可关闭集群程序。当master节点退出后,运行在其他机器的worker节点也会自动退出并结束相关程序。
## 扩展阅读
我们现在已经知道了如何搭建一个集群,以及如何通过修饰符`@parl.remote_class`来使用集群。
[下一个教程](./example.md)我们将会演示如何通过这个修饰符来打破Python的全局解释器锁(Global Interpreter Lock, GIL)限制,从而实现真正的多线程计算。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册