1. 程式人生 > >caffe學習筆記3.1 -- caffe的三級結構

caffe學習筆記3.1 -- caffe的三級結構

在caffe教程中,介紹了caffe的三級結構:Blobs, Layers,Nets.如下圖所示:

深度網路是一個複雜的模型,caffe定義了一個層與層之間連線的網路模型。這個網路定義了從輸入層到損失的所有模型。caffe使用blobs結構儲存,交換和處理網路中正向和反向迭代時的資料和導數資訊,blob是Caffe的標準陣列結構,它提供了一個統一的記憶體介面。Layer是Caffe模型和計算的基本單元。Net是一系列的Layer和其連線的集合,Blob詳細描述了資訊是如何在Layer和net中儲存和交換的。

solving:是求解方法,分別配置解耦模型和優化方法

1. Blob儲存與交換

Blob是caffe中負責資料傳遞和處理的包,並且提供了在CPU與GPU之間同步處理的功能。從數學角度看,Blob是C語言風格的一個連續存數的N維陣列。

Caffe利用Blobs儲存和交換資料,Blobs提供了統一的記憶體介面儲存某種型別的資料,如批處理的影象資料,模型引數和用與優化演算法的導數。

Blobs可以根據CPU主機和GPU裝置的需要,遮蔽CPU/GPU計算和混合操作中的開銷,主機和裝置的記憶體按需求分配(lazily),以提高記憶體利用率。

對於批處理的圖片來說,Blobs常規的維度為:影象資料的個數N * 通道數K * 影象高度H * 影象寬度W。Blob按行儲存, 這使得它最後/最右面的維度更新的最快。比如,在一個4維的blob中,位置為(n,k,h,w)的值的物理位置為((n * K + k) * H + h) * W + w.

  • Number/N是每批處理資料的規模,批量處理資料有利於提高裝置處理和交換資料的吞吐率。在Imagenet上訓練資料每批有256張影象,則N=256.
  • Channel/K是特徵維度,對於RGB影象K=3.

雖然在Caffe在用於影象資料過程中,Blob都是4維的,但是它完全可以用於非影象資料。例如,如果你僅需要類似於傳統多層感知機那樣的全連線網路,使用2維的blobs(形式為(N,D)) , 然後呼叫InnerProdectLayer(全連線層)即可。

Blob的維度引數根據層的型別和配置而變化,對於由96個規模為11*11,輸入為3通道的濾波器構成的卷積層,blob的維度為96*3×11*11,對於一個輸入是1024維,輸出是1000維的內幾層和全連線層,blob的維度是1000*1024.

對於一些特定的資料,自己設計輸入工具或資料層是有必要的,但是,一旦資料準備完畢,各種層模組會幫你完成剩下的工作。

實現細節:

對於blob中的資料,我們通常會關心它的值和梯度,因此,blob儲存了兩塊資料內容:data和diff。前者是通過網路的普通資料,後者是網路反向計算得到的梯度。

而且,因為資料既可以儲存在CPU上,又可以儲存在GPU上,因而有兩種不同的訪問方式:靜態方式,不改變數值,動態方式,改變數值。

const Dtype* cpu_data() const;
Dtype* mutable_cpu_data();
(gpu和diff類似)

這麼設計是為了用SyncedMem類來同步CPU與GPU上的數值,以隱藏同步細節,最小化傳送資料。一個準則是,如果你不想改變數值,就一直用常數呼叫,不要在你的專案中儲存指標,每層操作blob時,呼叫相應的函式獲取它的指標,因為SyncedMem需要用這種方式確定何時需要複製資料。

實際上,使用GPU時,Caffe中的CPU 程式碼先從磁碟載入到blob,呼叫一個(GPU)裝置核使GPU進行計算, 再將算好的blob資料送入下一層,忽略了底層的細節,同時又能維持高效的運算。一旦所有的層都有GPU實現,那麼所有中間資料和梯度都會儲存在GPU上。

