ResNet

最近更新时间:2024-08-28 17:44:21

我的收藏
本指导适用于在 TencentOS Server 3上使用 DeepSpeed 训练框架运行 ResNet 模型的官方 Demo,以 Docker 方式启动。

环境准备

1. 从 GitHub 下载 DeepSpeed 的 Demo 开源仓库到本地。
git clone https://github.com/microsoft/DeepSpeedExamples.git
cd DeepSpeedExamples/
2. 由于 DeepSpeed 镜像已经长期没有更新,我们使用 Pytorch 的基础镜像即可包含大部分运行环境。这里我们拉取 Nvidia 的基础镜像来启动容器。
docker run -it --gpus all --name=deepspeed -e HF_ENDPOINT="https://hf-mirror.com" --ipc=host -v $PWD:/workspace nvcr.io/nvidia/pytorch:23.06-py3 /bin/bash
此时会从 nvcr 拉取 docker 镜像,请确保网络环境较好,直到镜像里所有层下载成功。成功后会直接进入容器内部。
注意:
使用 -e HF_ENDPOINT="https://hf-mirror.com" 是因为其中有些 examples 会从 Hugging Face 导入某些模型或者参数,我们需要保证与 Hugging Face 的通信是正常的。

数据集下载

我们使用 DeepSpeed 训练框架运行 ResNet-18 模型,官方预训练 ResNet-18 模型最常用的数据集是 ImageNet-1K,所以这里我们使用 ImageNet-1K 训练模型。
1. 首先创建数据集的存放地址,放在 /workspace/datasets/imagenet 文件夹下,首先创建文件夹。
mkdir -p datasets/imagenet
cd 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 io
import os
import shutil

