本系列爲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
完,