Caffe2 快速指南
Caffe2 - 简介
过去几年,深度学习已成为机器学习领域的一大趋势。它已成功应用于解决计算机视觉、语音识别和自然语言处理 (NLP) 中以前无法解决的问题。深度学习正被应用于更多领域,并展现出其实用性。
Caffe(用于快速特征嵌入的卷积架构)是伯克利视觉和学习中心 (BVLC) 开发的一个深度学习框架。Caffe 项目由杨庆佳 (Yangqing Jia) 在加州大学伯克利分校攻读博士学位期间创建。Caffe 提供了一种简单的方法来试验深度学习。它使用 C++编写,并为Python 和Matlab 提供绑定。
它支持许多不同类型的深度学习架构,例如CNN(卷积神经网络)、LSTM(长短期记忆)和 FC(全连接)。它支持 GPU,因此非常适合涉及深度神经网络的生产环境。它还支持基于 CPU 的内核库,例如NVIDIA 的 CUDA 深度神经网络库(cuDNN) 和英特尔数学内核库(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 将模型存储在一个文件中,该文件稍后可用于测试数据和最终预测部署。
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 模块已安装在您的机器上,并且您的代码可以访问它。
现在,您可以编写您自己的 Python 代码来使用 Caffe2 squeezenet 预训练模块进行图像分类了。
使用预训练模型进行图像分类
在本课中,您将学习如何使用预训练模型来检测给定图像中的对象。您将使用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。调用我们前面定义的resize函数可以完成这项工作。
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的预训练模型中定义的init和predict网络设置路径。
设置模型文件路径
请记住我们之前的讨论,所有预训练模型都安装在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文件所做的那样。
预测对象
预测给定图像中的对象很简单 - 只需执行一行命令。我们在predictor对象上调用run方法来检测给定图像中的对象。
results = p.run({'data': img})
预测结果现在可在results对象中获得,为了方便阅读,我们将它转换为数组。
results = np.asarray(results)
使用以下语句打印数组的维度,以便您了解:
print("results shape: ", results.shape)
输出如下所示:
results shape: (1, 1, 1000, 1, 1)
我们现在将删除不必要的轴:
preds = np.squeeze(results)
现在可以通过获取preds数组中的max值来检索最高的预测结果。
curr_pred, curr_conf = max(enumerate(preds), key=operator.itemgetter(1))
print("Prediction: ", curr_pred)
print("Confidence: ", curr_conf)
输出如下:
Prediction: 984 Confidence: 0.89235985
正如您所看到的,该模型预测了一个索引值为984,置信度为89%的对象。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提供了几个算子函数。
Caffe2算子
在Caffe2中,算子是计算的基本单元。Caffe2算子表示如下。
Caffe2提供了详尽的算子列表。对于我们当前正在设计的网络,我们将使用称为FC的算子,它计算将输入向量X传递到具有二维权重矩阵W和一维偏差向量b的完全连接网络的结果。换句话说,它计算以下数学公式
Y = X * W^T + b
其中X的维度为(M x k),W的维度为(n x k),b为(1 x n)。输出Y的维度将为(M x n),其中M是批大小。
对于向量X和W,我们将使用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 文件中定义网络架构,可以通过在创建的net对象上调用 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、W和b。让我们以W的定义为例进行检查。W的类型被指定为GausianFill。均值定义为浮点数0.0,标准差定义为浮点数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
}
...
}
检查X和b的定义以了解其含义。最后,让我们看一下我们单层网络的定义,此处进行了复制
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)
运行代码时,您将看到以下输出:
对于大型网络,图形表示在可视化和调试网络定义错误方面非常有用。
最后,现在是时候运行网络了。
运行网络
您可以通过在workspace对象上调用RunNetOnce方法来运行网络:
workspace.RunNetOnce(net)
网络运行一次后,我们随机生成的所有数据都将被创建,馈送到网络中,并创建输出。在运行网络后创建的张量在 Caffe2 中称为blobs。工作区包含您创建并存储在内存中的blobs。这与 Matlab 非常相似。
运行网络后,您可以使用以下print命令检查工作区包含的blobs
print("Blobs in the workspace: {}".format(workspace.Blobs()))
您将看到以下输出:
Blobs in the workspace: ['W', 'X', 'Y', 'b']
请注意,工作区包含三个输入 blobs:X、W和b。它还包含名为Y的输出 blob。现在让我们检查这些 blobs 的内容。
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 中的许多预定义运算符来训练此类自定义网络。训练好的模型存储在一个文件中,可以将其用于生产环境。