65.9K
CodeProject 正在变化。 阅读更多。
Home

JavaScript 机器学习和神经网络(使用 Encog)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (7投票s)

2012年10月16日

Apache

18分钟阅读

viewsIcon

91217

downloadIcon

1414

使用 Encog 的遗传算法、模拟退火、神经网络等在 HTML5 JavaScript 中。

引言

在本文中,您将对如何在 JavaScript 中使用一些机器学习主题获得基本了解。我将使用Encog 框架。我将向您展示如何使用 Encog 对象来完成光学字符识别模拟退火遗传算法神经网络。Encog 还包含一些 GUI 小部件,可以轻松显示常见机器学习任务的输出。

背景

Encog 是一个先进的机器学习平台,支持 Java、C#、JavaScript 和 C/C++。此外,Encog 还可以为http://www.heatonresearch.com/wiki/Meta_Trader_4 生成代码。本文重点介绍 Encog 在 JavaScript 中的使用。Encog for Javascript 允许您创建利用人工智能的交互式 Web 应用程序。有关 Encog 的更多信息,请访问以下 URL

http://www.heatonresearch.com/encog

Using the Code

本文中涵盖的所有示例都可以在随附的下载中找到。

此代码也托管在以下 GitHub 存储库中。

https://github.com/encog/encog-javascript

您可以在以下 URL 实时查看本文涵盖的所有示例

http://www.heatonresearch.com/fun

Encog 框架包含在两个 JavaScript 文件中。第一个 JavaScript 文件包含所有核心机器学习函数。此 JavaScript 文件名为 encog-js。第二个文件包含所有 GUI 小部件,名为 encog-widget.js。您可以在此处看到包含这两个文件。

<script src=" encog-js-1.0.js"></script>
<script src="encog-widget-1.0.js"></script>

欧几里得距离

我们将从查看欧几里得距离开始。欧几里得距离是一个非常简单的概念,用于许多不同的机器学习技术。欧几里得距离提供一个数字,用于确定两个相同长度数组之间的相似性。考虑以下三个数组

Array 1: [ 1, 2, 3]
Array 2: [ 1, 3, 2]
Array 3: [ 3, 2, 1]

我们可以计算以上任意两个数组之间的欧几里得距离。这对于确定数组彼此之间的相似程度非常有用。考虑如果我们想找出数组 2 或数组 3 是否比数组 1 更接近。要做到这一点,我们计算数组 1 和数组 2 之间的欧几里得距离。然后我们计算数组 1 和数组 3 之间的距离。距离最短的数组最相似。

数学上,欧几里得距离通过以下方程计算。

图 1:欧几里得距离 

使用此公式,我们现在可以计算欧几里得距离。

d(a1,a2) = sqrt( (a2[0]-a1[0])^2 + (a2[1]-a1[1])^2 +  (a2[1]-a1[1])^2  )
d(a1,a2) = sqrt( (1-1)^2 + (3-2)^2 + (2-3)^2 )
d(a1,a2) = sqrt( 0+1+1 )
d(a1,a2) = sqrt(2) = 1.4

d(a1,a3) = sqrt( (a3[0]-a1[0])^2 + (a3[1]-a1[1])^2 +  (a3[1]-a1[1])^2  )
d(a1,a3) = sqrt( (3-1)^2 + (2-2)^2 + (1-3)^2 )
d(a1,a3) = sqrt( 4+0+4 )
d(a1,a3) = sqrt(8) = 2.3

由此可见,数组 2 比数组 3 更接近数组 1。

以下 JavaScript 代码实现了欧几里得距离计算。

ENCOG.MathUtil.euclideanDistance = function (a1, a2, startIndex, len) {
    'use strict';

    var result = 0, i, diff;
    for (i = startIndex; i < (startIndex + len); i += 1) {
        diff = a1[i] - a2[i];
        result += diff * diff;
    }
    return Math.sqrt(result);
};

