这篇文章主要介绍Caffe2的基本数据结构:

  • Workspaces
  • Operators
  • Nets

在开始之前最好先阅读以下Intro Turorial

首先,导入caffe2。其中coreworksapce模块,这是必须的两个模块。如果你要使用Caffe2生成的protocol buffers,那么你也需要从caffe2_pb2 中导入caffe2_pb2模块。

# We'll also import a few standard python libraries
from matplotlib import pyplot
import numpy as np
import time # These are the droids you are looking for.
from caffe2.python import core, workspace
from caffe2.proto import caffe2_pb2

如果你看到一些警告:Caffe2不支持GPU。这说明,你正在跑的Caffe2仅仅编译了CPU模式。不用担心,Caffe2在CPU上也是可以运行的。

Workspaces

让我们先来介绍Workspace,它包含了所有数据。如果你熟悉Matlabworksapce包含了所有你创建的blob并保存在内存中。现在,让我们考虑一个N维的blob,blob和numpy的矩阵很像,但是它是连续的。接下来,我们将展示blob实际上是一种能指向任何C++类型对象的指针。下面,我们来看看接口是什么样的的。

Blobs()函数可以打印workspace里面所有的blobs。HasBlob则用于查询worksapce里面是否存在某个blob。不过,目前为止,我们的workspace里面没有任何东西。

print("Current blobs in the workspace: {}".format(workspace.Blobs()))
print("Workspace has blob 'X'? {}".format(workspace.HasBlob("X")))

FeedBlob() 函数用于向worksapce里面传递blob。

X = np.random.randn(2, 3).astype(np.float32)
print("Generated X from numpy:\n{}".format(X))
workspace.FeedBlob("X", X)

打印出来的X如下:

Generated X from numpy:
[[-0.56927377 -1.28052795 -0.95808828]
[-0.44225693 -0.0620895 -0.50509363]]

让我们看一下workspace里面的blob是什么样的。

print("Current blobs in the workspace: {}".format(workspace.Blobs()))
print("Workspace has blob 'X'? {}".format(workspace.HasBlob("X")))
print("Fetched X:\n{}".format(workspace.FetchBlob("X")))

输出如下:

Current blobs in the workspace: [u'X']
Workspace has blob 'X'? True
Fetched X:
[[-0.56927377 -1.28052795 -0.95808828]
[-0.44225693 -0.0620895 -0.50509363]]

接着验证两个矩阵是否相等:

np.testing.assert_array_equal(X, workspace.FetchBlob("X"))

注意,如果你访问一个不存在的blob,将会引发一个错误:

try:
workspace.FetchBlob("invincible_pink_unicorn")
except RuntimeError as err:
print(err)

错误输出如下:

[enforce fail at pybind_state.cc:441] gWorkspace->HasBlob(name).

另外,有一个你目前可能还用不上的东西:你可以定义两个不同名字的workspace,并且在他们之间切换。不同workspace的bolb是相互分离的。你可以通过CurrentWorkspace()函数来访问当前的workspace。下面演示了如何切换不同的workspace和创建新的workspace。

print("Current workspace: {}".format(workspace.CurrentWorkspace()))
print("Current blobs in the workspace: {}".format(workspace.Blobs())) # 切换到`gutentag` workspace,第二个参数`True`表示,如果`gutentag`不存在,则创建一个。
workspace.SwitchWorkspace("gutentag", True) # 现在重新打印,注意到当前的workspace是`gutentag`,并且其中不包含任何东西。
print("Current workspace: {}".format(workspace.CurrentWorkspace()))
print("Current blobs in the workspace: {}".format(workspace.Blobs()))

程序输出:

Current workspace: default
Current blobs in the workspace: ['X']
Current workspace: gutentag
Current blobs in the workspace: []

重新切换回到defaultworkspace

workspace.SwitchWorkspace("default")
print("Current workspace: {}".format(workspace.CurrentWorkspace()))
print("Current blobs in the workspace: {}".format(workspace.Blobs()))

并有如下输出:

Current workspace: default
Current blobs in the workspace: ['X']

最后,调用ResetWorkspace()函数可以清空当前的workspace的所有东西

workspace.ResetWorkspace()

Operators

