面向中学生的机器学习

Wolfram语言的基本介绍可在打印,作为电子书,网上免费-以及在Wolfram编程实验室乌云.这里还有a免费在线实践课程基于它。)

Wolfram语言的基本介绍

一年前我出版了一本名为Wolfram语言的基本介绍作为我努力的一部分计算思维教育给下一代。我刚刚出版了第二版(除其他外)本书中关于现代机器学习

我原本以为这本书的读者是高中生以上。但实际上,它也在中学生(11至14岁)中找到了大量受众。因此,现在的问题是:我们能否将现代机器学习的核心概念传授给中学生?有趣的是,多亏了这个技术堆栈我们现在已经Wolfram语言-答案似乎是“是的”!

我是这么做的在书中:


在这篇课文之后,书就有了练习常见问题科技的笔记

练习,问答,技术笔记

背后的故事

关于机器学习这部分,我是怎么想的?首先,它必须适应书中的内容,只使用已经介绍过的概念,并在可能的情况下加强这些概念。所以它可以谈论图片,真实的数据,,文本但不函数式编程外部数据资源

章列表

有了现代机器学习,很容易就能展示出令人惊叹的例子——比如我们的imageidentify.com(基于Wolfram语言ImageIdentify功能)。但是我写这本书的目的也是为了传达一些关于机器学习如何工作的背景和直觉,以及它可以在哪里使用。

我首先解释机器学习不同于传统的“编程”,因为它基于从示例中学习,而不是明确指定计算步骤。我讨论的第一件事其实并不需要现代神经网络机器学习的所有幻想:它是识别文本片段来自什么语言:

LanguageIdentify[{“谢谢”,“谢谢”,“dar las谢谢”,“感謝”、“благодарить”}]

孩子们(和其他人)可以想象(或在教室里讨论)这样的东西是如何工作的在字典里查单词等。我认为给出一个不像“纯魔法”的例子是有用的。(在现实中,LanguageIdentify结合了传统查找和现代机器学习技术。)

然后我再举一个更“神奇”的例子ImageIdentify

ImageIdentify []

我不会马上解释它是如何工作的,而是继续做一些不同的事情:情感分析。孩子们在情感分析上很有乐趣。但这里真正的重点是它展示了一个想法分类器“:有无限多个可能的输入,但(在本例中)只有3个可能的输出:

分类[“情感”,“我很兴奋能编程”]

看到这一点后,我们准备给出更多关于这类东西如何工作的指示。我要做的就是展示这个函数分类将手写数字分为0和1。我不是说里面发生了什么,但人们会觉得分类给出了一堆例子,然后它用这些例子把一个特定的输入分类为0或1:

分类[]

好的,但是它是怎么做到的呢?在现实生活中关于流动以及位于特定产出吸引力区域的投入。但是在第一次近似中,我们可以说,输入“更接近”,例如,0个例子被认为是0,而接近1个例子的输入被认为是1。

人们通常对这种解释没有什么困难,除非他们开始认真思考“最近的”在这个上下文中的真正含义。但我在书中并没有把重点放在这一点上,我只是讨论了一种数字的情况,在这种情况下,“最近的”的意思很容易理解:

最近邻[{10,20,30,40,50,60,70,80},22]

最近的这不是最令人兴奋的功能:一个潜在地放入许多东西,然后只出一个“最近的东西”。尽管如此,最近的Is很好,因为它的功能非常容易理解(人们可以合理地猜测它可能使用的算法)。

看到最近的对于数字,我展示最近的的颜色。在书中,我已经讲过了颜色由红、绿、蓝三组数字表示所以这并不是一种延伸,而是一种观察最近的对颜色的操作开始让它更有可能对像图像这样的东西进行操作。

最近的[]

接下来我展示单词的例子。在书中,我已经做了很多字符串和单词.在正文中,我没有讨论单词“接近”的精确定义,但同样,孩子们很容易得到基本概念。(在一篇技术笔记中,我确实谈到了编辑距离,这是人们可以思考和尝试的另一种很好的算法操作。)

最近的[WordList[],“好”,10]