欧几里得距离可用于创建简单的光学字符识别。您可以在此处看到此应用程序的运行情况

图 2:JavaScript OCR 

您可以在以下 URL 看到此程序正在运行

http://www.heatonresearch.com/fun/ocr

下面您看到的 HTML5(支持触摸设备)JavaScript 应用程序使用简单的欧几里得距离比较实现了基本的光学字符识别。要使用此示例,请在下面的大矩形中绘制一个数字。单击“识别”按钮,程序将尝试猜测您绘制的数字。准确性并非完美,但它确实做得相当好。

程序已针对数字进行了训练。您可以删除任何这些数字条目,或创建自己的数字。要为新字符训练 OCR,只需绘制该字符并单击“训练”按钮。这将把该字符添加到程序已知的字符列表中。

您会注意到,您绘制的任何内容都会先被裁剪,然后进行降采样。您绘制的高分辨率字符会被降采样到 5x8 的网格上。然后,此降采样网格将与每个数字的类似降采样网格进行比较。要查看程序训练的网格,请单击列表中您想查看的字符。然后,此网格被转换为一维数组。5x8 的网格将有 40 个元素。现在,字符是数组,识别新字符只是找到与您绘制的内容长度最短的已知字符的问题。

以下 JavaScript 执行此搜索,并实现欧几里得距离计算。

var c, data, sum, i, delta;

for(c in charData )
{
  data = charData[c];

// Now we will actually recognize the letter drawn.
// To do this, we will use a Euclidean distance
// http://www.heatonresearch.com/wiki/Euclidean_Distance

 sum = 0;
 for(var i = 0; i<data.length; i++ )
 {
   delta = data[i] - downSampleData[i];
   sum = sum + (delta*delta);
 }

 sum = Math.sqrt(sum);

// Basically we are calculating the Euclidean distance between
// what was just drawn, and each of the samples we taught
// the program.  The smallest Euclidean distance is the char.

if( sum<bestScore || bestChar=='??' )
{
  bestScore = sum;
  bestChar = c;
}

}

群集

这个例子展示了一个迷人的简单算法,称为群集。这里看到的粒子正在群集。最初它们位于随机位置,但是,它们很快就会形成群体并以看似复杂的模式飞行。您也可以单击(或触摸)一个位置,粒子就会从您触摸的位置排斥。

图 3:群集

您可以在以下 URL 在线运行此示例

http://www.heatonresearch.com/fun/flock 

可能需要一分钟左右的时间才能形成成熟的群集。即使这些群集形成后,它们也常常会分裂和重组。要重新开始,请单击“清除”。也可以单击“宇宙大爆炸”,它根本不使用任何随机初始化。粒子都位于宇宙的中心,并迅速向外移动,呈现出它们的“复杂”模式。

Craig Reynolds 在 1986 年使用他的模拟程序 Boids 首次在计算机上模拟了群集行为。群集看起来是一种非常复杂的行为。它被许多不同的动物表现出来,使用许多不同的名称。一群鸟。一群昆虫。一群鱼。一群牛。所有这些名称都描述了本质上相同的行为。

乍一看,群集算法可能很复杂。我可能会创建一个对象来处理群集中的个体。我需要定义一个群集对象来保存群集成员。需要开发例程来确定群集应该朝哪个方向移动。我们还必须决定一个群集如何分成两个或更多个群集。什么标准决定群集的大小?如何接纳新成员?您可以在此处看到一些真实的鸟群示例。

群集算法实际上非常简单。它只有三个规则。

  1. 分离 - 避免拥挤的邻居(短距离排斥)
  2. 对齐 - 朝向邻居的平均方向行驶
  3. 内聚 - 朝向邻居的平均位置行驶(长距离吸引)

这三个规则就足够了。群集确实是“简单复杂性”的一个例子。

我想让这个例子尽可能地简单,但仍然展现出看似复杂的行为。粒子都在以恒定的速度移动。每个粒子都有一个定义粒子移动方向的角度。粒子不能加速或减速。它们只能转弯。

