读书笔记《Deep Learning for Computer Vision with Python》- 第三卷 第5章 在ImageNet上训练VGGNet

2021/12/26 22:10:34

本文主要是介绍读书笔记《Deep Learning for Computer Vision with Python》- 第三卷 第5章 在ImageNet上训练VGGNet,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

        第三卷 第五章 在ImageNet上训练VGGNet

        在本章中,我们将从头开始学习如何在 ImageNet 数据集上训练 VGG16 网络架构。

        该网络的特点是简单,仅使用3*3 卷积层堆叠在彼此之上,深度逐渐增加。 减少体积的空间维度是通过使用最大池化来实现的。 两个完全连接的层,每个层有 4,096 个节点(以及中间的 dropout),然后是一个 softmax 分类器。

        今天,VGG 经常用于迁移学习,因为该网络表现出高于平均水平的能力,可以将其泛化到未经训练的数据集(与其他网络类型如 GoogLeNet 和 ResNet 相比)。但是从头开始训练 VGG 是一种痛苦。 该网络的训练速度非常缓慢,并且网络架构本身的权重非常大(超过 500MB)。如果您没有至少四个 GPU,我建议您不要训练。由于网络的深度以及全连接层,反向传播阶段非常缓慢。在 8 个 GPU 上训练 VGG 需要 10 天——如果少于 4 个 GPU,从头开始训练 VGG 可能需要非常长的时间(除非你非常有耐心)。也就是说,作为深度学习从业者,重要的是 了解深度学习的历史,尤其是预训练的概念,以及我们后来如何通过优化初始化权重函数来避免这种昂贵的操作。

        本章将重点介绍 PReLU 激活函数和 MSRA 初始化的正确使用。

        1、实施VGGNet

通常称为 VGG16

        无论何时训练 VGG16 或 VGG19,请确保:

        1. 将所有 ReLU 换成 PReLU。

        2. 使用 MSRA(也称为“He”)来初始化网络中的权重层。    

        创建mxvggnet.py文件。

# import the necessary packages
import mxnet as mx

