咱们都据说过深度学习,可是有多少人知道深度信念网络是什么?让咱们从本章开始回答这个问题。深度信念网络是一种很是先进的机器学习形式,其意义正在迅速演变。做为一名机器学习开发人员,对这个概念有必定的了解是很重要的,这样当您遇到它或它遇到您时就会很熟悉它!算法
在机器学习中,深度信念网络在技术上是一个深度神经网络。咱们应该指出,深度的含义,当涉及到深度学习或深度信念时,意味着网络是由多层(隐藏的单位)组成的。在深度信念网络中,这些链接在一层内的每一个神经元之间,而不是在不一样的层之间。一个深度信念网络能够被训练成无监督学习,以几率重建网络的输入。这些层就像“特征检测器”同样,能够识别或分类图像、字母等等。c#
在本章,咱们将包括:数组
受限玻尔兹曼机网络
在c#中建立和培训一个深度信念网络框架
构建深度信念网络的一种流行方法是将其做为受限玻尔兹曼机(RBMs)的分层集合。这些RMBs的功能是做为自动编码器,每一个隐藏层做为下一个可见层。深度信念网络将为训练前阶段提供多层RBMs,而后为微调阶段提供一个前馈网络。训练的第一步将是从可见单元中学习一层特性。下一步是从之前训练过的特性中获取激活,并使它们成为新的可见单元。而后咱们重复这个过程,这样咱们就能够在第二个隐藏层中学习更多的特性。而后对全部隐藏层继续执行该过程。机器学习
咱们应该在这里提供两条信息。函数
首先,咱们应该稍微解释一下什么是自动编码器。自动编码器是特征学习的核心。它们编码输入(输入一般是具备重要特征的压缩向量)和经过无监督重构的数据学习。工具
其次,咱们应该注意到,将RBMs堆积在一个深度信念网络中只是解决这个问题的一种方法。将限制线性单元(ReLUs)叠加起来,再加上删除和训练,而后再加上反向传播,这又一次成为了最早进的技术。我再说一遍,由于30年前,监督方法是可行的。与其让算法查看全部数据并肯定感兴趣的特征,有时候咱们人类实际上能够更好地找到咱们想要的特征。性能
我认为深度信念网络的两个最重要的特性以下:学习
l 有一个有效的,自上而下的逐层学习过程,生成权重。它决定了一个层中的变量如何依赖于它上面层中的变量。
l 当学习完成后,每一层变量的值均可以很容易地经过一个自底向上的单次遍历推断出来,该遍历从底层的一个观察到的数据向量开始,并使用生成权值逆向重构数据。
说到这里,咱们如今来谈谈RBMs以及通常的玻尔兹曼机。
玻尔兹曼机是一个递归神经网络,具备二进制单元和单元之间的无向边。无向是指边(或连接)是双向的,它们没有指向任何特定的方向。
下面是一个带有无向边的无向图:
玻尔兹曼机是最先可以学习内部表征的神经网络之一,只要有足够的时间,它们就能解决难题。可是,它们不擅长伸缩,这就引出了咱们的下一个主题,RBMs。
引入RBMs是为了解决玻尔兹曼机器没法伸缩的问题。它们有隐藏层,每一个隐藏单元之间的链接受到限制,但不在这些单元以外,这有助于提升学习效率。更正式地说,咱们必须深刻研究一些图论来正确地解释这一点。
RBMs的神经元必须造成二分图,这是图论的一种更高级的形式;来自这两组单元(可见层和隐藏层)中的每一组的一对节点之间可能具备对称链接。任何组中的节点之间都不能有链接。二分图,有时称为生物图,是将一组图顶点分解为两个不相交的集合,使同一集合内没有两个顶点相邻。
这里有一个很好的例子,它将有助于可视化这个主题。
注意同一组内没有链接(左边是红色的,右边是黑色的),但两组之间有链接:
更正式地说,RBM是所谓的对称二分图。这是由于全部可见节点的输入都传递给全部隐藏节点。咱们说对称是由于每一个可见节点都与一个隐藏节点相关。
假设咱们的RBM显示了猫和狗的图像,咱们有两个输出节点,每一个动物一个。在咱们向前经过学习,咱们的RBM问本身:“对于我看到的像素,我应该向猫或狗发送更强的权重信号吗?”在它想知道“做为一只狗,我应该看到哪一个像素分布?”个人朋友们,这就是今天关于联合几率的课: 在给定A和给定X的状况下X的同时几率。在咱们的例子中,这个联合几率表示为两层之间的权重,是RBMs的一个重要方面。
咱们如今来谈谈重构,这是rbms的一个重要部分。在咱们所讨论的示例中,咱们正在学习一组图像出现哪些像素组(即打开)。当一个隐藏层节点被一个重要的权重激活时(不管决定打开它的权重是什么),它表示正在发生的事情的共同发生,在咱们的例子中,是狗仍是猫。若是这是一只猫,尖耳朵、圆脸、小眼睛可能就是咱们要找的。大耳朵+长尾巴+大鼻子可能会让你的形象变成一只狗。这些激活表示RBM“认为”原始数据的样子。实际上,咱们正在重建原始数据。
咱们还应该迅速指出,RBM有两个偏见,而不是一个。这是很是重要的,由于这是区别于其余自动编码算法。隐藏的误差帮助咱们的RBM在向前传递时产生咱们须要的激活,而可见的层误差帮助咱们在向后传递时学习正确的重构。隐藏的误差很重要,由于它的主要工做是确保不管咱们的数据有多么稀疏,一些节点都会被触发。稍后您将看到这将如何影响一个深层信仰网络的梦想。
一旦咱们的RBM了解了输入数据的结构,它与在第一个隐藏层中进行的激活相关,数据就会传递到下一个隐藏层。第一个隐藏层而后成为新的可见层。咱们在隐藏层中建立的激活如今成为咱们的输入。它们将乘以新隐藏层中的权重,以产生另外一组激活。
这个过程在咱们的网络的全部隐藏层中继续进行。隐藏层变成可见层,咱们有另外一个隐藏层,咱们将使用它的权重,而后重复。每一个新的隐藏层都会产生调整后的权重,直到咱们可以识别来自前一层的输入为止。
为了更详细地说明,这在技术上被称为无监督的、贪婪的、分层的培训。改进每一层的权值不须要输入,这意味着不涉及任何类型的外部影响。这进一步意味着咱们应该可以使用咱们的算法来训练之前没有见过的无监督数据。
正如咱们一直强调的,咱们拥有的数据越多,咱们的结果就越好!随着每一层图像的质量愈来愈好,也愈来愈准确,咱们就能够更好地经过每个隐藏层来提升咱们的学习能力,而权重的做用就是引导咱们在学习的过程当中进行正确的图像分类。
可是在讨论重构时,咱们应该指出,每次重构工做中的一个数字(权重)都是非零的,这代表咱们的RBM从数据中学到了一些东西。在某种意义上,您能够像处理百分比指标同样处理返回的数字。数字越大,算法对它所看到的东西就越有信心。记住,咱们有咱们要返回的主数据集,咱们有一个参考数据集用于咱们的重建工做。当咱们的RBM遍历每一个图像时,它还不知道它在处理什么图像;这就是它想要肯定的。
让咱们花一点时间来澄清一些事情。当咱们说咱们使用贪婪算法时,咱们真正的意思是咱们的RBM将采用最短路径来得到最佳结果。咱们将从所看到的图像中随机抽取像素,并测试哪些像素引导咱们找到正确答案。
RBM将根据主数据集(测试集)测试每一个假设,这是咱们的正确最终目标。请记住,每一个图像只是咱们试图分类的一组像素。这些像素包含了数据的特征和特征。例如,一个像素能够有不一样的亮度,其中深色像素可能表示边框,浅色像素可能表示数字,等等。
但若是事情不像咱们想的那样,会发生什么呢?若是咱们在给定步骤中学到的东西不正确会发生什么?若是出现这种状况,就意味着咱们的算法猜错了。咱们要作的就是回去再试一次。这并不像看上去那么糟糕,也不像看上去那么耗时。
固然,错误的假设会带来时间上的代价,但最终的目标是咱们必须提升学习效率,并在每一个阶段减小错误。每个错误的加权链接都会受到惩罚,就像咱们在强化学习中所作的那样。这些链接会减小重量,再也不那么强。但愿下一个遍历能够在减小偏差的同时提升精度,而且权重越大,影响就越大。
假设咱们对数字图像进行分类,也就是数字。有些图像会有曲线,好比二、三、六、八、9等等。其余数字,如一、4和7,则不会。这样的知识是很是重要的,由于咱们的RBM,会用它来不断提升本身的学习,减小错误。若是咱们认为咱们处理的是数字2,那么这个路径的权值就会比其余路径的权值更重。这是一个极端的过分简化,但但愿它足以帮助你理解咱们将要开始的内容。
当咱们把全部这些放在一块儿,咱们如今有了一个深层信仰网络的理论框架。虽然咱们比其余章节更深刻地研究了理论,可是正如您所看到的咱们的示例程序所工做的那样,它将开始变得有意义。您将更好地准备在应用程序中使用它,了解幕后发生的事情。
为了展现深度信念网络和RBMs,咱们将使用Mattia Fagerlund编写的出色的开源软件SharpRBM。这个软件对开源社区作出了难以想象的贡献,我绝不怀疑您将花费数小时甚至数天的时间来使用它。这个软件附带了一些使人难以置信的演示。在本章中,咱们将使用字母分类演示。
下面的截图是咱们的深度信念测试应用程序。有没有想过电脑睡觉时会梦到什么?
程序的左上角是咱们指定要训练的图层的区域。咱们有三个隐藏层,它们都须要在测试以前进行适当的训练。咱们能够一次训练一层,从第一层开始。训练得越多,你的系统就会越好:
训练选项以后的下一部分是咱们的进展。当咱们在训练时,全部相关的信息,如生成,重构偏差,检测器偏差,学习率,都显示在这里:
下一个是咱们的特性检测器的绘图,若是选中Draw复选框,它将在整个训练过程当中更新本身:
当开始训练一个层时,咱们将注意到重构和特征检测器基本上是空的。他们会随着咱们的训练不断完善本身。记住,咱们正在重建咱们已经知道是真实的东西!随着训练的继续,重构的数字变得愈来愈清晰,咱们的特征检测器也愈来愈清晰:
下面是训练期间应用程序的快照。如图所示,这是在第31代,重建的数字是很是明确的。
它们仍然不完整或不正确,但能够看到咱们取得了多大的进步:
电脑作梦时会梦到什么?对咱们来讲,直觉是一个特征,它容许咱们看到计算机在重构阶段在想什么。当程序试图重建咱们的数字时,特征检测器自己将在整个过程当中以各类形式出现。咱们在dream window中显示的就是这些形式(红色圆圈表示):
咱们花了不少时间查看应用程序的屏幕截图。我想是时候看看代码了。让咱们先看看如何建立DeepBeliefNetwork对象自己:
DeepBeliefNetwork = new DeepBeliefNetwork(28 * 29, 500, 500, 1000);
一旦建立了这个,咱们须要建立咱们的网络训练器,咱们根据咱们正在训练的层的权重来作这件事:
DeepBeliefNetworkTrainer trainer = new DeepBeliefNetworkTrainer(DeepBeliefNetwork, DeepBeliefNetwork?.LayerWeights?[layerId], inputs);
这两个对象都在咱们的主TrainNetwork循环中使用,这是应用程序中大部分活动发生的部分。这个循环将继续,直到被告知中止。
private void TrainNetwork(DeepBeliefNetworkTrainer trainer) { try { Stopping = false; ClearBoxes(); _unsavedChanges = true; int generation = 0; SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS | EXECUTION_STATE.ES_SYSTEM_REQUIRED);
while (Stopping == false) { Stopwatch stopwatch = Stopwatch.StartNew(); TrainingError error = trainer?.Train(); label1.Text = string.Format( "Gen {0} ({4:0.00} s): ReconstructionError= {1:0.00}, DetectorError={2:0.00}, LearningRate={3:0.0000}", generation, error.ReconstructionError, error.FeatureDetectorError, trainer.TrainingWeights.AdjustedLearningRate, stopwatch.ElapsedMilliseconds / 1000.0); Application.DoEvents(); ShowReconstructed(trainer); ShowFeatureDetectors(trainer); Application.DoEvents(); if (Stopping) { break; }
generation++; }
DocumentDeepBeliefNetwork(); }
finally { SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS); } }
在前面的代码中,咱们突出显示了trainer.Train()方法,它是一个基于数组的学习算法,以下所示:
public TrainingError Train() { TrainingError trainingError = null; if (_weights != null) { ClearDetectorErrors(_weights.LowerLayerSize, _weights.UpperLayerSize); float reconstructionError = 0; ParallelFor(MultiThreaded, 0, _testCount, testCase => { float errorPart = TrainOnSingleCase(_rawTestCases,_weights?.Weights, _detectorError,testCase,
_weights.LowerLayerSize,_weights.UpperLayerSize, _testCount); lock (_locks?[testCase % _weights.LowerLayerSize]) { reconstructionError += errorPart; } }
); float epsilon = _weights.GetAdjustedAndScaledTrainingRate(_testCount); UpdateWeights(_weights.Weights,_weights.LowerLayerSize, _weights.UpperLayerSize,_detectorError, epsilon); trainingError = new TrainingError(_detectorError.Sum(val =>Math.Abs(val)), reconstructionError); _weights?.RegisterLastTrainingError(trainingError); return trainingError; }
return trainingError; }
此代码使用并行处理(突出显示的部分)并行地训练单个案例。这个函数负责处理输入层和隐藏层的更改,正如咱们在本章开头所讨论的。它使用TrainOnSingleCase函数,以下图所示:
private float TrainOnSingleCase(float[] rawTestCases, float[] weights, float[] detectorErrors, int testCase, int lowerCount, int upperCount, int testCaseCount) { float[] model = new float[upperCount]; float[] reconstructed = new float[lowerCount]; float[] reconstructedModel = new float[upperCount]; int rawTestCaseOffset = testCase * lowerCount; ActivateLowerToUpperBinary(rawTestCases, lowerCount, rawTestCaseOffset, model, upperCount, weights); // Model ActivateUpperToLower(reconstructed, lowerCount, model,upperCount, weights); // Reconstruction ActivateLowerToUpper(reconstructed, lowerCount, 0, reconstructedModel, upperCount, weights); // Reconstruction model return AccumulateErrors(rawTestCases, lowerCount,rawTestCaseOffset, model, upperCount, reconstructed, reconstructedModel, detectorErrors); // Accumulate detector errors }
最后,咱们在处理过程当中积累错误,这就是咱们的模型应该相信的和它实际作的之间的区别。
显然,错误率越低越好,对于咱们的图像重建最准确。AccumulateErrors函数以下所示:
private float AccumulateErrors(float[] rawTestCases, int lowerCount, int rawTestCaseOffset, float[] model, int upperCount, float[] reconstructed, float[] reconstructedModel, float[] detectorErrors) { float reconstructedError = 0; float[] errorRow = new float[upperCount]; for (int lower = 0; lower < lowerCount; lower++) { int errorOffset = upperCount * lower; for (int upper = 0; upper < upperCount; upper++) { errorRow[upper] = rawTestCases[rawTestCaseOffset + lower] * model[upper] + // 模型应该相信什么 -reconstructed[lower] * reconstructedModel[upper]; // 模型真正相信什么 } lock (_locks[lower]) { for (int upper = 0; upper < upperCount; upper++) { detectorErrors[errorOffset + upper] -= errorRow[upper]; } } reconstructedError += Math.Abs(rawTestCases[rawTestCaseOffset + lower] - reconstructed[lower]); } return reconstructedError; }
在本章中,咱们学习了RBMs、一些图论,以及如何在c#中建立和训练一个深刻的信念网络。我建议你对代码进行试验,将网络层训练到不一样的阈值,并观察计算机在重构时是如何作梦的。记住,你训练得越多越好,因此花时间在每一层上,以确保它有足够的数据来进行准确的重建工做。
警告:若是启用特性检测器和重构输入的绘图功能,性能将会极速降低。
若是你正在尝试训练你的图层,你可能但愿先在没有可视化的状况下训练它们,以减小所需的时间。相信我,若是你把每个关卡都训练成高迭代,那么可视化会让你感受像一个永恒的过程!在你前进的过程当中,随时保存你的网络。
在下一章中,咱们将学习微基准测试,并使用有史以来最强大的开源微基准测试工具包之一!