上述三个规则分别指定了粒子要朝向的“理想角度”。遵守这三个规则的愿望被特定百分比所抑制。这是您在底部看到的三个数字。您可以玩这三个数字,看看它们如何影响事物。许多组合根本不会产生群集行为。我提供的默认值效果很好。

如果您想查看其中一个规则的隔离效果,请将该规则设置为 1.0,其他规则设置为 0.0。例如,如果您隔离了内聚,那么所有粒子最终都会收敛到宇宙中的几个位置。

这个宇宙中根本没有随机性。除了将粒子放置在初始随机位置外,不再生成任何随机数。您甚至可以单击“宇宙大爆炸”按钮,消除系统中的所有随机性。如果您单击“宇宙大爆炸”,所有粒子都会被放置在中心,并朝同一方向移动。很快就会出现复杂的模式。群集是如何通过非常简单的规则产生非常复杂的系统的绝佳示例。

欧几里得距离对此示例非常重要。每个粒子有两个维度,一个 x 坐标和一个 y 坐标。使用欧几里得距离,我们可以找到最近的邻居。这引入了另一个重要的机器学习算法,称为“K 最近邻”。K 是您要找到的邻居数量。

上述三个规则可以轻松地在 JavaScript 中实现。首先,我们计算理想的分离角度。

// 1. Separation - avoid crowding neighbors (short range repulsion)
separation = 0;
if (nearest.length > 0) {
  meanX = ENCOG.ArrayUtil.arrayMean(nearest, 0);
  meanY = ENCOG.ArrayUtil.arrayMean(nearest, 1);
  dx = meanX - this.agents[i][0];
  dy = meanY - this.agents[i][1];
  separation = (Math.atan2(dx, dy) * 180 / Math.PI) - this.agents[i][2];
  separation += 180;
}

首先,我们计算所有邻居的平均(均值)x 和 y 坐标。这个平均值是邻居簇的中心。然后,使用一点三角学,我们计算我们与簇中心之间的角度。然后我们加 180,因为我们想远离我们最近的邻居(所以我们不会碰到它们)。这是我们应该“瞄准”的理想分离角度。

接下来,我们计算理想的对齐角度。以下代码执行此操作。

// 2. Alignment - steer towards average heading of neighbors
alignment = 0;

if (neighbors.length > 0) {
  alignment = ENCOG.ArrayUtil.arrayMean(neighbors, 2) - this.agents[i][2];
}

对齐非常简单。它是所有邻居的平均角度。

接下来我们计算内聚。为此,我们再次查看邻居,但这次我们考虑一个更大的集合,几乎是所有粒子。这与分离的工作方式非常相似,除了我们朝着长程邻居的中心移动。

// 3. Cohesion - steer towards average position of neighbors (long range attraction)
cohesion = 0;

if (neighbors.length > 0) {
  meanX = ENCOG.ArrayUtil.arrayMean(this.agents, 0);
  meanY = ENCOG.ArrayUtil.arrayMean(this.agents, 1);
  dx = meanX - this.agents[i][0];
  dy = meanY - this.agents[i][1];
  cohesion = (Math.atan2(dx, dy) * 180 / Math.PI) - this.agents[i][2];
}

现在我们有了来自三个规则的理想角度,我们必须实际旋转粒子(或代理)。

// perform the turn
// The degree to which each of the three laws is applied is configurable.
// The three default ratios that I provide work well.
turnAmount = (cohesion * this.constCohesion) + (alignment * this.constAlignment) + (separation * this.constSeparation);

this.agents[i][2] += turnAmount;

到目前为止,我们已经研究了不引入随机性的技术。这些技术可以称为确定性的。也就是说,结果总是可以预测的。对于本文的其余部分,我们将进行 180 度转弯。剩余的技术是随机的。它们使用随机性来解决问题。

旅行商问题

