神经网络系列---损失函数



损失函数

神经网络中的损失函数用于衡量模型预测结果与实际标签之间的差异。选择适当的损失函数对于训练一个高效且准确的神经网络至关重要。以下是一些常见的神经网络损失函数:

  • 我们假设y_truey_pred都是二维向量,表示批量样本的真实标签预测结果

均方误差(Mean Squared Error,MSE):

用于回归问题,计算预测值与实际值之间的平方差的均值。

均方误差(Mean Squared Error,MSE)是常用的回归问题中的损失函数,用于衡量模型预测结果与真实标签之间的差异。对于回归问题,我们预测的是连续值,而不是分类标签。均方误差计算方法是将每个预测值与对应的真实值之差的平方求和,然后再取平均,得到最终的损失值。

假设我们有一个批量样本,其中每个样本的真实标签为 y_true,模型预测结果为 y_pred。样本数为 N,则均方误差公式如下:

MSE = 1 N ∑ i = 1 N ( y true , i − y pred , i ) 2 \text{MSE} = \frac{1}{N} \sum_{i=1}^{N} (y_{\text{true},i} - y_{\text{pred},i})^2 MSE=N1i=1N(ytrue,iypred,i)2

其中:

  • y true , i y_{\text{true},i} ytrue,i 是第 i 个样本的真实标签值。
  • y pred , i y_{\text{pred},i} ypred,i 是第 i 个样本的模型预测结果。

均方误差衡量的是预测值与真实值之间的距离的平方,并求平均值。该值越小,表示模型的预测结果越接近真实标签,即模型的性能越好。

要计算均方误差(Mean Squared Error,MSE)损失函数对模型预测结果 y_pred 的偏导数,我们需要使用链式法则(Chain Rule)来求解。在以下偏导数公式中,y_true 表示真实标签,y_pred 表示模型预测的结果。

对于第 i 个样本的预测结果 y_pred[i],均方误差损失函数对 y_pred[i] 的偏导数公式如下:

∂ MSE ∂ y pred [ i ] = 2 N ( y pred [ i ] − y true [ i ] ) \frac{\partial \text{MSE}}{\partial y_{\text{pred}[i]}} = \frac{2}{N} (y_{\text{pred}[i]} - y_{\text{true}[i]}) ypred[i]MSE=N2(ypred[i]ytrue[i])

其中:

  • N N N 是样本数(Batch Size)。
  • y true [ i ] y_{\text{true}[i]} ytrue[i] 是第 i 个样本的真实标签值。
  • y pred [ i ] y_{\text{pred}[i]} ypred[i] 是第 i 个样本的模型预测结果。

这个公式表示在均方误差损失函数中,对于每个样本的预测结果 y_pred[i],偏导数的值是预测结果与真实标签之差的2倍除以样本数。

注意,这个偏导数的计算是针对每个样本的预测结果 y_pred[i] 的,而不是整个批量样本的预测结果 y_pred

在实际训练过程中,我们通常将均方误差损失函数的偏导数与其他梯度一起用于反向传播算法,从而更新网络参数。这样可以通过梯度下降等优化算法来最小化均方误差损失,从而提高回归模型的准确性和性能。

   MSE = (1/n) * Σ(y_pred - y_true)^2
   
#include <cmath>

//单个数据
double meanSquaredError(double y_true, double y_pred) {
    return pow(y_true - y_pred, 2);
}

//多个数据
double batchMeanSquaredError(const std::vector<double>& y_true, const std::vector<double>& y_pred) {
    int batch_size = y_true.size();
    double loss = 0.0;

    for (int i = 0; i < batch_size; i++) {
        loss += pow(y_true[i] - y_pred[i], 2);
    }

    return loss / batch_size;
}

// 计算 MSE 损失函数对 y_pred 的偏导数
std::vector<double> mseLossDerivative(const std::vector<double>& y_true, const std::vector<double>& y_pred) {
    int size = y_true.size();
    std::vector<double> derivative(size);

    for (int i = 0; i < size; i++) {
        derivative[i] = 2 * (y_pred[i] - y_true[i]);
    }

    return derivative;
}


// 计算 MSE 损失函数对 y_pred 的偏导数
std::vector<std::vector<double>> batchMSELossDerivative(const std::vector<std::vector<double>>& y_true,
                                                        const std::vector<std::vector<double>>& y_pred) {
    int batch_size = y_true.size();
    int size = y_true[0].size();
    std::vector<std::vector<double>> derivative(batch_size, std::vector<double>(size, 0.0));

    for (int i = 0; i < batch_size; i++) {
        for (int j = 0; j < size; j++) {
            derivative[i][j] = 2 * (y_pred[i][j] - y_true[i][j]);
        }
    }

    return derivative;
}


平均绝对误差(Mean Absolute Error,MAE):

用于回归问题,计算预测值与实际值之间的绝对差的均值。

平均绝对误差(Mean Absolute Error,MAE)是常用的回归问题中的损失函数,用于衡量模型预测结果与真实标签之间的差异。对于回归问题,我们预测的是连续值,而不是分类标签。平均绝对误差计算方法是将每个预测值与对应的真实值之差的绝对值求和,然后再取平均,得到最终的损失值。

假设我们有一个批量样本,其中每个样本的真实标签为 y_true,模型预测结果为 y_pred。样本数为 N,则平均绝对误差公式如下:

MAE = 1 N ∑ i = 1 N ∣ y true , i − y pred , i ∣ \text{MAE} = \frac{1}{N} \sum_{i=1}^{N} \left| y_{\text{true},i} - y_{\text{pred},i} \right| MAE=N1i=1Nytrue,iypred,i

