【darknet源碼解析-13】activation_layer.h 和 activation_layer.c 解析

本系列爲darknet源碼解析,本次解析src/activation_layer.h 和 src/activation_layer.c 兩個,activation_layer 主要用於構建激活函數層,完成激活函數的前向,梯度反向傳播。

關於激活函數的一層底層實現,請先閱讀【darknet源碼解析-07】activations.h 和 activations.c 解析

activation_layer.h 的解析如下:

#ifndef ACTIVATION_LAYER_H
#define ACTIVATION_LAYER_H

#include "activations.h"
#include "layer.h"
#include "network.h"

// 構建激活函數層
layer make_activation_layer(int batch, int inputs, ACTIVATION activation);

// 激活函數層前向傳播函數
void forward_activation_layer(layer l, network net);

// 激活函數層反向傳播函數
void backward_activation_layer(layer l, network net);

#ifdef GPU
void forward_activation_layer_gpu(layer l, network net);
void backward_activation_layer_gpu(layer l, network net);
#endif

#endif

activation_layer.c 的解析如下:

#include "activation_layer.h"
#include "utils.h"
#include "cuda.h"
#include "blas.h"
#include "gemm.h"

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
 * 構建激活函數層
 * @param batch 一個batch包含的圖片的張數
 * @param inputs  //
 * @param activation  //激活函數類型
 * @return
 */
layer make_activation_layer(int batch, int inputs, ACTIVATION activation)
{
    layer l = {0};
    l.type = ACTIVE;//層類別

    l.inputs = inputs; //激活函數層一張輸入圖片中所有元素個數
    l.outputs = inputs; //激活函數層對應一張輸入圖片的輸出元素個數,激活函數層不改變輸入輸出的個數
    l.batch=batch; //一個batch中圖片的張數

    l.output = calloc(batch*inputs, sizeof(float*)); // 激活函數層的所有輸出(包含整個batch的)
    l.delta = calloc(batch*inputs, sizeof(float*)); // 激活函數層的誤差損失項 (包含整個batch的)

    // 激活函數層的前向,反向傳播函數
    l.forward = forward_activation_layer;
    l.backward = backward_activation_layer;
#ifdef GPU
    l.forward_gpu = forward_activation_layer_gpu;
    l.backward_gpu = backward_activation_layer_gpu;

    l.output_gpu = cuda_make_array(l.output, inputs*batch);
    l.delta_gpu = cuda_make_array(l.delta, inputs*batch);
#endif
    l.activation = activation;
    fprintf(stderr, "Activation Layer: %d inputs\n", inputs);
    return l;
}


void copy_cpu(int N, float *X, int INCX, float *Y, int INCY)
{
    int i;
    for(i = 0; i < N; ++i) Y[i*INCY] = X[i*INCX];
}
// 計算激活函數的輸出值
void activate_array(float *x, const int n, const ACTIVATION a)
{
    int i;
    for(i = 0; i < n; ++i){
        x[i] = activate(x[i], a); // 調用不同的激活函數類型,調用不同的激活函數處理輸入
    }
}


/**
 * 激活函數層前向傳播函數
 * @param l 當前激活函數層
 * @param net 整個網絡
 */
void forward_activation_layer(layer l, network net)
{
    // l.output = net.input
    copy_cpu(l.outputs*l.batch, net.input, 1, l.output, 1);
    // 計算輸入經過激活函數的輸出值
    activate_array(l.output, l.outputs*l.batch, l.activation);
}

// 計算激活函數對加權輸入的導數,並乘以delta,得到當前層最終的誤差項delta
void gradient_array(const float *x, const int n, const ACTIVATION a, float *delta)
{
    int i;
    for(i = 0; i < n; ++i){
        delta[i] *= gradient(x[i], a);
    }
}
/**
 * 激活函數層反向傳播函數
 * @param l 當前激活函數層
 * @param net 整個網絡
 */
void backward_activation_layer(layer l, network net)
{
    // 計算激活函數對加權輸入的導數,並乘以delta,得到當前層最終的誤差項delta
    gradient_array(l.output, l.outputs*l.batch, l.activation, l.delta);
    // net.delta = l.delta 誤差項向前一層傳播,存入中轉位置 net.delta
    copy_cpu(l.outputs*l.batch, l.delta, 1, net.delta, 1);
}

#ifdef GPU

void forward_activation_layer_gpu(layer l, network net)
{
    copy_gpu(l.outputs*l.batch, net.input_gpu, 1, l.output_gpu, 1);
    activate_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation);
}

void backward_activation_layer_gpu(layer l, network net)
{
    gradient_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu);
    copy_gpu(l.outputs*l.batch, l.delta_gpu, 1, net.delta_gpu, 1);
}
#endif

完,

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