三、多分类神经网络:Softmax回归

1 认识softmax函数

之前介绍分类神经网络时,我们只说明了二分类问题,即标签只有两种类别的问题(0和1,猫和狗)。 虽然在实际应用中,许多分类问题都可以用二分类的思维解决,但依然存在很多多分类的情况,最典型的就是手写数字的识别问题。计算机在识别手写数字时,需要对每一位数字进行判断,而个位数字总共有10个(09),所以手写数字的分类是十分类问题,一般分别用0-9表示。

在之前展示深度学习中的数据时,我们也展示过另一个数据集的标签:

这个数据集是对服装样式进行分类,从它标签也可以看出来,这也是一个多分类问题。正常来说,我们都对多分类的类别按整数[1,+∞]的数字进行编号,比较少有使用0类的情况。

在机器学习中,我们会使用二分类算法的Many-vs-Many(多对多)和One-vs-Rest (一对多)模式来进行多分类。其中,OvR是指将多个标签类别中的一类作为类别1,其他所有类别作为类别0,分别建立多个二分类模型,综合得出多分类结果的方法。MvM是指把好几个标签类作为1,剩下的几个标签类别作为0,同样分别建立多个二分类模型来得出多分类结果的方法。这两种方法非常有效,尤其是在逻辑回归 做多分类的问题上能够解决很多问题,但是在深度学习世界却完全不奏效。理由非常简单:

  1. 逻辑回归是一个单层神经网络,计算非常快速,在使用OvR和MvM这样需要同时建立多个模型的方法时,运算速度不会成为太大的问题。但真实使用的神经网络往往是一个庞大的算法,建立一个模型就会耗费很多时间,因此必须建立很多个模型来求解的方法对神经网络来说就不够高效。
  2. 我们有更好的方法来解决这个问题,那就是softmax回归。
    Softmax函数是深度学习基础中的基础,它是神经网络进行多分类时,默认放在输出层中处理数据的函数。假设现在神经网络是用于三分类数据,且三个分类分别是苹果,柠檬和百香果,序号则分别是分类 1、分类2和分类3。则使用softmax函数的神经网络的模型会如下所示:


与二分类一样,我们从网络左侧输入特征,从右侧输出概率,且概率是通过线性回归的结果zzz外嵌套softmax函数来进行计算。在二分类时,输出层只有一个神经元,只输出样本对于正类别的概率(通常是标签为1的概率),而softmax的输出层有三个神经元,分别输出该样本的真实标签是苹果、柠檬或百香果的概率 。在多分类中,神经元的个数与标签类别的个数是一致的,如果是十分类,在输出层上就会存在十个神经元,分别输出十个不同的概率。此时,样本的预测标签就是所有输出的概率中最大的概率对应的标签类别。

那每个概率是如何计算出来的呢?来看Softmax函数的公式:
σk=Softmax⁡(zk)=ezk∑Kez\sigma_{k}=\operatorname{Softmax}\left(z_{k}\right)=\frac{e^{z_{k}}}{\sum^{K} e^{z}} σk​=Softmax(zk​)=∑Kezezk​​
其中eee为自然常数(约为2.71828),zzz与sigmoid函数中的zzz一样,表示回归类算法(如线性回归)的结果。K表示该数据的标签中总共有K个标签类别,如三分类时K=3,四分类时K=4。kkk表示标签类别kkk类。很容易可以看出,Softmax函数的分子是多分类状况下某一个标签类别的回归结果的指数函数,分母是多分类状况下所有标签类别的回归结果的指数函数之和,因此Softmax函数的结果代表了样本的结果为类别kkk的概率。

举个例子,当我们有三个分类,分别是苹果,梨,百香果的时候,样本i被分类为百香果的概率σ百香果\sigma_{百香果}σ百香果​为:

2 PyTorch中的softmax函数

我们曾经提到过,神经网络是模型效果很好,但运算速度非常缓慢的算法。 softmax函数也存在相同的问题——它可以将多分类的结果转变为概率(这是一个极大的优势),但它需要的计算量非常巨大。由于softmax的分子和分母中都带有为底的指数函数,所以在计算中非常容易出现极大的数值。

