小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

caffe 源碼分析[三]:Euclidean loss layer

 印度阿三17 2018-09-28

以下是Euclidean loss layer的代碼分析,轉(zhuǎn)自:

https://blog.csdn.net/seashell_9/article/details/68064294

一. 前向函數(shù)

template <typename Dtype>
void EuclideanLossLayer<Dtype>::Forward_gpu(const vector<Blob<Dtype>*>& bottom,
    const vector<Blob<Dtype>*>& top) {
  int count = bottom[0]->count(); //這里的count就是你的batchsize的大小
  caffe_gpu_sub(
      count,                 
      bottom[0]->gpu_data(), //網(wǎng)絡(luò)的輸出值
      bottom[1]->gpu_data(), //標(biāo)簽值
      diff_.mutable_gpu_data());//存儲(chǔ)bottom[0] - bottom[1]
  Dtype dot;
  caffe_gpu_dot(count, diff_.gpu_data(), diff_.gpu_data(), &dot);//做點(diǎn)乘運(yùn)算
  Dtype loss = dot / bottom[0]->num() / Dtype(2); //除以總數(shù)再除以2
  top[0]->mutable_cpu_data()[0] = loss; //將loss值賦給輸出
}

1.Euclidean loss函數(shù)

首先明確caffe中Euclidean loss函數(shù):(多除了個(gè)2,方便后面求梯度時(shí)剛好約掉)

其次是代碼里面一些變量的意義:

count: count其實(shí)等于num*channels*height*width,也就是整個(gè)Blob元素的數(shù)量,但是因?yàn)榇藢又衏hannels height width都為1,所以這里的count()與num()實(shí)際上是相等的,都代表輸入圖片的數(shù)量,也就是batchsize的大小,也即公式里的N

bottom[0]->gpu_data(): 網(wǎng)絡(luò)的輸出值,注意這個(gè)變量并不只是單個(gè)的輸出值,而是包含整個(gè)batchsize每張圖片的輸出值,也就是一個(gè)含有N個(gè)元素的向量

bottom[1]->gpu_data():?真實(shí)標(biāo)簽值,也是個(gè)含有N個(gè)元素的向量

diff_.mutable_gpu_data(): 上述兩個(gè)向量做元素減法得到的向量。這里說(shuō)明一下為什么是diff_.mutable_gpu_data()而不是diff_.gpu_data(),因?yàn)閏affe定義了gpu_data()??為只讀變量,而mutable_gpu_data()為可變變量,也就是說(shuō)讀操作用gpu_data(),寫操作用mutable_gpu_data()

dot: 對(duì)diff_.gpu_data()進(jìn)行點(diǎn)乘運(yùn)算得到的值(點(diǎn)乘運(yùn)算得到的是一個(gè)數(shù)值)

top[0]->mutable_cpu_data()[0]:該層的輸出給下一層的變量。這里top[0]的“0”指的是第一個(gè)輸出值(就像上面bottom[0]指第一個(gè)輸入值,bottom[1]指第二個(gè)輸入值),由于這個(gè)層只有一個(gè)輸出值,因此也就只有0這個(gè)索引

2.caffe_gpu_sub()函數(shù)

在math_function.cu里可以查到caffe_gpu_sub()函數(shù),如下:

template <>
void caffe_gpu_sub<float>(const int N, const float* a, const float* b,
    float* y) {
  // NOLINT_NEXT_LINE(whitespace/operators)
  sub_kernel<float><<<CAFFE_GET_BLOCKS(N), CAFFE_CUDA_NUM_THREADS>>>(
      N, a, b, y);
}

可以看到,該函數(shù)又調(diào)用了sub_kernel函數(shù):

template <typename Dtype>
__global__ void sub_kernel(const int n, const Dtype* a,
    const Dtype* b, Dtype* y) {
  CUDA_KERNEL_LOOP(index, n) {
    y[index] = a[index] - b[index];
  }
}

從這個(gè)函數(shù)就可以明白了,caffe_gpu_sub()就是做了這樣一個(gè)運(yùn)算:把a(bǔ)向量和b向量對(duì)應(yīng)元素相減,然后賦給y向量。放到我們Euclidean loss代碼里面,也就是bottom[0]->gpu_data()(網(wǎng)絡(luò)輸出值)和bottom[1]->gpu_data()(真實(shí)標(biāo)簽值)做對(duì)應(yīng)元素相減,然后賦給diff_.mutable_gpu_data()
3.caffe_gpu_dot()函數(shù):

template <>
void caffe_gpu_dot<float>(const int n, const float* x, const float* y,
    float* out) {
  CUBLAS_CHECK(cublasSdot(Caffe::cublas_handle(), n, x, 1, y, 1, out));
}

可以看到調(diào)用了cublasSdot()函數(shù),在cuda文檔中看到該函數(shù)的作用:

即計(jì)算兩個(gè)輸入向量的點(diǎn)乘。最后就是除以2N了

二.反向函數(shù)

