- Published on
机器学习 | 神经网络初探
- Authors

- Name
- Jiaqi Wang(Ramis)
[ 最后更新 于 2024-07-21]
恭喜我时隔两周终于鼓起勇气开始写神经网络。 上周跑代码跑伤了,深深地感受到调参这个东西对人生的消耗,真无语(不是 不过也感受到这个东西其实核心非常的暴力(是真的非常的暴力…)。当然,也发现展开来需要了解的东西以及操作上的细节倒是很多,实施和迭代的过程需要巨大的耐心,以至于我每天晚上一边跑代码一边重温琅琊榜!(…。
什么是神经网络?
神经网络其实并不是最近才诞生的算法:早在上世纪80年代,它就开始在学术圈内引起关注,其灵感来自于模拟人脑“神经网络”和”神经元“之间的交互。神经网络由多个层(layers)和节点(neurons)组成,通过模拟神经元之间的连接和信息传递,来实现复杂的计算和模式识别。尽管这个概念看似与人脑的运作方式有很大关联,但实际上,这是工程师自己一种过于机械的类比(很好,和建筑师一样哎),神经网络的工作原理与人脑的实际工作方式有着本质区别(当然,我也不知道人脑怎么工作,但是肯定不是这样工作)。
在神经网络初创的几年里,由于各种限制,包括数据和计算资源的匮乏,这一领域的发展曾一度停滞。然而,随着最近几十年数据量的爆炸性增长和计算能力的显著提升,神经网络迎来了新的发展契机,并以“深度学习”的名字重新焕发活力(这段是gpt的”如数家珍“)。如今,神经网络已经广泛应用于机器学习和人工智能领域,成功解决了大量模式识别、文本处理、音频和图像识别等任务。下面就来看看一个神经网络包含哪些部分:
神经网络的组成部分

隐藏层(Hidden Layers)
神经网络的输入、输出形式以及损失计算和普通的线性回归/逻辑回归可以说是很类似的。而其和其他的算法最重要的区别就在于中间加入的一些隐藏层(hidden layers)。这些隐藏层用来输入的信息进行在交互,从而整合/捕捉出新的有用信息来构造模型。
看下面一个小小的示意图:

比方说,我们要建立一个模型来预测大家会不会购买一个商品。输入的X只包含X1价格和X2实用性,我们可以想象出这两个量之间的相互交护或许可以创造出新的有助于预测的量,比如说:我们构造一个物美价廉指数来测量这个东西是不是价格便宜又实用。或者,假如一个很实用的东西,洗衣机什么的,大家买的时候肯定不会朝着便宜货去,那么实用性的占比就会更高一些。再比如说,有些东西实用性可能非常低,但是特别特别便宜,那么是不是还可以有一个薅羊毛指数。最后,由这三个指数共同来决定,买还是不买。当然,下图中的系数是乱写的,你也可以进一步再加很多层——让指数之间也可以进行交互,那么我们的神经网路就可以有更多的隐藏层。
好的,看到这个时候该要问了:如果每一个神经元对输入都仅仅进行线性计算,你上面这些系数一顿操作猛如虎最后不还是线性的吗(我一开始看的时候也充满了这样的疑问)。没错,每一个神经元对输入所进行不能是线性计算,而要对每个分解量的权重进行变化才有意义。这也就是为什么神经网路中有一个很重要的概念——激活函数。
隐藏层的激活函数(Activation Function)
激活函数是神经网络的关键组成部分,它来决定了一个神经元的输出,也好像是一个神经元的开关。这里可以使用我们熟悉的Sigmoid 函数。当然,对于隐藏层的神经元来说更常使用的是ReLU(Rectified Linear Unit)函数。

ReLU(Rectified Linear Unit)函数: f(x)=max(0,x), 可以很简单地将所有负输入值置为0,正输入值不变。 这相比于sigmoid来说,它的计算简单,收敛速度也快。
倘若我们在回看上图中的概念版”买or不买”神经网络,如果一个东西很实用但比较贵,可能其物美价廉指数和薅羊毛指数小于0,在使用ReLU函数的情况下这两个神经元就会被关闭(而不会产生负效应),也就是说只考虑耐用指数对买不买的最终影响。这样的效果就不再是一个简单的线性函数所能替代的了。
输出层,损失函数
输出层(Output Layer)输出层是网络的最后一层,用于产生最终的预测结果。我们也要根据具体任务选择不同的激活函数,比如0/1分类问题当然是选用sigmoid函数,而多个分类问题常用的是softmax函数(下面详细介绍),数值回归问题还是使用线性函数。其损失函数(Loss Function),当然也是根据这些不同的输出函数进行构造,和之前的其他回归并没有什么不同。
SoftMax函数和sigmoid函数其实有些类似,用来预测输入值属于1,2,3...k个类别中的哪一类,其函数表达式为:
其中Zi是来判断其属于第i个类别的原始分数,而softmax将其转换为属于各个类别的概率。
图片来源:Dario Radečić而softmax函数的损失函数,交叉熵损失函数(Cross-entropy loss),和sigmoid的损失函数也是很类似的形式,用于衡量预测概率分布与真实分布之间的差异。公式如下:
对于一个样本的交叉熵损失:
其中 是真实标签的one-hot编码(对于正确类别为1,其余类别为0)。 是softmax函数计算出的预测值。
——不难回忆出而在sigmoid函数的loss function中的这一项也符合上述的形式,只是因为正确类别是0。
参数回调
那么,在搭好神经网络之后我们免不了要接入训练用的数据集开始进行梯度下降从而找到合适的参数w和b。在这里,梯度下降法仍然是基于将整个数据集的cost function(每个数据的输出层的输出结果和真实值之间的偏差)最小化为目的,请注意,在这里神经网络内部的w都是一起通过梯度递降不断迭代更新实现的(再次感叹暴力)。在这里对于每个神经元内部的参数的梯度下降运用的是 反向传播(Backpropagation)。具体来说涉及到先通过求导的链式法则计算损失函数关于每个参数的梯度,从输出层反推输入层。比如我们要计算损失函数对于第一层的w偏导的值,就从从输出层各个参数的梯度值一路反推回去: 这样逐层反推获得更新梯度之后,再使用梯度下降算法更新参数就可以像之前一样不断迭代了(具体到底是不是这么简单的其实我也不确定就对了因为这部分代码我们调包侠肯定不会是自己去写的啦)。
尾声
至此,我们以及大概说明了神经网路的工作原理和过程。当然,在实际的神经网路工作中,所有的神经元内部的参数都是以自动挡的形式直接出现的,而不是说每个神经元的参数都有其明显的意义(比如说我们之前给的“薅羊毛”指数的例子)。尤其是当神经网络的层数过多时,人脑已经无法阅读出单个神经元在其中扮演的角色是什么了。如下图,是我在做的手写数字识别的神经网络的某次迭代中一层的60个神经元的w的值(0为黑色),只能说少有一部分些许能看出它们和某些笔画和某些数字的局部的识别之间有些联系,除此之外很多系数都是很奇特的形状。

但是神奇的是,它们在一起形成的神经网络竟然有很好的效果。因此我在感叹这整个算法这么暴力这么玄的同时,也感慨当数据足够多的时候,这些小小的旋钮总能找到一个合适的位置来给出一个良好的输出结果。可以说整个过程跟炼丹没什么区别(捂脸)。
当然,由于今天所说的传统的神经网络算法其算法本身已经比较成熟了,要想取得比较好的结果主要还是在于怎么调节模型的超参数以及对数据本身的engineering。这其中其他超参数的选择我们在实操过程中还会继续提到,虽然我自己也还在不断学习的过程中就是了。
今天还未提到的另一个点是Convolutional Layer(神经元及数据根据区域有选择地相连,而不是网络内每一个神经元都完全相连),因为最近在做MNIST中得知Convolutional Network会有更好一些的表现,想要下周再做尝试。不过最快估计要等到下下周才能写文讨论了(饼-0-)。

天哪 7月的第一份稿子竟然拖到了21号才发出来。 拖延癌晚期警告。(主要是感到一边跑代码一边学不停地改进以及重跑也太累了… 说实话并不知道下周能不能把实操做完再整理完发出来。 不过这个月以来也总算是实现了对神经网络的祛魅就对了。以前老觉得这个东西高深莫测来着,现在发现这个东西只是莫测,并不高深。算法不难理解,而且新手代码也能跑出不错的表现,所以还算是挺开心的。 其他算法也继续看到决策树和随机森林了,积压的内容也越来越多啦(。。 没关系,慢慢写吧… 希望七月结束之前再写两篇;p
-鹅仔 2024/07/21 18:30