那么一个人是如何从这里变成ImageIdentify?我接下来要讲的是OCR和TextRecognize.这看起来并不像ImageIdentify(很多人都知道“OCR文档”),但这是进一步了解什么内容的好地方ImageIdentify在干什么。

将一段文字转换成图像,然后再转换成同样的文本,似乎并没有那么令人印象深刻或有用。但如果把文字弄模糊了就更有趣了(是的,模糊图像是我说的吗在书的前面):

表(模糊(栅格化[风格(“hello”,20)],r), {r 0 4}]

考虑到模糊的图像,问题是:人们还能认出文字吗?在这本书的这个阶段,我还没有讲到/ @地图)或(最后一次输出),所以我不得不把代码写得更详细一些。但结果是:

TextRecognize / @ %

是的,当图像不是太模糊的时候,TextRecognize可以识别文字,但当文字变得太模糊时,它就不能识别了。我喜欢这个例子,因为它展示了一些令人印象深刻的事情——但不是“奇迹”——正在发生。我认为展示基于机器学习的功能在哪里成功,在哪里失败是很有用的。顺便说一下,这里的结果与书中不同,因为文本字体不同,当一个人处于可以识别的边缘时,这些细节很重要。(例如,如果你在课堂上这么做,你可能会尝试一些不同的字体和大小,并讨论为什么有些字体比其他字体更容易模糊。)

TextRecognize展示了一个人如何有效地做一些事情ImageIdentify,但只有26个字母(实际上,TextRecognize处理更多的字形)。但现在在我的书里ImageIdentify再一次,像字母一样模糊:

表[模糊[],r], {r, 0,22,2}]

图像识别/@%

看到它做什么很有趣,但也很有帮助。因为它让人感觉到了“猎豹”概念的“吸引者”:保持相当近的距离,猎豹仍然可以被识别;走得太远,它就不能被识别。(一个稍微棘手的问题是,我们正在不断地为它制造新的、更好的神经网络。)ImageIdentify所以,即使在这本书完成到今天这段时间里,也有一些新的网,而且它们对非猎豹的情况给出了不同的结果。假设新的结果是“更好”的,尽管还不清楚这意味着什么,因为我们没有一个官方的正确答案“模糊的猎豹”类别,谁又能说最模糊的图像更像一个whortleberry还是一个人。)

我不会把书中关于机器学习的全部内容都讲完。我只想说,在讨论明确训练过的函数之后TextRecognizeImageIdentify,我开始讨论“无监督学习”,以及特性空间中的聚类。我想我们的新FeatureSpacePlot尤其有帮助。

排列颜色的含义很清楚:

FeatureSpacePlot [RandomColor [100]]

但人们可以对字母图像“做同样的事情”。(在书中,代码稍微长一点,因为我没有谈到/ @然而,.)

FeatureSpacePlot[栅格化/ @字母[]]

这个方法的优点在于,它本身就很有用,同时也强化了这样一种观点TextRecognize可以通过找到“最近的字母”的输入来工作。

本节的最后一个例子使用了照片。FeatureSpacePlot很好地分离了不同种类的图像,并给出了如何分离的想法ImageIdentify可能有效:

FeatureSpacePlot ({})

很明显,在一本基础书的10页中,我无法完整地阐述现代机器学习,但我很高兴看到我能接触到多少核心概念。

当然,这是否可能完全取决于我们的整体Wolfram语言技术栈.不管这是我们拥有的事实机器学习在语言上,或者我们可以无缝地处理图像、文本或任何东西,或者整个(28岁!)Wolfram的笔记本这个系统让我们把所有这些碎片放在一起——所有这些碎片对于把现代机器学习带给像中学生这样的人是至关重要的。

我真正喜欢的是,一个人可以做的不是玩具:一个人可以把我在书中讨论的东西,立即应用到现实生活中。在某种程度上,这是整体的反映自动化重点Wolfram语言:里面有非常复杂的东西,但它在所有层面都是自动化的,所以你不需要是一个专家,了解细节就能使用它,或者对什么能做什么不能有很好的直觉。

要进一步

好吧,那么如何在机器学习教学上更进一步呢?

一个早期的事情可能是开始谈论概率。ImageIdentify有各种可能的身份选择,但它分配给它们的概率是多少?
ImageIdentify[,全部,10,“概率”]

