当前位置: 代码迷 >> 综合 >> Pytorch -----多分类问题 (Softmax Classifier)原理和代码实现,损失函数NLLLoss和CrossEntropyLossr的用法和区别。
  详细解决方案

Pytorch -----多分类问题 (Softmax Classifier)原理和代码实现,损失函数NLLLoss和CrossEntropyLossr的用法和区别。

热度:89   发布时间:2023-12-26 11:13:30.0

之前的分类都是只有两个分类,是或者不是。

今天学一下多分类问题,比如下面这个图。识别这个图中的数字,当输出结果的时候 有 0-9 十个分类结果。

在这里插入图片描述
比如第一个数字5,经过训练输出可能是 P(Y=5) = 0.9 ,即理解为等于5的概率是90%。
但这样有一个问题,如果这个数字特别模糊,可能出现的情况是P(Y=1) = 0.8 ,P(Y=2) = 0.8 , P(Y=5) = 0.9 ,也就是说,这个数字是1的概率是0.8,是2的概率也是0.8,是5的概率是0.9。这样的结果并不利于训练结果的分类。
所以我们让所有结果符合概率要求,即全部概率相加 = 1。输出一个分布。

用于训练的神经网络中间依旧使用 Sigmoid,最后输出10个概率的分布使用 softmax。
在这里插入图片描述
Softmax 函数计算公式:
在这里插入图片描述

这里的 Zi 就是最后一层线性层(上图蓝色的层)的输出值,给softmax层的值。线性层输出的值是一般值,还不是概率值。经过sotfmax层之后才能变成概率值。
分子上面的e的指数形式。必然大于0.
分子下面的求和。就是e的每一项都除以全部的e的Zi次方的和。.
在这里插入图片描述
等价于
在这里插入图片描述
显然这样操作之后,每一项单独的都除以全部总和,保证了结果的值之和为1.

损失函数:
在这里插入图片描述
计算过程代码实现:
在这里插入图片描述
这里的y就是值真实值,z就是 最后一层的线性层给Softmax的值。
先用 softmax 的公式算出来 y(预测值)。
然后用上面的损失函数算出来y的损失值。

在Pytorch中使用:
这条函数包括了上面的softmax算预测值和算损失值的全部过程。
在使用CrossEntropyLossr的时候,最后一层线性层不要做非线性变换,就是乘以那个α 或 sigmoid激活函数。这条函数(交叉熵)会自动帮你激活。
在这里插入图片描述
关于上面的整体流程可以用下面这张图表示:
在这里插入图片描述
课上老师问了一个问题 就是 两个损失函数 NLLLoss和CrossEntropyLossr的区别。

其实看到这应该能知道,CrossEntropyLossr交叉熵损失函数是整体计算的。就是从最后一层的线性层开始计算到最后,里面包含了softmax.在这里插入图片描述
而NLLLoss仅仅做了最后一步:在这里插入图片描述
NLLLoss 对数似然损失函数(log-likehood loss function) :
在这里插入图片描述
其中,ak表示第k个神经元的输出值,yk表示第k个神经元对应的真实值,取值为0或1。

CrossEntropyLossr = softmax + NLLLoss

回到刚开始的那个数字图像。拿出第一个数字。
该图像由28*28的矩阵像素点构成。颜色深浅由0-255表示,映射到0-1.每个矩阵中的值为0-1,表示该点的颜色的深浅。
在这里插入图片描述

代码实现过程还是之前的四步,
1.数据准备 2.设计模型类 3.选择优化器和损失函数 4. 循环训练

其中用到的包:
在这里插入图片描述

用于图像的处理。
在这里插入图片描述
不在使用之前的sigmoid了 使用 relu()作为激活函数。
在这里插入图片描述
数据准备:

在这里插入图片描述
datasets.MNIST参数:

train=True 代表我们读入的数据作为训练集(如果为true则从training.pt创建数据集,否则从test.pt创建数据集)

download=True则是当我们的根目录(root)下没有数据集时,便自动下载。

python3使用pillow处理图片,
将输入进来的图像(0-255像素值 28 * 28)值变为图像张量(映射0-1像素值 1 * 28 * 28)。
1 * 28 * 28 通道 * 宽 * 高

灰白图成为单通道,
读进来的为 W * H * C 变为 C * W * H之后交给Pytorch处理。
在这里插入图片描述
下面这个函数就是做变换处理的。
在这里插入图片描述
下面这个函数后面的两个参数, 均值(mean) 标准差(std)。
这俩值并不是固定的,而是根据图像算出来的,不过这个图比较经典,所以就用这俩最好。
在这里插入图片描述
模型训练:
在这里插入图片描述
下图表示输入N个样本,1维 28 *28 。
在这里插入图片描述

全连接神经网络要求输入样本二维矩阵。
使用view函数。改变张量形状。
变成二维矩阵。第一个数为 -1 表示自动计算。
在这里插入图片描述
所以第一层拿到的数据是N * 784的矩阵

