神经网络扮演 Flappy Bird
目前,我是一名大学 IT 专业的学生。这学期,我上了一门非常有趣的课,我选择了自己的主题来学习,并设计了自己的项目。所以我决定学习和研究一些非常有趣和独特的东西。当我在探索各种主题时,我发现了一个关于神经网络的视频教程,我对学习这个主题非常感兴趣。
看完教程视频后,我想到了一个实现一个学习如何玩 Flappy Bird 游戏的神经网络程序的想法。最后,这是我的结果。
当程序第一次运行时,它什么也不做,因为它对游戏一无所知。然而,几个小时的训练,它学会了如何玩游戏。
在这篇文章中,我想给出一个关于神经网络的基本信息以及我是如何实现我的程序的。
什么是神经网络?
首先,我想说说什么是神经网络。在编程中,人工神经网络是受生物神经网络的结构和功能方面启发的用于机器学习的计算模型/算法。
Neural Network
每个神经网络都有一个输入层、一个输出层和一个或多个隐藏层。每个圆圈代表一个神经元,一个神经元与下一层的每个神经元都有联系。
每个连接都有权重值,每个神经元都有偏差值。例如,下图显示了神经网络中发生的情况。
当神经网络计算输出时,它会对权重和偏差进行大量计算。你可以很容易地预料到,如果我改变其中一个权重或偏差值,最终的输出也会改变。换句话说,训练神经网络意味着找到并调整权重和偏差值,以给出我们想要的最佳输出。
新立得
Synaptic.js 是一个非常棒的用于 node.js 和浏览器的 JavaScript 神经网络库。如果您安装了 node.js,您可以使用以下命令安装 synaptic.js:
npm install synaptic --save
现在,我想提供一个使用 Synaptic.js 库解决问题的简单例子。
如果你看一下左边的表格,它有两个输入和一个输出。如果第一个输入大于第二个输入,则输出为 1。否则输出为 0。
前五个记录将是我们的测试数据,我们将看到最后两个记录的输出是什么。
下面的代码展示了我如何使用 synaptic.js 来解决这个问题。
**var** synaptic = require('synaptic');**var** myNetwork = **new** synaptic.Architect.Perceptron(2, 3, 1);
**var** learningRate = .2;
//training
**for** (**var** i = 0; i < 20000; i++)
{
//test data 1
myNetwork.activate([2,1]);
myNetwork.propagate(learningRate, [1]);
//test data 2
myNetwork.activate([1,5]);
myNetwork.propagate(learningRate, [0]);
//test data 3
myNetwork.activate([4,2]);
myNetwork.propagate(learningRate, [1]);
//test data 4
myNetwork.activate([5,2]);
myNetwork.propagate(learningRate, [1]);
//test data 5
myNetwork.activate([3,5]);
myNetwork.propagate(learningRate, [0]);
}
console.log(myNetwork.activate([2,5])); //[ 0.0000728000640238746 ]
console.log(myNetwork.activate([3,1])); //[ 0.9999465030073619 ]
代码分解
**var** myNetwork = **new** synaptic.Architect.Perceptron(2, 3, 1);
上面的代码创建了一个具有 2 个输入神经元、3 个隐藏神经元和 1 个输出神经元的神经网络。
myNetwork.activate([2,1]);
activate()方法从给定的输入返回输出(在上面的代码中,2 和 1)。输出值在 0 到 1 个浮点值之间。
myNetwork.propagate(learningRate, [1]);
在调用 activate 函数之后,我们调用 propagate()函数,用学习率和目标输出值来训练网络。这是通过反向传播完成的。
带 Flappy Bird 的神经网络
当我实现 flappy bird 程序时,我得到了 5 个输入和 1 个输出。
这些输入是:
- 鸟的位置
- 速度
- 下一个管道之间的距离
- 上部管道高度
- 下部管道高度
每次渲染游戏时,神经网络都会获得输入并激活它。如果输出大于 0.55,鸟会跳起来。
但是,我不能使用 propagate()方法,因为没有测试数据及其目标值。在这种情况下,我们可以使用遗传算法来训练我们的神经网络。
假设我一代有 8 只鸟,每只鸟有 6 个神经元。(在我的实际实现中,一代中有 12 只鸟,每只鸟有 18 个神经元)。
在一代(12 场)之后,我挑选最好的 4 只鸟并应用交叉和变异。重复同样的过程,直到我们得到好的结果。
对于交叉函数,偏置值将被交叉运算。
Learn.crossOverDataKey(netA.neurons, netB.neurons, 'bias');
剪切神经元的随机位置,并将其交叉。
Learn.crossOverDataKey = **function** (a, b, key) {
**var** cutLocation = Math.round(a.length * Math.random());
**var** tmp;
**for** (**var** k = cutLocation; k < a.length; k++) {
// Swap
tmp = a[k][key];
a[k][key] = b[k][key];
b[k][key] = tmp;
}
}
对于变异函数,偏差和权重值都会发生变异。
Learn.mutateDataKeys(net.neurons, 'bias', Learn.mutationProb);
Learn.mutateDataKeys(net.connections, 'weight', Learn.mutationProb);
连接和神经元被随机选择并变异为随机值。在我的实现中,我有 20%的突变率。
Learn.mutateDataKeys = **function** (a, key, mutationRate){
**for** (**var** k = 0; k < a.length; k++) {
// Should mutate?
**if** (Math.random() > mutationRate) {
**continue**;
}
a[k][key] += a[k][key] * (Math.random() - 0.5) * 3 + (Math.random() - 0.5);
}
}
你可以在这里看到我的完整代码。
此外,以下是我使用的资源的链接:
synaptic . js:【https://github.com/cazala/synaptic】
flappy bird 网页版:https://github.com/nebez/floppybird
我引用的代码/算法:https://github.com/ivanseidel/IAMDinosaur
神经网络助手
这篇文章的想法是训练一个助手神经网络,专门针对我们的主网络所处理的数据。类似蝙蝠侠和罗宾的网络之间的内置层级将有助于该模型更好地执行。
尽管面临计算复杂性的挑战,集成学习仍然是一个非常令人兴奋的概念。有两类主要的集成学习方法,bagging 和 boosting。使用 bagging,来自训练数据的随机样本用于构建单独的模型,然后从这些模型中进行多数投票以确定输出。boosting 算法更有趣。Boosting 构建一个模型,然后评估该模型在哪个数据子集上出错最多,然后主要使用该数据来构建后续模型。这可能在计算上是昂贵的,并且在如何确定求解算法上是混乱的。
在最近的一个项目中,我使用许多卷积层和数以千计的数据图像构建了一个怪物图像分类器。我已经尝试了书中的每一个技巧来提高这个模型的准确性。然而,这个模型有时仍然会出错。一个模型执行了大约 90–95%可以被认为是做得很好,不幸的是,一些任务需要比这更高的精度。
Boosting 似乎是支持这种整体网络的一个很好的解决方案。类似于蝙蝠侠和罗宾,我的主要卷积网络是蝙蝠侠,而由主要错误分类的实例构建的卷积网络是罗宾。罗宾不需要成为团队的代言人,而是应该专注于学习蝙蝠侠的杂项倾向。通过成为蝙蝠侠的专家,他很可能会自己解决问题,罗宾能够为团队做出贡献。
用神经网络实现 boosting 是很棘手的,因为没有一个我们可以调用的库函数,比如 Boosted_CNNs。
这是我如何实现 boosting 来创建蝙蝠侠和罗宾类型的分类模型的粗略工作流程:
Partition Train/Test split
Train CNN with Train dataEvaluate CNN on Train data outside of training loop
Select misclassified instances and 25% of the dataTrain CNN on this new subsetWith future predictions weight the output of CNN1 80% & CNN2 20%Evaluate model
我发现要做到这一点,你需要大量的数据,然而,我确实看到了我的具体问题的准确性略有提高。感谢阅读!
CShorten
Connor Shorten 是佛罗里达大西洋大学计算机科学专业的学生。对计算机视觉、深度学习和软件工程感兴趣。
神经网络:专家与注意力的混合体
一个混合专家 (MoE)是一种特殊类型的神经网络:神经元连接成许多小簇,每个簇只在特殊情况下活跃。网络的较低层提取特征,并召集专家来评估这些特征——对于每种情况,只召集一些专家。混合的专家有明显的优势:他们可以用更大的专业化来应对特定的环境,允许网络展示更多样的行为;专家可以接收混合刺激,整合来自不同传感器的数据。而当网络在运行时,只有少数专家在活动——即使是一个庞大的网络也只需要少量的处理能力。随着神经网络变得越来越复杂,集成了许多数据流,并提供了更多样的响应,专家模型的混合将占主导地位。因此,它有助于理解专家的混合体是如何进化的。
基于特征的注意力
专家的混合已经被用于翻译任务。每个小的专家群学习处理一个单独的词类或特殊的语法规则。然而,专家翻译的混合体目前没有使用注意力模型。
注意力只是一个过滤器,一次只允许部分输入进入网络。通过不断转移注意力,网络可以处理任何规模的输入。一个图像识别网络可以接收许多不同大小的图像,并努力一幅一幅地解析这些图像。一个翻译网络可以在句子之间跳跃,形成被许多行分隔的单词之间的关系。以这种方式移动输入的专家组合将能够提取单词之间更丰富的关系,并且能够更好地准确翻译。当某些特征被识别时,每个专家被调用;这些特性可能出现在输入的不同区域,需要对输入有广泛的理解。专家会知道哪里需要注意。
注意力记忆
专家的混合也是更复杂记忆形式的理想模型。解析翻译通常需要网络关注文本中的几个不同区域。多处出现的语句必须组合成一个连贯的整体,这样翻译才有意义。一群专家必须将注意力集中在一个领域,同时记住来自另一个领域的信息。这是通过将专家集群连接到网络的过去状态来实现的,类似于 LSTM 的连接。
LSTMs wire 每个神经元到都有自己的过去、而不考虑其邻居的过去状态。然而,专家的混合体会被连接到触发专家的特征检测器的过去状态。这是一种“更高级”的记忆。一个特性可能出现,而不会触发一个专家集群。然而,当与相关输入结合时,该特征的记忆可能会在稍后触发其专家*!专家将对当前输入和过去特征的组合做出响应。这种行为目前在 LSTM 或其他循环网络中是不可能的。而且,这一切都很重要。*
评审流程
如果一个专家的混合体有一个注意力系统,那么它可以在文本中跳来跳去,寻找信息,直到找到触发专家的东西。这类似于我们复习所读内容的方式;如果我们感到困惑,我们会跳回到不同的地方,直到我们找到澄清文本的信息。一只 LSTM 犬无法四处跳跃,直到它变得清晰。如果注意力系统与特征层次的记忆相连,一群专家将审查它的输入*,直到它确定无疑*。它四处寻找支持结论的信息,而不是脱口而出答案。
这个审查过程可以被捕获和可视化。当我们看到什么信息触发了一个专家,我们知道网络已经基于那个信息做出了决定。而且,因为每个专家处理一个非常具体的子任务,我们知道每个专家正在解决什么样的问题。这是‘窥视内部’神经网络黑盒的最佳方式。“我们得到了这个答案,因为这个专家在看到这个信息时开火了……”这是我们可能接近的告诉我们它在想什么的网络。
成长
专家的组合也是解决复杂多变的问题的理想选择。当必须学习新信息时,大多数网络需要完全重新训练,并且它们丢失了它们在之前学习的课程。混合的专家可以保留其旧知识,并简单地插入新的专家群,接受新信息的培训。当使用反向传播训练这种“扩充”的专家混合物时,所有旧的神经连接都被“冻结”——只有新的集群被允许学习。这些“菜鸟”很快成为新任务的专家,而不会在网络的其他部分失去原有的专业知识。
总之,这些修改提供了一个专家的混合体,他们有能力适应、消化任何规模的输入,并回忆起影响结果的复杂特征。我期望这些品质对于“下一波”神经网络任务来说是至关重要的:连贯反应。目前,被训练生成文本的神经网络仅从文本中学习*。被训练来识别图像的网络仅从图像中学习。这种“与世隔绝”的训练将会停止。生成真实文本的神经网络需要对真实世界有足够的了解,以使其陈述在逻辑上和物理上连贯,而不仅仅是语法上正确。一个图像识别网络将需要尝试在 3D 中实例化它的图像,看看它是否有意义。谷歌已经开始在这一领域开展工作,用“一个模型来学习所有这些”,它从文本、图像和声音的稀疏配对中学习。他们还有很长的路要走。而且,他们还没有将注意力、记忆力和成长能力的专家结合起来使用。我们很快就会需要它。*
神经网络:ReLU 的替代方案
ReLU activation, two neurons
上面是两个神经元(紫色和橙色)的激活(粉色)图,使用了一个常用的激活函数:整流线性单元,或 ReLU。当每个神经元的总输入增加时,ReLU 也会增加其激活——前提是输入超过某个阈值。这导致峰值出现在输入空间的一角,如上图右上角所示。添加更多的 ReLU 神经元将增加立方体的维度,然而模式仍然存在:神经元的激活总是在一个远处的角落达到峰值,沿着相邻的边有倾斜的侧翼。因为峰值只能出现在一个单个拐角,所以这些神经网络的表达能力是有限的。我提供一个替代方案: 反射线性单元 。(RefLU?)
反射线性单元
整流线性单元仍有几个优点。它们易于计算,非常适合专门的硬件架构,如谷歌的 TPU。它们是非线性的,形成了清晰的行为边界,这是单一直线所不能做到的。而且,它们创造了一个持久的梯度(至少在它们的右侧),这将它们与激活功能区分开来,激活功能逐渐变细——例如,s 形函数和逻辑函数。随着这些激活逐渐减少,它们的斜率几乎是水平的,因此梯度下降几乎不会试图修改神经元,学习进展缓慢。ReLUs 的右半部分具有恒定的正斜率,因此它在所有这些点接收到来自梯度下降的相同强度的信号*。*
Rectified Linear Units, with various synapse weights causing different tilts
然而,经过整流的线性单元可能会遭受“死亡神经元”的困扰:当它们接收到强烈的负信号时,它们的倾斜度会下降到零,之后没有任何东西能够刺激神经元。没有新的梯度信号通过它们,因为它们是平的,所以神经元的损失永远不会被纠正。经常被引用的斯坦福课程笔记解释说“你的网络中有多达 40%可能是‘死的’。”
Reflected Linear Units, with various peak positions and tilts
反射的线性单位分享了 ReLUs 的好处:它们易于计算,是非线性的,并且不会逐渐变细。此外,他们不受“死亡”的影响。无论任何一半的斜率是正还是负,RefLU 仍然起作用。只有双方奇迹般地更新到恰好零斜率,那一个神经元才会死亡。RefLUs 提供了其他好处,我可以在一些细节之后解释这些好处…
倾斜线条
RefLU 由在中间某处相交的两条线段组成。你可能会把这些线条想象成一束激光,从一面看不见的镜子上反射回来。天真的是,他们都在倾斜,在他们的接触点形成一个高峰。此 RefLU 的变量比 ReLU 多;虽然 ReLU 仅指定其右半部分的斜率,但 RefLU 必须指定左右半部分的斜率,以及它们相交的高度和距离*。*
这四个变量都接收梯度下降信号,以小步长更新,试图匹配数据要求的激活的曲线。虽然陡峭的拱形可能不完全符合数据所需的形状,但是 RefLU 激活的总和可以在输入空间内的任何位置形成组合峰值。那是远远优于 ReLUs 的,ReLUs 只能在单个角形成一个峰值。而且,虽然大多数表面可以由分散在各处的峰的集合来近似,允许多个 RefLUs 构建他们需要的景观,但是 ReLUs 的角峰只能创建更陡的角。反射线性单位可以采用数据所需的形状。
稀疏激活
通过将反射的线性单元初始化为倾斜的峰值,网络在学习阶段的行为被修改。除了 RefLU 之外,只有 sinc 和高斯分布包括其激活函数的右下侧。在操作过程中,这意味着变得过于兴奋的神经元实际上会关闭!这导致了一种完全不同的激活模式:当许多神经元在一个簇中放电时,它们会在下一层的一些神经元中重叠太多,导致这些神经元中的一些变得安静,而不是所有的都变成不和谐的一致。
通过让一些兴奋簇中的神经元安静下来,网络的活动朝着稀疏的方向调整。没有癫痫发作的“是”。每个图像或声音都是用最少的活跃神经元进行分类的,只对最相关的信息进行编码。这在网络中留下了更多的“空间”来学习多种任务和丰富的分类,稀疏性将相似分类的输入推得更近。通过这种方式,用 RefLU 激活功能构建的网络将表现得更像人脑,具有自己稀疏而有意义的活动。
否定之肯定
此外,RefLU 不是一个简化的 sinc 函数,sinc 和高斯函数在其两侧降至零,而 RefLU 实际上降至负值区域。这使得 RefLU 成为显著不同的激活功能。通常,对于一个神经元来说,要从积极的兴奋性信号转换为消极的抑制性信号,其突触权重必须从积极的变为消极的。在中间,神经元的权重为零,“杀死”它。并且,当权重接近零时,整个激活功能被压扁。
相反,RefLUs 可以为某些输入电平产生强负输出,而为其他电平产生强正输出。功能没有被压扁。它可以起到双重作用,同时兴奋和抑制更多的神经元*,分享 arctan 激活功能的这种强度。虽然 arctan 仅在输入电平为低时产生负信号,但 RefLUs 是唯一能够为高和低输入变为负的*。**
RefLUs 不需要在两端变为负;梯度下降可以从向下的斜坡向向上的斜坡平滑地倾斜任一侧,在这种情况下,神经元作为现有激活函数的近似来操作。
我希望这一分析能够激发研究人员将 RefLU 与它的哥哥 ReLU 进行比较,看看这些潜在的好处在哪里提高了性能。特别是,网络可能受益最大,它必须学习和联系许多任务,解释神经元活动,并避免死亡的神经元。在这些概念的基础上,可能还有其他更高级的激活功能;重要的一点是,我们还没有挖掘出神经网络新花样的源泉。我们应该继续假设和实验,因为智力是我们面临的最大问题。我希望有些人会受到启发继续寻找!
神经网络:类比
当我们的大脑形成类比时,它们正在做一些特殊的事情——一些现有神经网络中缺少的事情。我们的大脑采用了为一个任务设计的过程,并且在一个新的任务中使用相同的过程。他们转移他们所学的东西。当我们形成一个概括,两个任务之间的类比时,这被称为’迁移学习’。
我将概述展示迁移学习的人工神经网络的潜在途径:
首先,稀疏分布式表示
Numenta 的分级临时记忆,以及其他技术,依赖于稀疏分布式表示。这方面的一个例子是一个很长的 1 和 0 的字符串,其中几乎所有的值都是 0——1 有一个稀疏分布*。如果每个数字代表不同的东西,比如“尖耳朵”、“尾巴”、“胡须”,那么任何输入都可以被编码为这些稀疏分布之一。记录“尖耳朵”、“尾巴”和“胡须”存在的图像将与输出“猫”紧密相关。稀疏分布表示可以以这种方式映射到输出,隐含地为它选择“猫”的提供了一个理由——它看到了胡须、尾巴和尖尖的耳朵。*
稀疏分布式表示允许一些巧妙的技巧。因为它们只有几个 1 和许多 0,两个表示可以通过加法组合*,当一个新的表示与这个组合进行比较时,通常只有当新的输入与旧的输入之一匹配时,它才会匹配。例如,如果向网络呈现一幅有猫和自行车的图像,则对应于“胡须”、“尾巴”和“尖耳朵”的位的值为 1,以及对应于“两个轮子”、“座位”和“车把”的位的值为 1。该表示的所有其他位将为零。然后,一个带有猫和棒球的新图像呈现在网络上,并附有问题:“这两个图像之间的相同点是什么?”虽然棒球触发了“球体”、“白色”和“缝合”的位,但猫仍然点亮了“尖耳朵”、“尾巴”和“胡须”的位,因此通过比较可以立即确定猫为两幅图像所共有。*
这种识别两种表征之间共性的能力是形成类比的关键。为了看到这一点,我们需要看看专家的混合的动态的稀疏分布表示。
混合专家
听起来像是专家的混合体。神经网络接收输入,并将它们传递给各种各样的“专家”——经过训练处理特定类型输入的神经元集群。在一组专家对输入进行处理后,他们的输出将被解析给新的一组专家。也就是说,使用的专家取决于输出是什么。每个结果对应不同的专家组。
所以,每个专家的表现都不一样。而且,当遇到一种新的任务时,一批新的专家会接受这项任务的培训。我提出,当新任务被训练后,它的结果可以与旧任务在不同情况下的结果进行比较。如果结果与匹配,那么新任务的专家可以由老专家替换。这个过程就是类比的形成。
想象一下——一个神经网络被训练来观看视频并预测棒球的运动。神经网络学会将“球体”、“白色”、“缝线”识别为“棒球”,并将棒球的数据发送给一组处理“下落物体”的专家神经元。网络将球视为下落物体,期望它以抛物线运动下落。它的专家,在许多棒球下落的例子之后,能准确地预测棒球的运动。
然后,同一个网络呈现出一个篮球的图像。就像以前一样,“球体”在稀疏分布表示中亮起,但它是“橙色”和“纹理化的”,而不是“白色”和“缝合的”。这是一个新的环境,首先,它被授予一个新的专家神经元集群。过了一会儿,这些新的神经元学会了预测篮球的运动——专家们独立地重新发现了抛物线运动。
接下来,抛物线运动必须与棒球的抛物线运动相比较。如果神经网络识别出两个专家集群的行为方式相同,那么“篮球”实例可以被馈送给棒球使用的同一“下落物体”专家。把“篮球”和“棒球”放在同一个地方,这是两个之间的一个类比——它们都是“落体”。
假设出现了第三个图像,一个茶杯。它不是一个“球体”,在它的较低层没有任何其他的特性。然而,如果它下落,它会受到与棒球和篮球相同的抛物线运动的影响——因此,它将被映射为“下落物体”的相同专家群集的类比。最终,大多数事情将被映射到同一个“落体”专家——这将是一个近乎普遍的原则,达到我们在自己心中看到的概括水平。
如何比较专家
当专家们观察棒球时,他们学会了下落物体的抛物线运动。看篮球,一群新的专家学习同样的抛物线运动。我们怎么知道他们学到了相同的原理?我们可以向他们中的每一个输入另一个,看看他们的预测是否匹配。让棒球数据反馈给受过篮球训练的专家,反之亦然。
我们不需要测试棒球和篮球的每一个实例。要测试的最重要的是 边缘案例 。这些被定义为两类专家最有可能产生分歧的地方。如果这些边缘情况匹配,那么我们有信心这两个专家集群已经学习了相同的一般原理。在这些边缘情况下的测试等同于科学方法,在极端情况下进行实验以验证一个概括。
然而,有许多可用的专家集群。我们如何知道从哪里开始?这就是我们需要每个专家的 行为 的稀疏分布表示的地方。这是新的,可能会在一个幼稚的网络和一个推广良好的网络之间产生差异。
编码专家行为
每个专家都需要自己的稀疏分布式表示。该字符串的位通常为“0”,在对应于该专家属性的位置有几个“1”。在棒球上训练的“下落物体”专家将在对应于“抛物线”的位置包含值为“1”的位,因为它的抛物线运动,以及“向下移动”和“小物体”。相比之下,在破碎的冰山上训练的一群专家神经元会识别相同的抛物线下落,照亮“抛物线”并“向下移动”,然而与下落的棒球相比,它们巨大的尺寸会减缓感知的抛物线运动。它将包含用于“大对象”而不是“小对象”的位。这是每个专家的编码,根据其行为的性质。
当两个专家描述相似的行为时,他们有相似的稀疏分布表示。许多“1”在它们的位串上重叠。棒球和篮球的专家几乎完全重叠,这表明他们很可能是相似的。
潜在的相似性是这样发现的:一个新专家的稀疏分布表示与现有专家的表示进行比较,如果两者具有匹配的表示,则假设它们是相似的。从那里,交换他们的输入来检查边缘情况,如前所述。如果边缘案例匹配,假设类比有效,新专家被更成熟的专家取代。
注意,通过使用稀疏分布式表示,只有当某个属性存在时,1 和 0 的字符串才进行编码。它不声明输入的哪个分量触发属性的存在。下落的棒球和破碎的冰山都可以包含“天际线”作为其输入的属性,并且稀疏分布表示不会说明“天际线”或“冰山”是“向下移动”的来源。该信息必须通过消融来发现。
如果下落棒球的视频在其稀疏分布表示中包含比特“天际线”,那么当“天际线”被移除时,可以测试专家的结果。天际线的消除会改变“抛物线”、“向下移动”或“小物体”吗?如果不是,那么“天际线”被认为与专家表示的比特无关。“Skyline”对于处理“外部”的不同专家来说可能很重要,但是稀疏分布表示允许我们将其与“向下移动”位分开。就像在两个图像中识别一只猫,独立于旁边的棒球或自行车。通过这种方式,输入的每个组成部分都被映射到它所修改的专家表示的一组位上——我们发现每个位都依赖于哪些输入。
每当专家表示的一个比特不依赖于输入表示的一个比特时,比如“向下移动”独立于“skyline ”,当解析使用哪个专家时,网络学会忽略该独立比特。无论是否出现“天际线”,棒球下落的视频都会触发“抛物线运动”。棒球是网络关注的地方,将数据传递给“抛物线运动”专家。
因此,我们可以说视频中“抛物线运动”的源是棒球。这就是我们如何克服专家的稀疏分布表示没有明确描述输入的哪一部分触发其表示中的位的事实。这个消融过程相当于测量输入相对于专家表示的比特的梯度。
类比的价值
通常,在专家混合的情况下,每个专家只对训练集的一小部分是活跃的。它没有什么可以借鉴的例子。然而,如果专家是由稀疏的分布式表示来描述的,我们可以比较他们,看看假设的类比潜伏在哪里。如果他们的表现匹配,我们测试他们之间的边缘情况,以验证假设的类比,这将那些专家结合成一个。一旦专家有了这样的类比,更大部分的训练数据就会流过它。“落体”专家不再仅仅处理棒球下落的视频,而是概括处理茶杯、篮球、冰山、猫下落的视频。
所有这些不同事件的知识相互补充,大大提高了专家的准确性。专家可以像我们一样从更少的例子中学习。并且,它使用假设和实验不断地发现类似的行为。在强人工智能之前,我只看到一些其他的障碍,使用这种方法。
神经网络和反向传播以简单的方式解释
任何复杂的系统都可以用一种简单的方式抽象出来,或者至少可以分解成基本的抽象组件。复杂是由几个简单的层次累积而成的。这篇文章的目的,是解释神经网络如何以最简单的抽象方式工作。我们将尝试将 NN 中的机器学习机制简化为其基本的抽象组件。与其他解释神经网络的帖子不同,我们将尝试使用尽可能少的数学方程和编程代码,并只关注抽象的概念。
在最高和最简单的抽象表示中,监督神经网络可以表示为具有如下两种学习和预测方法的黑盒:
Neural network as a black box
学习过程接受输入和期望输出,并相应地更新其内部状态,以便计算出的输出尽可能接近期望输出。预测过程接受输入,并根据其过去的“训练经验”使用内部状态生成最可能的输出。这就是为什么机器学习有时被称为模型拟合。
为了实现这一点,我们将把学习过程分解为几个组成部分:
一个简单的数字例子
从神经网络和监督学习开始的最简单的例子是简单地从一个输入和一个输出以及它们之间的线性关系开始。监督神经网络的目标是尝试在所有可能的线性函数中搜索最适合数据的函数。以下面的数据集为例:
+--------+-----------------+
| Input | Desired output |
+--------+-----------------+
| 0 | 0 |
| 1 | 2 |
| 2 | 4 |
| 3 | 6 |
| 4 | 8 |
+--------+-----------------+
对于这个例子来说,输出= 2 x 输入似乎非常明显,但是对于大多数真实数据集来说,情况并非如此(其中输入和输出之间的关系是高度非线性的)。
步骤 1-模型初始化
学习的第一步,是从某个地方开始:最初的假设。就像遗传算法和进化论一样,神经网络可以从任何地方开始。因此,模型的随机初始化是常见的做法。背后的理性是,无论我们从哪里开始,如果我们足够坚持,并通过一个迭代学习过程,我们可以达到伪理想模型。
为了打个比方,举一个一生中从未踢过足球的人为例。他第一次试着投篮时,他可以随意投篮。同样,对于我们的数值案例研究,让我们考虑下面的随机初始化:(模型 1): y=3.x 。这里的数字T33是随机生成的。另一种随机初始化可以是:(模型 2): y=5.x ,或者**(模型 3): y=0,5.x** 。
我们稍后将探索,通过学习过程,所有这些模型如何能够收敛到理想解 (y=2.x) (我们正在努力寻找)。
在本例中,我们将探索通用形式 y=W.x 的哪个模型最适合当前数据集。其中 W 被称为网络的权重,并且可以被随机初始化。这些类型的模型被简单地称为前馈线性层。
步骤 2-向前传播
在随机初始化模型之后,自然要做的一步是检查它的性能。
我们从现有的输入开始,通过网络层传递它们,并向前计算模型的实际输出。
+--------+------------------------------------+
| Input | Actual output of model 1 (y= 3.x) |
+--------+------------------------------------+
| 0 | 0 |
| 1 | 3 |
| 2 | 6 |
| 3 | 9 |
| 4 | 12 |
+--------+------------------------------------+
这一步被称为前向传播,因为计算流程从输入- >通过神经网络- >以自然的前向方向进行。
第 3 步-损失函数
在这个阶段,一方面,我们有随机初始化的神经网络的实际输出。
另一方面,我们有我们希望网络学习的期望输出。让我们把它们都放在同一个表中。
+--------+-----------------+-----------------+
| Input | Actual output | Desired output |
+--------+-----------------+-----------------+
| 0 | 0 | 0 |
| 1 | 3 | 2 |
| 2 | 6 | 4 |
| 3 | 9 | 6 |
| 4 | 12 | 8 |
+--------+-----------------+-----------------+
如果我们将此与我们的足球运动员第一次射门进行比较,实际输出将是球的最终位置,期望输出将是球进入球门。开始时,我们的玩家只是随意射击。假设球大部分时间都在球门的右侧。他可以从中学到的是,下次训练时,他需要更多地向左侧投篮。
为了能够概括任何问题,我们定义了我们所说的:损失函数。基本上,它是一个性能指标,衡量神经网络如何达到其目标,产生尽可能接近期望值的输出。
最直观的损失函数简单来说就是损失=(期望输出—实际输出)。然而,当网络下冲时,该损失函数返回正值(预测<期望输出),当网络过冲时,该损失函数返回负值(预测>期望输出)。如果我们希望损失函数反映性能上的绝对误差,不管它是过冲还是欠冲,我们可以将其定义为:
损失=绝对值(期望-实际)。
如果我们回到足球运动员的例子,如果我们的新手把球射向球门右侧 10 米或左侧 10 米,我们认为,在这两种情况下,无论方向如何(右或左),他都没有击中目标 10 米。
在这种情况下我们将向表中添加一个新列- > 绝对误差。
然而,有几种情况会导致相同的总误差:例如,许多小误差或几个大误差会精确地累加到相同的总误差。因为我们希望预测在任何情况下都有效,所以最好是分布许多小误差,而不是几个大误差。
为了鼓励神经网络收敛到这种情况,我们可以将损失函数定义为绝对误差的平方和(这是神经网络中最著名的损失函数)。这样,小错误比大错误要少得多!(2 的平方是 4,但 10 的平方是 100!因此,10 的错误比 2 的错误多罚 25 倍——不仅仅是 5 倍!)
我们的表格如下:
+--------+----------+-----------+------------------+---------------+
| Input | actual | Desired | Absolute Error | Square Error |
+--------+----------+-----------+------------------+---------------+
| 0 | 0 | 0 | 0 | 0 |
| 1 | 3 | 2 | 1 | 1 |
| 2 | 6 | 4 | 2 | 4 |
| 3 | 9 | 6 | 3 | 9 |
| 4 | 12 | 8 | 4 | 16 |
| Total: | - | - | 10 | 30 |
+--------+----------+-----------+------------------+---------------+
请注意,如果我们只考虑第一个输入 0,我们可以说网络正确预测了结果!然而,在我们的足球运动员类比中,这只是初学者的运气,他也能设法从第一次射门就得分。我们关心的是最小化整个数据集的总误差(误差平方和的总和!).
总的来说,损失函数是一个误差度量,它给出了如果我们用由我们训练的神经网络模型生成的实际输出来代替真正的期望输出,我们损失多少精度的指标。所以才叫亏!
简单地说,机器学习的目标变成最小化损失函数(尽可能接近 0)。
我们现在可以将我们的机器学习问题转化为一个优化过程,目的是最小化这个损失函数。
第四步-差异化
显然,我们可以使用任何优化技术来修改神经网络的内部权重,以最小化我们之前定义的总损失函数。这些技术可以包括遗传算法或贪婪搜索或甚至简单的强力搜索:
在我们简单的数字示例中,仅使用一个权重参数来优化 W ,我们可以搜索从-1000.0 到+1000.0 的步长 0.001,其中 W 在数据集上具有最小的误差平方和。
如果模型只有很少的参数,并且我们不太关心精度,这可能行得通。然而,如果我们在一个 600x400 输入的数组上训练神经网络(像在图像处理中),我们可以很容易地达到有数百万个权重的模型进行优化,暴力甚至是不可想象的,因为这纯粹是浪费计算资源!
幸运的是,数学中有一个强大的概念可以指导我们如何优化称为微分的权重。基本上它处理的是损失函数的导数。在数学中,函数在某一点的导数给出了该函数在该点改变其值的速率或速度。
为了看到导数的效果,我们可以问自己以下问题:如果我们用某个小值 δW 改变神经网络的内部权重,总误差会变化多少?为了简单起见,将考虑δW=0.0001。实际上它应该小得多!。
当重量 W 变化很小时,让我们重新计算误差平方和:
+--------+----------+-------+-----------+------------+---------+
| Input | Output | W=3 | rmse(3) | W=3.0001 | rmse |
+--------+----------+-------+-----------+------------+---------+
| 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 2 | 3 | 1 | 3.0001 | 1.0002 |
| 2 | 4 | 6 | 4 | 6.0002 | 4.0008 |
| 3 | 6 | 9 | 9 | 9.0003 | 9.0018 |
| 4 | 8 | 12 | 16 | 12.0004 | 16.0032 |
| Total: | - | - | 30 | - | 30.006 |
+--------+----------+-------+-----------+------------+---------+
现在我们可以从这个表中看到,如果我们将 W 从 3 增加到 3.0001,误差的平方和将从 30 增加到 30.006。因为我们知道最适合这个模型的函数是 y=2.x ,将权重从 3 增加到 3.0001 显然会产生更多一点的误差(因为我们离直观的正确权重 2 更远了)。3.0001 > 3 > 2 因此误差更高)
但是我们真正关心的是误差相对于重量变化的变化率**。
基本上,这里的比率是重量每增加 0.0001,总误差增加 0.006->即比率为 0.006/0.0001 = 60x!
它在两个方向上都起作用,所以基本上如果我们将权重减少 0.0001,我们也应该能够将总误差减少 0.006!
这里是证明,如果你再次运行计算,在 W=2.9999 你会得到误差 29.994 。我们设法减少了总误差!**
我们可以通过直接计算损失函数的导数来猜测这个比率。使用数学导数的好处是计算起来更快更精确(浮点精度问题更少)。
这是我们损失函数的样子:
- 如果 w=2,我们的损失为 0,因为神经网络的实际输出将完全符合训练集。
- 如果 w <2, we have a positive loss function, but the derivative is negative, meaning that an increase of weight will decrease the loss function.
- At w=2, the loss is 0 and the derivative is 0, we reached a perfect model, nothing is needed.
- If w> 2,损失再次为正,但导数也为正,这意味着重量的任何增加都会使损失增加更多!!
如果我们随机初始化网络,我们会在这条曲线上放置任意一个随机点(假设 w=3 )。学习过程实际上是这样说的:
-让我们检查导数。
——如果是正数,意味着如果我们增加权重,误差会增加,那么我们应该减少权重。
-如果是负数,意味着如果我们增加权重,误差会减小,那么我们应该增加权重。如果是 0,我们什么都不做,我们到达我们的稳定点。
一个简单的问题是,我们正在设计一个类似重力的过程。无论我们在这个误差函数曲线上的什么地方随机初始化球,都有一种力场驱使球回到地面 0 的最低能级。
步骤 5-反向传播
在这个例子中,我们在神经网络的输入和输出之间只使用了一层。在许多情况下,为了达到神经网络功能的更多变化,需要更多的层。当然,我们总是可以创建一个复杂的函数来表示网络各层的组成。例如,如果第 1 层执行:3.x 以生成隐藏输出 z,而第 2 层执行:z 以生成最终输出,则组成的网络将执行(3.x) = 9.x 然而,在大多数情况下,组合函数是非常困难的。另外,对于每个组合,必须计算该组合的专用导数(这根本不可扩展,并且非常容易出错)。
为了解决这个问题,幸运的是,导数是可分解的,因此可以反向传播。
我们有误差的起点,即损失函数,我们知道如何对它求导,如果我们知道如何从合成中对每个函数求导,我们就可以将误差从终点传播回起点。让我们考虑一下简单的线性例子:我们将输入乘以 3 倍得到一个隐藏层,然后我们将隐藏层(中间层)乘以 2 倍得到输出。
输入-> 3.x -> 2.x ->输出。
输入上的 0.001δ变化将在第一层后转换为 0.003δ变化,然后转换为输出上的 0.006δ变化。
如果我们将两种功能合二为一,情况会是怎样:
输入-> 6.x ->输出。
类似地,输出端的误差 0.006 可以反向传播到中间隐藏级的误差 0.003,然后到输入端的误差 0.001。
如果我们创建一个可微分函数或层的库,其中对于每个函数,我们知道如何向前传播(通过直接应用函数)和如何向后传播(通过知道函数的导数),我们可以组成任何复杂的神经网络。我们只需要在正向传递期间保存一个函数调用及其参数的堆栈,以便知道使用这些函数的导数反向传递错误的方法。这可以通过函数调用来实现。这种技术被称为自动微分,只要求每个函数都有其导数的实现。在以后的博客文章中,我们将解释如何通过在矩阵上实现基本的数学运算来加速自动微分。
现在,任何层都可以将其结果转发给许多其他层,在这种情况下,为了进行反向传播,我们对来自所有目标层的增量求和。这样我们的计算栈就可以变成一个复杂的计算图。
该图显示了错误的反向传播过程,遵循以下模式:
输入- >前向调用- >损失函数- >导数- >错误的反向传播。在每一个阶段,我们得到这个阶段的权重的增量。
Diagram of Forward and Backward paths
Cool animation for the forward and backward paths
第 6 步-体重更新
如前所述,导数就是误差相对于重量变化的变化率。在前面给出的数字示例中,这个速率是 60x。这意味着 1 个单位的重量变化导致 60 个单位的误差变化。
由于我们知道误差目前为 30 个单位,通过外推比率,为了将误差减少到 0,我们需要将权重减少 0.5 个单位。
然而,对于现实生活中的问题,我们不应该以如此大的幅度更新权重。由于存在许多非线性,任何大的权重变化都会导致混乱的行为。我们不应该忘记,在我们计算导数的时候,导数只是局部的。
因此,权重更新的一般规则是 delta 规则:
新权重=旧权重-导数率*学习率
学习率作为一个常数(通常非常小)被引入,以便迫使权重非常平滑和缓慢地更新(以避免大步和混乱的行为)。
为了验证这个等式:
- 如果导数率为正,这意味着权重的增加将增加误差,因此新的权重应该更小。
- 如果导数率为负,这意味着增加权重会降低误差,因此我们需要增加权重。
- 如果导数为 0,说明我们处于稳定的最小值。因此,不需要更新权重->我们达到了稳定状态。
现在有几种权重更新方法。这些方法通常被称为优化器。德尔塔法则是最简单和直观的,但是它也有一些缺点。这篇出色的博文展示了更新权重的不同方法。
在这里给出的数值例子中,我们只有 5 个输入/输出训练集。实际上,我们可能有数百万个条目。之前,我们讨论了在整个数据集上最小化误差成本函数(损失函数)。这被称为批量学习,对于大数据来说可能非常慢。相反,我们可以做的是在每一次批量训练中更新权重,假设数据集被随机打乱。这被称为小批量梯度下降。如果 N=1 ,我们称这种情况为完全在线学习或随机梯度下降,因为我们在观察到每个输入输出后更新权重!
任何优化器都可以使用这三种模式(全在线/小批量/全批量)。
步骤 7-迭代直到收敛
因为我们一次用一个小的增量步长来更新权重,所以需要多次迭代来学习。这非常类似于遗传算法,在每一代之后,我们应用一个小的突变率,适者生存。
在神经网络中,每次迭代后,梯度下降力朝着越来越小的全局损失函数更新权重。
相似之处在于,delta 规则充当变异算子,损失函数充当适应度函数来最小化。
区别在于,在遗传算法中,变异是盲目的。有些突变是不好的,有些是好的,但是好的有更高的几率存活下来。然而,神经网络中的权重更新更智能,因为它们由误差上的递减梯度力来引导。
需要多少次迭代才能收敛?
- 这取决于我们应用的学习率有多强。高学习率意味着更快的学习,但是具有更高的不稳定性。
- 这也取决于网络的元参数(有多少层,非线性函数有多复杂)。变量越多,收敛时间越长,但能达到的精度越高。
- 这取决于优化方法的使用,一些权重更新规则被证明比其他规则更快。
- 这取决于网络的随机初始化。也许运气好的话,你会用W=1.99初始化网络,而你离最优解只有一步之遥。
- 这取决于训练集的质量。如果输入和输出彼此之间没有相关性,神经网络就不会变魔术,也无法学习随机相关性。
总体情况
总结一下,神经网络的学习过程是这样的:
Neural networks step-by-step
在我们的 GreyCat 中,一个全功能神经网络的不同构建模块在这里实现。
如果您还有任何问题,请不吝赐教或联系我:【assaad.moawad@datathings.com
如果你有任何想法,我会很乐意回复你,改进这篇文章,或者与你合作。
如果喜欢阅读,请关注我们: 脸书 , 推特 ,Linkedin
原载于 2018 年 2 月 1 日 medium.com。
神经网络和 3D 程序内容生成的未来
Light field landscape generated with the help of style transfer
作为全球制作机构 MediaMonks 的一名创意技术专家,人们总是问我关于人工智能、人工智能、神经网络等方面的问题。它们是什么?他们能做什么?我们如何使用它们?
这篇文章是我将写的探索人工智能、创造力和 3D 内容相遇的空间系列的第一篇。
在我看来,人工智能将是人类有史以来创造的最伟大的创造性工具。目前人工智能的愿景包括数百万失业工人,甚至是一场天启。但是,如果我们的未来涉及将我们的创造过程与人工智能交织在一起,而不是被它取代,那会怎样?
为了开始展望未来,我从一个简单的问题开始:我如何使用神经网络来帮助程序化地创建 3D 景观?例如,想象一下,你站在一个虚拟空间里,说“计算机,让这个空间看起来像吉卜力工作室的风景。”
Landscape from Howl’s Moving Castle
我们如何着手做这样的事情?由于我对机器学习相对较新,我的第一直觉是使用风格转移。
风格转移使用深度卷积神经网络的训练过滤器来优化两个输入图像之间的风格和内容损失,这两个输入图像是内容图像(您的自拍)和风格图像(梵高的画)。
我知道这听起来很专业,所以让我们这么说吧。深度卷积神经网络最近在图像分类方面非常成功。你给它一张照片,比如说一张狗的照片,神经网络就能分辨出这是一只狗。
机器理解图像内容的能力非常强大,因为这意味着它可以理解构成图像的所有细节。它知道狗有下垂的耳朵和尖尖的脸,而猫有尖尖的耳朵和扁平的脸。简单来说,风格转移网络所做的就是获取两幅图像之间的细节,并将它们组合起来。
那么,我们如何将它用于 3D 内容生成呢?3D 程序内容生成中的一个常见问题是地形生成。许多游戏将各种形式的噪波结合起来,以灰度高度图的形式创建山脉、丘陵和平原。大概是这样的:
Height map generated by noise.
然后这些高度图被用来移动平面中的顶点,从而创建山丘、山谷和山脉。这些在 3D 渲染时看起来相当不错,但它们比不上真实的东西。
Height map taken from actual elevation data of the earth.
即使我们可以用简单的旧数学方法生成好看而复杂的地形,但仍然很难模拟所有创造真实地形的过程,如板块构造和侵蚀。
但这正是神经网络的用武之地。有了风格转移的力量,就不用了。我们只需要创建我们想要的大致形状,然后神经网络会添加所有看起来真实的细节。
所以回到 Ghibli 用例,我必须找到真实世界的海拔数据,看起来像图像中的地形。对我来说,它看起来像在阿尔卑斯山的某个地方。通过谷歌图片搜索“阿尔卑斯山”,我看到了这张图片:
原来是 Val Gardena,意大利白云石山脉的一个山谷。知道了这些,我去了一个网站,在那里你可以下载地球的海拔数据。我搜索了 Val Gardena,下载了这张身高图:
也许将来你可以自动化这整个过程,但是现在手动在网上搜索就可以了。
现在是开始建造一切的时候了。使用 Unity,我编写了噪声着色器,它允许我实时调整噪声生成。经过大量实验后,我发现当生成的噪声与期望的输出有一些相似之处时,会出现最佳结果:
Procedural noise on the left, real terrain data on the left.
终于到了使用风格转移的时候了。经过大量的谷歌和 Youtube 搜索,我决定使用 Siraj Raval 的 Youtube 频道的视频中的实现。如果你想学机器学习,我怎么推荐他的频道都不为过!他杀了它!
下面是样式传递输出的样子:
Style transfer output on the left, real terrain on the right. Both are planes whose vertices are being displaced by the height map texture.
很酷吧?可能有成百上千的事情可以优化它,但对我来说已经足够好了。现在是时候将一个虚拟相机放到生成的地形中,并使用原始的 Ghibli 图像作为样式图像,相机渲染作为内容图像。就像噪声一样,我认为如果内容图像与样式图像有相似之处会更好。因此,我在 Blender 中导入了高度图,以便做一些非常简单的纹理绘制。
The height map in blender. Full height map terrain on left, and zoomed in height map terrain on right.
我选择了一个类似吉卜力图像的相机位置和角度:
A simple Blender render.
之后我画了一些非常简单的纹理,给它一个天空的颜色,并为花添加了一个简单的粒子系统。
An obviously very quick and dirty render.
这就是酷的地方。是时候通过风格转换运行简单的 Blender render 了,看看结果如何。在花了很多时间调整超级参数后,我得到了这个:
The final output of the whole process.
这是与输入图像的最终对比:
显然远不及吉卜力工作室的原声大师,但对于一台无脑机器来说也不错。
但是从这里去哪里呢?理想情况下,你会希望实时完成整个过程,但即使我的虚拟现实电脑上有 GPU 支持的神经网络,风格转换过程也需要大约 4 分钟。显然,这对于实时 3D 图形来说还远远不够快。你可以尝试使用快速风格转换,它可以在不到一秒的时间内输出图像,但我不喜欢它在我的图像中产生的伪像。那么,我们如何利用这张静止图像,制作出 3D 的实时渲染的东西呢?
答案是光场!
光场早在 1996 年就被演示过了!但随着虚拟现实的兴起,它们越来越受欢迎,并被像 OTOY 这样的公司推广。光场只是一个花哨的术语,指的是由一系列相机拍摄的一系列图像。它们看起来像这样:
该阵列捕获给定体积内的每条光线,从而能够合成新的摄像机角度。看原视频这里。这些对于实时图形非常有用,因为你可以预先渲染一切。这样你可以获得实时图形的自由,但具有预渲染场景的图像质量。
现在是时候创建我自己的风格传递光场和光场渲染器了。我基本上在 Unity 中重新实现了 Andrew Lowndes 的 WebGl 光场渲染器。
通过用 python 编写一些 blender 脚本,我能够输出一个 8×8 的图像网格,这些图像分别通过样式传输网络发送。然后它们被缝合在一起成为一个统一的图像。下面是生成的样式传递光场:
Downsized because the original is 8192 × 8192!
这里有一个光场渲染器的视频!
正如你所看到的,通过完全预渲染多个相机视图,我们可以创建一个光场渲染器,它提供了传统渲染系统的许多功能。但它也允许我们现在使用生成神经网络来创建 3D 内容!
但是这篇文章的标题是“神经网络和 3D 程序内容生成的未来”这真的是未来程序化内容的创作方式吗?就这样吗?大概不会。有很多优化工作要做,也许其他的生成算法,比如 GANN 的算法,会更适合这种类型的任务。
这篇文章展示的是神经网络可以从根本上改变我们生成 3D 内容的方式。我选择了光场,因为目前我的 GPU 速度不够快,无法以 60 FPS 的速度进行风格转换或任何其他生成网络。但是如果我们真的到了那一步,完全有可能看到生成神经网络成为标准光栅化方法的替代渲染管线。通过这种方式,神经网络可以根据用户的实时反馈实时生成游戏的每一帧。
但是对于创作者和最终用户来说,它也潜在地允许更强大的创作方法。想象一下玩《战争机器》,然后告诉电脑“保留游戏性、故事和 3d 模型,但让它看起来像塞尔达:野性的呼吸。”这就是创造或玩未来游戏体验的方式,因为计算机现在知道事物“看起来”是什么样子,并且可以让其他事物也“看起来”像它们。
就像我之前说的,计算机理解图像的能力,虽然可能没有《她》中的萨曼莎或《2001》中的哈尔那么令人印象深刻,但仍然是一件非常强大的事情。这是最近的一项创新,仍然有许多可能性有待发现!
神经网络=黑盒?
如你所知,我是一个机器学习爱好者。具体地说,是神经网络。
在过去的几个月里,我读了很多关于 NNs 的文章。我得出了一个结论:
没有一个真正让你看到 NNs 是怎么学习的。
示例#1
福布斯,解释:神经网络
在这篇文章中,作者说:
“训练数据被输入到底层,即输入层,然后通过后续层,以复杂的方式相乘并相加,直到最终到达输出层,进行彻底的转换。
哇,情结确实帮助我理解了 NNs 是如何学习的…然后:
在训练期间,不断调整权重和阈值,直到具有相同标签的训练数据始终产生相似的输出。"
我有这个想法…模糊地。
实施例 2
哈佛商业评论,深度学习将从根本上…
“然而,要使神经网络有用,它需要训练。为了训练神经网络,一组虚拟神经元被绘制出来,并被分配一个随机的数字“权重”,该权重决定神经元如何对新数据(数字化的对象或声音)做出反应。
嗯,我想这是有道理的。
“最终,经过充分的训练,神经网络将始终如一地识别语音或图像中的正确模式。”
哦,再一次,它关上了正确解释神经网络如何学习的大门…
实施例 3
NYT ,伟大的人工智能觉醒
“训练是一个过程,通过这个过程,在 blob 中挖掘出一系列错综复杂的隧道,这些隧道将任何给定的输入连接到其正确的输出。”
我想我不喜欢隐喻数学方法。在这种情况下,一个 NN。
“他们*【权重】*必须独立证明他们是否也擅长挑选狗和除颤器,(…)
对(?).
(…)但让神经网络如此灵活的一点是,每个单独的单元都可以对不同的预期结果做出不同的贡献。"
尽管如此,我还是没有亲眼看到真正的学习。总之。
我的观点是:
了解机器如何学习。
真的!
神经网络训练(涉及梯度下降、反向传播等方法)很有意思。这也有点不可思议,因为数字开始自我调整,试图找到最大预测的最佳路径。
我知道这些文章并不是要向你展示这一点,因为它们的目标受众(这显然不是技术或教育)。
但我想,即便如此,我还是想看看里面的一些东西。
神经网络:误差预测层
早在 2005 年,杰夫·霍金斯就写过“论智力”——关于人类神经科学中的一个奇特发现,尚未被深度学习利用。它值得仔细看看。
人类、海豚和猴子拥有不同于其他生物的大脑:在我们的额叶,我们有许多相邻的多种类型神经元的堆叠——就像一张覆盖着盘子的早餐桌上,每个盘子上都有自己的一堆煎饼,点缀着配料。这些神经元以一种特殊的方式发挥作用:
- 最下面的层被赋予预测下一时刻的任务;每当它知道未来时,多巴胺就会激增,并加强它的联系。
- 下一个层的任务是预测下层何时出错;每当它知道下层的未来时,它就会感到兴奋,并加强它的学习。
- 进一步的层也被赋予预测其下层何时出错的任务;他们学会成为错误检测者。
人类在我们的“煎饼堆”中有六层,我们有一个有数百万个盘子的“早餐桌”,以形成我们更高层次的推理和自我反思的智能。这个模型与今天的人工神经网络完全不同。
但是,深度神经网络工作得很好……
是的,他们有。甘斯和 LSTM 网络公司也是如此。这些方法很快就找到了将数据压缩成特征的好方法。然而,我们的大脑做的不止这些。
让我们比较一下:一个循环神经网络,和人脑。rnn 接收环境的当前状态(其“输入”是屏幕上的像素)并在该环境中产生一个动作(“输出”在可能动作的空间中,而“输入”在可能像素排列的空间*)。然而,人类的额叶有一个最底层的“煎饼”,它取系统的当前状态(即“输入”),并试图预测系统的未来状态(“输出”是与“输入”在同一空间上的*,损失函数是两个之差)。人脑不会像 RNN 那样试图“选择游戏中的最佳走法”!我们实际上是在试图预测接下来会发生什么。**
此外,一个 RNN 可能有许多“层”神经元,但它只代表单一“层”的功能*。像素输入→动作输出。我们自己的“煎饼”栈实际上就像多个神经网络的栈一样运行。每个“煎饼”接受状态输入→状态预测输出。更高层的“煎饼”起着错误检测器的作用,因为它们观察到的状态是它们更低层的基本事实与其预测之间的差异的映射。较高层只看到较低层做出错误预测 的像素。*
如果我们希望神经网络的行为像额叶一样,它需要映射像素输入→像素预测输出,然后将该输出与随后时刻的地面真实像素进行比较,以查看哪里的预测是错误的*。那将是最低级的“煎饼”。然后,被误预测的像素将成为下一个“煎饼”的输入,并且该“煎饼”试图预测下一个时刻的误差将在的哪里。第二个“煎饼”将是一个新的神经网络。*
我们将需要六个这样的神经网络,一个堆叠在另一个之上——让事情变得更复杂的是,更高层的“煎饼”也将接收来自多个特征检测器神经元的信号。我们的第六个“煎饼”将接收第五个“煎饼”的误差作为输入,以及来自第一个、第二个、第三个和第四个煎饼的一些特征检测器信号!这与现有的人工神经网络不同,我认为这种差异很重要。**
有什么帮助?
目前,研究人员期望单个神经网络每次都能正确处理问题。大脑不是这样工作的。在我们六个‘煎饼’中,第一个‘煎饼’神经网络出错往往*。如果我们将一个人工神经网络训练成最低的“煎饼”,我们将需要尽早降低学习速度,并在过度拟合之前停止。我们的网络仍然会得到许多错误的答案。*
然后,我们需要第二个“煎饼”,第二个神经网络,接收下层的错误。那一层也会很早就停止。很可能仍然会错误地判断最低层何时会出错。只有当许多这样的“煎饼”堆叠在一起时,错误率才会显著下降。**
对于注重数字的人来说:当前的 NLP 网络在大约 4%的时间里是错误的。同时,假设一个有六个“煎饼”的“额叶”有一个最低的“煎饼”有 50%的时间是错误的。就其本身而言,这个“煎饼”比我们目前的网络要糟糕得多。然而,它的错误实例被传递给第二个“煎饼”。这个煎饼只寻找 50%的错误,我们可以假设它纠正了 50%的错误。到目前为止,这两个“煎饼”加在一起,有 75%的正确率。有了这些“煎饼”中的六个“煎饼”,每个“煎饼”只纠正一半剩余的错误,组合的准确度是 98.4375%!因此,即使每个错误检测器都“有故障”,错误检测器的堆栈也能很快胜过端到端网络。
通过种植煎饼预测误差
人类有更强的理性和反思能力,我们的盘子里也有更多的“煎饼”!海豚有四个,猿和猴子更少。我预计,如果一台机器有比我们更多的【煎饼】,每个“煎饼”都试图预测它下面的“煎饼”的误差,那么这台机器将会比我们更有能力。这为机器智能开辟了一个新的方向,在前进中学习。**
边学边做:机器将从一个单一的深度神经网络开始,并被赋予预测下一时刻的任务。当它的成功率超过某个阈值时,在顶部添加一个新的深度神经网络。这个新网络将接收下层网络的错误预测,并负责预测下一个错误将发生在哪里。当网络的成功率超过阈值时,添加一个新的深度神经网络。继续这个过程,以逐步改善网络的组合。
通过这种“煎饼”范式,网络通过在所有旧网络之上生长另一个深度神经网络“煎饼”*来响应新信息**。学习永远不会停止。“煎饼”堆得越来越高。当与专家混合的各种神经网络相结合时,这个概念变得更加重要。***
专家混合
在混合专家神经网络中,神经元像树莓一样“聚集”成密集连接的束。而且,就像树莓酱一样,有一些长距离的连接将所有的树莓“粘合”在一起。目前,专家模型混合就此打住。当一个输入进入果酱堆的底部时,它只激活几个“树莓”,每个“树莓”执行一点点特征检测。这些特征激活了果酱堆中较高的一些“覆盆子”,在那里检测到更高水平的特征。
沿着果酱堆向上移动,这些专家覆盆子能够发现不同输入的特征;对于输入的每个子集,不同的一组专家开始工作。专家混合网络的行为就像许多稀疏网络的联合,每个覆盆子都是其中一些网络的 T2 交集。
(在稀疏网络的拥挤维恩图中,每个网络都有特征检测器,这些特征检测器与任何其他网络有一点点重叠;因为有如此多的网络在一起,组合图允许大多数稀疏网络与其他人共享大多数特征检测器。例如,稀疏网络#1 可能利用特征检测器“树莓”A、B 和 C。同时,稀疏网络#2 使用 A、D 和 e。网络#3 需要检测特征 C、D 和 e 的“树莓”。因此,您可以将这三个稀疏网络组合成检测所有特征的专家混合物:A、B、C、D 和 e。每个稀疏网络仅在少数地方与其他稀疏网络重叠。但是,综合起来看,所有的专家都有重叠之处。)
所有浇头
回到构成我们额叶的“煎饼”。为了与我们自己的大脑相匹配,煎饼的比喻需要更加精细:我们的神经元显示出从一个盘子到另一个盘子的跨越堆叠的链接。这就像一个覆盖着盘子的早餐桌,每个盘子上堆放着六个“煎饼”……并且覆盆子果酱涂抹在所有的盘子上,沿着盘子的侧面滴落,并且每个盘子都接触到其他的盘子!我们的大脑很混乱。
如果我们想要一个像我们一样学习和成长的人工神经网络,它将需要多个错误预测深度神经网络的“煎饼”,其中每个深度神经网络都由专家的分层混合物组成。每个“煎饼”网络接收其下方“煎饼”的错误**、以及由相邻“煎饼”检测到的一些特征作为输入。这与当前的深度神经网络架构大相径庭。而且,值得一试。**
音乐神经网络:历史之旅
从刘易斯和托德在 80 年代写的开创性论文到当前 GANs 作曲家的浪潮之间发生了许多事情。在这段旅程中,连接主义者的工作在人工智能冬天被遗忘,非常有影响力的名字(如 Schmidhuber 或 ng)贡献了开创性的出版物,与此同时,研究人员取得了大量令人敬畏的进展。
我们不会浏览音乐神经网络领域的每一篇论文,也不会钻研技术细节,但我们会涵盖我们认为有助于塑造音乐人工智能当前状态的里程碑——这是一个很好的借口,可以表扬这些决定关注一个除了酷以外什么也不是的信号的疯狂研究人员。开始吧!
首字母缩略词
AI —人工智能
CNN —卷积神经网络
GAN —生成对抗网络
LSTM —长时短时记忆(一种递归神经网络)
—MIDI—乐器数字接口(一种类似乐谱的符号化音乐表示)
MLP —多层感知器
连接主义者对算法组合感兴趣
几百万年前,在一颗大的小行星撞击地球后,一个漫长的冬天开始了。在这场灾难中,地球上的物种突然大量灭绝。
足够幸运的是,在人工智能的冬天,应用于音乐的神经网络有着不同的信念。这一时期导致了一系列关于算法组合的虚假工作,这些工作维持了该领域从 1988 年到 2009 年的相关性。这就是所谓的连接主义者对神经网络和机器学习的贡献。
然而,这些早期作品对大多数当代研究者来说几乎是未知的。
第一波工作是由刘易斯和托德在 1988 年发起的,他们提出了使用神经网络进行自动音乐创作。
一方面,刘易斯将多层感知器用于他的算法作曲方法,称为“精炼创造”。这在本质上是基于和 DeepDream 相同的想法:利用渐变来创造艺术。
另一方面,托德使用约旦自动回归神经网络(RNN)来顺序生成音乐——这一原则在这么多年后仍然有效。这些年来,许多人一直在使用同样的想法,其中包括:Eck 和 Schmidhuber,他们提出将 LSTMs 用于算法合成。或者,考虑一个更近的工作, Wavenet 模型(它“能够”生成音乐)也利用了同样的因果原理。
看看 Todd 和 Lewis 在 80 年代为算法合成引入的旧的连接主义思想今天仍然有效。但是,如果他们的原则是正确的,为什么他们没有成功?嗯,用刘易斯的话来说:“很难计算很多东西。”一个深度学习工作站中的一个现代 GPU 可能具有大约 110 tflops 的理论性能,而一个 VAX-11/780 (他在 1988 年用于工作的工作站)只有 0.1 mflops。
但是让我们回过头来讨论一下 Eck 和 Schmidhuber 的工作。在他们的论文寻找音乐中的时间结构:LSTM 的布鲁斯即兴创作中,他们试图解决算法作曲曾经(现在仍然)面临的一个主要问题:缺乏整体一致性或结构。
为了应对这一挑战,他们提出了使用 lst ms——据说它比香草-RNNs 更好地学习更长的时间依赖性。注意,作为这个实验的结果,音乐是 LSTMs 的早期应用之一!
LSTM 创作的音乐听起来怎么样?它能产生一种结构合理的蓝调吗?自己判断!
让我们处理底层数据!
在 2009 年之前(请记住,直到 2006 年,辛顿和他的同事们还没有找到一种用深度信念网络训练深度神经网络的系统方法),大多数作品都是在解决算法音乐创作的问题。他们大多试图通过无线网络来做到这一点。
但是有一个例外。
早在 2002 年,Marolt 和他的同事们使用了一种多层感知器(在光谱图之上操作!)用于音符开始检测的任务。这是第一次有人以非符号的形式处理音乐。这开启了一个新的研究时代:一个种族开始第一个以端到端的学习方式解决任何任务。这意味着学习一个映射系统(或函数),能够直接从原始音频中解决一个任务,而不是使用工程功能(如频谱图)或符号音乐表示(如 MIDI 乐谱)来解决它。
2009 年,人工智能冬天结束了,第一批深度学习作品开始影响音乐和音频人工智能领域。
人们开始用深度学习分类器处理更复杂的问题(如音乐音频标记或和弦识别)。
根据 Hinton 基于用深度信念网络预训练深度神经网络的方法,Lee 和他的同事(其中包括吴恩达)建立了第一个用于音乐流派分类的深度卷积神经网络。这是一项基础性工作,为一代深度学习研究人员奠定了基础,他们花了很大精力设计更好的模型,以从音乐频谱图中识别高级(语义)概念。
然而,并不是每个人都满意使用基于声谱图的模型。大约在 2014 年,Dieleman 和他的同事们开始探索一个雄心勃勃的研究方向,这个方向向世界展示了音乐音频的端到端学习。在这项工作中,他们探索了直接处理波形以完成音乐音频标记任务的想法——这取得了一定程度的成功,因为基于频谱图的模型仍然优于基于波形的模型。当时,不仅模型不够成熟,而且与现在一些公司可以获得的大量数据相比,训练数据也很匮乏。例如,最近在 Pandora Radio 进行的一项研究表明,如果有足够的训练数据,基于波形的模型可以优于基于频谱图的模型。
另一项具有历史意义的工作来自 Humphrey 和 Bello (2012 年),他们当时提出使用深度神经网络进行和弦识别。他们说服乐存与人合著了《深度学习音乐宣言》——实际(略有不同)标题见参考资料!在这篇文章中,他们向音乐技术研究人员解释说,从数据中学习(分层)表示并不是一个坏主意——有趣的是,他们认为社区已经在利用深层(分层)表示了!
因此…下一步是什么?
概括地说,这个领域可以分为两个主要的研究领域:音乐信息检索,旨在设计能够识别音乐信号中存在的语义的模型;和算法作曲,目标是通过计算生成新的吸引人的音乐作品。
这两个领域目前都在蓬勃发展,研究团体稳步前进!
例如,在音乐信息检索领域:尽管目前的深度神经网络已经取得了相当大的成功,但最近的工作仍然在通过改进定义这些模型的架构来推动可能的边界。
但是实际的研究人员不仅仅打算改进这种模型的性能。他们也在研究如何增加它的可解释性,或者如何减少它的计算量。
此外,如前所述,人们对设计能够直接处理各种任务波形的架构非常感兴趣。然而,研究人员尚未成功设计出一种通用策略,使基于波形的模型能够解决广泛的问题——这将允许端到端分类器的广泛适用性。
另一组研究人员也在探索科学的边缘,以改进算法合成方法。记得在 80 年代(Todd 和 Lewis)和 21 世纪初(Eck 和 Schmidhuber),使用了相当简单的自回归神经网络。但现在是现代生成模型的时候了,比如 GANs(生成对抗网络)或 VAEs(可变自动编码器)。
足够有趣的是:这些现代生成模型不仅被用于以符号格式创作新颖的乐谱,而且像 WaveGAN 或 Wavenet 这样的模型可以成为探索新颖的鼓空间或直接在波形域中呈现新歌的工具(与创作新颖的 MIDI 乐谱相反)。
神经网络现在是使能工具(和新颖的方法!)这些都是以前达不到的。像音乐源分离或音乐转录(被认为是音乐技术专家的圣杯)这样的任务现在从深度学习的角度重新审视。现在是时候重新定义什么是可能的,什么是不可能的,简单地将音乐神经网络领域分为两个领域是过于短视的。新一代研究人员目前正在寻找创新的方法来将这些片段组合在一起,尝试新的任务,并将神经网络用作创造力的工具——这可以导致人类与音乐互动的新方法。
你想成为塑造未来的人之一吗?
参考文献
如果你不是一个有上进心的学者
跳过这一节这篇文章是基于我几个月前准备的教程演示文稿。
刘易斯和托德 80 年代的论文:
- Todd,1988 —“音乐应用的顺序网络设计”发表在《联结主义模型暑期学校学报》上。
- Lewis,1988—“精化创造:梯度下降学习网络的创造性范式”国际神经网络会议。
第一次有人用 LSTMs 做音乐:
- Eck & Schmidhuber,2002—寻找音乐中的时间结构:用 LSTM 递归网络进行蓝调即兴创作,IEEE 信号处理神经网络研讨会*。*
第一次有人用神经网络处理光谱图:
- Marolt 等,2002 —“用于钢琴音乐中音符开始检测的神经网络”国际计算机音乐会议(ICMC) 。
第一次有人用神经网络构建了一个音乐流派分类器——基于 Hinton 的深度信念网络进行无监督预训练:
- Lee 等人,2009—“使用卷积深度信念网络的无监督特征学习音频分类”《神经信息处理系统(NIPS)进展》。
- Hinton 等人,2006—“一种深度信念网的快速学习算法”神经计算,18(7),1527–1554。
第一次有人建立了端到端的音乐分类器:
- Dieleman & Schrauwen,2014 年。音乐音频的端到端学习IEEE 国际声学、语音和信号处理会议(ICASSP) 。
最近在 Pandora Radio 进行的研究显示了大规模端到端学习的潜力:
- Pons 等人,2018—“音乐音频标注规模的端到端学习”在国际音乐信息检索学会会议(ISMIR) 。
Humphrey 和 Bello (2012)在和弦识别方面做了一些工作,并撰写了《音乐深度学习宣言:
- Humphrey & Bello,2012—“用卷积神经网络重新思考自动和弦识别”在国际机器学习与应用会议(ICMLA) 上。
- Humphrey 等人,2012 年。超越特征设计:音乐信息学中的深度架构和自动特征学习国际音乐信息检索学会会议(ISMIR) 。
要了解有关如何改进当前架构的更多讨论,请访问:
- Choi 等人,2016 年。使用深度卷积神经网络的自动标记在国际音乐信息检索学会会议(ISMIR) 上。
- 庞斯等人,2016 年。"音乐激励的卷积神经网络实验 " 基于内容的多媒体索引国际研讨会(CBMI) 。
- Lee 等人,2017 年。利用原始波形进行音乐自动标注的样本级深度卷积神经网络国际声音与音乐计算大会(SMC) 。
算法组合的一些现代生成模型(基本上是 GANs 和 VAEs):
- 杨等,2017—“midit:一种用于符号域音乐生成的卷积生成对抗网络”国际音乐信息检索学会会议(ISMIR) 。
- Roberts 等人,2018—“学习音乐中长时结构的分层潜向量模型”arXiv。
还有一些直接合成音乐音频的作品(waveGAN 和 Wavenet,基本都是):
- Donahue 等人,2018—ICLR 研讨会中的“用生成对抗网络合成音频”。
- Van Den Oord 等人,2016—“wave net:原始音频的生成模型”arXiv。
- Dieleman 等人,2018—“现实音乐生成的挑战:模拟大规模原始音频”arXiv。
- Engel 等人,2017 —“用 Wavenet 自动编码器进行音符的神经音频合成”在国际机器学习会议(ICML) 。
鸣谢
与Exxact(@ Exxact corp)合作撰写的帖子。非常感谢 JP Lewis 和 Peter M. Todd 回复电子邮件,感谢 Yann Bayle 维护这份(字面上的)棒极了的应用于音乐的深度学习论文列表。
神经网络:形成类比
我们的大脑具有人工神经网络仍然缺乏的能力:我们可以形成类比,将不同的输入联系起来,并使用相同的启发式方法处理它们。官方的行话是“迁移学习”。从根本上说,类比是压缩的一种形式,允许大脑以最小的空间模拟许多不同系统的动态。当两个系统行为相似时,可以用一个简单的类比来描述它们。
学习新东西
人工神经网络现在可以避免灾难性遗忘,这是一个主要的绊脚石。以前,当一个神经网络被训练完成一项新任务时,要么是太有可塑性,学习新任务的同时忘记了旧任务,要么是太死板,记住了第一个任务,却从未学习第二个任务。这一进步是向迁移学习迈出的重要一步,然而它只是将新任务划分到网络中不太有用的区域——它没有使用共享试探法将不同的任务组合起来。
我认为,为了形成类比并压缩用于许多类似任务的空间,神经网络必须将时间投入到一个独特的状态:假设类比。当假设类比时,神经网络必须忽略新的感觉输入,同时比较来自不同专家的输出。这样的网络有三种状态:接收和处理输入,反向传播突触权重的更新,以及假设类比。网络在所有三种状态之间交替;正如它在前馈和反馈之间交替一样,如果它希望发现不同子系统之间的联系,它必须花时间“思考”。
为专家提供制图投入
为了了解类比形成是如何工作的,想象一下所有可能输入的空间。在这个高维空间中,每个点对应不同的输入。混合专家神经网络的任务是将这些输入映射到各种专家聚类上。假设棋盘是输入的空间,每个专家占据棋盘上的一个样本,这样棋盘上的每个方格都与一个特定的样本相关联。神经网络接收输入,检查哪个专家在那里应用,并将输入数据发送给该专家进行处理。从输入到专家的映射是对这些输入的解析。
当网络假设一个类比时,它接受从输入空间到专家空间的新的映射为正确的。通常接收{X,Y,Z}作为输入的专家可能改为接收{A,B,C},以查看新的分配是否产生正确的预测。测试每个可能的重新分配的过程是假设状态;网络花在“思考”上的时间越多,就越有可能找到准确模拟不同子系统的现有专家群。当这种“思考”发生时,感觉输入被搁置,反向传播没有发生——网络“陷入思考”。
增长理解力
首先,一个不熟悉的输入被赋予它自己的专家聚类——它被视为全新的,处理起来效率低下且缓慢。然而,随着更多的输入出现在输入空间的该区域中,由于反向传播,其专家聚类变得更加准确。一旦新任务的专家足够准确,神经网络就可以开始思考——它模拟输入,并查看其现有专家集群的答案是否与类似集群的答案匹配。如果它们足够相似,那么类比是有效的,并且笨拙的新专家群被更成熟的模块所取代。(注意,网络是而不是将类似的专家集群与现实进行比较;它是相对于特定任务的专家来说的!比较的是想象的。)
这种行为类似于我们在大脑中看到的——当学习一项新任务时,我们很笨拙,容易分心。然而,一旦我们消化了新的任务,我们就会变得流畅和敏感。这种消化的发生是因为对任务的反思,以及将该任务与更熟悉的任务联系起来的过程。我们必须思考以找到有助于理解的联系和相似之处。寻找这些类比是一项非常有价值的工作,我们花了很多时间思考假设的类比,我预计高级人工神经网络也需要花很多时间思考。
好奇心
另一组研究人员已经在雅达利游戏的人工智能方面取得了很大进展,完全依靠好奇心。这台机器不知道奖励和死亡;它只是试图用一种新的、意想不到的事情发生的方式来玩。这可能非常像新大脑皮层,它不断地试图预测未来,并非常注意其预测出错的时间和方式。新大脑皮层不太受不常见的愉悦信号的驱动。相反,大脑皮层渴望理解。
然而,还有另一种形式的好奇心。
假设你的神经网络形成了一个类比,X →A,Y →B,Z →C,它想看看这个类比是否成立。两个专家系统,一个在数据{A,B,C}上被明确训练,另一个在{X,Y,Z}上被训练并且被假定为类似的,具有可以被模拟以比较那些专家群的边缘情况。
测试这些边缘情况是一种不同的好奇心——神经网络不会搜索新的输入。相反,它怀疑通过系统地寻找矛盾来进行类比是否恰当。“边缘案例”测试在发现任务的潜在动力方面可能超过“新经验”的好奇心;新体验的好奇心会落入陷阱,就像不断地换一个电视频道。新体验的好奇心是一台分心机器。相比之下,测试边缘案例更类似于科学家的工作,系统地假设,然后在这些假设的边界上进行实验。
需要时间
因此,神经网络使其感觉静音,停止反向传播,并开始思考假设的类比。不幸的是,没有找到可靠类比的硬性快速规则。我们自己的大脑似乎使用稀疏分布表示来编码信息,如果两种表示在许多方面重叠,它们可能会受到相同的动力学影响。所以,我们有一个粗略的相似性度量,给我们一个好的开始。然而,思考仍然缓慢。
对于每一个假设的类比,必须想象许多边缘情况。每个实例都呈现给特定任务专家和类似专家集群。如果许多想象的实例产生匹配的输出,那么类似的专家是一个很好的选择,并取代特定任务的专家。这可能很少见。因此,必须尝试许多假设,每个假设都需要多个想象的实例来验证。这可能是神经网络工作的很大一部分!我们自己对睡眠的需求,以及睡眠在消化新概念中的作用,似乎反映了这种行为。我们梦想在子系统之间形成新的连接,当我们的传感器静音,行动只是想象。
贝叶斯视角下的神经网络
无论从从业者的角度还是对于许多不同机器学习应用程序的最终用户来说,理解模型不知道什么都是很重要的。在我们之前的博客文章中,我们讨论了不同类型的不确定性。我们解释了如何使用它来解释和调试我们的模型。
在这篇文章中,我们将讨论在深度神经网络中获得不确定性的不同方法。让我们从贝叶斯的角度来看神经网络。
贝叶斯学习 101
贝叶斯统计允许我们根据证据(数据)和我们对世界的先验知识得出结论。这常常与只考虑证据的频率主义统计形成对比。先验知识捕获了我们对哪个模型生成了数据的信念,或者那个模型的权重是什么。我们可以使用模型权重的先验分布 p(w)来表示这种信念。
随着我们收集更多的数据,我们更新先验分布,并使用贝叶斯法则将其转化为后验分布,这一过程称为贝叶斯更新:
这个等式引入了贝叶斯学习中的另一个关键角色——可能性*,定义为 p(y|x,w)。这一项表示在给定模型权重 w 的情况下,数据的可能性有多大。*
贝叶斯视角下的神经网络
神经网络的目标是估计可能性 p(y|x,w)。即使你没有明确地这样做,也是如此,例如当你最小化 MSE 时。
为了找到最佳的模型权重,我们可以使用最大似然估计* (MLE)😗
或者,我们可以使用我们的先验知识,表示为权重的先验分布,并最大化后验分布。这种方法被称为最大先验估计* (MAP) :*
代表我们的先验的项 logP(w) 充当正则化项。选择均值为 0 的高斯分布作为先验,你将得到 L2 正则化的数学等价。
既然我们开始将神经网络视为概率性生物,我们就可以开始享受乐趣了。首先,谁说我们必须在训练过程结束时输出一组权重?如果我们不学习模型的权重,而是学习权重的分布,会怎么样?这将允许我们估计权重的不确定性。那么我们该怎么做呢?
一旦你去贝叶斯,你永远不会回来
我们再次从权重的先验分布开始,目的是找到它们的后验分布。这一次,我们将对所有可能的权重进行平均,而不是直接优化网络的权重(称为边缘化)。
在推断时,我们不是采用最大化后验分布(或最大似然法,如果我们使用最大似然法)的单一权重集,而是考虑所有可能的权重,按其概率进行加权。这通过使用积分来实现:
x 是我们要推断 y 的数据点, X , Y 是训练数据。第一项 p(y|x,w)是我们良好的旧可能性,第二项 p(w|X,Y)是给定数据时模型权重的后验概率。
我们可以把它想象成一个由每个模型的概率加权的模型集合。事实上,这相当于无限数量的神经网络的集合,具有相同的架构,但具有不同的权重。
我们到了吗?
啊,问题就在这里!事实证明,这个积分在大多数情况下是难以处理的。这是因为后验概率无法通过分析来评估。
这个问题不是贝叶斯神经网络独有的。在贝叶斯学习的许多情况下,你会遇到这个问题,多年来已经开发了许多克服这个问题的方法。我们可以把这些方法分为两类:变分推断法和抽样法。
蒙特卡罗抽样
我们有麻烦了。后验分布是难以处理的。如果我们不用计算真实分布的积分,而是用从中抽取的样本的平均值来逼近它,会怎么样?一种方法是马尔可夫链蒙特卡罗——你用期望的分布作为它的平衡分布来构造一个马尔可夫链。
变分推理
另一种解决方案是用来自易处理族的不同分布来近似真实的难处理分布。为了测量两个分布的相似性,我们可以使用 KL 散度:
设 q 是由θ参数化的变分分布。我们希望找到最小化 KL 散度的θ值:
看看我们得到的:第一项是变分分布和先验分布之间的 KL 散度。第二项是关于 q θ的可能性。因此,我们在寻找能够最好地解释数据的 q θ,但另一方面,它又尽可能接近先验分布。这只是将正则化引入神经网络的另一种方式!
现在我们有了 qθ,我们可以用它来做预测:
以上提法来自 deep mind2015 年的一部作品。类似的想法由格雷夫斯于 2011 年提出,并追溯到辛顿和范坎普于 1993 年提出。NIPS 贝叶斯深度学习研讨会上的主题演讲非常好地概述了这些想法多年来是如何演变的。
好吧,但是如果我们不想从零开始训练一个模型呢?如果我们有一个训练好的模型,我们想从这个模型中得到不确定性估计,该怎么办?我们能做到吗?
事实证明,如果我们在训练中使用辍学,我们实际上可以。
Professional data scientists contemplating the uncertainty of their model — an illustration
辍学是一种不确定性
辍学是一种经常使用的做法。在训练时,你随机抽取节点样本,并删除它们,也就是说,将它们的输出设置为 0。动机呢?您不希望过度依赖特定的节点,这可能意味着过度适应。
在 2016 年, Gal 和 Ghahramani 表明,如果你也在推理时应用 dropout,你可以很容易地得到一个不确定性估计量:
- 多次推断 y|x,每次采样一组不同的节点以排除。
- 对预测值进行平均,得到最终的预测值 E(y|x)。
- 计算预测的样本方差。
就是这样!你得到了方差的估计!这种方法背后的直觉是,训练过程可以被认为是同时训练 2^m 不同的模型——其中 m 是网络中的节点数量:每个没有被遗漏的节点子集定义了一个新的模型。所有模型都共享它们没有丢失的节点的权重。在每一批中,这些模型的随机抽样集被训练。
训练结束后,你手中就有了一套模型。如果你如上所述在推理时使用这个集合,你就得到这个集合的不确定性。
抽样方法与变分推断
就偏差-方差权衡而言,变分推断有很高的偏差,因为我们选择了分布族。这是我们正在做的一个强有力的假设,和任何强有力的假设一样,它会引入偏见。然而,它很稳定,方差很小。
另一方面,抽样方法具有较低的偏差,因为我们不对分布进行假设。这是以高方差为代价的,因为结果取决于我们抽取的样本。
最后的想法
能够估计模型的不确定性是一个热门话题。在医疗辅助和自动驾驶汽车等高风险应用中,了解这一点非常重要。这也是一个很有价值的工具,可以了解哪些数据有利于模型,因此我们可以去获取它。
在这篇文章中,我们介绍了一些获得模型不确定性估计的方法。还有更多的方法,所以如果你对此感到非常不确定,继续寻找更多的数据🙂
在下一篇文章中,我们将向您展示如何在推荐系统中使用不确定性,特别是如何应对探索-开发挑战。敬请关注。
这是与我们在今年 KDD 会议的研讨会上提交的论文相关的系列文章的第二篇: 深度密度网络和推荐系统中的不确定性 。
第一篇帖子可以在 这里找到 。
这是一个与 Inbar Naor 的联合帖子。最初发表于engineering.taboola.com
神经网络从无到有。容易还是困难
对于那些不了解人工神经网络工作原理的人来说,人工神经网络就像魔法一样神奇。当你可以成为魔术师的时候,为什么要做一个观众!。我想讨论一下开始使用标准的机器学习库(keras
)是多么容易,如果我们试图从零开始实现一个神经网络(numpy
)以及一点点数学知识,它会变得多么有趣。
有什么新鲜事!
嗯,已经有几篇关于如何从头开始开发神经网络的文章了。但是,在大多数让初学者记住的文章中,实现了一个简单的网络,没有任何关于成本或激活功能的讨论。当手头的问题改变时,由于输入值的范围、激活函数的类型、误差函数,网络不能正常工作。所以,我们再深入挖掘一下。
这是我们将要做的。准备一些数据集,创建一个合适的网络体系结构,先用简单的方法实施该体系结构,然后用困难的方法实施。
数据准备
让我们使用来自 sklearn 的数字数据集。它是一个多类分类。
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_splitdig = load_digits()
plt.gray()
plt.matshow(dig.images[25])
看起来像迷你版的 mnist。应该没问题。该数据集包含data
中的输入和target
变量中的输出。target
取值范围从 0 到 9。它必须被转换成一位热码表示,其中数组包含所有的零,除了索引值为 1。即,值 4 将被表示为[0,0,0,0,1,0,0,0,0]。pandas
在单一功能中完成。由于误差函数的性质,这种多类分类问题必须被转换成用于训练 NN 模型的一个热点表示。
onehot_target = pd.get_dummies(dig.target)
就是这样!。数据集准备好了。现在,让我们把它分成训练和测试数据集。train_test_split
也随机化实例
x_train, x_val, y_train, y_val = train_test_split(dig.data, onehot_target, test_size=0.1, random_state=20)# shape of x_train :(1617, 64)
# shape of y_train :(1617, 10)
训练集中有 1617 个实例。每个输入实例包含 64 个值的数组,相应的输出包含 10 个值的数组。
Dataset preparation
人工神经网络体系结构;
让我们构建一个四层网络(输入,两个隐藏层,输出)。
输入层- 64 个神经元(输入图像数组)
隐藏层 1 - 128 个神经元(任意)
隐藏层 2 - 128 个神经元(任意)
输出层- 10 个神经元(输出单热数组)
ANN architecture
keras——简单的方法:
Keras
是一个非常棒的入门库。又快又简单。
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import RMSprop, Adadelta, Adammodel = Sequential()
model.add(Dense(128, input_dim=x_train.shape[1], activation='sigmoid'))
model.add(Dense(128, activation='sigmoid'))
model.add(Dense(10, activation='softmax'))
在我们的例子中,我们将建立一个完全连接的神经网络,这意味着当前层中的所有神经元都连接到下一层中的所有神经元。因此,我们将模型定义为sequential
,并添加了三个连续的层。需要注意的是,没有必要为输入添加单独的层。当定义第一个隐藏层时,输入层将自动初始化。
该模型添加了具有 128 个神经元的第一个隐藏层,其中input_dim
也指定了输入层的大小。第二个隐藏层也有同样的 128 个神经元。最后,输出层有 10 个神经元。
model.summary()
模型不完整,没有指定成本函数和梯度下降优化。
model.compile(optimizer=Adadelta(), loss='categorical_crossentropy', metrics=['categorical_accuracy'])
model.fit(x_train, y_train, epochs=50, batch_size=64)
好吧。这是做这件事最简单的方法。总的来说,
ANN using keras
安白手起家——艰难之路
准备好。。这是一条艰难的路。该算法通过两个主要过程来训练模型。前馈和反向传播。前馈预测具有某些权重的给定输入的输出,反向传播通过调整权重来训练模型。因此,必须首先初始化权重。
import numpy as npclass MyNN:
def __init__(self, x, y):
self.input = x
self.output = y
neurons = 128 # neurons for hidden layers
self.lr = 0.5 # user defined learning rate
ip_dim = x.shape[1] # input layer size 64
op_dim = y.shape[1] # output layer size 10
self.w1 = np.random.randn(ip_dim, neurons) # weights
self.b1 = np.zeros((1, neurons)) # biases
self.w2 = np.random.randn(neurons, neurons)
self.b2 = np.zeros((1, neurons))
self.w3 = np.random.randn(neurons, op_dim)
self.b3 = np.zeros((1, op_dim))
从一组好的权重开始肯定会快速提供局部最小值。除了缓慢地达到局部最小值之外,坏的权重集有时可能永远不会收敛。为了打破对称性,初始权重应该被随机化。这些值不应该为零,而应该更接近于零,这样输出就不会激增。最好有正值也有负值,这样除了大小,方向也不同。所以用正态分布。将偏置向量初始化为零是可以的。
source: Machine learning memes for convolutional teens, facebook
前馈
Forward pass
def sigmoid(s):
return 1/(1 + np.exp(-s))# for numerical stability, values are normalised
def softmax(s):
exps = np.exp(s - np.max(s, axis=1, keepdims=True))
return exps/np.sum(exps, axis=1, keepdims=True)def feedforward(self):
z1 = np.dot(self.x, self.w1) + self.b1
self.a1 = sigmoid(z1)
z2 = np.dot(self.a1, self.w2) + self.b2
self.a2 = sigmoid(z2)
z3 = np.dot(self.a2, self.w3) + self.b3
self.a3 = softmax(z3)
前馈过程非常简单。(input x weights) + bias
计算z
并将其传递到包含特定激活函数的层中。这些激活函数产生输出a
。当前层的输出将是下一层的输入,依此类推。如您所见,第一和第二隐藏层包含作为激活函数的 sigmoid 函数,输出层包含作为激活函数的 softmax 。softmax 产生的最终结果a3
是神经网络的输出。
应用于该层的函数类型有很大的不同。Sigmoid 函数将输入压缩到(0,1),Softmax 也做同样的事情。但是,Softmax 确保输出之和等于 1。在我们的输出中,我们想要测量每个类的输入的概率。例如,如果一个图像有 0.9 的概率是数字 5,那么在其他类中分配 0.1 的概率是明智的,这是由 softmax 完成的。
反向传播
Backward pass
好吧。Back-prop 可能看起来很棘手,因为你要处理多个层,多个权重,损失函数,梯度。不要担心,我们会用数学、直觉和代码实现来尝试它。从根本上来说,反向推进根据前馈输出和真实值计算误差。通过计算每层中的梯度,该误差被反向传播到所有的权重矩阵,并且这些权重被更新。听起来很简单对吧。嗯。让我们看看。
损失函数:
成本函数和损失函数可以互换使用。严格地说,损失函数是针对单个实例计算的,而成本函数是针对整个网络的。成本函数是一种衡量网络与真实值相比表现如何的方法。实际上,我们永远不会在训练中的任何地方使用成本函数,但是我们必须计算成本函数 w.r.t .权重和偏差的梯度以进行更新。所以,计算损失只是为了看看,我们在每个时期做得有多好。
最常用的损失函数是均方误差。由于我们正在处理多类分类问题,输出将是一个概率分布。我们要把它和我们的真实值进行比较,这也是一个概率分布,找到误差。对于这种情况,强烈建议使用交叉熵作为代价函数。为什么因为,交叉熵函数能够计算两个概率分布之间的误差。
连锁规则:
让我们考虑成本函数为 c,从前馈,我们知道
z = xw + b -> z = function(w)
a = sig(z) or softmax(z) -> a = function(z)
c = -(y*log(a3)) -> c = function(a)
因此,根据输出,我们所要做的就是找出误差,以及每个权重对输出的影响程度。换句话说,求成本函数 w.r.t w3 的导数。是的,反向传播就是用链式法则计算导数。
外层:
dc dc da3 dz3
--- = --- . --- . ---
dw3 da3 dz3 dw3z3 = a2w3 + b3
a3 = softmax(z3)dz3/dw3 = a2
da3/dz3 = softmax derivative(z3)
dc/da3 = cost function derivative(a3) = -y/a3
令人惊讶的是,由于交叉熵经常与 softmax 激活函数一起使用,我们实际上不必计算这两个导数。因为,这些导数的某些部分相互抵消,正如这里清楚解释的。由此可见,predicted value — real value
是他们产物的结果。
Let, a3_delta be the product of these terms as it will be needed in the upcoming chain rules.
dc da3
a3_delta = --- . ---
da3 dz3Thus, a3_delta = a3-y (the error to be propagated)dc
--- = (a3 - y) . a2
dw3
w3 = w3 - dc/dw3For changes in biases,
dc dc da3 dz3
--- = --- . --- . ---
db3 da3 dz3 db3dz3/db3 = 1\. Rest is already calculated
b3 = b3 - dc/db3 => b3 = b3 - a3_delta
隐藏图层:
在隐藏层中,成本函数相对于隐藏层的偏导数也将遵循链式规则,因为成本函数是外层的函数。
z2 = a1w2 + b2
a2 = sigmoid(z2)dc dc da2 dz2
--- = --- . --- . ---
dw2 da2 dz2 dw2dz2/dw2 = a1
da2/dz2 = sigmoid_derv(z2)dc dc da3 dz3
--- = --- . --- . --- => dc/da2 = a3_delta.w3
da2 da3 dz3 da2w2 = w2 - dc/dw2
and set a2_delta = dc/da2 . da2/dz2dc dc da2 dz2
--- = --- . --- . ---
db2 da2 dz2 db2dz2/db2 = 1
b2 = b2 - dc/db2 => b2 = b2 - a2_delta
成本函数 w.r.t w1 的导数也是如此
z1 = x.w1 + b1
a1 = sigmoid(z1)
c = a1.w2 + b2dc dc da1 dz1
--- = --- . --- . ---
dw1 da1 dz1 dw1dz1/dw1 = x
da1/dz1 = sigmoid_derv(z1)dc dc da2 dz2
--- = --- . --- . --- => dc/da1 = a2_delta.w2
da1 da2 dz2 da1w1 = w1 - dc/dw1
and set a1_delta = dc/da1 . da1/dz1dc dc da1 dz1
--- = --- . --- . ---
db1 da1 dz1 db1dz1/db1 = 1
b1 = b1 - dc/db1 => b1 = b1 - a1_delta
收集所有上述方程,并把它放在一个单一的镜头将提供一个更好的概览。与向前传球相比,直觉上也是有道理的。
Feed forward equations:
z1 = x.w1+b1
a1 = sigmoid(z1)z2 = a1.w2+b2
a2 = sigmoid(z2)z3 = a2.w3+b3
a3 = softmax(z3)Back propagation equations:There is no z3_delta and softmax_derv(a3), as explained before.
a3_delta = a3-y z2_delta = a3_delta.w3.T
a2_delta = z2_delta.sigmoid_derv(a2)z1_delta = a2_delta.w2.T
a1_delta = z1_delta.sigmoid_derv(a1)
**注:**sigmoid 函数的导数可通过两种方式实现,具体取决于输入。这总是有一个困惑。如果输入已经是 sigmoid 的输出,即a
,则
def sigmoid_derv(x):
return x * (1 - x)
如果输入为z
且未通过 softmax 激活,则
def sigmoid_derv(x):
return sigmoid(x) * (1 - sigmoid(x))
在链式法则计算中,使用了z
的 sigmoid 导数。但是在实现中,使用了a
的 sigmoid 导数。因此,使用前一个等式。希望,这已经够清楚了。现在有了 numpy 实现。
def sigmoid_derv(s):
return s * (1 - s)def cross_entropy(pred, real):
n_samples = real.shape[0]
res = pred - real
return res/n_samplesdef error(pred, real):
n_samples = real.shape[0]
logp = - np.log(pred[np.arange(n_samples), real.argmax(axis=1)])
loss = np.sum(logp)/n_samples
return lossdef backprop(self):
loss = error(self.a3, self.y)
print('Error :', loss)
a3_delta = cross_entropy(self.a3, self.y) # w3
z2_delta = np.dot(a3_delta, self.w3.T)
a2_delta = z2_delta * sigmoid_derv(self.a2) # w2
z1_delta = np.dot(a2_delta, self.w2.T)
a1_delta = z1_delta * sigmoid_derv(self.a1) # w1 self.w3 -= self.lr * np.dot(self.a2.T, a3_delta)
self.b3 -= self.lr * np.sum(a3_delta, axis=0, keepdims=True)
self.w2 -= self.lr * np.dot(self.a1.T, a2_delta)
self.b2 -= self.lr * np.sum(a2_delta, axis=0)
self.w1 -= self.lr * np.dot(self.x.T, a1_delta)
self.b1 -= self.lr * np.sum(a1_delta, axis=0)
Source: neuralnetmemes, picbear
预测阶段:
一旦模型经过足够的训练,预测就简单了。查询输入必须传递给前馈网络并预测输出。
def predict(self, data):
self.x = data
self.feedforward()
return self.a3.argmax()
总的来说,
ANN using numpy
最后的想法
- 调整参数,如学习率,纪元,初始权重,激活函数,看看系统如何反应
- 如果您的模型不起作用,即误差直线上升或权重都是 NaNs,或者只是预测所有输入都属于同一个类别:
-检查每个函数是否标准化
-用单个类进行训练,看看它是如何工作的
-检查矩阵的维数及其转置
-验证所用产品的类型,点积还是哈达玛积
感谢您阅读帖子。如果你喜欢,请鼓掌。如果你发现了错误或有疑问,请在评论中告诉我。
欢迎通过 Github 、 Twitter 和 Linkedin 联系我。干杯!。
神经网络捏造数字
为什么我们仍然需要数据科学家?
监督神经网络的全部目的是找到能够成功地将一些输入映射到一些输出的某个模型的最佳参数。虽然神经网络可以令人印象深刻,甚至到神奇的极限,但学习机制本身背后真的没有什么神奇之处。它通常包括指定一个损失函数作为**,一个惩罚指标和一个优化器**尽力减少损失。在之前的博客文章中,我们详细解释了所有这些概念,神经网络的一般机制和每个特定齿轮的细节。
假设我们正试图通过检查卫星信号的接收功率来预测室外的降雨量。正如我们所料,我们通常在非雨天有最强的卫星信号,在大雨天信号最差。神经网络的目标是找到将卫星信号映射到降雨量的传递函数。
Rain rate vs satellite attenuation (source here)
假设神经网络的第一次随机初始化创建了模型 rain=2。也就是说,不管卫星接收能力如何,我们预测外面的降雨量为每小时 2 毫米。这个模型与我们说的(rain=5)或(rain=30)模型相比有多好?
自然地,通过查看当前曲线,我们可以说(rain=30)是最差的模型,因为在我们的数据集中,降雨量从未达到 30 的水平!而 rain=2 却在很多时候莫名其妙的正确!笼统地说,如何衡量一个模型有多差?
损失函数是度量神经网络在特定数据集上的性能的指标。他们对神经网络在学习任务中的当前状态进行分级。
最著名的损失函数是误差平方和。它对误差的平方进行求和(误差是神经网络的预测和数据集的实际输出之间的差异)。误差越大,损失越大(以平方的方式!10 个单位的差异产生 100 个单位的损失)。
如果神经网络正确预测了所有输出,则误差平方(预测-实际)为零。完全没有损失,我们可以用这个神经网络代替整个数据集。基本上我们已经找到了一种无损压缩数据集的方法(这就是为什么它被称为损失函数!)
好,现在我们的第一个模型总共亏损 1000,我们能找到一个亏损 800 或 400 或 100 或 0 的更好的模型吗?(请注意,由于过度拟合问题,0 损耗可能不理想!)
梯度下降技术如 sgd、 rmsprop、adagrad 帮助神经网络在损失周围导航。通常他们会给神经网络指明方向以减少损失。把优化者想象成财务顾问。他们看着你的开销,告诉你:在这里你可以做得更好,在这里你可以花得更少,在这里你可以节省更多,在这里你可以减少你的损失!
优化器通过遵循**损失函数的梯度来减少损失。**数学上,这涉及计算损失函数的导数,并遵循最负斜率。因为目标是最小化损失,跟踪最负的导数就像跟踪最陡的下坡路。我们可以期待最快到达底部!
好吧,到目前为止,机器学习看起来很容易。我们只需要定义一个损失函数,然后优化器就来了!什么会出错?为什么我们甚至需要雇佣数据科学家?
当你选择用一个指标来管理的时候,你就邀请你的经理来欺骗和操纵它。
想象一下,一家公司根据员工在办公室工作的时间对他们进行评级。当员工意识到这一点时,她可以用许多方式作弊。她可以早点来办公室,一整天什么都不做,晚点下班,然后赢得本月最佳员工奖!当科学家以他们发表的论文数量来评价时,他们有巨大的动机在尽可能多的会议上发表尽可能多的论文,同时重复相同的想法。更不用说学生为了得到更高的分数而在考试中作弊的无数方式了。 《关于不诚实的诚实真相:我们如何欺骗所有人——尤其是我们自己》 是一本关于欺骗话题的优秀书籍。
Let’s play the number game!
这与我们的神经网络示例有什么关系?例如,如果我们认为我们的数据库是倾斜的:99%的时间不下雨,神经网络可以在所有时间懒洋洋地收敛到输出 rain=0。通过这样做,神经网络节省了 99%的损失!这就像一家金融公司逃避了 99%的税收,只缴纳了 1%的小额罚款。这里有一些其他机器学习的有趣例子懒洋洋地收敛到令人惊讶的解决方案。
我们可以说,如果被测量的对象不知道他们正在被测量或如何被测量,那么一个度量标准会更好。但是优化器正是通过遵循损失函数的梯度来工作的,如果我们不定义损失函数,我们就无法优化!有什么解决办法?
只有深刻理解这个数字游戏、深度学习的机制及其背后的数学,数据科学家才能摆脱优化器玩这个游戏的陷阱!这是一份侦探工作!
当事情进展顺利时,我们不需要侦探。但是当一个模型没有收敛,或者收敛到一个懒解的时候,只有有深度理解和知识的人才能解决深度学习的问题。
这是在这种情况下我们可以做的事情的非详尽列表:
- 花更多的时间与领域专家一起更好地理解问题。能不能提取出更好更相关的特征?
- 检查数据集,我们能得到更多的数据吗?我们能减少偏见吗?我们能减少功能的数量吗?特征是独立的吗?我们能否尝试过滤、转换、预处理或减少偏斜或偏差。
- 检查模型是否太简单或太复杂。尝试不同的神经网络模型和架构。试图用一个简单的前馈层(y=ax+b)和一个线性激活来学习一个二次或高度非线性的函数显然是不合适的!
- 调整损失函数本身。当一个指标不起作用时,我们可以尝试另一个指标!例如,我们可以将“99x”乘数加到损失平方和上。这可能会阻碍优化器收敛到惰性解决方案 y=0。我们可以自定义损失函数来适应我们的需要(根据问题)!我们不仅仅局限于现有的标准损失函数(平方和和对数损失)!
- 尝试不同的优化器,不同的学习率,不同的正则化率。
- 提高自己的知识。例如,看看这个有趣的帖子:“关于深度学习的深层误解”
- 作为最后的手段,我们可以尝试元学习。元学习是一种探索几种各有不同配置的神经网络结构的技术。从这个意义上来说,可以看作是机器学习的蛮力。缺点是在计算能力方面显然是非常昂贵的!
原载于 2018 年 10 月 29 日【medium.com。
神经网络 I:符号和构建模块
神经网络简介
这个关于神经网络的系列帖子是脸书 PyTorch 挑战赛期间笔记收集的一部分,在 Udacity 的深度学习纳米学位项目之前。
内容
- 神经元
- 连接
- 层——神经元与连接
3.1 神经元层
3.2.连接层— PyTorch 示例
4。符号歧义:Y = X W vs Wt X
1.神经元
神经元是神经网络的组成部分。
毕竟,神经网络是神经元的简单集合,它们朝着同一个目标一起工作,通常是执行给定的任务,实现尽可能少的错误。
由于我们用于神经网络的图形表示,如图 1 所示,神经元也接收节点的名称,因为每个节点对应于图形表示中的一个唯一节点。图形表示不仅仅是用来简单地观察这些网络,也是我们如何计算实际发生的操作的一个基本方面。这将在更高级的部分“图形方法”中介绍,不在本节的讨论范围之内。
Figure 1. Graph representation of a neural network
只要看一下图 1,我们就可以通过观察神经元看起来是按层组织的,一个接一个地堆叠起来,从而对下一部分有一个直觉。然而,让我们先来看看单个网络的细节,并评估它们与真实生物神经元的接近程度。
在图 2 中,我们只是从网络中随机选取了一个神经元,并观察它由不同的部分组成。放大镜所指的那个神经元将对应于它上面的生物表示中的第二个网络。
我们观察哪些部分?
收集器
我将其命名为收集器,因为它的功能是聚集来自其他网络的传入连接。用生物学术语来说就是突触连接。请注意,合计行为的数学等价形式是求和。我们将看到,这种求和不仅仅是对所有连接求和,而是对它们进行加权,这意味着每个连接对收集器都有不同的“重要性”。
激活器
聚集在收集器中的接收信号的激活是发生在网络主体内部的过程。目前还不清楚对那个过程来说什么是更好的表示,在深度学习中,它是通过所谓的激活函数来建模的。这些函数的形式各不相同,但它们只是将一个函数应用于采集器传递的信号。这些函数是非线性的,因为激活器是神经元(或网络)的唯一部分,在那里我们可以引入学习非线性映射的能力,这是绝大多数真实世界场景所需要的。
经销商
在它的名字之后,分配器简单地收集激活器之后的信号,并以相同的强度将它发送到它所属的神经元所连接的其余神经元。在神经网络的图形表示中,它指的是堆叠神经元的下一层中的每一个单个神经元。
Figure 2. Biological neuron vs Artificial neuron
2.连接
在人工神经网络的图形表示中,不同神经元之间的连接由连接两个节点的边来表示。它们被称为权重,通常表示为和。神经网络上的权重是任何参数模型上的参数的特例。
如图 3 所示,我们可以给组成每一层的每一个神经元一个数字。权重的子指数表示哪些神经元被连接。因此, wij 表示在前一层神经元 i 与后一层神经元 j 之间建立的连接。
一种更有效的表示两层之间所有连接的数学方法是将它们收集到权重矩阵、**、 W 、**中。该矩阵将具有以下形式:
其中 l 表示这些权重的层的索引。 N 为上一层神经元的数量, M 为下一层神经元的数量。因此,行数由第一层神经元的数量定义,而列数由第二层神经元的数量定义,这当然不必相同。为简单起见,图 3 中省略了 l 索引。
Figure 3. Connections of neurons between 2 layers
为了对权重矩阵有更强的直觉,让我们给网络的输入命名,看看矩阵的行和列代表什么。
我们可以把图 4 中的输入看作是,例如花在学习理论上的小时数和花在做练习上的小时数。模型的输出可以是通过考试的概率。所以我们要输入变量和 x1 和 x2 可以简化为输入向量 X 。
Figure 4. Computing the output of a layer
通过矩阵乘法,我们看到输入如何乘以每个权重,以产生输出 H (隐藏后),其维数与该隐藏层的神经元数相匹配(我们将在下一节中更多地讨论层)。在这种情况下, h1 , h2 , h3 。
Figure 5. Weights matrix
让我们更仔细地看看权重矩阵。在图 5 中,我们可以看到每一列实际上代表了后一层中每个神经元的收集器。因此,每一行都代表前一层中每个神经元的分布器。
3.层——神经元与连接
我们已经介绍了人工神经元是如何分组分层的。特定层的每个神经元接收其所属层之前的层的每个单个神经元的信号,并将信号发送到下一层的每个单个神经元。
神经元层
Figure 6. Layers of an artificial neural network
深度学习中的单词层然后被用来称为神经元的每个堆叠聚集。在图 6 之后,第一层通常称为输入层。这是因为这是将输入引入网络以初始化正向传递的层,如图 4 所示。最后一层接收输出层的名称。直观上,它将把每个神经元执行的所有计算的输出结果提供给输入。输入层和输出层之间的所有中间层称为隐藏层。这些层通过创建数据特征的分层表示来执行所谓的表示学习。在使用了许多隐藏层之后,术语“深度学习”“T21”出现了,就这些层的数量而言,它让位于更深的网络。
连接的层次
在某些情况下,“层”这个词也用来指网络的权值层。因此,我们可以在网络中有两种堆叠实体表示,如图 7 所示。
左手边代表典型的神经元层。右手边代表连接的层,其中每一层代表封装该特定层的参数的权重矩阵。
记住这一点是很有用的,因为当我们实现我们的网络时,例如在 PyTorch 中,我们通常定义两层神经元之间连接的隐藏单元的数量。因此,我们给出权重矩阵的维数,并遵循右侧的表示。在这种情况下,PyTorch 中的深度前馈神经网络实现为:
Figure 7. Layers of neuron vs Layers of weights
图 8 表示在 PyTorch 中实现的图中使用的上述网络的输出。我们简单地传递了输入和输出维度以及一个列表,其中每个条目是每个隐藏层中的神经元数量 (2,2,[3,3]) 。
Figure 8. PyTorch implementation of the network
注意,当我们通过网络简单地调用网络时, PyTorch 打印出一个表示,将层理解为连接层!如图 7 的右手侧。根据 PyTorch 的隐藏层数是 1,对应于 W2 ,而不是 2 层 3 个神经元,这将对应于隐藏层 1 和隐藏层 2 。
4.符号歧义:Y = X W vs Wt X
深度学习中一个非常常见的模糊符号是如何在数学上表示输入和权重之间的乘积,以产生输出或任何特定层的隐藏表示。
首先,无论哪种记数法,如果使用得当,输出都是一样的。它们之间的区别在于如何理解变量(向量),是水平向量还是垂直向量。
4.1.Y = Wt * X
在我们看到权重矩阵的转置出现在输入向量之前的情况下,意味着向量被表示为列向量。这种符号如此常见的原因是,特别是在神经网络被纯粹视为统计黑盒模型的背景下,遵循文献中其余模型的符号。
说到底,神经网络只是做所谓的曲线拟合的另一个工具而已。这意味着我们试图找到一个更好地适应数据分布的模型,当然,神经网络不是用来解决这一任务的第一个模型。线性回归模型是这种做法的最简单形式,如图 9 所示,用于图 7 所示网络中的二维输入。
Figure 9. Curve fitting for 2D input space
搜索该表面的表达式将以一些未知的系数开始,这些系数对输入值进行“加权”:
为简单起见,我们将从现在起省略偏置项 b 。
要点是系数通常放在它们所附属的变量之前。
听起来很熟悉,对吗?我们可以用一个非常简单的神经网络来表示这个方程,如图 10 所示。
Figure 10. 2D Surface fitting NN
我们还省略了权重的第二个子索引,因为只有一个收集器。
现在,如果我们将输入理解为两个条目的列向量,则这些网络的组件将是:
因此,计算输出的最终公式为:
延伸到一个更复杂的例子,对于图 7 中的网络,第一个隐藏层(没有任何偏置和激活)之后的隐藏值将是:
4.2.Y = X W*
在文献中找到神经网络的前向传递的另一种可能性是在没有任何转置的情况下利用权重矩阵之前的输入变量。
这自然是理解变量的相反方式。这一次,作为一个行向量,因此:
在我个人看来,这种方式感觉更自然。
作为人类,我们非常习惯于表格数据,我们通常赋予列维度的含义,赋予行不同记录(或观察)的含义。因此,类似于图 11 中描述的表格信息,结果非常直观。
Figure 11. Tabular data to row vector representation
在图 11 中,每一列代表现实世界中的一件真实的事情。 x1 可能是花在为考试学习理论上的时间,而 x2 可能是花在做练习上的时间。这个模型可以试着预测学生将得到的分数。#不是一个栏目,而是一个索引,简单地说就是每个不同的学生询问他是如何分配学习时间的。
在这些帖子中,我们将更详细地讨论批量大小。这一批仅仅意味着如果我们不是一次选择一个学生,而是选择更多的学生在网络上做同样的转发。绿框表示批量为 1。
神经网络 II:第一次接触
神经网络简介
这个关于神经网络的系列帖子是脸书 PyTorch 挑战赛期间笔记收集的一部分,在 Udacity 的深度学习纳米学位项目之前。
内容
- 简介
- 向前传球
- 反向传播
- 学习
- 测试
- 结论
1.介绍
在下图中,显示了一个人工神经网络。我们可以看到如何将我们的输入数据( X )转发到神经网络中,并获得对我们输出的预测 Y_hat 。
Figure 1. Schematic Feed Forward Neural Network
因为我们有了预测应该有的实际值,所以我们可以使用所谓的“成本函数”来计算误差。通过这些,我们可以看到每个神经元对该误差的责任有多大,并更新它们的值,以便随着时间(实际上,随着输入向量的迭代次数)减少该误差。
在写下方程之前,为了更容易理解,让我们把神经网络转换成矩阵符号:
请注意,我们使用 n 和 m 来表示它可以是任何数字,也可以是更多的隐藏层。为了简单起见,我们将检查图 1 中所示的 NN,它意味着 1 个隐藏层, n=2 , m=3 。
在下一个表格中,我们还可以看到一个表格,其中包含我们在本文档中使用的符号:
Table 1. Notation used
2.前进传球
现在我们有了所有这些信息,我们可以在神经网络上发展数学方法。就在它之前,也是为了方便可视化,让我们看看神经元内部发生了什么,例如我们的第一个隐藏神经元。
Figure 2. Single Neuron Scheme
这应该仍然是本系列第 1 部分中介绍的收集器和分配器的概念。
根据图示,神经元将乘以相应权重的所有输入相加,然后应用一个激活函数将输出传递给下一步。
因此,描述 NN 的整个方程组是:
有了这些等式,我们就完成了将在我们的 Neural_Network 类中定义的 forward 方法(我们将开始引入 Python 符号,以便在查看代码时更加熟悉)。
然后,将使用 NN 的输出和真实值来计算成本函数(注意这是监督学习)。根据你的问题,有几种方法可以计算这个函数。
这一次,我们将尝试最小化 RMSE(均方根误差),这对应于下一个公式。RMSE 是用于回归问题的典型误差函数,正如交叉熵用于分类问题一样。然而,在这两种情况下,我们都可以使用各种不同的函数
更有趣的是,我们可以根据网络的参数来表达相同的等式:
3.反向传播
这是告知神经网络关于该值应该具有的误差,并将其反向传播给*‘告诉’*权重的时刻,它们对该误差负有多大责任,因此它们可以更新它们自己的值,以便持续减小该误差。
因此,我们想知道 dJ/dW1 和 dJ/dW2 的值是多少。
如果我们首先开发对应于隐藏层权重的梯度:
现在,如果我们暂时忘记常数项,将链式法则应用于剩余的导数项:
1–预测 Y_hat 是 z3 的函数,因此可以直接导出。
2–预测 Y_hat 可以相对于 W2 绘制成斜率为 a2 的直线。数学上,这被表示为该向量的转置。此外,我们已经将剩余的项分组到 delta3 中,这被称为“反向传播误差”。
上述陈述也可以在这个伟大的 youtube 解释中重温。
现在,再次将链式法则应用于下一个方程,我们可以计算剩余的梯度:
1–输入 z3 可以相对于 a2 绘制成斜率为 w2 → w2 的直线。T
2–输入 a2 是 z2 的函数,因此可以直接导出
3–输入 z3 可以相对于 a2 绘制成斜率为 X → X.T 的直线
4.学习—权重更新
到目前为止,我们已经解释了如何进行前向步骤来获得预测,然后如何使用真实值来计算成本函数值,以及如何使用反向传播(偏导数+链规则)来告诉每个神经元它们对该错误负有多大责任。但是现在,我们的 NN 是怎么学习的呢?嗯,和我们做的一样:尝试,失败,学习。这些步骤就是我们所说的:训练。
因此,我们已经讨论了尝试(转发)和失败(开销)步骤。现在,剩下的步骤 learn 是通过更新权重值来完成的。每个节点(或神经元)将按照下一个等式更新其最后一个值:
该参数λ被称为“学习率”。因此,作为 dJ/dWi 的错误 J 是由 Wi 的责任造成的——(注意 Wi 是一整层的权重)。鉴于所犯的错误(由于他们的错误)乘以这个学习率,我们确切地告诉我们的神经元纠正他们自己的值。
如果学习率的值太高(通常从 0.01 或 0.001 开始),学习的过程就不能收敛,如果值太低,学习就可能永远进行下去。就像现实生活中一样!我们想尽可能快地学习,但是我们知道在开始的时候我们需要慢慢来吸收所有我们不知道的信息,直到我们有信心继续尝试新的信息
(*)情商。9 是学习的最简单的实现。通常,我们说优化器负责对权重进行更新。优化器有许多不同的实现。这种情况下,最基本的和其余的支柱被称为t。这里有一篇关于不同优化器以及如何实现它们的好文章。
5.测试——我们学会了吗?
学习的过程一遍又一遍地重复,直到我们认为我们已经学会了。但是,“已经学会”是什么意思呢?就像为了考试而学习一样,我们通常会做所有书籍中的问题,直到我们确保能够解决所有问题。但是后来考试来了。在考试中,有(通常)你没有处理过的数据,你不知道你会发现什么!
但是,如果你已经做了足够的研究,你很确定如果你对考试数据应用 forward( ) 函数,你将得到一个 A+的输出(你的答案)。NN 的行为完全相同。它进行训练,直到有信心转发新数据。(小心过拟合,训练过度)。
5.结论——我们学会了吗?
我希望这很有趣!这只是神经网络世界的开始!现在你可以去 GitHub repo 看看如何从头开始编写这个代码,或者如何使用 PyTorch 这样的深度学习框架来实现。
我强烈建议您从头开始编程,并确保您了解所有内容。然后你将准备好节省使用这些库的时间,但是要确信你理解在引擎盖下发生了什么。
你需要什么,就留下评论,如果你喜欢,请推荐它!下次再见,享受神经网络!!
神经网络 III:大图
神经网络简介
这个关于神经网络的系列帖子是脸书 PyTorch 挑战赛期间笔记收集的一部分,在 Udacity 的深度学习纳米学位项目之前。
内容
- 简介
- 作为功能的网络
- 让我们增加尺寸
- 批量大小
1.介绍
在第一章中,我们已经看到了什么是神经网络,以及我们可以从它们那里得到什么。这种方法是从外部观察神经网络。它们是我们见过的实际上在执行数学运算的黑盒。
为了完全理解神经网络,是时候从内部来看它们了。在这一章中,我们将看看这些神经网络如何被分解成最小的块,以及如何根据我们将要去神秘化的行为从零开始编码。事实上,我们要做的这种分解是神经网络和计算效率的原因,我们可以用它们来解决需要大型数据集的复杂问题。
首先,让我们再看看我们的老朋友,让我们试着找出我们能从中提取的最小片段。
Figure 1. Schematic Feed Forward Neural Network
唯一的区别是输入的维度和隐藏层中的神经元将是固定的,以便在我们回顾过程时获得更好的理解。我们将在途中发现所有的魔法从何而来。
2.让网络发挥作用
正如我们在前一部分中所做的那样,神经网络只是正在发生的数学运算的一种表示。因此,我们可以将所有变量表示为不同维度的向量。
首先,我们有一个二维输入。这意味着,我们有两个不同的概念,例如,温度和压力。我们的输入神经元将这 2 个输入维度映射到 3 个隐藏维度,这是隐藏层中神经元的数量。因此,矩阵的维数必须是*(2×3)*。此外,为了更清晰地显示,它没有被表示出来,但是我们有一个“b”项,用于在矩阵乘法之后添加偏差。这导致了这种情况:
现在我们有了到达隐藏层所需的所有工具。我们只需要应用矩阵乘法:
这样做的结果将是:
记住,每个输入权重将每个输入 i 与每个神经元 j 连接起来。这就是为什么符号 Wij ,连接 i 和 j.
一旦我们进入图层,就该应用激活功能了。这样做的结果将是:
我们将函数逐元素地应用于每个神经元的网络输入的每个元素,因此我们将 3 维作为隐藏神经元的数量。
根据神经网络方案,现在需要的变换是从 3 到 1,因为下一层只有 1 个神经元。最后一个神经元通常不执行任何功能,因为我们不想在这一点上应用非线性。因此,连接这两层的矩阵必须是 (3x1) 。
现在我们有了所有的工具来应用隐藏层的权重。那么需要执行等式是:
由于我们的最终输入不执行任何操作(还没有),我们可以说 z3 是输入通过所有网络后的最终输出*(在前面的部分中称为 Y _ hat)*。这就是我们所说的前馈操作。
3.让我们增加维度
对于二维的单个观察,我们已经经历了整个前进过程。例如,我们有温度= 20°C,压力= 1 巴。但是谁能仅凭一次观察就学会任何东西呢?我们需要知道更多,以提高我们对周围事物的认识,并能够从中找到规律。如果我们想学习如何下棋,我们肯定要尝试几次。这个事实会使过程复杂化吗?当然不是!让我们过一遍。
比方说,我们有 4 个观察值(当然你需要更多,但我只是选择了与已经使用的 1,2,3 不同的最小数字,所以最后你可以使关系更容易;我们会达到这一点,不要担心)。
因此,我们的输入如下所示:
现在,对应于网络本身的变量保持不变。我们没有改变网络中的任何东西,我们只是用一批观测值作为输入,而不是单个观测值。因此,如果我们执行向前,我们的结果是:
同样,唯一的区别是我们对每个不同的观察都有一个新的行。接下来的步骤是使用 z2 作为每个隐藏神经元的净输入,以元素方式应用激活函数。因此 a3 的形状与 z2 的大小完全相同。
最后,应用等式。2 计算输出:
4.批量
我们一直在使用一批输入示例。在深度学习中,最重要的突破之一是对众所周知的优化器随机梯度下降(SGD)进行小规模修改,成为小批量随机梯度下降。
有什么区别?
那么,SGD 的普通实现将输入一个输入,并执行向前传递。这将导致误差,该误差将被反向传播到网络中以更新权重。
如第 3 节所示,mini-batchSGD 接收一批输入,并在转发过程中将它们一起转发,将每个输入的误差平均为 mini-batch 的单个误差。
使用小批量有什么好处?
首先,由于我们对每批输入进行 1 次权重更新,总的更新次数将会更少!因此,我们减少了训练中的计算量,从而减少了训练时间。
其次,硬件和软件都被优化以执行矩阵乘法。因此,在单次矩阵乘法中进行批量计算所需的时间比顺序进行要少。
最后,结果表明,使用小批量优化器具有更好的收敛特性,对训练有很大帮助。
这个 kaggle 内核是一个很好的资源,可以更好地理解批处理大小如何影响优化器。
行动中的神经网络
使用人工神经网络模拟现实世界的问题
神经网络
A 人工神经网络是对人脑功能的计算机模拟,使用编程结构来模拟脑神经元的功能。你可能会发现我之前在的帖子 ( 神经网络介绍)在这方面非常有趣。
真实世界的场景和启发
使用神经网络解决的真实世界场景从简单的回归问题扩展到更复杂的问题,如自动驾驶汽车。但是对于神经网络的训练,我们必须评估网络的结果,以便它可以进行学习。有几种方法可以做到这一点。
- 使用监督训练
你知道对于给定的输入,输出应该是怎样的,因此我们提供带有输出的训练集来训练网络。 - 启发式函数
具有确定输入对输出的适合度的函数。主要用于决策制定,其中某个函数评估决定的输出,并为训练提供权重。举个例子,训练安玩 井字游戏 ,移动得越好,移动的重量就越大。 - 强化学习
通过奖励学习。我们可以想象一个场景,我们训练一个安去玩*【flappy bird】。你通过一个障碍间隙,你被奖励,赞成移动。如果你不喜欢任何其他不适合你的动作,那就继续做下去,直到找到最好的动作并开始训练。*
用于文本识别的人工神经网络
建模输入
培训的投入是至关重要的,质量越好,产出越好,是吗?对于文本识别,我们不需要更好的质量,我们需要更好的准确性和清晰度。在练习中,我们将使用 16×16 的图像。事实上,我们将把接收到的图像缩小到这个维度。同样,我们将使图像的灰度为。因为对于字符识别,我们需要的是画布上黑色像素的分布。将画布变小会使图像变得更轻,并使神经网络更容易将它们馈入。**
Downscaling images
如图所示,我们可以将数字从 0 缩小到 9 以适应 256 像素。这将使人工神经网络的外层具有 256 个节点来吸收每个像素,即使在便携环境中也是可管理的。
人工神经网络建模
16×16 grid image
该图描绘了一个数字缩小到 16×16 网格后的样子。
另一项重要任务是确定人工神经网络的规模。很明显,我们将 256 个(图像像素)输入映射到 10 个输出。内部节点呢?。我们实际上不需要太多的中间。因为中间有很多节点和层会使网络过重,增加传播误差更大。
使用 TypeScript 的实际示例
我已经使用0–9数字图片使用 Calibri 字体。并使用来自 苹果专卖店 字体的数字来测试神经网络。考虑下图。
Detecting whats in left by training from font in left
在这个例子中,图像的右侧包含测试输入,并且它由配置的 ANN 通过输出 0.11339060644370896 来检测,最后输出 10。所有其他输出都小于 0.02,这使得我们的选择比下一个最有可能的选择概率高近 5 倍。
示例代码
输出
**Started training
iterations 1000 error 0.031689028142154754 rate 0.1
iterations 2000 error 0.014637913521207704 rate 0.1
iterations 3000 error 0.009482464231621053 rate 0.1
iterations 4000 error 0.007004583325468936 rate 0.1
iterations 5000 error 0.005550772188951524 rate 0.1
Finished training
Attempting detection
Detected file 1_test.png as 1
Detected file 7_test.png as 7
Detected file 9_test.png as 9**
附言
有人指出,在某些情况下,5 和 9 混淆了。这是由于相似的形状。如果训练集使用顶部区域较小的 7,这甚至会发生在 1 和 7 上。为了更加准确,我们可以使用几种字体进行训练,我刚刚使用了一种。你也可以从链接的要点评论中下载训练数据文件。
神经网络:你的大脑像电脑吗?
大约三年前,当我怀儿子的时候,一个朋友送给我一本丽莎·艾略特的《里面发生了什么事?》。。对我来说,这是一个非常受欢迎的偏离,因为所有的育儿书籍都坚持告诉我,我的生活将变成一个充满不眠之夜、疲惫和普遍缺乏理智的生活地狱。所以我开始通读这本书,很快意识到这是我为即将为人父母做准备时所读的最重要的一本书。这本书讲述了人类从出生到童年早期的大脑发育。这本身就是一个迷人的主题,但在人工智能的背景下更是如此。
从神经生物学角度来说,人脑通过神经元或神经细胞网络发挥作用,这些神经元或神经细胞相互作用,以交流和处理信息,我们基于这些信息来看、听、动、想、做决定和一般功能。中枢神经系统及其神经元网络是体内所有活动的核心。
如果我们把它分解开来,人体的功能与现代计算机非常相似——或者说,计算机与有史以来最复杂的处理单元,即人脑非常接近。让我们暂时回到基础——任何信息处理系统都由 5 个主要部分组成——输入、输出、存储、处理和程序。我们可以在大脑和计算机之间画出这些元素的相似之处。
1。输入:输入是作为行动基础的刺激、数据和对象。
- 在机器世界中,输入来自输入设备,如键盘、鼠标、扫描仪、照相机、麦克风等。它们有不同的格式,如图像、文本和结构化数据。
- 对于人脑来说,输入是通过内部和外部刺激提供的。大脑的输入“设备”将包括感觉细胞(与视觉、听觉、触觉、味觉相关的细胞)、运动/肌肉细胞,甚至大脑中的一些细胞。这些细胞接收提供的刺激,并进行进一步处理。
2。输出:输出是对输入进行处理后得到的信息或动作。
- 在机器的情况下,输出动作可以是打印东西、添加数字、投影图像、基于算法解决复杂问题、识别模式等。
- 人脑中发生的处理输出显然更加复杂,包括控制内部和外部肌肉、感官知觉、管理内部身体功能(如血压、心率、温度)、决策、解决问题、情绪、社会行为等活动。
3。存储:这是存放输入信息、输出信息和程序等东西的地方。
- 计算机有两种存储方式。一种短期存储器,我们通常称之为 RAM,它存储计算机执行即时任务所需的有限数量的信息(数据或程序)。以及硬盘驱动器、光盘、磁带等形式的具有更大容量的长期存储。
- 与这种结构非常相似的是人脑的记忆系统。大脑的短期存储被称为 【工作记忆】 ,它在短时间内利用大脑的记忆和注意力能力来执行手头的即时任务(例如,在课堂上记笔记时记住老师在说什么)。人类的长期记忆是通过他们的 【长期记忆】 或 LTM 功能实现的,这使他们能够无限期地储存几乎无限量的信息。它包括对特定事件、事实信息和经历等事物的记忆(或存储)。
4。处理器:这可能是最容易掌握的并行。它是指执行所提供指令的中央处理电路。在计算机中,这是由其 CPU 管理的,而对于人类来说,这是由大脑和脊髓及其内部电路组成的中枢神经系统。
5。程序:或计算机语言中的“代码”。
- 这是指一套复杂的指令,规定了需要对输入做什么才能得到输出,汇集了一套必须在规定的参数内按规定的顺序执行的功能。
- 这是我们今天讨论的核心。在人类中,这种代码是通过大脑中的神经元执行的。神经元相互连接,通过它们传递信号,这些信号经过处理后实现所需的动作,整个过程由中枢神经系统控制。信号或信息交换通过称为 突触 的非常关键的神经系统结构发生。
- 计算机程序通过机器码运行,机器码是位的模式(即信息的二进制单位,如“1”和“0”)。类似地,人脑通过化学或电子信号的模式运行其“代码”,这些信号从一个神经元传递到下一个神经元,形成神经网络。
人工神经网络(或 ann)的工作方式与生物网络完全一样。人工神经网络是一个相互发送信号的人工神经元网络。这些网络的功能就像大脑一样,通过观察来学习,不需要特别编程。考虑一下婴儿第一次认出一只狗的情况——你给他们看一只狗,你说“狗”这个词,你这样做几次,现在婴儿的大脑已经学会识别狗的视觉和用于描述它的术语之间的模式。机器以完全相同的方式学习。
有趣的是,人类的智能和人工智能一样,也是需要开发的。在大脑发育的早期(通常从受孕到 4-5 岁),连接神经元的突触会非常快速地产生。用数据科学的术语来说,这个阶段有点类似于“模型训练”——孩子接触的东西越多,与之相关的突触就越多。一个很好的例子是语言——一个孩子在早年接触语言学越多,她大脑中与语言相关的区域就越进化。其他认知和非认知能力同上。
这对我是一个启示,它改变了我看待童年早期的方式。对我来说,大致的底线是,在早期,你让你的孩子接触不同的事物越多,他们的突触在这些功能上就越强大,训练得越好。换句话说,人类的智能是可以被塑造的,就像它的人工对应物一样。这种理解也是我们经常听到的先天与后天之争的关键。
我们对人脑结构及其功能的理解仍在发展中,有许多方面我们甚至还没有开始触及其表面。对我来说,这一切都可以归结为一个问题:所有的大脑功能都是可计算的吗,也就是说,它们是否都可以归结为“输入-处理-输出”等式,这个等式在理论上可以在物理世界中复制,不管它的“处理”方面有多复杂。或者是人脑的角色的一部分(像意识、自由意志、记忆分类等等)。)就是不可“编程”?
这个问题还没有定论,人工智能的极限将在很大程度上取决于这个问题的答案。
神经网络 IV:图表方法
神经网络简介
这个关于神经网络的系列帖子是脸书 PyTorch 挑战赛期间笔记收集的一部分,在 Udacity 的深度学习纳米学位项目之前。
内容
- 简介
- 神经网络的图形表示
- 计算中的模式——门
- 结论
1.介绍
和前面的部分一样,我们将使用相同的神经网络结构。
正如我们在“学习”一节中看到的,神经网络通过转发输入并将结果输出与真实值或这些输出应该是什么进行比较,根据所犯的错误来更新其权重。
在数学上,我们计算这些误差的梯度,并更新权重如下:
这些更新发生的次数非常多。神经网络使用大量数据进行大量迭代训练。为了使该过程在计算上可行,神经网络被理解为数学运算的非循环图,这便于导数的计算和链规则的应用。
2.神经网络的图形表示
是时候发现我们的神经网络是如何运作的了。如果你曾经使用过像 TensorFlow 或 PyTorch 这样的机器学习框架,我们要做的就是试图找出简化你生活的那几行代码背后的东西。
好吧,信不信由你,我们一直在工作的友好的神经网络如图 1 所示。
Figure 1. Graph representation of out [2, 3, 1] neural network.
让我们先看一看,并确定每种形状和颜色是什么:
黄色方框是变量。x 是输入,Y 是输出,正向过程的输出应该是什么;J 是实际输出 Y 和 Y_hat 之间的误差。
蓝色形状对应于各层之间的数学运算。它们基本上是基于元素的函数(激活)和矩阵加法和乘法。
现在我们可以转到上一章,回顾一下我们是如何通过将不同矩阵相乘的几个步骤来完成正向过程的。让我们写下我们可以为图表推断的方程,并检查它们是否与我们以前的知识一致。
那是所有的向前传球。我们现在开始神经网络的反向传播。这变得越来越有趣,所以让我们用第一个反向传播误差的例子来解释一下细节,看看这个过程基本上是如何以相同的方式重复的,直到我们到达我们网络的起点。
Figure 2.First Backward Propagation Step
我们需要从这里得到的想法如下:
首先,我们输入我们的输入值 z3 和 y 作为方程中描述的函数(成本)的输入值。F7。它们的值以灰色给出。因此,此时我们能够计算出 的局部梯度 。这些是电流方程的偏导数(我们看到它们在盒子下面和上面是黑色的)。如果我们拿 Eq。F7 和我们推导它,结果是(鉴于我们只有 1 次步骤,所以 n = 1):
接下来,我们总是从函数本身的局部梯度开始反投影过程,显然是 1。之后,我们希望将该错误反向传播给所犯错误的负责人。我们所做的是应用链式法则将一个导数分解成更简单的导数。
你必须意识到,这第一步可能不是最能说明问题的,因为成本函数本身的导数是 1,因此链式法则看起来没有任何作用。然而,当我们查看反向传播过程中的任何其他步骤时,它会对我们有所帮助,如图 3 所示。这里,我们以应用激活函数在系统中引入非线性的过程为例。
Figure 3. Activation Backward Propagation Step
本图中使用的激活函数是 sigmoid 函数。
重复相同的过程,我们有输入 z2 ,它被应用 sigmoid 函数以获得 a2 。在这一点上,我们可以计算局部梯度,并等待来自网络末端的反向传播误差。
然后,当它到达时,我们有 *dJ/da2,*它基本上意味着“J 的值对 a2 的多少负责”。应用链式法则,我们可以确定 z2 — (1)对那个 J 有多大的影响。
为此,我们应用与上一个示例相同的方法。我们使用链式法则将梯度表示为局部梯度和误差反向传播到这一点的乘积。
为什么我们要计算这些梯度?
为什么反向传播 J 成本的值?我们需要回忆一下网络是如何学习的。它们基于相对于权重的误差梯度来更新权重值。我们想知道“神经元的权重对所犯错误的责任有多大”,这样我们就可以调整它们的值,让我们的网络学习。所以,最后我们要找的是 dJ/dW1 和 dJ/dW2 。
好,现在我们知道了工具和目标,让我们为我们的网络开发逆向方程!
反向传播
在图 1 上向后看的同时看下面的等式。
我们走吧!我们已经完成了所有的反向投影过程,并计算我们感兴趣的梯度如此容易!我们将使用这些值来更新权重值,并减少下次的误差。但是现在,让我们仔细看看,做一件人类仍然比机器做得更好的事情,让我们找到模式。
3.计算中的模式—门
正如我们在图 2 和图 3 中详细看到的,全局网络的每个组件的反向传播基本上包括将从网络末端接收的梯度乘以局部梯度。这就是人工智能的魔力,就这么简单!
我们看到的另一个模式是不同的模块是如何工作的。在人工智能世界中,它们有自己的术语,如下:
添加门
在正向传递中,加法门将输入相加作为输出。
在后向通路中,加闸称为*。看看 B2-B2 或 B7-B8 这两个方程中的一个。它们代表网络中两个加法门的反向传播过程。他们两个的本地梯度都是 1,这就是为什么我们称他们为分销商。它们只是捕捉到到达它的梯度,然后原封不动地传递给它的所有输入。*
Figure 4. Python code for the Add Gate
乘法门
在前向传递中,乘法门将输入的乘积作为输出。
在反向通路中,乘法门被称为 开关 。看看方程式 B4-B5 或 B9-B10 中的一个。它们代表网络中每个乘法门的反向传播过程。两者的局部梯度是另一个输入的值,这就是为什么我们称它们为开关*。*
Figure 5. Python code for the Multiply Gate
激活
在正向传递中,各层将我们为其选择的函数(激活函数)应用于输入,以计算输出。
在后向过程中,层只是将其自身的导数值乘以传入渐变来计算输出渐变(也称为底部渐变)。看一下等式 6。两个典型的激活函数是 sigmoid 或双曲线正切。
Figure 6. Python code for two common Activations
成本计算器— PyTorch 标准
我们所看到的成本(通常也称为损失)是网络在与真实值进行比较时产生的误差。这可以解释为向前传递的最后一步或向后传递的第一步。
在这种特殊情况下,我们应用了一个成本函数来最小化均方误差。然而,根据神经网络的应用,这个盒子的结构和其上的值将会不同。
在这种特殊情况下,它接收输入的前向过程的输出,并将其与应有的值进行比较。之后,它返回导数的值,我们称之为 top_diff 。
Figure 7. Python code for the Loss calcualtion
4.结论
我们已经看到了神经网络的图形表示如何让我们容易地看到反向传播是如何发生的,以及如何通过图形的小组件使用链规则来容易地计算传播。
只剩下最后一件事,我真的鼓励你去看,特别是现在你已经到了这一步。我建议你到下一章去关注反向传播过程中矩阵的维数,去理解所有这些矩阵的转置和乘法是从哪里来的。
神经网络:问题与解决方案
虽然人工神经网络的概念从 20 世纪 50 年代就已经存在,但直到最近我们才有能力将理论付诸实践。神经网络应该能够模仿任何连续的功能。但是很多时候,我们会被表现不佳的网络所困扰,或者要花很多时间才能得到满意的结果。人们应该从统计学的角度来处理这个问题,而不是凭直觉去考虑网络结构中应该发生的变化。第一步应该是对数据进行适当的预处理。除了均值归一化和缩放,主成分分析可能有助于加速训练。如果数据的维数减少到这样的程度,即仍然保留适当的方差,则可以节省空间,而不会对数据的质量造成很大的损害。此外,当向神经网络提供较少的数据时,可以更快地训练神经网络。
通过使用奇异值分解将训练数据的协方差矩阵分解成三个矩阵,可以实现维数的降低。第一个矩阵应该包含特征向量。此外,矩阵中存在的向量组是正交的,因此它们可以被视为基向量。我们从这个矩阵中挑选出前几个向量,其数量等于我们希望将数据减少到的维数。用上一步得到的矩阵对原始矩阵(具有原始维数)进行变换,我们得到一个新的矩阵,它既降维又线性变换。
The sum of the lengths of the blue lines is to be minimised (2D to 1D)
上述步骤本质上是数学性的,但本质上我们只是将数据从较高的维度“投影”到较低的维度,类似于将平面中的点投影到拟合良好的直线上,使点必须“行进”的距离最小化。
PCA 的倍频程实现是:
function [U, S] = pca(X)
[m, n] = size(X);
U = zeros(n);
S = zeros(n);
Sigma=(1/m)*(X' * X);
[U, S, V] =svd(Sigma);
endfunction Z = projectData(X, U, K)
Z = zeros(size(X, 1), K);
U_reduce = U(:, 1:K);
Z = X*U_reduce;
endload ('<data>'); % Loads the dataset into
% variable X
[X_norm, mu, sigma] = Normalise(X); % Performs normalisation
[U, S] = pca(X_norm); % Performs PCA
K = input("Enter reduced dimension");
Z = projectData(X_norm, U, K);
人们可以使用主成分分析将数据简化为 3D 或 2D 来可视化。但是,更推荐的方法是利用 t 分布随机邻居嵌入,它基于概率分布,不同于 PCA。t-SNE 试图最小化高维和降维条件概率之间的差异。
Conditional probability in the higher dimension
条件概率对于靠得很近的点来说是高的(通过它们的欧几里德距离来测量),而对于相距很远的点来说是低的。根据获得的分布对点进行分组。选择方差,使得密集区域中的点与稀疏区域中的点相比具有较小的方差。
尽管 George Cybenko 在 1989 年证明了具有甚至单个隐藏层的神经网络可以逼近任何连续函数,但是可能希望将更高次的多项式特征引入到网络中,以便获得更好的预测。可以考虑增加隐藏层的数量。事实上,网络的层数等于它应该能够表示的多项式的最高次数。虽然这也可以通过增加现有层中的神经元数量来实现,但与向网络添加隐藏层相比,这将需要更多的神经元(因此增加了计算时间),以近似具有类似误差量的函数。另一方面,使神经网络“深”会导致不稳定的梯度。这可以分为两部分,即消失和爆发的梯度问题。
神经网络的权重通常用随机值初始化,具有平均值 0 和标准偏差 1,粗略地置于高斯分布上。这确保了大多数权重在-1 和 1 之间。sigmoid 函数给出的最大导数为 0.25(当输入为零时)。这与权重属于有限范围的事实相结合,有助于确保其乘积的绝对值也小于 0.25。感知器的梯度包括许多这样的项的乘积,每个项小于 0.25。我们越深入图层,这样的术语会越来越多,导致渐变消失的问题。
Backpropagation of weights
本质上,外部隐藏层(更接近输入层)的感知器的梯度将由更深层的梯度和分配给它们之间的每个链接的权重的乘积之和给出。因此,很明显,浅层的梯度很小。这将导致他们的体重在学习过程中变化较小,并在适当的时间内变得几乎停滞不前。第一层应该承载大部分信息,但我们看到它得到的训练最少。因此,梯度消失的问题最终导致网络的死亡。
训练时可能会出现重量超过 1 的情况。在这种情况下,人们可能会想消失的渐变怎么还会产生问题。嗯,这可能会导致爆炸梯度问题,在早期层梯度变得巨大。如果权重很大,并且偏差使得它与激活函数的 sigmoid 的导数的乘积也保持在较高的一侧,那么这个问题就会发生。但是,另一方面,这有点难以实现,因为增加的权重可能会导致激活函数的输入值更高,而 sigmoid 的导数会非常低。这也有助于确立这样一个事实,即消失梯度问题是难以预防的。为了解决这个问题,我们选择其他激活函数,避免使用 sigmoid。
虽然 sigmoid 是一种流行的选择,因为它将输入压缩在 0 和 1 之间,并且它的导数可以写成 sigmoid 本身的函数,但是依赖于它的神经网络可能会遭受不稳定的梯度。此外,sigmoid 输出不是零中心的,它们都是正的。这意味着,所有的梯度要么是正的,要么是负的,这取决于下一层单元的梯度。
最推荐使用的激活功能是 Maxout 。Maxout 维护两组参数。使用产生更高值的那个作为激活函数的输入。此外,权重可以根据某些输入条件而变化。一种这样的尝试导致泄漏的整流线性单元。在这种特殊情况下,当输入大于 0 时,梯度保持为 1,当输入小于 0 时,梯度得到一个小的负斜率,与输入成比例。
在神经网络中遇到的另一个麻烦是内部协变量移位,特别是当它们很深的时候。随着训练的进行,输入的统计分布保持变化。这可能会导致领域发生重大变化,从而降低培训效率。该问题的解决方案是对每个小批量进行标准化。我们计算所有这些批次的平均值和方差,而不是整个数据。在输入到几乎每一个隐藏层之前,输入被标准化。这个过程通常被称为批量正火。应用批量标准化也有助于克服渐变消失的问题。
可以通过实现退出来提高正则化。通常,网络中的某些节点从神经网络的一些或所有层被随机关闭。因此,在每次迭代中,我们得到一个新的网络,而最终的网络(在训练结束时获得)是所有这些网络的组合。这也有助于解决过度拟合的问题。
无论采用什么样的调整,都必须始终跟踪网络中死亡神经元的百分比,并相应地调整学习速率。
可以对参数进行某些诊断,以获得更好的统计数据。图中的偏差和方差是这里的两个重要因素。它们可以通过绘制曲线来确定,该曲线具有关于训练和交叉验证数据集的损失函数(没有正则化)的输出对训练样本的数量。
(i) High bias (ii) High variance
在上图中,红色曲线代表交叉验证数据,而蓝色用于标记训练数据集。第一个数字是当架构遭受高偏置时粗略获得的数字。这意味着,该架构很差,因此即使在训练数据集上,它也给出相当高的错误。向网络中添加更多要素(如添加更多隐藏图层,从而引入多项式要素)可能会很有用。如果方差很大,这意味着训练的参数很好地符合训练集,但在“看不见的”数据(训练集或验证集)上测试时表现不佳。这可能是因为模型“过度拟合”了训练数据。获取更多的数据可以作为一种解决办法。在这种情况下,减少网络中隐藏层的数量也可能是有用的。玩正则化参数也会有所帮助。增加它的值可以修正高方差,而减少它应该有助于修正高偏差。
绘制诊断曲线的倍频程实现将是:
function [error_train, error_val] = ...
learningCurve(X, y, Xval, yval, lambda)
m = size(X, 1);
error_train = zeros(m, 1);
error_val = zeros(m, 1);
for i = 1:m
X_here = X(1:i,:);
y_here = y(1:i);
theta = train(X_here, y_here, lambda);
error_train(i) = LossFunction(X_here,y_here,theta,0);
error_val(i) = LossFunction(Xval,yval,theta,0);
end;
endlambda = input("Enter regularisation parameter");
[theta] = train(X_poly, y, lambda);graphics_toolkit "gnuplot";
figure(1);
[error_train, error_val] = ...
learningCurve(X_poly, y, X_poly_val, yval, lambda); plot(1:m, error_train, 1:m, error_val);
xlabel('Number of training examples'); ylabel('Error');
legend('Train', 'Cross Validation');
尽管已经注意到大量的训练数据可以提高任何网络的性能,但是获得大量数据可能是昂贵且耗时的。在网络遭受高偏差或消失梯度问题的情况下,更多的数据将是无用的。因此,简单的数学应该被实现,因为它将指导我们应该下降到哪一步。
**参考文献:
*** 机器学习,斯坦福大学
- 卷积神经网络用于视觉识别,斯坦福大学
- 迈克尔·a·尼尔森,《神经网络与深度学习》,决心出版社,2015 年
- 批量归一化——什么玩意儿?(作者卡尔·n .)
*论文摘要 →用于文本分类的字符级卷积网络(作者尼尚特·尼基尔 )
代码库:https://github.com/americast/ML_ANg
预测市场的神经网络
机器学习和深度学习已经成为量化对冲基金常用的新的有效策略,以实现利润最大化。作为一名人工智能和金融爱好者,这是一个令人兴奋的消息,因为它结合了我感兴趣的两个领域。本文将介绍如何使用神经网络来预测股票市场,特别是股票(或指数)的价格。本帖基于我的 GitHub 中的 python 项目 ,在这里可以找到完整的 python 代码以及如何使用该程序。此外,更多类似的内容,请查看我自己的页面: 工程师 Quant
金融中的神经网络
金融是高度非线性的,有时股票价格数据甚至看起来完全是随机的。传统的时间序列方法,如 ARIMA 和 GARCH 模型,只有当序列是平稳的时才有效,这是一个限制性的假设,需要通过取对数回归(或其他变换)对序列进行预处理。然而,在实时交易系统中实现这些模型的主要问题是,随着新数据的增加,不能保证稳定性。
这是通过使用不需要使用任何平稳性的神经网络来解决的。此外,神经网络本质上可以有效地发现数据之间的关系,并使用它来预测(或分类)新数据。
典型的全栈数据科学项目具有以下工作流程:
- 数据采集——这为我们提供了功能
- 数据预处理——使数据可用的一个经常令人畏惧但又必要的步骤
- 开发和实现模型——我们选择神经网络的类型和参数
- 回溯测试模型——任何交易策略中非常关键的一步
- 优化—寻找合适的参数
我们的神经网络的输入数据是过去十天的股票价格数据,我们用它来预测第二天的股票价格数据。
数据采集
幸运的是,这个项目所需的股票价格数据在 Yahoo Finance 中很容易找到。这些数据可以通过使用他们的 Python API 获得,也可以直接从他们的网站上获得。
数据预处理
在我们的例子中,我们需要将数据分解成十个价格和第二天价格的训练集。我通过定义一个类Preprocessing
,将它分解成训练和测试数据,并定义一个方法get_train(self, seq_len)
,它返回训练数据(输入和输出)作为numpy
数组,给定一个特定的窗口长度(在我们的例子中是 10)。完整代码如下:
def gen_train(self, seq_len):
*"""
Generates training data* ***:param*** *seq_len: length of window* ***:return****: X_train and Y_train
"""* for i in range((len(self.stock_train)//seq_len)*seq_len - seq_len - 1):
x = np.array(self.stock_train.iloc[i: i + seq_len, 1])
y = np.array([self.stock_train.iloc[i + seq_len + 1, 1]], np.float64)
self.input_train.append(x)
self.output_train.append(y)
self.X_train = np.array(self.input_train)
self.Y_train = np.array(self.output_train)
同样,对于测试数据,我定义了一个返回测试数据X_test
和Y_test
的方法。
神经网络模型
对于这个项目,我使用了两个神经网络模型:多层感知器(MLP)和长期短期模型(LSTM)。我将简要介绍这些模型是如何工作的,但要阅读 MLPs 是如何工作的,请查看这篇 文章 。对于 LSTMs,请查看 Jakob Aungiers 的这篇优秀的 文章 。
MLPs 是最简单的神经网络形式,其中输入被输入到模型中,并且使用特定的权重,值通过隐藏层被前馈以产生输出。学习来自于通过隐藏层的反向传播来改变每个神经元之间的权重值。MLP 的一个问题是缺少“内存”。不知道以前的训练数据中发生了什么,也不知道这会如何影响新的训练数据。在我们的模型中,一个数据集和另一个数据集中的十天数据之间的差异可能是重要的(例如),但是 MLP 不具有分析这些关系的能力。
这就是 LSTMs 或一般的递归神经网络(RNNs)的用武之地。rnn 具有存储关于数据的某些信息以供以后使用的能力,这扩展了网络在分析股票价格数据之间关系的复杂结构方面的能力。RNNs 的一个问题是消失梯度问题。这是因为当层数增加时,学习率(小于 1 的值)会增加几倍,从而导致梯度不断降低。LSTMs 解决了这一问题,使其更加有效。
实现模型
为了实现这些模型,我选择了keras
,因为它使用了向网络添加层的思想,而不是一次定义整个网络。这使我们可以快速改变层的数量和类型,这在优化网络时非常方便。
使用股票价格数据的一个重要步骤是将数据标准化。这通常意味着你减去平均值,然后除以标准差,但是在我们的例子中,我们希望能够在一段时间的实时交易中使用这个系统。因此,采用统计矩可能不是归一化数据的最准确方法。所以我只是将整个数据除以 200(一个任意的数字,使一切都变小)。虽然这看起来像是无中生有,但它仍然可以有效地确保神经网络中的权重不会变得太大。
让我们从更简单的 MLP 开始。在keras
中,这是通过制作一个序列模型并在其上添加密集层来实现的。完整代码如下:
model = tf.keras.models.Sequential()model.add(tf.keras.layers.Dense(100, activation=tf.nn.relu))
model.add(tf.keras.layers.Dense(100, activation=tf.nn.relu))
model.add(tf.keras.layers.Dense(1, activation=tf.nn.relu))
model.compile(optimizer="adam", loss="mean_squared_error")
这才是 keras 的优雅真正展现的地方。仅仅用这五行代码,我们就创建了一个有两个隐藏层的 MLP,每个隐藏层有一百个神经元。简单介绍一下优化器。Adam optimizer 在机器学习社区中越来越受欢迎,因为与传统的随机梯度下降相比,它是一种更有效的优化算法。通过观察随机梯度下降的两个其他扩展的优点,可以最好地理解这些优点:
- 自适应梯度算法 (AdaGrad),保持每个参数的学习率,提高稀疏梯度问题(如自然语言和计算机视觉问题)的性能。
- 均方根传播 (RMSProp),其还保持基于权重的梯度的最近幅度的平均值(例如,其变化有多快)调整的每个参数的学习速率。这意味着该算法在在线和非平稳问题(例如噪声)上表现良好。
可以认为 Adam 结合了上述扩展的优点,这就是为什么我选择 Adam 作为我的优化器。
现在我们需要用我们的训练数据来拟合模型。同样,keras 使它变得简单,只需要以下代码:
model.fit(X_train, Y_train, epochs=100)
一旦我们符合我们的模型,我们需要根据我们的测试数据来评估它,看看它的表现如何。这由以下人员完成
model.evaluate(X_test, Y_test)
您可以使用来自评估的信息来评估模型预测股票价格的能力。
对于 LSTM 模型,过程是相似的,因此我将在下面发布代码,留下解释供您阅读:
model = tf.keras.Sequential()
model.add(tf.keras.layers.LSTM(20, input_shape=(10, 1), return_sequences=True))
model.add(tf.keras.layers.LSTM(20))
model.add(tf.keras.layers.Dense(1, activation=tf.nn.relu))
model.compile(optimizer="adam", loss="mean_squared_error")
model.fit(X_train, Y_train, epochs=50)
model.evaluate(X_test, Y_test)
需要注意的重要一点是,keras 要求输入数据具有特定的维度,由您的模型决定。使用 numpy 重塑数据至关重要。
对模型进行回溯测试
现在,我们已经使用训练数据拟合了模型,并使用测试数据对模型进行了评估,我们可以通过使用新数据对模型进行回溯测试来进一步进行评估。这可以简单地通过
def back_test(strategy, seq_len, ticker, start_date, end_date, dim):
*"""
A simple back test for a given date period* ***:param*** *strategy: the chosen strategy. Note to have already formed the model, and fitted with training data.* ***:param*** *seq_len: length of the days used for prediction* ***:param*** *ticker: company ticker* ***:param*** *start_date: starting date* ***:type*** *start_date: "YYYY-mm-dd"* ***:param*** *end_date: ending date* ***:type*** *end_date: "YYYY-mm-dd"* ***:param*** *dim: dimension required for strategy: 3dim for LSTM and 2dim for MLP* ***:type*** *dim: tuple* ***:return****: Percentage errors array that gives the errors for every test in the given date range
"""* data = pdr.get_data_yahoo(ticker, start_date, end_date)
stock_data = data["Adj Close"]
errors = []
for i in range((len(stock_data)//10)*10 - seq_len - 1):
x = np.array(stock_data.iloc[i: i + seq_len, 1]).reshape(dim) / 200
y = np.array(stock_data.iloc[i + seq_len + 1, 1]) / 200
predict = strategy.predict(x)
while predict == 0:
predict = strategy.predict(x)
error = (predict - y) / 100
errors.append(error)
total_error = np.array(errors)
print(f"Average error = {total_error.mean()}")
然而,这个回溯测试是一个简化的版本,而不是一个完整的回溯测试系统。对于成熟的回测系统,你需要考虑生存偏差、前瞻偏差、市场制度变化和交易成本等因素。因为这仅仅是一个教育项目,一个简单的回溯测试就足够了。但是,如果你对建立一个完整的回溯测试系统有任何疑问,请随时联系我。
下面展示了我的 LSTM 模型在预测二月份苹果股票价格时的表现
对于一个没有优化的简单 LSTM 模型来说,这是一个相当好的预测。它真正向我们展示了神经网络和机器学习模型在模拟参数之间的复杂关系方面有多么强大。
超参数调谐
优化神经网络模型对于提高模型在样本外测试中的性能通常很重要。我没有在我的项目开源版本中包括调优,因为我希望它对那些阅读它的人是一个挑战,以便继续尝试优化模型,使其性能更好。对于那些不了解优化的人来说,它涉及到寻找使模型性能最大化的超参数。有几种方法可以搜索这些理想的超参数,从网格搜索到随机方法。我强烈地感觉到,学习优化模型可以将您的机器学习知识提升到一个新的水平,因此,我将挑战您提出一个优化的模型,超越我在上面图表中的表现。
结论
机器学习不断发展,每天都有新的方法被开发出来。不断更新我们的知识是至关重要的,最好的方法是为有趣的项目建立模型,如股票价格预测。虽然上面的 LSTM 模型还不够好,不能用于现场交易,但是开发这样一个模型所建立的基础可以帮助我们建立更好的模型,也许有一天可以用在我们的交易系统中。
神经网络 V:反向传播
神经网络简介
这个关于神经网络的系列帖子是脸书 PyTorch 挑战赛期间笔记收集的一部分,在 Udacity 的深度学习纳米学位项目之前。
内容
- 简介
- 反向传播中的维数
- 权重更新
1.介绍
像往常一样,让我们首先介绍我们正在使用的神经网络的场景:
Figure 1. Artificial Neural Network with shape [2, 3, 1]
及其图形表示:
Figure 2. Graph representation of the [2, 3, 1] neural network.
我们需要记录我们上一章做了什么,因为上一章的输出是这一章的输入。所以,让我们也拿回我们需要继续进行的工具。
我们已经知道这些值的来源,因为我们根据输入、层、批次和输出的维度定义了 doe 矩阵的形状。我们将再次包括该等式,以便于理解:
2.反向传播中的维数
我们将在最后一章中介绍的内容是,通过前面公式描述的第 4 章中的图形方法中的反向投影过程,对第 3 章进行同样的可视化。
我们讨论的第一个模式是 addGate:
现在我们需要知道如何用数学方法实现这些乘法。如果我们看一下两个输入在每个突触中的线性关系,在下图中:
我们可以看到,我们是如何首先根据偏差 b2 在梯度之前进行转置和放置的,因为我们打算对不同观测中的所有误差进行平均,以提供一个值来更新该偏差。
另一方面,对于 a2W2 项,我们只是将反向传播误差与一列 1 进行矩阵乘法运算,因为我们希望跟踪每个不同的反向传播误差,以便继续反向传播。
我们已经知道 addGates 的行为类似于分布器,并且两种情况下的局部导数都是 1。其结果我们从现在起可以称之为‘反向传播误差’δ。
下一步是 mulGate:
(*)我们已经应用了从 J 开始的整个链规则。从现在开始,我们将简化它,只使用顶部差异和局部梯度。
现在我们要确定这两个方程右边的导数。请看图 3 中的 LHS,这两项之间有线性关系,我们可以确定斜率。从数学上讲,我们通过转置矩阵来实现这个操作。
Figure 3. Back-Propagation Error through Connection between Layers
如果我们把它翻译成人类的语言,试着想想图 1 中发生了什么,我们可以说:
1.我们对不同迭代的乘积a2δ进行平均,以定义 a2W2 相对于 W2 的变化率。这就是为什么我们有 3 个维度,隐藏神经元的每个输出都有一个维度;并且它们中的每一个都增加了不同观测的误差。
2.我们将每个观察值的反向传播误差分割给不同的隐藏神经元,这就是为什么我们有 (4x3) 矩阵(4 个观察值和 3 个神经元)。
Figure 4. Back-Propagation through Activated Neuron
非常重要的是要看到,在这个场合,我们做的是标量积,而不是矩阵间的积。这是因为在向前传递时,激活函数是一个基于元素的操作,在向后传递时也是如此。
下一步,我们面临另一个添加门:
看一看,正如我们所知,addGates 是反向传递中的分发器,两次传播将是相同的值。我们应用了与第一个 addGate 完全相同的过程。
最后一步是另一个 mulGate:
X 的最后转置用于对隐藏层中每个神经元的每个观察的每个输入的每个观察的每个反向传播误差进行平均。
3.更新权重
我们现在应该前进到最后一步,学习。使用称重更新公式:
因此,将此应用于我们的两层砝码:
Figure 5. Weights updated