多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
http://www.jianshu.com/p/bfabcc47cb4d http://iamtrask.github.io/2015/07/12/basic-python-network/ AlphaGo在人机大战中战胜李世乭让深度学习这个名词在大众口中广为传播,深度学习的强大,也让其增加了一层神秘色彩,似乎这是名校博士才能理解的算法。虽然相关的论文自己也无法看懂,然而,在网络查阅了无数资料之后,才发现如果不去纠结理论的证明,只关心结论的话,神经网络算法也并非高不可攀。   在查阅资料的过程中,有幸看到iamtrask的一篇文章A Neural Network in 11 lines of Python (Part 1),文章抛弃推理,直接从结论出发,用简单的Python实现了一个简易的神经网络算法,对于像我这样数学基础一般的初学者,具有很大的启发作用。本文主要参考这篇文章,并加入自己的理解,试图以易于理解的方式阐述其思路,如果有表述不当的地方,您可以参考原文,或者参考其中文翻译。 什么是神经网络算法   在解释什么是神经网络之前,我们先明确几个机器学习中的术语:特征,样本,监督学习 样本:可以用于学习的个体,我们可以获得这些个体的其某些信息,并且我们已知这些个体的类别 特征:同一属性在不同个体所体现出来的特点,在神经网络中,特征应该是易于提取的 监督学习:根据样本中个体的特征以及类别,然后创建我们的判定规则,并依靠这些调查所得信息,调整我们的规则,使得规则的预测结果尽可能接近我们的样本,这样,我们利用这个规则和新个体的特征信息,来判定其所属分类   举个例子,我们想要构建一套性别识别算法,而我们能够获得的特征信息只有三种:这个人不是长头发;衣服颜色是红色;身高是否超过了178cm。那以一个正常成年人的思维,我们应该采用什么策略来判断这个人的性别呢?   我想大部分人都会采用和我一样的策略:依据我们平时的经验,一个人如果留长头发,很大可能这个人是女的,而一个人穿红色衣服,很难判断其性别,一个人身高超过175cm,在中国人里面可能都属于偏高的,而依据我们的经验,男性的平均身高要大于女性,所以单纯通过身高这个信息,我们推断这个人是男性的可能性较大。   以上推测过程中,我们对每一个条件的推断都基于一个词,经验,这里的经验就是我们长期以来观察身边的人的特征所留下的记忆。而我们的推测结果主要用了一个词汇,可能性,其代表基于某条已知信息推测出某个未知结论的概率。   科学家将我们的思维方法归纳为信息在神经元之间的传导过程,这些神经元每一个都处理及简单的信息,但是通过无数个神经元之间错综复杂的连接传导,使得我们的大脑能够处理及其复杂的信息。神经网络算法就是对我们大脑思维方式的抽象,比如在上面的例子中,我们将每一个特征,输入到一个神经元,这些接受输入的神经元构成了第一层神经网络,也叫做输入层,我们的目标是判断一个人的性别,是男是女分属于两个不同类别,我们将其抽象为一个神经元,这个神经元构成神经网络的输出层,每一个特征(输入层神经元),都有到分类(输出层神经元)的连接,也就是从特征转化为分类的概率(权重),通过综合各个连接的权重,传送到输出神经元。 人工神经网络模型   以上便是神经网络算法最简单的模型,神经网络算法的学习过程中,首先随机初始化各层神经元之间的连接权重(上文中的可能性),然后输入样本进行预测,根据预测的误差,调整神经网络中的权重,完成这个过程之后,我们的神经网络就训练好了。所以,神经网络训练的结果,就是得到各个连接的权重(经验),这样,一旦有新的数据输入神经网络的时候,就能推测出其分类了。 神经网络算法实例 问题定义   还是用上面的预测性别的例子,现在将其数学化,假设在一群人中,我们只能获得每个人的三个特征: 特征1:长发(1)还是短发(0) 特征2:衣服颜色是红色(1)还是不是红色(0) 特征3:身高大于178cm(1)还是不超过178(0)   假设我们只知道其中四个人的性别(男:0,女:1),我们需要依据这四个人的三个特征以及性别训练一个神经网络,用于预测一个人的性别。样本信息如下: 头发 衣服 身高 性别 0 0 1 0 1 1 1 1 1 0 1 1 0 1 1 0   下面我们先实现最简单的单层神经网络。我们用X表示输入的特征向量,由于每个样本有三个特征,一共有四个样本,所以我们定义一个4X3的矩阵,每一行代表一个样本,如下代码所示。其中,NumPy是Python语言的一个扩充程序库。支持高级大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。 #import numpy import numpy as np # input dataset X = np.array([ [0,0,1], [0,1,1], [1,0,1], [1,1,1] ])   而四个样本对应输出(分类结果)我们用一个1X4的矩阵表示。“.T” 为转置函数,转置后变成了4X1的矩阵。同我们的输入一致,每一行是一个训练实例,而每一列(仅有一列)对应一个输出节点。因此,这个网络还有三个输入和一个输出。代码如下所示: # output dataset y = np.array([[0,0,1,1]]).T   训练开始之前,我们先要初始化神经网络的权重,由于输入层有三个神经元,而输出结果只有一个神经元,所以权重矩阵为3X1。由于一般初始化权重是随机选择的,因此要为随机数设定产生的种子,如下第一行代码所示。这样可以使每次训练开始时,得到的训练随机数都是一致的。这样便于观察策略变动是如何影响网络训练的,消除初始权重的影响。   对于第二行代码,这里由于我们要将随机初始化的权重矩阵均值设定为 0 (至于权重矩阵的初始化,大家有兴趣的话,请查看相关资料)。因此使用第二行代码来计算syn0(第一层网络间的权重矩阵),如下所示: # 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   为了将输出的权重归一化,定义一个sigmoid函数,其定义为:   sigmoid函数可以用以下Python代码实现,其中,deriv参数表示是否计算的是其导数值: # sigmoid function def nonlin(x,deriv=False): if(deriv==True): return x*(1-x) return 1/(1+np.exp(-x))   其函数图像如下图所示: sigmoid   sigmoid函数的特点是,其导数可以用其自身表示出来,在计算的时候,我们只需要计算出其函数值,就可以计算出其导数值,从而可以减少浮点运算次数,提高效率,其导数如下:   接下来,我们开始训练神经网络: for iter in range(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)   我们的训练过程迭代10000次,以得到一个较优的结果,每一次迭代的过程可以描述为: 计算输入层的加权和,即用输入矩阵L0乘以权重矩阵syn0,并通过sigmid函数进行归一化。得到输出结果l1; 计算输出结果L1与真实结果y之间的误差L1_error; 计算权重矩阵的修正L1_delta,即用误差乘以sigmoid在L处的导数; 用L1_delta更新权重矩阵syn0   此处着重解释 下第三步,这里利用的是梯度下降法,算法的原理我们暂不深究,只需要明白其目的是为了使迭代后的误差逐渐减小即可。   一次训练过程的参数更新如下图所示:   由于我们的输入X中一共有四个样本,我们进行“批量的训练”,所以其过程类似于下图所示:   在上述代码中,我们通过10000次迭代,我们得到的输出结果如下: Output syn0 After Training: [[ 9.67299303] [-0.2078435 ] [-4.62963669]]   可以看出,syn0的第一个元素,也就是第一个输入特征(长发)的权重最大,而第二个和第三个特征都很小,所以神经网络学习的结果是加重第一个特征的权重,而其他两个特征对于是女性这个推测的贡献较小,所以减小其权重。为了验证训练结果,我们加入两组新数据,(短头发,红衣服,矮个子),(长头发,不是红衣服,矮个子),并用神经网络来进行分类: X_new = np.array([[0,1,0], [1,0,0]]) y_new = np.dot(X_new,syn0)   计算结果如下: Predicte With syn0: [[-0.2078435 ] [ 9.67299303]] 二层神经网络   上面的例子中,我们只用了一层神经网络,这只能解决线性问题,而现实中,一个孤立的特征并不是对应一个分类,还是用上面的例子说明:上面的问题中,我们假定了长头发是女性的概率一定大于男性,高个子是男性的概率一定大于女性,这种假设中,特征和分类是一种确定的关系,而特征之间没有依赖关系。而现在,我修改这种假设,在穿红衣服的人群中,长头发更可能是女性,而在穿其他颜色的衣服中,短头发更有可能是女性,此时,我们上面的神经网络模型就失效了,因为我们无法直接建头发这个输入特征到性别这个输入的直接联系。   为了解决上面的问题,我们需要在加入一层神经网络,将输入层的特征进行组合,然后在传导到输出层,这就是二层神经网络的模型,其示意图如下:   中间加入的这一层佳作隐含层,由于这一层的加入,我们多了一层传导,所以初始化的时候需要再加入一个权重矩阵: syn1 = 2*np.random.random((4,1)) - 1   两层神经网络的学习更新过程如下: for j in range(60000): # Feed forward through layers 0, 1, and 2 l0 = X l1 = nonlin(np.dot(l0,syn0)) l2 = nonlin(np.dot(l1,syn1)) # how much did we miss the target value? l2_error = y - l2 if (j% 10000) == 0: print("Error:" + str(np.mean(np.abs(l2_error)))) # in what direction is the target value? # were we really sure? if so, don't change too much. l2_delta = l2_error*nonlin(l2,deriv=True) # how much did each l1 value contribute to the l2 error (according to the weights)? l1_error = l2_delta.dot(syn1.T) # in what direction is the target l1? # were we really sure? if so, don't change too much. l1_delta = l1_error * nonlin(l1,deriv=True) syn1 += l1.T.dot(l2_delta) syn0 += l0.T.dot(l1_delta)   结合前面的单层神经网络的实现,就很容易理解上面的代码了,代码中,L0的输出没有直接作为最终输出层,而是传导给了L2层,L2层以相同的方式传导到输出层。而更新权重的时候,采用的是相反的过程,先依据L2输出的误差,更新syn1,再用L2的误差乘以syn1,作为L1层的误差,最后用同样的方法更新第一层权重矩阵syn0 结语   这篇文章以最简单的方式构建了一个基本的神经网络,虽然离实用还相去甚远,但是已经初现神经网络的雏形框架,如果需要构建一个实用级别的神经网络,还需要加入一些其他的功能,原作者建议我们从以下这些概念开始入手,优化我们的神经网络): Alpha Bias Units Mini-Batches Delta Trimming Parameterized Layer Sizes Regularization Dropout Momentum Batch Normalization GPU Compatability 其他脑洞 作者:ZPPenny 链接:http://www.jianshu.com/p/bfabcc47cb4d 來源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。