深度学习——多分类问题

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

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

多分类问题

逻辑图

  

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

         但是这种情况下出现一个问题,每个sigmoid的输出都是独立的,当一个类别的输出概率较高时,其他类别的概率仍然会高,也就是说在输出了1的概率后,2输出的概率不会因为1的出现而受影响,这点说明了所有输出的概率值之和大于1。

所以我们希望输出的概括要满足分布性质的要求:

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

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

多分类问题的每个输出需要有竞争性

增加SOFTmax Layer

为了解决以上两个问题需要对神经网络进行改进在神经网络最终输出层加入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,这两点能很好的解决我们的问题

函数:

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

  1. 接收到Linear layer 的每个输出结果进行以e为底的指数幂运算,这样能确保所有的输出为正数
  2. 把所有的输出进行exp运算后全部加和然后除以每个输出的exp运算的结果这样就能保证全部输出和为1,满足每个输出之间有竞争性

损失函数怎么做

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

1、二分类的损失函数:

 

其中:
 —— 表示样本 i的label,正类为1,负类为0
—— 表示样本 i预测为正类的概率

在二分的情况下,模型最后需要预测的结果只有两种情况,对于每个类别我们的预测得到的概率为 或者 1-

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

就是在二分类的基础上加一个求和

M——类别的数量

——符号函数(0或1),如果样本i的真实类别等于c取1,否 则取0

——观测样本i属于类别c的预测概率

        由于上述计算过程中非0即1,且有且只能有一个1,因此一个样本所有分类的loss计算过程可以简化为: 

 

代码实现:

import numpy as np
y = np.array([1,0,0])
z = np.array([0.99,0.1,-0.1])
y_pead = np.exp(z) / np.exp(z).sum()
loss = (-y*np.log(y_pead)).sum()
print(loss)

在pytorch中后面这段被称为NLLLoss函数

 

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

代码改变如下:

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数据集

MNIST数据集是由很多个28*28=784个像素的图片组成的,这些是灰阶图像所以是一层三维的数据,

原始图像

输入的图像计算机看不懂,把图片用torchvision的工具的映射转化成一维Tensor数据

对图像做映射(可视化)

代码实现

引入包:

import torch
'''为准备数据集导入的包'''
from torchvision import transforms
from torchvision import datasets
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.1307, 0.3081, )])  # 把图片处理成张量,
# 用Normalize是希望数据满足平均分布(0,1)之间,第一个为均值,第二个为标准差,这两个值是MNIST这个数据均值和标准差,这都是前辈们算好的。
train_datast = datasets.MNIST(root='./', train=True, 
download=True, transform=transform)  # 加载数据,实例化数据集

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(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)
        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冲量目的是能冲过鞍点和局部最优,优化更好

​​​​​​​

训练与测试:

训练​​​​​​​:

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

测试:

1、不需要进行反向传播,不需要计算梯度

2、只需要计算分类正确率

这行代码会让下面的循环不计算梯度

with torch.no.grad()
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))

预测后会得到一个结果的矩阵,我们需要用torch.max函数找最大值还有最大值的下标,拿推测的结果和原始的数据进行张量之间的比较运算 (相等为真(1),不等为假(0)),再行求和算概率,得到我想要的概率值。

运行:

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

运行结果:

训练10次准确率达到97% 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