一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。tisp:这是一片关于多分类的文章,带你从引入问题到解决问题,到最后的实践,这些都是我自己学习和理解的过程,看法文章比较长如果你感兴趣请耐心看完,谢谢🙏
多分类问题
在深度学习里有个经典的数据集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(交叉熵损失函数)
二分类的损失函数:
(#无语掘金不能写公式或者是我不会)
多分类的情况就是在二分类的基础上扩展:
就是在二分类的基础上加一个求和
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数据集是由很多个28*28=784个像素的图片组成的,这些是灰阶图像所以是一维的数据
原始的图像
输入的图像计算机自然看不懂,要使用torchvision的工具的把图片映射转化成n维的Tensor数据
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize(0.1307, 0.3081, )])
数据准备和预处理
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.1307, 0.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(784, 512)#输入为一张图片的像素点总数28*28=784,MNIST
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冲量目的是能冲过鞍点和局部最优,优化更好
训练与测试
'''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()
运行结果:
准确率到达97%,97%刚算合格,在模型上还需改进
本文的重点是了解多分类问题和解决,学习到SOTFmax函数的原理和CEL交叉熵,最后也实现了应用,希望能帮到你。
看到这里可以帮我点个小小的赞👍