DistributedDataParallel的简单使⽤常见问题⼀些原理
⽂章⽬录
前⾔
想变得更快
简单使⽤
import
from torch.utils.data.distributed import DistributedSampler
import torch.distributed as dist
argments
在参数中添加local_rank
parser.add_argument("--local_rank",type = int,default=-1)
主函数中配置
local_rank = parser.local_rank
if local_rank != -1:
dist_backend = 'nccl'
dist.init_process_group(backend=dist_backend) #  初始化进程组,同时初始化 distributed 包
device = local_rank if local_rank != -1 else (torch.device('cuda:0') if torch.cuda.is_available() else torch.device('cpu'))
torch.cuda.set_device(local_rank)  # 配置每个进程的gpu
<(device) #  封装之前要把模型移到对应的gpu
if torch.cuda.device_count() > 1:
print("Let's use", torch.cuda.device_count(), "GPUs!")
model = parallel.DistributedDataParallel(model, device_ids=[local_rank], find_unused_parameters=True)
sampler
# 使⽤DistributedSampler
train_loader = DataLoader(train_data,
shuffle=True if not train_sampler else False,
sampler = train_sampler,
batch_size=args.batch_size)
train_sampler = DistributedSampler(train_data) # 这⾥的train_data是⼀个dataset
# 下⾯错了,valid不需要分布
valid_sampler = DistributedSampler(valid_data)
valid_loader = DataLoader(valid_data,
shuffle=False,
sampler=valid_sampler,
batch_size=batch_size)
命令⾏执⾏
CUDA_VISIBLE_DEVICES=0,1,2,3 python -m torch.distributed.launch --nproc_per_node=4 train_snli.py --checkpoint=False --batch_size=128
torch.distributed.launch 会给模型分配⼀个args.local_rank的参数,也可以通过_rank()获取进程id
常见问题
问题⼀:显存不释放
我认为是我的代码有问题:
代码逻辑是这样:在0节点上我去判断accuracy有没有下降,没有的话patience_counter就会+1…
然后下⾯的if是判断程序是否要提前stop…
由于只在0节点上判断,patience_counter也只可能在0节点上+1,导致其它⼦进程不会随着0节点的进程⼀起停⽌,这就会导致⼦进程依旧占⽤着显存的问题。
#解决问题⼀(1):
其实,上⾯的代码那样写的本意是想让⼀个节点去保存模型,因为多个节点好像会出错(?)。那我其实可以让所有节点做判断,然后让其中⼀个节点保存模型:
#解决问题⼀(2):
我试着在程序的最后加上了(还不知道有没有⽤):
dist.destroy_process_group()
#解决问题⼀(3):
话说,如果产⽣了显存不释放的问题怎么办?这时候可能你⽤nvidia-smi语句都会卡住,解决⽅法如下:
⾸先,执⾏下⾯语句,会看到很多卡死的进程
fuser -v /dev/nvidia*
然后试着⽤kill -9去杀死它们,我因为使⽤kill -9没有⽤…所以直接执⾏下⾯的语句,杀死了我的所有进程:
killall -u usrname
问题⼆:DistributedDataParallel’ object has no attribute XXXX
我在我的模型⾥定义了损失函数:
然后把模型
然后报错:
#解决问题⼆
rank函数怎么用
问题三:
RuntimeError: Expected to have finished reduction in the prior
iteration before starting a new one. This error indicates that your
module has parameters that were not used in producing its output (the
return value of forward). You can enable unused parameter detection
by passing the keyword argument find_unused_parameters=True to
argument set, then the distributed data parallel module wasn’t able to
locate the output tensors in the return value of your module’s
forward function. Please include the structure of the return value
of forward of your module when reporting this issue (e.g. list,
dict, iterable). (prepare_for_backward at
/pytorch/torch/csrc/distributed/c10d/reducer.cpp:408)
emmmmm我跟⽹上的情况不太⼀样,我明明已经加了:fine_unused_parameters=True了
我猜测⾃⼰的错误原因在于,我的model在定义的时候⽤到了encoder,这⾥⾯的encoder是我⽤来作embeddings的另⼀个类。
然后可能encoder的参数更新出了某些问题,anyway…
#问题三解决:
翻了翻我以前的代码,看看是怎么⽤分布式进⾏训练的,于是就在validate和test过程中, 送⼊模型之间加⼊了:_grad():
然后莫名就不报错了。。。
但真正的错误原因,⼀直没有到。
⼀些原理
原理⼀:batch_size
如果我⽤三块显卡:
CUDA_VISIBLE_DEVICES=0,1,2 python -m torch.distributed.launch --nproc_per_node=3
如果我的batchsize设置为3:
parser.add_argument('--train_bs',help='Batch size',type=int, default=3)
那么每⼀次dataloader会给3块显卡都sample3个样本,等价于你设置的batch_size=9.
参考