Pytorch之Softmax多分类任务

上一篇文章中,笔者介绍了什么是Softmax回归及其原理。因此在接下来的这篇文章中,我们就来开始动手实现一下Softmax回归,并且最后要完成利用Softmax模型对Fashion MINIST进行分类的任务。在开始实现Softmax之前,我们先来了解一下Fashion MINIST这一数据集。

1 数据集

1.1 FashionMNIST

数据集FashionMNIST虽然名字里面有’MNIST’这个词,但是其与手写体识别一点关系也没有,仅仅只是因为FashionMNIST数据集在数据集规模、类别数量和图片大小上与MINIST手写体数据集一致。

图 1. Fashion MINIST数据集

如图1所示便为Fashion MNIST数据集的部分可视化结果,其包含有训练集6万张和测试集1万张,每张图片的大小为[28,28]。在Pytorch中,我们可以通过如下代码对其进行载入:

def load_dataset():
    mnist_train = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST', 	train=True, download=True,transform=transforms.ToTensor())
    mnist_test = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST', 		train=False, download=True,transform=transforms.ToTensor())
    return mnist_train, mnist_test

其中参数root表示指定数据集的保存目录;train表示返回训练集还是测试集;download表示数据集不存在时是否需要下载;transform表示指定一种转换方法,而指定transforms.ToTensor()就是将尺寸为(H x W x C)且数据位于[0,255]的PIL图片或者数据类型为np.unit8的numpy数组转换为尺寸为(C x H x W)且数据类型为torch.float32,范围在 [ 0.0 , 1.0 ] [0.0,1.0] [0.0,1.0]Tensor

同时,我们还可以通过代码image= mnist_train[0][0]label=mnist_train[0][1]来分别访问一张图片和其对应的标签值。

1.2 构造数据集

在模型实际训练过程中,数据读取经常是训练的性能瓶颈。同时,为了能够更好的训练模型我们通常会对数据进行打乱,以及分批(batch)的将数据输入的模型中。在Pytorch中,我们可以通过DataLoader这个类来方便的完成上述功能。

start = time.time()
train_iter = torch.utils.data.DataLoader(mnist_test, batch_size=1024, shuffle=True, num_workers=2)
for x_test, y_test in train_iter:
    print(x_test.shape)
    print('%.2f sec' % (time.time() - start))

#结果
torch.Size([1024, 1, 28, 28])
torch.Size([1024, 1, 28, 28])
torch.Size([1024, 1, 28, 28])
torch.Size([1024, 1, 28, 28])
torch.Size([1024, 1, 28, 28])
torch.Size([1024, 1, 28, 28])
torch.Size([1024, 1, 28, 28])
torch.Size([1024, 1, 28, 28])
torch.Size([1024, 1, 28, 28])
torch.Size([784, 1, 28, 28])
2.60 sec

其中batsh_size表示指定每次返回batsh_size个样本;shuffle=True表示对数据集进行打乱;num_workers=2表示用两个进程来读取数据。

但需要注意的是,这里的数据集mnist_test是Pytorch内置的,那如果是我们自己读入的数据集该怎么使用DataLoader呢?我们只需要首先将自己的数据集转换成tensor,然后再通过TensorDataset这个类来构造一个数据集即可。

def make_dataset():
    x = torch.linspace(0, 100, 100, dtype=torch.float32).reshape(-1, 2)
    y = torch.randn(50 )
    dataset = torch.utils.data.TensorDataset(x, y)
    return dataset

此时返回的dataset数据集也就同样能够通过DataLoader进行读取。

## 2 Softmax多分类

在实现这个分类模型之前,我们先来介绍一下几个需要用到的函数。

2.1 softmax计算实现

在上一篇文章中我们介绍了softmax的计算公式,其实现可以通过如下代码来完成:

def softmax(x):
    s = torch.exp(x)
    return s / torch.sum(s, dim=1, keepdim=True)# 此处触发了广播机制

a = torch.tensor([[1,2,3.],[2,3,1.]])
print(softmax(a))
#结果:
tensor([[0.0900, 0.2447, 0.6652],
        [0.2447, 0.6652, 0.0900]])

其中torch.exp()为计算每个元素的指数次方;sum(s, dim=1, keepdim=True)表示计算得到每一行的和;最后是按位除操作。需要注意的是传入的x必须是浮点类型的,不然会报错。

2.2 交叉熵计算实现

假设我们现在有两个样本,其预测得到的概率分布为[0.1,0.3,0.6][0.5,0.4,0.1]。同时,正确的标签分布为[0,0,1][0,1,0],则对应的交叉熵为 − ( 1 ⋅ log ⁡ 0.6 + 1 ⋅ log ⁡ 0.4 ) -(1\cdot\log{0.6}+1\cdot\log{0.4}) (1log0.6+1log0.4)。但是,我们在用代码实现的时候完全不用这么麻烦,只需要通过正确的标签找到预测概率分布中对应的值,再取对数即可。

