【darknet源碼解析-27】l2norm_layer.h 與 l2norm_layer.c 解析

本系列爲darknet源碼解析,本次解析爲src/l2norm_layer.h 和 src/l2norm_layer.c 兩個,l2norm_layer主要是完成在每個batch對每個通道進行l2標準化操作;

正向傳播:

y_i=\frac{x_i}{\sqrt{\sum_{i=1}^{D}x_i^2}}

反向傳播:

\frac{\partial y_j}{\partial x_i}=\frac{\partial }{\partial x_i}\left [ x_j \left( \sum_{k=1}^{D}x_k^2 \right)^{- \frac{1}{2}} \right ]

       =\frac{\partial x_j}{\partial x_i} \left( \sum_{k=1}^{D}x_k^2 \right)^{- \frac{1}{2}} - x_j \left( \sum_{k=1}^{D}x_k^2 \right)^{- \frac{3}{2}}\sum_{k=1}^{D}x_k\frac{\partial x_k}{\partial x_i}

       =\delta_{ij}\left( \sum_{k=1}^{D}x_k^2 \right)^{- \frac{1}{2}}-x_j\left( \sum_{k=1}^{D}x_k^2 \right)^{- \frac{3}{2}}x_i

       =\left( \sum_{k=1}^{D}x_k^2 \right)^{- \frac{1}{2}} \left( \delta_{ij}-y_jy_i \right )

如果i=j,則\delta_{ij}=1, 否則 \delta_{ij}=0,這與源碼上的反傳存在差入;

l2norm_layer.h的詳細定義如下:

#ifndef L2NORM_LAYER_H
#define L2NORM_LAYER_H
#include "layer.h"
#include "network.h"

// 構建l2標準化層
layer make_l2norm_layer(int batch, int inputs);
// l2標準化層的前向,反向傳播函數
void forward_l2norm_layer(const layer l, network net);
void backward_l2norm_layer(const layer l, network net);

#ifdef GPU
void forward_l2norm_layer_gpu(const layer l, network net);
void backward_l2norm_layer_gpu(const layer l, network net);
#endif

#endif

l2norm_layer.c 的詳細解析

#include "l2norm_layer.h"
#include "activations.h"
#include "blas.h"
#include "cuda.h"

#include <float.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

/**
 * 構造l2標準化層
 * @param batch
 * @param inputs
 * @return
 */
layer make_l2norm_layer(int batch, int inputs)
{
    fprintf(stderr, "l2norm                                         %4d\n",  inputs);
    layer l = {0};
    l.type = L2NORM; // 層類別
    l.batch = batch; // 一個batch包含圖片的張數
    l.inputs = inputs; // l2norm_layer層一張輸入圖片元素的個數
    l.outputs = inputs; // l2norm_layer層對應輸入圖片的輸出元素的個數,
    l.output = calloc(inputs*batch, sizeof(float)); // l2norm_layer 所有輸出(包含整個batch的)
    l.scales = calloc(inputs*batch, sizeof(float)); //
    l.delta = calloc(inputs*batch, sizeof(float)); // l2norm_layer 誤差項(包含整個batch的)

    l.forward = forward_l2norm_layer; // l2norm_layer 前向傳播
    l.backward = backward_l2norm_layer; // l2norm_layer 反向傳播
    #ifdef GPU
    l.forward_gpu = forward_l2norm_layer_gpu;
    l.backward_gpu = backward_l2norm_layer_gpu;

    l.output_gpu = cuda_make_array(l.output, inputs*batch); 
    l.scales_gpu = cuda_make_array(l.output, inputs*batch); 
    l.delta_gpu = cuda_make_array(l.delta, inputs*batch); 
    #endif
    return l;
}


// l2normalize_cpu(l.output, l.scales, l.batch, l.out_c, l.out_w*l.out_h);
void l2normalize_cpu(float *x, float *dx, int batch, int filters, int spatial)
{
    int b,f,i;
    for(b = 0; b < batch; ++b){ //遍歷每一張圖片
        for(i = 0; i < spatial; ++i){ //遍歷每一個通道上的所有元素
            float sum = 0;
            for(f = 0; f < filters; ++f){ // 遍歷所有通道
                int index = b*filters*spatial + f*spatial + i;
                sum += powf(x[index], 2); // 計算每個batch中,每個像素點所有通道上平方和
            }
            sum = sqrtf(sum);//計算二範數
            for(f = 0; f < filters; ++f){
                int index = b*filters*spatial + f*spatial + i;
                x[index] /= sum; //對每個元素進行l2標準化
                dx[index] = (1 - x[index]) / sum; // 計算反傳梯度
            }
        }
    }
}

/**
 * l2norm前向傳播函數
 * @param l 當前l2norm層
 * @param net 整個網絡
 */
void forward_l2norm_layer(const layer l, network net)
{
    // l.output = net.input 初始化操作
    copy_cpu(l.outputs*l.batch, net.input, 1, l.output, 1);
    l2normalize_cpu(l.output, l.scales, l.batch, l.out_c, l.out_w*l.out_h);
}

void backward_l2norm_layer(const layer l, network net)
{
    // l.delta = l.scales 多出一個l.scales奇葩;
    axpy_cpu(l.inputs*l.batch, 1, l.scales, 1, l.delta, 1);
    // net.data = l.delta
    axpy_cpu(l.inputs*l.batch, 1, l.delta, 1, net.delta, 1);
}

#ifdef GPU

void forward_l2norm_layer_gpu(const layer l, network net)
{
    copy_gpu(l.outputs*l.batch, net.input_gpu, 1, l.output_gpu, 1);
    l2normalize_gpu(l.output_gpu, l.scales_gpu, l.batch, l.out_c, l.out_w*l.out_h);
}

void backward_l2norm_layer_gpu(const layer l, network net)
{
    axpy_gpu(l.batch*l.inputs, 1, l.scales_gpu, 1, l.delta_gpu, 1);
    axpy_gpu(l.batch*l.inputs, 1, l.delta_gpu, 1, net.delta_gpu, 1);
}

#endif

完,

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章