旅行推销员问题 (TSP) 涉及一个“旅行推销员”,他必须访问一定数量的城市。所要寻找的是推销员将要旅行的最短路线。推销员可以从任何城市开始并在任何城市结束。唯一的要求是旅行推销员必须每到一个城市一次。推销员不得重复访问任何城市。

这对于普通的迭代程序来说似乎是一项简单的任务。考虑到随着城市数量的增加,可能组合的数量如何急剧增长。如果只有一个或两个城市,只需一步即可。三个城市会增加到六个。表 8.1 显示了这些数字的增长速度。

表 1: 使用传统程序解决 TSP 的步数

城市数量步数
11
21
36
424
5120
6720
75,040
840,320
9362,880
103,628,800
1139,916,800
12479,001,600
136,227,020,800
......
503.041 * 10^64

上表背后的公式是阶乘。城市数量 n 使用阶乘运算符(!)计算。任意 n 值的阶乘由 n * (n-1) * (n-2) * ... * 3 * 2 * 1 给出。如上表所示,当程序必须进行“蛮力”搜索时,这些值会变得非常大。我们将在下一节中检查的示例程序将在几分钟内找到一个五十城市问题的解决方案。这是通过使用模拟退火而不是普通的蛮力搜索来实现的。

模拟退火

模拟退火是一种编程方法,它试图模拟退火的物理过程。退火是指一种材料被加热然后冷却(如钢或玻璃),通常是为了软化并使材料不易碎。因此,模拟退火将一个“解决方案”暴露在“热”中然后冷却,从而产生更优的解决方案。您可以在以下 URL 运行模拟退火示例。

http://www.heatonresearch.com/fun/tsp/anneal 

模拟退火通过在每次迭代中从起始温度移动到结束温度来工作。循环计数允许您指定温度下降的粒度。温度越高,引入系统的随机性就越多。您可以配置所有这三个参数。

以下 JavaScript 实现退火。

anneal.randomize = function(path, temperature) {
  var length = path.length - 1;

  // make adjustments to city order(annealing)
  for (var i = 0; i < temperature; i++) {
    var index1 = Math.floor(length * Math.random());
    var index2 = Math.floor(length * Math.random());
    var d = universe.pathDistance(path, index1, index1 + 1)
      + universe.pathDistance(path, index2, index2 + 1)
      - universe.pathDistance(path, index1, index2)
      - universe.pathDistance(path, index1 + 1, index2 + 1);
    if (d > 0) {
      // sort index1 and index2 if needed
      if (index2 < index1) {
        var temp = index1;
        index1 = index2;
        index2 = temp;
      }
      for (; index2 > index1; index2--) {
        var temp = path[index1 + 1];
        path[index1 + 1] = path[index2];
        path[index2] = temp;
        index1++;
      }
    }
  }
}

上面的 randomize 函数是专门为 TSP 设置的。Encog 中的模拟退火算法是通用的,与 TSP 无关。您必须为要解决的问题提供一个 randomize 函数。

基本上,randomize 函数根据温度修改城市路径。上面的函数只是在温度范围内翻转路径中的城市。温度越高,翻转越多。

随机城市

此程序最常见的用法是简单地将随机城市放置在地图上。这些城市将放置在地图上的随机位置。一些随机的城市组合比其他组合更难。您可以在此处看到一个 50 个随机城市的地图。

图 4:随机城市 

一旦一组城市被“解决”,它看起来就像这样。

图 5:潜在解决方案 

当您更改参数时,您可能想评估模拟退火的有效性。要重新运行,您应该只是随机化路径。这将允许您使用相同的城市配置重新开始。

圆圈中的城市

您还可以将城市放在一个圆圈中。这使得可视化模拟退火与最优解的接近程度更加容易。围绕圆圈的最优路径是沿着周长。在这里,您可以看到模拟退火找到了一个接近最优的路径。

图 6:圆圈中的城市 

遗传算法

