Caffe2 的基本数据结构(Basics of Caffe2 - Workspaces, Operators, and Nets)[4]
這篇文章主要介紹Caffe2的基本數據結構:
- Workspaces
- Operators
- Nets
在開始之前最好先閱讀以下Intro Turorial
首先,導入caffe2。其中core和worksapce模塊,這是必須的兩個模塊。如果你要使用Caffe2生成的protocol buffers,那么你也需要從caffe2_pb2中導入caffe2_pb2模塊。
如果你看到一些警告:Caffe2不支持GPU。這說明,你正在跑的Caffe2僅僅編譯了CPU模式。不用擔心,Caffe2在CPU上也是可以運行的。
Workspaces
讓我們先來介紹Workspace,它包含了所有數據。如果你熟悉Matlab ,worksapce包含了所有你創建的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.
下面看一個實際例子:
我們之前說到,創建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并且用高斯隨機值填充它。
看看輸出:
Content of op: output: "Z" name: "" type: "GaussianFill" arg {name: "std"f: 1.0 } arg {name: "shape"ints: 100ints: 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 + bCaffe2中的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: 2ints: 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只是個符號,里面什么也沒有。別只望它會輸出什么值。
讓我們繼續創建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: 2ints: 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: 5ints: 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來跑一個網絡。我們選擇第一種來展示。
第一種
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.]第二種
現在嘗試第二種方法去創建這個網絡,并跑起它。
輸出
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
轉載于:https://www.cnblogs.com/sysuzyq/p/6824916.html
總結
以上是生活随笔為你收集整理的Caffe2 的基本数据结构(Basics of Caffe2 - Workspaces, Operators, and Nets)[4]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何使用Matlab实现数据的拟合
- 下一篇: android系统计步修改,安卓手机计步