如上图所示,就已经等于20000了,而回归结果完全可能是成千上万的数字。事实上会变成一个后面有40多个0的超大值,则会直接返回无限大inf,这意味着这些数字已经超出了计算机处理数时要求的有限数据宽度,超大数值无法被计算机运算和表示。这种现象叫做“溢出“,当计算机返回”内存不足”或Python服务器直接断开连接的时候,可能就是发生了这个问题。来看看这个问题实际发生时的状况:

#对于单一样本,假定一组巨大的z
z = torch.tensor([1010,1000,990], dtype=torch.float32)
torch.exp(z) / torch.sum(torch.exp(z)) # softmax函数的运算
#tensor([nan, nan, nan])

因此,我们一般不会亲自使用tensor来手写softmax函数。在PyTorch中,我们往往使用内置好的softmax函数来计算softmax的结果,我们可以使用torch.softmax来轻松的调用它,具体代码如下:

z = torch.tensor([1010,1000,990], dtype=torch.float32)
torch.softmax(z,0) #你也可以使用F.softmax, 它返回的结果与torch.softmax是完全一致的
#tensor([9.9995e-01, 4.5398e-05, 2.0611e-09])

可以看出,同样的数据在PyTorch的softmax计算下,不会发生溢出问题,这是因为PyTorch使用了一些巧妙地手段来解决溢出问题,感兴趣地小伙伴可以自行阅读本节最后的加餐内容。如果我们将调整到较小的数值,可以发现,手写的softmax函数与torch库自带的softmax返回的结果是一模一样的。

#假设三个输出层神经元得到的z分别是10,9,5
z = torch.tensor([10,9,5], dtype=torch.float32)
torch.exp(z) / torch.sum(torch.exp(z)) # softmax函数的运算
#tensor([0.7275, 0.2676, 0.0049])
z = torch.tensor([10,9,5], dtype=torch.float32)
torch.softmax(z,0)
#tensor([0.7275, 0.2676, 0.0049])

从上面的结果可以看出,softmax函数输出的是从0到1.0之间的实数,而且多个输出值的总和是1。因为有了这个性质,我们可以把softmax函数的输出解释为“概率”,这和我们使用sigmoid函数之后认为函数返回的结果是概率异曲同工。从结果来看,我们可以认为返回了我们设定的([10,9,5])的这个样本的结果应该是第一个类别(也就是z=10的类别),因为类别1的概率是最大的(实际上几乎达到了99.9%)。

需要注意的是,使用了softmax函数之后,各个之间的大小关系并不会随之改变,这是因为指数函数是单调递增函数,也就是说,使用softmax之前的如果比较大,那使用softmax之后返回的概率也依然比较大。这是说,无论是否使用softmax,我们都可以判断出样本被预测为哪一类,我们只需要看最大的那一类就可以了。所以,在神经网络进行分类的时候,如果不需要了解具体分类问题每一类的概率是多少,而只需要知道最终的分类结果,我们可以省略输出层上的softmax函数。

但如果,你确实遇见了需要自己计算的情况,你就可以使用torch中的softmax函数。大家可能有注意到,torch中的softmax函数有两个参数,第一个参数是我们输入的用来进行计算的张量z,另一个参数我输入了0。这其实代表了你希望运行softmax计算的维度的索引。

softmax函数只能对单一维度进行计算,它只能够识别单一维度上的不同类别,但我们输入softmax的张量却可能是一个很高维的张量。比如:

s = torch.tensor([[[1,2],[3,4],[5,6]],[[5,6],[7,8],[9,10]]], dtype=torch.float32)
s.ndim
#3
s.shape
#torch.Size([2, 3, 2])

对于s而言,我们现在有三个维度——最外层代表了“2个二维张量”,3则代表每个二维张量中有3行,最后的2则代表每个二维张量中有2列。此时,我们可以从外向内索引我们的维度,索引0对应的就是最外层,索引2对应的就是最里层,相似的,我们也可以反向索引,-1对应的就是最里层,-3对应的就是最外层。所以softmax函数中需要我们输入的,就是我们希望在哪个维度上进行softmax运算。

