人工智能培训

搜索

人工智能技术:资源 | 如何只用NumPy码一个神经网络

[复制链接]
abooooooo 发表于 2018-10-30 08:34:40 | 显示全部楼层 |阅读模式
abooooooo 2018-10-30 08:34:40 187 0 显示全部楼层
原标题:资源 | 如何只用NumPy码一个神经网络
            选自Towards Data Science
作者:Piotr Skalski
参与:高璇、张倩
  Keras、TensorFlow、PyTorch 等高级框架可以帮助我们快速构建复杂模型。深入研究并理解其中的理念很有价值。不久前,本文作者发表了一篇文章(参见《资源 | 来自独秀同学的深度网络数学笔记,还不快收藏?》),简明扼要地解释了神经网络的工作原理,但那篇文章偏向于数学理论知识。所以作者打算以一种更实际的方式来跟进这一话题。他们尝试只使用 NumPy 构建一个全运算的神经网络,通过解决简单的分类问题来测试模型,并将其与 Keras 构建的神经网络进行性能比较。
注:本文将包含大量用 Python 编写的代码片段。希望读起来不会太无聊。:)所有源代码都可以在作者的 GitHub 上找到。链接:http://github.com/SkalskiP/ILearnDeepLearning.py
R2RMCfir53VTRbRF.jpg
图 1 :密集神经网络架构
磨刀不误砍柴工
在开始编程之前,需要先整理一个基本的路线图。我们的目标是创建一个程序,该程序能创建一个拥有特定架构(层的数量和大小以及激活函数都是确定的)的密集连接神经网络。图 1 给出了网络的示例。最重要的是,网络必须可训练且能进行预测。
F34U77UMpTM3323m.jpg
图 2 :神经网络框图
上图显示了在训练神经网络时需要执行的操作。它还显示了在单次迭代的不同阶段,需要更新和读取多少参数。构建正确的数据结构并熟练地管理其状态是任务中最困难的部分之一。
tPAuoQkkDoiu2550.jpg
图 3 :l 层的权值矩阵 W 和偏置向量 b 的维数。
神经网络层初始化
首先初始化每一层的权值矩阵 W 和偏置向量 b。在图 3 中。先准备一个为系数分配适当维数的清单。上标 [l] 表示当前层的索引 (从 1 数起),值 n 表示给定层中的单位数。假设描述 NN 架构的信息将以类似 Snippet 1 的列表形式传递到程序中,列表的每一项是一个描述单个网络层基本参数的字典:input_dim 是输入层信号向量的大小,output_dim 是输出层激活向量的大小,activation 是在内层使用的激活函数。
nn_architecture = [
{ "input_dim": 2, "output_dim": 4, "activation": "relu"},
{ "input_dim": 4, "output_dim": 6, "activation": "relu"},
{ "input_dim": 6, "output_dim": 6, "activation": "relu"},
{ "input_dim": 6, "output_dim": 4, "activation": "relu"},
{ "input_dim": 4, "output_dim": 1, "activation": "sigmoid"},
]
Snippet 1:包含描述特定神经网络参数的列表。该列表对应图 1 所示的 NN。
如果你对这个话题很熟悉,你可能已经在脑海中听到一个焦虑的声音:「嘿,嘿!这里有问题!有些领域是不必要的……」是的,这次你内心的声音是对的。前一层输出的向量是下一层的输入,所以实际上只知道一个向量的大小就足够了。但我特意使用以下符号来保持所有层之间目标的一致性,使那些刚接触这一课题的人更容易理解代码。
def init_layers(nn_architecture, seed = 99):
np.random.seed(seed)
number_of_layers = len(nn_architecture)
params_values = {}
foridx, layer inenumerate(nn_architecture):
layer_idx = idx + 1
layer_input_size = layer[ "input_dim"]
layer_output_size = layer[ "output_dim"]
params_values[ 'W'+ str(layer_idx)] = np.random.randn(
layer_output_size, layer_input_size) * 0.1
params_values[ 'b'+ str(layer_idx)] = np.random.randn(
layer_output_size, 1) * 0.1
returnparams_values
Snippet 2:初始化权值矩阵和偏置向量值的函数。
最后是这一部分最主要的任务——层参数初始化。看过 Snippet 2 上的代码并对 NumPy 有一定经验的人会发现,矩阵 W 和向量 b 被小的随机数填充。这种做法并非偶然。权值不能用相同的数字初始化,不然会出现「对称问题」。如果所有权值一样,不管输入 X 是多少,隐藏层中的所有单位都相同。在某种程度上,我们在初始阶段就会陷入死循环,无论训练模型时间多长、网络多深都无法逃脱。线性代数是不会被抵消的。
在第一次迭代中,使用较小的数值可以提高算法效率。通过图 4 所示的 sigmoid 函数图可以看到,对于较大数值,它几乎是平的,这十分影响 NN 的学习速度。总之,使用小随机数进行参数初始化是一种简单的方法,能保证我们的算法有足够好的起点。准备好的参数值存储在带有唯一标定其父层的 python 字典中。字典在函数末尾返回,因此算法的下一步是访问它的内容。
vIILIZG45DTiBQ9P.jpg
图 4:算法中使用的激活函数。
激活函数
我们将使用的函数中,有几个函数非常简单但功能强大。激活函数可以写在一行代码中,但却能使神经网络表现出自身所需的非线性性能和可表达性。「没有它们,我们的神经网络就会变成由多个线性函数组合而成的线性函数。」可选激活函数很多,但在这个项目中,我决定使用这两种——sigmoid 和 ReLU。为了能够得到完整循环并同时进行前向和反向传播,我们还需要求导。
def sigmoid(Z):
return1/( 1+np.exp(-Z))
def relu(Z):
returnnp.maximum( 0,Z)
def sigmoid_backward(dA, Z):
sig = sigmoid(Z)
returndA * sig * ( 1- sig)
def relu_backward(dA, Z):
dZ = np.array(dA, copy = True)

dZ[Z
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则 返回列表 发新帖

abooooooo当前离线
新手上路

查看:187 | 回复:0

快速回复 返回顶部 返回列表