以伪分布式模式部署 GeoMX#

伪分布式部署旨在快速试用和调试 GeoMX。在此模式中,所有节点都在单个 Docker 容器内启动,并且它们的 IP 地址都设置为 127.0.0.1,所以不需要额外的网络配置。虽然这种方法便于快速了解 GeoMX 系统的部署和运行,但不适用于实际的生产环境部署。

在这里 (here),您可以找到一个伪分布式部署的 shell 脚本。在这个脚本中,我们启动了总共 12 个节点,每个命令对应运行一个不同的进程来代表一个节点,节点角色由 DMLC_ROLEDMLC_ROLE_GLOBAL 指定。

警告

在任意一个机构内,请确保全局调度器或本地调度器优先于其它节点启动。

启动中央机构的节点#

中央机构包括 4 个节点:全局调度器,本地调度器,全局参数服务器和主控工作节点。

全局调度器用于管理全局参数服务器和其他机构的本地参数服务器,使用以下命令启动它:

DMLC_ROLE_GLOBAL=global_scheduler \
DMLC_PS_GLOBAL_ROOT_URI=127.0.0.1 \
DMLC_PS_GLOBAL_ROOT_PORT=9092 \
DMLC_NUM_GLOBAL_SERVER=1 \
DMLC_NUM_GLOBAL_WORKER=2 \
PS_VERBOSE=1 \
DMLC_INTERFACE=eth0 \
nohup python -c "import mxnet" > /dev/null &

这些环境变量的定义如下:

  1. DMLC_ROLE_GLOBAL:当前进程的角色。在这里,它是一个 global_scheduler 节点。它也可以设置为 global_server

  2. DMLC_PS_GLOBAL_ROOT_URI:全局调度器的 IP 地址。在这里,它被设置为 127.0.0.1,意味着进程在本地机器上运行。

  3. DMLC_PS_GLOBAL_ROOT_PORT:全局调度器绑定的端口号。在这里,端口号设置为 9092。

  4. DMLC_NUM_GLOBAL_SERVER:全局参数服务器的数量。在这里,它设置为 1,意味着只有一个全局参数服务器。

  5. DMLC_NUM_GLOBAL_WORKER:域内参数服务器的数量,也是参与数据中心的数量。在这里,它设置为 2,代表有 2 个参与数据中心(数据中心 A 和数据中心 B)。

  6. PS_VERBOSE:日志详细等级。设置为 0 禁用日志输出,1 输出必要的日志信息,2 输出日志详情。

  7. DMLC_INTERFACE:用于进程间通信的网络接口名字。在这里,它被设置为 eth0。在实际部署中,该环境变量应该被替换为您的系统或容器使用的网络接口的名称。

然后,我们启动一个本地调度器,用于管理全局参数服务器和主控工作节点。

DMLC_ROLE=scheduler \
DMLC_PS_ROOT_URI=127.0.0.1 \
DMLC_PS_ROOT_PORT=9093 \
DMLC_NUM_SERVER=1 \
DMLC_NUM_WORKER=1 \
PS_VERBOSE=1 \
DMLC_INTERFACE=eth0 \
nohup python -c "import mxnet" > /dev/null &

这里引入一些新的环境变量用来控制数据中心内部参数服务器系统的行为:

  1. DMLC_ROLE:当前进程的角色。在这里,它是一个 scheduler 节点。它也可以设置为 serverworker

  2. DMLC_PS_ROOT_URI:本地调度器的 IP 地址。这里,它被设置为 127.0.0.1,意味着本地调度器在本地机器上运行。

  3. DMLC_PS_ROOT_PORT:本地调度器绑定的端口号。如果在同一台机器上启动,该端口号应该与其他调度器(包括全局调度器)不同。这里,我们将端口号设置为 9093。

  4. DMLC_NUM_SERVER:如果在中心机构,这表示全局参数服务器节点的数量。在这里,它设置为 1,因为我们只启动了一个全局参数服务器。

  5. DMLC_NUM_WORKER:如果在中心机构,这表示工作节点的数量(包括主控工作节点)。在这里,我们只启用了一个主控工作节点,所以这个值设置为 1。

要启动全局参数服务器,请运行以下命令:

DMLC_ROLE_GLOBAL=global_server \
DMLC_PS_GLOBAL_ROOT_URI=127.0.0.1 \
DMLC_PS_GLOBAL_ROOT_PORT=9092 \
DMLC_NUM_GLOBAL_SERVER=1 \
DMLC_NUM_GLOBAL_WORKER=2 \
DMLC_ROLE=server \
DMLC_PS_ROOT_URI=127.0.0.1 \
DMLC_PS_ROOT_PORT=9093 \
DMLC_NUM_SERVER=1 \
DMLC_NUM_WORKER=1 \
DMLC_ENABLE_CENTRAL_WORKER=0 \
DMLC_NUM_ALL_WORKER=4 \
PS_VERBOSE=1 \
DMLC_INTERFACE=eth0 \
nohup python -c "import mxnet" > /dev/null &

请注意,DMLC_PS_GLOBAL_ROOT_URIDMLC_PS_GLOBAL_ROOT_PORT 指向的是全局调度器的 IP 和端口号,而 DMLC_PS_ROOT_URIDMLC_PS_ROOT_PORT 指向的是本地调度器的 IP 和端口号。

