深度学习——计算性能(十)

多GPU训练

问题拆分

  1. 在多个GPU之间拆分⽹络

每个GPU将流⼊特定层的数据作为输⼊,跨多个后续层对数据进⾏处理,然后将数据发送到下⼀个GPU

除⾮存框架或操作系统本⾝⽀持将多个GPU连接在⼀起,否则不建议这种⽅法

  1. 拆分层内的⼯作

例如,将问题分散到4个GPU,每个GPU⽣成16个通道的数据,⽽不是在单个GPU上计算64个通道。对于全连接的层,同样可以拆分输出单元的数量。

因为每⼀层都依赖于所有其他层的结果。此外,需要传输的数据量也可能⽐跨GPU拆分层时还要⼤。因此,基于带宽的成本和复杂性,我们同样不推荐这种⽅法

  1. 跨多个GPU对数据进⾏拆分

所有GPU尽管有不同的观测结果,但是执⾏着相同类型的⼯作。在完成每个⼩批量数据的训练之后,梯度在GPU上聚合。这种⽅法最简单,并可以应⽤于任何情况,同步只需要在每个⼩批量数据处理之后进⾏。

总体⽽⾔,只要GPU的显存⾜够⼤,数据并⾏是最⽅便的。****

数据并行性

⼀般来说, k个GPU并⾏训练过程如下:

  • 在任何⼀次训练迭代中,给定的随机的⼩批量样本都将被分成k个部分,并均匀地分配到GPU上;
  • 每个GPU根据分配给它的⼩批量⼦集,计算模型参数的损失和梯度;
  • k个GPU中的局部梯度聚合,以获得当前⼩批量的随机梯度; 聚合梯度被重新分发到每个GPU中;
  • 每个GPU使⽤这个⼩批量随机梯度,来更新它所维护的完整的模型参数集。

数据同步

对于⾼效的多GPU训练,我们需要两个基本操作。
⾸先,我们需要向多个设备分发参数并附加梯度(get_params)
第⼆,需要跨多个设备对参数求和,也就是说,需要⼀个allreduce函数。

数据分发

我们需要⼀个简单的⼯具函数,将⼀个⼩批量数据均匀地分布在多个GPU上。

多GPU简洁实现

import torch
from torch import nn
from d2l import torch as d2l

# 1. 简单残差18
def resnet18(num_classes, in_channels=1):
    """稍加修改的ResNet-18模型"""
    def resnet_block(out_channels, num_residuals, first_block=False):
        blk = []
        for i in range(num_residuals):
            if i == 0 and not first_block:
                blk.append(d2l.Residual(out_channels, use_1x1conv=True, strides=2))
            else:
                blk.append(d2l.Residual(out_channels))
        return nn.Sequential(*blk)
    
    net = nn.Sequential(
        nn.Conv2d(in_channels, 64, kernel_size=3, stride=1, padding=1),
        nn.BatchNorm2d(64),
        nn.ReLU())
    net.add_module("resnet_b1", resnet_block(64, 2, first_block=True))
    net.add_module("resnet_b2", resnet_block(128, 2))
    net.add_module("resnet_b3", resnet_block(256, 2))
    net.add_module("resnet_b4", resnet_block(512, 2))
    net.add_module("global_avg_pool",nn.AdaptiveAvgPool2d((1, 1)))
    net.add_module("fc", nn.Sequential(nn.Flatten(),nn.Linear(512, num_classes)))
    return net

# 2.网络初始化
net = resnet18(10)

device = d2l.try_all_gpus()

# 3.训练模型
def train(net, num_gpus, batch_size, lr):
    train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
    devices = [d2l.try_gpu(i) for i in range(num_gpus)]
    def init_weights(m):
        if type(m) in [nn.Linear, nn.Conv2d]:
            nn.init.normal_(m.weight, std=0.01) 
    net.apply(init_weights)
    # 在多个GPU上设置模型
    net = nn.DataParallel(net, device_ids=devices)
    trainer = torch.optim.SGD(net.parameters(), lr)
    loss = nn.CrossEntropyLoss()
    timer, num_epochs = d2l.Timer(), 10
    animator = d2l.Animator('epoch', 'test acc', xlim=[1, num_epochs])
    for epoch in range(num_epochs):
        net.train()
        timer.start()
        for X, y in train_iter:
            trainer.zero_grad()
            X, y = X.to(devices[0]), y.to(devices[0])
            l = loss(net(X), y)
            l.backward()
            trainer.step()
        timer.stop()
        animator.add(epoch + 1, (d2l.evaluate_accuracy_gpu(net, test_iter),))
    print(f'测试精度: {animator.Y[0][-1]:.2f}, {timer.avg():.1f}秒/轮, 'f'在{str(devices)}')

# 1 GPU训练
train(net, num_gpus=1, batch_size=256, lr=0.1)
# 2 GPU训练
# train(net, num_gpus=2, batch_size=512, lr=0.2)

参数服务器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

君逸~~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值