tensorflow gpu_多GPU,具有Tensorflow的多进程

e9dca0d2bb1a7ecb68f62f86e71c6880.png

作者 | GrégoireDelétang

来源 | Medium

编辑 | 代码医生团队

Tensorflow是实验深度学习算法的绝佳工具。但是要利用深度学习的力量,需要利用计算能力和良好的工程技术。最终需要使用多个GPU,甚至可能需要多个流程才能实现目标。建议先阅读TensorFlow关于GPU 的官方教程。

https://www.tensorflow.org/guide/using_gpu

一个过程,很多GPU

这是最常见的情况,因为大多数深度学习社区正在进行监督学习,具有大数据集(图像,文本,声音......)和许多参数。这也是最困难的一个:需要在多个计算单元上并行化反向传播。

Jonathan Hui 在2017年发表了一篇很好的文章,可以直接阅读。

https://jhui.github.io/2017/03/07/TensorFlow-GPU/

多个进程,许多GPU

这是本文的真正意义所在。如果正在从事强化学习或“奇特”类型的学习,例如遗传算法或储层计算,可能会注意到有多个过程是必不可少的。

经验

将尝试以解决蛇的游戏为例。蛇是一条正方形的链,目标是在网格上吃水果。吃水果时,蛇的长度增加一个,并且在网格上随机出现新的水果。当他(不小心)吃掉他的尾巴时,蛇会失去。

0dd23a07ea7dc5128da5062382c30349.png

贪吃蛇,红点是水果

将同时播放多个代理以加速学习过程。

代理

将使用一个简单的卷积神经网络,但可以使用任何想要的模型。例如也可以使用密集神经网络或决策树。

这个游戏不是“动态的”:代理人需要采取的政策只取决于最后一帧。因此网络最后一帧,开发的python版本中的10x10图像。使用100个4x4过滤器,然后使用200个3x3过滤器。最终展平了卷积,添加了200个密集层,以及长度为4的最终输出层,用于4种可能的操作(向上,向右,向左,向下)。

学习

不会详细介绍,因为这不是重点。例如可以使用策略渐变,其中输出层包含每个操作的概率,算法的概念是“提升”与其导致的分数相关的操作。

还可以使用Q-learning,其中输出图层包含指定状态(输入框架)中每个动作的平均分数,并采用这些分数的argmax来选择动作。

最后,还可以使用遗传算法,其中的想法是为参数(这里是网络的权重)添加噪声,并且只保留最佳代理。

让开始多处理吧

好的,现在可以谈论多处理。一般来说,这不是一件容易的事。在这里,不谈多线程,这种方式更简单但功能也更少。

多处理意味着多核。需要与要启动的进程一样多的内核(有时内核可以处理多个“线程”,因此这是最后关注的数字)。

将使用AWS的实例p3.8xlarge,提供32个vCores和4个V100显卡。AWS租金约为12美元/小时,而此套装的投资额约为45,000美元,加上运行所需的能源成本。

因此,可以同时运行32个不同的代理,每个代理在一个单独的流程中。将在python中使用“多处理”包。这个包允许启动进程并创建管道以与它们通信。以下是架构的拓扑:

3f3014b26b75bb4b6b8729240ad0759c.png

多处理图

有32个工作进程和1个主进程。工作进程只是在玩游戏来收集数据并将其发送到主进程,主进程将训练这些数据并将新网络保存在文件中。然后,工作人员收到加载新网络,加载并再次播放N个游戏的消息。因此,需要从主进程启动32个进程,并在主进程和每个进程(即32个管道)之间创建一个管道。还需要在主进程内创建线程以异步侦听管道。这是它的代码:

class MasterProcess():    def __init__(self, verbose=False):        self.processes = {}     def train_agents(self):        pipes = {}        for i in range(0, variables.n_process+1):            parent_conn, child_conn = Pipe()            pipes[i] = parent_conn            p = AgentProcess(conn=child_conn, id=i, n_games=variables.n_per_process)            p.start()            self.processes[i] = p         scores = {}        batchs = {}        t0 = time.time()        def listenToAgent(id, scores):            while True:                msg = pipes[id].recv()                if msg == "saved":                    print("Master process (0) saved his weights.")                    for j in pipes:                        if(j != 0):                            pipes[j].send("load")                else:                    score = float(msg[0])                    scores[id] = score                    batchs[id] = msg[1]                    print("Process "+str(id)+" returns score "+str(score))         threads_listen = []        print("Threads to start")        for id in pipes:            t = threading.Thread(target=listenToAgent, args=(id,scores))            t.start()            threads_listen.append(t)        print("Threads started")         window = 50000 // (variables.n_process*variables.n_per_process)        iter = 1        mean_scores = []        file = open("log_scores", "w")        while True:            if(len(scores) == variables.n_process):                id_best = min(scores, key=scores.get)                mean_scores.append(np.mean(list(scores.values())))                print("End of iteration "+str(iter)+". Mean score sor far : "+str(np.mean(mean_scores)))                iter += 1                file.write(str(np.mean(mean_scores))+"\n")                print("Time : "+str(time.time()-t0))                print("\n")                pipes[0].send(("train_with_batchs", list(batchs.values())))                t0 = time.time()                scores.clear()                batchs.clear()             if(len(mean_scores) >= window):                mean_scores = mean_scores[1:]

