专栏名称: 机器学习算法与Python实战
长期跟踪关注统计学、数据挖掘、机器学习算法、深度学习、人工智能技术与行业发展动态,分享Python、机器学习等技术文章。回复机器学习有惊喜资料。
目录
相关文章推荐
红古发布  ·  改善睡眠的方法来了:晚餐吃点它…… ·  昨天  
LRTV辽宁之声  ·  刷短视频“刷”成2300度近视?这些习惯,正 ... ·  2 天前  
镇江发布  ·  提醒家人!这些食物可常吃! ·  2 天前  
南京日报  ·  正大量上市,有人吃进急诊! ·  2 天前  
51好读  ›  专栏  ›  机器学习算法与Python实战

深度!图解神经网络的数学原理

机器学习算法与Python实战  · 公众号  ·  · 2024-04-03 11:25

正文

如今,熟练使用像 Keras 、TensorFlow 或 PyTorch 之类的专用框架和高级程序库后,我们不用再经常费心考虑神经网络模型的大小,或者记住激活函数和导数的公式什么的。有了这些库和框架,我们创建一个神经网络,哪怕是架构很复杂的网络,往往也只是需要几个导入和几行代码而已。如下示例:

使用框架搭建 神经网络

首先,我会展示一种热门神经网络框架 -- Keras用来 搭建神经网络模型。


from keras.models import Sequential
from keras.layers import Dense

model = Sequential()
model.add(Dense(4, input_dim=2,activation='relu'))
model.add(Dense(6, activation='relu'))
model.add(Dense(6, activation='relu'))
model.add(Dense(4, activation='relu'))
model.add(Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(X_train, y_train, epochs=50, verbose=0)


如前面说过,只需几个导入、几行代码就能创建和训练一个模型,拿来解决分类问题几乎能达到 100% 的准确率。我们的工作归纳起来就是根据选择的模型结构,为模型提供超参数,比如网络层的数量、每层的神经元数量、激活函数和训练周期数量等等。下面我们来看看训练的过程发生了什么,可以看到随着训练过程中,数据被正确地区分开了!

有了这些框架,这的确为我们节省了大量写bugs(...) 的时间,让我们的工作也更加流程化。然而, 熟知神经网络背后的工作原理,对于我们选择模型架构、调参或优化模型都有莫大的帮助。


神经网络原理

为了更深入的理解神经网络的工作原理,本文将帮助大家理解一些在学习过程中可能会感到困惑的概念。我会尽量让那些对代数和微积分不感冒的朋友读着不那么痛苦,但是正如文章题目所示,本文主要讲数学原理,所以会谈论大量的数学,在这里先提个醒。


举个例子,我们要解决一个数据集的二元分类问题,数据集如上所示。

数据点组成了两个类别的圆圈状,要区分这个数据,对很多传统的机器学习算法来说都非常麻烦,但 神经网络可以很好地处理这个 非线性分类问题

为了解决这个问题,我们会使用如下图所示结构的神经网络,它有 5 个全连接层,每层有不同数量的神经元。对于隐藏层,我们会使用 ReLU 作为激活函数,在输出层中使用 S 型函数。这是个相当简单的结构,但也足够解决我们的难题了。



什么是神经网络?

我们首先来回答这个关键的问题:什么是神经网络?它是一种在生物学启发下创建的计算机程序,能够学习知识,独立发现数据中的关系。如图 2 所示,神经网络就是一系列的神经元排列在网络层中,网络层以某种方式连接在一起,从而相互之间实现沟通。


单个神经元

每个神经元会接受一系列的 x 值(从 1 到 n 的数字)作为输入,计算预测的 y-hat 值。向量 x 实际上包含了训练集中 m 个样本中一个样本的特征值。而且每个神经元会有它自己的一套参数,通常引用为 w(权重的列向量)和 b(偏差),在学习过程中偏差会不断变化。在每次迭代中,神经元会根据向量 x 的当前权向量 x 计算它的加权平均值,再和偏差相加。最后,计算的结果会传入一个非线性或函数 g 中。我在下面会提及一些最常见的激活函数。


图:单个神经元


单个网络层


现在,我们把范围缩小一点,思考一下神经网络的整个网络层是怎么进行数学运算的。我们会利用单个神经元的计算知识,在整个层中进行向量化,将这些计算融合进矩阵方程中。为了让数学符号一致,这些方程会写给选定的网络层。另外,下标的 i 符号标记了这一层的神经元顺序。


图:单个网络层


还有一件重要的事:在我们为单个神经元写方程时,我们使用 x 和 y-hat,它们分别表示特征列向量和预测值。当换成网络层的通用符号时,我们使用向量 a —— 意指对应网络层的激活。因此 x 向量是网络层 0(输入层)的激活。网络层中的每个神经元都按照如下方程式执行相同的运算:



让大家更清晰的看看,我们把第 2 层的公式写下来:



你可以看到,对每个网络层,我们必须执行一系列非常相似的运算。在这里使用 for 循环并不是非常高效,所以我们换成向量化来加快计算速度。首先,将权重 w 的水平向量堆放在一起,我们创建矩阵 W。同样地,我们将网络层中每个神经元的偏差堆放在一起,创建垂直向量 b。现在,我们可以顺利地创建一个矩阵方程式了,从而一次性计算该网络层的所有神经元。我们同样写下来用过的矩阵和向量的维度。



多个例子中的向量化

我们迄今所用的方程式只涉及了一个例子。在神经网络的学习过程中,你通常要处理大量的数据,最高可达数百万条。所以下一步就是在多个例子中实现向量化。假设我们的数据集有 m 个条目,每个有 nx 个特征。首先,我们将每一层的垂直向量 x,a 和 z 放在一起,分别创建矩阵 X,A 和 Z。然后,我们根据新创建的矩阵,重新编写之前列出的方程式。



什么是激活函数?我们为何需要它?


激活函数是神经网络中最重要的部分之一。 没有激活函数,我们的神经网络就只是一些线性函数的组合,那样无非就是个线性函数而已 。如果是这样,模型的扩展性就很有限了,比逻辑回归也强不到哪去。非线性部分能让模型有更大的灵活性,在学习过程中也能创建复杂的函数。


此外,激活函数对模型的学习速度也有重大影响,而学习速度是选择模型的主要标准之一。下图显示了一些常用的激活函数。当前,隐藏层中最常用的激活函数应该是 ReLU。在处理二元分类问题时,特别是想让模型返回在 0 到 1 之间的值时,我们有时也会使用 S 型函数,特别是在输出层中。


损失函数


学习过程中基本信息源就是损失函数的值。通常来讲,使用损失函数的目的就是展示我们离“理想”情况的差距。在我们这个例子中,我们使用了二元交叉熵,但根据我们处理的具体问题,可以使用不同的函数。我们所用的函数用如下公式表示,在学习过程中它的值的变化情况可视化动图如下。它显示了每次迭代中,损失函数的值在不断下降,准确度的值也不断增加。







请到「今天看啥」查看全文