【深度学习】softmax 回归的从零开始实现与简洁实现

前言

小时候听过一个小孩练琴的故事,老师让他先弹最简单的第一小节,小孩练了两天后弹不出。接着,老师让他直接去练更难的第二小节,小孩练习了几天后还是弹不出,开始感觉到挫败和烦躁了。

小孩以为老师之后会让他从简单的开始练,谁知老师直接让他开始练最难的一小节。小孩不干了,问老师是不是故意刁难他。

老师笑笑,让他现在弹弹第一小节试试。神奇的是,小孩竟然发现自己已经能完整弹出来了。

这有点像我现在的学习状况,前些天我还没搞明白线性模型和多层感知机,就去看了后面卷积神经网络的内容。现在再回看前面的内容时,竟然发现有点豁然开朗了,所以有了前一篇文章。

当然并不是学任何东西都可以像练琴故事里那样,往后学着学着,前面的自然就会了。

我想说的其实是,不要老想着有什么很好的学习方法,陷入“完美主义”而迟迟不开始学。

学就是了,或许学着学着,你就可以搞明白之前觉得困惑的地方。

当然,也不能非常头铁,也是需要一定的“顶层设计”的。学的过程中要时刻搞清楚“是什么”和“为什么”。

一、softmax 回归

前面我们学习了回归,它可以帮助我们解决“多少”的问题。比如预测房屋的价格是多少,某支篮球队的胜场数是多少等等。

在生活中,我们除了会碰到“多少”的问题,还会碰到“哪一个”的分类问题,如:

  • 某个图像是蚂蚁还是蜜蜂?
  • 某人有可能去看哪一部电影?

这两个的问题界限比较模糊,其中一个原因是分类可以用回归实现。

就比如,我们只希望得到结果到底是哪个类别,到底是蚂蚁还是蜜蜂。

但在实现时,我们会采用连续值的方法,即如果这张图片属于蚂蚁的概率超过了某个值,我们就把它分为蚂蚁这一类。

分类问题

我们从一个图像分类问题开始。假设每次输入的是一个 2×22\times22×2 的灰度图像,需要判断其是“猫”、“狗”还是“鸡”。

对图像的基本知识不清楚的,可以另外看看其他资料。

我们可以用一个标量表示每个像素值,这样一个图像就对应于四个特征 x1,x2,x3,x4x_1,x_2,x_3,x_4x1,x2,x3,x4

接下来,我们要选择如何表示类别(标签)。统计学家发明了一种简单的表示分类数据的方法:独热编码(one-hot encoding)。

独热编码是一个向量,它的分量和类别一样多。类别对应的分量设为 1,其他分量设为 0。

网络架构

前面提到,我们实现分类需要计算出所有可能类别的条件概率,因此需要一个有多输出的模型,每个类型对应于一个输出。

为了解决线性模型的分类问题,我们需要和输出一样多的仿射函数(affine function),每个输出对应于它自己的仿射函数。

与线性回归一样,softman 回归也是一个单层神经网络(如下图)。由于计算每个输出取决于所有输入,所以 softmax 回归的输出层也是全连接层。

【深度学习】softmax 回归的从零开始实现与简洁实现_第1张图片

softmax 运算

有了多个输出后,怎么确定好分类结果呢?我们设置一个阈值,比如选择具有最大概率的标签作为预测类别。

例如,得到的三个输出分别为 0.8,0.1 和 0.1,那么我们预测的类别可以说是 1。

若是出现三个输出为 0.8,0.6 和 0.4 呢?我们还能直接确定预测类别是 1 吗?

答案是否定的。一方面,线性层没法保证所有输出之和为 1;另一方面,不同的输入,输出结果可能是负值。这两条违反了概率的基本公理。

要将输出视为概率,我们必须保证在任何数据上的输出是非负的且总和为 1。此外,我们需要一个训练的目标函数,来激励模型精准地估计概率。

例如,在分类器输出 0.5 的所有样本中,我们希望这些样本确实是有一半预测对了。这个属性称为校准(calibration)。

1959 年,社会科学家邓肯·卢斯在选择模型(choice model)的基础上,发明的 softmaxsoftmaxsoftmax 函数正是这样做的:

softmax 函数能够将未规范化的预测变换为非负数且总和为 1,同时让模型保持可导的性质。如下式:
y^=softmax(o),y^j=exp⁡oj∑kexp⁡(ok) \hat{\pmb{y}}=softmax(\pmb{o}),\hat{y}_j=\frac{\exp{o_j}}{\sum_k\exp{(o_k)}} y^=softmax(o),y^j=kexp(ok)expoj

这个让我想起了交通规划里的 Logit 模型,也是通过这个公式将效用转为选择概率的。

尽管 softmax 是一个非线性函数,但 softmax 回归的输出仍然是由输入特征的仿射变换决定的。因此,softmax 回归是一个线性模型(linear model)。

二、softmax 回归的从零开始实现

准备数据集

与线性回归实现时我们自己生成数据集不同,我们借助已有的图像分类数据集 Fashion-MNIST。

Fashion-MNIST由10个类别的图像组成,分别为t-shirt(T恤)、trouser(裤子)、pullover(套衫)、dress(连衣裙)、coat(外套)、sandal(凉鞋)、shirt(衬衫)、sneaker(运动鞋)、bag(包)和ankle boot(短靴)。

每个类别由训练数据集(train dataset)中的6000张图像和测试数据集(test dataset)中的1000张图像组成。

因此,训练集和测试集分别包含60000和10000张图像,图像大小为 28×2828\times2828×28。测试数据集不会用于训练,只用于评估模型性能。

torchvision 为我们提供了一系列机器视觉工具包,其中就包括图像数据集及图像处理工具 transforms。

为了使我们读取图像时更轻松,我们直接使用 DataLoader,并使用多线程 loader_num 读取数据集。

将下载数据集与读取数据集整合进一个函数中,并加入 resize 参数用于对图像大小进行调整。

def load_data_fashion_mnist(batch_size, loader_num, resize=None):    # 数据集准备
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    trans = [transforms.ToTensor()]    # 数据变换操作列表
    if resize:     # 如果需要改变形状的话,把resize的操作加到变换列表里
        trans.insert(0, transforms.Resize(resize))
    trans = transforms

你可能感兴趣的:(深度学习,深度学习,回归,人工智能,softmax回归,交叉熵损失函数)