template <typename Dtype>
void EuclideanLossLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,
    const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
  for (int i = 0; i < 2;   i) {
    if (propagate_down[i]) {    //對(duì)于輸入的第i個(gè)Blob propagate_dowm 為1(該變量即為該Blob輸入后是否要向前面的層提供反向傳播的梯度)
      const Dtype sign = (i == 0) ? 1 : -1; 
      const Dtype alpha = sign * top[0]->cpu_diff()[0] / bottom[i]->num();
      caffe_gpu_axpby(
          bottom[i]->count(),              // count
          alpha,                              // alpha
          diff_.gpu_data(),                   // a
          Dtype(0),                           // beta
          bottom[i]->mutable_gpu_diff());  // b
    }
  }
}

1.因?yàn)閘oss層沒(méi)有參數(shù),所以求導(dǎo)時(shí)是對(duì)兩個(gè)輸入求偏導(dǎo),即:

Derive (X1) = [(x11-x21) … (x1n-x2n)]/N

Derive (X2) =?-[(x11-x21) … (x1n-x2n)]/N(注意前面有個(gè)負(fù)號(hào))

所以代碼中for循環(huán)兩次就是分別對(duì)X1和X2求偏導(dǎo)

2.propagate_down[i]:這里兩個(gè)propagate_down都是1的。如果要了解propagate_down的含義,可以看來(lái)自百度的一個(gè)描述:

caffe中怎么固定前面的網(wǎng)絡(luò)參數(shù),訓(xùn)練后面層的參數(shù)??
這里面就用到了propagate_down,?有兩種情況:比如有4個(gè)全連接層A->B->C->D?
a. 你希望C層的參數(shù)不會(huì)改變,C前面的AB層的參數(shù)也不會(huì)改變,這種情況也就是D層的梯度不往前反向傳播到D層的輸入blob(也就是C層的輸出blob 沒(méi)有得到梯度),你可以通過(guò)設(shè)置D層的propagate_down為false來(lái)做到。?

propagate_down的數(shù)量與輸入blob的數(shù)量相同,假如你某個(gè)層有2個(gè)輸入blob,那么你應(yīng)該在該layer的Param里面寫上兩行:?

propagate_down : 0 # 第1個(gè)輸入blob不會(huì)得到反向傳播的梯度?
propagate_down : 0 # 第2個(gè)輸入blob不會(huì)得到反向傳播的梯度?
這樣的話,你這個(gè)layer的梯度就不會(huì)反向傳播啦,前面的所有l(wèi)ayer的參數(shù)也就不會(huì)改變了?
b. 你希望C層的參數(shù)不會(huì)改變,但是C前面的AB層的參數(shù)會(huì)改變,這種情況,只是固定了C層的參數(shù),C層得到的梯度依然會(huì)反向傳播給前面的B層。只需要將對(duì)應(yīng)的參數(shù)blob的學(xué)習(xí)率調(diào)整為0:?

layer { 
type: "InnerProduct" 
    param { # 對(duì)應(yīng)第1個(gè)參數(shù)blob的配置,也就是全連接層的參數(shù)矩陣的配置 
         lr_mult: 0 # 學(xué)習(xí)率為0,其他參數(shù)可以看caffe.proto里面的ParamSpec這個(gè)類型 
    } 
    param { # 對(duì)應(yīng)第2個(gè)參數(shù)blob的配置,也就是全連接層的偏置項(xiàng)的配置 
        lr_mult: 0 # 學(xué)習(xí)率為0 
    } 
} 

3.sign的作用:

? ?第一次求偏導(dǎo)是對(duì)X1,前面不需要加負(fù)號(hào);第二次求偏導(dǎo)是對(duì)X2,前面需要乘-1

4.top[0]->cpu_diff()[0]:

在反向傳播中,top代表從高一層反向傳過(guò)來(lái)的變量,所以top[0]->cpu_diff()表示從高一層傳過(guò)來(lái)的error。但問(wèn)題來(lái)了,這明明是loss層,也就是最后一層,為什么還有所謂的再高一層呢?其實(shí)大家可以發(fā)現(xiàn),這里用的是top[0]->cpu_diff()[0],而不是top[0]->cpu_diff()。caffe中反向傳給低層error時(shí)其實(shí)用戶還可以給這個(gè)error乘以一個(gè)倍數(shù),這個(gè)倍數(shù)就存儲(chǔ)在top[0]->cpu_diff()的第一個(gè)元素,也就是top[0]->cpu_diff()[0]。而用戶設(shè)置這個(gè)倍數(shù)則是通過(guò)在layer參數(shù)中添加loss_weight參數(shù)

layer {
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "pred"
  bottom: "label"
  top: "loss"
  loss_weight: 1
}

默認(rèn)的loss_weight都是1

參見(jiàn):http:///questions/31099233/euclidean-loss-layer-in-caffe

5.caffe_gpu_axpby()函數(shù):

template <>
void caffe_gpu_axpby<float>(const int N, const float alpha, const float* X,
    const float beta, float* Y) {
  caffe_gpu_scal<float>(N, beta, Y); // Y = beta*Y
  caffe_gpu_axpy<float>(N, alpha, X, Y);// Y = Y   alpha*X
}

做的運(yùn)算其實(shí)就是bottom[i]->mutable_gpu_diff() = alpha*diff.gpu_data() beta*bottom[i]->mutable_gpu_diff() =?alpha*diff.gpu_data() (因?yàn)閎eta = 0)
?

來(lái)源:http://www./content-1-27551.html

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多