# 自己的数据获取
dataset = MyDataset(input_size, data_size)
# 使用 DistributedSampler
train_sampler = torch.utils.data.distributed.DistributedSampler(dataset)
trainloader = DataLoader(dataset=dataset,
pin_memory=true,
shuffle=(train_sampler is None), # 使用分布式训练 shuffle 应该设置为 False
batch_size=args.batch_size,
num_workers=args.workers,
sampler=train_sampler)
需要注意的几个参数:batch_size、num_workers、shuffle、pin_memory在进行多机多卡以及单机多卡的设置。
1、 Batch_size设置:
Dataparallel
: 设置 batch_size 是指总多卡的Batch size,数据被直接划分到多个 GPU
上
DistributedDataParallel
:batch size
设置成单卡一样即可,因为各个GPU对应的进程独立从磁盘中加载数据,这里的 Batch_size指的是单卡的。
2、shuffle设置:
shuffle:
Dataparallel
:设置 ‘shuffle’: True
DistributedDataParallel
:为了能够按顺序划分数据子集,拿到不同部分数据,所以数据集不能够进行随机打散,所以用了参数 ‘shuffle’: False
3、 pin_memory 设置:
是否提前申请CUDA内存(默认为False,但有说法除非数据集很小,否则在N卡上推荐总是打开)。
如果开了pin memory:
每个worker都需要缓存一个batch的数据.
batch size和num_workers都大, 显存会炸。
为什么 设置 pip_memory=true, 看解释:
多GPU训练的时候注意机器的内存是否足够(一般内存为显卡显存x2),如果不够,建议关闭pin_memory(锁页内存)选项。
采用DistributedDataParallel多GPUs训练的方式比DataParallel更快一些,如果你的Pytorch编译时有nccl的支持,那么最好使用DistributedDataParallel方式。
关于什么是锁页内存:
pin_memory就是锁页内存,创建DataLoader时,设置pin_memory=True,则意味着生成的Tensor数据最开始是属于内存中的锁页内存,这样将内存的Tensor转义到GPU的显存就会更快一些。
主机中的内存,有两种存在方式,一是锁页,二是不锁页,锁页内存存放的内容在任何情况下都不会与主机的虚拟内存进行交换(注:虚拟内存就是硬盘),而不锁页内存在主机内存不足时,数据会存放在虚拟内存中。显卡中的显存全部是锁页内存,当计算机的内存充足的时候,可以设置pin_memory=True。当系统卡住,或者交换内存使用过多的时候,设置pin_memory=False。因为pin_memory与电脑硬件性能有关,pytorch开发者不能确保每一个炼丹玩家都有高端设备,因此pin_memory默认为False。
当计算机的内存充足的时候,可以设置pin_memory=True。当系统卡住,或者交换内存使用过多的时候,设置pin_memory=False。pin_memory默认为False。
4、 num_workers 设置:num_worker的设置值一般是所运行机子上的CPU核心数
可以设置set num_workers =4 x number of available GPUs
um_worker大: 下一轮迭代的batch可能在上一轮/上上一轮…迭代时已经加载好了。 坏处是GPU memory开销大 (这是开了pin memory的情况吧) ,也加重了CPU负担。
CPU的物理个数:grep ‘physical id’ /proc/cpuinfo | sort | uniq | wc -l 结果为2,说明CPU有两个。 每个CPU的核数:cat /proc/cpuinfo |grep “cores”|uniq 10,说明每个10核。 cpu核数 = 2×10
1、cpu个数
grep ‘physical id’ /proc/cpuinfo | sort -u
2、核心数【当数据集较大时建议采用,num_works一般设置为(CPU 核心数 +-1)为最佳】
grep ‘core id’ /proc/cpuinfo | sort -u | wc -l
3、线程数
grep ‘processor’ /proc/cpuinfo | sort -u | wc -l
一般建议 num_workers
的值接近 CPU 核心数,但不要超过,以免导致过多的上下文切换。
如果数据集较大且预处理复杂,较高的 num_workers
值可能会更有效。反之,如果数据集较小或者预处理简单,则可能不需要太多的工作线程。
Num workers:只要你的 GPU 计算占用没有用满,说明 GPU 要等数据准备。可以试着增加进程数目,同时观察是否是硬盘 IO 瓶颈,如果是多机训练,还要注意网络瓶颈。不过,最大也不能超过核心数,一般还要减一点,因为主进程,多卡多进程训练,都会占用核心。
num_worker通过影响数据加载速度,从而影响训练速度。 每轮dataloader加载数据时:dataloader一次性创建num_worker个worker,worker就是普通的工作进程。并用batch_sampler将指定batch分配给指定的worker,worker将它负责的batch加载进RAM。然后,dataloader从RAM中找本轮迭代要用的batch,如果找到了,就使用;如果没找到,就用num_worker个worker继续加载batch到RAM,直到dataloader在RAM中找到目标batch。