如您所见,在开始时创建流程和管道,然后初始化词典以存储最后的分数和批量数据(键是流程ID)。然后创建线程来监听代理并启动它们。通信协议非常简单,只有一个单词消息,如“已保存”或“train_with_batchs”。在进程之间进行通信并不容易,因为只需要传递可序列化的对象,因此基本上是易于解析的数据。例如,无法直接传递Tensorflow会话。最后,在将分数的移动平均值存储在文件中的同时玩游戏。

现在来看看AgentProcess类,它非常简单:

class AgentProcess(Process):    def __init__(self, conn, id, n_games):        super(AgentProcess,self).__init__()        self.conn = conn        self.n_games = n_games        self.id = id        self.msg_queue = []        np.random.seed(self.id*100)     def run(self):        self.agent = A2C(self.id)         def treatQueue():            msg = self.conn.recv()            if msg == "load":                self.agent.load_model()                print("Process "+str(self.id)+" loaded the master (0) model.")             if msg[0] == "train_with_batchs":                print("Master process is training ...")                t0 = time.time()                self.agent.train_with_batchs(msg[1])                self.agent.save_model()                print("Master process finished training. Time : "+str(time.time()-t0)+" \n")                self.conn.send("saved")         while True:            if(self.id != 0):                batch_values = []                batch_states = []                batch_actions = []                print("Process "+str(self.id)+" starts playing "+str(self.n_games)+" games.")                scores = []                env = SnakeEnv()                overall_data = 0                for i in range(self.n_games):                    state = env.init()                    t = 0                    lastScoring = -1                    while True:                        action = self.agent([state])                        newState, reward, done = env.step(action)                        if(reward == 1):                            for j in range(t - lastScoring):                                batch_values.append(1)                            lastScoring = t                         batch_states.append([state])                        batch_actions.append(action)                        t += 1                        if(done or (t - lastScoring >= 100)):                            for j in range(t - lastScoring - 1):                                batch_values.append(0)                            break                        state = newState                    scores.append(env.score)                    overall_data += t                     if(overall_data >= 10000):                        break                print("Process "+str(self.id)+" finished playing.")                batch = (batch_states, batch_actions, batch_values)                self.conn.send((np.mean(scores),batch))            treatQueue()

代理正在创建其AI模型,并正在使用它来玩游戏。评分方法不是我的重点,但您=可以检查并自己调整以获得更好的性能。“数据”是(状态,行动,奖励)的三倍。相当简单吧?

GPU分配和内存

默认情况下,Tensorflow会为模型选择第一个可用GPU,并在设备上为进程分配完整内存。不想要两个!希望工作进程共享一个模型,但是为自己的用法分配自己的GPU集部分。

共享模型非常困难,因为Tensorflow不允许在多个进程之间轻松共享图形或会话。目前正在深入了解Tensorflow,看看它是否可行并提高性能。目前,所拥有的唯一解决方案是在每个进程中实现一个新的Tensorflow核心,即在AgentProcess类中调用“import tensorflow”。每个流程都有自己的图表和会话。

对于GPU分配,有32个进程,4个GPU,每个16GB内存。增加每个进程的内存可以提高运行模型的进程速度。但内存有限,所以必须手动进行非常严格的优化......训练由主进程完成,需要大量内存,因此为他分配了几乎一整个GPU。

最终的分配是[3,10,10,10](每个GPU的进程数,其中第一个也包含主数据)。要限制内存,可以使用per_process_gpu_memory_fractiongpu_options.allow_growth为每个进程手动限制比例,这将处理内存(在初始化时不分配所有内存,仅在需要时增加它)。这是代码:

       n_gpu = 4        if(n_gpu == 1):            self._build_model()            self._build_train_op()        else:            # 1 GPU for training, n-1 for playing            if(self.id <= 3):                gpu_id = 0            else:                gpu_id = (1 + (self.id%(n_gpu-1)))            os.environ["CUDA_VISIBLE_DEVICES"]=str(gpu_id)            self._build_model()            self._build_train_op()

为了强制进程使用特定的GPU,使用环境变量CUDA_VISIBLE_DEVICES,它独立于分配工作进程的主进程。

很明显,增加进程数会提高性能,因为已经处理了更多批次。

结论

可以使用Tensorflow进行多处理,并在“相当”强大的机器上进行真正的强化学习。请记住,机器学习不是关于如何设想算法,而是主要关于如何有效地构建算法。

这是整个github回购。

https://github.com/fazega/snake-rls

推荐阅读

如何在TensorFlow中运行GPU加速信号处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值