推荐 :用Python实现神经网络(附完整代码)!

Datawhale干货 作者:[美]霍布森?莱恩,科尔?霍华德

在进修神经收集之前,我们须要对神经收集底层先做一个根本的懂得。我们将在本节介绍感知机、反向传播算法以及多种梯度降低法以给大年夜家一个周全的熟悉。

一、感知机

数字感知机的本质是从数据集中拔取一个样本(example),并将其展示给算法,然后让算法断定“是”或“不是”。一般而言,把单个特点表示为 xi,个中 i是整数。所有特点的集合表示为 ,表示一个向量: ,

类似地,每个特点的权重表示为 个中 对应于与该权重接洽关系的特点 的下标,所有权重可同一表示为 一个向量 :

这里有一个缺乏的部分是是否激活神经元的阈值。一旦加权和跨越某个阈值,感知机就输出1,不然输出0。我们可以应用一个简单的 阶跃函数(在图5-2中标记为“激活函数”)来表示这个阈值。

一般而言我们还须要给上面的阈值表达式添加一个偏置项以确保神经元对全0的输入具有弹性,不然收集在输入全为0的情况下输出仍然为0。

注:所有神经收集的根本单位都是神经元,根本感知机是广义神经元的一个特例,从如今开端,我们将感知机称为一个神经元。

二、反向传播算法 2.1 价值函数

很多半据值之间的关系不是线性的,也没有好的线性回归或线性方程可以或许描述这些关系。很多半据集不克不及用直线或平面来线性瓜分。比如下图中左图为线性可分的数据,而右图为线性弗成分的数据:

在这个线性可分数据集上对两类点做切分获得的误差可以收敛于0,而对于线性弗成分的数据点集,我们无法做出一条直线使得两类点被完美分开,是以我们随便率性做一条瓜分线,可以认为在这里误差不为0,是以我们须要一个衡量误差的函数,平日称之为价值函数:

而我们练习神经收集(感知机)的目标是最小化所有输入样本数据的价值函数

2.2 反向传播

权重 经由过程下一层的权重( )和( )来影响误差,是以我们须要一种办法来计算对 误差的供献,这个办法就是 反向传播

下图中展示的是一个 全连接收集,图中没有展示出所有的连接,在全连接收集中,每个输入元素都与下一层的 各个神经元相连,每个连接都有响应的权重。是以,在一个以四维向量为输入、有5个神经元的全连接神经收集中,一共有20个权重(5个神经元各连接4个权重)。

感知机的每个输入都有一个权重,第二层神经元的权重不是分派给原始输入的,而是分派给来自第一层的各个输出。从这里我们可以看到计算第一层权重对总体误差的影响的难度。第一层权重对误差的影响并不是只来自某个零丁权重,而是经由过程下一层中每个神经元的权重来产生的。反向传播的推导过程较为复杂,这里仅简单展示其成果:

假如该层是输出层,借助于可微的激活函数,权重的更新比较简单, 对于第 个输出,误差的导数如下

假如要更新隐蔽层的权重,则会稍微复杂一点儿:

函数 表示实际成果向量, 表示该向量第 个地位上的值, , 是倒数第二层第 个节点和输出第 个节点的输出,连接这两个节点的权重为 ,误差价值函数对 求导的成果相当于用 (进修率)乘以前一层的输出再乘今后一层价值函数的导数。公式中 表示 层第 个节点上的误差项,前一层第 个节点到 层所有的节点进行加权乞降。

2.3 多种梯度降低法

到今朝为止,我们一向是把所有练习样本的误差聚合起来然后再做梯度降低,这种练习办法称为 批量进修(batch learning)。一批是练习数据的一个子集。然则在批量进修中误差曲面对于全部批是静态的,假如从一个随机的肇端点开端,获得的很可能是某个局部极小值,从而无法看到其他的权重值的更优解。这里有两种办法来避开这个陷阱。

