Python单程序 多进程 多显卡同时独立训练多个模型

一、问题描述

        在一个python程序中实现多个模型(使用Pytorch实现)独立地训练,每个模型可指定的显卡,模型之间没有共享变量、参数也完全独立。如下图:

        类似的问题(但不是上面描述的问题):模型多显卡训练,这应该是指在多张显卡训练一个模型。而我们上面描述的是指在多张显卡中分别训练多个独立的模型,并且是并行地训练。

二、实现方式

        思路:使用python自带的并行库multiprocessing,添加多个子进程,模型在子进程(可指定不同的显卡)中进行训练。备注:各个子进程的内存空间一般是独立的,当然可以自由添加各个子进程的共享变量,但这些共享变量必须要加锁更新才行,否则会出现各种问题。而且,共享变量越多,多进程并行就越复杂。

        示例代码

import os
import time
import torch
import torch.nn as nn
import multiprocessing as mp

# 控制模型选择的函数
def train(is_end, gpu_id, model_name):
    if model_name == 'TransE':
        transe_run(gpu_id, model_name)
        transe_run(gpu_id, model_name)
        with is_end.get_lock():
            is_end.value += 1
    elif model_name == 'TransH':
        transh_run(gpu_id, model_name)
        transh_run(gpu_id, model_name)
        transh_run(gpu_id, model_name)
        with is_end.get_lock():
            is_end.value += 1
    else:
        sacn_run(gpu_id, model_name)
        sacn_run(gpu_id, model_name)
        sacn_run(gpu_id, model_name)
        sacn_run(gpu_id, model_name)
        with is_end.get_lock():
            is_end.value += 1

# 父进程中的主函数
def main(os_context):
    # 设置一个共享变量,用来记录完成训练的子进程个数
    is_end = os_context.Value("i", 0)
    # 创建多个子进程
    transe_trainer = os_context.Process(target=train, args=(is_end, 0, 'TransE'))
    transh_trainer = os_context.Process(target=train, args=(is_end, 1, 'TransH'))
    sacn_trainer = os_context.Process(target=train, args=(is_end, 2, 'SACN'))
    # 启动子进程
    transe_trainer.start()
    transh_trainer.start()
    sacn_trainer.start()
    # 下面的代码使得共享变量能够同步更新
    transe_trainer.join()
    transh_trainer.join()
    sacn_trainer.join()
    # 输出完成训练的子进程个数
    print('finish count: %d' % is_end.value)


# 程序入口
if __name__ == '__main__':
    # 输出CPU个数和GPU个数
    print('The machine has %d CPUs.' % os.cpu_count())
    print('The machine has %d GPUs.' % torch.cuda.device_count())
    # 获取操作系统上下文
    context = mp.get_context('spawn')
    # 运行主函数
    main(context)


# 模拟TransE训练
def transe_run(gpu_id, model_name):
    # 设置模型训练所在GPU
    torch.cuda.set_device(gpu_id)
    torch.cuda.is_available()
    print('b train [%s] on gpu[%d]' % (model_name, torch.cuda.current_device()))
    # 模型的初始化
    entity_embedding = nn.Parameter(torch.zeros(2000, 50))
    nn.init.uniform_(tensor=entity_embedding, a=-20, b=20)
    relation_embedding = nn.Parameter(torch.zeros(16, 50))
    nn.init.uniform_(tensor=relation_embedding, a=-20, b=20)
    entity_embedding.cuda(gpu_id)
    relation_embedding.cuda(gpu_id)
    # 模拟模型的训练
    for i in range(100):
        entity_embedding = entity_embedding + torch.tensor([0.1])
        relation_embedding = relation_embedding + torch.tensor([-0.1])
        time.sleep(1.5)
    print('a train [%s] on gpu[%d]' % (model_name, torch.cuda.current_device()))


# 模拟TransH训练
def transh_run(gpu_id, model_name):
    # 设置模型训练所在GPU
    torch.cuda.set_device(gpu_id)
    torch.cuda.is_available()
    print('b train [%s] on gpu[%d]' % (model_name, torch.cuda.current_device()))
    # 模型的初始化
    entity_embedding = nn.Parameter(torch.zeros(2000, 100))
    nn.init.uniform_(tensor=entity_embedding, a=-20, b=20)
    relation_embedding = nn.Parameter(torch.zeros(16, 100))
    nn.init.uniform_(tensor=relation_embedding, a=-20, b=20)
    entity_embedding.cuda(gpu_id)
    relation_embedding.cuda(gpu_id)
    # 模拟模型的训练
    for i in range(100):
        entity_embedding = entity_embedding + torch.tensor([0.1])
        relation_embedding = relation_embedding + torch.tensor([-0.1])
        time.sleep(1.5)
    print('a train [%s] on gpu[%d]' % (model_name, torch.cuda.current_device()))


# 模拟SACN训练
def sacn_run(gpu_id, model_name):
    # 设置模型训练所在GPU
    torch.cuda.set_device(gpu_id)
    torch.cuda.is_available()
    print('b train [%s] on gpu[%d]' % (model_name, torch.cuda.current_device()))
    # 模型的初始化
    entity_embedding = nn.Parameter(torch.zeros(2000, 150))
    nn.init.uniform_(tensor=entity_embedding, a=-20, b=20)
    relation_embedding = nn.Parameter(torch.zeros(16, 150))
    nn.init.uniform_(tensor=relation_embedding, a=-20, b=20)
    entity_embedding.cuda(gpu_id)
    relation_embedding.cuda(gpu_id)
    # 模拟模型的训练
    for i in range(100):
        entity_embedding = entity_embedding + torch.tensor([0.1])
        relation_embedding = relation_embedding + torch.tensor([-0.1])
        time.sleep(1.5)
    print('a train [%s] on gpu[%d]' % (model_name, torch.cuda.current_device()))

        效果(模拟程序输出)

The machine has 88 CPUs.
The machine has 4 GPUs.
b train [TransH] on gpu[1]
b train [TransE] on gpu[0]
b train [SACN] on gpu[2]
a train [TransH] on gpu[1]
b train [TransH] on gpu[1]
a train [SACN] on gpu[2]
b train [SACN] on gpu[2]
a train [TransE] on gpu[0]
b train [TransE] on gpu[0]
a train [TransH] on gpu[1]
b train [TransH] on gpu[1]
a train [SACN] on gpu[2]
b train [SACN] on gpu[2]
a train [TransE] on gpu[0]
a train [TransH] on gpu[1]
a train [SACN] on gpu[2]
b train [SACN] on gpu[2]
a train [SACN] on gpu[2]
finish count: 3

总结

        实现上述的方式不仅仅可以通过multiprocessing,也可以通过shell的方式(一个shell脚本对应一个进程,在命令行窗口执行多个脚本就能开多个进程),并且这种方式更加简单。然而,使用shell的方式多进程训练也有一定的限制,例如它只能在命令行窗口中执行。不过,按道理来说python应该也是可以调用shell脚本来执行的(猜的,待以后有空再做调研)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞机火车巴雷特

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值