torch.softmax(s,dim=0) #在整个张量中,有2个张量,一个二维张量就是一类
#tensor([[[0.0180, 0.0180],
#         [0.0180, 0.0180],
#         [0.0180, 0.0180]],
#
#        [[0.9820, 0.9820],
#         [0.9820, 0.9820],
#         [0.9820, 0.9820]]])
torch.softmax(s,dim=1) #在一个二维张量中,有3行数据,每一行是一种类别
#tensor([[[0.0159, 0.0159],
#         [0.1173, 0.1173],
#         [0.8668, 0.8668]],
#
#        [[0.0159, 0.0159],
#         [0.1173, 0.1173],
#         [0.8668, 0.8668]]])
torch.softmax(s,dim=2) #在每一行中,有4个数据,每个数据是一种类别
#tensor([[[0.2689, 0.7311],
#         [0.2689, 0.7311],
#         [0.2689, 0.7311]],
#
#        [[0.2689, 0.7311],
#         [0.2689, 0.7311],
#         [0.2689, 0.7311]]])

看到计算的结果,你明白这个维度的含义了吗?实际上softmax函数在实际中应用不多,但是只要用到,新手就一定会被绕进dim参数的选项中去,因此我们在这里特此说明。

在实际中,训练神经网络时往往会使用softmax函数,但在预测时就不再使用softmax函数,而是直接读取结果最大的对应的类别了。但无论如何,了解softmax是必要的,也是非常有用的。

3 使用nn.Linear与functional实现多分类神经网络的正向传播

学到这里,我相信你应该能够写出softmax回归的正向传播代码。请使用如下的数据:

X = torch.tensor([[0,0],[1,0],[0,1],[1,1]], dtype = torch.float32)

我强烈建议你先进行思考,对照下面的神经网络结构,尝试自己写一写这段代码,然后再查看答案。在这里你需要弄明白这些问题:

  1. nn.Linear中需要输入什么结构?
  2. softmax中的dim应该填写什么值?
  3. 最终输出的sigma的shape应该是几乘几才对?
import torch
from torch.nn import functional as F
X = torch.tensor([[0,0],[1,0],[0,1],[1,1]], dtype = torch.float32)
torch.random.manual_seed(420)
dense = torch.nn.Linear(2,3) #此时,输出层上的神经元个数是3个,因此应该是(2,3)
zhat = dense(X)
sigma = F.softmax(zhat,dim=1) #此时需要进行加和的维度是1
sigma.shape #这里应当返回的正确结构是(4,3),4个样本,每个样本都有自己的3个类别对应的3个概率
#torch.Size([4, 3])
sigma
#tensor([[0.4623, 0.3494, 0.1883],
#        [0.4598, 0.4422, 0.0980],
#        [0.4896, 0.3229, 0.1875],
#        [0.4902, 0.4115, 0.0983]], grad_fn=<SoftmaxBackward0>)

4 【加餐】解决Softmax的溢出问题

