轉自:http://blog.csdn.net/liyaohhh/article/details/52115638
caffe源碼學習:softmaxWithLoss
在caffe中softmaxwithLoss是由兩部分組成,softmax+Loss組成,其實主要就是為了caffe架構的可擴充性。
運算式(1)是softmax計算運算式,(2)是sfotmaxLoss的計算損失表達。在caffe中是單獨的計算每層的輸入和輸出,然後再進行向後傳遞data結果和向前傳遞diff的結果。
caffe中softmax的計算:
如上所示;在reshape階段主要是進行相關參數維度設定。softmax_axis主要是制定從哪個維度開始切,預設是1.後面主要是初始化了兩個變數,sum_multiplier_和scale_局部變數的初始化。sum_multiplier_的形狀是channel的大小,一般主要是通過矩陣乘法來實現每個feature map相對應的座標資料進行相加或者相減或者其他的操作。scale通常情況下被用來當作臨時的緩衝變數。這兩個變數的使用在caffe中到處可見。接下來就是forward_cpu部分的代碼。
首先還是擷取top和bottom的data,把bottom的data中的資料拷貝給top,以後就直接在top中進行計算。另外在caffe中,dim變數表示的C*W*H,spatial_dim 標識的是W*H。
在上面首先是outer_num_樣本資料中輸入softmax的資料進行一個‘歸一化’操作,大紅色方框是為了找出當前instance中輸入softmax的特徵中的那個最大的數值,然後再減去那個最大數值防止產生數值計算方面的問題(例如在mnist資料集中,outer_num_是128代表的是batch的大小,inner_num_是1,這裡的softmax前面連結的是inner product層,所以spatial_dim和inner_num的大小均為1,dim的大小是10,scale_data中其實也只有一個數,雖然caffe_copy之後有兩行迴圈,其實由於inner_num是1,也就一行迴圈). 計算出最大的數值,然後把那個最大是數值乘以sum_multiplier_,就從1個數變成了C個數。繼而做exp,把結果存放到topdata,然後把全部channel的資料求和(求和caffe使用矩陣和向量的乘法進行計算)存放到scale_data,形狀是inner_num_,也就是W*H(其實就是1*1),最後就是除以求和結果啦。這麼多的語句也就描述了運算式(1). 最主要的是考慮到了數值計算中存在的問題。
下面就應該是softmaxWithLoss計算損失;
上面是softmaxWithLoss的set函數,可以和很清楚地看到在初始化完成softmax_param這個參數之後,直接把type設定成了softmax,然後又通過工廠函數建立softmaxlayer,繼而進行Set_up函數。可以看出softmaxWithLoss是內部建立了一個softmaxlayer。
繼續後面主要就是檢查當前layer是否設定不對某個label進行計算Loss,也就是說,在mnist中有10個class,但是我只想對12345678進行分類,那就設定ignore_label為9.這個時候遇到標籤是9的就不計算Loss。注意protobuf中沒有設定預設的數值,has_xxx就返回false,只有設定了數值,才會true。後面的norm是為ignore服務的。
接下來的reshape函數也沒啥用處,就是設定了三個變數。softmax_axis_=1,outer_num_=128,inner_num_=1.需要注意的是outer_num_ * inner_num_必須和bottom[1]->count()給定的lable數相同。也就是說當softmax前面接inner product的時候,每個lable對應的是instance對應的類別(一個數),但是當softmax前面是卷積層的時候,每個label就不是一個數,而是一個矩陣,對應著每個 feature map中每個像素值的分類。下面就是計算Loss:
這裡壽麵是計算softmax_layer_的forward函數,softmax_top_vec_其實是prob_的一個引用。prob_data的大小是N*C*1*1.static_cast<int>(label[i * inner_num_ + j])是從對應的instance中取出相對應的標籤。如果當前的label和ignore label相同,那麼就不計算Loss損失,這樣就存在問題,假如128個instance中有10個不計算Loss,那麼最終在計算Loss的平均的時候,是除以多少呢。這就用到了norm。count在這裡統計我們計算了多少個instance的Loss數值。prob_data[i * dim + label_value * inner_num_ + j] dim是10,label_value是instance的標籤來當作index來從prob中取資料(該instance的分類結果),j是0,因為W=H=1。
get_normalizer在VAILD模式下會返回我們計算了多少個Loss的個數,也就是count。這個時候當前batch的Loss就已經放回到top[0]中去了。下一步要計算的就是Loss的反向傳播。