第一种办法是 随机梯度降低法。在随机梯度降低中,不消去查看所有的练习样本,而是在输入每个练习样本后就去更新收集权重。在这个过程中,每次都邑从新分列练习样本的次序,如许将为每个样本从新绘制误差曲面,因为每个相异的输入都可能有不合的预期谜底,是以大年夜多半样本的误差曲面都不一样。对每个样本来说,仍然应用梯度降低法来调剂权重。不过不消像之前那样在每个练习周期停止后聚合所有误差再做权重调剂,而是针对每个样本都邑去更新一次权重。个中的关键点是,每一步都在向假定的极小值 进步(不是所有路径都通往假定的极小值)。

应用精确的数据和超参数,在向这个波动误差曲面的各个最小值进步时,可以更轻易地获得全局极小值。假如模型没有进行恰当的调优,或者练习数据不一致,将导致原地踏步,模型无法收敛,也学不会任何器械。不过在实际应用中,随机梯度降低法在大年夜多半情况下都能有效地避免局部极小值。这种办法的缺点是计算速度比较慢。计算前向传播和反向传播,然后针对每个样本进行权重更新,这在本来已经很慢的计算过程的基本上又增长了很多时光开销。

第二种办法,也是更常见的办法,是 小批量进修。在小批量进修中,会传入练习集的一个小的子集,并按照批量进修中的误差聚合办法对这个子集对应的误差进行聚合。然后对每个子集按批将其误差进行反向传播并更新权重。下一批会反复这个过程,直到练习集处理完成为止,这就从新构成了一个练习周期。这是一种折中的办法,它同时具有 批量进修(快速)和 随机梯度降低(具有弹性)的长处。

三、Keras:用Python实现神经收集

用原生Python来编写神经收集是一个异常有趣的测验测验,并且可以赞助大年夜家懂得神经收集中的各类概念,然则Python在计算速度上有明显缺点,即使对于中等范围的收集,计算量也会变得异常棘手。不过有很多Python库可以用来进步运算速度,包含PyTorch、Theano、TensorFlow和Lasagne等。本书中的例子应用Keras。

Keras是一个高等封装器,封装了面向Python的API。API接口可以与3个不合的后端库相兼容:Theano、谷歌的TensorFlow和微软的CNTK。这几个库都在底层实现了根本的神经收集单位和高度优化的线性代数库,可以用于处理点积,以支撑高效的神经收集矩阵乘法运算。

我们以简单的异或问题为例,看看若何用Keras来练习这个收集。

importnumpy asnp

fromkeras.models importSequential # Kera的基本模型类

fromkeras.layers importDense, Activation # Dense是神经元的全连接层

fromkeras.optimizers importSGD # 随机梯度降低,Keras中还有一些其他优化器

# Our examples for an exclusive OR.

x_train = np.array([[ 0, 0],

[ 0, 1],

[ 1, 0],

[ 1, 1]]) # x_train是二维特点向量表示的练习样本列表

y_train = np.array([[ 0],

[ 1],

[ 1],

[ 0]]) # y_train是每个特点向量样本对应的目标输出值

model = Sequential

num_neurons = 10#全连接隐蔽层包含10个神经元

model.add(Dense(num_neurons, input_dim= 2)) # input_dim仅在第一层中应用,后面的其他层会主动计算前一层输出的外形,这个例子中输入的XOR样本是二维特点向量,是以input_dim设置为2

model.add(Activation( 'tanh'))

model.add(Dense( 1)) #输出层包含一个神经元,输出成果是二分类值(0或1)

model.add(Activation( 'sigmoid'))

model.summary

可以看到模型的构造为:

Layer (type) Output Shape Param

=================================================================

dense_18 (Dense) (None, 10) 30

_________________________________________________________________

activation_6 (Activation) (None, 10) 0

_________________________________________________________________

dense_19 (Dense) (None, 1) 11

_________________________________________________________________

activation_7 (Activation) (None, 1) 0

=================================================================

Total params: 41.0

Trainable params: 41.0

Non-trainable params: 0.0

