MXNet官方文档中文版教程(9):大规模图像分类

文档英文原版参见Large Scale Image Classification

训练大规模图像的神经网络具有很多挑战。即使使用最新的GPU,也不可能使用单个GPU在合理的时间内使用大量图像来训练大型网络。通过在单个机器中使用多个GPU可以稍微缓解这个问题。但是,可以连接到一台机器(通常为8或16个)的GPU数量是有限制的。本教程介绍了如何使用多台机器(每个包含多个GPU)来训练具有TB级数据的大型网络。

前提条件

为了完成以下教程,我们需要:

$ pip install opencv-python

预处理

磁盘空间

大数据训练的第一步是下载数据并对其进行预处理。对于本教程,我们将使用完整的ImageNet数据集。请注意,下载和预处理此数据至少需要2 TB的磁盘空间。强烈建议使用SSD而不是HDD。 SSD在处理大量小图像文件方面要好得多。预处理完成后,图像被打包到recordIO文件中,HDD应该适合训练。

在本教程中,我们将使用AWS存储实例进行数据预处理。存储实例i3.4xlarge 在两个NVMe SSD磁盘上具有3.8 TB的磁盘空间。我们将使用软件RAID将它们组合成一个磁盘,并将其安装在〜/ data

sudo mdadm --create --verbose /dev/md0 --level=stripe --raid-devices=2 \
    /dev/nvme0n1 /dev/nvme1n1
sudo mkfs /dev/md0
sudo mkdir ~/data
sudo mount /dev/md0 ~/data
sudo chown ${whoami} ~/data

我们现在有足够的磁盘空间来下载和预处理数据。

下载ImageNet

在本教程中,我们将使用从http://www.image-net.org/download-images下载的完整ImageNet数据集。 fall11_whole.tar 包含了所有图像。 该文件大小为1.2 TB,可能需要很长时间才能下载。

下载之后,解压文件。

export ROOT=full
mkdir $ROOT
tar -xvf fall11_whole.tar -C $ROOT

你将得到tar文件的集合。每个tar文件代表一个类别,并包含属于该类别的所有图像。我们可以解压每个tar文件,并将图像复制到以tar文件名称命名的文件夹中。