这可能会导致关于先验概率的有用讨论,以及关于以特异性换取确定性的问题。

但最重要的是训练。(毕竟,“机器学习培训师”对今天的一些中学生来说肯定是一个巨大的未来职业……)好消息是,在Wolfram语言环境中,只需少量数据就可以使培训工作正常进行。

让我们看一些人物形象的例子银河护卫队通过搜索网络(我们正在使用外部搜索API,所以很遗憾,您不能在开放云):

data = AssociationMap[WebImageSearch[#,

现在我们可以使用这些图像作为训练材料来创建一个分类器:

分类器=分类[数据]

当然,它可以识别火箭:

分类器[]

而且,是的,它认为真正的浣熊也是他:

分类器[]

它是怎么做到的?让我们看看FeatureSpacePlot

FeatureSpacePlot[平[值(数据)]]

有些看起来不错,但有些看起来令人困惑。因为它不是根据图片的主人,而是根据图片的背景颜色来排列图片。这里我们开始看到机器学习的一些微妙之处。我们构建的实际分类器之所以有效,只是因为在每个字符的训练示例中,都有不同的背景——所以它可以看出背景不是唯一的区别特征。

实际上,还有另一件重要的事情:分类不是从头开始分类图像。因为它已经经过了预先训练,可以挑选出“好的特征”,帮助区分真实世界的图像。事实上,它使用了它从创造中所学到的一切ImageIdentify以及它看到的与之相关的数千万张图像——预先知道它应该注意哪些特征。

看起来有点奇怪,但在内心分类是将每张图像描述为一系列数字,每一个数字都与不同的“特征”相关联:

特征提取[{}]

我们可以做一个极端的版本,坚持每个图像只减少到两个数字,这就是本质FeatureSpacePlot确定图像的位置:

降维[{}]

幕后

好吧,但在引擎盖下面到底发生了什么?它是复杂的。但是在Wolfram语言中,很容易看到它,并且了解它有助于对神经网络如何真正工作有一个直观的了解。举个例子,这是低级别的Wolfram语言符号表示神经网络的能量ImageIdentify

net=NetModel[“Wolfram ImageIdentification net for WL 11.1”]

实际上还有更多:只要点击并继续深入:

net=NetModel[“Wolfram ImageIdentification net for WL 11.1”]

是的,这很难理解——对中学生来说,甚至对专业人士来说都是如此。但如果我们把整个神经网络对象,应用到老虎的图片上,它会做什么ImageIdentify告诉我们这是只老虎

净[]

但这里有一件整洁的事情,由Wolfram语言的一大堆功能成为可能:我们实际上可以“进入”神经网络,以了解正在发生的事情。举个例子,让我们以网络的前3个“层”为例,将它们应用到老虎身上,然后可视化结果:

图片/@ Take[net, 3][]

基本上,网络制作了很多原始图像的副本,然后对每一个副本进行处理,以挑选出图像的不同方面。(事实上,发生的事情似乎与最初的几个层次非常相似大脑中的视觉处理.)

如果我们深入网络会怎么样?下面是第10层发生的情况。图像更抽象,可能会挑选出更高层次的特征:

图片/@ Take[net, 10][],20]

到第20级,网络正在“思考”许多小图像:

ImageAssemble[Partition[Image /@ Take[net, 20][]

但到了第28个关卡,它开始“得出一些结论”,只有一些可能的活动渠道“亮起”了:

ImageAdjust[ImageAssemble[Partition[Image /@ Take[net, 28][],50]]

最后,到第31个关卡时,剩下的便是一系列数字,以及一些可见的峰值:

ListLinePlot[采取[净、31][]]

并应用网络的最后一层(a“softmax”层)只剩下几个山峰:

ListLinePlot[net[,None], PlotRange -> All]

而最高的一种恰好与“老虎”的概念相对应:

净[,“最高概率”]

我不认为中学生会遵循所有这些细节(不,没有人应该学习)神经网络层类型就像他们学习水循环的一部分),但我认为看到“内部”真的很有用ImageIdentify,甚至可以粗略地了解它是如何工作的。对像我这样的人来说,这仍然有点像魔法,所有的一切都像它那样聚集在一起。但是,现在有了我们最新的Wolfram语言工具,人们可以很容易地查看内部,并开始对正在发生的事情有一个直觉。

培训过程

Wolfram语言的概念分类其功能是在尽可能高的水平上进行机器学习——尽可能地自动,并尽可能地建立在预先训练的基础上。但如果你想更全面地了解机器学习是什么样子的,看看如果你尝试从头开始训练神经网络会发生什么是有用的。

然而,有一个直接的实际问题:要得到一个神经网络,从头开始,真正做任何有用的事情,通常必须给它大量的训练数据,这很难收集和整理。但好消息是,随着最近发布的Wolfram数据存储库我们有一个不断增长的现成训练集集合,可以立即在Wolfram语言中使用。

这是经典的MNIST手写数字训练集,提供了6万个培训实例:

ResourceData(“MNIST”)

使用这样的训练集可以做的一件事就是将它的随机样本输入到分类.当然,这给了一个分类器函数它本质上是一个简单的版本TextRecognize对于手写数字:

c = Classify[RandomSample[ResourceData[

即使只有1000个训练例子,它也做得很好:

c ({})

是的,我们可以用FeatureSpacePlot看看不同的数字如何在特征空间中分开:

FeatureSpacePlot[First /@ RandomSample[ResourceData[

但是,好吧,如果我们想从头开始训练一个神经网络呢分类?首先我们得建立一个原始的神经网络。而且很方便,Wolfram语言内建了一堆经典的神经网络。这里一个叫做LeNet

lenet = NetModel(“lenet”)

它比ImageIdentifyNet,但它仍然相当复杂。但我们不需要了解它里面有什么,就可以开始训练它。相反,在Wolfram语言中,我们可以使用NetTrain(不用说,自动应用所有最新的GPU技巧等等):

net = NetTrain[lenet, RandomSample[ResourceData[

观察训练过程是很不错的,观察神经网络在拟合样本时的错误率的橙色线是不断下降的。大约20秒后,NetTrain决定它已经走得够远了,并生成了一个最终训练的网络,它运行得非常好:

净({})

如果你提前停止训练,效果就不会那么好:

1]" src="//www.fiberbeat.com/data/uploads/2017/05/InOutImg39-3.png" alt="net = NetTrain[lenet, RandomSample[ResourceData["MNIST"], 1000], MaxTrainingRounds -> 1]" width="419" height="305">

净({})

在机器学习的专业领域,有一个艺术和科学找出训练的最佳参数。但是有了现在的Wolfram语言,没有什么能阻止一个中学生做他们自己的实验,可视化并分析结果,得到和任何人一样好的直觉。

神经网络是由什么组成的?

好的,如果我们真的想深入到最低层次,我们必须讨论神经网络是由什么组成的。我不确定这有多少是中学的东西,但是一旦你知道了函数图,你就可以解释很多东西了。因为,你看,神经网络中的“层”实际上只是函数,接收数字,输出数字。

以LeNet的第二层为例。它本质上很简单斜坡函数,我们可以立即绘制(是的,它看起来像一个斜坡):

Plot[Ramp[x], {x, - 1,1}]

不过,神经网络通常不只是处理单个数字。它们处理数字的数组(或“张量”)——在Wolfram语言中表示为嵌套列表。每一层都输入一组数字,然后输出一组数字。这是一个典型的单层:

2]]" src="//www.fiberbeat.com/data/uploads/2017/05/InOutImg421.png" alt="layer = NetInitialize[LinearLayer[4, "Input" -> 2]]" width="309" height="72">

这个特定的层被设置为取2个数字作为输入,并输出4个数字:

层({2,3})

它可能看起来是在做一些非常“随机”的事情,事实上确实如此,因为层所实现的实际功能是由另一个数字数组或“权重”决定的,而这个数组NetInitialize这里是随机设置的。这是它在这个特殊情况下设置的值:

NetExtract(层,“权重”)

为什么这些有用呢?关键的一点是NetTrain方法是逐步调整神经网络每一层的权重,试图得到网络的整体行为,以匹配你给出的训练示例。

不过,有两个迫在眉睫的问题。首先,网络的结构必须是这样的:通过使用一些适当的权值集,可以得到你想要的行为。其次,必须有一些方法来逐步调整权重,以获得适当的值。

结果是一首单曲LinearLayer就像上面的那个不能做任何有趣的事情。这是它的输出(第一个元素)的等高线图,作为它的两个输入的函数。作为名字LinearLayer可能会说,我们总是得到一些平坦和线性的东西:

ContourPlot[第一[层[{x, y}]], {x, 1,1}, {y, 1,1}]

但这是一个让神经网络变得有用的重大发现:如果我们把几层连在一起,很容易得到更复杂的东西。(是的,在Wolfram语言中,一层的输出以一种漂亮、自动的方式编织成下一层的输入。)下面是一个4层、两个线性层和两个斜坡的例子:

2]]" src="//www.fiberbeat.com/data/uploads/2017/05/InOutImg461.png" alt="net = NetInitialize[NetChain[{LinearLayer[10], Ramp, LinearLayer[1], Ramp}, "Input" -> 2]]" width="527" height="135">

现在,当我们绘制函数时,它变得更复杂了:

ContourPlot[net[{x, y}], {x, - 1,1}, {y, - 1,1}]

我们也可以看看一个更简单的例子,一个有三层的神经网络,只有一个数字作为最终输出。(出于技术原因,仍然有两个输入是很好的,尽管我们总是将其中一个输入设置为常量1。)

2]]" src="//www.fiberbeat.com/data/uploads/2017/05/InOutImg48.png" alt="net=NetInitialize[NetChain[{LinearLayer[3],Ramp,LinearLayer[1]},“输入”->2]]" width="479" height="120">

这是这个特殊的网络作为它的输入的功能:

图[net[{x,1}],{x,-2,2}]

在网络内部,生成了一个由3个数字组成的数组——结果是“3”导致函数中最多有3(+1)个不同的线性部分。增加到100,事情会变得更复杂:

2]]" src="//www.fiberbeat.com/data/uploads/2017/05/InOutImg50.png" alt="net=NetInitialize[NetChain[{LinearLayer[100],Ramp,LinearLayer[1]},“输入”->2]]" width="492" height="120">
图[net[{x,1}],{x,-2,2}]

现在,关键是这在某种意义上是一个"随机函数"由特定的随机权值决定NetInitialize.如果我们跑NetInitialize很多次,我们会得到很多不同的结果:

2]]}, Plot[net[{x, 1}], {x, -2, 2}]], 8]" src="//www.fiberbeat.com/data/uploads/2017/05/InOutImg52.png" alt="Table[With[{net = NetInitialize[NetChain[{LinearLayer[100], Ramp, LinearLayer[1]}, "Input" -> 2]]}, Plot[net[{x, 1}], {x, - 2,2}]], 8]" width="489" height="360">