也可以使用遗传算法 (GA) 来获得 TSP 的潜在解决方案。GA 使用简单的进化操作来创建不断改进的解决方案种群。整个过程是生物进化的简化版本。进化通过交叉和突变来实现。当两个解决方案“交配”并产生后代时,就会发生交叉。当单个解决方案被轻微改变时,就会发生突变。

与模拟退火一样,GA 也是随机的。随机性在交叉过程中用于确定母亲和父亲将传递哪些特征。突变也是通过随机性引入的,这本身就是一个本质上随机的过程。

您可以在此处在线看到遗传 TSP 应用程序

http://www.heatonresearch.com/fun/tsp/genetic

要使用 Encog 创建遗传算法,您必须定义突变和交叉操作。实现这两个操作的方法取决于您要寻找的解决方案类型。

以下代码为 TSP 分配了突变操作。

genetic.mutate = function performMutation(data)
{
  var iswap1 = Math.floor(Math.random() * data.length);
  var iswap2 = Math.floor(Math.random() * data.length);

  // can't be equal
  if (iswap1 == iswap2)
  {
    // move to the next, but
    // don't go out of bounds
    if (iswap1 > 0)
    {
      iswap1--;
    } else {
      iswap1++;
    }
  }
  var t = data[iswap1];
  data[iswap1] = data[iswap2];
  data[iswap2] = t;
}

这实际上与为模拟退火定义的 randomize 操作非常相似。本质上,上面的代码会交换列表中的两个城市。我们确保两个随机城市不相等,因为这样就不会发生交换。

交叉操作有点复杂。以下代码实现了交叉函数。

genetic.crossover = function performCrossover(motherArray, fatherArray, child1Array, child2Array)
{
  // the chromosome must be cut at two positions, determine them
  var cutLength = motherArray.length / 5;
  var cutpoint1 = Math.floor(Math.random() * (motherArray.length - cutLength));
  var cutpoint2 = cutpoint1 + cutLength;

  // keep track of which genes have been taken in each of the two
  // offspring, defaults to false.
  var taken1 = {};
  var taken2 = {};

  // handle cut section
  for (var i = 0; i < motherArray.length; i++)
  {
    if (!((i < cutpoint1) || (i > cutpoint2)))
    {
      child1Array[i] = fatherArray[i];
      child2Array[i] = motherArray[i];
      taken1[fatherArray[i]] = true;
      taken2[motherArray[i]] = true;
    }
  }

  // handle outer sections
  for (var i = 0; i < motherArray.length; i++)
  {
    if ((i < cutpoint1) || (i > cutpoint2))
    {
      child1Array[i] = getNotTaken(motherArray,taken1);
      child2Array[i] = getNotTaken(fatherArray,taken2);
    }
  }
};

上面的代码通过在城市的路径中取两个“切割点”来工作。这会将母亲和父亲都分成三段。母亲和父亲都有相同的切割点。这些切割的大小是随机的。然后通过交换这三段来创建两个后代。例如,考虑以下母亲和父亲。

[m1, m2, m3 ,m4, m5, m6, m7, m8, m9, m10]
[f1, f2, f3 ,f4, f5, f6, f7, f8, f9, f10]

现在我们施加切割点。

[m1, m2] [m3 ,m4, m5, m6] [m7, m8, m9, m10]
[f1, f2] [f3 ,f4, f5, f6] [f7, f8, f9, f10]

这将产生以下两个孩子。

[m1, m2] [f3 ,f4, f5, f6] [m7, m8, m9, m10]
[f1, f2] [m3 ,m4, m5, m6] [f7, f8, f9, f10]

取决于另一个随机事件,每个子解决方案可能都会被突变。突变是“新信息”被添加到种群基因中的过程。否则,我们只是在传递已经存在的特征。

XOR 神经网络

神经网络是另一种基于生物学的机器学习方法。神经网络非常松散地基于人脑。神经网络由通过突触连接的神经元构成。每个突触都有一个权重。这些权重构成了神经网络的记忆。您可以在此处看到一个神经网络。

图 7:一个神经网络 