例如[0,0,1][0,1,0]这两个真实分布对应的标签就应该是2和1(从0开始),因此我们只需要分别取[0.1,0.3,0.6][0.5,0.4,0.1]中第2个元素0.6和第1个原始0.4,再取对数就能实现交叉熵的计算。

上述过程通过如下代码便可完成:

def crossEntropy(logits,y):
    c = -torch.log(logits.gather(1,y.reshape(-1,1)))
    return torch.sum(c)# 注意这里返回的是和

logits = torch.tensor([[0.1, 0.3, 0.6], [0.5, 0.4, 0.1]])
y = torch.LongTensor([2, 1])
c = crossEntropy(logits,y)
print(c)

#结果
tensor(1.4271)

其中.gather()就是根据指定维度和索引,选择对应位置上的元素。同时,需要注意的是logits的每一行为一个样本的概率分布,因此我们需要在行上进行索引操作,故gather()的第一个参数应该是1,这一点一定要注意。

2.3 准确率计算实现

在前面介绍softmax时说到,对于每个样本的预测类别,我们会选择对应概率值最大的类别作为输出结果。因此,在计算预测的准确率时,我们首先需要通过torch.argmax()这个函数来返回预测得到的标签。

y_true = torch.tensor([[2,1]])
logits = torch.tensor([[0.1,0.3,0.6],[0.5,0.4,0.1]])
y_pred = logits.argmax(1)
print(y_pred)

#结果
tensor([2, 0])

最后,我们将预测得到的标签同正确标签进行对比即可求得准确率。

def accuracy(y_true,logits):
    acc = (logits.argmax(1) == y_true).float().mean()
    return acc.item()

print(accuracy(y_true,logits))
#结果
0.5

2.4 评估模型

一般我们训练得到一个模型后都需要对其在测试集上进行评估,也就是在测试集上计算其总的准确率。因此,我们首先需要计算得到所有预测对的样本(而不仅仅只是一个batch),然后再除以总的样本数即可。

def evaluate(data_iter, forward, input_nodes, w, b):
    acc_sum, n = 0.0, 0
    for x, y in data_iter:
        logits = forward(x, input_nodes, w, b)
        acc_sum += (logits.argmax(1) == y).float().sum().item()
        n += len(y)
    return acc_sum / n

2.5 分类模型实现

w = torch.tensor(np.random.normal(0, 0.5, [input_nodes, output_nodes]),
                 dtype=torch.float32, requires_grad=True)
b = torch.tensor(np.random.randn(output_nodes), dtype=torch.float32, requires_grad=True)
for epoch in range(epochs):
    for i, (x, y) in enumerate(train_iter):
        logits = forward(x, input_nodes, w, b)
        l = crossEntropy(y, logits)
        l.backward()
        gradientDescent([w, b], lr)
        acc = accuracy(y, logits)
        if i % 50 == 0:
            print("Epoches[{}/{}]---batch[{}/{}]---acc{:.4}---loss {:.4}".format(
                epoches, epoch, len(mnist_train) // batch_size, i, acc,l))
            acc = evaluate(test_iter, forward, input_nodes, w, b)
            print("Epoches[{}/{}]--acc on test{:.4}".format(epochs, epoch, acc))
# 结果:
Epochs[8000/20]--acc on test0.8323
Epochs[8000/21]---batch[468/0]---acc0.8516---loss 47.13
Epochs[8000/21]---batch[468/50]---acc0.8203---loss 67.22
Epochs[8000/21]---batch[468/100]---acc0.9219---loss 38.74
Epochs[8000/21]---batch[468/150]---acc0.8516---loss 57.39
Epochs[8000/21]---batch[468/200]---acc0.8281---loss 74.76
Epochs[8000/21]---batch[468/250]---acc0.8672---loss 55.32
Epochs[8000/21]---batch[468/300]---acc0.8281---loss 60.19

可以看到,大约20轮迭代后,softmax模型在测试集上的准确率就达到了0.83左右。

3 总结

在这篇文章中,笔者首先介绍了FashionMNIST数据集。然后接着介绍了如何使用Pytorch中的DataLoader来构造训练数据迭代器。最后,介绍了如何通过Pytorch来一步步的实现Softmax分类模型,包括如何实现softmax操作、如何快捷的计算交叉熵、如何计算模型的准确率等等。本次内容就到此结束,感谢您的阅读!

本次内容就到此结束,感谢您的阅读!如果你觉得上述内容对你有所帮助,欢迎关注并传播本公众号!若有任何疑问与建议,请添加笔者微信’nulls8’加群进行交流。青山不改,绿水长流,我们月来客栈见!

引用

[1]动手深度学习

[2]示例代码:https://github.com/moon-hotel/DeepLearningWithMe

推荐阅读

[1]想明白多分类必须得谈逻辑回归

[2]Pytorch之Linear与MSELoss

[3]Pytorch之拟合正弦函数你会吗?

[4]你告诉我什么是深度学习

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页