其中:

  • y true , i y_{\text{true},i} ytrue,i 是第 i 个样本的真实标签值。
  • y pred , i y_{\text{pred},i} ypred,i 是第 i 个样本的模型预测结果。

平均绝对误差衡量的是预测值与真实值之间的绝对距离,并求平均值。与均方误差相比,平均绝对误差更加鲁棒,因为它对异常值(离群点)不敏感。然而,均方误差在数学优化中更易处理,因为其导数计算较为简单。

要计算平均绝对误差(Mean Absolute Error,MAE)损失函数对模型预测结果 y_pred 的偏导数,我们需要使用链式法则(Chain Rule)来求解。在以下偏导数公式中,y_true 表示真实标签,y_pred 表示模型预测的结果。

对于第 i 个样本的预测结果 y_pred[i],平均绝对误差损失函数对 y_pred[i] 的偏导数公式如下:

∂ MAE ∂ y pred [ i ] = 1 N ∑ j = 1 N y pred [ j ] − y true [ j ] ∣ y pred [ j ] − y true [ j ] ∣ \frac{\partial \text{MAE}}{\partial y_{\text{pred}[i]}} = \frac{1}{N} \sum_{j=1}^{N} \frac{y_{\text{pred}[j]} - y_{\text{true}[j]}}{\left| y_{\text{pred}[j]} - y_{\text{true}[j]} \right|} ypred[i]MAE=N1j=1N ypred[j]ytrue[j] ypred[j]ytrue[j]

其中:

  • N N N 是样本数(Batch Size)。
  • y true [ j ] y_{\text{true}[j]} ytrue[j] 是第 j 个样本的真实标签值。
  • y pred [ j ] y_{\text{pred}[j]} ypred[j] 是第 j 个样本的模型预测结果。

这个公式表示在平均绝对误差损失函数中,对于每个样本的预测结果 y_pred[i],偏导数的值是每个样本的预测结果与真实标签之差的符号(正负号)的平均值。

注意,这个偏导数的计算是针对每个样本的预测结果 y_pred[i] 的,而不是整个批量样本的预测结果 y_pred

   MAE = (1/n) * Σ|y_pred - y_true|
#include <cmath>
//单个数据
double meanAbsoluteError(double y_true, double y_pred) {
    return fabs(y_true - y_pred);
}

//多个数据
double batchMeanAbsoluteError(const std::vector<double>& y_true, const std::vector<double>& y_pred) {
    int batch_size = y_true.size();
    double loss = 0.0;

    for (int i = 0; i < batch_size; i++) {
        loss += fabs(y_true[i] - y_pred[i]);
    }

    return loss / batch_size;
}

// 计算 MAE 损失函数对 y_pred 的偏导数
std::vector<double> maeLossDerivative(const std::vector<double>& y_true, const std::vector<double>& y_pred) {
    int size = y_true.size();
    std::vector<double> derivative(size);

    for (int i = 0; i < size; i++) {
        derivative[i] = (y_pred[i] > y_true[i]) ? 1 : -1;
    }

    return derivative;
}

// 计算 MAE 损失函数对 y_pred 的偏导数
std::vector<std::vector<double>> batchMAELossDerivative(const std::vector<std::vector<double>>& y_true,
                                                        const std::vector<std::vector<double>>& y_pred) {
    int batch_size = y_true.size();
    int size = y_true[0].size();
    std::vector<std::vector<double>> derivative(batch_size, std::vector<double>(size, 0.0));

    for (int i = 0; i < batch_size; i++) {
        for (int j = 0; j < size; j++) {
            derivative[i][j] = (y_pred[i][j] > y_true[i][j]) ? 1 : -1;
        }
    }

    return derivative;
}

交叉熵损失函数(Cross-Entropy Loss):

用于分类问题,衡量模型输出概率分布与实际标签的差异。
交叉熵损失函数(Cross-Entropy Loss)是用于衡量分类模型预测结果与真实标签之间差异的常用损失函数。对于多类别分类问题,它是一种广泛使用的损失函数。以下是交叉熵损失函数的数学公式:

假设我们有一个批量样本,其中每个样本有 C 个类别。y_true 表示真实标签的独热编码形式,y_pred 表示模型预测的类别概率向量。

交叉熵损失函数公式如下:

CrossEntropyLoss = − 1 N ∑ i = 1 N ∑ j = 1 C y true , i , j ⋅ log ⁡ ( y pred , i , j ) \text{CrossEntropyLoss} = - \frac{1}{N} \sum_{i=1}^{N} \sum_{j=1}^{C} y_{\text{true},i,j} \cdot \log(y_{\text{pred},i,j}) CrossEntropyLoss=N1i=1Nj=1Cytrue,i,jlog(ypred,i,j)

其中:

  • N N N 是批量样本的数量(Batch Size)。
  • C C C 是类别的数量。
  • y true , i , j y_{\text{true},i,j} ytrue,i,j 表示第 i 个样本的真实标签中类别 j 的值,它是一个0或1的独热编码。
  • y pred , i , j y_{\text{pred},i,j} ypred,i,j 表示第 i 个样本的模型预测结果中类别 j 的概率。

交叉熵损失函数是一个非负数,当模型预测结果与真实标签完全匹配时,损失为0。它的值越小表示模型的预测结果与真实标签越接近,即模型的性能越好。