def 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) -> WIND
organize like:
/val
/n01440764
images
/n01443537
images
.....
"""
# load synset, val ground truth and val images list
synset = 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 -> WIND
val_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 images
output_dir = os.path.join(root, WIND)
if os.path.isdir(output_dir):
pass
else:
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个文件夹之下。

运行模型

安装运行模型必要的包

1. 本示例使用 DeepSpeed 运行 ResNet-18 模型,位于 training/imagenet/ 文件夹下。
cd training/imagenet/
2. 将 pip 换为国内清华源以加快下载速度。
#将pip换成清华源
#设为默认,永久有效
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
3. 安装运行 DeepSpeed 必要的包,通过 pip 下载。
#安装所有需要的包
pip install datasets fire loguru sh deepspeed transformers openpyxl
所有需要的包安装成功之后,即可开始准备运行模型。

运行模型

使用 main.py 文件运行模型,首先需要对 main.py 内部代码进行一些修改。
将32行的 data 变成可添加的参数,在前面加上“--”即可。
#修改前
parser.add_argument('data', metavar='DIR', nargs='?', default='imagenet',

#修改后
parser.add_argument('--data', metavar='DIR', nargs='?', default='imagenet',
修改好之后进行保存,即可运行模型。
deepspeed main.py -a resnet18 --deepspeed --deepspeed_config config/ds_config.json --data /workspace/datasets/imagenet --multiprocessing_distributed
注意:
可以在 config/ds_config.json 里更改训练的参数,同时命令行也可以对训练参数进行更改。可更改参数如下:
parser.add_argument('--data', metavar='DIR', nargs='?', default='imagenet',
help='path to dataset (default: imagenet)')
parser.add_argument('-a', '--arch', metavar='ARCH', default='resnet18',
choices=model_names,
help='model architecture: ' +
' | '.join(model_names) +
' (default: resnet18)')
parser.add_argument('-j', '--workers', default=4, type=int, metavar='N',
help='number of data loading workers (default: 4)')
parser.add_argument('--epochs', default=90, type=int, metavar='N',
help='number of total epochs to run')
parser.add_argument('--start-epoch', default=0, type=int, metavar='N',
help='manual epoch number (useful on restarts)')
parser.add_argument('-b', '--batch-size', default=256, type=int,
metavar='N',
help='mini-batch size (default: 256), this is the total '
'batch size of all GPUs on the current node when '
'using Data Parallel or Distributed Data Parallel')
parser.add_argument('--lr', '--learning-rate', default=0.1, type=float,
metavar='LR', help='initial learning rate', dest='lr')
parser.add_argument('--momentum', default=0.9, type=float, metavar='M',
help='momentum')
parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float,
metavar='W', help='weight decay (default: 1e-4)',
dest='weight_decay')
parser.add_argument('-p', '--print-freq', default=10, type=int,
metavar='N', help='print frequency (default: 10)')
parser.add_argument('--resume', default='', type=str, metavar='PATH',
help='path to latest checkpoint (default: none)')
parser.add_argument('-e', '--evaluate', dest='evaluate', action='store_true',
help='evaluate model on validation set')
parser.add_argument('--pretrained', dest='pretrained', action='store_true',
help='use pre-trained model')
parser.add_argument('--world-size', default=-1, type=int,
help='number of nodes for distributed training')
parser.add_argument('--seed', default=None, type=int,
help='seed for initializing training. ')
parser.add_argument('--gpu', default=None, type=int,
help='GPU id to use.')
parser.add_argument('--multiprocessing_distributed', action='store_true',
help='Use multi-processing distributed training to launch '
'N processes per node, which has N GPUs. This is the '
'fastest way to use PyTorch for either single node or '
'multi node data parallel training')
parser.add_argument('--dummy', action='store_true', help="use fake data to benchmark")
parser.add_argument('--local_rank', type=int, default=-1, help="local rank for distributed training on gpus")

parser.add_argument('--data', metavar='DIR', nargs='?', default='imagenet',
help='path to dataset (default: imagenet)')
parser.add_argument('-a', '--arch', metavar='ARCH', default='resnet18',
choices=model_names,
help='model architecture: ' +
' | '.join(model_names) +
' (default: resnet18)')
parser.add_argument('-j', '--workers', default=4, type=int, metavar='N',
help='number of data loading workers (default: 4)')
parser.add_argument('--epochs', default=90, type=int, metavar='N',
help='number of total epochs to run')
parser.add_argument('--start-epoch', default=0, type=int, metavar='N',
help='manual epoch number (useful on restarts)')
parser.add_argument('-b', '--batch-size', default=256, type=int,
metavar='N',
help='mini-batch size (default: 256), this is the total '
'batch size of all GPUs on the current node when '
'using Data Parallel or Distributed Data Parallel')
parser.add_argument('--lr', '--learning-rate', default=0.1, type=float,
metavar='LR', help='initial learning rate', dest='lr')
parser.add_argument('--momentum', default=0.9, type=float, metavar='M',
help='momentum')
parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float,
metavar='W', help='weight decay (default: 1e-4)',
dest='weight_decay')
parser.add_argument('-p', '--print-freq', default=10, type=int,
metavar='N', help='print frequency (default: 10)')
parser.add_argument('--resume', default='', type=str, metavar='PATH',
help='path to latest checkpoint (default: none)')
parser.add_argument('-e', '--evaluate', dest='evaluate', action='store_true',
help='evaluate model on validation set')
parser.add_argument('--pretrained', dest='pretrained', action='store_true',
help='use pre-trained model')
parser.add_argument('--world-size', default=-1, type=int,
help='number of nodes for distributed training')
parser.add_argument('--seed', default=None, type=int,
help='seed for initializing training. ')
parser.add_argument('--gpu', default=None, type=int,
help='GPU id to use.')
parser.add_argument('--multiprocessing_distributed', action='store_true',
help='Use multi-processing distributed training to launch '
'N processes per node, which has N GPUs. This is the '
'fastest way to use PyTorch for either single node or '
'multi node data parallel training')
parser.add_argument('--dummy', action='store_true', help="use fake data to benchmark")
parser.add_argument('--local_rank', type=int, default=-1, help="local rank for distributed training on gpus")
模型运行中途会保存训练好的权重参数,训练输出如下所示(参考):
...
Test: [171/196] Time 0.119 ( 0.034) Loss 1.3480e+00 (1.2659e+00) Acc@1 68.75 ( 68.97) Acc@5 93.75 ( 89.18)
Test: [171/196] Time 0.119 ( 0.034) Loss 5.9162e-01 (1.2233e+00) Acc@1 90.62 ( 69.35) Acc@5 96.88 ( 89.71)
Test: [181/196] Time 0.074 ( 0.032) Loss 1.3154e+00 (1.2337e+00) Acc@1 59.38 ( 69.82) Acc@5 90.62 ( 89.33)
Test: [181/196] Time 0.027 ( 0.033) Loss 2.0253e+00 (1.2606e+00) Acc@1 53.12 ( 68.89) Acc@5 81.25 ( 88.52)
Test: [181/196] Time 0.109 ( 0.033) Loss 1.0418e+00 (1.2571e+00) Acc@1 65.62 ( 69.54) Acc@5 96.88 ( 89.04)
Test: [181/196] Time 0.005 ( 0.033) Loss 1.4672e+00 (1.3019e+00) Acc@1 56.25 ( 69.32) Acc@5 84.38 ( 88.00)
Test: [181/196] Time 0.105 ( 0.033) Loss 1.2578e+00 (1.2628e+00) Acc@1 56.25 ( 69.30) Acc@5 93.75 ( 89.17)
Test: [181/196] Time 0.005 ( 0.033) Loss 1.7441e+00 (1.2842e+00) Acc@1 59.38 ( 68.56) Acc@5 78.12 ( 88.98)
Test: [181/196] Time 0.005 ( 0.033) Loss 1.0964e+00 (1.2335e+00) Acc@1 65.62 ( 69.01) Acc@5 93.75 ( 89.55)
Test: [171/196] Time 0.092 ( 0.035) Loss 8.8425e-01 (1.2210e+00) Acc@1 75.00 ( 70.19) Acc@5 93.75 ( 88.98)
Test: [191/196] Time 0.005 ( 0.032) Loss 1.1533e+00 (1.2184e+00) Acc@1 65.62 ( 70.09) Acc@5 93.75 ( 89.59)
Test: [191/196] Time 0.005 ( 0.032) Loss 1.1176e+00 (1.2504e+00) Acc@1 71.88 ( 69.65) Acc@5 96.88 ( 89.10)
Test: [191/196] Time 0.010 ( 0.033) Loss 1.4503e+00 (1.3034e+00) Acc@1 65.62 ( 69.32) Acc@5 84.38 ( 87.93)
Test: [191/196] Time 0.062 ( 0.033) Loss 1.7334e+00 (1.2584e+00) Acc@1 50.00 ( 69.01) Acc@5 87.50 ( 88.53)
Test: [191/196] Time 0.005 ( 0.033) Loss 1.2310e+00 (1.2586e+00) Acc@1 78.12 ( 69.42) Acc@5 90.62 ( 89.17)
Test: [191/196] Time 0.109 ( 0.033) Loss 2.0439e+00 (1.2897e+00) Acc@1 56.25 ( 68.39) Acc@5 75.00 ( 88.89)
Test: [181/196] Time 0.054 ( 0.035) Loss 6.9501e-01 (1.2399e+00) Acc@1 78.12 ( 69.82) Acc@5 100.00 ( 88.81)
Test: [191/196] Time 0.118 ( 0.034) Loss 1.4798e+00 (1.2315e+00) Acc@1 50.00 ( 68.88) Acc@5 87.50 ( 89.59)
Test: [191/196] Time 0.078 ( 0.035) Loss 1.0073e+00 (1.2461e+00) Acc@1 68.75 ( 69.63) Acc@5 93.75 ( 88.79)
* Acc@1 69.496 Acc@5 89.034
* Acc@1 69.496 Acc@5 89.034
* Acc@1 69.496 Acc@5 89.034
* Acc@1 69.496 Acc@5 89.034
* Acc@1 69.496 Acc@5 89.034
* Acc@1 69.496 Acc@5 89.034
* Acc@1 69.496 Acc@5 89.034
* Acc@1 69.496 Acc@5 89.034
同时目录下会出现 model_best.pth.tar 记录精度最好的模型权重。可以看到测试时同一个 iteration 有多条记录,这是因为运行时在每一张卡上都有一个进程,每一张卡都会打印一条记录。训练完成之后目录下会出现 Acc_loss_log.xlsx,用于记录精度和 loss 变化。

参考文档

ResNet