关于深度学习的深层误解
我开始写这篇文章,是希望对抗一些关于深度学习(DL)的误解,深度学习是一个机器学习领域,同时被称为银弹和研究炒作。真相在中间的某个地方,我希望我能把水搅浑——至少一点点。重要的是,我希望阐明解决 DL 问题的一些过程,并讨论为什么它在自然语言处理(NLP)、图像识别和机器翻译等领域表现如此好,而在其他领域却失败了。
深度学习不是黑暗艺术
媒体经常将深度学习描绘成世界末日的神奇配方或所有生活问题的解决方案。事实上,它绝不是。此外,尽管 DL 有其奇怪的行为和无法解释的结果,但它最终是由精英驱动的。通过在开发过程中要求证明和推理,您可以在无限数量的模型排列中构建一个健壮的框架。
凡事都有目的(大多数时候)
我看到新从业者表现出的第一个问题是将几个 LSTM(长短期记忆)层、一个 GRU(门控循环单元)层和几个卷积层粉碎在一起,然后坐下来等待神奇的结果。然后沮丧地看着他们的模型 5%的精确度。正如我将在后面讨论的,DL 需要一个更加合理的方法。
通过采用基于整合数据、网络设计和好奇心的方法,你将能够产生惊人的结果。我每天都花时间阅读关于最新方法的新论文和文章,并不断看到推动新设计的巧妙想法。
在 DL 中,不同的层服务于不同的目的。这是一个简单的想法,但对于这个领域的新手来说,这种想法往往会被遗忘。此外,虽然我在设计过程中做了大量的实验,但我总是沿着一条思路进行测试。增加复杂性应该有一个理由(或者至少是一个假设)。它应该用一个可测量的标准来检查,只有这样我才能在最终的设计中接受它。此外,你应该偏爱简单的模型,而不是复杂的模型,因为它们可以达到相同的结果。我一直在测试更小的网络,或者更少的神经元或者更少的层,并将它们与我的最佳设计进行比较。更小的系统也意味着更快的训练(在某些情况下几个数量级),这意味着更多的迭代和更好的最终设计。
您可以创建一些工具来测试小型和大型网络上的新论点,这种理解将推动您对网络设计的全面了解。随着你越来越多地使用神经网络,你将会更好地理解是什么驱动它的行为来完成特定的任务。你开始在你的头脑中绘制一张地图,标明什么时候尝试什么,但前提是你有正确的度量标准和过程来理解。
设计流程
我认为设计过程就像一个孩子试图建造一座砖砌的塔,一路测试。戳,刺激,失败,但总是测试。塔的第二层是不是太不稳了?该不该换基地?考虑拥有有限数量的块是有帮助的。神经网络越小,你训练它的速度就越快,测试它的速度也就越快。仅在需要时增加复杂性。
在 DL 中,这个问题有无限多种可能的解决方案——因此,您处理的网络可以模拟几乎无限的公式集。虽然最终结果可能是离散的,但神经网络生活在一个连续数字的世界中。复杂性往往会迅速增加,而增加的复杂性需要更多的控制。我试着用建立一个新的数据项目的方式来建立我的网络。例如,一个简单的探索性数据分析管道可以是:
接收数据→清理数据→转换数据→呈现数据
所有这些模块都可能涉及许多不同的步骤。如果我在整个过程中没有确保上游过程的质量,整个系统将会失败。所以我一路测试。我测试一切——输入、输出、类、函数。我确保每样东西单独工作,然后一起工作。我知道这不是一个突破性的想法,但它值得指出。一旦我有了一个工作流程,我就会转移到下一个,并在此基础上继续工作。
在深度学习中,我从创建最简单的模型开始。在处理文本分类任务时,我将从最简单的嵌入-编码-参与-预测模型开始。在看到基础模型是如何工作的之后,我将开始在它的基础上进行构建。这可能包括增加隐藏层的大小,加深网络,合并新的网络,改变激活和初始化,用词性标签或其他数字数据增加文本。我可能会让几个神经网络互相反馈,以达到不同的目的,或者改变数据的处理方式,或者将文本矢量化。这样的例子不胜枚举。
对于每一步,我都将它与我的原始模型和我的迭代进行比较。这个过程允许我回顾我的模型的演变,理解它在哪里变得更好(或更差),并通知设计的未来迭代;总是越来越接近全局最小值。
成为数据的主人
在开始试验您的 DL 模型之前,有必要了解给定问题的价值所在。有一个简单的层次结构可以遵循:
数据大于模型设计,模型设计大于参数优化。
如果 A 公司有 B 公司十倍的数据,这个世界上没有一个模型会允许 B 公司胜过 A 公司的模型。同样的想法也适用于数据质量。
如果我花数周时间开发一个基于弱数据质量的模型,它的表现将不如我用几天时间构建的基于高质量数据的模型。在你花数周时间做模型之前,总是问自己是否能得到更多的数据。也许你可以改变你的预处理步骤来提高你已经拥有的数据的质量。这通常会比仅仅通过模型调整获得更快的性能提升。
不仅需要高质量的数据,还需要对数据的深刻理解。你应该花时间开发工具来分析、展示和研究你的输入数据。您应该构建度量和分析工具来理解您的模型的预测。避免将数据仅仅视为输入。所有数据都有隐藏的特征,如果你能提取出来,这些特征具有巨大的预测能力。有时您必须自己完成这项工作,其他时候您可以开发您的 DL 模型,让它自己完成这项工作。把你的数据想象成一种加密算法的输入——但它不是对文本或电子邮件进行加密,而是对世界的运行方式进行编码。每个数据点都是更好理解的线索。
自然语言处理中的文本分类就是一个很好的例子。给你一个几百或者几千字的语料库,每个文本有一个类或者多个类。也许这个类是积极的,消极的,或者中性的,也许你有数百个复杂而错综复杂的类。无论哪种方式,文本都是我们用来确定输出的代码。
当你不再认为你的输入只是文字时,它会变得非常有价值,并为你带来难以置信的结果。这种想法导致了从单词向量到单词级模型、句子级模型、段落级模型的发展。每一个实现都是基于人类理解的工作方式,并在性能增益中表现出来。
与领域专家共度时光
这与前一部分紧密相关,甚至可能更重要——花时间与具有领域知识的人交谈。和他们讨论你的结果,和他们讨论数据。让他们参与设计过程。
你会惊讶于讨论你的模型的设计哲学和你的模型的输出经常会带来快速的成功。这些立竿见影的效果往往转化为准确性的大幅提高。当然,这需要时间投资来教育他们对深度学习的直觉和概念理解,但对我来说这总是值得的。如果你没有领域专家,花时间自己学习。避免试图为你不理解的问题建立一个 DL 模型。
消除深度学习就像在没有灯的迷宫中摸索前进的想法。在现实中,你可以通过遵循正确的方法并试图解决正确的问题来处理深度学习。你应该经常问自己为什么。
虽然我将要描述的方法听起来可能与度量驱动设计的想法相反,但我发现它以最好的方式建立了理解;一直问为什么。
像孩子一样学习
也许我一直提到孩子是因为我和我的妻子在下个月左右就要有我们的女儿了,但是我发现他们是如何学习的一个很好的例子。孩子从子宫里出来就是小科学家。一切都是实验,任何抚养过孩子的人都记得他们的小探险家发出的无尽的一串“为什么”。数据科学家应该都有这种方法,但在实施深度学习时记住这一点尤其重要——深度学习是一个有着令人眼花缭乱的设计选择的领域,可能会导致最勤奋的数据科学家的思维变得迟钝。
一旦我测试了一个有积极效果的设计变更,我会试着找出它为什么有效。这个方法论让我不断提高自己的理解能力。今天,我可能不是某个特定领域的专家,但如果有足够的时间,我总能朝着成为专家的方向努力。
我一直在问为什么,直到我真正理解了选择一个设计决策的基础。通常一个问题引出另一个问题,三天后你对一系列有价值的概念有了新的深刻的理解。这是我最近去过的一个兔子洞的合法例子(如果这些术语对你没有任何意义,请不要担心):
- 为什么我要用 LSTM 编码我的句子?
- LSTM 是如何工作的?
- 规范 LSTM 的最好方法是什么?
- 我应该如何解码 LSTM 输出的序列?
- 最大池与平均池或使用另一个 LSTM 作为我的解码器相比如何?
- GRU 比 LSTM 表现得更好还是更快?为什么?
- 我应该如何初始化我的 LSTM 层?为什么?
- 这个 LSTM 太慢了,我能加速吗?
- 为什么传统的 LSTM 在 GPU 上的运行速度比 Cuda 优化的 LSTM 层慢这么多?
- 我可以使用较低精度的浮点来增加我可以在 GPU 上实现的网络大小吗?
我称之为兔子洞是因为学习路径从一开始就是随机的,不确定的。我只是允许自己探索我的问题。我不担心这是偶然的,而且我对每一个科目的兴趣倍增,我学习答案的速度和效果都很好。这个方法是我一生学习的方式,我发现它总能让我到达正确的地方(最终)。我也带着一套技能到达那里,否则,如果我只专注于预先设计的计划,我不会。这种方法的缺点是耗时,但大多数数据科学家天生就有这种好奇心。一旦我遇到问题,我必须找到答案。
“找到答案或者尝试而死”——平行宇宙中的 50 美分
结果是我现在对第一个问题有了回应;为什么我应该用 LSTM 来编码我的句子。此外,我有另一组半相关问题的答案,允许我进行新的调整和设计更改,以进一步优化。在测试这些调整时,我可能会得到新的和新奇的结果,所以我开始再次探索。如果你这样做的时间足够长,你可以从学习到实验,到设计,到专业知识。很快你就会发现自己正在尝试以前从未尝试过的全新想法。
这个特别的问题让我将我的神经网络在特定问题上的速度提高了四倍,同时提高了 3%的准确率(对我来说这是很大的一个进步)。在此过程中,我还学习了编码器-解码器的设计以及相当多的机器翻译知识。回答我的问题,而不是像传统方法那样预先设定的问题,可以让我连续几个小时保持专注,而不会耗尽我的意志力。
问正确的问题
识别正确的深度学习问题至关重要。DL 在语言任务、图像分类、语音翻译、机器翻译和玩游戏(即国际象棋、围棋、星际争霸)方面非常出色。在传统的机器学习任务中,如信用卡欺诈检测、资产定价和信用评分,它的性能较差。或许在未来,我们将调整我们的模型、优化方法和激活函数,使深度学习更加广泛适用,但目前,它仅限于非常复杂的领域。
DL 之所以表现出色,是因为它可以建模巨大的复杂性。在我确定的所有任务中,数据的不同部分之间有数千甚至数百万的细微差别和相关性。神经网络以一种人类无法做到的方式寻找并利用这些细微差别。此外,虽然传统的机器学习可以应用于这些领域,但它们在复杂性建模方面有很大的局限性。数据科学家通常需要花费数周时间进行特征提取和工程设计,才能让传统模型发挥作用。原因是该网络不具有深度学习网络的相同能力来独自获得特征。
人类设计师经常限制传统的机器学习模型。深度学习消除了很多人类的限制,让人类成为向导而不是老师。现在,在人类开发人员的帮助下,模型可以自由探索并找到解决方案的途径。我的工作是给神经网络提供正确的工具,将一个问题分解成数百万个子问题,然后将它们重新组合成一个解决方案。它的精彩之处在于它既简单又复杂。
一剂有益健康的偏执狂
作为最后,也可能是最重要的主题,您如何确保您的模型运行良好?不幸的是,没有单一的解决方案,但有一个适合我的系统。
当设计一个网络时,我怀疑它不能正常工作。我不断地想,是分数在愚弄我,还是有罕见的长尾事件让我的模型看起来很傻。一个可怕的预测会让你的模型在很多人眼里变得不准确。因此,我构建了以多种方式查看相同预测的工具,制作了图形工具以允许更快的迭代和更快的数据消化,并继续开发工具,直到那种不安的感觉消退到舒适的程度。
有时你永远也到不了那里。这就是为什么你不断调整,你不断寻找缺陷。像一个固执的侦探,不断刺激,测试和寻找漏洞。我向你保证,有比你原来的准确性指标可能会建议更多。单独看每节课,分组看,用精准,回忆,f1-score;见鬼,我甚至有一半时间都在编故事。
关键是要认识到你的模型是复杂的,你只能理解其决策过程的一部分。如果你知道走进去,你就更有可能成功走出去。您的度量导致设计变更,您的设计变更导致更好的模型。
有时,你会了解模型的小利基,以及如何控制它们。有时你不得不接受它并不总是有效。重点通常是缓解,而不是消除。任何一个好的深度学习系统,后台都有一个智能质量保证(QA)系统。这个问答系统是防止用户尴尬预测的最后一道防线。
通过使用适当的 QA 过程,您可以获得有目的地设计所需的度量,在过程中学习,并不断改进您自己和您的模型。在一天结束的时候,也许我们所做的只是搅动那个线性代数锅,但是通过一种由目的和理解驱动的方式去做,我们成功的几率会成倍增加。
在 BigQuery 上用纯 SQL 实现的深度神经网络
在这篇文章中,我们将纯粹用 SQL 实现一个基本的深度神经网络。神经网络训练的端到端步骤(包括前向传递和反向传播)将作为 BigQuery 上的单个 SQL 查询来实现。由于它运行在 Bigquery 上,实际上我们是在 100 到 1000 台服务器上执行分布式神经网络训练。听起来很酷!对吗?
也就是说,请注意,这是一个有趣的项目,用来测试 SQL 和 BigQuery 的局限性,并从声明性数据转换的角度来看神经网络训练。这是在没有任何实际应用的情况下完成的,尽管我将在最后讨论一些实际的研究意义。让我们开始吧。
我们将从一个简单的基于神经网络的分类器开始。它的输入维数为 2,输出为二进制。我们将有一个 2 维的隐藏层和 ReLU 激活函数。输出层将有一个最终带有 softmax 函数的二维输出。我们在实现网络时将遵循的步骤是 Karpathy 的 CS231n 教程中所示的 Python 示例的基于 SQL 的版本。
模型
该模型具有以下参数:
输入隐藏
W
: 2x2 权重矩阵(元素:w_00, w_01, w_10, w_11
)B
: 2x1 偏置矢量(元素:b_0, b_1
)
隐藏到输出
W2
: 2x2 权重矩阵(元素:w2_00, w2_01, w2_10, w2_11
)B2
: 2x1 偏置矢量(元素:b2_0, b2_1
)
训练数据存储在 BigQuery 表中,其中列x1
和x2
具有输入,y 具有输出,如下所示(表名:example_project.example_dataset.example_table
)。
如前所述,我们将把整个训练作为一个 SQL 查询来实现。训练完成后,查询将返回参数值。正如您可能已经猜到的,这将是一个嵌套很深的查询。我们将逐步构建来准备这个查询。我们将从最里面的子查询开始,然后我们将一个接一个地添加嵌套的外层。
前进传球
最初,我们将为权重参数W
和W2
分配随机正常值,并为偏差参数B
和B2
分配零值。W
和W2
的随机值可以在 SQL 本身中生成。为简单起见,我们将在外部生成这些值,并在 SQL 查询中使用。用于初始化参数的内部子查询是:
请注意,表example_project.example_dataset.example_table
已经包含列x1
、 x2
和y
。模型参数将作为附加列添加到上述查询的结果中。
接下来,我们将计算隐藏层。我们将用包含元素d0
和d1
的矢量D
来表示隐藏层。我们需要执行矩阵运算:D = np.maximum(0, np.dot(X, W) + B)
,其中X
表示输入向量(元素x1
和x2
)。该矩阵运算首先将X
乘以W
中的权重,然后加上偏置向量B
。然后,结果通过非线性ReLu
激活函数,该函数将负值设置为 0。SQL 中的等效查询是:
上面的查询在前一个内部子查询的结果中添加了两个新列d0
和d1
。上述查询的输出如下所示。
这就完成了输入到隐藏层的转换。现在我们将执行隐藏层到输出层的转换。
首先,我们将计算输出图层的分数。公式为:scores = np.dot(D, W2) + B2
。然后,我们将对分数应用 softmax 函数,以获得每个类别的预测概率。SQL 中等效的内部子查询是:
这就完成了神经网络的正向传递。接下来,我们将基于预测输出(probs
)与预期输出(Y
)的比较,进行反向传播以调整模型参数。
首先,我们将计算当前预测导致的总损失。我们将使用交叉熵损失函数来计算损失。我们将首先计算每个例子中正确类别的预测概率的负对数。交叉熵损失只不过是在X
和Y
的所有实例中这些值的平均值。自然对数是一个递增函数。因此,直观地将损失定义为正确类别的预测概率的对数的负值。如果正确类别的预测概率很高,则损失会很低。相反,如果正确类别的预测概率较低,丢失率将会较高。
为了减少过度拟合的机会,我们还将添加 L2 正则化。在总损失中,我们将包括0.5*reg*np.sum(W*W) + 0.5*reg*np.sum(W2*W2)
,其中reg
是一个超参数。在损失中包括这个函数将会惩罚权重向量中的高幅度值。
在查询中,我们还将统计训练示例的数量(num_examples
)。这在以后计算平均值时会很有用。计算总损失的 SQL 查询是:
反向传播
接下来,对于反向传播,我们将计算每个参数相对于损耗的偏导数。我们将使用链式法则从最后一层开始逐层计算。首先,我们将通过使用交叉熵和 softmax 函数的导数来计算分数的梯度。与此对应的查询是:
回想一下,我们使用scores = np.dot(D, W2) + B2
计算分数。因此,基于分数的导数(称为dscores
,我们可以计算隐藏层 D 的梯度和模型参数W2
和B2
。相应的查询是:
以类似的方式进行,我们知道D = np.maximum(0, np.dot(X, W) + B)
。因此,通过使用D
的导数,我们可以计算W
和B
的导数。计算X
的导数没有意义,因为它不是模型参数或使用任何模型参数计算。计算W
和B
的导数的查询是:
最后,我们将使用它们各自的梯度更新模型参数W, B, W2
和B2
。这可以通过param -= learning_rate * d_param
来计算,其中learning_rate
是一个参数。在dW
和dW2
中还将增加一个额外的因子reg*weight
,以将L2
正则化合并到梯度计算中。我们还将删除临时列,如dw_00
、correct_logprobs
等。它是我们在内部子查询中创建的,只保留训练数据(列x1
、x2
和y
)和模型参数(权重和偏差)。相应的查询是:
这完成了正向传递和反向传播的一次迭代。上述查询将提供权重和偏差的更新值。样本结果如下所示:
为了进行更多的训练迭代,我们将递归地执行上述所有步骤。我们可以使用一个简单的 Python 函数来实现。该代码可在链接中找到。随着我们增加更多的迭代,查询得到了大量的嵌套。执行 10 次训练迭代的结果查询可在链接中获得。
由于大量的嵌套和查询的复杂性,当我试图在 BigQuery 中执行它时,遇到了多个资源限制。与遗留的 SQL 方言相比,Bigquery 的标准 SQL 方言具有更好的伸缩性。即使使用标准 SQL,对于具有 100k 个实例的数据集,也很难执行 10 次以上的迭代。由于资源的限制,我们将在一个简单的决策边界上评估这个模型,这样我们就可以通过少量的迭代获得相当好的精度。
我们将使用一个简单的数据集,其输入x1
和x2
是从均值为 0、方差为 1 的正态分布中采样的。二进制输出 y 简单地检查x1 + x2
是否大于零。为了在 10 次迭代内训练得更快,我们将使用 2.0 的高学习率(注意:在实践中不推荐这么高的值,因为学习可能会发散)。应用上述查询 10 次迭代,得到如下所示的学习模型参数。
我们将使用 Bigquery 的’ save to table '功能将结果存储到一个新表中。现在,我们可以通过仅执行正向传递,然后比较预测结果和预期结果,来检查训练数据的准确性。这个查询片段在链接中。我们能够通过 10 次迭代获得 93%的准确度(在单独的测试数据集上准确度是相似的)。
如果我们能进行大约 100 次迭代,我们将获得超过 99%的准确率。
最佳化
这就结束了在 BigQuery 中使用纯 SQL 实现深度神经网络的有趣项目。我们将何去何从?正如我们看到的,资源限制是限制数据集大小和我们可以执行的训练迭代次数的因素。除了希望谷歌放松资源限制,我们可以做多种优化来解决这个问题。
- 我们可以创建中间表和多个 SQL 查询来执行更多的迭代。例如,前 10 次迭代的结果可以存储在中间表中。现在可以在这个中间表上应用相同的训练查询来执行接下来的 10 次迭代。因此,实际上,我们已经执行了 20 次训练迭代。这可以重复多次,以执行大量的迭代。
- 我们可以尽可能使用函数的函数,而不是在每一步都添加外部查询。例如,我们可以在一个子查询中计算
scores
和probs
,而不是两个嵌套的子查询。 - 在上面的例子中,我保留了所有的中间列,直到最后一个外部查询。其中一些,比如
correct_logprobs
,可以提前删除(尽管 SQL 引擎可能会自动执行这种优化)。 - 可以探索用户定义函数(UDF)的应用。如果感兴趣,您可以查看一个项目,其中使用 BigQuery UDF 进行模型服务(但是,不使用 SQL 或 UDF 进行训练)。
含义
现在,让我们看看深度学习环境中分布式 SQL 引擎的更深层含义。BigQuery 和 Presto 之类的仓库 SQL 引擎的一个局限性是查询处理是使用 CPU 而不是 GPU 来执行的。使用 GPU 加速的 SQL 数据库(如 blazingdb 和 mapd)检查结果会很有趣。一种简单的检验方法是使用分布式 SQL 引擎执行查询和数据分发,并使用 GPU 加速数据库执行本地计算。
退一步,我们可以看到,现在,执行分布式深度学习是很难的。几十年来,大量的研究工作已经深入到分布式 SQL 引擎中,从而产生了查询规划、数据分区、操作符放置、检查点、多查询调度等技术。其中一些可以纳入分布式深度学习。如果你对这些方面感兴趣,请看看这篇关于分布式数据库和深度学习的一般研究讨论的论文。
希望你和我一样开心!请在下面分享你的评论和想法。我很乐意回应。
回归问题的深度神经网络
神经网络在分类问题上是众所周知的,例如,它们被用于手写数字分类,但问题是,如果我们将它们用于回归问题,会有成效吗?
在本文中,我将使用一个深度神经网络,通过 Kaggle 的数据集来预测房价。
你可以从这里下载数据集
我强烈推荐你试着用我的笔记本在 Google colab [ 这里 ]上运行代码
内容:
1- Process the dataset 2- Make the deep neural network 3- Train the DNN 4- Test the DNN 5- Compare the result from the DNN to another ML algorithm
首先,我们将导入所需的依赖项:
首先:处理数据集
我们不会深入处理数据集,我们想要做的只是让数据集准备好输入我们的模型。
我们将去掉任何具有缺失值的特征,然后我们将对分类特征进行编码,就这样。
加载数据集:
- 将训练和测试数据加载到 pandas 数据框架中
- 组合训练和测试数据以一起处理它们
combined.describe()
让我们定义一个函数来获取没有任何缺失值的列
获取没有任何缺失值的列。
让我们看看有多少列
[out]:
Number of numerical columns with no nan values : 25
Number of nun-numerical columns with no nan values : 20
Histogram of the features
特征之间的相关性
从上面的关联热图中,我们看到大约 15 个特征与目标高度相关。
一个热编码分类特征:
我们将使用一个热编码对分类特征进行编码。
[out]:
There were 45 columns before encoding categorical features
There are 149 columns after encoding categorical features
现在,将组合数据帧拆分为训练数据和测试数据
第二:制作深度神经网络
- 定义顺序模型
- 添加一些密集层
- 使用’ relu '作为隐藏层的激活功能
- 使用一个’普通的’初始化器作为内核初始化器
Initializers define the way to set the initial random weights of Keras layers.
- 我们将使用平均绝对误差作为损失函数
- 定义只有一个节点的输出层
- 使用’ linear '作为输出层的激活函数
[Out]:
_________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense_1 (Dense) (None, 128) 19200 _________________________________________________________________ dense_2 (Dense) (None, 256) 33024 _________________________________________________________________ dense_3 (Dense) (None, 256) 65792 _________________________________________________________________ dense_4 (Dense) (None, 256) 65792 _________________________________________________________________ dense_5 (Dense) (None, 1) 257 ================================================================= Total params: 184,065 Trainable params: 184,065 Non-trainable params: 0 _________________________________________________________________
定义检查点回调:
第三:训练模型:
[out]:
Train on 1168 samples, validate on 292 samples
Epoch 1/500
1168/1168 [==============================] - 0s 266us/step - loss: 19251.8903 - mean_absolute_error: 19251.8903 - val_loss: 23041.8968 - val_mean_absolute_error: 23041.8968
Epoch 00001: val_loss did not improve from 21730.93555
Epoch 2/500
1168/1168 [==============================] - 0s 268us/step - loss: 18180.4985 - mean_absolute_error: 18180.4985 - val_loss: 22197.7991 - val_mean_absolute_error: 22197.7991
Epoch 00002: val_loss did not improve from 21730.93555
.
.
.
Epoch 00500: val_loss did not improve from 18738.19831<keras.callbacks.History at 0x7f4324aa80b8>
我们看到最佳模型的验证损失是 18738.19
第四:测试模型
我们将把测试数据上的预测提交给 Kaggle,看看我们的模型有多好。
The result of the deep neural network’s submission on Kaggle
一点也不差,再做一些预处理和更多的训练,我们可以做得更好。
第五:尝试另一种 ML 算法:
现在,让我们尝试另一种 ML 算法来比较结果。
我们将使用随机森林回归器和 xgb 回归器。
将训练数据分割成训练和验证数据
我们先试试随机森林模式:
Random forest validation MAE = 19089.71589041096
制作提交文件,提交给 Kaggle 看结果:
The result of random forest’s submission on Kaggle
现在,让我们试试 XGBoost 模型:
[out]:
XGBoost validation MAE = 17869.75410958904
制作一个提交文件,提交给 Kaggle 看结果:
The result of XGBoost’s submission on Kaggle
这不是一个惊喜吗,我真的不认为神经网络会击败随机森林和 XGBoost 算法,但让我们试着不要太乐观,记住我们没有在随机森林和 XGBoost 模型上配置任何超参数,我相信如果我们这样做,这两个模型会超过神经网络。
总结一下我们所做的事情:
- 我们加载并处理数据集
- 我们通过绘制一些直方图和特征的相关热图来熟悉数据集
- 我们使用了具有三个隐藏层的深度神经网络,每个隐藏层具有 256 个节点。
- 我们在输出层使用了线性激活函数
- 我们训练了模型,然后在 Kaggle 上测试。
- 我们还测试了另外两个模型
- 我们的深度神经网络能够超越这两个模型
- 我们相信,如果我们调整它们的超参数,这两个模型可以击败深度神经网络模型。
后续步骤:
- 尝试在处理数据集上投入更多精力
- 尝试其他类型的神经网络
- 尝试调整我们使用的两个模型的超参数
- 如果你真的想在回归问题上做得更好,遵循这个教程:
参考资料:
可以在推特上关注我 @ModMaamari
您可能还喜欢:
- AI 生成泰勒斯威夫特的歌词
- 用 Python 介绍随机森林算法
- 带 TensorFlow APIs 的机器学习速成班汇总
- 如何用 Tensorflow 和 Keras 制作一个 CNN?
- 如何选择最好的机器学习模型?
深层神经进化:遗传算法是用于强化学习的深层神经网络训练的竞争性替代方案——论文摘要
2017 年 12 月,优步人工智能实验室发布了与神经进化相关的五篇论文,这是一种通过进化算法优化深层神经网络的实践。
本文是对题为“T4 深度神经进化:遗传算法是训练强化学习深度神经网络的一种竞争性选择”的论文的总结。它是为那些对机器学习相关主题有一些基本熟悉的人设计的。“遗传算法”和“梯度下降”等概念是前提知识。
优步人工智能实验室的五篇论文中的大部分研究,包括我正在总结的这篇论文,都是基于 OpenAI 在论文“进化策略作为强化学习的可扩展替代方案”中所做的研究。OpenAI 已经写了一篇博客文章来总结他们的论文。一个更短的总结(我写的)可以在这里找到。
本文实验所用代码可在找到,此处为。2018 年 4 月,该代码被优化为在一台个人电脑上运行。优步人工智能实验室的一篇博文描述了实现这一点的工作,具体代码可以在这里找到。
结果如何?
深度神经网络(DNN)通常使用基于梯度的方法(如反向传播)进行优化。本文表明,使用简单的遗传算法(g a)优化 dnn 是可能的,并且在应用于强化学习(RL)任务时,如学习玩雅达利游戏、模拟仿人运动或欺骗性迷宫问题时,GA 是基于梯度的方法的竞争替代品。
遗传算法的可伸缩性通过进化一个具有超过四百万参数的 DNN 来展示,这是有史以来用进化算法进化的最大的网络。并行化的能力意味着 GAs 的计算可以分布在许多 CPU 上,与基于梯度的方法相比,可以更快地训练 DNNs。此外,通过巧妙地将 DNN 参数表示为一系列随机种子,一个拥有超过 400 万个参数的网络可以在仅仅几千个字节内进行编码(小 1 万倍)。
新奇搜索 (NS),一种与 GAs 一起使用的技术,用于鼓励在具有欺骗性或稀疏奖励的任务中进行探索,在一个问题域(称为“图像硬迷宫”)中显示出优于其他仅针对奖励进行优化的基于梯度和进化的算法。
令人惊讶的是,在学习玩一些雅达利游戏时,随机搜索(RS)被发现是有竞争力的(尽管从未像 GAs 那样有竞争力)。这表明,在某些领域,围绕原点进行采样就足以找到合适的解决方案。
这些结果表明,GAs(和 RS)并不比其他优化 DNN 的方法更好或更差,但它们是一种“有竞争力的替代方案”,可以添加到他们的 RL 工具带中。
像 OpenAI 一样,他们指出,尽管 dnn 在监督学习中不会陷入局部最优,但由于欺骗性或稀疏的奖励信号,它们仍然会在 RL 任务中陷入困境。正是由于这个原因,与 RL 中其他流行的算法相比,基于非梯度的方法(如 GAs)可以执行得很好。
关于大会
关于天然气的介绍,我推荐 Vijini Mallawaarachchi 的博客文章。
RL 中流行的算法如 Q-learning 和策略梯度使用梯度下降。ES 也通过类似于有限差分的运算跟随梯度。与这些方法不同,遗传算法不遵循梯度,而是通过变异进行探索,通过选择进行开发来进化群体中的个体。
这项工作使用一个非常简单的遗传算法,目的是建立一个埃及艾滋病的基线。它非常简单,甚至没有使用交叉,这是一种在 GAs 中非常常见的技术,以至于一开始把这种算法称为 GA 都感觉很奇怪。
在初始化群体后,前 T 个个体(在这种情况下是神经网络参数向量)被选为潜在的亲本。然后从均匀随机(有替换)中选择这些个体,并通过添加高斯噪声使其变异,该高斯噪声具有为每个实验凭经验确定的标准偏差。每一代中得分最高的个体也被添加到下一代中(一个个体的精英主义)。
最先进的编码技术
我们提出了一种新的方法,通过将每个参数向量表示为初始化种子加上产生应用于 theta 的一系列突变的随机种子的列表,来紧凑地存储大的参数向量。这项创新对于使遗传算法能够在深度神经网络的规模上工作至关重要,因此我们称之为深度遗传算法。这种技术还具有提供最先进的压缩方法的优势。
在没有交叉的情况下,定义个体的种子列表等于代数。它具有降低内存和网络传输成本的优势。对于交叉,需要一种新的更复杂的方法来对个体进行编码。这种编码方法非常酷。用相对少量的种子代表数百万个神经网络参数。
新颖性搜索
[新奇搜索]是为欺骗性领域设计的,在这些领域中,基于奖励的优化机制会收敛到局部最优。NS 通过在进化过程中忽略奖励函数来避免这些局部最优,而是奖励代理执行以前从未执行过的行为(即,新颖的)。
NS 需要一个可以量化任意两个人行为差异的函数。在每一代之后,个人有小概率被添加到档案中。新个体的“新颖性”是通过与同代人以及档案中存储的人进行比较来确定的。
实验
实验集中在遗传算法在下列任务中的性能;从像素开始学习玩 Atari 游戏,连续控制模拟的人形学步车(MuJoCo),以及一个已知的欺骗性局部优化问题,称为“图像硬迷宫”。
雅达利
A simple genetic algorithm can evolve a deep neural network with 4M+ weights to play Frostbite well
13 款雅达利游戏入选。五个被选中是因为 ES 在他们身上表现很好。其他四个被选中是因为 ES 表现不佳。最后,从可用游戏的集合中按字母顺序选择四个。
雅达利游戏上的 GAs 性能与其他强化学习算法的性能进行了比较,如 DQN (Q-learning)、es 和 A3C (策略梯度)。ES 和 A3C 的结果都来自 OpenAI 的论文。为了便于比较,数据预处理和网络架构与 2015 年关于 DQNs 的论文中介绍的相同。自 2015 年以来,对 DQNs 进行了许多改进,但作者认为,将结果与香草 DQN 进行比较是有意义的,因为这项工作只使用了香草遗传算法。为了与 OpenAI 最近在 es 上的工作进行比较,代理在 GA 运行过程中经历的游戏帧数保持不变(10 亿帧),就像在 ES 实验中一样。这意味着代理将看到不同数量的帧(取决于它们的成功程度),并且 GA 运行中的代数是不固定的。
【本研究使用】Mnih 等人(2015) 提出的更大的 DQN 架构,由 3 个 32、64 和 64 信道的卷积层和一个 512 单元的隐藏层组成。卷积层使用跨度分别为 4、2 和 1 的 8 x 8、4 x 4 和 3 x 3 滤波器。所有隐藏层之后是整流器非线性(ReLU)。该网络包含超过 4M 个参数。
将遗传算法的结果与其他算法进行比较存在一些困难。不像 GA、ES 和 DQN 都是随机开始训练的,A3C 是使用人类开始样本的数据库来训练的。此外,DQN 的结果是使用大约 2 亿帧(由于计算时间过长)获得的,而 es、RS 和 GA 使用了 10 亿帧。
总的来说,遗传算法的性能与其他算法大致相当(见下表 1)。DQN、ES 和 GA 分别在三场比赛中获得最佳成绩,而 A3C 在四场比赛中获得最佳成绩。
对于某些游戏,GA 有时在一代或几十代内就胜过 DQN 和 es。在第一代中发现的那些解决方案本质上是随机的 dnn,因为甚至没有一轮选择。那么 GA 只是在做随机搜索吗?这里 RS 是 GA 的特例,代数等于 1。看结果,GA 总是胜过 RS。但是天哪,RS 可以在某些领域打败 DQN,A3C 和 ES。
A deep neural network found through random search plays Frostbite surprisingly well
RS 对 DQN、A3C 和 ES 的成功表明,许多基于领先的深度 RL 算法的低性能而看起来很难的 Atari 游戏可能没有我们想象的那么难,相反,由于某种原因,这些算法在实际上很容易的任务上表现极差。
Table 1. The Atari results reveal a simple genetic algorithm is competitive with Q-learning (DQN), policy gradients (A3C), and evolution strategies (ES). Shown are game scores (higher is better). Comparing performance between algorithms is inherently challenging (see main text), but we attempt to facilitate comparisons by showing estimates for the amount of computation (operations, the sum of forward and backward neural network passes), data efficiency (the number of game frames from training episodes), and how long in wall-clock time the algorithm takes to run. The GA, DQN, and ES, perform best on 3 games each, while A3C wins on 4 games. Surprisingly, random search often finds policies superior to those of DQN, A3C, and ES (see text for discussion). Note the dramatic differences in the speeds of the algorithm, which are much faster for the GA and ES, and data efficiency, which favors DQN. The scores for DQN are from Hessel et al. (2017) while those for A3C and ES are from Salimans et al. (2017).
人形运动
Humanoid locomotion, controlled by a deep neural networks trained with a simple genetic algorithm.
遗传算法接下来在一个连续控制问题上进行测试,其中一个模拟的人形机器人学习走路。使用的软件是MuJoCo(open Dai 健身房的 Humanoid-v1 环境)。
这个问题包括将描述人形机器人状态(例如,其位置、速度、角度)的 376 个标量的向量映射到 17 个关节扭矩。机器人会收到一个标量奖励,每个时间步长由四个部分组成。它的站姿和在 x 轴正方向的速度会得到正回报,消耗的能量越多,撞击地面的力度越大,得到的回报就越低。这四项在一集的每一个时间点上相加,计算出总奖励。
与 es 相比,GA 需要更多的计算来获得类似的结果,这是令人惊讶的,因为 GA 在 Atari 游戏上表现得非常好。
形象硬迷宫
The Image Hard Maze domain
图像硬迷宫是一个包含局部最优(陷阱)的问题,它欺骗贪婪地接近目标的代理。
查新没有这个问题,因为它忽略了奖励,鼓励代理人去新的地方。如前所述,NS 需要一个行为差异函数来确定“新颖性”。对于这个问题,该函数是最终位置之间的平方欧几里得距离。
神经网络看到迷宫的鸟瞰图,这是 84×84 像素的图像,有超过 400 万个参数。它通过速度和旋转输出来控制机器人在迷宫中的导航。前三帧也是提供时间信息的输入的一部分。
执行新颖性搜索允许遗传算法解决这个任务,而只为奖励(到目标的最小距离)优化的遗传算法和专家系统陷入局部最优。DQN 和 A2C(A3C 的源代码不可用)也无法进行足够的探索来与 GA-n 竞争。
How different algorithms explore the deceptive Image Hard Maze over time. Traditional reward-maximization algorithms do not exhibit sufficient exploration to avoid the local optimum (going up). In contrast, a GA optimizing for novelty only (GA-NS) explores the entire environment and ultimately finds the goal. All 3 evolutionary algorithms had the same number of evaluations. For DQN and A2C, we plot the end-of-episode position of the agent for each of the 20K episodes prior to the checkpoint listed above the plot.
结论
实验表明,与 DQN、A3C 和 ES 等其他众所周知的算法相比,遗传算法是 RL 中有竞争力的替代算法,有时性能更好,有时性能更差。这项工作只使用了简单的遗传算法,通过将遗传算法与其他已知在神经进化领域有效的技术相结合,或者将遗传算法与其他 RL 算法相结合,可能会发现新的结果。
虽然这些结果看起来很有希望,但也有可能采取稍微悲观一点的观点。2018 年 3 月,Ben Recht 与人合著了一篇名为简单随机搜索的论文,提供了一种增强学习的竞争方法,其中他的团队利用 RS 实现了 MuJoCo 行走任务的最先进水平。他的结论与本文中关于这些结果的结论相似,即 RS 在这些领域的成功表明这些问题并不像以前认为的那样困难。
然而,论文中的工作确实证明了一个简单的遗传算法可以优于 RS,并且通过继续在这个方向上的研究可能会有进一步的收获。它还表明,在当前流行的算法中,存在关于采样效率和计算时间的折衷。考虑到您的用例,这是可以考虑的。如果样品的成本很高,基于梯度的方法可能更好。如果时间是限制因素,遗传算法会更快地为你找到解决方案。也许在未来,我们将能够指定一个“速度与采样效率”的折衷方案,并找到一个同时使用 EAs 和基于梯度的方法的解决方案。
深度分位数回归
深度学习尚未广泛探索的一个领域是估计的不确定性。大多数深度学习框架目前专注于给出由损失函数定义的最佳估计。偶尔需要一些超出点估计的东西来做决定。这就是发行版有用的地方。贝叶斯统计很好地解决了这个问题,因为可以推断出数据集的分布。然而,贝叶斯方法迄今为止一直相当慢,并且应用于大型数据集将是昂贵的。
就决策而言,大多数人实际上需要分位数,而不是估计中的真正不确定性。例如,当测量给定年龄的儿童的体重时,个体的体重会变化。有趣的是(为了便于讨论)第 10 和第 90 百分位。请注意,不确定性不同于分位数,因为我可以请求第 90 个分位数的置信区间。本文将纯粹专注于推断分位数。
分位数回归损失函数
在回归中,最常用的损失函数是均方误差函数。如果我们取这个损失的负值并对其求幂,结果将对应于高斯分布。该分布的模式(峰值)对应于均值参数。因此,当我们使用使这种损失最小化的神经网络进行预测时,我们预测的是可能在训练集中有噪声的输出的平均值。
单个数据点的分位数回归损失定义为:
Loss of individual data point
其中,alpha 是所需的分位数(0 到 1 之间的值),而
其中 f(x)是预测(分位数)模型,y 是相应输入 x 的观测值。整个数据集的平均损失如下所示:
Loss funtion
如果我们取个体损失的负值并对其取指数,我们得到的分布称为不对称拉普拉斯分布,如下所示。这个损失函数起作用的原因是,如果我们要找到图中零左边的面积,它将是α,即所需的分位数。
probability distribution function (pdf) of an Asymmetric Laplace distribution.
当α= 0.5 时的情况很可能更熟悉,因为它对应于平均绝对误差(MAE)。这个损失函数始终估计中位数(第 50 个百分位数),而不是平均值。
Keras 中的建模
正向模型与你做 MSE 回归时的模型没有什么不同。改变的只是损失函数。下面几行定义了上一节中定义的损失函数。
**import** **keras.backend** **as** **K****def** tilted_loss(q,y,f):
e = (y-f)
**return** K.mean(K.maximum(q*e, (q-1)*e), axis=-1)
当涉及到编译神经网络时,只需简单地做:
quantile = 0.5
model**.**compile(loss**=lambda** y,f: tilted_loss(quantile,y,f), optimizer**=**'adagrad')
完整的例子见[这个 Jupyter 笔记本](https://github.com/sachinruk/KerasQuantileModel/blob/master/Keras Quantile Model.ipynb),我在那里看了一段时间的摩托车碰撞数据集。结果如下,我显示了第 10、50 和 90 个分位数。
Acceleration over time of crashed motor cycle.
最终注释
- **注意,对于每个分位数,我必须重新运行训练。**这是由于每个分位数的损失函数是不同的,因为分位数本身就是损失函数的一个参数。
- 由于每个模型都是简单的重新运行,因此存在分位数交叉的风险。即第 49 个分位数可能在某个阶段超过第 50 个分位数。
- 请注意,分位数 0.5 与中位数相同,可以通过最小化平均绝对误差来实现,这可以在 Keras 中实现,与
loss='mae'
无关。 - 不确定性和分位数是而不是一回事。但是大多数时候你关心的是分位数而不是不确定性。
- 如果你真的想要深度网络结帐的不确定性http://mlg.eng.cam.ac.uk/yarin/blog_3d801aa532c1ce.html
编辑 1:
正如 Anders Christiansen (在回答中)指出的,我们可以通过设定多个目标一次获得多个分位数。然而,Keras 通过一个loss_weights
参数组合了所有损失函数,如下所示:https://keras . io/getting-started/functional-API-guide/# multi-input-and-multi-output-models。在 tensorflow 中实现会更容易。如果有人捷足先登,我会很乐意改变我的笔记本/帖子来反映这一点。作为一个粗略的指导,如果我们想要分位数 0.1,0.5,0.9,Keras 中的最后一层将有Dense(3)
,每个节点都连接到一个损失函数。
编辑 2
感谢 Jacob Zweig 在 TensorFlow 中实现同时多个分位数:https://github . com/strong io/Quantile-regression-tensor flow/blob/master/Quantile % 20 loss . ipynb
思考后:
这些是我的一些想法,是我在一年前写这篇文章时补充的。
- 多个分位数:我想得越多,我就越确信你应该一次完成所有的分位数。例如,如果我们需要分位数 0.05、0.5 和 0.95,则有 3 个输出节点,每个节点有一个不同的损失函数(求和得到最终损失)。这确保了数据的结构在前几层是共享的。
- 分位数交叉:为了确保我们避免这种情况,非中位数分位数,例如 0.95,可以建模为(节点 0.5 + sigma * sigmoid(0.95 节点))。其中 sigma 是最大值,我们期望 0.95 分位数偏离中值。类似思想可以在 0.05 分位数上实施,而代之以 sigma * sigmoid(0.05 节点)上的负号。
看这里是我关于机器学习和深度学习的课程(使用代码 Deep school-三月到九折)。
张量流中的深度分位数回归
深度学习中的一个关键挑战是如何获得对预测器界限的估计。分位数回归最早由 Koenker 和 Bassett [1]在 70 年代引入,它允许我们估计潜在条件数据分布的百分位数,即使在它们不对称的情况下,也能让我们了解预测因子和反应之间的变异性关系。
如 Koeneker 和 Hallock [2]所述,OLS 回归的标准方法是通过绘制一条线来估计数据的条件平均值*,该线使该线和图上所有点之间的距离最小化。相反,分位数回归的目标是估计有条件的*中位数、或任何其他分位数。为此,我们根据所选的分位数对图表上的点和我们的回归线之间的距离进行加权。例如,如果我们选择第 90 个分位数进行估计,我们将拟合一条回归线,使 90%的数据点低于该线,10%高于该线。
张量流代码
Sachin Abeywardana 最近在 medium 上发表的一篇伟大的文章展示了如何使用 Keras 进行深度分位数回归。然而,这种实现的限制是,它需要每个分位数单独拟合模型。然而,使用 Tensorflow,我们可以同时拟合任意数量的分位数。
用于此实现的笔记本可以在 Strong Analytics github 上找到。
我们首先实例化一个简单的回归模型,并为我们想要估计的每个分位数添加输出
# Define the quantiles we’d like to estimate
quantiles = [.1, .5, .9]# Add placeholders for inputs to the model
x = tf.placeholder(tf.float32, shape=(None, in_shape))
y = tf.placeholder(tf.float32, shape=(None, out_shape))# Create model using tf.layers API
layer0 = tf.layers.dense(x, units=32, activation=tf.nn.relu)
layer1 = tf.layers.dense(layer0, units=32, activation=tf.nn.relu)# Now, create an output for each quantile
outputs = []
for i, quantile in enumerate(quantiles):
output = tf.layers.dense(layer1, 1, name=”{}_q{}”.format(i, int(quantile*100)))
outputs.append(output)
现在,我们为每个分位数创建损失
losses = []
for i, quantile in enumerate(quantiles):
error = tf.subtract(y, output[i])
loss = tf.reduce_mean(tf.maximum(quantile*error, (quantile-1)*error), axis=-1)
losses.append(loss)
最后,我们合并损失并实例化我们的优化器
combined_loss = tf.reduce_mean(tf.add_n(losses))
train_step = tf.train.AdamOptimizer().minimize(combined_loss)
基本上就是这样了!现在,我们可以拟合我们的模型,同时估计所有指定的分位数:
for epoch in range(epochs):
# Split data to batches
for idx in range(0, data.shape[0], batch_size):
batch_data = data[idx : min(idx + batch_size, data.shape[0]),:]
batch_labels = labels[idx : min(idx + batch_size, labels.shape[0]),:] feed_dict = {x: batch_data,
y: batch_labels} _, c_loss = sess.run([train_step, combined_loss], feed_dict)
将这些数据拟合到摩托车碰撞的数据集上(从此处开始)得到以下输出:
Estimated quantiles
参考文献
1.回归分位数。罗杰·科恩克;吉尔伯特·巴塞特,《计量经济学》,第 46 卷,第 1 号。(1978 年 1 月),第 33-50 页。
2.分位数回归。罗杰·科恩克;凯文·f·哈洛克。《经济展望杂志》第 15 卷第 4 期,2001 年秋季。(第 143-156 页)。
想和芝加哥的顶级数据科学家团队一起从事各种行业中具有挑战性的机器学习和人工智能工作吗?我们正在招聘有才华的数据科学家和工程师!
在 strong.io 了解更多信息,并在 careers.strong.io 申请
新闻推荐的深度强化学习。第 1 部分:架构。
我将试着涵盖这篇文章的内容,如果你想了解更多细节,可以考虑阅读它。
.
.
.
重要说明
在这一点上,这篇文章已经过时了。它包含许多错误和不一致之处。我发表了另一篇 33 分钟(7k 字)的文章,对这个项目进行了深入的探讨。考虑改为阅读。附:绝对免费:链接
强化学习是一个相当难的话题。当我开始深入挖掘时,我意识到需要一个好的…
towardsdatascience.com](/reinforcement-learning-ddpg-and-td3-for-news-recommendation-d3cddec26011)
.
.
.
大多数现有的推荐系统使用静态方法进行新闻预测,也就是说,它们不理解你的行为(新闻选择)是连续的。第二个问题是,以前的方法往往是短视的,这意味着它们不会关注长期回报。使用较高的 gamma 值可以很容易地解决这个问题,但通常情况并非如此。
嵌入概述
嵌入是一种工具,它允许我们对一些对象的含义进行编码,以便找出它们之间的相似程度。在下面的例子中,你可以看到托尔斯泰的书彼此之间非常相似,但与希区柯克的有很大不同。它们用向量表示,相似性用点积符号计算
**books = ["War and Peace", "Anna Karenina",
"The Hitchhiker's Guide to the Galaxy"]****books_encoded_ideal = [[0.53, 0.85],
[0.60, 0.80],
[-0.78, -0.62]]****Similarity (dot product) between First and Second = 0.99
Similarity (dot product) between Second and Third = -0.94
Similarity (dot product) between First and Third = -0.97**
强化学习回顾:
强化学习简介:我们有一个在环境中行动的代理。相应地,这些行为会以分数的形式得到奖励。环境为代理提供状态并采取行动,将他带到下一步。整个过程被称为马尔可夫决策过程(或简称为 MDP)。基本上,有两种类型的预测:政策和 Q 学习。Q-Learning 训练一个神经网络,试图估计下一步和未来步骤的最佳回报(记住,不同的行动可能会有不同的回报)。然而,政策学习试图学习下一步行动的概率。
马尔可夫表示:
- 状态 s 是用户正面交互历史的表示
- 动作 a。动作 a 是排名分数的向量。这个动作是用户想要阅读的理想新闻。
- 状态被建模为用户的积极交互历史的表示。因此,一旦收集了用户的反馈,就定义了状态转换。
- 给定基于动作 a 和用户状态 s 的推荐,用户将提供她的反馈,即点击、未点击或评级等。推荐者根据用户的反馈接收即时奖励 R(s,a)。
- 贴现率γ。γ ∈ [0,1]是衡量长期报酬现值的一个因素。
以前的方法比较
Q-Learning 的问题是,由于对整体行动的评估,如果行动空间太大,这些方法可能会变得非常低效。尽管政策在更大的行动空间上更收敛,但它们不能理解序列特征。
介绍够了,我们来看看是什么样子的:
它是如何工作的
假设,我们观察了用户的行为,获得了他们点击的一些新闻帖子。它被输入到演员网络,演员网络决定我们接下来想读什么。它产生一个理想的新闻嵌入。可以和其他新闻嵌入进行对比,寻找相似之处。最匹配的会推荐给用户。评论家帮助判断演员,并帮助它找出错误。也有助于预测未来的行为。例如,推荐者建议用户购买一本书。它买它没有问题,并立即收到 100 英镑的奖励。但也可能发生的情况是,用户想要在未来归还这本书,惩罚我们-500。所有未来的行动都需要考虑在内。Q 网(评论家)也是如此,如前所述,政策方法往往是短视的,没有能力预测未来的回报。
体系结构
如你所见,这个网络由两层组成:演员和评论家。每一种都类似于不同的学习类型:行动者学习政策(下一步选择哪种行动的概率),而批评家关注回报(Q 学习)。首先,一堆新闻嵌入被馈入演员的状态表示模块,在那里被编码。然后做出决定(动作)(以向量的形式)。结合项目嵌入,行动被输入到评论家模块,该模块旨在评估奖励将有多好。
状态模块
首先是状态表示模块,“它显式地对复杂的动态用户-项目交互进行建模,以追求更好的推荐性能”。简单地说,你可以把它看作是捕捉句子中单词语义的嵌入,但是用新闻和客户代替。嵌入的嵌入,多么可悲。它起着两个关键作用:用于排名和作为评论家网络的输入与行动相结合。
演员网络(政策)
对于给定的用户,网络负责基于她的状态 s 生成动作。由她的 n 个最近的积极交互项目的嵌入所表示的用户状态被用作输入。它们被馈送到状态模块,以便为用户产生状态的概括表示。然后,表示被转换成动作,一个大小为 n 的向量,范围在(-1,1)之间。
正如你所看到的,作者没有使用 softmax 来排名分数:
注意,在我们的模型中,一个动作既不对应于推荐一个项目,也不对应于推荐一个项目列表。相反,动作是一个连续的参数向量。采取这样的动作,通过执行具有项目嵌入的乘积(多重向量),参数向量用于确定所有候选项目的排名分数。
下一步很重要:
批评家网络(Q 学习)
用来估计当前状态和动作的奖励会有多好:Qπ (s,a)。批评家网络的输入是用户状态表示模块产生的用户状态 s 和策略网络产生的动作,输出是 Q 值,Q 值是一个向量。根据 Q 值,演员网络的参数朝着提高动作 a 性能的方向更新,我不打算写所有的渐变,主要是因为我用的是 Pytorch。损失函数是简单的均方误差,因为我们要估计的是通常不归一化的实值回报,所以这是一个回归问题。
州立模块动物园
DRR-p-利用项目之间的成对依赖性。它通过使用元素级乘积运算符来计算 n 个项目之间的成对交互。(用户-项目交互被忽略!)
在 DRR 大学,我们可以看到用户嵌入也被纳入。除了项目之间的局部依赖,还考虑了用户-项目的成对交互。
当我们处理大量长期新闻时,我们并不期望立场有什么影响。但是,如果序列 H 是一个短期序列,记忆项目的位置可能会导致过拟合。由于采用了平均池层,我们将该结构称为 DRR-ave。从图 6 中可以看出,H 中的项目嵌入首先通过加权平均池层进行变换。然后,利用得到的向量来对与输入用户的交互进行建模。最后,嵌入的用户、交互向量和项目的平均池化结果被连接成一个向量来表示状态表示。
在下一篇文章中,我将尝试使用深度确定性策略梯度在 Pytorch 中实现这个网络,正如在原始论文中所描述的那样。如果这引起了足够的注意,我将详细解释算法。为它鼓掌,敬请关注!
Upd:开始了一个 github 项目(还不理想,但是模型正在学习)
经过处理的数据将很快上传,但现在需要几个小时来处理。需要的话就 Dm 我。我对下一篇文章有一些大的想法,这将是关于这个主题的最后一篇文章,但是非常大而且有解释性。别忘了开始!
https://github.com/awarebayes/RecNN
对一个不很深的神经网络的深入研究。第 1 部分:我们的数据中有什么
介绍
开始深度学习之旅的人经常被为他们的神经网络选择正确的配置和超参数的问题所困惑。各种课程、教程和教科书只是简要描述了可用的选项,并没有提供关于它们的适用性和性能的指导。在大多数教程中,都提到一些函数、优化器或初始化器比其他的更好,但没有解释为什么,或者在什么条件下。某些参数,如单位数或时期数,有时没有任何正当理由。学生们被告知在课余时间和他们一起“玩耍”,看看是否能提高成绩。许多作者将寻找神经网络最佳配置的过程称为一门艺术,并表示选择正确值的能力来自经验。我不相信它总是对的。一些常识应该让在这一领域迈出第一步的人容易获得,而不需要浏览数百篇研究论文。
所以我开始写一系列文章,描述我在过去几个月里进行的一项实验的结果。也就是说,我已经评估了一个非常简单的神经网络的几乎每一个可调位,以了解这些变化如何影响最终的精度。
具体来说,在这个系列中,我们将看到:
- 训练数据的统计如何影响模型的质量;
- 对于全连接神经网络,激活函数和优化器的哪种组合工作得更好;
- 学习率过高有什么影响,如何处理;
- 如何让训练更稳定、可复制;
- 初始化器、正则化器和批处理规范化如何影响神经网络的内部结构;
- 模型的性能如何取决于单元和层数。
在这个系列中,我将只关注一个全连接的神经网络,它被设计用来从著名的 MNIST 数据集中对手写数字进行分类。使用 MNIST 的原因很简单:它很均衡,所以我们可以确信测试结果是充分的;它不是太大,所以我们可以在合理的时间内训练各种参数组合的模型,它内置在所有主要的深度学习框架中。
全连接神经网络架构可能是最简单的一种,而且它通常是深度学习书籍和课程中介绍的第一种。尽管这种架构很简单,但它有如此多的参数和选项可供选择,以至于人们常常将一切都保留为默认值,希望这些值会产生可接受的性能。
当然,还有更合适的方法来处理 MNIST,比如使用卷积神经网络。但在这里,我们的任务不是要打破最先进的模型的分数。相反,我们关注的是网络的每个参数在最终精度中的作用。
这不是一个介绍性的教程,读者应该至少对神经网络有基本的了解,它们是如何工作的,如何构建和训练它们。为了建立网络,我将使用 Keras 与 TensorFlow 后端。
试验设计
我们实验的基线代码将取自 Keras 库的示例。在本系列的课程中,我们将稍微修改它,以便让我们看到它的各个部分的变化是如何影响测试集的准确性的。
配置将基于验证准确性值进行评估,因为这是我们案例中最客观的指标。损失分数不提供太多信息,并且由于可能的过度拟合,训练集的准确性不具有代表性。
每个配置将被测试 5 次(即,对于每个配置,将从头开始训练 5 个神经网络),以便减少不可避免的随机性的影响,然后将对训练这些配置的结果精度进行平均。这将有助于确保我们看到更有代表性的结果。
我们的神经网络最初将由两个各有 64 个单元的全连接层和一个有 10 个单元和 softmax 激活的输出层组成。
数据准备
本系列的第一部分讨论了在将数据输入神经网络之前用于转换数据的各种技术。
MNIST 数据集包含 60 000 幅训练图像和 10 000 幅测试图像,每幅图像为 28x28 像素。每个像素都有一个介于 0 和 255 之间的值,其中 0 表示完全黑色,255 表示白色。在数据科学中,数据通常被换算成小的实数。这样做的原因是为了使模型更快地收敛,并找到更好的极小值。
下面是 Geoffrey Hinton 如何表述这个问题:“当误差面是一个二次碗的时候”……“下坡减少了误差,但是最陡下降的方向并不指向最小值,除非椭圆是一个圆。”以下是他对这个问题的设想:
Fig.1 Error surfaces with shifted and scaled inputs. Source: http://www.cs.toronto.edu/~tijmen/csc321/slides/lecture_slides_lec6.pdf
有两种方法可以在将数据输入神经网络之前对其进行转换,并使误差曲面更加平滑。
一种是转换数据,使所有矢量分量都适合[0;1]范围。这叫正常化。这里,我们取输入向量的每个分量的最小值,从各自的分量中减去它们,然后除以每个分量的最大值。
另一种方法是使数据具有 0 均值和单位方差(等于 1)。它是通过计算每个分量的平均值和标准偏差,然后从每个分量值中减去平均值,再除以各个分量的标准偏差来实现的。有时这也被称为标准化,但是为了清楚起见,我们称之为标准化。
在处理 MNIST 时,我们使用 28x28 的图像,为了能够将其输入到一个完全连接的网络中,我们首先需要将它们展平,即将每个图像转换为 28x28 = 784 维的向量。在 MNIST,这个向量的所有分量都代表像素,像素的值只能在 0 到 255 之间。
因此,可以自然地假设每个图像应该具有至少一个完全黑色和完全白色的像素,并且只使用整个数据集的全局最小值和最大值,或者平均值和标准偏差值。实际上,数据集中的每个样本可能都有自己的统计数据,因此,如果使用全局值对它们进行变换,数据集作为一个整体将具有所需的统计数据,但每个单独的样本将会发生移位和缩放。为了说明这一点,让我们看看前 4 个 MNIST 样本:
Fig.2 MNIST Sample Images
几乎可以肯定,这些图像中的每一个都具有至少一个完全黑色的像素(即零值)。但是代表白色的值 255 可能不是这种情况。看看这些样品:
Fig.3 MNIST Sample Images
令人惊讶的是,这些样本没有完全白色的像素,即使你可能认为粗体白色区域应该是白色的。因此,如果您使用全局最小值和最大值对它们进行规格化,则这些图像的值范围会以不同于样本的方式进行缩放,样本的最小值和最大值等于全局值。对于我们的黑白图像来说,这不是一个大问题,人眼甚至不会注意到这样的变化。但在其他情况下,如彩色图像,这可能会完全转换图像,因此单独归一化每个样本总是更好的方法。
标准化也是如此。直观地,我们可以理解,对于每个图像,各种像素值的分布是不同的,并且使用全局平均值和标准偏差值来标准化它们可能会错误地转换样本,并且最终导致模型的较差性能。我们将很快验证这个假设。
现在让我们看一下归一化数据分布图(数据集和样本的归一化几乎相同,所以我只给出第一种情况):
Fig.4 MNIST data. Pixel values distribution after dataset-wise normalization.
该直方图显示了数据中每个离散值的计数。从左侧的直方图中,我们可以看到大部分数据为零。事实上,零值构成了我们的归一化数据中所有数据点的大约 20%,即我们的 MNIST 图像数据中 20%的像素是黑色的。如果我们绘制不带 0 的数据分布,下一个大组是 1,代表完全白色的像素。
标准化数据的统计数据:
数据集标准化数据的直方图是相同的,只是水平轴的范围现在为-0.424 到 2.822。
Fig.5 MNIST data. Pixel values distribution after dataset-wise standardization.
数据的形式没有改变,但它被移动和缩放了。现在让我们来看看数据集标准化数据的统计数据:
数据集现在作为一个整体被标准化了(数据集的平均值实际上是零),但是每个样本都有不同的平均值和方差。还要注意,数据分布形状的身份由与归一化数据相同的全局偏斜度和峰度来确认。
现在让我们对每个样本进行标准化。这是样本标准化数据的曲线图:
Fig.6 MNIST data. Pixel values distribution after sample-wise standardization.
统计数据是:
显著的差异。现在每个样本都是标准化的,但是整个数据集也是标准化的:平均值非常小,我们可以把它算作零,方差非常接近 1。
现在让我们来看看,这些转变是否真的有所不同。我们将使用所有四个转换数据集来训练我们的简单神经网络。
培训结果
示例代码最初使用 RMSProp 优化器和 ReLU 激活,所以我们将在第一个实验中使用它们。我们将比较在四种数据转换类型中的每一种上训练相同配置的神经网络的结果:标准化数据集和样本,以及标准化数据集和样本。
它们将通过以下方法进行比较:
- 最后一个时期的平均值:第 100 个时期的验证准确度值在 5 次实验中取平均值;
- 达到的平均最大值:5 次实验的平均训练统计的最高验证准确度值;
- 最后一个时期的总最大值:5 次实验中第 100 个时期的最高验证准确度值
- 达到的总最大值:在该激活的所有实验中观察到的最高验证准确度值;
- 平均最大历元:达到平均最大值时的历元数。
注意:为了计算该实验和进一步实验中的平均最大精度,而不是找出特定配置的 5 个实验中每个实验的最大值,然后将它们除以 5,我对整个训练过程进行平均,即对于步骤 N,我取在步骤 N 的 5 个实验中的每个实验中观察到的精度,然后对它们进行平均。这样,我计算平均训练进度,并取其最大精度。我相信,这种方法更好地代表了特定配置下的典型训练结果。
很明显,在我们的网络配置中,标准化数据比标准化数据显示出更好的结果。从这些结果来看,不可能判断数据集的标准化是否优于样本的标准化,但这可能只是因为我们数据集的特殊性。最有可能的是,对于其他数据集,您将通过样本标准化获得更高的结果。
至于标准化,我们已经证实,使用全局值来标准化我们的训练数据是错误的。然而,在我们的实验中,样本标准化导致较低的准确性,这一事实并不意味着情况总是如此。在后面的部分中,我们将看到在一些特定的神经网络配置中,样本标准化可能会导致比标准化数据高得多的结果。
下图显示了四种数据在训练过程中的准确度变化:
Fig.7 Validation accuracy for dataset-wise and sample-wise input data transformations.
虚线代表单个实验,黑线代表这些实验的平均精度。以下是所有四个平均值的比较结果:
Fig.8 Average validation accuracy for networks trained on various transformations of the input data.
同样,根据标准化数据训练的网络彼此非常接近,样本标准化训练稍差,但不像数据集标准化那样差。此外,请注意,对于标准化数据,训练的稳定性稍差,即,随着训练的进行,精确度上下变化较大。这可以通过调整学习速率来部分解决,我们将在下一部分中尝试。
第一部分的学习要点:
- 在建立你的神经网络和开始训练之前,看一下你的数据:统计数据和它的底层结构可能在实现你的模型的高性能方面发挥很大的作用;
- 规范化或标准化数据组件的方式,而不是使用全局统计值,否则您可能会改变它,使一些有价值的信息隐藏您的数据可能会丢失或损坏。
实验的代码可以在我的 github 上找到。在下一部分我们将研究激活函数,它是神经网络的一部分,将你准备好的输入数据转化为输出。敬请期待!
我总是很高兴认识新朋友并分享想法,所以如果你喜欢这篇文章,可以考虑在 LinkedIn 上加我。
深度学习一个不是很深的神经网络系列:
- 第 1 部分:我们的数据中有什么
- 第二部分:激活功能
- 第 3a 部分:优化器概述
- 第 3b 部分:选择优化器
- 第四部分:如何找到合适的学习率
- 第 5 部分:压差和噪声
- 第 6 部分:权重初始化
- 第 7 部分:正规化
- 第 8 部分:批处理规范化
- 第 9 部分:大小很重要
- 第 10 部分:将它们融合在一起
对一个不很深的神经网络的深入研究。第 2 部分:激活函数
这是该系列的第二部分,这次我们将探讨激活函数。我假设你已经知道,什么是激活函数,以及它在神经网络中扮演什么角色。
在我们的实验中,我们将比较 Keras 中包含的激活功能,具体如下:
- 线性;
- 乙状结肠;
- 硬乙状结肠;
- TanH
- 软设计;
- ReLU
- 漏水的 ReLU
- 阈值 ReLU
- ELU;
- SELU;
- 软加。
正如在上一部分中一样,这里我们将坚持使用 RMSProp 优化器。在本系列的后面部分,我们还将评估各种激活函数如何与不同的优化器一起工作,但现在让我们先来看看激活。至于数据,我们将在数据集标准化数据(因为它与样本标准化数据相比表现同样出色)和样本标准化数据(如果你读过前一部分,你就知道为什么)上训练我们的网络。
下面是对每个激活功能的一个简短且不太科学的描述,只是为了让你对每个有一个直观的了解。
线性的
线性激活(也称为恒等式)函数是最简单的激活函数之一。它将输入线性转化为输出。现在,无论是在隐含层还是在最终层,它几乎从未用于训练神经网络。它的值域和定义域等于[-Inf;+Inf]。
Fig.1 Linear activation
乙状结肠的
Sigmoid 激活函数翻译范围在[-Inf;+Inf]到(0;1),看起来像一条 S 形曲线。当开发用于学习目的的简单神经网络时,它通常是第一选择,但是到目前为止,它通常被避免,因为与其他激活函数相比,它的质量较低。
遭受消失梯度问题,当“在某些情况下,梯度将消失得很小,有效地防止重量改变其值”。
Fig.2 Sigmoid activation
硬乙状结肠
该函数是 sigmoid 函数的分段线性近似。在区间[-Inf 上等于 0;-2.5),然后在[-2.5;+2.5]并在范围(+2.5;+Inf] ( 来源)。计算硬 Sigmoid 被认为比计算常规 Sigmoid 更快,因为您不必计算指数,并且它在分类任务上提供了合理的结果。但是正因为它是一个近似值,它不应该用于回归任务,因为误差会比常规的 sigmoid 高得多。
Fig.3 Hard Sigmoid activation
双曲正切值
TanH 看起来很像 sigmoid 的 S 形曲线(其实只是一个缩放的 Sigmoid),但是它的范围是(-1;+1).在更复杂的激活函数出现之前,它已经相当流行了。简而言之,使用 TanH 代替乙状结肠的好处是(来源):
- 更强的梯度:如果数据以 0 为中心,导数更高。
- 避免由于包含范围(-1;0).
然而,与 Sigmoid 类似,TanH 也容易受到消失梯度问题的影响。
Fig. 4 Hyperbolic Tangent (TanH) activation
软设计
作为符号函数的连续近似,其图形看起来非常类似于 TanH。然而,TanH 是指数增长,而 SoftSign 是多项式增长。软签名的范围也是(-1;+1).
Fig.5 SoftSign activation
整流线性单元
一个非常简单而强大的激活函数,如果输入为正,则输出输入,否则输出 0。声称它是目前训练神经网络最流行的激活函数,并且产生比 Sigmoid 和 TanH 更好的结果。这种类型的激活函数不容易受到消失梯度问题的影响,但是它可能遭受“垂死的 ReLU 问题”。正如在维基百科中所说:“ReLU 神经元有时会被推入对几乎所有输入都不活跃的状态。在这种状态下,没有梯度回流通过神经元,因此神经元陷入永久不活动状态并“死亡”。“在某些情况下,网络中的大量神经元可能会陷入停滞状态,从而有效降低模型容量。当学习率设置得太高时,通常会出现这个问题。”下面是对这个问题的一个很好的描述:https://www . quora . com/What ’ s-the-dying-ReLU-problem-in-neural-networks。
这个激活函数有参数 alpha,它控制 x < 0 and is set to 0.0. Setting this parameter to any value < 1.0 transforms this activation into Leaky ReLU and setting it to 1.0 makes this function work as Linear activation. What happens, when alpha is > 1.0 的直线的陡度,研究起来会很有趣。
Fig.6 Rectified Linear Unit (ReLU) activation
泄漏 ReLU
ReLU 函数的一个变体,允许输入< 0, which helps to 的梯度α的少量“泄漏”,克服了即将消失的 ReLU 问题。默认情况下,Keras alpha 设定为 0.3
Fig.7 Leaky ReLU activation with various alpha parameters
阈值 ReLU
ReLU 激活的另一个变体,其中 x < theta, and equals to x if x > = theta 的输出为 0。在 Keras 中,θ的默认值被设置为 1.0,而在原始论文中,据说 0.7 的值为他们的特定实验提供了最佳结果。
Fig.8 Thresholded ReLU activation
指数线性单位(ELU)
较少使用的 ReLU 修改,据说比传统的 ReLU 导致更高的分类结果。对于 x>= 0,它遵循与 ReLU 相同的规则,对于 x < 0,它按指数规律增加。ELU 试图使平均激活接近零,这加快了训练速度。
它只有一个参数 alpha,用于控制负声部的比例,默认设置为 1.0。
Fig.9 Exponential Linear Unit (ELU) activation
比例指数线性单位(SELU)
我们将在实验中评估的最后一个整流器。它用参数λ扩展了 ELU,负责缩放正负部分。如 Klambauer 等人在的论文中所建议的,该函数中的 Alpha 和 lambda 被硬编码为分别大约等于 1.67 和 1.05。他们还说,这个激活应该与“lecun_normal”初始化器和 AlphaDropout 一起使用,但是为了与本部分中的其他激活相比较,我们将使用默认初始化器和常规 Dropout。我们将在后面的系列文章中检查 SELU 的初始化和退出。
Fig.10 Scaled Exponential Linear Unit (SELU) activation
SoftPlus
SoftPlus 函数的图形看起来像平滑的 ReLU。我找不到为什么 SoftPlus 应该优于任何其他激活函数的确切原因,这一点得到了 Glorot、Bordes 和 Bengio 在深度稀疏整流器神经网络论文中的陈述的支持:“尽管硬阈值为 0,但用整流器激活函数训练的网络可以找到比用其平滑对应物 softplus 获得的质量更好或相同的局部最小值。”
Fig.11 SoftPlus activation
为了方便起见,这里将所有激活功能合并为两个图表:
Fig.12 Comparison of various activation functions
实验结果
当比较激活函数时,我们将考虑与先前实验中相同的指标。在我们用 RMSProp optimizer 对每个激活函数训练了我们的网络 5 次之后,下面是我们得到的归一化数据集(按达到的最大验证准确度排序):
这里有明显的表现不佳者:线性激活、具有默认 theta 值的阈值 ReLU 和具有非常大的 alpha 的泄漏 ReLU。线性激活在所有四项指标中都未能超过 93%的准确率。阈值 ReLU 在最终时期具有最低的精度值,并且是最低的最大实现精度之一,这意味着存在过拟合。广泛使用的 ReLU 显示了平均结果。这里明显的赢家是 ELU 和 SELU 的激活。
这是样本标准化数据的同一张表:
这里的排名基本上是一样的,只是在表的中间有一些小的变动。然而,一般来说,用标准化数据训练的网络的性能稍差。唯一的例外是阈值 ReLU,其结果有了显著的改善。
现在让我们更仔细地比较一下这两种数据转换方式:
(抱歉,桌子太宽了。向右滚动以查看其余部分。)
平均而言,使用标准化数据,您将能够获得稍微好一点的结果。有几个激活,它们(如果你出于某种原因决定使用它们)在标准化数据上表现得更好。然而,这里有一件有趣的事情需要注意:对于大量的激活,使用标准化数据比使用标准化数据更早达到最大准确度值。因此,如果为了您的特定实验,您可以牺牲一些准确性,以便更快地获得最大结果,那么标准化数据是一条可行之路。
现在让我们更仔细地研究用每个激活函数训练网络的细节,这样你可以清楚地看到它们训练行为的不同。(注意,很多图!)
线性的
线性激活显示了最坏的结果。从下图可以看出,训练一直很不稳定,尤其是对于标准化的数据。显示每次实验达到最大验证准确度的时刻的垂直线分布在整个 x 轴上。这意味着在某个点之后,优化器无法找到更好的局部最小值,来回跳跃。这可以通过降低学习率来解决(我们将在后面的系列文章中探讨),但这也是模型的线性问题,无法对过于复杂的依赖关系进行建模。
Fig.13 Validation accuracy for models trained with Linear activation
乙状结肠的
与线性模型相比,Sigmoid 激活产生了更稳定的模型,并且达到的最大验证准确度值更接近训练结束,但是验证准确度值是平均的。
Fig.14 Validation accuracy for models trained with Sigmoid activation
硬乙状结肠
与普通的 Sigmoid 非常相似,它具有较低的最终平均值和较低的最大平均值,但达到的最大验证精度与 Sigmoid 完全相同。因此,对于这种特殊的设置,我们可以说,硬乙状结肠表现不如乙状结肠。归一化数据上的最大值更接近训练结束,这告诉我们,如果调整学习率,可能会取得更好的效果。
Fig.15 Validation accuracy for models trained with Hard Sigmoid activation
双曲正切值
尽管与 Sigmoid 的最大验证精度大致相同,TanH 的稳定性稍差。大多数局部最小值已经接近训练的中间,优化器无法进一步改善结果。具有这种激活函数的模型也可以从降低学习速率中受益。有趣的是,尽管 TanH 被认为比 Sigmoid 更先进,并且现在使用得更频繁,但后者可能仍然更适用于某些网络设置和任务。
Fig.16 Validation accuracy for models trained with TanH activation
软设计
在标准化数据中,所有线都非常接近平均值。但是训练的结果也有些一般。使用标准化数据时,SoftSign 的稳定性要差得多,尽管最终精度稍高。
Fig.17 Validation accuracy for models trained with SoftSign activation
整流线性单元
这是我们在实验中第一次看到过度拟合。正如我们所看到的,模型在第 10 和第 40 个时期之间达到其峰值性能,然后开始缓慢下降。达到的最大验证准确度与标准化数据的 Sigmoid 相同,但低于标准化数据。因此,无需进一步微调,乙状结肠搏动 ReLU 在这里。
Fig.18 Validation accuracy for models trained with ReLU activation
泄漏 ReLU
Alpha = 0.3(默认)
泄漏 ReLU 的表现比它的传统变种 ReLU 更差。最大验证精度和最后时期的精度都低于 ReLU。这意味着即使过度拟合,ReLU 对于我们的情况也是更可取的。
Fig.19 Validation accuracy for models trained with Leaky ReLU activation with alpha = 0.3
阿尔法= 0.01
减少“泄漏”参数α有助于模型显著改善标准化和规范化数据的结果。
Fig.20 Validation accuracy for models trained with Leaky ReLU activation with alpha = 0.01
阿尔法= 1.5
将 alpha 设置为相对较大的值会导致我们实验中最差的性能之一。训练极不稳定,准确率很低。所以不要那么做。
Fig.21 Validation accuracy for models trained with Leaky ReLU activation with alpha = 1.5
阈值 ReLU
θ= 0.7
这是一个非常有趣的案例。用阈值 ReLU 和标准化数据训练的模型很快达到最大值,然后开始下降。所以这是一个明显的过度拟合,也是非常糟糕的整体性能。而在标准化数据上,尽管与其他激活相比仍然表现不佳,但根本没有过度拟合。我们看到,原始论文中提出的θ值对于归一化数据并不适用。这可能是对这两种数据转换方法的模型性能差异的最好证明。
Fig.22 Validation accuracy for models trained with Thresholded ReLU activation with theta = 0.7
θ= 1.0(默认)
这个θ值导致了更差的性能。这意味着你应该谨慎使用深度学习框架中内置的默认值,并始终检查更改它们是否会带来更好的结果。
Fig.23 Validation accuracy for models trained with Thresholded ReLU activation with theta = 1.0
θ=数据平均值
在精度和过拟合方面都有显著提高,但总体性能仍低于平均水平。这个例子演示了根据您输入的数据调整模型参数是非常重要的。
Fig.24 Validation accuracy for models trained with Thresholded ReLU activation with theta = data_mean
指数线性单位(ELU)
Alpha = 1.0(默认)
相当稳定,在最大验证准确度方面是最好的之一。最大值是在训练过程中达到的,因此它可以更快地达到最佳结果,并且通过对学习速率进行一些微调,这些结果可能会得到进一步改善。这种激活在标准化和规范化数据上都表现很高。
Fig.25 Validation accuracy for models trained with ELU activation with alpha = 1.0
阿尔法= 0.5
性能也非常好,至少在标准化数据上是这样。
Fig.26 Validation accuracy for models trained with ELU activation with alpha = 0.5
阿尔法= 1.5
在标准化数据中,alpha = 1.5 的 ELU 在所有激活函数中处于领先地位。它的表现几乎和 SELU 一样好。标准化数据的最大值非常接近训练结束,因此,如果进一步训练,可能会取得更好的结果。
Fig.27 Validation accuracy for models trained with ELU activation with alpha = 1.5
比例指数线性单位(SELU)
第二好的激活功能,相当稳定,表现出更高的性能。标准化数据稍不稳定。稍后,我们将检查是否有可能通过降低学习率和调整辍学来改善结果,以便稳定训练和实现更高的准确性。
Fig.28 Validation accuracy for models trained with SELU activation
SoftPlus
就标准化数据的最大验证精度而言,该函数排名第三(仅次于 SELU 和 ELU),但与领先者有很大差距。根据标准化数据,存在过度拟合和低于平均水平的性能。
如果我们将 SoftPlus 与 ReLU 的结果进行比较,我们可以看到,ReLU 与 SoftPlus 相比“质量更好或相当”的说法并没有在我们的特定设置中得到证实。它支持了一个被广泛接受的观点,即神经网络组件的基准测试是困难的,并且在不同的网络设置中会导致相互矛盾的结果。
Fig.29 Validation accuracy for models trained with SoftPlus activation
摘要
SoftPlus 击败 ReLU 的例子与深度学习之父在他们的论文中所说的相反,这意味着我们在这个实验中获得的激活函数的排名和结果只适用于我们正在考虑的神经网络的特定配置,并且通常不会告诉你一个激活函数比另一个更好。但至少对于在图像分类任务上具有 RMSProp 优化器的 3 层全连接网络,我们可以说,通过使用指数线性单元激活或其缩放变化,您将能够获得比其他激活更好的结果。
总而言之,本实验的主要学习点是,对于类似的任务和神经网络设置,您应该:
- 为了达到更高的验证精度,对数据进行标准化,如果需要更快的结果,则进行标准化;
- 使用 ELU 或 SELU 激活;
- 尝试调整激活函数参数,看看它们是否能产生更好的结果;
- 是的,永远不要使用线性激活。
你可以在我的 github 上找到实验和可视化结果的代码。在的下一部分中,我们将扩展我们的实验,并以同样的方式测试其他优化器,看看它们在与相同的激活函数组合时表现如何。
我总是很高兴认识新朋友并分享想法,所以如果你喜欢这篇文章,可以考虑在 LinkedIn 上加我。
深度学习一个不是很深的神经网络系列:
- 第 1 部分:我们的数据中有什么
- 第二部分:激活功能
- 第 3a 部分:优化器概述
- 第 3b 部分:选择优化器
- 第四部分:如何找到合适的学习率
- 第 5 部分:压差和噪声
- 第 6 部分:权重初始化
- 第 7 部分:正规化
- 第 8 部分:批处理规范化
- 第 9 部分:大小很重要
- 第 10 部分:将它们融合在一起
深度监视
用神经网络检测暴力
根据 IHS Markit 关于视频监控的年度出版物,今年可能会有 1.3 亿台监控摄像机出货。[1]
英国安全行业协会 2013 年委托的一份报告得出结论,英国安装了 410 万至 590 万个闭路电视摄像机。[2]最高估计是每 11 个人有一台监控摄像机。
除了围绕国有和私营摄像头的政策问题,视频监控也给大数据和机器学习带来了挑战。
一份报纸估计,中国天津市的 60 万个摄像头每天产生 50pb 的数据。[3]
许多监控系统仍然需要人工监管。因此,计算机视觉的最新进展被视为视频监控的一个重要趋势,可能会带来显著的效率提升。
本文重点关注一个重要方面,探索最先进的深度神经网络可以在多大程度上“看到”图像和视频中的暴力。
在下面描述的模型中有两个反复出现的主题:首先,迁移学习用于提高以小训练集为特征的设置中的性能。第二,已经成功应用于图像分类和时间序列分析的构建块可以重新用于与视频相关的任务。
图像中暴力检测的迁移学习
Kalliatakis 等人(2017 年)[4]汇编了人权理解数据集。这个由 400 个手动标记的图像文件组成的集合包括儿童士兵的照片和警察与平民之间的暴力互动。
只有借助迁移学习,才能有效处理如此小规模的数据集。
为此,作者比较了 10 个已建立的卷积神经网络的性能。这些模型要么在 ImageNet 数据集上进行预训练,要么——在 8 层 Places 架构的情况下——针对 1000 万张场景照片的集合进行优化[5]。要将模型用作特征提取器,需要移除生成预测的图层。
该模型的第二部分是线性 SVM 分类器,其在 HRUN 数据集上被训练,并接受提取的特征作为输入。
Human rights violations recognition pipeline in Kalliatakis et al. (2017)
使用 50/50 分割的训练和测试图像,作者报告了极好的结果。迁移学习方法对儿童兵类别的平均准确率达到 90%,对警察与平民之间的暴力互动的平均准确率接近 96%。有趣的是,最好的结果是用位置架构实现的。
暴力视频的检测
当然,视频是图像序列。虽然大多数最先进的图像分类系统以某种形式使用卷积层,但顺序数据经常由长短期记忆(LSTM)网络处理。因此,这两个构件的组合有望在视频分类任务中表现良好。
一个这样的组合有一个自我描述的名字convltm[6]。标准 LSTM 使用简单的矩阵乘法来衡量不同门内的输入和先前状态。在 ConvLSTM 中,这些运算被卷积所取代。
Sudhakaran 和 Lanz (2017)的一篇论文测试了这种方法在检测视频内容中的暴力方面的效果如何[7]。
为了迫使网络模拟随时间的变化,作者使用两个相邻帧的差异作为每一步的输入。然后使用 AlexNet 架构生成一个向量表示,并发送给 ConvLSTM 实例。在所有帧被处理之后,最终的隐藏状态被转发到计算分类的完全连接的层序列。
Block diagram of the model proposed by Sudhakaran & Lanz (2017)
在小数据集上评估该模型。冰球比赛数据集由 500 个冰球比赛视频组成,显示比赛或其他内容。电影数据集包含 100 个打斗场景和 100 个没有暴力的场景。暴力流人群暴力数据集是 246 个视频的集合,描述了体育赛事中暴力和非暴力的人群行为。为了扩充数据,作者进行了随机裁剪和水平翻转。
该论文报告了在暴力流数据集上的第二名,冰球视频中暴力检测的最先进结果,以及在电影数据集上的完美结果。
对于曲棍球数据集,使用两个相邻帧的差异作为输入,并使用预训练的 AlexNet 进行特征提取,与使用单个帧作为输入的随机初始化网络相比,精确度从 94%提高到 97%。
考虑到暴力和非暴力场景可能表现出高度的特征重叠,这些结果是显著的。需要仔细观察一些较低层次的细节,例如,区分冰球比赛中的打斗和拥抱。
暴力是可以察觉的异常现象
在文明社会中,和平共处是常态,暴力是例外。这一幸运的事实允许 Sultani 等人(2018)[8]将智能监控视为异常检测问题。除了人际暴力,他们考虑的 13 种异常情况还包括其他纵火、盗窃和事故。
利用 YouTube 和 LiveLeak 上的搜索功能,研究人员汇编了一组显示真实世界异常情况的视频。只有监控摄像头拍摄的未经编辑的录像进入了 1900 个视频的最终集合。数据集在异常事件(标记为正)和正常事件(标记为负)之间保持平衡。
多示例学习
每个视频被表示为一包 m 个时间片段。在肯定的情况下,假定至少一个 m 段包含异常。在否定的情况下,没有一个段包含异常。
为了收集大量视频的例子,注释者在包的层次上提供标签,而不是在单个片段的层次上。换句话说,数据集告诉你一个给定的视频是否显示任何异常。它不会告诉你异常发生的时间。
以下符号指的是代表视频 V 的包 B 中的第 i 段。字母 a 和 n 分别用于表示异常和正常事件:
函数 f 为每个片段分配 0 到 1 之间的异常分数。
一个关键的想法是将得分最高的积极部分推得尽可能远离得分最高的消极部分。这一基本目标用以下铰链损失函数表示:
在最好的可能情况下,对于异常视频,最高分段分数是 1,对于正常视频,最高分段分数是 0。这会导致 0:
最差情况下,分数颠倒,损失为 2:
基于 C3D 的时空特征学习
评分函数 f 使用从预训练的卷积 3D (C3D)网络中提取的表示,这是一种专门针对迁移学习而设计的架构。
图像是二维的。视频分析是时空的:它增加了时间作为第三维。在 Tran 等人(2015)[9]描述的 C3D 网络中,视频的大小被调整为 128 x171(4:3 的纵横比),并被分成每个 16 帧的剪辑。使用三个颜色通道,输入的大小为 3x16x128x171。该网络中的卷积滤波器具有 d x k x k 格式,其中 d 指时间维度,而 k x k 指空间维度。经验结果表明,3x3x3 配置是一个合适的选择。
网络中的前五个块由一个或两个卷积层组成,随后是一个汇集操作。为了生成预测,计算由两个全连接层(标识为 fc6 和 fc7 )的序列继续,并最终由 softmax 层完成。C3D 网络的作者在 Sports-1M 数据集上训练了该模型,该数据集收集了来自 487 个体育类别的超过 100 万个视频。
经过训练的模型的表达能力可以在其他任务中重复使用。来自不同数据集的视频首先被分割成所需格式的 16 帧长的剪辑。然后,对各个片段的 fc6 片段激活进行平均,以形成具有 4096 个条目的 L2 归一化特征向量。
实时处理
回到异常检测器,该特征向量被用作具有丢失的 3 层全连接神经网络的输入。该架构中的最后一层只有一个单元,通过将 sigmoid 激活函数应用于加权输入来计算异常分数。
Flow diagram of the anomaly detection approach proposed by Sultani et al. (2018)
该论文报告了对于 50%的分类阈值,接收器工作特性(ROC)的曲线下面积(AUC)值为 0.754,并且虚警率为 1.9。对于这两个指标,与其他三种方法相比,本系统获得了最高分。
要在现实世界的应用中可行,异常检测器需要计算效率高,并及时发出警报。在极端情况下,慢速和快速性能之间的差异可能是生死攸关的问题。使用 Nvidia Geforce GTX 1080 GPU 作为参考,作者报告说,该模型能够以每秒 367 帧的运行速度进行实时处理。
深度学习模型在图像分析方面取得的成功正在被复制到视频内容中。
暴力检测是一个重要的应用,但还有许多其他应用。神经网络已被用于检测跌倒,这是老年人的一个主要风险,可以通过智能家庭医疗保健系统来解决。[10]在灵长类动物学中,自动处理通过相机陷阱录制的视频可以帮助科学家全面研究我们进化亲属的行为[11]。
虽然可能有人会说这种技术天生就偏向某一方,但它既有做好事的潜力,也有做坏事的潜力。
智能监控可以被那些致力于确保公共安全、保护亲人和威慑罪犯的人所使用。或者它可能被那些试图建立或扩大警察国家的人滥用。
视频平台上的自动内容分类可以被视为维护社区标准的有效方式。或者,它可以被视为审查制度的一种手段,有可能扼杀挑战性和非传统的表达方式。
为了成功实现人工智能的大众化,我们需要探索并有可能促进双刃技术的防御用途。
感谢您的阅读!如果你喜欢这篇文章,请点击拍手按钮并关注我,以获得更多关于最新机器学习应用程序的信息。
参考
[1] *IHS Markit 的 2018 年顶级视频监控趋势。*检索到 108 年 3 月 23 日。
[2] 英国安全行业协会—概述。2018 年 3 月 23 日检索。
[3]肖军,廖,李,胡军,陈,杨,胡,2015 .利用大监控视频数据中的全局冗余进行高效编码。集群计算, 18 (2),第 531–540 页。
[4] Kalliatakis,g .,Ehsan,s .,Fasli,m .,Leonardis,a .,Gall,j .,McDonald-Maier,K.D .,2017 年。图像中人权侵犯的检测:卷积神经网络有帮助吗?。 arXiv 预印本 arXiv:1703.04103 。
[5]周,b .,拉佩德里扎,a .,科斯拉,a .,奥利瓦,a .,托拉尔巴,a .,2017。地点:用于场景识别的 1000 万图像数据库。IEEE 模式分析与机器智能汇刊*。*
[6]邢建华、陈志军、王、杨、黄伟光和吴伟光,2015 年。卷积 LSTM 网络:降水临近预报的机器学习方法。在神经信息处理系统的进展(第 802–810 页)。
[7] Sudhakaran,s .和 Lanz,o .,2017 年 8 月。学习使用卷积长短期记忆检测暴力视频。在基于先进视频和信号的监控(AVSS),2017 年第 14 届 IEEE 国际会议上(第 1–6 页)。IEEE。
[8]苏丹尼,w .,陈,c .和沙阿,m .,2018 年。监控视频中的真实世界异常检测。 arXiv 预印本 arXiv:1801.04264 。
[9] Tran,d .,Bourdev,l .,Fergus,r .,Torresani,l .和 Paluri,m .,2015 年 12 月。用 3d 卷积网络学习时空特征。在计算机视觉(ICCV),2015 IEEE 国际会议上(第 4489–4497 页)。IEEE。
[10]王,s,陈,l,周,z,孙,x,董,2016 .基于 PCANet 的监控视频中人体跌倒检测。多媒体工具与应用, 75 (19),第 11603–11613 页。
[11] Pebsworth,P.A .和 LaFleur,m .,2014 年。通过使用照相机陷阱推进灵长类动物的研究和保护:特刊介绍。《国际灵长类动物杂志, 35 (5),第 825–840 页。
面向自然语言处理的深度迁移学习——基于通用嵌入的文本分类
揭秘通用句子编码器指南
介绍
迁移学习是一个令人兴奋的概念,我们试图将一个领域和任务中的现有知识运用到另一个领域和任务中。灵感来自我们——人类,我们自己——我们有一种天生的能力,不需要从零开始学习一切。我们将过去学到的知识转移并加以利用,以解决各种各样的任务。有了计算机视觉,我们有了优秀的大数据集,如 Imagenet,在此基础上,我们可以获得一套世界级的、最先进的预训练模型,以利用迁移学习。但是自然语言处理呢?考虑到文本数据是如此多样、嘈杂和非结构化,这就是挑战所在。我们最近在单词嵌入方面取得了一些成功,包括像 Word2Vec、GloVe 和 FastText 这样的方法,所有这些都在我的文章 【文本数据的特征工程】 中有所涉及。
[## 理解特征工程(第 4 部分)——深度学习的直观实践方法…
驯服非结构化文本数据的更新、高级策略
towardsdatascience.com](/understanding-feature-engineering-part-4-deep-learning-methods-for-text-data-96c44370bbfa)
在本文中,我们将展示几种最先进的通用句子嵌入编码器,与单词嵌入模型相比,它们往往会提供令人惊讶的良好性能,特别是在迁移学习任务的少量数据上。我们将介绍以下型号:
- 基线平均句子嵌入
- Doc2Vec
- 神经网络语言模型(动手演示!)
- 跳过思维向量
- 快速思考矢量
- 推断
- 通用语句编码器
我们将尝试涵盖基本概念,并展示一些利用 Python 和 Tensorflow 的实际例子,这是一个侧重于情感分析的文本分类问题!
为什么我们对嵌入如此着迷?
嵌入式背后突然出现的热潮是什么?我相信你们中的许多人可能在任何地方都能听到它。让我们先搞清楚基本情况,从炒作中切入。
嵌入是固定长度的向量,通常用于编码和表示实体(文档、句子、单词、图形!)
Comparing feature representations for audio, image and text
预测方法 像 基于神经网络的语言模型 尝试通过查看语料库中的单词序列来预测单词,在此过程中,它学习分布式表示,给我们提供密集的单词嵌入。
现在你可能会想,没什么大不了的,我们从文本中得到一堆向量。现在怎么办?对嵌入的狂热在于,如果我们有一个很好的文本数据的数字表示,甚至可以捕捉上下文和语义,我们就可以用它来完成各种下游的真实任务,比如情感分析、文本分类、聚类、摘要、翻译等等。事实是,机器学习或深度学习模型在数字上运行,嵌入是编码这些模型将使用的文本数据的关键。
Text Embeddings
这里的一个大趋势是找出所谓的 【通用嵌入】 ,这些嵌入基本上是在巨大的语料库上训练深度学习模型获得的预训练嵌入。这使我们能够在各种各样的任务中使用这些预训练(通用)嵌入,包括缺乏足够数据等约束的场景。这是一个迁移学习的完美例子,利用来自预训练嵌入的先验知识来解决一个全新的任务!下图展示了通用词&句子嵌入的一些最新趋势,这要感谢 HuggingFace 的朋友们的一篇精彩文章!
Recent Trends in Universal Word & Sentence Embeddings (Source: https://medium.com/huggingface/universal-word-sentence-embeddings-ce48ddc8fc3a)
当然,上图中有一些有趣的趋势,包括谷歌的通用句子编码器,我们将在本文中详细探讨!我绝对推荐读者去看看 来自 HuggingFace 的关于通用嵌入趋势 的文章。
单词和句子嵌入在过去的几个月里发展得非常快——关于发生了什么的简要介绍
medium.com](https://medium.com/huggingface/universal-word-sentence-embeddings-ce48ddc8fc3a)
现在,在深入研究通用句子编码器之前,让我们先简要了解一下单词和句子嵌入模型的趋势和发展。
单词嵌入模型的发展趋势
word 嵌入模型可能是从 2013 年的 Word2Vec 开始开发的一些更老、更成熟的模型。基于在基于语义和上下文相似性的连续向量空间中嵌入单词向量,利用深度学习(无监督方法)模型的三种最常见的模型是:
- Word2Vec
- 手套
- 快速文本
这些模型是基于 分布语义学 , 领域中的 分布假说 的原理,它告诉我们,在同一语境中出现和使用的词在语义上彼此相似,并且具有相似的含义(【一个词由其所保持的伙伴来表征】)。如果你对血淋淋的细节感兴趣,请参考我的关于单词嵌入的文章,这篇文章详细介绍了这三种方法。
最近在这方面开发的另一个有趣的模型是【ELMo】。这是艾伦人工智能研究所开发的。ELMo* 是一个取自著名电视剧《芝麻街》的同名著名布偶角色,但实际上是一个缩写,代表“Embedings fromL语言Mo*dels”。
Elmo from Sesame Street!
基本上, ELMo 为我们提供了从深度双向语言模型(biLM)中学习的单词嵌入,该模型通常在大型文本语料库上进行预训练,使这些嵌入的迁移学习能够跨不同的 NLP 任务使用。艾伦·艾告诉我们,ELMo 表征是上下文相关的、深层的和基于字符的,它使用形态学线索来形成表征,即使是对于 OOV(词汇之外)表征。
通用句子嵌入模型的发展趋势
句子嵌入的概念并不是一个非常新的概念,因为当构建单词嵌入时,构建基线句子嵌入模型的最简单的方法之一是平均。
一个 基线句子嵌入模型 可以通过平均每个句子\文档的单个单词嵌入来构建(有点类似于单词袋,其中我们丢失了句子中单词的固有上下文和序列)。我们会在我的文章 中详细介绍 。下图显示了实现这一点的方法。
当然,还有更复杂的方法,比如以单词嵌入的线性加权组合对句子进行编码,然后去除一些共同的主要成分。一定要检查一下, 【一个简单但难以击败的句子嵌入基线】 。
*Doc2Vec 也是米科洛夫等人提出的一种非常流行的方法。艾尔。在他们的论文 【句子和文件的分布式表示】 *。在这里,他们提出了段落向量,这是一种无监督的算法,可以从可变长度的文本片段(如句子、段落和文档)中学习固定长度的特征嵌入。
Word2Vec vs. Doc2Vec (Source: https://arxiv.org/abs/1405.4053)
基于上述描述,该模型通过被训练来预测文档中的单词的密集向量来表示每个文档。唯一的区别是段落或文档 ID,它与常规单词标记一起用来构建嵌入。这样的设计使得该模型能够克服单词袋模型的弱点。
****【NNLM】**神经网络语言模型(Neural-Net Language Models)是基于 Bengio 等人提出的神经概率语言模型的一个非常早期的想法。在他们的论文【A Neural probability Language Model】中,他们在 2003 年谈到学习单词的分布式表示,这允许每个训练句子通知模型关于指数数量的语义相邻句子。该模型同时学习每个单词的分布式表示以及用这些表示表达的单词序列的概率函数。获得概括是因为如果从未见过的单词序列由与形成已经见过的句子的单词相似的单词组成(在具有邻近表示的意义上),则该单词序列获得高概率。
Google 已经建立了一个通用的句子嵌入模型,nnlm-en-dim 128,这是一个基于标记的文本嵌入训练模型,在英文 Google News 200B 语料库上使用了一个三隐层前馈神经网络语言模型。这个模型将任何文本映射成 128 维嵌入。我们不久将在实际操作演示中使用它!
跳过思维向量 也是基于无监督学习的通用句子编码器领域的首批模型之一。在他们提出的论文 【跳过思维向量】 中,他们利用书籍中文本的连续性,训练了一个编码器-解码器模型,试图重建编码段落的周围句子。共享语义和句法属性的句子被映射到相似的向量表示。
Skip-Thought Vectors (Source: https://arxiv.org/abs/1506.06726)
这就像 Skip-gram 模型,但是对于句子,我们试图预测给定源句子的周围句子。
*快速思维向量 是一种最近才出现的学习句子提示的无监督方法。详细内容在论文 【学习句子表征的高效框架】 *中有所提及。有趣的是,他们通过用常规编码器-解码器架构中的分类器替换解码器,将预测句子出现的上下文的问题重新表述为分类问题。
Quick Thought Vectors (Source: https://openreview.net/forum?id=rJvJXZb0W)
因此,给定一个句子和它出现的上下文,分类器根据它们的嵌入表示将上下文句子与其他对比句子区分开来。给定一个输入句子,首先使用某种函数对其进行编码。但是该模型不是生成目标句子,而是从一组候选句子中选择正确的目标句子。将生成视为从所有可能的句子中选择一个句子,这可以被视为对生成问题的一种有区别的近似。
推断 有趣的是,它是一种使用自然语言推断数据学习通用句子嵌入的监督学习方法。这是核心的监督迁移学习,就像我们在计算机视觉的 ImageNet 数据集上训练的预训练模型一样,他们使用斯坦福自然语言推理数据集的监督数据训练通用句子表示。详细内容在他们的论文中提到, 【从自然语言推理数据中监督学习通用句子表示】 。该模型使用的数据集是 SNLI 数据集,包括 570,000 个人工生成的英语句子对,人工标记为三个类别之一:蕴涵、矛盾和中性。它捕捉对理解句子语义有用的自然语言推理。
InferSent training scheme (Source: https://arxiv.org/abs/1705.02364)
基于上图所示的架构,我们可以看到它使用了一个共享的句子编码器,该编码器输出前提和假设 v 的表示。一旦生成了句子向量,就应用 3 种匹配方法来提取 u 和 v 之间的关系:**
- 串联 (u,v)
- 逐元素乘积u∫v****
- 绝对元素差异*| u v |*****
然后将得到的矢量输入到一个三级分类器中,该分类器由多个完全连接的层组成,最终形成一个 softmax 层。
来自 Google 的 通用句子编码器 是 2018 年初发布的最新最好的通用句子嵌入模型之一!通用语句编码器将任何文本编码成 512 维嵌入,可用于各种 NLP 任务,包括文本分类、语义相似性和聚类。它在各种数据源和各种任务上接受训练,目的是动态适应各种各样的自然语言理解任务,这些任务需要对单词序列而不仅仅是单个单词的含义进行建模。
他们的主要发现是,使用句子嵌入的迁移学习往往优于单词嵌入水平迁移。请查看他们的论文, 【通用句子编码器】 了解更多详情。本质上,他们在 TF-Hub 中有两个版本的模型,即 通用语句编码器 。版本 1 利用基于变换器网络的句子编码模型,版本 2 利用深度平均网络(DAN ),其中单词和二元模型的输入嵌入首先被一起平均,然后通过前馈深度神经网络(DNN)产生句子嵌入。我们将很快在实际操作演示中使用版本 2。
理解我们的文本分类问题
**是时候通过动手演示将这些通用句子编码器付诸实践了!正如文章中提到的,我们今天演示的前提将集中在一个非常流行的 NLP 任务,文本分类——在情感分析的背景下。我们将使用基准测试 IMDB 大型电影评论数据集 。随意在这里下载或者你甚至可以从我的 GitHub 资源库 下载。
该数据集包括总共 50,000 条电影评论,其中 25,000 条具有正面情绪,25,000 条具有负面情绪。我们将在总共 30,000 条评论上训练我们的模型作为我们的训练数据集,在 5,000 条评论上验证,并使用 15,000 条评论作为我们的测试数据集。主要目标是正确预测每个评论的正面或负面情绪。
行动中的普遍句子嵌入
现在我们已经明确了我们的主要目标,让我们把通用句子编码器付诸行动吧!整篇教程可在 我的 GitHub 资源库 中作为 Jupyter 笔记本 。随意 下载吧 随便玩玩。我推荐使用一个基于 GPU 的实例来试验这一点。我喜欢使用paper space,在这里你可以在云端旋转笔记本,而无需担心手动配置实例。
云机器学习、人工智能和轻松的 GPU 基础设施
www.paperspace.com](https://www.paperspace.com)
我的设置是一个 8 CPU,30 GB,250 GB 的 SSD 和一个 NVIDIA Quadro P4000 ,它通常比大多数 AWS GPU 实例便宜(尽管我喜欢 AWS!).
**注意:**本教程完全是使用 TensorFlow 构建的,因为它们提供了对句子编码器的简单访问。然而,我不太喜欢他们的旧 API,我正在找人帮助我使用
**tf.keras**
API 而不是**tf.estimator**
重新实现代码。如果你有兴趣投稿的话,请联系我,我们甚至可以在上面刊登你的作品!(我的个人资料和页脚中的联系链接)
加载依赖项
我们从安装**tensorflow-hub**
开始,这使我们能够容易地使用这些句子编码器。
现在让我们加载本教程的基本依赖项!
**import** **tensorflow** **as** **tf**
**import** **tensorflow_hub** **as** **hub**
**import** **numpy** **as** **np**
**import** **pandas** **as** **pd**
以下命令帮助您检查**tensorflow**
是否将使用 GPU(如果您已经设置了一个!)
**In [12]: tf.test.is_gpu_available()
Out[12]: True****In [13]: tf.test.gpu_device_name()
Out[13]: '/device:GPU:0'**
加载和查看数据集
我们现在可以加载数据集,并使用**pandas**
**查看它。**我在我的存储库中提供了数据集的压缩版本,您可以按如下方式使用。
**<class 'pandas.core.frame.DataFrame'>
RangeIndex: 50000 entries, 0 to 49999
Data columns (total 2 columns):
review 50000 non-null object
sentiment 50000 non-null object
dtypes: object(2)
memory usage: 781.3+ KB**
我们将情感列编码为 1 和 0,只是为了在模型开发过程中使事情更容易(标签编码)。
Our movie review dataset
构建训练、验证和测试数据集
在开始建模之前,我们将创建训练、验证和测试数据集。我们将使用 30,000 条评论用于训练,5,000 条用于验证,15,000 条用于测试。您可以使用类似于scikit-learn
中的train_test_split()
的列车测试分割功能。我很懒,使用简单的列表切片对数据集进行了子集化。
**((30000,), (5000,), (15000,))**
基本文本争论
我们需要做一些基本的文本争论和预处理,以消除文本中的一些干扰,如缩写、不必要的特殊字符、HTML 标签等。下面的代码帮助我们构建了一个简单而有效的文本辩论系统。请务必安装以下库,以防您没有这些库。
下面的函数帮助我们构建我们的文本辩论系统。
现在,让我们使用上面实现的函数对数据集进行预处理。
构建数据摄取功能
由于我们将使用**tf.estimator**
API 在**tensorflow**
中实现我们的模型,我们需要定义一些函数来构建数据和特性工程管道,以使数据在训练期间流入我们的模型。以下函数将帮助我们。我们利用了**numpy_input_fn()**
,它有助于将 numpy 数组的字典输入到模型中。
我们现在准备建立我们的模型!
用通用语句编码器构建深度学习模型
在构建模型之前,我们需要首先定义利用通用句子编码器的句子嵌入特性。我们可以使用下面的代码来实现。
**INFO:tensorflow:Using /tmp/tfhub_modules to cache modules.**
正如我们所讨论的,我们使用通用句子编码器版本 2,它作用于我们输入字典中的**sentence**
属性,这将是我们评论的**numpy**
数组。我们将建立一个简单的前馈 DNN,现在有两个隐藏层。只是一个标准模型,没有什么太复杂的,因为我们想看看这些嵌入在一个简单的模型上表现如何。这里,我们以预训练嵌入的形式利用迁移学习。我们在这里不是通过设置**trainable=False**
来保持嵌入权重固定来进行微调。
我们已经将我们的**batch_size**
设置为 256 ,并且我们将在 1500 步的 256 记录中分批流入数据,这大致相当于12–13 个时期。
模特培训
现在,让我们在训练数据集上训练我们的模型,并在步骤 100 在训练和验证数据集上进行评估。
--------------------------------------------------------------------
**Training for step = 0** Train Time (s): 78.62789511680603
**Eval Metrics (Train):** {**'accuracy': 0.84863335**, 'accuracy_baseline': 0.5005, **'auc': 0.9279859**, 'auc_precision_recall': 0.92819566, 'average_loss': 0.34581015, 'label/mean': 0.5005, 'loss': 44.145977, **'precision': 0.86890674**, 'prediction/mean': 0.47957155, **'recall': 0.8215118**, 'global_step': 100}
**Eval Metrics (Validation):** {**'accuracy': 0.8454**, 'accuracy_baseline': 0.505, **'auc': 0.92413086**, 'auc_precision_recall': 0.9200026, 'average_loss': 0.35258815, 'label/mean': 0.495, 'loss': 44.073517, **'precision': 0.8522351**, 'prediction/mean': 0.48447067, **'recall': 0.8319192**, 'global_step': 100}
--------------------------------------------------------------------
**Training for step = 100**
Train Time (s): 76.1651611328125
**Eval Metrics (Train):** {**'accuracy': 0.85436666**, 'accuracy_baseline': 0.5005, **'auc': 0.9321357**, 'auc_precision_recall': 0.93224275, 'average_loss': 0.3330773, 'label/mean': 0.5005, 'loss': 42.520508, **'precision': 0.8501513**, 'prediction/mean': 0.5098621, **'recall': 0.86073923**, 'global_step': 200}
**Eval Metrics (Validation):** {**'accuracy': 0.8494**, 'accuracy_baseline': 0.505, **'auc': 0.92772096**, 'auc_precision_recall': 0.92323804, 'average_loss': 0.34418356, 'label/mean': 0.495, 'loss': 43.022945, **'precision': 0.83501947**, 'prediction/mean': 0.5149463, **'recall': 0.86707073**, 'global_step': 200}
--------------------------------------------------------------------
...
...
...
--------------------------------------------------------------------
**Training for step = 1400**
Train Time (s): 85.99037742614746
**Eval Metrics (Train):** {**'accuracy': 0.8783**, 'accuracy_baseline': 0.5005, **'auc': 0.9500882**, 'auc_precision_recall': 0.94986326, 'average_loss': 0.28882334, 'label/mean': 0.5005, 'loss': 36.871063, **'precision': 0.865308**, 'prediction/mean': 0.5196238, **'recall': 0.8963703**, 'global_step': 1500}
**Eval Metrics (Validation):** {**'accuracy': 0.8626**, 'accuracy_baseline': 0.505, **'auc': 0.93708724**, 'auc_precision_recall': 0.9336051, 'average_loss': 0.32389137, 'label/mean': 0.495, 'loss': 40.486423, **'precision': 0.84044176**, 'prediction/mean': 0.5226699, **'recall': 0.8917172**, 'global_step': 1500}
--------------------------------------------------------------------
**Training for step = 1500**
Train Time (s): 86.91469407081604
**Eval Metrics (Train):** {**'accuracy': 0.8802**, 'accuracy_baseline': 0.5005, **'auc': 0.95115364**, 'auc_precision_recall': 0.950775, 'average_loss': 0.2844779, 'label/mean': 0.5005, 'loss': 36.316326, **'precision': 0.8735527**, 'prediction/mean': 0.51057553, **'recall': 0.8893773**, 'global_step': 1600}
Eval Metrics (Validation): {**'accuracy': 0.8626**, 'accuracy_baseline': 0.505, **'auc': 0.9373224**, 'auc_precision_recall': 0.9336302, 'average_loss': 0.32108024, 'label/mean': 0.495, 'loss': 40.135033, **'precision': 0.8478599**, 'prediction/mean': 0.5134171, **'recall': 0.88040406**, 'global_step': 1600}
我在上面的输出日志中突出显示了感兴趣的指标,如您所见,在我们的验证数据集上,我们获得了接近 87%的总体精度,以及 94%的 T21 AUC,这在这样一个简单的模型上是非常好的!
模型评估
现在,让我们评估我们的模型,并检查训练和测试数据集的整体性能。
根据我们之前在验证数据集上观察到的情况,我们获得了接近 87%的测试数据总体准确性,从而为我们提供了一致的结果!因此,这应该让您知道利用预先训练的通用句子嵌入是多么容易,而不用担心功能工程或复杂建模的麻烦。
额外收获:不同通用句子嵌入的迁移学习
现在让我们尝试基于不同的句子嵌入来构建不同的深度学习分类器。我们将尝试以下方法:
- NNLM-128
- 使用-512
我们还将在这里讨论两种最重要的迁移学习方法。
- 使用冻结的预训练句子嵌入建立模型
- 建立一个模型,我们在训练期间微调和更新预训练的句子嵌入
下面这个通用函数可以从**tensorflow-hub**
开始即插即用不同的通用语句编码器!
我们现在可以使用上面定义的方法来训练我们的模型。
**====================================================================
Training with** [**https://tfhub.dev/google/nnlm-en-dim128/1**](https://tfhub.dev/google/nnlm-en-dim128/1) **Trainable is: False
====================================================================**
--------------------------------------------------------------------
**Training for step = 0**
Train Time (s): 30.525171756744385
**Eval Metrics (Train): {'accuracy': 0.8480667, 'auc': 0.9287864, 'precision': 0.8288572, 'recall': 0.8776557}**
**Eval Metrics (Validation): {'accuracy': 0.8288, 'auc': 0.91452694, 'precision': 0.7999259, 'recall': 0.8723232}**
--------------------------------------------------------------------
...
...
--------------------------------------------------------------------
**Training for step = 1500**
Train Time (s): 28.242169618606567
**Eval Metrics (Train): {'accuracy': 0.8616, 'auc': 0.9385461, 'precision': 0.8443543, 'recall': 0.8869797}
Eval Metrics (Validation): {'accuracy': 0.828, 'auc': 0.91572505, 'precision': 0.80322945, 'recall': 0.86424243}** **====================================================================
Training with** [**https://tfhub.dev/google/nnlm-en-dim128/1**](https://tfhub.dev/google/nnlm-en-dim128/1) **Trainable is: True
====================================================================**
--------------------------------------------------------------------
**Training for step = 0**
Train Time (s): 45.97756814956665
**Eval Metrics (Train): {'accuracy': 0.9997, 'auc': 0.9998141, 'precision': 0.99980015, 'recall': 0.9996004}
Eval Metrics (Validation): {'accuracy': 0.877, 'auc': 0.9225529, 'precision': 0.86671925, 'recall': 0.88808084}**
--------------------------------------------------------------------
...
...
--------------------------------------------------------------------
**Training for step = 1500** Train Time (s): 44.654765605926514
**Eval Metrics (Train): {'accuracy': 1.0, 'auc': 1.0, 'precision': 1.0, 'recall': 1.0}
Eval Metrics (Validation): {'accuracy': 0.875, 'auc': 0.91479605, 'precision': 0.8661916, 'recall': 0.8840404}** **====================================================================
Training with** [**https://tfhub.dev/google/universal-sentence-encoder/2**](https://tfhub.dev/google/universal-sentence-encoder/2) **Trainable is: False
====================================================================**
--------------------------------------------------------------------
**Training for step = 0** Train Time (s): 261.7671597003937
**Eval Metrics (Train): {'accuracy': 0.8591, 'auc': 0.9373971, 'precision': 0.8820655, 'recall': 0.8293706}
Eval Metrics (Validation): {'accuracy': 0.8522, 'auc': 0.93081224, 'precision': 0.8631799, 'recall': 0.8335354}**
--------------------------------------------------------------------
...
...
--------------------------------------------------------------------
**Training for step = 1500** Train Time (s): 258.4421606063843
**Eval Metrics (Train): {'accuracy': 0.88733333, 'auc': 0.9558296, 'precision': 0.8979955, 'recall': 0.8741925}
Eval Metrics (Validation): {'accuracy': 0.864, 'auc': 0.938815, 'precision': 0.864393, 'recall': 0.860202}** **====================================================================
Training with** [**https://tfhub.dev/google/universal-sentence-encoder/2**](https://tfhub.dev/google/universal-sentence-encoder/2) **Trainable is: True
====================================================================**
--------------------------------------------------------------------
**Training for step = 0** Train Time (s): 313.1993100643158
**Eval Metrics (Train): {'accuracy': 0.99916667, 'auc': 0.9996535, 'precision': 0.9989349, 'recall': 0.9994006}
Eval Metrics (Validation): {'accuracy': 0.9056, 'auc': 0.95068294, 'precision': 0.9020474, 'recall': 0.9078788}**
--------------------------------------------------------------------
...
...
--------------------------------------------------------------------
**Training for step = 1500** Train Time (s): 305.9913341999054
**Eval Metrics (Train): {'accuracy': 1.0, 'auc': 1.0, 'precision': 1.0, 'recall': 1.0}
Eval Metrics (Validation): {'accuracy': 0.9032, 'auc': 0.929281, 'precision': 0.8986784, 'recall': 0.9066667}**
我已经在上面的输出中描述了重要的评估指标,你可以看到我们的模型确实得到了一些好的结果。下表很好地总结了这些比较结果。
Comparing results from different Universal Sentence Encoders
看起来谷歌的带有微调的通用句子编码器在测试数据上给了我们最好的结果。让我们加载这个保存的模型,并对测试数据进行评估。
**[0, 1, 0, 1, 1, 0, 1, 1, 1, 1]**
评估模型性能的最佳方法之一是以混淆矩阵的形式可视化模型预测。
Confusion Matrix from our Best Model Predictions
我们还可以使用 scikit-learn 打印出模型的分类报告,以显示可以从混淆矩阵中获得的其他重要指标,包括精确度、召回率和 f1 分数。
Model Performance Metrics on Test Data
我们在测试数据上得到了整体模型精度和f1-得分的 90% ,这真是太好了!去试试吧,也许会得到更好的分数,然后告诉我!
结论和未来范围
通用的句子嵌入无疑是在多样化的自然语言处理任务中实现迁移学习的一个巨大进步。事实上,我们已经看到像 ELMo,Universal Sentence Encoder,ULMFiT 这样的模型确实成为头条新闻,因为它们展示了预先训练的模型可以用于在 NLP 任务中实现最先进的结果。著名的研究科学家和博客 Sebastian Ruder ,根据他最近写的一篇非常有趣的文章 在他最近的推文中提到了同样的事情。
我对进一步推广 NLP,使我们能够轻松解决复杂任务的未来充满期待!**
本文中用于动手演示的代码可以在 我的 GitHub 资源库 中找到,作为一个 Jupyter 笔记本 你可以随便摆弄!
****需要帮助:正如我提到的,我正在找人帮我转换这段代码,以使用更新的**tf.keras**
API,而不是**tf.estimator**
。感兴趣吗?向我伸出手来!
有反馈给我吗?或者有兴趣与我一起从事研究、数据科学、人工智能甚至发表一篇关于TDS的文章?可以在LinkedIn上联系我。
** [## 人工智能顾问&数据科学导师-跳板| LinkedIn
查看 Dipanjan Sarkar 在世界最大的职业社区 LinkedIn 上的个人资料。Dipanjan 有 2 份工作列在…
www.linkedin.co](https://www.linkedin.com/in/dipanzan/)
感谢 Durba 编辑此文。***
深度、广度和廉价的机器学习
在竞争的假设中,应该选择假设最少的一个。
以上引文是维基百科对奥卡姆剃刀的翻译。它也是计算机系统设计、分析建模和一般系统工程中通用原则的基础。换句话说:简单胜过复杂。
然而,在技术、数据、分析生态圈中,我们(包括我自己)对闪亮的新事物着迷。部分原因是,热门、时髦、闪亮的东西会获得大量关注,我们在招聘和工作中已经看到,在简历中加入最新的前沿技术专业知识对招聘蜜蜂来说就像蜂蜜一样(这也延伸到了虚高的薪酬)。这是一个循环!公司和招聘人员想要闪亮和时髦的技术技能,候选人被闪亮和时髦的技术吸引来充实他们的简历。在这种炒作周期中,简单战胜了复杂,管用且容易解释的旧袖手旁观被推到一边,取而代之的是最新、最棒、也最复杂的东西。
降低树来增加低垂的果实
对于面临业务问题的分析和数据科学领域的任何专业人士来说,这里有一个重要的启示:首先应用简单而不是复杂的东西!这并不意味着不去学习和跟上最新的技术、工艺、方法和应用。在一个关于廉价学习的重复演讲中,Ted Dunning 向与会者介绍了几个解决大问题的廉价学习技术的例子,值得注意的是——数据泄露网络攻击、商业金融服务欺诈。在这些例子中,Ted 展示了使用算术和基础代数进行大规模数据分析是如何产生显著有效的结果的。Ted Dunning 演讲的主要观点是,使用廉价的学习技术,我们可以降低树的高度,以增加业务问题树上的低挂果实。他建议使用大型数据集、可扩展架构和简单数学的组合来解决问题。一旦降低的采油树上的低悬挂问题得到解决,那么就只能使用一些更复杂和新兴的方法来解决采油树上更高层次的不可能解决的难题。
关于这个话题的另一个观点来自 Xavier Amatriain,他推荐了一个关于如何解决难题的平衡的观点,并且在追求最闪亮和最新的方法时不要忽略已经尝试、测试和证明的技术。他给出的一些反模式的例子是忽略元数据特征,直接跳到从图像中学习特征来进行活动识别。另一个反模式是不使用集成方法和更简单的可解释模型,而是支持复杂的多层网络。当心这个星球上只有少数专家能破译的复杂模型。
如何接受深度、广度和廉价的学习?
Source: Unsplash Map
总结分析领域两位领导者的观点,在进入复杂模型和尖端机器学习、深度学习和人工智能领域的世界时,请记住以下一些概念:
- 不要忘记元数据中隐藏的特性。
- 特征工程可能是繁重的,但是架构工程可能是正确的炼金术。
- 如果你陷入了一个流血的边缘问题,你可能是孤独的,所以要做好准备,为深潜时间做好计划。
- 模型、算法和结果的可解释性非常重要,尤其是在高度管制的领域,甚至在其他领域,这也是一种道德责任。
深度和广度学习
Source: Unsplash Grand Canyon
鉴于这些警告信号,没有理由放弃复杂建模领域的巨大进步——深度学习。这里有一个两全其美的方法,那就是深度&广泛学习。简而言之,在存在特征歧义的地方使用深度学习,将其与广泛学习相结合,以解决具有提供高预测能力的特征的问题。深度和广度学习也可能有助于处理小数据集时的一些冷启动问题。关于深度&广泛学习的进一步阅读,我建议你看一下这篇非常可读的文章,来自谷歌关于这个主题的研究。
如果你喜欢阅读这篇文章,并且对如何在你的企业中引入人工智能和深度学习感到好奇,请阅读我关于分析自动化的文章。
DeepClassifyML 第 1 周第 2 部分
这篇文章是“Hasura 实习”系列的一部分,详细介绍了应用程序的原型阶段。查看 第 1 部分 了解 app 理念和一些计算机视觉基础知识。
第一部分被数据科学精选推荐,这真的很酷,因为这是我的第一个博客。我会尽量让这些博客非常入门,因为我的目标是让深度学习和计算机视觉对于 it 经验很少并且想马上开始的人来说非常简单。但首先要做的是。
Hasura 实习的应用理念是一个使用 Hasura 后端部署的图像分类 web 应用。它会从用户那里获取一张图片,并使用预训练的 InceptionV3 模型将其分类为前 5 类。接下来是原型阶段。这是使用流行的原型工具 Proto.io 完成的。作为应用程序的指导方针/规则的一部分,应用程序必须不超过 3 个屏幕,并且在复杂性方面应该相当简单。但 DeepClassifyML 会更简单,因为应用程序的核心功能可能仅限于一个屏幕。不知何故,我决定把它分成 3 个屏幕,结果如下:
The basic landing page
现在第一个屏幕只是这个基本的登录页面。没什么好吹嘘的。这个登录页面将用户带到剩下的两个页面—
Using a link to upload the your image.
To upload the Image from your system.
第一页使用一个链接上传一张图片,第二页使用您系统中的一张图片对它们进行分类。老实说(正如你可以从这些快照本身看到的!),我真的没有为原型阶段做足够的工作,因为这个应用程序非常基础。我打算做的是深度学习部分。让我们从这个开始。
在这篇文章中,我将讨论作为深度学习基础的神经网络。
神经网络就像你大脑内部的连接。那么你的大脑是如何工作的呢?(神经科学来袭!!)
主要部分神经元,通过一系列被称为树突的精细结构收集来自其他神经元的信号。然后,这些神经元通过被称为轴突的细长支架发出电活动尖峰。**轴突分裂成上千个分支,如图所示。*突触,*每个分支末端的一种结构,将轴突的这种活动转化为电信号,抑制或兴奋相连神经元的活动。当一个神经元接收到的兴奋性输入与其抑制性输入相比足够大时,它会沿着轴突发出一个电活动尖峰。学习是通过改变突触的有效性,从而改变一个神经元对另一个神经元的影响而发生的。
类似地,人工神经网络是由互连单元组成,充当神经元。突触的功能由一个可修改的权重建模,该权重与每个连接相关联。每个人都查看输入数据,并决定如何对数据进行分类,即转换传入活动的模式。因此,一个简单的神经网络通过接收输入数据、处理该信息并最终以决策的形式产生输出来做出决策。简而言之,这可以通过以下两步完成:
- 将每个传入活动乘以连接上的权重,并将所有这些加权输入相加。这是总输入。
- 然后,该单元(神经元)使用输入输出函数,将总输入转换为输出活动(决策)。
这取决于神经网络来学习什么是最重要的,并调整它如何考虑这些数据。因此,神经网络的行为取决于可修改的权重和输入输出函数(激活函数)。
这些权重和激活函数是什么?
让我们考虑一个典型的情况。一些输入数据(比如一个数字,可能同时有许多输入)进入神经网络。这将乘以一个权重值(可修改)。这些权重开始时是随机值,当神经网络知道哪种输入数据会产生正确的结果时,网络就会根据以前的权重导致的分类错误来调整权重。这些权重指定了输入数据的影响强度。这叫做训练神经网络。
较高的权重意味着神经网络认为该输入比其他输入更重要,而较低的权重意味着该输入被认为不太重要。这之后是加权输入数据的总和,以产生单个值。最终的输出值通过一个激活函数,它是神经元动作电位发放的速率。激活功能是在给定神经元输入的情况下,决定其输出应该是什么的功能。它决定实际输出,因此其输出通常被称为“激活”。
接下来我会举一个经典的例子。
假设你想知道哪种学生在学校考试中得分最高。你可能会考虑一些因素,比如一个人学习的小时数,他/她参考的书籍,他们的睡眠时间等等。这些因素将成为神经网络的输入。如果你能计算出每个因素对分数的影响程度,那就太好了。这是神经网络学习识别的内容。它通过更新每个特征的相应权重来学习哪些特征是最好的。权重会发生变化,以便下次对结果进行更准确的分类。
这就是我在这篇文章中的全部内容。下一次,我将深入探讨训练和*学习背后的数学和算法。*再加上深度学习非常酷的应用,比如这个:
【https://www.youtube.com/watch?v=Svk0SxyNdr8
编辑:你可以在 akshaybhatia10@gmail.com 找到我
DeepClassifyML 第 2 周第 1 部分
这篇文章是“Hasura 实习”系列文章的一部分,涵盖了为其设置开发环境。查看 第一部分 、 第二部分 了解 app 想法和一些计算机视觉和神经网络基础知识。
前两篇帖子得到了很大的反响,第二部分**发表在 【走向数据科学】 上。这篇文章将讲述如何为图像分类应用程序设置开发环境。我使用的是 macOS X,但是这些说明适用于 Windows、Mac OS X 和 Linux 平台。这个项目的所有库都可以使用 pip 或 brew 安装。这个项目的主要库是 Keras (带 Tensorflow 后端)和 Flask 。如果想建立一个完整的机器学习环境,最好的办法就是安装 Anaconda 。它是一个开源平台,为数据科学和机器学习提供了 100 多个最流行的 Python 和 R 包。先从 这里 安装 Anaconda。确保选择适合您的平台(Windows、OSX 或 Linux)的下载选项和正确的 python 版本(2.7 或 3.5)。安装是一个快速简单的过程,应该不到 10 分钟(取决于您的连接),并占用大约 1 GB 的驱动器空间。Anaconda 附带了一套叫做 Anaconda Navigator 的图形工具。
深度学习部分,我推荐名为 conda 的 Anaconda 命令行环境。Conda 快速、简单,很难隐藏错误消息,并且您可以快速确认您的环境已安装并正常工作。接下来的几个步骤在 这里 下设置环境部分有很好的记录,所以我将跳过这一部分。另一个很棒的博文是 这里 。
现在你有了一个工作的 Python 环境,可以开始学习、实践、开发和解决机器学习和深度学习问题。你也可以在这里 阅读 Dat Tran 关于在 AWS 上建立深度学习环境的一篇很棒的帖子。
深度学习需要数学。因此,在开始构建我们自己的神经网络之前,理解基础知识非常重要。这篇文章旨在提供一个关于线性代数的 vert 简短复习,以及一些使用已经预装在 Anaconda 中的 NumPy 库的指导,以便在 Python 中有效地处理矩阵。那我们开始吧。(注:需要基本的 Python 编程)
Numpy
NumPy 为 Python 中的数学运算提供了快速的替代方法,因此被设计成可以有效地处理数字组,比如矩阵。你一定要花些时间探索它的 文档 来了解更多。我们在 NumPy 中处理数字最常见的方式是通过[ndarray](https://docs.scipy.org/doc/numpy/reference/arrays.html)
对象。它们类似于 Python 列表,但是可以有任意数量的维度。ndarray
支持非常快速的数学运算(这正是我们想要的),而使用 python 列表会非常慢,不适合这里。一个ndarray
的元素是同质的(都是相同的dtype
),并且由一个正整数元组索引。因为它可以存储任意数量的维度,所以您可以使用ndarray
来表示我们之前讨论过的任何数据类型:标量、向量、矩阵或张量。我们可以从嵌套的 Python 列表中初始化 numpy 数组,并使用方括号访问元素:
*import numpy as npa = np.array([[0, 1, 2], [3, 4, 5]]) *# a 2D, 2 x 3, array*
a*
输出:
*array([[0, 1, 2],
[3, 4, 5]])*
Numpy 还提供了许多创建数组的函数:
*import numpy as npa = np.zeros((2,2)) *# Create an array of all zeros*
print(a) *# Prints "[[ 0\. 0.]*
*# [0\. 0.]]"*b = np.ones((1,2)) *# Create an array of all ones*
print(b) *# Prints "[[ 1\. 1.]]"*d = np.eye(2) *# Create a 2x2 identity matrix*
print(d) *# Prints "[[ 1\. 0.]*
*# [ 0\. 1.]]"**
你可以在文档中阅读关于数组创建的其他方法。
标量
NumPy 中的标量比 Python 中的要复杂一些。而不是 Python 的基本类型像int
、float
等。,NumPy 让我们指定有符号和无符号类型,以及不同的大小。所以除了 Python 的int
,还有类似uint8
、int8
、uint16
、int16
等类型。
我们可以创建一个 Numpy 标量,如下所示:
*a = np.array(5)*
不过,您仍然可以在ndarray
、NumPy 标量和普通 Python 标量之间执行数学运算,您将在基于元素的数学课程中看到这一点。
您可以通过检查它们的shape
属性来查看数组的形状。维数是数组的秩*;数组的形状是一个整数元组,给出了数组在每个维度上的大小。*
*a.shape*
即使标量在数组内部,你仍然可以像普通标量一样使用它们。所以你可以输入:
*n = a + 3*
并且n
现在将等于8
。注意,n
的类型是numpy.int64
,因为它使用 NumPy 类型,而不是 Python 类型。
矩阵
我们使用 NumPy 的array
函数创建矩阵,就像我们创建向量一样。然而,我们不是仅仅传入一个列表,而是提供一个列表的列表,其中每个列表代表一行。因此,要创建一个包含数字 1 到 9 的 3x3 矩阵,我们可以这样做:
*m = np.array([[1,2,3], [4,5,6], [7,8,9]])*
检查它的shape
属性将返回元组(3, 3)
以表明它有两个维度,每个长度为 3。
我们可以像访问向量一样访问矩阵的元素,但是使用额外的索引值。所以为了在上面的矩阵中找到数字6
,我们将访问m[1][2]
。
Numpy 中的元素式操作
*values = [1,2,3,4,5]
values = np.array(values) + 5It now holds [6,7,8,9,10]*
因此,如果您已经有了一个名为values
的ndarray
,您可以只做:
*values += 5*
NumPy 实际上有加法、乘法等功能。但是它也支持使用标准的数学运算符。所以下面两行是等价的:
*x = np.multiply(array, 5)
x = array * 5*
需要注意的是*
是元素式乘法,不是矩阵乘法。相反,我们使用dot
函数来计算向量的内积,将向量乘以矩阵,以及将矩阵相乘。dot
既可以作为 numpy 模块中的函数,也可以作为数组对象的实例方法:
*x = np.array([[1,2],[3,4]])
y = np.array([[5,6],[7,8]])v = np.array([9,10])
w = np.array([11, 12])*# Inner product of vectors; both produce 219*
print(v.dot(w))
print(np.dot(v, w))*# Matrix / vector product; both produce the rank 1 array [29 67]*
print(x.dot(v))
print(np.dot(x, v))*# Matrix / matrix product; both produce the rank 2 array*
*# [[19 22]*
*# [43 50]]*
print(x.dot(y))
print(np.dot(x, y))*
Numpy 为在数组上执行计算提供了许多有用的函数;其中最有用的是sum
:
*import numpy as npx = np.array([[1,2],[3,4]])print(np.sum(x)) *# Compute sum of all elements; prints "10"*
print(np.sum(x, axis=0)) *# Compute sum of each column; prints "[4 6]"*
print(np.sum(x, axis=1)) *# Compute sum of each row; prints "[3 7]"**
除了使用数组计算数学函数之外,我们经常需要对数组中的数据进行整形或操作。这种操作最简单的例子是转置一个矩阵;要转置一个矩阵,只需使用数组对象的T
属性:
*import numpy as npx = np.array([[1,2], [3,4]])
print(x) *# Prints "[[1 2]*
*# [3 4]]"*
print(x.T) *# Prints "[[1 3]*
*# [2 4]]"**# Note that taking the transpose of a rank 1 array does nothing:*
v = np.array([1,2,3])
print(v) *# Prints "[1 2 3]"*
print(v.T) *# Prints "[1 2 3]"**
矩阵积
要找到矩阵乘积,可以使用 NumPy 的matmul
函数。
*a = np.array([[1,2,3,4],[5,6,7,8]])
a
# displays the following result:
# array([[1, 2, 3, 4],
# [5, 6, 7, 8]])
a.shape
# displays the following result:
# (2, 4)b = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
b
# displays the following result:
# array([[ 1, 2, 3],
# [ 4, 5, 6],
# [ 7, 8, 9],
# [10, 11, 12]])
b.shape
# displays the following result:
# (4, 3)c = np.matmul(a, b)
c
# displays the following result:
# array([[ 70, 80, 90],
# [158, 184, 210]])
c.shape
# displays the following result:
# (2, 3)*
如果您的矩阵有不兼容的形状,您会得到一个错误,如下所示:
*np.matmul(b, a)
# displays the following error:
# ValueError: shapes (4,3) and (2,4) not aligned: 3 (dim 1) != 2 (dim 0)*
尽管理解 Numpy 的工作原理非常重要,但我想让这篇文章保持真正的介绍性,所以很明显 Numpy 中有很多操作没有在这里介绍。因此我在下面链接了一些很棒的资源。
最后让我们看看权重(你在第二部分 中学到的)如何将与 Numpy 矩阵和转置函数联系起来:
假设你有下面两个矩阵,叫做“输入”和“权重”。“输入”是传递给你的神经网络的数据,“权重”是你的神经网络学习修改以找到最佳可能的解决方案。
*inputs = np.array([[-0.27, 0.45, 0.64, 0.31]])
inputs
# displays the following result:
# array([[-0.27, 0.45, 0.64, 0.31]])inputs.shape
# displays the following result:
# (1, 4)weights = np.array([[0.02, 0.001, -0.03, 0.036], \
[0.04, -0.003, 0.025, 0.009], [0.012, -0.045, 0.28, -0.067]])weights
# displays the following result:
# array([[ 0.02 , 0.001, -0.03 , 0.036],
# [ 0.04 , -0.003, 0.025, 0.009],
# [ 0.012, -0.045, 0.28 , -0.067]])weights.shape
# displays the following result:
# (3, 4)*
让我们试着把它们相乘,因为这就是我们如何得到神经网络的输出。
*np.matmul(inputs, weights)
# displays the following error:
# ValueError: shapes (1,4) and (3,4) not aligned: 4 (dim 1) != 3 (dim 0)*
如果你像他们现在这样尝试,你会得到一个错误。由于左矩阵中的列数4
与右矩阵中的行数3
不相等,导致形状不兼容,从而出现错误。
但是如果我们对“权重”矩阵进行转置,结果是:
*np.matmul(inputs, weights.T)
# displays the following result:
# array([[-0.01299, 0.00664, 0.13494]])*
如果你转置“输入”并交换它们的顺序,它也是有效的。
*np.matmul(weights, inputs.T)
# displays the following result:
# array([[-0.01299],#
# [ 0.00664],
# [ 0.13494]])*
两个答案是彼此的转置,所以我们使用的乘法的顺序实际上取决于我们想要的形状。
在下一篇文章中,我们将把这些概念应用到一个神经网络问题中。
Numpy 链接:
这本 笔记本详细解释了许多操作。还要查看 Numpy 库文档:https://docs.scipy.org/doc/numpy-dev/user/quickstart.html。另一个很好的资源是关于这个主题的科学讲座系列。Numpy 章出自本本本本。
DeepClassifyML 第 2 周第 2 部分
这篇文章是“Hasura 实习”系列的一部分,涵盖了git是什么。此外,我们开始实现和设计一个神经网络,根据学生的成绩来查找哪些学生被大学录取。还查看了我以前的帖子: 第一部分 , 第二部分 , 第三部分 对于 app 的想法以及一些计算机视觉和神经网络的基础知识。
Git 是一个版本控制系统,用于管理一个项目或一组文件,并随着时间的推移跟踪它们的变化。Git 是由 Linus Torvalds 开发的。它允许您将文件恢复到以前的状态,将整个项目恢复到以前的状态,比较一段时间内的更改,查看谁最后修改了可能导致问题的内容,谁在何时引入了问题,等等。使用 Git 或任何其他版本控制系统也意味着,如果你搞砸了事情或丢失了文件,你可以很容易地恢复它们。它有 3 种主要状态:提交、修改和暂存。提交是指所有数据都安全地存储在本地数据库中的状态。修改是指用户已经更新或更改了文件中的某些内容,但尚未提交到数据库。暂存意味着用户已在其当前版本中标记了一个修改过的文件,以便进入您的下一个提交快照。
Git 将这些信息存储在一个称为存储库的数据结构中。git 存储库包含以下内容:
- 一组提交对象。
- 提交对象的一组引用,称为 heads。
存储库存储在与项目本身相同的目录中,在一个名为. git 的子目录中。
- 只有一个。git 目录,在项目的根目录下。
- 存储库存储在项目旁边的文件中。没有中央服务器存储库。
提交对象包含三样东西:
- 定义项目在给定时间点的状态的一组文件。
- 对父提交对象的引用。
- SHA1 名称,一个 40 个字符的字符串,唯一标识提交对象。
基本的 Git 工作流程可以用 3 个步骤来描述:
- 修改/更新/更改项目存储库中的文件。
- 暂存已修改的文件。
- 提交暂存文件,并将它们永久存储到 Git 目录中。
修改文件但不添加它会导致 g it 将以前的版本(修改前)包括到提交中。因此,修改后的文件将保留在原处。
这篇 优秀的博文详细涵盖了你需要了解的关于 Git 的一切。 Hubspot 在 Git 和 Github 上的帖子 也是入门初学者的绝佳入门。
现在转到机器学习。上次我们用 Numpy 从基础数学开始。让我们看看如何应用它来解决问题。
神经网络从数据中学习的方式可以分为 3 个步骤:
- 我们为神经网络提供训练示例,它由输入单元的活动模式和输出单元的期望活动模式组成。
- 我们确定网络的实际输出与期望输出的匹配程度。
- 我们改变每个连接的权重,以便网络产生更好的期望输出的近似值。
A 3 layer Neural Network
这是一个 3 层神经网络。每一层都有独立的块,我们称之为神经元,这些是神经网络的基本单位。每个人都查看输入数据并决定如何对这些数据进行分类*。*箭头代表连接,可以想象为“重量”。
假设我们想知道一个学生是否能以他/她的 4 门学科的成绩进入大学。我们有他们的分数,也知道他们是否被录取。我们现在想知道当一个新学生进来时会发生什么。分数是我们网络的输入,被称为“特征”。输出层中的输出节点会给我们‘接受’或‘拒绝’,称为目标变量。总而言之,输入数据(分数)被输入到一个由相互连接的节点组成的网络中。现在,在上面的例子中,如果输入组合在一起并且超过了某个阈值,我们输出一个“是”,这个学生被大学录取了。网络决定一个学生的分数是否足够高,可以被大学录取。
现在你可能想知道它是如何知道哪些主题在做出接受决定时更重要的。一开始,当我们初始化神经网络时,我们不知道哪一个对决策最重要。这是使用“权重”完成的。网络的每个输入都有代表其重要性的相关权重,这些权重是在神经网络的学习过程中确定的。我们将权重表示为’ W’ ,将输入(科目等级)表示为’ S '。 网络使用这些权重对输入进行求和,这一过程称为 **线性组合。**方程式如下:
x = w1 ⋅s1+w2⋅s2+w3 . S3+w4 . S4
但是对于这个例子,我们只有 4 个输入。一般来说,我们可能有许多科目,或者其他功能,如课外活动等。假设我们有 m 个不同的输入(特征),我们将它们标记为 s(1),s( 2),。….s( m )。我们也假设 s 1 对应的权重是 w 1 等等。在这种情况下,我们可以将线性组合简洁地表示为:
x =w1 ⋅s1+w2⋅s2+w3 . S3+w4 . S4+………+ w m⋅sm
或者
x =(1∑m)[(wt39】I⋅sI)】
适马(≘)用来表示**求和。**这个表示我们多次向右计算等式,然后将结果相加。这里 1 ∑ m 表示迭代所有的 i 值,从 1 到 m 。
上面的等式意味着:
- 我们从 1 号主题开始,即 i =1
- 评估⋅s 1 号并记住结果
- 移动到 i =2
- 评估 w 2 ⋅s2 并将这些结果添加到 w 1 ⋅ xs 1
- 重复上述过程,直到 i = m 为止,其中 m 为输入(对象)数。
(注意:你可以把从 1 到 m 的总和∑写成 ∑w i ⋅s i。)
还记得第二部分中的激活功能吗?给定神经元的输入,它们是决定神经元输出的函数。它决定实际输出,因此其输出通常被称为“激活”。
现在,通过将线性组合输入到激活函数,使用该激活函数将来自输入层的输出(求和)转换成最终输出,即“接受或“拒绝”。一些激活功能是, tanh , softmax 和 relu 功能。我们将使用 sigmoid 给出的激活函数:
Sigmoid Function
sigmoid 函数介于 0 和 1 之间,输出以成功概率的形式给出。(1 代表“接受”,0 代表“拒绝”)。
我们还在线性组合中加入一个术语叫做’ 偏差 '。偏置,在等式中表示为 b ,允许我们将激活功能向左或向右移动,这对于成功的学习可能是至关重要的。像“权重”一样,神经网络不会预先知道为偏差选择什么值。因此,偏差也可以在训练期间更新和改变。因此输入层输出变成
x = ∑w i ⋅s i +b
让我们使用 Numpy 来计算我们的神经网络的输出,该神经网络具有 4 个输入(受试者)节点和一个带有 sigmoid 激活函数的输出节点(“接受”或“拒绝”)。我们将此任务分为 3 个步骤:
- 计算线性组合(输入层的输出)。
- 应用激活功能。
- 计算产量
为了计算线性组合或权重的和,我们将使用 Numpy 的点积函数,它允许我们执行元素级乘法和求和。
*import numpy as npdef sigmoid(x):
return 1/(1 + np.exp(-x)) ##numpy's exponential function inputs = np.array([50, 60, 10, 45])
weights = np.array([0.1, 0.8, 0.02, -0.7])
bias = -0.1output = sigmoid(np.dot(weights, inputs) + bias)print('Output: {}'.format(output)) ##Output: 0.999999999584*
神经网络的这种操作称为“前向传播”,让我们观察我们的模型执行得如何。下一步是实际更新权重并进行预测。这被称为“反向传播”,是实际学习发生的阶段。我们从数据中学习权重,然后使用它们进行预测。
在下一篇文章中,我们将讨论’反向传播和’梯度下降’,这是机器学习的主干和最重要的概念之一。
编辑:你可以在 akshaybhatia10@gmail.com 找到我
DeepClassifyML 第 2 周第 3 部分
这篇文章是“Hasura 实习”系列文章的一部分,讲述了如何为当地发展建立 Hausra。除此之外,我们终于看到‘神经网络如何学习’。还查看了我以前的帖子: 第一部分第二部分第三部分第四部分 对于 app 的想法以及一些计算机视觉和神经网络的基础知识。**
为本地开发设置 Hasura 非常简单,因为 Hasura 在这个 自述文件 中提供的指令都有很好的文档记录,并且非常容易实现。以下是我在我的系统上是如何做到的:
第一步: 安装virtualbox。 VirtualBox 是一款免费、开源、跨平台的应用,用于创建和运行虚拟机(VM)——其硬件组件由运行程序的主机计算机模拟的计算机。它允许在其上安装额外的操作系统,作为来宾操作系统,并在虚拟环境中运行。需要注意的是,主机应该至少有 4GB 的内存(因为虚拟机可能会占用高达 2GB 的内存)。还要确保你应该有一个 64 位操作系统。
**第二步: 安装 hasuractl。在我的系统(mac)上安装它的命令是:
**curl -Lo hasuractl https://storage.googleapis.com/hasuractl/v0.1.2/darwin-amd64/hasuractl && chmod +x hasuractl && sudo mv hasuractl /usr/local/bin/**
第三步: 安装 kubectl 。
要在 Hasura 上开始一个项目,在 beta.hasura.io 上创建一个帐户,然后运行以下命令:
**hasuractl login**
Hausra Login
登录后,运行以下命令(注意:如果您是第一次运行下一个命令,它将大致下载大约 1–1.5 GB 的 docker 映像。):
**hasuractl local start**
Starting a project
停止和删除 Hasura 项目的附加命令:
****hasuractl local stop** ## To stop the running hasura platform.***hasuractl local clean *** ## To clean up the incomplete setup.**hasuractl local delete** ## To delete the underlying VM**
**让我们快速进入’'反向传播’和’‘梯度下降’。在 第四部分 中,我们看到了神经网络如何在我们称之为“正向传播的过程中做出预测。我们根据一个学生以前的分数来预测他是否能进入大学。现在我们有了一个预测,我们如何知道它是否正确,以及我们离正确答案有多近。这是在“训练”或更新这些权重以进行预测的过程中发生的。
**我们想要的是一种算法,它能让我们找到这些权重和偏差,从而使网络的输出接近正确答案。(请记住,在培训期间,我们有他们的分数,也知道他们是否被录取,也就是说,我们事先知道正确答案。我们想知道当一个新学生进来时会发生什么。为了衡量这一点,我们需要一个指标来衡量预测的不正确程度。让我们称之为’误差’(你会注意到它也被称为’成本函数’或’损失函数’)。该误差可以用下式表示:
Sum of Squared Errors (SSE)
我在这里使用的误差指标被称为误差平方和(SSE)。我决定选择这个(也有其他损失函数),因为平方确保误差总是正的,较大的误差比较小的误差受到更多的惩罚。此外,它使数学看起来很好,不那么吓人。这里 f(x) 是预测值,y 是真实值,然后我们对所有数据点 i 求和。这也是有意义的,因为最终我们想从正确的答案中发现我们的预测有多差。这意味着如果我们的神经网络做得不好,这个’误差’将会很大——这将意味着对于大量数据点来说 f(x) 并不接近输出 y 。此外,如果成本(误差)变小,即 SSE(f) ≈0,精确地说,当 y 近似等于预测值 f(x) 时,对于所有训练输入 I,我们可以得出结论,NN 已经做得很好。因此,我们的训练算法的目标将是最小化作为权重和偏差的函数的这个’误差’。换句话说,我们希望找到一组权重和偏差,使这个“误差”尽可能小。我们将使用一种叫做梯度下降的算法来实现。
梯度下降是执行优化的最流行算法之一,也是迄今为止优化神经网络的最常见方法。它要求我们计算损失函数(误差)相对于网络中所有权重的梯度,以执行权重更新,从而最小化损失函数。反向传播以系统的方式计算这些梯度。反向传播和梯度下降可以说是训练深度神经网络的最重要的算法,可以说是最近出现的深度学习的驱动力。
让我们通过一个经典的例子来理解这一点。假设你在一座山的山顶,想到达山脚(也就是山的最低点)。那么,你会怎么做呢?最好的方法是环顾四周所有可能的方向,检查你附近的地面,观察哪个方向最容易下降。这会给你一个方向的想法,你应该采取你的第一步。然后我们一遍又一遍地重复这个过程。如果我们继续沿着下降的路径,很可能你会到达底部。
Plot for the Loss Function
把大山想象成误差函数。该图表面上的随机位置是权重和偏差的当前值的成本。山的底部(也是图的底部)是最佳权重和偏差集的成本,加上最小误差。我们的目标是继续尝试这些权重和偏差的不同值,评估误差,并选择导致误差稍好(较低)的新系数。重复这个过程足够多次,就会到达山的底部。
我们朝着目标迈出了一小步。在这种情况下,我们希望逐步改变权重以减少误差。因为下山最快的路是在最陡的方向,所以应该在误差最小的方向走。我们可以通过计算平方误差的梯度找到这个方向。
**梯度(或导数)是变化率或斜率的另一个术语。导数是微积分中的一个概念,指的是函数在给定点的斜率。我们需要知道斜率,以便知道移动系数值的方向(符号),从而在下一次迭代中获得更低的成本。
让我们找到一个函数的导数 f ( x )。我们取一个简单的函数 f(x) = x 。导数将给我们另一个函数f’(x),该函数返回 f ( x )在点 x 的斜率。 x 的导数为f’(x)= 2x。所以,在 x =2 时,斜率为f’(2)= 4。画出这个,看起来像:
Graph of f(x) = x² and its derivative at x = 2
梯度只是一个推广到具有多个变量的函数的导数。我们可以使用微积分来找到误差函数中任意一点的梯度,这取决于输入权重。你将在下一页看到梯度下降步骤是如何推导出来的。
权重将更新为:
W += ndelta*
**其中 n 称为“学习率”,δ是误差( y-f(x) )和激活函数的导数(f’(x))的乘积。梯度告诉我们函数具有最大增长率的方向,但是它没有告诉我们应该沿着这个方向走多远。由一个常数:学习率(或步长)决定,它是训练神经网络中最重要的超参数设置之一。
为此,我们再次使用 numpy:
**import numpy as np*## Sigmoid (Activation) function* **def sigmoid(x):
return 1/(1+np.exp(-x))***## Derivative of the Sigmoid (Activation) function* **def sigmoid_derivative(x):
return sigmoid(x) * (1 - sigmoid(x))***## Grades for single student in 4 subjects i.e only 1 data point* **inputs = np.array([50, 22, 10, 45])***## Correct answer (1 : admitted, 0: not admitted)* **y = np.array([1])** *## Initialise the weights and bias randomly* **initial_weights = np.array([0.01, 0.8, 0.02, -0.7])
bias = -0.1**## Set a value for learning rate
**learning_rate = 0.001***## Our Prediction (f(x))* **output = sigmoid(np.dot(weights, inputs) + bias)***## Calculate the error i.e how incorrect are we* **error = y - output****delta = error * sigmoid_derivative(output)***# Gradient descent step***change_in_weights = learning_rate * delta * inputs***## Updating our weights* **new_weights = initial_weights + change_in_weights****print ('Initial Weights: {}'.format(initial_weights))
print('Our prediction: {}'.format(output))
print('Amount of Error: {}'.format(error))
print('Change in Weights: {}'.format(change_in_weights))
print('New weights: {}'.format(new_weights))****
输出:
**Initial Weights: [ 0.01 0.8 0.02 -0.7 ]
Our prediction: 1.6744904055114616e-06
Amount of Error: [ 0.99999833]
Change in Weights: [ 0.01249998 0.00549999 0.0025 0.01124998] New weights: [ 0.02249998 0.80549999 0.0225 -0.68875002]**
虽然理解反向传播背后的概念总是有帮助的,但是如果你发现数学很难理解,那也没关系。我们使用的机器学习和深度学习库(scikit-learn、Tensorflow 等。)有内置的工具为你计算一切。
(编辑:请在评论中报告任何错误或差异,或者您可以联系我:akshaybhatia10@gmail.com)
用 DeepGL 在 Neo4j 上提取图形数据的 ML 特征
图的深度特征学习
2013 年,托马斯·米科洛夫和他的谷歌同事发布了一篇描述 word2vec 的论文,并推广了生成嵌入来表示数据片段的想法。
什么是嵌入?
嵌入是一个数组或数字向量,用来表示某种东西,在 word2vec 中是一个单词。
Adrian Colyer 有一个很好的图表,展示了一个词汇的非常简单的 hot 编码表示(下面有一个元素“hot”):
One hot encoding of a vocabulary of words
在这个例子中,每个单词有一列,一个单词的向量在适当的列中为 1,在其他地方为 0。
使用这种简单的表示,我们实际上无法在我们的嵌入之间进行任何有意义的比较,但是 word2vec 比这更进一步,它为每个单词提供了一种表示,这是这些元素之间的权重分布。
例如,我们可能会以这样的单词表示法结束:
自从 word2vec 发布以来,其他人已经使用类似的方法为单词之外的东西提供了嵌入。Pinterest 创建了 pin2vec ,用于向用户推荐 pin,Airbnb 使用嵌入功能寻找相似的房产列表。
我们如何处理这些嵌入?
嵌入可以被认为是表征学习的一种实现,在这种情况下,我们会自动提出一些特征来输入我们的机器学习模型,而不是手动创建它们。
例如,这些算法的输出可以用作张量流模型的输入。
我们还可以使用图形算法库 3.4.7.0 版本中引入的余弦相似性过程从嵌入中构建一个 kNN 相似性图。
然后,可以使用相似性图作为 k-最近邻查询的一部分来进行推荐。
图形嵌入
有几种算法可以用来生成图嵌入。这些是我们知道的:
这个想法和单词嵌入算法是一样的。我们希望为图中的每个实体(通常是节点)确定一个向量表示,然后将这些表示输入到机器学习算法中。
列表中最近增加了一个由 Ryan A. Rossi、Rong Zhou 和 Nesreen K. Ahmed 创建的图形深度特征学习 (DeepGL)。
这个对我们来说非常有趣,因为它是专门设计来保持低内存使用率的,并且还返回它提出的功能的名称,这在机器学习模型可解释性和功能提取的这些天很有帮助。另一个很好的特性是,它允许你将数字节点属性传递给算法,而其他的只有依赖于图形结构。
DeepGL 是如何工作的?
提供初始功能
我们首先为每个节点构建一组基本特征:入度、出度和两个度。我们还可以选择包含节点的任何其他数字属性(或投影)。
这篇论文描述了更多可以通过本地 graphlet 分解生成的基本特性,但是我们还没有实现。
对于一个包含 4 个节点的非常简单的图形,这些基本特征可能如下所示:
Bin 值
然后我们对这些值按列应用对数宁滨。这导致以下嵌入:
对每个邻域的要素应用运算符
接下来,我们对每个节点的(入、出、整体)邻域的这组特征应用一组操作符。我们应用的运算符有
- 哈达玛,
- 意思是,
- 总和,
- 最大值,
- L1·诺姆,
- 和径向基函数。
这意味着,比方说,对于 sum 运算符,我们将为每个节点创建 9 个新列:
- 在邻域中我们的入度之和
- 我们的在邻域的外向度之和
- 我们的在邻域中的双度之和
- 我们的出邻域的入度之和
- 我们的 out 邻域的 out 度之和
- 我们外邻区的双度之和
- 我们整个邻居的入度之和
- 我们整个邻居的外向度之和
- 我们的整体邻域的双度之和
让我们看看,如果我们将和运算符应用于邻域中的**,会发生什么。我们以 3 个额外的列结束,汇总了每个节点的邻居对于入度、出度和双度的得分。**
我们得到的一组特征如下所示:
我们为我们的其他邻域以及所有其他操作符计算相同的值,这给我们一个 57 列 4 行的矩阵**。**
在邻居之间分配分数
然后我们应用一个扩散过程,其中每个节点将其分数分配给其邻居。我们实现这一点的方式是,每个节点为每个特征取其所有邻居的平均值。
扩散过程不包括基本特征,因此在我们的示例中,我们将只扩散输入求和、输出求和和输入和输出求和。
我们得到的一组特征如下所示:
在我们继续下一阶段之前,我们再次应用对数宁滨,从而产生这组特征:
修剪特征
在这一步之后,我们应用特征修剪,这包括在特征相似性图上运行弱连通分量算法,然后只保留每个连通分量的一列。
我们通过对每一列中的值进行逐项比较来计算特征的相似性。如果两列中的所有值都相同,则该列的相似性得分为 1。在我们的例子中,扩散入和和扩散入和以及其他几对列就是这种情况。
假设剪枝λ为 0.7 以移除较低等级的关系,这是我们最终得到的特征图:
Our feature graph
连通分量算法将给出 3 个分量:
- 累计金额
- 两者相加、入度、出度和
- 双学位、双学位、双学位、双学位、双学位
我们倾向于保留早期迭代中的特性,这意味着基础特性。我们为每个组件保留一个特性,这在本例中意味着我们保留:
- 求和输入
- 输入度数
- 双学位或双学位(我们在这两者之间随机选择)
反复重复该过程
我们根据需要重复这个过程。在第二次和随后的迭代中,我们将把操作符应用到上一次迭代留下的特征上,而不是再次应用到基础特征上。
结果
一旦算法完成,我们将为图中的每个节点获得等长的向量。
在我们的例子中,这些向量是:
因此,节点 0 的向量是*【0,0,0】,节点 1 的向量是【1,1,0】,节点 2 的向量是【2,0,2】,节点 3 的向量是【0,2,1】*
我如何使用 DeepGL?
在过去的几个月里 Pete Meltzer ,他作为研究助理在 BrainTree 工作,并且正在 UCL 攻读博士学位,我已经将这个算法作为 Neo4j 程序来实现,并且现在已经有了一个版本供您试用!
DeepGL 算法作为 ml-models 库的一部分可用。你会想抓住 1.0.2 版本。
一旦你安装了它(存储库中的指令),你会找到两个版本的算法供你使用:
- embedding.deepgl —这将嵌入作为每个节点上的属性进行存储
- embedding . deepgl . stream—返回节点的流,嵌入
当你在玩算法的时候,流式可能是有意义的,但是一旦你想在一个合适的数据集上使用它,我建议使用第一个版本。
这是算法的签名:
CALL embedding.deepgl("NodeLabel" ,"RelationshipType", {
nodeFeatures: [string]
pruningLambda: double,
diffusions: integer,
iterations: integer,
writeProperty: string
})
- nodeFeatures —包含一个附加属性名的数组,您希望将其用作算法基本功能的一部分(默认为[])
- pruningLambda —移除相似特征时算法应该有多严格。较低意味着积极修剪莫尔斯(默认为 0.7)
- 扩散 —算法应该执行多少次扩散(默认为 10 秒)
- 迭代 —算法应该运行多少次迭代(默认为 10 次)
- writeProperty —将存储嵌入数组的属性的名称(默认为“deepgl”)
您还可以通过传递 graph: “cypher” config 参数并提供对节点 id 和关系列表的 cypher 查询来利用图形投影(视图),而不是节点标签和关系类型。
我们设置的默认值似乎在我们测试的数据集上工作得很好,但它们与论文中建议的不同,所以 YMMV。
效果如何?
我们已经在论文中引用的 Enzyme 和 EU 网络数据集上测试了该算法,与使用其他算法创建的嵌入相比,它似乎工作得相当好。
F1 scores of graph embeddings on various datasets
后续步骤
我们希望这种算法是有用的,并且你能在你自己的数据集上使用它。
如果你对我们下一步应该做什么有任何问题或建议,请在评论中告诉我们,或者给我们发电子邮件到devrel@neo4j.com。您也可以在我们新的 Neo4j 社区论坛中提出任何问题。
如果你想和皮特·梅尔策联系,你可以在 p.meltzer@braintree.com 的找到他。