Caffe2中,operator就像函数一样。从C++的角度理解,operator全部从一个通用的接口继承而来,它们通过类型进行注册,所以,我们可以在运行时调用不同的操作。operator的接口定义在caffe2/proto/caffe2.proto文件中。Operator根据输出产生相应的输出。

记住,在Caffe2的Python接口中,当我们说“创建一个operator”时,程序并没有跑起来,它只是创建了关于这个operator的protocol buffere,也就是定义了这个operator,但还没执行。之后,这个operator才会传递给C++接口禁止执行。如果你不明白什么是protobuf,那么你可以看下这个链接.

**1. **

下面看一个实际例子:

# Create an operator.
op = core.CreateOperator(
"Relu", # The type of operator that we want to run
["X"], # 输入 blobs 的名字的列表
["Y"], # A list of 输出 blobs by their names
)
# and we are done!

我们之前说到,创建op(operator),事实上只是创建了一个protobuf对象。我们可以查看它的内容。

print("Type of the created op is: {}".format(type(op)))
print("Content:\n")
print(str(op))

输出如下:

Type of the created op is: <class 'caffe2.proto.caffe2_pb2.OperatorDef'>
Content:
input: "X"
output: "Y"
name: ""
type: "Relu"

现在跑起这个operator,我们首先需要向workspace中传入数据X,然后简单的调用 workspace.RunOperatorOnce(operator)函数就可以。

workspace.FeedBlob("X", np.random.randn(2, 3).astype(np.float32))
workspace.RunOperatorOnce(op)

执行完后,让我们检查下这个operator是否正确操作。在这个操作中我们使用的是Relu函数。Relu函数在输入小于0时,取0,在输入大于0时,保持不变。

print("Current blobs in the workspace: {}\n".format(workspace.Blobs()))
print("X:\n{}\n".format(workspace.FetchBlob("X")))
print("Y:\n{}\n".format(workspace.FetchBlob("Y")))
print("Expected:\n{}\n".format(np.maximum(workspace.FetchBlob("X"), 0)))

输出如下,可以看到输出Y和你期望值一样,这个operator正确跑起来了:

Current blobs in the workspace: ['X', 'Y']
X:
[[ 1.03125858 1.0038228 0.0066975 ]
[ 1.33142471 1.80271244 -0.54222912]]
Y:
[[ 1.03125858 1.0038228 0.0066975 ]
[ 1.33142471 1.80271244 0. ]] Expected:
[[ 1.03125858 1.0038228 0.0066975 ]
[ 1.33142471 1.80271244 0. ]]

2.

当然Operator也支持选项参数。选项参数通过key-value对确定。下面是一个简单的例子:创建一个tensor并且用高斯随机值填充它。

op = core.CreateOperator(
"GaussianFill",
[], # GaussianFill does not need any parameters.
["Z"],
shape=[100, 100], # shape argument as a list of ints.
mean=1.0, # mean as a single float
std=1.0, # std as a single float
)
print("Content of op:\n")
print(str(op))

看看输出:

Content of op:
output: "Z"
name: ""
type: "GaussianFill"
arg {
name: "std"
f: 1.0
}
arg {
name: "shape"
ints: 100
ints: 100
}
arg {
name: "mean"
f: 1.0
}

然后我们跑起这个op,看看事情是否如期。

workspace.RunOperatorOnce(op)
temp = workspace.FetchBlob("Z")
pyplot.hist(temp.flatten(), bins=50)
pyplot.title("Distribution of Z")



没错,就是这样。

Nets

Net其实是多个operator的集合,就像写程序时一行一行的命令。

让我们创建一个等价于下面Python代码的网络。

X = np.random.randn(2, 3)
W = np.random.randn(5, 3)
b = np.ones(5)
Y = X * W^T + b

Caffe2中的core.net是对NetDef protocol buffer的一个封装类。当创建一个网络时,这个对象完全是空的,除了拥有它的名字信息外。

net = core.Net("my_first_net")
print("Current network proto:\n\n{}".format(net.Proto()))
Current network proto:
name: "my_first_net"

接着创建一个blob,命名为“X”,使用高斯函数进行填充。

X = net.GaussianFill([], ["X"], mean=0.0, std=1.0, shape=[2, 3], run_once=0)
print("New network proto:\n\n{}".format(net.Proto()))

这时网络的结构如下

