【神经网络】11行Python代码实现的神经网络

栏目: Python · 发布时间: 6年前

内容简介:【神经网络】11行Python代码实现的神经网络

今天看了一篇神经网络的文章,作者用11行就实现了一个神经网络,原文地址: A Neural Network in 11 lines of Python (Part 1) ,深为叹服,翻译如下。

http://python.jobbole.com/82758/

BP(Back Propagation)神经网络是1986年由Rumelhart和McCelland为首的科学家小组提出,是一种 按误差逆传播算法训练的多层前馈网络 ,是目前应用最广泛的神经网络模型之一。BP网络能学习和存贮大量的 输入-输出模式映射关系 ,而无需事前揭示描述这种映射关系的数学方程。它的学习规则是使用梯度下降法,通过反向传播来不断调整网络的权值和阈值,使 网络的误差平方和最小 。BP神经网络模型拓扑结构包括输入层(input)、隐层(hidden layer)和输出层(output layer)。

概要: 直接上手代码是最好的学习方式. 这篇教程通过使用 Python 语言实现一段非常简单的示例代码来讲解 back propagation (BP反向传播算法)算法。

直接上代码:

X = np.array([ [0,0,1],[0,1,1],[1,0,1],[1,1,1] ])  
y = np.array([[0,1,1,0]]).T  
syn0 = 2*np.random.random((3,4)) - 1  
syn1 = 2*np.random.random((4,1)) - 1  
for j in xrange(60000):  
    l1 = 1/(1+np.exp(-(np.dot(X,syn0))))
    l2 = 1/(1+np.exp(-(np.dot(l1,syn1))))
    l2_delta = (y - l2)*(l2*(1-l2))
    l1_delta = l2_delta.dot(syn1.T) * (l1 * (1-l1))
    syn1 += l1.T.dot(l2_delta)
    syn0 += X.T.dot(l1_delta)

11行代码就实现了一个神经网络....然而,这也是一个过于简洁的神经网络。下面我就将分步讲解这个神经网络的实现,以帮助读者了解并使用BP反向传播算法。

第一部分: 简洁的神经网络

神经网络使用反向传播算法训练输入数据来预测输出数据。

Inputs Output
0 0 1 0
1 1 1 1
1 0 1 1
0 1 1 0

观察上表,考虑如何用三个维度的输入数据去预测一个维度的输出。我们可以通过一个简单方法来解决这个问题:测量统计一下输入值与输出值之间的关系。使用这个方法,我们可以发现左边的输入数据与右边的输出数据是紧密相关的。直观意义上讲,BP反向传播算法便是通过这种方式来衡量数据间的统计关系进而得到模型的。下面开始动手实践。

两层的神经网络

import numpy as np

# sigmoid function
def nonlin(x,deriv=False):  
    if(deriv==True):
        return x*(1-x)
    return 1/(1+np.exp(-x))

# input dataset
X = np.array([  [0,0,1],  
                [0,1,1],
                [1,0,1],
                [1,1,1] ])

# output dataset            
y = np.array([[0,0,1,1]]).T

# seed random numbers to make calculation
# deterministic (just a good practice)
np.random.seed(1)

# initialize weights randomly with mean 0
syn0 = 2*np.random.random((3,1)) - 1

for iter in xrange(10000):

    # forward propagation
    l0 = X
    l1 = nonlin(np.dot(l0,syn0))

    # how much did we miss?
    l1_error = y - l1

    # multiply how much we missed by the 
    # slope of the sigmoid at the values in l1
    l1_delta = l1_error * nonlin(l1,True)

    # update weights
    syn0 += np.dot(l0.T,l1_delta)

print "Output After Training:"  
print l1

输出结果:

Output After Training:

[[ 0.00966449]

[ 0.00786506]

[ 0.99358898]

[ 0.99211957]]
变量 定义
X 输入数据集,形式为矩阵,每 1 行代表 1 个训练样本。
y 输出数据集,形式为矩阵,每 1 行代表 1 个训练样本。
l0 网络第 1 层,即网络输入层。
l1 网络第 2 层,常称作隐藏层。
syn0 第一层权值,突触 0 ,连接 l0 层与 l1 层。
* 元素相乘,故两等长向量相乘等同于其对等元素分别相乘,结果为同等长度的向量。
- 元素相减,故两等长向量相减等同于其对等元素分别相减,结果为同等长度的向量。
x.dot(y) 如果x 和 y 为向量,则进行点积操作;如果均为矩阵,则进行矩阵相乘操作;若其中之一为矩阵,则进行向量与矩阵相乘操作。

在"Output After Training"可以看到, 运行成功!!! 在我描述运行过程之前, 建议读者先自己运行一次以有一个对代码运行的直观感受。最好是在ipython notebook中完整跑通代码(自己写脚本也行,但强烈建议notebook). 下面是理解代码的几个关键点:

  • 对比l1层在首次迭代和最后一次迭代的状态
  • 观察"nonlin" 函数, 正是它将一个概率值作为输出提供给了我们
  • 观察l1_error在迭代中是如何变化的
  • 将第36行拆开,这里有大部分的秘密武器
  • 观察第39行,网络中所有的操作都是在为这步运算作准备

