Caffe2 - 快速指南


Caffe2 - 简介

近几年来,深度学习已成为机器学习的一大趋势。它已成功应用于解决视觉、语音识别和自然语言处理(NLP)领域以前无法解决的问题。深度学习正在许多领域得到应用并显示出其实用性。

Caffe(快速特征嵌入的卷积架构)是伯克利视觉与学习中心(BVLC)开发的深度学习框架。Caffe项目是贾扬清博士在读博士期间创建的。在加州大学伯克利分校。Caffe 提供了一种简单的方法来实验深度学习。它是用 C++ 编写的,并提供PythonMatlab的绑定。

它支持多种不同类型的深度学习架构,例如CNN(卷积神经网络)、LSTM(长短期记忆)和 FC(全连接)。它支持 GPU,因此非常适合涉及深度神经网络的生产环境。它还支持基于CPU的内核库,例如NVIDIA、CUDA深度神经网络库(cuDNN)和Intel数学内核库(Intel MKL)

2017 年 4 月,美国社交网络服务公司 Facebook 宣布推出 Caffe2,其中包含 RNN(循环神经网络),并于 2018 年 3 月将 Caffe2 合并到 PyTorch。Caffe2 创建者和社区成员创建了解决各种问题的模型。这些模型作为预训练模型向公众开放。Caffe2 帮助创建者使用这些模型并创建自己的网络来对数据集进行预测。

在详细介绍Caffe2之前,让我们先了解一下机器学习深度学习之间的区别。这对于理解 Caffe2 中如何创建和使用模型是必要的。

机器学习与深度学习

在任何机器学习算法中,无论是传统算法还是深度学习算法,数据集中特征的选择对于获得所需的预测精度都起着极其重要的作用。在传统的机器学习技术中,特征选择主要是通过人类的检查、判断和深层领域知识来完成的。有时,您可能会向一些经过测试的算法寻求帮助以进行特征选择。

传统的机器学习流程如下图所示 -

机器学习

在深度学习中,特征选择是自动的,是深度学习算法本身的一部分。如下图所示 -

深度学习

在深度学习算法中,特征工程是自动完成的。一般来说,特征工程非常耗时,并且需要良好的领域专业知识。为了实现自动特征提取,深度学习算法通常需要大量数据,因此如果只有数千、数万个数据点,深度学习技术可能无法给您满意的结果。

对于更大的数据,深度学习算法比传统的机器学习算法产生更好的结果,并且具有较少或没有特征工程的额外优势。

Caffe2 - 概述

现在,您已经对深度学习有了一些了解,让我们概述一下什么是 Caffe。

训练 CNN

让我们了解训练 CNN 进行图像分类的过程。该过程包括以下步骤 -

  • 数据准备- 在此步骤中,我们对图像进行中心裁剪并调整其大小,以便用于训练和测试的所有图像具有相同的大小。这通常是通过在图像数据上运行一个小的 Python 脚本来完成的。

  • 模型定义- 在这一步中,我们定义 CNN 架构。配置存储在.pb (protobuf)文件中。典型的CNN架构如下图所示。

  • 求解器定义- 我们定义求解器配置文件。求解器进行模型优化。

  • 模型训练- 我们使用内置的 Caffe 实用程序来训练模型。训练可能会花费大量时间和 CPU 使用率。训练完成后,Caffe 将模型存储在文件中,稍后可将其用于测试数据和最终部署以进行预测。

训练CNN

Caffe2 的新增功能

在 Caffe2 中,您会发现许多现成的预训练模型,并且还经常利用新模型和算法的社区贡献。您创建的模型可以使用云中的 GPU 功能轻松扩展,也可以通过其跨平台库在移动设备上大规模使用。

Caffe2 相对于 Caffe 所做的改进可总结如下:

  • 移动部署
  • 新硬件支持
  • 支持大规模分布式训练
  • 量化计算
  • Facebook 压力测试

预训练模型演示