class MxVGGNet:
    @staticmethod
    def build(classes):
        # data input
        data = mx.sym.Variable("data")

        # Block #1: (CONV => RELU) * 2 => POOL
        conv1_1 = mx.sym.Convolution(data=data, kernel=(3, 3), pad = (1, 1), num_filter = 64, name = "conv1_1")
        act1_1 = mx.sym.LeakyReLU(data=conv1_1, act_type="prelu", name = "act1_1")
        bn1_1 = mx.sym.BatchNorm(data=act1_1, name="bn1_1")
        conv1_2 = mx.sym.Convolution(data=bn1_1, kernel=(3, 3), pad = (1, 1), num_filter = 64, name = "conv1_2")
        act1_2 = mx.sym.LeakyReLU(data=conv1_2, act_type="prelu", name = "act1_2")
        bn1_2 = mx.sym.BatchNorm(data=act1_2, name="bn1_2")
        pool1 = mx.sym.Pooling(data=bn1_2, pool_type="max", kernel = (2, 2), stride = (2, 2), name = "pool1")
        do1 = mx.sym.Dropout(data=pool1, p=0.25)

        # Block #2: (CONV => RELU) * 2 => POOL
        conv2_1 = mx.sym.Convolution(data=do1, kernel=(3, 3), pad = (1, 1), num_filter = 128, name = "conv2_1")
        act2_1 = mx.sym.LeakyReLU(data=conv2_1, act_type="prelu", name = "act2_1")
        bn2_1 = mx.sym.BatchNorm(data=act2_1, name="bn2_1")
        conv2_2 = mx.sym.Convolution(data=bn2_1, kernel=(3, 3), pad = (1, 1), num_filter = 128, name = "conv2_2")
        act2_2 = mx.sym.LeakyReLU(data=conv2_2, act_type="prelu", name = "act2_2")
        bn2_2 = mx.sym.BatchNorm(data=act2_2, name="bn2_2")
        pool2 = mx.sym.Pooling(data=bn2_2, pool_type="max", kernel = (2, 2), stride = (2, 2), name = "pool2")
        do2 = mx.sym.Dropout(data=pool2, p=0.25)

        # Block #3: (CONV => RELU) * 3 => POOL
        conv3_1 = mx.sym.Convolution(data=do2, kernel=(3, 3), pad = (1, 1), num_filter = 256, name = "conv3_1")
        act3_1 = mx.sym.LeakyReLU(data=conv3_1, act_type="prelu", name = "act3_1")
        bn3_1 = mx.sym.BatchNorm(data=act3_1, name="bn3_1")
        conv3_2 = mx.sym.Convolution(data=bn3_1, kernel=(3, 3), pad = (1, 1), num_filter = 256, name = "conv3_2")
        act3_2 = mx.sym.LeakyReLU(data=conv3_2, act_type="prelu", name = "act3_2")
        bn3_2 = mx.sym.BatchNorm(data=act3_2, name="bn3_2")
        conv3_3 = mx.sym.Convolution(data=bn3_2, kernel=(3, 3), pad = (1, 1), num_filter = 256, name = "conv3_3")
        act3_3 = mx.sym.LeakyReLU(data=conv3_3, act_type="prelu", name = "act3_3")
        bn3_3 = mx.sym.BatchNorm(data=act3_3, name="bn3_3")
        pool3 = mx.sym.Pooling(data=bn3_3, pool_type="max", kernel = (2, 2), stride = (2, 2), name = "pool3")
        do3 = mx.sym.Dropout(data=pool3, p=0.25)

        # Block #4: (CONV => RELU) * 3 => POOL
        conv4_1 = mx.sym.Convolution(data=do3, kernel=(3, 3), pad=(1, 1), num_filter=512, name="conv4_1")
        act4_1 = mx.sym.LeakyReLU(data=conv4_1, act_type="prelu", name="act4_1")
        bn4_1 = mx.sym.BatchNorm(data=act4_1, name="bn4_1")
        conv4_2 = mx.sym.Convolution(data=bn4_1, kernel=(3, 3), pad=(1, 1), num_filter=512, name="conv4_2")
        act4_2 = mx.sym.LeakyReLU(data=conv4_2, act_type="prelu", name="act4_2")
        bn4_2 = mx.sym.BatchNorm(data=act4_2, name="bn4_2")
        conv4_3 = mx.sym.Convolution(data=bn4_2, kernel=(3, 3), pad=(1, 1), num_filter=512, name="conv4_3")
        act4_3 = mx.sym.LeakyReLU(data=conv4_3, act_type="prelu", name="act4_3")
        bn4_3 = mx.sym.BatchNorm(data=act4_3, name="bn4_3")
        pool4 = mx.sym.Pooling(data=bn4_3, pool_type="max", kernel=(2, 2), stride=(2, 2), name="pool3")
        do4 = mx.sym.Dropout(data=pool4, p=0.25)

        # Block #5: (CONV => RELU) * 3 => POOL
        conv5_1 = mx.sym.Convolution(data=do4, kernel=(3, 3), pad = (1, 1), num_filter = 512, name = "conv5_1")
        act5_1 = mx.sym.LeakyReLU(data=conv5_1, act_type="prelu", name = "act5_1")
        bn5_1 = mx.sym.BatchNorm(data=act5_1, name="bn5_1")
        conv5_2 = mx.sym.Convolution(data=bn5_1, kernel=(3, 3), pad = (1, 1), num_filter = 512, name = "conv5_2")
        act5_2 = mx.sym.LeakyReLU(data=conv5_2, act_type="prelu", name = "act5_2")
        bn5_2 = mx.sym.BatchNorm(data=act5_2, name="bn5_2")
        conv5_3 = mx.sym.Convolution(data=bn5_2, kernel=(3, 3), pad = (1, 1), num_filter = 512, name = "conv5_3")
        act5_3 = mx.sym.LeakyReLU(data=conv5_3, act_type="prelu", name = "act5_3")
        bn5_3 = mx.sym.BatchNorm(data=act5_3, name="bn5_3")
        pool5 = mx.sym.Pooling(data=bn5_3, pool_type="max", kernel = (2, 2), stride = (2, 2), name = "pool5")
        do5 = mx.sym.Dropout(data=pool5, p=0.25)

        # Block #6: FC => RELU layers
        flatten = mx.sym.Flatten(data=do5, name="flatten")
        fc1 = mx.sym.FullyConnected(data=flatten, num_hidden=4096, name="fc1")
        act6_1 = mx.sym.LeakyReLU(data=fc1, act_type="prelu", name="act6_1")
        bn6_1 = mx.sym.BatchNorm(data=act6_1, name="bn6_1")
        do6 = mx.sym.Dropout(data=bn6_1, p=0.5)

        # Block #7: FC => RELU layers
        fc2 = mx.sym.FullyConnected(data=do6, num_hidden=4096, name="fc2")
        act7_1 = mx.sym.LeakyReLU(data=fc2, act_type="prelu", name="act7_1")
        bn7_1 = mx.sym.BatchNorm(data=act7_1, name="bn7_1")
        do7 = mx.sym.Dropout(data=bn7_1, p=0.5)

        # softmax classifier
        fc3 = mx.sym.FullyConnected(data=do7, num_hidden=classes, name = "fc3")
        model = mx.sym.SoftmaxOutput(data=fc3, name="softmax")
        # return the network architecture
        return model

        2、训练VGGNet

        创建imagenet_vggnet_config.py文件。参考下面的链接,

