gctl_ai/lib/dnn/olayer_binaryentropy.cpp
2024-09-10 20:15:33 +08:00

160 lines
5.5 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/********************************************************
* ██████╗ ██████╗████████╗██╗
* ██╔════╝ ██╔════╝╚══██╔══╝██║
* ██║ ███╗██║ ██║ ██║
* ██║ ██║██║ ██║ ██║
* ╚██████╔╝╚██████╗ ██║ ███████╗
* ╚═════╝ ╚═════╝ ╚═╝ ╚══════╝
* Geophysical Computational Tools & Library (GCTL)
*
* Copyright (c) 2022 Yi Zhang (yizhang-geo@zju.edu.cn)
*
* GCTL is distributed under a dual licensing scheme. You can redistribute
* it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 2
* of the License, or (at your option) any later version. You should have
* received a copy of the GNU Lesser General Public License along with this
* program. If not, see <http://www.gnu.org/licenses/>.
*
* If the terms and conditions of the LGPL v.2. would prevent you from using
* the GCTL, please consider the option to obtain a commercial license for a
* fee. These licenses are offered by the GCTL's original author. As a rule,
* licenses are provided "as-is", unlimited in time for a one time fee. Please
* send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget
* to include some description of your company and the realm of its activities.
* Also add information on how to contact you by electronic and paper mail.
******************************************************/
#include "olayer_binaryentropy.h"
gctl::binary_entropy::binary_entropy() {}
gctl::binary_entropy::~binary_entropy() {}
void gctl::binary_entropy::check_target_data(const matrix<double> &target)
{
int nobs = target.col_size();
int ncls = target.row_size();
// Each element should be either 0 or 1
for (size_t i = 0; i < nobs; i++)
{
for (size_t j = 0; j < ncls; j++)
{
if (fabs(target[j][i] - 1.0) > 1e-6 && fabs(target[j][i]) > 1e-6)
{
throw std::invalid_argument("[gctl::binary_entropy] Target data should only contain zero or one.");
}
}
}
return;
}
void gctl::binary_entropy::check_target_data(const array<int> &target)
{
int nobs = target.size();
for (size_t i = 0; i < nobs; i++)
{
if (target[i] != 0 && target[i] != 1)
{
throw std::invalid_argument("[gctl::binary_entropy] Target data should only contain zero or one.");
}
}
return;
}
void gctl::binary_entropy::evaluation(const matrix<double> &prev_layer_data, const matrix<double> &target)
{
int nobs = prev_layer_data.col_size();
int ncls = prev_layer_data.row_size();
if (target.col_size() != nobs || target.row_size() != ncls)
{
throw std::invalid_argument("[gctl::binary_entropy] Target data have incorrect dimension.");
}
// Compute the derivative of the input of this layer
// L = -y * log(phat) - (1 - y) * log(1 - phat)
// in = phat
// dL / din = -y / phat + (1 - y) / (1 - phat), y is either 0 or 1
der_in_.resize(ncls, nobs);
int i, j;
#pragma omp parallel for private (i, j) schedule(guided)
for (i = 0; i < ncls; i++)
{
for (j = 0; j < nobs; j++)
{
der_in_[i][j] = -1.0*target[i][j]/prev_layer_data[i][j] + (1.0 - target[i][j])/(1.0 - prev_layer_data[i][j]);
}
}
return;
}
void gctl::binary_entropy::evaluation(const matrix<double> &prev_layer_data, const array<int> &target)
{
// target is a vector of class labels that take values from [0, 1, ..., nclass - 1]
// The i-th element of target is the class label for observation i
int nobs = prev_layer_data.col_size();
int ncls = prev_layer_data.row_size();
if (ncls != 1)
{
throw std::invalid_argument("[gctl::binary_entropy] Only one response variable is allowed when class labels are used as target data.");
}
if (target.size() != nobs)
{
throw std::invalid_argument("[gctl::binary_entropy] Target data have incorrect dimension.");
}
// Compute the derivative of the input of this layer
// L = -log(phat[y])
// in = phat
// d(L) / d(in) = [0, 0, ..., -1/phat[y], 0, ..., 0]
der_in_.resize(ncls, nobs);
der_in_.assign_all(0.0);
int j;
#pragma omp parallel for private (j) schedule(guided)
for (j = 0; j < nobs; j++)
{
der_in_[0][j] = -1.0*target[j]/prev_layer_data[0][j] + (1.0 - target[j])/(1.0 - prev_layer_data[0][j]);
}
return;
}
double gctl::binary_entropy::loss_value() const
{
// L = -y * log(phat) - (1 - y) * log(1 - phat)
// y = 0 => L = -log(1 - phat)
// y = 1 => L = -log(phat)
// m_din contains 1/(1 - phat) if y = 0, and -1/phat if y = 1, so
// L = log(abs(m_din)).sum()
double res = 0.0;
int nobs = der_in_.col_size();
int ncls = der_in_.row_size();
int i, j;
//#pragma omp parallel for private (i, j) schedule(guided)
for (i = 0; i < ncls; i++)
{
for (j = 0; j < nobs; j++)
{
res += std::log(fabs(der_in_[i][j]));
}
}
return res/der_in_.col_size();
}
std::string gctl::binary_entropy::get_output_name() const
{
return "BinaryClassEntropy";
}
gctl::olayer_type_e gctl::binary_entropy::get_output_type() const
{
return BinaryClassEntropy;
}