但最大的问题是:我们能找到一个“随机函数”的实例,对我们要做的任何事情都有用吗?或者,更具体地说,我们能找到一个随机函数来再现特定的训练例子吗?

让我们假设我们的训练示例给出了函数在图中点上的值(顺便说一下,这里的设置更像是机器学习的风格预测分类):

ListLinePlot[Table[Mod[n^2, 5], {n, 15}], Mesh -> All]

这是我们网络的一个实例:

2]]" src="//www.fiberbeat.com/data/uploads/2017/05/part2-InOutImg1.png" alt="net=NetInitialize[NetChain[{LinearLayer[100],Ramp,LinearLayer[1]},“输入”->2]]" width="492" height="120">

下面是它在一系列培训示例中最初所做的事情(是的,这显然是完全错误的):

图[net[{n, 1}], {n, 1,15}]

让我们用训练数据来训练网络NetTrain

网= NetTrain[净,data =表[{n, 1} - >{国防部[n ^ 2, 5]}, {n, 15}]]

在我的电脑上进行了大约20秒的训练后,有一些模糊的迹象表明我们开始重现至少一些原始训练数据。但这充其量是一种缓慢的过程,而且最终会发生什么还不清楚。

图[net[{n, 1}], {n, 1,15}]

这是神经网络研究的前沿问题,即什么结构的网络在任何特定情况下最有效(是的,我们正在研究这个问题)。但这里让我们尝试一个稍微复杂一点的网络:

2]]" src="//www.fiberbeat.com/data/uploads/2017/05/part2-InOutImg5.png" alt="net = NetInitialize[NetChain[{LinearLayer[100], Tanh, LinearLayer[10], Ramp, LinearLayer[1]}, "Input" -> 2]]" width="369" height="38">

这个网络的随机实例与我们上一个网络的结果并没有太大的不同(尽管它的存在)双曲正切层使功能更平滑):

双曲正切层

但现在让我们做一些训练(数据定义在上面):

net = NetTrain[网络,数据]

结果是这样的:

图[net[{n, 1}], {n, 1,15}]

事实上,如果我们将其与原始训练数据进行比较,我们会发现训练值恰好与神经网络产生的函数一致:

Show[Plot[net[{n,1}],{n,1,15}],ListPlot[Table[Mod[n^2,5],{n,1,15}],PlotStyle->Red]]

这是在训练过程中发生的事情。神经网络有效地“尝试”了一系列不同的可能性,最终确定了这里的结果:

机器学习动画

在什么意义上这个结果是“正确的”?它符合训练的例子,这就是我们所能问的。因为这是我们给出的所有输入。它如何在训练示例之间“插入”是它自己的事情。我们希望它能从给出的数据中“一般化”,但它不能从这里给出的几个点推导出整个数据分布,所以它所做的平滑插值是最好的。

