教程 | 如何用30行JavaScript代码编写神经网络异或运算器

摘要: 本文介绍了一种仅用 30 行 JavaScript 代码就创建出了一个神经网络的教程,而且使用的工具只有 Node.js、Synaptic.js 和浏览器。

11-09 04:12 首页 机器之心

选自Medium

机器之心编译

参与:Panda


配置环境、安装合适的库、下载数据集……有时候学习深度学习的前期工作很让人沮丧,如果只是为了试试现在人人都谈的深度学习,做这些麻烦事似乎很不值当。但好在我们也有一些更简单的方法可以体验深度学习。近日,编程学习平台 Scrimba 联合创始人 Per Harald Borgen 在 Medium 上发文介绍了一种仅用 30 行 JavaScript 代码就创建出了一个神经网络的教程,而且使用的工具也只有 Node.js、Synaptic.js 和浏览器而已。另外,作者还做了一个交互式 Scrimba 教程,也许能帮你理解其中的复杂概念。


Synaptic.js:https://synaptic.juancazala.com

Node.js:https://nodejs.org

Scrimba 教程:https://scrimba.com/casts/cast-1980


Synaptic.js 让你可以使用 Node.js 和浏览器做深度学习。在这篇文章中,我将介绍如何使用 Synaptic.js 创建和训练神经网络。


  1. // 创建网络const { Layer, Network } = window.synaptic;var inputLayer = new Layer(2);var hiddenLayer = new Layer(3);var outputLayer = new Layer(1);

  2. inputLayer.project(hiddenLayer);

  3. hiddenLayer.project(outputLayer);var myNetwork = new Network({

  4.    input: inputLayer,

  5.    hidden: [hiddenLayer],

  6.    output: outputLayer

  7. });// 训练网络——学习异或运算var learningRate = .3;for (var i = 0; i < 20000; i++)

  8. {    // 0,0 => 0

  9.    myNetwork.activate([0,0]);

  10.    myNetwork.propagate(learningRate, [0]);    // 0,1 => 1

  11.    myNetwork.activate([0,1]);

  12.    myNetwork.propagate(learningRate, [1]);    // 1,0 => 1

  13.    myNetwork.activate([1,0]);

  14.    myNetwork.propagate(learningRate, [1]);    // 1,1 => 0

  15.    myNetwork.activate([1,1]);

  16.    myNetwork.propagate(learningRate, [0]);

  17. }// 测试网络console.log(myNetwork.activate([0,0])); // [0.015020775950893527]console.log(myNetwork.activate([0,1])); // [0.9815816381088985]console.log(myNetwork.activate([1,0])); // [0.9871822457132193]console.log(myNetwork.activate([1,1])); // [0.012950087641929467]

我们将创建一个最简单的神经网络:一个可以执行异或运算的网络。上面就是这个网络的全部代码,但在我们深入解读这些代码之前,首先我们先了解一下神经网络的基础知识。


神经元和突触


神经网络的基本构造模块是神经元。神经元就像是一个函数,有几个输入,然后可以得到一个输出。神经元的种类有很多。我们的网络将使用 sigmoid 神经元,它可以输入任何数字并将其压缩到 0 到 1 之间。下图就是一个 sigmoid 神经元。它的输入是 5,输出是 1。箭头被称为突触,可以将该神经元与网络中的其它层连接到一起。




所以,红色的数字 5 是哪里来的?它是左边的三个突触的和,让我们来剖析一下。


在最左边我们可以看到两个值和一个所谓偏置(bias)值。这两个值是 1 和 0,用绿色表示。偏置值是 -2,用棕色表示。


首先,这两个输入与它们的权重(weight)相乘,即蓝色的数字 7 和 3。最后我们将这两个值与偏置加到一起就得到了红色的 5。这就是这个人工神经元的输入。




因为这是一个 sigmoid 神经元,会将任何值压缩到 0 到 1 之间,那么这个输出可以被压缩成 1。


如果你将这些神经元连接成一个网络,你就得到了一个神经网络。通过突触彼此相连的神经元可以向前传播输入,从而得到输出,如下图所示:



训练神经网络的目的是让它能够进行泛化,比如识别手写的数字或垃圾邮件。实现很好的泛化涉及为整个网络找到合适的权重和偏置值,就像我们上面案例中的蓝色和棕色数字。