在实际应用中,通常结合梯度下降等优化算法,将交叉熵损失函数用于训练神经网络,从而调整网络参数以最小化损失函数。这样可以提高分类模型的准确性和性能。
要计算交叉熵损失函数对模型预测结果 y_pred 的偏导数,我们需要使用链式法则(Chain Rule)来求解。在以下偏导数公式中,y_true 表示真实标签的独热编码形式,y_pred 表示模型预测的类别概率向量。

对于第 i 个样本的第 j 个类别,交叉熵损失函数对 y_pred[i][j] 的偏导数公式如下:

∂ CrossEntropyLoss ∂ y pred [ i ] [ j ] = − y true [ i ] [ j ] y pred [ i ] [ j ] \frac{\partial \text{CrossEntropyLoss}}{\partial y_{\text{pred}[i][j]}} = - \frac{y_{\text{true}[i][j]}}{y_{\text{pred}[i][j]}} ypred[i][j]CrossEntropyLoss=ypred[i][j]ytrue[i][j]

其中:

  • y true [ i ] [ j ] y_{\text{true}[i][j]} ytrue[i][j] 是第 i 个样本的真实标签中类别 j 的值,它是一个0或1的独热编码。
  • y pred [ i ] [ j ] y_{\text{pred}[i][j]} ypred[i][j] 是第 i 个样本的模型预测结果中类别 j 的概率。

这个公式表示在交叉熵损失函数中,对于每个样本的每个类别,偏导数的值是真实标签中类别值与模型预测结果中类别概率的比值的负数。

请注意,交叉熵损失函数对于真实标签中非零值(即对应真实类别的位置)的偏导数是负无穷大,因为在这些位置上 y true [ i ] [ j ] y_{\text{true}[i][j]} ytrue[i][j] 是1,而 y pred [ i ] [ j ] y_{\text{pred}[i][j]} ypred[i][j] 逼近于0,导致分母接近于0。这就是为什么在实际应用中,通常使用计算机的浮点数精度范围内的小值(如1e-9)来避免除以0的情况,从而保证计算的稳定性。

对于二分类问题:

   Binary Cross-Entropy = -[y_true * log(y_pred) + (1 - y_true) * log(1 - y_pred)]
   

对于多分类问题:

   Categorical Cross-Entropy = -Σ(y_true * log(y_pred))
#include <cmath>


    // 单个数据 - 二分类
    double NetWork::binaryCrossEntropyLoss(double y_pred, double y_true)
    {
        y_pred = std::max(std::min(y_pred, 1.0 - 1e-15), 1e-15); // 避免log(0)的情况
        return -(y_true * log(y_pred) + (1 - y_true) * log(1 - y_pred));
    }

    // 计算交叉熵损失函数对 y_pred 的偏导数 - 单个数据 - 二分类
    double binaryCrossEntropyLossDerivative(double y_pred, double y_true)
    {
        // 计算predicted_prob对于损失函数L1和L2的偏导数
        double d_L1 = -y_true / std::max(std::min(y_pred, 1.0 - 1e-15), 1e-15);
        double d_L2 = (1.0 - y_true) / (1.0 - std::max(std::min(y_pred, 1.0 - 1e-15), 1e-15));

        // 计算predicted_prob对于总损失L的偏导数
        double d_L = d_L1 + d_L2;
        return d_L;
    }



    //单个数据 - 多分类
    double NetWork::categoricalCrossEntropyLoss(const Eigen::VectorXd& y_true,const Eigen::VectorXd& y_pred)
    {
        int class_size = y_true.rows();// 类别数
        double loss = 0.0;

        for (int i = 0; i < class_size; i++)
        {
            loss -= y_true(i) * log(std::max(std::min(y_pred(i), 1.0 - 1e-15), 1e-15));

        }

        return loss;// 求平均损失
    }
    // 计算交叉熵损失函数对 y_pred 的偏导数 - 单个数据 - 多分类
    Eigen::VectorXd NetWork::categoricalCrossEntropyLossDerivative(const Eigen::VectorXd& y_true, const Eigen::VectorXd& y_pred)
    {
        int size = y_true.size();
        Eigen::VectorXd derivative(size);

        for (int i = 0; i < size; i++)
        {
            derivative[i] = -y_true[i] / std::max(std::min(y_pred[i], 1.0 - 1e-15), 1e-15);
        }

        return derivative;
    }



    //多个数据 - 多分类
    double batchCategoricalCrossEntropy(const std::vector<std::vector<double>>& y_true,
                                        const std::vector<std::vector<double>>& y_pred) {
        int batch_size = y_true.size();
        int num_classes = y_true[0].size();
        double loss = 0.0;
        
        for (int i = 0; i < batch_size; i++) {
            for (int j = 0; j < num_classes; j++) {
                loss += -y_true[i][j] * log(std::max(std::min(y_pred[i][j], 1.0 - 1e-15), 1e-15));
            }
        }
        
        return loss / batch_size;
    }
    

// 计算交叉熵损失函数对 y_pred 的偏导数 - 多分类
std::vector<std::vector<double>> batchCrossEntropyLossDerivative(const std::vector<std::vector<double>>& y_true,
                                                                 const std::vector<std::vector<double>>& y_pred) {
    int batch_size = y_true.size();
    int size = y_true[0].size();
    std::vector<std::vector<double>> derivative(batch_size, std::vector<double>(size, 0.0));

    for (int i = 0; i < batch_size; i++) {
        for (int j = 0; j < size; j++) {
            derivative[i][j] = -y_true[i][j] / std::max(std::min(y_pred[i][j], 1.0 - 1e-15), 1e-15);
        }
    }

    return derivative;
}