下面,我们一行一行地来分析代码,理解其原理。

建议:用两个屏幕打开这篇文章,这样就能一边阅读代码,一边阅读文章。 撰写博客时我正是这么做的:)。

第1行:导入线性代数 工具numpy , 这也是我们唯一依赖的库。

第4行:这是"非线性"部分. 虽然它可以是许多种函数,但在这里,使用的非线性映射为一个称作 “sigmoid” 的函数( S(x)=1/(1+e -x ) )。 Sigmoid 函数 可以将任意数映射到 0-1 之间. 我们用它将 数值转变为概率 . 对于神经网络的训练, Sigmoid 函数还有其它几个非常不错的特性。

【神经网络】11行 <a href='https://www.codercto.com/topics/20097.html'>Python</a> 代码实现的神经网络

第5行:注意,当 deriv=true 的时候,这个函数还可以返回 sigmoid 函数的倒数。 sigmoid 函数的优秀特性之一就是只用它的输出值就可以得出它的导数。如果 sigmoid 函数的输出值为 out ,那么其导数值为: out*(1-out) 。效率非常高。

如果不熟悉导数,可以将导数理解为 sigmoid 函数在一个给定点上的斜率 (不同的点有不同的斜率)。 想了解更多关于导数的知识,可以参考可汗学院的 导数求解过程

第10行: 这行代码将我们的输入数据集初始化为 numpy 的矩阵。每一行都是单一的“训练实例” 。每一列都对应着一个输入节点。因此,我们的神经网络有3个输入节点和4个训练实例。

第16行: 这一行初始化我们的输出数据集。在本例中, 我为了节省空间就以水平格式定义了数据集(1行和4列)。".T" 表示转置函数。在转置后, y 矩阵就有4行和1列。和输入一样,每一行都是一个训练实例,并且每一列都是一个输出节点。所以,我们的神经网络有3个输入和1个输出。

第20行: 为随机数设定随机种子是一个好习惯。这样一来,我们得到的初始权重集仍然是随机分布的,不过在每次开始训练时,得到的权重初始集分布是完全一致的。这便于我们观察策略变动是如何影响神经网络的训练的。

第23行: 这是神经网络的权重矩阵(可以输出 syn0 进行观察, syn0=[[-0.16595599],[ 0.44064899],[-0.99977125]] ,与输入节点数保持一致)。 syn0 是"synapse zero"的简称,意味着0层突触。因为我们只有2层(输入层和输出层),所以我们只需要一个权重矩阵来连接它们。权重矩阵的维度为 (3,1) ,因为我们有 3 个输入节点和 1 个输出节点。换一种方式解释,因为 l0 层大小为 3l1 层大小为 1 ,因为,如果我们想要连接 l0l1 ,就需要一个维度为 (3,1) 的中间矩阵。

同时,要注意到随机初始化的权重矩阵均值为 0 。关于初始化权重,这里面可有不少学问。因为现在我们还只是练习而已,所以在权值初始化时设定均值为 0 就可以了。

另一个要注意的点是所谓的“神经网络”其实就是指的这一个矩阵。尽管我们有 l0 层和 l1 层,但它们只是基于基础数据集的瞬时变化值而已,我们不需要存储它们。在学习训练过程中,只存储 syn0

第25行: 本行开始就是实际的神经网络代码了。 for 循环迭代式地循环多次执行训练代码,使得我们的网络能更好地拟合训练集。

第28行: 网络第一层 l0 就是我们的输入数据。接下来详细阐述。输入集 X 包含 4 个训练实例,我们同时对所有实例进行处理。这种训练方式被称为“整批”训练。因此,虽然我们有 4 个不同的 l0 行,但你可以将其整体视为单个训练实例,这样做并没有什么差别。(我们可以在不改动代码的前提下,一次性装入 1000 个甚至 10000 个实例)。

第29行: 这是神经网络的预测步骤。首先我们让网络根据输入尝试预测输出。然后我们通过观察预测结果作出一些调整,让神经网络下轮迭代中表现得更好一点。

这一行实际上包含两个步骤。首先是 l0syn0 两个矩阵相乘;第二部是让我们的输出进入 sigmoid 函数进行处理。观察矩阵相乘时的维度变化过程:

(4 x 3) dot (3 x 1) = (4 x 1)

矩阵相乘是有约束的,比如等式靠中间的两个维度必须一致。最终产生的矩阵,其行数为第一个矩阵的行数,列数则为第二个矩阵的列数。

