【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

 完,


 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
需要学习Windows系统YOLOv4的同学请前往《Windows版YOLOv4目标检测实战:原理与源码解析》,课程链接 https://edu.csdn.net/course/detail/29865【为什么要学习这门课】 Linux创始人Linus Torvalds有一句名言:Talk is cheap. Show me the code. 冗谈不够,放码过来!  代码阅读是从基础到提高的必由之路。尤其对深度学习,许多框架隐藏了神经网络底层的实现,只能在上层调包使用,对其内部原理很难认识清晰,不利于进一步优化和创新。YOLOv4是最近推出的基于深度学习的端到端实时目标检测方法。YOLOv4的实现darknet是使用C语言开发的轻型开源深度学习框架,依赖少,可移植性好,可以作为很好的代码阅读案例,让我们深入探究其实现原理。【课程内容与收获】 本课程将解析YOLOv4的实现原理和源码,具体内容包括:- YOLOv4目标检测原理- 神经网络及darknet的C语言实现,尤其是反向传播的梯度求解和误差计算- 代码阅读工具及方法- 深度学习计算的利器:BLAS和GEMM- GPU的CUDA编程方法及在darknet的应用- YOLOv4的程序流程- YOLOv4各层及关键技术的源码解析本课程将提供注释后的darknet源码程序文件。【相关课程】 除本课程《YOLOv4目标检测:原理与源码解析》外,本人推出了有关YOLOv4目标检测的系列课程,包括:《YOLOv4目标检测实战:训练自己的数据集》《YOLOv4-tiny目标检测实战:训练自己的数据集》《YOLOv4目标检测实战:人脸口罩佩戴检测》《YOLOv4目标检测实战:中国交通标志识别》建议先学习一门YOLOv4实战课程,对YOLOv4的使用方法了解以后再学习本课程。【YOLOv4网络模型架构图】 下图由白勇老师绘制  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值