Lesson 8.5 SOFTMAX回归
三、多分类神经网络:Softmax回归
1 认识softmax函数
之前介绍分类神经网络时,我们只说明了二分类问题,即标签只有两种类别的问题(0和1,猫和狗)。 虽然在实际应用中,许多分类问题都可以用二分类的思维解决,但依然存在很多多分类的情况,最典型的就是手写数字的识别问题。计算机在识别手写数字时,需要对每一位数字进行判断,而个位数字总共有10个(09),所以手写数字的分类是十分类问题,一般分别用0-9表示。
在之前展示深度学习中的数据时,我们也展示过另一个数据集的标签:
这个数据集是对服装样式进行分类,从它标签也可以看出来,这也是一个多分类问题。正常来说,我们都对多分类的类别按整数[1,+∞]的数字进行编号,比较少有使用0类的情况。
在机器学习中,我们会使用二分类算法的Many-vs-Many(多对多)和One-vs-Rest (一对多)模式来进行多分类。其中,OvR是指将多个标签类别中的一类作为类别1,其他所有类别作为类别0,分别建立多个二分类模型,综合得出多分类结果的方法。MvM是指把好几个标签类作为1,剩下的几个标签类别作为0,同样分别建立多个二分类模型来得出多分类结果的方法。这两种方法非常有效,尤其是在逻辑回归 做多分类的问题上能够解决很多问题,但是在深度学习世界却完全不奏效。理由非常简单:
- 逻辑回归是一个单层神经网络,计算非常快速,在使用OvR和MvM这样需要同时建立多个模型的方法时,运算速度不会成为太大的问题。但真实使用的神经网络往往是一个庞大的算法,建立一个模型就会耗费很多时间,因此必须建立很多个模型来求解的方法对神经网络来说就不够高效。
- 我们有更好的方法来解决这个问题,那就是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)
我强烈建议你先进行思考,对照下面的神经网络结构,尝试自己写一写这段代码,然后再查看答案。在这里你需要弄明白这些问题:
- nn.Linear中需要输入什么结构?
- softmax中的dim应该填写什么值?
- 最终输出的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移动到指数函数中,计作elogCe^{\log C}elogC,在这里log指的是以自然底数e为底的对数函数。只要C大于0,logC就必然存在,此时我们可以把logC替换为另一个符号C’ ,它同样代表了一个常数。即是说,其实我们只需要在softmax函数的两个指数函数自变量上都加上一个常数就可以了,这样做不会改变计算结果。
ok=ezk∑i=1Kezi=CezkC∑i=1Kezi=elogC⋅ezk∑i=1KelogC⋅ezi=e(zk+logC)∑i=1Ke(zi+logC)=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=1Keziezk=C∑i=1KeziCezk=∑i=1KelogC⋅ezielogC⋅ezk=∑i=1Ke(zi+logC)e(zk+logC)=∑i=1Ke(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回归相关推荐
- Lesson 12.5 softmax回归建模实验
Lesson 12.5 softmax回归建模实验 接下来,继续上一节内容,我们进行softmax回归建模实验. 导入相关的包 # 随机模块 import random# 绘图模块 import ma ...
- Softmax 回归 vs. k 个二元分类器
如果你在开发一个音乐分类的应用,需要对k种类型的音乐进行识别,那么是选择使用 softmax 分类器呢,还是使用 logistic 回归算法建立 k 个独立的二元分类器呢? 这一选择取决于你的类别之间 ...
- 【深度学习】基于Pytorch的softmax回归问题辨析和应用(一)
[深度学习]基于Pytorch的softmax回归问题辨析和应用(一) 文章目录 1 概述 2 网络结构 3 softmax运算 4 仿射变换 5 对数似然 6 图像分类数据集 7 数据预处理 8 总 ...
- 【深度学习】基于Pytorch的softmax回归问题辨析和应用(二)
[深度学习]基于Pytorch的softmax回归问题辨析和应用(二) 文章目录1 softmax回归的实现1.1 初始化模型参数1.2 Softmax的实现1.3 优化器1.4 训练 2 多分类问题 ...
- Softmax回归——logistic回归模型在多分类问题上的推广
Softmax回归 Contents [hide] 1 简介 2 代价函数 3 Softmax回归模型参数化的特点 4 权重衰减 5 Softmax回归与Logistic 回归的关系 6 Softma ...
- DeepLearning tutorial(1)Softmax回归原理简介+代码详解
FROM: http://blog.csdn.net/u012162613/article/details/43157801 DeepLearning tutorial(1)Softmax回归原理简介 ...
- Logistic and Softmax Regression (逻辑回归和Softmax回归)
1. 简介 逻辑回归和Softmax回归是两个基础的分类模型,虽然听名字以为是回归模型,但实际我觉得他们也有一定的关系.逻辑回归,Softmax回归以及线性回归都是基于线性模型,它们固定的非线性的基函 ...
- 简单探索MNIST(Softmax回归和两层CNN)-Tensorflow学习
简述 这次是在看<21个项目玩转深度学习>那本书的第一章节后做的笔记. 这段时间,打算把TensorFlow再补补,提升一下技术水平~ 希望我能坚持下来,抽空把这本书刷下来吧~ 导入数据 ...
- 2.3.3 Softmax回归介绍
Softmax回归 到现在我们的分类问题都只能识别0和1的问题,比如说是猫或者不是猫,那么更复杂的问题怎么办呢?Softmax回归就能让你在多分类中识别是哪一个分类,而不只是二分类中识别. 如图所示, ...
最新文章
- python基础与大数据_Python大数据基础与实战第10章数据可视化.pptx
- Git之提交项目到远程github
- 经常使用的时间同步server地址
- Pytorch List Tensor转Tensor,,reshape拼接等操作
- 我的产品需求说明文档模板(PRD)
- css 文字重叠_html网页文字重叠 字体叠加显示css如何解决
- 通过历史控制文件恢复Oracle数据库,只需这10步
- oracle的sid相同如何解决,oracle数据库的SID重复有关问题
- 万亿美元软件浪潮来临,开发者是核心!
- 微信开放平台:网站应用-微信登录
- 用C语言编写高斯消元法解线性方程组
- 计算机主机的声音线是哪个好,电脑显示器连接线哪种接口好|VGA、HDMI视频接口选哪个好...
- 2018ICPC焦作站赛后总结
- index ffs、index fs原理考究-1109
- 阿里,昨天被主管告知3.25了,感觉自己好失败...
- static_cast 剖析
- 一缕黑暗中的火光-----------用例图--------------优雅的建模语
- win7让笔记本的自带键盘失灵
- LabVIEW学习笔记十三:窗格详解(调整窗格大小时缩放特定对象)
- 《进化 从孤胆极客到高效团队》总结
热门文章
- java异常在哪一层捕获_当在一个方法的代码中抛出一个检测异常时,该异常或被方法中的 ( )结构 捕获,或者在方法的 ( ) 中声明_学小易找答案...
- Android中的Dialog
- 5位随机数重复的概率 php_php防止表单重复提交的方法
- Oracle数据库对象----视图
- FrameLayout(帧布局)的基本使用
- 训练超参数, 出现 Cannot use GPU in CPU-only Caffe 错误?
- 华为云查询弹性云服务器规格信息,通用计算型弹性云服务器规格介绍详情-华为云...
- python安装numba_python – 在OS X上安装Numba时出错
- pycharm调试远程服务器代码
- python programming training(一):最大回文子字符串