深度学习——多分类问题(SOFTmax ,CEL交叉熵函数)

3,916 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。tisp:这是一片关于多分类的文章,带你从引入问题到解决问题,到最后的实践,这些都是我自己学习和理解的过程,看法文章比较长如果你感兴趣请耐心看完,谢谢🙏

多分类问题

在深度学习里有个经典的数据集MNIST-dataset ,它是一个手写的数字照片集合,也是一个经典的做多分类的一个集合。

图片1.png 这个集合是由0~9 10个数组成的集合,我们需求是分析输入的数字对应真实数字的概率是多少,说明需要十个标签,十个分类。

解决这个问题很简单,在二分类的模型上的输出加到10个每个输出对应一个数字,这样可以得到每个数字对应的概率值,这里每个输出做的都是sigmoid二分类(即是非1即0),所以只要有一项输出为1时,其他非1的输出都规定为0,以此来判断。

图片2.png

但是这种情况下又会出现一个问题,每个sigmoid的输出都是独立的,当一个类别的输出概率较高时,其他类别的概率仍然会高,也就是说在输出了1的概率后,2输出的概率不会因为1的出现而受影响,这点说明了所有输出的概率值之和大于1。 所以我们希望输出的概括要满足分布性质的要求:

1、每个分类出现的概率都大于0

图片3.png

2、所有分类出现的概率的和为1

图片4.png 可以说多分类问题的每个输出需要有竞争性

增加SOFTmax Layer

图片5.png

为了解决以上两个问题需要对神经网络进行改进,在神经网络最终输出层加入Softmax Layer能很好的解决这两个问题

SOFTmax

定义:

Applies the Softmax function to an n-dimensional input Tensor rescaling them so that the elements of the n-dimensional output Tensor lie in the range [0,1] and sum to 1.

将Softmax函数应用于n维输入张量,重新缩放它们,使n维输出张量的元素位于[0,1]范围内,总和为1。 从定义看出Softmax输出的范围在[0,1]之内,然后总和为1,这两点能很好的解决我们的问题

函数:

图片6.png

SOFTmax函数运作流程步骤分析:

1、接收到Linear layer 的每个输出结果进行以e为底的指数幂运算,这样能确保所有的输出为正数

图片7.png 2、 把所有的输出进行exp运算后全部加和,然后除以每个输出的exp运算的结果,这样就能保证全部输出和为1,满足每个输出之间有竞争性

图片8.png 这两个过程完美解决前面的两个问题!

对分类的损失函数怎么做?

Cross Entropy Loss Function(交叉熵损失函数)

二分类的损失函数:

图片9.png

image.png

image.png(#无语掘金不能写公式或者是我不会)

多分类的情况就是在二分类的基础上扩展:

图片10.png 就是在二分类的基础上加一个求和

image.png

image.png

image.png

image.png

图片16.png

pytorch把Softmax和NLLLoss封装在Cross Entropy Loss 函数中

图片18.png

代码实现:

import torch
y = torch.LongTensor([0]) #这里的y需要长整型的张量
z = torch.Tensor([[0.99,0.1,-0.1]])
criterion = torch.nn.CrossEntropyLoss()
loss1 = criterion(z,y)
print(loss1)

这里代码上整体进行封装,所以输入的数据不需要激活只用做线性的处理

实践

用上面介绍的算法来训练MNIST数据集 MNIST数据集是由很多个28*28=784个像素的图片组成的,这些是灰阶图像所以是一维的数据

原始的图像 图片19.png 输入的图像计算机自然看不懂,要使用torchvision的工具的把图片映射转化成n维的Tensor数据

transform = transforms.Compose([transforms.ToTensor(),
                                transforms.Normalize(0.13070.3081, )])

图片20.png

数据准备和预处理

pytorch 直接提供了MNIST数据集的接口,在代码上即可调用

引入包:

import torch
'''为准备数据集导入的包'''
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
from torch.utils.data **import** DataLoader
'''做rule'''
import torch.nn.functional as f
'''做回归'''
import torch.optim as optim

数据准备及预处理

'''1、数据准备及处理'''
batch_size = 64
transform = transforms.Compose([transforms.ToTensor(),
                                transforms.Normalize(0.13070.3081, )])   # 把图片做预处理成张量,
# 用是希望数据满足平均分布(0,1)之间,第一个为均值,第二个为标准差,这两个值是MNIST这个数据均值和标准差,这都是前辈们算好的。
train_datast=datasets.MNIST(root=‘./’ train=True, download=True, transform=transform)  # 从torch中加载数据
train_loader=DataLoader(train_datast, shuffle=True, batch_size=batch_size)   # 实例化数据集

# 这里定义了测试数据集,用于测试
test_datast=datasets.MNIST(root='./', train=False, download=True, transform=transform)

test_loader=DataLoader(test_datast, shuffle=False, batch_size=batch_size)

制止模型

模型仅用了简单的线性变化,激活函数选择Relu

'''2、制作模型'''
class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.l1 = torch.nn.Linear(784512)#输入为一张图片的像素点总数28*28=784,MNIST
        self.l2 = torch.nn.Linear(512256)
        self.l3 = torch.nn.Linear(256128)
        self.l4 = torch.nn.Linear(12864)
        self.l5 = torch.nn.Linear(6410)
    def forward(self, x):
        x = x.view(-1784)
        x = 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)   # 最后一层没有非线性激活,因为CrossEntropyLoss里包装了SOFTmax函数,
# CrossEntropyLoss希望得到的是线性数据

model = Net()

损失函数和优化器

'''3、制作损失函数和优化器'''
criterion = torch.nn.CrossEntropyLoss()   # 交叉熵损失
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)   #momentum冲量目的是能冲过鞍点和局部最优,优化更好

训练与测试

'''4、设计训练 和 测试'''
def train(epoch):
     '''训练'''
    running_loss = 0.0
    for batch_idx, data in enumerate(train_loader, 0):
        inputs, target = data   # 取数据
        optimizer.zero_grad()   # 梯度清零
        outputs = model(inputs)   # 训练
        loss = criterion(outputs, target)   # 算损失
        loss.backward()   # 反向传播
        optimizer.step()   # 优化梯度
        running_loss += loss.item()
         # 累计loss ,这里要用item()取数据要不回构建计算图
        if batch_idx % 300 == 299:
             # 每300次输出一次loss
            print( '[%d,%5d] loss:%.3f' % (epoch + 1,batch_idx + 1, 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\
            outputs = model(images)\
            _, pred = torch.max(outputs.data, dim=1)\
             # 取每一行(dim=1)最大值(max)的下标(_,)及最大值(pred)
total += labels.size(0)   # 求预测的总数,label的形状为[N,1]
correct += (pred == labels).sum().item()   # pred(预测y) == labels(真值y)相等得1,然后求相等的个数
print( 'Accuracy on test set: %d %%' % (100 * correct / total))

运行:

if __name__ ==  '__main__' :
    for epoch in range(10):
        train(epoch)
        test()

运行结果:

图片21.png 准确率到达97%,97%刚算合格,在模型上还需改进

本文的重点是了解多分类问题和解决,学习到SOTFmax函数的原理和CEL交叉熵,最后也实现了应用,希望能帮到你。

看到这里可以帮我点个小小的赞👍

181649815898_.pic.jpg