本系列為darknet源碼解析,本次解析src/logistic_layer.h 與 src/logistic_layer.c 兩個。logistic_layer主要完成了邏輯回歸。
在這裡,我們推導一下梯度反傳,詳細請參看李航 《統計學習方法》一書。
設:
似然函數為:
對數似然函數為:
對
求極大值,就得到
的估計值。将其前邊添加負号,變為求極小值,就是我們的代價函數
求極小值,則乘上一個符号即可
logistic_layer.h 的解析如下:
#ifndef LOGISTIC_LAYER_H
#define LOGISTIC_LAYER_H
#include "layer.h"
#include "network.h"
// 邏輯回歸層構造函數
layer make_logistic_layer(int batch, int inputs);
// 邏輯回歸層前向,反向傳播函數
void forward_logistic_layer(const layer l, network net);
void backward_logistic_layer(const layer l, network net);
#ifdef GPU
void forward_logistic_layer_gpu(const layer l, network net);
void backward_logistic_layer_gpu(const layer l, network net);
#endif
#endif
logistic_layer.c 的解析如下:
#include "logistic_layer.h"
#include "activations.h"
#include "blas.h"
#include "cuda.h"
#include <float.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
/**
* 建構邏輯回歸層
* @param batch 一個batch中包含神經元的數量
* @param inputs 邏輯回歸層一張輸入圖檔的元素個數
* @return
*/
layer make_logistic_layer(int batch, int inputs)
{
fprintf(stderr, "logistic x entropy %4d\n", inputs);
layer l = {0};
l.type = LOGXENT; // 層類别
l.batch = batch; // 一個batch中圖檔的張數
l.inputs = inputs; // 邏輯回歸層一張輸入圖檔的元素個數
l.outputs = inputs; // 邏輯回歸層對應輸入圖檔的輸出元素個數,邏輯回歸層不改變輸入輸出的個數
l.loss = calloc(inputs*batch, sizeof(float)); // 邏輯回歸層所有損失(包含整個batch)
l.output = calloc(inputs*batch, sizeof(float)); // 邏輯回歸層所有輸出(包含整個batch)
l.delta = calloc(inputs*batch, sizeof(float)); // 邏輯回歸層誤差項(包含整個batch)
l.cost = calloc(1, sizeof(float)); // 邏輯回歸層的總損失
l.forward = forward_logistic_layer; // 邏輯回歸層前向傳播
l.backward = backward_logistic_layer; // 邏輯回歸層反向傳播
#ifdef GPU
l.forward_gpu = forward_logistic_layer_gpu;
l.backward_gpu = backward_logistic_layer_gpu;
l.output_gpu = cuda_make_array(l.output, inputs*batch);
l.loss_gpu = cuda_make_array(l.loss, inputs*batch);
l.delta_gpu = cuda_make_array(l.delta, inputs*batch);
#endif
return l;
}
// 逐元素做logistic計算
void logistic_x_ent_cpu(int n, float *pred, float *truth, float *delta, float *error)
{
int i;
for(i = 0; i < n; ++i){
float t = truth[i];
float p = pred[i];
error[i] = -t*log(p) - (1-t)*log(1-p); // 損失計算
delta[i] = t-p; // 每一項的誤差項
}
}
/**
* 邏輯回歸層前向傳播函數
* @param l 目前邏輯回歸層
* @param net 整個網絡
*/
void forward_logistic_layer(const layer l, network net)
{
// l.output = net.input
copy_cpu(l.outputs*l.batch, net.input, 1, l.output, 1);
// 利用sigmoid激活函數處理
activate_array(l.output, l.outputs*l.batch, LOGISTIC);
if(net.truth){
logistic_x_ent_cpu(l.batch*l.inputs, l.output, net.truth, l.delta, l.loss); // 逐元素做logistic計算
l.cost[0] = sum_array(l.loss, l.batch*l.inputs); // 計算目前邏輯回歸層損失總和
}
}
/**
* 邏輯回歸層反向傳播函數
* @param l 目前邏輯回歸層
* @param net 整個網絡
*/
void backward_logistic_layer(const layer l, network net)
{
// net.delta = l.delta
axpy_cpu(l.inputs*l.batch, 1, l.delta, 1, net.delta, 1);
}
#ifdef GPU
void forward_logistic_layer_gpu(const 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, LOGISTIC);
if(net.truth){
logistic_x_ent_gpu(l.batch*l.inputs, l.output_gpu, net.truth_gpu, l.delta_gpu, l.loss_gpu);
cuda_pull_array(l.loss_gpu, l.loss, l.batch*l.inputs);
l.cost[0] = sum_array(l.loss, l.batch*l.inputs);
}
}
void backward_logistic_layer_gpu(const layer l, network net)
{
axpy_gpu(l.batch*l.inputs, 1, l.delta_gpu, 1, net.delta_gpu, 1);
}
#endif
,