伯克利视觉和学习中心 (BVLC) 网站提供了其预训练网络的演示。此处所述的链接上提供了一种用于图像分类的网络:https://caffe2.ai/docs/learn-more#null__caffe-neural-network-for-image-classification,并如下面的屏幕截图所示。

预训练模型

在屏幕截图中,狗的图像被分类并标记了其预测准确性。它还表示,对图像进行分类只花了0.068 秒。您可以通过指定图像 URL 或在屏幕底部给出的选项中上传图像本身来尝试自己选择的图像。

Caffe2 - 安装

现在,您已经对 Caffe2 的功能有了足够的了解,是时候亲自试验 Caffe2 了。要使用预先训练的模型或使用自己的 Python 代码开发模型,您必须首先在计算机上安装 Caffe2。

在 Caffe2 站点的安装页面(可通过链接https://caffe2.ai/docs/getting-started.html访问)上,您将看到以下内容以选择您的平台和安装类型。

安装页面

正如您在上面的屏幕截图中看到的,Caffe2支持多种流行的平台,包括移动平台。

现在,我们将了解MacOS 安装步骤,本教程中的所有项目都在其上进行了测试。

MacOS安装

安装可以有四种类型,如下所示 -

  • 预构建的二进制文件
  • 从源代码构建
  • Docker 镜像

根据您的喜好,选择以上任意一种作为您的安装类型。此处给出的说明是根据 Caffe2 安装站点的预构建二进制文件进行的。它使用 Anaconda 作为Jupyter 环境。在控制台提示符下执行以下命令

pip install torch_nightly -f 
https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html

除了上述内容之外,您还需要一些第三方库,这些库是使用以下命令安装的 -

conda install -c anaconda setuptools
conda install -c conda-forge graphviz  
conda install -c conda-forge hypothesis
conda install -c conda-forge ipython
conda install -c conda-forge jupyter
conda install -c conda-forge matplotlib
conda install -c anaconda notebook
conda install -c anaconda pydot
conda install -c conda-forge python-nvd3
conda install -c anaconda pyyaml
conda install -c anaconda requests
conda install -c anaconda scikit-image
conda install -c anaconda scipy

Caffe2 网站中的一些教程还需要安装Zeromq,它是使用以下命令安装的 -

conda install -c anaconda zeromq

Windows/Linux安装

在控制台提示符下执行以下命令 -

conda install -c pytorch pytorch-nightly-cpu

您一定已经注意到,您需要 Anaconda 才能使用上述安装。您将需要安装MacOS 安装中指定的附加软件包。

测试安装

为了测试您的安装,下面给出了一个小的 Python 脚本,您可以将其剪切并粘贴到 Juypter 项目中并执行。

from caffe2.python import workspace
import numpy as np
print ("Creating random data")
data = np.random.rand(3, 2)
print(data)
print ("Adding data to workspace ...")
workspace.FeedBlob("mydata", data)
print ("Retrieving data from workspace")
mydata = workspace.FetchBlob("mydata")
print(mydata)

当您执行上述代码时,您应该看到以下输出 -

Creating random data
[[0.06152718 0.86448082]
[0.36409966 0.52786113]
[0.65780886 0.67101053]]
Adding data to workspace ...
Retrieving data from workspace
[[0.06152718 0.86448082]
[0.36409966 0.52786113]
[0.65780886 0.67101053]]

此处显示安装测试页面的屏幕截图,供您快速参考 -

测试安装

现在,您已在计算机上安装了 Caffe2,请继续安装教程应用程序。

教程安装

在控制台上使用以下命令下载教程源 -

git clone --recursive https://github.com/caffe2/tutorials caffe2_tutorials

下载完成后,您将在安装目录的caffe2_tutorials文件夹中找到几个Python项目。该文件夹的屏幕截图供您快速浏览。

/Users/yourusername/caffe2_tutorials
教程安装

您可以打开其中一些教程来查看Caffe2 代码是什么样的。本教程中描述的接下来的两个项目主要基于上面显示的示例。

现在是时候编写一些我们自己的 Python 代码了。让我们了解如何使用 Caffe2 中的预训练模型。稍后,您将学习创建自己的简单神经网络,以在您自己的数据集上进行训练。

Caffe2 - 验证对预训练模型的访问

在学习在 Python 应用程序中使用预训练模型之前,我们首先验证模型是否已安装在您的计算机上并且可以通过 Python 代码访问。

安装 Caffe2 时,预先训练的模型会复制到安装文件夹中。在安装了 Anaconda 的计算机上,这些模型位于以下文件夹中。

anaconda3/lib/python3.7/site-packages/caffe2/python/models

检查计算机上的安装文件夹是否存在这些型号。您可以尝试使用以下简短的 Python 脚本从安装文件夹加载这些模型 -

CAFFE_MODELS = os.path.expanduser("/anaconda3/lib/python3.7/site-packages/caffe2/python/models")
INIT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'init_net.pb')
PREDICT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'predict_net.pb')
print(INIT_NET)
print(PREDICT_NET)

