时间: 2020-09-03 00:08:26 人气: 2285 评论: 0
在本文中,你会对如何使用JavaScript实现机器学习这个话题有一些基本的了解。我会使用Encon(一个先进的神经网络和机器学习框架)这个框架,并向你们展示如何用这个框架来实现光学字符辨识,模拟退火法,遗传算法和神经网络。Encog同时包括了几个GUI窗体小部件,这些小部件可以更方便地显示出一般机器学习任务的输出。
Encog是一个面向Java,C#,JavaScript和C/C++的高级机器学习平台。除此之外,Encog还可以为http://www.heatonresearch.com/wiki/Meta_Trader_4的实际案例生成对应代码,本文将重点介绍如何使用支持JavaScript的Encog框架,该框架允许你创建利用人工智能实现的交互式web应用程序。访问以下网址获取有关Encog的更多信息。
http://www.heatonresearch.com/encog
本文介绍的所有示例代码都可以在附带的下载中找到。
这段代码同时被托管在下面的GitHub库中。
https://github.com/encog/encog-javascript
你可以看到本文讨论的所有示例都运行在下面的URL中:
http://www.heatonresearch.com/fun
Encog框架包含在两个JavaScript文件中。第一个JavaScript文件包含了所有核心的机器学习函数。这个JavaScript文件被命名为encog-js-x.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比数组1更为接近数组3。
下面的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光学字符辨识
你可以在下面这个URL中查看程序的运行实例:
http://www.heatonresearch.com/fun/ocr
HTML5(启用触控设备)的JavaScript应用程序可以通过简单的欧氏距离来实现基本的光学字符辨识。为了使用这个示例,需要在下面这个大的矩形中绘制一个数字,然后点击“Recognize”(识别)按钮,程序会尝试猜测你画的这个数字。虽然准确性并不是特别高,但它做的确实已经很不错了。
该程序已经通过了数据训练,你可以移除这些数字条目中的任何一个,或者创建你自己的条目。如果需要训练一个新字符的OCR,只要简单绘出那个字符,然后点击“Teach”(教学)按钮。则该字符就会被增加到已知的字符列表中。
你会发现,你所绘制的任何东西都是先剪裁然后向下采样的。程序会对你所绘制的高分辨率字符向下采样并将采样点分配到5×8网格中。然后将这个通过向下采样得到的网格与每个数字的向下采样网格进行比较。如果要查看程序中经训练后得到的网格,需要在字符列表中单击你希望看到的字符。然后程序会将这个网格转换成一个一维数组,而一个5×8的网格会有40个数组元素。
以下JavaScript代码执行了这个搜索,并且实现了一个欧氏距离的计算
var c, data, sum, i, delta; for(c in charData ) { data = charData[c]; // 现在我们将会识别出这个画出来的字母. // 为此,我们需要利用欧氏距离来计算 // http://www.heatonresearch.com/wiki/Euclidean_Distance (这是欧氏距离运行实例的URL) sum = 0; for(var i = 0; i<data.length; i++ ) { delta = data[i] - downSampleData[i]; sum = sum + (delta*delta); } sum = Math.sqrt(sum); // 最基本的,我们需要计算的是欧氏距离 // 我们画上去的字母,我们学习的每一个样本 // 程序将会返回欧氏距离最小的那个字符 if( sum<bestScore || bestChar=='??' ) { bestScore = sum; bestChar = c; } }
这个例子展示一个名为flocking(蜂拥)的迷人的简单算法。此处的粒子是成群存在的。起初的时候,它们各自随机出现在某个位置,然而,这些粒子会很快地填充成各种形式的组,并以看似复杂的模式路线飞行。或者你也可以手动点击(或者触摸)一个位置,这些粒子会排斥并远离你的接触点。
图3:flocking(蜂拥算法)
你可以在线运行以下URL的实例程序:
http://www.heatonresearch.com/aifh/vol2/flock_2d.html
这个实例可能需要一分钟(大约),才能让成熟的蜂拥集群出现。即使这些集群出现了,它们也经常会再次分裂和重组。重启时点击“Clear”(清除),或者也可以点击“Big Bang”(大爆炸模式),该模式不会使用任何随机的初始化,而是将粒子统一放置在面板中央,并且以粒子设定的“复杂模式”迅速向外移动。
克雷格·雷诺兹在1986年首次用他的模拟程序Boids在计算机上成功模拟出了蜂拥算法。蜂拥集群是一种非常复杂的行为。他在不同种类的动物中有各自表现形式,各自使用了很多不同的名字。比如一群小鸟,一群昆虫,一个学校的鱼群,一群牛等等。其实就是用不同的名字来描述本质相同的行为。
初看上去,蜂拥算法可能看似复杂。因为我们需要创建一个对象来处理集群中的个体、需要定义一个蜂拥对象来容纳集群成员、需要为确定蜂拥集群应该向哪个方向移动而制定常规行为。我们还必须确定如何将蜂拥集群分成两群或者更多的群。那么问题是什么样的标准可以决定某个群体可以得到成员数量?新成员如何被确认是属于哪一个集群?你可以在下面内容中看到一些真实的蜂拥集群例子。
蜂拥算法其实很简单,它只有三条规则:
这三个基本规则是必需的。蜂拥算法其实就是“简单的复杂”的典型例子。
我希望这个例子能够尽可能的简单,但是仍然表现出看似复杂的行为方式。其实这些粒子都是以恒定的速度运行的,每个粒子都有一个角度属性来定义粒子运动的方向。所以这些粒子不可以加速或者减速,唯一可以做到是转向。
上述的三种规则其实是分别为粒子的运动设定好了一个“理想的角度”,遵守这三种规则的期望被特定的百分比属性所抑制。这些抑制因子是你在底部看到的三个数字。你可以尝试填入一些数字,看看它们是如何影响集群粒子的运动轨迹的。其实有很多的数字组合不会产生集群的行为,而我在实例中填入的默认值是比较合适的。
如果你想单独查看这三种规则中单独一条生效时的结果,那么可以将该规则设置为1.0,其它的规则设置为0.0。例如当内聚的规则单独生效时,你会所有的粒子会聚集在面板区域中少数的几个位置。
在这个区域中不存在任何随机性。除了粒子最初出现的位置是随机的之外,不会产生更多的随机数。你甚至可以点击“Big Bang”(大爆炸模式)按钮,来消除系统中所有的随机性。如果你点击了“Big Bang”按钮,则所有的粒子都会被放置到区域的中心位置,并以同样的方向运动。如此一来,要形成一幅复杂的运动模式并不会花费很长时间。所以对于用非常简单的规则来实现非常复杂的系统来说,蜂拥算法是一个非常典型的例子。
理解欧氏距离对于例子很重要。因为每个粒子都有两个维度,分别是x坐标和y坐标。利用欧氏距离的计算方法,我们就可以很快找到最近的邻居。由此即引入了另一种重要的机器学习算法,即“K-邻近算法”。这个K就是你希望找到的邻居的数量。
这三种规则可以很容易的用JavaScript实现。首先,我们计算出理想的分离角度。
// 1. 隔离-避免拥挤的邻居 (短距离的排斥力) separation = 0; if (nearest.length > 0) { meanX = ENCOG.ArrayUtil.arrayMean(nearest, 0); meanY = ENCOG.ArrayUtil.arrayMean(nearest, 1); dx = meanX - this.agentsi; dy = meanY - this.agentsi; separation = (Math.atan2(dx, dy) \* 180 / Math.PI) - this.agentsi; separation += 180; }
首先,我们需要计算出所有邻居粒子的x坐标的平均值和y坐标的平均值,这个平均坐标点就是邻近集群的中心点。然后,借用一些三角函数中的知识,计算出我们和邻近集群中心点之间的夹角值。对这个夹角值加上180,因为我们是希望远离这个邻近的邻居的(进而我们就不会撞到它们)。这个才是我们应该努力争取的理想分离角度。
紧接着,我们会计算出理想的对齐角度。如下代码所示。
// 2. 对齐-转向邻居的平均方向 alignment = 0; if (neighbors.length > 0) { alignment = ENCOG.ArrayUtil.arrayMean(neighbors, 2) - this.agents[i][2]; }
对齐非常简单,其实就是所有邻居的平均角度。
接下来我们计算内聚力。为此我们再来看看邻居,不过这回考虑的是一个更大的集合,几乎包括了所有的粒子。
// 3. 内聚-转向邻居的平均位置(长距离的吸引力) 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]; }
现在我们从这个规则中得到了理想的角度,那么必须要开始转动粒子(或者说是代理)了。
// 执行转向操作 // 这三种规则的参数应用值是可以配置的 // 我提供的这三个默认值比例的运行表现很好 turnAmount = (cohesion * this.constCohesion) + (alignment * this.constAlignment) + (separation * this.constSeparation); this.agents[i][2] += turnAmount;
到目前为止,我们研究的技术并不是随机性的,而是可以被认定为决定性的。也就是说得到的结果总是可以预测的。对于本文的内容的排版,我们会做出180度的调整,剩下的技术都是研究随机性的。也就是用随机性来解决问题。
旅行推销员问题(TSP)意为存在一名“推销员”,他必须经过一定数量的城市,而这条最短的旅行路线就是我们寻找的目标。其中允许推销员从任意一个城市开始或者结束。唯一的要求是“推销员”必须经过每一个城市并且只能经过一次。
如果用一般的迭代程序实现,这似乎是一个简单的任务。思考一下随着城市数量的增加,可能的排列组合数量会增加多少。如果只有一两个城市,那只需要一步迭代就够了。如果是三个城市呢,迭代步骤就变成了6步。表格8-1列举出了迭代步骤的增长速度。
表1:用常规程序解决TSP问题的步骤数目
城市数目 | 步骤数目 |
|---|---|
1 | 1 |
2 | 1 |
3 | 6 |
4 | 24 |
5 | 120 |
6 | 720 |
7 | 5040 |
8 | 40,320 |
9 | 362,880 |
10 | 3,628,800 |
11 | 39,916,800 |
12 | 479,001,600 |
|
评论
热门投稿
|