框架的执行流程主要如下:
首先被调用的为最外层的train.py,在调用train.py时,fairseq会读取你的命令行参数,并根据命令行参数内的信息,生成相应的分布式策略,其调用流程如下:
分布式
fairseq共支持以下三种训练方式,分别为单GPU,分布式训练,单节点多GPU。
下面就单节点多GPU的分布式训练策略做一下简单的介绍:
if args.distributed_init_method is not None:
# distributed training
distributed_main(args.device_id, args)
elif args.distributed_world_size > 1:
# fallback for single node with multiple GPUs
port = random.randint(10000, 20000)
args.distributed_init_method = 'tcp://localhost:{port}'.format(port=port)
args.distributed_rank = None # set based on device id
if max(args.update_freq) > 1 and args.ddp_backend != 'no_c10d':
print('| NOTE: you may get better performance with: --ddp-backend=no_c10d')
torch.multiprocessing.spawn(
fn=distributed_main,
args=(args, ),
nprocs=args.distributed_world_size,
)
其中有关多卡训练最重要的一行为torch.multiprocessing.spawn(),该方法是pytorch多进程处理包中的一个方法,主要用来生成分布式策略,通过创建进程实例并调用join来等待它们完成,可以生成大量子进程来执行某些功能。其参数分别为:
- fn:函数被称为派生进程的入口点
- args (tuple) – 传递给 fn 的参数.
- nprocs (int) – 派生的进程数.
所以该句话的意思为:将使用nprocs个子进程来分布式运行fn(args)。
继续向下看可以看到在train.py中这个fn就是指distributed_main,而distributed_main又是通过间接调用main方法实现的,所以这将我们关注的重点指向了main函数的执行。
开始训练
命运的车轮让我们来到了main函数面前,让我们一起看看main函数都干了些什么。
思来想去不知道这一部分该怎么去描述,最后还是决定用一张图来简介明了的来展示给大家,
其步骤大体为:
-
初始化cuda,查看设备的可用状态。
-
根据命令行参数设置需要执行的任务,如translation
-
加载数据集并切分
-
初始化分布式训练
这部分加载了socket,其分布式训练是通过进程间通信的方式进行实现,其底层的分布式的运行是通过MPI库实现
-
加载模型与训练标准(criterion)
-
创建虚拟(dummy)的batch
这里他的作用有两点:1、预热缓存分配器。2、在每个工作进程的batch数不均匀时作为占位符进行分布式数据并行训练。
-
初始化trainer
trainer是一个支持数据并行训练的一个class,这个在稍后的博文中会进行介绍。 -
初始化dataloder
上面只是加载了数据集,在此处加载模型的dataloader,Dataloader的处理逻辑是先通过Dataset类里面的 getitem 函数获取单个的数据,然后组合成batch,再使用collate_fn所指定的函数对这个batch做一些操作。 -
加载检查点
-
训练
可以看到影响fairseq训练轮数的主要有三个参数:lr(学习率),max_epoch,max_update,只有这三个参数同时满足训练的要求,模型的训练才会继续。
在训练过程中模型主要经历了,train,valid,保存价差点等操作,这些在之后的章节中在进行分享。