Press "Enter" to skip to content

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

在这篇博文中,你将会了解如何使用 ApacheMXNet预训练出的多个模型。为什么要尝试多个模型呢?为什么不直接选择准确率最高的呢?稍后我们会在文章中看到,尽管这些模型是在相同的数据集上训练的,并且都针对最大准确率进行了优化,但它们在特定图像上的表现略有不同。此外,(不同模型)预测速度也不同,而对很多应用来说速度是一个重要的影响因素。尝试这些模型,或许能找到一个合适的模型解决手头上的难题。首先,我们先从 ApacheMXNet模型库中下载三个图像分类模型。(模型库地址: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.txtimport mxnet as mximport numpy as npimport cv2, sys, time  # You can easily install OpenCV with 'pip install cv2'from collections import namedtuplefrom IPython.core.display import Image, displayprint("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 synsetssynsets = 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 = Falsevgg16 = 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.jpgimage = "violin.jpg"display(Image(filename=image))topn = 5print("*** 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 = Truevgg16 = 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() 函数。

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注