神经网络初试(二)

引入

今天我们就继续上次的那篇文章,谈一谈神经网络中比较基础也比较核心的部分。按顺序分别是,前向传播、反向传播、权重更新以及代码实现。

在进入正题之前,还是有必要介绍一些事情,方便对于下文的理解。

第一、本文,乃至本系列的文章,其主体都是全连接神经网络。全连接神经网络应该是神经网络中最简单的一种了。顾名思义,全连接指的是对于任意一层的某个神经元来说,它与前后两层的所有神经元之间都存在连接。如图所示:

这个神经网络有三层,从左往右分别是之前提到过的输入层、隐藏层、输出层。每一层都有三个神经元,一般情况下,这代表着它的输入应该是一个三维数据,也就是三维向量,例如三维空间中任意一个坐标(x,y,z)。隐藏层的三个神经元,把它当成处理数据的黑盒就好了。输出层的三个神经元,则代表神经网络的输出也是一个三维的数据,例如它的输出也是一个坐标(a,b,c)

之前我们也提到了偏置项,使用偏置项来表示输入层之外的神经元的阈值,并且提到了,可以在每一层神经元(除了输出层)上方添加一个偏置神经元,形式更加统一的表示神经网络。上方的图片是不包含偏置神经元的神经网络结构图,我们可以再看一个包含偏置神经元的神经网络。


诺,对于这个神经网络而言,它的前两层最上方的神经元都是偏置神经元,其输入恒为1,它与下一层的神经元连接的权重,则表示着相应神经元的阈值。

当然啦,倘若我们不把最上面两个神经元看作偏置神经元,这就是一个非全连接的神经网络。这不是我们研究的对象~

第二、本文提到的神经网络都是前馈的,也就是输入层接受外界输入, 隐含层与输出层神经元对信号进行加工, 最终结果由输出层神经元输出。数据在其中是从输入到输出单向传递的,不会从输出层又向输入层传回来。实际的应用中,自然也是存在这种可以反馈的,也就是信号回传的神经网络,但这也不是我们的研究对象。

所以,我们的研究对象就是全连接的,前馈的神经网络,也就是最简单的一种神经网络。它是之后神经网络各种变形的基础,所以还是很有必要了解掌握的。

接下来进入正题。

前向传播

前向传播其实我们在上一篇文章中提过,所谓的前向传播,其实就是神经网络进行预测输出时,所进行的操作。我们以一个简单的神经网络为例再次说明这一过程,作为接下来的铺垫。

上图的神经网络就是我们用于介绍前向传播与反向传播的主体啦。它的输入层是一个二维向量(x_1,x_2),输出也是一个二维向量(y_1,y_2),隐藏层有三个神经元(a_1,a_2,a_3)。一般来说,前向传播过程中一个神经元对应一个一维数据。

对于权重,我们使用w_{ij}来表示,w_{ij}代表这一层第i个神经元与下一层第j个神经元之间的权重,是一个具体的数值。这里为了简化,我们暂时不把偏置项加进去,换句话说,我们将所有神经元的阈值都置为0。因此,这是一个只有权重,阈值为0的全连接前馈神经网络。

除了权重和阈值,激活函数也是神经网络的核心之一。这里我们采用较为传统的sigmoid函数作为激活函数,记为h(x)。因此,激活函数
h(x)=\frac{1}{1+e^{-x}}

接下来我们来叙述一下前向传播的过程:

给定一个输入向量(x_1,x_2),我们可以计算出隐藏层神经元的输入向量,记为
X_{hidden}=[a_1;a_2;a_3]
X_{hidden}是一个列向量(本文以分号为分隔符的向量均为列向量),其中每一个分量的计算公式为
\begin{cases} a_1=w_{11}x_1+w_{21}x_2 \\ a_2=w_{12}x_1+w_{22}x_2 \\ a_3=w_{13}x_1+w_{23}x_2 \end{cases}
这个过程在上一篇文章中已经提到了,为了方便表示以及计算,我们可以使用矩阵来表示这一过程。(没学过线性代数的自己去学一下哦)

这个其实很简单,我们将输入向量(列向量)记为
I=[x_1;x_2]
这其实也是一个2×1的矩阵,两行一列。类似的,隐藏层的输入向量X_{hidden}是一个3×1的矩阵。之后,我们记输入层与隐藏层之间的矩阵为W_{input\_hidden},所以
W_{input\_hidden}=\begin{bmatrix} w_{11} & w_{21} \\ w_{12} & w_{22} \\ w_{13} & w_{23} \end{bmatrix}
这是一个3×2的矩阵。

