1. Caffe目錄結(jié)構(gòu)
data/
用于存放下載的訓(xùn)練數(shù)據(jù)
docs/
幫助文檔
examples/
代碼樣例
matlab/
MATLAB接口文件
python/
PYTHON接口文件
models/
一些配置好的模型參數(shù)
scripts/
一些文檔和數(shù)據(jù)會(huì)用到的腳本
核心代碼
tools/
保存的源碼是用于生成二進(jìn)制處理程序的婆咸,caffe在訓(xùn)練時(shí)實(shí)際是直接調(diào)用這些二進(jìn)制文件
include/
caffe的實(shí)現(xiàn)代碼的頭文件
src/
實(shí)現(xiàn)caffe的源文件
src/文件結(jié)構(gòu)
gtest/
google test一個(gè)用于測(cè)試的庫(kù)你make runtest時(shí)看見(jiàn)的很多綠色RUN OK就是它,這個(gè)與caffe的學(xué)習(xí)無(wú)關(guān)奈泪,不過(guò)是個(gè)有用的庫(kù)
caffe/
關(guān)鍵代碼
test/
用gtest測(cè)試caffe的代碼
util/
數(shù)據(jù)轉(zhuǎn)換時(shí)用的一些代碼。caffe速度快戈钢,很大程度得益于內(nèi)存設(shè)計(jì)上的優(yōu)化(blob數(shù)據(jù)結(jié)構(gòu)采用proto)和對(duì)卷積的優(yōu)化(部分與im2col相關(guān))
proto/
即所謂的“Protobuf”,全稱“Google Protocol Buffer”是尔,是一種數(shù)據(jù)存儲(chǔ)格式殉了,幫助caffe提速
layers/
深度神經(jīng)網(wǎng)絡(luò)中的基本結(jié)構(gòu)就是一層層互不相同的網(wǎng)絡(luò)了,這個(gè)文件夾下的源文件以及目前位置src/caffe
中包含所有.cpp文件就是caffe的核心目錄下的核心代碼了拟枚。
2. Caffe核心代碼
核心代碼:
blob.cpp.h
基本的數(shù)據(jù)結(jié)構(gòu)Blob類
commo.cpp.h
定義Caffe類
internal_thread.cpp .h
使用boost::thread線程庫(kù)
net.cpp.h
網(wǎng)絡(luò)結(jié)構(gòu)類Net
solver.cpp.h
優(yōu)化方法類Solver
data_transformer.cpp.h
輸入數(shù)據(jù)的基本操作類DataTransformer
syncedmem.cpp.h
分配內(nèi)存和釋放內(nèi)存類CaffeMallocHost薪铜,用于同步GPU,CPU數(shù)據(jù)
layer.cpp.h
層類Layer
layers/
此文件夾下面的代碼全部至少繼承了類Layer, 從layer_factory中注冊(cè)繼承
3. Caffe三層結(jié)構(gòu)(Blob恩溅,Layers隔箍,Nets)
Blob:用于數(shù)據(jù)的保存、交換和操作脚乡,Caffe基礎(chǔ)存儲(chǔ)結(jié)構(gòu)
Layer:用于模型和計(jì)算的基礎(chǔ)
Net:整合連接Layers
Blob
在內(nèi)存中表示4維數(shù)組蜒滩,在caffe/blob.hpp
中,維度包括(width_,height_,channels_,num_
)
num_
用于存儲(chǔ)數(shù)據(jù)或權(quán)值(data)和權(quán)值增量(diff)
blob.hpp
Blob 在caffe源碼blob.hpp
中是一個(gè)模板類奶稠。
protected 的成員變量有:data_ , diff_ , shape_ , count_ , capacity_
俯艰,其中data_
和diff_
是共享SyncedMemory 類(在syncedmem的源碼中定義)的智能指針,shape_
是int型的vector锌订,count_
和capacity_
是整型變量竹握。
其成員函數(shù)主要有:Reshape, ReshapeLike, SharedData, Updata
等等。
blob.hpp
包含了caffe.pb.h
瀑志,說(shuō)明caffe protobuf 會(huì)向blob 傳遞參數(shù)涩搓。
#include "caffe/proto/caffe.pb.h"
caffe.pb.h
是google protocol buffer根據(jù)caffe.proto
自動(dòng)生成的,可以到src/caffe/proto/caffe.proto
里看下caffe里面用到的各個(gè)數(shù)據(jù)的定義劈猪,比如BlobProto昧甘,Datum,NetParameter等战得。使用這個(gè)protocol buffer看起來(lái)確實(shí)方便充边,一方面可以用文本文件定義結(jié)構(gòu)化的數(shù)據(jù)類型,另一方面可以生成查詢效率更高常侦、占空間更小的二進(jìn)制文件
#include "caffe/common.hpp"
主要singleton化Caffe類浇冰,并封裝了boost和CUDA隨機(jī)數(shù)生成的函數(shù),提供了統(tǒng)一的接口聋亡。
#include "caffe/syncedmem.hpp"
定義了以下的接口:
inline void CaffeMallocHost(void** ptr, size_t size)
inline void CaffeFreeHost(void* ptr)
主要是分配內(nèi)存和釋放內(nèi)存的肘习。而class SyncedMemory定義了內(nèi)存分配管理和CPU與GPU之間同步的函數(shù)
#include "caffe/util/math_functions.hpp"
封裝了很多cblas矩陣運(yùn)算。
caffe.proto
里面BlobProto的定義:
對(duì)于BlobProto坡倔,可以看到定義了四個(gè)optional的int32類型的名字(name)num漂佩、channels脖含、height和width,optional意味著B(niǎo)lob可以有一個(gè)或者沒(méi)有這個(gè)參數(shù)投蝉,每個(gè)名字(name)后面都有一個(gè)數(shù)字养葵,這個(gè)數(shù)字是其名字的一個(gè)標(biāo)簽。這個(gè)數(shù)字就是用來(lái)在生成的二進(jìn)制文件中搜索查詢的標(biāo)簽瘩缆。關(guān)于這個(gè)數(shù)字关拒,1到15會(huì)花費(fèi)1byte的編碼空間,16到2047花費(fèi)2byte庸娱。所以一般建議把那些頻繁使用的名字的標(biāo)簽設(shè)為1到15之間的值着绊。而后面的repeated意味著float類型的data和diff可以重復(fù)任意次,而加上[packed = true]是為了更高效的編碼涌韩。
主要數(shù)據(jù)有兩個(gè)data和diff畔柔,用num、channels臣樱、height和width這四個(gè)維度來(lái)確定數(shù)據(jù)的具體位置,做一些數(shù)據(jù)查詢和Blob reshape的操作腮考。
message BlobProto {
optional BlobShape shape = 7;
repeated float data = 5 [packed = true];
repeated float diff = 6 [packed = true];
repeated double double_data = 8 [packed = true];
repeated double double_diff = 9 [packed = true];
// 4D dimensions -- deprecated. Use "shape" instead.
optional int32 num = 1 [default = 0];
optional int32 channels = 2 [default = 0];
optional int32 height = 3 [default = 0];
optional int32 width = 4 [default = 0];
}
Blob主要變量
shared_ptr<SyncedMemory> data_;
shared_ptr<SyncedMemory> diff_;
shared_ptr<SyncedMemory> shape_data_;
vector<int> shape_;
int count_;
int capacity_;
BLob只是一個(gè)基本的數(shù)據(jù)結(jié)構(gòu)雇毫,因此內(nèi)部的變量相對(duì)較少,首先是data_
指針踩蔚,指針類型是shared_ptr
棚放,屬于boost庫(kù)的一個(gè)智能指針,這一部分主要用來(lái)申請(qǐng)內(nèi)存存儲(chǔ)data馅闽,data主要是正向傳播的時(shí)候用的飘蚯。同理,diff_
主要用來(lái)存儲(chǔ)偏差福也,update data
局骤,shape_data
和shape_
都是存儲(chǔ)Blob的形狀,一個(gè)是老版本一個(gè)是新版本暴凑。count_
表示Blob中的元素個(gè)數(shù)峦甩,也就是個(gè)數(shù)*通道數(shù)*高度*寬度
,capacity_
表示當(dāng)前的元素個(gè)數(shù),因?yàn)锽lob可能會(huì)reshape现喳。
主要函數(shù)
1. 構(gòu)造函數(shù) & 2. reshape函數(shù)
構(gòu)造函數(shù)開(kāi)辟一個(gè)內(nèi)存空間來(lái)存儲(chǔ)數(shù)據(jù)凯傲,Reshape函數(shù)在Layer中的reshape或者forward操作中來(lái)adjust dimension。同時(shí)在改變Blob大小時(shí)嗦篱,內(nèi)存將會(huì)被重新分配如果內(nèi)存大小不夠了冰单,并且額外的內(nèi)存將不會(huì)被釋放。對(duì)input的blob進(jìn)行reshape,如果立馬調(diào)用Net::Backward
是會(huì)出錯(cuò)的灸促,因?yàn)閞eshape之后诫欠,要么Net::forward
或者Net::Reshape
就會(huì)被調(diào)用來(lái)將新的input shape傳播到高層涵卵。
3. count函數(shù)
重載很多個(gè)count()函數(shù),主要還是為了統(tǒng)計(jì)Blob的容量(volume)呕诉,或者是某一片(slice)缘厢,從某個(gè)axis到具體某個(gè)axis的shape乘積(如 inline int count(int start_axis, int end_axis)
)。
4. data_數(shù)據(jù)操作函數(shù) & 5. 反向傳播導(dǎo)數(shù)diff_操作函數(shù)
inline Dtype data_at(const int n, const int c, const int h, const int w)
inline Dtype diff_at(const int n, const int c, const int h, const int w)
inline Dtype data_at(const vector<int>& index)
inline Dtype diff_at(const vector<int>& index)
inline const shared_ptr<SyncedMemory>& data()
inline const shared_ptr<SyncedMemory>& diff()
這一部分函數(shù)主要通過(guò)給定的位置訪問(wèn)數(shù)據(jù)甩挫,根據(jù)位置計(jì)算與數(shù)據(jù)起始的偏差offset贴硫,在通過(guò)cpu_data*
指針獲得地址
6. FromProto/ToProto 數(shù)據(jù)序列化
將數(shù)據(jù)序列化,存儲(chǔ)到BlobProto伊者,這里說(shuō)到Proto是谷歌的一個(gè)數(shù)據(jù)序列化的存儲(chǔ)格式英遭,可以實(shí)現(xiàn)語(yǔ)言、平臺(tái)無(wú)關(guān)亦渗、可擴(kuò)展的序列化結(jié)構(gòu)數(shù)據(jù)格式挖诸。
7. Update函數(shù)
該函數(shù)用于參數(shù)blob的更新(weight,bias 等減去對(duì)應(yīng)的導(dǎo)數(shù))
8.其他運(yùn)算函數(shù)
Dtype asum_data() const;//計(jì)算data的L1范數(shù)(所有元素絕對(duì)值之和)
Dtype asum_diff() const;//計(jì)算diff的L1范數(shù)
Dtype sumsq_data() const;//計(jì)算data的L2范數(shù)(所有元素平方和)
Dtype sumsq_diff() const;//計(jì)算diff的L2范數(shù)
void scale_data(Dtype scale_factor);//將data部分乘以一個(gè)因子
void scale_diff(Dtype scale_factor);//將diff部分乘一個(gè)因子
Blob編程操作
cd ~
ls
cd caffe
cd build
之前安裝caffe法精,裝在/usr/local
下或~/caffe/build/install
下
如~/caffe/build/install
下多律,重裝一下
~/caffe/build$ sudo cmake -D CPU_ONLY=ON -D CMAKE_INSTALL_PREFIX=/usr/local ..
~/caffe/build$ sudo make install
export LD_LIBRARY_PATH=/usr/local/lib:${LD_LIBRARY_PATH}
cd ~/teach_samples/caffe/
ls
vim test_blob.cpp
test_blob.cpp
gcc -o test_blob test_blob.cpp -D CPU_ONLY -lcaffe -lstdc++ -lglog
/*
*
* 功能:
* 1. 測(cè)試使用Blob
* 2. 給blob賦值
* 3. 獲取blob指定位置的值
* 4. 輸出通過(guò)L1范式和L2范式得到的結(jié)果
*
*/
#include <iostream>
#include "caffe/blob.hpp"
int main(int argc,char* argv[]){
//構(gòu)造一個(gè)Blob
caffe::Blob<float> b;
std::cout<<"Size : "<<b.shape_string()<<std::endl;
b.Reshape(1,2,3,4);
std::cout<<"Size : "<<b.shape_string()<<std::endl;
//使用mutable_cpu_data函數(shù)修改Blob內(nèi)部數(shù)值
float* p = b.mutable_cpu_data();
for(int i=0;i<b.count();i++){
p[i] = i;
}
//打印指定位置的每一個(gè)數(shù)值
for(int u=0; u<b.num() ;u++){
for(int v=0;v<b.channels();v++){
for(int w=0;w<b.height();w++){
for(int x=0; x<b.width();x++){
std::cout<<"b["<<u<<"] ["<<v<<"] ["<<w<<"] ["<<x<<"] ="<<b.data_at(u,v,w,x)<<std::endl;
}
}
}
}
//求L1,L2范式及其輸出結(jié)果
std::cout<<"ASUM : "<<b.asum_data()<<std::endl;
std::cout<<"SUMSQ : "<<b.sumsq_data()<<std::endl;
return 0;
}
測(cè)試
~/teach_samples/caffe$ ./test_blob
Layer
所有的Pooling,Convolve搂蜓,apply nonlinearities等操作都在這里實(shí)現(xiàn)狼荞。在Layer中input data用bottom表示output data用top表示。每一層定義了三種操作setup(Layer初始化), forward(正向傳導(dǎo)帮碰,根據(jù)input計(jì)算output), backward(反向傳導(dǎo)計(jì)算相味,根據(jù)output計(jì)算input的梯度)。forward和backward有GPU和CPU兩個(gè)版本的實(shí)現(xiàn)殉挽。
5種衍生Layers:
data_layer
neuron_layer
loss_layer
common_layer
vision_layer
data_layer
data_layer
主要包含與數(shù)據(jù)有關(guān)的文件丰涉。在官方文檔中指出data是caffe數(shù)據(jù)的入口是網(wǎng)絡(luò)的最低層,并且支持多種格式斯碌,在這之中又有5種LayerType:
DATA
用于LevelDB或LMDB數(shù)據(jù)格式的輸入的類型一死,輸入?yún)?shù)有source, batch_size, (rand_skip), (backend)。后兩個(gè)是可選输拇。
MEMORY_DATA
這種類型可以直接從內(nèi)存讀取數(shù)據(jù)使用時(shí)需要調(diào)用MemoryDataLayer::Reset摘符,輸入?yún)?shù)有batch_size, channels, height, width。
HDF5_DATA
HDF5數(shù)據(jù)格式輸入的類型策吠,輸入?yún)?shù)有source, batch_size逛裤。
HDF5_OUTPUT
HDF5數(shù)據(jù)格式輸出的類型,輸入?yún)?shù)有file_name猴抹。
IMAGE_DATA
圖像格式數(shù)據(jù)輸入的類型带族,輸入?yún)?shù)有source, batch_size, (rand_skip), (shuffle), (new_height), (new_width)。
其實(shí)還有兩種WINDOW_DATA
, DUMMY_DATA
用于測(cè)試和預(yù)留的接口,不重要蟀给。
neuron_layer
同樣是數(shù)據(jù)的操作層蝙砌,neuron_layer
實(shí)現(xiàn)里大量激活函數(shù)阳堕,主要是元素級(jí)別的操作,具有相同的bottom,top size择克。
Caffe中實(shí)現(xiàn)了大量激活函數(shù)GPU和CPU的都有很多恬总。它們的父類都是NeuronLayer
template <typename Dtype>
class NeuronLayer : public Layer<Dtype>
一般的參數(shù)設(shè)置格式如下(以ReLU為例):
layers {
name: "relu1"
type: RELU
bottom: "conv1"
top: "conv1"
}
loss_layer
Loss層計(jì)算網(wǎng)絡(luò)誤差,loss_layer.hpp
頭文件調(diào)用情況:
#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/layer.hpp"
#include "caffe/neuron_layers.hpp"
#include "caffe/proto/caffe.pb.h"
可以看見(jiàn)調(diào)用了neuron_layers.hpp
肚邢,估計(jì)是需要調(diào)用里面的函數(shù)計(jì)算Loss壹堰,一般來(lái)說(shuō)Loss放在最后一層。caffe實(shí)現(xiàn)了大量loss function骡湖,它們的父類都是LossLayer贱纠。
template <typename Dtype>
class LossLayer : public Layer<Dtype>
common_layer
這一層主要進(jìn)行的是vision_layer
的連接
聲明了9個(gè)類型的common_layer
,部分有GPU實(shí)現(xiàn):
InnerProductLayer
常常用來(lái)作為全連接層
SplitLayer
用于一輸入對(duì)多輸出的場(chǎng)合(對(duì)blob)
FlattenLayer
將n * c * h * w變成向量的格式n * ( c * h * w ) * 1 * 1
ConcatLayer
用于多輸入一輸出的場(chǎng)合
SilenceLayer
用于一輸入對(duì)多輸出的場(chǎng)合(對(duì)layer)
(Elementwise Operations) 這里面是我們常說(shuō)的激活函數(shù)層Activation Layers
响蕴。
EltwiseLayer
SoftmaxLayer
ArgMaxLayer
MVNLayer
vision_layer
主要是實(shí)現(xiàn)Convolution和Pooling操作, 主要有以下幾個(gè)類:
ConvolutionLayer
最常用的卷積操作
Im2colLayer
與MATLAB里面的im2col類似谆焊,即image-to-column transformation,轉(zhuǎn)換后方便卷積計(jì)算
LRNLayer
全稱local response normalization layer浦夷,在Hinton論文中有詳細(xì)介紹ImageNet Classification with Deep Convolutional Neural Networks 辖试。
PoolingLayer
Pooling操作
Net
Net由一系列的Layer組成(無(wú)回路有向圖DAG),Layer之間的連接由一個(gè)文本文件描述劈狐。模型初始化Net::Init()
會(huì)產(chǎn)生blob和layer并調(diào)用Layer::SetUp
剃执。在此過(guò)程中Net會(huì)報(bào)告初始化進(jìn)程。這里的初始化與設(shè)備無(wú)關(guān)懈息,在初始化之后通過(guò)Caffe::set_mode()
設(shè)置Caffe::mode()
來(lái)選擇運(yùn)行平臺(tái)CPU或GPU,結(jié)果是相同的摹恰。
4. ProtoBuf
Caffe中辫继,數(shù)據(jù)的讀取、運(yùn)算俗慈、存儲(chǔ)都是采用Google Protocol Buffer來(lái)進(jìn)行的姑宽。
Protocol Buffer(PB)是一種輕便、高效的結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)格式闺阱,可以用于結(jié)構(gòu)化數(shù)據(jù)串行化炮车,很適合做數(shù)據(jù)存儲(chǔ)或 RPC 數(shù)據(jù)交換格式。它可用于通訊協(xié)議酣溃、數(shù)據(jù)存儲(chǔ)等領(lǐng)域的語(yǔ)言無(wú)關(guān)瘦穆、平臺(tái)無(wú)關(guān)、可擴(kuò)展的序列化結(jié)構(gòu)數(shù)據(jù)格式赊豌。是一種效率和兼容性都很優(yōu)秀的二進(jìn)制數(shù)據(jù)傳輸格式扛或,目前提供了 C++、Java碘饼、Python 三種語(yǔ)言的 API熙兔。Caffe采用的是C++和Python的API悲伶。
protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto
這里將給出上述命令的參數(shù)解釋。
-
protoc
為Protocol Buffer提供的命令行編譯工具住涉。 -
--proto_path
等同于-I選項(xiàng)麸锉,主要用于指定待編譯的.proto消息定義文件所在的目錄,該選項(xiàng)可以被同時(shí)指定多個(gè)舆声。 -
--cpp_out
選項(xiàng)表示生成C++代碼花沉,--java_out
表示生成Java代碼,--python_out
則表示生成Python代碼纳寂,其后的目錄為生成后的代碼所存放的目錄主穗。 -
path/to/file.proto
表示待編譯的消息定義文件。
注:對(duì)于C++而言毙芜,通過(guò)Protocol Buffer編譯工具忽媒,可以將每個(gè).proto文件生成出一對(duì).h和.cc的C++代碼文件。生成后的文件可以直接加載到應(yīng)用程序所在的工程項(xiàng)目中腋粥。
代碼文件
ls
vim ly.helloworld.proto
:q!退出
C++編譯
protoc -I=. --cpp_out=. ly.helloworld.proto
ls
vim test_pb.cpp
:q!退出
編譯
gcc -o test_pb test_pb.cpp ly.helloworld.pb.cc -lprotobuf -lstdc++
ls
./test_pb
ls
看到log文件輸出成功
5. 訓(xùn)練mnist數(shù)據(jù)集
Training LeNet on MNIST with Caffe
- 獲取數(shù)據(jù)
./data/mnist/get_mnist.sh
- 將數(shù)據(jù)轉(zhuǎn)化為lmdb格式
./examples/mnist/create_mnist.sh
- 訓(xùn)練
./examples/mnist/train_lenet.sh
注意:
- 腳本的運(yùn)行基于
$Caffe_Root
文件加下的路徑執(zhí)行 - 訓(xùn)練的時(shí)候晦雨,如果安裝的時(shí)候選擇了CPU_ONLY的話,在
*.prototxt
文件中隘冲,把mode:GPU
改成mode:CPU
下載數(shù)據(jù)
cd caffe
./data/mnist/get_mnist.sh
轉(zhuǎn)化數(shù)據(jù)
./examples/mnist/create_mnist.sh
./examples/mnist/create_mnist.sh
create_mnist.sh
是利用caffe-master/build/examples/mnist/
的convert_mnist_data.bin
工具闹瞧,將mnist date轉(zhuǎn)化為可用的lmdb格式的文件。并將新生成的2個(gè)文件mnist-train-lmdb
和mnist-test-lmdb
放于create_mnist.sh
同目錄下展辞。
訓(xùn)練
./examples/mnist/train_lenet.sh
如果沒(méi)有GPU,使用CPU模式,修改$CAFFE_ROOT/examples/mnist/lenet_solver.prototxt:
# solver mode: CPU or GPU
solver_mode: CPU
# The train/test net protocol buffer definition
net: "examples/mnist/lenet_train_test.prototxt" //網(wǎng)絡(luò)協(xié)議具體定義
# test_iter specifies how many forward passes the test should carry out.
# In the case of MNIST, we have test batch size 100 and 100 test iterations,
# covering the full 10,000 testing images.
test_iter: 100 //test迭代次數(shù),如果batch_size=100,則100張圖一批,訓(xùn)練100次,則可以覆蓋10000張圖的需求
# Carry out testing every 500 training iterations.
test_interval: 500 //訓(xùn)練迭代500次測(cè)試一次
# The base learning rate, momentum and the weight decay of the network.//網(wǎng)絡(luò)參數(shù):學(xué)習(xí)率,動(dòng)量,權(quán)重的衰減
base_lr: 0.01
momentum: 0.9
weight_decay: 0.0005
# The learning rate policy //學(xué)習(xí)策略:有固定學(xué)習(xí)率和每步遞減學(xué)習(xí)率
lr_policy: "inv"
gamma: 0.0001
power: 0.75
# Display every 100 iterations //每迭代100次顯示一次
display: 100
# The maximum number of iterations //最大迭代次數(shù)
max_iter: 10000
# snapshot intermediate results //每5000次迭代存儲(chǔ)一次數(shù)據(jù),路徑前綴如下
snapshot: 5000
snapshot_prefix: "examples/mnist/lenet"
# solver mode: CPU or GPU //使用CPU或者GPU
solver_mode: GPU
./examples/mnist/train_lenet.sh
調(diào)用工具:./build/tools/caffe train --solver=examples/mnist/lenet_solver.prototxt
看到optimization done訓(xùn)練結(jié)束奥邮,~/caffe/examples/mnist/
多了caffemodel
CPU:i3-3240 CPU @ 3.40GHz 訓(xùn)練耗時(shí)15分鐘