New network proto:
name: "my_first_net"
op {
output: "X"
name: ""
type: "GaussianFill"
arg {
name: "std"
f: 1.0
}
arg {
name: "run_once"
i: 0
}
arg {
name: "shape"
ints: 2
ints: 3
}
arg {
name: "mean"
f: 0.0
}
}

聪明的读者肯定想起了我们之前提到的core.CreateOperator()。事实上,当我们有了一个net,我们可以直接创建一个operator然后通过Python接口加到net中去。比如,你调用了net.SomeOp,这里的SomeOp是一个注册了的operator的字符串,因此上面的操作和下面等效。

op = core.CreateOperator("SomeOp", ...)
net.Proto().op.append(op)

译者注:

比如在我用op = core.CreateOperator("GaussianFill",[], ["Z"],shape=[100, 100],mean=1.0, std=1.0)创建了一个op,op的type为“GaussianFill”,这是一个注册了的类型。然后再调用net.Proto().op.append(op)把这个op添加到网络中去。

以上的操作可以同过net来调用直接实现。直接使用op的type string---“GaussianFill”作为函数名字,net.GaussianFill([], ["X"], mean=0.0, std=1.0, shape=[2, 3], run_once=0)。

当然,读者可能感到困惑,X是什么?X是一个 BlobReference,这个引用包含两样东西:

- 名字,可以通过str(X)来访问得到

- 它是哪个net创建的,记录在其中的变量_from_net

现在让我们验证它。同样记住,我们还没有跑任何东西,所以X只是个符号,里面什么也没有。别只望它会输出什么值。

print("Type of X is: {}".format(type(X)))
print("The blob name is: {}".format(str(X)))
Type of X is: <class 'caffe2.python.core.BlobReference'>
The blob name is: X

让我们继续创建W和b.

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

现在一个简单的代码:Note由于BlonReference对象知道它由什么网络创建的,所以除了从net中创建op,你还可以通过BlobReference创建op。因此,我们可以通过如下方式创建FC操作。

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

事实上,在底下,X.FC(...)只是简单的委托net.FC来实现,X.FC()会将X作为op的第一个输入。所以上面的操作其实等价于下面的:

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

现在让我们看下当前这个网络。

print("Current network proto:\n\n{}".format(net.Proto()))
Current network proto:
name: "my_first_net"
op {
output: "X"
name: ""
type: "GaussianFill"
arg {
name: "std"
f: 1.0
}
arg {
name: "run_once"
i: 0
}
arg {
name: "shape"
ints: 2
ints: 3
}
arg {
name: "mean"
f: 0.0
}
}
op {
output: "W"
name: ""
type: "GaussianFill"
arg {
name: "std"
f: 1.0
}
arg {
name: "run_once"
i: 0
}
arg {
name: "shape"
ints: 5
ints: 3
}
arg {
name: "mean"
f: 0.0
}
}
op {
output: "b"
name: ""
type: "ConstantFill"
arg {
name: "run_once"
i: 0
}
arg {
name: "shape"
ints: 5
}
arg {
name: "value"
f: 1.0
}
}
op {
input: "X"
input: "W"
input: "b"
output: "Y"
name: ""
type: "FC"
}

是不是觉得太过冗长?GOOD~让我们尝试下把它变成一个图。用ipython显示。

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



目前为止,我们已经定义了一个Net,但是并没有执行任何东西。记住,上面的net只是一个protobuf,仅仅定义了网路的结构。当我们真正跑起这个网络时,底层发生的事件如下。

- 实例化protobuf中定义的C++net 对象

- 调用实例化后的net的Run()函数

在我们进行任何操作前,我们应该先使用ResetWorkspace()清空workspace里的东

西。

NOTE有两种方式通过python来跑一个网络。我们选择第一种来展示。

  1. 使用 workspace.RunNetOnce()
  2. 第二种更复杂点:需要两步,a) 调用workspace.CreateNet()创建C++net对象,b)使用workspace.RunNet(),这步需要传递网络的名字作为参数。

第一种

workspace.ResetWorkspace()
print("Current blobs in the workspace: {}".format(workspace.Blobs()))
workspace.RunNetOnce(net)
print("Blobs in the workspace after execution: {}".format(workspace.Blobs()))
# Let's dump the contents of the blobs
for name in workspace.Blobs():
print("{}:\n{}".format(name, workspace.FetchBlob(name)))

