1. 程式人生 > >caffe之SoftmaxWithLoss層 自定義實現

caffe之SoftmaxWithLoss層 自定義實現

        caffe中的各層實現,因為封裝了各種函式和為了擴充套件,在提升了效率的同時,降低了一定的程式碼可讀性,這裡,為了更好地理解softmax以及caffe中前向傳播和反向傳播的原理,我用通俗易懂的程式碼實現了SoftmaxWithLoss層(以下簡稱loss層),進行前向傳播和反向傳播,得到的訓練結果和內建的程式碼結果是一樣的。

       這裡定義batch_size為網路輸入的批大小,label_num表示標籤的類別數。而loss層的輸入blob是兩個,一個是全連線層,維度是batch_size*label_num,一個是標籤層,維度是label_num*1,為了通俗易懂,我們舉個例子,比如mnist問題的lenLet網路,是一個10類的分類問題(數字0~9),訓練時,每個batch大小為64,所以,這裡的batch_size=64,label_num=10。  這裡Softmax 層的各種原理,以及根據loss反向傳播時的梯度推導,因為這裡寫公式不方便,我就在word裡寫了,如下圖,


然後,貼程式碼吧:

標頭檔案:

#ifndef CAFFE_MY_LOSS_LAYER_HPP_
#define CAFFE_MY_LOSS_LAYER_HPP_

#include <vector>

#include "caffe/blob.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"

#include "caffe/layers/loss_layer.hpp"
#include "caffe/layers/softmax_layer.hpp"

namespace caffe {

template <typename Dtype>
class MyLossLayer : public LossLayer<Dtype> {
 public:
  explicit MyLossLayer(const LayerParameter& param)
      : LossLayer<Dtype>(param) {}
  virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top);
  virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top);

  virtual inline const char* type() const { return "MyLoss"; }
  virtual inline int ExactNumTopBlobs() const { return 1; }
  virtual inline int MinTopBlobs() const { return 1; }
  virtual inline int MaxTopBlobs() const { return 2; }

 protected:
  virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top);
  virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
      const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);

  vector<vector<Dtype> > prob_;   //儲存置信度
  int label_num;    //標籤個數
  int batch_size;   //批大小

};

}  // namespace caffe

#endif  // CAFFE_MY_LOSS_LAYER_HPP_

原始檔,反向傳播時,按照公式更新梯度就好了
#include <algorithm>
#include <cfloat>
#include <vector>