Hinge Loss:

用于支持向量机(SVM)和一些特定的分类问题。

Hinge Loss(合页损失)是一种常用的用于支持向量机(SVM)和一些分类任务的损失函数。它在处理二分类问题时非常常见,特别是用于支持向量机中的线性分类和最大间隔分类。

对于二分类问题,假设我们有一个批量样本,其中每个样本的真实标签为 y_true,模型预测的结果为 y_pred。Hinge Loss 的计算方法如下:

Hinge Loss = 1 N ∑ i = 1 N max ⁡ ( 0 , 1 − y true , i ⋅ y pred , i ) \text{Hinge Loss} = \frac{1}{N} \sum_{i=1}^{N} \max(0, 1 - y_{\text{true},i} \cdot y_{\text{pred},i}) Hinge Loss=N1i=1Nmax(0,1ytrue,iypred,i)

其中:

  • N N N 是样本数(Batch Size)。
  • y true , i y_{\text{true},i} ytrue,i 是第 i 个样本的真实标签,取值为+1或-1。
  • y pred , i y_{\text{pred},i} ypred,i 是第 i 个样本的模型预测结果,也是一个+1或-1的值。

Hinge Loss 衡量的是模型预测结果与真实标签之间的差异,并利用了间隔(margin)的概念。如果模型的预测结果与真实标签的符号相同,意味着预测正确,Hinge Loss 为0。如果预测结果与真实标签的符号相反,意味着预测错误,Hinge Loss 此时正比于预测结果与真实标签之差,当它大于1时损失开始增加。

Hinge Loss 的特点是,对于预测结果与真实标签的差异小于1的情况,损失为0,这被称为“合页点”(hinge point)。只有当预测结果与真实标签的差异大于等于1时,损失开始增加。

Hinge Loss 是一个非平滑的损失函数,因此其偏导数在某些点上不存在或为零。在通常情况下,Hinge Loss 的导数在预测结果与真实标签之差等于1的时候是不可导的(不连续)。然而,我们可以使用次梯度(subgradient)的概念来处理这个问题。

对于第 i 个样本的预测结果 y_pred[i],Hinge Loss 对 y_pred[i] 的偏导数公式如下:

$$
\frac{\partial \text{Hinge Loss}}{\partial y_{\text{pred}[i]}} =
\begin{cases}

  • y_{\text{true}[i]} & \text{if } 1 - y_{\text{true}[i]} \cdot y_{\text{pred}[i]} > 0 \
    0 & \text{otherwise}
    \end{cases}
    $$

其中:

  • y true [ i ] y_{\text{true}[i]} ytrue[i] 是第 i 个样本的真实标签,取值为+1或-1。
  • y pred [ i ] y_{\text{pred}[i]} ypred[i] 是第 i 个样本的模型预测结果,也是一个+1或-1的值。

在上面的偏导数公式中,如果预测结果与真实标签之差大于1(即 1 − y true [ i ] ⋅ y pred [ i ] > 0 1 - y_{\text{true}[i]} \cdot y_{\text{pred}[i]} > 0 1ytrue[i]ypred[i]>0),则偏导数为 -y_{\text{true}[i]}。否则,偏导数为0。

由于 Hinge Loss 在预测结果与真实标签之差等于1的点是不可导的,因此我们在这些点上使用次梯度 -y_{\text{true}[i]} 来近似表示导数的方向。

   Hinge Loss = max(0, 1 - y_true * y_pred)
#include <vector>
#include <algorithm>

double hingeLoss(double y_true, double y_pred) {
    return std::max(0.0, 1 - y_true * y_pred);
}


double batchHingeLoss(const std::vector<std::vector<double>>& y_true,
                      const std::vector<std::vector<double>>& y_pred) {
    int batch_size = y_true.size();
    double loss = 0.0;

    for (int i = 0; i < batch_size; i++) {
        double max_margin = 0.0;
        for (size_t j = 0; j < y_true[i].size(); j++) {
            max_margin = std::max(max_margin, 1 - y_true[i][j] * y_pred[i][j]);
        }
        loss += max_margin;
    }

    return loss / batch_size;
}


// 计算 Hinge Loss 函数对 y_pred 的偏导数
std::vector<double> hingeLossDerivative(const std::vector<double>& y_true, const std::vector<double>& y_pred) {
    int size = y_true.size();
    std::vector<double> derivative(size);

    for (int i = 0; i < size; i++) {
        derivative[i] = (y_true[i] * y_pred[i] < 1) ? -y_true[i] : 0;
    }

    return derivative;
}


// 计算 Hinge Loss 函数对 y_pred 的偏导数
std::vector<std::vector<double>> batchHingeLossDerivative(const std::vector<std::vector<double>>& y_true,
                                                         const std::vector<std::vector<double>>& y_pred) {
    int batch_size = y_true.size();
    int size = y_true[0].size();
    std::vector<std::vector<double>> derivative(batch_size, std::vector<double>(size, 0.0));

    for (int i = 0; i < batch_size; i++) {
        for (int j = 0; j < size; j++) {
            derivative[i][j] = (y_true[i][j] * y_pred[i][j] < 1) ? -y_true[i][j] : 0;
        }
    }

    return derivative;
}

交叉熵损失与对数似然损失(Log-Likelihood Loss):

  1. 对数似然损失(Log-Likelihood Loss)的偏导数:

假设我们有一个批量样本,其中每个样本有 C 个类别。y_true 表示真实标签的独热编码形式,y_pred 表示模型预测的类别概率向量。

