【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,前向操作与反向操作如下图:

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

 完,


 

发布了401 篇原创文章 · 获赞 39 · 访问量 45万+

猜你喜欢

转载自blog.csdn.net/caicaiatnbu/article/details/103732089