for i in $ROOT/*.tar; do j=${i%.*}; echo $j;  mkdir -p $j; tar -xf $i -C $j; done
rm $ROOT/*.tar

ls $ROOT | head
n00004475
n00005787
n00006024
n00006484
n00007846
n00015388
n00017222
n00021265
n00021939
n00120010

删除不常见的类用于迁移学习(可选)

在ImageNet数据上训练网络是为了将其用于迁移学习(包括特征提取或微调其他模型)。根据这项研究,图像太少的类别对迁移学习没有帮助。因此,我们可以删除少于一定数量图像的类别。 以下代码将删除少于500张图像的类别。

BAK=${ROOT}_filtered
mkdir -p ${BAK}
for c in ${ROOT}/n*; do
    count=`ls $c/*.JPEG | wc -l`
    if [ "$count" -gt "500" ]; then
        echo "keep $c, count = $count"
    else
        echo "remove $c, $count"
        mv $c ${BAK}/
    fi
done

产生验证集

为了确保不会过拟合,我们将创建一个与训练集分开的验证集。 在训练期间,我们将经常监督验证集上的损失。我们通过从每个类中挑选五十张随机图像并将它们放入验证集来创建验证集。

扫描二维码关注公众号,回复: 2490576 查看本文章
VAL_ROOT=${ROOT}_val
mkdir -p ${VAL_ROOT}
for i in ${ROOT}/n*; do
    c=`basename $i`
    echo $c
    mkdir -p ${VAL_ROOT}/$c
    for j in `ls $i/*.JPEG | shuf | head -n 50`; do
        mv $j ${VAL_ROOT}/$c/
    done
done

图像打包放入记录文件

MXNet可以直接读取图像文件,建议将图像文件打包到recordIO文件中以提高性能。MXNet提供了一个工具(tools/im2rec.py)来执行此操作。要使用此工具,需要在系统中安装MXNet和OpenCV的python模块。

将环境变量MXNET 设置为指向MXNet的安装目录,将NAME 指向数据集的名称。这里我们假设MXNet安装在~/mxnet

MXNET=~/mxnet
NAME=full_imagenet_500_filtered

要创建recordIO文件,我们首先在recordIO文件中创建我们想要的图像列表,然后使用im2rec 将列表中的图像打包到recordIO文件中。我们在train_meta 中创建这个列表。培训数据约为1TB。我们将其分为8部分,每部分大小约为100 GB。

mkdir -p train_meta
python ${MXNET}/tools/im2rec.py --list True --chunks 8 --recursive True \
train_meta/${NAME} ${ROOT}

然后,我们调整图像的大小,比如使得短边长为480像素,并将图像打包到recordIO文件中。 由于大多数工作是磁盘I / O,因此我们使用多个(16)线程来快速完成工作。

python ${MXNET}/tools/im2rec.py --resize 480 --quality 90 \
--num-thread 16 train_meta/${NAME} ${ROOT}

完成之后,我们将rec文件移动到名为train 的文件夹中。

mkdir -p train
mv train_meta/*.rec train/

在验证集上做相同的预处理:

mkdir -p val_meta
python ${MXNET}/tools/im2rec.py --list True --recursive True \
val_meta/${NAME} ${VAL_ROOT}
python ${MXNET}/tools/im2rec.py --resize 480 --quality 90 \
--num-thread 16 val_meta/${NAME} ${VAL_ROOT}
mkdir -p val
mv val_meta/*.rec val/

现在trainval 目录中分别是具有recordIO格式的所有训练和验证图像。 我们现在可以使用这些.rec文件进行训练。

训练

ResNet已经在ImageNet比赛中表现出其有效性。我们的实验也复现了论文中的结果。当我们将层数从18增加到152时,我们看到验证准确性有了很大提高。鉴于这是一个巨大的数据集,我们将使用152层的Resnet。

由于巨大的计算量,即使是最快的GPU,将整个数据集遍历一遍也需要超过一天的时间。 我们经常需要训练数十个批次才能达到良好的验证准确性。 虽然我们可以在机器中使用多个GPU,但机器中的GPU数量通常限制在8或16。为了更快地进行培训,在本教程中,我们将使用多台包含多个GPU的机器来训练该模型。

启动

我们将使用16台机器(P2.16x实例),每台机器包含16个GPU(特斯拉K80)。 这些机器通过20 Gbps以太网互连。

AWS CloudFormation使得创建深入学习集群变得非常简单。我们按照页面页面的指示,创建一个具有16个P2.16x实例的深度学习集群。

我们在第一台机器中加载数据和代码(我们将把这台机器称为主机)。 我们使用EFS将数据和代码共享给其他机器。

我们在第一台机器中加载数据和代码(我们将把这台机器称为主机)。 我们使用EFS将数据和代码共享给其他机器。

如果你手动设置集群,而不使用AWS CloudFormation,请记住执行以下操作:

  1. 使用USE_DIST_KVSTORE = 1 编译MXNet来启动分布式训练。
  2. 在主节点中创建主机文件,其中包含集群中所有计算机的主机名。 例如:
$ head -3 hosts
deeplearning-worker1
deeplearning-worker2
deeplearning-worker3

仅仅通过调用带有文件主机名的ssh就可以从master中ssh转换到这些机器中的任意一台。

$ ssh deeplearning-worker2
===================================
Deep Learning AMI for Ubuntu
===================================
...
ubuntu@ip-10-0-1-199:~$

一种方法是使用ssh代理转发。 请查看此页面了解如何设置。简而言之,你将使用本地机器上存在的特定证书(mycert.pem)将所有计算机配置为登录。然后使用证书和-A 交换机登录主服务器以启动代理转发。现在,从master的角度,你应该能够通过提供主机名(例如:ssh deeplearning-worker2)来登录群集中的任何其他机器。

开始训练

集群设置完成后,登录到master,在$ {MXNET} / example / image-classification目录下运行如下命令:

../../tools/launch.py -n 16 -H $DEEPLEARNING_WORKERS_PATH python train_imagenet.py --network resnet \
--num-layers 152 --data-train ~/data/train --data-val ~/data/val/ --gpus 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 \
--batch-size 8192 --model ~/data/model/resnet152 --num-epochs 1 --kv-store dist_sync

launch.py启动在集群中的所有机器提供的命令。必须使用-H 开关将集群中的计算机列表提供给launch.py。这是launch.py的选项的描述。

选项
描述
n 指定在每台机器上运行的任务数。我们有16名“工人”,因为我们在集群中有16台机器。
H 指定具有集群中机器主机名列表的文件的路径。由于我们使用AWS深度学习CloudFormation模板创建集群,所以环境变量*$ DEEPLEARNING_WORKERS_PATH* 指向要求的文件
train_imagenet.py使用*–data-train* 和*–data-val* 选项提供的数据和*–network* 选项提供的网络来训练。 以下是train_imagenet.py选项的说明。
网络 网络训练。 可以是*$ {MXNET}/example/image-classification* 中的任何网络。对于本教程,我们使用ResNet
层数 网络中使用的层的数量。我们使用的是152层的ResNet
训练数据 包含训练图像的目录。 我们指向存储训练图像的EFS位置(*〜/ data/train/*)。
验证数据 包含验证图像的目录。 我们指向存储验证图像的EFS位置(*〜/ data/val/*)。
GPU 用逗号分隔每台机器上训练的GPU的列表。 我们使用所有16个GPU。
批次大小 所有GPU中的批量大小。这等于每个GPU的批量大小* GPU总数。 我们在每个GPU使用32张图像的批量大小。因此,有效批量大小为32 * 16 * 16 = 8192。
模型 由训练创建的模型文件的路径前缀
批次数量 训练的批次数量
kv存储 参数同步的键/值存储。我们使用分布式kv存储,因为我们正在进行分布训练。

训练完成后,训练的模型存储在–model 选项指定的目录中。模型分两部分保存:model-symbol.json用于网络定义,model-n.params保存第n个批次后的参数。

可扩展性

使用大量机器进行训练的一个常见问题是可扩展性。我们在集群上跑这样常见的网络的标准可扩展性是使用高达256个GPU,它的速度已经非常接近理想情况了。

这种可扩展性测试是运行在十六个p2.16xl实例上,总共使用256 个GPU。我们使用安装了CUDA 7.5和cudnn 5.1的AWS的深度学习AMI。

我们固定每个GPU的批量大小,并对每一个测试子集将GPU数量加倍。使用同步SGD(–kV存储 dist_device_sync)。

alexnet inception-v3 resnet-152
批次大小/GPU 512 32 32
模型大小(MB) 203 95 240

每秒处理的图像数目如下表所示:

这里写图片描述

下图显示了GPU的使用数量与理想的情况下的加速比。

这里写图片描述

故障排除指南

验证集准确率

实现合理的验证集准确性通常是很简单的,但是实现论文中报告的最先进的结果有时是非常困难的。这里有几件事你可以试着去提高验证的准确性。

  • 数据增强通常会减少训练和验证准确率之间的差距。从开始训练到结束的过程中可以减少数据增强。
  • 从一个大的学习率开始,并保持较长时间。例如,在cifar10中,你可以在前200个批次保持学习率为0.1,之后把它减少到0.01。
  • 不要使用过大的批量大小,特别是批量大小>类的数量的时候。

速度

  • 当一个批次的计算量过大时,分布式训练会提高速度。所以,确保你的工作量不要太小(像LeNet在MNIST)。确保批量大小足够大。
  • 确保数据读取和预处理不是瓶颈。使用–test-io 1 标志来检查每秒可以预处理多少图像。
  • 增加–data -Nthreads(默认是4),使用多线程进行数据预处理。
  • 用opencv完成数据预处理。如果opencv是从源码编译的,请检查它是否配置正确。
  • 使用–benchmark 1 随机生成的数据而不是实际数据来缩小瓶颈。

内存

如果批量太大,它会耗尽GPU内存。在这种情况下,你会看到错误消息“cuda分配失败:内存不足”或类似的东西。有几种方法可以解决这个问题:

  • 减小批量大小
  • 设置环境变量mxnet_backward_do_mirror 为1。它通过降低速度来减少内存消耗。例如,批量大小的64的inception-v3采用10G内存在单个K80 GPU上每秒训练30张图像。当采用镜像后,同样10G的GPU内存消耗,我们可以利用128的批量大小来训练inception-v3。代价是,速度降低到27张/秒。

猜你喜欢

转载自blog.csdn.net/qq_36165459/article/details/78394450