ML

交叉熵损失

交叉熵损失(也称为对数损失)衡量分类模型的性能,其输出是介于0011之间的概率值。当预测概率与实际标签的差异增大时,损失也会增加。

对于二分类问题,交叉熵损失可以定义为:

L=1Ni=1N[yilog(pi)+(1yi)log(1pi)]L = -\frac{1}{N} \sum_{i=1}^{N} \left[ y_i \log(p_i) + (1 - y_i) \log(1 - p_i) \right]

其中:

  • $ N $ 是样本数量
  • $ y_i $ 是实际标签(0011
  • pip_i 是样本属于类别11的预测概率

对于多分类问题,交叉熵损失可以推广为:

L=i=1Nc=1Cyi,clog(pi,c)L = -\sum_{i=1}^{N} \sum_{c=1}^{C} y_{i,c} \log(p_{i,c})

其中:

  • CC 是类别数量
  • yi,cy_{i,c} 是一个二进制指示器(0011),如果类别标签 cc 是样本 ii 的正确分类
  • pi,cp_{i,c} 是样本 ii 属于类别 cc 的预测概率

交叉熵损失在机器学习中广泛用于训练分类模型,尤其是在神经网络中。

1
2
3
4
5
6
7
8
9
10
def cross_entropy(y_hat, y):\
"""交叉熵"""
return - torch.log(y_hat[range(len(y_hat)), y])

def accuracy(y_hat, y): #@save
"""计算预测正确的数量"""
if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
y_hat = y_hat.argmax(axis=1)
cmp = y_hat.type(y.dtype) == y
return float(cmp.type(y.dtype).sum())/len(y)

Softmax 函数

Softmax 函数是一种激活函数,常用于多分类问题的输出层。它将未归一化的输出(称为 logits)转换为归一化的概率分布。

Softmax 函数的定义如下:

σ(z)i=ezij=1Cezj\sigma(z)_i = \frac{e^{z_i}}{\sum_{j=1}^{C} e^{z_j}}

其中:

  • zz 是输入向量
  • ziz_i 是输入向量 zz 的第 ii 个分量
  • CC 是类别数量
  • σ(z)i\sigma(z)_i 是输入 zz 对应类别 ii 的概率

Softmax 函数的输出是一个概率分布,其所有元素的和为 11。这使得它非常适合用于多分类问题的模型输出。

在神经网络中,Softmax 函数通常与交叉熵损失一起使用,以优化模型的分类性能。

交叉熵损失对 Softmax 函数的求导

L=j=1nyjlog(pj)=L=-\sum_{j=1}^{n}y_jlog(p_j)=

j=1nyjlogexp(oj)k=1nexp(ok)=-\sum_{j=1}^{n}y_jlog\frac{exp(o_j)}{\sum_{k=1}^nexp(o_k)}=

j=1nyjlogk=1nexp(ok)j=1nyjoj=\sum_{j=1}^ny_jlog\sum_{k=1}^nexp(o_k) - \sum_{j=1}^ny_jo_j=

logk=1nexp(ok)j=1nyjojlog\sum_{k=1}^nexp(o_k) - \sum_{j=1}^ny_jo_j

Loj=exp(oj)k=1nexp(ok)yj=softmax(oj)yj\frac{\partial L}{\partial o_j}=\frac{exp(o_j)}{\sum_{k=1}^{n} exp(o_k)}-y_j=softmax(o_j)-y_j

其中:

  • oo是估计值,yy 是预测值,loglog 是以 ee为底数

Jacobian Matrix

1
2
3
4
5
6
7
x = torch.tensor([-1, 2, 3, 4], dtype=torch.float32, requires_grad=True)
y = x.softmax(dim=0)
print(y)
y[0].backward()
print(x.grad)
#tensor([0.0045, 0.0896, 0.2436, 0.6623], grad_fn=<SoftmaxBackward0>)
#tensor([ 0.0044, -0.0004, -0.0011, -0.0030])

其中:

yi=exp(xi)jexp(xj)y_i=\frac{exp(x_i)}{\sum_j exp(x_j)}

1
2
3
4
5
6
7
8
u = torch.tensor(3.0, requires_grad=True)
v = torch.tensor(4.0, requires_grad=True)
f = u * v + u**2 + v**3
f.backward()
print(u.grad) # 对u求导
print(v.grad) # 对v求导
#tensor(10.)
#tensor(51.)
1
2
3
4
5
6
7
8
9
10
11
12
13
x = torch.rand(size=(3, 3), requires_grad=True)
w1 = torch.rand(size=(3, 3), requires_grad=True)
w2 = torch.rand(size=(3, 3), requires_grad=True)
w3 = torch.rand(size=(3, 3), requires_grad=True)
w4 = torch.rand(size=(3, 3), requires_grad=True)
y = x * w1
z = x * w2
f = y + z
g = f * w3
g.sum().backward()
print(x.grad) #对x求导
print(w3 * (w1 + w2))

1
2
3
4
5
6
7
8
9
10
11
12
13
x = torch.tensor([1, 2, 3, 4], dtype=torch.float32, requires_grad=True)
y = 100 * x
loss = nn.MSELoss()
optimizer = torch.optim.SGD([x], lr=0.01)
optimizer.zero_grad()
y.sum().backward()
print(x)
print(x.grad)
optimizer.step()
print(x)
#tensor([1.0, 2.0, 3.0, 4.0], requires_grad=True)
#tensor([100.0, 100.0, 100.0, 100.0])
#tensor([2.2352e-08, 1.0000e00, 2.0000e00, 3.0000e00], requires_grad=True)