pytorch从零开始学习5
多分类问题
1. 信息量
信息是用来消除随机不确定性的东西,即衡量信息量的大小就是看这个信息消除不确定性的程度。信息量的大小与信息发生的概率成反比。
设某一件事发生的概率为ZZZ,其信息量为
I
(
x
)
=
−
l
o
g
(
P
(
x
)
)
I(x)=-log(P(x))
I(x)=−log(P(x))
其中,
I
(
x
)
I(x)
I(x)表示信息量
2. 信息熵
信息熵用来表示所有信息量的期望
H
(
X
)
=
−
∑
P
(
x
i
)
l
o
g
(
P
(
x
i
)
)
(
X
=
x
1
,
x
2
,
.
.
.
,
x
n
)
H(X)=-\sum P(x_i)log(P(x_i))\\ (X=x_1, x_2,...,x_n)
H(X)=−∑P(xi)log(P(xi))(X=x1,x2,...,xn)
对于0-1分布问题,信息熵计算公式
H
(
X
)
=
−
∑
P
(
x
i
)
l
o
g
(
P
(
x
i
)
)
=
−
P
(
x
)
l
o
g
(
P
(
x
)
)
−
(
1
−
P
(
x
)
)
l
o
g
(
1
−
P
(
x
)
)
H(X)=-\sum P(x_i)log(P(x_i))\\ =-P(x)log(P(x))-(1-P(x))log(1-P(x))
H(X)=−∑P(xi)log(P(xi))=−P(x)log(P(x))−(1−P(x))log(1−P(x))
3. 相对熵(KL散度)
用来衡量两个概率分布之间的差异
D
K
L
(
p
∣
∣
q
)
=
∑
i
=
1
n
p
(
x
i
)
l
o
g
(
p
(
x
i
)
q
(
x
i
)
)
D_{KL}(p||q)=\sum _{i=1}^np(x_i)log(\frac{p(x_i)}{q(x_i)})
DKL(p∣∣q)=i=1∑np(xi)log(q(xi)p(xi))
在minist数据集的分类中,0-9的原始分布(预测1)为[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],预测分布[0.1, 0.5, 0.1, 0.05, 0.05, 0.01, 0.09, 0.1, 0.1]
KL散度越小,表示 P ( X ) P(X) P(X)与 Q ( X ) Q(X) Q(X)的分布更接近
4. 交叉熵
D K L ( p ∣ ∣ q ) = ∑ i = 1 n p ( x i ) l o g ( p ( x i ) q ( x i ) ) = ∑ i = 1 n p ( x i ) l o g ( p ( x i ) ) − ∑ i = 1 n p ( x i ) l o g ( q ( x i ) ) = − H ( p ( x ) ) + [ − ∑ i = 1 n p ( x i ) l o g ( q ( x i ) ) ] D_{KL}(p||q)=\sum _{i=1}^np(x_i)log(\frac{p(x_i)}{q(x_i)})\\ =\sum _{i=1}^np(x_i)log(p(x_i))-\sum _{i=1}^np(x_i)log(q(x_i))\\ =-H(p(x))+[-\sum _{i=1}^np(x_i)log(q(x_i))] DKL(p∣∣q)=i=1∑np(xi)log(q(xi)p(xi))=i=1∑np(xi)log(p(xi))−i=1∑np(xi)log(q(xi))=−H(p(x))+[−i=1∑np(xi)log(q(xi))]
KL散度=交叉撒-信息熵
H ( p , q ) = − ∑ i = 1 n p ( x i ) l o g ( q ( x i ) ) H(p,q)=-\sum _{i=1}^np(x_i)log(q(x_i)) H(p,q)=−i=1∑np(xi)log(q(xi))
交叉熵能够衡量不同概率分布的差异,能够表示真实概率分布与预测概率分布之间的差异。交叉熵的值越小,模型预测效果就越好
5.为什么在多分类问题中使用的损失函数是交叉熵损失
在二分类问题中,使用的是二分类交叉熵损失函数,而在多分类问题中使用的是交叉熵损失。我的理解:
无论是二分类还是多分类,都是从概率分布角度来进行预测。如果在多分类中使用的损失函数和二分类一样,那么会出现问题,例子:一个手写数字分类问题,当使用二分类损失函数时,如果出现,预测为9的概率为0.8,预测为8的概率为0.9,预测为7的概率为0.8,那么模型最终给出的结果为8,此时这个结果正确吗?答案是否定的,因为,为9和7的概率高达0.8这说明了模型很难将他们进行区分。对于损失函数的选择问题,理解不是很深刻,只能说简单的理解。
满足以下两点:
- 概率大于0
- 和为1
理解为什么optimizer.zero_grad()不能放在loss.backward()与optimizer.step()中间
因为,step()函数的作用是执行一次优化步骤,通过梯度下降法来更新参数的值。因为梯度下降是基于梯度的,所以在执行optimizer.step()函数前应先执行loss.backward()函数来计算梯度。optimizer只负责通过梯度下降进行优化,而不负责产生梯度,梯度是tensor.backward()方法产生的。所以,如果放在它们中间的话,梯度被清零,没有梯度,也就无法进行梯度更新,参数优化
import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim
# -------------------------------------------------------prepare data-------------------------------------------------------
batch_size = 64
# ------------------------------------------------------数据预处理------------------------------------------------------------
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.1307, ), (0.3081, ))])
train_dataset = datasets.MNIST(root='./data/mnist/', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
test_dataset = datasets.MNIST(root='./data/mnist/', train=False, download=True, transform=transform)
test_loader = DataLoader(test_dataset, shuffle=False, batch_size=batch_size)
# ----------------------------------------------------定义模型---------------------------------------------------------------
class Model(torch.nn.Module):
def __init__(self):
super(Model, self).__init__()
self.linear1 = torch.nn.Linear(784, 512)
self.linear2 = torch.nn.Linear(512, 256)
self.linear3 = torch.nn.Linear(256, 128)
self.linear4 = torch.nn.Linear(128, 64)
self.linear5 = torch.nn.Linear(64, 10)
def forward(self, x): # pytorch中输入数据的shape为[N,C,H,W], 分别是batch size,通道数,高度,宽度
out1 = x.view(-1, 784) # 相当于numpy中的resize
out2 = F.relu(self.linear1(out1))
out3 = F.relu(self.linear2(out2))
out4 = F.relu(self.linear3(out3))
out5 = F.relu(self.linear4(out4))
out6 = self.linear5(out5) # 最后一层不使用激活函数
return out6
# ----------------------------------------------------实例化模型--------------------------------------------------------------
model = Model()
# ------------------------------------------------------损失函数与优化器定义---------------------------------------------------
criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
# -------------------------------------------将一次epoch单独封装成一个函数------------------------------------------------------
def train(epoch):
running_loss = 0.0
# 下面两种放置optimizer.zero_grad()位置均可以,只要不将optimizer.zero_grad()放在loss.backward()与optimizer.step()之间就行
# for batch_idx, data in enumerate(train_loader, 0): # enumerate返回(idx,(data,label))
# inputs, target = data
# pred_data = model(inputs)
# loss = criterion(pred_data, target)
#
# optimizer.zero_grad()
#
# loss.backward()
# optimizer.step()
# 我的疑问解惑:train_loader是如何在每次循环中获得相应的batch size dataset(每次循环,这里的train_loader都不同)
for batch_idx, data in enumerate(train_loader, 0): # enumerate返回(idx,(data,label))
inputs, target = data
optimizer.zero_grad()
pred_data = model(inputs)
loss = criterion(pred_data, target)
loss.backward()
optimizer.step()
running_loss += loss.item()
if batch_idx % 300 == 299: # 与299比较是因为python计数是从0开始
print('[%d, %5d] loss: %.3f' % (epoch+1, batch_idx+1, running_loss/300))
# 这里为什么要将running_loss置零--->因为这里running_loss的作用是为了计算每300个样本的损失,
# 即不是作为一个整体的计算值,甚至可以理解为它“无关紧要”,只是为了用户知道在这几百个样本中损失是多少
running_loss = 0.0
# ------------------------------------------------将一次测试单独封装成一个函数---------------------------------------------------
def test():
correct = 0
total = 0
with torch.no_grad(): # 不计算梯度,不构建计算图
for data in test_loader:
images, labels = data
pred_data = model(images)
_, predicted = torch.max(pred_data, dim=1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('accuracy on test set: %d %% ' % (100 * correct / total))
if __name__ == '__main__':
for epoch in range(10):
train(epoch)
test()