本指导适用于在 TencentOS Server 3上使用 DataParallel(DP)训练框架运行 ResNet 模型,以 Docker 方式启动。
环境准备
1. 从 GitHub 下载 DP的Demo 开源仓库到本地。
git clone https://github.com/tczhangzhi/pytorch-distributed.gitcd pytorch-distributed
2. 由于 DP 是基于 Pytorch 开发的,我们使用 Pytorch 的基础镜像即可包含大部分运行环境。这里我们拉取 Nvidia 的基础镜像来启动容器。
docker run -it --gpus all --name=DP --ipc=host -v $PWD:/workspace nvcr.io/nvidia/pytorch:23.06-py3 /bin/bash
此时会从 nvcr 拉取 docker 镜像,请确保网络环境较好,直到镜像里所有层下载成功。成功后会直接进入容器内部。
数据集下载
我们使用 DP 训练框架运行 ResNet-50 模型,官方预训练 ResNet-50 模型最常用的数据集是 ImageNet-1K,所以这里我们使用 ImageNet-1K 训练模型。
1. 首先创建数据集的存放地址,放在 /workspace/datasets/imagenet 文件夹下,首先创建文件夹。
mkdir -p datasets/imagenetcd datasets/imagenet
2. 在该目录下直接下载 ImageNet-1K 数据集,包括训练集,验证集和标签映射文件三个文件。
下载训练集:
wget https://image-net.org/data/ILSVRC/2012/ILSVRC2012_img_train.tar --no-check-certificate
下载验证集:
wget https://image-net.org/data/ILSVRC/2012/ILSVRC2012_img_val.tar --no-check-certificate
下载标签映射文件:
wget https://image-net.org/data/ILSVRC/2012/ILSVRC2012_devkit_t12.tar.gz --no-check-certificate
注意:
训练集包含1000个类,共计1281167张图片,大小有138G;验证集包含1000个类,共计50000张图片,大小有6G。请确保机器拥有较大的容量下载数据集。此外请确保网络环境较好,ImageNet 训练集较大需要较长时间下载。
训练集解压
1. 将训练集解压到 train 目录下。
mkdir train && tar -xvf ILSVRC2012_img_train.tar -C train && for x in `ls train/*tar`; do fn=train/`basename $x .tar`; mkdir $fn; tar -xvf $x -C $fn; rm -f $fn.tar; done
2. 进入 train 目录下。
cd train
3. 查看该目录下的文件夹数量,若解压成功,正常情况下应该返回1000(代表1000个文件夹,数据集的1000个类)。
ls -lR|grep "^d"|wc -l
4. 查看该目录下的所有图片的数量,若解压成功,正常情况下应该返回1281167(代表一共有1281167张训练图像)。
ls -lR|grep "^-"|wc -l
验证集解压
1. 将验证集解压到 train 目录下。
mkdir val && tar xvf ILSVRC2012_img_val.tar -C ./val
2. 解压完成后,所有图像都在 val 文件夹里了。但是此时 val 下全是图像,没有被分到1000个文件夹里,需要将所有验证集图像分类到1000个类里去。
3. 解压标签映射文件 ILSVRC2012_devkit_t12.tar.gz,里面内容为每张图像对应的类别的映射关系。
tar -xzf ILSVRC2012_devkit_t12.tar.gz
4. 在 /imagenet 目录下创建 unzip.py 文件,写入以下代码:
from scipy import ioimport osimport shutildef move_valimg(val_dir='./val', devkit_dir='./ILSVRC2012_devkit_t12'):"""move valimg to correspongding folders.val_id(start from 1) -> ILSVRC_ID(start from 1) -> WINDorganize like:/val/n01440764images/n01443537images....."""# load synset, val ground truth and val images listsynset = io.loadmat(os.path.join(devkit_dir, 'data', 'meta.mat'))ground_truth = open(os.path.join(devkit_dir, 'data', 'ILSVRC2012_validation_ground_truth.txt'))lines = ground_truth.readlines()labels = [int(line[:-1]) for line in lines]root, _, filenames = next(os.walk(val_dir))for filename in filenames:# val image name -> ILSVRC ID -> WINDval_id = int(filename.split('.')[0].split('_')[-1])ILSVRC_ID = labels[val_id-1]WIND = synset['synsets'][ILSVRC_ID-1][0][1][0]print("val_id:%d, ILSVRC_ID:%d, WIND:%s" % (val_id, ILSVRC_ID, WIND))# move val imagesoutput_dir = os.path.join(root, WIND)if os.path.isdir(output_dir):passelse:os.mkdir(output_dir)shutil.move(os.path.join(root, filename), os.path.join(output_dir, filename))if __name__ == '__main__':move_valimg()
5. 执行该文件。
python unzip.py
这样验证集就被分到了1000个文件夹之下。
运行模型
DP 运行的文件为 dataparallel.py,其中有以下几处需要修改。
代码第118行,可以根据自己的 GPU 数量更改训练时使用的 GPU 数。例如我们这里拥有8块L40 GPU,我们将所有 GPU 用于训练模型。
#原有代码gpus = [0, 1, 2, 3]#更改为gpus = [0, 1, 2, 3, 4, 5, 6, 7]
代码第375行会报错,将 view() 方法更改为 reshape() 方法即可。
#原有代码correct_k = correct[:k].view(-1).float().sum(0, keepdim=True)#更改为correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True)
以上修改完之后,即可运行模型。这里我们测试的是 ResNet-50 模型通过 ImageNet-1K 数据集训练。
python dataparallel.py --data datasets/imagenet --arch resnet50 --epochs 120 --batch-size 256
此时会使用 ResNet-50 模型架构,从0开始训练120个 epochs,数据集在 datasets/imagenet 文件夹下,batchsize 为256,学习率 lr 为0.1,优化方法为 SGD 使用 momentum=0.9,权重衰退 weight decay=1e-4。参数设置来源于 ResNet 原论文:ResNet,从而尽可能的复现出论文里的结果。
说明:
如需要更改 batchsize,学习率,momentum 参数和权重衰减参数,只需加上对应参数
--batch-size
,--learning-rate
,--momentum
,--weight-decay
并设置想要的值即可。此时会开始训练模型,由于我们设置 DP 汇总梯度的 GPU为 gpus[0],(代码第138行 model = nn.DataParallel(model, device_ids=gpus, output_device=gpus[0])),所以 GPU0 的显存使用量会明显高于其他7块 GPU。
运行一个 epoch 的时间大约为半个小时,120个 epoch 大约需要两天训练完成。训练花费的时间可以在 dataparallel.csv 文件里找到。运行模型过程中会保存当前 epoch 训练结束之后的模型权重以及到当前 epoch 为止在验证集上最好性能的 epoch 的模型权重。
训练时命令行会出现训练时每一个 iteration 和测试时每一个 iteration 的性能以及每一个 epoch 测试完成之后的总的性能(例如第74个 epoch 的参考如下,top1 Acc 复现的结果还是比较接近论文中的结果):
...Test: [ 0/196] Time 1.253 ( 1.253) Loss 4.9941e-01 (4.9941e-01) Acc@1 86.33 ( 86.33) Acc@5 98.05 ( 98.05)Test: [ 10/196] Time 0.919 ( 0.551) Loss 8.8788e-01 (6.3331e-01) Acc@1 80.86 ( 83.91) Acc@5 93.36 ( 96.09)Test: [ 20/196] Time 0.878 ( 0.518) Loss 7.3491e-01 (6.8267e-01) Acc@1 85.16 ( 82.59) Acc@5 92.97 ( 95.48)Test: [ 30/196] Time 0.279 ( 0.498) Loss 8.0295e-01 (6.4392e-01) Acc@1 79.30 ( 83.58) Acc@5 94.92 ( 95.70)Test: [ 40/196] Time 0.050 ( 0.489) Loss 6.4413e-01 (6.9066e-01) Acc@1 82.81 ( 82.16) Acc@5 96.88 ( 95.57)Test: [ 50/196] Time 0.050 ( 0.490) Loss 4.8661e-01 (6.8918e-01) Acc@1 86.33 ( 81.78) Acc@5 97.66 ( 95.80)Test: [ 60/196] Time 0.050 ( 0.496) Loss 8.7274e-01 (7.0430e-01) Acc@1 77.34 ( 81.37) Acc@5 94.92 ( 95.86)Test: [ 70/196] Time 0.051 ( 0.497) Loss 7.7110e-01 (6.9134e-01) Acc@1 80.08 ( 81.71) Acc@5 95.31 ( 96.00)Test: [ 80/196] Time 0.049 ( 0.499) Loss 1.3679e+00 (7.1449e-01) Acc@1 63.28 ( 81.25) Acc@5 87.89 ( 95.73)Test: [ 90/196] Time 0.050 ( 0.497) Loss 1.9769e+00 (7.6551e-01) Acc@1 54.69 ( 80.10) Acc@5 84.38 ( 95.18)Test: [100/196] Time 0.053 ( 0.498) Loss 1.2061e+00 (8.1943e-01) Acc@1 67.58 ( 78.93) Acc@5 91.41 ( 94.58)Test: [110/196] Time 0.050 ( 0.496) Loss 8.6113e-01 (8.4266e-01) Acc@1 76.56 ( 78.43) Acc@5 94.14 ( 94.35)Test: [120/196] Time 0.051 ( 0.498) Loss 1.2805e+00 (8.6212e-01) Acc@1 70.31 ( 78.14) Acc@5 88.67 ( 94.05)Test: [130/196] Time 0.050 ( 0.495) Loss 6.3223e-01 (8.9568e-01) Acc@1 83.20 ( 77.31) Acc@5 97.27 ( 93.74)Test: [140/196] Time 0.050 ( 0.494) Loss 9.7670e-01 (9.1388e-01) Acc@1 75.39 ( 76.99) Acc@5 92.58 ( 93.53)Test: [150/196] Time 0.050 ( 0.494) Loss 1.0779e+00 (9.3261e-01) Acc@1 77.73 ( 76.66) Acc@5 89.45 ( 93.26)Test: [160/196] Time 0.050 ( 0.494) Loss 6.7641e-01 (9.4572e-01) Acc@1 85.94 ( 76.39) Acc@5 95.70 ( 93.11)Test: [170/196] Time 0.050 ( 0.493) Loss 6.4245e-01 (9.6438e-01) Acc@1 83.20 ( 75.96) Acc@5 97.27 ( 92.89)Test: [180/196] Time 0.050 ( 0.492) Loss 1.3427e+00 (9.8147e-01) Acc@1 66.41 ( 75.60) Acc@5 91.80 ( 92.69)Test: [190/196] Time 0.051 ( 0.493) Loss 1.2744e+00 (9.8115e-01) Acc@1 66.41 ( 75.53) Acc@5 94.53 ( 92.72)* Acc@1 75.674 Acc@5 92.778
同时dataparallel.csv可以看到训练的时间(参考):
...2024-08-13 08:36:33,2058.76851749420172024-08-13 09:10:52,2003.97724938392642024-08-13 09:44:17,2054.349834203722024-08-13 10:18:32,2003.6272692680362024-08-13 10:51:56,1993.66645216941832024-08-13 11:25:10,2023.82219409942632024-08-13 11:58:54,2009.36106395721442024-08-13 12:32:24,2041.42269134521482024-08-13 13:06:26,1945.22222208976752024-08-13 13:38:51,2009.6588840484622024-08-13 14:12:21,1953.27940368652342024-08-13 14:44:55,2023.0560326576233
同时目录下会出现 model_best.pth.tar 和 checkpoint.pth.tar,记录模型权重。
参考文档
ResNet