为了解决softmax函数的溢出问题,我们需要对softmax的公式进行如下更改。首先,在分子和分母上都乘上常数C。因为同时对分母和分子乘以相同的常数,所以计算结果不变。然后,把这个C移动到指数函数中,计作elog⁡Ce^{\log C}elogC,在这里log指的是以自然底数e为底的对数函数。只要C大于0,logC就必然存在,此时我们可以把logC替换为另一个符号C’ ,它同样代表了一个常数。即是说,其实我们只需要在softmax函数的两个指数函数自变量上都加上一个常数就可以了,这样做不会改变计算结果
ok=ezk∑i=1Kezi=CezkC∑i=1Kezi=elog⁡C⋅ezk∑i=1Kelog⁡C⋅ezi=e(zk+log⁡C)∑i=1Ke(zi+log⁡C)=e(zk+C′)∑i=1Ke(zi+C′)\begin{aligned} o_{k} &=\frac{e^{z_{k}}}{\sum_{i=1}^{K} e^{z_{i}}} \\ &=\frac{C e^{z_{k}}}{C \sum_{i=1}^{K} e^{z_{i}}} \\ &=\frac{e^{\log C} \cdot e^{z_{k}}}{\sum_{i=1}^{K} e^{\log C} \cdot e^{z_{i}}} \\ &=\frac{e^{\left(z_{k}+\log C\right)}}{\sum_{i=1}^{K} e^{\left(z_{i}+\log C\right)}} \\ &=\frac{e^{\left(z_{k}+C^{\prime}\right)}}{\sum_{i=1}^{K} e^{\left(z_{i}+C^{\prime}\right)}} \end{aligned} ok​​=∑i=1K​ezi​ezk​​=C∑i=1K​ezi​Cezk​​=∑i=1K​elogC⋅ezi​elogC⋅ezk​​=∑i=1K​e(zi​+logC)e(zk​+logC)​=∑i=1K​e(zi​+C′)e(zk​+C′)​​
现在来思考,加入一个怎样的C’才会对抑制计算溢出最有效呢?我们是希望避免十分巨大的zzz,因此我们只要让C’的符号为负号,在让其大小接近zkz_{k}zk​中的最大值就可以了。来看看在python中我们如何操作:

import numpy as np
z = z = np.array([1010,1000,990])
#定义softmax函数
def softmax(z):c = np.max(z)exp_z = np.exp(z - c) #溢出对策sum_exp_z = np.sum(exp_z)o = exp_z / sum_exp_zreturn o #导入刚才定义的z
softmax(z)
#array([9.99954600e-01, 4.53978686e-05, 2.06106005e-09])

来看看刚才求出的softmax函数的结果加和之后会显示怎样的效果:

sum(softmax(z))
#1.0

四、回归vs二分类vs多分类

到这里,我们已经见过了三个不同的神经网络:

注意到有什么相似和不同了吗?
首先可能会注意到的是,这三个神经网络都是单层神经网络,除了输入层,他们都有且只有一层网络。实际上,现实中使用的神经网络几乎99%都是多层的,但我们的网络也能够顺利进行预测,这说明单层神经网络其实已经能够实现基本的预测功能。同时,这也说明了一个问题,无论处理的是回归还是分类,神经网络的处理原理是一致的。实际上,就连算法的限制、优化方法和求解方法也都是一致的。回归和分类神经网络唯一的不同只有输出层上的σ\sigmaσ 。

虽然线性回归看起来并没有的存在,但实际上我们可以认为线性回归中的是一个恒等函数(identity function),即是说σ(z)=z\sigma(z)=zσ(z)=z(相当于y=xy = xy=x,或f(x)=xf(x) = xf(x)=x )。而多分类的时候也可以不采用任何函数,只观察zzz的大小,所以多分类也可以被认为是利用了恒等函数作为σ\sigmaσ。总结来说,回归和分类对应的分别如下:

第二个很容易发现的现象是,只有多分类的情况在输出层出现了超过一个神经元。实际上,当处理单标签问题时(即只有一个y的问题),回归神经网络和二分类神经网络的输出层永远只有一个神经元,而只有多分类的情况才会让输出层上超过一个神经元。为了方便计算和演示,在之后的课程中我们都会使用二分类神经网络作为例子。

