天天看點

【darknet源碼解析-26】upsample_layer.h 與 upsample_layer.c 解析

本系列為darknet源碼解析,本次解析為src/upsample_layer.h 和 src/upsample_layer.c 兩個,sample_layer主要是建構上采樣層, upsample_layer使用于yolo v3中;

upsample_layer.h 解析如下:

#ifndef UPSAMPLE_LAYER_H
#define UPSAMPLE_LAYER_H
#include "darknet.h"

// 構造上采樣層
layer make_upsample_layer(int batch, int w, int h, int c, int stride);
// 上采樣層的前向,反向傳播函數
void forward_upsample_layer(const layer l, network net);
void backward_upsample_layer(const layer l, network net);
void resize_upsample_layer(layer *l, int w, int h);

#ifdef GPU
void forward_upsample_layer_gpu(const layer l, network net);
void backward_upsample_layer_gpu(const layer l, network net);
#endif

#endif
           

下面一圖解析如何進行上采樣操作,輸入特征圖為3*3,通道數為1,上采樣步幅為2,scale為1,前向操作與反向操作如下圖:

【darknet源碼解析-26】upsample_layer.h 與 upsample_layer.c 解析

upsample_layer.c的詳細解析如下:

#include "upsample_layer.h"
#include "cuda.h"
#include "blas.h"

#include <stdio.h>

/**
 * 構造上采樣層
 * @param batch 每個batch含有的圖檔數量
 * @param w 圖檔高度
 * @param h 圖檔寬度
 * @param c 輸入圖像的Channel數量
 * @param stride 步幅
 * @return
 */
layer make_upsample_layer(int batch, int w, int h, int c, int stride)
{
    layer l = {0};
    l.type = UPSAMPLE; //層類别
    l.batch = batch; // batch大小
    l.w = w; //輸入特征圖的寬度
    l.h = h; //輸入特征圖的高度
    l.c = c; //輸入特征圖的通道數
    l.out_w = w*stride; //輸出特征圖的寬度
    l.out_h = h*stride; //輸出特征圖的高度
    l.out_c = c; //輸出特征圖的通道數
    if(stride < 0){ // 步幅小于0,想當于卷積操作
        stride = -stride;
        l.reverse=1;
        l.out_w = w/stride; //輸出特征圖的寬度
        l.out_h = h/stride; //輸出特征圖的高度
    }
    l.stride = stride; //步幅
    l.outputs = l.out_w*l.out_h*l.out_c; // 上采樣層對應一張輸入圖檔輸出元素個數
    l.inputs = l.w*l.h*l.c; // 上采樣層一張輸入圖檔的元素個數
    l.delta =  calloc(l.outputs*batch, sizeof(float)); // 上采樣層誤差項(包含整個batch的)
    l.output = calloc(l.outputs*batch, sizeof(float)); // 上采樣層所有輸出(包含整個batch的)

    l.forward = forward_upsample_layer; // 上采樣層的前向傳播
    l.backward = backward_upsample_layer; //上采樣層的反向傳播
    #ifdef GPU
    l.forward_gpu = forward_upsample_layer_gpu;
    l.backward_gpu = backward_upsample_layer_gpu;

    l.delta_gpu =  cuda_make_array(l.delta, l.outputs*batch);
    l.output_gpu = cuda_make_array(l.output, l.outputs*batch);
    #endif
    if(l.reverse) fprintf(stderr, "downsample         %2dx  %4d x%4d x%4d   ->  %4d x%4d x%4d\n", stride, w, h, c, l.out_w, l.out_h, l.out_c);
    else fprintf(stderr, "upsample           %2dx  %4d x%4d x%4d   ->  %4d x%4d x%4d\n", stride, w, h, c, l.out_w, l.out_h, l.out_c);
    return l;
}

void resize_upsample_layer(layer *l, int w, int h)
{
    l->w = w;
    l->h = h;
    l->out_w = w*l->stride;
    l->out_h = h*l->stride;
    if(l->reverse){
        l->out_w = w/l->stride;
        l->out_h = h/l->stride;
    }
    l->outputs = l->out_w*l->out_h*l->out_c;
    l->inputs = l->h*l->w*l->c;
    l->delta =  realloc(l->delta, l->outputs*l->batch*sizeof(float));
    l->output = realloc(l->output, l->outputs*l->batch*sizeof(float));

#ifdef GPU
    cuda_free(l->output_gpu);
    cuda_free(l->delta_gpu);
    l->output_gpu  = cuda_make_array(l->output, l->outputs*l->batch);
    l->delta_gpu   = cuda_make_array(l->delta,  l->outputs*l->batch);
#endif
    
}

// upsample_cpu(net.input, l.w, l.h, l.c, l.batch, l.stride, 1, l.scale, l.output);
void upsample_cpu(float *in, int w, int h, int c, int batch, int stride, int forward, float scale, float *out)
{
    int i, j, k, b;
    for(b = 0; b < batch; ++b){
        for(k = 0; k < c; ++k){
            for(j = 0; j < h*stride; ++j){
                for(i = 0; i < w*stride; ++i){
                    int in_index = b*w*h*c + k*w*h + (j/stride)*w + i/stride; //輸入特征圖的位置
                    int out_index = b*w*h*c*stride*stride + k*w*h*stride*stride + j*w*stride + i; //輸出特征圖的位置
                    if(forward) out[out_index] = scale*in[in_index];// scale在cfg指定或者為預設值
                    else in[in_index] += scale*out[out_index]; //反轉
                }
            }
        }
    }
}


/**
 * 上采樣層的前向傳播
 * @param l 目前上采樣層
 * @param net 整個網絡
 */
void forward_upsample_layer(const layer l, network net)
{
    // l.output = 0, 初始化為0
    fill_cpu(l.outputs*l.batch, 0, l.output, 1);
    if(l.reverse){ //下采樣操作
        upsample_cpu(l.output, l.out_w, l.out_h, l.c, l.batch, l.stride, 0, l.scale, net.input);
    }else{// 上采樣操作
        upsample_cpu(net.input, l.w, l.h, l.c, l.batch, l.stride, 1, l.scale, l.output);
    }
}

/**
 * 上采樣層的反向傳播函數
 * @param l 目前上采樣層
 * @param net 整個卷積層
 */
void backward_upsample_layer(const layer l, network net)
{
    if(l.reverse){ //下采樣操作的反向傳播
        upsample_cpu(l.delta, l.out_w, l.out_h, l.c, l.batch, l.stride, 1, l.scale, net.delta);
    }else{ //上采樣操作的反向傳播
        upsample_cpu(net.delta, l.w, l.h, l.c, l.batch, l.stride, 0, l.scale, l.delta);
    }
}

#ifdef GPU
void forward_upsample_layer_gpu(const layer l, network net)
{
    fill_gpu(l.outputs*l.batch, 0, l.output_gpu, 1);
    if(l.reverse){
        upsample_gpu(l.output_gpu, l.out_w, l.out_h, l.c, l.batch, l.stride, 0, l.scale, net.input_gpu);
    }else{
        upsample_gpu(net.input_gpu, l.w, l.h, l.c, l.batch, l.stride, 1, l.scale, l.output_gpu);
    }
}

void backward_upsample_layer_gpu(const layer l, network net)
{
    if(l.reverse){
        upsample_gpu(l.delta_gpu, l.out_w, l.out_h, l.c, l.batch, l.stride, 1, l.scale, net.delta_gpu);
    }else{
        upsample_gpu(net.delta_gpu, l.w, l.h, l.c, l.batch, l.stride, 0, l.scale, l.delta_gpu);
    }
}
#endif
           

 完,