model.summary 供给了收集参数及各阶段权重数( Param # )的概览。我们可以快速计算一下:10个神经元,每个神经元有3个权重,个中有两个是输入向量的权重(输入向量中的每个值对应一个权重),还有一个是偏置对应的权重,所以一共有30个权重须要进修。输出层中有10个权重,分别与第一层的10个神经元一一对应,再加上1个偏置权重,所以该层共有11个权重。

下面的代码可能有点儿不轻易懂得:

sgd = SGD(lr= 0.1)

model.compile(loss= 'binary_crossentropy', optimizer=sgd, metrics=[ 'accuracy'])

SGD是之前导入的随机梯度降低 优化器,模型用它来最小化误差或者 损掉。 lr 是进修速度,与每个权重的误差的导数结合应用,数值越大年夜模型的进修速度越快,但可能会使模型无法找到全局极小值,数值越小越精确,但会增长练习时光,并使模型更轻易陷入局部极小值。损掉函数本身也定义为一个参数,在这里用的是 binary_crossentropy 。 metrics 参数是练习过程中输出流的选项列表。用 compile 办法进行编译,此时还未开端练习模型,只对权重进行了初始化,大年夜家也可以测验测验一下用这个随机初始状况来猜测,当然获得的成果只是随机猜测:

model.predict(x_train)

[[ 0.5]

[ 0.43494844]

[ 0.50295198]

[ 0.42517585]]

predict 办法将给出最后一层的原始输出,在这个例子中是由 sigmoid 函数生成的。

之后再没什么好写的了,然则这里还没有关于谜底的任何常识,它只是对输入应用了随机权重。接下来可以试着进行练习。

model.fit(x_train, y_train, epochs= 100) #从这里开端练习模型

Epoch 1/ 100

4/ 4[==============================] - 0s - loss: 0.6917- acc: 0.7500

Epoch 2/ 100

4/ 4[==============================] - 0s - loss: 0.6911- acc: 0.5000

Epoch 3/ 100

4/ 4[==============================] - 0s - loss: 0.6906- acc: 0.5000

...

Epoch 100/ 100

4/ 4[==============================] - 0s - loss: 0.6661- acc: 1.0000

提示

在第一次练习时收集可能不会收敛。第一次编译可能以随机分布的参数停止,导致难以或者不克不及获得全局极小值。假如碰到这种情况,可以用雷同的参数再次调用 model.fit ,或者添加更多练习周期,看看收集可否收敛。或者也可以用不合的随机肇端点来从新初始化收集,然后再次测验测验 fit 。假如应用后面这种办法,请确保没有设置随机种子,不然只会赓续反复同样的实验成果。

当收集一遍又一遍地进修这个小数据集时,它终于弄明白了这是怎么回事。它从样本中“学会”了什么是异或!这就是神经收集的神奇之处。

model.predict_classes(x_train)

4/ 4[==============================] - 0s

[[ 0]

[ 1]

[ 1]

[ 0]]

model.predict(x_train)

4/ 4[==============================] - 0s

[[ 0.0035659]

[ 0.99123639]

[ 0.99285167]

[ 0.00907462]]

在这个经由练习的模型上再次调用 predict (和 predict_classes )会产生更好的成果。它在这个小数据集上获得了 100%的精确度。当然,精确率并不是评估猜测模型的最佳标准,但对这个小例子来说完全可以解释问题。接下来展示了若何保存这个异或模型:

importh5py

model_structure = model.to_json #用Keras的帮助办法将收集构造导出为JSON blob类型以备后用

withopen( "basic_model.json", "w") asjson_file:

json_file.write(model_structure)

model.save_weights( "basic_weights.h5") #练习好的权重必须被零丁保存。第一部分只保存收集构造。在后面从新加载收集构造时必须对其从新实例化

同样也有对应的办法来从新实例化模型,如许做猜测时不必再去从新练习模型。固然运行这个模型只须要几秒,然则在后面的章节中,模型的运行时光将会快速增长到以分钟、小时甚至天为单位,这取决于硬件机能和模型的复杂度,所以请预备好!

本文内容经出版社授权宣布,节选自《天然说话处理实战》一书,由霍布森?莱恩,科尔?霍华德所著。理论与实战结合,有具体的代码可以参考,对于懂得算法过程很有赞助。

转自:Datawhale;

END

版权声明:本号内容部分来自互联网,转载请注明原文链接和作者,如有侵权或出处有误请和我们接洽。