干货|非常详细的神经网络入门解释
Python.NN(一) 神经网络入门
我们之前在 Python · SVM(一)· 感知机干货|SVM(一)·最全面的感知机总结 里面介绍感知机时,用的是这么一个公式:
这在二分类任务时当然没问题,但是到多分类时就会有些问题。
虽说我们是可以用各种方法让二分类模型去做多分类任务的,不过那些方法普遍都比较麻烦。
为了让我们的模型天然适应于多分类任务,我们通常会将样本中的y取为one-hot向量,亦即
此时我们的感知机就会变成这样(以 N 个拥有 n 维特征的样本的 K 分类任务为例;为简洁,省略了偏置量 b):
此时模型的表示会变为
也正因此,我们不能简单地沿用之前定义的损失函数(
由于我们的目的是让模型输出的向量
具体而言,我们可以把损失函数定义为
在有了损失之后就可以求导了。需要指出的是,虽然我接下来写的公式看上去挺显然,但由于我们进行的是矩阵求导工作,所以它们背后真正的逻辑其实没那么显然。
有兴趣的观众老爷可以看看这篇文章矩阵求导术,这里就先直接给出结果,细节会附在文末:
利用它,我们就能写出相应的梯度下降训练算法了:
该模型的表现如下:
此时正确率为 50% 左右。
可以看到模型输出中基本没有对角线上的那一类(而这一类恰恰是最多的),这是因为将感知机拓展为多类模型并不会改变它是线性模型的本质、所以其分类效果仍然是线性的,从而它无法拟合出对角线那一类的边界
2 从感知机到神经网络那么怎样才能把感知机变成一个非线性的模型呢?具体而言:
现有的感知机只有两层(输入层和输出层),是否能多加一些层?
核方法从直观上来说,是利用满足一定条件的核函数将样本空间映射到高维空间;如果我放宽条件、使用一些一般的比较好的函数,是否也能起到一定的效果?
基于这两个想法,我们可以把上面画过的感知机模型的结构弄成下图这种结构:
其中,我们通常(假设共有 N 个样本):
称
为“激活函数” 称
和 为权值矩阵 称往中间加的那些层为“隐藏层”;为简洁,此后我们讨论时认为只加了一层隐藏层,多层的情况会在下一篇文章讨论
称图中每个圈儿为“神经元”。以上图为例的话:
输入层有 n 个神经元
隐藏层有 m 个神经元
输出层有 K 个神经元
然后,激活函数其实就是上文所说的一般的比较好的函数;常用激活函数的相关介绍我会附在文末,这里就暂时认定激活函数为我们之前在 SVM 处就见过的 ReLU,亦即认定
这个结构看上去很强大,但求导求起来却也会麻烦不少(损失函数仍取
其中,“ * ”表示乘法的 element-wise 操作(或者专业一点的话,叫 Hadamard 乘积)、ReLU'表示对 ReLU 函数进行求导。由于当
利用这两个求导公式,相应的梯度下降算法是比较好实现的:
该模型的表现如下:
虽然还是比较差(准确率 70% 左右),但已经有模有样了
3 使用Softmax + Cross Entropy可能已经有观众老爷发现,我把上面这个模型的训练速率的默认值调到了,这是因为稍大一点的训练速率都会使模型直接爆炸。
这是因为我们没有对最后一层做变换、而是直接简单粗暴地用了
考虑到标签是
事实上,我们耳熟能详的 Softmax 正是干这活儿的,具体而言,假设现在有一个向量
不难看出这是个概率向量,且从直观上来看颇为合理。
在真正应用 Softmax 会有一个提高数值稳定性的小技巧,细节会附在文末,这里就暂时按下
于是在应用了 Softmax 之后,我们模型的输出就变成一个概率向量了。
诚然此时仍然能用欧氏距离作为损失函数,不过一种普遍更优的做法是使用 Cross Entropy(交叉熵)作为损失函数。
具体而言,两个随机变量
交叉熵拥有如下两个性质:
当真值为 0(
)时,交叉熵其实就化为了 此时预测值越接近 0、交叉熵就越接近 0,反之若预测值趋于 1、交叉熵就会趋于无穷 当真值为 1()时,交叉熵其实就化为了,此时预测值越接近 1、交叉熵就越接近 0,反之若预测值趋于 0、交叉熵就会趋于无穷
所以拿交叉熵作为损失函数是合理的。真正应用交叉熵时同样会有提高数值稳定性的小技巧——在 log 里面放一个小值以避免出现 log 0 的情况:
在加了这两个东西之后,我们就要进行求导了。
虽说求导过程比较繁复,但令人惊喜的是,最终结果和之前的结果是几乎一致的,区别只在于倍数(推导过程参见文末):
所以相应的实现也几乎一致:
该模型的表现如下:
虽说仍不完美,但它和不使用 Softmax + Cross Entropy 的模型相比,有这么两个优势:
训练速率可以调得更大(vs),这意味着该模型没那么容易爆炸(什么鬼)
模型的训练更加稳定,亦即每次训练出来的结果都差不多。
反观之前的模型,我所给出的模型表现其实是精挑细选出来的;在一般情况下,其表现其实是类似于这样的(这告诉我们,一个好的结果很有可能是由无数 sb 结果堆积出来的……):
1)常见激活函数
A、Sigmoid:
B、Tanh:
C、ReLU:
D、ELU:
E、Softplus:
以及最近出了一个叫 SELU 的激活函数,论文整整有 102 页……感兴趣的观众老爷们可以参见[1706.02515] Self-Normalizing Neural Networks
2)神经网络中导数的计算
为了书写简洁,接下来我们会用矩阵求导的技巧来进行计算;不过如果觉得看着太绕的话,建议还是参照这篇文章、依定义逐元素求导(事实上我也经常绕不过来……)
由简入繁,我们先来看多分类感知机的求导过程。我们之前曾说过多分类感知机可以写成
不少人在尝试求
事实上,多分类感知机的本质是对随机向量
于是求导过程就化简为标量对矩阵的求导了:
由此可知
虽说严谨的数学叙述会比较麻烦(需要用到矩阵的向量化【又称“拉直”】之类的),但我们可以这样直观地理解:
当样本从一个变成多个时,权值矩阵的导数理应是从一个变成多个的加总
因此我们只需利用矩阵乘法完成这个加总的过程即可。注意到我们可以直观地写出:
于是
以及
3)Softmax + Cross Entropy
也就是
亦即
本文我们主要讨论了如何将感知机应用于多分类任务,并通过直观的思想——加深感知机的层次和应用激活函数来得到更强力的模型(神经网络)。
此外,我们还讨论了如何应用 Softmax + Cross Entropy 来让模型变得更加稳定。
然而我们讨论的范围仍局限于单隐藏层和 ReLU 激活函数,下一篇文章我们会介绍更一般的情形,并通过一种方式来直观说明神经网络的强大
推荐阅读:
全是通俗易懂的硬货!只需置顶~欢迎关注交流~