这实际上是我们将在下一节中创建的神经网络。从上面的神经网络可以看出,它有一个输入层和一个输出层。神经网络从输入层接收刺激,并通过输出层响应。也可能有隐藏层,其中也包含神经元。隐藏层有助于处理。

XOR 神经网络是一个具有 2 个输入和 1 个输出的神经网络。两个输入接收布尔数(0 或 1)。输出神经元也输出布尔数。其思想是使神经网络执行与 XOR 运算符相同的操作。

0 XOR 0 = 0
1 XOR 0 = 1
0 XOR 1 = 1
1 XOR 1 = 0

只有当两个输入不同时,XOR 运算符的输出才为 1。

您可以在此处看到 XOR 示例的输出。

Training XOR with Resilient Propagation (RPROP)
Training Iteration #1, Error: 0.266564333804989
Training Iteration #2, Error: 0.2525674154011323
Training Iteration #3, Error: 0.2510141208338126
Training Iteration #4, Error: 0.2501895607116004
Training Iteration #5, Error: 0.24604660296617512
Training Iteration #6, Error: 0.24363697465430123
Training Iteration #7, Error: 0.24007542622000883
Training Iteration #8, Error: 0.23594361591893737
Training Iteration #9, Error: 0.23110199069041137
Training Iteration #10, Error: 0.22402031408256806
...
Training Iteration #41, Error: 0.0169149539750981
Training Iteration #42, Error: 0.012983289628979862
Training Iteration #43, Error: 0.010217909135985562
Training Iteration #44, Error: 0.007442433731742264
Testing neural network
Input: 0 ; 0   Output: 0.000005296759326400659   Ideal: 0
Input: 1 ; 0   Output: 0.9176637562838892   Ideal: 1
Input: 0 ; 1   Output: 0.9249242746585553   Ideal: 1
Input: 1 ; 1   Output: 0.036556423402042126   Ideal: 0

从上面可以看出,神经网络需要 44 次训练迭代才能学会执行 XOR。神经网络的权重从随机数开始。训练过程逐渐调整权重以产生所需的输出。神经网络的随机部分是权重初始化。除此之外,神经网络是确定性的。给定相同的权重和输入,神经网络将始终产生相同的输出。

您可能会注意到上面的输出并非精确。神经网络永远不会精确训练到 1.0 来输出 1。由于随机起始权重,您也永远不会两次获得相同的网络结果。另一些随机权重是完全无法训练的。因此,您有时会看到 XOR 网络达到最大迭代次数 5,000 次然后放弃。

您可以在以下 URL 看到此示例。

http://www.heatonresearch.com/fun/ann/xor

我们现在来看看这个程序是如何构建的。首先,我们创建输入和期望数组。

var XOR_INPUT = [
  [0,0],
  [1,0],
  [0,1],
  [1,1]
];

var XOR_IDEAL = [
  [0],
  [1],
  [1],
  [0]
];

上述两个数组包含输入和期望输出。“真值表”将用于训练神经网络。

接下来,我们创建一个三层神经网络。输入层有 2 个神经元,隐藏层有 3 个神经元,输出层有 1 个神经元。

var network = ENCOG.BasicNetwork.create( [
  ENCOG.BasicLayer.create(ENCOG.ActivationSigmoid.create(),2,1),
  ENCOG.BasicLayer.create(ENCOG.ActivationSigmoid.create(),3,1),
  ENCOG.BasicLayer.create(ENCOG.ActivationSigmoid.create(),1,0)] );
network.randomize();

网络被创建并随机化。调用 randomize 用随机值填充权重。

有几种不同的方法可以训练神经网络。在此示例中,我们将使用 RPROP。

var train = ENCOG.PropagationTrainer.create(network,XOR_INPUT,XOR_IDEAL,"RPROP",0,0);

现在我们将通过训练迭代,直到误差降低到可接受的水平以下。