#include "caffe/layers/my_loss_layer.hpp"
#include "caffe/util/math_functions.hpp"
using namespace std;
namespace caffe {

template <typename Dtype>
void MyLossLayer<Dtype>::LayerSetUp(
    const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
  LossLayer<Dtype>::LayerSetUp(bottom, top);
}

template <typename Dtype>
void MyLossLayer<Dtype>::Reshape(
    const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
  LossLayer<Dtype>::Reshape(bottom, top);
  this->label_num=bottom[0]->channels();   //標籤數 ,比如mnist為10
  this->batch_size=bottom[0]->num();       //batch大小,比如mnist 一次輸入64個
  this->prob_=vector<vector<Dtype> >(batch_size,vector<Dtype>(label_num,Dtype(0)));  //置信度陣列 64*10
}

template <typename Dtype>
void MyLossLayer<Dtype>::Forward_cpu(
    const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {

	//為了避免數值問題,計算prob_時,先減最大值,再按照softmax公式計算各置信度
	for(int i=0;i<batch_size;++i){
		//求最大值,並減最大值
		Dtype mmax=-10000000;
		for(int j=0;j<label_num;++j)
			mmax=max<Dtype>(mmax,bottom[0]->data_at(i,j,0,0));
		for(int j=0;j<label_num;++j)
			prob_[i][j]=bottom[0]->data_at(i,j,0,0)-mmax;
		Dtype sum=0.0;   //求出分母
		for(int j=0;j<label_num;++j)
			sum+=exp(prob_[i][j]);
		for(int j=0;j<label_num;++j)   //計算各個置信度
			prob_[i][j]=exp(prob_[i][j])/sum;
	}
	//根據計算好的置信度,計算loss
	Dtype loss=0.0;
    const Dtype* label = bottom[1]->cpu_data();   //標籤陣列  64
	for(int i=0;i<batch_size;++i){
		int realLabel=static_cast<int>(label[i]);  //圖片i的真實標籤
		Dtype tmpProb=prob_[i][realLabel];         //屬於真實標籤的置信度
        loss -= log(max<Dtype>(tmpProb,Dtype(FLT_MIN)));   //防止資料溢位問題
	}

    top[0]->mutable_cpu_data()[0] = loss / batch_size;
}

//反向傳播,計算梯度
template <typename Dtype>
void MyLossLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
    const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
  if (propagate_down[0]) {
    Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();
    const Dtype* label = bottom[1]->cpu_data();   //標籤 

	for(int i=0;i<batch_size;++i){
		int realLabel=static_cast<int>(label[i]);  //圖片i的真實標籤
		for(int j=0;j<label_num;++j){
			int offset=bottom[0]->offset(i,j);
			if(j==realLabel)                       //按照公式,如果分量就是真實標籤,直接在置信度上減去1,就得到該分量的梯度
				bottom_diff[offset]=prob_[i][j]-1;
			else                                  //否則,梯度等於置信度
				bottom_diff[offset]=prob_[i][j]; 
		}
	}
	for(int i=0;i<bottom[0]->count();++i)   //梯度歸一化,除以batch大小
		bottom_diff[i]/=batch_size;
  }
}


INSTANTIATE_CLASS(MyLossLayer);
REGISTER_LAYER_CLASS(MyLoss);

}  // namespace caffe
編譯好後,用mnist的資料跑一下試試:
layer {
    name: "my_loss"
    type: "MyLoss"
    bottom: "ip2"
    bottom: "label"
    top: "my_loss"
}
最後結果:


相關推薦

