Pytorch学习笔记第九课多分类问题CrossEntropyLoss和Softmax

笔记来源于B站up主,@刘二大人

视频链接09.多分类问题_哔哩哔哩_bilibili

感谢老师和其他社区创作者

前面提到的二分类问题是简单的判断是/否,通过Linear Layer将多特征的输入进行线性变化,最终变为一个输出,然后使用sigmoid函数将其变为一个分布在0~1之间的概率分布y_hat,可以以0.5为分界,将y_hat的结果分为是(>0.5)/否(<0.5)

 

 本次课程探讨多分类问题

以MNIST数据集为例,里面有手写数字0-9,共10个分类,那么随机输入一个手写数字,如何判断其是0-9中的哪一类呢?

输入层的数据经过Linear Layer进行线性变化,再经过Sigmoid层变为0-1之间的数值,但此时各个输出o_1\sim o_{10}之前各自独立,我们希望他们的概率能同属于一个分布,所有P(y=i)≥0且所有的P(y=i)求和能等于1,那么所有概率中哪一类的概率最大,就可以说y_hat判断是哪一类,此时用到Softmax函数。

Softmax函数如下:

通过将Sigmoid输出的结果z求e的指数,使得所有的P都实现了P(y=i)≥0

又通过将所有求过e指数的结果累加作为分母实现了所有P(y=i)求和等于1

具体例子说明:

假设输出经过某个激活函数后输出为[0.2, 0.1, -0.1],经过Softmax函数后变为[0.38, 0.34, 0.28]

合计概率为1,这三个输出结果就是一个分布,能够更方便的做损失函数。

对标签采用独热编码,即标签指示的分类项为1,其余全为0,采用损失函数NLLLoss计算损失。

如此一来就只会计算标签项与对应的y_hat的损失,其余项损失全为0;

如图所示为例子:

y_hat求出0.38表示第一项的概率最大,那么对应标签正好为第一项为1,计算loss_1 = -log0.38=0.42

假设标签对应的是第二项,即0 1 0,那么计算

loss _2= -log0.34=0.47

对比loss_1loss_2可知,如果预测的越准确,那么loss值将会越小。

=================================================

由log曲线可知,当y_hat<0时,y_hat越大,-log(y_hat)越小 

=================================================

交叉熵损失函数CrossEntropyLoss就是Softmax+log+NLLLoss

所以使用交叉熵损失的时候,网络最后一层的输出项无需采用激活函数

对MINIST数据集进行分类,代码如下:

# 多分类问题交叉熵损失
import torch
from torch.utils.data import DataLoader
from torchvision import transforms, datasets

batch_size = 64
# 对输入的图像进行PIL转Tensor,并进行归一化处理
transform = transforms.Compose([
    transforms.ToTensor(),
    # 归一化处理,(均值,方差)  (输入-均值)/方差
    transforms.Normalize(0.1307, 0.3081)
])

# 数据集准备
train_dataset = datasets.MNIST(root=r'H:\chenpytorch\data', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_dataset = datasets.MNIST(root=r'H:\chenpytorch\data', train=False, download=True, transform=transform)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)


# 设计网络模型
class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.model = torch.nn.Sequential(
            torch.nn.Linear(784, 512),
            torch.nn.ReLU(),
            torch.nn.Linear(512, 256),
            torch.nn.ReLU(),
            torch.nn.Linear(256, 128),
            torch.nn.ReLU(),
            torch.nn.Linear(128, 64),
            torch.nn.ReLU(),
            torch.nn.Linear(64, 10)
        )

    def forward(self, x):
        # 使用view将图片进行变形
        # 输入的图片是(batch_size,1,28,28),要将像素全部展开成一行,1*28*28=784
        # (-1,784)值根据输入,将数据转为784行后自动计算batch_size,batch_size*1*28*28/784=batch_size
        x = x.view(-1, 784)
        x = self.model(x)
        return x


model = Net()

criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5)

# 定义训练函数
def train(epoch):
    train_loss = 0.0
    for batch_idx, data in enumerate(train_loader,0):
        inputs, labels = data
        optimizer.zero_grad()
        outputs = model(inputs)

        loss = criterion(outputs,labels)
        loss.backward()
        optimizer.step()

        train_loss += loss.item()
        if batch_idx %300 == 299:
            print('epoch:{} batch_idx:{}  loss:{}'.format(epoch+1, batch_idx, train_loss/300))
            train_loss = 0.0


def test():
    correct = 0
    total = 0
    with torch.no_grad():
        for data in test_loader:
            images,labels = data
            outputs = model(images)
            _,predicted = torch.max(outputs.data, dim=1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    print('accuracy on test_dataset:{}'.format(correct/total))

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

结果:

部分代码解释:

一、

对于神经网络而言,希望输入的数据越小越好,希望的像素值能满足[0,1]分布,并且最好是均值为0方差为1的正态分布

transforms.Totensor()

将读入的PIL图像转化为Tensor类型的图像

PIL数据为WHC(宽 高 通道数),转为Tensor的CWH(通道 宽 高)

原来的像素为0-255,是三通道彩色的RGB图,压缩到像素分布为0-1的浮点数

transforms.Normalize()归一化处理

如果是Normalize((0.5,0.5,0.5),(0.5,0.5,0.5)),是归一化到【-1,1】
如果是Normalize(channel_mean, channel_std),才是均值为0,标准差为1的正态分布

【精选】pytorch初学笔记(四):常见的Transforms使用(ToTensor、Normalize、Resize、Compose、RandomCrop)_python totensor_好喜欢吃红柚子的博客-CSDN博客

# 对输入的图像进行PIL转Tensor,并进行归一化处理
transform = transforms.Compose([
    transforms.ToTensor(),
    # 归一化处理,(均值,方差)  (输入-均值)/方差
    transforms.Normalize(0.1307, 0.3081)
])

二、

x = x.view(-1, 784) 

x = x.view(-1, 784) 将输入数据x转化为64行784列的矩阵

因为已知原来的像素为28*28,将它展为一条后即为784列,前面的-1求出来的是batch_size为64

view()的作用相当于numpy中的reshape,重新定义矩阵的形状

view中一个参数定为-1,代表动态调整这个维度上的元素个数,以保证元素的总数不变。

python中view()函数怎么用?_view函数-CSDN博客

三、

torch.max()的使用    output = torch.max(input, dim)

分别用下划线“_空”来接收outputs中最大的数据,用predicted接收最大数据对应的索引

因为outputs输出的就是y_hats预测值,是一个64(batch_size)行,10(class类别)列的矩阵,通过dim=1对行求最大值索引,可以得到预测可能性最大的类。

_, predicted = torch.max(outputs.data, dim=1)

torch.max()使用讲解_torch.max accuracy 准确率评估-CSDN博客

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值