LR中的损失函数

在线性回归中损失函数的推导是根据一个假设——若误差是独立同分布,那么根据中心极限定理可以知道这个误差服从均值为0,它的方差为普通rnn总损失函数 lr损失函数推导_似然函数,则可以得到进一步的推导到对数似然函数,也就是损失函数:

普通rnn总损失函数 lr损失函数推导_中心极限定理_02

更详细的推导与代码可以看我之前的博客:

从线性回归到梯度下降法详细笔记



交叉熵损失函数

对于线性回归模型,我们定义的代价函数是所有模型误差的平方和。理论上来说,我们也可以对逻辑回归模型沿用这个定义。假设一共有在m组已知样本,普通rnn总损失函数 lr损失函数推导_似然函数_03表示第普通rnn总损失函数 lr损失函数推导_中心极限定理_04组数据及其对应的类别标记,其中普通rnn总损失函数 lr损失函数推导_中心极限定理_05为p+1维向量,普通rnn总损失函数 lr损失函数推导_似然函数_06则为表示类别的一个数(这里仅考虑分类问题),那么模型的参数为普通rnn总损失函数 lr损失函数推导_中心极限定理_07,因此有:

普通rnn总损失函数 lr损失函数推导_中心极限定理_08

假设函数(hypothesis function)定义为:

普通rnn总损失函数 lr损失函数推导_损失函数_09

因为我们做的是0/1分类问题,所以可以直接理解得到我们想要的损失函数,我们将概率取对数,其单调性不变,为:

普通rnn总损失函数 lr损失函数推导_损失函数_10

普通rnn总损失函数 lr损失函数推导_似然函数_11

那么对于一共普通rnn总损失函数 lr损失函数推导_似然函数_12组样本,我们就可以得到模型对于整体训练样本的表现能力:

普通rnn总损失函数 lr损失函数推导_中心极限定理_13

但这里就会出现一个矛盾,我们希望似然函数越大越好,代表整个事件发生的概率约高,但另一方面损失函数又要求我们的值越小越好,所以我们不妨对上面的对数几率取相反数就解决了这个问题:

普通rnn总损失函数 lr损失函数推导_损失函数_14

我们重新定义逻辑回归的损失函数为 普通rnn总损失函数 lr损失函数推导_普通rnn总损失函数_15,其中:

普通rnn总损失函数 lr损失函数推导_损失函数_16

从曲线的角度来看,沿用原线性的定义,代入似然函数中,将得到的代价函数将是一个非凸函数(non-convexfunction)。给似然函数加负号改变似然函数曲线的趋势,变成类似凸函数的下凸形状,再取对数变成真正可导的凸函数,再取平均值变成可以代表单个样本的函数,就是最终的目标函数.

普通rnn总损失函数 lr损失函数推导_中心极限定理_17

import numpy as np 
def cost(theta, X, y):   
	theta = np.matrix(theta)   
	X = np.matrix(X)   
	y = np.matrix(y)   
	first = np.multiply(-y, np.log(sigmoid(X* theta.T)))   
	second = np.multiply((1 - y), np.log(1 - sigmoid(X* theta.T)))   
	return np.sum(first - second) / (len(X))

交叉熵函数推导

上述最后对数函数取相反数的结果,便是交叉熵,与之对应的还有相对熵、信息熵。。。交叉熵是用来衡量两个概率分布之间的差异。交叉熵越大,两个分布之间的差异越大,越对实验结果感到意外,反之,交叉熵越小,两个分布越相似,越符合预期。

所以对于分类问题,交叉熵损失函数可以写为:

普通rnn总损失函数 lr损失函数推导_似然函数_18

其中:

普通rnn总损失函数 lr损失函数推导_普通rnn总损失函数_19

由此,我们便可以用梯度下降法求得能使损失函数最小的参数了,得到:

普通rnn总损失函数 lr损失函数推导_损失函数_20

这次再计算普通rnn总损失函数 lr损失函数推导_似然函数_21对第普通rnn总损失函数 lr损失函数推导_似然函数_22个参数分量普通rnn总损失函数 lr损失函数推导_普通rnn总损失函数_23求偏导:

普通rnn总损失函数 lr损失函数推导_普通rnn总损失函数_24

注意:虽然得到的梯度下降算法表面上看上去与线性回归的梯度下降算法一样,但是这里的普通rnn总损失函数 lr损失函数推导_中心极限定理_25与线性回归中的不同,所以实际上是不一样的。另外,在运行梯度下降算法之前,进行特征缩放依旧是非常必要的

python与pytorch交叉熵版本对比

import torch
import numpy as np


class Entropy:
    def __init__(self):
        self.nx = None
        self.ny = None
        self.dnx = None

    def loss(self, nx, ny):
        self.nx = nx
        self.ny = ny
        loss = np.sum(- ny * np.log(nx))
        return loss

    def backward(self):
        self.dnx = - self.ny / self.nx
        return self.dnx


np.random.seed(123)
np.set_printoptions(precision=3, suppress=True, linewidth=120)

entropy = Entropy()

x = np.random.random([5, 10])
y = np.random.random([5, 10])
x_tensor = torch.tensor(x, requires_grad=True)
y_tensor = torch.tensor(y, requires_grad=True)

loss_numpy = entropy.loss(x, y)
grad_numpy = entropy.backward()

loss_tensor = (- y_tensor * torch.log(x_tensor)).sum()
loss_tensor.backward()
grad_tensor = x_tensor.grad

print("Python Loss :", loss_numpy)
print("PyTorch Loss :", loss_tensor.data.numpy())

print("\nPython dx :")
print(grad_numpy)
print("\nPyTorch dx :")
print(grad_tensor.data.numpy())


参考与推荐:
[1]. 吴恩达机器学习笔记
[2]. 交叉熵损失函数原理详解 [3]. 知乎——逻辑回归的损失函数怎么理解? [4]. 交叉熵代价函数(损失函数)及其求导推导