Lesson 8.5 SOFTMAX回归相关推荐

  1. Lesson 12.5 softmax回归建模实验

    Lesson 12.5 softmax回归建模实验 接下来,继续上一节内容,我们进行softmax回归建模实验. 导入相关的包 # 随机模块 import random# 绘图模块 import ma ...

  2. Softmax 回归 vs. k 个二元分类器

    如果你在开发一个音乐分类的应用,需要对k种类型的音乐进行识别,那么是选择使用 softmax 分类器呢,还是使用 logistic 回归算法建立 k 个独立的二元分类器呢? 这一选择取决于你的类别之间 ...

  3. 【深度学习】基于Pytorch的softmax回归问题辨析和应用(一)

    [深度学习]基于Pytorch的softmax回归问题辨析和应用(一) 文章目录 1 概述 2 网络结构 3 softmax运算 4 仿射变换 5 对数似然 6 图像分类数据集 7 数据预处理 8 总 ...

  4. 【深度学习】基于Pytorch的softmax回归问题辨析和应用(二)

    [深度学习]基于Pytorch的softmax回归问题辨析和应用(二) 文章目录1 softmax回归的实现1.1 初始化模型参数1.2 Softmax的实现1.3 优化器1.4 训练 2 多分类问题 ...

  5. Softmax回归——logistic回归模型在多分类问题上的推广

    Softmax回归 Contents [hide] 1 简介 2 代价函数 3 Softmax回归模型参数化的特点 4 权重衰减 5 Softmax回归与Logistic 回归的关系 6 Softma ...

  6. DeepLearning tutorial(1)Softmax回归原理简介+代码详解

    FROM: http://blog.csdn.net/u012162613/article/details/43157801 DeepLearning tutorial(1)Softmax回归原理简介 ...

  7. Logistic and Softmax Regression (逻辑回归和Softmax回归)

    1. 简介 逻辑回归和Softmax回归是两个基础的分类模型,虽然听名字以为是回归模型,但实际我觉得他们也有一定的关系.逻辑回归,Softmax回归以及线性回归都是基于线性模型,它们固定的非线性的基函 ...

  8. 简单探索MNIST(Softmax回归和两层CNN)-Tensorflow学习

    简述 这次是在看<21个项目玩转深度学习>那本书的第一章节后做的笔记. 这段时间,打算把TensorFlow再补补,提升一下技术水平~ 希望我能坚持下来,抽空把这本书刷下来吧~ 导入数据 ...

  9. 2.3.3 Softmax回归介绍

    Softmax回归 到现在我们的分类问题都只能识别0和1的问题,比如说是猫或者不是猫,那么更复杂的问题怎么办呢?Softmax回归就能让你在多分类中识别是哪一个分类,而不只是二分类中识别. 如图所示, ...

最新文章

  1. python基础与大数据_Python大数据基础与实战第10章数据可视化.pptx
  2. Git之提交项目到远程github
  3. 经常使用的时间同步server地址
  4. Pytorch List Tensor转Tensor,,reshape拼接等操作
  5. 我的产品需求说明文档模板(PRD)
  6. css 文字重叠_html网页文字重叠 字体叠加显示css如何解决
  7. 通过历史控制文件恢复Oracle数据库,只需这10步
  8. oracle的sid相同如何解决,oracle数据库的SID重复有关问题
  9. 万亿美元软件浪潮来临,开发者是核心!
  10. 微信开放平台:网站应用-微信登录
  11. 用C语言编写高斯消元法解线性方程组
  12. 计算机主机的声音线是哪个好,电脑显示器连接线哪种接口好|VGA、HDMI视频接口选哪个好...
  13. 2018ICPC焦作站赛后总结
  14. index ffs、index fs原理考究-1109
  15. 阿里,昨天被主管告知3.25了,感觉自己好失败...
  16. static_cast 剖析
  17. 一缕黑暗中的火光-----------用例图--------------优雅的建模语
  18. win7让笔记本的自带键盘失灵
  19. LabVIEW学习笔记十三:窗格详解(调整窗格大小时缩放特定对象)
  20. 《进化 从孤胆极客到高效团队》总结

热门文章

  1. java异常在哪一层捕获_当在一个方法的代码中抛出一个检测异常时,该异常或被方法中的 ( )结构 捕获,或者在方法的 ( ) 中声明_学小易找答案...
  2. Android中的Dialog
  3. 5位随机数重复的概率 php_php防止表单重复提交的方法
  4. Oracle数据库对象----视图
  5. FrameLayout(帧布局)的基本使用
  6. 训练超参数, 出现 Cannot use GPU in CPU-only Caffe 错误?
  7. 华为云查询弹性云服务器规格信息,通用计算型弹性云服务器规格介绍详情-华为云...
  8. python安装numba_python – 在OS X上安装Numba时出错
  9. pycharm调试远程服务器代码
  10. python programming training(一):最大回文子字符串