输出如下:

Current blobs in the workspace: []
Blobs in the workspace after execution: ['W', 'X', 'Y', 'b']
W:
[[-0.96537346 0.42591459 0.66788739]
[-0.47695673 2.25724339 -0.10370601]
[-0.20327474 -3.07469416 0.47715324]
[-1.62159526 0.73711687 -1.42365313]
[ 0.60718107 -0.50448036 -1.17132831]]
X:
[[-0.99601173 -0.61438894 0.10042733]
[ 0.23359862 0.15135486 0.77555442]]
Y:
[[ 1.76692021 0.07781416 3.13944149 2.01927781 0.58755434]
[ 1.35693741 1.14979863 0.85720366 -0.37135673 0.15705228]]
b:
[ 1. 1. 1. 1. 1.]

第二种

现在尝试第二种方法去创建这个网络,并跑起它。

workspace.ResetWorkspace()
print("Current blobs in the workspace: {}".format(workspace.Blobs()))
workspace.CreateNet(net)
workspace.RunNet(net.Proto().name)#传入名字
print("Blobs in the workspace after execution: {}".format(workspace.Blobs()))
for name in workspace.Blobs():
print("{}:\n{}".format(name, workspace.FetchBlob(name)))

输出

Current blobs in the workspace: []
Blobs in the workspace after execution: ['W', 'X', 'Y', 'b']
W:
[[-0.29295802 0.02897477 -1.25667715]
[-1.82299471 0.92877913 0.33613944]
[-0.64382178 -0.68545657 -0.44015241]
[ 1.10232282 1.38060772 -2.29121733]
[-0.55766547 1.97437167 0.39324901]]
X:
[[-0.47522315 -0.40166432 0.7179445 ]
[-0.8363331 -0.82451206 1.54286408]]
Y:
[[ 0.22535783 1.73460138 1.2652775 -1.72335696 0.7543118 ]
[-0.71776152 2.27745867 1.42452145 -4.59527397 0.4452306 ]]
b:
[ 1. 1. 1. 1. 1.]

RunNetOnce()RunNet()之间有不少差异,其中最大的差异就是计算耗时。因为RunNetOnce()涉及到protobuf的序列化,和实例化网络。这可能会使用很长时间。让我们来看下开销。

# It seems that %timeit magic does not work well with
# C++ extensions so we'll basically do for loops
start = time.time()
for i in range(1000):
workspace.RunNetOnce(net)
end = time.time()
print('Run time per RunNetOnce: {}'.format((end - start) / 1000)) start = time.time()
for i in range(1000):
workspace.RunNet(net.Proto().name)
end = time.time()
print('Run time per RunNet: {}'.format((end - start) / 1000))

输出如下:

Run time per RunNetOnce: 0.000364284992218
Run time per RunNet: 4.42600250244e-06

可以看到RunNet()更快。

结语:以上就是Caffe2的Python接口的一些主要部件。装载请注明出处:

http://www.jianshu.com/c/cf07b31bb5f2