注意,最后一层 self.15(x) 不需要relu()函数激活,直接交给后面的softmax。

优化器和损失函数:
在这里插入图片描述

本次将训练和测试封装到函数中:
训练:
在这里插入图片描述
测试:
在这里插入图片描述
其中:
下图中的代码不会计算后面的梯度,因为已经是测试数据了。就是要测试前面训练好的权重W,所以不需要再次计算梯度。
在这里插入图片描述
首先拿测试数据去计算预测值,
然后取出来预测值每一行中的最大值,因为最大值才是我们要的那个最大概率的分类嘛。dim表示沿着那个维度去找。
ps:想一下!~组成二维数组中的一维数组是0号维度,一维数组中的值是1号维度。

torch.max:
这里拿到的预测值 每一行都对应10个分类,这10个分类就是该样本对应0-9的概率, 我们要拿到最大的那个概率和其对应的下标。
dim=1 就是从每一个一维数组中找到最大的值和其对应下标。
返回值是两个,最大值是多少,其下标是多少。
在这里插入图片描述
下面的 labels就是我们的初始的y数据,应该是一个N *1的矩阵,N就是一共有多少个样本。所以size就是元组(N,1).去0号元素,自然拿到的就是样本数。
在这里插入图片描述

下面的 == 号 就是在比较真实值和预测值之间的差距,相等就是1不相等就是0,然后求和 吧数值拿出来。
等所有的数都跑完了,看正确的数除以总数,就能得到正确率了。
在这里插入图片描述

主函数:
十轮训练+测试
在这里插入图片描述

完整代码:

import torch
from torch import optim
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as Fbatch_size = 64
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.1307,), (0.3081,))
])train_dataset = datasets.MNIST(root='../dataset/mnist/', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
test_dataset = datasets.MNIST(root='../dataset/mnist/', train=False, download=True, transform=transform)
test_loader = DataLoader(test_dataset, shuffle=False, batch_size=batch_size)# 模型类设计
class DiabetesDataset(torch.nn.Module):def __init__(self):super(DiabetesDataset, self).__init__()self.l1 = torch.nn.Linear(784, 512)self.l2 = torch.nn.Linear(512, 256)self.l3 = torch.nn.Linear(256, 128)self.l4 = torch.nn.Linear(128, 64)self.l5 = torch.nn.Linear(64, 10)def forward(self, x):x = x.view(-1, 784)  # -1就是自动获取mini_batchx = F.relu(self.l1(x))x = F.relu(self.l2(x))x = F.relu(self.l3(x))x = F.relu(self.l4(x))return self.l5(x)  # 最后一层不做激活,不进行非线性变换model = DiabetesDataset()# 损失函数
criterion = torch.nn.CrossEntropyLoss()
# 优化器
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)def train(epoch):runing_loss = 0.0for i, data in enumerate(train_loader):x, y = data# 清零 正向传播 损失函数 反向传播 更新optimizer.zero_grad()y_pre = model(x)loss = criterion(y_pre, y)loss.backward()optimizer.step()runing_loss += loss.item()
# 每轮训练一共训练1W个样本,这里的runing_loss是1W个样本的总损失值,要看每一个样本的平均损失值, 记得除10000print("这是第 %d轮训练,当前损失值 %.5f" % (epoch+1, runing_loss/10000))def test(epoch):correct = 0total = 0with torch.no_grad():for data in test_loader:x, y = datapre_y = model(x)# 这里拿到的预测值 每一行都对应10个分类,这10个分类都有对应的概率,# 我们要拿到最大的那个概率和其对应的下标。j, pre_y = torch.max(pre_y.data, dim=1)  # dim = 1 列是第0个维度,行是第1个维度total += y.size(0)  # 统计方向0上的元素个数 即样本个数correct += (pre_y == y).sum().item()  # 张量之间的比较运算print("第%d轮测试结束,当前正确率:%d %%" % (epoch+1,correct / total * 100))if __name__ == '__main__':for epoch in range(10):train(epoch)test(epoch)

看看结果:

这是第 1轮训练,当前损失值 0.104401轮测试结束,当前正确率:89 %
这是第 2轮训练,当前损失值 0.025632轮测试结束,当前正确率:94 %
这是第 3轮训练,当前损失值 0.016363轮测试结束,当前正确率:95 %
这是第 4轮训练,当前损失值 0.011994轮测试结束,当前正确率:96 %
这是第 5轮训练,当前损失值 0.009315轮测试结束,当前正确率:96 %
这是第 6轮训练,当前损失值 0.007456轮测试结束,当前正确率:97 %
这是第 7轮训练,当前损失值 0.006037轮测试结束,当前正确率:97 %
这是第 8轮训练,当前损失值 0.004978轮测试结束,当前正确率:97 %
这是第 9轮训练,当前损失值 0.004009轮测试结束,当前正确率:97 %
这是第 10轮训练,当前损失值 0.0033310轮测试结束,当前正确率:97 %