对数似然损失函数的数学公式如下:

LogLikelihoodLoss = − 1 N ∑ i = 1 N ∑ j = 1 C y true , i , j ⋅ log ⁡ ( y pred , i , j ) \text{LogLikelihoodLoss} = -\frac{1}{N} \sum_{i=1}^{N} \sum_{j=1}^{C} y_{\text{true},i,j} \cdot \log(y_{\text{pred},i,j}) LogLikelihoodLoss=N1i=1Nj=1Cytrue,i,jlog(ypred,i,j)

其中:

  • N N N 是批量样本的数量(Batch Size)。
  • C C C 是类别的数量。
  • y true , i , j y_{\text{true},i,j} ytrue,i,j 表示第 i 个样本的真实标签中类别 j 的值,它是一个0或1的独热编码。
  • y pred , i , j y_{\text{pred},i,j} ypred,i,j 表示第 i 个样本的模型预测结果中类别 j 的概率。

对数似然损失函数对 y_pred 的偏导数公式如下:

∂ LogLikelihoodLoss ∂ y pred [ i ] [ j ] = − y true [ i ] [ j ] y pred [ i ] [ j ] \frac{\partial \text{LogLikelihoodLoss}}{\partial y_{\text{pred}[i][j]}} = -\frac{y_{\text{true}[i][j]}}{y_{\text{pred}[i][j]}} ypred[i][j]LogLikelihoodLoss=ypred[i][j]ytrue[i][j]

用于多类别分类问题,类似于分类问题中的交叉熵损失。

   Log-Likelihood Loss = -log(y_pred[y_true])
#include <cmath>

double logLikelihoodLoss(double y_true[], double y_pred[], int classes) {
    double loss = 0.0;
    for (int i = 0; i < classes; i++) {
        loss -= y_true[i] * log(y_pred[i]);
    }
    return loss;
}


double batchLogLikelihoodLoss(const std::vector<std::vector<double>>& y_true,
                              const std::vector<std::vector<double>>& y_pred) {
    int batch_size = y_true.size();
    int num_classes = y_true[0].size();
    double loss = 0.0;

    for (int i = 0; i < batch_size; i++) {
        for (int j = 0; j < num_classes; j++) {
            loss += -y_true[i][j] * log(y_pred[i][j]);
        }
    }

    return loss / batch_size;
}


// 对数似然损失函数对 y_pred 的偏导数
std::vector<std::vector<double>> batchLogLikelihoodLossDerivative(const std::vector<std::vector<double>>& y_true,
                                                                  const std::vector<std::vector<double>>& y_pred) {
    int batch_size = y_true.size();
    int num_classes = y_true[0].size();
    std::vector<std::vector<double>> derivative(batch_size, std::vector<double>(num_classes, 0.0));

    for (int i = 0; i < batch_size; i++) {
        for (int j = 0; j < num_classes; j++) {
            derivative[i][j] = -y_true[i][j] / y_pred[i][j];
        }
    }

    return derivative;
}

KL 散度损失(Kullback-Leibler Divergence Loss,KL Loss):

用于衡量两个概率分布之间的差异。

  1. KL 散度(Kullback-Leibler Divergence)的偏导数:

KL 散度用于衡量两个概率分布之间的距离。假设有两个概率分布 P P P Q Q Q,其中 y_true 表示真实概率分布的独热编码形式,y_pred 表示模型预测的概率分布。

KL 散度的数学公式如下:

KL ( P ∥ Q ) = ∑ i = 1 C P ( i ) ⋅ log ⁡ ( P ( i ) Q ( i ) ) \text{KL}(P \| Q) = \sum_{i=1}^{C} P(i) \cdot \log\left(\frac{P(i)}{Q(i)}\right) KL(PQ)=i=1CP(i)log(Q(i)P(i))

其中:

  • C C C 是类别的数量。
  • P ( i ) P(i) P(i) 表示真实概率分布中类别 i 的概率。
  • Q ( i ) Q(i) Q(i) 表示模型预测概率分布中类别 i 的概率。

KL 散度对 y_pred 的偏导数公式如下:

∂ KL ( P ∥ Q ) ∂ y pred [ i ] = − P ( i ) y pred [ i ] \frac{\partial \text{KL}(P \| Q)}{\partial y_{\text{pred}[i]}} = -\frac{P(i)}{y_{\text{pred}[i]}} ypred[i]KL(PQ)=ypred[i]P(i)

请注意,KL 散度是非对称的,即 KL ( P ∥ Q ) ≠ KL ( Q ∥ P ) \text{KL}(P \| Q) \neq \text{KL}(Q \| P) KL(PQ)=KL(QP),因此在计算时需要注意分子和分母的位置。

   KL Loss = Σ(y_true * log(y_true / y_pred))
#include <cmath>

//单个数据
double klDivergenceLoss(double y_true[], double y_pred[], int classes) {
    double loss = 0.0;
    for (int i = 0; i < classes; i++) {
        if (y_true[i] != 0.0) {
            loss += y_true[i] * log(y_true[i] / y_pred[i]);
        }
    }
    return loss;
}
//多个数据
double batchKLDivergenceLoss(const std::vector<std::vector<double>>& y_true,
                             const std::vector<std::vector<double>>& y_pred) {
    int batch_size = y_true.size();
    int num_classes = y_true[0].size();
    double loss = 0.0;

    for (int i = 0; i < batch_size; i++) {
        for (int j = 0; j < num_classes; j++) {
            if (y_true[i][j] != 0.0) {
                loss += y_true[i][j] * log(y_true[i][j] / y_pred[i][j]);
            }
        }
    }

    return loss / batch_size;
}