当脚本成功运行时,您将看到以下输出 -

/anaconda3/lib/python3.7/site-packages/caffe2/python/models/squeezenet/init_net.pb
/anaconda3/lib/python3.7/site-packages/caffe2/python/models/squeezenet/predict_net.pb

这确认了您的计算机上已安装squeezenet模块并且您的代码可以访问该模块。

现在,您已准备好使用 Caffe2 squeezenet预训练模块编写自己的用于图像分类的 Python 代码。

使用预训练模型进行图像分类

在本课程中,您将学习使用预训练模型来检测给定图像中的对象。您将使用squeezenet预训练模块来高精度地检测和分类给定图像中的对象。

打开新的Juypter 笔记本并按照步骤开发此图像分类应用程序。

导入库

首先,我们使用以下代码导入所需的包 -

from caffe2.proto import caffe2_pb2
from caffe2.python import core, workspace, models
import numpy as np
import skimage.io
import skimage.transform
from matplotlib import pyplot
import os
import urllib.request as urllib2
import operator

接下来,我们设置一些变量-

INPUT_IMAGE_SIZE = 227
mean = 128

用于训练的图像显然具有不同的尺寸。所有这些图像必须转换为固定尺寸才能进行准确的训练。同样,测试图像和生产环境中要预测的图像也必须转换为与训练时使用的尺寸相同的尺寸。因此,我们在上面创建了一个名为INPUT_IMAGE_SIZE 的变量,其值为227。因此,在我们的分类器中使用之前,我们会将所有图像转换为227x227 的大小。

我们还声明了一个名为Mean 的变量,其值为128,稍后将使用该变量来改进分类结果。

接下来,我们将开发两个用于处理图像的函数。

图像处理

图像处理由两个步骤组成。第一个是调整图像大小,第二个是集中裁剪图像。对于这两个步骤,我们将编写两个用于调整大小和裁剪的函数。

调整图像大小

首先,我们将编写一个用于调整图像大小的函数。如前所述,我们将图像大小调整为227x227。因此,让我们定义函数resize如下 -

def resize(img, input_height, input_width):

我们通过将宽度除以高度来获得图像的纵横比。

original_aspect = img.shape[1]/float(img.shape[0])

如果长宽比大于1,则表明图像很宽,即处于横向模式。我们现在调整图像高度并使用以下代码返回调整大小的图像 -

