LR中的损失函数
在线性回归中损失函数的推导是根据一个假设——若误差是独立同分布,那么根据中心极限定理可以知道这个误差服从均值为0,它的方差为
σ2,则可以得到进一步的推导到对数似然函数,也就是损失函数:
ℓ(θ)=J(θ)=logL(θ)=logi=1∏m2π
σ1exp(−2σ2(y(i)−θTx(i))2)=i=1∑mlog2π
σ1exp(−2σ2(y(i)−θTx(i))2)mlog2π
σ1−σ21⋅21i=1∑m(y(i)−θTx(i))2=21i=1∑m(hθ(x(i))−y(i))2
更详细的推导与代码可以看我之前的博客:
从线性回归到梯度下降法详细笔记
交叉熵损失函数
对于线性回归模型,我们定义的代价函数是所有模型误差的平方和。理论上来说,我们也可以对逻辑回归模型沿用这个定义。假设一共有在m组已知样本,
(x(i),y(i))表示第
i组数据及其对应的类别标记,其中
x(i)=(1,x1(i),x2(i),…,xp(i))T为p+1维向量,
y(i)则为表示类别的一个数(这里仅考虑分类问题),那么模型的参数为
θ=(θ0,θ1,θ2,…,θp)T,因此有:
θTx(i):=θ0+θ1x1(i)+⋯+θpxp(i)
假设函数(hypothesis function)定义为:
hθ(x(i))=1+e−θTx(i)1
因为我们做的是0/1分类问题,所以可以直接理解得到我们想要的损失函数,我们将概率取对数,其单调性不变,为:
logP(y^(i)=1∣x(i);θ)=loghθ(x(i))=log1+e−θTx(i)1
logP(y^(i)=0∣x(i);θ)=log(1−hθ(x(i)))=log1+e−θTx(i)e−θTx(i)
扫描二维码关注公众号,回复:
11276541 查看本文章
那么对于一共
m组样本,我们就可以得到模型对于整体训练样本的表现能力:
i=1∑my(i)log(hθ(x(i)))+(1−y(i))log(1−hθ(x(i)))
但这里就会出现一个矛盾,我们希望似然函数越大越好,代表整个事件发生的概率约高,但另一方面损失函数又要求我们的值越小越好,所以我们不妨对上面的对数几率取相反数就解决了这个问题:
J(θ)=−m1i=1∑my(i)log(hθ(x(i)))+(1−y(i))log(1−hθ(x(i)))
我们重新定义逻辑回归的损失函数为
J(θ)=m1∑i=1mcost(hθ(x(i)),y(i)),其中:
cost(hθ(x),y)={−log(hθ(x))−log(1−hθ(x)) if y=1 if y=0
从曲线的角度来看,沿用原线性的定义,代入似然函数中,将得到的代价函数将是一个非凸函数(non-convexfunction)。给似然函数加负号改变似然函数曲线的趋势,变成类似凸函数的下凸形状,再取对数变成真正可导的凸函数,再取平均值变成可以代表单个样本的函数,就是最终的目标函数.
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))
交叉熵函数推导
上述最后对数函数取相反数的结果,便是交叉熵,与之对应的还有相对熵、信息熵。。。交叉熵是用来衡量两个概率分布之间的差异。交叉熵越大,两个分布之间的差异越大,越对实验结果感到意外,反之,交叉熵越小,两个分布越相似,越符合预期。
所以对于分类问题,交叉熵损失函数可以写为:
J(θ)=−m1i=1∑my(i)log(hθ(x(i)))+(1−y(i))log(1−hθ(x(i)))
其中:
loghθ(x(i))=log1+e−θTx(i)1=−log(1+e−θTx(i)) ,log(1−hθ(x(i)))=log(1−1+e−θTx(i)1)=log(1+e−θTx(i)e−θTx(i))=log(e−θTx(i))−log(1+e−θTx(i))=−θTx(i)−log(1+e−θTx(i)) .
由此,我们便可以用梯度下降法求得能使损失函数最小的参数了,得到:
J(θ)=−m1i=1∑m[−y(i)(log(1+e−θTx(i)))+(1−y(i))(−θTx(i)−log(1+e−θTx(i)))]=−m1i=1∑m[y(i)θTx(i)−θTx(i)−log(1+e−θTx(i))]=−m1i=1∑m[y(i)θTx(i)−logeθTx(i)−log(1+e−θTx(i))]=−m1i=1∑m[y(i)θTx(i)−(logeθTx(i)+log(1+e−θTx(i)))]=−m1i=1∑m[y(i)θTx(i)−log(1+eθTx(i))]
这次再计算
J(θ)对第
j个参数分量
θj求偏导:
∂θj∂J(θ)=∂θj∂(m1i=1∑m[log(1+eθTx(i))−y(i)θTx(i)])=m1i=1∑m[∂θj∂log(1+eθTx(i))−∂θj∂(y(i)θTx(i))]=m1i=1∑m(1+eθTx(i)xj(i)eθTx(i)−y(i)xj(i))=m1i=1∑m(hθ(x(i))−y(i))xj(i)
注意:虽然得到的梯度下降算法表面上看上去与线性回归的梯度下降算法一样,但是这里的
hθ(x)与线性回归中的不同,所以实际上是不一样的。另外,在运行梯度下降算法之前,进行特征缩放依旧是非常必要的
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]. 交叉熵代价函数(损失函数)及其求导推导