Caffe2 的基本数据结构(Basics of Caffe2 - Workspaces, Operators, and Nets)[4]的更多相关文章

  1. Caffe2(2)----Eclipse环境中使用Caffe2

    使用IDE开发深度学习的应用,可以事半功倍,Caffe2已经全面支持Python,这里介绍如何在Ubantu14.04下,利用EclipseCaffe2的二次开发或应用. 1.安装eclipse 具体 ...

  2. Caffe2 Tutorials[0]

    本系列教程包括9个小节,对应Caffe2官网的前9个教程,第10个教程讲的是在安卓下用SqueezeNet进行物体检测,此处不再翻译.另外由于栏主不关注RNN和LSTM,所以栏主不对剩下两个教程翻译. ...

  3. caffe2 教程入门(python版)

    学习思路 1.先看官方文档,学习如何使用python调用caffe2包,包括 Basics of Caffe2 - Workspaces, Operators, and Nets Toy Regres ...

  4. Caffe2 用户手册概览(Caffe2 Tutorials Overview)[1]

    在开始之前,我们很感激你对Caffe2感兴趣,希望Caffe2在你的机器学习作品中是一个高性能的框架.Caffe2致力于模块化,促进深度学习想法和原型的实现. 选择你的学习路线   1. 使用一个现成 ...

  5. Win10上使用VS2015编译Caffe2

    Caffe2的官网:https://caffe2.ai/ 1.下载.安装及相关准备 在Caffe2的官网点击"Get Started",即进入安装说明页面.官方还未提供编译好的bi ...

  6. caffe2 环境的搭建以及detectron的配置

    caffe2 环境的搭建以及detectron的配置 建议大家看一下这篇博客https://tech.amikelive.com/node-706/comprehensive-guide-instal ...

  7. detectron安装+caffe2安装

    detectron安装+caffe2安装 因为想跑一下facebook最近开源的detectron物体检测平台,所以安装caffe2+detectron 总结: 一定要好好看官方安装教程:https: ...

  8. ubuntu16.04 安装caffe2

    1.使用conda创建环境 conda create --name caffe2env python=3.6 ---------------------------------success----- ...

  9. CNN:Windows下编译使用Caffe和Caffe2

    用于检测的CNN分为基于回归网络的方法和基于区域+CNN网络的方法,其中基于回归网络的方法典型为YOLO9000,可以兼容使用VGG-Net框架.其中基于区域+CNN网络方法,大量使用了Caffe作为 ...

随机推荐

  1. ASP.NET CSRF 解决【网摘】

    http://stackoverflow.com/questions/29939566/preventing-cross-site-request-forgery-csrf-attacks-in-as ...

  2. PHP 文件上传之如何识别文件伪装?——PHP的fileinfo扩展可!

    问题:文件上传时候需要验证上传的文件是否合法,文件伪装如何识别? 一个简单测试:把txt文件后缀直接改成jpg;上传 <!DOCTYPE html> <html> <ti ...

  3. AT24C02芯片学习记录

    1.首先看AT24C02芯片的引脚说明 2.芯片的型号与存储容量(bit)的对应关系: 3.总线时序 我对时序的理解: 时钟线分两种:一种是外部时钟源控制时钟线低电平持续多久高电平持续多久,就像串口: ...

  4. 使用php-vmstat遇到的麻烦

    workerman-vmstat是一个基于workerman的扩展,用于监听服务器应用对内存.cpu消耗的友好的查看功能,具体介绍可以去git上看:    https://github.com/wal ...

  5. JQuery checkbox多选框组选中提交,当选择某(无)一项,其他项禁止选中

    在项目中难免会遇到一些表单的提交,尤其是多选框中,当用户选择了某一项时,禁止其他项的选择.所以为了避免这样的冲突,所以我们前端就得控制一下了,下面就来个简单demo,记录一下,有需要的伙伴可以拿去耍耍 ...

  6. mysql设置定时任务(对于中控心跳包的实现有意义)

    转载:https://www.cnblogs.com/laowu-blog/p/5073665.html 目前用途:因为 脚本正常开关会给中控发送消息 但是万一脚本被强制关闭 没有触发脚本关闭事件就无 ...

  7. linux中利用fstab实现磁盘分区自动挂载

    如何格式化磁盘.给磁盘分区以及挂载,参考我的另一篇博客: https://www.cnblogs.com/mediocreWorld/p/11123786.html 博客中有一个格式化分区的命令: m ...

  8. about Base64

    用webservice传送文件的时候发现,如果发送的文件中有0x00字符,会被认为是字符串结尾,后面的内容就发送不过去,因此需要对不是纯文本格式的文件做BASE64编码,这样文件中就不会有0x00这样 ...

  9. 【笔记6-支付及订单模块】从0开始 独立完成企业级Java电商网站开发(服务端)

    支付模块 实际开发工作中经常会遇见如下场景,一个支付模块,一个订单模块,有一定依赖,一个同事负责支付模块,另一个同事负责订单模块,但是开发支付模块的时候要依赖订单模块的相关类 ,方法,或者工具类,这些 ...

  10. 数据结构--Java语言描述

    本篇文章是为了记录自己在学习数据结构时的笔记,会对常见的数据结构做基本的介绍以及使用Java语言进行实现.包括 动态数组 栈 队列 链表 二分搜索树 优先队列和堆 线段树 Trie树 并查集 AVL树 ...