【caffe解读】 caffe从数学公式到代码实现3-shape相关类
文章首發于微信公眾號《與有三學AI》
[caffe解讀] caffe從數學公式到代碼實現3-shape相關類
接著上一篇說,本篇開始讀layers下面的一些與blob?shape有關的layer,比如flatten_layer.cpp等,具體包括的在下面;
flatten_layer.cpp
slice_layer.cpp
split_layer.cpp
tile_layer.cpp
concat_layer.cpp
reduction_layer.cpp
eltwise_layer.cpp
crop_layer.cpp
pooling_layer.cpp
scale_layer.cpp
conv與deconv雖然也與shape有關,但是由于比較復雜,我們以后專門留一篇來說。下面這些層,如果你沒有仔細讀過源碼,那么建議你來讀一讀,因為有很多并沒有想象中那么簡單。
?
01 flatten_layer.cpp
Flatten?layer的作用是把一個維度為n?*?c?*?h?*?w的輸入轉化為一個維度為?n*?(c*h*w)的向量輸出,雖然在我們看來不一樣,但是在blob看來,輸入和輸出的數據存儲是沒有差異的,只是記錄的shape信息不同。所以forward和backward只是數據拷貝
template?<typename?Dtype>
void?FlattenLayer<Dtype>::Forward_cpu(constvector<Blob<Dtype>*>&?bottom,
const?vector<Blob<Dtype>*>&?top)?{?top[0]->ShareData(*bottom[0]);
}
template?<typename?Dtype>
void?FlattenLayer<Dtype>::Backward_cpu(constvector<Blob<Dtype>*>&?top,
const?vector<bool>&?propagate_down,?constvector<Blob<Dtype>*>&?bottom)?{
bottom[0]->ShareDiff(*top[0]);
}
?
02 slice_layer.cpp
Slice?layer?的作用是將bottom按照需要分解成多個tops,它的定義如下:
message?SliceParameter?{
//?By?default,?SliceLayer
concatenates?blobs?along?the?"channels"?axis?(1).
optional?int32?axis?=?3?[default?=?1];
repeated?uint32?slice_point?=?2;
//?DEPRECATED:?alias?for?"axis"?--?does?not?supportnegative?indexing.
optional?uint32?slice_dim?=?1?[default?=?1];
}
默認axis是1,也就是blob的第1個維度,即channel通道,這也是我經常使用的,一般用于有多種label時分離label。
前向反向時小心計算好offset就行,有興趣可以去看。
?
03 split_layer.cpp
它的作用是將輸入復制多份。
Forward:?在前向的時候,top[i]=bottom[0],直接賦值。
template?<typename?Dtype>
void?SplitLayer<Dtype>::Forward_cpu(constvector<Blob<Dtype>*>&?bottom,
const?vector<Blob<Dtype>*>&?top)?{
for?(int?i?=?0;?i?<?top.size();?++i)?{
??top[i]->ShareData(*bottom[0]);
}
}
Backward:?在反向的時候,需要將所有top的diff疊加起來。
template?<typename?Dtype>
void?SplitLayer<Dtype>::Backward_cpu(constvector<Blob<Dtype>*>&?top,
const?vector<bool>&?propagate_down,?constvector<Blob<Dtype>*>&?bottom)?{
if?(!propagate_down[0])?{?return;?}
if?(top.size()?==?1)?{?caffe_copy(count_,?top[0]->cpu_diff(),?bottom[0]->mutable_cpu_diff());
return;
}
caffe_add(count_,?top[0]->cpu_diff(),?top[1]->cpu_diff(),
bottom[0]->mutable_cpu_diff());
//?Add?remaining?top?blob?diffs.
for?(int?i?=?2;?i?<?top.size();?++i)?{
const?Dtype*?top_diff?=?top[i]->cpu_diff();
Dtype*?bottom_diff?=?bottom[0]->mutable_cpu_diff();
caffe_axpy(count_,?Dtype(1.),?top_diff,?bottom_diff);
}
}
?
04 tile_layer.cpp
數學定義:
將數據按照某個維度擴大n倍,看下面forward源碼,將bottom_data的前inner_dim_個數據復制了tiles份,反向時將對應diff累加回去即可。
void?TileLayer<Dtype>::Forward_cpu(
????const?vector<Blob<Dtype>*>&?bottom,?constvector<Blob<Dtype>*>&?top)?{
??const?Dtype*?bottom_data?=?bottom[0]->cpu_data();
??Dtype*?top_data?=?top[0]->mutable_cpu_data();
??for?(int?i?=?0;?i?<?outer_dim_;?++i)?{
????for?(int?t?=?0;?t?<?tiles_;?++t)?{
??????caffe_copy(inner_dim_,?bottom_data,?top_data);
??????top_data?+=?inner_dim_;
????}
????bottom_data?+=?inner_dim_;
??}
}
?
05 concat_layer.cpp
與slice_layer是反向操作,將多個bottom?blob合并成一個top_data,forward,backward計算好index就行。
?
06 reduction_layer.cpp
顧名思義,這是一個降維的層。
數學定義:
message?ReductionParameter?{
enum?ReductionOp?{
SUM?=?1;
ASUM?=?2;
SUMSQ?=?3;
MEAN?=?4;
}
optional?ReductionOp?operation?=?1?[default?=SUM];?//?reduction?operation?The?first?axis?to?reduceto?a?scalar?--?may?be?negative?to?index?from?the?end(e.g.,?-1?for?the?last?axis).?(Currently,?only?reductionalong?ALL?"tail"?axes?is?supported;
reduction?of?axis?M?through?N,?where?N?<?num_axes-?1,?is?unsupported.)
Suppose?we?have?an?n-axis?bottom?Blob?with?shape:
(d0,?d1,?d2,?...,?d(m-1),?dm,?d(m+1),?...,?d(n-1)).
If?axis?==?m,?the?output?Blob?will?have?shape??(d0,d1,?d2,?...,?d(m-1)),
and?the?ReductionOp?operation?is?performed?(d0?*d1?*?d2?*?...?*?d(m-1))
times,?each?including?(dm?*?d(m+1)?*?...?*?d(n-1))individual?data.
If?axis?==?0?(the?default),?the?output?Blob?always?hasthe?empty?shape
(count?1),?performing?reduction?across?the?entireinput?often?useful?for?creating?new?loss?functions.
optional?int32?axis?=?2?[default?=?0];
optional?float?coeff?=?3?[default?=?1.0];?//?coefficientfor?output
}
從上面可以看出,reduct有4類操作,sum,mean,asum,sumsq,分別是求和,求絕對值和,求平方和與平均。它會從axis這個維度開始去降維,比如當axis=0,就是從第0維開始將所有blob降維,最終會得到一個標量數,常用于loss。
在reshape函數中可以看到,
axis_?=?bottom[0]->CanonicalAxisIndex(this->layer_param_.reduction_param().axis());
vector<int>?top_shape(bottom[0]->shape().begin(),bottom[0]->shape().begin()?+?axis_);
top[0]->Reshape(top_shape);
num_?=?bottom[0]->count(0,?axis_);
dim_?=?bottom[0]->count(axis_);
CHECK_EQ(num_,?top[0]->count());
通過reduction_param().axis())設置維度之后,top[0]的元素數目就是num_?=
bottom[0]->count(0,?axis_);我們假設輸入blob是10*3*224*224,如果axis=0,那么top[0]=10*1*1*1;如果axis=1,那么top[0]=10*3*1*1,以此類推。
Forward和Backward對應這4個操作去看代碼即可,只要知道反向的時候,top的每一個元素的梯度會反傳給bottom的多個元素。
?
07 eltwise_layer.cpp
eltwise是一個有多個bottom輸入,一個top輸出的layer,對逐個的元素進行操作,所bottom[i]和top[j]的大小都是相等的。Eltwise參數有相乘PROB,相加SUM,求MAX。對于SUM操作,該層定義了?coeff?參數,用于調整權重。?對于PROB操作,設定了stable_prod_grad?#[default?=?true?]?來選擇是否漸進較慢的梯度計算方法,forward過程不需要說太多,而對于backward,有必要說一下。下面舉prob操作的例子;
我們看相應函數,這只是內循環,實際上還有外循環。
case?EltwiseParameter_EltwiseOp_PROD:
if?(stable_prod_grad_)?{?bool?initialized?=?false;
for?(int?j?=?0;?j?<?bottom.size();?++j)?{
if?(i?==?j)?{?continue;?}
if?(!initialized)?{?
caffe_copy(count,?bottom[j]->cpu_data(),bottom_diff);
initialized?=?true;
}?else?{
caffe_mul(count,?bottom[j]->cpu_data(),?bottom_diff,
bottom_diff);
}
}
}?else?{
caffe_div(count,?top_data,?bottom_data,bottom_diff);
}
caffe_mul(count,?bottom_diff,?top_diff,?bottom_diff);
當stable_prod_grad?=?false時,直接對應了上面的式top_data/bottom_data*bottom_diff,但是如果stable_prod_grad?=?true,差異在哪呢?反正我是沒看出啥區別,只是為true時沒有利用已經計算好的結果,計算更慢了。
?
08 crop_layer.cpp
crop?layer改變blob的第2,3個維度,而不是改變前兩個維度,也沒有復雜的數學操作,所以只需要記錄下offset即可,感興趣還是去看源碼。
?
09 pooling_layer.cpp
pooling?layer想必大家都很熟悉了,caffe官方的有MAX,MEAN兩種,還保留了一種random的沒有實現。?Max和Mean的區別會在什么地方呢?主要就是max會存在一個mask,因為它要記錄對top有貢獻的那個元素,在梯度反傳的時候,也只會反傳到1個元素,而mean則會反傳到r*r個元素,r就是濾波的半徑。
其他的倒是沒有需要特別注意的地方,主要就是bottom到top的index計算,細節處小心即可。
?
10 bnll_layer.cpp
數學定義:
就這么多。
?
11 scale_layer.cpp
scale這個layer絕對比你想象中復雜多。我們通常以為是這樣就完了
其中a是一個標量,x是一個矢量,在caffe中就是blob,但是實際上a也可以是blob,它可以有如下尺寸,見scale參數的定義:
message?ScaleParameter?{
//?The?first?axis?of?bottom[0]?(the?first?input?Blob)along?which?to?apply,?bottom[1]?(the?second?inputBlob).?May?be?negative?to?index?from?the?end?(e.g.,-1?for?the?last
axis).
//?For?example,?if?bottom[0]?is?4D?with?shape100x3x40x60,?the?output?top[0]?will?have?the?sameshape,?and?bottom[1]?may?have?any?of?the?followingshapes?(for?the?given?value?of?axis):?
(axis?==?0?==?-4)?100;?100x3;?100x3x40;100x3x40x60
(axis?==?1?==?-3)?3;?3x40;?3x40x60
(axis?==?2?==?-2)?40;?40x60
(axis?==?3?==?-1)?60
//?Furthermore,?bottom[1]?may?have?the?emptyshape?(regardless?of?the?value?of?"axis")?a?scalarmultiplier.
optional?int32?axis?=?1?[default?=?1];
//?(num_axes?is?ignored?unless?just?one?bottom?isgiven?and?the?scale?is?a?learned?parameter?of?thelayer.?Otherwise,?num_axes?is?determined?by?thenumber?of?axes?by?the?second?bottom.)
//?The?number?of?axes?of?the?input?(bottom[0])covered?by?the?scale
//?parameter,?or?-1?to?cover?all?axes?of?bottom[0]starting?from?`axis`.
//?Set?num_axes?:=?0,?to
multiply?with?a?zero-axis?Blob:?a?scalar.
optional?int32?num_axes?=?2?[default?=?1];
//?(filler?is?ignored?unless?just?one?bottom?is?givenand?the?scale?is
//?a?learned?parameter?of?the?layer.)
//?The?initialization?for?the?learned?scale?parameter.
//?Default?is?the?unit?(1)?initialization,?resulting?in?theScaleLayer
//?initially?performing?the?identity?operation.
optional?FillerParameter?filler?=?3;
//?Whether?to?also?learn?a?bias?(equivalent?to?aScaleLayer+BiasLayer,?but
//?may?be?more?efficient).?Initialized?with?bias_filler(defaults?to?0).
optional?bool?bias_term?=?4?[default?=?false];
optional?FillerParameter?bias_filler?=?5;
}
從上面我們可以知道這些信息;
(1)?scale_layer是輸入輸出可以都是1個,但是,輸入可以是兩個,也就是bottom[1]是scale,當沒有bottom[1]時,就是通過一個標量參數來實現scale。
(2)?scale可以有多種尺寸。從1維到4維。
上面舉了例子,當輸入x是100x3x40x60,scale?blob可以是100;?100x3;
100x3x40;?100x3x40x60這幾種尺寸,所以在forward,backward的時候,需要對上尺寸。
這一節看起來比較亂,就當讀書筆記吧,只是有很多細節,真的需要自己去摳才知道坑在哪。
?
同時,在我的知乎專欄也會開始同步更新這個模塊,歡迎來交流
https://zhuanlan.zhihu.com/c_151876233
注:部分圖片來自網絡
—END—
打一個小廣告,我在gitchat開設了一些課程和chat,歡迎交流。
感謝各位看官的耐心閱讀,不足之處希望多多指教。后續內容將會不定期奉上,歡迎大家關注有三公眾號 有三AI!
?
?
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的【caffe解读】 caffe从数学公式到代码实现3-shape相关类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【caffe解读】 caffe从数学公式
- 下一篇: 【学习・求职必备】可谓是相当Awesom