浅谈神经网络理论与实践(一)(内附手写javascript源码)

浅谈神经网络理论与实践(一)

不经不觉2017年已经悄悄离我们而去了,在过去几年当中人工智能领域已经慢慢的成为了当下最为热门的关键词了。作为一个地地道道的码农的我,也想着抱着一腔热血打算好好吹残自己一番。

深度学习,算是当下比较接近人工智能领域的一个分支,从今天开始我将开始跟大家一起探讨一下深度学习中一种比较重要的技术:神经网络。在接下来的时间当中,我会跟大家一起来探讨神经网络的前世今生,当中包括:神经元(感知器),BP神经网络,卷积神经网络(CNN)等。

在介绍以上几种神经网络的同时,我将会在web上使用javascript实现鸢尾花数据集分类,图片压缩,手写体数字识别等应用例子(全部都附有源码噢)。这些例子均没有使用开发框架纯粹为了实践理论,在投产环境当中还是建议使用tensorflow,theano等开源框架,当然其中的代码可能会因为我个人理解的不到位而存在问题,所以各位大大发现任何问题,请务必告诉我喔。

隔壁老黄:(废话少说,赶紧上菜)

我:(咳咳…不说了,不说了,马上开始)


什么是神经网络

想要了解神经网络是什么,首先咱们先来了解一下何为神经元。还记得以前生物课的生物神经元的组成么?

隔壁老黄:(不记得!)

没关系,咱们先来看下图看一下神经元是长什么样子的。

这里写图片描述

神经元大致可以分为树突、突触、细胞体和轴突。当外部刺激通过树突(输入通道)进入传递到细胞体,细胞体根据信息量的大小判断为是否激活(细胞体的状态可分为激活与不激活),当细胞体被激活后会产生电脉冲。电脉冲沿着轴突并通过突触传递到其它神经元,这里的突触有着对电脉冲抑制或加强的作用。(请原谅的我盗图行为)

看完以上生物神经元的大致组成,下面我们一起来了解一下人工神经元的组成与工作原理吧。

这里写图片描述

由上图可见,左边的多个分支为输入参数(x1,x2,x3,…xn)所对应神经元的树突,中间的圆圈我们把它称为感知器(细胞体),而右边的箭头所指的output就是输出的结果。以上就是一个人工神经元(也称作为感知器)的大致结构组成,接下来我们来看它是如何工作的。

首先我们来看一个简单的分类问题:

这里写图片描述

上图有一组数据,已知这组数据只有两类,蓝色的圆圈为A类,红色的叉叉为B类。那么请想想如何用最简单而有效的方法把这两类数据给分出来?

这里写图片描述

最简单的方法就是在这两类数据之间画一条直线,如果有新的数据进来的时候,数据如果落到直线的左上方,那么他就属于A类啦,反之它就属于B类。以上的数据是分布在一个二维空间上的数据,一条直线就可以把数据分类出来,如果数据是三维的或者多维呢?那么咱们就可以使用一个平面或者超平面对数据进行分类操作了,这种分类器就是神经元(也称作感知器)。

以下是神经元的公式:

f(x)=i=0mwixi+b f ( x ) = ∑ i = 0 m w i ∗ x i + b

y=sign(f(x)) y = s i g n ( f ( x ) )

其中sign(n)函数代表为:

sign(x)={11if x > 0if x <= 0 s i g n ( x ) = { 1 if  x  > 0 − 1 if  x  <= 0

其中xi是输入参数(i代表有多少个输入参数),wi是权重值(每一个输入值对应一个权重值),b是偏置值,y是输出值。说白了就是对一堆输入值进行加权求和,然后再经过一个判别函数,当求和的值超出某个阀值的时候,输出值就为1,反之就为-1。以上就是神经元的向前计算的原理,接下来我们再来看下如何训练神经元。

从以上公式可以看出,x是已知参数,而w(权重值)和b(偏置值)是未知参数,那么其训练的目标就是找到一组合适权重值和偏置值(其实就是找出那条可以把数据一分为二的直线)。一般在训练神经元或神经网络的时候我们都会采用有监督的学习方式,所谓有监督学习方式就是要求训练的数据集是带有标签的,让每条数据经过神经元的判别后,所得出的结果,跟数据本身所标注的结果作比对,将比对后的结果作为这次训练的评分,根据评分的大小再判断是否继续迭代和修改权重值,而这种学习方式就称作为有监督学习

在本次例子当中,我们将会使用误差输出点到超平面的距离作为损失函数(作用于评价每次迭代的好坏),而训练的目标为最小化损失函数 L(w,b)=0 L ( w , b ) = 0 。并且训练的方式将采用随机梯度下降法(Stochastic gradient descent)下面简称SGD。

假设x0是误分类点,那么它到分类线或平面的距离为:

1||W|||wxi+b| 1 | | W | | | w · x i + b |

误差输出点到超平面的距离的总和:

L(w,b)=1||W||i=0myi(wxi+b) L ( w , b ) = − 1 | | W | | ∑ i = 0 m y i ( w · x i + b )

其中M为误分类数据 (xiM) ( x i ∈ M ) 。根据符号函数的特性所有误分类点的 wxi+b w · x i + b 输出值和样本标签一定是相反的,所以 yi(wxi+b) − y i ( w · x i + b ) 一定是正数。根据损失函数,可求得L(w,b)的w和b的梯度,利用w和b的梯度进行下一轮迭代的权重值和偏移值的更新。
w(n+1)=w(n)+ηδw w ( n + 1 ) = w ( n ) + η ∗ δ w

b(n+1)=b(n)+ηδb b ( n + 1 ) = b ( n ) + η ∗ δ b

δw=i=0myix δ w = ∑ i = 0 m y i ∗ x

δb=i=0myi δ b = ∑ i = 0 m y i

注: η η 是学习率 η(0,1) η ∈ ( 0 , 1 ) δw δ w 是权重值的梯度, δb δ b 是偏移值的梯度。

感知器训练步骤如下:

  1. 把数据集分为训练集和测试集
  2. 初始化参数(w权重值,b偏置值)
  3. 从训练集中随机选出N个样本( N=batchSize N = b a t c h S i z e )组成新的数据集,计算新的数据集内的所有样本的分类结果
  4. 通过损失函数评价当前迭代的结果并判断训练是否完成。如果 L(w,b)>0 L ( w , b ) > 0 ,计算权重梯度和偏移值梯度并更新权值和偏移值,训练将再跳到第二步进入新一轮迭代。如果 L(w,b)<=0 L ( w , b ) <= 0 ,训练结束进入测试阶段。
  5. 通过训练好的感知器,计算出测试集数据的准确率

关键代码:

前向计算代码:

    /**
     * f(x) = ∑ wi * xi + b
     * output = sign(f(x))
     */
    this.forward = function(){

        for(var i = 0;i<this.input.length;i++){

            for(var x = 0;x<this.input[i].length;x++){

                this.originalOutput[i] += this.input[i][x] * this.weight[x];

            }

            this.originalOutput[i] += this.bias;

            this.output[i] = Perceptron.utils.sginHardlims(this.originalOutput[i]);

        }

    }

计算权重值偏移量和偏置值偏移量
注:因为 1||W|| 1 | | W | | 恒定为常数,所以损失函数可以等价为 L(w,b)=mi=0yi(wxi+b) L ( w , b ) = − ∑ i = 0 m y i ( w · x i + b )

    /**
     * deltaW = - ∑ y(f(x)) * xi
     * deltab = - ∑ y(f(x))
     */
    this.backReverse = function(){

        for(var i = 0;i<this.output.length;i++){

            if(this.output[i] != this.label[i]){

                for(var x = 0;x<this.inputNum;x++){

                    /**
                     * deltaWi = - ∑ y(f(x)) * xi
                     */
                    this.deltaW[x] -= this.originalOutput[i] * this.input[i][x];

                }

                /**
                 * deltaB = - ∑ y(f(x))
                 */
                this.deltaB -= this.originalOutput[i];

            }

        }

    }

根据权重梯度和偏移梯度更新下一次迭代的权重值和偏移值

    /**
     * update weight and bias
     * W(n+1) = W(n) + rate * deltaW
     * b(n+1) = b(n) + rate * deltab
     */
    this.update = function(){

        /**
         * update weight
         * W(n+1) = W(n) + rate * deltaW
         */
        for(var i = 0;i<this.inputNum;i++){

            this.weight[i] += this.learningRate * this.deltaW[i];

        }

        /**
         * update bias
         * b(n+1) = b(n) + rate * deltab
         */
        this.bias += this.learningRate * this.deltaB;
    }

完整代码:
最后附上完整代码与测试事例,该应用例子是实现鸢尾花数据分类,内附训练数据集和测试数据集。
Perceptron.zip

精彩预告 :
下一章,我们将跟大家一起探讨一下人工神经网络(ANN),同样从原理到公式到实现一条龙服务…

联系方式:
QQ:465973119
邮箱:465973119@qq.com
欢迎打扰

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值