在训练值的范围之外,神经网络做的事情似乎是相当随机的——但再次强调,没有“正确答案”,所以我们不能责怪它:

图[net[{n, 1}], {n, - 5,25}]

但事实是,由于我们原始神经网络的任意性和混乱性,我们能够成功地训练它,这是相当了不起的。实际上,我们在这里讨论的这种神经网络已经被研究了60多年,但直到现代“深度学习革命”之前,没有人知道训练它们解决实际问题是可行的。

但是现在,特别是我们现在用Wolfram语言所拥有的一切,任何人都很容易做到这一点。

有很多东西需要探索

现代机器学习是一个非常新的领域,因此许多明显的实验都还没有被尝试过。但是有了我们的Wolfram语言设置,即使是中学生也可以做很多事情。例如(我承认在我写这篇文章的时候我对此很好奇):人们可以问我们正在研究的微小神经网络能学习多少东西。

下面是前60个罗马数字的长度图:

ListLinePlot[表[StringLength [RomanNumeral [n]], {n, 60}]]

经过少量的训练,网络成功复制了以下内容:

NetTrain[net, Table[{n, 1} -> {StringLength[romannumeric [n]]}, {n, 60}]];
图[%[{n, 1}], {n, 1,60}]

有人可能会想这可能是最好的了。但我很好奇它最终是否能做得更好——所以我就让它在我的电脑上训练了两分钟。这是一个相当好的结果

NetTrain[net, Table[{n, 1} -> {StringLength[RomanNumeral[n]]}, {n, 60}], MaxTrainingRounds -> Quantity[2,

图[%[{n, 1}], {n, 1,60}]

我想我知道为什么这个东西会这样运作了。但看到它,我们就会想到各种各样的新问题。但对我来说,最激动人心的一点是,这一领域是多么广阔,现在探索它是多么容易。

是的,有很多技术细节,一些基本的,一些肤浅的。但超越所有这些,还有直觉需要发展。这是一个可以从中学生开始的完美的东西…

2的评论

  1. 我的年龄比一般的中学生大得多,但我非常喜欢这个帖子,真的!这是我深入研究机器学习问题的一个很好的开始。继续,斯蒂芬!

    帕维尔·德沃夏克
  2. 谢谢你写这篇文章,非常鼓舞人心!
    我是荷兰中学(数学)老师。一些学生正在考虑花大约60个小时学习编程的基础知识(他们只有一点经验),如果可能的话,将编程和机器学习结合起来。根据他们的经验,你认为这能在这个时间段内行得通吗?或者你看到其他有趣的可能性了吗?我很想看到你的风景!

    Stijn
Baidu