所以
X_{hidden}=W_{input\_hidden} \cdot I
不过X_{hidden}仅仅是隐藏层的输入,也就是隐藏层每一个神经元接收到的数据。由于我们并没有考虑阈值,所以这里直接对隐藏层的输入数据进行激活,就得到了隐藏层每一个神经元的输出,即
O_{hidden}=h(X_{hidden})=sigmoid(X_{hidden})

这样,数据就由最原始的输入I,先转化为隐藏层接收的输入数据X_{hidden},又转化为了隐藏层的输出O_{hidden}

同理,我们将隐藏层的输出再传递到输出层去。记隐藏层与输出层之间的权重矩阵为
W_{hidden\_output}=\begin{bmatrix} w_{11}^{'} & w_{21}^{'} &w_{31}^{'} \\ w_{12}^{'} & w_{22}^{'} &w_{32}^{'}\\ \end{bmatrix}
其中w_{ij}^{'}表示隐藏层第i个神经元与输出层第j个神经元之间的权重。

接着,我们计算出输出层接收到的二维向量为
X_{output}=W_{hidden\_output} \cdot O_{hidden}
最后,再经过激活函数,得到最终的输出
O_{output}=sigmoid(X_{output})=[y_1;y_2]

就这样,我们实现了上述神经网络前向传播的过程,及其矩阵表示,实现了从输入到输出的转化。即
\begin{cases} X_{hidden}=W_{input\_hidden} \cdot I =W_{input\_hidden} \cdot [x_1;x_2]\\ O_{hidden}=h(X_{hidden})=sigmoid(X_{hidden}) \\ X_{output}=W_{hidden\_output} \cdot O_{hidden} \\ O_{output}=sigmoid(X_{output})=[y_1;y_2] \end{cases}

神经网络的前向传播,就是这么一个从x \longrightarrow y的过程。当然,这里的x,y都可以是任一维度的数据。

误差反向传播

前向传播的过程,是我们已知权重矩阵W,实现从输入x到输出y的过程。而误差的反向传播,解决的则是已知输入和输出,求解合适的权重矩阵W的过程。

之前提到过,机器学习三要素是模型、策略和算法。放在当前语境下,模型就是上文中那个简单的神经网络。策略,则是我们衡量误差的手段,这里我们使用最经典的平方损失函数作为损失函数,即我们的策略。记对于某个样本(x,y),该神经网络模型进行预测时的误差为
E=\sum_{i=1}^n(y_i-o_i)^2
其中,y_i表示样本的实际输出值y的第i个分量,o_i表示的是将样本的实际输入值x输入神经网络后,输出层第i个神经元的输出值,也就是y的第i个分量的预测值。对于上述的神经网络,显然,n=2

问题来了,我们现在表示出了误差,也知道了我们希望通过调整每一层的权重矩阵W来缩小这个误差,那如何进行调整呢?

这里就不再引入了,直说吧,我们可以使用梯度下降法进行权重的调整。如果对于梯度下降法不太了解,可以看看我之前写的那篇关于梯度下降的文章,或者自行搜索一下。

我们之前使用梯度下降法,是基于
f(x+\Delta x)=f(x)+\nabla f(x) \Delta x+o(\Delta x)
这里f(x)就是损失函数,x就是参数,而\Delta x则是关于x进行的移动。对于一般的问题,比如一元线性回归,可以直接构造出这样一个等式,并且发现误差下降的方向,就是负梯度-\nabla f(x)的方向。之后一步步进行迭代,对于参数x进行调整。

但是对于神经网络,问题就没有这么简单了。神经网络是一层层叠加的,每两层之间都有一个权重矩阵。但是我们可以观察到的误差,仅仅是最后输出的误差,也就是预测值与实际值之间的误差。虽然这一层的误差是由之前的误差层层传递而来,但是我们很难直接通过最后一层的误差,对最前几层的权重矩阵进行调整。假设一个神经网络有10层,那么我们或许可以使用梯度下降,基于最后一层的误差,对最后一层的权重矩阵进行调整,但是对于第一层和第二层之间的权重矩阵,又要怎么构建“误差与权重”之间的关系呢?

当然,肯定是可以一步步构建的,但一定会很麻烦……因为中间涉及到的每一个权重矩阵,对于最后的误差都有影响,所以如果想直接一步步推导回来,那么误差E与第一个权重矩阵W_1的关系等式中,必然包含着W_2,W_3,...,W_9。感兴趣的,可以以一个三层的神经网络,自己尝试一下。

因此,我们的想法就是,将最终可以观察到的误差,一步步往回进行反向传播,得到每一层的输入值与输出值之间的误差,并基于此,使用梯度下降的方式对于相应的权重矩阵进行调整。可以理解为,每两层之间的权重矩阵都对应着一个误差,这个误差与相应的权重矩阵是直接相关的。这样就不用一层层跋山涉水将距离很远的误差与权重直接构建关系啦。

如上图所示,e_4是我们可以直接观察到的误差(=实际值-预测值),它由e_{41},e_{42}这两个分量组成,代表输出层两个神经元与实际值的误差。e_4W_4有着直接的关系,因此我们可以通过e_4W_4进行修正。同理,我们通过e_3,e_2,e_1分别对W_3,W_2,W_1进行调整。所以问题就转化为,我们如何通过e_4,求解得到e_3,e_2,e_1呢?

这就涉及到误差的反向传播啦,回到我们的那个模型上


对于这个三层的神经网络,我们可以观察到的误差是e_{21},e_{22},并且,我们知道这两层之间的权重,那如何求解第二层的误差e_{11},e_{12},e_{13}呢?

简单理解的话,我们可以把误差的反向传播过程,看成误差的分配过程。对于误差e_{21},与其直接有关的误差分别是e_{11},e_{12},e_{13}。在神经网络前向传播的过程中,e_{11},e_{12},e_{13}也通过权重w_{11},w_{21},w_{31}传递了过来,最终汇总形成了e_{21}

我们用一个简单的方式理解一下:不妨假设激活函数为h(x)=x,则y_1=w_{11}a_1+w_{21}a_2+w_{31}a_3,那么由全微分公式
\Delta y_1=\frac{\partial y_1}{\partial a_1}\cdot \Delta a_1+\frac{\partial y_1}{\partial a_2}\cdot \Delta a_2+\frac{\partial y_1}{\partial a_3}\cdot \Delta a_3

\Delta y_1=w_{11}\cdot \Delta a_1+w_{21}\cdot \Delta a_2+w_{31}\cdot \Delta a_3
如果我们将\Delta看成相应的误差,那么y_1的误差其实就是a_1,a_2,a_3误差的加权和。事实上误差的传播公式也是这么回事,感兴趣的可以自行搜索一下。

因此,如果我们现在知道了\Delta y_1,想要求解\Delta a_1,\Delta a_2,\Delta a_3,很自然的一个想法,就是根据权重再将\Delta y_1分解下去。即
\begin{cases} \Delta a_1=\frac{w_{11}}{w_{11}+w_{21}+w_{31}}\cdot \Delta y_1 \\ \Delta a_2=\frac{w_{21}}{w_{11}+w_{21}+w_{31}}\cdot \Delta y_1 \\ \Delta a_3=\frac{w_{31}}{w_{11}+w_{21}+w_{31}}\cdot \Delta y_1 \end{cases}
其实这样的分解应该不是正确的,因为此时
\Delta y_1=w_{11}\cdot \Delta a_1+w_{21}\cdot \Delta a_2+w_{31}\cdot \Delta a_3
并不一定成立。但这是一个很合理的分配方式,相信你也是这么认为的。

让我们回到上述模型,则e_{11}e_{21}那里分到的误差为
\frac{w_{11}}{w_{11}+w_{21}+w_{31}}\cdot e_{21}
同理,它从e_{22}那里分到的误差为
\frac{w_{12}}{w_{12}+w_{22}+w_{32}}\cdot e_{22}
由于e_{21}e_{22}本身并不存在直接关系,因此这两者分给e_{11}的误差直接相加,就可以得到
e_{11}=\frac{w_{11}}{w_{11}+w_{21}+w_{31}}\cdot e_{21}+\frac{w_{12}}{w_{12}+w_{22}+w_{32}}\cdot e_{22}
以此类推
\begin{cases} e_{11}=\frac{w_{11}}{w_{11}+w_{21}+w_{31}}\cdot e_{21}+\frac{w_{12}}{w_{12}+w_{22}+w_{32}}\cdot e_{22} \\ e_{12}=\frac{w_{21}}{w_{11}+w_{21}+w_{31}}\cdot e_{21}+\frac{w_{22}}{w_{12}+w_{22}+w_{32}}\cdot e_{22} \\ e_{13}=\frac{w_{31}}{w_{11}+w_{21}+w_{31}}\cdot e_{21}+\frac{w_{32}}{w_{12}+w_{22}+w_{32}}\cdot e_{22} \end{cases}

举个例子看一下


事实上,我们更加关心的不是误差本身,而是对于误差的分配,即将后一层的误差按照权重向前一层进行传递。所以,为了进一步的简化计算,我们直接将上式中的分子去掉,即
\begin{cases} e_{11}=w_{11}\cdot e_{21}+w_{12}\cdot e_{22} \\ e_{12}=w_{21}\cdot e_{21}+w_{22}\cdot e_{22} \\ e_{13}=w_{31}\cdot e_{21}+w_{32}\cdot e_{22} \end{cases}
有些同学会对此感到迷惑,因为他们的分母并不完全一样啊,怎么能说删就删呢?可以这么想,e_{11}来自于两个部分,分别是e_{21}e_{22},这两者首先不是直接相关的,因此我们可以对于误差直接相加,得到e_{11}。而其中来自于e_{21}的部分\frac{w_{11}}{w_{11}+w_{21}+w_{31}}\cdot e_{21},其分母是对于w_{11}的归一化处理,我们可以将其转化为w_{11}\cdot e_{21},这并不改变按权重分配的本质。同样的,可以将\frac{w_{12}}{w_{12}+w_{22}+w_{32}}\cdot e_{22}转化为w_{12}\cdot e_{22}。最后如之前一般再将二者直接相加,就得到了
e_{11}=w_{11}\cdot e_{21}+w_{12}\cdot e_{22}

明白了吗?其实应该是似懂非懂,因为整个过程在数学上有些缺乏严谨性,更多的是从直观理解的角度进行处理,比如误差直接按权分配,去除归一化的因子等等。如果觉得不太能接受,那可以去看看更加数学化的误差反向传播方法的推导。本文关于误差反向传播的说明,好像确实有种邪·教的感觉hhh。但是本质上的思想应该是一样的。

最后,为了方便表示,我们记
\begin{alignat}{2} error_{output} &=[e_{21};e_{22}] \\ error_{hidden}&=[e_{11};e_{12};e_{13}] \end{alignat}
则由上文,我们知道
error_{hidden}=\begin{bmatrix} w_{11} & w_{12} \\ w_{21} & w_{22} \\ w_{31} &w_{32} \end{bmatrix} \cdot error_{output}

error_{hidden}=W_{hidden\_output}^{T} \cdot error_{output}

权重更新

以上我们(或许?)知道了误差是如何进行反向传播的,并进行了矩阵的表示,接下来就可以根据误差对权重进行调整啦。

首先,对于神经网络任意一层的误差
E=\sum_{i=1}^n(y_i-o_i)^2
所以对于相应的权重矩阵中的任一个参数w_{j,k}
\frac{\partial E}{\partial w_{j,k}}=\frac{\partial \sum_{i=1}^n(y_i-o_i)^2}{\partial w_{j,k}}
再看一眼下图,我们会发现,E中与w_{jk}有关的部分,仅仅由后一层的第k个神经元有关,相应的误差部分,也仅仅与第k个神经元的误差有关。(例如下图中w_{11}只与e_{21}有关)所以


\frac{\partial E}{\partial w_{j,k}}=\frac{\partial }{\partial w_{j,k}}(y_k-o_k)^2
根据链式法则(微积分中的求导法则都忘了?)
\begin{alignat}{2} \frac{\partial E}{\partial w_{j,k}} & =\frac{\partial E}{\partial o_k}\cdot \frac{\partial o_k}{\partial w_{j,k}} \\ & = -2(y_k-o_k)\cdot \frac{\partial o_k}{\partial w_{j,k}} \\ & =-2(y_k-o_k)\cdot \frac{\partial }{\partial w_{j,k}} sigmoid(\sum_{j}w_{jk}\cdot o_{j}) \end{alignat}
解释一下o_{k}=sigmoid(\sum_{j}w_{jk}\cdot o_{j})这部分。首先,o_k是后一层第k个输出,o_{j}则是前一层第j个节点的输出,w_{jk}k则是固定的。例如上图,对于w_{11},它仅与e_{21}有关,而e_{21}相应神经元的输出o_{1},则与前一层的三个神经元都有关系,就是前文提到的前向传播啦。(说明:o_k=o_k^{l+1},o_j=o_j^ll表示神经网络第l层)

对于sigmoid激活函数
\frac{\partial }{\partial x}sigmoid(x) =sigmoid(x)(1-sigmoid(x))
所以
\begin{alignat}{2} \frac{\partial E}{\partial w_{j,k}} & =-2(y_k-o_k)\cdot \frac{\partial }{\partial w_{j,k}} sigmoid(\sum_{j}w_{jk}\cdot o_{j}) \\ & = -2(y_k-o_k)\cdot \frac{\partial\ sigmoid(\sum_{j}w_{jk}\cdot o_{j})}{\sum_{j}w_{jk}\cdot o_{j}} \cdot \frac{\partial \sum_{j}w_{jk}\cdot o_{j}}{\partial w_{j,k}} \\ & = -2(y_k-o_k)\cdot sigmoid(\sum_{j}w_{jk}\cdot o_{j})\cdot (1-sigmoid(\sum_{j}w_{jk}\cdot o_{j}))\cdot o_j \end{alignat}
最后的o_j,自然是上一层的第j个输出啦,刚刚讲过了。(说明:若o_k=o_k^{l+1},则o_j=o_j^ll表示神经网络第l层)

考虑到在进行迭代时,导数前面的系数意义不大(还有学习率支撑嘛),所以我们可以把式子中的2删掉,另外error=y_k-o_k,就得到了误差关于权重的导数
\frac{\partial E}{\partial w_{j,k}}= -(error)\cdot sigmoid(\sum_{j}w_{jk}\cdot o_{j})\cdot (1-sigmoid(\sum_{j}w_{jk}\cdot o_{j}))\cdot o_j

相应的参数更新公式则为
new\ w_{j,k}=old\ w_{j,k} - \alpha \frac{\partial E}{\partial w_{j,k}}
其中\alpha为学习率。

但是W是一个权重矩阵,所以我们有必要把上式也转化成矩阵的形式,便于计算。以上文的那个三层神经网络为例,第二层的权重更新公式为
W_{hidden\_output}=W_{hidden\_output}+\alpha*((error_{output}*O_{output}*(1-O_{output}))\cdot(O_{hidden}^{T}))
其中error_{output},O_{output},O_{hidden}均是上文提到过的前向传播过程中的列向量。

相信你可以自己写出W_{input\_hidden}的迭代公式了…

以上,参数的更新部分就全部结束了

MNIST数据集

接下来介绍一下MNIST手写数字图像集。MNIST是机器学习领域最有名的数据集之一,被应用于从简单的实验到发表的论文研究等各种场合。MNIST数据集是由0到9的数字图像构成的。训练图像有6万张,测试图像有1万张,主要分别用于训练和评价。

MNIST数据集的一般使用方法是,先用训练图像进行学习,再用学习到的模型度量能在多大程度上对测试图像进行正确的分类。

MNIST的图像数据是28像素 × 28像素的灰度图像(1通道),各个像素的取值在0到255之间。每个图像数据都相应地标有“2”、“3”、“9”等标签。因此,这些图像在进行分类时的输入特征就是28*28=784个位置的像素灰度取值,输出特征就是相应的最可能的数字标签。

由于这类图像是比较简单的灰度图,所以完全可以自己手写一些数字,将其转化成28×28的灰度图像,进而进行验证。更加直接地看到神经网络的力量。

原始数据集网址:http://yann.lecun.com/exdb/mnist/

代码实现

原始的数据集应该是无法直接使用的……这里提供一个网址:https://pjreddie.com/projects/mnist-in-csv/

该网站提供了将原始数据集转化为csv格式的代码,以及csv的文件。同时也是接下来的代码使用到的数据集文件,如果需要的话自行下载吧。

代码也放在下面了,没有依赖现成的第三方库,完全是把上文的过程用代码重述了一遍。如果你已经对于本文的内容很理解了,那么看这段代码应该没有任何问题。如果你看代码还是有问题,不如再把文章读一读?

# -*- coding: utf-8 -*-
import numpy as np
import scipy.special
import matplotlib.pyplot as plt
class neuralNetwork:
    def __init__(self,inputnodes,hiddennodes,outputnodes,learningRate):
        #定义一个三层的神经网络
        #初始化输入层节点数量,隐藏层节点数量,输出层节点数量
        self.inodes=inputnodes
        self.hnodes=hiddennodes
        self.onodes=outputnodes
        
        #初始化两层之间的权重矩阵
        self.wih=(np.random.rand(self.hnodes,self.inodes)-0.5)
        self.who=(np.random.rand(self.onodes,self.hnodes)-0.5)
        
        #初始化激活函数
        self.activation_function=lambda x:scipy.special.expit(x)
        
        #初始化学习率
        self.lr=learningRate
        pass
    
    #训练函数
    def train(self,inputs_list,targets_list):
        #将输入的列表转化为list便于计算
        inputs = np.array(inputs_list,ndmin=2).T
        targets=np.array(targets_list,ndmin=2).T
        
        #前向传播一次,计算隐藏层的输入和输出
        hidden_inputs=np.dot(self.wih,inputs)
        hidden_outputs=self.activation_function(hidden_inputs)
        
        #再前向传播一次,计算输出层的输入和输出
        final_inputs=np.dot(self.who,hidden_outputs)
        final_outputs=self.activation_function(final_inputs)
        
        #确定输出层的误差与隐藏层的误差
        output_errors=targets-final_outputs
        hidden_errors=np.dot(self.who.T,output_errors)
        
        #根据误差,使用梯度下降法调整权重
        self.who+=self.lr*np.dot((output_errors*final_outputs*(1.0-final_outputs)),np.transpose(hidden_outputs))
        self.wih+=self.lr*np.dot((hidden_errors*hidden_outputs*(1.0-hidden_outputs)),np.transpose(inputs))
        
        pass
    
    #查询函数
    def query(self,inputs_list):
        #将列表转化成矩阵
        inputs=np.array(inputs_list,ndmin=2).T
        
        #进行两次前向传播得到最终解
        hidden_inputs=np.dot(self.wih,inputs)
        hidden_outputs=self.activation_function(hidden_inputs)
        
        final_inputs=np.dot(self.who,hidden_outputs)
        final_outputs=self.activation_function(final_inputs)
        
        return final_outputs
    
input_nodes=784
hidden_nodes=100
output_nodes=10
lr=0.25
n=neuralNetwork(input_nodes,hidden_nodes,output_nodes,lr)
training_data_file=open("mnist_train.csv","r")
training_data_list=training_data_file.readlines()
training_data_file.close()
'''
for record in training_data_list:
    all_values=record.split(",")
    #ia=np.asfarray(all_values[1:]).reshape((28,28))
    #plt.imshow(ia,cmap="Greys",interpolation="None")  画出图像

    inputs=(np.asfarray(all_values[1:])/255.0*0.99)+0.01 #标准化处理
    targets=np.zeros(output_nodes)+0.01
    targets[int(all_values[0])]=0.99 #处理输出向量
    n.train(inputs,targets)
    pass
'''   

epochs=4
for e in range(epochs):
    for record in training_data_list:
        all_values=record.split(",")
        #ia=np.asfarray(all_values[1:]).reshape((28,28))
        #plt.imshow(ia,cmap="Greys",interpolation="None")  画出图像
    
        inputs=(np.asfarray(all_values[1:])/255.0*0.99)+0.01 #标准化处理
        targets=np.zeros(output_nodes)+0.01
        targets[int(all_values[0])]=0.99 #处理输出向量
        n.train(inputs,targets)
        pass


test_data_file=open("mnist_test.csv","r")
test_data_list=test_data_file.readlines()
test_data_file.close()

score=[]
for record in test_data_list:
    test_value=record.split(",")
    correct_label=int(test_value[0])
    #print(correct_label," correct_label")
    inputs=(np.asfarray(test_value[1:])/255.0*0.99)+0.01
    outputs=n.query(inputs)
    label=np.argmax(outputs)
    #print(label," network's answer")
    if(label==correct_label):
        score.append(1)
    else:
        score.append(0)
        pass
    pass
print("performance = ",sum(score)/len(score))

写得累死我了……其实除了反向传播那里有些邪·教外,其他部分都还好,当然我的叙述可能是有一些啰嗦。这篇文章比较适合完全不了解神经网络的朋友了解一下神经网络,其实并没有想象的那么困难,几十行代码就可以实现一个简单的神经网络了。

如果深入学习的话,不妨去听一听吴恩达的深度学习,会更专业一些……不,会专业很多。毕竟这个神经网络连偏置都没有。

最后,不管怎样,感谢你看到这里,写的不容易,看的就更不容易了……是不是应该把它拆成两篇?

不管了,就酱,bye~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容