caffeSoftmaxWithLoss 定義實現

        caffe中的各層實現,因為封裝了各種函式和為了擴充套件,在提升了效率的同時,降低了一定的程式碼可讀性,這裡,為了更好地理解softmax以及caffe中前向傳播和反向傳播的原理,我用通俗易懂的程式碼實現了SoftmaxWithLoss層(以下簡稱loss層

Django模板-定義過濾器以及標籤

自定義標籤與過濾器 在settings中的INSTALLED_APPS配置當前app,不然django無法找到自定義的simple_tag. 在app中建立templatetags模組(模組名只能是templatetags) 建立任意 .py 檔案,如:my_tags.py from d

Django模板-定義過濾器以及標簽

library djang 必須 go import emp 使用 rar 模板 filter 自定義標簽與過濾器 在settings中的INSTALLED_APPS配置當前app,不然django無法找到自定義的simple_tag. 在app中創建templateta

【APACHE MINA2.0開發二】定義實現SERVER/CLIENT端的編解碼工廠(定義編碼與解碼器)!

在上一篇博文中已經簡單介紹過“過濾器”的概念,那麼在Mina 中的協議編解碼器通過過濾器 ProtocolCodecFilter 構造,這個過濾器的構造方法需 要一個 ProtocolCodecFactory,這從前面註冊 TextLineCodecFactory 的程式碼就可以看出來。 Protoc

Caffe中使用 DIGITS定義Python

 注意:包含Python層的網路只支援單個GPU訓練!!!!!   Caffe 使得我們有了使用Python自定義層的能力,而不是通常的C++/CUDA。這是一個非常有用的特性,但它的文件記錄不足,難以正確實現本演練將向您展示如何使用DIGHT來學習實現Python層。 注意:這個特性(自定義python層

【我的Android進階旅】定義控制元件使用ViewPager實現可以預覽的畫廊效果,並且定義畫面切換的動畫效果的切換時間

我們來看下效果 在這裡,我們實現的是,一個ViewPager來顯示圖片列表。這裡一個頁面,ViewPage展示了前後的預覽,我們讓預覽頁進行Y軸的壓縮,並設定透明度為0.5f,所有我們看到gif最後,左右兩邊的圖片有點朦朧感。讓預覽頁和主頁面有主從感。我們用分

我的Android進階旅------>Android定義View來實現解析lrc歌詞並同步滾動、上下拖動、縮放歌詞的功能

前言 最近有個專案有關於播放音樂時候,關於歌詞有以下幾個功能: 1、實現歌詞同步滾動的功能,即歌曲播放到哪句歌詞,就高亮地顯示出正在播放的這個歌詞; 2、實現上下拖動歌詞時候,可以拖動播放器的進度。即可以不停地上下拖動歌詞,

使用caffe的python layer定義資料增強DataAugmentationLayer

專案地址:https://github.com/zhongqianli/caffe_python_layer caffe自定義網路層的一種方式是使用python layer,這種方式需要使用pycaffe執行,命令列的方式執行會報錯。 編寫DataAugmentationLayer

AndroidToolBar和定義ToolBar實現沉浸式狀態列

沉浸式狀態列確切的說應該叫做透明狀態列。一般情況下,狀態列的底色都為黑色,而沉浸式狀態列則是把狀態列設定為透明或者半透明。 沉浸式狀態列是從android Kitkat(Android 4.4)開始出

Json反序列化ObjectMapper(定義實現反序列化方法)

     對於伺服器端開發人員而言,呼叫第三方介面獲取資料,將其“代理”轉化並返給客戶端幾乎是家常便飯的事兒。    一般情況下,第三方介面返回的資料型別是json格式,而伺服器開發人員則需將json格式的資料轉換成物件,繼而對其進行處理並封裝,以返回給客戶端。  

js學習心得js的定義事件-基於觀察者模式的實現

GOF對觀察者模式的定義:Observer的意圖是定義物件之間的一種一(被觀察者)對多(觀察者)的關係,當一個物件的狀態發生改變時,所有依賴它的物件得到通知,並且會自動更新自己。 從這段經典的定義中,可以推測下,觀察者模式中的倆個物件各自應該擁有的特徵 1,被觀察者應該可以

我的Android進階旅------>Android定義View實現帶數字的進度條(NumberProgressBar)

今天在Github上面看到一個來自於 daimajia所寫的關於Android自定義View實現帶數字的進度條(NumberProgressBar)的精彩案例,在這裡分享給大家一起來學習學習!同時感謝daimajia的開源奉獻! 第一步、效果展

定義實現字符串string的接口

初始 定義類 per code enter public 自定義 truct this 用char*管理String類的內存,new動態分配,在析構函數中delete char*指向的new出來的內存,一個string類需要實現那些接口可參考標準庫裏的string: ht

vector的定義實現

cnblogs logs name 成員變量 tor first ont const 技術 1 #pragma warning(disable:4996) 2 #include<iostream> 3 #include<string>

Vue徹底理解定義組件的v-model

自動 value tro 需要 this 變量 mode type 自定義 最近在學習vue,今天看到自定義事件的表單輸入組件,糾結了一會會然後恍然大悟...官方教程寫得不是很詳細,所以我決定總結一下。 v-model語法糖 v-model實現了表單輸入的雙向綁定,我們

定義實現復選框

列表 setw 屬於 隱藏 對象 p12 時間 下拉框 tar 項目中需要用到復選框,而QComboBox只能實現單選操作。即使是加以改造可以多選,也只能一次選擇一個選項,不符合項目需求。於是就花了兩天時間來自己實現一個可行的復選框。 實現方案:QLineEdit + QL

python路_定義forms組件

char deepcopy clean copy 所有 ges div object 失敗 import re import copy class ValidateError(Exception): def __init__(self,detail):

定義實現Map類

text PE value lse [] rgs ext per ati 1 package text; 2 3 public class SxtMap001{ 4 SxtEntry[] arr = new SxtEntry[990]; 5 i

HashMap的定義實現

map() obj static void 定義 [] ram OS ava 一、背景:           HashMap到底是怎麽實現的? 一對一對的存放,通過key找value;map的鍵不能重復;自己怎麽實現呢? 代碼: Wife.java 輔助類 pa

Flask中的session ,定義實現 session機制, 和 flask-session組件

time 基礎 如何 password pyc class 原理 less pan session 是基於cookie實現, 保存在服務端的鍵值對(形式為 {隨機字符串:‘xxxxxx’}), 同時在瀏覽器中的cookie中也對應一相同的隨機字符串,