# lightning_npu **Repository Path**: tlwzzy/lightning_npu ## Basic Information - **Project Name**: lightning_npu - **Description**: 本仓库为pytorch_lightning仓库的npu适配仓 - **Primary Language**: Python - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 4 - **Created**: 2023-06-11 - **Last Updated**: 2023-06-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Lightning Adaptor适配使用指导 - [适配原则](#适配原则) - [基础训练及muti-gpu使用](#基础训练及muti-gpu使用) - [muti-node使用](#muti-node使用) - [16bit\_precision使用](#16bit_precision使用) - [experiment\_managers使用](#experiment_managers使用) - [early\_stopping使用](#early_stopping使用) - [model\_checkpoint使用](#model_checkpoint使用) ## 适配原则 - Lightning Adaptor为Pytorch_Lightning在NPU硬件场景下适配组件,该组件为使用Pytorch_Lightning的模型训练提供NPU支持。 - Lightning Adaptor的适配原则如下: - 非侵入式修改,在适配组件中修改,以Accelerator、Strategy、Plugin、Callback等组件形式对Pytorch_Lightning提供NPU调用支持,不直接在原仓库中进行修改。 - 尽可能做到上层无感,使得调用方在尽可能少修改代码的情况下能够适配。 ## 基础训练及muti-gpu使用 ### NPU单卡训练 - Accelerator: - 原生Pytorch_Lightning:在使用不同硬件设备执行训练时,默认Trainer()使用auto(会自动依次检测TPU、IPU、HPU、GPU是否可用,如果都不可用使用CPU,如果可用则使用第一个可用的设备类型),在Trainer的accelerator参数可以传入指定设备字符串或者对应设备Accelerator()对象来调用不同的硬件设备。 - Lightning Adaptor:由于Lightning Adaptor不改动原仓库代码,所以在使用NPU硬件去执行训练时,同样是在Trainer中accelerator传入本仓lightning_npu/accelerators/npu.py中的NPUAccelerator()实例对象来指定使用NPU硬件,但是不支持accelerator使用字符串"npu"来进行指定。 - Strategy: - 原生Pytorch_Lightning:在Trainer的strategy中设定改变训练、验证、测试的行为策略,不同的设备和训练方案可能使用不同的策略。 - Lightning Adaptor:在NPU的适配中同样需要strategy的适配,无论是单卡还是多卡。在NPU上执行单卡训练需要在Trainer中strategy传入本仓lightning_npu/strategies/npu.py中的SingleNPUStrategy()实例对象来指定训练策略。 > 说明: > >- 当需要在单卡训练中指定某一张卡的时候,通过传入SingleNPUStrategy的初始化参数device_index=n来指定,n类型为int,是从0开始计算的设备编号。 >```python > # 指定0卡执行训练的Trainer传参 > strategy=SingleNPUStrategy(device_index=0) > ``` >- 需注意的是传入Trainer的devices设定为1,也同样需要在Trainer中传入strategy为SingleNPUStrategy,并不能单独依靠devices去指定单卡训练。 >- 一旦传入strategy为SingleNPUStrategy时,无论devices设为何值,最终都是单卡训练无法调起多卡。 使用Pytorch_Lightning进行训练有使用LightningCLI、直接创建Trainer两种方式: - LightningCLI(可配置命令行工具启动训练): ```python cli = LightningCLI( ImageClassifier, MNISTDataModule, trainer_defaults={ "accelerator": NPUAccelerator(), "devices": 1, "max_epochs": 5, "strategy" : SingleNPUStrategy(), }, seed_everything_default=42, save_config_overwrite=True, run=False ) cli.trainer.fit(cli.model, datamodule=cli.datamodule) cli.trainer.test(ckpt_path="best", datamodule=cli.datamodule) ``` - Trainer(执行脚本启动训练): ```python trainer = Trainer(accelerator=NPUAccelerator(), devices=1, max_epochs=5, strategy=SingleNPUStrategy()) trainer.fit(ImageClassifier, datamodule=MNISTDataModule) trainer.test(ckpt_path="best", datamodule=MNISTDataModule) ``` >说明: >- 使用LightningCLI和创建Trainer方式进行训练没有本质区别,后文将统一以LightningCLI为例讲解说明。 >- ImageClassifier为需要训练的模型定义(需继承于LightningModule),MNISTDataModule为需要训练的数据模块(需继承于LightningDataModule),详情见本仓lightning_npu/examples/module及dataset_moudle,实际场景需根据实际训练任务修改。 ### NPU多卡训练 - Accelerator: - 与单卡相同,NPU多卡训练的accelerator依然还是NPUAccelerator()。 - Strategy: - 原生Pytorch_Lightning:strategy使用DDPStrategy()以支持多卡。 - Lightning Adaptor:适配多卡训练的strategy使用lightning_npu/strategies/npu_parallel.py的NPUParallelStrategy()实例对象以支持NPU多卡。 >说明: 在进行NPU多卡的训练中,指定具体用卡的方式和单卡不同,通过传入Trainer的devices参数去进行设定,当为int时,为指定调用前n张卡,n为传入int值;当传入为[int,int...]时,为指定调用列表中int值对应索引的卡(从零开始计)。 >```python > > #指定前4张卡(即0,1,2,3卡)执行训练的Trainer传参 > devices=4,#与devices[0,1,2,3]等同 > strategy=NPUParallelStrategy() > > #指定0、1、2、3卡执行训练的Trainer传参,只能指定2、4、8张卡训练,无法指定奇数张卡,同时须确保配置的卡号为ranktable文件中的有效配置 > devices=[0,1,2,3], > strategy=NPUParallelStrategy() > >``` - LightningCLI(可配置命令行工具启动训练): ```python cli = LightningCLI( ImageClassifier, MNISTDataModule, trainer_defaults={ "accelerator": NPUAccelerator(), "devices": 8, "max_epochs": 5, "strategy" : NPUParallelStrategy(), }, seed_everything_default=42, save_config_overwrite=True, run=False ) cli.trainer.fit(cli.model, datamodule=cli.datamodule) cli.trainer.test(ckpt_path="best", datamodule=cli.datamodule) ``` >说明: 需要注意的是npu的多卡只支持DDP,不支持DP,因此muti-gpu的NPU实现也是基于DDP的。 ## muti-node使用 原生Pytorch_Lightning:多机多卡不需要在模型中有额外的设置,只需要在Trainer中开启DDP(GPU场景,其他设备不一定使用DDP),并设置相应环境变量,传入Trainer的num_nodes填写使用的多机集群节点数。 Lightning Adaptor:正如在muti-gpu中的说明一样,NPU的多卡strategy适配NPUParallelStrategy就是基于DDP的,所以在使用NPU设备执行多机多卡训练时直接使用NPUParallelStrategy()实例对象就可以,无需额外开启DDP,同样传入Trainer的num_nodes填写使用的多机集群节点数。 - NPU多机多卡通讯配置: 1. 需要多机之间通过交换机配置有局域网 2. 配置多机多卡的连通性: - 0机上可以是这样配置: ```shell hccn_tool -i 0 -ip -s address 192.168.100.101 netmask 255.255.255.0 hccn_tool -i 1 -ip -s address 192.168.101.101 netmask 255.255.255.0 hccn_tool -i 2 -ip -s address 192.168.102.101 netmask 255.255.255.0 hccn_tool -i 3 -ip -s address 192.168.103.101 netmask 255.255.255.0 hccn_tool -i 4 -ip -s address 192.168.100.100 netmask 255.255.255.0 hccn_tool -i 5 -ip -s address 192.168.101.100 netmask 255.255.255.0 hccn_tool -i 6 -ip -s address 192.168.102.100 netmask 255.255.255.0 hccn_tool -i 7 -ip -s address 192.168.103.100 netmask 255.255.255.0 ``` - 1机上可以是这样配置: ```shell hccn_tool -i 0 -ip -s address 192.168.100.111 netmask 255.255.255.0 hccn_tool -i 1 -ip -s address 192.168.101.111 netmask 255.255.255.0 hccn_tool -i 2 -ip -s address 192.168.102.111 netmask 255.255.255.0 hccn_tool -i 3 -ip -s address 192.168.103.111 netmask 255.255.255.0 hccn_tool -i 4 -ip -s address 192.168.100.110 netmask 255.255.255.0 hccn_tool -i 5 -ip -s address 192.168.101.110 netmask 255.255.255.0 hccn_tool -i 6 -ip -s address 192.168.102.110 netmask 255.255.255.0 hccn_tool -i 7 -ip -s address 192.168.103.110 netmask 255.255.255.0 ``` >说明: 更多机器节点以此类推,IP不冲突可以相互访问即可。 3. 连通性检查: ```shell # 将192.168.xxx.xxx注册到连通性检查中 hccn_tool -i 0 -netdetect -s address 192.168.xxx.xxx # 执行注册的IP连通性检查 hccn_tool -i 0 -net_health -g ``` > 说明: 如果执行没有任何打印显示则说明没有联通,连通性检查成功后应有success的打印信息。 4. 确认交换机状态正常: ```shell # 执行下面指令 for i in {0..7}; do hccn_tool -i $i -lldp -g ; done ``` >说明: 若返回值不为空则lldp正常。 5. 额外的环境变量: ```shell export HCCL_IF_IP=${本机IP} ``` >说明: 每个节点都要设置HCCL_IF_IP为节点本机IP。 - NPU环境变量配置: 1. 需要配置的环境变量介绍: - MASTER_PORT - required; has to be a free port on machine with NODE_RANK 0 - MASTER_ADDR - required (except for NODE_RANK 0); address of NODE_RANK 0 node - WORLD_SIZE - required; how many nodes are in the cluster - NODE_RANK - required; id of the node in the cluster 2. 环境变量配置shell脚本说明(lightning_npu/muti_node.sh): ```shell # 选取未被占用的端口,60010是举例,如果60010被占用请填写其他未占用端口 export MASTER_PORT="60010" # 主节点的IP,在计算集群的任意节点上执行都填写主节点的IP,格式是点分十进制 export MASTER_ADDR="xxx.xxx.xxx.xxx" # 计算集群的节点数量,需根据当前使用的集群节点数填写,此处以两个节点的多机计算集群为例 export WORLD_SIZE="2" # 执行环境变量配置的节点序号,此处以当前节点为主节点(即节点序号为0)为例,需注意不同节点使用不同节点序号配置环境变量 export NODE_RANK="0" ``` 3. 按需修改好上述shell脚本后,在计算集群的每个节点上执行: ```shell source ./lightning_npu/muti_node.sh ``` >说明: > 需要每个节点都执行根据每个节点修改后的shell > ```mermaid > graph LR; > Computing_cluster((多机计算集群)) > Node_A(A节点
主节点) > Node_B(B节点
副节点) > Node_C(C节点
副节点) > Node_more(...
副节点) > A_Modify_file[修改MASTER_PORT为查询A未使用的端口60010
修改MASTER_ADDR为A节点IP
修改WORLD_SIZE为集群节点总数
修改NODE_RANK为当前节点编号0] > B_Modify_file[修改MASTER_PORT为查询A未使用的端口60010
修改MASTER_ADDR为A节点IP
修改WORLD_SIZE为集群节点总数
修改NODE_RANK为当前节点编号1] > C_Modify_file[修改MASTER_PORT为查询A未使用的端口60010
修改MASTER_ADDR为A节点IP
修改WORLD_SIZE为集群节点总数
修改NODE_RANK为当前节点编号2] > MORE_Modify_file[修改MASTER_PORT为查询A未使用的端口60010
修改MASTER_ADDR为A节点IP
修改WORLD_SIZE为集群节点总数
修改NODE_RANK为当前节点编号3...] > A_source[命令行source修改后的shell脚本] > B_source[命令行source修改后的shell脚本] > C_source[命令行source修改后的shell脚本] > more_source[命令行source修改后的shell脚本] > > > Computing_cluster-->Node_A > Computing_cluster-->Node_B > Computing_cluster-->Node_C > Computing_cluster-->Node_more > Node_A-->A_Modify_file > Node_B-->B_Modify_file > Node_C-->C_Modify_file > Node_more-->MORE_Modify_file > A_Modify_file-->A_source > B_Modify_file-->B_source > C_Modify_file-->C_source > MORE_Modify_file-->more_source > > > ``` - LightningCLI(可配置命令行工具启动训练): ```python cli = LightningCLI( ImageClassifier, MNISTDataModule, trainer_defaults={ "accelerator": NPUAccelerator(), "devices": 2, "max_epochs": 5, "strategy" : NPUParallelStrategy(), "num_nodes" : 2 }, seed_everything_default=42, save_config_overwrite=True, run=False ) cli.trainer.fit(cli.model, datamodule=cli.datamodule) cli.trainer.test(cli.model, datamodule=cli.datamodule) ``` >说明: >- 使用muti-node时,传入num_nodes为计算集群的节点数量,devices为当前执行脚本节点的用于计算卡数,可像多卡通过[int,int,...]方式指定卡序号用哪几张卡。 >- strategy必须使用NPUParallelStrategy()。 >- 在使用DDP的时候,使用Trainer的checkpoint保存功能,lightning会自动确保只有主节点上会保存ckpt,也就是rank_zero_only,所以在test流程时,如果使用ckpt_path而非model实例,会存在分节点找不到ckpt文件的问题,建议使用model实例,如果一定要使用ckpt,请确保在分节点保存ckpt文件。 >- 若出现host nic listen start failed, ip[0xxxxxxxxx], port[xxxxx], return[xx]的报错,可能是由于端口有问题,可以尝试更换端口 ## 16bit_precision使用 原生Pytorch_Lightning:通过设定传入Trainer的precision为16,amp_backend为"native"以支持torch原生混合精度,传入Trainer的precision为16,amp_backend为"apex"以支持amp.apex混合精度。 Lightning Adaptor:可以使用传入Trainer的amp_backend为"apex"的方式使用amp.apex混合精度,但是该方式不支持指定固定loss_scale,只能使用动态loss_scale。所以Lightning Adaptor通过传入Trainer中的plugins使用lightning_npu/plugins/npu的NpuMixedPrecisionPlugin(loss_scale=)实例对象的方式,基于amp.apex支持固定loss_scale的混合精度训练。 >说明: > 使用NpuMixedPrecisionPlugin方式,当需要动态loss_scale的时候,可以通过plugin不传入loss_scale来启用动态loss_scale。Lightning Adaptor不支持torch原生方式执行混合精度训练。 - LightningCLI(可配置命令行工具启动训练): ```python cli = LightningCLI( ImageClassifier, MNISTDataModule, trainer_defaults={ "accelerator": NPUAccelerator(), "devices": 4, "max_epochs": 5, "strategy": NPUParallelStrategy(), "precision": 16, "plugins": NpuMixedPrecisionPlugin(amp_level="O1", loss_scale=64.0) }, seed_everything_default=42, save_config_overwrite=True, run=False ) cli.trainer.fit(cli.model, datamodule=cli.datamodule) cli.trainer.test(ckpt_path="best", datamodule=cli.datamodule) ``` >说明: >- NpuMixedPrecisionPlugin中不传入loss_scale,即为动态loss_scale。 >- 使用NpuMixedPrecisionPlugin不需要额外在Trainer中传入amp_backend。 >- NpuMixedPrecisionPlugin通过传入的amp_level的方式,指定NVIDIA optimization level。 ## experiment_managers使用 原生Pytorch_Lightning:通过logger的方式使用experiment_managers,不同manager的logger的定义方式不同,目前支持Comet.ml、MLflow、Neptune、TensorBoard、Weights and Biases五种。 Lightning Adaptor:和原生使用方式相同。 - LightningCLI(可配置命令行工具启动训练): ```python comet_logger = CometLogger( api_key="comet_API_KEY", save_dir=".", project_name="MNIST", rest_api_key="comet_API_KEY", experiment_name="comet_logs", ) mlf_logger = MLFlowLogger(experiment_name="mlf_logs", tracking_uri="to_path") neptune_logger = NeptuneLogger( api_key="neptune_API_KEY", project="WORKSPACE/PROJECT", tags=["training","minist"] ) tb_logger = TensorBoardLogger("to_path", name="tb_logs") wandb_logger = WandbLogger(project="MNIST", log_model="all") cli = LightningCLI( ImageClassifier, MNISTDataModule, trainer_defaults={ "accelerator": NPUAccelerator(), "devices": [0,1,2,3], "max_epochs": 10, "strategy": NPUParallelStrategy(), "logger": [comet_logger,neptune_logger,tb_logger,wandb_logger,mlf_logger] }, seed_everything_default=42, save_config_overwrite=True, run=False ) wandb_logger.watch(ImageClassifier()) cli.trainer.fit(cli.model, datamodule=cli.datamodule) cli.trainer.test(ckpt_path="best", datamodule=cli.datamodule) ``` >说明: 具体进阶使用详见对应logger官网。 ## early_stopping使用 原生Pytorch_Lightning:通过在Trainer中传入callbacks为EarlyStopping()的方式实现early_stopping,在模型定义Module处需要记录期望度量的值。 Lightning Adaptor:和原生使用方式相同。 - Module(模型定义处): ```python class ImageClassifier(LightningModule): def __init__(self, model, lr=1.0, gamma=0.7, batch_size=32): super().__init__() self.save_hyperparameters(ignore="model") self.model = model or Net() self.test_acc = Accuracy() def forward(self, x): return self.model(x) def training_step(self, batch, batch_idx): x, y = batch logits = self.forward(x) loss = F.nll_loss(logits, y.long()) # 记录需要度量的值,EarlyStopping回调才可以根据该值衡量停止训练 self.log("val_loss", loss) return loss ``` >说明: >- 此处以loss为例,可以是其他值。 >- 此处以training_step为例,可以是验证和测试等其他阶段。 - LightningCLI(可配置命令行工具启动训练): ```python cli = LightningCLI( ImageClassifier, MNISTDataModule, trainer_defaults={ "accelerator": NPUAccelerator(), "devices": 8, "max_epochs": 50, "strategy" : NPUParallelStrategy(), "check_val_every_n_epoch" : 2, "callbacks": EarlyStopping(monitor="val_loss",verbose=True,min_delta=0.001,patience=3) }, seed_everything_default=42, save_config_overwrite=True, run=False ) cli.trainer.fit(cli.model, datamodule=cli.datamodule) cli.trainer.test(ckpt_path="best", datamodule=cli.datamodule) ``` >说明: >- EarlyStopping中传入monitor度量值的别名,verbose为是否打印详情,min_delta为界定的尺度,当度量值变化不超过该尺度时认为没有提升,patience为多少次度量值没有提升后停止训练,mode为需要的是度量值大还是小,只有"max"、"min"两种可选,默认值为"min",上述样例中就是记录的loss值,当连续3(patience)次的loss降低(mode:min)幅度不超过0.001(min_delta)时,认为没有提升,趋于稳定,停止训练,训练过程中会将度量值的相关信息打印(verbose:True)。 >- 需要注意的是patience的次数与记录的频率挂钩,当两个epoch一次记录,patience为3时,就是三次记录也就是6个epoch度量值没有提升时停止训练,不是指的epoch数量,同时这个过程只关注记录的那一次,也就是说中间间隔的epoch度量值没有提升的也不会被算进patience的次数。 ## model_checkpoint使用 原生Pytorch_Lightning:通过在Trainer中传入callbacks为ModelCheckpoint()的方式实现条件ckpt保存,通过在trainer.fit()中传入ckpt_path来加载ckpt实现断点续训。 Lightning Adaptor:和原生使用方式相同。 - LightningCLI(可配置命令行工具启动训练): ```python checkpoint_callback = ModelCheckpoint(dirpath="./ckpt/", #保存的路径 save_top_k=2, #保存top的k个ckpt monitor="val_loss", #保存ckpt的条件度量值 mode="min", #要保存的度量值最大还是最小的ckpt filename="sample-mnist--{epoch:02d}-{val_loss:.2f}", #自定义保存ckpt的文件名 save_weights_only=True, #是否只保存权重 ) cli = LightningCLI( ImageClassifier, MNISTDataModule, trainer_defaults={ "accelerator": NPUAccelerator(), "devices": 8, "max_epochs": 5, "strategy": NPUParallelStrategy(), #通过callbacks传入条件ckpt保存的回调 "callbacks": [checkpoint_callback], }, seed_everything_default=42, save_config_overwrite=True, run=False ) #通过ckpt_path读入ckpt文件,断点续训 cli.trainer.fit(cli.model, datamodule=cli.datamodule, ckpt_path=checkpoint) cli.trainer.save_checkpoint("myckpt.ckpt") cli.trainer.test(ckpt_path="best", datamodule=cli.datamodule) ``` >说明: 依赖2022.12以后的torch_npu,之前的在条件ckpt保存的时候,load进来的值和运行值可能不在同一设备导致无法比对。