// KL 散度对 y_pred 的偏导数
std::vector<std::vector<double>> batchKLDivergenceDerivative(const std::vector<std::vector<double>>& P,
                                                            const std::vector<std::vector<double>>& Q) {
    int batch_size = P.size();
    int num_classes = P[0].size();
    std::vector<std::vector<double>> derivative(batch_size, std::vector<double>(num_classes, 0.0));

    for (int i = 0; i < batch_size; i++) {
        for (int j = 0; j < num_classes; j++) {
            derivative[i][j] = -P[i][j] / Q[i][j];
        }
    }

    return derivative;
}

余弦相似度损失(Cosine Similarity Loss):

用于度量向量之间的余弦相似度。

余弦相似度损失(Cosine Similarity Loss)是用于衡量向量之间相似度的一种损失函数。它通常用于训练嵌入模型(Embedding Model)或进行相关性学习任务,特别是在自然语言处理(NLP)和推荐系统等领域。

假设我们有一个批量样本,其中每个样本有两个向量表示,分别是 v1v2,余弦相似度损失的计算方法如下:

Cosine Similarity Loss = 1 N ∑ i = 1 N 1 − cosine_similarity ( v 1 i , v 2 i ) \text{Cosine Similarity Loss} = \frac{1}{N} \sum_{i=1}^{N} 1 - \text{cosine\_similarity}(v1_i, v2_i) Cosine Similarity Loss=N1i=1N1cosine_similarity(v1i,v2i)

其中:

  • N N N 是样本数(Batch Size)。
  • v 1 i v1_i v1i 是第 i 个样本的第一个向量。
  • v 2 i v2_i v2i 是第 i 个样本的第二个向量。
  • cosine_similarity ( v 1 i , v 2 i ) \text{cosine\_similarity}(v1_i, v2_i) cosine_similarity(v1i,v2i) 是计算向量 v1_iv2_i 之间的余弦相似度。

余弦相似度是一个衡量两个向量方向之间相似程度的指标,取值范围在 -1 到 1 之间。当余弦相似度为1时,表示两个向量完全相似;当余弦相似度为-1时,表示两个向量完全相反;当余弦相似度为0时,表示两个向量正交(垂直)。

余弦相似度损失的计算是通过计算向量之间的余弦相似度并将其从1中减去,然后对所有样本取平均得到最终的损失值。这样做的目的是使余弦相似度尽可能接近1,从而提高向量之间的相似度。

为了计算余弦相似度损失(Cosine Similarity Loss)对向量 v1_iv2_i 的偏导数,我们需要先计算余弦相似度对向量的偏导数,然后再根据链式法则将其传递给损失函数。在以下偏导数公式中,v1_iv2_i 表示第 i 个样本的两个向量。

设余弦相似度为 cos_sim \text{cos\_sim} cos_sim,则余弦相似度损失对向量 v1_i 的偏导数为:

∂ Cosine Similarity Loss ∂ v 1 i = − 1 N ( 1 − cos_sim ( v 1 i , v 2 i ) ) ∂ cos_sim ( v 1 i , v 2 i ) ∂ v 1 i \frac{\partial \text{Cosine Similarity Loss}}{\partial v1_i} = - \frac{1}{N} \left(1 - \text{cos\_sim}(v1_i, v2_i)\right) \frac{\partial \text{cos\_sim}(v1_i, v2_i)}{\partial v1_i} v1iCosine Similarity Loss=N1(1cos_sim(v1i,v2i))v1icos_sim(v1i,v2i)

余弦相似度损失对向量 v2_i 的偏导数为:

∂ Cosine Similarity Loss ∂ v 2 i = − 1 N ( 1 − cos_sim ( v 1 i , v 2 i ) ) ∂ cos_sim ( v 1 i , v 2 i ) ∂ v 2 i \frac{\partial \text{Cosine Similarity Loss}}{\partial v2_i} = - \frac{1}{N} \left(1 - \text{cos\_sim}(v1_i, v2_i)\right) \frac{\partial \text{cos\_sim}(v1_i, v2_i)}{\partial v2_i} v2iCosine Similarity Loss=N1(1cos_sim(v1i,v2i))v2icos_sim(v1i,v2i)

接下来,我们需要计算余弦相似度对向量的偏导数。假设向量 v1_iv2_i 的维度都为 d,则余弦相似度 cos_sim ( v 1 i , v 2 i ) \text{cos\_sim}(v1_i, v2_i) cos_sim(v1i,v2i) 的计算方法为:

cos_sim ( v 1 i , v 2 i ) = v 1 i ⋅ v 2 i ∥ v 1 i ∥ ⋅ ∥ v 2 i ∥ \text{cos\_sim}(v1_i, v2_i) = \frac{v1_i \cdot v2_i}{\|v1_i\| \cdot \|v2_i\|} cos_sim(v1i,v2i)=v1iv2iv1iv2i

其中 ⋅ \cdot 表示向量的点积, ∥ ⋅ ∥ \| \cdot \| 表示向量的范数(模长)。

现在,我们计算余弦相似度对向量 v1_i 的偏导数:

∂ cos_sim ( v 1 i , v 2 i ) ∂ v 1 i = v 2 i ∥ v 1 i ∥ ⋅ ∥ v 2 i ∥ − v 1 i ⋅ v 2 i ∥ v 1 i ∥ 3 ⋅ ∥ v 2 i ∥ v 1 i \frac{\partial \text{cos\_sim}(v1_i, v2_i)}{\partial v1_i} = \frac{v2_i}{\|v1_i\| \cdot \|v2_i\|} - \frac{v1_i \cdot v2_i}{\|v1_i\|^3 \cdot \|v2_i\|} v1_i v1icos_sim(v1i,v2i)=v1iv2iv2iv1i3v2iv1iv2iv1i

接着,我们计算余弦相似度对向量 v2_i 的偏导数:

∂ cos_sim ( v 1 i , v 2 i ) ∂ v 2 i = v 1 i ∥ v 1 i ∥ ⋅ ∥ v 2 i ∥ − v 1 i ⋅ v 2 i ∥ v 1 i ∥ ⋅ ∥ v 2 i ∥ 3 v 2 i \frac{\partial \text{cos\_sim}(v1_i, v2_i)}{\partial v2_i} = \frac{v1_i}{\|v1_i\| \cdot \|v2_i\|} - \frac{v1_i \cdot v2_i}{\|v1_i\| \cdot \|v2_i\|^3} v2_i v2icos_sim(v1i,v2i)=v1iv2iv1iv1iv2i3v1iv2iv2i

最后,我们将上述结果代入余弦相似度损失对向量的偏导数公式,得到最终的偏导数计算公式。

请注意,对于余弦相似度损失函数的偏导数计算,需要根据具体的向量表示和损失函数的定义进行推导。以上给出的是一种常见情况下的计算方法,实际应用中可能会根据具体的问题和模型定义进行调整。

   Cosine Similarity Loss = 1 - (y_true * y_pred) / (||y_true|| * ||y_pred||)
#include <cmath>
//单个数据
double cosineSimilarityLoss(double y_true[], double y_pred[], int dimensions) {
    double dotProduct = 0.0;
    double norm_y_true = 0.0;
    double norm_y_pred = 0.0;

    for (int i = 0; i < dimensions; i++) {
        dotProduct += y_true[i] * y_pred[i];
        norm_y_true += pow(y_true[i], 2);
        norm_y_pred += pow(y_pred[i], 2);
    }

    norm_y_true = sqrt(norm_y_true);
    norm_y_pred = sqrt(norm_y_pred);

    double similarity = dotProduct / (norm_y_true * norm_y_pred);

    return 1 - similarity;
}

//多个数据
double batchCosineSimilarityLoss(const std::vector<std::vector<double>>& y_true,
                                 const std::vector<std::vector<double>>& y_pred) {
    int batch_size = y_true.size();
    int dimensions = y_true[0].size();
    double loss = 0.0;

    for (int i = 0; i < batch_size; i++) {
        double dotProduct = 0.0;
        double norm_y_true = 0.0;
        double norm_y_pred = 0.0;

        for (int j = 0; j < dimensions; j++) {
            dotProduct += y_true[i][j] * y_pred[i][j];
            norm_y_true += pow(y_true[i][j], 2);
            norm_y_pred += pow(y_pred[i][j], 2);
        }

        norm_y_true = sqrt(norm_y_true);
        norm_y_pred = sqrt(norm_y_pred);

        double similarity = dotProduct / (norm_y_true * norm_y_pred);

        loss += 1 - similarity;
    }

    return loss / batch_size;
}


// 计算余弦相似度损失函数对 y_pred 的偏导数
std::vector<double> cosineSimilarityLossDerivative(const std::vector<double>& y_true, const std::vector<double>& y_pred) {
    int size = y_true.size();
    std::vector<double> derivative(size);

    double norm_y_true = 0.0;
    double norm_y_pred = 0.0;
    double dotProduct = 0.0;

    for (int i = 0; i < size; i++) {
        dotProduct += y_true[i] * y_pred[i];
        norm_y_true += pow(y_true[i], 2);
        norm_y_pred += pow(y_pred[i], 2);
    }

    norm_y_true = sqrt(norm_y_true);
    norm_y_pred = sqrt(norm_y_pred);

    for (int i = 0; i < size; i++) {
        derivative[i] = (y_true[i] / (norm_y_true * norm_y_pred)) - (dotProduct * pow(y_true[i], 2) / pow(norm_y_true, 3) * norm_y_pred);
    }

    return derivative;
}


// 计算余弦相似度损失函数对 y_pred 的偏导数
std::vector<std::vector<double>> batchCosineSimilarityLossDerivative(const std::vector<std::vector<double>>& y_true,
                                                                     const std::vector<std::vector<double>>& y_pred) {
    int batch_size = y_true.size();
    int size = y_true[0].size();
    std::vector<std::vector<double>> derivative(batch_size, std::vector<double>(size, 0.0));

    for (int i = 0; i < batch_size; i++) {
        double norm_y_true = 0.0;
        double norm_y_pred = 0.0;
        double dotProduct = 0.0;

        for (int j = 0; j < size; j++) {
            dotProduct += y_true[i][j] * y_pred[i][j];
            norm_y_true += pow(y_true[i][j], 2);
            norm_y_pred += pow(y_pred[i][j], 2);
        }

        norm_y_true = sqrt(norm_y_true);
        norm_y_pred = sqrt(norm_y_pred);

        for (int j = 0; j < size; j++) {
            derivative[i][j] = (y_true[i][j] / (norm_y_true * norm_y_pred)) - (dotProduct * pow(y_true[i][j], 2) / pow(norm_y_true, 3) * norm_y_pred);
        }
    }

    return derivative;
}