当训练一个神经网络时,你只需要向其展示大量样本(比如手写数字),然后让其预测正确的答案即可。


在每次预测之后,你要计算这个预测的错误程度,并调整其权重和偏置值让该网络在下一轮预测时能更正确一点。这个学习过程被称为反向传播(backpropagation)。如此反复几千次,你的网络很快就擅长泛化了。


本教程不会解释反向传播的具体技术细节,但如果你有兴趣了解,可以参阅下面的文章:


  • 反向传播的一步步示例:http://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/

  • 神经网络黑客指南:http://karpathy.github.io/neuralnets/

  • 神经网络和深度学习:http://neuralnetworksanddeeplearning.com/chap1.html



代码


现在你已经了解了基本的知识,就开始写代码吧!首先我们需要创建层。我们可以使用 synaptic 中的 new Layer() 函数。传递给该函数的数字表示每层应该有多少个神经元。


如果你不知道层是什么,可以看看上面提到的交互式教程。


  1. const { Layer, Network } = window.synaptic;var inputLayer = new Layer(2);var hiddenLayer = new Layer(3);var outputLayer = new Layer(1);


接下来,我们将这些层连接到一起,并实例化一个新网络,如下:


  1. );

  2. hiddenLayer.project(outputLayer);var myNetwork = new Network({

  3. input: inputLayer,

  4. hidden: [hiddenLayer],

  5. output: outputLayer

  6. });


所以,这就是一个「2 层-3 层-1 层」的网络,可以可视化为下图的形式:


现在训练这个网络:


  1. // train the network - learn XORvar learningRate = .3;for (var i = 0; i < 20000; i++) {  // 0,0 => 0

  2.  myNetwork.activate([0,0]);

  3.  myNetwork.propagate(learningRate, [0]);  // 0,1 => 1

  4.  myNetwork.activate([0,1]);

  5.  myNetwork.propagate(learningRate, [1]);  // 1,0 => 1

  6.  myNetwork.activate([1,0]);

  7.  myNetwork.propagate(learningRate, [1]);  // 1,1 => 0

  8.  myNetwork.activate([1,1]);

  9.  myNetwork.propagate(learningRate, [0]);

  10. }


这里我们运行该网络 20000 次。每一次我们都前向和反向传播 4 次,为该网络输入 4 组可能的输入:[0,0] [0,1] [1,0] [1,1]。


首先我们执行 myNetwork.activate([0,0]),其中 [0,0] 是我们发送给该网络的数据点。这是前向传播,也称为激活这个网络。在每次前向传播之后,我们需要执行反向传播,这时候网络会更新自己的权重和偏置。


反向传播是通过这行代码完成的:myNetwork.propagate(learningRate, [0]),其中 learningRate 是一个常数,给出了网络每次应该调整的权重的量。第二个参数 0 是给定输入 [0,0] 对应的正确输出。


然后,该网络将自己的预测与正确的标签进行比较,从而了解自己的正确程度有多少。


然后网络使用这个比较为基础来校正自己的权重和偏置值,这样让自己的下一次猜测更加正确一点。


这个过程如此反复 20000 次之后,我们可以使用所有四种可能的输入来检查网络的学习情况:


  1. -> [0.015020775950893527]console.log(myNetwork.activate([0,1]));

  2. ->[0.9815816381088985]console.log(myNetwork.activate([1,0]));

  3. -> [0.9871822457132193]console.log(myNetwork.activate([1,1]));

  4. -> [0.012950087641929467]


如果我们将这些值四舍五入到最近的整数,我们就得到了正确的异或运算结果。


这样就完成了。尽管这仅仅只碰到了神经网络的表皮,但也足以帮助你进一步探索 Synaptic 和继续学习了。https://github.com/cazala/synaptic/wiki 这里还包含了更多好教程。


原文链接:https://medium.freecodecamp.org/how-to-create-a-neural-network-in-javascript-in-only-30-lines-of-code-343dafc50d49



本文为机器之心编译,转载请联系本公众号获得授权

?------------------------------------------------

加入机器之心(全职记者/实习生):hr@jiqizhixin.com

投稿或寻求报道:content@jiqizhixin.com

广告&商务合作:bd@jiqizhixin.com


首页 - 机器之心 的更多文章: