从VGG到ResNet,你想要的MXNet预训练模型轻松学

本文介绍了如何利用 Apache MXNet 预训练出的多个模型。每个模型在特定图像上的表现略有不同,训练多个模型旨在找出更适合特定任务的模型。

在这篇博文中,你将会了解如何使用 Apache MXNet 预训练出的多个模型。为什么要尝试多个模型呢?为什么不直接选择准确率最高的呢?稍后我们会在文章中看到,尽管这些模型是在相同的数据集上训练的,并且都针对最大准确率进行了优化,但它们在特定图像上的表现略有不同。此外,(不同模型)预测速度也不同,而对很多应用来说速度是一个重要的影响因素。尝试这些模型,或许能找到一个合适的模型解决手头上的难题。首先,我们先从 Apache MXNet 模型库中下载三个图像分类模型。(模型库地址:http://mxnet.io/model_zoo/)

三个模型分别是:

  • VGG-16,获得 2014 年 ImageNet 大规模视觉识别挑战赛分类项目冠军。

  • Inception v3,GoogleNet 的进化版,获得 2014 年比赛的目标检测项目冠军。

  • ResNet-152,获得 2015 年比赛的多个项目的冠军。

我们需要为每一个模型下载两个文件:

  • 包含神经网络 JSON 定义的符号文件:层、连接、激活函数等。

  • 网络在训练阶段学习到的存储了所有连接权重、偏置项和 AKA 参数权重文件。

# MacOS users can easily install 'wget' with Homebrew: 'brew install wget'
!wget http://data.dmlc.ml/models/imagenet/vgg/vgg16-symbol.json -O vgg16-symbol.json
!wget http://data.dmlc.ml/models/imagenet/vgg/vgg16-0000.params -O vgg16-0000.params
!wget http://data.dmlc.ml/models/imagenet/inception-bn/Inception-BN-symbol.json -O Inception-BN-symbol.json
!wget http://data.dmlc.ml/models/imagenet/inception-bn/Inception-BN-0126.params -O Inception-BN-0000.params
!wget http://data.dmlc.ml/models/imagenet/resnet/152-layers/resnet-152-symbol.json -O resnet-152-symbol.json
!wget http://data.dmlc.ml/models/imagenet/resnet/152-layers/resnet-152-0000.params -O resnet-152-0000.params
!wget http://data.dmlc.ml/models/imagenet/synset.txt -O synset.txt

让我们来看看 VGG-16 符号文件的第一行。可以看到输入层的定义('data'),第一个卷积层的权重和偏置项。卷积操作和线性修正单元激活函数分别用(『conv1_1』)和(『relu1_1』)定义。

!head -48 vgg16-symbol.json

三个模型都使用 ImageNet 训练集进行预训练。这个训练集包含超过 120 万张物体和动物的图像,这些图像被分成了 1000 个类别。我们可以在 synset.txt 文件中查看这些类别。

!head -10 synset.txt
import mxnet as mx
import numpy as np
import cv2, sys, time   # You can easily install OpenCV with 'pip install cv2'
from collections import namedtuple
from IPython.core.display import Image, display

print("MXNet version: %s"  % mx.__version__)

现在加载一个模型。

首先,我们需要从文件中加载权重和模型描述。MXNet 将此称为检查点。在每个训练 epoch 之后保存权重是个好习惯。一旦训练完成,我们可以查看训练日志,然后选择最佳 epoch 的权重,最优 epoch 即具有最高验证准确度的 epoch。一般来说它不会是最后一个 epoch。在模型加载完成之后,我们得到一个 Symbol 对象和权重、AKA 模型参数。之后我们创建一个新 Module 并为其分配 Symbol 作为输入。我们可以选择运行模型的环境:默认情况下使用 CPU 环境。这么做有两个原因:

  • 第一,即使你的电脑没有 GPU,你也能测试 notebook(https://s3.amazonaws.com/aws-ml-blog/artifacts/pre-trained-apache-mxnet-models/Pre-trained%2Bmodels.ipynb)。

  • 第二,我们接下来只预测单个图像,因此对性能没有特殊要求。对于那些希望通过预测大量图像以获得最佳吞吐量的应用产品,GPU 肯定是最优选择。

然后,我们将 Symbol 作为输入数据。我们称之为 data,为了与它在网络输入层时的名字保持一致(JSON 文件的前几行提过)。最后,我们将 data 的形态定义成 1 x 3 x 224 x 224。224 x 224 是图像分辨率:模型就是使用这个分辨率的图像来训练的。3 是通道数量:红色、绿色和蓝色(按此顺序)。1 是批量大小:一次预测一个图像。

def loadModel(modelname, gpu=False):
        sym, arg_params, aux_params = mx.model.load_checkpoint(modelname, 0)
        arg_params['prob_label'] = mx.nd.array([0])
        arg_params['softmax_label'] = mx.nd.array([0])
        if gpu:
            mod = mx.mod.Module(symbol=sym, context=mx.gpu(0))
        else:
            mod = mx.mod.Module(symbol=sym)
        mod.bind(for_training=False, data_shapes=[('data', (1,3,224,224))])
        mod.set_params(arg_params, aux_params)
        return mod

我们还需要加载存储在 synset.txt 文件中的 1000 个类别。预测时会使用这些类别描述。

def loadCategories():
        synsetfile = open('synset.txt', 'r')
        synsets = []
        for l in synsetfile:
                synsets.append(l.rstrip())
        return synsets

synsets = loadCategories()
print(synsets[:10])

现在我们编写一个从文件中加载图像的函数。别忘了,模型需要一个四维的 NDArray,其中包含 224 x 224 图像的红色、绿色以及蓝色通道。我们将利用 openCV 库及输入图像来构建 NDArray。

下面是具体步骤:

  • 读取图像:此步会返回一个形如(图像高度,图像宽度,3)的 numpy 数组。它包含三个 BGR 顺序的通道(蓝色、绿色、红色)。

  • 将图像转换为 RGB 顺序(红色、绿色、蓝色)。

  • 将图像大小改为 224 x 224。

  • 将数组从(图像高度,图像宽度,3)转换为(3,图像高度,图像宽度)。

  • 加上第四个维度,然后构建 NDArray。

def prepareNDArray(filename):
        img = cv2.imread(filename)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = cv2.resize(img, (224, 224,))
        img = np.swapaxes(img, 0, 2)
        img = np.swapaxes(img, 1, 2)
        img = img[np.newaxis, :]
        array = mx.nd.array(img)
        print(array.shape)
        return array

现在我们把重点放在预测上。我们的参数是单个图像、模型、类别列表以及我们想要优先返回的类别数量。

记住,Module 对象必须批量地向模型输入数据。通常的做法是使用数据迭代器。此处,我们想要预测单个图像,所以虽然我们可以使用数据迭代器,但它可能会越界。所以,我们创建一个叫 Batch 的元组,它将作为伪迭代器,在其 data 属性被引用时返回输入 NDArray。

在图像馈送至模型后,模型输出一个包含 1000 种可能性的 NDArray,对应 1000 个类别。NDArray 只有一行因为批大小为 1。

我们使用 squeeze() 将其转换为数组。之后使用 argsort() 创建第二个数组用于存放这些降序排列的概率索引。最后我们返回前 n 个类别及其描述。

def predict(filename, model, categories, n):
        array = prepareNDArray(filename)
        Batch = namedtuple('Batch', ['data'])
        t1 = time.time()
        model.forward(Batch([array]))
        prob = model.get_outputs()[0].asnumpy()
        t2 = time.time()
        print("Predicted in %.2f microseconds" % (t2-t1))
        prob = np.squeeze(prob)
        sortedprobindex = np.argsort(prob)[::-1]

        topn = []
        for i in sortedprobindex[0:n]:
                topn.append((prob[i], categories[i]))
        return topn

现在是时候将所有的模块组装起来了。加载这三个模型:

gpu = False
vgg16 = loadModel("vgg16", gpu)
resnet152 = loadModel("resnet-152", gpu)
inceptionv3 = loadModel("Inception-BN", gpu)
categories = loadCategories()

在进行图像分类之前,我们来仔细查看一下之前从 .params 文件中加载得到的 VGG-16 模型参数。首先,我们输出所有层的名字。

params = vgg16.get_params()

layers = []
for layer in params[0].keys():
    layers.append(layer)

layers.sort()    
print(layers)

对每一层而言,有两个部分值得我们关注:权重和偏置项。数一数这些权重你就会发现一共有 16 个层:13 个卷积层以及 3 个全连接层。现在你知道为什么这个模型叫 VGG-16 了。现在输出剩下的全连接层的权重

print(params[0]['fc8_weight'])

你注意到这个矩阵的形状了吗?它是 1000×4096 的。这个层包含了 1000 个神经元:每一个神经元会存储图像属于某个特定分类的概率。每个神经元也和前一层(『fc7』)所有的神经元(4096 个)全部连接。

现在开始使用这些模型来对我们自己的图像进行分类:

!wget http://jsimon-public.s3.amazonaws.com/violin.jpg -O violin.jpg
image = "violin.jpg"

display(Image(filename=image))

topn = 5
print("*** VGG16")
print(predict(image,vgg16,categories,topn))
print("*** ResNet-152")
print(predict(image,resnet152,categories,topn))
print("*** Inception v3")
print(predict(image,inceptionv3,categories,topn))

再用 GPU 环境试试:

gpu = True
vgg16 = loadModel("vgg16", gpu)
resnet152 = loadModel("resnet-152", gpu)
inceptionv3 = loadModel("Inception-BN", gpu)

print("*** VGG16")
print(predict(image,vgg16,categories,topn))
print("*** ResNet-152")
print(predict(image,resnet152,categories,topn))
print("*** Inception v3")
print(predict(image,inceptionv3,categories,topn))

注意:如果遇到了关于 GPU 支持的错误,有可能是你的机器没有配置 GPU,或者你使用的 MXNet 版本尚未提供 GPU 支持(USE_CUDA=1)。

构建提供 GPU 支持的 MXNet 教程可参考:https://mxnet.incubator.apache.org/get_started/build_from_source.html;你也可以安装预制版本:https://mxnet.incubator.apache.org/install/index.html。

GPU 版本和 CPU 版本的性能差异非常明显,在 15 倍到 20 倍之间。如果我们同时预测多个图像,由于 GPU 架构的大规模并行性,二者差距会更大。

现在是时候用你自己的图像试试了。只需将它们复制到此 notebook(https://s3.amazonaws.com/aws-ml-blog/artifacts/pre-trained-apache-mxnet-models/Pre-trained%2Bmodels.ipynb)所处的文件夹即可,更新上述模块的文件名,然后再次运行 predict() 函数。

原文地址:https://aws.amazon.com/cn/blogs/machine-learning/use-pre-trained-models-with-apache-mxnet/ 

入门预训练卷积神经网络MXNetAWS
1
相关数据
激活函数技术

在 计算网络中, 一个节点的激活函数定义了该节点在给定的输入或输入的集合下的输出。标准的计算机芯片电路可以看作是根据输入得到"开"(1)或"关"(0)输出的数字网络激活函数。这与神经网络中的线性感知机的行为类似。 一种函数(例如 ReLU 或 S 型函数),用于对上一层的所有输入求加权和,然后生成一个输出值(通常为非线性值),并将其传递给下一层。

权重技术

线性模型中特征的系数,或深度网络中的边。训练线性模型的目标是确定每个特征的理想权重。如果权重为 0,则相应的特征对模型来说没有任何贡献。

VGG技术

2014年,牛津大学提出了另一种深度卷积网络VGG-Net,它相比于AlexNet有更小的卷积核和更深的层级。AlexNet前面几层用了11×11和5×5的卷积核以在图像上获取更大的感受野,而VGG采用更小的卷积核与更深的网络提升参数效率。VGG-Net 的泛化性能较好,常用于图像特征的抽取目标检测候选框生成等。VGG最大的问题就在于参数数量,VGG-19基本上是参数量最多的卷积网络架构。VGG-Net的参数主要出现在后面两个全连接层,每一层都有4096个神经元,可想而至这之间的参数会有多么庞大。

参数技术

在数学和统计学裡,参数(英语:parameter)是使用通用变量来建立函数和变量之间关系(当这种关系很难用方程来阐述时)的一个数量。

卷积技术

神经网络技术

(人工)神经网络是一种起源于 20 世纪 50 年代的监督式机器学习模型,那时候研究者构想了「感知器(perceptron)」的想法。这一领域的研究者通常被称为「联结主义者(Connectionist)」,因为这种模型模拟了人脑的功能。神经网络模型通常是通过反向传播算法应用梯度下降训练的。目前神经网络有两大主要类型,它们都是前馈神经网络:卷积神经网络(CNN)和循环神经网络(RNN),其中 RNN 又包含长短期记忆(LSTM)、门控循环单元(GRU)等等。深度学习是一种主要应用于神经网络帮助其取得更好结果的技术。尽管神经网络主要用于监督学习,但也有一些为无监督学习设计的变体,比如自动编码器和生成对抗网络(GAN)。

准确率技术

分类模型的正确预测所占的比例。在多类别分类中,准确率的定义为:正确的预测数/样本总数。 在二元分类中,准确率的定义为:(真正例数+真负例数)/样本总数

OpenCV技术

OpenCV的全称是Open Source Computer Vision Library,是一个跨平台的计算机视觉库。OpenCV是由英特尔公司发起并参与开发,以BSD许可证授权发行,可以在商业和研究领域中免费使用。OpenCV可用于开发实时的图像处理、计算机视觉以及模式识别程序。

神经元技术

(人工)神经元是一个类比于生物神经元的数学计算模型,是神经网络的基本组成单元。 对于生物神经网络,每个神经元与其他神经元相连,当它“兴奋”时会向相连的神经元发送化学物质,从而改变这些神经元的电位;神经元的“兴奋”由其电位决定,当它的电位超过一个“阈值”(threshold)便会被激活,亦即“兴奋”。 目前最常见的神经元模型是基于1943年 Warren McCulloch 和 Walter Pitts提出的“M-P 神经元模型”。 在这个模型中,神经元通过带权重的连接接处理来自n个其他神经元的输入信号,其总输入值将与神经元的阈值进行比较,最后通过“激活函数”(activation function)产生神经元的输出。

MXNet技术

MXNet是开源的,用来训练部署深层神经网络的深度学习框架。它是可扩展的,允许快速模型训练,并灵活支持多种语言(C ++,Python,Julia,Matlab,JavaScript, Go,R,Scala,Perl,Wolfram语言)

批次技术

模型训练的一次迭代(即一次梯度更新)中使用的样本集。

暂无评论
暂无评论~