信息熵损失(Entropy Loss):

用于最大化预测的不确定性,通常在生成模型中使用。

信息熵损失(Entropy Loss),也称为交叉熵损失(Cross-Entropy Loss),是一种用于衡量分类模型预测结果与真实标签之间差异的常用损失函数。它通常用于多类别分类问题,尤其是在神经网络中用于训练分类任务。

假设我们有一个批量样本,其中每个样本的真实标签用独热编码表示为 y_true,模型预测的类别概率向量为 y_pred。信息熵损失的计算方法如下:

Entropy Loss = − 1 N ∑ i = 1 N ∑ j = 1 C y true , i , j ⋅ log ⁡ ( y pred , i , j ) \text{Entropy Loss} = - \frac{1}{N} \sum_{i=1}^{N} \sum_{j=1}^{C} y_{\text{true},i,j} \cdot \log(y_{\text{pred},i,j}) Entropy Loss=N1i=1Nj=1Cytrue,i,jlog(ypred,i,j)

其中:

  • N N N 是样本数(Batch Size)。
  • C C C 是类别的数量。
  • y true , i , j y_{\text{true},i,j} ytrue,i,j 表示第 i 个样本的真实标签中类别 j 的值,它是一个0或1的独热编码。
  • y pred , i , j y_{\text{pred},i,j} ypred,i,j 表示第 i 个样本的模型预测结果中类别 j 的概率。

信息熵是信息论中的概念,用于衡量随机变量的不确定性。在分类任务中,信息熵损失函数的作用是最小化模型预测的类别概率分布与真实标签的差异,使得模型能够更好地预测正确类别。当模型的预测结果与真实标签完全匹配时,损失为0。信息熵损失越小,表示模型的预测结果与真实标签越接近,即模型的性能越好。

要计算信息熵损失(Entropy Loss)对模型预测结果 y_pred 的偏导数,我们需要使用链式法则(Chain Rule)来求解。在以下偏导数公式中,y_true 表示真实标签的独热编码形式,y_pred 表示模型预测的类别概率向量。

对于第 i 个样本的第 j 个类别,信息熵损失函数对 y_pred[i][j] 的偏导数公式如下:

∂ Entropy Loss ∂ y pred [ i ] [ j ] = − y true [ i ] [ j ] y pred [ i ] [ j ] \frac{\partial \text{Entropy Loss}}{\partial y_{\text{pred}[i][j]}} = - \frac{y_{\text{true}[i][j]}}{y_{\text{pred}[i][j]}} ypred[i][j]Entropy Loss=ypred[i][j]ytrue[i][j]

其中:

  • y true [ i ] [ j ] y_{\text{true}[i][j]} ytrue[i][j] 是第 i 个样本的真实标签中类别 j 的值,它是一个0或1的独热编码。
  • y pred [ i ] [ j ] y_{\text{pred}[i][j]} ypred[i][j] 是第 i 个样本的模型预测结果中类别 j 的概率。

这个公式表示在信息熵损失函数中,对于每个样本的每个类别,偏导数的值是真实标签中类别值与模型预测结果中类别概率的比值的负数。

请注意,信息熵损失函数对于真实标签中非零值(即对应真实类别的位置)的偏导数是负无穷大,因为在这些位置上 y_{\text{true}[i][j]} 是1,而 y_{\text{pred}[i][j]} 逼近于0,导致分母接近于0。这就是为什么在实际应用中,通常使用计算机的浮点数精度范围内的小值(如1e-9)来避免除以0的情况,从而保证计算的稳定性。

   Entropy Loss = -Σ(y_pred * log(y_pred))
#include <cmath>
//单个数据
double entropyLoss(double y_pred[], int classes) {
    double loss = 0.0;
    for (int i = 0; i < classes; i++) {
        if (y_pred[i] != 0.0) {
            loss -= y_pred[i] * log(y_pred[i]);
        }
    }
    return loss;
}

//多个数据
double batchEntropyLoss(const std::vector<std::vector<double>>& y_pred) {
    int batch_size = y_pred.size();
    int num_classes = y_pred[0].size();
    double loss = 0.0;

    for (int i = 0; i < batch_size; i++) {
        for (int j = 0; j < num_classes; j++) {
            if (y_pred[i][j] != 0.0) {
                loss += y_pred[i][j] * log(y_pred[i][j]);
            }
        }
    }

    return -loss / batch_size;
}

// 计算信息熵损失函数对 y_pred 的偏导数
std::vector<double> entropyLossDerivative(const std::vector<double>& y_pred) {
    int size = y_pred.size();
    std::vector<double> derivative(size);

    for (int i = 0; i < size; i++) {
        derivative[i] = -log(y_pred[i]) - 1;
    }

    return derivative;
}


// 计算信息熵损失函数对 y_pred 的偏导数
std::vector<std::vector<double>> batchEntropyLossDerivative(const std::vector<std::vector<double>>& y_pred) {
    int batch_size = y_pred.size();
    int size = y_pred[0].size();
    std::vector<std::vector<double>> derivative(batch_size, std::vector<double>(size, 0.0));

    for (int i = 0; i < batch_size; i++) {
        for (int j = 0; j < size; j++) {
            derivative[i][j] = -log(y_pred[i][j]) - 1;
        }
    }

    return derivative;
}

我们假设输入数据是一个二维矩阵,其中每一行代表一个样本,每一列代表样本的特征。我们将使用C++标准库的向量来表示这些矩阵,并根据需要实现批量样本的损失函数。

  • 24
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值