因为我们装入了 4 个训练实例,所以最终也得到了 4 个猜测结果,即一个( 4 x 1 )的矩阵。每一个输出都对应了给定输入下网络对正确结果的一个猜测。这也能直观地解释:为什么我们可以“载入”任意数目的训练实例。在这种情况下,矩阵乘法仍是奏效的。:)

第32行:所以,对于每一输入, l1 都会有对应的一个“猜测”结果。然后将真实的结果 y 与猜测结果 l1 作减,就可以对比得到网络预测的效果怎么样。 l1_error 是一个有正数和负数组成的向量,它可以反映出网络的误差有多大。

第36行: 干货到了!这里就是秘密武器所在!本行代码信息量比较大,所以将它拆成两部分来分析。

求导

nonlin(l1,True)

如果 l1 可表示成 3 个点,如下图所示,以上代码就可产生下图的三条斜线。注意到,在 x=2.0 处(绿色点)输出值很大时,以及在 x=-1.0 处(紫色点)输出值很小时,斜线都非常十分平缓。如你所见,斜度最高的点位于 x=0 处(蓝色点)。这一特性非常重要。另外也可发现,所有的导数值都在 01 范围之内。 【神经网络】11行Python代码实现的神经网络

整体认知:误差项加权导数值

l1_delta = l1_error * nonlin(l1,True)

“误差项加权导数值”这个名词在数学上还有更为严谨的描述,但是我觉得这个定义准确地捕捉到了算法的意图。 l1_error(4,1) 大小的矩阵, nonlin(l1,True) 返回的便是 (4,1) 的矩阵。而我们所做的就是将其“ 逐元素地 ”相乘,得到的是一个 (4,1) 大小的矩阵 l1_delta ,它的每一个元素就是元素相乘的结果。

当我们将“斜率”乘上误差时, 实际上就在以高确信度减小预测误差 。再看下 sigmoid 函数曲线图!当斜率非常平缓时(接近于 0 时),那么网络输出要么是一个很大的值,要么是一个很小的值。这就意味着网络十分确定是否是这种情况,或是另一种情况。然而,如果网络的判定结果对应( x = 0.5,y = 0.5 )附近时,它便就不那么确定了。对于这种“似是而非”预测情形,我们对其做最大的调整,而对确定的情形则不多做处理,乘上一个接近于 0 的数,则对应的调整量便可忽略不计。

第 39 行:更新网络已准备就绪!下面一起来看下一个简单的训练示例。 【神经网络】11行Python代码实现的神经网络

在这个训练示例中,我们已经为权值更新做好了一切准备。下面让我们来更新最左边的权值(9.5)。

权重更新量 = 输入值 * l1_delta

对于最左边的权值,在上式中便是 1.0 乘上 l1_delta 的值。可以想得到,这对权值 9.5 的增量是可以忽略不计的。为什么只有这么小的更新量呢?是因为我们对于预测结果十分确信,而且预测结果有很大把握是正确的。误差和斜率都偏小时,便意味着一个较小的更新量。考虑所有的连接权值,这三个权值的增量都是非常小的。

【神经网络】11行Python代码实现的神经网络

由于采取的是“整批”训练的机制,因此上述更新步骤是在全部的 4 个训练实例上进行的,这看上去也有点类似于图像。那么,第 39 行做了什么事情呢?在这简单的一行代码中,它共完成了下面几个操作: 首先计算每一个训练实例中每一个权值对应的权值更新量,再将每个权值的所有更新量累加起来,接着更新这些权值 。亲自推导下这个矩阵相乘操作,你便能明白它是如何做到这一点的。

延伸

现在,我们已经知道了神经网络是如何进行更新的。回过头来看看训练数据,作一些深入思考。当输入和输出均为 1 时,我们增加它们间的连接权重;当输入为 1 而输出为 0 时,我们减小其连接权重。

Inputs Output
0 0 1 0
1 1 1 1
1 0 1 1
0 1 1 0

因此,在如上 4 个训练示例中,第一个输入结点与输出节点间的权值将 持续增大或者保持不变 ,而其他两个权值在训练过程中表现为 同时增大或者减小 (忽略中间过程)。这种现象便使得网络能够基于输入与输出间的联系进行学习。

转载请注明出处

http://www.zgljl2012.com/shen-jing-wang-luo-11xing-pythondai-ma-shi-xian-de-shen-jing-wang-luo/


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

七周七语言

七周七语言

Bruce A.Tate / 巨成、戴玮、白明 / 人民邮电出版社 / 2012-5-8 / 59.00元

内容简介: 从计算机发展史早期的Cobol、Fortran到后来的C、Java,编程语言的家族不断壮大。除了这些广为人知的语言外,还涌现了Erlang、Ruby等后起之秀,它们虽被喻为小众语言,但因其独特性也吸引了为数不少的追随者。 Bruce A. Tate是软件行业的一名老兵,他有一个宏伟目标:用一本书的篇幅切中要害地探索七种不同的语言。本书就是他的成果。书中介绍了Ruby、Io、......一起来看看 《七周七语言》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试