if(original_aspect>1):
   new_height = int(original_aspect * input_height)
   return skimage.transform.resize(img, (input_width,
   new_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)

如果长宽比小于1,则表示纵向模式。我们现在使用以下代码调整宽度 -

if(original_aspect<1):
   new_width = int(input_width/original_aspect)
   return skimage.transform.resize(img, (new_width,
   input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)

如果长宽比等于1,我们不进行任何高度/宽度调整。

if(original_aspect == 1):
   return skimage.transform.resize(img, (input_width,
   input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)

下面给出了完整的功能代码供您快速参考 -

def resize(img, input_height, input_width):
   original_aspect = img.shape[1]/float(img.shape[0])
   if(original_aspect>1):
      new_height = int(original_aspect * input_height)
      return skimage.transform.resize(img, (input_width,
	   new_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)
   if(original_aspect<1):
         new_width = int(input_width/original_aspect)
         return skimage.transform.resize(img, (new_width,
         input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)
   if(original_aspect == 1):
         return skimage.transform.resize(img, (input_width,
         input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)

我们现在将编写一个函数来围绕图像中心裁剪图像。

图像裁剪

我们声明crop_image函数如下:

def crop_image(img,cropx,cropy):

我们使用以下语句提取图像的尺寸 -

y,x,c = img.shape

我们使用以下两行代码为图像创建一个新的起点 -

startx = x//2-(cropx//2)
starty = y//2-(cropy//2)

最后,我们通过创建具有新尺寸的图像对象来返回裁剪后的图像 -

return img[starty:starty+cropy,startx:startx+cropx]

下面给出了完整的函数代码供您快速参考 -

def crop_image(img,cropx,cropy):
   y,x,c = img.shape
   startx = x//2-(cropx//2)
   starty = y//2-(cropy//2)
   return img[starty:starty+cropy,startx:startx+cropx]

现在,我们将编写代码来测试这些功能。

处理图像

首先,将图像文件复制到项目目录中的images 子文件夹中。tree.jpg文件被复制到项目中。以下 Python 代码加载图像并将其显示在控制台上 -

img = skimage.img_as_float(skimage.io.imread("images/tree.jpg")).astype(np.float32)
print("Original Image Shape: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Original image')

输出如下 -

处理图像

请注意,原始图像的尺寸为600 x 960。我们需要将其大小调整为我们的规格227 x 227。调用我们之前定义的调整大小函数可以完成这项工作。

img = resize(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE)
print("Image Shape after resizing: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Resized image')

输出如下 -

原始图像

请注意,现在图像大小为227 x 363。我们需要将其裁剪为227 x 227,作为我们算法的最终输入。为此,我们调用之前定义的裁剪函数。

img = crop_image(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE)
print("Image Shape after cropping: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Center Cropped')

下面提到的是代码的输出 -

裁剪图像

此时,图像的大小为227 x 227,可以进行进一步处理。现在,我们交换图像轴以将三种颜色提取到三个不同的区域。

img = img.swapaxes(1, 2).swapaxes(0, 1)
print("CHW Image Shape: " , img.shape)

下面给出的是输出 -

CHW Image Shape: (3, 227, 227)

请注意,最后一个轴现在已成为数组中的第一个维度。我们现在将使用以下代码绘制三个通道 -

pyplot.figure()
for i in range(3):
   pyplot.subplot(1, 3, i+1)
   pyplot.imshow(img[i])
   pyplot.axis('off')
   pyplot.title('RGB channel %d' % (i+1))

输出如下:

方面

最后,我们对图像进行一些额外的处理,例如将红绿蓝转换为蓝绿红(RGB 到 BGR),删除平均值以获得更好的结果,并使用以下三行代码添加批量大小轴 -

# convert RGB --> BGR
img = img[(2, 1, 0), :, :]
# remove mean
img = img * 255 - mean
# add batch size axis
img = img[np.newaxis, :, :, :].astype(np.float32)

此时,您的图像已采用NCHW 格式,并已准备好输入我们的网络。接下来,我们将加载预先训练的模型文件并将上面的图像输入其中进行预测。

预测处理图像中的对象

我们首先设置Caffe 预训练模型中定义的初始化预测网络的路径。

设置模型文件路径

请记住,从我们之前的讨论来看,所有预训练的模型都安装在models文件夹中。我们设置该文件夹的路径如下 -

CAFFE_MODELS = os.path.expanduser("/anaconda3/lib/python3.7/site-packages/caffe2/python/models")

我们设置squeezenet模型的init_net protobuf文件的路径如下 -

INIT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'init_net.pb')

同样,我们设置Predict_net protobuf 的路径如下 -

PREDICT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'predict_net.pb')

我们打印两条路径用于诊断目的 -

print(INIT_NET)
print(PREDICT_NET)

此处给出上述代码以及输出以供您快速参考 -

CAFFE_MODELS = os.path.expanduser("/anaconda3/lib/python3.7/site-packages/caffe2/python/models")
INIT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'init_net.pb')
PREDICT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'predict_net.pb')
print(INIT_NET)
print(PREDICT_NET)

输出如下 -

/anaconda3/lib/python3.7/site-packages/caffe2/python/models/squeezenet/init_net.pb
/anaconda3/lib/python3.7/site-packages/caffe2/python/models/squeezenet/predict_net.pb

接下来,我们将创建一个预测器。

创建预测器

我们使用以下两个语句读取模型文件 -

with open(INIT_NET, "rb") as f:
   init_net = f.read()
with open(PREDICT_NET, "rb") as f:
   predict_net = f.read()

通过将两个文件的指针作为参数传递给Predictor函数来创建预测器。

p = workspace.Predictor(init_net, predict_net)

p对象是预测器,用于预测任何给定图像中的对象。请注意,每个输入图像必须采用 NCHW 格式,就像我们之前对tree.jpg文件所做的那样。

预测物体

预测给定图像中的对象很简单 - 只需执行一行命令即可。我们在预测器对象上调用run方法来检测给定图像中的对象。

results = p.run({'data': img})

预测结果现在在结果对象中可用,为了便于阅读,我们将其转换为数组。

results = np.asarray(results)

使用以下语句打印数组的维度以供您理解 -

print("results shape: ", results.shape)

输出如下所示 -

results shape: (1, 1, 1000, 1, 1)

我们现在将删除不必要的轴 -

preds = np.squeeze(results)

现在可以通过获取preds数组中的最大值检索最上面的预测。

curr_pred, curr_conf = max(enumerate(preds), key=operator.itemgetter(1))
print("Prediction: ", curr_pred)
print("Confidence: ", curr_conf)

输出如下 -

Prediction: 984
Confidence: 0.89235985

正如您所看到的,模型以89% 的置信度预测了一个索引值为984的对象。984的索引对于我们理解检测到什么样的物体没有多大意义。我们需要使用对象的索引值获取对象的字符串化名称。模型识别的对象类型及其相应的索引值可在 github 存储库中找到。

现在,我们将了解如何检索索引值为 984 的对象的名称。

字符串化结果

我们创建 github 存储库的 URL 对象,如下所示 -

codes = "https://gist.githubusercontent.com/aaronmarkham/cd3a6b6ac0
71eca6f7b4a6e40e6038aa/raw/9edb4038a37da6b5a44c3b5bc52e448ff09bfe5b/alexnet_codes"

我们读取 URL 的内容 -

response = urllib2.urlopen(codes)

响应将包含所有代码及其描述的列表。下面显示了几行响应,以便您理解它包含的内容 -

5: 'electric ray, crampfish, numbfish, torpedo',
6: 'stingray',
7: 'cock',
8: 'hen',
9: 'ostrich, Struthio camelus',
10: 'brambling, Fringilla montifringilla',

现在,我们使用for循环迭代整个数组以找到所需的代码 984,如下所示 -

for line in response:
   mystring = line.decode('ascii')
   code, result = mystring.partition(":")[::2]
   code = code.strip()
   result = result.replace("'", "")
   if (code == str(curr_pred)):
      name = result.split(",")[0][1:]
      print("Model predicts", name, "with", curr_conf, "confidence")

当您运行代码时,您将看到以下输出 -

Model predicts rapeseed with 0.89235985 confidence

您现在可以在另一张图像上尝试该模型。

预测不同的图像

要预测另一个图像,只需将图像文件复制到项目目录的images文件夹中即可。这是我们之前存储的tree.jpg文件的目录。更改代码中图像文件的名称。只需要进行一处更改,如下所示

img = skimage.img_as_float(skimage.io.imread("images/pretzel.jpg")).astype(np.float32)

原始图片和预测结果如下所示 -

预测图像

输出如下 -

Model predicts pretzel with 0.99999976 confidence

正如您所看到的,预训练的模型能够非常准确地检测给定图像中的对象。

完整源码

此处提到了上述代码的完整源代码,该代码使用预先训练的模型在给定图像中进行对象检测,以供您快速参考 -

def crop_image(img,cropx,cropy):
   y,x,c = img.shape
   startx = x//2-(cropx//2)
   starty = y//2-(cropy//2)
   return img[starty:starty+cropy,startx:startx+cropx]
img = skimage.img_as_float(skimage.io.imread("images/pretzel.jpg")).astype(np.float32)
print("Original Image Shape: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Original image')
img = resize(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE)
print("Image Shape after resizing: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Resized image')
img = crop_image(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE)
print("Image Shape after cropping: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Center Cropped')
img = img.swapaxes(1, 2).swapaxes(0, 1)
print("CHW Image Shape: " , img.shape)
pyplot.figure()
for i in range(3):
pyplot.subplot(1, 3, i+1)
pyplot.imshow(img[i])
pyplot.axis('off')
pyplot.title('RGB channel %d' % (i+1))
# convert RGB --> BGR
img = img[(2, 1, 0), :, :]
# remove mean
img = img * 255 - mean
# add batch size axis
img = img[np.newaxis, :, :, :].astype(np.float32)
CAFFE_MODELS = os.path.expanduser("/anaconda3/lib/python3.7/site-packages/caffe2/python/models")
INIT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'init_net.pb')
PREDICT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'predict_net.pb')
print(INIT_NET)
print(PREDICT_NET)
with open(INIT_NET, "rb") as f:
   init_net = f.read()
with open(PREDICT_NET, "rb") as f:
   predict_net = f.read()
p = workspace.Predictor(init_net, predict_net)
results = p.run({'data': img})
results = np.asarray(results)
print("results shape: ", results.shape)
preds = np.squeeze(results)
curr_pred, curr_conf = max(enumerate(preds), key=operator.itemgetter(1))
print("Prediction: ", curr_pred)
print("Confidence: ", curr_conf)
codes = "https://gist.githubusercontent.com/aaronmarkham/cd3a6b6ac071eca6f7b4a6e40e6038aa/raw/9edb4038a37da6b5a44c3b5bc52e448ff09bfe5b/alexnet_codes"
response = urllib2.urlopen(codes)
for line in response:
   mystring = line.decode('ascii')
   code, result = mystring.partition(":")[::2]
   code = code.strip()
   result = result.replace("'", "")
   if (code == str(curr_pred)):
      name = result.split(",")[0][1:]
      print("Model predicts", name, "with", curr_conf, "confidence")

此时,您已经知道如何使用预训练模型对数据集进行预测。

接下来是学习如何在Caffe2中定义神经网络 (NN)架构并在数据集上训练它们。我们现在将学习如何创建一个简单的单层神经网络。

Caffe2 - 创建您自己的网络

在本课程中,您将学习在 Caffe2 中定义单层神经网络 (NN)并在随机生成的数据集上运行它。我们将编写代码以图形方式描述网络架构、打印输入、输出、权重和偏差值。要理解本课程,您必须熟悉神经网络架构、其术语以及其中使用的数学。

网络架构

让我们考虑一下我们想要构建一个单层神经网络,如下图所示 -

网络架构

从数学上讲,该网络由以下 Python 代码表示 -

Y = X * W^T + b

其中X、W、b是张量,Y是输出。我们将用一些随机数据填充所有三个张量,运行网络并检查Y输出。为了定义网络和张量,Caffe2 提供了几个Operator函数。

Caffe2 运算符

在Caffe2中,Operator是基本的计算单元。Caffe2 Operator表示如下。

Caffe2 运算符

Caffe2 提供了详尽的运算符列表。对于我们当前正在设计的网络,我们将使用称为 FC 的运算符,该运算符计算将输入向量 X 传递具有二维权重矩阵W和一维偏置向量b的全连接网络的结果。换句话说,它计算以下数学方程

Y = X * W^T + b

其中X的尺寸为(M xk),W的尺寸为(nxk)b(1 xn)。输出Y 的尺寸为(M xn),其中M是批量大小。

对于向量XW,我们将使用GaussianFill运算符创建一些随机数据。为了生成偏差值b,我们将使用ConstantFill运算符。

我们现在将继续定义我们的网络。

创建网络

首先,导入所需的包 -

from caffe2.python import core, workspace

接下来,通过调用core.Net定义网络,如下所示 -

net = core.Net("SingleLayerFC")

网络的名称指定为SingleLayerFC。至此,名为net的网络对象就创建完成了。到目前为止它不包含任何层。

创建张量

我们现在将创建网络所需的三个向量。首先,我们将通过调用GaussianFill运算符来创建 X 张量,如下所示 -

X = net.GaussianFill([], ["X"], mean=0.0, std=1.0, shape=[2, 3], run_once=0)

X向量尺寸为2 x 3,平均数据值为 0,0 ,标准差为1.0

同样,我们创建W张量如下 -

W = net.GaussianFill([], ["W"], mean=0.0, std=1.0, shape=[5, 3], run_once=0)

W向量大小为5 x 3

最后,我们创建大小为 5 的偏置b矩阵。

b = net.ConstantFill([], ["b"], shape=[5,], value=1.0, run_once=0)

现在,代码中最重要的部分来了,那就是定义网络本身。

定义网络

我们在以下 Python 语句中定义网络 -

Y = X.FC([W, b], ["Y"])

我们将输入数据X称为FC运算符。权重在W中指定,偏差在 b 中指定。输出是Y。或者,您可以使用以下更详细的 Python 语句创建网络。

Y = net.FC([X, W, b], ["Y"])

至此,网络就简单创建了。在我们至少运行一次网络之前,它不会包含任何数据。在运行网络之前,我们将检查其架构。

打印网络架构

Caffe2 在 JSON 文件中定义了网络架构,可以通过在创建的网络对象上调用 Proto 方法来检查该架构。

print (net.Proto())

这会产生以下输出 -

name: "SingleLayerFC"
op {
   output: "X"
   name: ""
   type: "GaussianFill"
   arg {
      name: "mean"
      f: 0.0
   }
   arg {
      name: "std"
      f: 1.0
   }
   arg {
      name: "shape"
      ints: 2
      ints: 3
   }
   arg {
      name: "run_once"
      i: 0
   }
}
op {
   output: "W"
   name: ""
   type: "GaussianFill"
   arg {
      name: "mean"
      f: 0.0
   }
   arg {
      name: "std"
      f: 1.0
   }
   arg {
      name: "shape"
      ints: 5
      ints: 3
   }
   arg {
      name: "run_once"
      i: 0
   }
}
op {
   output: "b"
   name: ""
   type: "ConstantFill"
   arg {
      name: "shape"
      ints: 5
   }
   arg {
      name: "value"
      f: 1.0
   }
   arg {
      name: "run_once"
      i: 0
   }
}
op {
   input: "X"
   input: "W"
   input: "b"
   output: "Y"
   name: ""
   type: "FC"
}

正如您在上面的清单中看到的,它首先定义了运算符X、Wb让我们以W的定义为例。W的类型指定为GausianFill。平均值定义为 float 0.0,标准差定义为 float 1.0 形状5 x 3

op {
   output: "W"
   name: "" type: "GaussianFill"
   arg {
      name: "mean" 
	   f: 0.0
   }
   arg { 
      name: "std" 
      f: 1.0
   }
   arg { 
      name: "shape" 
      ints: 5 
      ints: 3
   }
   ...
}

检查Xb的定义以了解您自己的理解。最后,让我们看看我们的单层网络的定义,它被复制在这里

op {
   input: "X"
   input: "W"
   input: "b"
   output: "Y"
   name: ""
   type: "FC"
}

这里,网络类型为FC(全连接),X、W、b作为输入,Y为输出。这个网络定义过于冗长,对于大型网络来说,检查其内容将变得乏味。幸运的是,Caffe2 为所创建的网络提供了图形表示。

网络图形表示

要获取网络的图形表示,请运行以下代码片段,该代码片段本质上只有两行 Python 代码。

from caffe2.python import net_drawer
from IPython import display
graph = net_drawer.GetPydotGraph(net, rankdir="LR")
display.Image(graph.create_png(), width=800)

当您运行代码时,您将看到以下输出 -

图示

对于大型网络,图形表示在可视化和调试网络定义错误方面变得非常有用。

最后,现在是运行网络的时候了。

跑步网络

您可以通过在工作区对象上调用RunNetOnce方法来运行网络-

workspace.RunNetOnce(net)

网络运行一次后,将创建所有随机生成的数据,将其输入网络并创建输出。运行网络后创建的张量在 Caffe2 中称为blob工作区由您创建并存储在内存中的blob组成。这与Matlab 非常相似。

运行网络后,您可以使用以下打印命令检查工作区包含的blob

print("Blobs in the workspace: {}".format(workspace.Blobs()))

您将看到以下输出 -

Blobs in the workspace: ['W', 'X', 'Y', 'b']

请注意,工作区由三个输入 blob 组成 - X、Wb。它还包含名为Y的输出 blob 。现在让我们检查这些 blob 的内容。

for name in workspace.Blobs():
   print("{}:\n{}".format(name, workspace.FetchBlob(name)))

您将看到以下输出 -

W:
[[ 1.0426593 0.15479846 0.25635982]
[-2.2461145 1.4581774 0.16827184]
[-0.12009818 0.30771437 0.00791338]
[ 1.2274994 -0.903331 -0.68799865]
[ 0.30834186 -0.53060573 0.88776857]]
X:
[[ 1.6588869e+00 1.5279824e+00 1.1889904e+00]
[ 6.7048723e-01 -9.7490678e-04 2.5114202e-01]]
Y:
[[ 3.2709925 -0.297907 1.2803618 0.837985 1.7562964]
[ 1.7633215 -0.4651525 0.9211631 1.6511179 1.4302125]]
b:
[1. 1. 1. 1. 1.]

请注意,您计算机上的数据或事实上网络每次运行的数据都会不同,因为所有输入都是随机创建的。您现在已成功定义网络并在计算机上运行它。

Caffe2 - 定义复杂网络

在上一课中,您学习了创建一个简单的网络并学习了如何执行它并检查其输出。创建复杂网络的过程与上述过程类似。Caffe2 提供了大量用于创建复杂架构的运算符。我们鼓励您检查 Caffe2 文档以获取运算符列表。在研究了各种运算符的目的之后,您将能够创建复杂的网络并训练它们。为了训练网络,Caffe2 提供了几个预定义的计算单元- 即运算符。您将需要选择合适的操作员来针对您要解决的问题类型来训练您的网络。

一旦网络训练到您满意的程度,您就可以将其存储在与您之前使用的预训练模型文件类似的模型文件中。这些经过训练的模型可以贡献给 Caffe2 存储库,以造福其他用户。或者您可以简单地将经过训练的模型供您自己的私人生产使用。

概括

Caffe2 是一个深度学习框架,允许您尝试多种神经网络来预测数据。Caffe2网站提供了许多预训练的模型。您学习了如何使用预训练模型之一对给定图像中的对象进行分类。您还学习了定义您选择的神经网络架构。可以使用 Caffe 中的许多预定义运算符来训练此类自定义网络。经过训练的模型存储在可以带入生产环境的文件中。