下面的例子解釋blub何時複製資料:

// Assuming that data are on the CPU initially, and we have a blob.假定資料在CPU上進行初始化,我們有一個blob
const Dtype* foo;
Dtype* bar;
foo = blob.gpu_data(); // data copied cpu->gpu.資料從cpu複製到gpu
foo = blob.cpu_data(); // no data copied since both have up-to-date contents.沒有資料複製,兩者都有最新的內容
bar = blob.mutable_gpu_data(); // no data copied.沒有資料複製
// ... some operations ...一些操作
bar = blob.mutable_gpu_data(); // no data copied when we are still on GPU. 沒有資料拷貝,依舊在gpu
foo = blob.cpu_data(); // data copied gpu->cpu, since the gpu side has modified the data 資料從gpu複製到cpu,因為gpu端已經修改了資料
foo = blob.gpu_data(); // no data copied since both have up-to-date contents沒有資料拷貝,兩邊都是最新內容
bar = blob.mutable_cpu_data(); // still no data copied.沒有資料拷貝
bar = blob.mutable_gpu_data(); // data copied cpu->gpu.資料從cpu到gpu
bar = blob.mutable_cpu_data(); // data copied gpu->cpu.資料從gpu到cpu

2. Layer : 計算和連線

這一層是非常重要的模型,是執行計算的基本單元, Layers可以進行的計算有:convolve filters(卷積濾波), pool(池化),inner products(內積), 及一些非線性運算如rectified-linear(限制線性運算),sigmoid及元素級的資料轉換,normalize(歸一化),資料載入,計算損失,如softmax和hinge。


這一層通過bottom(底部)連線層接收資料,通過top(頂部)連線層輸出資料

每個型別的層都定義了三種重要運算:setup, forward, 和backward.

  1. Setup: 在模型初始化時,初始化層和它的連線
  2. Forward: 從bottom給定的輸入計算輸出,傳到top層中
  3. Backward: 給定輸出層的梯度,計算相對於輸入層的梯度,並傳到bottom層,一個有參的layer需要計算相對於各個引數的梯度並存儲。

特別的,Forward和Backward函式有CPU和GPU兩種執行方式。如果你沒有執行GPU版本,layer會轉為作為備選的CPU方式,這會增加額外的資料傳送成本,但是對於一些快速實驗還是很方便的(儘管輸入資料要由gpu到cpu,之後輸出資料從gpu回到cpu)。

Layers承擔了整個網路的兩個核心操作: 前向傳播,接收輸入計算輸出;反向傳播,接收關於輸出的梯度,計算引數的梯度並反向傳播給前面的層,由此組成了每個layer的前向和反向通道。

由於caffe網路的組合性和其程式碼的模組化,定義layer是很容易的,只要定義好setup,forward,backward,就可以將layer接入到網路中。

Net的定義和操作:

net通過組合和自動微分,定義了一個函式和其對應的梯度。通過各個層的輸出計算這個函式,執行給定的任務。並通過組合各個層的反向傳播過程計算學習任務中損失函式的梯度,caffe模型是一種端到端的機器學習引擎。

net是由一系列層組成的有向無環圖,Caffe保留了圖中所有層的中間值以確保前向和反向迭代的正確性。一個典型的net開始於資料(data)層,從磁碟中載入資料,結束於損失(loss)層,計算目標任務,如分類和重構。

net模型定義在一個純文字模型語言中,有一系列的層和它們的連線構成。一個簡單的邏輯迴歸定義如下:


其網路結構定義為:

name: "LogReg"
layer {
  name: "mnist"
  type: "Data"
  top: "data"
  top: "label"
  data_param {
    source: "input_leveldb"
    batch_size: 64
  }
}
layer {
  name: "ip"
  type: "InnerProduct"
  bottom: "data"
  top: "ip"
  inner_product_param {
    num_output: 2
  }
}
layer {
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "ip"
  bottom: "label"
  top: "loss"
}
模型通過Net::Init()初始化,初始化主要做兩件事情:通過建立blobs和layers搭建整個網路的DAG圖; 呼叫layers的Setup()函式。初始化時會有一系列的記錄,例如整個網路的結構是否正確構建。同時,Net在初始化階段會將其日誌答應在INFO中:
I0902 22:52:17.931977 2079114000 net.cpp:39] Initializing net from parameters:
name: "LogReg"
[...model prototxt printout...]
# construct the network layer-by-layer
I0902 22:52:17.932152 2079114000 net.cpp:67] Creating Layer mnist
I0902 22:52:17.932165 2079114000 net.cpp:356] mnist -> data
I0902 22:52:17.932188 2079114000 net.cpp:356] mnist -> label
I0902 22:52:17.932200 2079114000 net.cpp:96] Setting up mnist
I0902 22:52:17.935807 2079114000 data_layer.cpp:135] Opening leveldb input_leveldb
I0902 22:52:17.937155 2079114000 data_layer.cpp:195] output data size: 64,1,28,28
I0902 22:52:17.938570 2079114000 net.cpp:103] Top shape: 64 1 28 28 (50176)
I0902 22:52:17.938593 2079114000 net.cpp:103] Top shape: 64 (64)
I0902 22:52:17.938611 2079114000 net.cpp:67] Creating Layer ip
I0902 22:52:17.938617 2079114000 net.cpp:394] ip <- data
I0902 22:52:17.939177 2079114000 net.cpp:356] ip -> ip
I0902 22:52:17.939196 2079114000 net.cpp:96] Setting up ip
I0902 22:52:17.940289 2079114000 net.cpp:103] Top shape: 64 2 (128)
I0902 22:52:17.941270 2079114000 net.cpp:67] Creating Layer loss
I0902 22:52:17.941305 2079114000 net.cpp:394] loss <- ip
I0902 22:52:17.941314 2079114000 net.cpp:394] loss <- label
I0902 22:52:17.941323 2079114000 net.cpp:356] loss -> loss
# set up the loss and configure the backward pass
I0902 22:52:17.941328 2079114000 net.cpp:96] Setting up loss
I0902 22:52:17.941328 2079114000 net.cpp:103] Top shape: (1)
I0902 22:52:17.941329 2079114000 net.cpp:109]     with loss weight 1
I0902 22:52:17.941779 2079114000 net.cpp:170] loss needs backward computation.
I0902 22:52:17.941787 2079114000 net.cpp:170] ip needs backward computation.
I0902 22:52:17.941794 2079114000 net.cpp:172] mnist does not need backward computation.
# determine outputs
I0902 22:52:17.941800 2079114000 net.cpp:208] This network produces output loss
# finish initialization and report memory usage
I0902 22:52:17.941810 2079114000 net.cpp:467] Collecting Learning Rate and Weight Decay.
I0902 22:52:17.941818 2079114000 net.cpp:219] Network initialization done.
I0902 22:52:17.941824 2079114000 net.cpp:220] Memory required for data: 201476

注意:caffe中網路的構件與裝置無關,回想之前解釋過的blobs和layers是為了從模型定義中隱藏實現細節的,網路構建完成之後,通過設定Caffe::mode()和Caffe::set_mode(),網路將在CPU或GPU中執行。CPU和GPU的實現結果相同,CPU和GPU可以無縫切換並且獨立於模型定義

Model格式

Caffe的模型定義在純文字的協議上(prototxt),學習好的模型被序列化到二進位制protocol buffer(binaryproto).caffemodel檔案中。

模型格式用protobuf語言定義在caffe.proto檔案中。這部分也會在筆記中解釋。

參考資料:

Caffe官網: http://caffe.berkeleyvision.org/tutorial/net_layer_blob.html