var iteration = 1;
do
{
  train.iteration();
  var str = "Training Iteration #" + iteration + ", Error: " + train.error;
  con.writeLine(str);
  iteration++;
} while( iteration<1000 && train.error>0.01);

现在神经网络已经训练好了。我们将遍历输入数组并将每个输入提交给神经网络。神经网络的输出将被显示。

var input = [0,0];
var output = [];

con.writeLine("Testing neural network");
for(var i=0;i<XOR_INPUT.length;i++)
{
  network.compute(XOR_INPUT[i],output);
  var str = "Input: " + String(XOR_INPUT[i][0])
    + " ; " + String(XOR_INPUT[i][1])
    + "   Output: " + String(output[0])
    + "   Ideal: " + String(XOR_IDEAL[i][0]);
  con.writeLine(str);
}

这是对神经网络的一个非常简单的介绍。我还写了 Java 和 C# 的文章,重点关注神经网络。您可以在此处找到它们。

  • Java 神经网络入门
  • C# 神经网络入门

此外,如果您想对神经网络有一个基本的了解,以下文章可能很有用。

http://www.heatonresearch.com/content/non-mathematical-introduction-using-neural-networks

分类神经网络

现在我们将看一个稍微复杂一点的神经网络,用于分类。这个神经网络将学会分类。您将看到神经网络如何学会对训练集中的数据点以及训练数据中未提供但附近的数据点进行分类。

您可以在以下 URL 运行此示例: 

http://www.heatonresearch.com/ann/classification 

此示例使用前馈神经网络来演示分类。要使用此应用程序,请在绘图区域绘制几个彩色点。确保您至少有两个颜色,否则将没有任何内容可供分类。绘制完内容后,单击“开始”,神经网络将开始训练。您将看到附近区域根据您提供的数据点进行分类。

上述神经网络有两个输入神经元和三个输出神经元。隐藏层结构由下拉列表定义。例如,如果您选择 2:10:10:3,您将拥有一个如下图像所示的网络。该网络有两个隐藏层,每个隐藏层有 10 个神经元。

输入神经元代表点的 x 和 y 坐标。要绘制上述图像,程序会循环遍历 x 和 y 坐标的网格。神经网络会为每个网格组件查询。左上角的单元格是 [0,0],右下角的单元格是 [1,1]。对于具有 sigmoid 激活函数的神经网络,数据通常应接收 0 到 1 之间的输入,因此此范围效果很好。中心将是 [0.5,0.5]。

神经网络的输出代表该网格正方形应具有的 RGB 颜色。值 [0,0,0] 表示黑色,值 [1,1,1] 表示白色。

在绘图区域绘制时,您将提供训练数据。输入神经元将代表您放置数据的 x 和 y 坐标。期望的或理想的输出将代表您为该位置选择的颜色。

让我们看一个简单的例子。如果您只绘制两个数据点,那么该区域将被分成两半。在这里,您可以看到一个红色和一个蓝色数据点。

图 8:两点分类 

为了使应用程序的错误级别降低,它只需要确保蓝色数据点位于蓝色区域,红色数据点位于红色区域。所有其他点都是根据其他点“猜测”的。由于数据量很少,神经网络很难真正猜测两个区域之间的边界。

如果您提供更多训练数据,您将获得更复杂的形状。如果您选择创建随机彩色图像,您将获得类似以下的数据点。

图 9:多点分类 

在这里,神经网络创建了一个更复杂的模式来尝试适应所有数据点。

您也可以选择创建复杂的彩色图案。这里为点生成了随机颜色。神经网络甚至会混合颜色以尝试折衷并尽可能降低错误。

图 10:多色分类 

甚至可以学习复杂的交错形状,例如以下形状。

图 11:螺旋线分类 

延伸阅读

本文介绍了 JavaScript 中的机器学习。如果您想了解更多关于机器学习的信息,您可能会对以下链接感兴趣。

历史

  • 2012年10月16日:第一个版本,使用 Encog JS v1.0
© . All rights reserved.