读书笔记《Deep Learning for Computer Vision with Python》- 第三卷 第3章 准备ImageNet(2)_bashendixie5的博客-CSDN博客第三卷 第三章 准备ImageNet(2)1、构建 ImageNet 数据集构建 ImageNet 数据集的总体目标是让我们可以从头开始训练卷积神经网络。 因此,我们将在为 CNN 做准备的背景下回顾构建 ImageNet 数据集。 为此,我们将首先定义一个配置文件,该文件存储所有相关的图像路径、纯文本路径以及我们希望包含的任何其他设置。我们将定义一个名为 ImageNetHelper 的 Python 类,使我们能够快速轻松地构建...https://blog.csdn.net/bashendixie5/article/details/122149332        只修改

        BATCH_SIZE = 32
        NUM_DEVICES = 8

         创建train_vggnet.py文件。

# import the necessary packages
import imagenet_vggnet_config as config
from mxvggnet import MxVGGNet
import mxnet as mx
import argparse
import logging
import json
import os

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-c", "--checkpoints", required=True, help="path to output checkpoint directory")
ap.add_argument("-p", "--prefix", required=True, help="name of model prefix")
ap.add_argument("-s", "--start-epoch", type=int, default=0, help="epoch to restart training at")
args = vars(ap.parse_args())

# set the logging level and output file
logging.basicConfig(level=logging.DEBUG, filename="training_{}.log".format(args["start_epoch"]), filemode="w")

# load the RGB means for the training set, then determine the batch
# size
means = json.loads(open(config.DATASET_MEAN).read())
batchSize = config.BATCH_SIZE * config.NUM_DEVICES

# construct the training image iterator
trainIter = mx.io.ImageRecordIter(
    path_imgrec=config.TRAIN_MX_REC,
    data_shape=(3, 224, 224),
    batch_size=batchSize,
    rand_crop=True,
    rand_mirror=True,
    rotate=15,
    max_shear_ratio=0.1,
    mean_r=means["R"],
    mean_g=means["G"],
    mean_b=means["B"],
    preprocess_threads=config.NUM_DEVICES * 2)

# construct the validation image iterator
valIter = mx.io.ImageRecordIter(
    path_imgrec=config.VAL_MX_REC,
    data_shape=(3, 224, 224),
    batch_size=batchSize,
    mean_r=means["R"],
    mean_g=means["G"],
    mean_b=means["B"])

# initialize the optimizer
opt = mx.optimizer.SGD(learning_rate=1e-2, momentum=0.9, wd=0.0005, rescale_grad=1.0 / batchSize)

# construct the checkpoints path, initialize the model argument and
# auxiliary parameters
checkpointsPath = os.path.sep.join([args["checkpoints"], args["prefix"]])
argParams = None
auxParams = None

# if there is no specific model starting epoch supplied, then
# initialize the network
if args["start_epoch"] <= 0:
    # build the LeNet architecture
    print("[INFO] building network...")
    model = MxVGGNet.build(config.NUM_CLASSES)

# otherwise, a specific checkpoint was supplied
else:
    # load the checkpoint from disk
    print("[INFO] loading epoch {}...".format(args["start_epoch"]))
    model = mx.model.FeedForward.load(checkpointsPath, args["start_epoch"])

    # update the model and parameters
    argParams = model.arg_params
    auxParams = model.aux_params
    model = model.symbol

# compile the model
model = mx.model.FeedForward(
    ctx=[mx.gpu(i) for i in range(0, config.NUM_DEVICES)],
    symbol=model,
    initializer=mx.initializer.MSRAPrelu(),
    arg_params=argParams,
    aux_params=auxParams,
    optimizer=opt,
    num_epoch=80,
    begin_epoch=args["start_epoch"])

# initialize the callbacks and evaluation metrics
batchEndCBs = [mx.callback.Speedometer(batchSize, 250)]
epochEndCBs = [mx.callback.do_checkpoint(checkpointsPath)]
metrics = [mx.metric.Accuracy(), mx.metric.TopKAccuracy(top_k=5), mx.metric.CrossEntropy()]

# train the network
print("[INFO] training network...")
model.fit(
    X=trainIter,
    eval_data=valIter,
    eval_metric=metrics,
    batch_end_callback=batchEndCBs,
    epoch_end_callback=epochEndCBs)

         3、评估VGGNet

        为了评估 VGGNet,我们将使用上一章中的 test_alexnet.py 。 那里没有对脚本进行任何更改,因为本章中的 test_*.py 脚本旨在成为可以应用和重新应用到任何在 ImageNet 上训练的 CNN 的模板。

        4、VGGNet 实验

        在这个实验中,我使用SGD优化器来训练VGG16,初始学习率为1e-2,动量项为0.9,L2权重正则化为 0.0005。为了加快训练速度,我使用了一个带有8个GPU的AmazonEC2实例。除非您非常有耐心,否则我不建议您尝试在GPU少于四个的机器上训练VGG。

        我使用以下命令开始了 VGG 训练过程:

         我让网络训练到第 50 期,此时训练和验证准确度似乎都停滞不前(图 7.2,左上角)。 然后我从 train_vggnet.py 脚本中按 ctrl + c 并将学习率从 1e-2 降低到 1e-3:

opt = mx.optimizer.SGD(learning_rate=1e-3, momentum=0.9, wd=0.0005, rescale_grad=1.0 / batchSize)

        然后使用以下命令恢复训练:

         在下图(右上)中,您可以看到在 20 个时期内降低学习率的结果。 您可以立即看到训练和验证准确度的大幅提升。

        在第 70 时期之后,我再次注意到验证损失/准确率停滞,而训练损失继续下降——该指标表明过度拟合开始发生。

        为了尽可能地从 VGG 中榨取最后一点性能(不会过度拟合太可怕),我再次将学习率从 1e3 降低到 1e4,并在第 70 期重新开始训练:

         然后我让网络继续训练另外10个时期,直到第80个时期,在那里我应用了早期停止标准(图7.2,底部)。在第80个时期结束时,VGG16获得了68.77%的rank-1和88.78%的rank-5验证准确率。然后我使用以下命令在测试集上评估了第80个时期:

        正如我的输出所示,VGG16 达到了 71.42% rank-1 和 90.03% rank-5 准确率,这与 Simonyan 和 Zisserman 的原始 VGGNet 论文几乎相同。 为完整起见,我在下表中包含了我的学习率计划,供希望复制这些结果的读者使用。

         VGG16 的最大缺点(除了训练需要多长时间)是由此产生的模型大小,重量超过 533MB。

        幸运的是,我们将在本包中讨论的所有剩余模型都比VGGNet小得多。我们高度准确的ResNet模型的权重为102MB。GoogLeNet更小,只有28MB。并且超小、高效的SqueezeNet模型大小仅为4.9MB,非常适合任何类型的资源受限深度学习。

        5、小结

        在本章中,我们使用mxnet库实现了VGG16架构,并在ImageNet数据集上从头开始训练。我们没有使用繁琐、耗时的预训练过程来训练较小版本的网络架构,然后使用这些预训练的权重作为我们更深层次架构的初始化,而是跳过了这一步,依赖于 He 等人的工作 阿尔。 和米什金等人:

        1. 我们用 PReLU 替换了标准的 ReLU 激活。

        2. 我们为 MSRA/He 等人交换了 Glorot/Xavier 权重初始化。

        这个过程使我们能够在一个实验中复制 Simonyan 和 Zisserman 的工作。无论何时从头开始训练类似 VGG 的架构,一定要考虑尽可能使用 PReLUs 和 MSRA 初始化。 在某些网络架构中,使用 PReLU + MSRA 时您不会注意到对性能的影响,但使用 VGG 时,影响是巨大的。

        总的来说,我们的VGG16版本在ImageNet测试集上获得了71.42% rank-1 和 90.03% rank-5 的准确率,这是我们迄今为止在这个包中看到的最高准确率。此外,VGG 架构已经证明自己非常适合泛化任务。在下一章中,我们将更详细地探索微架构,包括GoogLeNet 模型,它将为包括ResNet和SqueezeNet在内的更专业的微架构奠定基础。



这篇关于读书笔记《Deep Learning for Computer Vision with Python》- 第三卷 第5章 在ImageNet上训练VGGNet的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程