其他环境变量的说明如下:

  1. DMLC_ENABLE_CENTRAL_WORKER:这个选项用来启用或禁止中心机构参与模型训练。如果设置为 0,中心机构只提供一个主控工作节点来初始化全局参数服务器。如果设置为 1,中心机构可以提供工作节点集群并参与模型训练,此时主控工作节点附加到某一个工作节点上。

  2. DMLC_NUM_ALL_WORKER:全局参与模型训练的工作节点总数。在这个示例中,参与数据中心 A 有 2 个工作节点,参与数据中心 B 也有 2 个工作节点,所以总数为 4。请注意,虽然主控工作节点也是一个工作节点,但在本例中它不参与模型训练,所以没有计入。

最后,我们启动主控工作节点。

DMLC_ROLE=worker \
DMLC_ROLE_MASTER_WORKER=1 \
DMLC_PS_ROOT_URI=127.0.0.1 \
DMLC_PS_ROOT_PORT=9093 \
DMLC_NUM_SERVER=1 \
DMLC_NUM_WORKER=1 \
DMLC_NUM_ALL_WORKER=4 \
PS_VERBOSE=1 \
DMLC_INTERFACE=eth0 \
nohup python ${EXAMPLE_PYTHON_SCRIPT} --cpu > /dev/null &

主控工作节点设置 DMLC_ROLE_MASTER_WORKER=1 来声明自己为主控工作节点。它需要与本地调度器建立 socket 连接,所以需要设置 DMLC_PS_ROOT_URI=127.0.0.1DMLC_PS_ROOT_PORT=9093 以确保主控工作节点可以顺利找到本地调度器。

启动其它机构的节点#

接下来,我们将在其他参与机构启动一个本地调度器、一个域内参数服务器和两个工作节点。我们以其中一个参与机构为例进行说明。

首先,我们启动本地调度器:

DMLC_ROLE=scheduler \
DMLC_PS_ROOT_URI=127.0.0.1 \
DMLC_PS_ROOT_PORT=9094 \
DMLC_NUM_SERVER=1 \
DMLC_NUM_WORKER=2 \
PS_VERBOSE=1 \
DMLC_INTERFACE=eth0 \
nohup python -c "import mxnet" > /dev/null &

这里的设置与中央机构内本地调度器的设置相同,但在参与机构内,DMLC_NUM_SERVER 指定当前参与机构中的域内参数服务器数量,通常设置为 1。此外,DMLC_NUM_WORKER 指定当前参与机构中的工作节点数量。由于我们计划在这个参与机构内启动两个工作节点,所以我们将这个值设置为 2。

接下来,我们启动域内参数服务器:

DMLC_PS_GLOBAL_ROOT_URI=127.0.0.1 \
DMLC_PS_GLOBAL_ROOT_PORT=9092 \
DMLC_NUM_GLOBAL_SERVER=1 \
DMLC_NUM_GLOBAL_WORKER=2 \
DMLC_ROLE=server \
DMLC_PS_ROOT_URI=127.0.0.1 \
DMLC_PS_ROOT_PORT=9094 \
DMLC_NUM_SERVER=1 \
DMLC_NUM_WORKER=2 \
PS_VERBOSE=1 \
DMLC_INTERFACE=eth0 \
nohup python -c "import mxnet" > /dev/null &

如我们之前提到的,域内参数服务器需要与本地调度器和全局调度器建立 socket 连接。因此,它需要知道本地调度器和全局调度器的 IP 和端口号。

最后,我们启动两个工作节点:

DMLC_ROLE=worker \
DMLC_PS_ROOT_URI=127.0.0.1 \
DMLC_PS_ROOT_PORT=9094 \
DMLC_NUM_SERVER=1 \
DMLC_NUM_WORKER=2 \
DMLC_NUM_ALL_WORKER=4 \
PS_VERBOSE=1 \
DMLC_INTERFACE=eth0 \
nohup python ${EXAMPLE_PYTHON_SCRIPT} --data-slice-idx 0 --cpu > /dev/null &

DMLC_ROLE=worker \
DMLC_PS_ROOT_URI=127.0.0.1 \
DMLC_PS_ROOT_PORT=9094 \
DMLC_NUM_SERVER=1 \
DMLC_NUM_WORKER=2 \
DMLC_NUM_ALL_WORKER=4 \
PS_VERBOSE=1 \
DMLC_INTERFACE=eth0 \
python ${EXAMPLE_PYTHON_SCRIPT} --data-slice-idx 1 --cpu

工作节点的启动方式与前面类似,但它们连接到其各自机构的本地调度器。

训练数据将被切片给各个工作节点,每个工作节点处理一部分数据,这是通过 --data-slice-idx 选项指定的。例如,第一个工作节点拥有数据的第 0 个切片,第二个工作节点拥有数据的第 1 个切片。

警告

这个示例可能会因为数据集缺失而出错。如果发生这种情况,无需担心,该脚本会自动下载所需的数据集。您只需要重新启动这些进程即可。

如果您使用的是我们预先构建的 Docker 镜像,所需数据集已经内置在镜像中,所以不必担心这些问题。