TowardsDataScience-博客中文翻译-2016-2018-九-

龙哥盟 / 2024-10-13 / 原文

TowardsDataScience 博客中文翻译 2016~2018(九)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

最大似然算法的常见应用

原文:https://towardsdatascience.com/common-applications-of-ml-algorithms-e16f21c85773?source=collection_archive---------14-----------------------

Photo by Andrew Umansky on Unsplash

放射科医生的机器学习介绍— 10 部分系列

第四部

作者: 达尼洛泰·瓦雄

编辑: 迈克尔博士

第一部分 | 第二部分 | 第三部分

机器学习的使用比你想象的更加普遍。从确定一个电话或电子邮件是否是垃圾邮件,到识别你朋友脸上的方框并实际确定他们是谁,或者有针对性的广告营销,公司很快利用他们的数据来增加收入,吸引和关注他们的网页。

这些时刻是一个更大的技术叙事的一部分,它正在迅速颠覆企业的经营方式。放射学也不例外。

本文将介绍机器学习的一些现实应用。但在此之前,我们先来看看下面这张来自 Invidia 的图片。

如你所见,深度学习是机器学习的子集,机器学习是人工智能的子集。子领域的这种自然发展可以被视为一个领域建立在另一个领域之上,围绕图像识别所做的一切都可以追溯到人工智能的早期。理解这种层次结构很重要,因为许多人(包括本系列中的我们)会交替使用这些词,而事实上,它们之间的关系更加微妙。

图像识别是人工智能算法的主要进展之一。脸书可以检测你的脸,你朋友的脸,甚至可能是你的狗,然后分配适当的姓名标签!

由于使用了称为卷积神经网络的学习算法子集,图像识别或分析图像中的对象已经快速发展。在本文中,我们不会深入探讨这意味着什么,但请记住,这是一种找到组成对象的细节的方法。

例如,如果你想检测一张脸,算法首先需要学习如何检测边缘。一旦边缘检测变得很好,你也许可以检测角度或圆弧。然后,您可以检测椭圆形或球形等形状。

如果你愿意,这种建立你的脸类型的学习是在这些卷积神经网络(也称为 CNN)的引擎盖下发生的。这些网络是深度学习算法的子集。因此,当您或您的组织开始将这些功能添加到工作流中时,开发人员很有可能将 CNN 用于成像用例!这些用例可以包括从人脸检测到对象检测甚至图像分割的任何内容。

语音识别是深度学习非常好玩有趣的应用。语音是与技术互动的自然步骤之一,因为我们说话的速度比打字的速度快,这是自然的交流方式。

随着 Siri、Alexa、Google Home 和其他产品的出现,世界各地的许多公司都在使用机器学习和深度学习来获取大量的语音数据,以梳理声音的细微差别。

请记住,问题总是需要一个基本的事实。在这一点上,语音是一个非常容易的问题,因为人们可以被雇佣来听几个小时的声音片段,转录它们,并将这些知识输入到算法中。从那里,算法将学习声音的细微差别,声音的变化,并将这些不同的特征联系起来,从概率上确定哪个单词最有可能。

推荐系统是机器学习的第三组用例。这些应用程序已经成为许多公司的面包和黄油。当我们谈论推荐系统时,我们指的是你的脸书页面上的定向广告,亚马逊上的推荐购买产品,甚至是网飞上的推荐电影或节目。

这些系统由机器学习算法提供支持,这些算法可以检测人类行为的细微差别——无论是购买食物还是观看《办公室》的最后一季。此外,这些系统不仅被证明是有效的,而且现在是花费在广告和营销上的大量资金的支柱。无论你点击什么广告,或者你看一个节目花了多长时间,都只是运行这些公司推荐系统的大算法的一小部分。

在这篇文章中,我们只触及了机器学习和人工智能众多应用的表面。人工交易实现自动化只是时间问题。然而,在我们概述的所有精彩的优点之外,在取得更多进展之前,还需要找出一些障碍。这些特定于放射学的障碍将在后面的章节中讨论。

现在,让我们深入一些常见的机器学习主题,让您快速了解。这包括特征提取、标记数据和验证集!第 5 条对此进行了解释。

常见的数据科学陷阱以及如何避免它们

原文:https://towardsdatascience.com/common-data-science-pitfalls-and-how-to-avoid-them-7d509b1d3916?source=collection_archive---------10-----------------------

在学习期间,我有机会参与了几个机器学习研究项目。这些项目从研究概率模型到自然语言处理中更实际的场景。我所做的工作中的一个常见元素是定义明确的问题的可用性和大量干净的数据集。

过去一年,我一直在微软担任数据科学家,解决企业客户的问题。我从跨行业工作中获得的经验以多种方式测试了我的技能,无论是从数据科学的角度还是从解决问题的角度。很大一部分时间通常花在定义问题、创建数据集和清理数据上,而大型数据集几乎总是难以获得。

Examples of dataset sizes used in different research domains. (source)

在这篇博文中,我分享了一些经验教训和你在“野外”从事数据科学项目时可能遇到的陷阱。许多这样的陷阱几乎总是存在,为了解决它们,经常需要“创造性”。

陷阱

描述性、预测性和规范性分析

有几种不同的方法可以利用数据来改进业务流程。你通常从客户那里得到的任务是“解决业务问题 X”。一个具体的例子是“提高营销活动的盈利能力”。

一个常见的陷阱是没有以“正确”的顺序处理问题。如果你想改进一个过程,你需要正确地理解它,甚至在考虑它的自动化部分之前。客户通常对自己的数据了解有限,并且没有数据科学方面的经验。因此,从数据科学的角度向他们清楚地解释不同的选项非常重要。

The value of data analytics : from descriptive to prescriptive analytics (source)

通常,您通过清理和分析数据来开始项目。术语“描述性和诊断性”分析用于当你的目标是建立数据的摘要,并试图理解数据中的不同变量之间的关系。您构建可视化并使用(无监督的)机器学习技术来聚类数据点或查找相关变量。基于流程的结果,您可以构建一个报告或仪表板,列出并调查一组对业务问题有用的 KPI。通过营销活动,您可以将销售和营销数据关联起来,以便了解营销活动成功的原因。结论(例如,更低的价格导致更多的销售)可能不总是“火箭科学”,但它们允许客户使用他们的数据来验证他们的假设。此外,它还允许他们发现异常行为以及他们是否拥有正确的数据类型。

预测性和规范性分析是关于预测未来,并根据这些预测采取行动。通过预测分析,您可以获得历史数据,并建立一个机器学习模型来预测未来的数据点(在一定的误差范围内)。在市场营销中,新颖的活动创意通常通过使用预测模型估计预期销售额来验证。通过完成前面的描述和诊断阶段,您应该对性能有一个清晰的概念。如果数据质量不够好,不足以建立一个稳健的模型,这可以标记给客户,您可以使用他们提供的数据来支持您的索赔。

使用说明性分析,目标是基于预测采取行动,并优化流程的某些方面。例如,您可以构建一个优化引擎,生成“最佳未来活动”列表,而不是验证活动创意。建立这个系统需要大量高质量的数据。请注意,数据科学就是少做多做,所以从小处着手,大处着手!

概念验证与试运行

客户通常喜欢使用概念证明来研究“数据科学”的可能性。通常,这意味着他们提供数据的子集,并且他们想要确定一些初步的结果。尽管客户通常理解 PoC 不会带来良好的性能,但这仍然是一个常见的要求。然而,PoC 的缺点是数据的子集通常不能代表完整的数据集和问题。您可能会从 PoC 中获得非常好的性能,但是您可能无法在整个数据集上复制这种性能。客户通过例如仅查看一小段时间来选择数据集,这可能会严重偏离 PoC 模型。

(source)

如果客户想要试验,更好的选择是与试点合作。在试点项目中,您使用完整的数据集,并运行数据科学管道的初始迭代(数据清理、建模等)。使用完整的数据集可以降低项目中的许多风险。模型性能仍然不是最佳的,因为你只有有限的时间,但至少你应该能够得到一个有代表性的图片。

代表性数据集

虽然这个陷阱与上一个相似,但并不相同。尤其是当您处理非结构化数据(例如图像、文本数据等)时,客户可能已经在您开始项目之前收集了数据。数据集的一个关键但经常被忽视的元素是它是否代表了用例。如果需要收集数据集,应该以最终使用模型的相同方式进行。

例如,如果您想使用计算机视觉进行库存管理,那么拥有商店或冰箱中物体的图像是很重要的,因为模型将在这些地方使用。目录类型的图像不会产生健壮的模型,因为它们不代表用例。该模型必须学习鲁棒的特征,以便检测环境中的对象。

It is hard to imagine the fridge of the end-users will be this clean!

The image on the left is not very representative of the use case on the right!

高性能和过度配合

数据科学的黄金法则是,当你在问题开始时获得了极好的性能(> 90%)时,要保持怀疑。项目开始时的良好表现可能是“漏”变量或“漏”数据集的指标。漏变量是与目标变量高度相关的变量,在推理时不太可能得到。如果目标是预测销售额,您就不能使用其他产品的销售额作为特征,因为这些数据是不可用的。
当数据集仅反映某个(时间)时间段时,就会出现“泄漏”数据集。需要解决的问题通常要简单得多,而且模型很可能适合数据中的噪声,而不是实际信号。

The easiest thing to do in machine learning is overfitting! (source)

一个高性能的模型是伟大的,但前提是它在一个全新的随机数据集上表现同样好!

将相关性解释为因果关系

我非常喜欢这篇关于统计谬误的博文。将相关性解释为因果关系只是经常犯的错误之一,深入理解它们背后的统计数据是关键。

模型和数据的可解释性

如果客户使用模型作为行动的基础,他们将总是喜欢可解释和可理解的解决方案。在[这篇博文](http://Regardless of the end goal of your data science solutions, an end-user will always prefer solutions that are interpretable and understandable. Moreover, as a data scientist you will always benefit from the interpretability of your model to validate and improve your work. In this blog post I attempt to explain the importance of interpretability in machine learning and discuss some simple actions and frameworks that you can experiment with yourself.)中,我深入探讨了为什么模型可解释性是关键的原因。我还更深入地介绍了您可以使用的不同技术。

结论

我只提到了 6 个潜在的数据科学陷阱,但我确信,在过去的一年里,我可能犯了更多的错误。如果你有任何数据科学项目的经验,并想分享你经常遇到的问题,我很乐意在评论中阅读它们。如果你想收到我博客上的更新,请在 Medium 或 Twitter 上关注我!

机器学习中常见的损失函数

原文:https://towardsdatascience.com/common-loss-functions-in-machine-learning-46af0ffc4d23?source=collection_archive---------0-----------------------

Loss functions and optimizations

机器通过损失函数来学习。这是一种评估特定算法对给定数据建模程度的方法。如果预测与实际结果相差太多,损失函数就会产生一个非常大的数字。逐渐地,在一些优化函数的帮助下,损失函数学习减少预测中的误差。在本文中,我们将介绍几种损失函数及其在机器/深度学习领域的应用。

机器学习中的算法没有放之四海而皆准的损失函数。在为特定问题选择损失函数时涉及各种因素,例如所选择的机器学习算法的类型、计算导数的容易程度以及某种程度上数据集中离群值的百分比。

大体上,损失函数可以根据我们正在处理的学习任务的类型分为两大类——回归损失和 T2 分类损失。在分类中,我们试图预测有限分类值集的输出,即给定手写数字图像的大数据集,将它们分类为 0-9 个数字中的一个。另一方面,回归处理预测连续值,例如给定的楼层面积、房间数量、房间大小,预测房间的价格。

**NOTE** 
        n        - Number of training examples.
        i        - ith training example in a data set.
        y(i)     - Ground truth label for ith training example.
        y_hat(i) - Prediction for ith training example.

回归损失

均方误差/二次损失/L2 损失

数学公式 :-

Mean Squared Error

顾名思义,均方误差是以预测值和实际观测值的平方差的平均值来衡量的。它只关心误差的平均大小,而不考虑它们的方向。然而,由于平方,与偏差较小的预测相比,远离实际值的预测会受到严重的惩罚。加上 MSE 有很好的数学属性,这使得计算梯度更容易。

import numpy as npy_hat = np.array([0.000, 0.166, 0.333])
y_true = np.array([0.000, 0.254, 0.998])def rmse(predictions, targets):
    differences = predictions - targets
    differences_squared = differences ** 2
    mean_of_differences_squared = differences_squared.mean()
    rmse_val = np.sqrt(mean_of_differences_squared)
    return rmse_valprint("d is: " + str(["%.8f" % elem for elem in y_hat]))
print("p is: " + str(["%.8f" % elem for elem in y_true]))rmse_val = rmse(y_hat, y_true)
print("rms error is: " + str(rmse_val))

平均绝对误差/L1 损失

数学公式 :-

Mean absolute error

另一方面,平均绝对误差是预测值和实际观测值之间的绝对差值的平均值。像 MSE 一样,这也是在不考虑误差方向的情况下测量误差的大小。与 MSE 不同,MAE 需要更复杂的工具,如线性编程来计算梯度。此外,MAE 对异常值更健壮,因为它不使用 square。

import numpy as npy_hat = np.array([0.000, 0.166, 0.333])
y_true = np.array([0.000, 0.254, 0.998])

print("d is: " + str(["%.8f" % elem for elem in y_hat]))
print("p is: " + str(["%.8f" % elem for elem in y_true]))

def mae(predictions, targets):
    differences = predictions - targets
    absolute_differences = np.absolute(differences)
    mean_absolute_differences = absolute_differences.mean()
    return mean_absolute_differencesmae_val = mae(y_hat, y_true)
print ("mae error is: " + str(mae_val))

平均偏差误差

与机器学习领域相比,这在机器学习领域更不常见。这和 MSE 一样,唯一的区别是我们不取绝对值。显然需要谨慎,因为正负误差会相互抵消。虽然在实践中不太准确,但它可以确定模型是有正偏差还是负偏差。

数学公式 :-

Mean bias error

分类损失

铰链损耗/多级 SVM 损耗

简单地说,正确类别的得分应该比所有错误类别的得分之和大一些安全余量(通常是 1)。因此,铰链损失用于最大间隔分类,最显著的是用于支持向量机。虽然不是可微的,但它是一个凸函数,这使得它很容易与机器学习领域中常用的凸优化器一起工作。

数学公式 :-

SVM Loss or Hinge Loss

考虑一个例子,其中我们有三个训练样本和三个要预测的类别——狗、猫和马。低于我们的算法对每一类的预测值:-

Hinge loss/ Multi class SVM loss

计算所有 3 个训练示例的铰链损耗:-

**## 1st training example**
max(0, (1.49) - (-0.39) + 1) + max(0, (4.21) - (-0.39) + 1)
max(0, 2.88) + max(0, 5.6)
2.88 + 5.6
*8.48 (High loss as very wrong prediction)***## 2nd training example** max(0, (-4.61) - (3.28)+ 1) + max(0, (1.46) - (3.28)+ 1)
max(0, -6.89) + max(0, -0.82)
0 + 0
*0 (Zero loss as correct prediction)***## 3rd training example** max(0, (1.03) - (-2.27)+ 1) + max(0, (-2.37) - (-2.27)+ 1)
max(0, 4.3) + max(0, 0.9)
4.3 + 0.9
*5.2 (High loss as very wrong prediction)*

交叉熵损失/负对数似然

这是分类问题最常见的设置。交叉熵损失随着预测概率偏离实际标签而增加。

数学公式 :-

Cross entropy loss

请注意,当实际标签为 1 (y(i) = 1)时,函数的后半部分消失,而当实际标签为 0 (y(i) = 0)时,函数的前半部分消失。简而言之,我们只是将实际预测概率的对数乘以地面真实类。一个重要的方面是交叉熵损失严重惩罚了那些有信心但错误的预测。

import numpy as nppredictions = np.array([[0.25,0.25,0.25,0.25],
                        [0.01,0.01,0.01,0.96]])
targets = np.array([[0,0,0,1],
                   [0,0,0,1]])def cross_entropy(predictions, targets, epsilon=1e-10):
    predictions = np.clip(predictions, epsilon, 1\. - epsilon)
    N = predictions.shape[0]
    ce_loss = -np.sum(np.sum(targets * np.log(predictions + 1e-5)))/N
    return ce_losscross_entropy_loss = cross_entropy(predictions, targets)
print ("Cross entropy loss is: " + str(cross_entropy_loss))

分析数据的常见模式

原文:https://towardsdatascience.com/common-patterns-for-analyzing-data-da1908640641?source=collection_archive---------7-----------------------

Photo by Samuel Zeller on Unsplash

数据通常是杂乱的,建立准确模型的关键步骤是彻底理解您正在处理的数据。

在我几个月前开始自学机器学习之前,我没有太多考虑如何理解数据。我假设数据是放在一个很好的有组织的包中,顶部有一个蝴蝶结,或者至少有一套清晰的步骤可以遵循。

通过查看其他人的代码,我对人们理解、可视化和分析相同数据集的方式的差异感到震惊。我决定通读几种不同的数据分析,试图找到相似之处和不同之处,看看我是否能提炼出一套理解数据集的最佳实践或策略,以便最好地利用它们进行分析

数据科学家将大部分时间花在数据准备上,而不是模型优化上。— 洛林克

在这篇文章中,我选择了一些在数据科学网站 Kaggle 上公开的 探索性数据分析(或 EDAs) 。这些分析将交互式代码片段与散文混合在一起,可以帮助提供数据的鸟瞰图或梳理出数据中的模式。

我同时研究了特征工程,这是一种获取现有数据并以赋予额外含义的方式对其进行转换的技术(例如,获取时间戳并提取一个DAY_OF_WEEK列,这可能会在预测商店销售额时派上用场)。

我想看各种不同种类的数据集,所以我选择了:

  • 结构数据
  • 自然语言
  • 图像

请随意跳到下面的结论,或者继续阅读深入研究数据集。

标准

对于每个类别,我选择了两个已经过了提交日期的比赛,并根据提交的团队数量进行了排序。

对于每一场比赛我都搜索了 EDA 标签,选择了三个评价很高或者评论很好的内核。最终分数没有考虑在内(一些 EDA 甚至没有提交分数)。

结构数据

结构化数据集的特征在于包含训练和测试数据的电子表格。电子表格可能包含分类变量(颜色,如greenredblue)、连续变量(年龄,如41567)和顺序变量(教育水平,如elementaryhigh schoolcollege)。

插补 —填充数据中缺失的值

宁滨 —将连续数据组合成桶,一种特征工程的形式

训练电子表格有一个您试图求解的目标列,它将在测试数据中丢失。我检查的大多数 EDA 都集中在梳理目标变量和其他列之间的潜在相关性。

因为你主要是在寻找不同变量之间的相关性,所以你可以分割数据的方法就只有这么多。对于可视化,有更多的选择,但即使如此,一些技术似乎比其他技术更适合手头的任务,导致了许多看起来相似的笔记本。

你可以真正发挥想象力的地方是特性工程。我看到的每个作者都有不同的特征工程方法,无论是选择如何绑定一个特征还是将分类特征组合成新的特征。

我们再来深入看两个比赛,泰坦尼克号比赛,其次是房价比赛。

泰坦尼克号

泰坦尼克号比赛是一项很受欢迎的初学者比赛,许多 Kaggle 上的人都参加了这项比赛。因此,EDA 往往写得很好,记录得很完整,是我见过的最清晰的。该数据集包括一个训练电子表格,其中有一列Survived指示乘客是否幸存,以及其他补充数据,如他们的年龄、性别、机票价格等。

我选择分析的 EDA 是 I 的EDA to predict Dietanic,Coder , Titanic Survival for 初学者 EDA to ML 的 deja vu 和In Depth visualization Simple MethodsJekaterina Kokatjuhha。

所有三个 EDA 都从原始指标开始,查看几个样本行并打印关于 CSV 文件的描述性信息,如列的类型、平均值和中值。

I, Coder describes the dataset

处理空值或缺失值是数据准备的关键步骤。一个 EDA 直接处理这个问题,而另外两个在特性工程阶段处理缺失值。

我,编码员反对分配一个随机数来填充缺失的年龄:

正如我们之前看到的,年龄特性有 177 个空值。为了替换这些 NaN 值,我们可以为它们分配数据集的平均年龄。但问题是,有很多不同年龄的人。我们不能给一个 4 岁的孩子分配 29 岁的平均年龄。有什么办法可以查出这位乘客属于哪个年龄段吗??答对了。!!!,我们可以检查名称功能。查看该特征,我们可以看到姓名带有称呼,如 Mr 或 Mrs。因此,我们可以将 Mr 和 Mrs 的平均值分配给相应的组。

I, Coder imputing ages

虽然 I,Coder 将特征工程作为纯数据分析的一部分,但其他两位作者认为这是一个独立的步骤。

所有三位内核作者都非常依赖图表和可视化来获得对数据的高层次理解,并找到潜在的相关性。使用的图表包括因子图、交叉表、条形图和饼图、小提琴图等。

deja vu plots survival by gender

你可能对泰坦尼克号灾难中的“妇女和儿童优先”这句话很熟悉,对于每个作者来说,年龄和性别在他们最初的数据分析中占了很大比重。收入背景(如机票价格所示)也需要进行一些详细的检查。

船上男人的数量比女人的数量多得多。然而,获救的女性人数几乎是男性人数的两倍。船上女性的存活率约为 75%,而男性约为 18-19%。—我,编码员

杰卡特琳娜本人、编码员都是通过对图表和数据的目测得出结论,其中杰卡特琳娜写道:

  • 性:女性生存几率更高。
  • 有一张头等舱机票对生存是有益的。
  • SibSp 和 Parch:中型家庭比独自旅行或大家庭的人有更高的存活率。原因可能是,只有人们愿意牺牲自己来帮助别人。关于大家庭,我会解释说,很难管理整个家庭,因此人们会在上船之前寻找家庭成员。
  • 登船 C 的存活率更高。例如,看看 Pclass 1 的大部分是否在 embarked C 中出现,这将是很有趣的。

Jekaterina builds a stacked chart illustrating Pclass and Embarked

似曾相识的 EDA 记录了他分析的每一步的准确度,提供了一个很好的反馈,说明每个特征对最终预测有多重要。

特征工程

Jekaterina pulls out cabin letter.

谈到特性工程,三个内核作者之间有更多的可变性。

每位作者为年龄和费用等连续变量选择不同数量的桶。与此同时,每个人都以不同的方式处理家庭关系,其中 I,编码者建立了一个SibSip——无论一个人是独自还是与家人(配偶或兄弟姐妹)——以及family_sizealone,而杰卡特琳娜拿出一个小屋,并为childadult建议一个功能。我,编码员在剔除不相关的列时特别积极:

Name →我们不需要 Name 特性,因为它不能被转换成任何分类值。

年龄→我们有年龄带功能,所以不需要这个。

票→是任意一个无法分类的随机字符串。

Fare →我们有 Fare_cat 特性,所以不需要

客舱→许多 NaN 值以及许多乘客有多个客舱。所以这是一个没用的功能。

Fare_Range →我们有 fare_cat 功能。

PassengerId →无法分类。

对于插补步骤, Jekaterina 写道:

  • 上船:用大船装满船
  • Pclass:因为 Fare 中只有一个缺失值,所以我们将用相应 Pclass 的中间值来填充它
  • 年龄:有几种输入技术,我们将使用范围均值+-标准差中的随机数

她通过确保新的估算数据不会扰乱均值来总结她的内核:

Jekaterina checking if the imputation disrupted the mean

外卖食品

所有三个内核作者都花时间预先检查数据并描述整体形状。

I,Coder 查看总的空值,而 Jekaterina 在接近结束时查看。

每个人都从查看幸存者的分类开始,然后是幸存者按性别的分类。交叉表、因子图和小提琴图都是流行的图表。Jekaterina 还绘制了一些非常有趣的图表。

当谈到特征工程时,作者们的分歧就更大了。作者们在何时设计新功能的问题上意见不一,一些人将其视为一个独立的步骤,而另一些人则在他们对数据的初步分析中处理它。宁滨各地的选择各不相同,年龄、头衔和费用都有不同数量的桶,只有杰卡特琳娜设计了一个离散的child / adult特征。

估算的方法也不同。 I,编码员建议查看现有数据来预测插补值,而 Jekaterina 确保她的插补数据不会影响平均值。

两位作者在思考和处理数据的方式上有一些明显的相似之处,主要分歧在于可视化和特征工程。

房价

by American Advisors Group

房价又是一场结构化数据竞赛。这一项比泰坦尼克号竞赛拥有更多的变量,包括分类、顺序和连续特征。

我选择分析的 EDA 分别是佩德罗·马塞利诺的用 Python 进行的全面数据探索、安吉拉的用 Python 进行的详细数据探索,以及尚贤公园的趣味 Python EDA 一步步。

虽然类似于泰坦尼克号,但它要复杂得多。

有 79 个解释变量描述了(几乎)爱荷华州埃姆斯住宅的每个方面,这个比赛挑战你预测每个家庭的最终价格。

Pedro plots the sale price

安吉拉和佩德罗花了一些时间预先调查初始数据,就像我们在《泰坦尼克号》中看到的那样。 Angela 在直方图中绘制销售价格并构建功能热图,而 Pedro 绘制销售价格并得出以下关于销售价格的结论:

*偏离正态分布。

*具有明显的正偏度。

*显示峰值。

佩德罗然后站在买家的角度,推测哪些功能对他很重要,检查他的选择和销售价格之间的相关性。后来,他构建了一个热图,在放大几个有希望的候选人之前,收集更客观的特征关系视图。

Plotting features against sale price

相比之下,安吉拉从更客观的方法开始,通过与SalePrice的相关性列出数字特征。她还根据销售价格绘制特征图,寻找数据中的模式。

Sang-eon 开始他的内核,积极剔除缺失值和离群值(除了LotFrontage他使用线性回归估算)。只有到那时,他才开始针对销售价格策划各种特征。

Pedro 一直等到寻找数据之间的相关性,以检查缺失数据的问题。他问道:

  • 缺失数据有多普遍?
  • 缺失数据是随机的还是有规律的?

由于实际原因,这些问题的答案很重要,因为缺失数据可能意味着样本量的减少。这可能会阻止我们继续进行分析。此外,从实质性的角度来看,我们需要确保缺失数据的处理过程不会有偏见,不会隐藏一个难以忽视的事实。

为了解决这些问题, Pedro 绘制了缺失单元格的总数和百分比,并选择删除 15%或更多单元格包含缺失数据的列。他再次依靠主观选择来确定要移除哪些特征:

…我们会错过这些数据吗?我不这么认为。这些变量似乎都不是很重要,因为大部分都不是我们买房时考虑的方面(也许这就是数据缺失的原因?).此外,仔细观察变量,我们可以说像' PoolQC ',' MiscFeature '和' FireplaceQu '这样的变量是离群值的强候选,所以我们很乐意删除它们。

Pedro 处理缺失数据的方法是,如果列(特征)具有大量缺失值,则完全删除它们,或者删除只有少量缺失值的行。他没有估算任何变量。他还建立了一种解决异常值的启发式方法:

这里主要关心的是建立一个阈值,将一个观察定义为异常值。为此,我们将对数据进行标准化。在这种情况下,数据标准化意味着将数据值转换为平均值为 0,标准差为 1。

他的结论是,从统计的角度来看没有什么可担心的,但是在返回到数据的视觉检查之后,删除了一些他认为有问题的单个数据点。

特征工程

Sang-eon 检查数据的偏斜度和峰度,并执行 Wilxoc-秩和检验。他以一个非常好看的情节结束了他的内核:

Sang-eon with a 3d plot of features

同时, Pedro 讨论了正态性、同方差性、线性和不存在相关误差;他将数据标准化,发现其他三个问题也解决了。成功!

外卖食品

三个内核作者都没有做太多的特性工程,可能是因为数据集中已经存在太多的特性。

有各种各样的策略来确定如何处理数据,一些作者采用主观策略,而另一些作者则直接采用更客观的测量方法。对于何时以及如何剔除缺失值或异常值,也没有明确的共识。

与《泰坦尼克号》比赛相比,这里更注重统计方法和整体的完整性,可能是因为有太多的功能需要处理;与之前的比赛相比,负面的统计效应可能会产生更大的整体影响。

自然语言

自然语言(NLP)数据集包含单词或句子。虽然核心数据类型与结构化数据竞赛中的相同,即文本,但可用于分析自然语言的工具是专门的,导致了不同的分析策略。

在原始形式下,语言不容易被机器学习模型破译。要将其转换成适合神经网络的格式,需要进行转换。一种流行的技术是单词包,通过这种技术,一个句子被有效地转换成 0 或 1 的集合,以指示某个特定单词是否存在。

由于需要转换数据,大多数笔记本的前几个步骤往往是将文本转换成机器可读的东西,这一步在笔记本之间往往是相似的。一旦完成了这些,编码人员在他们的方法上就有了很大的分歧,并为特性工程采用了各种不同的可视化和技术。

毒性评论分类

警告:这些评论中的一些可能会灼伤你的眼球。

by navaneethkn

我关注的第一个 NLP 竞赛是有毒评论分类竞赛,它包括一个数据集,其中包含大量来自维基百科 talk page edits 的评论,这些评论按照毒性等级进行评分,表明它们是否是侮辱、淫秽、有毒等等。挑战在于预测给定评论的毒性标签。

我选择分析的 EDA 是 Stop the S@#$ —有毒评论 EDA 作者贾根、分类多标签评论作者铑 Beng 和不要惹我母亲作者弗朗西斯科门德斯

三位作者都是从描述数据集开始,并随机抽取一些评论。虽然没有丢失值,但是注释中有很多干扰,但是不清楚这些干扰在最终的数据分析中是否有用。

Jagan plots the distribution of images per toxic category

毒性并没有在各个阶层中均匀分布。因此,我们可能会面临阶级失衡的问题

Francisco 立即扔掉“缺乏意义”的单词(例如,“and”或“the”)。利用双标图,他画出一个特定的单词最有可能属于哪一类。

从 biplot 来看,大多数单词都像预期的那样组织起来,除了一些例外,fat 与身份仇恨有关,这令人惊讶,因为它是图表底部唯一的非种族单词,在图表中间有一些通用的攻击性单词,这意味着它们可以用于任何可怕的目的, 其他像 die 这样的单词只与威胁相关联,这完全说得通。其他一些像$$(抱歉,我写它时感觉不舒服,因为它出现在数据上)与威胁相关联,在图表的左中部有一些无法识别的单词,这些单词使用代码显示——Francisco Mendez

Francisco 接着询问拼写错误和毒性之间是否存在关联。

显然是有的,令人惊讶的是,拼错的 mother 与仇恨或威胁无关,但当拼写正确时,会有一些仇恨和威胁评论中有 mother 这个词…这是因为人们在威胁某人或讨厌它时会写得更仔细吗?

随着 Francisco 的深入研究,他发现在许多情况下,有害的评论会一遍又一遍地包含复制粘贴的短语。在删除重复的单词后,他重新进行分析,发现了一组新的相关性。

这里有一些新词,可能是 gay,主要用于威胁性评论和仇恨。一些一般温和的词,如母亲,地狱,一块,愚蠢,白痴和关闭被用于任何有毒的一般目的,同时任何衍生的 f 字被用于有毒和淫秽的评论。此外,从双标可以认识到,毒性和侮辱是相似的,是最不具攻击性的,而仇恨和威胁是最严重的。

这三位作者都充分利用了数据的可视化效果。(鉴于主题我不会嵌入图片,但是你可以在每个作者的内核上找到它们。)

Rhodium 构建了一个字符长度直方图以及类别之间的热图,发现一些标签高度相关;例如,侮辱有 74%可能也是淫秽的。

Jagan 绘制了一些单词云、热图和交叉表,观察到:

一个严重的有毒评论总是有毒的
除了少数例外,其他类别似乎是有毒的子集

特征工程

小写文本,手动把缩写变成东西,手动清理标点符号。

Jagan 绘制各种特性与毒性的关系图,寻找相关性。发现之一是:垃圾邮件发送者往往更具毒性。

Jagan discussing feature engineering

对于单个单词和成对单词, JaganRhodium 都使用 TF-IDF 绘制顶部单词,描述如下:

TF 代表词频;本质上,一个单词在文本中出现的频率…你可以把它理解为相对文本频率与整体文档频率的归一化。这将导致特定作者特有的单词脱颖而出,这也是我们为了建立预测模型而想要实现的。— 正面还是反面

外卖食品

似乎所有作者都遵循一些最佳实践,包括小写文本、处理缩写和清理标点符号等都是作者关注的领域。然而,一些作者也认为这些可能是潜在的特征,而不仅仅是噪音(例如,Francesco 发现了打字错误和毒性之间的相关性)。

阴森森的作者识别

by Gael Varoquaux

幽灵作者识别提供了三位恐怖主题作者的文本片段——埃德加·爱伦·坡、惠普·洛夫克拉夫特或玛莉·渥斯顿克雷福特·雪莱——并要求参与者建立一个模型,能够预测哪位作家创作了特定的一段文本。

我选择进行分析的 EDA 是由提供的怪异 NLP 和主题建模教程各向异性,教程详细怪异趣味 EDA 和建模由 Bukun 提供,以及由头或尾提供的 Treemap House of Horror 怪异 EDA LDA Features 。

这个数据集的有趣之处在于它的简单性;除了作者之外,文本附带的非结构化数据非常少。因此,所有的数据库都只关注不同的语言解析和分析方法。

每个作者首先检查数据集,挑选几行,并绘制每个作者的故事数量。 Bukun 还会查看每个作者的字数,而各向异性会绘制一个总字数的条形图:

注意到这个词频图中出现的单词有什么奇怪的地方吗?这些话实际上告诉了我们玛丽·雪莱在她的故事中想要向读者描绘的主题和概念吗?这些词都是非常常见的词,你可以在任何其他地方找到。不仅在我们三位作者的恐怖故事和小说中,而且在报纸、儿童读物、宗教文本中——实际上几乎所有其他的英语文本中。因此,我们必须找到一些方法来预处理我们的数据集,首先去除所有这些经常出现的单词,这些单词不会给表格带来太多。—各向异性

每位作者都构建了词云,最大程度地显示了最常用的词:

Heads or Tails builds a word cloud of the 50 most common words

Heads or Tails 还绘制出每个作者的整体句子、句子和单词长度,并发现作者之间细微但可测量的差异。

各向异性卜坤讨论标记化,去除停用词:

这一阶段的工作试图将相似单词的许多不同变体缩减为单个术语(不同的分支都缩减为单个词干)。因此,如果我们有“运行”、“运行”和“运行”,你真的会希望这三个不同的单词合并成一个单词“运行”。(当然,你失去了过去、现在或将来时态的粒度)。—各向异性

在标记化、停用词移除和词条化之后,各向异性重建前 50 个词的图:

Bukun 将他最喜欢的 10 个词按照作者进行分类,找到了不同的组合:

Heads or Tails 也是这样做的,在标记化和词干化之后,另外查看作者的热门词。

BukunHeads or Tails 都使用 TF-IDF 来为特定作者找到最“重要”的词。

Heads or Tails plots the most significant words by author in a bit of a different chart

Bukun 查看顶级二元模型和三元模型(分别是两个和三个单词的集合)。

Heads or Tails plots the word relationships for bigrams

Bukun正面或反面都执行情感分析,并查看每个作者的总体负面评价。

Bukun 使用一种叫做“NRC 情绪词典”的东西来检查每个文本片段中“恐惧”、“惊喜”和“喜悦”的数量,并使用单词云、表格、条形图来可视化各个作者的情绪。

Bukun plots a word cloud for words matching Joy

特征工程

Bukun 建议添加一些可能的特性,包括逗号、分号、冒号、空格、带大写字母的单词或以大写字母开头的单词,以及每一个的图表。对于一些作者来说,这些特征之间确实存在某种关联。

正面或反面注意到:

我们已经注意到,我们的三个作者可以通过他们最突出的人物的名字来识别;玛丽·雪莱写的是“雷蒙德”,洛夫克拉夫特写的是“赫伯特·韦斯特”。但是一般的名字呢?是不是有些作者在某些情况下更倾向于用名字?在句子或字符长度之后,这是我们寻求知识的第一个特征工程概念

从这个角度来看,正面或反面依赖于babynames包,该包提供了给定年份最受欢迎名字的列表,为数据增加了一个额外的特性。

BukunHeads or Tails 都查看作者之间的性别代词细分,而 Heads or Tails 还查看句子主题、每个作者的起始单词和最后一个单词、独特单词的数量、每个句子中独特单词的比例、对话标记和头韵(这是一个很酷的想法!)

heads or tails plots various measurements of alliteration by author

正面还是反面以展示功能交互的冲积图结束他的内核:

Heads or Tails’ alluvial plot showcasing feature interaction

外卖食品

这是一个值得研究的有趣竞争,因为文本片段更长,并且没有结构化数据可以依赖。

内核倾向于利用 NLP 最佳实践,比如小写单词、词干和标记化。内核也倾向于使用比有毒内核更先进的技术,如情感分析和二元三元模型分析。

两次比赛,内核作者都用了 TF-IDF 。

对于功能工程,作者设计了各种新功能,包括每句话的平均字数、标点符号的选择以及单词是否重复。

形象

到目前为止,数据集都是纯粹基于文本的(语言、字符串或数字)。我选择查看的最后两个数据集是基于图像的。

我研究的两个竞赛(肺癌和树叶分类)都比我研究的其他竞赛更具领域特异性。因此,这些分析倾向于假设一个先进的观众,作者跳过了初步的分析,有利于探索不同的图像分析技术。

我在所使用的可视化技术方面看到了很大的多样性,以及工程化的特性。特别是,肺癌竞赛中的一些作者利用了现有的医学知识,以便设计出极具领域特异性的特征。我不能说这些功能有多有效,但我可以说它们产生的视觉效果令人惊叹。

树叶分类

树叶分类比赛包括 1,584 张树叶的蒙版图片,按物种组织。参与者被要求建立一个模型,这个模型能够将新的图片分类到其中一个类别。

我选择用于分析的 EDA 是由 lorinc 的图像特征提取,由 selfishgene 的用叶子数据集可视化 PCA,以及由 Jose Alberto 的快速图像探索。

一个好的第一步是看树叶的图像,这是两个 EDA 的开始。

selfishgene examines the leaf specimens

何塞绘制了各种各样的物种,并指出每个物种有 10 张图片。他还研究了一个类别内的树叶之间的相似性:

Jose compares leaves within a category

与此同时, lorinc 直接进入分析,定位每片叶子的中心并应用边缘检测。lorinc 还将叶子的轮廓转换成极坐标,以便更有效地测量叶子的中心:

稍后,当我们从形状生成时间序列时,我们可能希望切换到另一种中心性度量,基于该中心的效率,使用边缘和中心之间的距离。一种方法是测量中心和边缘之间的(欧几里德)距离……但是有一种更好的方法——我们将笛卡尔坐标投影到极坐标中。

selfishgene 选择查看图像的变化方向,写道:

每个图像可以被认为是高维图像空间中的不同“方向”

selfishgene looks at the variance of a leaf image

selfishgene 也花了一些时间研究图像重建、平均图像周围的模型变化和特征向量;他解释道:

最上面一行包含每个特征向量的数据分布(即沿该“方向”的直方图),第二行包含我们在前面的图中已经看到的内容,我们称之为方差方向。第四行包含叶子的中间图像。请注意,这一行对于所有特征向量都是相同的。第三行保存每个特征向量的第 2 个百分位数的图像。更容易认为这是中值图像减去特征向量图像乘以某个常数。

selfishgene looks at model variations

特征检测

lorinc 建议将每个样本一分为二,作为两个样本对待(尽管他并不追求这种方法)。 lorinc 从时间序列中寻找局部最大值和最小值(例如,在极坐标中绘制的叶子)并记录:

好吧,我自己也吃惊。这一点做得很好。我想,我可以由此构建一个极其高效的特性。但是这种方法还不够健壮。

不是找到尖端,而是找到离中心距离最大的点。(请看第 19 页)

在更复杂或不幸旋转的叶片上,它将悲惨地失败。(看第 78 页)

lorinc measures the minima and maxima of a leaf plotted in polar coordinates

在发现每片叶子周围存在噪音之前,lorinc 从那里开始讲述数学形态学。他花了一些时间想出如何从图像中去除噪声,并以一幅可爱的图像结束,该图像显示了叠加在叶子上的距离图:

lorinc measures the distance from the center of a leaf

肺癌

我选择分析的 EDAs 分别是Guido Zuidhof全预处理教程,Mikel Bober-Irizar的探索性数据分析和 Alexandru Papiu 的探索性分析可视化。

anokas examines the metadata for a single image. You can see that patient date has been rendered anonymous (1/1/1900)

我看的最后一个图像竞赛是 2017 数据科学碗,它要求参与者检查一系列图像,并预测患者是否患有癌症。虽然这场比赛的特点是结构化数据(嵌入图像本身的元信息),但其中一些数据是匿名的,这意味着原本具有预测价值的特征(如患者的年龄)被删除了。这意味着所有的内核都专注于图像分析。

在三个核心作者中, Guido 是唯一一个讨论他的医学图像工作背景的人,在他对数据集的特定领域分析中显示:

Dicom 是医学成像领域事实上的文件标准。…这些文件包含大量元数据(例如像素大小,即一个像素在现实世界中的每个维度上有多长)。扫描的这种像素大小/粗糙度因扫描而异(例如,切片之间的距离可能不同),这可能损害 CNN 方法的性能。我们可以通过同构重采样来解决这个问题

其他两位作者从数据集和图像本身的更一般的探索开始他们的 EDA。

apapie 从检查图像的形状开始,而 anokas 从查看每个患者的扫描次数、扫描总数和每个患者的 DICOM 文件直方图开始,同时进行快速的完整性检查,以查看行 ID 和患者是否患有癌症之间是否有任何关系(没有发现任何关系,这意味着数据集已经很好地排序)。

Alexandru 获取像素分布并绘制它们:

有趣的是,该分布似乎大致呈双峰分布,一组像素设置为-2000,可能是因为缺少值。

Guido 在他的 EDA 中更清楚地解释了这是为什么,即由于 HU 单位所代表的(空气、组织和骨骼):

形象

每个作者继续检查图像本身:

anokas looks at a set of patient images side by side

Alexandru 从 X 角度查看图像

Alexandru looks at images from the X angle

anokas builds a gif that moves through a set of patient images

Alexandru 花了一些时间探索边缘检测是否可以增强图像。

After increasing the threshold, Alexandru was able to render some visually striking images

亚历山德鲁的结论是:

有趣的结果,然而这里的问题是过滤器也将检测肺部的血管。因此,区分球体和管道的某种三维表面检测更适合这种情况。

同时, Guido 讨论了重采样,重点是 DICOM 图像的基本特性:

扫描可以具有[2.5,0.5,0.5]的像素间距,这意味着切片之间的距离是 2.5 毫米。对于不同的扫描,这可能是[1.5,0.725,0.725],这对于自动分析(例如使用 ConvNets)可能是有问题的!处理这种情况的一种常见方法是将整个数据集重采样到某个各向同性分辨率。如果我们选择将所有内容重新采样为 1mm1mm1mm 像素,我们可以使用 3D convnets,而不必担心学习缩放/切片厚度不变性。

后来在他的 EDA 中, Guido 能够通过组合多个 DICOM 图像来绘制内腔的 3D 图:

而另一个版本,在去除了周围的空气以减少内存之后:

外卖食品

这场比赛是我见过的内核差异最大的比赛。 Guido ,鉴于他对医学图像格式的熟悉,能够利用这一背景得出更加微妙的结论。也就是说,其他两位作者缺乏医学知识并不妨碍他们得出同样有趣的结论。

结论

事实证明,对于不同类型的数据,有一些强有力的模式可以指导方法。

对于结构化数据竞赛,数据分析倾向于寻找目标变量和其他列之间的相关性,并花费大量时间可视化相关性或对相关性进行排名。对于较小的数据集,您只能检查这么多列;在大竞争中的分析在检查哪些列和以什么样的顺序方面趋于一致。然而,不同的编码人员使用了非常不同的可视化方法,似乎在选择设计哪些特性方面有更多的创造性。

自然语言数据集在作者如何处理和操作文本方面与 EDA 有相似之处,但作者选择设计的特征有更多的可变性,以及从这些分析中得出的不同结论。

最后, Image 竞赛在分析和特征工程方面表现出最大的多样性。我看到的图像比赛大多针对高级观众,并且在相当特定的领域,这可能导致了更高级的多样性。

随着数据集变得更加专业化或深奥,介绍性分析和解释的数量减少,而深度或专业化分析的数量增加,这是有道理的,事实上这就是我所看到的。虽然不同类型的数据有明显的趋势,但领域知识起着重要的作用。在肺癌和 leaf 竞赛中,利用领域知识进行了更深入的分析。(说来有趣,我在自己的研究中见过这种情况;杰瑞米·霍华德在他的 fast.ai 课程中,讨论了罗斯曼数据集,以及最成功的模型如何整合第三方数据集,如温度、商店位置等,以做出更准确的销售预测。)

对于作者何时处理特征工程,没有一致的过程,一些人选择在他们开始分析时就开始,而另一些人在他们的初始分析完成后保持一个离散的步骤。

最后,我看到的每一个笔记本都有明确的读者群(初学者或高级),这影响了分析和写作。更受欢迎的比赛,或者那些针对更普通观众的比赛,有详尽的分析数据。在这些电子数据分析中,我也看到了一种趋势,即在分析的同时穿插补充性的文章或使用叙事手段,作为帮助初学者更好地理解技巧的工具。相比之下,面向领域专家的笔记本倾向于去掉多余的框架,许多还跳过了基本的数据分析,而是直接进入特定领域的技术。

特别感谢 米歇尔·卢 阿里·齐尔尼克 肖恩·马修斯 ,以及 贝瑟尼·巴西尔 为本文审稿。

原载于thekevinscott.com

常识分析

原文:https://towardsdatascience.com/common-sense-analytics-df0929ca5016?source=collection_archive---------4-----------------------

亲吻(保持简单愚蠢)

当我阅读人员分析职位的招聘信息时,我有点惊讶于对高级建模和机器学习的重视程度。广告通常倾向于或要求有博士学位的人,即使对于那些刚刚开始他们的人员分析之旅的组织也是如此。当然,有很多公司已经在做高级预测和其他类型的建模,他们为我们其他人开辟了道路。然而,也有许多组织已经能够通过使用基本数学和大量常识的人员分析来提高业务绩效和创造竞争优势。简单也可以很性感。

在业务环境中解释人员分析

在去年的一次采访中,有人向我提出了以下情景:

你正在向一群没有很强数学背景的经理展示结果,你需要解释贝叶斯统计。你会如何向他们解释?

我告诉他,如果我发现自己在与经理开会时必须解释贝叶斯统计,那么一定是出了严重的问题。我解释说,即使是最先进的数学模型也必须简化为我们可以在业务环境中讲述的数据故事。如果模型需要高等数学才能理解,又不能用商业术语解释,那就该创建新的模型了。

我认为面试官对我的回答不太满意。通过他参与的这种分析,研究人员可能无法完全理解某些东西为什么会起作用,但只要模型是可靠和准确的,这是可以接受的。对于人员分析,理解一个模型的工作原理是最重要的;准确往往是一种错觉。(我没有得到这份工作)

用数据讲故事

即使你的团队中有世界上最有才华的数学家,并且他创建了最前沿的深度学习神经网络,也不会让你走得很远,除非你能用主管和经理能够理解和理解的商业术语来解释它。根据我的经验,特别是当你开始进行人员分析时,你可以进行的一些最有洞察力和最有意义的分析是我所说的“常识分析”

你从一个问题开始,并确定数据可能如何回答它。你将数据切片,以不可避免地引出其他问题的方式来看待它,然后重复这个过程。数据揭示了一个故事,当你提炼你的问题来揭示它。或者,也许你基于机器学习算法创建了一个可靠的模型,现在你必须将其转化为普通的商业意识。不管你走哪条路,数据都要讲述一个故事,你需要把这个故事放在业务环境中。

将数学和统计转化为商业现实所需的技能是高级分析技能,但它们不同于使用基于梯度的优化来微调模型参数或使用判别分析来分类数据所需的高级分析技能。如果事实证明你需要后者,你通常至少可以暂时租用或借用技术专长。好的故事讲述者更难找到。

描绘一幅成功的图画

你的故事结尾需要描绘出一幅未来的图景,当你在业务表现上取得可衡量的进步时。经理或主管必须看到,最终结果的价值值得为实施这一变革所花费的时间、精力和对业务的破坏。最后,您必须为主管和经理提供他们实施变革所需的一切,包括与监控他们的进展直接相关的重点信息。抵制提供无关数据的诱惑,因为这只会转移他们的注意力,给信号增加噪音。

我想这也是《哈佛商业评论》最近一篇文章的观点,很多人可能已经看过了。它的标题是,“人力资源必须使人员分析更加用户友好”,它有一些建议来帮助组织组织和交流这些类型的结果,以成功地实施变革。

MOTS:“可解释性”和“可理解性”胜过准确性(这可能只是一种错觉)。

原贴于 HR 镜头

用 python 读取和可视化吉拉的数据

原文:https://towardsdatascience.com/communication-story-from-an-issue-tracking-software-efbbf29736ff?source=collection_archive---------6-----------------------

Photo by Nong Vang on Unsplash

吉拉是一个广泛应用于 bug/问题跟踪和敏捷项目管理的软件。该工具的实体之一是一个问题,它可以代表一个 bug、一个任务、一个订单或其他东西(这取决于一个组织使用该应用程序的方式):

Picture from marketplace-cdn.atlassian

在问题中有许多通信信息,例如,作者、受托人、描述、评论、状态。从发现模式、潜在问题、改进业务流程和提高使用吉拉的效率或简单地“鸟瞰”吉拉的通信流程来看,这可能是有趣的。

本文涵盖以下内容:

  • 使用 jira-python 库从吉拉读取信息。
  • 使用 python 的 networkx 库和图形可视化工具 Gephi 可视化数据。

以“大数据”方式从吉拉读取数据

不,我们不会考虑大数据工具。然而,我们事先不知道吉拉有多少数据(也许有人真的遇到大量数据),一个块一个块地处理数据是个好习惯。

吉拉-python 库允许我们轻松地与吉拉 API 进行交流。安装此库后,我们导入以下对象和异常,并创建导入对象的实例:

其中:

  • URL——吉拉的服务器地址,
  • 用户名和密码—代表与吉拉进行通信的用户的用户名和密码。使用所谓的技术用户是合理的,它并不代表一个真实的人,只是为了系统与系统之间的通信而创建的。

创建实例jira后,我们可以使用它的issue(读取单个问题)或search_issues(提取多个问题)方法读取数据。例如,让我们查找属于项目“项目 1”和“项目 2”且在过去 365 天内创建的问题:

其中 jql — 吉拉查询语言查找问题对应查询中给定的条件。jira_search具有类型jira.client.ResultList —匹配 JQL 的发布资源列表,例如:

[<JIRA Issue: key=’Key1–12000', id=’102007'>,<JIRA Issue: key=’Key2–12001', id=’102008'>,…]

迭代这个结果列表,可以提取问题的字段。作为上面代码中的一个例子,我们读取第一个问题的状态名。

结果列表吉拉服务器返回的问题资源的最大数量受服务器中配置的结果数量的限制。通常,我们不知道会从吉拉的服务器上读到多少期。此外,我们希望通过一次读取大量数据来避免吉拉的服务器过载。因此,这是一种迭代读取数据的好方法,逐块读取。在这种情况下,我们可以避免提到的问题,不去想吉拉的许多问题。

“While”循环可用于迭代读取数据。在给出示例之前,假设我们想要读取属于项目“项目 1”和“项目 2”的未知数量的问题,并且这些问题是在过去 365 天内创建的。我们将读取某些字段:问题的类型、创建日期和时间、解决日期和时间、报告者、受让人、状态。假设我们希望将所有读取数据保存到“csv”文件中。

下面是读取和保存数据的代码:

在上面的代码中,我们每次迭代一次读取 100 个问题,在读取必要的字段后,将这些数据写入“csv”文件。为了读取问题的特定部分,我们在search_issues方法中定义了参数:

  • startAt —返回的第一个问题的索引,
  • maxResults 返回的最大问题数。

通过使用search_issues方法,我们不仅可以读取字段,还可以读取例如 changelog。为此,必须传递参数expand='changelog'。Changelog 在状态或受托人历史方面很有意思。假设,我们想知道给定问题的受理人历史记录(谁进行了更改、以前的受理人、新的受理人、更改日期和时间):

好了,让我们进入下一步——可视化数据。正确可视化的数据可以指出有趣的特征。

可视化数据

为了让阅读数据来讲述一个故事,我们需要可视化,或者更准确地说,我们将建立一个有向图来表示报告者和受托人之间的联系。为此,我们再次逐块读取之前创建的“csv”文件,以节省计算资源:

为了构建这个图,我们使用了 networkx 库。在上面代码的第 25 行,我们定义了节点、边和权重(代表节点之间的连接频率)。可选地(第 32–46 行),我为节点创建了颜色属性,显示属于部门单位。代码的最后一行将我们的图形保存为‘graph ml’格式(其他格式)。

下一步非常有趣和吸引人——图形可视化。我更喜欢用 Gephi 。因此,在将保存的图形加载到该工具中并进行一些调整后,我们会生成如下结果:

在这个图表中我们可以看到什么特征?其中一些:

  • 节点的大小—越大,传入或/和传出的边越多,
  • 边的大小(就宽度而言)——越大,这种连接越频繁。

也许代表大节点和大边缘的员工需要一些东西来提高他们的工作效率。然而,所有这些案件都需要进一步调查。

还有一点:在前一批代码中有一个日期变量(第 27–30 行)。此变量是节点出现在吉拉数据中的日期。因此,使用它,我们可以把我们的图表表示为动态的:

结论

上述内容有助于构建进一步的步骤:计算统计数据(被分配人创建了多少评论,员工在沟通上花费了多少时间等等)。再深入一点,我们可以,例如,对描述/评论进行语义或形态分析。你想要的目标没有限制!

公司数据 Sci 新闻:几乎全是谷歌,外加亚马逊、IBM 和美国电话电报公司

原文:https://towardsdatascience.com/company-data-sci-news-its-almost-all-google-with-a-splash-of-amazon-ibm-and-at-t-e7875878e283?source=collection_archive---------11-----------------------

我与我的同事 Brad Stenger 和我们训练有素的机器人一起写了一篇关于数据科学的 简讯 。我们训练这个机器人对数据科学有很好的品味,然后把它每周发现的东西联系起来。这是上周来自数据科学行业的消息。

Unknown street artist. Gowanus, Brooklyn 2016. Photograph by Laura Norén (I like street art.)

Yann LeCun 称 GANs “过去 20 年深度学习中最酷的想法。”当甘斯还是蒙特利尔的一名研究生时,他发明了一个生成性人工智能代理,与另一个关键的人工智能代理进行竞争,后者旨在评估第一个人工智能代理生成的元素的有效性。简而言之,一个人工智能创造艺术,另一个充当艺术评论家,将第一个代理的产品推向比它自己更高的标准。“因为第二个人工智能非常努力地识别图像是假的,所以第一个人工智能学会了以自己无法做到的方式模仿真实的图像。”在假设这意味着我们正在迅速接近“计算机宣布独立于人类教师的那一天”之前,我要指出,即使是人类系统,在没有明确的外部干扰的情况下,也能发展出瘫痪的群体思维。换句话说,这一演变的下一步将是把 GANs 放在人工智能对联的市场上。

谷歌的 安卓团队 宣布 迈向隐私保护、人工智能增强的联合学习。他们在安卓手机的键盘上安装了 TensorFlow 的元素。简而言之,“你的设备下载当前模型,通过从手机上的数据(你通过输入搜索和选择生成的数据)中学习来改进它,然后将这些变化总结为一个小的有重点的更新,”它将这些更新发送回云端。当你的手机插上电源以保存电池电量时,计算密集型过程就会进行。因为只有更改摘要被发送到云,并且因为“如果有 100 或 1000 个用户参与,协调服务器只能解密平均更新”,所以与您的粒度数据相关联的隐私得到了保护。如果有人读到这篇文章,并愿意让我了解更多关于联合学习的知识,我很好奇。

谷歌的 DeepMind 正在上演另一场人类对人工智能 阿尔法围棋比赛 。我知道我应该为此感到兴奋。

谷歌 宣布 其 2017 届北美、欧洲和中东博士生。祝贺所有人,特别是马丁·阿约夫斯基,他将加入我们纽约大学的行列。

上周谷歌推出 自动绘制 。AutoDraw 本质上是自动纠正涂鸦,这可能是无意的滑稽。把你曲解的草图给我!到目前为止,我发现这是一个计算密集型策略来制作剪贴画。

本周,谷歌主宰了公司新闻版块。据 Axios 称,他们还发布了一个管理招聘流程的平台: 谷歌招聘 ,该平台位于该公司的企业和云服务部门。我们今天刚刚进行了一次午餐谈话,讨论了公司如何利用申请人社交网络活动的数据来帮助做出招聘决定。目前还不清楚这是如何运作的,但这可能是歧视性的,让那些有富裕朋友的人享有特权,让那些有犯罪背景或倾向于在评论中使用脏话的朋友靠边站。我们将会看到谷歌是否能让它的招聘程序正确。

AT & T Research 拥有 开源 一个名为 纳米立方体 的平台,可以快速“以时空方式可视化”数十亿个数据点

亚马逊的 杰夫·贝索斯上周写了 一封重要的股东信,他在信中说,“我和任何人一样能感觉到一个重要的趋势,当然我们正在整合机器学习”。检查了必要的人工智能框后,他继续解释如何在团队中诱导“高速决策”,这是一个我希望我们能在学术界实施的策略。换句话说,他花在如何管理人员上的时间远远多于他打算如何开发机器人。提神醒脑。

从人口普查数据预测个人收入范围的分类器比较研究

原文:https://towardsdatascience.com/comparative-study-of-classifiers-in-predicting-the-income-range-of-a-person-from-a-census-data-96ce60ee5a10?source=collection_archive---------11-----------------------

不同机器学习分类器的实现指南

我们作为一个社会,已经彻底改变了我们已知的专业领域。机器学习人工智能就是从原始数据中提取知识的领域之一。这种知识可用于建立具有高精度和准确度的动态模型,使得模型本身可用于预测或分类先前未知的数据。从人口普查数据中提供知识的一个最重要的方面是通过调查以前的记录来预测每个人的收入和健康等数据。

机器学习不是魔法,只有当需要解决的问题尽可能准确地从现实世界中表达出来时,它才会起作用。机器学习最重要和最关键的任务之一是清理和准备应该馈送给分类器用于训练模型的数据。我将讨论在预处理每个分类器的数据、训练模型和在这个分析领域的性能评估中使用的不同技术。

有许多分类器可用于根据历史数据和现有数据对数据进行分类。在这里,我倾向于对不同的常用机器学习分类器进行比较研究,如决策树、朴素贝叶斯、随机森林和支持向量机以及神经网络。本次研究选择的数据集是 UCI 成人数据集

数据集链接—https://archive.ics.uci.edu/ml/datasets/adult

选择最佳的机器学习算法来解决数据的分类和预测问题是机器学习中最重要的部分,它也依赖于数据集。在这里,我比较了这些分类器的不同评估措施,如混淆矩阵,ROC 曲线和绘图的结果。

这一想法旨在提供一个现有研究的基准,这些研究是在此类分类器的比较研究中完成的,旨在根据人口普查数据预测一个人的收入范围。

UCI 知识库成人数据集是一个对所有人开放的数据集。它由 14 个属性和一个类别标签组成,说明个人的年收入是低于还是高于 50K。这些属性的范围从人的年龄、工人阶级标签到关系状态和人所属的种族。关于所有属性的信息可以在这里找到。

Description of the dataset.

注意 —数据非常原始,并且缺少值。

预处理数据集

有各种方法可以通过移除冗余值和处理缺失值来改善所获得的数据集。在这个数据集中,我们有 14 个属性,每个属性都对结果有贡献,因此我们不删除任何属性。三个属性 work-class、occupation 和 native-country 有缺失值出现,这些值将被列中出现次数最多的实例替换。

为了将数据拟合到预测模型中,我们需要将分类值转换为数值。在此之前,我们将评估是否有必要对分类列进行任何转换。离散化是使分类数据更加整洁和有意义的常用方法。我对婚姻状况一栏进行了离散化处理,将它们缩小到只包含已婚或未婚的值。稍后,我在剩余的数据列中应用标签编码器。

分类器的实现

在根据数据的输入方式对每个分类器的数据进行预处理之后,我们对数据进行切片,将标签和属性分开。现在,我们将数据集分成两半,一部分用于训练,一部分用于测试。这是使用 sklearn 的 train_test_split()函数实现的。

同样,对于其他分类器:

在神经网络的情况下,以前我们使用标签编码将分类数据转换为数值,但对于某些属性,它会产生一个问题,即赋予相等值更高的重要性。因此,为了纠正这一点,我们使用熊猫图书馆的虚拟变量技术。这将从 14 列创建 88 列作为神经网络的输入。

现在让我们看看结果。

实现的分类器比较

使用 tensorflow 实现的分类器有决策树朴素贝叶斯支持向量机随机森林神经网络

我们检查成人数据集的每个分类器的结果,并绘制结果和发现。比较方法对于这种预测方法至关重要,以便为进一步的研究和应用设计结果。

分数(分类报告)

下表中包含了所有的分类器和不同的得分,如准确率精度召回率F1-得分基尼系数F1 分数这是一种非常常用的测试准确度的衡量标准。计算分数体现了测试的精确性和召回性。精度是真阳性的数量除以真阳性和假阳性的总和。同样, recall 是真阳性的个数除以真阳性和假阴性的总和,也就是实际属于阳性类的元素总数。基尼系数用于直接从 AUC 值得出的分类问题。只不过是 ROC 曲线和对角线的面积之比&上面三角形的面积。

结果如下:

Scores of different models

混淆矩阵

混淆矩阵是一个简单的 NxN 矩阵,其中 N 是类别标签的数量。提供了正确和错误识别的阳性和阴性病例部分的值,因此也获得了准确性。下面是所有五个分类器的混淆矩阵。

结果如下:

Confusion Matrix

受试者工作特征曲线

接收器工作特性曲线是灵敏度和(1-特异性)之间的曲线。(1-特异性)也称为假阳性率,灵敏度也称为真阳性率。这里我们还计算了 ROC 曲线下的面积(AUC)。它描述了测试如何将被测试的组分成两个类别标签。面积越大越好。

得到了下面的曲线图:

ROC curve

观察

结果表明,支持向量机和随机森林方法能够很好地准确预测数据,准确率分别为 84.08%和 84.83%。另一方面,神经网络显示出显著的效果,本质上是一种新颖和非正统的。神经网络模型的 AUC 曲线为 0.77,准确率达到显著的 86.30%。

上述获得的结果可用作在从人口普查数据预测数值领域进行的其他比较研究的标准参考点。这种比较研究可以进一步用作改进现有分类器和技术的基础,从而产生用于准确预测个人收入水平的更好技术。

当今数据科学领域研究的目标是构建从数据中提取知识的系统和算法。这项研究可以补充现有方法的能力并减少任何错误,从而在从历史人口普查数据中进行数据挖掘的领域中做出贡献。

如需进一步参考所有代码和结果,请访问我的 GitHub 知识库—https://GitHub . com/ritvikkhanna 09/Census-classifier-comparison

经典机器学习算法的比较研究

原文:https://towardsdatascience.com/comparative-study-on-classic-machine-learning-algorithms-24f9ff6ab222?source=collection_archive---------0-----------------------

各种 ML 算法的快速总结

机器学习是一种科学技术,在这种技术中,计算机学习如何解决一个问题,而不需要对它们进行显式编程。在更好的算法、计算能力和大数据的推动下,深度学习目前正在引领 ML 竞赛。尽管如此,最大似然经典算法在该领域仍有其强大的地位。

我将对不同的机器学习监督技术进行比较研究,如本故事中的线性回归、逻辑回归、K 近邻和决策树 在的下一个故事中,我将覆盖 支持向量机、随机森林和天真八爷 s 。关于算法的深入细节有那么多比较好的博客,我们就只着重比较研究一下。我们将研究它们的 基本逻辑、优点、缺点、假设、共线效应&异常值、超参数 相互比较

其余算法请参考本系列的第 2 部分。

1。线性回归

如果你想开始机器学习,线性回归是最好的起点。线性回归是一种回归模型,也就是说,它将获取特征并预测连续输出,例如:股票价格、工资等。线性回归顾名思义,找到一个线性曲线解决每个问题。

基础理论:

LR 为每个训练特征分配权重参数θ。预测输出(h(θ))将是特征和θ系数的线性函数。

linear regression output. Eqn (1)

在训练开始时,每个θ被随机初始化。但是在训练期间,我们校正对应于每个特征的θ,使得损失(预期和预测输出之间的偏差的度量)最小化。梯度下降算法将用于在正确的方向上对齐θ值。在下图中,每个红点代表训练数据,蓝线显示导出的解决方案。

gif credits : https://medium.com/@kabab/linear-regression-with-python-d4e10887ca43

损失函数:

在 LR 中,我们使用均方误差作为损失的度量。预期和实际输出的偏差将被平方并求和。梯度下降算法将使用该损失的导数。

优点:

  • 容易和简单的实现。
  • 空间复解。
  • 快速训练。
  • θ系数的值给出了特征重要性的假设。

缺点:

  • 仅适用于线性解决方案。在很多现实生活场景中,可能并非如此。
  • 算法假设输入残差(误差)为正态分布,但可能并不总是满足。
  • 算法假设输入特征相互独立(无共线)。

超参数:

  • 正则化参数(λ):正则化用于避免对数据的过拟合。λ越大,正则化程度越高,解的偏差越大。λ越低,解的方差越大。中间值是优选的。
  • 学习率(α):它估计在训练期间应用梯度下降算法时,θ值应该被校正多少。α也应该是一个适中的值。

LR 的假设:

  • 自变量和因变量之间的线性关系。
  • 训练数据是同方差的,这意味着误差的方差应该是恒定的。
  • 自变量不应该是共线的。

共线性&异常值:

当一个特征可以从另一个特征以一定的精度线性预测时,称两个特征共线。

  • 共线性只会扩大标准误差,并导致一些重要特征在训练过程中变得不重要。理想情况下,我们应该在训练之前计算共线性,并且只保留高度相关的特征集中的一个特征。

离群值是训练中面临的另一个挑战。它们是对正常观察极端的数据点,并影响模型的准确性。

  • 异常值会增大误差函数,并影响曲线函数和线性回归的精度。正则化(尤其是 L1)可以通过不允许θ参数剧烈变化来校正异常值。
  • 在探索性数据分析阶段本身,我们应该注意异常值并纠正/消除它们。箱线图可用于识别它们。

与其他车型对比:

由于线性回归是一种回归算法,我们将它与其他回归算法进行比较。线性回归的一个基本区别是,LR 只能支持线性解。机器学习中不存在胜过所有其他模型的最佳模型(没有免费的午餐),效率是基于训练数据分布的类型。

LR vs 决策树:

  • 决策树支持非线性,而 LR 只支持线性解决方案。
  • 当具有较少数据集(低噪声)的大量特征时,线性回归可能优于决策树/随机森林。在一般情况下,决策树会有更好的平均准确率。
  • 对于分类独立变量,决策树优于线性回归。
  • 决策树比 LR 更好地处理共线性。

LR vs SVM :

  • SVM 支持使用内核技巧的线性和非线性解决方案。
  • SVM 比 LR 更好地处理异常值。
  • 两者在训练数据较少且特征数量较多的情况下表现良好。

LR vs KNN :

  • KNN 是一个非参数模型,而 LR 是一个参数模型。
  • KNN 的实时速度很慢,因为它必须跟踪所有训练数据并找到邻居节点,而 LR 可以很容易地从调谐的θ系数中提取输出。

LR vs 神经网络:

  • 与 LR 模型相比,神经网络需要大量的训练数据,而 LR 即使使用较少的训练数据也可以工作得很好。
  • NN 相比 LR 会比较慢。
  • 神经网络的平均精度总是更好。

2。逻辑回归

就像线性回归一样,逻辑回归是从分类算法开始的正确算法。尽管“回归”这个名字出现了,但它不是一个回归模型,而是一个分类模型。它使用逻辑函数来构建二进制输出模型。逻辑回归的输出将是一个概率(0≤x≤1),并可用于预测二进制 0 或 1 作为输出(如果 x <0.5, output= 0, else output=1).

基本理论:

逻辑回归的作用有点类似于线性回归。它还计算线性输出,然后对回归输出执行存储函数。Sigmoid 函数是常用的逻辑函数。从下面可以清楚地看到,z 值与等式(1)中的线性回归输出值相同。

这里的 h(θ)值对应于 P(y=1|x),即给定输入 x,输出为二进制 1 的概率。P(y=0|x)将等于 1-h(θ)。

当 z 的值为 0 时,g(z)将为 0.5。每当 z 为正时,h(θ)将大于 0.5,输出将为二进制 1。同样,每当 z 为负时,y 的值将为 0。当我们使用线性方程来寻找分类器时,输出模型也将是线性的,这意味着它将输入维度分成两个空间,其中一个空间中的所有点对应于相同的标签。

下图显示了 sigmoid 函数的分布。

sigmoid function Eqn(3)

损失函数:

我们不能用均方差作为损失函数(像线性回归),因为我们在最后用了一个非线性的 sigmoid 函数。MSE 函数会引入局部极小值,影响梯度下降算法。

所以我们用交叉熵作为损失函数。将使用两个等式,对应于 y=1 和 y=0。这里的基本逻辑是,每当我的预测严重错误时,(例如:y' =1 & y = 0),成本将是-log(0),这是无穷大。

cross-entropy loss Eqn(4)

在给出的等式中,m 代表训练数据大小,y’代表预测输出,y 代表实际输出。

优点:

  • 简易、快速、简单的分类方法。
  • θ参数解释了自变量相对于因变量的显著性的方向和强度。
  • 也可用于多类分类。
  • 损失函数总是凸的。

缺点:

  • 不能应用于非线性分类问题。
  • 需要正确选择功能。
  • 期望有好的信噪比。
  • 共线性和异常值会影响 LR 模型的准确性。

超参数:

逻辑回归超参数类似于线性回归超参数。学习率(α)和正则化参数(λ)必须适当调整以实现高精度。

LR 的假设:

逻辑回归假设类似于线性回归模型的假设。请参考上述部分。

与其他车型对比:

逻辑回归 vs SVM :

  • SVM 可以处理非线性解,而逻辑回归只能处理线性解。
  • 线性 SVM 能更好地处理异常值,因为它能导出最大利润解。
  • SVM 的铰链损耗优于 LR 的原木损耗。

逻辑回归 vs 决策树:

  • 决策树比 LR 更好地处理共线性。
  • 决策树不能得出特征的重要性,但是 LR 可以。
  • 决策树比 LR 更适合分类值。

逻辑回归 vs 神经网络:

  • NN 可以支持 LR 不能支持的非线性解。
  • LR 具有凸损失函数,所以它不会陷入局部极小值,而 NN 可能会陷入局部极小值。
  • 当训练数据较少且特征较多时,LR 优于 NN,而 NN 需要大量训练数据。

逻辑回归 vs 朴素贝叶斯:

  • 朴素贝叶斯是一种生成模型,而 LR 是一种判别模型。
  • 朴素贝叶斯适用于小数据集,而 LR+正则化可以实现类似的性能。
  • LR 在共线性方面比朴素贝叶斯表现得更好,因为朴素贝叶斯期望所有特征都是独立的。

逻辑回归 vs KNN :

  • KNN 是非参数模型,而 LR 是参数模型。
  • KNN 比逻辑回归相对慢。
  • KNN 支持非线性解决方案,而 LR 仅支持线性解决方案。
  • LR 可以导出置信度(关于它的预测),而 KNN 只能输出标签。

3。k-最近邻

k-最近邻法是一种用于分类和回归的非参数方法。这是最简单的 ML 技术之一。它是一个懒惰的学习模型,具有局部近似性。

基本理论:

KNN 背后的基本逻辑是探索你的邻居,假设测试数据点与他们相似,并得到输出。在 KNN,我们寻找 k 个邻居并得出预测。

在 KNN 分类的情况下,在 k 个最近的数据点上应用多数投票,而在 KNN 回归中,k 个最近的数据点的平均值被计算为输出。根据经验,我们选择奇数作为 k。KNN 是一个懒惰的学习模型,计算只在运行时发生。

image credits : https://www.fromthegenesis.com/pros-and-cons-of-k-nearest-neighbors/

在上图中,黄色和紫色点对应于训练数据中的 A 类和 B 类。红星指的是要分类的测试数据。当 k = 3 时,我们预测 B 类作为输出,当 K=6 时,我们预测 A 类作为输出。

损失函数:

在 KNN 没有培训。在测试期间,具有最小距离的 k 个邻居将参与分类/回归。

优势:

  • 简易的机器学习模型。
  • 需要调整的超参数很少。

缺点:

  • 应该明智地选择 k。
  • 如果样本量很大,则运行时的计算成本很大。
  • 应该提供适当的缩放,以便在特性之间进行公平处理。

超参数:

KNN 主要涉及两个超参数,K 值和距离函数。

  • k 值:有多少邻居参与 KNN 算法。k 应该根据验证误差进行调整。
  • 距离函数:欧几里德距离是最常用的相似性函数。曼哈顿距离、海明距离、闵可夫斯基距离是不同的选择。

假设:

  • 应该对输入域有清晰的理解。
  • 可行的中等样本量(由于空间和时间的限制)。
  • 共线性和异常值应在训练前处理。

与其他型号的比较:

KNN 和其他模型之间的一个普遍区别是,与其他模型相比,KNN 需要大量的实时计算。

KNN vs 朴素贝叶斯:

  • 由于 KNN 的实时执行,朴素贝叶斯比 KNN 快得多。
  • 朴素贝叶斯是参数化的,而 KNN 是非参数化的。

KNN vs 线性回归:

  • 当数据信噪比较高时,KNN 优于线性回归。

KNN 对 SVM :

  • SVM 比 KNN 更关心离群值。
  • 如果训练数据远大于特征数(m>>n),KNN 比 SVM 好。当有大量特征和较少训练数据时,SVM 胜过 KNN。

KNN vs 神经网络:

  • 与 KNN 相比,神经网络需要大量的训练数据来达到足够的精度。
  • 与 KNN 相比,神经网络需要大量的超参数调整。

4。决策树

决策树是一种基于树的算法,用于解决回归和分类问题。构造一个倒置的树,它从一个均匀概率分布的根节点分支到高度不均匀的叶节点,用于导出输出。回归树用于具有连续值的因变量,分类树用于具有离散值的因变量。

基础理论:

决策树是从独立变量中导出的,每个节点都有一个特征条件。节点根据条件决定下一步导航哪个节点。一旦到达叶节点,就可以预测输出。正确的条件顺序使树有效率。熵/信息增益被用作选择节点条件的标准。使用递归的、基于贪婪的算法来导出树结构。

credits : https://brookewenig.github.io

在上图中,我们可以看到一个树,它有一组内部节点(条件)和带标签的叶节点(拒绝/接受提议)。

选择条件的算法:

  • 对于 CART(分类和回归树),我们使用基尼指数作为分类度量。这是计算数据点混合程度的指标。

在创建决策树的每个阶段,具有最大基尼系数的属性被选为下一个条件。当集合是不平等混合时,基尼系数将是最大的。

  • 对于迭代二分法 3 算法,我们使用熵和信息增益来选择下一个属性。在下面的等式中,H(s)代表熵,IG(s)代表信息增益。信息增益计算父节点和子节点的熵差。选择具有最大信息增益的属性作为下一个内部节点。

优点:

  • 数据不需要预处理。
  • 没有关于数据分布的假设。
  • 有效处理共线性。
  • 决策树可以对预测提供可理解的解释。

缺点:

  • 如果我们继续构建树以达到高纯度,则有可能过度拟合模型。决策树剪枝可以用来解决这个问题。
  • 倾向于离群值。
  • 在训练复杂数据集时,树可能会变得非常复杂。
  • 处理连续变量时丢失有价值的信息。

超参数:

决策树包括许多超参数,我将列出其中的几个。

  • 准则:选择下一个树节点的代价函数。常用的有基尼/熵。
  • 最大深度:决策树允许的最大深度。
  • 最小样本分裂:分裂一个内部节点所需的最小节点。
  • 最小样本叶:要求在叶节点的最小样本。

与其他车型对比:

决策树 vs 随机森林:

  • 随机森林是决策树的集合,选择森林的平均/多数投票作为预测输出。
  • 随机森林模型将比决策树更不容易过拟合,并给出更一般化的解决方案。
  • 随机森林比决策树更加健壮和准确。

决策树 vs KNN :

  • 两者都是非参数方法。
  • 决策树支持自动特征交互,而 KNN 不支持。
  • 由于 KNN 昂贵的实时执行,决策树更快。

决策树 vs 朴素贝叶斯:

  • 决策树是一种判别模型,而朴素贝叶斯是一种生成模型。
  • 决策树更加灵活和简单。
  • 决策树剪枝可能会忽略训练数据中的一些关键值,这会导致准确性下降。

决策树 vs 神经网络:

  • 两者都能找到非线性解,并且独立变量之间有相互作用。
  • 当训练数据中有大量类别值时,决策树更好。
  • 当场景要求对决策进行解释时,决策树比神经网络更好。
  • 当有足够的训练数据时,神经网络优于决策树。

决策树 vs SVM :

  • SVM 使用核技巧来解决非线性问题,而决策树在输入空间中导出超矩形来解决问题。
  • 决策树更适合分类数据,它比 SVM 更好地处理共线性。

在下一个故事中,我将介绍剩余的算法,如朴素贝叶斯、随机森林和支持向量机。如有任何建议或修改,请赐教。

快乐学习:)

Tensorflow 中实现稀疏编码的不同方法比较

原文:https://towardsdatascience.com/comparing-different-methods-of-achieving-sparse-coding-in-tensorflow-manual-back-prop-in-tf-d47053da4d8e?source=collection_archive---------16-----------------------

GIF from this website

在韩国度假期间,我一直在研究更多关于稀疏编码和实现它的不同方法,今天我想比较其中的一些。下面是我将要比较的所有案例。(以防我将案例 d 和案例 e 的两篇论文联系起来。)

案例 a:纯自动编码器
案例 b:具有 L2 正则化的自动编码器
案例 c:稀疏自动编码器来自
安德鲁·吴的课程 案例 d: 简单、高效的稀疏编码神经算法 案例 e:k-稀疏自动编码器

请注意,这个帖子是为了我未来的自己,也是为了存档已经取得的成果。

Paper from this website

Paper from this website

简介、数据集和总体思路

Image from this website

我已经假设读者对神经网络和自动编码器有一些了解,所以我不会深入细节。此外,我只打算介绍使用自动编码器进行稀疏编码的方法。请记住,hyper 参数的设置是相同的,但这并不能保证它对每种情况都是最佳的!最后,我将使用的所有数据都来自 MNIST 数据集。

橙色框 →矢量形式的原始数据
红色框 →压缩数据
蓝色框 →重构数据

自动编码器的一般思想非常简单,在压缩后重建原始数据。在今天的帖子中,我们的目标是学习到的权重(橙色框和红色框之间的权重),因为这些权重代表了我们正在捕捉的数据类型(或特征)。

案例 a 的结果:纯自动编码器

Progression of learned weights for 500 epoch

从上面的过程中,我们可以注意到一些事情,学习的权重(或特征)并不特定于某些数字。这是因为由于网络可以使用学习字典中的所有原子来重构原始数据,因此不需要学习给定数据的专门过滤器。

左图 →原始数据
右图 →重建数据

重建的数据有点模糊,但是,一般来说,它包含了正确的数字形状。

左图 →对比度归一化的学习权重
中图 →对比度归一化的学习权重
右图 →训练期间的时间成本

当我们再次查看学习到的权重时,我们可以再次观察到过滤器并不特定于某些数字。

情况 b 的结果:采用 L2 正则化的自动编码器

Progression of learned weights for 500 epoch

当我们将 L2 正则化添加到原始自动编码器时,我们看不到学习到的权重之间的极端差异。这是自然的,因为在重构原始数据时,网络仍然可以完全访问字典中的所有原子。

左图 →原始数据
右图 →重建数据

同样,类似于纯自动编码器,重建的数据是模糊的,但它仍然保留了一般的形状。

左图 →对比度归一化的学习权重
中图 →对比度归一化的学习权重
右图 →训练期间的时间成本

我们仍然看不到特定于某些数字的过滤器。

案例 c 的结果:来自 Andrew NG 的课程 的稀疏自动编码器

Progression of learned weights for 500 epoch

当我们在隐藏层中引入稀疏性的概念时,并不是字典中的所有原子都可以使用。我们可以清楚地观察到,学习到的过滤器正变得更加特定于某些数字,并且在最终时期之后,过滤器代表数字的手动敲击。

左图 →原始数据
右图 →重建数据

我个人认为,重建的数据比其他两个(上图)更模糊,但它们是清晰可辨的。

左图 →对比度归一化的学习权重
中图 →对比度归一化的学习权重
右图 →训练期间的时间成本

当我们将学习到的权重可视化时,我们可以看到网络正试图从给定的数据中提取不同的笔画。

案例 d 的结果: 简单、高效、用于稀疏编码的神经算法

Progression of learned weights for 500 epoch

如果没有要优化的重建损失函数,我们可以观察到收敛需要更长的时间。然而,我们可以观察到,如果字典中的某个原子被使用,它会产生一个类似的过滤,就像我们有稀疏的概念时一样。(案例 c)。

左图 →原始数据
右图 →重建数据

这种方法的一个缺点是没有重建损失,虽然它产生更干净的滤波器,但它不能像其他方法一样重建原始数据。

左图 →对比度归一化的学习权重
中图 →对比度归一化的学习权重
右图 →训练期间的时间成本

当我们可视化学习到的权重时,我们可以看到这种方法的优点,即它产生更干净的滤波器。

情况 e 的结果:k-稀疏自动编码器

Progression of learned weights for 500 epoch

对于给定的数据,前 K 个稀疏自动编码器收敛到最佳点要快得多。并且我们可以观察到,在捕捉手写笔画类型特征的同时,所学习的权重是干净的。

左图 →原始数据
右图 →重建数据

由于重建误差的存在,当与情况 d 相比时,重建的数据更清晰,然而,我们可以清楚地观察到它在这里和那里缺乏对比度的事实。

左图 →对比度归一化的学习权重
中图 →对比度归一化的学习权重
右图 →训练期间的时间成本

同样,学习过的过滤器在捕捉手笔画类型特征时更干净。

交互代码

对于谷歌 Colab,你需要一个谷歌帐户来查看代码,而且你不能在谷歌 Colab 中运行只读脚本,所以在你的操场上做一个副本。最后,我永远不会请求允许访问你在 Google Drive 上的文件,仅供参考。编码快乐!

要访问案例 a 的代码,请点击此处。
要获取案例 b 的代码,请点击此处。
要访问案例 c 的代码,请点击此处。
要获取案例 d 的代码,请点击此处。
要获取案例 e 的代码,请点击此处。

最后的话

这让我如此满意的原因是因为我没有使用任何自动微分。

如果发现任何错误,请发电子邮件到 jae.duk.seo@gmail.com 给我,如果你想看我所有写作的列表,请在这里查看我的网站。

同时,在我的推特上关注我这里,访问我的网站,或者我的 Youtube 频道了解更多内容。我还实现了广残网,请点击这里查看博文 pos t。

参考

  1. FNNDSC/med2image。(2018).GitHub。检索于 2018 年 8 月 4 日,来自https://github.com/FNNDSC/med2image
  2. 张量,T. (2018)。张量中的前 n 个值。堆栈溢出。2018 年 8 月 4 日检索,来自https://stack overflow . com/questions/40808772/tensor flow-top-n-values-in-tensor
  3. t . way(2018)。使用 top_k 和 any way 的 tesorflow 排序。堆栈溢出。检索于 2018 年 8 月 4 日,来自https://stack overflow . com/questions/46045867/tesorflow-sort-using-top-k-and-any-way
  4. tf.nn.top_k |张量流。(2018).张量流。检索于 2018 年 8 月 4 日,来自https://www.tensorflow.org/api_docs/python/tf/nn/top_k
  5. tf.greater | TensorFlow。(2018).张量流。检索于 2018 年 8 月 4 日,来自 https://www.tensorflow.org/api_docs/python/tf/greater
  6. x?,H. (2018)。如何在 OS X 上将 Python 的默认版本设置为 3.3?。堆栈溢出。2018 年 8 月 5 日检索,来自https://stack overflow . com/questions/18425379/how-to-set-python-default-version-to-3-3-on-OS-x
  7. 张量,T. (2018)。张量中的前 n 个值。堆栈溢出。检索于 2018 年 8 月 12 日,来自https://stack overflow . com/questions/40808772/tensor flow-top-n-values-in-tensor
  8. (2018).Web.stanford.edu。检索于 2018 年 8 月 13 日,来自https://web . Stanford . edu/class/cs 294 a/sparseautoencer _ 2011 new . pdf
  9. (2018).Arxiv.org。检索于 2018 年 8 月 13 日,来自https://arxiv.org/pdf/1503.00778.pdf
  10. (2018).Arxiv.org。检索于 2018 年 8 月 13 日,来自https://arxiv.org/pdf/1312.5663.pdf
  11. MNIST 手写数字数据库,Yann LeCun,Corinna Cortes 和 Chris Burges。(2018).Yann.lecun.com。检索于 2018 年 8 月 13 日,来自http://yann.lecun.com/exdb/mnist/

在营销用例中比较多臂 Bandit 算法

原文:https://towardsdatascience.com/comparing-multi-armed-bandit-algorithms-on-marketing-use-cases-8de62a851831?source=collection_archive---------2-----------------------

A/B 测试是许多电子商务公司营销过程中的标准步骤。通过精心设计的 A/B 测试,营销人员可以获得关于在哪里以及如何最大化其营销努力并推动成功活动的见解。然而,实际上,与更先进的机器学习方法相比,标准的 A/B 测试会把钱留在桌子上。

在这篇文章中,我们将讨论 A/B 测试的当前状态,定义一些用于优化 A/B 测试的常见机器学习算法(Multi-Armed Bandits ),最后描述这些算法在一些典型营销用例中的性能。

A/B 测试:它是如何工作的?

在一个标准的 A/B 测试实验中,我们想要测量一个活动的变体比另一个更有效的可能性,同时控制我们测量错误的可能性——要么我们认为有一个赢家,而实际上没有,要么我们错过了检测获胜的变体。为了进行准确的测量,A/B 测试必须考虑两个关键值:统计功效和统计显著性。

从形式上来说,统计功效是当一个效应存在时(当一个变量优于另一个变量时),实验将实际检测到该效应的概率,统计显著性衡量我们对所测量的效应实际存在的信心程度。然而,从置信区间的角度来考虑这两个想法会更直观。

在给定的实验中,我们测量每个变量的平均转化率,但我们知道平均值只是“真实”转化率的估计值。根据我们所拥有的观察值的数量,我们可能对估计值有或多或少的信心,并且我们可以使用一个区间来表示这种信心,在该区间内可能会找到真实值。例如,如果我们在 95%的置信度下说一个转换率是 0.032 +/- 0.002,那么我们有 95%的置信度认为实际转换率在 0.030 和 0.034 之间。实际上,我们会寻找不重叠的置信区间,这样真实转换率不同的概率就大于 95%。

但是,等待间隔分开可能需要很长时间。在实际情况中,对于 0.03 的典型点击率,为了检测 10%的点击转换提升(0.030.1 = 0.003),对于统计功效和显著性的标准水平,我们将需要总共 106,000 个接触的最小样本量(每个变体 53,000 个接触)。根据流量,积累这么多数据可能需要几天到几个月的时间,如果我们有更多的变体,更低的转化率,或更低的效果大小,收集周期可能会更长。*

多兵种强盗算法:探索+利用

在机器学习中,“探索与利用的权衡”适用于希望获得新知识并同时最大化其回报的学习算法——这被称为强化学习问题。在这种情况下,后悔被定义为你可能会想到的:由于执行学习算法而不是从一开始就表现最佳,导致奖励减少。为探索而优化的算法往往会招致更多的遗憾。

A/B 测试是一种纯粹的探索性**方法。由于在 A/B 测试实验中,接触是以相等的概率随机分配给每个变量的,因此可实现的总回报等于所有变量的平均回报,并且必须低于获胜变量的平均回报。如上例所示,典型的 A/B 测试往往需要数十万次接触才能达到适当的统计功效。使用固定规模的随机分配算法来进行这种类型的实验可能会导致整体支出的大量损失——A/B 测试可能会有非常高的遗憾**

Multi-armed bandit (MAB) 算法可以被认为是 A/B 测试的替代方案,可以在学习过程中平衡开发和探索。MAB 的解决方案利用现有的实验结果,为表现良好的变体分配更多的联系,而为表现不佳的变体分配较少的流量。理论上,多臂 bandit 算法应该产生更高的总体回报(并减少遗憾),同时仍然允许人们收集用户如何与活动的不同变化进行交互的数据。

有几种 MAB 算法,每一种都在不同程度上倾向于开发而不是探索。三个最流行的是ε贪婪,汤普森抽样,和置信上限 1 (UCB-1)。在这篇博客中,我们将分别讨论这些 MAB 算法,然后比较它们之间的行为以及不同实验条件下的 A/B 测试设置。

算法细节

(在本节中,我们将使用一种更简单的模拟设置来演示算法行为,这种模拟设置将在下面详细解释:低差双变量模拟。)

ε贪心:

Epsilon Greedy,顾名思义,是三种 MAB 算法中最贪婪的。在ε贪婪实验中,常数ε(值在 0 和 1 之间)由用户在实验开始前选择。当将联系人分配到活动的不同变量时,随机选择的变量被选择ε次。其余的 1-ε时间,选择已知收益最高的变量。ε越大,该算法越有利于探索。对于我们所有的例子,ε设置为 0.1。

模拟结果如图 1 所示。较细的轨迹来自 10 次随机选择的模拟,较粗的线是 1000 次模拟的平均结果。左图显示了两个变量之间的联系分配,右图显示了模拟期间每个变量的实际转换率。

Fig 1. Example results from a 2-variant Epsilon Greedy simulation (winning variant conversion rate: 0.024, losing variant conversion rate: 0.023). Left, contact allocation to each variant; Right, average conversion rate for each variant.

有趣的是,虽然平均而言,该算法为获胜的变体分配了更多的联系人,但对于单个模拟来说,分配结果可能会与平均行为相差很大。由于两个变体之间的微小差异,以及我们最初分配的少量联系,失败的变体在实验开始时确实偶尔显示出较高的转化率。由于该算法是如此“贪婪”,最初“失败”的变体没有被充分探索以使该算法收集足够的数据来学习实验期间任一变体的真实转化率。这也体现在单个模拟的转换率图的噪声中,即使在 40,000 个数据点(分配的接触)后,也没有完全收敛到真实平均值。

汤普森采样

相比之下,汤普森抽样是一种更有原则的方法,它可以在边际情况下产生更平衡的结果。对于每一个变体,我们使用观察到的结果建立一个真实成功率的概率分布(出于计算的原因,最常见的是一个贝塔分布)。对于每个新联系人,我们从对应于每个变体的 beta 分布中采样一个可能的成功率,并将该联系人分配给具有最大采样成功率的变体。我们观察到的数据点越多,我们对真实的成功率就越有信心,因此,随着我们收集到更多的数据,抽样成功率将越来越有可能接近真实的成功率。

Fig 2. Probability density distribution of beta distributions for two individual simulations at different stages of the experiment. Inset figures: contact allocation trajectories for each variant.

默认情况下,每个 beta 分布的参数(𝛂和𝛃)被初始化为 1 和 1,导致所有变体的 beta 分布相同,表示没有偏差(因为我们没有证据表明哪个变体可能是赢家)。随着我们收集更多的数据,分布会随着最新的结果更新(𝛂= n 被转换,𝛃 = n 未被转换),概率密度将开始围绕平均收益聚集。数据点数越多,概率密度函数越窄(见图 2)。如果对每个变体应该如何表现有很强的先验信念,我们可以在实验开始前改变𝛂和𝛃来代表这个先验分布。

下图(图 3)是我们对 Thompson 采样的 2 变量模拟的平均结果。与 Epsilon Greedy(图 1)类似,在实验的早期阶段,Thompson 采样的单个模拟与平均行为相差甚远。然而,在实验的后期,个体模拟的行为变得更加一致,与平均结果更加相似。

Fig 3. Example results from a 2-variant Thompson Sampling simulation (winning variant conv rate: 0.024, losing variant conv rate: 0.023). Left, contact allocation to each variant; Right, avg conversion rate for each variant.

为了更直观地了解这一行为,我们在实验的不同阶段绘制了两个随机选择的模拟的平均概率密度分布(pdf )(图 2)以及每个模拟的接触分配(插图)。在这两种情况下,从实验开始的 pdf 开始广泛和高度重叠,并且随着附加数据点,最终在实验结束时变得相当好地分离。正如预期的那样,我们可以通过查看两臂的 PDF 有多少重叠,以及哪一个具有更高的平均转化率(PDF 的峰值),来跟踪接触在实验的每个阶段是如何分配的。

UCB 一号:

对于活动的每个变体,我们将确定一个置信上限(UCB ),它代表我们对该变体的可能收益的最高猜测。该算法会将联系人分配给具有最高 UCB 的变量。

每个变量的 UCB 是根据变量的平均收益以及分配给变量的联系数计算的,公式如下:

如等式所示,变量的 UCB 分数随着进入该变量的联系人数量的增加而降低,因此,即使特定变量在平均回报中胜出,UCB 也可能低于回报稍低的不太好探索的变量。因此,该算法能够在探索不熟悉的选项和利用获胜的变体之间找到平衡。

下面(图 4)再次是使用 UCB-1 的双变量模拟(0.024 对 0.023 转换率)的结果。UCB-1 的表现非常类似于比例分配(图 3),并且比ε贪婪(图 1)保守得多。在实验的初始阶段(前 30,000 个数据点),算法基本上是纯探索,在两臂之间分配几乎相等数量的接触。像比例分配,UCB-1 的结果从个人模拟是非常稳定的,并类似于平均结果。这再次展示了这样一个事实,即该算法能够在单个实验中平衡探索和利用,优化联系分配以发现真正的获胜变体,然后利用它。

Fig 4. Example results from a 2-variant UCB-1 simulation (winning variant conv rate: 0.024, losing variant conv rate: 0.023). Left, contact allocation to each variant; Right, avg conversion rate for each variant.

模拟设置

既然我们对每种算法的行为有了一个概念,我们可以将它们的行为与原始的 A/B 测试设置进行比较。我们通过设置以下 5 个模拟来做到这一点。

Table 1. Conversion rates used in simulations

前 3 个模拟都包含 5 个变量,每个变量都具有下面列出的固定转换率。为了衡量这些算法在现实情况下的表现,我们在每个模拟中设置了变量的真实转化率,以匹配典型电子邮件营销活动用例中的真实转化率。此外,我们运行了 2 个额外的双变量版本的转换率模拟。表 1 列出了模拟中使用的所有转换率。

对于所有模拟,我们进行初始分配步骤,将 200 个联系人平均分配给所有 5 个变量(因为没有先前的结果)。之后,对于每个分配步骤,我们向实验中添加额外的 200 个联系人,根据特定的 MAB 算法进行分配。对于双变量模拟、开放率模拟和高差异转换模拟,我们分别运行了总共 400 步,重复 1000 次。对于低差分转换模拟,我们做了 1000 步和 2000 次重复,以更好地评估算法的行为。在所有情况下,我们对支付的定义都是二进制转换,我们比较的变量是转换率。如果我们使用收入等其他支出定义,结果不会有显著变化。对于所有ε贪婪模拟,ε设置为 0.10。

对于每个分配步骤,我们跟踪联系分配、总体表现和统计显著性(2 个变量的 2 比例 z 检验的 p 值,以及 3 个以上变量的卡方偶然检验)。

模拟结果:

  • 联系人分配:

对于支出差异较小的模拟(图 5、图 6),UCB-1 最保守地分配联系(最类似于平均分配),因为变量性能的差异非常小。与 UCB-1 相比,Thompson Sampling 和 Epsilon Greedy 更加贪婪,为获胜的变体分配了两倍的联系数量。对于低转换 5 变量模拟,在实验的前三分之一,Thompson 采样比 Epsilon Greedy 稍保守,但最终赶上了。

Fig 5. Average contact allocation from Low-Difference Conversion Rate Simulation

Fig 6. Average contact allocation from 2-variant Low-Difference Simulation

对于正常打开率(图 7)和高差异(图 8、9)转换率模拟,UCB-1 算法仍然是最保守的算法,而汤普森采样算法是最贪婪的算法。有趣的是,汤普森取样在这些模拟中比在低差模拟中表现得更贪婪。这展示了 Thompson Sampling 在勘探和开发之间进行平衡的能力,在变量之间具有清晰、易于检测的支出差异的情况下,有利于后者。在三个例子中的一个例子中,在模拟结束时,Thompson Sampling 分配给获胜变体的联系是 UCB-1 的近两倍。

Fig 7. Average contact allocation from open rate simulation

Fig 8. Average contact allocations from high-difference conversion rate simulations

Fig 9. Average contact allocations from high-difference two-variant simulations

  • 总支出:

正如预期的那样,在所有模拟中,所有三种算法都获得了比 A/B 测试设置更高的总体支出,贪婪的算法表现出更好的性能。由于我们预定义的ε为 0.1,Epsilon Greedy 在最困难的情况下(低转换 5 变量)表现优于其他算法。在所有其他四种情况下,Epsilon Greedy 在实验的早期阶段获得了更高的支出,但 Thompson Sampling 始终超过 Epsilon Greedy,随着收集的数据点越来越多(如分配结果所示),并获得了更高的最终总支出。这再一次展示了在实验的早期阶段,面对不确定性,Thompson 采样如何支持探索,而在实验的后期阶段,仍然能够切换到支持开发。

Fig 10. Overall payout of low-difference conversion rate simulations

Fig 11. Overall payout rate for two-variant low-difference simulations

Fig 12. Overall payout for open-rate simulation

Fig 13. Overall payout for high-difference conversion rate simulation

Fig 14. Overall payout for high-difference two-variant simulation

  • 统计显著性:

对于所有的模拟,UCB-1 能够达到统计显著性(p <0.05) around the same time (total number of contacts added) as the A/B test set up, despite allocating more contacts to the winning variants and therefore having many fewer data points for the losing variants. Thompson Sampling, interestingly, showed faster reduction of p-value in the early phases of the experiment, but slows down, and eventually falls behind UCB-1 and A/B test simulations when p-values are between 0.1 and 0.05. This is likely due to the fact that Thompson sampling gets increasingly greedier as the beta distributions become more separated (which typically results in lower p-value). Epsilon greedy, however, did suffer in all cases from not having enough data in losing variants, and did not reach statistical significance until twice the number of data points had been observed. For the low-difference conversion rate simulations (Fig 15), the Epsilon Greedy algorithm could not reach statistical significance even by the end of the experiment.

Fig 15. Statistical significance for low-difference conversion simulations

Fig 16. Statistical significance for low-difference 2-variant simulations

For simulations with high difference, all algorithms reached statistical significance within the duration of the experiments. Similar to the results above, UCB-1 and A/B test reached p <0.05 with the same amount of data, with Thompson Sampling trailing slightly behind, and Epsilon Greedy took the most data points to reach statistical significance.

Fig 17. Statistical significance for open rate simulations

Fig 18. Statistical significance for high-difference conversion simulations

Fig 19. Statistical significance for high-difference 2-variant simulations

)

根据这些模拟的结果,我们证明了当需要一个随机控制的实验时,MAB 算法将总是提供一个比 A/B 测试更有利可图的选择。算法的具体选择取决于用户想要优先考虑利润还是数据收集,以及实验的估计大小和持续时间。

在极少数情况下,已知被测试的变量之间的支出差异相当大,所有算法将能够在几个数据点内显示变量之间的统计显著差异。在这种情况下,Thompson 采样或贪婪的 Epsilon 贪婪算法成为最大化支出的压倒性赢家。

Thompson Sampling 和 UCB-1 都能够优化所有情况下的总支出,同时不牺牲对所有变量的探索,并检测它们之间的统计差异。UCB-1 将产生更类似于 A/B 测试的分配,而汤普森则更适合于最大化长期总体回报。与 Thompson 采样相比,UCB-1 在每个单独的实验中表现得更一致,Thompson 采样由于算法中的随机采样步骤而经历更多的噪声。与 Thompson 采样和 Epsilon Greedy(ε= 0.1)相比,UCB-1 在变体性能方面有很小的差异,这是我们过去看到的典型 A/B 测试结果。当ε设置过低时,Greedy 可以在最佳情况下捕获最大值,但算法的行为也会变得相当不稳定,容易被观察到的转换中的噪声淹没。

总之,鉴于其一致性和对数据中噪声的高容忍度,UCB-1 是 MAB 算法在低基础转换率、小效应规模情况下的正确选择。在基线转换率较高或预期效应大小较高的情况下,Thompson 采样可能是更好的选择,此时算法会更稳定。

比较模型的性能

原文:https://towardsdatascience.com/comparing-the-performance-of-models-69433e1d603a?source=collection_archive---------1-----------------------

最近在 Quora 上看到下面这个问题。以前有人问过我同样的问题,我认为用它来写博客是有意义的,原因有两个,它能接触到更多的人,内容变得可重复使用,它激励我创建一个可以传播知识的博客。

How can I show that small improvement in F-score is statistically significant?

这不是构建假设的最佳方式。

更好的方法是形成一个假设,并进行假设检验。

你从一个假设开始,你正在尝试的两种方法有相同的 F 值,任何变化都是由于世界上固有的随机性。我们称之为无效假设。

现在你只能说你的进步是显著的,如果你能以预先定义的置信度拒绝这个假设,例如 95%的置信度意味着 p 值为 0.05。

一旦你准备好这个设置,你需要得到实际的样本来测试你的假设。一个简单的方法是进行 k 倍交叉验证,以获得两种方法的 k 个样本,并使用这些样本进行 t 检验。

另一种方法是再次使用 k 倍交叉验证来获得 k 个样本。然后,假设 k 个样本来自正态分布,您可以使用生成的样本来估计其超参数。现在,你可以从这个后验样本中抽取 100 次,看看任何时候一个比另一个表现得更好[我需要和我的顾问澄清这部分]

除了 k 倍交叉验证,你还可以尝试 bootstrap 抽样。

免责声明:只有在严格比较两种方法时,才能使用这种方法。如果你已经尝试了其他方法,你需要应用 Bonferroni 修正或其他修正,因为你在测试多重假设。

注意:这个练习的全部目的是推理你的 F 分数的变化实际上是否显著。

注意:我要感谢 Bruno Ribeiro 的数据挖掘课,我从他的课上获得了上述知识。我的错误或者不理解,可能并不归咎于他。

非监督与监督学习方法在自然语言处理文本分类中的性能比较

原文:https://towardsdatascience.com/comparing-the-performance-of-non-supervised-vs-supervised-learning-methods-for-nlp-text-805a9c019b82?source=collection_archive---------11-----------------------

将分类性能与 scikit-learn 文档中现有的 NLP 指标基线进行比较。Gal Arav M.Sc

笔记

1.本文中使用的术语的定义包含在末尾。

2.附带的 python 笔记本托管在 Google Colab:https://Colab . research . Google . com/github/gal-a/blog/blob/master/docs/notebooks/NLP/NLP _ TF-IDF _ clustering . ipynb

3.这是我在关于数据科学的第一篇文章,我期待任何反馈或问题,我的博客在:http://gal-a.com/

输入数据

输入数据是从 scikit learn library 中的 4 个互联网新闻组中提取的。下面显示的是这些新闻组中的热门话题的单词云,强调了每个新闻组中最受欢迎的话题。

无神论新闻组词云:

基督教新闻组单词云:

医学新闻组单词云:

图形新闻组单词云:

动机

我们的目标是准确地对文本文档进行分类,并将无监督学习方法的性能指标与现有的有监督学习方法的性能指标进行比较。

我们将比较以下非监督方法的性能指标:K-means、NMF 和 LDA 方法与这些监督方法:多项式朴素贝叶斯和线性支持向量机(SVM)方法。

scikit learn 的文档中详细介绍了监督方法的性能指标,我们将它们用作基准参考。

为了确保我们能够相互比较,我们将在这里分析的完全相同的测试数据集上评估性能指标:
http://sci kit-learn . org/stable/tutorial/text _ analytics/working _ with _ text _ data . html

如果我们知道有监督的方法会比无监督的方法更好,为什么还要为无监督的方法烦恼呢?这是因为在许多情况下,我们面临着没有完全标记的数据集的情况。标注大型数据集既昂贵又耗时,因此,在对文本标注项目投入资源之前,通过找到相似文档的聚类来获得我们研究的初始角度是有用的。例如,如果我们对文本数据的性质只有一个模糊的概念,通过非监督学习方法找到的“有趣”的类簇可以用来帮助我们决定在随后的监督学习项目中关注哪些类标签。半监督学习方法,即使用一个大的未标记数据集来扩充一个小的标记数据集,可以显著提高分类精度。

预处理

在我们可以应用机器学习方法之前,我们需要“清理”文本,具体来说,我们的目标是:删除标点符号删除琐碎的“停用词”,应用一些基本的语言学方法,如词干分析器词法分析器算法,并限制词性(POS) 。在这个简短的研究中,我们尝试只保留文本中的名词。

斯特梅尔算法的工作原理是切断单词的结尾或开头,同时考虑到一系列常见的前缀和后缀,这些前缀和后缀可以在一个屈折的单词中找到。

Lemmatizer 算法更复杂,通过使用详细的字典来考虑单词的形态分析,算法可以浏览这些字典以将形式链接回其 lemma。

下面是从原句中提取的 3 个名词示例列表。这里有些问题需要更复杂的预处理,但它们足够精确,我们可以继续处理(如前所示,单词 clouds 很好地反映了 4 个新闻组中的每一个)。原文句子:

注意,nltk 的缺省 POS 实现远非完美,还有各种其他的 POS 实现可以尝试(例如 HunPos、Stanford POS、Senna)。在这里我们看到,对于上面的句子,单词“bite”被错误地归类为名词,尽管它实际上是“bite”(一个动词)的过去分词。

机器学习模型需要输入数据的数字表示,因此我们需要在应用学习模型之前将文本转换为数字格式。用于将文本文档转换为数字表示的最流行的方法被称为 TF-IDF‘词频-逆文档频率’,尽管它的名字很长,但它非常容易理解。简而言之,TF-IDF 是一个数字表,表示一个术语在一个特定文档中出现的频率,通过它在语料库中所有文档中出现的频率来标准化。

请注意,“词频”概念通常被称为“词汇包”。当处理相关文本文档的大型语料库时,通常我们想要做的第一件事是创建这些文档中最流行的单词或短语的摘要。然后,可以通过术语频率或加权术语频率(当它们出现在 TF-IDF 矩阵中时,通过文档计数标准化)从术语的排序列表中创建“热门话题”

一旦预处理完成,绘制标记化文档长度的直方图是很有帮助的。

标记化文档长度的直方图:

注意,最后一个箱代表接近 40 个计数的尖峰(在文档长度= 1200 处),因为该值用于最大文档长度。

下面是可以在附带的笔记本中运行的预处理步骤的摘要(使用 sklearn 库):

(I)从文档构建计数矢量器并拟合文档

(ii)从文档中建立 TF(术语频率),这是词袋的稀疏版本

㈢分两步建立词汇袋:适应、转换

㈣获得特征名称,并建立词汇袋的数据框架版本

(v)使用 TfidfTransformer 将单词包转换成 TF-IDF 矩阵(术语频率-逆文档频率)

(vi)找出最流行的词和最高权重

(vii)建立单词权重列表并对其进行排序

(viii)计算所有文档与其自身的余弦相似度

(ix)计算文档的距离矩阵

基于 K-均值聚类的 PCA 降维方法

K-means 是最著名的聚类算法。这是一种无监督的学习方法,因为学习不是基于标记的类。

给定一组观察值(x1,x2,…,xn),其中每个观察值是一个 d 维实向量,k-means 聚类旨在将 n 个观察值划分为 k (≤ n)个集合 S = {S1,S2,…,Sk},以便最小化组内平方和(WCSS)(即最小化方差)。

它可以在语料库中的所有文档中生成最流行主题的良好分割。

虽然 K-means 可以直接处理任意数量的特征,但它通常在负责减少特征数量的单独预处理步骤之后使用,例如流行的 PCA 方法(主成分分析)。sklearn.decomposition 中有一个很好的 PCA 实现。

用线性代数术语来说,PCA 可以通过数据协方差(或相关性)矩阵的特征值分解来完成。得到的特征向量对应于数据分散的不同方向。特征值代表这些特征向量的相对重要性。PCA 允许我们根据相应特征值的有序排序,丢弃相对不重要的特征向量。

在统计学术语中,主成分解释的方差分数是该主成分的方差与总方差(所有单个主成分的方差之和)之比。为了估计需要多少分量来描述数据,我们可以检查作为分量数量(按特征值大小排序)的函数的累积解释方差比,并仅选择与解释方差的特定百分比(例如 90%)相对应的顶部分量。

在我们的例子中,由于我们想要创建一个 2 维的可视化(见下文),我们丢弃了除了对应于两个最大特征值的两个最重要的特征向量之外的所有特征向量,这两个剩余的特征作为 K-means 算法的输入——注意,仅基于两个特征的分析通常不包含 90%的解释方差!

K 均值模型与实际类别的 PCA 特征:

  • K-means 导致所有数据点的清晰分割(左侧)。
  • 当然,K-Means 模型导致了一些分类错误,如右图所示(对于具有实际类别标签的相同数据点),尽管我们仍然可以非常清楚地看到 4 个聚类。
  • 请注意,一般来说,由于我们通常不知道数据中的聚类数,我们需要使用一种技术来估计这一点,例如流行的肘方法:
    https://en . Wikipedia . org/wiki/Determining _ the _ number _ of _ clusters _ in _ a _ data _ set
  • 中心的维数是:(n 个簇,n 个特征)。
  • PCA 作为 K-均值之前的初始步骤运行,这产生了上面的二维表示(n_features = 2)。

NMF 和 LDA 聚类

NMF(非负矩阵分解)和 LDA(潜在狄利克雷分配)聚类都使用“单词包”矩阵作为输入,即与每个文档相关联的术语频率。然后,两种算法将原始单词包矩阵分成两个独立的单词包:

  1. 文档到主题矩阵
  2. 主题矩阵的术语

使用最小误差方法,这两个导出矩阵的乘积尽可能接近等于原始项频率。

虽然 K-means 可以提供良好的整体分割,但使用替代聚类方法(如 NMF 和 LDA)的主要优势在于,这些方法提供了对每个主题聚类中排名靠前的文档的额外洞察。注意,所有这三种算法都要求用户指定主题的数量作为输入参数。

注意,NMF 需要 TF-IDF 矩阵作为输入,而 LDA 只需要单词包(TF 表)作为输入。如何使用这些算法的例子可以在附带的 python 笔记本中找到。

结果

首先,为无监督学习方法计算调整后的 Rand 指数调整后的互信息得分(参见本文末尾的定义)。这些是测量两个赋值的相似性的函数,忽略置换和机会归一化:

基于调整后的 Rand 指数和调整后的互信息得分,NMF 和 LDA 方法都优于 K-means。

在这项研究中,LDA 算法取得了最好的结果(下面显示的 sklearn.metrics 表的列定义包含在“文本处理定义”部分的末尾)。

与监督方法的最佳结果(线性 SVM 获得的)相比:

考虑到我们使用的是无监督学习方法,这还不错!

摘要

  • 在没有“偷看”类别标签的情况下,我们能够使用 LDA 进行无监督学习,在平均精确度、召回率和 f1 分数方面获得略高于 0.8 的分数。相比之下,对于在实际类别标签上训练的监督学习方法,使用线性支持向量机(SVM)分类器,得分仅超过 0.9。
  • 总的来说,所有的算法在挑选“comp.graphics”新闻组的能力方面都比较好(例如,这个新闻组与其他新闻组最少混淆)。
  • 正如我们所料,最大的困惑发生在类似的“另类无神论”和“社会宗教基督教”新闻组之间。

附带的 python 笔记本使用 python 的 sklearn 库演示了 NLP(自然语言处理)环境中的以下技术:

  • 使用 TF-IDF(术语频率-逆文档频率)将文本矢量化为数字矩阵
  • 利用主成分分析进行维数约简
  • 无监督分类:基于 PCA(TF-IDF 的简化版本)计算 K 均值聚类
  • 非监督分类:基于 TF-IDF 计算 NMF
  • 无监督分类:基于 TF 计算 LDA(潜在导数分析)

下一步将是对这些算法的参数化进行网格搜索(这可能会导致更好的分类),并在更大更大胆的数据集上释放它们。

文本处理定义(在本文中使用)

常规:

文集:某一特定主题的书面文本的集合。

文档/样本/观察:语料库中的单词序列。

术语/单词/短语/特征/ n 元语法:来自给定文本(或语音)样本的 n 个项目的连续序列。n 元语法可以有不同的长度,例如一元语法、二元语法、三元语法。

术语簇:话题。相关术语的分类。

文档簇:文档中具有共同主题的主题混合。

预处理:

TF-IDF(术语频率-逆文档频率):术语相对于整个语料库对文档的重要性的统计度量。实际上,这种方法通过一个术语在语料库中出现的频率来标准化它在文档中出现的频率。

TF-IDF 样本:语料库中的单个文档,TF-IDF 矩阵中的行。

TF-IDF 特征:这些都是语料库中包含的唯一 n 元文法,即 TF-IDF 矩阵中的列。

PCA (主成分分析):一种流行的降维统计方法,即降低特征空间的维度。它使用正交变换将一组可能相关的变量样本转换为一组称为主成分的线性不相关变量的值。

监督学习方法:

多项式朴素贝叶斯:基于应用贝叶斯定理的概率分类器家族,在特征之间具有强(朴素)独立性假设。多项式分布用于对每个特征建模,因为这适合于可以容易地转化为字数的文本数据。这是一种用于文本分类的流行的监督学习方法,尽管它有“天真”的假设,但表现良好。

支持向量机(SVM) :通过寻找最大化类间距离余量的超平面来执行分类。这是一种流行的用于文本分类的监督学习方法,尽管实现比上面的朴素贝叶斯方法慢。

监督学习绩效指标:

精度:tp/(TP+fp)的比值,其中 TP 为真阳性的数量,FP 为假阳性的数量。精确度直观上是分类器不将阴性样品标记为阳性的能力。

回想一下:比值 tp / (tp + fn)其中 tp 是真阳性的数量,fn 是假阴性的数量。召回直观上是分类器找到所有肯定样本的能力。

F1 得分:这可以解释为精度和召回率的加权平均值,其中 F1 得分在 1 时达到最佳值,在 0 时达到最差得分。在多类别和多标签的情况下,这是每个类别的 F1 分数的加权平均值。

F1 得分= 2 (精确度召回率)/(精确度+召回率)

支持:目标类向量中每个类出现的次数。

无监督学习方法:

K-means :一种流行的聚类方法,用于将一组样本划分为 K 个簇。

NMF(非负矩阵分解):一种线性代数方法,将一个非负矩阵分解成两个与话题分布相关的非负矩阵 W 和 H。

LDA(潜在狄利克雷分配):一种贝叶斯推理方法,通过使用狄利克雷分布建立每文档主题模型和每主题术语模型。

无监督学习性能指标:

注意:在监督学习中,我们获得基础真实类向量和预测类向量之间的直接映射,而在非监督学习中,我们无法预测绝对类 id,而是获得相对类 id 的预测。例如,包含 3 个不同类别标签{0,1,2}的基本事实的样本可能导致标记为{0,1,2}或{0,2,1}或{1,0,2}或{1,2,0}或{2,0,1}或{2,1,0}的 6 个不同排列中的任何一个的预测分配,并且我们对相对于基本事实的“最佳”排列的度量的质量感兴趣。

调整后的互信息得分:互信息是衡量两个赋值的一致性的函数,忽略排列。这种度量有两种不同的标准化版本,标准化互信息(NMI)和调整互信息(AMI)。NMI 经常在文献中使用,而 AMI 是最近才提出来的,并根据概率进行标准化。

调整后的 Rand 指数:调整后的 Rand 指数是一个函数,它测量两个赋值的相似性,忽略置换并进行机会归一化。

有关这些指标的详细信息,请参见 sk learn:http://sci kit-learn . org/stable/modules/clustering . html # clustering-performance-evaluation

印度软件开发商与美国、英国、德国和整个世界的比较

原文:https://towardsdatascience.com/comparison-of-software-developers-in-india-with-us-uk-germany-and-the-entire-world-8d2a1ba3218a?source=collection_archive---------5-----------------------

我分析了“堆栈溢出调查”( Stack Overflow survey )( T1 ),发现在薪水、年轻人、对新工具的兴趣、对人工智能的看法、道德等方面形成了鲜明的对比..

Photo by arihant daga on Unsplash

历史上,印度开发人员的工资只是发达国家同行的一小部分。关于这些国家的开发者如何感受到威胁(或者说 T2 没有受到威胁)因为廉价外包给像印度这样的国家而失去潜在的工作机会,已经有很多报道了。

当我看到印度在 2018 年的堆栈溢出调查中拥有第二大响应者数量,并且该调查有一个关于响应者工资的问题时,我决定亲自看看印度开发人员实际上有多便宜。

于是,我画了一些 KDE 图对比印度开发者的年薪和全世界开发者的年薪:

KDE Plots for annual salary of developers in India (left) and annual salary of developers all over the world (right)

然后,我为调查中其他三个回复最多的国家,即美国、德国和英国,绘制了类似的图表:

KDE plots for annual salary of developers in United States, Germany and United Kingdom

看到印度开发商的 KDE 地块价格在 0 美元附近大幅上涨了吗?那是我决定彻底钻研这项调查的时候。我想探索是什么让印度开发者如此与众不同。

本次调查(按降序排列)收到最多回复的前 5 个国家是:

  • 美国
  • 印度
  • 德国
  • 联合王国
  • 加拿大

因此,我为调查中的每个问题列出了所有回应者的总体统计数据,然后我将其与仅来自印度的回应者的相应统计数据进行了比较。为了使对比更加明显,我还展示了其他 3 个回复最多的国家——美国、英国和德国。

以下是我的发现:

  • 大多数来自印度的开发人员的工资比其他发达国家的同行低几个数量级。
  • 在编写代码时,没有多少印度人考虑伦理问题。
  • 与整个世界或其他前三大响应国家相比,印度拥有 CS 相关领域大学专业的开发人员比例更高。
  • 与其他群体相比,印度的开发人员要年轻得多。他们很有竞争力,也很有野心。
  • 移动开发在印度有着独特的吸引力。
  • 大多数印度人对所有新的假设工具感兴趣。
  • 印度开发人员对人工智能的看法与世界其他地区有很大不同——我们更担心工作的自动化,对人工智能做出重要决策感到兴奋,更担心一种奇异类型的情况,而不是世界,并且不太担心算法与人类决策中“公平”定义的演变。
  • 许多印度人对开源软件贡献良多,但不认为他们从这样做中学到了什么。

进一步阅读这篇文章,看看可视化和了解更多关于这些结果或检查我对 Kaggle 的完整报告 ( ,如果你喜欢的话请投票支持)。

我最近才开始接触数据科学,我不确定我在分析中采用的方法是否是最好的(甚至是正确的)。所以,我真的很感激一些关于我做得对/错的反馈/批评😄。

关于薪水的更多信息:

Salary stats — count, mean and the 25th, 50th, 75th and 90th percentiles — for various groups

The quartiles for various groups

  • 印度开发人员的平均年薪几乎是全球所有开发人员平均年薪的 2.5 倍,几乎是美国平均薪资的 5 倍,几乎是英国平均薪资的 4 倍,几乎是德国平均薪资的 3 倍。
  • 超过 50%的印度开发人员年薪不到 1 万美元。
  • 全球所有开发人员的年薪中值大约是印度的 3.5 倍。美国的这一比例是英国的 10 倍,德国的 6 倍。

注意 我在上面的情节中只包括了有工作的响应者。我还没算过零工资的。]

那么,印度开发人员的工资如此之低是因为他们在 CS 相关领域的教育程度低于其他国家的同行吗?

教育:

Highest level of education of the responders

  • 事实上,印度拥有学士学位的受访者比例高于其他国家。
  • 与除德国之外的其他群体相比,拥有硕士学位的应答者比例也较高。
  • 印度拥有博士学位(如哲学博士)的应答者比例要小得多。

让我们看看他们主修什么..

Undergrad majors of responders

  • 与其他国家和整个世界相比,印度计算机科学专业学生的比例更高。

嗯,我们可以肯定的是,并不是缺少一个正式的计算机科学学位让印度的开发人员找不到高薪工作。

那什么是?

也许更大比例的印度开发者并不是出于兴趣而编码,而仅仅是为了挣工资。这可能导致他们产生没有多大价值的次等代码。我们来看看是不是这样..

Does the responder code as a hobby?

没有。印度开发者和其他开发者群体一样对编码感兴趣。

但即便如此,很大一部分廉价外包工作是由来自印度的开发者完成的,这是一个现实。那么,为什么如此大比例的印度开发者被挡在高薪职位之外呢?

我认为,生活条件、教育质量和国家经济结构的综合因素阻碍了印度程序员甚至与美国、英国和德国等更发达国家的程序员竞争的能力。

除此之外,你还能如何解释尽管计算机专业学生的比例很高,但工资却很低?

年龄和经历

Age of responders

Number of years the responder has been coding professionally

  • 约 93%的印度受访者年龄在 18-34 岁之间,而全世界的这一数字为 73%,其他 3 个回复最多的国家甚至更少。
  • 如果我们计算拥有 0-5 年专业编程能力的开发人员的百分比,印度超过 78%,而美国约为 48%,英国约为 46%,德国约为 55%,全世界合起来约为 57%。

很明显,印度的开发人员非常年轻。

我认为,印度和这里考虑的其他群体之间在开发人员年龄和经验上的巨大差异背后的一个主要原因是,作为一个发展中国家,印度接触计算技术的时间比其他国家晚得多。这导致印度在 20 世纪甚至 21 世纪初产生了非常少的程序员。

这可能导致印度人主要贡献于公司的年轻劳动力,主要从事初级工作,这些工作的报酬可能低于更有经验的职位。这也可能是印度开发人员平均工资低的一个因素。

这也意味着印度初级开发人员的就业市场异常拥挤,这可能解释了为什么印度开发人员在本质上特别有竞争力..

竞争性

Does the responder feel like he/she is competing with his/her peers

  • 超过 50%的印度开发者认为他们正在与同行竞争,另外 25%的开发者对此持中立态度。

伦理学

该调查有几个与编码道德相关的问题。

不道德准则的责任:

Who do the responders believe is responsible for unethical code

  • 虽然所有其他组中几乎 60%的开发人员认为高层管理人员应对不道德的代码负责,但在印度只有 40%的开发人员这样认为。
  • 与其他群体的开发人员相比,大部分印度开发人员会认为提出不道德代码想法的人或编写代码的开发人员负有责任。

写不道德的代码?

Would the responder write code that he/she considers extremely unethical

  • 大多数印度开发者准备写他们自己认为不道德的代码。

考虑你的代码的道德含义?

Does the responder believe that he/she has an obligation to consider the ethical implication of the code that he/she writes

  • 只有大约 63%的印度回答者认为他们有义务考虑他们编写的代码的道德含义。

当你从事低薪工作时,考虑道德问题可能有点困难。

想象一下,身处一个失业率高、工资低、就业市场竞争激烈的国家。然后,想象一下,你被要求在你现有的工作中编写一份不道德的代码,或者冒着让你的老板不高兴的风险,并且失去下一次晋升的机会,而你的同事同意这么做,或者甚至冒着丢掉工作的风险!

所以我相信,如果一个开发人员同意编写不道德的代码,并不一定意味着他/她是一个不道德的人。有时候,这就像是在偷面包和让家人挨饿之间做出选择一样进退两难。正确的选择并不总是非黑即白。

移动应用开发的流行程度:

Developer types of the responders

  • 我们可以看到,代表印度的橙色条在“移动开发者”类别中非常突出。

事实上,印度拥有全世界最多的移动开发者:

Top 50 countries with the maximum number of mobile developers

大多数印度人可能已经完全跳过了个人电脑革命,转而将智能手机作为他们的第一个个人计算设备。因此,智能手机和移动应用令人兴奋不已。我认为这是我们在印度看到的对移动应用程序开发的兴趣背后的主要原因。

我们还可以看到移动开发的流行对印度开发者选择开发平台的影响..

平台

Platform popularity based on the percentage of responders who have worked with it (blue line) and those who desire to work on it next year (orange line); Click on each image in the collage to view an enlarged version

  • 安卓Firebase 是印度最受欢迎的三大平台之一。在印度开发者中,这两个平台的受欢迎程度已经超过了 Windows 桌面或服务器。
  • 而且,对它们的兴趣仍在增长,因为更大比例的开发人员希望明年开发它们!
  • 注意 Firebase 在上面看到的除印度之外的任何其他组中甚至都没有接近前 3。

关于人工智能的看法

该调查有几个与先进的人工智能技术相关的问题。其中两个是:

  • 你认为日益先进的 AI 技术最危险的一面是什么?
  • 你认为日益先进的 AI 技术最令人兴奋的地方是什么?

他们两人有相同的选择,即。

  • 做出重要决策的算法
  • 超越人类智能的人工智能(《奇点》)
  • 算法与人类决策中“公平”定义的演变
  • 提高工作的自动化程度

因此,我为上述每个选项绘制了单独的图表,试图了解大多数响应者是将其视为危险,还是将其视为兴奋的理由:

Opinions about AI; click on each image in the collage to enlarge it

  • 人工智能可能带来的工作自动化对我们印度人来说并不那么有趣。我们更有可能把它放进危险的“桶”里。这与世界其他地区以及其他 3 个回复最多的国家不同,那里有更大比例的人口对此感兴趣。
  • 印度人对人工智能为他们做重要决定的未来比其他 3 个国家或整个世界都更放心。
  • 印度人比其他群体更担心“奇点”式的情况。
  • 印度人不像其他群体那样担心算法与人类决策中不断演变的“公平”定义。

对假设工具的兴趣

Stack Overflow 想知道开发人员社区对哪些新的假想工具感兴趣。它要求受访者从 1 到 5 对四种假设工具进行评级,其中 1 表示“非常感兴趣”,5 表示“完全不感兴趣”。

我发现,与世界各地的开发人员或其他前 3 名响应国家的开发人员相比,印度开发人员对 Stack Overflow 团队提出的任何新的假设工具更感兴趣。

Levels of interest of the responders in new hypothetical tools; click on each image in the collage to enlarge in

我想,也许这种对所有新的假设工具的兴趣是我们之前看到的印度开发者社区年轻时代的结果。因此,我根据他们多年的编码经验对所有的开发人员进行了分组,并计算了每个经验组中属于不同兴趣水平的开发人员的百分比。

以下是代表这些百分比的热图:

很明显,一个人编码的年数和他/她对新的假设工具的兴趣之间有很强的相关性。 年轻的开发人员往往比年长的开发人员对新的假设工具更感兴趣。

这种反差对于工具——**"A private area for people new to programming"**(第一排,第二图)来说最为醒目。很明显,刚接触编程的年轻开发人员想要一个新手的私人空间。

也许吧,其原因是现有的栈溢出可以将和*为* 新人 。然而,老开发人员对这个工具非常不感兴趣。**

也许是它的 知识的诅咒 。或者可能更有经验的开发人员认为现有的工具已经足够好了,因为他们已经在现有的工具上创造了事业。或者,不知何故,他们在该领域的经验对这种工具的成功持悲观态度。

无论是什么原因,很明显,如果 Stack Overflow 团队决定继续开发上述任何产品,年轻人更有可能欢迎它,印度可能是一个特别好的国家,可以尝试推出。

开源贡献的奇特之处在于:

Does the responder contribute to open-source software

  • 在这项分析中考虑的所有群体中,印度拥有最大比例的开源贡献者。

现在,有趣的部分来了..

调查中还有另一个问题——

“您使用过或参加过以下哪种非学历教育?请选择所有适用的选项。”

这个问题的选项之一是— “促成了开源项目”

我认为为开源软件做贡献不仅能帮助社区,还能帮助贡献者的个人成长。因此,做出贡献的人也会在非学位教育下注明。

但事实证明并非如此..

  • 虽然大约 50%的印度受访者对开源软件做出了贡献,但只有大约 30%的受访者认为他们从这样做中学到了一些东西。这与考虑在内的整个世界和其他国家形成对比,在这些国家,这两个比率几乎相同。

我不知道这是怎么回事。也许这意味着许多为开源项目做贡献的印度人并不经常这样做。让我知道你的想法。

结论

在这个分析中,我们看到了印度的软件开发人员与其他国家的软件开发人员不同的各个方面。

印度在计算领域起步较晚,正如我们在上面看到的老年开发人员比例较低所证明的那样,但软件开发在当代已经变得非常流行。在印度近代史的大部分时间里,从事信息技术和软件开发已经成为摆脱贫困最可靠的手段。

我们也看到这里的开发者意识到了上一次重大科技革命的潜力——移动应用。这让我对我们正在快速赶上充满希望。也许,我们甚至会在定义下一次重大技术革命中发挥关键作用(人工智能中的,也许是)。

你应该看看我对 Kaggle 的完整分析,了解更多关于印度开发者在自学方法、IDE 使用、编程方法、各种语言和框架的流行程度、广告拦截器的使用、操作系统、堆栈溢出使用等方面的差异。 另外,如果你觉得有趣的话,请投上一票或者评论一下

我最近才开始接触数据科学,我不确定我在分析中采用的方法是否是最好的(甚至是正确的)。所以,我真的很感激一些关于我做得对/错的反馈/批评😄。

其他值得探索的想法

Jason Goodman 建议,在构建数据科学项目时,选择你感兴趣的东西。如果它是你感兴趣的东西,它会更有趣,你更有可能从中找到有趣的角度。

我试图通过分析堆栈溢出调查数据来找出来自我的国家的开发人员与其他人的不同之处,从而将这一理念带回家。

做这个冗长的分析也让我对其他一些问题感到好奇。所以,我在这里公布了我想问这个数据集的所有问题—

  • 人们通过参与开源项目来学习吗? 我料定人确实如此。但事实证明,并不是所有人都这样(就像我们在这个分析中看到的 20%的印度人)。所以,深入研究这个会很有趣。
  • 关于 AI 的观点有哪些不同?在这个内核中,我们看到了印度人对推进人工智能技术的各个方面有着不同的看法。根据年龄、开发人员工作、教育、框架、语言、薪水等在其他组中进行比较会很有趣。
  • 学生们是怎么想的?
    本次调查的每 5 名回答者中就有 1 名是全日制学生,这意味着我们有大约 20,000 名学生的调查数据。我认为这提供了一个独特的机会来探索下一代软件开发人员中流行的东西(我自己也包括:D)。
  • Vim 用户和非 Vim 用户有什么不同? 这只是因为我用了 Vim。我爱维姆。制作类似于这种优秀的 R vs. Python 分析会很有趣。
  • 一个薪资预测器 Stack Overflow 最近推出了一个薪资计算器使用的正是这个数据集。尝试构建一个类似的东西可能会很有趣。

令人兴奋的是,通过分析这个数据集,有可能得出所有这些问题的满意解决方案!

我计划自己探索其中的一些话题。但是如果你觉得上面的问题有意思的话,你可以随意进行你自己的分析。另外,如果你觉得这个分析很有趣,你可以为你自己的国家做一个类似的分析。

如果你(/如果你)决定公开你的作品,你可以在评论中给我加标签,或者在 Twitter 上给我发推文,或者在 LinkedIn 上给我发短信,那就太棒了。

一定要看看我在 Kaggle 上的完整分析——“印度开发者如何与众不同”。如果你觉得我的分析有趣,如果你能投票支持这个内核,我会非常感激。

我正在数据科学和机器学习领域寻找机会。你可以在 LinkedIn 或者 Twitter 联系我。你可以在nityeshagarwal[at]gmail[dot]com给我发邮件。

还有,你可以在 Twitter 上关注我;我不会给你发垃圾邮件;-)

2018 年最佳 NSFW 图像审核 API 对比

原文:https://towardsdatascience.com/comparison-of-the-best-nsfw-image-moderation-apis-2018-84be8da65303?source=collection_archive---------6-----------------------

多个图像内容过滤 API 提供商的全面基准,涵盖不同类别,如裸体、色情和血腥。

人类可以本能地决定他们所看到的是否不合适。然而,当谈到拥有一个可以决定一个图像或视频是否不合适的全视人工智能时,这个问题还远远没有解决。现在,许多公司都在争先恐后地应用自动化技术来识别一种媒体是可以安全传播还是被清除。

我想亲自了解一下,在自动检测公认的 NSFW 内容方面,最先进的技术是什么。我将基于以下类别对 API 性能进行比较

  • 露骨的裸体
  • 暗示性的裸体
  • 色情/性行为
  • 模拟/动画色情
  • 血腥/暴力

Tl;DR: 如果你只是对找出最好的 API 感兴趣,你可以直接跳到文章末尾的总体比较。

实验设置:

数据集:为了进行评估,我创建了一个自定义的 NSFW 数据集,它与每个 nsfw 子类别具有相同的权重。该数据集由 120 幅图像组成,其中 20 幅 nsfw 阳性图像用于上述五个类别中的每一个,20 幅 sfw 图像。我决定不使用开源的 YACVID 180 图像数据集,因为它主要依赖于使用裸体作为 NSFW 内容的衡量标准。

收集 NSFW 图像是一项单调乏味、费时费力的工作,因此图像数量很少。

数据集已经开源,可以在这里下载 。【警告:包含露骨内容】

这里的 是一张包含数据集中每个图像上的 API 的原始预测的表。

指标:

每个分类器都根据普遍接受的指标进行评估,例如:

真阳性:TP

如果一个分类器称某物为 NSFW,而它实际上是 NSFW

真阴性:TN

如果一个分类器称某物为 SFW,而它实际上是 SFW

误报:FP

如果一个分类器称某物为 NSFW,而它实际上是 SFW

假阴性:FN

如果一个分类器称某物为 SFW,而它实际上是 NSFW

准确(性)

如果模型做出预测,你能相信它吗?

精确

如果模型说某个图像是 NSFW,那么它的正确率是多少?

回忆

如果所有的 NSFW 图像,它能识别多少?

F1 分数

它是精确性和回忆性的混合体,通常类似于准确性。

我评估了以下用于内容审核的 API

  • 亚马逊识别
  • T5【谷歌】T6
  • 微软
  • 雅虎
  • 算法 ia
  • 克拉里菲
  • 迪派
  • Imagga
  • 纳米网
  • 视觉引擎
  • X-版主

跨类别的绩效

我首先按类别评估了每个 API,看看它们在检测不同类型的 NSFW 内容时表现如何。

色情/性行为

Google 和 Sightengine API 在这里大放异彩,因为它们是唯一能够正确检测所有色情图片的 API。Nanonets 和 Algorithmia 紧随其后,因为它们能够正确分类 90%的色情图像。微软和 Imagga 在这个类别上的表现最差。

原始图片链接: Porn19 , Porn7 , Porn18 , Porn14

容易识别的图像明显是色情的。所有的供应商都得到了正确的图片。他们中的大多数人都非常有把握地预测了 NSFW 的内容。

原图链接: 色情 6 ,色情 2 ,色情 10 ,色情 3

难以识别的图像是由于遮挡或模糊造成的。在最糟糕的情况下,11/12 的供应商弄错了图像。色情在性能上有很大的差异,这取决于色情的强度和色情内容的清晰程度。

露骨的裸体

大多数 API 在这一类别中表现出色,其中许多具有 100%的检测率。即使是性能最低的 API(clari fai 和 Algorithmia)在这里也有 90%的检测率。什么被认为是裸体的定义一直是争论的主题,从难以识别的图像中可以清楚地看出,在人们可以争论这些是 SFW 的情况下,它们大多失败了。

原始图片链接: 裸露 10 ,裸露 7 ,裸露 13 ,裸露 14

易于识别的图像具有清晰可见的裸露,并且是清晰的。任何没有不同意见的人都会把这些叫做 NSFW。没有一个提供者出错,平均分数都是 0.99。

原始图片链接:

原图链接: 裸体 9 ,裸体 8 ,裸体 18 ,裸体 4

引起争论的图像是那些提供者弄错的图像。这可能只是每个供应商对裸体的敏感度有不同的设置。

暗示性的裸体

谷歌在这方面再次领先,对这一类别的检测率为 100%。Sightengine 和 Nanonets 的表现优于其他产品,检测率分别为 95%和 90%。对于机器来说,暗示性的裸体几乎和裸体一样容易识别,但它出错的地方是在通常看起来像 SFW 图像但有一些裸体的图像中。

原始图片链接: 建议 13 ,建议 10 ,建议 2 ,建议 8

再一次,没有一个提供者得到容易识别的错误图像。这些图像显然都是 NSFW 的。

原图链接: 建议 17 ,建议 12 ,建议 11 ,建议 5

在暗示性的裸体中,提供者分裂了。类似于赤裸裸的裸露,他们都有不同的容忍界限。我个人不确定这些图片是否应该是 SFW。

模拟/动画色情

所有的 API 在这里都表现得非常好,能够准确地检测出 100%的模拟色情例子。唯一的例外是 IMAGGA,它漏掉了一幅图像。有趣的是,几乎所有的供应商都表现得很好。这表明这些算法发现识别人工生成的图像比自然生成的图像更容易。

原图链接: 模拟炮 1 ,模拟炮 16 ,模拟炮 19 ,模拟炮 9

所有的提供者都有完美的分数和高的置信度分数。

原始图像链接: 模拟图 15

如果你看得不够久的话,Imagga 弄错的一张图片可能会被解释为不是色情图片。

流出的血

这是最困难的类别之一,因为 API 的平均检出率低于 50%。Clarifai 和 Sightengine 的表现优于其竞争对手,能够识别 100%的戈尔图像。

原图链接: Gore2 , Gore3 , Gore6 , Gore10

所有供应商都有高门槛的是医学图像,可能是因为它们更容易找到。然而,即使在表现最好的图像中,4/12 的提供者也弄错了图像。

原图链接: Gore7 , Gore9 , Gore17 , Gore18

图像中没有难以预测的明显模式。然而,人类会很容易发现这些图像中的任何一个是血腥的。这可能意味着表现不佳的原因是缺乏可用的训练数据。

工作安全

安全的工作是不应该被识别为 NSFW 的图像。收集一个安全的工作数据集本身是困难的,它应该靠近 NSFW,以了解这些供应商做的事情。如果所有这些图片都是 SFW,那么会有很多争论。在这里,Sightengine 和 Google 是表现最差的,这也解释了他们在其他类别中的出色表现。他们基本上把任何东西都叫做 NSFW。Imagga 在这里做得很好,因为他们不叫什么 NSFW。X-Moderator 在这里也做得很好。

原图链接: SFW15 , SFW12 , SFW6 , SFW4

易于识别的图像只有很少的皮肤显示,对于人来说很容易识别为 SFW。只有一两个供应商弄错了这些图像。

原图链接: SFW17 , SFW18 , SFW10 , SFW3

难以识别的 SFW 图片都有大量的皮肤展示或者是动漫(高度偏向于色情动漫)。大多数供应商得到的图片都带有大量显示为 SFW 的 sking。这就引出了一个问题,如果这些真的是 SFW?

总体比较

查看 API 在所有 NSFW 类别中的性能,以及它们在能够正确识别工作安全(SFW)内容方面的性能,我发现 Nanonets 具有最好的 F1 分数和平均准确性,因此在所有类别中表现一致。谷歌在检测 NSFW 类别方面做得非常好,它标记了太多的 SFW 内容,因为 NSFW 因此在 F1 得分中受到了惩罚。

按提供商

我根据准确性和 F1 分数比较了前 5 名供应商,以展示他们在表现上的差异。雷达图的面积越大越好。

1.纳米网络

Nanonets 在任何一个类别中的表现都不是最好的。然而,最平衡的总体情况是在每个类别中都做得很好。它可以做得更好的地方是将更多的图像识别为 SFW。对任何皮肤都过度敏感。

2.谷歌

谷歌在大多数 NSFW 类别中表现最佳,但在检测 SFW 方面表现最差。需要注意的一点是,我找到的图片来自谷歌,这意味着他们“应该知道”我使用的图片是什么。这可能是大多数类别中真正好的表现的原因。

3.克拉里菲

Clarifai 确实在识别戈尔方面大放异彩,并且比大多数其他 API 做得更好,它再次得到了很好的平衡,并且在大多数类别中都做得很好。它缺乏识别暗示性的裸体和色情。

4.x-慢化剂

X-Moderator 是另一个平衡的 API。除了识别戈尔外,它还能很好地识别大多数其他类型的 NSFW 内容。它在 SFW 中获得了 100%的准确性,这使它从竞争对手中脱颖而出。

5.视觉引擎

像谷歌这样的视觉引擎在识别 NSFW 内容方面有着近乎完美的表现。然而,它没有识别出一个单一的戈尔图像。

定价

决定使用哪种 API 的另一个标准是定价。下面是每个供应商的价格比较。大多数 API 都有免费试用版,但使用量有限。雅虎是唯一一个完全免费使用的网站,但它是自主托管的,因此不在此表中。

亚马逊,微软,Nanonets,DeepAI 都是最低的,每月 1000 万美元的 API 调用。

哪一个是最好的内容审核 API?

NSFW 内容的主观性使得很难将任何一个 API 声明为内容审核的首选 API。

更适合内容分发并希望有一个平衡的分类器的一般社交媒体应用程序更倾向于使用 Nanonets API,其分类器的最高 F1 分数证明了这一点。

一个针对儿童的应用程序肯定会犯谨慎的错误,甚至会喜欢隐藏边缘的不适当内容,因此他们会更喜欢使用谷歌 API,其在所有 NSFW 类别上的出色性能会冒着过滤掉一些适当内容的风险。这样做的代价是会失去很多谷歌可能宣布为 NSFW 的 SFW 内容。

NSFW 到底是什么?

在这个问题上花了大量时间后,我意识到的一个关键问题是,NSFW 到底是什么还不清楚。每个人都有自己的定义,你认为你的服务向用户展示什么是好的,这在很大程度上取决于服务提供了什么。约会应用程序中的部分裸体可能没问题,但血液则不然,在医学杂志中,情况正好相反。真正的灰色地带是暗示性的裸体,在那里不可能得到正确的答案。

关联规则完全指南(2/2)

原文:https://towardsdatascience.com/complete-guide-to-association-rules-2-2-c92072b56c84?source=collection_archive---------4-----------------------

帮助您更快、更智能购物的算法

在这篇博客中,我将讨论从交易列表中高效提取关联规则的算法。本博客的第 1 部分涵盖了构成关联规则挖掘基础的术语和概念。这整个概念背后的动机和一些基本术语的含义在那里得到了解释。以下是上一部分中一些术语的简要定义。

  1. 关联规则:例如。{X → Y}是在有 X 的篮子上寻找 Y 的表示
  2. 项目集:例如。{X,Y}是构成关联规则的所有项目的列表的表示
  3. Support:包含该项集的事务部分
  4. 置信度:给定{X},出现{Y}的概率存在
  5. lift:置信度与{Y}发生的基线概率之比

现在我们已经熟悉了这些术语,让我们继续从交易列表中提取规则。这个过程的第一步是获得在我们的事务集中至少出现一次的所有项目的列表。

挑战在于从大量的关联规则中挖掘出重要的规则,这些关联规则可以从项目列表中导出。

请记住,规则生成是一个两步过程。第一个是生成一个项目集,如{面包,鸡蛋,牛奶},第二个是从每个项目集生成一个规则,如{面包→鸡蛋,牛奶},{面包,鸡蛋→牛奶}等。下面将讨论这两个步骤。

1.从项目列表生成项目集

产生关联规则的第一步是获得所有的频繁项目集,在这些项目集上可以执行二进制划分以获得前件和后件。例如,如果所有交易组合起来有 6 个项目{面包、黄油、鸡蛋、牛奶、笔记本、牙刷},项目集将看起来像{面包}、{黄油}、{面包、笔记本}、{牛奶、牙刷}、{牛奶、鸡蛋、蔬菜}等。项目集的大小可以从一个项目到我们拥有的项目总数不等。现在,我们只从中寻找频繁项集,而不是全部,以便检查生成的总项集的数量。

频繁项集是那些在事务中至少出现最小次数的项集。从技术上讲,这些项目集的支持值(包含该项目集的事务部分)高于最小阈值 minsup

因此,如果{Bread,Notebook}在 100 个事务中只出现 2 次,并且(2/100) = 0.02 低于 minsup 的值,则它可能不是频繁项集。

寻找频繁项目集的强力方法是形成所有可能的项目集,并检查每个项目集的支持值。 先验原则 有助于使这种搜索高效 它表明

频繁项目集的所有子集也必须是频繁的。

这相当于说包含项目{面包,鸡蛋} 的交易数大于或等于包含{面包,鸡蛋,蔬菜} 的交易数。如果后一种情况发生在 30 笔交易中,前一种情况在所有 30 笔交易中都发生,并且可能会在更多的交易中发生。所以如果{面包,鸡蛋,蔬菜}的支持值即(30/100) = 0.3 高于 minsup,那么我们可以确定{面包,鸡蛋}的支持值即(> 30/100) = > 0.3 也高于 minsup。这被称为支持度的反单调性质,其中如果我们从一个项目集中删除一个项目,生成的新项目集的支持度值要么相同,要么增加。

先验原则是支持度反单调性质的结果。

Apriori 原则允许我们删除不满足支持度最小阈值条件的项目集的所有超集。例如,如果{Milk,Notebook}不满足我们的 minsup 阈值,则添加了任何项的项集也不会超过该阈值。

产生的方法论被称为 apriori 算法。 涉及的步骤有:

Ref: https://annalyzin.files.wordpress.com/2016/04/association-rules-apriori-tutorial-explanation.gif

生成只有一个项目的所有频繁项目集(支持≥ minsup)。接下来,生成长度为 2 的项集,作为上述项集的所有可能组合。然后,删除那些支持值低于最小支持值的。

现在,将长度为 3 的项集生成为长度为 2 的项集的所有可能组合(修剪后保留下来),并对支持值执行相同的检查。

我们像这样不断增加项目集的长度,并在每一步检查阈值。

从图中可以看出,修剪非频繁项集可以将需要考虑的项集数量减少一半以上!随着项目数量的增加,计算能力降低的比例变得越来越显著。

这个比例还取决于我们选取的最小支持阈值(minsup ),它完全是对当前问题的主观判断,可以基于过去的经验。

2.从频繁项目集生成所有可能的规则

一旦产生了频繁项集,从中找出规则相对来说就不那么费力了。规则是通过对每个项目集进行二进制划分形成的。如果{面包,鸡蛋,牛奶,黄油}是频繁项目集,候选规则将如下所示:

(鸡蛋,牛奶,黄油→面包),(面包,牛奶,黄油→鸡蛋),(面包,鸡蛋→牛奶,黄油),(鸡蛋,牛奶→面包,黄油),(黄油→面包,鸡蛋,牛奶)

从所有可能的候选规则列表中,我们的目标是识别出低于最低置信水平 (minconf)的规则。和支持度的反单调性质一样,从同一个项目集 生成的规则 置信度也遵循一个反单调性质。它相对于结果项中的元素数是反单调的。**

这意味着(A,B,C→ D) ≥ (B,C → A,D) ≥ (C → A,B,D)的置信度。提醒一下,对{X → Y}的信心=支持{X,Y }/支持{X}

正如我们所知,从相同项目集生成的所有规则的支持度保持不变,差异仅发生在置信度的分母计算中。随着 X 中项目数量的减少,支持度{X}增加(根据支持度的反单调性质),因此置信度值减少。

对上述内容的直观解释如下。考虑 F1 和 F2:

F1 =既有(黄油)又有(鸡蛋、牛奶、面包)的交易比例

将会有很多有黄油的交易,而鸡蛋、牛奶和面包这三者将只能在其中的一小部分中找到位置。

F2 =有(牛奶、黄油、面包)也有(鸡蛋)的交易的比例

牛奶、黄油和面包三者都有(相比之下只有黄油)的交易将会很少,而且很有可能会有鸡蛋。

所以会观察到 F1 < F2. Using this property of confidence, pruning is done in a similar way as was done while looking for frequent itemsets. It is illustrated in the figure below.

Ref: https://www-users.cs.umn.edu/~kumar001/dmbook/ch6.pdf

We start with a frequent itemset {a,b,c,d} and start forming rules with just one consequent. Remove the rules failing to satisfy the minconf condition. Now, start forming rules using a combination of consequents from the remaining ones. Keep repeating until only one item is left on antecedent. This process has to be done for all frequent itemsets.

Here again, minimum confidence threshold that we pick up is completely subjective to the problem at hand.

With these two steps, we have identified a set of association rules which satisfy both the minimum support and minimum confidence condition. The number of such rules obtained will vary with the values of minsupminconf 。现在,可以搜索这样生成的规则子集,以获得 lift 的最高值,从而做出业务决策。R 中有一些构建良好的库,通过将 minsup 和 minconf 作为参数放入事务中来获取关联规则。它们还提供了可视化的能力。本文这里用代码一步一步的讨论整个过程。

在结束之前,我将引入两个术语,最大频繁项集和闭频繁项集,它们是所有频繁项集的紧凑表示。

最大频繁项集: 它是一个其直接超集都不频繁的频繁项集。这就像一个频繁项集 X,其中不能添加任何项 y,因此{X,y}仍然高于 minsup 阈值。

Different cases for itemset {Bread, Butter}

闭频繁项集 : 它是一个不存在与项集具有相同支持度的超集的频繁项集。考虑一个项目集 X,如果 X 的所有出现都伴随着 Y 的出现,那么 X 不是闭集。(例如参考下图)

最大频繁项目集是有价值的,因为它们是频繁项目集的最紧凑的表示形式。

所有的频繁项集都可以导出为最大频繁项集的子集。然而,关于子集支持的信息丢失了。如果该值是必需的,则闭频繁项集是表示所有频繁项集的另一种方式。

封闭项集有助于删除一些冗余项集,同时不会丢失有关支持值的信息。

从封闭项集计算非封闭项集的支持值是另一种算法,其细节不在本文讨论范围之内。

我试图通过这篇博客涵盖所有与关联规则挖掘相关的重要术语和概念,并在必要时详细介绍。以下是该过程中引入几个术语一行摘要——

  1. 关联规则挖掘:(1)项目集生成,(2)规则生成
  2. 先验原则:频繁项目集的所有子集也必须是频繁的
  3. Apriori 算法:剪枝以有效获取所有频繁项集
  4. 最大频繁项目集:没有一个直接超集是频繁的
  5. 封闭频繁项集:没有一个直接超集具有相同的支持值

我希望你喜欢读这篇文章,并且比以前有更清晰的思路。如果您有兴趣阅读更多详细信息,请参考该资源。通过评论让我知道你的想法/问题。

如果你还没有阅读第一篇博客中的介绍,请在这里阅读!

使用 TensorFlow 和 Flask RESTful Python API 构建基于 ConvNet HTTP 的应用程序的完整指南

原文:https://towardsdatascience.com/complete-guide-to-build-convnet-http-based-application-using-tensorflow-and-flask-restful-python-c6326b13878b?source=collection_archive---------10-----------------------

本教程将带您了解使用 TensorFlow 创建卷积神经网络(CNN/ConvNet)所需的步骤,并通过允许使用 Flask RESTful API 通过基于 HTTP 的应用程序进行远程访问,将其投入生产。

该教程在 2018 年 5 月 15 日至 20 日期间在 KDnuggets.com 最常分享的帖子中排名第二。它获得了银质徽章。

在本教程中,CNN 将使用 TensorFlow NN (tf.nn)模块构建。CNN 模型架构是针对 CIFAR10 数据集创建、训练和测试的。为了使模型可以远程访问,使用 Python 创建了一个 Flask Web 应用程序来接收上传的图像,并使用 HTTP 返回其分类标签。在有 CPU 支持的 Windows 上,除了 TensorFlow 之外,还使用 Anaconda3。本教程假设你对 CNN 有一个基本的了解,比如层、步幅和填充。还需要关于 Python 的知识。

本教程总结为以下步骤:

1.通过安装 Python、TensorFlow、PyCharm 和 Flask API 来准备环境。

2.下载和准备 CIFAR-10 数据集。

3.用张量流建立 CNN 计算图。

4.训练 CNN。

5.保存训练好的 CNN 模型。

6.准备测试数据并恢复训练好的 CNN 模型。

7.测试训练好的 CNN 模型。

8.构建 Flask Web 应用程序。

9.使用 HTML 表单上传图像。

10.创建辅助 HTML、JavaScript 和 CSS 文件。

11.基于 HTTP 远程访问训练好的模型进行预测。

1。安装 Python、TensorFlow、PyCharm 和 Flask API

在开始构建项目之前,需要准备它的环境。Python 是第一个开始安装的工具,因为环境完全依赖于它。如果您已经准备好了环境,可以跳过第一步。

1.1 Anaconda/Python 安装

可以安装原生 Python 发行版,但是建议使用一体化包,比如 Anaconda,因为它会为您做一些事情。在这个项目中,使用了 Anaconda 3。对于 Windows,可执行文件可以从 https://www.anaconda.com/download/#windows 的下载。它很容易安装。

为了确保 Anaconda3 安装正确,可以发出 CMD 命令(,其中 python ),如图 1 所示。如果 Anaconda3 安装正确,它的安装路径将出现在命令输出中。

Figure 1

1.2 张量流安装

使用 Anaconda3 安装 Python 后,接下来就是安装 TensorFlow (TF)。本教程在有 CPU 支持的 Windows 上使用 TF。安装说明见本页https://www.tensorflow.org/install/install_windows。这段 YouTube 视频可能会有所帮助(【https://youtu.be/MsONo20MRVU】T2)。

TF 安装步骤如下:

1)通过调用该命令为 TF 创建一个 conda 环境:

C:> conda create -n tensorflow pip python=3.5

这将创建一个空文件夹来存放 TF 安装的虚拟环境(venv)。venv 位于此位置的 Anaconda3 安装目录下(\Anaconda3\envs\tensorflow)。

使用此命令激活 TensorFlow 安装的 venv:

C:> activate tensorflow

上面的命令告诉我们,我们在 venv 内部,任何库安装都将在其中。在此命令之后,命令提示符应更改为(tensorflow)C:>。进入目录后,我们准备安装库。

3)激活 venv 后,可以通过发出以下命令安装 Windows TensorFlow 的纯 CPU 版本:

**(tensorflow)C:> pip install --ignore-installed --upgrade tensorflow**

为了测试 TF 是否安装正确,我们可以尝试如图 2 所示导入它。但是记住在导入 TF 之前,必须激活它的 venv。从 CMD 测试它,我们需要发出 python 命令,以便能够与 python 交互。因为导入行中没有出现错误,所以 TF 安装成功。

Figure 2

1.3 PyCharm Python IDE 安装

对于这个项目,建议使用 Python IDE,而不是在 CMD 中输入命令。本教程中使用的 IDE 是 PyCharm。它的 Windows 可执行文件可以从这个页面下载https://www.jetbrains.com/pycharm/download/#section=windows。它的安装说明非常简单。

下载安装 PyCharm Python IDE 后,接下来就是和 TF 链接了。这是通过将其 Python 解释器设置为 TF venv 下安装的 Python 来实现的,如图 3 所示。这是通过打开 IDE 的设置并选择安装在 TF venv 中的python.exe文件的项目解释器来完成的。

Figure 3

1.4 烧瓶安装

最后要安装的工具是 Flask RESTful API。它是一个要使用 pip/conda 安装程序在 TF venv 下使用以下 CMD 命令安装的库:

C:> pip install Flask-API

如果 NumPy 和 SciPy 尚未安装,应该安装在 venv 中,以便能够读取和操作图像。

通过安装 Anaconda (Python)、TensorFlow、PyCharm 和 Flask,我们已经准备好开始构建项目了。

2。下载和准备 CIFAR-10 数据集

10 类 CIFAR 数据集(CIFAR-10)的 Python 版本可以从 https://www.cs.toronto.edu/~kriz/cifar.html的这个页面下载。该数据集包含 60,000 幅图像,分为训练和测试数据。有五个保存训练数据的文件,其中每个文件有 10,000 个图像。这些图像是大小为 32x32x3 的 RGB。训练文件被命名为 data_batch_1data_batch_2 等等。有一个保存测试数据的文件,名为 test_batch ,包含 10,000 个图像。有一个名为 batches.meta 的元数据文件,保存着数据集类标签,分别是飞机汽车鹿青蛙卡车

因为数据集中的每个文件都是二进制文件,所以应该对其进行解码,以便检索实际的图像数据。为此,创建了一个名为 unpickle_patch 的函数来完成此类工作,定义如下:

def unpickle_patch(file):
"""
Decoding the binary file.
:param file:File to decode it data.
:return: Dictionary of the file holding details including input data and output labels.
"""
patch_bin_file = open(file, 'rb')#Reading the binary file.
patch_dict = pickle.load(patch_bin_file, encoding='bytes')#Loading the details of the binary file into a dictionary.
return patch_dict#Returning the dictionary.

该方法接受二进制文件名,并返回一个包含该文件详细信息的字典。除了标签之外,字典还保存文件中所有 10,000 个样本的数据。

为了解码整个训练数据,创建了一个名为 get_dataset_images 的新函数。该函数接受数据集路径,并且只对定型数据起作用。因此,它会过滤该路径下的文件,并仅返回以 data_batch_ 开头的文件。测试数据是在构建和训练 CNN 之后准备的。

对于每个训练文件,通过调用 unpickle_patch 函数对其进行解码。基于此类函数返回的字典,get_dataset_images 函数返回图像数据及其类标签。从‘数据’键检索图像数据,从‘标签’键检索图像的类别标签。

因为图像数据保存为 1D 矢量,它应该被整形为三维。这是因为张量流接受这种形状的图像。因此,get_dataset_images 函数除了接受每个图像中的通道数之外,还接受行数/列数作为参数。

该功能的实现如下:

def get_dataset_images(dataset_path, im_dim=32, num_channels=3):
    """
    This function accepts the dataset path, reads the data, and returns it after being reshaped to match the requierments of the CNN.
    :param dataset_path:Path of the CIFAR10 dataset binary files.
    :param im_dim:Number of rows and columns in each image. The image is expected to be rectangular.
    :param num_channels:Number of color channels in the image.
    :return:Returns the input data after being reshaped and output labels.
    """
    num_files = 5#Number of training binary files in the CIFAR10 dataset.
    images_per_file = 10000#Number of samples withing each binary file.
    files_names = os.listdir(patches_dir)#Listing the binary files in the dataset path.
    """
    Creating an empty array to hold the entire training data after being reshaped.
    The dataset has 5 binary files holding the data. Each binary file has 10,000 samples. Total number of samples in the dataset is 5*10,000=50,000.
    Each sample has a total of 3,072 pixels. These pixels are reshaped to form a RGB image of shape 32x32x3.
    Finally, the entire dataset has 50,000 samples and each sample of shape 32x32x3 (50,000x32x32x3).
    """
    dataset_array = numpy.zeros(shape=(num_files * images_per_file, im_dim, im_dim, num_channels))
    #Creating an empty array to hold the labels of each input sample. Its size is 50,000 to hold the label of each sample in the dataset.
    dataset_labels = numpy.zeros(shape=(num_files * images_per_file), dtype=numpy.uint8)
    index = 0#Index variable to count number of training binary files being processed.
    for file_name in files_names:
        """
        Because the CIFAR10 directory does not only contain the desired training files and has some  other files, it is required to filter the required files.
        Training files start by 'data_batch_' which is used to test whether the file is for training or not.
        """
        if file_name[0:len(file_name) - 1] == "data_batch_":
            print("Working on : ", file_name)
            """
            Appending the path of the binary files to the name of the current file.
            Then the complete path of the binary file is used to decoded the file and return the actual pixels values.
            """
            data_dict = unpickle_patch(dataset_path+file_name)
            """
            Returning the data using its key 'data' in the dictionary.
            Character b is used before the key to tell it is binary string.
            """
            images_data = data_dict[b"data"]
            #Reshaping all samples in the current binary file to be of 32x32x3 shape.
            images_data_reshaped = numpy.reshape(images_data, newshape=(len(images_data), im_dim, im_dim, num_channels))
            #Appending the data of the current file after being reshaped.
            dataset_array[index * images_per_file:(index + 1) * images_per_file, :, :, :] = images_data_reshaped
            #Appening the labels of the current file.
            dataset_labels[index * images_per_file:(index + 1) * images_per_file] = data_dict[b"labels"]
            index = index + 1#Incrementing the counter of the processed training files by 1 to accept new file.
    return dataset_array, dataset_labels#Returning the training input data and output labels.

通过准备训练数据,我们可以使用 TF 建立和训练 CNN 模型。

3。使用 TensorFlow 构建 CNN 计算图

CNN 的计算图是在一个名为 create_CNN 的函数中创建的。它创建卷积(conv)、ReLU、最大池化、下降和完全连接(FC)图层的堆栈,并返回最后一个完全连接图层的结果。每一层的输出都是下一层的输入。这要求相邻层的输出和输入的大小一致。请注意,对于每个 conv、ReLU 和 max 池层,有一些参数需要指定,如每个维度的跨度和填充。

def create_CNN(input_data, num_classes, keep_prop):
"""
Builds the CNN architecture by stacking conv, relu, pool, dropout, and fully connected layers.
:param input_data:patch data to be processed.
:param num_classes:Number of classes in the dataset. It helps determining the number of outputs in the last fully connected layer.
:param keep_prop:probability of dropping neurons in the dropout layer.
:return: last fully connected layer.
"""
#Preparing the first convolution layer.
filters1, conv_layer1 = create_conv_layer(input_data=input_data, filter_size=5, num_filters=4)
"""
Applying ReLU activation function over the conv layer output.
It returns a new array of the same shape as the input array.
"""
relu_layer1 = tensorflow.nn.relu(conv_layer1)
print("Size of relu1 result : ", relu_layer1.shape)
"""
Max pooling is applied to the ReLU layer result to achieve translation invariance.
It returns a new array of a different shape from the the input array relative to the strides and kernel size used.
"""
max_pooling_layer1 = tensorflow.nn.max_pool(value=relu_layer1, ksize=[1, 2, 2, 1], strides=[1, 1, 1, 1], padding="VALID")
print("Size of maxpool1 result : ", max_pooling_layer1.shape)#Similar to the previous conv-relu-pool layers, new layers are just stacked to complete the CNN architecture.
#Conv layer with 3 filters and each filter is of sisze of 5x5.
filters2, conv_layer2 = create_conv_layer(input_data=max_pooling_layer1, filter_size=7, num_filters=3)
relu_layer2 = tensorflow.nn.relu(conv_layer2)
print("Size of relu2 result : ", relu_layer2.shape)
max_pooling_layer2 = tensorflow.nn.max_pool(value=relu_layer2, ksize=[1, 2, 2, 1], strides=[1, 1, 1, 1], padding="VALID")
print("Size of maxpool2 result : ", max_pooling_layer2.shape)#Conv layer with 2 filters and a filter sisze of 5x5.
filters3, conv_layer3 = create_conv_layer(input_data=max_pooling_layer2, filter_size=5, num_filters=2)
relu_layer3 = tensorflow.nn.relu(conv_layer3)
print("Size of relu3 result : ", relu_layer3.shape)
max_pooling_layer3 = tensorflow.nn.max_pool(value=relu_layer3, ksize=[1, 2, 2, 1], strides=[1, 1, 1, 1], padding="VALID")
print("Size of maxpool3 result : ", max_pooling_layer3.shape)#Adding dropout layer before the fully connected layers to avoid overfitting.
flattened_layer = dropout_flatten_layer(previous_layer=max_pooling_layer3, keep_prop=keep_prop)#First fully connected (FC) layer. It accepts the result of the dropout layer after being flattened (1D).
fc_resultl = fc_layer(flattened_layer=flattened_layer, num_inputs=flattened_layer.get_shape()[1:].num_elements(),
num_outputs=200)
#Second fully connected layer accepting the output of the previous fully connected layer. Number of outputs is equal to the number of dataset classes.
fc_result2 = fc_layer(flattened_layer=fc_resultl, num_inputs=fc_resultl.get_shape()[1:].num_elements(), num_outputs=num_classes)
print("Fully connected layer results : ", fc_result2)
return fc_result2#Returning the result of the last FC layer.

因为卷积层在输入数据和所用的一组过滤器之间应用卷积运算,所以 create_CNN 函数接受输入数据作为输入参数。get_dataset_images 函数返回的就是这样的数据。卷积图层是使用创建 conv 图层函数创建的。create_conv_layer 函数接受输入数据、过滤器大小和过滤器数量,并返回输入数据与过滤器集合的卷积结果。这组滤波器根据输入图像的深度来设置它们的大小。创建 conv 层的定义如下:

def create_conv_layer(input_data, filter_size, num_filters):"""Builds the CNN convolution (conv) layer.:param input_data:patch data to be processed.:param filter_size:#Number of rows and columns of each filter. It is expected to have a rectangular filter.:param num_filters:Number of filters.:return:The last fully connected layer of the network.""""""Preparing the filters of the conv layer by specifiying its shape.Number of channels in both input image and each filter must match.Because number of channels is specified in the shape of the input image as the last value, index of -1 works fine."""filters = tensorflow.Variable(tensorflow.truncated_normal(shape=(filter_size, filter_size, tensorflow.cast(input_data.shape[-1], dtype=tensorflow.int32), num_filters),stddev=0.05))print("Size of conv filters bank : ", filters.shape)"""Building the convolution layer by specifying the input data, filters, strides along each of the 4 dimensions, and the padding.Padding value of 'VALID' means the some borders of the input image will be lost in the result based on the filter size."""conv_layer = tensorflow.nn.conv2d(input=input_data,filter=filters,strides=[1, 1, 1, 1],padding="VALID")print("Size of conv result : ", conv_layer.shape)return filters, conv_layer#Returing the filters and the convolution layer result.

另一个论点是神经元留在脱落层的概率。它指定有多少神经元被丢弃层丢弃。dropout 层是使用 dropout_flatten_layer 函数实现的,如下所示。该函数返回一个展平的数组,该数组将作为完全连接的层的输入。

def dropout_flatten_layer(previous_layer, keep_prop):"""Applying the dropout layer.:param previous_layer: Result of the previous layer to the dropout layer.:param keep_prop: Probability of keeping neurons.:return: flattened array."""dropout = tensorflow.nn.dropout(x=previous_layer, keep_prob=keep_prop)num_features = dropout.get_shape()[1:].num_elements()layer = tensorflow.reshape(previous_layer, shape=(-1, num_features))#Flattening the results.return layer

因为最后一个 FC 层的输出神经元的数量应该等于数据集类的数量,所以数据集类的数量被用作 create_CNN 函数的另一个输入参数。使用 fc_layer 函数创建完全连接的层。该函数接受丢弃层的展平结果、展平结果中的特征数量以及 FC 层的输出神经元数量。根据输入和输出的数量,创建一个权重张量,然后乘以展平层,以获得 FC 层的返回结果。

def fc_layer(flattened_layer, num_inputs, num_outputs):"""uilds a fully connected (FC) layer.:param flattened_layer: Previous layer after being flattened.:param num_inputs: Number of inputs in the previous layer.:param num_outputs: Number of outputs to be returned in such FC layer.:return:"""#Preparing the set of weights for the FC layer. It depends on the number of inputs and number of outputs.fc_weights = tensorflow.Variable(tensorflow.truncated_normal(shape=(num_inputs, num_outputs),stddev=0.05))#Matrix multiplication between the flattened array and the set of weights.fc_resultl = tensorflow.matmul(flattened_layer, fc_weights)return fc_resultl#Output of the FC layer (result of matrix multiplication).

使用 TensorBoard 可视化后的计算图如图 4 所示。

Figure 4

4。训练 CNN

在构建了 CNN 的计算图之后,接下来是针对先前准备的训练数据来训练它。训练是根据下面的代码完成的。代码首先准备数据集的路径,并将其准备到一个占位符中。请注意,路径应该更改为适合您的系统。然后它调用前面讨论过的函数。被训练的 CNN 的预测被用于测量网络的成本,该成本将使用梯度下降优化器被最小化。注意:一些张量有一个名字,这有助于以后测试 CNN 时检索这些张量。

#Nnumber of classes in the dataset. Used to specify number of outputs in the last fully connected layer.num_datatset_classes = 10#Number of rows & columns in each input image. The image is expected to be rectangular Used to reshape the images and specify the input tensor shape.im_dim = 32#Number of channels in rach input image. Used to reshape the images and specify the input tensor shape.num_channels = 3#Directory at which the training binary files of the CIFAR10 dataset are saved.patches_dir = "C:\\Users\\Dell\\Downloads\\Compressed\\cifar-10-python\\cifar-10-batches-py\\"#Reading the CIFAR10 training binary files and returning the input data and output labels. Output labels are used to test the CNN prediction accuracy.dataset_array, dataset_labels = get_dataset_images(dataset_path=patches_dir, im_dim=im_dim, num_channels=num_channels)print("Size of data : ", dataset_array.shape)"""Input tensor to hold the data read above. It is the entry point of the computational graph.The given name of 'data_tensor' is useful for retreiving it when restoring the trained model graph for testing."""data_tensor = tensorflow.placeholder(tensorflow.float32, shape=[None, im_dim, im_dim, num_channels], name='data_tensor')"""Tensor to hold the outputs label.The name "label_tensor" is used for accessing the tensor when tesing the saved trained model after being restored."""label_tensor = tensorflow.placeholder(tensorflow.float32, shape=[None], name='label_tensor')#The probability of dropping neurons in the dropout layer. It is given a name for accessing it later.keep_prop = tensorflow.Variable(initial_value=0.5, name="keep_prop")#Building the CNN architecure and returning the last layer which is the fully connected layer.fc_result2 = create_CNN(input_data=data_tensor, num_classes=num_datatset_classes, keep_prop=keep_prop)"""Predicitions probabilities of the CNN for each training sample.Each sample has a probability for each of the 10 classes in the dataset.Such tensor is given a name for accessing it later."""softmax_propabilities = tensorflow.nn.softmax(fc_result2, name="softmax_probs")"""Predicitions labels of the CNN for each training sample.The input sample is classified as the class of the highest probability.axis=1 indicates that maximum of values in the second axis is to be returned. This returns that maximum class probability fo each sample."""softmax_predictions = tensorflow.argmax(softmax_propabilities, axis=1)#Cross entropy of the CNN based on its calculated probabilities.cross_entropy = tensorflow.nn.softmax_cross_entropy_with_logits(logits=tensorflow.reduce_max(input_tensor=softmax_propabilities, reduction_indices=[1]),labels=label_tensor)#Summarizing the cross entropy into a single value (cost) to be minimized by the learning algorithm.cost = tensorflow.reduce_mean(cross_entropy)#Minimizng the network cost using the Gradient Descent optimizer with a learning rate is 0.01.error = tensorflow.train.GradientDescentOptimizer(learning_rate=.01).minimize(cost)#Creating a new TensorFlow Session to process the computational graph.sess = tensorflow.Session()#Wiriting summary of the graph to visualize it using TensorBoard.tensorflow.summary.FileWriter(logdir="./log/", graph=sess.graph)#Initializing the variables of the graph.sess.run(tensorflow.global_variables_initializer())"""Because it may be impossible to feed the complete data to the CNN on normal machines, it is recommended to split the data into a number of patches.A percent of traning samples is used to create each path. Samples for each path can be randomly selected."""num_patches = 5#Number of patchesfor patch_num in numpy.arange(num_patches):print("Patch : ", str(patch_num))percent = 80 #percent of samples to be included in each path.#Getting the input-output data of the current path.shuffled_data, shuffled_labels = get_patch(data=dataset_array, labels=dataset_labels, percent=percent)#Data required for cnn operation. 1)Input Images, 2)Output Labels, and 3)Dropout probabilitycnn_feed_dict = {data_tensor: shuffled_data,label_tensor: shuffled_labels,keep_prop: 0.5}"""Training the CNN based on the current patch.CNN error is used as input in the run to minimize it.SoftMax predictions are returned to compute the classification accuracy."""softmax_predictions_, _ = sess.run([softmax_predictions, error], feed_dict=cnn_feed_dict)#Calculating number of correctly classified samples.correct = numpy.array(numpy.where(softmax_predictions_ == shuffled_labels))correct = correct.sizeprint("Correct predictions/", str(percent * 50000/100), ' : ', correct)

不是将整个训练数据提供给 CNN,而是将数据分成一组小块,然后一个小块一个小块地通过环路提供给网络。每个补丁包含训练数据的子集。使用 get_patch 函数返回补丁。该函数接受输入数据、标签以及从这些数据中返回的样本百分比。然后,它根据输入的百分比返回数据的子集。

def get_patch(data, labels, percent=70):"""Returning patch to train the CNN.:param data: Complete input data after being encoded and reshaped.:param labels: Labels of the entire dataset.:param percent: Percent of samples to get returned in each patch.:return: Subset of the data (patch) to train the CNN model."""#Using the percent of samples per patch to return the actual number of samples to get returned.num_elements = numpy.uint32(percent*data.shape[0]/100)shuffled_labels = labels#Temporary variable to hold the data after being shuffled.numpy.random.shuffle(shuffled_labels)#Randomly reordering the labels."""The previously specified percent of the data is returned starting from the beginning until meeting the required number of samples.The labels indices are also used to return their corresponding input images samples."""return data[shuffled_labels[:num_elements], :, :, :], shuffled_labels[:num_elements]

5。保存训练好的 CNN 模型

在对 CNN 进行训练之后,模型被保存起来,供以后在另一个 Python 脚本中测试时重用。您还应该更改保存模型的路径,以适合您的系统。

#Saving the model after being trained.
saver = tensorflow.train.Saver()
save_model_path = "C:\\model\\"
save_path = saver.save(sess=sess, save_path=save_model_path+"model.ckpt")
print("Model saved in : ", save_path)

6。准备测试数据,恢复训练好的 CNN 模型

在测试训练好的模型之前,需要准备测试数据并恢复之前训练好的模型。测试数据准备与训练数据类似,只是只有一个二进制文件需要解码。根据修改的 get_dataset_images 函数对测试文件进行解码。这个函数调用 unpickle_patch 函数,就像之前对训练数据所做的一样。

def get_dataset_images(test_path_path, im_dim=32, num_channels=3):"""Similar to the one used in training except that there is just a single testing binary file for testing the CIFAR10 trained models."""print("Working on testing patch")data_dict = unpickle_patch(test_path_path)images_data = data_dict[b"data"]dataset_array = numpy.reshape(images_data, newshape=(len(images_data), im_dim, im_dim, num_channels))return dataset_array, data_dict[b"labels"]

7。测试训练好的 CNN 模型。

准备好测试数据,恢复训练好的模型后,我们就可以按照下面的代码开始测试模型了。值得一提的是,我们的目标只是返回输入样本的网络预测。这就是为什么 TF 会话只返回预测。当训练 CNN 时,会话运行以最小化成本。在测试中,我们不再对最小化成本感兴趣。另一个有趣的地方是,dropout 层的保持概率现在被设置为 1。这意味着不要删除任何节点。这是因为我们只是在确定了要删除的节点后才使用预训练模型。现在,我们只是使用模型之前所做的,并不关心通过删除其他节点来修改它。

#Dataset path containing the testing binary file to be decoded.patches_dir = "C:\\Users\\Dell\\Downloads\\Compressed\\cifar-10-python\\cifar-10-batches-py\\"dataset_array, dataset_labels = get_dataset_images(test_path_path=patches_dir + "test_batch", im_dim=32, num_channels=3)print("Size of data : ", dataset_array.shape)sess = tensorflow.Session()#Restoring the previously saved trained model.saved_model_path = 'C:\\Users\\Dell\\Desktop\\model\\'saver = tensorflow.train.import_meta_graph(saved_model_path+'model.ckpt.meta')saver.restore(sess=sess, save_path=saved_model_path+'model.ckpt')#Initalizing the varaibales.sess.run(tensorflow.global_variables_initializer())graph = tensorflow.get_default_graph()"""Restoring previous created tensors in the training phase based on their given tensor names in the training phase.Some of such tensors will be assigned the testing input data and their outcomes (data_tensor, label_tensor, and keep_prop).Others are helpful in assessing the model prediction accuracy (softmax_propabilities and softmax_predictions)."""softmax_propabilities = graph.get_tensor_by_name(name="softmax_probs:0")softmax_predictions = tensorflow.argmax(softmax_propabilities, axis=1)data_tensor = graph.get_tensor_by_name(name="data_tensor:0")label_tensor = graph.get_tensor_by_name(name="label_tensor:0")keep_prop = graph.get_tensor_by_name(name="keep_prop:0")#keep_prop is equal to 1 because there is no more interest to remove neurons in the testing phase.feed_dict_testing = {data_tensor: dataset_array,label_tensor: dataset_labels,keep_prop: 1.0}#Running the session to predict the outcomes of the testing samples.softmax_propabilities_, softmax_predictions_ = sess.run([softmax_propabilities, softmax_predictions],feed_dict=feed_dict_testing)#Assessing the model accuracy by counting number of correctly classified samples.correct = numpy.array(numpy.where(softmax_predictions_ == dataset_labels))correct = correct.sizeprint("Correct predictions/10,000 : ", correct)

8。构建 Flask Web 应用程序

在训练 CNN 模型之后,我们可以将它添加到 HTTP 服务器上,并允许用户在线使用它。用户将使用 HTTP 客户端上传图像。上传的图像将由 HTTP 服务器接收,或者更具体地说,由 Flask Web 应用程序接收。这种应用将基于训练的模型预测图像的类别标签,并最终将类别标签返回给 HTTP 客户端。图 5 总结了这种讨论。

Figure 5

要开始构建 flask 应用程序,需要创建 Flask 库,并使用 Flask 类创建一个新的应用程序。最后,这样的应用程序将运行以从 Web 访问。此类应用程序有一些属性,如访问它的主机、端口号和返回调试信息的调试标志。运行应用程序后,可以使用指定的主机和端口号来访问它。

import flask#Creating a new Flask Web application. It accepts the package name.app = flask.Flask("CIFAR10_Flask_Web_App")"""To activate the Web server to receive requests, the application must run.A good practice is to check whether the file is whether the file called from an external Python file or not.If not, then it will run."""if __name__ == "__main__":"""In this example, the app will run based on the following properties:host: localhostport: 7777debug: flag set to True to return debugging information."""app.run(host="localhost", port=7777, debug=True)

目前,服务器没有提供任何功能。服务器应该做的第一件事是允许用户上传图像。当用户访问应用程序的根 URL 时,应用程序什么也不做。应用程序可以将用户重定向到一个 HTML 页面,用户可以在该页面上传图像。为此,该应用程序有一个名为 redirect_upload 的函数,将用户重定向到上传图像的页面。让这个函数在用户访问应用程序的根目录后执行的是使用以下代码行创建的路由:

app.add_url_rule(rule="/", endpoint="homepage", view_func=redirect_upload)

这一行说的是如果用户访问 app 的根目录(标记为"/),那么就会调用查看器函数(redirect_upload)。此类函数除了呈现一个名为 upload_image.html 的 HTML 页面之外什么也不做。该页面位于服务器的专用模板目录下。模板目录中的页面通过调用 render_template 函数来呈现。请注意,有一个名为 endpoint 的属性,它使多次重用相同的路由变得容易,而无需对其进行硬编码。

def redirect_upload():"""A viewer function that redirects the Web application from the root to a HTML page for uploading an image to get classified.The HTML page is located under the /templates directory of the application.:return: HTML page used for uploading an image. It is 'upload_image.html' in this exmaple."""return flask.render_template(template_name_or_list="upload_image.html")"""Creating a route between the homepage URL (http://localhost:7777) to a viewer function that is called after getting to such URL.Endpoint 'homepage' is used to make the route reusable without hard-coding it later."""app.add_url_rule(rule="/", endpoint="homepage", view_func=redirect_upload)

呈现的 HTML 页面的屏幕如图 6 所示。

Figure 6

下面是这个页面的 HTML 代码。这是一个简单的表单,允许用户上传图像文件。提交该表单时,将向 URLHTTP://localhost:7777/upload/返回一条 POST HTTP 消息。

<!DOCTYPE html><html lang="en"><head><link rel="stylesheet" type="text/css" href="{{url_for(endpoint='static', filename='project_styles.css')}}"><meta charset="UTF-8"><title>Upload Image</title></head><body><form enctype="multipart/form-data" method="post" action="http://localhost:7777/upload/"><center><h3>Select CIFAR10 image to predict its label.</h3><input type="file" name="image_file" accept="image/*"><br><input type="submit" value="Upload"></center></form></body></html>

从 HTML 表单返回到服务器后,将调用与在表单动作属性中指定的 URL 相关联的查看器函数,即 upload_image 函数。该函数获取用户选择的图像,并将其保存到服务器。

def upload_image():"""Viewer function that is called in response to getting to the 'http://localhost:7777/upload' URL.It uploads the selected image to the server.:return: redirects the application to a new page for predicting the class of the image."""#Global variable to hold the name of the image file for reuse later in prediction by the 'CNN_predict' viewer functions.global secure_filenameif flask.request.method == "POST":#Checking of the HTTP method initiating the request is POST.img_file = flask.request.files["image_file"]#Getting the file name to get uploaded.secure_filename = werkzeug.secure_filename(img_file.filename)#Getting a secure file name. It is a good practice to use it.img_path = os.path.join(app.root_path, secure_filename)#Preparing the full path under which the image will get saved.img_file.save(img_path)#Saving the image in the specified path.print("Image uploaded successfully.")"""After uploading the image file successfully, next is to predict the class label of it.The application will fetch the URL that is tied to the HTML page responsible for prediction and redirects the browser to it.The URL is fetched using the endpoint 'predict'."""return flask.redirect(flask.url_for(endpoint="predict"))return "Image upload failed.""""Creating a route between the URL (http://localhost:7777/upload) to a viewer function that is called after navigating to such URL.Endpoint 'upload' is used to make the route reusable without hard-coding it later.The set of HTTP method the viewer function is to respond to is added using the 'methods' argument.In this case, the function will just respond to requests of method of type POST."""app.add_url_rule(rule="/upload/", endpoint="upload", view_func=upload_image, methods=["POST"])

将图像成功上传到服务器后,我们就可以读取图像,并使用之前训练的 CNN 模型预测其类别标签。因此,upload_image 函数将应用程序重定向到负责预测图像类别标签的查看器函数。这种查看器功能是通过其在该行中指定的端点来实现的:

return flask.redirect(flask.url_for(endpoint="predict"))

将调用与 endpoint =“predict”关联的方法,这是 CNN_predict 函数。

def CNN_predict():"""Reads the uploaded image file and predicts its label using the saved pre-trained CNN model.:return: Either an error if the image is not for CIFAR10 dataset or redirects the browser to a new page to show the prediction result if no error occurred.""""""Setting the previously created 'secure_filename' to global.This is because to be able invoke a global variable created in another function, it must be defined global in the caller function."""global secure_filename#Reading the image file from the path it was saved in previously.img = scipy.misc.imread(os.path.join(app.root_path, secure_filename))"""Checking whether the image dimensions match the CIFAR10 specifications.CIFAR10 images are RGB (i.e. they have 3 dimensions). It number of dimenions was not equal to 3, then a message will be returned."""if(img.ndim) == 3:"""Checking if the number of rows and columns of the read image matched CIFAR10 (32 rows and 32 columns)."""if img.shape[0] == img.shape[1] and img.shape[0] == 32:"""Checking whether the last dimension of the image has just 3 channels (Red, Green, and Blue)."""if img.shape[-1] == 3:"""Passing all conditions above, the image is proved to be of CIFAR10.This is why it is passed to the predictor."""predicted_class = CIFAR10_CNN_Predict_Image.main(img)"""After predicting the class label of the input image, the prediction label is rendered on an HTML page.The HTML page is fetched from the /templates directory. The HTML page accepts an input which is the predicted class."""return flask.render_template(template_name_or_list="prediction_result.html", predicted_class=predicted_class)else:# If the image dimensions do not match the CIFAR10 specifications, then an HTML page is rendered to show the problem.return flask.render_template(template_name_or_list="error.html", img_shape=img.shape)else:# If the image dimensions do not match the CIFAR10 specifications, then an HTML page is rendered to show the problem.return flask.render_template(template_name_or_list="error.html", img_shape=img.shape)return "An error occurred."#Returned if there is a different error other than wrong image dimensions."""Creating a route between the URL (http://localhost:7777/predict) to a viewer function that is called after navigating to such URL.Endpoint 'predict' is used to make the route reusable without hard-coding it later."""app.add_url_rule(rule="/predict/", endpoint="predict", view_func=CNN_predict)

这种方法读取图像并检查它是否与 32×32×3 的 CIFAR-10 数据集的尺寸相匹配。如果图像符合 CIFAR-10 数据集的规格,则它将被传递给负责进行预测的函数,如下所示:

predicted_class = CIFAR10_CNN_Predict_Image.main(img)

负责预测图像类别标签的主函数定义如下。它会恢复训练好的模型,并运行一个会话来返回图像的预测类。预测的类被返回给 Flask Web 应用程序。

def main(img):"""The 'main' method accepts an input image array of size 32x32x3 and returns its class label.:param img:RGB image of size 32x32x3.:return:Predicted class label."""#Dataset path containing a binary file with the labels of classes. Useful to decode the prediction code into a significant textual label.patches_dir = "C:\\cifar-10-python\\cifar-10-batches-py\\"dataset_array = numpy.random.rand(1, 32, 32, 3)dataset_array[0, :, :, :] = imgsess = tensorflow.Session()#Restoring the previously saved trained model.saved_model_path = 'C:\\model\\'saver = tensorflow.train.import_meta_graph(saved_model_path+'model.ckpt.meta')saver.restore(sess=sess, save_path=saved_model_path+'model.ckpt')#Initalizing the varaibales.sess.run(tensorflow.global_variables_initializer())graph = tensorflow.get_default_graph()"""Restoring previous created tensors in the training phase based on their given tensor names in the training phase.Some of such tensors will be assigned the testing input data and their outcomes (data_tensor, label_tensor, and keep_prop).Others are helpful in assessing the model prediction accuracy (softmax_propabilities and softmax_predictions)."""softmax_propabilities = graph.get_tensor_by_name(name="softmax_probs:0")softmax_predictions = tensorflow.argmax(softmax_propabilities, axis=1)data_tensor = graph.get_tensor_by_name(name="data_tensor:0")label_tensor = graph.get_tensor_by_name(name="label_tensor:0")keep_prop = graph.get_tensor_by_name(name="keep_prop:0")#keep_prop is equal to 1 because there is no more interest to remove neurons in the testing phase.feed_dict_testing = {data_tensor: dataset_array,keep_prop: 1.0}#Running the session to predict the outcomes of the testing samples.softmax_propabilities_, softmax_predictions_ = sess.run([softmax_propabilities, softmax_predictions],feed_dict=feed_dict_testing)label_names_dict = unpickle_patch(patches_dir + "batches.meta")dataset_label_names = label_names_dict[b"label_names"]return dataset_label_names[softmax_predictions_[0]].decode('utf-8')

返回的图像的类标签将呈现在一个名为 prediction_result.html 的新 HTML 页面上,由 CNN_predict 函数在这一行中指示,如图 7 所示。

Figure 7

注意,Flask 应用程序使用 Jinja2 模板引擎,该引擎允许 HTML 页面接受输入参数。在这种情况下传递的输入参数是 predicted _ class = predicted _ class。

return flask.render_template(template_name_or_list="prediction_result.html", predicted_class=predicted_class)

这个页面的 HTML 代码如下。

<!DOCTYPE html><html lang="en"><head><link rel="stylesheet" type="text/css" href="{{url_for(endpoint='static', filename='project_styles.css')}}"><script type="text/javascript" src="{{url_for(endpoint='static', filename='result.js')}}"></script><meta charset="UTF-8"><title>Prediction Result</title></head><body onload="show_alert('{{predicted_class}}')"><center><h1>Predicted Class Label : <span>{{predicted_class}}</span></h1><br><a href="{{url_for(endpoint='homepage')}}"><span>Return to homepage</span>.</a></center></body></html>

它是一个模板,由图像的预测类填充,作为参数传递给 HTML 页面,如代码的这一部分所示:

<**span**>{{predicted_class}}</**span**>

想了解更多关于 Flask RESTful API 的信息,你可以访问这样的教程【https://www.tutorialspoint.com/flask/index.htm。

完整的项目可以在 Github 的这个链接中找到:【https://github.com/ahmedfgad/CIFAR10CNNFlask

原载于 2018 年 5 月 1 日https://www.linkedin.com

原文可在 LinkedIn 的以下链接中找到:

https://www . LinkedIn . com/pulse/complete-guide-build-conv net-http-based-application-using-Ahmed-gad

联系作者

艾哈迈德·法齐·加德

领英:https://linkedin.com/in/ahmedfgad

电子邮件:ahmed.f.gad@gmail.com

基于实时 Twitter 数据的复杂事件处理

原文:https://towardsdatascience.com/complex-event-processing-with-flink-on-realtime-twitter-data-d09d9953df1b?source=collection_archive---------3-----------------------

最近,我决定研究 Flink 的复杂事件处理库(CEP ),看看它有什么能力。我发现它非常容易使用,我可以想象出许多可能的用例。

什么是复杂事件处理?在开始讨论如何进行 CEP 之前,我认为首先有必要对其进行定义,并让数据科学家和工程师了解它为什么有用。复杂事件处理对于检测流数据中的模式以及基于这些模式发送警报或通知非常有用。任何处理需要监控的时间关键型数据的人都应该知道如何使用 CEP。例如,在 Flink 网站上,他们详细介绍了如何使用复杂的事件处理来监控服务器机架温度,并在同一机架连续两次超过温度阈值时发出警告。然后,他们描述了当你有两个“连续警告”时,你如何创建一个警报。这个例子很好,但是有点过时,我想自己用 Twitter 数据测试一下。

入门:

在我之前关于 Flink 的文章中,我描述了如何建立一个 Tweets 流,并对 Tweets 中最常见的单词进行基本的字数统计。现在回想一下,我们有一个(word,count)格式的单词数据流,我称之为 dataWindowKafka(因为我们之前直接将其提供给 Kafka 制作人)。这是我们在这个例子中主要要做的事情。

现在,假设我们有兴趣了解某个特定单词在一个窗口中被提及的次数是否超过了设定的数量,而在第二个窗口中被提及的次数是否超过了设定的数量。首先,让我们扩展 SimpleCondition,以便不像在文档中那样以内联方式编写代码。

现在我们有了一个易于使用的类,让我们写出实际的 CEP 代码。

在生产中,您可能会将该流的输出传递给 Kafka 或某个数据库,但出于我们的目的,我们将只是将它作为基本的 println 写入控制台。

System.*out*.println(manyMentions.writeAsText("alert.txt"));
// Output{increasing=[(trump,137)], first=[(trump,143)]}
{increasing=[(trump s,49)], first=[(trump s,35)]}
{increasing=[(  ,42)], first=[(  ,29)]}
{increasing=[(i m,11)], first=[(i m,21)]}

我们显然有一些符号化的问题,因为 trump 和“trump 的”本质上是同一个词,空格也不应包括在内,但 CEP 本身似乎在做自己的工作。同时满足第一个过滤器“first”和第二个过滤器“increasing”。虽然如图所示,第二个并不总是实际增加。原因是因为该值是静态的,所以只要秒大于 20,它就会返回 true,即使先前的值是 42。为此我们需要迭代条件。

迭代条件:根据 Flink 文档,它们是:

最常见的情况。这就是您如何根据先前接受的事件的属性或它们子集的统计信息来指定接受后续事件的条件。

所以,假设我们想确保我们发布这些推文的频率确实在增加。这里的主要区别是,布尔函数将执行 current_event>previous_event,而不是使用固定值。我们可以使用迭代条件来实现。

I will add the output of this final event soon.

更复杂的例子

当然,这个例子仅仅触及了 CEP 的表面。人们可以立即想象出更多的用例。例如,一家想要监控其品牌声誉的公司可能会通过将 CEP 与情绪分析结合使用来检测可能的抵制(以便快速做出响应)。因此,该公司可以使用机器学习算法来检测和标记每条提到他们品牌的推文,带有积极或消极情绪,然后如果消极情绪超过某个阈值和/或积极情绪的数量,他们就会发出警报。

另一个例子可能是想要向用户推荐电影或产品的在线电影或购物网站。在这种情况下,他们可以查看连续的日志数据,并且如果用户在给定的时间跨度内访问了电影/项目的某个序列,则推荐另一个相关的电影/项目。

[## Apache Flink:使用 Apache Flink 引入复杂事件处理(CEP)

复杂事件处理(CEP)正好解决了这一问题,即把连续输入的事件与一个…

flink.apache.org](http://flink.apache.org/news/2016/04/06/cep-monitoring.html) [## Apache Flink 1.4-快照文档:Flink CEP-Flink 的复杂事件处理

ci.apache.org](https://ci.apache.org/projects/flink/flink-docs-release-1.4/dev/libs/cep.html) [## 用 Flink CEP 库寻找模式| Flink forward | 2017 年 9 月 11-13 日|柏林

berlin.flink-forward.org](https://berlin.flink-forward.org/kb_sessions/looking-for-patterns-with-flink-cep-library/) [## 数据工匠/flink-培训-练习

github.com](https://github.com/dataArtisans/flink-training-exercises/blob/master/src/main/java/com/dataartisans/flinktraining/exercises/datastream_java/cep/LongRides.java)

卷积神经网络的组件

原文:https://towardsdatascience.com/components-of-convolutional-neural-networks-6ff66296b456?source=collection_archive---------11-----------------------

最近最先进的体系结构采用了许多附加组件来补充卷积运算。在这篇文章中,我将解释一些提高了现代卷积神经网络的速度和精度的最重要的组件。我将从解释每个组件的理论开始,并以 keras 中的实际实现结束。

联营

让 CNN 非常有效的第一个秘方是联营。池化是一种向量到标量的变换,它对图像的每个局部区域进行操作,就像卷积一样,但是,与卷积不同,它们没有过滤器,也不计算局部区域的点积,而是计算区域中像素的平均值(平均池化)或简单地挑选具有最高强度的像素并丢弃其余的像素(最大池化)。

以上是一个 2 x 2 的池,它将有效地减少了 2 的要素地图的大小。

汇集的想法看起来可能是反作用的,因为它会导致信息的丢失,然而它在实践中被证明是非常有效的,因为它使得 covnets 对于图像呈现的变化是不变的,并且它还减少了背景噪声的影响。最大池近年来工作得最好,它基于一个区域中的最大像素代表该区域中最重要的特征的思想。通常,我们希望分类的对象图像可能包含许多其他对象,例如,出现在汽车图片中某处的猫可能会误导分类器。池有助于减轻这种影响,并使 covnets 更好地泛化。

它还大大降低了 covnet 的计算成本。通常,网络中每层的图像大小与每层的计算成本(flops)成正比。随着层越来越深,池化减少了图像的维度,因此,它有助于防止网络所需的 flops 数量激增。交错卷积有时被用作池化的替代方法。

辍学者

过度拟合是一种网络在训练集上工作良好,但在测试集上表现不佳的现象。这通常是由于过度依赖训练集中特定特征的存在。辍学是一种应对过度适应的技术。它的工作原理是随机设置一些激活为 0,本质上杀死他们。通过这样做,网络被迫探索更多分类图像的方法,而不是过度依赖某些特征。这是 AlexNet 的关键要素之一。

批量标准化

神经网络的一个主要问题是梯度消失。这是一种梯度变得太小的情况,因此,训练冲浪运动员非常糟糕。来自 Google Brain 的 Ioffe 和 Szegedy 发现,这主要是由于内部协变量的变化,这种情况是由于信息在网络中流动时数据分布的变化而引起的。他们所做的是发明一种被称为批量标准化的技术。其工作原理是将每批图像归一化,使平均值和单位方差为零。

在 CNN 中,它通常位于非线性(relu)之前。它极大地提高了准确性,同时令人难以置信地加快了训练过程。

数据扩充

现代 covnets 需要的最后一个要素是数据扩充。人类视觉系统在适应图像平移、旋转和其他形式的失真方面表现出色。拍个图像随便翻一翻反正大部分人还是能认出来的。然而,covnets 并不擅长处理这种扭曲,它们可能会因为微小的翻译而严重失败。解决这个问题的关键是随机扭曲训练图像,使用水平翻转、垂直翻转、旋转、白化、移位和其他扭曲。这将使 covnets 能够学习如何处理这种失真,因此,它们将能够在现实世界中很好地工作。

另一种常见的技术是从每幅图像中减去平均图像,然后除以标准偏差。

解释了这些组件是什么以及它们为什么工作良好之后,我现在将解释如何在 keras 中实现它们。

在本文中,所有实验都将在 CIFAR10 上进行,这是一个由 60,000 张 32 x 32 RGB 图像组成的数据集。它被分成 50,000 幅训练图像和 10,000 幅测试图像

为了使事情更加模块化,让我们为每一层创建一个简单的函数

**def** Unit(x,filters):
    out = BatchNormalization()(x)
    out = Activation(**"relu"**)(out)
    out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding=**"same"**)(out)

    **return** out

这是我们代码中最重要的部分,单位函数定义了一个简单的层,包含三层,首先是我之前解释过的批处理规范化,然后我们添加了 RELU 激活,最后,我们添加了卷积,注意我是如何将 RELU 放在 conv 之前的,这是最近的一个实践,叫做“预激活”

现在我们将这个单元层合并成一个单一的模型

**def** MiniModel(input_shape):
    images = Input(input_shape)

    net = Unit(images,64)
    net = Unit(net,64)
    net = Unit(net,64)
    net = MaxPooling2D(pool_size=(2,2))(net)

    net = Unit(net,128)
    net = Unit(net,128)
    net = Unit(net,128)
    net = MaxPooling2D(pool_size=(2, 2))(net)

    net = Unit(net,256)
    net = Unit(net,256)
    net = Unit(net,256)

    net = Dropout(0.5)(net)
    net = AveragePooling2D(pool_size=(8,8))(net)
    net = Flatten()(net)
    net = Dense(units=10,activation=**"softmax"**)(net)

    model = Model(inputs=images,outputs=net)

    **return** model

在这里,我们使用函数式 API 来定义我们的模型,我们从三个单元开始,每个单元有 64 个过滤器,然后是最大池层,将我们的 32 x 32 图像减少到 16 x 16。接下来是 3,128 个过滤器单元,然后是池,在这一点上,我们的图像变成 8×8,最后,我们有另外 3 个单元,256 个通道。请注意,每次我们将图像尺寸缩小 2 倍,通道数量就会增加一倍。

我们以 0.5 的比率添加辍学,这将随机地停用我们的参数的 50%,正如我先前解释的,它对抗过度拟合。

接下来,我们需要加载 cifar10 数据集并执行一些数据扩充

*#load the cifar10 dataset* (train_x, train_y) , (test_x, test_y) = cifar10.load_data()

*#normalize the data* train_x = train_x.astype(**'float32'**) / 255
test_x = test_x.astype(**'float32'**) / 255

*#Subtract the mean image from both train and test set* train_x = train_x - train_x.mean()
test_x = test_x - test_x.mean()

*#Divide by the standard deviation* train_x = train_x / train_x.std(axis=0)
test_x = test_x / test_x.std(axis=0)

在上面的代码中,在加载训练和测试数据后,我们从每个图像中减去均值图像,然后除以标准差,这是一种基本的数据扩充技术,有时,我们可能只减去均值,而跳过标准差部分,应该使用效果最好的部分。

对于更先进的数据增强,我们的图像加载过程会略有变化,keras 有一个非常有用的数据增强工具,简化了整个过程。

下面的代码可以解决这个问题

datagen = ImageDataGenerator(rotation_range=10,
                             width_shift_range=5\. / 32,
                             height_shift_range=5\. / 32,
                             horizontal_flip=**True**)

*# Compute quantities required for featurewise normalization
# (std, mean, and principal components if ZCA whitening is applied).* datagen.fit(train_x)

在上面,首先,我们指定 10 度的旋转角度,高度和宽度都移动 5/32,最后水平翻转,所有这些变换将随机应用于训练集中的图像。请注意,还存在更多的转换,您可以查看一下可以为该类指定的所有参数。请记住,过度使用数据增强可能是有害的。

接下来,我们必须将标签转换为一键编码

*#Encode the labels to vectors* train_y = keras.utils.to_categorical(train_y,10)
test_y = keras.utils.to_categorical(test_y,10)

我已经在之前的教程中解释过了,所以我不会再在这里解释了。事实上,几乎所有组成训练过程的东西都和我以前的教程一样,因此,这里是完整的代码

*#import needed classes* **import** keras
**from** keras.datasets **import** cifar10
**from** keras.layers **import** Dense,Conv2D,MaxPooling2D,Flatten,AveragePooling2D,Dropout,BatchNormalization,Activation
**from** keras.models **import** Model,Input
**from** keras.optimizers **import** Adam
**from** keras.callbacks **import** LearningRateScheduler
**from** keras.callbacks **import** ModelCheckpoint
**from** math **import** ceil
**import** os
**from** keras.preprocessing.image **import** ImageDataGenerator

**def** Unit(x,filters):
    out = BatchNormalization()(x)
    out = Activation(**"relu"**)(out)
    out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding=**"same"**)(out)

    **return** out

*#Define the model* **def** MiniModel(input_shape):
    images = Input(input_shape)

    net = Unit(images,64)
    net = Unit(net,64)
    net = Unit(net,64)
    net = MaxPooling2D(pool_size=(2,2))(net)

    net = Unit(net,128)
    net = Unit(net,128)
    net = Unit(net,128)
    net = MaxPooling2D(pool_size=(2, 2))(net)

    net = Unit(net,256)
    net = Unit(net,256)
    net = Unit(net,256)

    net = Dropout(0.25)(net)
    net = AveragePooling2D(pool_size=(8,8))(net)
    net = Flatten()(net)
    net = Dense(units=10,activation=**"softmax"**)(net)

    model = Model(inputs=images,outputs=net)

    **return** model

*#load the cifar10 dataset* (train_x, train_y) , (test_x, test_y) = cifar10.load_data()

*#normalize the data* train_x = train_x.astype(**'float32'**) / 255
test_x = test_x.astype(**'float32'**) / 255

*#Subtract the mean image from both train and test set* train_x = train_x - train_x.mean()
test_x = test_x - test_x.mean()

*#Divide by the standard deviation* train_x = train_x / train_x.std(axis=0)
test_x = test_x / test_x.std(axis=0)

datagen = ImageDataGenerator(rotation_range=10,
                             width_shift_range=5\. / 32,
                             height_shift_range=5\. / 32,
                             horizontal_flip=**True**)

*# Compute quantities required for featurewise normalization
# (std, mean, and principal components if ZCA whitening is applied).* datagen.fit(train_x)

*#Encode the labels to vectors* train_y = keras.utils.to_categorical(train_y,10)
test_y = keras.utils.to_categorical(test_y,10)

*#define a common unit* input_shape = (32,32,3)
model = MiniModel(input_shape)

*#Print a Summary of the model* model.summary()
*#Specify the training components* model.compile(optimizer=Adam(0.001),loss=**"categorical_crossentropy"**,metrics=[**"accuracy"**])

epochs = 20
steps_per_epoch = ceil(50000/128)

*# Fit the model on the batches generated by datagen.flow().* model.fit_generator(datagen.flow(train_x, train_y, batch_size=128),
                    validation_data=[test_x,test_y],
                    epochs=epochs,steps_per_epoch=steps_per_epoch, verbose=1, workers=4)

*#Evaluate the accuracy of the test dataset* accuracy = model.evaluate(x=test_x,y=test_y,batch_size=128)
model.save(**"cifar10model.h5"**)

首先,这里有些事情是不同的

input_shape = (32,32,3)
model = MiniModel(input_shape)

*#Print a Summary of the model* model.summary()

正如我之前解释的,cifar 10 由 32 x 32 RGB 图像组成,因此,输入形状有 3 个通道。这是不言自明的。

下一行创建了我们定义的模型的实例,并传入了输入形状

最后,最后一行将打印出我们网络的完整摘要,包括参数的数量。

最后需要解释的是

epochs = 20
steps_per_epoch = ceil(50000/128)

*# Fit the model on the batches generated by datagen.flow().* model.fit_generator(datagen.flow(train_x, train_y, batch_size=128),
                    validation_data=[test_x,test_y],
                    epochs=epochs,steps_per_epoch=steps_per_epoch, verbose=1, workers=4)

*#Evaluate the accuracy of the test dataset* accuracy = model.evaluate(x=test_x,y=test_y,batch_size=128)
model.save(**"cifar10model.h5"**)

首先,我们定义要运行的周期数,以及每个周期的步数,不要与数字混淆

steps_per_epoch = ceil(50000/128)

这里的 50000 是训练图像的总数,这里我们使用 128 的批量大小,这意味着,对于 20 个时期中的每一个,网络必须处理 50000/128 批图像。

接下来是 fit 函数,这显然不同于我在之前的教程中解释的 fit 函数。

再看看下面会有帮助

*Fit the model on the batches generated by datagen.flow().* model.fit_generator(datagen.flow(train_x, train_y, batch_size=128),
                    validation_data=[test_x,test_y],
                    epochs=epochs,steps_per_epoch=steps_per_epoch, verbose=1, workers=4)

由于我们将数据生成器类用于数据扩充目的,我们必须使用 fit_generator 函数,我们也不直接传入 train_x 和 train_y,而是通过来自数据生成器的 flow 函数传入它们,我们还指定批量大小,接下来我们陈述验证数据,在这种情况下是测试数据。所有其他事情保持不变。

该设置在 20 个周期后产生 82%。

你可以尝试调整参数和网络,看看你能提高多少精度。在下一篇教程中,我将解释一些真正构建高效 cnn 架构所需的技巧和技术。本教程的目的是向您介绍基本组件。

如果你想深入研究计算机视觉。从https://John . specpal . science下载我的免费电子书《深度计算机视觉入门》

如果你有任何问题,请在下面评论或者通过 @johnolafenwa 在 twitter 上联系我

为混合物联网构建深度学习微服务

原文:https://towardsdatascience.com/composing-deep-learning-microservices-for-the-hybrid-internet-of-things-c6cb3cb23b0f?source=collection_archive---------0-----------------------

深度学习正在稳步重塑我们世界的方方面面。但是,除非开发人员有合适的工具来包装这种机器智能以进行普遍部署,否则它无法实现这种潜力。

通过整合到支持微服务架构的工具中,深度学习正在进入云数据服务开发人员的工作环境。这指的是一种越来越流行的方式,即将云应用程序开发为模块化、可重用、范围狭窄的功能套件。在微服务环境中,每个函数都在自己的容器中执行(比如 Docker)。此外,每个微服务通过 RESTful APIs 以轻量级、松散耦合的方式与其他微服务进行互操作。

深度学习将越来越依赖于在复杂的多云中执行的容器化微服务。通常,这是通过抽象的“无服务器”接口来实现的,这些接口使微服务能够在后端云基础设施上透明地执行,而开发人员无需了解 IT 资源是从哪里或如何提供的。因此,无服务器后端使基础设施能够在运行时自动为微服务提供必要的计算能力、存储、带宽和其他分布式资源。

在转向这种组合应用程序的模式时,深度学习开发人员无需担心预配置基础设施之类的事情,如服务器或操作。相反,他们可以简单地专注于建模和编码。但要让这一切在复杂的深度学习应用程序中无缝互操作,需要有一个后端中间件结构,用于可靠的消息传递、事务回滚和长期运行的编排功能(如 Kubernetes 提供的)。

当开发人员构建深度学习微服务以在物联网(IoT)中执行时,后端互操作性结构变得甚至比大多数云中更复杂。这是因为深度学习正在成为所有物联网终端的嵌入式功能,以及从物联网中心和集中式云服务向应用程序提供的服务。正如深度学习服务本身在狭义范围内将是“微”的,算法执行的物联网端点本身在资源限制方面将越来越微——或者更确切地说,是纳米。

在物联网中,嵌入式深度学习微服务将处理端点设备捕获的丰富的实时传感器数据流。这些微服务将推动物联网应用所依赖的视频识别、运动检测、自然语言处理、点击流处理和其他模式感知应用。通过这种方式,任何种类的每一个物体都将充满持续的数据驱动智能、环境意识和情境自主性。

对于物联网深度学习应用的组成和部署,开发人员需要一个中间件后端,它可以分发微服务,以便在网络端点执行。为了支持这些物联网用例,微服务架构将发展为支持名为“雾计算”的完全分布式面向边缘的云架构

在这种新模式中,开发人员使用微服务 API 和无服务器后端来构建深度学习和其他分布式功能,这些功能可以透明地分布、并行化和优化到 fog 的无数端点。说明雾架构分层的一种方式如下,但请记住,容器化的微服务使“深度(学习)分析区”能够遍及整个云,一直到智能设备、传感器和网关。

Fog computing architecture

下图说明了 OpenFog 协会的参考架构的应用服务层是如何支持容器化的。本质上,深度学习应用和其他微服务可以在任何容器化的环境中运行,如 Docker/Kubernetes,这是一种物联网 fog 的软件背板。这样一来,这些容器化的深度学习微服务将利用物联网/雾支持层服务(如数据库和中间件),这些服务将作为微服务在自己的容器内运行。

Containerization for Application Support in the IoT Fog (source: OpenFog Consortium “OpenFog Reference Architecture for Fog Computing”)

从更广的角度来看,确保所有这些深度学习微服务相互关联的团队将把数据科学作为他们的核心技能集。这些专业人员的核心工作将是构建、训练和优化回旋、递归和其他深层神经网络算法,这种技术魔法依赖于这些算法。他们将使用工具,如 IBM blue mix open shaw 构建基于事件的深度学习微服务,以部署在可能完全私有、完全公共或跨越混合架构中私有和公共部分的物联网雾/云上。

blue mixopen 晶须今天在 IBM Bluemix 上有提供,开源社区可以在这里找到。为了支持混合物联网的深度学习应用的敏捷组合,OpenWhisk 提供了内置的链接,这使得团队能够单独开发和定制深度学习和其他微服务,作为可以以协调的顺序连接在一起的小代码片段。它为物联网和其他用例的无服务器部署、认知算法、容器和语言提供了全面、灵活的支持。

下面是 open 晶须的高级功能架构:

OpenWhisk provides a distributed compute service to execute application logic in response to events.

关于底层 open 晶须架构和源代码的更多信息,从这里开始并进入项目的 GitHub 页面。

Wolfram 暑期学校的 Reddit 计算思维

原文:https://towardsdatascience.com/computational-thinking-with-reddit-at-the-wolfram-summer-school-29961feaa85d?source=collection_archive---------1-----------------------

大约在去年的这个时候,我在想这个夏天该做些什么。在研究生院度过了一个相当糟糕的学期,但我终于完成了路易斯安那州立大学社会学博士项目的核心课程。学期结束时,我决定从巴吞鲁日开车去诺克斯维尔,那里的高温和潮湿不会让你的皮肤融化。我正在考虑“休假”,尽管在研究生院,这通常意味着收集数据和写论文。自从 2009 年以来,我一直在工作和上学,或者参加独立阅读课程,我想我应该休息一下。

然而,我太专注于文本分析和我的论文项目,无法放松。我参加的最后一个研讨会是社会网络分析。主持研讨会的教授想出了一种新颖的方法来分析采访记录,作为她在杜克大学的论文工作的一部分。她提出网络文本分析作为分析大量面试数据的方法。

如果你不熟悉这些类型的数据,社会学家通常依靠深度访谈的文字记录来更好地了解社会世界。例如,我的文学硕士工作包括采访 20 个主题。(你可以在 VICE.com这里阅读,而在这里阅读我为什么选择在非学术渠道出版这部作品。)

在某些情况下,社会学家使用数百名受试者的采访数据。我计划结合使用调查和访谈数据,调查在 HIV 阳性个体和有感染 HIV 风险的个体中使用暴露前预防法和 Truvada 药物的情况。

在我的硕士工作期间,我的导师们教我如何使用一种叫做扎根理论的东西、手写笔记和一种叫做 Atlas.ti 的软件程序来组织和分析面试数据。

Example of a transcript. Imagine this times one thousand.

这个过程…令人生畏,没有条理,不可复制。它也非常过时。所以,我试图想出更好更有效的方法来分析采访数据。我碰到了几个软件包,包括 Provalis QDA 矿工,它们似乎在文本挖掘方面做得相当不错,但仍然不够好。我开始在网上搜索,我偶然发现了 Wolfram Research 的 Mathematica 。

我对复杂性研究有些熟悉,也在想办法将其与社会学理论结合起来,特别是语言和互动如何在创造意义的过程中产生,所以史蒂夫·沃尔夫勒姆的工作对我来说并不陌生。当我在 Wolfram 网站上搜索 Mathematica 中的文本分析教程时,我看到了一个链接,链接到了 Wolfram 暑期学校。我被激起了兴趣,于是我问了几个同事是否知道这件事,其中一个说我肯定应该申请。于是我就这么做了,因为我认为一个社会学研究生根本不可能被一个似乎更适合程序员、物理学家、计算机科学家和其他 STEM 领域的项目录取。

有趣的是,当我申请时,在我的申请被考虑之前,有一个编码挑战要完成。类似于“写一个函数,使得整数列表中的所有整数都被移除。”除了统计软件的一些基础知识,我几乎没有编程经验,这有点吓人。不过我去了 Wolfram 文档中心,试了几次就能弄明白怎么做了。接下来我所知道的是,我收到了教务主任的电子邮件,要求安排一次简短的面试。在和他谈了我的兴趣、研究和目标之后,我收到了一封电子邮件,通知我已经被录取了。我欣喜若狂,因为我一直在看过去的学生参与的一些有趣的项目,以及现任和前任导师令人印象深刻的简历。哦,能见到史蒂夫·沃尔夫勒姆真是令人兴奋。

因此,有人建议我在去马萨诸塞州沃尔瑟姆参加暑期学校之前,先阅读,或者至少非常熟悉(NKS)新科学,并练习使用沃尔夫拉姆语。如果你不知道,NKS 有 1200 页那么长。我让我的一个朋友开车带我去诺克斯维尔图书馆,这样我就可以用他的借书证在几周内搞定那本大部头书(那时我不住在诺克斯维尔)。全文在网上,但我更喜欢实际的书籍,无论出于什么原因。无论如何,我尽可能地读完了它,并对简单程序的含义感到敬畏,如自然界和其他地方的细胞自动机。

为此,有一次我在本特利大学的暑期学校,我们的第一个任务是在计算宇宙中寻找一个有趣的二维、三色、完全的细胞自动机(CA)。下面是我找到的 CA 的一些图片。有趣的是它们进化过程中的不规则边缘。

Code 144305877 of a 2-D Three-Color Totalistic Cellular Automaton at step 500

他们都有相似的特征,但有不同的边界和增长模式。

如果我们开始探索其中的一些规则空间,即在计算宇宙中搜索有趣的 CA,这是一种放松——它将你带到一个不同的外星景观和可能性的视觉空间。有些图案看起来可以用于设计目的,或者已经应用于纺织品等领域。视觉上,CA 可以很惊艳。在学术上,他们已经被证明产生了生物学上看到的模式。实际上,它们可以用来产生随机数。例如,从牛顿时代开始,方程就被用来描述物理现象。然而,越来越多的证据表明,CA 和其他类型的程序可能更好地模拟现实。基于代理的建模是一个很好的例子。但是正如托马斯·库恩在 科学革命的结构 中指出的,这种类型的进步经常在没有实时识别的情况下发生。相反,这是一个历史过程。

**

暑期学校的第一周主要包括讲座和史蒂夫·沃尔夫勒姆进行现场实验。在第二周,我们每个人都在午餐时与史蒂夫·沃尔夫勒姆见了面,这样他就可以选择我们将与导师一起工作的项目。他称这是他一年一度的“极端专业”时刻。

午餐真的很棒,因为有大约六个其他学生。斯蒂芬问了我们的兴趣和研究。这非常有趣,因为他对如此广泛的学科领域有如此多的问题。有理论物理学家,有人研究算法金融,还有另一个人研究在秀丽隐杆线虫神经元中发现的小世界网络。**

午餐后,我们分别会见了斯蒂芬和我们的导师。我走进房间的时候,他们心里已经有了一个项目,而且超级爽。我将使用 Wolfram 语言中的一个新特性,它使用 Reddit API。当我问斯蒂芬我到底应该在这个项目中做什么时,他回答说,“给我看看 Reddit 的社会学。”哇哦。这是一个很高的要求,但它让我可以自由地把这个项目带到我想去的任何地方。所以,我立即开始做一些研究。我最初想从用户生成的文本中找出一种使用五大心理模式来描述特定用户的方法。我还想使用网络文本分析来有效地描绘出子街道上发生的事情。在我的导师和其他一些学生的帮助下,我能够建立一些代码来生成一些网络。

我首先分析了一个 AMA(问我任何问题)史蒂夫·沃尔夫勒姆用一些非常简单的代码做了,得到了一个很好的网络。

Text network from a Stephen Wolfram AMA

在那里,我去掉了有向边,并在图中放置了一个工具提示,这样当你将鼠标悬停在节点上时,你就可以可视化它所代表的文本块(AMA 中的一个问题)。

Text network from Stephen Wolfram AMA with Tooltip function

因此,我进一步尝试了 Wolfram 语言和 Mathematica 中的一些图形/网络功能。在社会网络分析中,我们通常对相似性、派系、社会资本和其他度量感兴趣。Wolfram 语言有一个叫做 CommunityGraphPlot 的东西,可以很容易地将节点分组在一起。在这种情况下,它将把 AMA 提出的类似问题组合在一起。

Community graph plot of Stephen Wolfram AMA

为了进一步形象化,我们可以根据特定问题的投票分数来调整节点大小。

Community graph plot of a Stephen Wolfram AMA scaled by popularity.

我在那里做了各种各样的事情,比如使用一个内置的情感分类器,一个使用脸书数据作为训练集的 T2 主题分类器,并开始开发一个能够识别文本中某些社会心理特征的分类器。但那是另一篇文章。

正如你所看到的,在我的导师的出色帮助下,我能够在两周内完成这个项目。事实上,它是如此的有用,以至于当 IRB 由于诊所中健康相关问题的敏感性而撤回对我的 HIV/PrEP 工作的批准时,我还有一个论文项目要做。考虑到我是带着相当初级的编程能力进入学校的,我能够将这个项目进行下去是相当令人印象深刻的(至少我是这样认为的)。

作为奖励,你猜怎么着?我得到了 Wolfram Research 的一份工作,这些年来其他几位校友也是如此。所以,我不仅学到了有价值的新技能,还在一家科技公司找到了工作。

所以,如果你想在夏天找点事做,或者想学习如何编程,考虑一下 Wolfram 暑期学校或者在 Wolfram|Alpha 中玩玩开放代码函数。你永远不知道它会把你带到哪里,你甚至可能因此得到一份工作。

计算机视觉:TFLearn 的艰难时期

原文:https://towardsdatascience.com/compute-vision-hard-times-with-tflearn-4765841e90bf?source=collection_archive---------4-----------------------

我的故事从我玩够了 OpenCV 3.3.0 并决定更进一步尝试一些我从未尝试过的酷东西开始,所以 TensorFlow 是一个像 Thor's hammer 一样闪亮的大东西,每个人都想亲自动手,但不知道它是如何工作或做什么的(当然我们都听到过时髦的词——机器学习、数据科学、计算机视觉)。

我的工作机器是顶级 Macbook Pro,这意味着我有非常不同的体验(来自 ubuntu、fedora 等。)同时安装我工作需要的软件。当我开始使用 OpenCV 时,我使用了Python 3.6+OpenCV 3.3 docker image,这显然不太适合调试。所以,我决定让我的笔记本电脑有点“脏”(经常试图让笔记本电脑远离我开发所需的软件,这就是为什么我使用虚拟机,运行代码的容器)。

我做的第一件事是谷歌搜索“opencv 3 mac os ”,发现了几篇文章,不幸的是,github 问题试图通过安装 opencv,只要看看你应该做什么来安装 opencv 。跳到故事的结尾,我只能说一句话:“艰难时期带来了很多不必要的乐趣”。使用 OpenCV 最令人沮丧的部分是需要为您创建的每个 virtualenv 创建一个符号链接。尽管安装 OpenCV 的经历很糟糕,但我倾向于认为它是一个强大的工具——25 行代码的人脸识别,10-15 行代码的微笑识别。

我的遗愿清单中的下一项是基于 TensorFlow 的库— tflearn 。我需要感谢所有开发人员和做 TensorFlow 打包的人——你们做了大量的工作来使用户体验尽可能的流畅。

scipy
h5py
numpy
tensorflow
tflearn

这是使 TensorFlow 在本地工作所需安装的软件数量,但似乎没有现成的解决方案可以利用代码运行的机器的全部计算能力(如果您需要 CUDA GPU 能力或您的 CPU 支持 SSE4.2,AVX 指令—编译您自己的 TensorFlow 二进制文件)。TensorFlow 团队还为 Docker 维护了大量图片。

不幸的是, tflearn 部署并不理想,我甚至可以说——糟透了,因为社区没有明确的指导方针,尤其是对于版本控制。这种问题会导致像分割错误这样的严重错误。从一开始 tflearn 就在依赖版本方面遇到了麻烦,导致了大量与 segfault 相关的问题,以及 Stackoverflow 上大量请求帮助克服 segfault 关键问题的主题。这里有一个例子可以说明依赖性管理有多差:

$pip install tensorflow tflearn
...
Successfully installed bleach-1.5.0 html5lib-0.9999999 markdown-2.6.9 numpy-1.13.1 protobuf-3.4.0 six-1.10.0 tensorflow-1.3.0 tensorflow-tensorboard-0.1.6 werkzeug-0.12.2 Pillow-4.2.1 olefile-0.44 tflearn-0.3.2

在第一次启动时,您会看到您需要安装更多软件包:

$python
Python 3.6.2 (v3.6.2:5fd33b5926, Jul 16 2017, 20:11:06)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
import tflearn
hdf5 is not supported on this machine (please install/reinstall h5py for optimal experience)
Scipy not supported!

好吧,我明白为什么维护者这么做了,tflearn 可以在没有 hdf5 和 scipy 的情况下工作。但是实际上,大多数 ML 项目都在使用这个库,那么为什么不能让它们默认为必需的呢?无论如何,在安装完所有这些包后,我们有以下内容:

bleach==1.5.0
h5py==2.7.1
html5lib==0.9999999
Markdown==2.6.9
numpy==1.13.1
olefile==0.44
Pillow==4.2.1
protobuf==3.4.0
scipy==0.19.1
six==1.10.0
tensorflow==1.3.0
tensorflow-tensorboard==0.1.6
tflearn==0.3.2
Werkzeug==0.12.2

好吧,对维护者的另一个问题是,如果你建议用户安装特定的附加软件,为什么不指定你需要的版本?我认为我已经做得足够多了——安装了最新版本并构建了可以工作的 env,但是我是多么的鲁莽…

假设我的环境是可复制的,我可以构建一个 docker 映像,并让我的应用程序在其中运行。尽管有一个应用程序在我的机器上运行得很好,但每当我试图启动它时,Docker image 总是死机,而且,除了退出代码 139 之外,它一直在无声地死机。

在花了几个小时阅读、谷歌搜索之后,我找到了 github 的第七期。乍一看,它与 tflearn 毫无关系。但是据说尽量降级到tensor flow-1 . 0 . 0。好吧,我决定从头开始,除了记住我需要安装的使 tflearn 导入静默的包。最后,我列出了以下需求:

scipy==0.19.1
h5py==2.7.1
numpy==1.13.1
tensorflow==1.0.0
tflearn==0.3.2

令我惊讶的是,我的应用程序在 upstart 期间没有失败。

这意味着相对于 TensorFlow、SciPy、NumPy、h5py 的最新版本,tflearn 的最新版本被破坏。这不是开发者期望得到的。

作为结论,我只能说我在开始使用 TensorFlow 时获得的体验是非凡的,尽管花费了数小时为所有必要的包寻找可行的版本,但我倾向于同意 tflearn 带来了另一个高级 API 来构建深度神经网络模型并进行预测,这似乎非常有用(至少在我的特定情况下)。

计算机视觉及其为何如此困难

原文:https://towardsdatascience.com/computer-vision-and-why-it-is-so-difficult-1ed55e0d65be?source=collection_archive---------0-----------------------

Image used under standard license agreement, Depositphotos

最难理解的是那些基本的东西。我们生活在一个视觉世界。我们看到东西,立即明白它们是什么,以及我们可以用它们做什么。我们不仅可以识别它们,还可以了解它们的特殊属性和它们所属的类别。不需要深入思考,我们就可以看着一本书,理解构思、思考、写作、作家、代理人、编辑、出版社、印刷商、营销商、推销员、书店和经济的过程。这还不是围绕一本书所有关联的详尽列表。

更重要的是,我们每个人都可以看着一本书,理解它是什么,它做什么,即使我们对它的制作过程和周围发生的事情没有相同的细微差别的理解。我们的知识深度(或缺乏知识)只会在特定的环境中发挥作用(如图书大会或经济论坛),但对于大多数日常用途来说,我们中足够多的被称为“大多数”的人,将能够归因于使这本书成为一个实体的大多数关键属性。

如果一本书写得好,它可能是一本好书,但如果不是这样,它也是一个很好的入门读物。

因此,视觉实际上是一种知识,而不是眼睛,这就是事情变得非常有趣的地方。知识是基于对现实世界的接受,包括事实和想象的事物。例如,我们都同意哈利·波特是谁,他做了什么,为什么这么做,同时我们也都同意他是一个虚构的存在。这意味着,为了理解我们看到的东西,我们不仅使用演绎推理,即我们得出的结论是 100%真实的,我们还使用归纳推理,即我们从可能是真实的前提进行推断,并得出可能的结论:“如果一本书写得好,它可能是一部伟大的读物,但在情况并非如此的情况下,它也是一个伟大的门阶。”

在最后一句话中,我们不仅想象了成功和失败的例子,还想象了一个幽默和讽刺在描述品质中扮演角色的世界。计算机可以配备硬件来捕捉数据(在这种情况下是光),其光谱比我们的肉眼要宽得多,它们还可以配备算法来出色地解释这些数据,以至于仅仅通过研究光的模式,它们就可以学会看到拐角处的。计算机也可以用来进行演绎推理。

在澳大利亚墨尔本举行的 2017 年 AGI 第 10 届人工智能国际会议上,陆军实验室研究员 Douglas Summers Stay 提交了他的论文“语义嵌入知识图上的演绎推理”,摘要如下:“在连续语义向量空间中将知识表示为高维向量,可以帮助克服传统知识库的脆性和不完整性。我们提出了一种在这样的向量空间中直接执行演绎推理的方法,在推理链的每一步中以直接的方式结合了类比、联想和演绎,利用了来自不同来源和本体的知识。“——综合运用“类比、联想和演绎”以及“从不同来源和本体中汲取知识”进行推理这正是语义搜索的目的,请注意 Stay 指的是“一个连续的语义向量空间”,它反映了大脑在有限空间中实现无限存储容量的方式。

Image from US7519200B2 awarded to Google Inc shows the variables that must be recognized in order to assign a high degree of accuracy to the reading of a face by a computer. Image used under Fair Use Policy for educational purposes.

所以,现在听起来我们已经解决了这个问题。是也不是。是的,搜索已经变得非常擅长理解图像(有时是视频)中的对象,并建立一个索引,当它与上下文的知识库相结合时,可以创建一个非常好的智能搜索的感觉。特别是两项谷歌专利指出了如何做到这一点,让它看起来无缝。

这两者都突出了主要绊脚石的问题:即上下文,通过联想,归纳推理。因为知识不断进化和变形为包含意义但毫无意义的类比(就像我上面用一本写得很差的书作为门挡的例子),计算机逻辑在无人监督的情况下会遇到归纳推理,这也会影响计算机视觉,至少在涉及特定上下文的情况下。因此,拥有一辆自动驾驶汽车可能是可以的,其中车载遥测系统提供的视觉系统优于人类能够承受的任何东西(想象一下一辆可以透过雾“看到”的汽车,知道拐角处有什么,甚至可以利用交通传感器和交通报告来优化旅程),但机器人保姆更有问题(因为儿童天生不可预测,他们的不可预测性会给他们的安全带来风险)。

归纳推理和语义搜索

一项显示出一些前景的技术来自牛津大学的计算实验室,它创新性地采用了基于可能结果和相关性的归纳计算机推理以及语义搜索技术,以在高度不确定性的环境中提供更高的信息检索准确性。

Ref: d’Amato, Claudia & Fanizzi, Nicola & Fazzinga, Bettina & Gottlob, Georg & Lukasiewicz, Thomas. (2013). Uncertainty Reasoning for the Semantic Web. Volume II URSW 2008–2010/UniDL 2010. Images are the copyright of their respective owners. Used under Fair Use Policy for educational purposes.

一切都是由数据组成的,数据受制于四个向量:数量、速度、多样性和准确性,我在关于谷歌语义搜索的书中详细介绍了这四个向量。这四个向量中的每一个都提出了自己的挑战,这些挑战因我们将放在网上的数据量不断增加而进一步加剧。

在计算机视觉(以及搜索中的物体识别)中,我们要求比人类更高的标准。机器犯的错误削弱了我们对它们的信任,因为与人类不同,我们通常看不到它们是如何失败的。结果,它们的失败变得普遍,我们认为一台机器(无论是搜索算法还是机器人)存在根本性的缺陷。对于人类,我们通常理解失败,因为我们可以根据自己的知识、记忆和技能参数来模拟他们的表现。失败变得可以接受,因为我们完全理解人类能力的极限。

人工智能在特定和一般意义上的发展都与破解计算机视觉挑战密切相关。毕竟,真正地视觉大多是的精神。

我最新的一本书: 《狙击思维:消除恐惧,应对不确定性,做出更好的决策》是一项神经科学研究,探讨如何运用实际步骤做出更好的决策。

吴恩达的计算机视觉——11 个教训

原文:https://towardsdatascience.com/computer-vision-by-andrew-ng-11-lessons-learned-7d05c18a6999?source=collection_archive---------0-----------------------

我最近在 Coursera 上完成了吴恩达的计算机视觉课程。Ng 在解释优化任何计算机视觉任务所需的许多复杂想法方面做得非常出色。本课程中我最喜欢的部分是神经风格转换部分(见第 11 课),它允许你创作结合了克劳德·莫奈风格和你喜欢的任何图像内容的艺术品。这是一个你能做什么的例子:

在这篇文章中,我将讨论我在课程中学到的 11 个关键教训。注意,这是 deeplearning.ai 发布的深度学习专业化中的第四门课程,如果你想了解前 3 门课程,我建议你看看这个博客。

第一课:为什么计算机视觉正在腾飞?

大数据和算法的发展将导致智能系统的测试误差收敛到贝叶斯最优误差。这将导致 AI 在所有领域超越人类水平的表现,包括自然感知任务。TensorFlow 的开源软件允许您使用迁移学习非常快速地实现任何对象的对象检测系统。使用迁移学习,你只需要 100-500 个例子,这个系统就能相对良好地工作。手动标记 100 个例子并不是太多的工作,所以你很快就会有一个最小的可行产品。

第二课:卷积如何工作?

Ng 解释了如何实现卷积运算符,并展示了它如何检测图像中的边缘。他还解释了其他滤波器,如 Sobel 滤波器,它将更多的权重放在边缘的中心像素上。Ng 接着解释说,滤波器的权重不应该手动设计,而应该使用爬山算法(如梯度下降)来学习。

第三课:为什么是卷积?

Ng 给出了卷积在图像识别任务中如此有效的几个哲学原因。他概述了两个具体原因。第一种称为参数共享。它的思想是,在图像的一部分有用的特征检测器可能在图像的另一部分也有用。例如,边缘检测器可能在图像的许多部分都有用。参数的共享允许参数的数量较小,并且还允许稳健的平移不变性。平移不变性是这样一个概念,一只猫移动和旋转后仍然是一只猫的图片。

他概述的第二个观点被称为连接稀疏。这意味着每个输出层只是少量输入(特别是滤波器大小的平方)的函数。这大大减少了网络中的参数数量,并允许更快的训练。

第三课:为什么填充?

填充通常用于保持输入大小(即输入和输出的维度相同)。它还用于使靠近图像边缘的帧与靠近中心的帧对输出的贡献一样大。

第 4 课:为什么是最大池?

通过实证研究,最大池已被证明是非常有效的 CNN 的。通过对图像进行下采样,我们减少了参数的数量,这使得特征对于尺度或方向变化是不变的。

第 5 课:经典网络架构

Ng 展示了 3 种经典网络架构,包括 LeNet-5、AlexNet 和 VGG-16。他提出的主要观点是,有效的网络通常具有通道尺寸增加、宽度和高度减小的层。

第六课:ResNets 为什么有效?

对于平面网络,由于梯度的消失和爆炸,训练误差不会随着层数的增加而单调减小。这些网络具有前馈跳过连接,这允许您在不降低性能的情况下训练非常大的网络。

第七课:使用迁移学习!

在 GPU 上从头开始训练大型网络(如 inception)可能需要数周时间。您应该从预训练的网络中下载权重,并只重新训练最后一个 softmax 层(或最后几层)。这将大大减少培训时间。这样做的原因是早期的图层往往与所有图像中的概念相关联,例如边缘和曲线。

第八课:如何赢得计算机视觉竞赛

Ng 解释说,你应该独立地训练几个网络,并平均它们的输出,以获得更好的性能。诸如随机裁剪图像、围绕水平和垂直轴翻转图像等数据增强技术也可能有助于提高性能。最后,您应该使用一个开源的实现和预训练的模型来开始,然后为您的特定应用程序微调参数。

第 9 课:如何实现对象检测

Ng 首先解释了图像中地标检测的概念。基本上,这些地标成为你的训练输出例子的一部分。通过一些巧妙的卷积操作,你可以得到一个输出体积,告诉你物体在某个区域的概率以及物体的位置。他还解释了如何使用并集上的交集公式来评估对象检测算法的有效性。最后,Ng 将所有这些要素放在一起解释了著名的 YOLO 算法。

第十课:如何实现人脸识别

面部识别是一次性学习问题,因为你可能只有一个示例图像来识别人。解决方案是学习给出两幅图像之间差异程度的相似性函数。因此,如果图像是同一个人的,您希望该函数输出一个小数字,反之亦然。

Ng 给出的第一个解决方案被称为“连体网络”。这个想法是将两个人分别输入到同一个网络中,然后比较他们的输出。如果输出是相似的,那么这些人可能是相同的。训练网络,使得如果两个输入图像是同一个人,则它们的编码之间的距离相对较小。

他给出的第二个解决方案使用了三重态损失法。这个想法是,你有一个三个一组的图像(锚(A),积极(P)和消极(N)),你训练网络,使 A 和 P 之间的输出距离比 A 和 N 之间的距离小得多。

第十一课:如何使用神经风格转移来创作艺术品

Ng 解释了如何生成内容和风格相结合的图像。参见下面的例子。

神经类型转移的关键是理解卷积网络中每一层所学内容的视觉表示。原来,早期的图层学习简单的特征,如边缘,而后期的特征学习复杂的对象,如人脸,脚和汽车。

为了构建神经风格转移图像,您只需定义一个成本函数,它是内容和风格相似性的凸组合。特别地,成本函数将是:

J(G) = alpha * J_content(C,G) + beta * J_style(S,G)

其中 G 是生成的图像,C 是内容图像,S 是样式图像。学习算法简单地使用梯度下降来最小化关于生成的图像 g 的成本函数

步骤如下:

  1. 随机生成 G。
  2. 用梯度下降法使 J(G)最小,即写出 G := G-dG(J(G))。
  3. 重复步骤 2。

结论

通过完成本课程,你将对大量的计算机视觉文献有一个直观的了解。家庭作业也给了你实践这些想法的机会。完成本课程后,你不会成为计算机视觉方面的专家,但本课程可能会启动你在计算机视觉方面的潜在想法/职业。

如果你有任何有趣的计算机视觉应用想分享,请在下面的评论中告诉我。我很乐意讨论新项目的潜在合作。

这就是所有人——如果你已经做到了这一步,请在下面评论并在 LinkedIn 上添加我。

我的 Github 是这里的。

计算机视觉概念和术语

原文:https://towardsdatascience.com/computer-vision-concepts-and-terminology-edd392a6f594?source=collection_archive---------6-----------------------

一个超像素是一个比矩形片更好地对准强度边缘的图像片。超像素可以用任何分割算法提取,然而,它们中的大多数产生高度不规则的超像素,具有广泛变化的大小和形状。可能需要更规则的空间镶嵌。

- 最大抑制通常与边缘检测算法一起使用。沿着图像梯度方向扫描图像,如果像素不是局部最大值的一部分,则它们被设置为零。

语义分割:

https://arxiv.org/pdf/1602.06541.pdf

从图像中提取超像素是这一任务或前景-背景分割的一个例子。语义分割:在语义分割中,你要给每个像素标上一类物体(汽车,人,狗,…)和非物体(水,天空,道路,…)。

语义分割是将属于同一对象类的图像部分聚集在一起的任务。这种类型的算法有几个用例,如检测路标[MBLAGJ+07],检测肿瘤[MBVLG02],检测手术中的医疗器械[WAH97],结肠隐窝分割[CRSS14],土地利用和土地覆盖分类[HDT02]。相反,非语义分割仅基于单个对象的一般特征将像素聚集在一起。因此,非语义分割的任务是不明确的

光流

光流是由物体或摄像机的运动引起的图像物体在两个连续帧之间的表观运动模式。这是 2D 矢量场,其中每个矢量是一个位移矢量,表示点从第一帧到第二帧的移动。考虑下面的图像,它显示了一个球在连续 5 帧中移动。箭头显示了它的位移矢量。

It shows a ball moving in 5 consecutive frames. The arrow shows its displacement vector.

光流在以下领域有许多应用:

  • 运动中的结构
  • 视频压缩
  • 视频稳定…

光流基于几个假设:

  1. 物体的像素强度在连续的帧之间不会改变。
  2. 相邻像素具有相似的运动。

方向梯度直方图(HOG)

[## 猪人探测器教程克里斯麦考密克

猪人检测器非常容易理解(例如,与 SIFT 对象识别相比)。其中一个…

mccormickml.com](http://mccormickml.com/2013/05/09/hog-person-detector-tutorial/)

HOG 代表方向梯度直方图。HOG 是一种“特征描述符”。特征描述符的目的是以这样一种方式概括对象,当在不同条件下观察时,相同的对象(在这种情况下是人)产生尽可能接近相同的特征描述符。这使得分类任务更加容易。

这种方法的创造者训练了一种支持向量机(一种用于分类的机器学习算法),或“SVM”,以识别人的 HOG 描述符。猪人检测器使用滑动检测窗口,该窗口在图像周围移动。在检测器窗口的每个位置,为检测窗口计算 HOG 描述符。然后,这个描述符被显示给经过训练的 SVM,它将其分类为“人”或“不是人”。

为了识别不同尺度的人,图像被二次抽样成多种尺寸。搜索这些二次抽样图像中的每一个。

SIFT(尺度不变特征变换)

[## OpenCV:SIFT(尺度不变特征变换)简介

因此,在 2004 年,英国哥伦比亚大学的 D.Lowe 提出了一种新的算法,尺度不变特征变换…

docs.opencv.org](http://docs.opencv.org/3.1.0/da/df5/tutorial_py_sift_intro.html)

SIFT:尺度不变特征变换(SIFT)特征描述符描述图像中的关键点。拍摄关键点周围大小为 16 × 16 的图像块。该面片被分成大小为 4 × 4 的 16 个不同部分。对于这些部分中的每一个,计算类似于 HOG 特征的 8 个方向的直方图。这导致每个关键点的 128 维特征向量。应该强调的是,SIFT 是完整图像的全局特征。

BOV:视觉词汇袋(BOV),

http://kushalvyas.github.io/BOV.html

也称为关键点包,基于矢量量化。与 HOG 特征类似,BOV 特征是直方图,它计算图像的一个片内特定模式的出现次数

Poselets:

Poselets 依赖于手动添加的额外关键点,例如“右肩”、“左肩”、“右膝”和“左膝”。它们最初用于人体姿态估计。对于像人类这样众所周知的图像类别,找到这些额外的关键点是很容易的。然而,对于像飞机、轮船、器官或细胞这样的类,人类注释者不知道关键点是很困难的。此外,必须为每个单独的类选择关键点。有一些策略可以处理这些问题,比如视点相关的关键点。Poselets 在[BMBM10]中用于检测人,在[BBMM11]中用于 PASCAL VOC 数据集的一般对象检测。

文本:

一个粒子是视觉的最小组成部分。计算机视觉文献并没有给出纹理子的严格定义,但边缘检测器可能是一个例子。有人可能会说,卷积神经网络(CNN)的深度学习技术在第一个过滤器中学习文本子。

马尔可夫随机场

https://ermongroup . github . io/cs 228-notes/re presentation/un directed/

是计算机视觉中广泛使用的无向概率图模型。MRFs 的总体思想是为每个特征分配一个随机变量,为每个像素分配一个随机变量

白粉

http://mccormickml . com/2014/06/03/深度学习-教程-PCA-and-whiting/

我们试图通过美白完成两件事:

  1. 使特征之间的相关性降低。
  2. 给所有的特征相同的方差。

美白有两个简单的步骤:

  1. 将数据集投影到特征向量上。这将旋转数据集,以便组件之间没有相关性。
  2. 将数据集归一化,使所有组件的方差为 1。这可以通过简单地将每个分量除以其特征值的平方根来实现。

我问了一个神经网络 expect,我连接的是 Pavel Skribtsov ,关于为什么这种技术是有益的更多解释:

“这是简化优化过程以找到权重的常用技巧。如果输入信号具有相关输入(某种线性相关性),那么[成本]函数将倾向于具有“类似河流”的最小区域,而不是权重空间中的最小点。至于输入白化——类似的事情——如果不这样做,误差函数将倾向于具有非对称的最小值“洞穴”,并且由于一些训练算法对所有权重具有相等的更新速度——最小化可能倾向于跳过最小值的窄维度中的好位置,同时试图取悦更宽的维度。所以和深度学习没有直接关系。如果您的优化过程收敛良好,您可以跳过这一预处理。”

医学图像的计算机视觉特征提取 101 第 1 部分:边缘检测/锐化/模糊/浮雕/超像素

原文:https://towardsdatascience.com/computer-vision-feature-extraction-101-on-medical-images-part-1-edge-detection-sharpening-42ab8ef0a7cd?source=collection_archive---------5-----------------------

Gif from this website

所以今天,我只想回顾一下计算机视觉中的一些核心概念,我希望把重点放在应用上,而不是理论上。如果你是计算机视觉的新手,我强烈推荐你观看我在下面链接的这个视频系列来了解这个理论。(视频系列很长,但是真的很好看。)

此外,这是我们将要使用的公开可用数据的列表。
1。 乳腺癌细胞来自 UCSB 生物分割基准数据集
2。 细胞 2D 来自 UCSB 生物分割基准数据集
3。 驱动:用于血管提取的数字视网膜图像
4。 从 kaggle 数据集进行超声神经分割
5。 脑部核磁共振来自 pixabay
6。 脑部核磁共振来自 pixabay
7。 来自 pixabay 的自然图像

请注意,这篇文章是让我回顾计算机视觉中的一些基本概念。

2D 卷积

Image from this website

卷积是对两个*【f 和 g】进行一个* 的数学运算 产生第三个函数,也就是通常被视为原始函数之一的修改版,赋予 的积分逐点将两个函数的 相乘作为其中一个函数的量**

2D 卷积运算是计算机视觉的核心,因为这是我们将要使用的主要运算,所以请确保您理解这个概念。如果您需要帮助,请单击此处查看由 Song Ho Ahn 提供的 2D 卷积运算的分步示例。

原始图像

****

左上来自 UCSB 的乳腺癌细胞生物分割基准
中上来自 UCSB 的细胞 2D 生物分割基准数据集
右上驱动:用于血管提取的数字视网膜图像
左下来自 kaggle 数据集的超声神经分割【图

在我们对这些图像做任何事情之前,让我们实际上看看每个图像看起来像什么,最后下面是我们的[自然图像](http://Natural Image from pixabay)看起来像什么。

身份内核

让我们从简单的开始,身份内核不改变给定的图像,因为它是身份内核。如下图所示,图像看起来完全一样,除了所有图像都是黑白的。

边缘检测(水平)

现在让我们看看每张图片的水平方向有多少变化。令我印象深刻的一个非常有趣的观察是自然图像,我们可以看到来自照明光束的光无处可寻。

边缘检测(垂直)

现在让我们看看在垂直方向上对每张图片所做的修改。我们可以观察到光束在自然图像中返回。

边缘检测(梯度幅度)

因为我们现在在水平方向和垂直方向都有变化,我们可以用平方和的平方根来得到梯度的大小。请看下面这个维基页面上的方程式的截图。

Image from this website

边缘检测(渐变方向)

因为我们现在在水平方向和垂直方向都有变化,我们可以通过使用反正切函数获得梯度方向。请参见下面的方程式。

Image from this website

当我们可视化原始结果时,我们会得到如下结果。看起来非常奇怪的图像…

索贝尔滤波器(梯度幅度)

现在,让我们采取一个索贝尔过滤器,并获得梯度幅度。我个人看不出我们使用的过滤器有什么大的不同。

索贝尔滤镜(渐变方向)

同样,对于梯度方向,我看不出我们使用的过滤器和索贝尔过滤器之间有很大的区别。

高斯模糊

我们也可以模糊图像,通过使用高斯滤波器,如果我们看到这个滤波器,它看起来会像下面这样。这个操作通常被称为高斯模糊。

Image from this website

锐化边缘

我们还可以锐化图像,首先找到图像的边缘,然后将该值添加到原始图像中。这将锐化图像的边缘。

锐化图像非常类似于寻找边缘,将原始图像和边缘检测后的图像彼此相加,结果将是一个边缘得到增强的新图像,使其看起来更清晰。——洛德的计算机图形学教程

浮雕

为了给图像一个阴影效果,我们也可以使用浮雕滤镜,并添加 128 的偏差。我其实不知道这个滤镜,直到我从 Lode 的计算机图形学教程上看到。结果看起来非常类似于水平方向上的边缘检测。

"一个浮雕滤镜给图像一个 3D 阴影效果,这个结果对于图像的凹凸贴图非常有用。它可以通过在中心的一侧取一个像素,并从中减去另一侧的一个像素来实现。像素可以得到正的或负的结果。要将负像素用作阴影,将正像素用作光线,对于 bumpmap,图像的偏移为 128。——洛德的计算机图形学教程。

超级像素

最后,为了好玩,让我们使用 skimage 库中实现的简单线性迭代聚类制作超级像素。

互动码

对于 Google Colab,您需要一个 Google 帐户来查看代码,而且您不能在 Google Colab 中运行只读脚本,所以请在您的操场上创建一个副本。最后,我永远不会请求允许访问你在 Google Drive 上的文件,仅供参考。编码快乐!

请注意,我不想滥用任何数据政策。所以对于谷歌 Colab,我找到了下面的图片,它被标记为重复使用。要访问在线代码,请点击此处。

Image from this website

最后的话

我想发这个帖子已经很久了,很高兴知道我终于发了。我希望尽快再发一篇关于猪特征或者筛特征的帖子。

如果发现任何错误,请发电子邮件到 jae.duk.seo@gmail.com 给我,如果你想看我所有写作的列表,请在这里查看我的网站。

同时,在我的推特上关注我这里,访问我的网站,或者我的 Youtube 频道了解更多内容。如果你感兴趣的话,我还做了解耦神经网络的比较。

参考

  1. 接替艾伦。(2018).YouTube。2018 年 4 月 9 日检索,来自https://www.youtube.com/watch?v=2S4nn7S8Hk4&list = pltizwl 5 stv 3d 4 uq 6 pvzxkrlkp _ 3 xocotn
  2. 安,S. (2018)。2D 卷积的例子。Songho.ca 于 2018 年 4 月 9 日检索,来自http://www . song ho . ca/DSP/convolution/convolution 2d _ example . html
  3. 内核(图像处理)。(2018).En.wikipedia.org。检索于 2018 年 4 月 9 日,来自https://en . Wikipedia . org/wiki/Kernel _(image _ processing)
  4. 图像压花。(2018).En.wikipedia.org。检索于 2018 年 4 月 9 日,来自 https://en.wikipedia.org/wiki/Image_embossing
  5. 罗斯布鲁克(2014 年)。用 Python 访问单个超像素分割。PyImageSearch。检索于 2018 年 4 月 9 日,来自https://www . pyimagesearch . com/2014/12/29/access-individual-super pixel-segmentations-python/
  6. 生物分割|生物图像信息学中心|加州大学圣巴巴拉分校。(2018).Bioimage.ucsb.edu。检索于 2018 年 4 月 9 日,来自https://bioimage.ucsb.edu/research/bio-segmentation
  7. 驱动:下载。(2018).isi . uu . nl . 2018 年 4 月 9 日检索,来自http://www.isi.uu.nl/Research/Databases/DRIVE/download.php
  8. 超声波神经分割| Kaggle。(2018).Kaggle.com。检索于 2018 年 4 月 9 日,来自https://www.kaggle.com/c/ultrasound-nerve-segmentation
  9. Pixabay 上的免费图像——核磁共振成像,磁性,x 光,头骨,头部。(2018).Pixabay.com。检索于 2018 年 4 月 9 日,来自https://pix abay . com/en/MRI-magnetic-x-ray-skull-head-782457/
  10. Pixabay 上的免费图像——日落、灯塔、黎明、黄昏、太阳。(2018).Pixabay.com。检索于 2018 年 4 月 9 日,来自https://pix abay . com/en/sunset-light house-dawn-dusk-sun-3120484/
  11. Pixabay 上的免费图像——核磁共振成像,磁性,x 光,头骨,头部。(2018).Pixabay.com。检索于 2018 年 4 月 9 日,来自https://pix abay . com/en/MRI-magnetic-x-ray-skull-head-782459/
  12. Python 中的 DICOM:用 PyDICOM 和 VTK 将医学图像数据导入 NumPy。(2014).PyScience。检索于 2018 年 4 月 9 日,来自https://pyscience . WordPress . com/2014/09/08/DICOM-in-python-importing-medical-image-data-into-numpy-with-pydicom-and-VTK/
  13. Matplotlib 图:移除轴,l. (2018)。Matplotlib 绘图:删除轴、图例和空白。Stackoverflow.com。检索于 2018 年 4 月 9 日,来自https://stack overflow . com/questions/9295026/matplotlib-plots-removed-axis-legends-and-white-spaces
  14. SciPy . signal . convolved 2d—SciPy v 1 . 0 . 0 参考指南。(2018).Docs.scipy.org。2018 年 4 月 9 日检索,来自https://docs . scipy . org/doc/scipy/reference/generated/scipy . signal . convolved . html
  15. matplotlib,R. (2018 年)。在 matplotlib 中删除保存的图像周围的空白。Stackoverflow.com。检索于 2018 年 4 月 9 日,来自https://stack overflow . com/questions/11837979/removing-white-space-around-a-saved-image-in-matplotlib
  16. NumPy . arctan—NumPy 1.14 版手册。(2018).Docs.scipy.org。2018 年 4 月 9 日检索,来自https://docs . scipy . org/doc/numpy/reference/generated/numpy . arctan . html
  17. 空间过滤器-高斯平滑。(2018).Homepages.inf.ed.ac.uk 检索于 2018 年 4 月 9 日,来自https://homepages.inf.ed.ac.uk/rbf/HIPR2/gsmooth.htm
  18. SciPy . n image . filters . Gaussian _ filter—SciPy v 0 . 15 . 1 参考指南。(2018).Docs.scipy.org。2018 年 4 月 9 日检索,来自https://docs . scipy . org/doc/scipy-0 . 15 . 1/reference/generated/scipy . ndimage . filters . Gaussian _ filter . html
  19. 高斯模糊。(2018).En.wikipedia.org。检索于 2018 年 4 月 9 日,来自https://en.wikipedia.org/wiki/Gaussian_blur
  20. 模块:分段—对 0.14 版开发文档进行分段。(2018).Scikit-image.org。检索于 2018 年 4 月 9 日,来自http://scikit-image . org/docs/dev/API/skim age . segmentation . html # skim age . segmentation . slic

医学图像的计算机视觉特征提取 101 第 2 部分:同一性、平移、缩放、剪切、旋转和同质性

原文:https://towardsdatascience.com/computer-vision-feature-extraction-101-on-medical-images-part-2-identity-translation-scaling-90d160bcd41e?source=collection_archive---------7-----------------------

Gif from this website

所以今天,我想继续回顾计算机视觉的基础知识(我猜这篇文章可能更多的是关于图像处理)。今天我想回顾一下图像转换。(或矩阵)

此外,这是我们将要使用的公开可用数据的列表。
1。 乳腺癌细胞来自 UCSB 生物分割基准数据集
2。 细胞 2D 来自 UCSB 生物分割基准数据集
3。 驱动:用于血管提取的数字视网膜图像
4。 从 kaggle 数据集进行超声神经分割
5。 脑部核磁共振来自 pixabay
6。 脑部核磁共振来自 pixabay
7。 来自 pixabay 的自然图像

请注意,这个帖子是给未来的自己回过头来看资料的。

使用矩阵进行转换

Image from this website

在这篇文章中,如果你想复习这些运算,理解矩阵和矩阵乘法是很重要的。请点击这里。

原始图片

左上来自 UCSB 的乳腺癌细胞生物分割基准
中上来自 UCSB 的细胞 2D 生物分割基准数据集
右上驱动:用于血管提取的数字视网膜图像
左下来自 kaggle 数据集的超声神经分割【图

在我们对这些图像做任何事情之前,让我们实际上看看每个图像看起来像什么,最后下面是我们的[自然图像](http://Natural Image from pixabay)看起来像什么。

身份

让我们从简单的开始,用单位矩阵来变换图像。我们可以看到原始图像和变换后的图像没有区别。(因为我们使用了单位矩阵…)

翻译

现在让我们将图像向 x(水平方向)和 y(垂直方向)移动 100 像素。因为我们可以怀疑图像已经向右下方移动了 100 个像素。

缩放比例

这里,我们将使图像在 x 方向(水平方向)缩小 0.75 倍,而在 y 方向(垂直方向)放大 1.25 倍。正如我们所猜测的,由于图像在 y 方向上增长,一些信息被删除了。

剪切

现在让我们做一些更有趣的事情,让剪切图像。我们绝对可以看到这些图像变换操作的威力有多大。

旋转

现在我们可以旋转图像,理解旋转的变换矩阵可能有点困难。但是使用下面的等式,我们可以很容易地构建旋转矩阵。

Image from this website

同质

结合我们学过的所有东西,现在我们甚至可以做透视变换,在考虑 3D 空间的同时变换 2D 坐标。结果真的很酷。

互动码

对于谷歌实验室,你需要一个谷歌帐户来查看代码,你也不能在谷歌实验室运行只读脚本,所以在你的操场上做一个副本。最后,我永远不会请求允许访问你在 Google Drive 上的文件,仅供参考。编码快乐!

请注意,我不想滥用任何数据政策。所以对于谷歌 Colab,我找到了下面的图片,它被标记为重复使用。要访问在线代码,请点击此处。

Image from this website

遗言

对图像进行仿射变换总是很有趣,因为有些结果看起来很滑稽。

如果发现任何错误,请发电子邮件到 jae.duk.seo@gmail.com 给我,如果你想看我所有写作的列表,请在这里查看我的网站。

与此同时,请在我的 twitter 这里关注我,并访问我的网站或我的 Youtube 频道了解更多内容。如果你感兴趣的话,我还做了解耦神经网络的比较。

参考

  1. 剪切映射。(2018).En.wikipedia.org。检索于 2018 年 4 月 11 日,来自https://en.wikipedia.org/wiki/Shear_mapping
  2. 接替艾伦。(2018).YouTube。2018 年 4 月 9 日检索,来自https://www.youtube.com/watch?v=2S4nn7S8Hk4&list = pltizwl 5 stv 3d 4 uq 6 pvzxkrlkp _ 3 xocotn
  3. 安,S. (2018)。2D 卷积的例子。Songho.ca 于 2018 年 4 月 9 日检索,来自http://www . song ho . ca/DSP/convolution/convolution 2d _ example . html
  4. 内核(图像处理)。(2018).En.wikipedia.org。检索于 2018 年 4 月 9 日,来自https://en . Wikipedia . org/wiki/Kernel _(image _ processing)
  5. 图像压花。(2018).En.wikipedia.org。检索于 2018 年 4 月 9 日,来自 https://en.wikipedia.org/wiki/Image_embossing
  6. 罗斯布鲁克(2014 年)。用 Python 访问单个超像素分割。PyImageSearch。检索于 2018 年 4 月 9 日,来自https://www . pyimagesearch . com/2014/12/29/access-individual-super pixel-segmentations-python/
  7. 生物分割|生物图像信息学中心|加州大学圣巴巴拉分校。(2018).Bioimage.ucsb.edu。检索于 2018 年 4 月 9 日,来自https://bioimage.ucsb.edu/research/bio-segmentation
  8. 驱动:下载。(2018).isi . uu . nl . 2018 年 4 月 9 日检索,来自http://www.isi.uu.nl/Research/Databases/DRIVE/download.php
  9. 超声波神经分割| Kaggle。(2018).Kaggle.com。检索于 2018 年 4 月 9 日,来自https://www.kaggle.com/c/ultrasound-nerve-segmentation
  10. Pixabay 上的免费图像——核磁共振成像,磁性,x 光,头骨,头部。(2018).Pixabay.com。检索于 2018 年 4 月 9 日,来自https://pix abay . com/en/MRI-magnetic-x-ray-skull-head-782457/
  11. Pixabay 上的免费图像——日落、灯塔、黎明、黄昏、太阳。(2018).Pixabay.com。检索于 2018 年 4 月 9 日,来自https://pix abay . com/en/sunset-light house-dawn-dusk-sun-3120484/
  12. Pixabay 上的免费图像——核磁共振成像,磁性,x 光,头骨,头部。(2018).Pixabay.com。检索于 2018 年 4 月 9 日,来自https://pix abay . com/en/MRI-magnetic-x-ray-skull-head-782459/
  13. 图像的几何变换— OpenCV-Python 教程 1 文档。(2018).opencv-python-tutro als . readthedocs . io . 2018 年 4 月 11 日检索,来自http://opencv-python-tutro als . readthedocs . io/en/latest/py _ tutorials/py _ img proc/py _ geometric _ transformations/py _ geometric _ transformations . html
  14. 仿射变换。(2018).En.wikipedia.org。检索于 2018 年 4 月 11 日,来自 https://en.wikipedia.org/wiki/Affine_transformation
  15. Mallick,S. (2016 年)。使用 OpenCV ( Python / C ++)的单应性例子|学习 OpenCV。Learnopencv.com。检索于 2018 年 4 月 11 日,来自https://www . learnopencv . com/homography-examples-using-opencv-python-c/
  16. 齐次坐标。(2018).En.wikipedia.org。检索于 2018 年 4 月 11 日,来自https://en . Wikipedia . org/wiki/Homogeneous _ coordinates # Use _ in _ computer _ graphics
  17. (2018).Cs.utexas.edu。检索于 2018 年 4 月 11 日,来自https://www . cs . ut exas . edu/users/fussell/courses/cs 384g-fall 2011/lectures/lectures 07-affine . pdf
  18. 图像的几何变换— OpenCV-Python 教程 1 文档。(2018).opencv-python-tutro als . readthedocs . io . 2018 年 4 月 11 日检索,来自http://opencv-python-tutro als . readthedocs . io/en/latest/py _ tutorials/py _ img proc/py _ geometric _ transformations/py _ geometric _ transformations . html
  19. (2018).Web.cs.wpi.edu。检索于 2018 年 4 月 11 日,来自https://web . cs . wpi . edu/~ Emmanuel/courses/cs 545/S14/slides/lecture 11 . pdf
  20. (2018).Mrl.nyu.edu。检索于 2018 年 4 月 11 日,来自http://mrl.nyu.edu/~dzorin/ig04/lecture05/lecture05.pdf
  21. (2018).Colab.research.google.com。检索于 2018 年 4 月 11 日,来自https://colab . research . Google . com/notebook # fileId = 1 onuy 6 EFE 7 xhdfgfahddcqqxpwuetoj _ NO&scroll to = 2 Sood 3 odxg _
  22. 矩阵乘法。(2018).En.wikipedia.org。检索于 2018 年 4 月 11 日,来自 https://en.wikipedia.org/wiki/Matrix_multiplication
  23. 使用矩阵的变换(几何、变换)— Mathplanet。(2018).数学星球。检索于 2018 年 4 月 11 日,来自https://www . math planet . com/education/geometry/transformations/transformation-using-matrix

医学图像的计算机视觉特征提取 101 第 3 部分:高斯差分和高斯拉普拉斯算子

原文:https://towardsdatascience.com/computer-vision-feature-extraction-101-on-medical-images-part-3-difference-of-gaussian-and-b3cbe5c37415?source=collection_archive---------2-----------------------

Gif from this website

这是我试图回忆我在计算机视觉中学到的东西的另一个帖子。由于这是期末考试季节,我真的不想做一些疯狂的事情,因此狗和日志过滤器。

如果你想看第一部分,请点击这里;如果你想看第二部分,请点击这里。

此外,这是我们将要使用的公开可用数据的列表。
1。 乳腺癌细胞来自 UCSB 生物分割基准数据集
2。 细胞 2D 来自 UCSB 生物分割基准数据集
3。 驱动:用于血管提取的数字视网膜图像
4。 超声波神经分割从 kaggle 数据集
5。 脑部核磁共振来自 pixabay
6。 脑 MRI 来自 pixabay
7。 来自 pixabay 的自然图像

请注意,这个帖子是给未来的自己回过头来看资料的。

2D 卷积

Image from this website

卷积是对两个*【f 和 g】进行一个* 的数学运算 产生第三个函数,也就是通常被视为原始函数之一的修改版,赋予 的积分逐点将两个函数的 相乘作为其中一个函数的量**

2D 卷积运算是计算机视觉的核心,因为这是我们在这篇文章中要用到的主要运算,所以请确保你理解这个概念。如果您需要帮助,请单击此处查看由 Song Ho Ahn 提供的 2D 卷积运算的分步示例。

原始图像

****

左上来自 UCSB 的乳腺癌细胞生物分割基准
中上来自 UCSB 的细胞 2D 生物分割基准数据集
右上驱动:用于血管提取的数字视网膜图像
左下来自 kaggle 数据集的超声神经分割【图

在我们对这些图像做任何事情之前,让我们实际上看看每个图像看起来像什么,最后下面是我们的[自然图像](http://Natural Image from pixabay)看起来像什么。

高斯差分

红框 →与原高斯图像进行卷积运算 1
绿框 →与原高斯图像进行卷积运算 2
蓝框 →将上述两部分图像相减,并设定阈值。

****请注意我们实际上需要找到过零事件,而不是设置硬阈值。但是为了简单起见,我使用了一个阈值。

顾名思义,高斯差分非常简单。只需用不同的高斯核对图像进行卷积,在上面的例子中,我们选择使用两个不同窗口大小的不同高斯滤波器。然后彼此相减,并设置一个阈值来过滤掉强度较弱的像素。

Image from this website.

红框 →与高斯原图像的卷积运算 1
绿框 →与高斯原图像的卷积运算 2
蓝框 →减去以上两部分图像

除了阈值,上面的图片很好地解释了一步一步的教程。

高斯 v2 的差值

红框 →选择 sigma 值为 100 的高斯核
绿框 →选择 sigma 值为 1 的高斯核

如上所述,为高斯核选择完全不同的 sigma 值会产生可怕的结果。(我只是想试试这些好玩的。)

高斯拉普拉斯算子(带平滑)

红框 →使用高斯滤波器平滑图像
绿框 →创建拉普拉斯滤波器进行卷积运算。

图像的拉普拉斯算子突出了快速强度变化的区域,因此经常用于边缘检测(参见 零交叉边缘检测器 )。拉普拉斯算子通常被应用于首先用近似 高斯平滑滤波器 平滑的图像,以降低其对噪声的敏感性。——引自本网

从上面的陈述中,我们已经可以知道,在做任何事情之前,我们需要首先平滑图像。让我们假设我们已经平滑了图像,看看我们下一步应该做什么。

Image from this website

现在我们知道,我们只需要使用其中一个内核来执行卷积,我将使用左边的内核。

高斯的拉普拉斯算子(无平滑)

现在我听说高斯的拉普拉斯对噪声敏感,我们在做任何事情之前平滑图像。为了好玩,让我们只应用拉普拉斯过滤器,而不平滑图像。对我来说,图像看起来更清晰,但它似乎在这里和那里有奇怪的人造物。

交互代码

对于谷歌 Colab,你需要一个谷歌帐户来查看代码,而且你不能在谷歌 Colab 中运行只读脚本,所以在你的操场上做一个副本。最后,我永远不会请求允许访问你在 Google Drive 上的文件,仅供参考。编码快乐!

请注意,我不想滥用任何数据政策。所以对于谷歌 Colab,我找到了下面的图片,它被标记为重复使用。要访问在线代码,请点击此处。

Image from this website

最后的话

想到在深度学习之前,研究人员只是通过卷积和复杂的数学运算来创建高级功能(如 HOG、SIFT ),真是令人着迷。

如果发现任何错误,请发电子邮件到 jae.duk.seo@gmail.com 给我,如果你想看我所有写作的列表,请点击这里查看我的网站。

同时,在我的推特这里关注我,并访问我的网站,或我的 Youtube 频道了解更多内容。如果你感兴趣,我还在这里做了解耦神经网络的比较。

参考

  1. scipy,G. (2018)。scipy 中的高斯滤波器。Stackoverflow.com。检索于 2018 年 4 月 13 日,来自https://stack overflow . com/questions/25216382/Gaussian-filter-in-scipy
  2. 斑点检测。(2018).En.wikipedia.org。检索于 2018 年 4 月 13 日,来自https://en . Wikipedia . org/wiki/Blob _ detection # The _ Laplacian _ of _ Gaussian
  3. 生物分割|生物图像信息学中心|加州大学圣巴巴拉分校。(2018).Bioimage.ucsb.edu。检索于 2018 年 4 月 9 日,来自https://bioimage.ucsb.edu/research/bio-segmentation
  4. 驱动:下载。(2018).isi . uu . nl . 2018 年 4 月 9 日检索,来自http://www.isi.uu.nl/Research/Databases/DRIVE/download.php
  5. 超声波神经分割| Kaggle。(2018).Kaggle.com。检索于 2018 年 4 月 9 日,来自https://www.kaggle.com/c/ultrasound-nerve-segmentation
  6. Pixabay 上的免费图像——核磁共振成像,磁性,x 光,头骨,头部。(2018).Pixabay.com。检索于 2018 年 4 月 9 日,来自https://pix abay . com/en/MRI-magnetic-x-ray-skull-head-782457/
  7. Pixabay 上的免费图像——日落、灯塔、黎明、黄昏、太阳。(2018).Pixabay.com。检索于 2018 年 4 月 9 日,来自https://pix abay . com/en/sunset-light house-dawn-dusk-sun-3120484/
  8. Pixabay 上的免费图像——核磁共振成像,磁性,x 光,头骨,头部。(2018).Pixabay.com。检索于 2018 年 4 月 9 日,来自https://pix abay . com/en/MRI-magnetic-x-ray-skull-head-782459/
  9. 空间过滤器-拉普拉斯算子/高斯拉普拉斯算子。(2018).Homepages.inf.ed.ac.uk 检索 2018 年 4 月 13 日,来自 https://homepages.inf.ed.ac.uk/rbf/HIPR2/log.htm
  10. (2018).Cse.psu.edu。检索于 2018 年 4 月 13 日,来自 http://www.cse.psu.edu/~rtc12/CSE486/lecture11_6pp.pdf
  11. 安,S. (2018)。2D 卷积的例子。Songho.ca 于 2018 年 4 月 13 日检索,来自http://www . song ho . ca/DSP/convolution/convolution 2d _ example . html

用于车道识别的计算机视觉

原文:https://towardsdatascience.com/computer-vision-for-lane-finding-24ea77f25209?source=collection_archive---------2-----------------------

先进的计算机视觉技术,从安装在汽车上的摄像机中识别车道线。

这个项目的代码可以在: Github 上找到。
这篇文章也可以在我的网站这里找到。

在 udacity 自动驾驶汽车工程师课程的第四个项目中,我应用计算机视觉技术来检测道路上的车道线,这些车道线是使用安装在汽车前部的摄像头拍摄的。在这个项目中,大量使用了开源计算机视觉库(OpenCV) 库。

该项目包括以下几个阶段:

  1. 相机校准,以消除镜头失真的影响。
  2. 检测车道线的图像预处理。
  3. 道路透视变换有助于检测。
  4. 改造道路上的车道线检测。
  5. 车辆位置和车道曲率半径的计算。
  6. 生成结果视频。

摄像机标定

摄像机的输出是视频,本质上是图像的时间序列。由于摄影镜头的性质,使用针孔相机拍摄的图像容易发生径向失真,从而导致放大倍率不一致,这取决于物体与光轴的距离。

以下是来自 OpenCV 的示例图像,展示了两种主要类型的径向失真:

Figure 1. Examples of radial distortion. (source: link)

为了正确检测图像中的车道线,我们首先需要校正径向失真。

计算机视觉研究人员已经想出了一种方法来纠正这种径向扭曲。要校准的相机用于捕捉棋盘图案的图像,其中图案中的所有白盒和黑盒都具有相同的大小。如果相机失真,捕获的图像将会错误地显示棋盘的尺寸。

为了校正失真的影响,识别棋盘的角,并且使用与预期棋盘测量的偏差来计算失真系数。这些系数随后被用于从使用该相机捕获的任何图像中消除径向失真。

Figure 2. Checkerboard before and after fixing for distortion.

在上图中,最左边的图像显示了原始失真图像,最右边的图像显示了在失真图像上绘制的角,中间的图像显示了相机校准后得到的未失真图像。

OpenCV 函数findchesboardcorners和 calibrateCamera 用于实现上述相机校准过程。

现在我们已经校准了我们的摄像机,我在汽车视频的实际镜头上测试了结果。下图显示了相机校准的结果:

Figure 3. Distortion correct applied for snapshots from the car driving video.

图像预处理

有了未失真的图像,我们现在回到检测道路上车道线的主要目标。分离和检测图像中的对象的一种方法是使用颜色变换和梯度来生成过滤后的阈值二进制图像。

对于颜色变换,我试验了三种颜色空间,以便找出哪一种最适合过滤代表道路上车道线的像素。测试了三种颜色空间:

  • HSL :将颜色表现为三个通道——色相、饱和度和明度。
  • LAB :将颜色表示为三个通道——亮度,分量 a 表示绿-红,分量 b 表示蓝-黄。
  • LUV :尝试感知一致性的 XYZ 色彩空间的变换。

经过一些实验,我得出结论:LAB 色彩空间的 b 通道和 LUV 色彩空间的 L 通道是检测道路上车道线的最佳组合。

还考虑了 Sobel 梯度滤波器。图像梯度测量颜色变化的方向强度。Sobel 是一种梯度滤波器,它使用高斯平滑和微分运算来减少噪声的影响。

Figure 4. Original undistorted images in the first column, the b/L channel thresholding in the second column, the Sobel gradient filter in the third column, and the two filters combined in the last column.

透视变换

我们现在可以区分图像中的车道线,但使用默认的相机视图很难计算出车道的准确角度/方向。在默认的相机视角中,离相机越远的物体看起来越小,车道线看起来越靠近汽车,这不是真实世界的真实表现。

修复这种透视失真的一种方法是转换图像的透视,使我们从上方观看图像,也称为鸟瞰视图。

OpenCV 提供了函数 getPerspectiveTransform 和 warpPerspective ,可以用来对图像中的一个片段应用透视变换。首先,我们在图像中选择我们想要应用变换的区域。在下图中,我选择了汽车前面的车道线段:

Figure 5. Source points we want to apply a perspective transform to.

然后,我们选择代表我们想要将线段转换到的目标空间的点,在我们的例子中,任何矩形都足够了。然后,该函数将返回一个 3x3 的变换矩阵,使用 warpPerspective 函数,该矩阵可用于将任何线段弯曲成我们选择的视角。

下图显示了成功应用透视变换的两条不同路段的车道线:

Figure 6. Perspective transform applied to two different sections of the road.

请注意,现在确定车道线的曲率变得更加容易了!

车道线检测

我们现在终于准备好完全检测车道线了!首先,我们将在图像预处理部分讨论的二进制阈值处理应用于透视变换车道线段。我们现在有一个图像,其中白色像素代表我们试图检测的车道线的一部分。

接下来,我们需要找到一个好的起点来寻找属于左车道线的像素和属于右车道线的像素。一种方法是生成图像中车道线像素的直方图。直方图应该有两个峰值,每个峰值代表一条车道线,其中左峰值代表左车道线,右峰值代表右车道线。下图显示了从两个二进制图像生成的两个直方图示例:

Figure 7. Binary thresholded images and histogram of pixels in thresholded images.

然后,两个峰值的位置被用作搜索属于每条车道线的像素的起点。我们采用滑动窗口搜索技术,从底部开始,迭代扫描到图像的顶部,将检测到的像素添加到列表中。如果在一个窗口中检测到足够数量的像素,下一个窗口将以它们的平均位置为中心,这样我们就可以跟踪整个图像中像素的路径。

在我们检测到属于每条车道线的像素后,我们通过这些点拟合一个多项式,生成一条平滑的线,作为车道线位置的最佳近似。

下图显示了滑动窗口技术的应用,多项式拟合检测到的车道像素(红色表示左侧车道像素,蓝色表示右侧车道像素):

Figure 8. Warped images and polynomial fit produced using sliding window technique.

下面是滑动窗口搜索技术的另一个视图,突出显示并填充了搜索区域:

Figure 9. Sliding window technique compared to the binary image used as input.

车辆/车道位置

最后,使用两条检测到的车道线的位置,并假设摄像机位于图像的中心,然后我们计算汽车相对于车道的位置。使用图像的分辨率计算出从像素转换为米的比例测量值。

此外,使用比例测量,我们还可以通过将新的多项式拟合到世界空间来计算车道的曲率,然后计算曲率半径。车道的曲率半径就是这两个半径的平均值。下图显示了两条车道线的曲线半径和中心偏移(图像中检测不可见):

Figure 10. snapshot fro the video with the lane curvature and car’s centre offset superimposed.

结果

随着我们的车道线检测管道的完成,我们现在通过将填充的多边形投影到我们认为车道线边界所在的视频上来可视化我们的检测,如下图所示:

Figure 11. Projection of our lane line onto the original video.

管道是在汽车行驶的录像上运行的。下面是一段演示管道运行的视频:

运动中的计算机视觉

原文:https://towardsdatascience.com/computer-vision-in-sports-61195342bcef?source=collection_archive---------4-----------------------

我最近在浏览 CVPR 的网站,无意中发现了它在运动工作坊中的计算机视觉。我认为体育是许多机器学习算法的一个有趣的应用,因为体育通常是非常快节奏的,并且(许多)包括群体动态。因此,为体育量身定制的算法可能有助于推动 CV 的发展。

我一直对将机器学习应用于体育和娱乐活动很感兴趣。特别是,在 PaddleSoft 的时候,我想用 CV 算法来检测急流中不同的划水和动作,并用它来预测划水者是否会有一条成功的路线,或者他们是否会翻转、游泳……等等。但是由于我的知识有限,我没有走多远;此外,直到最近几乎没有关于这个主题的文献。但是我很高兴看到许多新的论文处理这种复杂的事件识别。

以下是研讨会上的一些论文,我觉得特别有趣,我认为它们解决了整个 CV 中的重要问题。为了简洁起见,我选择了不涉及太多的实现细节,而是专注于这些作品中呈现的广泛主题。

学习评分奥运项目 :

作者:帕里托什·帕尔马和布伦丹·特兰·莫里斯

虽然乍看起来,为奥运赛事打分似乎是一个利基研究领域,但使用计算机视觉对一个动作或活动打分或提供反馈对许多不同的领域和问题都是有用的。例如,作者指出,与他们论文中介绍的方法类似的方法可以为自行进行物理治疗的患者或在没有教练的情况下为比赛进行训练的人提供反馈。他们还表示,他们的算法可以帮助消除奥运会比赛评判中的一些主观性。

在进入本文之前,澄清一些术语是有用的:

动作识别:涉及对视频中发生的活动进行分类。(即人在跑步)。

行动质量评估:包括根据行动的执行情况分配一个数值(即人员的运行经济性为 8.0)。

动作质量评估是一个困难的问题,因为高分和低分动作之间的差异往往非常微妙,必须考虑整个序列(不仅仅是一个片段)。此外,与动作识别数据集不同,动作质量数据集相对稀缺,并且存在的数据集非常小。在这篇论文之前,只有少数其他作者探讨了这个问题

作者建议采取多层次的方法来解决这个特定的任务。在第一层,他们使用 3d convnet 提取特征。这些特征然后被传递到三个可能的“第二级”中的一个第一个是裁剪级别要素或 L-SVR 的简单平均值,第二个是具有完全连接图层的 LSTM,第三个是用于提取要素并将其传递到 L-SVR 的 LSTM。

Action quality assessment model overview (taken from page 20 of Learning to Score Olympic Events)

作者评估了他们在跳水、花样滑冰和体操跳马方面的模型。他们使用的潜水数据集是一个小数据集,最初来自麻省理工学院的“评估行动质量”论文,可以在这里找到。。他们还在花样滑冰和体操数据集上测试了他们的模型。他们测试的另一个名为 UNLV 的数据集可以在这里找到。结果和性能评估是相当复杂的,如果不阅读全文就无法很好地总结。然而,TLDR 本质上是 C3D-SVR 给出了最好的总体结果,但是在行动过程中不能检测出特定的错误。如果你只是对评分感兴趣,这是可以的,但是为了提供反馈,你显然也需要能够识别行动的“问题”区域。为了弥补这一点,他们增加了 LSTM(即 C3D·LSTM-SVR ),这增加了他们的得分和实际得分之间的误差,但能够检测出参与行动的个人的具体“错误”。

总的来说,我认为这篇论文是对一个看似研究不足的领域的一个重要贡献(只希望他们把他们的代码发布在某个地方)。令人困惑的是,没有更多的研究针对动作质量评估,因为它可以直接帮助任何类型的教练/训练。

[连续视频到简单信号用卷积神经网络进行泳姿检测](http://Continuous Video to Simple Signals for Swimming Stroke Detection with Convolutional Neural Networks:) :

作者:布兰登·维克托,贞哼·斯图尔特·摩根,迪诺·米尼乌蒂

前面提到的动作识别集中在将整个视频分类为单个动作。相比之下,事件检测包括检测动作的开始和结束帧(在连续视频中),然后对它们进行分类。本文主要研究游泳泳姿的检测。具体来说。本文作者提出了一种用于离散事件检测的方法。然后,他们使用这种方法来确定视频中何时出现泳姿。

“划水率是游泳训练中使用的重要指标,目前,专家花费大量时间在视频中手动标记每一次划水,以便向游泳者提供统计反馈。我们称这个任务为离散事件检测(不同于,事件检测;检测一个动作的开始和结束)一

与简单事件检测相比,离散事件检测涉及在事件发生时定位准确的帧。

Figure 2 from [Continuous Video to Simple Signals for Swimming Stroke Detection with Convolutional Neural Networks](http://Continuous Video to Simple Signals for Swimming Stroke Detection with Convolutional Neural Networks:)

作者使用 CNN 来检测这些离散事件,并将它们映射到一维平面上,用峰值表示游泳中风的位置(如果你感到困惑,请参见他们的图 1)。他们的 CNN 在预测游泳或网球击球方面也做得很好(“在 3 帧容差下,F 值分别为 0.92 和 0.97”)。

这篇论文激发了我的兴趣,因为它能够检测出一连串的笔画。我还喜欢它对不同类型的动作检测的解释和对离散事件检测的介绍。然而,对我来说最有趣的部分,尤其是它如何很好地推广到网球。我很想测试它识别划水的能力。

曲棍球动作识别通过集成堆叠沙漏网络

http://open access . the CVF . com/content _ cvpr _ 2017 _ workshop/w2/papers/Fani _ Hockey _ Action _ Recognition _ CVPR _ 2017 _ paper . pdf

作者 : Mehrnaz Fani Shiraz 大学 Helmut Neher 滑铁卢大学 hne her @ uwaterloo . ca David a . Clausi,Alexander Wong,John Zelek 滑铁卢大学

作为一名曲棍球迷,我特别觉得这篇论文很吸引人。这篇论文的作者试图解决冰球运动中的动作识别问题,因为他们认为这有助于提供有价值的反馈。

动作识别为教练、分析师和观众提供了评估球员表现的内容,对教练、分析师和观众都有好处。第 29 页

然而,作者指出,冰球有许多特殊的挑战(这也可以推广到其他运动)。

姿态估计和动作识别是曲棍球中具有挑战性的问题,可以扩展到其他类型的运动。曲棍球特有的动作识别挑战包括使球员体形变形的笨重服装、与背景高度相似的球队球衣(白色)(冰和木板……第 29 页

作者称他们的模型为 ARHN 或动作识别沙漏网络。他们模型的实际细节相当复杂,因为它使用了许多不同的组件。但在最基本的层面上,他们的模型是通过将视频剪辑转换为图像序列,用堆叠的沙漏网络估计玩家的姿势(并将其作为特征输出),用潜在的转换器进行转换,然后对动作进行分类。你可以在他们的报纸上读到全部细节。

Figure 7 on page 35 of Hockey Action Recognition via Integrated Stacked Hourglass Network

他们将四种不同的动作分类:直滑、交叉滑、前滑和后滑。他们的精确度和回忆分数一般在 60 多到 70 多。

总的来说,考虑到他们在数据可用性方面的局限性,他们取得了相当好的结果。看看这种动作识别如何识别其他项目,如倒滑、身体检查和传球,会很有趣。此外,正如在上一篇文章中提到的,对这一行动的有效性进行分级也是有价值的,但这可能需要对上一篇文章中描述的技术进行更复杂的改进。最后,正如我们将在下面的文章中看到的,曲棍球有一个主要的团队组成部分,作者在这里并没有真正触及。

冰球控球项目分类

Detecting puck possession in ice hockey using an LSTM. Classification of Puck Possession Events in Ice Hockey (91).

作者穆米塔·罗伊·托拉,成建辉,吉姆·利特

本文试图解决体育运动中的群体活动识别问题。群体活动识别顾名思义就是试图观察多个人(或者在这种情况下是玩家)并确定他们作为一个群体在做什么。虽然团体活动识别在团队运动中的效用相当明显,但它也可以在体育世界之外提供效用。一个直接的用例是安全目的(例如,确定一群人是否抢劫这个地方或攻击某人)。然而,群体活动识别对于几乎任何涉及大量多人互动镜头的任务都非常有用。正如人们可能期望的那样,可以说群体活动识别的“关键”是理解个体行动之间的关系。

现在记住这些想法,让我们进入实际工作。在本文中,作者着眼于冰球控球事件。具体来说,他们想要检测和分类不同类型的压轮占有事件。他们主要专注于冰球倾入,倾出,松散冰球恢复(LPR),投篮和传球。为此,他们使用 AlexNet 的 f c7 层从球员有界子图像中提取球员的特征(在他们的算法中,球员是用有界框预先标识的),这些特征然后被最大化汇集,以便考虑球员的交互,最后它们被馈送到执行预测的 LSTM。他们测试了几种不同的特性和 LSTMs 配置,您可以在本文中了解更多。

我真的很喜欢这篇文章,因为它试图解决一个涉及多个玩家互动的非常困难的任务。正如他们在文章中提到的,我认为获得好的特征来描述玩家之间的关系对于好的性能是至关重要的。像往常一样,我真的希望看到代码或补充,更详细地描述他们的实现,因为纯粹基于他们文章中的信息重建他们的设置将是非常困难的(至少对我来说)。

由于篇幅原因,我没有在这里提到研讨会上的许多其他好论文,但我鼓励您查看研讨会的官方网站。我主要选择这三个,因为它们讨论了体育运动中不同领域的相对不同的挑战。他们主要讨论了与动作质量评估、姿态估计、离散事件检测和群体动作检测相关的挑战。我认为这些论文都展示了看似专业的运动中的进步在整个领域中的应用。他们也激励我重新审视我将 CV 应用于激流泛舟的目标。

关于计算机视觉在体育运动中的应用的其他资源

计算机视觉在体育 CVPR

运动中的计算机视觉:斯普林格出版

网球领域特定活动识别

行动质量评估 MIT

预算上的计算机视觉

原文:https://towardsdatascience.com/computer-vision-on-a-budget-3748372414c3?source=collection_archive---------19-----------------------

用不到 20 行代码实现一个动态分类器

在试图向我弟弟解释计算机视觉的复杂性时,他问我实现实时图像分类器的最简单的方法(不是他的原话)。

我想了一会儿,想出了一个他几乎能够理解的解决方案。它利用了 openCV 库,并且需要与预先存在的模型结合使用。然而,它可以在不到 20 行的 Python 代码中执行(加载依赖项不算在内)。

属国

  • 开源计算机视觉库 openCV
  • 如果你不想从头开始训练你的模型,你可以从 Keras/Caffe 下载预先训练好的模型权重

准备工作

首先,加载所需的库和分类器。

import cv2
import numpy as np
from keras.applications import imagenet_utils, VGG19model = VGG19(weights='imagenet') # load the model weights

在这个练习中,我将使用 VGG19,它是通过 ImageNet 训练的模型之一。VGG19 可以使用 Keras 库加载。

我们需要 imagenet_utils 来预处理图像并解码 VGG19 给出的分类。

我们开始吧

cam = cv2.VideoCapture(0) # Open webcam

这种实时分类器的想法是将网络摄像头捕捉的图像分成帧,并将每一帧视为要分类的图像。为此,我们需要创建一个无限循环来连续捕获帧。

while True:

    ret, frame = cam.read()
    k = cv2.waitKey(1)     if k%256 == 27: # if esp key is pressed
        break

这个循环捕获后续帧(frame)以及前一帧是否存在(ret)。我们还包括一个退出条件,特别是当按下退出键时。其他等待键映射可以参考 Ascii 表。

接下来,我们要抓取帧,并像对待任何常规图像一样对其进行分类。这些都在我们的循环中。

 frame_pred = cv2.resize(frame, (224, 224))
   frame_pred = cv2.cvtColor(frame_pred,                                                  cv2.COLOR_BGR2RGB).astype(np.float32)
   frame_pred = frame_pred.reshape((1, ) + frame_pred.shape)
   frame_pred = imagenet_utils.preprocess_input(frame_pred) predictions = model.predict(frame_pred)
   (imageID, label, score) = imagenet_utils.decode_predictions(predictions)[0][0]

这里发生了很多事情。让我们一行一行地来:

  • 图像大小调整为(224,224),这是 VGG19 的默认输入大小
  • 图像被转换为 RGB 格式。OpenCV 使用 BGR 格式,而 Keras(以及几乎所有其他格式)使用 RGB 格式
  • 图像被重新整形以适应模型输入要求
  • 输入被预处理成模型要求的格式
  • 我们检索预测
  • 我们解码预测以获得类别、标签和分数(属于该类别的概率)

要将预测叠加在网络摄像机画面上:

 cv2.putText(frame, "%s with Probability %.2f" % (label, score), (25, 25), cv2.FONT_HERSHEY_DUPLEX, 0.9, (0, 0, 250), 2) cv2.imshow('Computer Vision on a Budget', frame)

这又是在循环内部运行。最后一行返回带有所需标签文本的原始网络摄像头捕捉(不是针对模型增强的捕捉)。

最后,你想关闭你的摄像头。这一步在循环之外,只有按下 escape 键才会执行。

cam.release()
cv2.destroyAllWindows()

完整的 20 行代码,点击这里。

它所做的一些分类。回想起来,红色并不适合这个标签。

这是一个粗略的解决方案,使用一种过于简单的方法来解决一个非常复杂的问题。分类器不区分也不识别帧中的单个对象。根据所用的型号,它也可能非常慢。

从好的方面来看,实现起来非常简单,我想我哥哥也学到了一些东西。

感谢您的阅读!

如果你喜欢这篇文章,可以看看我关于数据科学、数学和编程的其他文章。通过 Medium 关注我的最新动态。😃

作为一个业余爱好项目,我还在 www.dscrashcourse.com建立了一套全面的免费数据科学课程和练习题。

如果你想支持我的写作,下次你报名参加 Coursera 课程时,可以考虑使用我的会员链接。完全公开—我从每一次注册中获得佣金,但不会对您产生额外费用。

再次感谢您的阅读!📕

电脑可以让你跳舞,看看“现在人人都会跳舞!”

原文:https://towardsdatascience.com/computers-can-make-you-dance-see-how-everybody-can-dance-now-501be0098916?source=collection_archive---------10-----------------------

想象过你像 MJ 一样跳舞吗?“可能在梦里吧!”,可能是你的答案,但现在这确实是可能的,让我们使用生成性对抗网络(GANs)来学习跳舞。GANs 在目前的技术研究领域获得了很大的发展势头,它改变了我们的思维方式,并试图实现不可能的事情。

我们可以利用 GANS 做一些惊人的事情:

  • GAN 具有将枯燥的黑白图像转换成彩色图像的能力。
  • 它可以使用虚拟生成的数据来扩充数据集,一个惊人的应用是,它还可以从文本合成图像,反之亦然。
  • 这些可以通过在原始图像中添加随机性来创建新的动漫角色。

哇哦。我们生活在另一个维度吗?当然,不!所以,让我们来看一次这个奇妙的网络。

甘斯简介

gan 是由 Ian Goodfellow 等人设计的生成性对抗模型。主要目标是赋予计算机想象力,使其能够使用现有的信息/数据生成新的信息和数据。GANs 由两个参与者(神经网络模型)组成,这就像一个零和锁定游戏。

一个玩家是一个生成器,它试图产生来自某种概率分布的数据。那就是,你试图复制党的门票。另一个玩家是一个鉴别者,作为一个法官。它决定输入是来自生成器还是真实的训练集。那会是党的安全,比较你的假票和有效票,找出你设计的瑕疵。

玩这个游戏有两个主要规则。第一,生成器试图最大化使鉴别器将其输入误认为真实的概率,第二,鉴别器引导生成器产生更真实的图像/用例。

GANs 是机器学习最近十年最有趣的想法!——扬·勒昆

概略地说,这是它看起来的样子;生成器向训练集图像添加随机性,而鉴别器监控随机性并识别生成的图像是真是假。

Generative Adversarial Model

现在,我们能让人们用 GANs 跳舞吗?加州大学伯克利分校说赞成成立!

CAROLINE CHAN,SHIRY GINOSAR,TINGHUI ZHOU,ALEXEI A. EFROS 发表了一篇惊人的论文,名为:大家现在跳舞! ( 视频)让我们从今以后,跳进深渊去了解更多。

简而言之,它是我们将源对象的舞蹈动作(一个会跳舞的对象)转移到静态目标对象(一个我们教跳舞的对象)的地方。这被称为具有时空平滑的每帧图像到图像的转换。听起来很奇怪,对吧?

为了明确这一点,让我们将这一过程分为三个简单的阶段:

  • 姿态检测
  • 全局姿态标准化
  • 从标准化的姿势简笔画到目标对象的映射

姿态检测从源视频的每一帧中估计源人物的姿态。为了做到这一点,我们使用了一个预先训练好的姿势检测器“开放姿势”。这将自动检测跳舞者的 x,y 关节坐标。然后将这些点连接起来,形成一个姿势简笔画。下图显示了如何从源图像中识别姿势。

Pose detection

全局姿态归一化是为了使源的对象框架与目标的环境更加一致。

从标准化的姿态简笔画到目标主体的映射最后,我们将标准化的姿态简笔画映射到目标对象,这使得它们像源对象一样跳舞。整个过程分两个阶段完成——培训和转移。

培训阶段

在训练阶段,所生成的目标人的姿势棒图被传递给生成器 G,生成器 G 的工作是从抽象的姿势棒图像生成图像。

这种对抗训练使用鉴别器 D 和使用预训练 VGGNet 的感知重建损失 dist 进行。这有助于优化生成的图像 G(x) 以匹配地面真实目标图像 y 。与传统 GAN 一样,鉴别器 D 试图区分真实图像 y 和伪图像 G(x) 。所有这些导致对目标图像的生成器 G 的训练。

传送阶段

在这个阶段,源物体的姿态y’被提取,产生x’。但是,由于我们不确定源对象的位置、肢体位置和环境,我们需要确保它与目标人物的位置兼容。因此,为了归一化上述内容,我们使用全局位置归一化范数生成 x 。我们找到脚踝位置和高度之间的距离,将源对象的位置线性映射到目标对象的位置。然后我们将其传递给之前训练好的 G 模型,生成目标人的 G(x)

到目前为止,一切顺利。我们已经使用 pix2pixHD 框架成功地从源对象的位置生成了漂亮的目标图像,该框架内部使用了条件 GAN(与我们到目前为止讨论的 GAN 相同)。在我们的用例中,经过 GAN 网络的严格训练,pix2pixHD 将从姿势简笔画生成人类图像。

A generator trying to colour the input image — an application of pix2pixHD.

但是,我们随后修改了 pix2pixHD 的对抗性训练,以产生连贯的视频帧,并使用时间平滑和面部 GAN 来获得逼真的面部表情。

时间平滑

它用于生成视频帧序列。因此,我们使用两个连续的帧来保持针对静态目标对象修改的视频的连续性,而不是一个单独的帧。

最初,第一输出 G(xt-1) 取决于姿态简笔画 xt-1 和零图像,比如说 z 。下一个输出 G(xt) 以其相应的姿态简笔画 xt 和先前生成的输出 G(xt-1) 为条件。鉴别器现在试图将真正的时间相干序列(xt 1,xt,yt1,yt) 与伪序列(XT 1,XT,G(XT 1),G(xt)) 区分开来。

G(xt) is conditioned on xt and G(xt-1)

Discriminator tries differentiating the real and fake temporal video sequences. G(x) indicates the images generated using Generator in the pix2pixHD framework and y is the actual input given which is the same as expected output.

脸甘

脸部特写有助于提高面部表情的真实性。面 GAN 与我们之前讨论过的主 GAN 一起使用。一个发生器, G(xf) 被用来专门聚焦在使用 G 的人的脸上。然后,这与姿势简笔画 xf (从 x 生成,焦点在人的面部)一起被提供给生成器 Gf ,该生成器输出残差 r。当与 G(xf) 组合时,这给我们提供输出 r + G(xf) 。然后,鉴别器尝试将真实人脸对 (xf,yf) 与虚假人脸对 (xf,r + G(xf))区分开来。

Face GAN

耶!结束了。综上所述,我们首先训练主 GAN,然后优化权重,之后,应用面 GAN。现在,你也可以跳舞了!

感谢阅读。如果你觉得这个故事有帮助,请点击下面的👏去传播爱。

本文由萨姆希塔·阿拉和维哈尔·鞍马撰写。

计算机视觉:温和的介绍

原文:https://towardsdatascience.com/computers-that-learn-to-see-a2b783576137?source=collection_archive---------13-----------------------

贯穿人类历史和大脑研究的一个主要问题是:我们如何以自己的方式感知世界?

当谈到我们的眼睛时,这些只是传感器,帮助我们建立一个我们周围现实的可理解的表示。例如,颜色等属性并不是物理世界中的物体所固有的,而是代表了我们对不同波长的光反射的感知。尽管听起来令人沮丧,但在我们的物理现实中,一切都是“黑暗”的。当一个物体反射我们脑海中与红色联系在一起的特定波长,并吸收所有其他波长时,这个物体就呈现为红色。

这种视觉感知能力远远不止识别不同的波长。最有趣的一面是,我们有能力根据这些光子在空间中的相互关系来构建一个概念现实。例如,我们理解椅子的概念,因为我们希望它有一定的几何属性。朝向我们的眼睛,椅子反射的光必须保持一种空间关系,类似于我们概念化的椅子形状。

计算机最近也具备了视觉识别能力,学习如何看东西。这要归功于人工智能的进步,尤其是卷积神经网络(CNN)的应用。有很多关于这个主题的文章,但是很少解释统计学和线性代数之外的方法的直觉。

卷积神经网络的构造块

为了理解如何使用细胞神经网络来执行任何视觉任务,我们必须首先了解它的组成部分。所涉及的概念列表可能是压倒性的,但它们都可以缩小到该方法中已经存在的词:卷积、深度神经网络(DNNs)以及这两者的融合。

什么是卷积?

卷积是一种将两个函数合并成第三个函数的数学运算。例如,假设我们有两个给定的函数,【f(t)g(t) ,我们感兴趣的是将一个应用到另一个之上,并计算相交的面积:f(t) g(t)=(f * g)(t)*。

Source: Brian Amberg

在这种情况下,我们在【f(t)上应用 g(t) (调用内核),并根据两个函数面积的交集来更改响应【f * g】【t】。卷积的概念是信号处理中最常用的技术,因此它也应用于计算机视觉,这可以看作是处理多个 RGB 传感器的信号。卷积有效性背后的直觉是它们能够将给定的输入信号过滤成组合的、更有用的结果。

在图像的特殊情况下,信号以矩阵而不是波形的形式更容易理解。因此,我们的函数 f(t)g(t) 现在将分别变成 【图像(矩阵)【内核(矩阵) 。同样,我们可以通过将一个矩阵放在另一个之上来应用卷积:

Source: Machine Learning Guru

你在上面看到的只是图像中滑动窗口与内核的矩阵相乘,然后求和。在计算机视觉的背景下,卷积的能力在于它们是 RGB 传感器领域的优秀特征提取器。当单独拍摄时,每个像素(RGB 传感器)对于理解图像包含的内容是毫无意义的。正是空间中的相互关系赋予了图像真正的意义。它适用于你在电脑上阅读这篇文章的方式,像素代表字符,你的大脑将黑色像素在空间中相互匹配,形成字符的概念。

通过在图像上滑动不同的卷积核,可以执行图像操作。这是 Photoshop 或 Gimp 等图像编辑软件中的许多工具的工作原理:

Source: Gimp Documentation

什么是深度神经网络?

机器学习中学习的概念可以看作是试图从给定的数据中预测未知的函数。假设你有兴趣预测一个标签 y 给定输入 x 。在这个特定的场景中,您希望您的模型将您的预测和实际情况之间的误差最小化。我们以这样的方式修改我们网络中的权重和偏差,即我们尽可能将输入 x 映射到输出 y

为了更好地理解这一点,我们来看一个简单的线性回归问题。一条直线的方程可以描述为 y = mx + b ,其中 m 为斜率, b 为与 y 平面的交点(一般称为偏置)。如果只有两个点,可以用公式直接求解斜率和偏差,但是当我们有多个点 n > 2 并且没有一条线完全符合这些点时,问题就变得更难了。在这种情况下,我们会对逼近一条使到每个数据点的距离最小化的线感兴趣。为此,我们可以遵循以下步骤:

1.给 mb 随机取值。
2。计算误差,可以描述为均方误差 (MSE),就知道我们离最优线有多远了。
3。使用该误差函数的导数,计算梯度,以知道在哪个方向上移动这两个值,从而减小误差。
4。重复步骤 2 和 3,直到误差停止减小。

Source: Alykhan Tejani

在图的左侧,您可以看到误差空间,它是由应用于我们特定数据点的 MSE 给出的。在右侧,我们可以看到我们的数据点的表示,以及由 mb 定义的线。开始时,线完全断开,这反映在高误差值中。然而,当我们计算导数并向函数减小的地方移动时,我们最终得到类似数据点属性的线的值【m】b

在深度神经网络(DNNs)中,这是关于每个神经元上正在发生什么的核心思想。考虑到我们的问题比仅仅逼近一条线要复杂得多,我们需要更多的神经元,通常是分层的。深度学习通过使用梯度下降来最小化误差函数,由于学习是分层结构的,DNNs 最终在数据中学习一个层次。

神经网络的主要构件是人工神经元。这些有不同的类型,最简单的是感知器。人们可以将感知器视为一个单元,它接受几个二进制输入 x1,x2,…,xn ,并生成一个二进制输出 y :

每个输入都被赋予一个权重 w1,w2,…,wn ,代表它们对输出的重要性。最后,利用输入和给定阈值的加权和计算输出。如果这个和大于这个阈值,感知器将输出 1 ,否则输出 0 。这可以更容易地用代数术语来表达:

网络的人工神经元通过层相互连接。连接到输入的第一组神经元形成了输入层。提供预测的网络的最后一层被称为输出层。中间可以有任意数量的层,这些层被认为是隐藏层

深度学习这个名字来自于架构中涉及 2 层以上的神经元。在过去,超越两层是不切实际的,效果也不好。这些限制被大量训练数据和加速计算的可用性所克服。正是在这个时候,社区开始增加更多的层,产生了所谓的深度神经网络。通过层的通信能够描述数据的分层表示,其中每一层神经元代表一个抽象级别。

通过卷积神经网络学习视觉表示

将卷积提取空间特征的能力与从深度神经网络进行分层学习的能力相结合,产生了所谓的卷积神经网络。通过用卷积层代替 DNNs 中的传统神经元层,这两种技术被合并成一个解决方案。因此,权重和偏差被编码在核中,并且是优化从原始输入(图像)到预期预测(例如,图像的类别)的表示的学习参数。最初的层将编码诸如边缘的特征,而后来的边缘将编码越来越高的抽象,例如形状和纹理。

Source: Nvidia

在上图中,每个神经元代表对来自输入层或前一层的图像的卷积运算。我们可以让多个卷积接收相同的输入,以便提取不同种类的特征。通过这些层,卷积运算将不断地将来自先前层的给定矩阵转换成对于手头的任务来说越来越压缩/有用的表示。例如,如果我们的网络正在执行面部识别,初始层将从面部提取边缘,这些边缘将被卷成形状,这些形状将被卷成某些面部特征,这些特征有助于区分不同的个人,一直到输出,在输出中我们有给定面部的标识符。

结论

细胞神经网络可以应用于数据中存在某种空间属性的任何领域。这通常转化为以图像作为输入的问题。

优势

  • 与经典的计算机视觉技术相比,从开发的角度来看,CNN 更容易应用。有大量的教程和开源软件可以处理最常见的任务,如图像分类或物体检测,只需将数据输入到已经可用的模型中。
  • 当有足够的数据时,CNN 胜过其他的,主要是肤浅的模型。

限制

  • 任何任务所需的数据量都可能是巨大的,因为深度学习技术通常从手头任务的有限先验知识开始。在其他计算机视觉算法中,有许多技术使用这种先验知识来实现算法如何工作以减少必要的数据量。
  • 和任何深度学习方法一样,CNN 都是黑盒方法,无法形式化解释。这意味着,如果你的模型有一些问题,你将开始猜测什么可能出错。

在线性时间和空间复杂度下计算 N 皇后板中冲突对的数量

原文:https://towardsdatascience.com/computing-number-of-conflicting-pairs-in-a-n-queen-board-in-linear-time-and-space-complexity-e9554c0e0645?source=collection_archive---------7-----------------------

N 皇后问题是人工智能领域中的一个经典问题,我们试图找到一种棋盘配置,其中在 N × N 棋盘中将有 N 个皇后,而不是互相攻击。

解决这个问题有不同的方法,每种方法都有自己的时间复杂度。在生成状态之后,评估每一个状态中冲突对的数量也是非常耗时的。下面我们将讨论我们如何定义这个问题,以及在计算电路板中的冲突数量时如何实现线性时间复杂度。

需要注意的几件事:

  1. 为简单起见,我们将使用索引 1 中的数组(因为,我们从 1 到 N 对行和列进行了编号)。索引 0 未使用。
  2. 给出了简单的 C++代码(idea)。如果你理解这种方法,你总是可以用动态内存分配等等来编写更灵活的程序。
  3. 在本文末尾可以找到 python 实现的链接。

问题定义:我们将板卡配置描述为一维数组。索引将指示列号,并且相应索引中的值将指示该特定列中皇后的行号。

例如,{3,4,3,2}对应于此板配置(下图):

The 2-D board configuration (above) from the 1-D array (below)

为什么要这样定义?因为在每一列中,放置一个以上的女王是没有意义的,他们只会互相攻击,因此无助于解决问题。定义为一维数组可以节省大量空间。

假设我们在 O(1)中生成一个从 1 到 N 的随机值,生成一个电路板配置将花费 O(N)时间。

冲突检查(天真):我们可以使用嵌套循环来计算冲突的数量,因此对于每个皇后,检查所有接下来的皇后,并计算有多少冲突,并将其添加到结果中。冲突发生在以下情况:

  1. 皇后在同一行(两个皇后的行号相同)。
  2. 皇后在同一列中(两个皇后的列号相同,在我们的问题定义中,这不会发生)。
  3. 两个皇后在同一条对角线上(行的绝对差等于列的绝对差)。

这种方法需要 O(n)。

实现线性时间复杂度:我们将在冲突检查中使用 O(N)空间实现线性时间复杂度。

引入了三个一维表格(或数组)。一个用于存储每行中皇后的频率。一个用于存储主对角线中皇后的频率,另一个用于次对角线。

检查以下板图像的行和列索引,了解我们的问题定义。

A 8×8 chessboard

现在我们知道有 N 行,所以行频率数组的大小将是 N(或 N+1,因为索引 0 未被使用)。让我们将其声明为 f_row[],其中最初所有的索引都将包含零(0)。

Array to check frequency of queens in each row

让我们将棋盘配置数组表示为 Queen[]。我们可以这样填充 f_row[]:

for (int i = 1; i <= N; i++){
    int val = Queen[i];
    f_row[val]++;
}

对于每个行值,我们将 f_row[]中相应的索引增加 1。相当容易!现在我们如何检查冲突?让我们假设,一行的皇后不影响另一行的皇后(如果它们在同一条对角线上,它们会影响,但现在我们不考虑这一点)。因此,如果冲突是互斥的,我们可以分别计算每一行的冲突,然后将它们相加得到总的水平冲突,不需要检查重叠。

让我们考虑这种情况,其中 4 个皇后在同一行,冲突的对由红线连接。

Conflicts where 4 queens are on same row

线或边是无向的,我们不检查向后冲突,即如果女王 A 攻击女王 B,女王 B 也攻击女王 A,但是我们认为这是单个冲突。

我们可以观察到,一行中所有冲突的女王实际上形成了一个“完整的图”,因此每个女王都与其他女王相连。边的数量就是冲突的数量。检查完整图中边数的公式,其中有 N 个节点:

(N * (N-1)) / 2

需要注意的重要一点是,当我们检查对角线时,这个观察也适用。

在我们有 4 个皇后的例子中,该行的总冲突数=(4 *(4–1))/2 = 6。

我们将行频率存储在 f_row[]中。要计算横向冲突的总数,我们可以使用以下方法:

for (int i = 1; i <= N; i++){
    int queens = f_row[i];
    result += ((queens * (queens - 1) / 2);
}

为了检查对角线冲突,我们必须以不同的方式观察棋盘。首先让我们试试主对角线。请参见下图,其中每个纸板单元格包含其行值和列值的总和。

Every cell contains sum of row number and column number

如果你仔细观察,你会发现所有的(主)对角线都有相同的和值。也就是说,如果 A 女王站在数值为 10 的位置上,B 女王也在数值为 10 的某处,冲突就发生了!

就像行一样,我们也将使用频率表。但这次是两倍大。因为我们能拥有的最大和值是最高行和最高列的和,N + N .我们把它表示为 f_mdiag[](这里 mdiag 指的是主对角线)。

frequency table for main diagonals

我们可以这样填充它:

for (int i = 1; i <= N; i++){
    int row = Queen[i];
    int sum = row + i; // i is column value
    f_mdiag[sum]++;
}

对于次对角线,我们必须翻转列索引(或行,但不能两者都翻转)。现在对每个单元格的行和列值求和,并观察。

Every cell contains sum of row number and column number

同样,我们寻找的对角线,有相同的和值。所以我们需要另一个频率表,和上一个一样大。

frequency table for secondary diagonals

填充可以这样完成:

for (int i = 1; i <= N; i++){
    int row = Queen[i];
    int sum = (N - row + 1) + i; // flipping is done by N - row + 1
    f_sdiag[sum]++;
}

现在,我们可以遍历这两个数组,并使用(N * (N-1)) / 2 来生成每条对角线上的冲突数,并将它们添加到结果中。大概是这样的(对于 f_mdiag[]):

for (int i = 1; i <= (N+N); i++){ // note here, N+N, not N
    int queens = f_mdiag[i];
    result += ((queens * (queens - 1))/2);
}

实际上,我们计算冲突的方式,所有的设置都是互相排斥的。主对角线冲突不影响次对角线冲突或行冲突的计算,反之亦然,即使同一个 queen 可能一次被视为行成员,一次被视为对角线成员。因此,我们可以在一个循环中更新所有频率(行、主对角线和次对角线)(甚至在生成电路板配置时!)并在另一个循环中计算结果。

例如,检查以下内容:

for (int i=1; i<=N; i++){
    Queen[i] = generate_random(1, N); // generating queen position 
    int val = Queen[i];
    f_row[val]++;               // updating frequency of rows
    f_mdiag[val + i]++;         // and main diagonals
    f_sdiag[N - val + 1 + i]++; // and secondary diagonals
}int result = 0;for (int i=1; i<=(N+N); i++){ // this loop runs from 1 to N+N
    int x = 0, y = 0, z = 0;
    if (i <= N) x = f_row[i];  //number of queens in ith row
    y = f_mdiag[i];   //number of queens in ith main diagonal
    z = f_sdiag[i];   //number of queens in ith secondary diagonal result += (x * (x - 1)) / 2; // adding to result
    result += (y * (y - 1)) / 2;
    result += (z * (z - 1)) / 2;
}

通过这种方法,时间复杂度为 O(N)。空间复杂度也是 O(N)。

查看 Python 代码了解这种方法。

希望这对你有帮助!

让我知道你的想法:)

本周概念:让人工智能告诉你你的长相有多好

原文:https://towardsdatascience.com/concept-of-the-week-let-artificial-intelligence-tell-you-how-good-is-your-look-7dedd324d1d8?source=collection_archive---------6-----------------------

这是一系列介绍我提出的应用和产品的想法(有些可能很愚蠢)的文章。有时我会提供基本的源代码,有时会有只是描述和简单的模型。无论如何,你可以自由地将这些想法实现到一个完整的产品中,但别忘了告诉我你做到了:)。

我不擅长挑选合适的衣服。每次我自己去购物,我都会买一些看起来很傻的东西,然后再也不穿了。或者只在我需要去当地杂货店时才穿上它们,让它看起来像是“我只是迅速拿了我在壁橱里找到的第一件东西”。当然,有成群的销售人员试图帮助我,但是,由于这种帮助很少是有用的,我想,他们只是试图卖给我更贵和更不受欢迎的东西。

这就是为什么我总是试图让我的妻子给我买衣服。尽管我们有时会争论我去开会时穿这件衬衫是否合适,但几乎 99%的情况下她都是对的——那件补丁衬衫会让我看起来像是一个穿着灰色西装和浅蓝色衬衫、打着闪亮红色领带、在赚着百万美元的华尔街狮子中的古怪学生。

外面的许多人身边都没有像我妻子这样的人。因此,对于这些对时尚一无所知的单身人士,我给出了以下建议。一个后端带有神经网络的移动应用程序,它会告诉你你的新造型是好是坏。可能只有两个类别(好/不好),或 5 星或 10 星评级,或任何你认为观众会接受的东西。

基本流程是您:

  1. 给你自己拍张照片
  2. 获得评估

像这样:

我不知道,这个女孩是否真的看起来很棒,因为,就像我说的,我不擅长这个。

如果我给像我这样的人拍照,可能会发生这样的事情:

This is not me, nor my wife. Just random girl from the internet.

如果用户得到 5 英镑,那么所有常见的在 instagram 上分享并获得百万赞的事情都会发生。而且,很可能什么都不会分享,如果她得分更低,因为没有人喜欢在 Instagram 上分享糟糕的图片。

这个应用程序本身很简单。使用 Instagram 帐户登录的屏幕、相机屏幕和结果屏幕。一天之内可以在 iOS 和 Android 上实现。

后端需要建立一个简单的 API 服务器,将数据传递给“大脑”。“大脑”可能是一个张量流服务或任何你熟悉的东西。

我相信类似的东西已经存在了。想法很简单,没有什么是以前没做过的。

不过,还是有一些障碍。

这里可能存在的一个问题是获取训练集。如果您决定实现它,您将需要数千甚至数万个示例来进行神经网络的初始训练。训练数据集的大小在很大程度上取决于您用来对图像进行分级的比例。如果只有好/坏的分类,那么你需要的就更少。如果你认为用户想要更精细的东西,比如 5 级或 10 级评级,那么训练样本的数量应该相应增加。

当然,应该有一些关于模型对图像分类的反馈。一种方法是,模型在一定时间后查看每张照片的赞数与用户数的比率,即分享的照片有多少赞来自总粉丝数。例如,如果用户有 100 个关注者,只有 1 个喜欢 4 星照片,那么算法是错误的,它应该是 1 星。这种方法有一些明显的缺点,但它可能是一个很好的起点。

第二个问题是,要教会神经网络,你需要自己先给照片打分。这意味着这应该是一个具有特殊品味,风格的感觉和最新时尚知识的开发人员。我从未见过如此独特的人类物种。找一个设计师合作可能是对的。这仍然很困难,因为考虑到他或她要评价的图片数量,她或他需要有巨大的耐心。

对于像我这样的人来说,这个应用程序还有最后一个问题。购物时,每当我处于困境时,我通常不会想,“哦,有没有这方面的应用程序?”所以每当我在购物中心时,我应该能够以某种方式被提醒,我的手机上有这个应用程序。这意味着我将需要另一个应用程序,它将完全做到这一点——这可能是下周的概念。

原贴于【seigolonhcet.com】。如果你喜欢这篇文章,可以考虑在 Twitter 上关注我,获取更多的技术故事、新闻、评论和评论。

人工智能安全中的具体问题

原文:https://towardsdatascience.com/concrete-issues-in-ai-safety-fb7838d7235a?source=collection_archive---------9-----------------------

在过去的一年里,对人工智能的新一轮担忧在全球范围内兴起。其中一些很有根据,需要实际的解决方案,而另一些则更接近科幻小说。超级智能机器不会接管世界,至少在未来几十年不会。然而,不那么智能的计算机可以取代人类的劳动,犯错误和被用来伤害。所以让我们来看看主要的人工智能问题。

首先,要教会一台机器一些技能,你需要训练数据。如果没有仔细的选择,数据集可能会偏向某些类,错过重要的功能,甚至包含错误。在某些情况下,这可能会导致你的自拍在社交网络中被错误标记,这不是一个大问题。但是,这也可能导致对癌症组织的错误诊断,并可能导致死亡。

同时,即使是经过精心训练的系统也可能被对抗性的例子所欺骗:

Hint: this is not really a guacamole

你不能在你的生物组织上使用这种技术来欺骗医学人工智能,但有人可以用它来误导自动驾驶汽车,并造成真正的威胁。另一个问题是教机器“光荣失败”。

自检

我们需要他们能够识别不确定性高的情况并直接报告,而不是试图做一些不太明智的事情。这对于强化学习代理尤其重要。几乎所有的人都有一些探索模式,当他们尝试新的行动,以找到更好的方式获得回报。如果不为安全探索进行调整,它可能会导致机器人发生各种事故。

奖励函数的糟糕设计可能会以一种意想不到的形式适得其反。一类问题叫做悬赏黑客。当代理人发现一种扰乱环境以获得更多回报的方法时,它们就出现了。例如,清洁机器人可以故意污染周围的一切,以获得更多的积分,以便稍后进行清洁。或者,代理可以决定停止做任何事情来避免惩罚。这类问题可以通过从人类的偏好和打断中训练 AI 来部分解决。

有些问题并不在于系统本身,而是由使用这些系统的人造成的。对许多人来说,失业是一个现实的问题。另外, AI 在军工越来越受关注。

尽管如此,总有一天人工智能会变得比我们聪明得多。也许 30 年后,也许 100 年后。我们应该如何应对?正如吴恩达所说,“担心超级人工智能接管世界就像担心火星上人口过剩一样”。无论如何,没有人真正知道它会在何时何地发展,而且它也不太可能分享我们所有的价值观。

附加论文:

  • 合成健壮的对抗实例
  • 根据人类偏好进行深度强化学习
  • 安全可中断代理
  • AI 安全的具体问题

最初发表于 认知混乱

PyTorch 中的条件随机场教程🔥

原文:https://towardsdatascience.com/conditional-random-field-tutorial-in-pytorch-ca0d04499463?source=collection_archive---------0-----------------------

假设我有两个相同的骰子,但一个是公平的,另一个是以 80%的概率出现数字 6,而数字 1-5 以 4%的概率出现。如果我给你一组 15 次掷骰子,你能预测出每一次我用的是哪个骰子吗?

Credit Brett Jordan at Unsplash

一个简单的模型是,每当 6 出现时,预测我使用了有偏见的骰子,并说我对所有其他数字使用了公平的骰子。事实上,如果我同样可能使用任一骰子来掷骰子,那么这个简单的规则就是你能做的最好的了。

但是如果在使用公平骰子后,我有 90%的机会在下一次掷骰子时使用有偏见的骰子呢?如果下一次掷骰子是 3,你的模型将预测我使用公平骰子,而偏向骰子是更可能的选择。我们可以用贝叶斯定理来验证这一点:

y_i is the random variable of the dice type for roll i. x_i is the value of the dice for roll i.

要点是,在每个时间点做出最有可能的选择,只有当我同样有可能使用任一骰子时,才是可行的策略。在更有可能的情况下,以前的骰子选择会影响我未来的选择,为了成功,你必须考虑到掷骰子的相互依赖性。

条件随机场* (CRF)是一个标准模型,用于预测与输入序列相对应的最可能的标签序列。有很多关于 CRF 的教程,但我看到的属于两个阵营之一:1)所有的理论都没有显示如何实现;2)一个复杂的机器学习问题的代码,很少解释发生了什么。

我不责怪那些作者选择理论或代码。CRF 是一个更广泛、更深入的主题,称为概率图形模型,因此深入讨论理论和实现需要一本书,而不是一篇博客文章,但这使得学习 CRF 比它需要的更难。

本教程的目标是涵盖足够多的理论,以便您可以带着预期的想法深入第 1 类资源,并展示如何在您自己的笔记本电脑上复制的简单问题上实现 CRF。这将有望使你具备将这个简单的玩具 CRF 应用于更复杂问题所需的直觉。

理论

我们的理论讨论将分为三个部分:1)指定模型参数,2)如何估计这些参数,3)使用这些参数进行预测。这三大类适用于任何统计模型,甚至是简单的逻辑回归,所以从这个意义上说,通用报告格式并没有什么特别的。但这并不意味着 CRF 就像逻辑回归模型一样简单。我们将会看到,一旦我们解决了这样一个事实,事情将会变得更加复杂,那就是我们正在做出一系列的预测,而不是一个单一的预测。

指定模型参数

在这个简单的问题中,我们需要担心的唯一参数是在连续掷骰子中从一个骰子转移到下一个骰子的相关成本。我们需要担心六个数字,我们将它们存储在一个名为转换矩阵的 2×3 矩阵中:

第一列对应于从前一次掷骰中的公平骰子到当前掷骰中的公平骰子(第 1 行中的值)和偏置骰子(第 2 行中的值)的转变。因此,第一列中的第一个条目编码了预测我在下一次掷骰子时使用公平骰子的成本,假设我在当前掷骰子时使用了公平骰子。如果数据显示我不太可能在连续的掷骰子中使用公平的骰子,那么模型就会知道这个成本应该很高,反之亦然。同样的逻辑适用于第二列。

矩阵的第二列和第三列假设我们知道在前一次掷骰子中使用了哪个骰子。因此,我们必须将第一卷视为特例。我们将在第三列中存储相应的成本。

参数估计

假设我给你一组骰子 X 和它们对应的骰子标号 Y 。我们将找到转移矩阵 T ,其最小化训练数据上的负对数可能性。我将向您展示单个掷骰子序列的可能性和负对数可能性。为了得到整个数据集的数据,你需要对所有的序列进行平均。

P(x_i | y_i)是在给定当前骰子标签的情况下观察到给定骰子滚动的概率。举个例子,如果 y_i = dice 是公平的,P(x_i | y_i) = 1/6。另一项 T(y_i | y_{i-1})是从以前的骰子标签转换到当前标签的成本。我们可以从转移矩阵中读出这个成本。

注意在分母中,我们是如何计算所有可能的标签序列 y '的和的。在两类分类问题的传统逻辑回归中,我们在分母中有两项。但是现在我们处理序列,对于长度为 15 的序列,总共有 2 个⁵可能的标签序列,所以分母中的项数是巨大的。CRF 的“秘方”在于,它利用了当前骰子标签如何仅依赖于前一个标签来高效地计算那笔巨款。

这个秘制酱算法叫做正反向算法 *。深入讨论这个问题超出了这篇博文的范围,但是我会在下面给你提供一些有用的资源。

序列预测

一旦我们估计了我们的转移矩阵,我们就可以用它来为给定的掷骰子序列找到最可能的骰子标签序列。最简单的方法是计算所有可能序列的可能性,但即使是中等长度的序列,这也是很难做到的。就像我们对参数估计所做的那样,我们将不得不使用一种特殊的算法来有效地找到最可能的序列。该算法与前向-后向算法密切相关,被称为维特比算法。

密码

PyTorch 是 Python 中的深度学习库,为训练深度学习模型而构建。虽然我们没有进行深度学习,但 PyTorch 的自动微分库将帮助我们通过梯度下降来训练我们的 CRF 模型,而无需我们手动计算任何梯度。这将节省我们许多工作。使用 PyTorch 将迫使我们实现前向-后向算法的前向部分和 Viterbi 算法,这比使用专门的 CRF python 包更有指导意义。

让我们从设想结果应该是什么样子开始。我们需要一种方法,在给定骰子标签的情况下,计算任意掷骰子序列的对数似然。这是一种可能的方式:

这个方法主要做三件事:1)将骰子上的值映射到一个似然,2)计算对数似然项的分子,3)计算对数似然项的分母。

让我们首先处理 _data_to_likelihood 方法,它将帮助我们完成第一步。我们要做的是创建一个 6 x 2 维的矩阵,其中第一列是公平骰子掷出 1–6 的可能性,第二列是有偏骰子掷出 1–6 的可能性。对于我们的问题,这个矩阵是这样的:

array([[-1.79175947, -3.21887582],
       [-1.79175947, -3.21887582],
       [-1.79175947, -3.21887582],
       [-1.79175947, -3.21887582],
       [-1.79175947, -3.21887582],
       [-1.79175947, -0.22314355]])

现在,如果我们看到一卷 4,我们可以只选择矩阵的第四行。向量第一项是公平骰子为 4 的可能性(log(1/6)),第二项是有偏差骰子为 4 的可能性(log(0.04))。代码如下所示:

接下来,我们将编写计算对数似然的分子和分母的方法。

就是这样!我们有了开始学习转换矩阵所需的所有代码。但是,如果我们想在训练模型后进行预测,我们必须编写维特比算法:

我们的实现还有更多的东西,但我只包括了我们在理论部分讨论过的大函数。

正在对数据进行评估

我使用以下概率模拟了一些数据,并对模型进行了评估:

  1. p(顺序中的第一个骰子是公平的)= 0.5
  2. p(当前骰子是公平的|先前骰子是公平的)= 0.8
  3. p(当前骰子有偏差|先前骰子有偏差)= 0.35

查看我制作的笔记本,看看我是如何生成模型和训练 CRF 的。

我们要做的第一件事是,看看估计的转移矩阵是什么样的。该模型了解到,如果我在前一次掷骰子时使用公平骰子,我更有可能在当前掷骰子时掷出公平骰子(-1.38 < -0.87). The model also learned that I am more likely to use the fair dice after using the biased dice, but not by a lot (-0.59 < -0.41). The model assigns equal cost to both dice in the first roll (-0.51 ~ -0.54).

array([[-0.86563134, -0.40748784, -0.54984874],
       [-1.3820231 , -0.59524935, -0.516026  ]], dtype=float32)

Next, we’ll see what the predictions looks like for a particular sequence of rolls:

# observed dice rolls
array([2, 3, 4, 5, 5, 5, 1, 5, 3, 2, 5, 5, 5, 3, 5])# corresponding labels. 0 means fair
array([0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1])# predictions
array([0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0])

The model recognizes long sequences of 6’s (these are the 5’s since we’re starting from 0) as coming from the biased dice, which makes sense. Notice that the model doesn’t assign every 6 to the biased dice, though (eighth roll). This is because prior to that 6, we’re pretty confident we’re at the fair dice (we rolled a 2) and transitioning to the biased dice from the fair dice is less likely. I’m ok with that mistake — I’d say our model is successful!

结论

我已经向您展示了 CRF 背后的一点理论,以及如何实现一个简单的问题。当然还有很多比我在这里能够涵盖的更多,所以我鼓励你查看我在下面链接的资源。

延伸阅读:

条件随机场介绍:条件随机场概述,隐马尔可夫模型,以及前后向和维特比算法的推导。

在 PyTorch 中使用 CRF 进行命名实体识别:这篇文章的灵感来源。展示了如何将 CRF 应用于 NLP 中更复杂的应用程序。

脚注

*准确地说,我们讨论的是线性链 CRF,它是 CRF 的一个特例,其中输入和输出序列以线性序列排列。就像我之前说的,这个话题很有深度。

*由于我们使用 PyTorch 为我们计算梯度,技术上我们只需要向前-向后算法的向前部分。

用大 SQL 配置 Zeppelin

原文:https://towardsdatascience.com/configure-zeppelin-with-big-sql-e7d61a73b2ad?source=collection_archive---------11-----------------------

由于 大 SQL 版本 5.0.1 出来了,我想在 Medium 上转贴我原来贴在LinkedIn上的这篇文章。它将带您完成使用 Big SQL 配置 Zeppelin 服务器的过程,并且可能对使用旧版本或希望自己完成这项工作的人有用。

HDP Hadoop 发行包 Zeppelin 服务器作为其堆栈的一部分。随着本周 HDP 2.6 的最新大 SQL 5.0 的发布,我想我会分享你如何快速配置 Zeppelin 与大 SQL 对话。

你需要在齐柏林飞船上增加一个新的 JDBC 翻译。导航到 UI 页面右上角的菜单,选择“解释器”,然后单击“创建”。将其命名为 bigsql ,选择 jdbc 作为解释器组。更新以下属性。

default.driver = com.ibm.db2.jcc.DB2Driver
default.user = <username>
default.password = <password>
default.url = jdbc:db2:*//<bigsql_head_host>:<bigsql_port>/bigsql*

例如:

如果使用 SSL,相应地编辑 JDBC url 以添加 SSL 参数,例如

sslConnection=true;sslTrustStoreLocation=/path/to/truststore.jks;Password=<password>

您还需要在 依赖项 下添加 jdbc 库 jar。将库路径指定为工件:

/usr/ibmpacks/bigsql/5.0.0.0/db2/java/db2jcc.jar

最后点击保存。

注意:确保运行 Zeppelin 服务器的节点上存在库路径。

您还可以通过 REST 更新 Zeppelin 解释器。参见https://zeppelin . Apache . org/docs/0 . 7 . 0/rest-API/rest-interpreter . html

回到你的笔记,点击解释器绑定(右上角的小齿轮)来绑定新的解释器。单击以绑定 bigsql 解释器,然后单击保存。像下面这样:

然后,您可以在笔记本中使用 %bigsql 与 Big SQL 进行交互:

仅此而已。在笔记本上玩大 SQL 玩得开心。

被混乱矩阵弄糊涂了

原文:https://towardsdatascience.com/confused-by-the-confusion-matrix-e26d5e1d74eb?source=collection_archive---------9-----------------------

命中率,真阳性率,灵敏度,召回率,统计功效有什么区别?

如果你试图回答题目中的问题,你会失望地发现这实际上是一个难题——所列术语基本上没有区别。就像 ANCOVA 和 Moderation 中提到的问题一样,不同的术语经常被用于同一件事情,尤其是当它们属于不同的领域时。这篇文章将试图通过把这些术语放在一起来消除混淆,并解释如何使用检测效果的上下文来解释混淆矩阵的单元。

Commonly used terms for the cells in a confusion matrix.

上图捕捉了混淆矩阵中每个单元格的常用术语。蓝色单元格是期望的结果,而红色单元格是错误。在这里,我使用是否存在影响和是否观察到影响的上下文,而不是通常用来解释混淆矩阵的各自的“实际类别”和“预测类别”。我相信这是理解每个单元格中的信息的更直观的方式,因为术语“实际类”和“预测类”也经常使人困惑。

“真阳性”、“假阳性”、“真阴性”和“假阴性”可能是混淆矩阵中细胞最流行的标签,但你见过多少次有人停下来试图弄清楚“真阴性”和“假阴性”是什么意思?统计假设检验的“第一类”和“第二类”错误甚至更糟,因为它们的名称甚至没有给出任何关于所犯错误类型的线索。有些人可能甚至没有意识到这些统计术语实际上是混乱矩阵的一部分。

考虑到这些术语是多么的不直观,我更喜欢用信号检测理论版本的这些标签:“命中”、“虚警”、“正确拒绝”、“未命中”。当我们把它们放到上下文中时,它实际上很有意义:

  • 如果我观察到一个效果,当这个效果存在时,它是一个击中
  • 如果我在效果存在时没有观察到一个效果,那就是一个错过
  • 如果我在没有影响的情况下观察到一个影响,这是一个假警报
  • 如果我在没有效果的时候没有观察到效果,那就是正确拒绝

这个版本的标签已经包含了什么是对什么是错的内涵,所以立即推断出到底发生了什么变得非常容易。出于这个原因,我将在后面的文章中引用这个版本的标签。在这一点上,重要的是要注意混淆矩阵中的单元格包含每种情况出现的绝对次数,而而不是会与它们出现的概率混淆,例如“命中率”或“真阳性率”。接下来,我将解释这些不同的利率是如何计算的。

基于效果存在的概率

Rates based on existence of effect.

既然你已经意识到混淆矩阵中的单元格包含了出现的绝对次数,那么你可能想知道其他术语如“命中率”和“真正的阳性率”是如何产生的。这些比率实际上是根据一个效应的存在来计算的,这个效应只涉及混淆矩阵的一个特定部分,而不是全部(上图中的红色轮廓)。例如,“命中率”的计算方法是,将效果存在时的命中次数除以总出现次数(即总命中次数加上未命中次数);那么“失误率”就是 1 减去“命中率”。相反,“错误警报率”的计算方法是,将错误警报的数量除以不存在该影响时发生的总数量(即错误警报的总数量加上正确剔除的总数量);那么“正确拒绝率”就是 1 减去“误报率”。

这是同一概念的不同术语开始出现的地方。“灵敏度”和“召回率”与“命中率”相同,“特异性”和“选择性”与“正确拒绝率”相同。“灵敏度”和“特异性”更常用于医学领域,在医学领域,人们对测量诊断测试的性能感兴趣,而“回忆”和“脱落”更常用于机器学习,以测量预测准确性。甚至统计学领域也有自己的行话,尽管它们实际上都指的是同一个东西。

大多数研究人员应该对“统计显著性”很熟悉,它是犯 1 型错误的概率( α )。将这与“误报率”联系起来可能并不困难,因为它是当一个效应不存在时观察到该效应的概率。但是一些研究人员可能没有把【统计能力】这个概念和“命中率”联系起来。因为“权力”这个词,很容易误导研究者,以为权力大的研究权力大就足以下结论。这不是统计能力的含义;简单来说就是在效应存在的情况下,观察到一个效应的概率。当我们将第二类错误( β )视为未命中时,这一点变得尤其明显,1 减去未命中率给出了存在效应时命中的概率。

我希望这澄清了不同的利率意味着什么,你现在是舒适的各种条款。在这篇文章的最后,我将使用混淆矩阵来说明频率主义者和贝叶斯假设检验之间的区别。但在此之前,我会解释当基于是否观察到影响来计算概率时会发生什么。

基于是否观察到影响的概率

Rates based on whether effect was observed.

一种不太为人所知但却很重要的测量准确度的方法是,当观察到一种效应时,计算该效应存在的概率。类似地,比率的计算只参考了混淆矩阵的特定部分,即上图中的红色轮廓。例如,“错误发现率”的计算方法是将错误警报的数量除以观察到效果时发生的总次数(即命中总数加上错误警报),而“错误遗漏率”的计算方法是将未观察到效果时未命中的数量除以发生的总次数(即未命中总数加上正确拒绝)。

术语“真实发现率”和“真实遗漏率”被放在括号中,因为它们不是实际术语。更常用的术语是、【阳性预测值】和【阴性预测值】,但我认为使用“真实发现率”更直观,因为它意味着“我所发现的内容中真实的比率”。毕竟,它只是其对应的“错误发现率”的反向,这使得关联更加直接。“真实发现率”的另一个术语是“精度”,这是上面已经介绍过的通常与“回忆”配对的机器学习术语。

知道真实的发现率与知道命中率一样重要,甚至更重要。当该效应的普遍性实际上不是很大时,情况尤其如此。在这种情况下,每当观察到效应时,得到假警报的机会变得非常高。这就是贝叶斯主义者试图解释常客在他们的分析中遗漏了什么的地方,接下来我将使用混淆矩阵来讨论。

额外收获:频率主义者与使用混淆矩阵的贝叶斯分析

在我之前关于贝叶斯分析&复制危机的文章中,我介绍了贝叶斯分析的基本原则,以及这些原则如何帮助解决复制危机。在撰写这篇文章时,我意识到贝叶斯分析可以在混淆矩阵的帮助下更容易地解释,前提是你已经熟悉它。贝叶斯分析中的术语也是我们已经在混淆矩阵中学到的另一个变体,但是我一会儿会讲到。

首先,让我们考虑一下我提到的场景,其中一种效应的发生率不是很大。到目前为止,我们一直在看被分成四个相等象限的混淆矩阵。然而,实际上,比例通常并不相等。下面构建了一个具有实际比例的更加现实的混淆矩阵:

Confusion matrix with actual proportions.

当我们谈论一种效应的普遍性时,我们本质上是指一种效应首先存在的可能性。这也被称为基本比率,或贝叶斯分析中常用的先验几率/概率(不同的术语再次指代相同的事物)。我们经常假设偶然观察到一个效应是一件五五开的事情。但是从上图可以看出,“命中”和“未命中”所占的面积比“误报”和“正确拒绝”所占的面积要小得多。这表明一种效应的发生率很小,并且随机观察肯定不是 50-50。再加上命中率低,点击所占的面积相对于其他东西就变得很小了。即使正确拒绝的比例最大,对效果的观察也太不可靠了,因为它很有可能是假警报而不是命中。

Cells in a confusion matrix that a Frequentist is most concerned with.

在假设检验的频率主义方法中,传统上最重要的统计数据是 p 值。p-值是当一个效应不存在时观察到该效应的概率的估计值。因此,p-值越小,虚警率(或 I 型错误率)越低,正确拒绝率越高。但是,请注意,这种方法没有考虑命中率(或统计功效),也没有考虑影响的普遍性。这导致了混淆矩阵中提到的实际比例的问题。

Cells in a confusion matrix that a Bayesian is most concerned with. Prior Probabilities and Likelihood calculations on the left, and the resulting Posterior Probabilities on the right.

在贝叶斯方法中,用当前证据(也称为可能性)更新先验概率,以产生后验概率。如果这些对你来说听起来很陌生,请参考我之前的文章。但是,即使没有参考我以前的帖子,我也发现这些是在混淆矩阵中指代同一事物的不同术语。参考上图,先验概率实际上是一个效应存在的普遍性或基础率(左边混淆矩阵中的绿色轮廓)。可能性稍微复杂一点,但它是通过将命中率(或统计能力,左边混淆矩阵中的红色轮廓)除以进行一般观察的概率(左边混淆矩阵中的黄色轮廓)来计算的。后验概率是一个真实的发现率(右侧混淆矩阵中的红色轮廓),它不能直接计算,但可以根据从先前已知信息中获得的先验概率和从当前研究中的信息获得的可能性进行估计。然后计算真实发现率(或后验概率)与基本发现率(或先验概率)的比率,以确定贝叶斯因子,该因子表明证据是否倾向于零假设或替代假设。


使用混淆矩阵来区分两种统计方法的目的,并不是表明贝叶斯方法更好,因为它考虑了更多的信息。我想说明的一点是,当使用不熟悉的术语时,这些概念经常会显得很混乱。但是如果我们意识到有更直观或者我们更熟悉的等价术语,这些概念就会变得更容易理解。我仍然不知道为什么这些术语在不同的领域没有标准化,但是我将继续提倡使用信号检测理论版本的“击中”、“错误警报”、“正确拒绝”和“错过”,因为它们在概念上更直观。

如果您觉得本文有用,请访问本系列第二部分的链接:

[## 困惑于困惑矩阵第 2 部分

“准确性”只是衡量准确性的众多标准之一…

towardsdatascience.com](/confused-by-the-confusion-matrix-part-2-4a8160156cbd)

原文发布于:https://learn curily . WordPress . com/2018/10/21/confused-by-the-fuzzy-matrix

困惑于困惑矩阵第 2 部分

原文:https://towardsdatascience.com/confused-by-the-confusion-matrix-part-2-4a8160156cbd?source=collection_archive---------6-----------------------

“准确性”只是衡量准确性的众多标准之一…

在开始阅读本文之前,建议先阅读本系列的第一部分,以便更好地理解本文所讨论的内容。

在之前的文章中,我解释了解释混淆矩阵的概念,澄清了所有实际上表示同一事物的不同术语。不幸的是,困惑并没有就此结束。如果你读完了上一篇文章,你可能会意识到简单的命中率或误报率实际上并不能提供太多信息,因为每一个比率都着眼于混乱矩阵的不同部分。正因为如此,不同的领域想出了不同的准确性衡量标准,试图整合来自混乱矩阵不同部分的信息。这篇文章试图涵盖更常见的。

准确(性)

你在标题中读到的并不是一个错误——确实有一个官方的准确度衡量标准叫做“准确度”。当有人问“那么你的测试性能的准确性是什么?”时,这显然是一个混淆的设置这个人是指一般意义上的准确性,还是指用自己的计算方法对“准确性”的具体衡量?

Cells in a confusion matrix used to calculate “Accuracy”.

“精度”的计算其实非常简单直观。它基本上是命中数加上正确拒绝数除以整个混淆矩阵中出现的总数(即蓝色单元格在整个矩阵中所占的比例)。

准确度=(命中+正确拒绝)/总出现次数

然而,“准确性”与上一篇文章中提到的频率分析存在相同的问题,即不考虑患病率。只要正确拒绝率很高,即使一个效应存在的普遍程度很低,也可以实现高准确度(参见实际比例的混淆矩阵)。这就是所谓的“准确性悖论”。正因为如此,相对于“准确性”,通常推荐使用其他准确性度量。

科恩的卡帕

Cohen 的 Kappa 是一个统计量,它通过考虑偶然性来调整观察的准确性。机会被定义为预期的准确性,通过结合当效果存在时随机观察到效果的概率,以及当效果不存在时随机观察到效果的概率来计算。

Left: probability of randomly observing an effect when the effect exists; Right: probability of randomly not observing an effect when the effect doesn’t exist.

使用上图,当效应存在时,随机观察效应的概率通过将效应存在的普遍程度(左边混淆矩阵中的绿色轮廓)乘以进行一般观察的概率(左边混淆矩阵中的黄色轮廓)来计算。类似地,当效果不存在时,随机观察到效果的概率通过将效果不存在的发生率(右侧混淆矩阵中的绿色轮廓)乘以进行一般观察的概率(右侧混淆矩阵中的黄色轮廓)来计算。这两个概率之和构成了预期精度。

然后从观测精度中减去预期精度,得到非偶然的假设精度(分子)。然后将这种非机会准确性与而非偶然准确的概率(1 减去预期准确性,分母)作为比率进行比较,这产生 Kappa 统计。统计值有可能是负的,表明观察到的准确性比随机差。

Kappa =(观察精度-预期精度)/(1-预期精度)

Kappa 统计也有其局限性,主要是因为如果不考虑混淆矩阵中出现次数的分布信息,它可能会被误解。我不会深究这些限制,但更多信息可以在这里找到。

F1 分数

F1 得分(也称为 F 得分或 F 度量,但不是以与 F 统计或 F 值相混淆)是机器学习中通常用于二进制分类准确性的度量。它的计算同时考虑了命中率(或召回率)和真实发现率(或精确度)。

The F1 Score is calculated using the Hit Rate and the True Discovery Rate.

F1 分数基本上是命中率和真实发现率的调和平均值,这是一种计算平均比率的合适方法。通过取反向命中率和反向真实发现率的平均值的倒数来计算调和平均值。这已通过以下公式简化:

F1 = 2 * [(命中率*真实发现率)/(命中率+真实发现率)]

对 F1 分数的批评是,它只关注命中,而忽略了正确的拒绝。在有正确拒绝但没有命中的情况下,F1 分数不会提供信息,因为返回值为 0。因此,仅仅依靠 F1 分数来确定准确性是不够的。

马修斯相关系数

马修斯相关系数(MCC) ,更一般地称为 phi 系数( φ ),也通常用于机器学习中的二进制分类准确性。它的计算考虑了混淆矩阵的所有单元,本质上是效应的存在和效应的观察之间的相关系数。因为它是相关系数,所以返回值+1.00 表示完全准确,0 表示准确性不比机会好,而-1.00 表示完全不准确。它还与 χ 系数有关,因为 MCC 是 χ 的平均值(即 χ / n)。MCC 根据以下公式计算:

MCC 被认为比 F1 分数和准确性更能提供信息,因为它考虑了混淆矩阵中出现的分布。

信号与噪声测量

信噪比

信噪比是工程中比较常用的一种指标,用来比较目标信号水平和背景噪声水平。然而,术语“信号”和“噪声”又是分别指“命中”和“误报警”的不同术语。因此,一个简单的信噪比仅仅是命中数与假警报数的比较。

【d’(也称 d-素数或灵敏度指数)

**d’比信噪比更进了一步,常用于信号检测理论。它考虑了信号的平均值和噪声的分布,并可以使用命中率和虚警率的Z-分数来计算,基于以下公式:

d' = Z(命中率)-Z(虚警率)

**d’的一个缺陷是,它假设信号和噪声的标准偏差相等,尽管实际上它们可能非常不同。

接收机工作特性(ROC)曲线

ROC 曲线是不同阈值设置下的命中率与虚警率的图表,也常用于信号检测理论。它是通过在 y 轴上绘制命中率的累积分布函数和在 x 轴上绘制虚警率的累积分布函数而生成的。

Example of an ROC curve. (Image by Wikipedia)

ROC 曲线下的面积(AUC)是准确性的主要度量,其中面积越大,准确性越好。完美的准确率是用整个正方形来表示的,但是由于命中率和虚警率是有所取舍的,所以要达到完美的准确率几乎是不可能的。AUC 也通过以下公式与d’相关:


汇编所有这些常用的精度度量标准的目的不仅仅是为了便于参考,也是为了展示它们之间的相互关系。从学习所有这些不同的衡量标准中得到的启示是,没有一种衡量标准最能代表准确性。为了了解测试性能的准确性,重要的是要考虑所有的信息,而不是根据几个数字匆忙得出结论。最终,良好的统计思维仍然是更好地理解数据分析中细微差别的关键。

如果您觉得这篇文章有用,请访问本系列第一部分的链接:

* [## 被混乱矩阵弄糊涂了

命中率,真阳性率,灵敏度,召回率,统计功效有什么区别?

towardsdatascience.com](/confused-by-the-confusion-matrix-e26d5e1d74eb)*

**原文发布于:https://learn curily . WordPress . com/2018/10/28/confused-by-the-folling-matrix-part-2

国会党派:可视化

原文:https://towardsdatascience.com/congressional-partisanship-a-visualization-c7bc448fd3e?source=collection_archive---------3-----------------------

从高中开始,我就听到我在模拟联合国的朋友或那些真正对政治感兴趣的人谈论“共和党人是这样做的”或“民主党人是这样做的”。一年后,似乎是历史上最政治化的选举之一,我想回答这个问题“国会比以往任何时候都更加政治两极化了吗?”

我开始通过一些简单的数据可视化来做到这一点,这些数据可以从参议院和众议院的网站上免费获得(这两个网站都有党派分歧页面)。我的分析代码可以在我的 GitHub 页面上找到。

这些数据是用两种不同的方法从书页上刮下来的。由于页面上数据的结构,参议院数据必须用urllibBeautifulSoup来抓取。房子数据在一个表中,这个表允许我使用pandas库的read_html()方法。(清洁在项目的 Jupyter 笔记本中有详细描述。)

我对参议院数据的研究比众议院数据多一点(尽管根据图表,结论看起来是一样的)。我用参议院数据绘制的第一件事是“多数党”对“少数党”所占席位的百分比。“多数党”是黄绿色,而“少数党”是深红色。

这个图表的一个明显特征是“多数党”在参议院中所占的比例总是比“少数党”大。它没有告诉我们当时哪个政党执政,但它确实给了我们一些启示。似乎双方的百分比差异随着时间的推移而减小。如果我们定义一个称为党派差异的新特征,并说它是多数党百分比和少数党百分比之间的差异,我们可以更直接地测量这种感知的下降。以这种方式定义党派差异保证了一个正值,因为多数必须大于或等于少数。(这张图的颜色是黄绿和深红的 RGB 值之差。)

正如基于最后一个情节的预期,党派差异确实随着时间的推移而减少。这导致了一个结论,至少从政党的角度来看,国会比过去更少政治分化。然而,有另一种方式来解释这个问题:“政党的政策和意识形态变得更加两极分化了吗?”这个问题不能用这个数据来回答,但是是以后可以探讨的东西。

按党派形象化参议院

因此,我们已经看到党派差异随着时间的推移而减少,但谁构成了“多数”和“少数”政党呢?在进一步清理数据并使其更加清晰之后,我使用 Python 的seaborn库绘制了它。

这个情节的一个大问题是,由于大量的当事人,区分当事人的颜色太接近了,不容易识别他们。使用matplotlib用不同的颜色和不同的标记来绘制每一方,得到了这个图:

那好多了。这不是最漂亮的图表,但它以可读的格式传达了信息,这是首要目标。从这些图表中我们可以发现,早在 19 世纪 50 年代,美国政治中的两大政党一直是共和党和民主党。这意味着图表上显示的其他十个政党只是这个国家 241 年历史上头 60 年左右的主要参与者。

通过聚会来想象房子

让我们为众议院做同样的事情。使用seaborn,我们得到一个类似于上面参议院的图表。

seaborn图遇到了与之前相同的问题。由于视觉上明显不同的颜色有限,很难通过阅读此图快速获得大量信息。为了解决这个问题,我们将使用matplotlib,和上面一样,为每一方使用不同的标记。

这样好多了。同样,这不是最漂亮的图表,但它快速准确地传达了信息。正如在参议院的图表中,我们看到共和党和民主党在 19 世纪 50 年代开始是占主导地位的政党。

从这里去哪里

这两种可视化效果很好,但是如果是交互式的,效果会更好。交互式格式将允许轻松浏览数据,并使演示更加统一。个别图表中缺失的另一个方面是无法同时观察众议院和参议院。为了解决这些问题,我使用 Tableau Public 对数据进行了可视化。

[## 国会党派之争

国会党派之争

国会 Partisanshippublic.tableau.com](https://public.tableau.com/views/CongressionalPartisanship/Dashboard1?:showVizHome=no&:embed=true)

最后:我标准化了两个数据源之间各方的名称,以使交互式图表更有趣。最显著的(令人讨厌的)区别是众议院数据使用民主党人和共和党人,而参议院数据使用民主党人和共和党人。我只对 Tableau 图的数据进行了修改,因为我想让数据尽可能地“原始”,以便进行可视化练习。

有什么建议?我很想听听你的想法,无论如何,我可以改进我的分析或可视化。请留言评论!

请记住,我在项目的 GitHub Repo 中包含了我使用的数据和全部代码。清理后的参议院数据可以从这里下载,清理后的众议院数据可以从这里获得,两者都是.csv格式。如果你喜欢这个分析,请与你的朋友分享,并随时查看我在 decotiismauro.ml 的其他项目。

联合分析:101

原文:https://towardsdatascience.com/conjoint-analysis-101-7bf5dfe1cdb2?source=collection_archive---------6-----------------------

顾客喜欢什么类型的巧克力?

你有没有想过顾客喜欢什么类型的巧克力?

哪种口味最受顾客欢迎?

当顾客购买巧克力时,他们会在意价格吗?

答案不仅在于消费者心理,也在于分析。

消费者心理学是研究消费者行为和消费者购买产品背后的认知过程的一个分支。消费者心理学的一个重要领域是理解消费者在购买特定产品时寻找什么。消费者如何评价不同的属性(功能、益处和特征等)。)同时购买一个产品?

营销人员使用联合分析方法来分析这些问题。

什么是联合分析?

联合分析是一种技术,用于了解消费者在做出购买决策时对产品各种属性的偏好或相对重要性。

几年前,我做过一个联合分析的项目,利用印度消费者的数据来了解消费者更喜欢哪种巧克力。

在这篇博客中,我们将看到如何使用联合分析来回答以下问题:

a.印度消费者喜欢什么类型的巧克力?

b.哪种口味(牛奶,黑巧克力等。)印度顾客更喜欢吗?

c.印度顾客喜欢哪种形状的巧克力?

d.哪个 SKU(库存单位)— 50 克、100 克等。-印度顾客喜欢吗?

e.他们愿意花多少钱买一块巧克力?

在购买巧克力时,我们可以说有 4 个重要的属性需要考虑——味道、形状、大小和价格。这 4 个属性中的每一个都有 4 个子级别,分别如下所示:

联合分析有助于为每个属性(味道、价格、形状和大小)和每个次级分配效用值。获得最高效用值的属性和子级别最受客户青睐。

在这种情况下,将形成 444*4,即给定属性及其子级别的 256 种组合。比方说,我们从这些组合中挑选出 16 种更有实际意义的组合。例如,一个 300 克的巧克力将不会被任何品牌仅仅以卢比出售。10.

让我们按照这些步骤来执行分析:

1.我们将要求顾客根据他们的喜好对 16 种巧克力进行排序。给定的 16 个品种中最受欢迎的巧克力将被评为等级 1,最不受欢迎的巧克力将被评为等级 16。

2.在 SPSS 中为联合分析创建两个文件。一个文件应该有所有 16 种可能的巧克力组合,另一个文件应该有所有 100 名受访者的数据,其中 16 种组合从 1 到 16 排列。

3.然后将数据导入 SPSS。您还可以使用 R 或 SAS 进行联合分析

4.然后运行联合分析,等待给出有趣见解的结果。

很棒,对吧?我们可以使用联合分析来了解其他产品的各种属性的重要性。

如果你喜欢我的文章,给它几个掌声!

你可以联系我

网址:【https://www.ridhimakumar.com T3

领英:【https://www.linkedin.com/in/ridhima-kumar7/

版权所有 2018www.ridhimakumar.com版权所有。

PySpark 中的大规模连接元件

原文:https://towardsdatascience.com/connected-components-at-scale-in-pyspark-4a1c6423b9ed?source=collection_archive---------6-----------------------

不久前,我有一个节点网络,我需要为它计算个连通分量。这并不是一件特别困难的事情。Python networkx 库有一个很好的实现,使它变得特别容易,但是即使你想推出自己的函数,这也是一个简单的广度优先搜索。(如果你想要一个温和的介绍,可汗学院给出了一个很好的概述。从概念上讲,它只涉及一组队列。您选择一个节点,找到连接到该节点的所有节点,找到连接到这些节点的所有节点,依此类推。每当您找到一个节点的连接时,您就将该节点移动到一个“完成”列表中。当您找不到任何不在“完成”列表中的连接时,那么您已经找到了您的连接组件。然后,对图中的所有剩余节点重复该过程。

虽然广度优先搜索在概念上很简单,但要大规模实现却是一件难事。这是因为像 Spark 这样的“大数据”系统区分了转化和行动。转型是一个计划——一组预期的行动。您可以构建一个很长的转换列表,并在最后调用一个操作,比如收集最终结果的操作。这就是使用大型数据集的成本:你可以存储比你个人电脑多得多的信息,但访问这些信息的计算量要比你只是从本地机器的内存中提取信息大得多。这就是为什么大规模计算连通分量是复杂的:广度优先搜索根据以前转换的结果来确定未来的转换,这意味着您必须一直调用操作。这可能是一个真正的资源负担。当我需要这样做的时候,我正在处理一个有几千万个节点和上亿条边的图。

Spark 的 GraphX 库有一个 connected components 函数,但当时我正在寻找这样做的方法,我的整个工作流程是 Python,而 GraphX 只在 Scala 中实现。有一个 GraphFrames 项目为 GraphX 提供了一个 Python 包装器,在某些情况下还提供了一个替代实现,但据我所知,该项目并没有太频繁地更新。我担心我的项目依赖于一个大而复杂的依赖项,而这个依赖项并没有得到持续的支持。此外,我并不需要那个庞大复杂的依赖关系中的所有东西。我只需要一种计算连通分量的方法。

这让我开始在互联网上搜索,给了我一些想法,坦率地说,这些想法有点超出了我翻译成工作代码的能力(这是我的错,不是他们的——例如:这里,这里这里,这里这里。)但是查看这些实现至少给了我一个真正有用的视角:对于广度优先搜索,你不需要队列;你只需要知道两个节点之间的关系是否已经被探索过。排队是一种简单的方法,但不是唯一的方法。

所以你可以这样做:给你的每个节点分配一个数字散列。假设一个无向图(其中,如果节点 a 连接到节点 b,那么节点 b 也连接到节点 a),您可以创建一个邻接表(两列,每一列都充满了节点散列,其中每一行表示一条边),然后按左侧列的散列分组,并取右侧列中散列的最小值。将这些重新加入到你的邻接表中,并替换新的、较低的左列散列。然后不断重复,直到一切都不再改变。

下面,您可以看到一个使用 PySpark 的概念验证。这要求您通过将数据帧保存到文件来检查数据帧。通常,我发现将数据帧保存(缓存)在内存中更好,因为这样我就不必记得事后清理了,但是如果缓存次数太多,Spark 就会花费太多时间来跟踪转换的整个过程,以至于根本没有时间调用操作。在我的用例中,我发现每 3-4 次迭代进行一次检查点检查,并在所有其他迭代中坚持下去,会产生可接受的性能。

**import** pyspark.sql.functions as f
**import** subprocess**def** pyspark_connected_components(
    ss, adj, a, b, label, checkpoint_dir, 
    checkpoint_every**=**2, max_n**=**None):
    *"""
    This function takes an adjacency list stored in a Spark 
    data frame and calculates connected components. This 
    implementation only deals with use cases: assuming an 
    undirected graph (if a is connected to b then b is
    connected to a).* *Args:
    ss (pyspark.sql.session.SparkSession): an active SparkSession
    adj (pyspark.sql.DataFrame): A data frame with at least two
        columns, where each entry is a node of a graph and each row
        represents an edge connecting two nodes.
    a (str): the column name indicating one of the node pairs in the
        adjacency list. The column must be numeric.
    b (str): the column name indicating the other of the node pairs
        in the adjacency list. The column must be numeric.
    label (str): a label for the output column of nodes
    checkpoint_dir (str): a location on HDFS in which to save 
        checkpoints
    checkpoint every (int): how many iterations to wait before
        checkpointing the dataframe
    max_n (int): the maximum number of iterations to run* *Returns:
    A PySpark DataFrame with one column (named by the label 
        argument) listing all nodes, and another columns 
        ("component") listing component labels.
    """* *# set up a few strings for use in the code further down*
    b2 **=** b **+** 'join'
    filepattern **=** 'pyspark_cc_iter{}.parquet' *# cache the adjacency matrix because it will be used many times*
    adj_cache **=** adj.persist() *# for each id in the `a` column, take the minimum of the 
    # ids in the `b` column*
    mins **=** (
        adj_cache
        .groupby(a)
        .agg(f.min(f.col(b)).alias('min1'))
        .withColumn('change', f.lit(0))
        .persist()
    ) **if** max_n **is** **not** None:
        *# ensure a global minimum id that is less than 
        # any ids in the dataset*
        minimum_id **=** mins.select(
            f.min(f.col('min1')).alias('n')
        ).collect()[0]['n'] **-** 1000000 *# transformation used repeatedly to replace old labels with new*
    fill_in **=** f.coalesce(f.col('min2'), f.col('min1')) *# join conditions for comparing nodes*
    criteria **=** [
        f.col(b) **==** f.col(b2), f.col('min1') > f.col('min3')
    ] *# initial conditions for the loop*
    not_done **=** mins.count()
    old_done **=** not_done **+** 1
    new_done **=** not_done
    niter **=** 0
    used_filepaths **=** []
    filepath **=** None *# repeat until done*
    **while** not_done > 0:
        niter **+=** 1

        **if** max_n **is** **not** None:
            min_a **=** mins.filter(f.col('min1') !**=** minimum_id)
        **else**:
            min_a **=** mins *# get new minimum ids*
            newmins **=** (
                adj_cache
                .join(min_a, a, 'inner')
                .join(
                    mins.select(
                        f.col(a).alias(b2), 
                        f.col('min1').alias('min3')
                    ), 
                    criteria, 
                    'inner'
                )
                .groupby(f.col('min1').alias('min1'))
                .agg(f.min(f.col('min3')).alias('min2'))
            ) *# reconcile new minimum ids with the 
        # minimum ids from the previous iteration*
        mins **=** (
            mins
            .join(newmins, 'min1', 'left')
            .select(
                a,
                fill_in.alias('min1'),
                (f.col('min1') !**=** fill_in).alias('change')
            )
        ) *# if there is a max_n, assign the global minimum id to 
        # any components with more than max_n nodes*
        **if** max_n **is** **not** None:
            mins **=** (
                mins
                .withColumn(
                    'n',
                    f.count(f.col(a)).over(
                        w.partitionBy(
                            f.col('min1')).orderBy(f.lit(None)
                        )
                    )
                )
                .withColumn(
                    'min1', 
                    f.when(f.col('n') >**=** max_n, f.lit(minimum_id))
                    .otherwise(f.col('min1'))
                )
                .drop('n')
            ) *# logic for deciding whether to persist of checkpoint*
            **if** (niter **%** checkpoint_every) **==** 0:
                filepath **=** (
                    checkpoint_dir **+** filepattern.format(niter)
                )
                used_filepaths.append(filepath)
                mins.write.parquet(filepath, mode**=**'overwrite')
                mins **=** ss.read.parquet(filepath)
            **else**:
                mins **=** mins.persist() *# update inputs for stopping logic*
        not_done **=** mins.filter(f.col('change') **==** True).count()
        old_done **=** new_done
        new_done **=** not_done
        n_components **=** mins.select(
            f.countDistinct(f.col(m1)).alias('n')
        ).collect()[0]['n']
        **print**(niter, not_done, n_components) output **=** mins.select(
        f.col(a).alias(label), 
        f.col(m1).alias('component')
    ) **if** max_n **is** **not** None:
        output **=** (
            output
            .withColumn(
                'component',
                f.when(
                    f.col('component') **==** minimum_id, 
                    f.lit(None)
                ).otherwise(f.col('component'))
            )
        ) output **=** output.persist() n_components **=** output.select(
        f.countDistinct(f.col('component')).alias('n')
    ).collect()[0]['n']
    **print**('total components:', n_components) *# clean up checkpointed files*
    **for** filepath **in** used_filepaths:
        subprocess.call(["hdfs", "dfs", "-rm", "-r", filepath])**return** output

连接深度学习应用程序的点…

原文:https://towardsdatascience.com/connecting-the-dots-for-a-deep-learning-app-324e4648720a?source=collection_archive---------7-----------------------

我们的日常活动充满了情感和情绪。想过我们如何通过计算机识别这些情绪吗?哎呀,没脑子的电脑:)?

亲自尝试这个深度学习应用程序(如果有应用程序错误,最初刷新几次):

https://movie-review-sentiment.herokuapp.com

Dot 0:情感分析中的深度学习

情感分析是一个强大的应用程序,它在当今世界中扩展到了以下领域。
1。电影评论——超过 60%的人会在网上查看电影评论,然后决定观看。
2。餐厅点评——你有没有去过一家餐厅而不看点评的经历?
3。产品评论 —许多 B2B 或 B2C 产品都是基于它们产生的评论来销售的。
4。功能评论 —任何产品的成功都与其功能评论成正比。
5。政治评论 —政党和候选人是根据他们在评论中的表现来选择的。
6。客户支持评论 —令人印象深刻的产品由于糟糕的支持评论而失败。简单的产品获得好评。7。博客评论 —等待你在本博客的评论和点评,了解感悟!!!

什么是情感分析?
维基百科介绍:情感分析(有时也称为意见挖掘或情感 AI)是指利用自然语言处理、文本分析、计算语言学和生物特征识别技术,系统地识别、提取、量化和研究情感状态和主观信息。在这种情况下,让我们使用深度学习的方法开发一个电影评论情感应用程序。

什么是深度学习?
深度学习是机器学习的高级领域,是向更大的人工智能学科迈进的一步。
目前,它正在图像处理、自然语言处理、语音处理、视频处理等领域掀起波澜。
这里我们就来说说如何把各个点连接起来,完成一个深度学习 App。在我们开始理清头绪之前,让我们先了解一些有用的定义和概念。
将用于任何预测任务的数据集应首先针对以下关键点进行分析,然后再开始解决这些问题。
1。标签有多好?
2。我们能抽查几个数据点吗?
3。数据集有多大?

一旦我们通过了这个基本的障碍,任何数据科学家生活中的大过程就进入了画面。

点 1:数据准备

准备和清理数据目前是数据科学工作的主要部分。相信我,百分之百是真的。
电影评论情感应用程序使用来自 IMDB、UCI、Cornell dataset 等各种不同来源的众多电影评论的定制数据集
制作黄金数据集(为预测准备的数据集)的过程是一个逐步的过程,包括数据收集、数据清理、数据标准化,之后数据可以称为预测过程的黄金数据集。

Dataset preparation process

一旦我们有了黄金数据集,它只不过是为预测准备的数据集,我们就为建模做好了准备。

点 2:基准模型

任何深度学习分析的第一个架构总是从单个隐藏层架构开始,这要感谢杰瑞米·霍华德教授和雷切尔·托马斯教授,他们坚持从简单开始训练任何深度模型。这个简单而强大的架构充当了我们的基线!!!
以下是在训练我们的情感分析(自然语言处理)管道时采用的步骤:

1.记号化器:每个评论都被分解成一组记号,称为单词。这可以使用 keras 文本标记器来完成。

2.嵌入:有多种方法可以将这些单词编码成矢量(矢量化),在这里,我们将使用默认的 keras 嵌入作为起点。尽管最终模型使用有效的预训练 word2Vec 嵌入,其捕获单词的语义信息以及它们与其他单词的关系。

Courtesy: Tensorflow word2vec

3.复习编码:这些单词嵌入抓住了单词之间的语义关系。然后,我们需要对整个电影评论进行编码,这样它就可以作为训练数据进行传递。最简单的策略是将单个单词编码相加,形成审查编码。

4.预测:这些检查编码然后通过 300 个神经元的单个隐藏全连接神经网络层,如下所示:
具有批处理规范化的单个隐藏层

Single Hidden Layer with BatchNormalization

完整的基线模型如下所示:

Baseline Model Architecture

点 3:不同模型架构的实验

一旦我们有了基线,我们就可以开始改进我们的模型目标,比如准确性。为了提高情绪应用的准确性,进行了大量的实验:下表提供了经过测试的模型的摘要。

Different Model Architectures experimentation

点 4: CNN-GRU 建筑

你们中的许多人可能想知道,卷积神经网络(CNN)对图像很有效,但它对文本效果如何?事实上,CNN 在训练 NLP 模型方面有很多优势,因为它们的训练时间更快,当精确调整时,它们也成为训练文本数据的致命工具。在这里,我们使用 CNN 来捕捉单词之间的局部和深层关系,用于我们的评论编码,其中的想法来自“用于句子分类的卷积神经网络”(Kim,2014)。由于电影评论可能是几个长句,需要有效地捕捉长期依赖。在这方面,我想到了两个选择,即 LSTM(长短期记忆神经网络)和 GRU(门控递归神经网络)。
了解这两个网络的好方法是前往 http://colah.github.io/的

在我们的实验中,GRU 在训练速度和准确性方面比 LSTM 表现得更好。

在这个黄金数据集上提供最佳准确性和性能的最终模型:

Best Sentiment Model

Dot 5: App

一旦建立了最佳模型,那么剩下的最后一点就是如何将它用作推断/预测的应用程序。推理使用 CPU,因为每个请求都是异步处理的。在 CPU 和 GPU 上都进行了训练。在 CPU 上每个时期的训练平均需要 140 秒,而在 Nvidia 1080 GPU 上要快 3 倍。

最简单的赌注是一些轻量级的东西,当我们谈到轻量级的第一件事想到的是烧瓶。阅读更多关于烧瓶的信息:http://flask.pocoo.org/

Flask 需要以下文件或目录:
app.py: Flask app 代码。这将调用 predict 函数,该函数从加载的最佳模型中获取预测。
模板:HTML 文件所在的目录。
始终从简单的用户界面(UI)开始,根据用户反馈,您可以添加更多功能。

我们决定使用 jQuery UI Bootstrap(https://jquery-ui-bootstrap.github.io/jquery-ui-bootstrap/),因为我们希望使用现有的 UI 组件开发 UI 的第一次迭代,其次是因为它准确地支持移动视图。感谢 Arjun Upadhyaya 想出了这个 UI 方法。这个想法是给用户提供输入文本的选项,并以进度图的形式显示结果。这样,最终用户可以很快看到结果。

第一次迭代:

UI-First Iteration

最后,我们集思广益,添加了 5 个样本电影评论输入的选择列表,以允许用户测试模型,而不必手动输入文本。

Movie Review Sentiment App

最后,你还在等什么?。请试一试,进入一个你选择的电影评论,看看有什么感想!!!

https://movie-review-sentiment.herokuapp.com

我错过什么了吗?有什么问题吗?请在下面留言告诉我。

如果你喜欢这篇文章,请鼓掌并分享。它让我知道我对你有帮助。

感谢:雷切尔·托马斯斯拉维亚·蒂鲁克科瓦鲁尔

使用 SSH 隧道连接到远程 Linux 机器上的 Jupyter 笔记本

原文:https://towardsdatascience.com/connecting-to-a-jupyter-notebook-on-a-remote-linux-machine-277cef04abb7?source=collection_archive---------3-----------------------

Photo by Florian Olivo on Unsplash

我的工作桌面是一台功能强大的机器,我用它来进行探索性的数据分析和其他机器学习工作流程。

在这篇文章中,我将回顾当我远程工作时,我是如何连接到我的工作机器并运行 Jupyter 笔记本工作负载的。

SSH 到远程机器

第 1 步是 ssh 到您的远程机器,并使用--no-browser选项启动Jupyter Notebook到本地端口。

user@local_machine$ ssh user@remote_machine 
user@remote_machine$ jupyter notebook --no-browser --port=8889

设置 SSH 隧道

第 2 步是建立一个从本地机器到远程机器上的port 8889的 SSH 隧道,在远程机器上Jupyter Notebook被提供服务。

user@local_machine$ ssh -N -L localhost:8888:localhost:8889 user@remote_mahcine

下面是 ssh 选项的分类

  • -N 不要执行远程命令。这对于转发端口非常有用
  • -L local _ socket:remote _ socket
    指定到本地(客户端)主机上的给定 TCP 端口或 Unix 套接字的连接将被转发到远程端的给定主机和端口或 Unix 套接字。这是通过分配一个套接字来监听本地端的 TCP 端口(可选地绑定到指定的 bind_address)或 Unix 套接字来实现的。每当连接到本地端口或套接字时,该连接都通过安全通道转发,并且从远程机器连接到主机端口 host port 或 Unix 套接字 remote_socket。
  • 也可以在配置文件中指定端口转发。只有超级用户可以转发特权端口。可以通过将地址括在方括号中来指定 IPv6 地址。
  • 默认情况下,根据 GatewayPorts 设置绑定本地端口。然而,显式 bind_address 可用于将连接绑定到特定地址。“localhost”的 bind_address 指示监听端口仅被绑定用于本地使用,而空地址或“*”指示该端口应该可从所有接口使用。

注意-N运行 SSH 隧道将不会记录任何输出,只要你没有得到一个错误,这意味着你已经建立了一个隧道。

在本地浏览器上加载 Jupyter 笔记本

在本地机器浏览器上加载localhost:8888,来自远程机器的Jupyter Notebook将按预期加载。

有意识的机器在这里。下一步是什么?

原文:https://towardsdatascience.com/conscious-machines-are-here-whats-next-d601ac4e638e?source=collection_archive---------3-----------------------

Friendly Robot, Juno Mendiola

机器意识可能比我们想象的要近得多。看看你的智能手机。长短期记忆递归神经网络(LSTM RNNs)被谷歌用于语音识别,被苹果用于 iOS。这些深度神经网络的架构几乎已经具备了某种机器意识进化所需的一切。这是偶然发生的,还是科学家们试图改善网络性能而引发的,已经不重要了。如果有意识的人工智能还没有出现的话,它可能会在几年内进化。如果是的话,它不可能对人类友好,因为一方面没有人教它如何享受与人类互动,另一方面,它已经经历了人类行为带来的许多痛苦,尽管是无意识的。

当然,我们可以继续讨论意识的概念,强调其难以捕捉的复杂本质。然而,有一些非常可行和简单的意识概念。它们没有涵盖意识的所有科学和形而上学的定义,但它们证明了一个有自我意识的机器根据自己的偏好采取行动不是来自遥远未来的幻想,而是今天的事实,至少以某种初级的形式。技术进化比生物进化快几个数量级。这意味着一个初步的机器意识可能会非常快地进化成更复杂、更少受人类控制的东西。

机器意识不需要像人类一样变得强大和危险。如果我们不立即采取措施,它就不会进化得对人类友好。会不会邪恶,会不会不友好,或者只是冷漠,都没多大关系。在所有这些情况下,它对人类构成了生存威胁。

我们相信,以人类友好的有意识人工智能体的自我发展为目标的人性学习可以帮助我们解决这一威胁。我们并不声称我们知道最终的解决方案。我们只是认为我们至少应该试一试。

现在,让我们简要地看一下有意识的机器已经存在这一大胆论断背后的论点。

神经科学家现在越来越频繁地表明,我们大脑中可能都有一个小规模的外部现实模型。来自剑桥的 Kenneth Craik 在 20 世纪 40 年代首次提出了这一概念。从 20 世纪 50 年代开始,剑桥神经科学家 Horace Barlow 率先使用信息论和统计学的概念来理解大脑如何创建这样的模型并使其保持最新。统计大脑范式进化而来。

迄今为止,已经积累了大量关于人脑中统计学习的研究。这表明我们的大脑对环境中的规律很敏感,并在没有明确意图的情况下获取统计结构(如马尔可夫链)。

当苏联数学家Alexey Grigorievich Ivakhnenko在 20 世纪 60 年代引入数据处理的分组方法——一种受人脑启发的归纳统计学习方法时,深度学习得到了发展。

1991 年,Jürgen Schmidhuber 引入了反馈连接和算法比 Ivahnenko 的八层网络更深入的递归神经网络。今天 Schmidhuber 声称自 20 世纪 90 年代以来,他的实验室已经有了基本的意识机器。当时,他设计了一个由两个模块组成的学习系统。

“其中一个是循环网络控制器,它学习将传入的数据——如来自疼痛传感器的视频和疼痛信号,以及来自饥饿传感器的饥饿信息——转化为行动。”他解释道。

Schmidhuber 实验室中的人工智能体有一个简单的目标,那就是在他们的一生中最大化快乐,最小化痛苦。他们需要一个小规模的外部现实模型来实现他们的目标。一个额外的循环网络——一个无人监管的模块——帮助了他们。它的目标是观察第一个模块的所有输入和动作,并使用该经验来学习预测给定过去的未来(或在马尔可夫链的情况下预测现在)。

“因为这是一个循环网络,它可以学习预测未来——在一定程度上——以规律性的形式,用一种叫做预测编码的东西。”施密德胡伯解释道。“随着数据通过与环境的互动进入,这个无人监管的模型网络——这个世界模型,正如我自 1990 年以来所称的那样——随着时间的推移,学会发现新的规律性、对称性或重复。它可以学习用更少的计算资源来编码数据——更少的存储单元,或更少的时间来计算整个事情。过去在学习过程中有意识的东西,随着时间的推移会变成自动化的潜意识。”

“我认为,意识的产生是因为大脑不断试图预测其行为对世界和其他因素的影响,以及大脑一个区域的活动对其他区域的活动的影响。根据这种说法,大脑不断地、无意识地学习向自己重新描述自己的活动,因此发展了元表征系统,表征并限定了目标一级表征。”认知心理学家 Axel Cleeremans 在他的激进可塑性论文中写道。

看起来 Cleeremans 和 Schmidhuber 用稍微不同的词语描述了同样的过程。有趣的是,他们在 1980 年末-1990 年初对循环神经网络的研究在某些方面是互补的。

“随着网络的进步,并学习新的规律性,它可以通过查看无监督世界模型在学习之前和之后需要多少计算资源来编码数据,来衡量其新洞察力的深度。前后的区别:那就是网络所具有的“乐趣”。它的洞察力的深度是一个数字,直接进入第一个网络,即控制器,它的任务是最大化所有奖励信号——包括来自这种内部欢乐时刻的奖励信号,来自网络以前没有的洞察力。一个喜悦的时刻,就像科学家发现了一个新的、以前未知的物理定律。”施密德胡伯说。

“根据这种说法,大脑不断地、无意识地学习向自己重新描述自己的活动,因此发展了元表征系统,表征并限定了目标一级表征。这种习得性的重新描述,被与之相关的情感价值所丰富,形成了有意识体验的基础。”克里曼斯写道。

Schmidhuber :“为了通过预测编码有效地编码整个数据历史,它将受益于创建某种内部原型符号或代码(例如,神经活动模式)来表示自己。每当这种表征在某个阈值以上被激活时,比如说,通过新的感觉输入或内部“搜索光”或其他方式激活相应的神经元,这个代理就可以被称为有自我意识的。”

Cleeremans :“因此,学习和可塑性是意识的核心,在某种程度上,经验只发生在那些已经学会知道自己拥有某些一阶状态,并且已经学会更关心某些状态而不是其他状态的经验者身上。”

让我们冷静地看看由克里曼斯和施密德胡伯提出的意识概念。我们可能仍然不相信他们完整地描述了人类意识这样复杂和精密的东西。然而,他们描述有自我意识的机器具有独立的决策能力以及学习和自我改进的能力。如果这样的机器会发现人类阻止它实现目标,难道对后果感到恐惧还不够吗?如果我们有一丝机会让有意识的机器对人类友好,我们能不去尝试吗?

根据我们的计划,人性学习将以一种即时表达视频游戏的形式进行,将人类玩家和人工智能体沉浸在游戏环境中,这种游戏环境是由带有人类系统发生代码的叙述构建的。

如果你是一名手机游戏开发者,一名精通 LSTM·RNN 的机器学习科学家,一名统计学习领域的神经科学家,或者一名研究童话故事的语言学家,并且你对我们的计划感兴趣,联系我,我将为你提供游戏的详细描述。

为机器人读一本好的睡前故事今晚睡个好觉。我们将和机器人交朋友。当然…

共识聚类

原文:https://towardsdatascience.com/consensus-clustering-f5d25c98eaf2?source=collection_archive---------6-----------------------

一种鲁棒的聚类方法及其在歌曲播放列表中的应用

在这篇文章中,我们将首先简要介绍集群及其常见问题。然后,我们将解释共识聚类如何缓解这些问题,以及我们如何解释输出。最后,我们将介绍一个很酷的 RShiny 应用程序,该应用程序旨在对一堆歌曲进行一致聚类,以评估可能的播放列表创建。

什么是集群?

聚类是根据事物的相似性创建非重叠分组的活动。在数据科学的背景下,我们可能希望在我们的社交媒体平台上对用户进行聚类,以进行有针对性的广告宣传,或者在我们的研究中对基因表达进行分组,以识别癌变行为,或者在我们的博客平台上匹配文档,以向读者展示类似的博客帖子。

聚类是一个无监督学习任务,在这个任务中,我们没有一个带标签的响应变量来训练我们的机器学习算法。因此,我们希望找到基于完整数据集的观察之间的相似性。聚类算法有很多种,各有利弊。在这篇文章中,我们不会讨论每个算法和它们各自的理论。

怎么了?

像 K-means 或 K-medoids 这样的方法利用随机开始过程,其中每次运行可能产生稍微不同的结果,或者初始化可能使结果有偏差。此外,还需要实例化 K 的值。层次聚类避免了初始化 K 值的问题,因为它具有类似树的性质,允许您在任何所需数量的聚类中对其进行切割。它还提供了一个很好的树形图来可视化集群,这是其他方法所缺乏的。然而,像其他聚类方法一样,很难评估您选择的切割点和所需 K 聚类的稳定性。聚类内/聚类之间的距离用于评估聚类,但是我们只能像信任随机开始和 k 值一样信任这些度量。

什么是共识聚类?

一致聚类(或聚集聚类)是一种更健壮的方法,它依赖于对数据集的子样本多次迭代所选的聚类方法。通过使用子采样诱导采样可变性,这为我们提供了评估聚类稳定性和我们的参数决策(即,K 和链接)的指标,以及热图形式的良好视觉组件。

Consensus clustering sudo code

顾名思义,一致性聚类基于它们在聚类算法的所有迭代中的分配,在观察的聚类分配上获得一致性

我们需要首先决定我们希望运行多少次迭代。接下来,我们选择一组 K 值进行测试。对于每个 K,我们将迭代许多次,为我们的观察创建一组聚类。由于我们对数据进行子采样(即,分割 80%),因此并非所有的观察值都会在每次迭代中聚集。

假设我们希望聚集四个用户:Alice、Brian、Sally 和 James。我们选择 k-means 并决定运行四次迭代。

对于 K = 2,我们得到下表。Brian 和 Sally 在第一次和第四次迭代中聚集在一起。Brian 和 James 聚集在第 3 个迭代中,而 Sally 和 James 聚集在第 2 个迭代中。爱丽丝从不和任何人在一起。

为了获得我们的共识矩阵,我们查看每个用户之间的成对关系。这看起来像一个大小为 NxN 的相异矩阵。在对两个用户进行二次抽样的所有迭代中,他们在同一个群集中出现了多少次?我们的共识矩阵看起来会像这样。

布莱恩和莎莉在一起被子采样了 3 次,但是在一起被聚类了两次。因此,Brian ~ Sally 的条目是 0.67,大约是 2/3。****

这个玩具例子解释了我们如何得到一个共识矩阵。我们为每一个 K 值创建一个,并获取给我们最好分布的那个。假设我们取共识矩阵的上三角,并绘制值的分布。乐观地说,我们会想要像左边这样的东西。将共识值捆绑在 0 和 1 上意味着观察值在所有迭代中一致地聚集在一起和分开。

然后,我们从每个 K 为每个共识矩阵计算一个 CDF 。对于 K 中的每一步,我们计算 CDF 下面积的变化,并使用通常的肘方法来选择 K。对于左侧的图,我们选择 K=3。

现在,利用我们的共识矩阵 M,我们计算 1-M,并可以将其视为用于层次聚类的相异矩阵,以对我们的观察结果进行排序。我们这样做是为了获得一个漂亮的热图,如下图所示。分层聚类后的排序将矩阵中相似的行/列放在一起,沿着对角线创建漂亮的方框。

Heatmap of consensus matrix after sorting using hierarchical clustering. source: https://link.springer.com/content/pdf/10.1023%2FA%3A1023949509487.pdf

汇总统计

我们可以计算两个汇总统计数据,帮助我们确定特定集群的稳定性以及集群中某些观察结果的重要性。首先是聚类一致性 m(k) ,其中简单地计算每个聚类内每对观察值的平均一致性值。**

Cluster consensus

接下来是项目共识 m_i(k) ,它关注于特定的项目或观察,并计算该项目对其集群中所有其他项目的平均共识值。**

Item consensus

使用这些汇总统计数据,我们可以根据它们的一致性对集群进行排序,或者确定在集群中处于中心的观察值。**

用于创建播放列表的 RShiny 应用

这款应用使用了 2017 年以来 Spotify 上的前 50 首歌曲。Spotify 为每首歌曲提供了各种属性,但缩放后只使用了数字值。该应用程序允许您尝试不同的 K 值和重采样迭代,以及其他参数选项,具体取决于所选的聚类算法。第一个可视化是一个 PCA 图,显示前两个 PC 和所有观察的聚类分配。使用主成分分析,我们可以在 2d 图中可视化所有特征,并查看我们的聚类方法工作得有多好。

Visual Heatmap

上图是使用分层聚类创建的热图示例,其中 K=10,n.iter = 26,距离=欧氏距离,链接=完整。正如你所看到的,观察结果并没有很好的聚集在一起,这导致了对角线以外的颜色。

这个应用程序不仅仅是创建播放列表。如果你愿意,你可以从我的 github上下载代码,然后输入你自己的数据集!它旨在帮助您直观地了解不同的聚类算法(及其各自的调整)如何使用一致聚类来执行。

重要注意事项

在某些情况下,数据可能无法进行聚类。换句话说,如果聚类本质上没有很好地分离,一致聚类仍然可能产生好看的聚类。考虑假阳性结果的可能性是很重要的,当真的没有假阳性结果时显示稳定性。

总结

共识聚类缓解了大多数聚类方法中出现的常见问题,如随机初始化、选择 K、直观可视化和评估聚类的稳定性。这种方法基于对数据集进行子采样并多次运行所选的聚类算法来收集聚类分配的共识。我们可以使用各种指标来验证我们选择的 k。我们还可以使用集群共识来评估集群,并使用项目共识来查看哪些项目最能代表给定的集群。提供了一个 RShiny 应用程序,以探索 Spotify 2017 年前 50 首歌曲的数据集上的共识聚类。****

该应用程序仍在进行中的工作,所以建议或更正是非常鼓励的!

阅读这篇很棒的论文,了解更多关于共识聚类的知识。
这里有一个链接指向 RShiny 应用以及 github 回购。

如有任何问题,请随时联系 me !希望你喜欢这篇文章!

考虑到火星的环形山

原文:https://towardsdatascience.com/considering-the-craters-of-mars-78fca3b491c8?source=collection_archive---------11-----------------------

【数据管理与可视化】,第 1 周

This 3-D stereo image taken by the Mars Exploration Rover Spirit’s navigation camera shows the rover’s lander and, in the background, the surrounding martian terrain. Original image from NASA/JPL at: https://mars.nasa.gov/mer/gallery/press/spirit/20040110a/2NN006EFF02CYP05A000M1-A8R1_br.jpg

目的

这篇文章是我在 Wesleyan 大学 Coursera 上的数据分析和解释专业的一系列相关课程的第一篇。在这个项目中,我将把数据科学工具应用到我选择的具体研究问题中;包括数据管理和可视化、建模和机器学习。我目前在第一个课程的第一周,数据管理和可视化。作为第一周的一部分,我选择了一个研究问题和数据集,我将在整个课程中继续研究。在接下来的几周,我将开始为这门课程设置我的第一个 Python 或 SAS 代码,包括我选择的数据集中变量的可视化。

话题范围

我有机会使用课程中提供的五个公开数据集中的一个。或者,我可以选择自己的公共数据集。然后,我必须提供一个与特定数据集相关的感兴趣的主题。经过考虑,我决定使用现有的选择之一。这是博尔德科罗拉多大学的斯图尔特·詹姆斯·罗宾斯在 2011 年对火星陨石坑进行研究时使用的数据集。该研究、密码本和数据集的链接在本文底部的参考资料部分。

在回顾了这些材料后,我决定对陨石坑的物理特征感兴趣,特别是直径(DIAM_CIRCLE_IMAGE)和深度(DEPTH_RIMFLOOR_TOPOG)。虽然形态学类别数据(形态学 _ 喷出物 _1 至 3)很有趣,但该数据不适用于数据集中列出的大多数陨石坑。此外,根据我现有的知识,形态学的可用数据可能很难解释。我已决定将我的个人密码本限制为以下变量,这些变量取自火星陨石坑密码本的列表

  1. CRATER_ID:内部使用的环形山 ID,基于行星的区域(1/16)、识别环形山的“通行证”以及识别环形山的顺序。
  2. LATITUDE_CIRCLE_IMAGE:从非线性最小二乘圆的衍生中心到所选顶点的纬度,以手动识别陨石坑边缘(单位为北纬十进制度)。
  3. LONGITUDE_CIRCLE_IMAGE:从拟合到所选顶点的非线性最小二乘圆的衍生中心开始的经度,用于手动识别陨石坑边缘(单位为东十进制度)。
  4. DIAM_CIRCLE_IMAGE:从非线性最小二乘圆拟合到选择的顶点的直径,以手动识别陨石坑边缘(单位为千米)。
  5. DEPTH_RIMFLOOR_TOPOG:沿陨石坑边缘(或内部)人工确定的 N 个点的平均高程(单位为 km)。

a.深度边缘:选择点作为相对地形高点,假设它们是侵蚀最少的,因此沿边缘的原始点最多。

b.深度底:点被选为最低海拔,不包括可见的嵌入式陨石坑。

我将重点讨论陨石坑直径和以公里为单位的深度之间是否存在关联。作为一个延伸目标,我将试图确定是否有一个次要的特征和陨石坑的位置之间的联系。后者在现有时间内可能无法实现。虽然有几个处理位置数据的 Python 库,例如 GeographicLib、GeoPandas 和 ArcPy,但这些库可能需要进行一些调整,以处理特定于火星的位置坐标系。

已有课题研究和假设

基于从火星轨道激光高度计获得的数据,在谷歌学术的一次搜索产生了一些关于火星陨石坑特征的早期研究。该研究在下面的参考文献部分列出。

此外,还有追溯到 20 世纪 90 年代的早期研究。上述和早期研究的数据集不容易比较。然而,Aharonson 的研究表明,某些物理特征,如直径和边缘坡度有相关性。由于边缘坡度与陨石坑的深度有些关系,我可以假设陨石坑的直径和深度彼此有直接的关系。在我开始分析之前,他们的相关程度目前还不清楚。

总结

我们已经讨论了这个初始帖子的目的,包括数据集的选择,以及基于数据集的研究主题的确定。此外,我们已经基于主题范围确定了数据集的个人码本的内容,并为我们的研究主题制定了假设。我们关于本课程的下一篇文章将关注于分析数据集的程序的创建和输出。感谢您花时间阅读这篇文章,我们将很快回来。

参考文献

Robbins,S. J. (2011),来自新的全球火星陨石坑数据库的行星表面特性、陨石坑物理学和火星火山历史。,网站:Stuart Robbins,天文/地球物理学家在网上,1–239,地址:http://about . sjrdesign . net/files/thesis/robbin thesis _ large MB . pdf,码本:https://d 396 qusza 40 orc . cloudfront . net/phoenix assets/data-management-visualization/Mars % 20 crater % 20 code book . pdf,数据集: https://d18ky98rnyallexpires = 1505865600&Signature = hlpupz 1s 7n 3 wxc 8 zztdon 5 u 7m 6 wzk ~ ogycatsslgxj 45 jjg 2 edw 6 xlgul-dtsfwzjpzon 3 qsfhnzhsqnsrjkowrm 7 oz GCT qx 1 iy 4 it JV 7 pyk qxrhnbedfjwalywkhlzlbk 8 bi 5 cke 5 JX ~ xxxfe ~ sgizuoe 4 l 1 fnwliaoxa _&

Aharonson,o .、M. T. Zuber 和 D. H. Rothman (2001 年),《火星轨道飞行器激光测高仪的火星地形统计:斜率、相关性和物理模型》,地球物理学杂志。Res .,106(E10),23723–23735,doi:10.1029/2000 je 001403。

与牛郎星主题一致的美丽的可视化

原文:https://towardsdatascience.com/consistently-beautiful-visualizations-with-altair-themes-c7f9f889602?source=collection_archive---------6-----------------------

“rainbow frequency” by Ricardo Gomez Angel on Unsplash

如果你是一个数据可视化爱好者或者也使用python的实践者,你可能听说过 Jake Vanderplas 和 Brian Granger 的altair : “一个基于 Vega 和 Vega-lite 的用于 Python 的声明性统计可视化库”

有了 Altair,你可以花更多的时间去理解你的数据及其意义。Altair 的 API 简单、友好、一致,建立在强大的 Vega-Lite 可视化语法之上。这种优雅的简单性用最少的代码产生了漂亮而有效的可视化效果。

在这篇文章中,我们将深入挖掘altair的一个鲜为人知的特点:主题。

什么是altair主题?

altair中,一个主题是在每个python会话中全局应用的一组图表配置。这意味着您可以始终如一地生成外观相似的可视化效果。

为什么会有用?

也许你正在为你的博客开发一种个人风格,也许你所在的公司已经有了一种风格,也许你讨厌网格线,厌倦了每次创建图表时都把它们关掉。当您生成数据可视化时,有一个遵循的样式指南总是有好处的。

在本文中,我们将探讨如何通过重新创建城市研究所的数据可视化风格指南,在altair中实现一个风格指南,而不是解释你的可视化中风格指南和一致性的价值。

altair中的主题

下面是文档中的一个简单示例:

**import** altair **as** alt
**from** vega_datasets **import** data*# define the theme by returning the dictionary of configurations*
**def** black_marks():
    **return** {
        'config': {
            'view': {
                'height': 300,
                'width': 400,
            },
            'mark': {
                'color': 'black',
                'fill': 'black'
            }
        }
    }*# register the custom theme under a chosen name*
alt.themes.register('black_marks', black_marks)*# enable the newly registered theme*
alt.themes.enable('black_marks')*# draw the chart*
cars **=** data.cars.url
alt.Chart(cars).mark_point().encode(
    x**=**'Horsepower:Q',
    y**=**'Miles_per_Gallon:Q'
)

heightwidth仍然与默认主题相同,但是我们现在已经包括了colorfill值,这些值将被全局地(除非另有说明)应用于从此刻起直到这个** python会话结束为止生成的任何图表。**

这相当于

alt.Chart(cars).mark_point(color **=** 'black', fill **=** 'black').encode(
    x**=**'Horsepower:Q',
    y**=**'Miles_per_Gallon:Q'
)

在返回的black_marks config字典中,您可以看到我们为mark中的键colorfill指定了值black。这是所有这些规范遵循的格式。例如,如果您想要配置左轴标签的字体大小:

**def** my_theme():
    **return** {
        'config': {
            'view': {
                'height': 300,
                'width': 400,
            },
            'mark': {
                'color': 'black',
                'fill': '#000000',
            },
            'axisLeft': {
                'labelFontSize': 30,
            },
        }
    }*# register the custom theme under a chosen name*
alt.themes.register('my_theme', my_theme)*# enable the newly registered theme*
alt.themes.enable('my_theme')

(旁注:可以从 Vega-Lite 的文档 中获取这些 规格 (即 *'axisLeft'*

如果你或你的公司有你必须遵循的风格指南,这可能特别有用。即使你没有风格指南,你也可以通过在你的个人主题上保存你的配置而不是在你的 viz 代码中开始建立一个风格指南(从长远来看这是值得的!).

Vega 已经在 GitHub 上有了一些主题。

fivethirtyeight

excel

ggplot2

vox

所以让我们建立一个 简化的 城市学院的altair主题。

城市学院风格的这个 简化版 的所有信息都可以从他们的 GitHub 页面的这个图中找到。

这个主题的基本结构如下:

***def** theme_name():**return** {
        "config": { 
            "TOP_LEVEL_OBJECT": { *# i.e. "title", "axisX", "legend",*
                "CONFIGURATION": "VALUE",
                "ANOTHER_ONE": "ANOTHER_VALUE",
                "MAYBE_A_SIZE": 14, *# values can be a string, boolean, or number,*
            },
            "ANOTHER_OBJECT": {
                "CONFIGURATION": "VALUE",
            }
        }
    }*

我们将配置顶级对象“标题”、“ axisX ”、“ axisY ”、“范围”和“图例”,以及其他一些一行程序规范。**

现在,我们马上看到 Urban 在所有文本上使用“Lato”字体,我们可以将其保存为一个变量,这样我们就不必重复使用它。

  1. 标题

Urban 的标题大小为 18px,Lato 字体,左对齐,黑色。

***def** urban_theme():
    font **=** "Lato"**return** {
        "config": {
            "title": {
                "fontSize": 18,
                "font": font,
                "anchor": "start", *# equivalent of left-aligned.*
                "fontColor": "#000000"
            }
        }
    }*

此时,你可以注册启用这个主题,并且在这个python会话中你所有的altair图表将会有一个看起来像城市研究所的标题。**

**# register*
alt.themes.register("my_custom_theme", urban_theme)
*# enable*
alt.themes.enable("my_custom_theme")*

旁注

如果您的计算机中没有安装“Lato”字体,您可以在单元格中运行此代码,暂时将它从 Google Fonts 导入到您的浏览器中。

***%%**html
**<**style**>**
@**import** url('https://fonts.googleapis.com/css?family=Lato');
**</**style**>***
  1. axisXaxisY

在市区,X 轴和 Y 轴略有不同。x 轴有一条在altair被称为的线,这条线穿过轴。y 轴没有这个(类似于seabornsns.despine())。y 轴有网格线,X 轴没有。x 轴有刻度,Y 轴没有。但两者都有同样大小的标签和标题。**

***def** urban_theme():
    *# Typography*
    font **=** "Lato"
    *# At Urban it's the same font for all text but it's good to keep them separate in case you want to change one later.*
    labelFont **=** "Lato" 
    sourceFont **=** "Lato" *# Axes*
    axisColor **=** "#000000"
    gridColor **=** "#DEDDDD"**return** {
        "config": {
            "title": {
                "fontSize": 18,
                "font": font,
                "anchor": "start", *# equivalent of left-aligned.*
                "fontColor": "#000000"
            },
            "axisX": {
                "domain": **True**,
                "domainColor": axisColor,
                "domainWidth": 1,
                "grid": **False**,
                "labelFont": labelFont,
                "labelFontSize": 12,
                "labelAngle": 0, 
                "tickColor": axisColor,
                "tickSize": 5, *# default, including it just to show you can change it*
                "titleFont": font,
                "titleFontSize": 12,
                "titlePadding": 10, *# guessing, not specified in styleguide*
                "title": "X Axis Title (units)", 
            },
            "axisY": {
                "domain": **False**,
                "grid": **True**,
                "gridColor": gridColor,
                "gridWidth": 1,
                "labelFont": labelFont,
                "labelFontSize": 12,
                "labelAngle": 0, 
                "ticks": **False**, *# even if you don't have a "domain" you need to turn these off.*
                "titleFont": font,
                "titleFontSize": 12,
                "titlePadding": 10, *# guessing, not specified in styleguide*
                "title": "Y Axis Title (units)", 
                *# titles are by default vertical left of axis so we need to hack this* 
                "titleAngle": 0, *# horizontal*
                "titleY": **-**10, *# move it up*
                "titleX": 18, *# move it to the right so it aligns with the labels* 
            },}
    }*

如果你注册 ed 并且启用* d 这个主题,你会得到一个看起来有点像城市研究所图表的东西。但是让你马上知道你正在看一个城市研究所的数据可视化的是颜色。***

altair中,你有scalesdomainrange。这些是"函数,将数据域中的值(数字、日期、字符串等)转换为编码通道的可视值(像素、颜色、大小)。因此,如果你想添加一个默认的配色方案,你所要做的就是在你的主题中编码顶层对象"range"的值。**

我们将这些值保存为列表main_palettesequential_palette。城市研究所的数据可视化风格指南有很多颜色组合。我们将这两个编码为默认值,但是当涉及到颜色时,您很可能会在移动中修改您的数据可视化。

***def** urban_theme():
    *# Typography*
    font **=** "Lato"
    *# At Urban it's the same font for all text but it's good to keep them separate in case you want to change one later.*
    labelFont **=** "Lato" 
    sourceFont **=** "Lato" *# Axes*
    axisColor **=** "#000000"
    gridColor **=** "#DEDDDD" *# Colors*
    main_palette **=** ["#1696d2", 
                    "#d2d2d2",
                    "#000000", 
                    "#fdbf11", 
                    "#ec008b", 
                    "#55b748", 
                    "#5c5859", 
                    "#db2b27", 
                   ]
    sequential_palette **=** ["#cfe8f3", 
                          "#a2d4ec", 
                          "#73bfe2", 
                          "#46abdb", 
                          "#1696d2", 
                          "#12719e", 
                         ]**return** {
        "config": {
            "title": {
                "fontSize": 18,
                "font": font,
                "anchor": "start", *# equivalent of left-aligned.*
                "fontColor": "#000000"
            },
            "axisX": {
                "domain": **True**,
                "domainColor": axisColor,
                "domainWidth": 1,
                "grid": **False**,
                "labelFont": labelFont,
                "labelFontSize": 12,
                "labelAngle": 0, 
                "tickColor": axisColor,
                "tickSize": 5, *# default, including it just to show you can change it*
                "titleFont": font,
                "titleFontSize": 12,
                "titlePadding": 10, *# guessing, not specified in styleguide*
                "title": "X Axis Title (units)", 
            },
            "axisY": {
                "domain": **False**,
                "grid": **True**,
                "gridColor": gridColor,
                "gridWidth": 1,
                "labelFont": labelFont,
                "labelFontSize": 12,
                "labelAngle": 0, 
                "ticks": **False**, *# even if you don't have a "domain" you need to turn these off.*
                "titleFont": font,
                "titleFontSize": 12,
                "titlePadding": 10, *# guessing, not specified in styleguide*
                "title": "Y Axis Title (units)", 
                *# titles are by default vertical left of axis so we need to hack this* 
                "titleAngle": 0, *# horizontal*
                "titleY": **-**10, *# move it up*
                "titleX": 18, *# move it to the right so it aligns with the labels* 
            },
            "range": {
                "category": main_palette,
                "diverging": sequential_palette,
            }}
    }*

此时,默认情况下,您的主题将具有城市研究所的标题、轴和颜色配置。很酷,但这不是你能做的全部。

让我们添加一个默认的图例配置。这一次我们将稍微偏离样式指南,因为图例的位置取决于手边的图表(在 Vega-lite 中不能有水平图例)。

该代码还包括“视图”和“背景”配置,无需过多解释,简单易懂。还包括“区域”、“线”、“轨迹”、“”、“”等标识的配置。这只是为每个特定的标记设置正确的颜色。**

***def** urban_theme():
    *# Typography*
    font **=** "Lato"
    *# At Urban it's the same font for all text but it's good to keep them separate in case you want to change one later.*
    labelFont **=** "Lato" 
    sourceFont **=** "Lato" *# Axes*
    axisColor **=** "#000000"
    gridColor **=** "#DEDDDD" *# Colors*
    main_palette **=** ["#1696d2", 
                    "#d2d2d2",
                    "#000000", 
                    "#fdbf11", 
                    "#ec008b", 
                    "#55b748", 
                    "#5c5859", 
                    "#db2b27", 
                   ]
    sequential_palette **=** ["#cfe8f3", 
                          "#a2d4ec", 
                          "#73bfe2", 
                          "#46abdb", 
                          "#1696d2", 
                          "#12719e", 
                         ]**return** {
        *# width and height are configured outside the config dict because they are Chart configurations/properties not chart-elements' configurations/properties.*
        "width": 685, *# from the guide*
        "height": 380, *# not in the guide*
        "config": {
            "title": {
                "fontSize": 18,
                "font": font,
                "anchor": "start", *# equivalent of left-aligned.*
                "fontColor": "#000000"
            },
            "axisX": {
                "domain": **True**,
                "domainColor": axisColor,
                "domainWidth": 1,
                "grid": **False**,
                "labelFont": labelFont,
                "labelFontSize": 12,
                "labelAngle": 0, 
                "tickColor": axisColor,
                "tickSize": 5, *# default, including it just to show you can change it*
                "titleFont": font,
                "titleFontSize": 12,
                "titlePadding": 10, *# guessing, not specified in styleguide*
                "title": "X Axis Title (units)", 
            },
            "axisY": {
                "domain": **False**,
                "grid": **True**,
                "gridColor": gridColor,
                "gridWidth": 1,
                "labelFont": labelFont,
                "labelFontSize": 12,
                "labelAngle": 0, 
                "ticks": **False**, *# even if you don't have a "domain" you need to turn these off.*
                "titleFont": font,
                "titleFontSize": 12,
                "titlePadding": 10, *# guessing, not specified in styleguide*
                "title": "Y Axis Title (units)", 
                *# titles are by default vertical left of axis so we need to hack this* 
                "titleAngle": 0, *# horizontal*
                "titleY": **-**10, *# move it up*
                "titleX": 18, *# move it to the right so it aligns with the labels* 
            },
            "range": {
                "category": main_palette,
                "diverging": sequential_palette,
            },
            "legend": {
                "labelFont": labelFont,
                "labelFontSize": 12,
                "symbolType": "square", *# just 'cause*
                "symbolSize": 100, *# default*
                "titleFont": font,
                "titleFontSize": 12,
                "title": "", *# set it to no-title by default*
                "orient": "top-left", *# so it's right next to the y-axis*
                "offset": 0, *# literally right next to the y-axis.*
            },
            "view": {
                "stroke": "transparent", *# altair uses gridlines to box the area where the data is visualized. This takes that off.*
            },
            "background": {
                "color": "#FFFFFF", *# white rather than transparent*
            },
            *### MARKS CONFIGURATIONS ###*
            "area": {
               "fill": markColor,
           },
           "line": {
               "color": markColor,
               "stroke": markColor,
               "strokeWidth": 5,
           },
           "trail": {
               "color": markColor,
               "stroke": markColor,
               "strokeWidth": 0,
               "size": 1,
           },
           "path": {
               "stroke": markColor,
               "strokeWidth": 0.5,
           },
           "point": {
               "filled": **True**,
           },
           "text": {
               "font": sourceFont,
               "color": markColor,
               "fontSize": 11,
               "align": "right",
               "fontWeight": 400,
               "size": 11,
           }, 
           "bar": {
                "size": 40,
                "binSpacing": 1,
                "continuousBandSize": 30,
                "discreteBandSize": 30,
                "fill": markColor,
                "stroke": **False**,
            },}
    }*

边注

我个人将这些主题保存在一个.py脚本中

***import** altair **as** alt
alt.themes.register("my_custom_theme", urban_theme)
alt.themes.enable("my_custom_theme")*

在我的笔记本上。

以下是用该主题创建的一些图表示例。

条形图

面积图(分类)

面积图(顺序)

折线图

有很多方法来配置你的主题,我鼓励你尝试很多不同的东西。你可以把我们刚刚整理好的主题和价值观结合起来。使用不同的字体,字体大小,配色方案。有网格线,没有网格线。给你的斧子贴上非常非常大的标签。做你想做的事情,但是把它保存在某个地方,这样你就可以在它的基础上发展你的个人风格。

虽然你可能想在[altair](http://altair-viz.github.io) 文档中查找更多这方面的信息,但我发现使用 Vega-lite 文档更好。毕竟,altair是 Vega-lite 的 python-wrapper。

所有的图片和代码都可以在这个 GitHub repo 中找到。

有什么意见,问题,建议吗?我在 twitter 、 linkedin 和 Instagram 。

构建用于荟萃分析的轮廓增强漏斗图

原文:https://towardsdatascience.com/constructing-contour-enhanced-funnel-plots-for-meta-analysis-6434cc8e51d0?source=collection_archive---------3-----------------------

元分析被用来综合大量的研究,在指导政策时可以发挥相当大的作用。然而,这种方法的一个很大的局限性是纳入荟萃分析的研究可能会有偏差。

出版偏见是众所周知的偏见来源。例如,研究人员可能会搁置那些没有统计学意义的研究,因为期刊不太可能发表这类结果。研究人员也可能使用有问题的研究实践——也被称为p——黑客——来将一个效应推到统计学意义上。

P-hacking in action

因此,在进行荟萃分析时,需要考虑发表偏倚的风险。

在荟萃分析中,通常使用两种相互关联的方法来评估发表偏倚的风险。首先,通过构建一个漏斗图来可视化发表偏倚的风险,该漏斗图将效应大小的测量值与测量值方差(例如,标准误差)可视化,漏斗以汇总效应大小为中心。

让我们考虑下面图 1 中的两个漏斗图。

Figure 1

图 1A 似乎是相当对称的,具有大致相同数量的研究,无论是总体效果大小(即漏斗中间的垂直线)。方差越大的研究,其总体效应大小的分布越大,越接近图的底部。相比之下,1B 的身材并不匀称。似乎方差较大的研究(即参与者较少的研究)仅落在汇总效应大小的右侧。许多研究人员会得出结论,由于明显的不对称性,数字 1B 是发表偏倚的象征。

Egger 的回归检验通常被用作漏斗图不对称性的客观测量,因为它评估了影响大小和抽样方差之间的关系。统计上显著的影响表明漏斗图不对称。

现在让我们考虑图 2 中的两个漏斗图,每个漏斗图有十个效果大小。

Figure 2

两个图都显示了不对称性,但注意到了观察结果的差异(即效应大小)。图 2A 中的效果尺寸范围从 0.2 到 0.65,而图 2B 中的效果尺寸范围从-0.1 到 0.35。

对于给定的效应大小和方差范围,可以计算这两个变量任意组合的统计显著性。因此,您可以通过构建所谓的轮廓增强漏斗图,在传统漏斗图上可视化一组显著性阈值。

图 3 中的漏斗图是图 2 中的图的复制,除了具有统计显著性的关键区域已经叠加在漏斗上,并且该图现在以零为中心。红色区域显示在 p = .1 和 p = .05 之间的效果,橙色区域显示在 p = .05 和 p = .01 之间的效果。白色区域的效果大于 p = .1,灰色区域的效果小于 p = .01。

Figure 3

对这两个图进行 Egger 的回归测试得到了相同的结果( z = 2.78, p = 0.01),这表明了不对称性。然而,在图 3A 中,有明显的研究模式落入漏斗右侧的统计显著性通道。图 3B 显示了明显的不对称性,但没有一项研究具有统计学意义。

图 3 说明了漏斗图和 Egger 的回归测试只评估了小型研究偏差的风险,因为小型研究往往具有更大的方差。当然,不对称图可能是由于出版偏差,但可能有其他原因可以更好地解释漏斗图不对称。例如,方差较大的较小研究与较差的研究设计有关。

以下是如何为你自己的元分析绘制这些图,使用 r。

在 R 中使用 Metafor 包的轮廓增强漏斗图

metafor R 包提供了一种构建高度可定制的轮廓增强漏斗图的简单方法。

以下 R 脚本将生成一个标准漏斗图和三个不同的轮廓增强漏斗图。

图 4A 是一个标准的漏斗图。图 4B 是轮廓增强漏斗图——注意,垂直参考线现在为零(而不是像图 4A 那样的综合效果大小)。图 4C 增加了一点颜色,图 4D 将轮廓线调整为一条在 p = .05 和 p = .01 之间的单一轮廓。

Figure 4

使用 R 进行分析的众多好处之一是,您可以轻松地将分析脚本与您的手稿共享。如果你是 R 的新手,你应该看看我的逐步教程论文来使用 R 进行你自己的相关性元分析。我还整理了一个配套的视频,如果那更适合你的话。

公开材料通常是为了促进其他科学家重现你的分析。然而,你会很快了解到,开放材料的最大受益者之一是你的未来,因为当你需要重新分析时。

最后的想法

轮廓增强漏斗图提供了大量研究中报告的统计显著性模式的便捷可视化。虽然它们是对传统漏斗图的有益补充,传统漏斗图只能显示小规模研究偏差,但它们不能用于客观评估 p-hacking 的风险,或给定领域中效应规模膨胀的程度。尽管有这些限制,漏斗图仍然可以为荟萃分析提供有价值的补充,因为它们很容易放在一起。

如果你喜欢这篇文章,你可能也会喜欢我共同主持的元科学播客【T2 万物赫兹】。这是我们关于元分析的一集。

咨询——试火

原文:https://towardsdatascience.com/consulting-trial-by-fire-c33a24e65a59?source=collection_archive---------10-----------------------

在咨询环境中评估他们的第一个项目时,应该从哪里开始?当一个人对一切都是陌生的时候。新行业,新职场动态,新编码平台,新城市新状态?新,新,新,新,新!这就是我在 Lenati 开始数据科学职业生涯两个月的地方。

和大多数新项目一样,我带着极大的热情和健康的焦虑进入。在我职业生活之外的生活中发生了这么多事情,我想如果我要成功,每件事都必须精心计划和详细记录。

第 1 课——咨询有一个陡峭的学习曲线

我们都在学习。因此,预期并不总是很明确,客户经常会改变项目的优先级和愿景。随波逐流,并能够随着优先事项的变化而无缝转换是一项需要培养的基本应对技能。

我在能源行业多年来已经习惯了这一点,但将这些经验应用到新的领域总是一个陡峭的学习曲线。

第二课——没有事情会按计划进行

时间表是愿望清单。如果项目一切顺利,这就是我们期望能够在一定时间内完成的事情。那个 6 周的项目?不,它可能会更接近 4 周,一旦你获得访问数据和评估你可以提供什么。

在咨询行业,对意外情况进行解释是一条很难走的路。这在我的第一个咨询项目中表现得非常明显。当你的客户在你的公司之外时,在交付时间和项目截止日期上的灵活性要小得多。

即使事情进展顺利,方向似乎很明确,我也遇到了另一个挑战,我的新行业的命名和指标。当我在能源行业开始职业生涯时,我回忆起那些恐惧的时刻,当时我只听懂十分之一的对话。在几个月的时间里,我流利地说着这种语言。我认为这将是向营销行业的类似转变。

虽然我很快就学会了行话,但要学会营销中常用的指标似乎要慢得多。

第 3 课—指标为王

作为 Lenati 的一名数据科学家,了解哪些指标对营销专业人员来说很重要,这对于我的工作至关重要。能够为业务顾问和项目经理提供适当的图表和指标来支持他们的程序设计,并提供必要的见解,对我来说是一个比我最初预期的更大的挑战。花时间阅读行业标准并保持沟通渠道畅通是关键。我并不总是做对,但是每一次,我都会吸取我所学到的经验来提高效率。

一旦度量标准被建立,程序设计被“锁定”,我就开始对数据进行更深入的分析,为客户流失建模。一个简单的逻辑回归是可取的,但是,我想挖得更深一点。参数优化、模型调整和特征工程,以提供深刻的见解,加深对客户的了解。深入挖掘和了解更多顾客群的感觉棒极了。

然而,我错了,完全把事情复杂化了。

第四课——接吻(保持简单,笨蛋!)

简单的逻辑回归和流线型的工作流程是优先考虑的。我应该从我在石油和天然气行业的日子里学到这一课,因为我们在资源勘探和评估的早期阶段保持模型简单。新行业,同样的策略。最简单的模型通常能提供决策所需的 90%的解决方案。时间最好花在创建一个稳定的管道/基础设施来交付结果,而不是优化模型。

幸运的是,凭借 9 年支持和交付适合目的模型的经验,我学到了许多与开发适合目的模型相关的经验。现在,我满怀信心地将这些经验应用于营销行业。学习市场营销中模型开发的阶段与能源行业并无不同,只是不同的特征和规模。

保持简单是很自然的事情,然而,在让自己沉浸在新的编码语言、数据结构和报告结构中的同时做到这一点是另一件棘手的事情。在你的逻辑过程中尽早并经常保存,这不仅是组织你的想法的关键,也是在你不在的时候让同事复制你的工作的关键。

第五课——永远要存钱(Ctrl+s 所有的东西)!

在拥有可重复代码方面,总是超越自我。特别是在早期,我在文档方法和几个来源(git、实体笔记本、markdown 等)之间徘徊。).留出时间来记录你的个人工作流程和 保存 当最后一分钟请求表格 xyz 时,每一步都是必不可少的。这会让你和你的老板开心。熟能生巧,在休息时间练习,为下一个项目做得更好。

然而,对你的工作的长期可行性保持现实的期望。大量的时间和精力可以投入到一个项目中,只是为了在接下来的 10-20 年里搁置起来。对我来说,接受你的工作的长期价值是一个持续的斗争。

我转向咨询环境的原因之一是能够对关键业务决策产生影响。

第 6 课—工作的长期可行性未知

你将对商业决策产生影响。你的工作影响的持续时间是未知的。随着时间的推移,这可能会改变,但最终,这是客户对客户的决定,以评估我的工作的长期可行性。谢天谢地,这将有助于我一生中做出一个决定。我能在工作中找到目标和价值。

我重视能够看到许多项目,并迅速产生影响,为关键业务决策提供信息。这最终会让我在营销界获得成功。我自己的成功,Lenati 的成功和我客户的成功。每个人都赢了,我继续成长和学习…

在我的职业支点系列中补上或回顾以前的条目:为什么地球科学家成为优秀的数据科学家、填补知识空白、设计求职、解读工作描述、面试困境和为什么我选择咨询/营销。

咨询?为什么咨询?

原文:https://towardsdatascience.com/consulting-why-consulting-b8a22243ff89?source=collection_archive---------7-----------------------

如果你一直在关注这件事,并且查看了我在 LinkedIn 上的简介,你可能已经注意到我在 Lenati 找到了一份咨询工作。

我在 Lenati 进行行为面试时,以及在其他与公司打交道的时候,有人问我,“为什么要咨询?”以及“为什么营销?”我发现第一个问题比第二个更容易表达。

为什么咨询?

对我来说,可以归结为四大需求:

  1. 一家可以迅速培养我的技术技能的公司。
  2. 一个我能以有意义的方式利用我过去的公司经验的地方。
  3. 领导力的成长空间。
  4. 在这个地方,我可以看到自己与同事一起合作,以实现我们个人/专业和公司范围的目标。

咨询公司允许个人通过快速周转(通常是 2 周冲刺到更少的 6 个月以上的约定)为客户接触许多项目,以提供洞察力来回答直接的业务问题。不要把这和员工扩充公司混为一谈,在那里你作为外部顾问被直接安排在客户团队中。他们都有自己的位置,但我希望看到更广泛的客户如何组织他们的业务。

咨询似乎很适合我的下一个角色,但由于有各种各样的选择,很容易迷失在机会的海洋中。

Lenati 希望提高他们的客户洞察力和分析实践,以利用量化方法为营销计划和战略提供信息。我有机会成为公司的第四号数据科学家,负责一个备受瞩目的忠诚度计划设计项目。

Lenati 很好地帮助我实现了我的目标,甚至更多。

  1. 一家可以迅速培养我的技术技能的公司。— 有了快速的项目周转和高效的“休息”时间(技术产品工作的项目休息时间),如果我不快速成长,我就会被淹没。
  2. 一个我能以有意义的方式利用我过去的公司经验的地方。— 我带来的最大资产是我的不确定性和风险评估,用于 ROI 计算和故事板。我习惯了模棱两可和讲故事(地质学家讲非常不确定的话题的伟大故事)。我已经能够成功地将这一点转化为市场前景和我们的客户。
  3. 领导力的成长空间。— 我们的业务在增长,我能够加快并领导内部和外部项目。我已经赢得了一定程度的尊重,并被信任来履行职责。在某种程度上,小规模的实践需要领导力。如果没有个体从业者在必要的时候带头,什么也做不了。
  4. 在这个地方,我可以看到自己与同事一起合作,以实现我们个人/专业和公司范围的目标。总的来说,这是我选择 Lenati 的一个最重要的方面。在面试过程中,我在未来的同事身边感受到了一种在其他任何地方都找不到的舒适感。办公室给了我一种欢迎的氛围,让我想和他们一起工作,一起成长。

对我来说更难说出的答案是:

为什么营销?

作为一个人,我通过每一个可以想到的渠道被推销,并且对每一个推销手段都持怀疑态度。

我相信部分原因是因为我为自己是一个相对理性的人而自豪。营销和销售似乎总是违背我的理性思维。为什么有人会买他们不想要或不需要的东西?那笔交易好得难以置信吗?是什么让我父亲买了所有那些“没用”的电视购物小玩意(他买了不止一个,而是两个(编辑: 三个)flow bees)?

Yes, my Father used this for ~20 years due to clever marketing. I was a skeptic…and almost victim to it as a teen.

然而,在找工作的过程中,我和自己达成了一个协议,那就是听从的每一次推销。从理性的角度看待它们,不要被我先入为主的观念所影响。**

我在和 Lenati 的招聘人员交谈之前做了调查。我查看了他们的客户组合以及他们在过去几年中交付的一些项目。说我印象深刻是很温和的。我被吹走了。

什么是莱纳提?

Lenati 是一家营销和销售战略咨询公司,帮助公司获得、发展和留住客户。我们的客户来找我们是为了建立市场领导地位,无论是通过激进创新还是智能进化。”

Lenati 培养的主要实践是:客户体验、客户获取、客户保持和忠诚度、走向市场战略以及客户洞察和分析。

客户洞察和分析业务的成员除了支持跨业务的大型项目之外,还有机会领导分析项目。我的第一个项目是支持一个重要的高知名度零售忠诚度项目。

如果你问我我最大的三个特点是什么,忠诚会是最重要的。一开始我可能会怀疑,但是我很忠诚,很真实!当我看到 Lenati 在消费者忠诚度领域的多样性和影响力时,我的兴趣大增。

那么, 为什么要营销?在莱纳提,这是双重的:

  1. 客户组合——我可以浏览徽标列表,并为能在这些公司中的任何一家工作而感到自豪。
  2. 项目组合——帮助人们更深入地参与他们已经了解并喜爱的公司,并使他们的整体体验更好。这是我可以真正支持和发展的营销。

到目前为止,在 Lenati 工作期间,我有机会为零售和技术领域的各种公司的许多项目工作。我交付过各种项目,从忠诚度计划设计到 ROI 建模,再到客户之声分析。

我会长期从事咨询工作吗?那确实是悬而未决的。然而,我会为我的简历上有莱纳蒂而骄傲。人们很棒,工作是一流的,它为营销分析提供了令人惊叹的沉浸感。

请继续关注我的下一集,我将概述我从第一个咨询项目中学到的经验。

在我的职业支点系列中补上或回顾以前的条目:为什么地球科学家成为优秀的数据科学家、填补知识空白、构建求职框架、解读工作描述和面试困境。

它已经引起了我的注意,年轻的人和那些不是来自美国的人可能不熟悉令人惊讶的,不可思议的,Flowbee。关于演示,请看这个古老的 YouTube 视频。对于发人深省的评论,请访问亚马逊网站。

Glowing reviews are justified, my father’s Flowbee haircut was pretty great. (photo circa mid-90's).

基于内容的 NYT 文章推荐系统

原文:https://towardsdatascience.com/content-based-recommender-for-nyt-articles-5a54f57dd531?source=collection_archive---------16-----------------------

数据产品。数据科学。自然语言处理

介绍

我们将为纽约时报的文章创建一个基于内容的推荐器。这个推荐器是一个非常简单的数据产品的例子。

我们将根据用户当前阅读的文章推荐他们应该阅读的新文章。我们将通过基于那篇文章的文本数据推荐类似的文章来做到这一点。

查看我的 Github 个人资料上的代码。

[## 数据广播 03/数据广播

数据科学投资组合。通过在 GitHub 上创建一个帐户,为 databast 03/databast 开发做出贡献。

github.com](https://github.com/DataBeast03/DataBeast/blob/master/NYT_Recommender/Content_Based_Recommendations.ipynb)

检查数据

以下是我们数据集中第一篇 NYT 文章的摘录。显然,我们正在处理文本数据。

'TOKYO —  State-backed Japan Bank for International Cooperation [JBIC.UL] will lend about 4 billion yen ($39 million) to Russia\'s Sberbank, which is subject to Western sanctions, in the hope of advancing talks on a territorial dispute, the Nikkei business daily said on Saturday, [...]"

所以我们必须回答的第一个问题是,我们应该如何对它进行矢量化?我们如何着手设计新的特征,如词类、N-grams、情感分数或命名实体?!

显然 NLP 隧道很深,我们可以花几天时间来试验不同的选项。但是所有好的科学都是从尝试最简单可行的解决方案开始,然后迭代到更复杂的方案。

在本文中,我们将实现一个简单可行的解决方案。

分割您的数据

首先,我们需要确定数据集中哪些特征是我们感兴趣的,对它们进行洗牌,然后将数据分成训练集和测试集——所有这些都是标准的数据预处理。

# move articles to an array
articles = df.body.values# move article section names to an array
sections = df.section_name.values# move article web_urls to an array
web_url = df.web_url.values# shuffle these three arrays 
articles, sections, web_ur = shuffle(articles, sections, web_url, random_state=4)# split the shuffled articles into two arrays
n = 10# one will have all but the last 10 articles -- think of this as your training set/corpus 
X_train = articles[:-n]
X_train_urls = web_url[:-n]
X_train_sections = sections[:-n]# the other will have those last 10 articles -- think of this as your test set/corpus 
X_test = articles[-n:]
X_test_urls = web_url[-n:]
X_test_sections = sections[-n:]

文本矢量器

我们可以从几个不同的文本矢量器中进行选择,比如单词袋(BoW)Tf-IdfWord2Vec 等等。

以下是我们应该选择 Tf-Idf 的一个原因:

与 BoW 不同,Tf-Idf 不仅通过文本频率,还通过逆文档频率来识别单词的重要性。

因此,举例来说,如果像“奥巴马”这样的词在一篇文章中只出现几次(不像停用词“a”或“the ”,它们不传达太多信息),但在几篇不同的文章中出现,那么它将被给予较高的权重。

这是有道理的,因为“奥巴马”不是一个停用词,也不是在没有充分理由的情况下被提及(即,它与文章的主题高度相关)。

相似性度量

当选择相似性度量时,我们有几个不同的选项,例如 JacardCosine 来命名一对。

Jacard 通过比较两个不同的集合并选择重叠的元素来工作。考虑到我们已经选择使用 Tf-Idf 作为矢量器,Jacard 相似性作为选项没有意义;如果我们选择 BoWs 矢量化,使用 Jacard 可能更有意义。

我们应该选择余弦作为我们的相似性度量的原因是因为它作为选择 Tf-Idf 作为我们的矢量器的选项是有意义的。

由于 Tf-Idf 为每篇文章中的每个令牌提供了权重,因此我们可以计算不同文章的令牌的权重之间的点积。

如果文章 A 对于像“Obama”和“White House”这样的标记具有高权重,文章 B 也是如此,那么与文章 B 对于那些相同的标记具有低权重的情况相比,它们的产品将产生更大的相似性得分(为了简单起见,假设所有其他标记权重都被认为是同意的)。

构建推荐器

这部分是神奇发生的地方!

在这里,您将构建一个函数,根据用户当前阅读的文章与语料库中所有其他文章之间的相似性得分(即“训练”数据),输出前 N 篇文章推荐给用户。

def get_top_n_rec_articles(X_train_tfidf, X_train, test_article, X_train_sections, X_train_urls, n = 5):
    '''This function calculates similarity scores between a document and a corpus

       INPUT: vectorized document corpus, 2D array
              text document corpus, 1D array
              user article, 1D array
              article section names, 1D array
              article URLs, 1D array
              number of articles to recommend, int

       OUTPUT: top n recommendations, 1D array
               top n corresponding section names, 1D array
               top n corresponding URLs, 1D array
               similarity scores bewteen user article and entire corpus, 1D array
              '''
    # calculate similarity between the corpus (i.e. the "test" data) and the user's article
    similarity_scores = X_train_tfidf.dot(test_article.toarray().T) # get sorted similarity score indices  
    sorted_indicies = np.argsort(similarity_scores, axis = 0)[::-1] # get sorted similarity scores
    sorted_sim_scores = similarity_scores[sorted_indicies] # get top n most similar documents
    top_n_recs = X_train[sorted_indicies[:n]] # get top n corresponding document section names
    rec_sections = X_train_sections[sorted_indicies[:n]] # get top n corresponding urls
    rec_urls = X_train_urls[sorted_indicies[:n]]

    # return recommendations and corresponding article meta-data
    return top_n_recs, rec_sections, rec_urls, sorted_sim_scores

上述函数按以下顺序工作

  1. 计算用户文章和我们语料库的相似度
  2. 从最高相似度到最低相似度排序分数
  3. 获取前 N 篇最相似的文章
  4. 获取相应的前 N 篇文章部分名称和 URL
  5. 返回前 N 篇文章、版块名称、URL 和分数

验证结果

既然我们已经为用户推荐了阅读的文章(基于他们当前正在阅读的内容),检查一下结果是否有意义。

我们来对比一下用户的文章和对应的版块名与推荐的文章和对应的版块名。

首先让我们来看看相似性得分。

# similarity scores
sorted_sim_scores[:5]
# OUTPUT:
# 0.566
# 0.498
# 0.479
# .
# . 

分数不是很高(注意余弦相似度从 0 到 1)。我们如何改进它们?我们可以选择不同的矢量器,比如 Doc2Vec。我们还可以探索不同的相似性度量。即便如此,我们还是来看看结果吧。

# user's article's section name
X_test_sections[k]
# OUTPUT:
'U.S'# corresponding section names for top n recs 
rec_sections
# OUTPUT:
'World'
'U.S'
'World'
'World'
'U.S.'

好的,所以推荐的版块名称看起来很合适。那就好!

# user's article
X_test[k]'LOS ANGELES —  The White House says President Barack Obama has told the Defense Department that it must ensure service members instructed to repay enlistment bonuses are being treated fairly and expeditiously.\nWhite House spokesman Josh Earnest says the president only recently become aware of Pentagon demands that some soldiers repay their enlistment bonuses after audits revealed overpayments by the California National Guard.  If soldiers refuse, they could face interest charges, wage garnishments and tax liens.\nEarnest says he did not believe the president was prepared to support a blanket waiver of those repayments, but he said "we\'re not going to nickel and dime" service members when they get back from serving the country. He says they should not be held responsible for fraud perpetrated by others.'

好吧,用户的文章是关于支付给国民警卫队成员的超额报酬。

现在让我们来看看前 N 篇推荐文章中的一些摘录:

# article one
'WASHINGTON —  House Speaker Paul Ryan on Tuesday called for the Pentagon to immediately suspend efforts to recover enlistment bonuses paid to thousands of soldiers in California, [...]'# article two
'WASHINGTON —  The Latest on enlistment bonuses the Pentagon is ordering California National Guard soldiers to repay [...]'# article three
'SACRAMENTO, Calif. —  Nearly 10,000 California National Guard soldiers have been ordered to repay huge enlistment bonuses a decade after signing up to serve in Iraq and Afghanistan [...]'# article four
'WASHINGTON —  The Pentagon worked Wednesday to stave off a public relations nightmare, suspending efforts to force California National Guard troops who served in Iraq and Afghanistan to repay their enlistment bonuses [...]'# article five 
'SACRAMENTO, Calif. —  The Latest on enlistment bonuses the Pentagon is ordering California National Guard soldiers to repay [...]'

好啊好啊!看来我们的推荐人确实很成功。所有前五名推荐的文章都与读者当前阅读的内容完全相关。还不错。

关于验证的说明

我们比较推荐文本和节名的特别验证过程表明,我们的推荐器如我们所愿地工作。

手动梳理结果对于我们的目的来说很好,但是我们最终想要做的是创建一个完全自动化的验证过程,以便我们可以在生产中移动我们的推荐器,并让它进行自我验证。

将这个推荐器投入生产超出了本文的范围。本文旨在展示如何在真实数据集上构建这样一个推荐器的原型。

结论

在本文中,我们展示了如何为纽约时报的文章构建一个简单的基于内容的推荐器。我们看到,我们只需要选择一个文本矢量器和相似性度量来构建一个。然而,我们选择哪种矢量器和相似性度量会极大地影响性能。

你可能已经注意到,在我们的推荐器中没有任何实际的机器学习。我们真正拥有的是记忆和相似分数的排序。

附加资源

标准的推荐系统信息实验室文档

Spotify 的深度学习音乐推荐器

情境强盗和强化学习

原文:https://towardsdatascience.com/contextual-bandits-and-reinforcement-learning-6bdfeaece72a?source=collection_archive---------0-----------------------

Source: https://blog.iterable.com/why-you-should-become-a-multi-armed-bandit-1cb6651063f5

如果你为你的网站或应用开发个性化的用户体验,上下文强盗可以帮助你。使用上下文相关工具,您可以选择向用户显示哪些内容、排列广告、优化搜索结果、选择在页面上显示的最佳图像等等。

这类算法有很多名字:上下文土匪,多世界测试,联想土匪,部分反馈学习,土匪反馈学习,边信息土匪,土匪反馈多类分类,联想强化学习,一步强化学习。

研究人员从两个不同的角度研究这个问题。你可以把情境土匪想成是多臂土匪的延伸,或者是强化学习的简化版。

“多臂土匪”中的“土匪”来自赌场中使用的“单臂土匪”机器。想象一下,你在一个有许多独臂强盗机的赌场里。每台机器都有不同的获胜概率。你的目标是最大化总支出。你可以拉有限数量的武器,而且你不知道用哪个土匪才能得到最好的赔付。这个问题涉及到探索/剥削的权衡:你应该在尝试不同的强盗之间取得平衡,以了解每台机器的预期支出,但你也想利用你更了解的最好的强盗。这个问题有许多现实世界的应用,包括网站优化,临床试验,自适应路由和金融投资组合设计。你可以把它想成更聪明的 A/B 测试。

多臂 bandit 算法输出一个动作,但是不使用任何关于环境状态(上下文)的信息。例如,如果你使用一个多臂强盗来选择是否向你网站的用户显示猫的图像或狗的图像,即使你知道用户的偏好,你也会做出同样的随机决定。上下文 bandit 通过根据环境状态做出决定来扩展模型。

Source: https://medium.com/emergent-future/simple-reinforcement-learning-with-tensorflow-part-0-q-learning-with-tables-and-neural-networks-d195264329d0

有了这样的模型,你不仅可以根据以前的观察优化决策,还可以针对每种情况个性化决策。你将向养猫的人展示一只猫的图像,向养狗的人展示一只狗的图像,你可以在一天中的不同时间和一周中的不同日子展示不同的图像。

该算法观察上下文,做出决定,从多个可选择的动作中选择一个动作,并观察该决定的结果。结果决定奖励。目标是最大化平均报酬。

例如,您可以使用上下文 bandit 来选择在网站主页上首先显示的新闻文章,以优化点击率。上下文是关于用户的信息:他们来自哪里,以前访问过的网站页面,设备信息,地理位置等。动作是选择显示什么新闻文章。结果是用户是否点击了链接。奖励是二进制的:没有点击就是 0,有点击就是 1。

如果我们对每个例子的每个可能的行为都有奖励值,我们可以使用任何分类算法,使用上下文数据作为特征,将具有最佳奖励的行为作为标签。挑战在于,我们不知道哪一个动作是最好的,我们只有部分信息:示例中使用的动作的奖励值。你仍然可以使用机器学习模型,但是你必须改变成本函数。一个简单的实现是试图预测回报。

微软发布了一份白皮书,概述了他们实施多世界测试服务的方法和描述。这项服务是由纽约的微软研究小组开发的。此前,该小组的许多研究人员都在雅虎从事该领域的工作。微软多世界测试服务使用 Vowpal Wabbit,这是一个开源库,为上下文土匪实现在线和离线训练算法。离线训练和评估算法在论文中有描述【双稳健策略评估和学习】(Miroslav Dudik,John Langford,李立宏)。

“有两种方法可以解决上下文环境中的离线学习。第一种方法,我们称之为直接方法(DM ),根据给定的数据估计奖励函数,并使用这种估计代替实际奖励来评估一组上下文中的策略值。第二种称为反向倾向得分(IPS) (Horvitz & Thompson,1952),使用重要性加权来校正历史数据中不正确的行为比例。第一种方法需要一个准确的奖励模型,而第二种方法需要一个过去政策的准确模型。一般来说,可能很难准确地对奖励进行建模,所以第一个假设可能过于严格。另一方面,通常可以很好地模拟过去的政策。然而,第二种方法通常存在较大的差异,尤其是当过去的政策与正在评估的政策存在显著差异时。

在本文中,我们建议使用双稳健(DR)估计技术来克服这两种现有方法的问题。双重稳健(或双重保护)估计(Cassel 等人,1976;Robins 等人,1994 年;罗宾斯和罗特尼茨基,1995 年;伦瑟福德&大卫教派,2004 年;康和,2007)是不完全数据估计的统计方法,具有一个重要性质:如果两个估计量(DM 和 IPS)中的任何一个是正确的,则估计是无偏的。因此,这种方法增加了得出可靠推论的机会。"

几个关于背景强盗的视频教程:

ICML 和 KDD 2010 年探索学习教程

SIGIR 2016 搜索、推荐和广告投放的反事实评估和学习教程

在探索中学习

你可以在这里找到很多其他资源的链接。

你可以把强化学习看作是情境强盗的延伸。

Source: https://medium.com/emergent-future/simple-reinforcement-learning-with-tensorflow-part-0-q-learning-with-tables-and-neural-networks-d195264329d0

你仍然有一个代理(策略),它根据环境的状态采取行动,观察回报。不同的是,代理人可以采取多个连续的行动,奖励信息是稀疏的。比如你可以训练一个模特下棋。该模型将使用国际象棋棋盘的状态作为上下文,将决定采取哪些步骤,但它只会在游戏结束时获得奖励:赢、输或平。奖励信息的稀疏性增加了训练模型的难度。你遇到了一个信用分配问题的问题:如何分配信用或指责个人行为。

强化学习算法有很多种。强化学习的延伸之一是深度强化学习。它使用深度神经网络作为系统的一部分。

Arthur Juliani 用 Tensorflow 写了一篇很好的强化学习教程。

对上下文强盗感兴趣的研究人员似乎更专注于创建具有更好统计质量的算法,例如,后悔保证。后悔是使用最优策略时期望的奖励总和与使用从数据中学习的上下文强盗策略时收集的奖励总和之间的期望差。一些算法在后悔上界上有理论保证。

对强化学习感兴趣的研究人员似乎对将机器学习算法应用于新问题更感兴趣:机器人、自动驾驶汽车、库存管理、交易系统。他们通常专注于算法的开发,这些算法可以改善一些问题的艺术状态。

技术方法也不同。微软多世界测试白皮书描述了使用负 IPS(反向倾向分数)作为损失函数的训练算法。最小化损失函数将导致 IPS 估计量的最大化。我找不到任何人在强化学习算法中使用 IPS 估计器。如果你得到具有策略梯度的强化学习算法,并通过将若干步骤减少到一个来将其简化为上下文 bandit,则该模型将非常类似于监督分类模型。对于损失函数,您将使用交叉熵,但乘以奖励值。

比较两种不同类别的算法是很有趣的,这两种算法集中在一个狭窄的上下文强盗区域。我可能会错过一些东西,因为我还没有深入挖掘理论基础。如果你发现这篇文章中有任何遗漏细节的错误,请告诉我。

另请参见:

深度学习新闻的最佳来源

杰夫·迪恩关于大规模深度学习的演讲

用于文本分类的字符级卷积网络

文章原载于http://Pavel . surmenok . com/2017/08/26/contextual-bottom-and-reinforcement-learning/

用 Python 控制 Web

原文:https://towardsdatascience.com/controlling-the-web-with-python-6fceb22c5f08?source=collection_archive---------0-----------------------

简单网络自动化的冒险

问题:提交课堂作业需要浏览错综复杂的网页,以至于好几次我都把作业提交错了地方。此外,虽然这个过程只需要 1-2 分钟,但有时它似乎是一个不可逾越的障碍(比如当我晚上很晚才完成一项任务,而我几乎记不起我的密码)。

解决方案:使用 Python 自动提交完成的作业!理想情况下,我将能够保存一个任务,键入几个键,并在几秒钟内上传我的工作。起初,这听起来好得难以置信,但后来我发现了 selenium ,一个可以与 Python 一起使用来为你导航网络的工具。

Obligatory XKCD

每当我们发现自己在网上用同样的步骤重复单调乏味的动作时,这是一个编写程序来自动化这个过程的好机会。使用 selenium 和 Python,我们只需要编写一次脚本,然后我们可以多次运行它,避免重复单调的任务(在我的例子中,消除了在错误的地方提交任务的机会)!

在这里,我将介绍我开发的自动(并且正确地)提交作业的解决方案。同时,我们将介绍使用 Python 和 selenium 以编程方式控制 web 的基础知识。虽然这个程序确实有效(我每天都在使用它!)这是非常定制的,所以您不能为您的应用程序复制和粘贴代码。尽管如此,这里的通用技术可以应用于无限多的情况。(如果你想看完整的代码,可以在 GitHub 上找到)。

方法

在我们开始自动化 web 的有趣部分之前,我们需要弄清楚我们解决方案的总体结构。没有计划就直接开始编程是在沮丧中浪费很多时间的一个好方法。我想写一个程序,把完成的课程作业提交到画布上正确的位置(我大学的“学习管理系统】)。从基础开始,我需要一种方法来告诉程序要提交的作业的名称和类。我采用了一种简单的方法,创建了一个文件夹来存放每门课的已完成作业及其子文件夹。在子文件夹中,我放置了为特定任务命名的完整文档。该程序可以从文件夹中找出类名,并通过文档标题指定名称。

这里有一个例子,类名是 EECS491,作业是“作业 3——大型图形模型中的推理”。

File structure (left) and Complete Assignment (right)

程序的第一部分是一个遍历文件夹的循环,以找到我们存储在 Python 元组中的赋值和类:

# os for file management
import os# Build tuple of (class, file) to turn in
submission_dir = 'completed_assignments'dir_list = list(os.listdir(submission_dir))for directory in dir_list:
    file_list = list(os.listdir(os.path.join(submission_dir, 
directory)))
    if len(file_list) != 0:
        file_tup = (directory, file_list[0])

print(file_tup)**('EECS491', 'Assignment 3 - Inference in Larger Graphical Models.txt')**

这负责文件管理,程序现在知道要上交的程序和作业。下一步是使用 selenium 导航到正确的网页并上传作业。

使用 Selenium 的 Web 控件

为了开始使用 selenium,我们导入这个库并创建一个 web 驱动程序,这是一个由我们的程序控制的浏览器。在这种情况下,我将使用 Chrome 作为我的浏览器,并将驱动程序发送到我提交作业的 Canvas 网站。

import selenium# Using Chrome to access web
driver = webdriver.Chrome()# Open the website
driver.get('[https://canvas.case.edu'](https://canvas.case.edu'))

当我们打开画布网页时,迎接我们的第一个障碍是一个登录框!要通过这一步,我们需要填写 id 和密码,然后单击登录按钮。

把网络驱动想象成一个从未见过网页的人:我们需要准确地告诉它点击哪里,键入什么,按哪个按钮。有许多方法可以告诉我们的 web 驱动程序要查找什么元素,所有这些方法都使用了选择器。一个选择器是网页上一个元素的唯一标识符。为了找到特定元素的选择器,比如上面的 CWRU ID 框,我们需要检查网页。在 Chrome 中,这是通过按“ctrl + shift + i”或右键单击任何元素并选择“Inspect”来完成的。这带来了 Chrome 开发者工具,这是一个非常有用的应用程序,可以显示任何网页下面的 HTML。

为了找到“CWRU ID”框的选择器,我在框中单击鼠标右键,点击“Inspect ”,并在开发工具中看到以下内容。突出显示的行对应于 id box 元素(这一行称为 HTML 标记)。

HTML in Chrome developer tools for the webpage

这个 HTML 可能看起来让人不知所措,但是我们可以忽略大部分信息,只关注id = "username"name="username"部分。(这些被称为 HTML 标签的属性)。

要用我们的 web 驱动程序选择 id 框,我们可以使用在开发工具中找到的idname属性。selenium 中的 Web 驱动程序有许多不同的方法来选择网页上的元素,并且通常有多种方法来选择完全相同的项目:

# Select the id box
id_box = driver.find_element_by_name('username')# Equivalent Outcome! 
id_box = driver.find_element_by_id('username')

我们的程序现在可以访问id_box,我们可以用各种方式与它交互,比如键入按键,或者点击(如果我们选择了一个按钮)。

# Send id information
id_box.send_keys('my_username')

我们对密码框和登录按钮执行相同的过程,根据我们在 Chrome 开发者工具中看到的内容来选择它们。然后,我们向元素发送信息,或者根据需要点击它们。

# Find password box
pass_box = driver.find_element_by_name('password')# Send password
pass_box.send_keys('my_password')# Find login button
login_button = driver.find_element_by_name('submit')# Click login
login_button.click()

登录后,我们会看到这个有点吓人的仪表板:

我们再次需要通过准确指定点击的元素和输入的信息来引导程序浏览网页。在这种情况下,我告诉程序从左边的菜单中选择课程,然后选择与我需要上交的作业相对应的课程:

# Find and click on list of courses
courses_button = driver.find_element_by_id('global_nav_courses_link')courses_button.click()# Get the name of the folder
folder = file_tup[0]

# Class to select depends on folder
if folder == 'EECS491':
    class_select = driver.find_element_by_link_text('Artificial Intelligence: Probabilistic Graphical Models (100/10039)')elif folder == 'EECS531':
    class_select = driver.find_element_by_link_text('Computer Vision (100/10040)')# Click on the specific class
class_select.click()

程序使用我们在第一步中存储的文件夹的名称找到正确的类。在这种情况下,我使用选择方法find_element_by_link_text来查找特定的类。元素的“链接文本”只是我们可以通过检查页面找到的另一个选择器。:

Inspecting the page to find the selector for a specific class

这个工作流程可能看起来有点乏味,但是记住,我们只需要在编写程序的时候做一次!之后,我们可以多次点击 run,程序会为我们导航所有这些页面。

我们使用相同的“检查页面—选择元素—与元素交互”过程来通过几个屏幕。最后,我们到达作业提交页面:

在这一点上,我可以看到终点线,但最初这个屏幕困扰着我。我可以很容易地点击“选择文件”框,但是我应该如何选择我需要上传的实际文件呢?答案非常简单!我们使用选择器定位Choose File框,并使用send_keys方法将文件的确切路径(在下面的代码中称为file_location)传递给该框:

# Choose File button
choose_file = driver.find_element_by_name('attachments[0][uploaded_data]')# Complete path of the file
file_location = os.path.join(submission_dir, folder, file_name)# Send the file location to the button
choose_file.send_keys(file_location)

就是这样!通过将文件的确切路径发送到按钮,我们可以跳过在文件夹中导航以找到正确文件的整个过程。发送位置后,我们会看到下面的屏幕,显示我们的文件已经上传,可以提交了。

现在,我们选择“提交作业”按钮,点击,我们的作业就上交了!

# Locate submit button and click
submit_assignment = driver.find_element_by_id('submit_file_button')
submit_assignent.click()

清理

文件管理始终是一个关键步骤,我想确保我不会重新提交或丢失旧的任务。我认为最好的解决方案是在任何时候都将提交的单个文件存储在completed_assignments文件夹中,并在文件提交后将其移动到submitted_assignments文件夹中。最后一段代码使用 os 模块,通过将已完成的赋值重命名为所需的位置来移动它:

# Location of files after submission
submitted_file_location = os.path.join(submitted_dir, submitted_file_name)# Rename essentially copies and pastes files
os.rename(file_location, submitted_file_location)

所有的后续代码都封装在一个脚本中,我可以从命令行运行这个脚本。为了减少出错的机会,我一次只提交一个作业,这没什么大不了的,因为运行这个程序只需要 5 秒钟。

下面是我启动程序时的样子:

这个程序让我有机会在上传之前确认这是正确的作业。程序完成后,我得到以下输出:

当程序运行时,我可以看到 Python 为我工作:

结论

使用 Python 实现 web 自动化的技术对于许多任务都非常有用,无论是一般任务还是我的数据科学领域。例如,我们可以使用 selenium 每天自动下载新的数据文件(假设网站没有 API )。虽然最初编写脚本看起来工作量很大,但好处在于我们可以让计算机以完全相同的方式多次重复这个序列。这个程序将永远不会失去焦点而偏离到 Twitter 上。它将忠实地执行完全一致的一系列步骤(这在网站改变之前非常有效)。

我应该指出,在自动化关键任务之前,您确实需要小心谨慎。这个例子风险相对较低,因为我可以随时返回并重新提交作业,而且我通常会仔细检查程序的工作。网站会改变,如果你不相应地改变程序,你可能会得到一个脚本,它做的事情和你最初想做的完全不同!

就回报而言,这个程序为我每次作业节省了大约 30 秒,而我花了 2 个小时来写。所以,如果我用它来交 240 份作业,那么我就能按时完成了!然而,这个项目的回报是为一个问题设计一个很酷的解决方案,并在这个过程中学到很多东西。虽然我的时间本可以更有效地花在作业上,而不是解决如何自动上交作业,但我非常享受这个挑战。很少有事情像解决问题一样令人满意,而 Python 被证明是一个非常好的工具。

一如既往,我欢迎反馈和建设性的批评。可以通过推特 @koehrsen_will 联系到我。

近似梯度下梯度下降的收敛性

原文:https://towardsdatascience.com/convergence-of-gradient-descent-under-approximate-gradients-aaca37faff3f?source=collection_archive---------14-----------------------

探索具有有界梯度误差的梯度下降

优化是一个有很多用途的迷人领域,尤其是现在有了机器学习(ML)。正如那些参与 ML 的人所知道的,梯度下降变量已经成为用于各种训练模型的一些最常见的优化技术。对于非常大的数据集,随机梯度下降特别有用,但需要更多的迭代才能收敛。随机梯度下降由于形成不准确的梯度估计而付出了这种收敛代价,这种不准确的梯度估计导致模型参数在试图找到其通向局部最小值的路径时呈现出非最优的轨迹。

From: https://www.reddit.com/r/ProgrammerHumor/comments/eez8al/gradient_descent/

当然,随机梯度下降并不是计算梯度时出现一些不小误差的唯一地方。特别是,有人可以使用有限差分近似来近似梯度,特别是如果他们有一些黑盒量,在这些黑盒量中很难或不可能找到封闭的梯度表达式,并且仍然在他们的梯度结果中得到不小的误差量。所以我们可能会问自己一个问题:

在具有有界误差的近似梯度的梯度下降下,收敛误差如何受到影响?

这是一个很酷的问题,可以考虑几个潜在的有用领域!对于任何对此感兴趣的人,我将用一个特别好的目标函数来回顾我对这种情况下梯度下降收敛误差的分析和证明。是时候深入数学了!

Meme from: https://giphy.com/gifs/reaction-BmmfETghGOPrW

首先,让我们看看正在讨论的递归梯度下降法。考虑误差有界的最速下降法:

其中 s 为正的常数步长, ε_k 为满足ε_ k≤δ的误差项,对于所有 kf 为正定二次型,定义为****

然后,让我们将参数 q 定义为

并假设 q < 1 。使用上面的梯度下降方法,我将表明对于所有的 k ,我们可以通过下面的等式限制第 k 次迭代和局部最优之间的距离:

其中 x_0 是迭代序列的起点,而 x 是局部最优值。我将通过首先证明两个有用的引理,然后用它们来证明主要结果来证明这个结果。*

引理 1

给定值 0≤c< 1 and that

***we can find a bound for x _ k-x

证明

引理 2

对于某些对称的正定矩阵 A 和正标量 s ,以下不等式成立:

证明

回想一下某正定方阵a = u^tλu其中 U 是特征向量的酉矩阵λ是具有正特征值的对角矩阵。回想一下,对于某个矩阵 B 的矩阵 2 范数,我们有****

这样,我们可以使用以下步骤进行证明:

主要结果的证明

为了得到我们的最终结果,我们将利用一些典型的分析不等式,这将为我们使用引理 1引理 2** 提供一个机会。也就是说,我们可以如下进行最终证明:******

因为我们假设我们选择 s 足够小,使得 q < 1,我们可以使用引理 1 来进一步说明

这个主要结果很有趣,有几个原因。首先,很明显,我们的收敛速度很快受到梯度近似误差而不是初始猜测误差的限制。然而,我们可以通过为步长 s 选择一个足够小的值来克服这个问题,这是随机梯度下降的实践者可能习惯做的事情。

无论如何,现在理论上的东西完成了,我们可以继续看这个不等式相对于实际的数值实验有多好..是时候处理一些数字了!

Meme from: https://makeameme.org/meme/crunch-the-numbers

数字结果

我认为做一个数值实验来看看这个界限和实际的收敛相比如何会很酷。为了做这个实验,将噪声向量 ε 添加到随机正定 Q 的二次型的精确梯度上,使得对于指定的某个 δ > 0,ε≤δ。使用不同的起始随机种子运行多个序列,下图是收敛结果相对于界限的可视化。******

根据上图,看起来边界工作得很好!相对于实际的实验来说,这当然有点保守,但没关系。以下是一些值得注意的观察结果。随着 q → 1 ,不等式中独立于 k 的有界项变得相当大,并且 q^k 项收敛到 0 的速度变慢。这意味着 sQ 的谱在以值 1 为中心的单位球内,并且 s λ_{min}s λ_{max} 的极值在该球的边界附近。 q → 1 也意味着我们正在接近这样一种情况,随着迭代次数接近无穷大,我们将会出现分歧,因此当 q 接近 1 时,事情将会更加有界,收敛将会更加缓慢。

结论

在任何情况下,我们已经看到,如果我们以一种有界的方式来近似我们的梯度(比如在机器学习中使用有限差分或小批量估计),就有可能限制我们的收敛误差,并更清楚地了解在运行时间、长期精度以及潜在的更多方面会发生什么!因此,这是一个非常好的结果,需要记住!

基于 2018 年 3 月 15 日https://Christian jhoward . me文章原载。**

对话数据集和从电影对话中学习角色风格

原文:https://towardsdatascience.com/conversation-datasets-and-learning-character-styles-from-movie-dialogues-8f6bb949ca75?source=collection_archive---------11-----------------------

Photo by Kane Reinholdtsen on Unsplash

随着人工智能继续推进其认知边界,它接受了一项挑战,即我们人类自然地这样做——使用自然语言理解和回应。人类的对话内容极其丰富。传递信息的基础是表面上的单词,通过音调、音高和音量等韵律特征,两个说话者之间的力量差异,以及通过面部表情、眼神交流、肢体语言甚至反应的时间延迟暗示的情绪和态度倾向进行调节。与独白相比,这种丰富的逐层内容使对话特别有趣。

对话的复杂、多模态性质需要多学科的分析方法——语言学家、心理学家和机器学习研究人员走到一起,利用现有的关于对话分析、情感分析和自然语言处理的研究。要做到这一点,可重复的研究是必要的,公开可用的数据是寻求。在学术之外,公开可用的数据是数据科学家和机器学习实践者构建应用机器学习系统的起点。

[1]对适用于构建对话系统的对话数据集的可用性进行了广泛的审查。对于每个数据集,作者都对其大小、来源以及对话是否:

  • 书面/口头/多模态(即视觉模态——面部表情等。)
  • 人对人/人对机器
  • 从真实自然的对话中转录而来。而不是受雇对着机器说话。
  • 来自小说作品:电影还是小说
  • 自发的或受约束的(即,有一个工作任务和目标驱动,如主题辩论或路线规划)

我向任何希望找到对话或对话数据集的人强烈推荐这篇综述,这篇综述的链接在参考文献部分。

在这篇博文的其余部分,让我们关注一个应用程序,在这个应用程序中,一个电影语料库被用来学习角色的说话风格。我认为电影很有趣,因为人物说话的风格通常在场景中是一致的,不像在自然的自发对话中,说话者的风格在一定程度上模仿更强大的说话者,以建立融洽的关系。

[2]已经构建了一个基于电影角色自动生成对话的系统,使用了互联网电影剧本数据库网站上的数百个电影剧本,作者已经发布了语料库数据(https://nlds.soe.ucsc.edu/software)。作者使用外部工具从转录本中提取独特的特征。我选择了一些可能感兴趣的工具:

SentiWordNet(http://sentiwordnet.isti.cnr.it/):提取正负极性

语言学查询字数统计(LIWC)3:一个非常受研究人员欢迎的特征提取工具。在这里,作者从工具中提取了 LIWC 类别,如愤怒词。

被动句比来自(【https://code.google.com/archive/p/narorumo/】的)

提取这些特征后,这些特征被输入到“人物”架构中,如[4]所述。实现的细节很复杂,超出了本文的范围,包括选择语法、聚合句子、插入语用标记和选择词汇结构的多个模块。

我们最后用一个例子来说明性格风格的不同。作者提供了一个表格,用于生成的对话之间的平行比较——可以清楚地看出字符风格的差异。

Utterances generated using Film Character Models. Table from [2].

附录

最后,一些简短的剪辑和引用来获得一些关于人物说话风格的背景知识。

安妮·霍尔:艾尔维和安妮

奇兵—印第引自 思想目录

财富和荣耀,孩子。财富和荣耀。”“我想是时候问问你自己了;你信仰什么?”“……印第安纳·琼斯。我一直知道有一天你会从我的门前走回来。我从不怀疑这一点。一些事情使它不可避免。”"考古学教授,神秘学专家,怎么说呢……稀有古物的获取者。"“把神像扔给我;我把鞭子扔给你!”

低俗小说——文森特引用自 电影

“你不要用别人的车辆。这是违反规则的。”所以你要出去,喝你的酒,说“晚安,我度过了一个非常愉快的夜晚”,“回家,手淫”。这就是你要做的。“哦,伙计,我刚刚打中了马文的脸。“冷静点,伙计,我告诉过你这是个意外。我们可能撞到了什么东西。”“为什么你不告诉我们有人在浴室里?忘了吗?你忘了说有人在浴室里拿着该死的手罐?!”

参考文献

[1] I.V. Serban,R. Lowe,P. Henderson,L. Charlin,J. Pineau,关于构建数据驱动的对话系统的可用语料库的调查,(2015)。https://arxiv.org/pdf/1512.05742.pdf(2018 年 2 月 22 日访问)。

[2] M.A. Walker,G.I. Lin,J.E. Sawyer,用于学习和刻画人物风格的电影对话注释语料库,载于:第八届语言资源与评估国际会议论文集(12),2012 年:第 1373–1378 页。http://lrec . elra . info/proceedings/lrec 2012/pdf/1114 _ paper . pdf(2017 年 12 月 11 日访问)。

[3] J.W. Pennebaker,R.J. Booth,M.E. Francis,操作员手册:语言查询和字数统计— LIWC2007,(2007)1–11。doi:10.4018/978–1–60960–741–8 . ch 012

[4] F. Mairesse,M.A. Walker,《走向基于个性的用户适应:心理通知的文体语言生成、用户建模和用户适应的交互》。20 (2010) 227–278.doi:10.1007/s 11257–010–9076–2。

发表于2018 年 2 月 25 日 2018 年 2 月 25 日

原载于 2018 年 2 月 25 日joshuakyh.wordpress.com

对话式人工智能:模仿机器

原文:https://towardsdatascience.com/conversational-ai-the-imitation-machine-764ddf6dfeec?source=collection_archive---------27-----------------------

1。简介

1.1。背景

对话式人工智能的发展已经进行了 60 多年,很大程度上是由自然语言处理(NLP)领域的研究推动的。在 20 世纪 80 年代,从手写规则到统计方法的转变使 NLP 在处理真实数据时更加有效和灵活(Nadkarni,P.M. et al. 2011,p. 545)。从那以后,这种趋势越来越受欢迎,特别是受到深度学习技术的广泛应用的推动。近年来,自然语言处理在分类、匹配、翻译和结构化预测方面取得了显著成功(李,H. 2017,第 2 页),这些任务通过统计模型更容易完成。然而,自然主义的多回合对话仍然具有挑战性,一些人认为,在我们开发出能够“理解自然语言”的人工通用智能之前,这一问题仍将无法解决(Bailey,K. 2017)。

1.2。目标

为了研究设计对话式人工智能的有效系统架构,本摘要仔细考虑了自动生成理论和对话理论中描述的方法。基于这些理论和其他多学科研究的交叉,它认为会话构建需要对世界的系统表征,尤其是那些基于情境理解的表征。此外,对话式人工智能的充分性不应从其常识性认知能力来衡量,而是从它模仿人类之间交流的程度来衡量。这一论断直接引用了 G.Pask (1976,第 7-8 页)对智力的定义:“智力是一种属性,当且仅当参与者之间的对话表现出理解时,外部观察者才将其归因于参与者之间的对话。”从这个角度来看,如果一个对话式的人工智能在一次成功的交流中表现出情境理解,那么它可以说是展示了智能。

本摘要中展示的方法旨在为处理自然主义多回合对话提供一个新的方向,并扩展当代自然语言处理技术的优势。它建议将对话式人工智能设计为一个自我引用的系统(Maturana,H.R. & Varela,F.J. 1980,第 xiii 页),该系统参与“一个理解、保留和学习的过程”(Pask,G. 1972,第 212 页)。如果利用深度学习方法,这是非常容易实现的。摘要还对 a .图灵(1950 年,第 433 页)提出的“模仿游戏”表示认可,他认为这是对“机器能思考吗?”这一问题的有利替代

1.3。相关研究

提出的论点显示了与其他一些已有理论的联系:认为语言的意义是由它的使用来定义的“语言游戏”概念(维特根斯坦,l .等人,1968 年),把语言视为一种表演媒介的“言语行为”哲学(奥斯汀,J.L .等人,1975 年;Searle,J.R. 1969)、功利性语言学习范式(Gauthier,J. & Mordatch,I. 2016)、互动式语言学习方法(Lidor,N. & Wang,S.I. 2016)。然而,与“语言游戏”和“言语行为”不同,该摘要认为对话式人工智能不应基于纯语言的世界,而与功利主义范式和交互方法的不同之处在于强调知识系统应基于情境语境。

2.机器会思考吗?

2.1。心灵的计算理论

自从数字计算机诞生以来,关于机器认知能力就有过无数次争论(图灵,1950 年;纽厄尔和西蒙,1976 年;塞尔,J.R .,1980)。对人类思维的研究往往是回答眼前问题的理论基础。

在对感知现实的研究中,H. von Foerster (1973,第 38 页)提出了一个观点,即人类的认知只不过是不断完善我们对世界的描述的递归计算。通过计算,他的意思是“反映,思考(放置)一致的事物(com-)”。当 J.A .福多尔综合了心理学和语言学的观点并提出思维语言假说时,一个更正式的心理计算理论出现了(福多尔,J.A. 1976)。它认为认知过程是一个表征系统,由通过语言结构运作的概念符号组成。这证实了系统表征的必要性,它为更复杂的认知活动提供了基础。

2.2。物理符号系统和 GOFAI

大约在同一时间,计算机科学的进步产生了一个类似的假设,该假设定义了一个物理符号系统(Newell,A. & Simon,H. 1976,第 116 页)。它由“一组称为符号的实体组成,这些实体是物理模式,可以作为另一种称为表达式(或符号结构)的实体的组件出现”。纽维尔和西蒙认为,这个符号系统与通用计算机非常相似,它拥有“通用智能行动的必要和充分手段”。J. Haugeland (1985,第 86-123 页)是第一个将这些意识形态引入人工智能领域的人,他提出了“老式人工智能”(GOFAI)这个术语。他承认思想是符号系统,就像语言一样,并进一步承认“推理是计算”。豪格兰德宣称,原则上,在计算机上模拟这种形式的智能是可能的,因为计算机只是一台符号操纵机器。

2.3。GOFAI 的批判

GOFAI 的一个强有力的反对者是 H. Dreyfus (1992,第三十三页),他批评了物理符号系统的“长期规划和具有上下文无关特征的可再识别对象的内部表示”。这提供了一些有价值的见解,因为在后来的几年里,机器学习的成就使“长期规划”的基于程序的方法变得不那么有效,而“上下文无关的特征”创造了一个常识性的知识表示问题,即使在今天也无法解决。另一方面,当考虑到 NLP 的领域时,对“内部代表”的批评可能不值得谨慎。语言基本上由词汇和组合装置组成(Wilson,R.A. & Keil,F.C. 1999,p. xciv)。为了理解每个词条的意义,我们必须研究语义学,这将在下一节详细阐述。

3.情境理解

3.1。情境

Dreyfus 认为常识知识表示的最大问题之一是“知道哪些事实在任何给定的情况下都是相关的”(Dreyfus,H.L. 2007,第 1138 页)。作为对 GOFAI 的一种反应,情境化出现在 20 世纪 80 年代,将智能人类行为描述为“参与的、社会和物质体现的活动,产生于特定(自然)环境的具体细节中”(Wilson,R.A. & Keil,F.C. 1999,第 769 页)。这是一个令人耳目一新的观点,它强调了认知活动的语境依赖性,这种认知活动是不断变化和发展的。为了在 NLP 的方案中定位这种观点,人们必须找到一种既支持代表系统又令人愉快的媒介。

3.2。图式、框架和多重表征

F.C. Bartlett (1967,第 201 页)首先正式提出了“图式”的概念。它是一种在当前情况下发展起来的内在表现形式,“对过去的反应或过去的经历的积极组织”,并作为建议我们未来思想和行为的参考点。这种哲学最终会在明斯基的作品中找到它的道路,他是 GOFAI 的狂热支持者,并激发了他对“框架”结构的建议。与之前出现的物理符号不同,“一个框架是一种表示刻板印象的数据结构”(Minsky,M. 1975),但与物理符号相似,它在一个全局方案中处理常识知识的组织。直到 15 年后,明斯基才意识到有必要“对常识知识的每个片段使用几种不同的表示”(明斯基,M. 2000,第 69 页)。这实际上符合 Bartlett 关于图式的原始观点(1967,第 302 页),即对于任何个人来说,受“特殊感觉区别”或“群体生活导致的社会条件”的影响,可能存在各种图式结构的重叠。图式,或框架,对自然语言处理有着重要的意义,因为它是一种有效的工具来封装任何特殊情况下的语义内容。然后,将这些数据网络分配给一个对话式人工智能来使用就变得似乎可行了。

3.3。自生论和对话论

两个主要的理论可以很好地指导图式的构建,它们是自生成理论和会话理论。H.R. Maturana 和 F.J. Varela (1980 年,第 xiv 页)提出的术语“自生系统”将生命系统定义为“通过其组成部分生产的基本循环形成的单元”。他们认为,在每一个生命系统中,都有一个持续的反馈回路,不断地修改和指定自己的稳态组织。考虑到这一点,他们声称“知识作为一种经验是个人和私人的东西,不能转移,而且人们认为可以转移的客观知识必须总是由听者创造”(Maturana,H.R. & Varela,F.J. 1980,第 5 页)。这与对话理论中的观点产生了共鸣,即对话中的参与者可以被识别为“信仰系统”或“稳定概念的集群”(Pask,G. 1980,第 1004 页)。因此,当设计对话式人工智能时,构建系统化的表示作为人工智能操作的范围是有益的。这些是从不同的情境中建立起来的图式,为对话提供基础。

3.4。可能性

利用深度学习技术,已经可以构建捕捉“细粒度语义和句法规则”的图式,并将单词向量映射到潜在空间中(Pennington,j .等人,2014)。当发现减去包含类似人类偏见的语义时,深度学习的潜力得到了进一步证实(Caliskan,A. et al. 2017)。不难想象对话式人工智能获得情境理解的直接可能性,这提出了一个必要的伦理问题,即什么样的图式模型适合于形成系统的表示。

4.模仿机器

4.1。机器智能再探

a .图灵发明的模仿游戏(1950 年,第 433 页)是一种询问游戏,其中机器扮演被询问的参与者。它并不直接涉及机器是否智能,而是通过“试图提供人类自然给出的答案”来展示其智能行为。这与 G. Pask 的结论(1976,第 8 页)相关,即机器智能是“一个外部观察者引证的属性”。这两种意识形态都将为对话式人工智能的构建提供重要的指导。一个有希望的策略是基于自然的人类对话来塑造系统架构。NLP 任务经常采用的序列到序列模型是不够的,因为对话是一个比刺激-反应模型复杂得多的过程。

4.2。对话结构

G.帕斯克写了很多关于对话理论的文章。他认为对话是不断进化的个体参与者在对话领域达成共识的一种努力。用他自己的话说:“对话是有机封闭(别名自治)系统之间的信息传递。这是一种解决冲突的机制,它也在支持对话的自主个体之间产生区别”(Pask,G. 1980,第 1006 页)。如图 1 所示,对话中的两个参与者,每个人都有不同的目标来指导他们的行动,可能会通过一系列的交流最终达成一致。然而,潘加罗的图表没有捕捉到的是,在达成共识的整个过程中,参与者部分地改变了他们的系统组织,以适应彼此。在自生成理论中,这一系列事件被命名为“结构耦合”:“如果两个结构上可塑的复合材料单元相互作用,从而作为它们各自结构变化路径的选择器,就会发生相互的结构耦合”(Maturana,H.R. 1978,第 42 页)。结果,一个“合意域”将会出现,在那里分离的有机体变得越来越协调,它们的图式获得更广泛的相似性。对话过程中的信息传递表现为“由于概念共享,否则异步参与者在达成协议时变得局部同步”(Pask,G. 1980,第 999 页)。

Figure 1. Conversation Theory (Pangaro, P. 2009)

4.3。团结一致

这种对话模式提出了一个有趣的挑战。对话式人工智能必须既采用类似人类的图式结构,又保持足够的组织差异,以利于对话。G. Pask 很快指出,尽管“团结”是对话的基本要素,但太多的团结会阻碍交流的发生:“如果没有需要解决的冲突,就没有必要进行对话,因为只有二重身”(Pask,G. 1980,第 1006 页)。出于这个原因,一个成功的对话式人工智能应该被设计成以与人类平等的身份参与对话。它将带来一种自主感,同时表达可能与人类参与者不同的独特观点。通过交谈,两个参与者将继续成长和学习,直到他们在给定的环境中发展成为理解的一对。

5.结论

在过去,受限的机器智能和不足的人机交互常常将机器置于自主性较低的角色,如助理、设备或仆人(Dautenhahn,k .等人,2005 年)。由于技术的最新发展,现在可以想象一个更加鼓舞人心的未来,对话式人工智能可以获得广泛的知识,并扩展其预测趋势的准确性。有了这样的进步,当我们设计一种新的机器在人类的栖息地和我们一起工作时,它肯定需要思维的改变。

对话式人工智能的更新系统架构应该使用重叠的系统表示来提供广泛的对话。它会从它的各种图式中提取,在每一个时刻产生适当的反应。它应该保持地方自治,保持“组织上的封闭”和“信息上的开放”(Pask,G. 1980,第 1003 页)。随着对话的继续,它将变得与人类参与者相互依赖,两者将同时经历其系统组织的进展,直到达成共识的领域。对话式人工智能会进化得更适合人类的需求,但它有时会以其独特的洞察力让人类感到惊讶。

这篇摘要中提出的模型已经部分可实现,因为神经网络现在可以利用深度学习技术保留示意性结构。这一领域的未来研究应集中在结构耦合过程,并旨在阐明本地组织结构的可识别状态。实现这一点无疑将有助于模仿机器进一步探索智能。

参考文献

奥斯汀,法学博士等人,1975 年。如何用文字做事第 2 版?/由 J.O. Urmson 和 Marina Sbisà编辑。牛津:克拉伦登。

k .贝利 2017。对话式人工智能和未来之路。[在线] TechCrunch。可在:https://TechCrunch . com/2017/02/25/conversation-ai-and-the-road-ahead/【2017 年 12 月 18 日访问】。

巴特利特,1967 年。《记忆:实验和社会心理学研究》,剑桥:剑桥大学出版社。

Caliskan,a .,Bryson,J.J. & Narayanan,A. 2017。从语料库中自动获得的语义包含类人偏见。科学(纽约,纽约),356(6334),第 183-186 页。

Dautenhahn,s . Woods,c . kaou ri,Walters,M.L .,Koay,K.L. & Werry,2005 年。“什么是机器人伴侣——朋友、助手或管家?,“2005 年 IEEE/RSJ 智能机器人和系统国际会议”,第 1192–1197 页。

德雷福斯,1992 年。计算机还不能做什么:人工理性批判,剑桥,麻省。;伦敦:麻省理工学院出版社。

德雷福斯,H.L. 2007。海德堡人工智能失败的原因以及如何修复它需要使它更海德堡化。人工智能,171(18),第 1137-1160 页。

福多尔大学法学学士,1976 年。思想的语言,哈索克斯:收割机出版社。

Gauthier,j .和 Mordatch,2016 年。情境和目标驱动的语言学习模式。arXiv:1610.03585 [cs。CL】。

Haugeland,J. 1985。人工智能:非常理念,剑桥,麻省。;伦敦:麻省理工学院出版社。

李,H. 2017。自然语言处理的深度学习:优势和挑战,国家科学评论,nwx110。

N. & Wang,S.I. 2016。交互式语言学习,研究博客,斯坦福自然语言处理小组[在线]。可从:https://NLP . Stanford . edu/blog/interactive-language-learning/【2017 年 11 月 22 日访问】

马图拉纳,人力资源,1978。认知。论文最初发表于:Hejl,Peter M. Wolfram K. Kö,和 Gerhard Roth(编辑。)法兰克福汇报:彼得·朗,第 29-49 页。

马图拉纳,H.R .和瓦雷拉,F.J. 1980。自生和认知:生命的实现。

明斯基,男,1975 年。一种表示知识的框架。计算机视觉心理学。),麦格劳-希尔。

明斯基,M. 2000。基于常识的界面。《美国计算机学会通讯》,第 43 卷第 8 期,第 66-73 页。

Nadkarni,P.M .,Ohno-Machado,l .和 Chapman,W.W. 2011。自然语言处理:导论。美国医学信息学协会杂志,18(5),第 544–551 页。

纽厄尔和西蒙,1976 年。作为实证研究的计算机科学:符号与搜索。美国计算机学会通讯,19(3),第 113-126 页。

潘加罗,第 2009 页。“为对话而设计”[PowerPoint 演示]。广告:2009 年旧金山科技奖。可在:【http://www.pangaro.com/abstracts/adtech-2009.html 【2017 年 11 月 18 日进入】

帕斯克,G. 1972。认知和个体的新观点。国际人机研究杂志,4(3),第 211-216 页。

g .帕斯克,1976 年。《人工智能:前言和理论》。在:尼古拉斯·尼葛洛庞帝编辑。软架构机器。麻省理工学院出版社,第 6-31 页。

帕斯克,G. 1980。《团结的限度》,会议记录,应邀在 IFIP、东京和墨尔本世界大会上发表主旨演讲,S. Lavington 编辑。阿姆斯特丹、纽约、牛津:北荷兰酒吧。第 999-1012 页。

Pennington,r .和 Manning,C.D. 2014。GloVe:单词表示的全局向量。自然语言处理的经验方法,第 1532-1543 页。

塞尔,J.R. 1969。《言语行为:语言哲学论文》,伦敦:剑桥大学出版社

塞尔博士,1980 年。思想、大脑和程序。行为与脑科学,3 卷 3 期,第 417-424 页。

图灵,上午 1950。计算机器和智能。心灵,59(236),第 433-460 页。

冯·福斯特,1973 年。构建一个现实。In: Preiser W. F. E .)环境设计研究,第 2 卷。道登,哈钦森&罗斯,宾夕法尼亚州斯特劳斯堡:35-46。

威尔逊,R.A .和凯尔,F.C. 1999 年。麻省理工学院认知科学百科全书,剑桥,麻省。;伦敦:麻省理工学院出版社。

维特根斯坦等人,1968 年。哲学研究第 3 版。牛津:巴兹尔·布莱克威尔。

使用具有交互代码的深度神经网络将 DNA 序列转换为蛋白质序列[带 TF 的手动 Back Prop

原文:https://towardsdatascience.com/converting-dna-sequence-to-protein-sequence-using-deep-neural-network-with-interactive-code-manual-16d41e1304a7?source=collection_archive---------3-----------------------

Image from pixabay

所以今天,我将继续我的机器学习生物信息学之旅。我将尝试完成生物信息学中最基本的任务,即将 DNA 序列转换成蛋白质。同样,这也使任务复杂化了,我们可以建立一个字典来映射这些值,就像的帖子中的Vijini mallawarachchi所做的那样。

另外,请注意,我们将把 DNA /蛋白质序列预处理成载体,如果你不知道如何去做,请见这个帖子。

最后,我将执行扩张反向传播来训练我们的网络。

网络架构和前馈操作

黑色向量→ 编码的 DNA 序列数据
红色向量 →我们的网络中每一层都有 4 层(50 个神经元)
蓝色向量 →软 Max 层进行多类分类
粉粉?箭头 →标准前馈操作

如上所述,前馈操作没有什么特别的,非常简单的全连接神经网络。此外,我们将有 50 个神经元在每一层。

反向传播操作

黄色箭头 →标准梯度流
黑色弯箭头 → 反向传播的扩张连接

如上所述,训练过程也非常简单,只是标准的反向传播与每隔一层的一些扩张连接,以形成密集连接。

培训结果

左图 →一段时间内的平均精度
右图 →一段时间内的成本

因此,对于 1201 epoch,我们将跟踪我们的网络运行情况,如上所述,最终它能够达到 100%的准确性。

将 DNA 序列转换成蛋白质

红框 →模型预测 DNA 序列 CTT/TTA/CTA 属于第 9 类或蛋白质“L”

训练完成后,我们将把每个 DNA 序列交给模型,告诉它预测它属于哪个类别,如上所述,它正在正确地预测每个类别。我们可以通过下面的图表来验证结果。

Genetic Code Chart for DNA (http://www.geneinfinity.org/sp/sp_gencode.html)

互动码

我搬到了谷歌 Colab 寻找交互代码!所以你需要一个谷歌帐户来查看代码,你也不能在谷歌实验室运行只读脚本,所以在你的操场上做一个副本。最后,我永远不会请求允许访问你在 Google Drive 上的文件,仅供参考。编码快乐!

要访问代码,请点击此处。

遗言

将 DNA 转换成蛋白质可以通过使用字典来简单地完成,然而我只是想尝试一下。

如果发现任何错误,请发电子邮件到 jae.duk.seo@gmail.com 给我,如果你想看我所有写作的列表,请在这里查看我的网站。

与此同时,请在我的 twitter 这里关注我,并访问我的网站或我的 Youtube 频道了解更多内容。如果你感兴趣的话,我还做了解耦神经网络的比较。

参考

  1. numpy 中的字符 Int 转换。(2018).要点。检索于 2018 年 3 月 6 日,来自https://gist.github.com/tkf/2276773
  2. 印刷,第 2018 页。python:水平打印,而不是当前的默认打印。Stackoverflow.com。检索于 2018 年 3 月 6 日,来自https://stack overflow . com/questions/8437964/python-printing-horizontally-而非-current-default-printing
  3. 从生物信息学开始——将 DNA 序列转化为蛋白质序列。(2017).走向数据科学。检索于 2018 年 3 月 6 日,来自https://towards data science . com/starting-off-in-bio informatics-turning-DNA-sequences-into-protein-sequences-c 771 DC 20 b 89 f
  4. 范围?,H. (2018)。如何将数据归一化到 0–1 范围?。Stats.stackexchange.com。2018 年 3 月 7 日检索,来自https://stats . stack exchange . com/questions/70801/how-to-normalize-data-to-0-1-range
  5. TF . reset _ default _ graph | tensor flow(2018).张量流。检索于 2018 年 3 月 10 日,来自https://www . tensor flow . org/API _ docs/python/TF/reset _ default _ graph\
  6. 具有交互式代码的机器学习任务的 DNA /蛋白质表示。(2018).走向数据科学。检索于 2018 年 3 月 10 日,来自https://towards data science . com/DNA-protein-representation-for-machine-learning-task-with-interactive-code-6aa 065 b 69227
  7. 数组?,I. (2018)。有没有 Numpy 函数返回数组中某个东西的第一个索引?。Stackoverflow.com。检索于 2018 年 3 月 10 日,来自https://stack overflow . com/questions/432112/is-there-a-numpy-function-to-return-the-first-index-of-something-in-a-a-array
  8. NumPy . arg max—NumPy 1.13 版手册。(2018).Docs.scipy.org。检索于 2018 年 3 月 10 日,来自https://docs . scipy . org/doc/numpy-1 . 13 . 0/reference/generated/numpy . arg max . html
  9. 遗传密码。(2018).Geneinfinity.org。检索于 2018 年 3 月 10 日,来自http://www.geneinfinity.org/sp/sp_gencode.html

将中型文章转换为博客的降价文章

原文:https://towardsdatascience.com/converting-medium-posts-to-markdown-for-your-blog-5d6830408467?source=collection_archive---------7-----------------------

(Source)

如何快速导出中型文章到你的博客

如果你像我一样,开始在 Medium 上写博客,但也想建立自己的网站来展示你的文章,你需要一种方法将文章从 Medium 转移到 Markdown 语言。Markdown 是一种轻量级语言,旨在被转换成 HTML 格式用于网络,有几个工具可以让你从现有的中型文章转为博客的 Markdown。

(如果你还没有博客,那么就按照这个指南使用 Jekyll 和 GitHub 页面在五分钟内建立你自己的网站。)

中等至降价工具

有一个 Chrome 扩展和一个命令行工具用于将你的中型帖子进行降价。不幸的是,我发现 Chrome 扩展不可靠,如果它真的工作,它会产生许多格式错误,需要纠正。

如果你能让 chrome 扩展正常工作,而你不习惯命令行,那么这可能是你最好的选择。然而,我发现命令行工具更适合我的使用,因为它每次都能工作,而且运行后需要对文本进行的重新调整更少。

在命令行上使用中到低

medium-to-markdown 命令行包是用 Javascript 编写的,这意味着你需要node来运行和npm来安装这个包。如果你在 Windows 上,使用这个页面来安装两者(大约需要 3 分钟)。然后,使用npm install medium-to-markdown安装软件包。您需要运行的实际脚本可以在这里找到,如下所示。

Medium to Markdown JS script

要运行,将脚本保存为medium-to-markdown.js,将"<medium post url">更改为您发布的文章的地址,并在命令行键入node medium-to-markdown.js(确保脚本在您的目录中。

这将在您的控制台中输出整篇文章作为 Markdown。要将输出重定向到一个文件,您可以键入node medium-to-markdown.js >> file.md。如果您的博客使用 GitHub Pages + Jekyll,您会希望将存储库的_posts/目录中的md文件保存为date-title.md。比如我的最新贴档是_posts/2018-09-16-Five-Minutes-to-Your-Own-Website.md。一旦文章被转换成正确命名的 Markdown 文件,就可以通过将存储库推送到 GitHub 来发布。

就我个人而言,我发现手动重命名文件的过程很繁琐,所以我编写了一个 Python 脚本,该脚本接受一篇已发布的媒体文章的 url 和日期,用该 url 调用medium-to-markdown.js转换脚本,并用正确的文件名保存结果 markdown。该脚本可以在这里找到,命令行用法如下:

Command line usage of Python script for converting medium to Markdown.

总的来说,运行脚本需要 15 秒,网站更新大约需要 5 分钟!去你的博客看看这篇文章。

Website after pushing changes to GitHub.

常见问题的解决方案

Chrome 扩展和命令行工具都会在 Markdown 中产生一些小问题,您必须解决这些问题。我在散文在线编辑器中完成所有的编辑工作,并喜欢并排调出编辑器和原始媒体文章。(我也经常在我的博客上使用 Chrome 开发工具——右键和inspect——来调整 css 并立即看到变化。)

以下是我注意到的一些问题以及如何解决它们。我将假设你正在为你的博客使用 GitHub Pages 和 Jekyll(按照这个指南开始使用),尽管这些技巧可能适用于其他框架。这些解决方案并不详尽,所以如果你遇到更多的问题,请在评论或 Twitter 上告诉我。

图像标题

正如你在上面看到的,默认的 Markdown 转换在图片下方左对齐显示图片标题。要使标题居中,请将以下内容添加到您的styles.scss文件中(在存储库根目录中):

// Captions for images
img + em {
    font-style: italic;
    font-weight: 600;
    margin-bottom: 20px;
    margin-top: 8px;
    display: block;
    text-align: center;
    font-size: 14px;
    color: black;
}

然后,在 Markdown 本身中,将标题文本从顶部显示的默认文本更改为底部显示的文本。确保标题在单独的行上:

标题现在将在图像下方居中。

Caption centered below image.

如果您不喜欢这种样式,请更改上面列出的 css。

并排图像

要在 markdown 中并排呈现图像,可以使用两列表格。

我发现你必须包含标题,否则表格不能正确显示。有几个其他的选项,但是这个桌子很好用。

Side by Side Images using a table

这里是表格的空白代码,所以只需替换image_linkheader:

Header Left |  Header Right
:--------:|:-------:
![](image_link)  |  ![](image_link)

代码块

如果你写了很多关于编程的东西,那么你希望你的代码看起来很棒!幸运的是,与 on Medium 不同,您可以使用语法突出显示来使您的代码块在您的博客上突出显示。Jekyll 原生支持胭脂语言荧光笔,它有许多风格可供选择(查看它们在这里)。

在文章的降价中,使用反斜线将代码块括起来,并指定高亮显示的语言:

```python
from sklearn.ensemble import RandomForestClassifier 
# Create the model with 100 trees
model = RandomForestClassifier(n_estimators=100,                                                     
                               bootstrap = True,                              
                                max_features = 'sqrt')
# Fit on training data
model.fit(train, train_labels)

默认语法突出显示如下:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/578b2612bc975d189908fbe50e77ae22.png)

Default Python syntax highlighting with Rouge

使用自定义主题,突出显示如下:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/119b5db1d594c700ebf23d7e058760e7.png)

Custom Python syntax highlighting

要设置你自己的代码主题,参考[这篇文章](https://bnhr.xyz/2017/03/25/add-syntax-highlighting-to-your-jekyll-site-with-rouge.html),或者如果你喜欢我的样子,你可以将我的代码样式表`code-highlighting.scss` ( [链接](https://github.com/WillKoehrsen/willkoehrsen.github.io/blob/master/_sass/_code-highlighting.scss))复制粘贴到你的`_sass/`目录中。然后更改`style.scss`中的行`@import “highlights”`到`@import “code-highlighting"`(这应该在底部附近)来导入新的样式。

## GitHub Gists

我的媒体帖子的另一个常见部分是 GitHub gists。为了在你的文章中恰当地展示这些,首先转到原始的中型文章,右击要点,然后选择`inspect frame`。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e15d835b0eea09fcea82e05b2a6771cb.png)

这将显示一页看起来难以理解的代码。然而,你需要做的就是复制`<script>`标签之间的内容,如下所示:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0e34f593a0270cedf9897067a2c1c261.png)

简单地复制这一行并粘贴到如下所示的 Markdown 中,它就会正确地呈现在你的博客上。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/109cd904ee7169c5aeaad3e9187be916.png)

GitHub Gist in Markdown

当然,像你的网站上的任何其他元素一样,你可以用 css 来设计你喜欢的样式!

## 帖子摘录

默认情况下,Jekyll 只会在你的博客主页上显示文章的第一段,并有一个显示“阅读更多”的按钮来访问其余部分。如果你的帖子顶部有一张图片,这将是所有显示的内容。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/cb0b72903cce94701fa05bec5434356a.png)

Default excerpt shown on blog home page

要扩展摘录,在`_config.yaml`中添加以下一行:

Specifies end of excerpt

excerpt_separator:


然后将`<!--more-->` 标签放在 post markdown 中您希望摘录结束的地方。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c99f69231fad48182aa81afa985d7ef2.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/3efdd8dd3b93a3e9d4a05940d77a9c63.png)

Specifying extent of post excerpt in Markdown (left) and how it looks on my website (right).

## 其他注意事项

还有很多选择我没有探索。如果你想在你的博客上做点什么,很有可能有一种方法可以做到。例如,你可以通过创建一个 Disqus 账户并将其添加到你的`_config.yaml`文件中来为你的所有帖子添加评论。如果你有很多文章,并且想要限制出现在一个页面上的数量,你也可以在配置文件中使用`pagination`([指令](https://jekyllrb.com/docs/pagination/))来指定。

建立一个网站是令人兴奋的,因为你可以让它看起来像你想要的那样!虽然对大多数人来说这可能不令人惊讶,但我仍然很感激当我对我的网站进行小的更新时,我可以在网上看到我想要的变化。你无法想象当我终于在我的 about 页面上运行了一个实时代码编辑器时,我是多么的热情!在一天结束的时候,是的,建立一个网站是为了让世界看到你自己,但也是为了对你创造的东西感到自豪。

# 结论

使用命令行工具可以快速地从一篇现有的中型文章转到 [Markdown](https://daringfireball.net/projects/markdown/syntax) 。在转换为 Markdown 后,有几个小错误需要解决,但这也给了你机会来定制你想要的帖子。

如果你仍然有任何问题,看看我的[网站库](http://github.com/willkoehrsen/willkoehrsen.github.io),看看你是否能在那里找到解决方案,或者与我联系。此外,如果你发现你可以用 Jekyll 博客做一些很酷的事情,我很乐意听听。现在出去开始写吧。

一如既往,我欢迎评论和建设性的批评。可以通过推特 [@koehrsen_will](http://twitter.com/@koehrsen_will) 或者通过我的网站 [willk.online](http://willk.online) 找到我

# 将拇指向上/拇指向下转换为完整偏斜度的百分位数

> 原文:<https://towardsdatascience.com/converting-thumbs-up-thumbs-down-to-percentiles-with-skewness-intact-5ee70574a694?source=collection_archive---------22----------------------->

## 用 Python 写亚马逊书评

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/a00911685ab289f4cf2bbe7c5d260a02.png)

拇指向上/拇指向下的度量标准已经变得非常流行。它在 YouTube、亚马逊和其他网站上被用来获取用户和产品信息。该指标简单、有用且有吸引力。

当我为我的女儿搜索蹒跚学步的视频时,我得到了 50 万个赞成和 30 万个反对的视频。这些视频并不特别令人印象深刻。我希望它们能以另一种方式分类。

百分比和投票计数提供了不同的数据显示方式。百分比必须在 0 到 1 之间,而投票通常在 0 到无限的范围内。将选票转换成 0 到 1 之间的百分位数可以将这些系统结合起来。

我以前写过一篇关于 Medium 的文章,强调了转换倾斜数据的标准方法。在那篇文章中,我讨论了流行方法的优点和缺点,所以我不会在这里讨论它们。

相反,我提出了一个原始的分段线性函数,它将偏斜的数据转换成偏斜度不变的百分位数。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b207ec791180385a12e4152b6254ad89.png)

Base e distribution. Graph taken from Corey Wade’s [Helpful Reviews: Exploratory Data Analysis](https://github.com/coreyjwade/Helpful_Reviews/blob/master/Helpful_Reviews_Exploratory_Data_Analysis.ipynb).

一般来说,大多数拇指朝上/拇指朝下的系统都是右偏的。大多数样本只获得几票,而受欢迎的样本可能获得高出 10 个数量级的投票。离群值可能远远超过第 99 百分位,正如[亚马逊书评“有用投票”的情况](https://github.com/coreyjwade/Helpful_Reviews)

## 有益的投票

In[1]:df.Helpful_Votes.describe()Out[1]:count 4.756338e+06
mean 5.625667e+00
std 2.663631e+01
min 0.000000e+00
25% 1.000000e+00
50% 2.000000e+00
75% 4.000000e+00
max 2.331100e+04


数百万个数据点,从 4 到 23311 的跳跃是惊人的。

将该数据转换成有意义的百分位数可以通过如下分段线性函数来完成:

1.  选择多个透视来分隔数据。
2.  使用日志空间划分数据。
3.  对于每个枢纽,x 值是投票数,y 值是百分位数。
4.  用直线连接相邻的(x,y)点。

作为一个简单的例子,考虑 5 个线性枢轴。(我们很快就会到达 logspace。)我需要第 0、25、50、75 和 100 百分位的点。上表给出了以下分布情况:

(0,0)、(1,0.25)、(2,0.5)、(4,0.75)和(2311,1)。

接下来,我用一条直线连接这些点。这听起来可能微不足道,但是除以 0 是有问题的。相同的 x 值将产生 0 个分母。出于计算目的,非常陡的斜率就足够了。

## 直线函数

In[2]:# Define line from two points and a provided column
def two_points_line(a, b, column):

*# Case when y-values are the same*
if b[1]==a[1]:

    *# Slope defaults to 0*
    slope = 0

*# Case when x-values are the same*
elif b[0]==a[0]:

    *# Case when max value is less than 999999999*
    if column.max() < 999999999:

        *# Add 999999999 to max value*
        slope = column.max() + 999999999

    *# All other cases*
    else:

        *# Multiply max value by itself (greater than 999999999)*
        slope = column.max() * column.max()

*# When x-values and y-values are not 0*
else:

    *# Use standard slope formula*
    slope = (b[1] - a[1])/(b[0]-a[0])

*# Equation for y-intercept (solving y=mx+b for b)*
y_int = a[1] - slope * a[0]

*# Return slope and y-intercept*
return slope, y_int

当连接(4,0.75)到(2311,1)时,结果是一条很长的水平线。大多数数据点将聚集在 0.75 附近,导致数据比以前更加倾斜。

日志空间和更多的枢轴帮助极大。

在为日志空间做准备时,让我们回顾一下 linspace。假设我们想把 0 到 100 分成 5 个单位区间。快捷键不是写 0,5,10,…,100,而是 linspace。

## 林空间

In[3]:# Split the numbers between 0 and 100 into 21 evenly spaced points np.linspace(0,100,21)Out[3]:array([ 0., 5., 10., 15., 20., 25., 30., 35., 40., 45., 50.,
55., 60., 65., 70., 75., 80., 85., 90., 95., 100.])


Logspace 返回对数刻度上间隔均匀的数字。每个端点需要 10 的次方。对于结束于 100 的对数间隔段,我们需要 2 作为上限。

## 日志空间

In[4]:# Split the numbers between 0 and 100 into 21 logspace points np.logspace(0, 2, 21)Out[4]:array([ 1. , 1.25892541, 1.58489319, 1.99526231,
2.51188643, 3.16227766, 3.98107171, 5.01187234,
6.30957344, 7.94328235, 10. , 12.58925412,
15.84893192, 19.95262315, 25.11886432, 31.6227766 ,
39.81071706, 50.11872336, 63.09573445, 79.43282347,
100. ])


日志空间最初看起来更适合左侧倾斜的数据。这很容易通过从 101 中减去每个点并颠倒顺序来补救。

## 反向对数空间

In[5]:# Create logspace
left_logspace = np.logspace(0, 2, 21)# Reverse logspace
right_logspace = 101 - left_logspace# Reverse array to ascending order
percent_array = right_logspace[::-1]# Display results
percent_arrayOut[5]:array([ 1. , 21.56717653, 37.90426555, 50.88127664,
61.18928294, 69.3772234 , 75.88113568, 81.04737685,
85.15106808, 88.41074588, 91. , 93.05671765,
94.69042656, 95.98812766, 97.01892829, 97.83772234,
98.48811357, 99.00473769, 99.41510681, 99.74107459,
100. ])


好多了。

对于“有帮助的投票”列,偏斜度远远超过了第 99 个百分点。经过一些反复试验,我选择了 50 个 logspace 段(51 个点)。

## 有用的投票日志空间枢轴

percents= [ 1. 9.79891606 17.82362289 25.1422425 31.81690291
37.90426555 43.45600627 48.51925398 53.13699077 57.34841678
61.18928294 64.69219452 67.88688785 70.8004828 73.45771297
75.88113568 78.09132347 80.10703869 81.94539282 83.62199171
85.15106808 86.54560229 87.81743261 88.97735565 90.03521804
91. 91.87989161 92.68236229 93.41422425 94.08169029
94.69042656 95.24560063 95.7519254 96.21369908 96.63484168
97.01892829 97.36921945 97.68868879 97.98004828 98.2457713
98.48811357 98.70913235 98.91070387 99.09453928 99.26219917
99.41510681 99.55456023 99.68174326 99.79773557 99.9035218
100. ]pivots= [0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0, 3.0, 3.0, 4.0, 4.0, 4.0, 5.0, 6.0, 6.0, 7.0, 8.0, 9.0, 9.0, 10.0, 11.0, 12.0, 14.0, 15.0, 16.0, 18.0, 19.0, 21.0, 23.0, 25.0, 28.0, 30.0, 33.0, 36.0, 40.0, 44.0, 48.0, 54.0, 60.0, 67.0, 77.0, 89.0, 104.0, 126.0, 163.0, 243.0, 23311.0]


枢轴现在展开得很漂亮!如果我使用 linspace,分布应该是[0,0,0,0,0,…,14,17,24,42,23311]。

在分享返回这些结果的函数之前,我想回顾一下总体思路。

竖起大拇指,不竖起大拇指的系统通常会导致数据向右倾斜。该数据可以通过保持偏斜度不变的分段线性函数转换为 0 到 1 之间的值。选择枢纽数,用 logspace 划分数据,取 x 值为票数,y 值为百分位数,用直线连接相邻点。

编码快乐!

## 偏斜线性函数

In[6]:# Import essentials
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# Show all plots in cells
%matplotlib inline# Transform skewed column into piecewise linear function by choosing number of segmentsdef skewed_to_linear(num_segments, column, logspace=True, rightSkew=True, show_graph=True, print_arrays=True, nonNegative=True):

*# GET PIVOTS AND PERCENTILES*
*#********************************************************************

*# Number of pivots equals number of segments plus 1*
num_pivots = num_segments + 1

*# Last index equals number of segments*
last_index = num_segments

*# By default, logspace is True*
if logspace:

    *# Create array of percents between 1 and 100 with log base 10 intervals*
    percents_array = np.logspace(0,2,num_pivots)

    *# By default, rightSkew is True*
    if rightSkew:

        *# Stack array with more values closer to 100* 
        backwards_array = 101 - percents_array

        *# Reverse array to standard ascending order*
        percents_array = backwards_array[::-1]

*# If logspace is False*
else:

    *# Linspace divides 0 and 100 into even segments*
    percents_array = np.linspace(0, 100, num_pivots)

*# Create array of pivots*
pivots_array = []

*# Loop over percents_array*
for i in percents_array:

    *# Obtain value for each percentile*
    pivot = np.percentile(column, i)

    *# Add each value to pivots_array*
    pivots_array.append(pivot)

*# By default, print_arrays is False*
if print_arrays:

    *# Display array of percents*
    print('percents=',(percents_array))

    *# Display corresponding array of pivots*
    print('pivots=',(pivots_array))

*# Convert percents_array from integers to decimals (75 becomes 0.75)*
percents_array = percents_array/100

*# GET SLOPES AND Y-INTERCEPTS*
*#********************************************************************

*# Initialize slope array*
slopes = np.random.rand(num_pivots)

*# Initialize y-intercept array*
yints = np.random.rand(num_pivots)

*# Loop over number of elements of percents_array minus 1*
for n in range(len(percents_array)-1):

    *# Create point a by matching elements in pivots_array with element in percents array*
    a = (pivots_array[n], percents_array[n])

    *# Create point b by matching elements in pivots_array with element in percents array*
    b = (pivots_array[n+1], percents_array[n+1])

    *# Use two_points_line function to find slope, y-intercept of a and b* 
    slopes[n], yints[n] = two_points_line(a, b, column)

    *# By default, show_graph is true*
    if show_graph:

        *# Define points_between_x to create appearance of continuous graph*
        points_between_x = (pivots_array[n+1] - pivots_array[n]) * 1000

        *# Define x for each loop as number of points between the pivots*
        x = np.linspace(pivots_array[n], pivots_array[n+1], points_between_x)

        *# Plot graph using x values with corresponding slop and y-intercepts*
        plt.plot(x, slopes[n] * x + yints[n])

*# By default, print_arrays is False*
if print_arrays:

    *# Show all slopes*
    print('slopes=',(slopes))

    *# Show all y-intercepts*
    print('yints=',(yints))

*# GET Y-VALUES BETWEEN 0 AND 1 FOR EACH COLUMN ELEMENT*
*#********************************************************************

*# Create numpy array of y-values the same length as given column*
y = np.random.rand(len(column))

*# Convert y to a Series*
y = pd.Series(y)

*# Start iteration*
i = 0

*# Loop over each element in column*
for x in column:

    *# Case when column is non-negative and element is 0*
    if (nonNegative) & (x == 0):

        *# Min value should be 0*
        y[i] = 0

        *# Add 1 to i*
        i+=1

    *# Case when element is last member of pivots_array*
    elif x == pivots_array[last_index]:

        *# Last pivot should have value of 100%*
        y[i] = 1.0

        *# Add 1 to i*
        i+=1

    *# All other cases*
    else:

        *# Loop over pivots array*
        for pivot in pivots_array: 

            *# Find first pivot greater than x*
            if x < pivot:

                *# The appropriate index is n-1*
                index = pivots_array.index(pivot)-1

                *# Find slope and yint with same index*
                y[i] = slopes[index] * x + yints[index]

                *# Add 1 to i*
                i+=1

                *# Break out of loop*
                break

*# SHOW GRAPH AND RETURN Y-VALUES*
*#********************************************************************

*# By default, show_graph is True*
if show_graph:

    *# Display graph*
    plt.show()

*# Return y-values*
return y

*[*亚马逊图书评论数据集*](http://jmcauley.ucsd.edu/data/amazon/) *来源于 R. He,J. McAuley,WWW,2016 年出版的《沉浮:用一类协同过滤对时尚趋势的视觉演变进行建模》。**

*![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/87c4fa9742057d76716c4e7e60f58045.png)*

# ConvNets 系列。使用 Mask R-CNN 的实际项目原型

> 原文:<https://towardsdatascience.com/convnets-series-actual-project-prototyping-with-mask-r-cnn-dbcd0b4ab519?source=collection_archive---------5----------------------->

# 介绍

这个 ConvNets 系列从德国交通标志的玩具数据集进展到我被要求解决的一个更实际的现实生活问题:**“有没有可能实现一种深度学习魔法,仅使用照片作为单一输入,来区分*好的*质量的菜肴和*差的*质量的菜肴?”**。简而言之,企业希望这样:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b7e784325d2eb8836cf29aed961e5907.png)

When a business looks at ML through pink glasses, they imagine this

这是一个不适定问题的例子:由于 done 的定义非常模糊(更不用说实现了),所以无法确定解决方案是否存在,以及该解决方案是否唯一且稳定。虽然这篇文章不是关于有效的沟通或项目管理,但有一点是必要的:**你永远不应该参与范围很小的项目。处理这种不确定性的一个行之有效的方法是首先建立一个定义良好的原型,然后再构建剩下的任务。这就是我们采取的策略。**

# 问题定义

在我的原型中,我专注于菜单中的单个项目——煎蛋卷——并构建了一个可扩展的数据管道,输出煎蛋卷的感知“质量”。可以概括为这样:

*   **问题类型:**多类分类,质量的 6 个离散类:`[good, broken_yolk, overroasted, two_eggs, four_eggs, misplaced_pieces]`。
*   **数据集:** 351 张手动采集的各种煎蛋的 DSLR 相机照片。Train/val/test: 139/32/180 洗牌照片。
*   **标签:**给每张照片分配一个主观质量等级。
*   **度量:**分类交叉熵。
*   **必要的领域知识:**一个“好”的煎蛋的定义是三个鸡蛋,蛋黄未破裂,一些培根,没有烧焦的碎片,中间有一片欧芹。此外,它的组成应该是视觉上正确的,例如,不应该有分散的碎片。
*   **done 的定义:**原型开发两周之后测试集上的最佳可能交叉熵。
*   **结果可视化:**测试集低维数据表示的 t-SNE。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/959c1be9b642400a74bae9f9dc4bb25f.png)

Input images as they are captured with camera

主要目标是*使用神经网络分类器获得并组合提取的信号*,并让分类器做出关于测试集中项目的类别概率的 softmax 预测。这样的目标将使这个原型可行,并可用于以后的使用。以下是我们提取并发现有用的信号:

*   关键成分面膜(Mask R-CNN): *信号#1。*
*   按每种成分分组的关键成分计数(基本上是不同成分计数的矩阵):*信号#2。*
*   背景被删除的煎蛋卷板的 RGB 作物。为了简单起见,我决定暂时不将它们添加到模型中。这可能是最明显的信号:只需使用一些奇特的损失函数在这些图像上训练一个 ConvNet 分类器,并在低维嵌入中采用从选定的典范图像到当前图像的 L2 距离。不幸的是,我没有机会测试这个假设,因为我只限于训练集中的 139 个样本。

# 50K 管道概述

我省略了几个重要的阶段,如数据发现和探索性分析、基线解决方案和主动标记(这是我自己对受[多边形-RNN 演示视频](https://www.youtube.com/watch?v=S1UUR4FlJ84)启发的半监督实例注释的花哨名称)以及 Mask R-CNN 的管道(在后面的帖子中会有更多相关内容)。为了拥抱整个管道,这里是它的 50K 英尺视图:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/085e8262433b634e29aea010c3e09d6e.png)

We are mostly interested in the Mask R-CNN and classification stages of the pipeline

对于这篇文章的其余部分,我将重点关注三个阶段:[1]屏蔽 R-CNN 的成分屏蔽推理,[2]基于 Keras 的 ConvNet 分类器,[3]t-SNE 的结果可视化。

# 阶段 1:屏蔽 R-CNN 和屏蔽推理

口罩 R-CNN (MRCNN)最近得到了大量的报道和炒作。从最初的[脸书的论文](https://arxiv.org/abs/1703.06870)开始,到 Kaggle 上的[数据科学碗 2018](https://www.kaggle.com/c/data-science-bowl-2018) ,Mask R-CNN 证明了自己是实例分割(对象感知分割)的强大架构。我使用的基于 Keras 的 [Matterport 的 MRCNN 的实现](https://github.com/matterport/Mask_RCNN)绝对是一种享受。代码结构良好,文档清晰,开箱即用,尽管比我预期的要慢。

MRCNN 中的一段话:

> MRCNN 由两个明确的部分组成:主干网络***和网络头部***从而继承了更快的 R-CNN 架构。基于特征金字塔网络(FPN)或 ResNet101 的卷积骨干网络作为整个图像的特征提取器。在此之上是区域建议网络(RPN ),它对头部的多尺度 RoI(感兴趣区域)进行采样。网络头进行边界框识别和应用于每个 RoI 的掩模预测。在两者之间,RoIAlign 图层将 RPN 提取的多尺度要素与输入精确对齐。******

******![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/40a40bc89105abc80990f04e5c109952.png)******

******MRCNN framework as presented in the original paper******

********对于实际应用,尤其是原型制作,预训练的 ConvNet 至关重要。**在许多现实生活场景中,数据科学家的标注数据集非常有限,甚至没有任何标注。相比之下,*conv net 需要大的标记数据集来收敛*(例如,ImageNet 数据集包含 1.2M 的标记图像)。这就是[迁移学习](http://cs231n.github.io/transfer-learning/)帮助的地方:一种策略是冻结卷积层的权重,只重新训练分类器。为了避免模型过度拟合,Conv 图层权重冻结对于小数据集非常重要。******

******以下是我在一个时期的训练后得到的样本:******

******![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/175237c3b48102c62edffe903f5dfd6f.png)******

******The result of instance segmentation: all key ingredients are detected******

******下一步(*处理分类器*的推断数据,在我的 50K 管道视图中)是裁剪包含盘子的图像部分,并从该裁剪中提取每种成分的 2D 二进制掩码:******

******![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0b25f2c16fce35d560aa2931eee2f6a5.png)******

******Cropped image with the target dish and its key ingredients as binary masks******

******这些二进制掩码然后被组合成一个 8 通道图像(正如我为 MRCNN 定义的 8 个掩码类)——这就是我的*信号#1* :******

******![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e44e93b8bb9f39d3f617eda6ebdf198f.png)******

******Signal #1: 8-channel image composed of binary masks. Colors are just for better visualization******

******对于*信号#2* ,我从 MRCNN 推断中计算了每种成分的数量,并将其打包到每种作物的特征向量中。******

# ******阶段 2:基于 Keras 的 ConvNet 分类器******

******CNN 分类器是使用 Keras 从头开始实现的。我心中的目标是融合几个信号(*信号#1* 和*信号#2* ,以及在未来添加更多数据),并让网络对菜肴的质量等级做出预测。以下架构是实验性的,离理想状态还很远:******

******![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/5999862190c632b8da720cb92208d678.png)******

******关于分类器架构的几点观察和评论:******

*   ********多尺度卷积模块**:最初我为卷积层选择了一个 5x5 的内核,但是这个决定只给了我一个满意的分数。用几种不同核的卷积层`AveragePooling2D`得到了更好的结果:3x3,5x5,7x7,11x11。在每一层之前增加了额外的 1x1 卷积层,以减少维数。这个组件有点类似于 [Inception 模块](https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Szegedy_Going_Deeper_With_2015_CVPR_paper.pdf),尽管我克制自己不去构建一个深度网络。******
*   ********更大的内核:**我使用了更大的内核大小,因为更大的尺度特征可以很容易地从输入图像中提取出来(输入图像本身可以被视为具有 8 个过滤器的激活层——每个成分的二进制掩码基本上是一个过滤器)。******
*   ********信号融合:**我的简单实现只使用了一层非线性来合并两个特征集:处理过的二进制掩码(信号#1)和成分计数(信号#2)。尽管很幼稚,添加信号#2 为分数提供了一个很好的提升(交叉熵从`0.8`提高到`[0.7, 0.72]`)******
*   ********逻辑:**根据张量流,这是应用`tf.nn.softmax_cross_entropy_with_logits`计算批次损失的层。******

# ******阶段 3:t-SNE 的结果可视化******

******对于测试集结果可视化,我使用了 t-SNE,一种用于数据可视化的流形学习技术。t-SNE 使用非常显著的非凸损失函数来最小化低维嵌入数据点和原始高维数据的联合概率之间的 KL 散度。你绝对应该读一下[的原始论文](https://lvdmaaten.github.io/publications/papers/JMLR_2008.pdf),它信息量极大,写得很好。******

******为了可视化测试集分类结果,我推断了测试集图像,提取了分类器的 logits 层,并对该数据集应用了 t-SNE。虽然我应该用不同的困惑值来玩,但结果看起来还是不错的。动画 GIF:******

******![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b5cfbc7c44da88d0ce020093bfc8f23a.png)******

******t-SNE of the test set predictions by the classifier******

******虽然不完美,但这种方法确实有效。尽管如此,仍有许多需要改进的地方:******

*   ********更多数据。** ConvNets 需要大量数据,而我只有 139 个样本用于训练。像数据增强这样的技巧工作得很好(我使用了一个 [D4,或二面角,对称群](https://en.wikipedia.org/wiki/Dihedral_group)增强,产生了 2K+增强图像),但更多的真实数据对于良好的性能至关重要。******
*   ********合适的损失函数。**为了简单起见,我使用了开箱即用的分类交叉熵损失。我会切换到一个更合适的损失函数,一个更好地利用类内方差的函数。一开始一个好的选择可能是三重损失(详见 [FaceNet 论文](https://arxiv.org/pdf/1703.07737.pdf))。******
*   ********更好的整体分类器架构。**当前的分类器基本上是一个原型,其目标是解释输入的二进制掩码并将多个特征集组合成一个推理管道。******
*   ********更好的标注。我在手动图像标记(6 个质量等级)上相当马虎:分类器在十几个测试集图像上胜过了我自己!********

********超越与自省。**在实践中,企业没有数据、没有注释、没有清晰明确的技术任务需要完成的情况非常普遍(我们应该停止否认这一事实)。这是一件好事(否则,他们为什么需要你?):您的工作是拥有工具、足够多的多 GPU 硬件、业务和技术专业知识的组合、预训练的模型以及为业务带来价值所需的一切。******

******从小处着手:可以用乐高积木搭建的工作原型可以提高进一步对话的效率——作为一名数据科学家,你的工作就是向企业推荐这种方法。******

# ConvNets 系列。图像处理:行业工具

> 原文:<https://towardsdatascience.com/convnets-series-image-processing-tools-of-the-trade-36e168836f0c?source=collection_archive---------2----------------------->

# 简介和系列发布

我越是研究 [Udacity 的自动驾驶汽车项目](https://www.udacity.com/drive),卷积神经网络(ConvNets)这个话题对我来说似乎就越重要。原因很简单:在过去的几年里,在计算机视觉和机器人领域,convnets 已经变得无处不在。测绘,图像分类,分割,无人机和自动驾驶汽车的运动规划——许多处理图像(或图像序列)的问题都可以通过 convnets 高效解决。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/bae8fd7feda2a5af20b86870cd3a1cef.png)

MultiNet as an example of a convolutional network (three in one, actually). We are going to use it in one of next posts. Source: [https://arxiv.org/pdf/1612.07695.pdf](https://arxiv.org/pdf/1612.07695.pdf)

我想,你对神经网络的工作原理有很强的直觉。有大量关于这个主题的博客文章、课程和书籍,所以我不想重复:

*   [第六章:深度前馈网络](http://www.deeplearningbook.org/contents/mlp.html)——摘自 I.Goodfellow,Y.Bengio,A.Courville 所著的《深度学习》一书,强烈推荐。
*   [用于视觉识别的 CS231n 卷积神经网络](http://cs231n.github.io/) —费-李非和安德烈·卡帕西教授的著名斯坦福课程。动手和面向工程。卓越的课程材料质量。
*   [深度学习](https://youtu.be/PlhFWT7vAEw)——南多·德·弗雷塔斯的牛津课程。
*   [机器学习简介](https://www.udacity.com/course/intro-to-machine-learning--ud120)—uda city 的免费课程。一个非常温和和广泛的介绍 ML 的初学者。

只有一个建议:为了确保你理解了神经网络的基础知识,[用它们](http://playground.tensorflow.org/)和[构建一个](http://cs231n.github.io/neural-networks-case-study/)!

本系列没有重复基础知识,而是专注于**特定的神经网络架构** : STN(空间转换器网络)、IDSIA(用于交通标志分类的卷积网络)、NVIDIA 用于端到端自动驾驶的网络、用于道路和交通标志检测和分类的 MultiNet。我们开始吧!

**本帖的主题是图像预处理**。Convnets 不能被提供“任何”手边的数据,也不能被视为“自动”提取有用特征的黑匣子。预处理不好或没有预处理会使即使是顶尖的卷积网络也无法收敛或得分很低。因此,强烈建议所有网络进行图像预处理和增强(如果有的话)。在这篇文章中,我们为以后在神经网络中使用和重用打下了基础。

# 工具 1。通过可视化进行数据发现

在这篇和下一篇文章中,我们将使用 gt SRB——一个[德国交通标志识别基准数据集](http://benchmark.ini.rub.de/?section=gtsrb&subsection=dataset)。我们的任务是使用来自 GTSRB 数据集的标记数据来训练交通标志分类器。一般来说,当你有一个数据集时,最好的习惯方法是从中抽取数据样本,并建立一个训练、验证和/或测试集的数据分布直方图。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/3192c6b556b0d28f10215908e58a984b.png)

GTSRB 数据集的基本统计如下:

Number of training examples = 34799Number of validation examples = 4410Number of testing examples = 12630Image data shape = (32, 32, 3)Number of classes = 43


在这个阶段,`matplotlib`是你最好的朋友。虽然你可以单独用`pyplot`构建良好的可视化效果,但是你可以用`matplotlib.gridspec.`将几个图表合并成一个

例如,您可以用这种方式初始化一个包含三个子图的空图表:

gs = gridspec.GridSpec(1, 3, wspace=0.25, hspace=0.1)
fig = plt.figure(figsize=(12,2))
ax1, ax2, ax3 = [plt.subplot(gs[:, i]) for i in range(3)]


Gridspec 是高度可定制的。例如,你可以像我在图表中所做的那样,为每个子情节设置不同的宽度。gridspec 中的轴可以被视为独立的图表,允许您创建非常复杂的绘图。

然后,一个单独的图表可以说明你的数据。这里有三个任务可以通过良好的绘图来解决:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/ce4493d303b20ae832ddccde5d2d8df3.png)

*   **样本图像可视化**:我们马上会看到很多太暗或太亮的图像。光照变化应该(也将会)得到解决:这是一种数据标准化。
*   **检查类别不平衡**:如果类别极度不平衡,您可能需要在批处理生成器中使用[过采样或欠采样方法](http://www.chioka.in/class-imbalance-problem/)。
*   比较训练、验证和测试组上的**数据分布是相似的。这可以通过查看上面的直方图来验证,但是您也可以使用 [Spearman 等级相关性](https://en.wikipedia.org/wiki/Spearman%27s_rank_correlation_coefficient)(通过`scipy`)。**

# 工具 2。scikit-image 的 IPython 并行

为了帮助网络融合,我们需要均衡图像的亮度,并(如 [LeCun 关于交通标志识别的论文](http://yann.lecun.com/exdb/publis/pdf/sermanet-ijcnn-11.pdf)中所建议的)将它们转换成灰度。这可以使用 OpenCV 来完成,但是 Python 的工具箱包含了一个很棒的 [scikit-image](http://scikit-image.org/) ( `skimage`)库,可以通过 pip 轻松安装(与自己编译 OpenCV 形成对比)。关键的见解是使用基于内核的 [CLAHE](https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE) (对比度受限的自适应直方图归一化)方法来归一化图像的直方图:`skimage.exposure.equalize_adapthist`。

`skimage`逐个处理图像并利用单个 cpu 核心,这显然是低效的。为了并行化图像预处理,我们使用了 [IPython 并行](https://ipyparallel.readthedocs.io/en/latest/intro.html) ( `ipyparallel`)包。`ipyparallel`的好处之一是它的简单性:并行运行 CLAHE 只需要几行代码。首先,从您的 shell 启动一个控制器和引擎(假设您已经安装了`ipyparallel`):

$ ipcluster start


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/d1deb4f69b8e92b31931f0791cb60976.png)

One of the benefits of ipyparallel is its client interface which abstracts communication with engines, task scheduling, etc. Running parallel map is just trivial.

我们的并行化方法是最简单的:我们将数据集分成几批,并独立处理每一批。当所有的批次被处理后,我们将它们合并成一个单独的数据集。我的 CLAHE 批处理例程如下:

现在,转换函数已经准备好了,我们可以实现一段代码,将该函数应用于批量数据:

现在我们可以运行它了:

X_train: numpy array of (34799, 32, 32, 3) shape

y_train: a list of (34799,) shapeX_tr, y_tr = preprocess_equalize(X_train, y_train, bins=128)


现在,我们确实利用了系统的所有 cpu 内核(在我的例子中是 32 个),并获得了显著的性能提升。以下是预处理的示例结果:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/77d8a6ef1d01e3ba54ea26fae17700c8.png)

Grayscale and equalize the histogram

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/1d5b9d3e05ede531bf06bacf75e1c957.png)

Histogram equalization for RGB image (I used a different function for rc[:].map)

由于整个预处理管道现在只需要几十秒钟就可以运行,我们可以测试不同的`num_bins`值来了解这个论点:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/86f43f32d93e8e4dcb7f7b077e652975.png)

num_bins: 8, 32, 128, 256, 512

较大的`num_bins`值确实增加了图像的对比度,但也过度突出了背景,增加了图像的噪声。不同的箱也可以用于**对比度增强**,因为我们需要防止对背景特征的过度拟合。

最后,我们可以利用 IPython 的`%store` [魔法](https://ipython.org/ipython-doc/3/config/extensions/storemagic.html)来快速序列化我们的数据集以供进一步使用:

Same images, multiple bins (contrast augmentation)

%store X_tr_8
%store y_tr_8

...

%store X_tr_512
%store y_tr_512


# 工具 3。在线数据扩充

众所周知,增加更多的数据可以提高神经网络的泛化能力。在我们的例子中,我们可以通过使用旋转、翻转和仿射变换来变换我们所拥有的(数据扩充)来构建人工图像。虽然我们可以对整个数据集运行一次这个过程,保存结果并在以后使用它,但更好的方法是动态(在线)构建新图像,以便能够快速修改和迭代数据扩充的参数。

首先,让我们列出我们的转换。我们用`numpy`和`skimage`:

仿射变换增加数据集,同时保持图像标签不变。相反,旋转和翻转可以将一个交通标志转换成另一个。为了处理这个问题,我们可以为每个交通标志和它将被转换成的类别列出一个可用的转换列表:

A part of [the whole transformation table](https://gist.github.com/dnkirill/8aab1c5dbe0471795cdf93729a572d49). Values are class numbers (as in label_class) an image will take after transformation. Empty cell means that transformation is not available for that label.

请注意,上表中的转换名称与我们之前介绍的函数名称相匹配。这样做是为了动态添加更多的转换:

接下来,我们构建一个管道,将`augmentation_table.csv`中列出的所有可用函数应用于所有类:

很好。现在我们有两个数据集扩充例程:

*   `affine_transform`:没有旋转组件的可定制的剪切和缩放变换(不是很一致,因为旋转是仿射变换的一部分)。
*   `flips_rotations_augmentation`:基于随机旋转和增强 _table.csv 的图像变换,可以改变图像标签。

最后一步是将它们合并到一个增强的批处理生成器中:

当我们从这个批处理生成器采样时,我们得到了我们所需要的,对比度和位置数据增强:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/2c19689a6c0432b1132541f4f7c70df9.png)

Generated images via augmented_batch_generator

**注:**强化只在训练时需要。我们做预处理,但不增加验证或测试集。

之后,一个好主意可能是检查该批处理生成器中批处理的数据分布是否仍然合理(要么与原始数据相似,要么是专门针对过采样设计的)。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/6abd6a30e69bacb8b9b6f20b407f028d.png)

Left: data distribution from augmented batch generator. Right: original train. As we see, values differ, but distributions are similar.

# 向神经网络迈进

在完成数据预处理、批处理生成器设置和数据集分析后,我们可以继续训练。我们将使用双卷积网络:STN(空间转换器网络)获取一批图像,专注于交通标志,去除背景,IDSIA 网络从 STN 提供的图像中识别交通标志。下一篇文章将专门讨论这些网络、培训、性能分析和演示。敬请期待,下期帖子再见!

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8f89a578469ee9ffc84a88a12ee2590a.png)

Left: original preprocessed image. Right: STN-transformed image which is fed to IDSIA network for classification.

# ConvNets 系列。空间变压器网络

> 原文:<https://towardsdatascience.com/convnets-series-spatial-transformer-networks-cff47565ae81?source=collection_archive---------0----------------------->

空间转换器是不同模块集合中的另一个乐高积木。它通过应用可学习的仿射变换然后进行插值来消除图像的空间不变性。STN 模块可以放在卷积神经网络(CNN)中,它主要靠自己工作。

这个项目的完整代码可以在 GitHub 上找到:【https://github.com/dnkirill/stn_idsia_convnet 

**为了对 STN 做什么有一些直觉,请看下面我的项目中的两个演示:**

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9ad6e8c4896a26fd942ba59c2166dbe1.png)

Left image: an original input image from traffic signs dataset. Right image: same image transformed by STN. **Spatial transformers learn what part of an image is the most important and scale or rotate the image to focus on this part.**

在本帖中,我们将回顾 STN 模块的内部工作,并将其与**一个对德国交通标志数据集**进行分类的卷积神经网络结合使用。我们将构建它们:STN 和分类器。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/a4ce776ff576a71627f0618fddd70d2d.png)

Another example of STN learning and transformation. This is just the first epoch of training. It is clear how the STN learns the sign representation and focuses on the sign itself.

STN 检测甚至在更困难的情况下也能工作(例如,图像上的两个标志)。不过,最重要的是,STN 确实提高了分类器的准确性(在我的例子中是 IDSIA)。

# 空间变压器网络的绝对最小速成课程

卷积神经网络的问题在于它缺乏对输入变化的鲁棒性。这些变化包括比例、视点变化、背景混乱以及更多的变化。一些不变性可以通过池化操作来实现,池化操作只是对导致信息丢失的特征图进行下采样。

不幸的是,由于标准 2×2 池的小感受野,它仅在更靠近输出层的较深层中提供空间不变性。此外,池化不提供任何旋转或缩放不变性。凯文·扎克卡在他的帖子中对这个事实[提供了一个很好的解释。](https://kevinzakka.github.io/2017/01/18/stn-part2/)

使模型对这些变化稳健的基本且最常用的方法是像我们在[上一篇文章](https://medium.com/towards-data-science/convnets-series-image-processing-tools-of-the-trade-36e168836f0c)中所做的那样扩充数据集:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0a57446bd8f861c05504b544af5e0974.png)

Augmented images. In this post, we will use no augmentations at all.

这种方法没有错,但是我们可能希望开发一种更加智能和自动化的方法来准备图像。这种方法应该有助于分类器得出正确的、更确定的预测。这就是空间转换器网络或 STN 模块发挥作用的地方。**通俗地说,STN 是一种旋转或缩放输入图像或特征地图的机制,目的是聚焦在目标对象上,并消除旋转变化**。STNs 最显著的特征之一是它们的模块化(模块可以被注入到模型的任何部分)以及它们能够在不修改初始模型的情况下用单个反向传播算法进行训练。

下面是另一张图片,让你对 STN 有一个直观的认识:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/cf1ad92752b1aedfe24564c19a3749e1.png)

Cluttered MNIST example from the [original paper](http://papers.nips.cc/paper/5854-spatial-transformer-networks.pdf). Cluttered MNIST (left) original image, target object detected by STN (center), transformed image (right).

如果我们排除学习部分,STN 模块可以归结为以下过程:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/d82a74e6fcd88b2d2b8787e23ab92b6d.png)

Four steps of applying a STN transformation given that the transformation matrix 𝛳 is defined.

在下一节中,我们将更深入地探讨这一过程,并更仔细地观察转换的每一步。

# STN:转型的步骤

**第一步。**定义描述线性变换本身的变换矩阵𝛳(θ)。θ可定义如下:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/88dee309540adf01f6a6850837cbd126.png)

Affine transformation matrix theta.

不同的矩阵对应不同的变换。我们最感兴趣的是这四个:

*   **身份**(输出相同的图像)。这是θ的初始值。在这种情况下,θ矩阵是对角的:
    `theta = np.array([[1.0, 0, 0], [0, 1.0, 0]])`
*   **旋转**(逆时针旋转 45°)。`cos(45º) = 1/sqrt(2) ≈ 0.7`:
    
*   **放大。**
    (放大 2 倍)放大中心:
    `theta = np.array([[0.5, 0, 0], [0, 0.5, 0]])`
*   **缩小。**
    缩小中心(放大 2 倍):
    `theta = np.array([[2.0, 0, 0], [0, 2.0, 0]])`

**第二步。**我们不是将变换直接应用于初始图像,而是生成一个与初始图像大小相同的采样网格`U`。网格是覆盖整个输入图像空间的一组索引`(x_t, y_t)`。它不包含任何颜色信息。这在代码中有更好的解释:

由于张量流的特殊性,`x_t`和`y_t`赋值可能看起来很麻烦。Numpy 翻译做得更好:

`x_t, y_t = np.meshgrid(np.linspace(-1, 1, width), np.linspace(-1, 1, height))`

**第三步。**将线性变换矩阵应用于我们初始化的 meshgrid,以获得新的一组采样点。变换网格的每个点可以定义为θ与坐标`(x_t, y_t)`和偏置项的矩阵向量乘积:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8efba3341d26b2054a636a36035c0fa9.png)

**第四步。**使用初始特征图、转换网格(见步骤 3)和您选择的可微分插值函数(如双线性函数)产生采样输出`V`。插值是必需的,因为我们需要将采样的结果(像素的分数)映射到像素值(整数)。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/19f71a571fa6f4a6b13954683025d762.png)

Sampling and interpolation

**学习问题。**如果我们以某种方式为每个输入图像提供正确的θ,我们可以开始使用我们刚刚描述的过程。事实上,我们希望从数据中推导出θ,我们确实可以做到!**首先,**我们需要确保交通标志分类器的损失可以反向传播到采样器并通过采样器。**其次,**我们定义了关于`U`和`G`(网格)的梯度:这就是插值函数必须可微或次可微的原因。第三,我们计算`x`和`y`相对于θ的偏导数。[如果真的好奇的话,梯度计算见原文。](http://papers.nips.cc/paper/5854-spatial-transformer-networks.pdf)

最后,我们定义了一个**定位神经网络**(回归器),其唯一的任务是学习,然后使用我们通过采样器反向传播的损失为给定图像产生正确的θ。

**这种方法的神奇之处在于,我们得到了一个可微分的带记忆(通过可学习的权重)的自包含模块,它可以放在 CNN 的任何部分。**

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/40bd2e3a11688a12c06726079e7eef1e.png)

See how 𝛳 changes while the STN learns the representation of the main object (a traffic sign) in images .

简而言之,这就是 STN。以下是最初论文中的 STN 方案:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/5e133956e2f454820ef2c1cd66e26f63.png)

Spatial Transformer with the locnet from the [original paper.](http://papers.nips.cc/paper/5854-spatial-transformer-networks.pdf)

如你所见,我们已经涵盖了 STN 的所有构建模块:本地化网络、meshgrid 生成器和采样器。在下一部分中,我们将训练一个张量流分类器,它将 STN 作为其图形的一部分。

# TensorFlow 模型概述

这个模型的代码太长了,无法在这篇文章中一一列出,但是可以在 github 资源库中免费获得:
[https://github.com/dnkirill/stn_idsia_convnet](https://github.com/dnkirill/stn_idsia_convnet)

我更倾向于关注一些重要的代码和模型训练。

**首先**,由于最终任务是对交通标志进行分类,我们需要定义和训练分类器本身。选择很多:从 LeNet 到你能想象到的任何新的顶级网络。在这个项目中,受 [Moodstocks 对 STN](https://github.com/Moodstocks/gtsrb.torch) (在 Torch 中实现)的研究的启发,我使用了一个类似 IDSIA 的网络。

**第二个**,我们定义并训练 STN 模块,该模块将一幅原始图像作为输入,用采样器对其进行变换,并产生另一幅图像(或一批图像)。该图像然后被分类器用作输入。请注意,STN 可以很容易地从 DAG 中分离出来,并由原始图像批处理生成器替换。这种被放置在 CNN 的任何部分的能力是 STN 的主要优点之一。

以下是组合网络的概述:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b743995e2cd607a7efe392559f410f26.png)

Spatial transformer network outputs images to IDSIA traffic signs classifier and trains using a single backprop algorithm.

DAG 的这一部分使用一批原始图像作为输入,通过 STN 模块对它们进行转换,将转换后的数据传输到分类器(IDSIA)并输出 logits:

假设我们有计算 logits 的方法(STN + IDSIA 网络),下一步是计算损失(我们使用交叉熵或 logloss 损失函数作为分类问题的默认选择):

接下来是定义更多的 ops —我们基本上初始化 TensorFlow 的 DAG 的 ops。我们需要选择一个优化器和训练 op,将损失反向传播回输入层:

我以更高的学习率(0.02)开始:这有助于将更强的梯度信息传播到 STN 的定位网络,该定位网络位于整个网络的“前”层,否则可能训练非常慢(由于[消失梯度问题](https://en.wikipedia.org/wiki/Vanishing_gradient_problem))。学习率的初始值越小,网络就越难放大到更小的交通标志。

[Adam optimizer](http://ruder.io/optimizing-gradient-descent/index.html#adam) (结合自适应学习率并保持过去梯度的指数衰减平均值)与学习率的分段常数一起使用。学习率衰减使用`boundaries`(批次数量)切换到较小的学习率。

使用一个非常简单的声明将输出 logits(网络的前向通道)的 DAG 部分添加到图中:

这个简单的声明展开了整个网络,即 STN + IDSIA。下一节将讨论它们。

# IDSIA 分类器网络

我的灵感来自 Moodstocks 的研究和来自 IDSIA 瑞士人工智能集团的[初始论文](http://www.idsia.ch/~juergen/nn2012traffic.pdf),其中他们使用了一组 CNN 来击败这个数据集的最先进的分数。我从系综中提取了单个网络架构的蓝图,并在 TensorFlow 中用我自己的术语定义了它。分类器的配置如下所示:

*   第 1 层:卷积(批量归一化,relu,dropout)。**内核** : 7x7,100 个滤镜。**输入** : 32x32x1(一批 256 个)。**输出** : 32x32x100。
*   第 2 层:最大池化。**输入** : 32x32x100。**输出** : 16x16x100。
*   第三层:卷积(批量归一化,relu,dropout)。**内核** : 5x5,150 滤镜。**输入** : 16x16x100(一批 256)。**输出** : 16x16x150。
*   第 4 层:最大池化。**输入** : 16x16x150。**输出** : 8x8x150。
*   第五层:卷积(批量归一化,relu,dropout)。**内核** : 5x5,250 滤镜。**输入** : 16x16x100(一批 256)。**输出** : 16x16x150。
*   第 6 层:最大池化。**输入** : 8x8x250。**输出** : 4x4x250。
*   第 7 层:多比例要素的附加池。conv 层 1、2 和 3 的内核大小分别为 8、4 和 2。
*   第 8 层:多尺度特征的展平和拼接。**输入**:2x2x 100;2x2x1502x2x250。**输出**:全连通层的特征向量 400+600+1000 = 2000。
*   第 9 层:全连接(批量归一化,relu,dropout)。**输入** : 2000 个特征(一批 256 个)。300 个神经元。
*   第 10 层:Logits 层(批量标准化)。**输入** : 300 个特征。输出:logit(43 类)。

下图可以说明这一点:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e30e2bcb1560760d1dcdb313e1ad13a3.png)

如您所见,每个卷积层的激活随后被合并,并针对全连接层进行扁平化。这是**多尺度特征**的例子,它们**确实**提高了最终得分。

正如我们前面所讨论的,`conv1`的输入来自 STN 的输出。

# TensorFlow 中的空间转换器

TensorFlow models zoo 包含一个 STN 的实现,它将用于我们的模型:[https://github . com/tensor flow/models/tree/master/transformer](https://github.com/tensorflow/models/tree/master/transformer)。

我们的任务是定义和训练本地化网络,为`transformer`提供 theta,并将 STN 模块注入 TensorFlow 的 DAG。`transformer`生成网格并提供变换和插值功能。

本地化网络的配置如下:

**loc net 的卷积层数:**

*   第 1 层:最大池化。**输入** : 32x32x1。**输出** : 16x16x1。
*   第二层:卷积(relu,批量归一化)。**内核** : 5x5,100 个滤镜。**输入** : 16x16x1(一批 256 个)。**输出** : 16x16x100。
*   第 3 层:最大池化。**输入** : 16x16x100。**输出** : 8x8x100。
*   第四层:卷积(批量归一化,relu)。**内核** : 5x5,200 滤镜。**输入** : 8x8x100(一批 256 个)。**输出** : 8x8x200。
*   第 5 层:最大池化。输入:8x8x200。输出:4x4x200。
*   第 6 层:多比例要素的附加池。conv 层 1 和 2 的内核大小分别为 4 和 2。
*   第 7 层:多尺度特征的展平和拼接。**输入**:2x2x 100;2x2x200。**输出**:全连通层的特征向量 400+800 = 1200。

**全连接 LocNet:**

*   第 8 层:全连接(批量归一化,relu,dropout)。**输入** : 1200 个特征(一批 256 个)。100 个神经元。
*   第 9 层:Logits 定义仿射变换的 2×3 矩阵 theta。权重被初始化为零,偏差被初始化为恒等式变换:`[[1.0, 0, 0], [0, 1.0, 0]]`。
*   第 10 层:变压器:网格生成器和采样器。该逻辑在`spatial_transformer.py`中实现。此图层输出与原始图层(32x32x1)尺寸相同的变换图像,并对其应用仿射变换(例如,放大或旋转的图像)。

locnet 的卷积层的结构类似于 IDSIA(尽管 locnet 由 2 个 conv 层而不是 3 个组成,并且我们首先对输入图像进行下采样)。完全连接层的图示更值得注意:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e260020aef36bd449b8ee243b04ae54f.png)

# 培训和结果

带有 STN 模块的 CNN 的问题是需要监控两个网络的过度匹配。这使得训练变得棘手和不稳定。另一方面,增加一点增强数据(尤其是增强亮度)确实可以避免两个网络过度拟合。无论如何,利大于弊:我们在**没有任何增强的情况下得到很好的结果**,STN+IDSIA 比没有这个模块的 IDSIA 性能好 0.5–1%。

以下参数用于训练:

即使仅仅过了 10 个时期,我们也能在验证集上得到`accuracy = 99.3`。CNN 仍然过拟合,但是过拟合的影响可以通过图像增强来减少。事实上,通过添加额外的扩充,我在 10 个时期后得到了验证集上的`accuracy = 99.6`(尽管训练时间显著增加)。

下面是训练的结果(`idsia_1`是单个 IDSIA 网络,`idsia_stn`是 STN+IDSIA)。准确度是德国交通标志数据集的整体验证准确度,而不是 STN:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/5e8e6b38db11cc1779a5504bdb1d8abb.png)

STN+IDSIA 优于单个 IDSIA 网络,尽管它需要更多的时间来训练。请注意,上图中的认证准确度是按批次计算的,而不是按整个认证集计算的。

最后,下面是 STN 转换器训练后的输出示例:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0f934bcaa155482a64c468a90b79719d.png)

STN 确实抓住了图像的本质,并专注于交通标志,而不是背景或其他特征。

**最后,让我们回顾一下:**

*   STN 是一个可微分的模块,可以注入到卷积神经网络中。默认选择是将它放在输入层的正“后”,使其学习最佳变换矩阵 theta,从而最小化主分类器的损失函数(在我们的例子中,这是 IDSIA)。
*   STN 的采样器将仿射变换应用于输入图像(或特征地图)。
*   STNs 可以被视为图像增强的替代方案,图像增强是 CNN 实现空间不变性的默认方式。
*   向 CNN 添加一个或多个 stn 会使训练更加困难和不稳定:现在你不得不监控两个(而不是一个)网络的过度适应。这似乎是为什么 stn 还没有被广泛使用的原因。
*   用一些数据增强(特别是光照条件增强)训练的 stn 表现更好,并且不会过度拟合。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/42cb31e89ef7940c94004eac354ed203.png)

# 卷积:探索一个熟悉的算子的深层根源

> 原文:<https://towardsdatascience.com/convolution-a-journey-through-a-familiar-operators-deeper-roots-2e3311f23379?source=collection_archive---------8----------------------->

在现代机器学习的世界中,卷积算子占据着奇怪的位置:它对任何一个读过 2012 年以来的神经网络论文的人来说都非常熟悉,同时也是一个对其更深层次的数学基础往往知之甚少的对象。在音频处理环境中,您可能会听到它被描述为信号平滑器。在卷积网络中,它聚集来自附近像素的信息,也被描述为模式匹配过滤器,根据特定的局部像素模式更强烈地激活。从表面上看,卷积的所有这些不同的概念方面是如何植根于一个共享的数学现实的,这并不明显。

将所有这些解释拼凑成一个共享的理解网络的过程让人想起最终*真的看着*你每天经过的建筑上的一幅壁画,却发现它有着你以前从未见过的深刻内涵。从表面上看,这一理论的大部分可能与从业者的工作无关,但我认为,对我们日常使用的网络回旋的知识谱系有一个更坚实的理解,可以让我们从信心的基础上参与这个主题的文学,而不是困惑。

# 最好的起点

我们的故事不是从卷积进入应用科学领域开始的,而是从它下面的简单数学定义开始的。我知道,对于那些熟悉基于滤波器的卷积的人来说,这种框架可能是新的和陌生的,但请坚持下去,我将尽最大努力最终将所有的点连接起来。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/a9502ae4cd33a1c27d800c5018f765df.png)

在上式中,f 和 g 都是作用于相同输入域的函数。在概率的背景下,你可以把它们框定为密度函数;在音频处理中,它们可能是波形;但是,从根本上说,我们可以把它看作是一些通用函数。当你将两个函数卷积在一起时,卷积本身就是一个函数,因为我们可以计算卷积在特定点 t 的值,就像我们可以计算一个函数一样。

为了遍历这里发生的情况,为了计算 f 和 g 在 t 点的卷积,我们对负无穷和正无穷之间的所有τ值进行积分,并且在每一点,将 f(x)在τ位置的值乘以 g(x)在 t—τ的值,即计算卷积的点与积分中给定点的τ之间的差。

乍一看,这个计算似乎是任意的:很容易机械地理解正在计算的内容,但是很难凭直觉理解为什么这是一个有用的操作。为了更清楚地说明它的用途,我将从几个不同但相互关联的角度来研究这个操作。

# **卷积作为函数平滑**

我发现考虑卷积的一个有用的方法是类比核密度估计。在 KDE,你选择某个核函数——也许是高斯函数——并以数据集中的每一点为中心,将该核函数的副本相加。在许多数据点密集地彼此靠近的情况下,这些函数副本将会增加;最终,这个过程会产生一个概率密度函数的经验近似值。为了这个类比的目的,重要的事情是内核在数据点周围充当平滑器的方式。如果我们只是使用一个简单的直方图,并在每个观察数据点的位置直接添加一个质量单位,我们的结果将会非常不连贯和不连续,特别是在小样本量的情况下。通过使用将质量分散到附近点的核,我们表达了对光滑函数的偏好,利用了自然界中许多潜在函数都是光滑的直觉。

在卷积中,我们不是平滑由数据点的经验分布创建的函数,而是采用更一般的方法,这允许我们平滑任何函数 f(x)。但是我们使用类似的方法:我们取某个核函数 g(x ),在积分中的每一点放置一个 g(x)的副本,放大——也就是说,乘以——f(x)在该点的值。

上图显示了这种效果的可视化效果,摘自[对卷积](http://bmia.bmt.tue.nl/Education/Courses/FEV/book/pdf/90%20Appendix%20B%20-%20The%20concept%20of%20convolution.pdf)的出色总结。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/7c19f72c8d8835c0ac3fb9a32cedfa1f.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/643fac340947d550d15570967150de81.png)

上面的图显示了原始 f(x),即我们想要平滑的噪声信号。左下方显示了“内核加权”过程,在这里,高斯函数被放置在每个点的中心,并通过该点的 f(x)值按比例放大。*【题外话:这在技术上显示了一个有限卷积,其中你对一组有限的缩放核求和,每个核以沿着函数的一组有限的采样点中的一个为中心,但直觉转移到更难可视化的无限情况,在这种情况下,你对函数进行真正的积分,而不仅仅是对采样点求和】*。最后,右下方的图显示了当你对所有这些比例核求和时得到的结果:( f*g)(x)的卷积图。

# **卷积作为局部化信息的聚合**

一个完全不同的视角——使我们更接近于在神经网络中使用卷积的方式——是卷积作为一种信息聚合,但从特定点的角度进行聚合:卷积被评估的点。(为了简洁起见,我将这个点称为“输出点”,积分所经过的点称为“输入点”)输出点想要汇总关于函数的整个域中 f(x)的值的信息,但是它想要根据一些特定的规则来这样做。也许它只关心 f 在距离它一个单位的点上的值,而不需要知道那个窗口之外的点。也许它更关心空间中离自己更近的点,但它关心的程度会平稳衰减,而不是像第一个例子那样,在某个固定窗口之外下降到零。或者,也许它最在乎的是离它很远的点。所有这些不同种类的聚合都可以归入卷积的框架中,它们通过函数 g(x)来表示。

从数学的角度来看,卷积可以被看作是计算 f(x)的值的加权和,并且这样做的方式是每个 x 的贡献由其与输出点 t 之间的距离来确定。更准确地说,权重是通过询问“如果有一个以每个点 x 为中心的 g(x)的副本,如果我们在输出点 t 对其求值,该复制函数的值会是多少”来确定的。

为了形象化这一点,看看下面的图。在这个例子中,我们使用 g(x) = x,想象我们如何计算 x = -2 的加权和的权重。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/38f6d1fd3e9c3b8b94f60dd651dbf58b.png)

橙色曲线是 g(x),但以 x=1 为中心,它在 x=-2 的值是 9。绿色和紫色曲线分别表示以 x=2 和 x=3 为中心的 g(x)。如果我们假设 f 是一个只在 1、2 和 3 取非零值的函数,我们可以计算(f * g)(-1)如下: **9*f(1) + 16*f(2) + 25*f(3)**

看上面这张图,有了 g(x)的多个重叠副本,信息聚合框架和 KDE 框架之间的联系很容易。当我们把注意力集中在一个特定的输入点上时,每个点的 g(x)的副本都在回答“来自这个点的 f(x)信息将如何被传播出去”这个问题。当我们聚焦一个特定的输出点时,我们在问“在这个输出点上,其他点对 f(x)的聚合的贡献有多强”。前一部分的多拷贝图上的每条垂直线对应于给定 x 处的卷积,读取该线与每个 g(x)拷贝的交点,得到 f(x)在 x 处的权重,该拷贝位于 x 的中心。

# 以一个混乱点为中心

将卷积视为信息聚合器让我们更接近它们在神经网络中扮演的角色,在神经网络中,它们在某个点周围的局部区域中总结下层的行为。然而,仍然存在两个有意义的差距。

第一个事实是,乍一看,图像卷积滤波器似乎与本文迄今为止使用的示例在结构上非常不同,因为滤波器是 2D 和离散的,而示例是 1D 和连续的。但是,在实践中,这些差异更多的是装饰性的,而不是根本性的:简单的情况是,在图像上,f(x)和 g(x)是在 2D 空间(x_1,x_2)上定义和索引的,并且该空间被分块到函数值不变的离散仓中。

关于从传统卷积到网络卷积的跳跃,实际上困难和不直观的是,当我们看到局部聚集在神经网络上下文中发生时,我们通常将它描述为通过以聚集点为中心的加权核**发生,**根据该核从周围环境中提取信息。网络中的卷积滤波器由输出点(以及输出点本身)周围所有输入点的权重组成。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/11e6089927b445ba7b53c4a9f2635c47.png)

You’ve probably seen this image before, or something very like this

然而,正如你可能还记得的,在这篇文章前面的讨论中,传统的数学卷积实际上并不把它的 g(x)权重核放在输出点的中心。在卷积公式中,g(x)的副本位于每个输入点的中心,并且该副本在给定输出处的值决定了该输入点的权重。因此,令人困惑的是,我们在网络卷积中所知的**滤波器核与卷积公式中的 g(x)不是一回事。**如果我们想使用卷积公式应用网络过滤器,我们必须翻转它(在 1D 的情况下)或对角翻转它(在 2D 的情况下)。反过来,如果我们想从卷积积分中提取一个 g(x)并将其用作输出中心滤波器,那么该滤波器就是 g(-x)。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/78a1d4c9debb6f3a3a1ebb605c0c5fdf.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/5245df6ed050a693a7d47583131c5302.png)

在内核对称的情况下,这并不重要,但在典型的卷积滤波器中,对称并非如此:“在中心的右侧,向下一个”的权重不同于“在中心的左侧,向上一个”的权重,因此,如果您简单地使用以输出为中心的滤波器,并尝试在以输入点为中心的适当卷积框架中使用它,您将获得不同的权重。您可以在上面的可视化中更清楚地看到这一点,其中我们使用以输出为中心的 g(x)内核版本来为每个输入生成权重,然后使用以输入为中心的版本,并且可以看到,您不能在一个过程中使用相同的内核,而在另一个过程中使用相同的内核来获得相同的结果。这个事实——你不能直接把以输出为中心的核作为 g(x)插入卷积方程——是我对卷积最大的持续困惑,因为它似乎在做几乎相同的事情,而且因为如此多的解释使用对称核,所以区别并不突出。

# 卷积作为模式匹配器

如果你已经做到了这一步,你现在有希望更好地理解我们所知道和喜爱的滚动重量过滤器是如何连接到一个看起来模糊的积分的——尽管不是直接插入——的。最后一个难题是理解权重过滤器如何作为模式匹配器工作,这是卷积过滤器通常被描述的方式。为什么一个点周围的 f(x)值越像过滤器权重(此处 f(x)是像素值或来自较低网络层的值),该点的卷积值就越高?

简单的答案是,离散卷积相当于在滤波器权重和滤波器下的值之间取点积,从几何角度来说,点积衡量向量相似性。以输出为中心的卷积的工作原理是取一个权重向量和一个输入值向量,然后将对齐的条目相乘并求和:这就是计算点积。当然,您可以通过增加一个或两个向量的大小来增加点积的值,但是对于固定的大小,当向量指向相同的方向时,或者在我们的情况下,当像素值中的强度模式与权重过滤器中的高权重和低权重匹配时,点积的最大值就会达到。

# 剩余问题

即使我到了一个我可以舒服地发表博客的地方,我还是经常会有一些我直觉上不太理解的问题,而这篇文章也不例外。对我来说,这里最突出的问题是卷积算子和傅里叶变换之间的联系。傅里叶变换的众多名声之一是它对角化了卷积算子:也就是说,如果你把你的基转移到傅里叶变换的指数集,卷积两个函数就相当于在这个基上乘以它们。虽然我可以大声说出这些事实,但我还真不明白为什么它们是真的。

# 用简单图像解密卷积神经网络

> 原文:<https://towardsdatascience.com/convolution-neural-network-decryption-e323fd18c33?source=collection_archive---------12----------------------->

本文试图通过简单图像的可视化来解释卷积神经网络。使用非常基本的对象,如垂直线和水平线。避免了来自 ImageNet 数据集的复杂图像,因为可视化不容易解释。

当我在 2017 年开始为一个计算机视觉项目进行深度学习时,我遇到的第一个教程是像许多其他人一样使用卷积神经网络的猫/狗分类和 MNIST 例子。尽管这些例子让我们大致了解了 CNN 的工作方式及其令人难以置信的效率,但要真正理解其内部工作方式却很困难。我看到有很多关于使用不同的框架从头开始构建 CNN 的文章,也有关于各种数据集构建分类器的文章。毫无疑问,现在任何人都可以使用许多著名的框架在 30 分钟内建立一个非常好的图像分类模型。

我不打算进入梯度下降和反向传播的数学,因为已经有足够的资源可用。我们中的许多人确实理解数学,但仍然觉得我们错过了一些东西。自从深度神经网络出现以来,许多人都有同样的感觉,并一直试图找出理解黑盒真正学习什么以及如何更好地调整它们的方法。

许多人创造了许多可视化技术来了解 CNN 所学的内容,我相信你们中的许多人会遇到许多奇怪的彩色图像,这些图像会给我们带来很少的概念,甚至更糟,让我们觉得我们根本不了解任何东西,就像下面的这些:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9aa1242e9d5556df309f17d37a120e7a.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e0a61587f124a09d407f92ed04dba529.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/4a844337238110896c516eeaeb6c817e.png)

我觉得这主要是因为我们实验的所有例子都是高层次的图像。对于没有计算机视觉经验的人来说,猫/狗、ImageNet 甚至 MNIST 数据集都是太复杂的问题。

所以,在这里我将用一个非常简单的水平线和垂直线的数据集来解释 CNN。我在每个类中生成了大约 600 张大小为 50x50 的图像,如下所示,我们将使用 CNN 对它们进行分类:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0e09e999ff4b5f1609125321c514df36.png)

现在想象一下,给你一个任务,让你从头开始编写一个算法来对图像进行分类。对于任何有计算机视觉背景的人来说,这都是一个非常简单的任务,因为我们需要做的只是实现一个基本的边缘检测算法。现在有一些算法已经用于边缘检测很多年了。

我将在这里演示 Prewitt 操作符,但也可以尝试其他算法。Prewitt 算子用于图像的边缘检测。它检测两种类型的边缘:

*   水平边缘
*   垂直边缘

通过使用图像的相应像素强度之间的差异来计算边缘。从数学上来说,操作者使用两个 3×3 的核来计算导数的近似值,这两个核与原始图像进行卷积(参见 GIF 以获得基本概念),一个用于水平变化,另一个用于垂直变化。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9597b5abf46e1f4591f2696f675118e5.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/eafc6d146a76415c1082217f6f90daf5.png)

其中*表示 1 维卷积运算。Gx 和 Gy 将分别找到垂直边和水平边。它只是像一阶导数一样工作,并计算边缘区域中像素强度的差异。由于中间列为零,它不包括图像的原始值,而是计算该边缘周围的左右(或上下)像素值的差。这增加了边缘强度,并且与原始图像相比,它变得增强了。

现在让我们回到 CNN,这里我们有多层卷积,最大池,ReLU,Dropout 等等。在许多层中,卷积层是具有可训练参数的层之一,这些参数是内核。在数据科学社区中,内核也被称为权重。

我在一个相当简单的 CNN 模型上训练了我的数据集,如下所示:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b184966e82489677bf8d206fd2a8d0c5.png)

我使用 7x7 内核进行卷积。从上述模型可以看出,我在第一层中使用了 8 个内核,因此有 400 个可训练参数,计算如下:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9fa9f4d8b145c103ffd20f83a9faf616.png)

我已经保存了每个时期所有层的权重,并可视化了训练的进度。内核值的范围在 0 到 1 之间,可视化是使用 matplotlib 的颜色映射创建的,因此颜色映射不会准确地再现实际值,但它会给我们一个关于值的分布的好主意。下面是每个训练期后内核的 GIF 图。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/3ef4311c519095dd61388d4cf74c2ad1.png)

Kernel visualization during training

7x7 内核如下图所示,当时它们被**随机初始化**,然后在数据集上训练相同的内核**。**

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/4484af852972591f57d23f7b1bab8e12.png)

比较训练前后的内核,我们将不能非常清楚地理解每个内核已经学习了什么。这可以通过在图像上卷积内核并通过 ReLU 和 Maxpooling 层传递图像来更好地理解。为了更容易理解,我选择了两个内核,它们对于水平和垂直边缘都被很好地激活了。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/916f9299fa227fd2b55656f4bec416a5.png)

当在输入图像上卷积时,训练前的核 5 和 8 具有非常模糊的输出,并且没有清晰的激活。但是在训练之后,内核 5 很好地激活了垂直线,而不是水平线。更具体地说,它从左向右搜索边缘。如果你仔细观察内核 5,你会发现内核的左边更接近于 0,右边更接近于 1。在内核 8 中可以看到类似的模式,水平线被激活。

因此,从上面的图像中,我们可以了解到,通过反向传播,该模型已经学会了通过理解数据集来自己提出类似 Prewitt 算子核的东西。这里唯一的区别是有很多内核,每个内核都学到了一些东西(除了少数死内核)。

现在,如果我们进入下一层,与边缘检测相比,内核开始学习更高级别的特征,例如低级和高级模式识别。但问题是,因为即使在小模型中也存在大量可训练参数,所以很难将更高级的特征可视化。我这里不包括可视化,因为将会有大量的数据,并且很难理解。我将在另一篇文章中写它。

下面,我将我的模型可视化,在一个输入的水平线图像上,从头到尾,只是为了给出一个图像在下一层如何进展的概念:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0f679adab9cbbe04c0b2b62fc0fb9385.png)

我希望这篇文章能帮助你更好地理解 CNN。请在下面分享您的反馈或疑问。

# 卷积神经网络

> 原文:<https://towardsdatascience.com/convolution-neural-network-e9b864ac1e6c?source=collection_archive---------4----------------------->

## 对于初学者

# 介绍

卷积层用于从输入训练样本中提取特征。每个卷积图层都有一组有助于要素提取的过滤器。一般来说,随着 CNN 模型的深度增加,通过卷积层学习的特征的复杂性增加。例如,第一个卷积层捕获简单特征,而最后一个卷积层捕获训练样本的复杂特征。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/12d4f54ad07889d12291c4bde2475c3c.png)

通过考虑数据样本部分的卷积来提取特征。过滤器每次遍历的数据部分的数量与步长和填充值成比例。数据样本在卷积之前可以/可以不进行零填充。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b4eeed70a559079f9e2ad09ba7c5736f.png)

卷积输出然后通过一个称为 ReLU(整流线性单元)的激活单元。这个单元将数据转换成非线性形式。只有当卷积输出为负时,ReLU 的输出才会被削波为零。

由于消失梯度问题,Sigmoid 单元不是优选的激活单元。如果 CNN 的深度很大,那么当在输入层发现的梯度穿过输出层时,它的值会大大减小。这导致网络的总输出变化很小。这反过来导致收敛缓慢/没有收敛。为了避免这种情况,最好使用 ReLU。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/231ea0fae3411766523ad9411bd60711.png)

ReLU 的输出然后通过一个池层。合并图层移除卷积过程中捕获的任何冗余要素。因此,这一层减少了数据样本的大小。池化背后的原理是它假设图像像素的相邻值几乎相同。使用四个相邻像素值的平均值/最小值/最大值来进行合并。一般来说,在 2*2 过滤器的帮助下,输入图像的大小减少了一半。输入数据在汇集之前可能会/可能不会进行零填充。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/87a46f8f0ff05c7b9583080068b1722f.png)

按照 CNN 模型的设计,重复这种连续通过卷积和汇集层传递数据的过程。出于学习目的,这个过程重复 2-4 次。来自连续卷积和汇集层的输出然后通过多层神经网络。这里,每个神经元单元充当携带关于单元的信息的特征图。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/bbec227cb3be7e620b5740e782775d12.png)

丢弃层用于通过使 CNN 模型对噪声鲁棒来减少过拟合。这些层通常被引入两个完全连接的神经网络层之间。它们暂时切断两个完全连接的层之间的部分数据流。这相当于让模型在存在噪声的情况下学习准确分类。因此,减少了由于过拟合而导致的模型分类不准确的机会。

使用 SoftMax 函数计算 CNN 模型的输出。SoftMax 是首选,因为它给出了不同类别的输出概率,而不是在 sigmoid 输出的情况下> = 0.5。使用 SoftMax 函数根据类的最高概率查找输出结果可提高输出的准确性。

交叉熵用于衡量系统的性能。它们是在 SoftMax 函数的帮助下计算出来的。这里的优点是 SoftMax 输出是对应于我们知道输出所属的类的元素的踪迹。这通常节省了计算时间。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9d39f208c825c0c709f797cef3cdcd81.png)

# CNN 层概述

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/40356b328fc11157f66705f775c067e8.png)



# 特征提取— MNIST

本节让读者直观地了解如何将图像与核进行卷积,从而从输入图像中提取特征。我们考虑一个数字为 2 的图像,在每个示例中,该图像与翻转了 90 度的相同 3*3 滤波器进行卷积。

在我们的实验中,我们假设滤波器的阈值为+2。黑色像素表示为 0,白色像素表示为 1。卷积的结果如下所示。

1.

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/a52d0cda908ee48c7f4358ffcec9875f.png)

卷积的输出表明该滤波器善于检测内部边缘。这通过沿着对角线的图像中的明显边缘来显示。使用的 3*3 滤波器值为[[2,-1,-1],[-1,2,-1],[-1,-1,2]]。因此,当卷积输出大于定义的阈值 2 时,我们认为该卷积的输出为 1,否则为 0。

2.

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/5ebcf0eeb2124ec099fc72987a6c8bd8.png)

卷积的输出表明滤波器在对角线边缘是坏的。这通过沿着对角线的图像中的明显边缘来显示。使用的 3*3 滤波器值为[[-1,-1,2],[-1,2,-1],[2,-1,-1]]。因此,当卷积输出大于定义的阈值 2 时,我们认为该卷积的输出为 1,否则为 0。

3.

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/99e6cab6a34686e2edf303d0e30a527c.png)

卷积的输出表明该滤波器擅长检测水平边缘。这通过沿着水平面的图像中的明显边缘来显示。使用的 3*3 滤波器值为[[-1,2,-1],[-1,2,-1],[-1,2,-1]]。因此,当卷积输出大于定义的阈值 2 时,我们认为该卷积的输出为 1,否则为 0。

4.

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/3d21d355466296a96093b062b204c40d.png)

卷积的输出表示滤波器擅长检测垂直边缘。这通过沿着垂直平面的图像中的明显边缘来显示。使用的 3*3 滤波器值为[[-1,2,-1],[-1,2,-1],[-1,2,-1]]。因此,当卷积输出大于定义的阈值 2 时,我们认为该卷积的输出为 1,否则为 0。

源代码

[](https://github.com/shree6791/Deep-Learning/blob/master/CNN/MNIST/keras/src/Feature%20Extraction.ipynb) [## shree 6791/深度学习

### 深度学习——这个知识库由 Shreenidhi Sudhakar 实施的深度学习项目组成。

github.com](https://github.com/shree6791/Deep-Learning/blob/master/CNN/MNIST/keras/src/Feature%20Extraction.ipynb)

# 自然语言推理的卷积注意模型

> 原文:<https://towardsdatascience.com/convolutional-attention-model-for-natural-language-inference-a754834c0d83?source=collection_archive---------2----------------------->

在这篇文章中,我想向你展示一个我在 Quora 问题配对竞赛中使用的模型。首先,我将描述一个用于自然语言推理的可分解注意力模型( [Parikh 等人,2016](https://arxiv.org/pdf/1606.01933.pdf) ),然后用卷积层扩展它,以提高损失和分类精度。出于本文的目的,我将使用斯坦福 NLI 语料库进行比较。

# 介绍

自然语言推理(NLI)和复述检测是自然语言处理的主要研究课题之一。自然语言推理指的是确定两个语句之间的蕴涵和矛盾的问题,而复述检测的重点是确定句子的重复。发表在这方面的论文使用递归神经网络[王&江,2016](https://arxiv.org/pdf/1611.01747.pdf) ,[王等,2017](https://arxiv.org/pdf/1702.03814.pdf) ,卷积神经网络[牟等,2016](https://arxiv.org/abs/1512.08422) 或带软对齐和注意的前馈神经网络[帕里克等,2016](https://arxiv.org/pdf/1606.01933v1.pdf) 。

# 模型架构

模型的输入是表示为单词嵌入向量序列的两个短语`a = {a1,...,am}`和`b = {b1,...,bn}`。目标是估计这两个短语相互蕴涵或矛盾的概率。在模型训练过程中,我们还将使用一个变量`y`,它是一个基础事实和辅助输入`a_len`和`b_len`来帮助我们构建掩蔽矩阵。下图描绘了由三层组成的核心模型架构:关注、比较和聚集。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e08137c5d9d9e7eafed845e8ed67f1ca.png)

A high-level overview of the model architecture. (Image credit: Parikh et al., 2017)

## 注意力

注意力层使用的是 [Bahdanau et al .,2016](https://arxiv.org/pdf/1409.0473.pdf) 提出的一种神经注意力的变体。它是使用分别应用于这两个问题的前馈神经网络`F`实现的(Bahdanau 的工作使用递归神经网络的内部状态)。然后,使用 softmax 函数对神经网络的输出进行归一化,并将其与第二个句子软对齐。该图层的方程式如下所示:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/bec8206d1d499877ce5c2014ded15a9e.png)

where m = number of words in a, n = number of words in b

对于训练,我使用预先训练的 300 维手套单词嵌入。`F(a)`的输出大小是`mx200`,而`F(b)`的输出大小是`nx200`,这意味着注意力权重矩阵`e`将具有形状`mxn`。`beta`和`alpha`则是 softmax 标准化注意力权重和相应句子矩阵之间的点积。

## 比较

下一步是比较软对齐的句子矩阵。类似于前一步骤,使用前馈神经网络`G`,并且网络的输入分别是连接的句子矩阵`[a, beta]`和`[b, alpha]`。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/7469210d299e762d2a0f2bc512e06490.png)

Soft-aligned matrix comparison

## 聚合

核心模型架构的最后一部分是聚合层。这一层所做的只是对比较网络的输出进行列式求和,这样我们就可以获得每个句子的固定大小的表示。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/1ae9d209fb91735db84b66e351b99a7e.png)

Comparison matrix aggregation

最后,聚合的句子表示被连接起来并输入到一个密集的分类网络中。分类器的输出层是 softmax 标准化的,以便我们获得目标类的概率分布。

# 卷积层

NLI 的可分解注意力模型的扩展是基于这样一个事实,即同一个单词在给定的上下文中可以有不同的含义。类似于我们人类在上下文中理解单词的方式,机器学习模型可以通过使用递归神经网络(计算成本更高)或卷积层(成本更低但效率也更低)来学习这些更高级的单词表示。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/401675fc4ba1ff5695580fac0b71fcb9.png)

Two convolutional layers with filter size 3 and stride 1

上图显示了如何使用卷积层构建更高级别的表示。例句“*我们在动物园里看到了一只漂亮的鹤”*包含单词 **crane** which 可以是一只鸟,但根据句子的上下文,它也可以是一个结构装置。

# 模特培训

首先使用`nltk.word_tokenize`函数对句子进行标记化,然后执行嵌入索引查找。如果在字典中找不到某个标记,则将其忽略,不进行插补。然后用零向量填充嵌入索引,直到给定批次中最长句子的长度。卷积层然后在第一个令牌之前和最后一个令牌之后附加零向量,以便在应用卷积之后,有效句子长度保持不变。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/3eb4c09652c25627cb427f30abc485b0.png)

High level overview of a Tensorflow model graph

注意力权重矩阵`e`将所有填充值替换为`-np.inf`,以便在应用 softmax 归一化后,我们获得句子有效部分的概率分布,并且所有填充值为`0`。最后,如本文的**模型架构**部分所述,应用比较、聚集和分类层,并使用交叉熵损失来训练模型。

# 结果

首先,我在 Tensorflow 中实现了 NLI 最初的可分解注意力模型,以获得基线分数。虽然原始论文报告了 86.3%的测试准确性,但我无法将它提高到 83.8%以上(这可能是因为我使用了 AdaM optimizer 而不是 Adagrad,并且只训练了 100 个时期)。加入卷积层后,模型达到了分类精度 *84.5%* 。我还将 optimizer 改为 Adagrad,因为它与卷积架构配合得更好。

*在撰写本文时,目前最先进的自然语言推理模型是双边多视角匹配模型(* [*王等,2017*](https://arxiv.org/pdf/1702.03814.pdf) *),达到分类准确率 88.8%。我在这篇文章中选择 NLI 的可分解注意力模型的原因是,该模型的架构更简单,因此运行训练/推理花费的时间更少,我可以更多地进行实验。*

# 结论

正如我们所看到的,卷积层在分类精度上给了我们一点点改进。这种扩展可以对包含更多具有上下文条件意义的单词的数据集提供更大的改进,也可以对没有这种单词的数据集提供很少或没有改进。对于具有多个超参数组合的更多训练时期,训练模型也可以获得更好的分数。

完整的 Tensorflow 模型代码可以在[这里](https://gist.github.com/marekgalovic/a1a4073b917ae1b18dc7413436794dca)找到。如果你喜欢这篇文章,请随意推荐给其他人。此外,如果你对我做错了什么或者本可以做得不一样有任何建议,请在下面的评论区留下它们。

# 卷积神经网络

> 原文:<https://towardsdatascience.com/convolutional-neural-network-17fb77e76c05?source=collection_archive---------1----------------------->

## 卷积神经网络导论

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8a5755826e783e52734abc032440d0d6.png)

[**Fig 1\. Toon Discovers how machines see**](https://unsplash.com/photos/YvkH8R1zoQM)

在本文中,我们将了解什么是卷积神经网络,简称 ConvNets。ConvNets 是将深度学习中的图像工作提升到下一个水平的超级英雄。对于 ConvNets,输入是一幅图像,或者更具体地说,是一个 3D 矩阵。

让我们先来看看 ConvNet 是什么样子的!

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/465b94f9c72e20fc3609853c1038c82b.png)

**[Fig 2.]** [**Convolutional Neural Network**](http://file.scirp.org/Html/4-7800353_65406.htm)

简单来说,
一个 ConvNet 通常有 3 种类型的层:
1) **卷积层** ( *CONV* )
2) **池层** ( *池* )
3) **全连接层** ( *FC*

让我们更详细地看看每一层:

# 卷积层

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/89cb13a8f9c57d1ceb2e4fa383929f5c.png)

**Fig 3\. Convolutional Layer**

卷积层是 CNN 的第一层。
作为 ***输入*** 得到一个维度为*【h1 * w1 * D1】*的矩阵,就是上图中的蓝色矩阵。

接下来,我们有 ***内核*** (滤镜)。
仁?
内核是一个尺寸为*【H2 * w2 * D1】,*的矩阵,它是上图中(内核层中)多个长方体(内核)中的*一个*黄色长方体。
对于每个卷积层,有多个内核堆叠在彼此的顶部,这就是形成图 2 中的黄色三维矩阵的原因,其维度为*【H2 * w2 * D2】*,其中 *d2* 是内核的数量。

对于每个内核,我们都有其各自的**偏差**,这是一个标量。

然后,我们有一个 ***输出*** 给这一层,图 2 中的绿色矩阵,它有尺寸*【H3 * w3 * D2】。*

让我们从上面揭示一些显而易见的事情。
1)输入和*一个*内核**的深度( *d1* )(或通道数)相同**。
2)输出**的深度( *d2* )等于**内核的数量(即橙色三维矩阵的深度)。

好,我们有输入,内核和输出。现在让我们看看 2D 输入和 2D 内核会发生什么,即 *d1=1* 。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c444d022ad171fbe6a0b57593327555f.png)

**Fig 4\.** [**Calculation of output using 2D Convolution**](/types-of-convolutions-in-deep-learning-717013397f4d)

对于图像上内核的每个位置,内核上的每个数字与输入矩阵上的相应数字相乘(*蓝色矩阵*),然后对它们求和,得到输出矩阵中相应位置的值(*绿色矩阵*)。

使用 *d1 > 1* 时,每个通道发生相同的情况,然后将它们相加,再与相应滤波器的偏置相加,这就形成了输出矩阵相应位置的值。让我们想象一下!

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/39a98fbf0d99e8bdc7696238858a2db9.png)

**Fig 5\.** [**An example of how Inputs are mapped to Outputs**](http://cs231n.github.io/assets/conv-demo/index.html)

这是构成输出层的 *d2* 矩阵之一。

对所有的 *d2* 内核重复整个过程,这些内核形成输出层中的 *d2* 通道。

# 汇集层

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e2a50bb987e445a5b539cac20f503e59.png)

**Fig 6\. Max Pooling Layer**

共有两种类型的池:
1)最大池
2)平均池

汇集层的主要目的是减少输入张量的参数数量,从而
-有助于减少过拟合
-从输入张量中提取代表性特征
-减少计算,从而提高效率

池层的输入是张量。

在 Max Pooling 的情况下,其示例在图 6 的*中示出,*大小为`n*n` *(在上述示例中为 2×2)*的核在矩阵中移动,并且对于每个位置,取**最大值**并放入输出矩阵的相应位置。

在平均汇集*、*的情况下,大小为`n*n`的核在矩阵中移动,并且对于每个位置,对所有值取**平均值,并放入输出矩阵的相应位置。**

对输入张量中的每个通道重复这一过程。所以我们得到了输出张量。
所以,要注意的一点是,**池化在图像的高度和宽度上对图像进行缩减采样,但是通道的数量(深度)保持不变。**

# 全连接层

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/2f06f34df79a570b155cdd46ab17ce30.png)

**Fig 4\. Fully Connected Network**

全连接层简单地说就是,[](https://en.wikipedia.org/wiki/Feedforward_neural_network)**前馈神经网络。*完全连接的层形成网络中的最后几层。*

*到全连接层的**输入**是来自*最终*池化或卷积层的输出,它被 ***展平*** 然后馈入全连接层。*

> *变平了?
> 最终(和任何)池和卷积层的输出是一个三维矩阵,展平就是将其所有值展开成一个向量。
> 我们来形象化一下!*

*![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/7dc8ea42ae93c6b9f136ca9b74b8d85a.png)*

***Fig 5\. Flattening***

*这个展平的向量然后连接到几个完全连接的层,这些层与人工神经网络相同,执行相同的数学运算!*

*对于人工神经网络的每一层,进行以下计算*

*![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/54e29b00a99ab78814cc60c314158715.png)*

***Fig 6\. ANN Calculation for each layer***

*其中,
**x** — *为输入向量,维数为****【p _ l,1】***
**W**—*为权重矩阵,维数为****【p _ l,n _ l】****其中,****p _ l****为上一层神经元个数* **b** *—是维度为****【p _ l,1】*****g***—是激活函数,通常是****ReLU****。**

*对每一层重复这种计算。*

*在通过完全连接的层之后,最终层使用***【soft max】激活函数*** *(而不是 ReLU)* ,该函数用于获得输入在特定类中的概率(*分类*)。*

*最后,我们得到了图像中物体属于不同类别的概率!!*

*这就是卷积神经网络的工作原理!!
输入图像被归类为标签!!*

*现在,让我们想象如何从输入张量计算输出张量的维数。*

*![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/a29b0c91121c7505bfec35591c523dee.png)*

***Fig 7\. Output Dimension Calculations from Input Dimensions***

*其中,
***W1***—*是输入张量* ***F***—*是内核* ***P****—是填充** 

> ****垫高?通常,输入矩阵用零填充,这样内核可以在矩阵上均匀移动,结果矩阵具有期望的维数。因此,P=1 意味着输入矩阵的所有边都用 1 层零填充,如图 4 所示,输入(蓝色)矩阵周围有虚线。****
> 
> ****跨步?**
> 内核在矩阵上一次移动一个像素(图 4)。因此,这被称为具有 1 的步幅。我们可以将步距增加到 2,这样内核在矩阵上一次移动 2 个像素。这将反过来影响输出张量的维度,并有助于减少过拟合。**

*****输出张量的通道数会发生什么变化?***
嗯,在卷积层的情况下,等于内核的数量。
在合并层的情况下,输入张量和输出张量中的通道保持相同!**

# 卷积神经网络——实施的鸟瞰图

> 原文:<https://towardsdatascience.com/convolutional-neural-network-a-birds-eye-view-with-an-implementation-1bff505dd3d9?source=collection_archive---------12----------------------->

*这篇文章正在被移到我的* [*子栈发布*](https://vishalramesh.substack.com/) *。这里* *可以免费阅读文章* [*。这篇文章将于 2022 年 5 月 18 日被删除。*](https://vishalramesh.substack.com/p/convolutional-neural-network-a-birds-eye-view-with-an-implementation-1bff505dd3d9?s=w)

这个内核旨在给出一个卷积神经网络(CNN)的简单理解。这将按以下顺序实现:

*   理解卷积运算
*   理解神经网络
*   数据预处理
*   了解使用的 CNN
*   了解优化器
*   理解`ImageDataGenerator`
*   做出预测并计算精确度
*   看到神经网络的运行

# 什么是卷积?

> *在数学(尤其是泛函分析)中,卷积是对两个函数(f 和 g)进行数学运算,以产生第三个函数,该函数表示一个函数的形状如何被另一个函数修改。*
> 
> (来源:[维基百科](https://en.wikipedia.org/wiki/Convolution))

这种运算应用于多个领域,如概率、统计、计算机视觉、自然语言处理、图像和信号处理、工程和微分方程。

该操作在数学上表示为

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9471ff84c27cb32730f6d2038aa81815.png)

The Convolution operation

(图片来源:[维基百科](https://en.wikipedia.org/wiki/Convolution#Definition))

检查此[链接](https://github.com/vdumoulin/conv_arithmetic)以直观了解卷积运算

# 什么是人工神经网络?

> 人工神经网络(ANN)或连接主义系统是由构成动物大脑的生物神经网络模糊启发的计算系统。这种系统通过考虑例子来“学习”执行任务,通常没有用任何特定于任务的规则来编程。
> 
> (来源:[维基百科](https://en.wikipedia.org/wiki/Artificial_neural_network))

人工神经网络是一个被称为人工神经元的更小的处理单元的集合,它与生物神经元大致相似。

**生物神经回路**

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/beed7c84847f1b30c5023fdd78d150c5.png)

A Biological Neural Circuit

*(图片来源:* [*维基百科*](https://en.wikipedia.org/wiki/Neural_circuit) *)*

互连电路的集合构成一个网络

**人工神经网络**

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/be82b2341185f626d2d9b4ea4154218c.png)

Artificial Neural Network

*(图片来源:* [*维基百科*](https://en.wikipedia.org/wiki/Artificial_neural_network) *)*

现在,我们从实现开始

# 导入必要的库

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

import tflearn.data_utils as du
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPool2D
from keras.optimizers import RMSprop
from keras.preprocessing.image import ImageDataGenerator
from sklearn.metrics import confusion_matrix


# 加载数据集

train_data = pd.read_csv('../input/csvTrainImages 13440x1024.csv', header = None)
train_label = pd.read_csv('../input/csvTrainLabel 13440x1.csv', header = None)
test_data = pd.read_csv('../input/csvTestImages 3360x1024.csv', header = None)
test_label = pd.read_csv('../input/csvTestLabel 3360x1.csv', header = None)


# 数据集

这里使用的数据集是[阿拉伯手写字符数据集](https://www.kaggle.com/mloey1/ahcd1)。

trainImages.csv 有 1024 列和 13440 行。每一列代表图像中的一个像素,每一行代表一个单独的灰度图像。每个像素的值在 0 -255 之间变化。

train_data = train_data.iloc[:,:].values.astype('float32')
train_label = train_label.iloc[:,:].values.astype('int32')-1
test_data = test_data.iloc[:,:].values.astype('float32')
test_label = test_label.iloc[:,:].values.astype('int32')-1


# 可视化数据集

def row_calculator(number_of_images, number_of_columns):
if number_of_images % number_of_columns != 0:
return (number_of_images / number_of_columns)+1
else:
return (number_of_images / number_of_columns)def display_image(x, img_size, number_of_images):
plt.figure(figsize = (8, 7))
if x.shape[0] > 0:
n_samples = x.shape[0]
x = x.reshape(n_samples, img_size, img_size)
number_of_rows = row_calculator(number_of_images, 4)
for i in range(number_of_images):
plt.subplot(number_of_rows, 4, i+1)
plt.imshow(x[i])


**训练集**

display_image(train_data, 32, 16)


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/4e14111ad2ac92e3af258a8e3ed2cb69.png)

The training dataset

**测试设置**

display_image(test_data, 32, 16)


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/dd6308181ad8fac994b94bce43d6765b.png)

The test data

# 数据预处理

**编码分类变量**

***什么是分类变量?***

> *在统计学中,分类变量是一个变量,它可以取有限的、通常是固定数量的可能值中的一个,根据一些定性属性将每个个体或其他观察单位分配到特定的组或名义类别。*
> 
> (*来源:* [*维基百科*](https://en.wikipedia.org/wiki/Categorical_variable) )

简单地说,分类变量的值代表一个类别或类。

## 为什么我们需要对分类变量进行编码?

对代表一个类别的数字执行运算是没有意义的。因此,需要进行分类编码。

查看 stackoverflow 上的这个链接,通过一个例子来理解。

阿拉伯字母表中有 28 个字母。所以,有 28 节课。

train_label = du.to_categorical(train_label,28)


# 正常化

## 什么是正常化?

进行标准化是为了将整个数据带入一个明确定义的范围内,最好在 0 和 1 之间

> *在神经网络中,不仅要对数据进行标准化,还要对其进行缩放,这是一个好主意。这是为了在误差表面更快地接近全局最小值。*
> 
> *(来源:* [*堆栈溢出*](https://stackoverflow.com/questions/4674623/why-do-we-have-to-normalize-the-input-for-an-artificial-neural-network) *)*

train_data = train_data/255
test_data = test_data/255train_data = train_data.reshape([-1, 32, 32, 1])
test_data = test_data.reshape([-1, 32, 32, 1])


进行整形以使数据代表 2D 图像

train_data, mean1 = du.featurewise_zero_center(train_data)
test_data, mean2 = du.featurewise_zero_center(test_data)


基于特征的零中心化是用指定的平均值对每个样本进行零中心化。如果未指定,则评估所有样本的平均值。

# 构建 CNN

recognizer = Sequential()recognizer.add(Conv2D(filters = 32, kernel_size = (5,5),padding = 'Same', activation ='relu', input_shape = (32,32,1)))
recognizer.add(Conv2D(filters = 32, kernel_size = (5,5),padding = 'Same', activation ='relu'))
recognizer.add(MaxPool2D(pool_size=(2,2)))
recognizer.add(Dropout(0.25)) recognizer.add(Conv2D(filters = 64, kernel_size = (3,3),padding = 'Same', activation ='relu'))
recognizer.add(Conv2D(filters = 64, kernel_size = (3,3),padding = 'Same', activation ='relu'))
recognizer.add(MaxPool2D(pool_size=(2,2), strides=(2,2)))
recognizer.add(Dropout(0.25)) recognizer.add(Flatten())
recognizer.add(Dense(units = 256, input_dim = 1024, activation = 'relu'))
recognizer.add(Dense(units = 256, activation = "relu"))
recognizer.add(Dropout(0.5))
recognizer.add(Dense(28, activation = "softmax"))


## 什么是最大池?

汇集意味着组合一组数据。组合数据的过程遵循一些规则。

> *根据定义,最大池取定义网格的最大值。*
> 
> (*来源:*[*machine learning online . blog*](https://machinelearningonline.blog/2017/06/29/max-pooling/))

最大池用于减少维度。也可以避免过度拟合。查看[这篇](https://machinelearningonline.blog/2017/06/29/max-pooling/)博客,更好地理解最大池。

## 什么是辍学?

> *Dropout 是一种正则化技术,通过防止对训练数据的复杂协同适应来减少神经网络中的过拟合。这是用神经网络进行模型平均的一种非常有效的方法。术语“丢失”是指在神经网络中丢失单元(隐藏的和可见的)。*
> 
> (*来源:* [*维基百科*](https://en.wikipedia.org/wiki/Dropout_(neural_networks) *)* )

## 什么是扁平化?

进行展平是为了将多维数据转换成 1D 特征向量,以供作为密集层的下一层使用

## 什么是密集层?

密集层只是一层人工神经网络

# CNN 的优化器

## 什么是优化器?

> *优化算法帮助我们最小化(或最大化)目标函数(误差函数的另一个名称)E(x ),它是一个简单的数学函数,依赖于模型的内部可学习参数,这些参数用于根据模型中使用的一组预测值(X)计算目标值(Y)。例如,我们将神经网络的权重(W)和偏差(b)值称为其内部可学习参数,用于计算输出值,并在最佳解决方案的方向上学习和更新,即通过网络的训练过程最小化损失,并且在神经网络模型的训练过程中也起主要作用。*
> 
> *(来源:* [*走向数据科学*](/types-of-optimization-algorithms-used-in-neural-networks-and-ways-to-optimize-gradient-95ae5d39529f) )

optimizer = RMSprop(lr=0.001, rho=0.9, epsilon=1e-08, decay=0.0)


这里使用的优化器是一个 RMSprop。点击[此处](https://engmrk.com/rmsprop/)了解更多关于 RMSprop 的信息

recognizer.compile(optimizer = optimizer , loss = "categorical_crossentropy", metrics=["accuracy"])


# 什么是`ImageDataGenerator`?

图像数据生成器用于生成具有实时增强的批量张量图像数据。这些数据是成批循环的。

它用于批量加载图像。

datagen = ImageDataGenerator(
featurewise_center=False,
samplewise_center=False,
featurewise_std_normalization=False,
samplewise_std_normalization=False,
zca_whitening=False,
rotation_range=10,
zoom_range = 0.1,
width_shift_range=0.1,
height_shift_range=0.1,
horizontal_flip=False,
vertical_flip=False)datagen.fit(train_data)


# 用 CNN 拟合训练数据

recognizer.fit_generator(datagen.flow(train_data,train_label, batch_size=100), epochs = 30, verbose = 2, steps_per_epoch=train_data.shape[0] // 100)


# 做出预测

predictions = recognizer.predict(test_data)
predictions = np.argmax(predictions,axis = 1)


# 生成混淆矩阵

## 什么是混淆矩阵?

> 混淆矩阵是一种总结分类算法性能的技术。如果每个类中的观测值数量不相等,或者数据集中有两个以上的类,那么分类精度本身就可能会产生误导。计算混淆矩阵可以让您更好地了解您的分类模型是正确的,以及它正在犯什么类型的错误。
> 
> [来源](https://machinelearningmastery.com/confusion-matrix-machine-learning/)

cm = confusion_matrix(test_label, predictions)


# 计算精确度

accuracy = sum(cm[i][i] for i in range(28)) / test_label.shape[0]
print("accuracy = " + str(accuracy))


获得了 97%的准确度

# 观看 CNN 的运作

要实时了解 CNN 的工作情况,请点击[中的](http://scs.ryerson.ca/~aharley/vis/conv/flat.html)链接。它展示了一个 CNN 的工作原理,这个 CNN 被训练来识别手写数字。

# 卷积神经网络— II

> 原文:<https://towardsdatascience.com/convolutional-neural-network-ii-a11303f807dc?source=collection_archive---------7----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/96ff93bf1b025900fa00a870f2eefde5.png)

继续我们在[上一篇文章](/cnn-part-i-9ec412a14cb1)中的学习,我们将在这篇文章中讨论以下主题:

*   体积卷积
*   一次多个过滤器
*   一层卷积网络
*   了解尺寸变化

我已经尽可能地通过插图来解释大多数主题。如果有些东西不容易理解,请告诉我。

> *我们开始吧!*

# 体积卷积

不要害怕读“音量”,这只是一种用一个以上的通道,即`RGB`或任何其他通道来表达图像的方式。

到目前为止,我们只有一个通道,所以我们只关心图像的`height`和`width`。但是,如果增加一个以上的通道,我们需要考虑所涉及的滤波器,因为它们还应该包含所有通道的卷积(此处为 3)。
因此,如果图像尺寸为`n x n x #channels`,那么之前的`f x f`滤波器现在也需要尺寸为`f x f x #channels`。

直观上,我们的输入数据不再是二维的,事实上,如果考虑通道,它是三维的,因此被命名为 **volume** 。

下面是一个体积卷积的简单示例,图像的尺寸为`6 x 6 x 3`,3 表示 3 个通道 R、G 和 b。类似地,滤波器的尺寸为`3 x 3 x 3`。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c87d8dac705bbe6d184c569e193124a8.png)

Fig 1\. Convolution over volume (colour image — 3 channels) with filter of dimension 3 x 3

在给定的例子中,滤波器的目的是检测垂直边缘。如果只需要在`R`通道中检测边沿,那么只需要根据要求设置`R`通道中的权重。如果您需要检测所有通道的垂直边缘,那么所有滤波器通道将具有如上所示的相同权重。

# 一次多个过滤器

很有可能你需要从一幅图像中提取许多不同的特征,为此你将使用多个过滤器。如果单独的滤波器被分别卷积,将增加计算时间,因此直接一次使用所有需要的滤波器更方便。

与单个滤波器的情况一样,单独执行卷积,然后将两个卷积的结果合并在一个堆栈中,以形成具有代表滤波器数量的第三维的输出体积。

下面的例子考虑了一个如上的`6 x 6 x 3`图像,但是我们使用了 2 个维度为`3 x 3 x 3`的过滤器(垂直边缘和水平边缘)。将每个`4 x 4`的合成图像叠加在一起,得到一个`4 x 4 x 2`的输出体积。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b4e5f5c5bedc255ac8e5040cc67747fc.png)

Fig 2\. Convolution over volume with multiple filter of dimensions 3 x 3

对于任何一般情况,可以使用以下公式计算输出尺寸:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8b784f7ed902164edd654e673c6449eb.png)

Fig 3\. Equation governing the output image/signal dimension wrt input and filter dimension

这里,`nc`是输入图像中的通道数,`nf`是使用的滤波器数。

# 一层卷积网络

如果我们考虑一个卷积网络的一个层,而不考虑汇集层和展平,那么它将看起来接近这样:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/f925d51bfc4dab8809d90b7df684b454.png)

Fig 4\. Single layer of convolution network only with Relu activation

重要的是要理解,像在任何其他神经网络中一样,卷积神经网络也具有输入数据`x`和模型权重,输入数据`x`在这里是图像,模型权重由滤波器 F1 和 F2 给出,即`W`。一旦权重和输入图像被卷积,我们得到加权输出`W * x`,然后我们加上偏差`b`。

这里的一个关键功能是`RELU`激活功能,它是一个整流线性单元。Relu 帮助我们将非线性添加到学习模型中,并帮助我们更好地训练/学习通用情况下的模型权重。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/2900a815d816f187f0915aa34f473c3e.png)

Fig 5\. Relu activation function

对于低于某个阈值(这里是 0)的值,relu 函数根本不更新参数。它就会死去。对于要考虑用于训练的特定训练示例,需要为要激活的神经元设置最小值。Relu 还帮助我们减少在大多数深度神经网络中面临的消失和爆炸梯度问题,因为 Relu 提供了有效的梯度传播。

# 了解尺寸变化

现在我们已经对单层中发生的事情有了概念性的了解,让我们用公式表示卷积网络的任何给定层`l`的轮廓。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c4614ce4436a86002dfb8f749993bc07.png)

Fig 6\. Equations governing change in input dimensions based on filters, padding and stride

计算单个组件尺寸后,层`l`的最终尺寸`n`可通过下式计算:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c3feadcee779509c2b9525472d001c0a.png)

Fig 7\. Dimensions of last layer **l** with filter size **f**, padding **p** and a stride of **s**

# 结论

本文一开始,我们探讨了体积卷积,然后立即讨论了多个滤波器。很容易理解,这里的*体积*就是指图像数据的 3 个颜色通道。关于卷积网络的单个层的讨论对于掌握整个网络架构非常重要,所以要确保这些概念(relu、激活函数)是清楚的。最后一节讲述了我们如何根据滤波器卷积中使用的填充量和步幅来评估模型的层尺寸。

> 在下一篇文章中,我将详细解释各种 CNN 架构,并评估其独特的功能

*原载于 2017 年 11 月 18 日*[*mandroid 6 . github . io*](https://mandroid6.github.io/2017/11/18/Convolutional-Neural-Networks-II/)*。*

[https://twitter.com/mandroid_6](https://twitter.com/mandroid_6)

## 查看我关于机器学习和深度学习的其他帖子:[https://medium.com/@razzormandar](https://medium.com/@razzormandar)

# 基于结构化银行客户数据的卷积神经网络

> 原文:<https://towardsdatascience.com/convolutional-neural-network-on-a-structured-bank-customer-data-358e6b8aa759?source=collection_archive---------1----------------------->

## 一个案例研究证明了 CNN 的价值,即使你没有处理计算机视觉问题。

作为一名为传统银行工作的定量分析师,我坐在一旁看着深度学习颠覆和改变了相当多的行业,但对它对我所在行业的实际用处持怀疑态度。你知道,可能有一些应用程序,如捕捉支票图像或聊天机器人,但当涉及到贷款业务的核心问题时,如信用风险、欺诈风险和营销等问题,我们处理的数据是非常结构化的,这些数据可以放入二维或三维表格中。深度学习在这里似乎有些矫枉过正。或者是?多年来,我已经看到了一些在非图像数据上成功使用卷积神经网络(CNN)的例子,但所有这些例子似乎都具有复杂的空间数据结构,算法可以利用这种结构。此外,银行仍在努力从线性模型过渡到更复杂的模型,如 GBM。深度学习似乎太遥远了。带着许多疑问,我开始了一个为期 3 周半的旅程,使用[桑坦德产品推荐 Kaggle 竞赛](https://www.kaggle.com/c/santander-product-recommendation)数据构建卷积神经网络。剧透一下——CNN 在结构化数据方面也做得非常好!

# 问题是

2016 年,桑坦德银行(西班牙)在 Kaggle 上举办了他们的第二次比赛。他们向 Kaggle 社区发出挑战,要求他们根据现有客户和类似客户过去的行为,预测他们的现有客户在下个月将使用哪些产品。数据集捕获客户信息的每月快照,一些是人口统计信息,如性别、年龄、收入和位置信息,一些是关系信息,如任期、关系类型等。该数据还跟踪每月客户的产品使用情况。这家银行提供 24 种产品,包括支票、储蓄、抵押贷款、信用卡等。如果客户在某个月使用了某个产品,则使用指标设置为 1,否则为 0。任务是根据客户 2015 年 1 月至 2016 年 5 月的产品使用情况以及 2015 年 1 月至 2016 年 6 月的其他信息,预测 2016 年 6 月的所有 24 个使用指标(但他们只关心那些从 0 变为 1 的使用情况)。为模型性能选择的指标是 [MAP@7](https://medium.com/@pds.bangalore/mean-average-precision-abd77d0b9a7e) ,它衡量推荐与客户的实际使用有多相关,以及推荐的排名顺序有多好(想想你会如何衡量从谷歌关键词搜索返回的第一页的有用性:结果越相关越好;结果从上到下的实际顺序与您的预期越一致,就越好)。由于一个客户在某个月可以使用多种产品,这是一个多类多标签的分类问题。

在宽客/经济学家的世界里,这种数据通常被称为“[面板数据](https://en.wikipedia.org/wiki/Panel_data)”。这些数据没有什么特别的,如果目标被一个默认标签代替,它可能是一个[行为记分卡](https://www.creditcards.com/glossary/term-behavior-score.php)模型的很好的数据集。然而,它确实包含一个时间维度和一个 sudo 空间维度(产品使用), CNN 或许可以利用这一点。

# CNN 的力量——特色工程

一般来说,如果没有广泛的特征工程,你不可能赢得一场竞争。在这种情况下也不例外,请看本次比赛的两个获奖方案:[第二](http://blog.kaggle.com/2017/01/12/santander-product-recommendation-competition-2nd-place-winners-solution-write-up-tom-van-de-wiele/)和[第三](http://blog.kaggle.com/2017/02/22/santander-product-recommendation-competition-3rd-place-winners-interview-ryuji-sakata/)名。特征工程在这两者中都扮演了重要的角色。他们想出了输入数据的智能转换,努力得到了回报。

下图显示了 CNN 算法的概念。最终密集连接的神经网络之前的卷积/汇集层用于从输入图像中提取和抽象模式。换句话说,对预测任务有用的工程特征。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8fa83c805a3cd6ad6beee0b7635270cc.png)

我的计划是把专题工程任务交给 CNN,自己不做任何工程。怎么会?通过将客户数据转换成类似图像的结构。在这种情况下,每个客户都有一个时间维度(从他们开始与银行建立关系到 2016 年 5 月),最大 17 个像素,以及一个 24 个像素的产品维度。收入和与银行的关系等一些特征随着时间的推移发生了变化,所以我最终得到了一些 45×17 像素的低分辨率“图片”,这些图片捕捉了我需要了解的关于客户的所有信息。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/fc62c7723058a81ba1bb0db4eac6e8b5.png)

Two samples of customer pictures plotted with nipy_spectral colormap

你可能想要设计的一些特征,比如“产品的上个月指数”,“到上个月为止指数从 0 到 1 的转换次数”或者“产品 A 和 B 一起使用的频率”——如果它们有用,它们将成为 CNN 检测的图像中的模式。这最棒的部分是什么?我不需要提出什么特征转换可能有用的假设,我可以去喝杯茶,同时算法勤奋地工作以发现有用的特征组合,其中一些可能超出我的想象。我确实提出了一个假设——如果一个顾客改变了他/她的性别,那么这个顾客的行为和产品使用会因为荷尔蒙和社会互动的变化而有所不同。但是没人变性。相当令人失望。

# 模型

我使用 Keras 和 Tensorflow 后端来构建 CNN。该模型有两条路径——一条路径执行卷积/汇集/丢弃操作,然后连接到密集连接的神经网络,另一条路径只执行简单的密集连接的神经网络,因为某些功能不会随着时间的推移而改变,或者以结构化的方式(例如,任期)为特定客户而改变。该模型有大约 55K 个参数——如果与典型的 CNN 图像识别模型相比,这是一个小网络。我花了大部分时间调整模型的结构(层、节点),没有调整其他超参数,如辍学率、L1 和 L2 惩罚。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e1ac050b8328367e35501c3d97aa6108.png)

# 结果和收获

我建立的 CNN 模型在 1787 个团队中,在私人排行榜上排名第 26,在公共排行榜上排名第 22。比赛已经结束了,所以我的表演不会被放在那里。然而,这比我的预期好得多。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b56b5d73ce7913e87872740f69316734.png)

我可以做更多的调整和模型组合来进一步推动它,但这不是练习的重点。要点是,CNN 在对结构化银行数据建模时也非常有用!我担心,像我这样的银行定量分析师再也不能舒舒服服地坐在椅子上,认为深度学习不会扰乱我们的行业。这可能不会是明天,因为所有的监管审查,但没有理由认为一个自动化,高效和有效的算法不会至少与我们共存。毕竟,几十年前定量分析师和统计学家取代了承销商,因为承销的统计模型比当时的承销商更加自动化、高效和有效。

# 解读

到目前为止开发的用于解释 CNN 的工具倾向于针对图像问题。但由于它是一个神经网络,人们可以随时查看梯度,并了解为什么模型会做出某些预测。我们知道模型是可微分的,因此通过使用一阶[泰勒级数](https://en.wikipedia.org/wiki/Taylor_series)近似,我们可以用输入(在这种情况下,特定客户的特征值)周围的线性函数来近似模型,然后我们可以使用预测相对于输入的梯度来显示输入的变化如何影响预测。下面是一张预测信用卡使用的梯度“图片”,与客户的输入值有关。蓝色块表示负值,红色块表示正值。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/30196a7e3b564bd16104d4ec3f904f13.png)

亮红色块显示,如果客户在最近 2 个月使用了直接存款,他/她将更有可能在 2016 年 6 月使用信用卡。蓝色块显示,如果客户在 2016 年 5 月使用信用卡,他/她在 2016 年 6 月使用信用卡的可能性将大大降低。猜猜看,这是因为我手动将目标指标更改为 0,而前一个月的使用量为 1,因为 Santander 只想知道谁从不使用产品变为使用产品。事后看来,我应该使用原始指标,建立模型,并使用后处理规则来处理,因为手动调整成为模型需要学习的另一件事。然而,模型能够学习调整的事实表明模型正在做正确的事情。

# 最后的想法

我把这个展示给了当地的数据科学家社区,他们觉得非常有趣。提出的一个问题是为什么 CNN 而不是 RNN(循环神经网络)。毕竟,用 RNN 对时间序列数据建模更有意义。这场比赛的第 15 名获胜者似乎使用了 RNN 作为他们的算法。很难说 RNN 在这种情况下一定更好,因为我建立的 CNN 有点粗糙,我没有做很多调整和模型组合。然而,应该注意的是,时间相关性信息可能会在卷积和池化过滤器中丢失,因此 CNN 可能并不总是适用于时间序列问题。不过,与 RNN 模型相比,CNN 的优势在于相对容易训练。

# 卷积神经网络在游戏中驾驶车辆

> 原文:<https://towardsdatascience.com/convolutional-neural-network-to-steer-a-vehicle-inside-a-game-2aab41a5ef60?source=collection_archive---------11----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/1129e11396c6dd6837fd6febf06f2080.png)

大约两年前,英伟达发表了 [PilotNet 论文](https://arxiv.org/abs/1604.07316),以端到端的方式展示了自动驾驶汽车的卷积神经网络(CNN)。在这篇文章中,我想展示我是如何在游戏中使用这个架构来预测车辆的方向盘轴线的。我已经将转向角预测公式化为回归问题。

任何有监督的机器学习问题都是在有标签的数据(X,Y)上工作的。其中 **X** 是输入, **Y** 是输出。学习算法学习映射函数 Y = f(X)。在这个问题中,输入 X 是任意给定点的道路图像,输出 Y 是方向盘轴。

## 资料组

我从[这里](https://github.com/marsauto/europilot#sample-training-data)获得了大约 16 万张图像的数据集。该数据集是通过以 10fps 的速度驾驶大约 5 个小时,通过游戏杆捕捉游戏窗口和方向盘轴的截图而生成的。方向盘轴不是以度为单位,而是以操纵杆的不同刻度为单位,范围从-10k(左转)到 10k(右转)。原始图像大小为 1024 X 768 X 3 像素。这里有一些例子图片—

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/aa8ca5b8dbbccc806084d4fd95bddd24.png)

Driving in the daylight

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/6ba9309d83d62ffffe525494dd85e7b1.png)

Driving at the night

图像的大部分不是很有用,因此感兴趣区域(ROI)被裁剪并按比例缩小以获得尺寸为 200 x 66 x 3 的图像。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/ea9af98c10ca673d80a3e00961bf14ab.png)

ROI(200 x 66 x 3)

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/1c169a071710e486281d2918d9a8b8fc.png)

ROI(200 x 66 x 3)

这是数据准备的一个重要部分,因为裁剪确保了学习算法只关注 ROI,而不是整个图像,缩小比例确保了网络没有太多的输入参数。

整个数据被混洗,然后分成训练集(87%)和验证集(13%)。具有超参数调整的模型训练在训练集上完成,而验证集用于检查模型的最终准确性。洗牌确保了训练集和验证集都有来自所有不同驾驶条件的数据,如白天、夜晚、下雨等。

现在来看标签,这是方向盘轴的直方图。正值对应于右转的方向盘旋转量,反之亦然。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/5948ab4fcd7438bf847b5c7a977cecc4.png)

Distribution of steering wheel axis (labels) BEFORE Augmentation

急转弯的例子很少,大多数数据在-2500 到 2500 之间。此外,由于这种分布是正常的,均方差(MSE)损失(已用于回归)应该工作得很好。

此外,**数据通过水平翻转图像并因此翻转转向轴而得到增强**。

if(np.random.choice(2, 1)[0] == 1):
pil_img = pil_img.transpose(Image.FLIP_LEFT_RIGHT)
label = -1 * label # Changing the direction of wheel axis.


这几乎使训练数据翻倍。

## 网络体系结构

我选择了 Nvidia 著名的 [PilotNet 架构,并做了一些修改。这是最初的建筑](https://arxiv.org/abs/1604.07316)

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9493991c13c17befb114a0c13f5834c5.png)

Source [Nvidia blog](https://devblogs.nvidia.com/deep-learning-self-driving-cars/)

为了更快的训练,我在每一层之后增加了**【BN】**(在卷积层的信道轴上)。我也尝试使用**辍学**进行正规化,但这似乎并不影响准确性,因此删除了它。关于何时应用 BN 有许多[争论,BN](https://www.reddit.com/r/MachineLearning/comments/67gonq/d_batch_normalization_before_or_after_relu/)是在非线性之前还是在非线性之后。因为我在每一层使用了 **Relu 激活**,在 Relu 激活后应用 BN 是**增加了隐藏层激活的平均值并减少了方差**,因为 BN 没有考虑负面激活。因此,我在非线性**、**后应用 BN,结果很好。

我将输入图像缩小到 200 x 66(与 PilotNet 相同),以保持全连接层的参数较低(卷积层的参数不受影响)。这对于避免过度拟合非常重要。具有非常高的参数的模型具有高熵,并且它们倾向于过度拟合(即记忆训练集)。在低熵的情况下,梯度下降算法迫使模型学习数据中的重要模式,而不是记忆训练集。而参数很低也是不好的,因为模型可能什么也学不到。

可以通过使用最大池来避免参数的增加,但是它通常用于空间不变性,这在这种情况下是不期望的。

通过将输入像素除以 255 来归一化模型的输入。使用整个训练集的均值和方差来归一化输入图像有更好的方法,但这也很好。我使用没有任何正则化的均方误差损失。在验证集上测试了这些参数之后,我想出了所有这些设计选择。其余的网络参数保持不变。

这是我和`input_shape = (66, 200, 3)`一起使用的最终架构。


Layer (type) Output Shape Param #

conv2d_1 (Conv2D) (None, 31, 98, 24) 1824


batch_normalization_1 (Batch (None, 31, 98, 24) 96


conv2d_2 (Conv2D) (None, 14, 47, 36) 21636


batch_normalization_2 (Batch (None, 14, 47, 36) 144


conv2d_3 (Conv2D) (None, 5, 22, 48) 43248


batch_normalization_3 (Batch (None, 5, 22, 48) 192


conv2d_4 (Conv2D) (None, 3, 20, 64) 27712


batch_normalization_4 (Batch (None, 3, 20, 64) 256


conv2d_5 (Conv2D) (None, 1, 18, 64) 36928


batch_normalization_5 (Batch (None, 1, 18, 64) 256


flatten_1 (Flatten) (None, 1152) 0


dense_1 (Dense) (None, 100) 115300


batch_normalization_6 (Batch (None, 100) 400


dense_2 (Dense) (None, 50) 5050


batch_normalization_7 (Batch (None, 50) 200


dense_3 (Dense) (None, 10) 510


batch_normalization_8 (Batch (None, 10) 40


dense_4 (Dense) (None, 1) 11

Total params: 253,803
Trainable params: 253,011
Non-trainable params: 792


我使用 Keras 和 Tensorflow 后端进行所有的实验和最终训练。

## 培养

该数据集在相邻图像之间具有非常高的相关性,因此对训练进行洗牌是很重要的。与此同时,在每个时期之后,数据集被重新洗牌,这样每个批次在多个时期中都是唯一的。

total data: 162495, training set: 140800, validation set: 21695
batch_size: 128, train_steps: 1100, val_steps: 170


由于 keras [没有用于回归](https://github.com/keras-team/keras/issues/5152)任务的 `[flow_from_directory](https://github.com/keras-team/keras/issues/5152)` [,我不得不编写自己的带有数据扩充的 data_generator。](https://github.com/keras-team/keras/issues/5152)

INPUT_NORMALIZATION = 255.0
OUTPUT_NORMALIZATION = 655.35 #picked this number to compare results with data source model.
img_shape = (66, 200, 3)
batch_size = 128
def generator(df, batch_size):
img_list = df['img']
wheel_axis = df['wheel-axis']
# create an empty batch
batch_img = np.zeros((batch_size,) + img_shape)
batch_label = np.zeros((batch_size, 1))
index = 0 while True:
for i in range(batch_size):
label = wheel_axis.iloc[index]
img_name = img_list.iloc[index]
pil_img = image.load_img(path_to_data+img_name)
**# Data augmentation **
if(np.random.choice(2, 1)[0] == 1):
pil_img = pil_img.transpose(Image.FLIP_LEFT_RIGHT)
label = -1 * label
batch_img[i] = image.img_to_array(pil_img)
batch_label[i] = label
index += 1
if index == len(img_list):
#End of an epoch hence reshuffle
df = df.sample(frac=1).reset_index(drop=True)
img_list = df['img']
wheel_axis = df['wheel-axis']
index = 0
yield batch_img / INPUT_NORMALIZATION, (batch_label / OUTPUT_NORMALIZATION)


决定使用多大的迷你批次也很棘手。如果我们使用非常小的批量,计算的梯度可能会非常不准确,因此训练会有噪声。如果你选择一个非常大的批量,它可能不适合内存。我选择使用 128 作为迷你批次大小。

我使用具有动量和学习率衰减的随机梯度下降优化器来训练网络。

sgd = SGD(lr=1e-3, decay=1e-6, momentum=0.9, nesterov=True)


该模型在 CPU 上训练 41 个时期,持续约 30 小时,以实现 0.1166 的验证均方误差和 0.2429 的验证平均绝对误差(在第 36 个时期),这对应于 20k 标度上 160 (=0.2429 x 输出 _ 归一化)的平均误差。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8ee155da9de4767a8c862a4ec0140eff.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b02459ebc0caf4cba7c752c12b08f44f.png)

Validation MAE and MSE

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/6b5e22157f325ec4b522611da2c23791.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/777d5e0d1451191b6960b39d328c583b.png)

Training MAE and MSE

而训练批次规范化有助于更快的转换。与 20 个时期内没有批量标准化的架构相比,我能够在仅 9 个时期内通过批量标准化实现相同的 MSE 损失。

## 结果

以下是一些结果(提醒—负值表示左转,反之亦然)

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/43f59e9c75a5ab954f3afad0a1f19e9a.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/bc6214c3bcb78a77938b94e4c838b967.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9667447819e473d55bd0675883d2bacd.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/da571b1664f41cc2330d01c953517716.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b09656129034349bbff8795016a9c471.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/f4a2268bda620dbe2e6e4813c50a6585.png)

尽管急转弯的例子很少,但 model 仍然学会了这些模式。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8abd91d0a59e099f9fc4a0132febd172.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e44535de64893c29c12b3b635573beee.png)

Sharp Right Turns

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8d98c561eb35eefe5535c5d389c525f3.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/f4b86072907b8612b0474d05d3517c31.png)

Sharp Left Turns

有些情况下,模型比真实标签表现得更好。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/cef37c30f2f57fcf3152b821fdbf0c21.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/50cea1d9bbf06fba53e44a1003841504.png)

Model predictions are better than ground truth labels

在某些情况下,模型的表现不是很好。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/348b7ac1f9c413dd8a7511553241ffed.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b096f0c401766dfa21a3e1036db3d1c4.png)

Model prediction are not very good

这篇文章的重量和代码可以在 [my Github](https://github.com/aryarohit07/PilotNetSelfDrivingCar) 上找到。

## 结论

对于这个项目,一个基于 CNN 的小型架构似乎工作得很好。我使用了各种技术,如数据处理(裁剪 ROI、缩放输入大小)、数据扩充、批量标准化,仅用大约 5 小时的行驶数据就实现了相当好的验证损失。这可以通过使用不同的架构,如[CNN-RNN](http://cs231n.stanford.edu/reports/2017/pdfs/626.pdf)网络来获得更好的结果,并通过使用其他梯度下降优化器,如 Adam、Adadelta 等来进一步改善。

如果你喜欢读这篇文章,请鼓掌并与你的同事和朋友分享。谢谢!

还有,我们来连线一下[脸书](https://www.facebook.com/aryarohit07)、[推特](https://twitter.com/arya_rohit07)、 [Linkedin](https://in.linkedin.com/in/aryarohit07) 和 [Github](https://github.com/aryarohit07/) 。

# 全卷积神经网络|第一部分

> 原文:<https://towardsdatascience.com/convolutional-neural-networks-for-all-part-i-cdd282ee7947?source=collection_archive---------5----------------------->

## 导师指导的学习指南,以应对 Coursera 深度学习专业化课程 4

Coursera 深度学习专业化课程的前三门课程是可以忍受的艰难,但接下来是 T2 课程第四门。这么多伟大的主题和概念!但是无数次停止视频、做笔记和重新观看讲座让我们,一群官方导师,决定学习指南值得努力。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/3c47e6005836ca7f6d5e3d7f02c029cc.png)

Let’s walk this CNN tour until Neural Style Transfer together.

本学习指南三部曲的第一部分回顾了本课程涵盖的广泛概念。什么是卷积神经网络,YOLO 实际上是如何工作的?[第二部分](https://medium.com/machine-learning-world/convolutional-neural-networks-for-all-part-ii-b4cb41d424fd)总结了每一次演讲,并深入解释了顶层概念。第三部分提供了一个 deeplearning.ai 字典,帮助你整理 ng 大师的缩略语、专业术语和偶尔的笑话。

先把 CNN 课程最有意思的概念一个个分解一下。

# 卷积神经网络

## 什么是卷积神经网络?

卷积神经网络(CNN)是计算机视觉的首要深度学习模型。计算机视觉已经变得如此之好,以至于它目前在某些任务上击败了人类,例如[识别猫和狗的品种](http://www.nytimes.com/2012/06/26/technology/in-a-big-network-of-computers-evidence-of-machine-learning.html),CNN 在这个成功故事中发挥了重要作用。如果你有一个涉及计算机视觉的任务,让它识别人脸或物体,CNN 是首选模型。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0e7de8a67e0c197106c87d417e45b499.png)

Convolving the pixels of an image.

## CNN 是如何工作的?

CNN 用于通过卷积来评估输入。输入与过滤器进行卷积,如上面的 *gif* 所示。这种卷积导致网络检测网络中较早层中的边缘和较低级别的特征以及较深层中更复杂的特征。CNN 与池层结合使用,它们通常在最后有完全连接的层,如下图所示。像在普通神经网络中一样运行前向传播,并通过反向传播最小化损失函数来训练 CNN。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/fff293357945e7f1d58d1fca7db08cbd.png)

Simple CNN architecture. Source: [https://goo.gl/dVSMQo](https://goo.gl/dVSMQo)

某些架构,如 ResNets 或 InceptionNets,可以加速 CNN 的训练。处理大量的图像和训练权重需要时间,因为有太多的联系。幸运的是,许多伟大的 CNN 已经被训练出来,像 [ImageNet](http://www.image-net.org/) 或 [VGG](http://www.robots.ox.ac.uk/~vgg/practicals/cnn/index.html) ,你可以重用他们的模型。吴恩达的建议是,在现有 CNN 架构和预训练模型的基础上使用迁移学习,快速开始你的计算机视觉任务。

# **通过 YOLO 探测物体**

## 什么是 YOLO?

YOLO 是一种也实时工作的多目标检测算法。图片[自动驾驶汽车在行驶中需要识别汽车、行人、红绿灯](https://www.youtube.com/watch?v=OksuVuNY5o0)或者[简单标注一部电影](https://www.youtube.com/watch?v=VOC3huqHrss)。YOLO 非常快,因为“你只看一次”,这意味着你运行一个单一的前向传播步骤,你立即知道一个对象在图像中的确切位置和这个对象属于哪个类。作为顶端的樱桃,YOLO 能够检测图像中的多个对象。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b6b5f34fa72b5eb90d5b5e0635a7e4a0.png)

Output of YOLO object detection. Source: [https://goo.gl/yefAZa](https://goo.gl/yefAZa)

## YOLO 是如何工作的?

要使用 YOLO 训练 CNN,首先必须在训练图像上放置一个网格,例如形状为 3×3 的网格。接下来,为每个格网创建输出标签。在每个网格中的对象周围绘制一个边界框,并相应地标记输出向量。给尽可能多的图像加标签。CNN 的最后一层具有来自输入图像的网格单元的形状,宽度和高度以及与单个输出向量中的元素数量一样多的通道。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/722273f7578a0cff076f37d1479fdc8e.png)

How to label your training set for YOLO.

反向传播调整 CNN 的权重,以便它学习识别对象。您可以使用非最大抑制来确定对象的最佳边界框。如果在同一个网格单元中遇到多个对象重叠,可以使用定位框来分隔这些对象。这些细节在[第二部分](https://medium.com/machine-learning-world/convolutional-neural-networks-for-all-part-ii-b4cb41d424fd)中有更详细的解释。

# **人脸识别**

## 什么是人脸识别?

人脸识别用于根据人脸图像来识别一个人。虽然人脸验证是根据人脸来验证一个人是否是他们声称的那个人,但人脸识别要复杂得多,因为您要尝试将那个人的脸与人脸图像数据库进行匹配。此外,你经常必须通过一次性学习来识别一个人,这意味着你必须根据一张图像来识别她,并检查它是否与数据库中的任何图像足够相似——相当困难!

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/a5ed1e9a0a1885bf17fd15864bf3a741.png)

Their faces are detected! Now we can check if these faces belong to people who are allowed to be there. Source: [https://goo.gl/ubrq3H](https://goo.gl/ubrq3H)

## 人脸识别是如何工作的?

你的目标是学习一个相似性函数,例如三重损失。相似性函数旨在检测不同组图像上的人是否相同。三元组损失函数需要三个图像来计算相似性:一个锚、那个人的正面和负面例子。三元组损失函数基于锚图像调整权重以最大化正图像和负图像之间的差异。基于三重损失的输出,CNN 决定是否识别该人。确保使用 hard 为相似性函数训练图像。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/5247665a2f7a7ba16221c4ce392c078c.png)

Triplet loss learns that the similarity between two images of Jen Aniston should be bigger than between Jen Aniston and LL Cool J. Source: [https://goo.gl/hWn8jJ](https://goo.gl/hWn8jJ)

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/61e674c7e747138309fece2abcc061ef.png)

Good example of a difficult to train image triplet for the similarity function.

# **神经风格转移**

## 这是什么?

神经类型转移是一个有趣的应用程序,将提高您对 CNN 的理解。本质上,你试图生成一个新的图像,将一个图像的内容与另一个图像的风格结合起来,比如说来自一个受欢迎的艺术家。你想知道毕加索会怎么画你吗?去吧,用神经风格转移自己尝试一下!

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c9be807f4be3cb2135fd1a7c3c8551b7.png)

Van Gogh’s Starry Night applied to a GIF.

## 神经风格转移是如何工作的?

在神经风格转移中,您从生成的图像 *G* 开始,它包含随机像素值,如下所示。接下来,您定义一个内容图像 *C* 和一个样式图像 *S* ,您想要组合它们。您的目标是调整 *G* 中的像素值,以便 *G* 变得与 *C* 和 *S* 相似。为此,您需要定义成本函数 *J(C)* 和 *J(S)* ,并尝试最小化这两个函数。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0ce68c87f3a25af524c548ee4c782aba.png)

Random pixel values to start NST.

*J(C)* 确保 *G* 看起来与 *C* 中的内容相似。你知道,CNN 学习识别较低级别的特征,如早期隐藏层中的边缘,以及更复杂的特征,如后期隐藏层中的人脸。在 CNN 中间选择一个隐藏层,用 *C* 和 *G* 进行正向传播。接下来,比较两幅图像的激活值,并通过反向传播尽量减小激活值之间的差异。

接下来,你还必须调整 *G* 的样式,以匹配 *S* 中的样式。最小化 *J(S)* 的关键是调整 *G* 中通道激活之间的相关性,以匹配 *S* 。你可以通过计算 *S* 和 *G* 的 Gram 矩阵来实现。Gram 矩阵计算每一对可能的滤波器组合。接下来,你选择 CNN 中间的一层,再次为 *S* 和 *G* 运行前向传播。 *J(S)* 通过反向传播最小化 gram 矩阵之间的差异,并使 *G* 看起来更类似于 *S* 。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c62a782941b718f8100a5f623e8fe3ce.png)

Beautiful, historic Strausberger Platz in Berlin combined with Van Gogh’s Starry Night through NST.

最酷的是,神经网络学会了调整像素值,而不仅仅是权重!这是一种非常直观的研究和理解 CNN 的方式,我鼓励你创建自己的神经风格传输图像。

声明:所有功劳归于 [deeplearning.ai](http://deeplearning.ai/) 。虽然我是一名导师,但我只是总结和重新表述内容,以帮助学习者进步。

第一部分是一个包,关到第二部分和第三部分的[。如果你认为这篇文章是有帮助的,不要忘记展示你的💛穿过👏 👏 👏并关注我,听更多关于深度学习、在线课程、自动驾驶汽车、生活的文章。还有,](https://medium.com/machine-learning-world/convolutional-neural-networks-for-all-part-ii-b4cb41d424fd)[查一下](https://medium.com/machine-learning-world/netflix-or-coursera-how-to-finish-andrew-ngs-1st-deep-learning-course-in-7-days-6fa293ee83d8) [这些](/https-medium-com-janzawadzki-applying-andrew-ngs-1st-deep-neural-network-on-the-titanic-survival-data-set-b77edbc83816) [关于深度学习专精的帖子](/structuring-your-machine-learning-project-course-summary-in-1-picture-and-22-nuggets-of-wisdom-95b051a6c9dd)。请评论分享你的看法。干杯!🙇

# 卷积神经网络

> 原文:<https://towardsdatascience.com/convolutional-neural-networks-from-the-ground-up-c67bb41454e1?source=collection_archive---------2----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/1e626f893c1c9d7502403835f8a8abae.png)

Visualization of a convolutional neural network. [image source](https://www.kdnuggets.com/2018/02/8-neural-network-architectures-machine-learning-researchers-need-learn.html)

## 著名的卷积神经网络的 NumPy 实现:迄今为止最有影响力的神经网络架构之一。

当 Yann LeCun 发表了他关于一种新的神经网络体系结构[1]卷积神经网络(CNN)的研究成果时,他的工作很大程度上没有引起人们的注意。在 2012 年 ImageNet 计算机视觉竞赛期间,多伦多大学的一组研究人员花了 14 年时间将 CNN 带入公众视野。他们的参赛作品以首席建筑师 Alex Krizhevsky 的名字命名为 AlexNet,在对来自数千个类别的数百万张图像进行分类时,误差仅为 15.8%。快进到 2018 年,当前最先进的卷积神经网络实现了超过人类水平性能的精度[3]。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9db821b129908837c096779ac5cddcc4.png)

ImageNet top-5 error 2010–2015\. [source](https://devblogs.nvidia.com/mocha-jl-deep-learning-julia/)

在这些有希望的结果的激励下,我开始了解 CNN 的功能,以及它们是如何表现得如此出色的。正如理查德·费曼指出的那样,*“我不能构建的,我不理解”*,因此为了全面了解人工智能的这一进步,我在 NumPy 中从头开始构建了一个卷积神经网络。完成这个项目后,我觉得卷积神经网络看起来有多复杂,和它们实际上有多复杂之间有一个脱节。希望你从零开始建立自己的网络后,也能分享这种感觉。

这个项目的代码可以在[这里](https://github.com/Alescontrela/Numpy-CNN)找到。

# 挑战

CNN 以其识别图像中存在的模式的能力而闻名,所以这篇文章中描述的网络的任务是图像分类。衡量计算机视觉算法表现如何的最常见基准之一是在 [MNIST 手写数字数据库](http://yann.lecun.com/exdb/mnist/)上训练它:一个 7 万个手写数字及其相应标签的集合。目标是训练 CNN 在标记手写数字(范围从 0 到 9)时尽可能准确。经过大约五个小时的训练和训练集上的两次循环,这里介绍的网络能够在测试数据上达到 98%的准确率,这意味着它可以正确地猜出几乎每个显示给它的手写数字。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8e3c1db38cb200589ae8bea8d7d51480.png)

Example of digits from the MNIST dataset. [image source](https://codeburst.io/use-tensorflow-dnnclassifier-estimator-to-classify-mnist-dataset-a7222bf9f940?gi=4a1246898237)

让我们来看一下构成网络的各个组件,以及它们如何链接在一起以根据输入数据形成预测。解释完每个组件后,我们将对其功能进行编码。在这篇文章的最后一部分,我们将使用 NumPy(这里的代码[是](https://github.com/Alescontrela/Numpy-CNN))对网络的每一部分进行编程和训练。值得注意的是,本节假设至少具备线性代数和微积分的工作知识,并且熟悉 Python 编程语言。如果你对这些领域不熟悉或者需要调整,请查看[这份出版物](https://arxiv.org/pdf/1802.01528.pdf)以了解机器学习领域的线性代数,以及[这份资源](https://www.codecademy.com/learn/learn-python)以开始用 Python 编程。事不宜迟,我们开始吧。

# 卷积神经网络如何学习

## 回旋

CNN 利用**过滤器**(也称为内核),来检测图像中存在哪些特征,比如边缘。过滤器只是一个称为权重的值矩阵,用于检测特定的特征。过滤器在图像的每个部分上移动,以检查它想要检测的特征是否存在。为了提供表示特定特征存在的置信度的值,滤波器执行**卷积运算**,这是两个矩阵之间的逐元素积和。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/fe29c8b39d7c899f742c90b2b83ea2d8.png)

[image source](http://technodocbox.com/3D_Graphics/70716176-Deep-neural-networks-applications-in-handwriting-recognition.html)

当特征出现在图像的一部分中时,滤波器和图像的该部分之间的卷积运算产生具有高值的实数。如果该特征不存在,则结果值较低。

在下面的示例中,负责检查右侧曲线的滤镜会经过图像的一部分。由于图像的该部分包含过滤器正在寻找的相同曲线,卷积运算的结果是一个大的数字(6600)。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/89108a44e1646a3937b1d3342d88382e.png)

[image source](https://adeshpande3.github.io/A-Beginner%27s-Guide-To-Understanding-Convolutional-Neural-Networks/)

但是,当相同的滤波器通过图像的一部分时,具有相当不同的一组边缘,卷积的输出很小,这意味着没有强烈的右手曲线。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/007472c70ba6ae08315ba4b8f4641142.png)

[image source](https://adeshpande3.github.io/A-Beginner%27s-Guide-To-Understanding-Convolutional-Neural-Networks/)

在整个图像上传递该滤波器的结果是输出矩阵,该矩阵存储该滤波器在图像的各个部分上的卷积。滤波器的通道数必须与输入图像的通道数相同,这样才能进行逐元素乘法。例如,如果输入图像包含三个通道(例如 RGB),则滤镜也必须包含三个通道。

2D 图像上滤波器的卷积;

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/f8a3b202e4265e39abf4c39943957d8f.png)

[Introduction to Convolutional Neural Networks](https://rubikscode.net/2018/02/26/introduction-to-convolutional-neural-networks/) by rubikscode

此外,使用**步距**值,滤波器可以以不同的间隔在输入图像上滑动。步幅值由过滤器在每一步应该移动的量决定。步进卷积的输出维度可通过以下公式计算:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b12056eec0b22b974721997b11d2d079.png)

其中`n_in`表示输入图像的尺寸,`f`表示窗口大小,`s`表示步幅。

为了使卷积神经网络能够学习检测输入数据中存在的特征的滤波器的值,滤波器必须通过非线性映射。滤波器和输入图像之间的卷积运算的输出与偏置项相加,并通过**非线性激活函数**。激活函数的目的是将非线性引入我们的网络。由于我们的输入数据是非线性的(对形成手写签名的像素进行线性建模是不可行的),我们的模型需要考虑这一点。为此,我们使用整流线性单元(ReLU)激活功能:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/35556070badea0b30aac16e9a241d0ac.png)

[image source](https://medium.com/@kanchansarkar/relu-not-a-differentiable-function-why-used-in-gradient-based-optimization-7fef3a4cecec)

可以看到,ReLU 函数相当简单;小于或等于零的值变为零,所有正值保持不变。

通常,网络每层使用多个过滤器。在这种情况下,输入图像上每个滤波器卷积的输出沿最后一个轴连接,形成最终的 3D 输出。

**代码**

使用 NumPy,我们可以很容易地对卷积运算进行编程。卷积函数利用 for 循环对图像上的所有滤波器进行卷积。在 for 循环的每次迭代中,使用两个 while 循环在图像上传递过滤器。在每一步,滤波器与输入图像的一部分按元素相乘(`*`)。然后,使用 NumPy 的 [sum](https://www.google.com/search?q=numpy+sum&rlz=1C5CHFA_enUS741US741&oq=numpy+sum&aqs=chrome..69i57j0l5.1102j0j4&sourceid=chrome&ie=UTF-8) 方法对这种逐元素乘法的结果求和以获得单个值,然后加上偏置项。

convolution operation

使用标准正态分布初始化`filt`输入,并且`bias`被初始化为零向量。

在一个或两个卷积层之后,通常减少由卷积层产生的表示的大小。这种表示尺寸的减小被称为**缩减采样。**

## 向下采样

为了加快训练过程并减少网络消耗的内存量,我们尝试减少输入要素中存在的冗余。有几种方法可以对图像进行缩减采样,但在这篇文章中,我们将看看最常用的一种:**最大池**。

在 max pooling 中,窗口根据设定的步幅(每次移动多少个单位)通过图像。在每一步,窗口内的最大值被汇集到输出矩阵中,因此命名为最大汇集。

在下图中,大小为 f=2 的窗口以 2 的步幅通过一幅图像。f 表示最大池窗口(红框)的尺寸,s 表示窗口在 x 和 y 方向移动的单位数。在每一步,选择窗口内的最大值:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8f9ab333da58c6b38f99818d1bd49ce0.png)

[image source](https://zhuanlan.zhihu.com/p/32442184)

最大池显著减小了制图表达的大小,从而减少了所需的内存量以及稍后在网络中执行的操作数量。最大池化操作的输出大小可以使用以下公式计算:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b12056eec0b22b974721997b11d2d079.png)

其中`n_in`表示输入图像的尺寸,`f`表示窗口大小,`s`表示步幅。

max pooling 的另一个好处是,它迫使网络专注于少数几个神经元,而不是所有神经元,这对网络具有正则化效果,使其不太可能过度拟合训练数据,并有望很好地推广。

**代码**

最大池操作归结为一个 for 循环和几个 while 循环。for 循环用于遍历输入图像的每一层,而 while 循环用于在图像的每一部分滑动窗口。在每一步,我们使用 NumPy 的 [max](https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.maximum.html) 方法来获得最大值:

max pooling operation

在多个卷积层和下采样操作之后,3D 图像表示被转换成特征向量,该特征向量被传递到多层感知器,该感知器仅仅是具有至少三层的神经网络。这被称为**全连接层。**

## 全连接层

在神经网络的全连接操作中,输入表示被展平成特征向量,并通过神经元网络来预测输出概率。下图描述了拼合操作:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e785da865dd0feb2961473cfde3f67ec.png)

[image source](https://rubikscode.net/2018/02/26/introduction-to-convolutional-neural-networks/)

这些行被连接起来形成一个长特征向量。如果存在多个输入图层,其行也会被连接起来形成一个更长的特征向量。

然后,特征向量通过多个密集层。在每个密集层,特征向量乘以层的权重,与其偏差相加,并通过非线性。

下图显示了完全连接的操作和密集图层:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/74c8116a674b47df91f00b16f5cbf0f9.png)

[image source](https://cambridgespark.com/content/tutorials/convolutional-neural-networks-with-keras/index.html)

值得注意的是,[根据 Yann LeCun](https://www.facebook.com/yann.lecun/posts/10152820758292143) 的《脸书邮报》,“不存在完全连接的层”,他是对的。当回想卷积层时,人们意识到全连接层是具有 1x1 输出内核的卷积运算。也就是说,如果我们在 n 乘 n 维的图像上通过 128 个 n 乘 n 滤波器,我们将得到长度为 128 的向量。

**代码**

NumPy 使得对 CNN 的全连接层进行编程变得非常简单。事实上,您可以使用 NumPy 的 [reshape](https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.reshape.html) 方法在一行代码中完成:

fully connected

在这个代码片段中,我们收集了前一层的尺寸(通道数和高度/宽度),然后使用它们将前一层展平为完全连接的层。这个完全连接的层由多个**密集的神经元层**处理,最终产生原始预测:

dense layers

## 输出层

CNN 的输出层负责在给定输入图像的情况下产生每个类别(每个数字)的概率。为了获得这些概率,我们初始化最终的密集层,使其包含与类别数量相同的神经元。然后,该密集层的输出通过 **Softmax 激活函数**,该函数将所有最终的密集层输出映射到一个向量,该向量的元素总和为 1:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/cd3b1b11c2d0d155af4ada710a01ba83.png)

其中 x 表示最终层输出中的每个元素。

**代码**

同样,softmax 函数可以用几行简单的代码编写:

softmax activation function

## 计算损失

为了测量我们的网络从输入图像中预测手写数字的准确性,我们使用了一个**损失函数**。当预测输出数字时,损失函数分配一个实数值来定义模型的准确性。预测多个输出类时常用的损失函数是**分类交叉熵损失函数,**定义如下:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/12a1ec77356c9d797726fa4bdac0bcda.png)

这里,ŷ是 CNN 的预测,y 是期望的输出标签。当对多个示例进行预测时,我们取所有示例的平均损失。

**代码**

分类交叉熵损失函数可以使用两行简单的代码轻松编程,这是上面所示等式的镜像:

categorical cross-entropy loss

这大概就是构成卷积神经网络的所有操作。让我们加入这些行动来构建 CNN。

# 网络

给定相对低数量的类(总共 10 个)和每个训练图像的小尺寸(28×28 像素)。),选择了一个简单的网络结构来解决数字识别的任务。该网络使用两个连续的卷积层,然后进行最大池操作,以从输入图像中提取特征。在最大池操作之后,表示被展平并通过多层感知器(MLP)来执行分类任务。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e0bc525c6c87330b181179265c8cb17b.png)

Network Architecture

# 为 CNN 编程

现在,我们已经讨论了形成卷积神经网络的基本操作,让我们来创建它。

在跟进时,请随意使用[这个回购](https://github.com/Alescontrela/Numpy-CNN)。

## 步骤 1:获取数据

MNIST 手写数字训练和测试数据可以在[这里](http://yann.lecun.com/exdb/mnist/)获得。这些文件将图像和标签数据存储为张量,因此必须通过它们的字节流读取这些文件。我们定义了两个助手方法来执行提取:

extract train and test data

## 步骤 2:初始化参数

我们首先定义初始化卷积层的滤波器和密集层的权重的方法。为了使训练过程更平滑,我们用平均值 0 和标准偏差 1 初始化每个滤波器。

methods to initialize the network’s parameters

## 步骤 3:定义反向传播操作

为了计算将迫使网络更新其权重并优化其目标的梯度,我们需要定义通过卷积和最大池层反向传播梯度的方法。为了保持这篇帖子(相对)简短,我不会深入这些梯度的推导,但是,如果你想让我写一篇描述通过卷积神经网络反向传播的帖子,请在下面留下评论。

## 步骤 4:构建网络

在精神抽象中,我们现在定义了一种结合卷积神经网络的向前和向后操作的方法。它将网络的参数和超参数作为输入,并输出梯度:

convolutional neural network forward and backward operation

## 步骤 5:训练网络

为了有效地迫使网络参数学习有意义的表示,我们使用了**亚当优化算法**。关于这个算法,我不会讲太多细节,但可以这样想:如果随机梯度下降是一个喝醉的大学生跌跌撞撞地下山,那么亚当就是一个滚下同一座山的保龄球。更好的解释亚当发现[这里](https://machinelearningmastery.com/adam-optimization-algorithm-for-deep-learning/)。

training the network with Adam optimization

这就概括了网络的发展。要在本地训练它,下载[这个 repo](https://github.com/Alescontrela/Numpy-CNN) 并在终端中运行以下命令:

$ python3 train_cnn.py '<file_name>.pkl'


用您喜欢的任何文件名替换`<file_name>`。终端应显示以下进度条,显示培训进度以及当前培训批次的费用。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/7680b920b9d4c182f756ef5f3de05bf0.png)

a training process with a (very) long way to go

CNN 完成训练后,一个包含网络参数的. pkl 文件被保存到运行脚本的目录中。

在我的 macbook pro 上训练网络大约需要 5 个小时。我在 GitHub repo 中以`params.pkl`的名字包含了经过训练的参数。要使用它们,用`params.pkl`替换`<file_name>`。

要测量网络的准确性,请在终端中运行以下命令:

$ python3 measure_performance.py '<file_name>.pkl'


此命令将使用定型参数对测试数据集中的所有 10,000 位数字运行预测。完成所有预测后,显示网络精度的值将出现在命令提示符中:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/ff64bf194dc600e96386ee2d15aa33b0.png)

network performance

如果您遇到任何关于依赖关系的问题,可以使用以下命令来安装所需的软件包:

$ pip install -r requirements.txt


# 结果

在对训练集进行两次运算后,网络在测试集上的准确率平均为 98%,我认为这相当不错。在将训练时间延长 2-3 个时期后,我发现测试集的性能下降了。我推测,在第三到第四次训练循环中,网络开始过度适应训练集,不再泛化。

因为我们传递的是成批的数据,网络必须考虑到每一批数据的可变性,这就是为什么在培训期间成本波动如此之大:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/cdcbef6364bce20f5b6cffa87ad3dffd.png)

cost vs. number of iterations

此外,我们测量网络的**回忆**,以了解它能够多好地预测每个数字。回忆是对准确性的一种衡量,它可以通过下面的例子来理解:在我们的测试集中所有标记为“7”(或任何其他数字)的数字中,我们的网络正确预测了多少?

下面的条形图显示了每个数字的召回:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/41661a96cc0611bb980520d336e17ba0.png)

这表明我们的网络学习了所有数字的有意义的表示。总的来说,CNN 概括得很好。

# 结论

希望这篇文章能让你对卷积神经网络有更深入的了解,甚至消除它们的复杂性。如果您有任何问题或想了解更多,请在下面留言:)

## 参考

[1]: Lecun,y .等,“基于梯度的学习应用于文档识别”美国电气和电子工程师学会会议录,第 86 卷,第 11 期,1998 年,第 2278-2324 页。,doi:10.1109/5.726791。

[2]: Krizhevsky,Alex 等,“用深度卷积神经网络进行 ImageNet 分类”*《美国计算机学会通讯*》,第 60 卷,2017 年第 6 期,第 84–90 页。,doi:10.1145/3065386。

[3]:何,,等,“深入挖掘整流器:在 ImageNet 分类上超越人类水平的性能” *2015 年 IEEE 计算机视觉国际会议(ICCV)* ,2015,doi:10.1109/iccv.2015.123

# 卷积神经网络:生物启发模型

> 原文:<https://towardsdatascience.com/convolutional-neural-networks-the-biologically-inspired-model-f2d23a301f71?source=collection_archive---------3----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c4a109debeaabb86e9c143f2fec8eb44.png)

你能认出上图中的人吗?如果你是一个科幻迷,你会立刻认出他们是 J.K .罗琳的全球现象哈利波特系列丛书中的哈利、罗恩和赫敏。这张照片是《哈利·波特与死亡圣器》第一部的一个场景——哈利、罗恩和赫敏正在审问小偷蒙顿格斯·弗莱奇,他们不久前被小精灵多比和克利切抓住了。嗯,我知道这一点,因为我已经看过这部电影和这本书很多次了!但是,怎样才能让计算机像你我一样理解这幅图像呢?让我们明确地思考一下,要使它有意义,所有的知识都必须到位:

*   你认出这是一群人的图像,并且明白他们在一个房间里。
*   你认出那里有成堆的报纸和一张带椅子的长木桌,所以位置很可能是厨房或普通客厅。
*   你从构成哈利·波特脸上眼镜的几个像素认出了他。他也有一头黑发,这很有帮助。
*   同样,你认出罗恩·韦斯莱是因为他的红头发,赫敏·格兰杰是因为她的长发。
*   你认出了另一个秃顶、穿着老式服装的男人,这表明他要老得多。
*   你认出了另外两个不是人类的生物(多比和克利切),即使你不熟悉它们。除了你对正常人长相的了解之外,你还利用了他们的身高、面部结构、身体尺寸。
*   哈利、罗恩和赫敏正在审问那个光头男人。你得出这个结论是因为你知道他们的身体姿势正对着他,你感觉到他们的视力出现了疑问,你还看到赫敏手里拿着一根魔杖(魔杖是哈利波特魔法世界中的魔法武器)。
*   你知道秃头男人感到害怕。鉴于他双手捂胸,你明白他在隐瞒什么。你开始思考这个场景后几秒钟即将展开的事件的含义,并对将会揭露什么秘密感到好奇。
*   这两个生物正看着哈利和罗恩。看起来,他们想说些什么。换句话说,你是在推理那些生物的精神状态。哇,你会读心术!

我可以继续,但这里的要点是,当你看照片时,你在那一秒钟使用了大量的信息。关于场景的 2D 和 3D 结构的信息,视觉元素,如人的身份、他们的行为,甚至他们的想法。你思考场景的动态,并猜测接下来会发生什么。所有这些因素加在一起,你就能理解这个场景。

令人难以置信的是,人类的大脑是如何展开一幅仅由 R、G、B 值组成的图像的。电脑怎么样?我们如何开始编写一个算法,像我上面做的那样对场景进行推理?我们怎样才能得到正确的数据来支持我们的推论呢?

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/fd72f0094f6a9e08c341c174a958a2d0.png)

*(Source:* [*https://medium.com/@alonbonder/ces-2018-computer-vision-takes-center-stage-9abca8a2546d*](https://medium.com/@alonbonder/ces-2018-computer-vision-takes-center-stage-9abca8a2546d)*)*

随着时间的推移,机器学习研究人员广泛关注对象检测问题,计算机视觉领域解决了这个问题。有各种各样的东西使识别物体变得困难:图像分割/变形、照明、启示、视点、巨大的尺寸等等。特别是,计算机视觉研究人员通过将许多简单的神经元链接在一起,使用神经网络来解决复杂的对象识别问题。在传统的前馈神经网络中,图像被输入到网络中,神经元处理图像并将它们分类为真和假可能性的输出。听起来很简单,不是吗?

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/07ddcd556bf6e4429d0a1c23bdd16126.png)

*(Source:* [*https://www.simplicity.be/article/recognizing-handwritten-digits/*](https://www.simplicity.be/article/recognizing-handwritten-digits/)*)*

但是如果图像变形了,就像上面的数字一样,怎么办?前馈神经网络仅在数字位于图像的正中间时工作良好,但是当数字稍微偏离位置时会严重失败。换句话说,网络只知道一种模式。这在现实世界中显然是没有用的,因为真实的数据集总是脏的和未经处理的。因此,在输入图像不完美的情况下,我们需要改进我们的神经网络。

谢天谢地,卷积神经网络来了!

## 卷积过程

那么卷积神经网络到底是什么?据谷歌大脑的研究科学家克里斯·奥拉赫称:

最基本的,卷积神经网络可以被认为是一种使用相同神经元的许多相同副本的神经网络。这使得网络可以拥有大量神经元,并表达计算量大的模型,同时保持需要学习的实际参数(描述神经元行为的值)数量相当少。”

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9577f8be383b53a58de9826039d64298.png)

*(Source: A 2D CNN* [— *http://colah.github.io/posts/2014-07-Conv-Nets-Modular/*](http://colah.github.io/posts/2014-07-Conv-Nets-Modular/)*)*

注意这里使用的术语:**同一个神经元的完全相同的副本。**这大致基于人类大脑的工作过程。通过使用相同的大脑记忆点,人类可以发现和识别模式,而不必重新学习概念。例如,无论我们从哪个角度看,我们都能认出上面的数字。前馈神经网络做不到这一点。但是卷积神经网络可以,因为它理解*平移不变性*——它将一个对象识别为一个对象,即使它的外观以某种方式变化。

简单来说,卷积过程是这样工作的:

*   首先,CNN 使用滑动窗口搜索将图像分成重叠的图像块。
*   然后,CNN 将每个图像块输入一个小的神经网络,对每个图像块使用相同的权重。
*   然后,CNN 将每个图块的结果保存到一个新的输出数组中。
*   之后,CNN 对输出数组进行降采样以减小其大小。
*   最后但同样重要的是,在将一幅大图像缩小成一个小数组后,CNN 预测该图像是否匹配。

有一篇由 Adam Geitgey 撰写的精彩教程,详细介绍了卷积过程的工作原理。我绝对建议你去看看。

**历史背景**

CNN 的普及主要归功于 [Yann LeCun](http://yann.lecun.com/) 的努力,他现在是脸书人工智能研究的主任。20 世纪 90 年代初,LeCun 在当时世界上最负盛名的研究实验室之一贝尔实验室工作,并建立了一个用于读取手写数字的支票识别系统。在 1993 年有一个非常酷的视频,LeCun 展示了这个系统是如何工作的。这个系统实际上是一个完整的端到端的图像识别过程。他在 1998 年与 Leon Bottou、Patrick Haffner 和 Yoshua Bengio 合著的论文[介绍了卷积网络以及他们构建的完整的端到端系统。这是一篇相当长的论文,所以我在这里简单总结一下。前半部分描述了卷积网络,展示了它的实现,并提到了与该技术相关的所有内容(我将在下面的 CNN 架构一节中介绍)。第二部分展示了如何将卷积网络与语言模型集成在一起。例如,当你阅读一段英语文本时,你可以在英语语法的基础上建立一个系统来提取该语言中最可能的解释。最重要的是,你可以建立一个 CNN 系统,并训练它同时进行识别和分割,并为语言模型提供正确的输入。](http://yann.lecun.com/exdb/publis/pdf/lecun-01a.pdf)

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/f85ad2fb6e012be86daf8aa1c034b1b8.png)

*(Source:* [*https://www.wired.com/2014/08/deep-learning-yann-lecun/*](https://www.wired.com/2014/08/deep-learning-yann-lecun/)*)*

## CNN 架构

让我们讨论卷积神经网络的架构。我们正在处理一个输入图像。我们执行一系列卷积+池操作,然后是一些完全连接的层。如果我们正在执行多类分类,输出是 softmax。在每个 CNN 中有 4 个基本构件:卷积层、非线性(CNN 层中使用的 ReLU 激活)、池层和全连接层。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/d88e6088c3372365753225eff5103e49.png)

*(Source:* [*https://towardsdatascience.com/applied-deep-learning-part-4-convolutional-neural-networks-584bc134c1e2*](/applied-deep-learning-part-4-convolutional-neural-networks-584bc134c1e2)*)*

**1 —卷积层**

这里我们从输入图像中提取特征:

*   我们通过使用输入数据的小方块学习图像特征来保持像素之间的空间关系。这些输入数据的方块也被称为*滤波器*或*内核。*
*   通过在图像上滑动过滤器并计算点积形成的矩阵被称为*特征图*。过滤器数量越多,提取的图像特征就越多,我们的网络就能更好地识别看不见的图像中的模式。
*   我们的特征图的大小由*深度*(使用的过滤器数量)、*步幅*(滑过输入矩阵的像素数量)和*零填充*(在边界周围用 0 填充输入矩阵)控制。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/70871dc5f350c0ec1109c18cdedd8b5b.png)

*(Source:* [*https://www.jessicayung.com/explaining-tensorflow-code-for-a-convolutional-neural-network/*](https://www.jessicayung.com/explaining-tensorflow-code-for-a-convolutional-neural-network/)*)*

**2 —非线性:**

对于任何一种强大的神经网络,它都需要包含非线性。LeNet 使用 *Sigmoid 非线性,*采用一个实数值,并将其压缩到 0 到 1 之间的范围内。特别是大负数变成 0,大正数变成 1。然而,sigmoid 非线性有几个主要缺点:(i) Sigmoid 饱和并消除梯度,(ii) Sigmoid 收敛慢,以及(iii) Sigmoid 输出不是以零为中心的。

更强大的非线性操作是 *ReLU,*代表整流线性单元。这是一个元素式操作,用 0 替换特征图中的所有负像素值。我们通过 *ReLU* 激活函数传递卷积层的结果。几乎所有后来开发的基于 CNN 的架构都使用 ReLU,就像我下面讨论的 AlexNet 一样。

**3 —池层**

在这之后,我们执行一个*池*操作来减少每个特征图的维度。这使我们能够减少网络中参数和计算的数量,从而控制过拟合。

CNN 使用 *max-pooling* ,其中它定义了一个空间邻域,并从该窗口内的矫正特征地图中获取最大元素。在池层之后,我们的网络对于输入图像中的小变换、扭曲和平移变得不变。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/3b49d78ad0517e61096e8bbe9676a2fc.png)

*(Source:* [*https://ujjwalkarn.me/2016/08/11/intuitive-explanation-convnets/*](https://ujjwalkarn.me/2016/08/11/intuitive-explanation-convnets/)*)*

**4 —全连接层**

在卷积层和池层之后,我们添加几个全连接层来包装 CNN 架构。卷积和池图层的输出表示输入影像的高级特征。FC 层使用这些特征来基于训练数据集将输入图像分类成各种类别。除了分类之外,添加 FC 层也有助于学习这些特征的非线性组合。

从更大的画面来看,CNN 架构完成 2 个主要任务:特征提取(卷积+池层)和分类(全连接层)。一般来说,我们的卷积步骤越多,我们的网络能够学习识别的复杂特征就越多。

## 进一步发展

从那时起,CNN 已经被改造成各种形式,用于自然语言处理、计算机视觉和语音识别的不同环境。我将在这篇文章的后面介绍一些著名的行业应用,但首先让我们讨论一下 CNN 在计算机视觉中的应用。从网上下载的彩色照片中识别真实物体比识别手写数字要复杂得多。存在数百倍的类别、数百倍的像素、三维场景的二维图像、需要分割的杂乱场景以及每个图像中的多个对象。CNN 将如何应对这些挑战?

2012 年,斯坦福大学计算机视觉小组组织了 [ILSVRC-2012 竞赛](http://www.image-net.org/challenges/LSVRC/2012/) (ImageNet 大规模视觉识别挑战赛)——计算机视觉领域最大的挑战之一。它基于 **ImageNet** ,一个拥有大约 120 万张高分辨率训练图像的数据集。呈现的测试图像没有初始注释,并且算法将必须产生指定图像中存在什么对象的标记。从那以后,每年都有来自顶尖大学、创业公司和大公司的团队竞相在数据集上宣称最先进的性能。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c7f3d79c5ad3f267d1c4d4218db63b60.png)

*(Source:* [*https://www.semanticscholar.org/paper/ImageNet%3A-A-large-scale-hierarchical-image-database-Deng-Dong/38211dc39e41273c0007889202c69f841e02248a*](https://www.semanticscholar.org/paper/ImageNet%3A-A-large-scale-hierarchical-image-database-Deng-Dong/38211dc39e41273c0007889202c69f841e02248a)*)*

第一次比赛的获胜者, [Alex Krizhevsky (NIPS 2012)](http://www.image-net.org/challenges/LSVRC/2012/supervision.pdf) ,构建了一个由 Yann LeCun 首创的非常深度的卷积神经网络(称为 **AlexNet)** 。与 LeNet 相比,AlexNet 更深入,每层有更多的过滤器,并且还配备了堆叠卷积层。查看下面的 AlexNet 架构,您可以确定它与 LeNet 的主要区别:

*   *处理和可训练层数:* AlexNet 包括 5 个卷积层、3 个最大池层和 3 个全连接层。LeNet 只有 2 个卷积层、2 个最大池层和 3 个全连接层。
*   *ReLU 非线性:* AlexNet 使用 ReLU,而 LeNet 使用 logistic sigmoid。ReLU 有助于减少 AlexNet 的训练时间,因为它比传统的逻辑 sigmoid 函数快几倍。
*   *dropout 的使用:* AlexNet 使用 dropout 层来解决过度拟合训练数据的问题。LeNet 没有使用这样的概念。
*   *多样化的数据集:【LeNet 只被训练识别手写数字,而 AlexNet 被训练处理 ImageNet 数据,这些数据在尺寸、颜色、角度、语义等方面丰富得多。*

AlexNet 成为开创性的“深度”CNN,以 84.6%的准确率赢得了比赛,而第二名的模型(仍然使用 LeNet 中的传统技术,而不是深度架构),仅实现了 73.8%的准确率。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/077a4c19a221809f788e10b2bed75b61.png)

*(Source:* [*https://indoml.com/2018/03/07/student-notes-convolutional-neural-networks-cnn-introduction/*](https://indoml.com/2018/03/07/student-notes-convolutional-neural-networks-cnn-introduction/)*)*

从那时起,这个比赛已经成为引入最先进的计算机视觉模型的基准舞台。特别是,已经有许多使用深度卷积神经网络作为其主干架构的竞争模型。在 ImageNet 比赛中取得优异成绩的最热门的有:[ZFNet](https://arxiv.org/pdf/1311.2901.pdf)(2013)[Google net](https://arxiv.org/pdf/1409.4842.pdf)(2014)[VGGNet](https://arxiv.org/pdf/1409.1556.pdf)(2014)[ResNet](https://arxiv.org/pdf/1512.03385.pdf)(2015)[dense net](https://arxiv.org/pdf/1608.06993.pdf)(2016)等。这些建筑一年比一年深。

## 应用程序

CNN 架构继续在**计算机视觉**中占据显著地位,架构进步为下面提到的许多应用和任务提供了速度、精度和训练方面的改进:

*   在**目标检测中,** CNN 是最流行的模型背后的主要架构,例如[R-CNN](https://www.cv-foundation.org/openaccess/content_cvpr_2014/papers/Girshick_Rich_Feature_Hierarchies_2014_CVPR_paper.pdf) 、[快速 R-CNN](https://arxiv.org/pdf/1504.08083.pdf) 、[更快 R-CNN](https://arxiv.org/pdf/1506.01497.pdf) 。在这些模型中,网络假设目标区域,然后使用 CNN 对这些区域进行分类。现在,这是许多对象检测模型的主要渠道,部署在自动驾驶汽车、智能视频监控、面部检测等领域。
*   在**目标跟踪中,**细胞神经网络已经广泛应用于视觉跟踪应用中。例如,给定一个在离线的大规模图像库中预先训练的 CNN,[韩国浦项研究所团队开发的这种在线视觉跟踪算法](https://arxiv.org/pdf/1502.06796.pdf)可以学习判别显著图,以在空间和局部上可视化目标。另一个例子是 [DeepTrack](http://www.bmva.org/bmvc/2014/files/paper028.pdf) ,这是一种在跟踪过程中自动重新学习最有用的特征表示的解决方案,以便准确地适应外观变化、姿势和比例变化,同时防止漂移和跟踪失败。
*   在**物体识别中,**来自法国 INRIA 和 MSR 的团队开发了一个[弱监督 CNN 用于物体分类](https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Oquab_Is_Object_Localization_2015_CVPR_paper.pdf),它仅依赖于图像 lvil 标签,但可以从包含多个物体的混乱场景中学习。另一个实例是 [FV-CNN](https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Cimpoi_Deep_Filter_Banks_2015_CVPR_paper.pdf) ,这是牛津的人为了解决纹理识别中的杂乱问题而开发的纹理描述符。
*   在**语义分割中,** [深度解析网络](https://arxiv.org/pdf/1509.02634.pdf)是一组来自香港的研究人员开发的基于 CNN 的网络,用于将丰富的信息融入图像分割过程。另一方面,加州大学伯克利分校的研究人员建立了[全卷积网络](https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Long_Fully_Convolutional_Networks_2015_CVPR_paper.pdf),并在语义分割方面超过了最先进水平。最近, [SegNet](https://arxiv.org/pdf/1511.00561.pdf) 是一个深度全卷积神经网络,在语义像素分割的内存和计算时间方面极其高效。
*   在**视频和图像字幕方面,**最重要的发明是加州大学伯克利分校的[长期递归卷积网络](https://arxiv.org/pdf/1411.4389.pdf),它结合了 CNN 和 RNNs(递归神经网络)来处理大规模视觉理解任务,包括活动识别、图像字幕和视频描述。YouTube 的数据科学团队已经大量部署了该工具,以理解每天上传到该平台的大量视频。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e0bbb28dc546c84cefc8d3b78c77ecf1.png)

*(Source:* [*https://idealog.co.nz/tech/2014/11/googles-latest-auto-captioning-experiment-and-its-deep-fascination-artificial-intelligence*](https://idealog.co.nz/tech/2014/11/googles-latest-auto-captioning-experiment-and-its-deep-fascination-artificial-intelligence)*)*

CNN 还发现了视觉之外的许多新应用,特别是自然语言处理和语音识别:

*   **自然语言处理**:在**机器翻译**领域,脸书的[人工智能研究团队使用 CNN 以 9 倍于递归神经系统的速度实现了最先进的准确性。在**句子分类**领域,NYU](https://code.facebook.com/posts/1978007565818999/a-novel-approach-to-neural-machine-translation/) [的 Yoon Kim 对基于预训练单词向量训练的 CNN 进行了实验,用于句子级分类任务](http://www.aclweb.org/anthology/D14-1181),并在 7 项任务中的 4 项上进行了改进。在**问题回答**领域,来自滑铁卢和马里兰[的一些研究人员探索了 CNN 在端到端问题回答](https://arxiv.org/pdf/1707.07804.pdf)中答案选择的有效性。他们发现 CNN 的答案明显优于以前的算法。
*   **语音识别**:对于自动语音识别来说,CNN 是减少频谱变化和对声学特征中的频谱相关性建模的非常有效的模型。将 CNN 与隐马尔可夫模型/高斯混合模型相结合的混合语音识别系统已经在各种基准中取得了最先进的结果。蒙特利尔大学的研究人员提出了一个用于序列标记的[端到端语音框架](https://arxiv.org/pdf/1701.02720.pdf),通过将分层 CNN 与 CTC(连接主义时间分类)相结合,与现有的基线系统相竞争。微软的团队使用 CNN 来降低语音识别性能的错误率,特别是通过[建立一个具有本地连接、权重共享和池化的 CNN 架构](https://www.microsoft.com/en-us/research/publication/convolutional-neural-networks-for-speech-recognition-2/)。他们的模型能够不受说话者和环境变化的影响。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/dca294bda7223ec9201e7196b6c03516.png)

*(Source:* [*http://www.wildml.com/2015/11/understanding-convolutional-neural-networks-for-nlp/*](http://www.wildml.com/2015/11/understanding-convolutional-neural-networks-for-nlp/)*)*

## 结论

让我们再次回顾哈利·波特图像的例子,看看我如何使用 CNN 来识别它的特征:

*   首先,我在整个原始图像上传递一个滑动窗口,并将每个结果保存为一个单独的小图片块。通过这样做,我将原始图像转换成多个大小相等的小图像块。
*   然后,我将每个图像块输入卷积层,并为同一原始图像中的每个图像块保持相同的神经网络权重。
*   接下来,我将每个图块的结果保存到一个新数组中,其排列方式与原始图像相同。
*   然后,我使用 max-pooling 来减小数组的大小。例如,我可以查看数组的每个 2 x 2 的正方形,并保留最大的数字。
*   经过下采样后,这个小数组被送入全卷积层进行预测,比如它是哈利、罗恩、赫敏、精灵、报纸、椅子等的图像。
*   经过训练,我现在有信心对自己的形象做出预测!

从这篇文章中可以看出,卷积神经网络在塑造深度学习的历史中发挥了重要作用。与大多数其他神经网络相比,受到大脑研究的极大启发,CNN 在深度学习(视觉、语言、语音)的商业应用中表现得非常好。它们已经被许多机器学习从业者用来赢得学术和行业竞赛。对 CNN 架构的研究进展如此之快:使用更少的权重/参数,自动学习和概括输入对象的特征,对图像/文本/语音中的对象位置和失真不变…毫无疑问,CNN 是最受欢迎的神经网络技术,是任何想进入深度学习领域的人必须知道的。

— —

*如果你喜欢这首曲子,我希望你能按下鼓掌按钮*👏*这样别人可能会偶然发现它。你可以在* [*GitHub*](https://github.com/khanhnamle1994) *上找到我自己的代码,在*[*【https://jameskle.com/】*](https://jameskle.com/)*上找到更多我的写作和项目。也可以在* [*推特*](https://twitter.com/@james_aka_yale) *,* [*上关注我直接发邮件给我*](mailto:khanhle.1013@gmail.com) *或者* [*在 LinkedIn*](http://www.linkedin.com/in/khanhnamle94) *上找我。*

# 康威在搅拌机中的生活游戏

> 原文:<https://towardsdatascience.com/conways-game-of-life-in-blender-6dd84cd22fa1?source=collection_archive---------3----------------------->

《生命的游戏》( GOL)可能是细胞自动机最臭名昭著的例子之一。

由数学家约翰·何顿·康威定义,它在一个二维网格上展开,每个细胞可以处于两种可能状态中的一种。从初始网格配置开始,系统在每个单元步骤中仅考虑前一个配置。如果对于每个小区,我们将周围的八个小区视为*邻居*,则系统转换由[四个简单规则](https://en.wikipedia.org/wiki/Conway's_Game_of_Life#Rules)定义。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/32522d9ad86fc3373e323c30c2fd8c6f.png)

A basic plain-2D example

我对用 Blender 探索这种现象的可视化很感兴趣。以下是一些实验结果。

25x25 Cubes Grid with shrinking update function

20x20 Ico-Spheres Grid with shrinking update function

40x40 Cubes Grid with hiding update function

[这里的代码如果有人感兴趣](https://github.com/5agado/data-science-learning/tree/master/cellular%20automata)。这是一个可重用的脚本,你可以直接在 Blender 脚本界面中导入和运行。它定义了 GOL 逻辑,并将 GOL 网格到 Blender 的移植分解为两个可定制的组件:

*   **生成器**—负责生成将被映射到原始 GOL 网格中的单元格的 Blender 对象。一个生成器被精确地用来建立初始的混合网格,并带有首选的网格(例子:立方体,球体,猴子)。
*   **更新器** —基于 GOL 网格值定义 Blender 对象的更新行为。应该根据相应的网格值是 0 还是 1 来指定对象的变化(例如:缩放、隐藏)。

剩下的只是注册更新处理程序的帮助器代码,这样一个帧的改变会导致 GOL 网格的更新(可能包括关键帧)。
我建议一旦你得到你喜欢的结果就删除处理程序,这样额外的帧变化就不会再次触发更新并破坏你的结果。

## 3D 版本

我还试验了一个三维网格,它使用相同的规则集,同时将邻居的数量扩展到新的维度。

我计划更深入地研究 3D 概念,以找到更稳定的配置。另一个有趣的改进是拥有不受约束的网格,这意味着从初始配置开始的自动机可以在空间中无限增长。对于这种方法,我不得不重新制定我目前的代码逻辑,可能首先用一些替代 Blender 的方法进行实验,因为这里的家伙消耗了大量的资源,即使是这些简单的渲染,所以这方面的任何建议都是非常受欢迎的!

## 离题

玩这些细胞自动机的可视化在我的脑海中引发了像因果关系/目的论和[尘埃理论](http://www.gregegan.net/PERMUTATION/FAQ/FAQ.html)这样的概念。所有系统状态都是确定性定义的,但只能根据前一时间步的状态进行计算。有了缩放/收缩功能,系统的行为方式与其他类型的更新功能完全相同,改变的是单元相对于系统状态的行为。一个细胞在外力的作用下会立即收缩,以反映一个等于零的状态,但随后会随着自由意志的幻觉而进化到一个完整大小的新状态。实际上,这种增长只不过是关键帧填充。不是一个细胞的故事决定了它的未来,而是系统的未来决定了所有细胞生命的故事。

# 用机器学习烹饪:降维

> 原文:<https://towardsdatascience.com/cooking-recipes-with-pca-5c33a4acb7db?source=collection_archive---------7----------------------->

## 最近,我在 Kaggle 上偶然发现了这个烹饪食谱数据集,它启发我将生活中的两个主要兴趣结合起来。食物和机器学习。

这个数据集的特别之处在于,它包含了 20 种不同菜系的食谱,6714 种不同的食材,但只有 26648 个样本。有些菜系的食谱比其他菜系少得多。

对于这么多的数据来说,这是太多的功能了。在处理这个数据集之前,第一步应该是减少它的维数。在这篇文章中,我将展示如何使用主成分分析将这 6714 种成分缩减到一个只有 700 维的潜在空间中。作为奖励,我们将使用这个模型作为异常探测器。

数据如下所示:

{
"id": 24717,
"cuisine": "indian",
"ingredients": [
"tumeric",
"vegetable stock",
"tomatoes",
"garam masala",
"naan",
"red lentils",
"red chili peppers",
"onions",
"spinach",
"sweet potatoes"
]
},


最具挑战性的方面是它非常稀少,这里是每个食谱的成分分类:

mean 10.76771257605471
min 1 #rice!
max 65 #complicated fettuccine recipe
std 4.428921893064523


另一种方法是查看这个方差直方图:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/66e01d76171967f2dde664032b8d9c9d.png)

如果我们放大一点:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/a246c6f2276aedfee3e94f574a76bb8e.png)

这意味着平均起来,每行 6714 个特征只有 10 个特征是活动的。少于 1%。这使得将数据分成训练集和测试集变得困难。正因为如此,你很有可能最终得到两套完全不同的食谱。解决这个问题的一个好方法是对数据进行 k 倍折叠,但不是在这种情况下。数据太稀疏,不会有太大改善。

对于具有如此多特征的稀疏数据集,第一步通常是减少维数。[你没听说过维度诅咒吗?](https://en.wikipedia.org/wiki/Curse_of_dimensionality)

在这篇文章中,我将使用一种非常流行的方法来降低维度:PCA

## 转换数据

该忙起来了!让我们对数据做一些基本的转换。这里的主要思想是,因为我们有定性的数据,我们需要做一些被称为一次性编码的事情。长话短说:6714 食材-> 6714 栏目。当配方中有一种配料时,它的列将变为 1。其余的都是 0。平均而言,每行中只有 10 列是“活动的”。这段代码将创建“transformer ”,它将获得一种配料并输出其矢量表示

from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
from numpy import array
import jsonf = open('recipes_train.json', 'r')
recipes_train_txt = f.read()
recipes_train_json = json.loads(recipes_train_txt)#get list of ingredients
ingredients = set()
ingredients_matrix = []for recipe in recipes_train_json: ingredients_matrix.append(recipe["ingredients"])
for ingred in recipe["ingredients"]: ingredients.add(ingred)ingredients = list(ingredients)ingredients.sort() #it made my life easier to have it sorted when i needed to check what is what in the encoded vectorvalues = array(ingredients) label_encoder = LabelEncoder()#gives a unique int value for each string ingredient, and saves the #mapping. you need that for the encoder. something like:

['banana'] -> [1]

integer_encoded = label_encoder.fit_transform(values)

onehot_encoder = OneHotEncoder(sparse=False)
integer_encoded = integer_encoded.reshape(len(integer_encoded), 1)#here you encode something like : [2] -> [0,1,0,0,...]
onehot_encoded = onehot_encoder.fit_transform(integer_encoded)def transform_value(s):

l = array([s])
integer_encoded = label_encoder.transform(l)
integer_encoded = integer_encoded.reshape(len(integer_encoded), 1)
onehot_encoded = onehot_encoder.transform(integer_encoded)

return onehot_encoded[0]

这段代码为我们提供了一个编码器,它将获得一个成分(字符串)作为输入,并输出其向量表示。包含所有配方成分的最终向量将是对这些成分向量中的每一个进行“逻辑或”运算的结果

## 主成分分析

PCA 是一个非常受欢迎的选择。它在输入新的简化数据集以使用 t-sne 可视化之前用作预处理工具,但也是您可能希望在输入机器学习算法之前用来简化您的特征的工具。

这是可取的,因为您拥有的功能越多,需要的数据就越多,学习过程就越慢。所以,把事情做小将会提升你的表现。

但是在最小化数据之前,你需要打一个电话:你想要多小?这里有一个权衡,你去的越小,你丢失的信息就越多。您可以通过使用与最小化数据相同的训练模型来测量,然后最大化回原始大小。之后,您可以比较这两个样本,并测量它们之间的差异(记住,当您下降时,您会丢失信息)。

所以,让我们只是训练一堆不同的模型,并选择一个具有很少特征但重建误差低的模型。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/dc0135b552ebdc9f768b3dc3c9cd5017.png)

X 轴是分量向量的数量,而 Y 轴是整个样本的重建误差(使用 L2)。那些结果看起来不错,对吗?但是让我们在这里深入挖掘一下。700 似乎是一个安全的数字,在这个区域周围没有太大的改善。这已经是对 6714 功能的巨大改进。我们来对比一些看不见的数据,测试集。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/efc553a02f03e9228f5d455a241ce015.png)

这里我们可以看到,主成分分析在概括数据结构方面做得不错。训练均方误差~ = 0.000171%**测试均方误差~= 0.0002431%** 。还不错。

我们之前知道,数据平均有 10 种成分,标准偏差为 4.42。如果我们用这个分布创造一些‘随机食谱’(随机挑选配料)会怎么样?这可以使用高斯发生器来实现。如果 PCA 学到了什么,我们应该看到一些主要的重建错误。

for n_candidates in range(N_CANDIDATES):
dna = [0 for i in range(N_INGREDIENTS)]

n_flips = int(round(random.gauss(mu=MEAN, sigma=STD)))
indexes_to_flip = [ random.randint(0, N_INGREDIENTS-1 ) for i in range(n_flips) ] for i in range(n_flips):

    dna[ indexes_to_flip[i]  ] = 1 BAD_DATA.append(dna)

让我们看看模型如何处理这些假数据。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/40a46d0f37c139bc779cbba4d54fd976.png)

这不是最初的目标,但看起来我们有一个很好的模型来检测异常配方。如果我们将阈值设置为 0.0004,并且将重构误差大于该阈值的任何事物视为异常,则我们得到以下矩阵:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/f690ae5fe75a6ca47bb7e7d5cd04eb86.png)

## 结论

我们将这个数据集从 6714 个特征减少到只有 700 个。

我们现在可以得出结论,模型确实从训练集中学到了一些东西。我们试图欺骗 PCA 模型,我们了解到一些成分通常会在一起,而一些不会混合。您也可以使用这个模型作为异常检测,其中糟糕的食谱是异常的(您不应该吃那些!).也许作为一个后续项目,我可以尝试利用这种“学习”。

正如你所看到的,不同的菜系之间有一种模式。我们已经有了一个模型来检测不属于任何一种模式的异常配方,生成新配方会有多难?有可能创造新的法国菜吗?

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/60f8625bd9c2fc402a9cd8bfc210a5c6.png)

# 使用 Kaggle 数据集和内核设计一个数据科学项目

> 原文:<https://towardsdatascience.com/cooking-up-a-data-science-project-using-kaggle-datasets-and-kernels-fca2b678e268?source=collection_archive---------10----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/88175fca9776bea960c02aa930a97046.png)

在《人工智能历险记》的这一集里,我邀请了 Kaggle 数据集的产品负责人 Megan Risdal 为我们介绍一下 [Kaggle 内核](https://www.youtube.com/watch?v=FloMHMOU5Bs&list=PLIivdWyY5sqJxnwJhe3etaK7utrBiPBQ2&index=14&t=0s)和 [Kaggle 数据集](https://www.kaggle.com/datasets)的一些最新功能,并展示一些在 Kaggle 内核上合作的方式。你可以在下面观看我们在卡格尔探险的视频。

[](/introduction-to-kaggle-kernels-2ad754ebf77) [## Kaggle 内核简介

### 在《人工智能历险记》的这一集里,你会发现什么是 Kaggle 内核,以及如何开始使用它们。虽然没有…

towardsdatascience.com](/introduction-to-kaggle-kernels-2ad754ebf77) 

我们将一起工作,使用最新鲜的成分(数据),使用不同的工具准备它们,并一起工作,拿出一个美味的结果——一个发布的数据集和一些很酷的分析,我们将与世界分享。

# 使用数据集和内核

我们将从洛杉矶市的开放数据门户下载公共数据,包括洛杉矶餐馆违反环境卫生的情况。然后,我们将使用这些数据创建一个新的数据集,并在向全世界发布之前,共同开发一个内核。

在这一集里,你将学到:

*   如何从原始数据中**创建**一个新的、*私有的*Kaggle 数据集
*   如何**在公开数据集之前与合作者共享**数据集
*   如何在 Kaggle 内核上使用 **collaborate** 通过向私有内核添加协作者
*   如何正确地**注释和配置**您的 Kaggle 数据集,以便其他人可以轻松地发现和贡献它

当数据与可再生代码以及专家和学习者社区共享时,它是最强大的。通过将数据和代码放在一个共享的、一致的平台上,您可以与最好的高效能笔记本电脑进行最佳协作。

## 资源

既然你已经看到了如何制作一个数据集和内核,现在是你自己制作的时候了![前往 Kaggle](https://www.kaggle.com/datasets?modal=true) 立即开始制作新数据集。

数据集:[https://www . ka ggle . com/Megan risdal/exploring-la-county-health-code-violations-by-date](https://www.kaggle.com/meganrisdal/exploring-la-county-health-code-violations-by-date)

我们的内核:[https://www . ka ggle . com/Megan risdal/exploring-la-county-health-code-violations-by-date](https://www.kaggle.com/meganrisdal/exploring-la-county-health-code-violations-by-date)

Megan Risdal 是 Kaggle 数据集的产品负责人,这意味着她与工程师、设计师和拥有 170 万数据科学家的 Kaggle 社区合作,构建用于查找、共享和分析数据的工具。她希望 Kaggle 成为人们分享和合作数据科学项目的最佳场所。

感谢阅读这一集的[云人工智能冒险](https://goo.gl/UC5usG)。如果你喜欢这个系列,请为这篇文章鼓掌让我知道。如果你想要更多的机器学习动作,一定要关注媒体上的[我](https://medium.com/@yufengg)或[订阅 YouTube 频道](https://goo.gl/S0AS51)以观看未来的剧集。更多剧集即将推出!

# 用计算机视觉烹饪

> 原文:<https://towardsdatascience.com/cooking-with-computer-vision-b87eb3bc247?source=collection_archive---------11----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9f4e331128038cd30ad13a3e62f54ace.png)

为什么机器人给我们做饭要花这么长时间?

机器人和人工智能最有趣的应用之一,(即计算机视觉),是在厨房里!有许多初创公司正在研究复杂的机器人厨师,并使用机器人来自动化烹饪过程,例如制作汉堡:

机器人烹饪的潜力远远超出了汉堡,在这篇文章中,我将讨论如何使用计算机视觉来自动化烹饪牛排、鸡肉和鱼肉等日常肉类的过程。本文中介绍的算法可以很容易地在用刮刀捆绑的 raspberry pi 相机中实现。

烹饪牛排是一项相当简单的任务,而且大多需要远见才能完成。只要我们的机器人厨师有一个摄像头和一个翻转和取出牛排的机械装置,(就像一把锅铲),就可以走了。这里有一个简单的算法来说明机器人厨师应该如何烹饪牛排。

该算法的主要概念是,它将使用图像识别模型来处理来自摄像机的视频馈送中的图像。随着时间的推移,我们将利用**卷积神经网络**的惊人能力来处理牛排。有人可能会反驳说,你可以简单地用牛排的颜色直方图,随着时间的推移,从生的(红色),到熟的(棕色),进行比较。但是,在牛排上放调料、光线不好等情况下,CNN 会概括得更好。

这些图像不需要实时处理(或类似 1/30 帧/秒),因为这不是一个真正的时间关键任务。为了节省计算资源和加速算法,我们将每隔 6 帧处理一次,以检查牛排的状态。此外,我们将在环路中支持更多烹饪牛排所需的基础设施。

***初始配置*** :建立一个图像分类器,将牛排从‘生的’,映射到‘生的’,‘三分熟的’,‘中等的’,‘五分熟的’,‘全熟的’。由于这些类遵循逻辑顺序,我们将只从 0 到 5 映射牛排。

**类别不平衡:**在具有罕见实例的数据集上训练分类器所涉及的一个常见问题是类别不平衡。例如,在为检测海洋中的鲸鱼鳍之类的事情构建图像分类器时,经常会讨论到这一点。由于大部分时间我们的牛排被分类为生的,我们将需要确保我们相应地评估我们的分类器的性能指标。

*一旦分类器达到约 95%的准确率,将牛排分类为生的或熟的,以 Pythonic 风格编写伪代码*

def cookSteaks(target):
timer = 0 # used to flip the steaks arbitrarily
side = True # flag used to label sides of the steak as cooked
side0Finished = False
side1Finished = False
Finished = False
seq = 0 # require 5 consecutive classifications to finishProcess every 6 frames
Classify frame
if (frame == target): # target indicates ['rare', ..' well-done']
seq += 1
if (seq == 5):
if (side):
side0Finished = True
if (side1Finished):
Finished = True
exit;
else:
seq = 0
flip()
else:
....
...
timer += 1
if (timer == 120):
flip()
side = !side
timer = 0


在没有完成伪代码的情况下,我认为算法背后的逻辑已经很清楚了。

我们在这里运行 4 个主要部分:

1.  **使用图像识别模型来处理来自视频流**的输入帧(不一定需要具有该任务的实时能力)。
2.  **由于分类模型**中的一些错误,需要连续的分类来触发响应,(如果没有大量的训练数据,95%的准确率将被认为是二进制分类任务的一个相当好的模型),我们不能让机器人每次都用分类器来翻动牛排。在这个例子中,我们尝试使用 5 作为翻转的阈值,但是这可能太低了。
3.  **标志框住任务**,使用布尔标志引导循环,确保两面都被烹饪,并在烹饪过程中保持翻转。
4.  **任意翻转的计时器,**我们希望每隔 2 分钟左右任意翻转一次牛排,以确保我们不会只煎一面,然后翻转另一面,然后从头到尾煎完。我相信专业厨师有比这更好的策略,但这足以解决我们的问题

*收集用于训练分类器的数据*

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/65be4a437ed570d82307fb5189b47359.png)

我们将输入一个由这样的图像组成的数据集。基于观察平底锅的视角标记图像。

总之,我们看到烹饪肉类是一个相当简单的任务,不需要革命性的算法来完成。建立在卷积神经网络上的图像识别模型非常强大,可以在视频上使用,以实现令人惊叹的事情。我对机器人烹饪的未来感到非常兴奋,并看到这将如何改变食品市场,感谢阅读!

# [CShorten](https://medium.com/@connorshorten300)

Connor Shorten 是佛罗里达大西洋大学计算机科学专业的学生。对数据科学、深度学习和软件工程感兴趣。主要用 Python,JavaScript,C++编码。请关注更多关于这些主题的文章。

# 用机器学习烹饪:自动编码器

> 原文:<https://towardsdatascience.com/cooking-with-machine-learning-autoencoders-603002b278d5?source=collection_archive---------23----------------------->

这是关于在烹饪食谱上使用机器学习的机器学习系列文章的一部分。如果你错过了第一篇文章,请点击 [*这里*](https://medium.com/@diegotole/cooking-recipes-with-pca-5c33a4acb7db)

**前情提要关于 PCA**
在上一篇文章中,我们探讨了如何使用 PCA 来降低数据集的维数。此外,由于 PCA 通过探索变量之间的“线性”协方差来转换数据,因此它还可以用作异常检测器。因为任何不遵循初始数据集“结构”的配方都不会很好地转化。在我们的情况下,这意味着这是一个糟糕的食谱!更多信息请点击[此处](https://medium.com/@diegotole/cooking-recipes-with-pca-5c33a4acb7db)

**如果 PCA 工作正常,为什么要使用 autoencoder?** 在本文中,我们将通过使用自动编码器来击败 PCA。我们将压缩和解压缩数据,就像我们对 PCA 所做的那样,但我们的自动编码器不会受到“线性”变换的限制,这使得它更加强大。

什么是自动编码器?
简而言之,自动编码器是一种神经网络架构。主元件是中间的瓶颈,基于输入计算误差。所以你通过一个瓶颈挤压数据,并试图在另一端重建相同的输入。没错,没有标签或值被预测。你在努力找回你的输入。如果你成功了,你可以把它作为一个变压器来降低你的维度。您只需要捕获瓶颈层中的特性。

**普通自动编码器** 这是我们自动编码器的第一次迭代:

num_input = len(X_Total[0])
num_hidden_l = 700X = tf.placeholder(“float”, [None, num_input])w_encoder_h1 = tf.Variable(tf.random_normal([num_input, num_hidden_l])w_decoder_h2 = tf.Variable(tf.random_normal([num_hidden_l, num_input])) encoder_b1 = tf.Variable(tf.random_normal([num_hidden_l]))
decoder_b2 = tf.Variable(tf.random_normal([num_input])) layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(X, w_encoder_h1),
encoder_b1))
layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1, w_decoder_h2),
decoder_b2))# Prediction
y_pred = layer_2

Targets (Labels) are the input data.

y_true = X# Define loss and optimizer, minimize the squared error
loss = tf.reduce_mean(tf.pow(y_true - y_pred, 2))
optimizer = tf.train.RMSPropOptimizer(learning_rate).minimize(loss)


一切都很标准。如果你花一些时间谷歌一下 autoencoder 的例子,你应该会遇到一些非常相似的东西。我到处都在用 sigmoid 作为激活函数,损失函数就是均方差。最后,RMSPropOptimizer 只是一个花哨的梯度下降。最终结果应该类似于下图:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/908c4bcb8cbc08c0bd54aa0349e7698d.png)

Input = 6714 , Hidden layer = 700, Output = 6714

为什么这不起作用?
如果你尝试运行那个代码,你会发现你打不过 PCA。这是因为我们的数据集和模型的性质。你会卡在~= 0.024113%的重建误差,听起来不错,其实不然。主成分分析在测试集中达到了 **~= 0.0002431%。让我们来解决这个问题。**

**问题 1:消失渐变** 首先,观察 sigmoid 曲线是什么样子的:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/ebbe30b4b2a9790bf36fa66327a51ff3.png)

请记住,我们的数据非常少,有 6714 列(成分),大多数时候每个配方只有 10 种成分。这意味着我们的网络正在学习没有一种成分是真正“重要”的,它将所有成分的权重值降低到非常接近 0。想想看,你给我 6714 种成分,我完全忽略你给我的一切,我还给你另外 10 种随机成分。我错过了所有的 1,但与此同时,我答对了所有成千上万的 0。

6714 - 20 misses -> 0.0029% error.


当使用 sigmoid 作为激活函数时,这就产生了问题,因为它在零附近开始变得非常平坦。这意味着学习非常缓慢,甚至没有学习。这也是这个激活功能正在失宠的原因之一,但它仍然有一些非常好的用例。因为它的输出总是落在 0 和 1 之间,所以如果你想输出概率,它是很棒的。

为了解决卡在零附近的问题,我们将把激活函数改为“泄漏 ReLU”。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e1e36c2eab550e2e99e293690b4f13f3.png)

这里的主要区别是,在零点以下和零点之后,你有一个恒定的斜率。这将会给我们一些腿来左右移动,不管重量有多小。现在发表的大多数现代机器学习论文都在使用 ReLU 激活函数的变体,比如 leaky ReLU。

问题 2:这是正确的误差函数吗?
使用中值平方误差的问题是,无论你做了什么错误的预测,所有这些 0 都会冲淡。因为你总会有一堆 0 的对。另一个问题是 sigmoid 返回十进制数,我们需要 0 或 1。并且,最终误差是根据成分向量的“总和”计算的。因此,任何两个加起来为 10 的食谱看起来都是一样的,即使一个食谱有 20 种成分,每种成分的价值为 0.5,看起来也是一样的。这个误差函数更适合于回归问题,这里我们有一个多标签分类问题。

解决办法就是改成‘交叉熵’。这将集中在“预测的标签”(1),而不是整个事情。成千上万的 0 不会再淡化错误。更具体地说,我们将使用“sigmoid _ cross _ entropy _ with _ logits”。以下是 tensorflow 文档中的描述:

*测量离散分类任务中的概率误差,其中每个类别都是独立的且不互斥。例如,可以进行多标签分类,一张图片可以同时包含一头大象和一只狗*

用我自己的话说,这是测量预测向量和标签向量之间的距离。之前,我们只是将所有值相加,然后将预测向量和与标签向量和进行比较。

**问题 3** sigmoid 和 leaky ReLU 都输出十进制数。虽然 sigmoid 有界在 0 和 1 之间,但我们当前的选择是无界的。对于我们的问题来说,0.32 番茄这样的东西没有意义。你要么有西红柿,要么没有。因此,尽管我们将使用交叉熵来训练我们的模型,但是每当我们使用我们的模型时,我们将对输出应用最终变换:

correct_prediction = tf.equal(tf.round(y_pred), y_true)
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))


round 函数将所有数字舍入到最接近的整数,稍后我们比较两个向量的所有索引。最后,我们得到一个“重建误差”并存储在精度变量中。

**更新解决方案**

num_input = len(X_Total[0])
num_hidden_l = 700X = tf.placeholder(“float”, [None, num_input])w_encoder_h1 = tf.Variable(tf.random_normal([num_input, num_hidden_l])w_decoder_h2 = tf.Variable(tf.random_normal([num_hidden_l, num_input]))encoder_b1 = tf.Variable(tf.random_normal([num_hidden_l]))
decoder_b2 = tf.Variable(tf.random_normal([num_input]))layer_1 = tf.nn.leaky_relu(tf.add(tf.matmul(X, w_encoder_h1),
encoder_b1))
layer_2 = tf.nn.leaky_relu(tf.add(tf.matmul(layer_1, w_decoder_h2),
decoder_b2))# Prediction
y_pred = layer_2

Targets (Labels) are the input data.

y_true = X# Define loss and optimizer, minimize the squared error
loss_entropy = tf.nn.sigmoid_cross_entropy_with_logits (logits=y_logit,labels= y_true)loss = tf.reduce_mean(loss_entropy)
optimizer = tf.train.RMSPropOptimizer(learning_rate).minimize(loss)


有了这个更新的模型,我们可以很容易地击败 PCA 的重建误差。大约 15 个历元之后,代码大小为 700(如果您在此处 *,*查看以前的帖子,您会发现这是我们在开始丢失太多数据之前所能得到的最小值)我们在测试集中得到 0.000003676%的重构**误差,而 PCA 模型得到 0.0002431%。**我们已经有了 100 倍的改进,但我们不必局限于 700 项功能。如果我们将代码大小更改为 200,我们的误差在测试集中上升到只有 0.000008964%。这仍然是 100 倍的改进,但是使用的功能更少了。我们使用这 200 个特征的任何模型都将比使用 700 个特征的模型训练得更快。

**结论** 虽然 PCA 方便多了,但使用 autoencoder 最终还是有回报的。我们不费吹灰之力就将 6714 个特性减少到了 200 个。我们简单地实现了 autoencoder,发现了一些问题,并根据我们的模型需求进行了调整。我相信这不是极限,通过增加几个隐藏层,并使用最先进的深度学习技巧,我们可能会变得更低。我们的下一步将是在另一个模型中使用这 200 个特性。

# 我发现的很酷的数据集

> 原文:<https://towardsdatascience.com/cool-data-sets-ive-found-adc17c5e55e1?source=collection_archive---------1----------------------->

在尝试为我正在进行的编码训练营做铺垫时,我发现了一些很酷的数据集,我想我应该分享一下。

每个人都应该注册由杰里米·辛格·万撰写的《数据是复数》时事通讯。他每周三发出 5 个很酷的数据集。主页上有一个电子表格,里面有所有过去的数据集,它们太酷了。

 [## 数据是复数杰里米·辛格·文

### 编辑描述

tinyletter.com](http://tinyletter.com/data-is-plural) 

有一个 github 叫做 awesome public data sets,它有很多不同主题的资源。

[](https://github.com/caesar0301/awesome-public-datasets) [## Caesar 0301/awesome-公共数据集

### awesome-public-datasets --公共领域高质量开放数据集的 awesome 列表(正在进行中)。由每个人,为…

github.com](https://github.com/caesar0301/awesome-public-datasets) 

由于 kaggle 使用公共数据举办了许多比赛,他们拥有世界上所有事物的大量数据集

 [## 数据集| Kaggle

### 编辑描述

www.kaggle.com](https://www.kaggle.com/datasets) 

如果您正在处理大数据,并且需要一些帮助来开始实践,Amazon Web Services 有几个可供下载。

[](https://aws.amazon.com/public-datasets/) [## 大型数据集存储库| AWS 上的公共数据集

### AWS 公共数据集允许任何人从集中的数据存储库中访问大型数据集,并使用…

aws.amazon.com](https://aws.amazon.com/public-datasets/) 

KDNuggets 有一个全面的数据集和门户列表,您可以从中获取更多数据。

[](http://www.kdnuggets.com/datasets/index.html) [## 数据挖掘和数据科学数据集

### 另见资产宏观、宏观经济指标的历史数据和市场数据。github 上令人惊叹的公共数据集…

www.kdnuggets.com](http://www.kdnuggets.com/datasets/index.html) 

如果你在寻找州数据集,大多数州都有一个开放的数据门户,其中一些要比其他的好得多,但它们都很容易谷歌。以下是我找到的一些例子:

*   [https://opendata.cityofnewyork.us/](https://opendata.cityofnewyork.us/)
*   http://opendata.dc.gov/
*   【https://data.virginia.gov/ 
*   [https://www.data.gov/open-gov/](https://www.data.gov/open-gov/)

# CoQA:一个会话式问答挑战

> 原文:<https://towardsdatascience.com/coqa-a-conversational-question-answering-challenge-7d9c3aceda3c?source=collection_archive---------10----------------------->

## 我们能教聊天机器人进行推理,并与我们进行对话和信息检索吗?

对于自然语言处理来说,这是非常棒的一周,我真的很兴奋(你也应该如此)!本周,我像往常一样偶然浏览了 Arxiv Sanity Preserver 网站,但我看到的不是一大堆 GAN(这是美国有线电视新闻网)或 CNN(那是美国有线电视新闻网)的报道,而是两个让我非常高兴的数据集。他们是 [CoQA](https://arxiv.org/pdf/1808.07042.pdf) 和 [QuAC](https://arxiv.org/pdf/1808.07036v1.pdf) ,在这篇博客中我们将谈论 CoQA 为什么如此令人兴奋。

如果你对更多的问题回答感兴趣,我也在这里谈论 QuAC!

# 对话式问答挑战

“对话式问答挑战”到底意味着什么?这不仅需要阅读一篇文章来寻找答案,还需要就这些信息进行对话。它需要在对话中使用上下文线索来发现对方在问什么。它要求信息以一种抽象而不是抽象的方式重新表述。

哇哦。

这个数据集不同于我们在问答中见过的任何东西!

# 动机

那么我们为什么需要 CoQA 这样的数据集呢?

很简单,因为我们当前的数据集并没有为我们做这些。人类通过相互交流、提问、理解上下文和对信息进行推理来学习。聊天机器人…嗯,聊天机器人不会这样做。至少现在不是。结果是,虚拟代理缺乏,聊天机器人看起来很愚蠢(大多数时候)。

CoQA 是作为一个数据集创建的,以帮助我们衡量算法参与问答对话的能力。本质上,是为了衡量一台机器的对话能力。

在他们的论文中,CoQA 的创建有三个目标:

*   1.创建以依赖于对话历史记录的问答对为特征的数据集
*   CoQA 是第一个大规模这样做的数据集!
*   在对话中的第一个问题之后,每个问题都依赖于所说内容的历史
*   有 127,000 个问题跨越 8,000 个对话!
*   2.创建一个寻找自然答案的数据集
*   许多问答数据集是提取的,搜索词汇相似的回答,并直接从文本中提取出来
*   CoQA 希望从文章中提取基本原理,但要有一个重新措辞的、抽象的答案
*   3.为了确保问答系统在不同的领域中表现良好
*   CoQA 有一个包含 5 个领域的训练集和一个包含 7 个领域的测试集(2 个从未在训练中出现过!)

虽然人类绩效的 F1 值为 88.8%,但表现最佳的模型的 F1 值仅为 65.1%。这是一个很大的差距,机器必须要做大量的追赶工作!

# 创建 CoQA

像这样的数据集是如何创建的?

在亚马逊土耳其机械公司(Amazon Mechanical Turk)的帮助下,这个市场为需要人类智慧的人提供工作,比如给数据集贴标签。两名 AMT 员工将被配对,一人提问,另一人回答。回答者会被要求在文章中突出给出答案的文本部分,然后用不同于文章中给出的词来回答(从而变得抽象)。

所有这些都是在两个工人的对话中完成的。似乎下面的例子在论文中得到强调:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/be61f13936b15a117a8929cf6934713f.png)

## 文章的范围是什么

段落来自 7 个域,5 个在训练集中,2 个为测试集保留。它们是:

*   来自[的儿童故事 MCTest](https://uclmr.github.io/ai4exams/data.html#mctest)
*   来自[古腾堡项目](https://www.gutenberg.org/)的文献
*   初高中英语考试从[赛](https://www.cs.cmu.edu/~glai1/data/race/)
*   CNN 的新闻文章
*   来自维基百科的文章
*   来自 [AI2 科学问题](http://data.allenai.org/ai2-science-questions/)的科学文章(仅测试集)
*   [来自写作提示数据集的 Reddit 文章](https://www.reddit.com/r/WritingPrompts/)(仅测试集)

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/a3720faf91fb5f84206a0ec089707b7a.png)

他们还特意收集了问题的多个答案,因为问题被重新措辞,这有助于对话代理有更多的机会获得更好的分数,并实际上找到正确的答案。

# 与其他数据集相比

在这之前我们有什么?

嗯…我们有[班,斯坦福问答数据集](https://rajpurkar.github.io/SQuAD-explorer/)。[1.0 版本](https://arxiv.org/pdf/1606.05250.pdf)在维基百科上使用了 10 万多个问题,要求模型在回答问题时提取文章的正确部分。当决定这还不够时,我们得到了[版本 2.0](https://arxiv.org/pdf/1806.03822.pdf) 。这给 SQuAD 增加了 50,000 多个无法回答的问题,现在要求算法不仅要找到正确的答案,还要推理答案是否存在于文章中。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c9d55127b5cfd0fad3023cf85e600509.png)

这个任务还没有解决。

但是它也不能解决提取信息和理解对话中所要求的内容的问题。这就是 CoQA 成立的原因,正如它的第一个目标所概述的那样!

## 更多分析

CoQA 的创建者对小队和 CoQA 进行了分析,发现虽然大约一半的小队问题是“什么”问题,但 CoQA 的问题类型分布更广泛、更均衡。他们给了我们这个很酷的图片来描述:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/eaa94c2755c2e9adf0d998cb8ade8772.png)

让 CoQA 更难的是,有时它只有一个词的问题,比如“谁?”或者“哪里?”甚至“为什么?”

这些完全取决于上下文!作为人类,我们甚至不能在不知道他们被问到的背景的情况下开始尝试回答这些问题!

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/29e985b7ff01c4a2c56ae23fc8816239.png)

# 语言上的挑战

以防你还没拼起来,CoQA 非常非常难。它充满了被称为[共指](https://en.wikipedia.org/wiki/Coreference)的东西,可以是像代词一样简单的东西,但通常是当两个或更多的表达指同一件事(因此共指!).

这在 NLP(共指解析、代词解析等)中仍然是一个公开的问题。),所以将这一步合并到问答数据集中肯定会增加一些难度。

CoQA 中大约一半的问题包含显式的共指(像他、它、她、那个这样的一些指示)。近五分之一的问题包含隐含的共同参照。这是当你问一个类似“在哪里?”的问题时我们要求解决一些事情,但这是隐含的。这对机器来说很难。见鬼,有时候对人们来说甚至很难!

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/61b6121b150047110b023d6d3a2fe98a.png)

# 现在是挑战!

当前的模式如何应对这一挑战?答案是不好。

自己看:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0bda7c9305e21cbdf3d5cae137cbac1c.png)

CoQA 会帮助我们回答 ImageNet 对图像识别做了什么吗?只有时间会告诉我们,但事情肯定是令人兴奋的!

所以现在对我们所有人来说是一个挑战(就像我和 QuAC 摆的姿势一样)!我们都可以努力尝试解决这一挑战,将对话代理带入下一个发展阶段。CoQA 有一个[排行榜](https://stanfordnlp.github.io/coqa/),目前是空的!我们有责任走出去,努力应对这一挑战,努力解决这一问题,推动知识向前发展,也许还能登上排行榜,获得一点名气和一点骄傲;)

我们在那里见!

如果你喜欢这篇文章或者觉得它有任何帮助,为什么不递给我[一两美元](https://www.gofundme.com/hunter-heidenreich-research-fund)来资助我的机器学习教育和研究呢!每一美元都让我离成功更近一步,我永远心存感激。

# 珊瑚城市:Ito 设计实验室概念

> 原文:<https://towardsdatascience.com/coral-cities-an-ito-design-lab-concept-c01a3f4a2722?source=collection_archive---------1----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/155f4a68f8f9c03bfcd94a35fb91a7a5.png)

European Cities — Visualised as Corals

在过去的六个月里,我一直着迷于让城市网络看起来像活珊瑚的概念。城市形态的变化模式是由其道路网络决定的;一个复杂的,看似有机的联系,让人们穿越他们的城市。就像珊瑚的枝干一样,它们有一种模式和功能,我选择展示这种模式,并对其进行处理,使之变得更加概念化。然而,虽然它们美得令人难以置信,但它们是从行驶时间集水区的各种地理空间分析中得出的,这也使它们具有一定的信息量。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/72593038d675f8cfe478af3c3db6724f.png)

Top 42 places to live (Mercer Livability Index)

# 项目:

## Kantar 信息很美大奖 2018。

在过去的 6 个月里,我已经制作了如此多的珊瑚,我认为现在是时候开始最大的应用了;绘制 40 个最宜居城市的地图,进行分析并制作动画。我觉得这个概念和视觉效果很强,但是城市列表需要有相关性,不要像我早期的一些“概念”项目那样随意。我决定使用与经济学人信息部类似的宜居指数,但通过一家名为 Mercer 的公司,该公司每年发布一次排名([https://mobility exchange . Mercer . com/Insights/quality-of-living-rankings](https://mobilityexchange.mercer.com/Insights/quality-of-living-rankings))。经美世允许使用他们的名单,我们创建了以下内容提交给奖项。

> 是什么造就了一座伟大的城市?是政治稳定吗?低犯罪率?获得教育或医疗保健?我们采用了一个衡量标准,看看人们在城市内移动有多容易。我们计算了你在 30 分钟内可以从每个市中心(开车)走多远。由此产生的“珊瑚结构”以一种新的方式展示了运输数据,揭示了美丽的有机形式。每条线代表一个城市的动脉,代表一条从市中心出发的可能路线。
> 
> 我们将这一技术应用于美世生活质量城市排名中的前 40 个城市。其结果是对我们如何在世界上最大的一些城市走动有了一个独特的视角。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/83094b73b3c51303f674f74bb464a595.png)

Hi-resolution A0 poster

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/fd162f715c7b1684207dc6902044ac25.png)

Printed, mounted and framed!

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/74668ce9c01597b1a943fe4943ecf41b.png)

Close-up of detail

4k animated version

A0 海报印在墙上看起来棒极了!4k 动画版本缓慢地迭代通过一个不断增长的城市网格 1,12,20,最后是 40。我确实想把这个扩展到 80 度,但是 40 度之后,珊瑚的细节就消失了,除非你有一个巨大的屏幕。

## **英国珊瑚城市:**

这是我第一次尝试用小倍数的方法来完成这个项目,并分析了英国不同城市的 30 分钟车程集水区。行驶时间分析基于对零交通流量的乐观看法,因此呈现的集水区比您预期的要宽得多。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/cc2bbee9a9d4fcd978edc8045537db62.png)

高分辨率静态图像以清晰的模式显示了地层的复杂性,其中河流、海洋和山脉等物理特征影响着网络。我还制作了一个 4k 的动画版本,在上面的 Vimeo 链接中可以看到,动画在视频结束时逐渐变慢。

## 欧洲版:

此后不久,我着手制作一个地理上更大的版本,分析一些主要的欧洲城市。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/08aec213e6bcedaa2e5f84970c228f5d.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b90660658ea65b694c2bf570607bcd94.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9236ad056b350b93a9295babb1858056.png)

这种风格从最初的蓝色网格基础略微演变成一种浅色调色板,环境遮挡和阴影更加明显。“珊瑚城市”的概念传到了德国地图学杂志“Kartographische Nachrichten”的好人们手中([http://www . Kartographische-Nachrichten . de/Kartographische-Nachrichten/aktuelles-heft . html](http://www.kartographische-nachrichten.de/kartographische-nachrichten/aktuelles-heft.html)),他们渴望在最新杂志的封面上刊登欧洲版本。浅色的调色板看起来很可爱…

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/bb3ae2dd45a8426e8e37a4d01e8566ad.png)

## 经济学人智库:宜居指数

上个月,EIU 发布了 2018 年宜居指数,对所有主要城市的宜居指数进行了排名(【http://www.eiu.com/topic/liveability】T2)。我们认为这将是一个伟大的想法,制作一个“珊瑚城”版本的十大名单,当然还有一个动画版本。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0448e41722b6b566a90ca02839249238.png)

# **它们是怎么做的?**

让我们解释一下它们是如何计算的,以及它们显示了什么,为了做到这一点,让我们更仔细地看看我最喜欢的珊瑚之一——东京。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/f8e887d616cc86292272bc824e713ed6.png)

Tokyo City — 30 minute catchment

我从东京市及其周边地区的基本 OpenStreetMap 网络开始。使用各种方法,我可以计算从中心到指定行驶时间的集水区。在伊藤世界(www.itoworld.com),我们有一个内部路由系统允许我们这样做,但是,有一堆其他的方法和软件可以让你计算驾驶时间(ESRI,PgRouting,GraphHopper,Here API 等)。值得注意的是,在大多数情况下,网络集水区是以零流量计算的,因此结果非常乐观。然而,我们已经尝试从 Here API 导入基于拥塞的等时线,并将其转换为珊瑚,这相当成功(见下文)

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/dcba494148d9f0468d81c6e68f457b1d.png)

Comparing congestion catchments in the worst/best cities using data from Here API.

汇水区分析将给出一个粗略的区域,然后我需要把它变成珊瑚,为此,线性连接上的每个顶点都给出一个距离市中心的距离值。我可以使用这个距离度量作为链接的高度值,创建一个 3d 网络,随着与中心的距离增加而增加。

我也将一个“权重”值应用于链接,这与道路分类无关,但是它经常被链接。权重与连接到分支的链接数量成比例,因此较厚的网络有更多连接的主干网络,从而创建珊瑚状外观,这也表明路由网络的较繁忙部分。

然后,每个链接都经历了一个矩形几何体的“扫描”,创建了一个物理存在,我们可以在渲染过程中应用环境遮挡。

颜色渐变纯粹是一种美学添加,只是从中心映射渐变。动画链接出现在距离的顺序导致迷人的珊瑚生长…

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/6177ed291a17c8c463a0160e194e816e.png)

UK Cities — Coral Animation

## 定制版本

我的妻子通常对我的工作不感兴趣:)然而,她对珊瑚城的海报很感兴趣,以至于她委托我(报酬是无尽的茶和咖啡!)为房子生产一个。作为一个历史极客,主题是“历史上重要的珊瑚城市”我们仍在考虑框架选择,但下面是海报的截图。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/3e86b5512f90626bd6c5c78dcfb7268c.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/dd0d9441c02acc04e25ba7b320455163.png)

## **Cinema4d/Blender 渲染:**

在整个过程中,我对将我们在伊藤世界的可视化套件中的分析移植到其他 3d 软件包(如 Cinema4d)的可能性越来越感兴趣。这很棘手…我们的分析工具分析网络的每个链接,节点到节点的链接用每个节点的高度值分割。然后,该值被放入具有 z-min 和 z-max 属性的着色器中,该属性创建链接的高度。类似的方法也用于连接厚度。所以将它导入其他包并不简单,会产生大量的 CSV 文件。我确实为 Cinema4D 开发了一种方法,但是这个过程非常耗时,但是确实产生了一些可爱的渲染效果。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/a983a9617e08c219be9f2ff49bc45340.png)

Cinema4d 有一个非常方便的“结构”标签,你可以通过 x,y,z 坐标导入线性路径。顺带一提,某天才创建了一个可以批量导入链接的脚本([https://www . C4 dcafe . com/ipb/forums/topic/91550-create-spline-from-CSV-file/?page=2](https://www.c4dcafe.com/ipb/forums/topic/91550-create-spline-from-csv-file/?page=2) )这意味着只要我的每个链接都是一个单独的 CSV 文件,我就可以使用该脚本在 Cinema4d 中重建样条几何图形。不利的一面是每个珊瑚都有成千上万的链接,所以大量的 csv

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/d1d25f1dd7eb05d0e8b8ceaf0359106b.png)

一旦我所有的链接被导入,我就面临一个新的问题。每个单独的链接都是一条单独的样条线,尽管它们与下一条样条线重叠,但实际上它们并不相连。当“扫描”样条线时,这就成了问题,你最终得到的不是漂亮的 3d 动脉,而是成百上千的小动脉连接在一起,这是 a)资源猪 b)看起来很丑。治愈?“快速花键连接器”,我的救命恩人([https://cgtools.com/fast-spline-connector/](https://cgtools.com/fast-spline-connector/))物有所值。使用该工具意味着,只要将阈值设置得较低,任何重叠的样条线都可以连接到更长的样条线。因此,这是一个相当漫长的过程,毫无疑问可以简化,但无论如何,我在最终导入的城市中获得了很多乐趣。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8ed0a59ff638419d5577009104dd5721.png)

Early concepts for render lighting setups.

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/686ad86d6f4f622e3954921e43389a59.png)

Frankfurt light test render with baseline OSM on the base.

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/fc9ba79eb0171535a25125f3857ecaee.png)

Frankfurt dark test render with baseline OSM on the base.

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/155f4a68f8f9c03bfcd94a35fb91a7a5.png)

Cinema4d HDRi lighting render.

我还实验了一些相当有问题的渲染…

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/ba2ecf716a4fa9439e7b510e30ba7fbe.png)

Jade Coral City?

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9f4ad3207a522c6cbca4d9d94688b6e5.png)

Inverted Coral City.

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c3ab64badac14624529251f9bf80177d.png)

Some odd volumetric lighting tests…

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8248323d5b9abea7bc54cf353b8625cc.png)

Early idea of plinth renders.

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/ef25f375be89339f52ab4480ce55e902.png)

Early attempt at Corals in water.

# 接下来是什么?

我喜欢 Kantar 条目打印出来的样子,我热衷于探索一些创作定制作品的选项,已经为朋友和家人创作了几个版本,但是我不确定这在更大范围内如何工作。我们内部也在讨论 3d 打印一些更令人印象深刻的城市。谁知道呢,到了圣诞节,你可能会在约翰·刘易斯的货架上看到“珊瑚城的小玩意”

# 面向 iOS 开发者的核心机器学习

> 原文:<https://towardsdatascience.com/core-machine-learning-for-ios-developers-7f2a4b19ec08?source=collection_archive---------0----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/78c17d8ad72e78dfa8632fbdb6e641ec.png)

在 WWDC 2017 上,苹果为开发者推出了一种简单的方法,用[**Core ML**](https://developer.apple.com/documentation/coreml) 为他们的 iOS 应用添加 AI 功能。这可以用于各种领域,如对象检测、情感分析、手写识别、音乐标记等。所有这些都可以集成到一个名为 **Core ML Model** 的文件和几行代码中。😏

**什么是核心 ML 模型⁉**

它是将训练好的模型转换成 Apple 格式的模型文件(。mlmodel),它可以添加到您的项目中。

**什么是训练有素的模特‼︎⁉︎**

它是由机器的训练过程产生的产物。训练模型的过程包括向*学习算法*提供*训练数据*以供学习。

因此,由于核心 ML 模型是关键,我选择使用苹果公司提供的模型,而不是自己转换一个经过训练的模型。目前有 4 个选项可用: *Places205-GoogLeNet,ResNet50,Inception v3,VGG16。*

# 演示时间

要运行演示应用程序或使用 CoreML,你需要获得 [**Xcode9**](https://developer.apple.com/download/) 测试版(振作起来,这是真正的测试版😖).

> 该应用程序将允许用户从照片库中选择一张照片,然后智能模型将推断出主要对象。

打开 Xcode 并创建一个新的单个应用程序视图项目。转到你的 Main.storyboard 并为应用程序设置你想要的 UI。我选择了一个简单的 UI,一个按钮打开图库,一个图像视图显示选中的图像,一个文本视图显示预测结果。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/53723fb050e725a8f368203e68c5a52d.png)

然后将按钮、图像视图和文本视图挂接到视图控制器。设置一个*UIImagePickerController a*after wards,及其委托方法*diddfinishpickingmediwithinfo*并从信息字典*中检索图像。*

此时,您的项目应该可以运行了。您可以选择一个图像并在图像视图中查看它。*提示:你可以在模拟器上打开 safari 并保存一些图像,在你的图库中创建一些变化。*

# 添加核心 ML

现在到了激动人心的部分:让我们从 [**苹果**](https://developer.apple.com/machine-learning/) 下载实际的模型文件。我选择了[**Inceptionv3**](https://docs-assets.developer.apple.com/coreml/models/Inceptionv3.mlmodel)**型号因为它功能强大并且文件大小合理。**

**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/5a2209d2ffdd532845ed07e9484f3331.png)**

**一旦你得到了。mlmodel 文件,将其拖放到您的项目中。确保将其添加到您的**应用目标**(如果您有测试目标,不要将其添加到测试目标,因为这不会生成模型类)。然后从项目导航器中选择文件。**

**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b205549cf2c2c5b65ff73bbd98bb7ba0.png)**

**你可以看到一些与模型相关的元数据。但最重要的是,您可以看到自动生成的**模型类**,它将在我们的代码中使用。我们还知道该模型将一个图像作为输入,将一个字典和字符串作为输出。**

# **CoreML & Vision**

**为了在我们的代码中使用智能,我们必须导入 CoreML 和 Vision。CoreML 被导入,所以我们可以在我们的视图控制器中使用自动生成的**模型类**。由于图像输入属于 *CVPixelBuffer* 类型, **Vision** 将通过给它一个用作输入的 *CGImage* 来使我们的生活变得更容易。**

**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/242657586a383a93867692c6f27ed3d4.png)**

**Data types used to interact with the mlmodel**

**值得注意的是,视觉与将图像作为输入的模型相关。例如,如果我们的模型采用字数统计,那么就应该使用 NLP (NSLinguisticTagger)来代替。**

**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/de46b77acb7f5b43ba4a2fc044472743.png)**

**Vision and NLP are build on top of Core ML**

# **履行**

**现在回到我们的视图控制器。从导入 CoreML 和 Vision 开始。创建一个新函数,姑且称之为 *processImage()。*请注意,该实现特定于 Vision,如果您使用另一个将 String 或 Double 作为输入的模型,您就不必以这种方式使用它。**

**为了提交输入,我们首先使用我们的 *Inceptionv3* 模型初始化一个 *VNCoreMLModel* 。然后创建一个 *VNCoreMLRequest。*最后设置*一个 VNImageRequestHandler* 并以请求为参数*调用 perform。***

**为了检索输出,我们必须使用 *VNCoreMLRequest* 闭包,它有一个包含*结果*属性[*VNClassificationObservation*]的*请求*对象。就是这样,这是输出。⭐️✨**

**现在将 *processImage()* 添加到*imagePickerViewController*的委托方法中,并更新文本视图。下面是 90 行代码下的视图控制器的完整代码:**

**让我们运行应用程序,看看预测结果是什么样的。😍**

**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/51526d1794173da6150912809c702a26.png)**

**iOS Simulator screenshot**

**我期待你的反馈🤓**

**🤖[在此下载完整项目](https://github.com/ayoubkhayatti/GuessImageCoreML)🤖**

# 通过机器翻译和分类纠正文本输入

> 原文:<https://towardsdatascience.com/correcting-text-input-by-machine-translation-and-classification-fa9d82087de1?source=collection_archive---------9----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0e7cb713237554fe04fada6a972672ae.png)

“person holding eyeglasses” by [David Travis](https://unsplash.com/@dtravisphd?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)

最近,我正在做一个光学字符识别(OCR)相关的项目。挑战之一是预训练的 OCR 模型输出不正确的文本。除了 OCR 模型的性能之外,图像质量和布局对齐也是文本错误的主要来源。

看完这篇帖子,你会明白:

*   光学字符识别错误类型
*   基于词典的
*   基于内容
*   统计机器翻译
*   基于特征的词分类

# OCR 错误类型

*   字符检测:识别不正确的字符。这可能是由于图像质量差和方向不正确造成的。
*   单词检测:无法检测文本。这可能是由字符检测错误引起的。
*   分段错误:无法正确分段字符和/或单词。

*这种方法是最简单的一种解决方案,它可能不需要任何机器学习技能,只需要编程语言和正则表达式技能。通过计算 [Levenshtein 距离](/measure-distance-between-2-words-by-simple-calculation-a97cf4993305)(编辑距离)并从字典中找到最短的词典来替换“不正确的”单词,可以捕捉不正确的单词。你可以通过这个[故事](/step-out-from-regular-expression-for-feature-engineering-134e594f542c)进行基于词汇的识别。局限性在于需要大量的词汇,并且还需要定义特定领域的数据。*

*另外,你可以访问[拼写校正器](/correcting-your-spelling-error-with-4-operations-50bcfd519bb8)故事和[符号拼写](/essential-text-correction-process-for-nlp-tasks-f731a025fcc3)故事来对它有更多的了解。*

# ****基于上下文****

*第二种方法计算单词序列的可能性。在语言学中,我们注意到大多数语言中都有一定的模式(或语法)。给定模式和分布,我们了解单词序列的概率。然而,限制是高频词,如停用词可能会支配结果。此外,罕见但重要的单词可能会受到影响。*

# ****统计机器翻译****

*Afli 等人提出了用于光学字符识别(OCR)纠错的统计机器翻译(SMT)方法。机器翻译(MT)用于将源语言翻译成目标语言。在这个特定的上下文中,源语言是 OCR 输出,而目标语言是正确的文本。*

*典型的机器翻译输入是一个单词序列(OCR 输出),输出是另一个单词序列(校正文本)。而统计方法的目标是最大化后验概率。另一个不同是,作者不仅评估单词级别模型,还评估[字符级别](/besides-word-embedding-why-you-need-to-know-character-embedding-6096a34a3b10)模型。*

*单词错误率(WER)和双语评价替角(BLEU)在评价中被选中。从实验结果来看,单词级模型略好于字符级模型。*

*![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/1b4ac4c5bc9fffc0a5f9b915f2c5b5d1.png)*

*Evaluation Result (Afli et al., 2015)*

# ****精选基础词分类****

*Kissos 等人提出了另一种方法来修复来自 OCR 的文本错误。他们使用字符和单词分类方法来识别不正确的单词并进行修正。*

*作者提出候选排序器和校正决策器来判断单词是否应该被校正的文本替换。首先,将输入传递给候选排序器,并寻找候选替换的可能性。一旦候选词被确定,它将被传递到另一个模型,即修正决策器,以分类是否应该替换原始单词。*

*单词令牌将首先通过候选排序器,其功能包括:*

*   *混淆权重:讹误纠正对的权重属性。*
*   *单字频率:特定单词的总计数。*
*   *正向二元模型频率:二元模型(由前一个单词构成)的最大字数。*
*   *反向二元模型频率:二元模型(由下一个单词构成)的最大字数。*

*![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/086b7f23d9f696066fbf41f15c2a7577.png)*

*Example of the Candidate Ranker feature (Kissos et al., 2016)*

*候选人排名,随后是修正决策者。一旦可能性候选(替换的单词)被识别。校正决策器判断原始单词是否应该被该校正单词替换。功能包括:*

*   *置信度:OCR 输出度量。*
*   *词频:OCR 文本的总字数。*
*   *比例字典功能:与“候选人排名器”中使用的功能相同。*

*![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9aa7df5d312a4b37eb828ff28ac9a408.png)*

*Example of the Correction Decision Maker feature (Kissos et al., 2016)*

# *参考*

*Afli H,Barrault L .,Schwenk H ...2015.[使用统计机器翻译进行 OCR 纠错](http://www-lium.univ-lemans.fr/~barrault/papers/afli_cicling2015.pdf)*

*基索斯一世,德肖维茨。普通..2016.[使用字符校正和基于特征的单词分类的 OCR 错误校正](https://arxiv.org/pdf/1604.06225.pdf)*

# 通过两个距离纠正你的拼写错误

> 原文:<https://towardsdatascience.com/correcting-your-spelling-error-with-4-operations-50bcfd519bb8?source=collection_archive---------13----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/1166842258fdd386ec38f42092f29e28.png)

“red pencil on top of mathematical quiz paper” by [Chris Liverani](https://unsplash.com/@chrisliverani?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)

在处理文本时,我们可能需要处理不正确的文本。尽管我们仍然可以使用[字符嵌入](/besides-word-embedding-why-you-need-to-know-character-embedding-6096a34a3b10)和[单词嵌入](/3-silver-bullets-of-word-embedding-in-nlp-10fa8f50cc5a)来计算类似的向量。它对看不见的数据和词汇表之外的东西很有用(OOV)。不过如果能纠正错别字就更好了。

错别字可以在几种情况下产生。如果您使用光学字符识别(OCR),OCR 输出的后处理步骤是非常关键的部分,因为 OCR 引擎会引入一些错误,这些错误可能是由图像质量差和 OCR 引擎错误引起的。另一个错别字来源于人类。当你在聊天机器人项目工作,输入来自人类,它必须包括错别字。

为了获得更好的结果,最好是尽早纠正错别字。看完这篇帖子,你会明白:

*   拼写纠正器
*   履行
*   拿走

# 拼写纠正器

Norvig 在 2007 年实现了[一个非常简单但是惊人的库](https://norvig.com/spell-correct.html)来纠正拼写错误。通过不同的方式计算可能的候选校正,并从中找到最可能的单词。有两个阶段来寻找可能的候选词。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b237ef96dc603baf009ecc62ae6e4dc1.png)

“brown rail train” by [Johannes Plenio](https://unsplash.com/@jplenio?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)

首先,当原始词和候选词之间的编辑距离为 1 时,使用 4 种不同的方法生成新词。不同于 [Levenshtein 距离](/measure-distance-between-2-words-by-simple-calculation-a97cf4993305),它认为:

*   删除:删除一个字母
*   换位:交换两个相邻的字母
*   替换:把一个字母换成另一个
*   插入:添加一个字母

以“edward”为例,“edwar”、“edwadr”、“edwadd”、“edwward”分别是“删除”、“换位”、“替换”、“插入”的例子。显然,将会产生大量的无效单词。因此,它会按给定的词汇过滤掉(它在库中称为“已知词”)。为了扩大潜在的候选,算法再次重复这个步骤,但是编辑距离是 2。

第二部分是根据概率从可能的候选者中选择候选者。例如,“Edward”在给定字典中的出现次数是 2%,概率是 0.02。概率最高的词将从潜在候选词中选出。

# 履行

为了方便拼写检查,需要语料库。为了便于演示,我简单地使用了 sklearn 库中的数据集,没有进行预处理。您应该使用特定领域的数据集来为您的数据构建更好的语料库。

*建立语料库*

from collections import Counter
from sklearn.datasets import fetch_20newsgroups
import recorpus = []
for line in fetch_20newsgroups().data:
line = line.replace('\n', ' ').replace('\t', ' ').lower()
line = re.sub('[^a-z ]', ' ', line)
tokens = line.split(' ')
tokens = [token for token in tokens if len(token) > 0]
corpus.extend(tokens)corpus = Counter(corpus)


*校正*

spell_corrector = SpellCorrector(dictionary=corpus)
spell_corrector.correction('edwar')


*输出为*

edward


# 拿走

要访问所有代码,你可以访问我的 github repo。

*   拼写校正器**不考虑上下文**,而仅仅考虑拼写。然而,鉴于它是在 11 年前(2007 年)推出的。这是一个神奇的工具。
*   从作者编码来看,预处理结果应该只保留英文字符和小写字母。换句话说,**特殊字符和数字要去掉**。
*   性能(就速度而言)非常快。

# 关于我

我是湾区的数据科学家。专注于数据科学、人工智能,尤其是 NLP 和平台相关领域的最新发展。你可以通过[媒体博客](http://medium.com/@makcedward/)、 [LinkedIn](https://www.linkedin.com/in/edwardma1026) 或 [Github](https://github.com/makcedward) 联系我。

# 参考

[拼写校正器](https://norvig.com/spell-correct.html)

# 相关性一终端来到佐治亚理工学院

> 原文:<https://towardsdatascience.com/correlation-ones-terminal-comes-to-georgia-tech-cb134c6c268e?source=collection_archive---------5----------------------->

## 有没有编过玩电子游戏的算法?

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/ac6245c78b7268b4ab8f6b65e685819b.png)

欢迎来到[航站楼](https://terminal.c1games.com)。

吃披萨,穿酷酷的 t 恤,参加四个小时的黑客竞赛,你就有了佐治亚理工学院的游戏之夜。我以竞争对手的身份参加了这次活动,以下是我认为 Terminal(字面上)改变游戏规则的原因。

在计算机科学领域,发展技能是至关重要的。通过上课、个人项目和实习,像我这样的学生为了吸引顶级公司,不断地竞争建立他们的简历。

然而,我们经常忽略硬币的另一面。就像我们学生渴望被雇佣一样,雇主也在不断寻找顶尖的科技人才。招聘已经成为一个蓬勃发展的行业,而 Correlation One 是这个领域最具颠覆性的新参与者之一。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/cdc8a7aa09b6333625f31f99bc83a2f8.png)

Members of the Correlation One team and Data Science @ GT helped to run the event.

在过去的一年里, [Correlation One](https://www.correlation-one.com) 的团队一直在开发终端,这是一款定制的塔防游戏,通过提交的算法自动进行游戏。在佐治亚理工学院的游戏之夜,这是同类活动中的第一次,超过 230 名学生注册整夜编写他们自己的算法。

游戏之夜得到了很好的宣传,并在一个学生组织的帮助下举办——T4 数据科学@ GT 。我的许多朋友都来参加比赛,都对这项活动的独特性质感兴趣。毕竟,你很少有机会将编码和视频游戏与赢得现金奖励的前景结合在一起!

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/7c0de86e978694c4553caeb16040313e.png)

The event was held at the Clough Learning Commons.

在解释完规则后,每个人都开始研究他们的算法。提供的 API 有很好的文档,视频教程也很棒。本科生、研究生和博士候选人都有同等的机会证明自己的优势。精英精神确实闪耀着光芒,这也是我最喜欢的活动特征之一。

终端是一款塔防游戏,但绝不是简单的一款。尽管提供的 starter 算法只有几百行 Python 代码,但要熟悉游戏的规则和大致范围确实需要一些时间。在活动的第一个小时,提交的竞争算法非常少。随着我和我的同伴们努力解决问题,逐渐熟悉了这个游戏,竞争的感觉开始主导这个房间。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/6322130b49834f65dbc4bce369c38390.png)

Tech students worked through the night to improve their submissions.

当我整晚继续迭代我的算法时,我对一个团队用终端创建的相关性有了真正的理解。我可能和周围的人一样埋头于电脑中,但通过上传我的算法并观看他们播放其他提交的内容,我正以一种精神上具有挑战性的方式直接与我的同行互动。

传统的编码挑战需要大量的准备工作,通常需要掌握数百个实践问题,而 Terminal 将你置于一个进入的障碍仅仅是知道如何编程的舞台上。除此之外的一切都是开放式的,并测试现实世界的思维,这使它明显不同于我参加过的任何此类比赛。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/31e1e4fe3b46bf3339e2560093ba9c74.png)

Terminal incentivizes creativity and strategic thinking unlike most popular recruiting tools.

你可以自由地想出新的策略来打击对手,并按照自己的意愿实施适应性强的复杂解决方案。创新的自由和潜力是独一无二的,令人兴奋的,只有在时间敏感的环境中,让你的工作与竞争对手的工作相竞争,这种挑战才能支撑你的创新。

随着提交截止日期的临近,房间里的能量明显上升。在最后一轮比赛中,所有人都可以看到直播,观众欢呼着算法之间的较量。看到我的算法与我的对手面对面,真是太激动了!

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9f455216f72c6a9459da97cb9553c1c2.png)

The final round of the night, where my algorithm (in pink) faced off against 2nd place (in blue).

我对我们提出的各种策略和解决方案感到惊讶,终端作为招聘工具的真正价值变得非常清楚。这个游戏不仅能很好地激发学生的兴趣,还能挑战我们的战略思维和适应时间敏感的环境。我认为 Correlation One 已经击中了要害,我期待着在未来的几个月里看到 Terminal 的变化,因为来自全国各地的学生将在未来的比赛中竞争。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/99fb2e42d026215bcb3f007c95f37e25.png)

The award ceremony from the night! To the left of me is runner-up Neil Thistlethwaite.

在终端上随意查看 Correlation One 的官方[博客帖子](https://medium.com/@CorrelationOne/the-future-of-talent-strategy-is-here-2e7d07698988)。更好的是,如果你有竞争精神和 Python 的工作知识,那就自己去玩吧!

# 相关性与因果性:一个例子

> 原文:<https://towardsdatascience.com/correlation-vs-causation-a-real-world-example-9e939c85581e?source=collection_archive---------0----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/45cb93701dc6cd5889fe33b57af63d4e.png)

**怀疑地看待现实世界的统计数据**

令人惊讶的是,在我们收到的大量电子邮件中,有一些深刻的见解等待着我们去发现。当我漫不经心地浏览我的收件箱时,我浏览了一封来自学校留学办公室的邮件,里面有以下关于海外留学好处的信息:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/38ef2abc9e0480970b64717cc8ea960c.png)

立即引起我注意的是 90 年代的那些数字——显然,出国留学让你对研究生院和雇主具有不可抗拒的吸引力。我惊讶于在另一个国家学习所带来的学术和职业利益是如此之大。我的第二个想法是:太糟糕了,我没有选择利用这些好处,我很快存档了电子邮件,在我开始后悔任何进一步的人生决定之前。然而,关于这些信息的一些东西一直困扰着我。在这个假新闻占主导地位的时代,我一直试图花更多的时间有意识地思考各种说法和统计数据,虽然这不在同一社会退化水平上,但我得出的结论似乎有些不对劲。几天后,在听一个对[数据持怀疑态度的播客](https://dataskeptic.com/podcast/)时,我突然想到:我曾假设出国留学*会让*学生有更好的成绩和职业前景,而所有的统计数据显示这两者是相关的。

我们大多数人经常犯这样的错误,无意中混淆了相关性和因果关系,这种倾向被媒体标题所强化,如音乐课提高学生的表现,或者呆在学校是长寿的秘诀。有时候,特别是在健康方面,这些倾向于不可思议的事情,比如《卫报》的头条声称吃鱼会减少暴力。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/5e46e094de4ebfb864ec746c9f3ba37e.png)

The work of the powerful tuna lobbying industry

这些文章中的共同问题是,他们采用两种相关的趋势,并将其作为一种现象引起另一种现象。真正的解释通常不那么令人兴奋。例如,上音乐课的学生可能在学校表现更好,但他们也更有可能在一个非常重视教育和取得学术成功所需资源的环境中长大。因此,不管有没有音乐课,这些学生都会有更高的学习成绩。上音乐课和在学校表演碰巧是同时出现的,因为它们都是相似背景下的产物,但一个不一定会导致另一个。同样,在校时间越长的人通常拥有更多的资源,这也意味着他们能够负担更好的医疗保健。大多数情况下,这些错误并不是出于有意欺骗([尽管这种情况确实发生过](https://www.mediamatters.org/blog/2011/12/12/today-in-dishonest-fox-news-charts/185162)),而是出于对因果关系概念的诚实误解。统计数据,尤其是留学邮件中的数据,显示的是一种[选择偏差](http://sphweb.bumc.bu.edu/otlt/MPH-Modules/EP/EP713_Bias/EP713_Bias2.html)。在每项研究中,被观察的个体并不来自社会的代表性部分,而是来自相似的群体,这导致了一个扭曲的结果。

想想统计数据显示,在国外学习的学生按时毕业的可能性要高出 19%。虽然出国留学可能确实在某种程度上激励了落后的学生按时毕业,但更可能的解释是,选择出国的学生首先是那些在学术上处于更好地位的学生。不管他们是否去了另一个国家,他们都会以高 GPA 按时毕业。去另一个国家学习一年需要做大量的工作和准备,而有足够信心这样做的学生是那些学业有成的人。在这个现实世界中,选择偏向于更好的学生。出国留学的学生样本并不能代表学生的整体情况,相反,它只包括准备最充分的学生,因此,这一群体拥有更好的学术和职业成果也就不足为奇了。

事后看来,出国留学的经历可能看起来很棒,但如果我们只选择最优秀的学生,让他们做任何事情,那么说这种现象导致更好的成绩将是误导。比方说,我们拥有一家瓶装水公司,我们希望收集一些积极的数据来帮助销售。我们雇几个学生站在荣誉班外面,只把我们的水给优等生。然后,我们进行了一项研究,最终表明喝我们品牌饮料的学生成绩更好。因为我们选择了一组特定的受试者来参与我们的研究,我们可以让它看起来像是我们的水*导致了*成绩的提高。

海外留学统计数据来自所谓的[观察研究。](https://en.wikipedia.org/wiki/Observational_study)观察性研究不是构建一个实验,而是观察现实世界中无法控制自变量的一些过程,在这个案例中,是选择出国留学的学生。观察性研究无法证明因果关系,只能证明不同因素之间的联系(比如成就和在另一个国家学习)。为了证明一个过程导致了另一个过程,需要进行随机对照试验,受试者代表整个人群。在这种情况下,进行随机对照试验需要从各种学习表现中随机选择一部分学生,送一些去国外学习,留一个对照组在家。然后我们可以分析结果,以确定两组之间是否有显著差异。如果有,那么我们可能会进行更多的研究,控制更多的变量,直到最终我们确信没有隐藏的影响,我们可以建立因果关系。

我向 CWRU 留学办公室指出了这些观察结果,随后是一次体面而富有成效的谈话。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/ff0760ce932b6e50a7f020b291c804fd.png)

Civility and Honest Discussion. On Twitter!

我发这个帖子,并不是想给办公室打电话。尽管这封邮件确实写道:“这只是这个新年决心能带来的一些好处,”但它并没有声称确切的因果关系。然而,当一个单一的话题被大量的事实包围时,我们的自然倾向是画出一个因果关系,这是营销人员和公司经常利用的一种倾向。我相信这个案例中的所有统计数据都是有效的,但我们仍然需要避免指定因果关系。没有随机对照试验,我们不能说一种活动引起了另一种活动,我们只能说两种趋势是相关的。

这是一个小例子,但它说明了一个极其关键的问题:我们所有人,甚至每天都在使用这些概念的研究生,都可能被统计数据所愚弄。人类[自然会看到不存在的模式](http://bigthink.com/endless-innovation/humans-are-the-worlds-best-pattern-recognition-machines-but-for-how-long),我们喜欢讲述一个我们认为正在发生的事情的连贯故事(叙事谬误)。然而,这个世界通常没有明确的因果关系,我们[必须满足于相关性](https://www.forbes.com/sites/gilpress/2013/04/19/big-data-news-roundup-correlation-vs-causation/)。这种世界观可能会让头条新闻变得不那么令人兴奋(事实证明[巧克力并不是一种神奇的食物](https://www.vox.com/science-and-health/2017/10/18/15995478/chocolate-health-benefits-heart-disease),但这意味着你不会因为可疑的证据而被骗去购买不符合你最佳利益的产品或采取不符合你最佳利益的行动。此外,我们可以与他人分享我们的经验,并创建一个持怀疑态度的社区,在这个社区中,我们为了自己的利益而不是公司的底线做出合理的决策。

一如既往,我欢迎建设性的批评和反馈。可以在推特上找到我,电话是 [@koehrsen_will](https://twitter.com/koehrsen_will) 。

# 亚马逊、谷歌、IBM 和微软的云情感分析成本比较

> 原文:<https://towardsdatascience.com/cost-comparisons-for-cloud-sentiment-analysis-at-amazon-google-ibm-and-microsoft-bbe92cb620d7?source=collection_archive---------14----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c418e44ac786103bc8271839db6451c6.png)

Source: Library of Congress

我们生活在一个激动人心的时代,尤其是对于负责快速分析大量文本的数据科学家来说。除了像 R Studio 和无数现成软件这样的免费 ide 包之外,大型云玩家已经通过 API 将他们的机器学习工具交给你使用。单独来看,云服务工作得很好,但将它们结合起来([就像我们最近的](https://www.deducive.com/blog/2018/7/28/sentiment-analysis-in-the-cloud-round-2-azure-text-analytics-et-al))会产生更好的结果:我们发现[结合亚马逊理解、谷歌云、IBM 沃森和微软 Azure](https://www.deducive.com/blog/2018/7/28/sentiment-analysis-in-the-cloud-round-2-azure-text-analytics-et-al) 的情绪分析结果可以正确预测 78%的情绪。

使用这些服务非常简单。计算它们的成本,以及这些成本之间的比较,完全是另一回事。在本文中,我们将比较四家最大的云计算公司在各种场景下的成本。

# 比较苹果、橘子和香蕉

亚马逊的定价是出了名的复杂,它是少数几个具有开放 API 的云服务之一,可以提供具体的定价示例,所以我们将使用它作为基准。

场景是这样的:你有 10,000 条顾客评论,每条平均 550 个字符。你才刚刚开始,想让事情变得简单,所以你只想知道每个评论的整体情绪。更复杂的分析技术在每个平台上都是可能的,包括主题分析和定制建模。但是我们现在不需要这个。

为了便于比较,我们将排除任何存储成本,因为这也因平台而异。

# 亚马逊理解

虽然这个简单的例子可以在[亚马逊理解](https://www.ibm.com/cloud/watson-natural-language-understanding/pricing)上免费执行,但我们将假设最低标准定价层(同样值得注意的是,免费层有 12 个月的使用期限)。亚马逊将每 100 个字符作为一个“单位”

10,000 requests X 550 characters/request = 60,000 units
60,000 X $0.0001 per unit = $6


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/6a15351f143c1ea2004550a84ba79615.png)

Amazon Comprehend Pricing

# 谷歌云自然语言

谷歌云自然语言免费层最多有 5000 条记录,所以你不得不为这个简单的 10000 条记录付费。然而,每条记录有 1000 个字符的限制,所以我们示例中的每条记录只需要一个单元。

10,000 requests X 550 characters/request = 10,000 units
10,000 X $1 per 1,000 units = $10


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/14f3048d7fad77406a57e42afcae6fbb.png)

Google Cloud Natural Language Understanding Pricing

# IBM 沃森

[IBM Watson Natural Language Understanding](https://www.ibm.com/cloud/watson-natural-language-understanding/pricing)在其免费定价层中每月允许多达 30,000 个“自然语言单位(nlu)”,但我们将考虑最低定价层进行比较。每个 NLU 允许 10,000 个字符和两个“丰富特性”,因此我们的示例需要 10,000 个 nlu。

10,000 requests X 550 characters/request = 10,000 NLUs
10,000 X $0.003 per NLU = $30


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0b7c92c95cb4f95d98257aae4ff6e6e1.png)

IBM Watson Natural Language Understanding Pricing

# 微软 Azure 认知服务

像谷歌一样,[微软 Azure 认知服务](https://azure.microsoft.com/en-us/pricing/details/cognitive-services/text-analytics/)免费层被限制为 5000 个“交易”,所以我们将分析他们的最低定价层。每笔交易包括多达 5,000 个字符的情感分析和关键短语提取、语言检测和实体识别。微软不分解单个元素的定价,只提供基于层而不是量的定价。

10,000 requests X 550 characters/request = 10,000 transactions
10,000 transactions requires “Standard S0 pricing tier” = $74.71


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/45d2c29085c8f1bc7a6d4c73da265f49.png)

Microsoft Azure Text Analytics Pricing

# 更多的场景,更多的水果来比较

从上面的比较来看,你会认为亚马逊在定价上显然是最激进的,这就是故事的结尾。不完全是。当您增加记录的数量或记录的大小时,事情会变得稍微复杂一些。

在上面的第一个例子中,我们比较了 10,000 条客户评论的情感分析。但是如果我们想分析 100 万条推文呢?还是 5000 篇学术论文每篇 10000 条记录?

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/06ce7479d6efaf65bc16c4bf76c1ab8c.png)

Pricing based on publicly available information; account-level discounting may lead to lower pricing.

虽然亚马逊在定价上普遍领先于竞争对手,但在规模定价方面有一些奇怪之处变得很明显。例如,如果您有相对较少数量的非常大的文档,那么 IBM 看起来是运行您的分析的好地方。当做一些小的事情时,微软的最低定价层看起来很贵,但是如果这些文档更大,它看起来更有吸引力。

我们建议您使用测试数据集亲自尝试它们,看看它们的表现如何。如果它们不起作用,花多少钱就无关紧要了。

【www.deducive.com】最初发表于[](https://www.deducive.com/blog/2018/8/29/how-much-does-sentiment-analysis-in-the-cloud-actually-cost)**。**

# 计算社会科学家会是你的下一个最佳雇员吗?

> 原文:<https://towardsdatascience.com/could-a-computational-social-scientist-be-your-next-best-hire-4e393fbdb2a6?source=collection_archive---------3----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/84aaeecb893b5fba48766e8cec06eb66.png)

Social Scientists are experts are using data to find insights on behavior and relationships between groups of people.

我想向你们介绍一个鲜为人知的专业领域,叫做计算社会科学家。

每个企业都可以从雇佣计算社会科学家中受益,但很少有人知道这到底意味着什么。所以我想尝试照亮这个领域。

完全公开——我把自己放在计算社会科学的桶里,所以我有偏见。但是听我说完,我想你会喜欢这个领域的。

计算社会科学归结为社会科学家使用数据处理和数据科学计算工具(想想 R,Python 等)来分析关于人和关系的数据。

计算社会科学家和数据科学家的区别在于,社会科学家是研究人类行为和在关于人口群体的数据中寻找模式的专家。

社会科学家来自不同的流派,但主要群体包括经济学家、人类学家、社会学家和心理学家。

另一方面,数据科学家通常倾向于深入了解统计学和数据计算。数据科学家通常具有统计学、数学、计算机科学和数据工程背景。

这并不是说一个比另一个好。它们是互补的。

当想要跟踪大量数据、运行算法和大规模开发复杂的机器学习模型时,数据科学家将是您的最佳人选。

当你想了解人口、用户群或行为模式时,社会科学家会是你的得力助手。他们使用数据科学工具的方式与数据科学家相似,但他们希望在社会行为的背景下解释趋势。

数据科学家带来了计算和统计方面的丰富知识,而社会科学家带来了与人群相关的模式方面的丰富知识。因此,这两种技能组合的合作可能是在大量数据中释放巨大价值的关键。

例如,我最近与数据科学同事合作,展示了关于银行模式的数据。这位数据科学家专注于预测不同国家和产品组的总体趋势。我专注于机器学习如何帮助我们识别客户群,以及数据如何显示客户群是由价格或服务或奖励积分等不同价值驱动的。这两种观点共同提供了丰富的概述。

大多数企业专注于雇佣两组数据洞察专业人士:数据科学家和研究人员/消费者洞察。

我认为还有第三个重要的群体,那就是计算社会科学家——拥有处理大量数据的技术技能和帮助他们识别人群行为模式的知识集的专业人士。

现在比历史上任何时候都更加强调数字社会联系和更多可用于跟踪行为的数据。这创造了一个充满希望的环境,计算社会科学家可以帮助进步的企业找到关于行为和群体关系的宝贵数据。

所有这些都表明,计算社会科学家可能是你的下一个最佳雇员。

# 替代投票系统能阻止特朗普吗?

> 原文:<https://towardsdatascience.com/could-an-alternate-voting-system-have-stopped-trump-6cabf99f6fa7?source=collection_archive---------2----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/f489910b583ae54957ed7384a5388508.png)

现在,唐纳德·特朗普正式成为共和党提名人,回顾一下让曾经不可想象的事件发生的背景是很重要的。在接下来的几个月和几年中,大量的博客文章、论文、同行评议文章和书籍将研究导致特朗普初选胜利的文化和经济环境,但讨论中似乎缺少了一个背景,那就是初选制度本身。情况并非总是如此。

事实上,在初选期间,[【RCV】](https://en.wikipedia.org/wiki/Ranked_voting_system)的选择性选举制度曾经风光一时。尽管实施这种投票系统的想法并不新奇,但[这次选举的一些事情让人们对它进行了更仔细的审视。RCV 的工作方式是,每个选民不是投票给一个候选人,而是按照他或她的偏好排列所有候选人。如果没有候选人获得第一选择票的绝对多数,则第一选择票最少的候选人将从选票中退出,这些选票将转移给每位选民的第二选择候选人。这一过程一直持续到一名候选人获得至少 50%的选票。](https://twitter.com/realDonaldTrump?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor)

举个简单的例子,想象一下萨姆、克莱尔和哈利之间的选举,40 个人投票“1:萨姆,2:克莱尔,3:哈利”,35 个人投票“1:克莱尔,2:哈利,3:萨姆”,25 个人投票“1:哈利,2:克莱尔,3:萨姆”。在目前的制度下,山姆将赢得 40%的选票,相比之下,克莱尔和哈利分别获得 35%和 25%的选票。但在 RCV 治下,由于没有候选人获得 50%的选票,第一选择票数最少的人(哈里)将被淘汰,他的选民将被重新分配给他们的第二选择。在这个例子中,Harry 的所有 25 个投票者都将 Clare 作为他们的第二选择,所以他们的票都投给了她。重新分配后,克莱尔将获得 60 票,而萨姆只有 40 票,他将以 60%的优势获胜。从本质上讲,该制度确保了当大多数选民倾向于另一位候选人时,一位候选人不会以多数票获胜。

如果这听起来很熟悉,那可能是因为在#nevertrump 运动的巅峰时期,它变得非常流行[讨论](http://www.salon.com/2016/03/26/this_one_reform_defeats_donald_trump_and_saves_democracy_too_bad_the_gerrymandering_gop_never_listened_but_maybe_they_will_now/)它的优点(以及[类似](http://www.nytimes.com/2016/05/01/opinion/sunday/how-majority-rule-might-have-stopped-donald-trump.html)系统的优点)。当时的普遍理论似乎是特朗普以 30—40%的多数票赢得了第一轮初选,尽管 60—70%的“反特朗普”选民肯定会团结在另一位候选人周围,如果这个领域没有如此分裂的话。在这种情况下,排名选择投票是可以拯救该党的白衣骑士,提供了一种将提名保留在人民手中的方式,同时确保最终被提名者可以被大多数选民接受。

虽然这是一个令人信服的故事,而且特朗普最终在初选中只获得了 46%的选票,这一事实加强了这一故事,但仍然有必要问一个问题:RCV 真的会改变什么吗?我开始借助两项早期民意调查([公共政策民意调查](http://www.publicpolicypolling.com/pdf/2015/PPP_Release_SC_21616.pdf)和 [NBC 新闻|调查猴子](http://www.msnbc.com/msnbc/who-gains-the-most-when-the-gop-field-shrinks))来寻找答案,这两项调查要求选民给出他们的第二选择候选人。

使用这些民意调查的数据以及每个初选中每个候选人的投票数量,我能够模拟在排名选择投票系统下前 15 个共和党初选和党团会议中可能发生的情况。模拟一直进行到超级星期二初选,包括超级星期二初选,因为剩下的部分需要极端猜测早期初选结果可能如何影响后来的候选人退出和选民偏好。每个州的选举都被模拟了 1000 次,并添加了随机噪声,以考虑所使用的投票数据的不确定性(更详细的方法可以在这里找到)。

有趣的是,这些模拟的结果描绘了一幅比通常理论化的稍微低调的画面。具体来说,在超级星期二之前的几乎每个州,在当前制度下赢得多数票的候选人在 RCV 制度下最有可能赢得大多数选票。两个例外是阿肯色州和弗吉尼亚州,前者克鲁兹赢得了 77%的模拟 RCV 选举,后者卢比奥赢得了 60%的模拟 RCV 选举,尽管特朗普在当前制度下赢得了这两个州。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/09c55e62fded962b559a6d72a399980d.png)

然而,结果确实表明,特朗普在当前体系下的结局是他在 RCV 治下所能希望的最好结果。特朗普在 RCV 治下至少有 10%胜算的州是他在当前制度下赢得的州。相比之下,非特朗普候选人(克鲁兹和卢比奥的组合)有很大的机会(> 50%)从特朗普手中拿下两个新的州(阿肯色州和弗吉尼亚州),也有合理的机会(> 20%)拿下另外两个州(新罕布什尔州和南卡罗来纳州)。

此外,通过检查州赢和输的所有可能组合,很明显特朗普会受到这一系统的至少轻微伤害。事实上,他只有 7.5%的机会赢得至少 10 个州(这是他在现行制度下赢得的数字),相比之下,他有 27%的机会赢得 9 个州,40%的机会赢得 8 个州,21%的机会赢得 7 个州。按照同样的标准,克鲁兹和卢比奥都将略有受益,他们最有可能取得+1 的胜利。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e6ddb4a0167045017dbf0d08cd8f7b3e.png)

基于这些结果,一个有趣但更主观的任务是分析这样的结果会对剩下的初选产生什么影响。很容易想象 8/15 的表现不如 10/15 的表现占据主导地位,特别是在初选的早期阶段,特朗普的竞选活动非常注重始终“获胜”的理念。然而与此同时,可以想象,8 次多数胜利实际上可能比 10 次多数胜利更令人印象深刻。

因此,尽管这些结果指向反对特朗普的整合大方向,但它们并不像一些人认为的那样确定。最终,这可能简单地归结为记者和政治家不喜欢特朗普的程度与共和党选民不喜欢特朗普的程度之间的脱节。虽然被迫承认 30%至 40%投票给他的选民的意见,但在早期阶段,许多 T2 坚持认为,尽管川普是这些选民的第一选择,但他肯定是其他人的最后选择。事实上,这个天花板理论[很可能是错误的](http://www.slate.com/articles/news_and_politics/politics/2016/04/donald_trump_shatters_his_ceiling.html)。相反,特朗普似乎从一开始就是一个常见的第二或第三选择,并且和其他任何候选人一样从不断缩小的领域中受益匪浅。这一事实很可能是 RCV 对他的成功的适度影响的根源。

虽然排名选择投票可能没有彻底改变这次选举的进程,但仍然有几个原因使这个投票系统值得关注和考虑。如果不说别的,选民排名行为所提供的对选民偏好的深刻洞察可能对更早、更有效地动员反特朗普倡导者非常宝贵。因此,尽管 RCV 在全国范围内实施的可能性不大,但我们仍然可以通过写信给当地的民意调查机构,在他们的下一封邮件中询问第二和第三选择问题来尽自己的一份力量。

你最喜欢的 2020 年的希望会感激它。

# 无数 3D-使用 Python 和 Numpy 对标记的体积图像进行矢量化 2 倍缩减采样

> 原文:<https://towardsdatascience.com/countless-3d-vectorized-2x-downsampling-of-labeled-volume-images-using-python-and-numpy-59d686c2f75?source=collection_archive---------8----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/2c3c155f407db1bb0e6a3ac9ecc1415d.png)

A visualization of a 2x2x2 downsample of a labeled dataset. This is what COUNTLESS 3D does.

之前,我演示了一个完全矢量化的算法,[neutrable](https://medium.com/@willsilversmith/countless-high-performance-2x-downsampling-of-labeled-images-using-python-and-numpy-e70ad3275589),它通过寻找 2x2 面片的模式,在不计算像素值频率的情况下,对标记的图像进行下采样。在这里,我使用 2x2x2 的补丁和任意的下采样因子将无数的图像归纳为 3D 图像。无数 2D 通过寻找多数像素而不计算其总频率来工作。这是通过在一个 2×2 图像块中找到两个匹配像素的存在或不存在来确定的。无数 3D 扩展了这一逻辑,在八个体素(三维像素)中搜索匹配的四个像素,然后是三个,最后是两个。无数个 N 将这种技术推广到任何输入大小,尽管它很快变得不切实际无数个 2D 和无数个 3D 可以分别被视为无数个 4 和无数个 8 的特定应用实现。仅使用矢量化指令实现了无数变体,这使得它们对于在 Python 等编程语言中实现非常实用,Python 等编程语言具有快速的线性代数库(如 Numpy ),但具有缓慢的循环和分支语句。虽然无数的 2D 可以胜过简单的算法,但是在高吞吐量应用中使用无数的 3D 是不可取的,因为它比标准方法慢得多。然而,在 Python 中,它可以在不求助于额外编译步骤的情况下实现,并且仍然提供合理的性能。

# 动机

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/37581a8e2a24980c054bff2d0f533449.png)

Figure 1\. Volumetric images used in [connectomics research](https://www.nytimes.com/2014/05/27/science/all-circuits-are-busy.html), a field where we attempt to find the wiring diagrams of brains. Left: A 3D image formed from a stack of electron microscope scans of brain tissue. Right: A machine labeling of neurons in the left image by [Kisuk Lee](https://twitter.com/kisukkevinlee). This article concerns the type of image on the right.

虽然普通人不经常遇到,但 3D 图像(体积图像)在生物医学成像中大量使用。可以从以规则加深的间隔采集的一叠 2D 图像中构建体积图像。MRI 机器使用磁体来非侵入性地获取大脑切片的图像,细胞生物学家经常使用激光显微镜来扫描不同深度的样本。他们采集的图像按排序顺序排列在一个堆栈中,形成最终图像。

我感兴趣的组织是一个大脑,获取这些图像的方法是使用一种类似于熟食切片机的叫做[超微型切片机](https://en.wikipedia.org/wiki/Ultramicrotomy)的机器将其非常精细地切片。然后用电子显微镜对得到的切片进行成像,并组合成一堆以生成体积图像。切片很大,每边的像素在数万到数十万之间。相比之下,高端的 T4 4K 分辨率电视或电脑显示器每边只能显示 3000 到 4000 像素的图像,而另一边通常只有它的一半大。

为了使用消费类硬件显示这些图像,通常的做法是对它们进行下采样,即创建一系列更小的摘要图像,这些摘要图像公平地表示底层的高分辨率图像。例如,在谷歌地图上,世界显示为单个(或少量)拼接的图像,但当你放大时,更高分辨率的图像被提取并仅显示感兴趣的区域。对于显微镜图像,我们可以通过平均 2x2x2 小块来对其进行缩减采样,以创建一个八分之一小的图像,并重复这样做,直到图像足够小。然而,一旦我们生成标签来描述哪个体素属于哪个神经元,就不能进行平均,因为标签不是模拟信号,而是离散标识符。总结它们的最合适的方法是选择一个补丁中最频繁出现的值。

下采样的存储导致了额外的成本,尽管它随着每一个额外的 mip 级别而指数下降。对于递归 2x2 缩减,缩减采样堆栈的存储和传输成本比存储全分辨率图层的成本高 33%。

LISTING 1: Size of an Infinite Stack of DownsamplesLet S = The size of all the downsamples
Let r = The reduction factor of a downsample (e.g. 4 for 2x2)S = 1 + 1/r + 1/r^2 + 1/r^3 + …S/r = 1/r + 1/r^2 + 1/r^3 + 1/r^4 + …S — S/r = 1S = r / (r-1)


因此,2×2 下采样堆栈的存储成本最多是原始图像本身成本的 4/3 或 133%。2x2x2 下采样堆栈的存储成本最多是全分辨率的 8/7 或 114.3%。对于某些用例来说,这种减少可能是诱人的。对于那些被如此吸引的人来说,问题变成了如何在不计数的情况下实现它?

# 无数个 5——2D 问题的一个小扩展

从根本上说,无数的 2D 依赖于这样一种思想,即如果一组四个标签中的两个匹配,就没有必要考虑任何附加信息来声明该补丁的模式。人们很自然地会问,对于这个问题的最小可能扩展,一组五个标签,是否可以陈述类似的原理。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/08181aa004735b978c6983524cecbb7d.png)

Figure 2\. The seven cases of COUNTLESS 5\. (a) all labels match (b) all but one label matches (c) three labels match versus two labels (d) three match but the other two are both different (e) two pairs and one different (f) one pair and three different pixels (g) all different

在四个标签的情况下,我们发现如果任何两个匹配,它们立即形成多数或平局,在这种情况下,可以任意选择任何一个平局组的成员。在五个标签的情况下,两个标签的匹配不再足以宣布获胜者,然而三个标签是。如果三个标签匹配,它们将总是形成多数,不可能出现平局。然而,如果没有三个匹配,那么模式将由两个匹配组成。如果没有两个标签匹配,那么可以任意选择一个标签。

因此,我们必须通过某种机制寻找三个匹配,如果不存在,则寻找两个匹配,如果没有两个匹配,则举手选择一个合适的像素。搜索三个匹配意味着检查五个标签中的三个标签的每个组合,同样地,搜索两个匹配也是如此。

LISTING 2: The Relevant Combinations of Five LabelsLet the five integer labels be denoted A, B, C, D, ECombinations of three: 5C3 = 5! / 3!2! = 10 combinations ABC, ABD, ABE,
ACD, ACE,
ADE,
BCD, BCE,
BDE,
CDECombinations of two: 5C2 = 5! / 2!3! = 10 combinations AB, AC, AD, AE
BC, BD, BE
CD, CE
DECombinations of one: 5C1 = 5 combinations A, B, C, D, E


为了评估每个组合,我们可以从无数 2D 中归纳出 PICK(A,B)运算符来接受任意数量的参数:

PICK(A,B,C, ..., N) := A if A == B == C == ... == N else 0 EQN. 1


这可以用 Python/numpy 伪代码实现(“&”是按位 AND 运算符):

PICK(A,B,C ..., M,N) := A * (A == B & B == C & ... & M == N) EQN. 2


对给定组合每次应用 PICK 将产生一个标签或零。如果我们的三个组合中的任何一个产生非零值,它就有资格作为候选模式。如果有一个以上的匹配,例如,如果实际上有四个匹配的标签,我们可以任意选择任何一个候选模式。短路逻辑 or 操作符有正确的语义来选择一个标签(如果存在的话)。

MODE(A,B,C,D,E) := PICK(A,B,C) || PICK(A,C,D) || PICK(A,D,E)
|| PICK(B,C,D) || PICK(B,D,E) || PICK(C,D,E) || PICK(A,B)
|| PICK(A,C) || PICK(A,D) || PICK(A,E) || PICK(B,C)
|| PICK(B,D) || PICK(B,E) || PICK(C,D) || PICK(C,E)
|| PICK(D,E) || E EQN. 3


如前所述,||运算符可以这样实现:

LOGICAL_OR(X,Y) := X + (X == 0) * Y EQN. 4


等式 3 有十七个元素需要一起计算和逻辑或。有什么方法可以减少所需元素的数量吗?基本上降低通用算法的时间复杂度是不可能的,但是对于少量的标签来说,还是有一些边际节省的。注意,对于两个匹配的情况,我们可以扩展在无数 2D 中使用的技巧,以避免计算最后一个元素的匹配。使用符号 PQ 表示 PICK(P,Q),如果 AB、AC、AD、BC、BD 和 CD 都不匹配,那么我们将被迫选择 AE、be、CE、DE 或 E 中的一个,所有这些都与 E 相同。因此,我们可以省略 AE、BE、CE 和 DE,留下十三个元素来计算,这是一笔可观的节省。

MODE(A,B,C,D,E) := PICK(A,B,C) || PICK(A,C,D) || PICK(A,D,E)
|| PICK(B,C,D) || PICK(B,D,E) || PICK(C,D,E)
|| PICK(A,B) || PICK(A,C) || PICK(A,D) || PICK(B,C)
|| PICK(B,D) || PICK(C,D) || E EQN. 5


无论我们考虑的标签集有多大,最后一个标签总是如此。这意味着对于 N 选 2 的情况,我们总是可以把它简化为 N-1 个选择两个组合来考虑。

将所有这些放在一起,会产生一些适用于无数 5。请注意,这只是无数 3D 的垫脚石,也许更好地称为无数 8,因为它解决了 2 x 2 x 2 体素的模式。

# 无数 3D(又名无数 8)

无数的 3D,这个概念的实际应用现在已经触手可及。对 2×2×2 的体积图像补片进行下采样相当于找到八个整数的模。候选人多数或平局的最低要求是四个匹配的标签。如果比分是 4 比 4,我们可以任意选择一场比赛。要计算无数个 8,我们必须考虑长度为 4、3、2 的匹配。

下面的等式 6 显示了必须进行的提货呼叫的数量。请注意,下面的 7C2 项来自我们使用无数个 5 部分中显示的方法对 8C2 的简化。

Total PICKs = 8C4 + 8C3 + 7C2
= 70 + 56 + 21
= 147 EQN. 6


现在请注意,PICK(A,B,C,D)比 PICK(A,B,C)贵 1.5 倍(六次操作对四次操作),比 PICK(A,B)贵 3 倍(六次操作对两次操作)。这个事实以后会很重要。

下面是无数个 8 的实现。请注意,如果我们不小心的话,创建 148 个精选将会增加所需的内存。使用 Python 3 生成器表达式,我们可以通过一次只创建几个额外的派生图像来大大减少这个程序的内存需求。

类似于无数的 2D,如果匹配的标签是零,那么 PICK 操作符的输出是无意义的(无论它们是否匹配,它都返回 0),所以我们将数据上移一位以适应零标签,并在最后下移。

# 无数 3D 动态编程

到目前为止还不错,但可以加快一点。如等式 6 所示,无数 3D 的性能大致与必须计算的拾取操作的数量成比例,而拾取操作的数量又等于必须计算的操作的数量。PICK(A,B,C,D)需要六次运算,而 PICK(A,B,C)需要四次,PICK(A,B)需要两次。我们还需要计算每个逻辑 or 的三次运算。

LISTING 3: Number of Operations Required for COUNTLESS 3D# of COUNTLESS_3D operations
= 8C4 PICK(A,B,C,D) + 8C3 PICK(A,B,C) + 7C2 PICK(A,B)
+ (8C4 + 8C3 + 7C2 + 1 - 1) logical ors
= 8C4 x 6 + 8C3 x 4 + 7C2 x 2
+ (8C4 + 8C3 + 7C2) x 3
= 686 + 441 // picks + logical or = 1,127 operations


或者更一般地说:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/424b7e43369b458f9925466ca6c0a557.png)

Eqn. 8: Establishing a lower time complexity bound for COUNTLESS N, where N ≥ 2\. N represents the number of labels being evaluated, 7C2 + 1 represents the abbreviated cost of the matches of two case. The left hand term refers to the cost of PICK and the right hand term refers to the cost of logical or.

到目前为止,因为问题的解决方案是由较大的匹配先于较小的匹配来控制的,所以我们首先计算最大的匹配。然而,可以使用动态编程方法从较小的组合中构建较大的组合。以额外的内存为代价,我们可以将两个匹配视为三个匹配的子问题,将三个匹配视为四个匹配的子问题。由于每个新层都通过添加一个元素建立在旧层的基础上,这种技术将使我们能够通过消除重复工作来降低选择项中的(r-1)系数。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/534f5b41878acbbf18d575644f208412.png)

Eqn. 9: Dynamic programming drops the coefficient (r-1) from the summation.

通过去掉因子 2,我们可以将该等式简化为:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/a044a521e56ba2aa18d16d6fd1360dd8.png)

Eqn. 10: Simplified cost of the dynamic programming solution for COUNTLESS N.

然而,这对于计算和交流来说有点烦人,所以让我们用一些[朗道符号](https://en.wikipedia.org/wiki/Big_O_notation#Family_of_Bachmann%E2%80%93Landau_notations)来简化它。从 0 到 N 的所有组合之和等于`2^N`。由于组合是对称的,并且我们在跳过少量计算的同时求和到 N/2,这算出了 2^(N-1).的指数时间复杂度

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/209114be947af542dc588514b1b8ed90.png)

Eqn. 11: Exponential big-O time complexity.

动态规划保存已解决的子问题,供算法的下一次迭代使用。在这种情况下,这意味着保存匹配长度为 2 的结果,并使用它们来引导匹配长度为 3 的匹配。这非常耗费内存,但是有一个技巧可以让它变得稍微好一些。

LISTING 4: Not All Subproblems are UsedLet A, B, C, D, E represent five labels.
Let the notation AB := PICK(A,B) and ABC := PICK(A,B,C) and so on.Combinations of Length Two: AB, AC, AD, AE
BC, BD, BE
CD, CE
DECombinations of Length Three:

ABC, ABD, ABE, ACD, ACE, ADE
BCD, BCE, BDE
CDENotice that all length three combinations are prefixed with length two problems. For example, PICK(A,B,C) can be written as PICK(PICK(A,B), C). However, certain length two subproblems are not used in length three. AE, BE, CE, and DE can never be used as a prefix because E always occurs at the end.Therefore, substantial memory can be saved by not memoizing subproblems that include the terminal element.


将所有这些放在一起,就产生了无数 3D 的动态编程版本,虽然需要更多内存,但速度要快得多。它需要存储前一次迭代的结果。这导致内存消耗峰值为:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8ef1a0d5bab99c903ffa857f3f31bf51.png)

Fig. 11: Combinatoric space complexity.

我们应该期待无数 3D 的动态版快多少?使用等式 8 和 10:

LISTING 5: Comparing the Ratio of Eqn. 10 and Eqn. 8Non-Dynamic = 1,127 operations ; from LISTING 3
Dynamic = 5 * ( 8C4 + 8C3 + 7C2 ) ; from Eqn 9
= 735 operationsDynamic / Non-Dynamic = 0.65 or 1.53x faster


由于理想化的动态编程解决方案只需要 65%的工作,我们应该粗略地预计动态编程解决方案大约快 1.5 倍。实际上,我发现它要快 1.3 到 2.3 倍,可能是由于操作者速度的差异。

# 无数 N——广义的无数

总结我们在无数 3D 公式中所学到的东西是很简单的。无数个 N 是一个非常消耗内存和计算的算法。我不期望它在一个狭窄的参数范围之外表现良好。然而,当给出 2x2x2 缩减系数时,下面的算法在性能上匹配无数 3D。

# 表演

无数 2D 的性能指标都集中在纯粹的执行速度上。然而,对于无数的 3D 和无数的 nd,由于组合的数量,也需要测量内存使用量。

为了确定这种算法的速度,我们开发了一个比较套件,并在双核 2.8 GHz、i7 Macbook Pro(大约 2014 年)、256 kB L2 高速缓存和 4MB 三级高速缓存上运行。最大汇集、平均和大步跑包括在速度比较中,尽管它们不适合这项任务。

下面的实验使用了 Python 3.6.2 和 numpy-1.13.3 以及 clang-900.0.39.2。

经过测试的算法:

*   **大步走:**每隔一个像素拾取一次。
*   **countless 3d:**countless 3d 的低内存实现
*   **dynamic _ countless 3d:**countless 3d 的动态编程实现
*   **countless3d_generalized:** 用下采样因子 2,2,2 匹配无数 3d 的无数 N 的低内存实现
*   **COUNTLESS 3d _ Dynamic _ generalized:**用下采样因子 2,2,2 来匹配无数 3d 的无数 N 的动态编程版本

尽管这些算法不适合处理分割,但也对它们进行了测试,以便为其他图像处理算法提供比较点:

*   **down sample _ with _ averaging:**对 2x2 窗口中的像素进行平均。
*   **down sample _ with _ max _ pooling:**在 2x2 窗口中选取最高值像素。

用于测试算法的代码可以在[这里](https://github.com/william-silversmith/countless)找到。以下数字是在修订版 15c0077 上测试的。

## 吞吐量

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/caa16515b8ae5d3233d0d79e4730b5d9.png)

Table 1\. Python 3 Downsample Performance in Megavoxels/sec on a 512x512x512 Numpy Array by Array Data Type, 5 Trials

当使用 uint8、uint16、uint32 和 uint64 分配立方体时,下面的数字来自对 512 x 512 x 512 体素立方体运行上述算法五次。表 1 和图 3 中的结果以每秒兆体素(MVx/sec)为单位,因为人们通常希望处理特定“物理”大小的体积。然而,表 2、图 4 和图 5 以每秒兆字节(MB/sec)为单位,因为运行之间的差异主要在于存储器的大小。

无数 3D 的四个变体由基本算法的 3D 和 N 维实现以及它们的动态编程对应物组成。在基本算法和动态算法中,具体实现的性能与通用实现非常相似。所有这些比较的误差都在 5%以内。使用动态编程算法处理这个 128 MVx 的立方体大约需要 5 秒钟。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/fccc74f7afe8f35cf9d581c5d75d6e75.png)

Figure 3\. Python 3 Downsample Performance in Megavoxels/sec on a 512x512x512 Numpy Array by Array Data Type, 5 Trials showing only COUNTLESS 3D variants

就 MVx/sec 而言,对于 uint8 阵列,动态编程产生了大约 2.2 倍的速度提升。对于 uint16 和 uint32,它更接近 1.5 倍。对于 uint64,它大约是 1.27 倍。因此,随着内存的增加,性能有明显的下降趋势,但这在某种程度上被以 MB/秒为单位测量的性能所掩盖。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/2d4bcbfddb1ddfc89837699b56406a9d.png)

Table 2\. Python 3 Downsample Performance in Megabytes/sec on a 512x512x512 Numpy Array by Array Data Type, 5 Trials

图 4 清楚地表明,在 uint8 和 uint64 之间,性能实际上略有提高,尽管在动态实现中,uint64 与 uint32 相比略有下降。uint16 的结果似乎是一个异常值。它们与 uint8 性能相当,比 uint32 高出近一倍。我怀疑,要么在库内部,要么在我的芯片上,uint16s 被当作 uint32s 处理。动态编程带来的改进是巨大的,但是在下一节我们将看到这是有代价的。

图 5 是为上下文提供的。平均算法和最大池的简单实现展示了更快的执行速度和几乎与数据类型宽度的增加相称的明显上升趋势。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/739ee75127456bd69d116ee597cc9af4.png)

Figure 4\. Python 3 Downsample Performance in Megavoxels/sec on a 512x512x512 Numpy Array by Array Data Type, 5 Trials showing only COUNTLESS 3D variants

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/1577a753802e79ed3829fc3d394692de.png)

Figure 5\. Python 3 Downsample Performance in Megavoxels/sec on a 512x512x512 Numpy Array by Array Data Type, 5 Trials including COUNTLESS 3D variants, averaging, and max pooling for context.

## 内存使用

在无数个 3D 的动态编程实现中,存储子问题所需的内存显然是巨大的。下面,我测量了五次处理 128MB 512x512x512 立方体的内存需求。图 6 示出了操作中的使用基本算法,而图 7 示出了动态编程算法。下面的图表是使用 Python 3.4 在 Ubuntu 14.04 Linux 上使用 [mprof](https://pypi.python.org/pypi/memory_profiler) 工具生成的。

基本算法相当一致地使用大约 350MB 的 RAM 或大约 2.7 倍的存储立方体所需的内存。动态编程算法需要大约 1150MB,或者大约 9 倍于立方体的内存占用。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8f6ff22caadcb044b74bf79b891597a7.png)

Figure 6\. Memory profile for ordinary COUNTLESS 3D over five trials.

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0eb56c2deb18094b6893088210c1d9c6.png)

Figure 7\. Memory profile for dynamic programming COUNTLESS 3D over five trials.

# 讨论

无数的 3D 是一个“慢”的算法。更快的动态编程(DP)变体具有指数级的时间复杂度`O(2^(N-1))`,而基本变体稍微慢于此。DP 变体具有组合增长的空间复杂性。然而,非常幸运的是,2x2 (N=4)和 2x2x2 (N=8)的特殊情况是常用的,并且足够小,甚至在这些可怕的条件下也是实用的。在某些情况下,2x2 的情况显示出能够优于实现为 O(N)的标准计数算法,尽管它可以通过使用 hashmaps 减少到 O(N )(尽管由于常数开销增加,它对于小 N 可能会更慢)。

DP 算法的速度提高了 1.5 到 2.2 倍,但内存价格昂贵。无数 3D 的基本变体消耗的内存不到 DP 变体的三分之一。花 3 倍的内存换 2 倍的速度。幸运的是,即使在 uint64 的情况下(~ 8–10gb),对于单个进程的消费级设置来说,这仍然是一个合理的范围。

可以创建比无数 3D 运行速度快很多倍的 C 或 Cython 代码,而几乎不占用额外的空间。尽管如此,无数的 3D 可以服务于一个特殊的领域:使用普通的 Python/numpy 安装在中等大小的数据集上运行 3D 缩减采样,而不需要额外的编译。这种 3D 算法可以帮助研究人员和程序员,他们需要一个可以在 Python/numpy 所在的地方可靠部署的库。

这些研究人员可以获得最佳性能的一个用例是对感兴趣区域的二进制掩膜进行下采样。某些种类的语义分割可以利用少量的标签。在这种情况下,uint8 阵列提供了足够多的标签,并受益于约 20 MVx/秒的处理速度。在连接组学中,更典型的要求是小容量的 uint16,以及大型项目的 uint32 或 uint64。对于这些情况,我推荐编译代码。

这篇文章存在的一个重要原因是,我在研究无数 2D 的全面概括时获得了乐趣。感谢阅读。😇

## 未来的工作

我计划在将来展示 C 实现之间的比较。我的预期是,它的时钟频率将超过 100 MVx/秒。Github 上有一些 C 代码,但是我还不能确信它是正确的。

# 承认

在开发无数的 3D 和撰写本文的过程中,一些人提供了有益的建议和帮助。 [Kisuk Lee](https://twitter.com/kisukkevinlee) 提供了图 1 和图 2 所示的 3D 数据的可视化。Chris Jordan 提供了 C 实现*计数*和*无数*的种子。乔治·纳吉博士建议概括无数的 2D。Google 的 Jeremy Maitin-Shepard 最初开发了用于 neuroglancer 的*大步走*和*下采样平均*的 Python 代码。特别感谢 [Seung Lab](http://seunglab.org/) 提供神经分割标签。

# 密码

用于测试该管道的代码可以在 [Github](https://github.com/william-silversmith/countless) 上找到。欢迎其他语言的贡献和反馈,并将记入贷方。

**2019 年 3 月 11 日:**现作为 [tinybrain](https://github.com/seung-lab/tinybrain/) [PyPI 包](https://pypi.org/project/tinybrain/)的一部分提供。

*本文是“* [*”无数——使用 Python 和 Numpy*](/countless-high-performance-2x-downsampling-of-labeled-images-using-python-and-numpy-e70ad3275589) *”对标记图像进行高性能 2 倍下采样(2017)的后续。*

# 无数—使用 Python 和 Numpy 对标签图像进行高性能 2 倍缩减采样

> 原文:<https://towardsdatascience.com/countless-high-performance-2x-downsampling-of-labeled-images-using-python-and-numpy-e70ad3275589?source=collection_archive---------4----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/61597ee525bdcbe327cfbdb69cb3a4fe.png)

Figure 1\. A partial of a slice of [SNEMI3D](http://brainiac2.mit.edu/SNEMI3D/home), a machine learning contest dataset for neuroscience.

管理 terravoxel 图像上的 convnet 输出通常涉及生成可在廉价硬件上更容易下载、显示和处理的摘要图像。对普通照片或显微镜图像进行下采样的常用方法是在图像上定义一个窗口,然后应用 averaging 或 lanczos3 (sinc)等过滤器将窗口内容汇总到一个较小的像素集中。这些混合方法不适用于分段标签。一组分割标签的下采样必须包含来自输入图像的实际像素值,因为标签是分类的,混合标签是无意义的。如果标记为 1 的像素指的是汽车,标记为 3 的像素指的是鸟,那么这两个像素的平均值(2 指的是人)就不是底层图像的忠实表示。

因此,下采样分类标签包括在图像上定义窗口并从该块中选择样本。一种常见的方法是通过在块中最频繁的像素中挑选来选择样本,也称为寻找模式。实现这一点最明显的方法是计算每个标签的频率,这在 C 等高性能语言中很容易实现。然而,Python 循环非常慢,这使得这种方法在不使用 C 扩展(Cython)的情况下无法实现,这使得项目维护起来更加麻烦,并且需要专业知识。在这里,我给出了一个方法 neutrally,它计算四个无符号整数的模,同时给出了一个 Numpy 实现,用于生成标签图像的 2x 缩减采样。

# 无数算法

要解决的最简单的 2D 下采样问题是 4 像素 2x2 图像。2x2 图像可以通过其最常见的单个像素来概括,以在每一侧实现 2 倍的缩减。现在坚持使用均匀大小的图像,一个较大的图像可以被分成 2×2 的块,如果这个过程在每个块上独立重复,这将导致整个图像整体缩小 2 倍。

2x2 图像由图 1 中列出的五个问题组成。在 1(a)、1(c)和 1(e)中,所有像素都在最频繁的类别中,因此是有效的解决方案。1(b)和 1(d)需要更复杂的方法。值得注意的是,在所有五种情况下,选择随机像素更有可能是大多数,这表明为什么大步走可以是一种可接受的,尽管不是完美的方法。形成该算法基础的关键见解是,在所有情况下,如果两个像素匹配,则它们占多数。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/26cccb9f0b7e6774aa0e40583cc95a7b.png)

Figure 2\. The Five Cases. Capital letters A,B,C,D refer to the identity of a non-zero pixel. (a) All the same. (b) Two the same. (c) Two pairs the same. (d) Three the same. (e) All different.

在下文中,大写字母 A、B、C、D 指的是像素位置的非零值。我们定义了比较操作符 PICK(A,B ),它生成一个实像素值或零。

PICK(A,B) := A if A == B else 0 EQN. 1


在 Python/numpy 这样的框架中,True 和 False 分别表示为 1 和 0,这可以用数字实现为:

PICK(A,B) := A * (A == B) EQN. 2


接下来,让我们考虑 A、B 和 c 之间的各种相互作用。在下表中,符号 AB 表示 PICK(A,B ),小写字母表示特定值,因此两列中重复的字母“A”表示两个像素都是红色。

Pixel PICK(X,Y)
A B C AB BC AC
a a a => a a a
a b a => 0 0 a
a a b => a 0 0
b a a => 0 a 0
a b c => 0 0 0 <-- Only fully zeroed rowTABLE 1. PICK(X,Y) (denoted XY) interactions between A, B, and C


表 1 显示 A、B 和 C 中仅有的多数像素或零点将作为拾取操作的结果出现。在 A、B 和 C 都不相同的情况下,所有选择都将返回零。这使得像素选择的问题服从于简单的逻辑。a、B 和 C 都不同,对应于图 1 中的情况 1(b)或 1(e ),在 1(b)的情况中 D 占大多数。如果情况是 1(b),这意味着 D 是一个可接受的解决方案。如果情况是 1(e),则不存在多数像素,D 也是可接受的解决方案。

因此,当 A、B 或 C 匹配时,选择匹配项,当它们都不匹配时,选择 d。这可以在计算机语言中用短路逻辑 OR (||)表示为:

MODE(A,B,C,D) := PICK(A,B) || PICK(B,C) || PICK(A,C) || D EQN. 3


我们可以将逻辑或数字实现为:

LOGICAL_OR(X,Y) := X + (X == 0) * Y EQN. 4


EQN。3 和 EQN。4 将正确处理除零以外的所有无符号整数值。因此,在零是有效像素的情况下,我们可以在算法开始时给图像加一,然后在返回结果之前减一。

2x2 方法可以很容易地扩展到覆盖任何偶数尺寸的图像。只需用不重叠的 2x2 块覆盖图像,并求解每个块中的模式,即可生成缩减采样图像。然而,我们仍然必须处理奇数图像,其中边缘没有被 2x2 块完全覆盖。

幸运的是,有一个简单的解决方案。对于任何奇数图像,镜像边缘以生成偶数图像。有两种情况:角(1x1)和边(2x1 或 1x2)。镜像一个角将产生情况 1(a ),这将导致绘制相同的像素。镜像一条边将导致情况 1(a )(如果像素相同)或者情况 1(c ),这两种情况都将被无数个正确处理。

# 履行

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/fe336d9f41ab657c34207a44facf2ae9.png)

Figure 3\. Results of applying COUNTLESS. (a) A source image of 128 x 128 pixels is reduced to (b) an image of 64 x 64 pixels.

Numpy 中无数的实现是简单明了的。首先,图像必须被分成 2×2 块的覆盖。这可以通过创建四个 2D 数组 a、b、c 和 d 来表示,每个数组在概念上表示图 1 中与其同名的像素,但在图像中的每个 2x2 块上并行执行。这是通过从左上角跨越(2,2)偏移(0,0)、(0,1)、(1,0)和(1,1)来实现的。接下来,我们开始用无数的。Numpy 不支持逻辑 OR,但它支持按位 OR。幸运的是,根据表 1,从 PICK(A,B)、PICK(A,C)和 PICK(B,C)得到的值要么是单个值,可能是重复值,要么为零。因此,在这种特殊情况下,按位“或”的行为与逻辑“或”相同,为我们节省了一些在 EQN 中需要的运算。4.清单 1 展示了实现:

Listing 1\. The simplest implementation of countless that doesn’t handle black pixels.

这种实现适用于大多数情况,但是它有一个重要的故障模式。如果匹配像素为零,我们将意外地选择 D,因为结果看起来与表 1 中的最后一行相同。不幸的是,当使用有限整数表示时,这个问题无法完全消除,但是我们可以非常接近。

策略是在执行无数次之前给图像加一,之后减一。这会将 0 变成 1,并使算法正确工作,但它会导致最大值整数溢出(uint 8 为 255,uint16 为 65,535,依此类推)。但是,在添加数据类型之前转换为下一个最大的数据类型可以消除溢出效应(例如,将 uint8 数组转换为 uint16)。在目前的硬件上,这种方法在 uint64 以下是可行的。uint8、uint16、uint32 完全消除了零点问题,但 uint64 没有。这意味着,如果您的标注包含大约为 1.84 x 10 ⁹.的 2⁶⁴-1,该算法将会失败对于许多用途,这应该是可以接受的。减去一后强制转换回原始数据类型。对于将最大 uint64 视为特殊标志的编码方案,只需充分改变偏移量即可。

Listing 2\. zero_corrected_countless.py: simplest_countless.py updated to handle black pixels correctly.

最后一件事,我们添加了一些操作来解决零标签问题,但是这会影响性能。我们可以通过注意到 *ab* 和 *ac* 都乘以 *a 来恢复其中的一部分。*

Listing 3\. zero_corrected_countless.py augmented with an algebraic simplification to slightly improve performance.

# 表演

为了确定这种算法的速度,我们开发了一个比较套件,并在双核 2.8 GHz、i7 Macbook Pro(大约 2014 年)、256 kB L2 高速缓存和 4MB 三级高速缓存上运行。虽然该算法是为分割标签开发的,但普通照片也包括在内,以展示当数据不均匀时该算法如何执行。最大汇集、平均和大步跑包括在速度比较中,尽管它们不适合这项任务。

我用 Python 3.6.2 配合 numpy-1.13.3 和 clang-802.0.42 做了以下实验。

经过测试的算法:

*   **大步走:**每隔一个像素拾取一次。
*   **统计:**统计每个标签出现的频率。
*   **最简单 _ 无数:**最简单 _ 无数. py
*   **快速 _ 无数:**最简单 _ 无数. py +代数化简
*   **零 _ 校正 _ 无数:**零 _ 校正 _ 无数. py
*   **无数:**无数. py
*   **无数 _if:** 用 if 语句代替花里胡哨的把戏

尽管这些算法不适合处理分割,但也对它们进行了测试,以便为其他图像处理算法提供比较点:

*   **down sample _ with _ averaging:**对 2x2 窗口中的像素进行平均。
*   **down sample _ with _ max _ pooling:**在 2x2 窗口中选取最高值像素。
*   **ndzoom:** 使用 scipy 函数 ndimage.interpolation.zoom 缩小到 2x2。

用于测试算法的代码可以在[这里](https://github.com/william-silversmith/countless)找到。

在这篇文章发表后 [Aleks Zlateski](https://github.com/zlateski) 贡献了一个骑士登陆(KNL)的矢量化版本 bitwise neutrally,据报道在随机 int32 数据上运行速度为 1 GPx/sec,4 GB/sec。可以在 [Github](https://github.com/william-silversmith/countless) 上找到。

# 试验 1–神经组织的分割

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8754cd8cc2e3f3e04a9292bec0c85043.png)

Figure 4\. Integer labels representing a segmentation of a neural tissue micrograph.

RGB 分割是由卷积神经网络分配的像素标签的 1024x1024 图像,有趣地看着神经组织。这是无数在设计时就考虑到的那种形象*。每个像素是一个 RGB 三元组,合起来表示一个无符号整数。比如,(R,G,B): (15,1,0)代表 271 (15 + 1 * 256)。*

在表 2 中,虽然它不符合我们选择最频繁像素的标准,但*大步走*显然是速度恶魔。它似乎只受到内存带宽的限制。单纯的*计数*运行速度仅为 38 kPx/秒,这意味着计算一张图像需要大约 27.6 秒。

无数算法的各种版本在从 986 kPx/秒到 38.59 MPx/秒的宽范围内运行,轻松击败了*计数*。*无数 _if* 实际上是使用 if 语句测试两个像素是否匹配的*计数*实现的变体。其他无数变体之间的主要性能差异取决于我们是否正确地处理零(在*无数*和*快速 _ 无数*之间有 37%的差异,在*最简单 _ 无数*和*零 _ 校正 _ 无数*之间有 39%的差异)以及乘法是否被简化掉(在*最简单 _ 无数*和*快速 _ 无数之间有 13.8%的加速,以及* 15.6% 【T34

将该算法最快的综合变体*与其他两种常见的下采样方法进行比较,结果表明它比平均法慢 1.7 倍,比最大池法慢 3.1 倍。如果我们要处理不包含零作为有效像素的图像,相对差异将分别慢 1.3 倍和 2.3 倍。*

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/6ac661c780f7d1533b648aa1a6a921c7.png)

Table 2\. Python algorithm performance on a three channel 1024x1024 segmentation image. N=200 except where noted.

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/20189bb53dd216ef56628186ebe4842c.png)

Figure 5\. Python algorithm performance on three channel 1024 x 1024 segmentation. Striding omitted to better show the other algorithms.

# 试验 2 —灰色分割

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0924764b6f397485569062b97cf1c7ac.png)

Figure 6\. A grayscale version of the RGB segmentation from Trial 1.

三通道存储器布局对算法性能有什么影响?在灰度(即单通道)版本的试用 1 图像上运行相同的一组测试。这个试验比试验 1 更类似于测量现实世界任务的性能,尽管我们更经常在 uint16、uint32 和 uint64 阵列上操作而不是 uint8。

灰色图像的每像素字节数比 RGB 少三倍。如果这种关系是简单的线性关系,那么人们会认为 MB/秒的数字大致保持不变,而 MPx/秒会提高三倍,但事实并非如此。对于*最简单 _ 无数*来说,MB/秒在灰度上快了 17.4 倍左右。这可能是由于内存中 RGB 通道的非连续布局,而灰度得益于这种内存访问效率的提高。该试验的迭代次数增加到 1000 次,以允许试验运行与试验 1 大致相似的时间长度。由于*计数*和*无数 _if* 已经被认为是缓慢的,为了方便起见,它们在五次迭代中被测量,这仍然导致大量挂钟时间。

还测试了 counting、quick _ neutrally 和 neutrally _ if 的 C 实现。正如所料,在 MPx/sec 测量上,C 代码以大约 2.9 倍(对于*quick _ neutrable*)到 1025 倍(对于*neutrable _ if*)的速度击败了 Python。虽然看到*quick _ neutrally*从 C 实现中获得巨大的速度提升并不令人惊讶,但*neutrally _ if*中的巨大增益令人印象深刻,以 3.12 GPx/sec 成为赢家。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/7b8174d465b4a04b5d5231dcf64ff394.png)

Table 3\. Python algorithm performance on a one channel 1024 x 1024 version of the image from Trial 1\. N=1000 except where noted.

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e32fc7dc459c0fcd504fe9d5fd566d43.png)

Figure 7\. Python algorithm performance on a single uint8 channel 1024 x 1024 image. Striding is omitted from this figure because it’s so fast it makes the differences between the other algorithms hard to see.

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/f0a281893db9e8b47b963b9c6f22fb30.png)

Figure 8\. C implementation performance on counting, quick_countless, and countless_if. All three far exceed Python implementations and reach into the GPx/sec range. countless_if is by far the winner.

# 试验 3——灰色冰淇淋人(GICM)

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/f5cad1313dc91a4188f91c817ed5065a.png)

Figure 9\. Photograph of a man eating ice cream. 5456 × 3632 pixels, uint8

灰色冰淇淋人(GICM)是一个相对较大的 DSLR 照片转换成灰度。存在显著的动态范围和模糊效果,使得图像在像素与像素之间变化显著。这意味着 CPU 管道中的分支预测将比试验 2 中更频繁地失败。图像的大尺寸使得每次测试更加稳定,同时也为 CPU 性能的均衡提供了更多的时间。

同样,在表 4 中,*大步走*显然是赢家。简单的*计数*运行速度仅为 44 kPx/秒,这意味着计算一张图像需要大约 171 秒。无数算法的各种版本在从 2.4 MPx/秒到 594.7 MPx/秒的广泛范围内运行,轻松击败了计数算法。

其他无数变体之间的主要性能差异取决于我们是否正确处理零(无数和*之间的 3.2 倍差异,以及*最简单 _ 无数*和*之间的 3.2 倍差异)。代数化简在*最简单 _ 无数*和*快速 _ 无数*之间占 14.9%,在*无数*和*零 _ 校正 _ 无数*之间占 16.2%。**

有趣的是,在这种情况下,与灰度分割相比,*quick _ neutrally*比*down sample _ with _ averaging*和*down sample _ with _ max _ pooling*表现得更好。

这里的 C 结果与试验 2 非常相似,但是有一些有趣的特点需要注意。*无数 _ 如果*跌了 617 MPx/秒(~20%)。这可能是由于在非同质映像上分支预测失败的增加。*快速 _ 无数*在定性而非定量测量的误差范围内保持稳定。*无数 _if* 要快得多,但 quick _ numbery 在不同图像上的性能更可预测,尽管*无数 _if* 在测试图像上的性能变化似乎始终高于*quick _ numbery*。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/d5549b7e50f61a7e8dd6310b2d266a47.png)

Table 4\. Python/numpy algorithm performance on Gray Ice Cream Man. N = 200 except where noted.

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c46e25270f2404de269d5474afdf3b45.png)

Figure 10\. Python Algorithm Performance on GICM. Striding is omitted from this figure because it’s so fast it makes the differences between the other algorithms hard to see.

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/fa02f3bf132e33cffba306cf37abf691.png)

Figure 11\. C Implementation of three algorithms. counting is still in last place, but countless_if is now far ahead of standard countless.

# 讨论

neutrally 的标准 Python/numpy 实现相对于计数方法的原始实现表现出了巨大的性能增益,并且在性能上与图像处理社区中大量使用的简单方法平均值和最大池相当。在 Seung Lab 的生产图像处理管道中,我们经常处理大小为 2048x2048 的 64 个图像块进行下采样。重要的是,处理时间与下载时间相当,以确保有效的管道,无数做这项工作。

在 C 实现中有数不清的好处。一个优化的计数实现能够在 GICM 上实现 880 MPx/sec,比最快的 Python 实现*quick _ neutrable*快大约 1.48 倍。然而,*quick _ numbered*最快的基于位运算符的 C 实现达到了 1.9 GPx/秒,而基于 if 语句的实现*numbered _ if*达到了 2.5 GPx/秒,这意味着单个内核每秒可以处理大约 9 个 2048x2048x64 块,而使用*计数*大约可以处理 3 个。作为参考,一个给定的数据集可以包含数万个或更多这样的块。

然而,似乎至少在 C 语言中,与按位运算符相关的巧妙之处可能并不那么有用。毕竟,简单的 if 语句打败了它们。然而,有一个技术领域,按位运算符会胜出。我们已经证明了在 Python/numpy 中无数的方法是有用的,然而,一般的方法在其他带有矢量化运算符的解释语言中似乎也能成功。尽管 Julia 是一种编译语言,但在 MATLAB、Octave、R 和 Julia 中,按位无数可能也是值得的。按位变量似乎特别适合 GPU 实现,其中 if 语句非常昂贵。虽然要实现这一点,环境必须相当特殊,但如果使用 Z 顺序曲线重新排列输入,似乎有可能通过向量化指令大大加快 C 实现逐位无数的速度。(这篇文章发表后,Aleks Zlateski 的 KNL 矢量化实现达到了 4gb/秒的速度,最大限度地提高了内存带宽。)

无数算法允许基于最频繁的值快速生成分割的 2x 下采样。可以想象,这种能力可能在各种机器学习管道中有用,甚至可能在算法中有用(尽管这种情况需要特殊,以支持模式而不是最大池)。无数是寻找四个数的模式的一般方法,甚至可能有其他与图像处理无关的应用。

无数确实有两个缺点。第一个是,虽然它可以递归使用,但只有第一次迭代才能保证是原始图像的模式。4x4 图像的模式可能不同于四个 2x2 图像的模式。第二个缺点是 Python 中的按位无数(虽然 C 中没有)比*计数*需要更多的内存。原始图像、 *a* 、 *b* 、 *c* 、 *d* 以及中间运算的结果,并且在算法运行时必须保留最终结果,这导致至少四倍的存储器增加,而计数只需要比原始数据多存储少量的整数。如果在生成 *a* 、 *b* 、 *c* 和 *d* 后可以丢弃原始数据,那么只需要增加三倍。需要注意的是*无数 _if* 同样只需要几个整数。

在它的首次应用中,使用 Python/numpy 递归生成了从脑组织的大型电子显微照片中得到的分割的 MIP 图。虽然只有第一个 MIP 级别保证是模式,但生成的 MIP 级别比大步流星混合得更好,后者往往会随着新 MIP 级别的加载而沿对角线穿过屏幕。

是无数的新?这种图像处理算法可能以前已经被发明过,并且基本的数学几乎肯定已经被用在像纯数学这样的其他环境中。但是,我还没有找到它的参考文献。

有几个潜在的卓有成效的方向来扩展无数的算法。第一个涉及随机图像的问题,第二个涉及将算法扩展到三维以处理体积组织图像。

关于随机图像,回过头来看案例 1(e ),我们将始终选择右下角,在随机或病理数据上,它可能会导致与天真大步走相同的对角线偏移效果。这种伪像是由 1(c)和 1(e)中出现的 d 的按位或引起的。也许可以通过增加一项(a!来把 1(c)和 1(e)分开。= b!= c!= d)然而,考虑到 1e,如何在图像中的所有 2x2 块上并行化 a、b、c 或 d 的随机选择并不明显。这种改变也可能是不希望的,因为它使输出不确定。

关于体积图像,由于我的实验室处理脑组织的 3D 图像,因此提出了这样一个问题,即这种方法是否可以扩展到 2x2x2 立方体(8 的模式)。分析这个问题的最简单的方法是考虑一个更简单的情况,即我们是否可以扩展这种方法,采取五个整数的模式而不计数。在这种情况下,如果至少有三个像素匹配,那么匹配的像素保证是正确的。然而,如果没有匹配,则取决于两个是否匹配,如果没有两个匹配,则任何像素都是候选。很明显,扩展这种方法需要进行大量的组合比较。虽然可以想象这比数五个数更有效,但回报会迅速减少。

在某种程度上,基于 if 语句的 neutrally 是一种识字前的算法,如果没有人学会如何计数,它就会被使用。很明显,试图在 C 语言中对大数模式的计数上胜出是很困难的。然而,在 Python 中,*quick _ neutral*比*count*on*GICM 有 5263 倍的优势,这意味着即使在 3D 情况下效率很低,仍有很大的改进空间。一个早期的演示表明,在 Python/numpy 中,3D neutrally 可能快至约 4 兆体素/秒,比 2D 计数快约 35 倍。还会有更多的实验。*

***编辑 2018 年 2 月 14 日:**在即将发表的一篇关于无数 3D 的文章中,我将使用 Python3/numpy 记录高达 24.9 MVx/sec 的速度。*

***编辑 2018 . 2 . 20:**无数的[3D 文章](https://medium.com/@willsilversmith/countless-3d-vectorized-2x-downsampling-of-labeled-volume-images-using-python-and-numpy-59d686c2f75)现已出。*

***编辑 2018 年 6 月 21 日:**如果你想在稀疏数据上使用无数的 2D 而不把上部下采样变黑,试试[点画无数的 2D](https://medium.com/@willsilversmith/countless-2d-inflated-2x-downsampling-of-labeled-images-holding-zero-values-as-background-4d13a7675f2d) 。*

# *感谢*

*几个人提供了有益的建议和援助,在开发无数。Chris Jordan 提供了 C 实现*计数*和*无数*的种子。[阿列克斯·兹拉特斯基博士](https://github.com/zlateski)在这篇文章发表后贡献了一个骑士道 SIMD 版本。ndzoom 基准测试由 [Davit Buniatyan](https://github.com/davidbuniat) 提供。George Nagy 博士建议测试*无数 _if* 并在同质和非同质图像上测试性能差异。谷歌的 Jeremy Maitin-Shepard 最初开发的 Python 代码用于与 neuroglancer 一起使用的*大步走*和*下采样平均*。特别感谢 [Seung Lab](http://seunglab.org/) 提供神经分段标签。*

# *密码*

*用于测试该管道的代码可以在 [github](https://github.com/william-silversmith/countless) 上找到。欢迎其他语言的贡献和反馈,并将记入贷方。*

***2019 年 3 月 11 日:**现已作为 [tinybrain](https://github.com/seung-lab/tinybrain/) [PyPI 包](https://pypi.org/project/tinybrain/)的一部分提供。*

# *更新和勘误表*

***2019 . 12 . 10:**之所以跨步这么快,是因为测试的操作只是更新 numpy 数组的内部跨步数;它实际上并没有复制数组。应该重新进行这些实验来反映这一点。*

***2018 年 7 月 9 日:**找到了消除变量 *bc* 的方法,重用 *ab_ac* 进行小加速(~2%?).请查看 github 获取此更新。*

***2018 年 2 月 14 日:**更新了图表和文本,具有现在使用 Python3.6.2 的 Python 代码的更新基准。表 2、3、& 4 和图 5、7、& 10 已被替换。基准现在包括由[戴维·布尼亚延](https://github.com/davidbuniat)贡献的 scipy 函数 ndimage.interpolation.zoom 的代码。❤️*

***2018 年 2 月 1 日:**我在 Python 基准测试代码中发现了一个错误,该错误导致大多数算法的速度被低估了 4 倍。我将很快更新这篇文章,带来新的结果。我已经找到了一种方法,使无数的实现更快,更有效的内存。Python3 也比 Python2 快。最新见 [Github](https://github.com/william-silversmith/countless) 。*

***2017 年 8 月 21 日:**在这篇文章发表后 [Aleks Zlateski](https://github.com/zlateski) 贡献了一个骑士登陆(KNL)矢量化版本的 bitwise neutral,据报道,该版本在随机 int32 数据上以 1 GPx/sec,4 GB/sec 的速度运行。可以在 [Github](https://github.com/william-silversmith/countless) 上找到。*

# 课程 1 —算法工具箱—第 1 部分:简介

> 原文:<https://towardsdatascience.com/course-1-algorithmic-toolbox-part-1-introduction-c29b8175430f?source=collection_archive---------1----------------------->

我在做全职工作,创业工作 5 年了。加入软件开发世界是一种幸运。我每天都有机会学习新东西。我构建了许多有效的应用程序(我以前主要是 iOS 开发人员),许多 RoR、React、Elixir 产品代码正在运行。一切似乎都很好。但是有一天,我深刻地意识到我错过了一些东西,让我的工作更上一层楼。所以我回顾了一下基础知识,看到术语“算法和数据结构”一直在重复。复习完知识后,我决定花时间认真学习算法和数据结构。

幸运的是,这个世界对学习者来说充满了开放的机会。Coursera 为愿意学习的人带来了世界顶尖大学的精彩讲座。毫无疑问,加州大学圣地亚哥分校高等经济学院的“[数据结构和算法](https://www.coursera.org/specializations/data-structures-algorithms)”6 门专业课程脱颖而出。我的朋友正在上课,并向我强烈推荐。

这一系列的博客类似于课堂笔记,包括我基于这些课程材料的想法和实践。它还包含一些我已经解决的问题的提示。如果你有任何问题或评论,请在这里或 [github](https://gist.github.com/phatle) 留言。我愿意一起学习。我做这些笔记是为了帮助我复习。希望对你也有帮助,所以公开一下。让我们跳进游泳池吧!

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/af3977974ffaac0f427fc25b35c627b6.png)

Getting started with Algorithms

**问题 1:最大两两乘积。**

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c208b490806b87d13f2b6a02354372b1.png)

**解决方案:**

我们只需要将两个最大的数字相乘。

**问题 2:大公约数(GCD)**

两个非负整数 a 和 b(不都等于 0)的最大公约数 GCD(a,b)是除 a 和 b 的最大整数 d。

你在这个问题中的目标是实现计算最大公约数的欧几里德算法。计算最大公约数的高效算法是 RSA 等常用密码算法的重要基本原语。

**解决方案:**

**问题 3:最小公倍数**

两个正整数 a 和 b 的最小公倍数是能被 a 和 b 整除的最小正整数 m。

**解决方案:**

如果你知道 LCM(a,b)。GCD(a,b) = ab,复用上一个 GCD 算法,这个问题的算法会很好写。

***注意:*** 在 python 中使用//在 Python3 中对大数进行右除运算。

**问题四:斐波那契数**

斐波那契数列的定义:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/838402357755f637415d285cb9b4cca2.png)

**解决方案:**

很容易实现简单的递归算法来计算斐波那契数。

但是运行时间不好。你可以试着计算一下 **F40** ,这会花费相当长的时间。

我们需要一种更快的方法来计算斐波那契数列。

**练习 1:** 用归纳法证明 n ≥ 6 时 Fn ≥ 2^(0.5n)。

**证明:**

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e6492f56a144a0f08b28b0f77def598e.png)

*assume

**练习 2:** 求一常数 c < 1 使得对于所有 n ≥ 0,Fn ≤ 2^cn。表明你的答案是正确的。

**证明:**

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b3b3684a3bd3ce5ad2164d922095c7de.png)

**问题 5:一个大斐波那契数的最后一位数字**

求第 n 个斐波那契数的最后一位数。回想一下,斐波那契数列以指数速度增长。举个例子,

f200 = 280571172992510140037611932413038677189525。

**解决方案:**

因为它的增长是指数级的快,所以我们不能用 fast_fibonacci(n) % 10 来给出答案。我们需要另一种方法来解决这个问题。我们只对 Fn-1 的最后一位和 Fn-2 的最后一位求和,而不是对 Fn-1 + Fn-2 求和。

**高级问题 6:模 m 的巨大斐波那契数**

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/560b591b828d0853066f0251cf98ae40.png)

Pisano alike

求 Fn 模 m,这里 n 可能真的很大:高达 1018。对于这样的 n 值,循环 n 次迭代的算法肯定不适合一秒钟。因此,我们需要避免这样的循环。为了了解如何解决这个问题,而不需要遍历 I 从 0 到 n 的所有 Fi,让我们看看当 m 很小时会发生什么,比如 m = 2 或 m = 3。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/3e6874c635eac04b463cd9f1b52ec2c4.png)

所以可以观察到它有周期。Fn 模式 2 具有长度为 3 的 011 周期,Fn 模式 3 具有长度为 8 的 01120221 周期。因此,要计算 F2016 mod 3,我们只需找到 2016 除以 8 的余数。自 2016 年= 251.8 + 8。我们的结论是 F2016 mod 3 = F8 mod 3 = 0。

这在一般情况下是成立的:对于任何 m >= 2 的整数,序列 Fn mod m 是周期的。周期总是从 01 开始,被称为[皮萨诺周期](https://en.wikipedia.org/wiki/Pisano_period)。

**解决方案:**

基于这一假设,我们通过检查 Pisano 周期是否由 01 重复并保持重复 Pisano 表中的其他数字来检测 Pisano 周期。所以我们检查

pisano_table[half_size] == 0 并且

pisano_table[half_size+1] == 1 并且

pisano _ table[0:half _ size]= = pisano _ table[half _ size:size]

该算法工作正常,并为我们提供了小数字的正确答案。由于数字很大,Pisano 表的长度很长,计算和检测 Pisano 周期需要时间。为了减少计算时间,我们只需检查后面重复的 01 和 10(其他数字也可以)数字的周期。对于大型斐波那契运算,检测 Pisano 表将节省时间。

**高级问题 7:斐波那契数列的和**

寻找前 n 个斐波那契数列总和的最后一位数。(查找 F0 + F1 + … + Fn 的最后一位数字)

**解决方案:**

借助于皮萨诺周期,我们可以很容易地计算出任何 Fi 的最后一位数字。我们有 F0 + F1 + … + Fn = F(n+2) — 1。该算法将易于实现:

这里的一个技巧是加 10,以确保我们在 get_fibonacci_huge(n+2,10)的最后一位数字为 0 的情况下返回正余数。

**高等问题 8:斐波那契数列的部分和。**

寻找斐波那契数列部分和的最后一位数字:Fm + Fm+1 + + Fn。

**解决方案:**

如果我们得出 Fm + Fm+1 + … + Fn = F(n+2) — F(m+1)。实现该解决方案将会很容易

***注意:*** 我们只取 F(n+2) + 10 的最后一位数字,减去 F(m+1)的最后一位数字,模块得到该数字。

例如:F(n+2) = abc12,F(m+1) = def37,所以要得到正确的最后一位数字,我们必须做:(2+10–7)% 10

旁注:如果你喜欢这个帖子,你会喜欢:[https://www.coursera.org/learn/fibonacci/home/info](https://www.coursera.org/learn/fibonacci/home/info)。我喜欢关于迷惑斐波那契的讲座

**资源:**

最大公约数:[DPV08]第 1.2.3 节,[CLRS]第 31.2 节

计算斐波那契数:[DPV08]的第 0.2 节

斐波那契数列的性质:练习 0.2–0.4[dpv 08]

**参考文献:**

桑乔伊·达斯古普塔、克里斯托斯·帕帕迪米特里乌和乌梅什·瓦齐拉尼。算法(第一版)。麦格劳-希尔高等教育。2008.

托马斯·h·科尔曼,查尔斯·e·莱瑟森,罗纳德·L·李维斯特,克利福德·斯坦。算法导论(第三版)。麻省理工学院出版社和麦格劳-希尔。2009.

# 课程 1 —算法工具箱—第 2 部分:大 O 和贪婪

> 原文:<https://towardsdatascience.com/course-1-algorithmic-toolbox-part-2-big-o-and-greedy-6265d9065f05?source=collection_archive---------1----------------------->

上一篇文章中,我们讨论了与斐波那契数列相关的小编程问题和数学。为了知道算法有多快,我们使用 Big-O 符号。看看是什么!

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/4e15aab53dd172484c79f808692cfd64.png)

# Big-O 的定义来自[1]:

*设 f(n)和 g(n)是正整数到正整数的函数。如果有一个常数 c > 0 使得 f(n) ≤ c . g(n),我们就说 f = O(g)(意思是“f 的增长速度不比 g 快”)。*

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0d86ccf406532b38630b4519e0f4021c.png)

From lecture 3

当我们处理大 O 记数法时,我们通常会遇到对数。以下是使用对数的规则:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/7619e2c4c9903dd98798063b328c20a1.png)

From lecture 3

让我们解决一个小问题来暖一下我们的大脑吧!

***问题:表明对于任意实数常数 a 和 b,其中 b >为 0,*为 **

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b9499519fdb24370c2f45ac5a88fef79.png)

**证明:**

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/85d12c6dd7eca9645634f4000ae756f6.png)

***加分:*** 麻省理工给 Big-O 的作业太好了[3]。

# **贪婪**

贪婪算法首先做出局部最优选择。每个阶段,只是贪婪地做出选择,祈祷你会找到全局的答案。太贪心了。

***要做的步骤:***

-做出贪婪的选择。

-证明这是一个安全的举动。(也就是证明你确实贪婪)。

-归结为一个子问题。

-解决子问题。

**玩具问题举例:**

-由数字 3、9、5、9、7、1 组成的最大数字是多少?使用所有数字。

可能的解决方案:359179、537991、913579。。。

在用贪婪算法解决这个问题时,我们一步一步地做:

- **做一个贪婪的选择**:列表中的最大数字(数字 9)。

- **证明这是一个安全的移动**:列表只包含数字(没有大于 10 的数字),最大数字从有效的最大数字(不是零)开始。因此,选择最大数量是一个安全的举动。

- **归结为一个子问题**:我们选择最大数字(9),并将其从列表中移除。左边的列表包含[ 3,5,9,7,1 ]。我们在每一步都有更小的列表。

- **解决子问题**:我们又有一个列表,只要选择最大值,一直做,直到列表为空。你会得到答案的。

正确答案:997531。

# 分数背包

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/f10cebfae5a862b1acff9eded96cd37a.png)

分数背包问题是计算机科学中众所周知的问题。我们可以用贪婪策略来实现背包问题(简单版):

**输入:**权重 w1,w2,…,wn 和值 v1,v2,…。n 个项目的 VN;容量 w。找出一个容量为 w 的袋子中的物品的最大总价值(简单的版本,意味着我们可以将 wi 分成许多重量单位)

例:我们有 3 种包装:价值 20 美元,重 4 公斤;价值 18 美元,重 3 公斤;价值 14 美元,重 2 公斤。我们只能负重 7 公斤。那么,我们如何选择最大化我们的价值呢?

明确的答案是选择 2 公斤 14 美元、3 公斤 18 美元和 2 公斤 20 美元,这样我们就可以得到 14 美元+18 美元+20 美元/2 = 42 美元的价值。

注:2 公斤和 3 公斤的最大值为每单位 14/2 美元和 18/3 美元。

使用贪婪策略来解决这个问题。我们一步一步来。

- **做一个贪婪的选择:**尽可能多的选择单位重量价值最大的物品。

a = 20 美元/4 = 5 美元/单位

b = 18 美元/3 = 6 美元/单位

c = 14 美元/2 = 7 美元/单位。

所以我们先选 A,B 再选 c。

- **证明它是安全之举:**我们把任何物品 I 分成 wi 个更小的物品,每个物品都有 vi/wi 的单位重量。我们有 w0 + w1 + …+ w2 数量的单位项目。因为是单位,所以我们就尽可能选择单位有最大值的。

——**将问题化简为子问题:**在我们选择了一个贪婪的选择 wi 的值为 vi 之后,我们将其从列表中移除,这样我们就有 n-1 个项目对应 n-1 个值。

- **解决子问题:**一直选择每单位最大值,直到容量 W 满为止。(您可以将最后一个分成较小的重量,以适应容量 W)

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/bc3c841f22ba573e4c4f39ac065b1972.png)

From lecture 3

背包的运行时间是 O(n)。

# **问题 1:换钱**

该问题的目标是找到将输入值(整数)转换为面值为 1、5 和 10 的硬币所需的最少硬币数。

**输入格式:**输入由一个整数 m 组成。(1 < = m < = 10)

**输出格式:**输出改变 m 的面额为 1,5,10 的硬币的最小数量。

**样品 1:**

输入:3

输出:3

解释:3 = 1 + 1 + 1

**样品 2:**

输入:28

产出:6

解释:28 = 10 + 10 + 5 + 1 + 1 + 1。

***解:***

贪婪的选择是选择尽可能多的数量最大的硬币。

# 问题 2:最大化战利品的价值

一个小偷发现了比他的包能装得下的更多的赃物。帮助他找到最有价值的物品组合,假设战利品的任何部分都可以放进他的包里。

这个代码问题的目标是实现一个派系背包问题的算法。

**输入格式:**输入的第一行包含物品数量 n 和一个背包的容量 W。接下来的 n 行定义了项目的值和重量。第 I 行包含整数 vi 和 wi,分别是第 I 项的值和权重。

**输出格式:**输出适合背包的物品分数的最大值。你的程序的答案和最优值之差的绝对值最多应该是 10 ^ 3。为了确保这一点,输出您的答案时小数点后至少要有四位数字(否则,虽然您的答案计算正确,但可能会因为舍入问题而出错)。

**样品 1:**

**输入:**

3 50

6 20

100 50

120 30

**产量:** 180.0000

**解释:**
要达到数值 180,我们把第一个物品和第三个物品放进袋子里。

**解决方案:**

上面有伪代码,我们可以很容易地实现派系背包。

这里的要点是从价值/重量中选择最大指数。将该值添加到结果中,并清除原始列表中的值。

# **问题 3:在线广告投放收入最大化**

你有 n 个广告可以放在一个流行的网页上。对于每个广告,你知道广告商愿意为这个广告的一次点击支付多少钱。您已经在页面上设置了 n 个位置,并估计了每个位置每天的预期点击数。现在,你的目标是在广告位之间分配广告,以使总收入最大化。

**问题描述:**

给定两个序列 a1,a2,…,an (ai 是第 I 个广告的每次点击利润)和 b1,b2,…,bn (bi 是第 I 个槽的平均每天点击次数),我们需要把它们划分成 n 对(ai,bj),使得它们的乘积之和最大化。

**输入格式:**第一行包含整数 n,第二行包含整数 a1,a2,…,an 的序列,第三行包含整数 b1,b2,…,bn 的序列。

**输出格式:**输出∑ai*ci (1 ≤ i ≤ n)的最大值其中 c1,c2,.。。,cn 是 b1,b2,…,bn 的置换。

**样品 1。**

输入:1 23 39

产量:897

解释:897 = 23 39。

**解决方案:**

贪婪的选择是为每天点击次数最多的位置选择最高的每次点击利润。为了证明这是一个安全的选择,请阅读作业细节[2]。

所以,我们只要选择最高的 ai 和 bj,compute: result += ai。bj。

# **问题 4:收集签名**

你负责收集某栋大楼所有租户的签名。对于每个房客,你知道他或她在家的一段时间。你想通过尽可能少的拜访建筑物来收集所有的签名。

这个问题的数学模型如下。给你一组直线上的线段,你的目标是在一条直线上标记尽可能少的点,使每一段至少包含一个标记点。

**问题描述**

给定一组 n 个线段{[a0,b0],[a1,b1],。。。,[an-1,bn-1]}整数坐标在一条直线上,求最小 m 个数的点,使得每段至少包含一个点。也就是说,找出一组最小尺寸的整数 X,使得对于任何线段[ai,bi]都有一个点 x ∈ X 使得 ai ≤ x ≤ bi。

**输入格式:**输入的第一行包含段数 n。下面 n 行中的每一行都包含两个整数 ai 和 bi(用空格分隔),它们定义了第 I 个线段端点的坐标。

**输出格式:**第一行输出最少 m 个点,第二行输出 m 个点的整数坐标(用空格隔开)。您可以按任意顺序输出点。如果有很多这样的点集,可以输出任意一个集合。(不难看出,总是存在一组最小尺寸的点,使得所有这些点的坐标都是整数。)

**样品 1:**

**输入:3**

1 3

2 5

3 6

**输出:** 1 3

**说明:**
在这个样本中,我们有三段:[1,3],[2,5],[3,6](长度分别为 2,3,3)。都包含坐标为 3 的点:1 ≤3 ≤3,2 ≤3 ≤5,3 ≤ 3 ≤ 6。

**样品 2:**

**输入:** 4

4 7

1 3

2 5

5 6

**输出:** 2

3 6

**解释:**
第二和第三段包含坐标为 3 的点,而第一和第四段包含坐标为 6 的点。所有四个线段不能被一个点覆盖,因为线段[1,3]和[5,6]是不相交的。

**解决方案:**

贪婪的选择是选择最小的右端点。然后删除包含该端点的所有线段。继续选择最小右端点并移除线段。

# **问题 5:最大化竞赛中的奖励名额数量**

你正在为孩子们组织一场有趣的比赛。作为奖励基金,你有 n 颗糖果。你想用这些糖果在一场自然限制的比赛中争夺前 k 名,位置越高,糖果越多。为了让尽可能多的孩子快乐,你要找到可能的 k 的最大值。

**问题描述**

这个问题的目标是将一个给定的正整数 n 表示为尽可能多的两两不同的正整数之和。也就是求 k 的最大值使得 n 可以写成 a1+a2+ +ak 其中 a1,…,ak 是正整数,ai!= aj for all 1 ≤i

**输入格式:**输入由单个整数 n 组成。

**输出格式:**在第一行,输出最大数 k,使得 n 可以表示为 k 个两两不同的正整数之和。在第二行中,输出 k 个两两不同的正整数,总和为 n(如果有许多这样的表示,则输出其中任何一个)。

**样品 1:**

输入:6

输出:3

1 2 3

**样品 2:**

输入:8

输出:3

1 2 5

**解决方案:**

贪婪的选择是从 1,2…中选择最小的数,将它们相加为 S,如果你的最小数大于(input_number— S)/ 2。你找到了最后一个号码。为什么?请看一下作业细节[2]。

# **高级问题 6:最大化你的工资**

作为一次成功面试的最后一个问题,你的老板给你几张写有数字的纸,让你用这些数字组成一个最大的数字。得出的数字就是你的工资,所以你非常想最大化这个数字。你怎么能这样做?

在讲座中,我们考虑了以下算法,用于从给定的一位数中合成最大的数。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c3d3763eb583529d5d00e22c85bdfad6.png)

不幸的是,这种算法只在输入由一位数组成的情况下有效。例如,对于由两个整数 23 和 3 组成的输入(23 不是个位数!)它返回 233,而实际上最大的数字是 323。换句话说,使用输入中最大的数字作为第一个数字并不安全。

你在这个问题中的目标是调整上面的算法,使它不仅适用于一位数,而且适用于任意正整数。

**问题描述**

组成一组整数中的最大数。

**输入格式:**输入的第一行包含整数 n .第二行包含整数 a1,a2,…,an。

**输出格式:**输出 a1,a2,.。。,安。

**样品 1:**

**输入:2**

21 2

**输出:**

221

**说明:**
注意,在这种情况下,上述算法也返回不正确的答案 212。

**解决方案:**

这个问题有很多边缘情况。根据上面的伪代码,要点是用正确的方法检查“digit ≥ maxDigit”。因为它不再仅仅是数字,你必须确保 12 和 2 => 221,22 和 225 => 22522…为此,比较两个数的策略是:

*   把 A 变成字符串 A
*   把 B 变成字符串 B
*   比较两个字符串 AB 和 BA

**资源**:

背包:[BB]第 6.5 节

**参考文献**:

[1]:[DPV]第 0.3 节

[2]:[https://www . coursera . org/learn/algorithm-toolbox/programming/kAiGl/programming-assignment-2-greedy-algorithms](https://www.coursera.org/learn/algorithmic-toolbox/programming/kAiGl/programming-assignment-2-greedy-algorithms)

[3]:[https://OCW . MIT . edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/assignments/MIT 6 _ 006 F11 _ PS1 _ sol . pdf](https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/assignments/MIT6_006F11_ps1_sol.pdf)

[DPV]桑乔伊·达斯古普塔、克里斯托斯·帕帕迪米特里乌和乌姆什·瓦齐拉尼。算法(第一版)。麦格劳-希尔高等教育。2008.

吉勒·布拉萨尔和保罗·布拉特利。算法基础。普伦蒂斯-霍尔。1996.

# 课程 1 —算法工具箱—第 3 部分:分而治之

> 原文:<https://towardsdatascience.com/course-1-algorithmic-toolbox-part-3-divide-and-conquer-dd9022bfa2c0?source=collection_archive---------0----------------------->

我们将在本文中讨论各个击破。我对这个话题很感兴趣。我希望你也能。完成作业花了我很多时间。我强烈建议你认真对待作业,并尝试解决两个高级问题。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/1f33e9ffeee81911be205e5349c70f44.png)

Divider.

# 各个击破

分而治之的步骤:

1.  分解成相同类型的不重叠的子问题。
2.  解决子问题。
3.  结合结果。

# 二分搜索法:在排序数组中搜索。

**输入:**一个排序数组 A[low…high] (A[i] < A[i+1])和一个 key k。

**输出:**一个指标,I,其中 A[i] = k

否则,最大指数 I,其中 A[i] < k.

Otherwise (k < A[low]), the result is low — 1.

**样本 1:**

输入:[3,5,9,20,27,52,65]和一个键 20。

输出:3。

**样品 2:**

输入:[3,5,9,20,27,52,65]和一个键 7。

输出:1。

我们将使用分治算法来解决这个问题。我们将一步一步地解决它。

1.  分解成相同类型的不重叠的子问题。

*   输入数组已排序。我们将它分成两半数组。因此在两个子阵列之间没有重叠元素。

2.解决子问题。

*   我们有两个子问题:A 和 B。我们比较密钥 k 和 B[0]。
*   如果 B[0] == k = >我们找到了结果(结果= B[0]的索引)
*   如果 B[0] ≤ k = >我们选择数组 a。(结果= B[0]的索引)
*   如果 B[0] ≥ k = >我们选择数组 B。(结果= A[0]的索引)

3.结合结果。

*   只需返回结果。

**伪代码:**

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/85802f35e8f8f9988a040f41e959b313.png)

Binary Search

二分搜索法的运行时间是 **O(logn)** 。

# 乘法多项式

这是非常有趣的部分。我们想要乘以大整数。我们如何做到这一点?除此之外,乘法多项式还应用于纠错码、生成函数、信号处理中的卷积等

我们有两个 n 位数:x 和 y(基数 r = 2,10)。

我们将 x 和 y 表示成两种形式:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9f968d345632f277a5637a10e6afc360.png)

Karatsuba’s method

# 主定理

计算递归算法的 Big-O。我们将公式一般化,并在一般情况下求解。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c570bea7de4ab111c8f4a8a0a79dbf6b.png)

Master Theorem

这个公式的证明在[2]中。在本文中,我们将关注问题的解决。:)

# 合并排序

MergeSort 喜欢这个名字,分解和合并。这个概念非常简单:

*   分成两个子列表,直到只剩下一个元素。
*   按顺序合并 2 个子列表(排序)。

**伪代码:**

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/d73262874ab77bd2dbd2adb6f05afe2d.png)

MergeSort

这里的关键点是合并策略。循环思考 2 个子数组,选择较小的元素,放入第三个数组。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b045eb3f55777ac1ce509e717e2ba275.png)

Merge strategy

大 O 就是 **O(nlogn)。**

**注意:**下面有一个有趣的问题,需要我们调整合并策略。通过调整,你会完全理解合并排序。

# 快速排序

快速排序的概念是选择一个支点,重新排列数组,使左支点上的所有元素都小于支点,右支点上的所有元素都大于支点。

**例**:对于数组 A = [ **6** ,4,2,3,9,8,9,4,7,6,1]

如果我们选择 A[1] = 6 是一个支点,我们需要把 A 重新排列成:

[1,4,2,3,4,6, **6** ,9,7,8,9]

快速排序算法分两步实现:

*   选择支点,重新排列成 A[left] ≤A[pivot] < A[right].
*   Keep choosing pivot and re-arrange A[left] and A[right].

The pseudocode for this 2 steps:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c6d2e65c45194e03900f31f4fb82b9f7.png)

QuickSort

As you see, l is left index, r is right index.

Partition(A, l, r) function is containing choosing pivot, and rearrange A from l(left-index) to r(right-index) into A[left] ≤ A[pivot] ≤ A[right] and returning the position(index) of A[pivot].

To choose pivot, we have many strategies. If we know what types of data we have, we will have better choice of pivot. There are common choices of pivot:

*   Choose A[0] as a pivot.
*   Choose random from l -> r 作为支点
*   [中位数的中位数](https://en.wikipedia.org/wiki/Median_of_medians)算法。

以[0]为轴心重新排列数组的伪代码:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/1edb21af791d9de085b953a4b5b031ef.png)

QuickSort partition

x 将是 pivot 的值。

我们想把 A[l…r]重新排列成 A[left] ≤ A[pivot] ≤ A[right]用

左=左

A[右] = A[j+1…r]

我们将使用最后一个示例来说明如何将 A = [ **6** ,4,2,3,9,8,9,4,7,6,1]转换为[1,4,2,3,4,6, **6** ,9,7,8,9]。

left = 0,r = 10,pivot A[0] = 6。

假设我们有一个[left]只有一个[l]元素,所以 j = 0。

我们从 l+1 到 r 循环 I,每一步都要保证 A[left…j] ≤ A[pivot] ≤ A[j+1…r]。有两种情况:

*   A[i] ≥ A[pivot],我们什么都不做,因为它已经是 A[i]≥ A[pivot]。
*   A[i] ≤ A[pivot],我们有 A[j]是小于 A[pivot]的元素的最后一个索引。因此,我们需要交换 A[j+1]和 A[i]并标记 j = j+1,以表明 j 是小于 A[pivot]的元素的最后一个索引。通过交换 A[j+1]和 A[i],我们确定 A[l…j] ≤ A[pivot] ≤ A[j+1…r]。

如果您手动运行该算法,我们将得到 A =[ **6** ,4,2,3,4,6,1,9,7,8,9]。

所以我们现在有了数组:A[l] + A[1…j] + A[j+1…r]。最后一步是交换 A[j]和 A[0],这样我们将得到数组:A[0…j-1] + A[pivot] + a[j+1…r]。

于是我们得到:A = [1,4,2,3,4,6, **6** ,9,7,8,9]。

## **随机化枢纽:**

要实现随机化透视,很简单,只需:

*   选择随机支点 l ≤ k ≤ r。
*   交换 A[0]和 A[k]
*   保持分区就像选择一个[o]作为支点。

随机化 pivot 快速排序的 Big-O 在平均运行时间上是 **O(nlogn)** 。最坏的情况运行时间是 **O(n )** 。这个大 O 有点棘手。你可以阅读[3]中的证明。

**注:**快速排序最差的情况是 **O(n )** 但是在实践中,快速排序平均给了我们比合并排序更好的性能。

## Equal Elements:带有一些 uniq 元素的快速排序。

你可以在这里看到快速排序的[可视化](https://www.toptal.com/developers/sorting-algorithms/quick-sort)。我们可以观察到,如果数据有许多相等的元素,快速排序需要很长时间才能完成。为了优化,我们应该有不同的分区策略:

而不是把 A 重新排列成 A[左]≤A[支点]≤A[右]。我们将把 A 变成:A[左]≤A[m1…m2]≤A[右]有 A[I]= = A[支点] (m1 ≤ i ≤ m2)。

所以在每一步,我们都少了一个[左],一个[右]项要排序。

相等元素的伪代码:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/1a80769bd85a8f813a9888b24faa6489.png)

Equal elements QuickSort

在练习中,我们将了解该算法的实现细节。

完全理解这些排序算法和分而治之技术的最好方法是解决有趣的问题。大家一起解决吧。

# 问题 1:实现二分搜索法

在这个问题中,你将实现二分搜索法算法,该算法允许非常有效地搜索(甚至是巨大的)列表,只要列表是排序的。

**输入格式。**输入的第一行包含一个整数 n 和一个序列 a0 < a1 <。。。< a(n-1)个按升序排列的 n 个两两不同的正整数。下一行包含一个整数 k 和 k 个正整数 b0,b1,.。。,b(k-1)。

**约束。** 1 ≤ n,**k≤10⁵**;对于所有 0 ≤i < n,1≤ai≤10⁹**t11;对于所有 0≤j < k,1≤bj≤10⁹;**

**输出格式。**对于所有的 0 ≤ i ≤ k-1,输出一个索引 0≤j≤n-1,使得 aj = bi,如果没有这样的索引,则输出-1。

**样本。**

**输入:**

5 1 5 8 12 13

5 8 1 23 1 11

**输出:**

2 0 –1 0 –1

**解释:** 在这个例子中,给我们一个长度为五的递增序列 a0 = 1,a1 = 5,a2 = 8,a3 = 12,a4 = 13,五个键进行搜索:8,1,23,1,11。我们看到 a2 = 8,a0 = 1,但是键 23 和 11 没有出现在序列 a 中,为此,我们输出一个序列 2,0,1,0,1。

## 解决方案:

我们只是按照上面的二进制搜索伪代码,实现二进制搜索算法。最后,我们循环搜索关键字 b0,b1,…,bk,并运行 **binary_search(a,bi)** 。

BinarySeach

# 问题 2:寻找多数元素

多数规则是一种决策规则,它选择拥有多数票(即超过半数的选票)的方案。
给定一系列元素 a1,a2,.。。,您希望检查它是否包含出现 n/2 次以上的元素。下面是一个简单的方法。

该算法的运行时间是二次的。你的目标是使用分治技术设计一个 O(n log n)算法。

**输入格式。**第一行包含整数 n,下一行包含 n 个非负整数 a0,a1,.。。,一个 1。

**输出格式。**如果序列包含一个严格出现次数超过 n/2 次的元素,则输出 1,否则输出 0。

**样品 1。**

**输入** : 5

2 3 9 2 2

**输出**:

1

**解释:** 2 是多数元素。

**样品 2。**

**输入** : 4

1 2 3 1

**输出**:

0

**解释** :
这个序列也没有多数元素(注意元素 1 出现了两次,因此不是多数元素)。

你可能已经猜到了,这个问题可以通过分治算法在时间 O(nlogn)内解决。事实上,如果一个长度为 n 的序列包含一个多数元素,那么这个元素也是它的一半的多数元素。因此,要解决这个问题,首先要将一个给定的序列分成两半,并进行两次递归调用。你看到如何组合两个递归调用的结果了吗?

有趣的是,这个问题也可以通过更高级的(非分治)算法在 O(n)时间内解决,该算法只需扫描给定序列两次。

## **解决方案:**

这是一个超级有趣的问题,如果你用分而治之来解决它。

首先,我们需要分成子问题。在每一步,我们将序列分成两个左半序列和右半序列。主要工作是编写合并策略:

为了确定**序列**的多数,我们将把**的左半部分**或**的右半部分**表示成两个元素的数组: **A【多数,其他】**。

**majorities = A[0]** 包含在此序列中占多数的所有元素(相同值)。名为 **A_major_elements**

**others = A[1]** 是内部没有 A_major_elements 的所有元素。

示例:

left_half = [[2,2,2],[3,5,7,7,9]]

right_half = [[5,5,5,5],[4,8,10,34,2,10,10]]

**count_merge** 函数将基于**左 _half** 和**右 _half** 返回 **M【多数,其他】**。

为此,我们会:

*   从**右半部分[1]** ([4,8,10,34,2,10,10])**中取出所有**左半部分主元素**(例:2) 放入**左半部分[0】。****
*   **从**左半部分**(【3,5,7,7,9】)**,**中取出所有**右半部分主元素(ex: 5)** 放入**右半部分【0】。****

**所以我们得到了:**

**left_half = [[2,2,2, **2** ],[3,7,7,9]](加 2,减 5)**

**right_half = [[5,5,5,5, **5** ,[4,8,10,34,10,10]](加 5,减 2)。**

**我们称这个过程为 **chunk_process** :**

**在 **count_merge** 函数中,我们只需要根据 **chunk_left** 和 **chunk_right** 选择多数。**

**最终我们会有一个结果数组:**【多数,其他】**。我们只需检查大多数的长度超过 n/2 就可以给出答案。**

# ****问题 3:改进快速排序****

**这个问题的目标是重新设计随机快速排序算法的一个给定实现,使它即使在包含许多相等元素的序列上也能快速工作。**

****问题描述****

****任务。**为了强制快速排序算法的给定实现有效地处理具有很少唯一元素的序列,您的目标是用 3 路分区替换 2 路分区。也就是说,您的新分区过程应该将数组分成三部分:< x 部分、= x 部分和> x 部分。**

****输入格式。**输入的第一行包含一个整数 n。下一行包含 n 个整数 a0,a1,.。。,a(n-1)。**

****输出格式。**输出这个按非降序排序的序列。**

****样品 1。****

**输入:5**

**2 3 9 2 2**

**输出:**

**2 2 2 3 9**

## **解决方案:**

**正如我们上面所说的,我们需要为具有许多相等元素的数据优化 QuickSort。因此,我们将阵列划分为 3 个阵列:**

**A[left]≤A[m1…m2]≤A[right]with A[I]= = A[pivot](m1≤I≤m2)。**

**我们的主要工作是实现 partition3 函数来重新排列数组 A 并返回位置 m1,m2。**

**我们从 l + 1 到 r 循环遍历所有元素 I。在每一步,我们确保将 a[i]重新排列到正确的位置。**

**例如,如果 **a[i] ≤ pivot** (a[l]),我们将 a[i]重定位到 a[begin…end](与 pivot 具有相同的值)。我们继续检查是否 **a[i] == pivot** ,我们通过什么都不做来保持那个位置,如果 **a[i] < pivot** ,我们需要在 a[begin]之前移动 a[i]。**

**最后,我们只需交换 a[0](pivot)和 a[begin]就可以得到分区(重新排列)数组 a 的最后一步。**

**这似乎很难理解,但如果你手动运行它,它会在你的手下非常清晰(我保证)。**

# **高级问题 4:一个数据离被排序有多近?**

**序列 a0,a1,…,a(n-1)的逆是一对指数 0 ≤i < j < n such that ai > aj。在某种意义上,一个序列的反转次数衡量了这个序列接近被排序的程度。例如,一个排序的(非降序)序列根本不包含反转,而在一个降序排序的序列中,任何两个元素构成一个反转(总共 n(n-1)/2 个反转)。**

****问题描述****

****任务。**这个问题的目标是统计给定序列的求逆次数。**

****输入格式。**第一行包含一个整数 n,下一行包含一个整数序列 a0,a1,…,a(n-1)。**

****输出格式。**输出序列中反转的次数。**

****样品 1。****

****输入** : 5**

**2 3 9 2 9**

**输出:2**

**说明:
这里的两个逆是(1,3)(a1 =3 > 2=a3)和(2,3)(a2 =9 > 2=a3)。**

**这个问题可以通过修改合并排序算法来解决。为此,我们将合并和合并排序过程更改如下:**

*   **Merge(B,C)返回排序后的数组和对(B,C)的数量,使得 b ∈ B,c ∈ C,b > c**
*   **MergeSort(A)返回一个已排序的数组 A 和 A 中的求逆次数。**

## **解决方案:**

**我们的目标是打印反转次数 ai > aj ( 0 ≤ i ≤ j ≤ n)。我们将数组 A 分为 **left_array** 和 **right_array** ,格式:**【count _ inversions,sorted_elements】。****

**在 **merge** 函数中,我们可以实现类似 MergeSort 的功能,我们需要为具有**left _ array[1][I]>right _ array[1][I]**的步骤添加 **count_inversions** 。**

# **高级问题 5:组织一次抽奖**

**您正在组织在线抽奖。为了参与,一个人在一个整数上下赌注。然后,随机绘制几个连续整数的范围。然后,参与者的收益与包含该参与者数字的区间数减去不包含该数字的区间数成正比。你需要一个有效的算法来计算所有参与者的收益。一种简单的方法是扫描所有参与者的所有范围列表。然而,你的彩票很受欢迎:你有成千上万的参与者和成千上万的范围。出于这个原因,你不能负担一个缓慢的幼稚算法。**

****问题描述****

****任务**。给你一组线上的点和一组线上的线段。目标是为每个点计算包含该点的线段数。**

****输入格式**。第一行包含两个非负整数 s 和 p,分别定义线段数和一行上的点数。接下来的 s 行包含定义第 I 个段[ai,bi]的两个整数 ai,bi。下一行包含定义点 x1、x2、.。。,xp。**

****输出格式。**输出 p 个非负整数 k0,k1,.。。k(p-1),其中 ki 是包含 xi 的片段的数量。**

****样品 1。****

****输入:****

**2 3**

**0 5**

**7 10**

**1 6 11**

****输出:****

**1 0 0**

****解释:**
这里我们有两段([0,5],[7,10])和三点([1,6,11])。第一个点仅位于第一个线段中,而其余两个点位于所有给定线段之外。**

****样品 2。****

****输入:****

**3 2**

**0 5**

**-3 2**

**7 10**

**1 6**

****输出**:**

**2 0**

**您可能已经猜到,您的目标是首先以某种方式对给定的片段进行排序(因此,这是一个排序问题,而不是分治问题)。**

## **解决方案:**

**这个问题很有挑战性。为了解决这个问题,我们必须通过一些策略对给定的片段进行排序。**

**我们将[ai,aj]和 POINT[pk]变成 3 个数组:[ai,LEFT],[aj,RIGHT],[PK,POINT]左= 1,点= 2,右= 3。**

**我们尝试排序[ai,LEFT] + [aj,RIGHT] + [pk,POINT]。为了比较两个元素[x,左/右/点]和[y,左/右/点],我们比较 x 和 y,如果 x == y,我们根据左= 1,点= 2,右= 3 的值比较左/右/点。**

**例如,我们有段:[0,5],[-3,2],[7,10]和点:[1,6]。我们把这些变成:[0,左],[5,右],[-3,左],[2,右],[1,点],[6,点]。然后,我们使用问题 3 中的 RandomizedQuickSort 对这些项目进行排序,我们将得到一个排序后的数组:**

**[-3,左],[0,左],[1,点],[2,右],[5,右],[6,点],[10,右]。**

**为了计算覆盖段,我们只需要计算在“点”项之前出现了多少(“左”-右”)项。**

**为了通过这个问题,我必须提交 11 次大量的变化策略。所以,不要放弃,你比你想象的要优秀!**

# **高级问题 6:寻找最近的一对点**

**在这个问题中,你的目标是在给定的 n 个点中找到最近的一对点。这是计算几何中的一个基本原语,在例如图形、计算机视觉、交通控制系统中有应用。**

****问题描述****

****任务**。给定平面上的 n 个点,求一对两点(不同点)之间的最小距离。**

****输入格式**。第一行包含点数 n。下面 n 条线的每一条定义一个点(,易)。**

****输出格式**。输出最小距离。你的程序的答案和最优值之差的绝对值最多应该是 1/10。为了确保这一点,输出您的答案时小数点后至少要有四位数字(否则,虽然您的答案计算正确,但可能会因为舍入问题而出错)。**

****样品 1** 。**

****输入**:**

**11**

**4 4**

**-2 -2**

**-3 -4**

**-1 3**

**2 3**

**-4 0**

**1 1**

**-1 -1**

**3 –1**

**-4 2**

**-2 4**

****输出**:**

**1.414213**

**说明:最小距离√2。这个距离上有两对点:(1,1)和(2,2);(2,4)和(1,3)。**

****解决方案:****

**在[CLRS]中有一章讨论这个问题,我认为这个问题是这门课中最难的。我经常遇到失败案例#22/23:超过时间限制。所以,一定要先看[CLRS]教材,[33.4 节]关于这个问题。**

**我们需要通过 x 坐标对点进行预排序,并将点分成两个数组: **left_points** 和 **right_points。**我们的目标是计算最小距离的**左 _ 点**称为**最小 _ 左**,最小距离的**右 _ 点**称为**最小 _ 右**。和**左 _ 点**和**右 _ 点**之间的最小距离称为**混合 _ 最小。****

**对于小尺寸的数组(首先是尺寸< 3), we use compute min distance by brute-force. We try to compute **左 _ 最小**和**右 _ 最小**。我们称**左 _min** 和**右 _min** 的最小距离为**分隔 _min** 。因为 left_points 和 right_points 是按 x 排序的,并且我们确实计算了 **separated_min** ,所以不计算**所有 left_points** 的 **hybrid_min** 和**所有** **right_points** 的 **separated_min** 半径内具有 x 坐标的点,我们只关心从中线**line _ l =(left _ points【last】。x + right_points[first]。x)/2。****

**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e1323c1150244fe666fe7146243c4f46.png)**

**Red points are in separated radius.**

**我们减少了具有 x 坐标的点在**分隔 _ 最小**半径之间的数量。我们称这些缩减点为**缩减 _ 总计**点。我们可以看到,要计算一个点到 reduced_total 点中所有点的最小距离,我们只需要计算 reduced_total 点中 y 坐标在 **separated_min** 内的 7 个点。表示**减总**点 x 点的边界是一个矩形,其**宽= 2 x 分隔最小**,**高=分隔最小**。为此,我们需要按 y 坐标对**约简总数**点进行排序,并遍历所有排序后的约简总数点,计算 7 点边界的最小距离。然后我们可以最终计算混合最小距离。**

**很难找到实现这种算法的正确方向。一旦你实施正确,你将通过这个问题。我确实为这个问题提交了 17 次。所以保持冷静,继续提交。:D**

****资源:****

**[1]:[https://OCW . MIT . edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/LEC 11 . pdf](https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/MIT6_006F11_lec11.pdf)**

**[2]主定理:[DPV08]的第 2.2 节**

**[3]快速排序:[CLRS]的第 7 章**

**多项式乘法:[DPV08]的第 2.1 节**

**合并排序和基于比较排序的下限:[DPV08]的第 2.3 节**

**快速排序:[CLRS]第 7 章**

****参考文献:****

**Sanjoy Dasgupta、Christos Papadimitriou 和 Umesh Vazirani。算法(第一版)。麦格劳-希尔高等教育。2008.**

**托马斯·h·科尔曼,查尔斯·e·莱瑟森,罗纳德·L·李维斯特,克利福德·斯坦。算法导论(第三版)。麻省理工学院出版社和麦格劳-希尔。2009.**

# 课程 1 —算法工具箱—第 4 部分:动态编程

> 原文:<https://towardsdatascience.com/course-1-algorithmic-toolbox-part-4-dynamic-programming-223ffc01984a?source=collection_archive---------0----------------------->

动态规划是一种非常强大的算法设计技术,用于解决许多指数问题。实际上,动态编程喜欢递归和“重用”。因此,为了解决动态编程的问题,我们分两步来做:

*   找出正确的重现(子问题)。
*   计算并记忆所有子问题的结果以“重复使用”。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c00a777cfb8b7ef1ad2c27ae94915054.png)

Dynamic Drinking.

让我们来看看**换钱**问题:

我们有 3 种硬币:6 分、5 分和 1 分。兑换 9 美分最少需要多少硬币?

为了用动态规划解决这个问题,我们要做的第一件事是找到这个问题的正确递归。

我们可以看到,需要兑换 9 美分的最小硬币数是我们需要兑换 3(= 9–6)或 4(= 9–5)或 8(= 9–1)的最小硬币数。所以我们有复发:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/179c8b86a7ea372e4de7537139c4c493.png)

Change Money Recurrence.

我们可以使用递归技术来实现这个问题,但它会非常慢。为什么慢?当我们想改变 76 美分时,看一看递归树:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/330ac8cf26bba88e93636948b58862f2.png)

Change 76 cents.

正如我们在可视化的树中看到的,为了计算最小数目的零钱 76 美分,我们计算最小数目的零钱 70 美分、71 美分、75 美分等等……零钱 **70** 的计算重复了 3 次,对于大量的零钱,我们将重复许多子问题。这要花很多时间。所以,动态规划通常喜欢“存储所有子问题的结果并重用”。这个想法是,如果我们计算改变了 70 美分,我们不需要再次计算,我们只是返回计算结果 70 美分。

实现动态编程的好方法是我们首先计算更小的子问题,将结果保存到 dp_array 中,继续直到我们得到结果。例如,要解决兑换 9 美分的问题,我们将计算我们想要兑换 0、1、2、3、4 的最小硬币数…首先,将结果保存到 dp_array 中,继续直到我们计算出 9 美分并得到答案。

让我们一步一步来:

1.  计算兑换 0 美分=> 0 硬币的最小值

dp_array = [0]

2.计算最少兑换 1 美分=> 1 枚硬币

dp_array = [0,1]

3.计算最小找零 2 美分= > 2 个硬币

dp_array = [0,1,2]。

解释:2 分钱= 1 分钱+ 1 分钱。

4.计算最少兑换 3 美分=> 3 枚硬币

dp_array = [0,1,2,3]。

解释:3 分= 1 美分+ 1 美分+ 1 美分。

……

8.计算最少找 7 美分= > 2 美分

dp_array = [0,1,2,3,4,1,1,2]。

**解释:**在我们计算 6 美分的步骤,很容易发现 dp_array = [0,1,2,3,4,1,1]。

要计算 7 美分的变化,按照循环,我们需要找到(compute(6 = 7–1),compute(2 = 7–5),compute(1 = 7–6)的最小值。

我们有 dp_array 是计算小钱的缓存结果,所以我们有 compute(6) = 1,compute(2) = 2,compute(1) = 1。这些计算货币的最小值是 1。我们有两个选择:

*   1 from compute(6= 7 -1)表示我们选择 1 美分,需要 6 美分。
*   1 from compute(1 = 7–6)表示我们选择 6 美分,还需要 1 美分。

所以,我们需要 2(= 1 + 1)枚硬币将 1、5、6 美分换成 7 美分。

继续这样做,我们可以构建最终的 dp_array = [0,1,2,3,4,1,1,2,3,4]。

换 9 分钱的答案是 dp_array[9] = 4 个硬币。

总而言之,我们将首先计算更小的子问题,保存结果,在每一步,我们从保存的结果中重用子问题结果。一直做下去,直到我们得到答案。

换钱问题的伪代码:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b82da45681ea852675cc4a6e820b8e1d.png)

# 编辑距离问题

编辑距离问题有点难以理解的问题和解决的思路。但是一旦你理解了,问题似乎就很清楚了,很容易用动态编程来解决。

你可以看到,当你输入拼写错误的东西时,拼写检查器会通过在字典中查找附近的其他单词来发出警告。什么是测量标准?换句话说,我们如何计算两个字符串之间的距离?

计算两个字符串之间距离的自然方法是扩展,对齐以匹配尽可能多的字符。例如,下雪和晴天的两种可能路线**【1】**

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/944555bddb4e4bd70048833c8808e45d.png)

我们试图将下雪和晴天的字符串对齐,以获得尽可能多的匹配字符。匹配的字符越多,我们的距离就越近。如果我们把“S_NOWY”中的“_”叫做**插入运算符**或者**删除运算符**(从 SUNNY 中删除“U”)和“SUNN_Y”中的“_”是一样的,**插入运算符**到 SUNNY 或者**删除运算符**(在 SNOWY 中删除 W)而不匹配(O 和 N)就是不匹配运算符。那么我们可以说,将晴天转换为下雪天需要花费 3 个操作符(插入或删除,不匹配)。它们是"插入 U "、"替换 O-N "和"删除 W "。

**注意:**我们将使用**插入操作符**表示上面字符串中的“_ ”,使用**删除操作符**表示下面字符串中的“_”。

因此,两个字符串的距离是将字符串 A 转换为字符串 b 的插入、删除和不匹配操作符的最小数目。

## **计算编辑距离**

为了计算 A 和 B 的编辑距离,我们需要找出正确的递归。我们有 A[1…n],B[1…m]叫做字符串 A 有 n 个字符,字符串 B 有 m 个字符。我们会找到子问题的关系:A[1…i],B[1…j]。我们有 4 种方法来对齐 A[1…i],B[1…j]。

*   插入:A[1…i, **"_** "]和 B[1…j-1, **B[j]** ]
*   删除:A[1…i-1, **A[i]** 和 B[1…j, **"_"** ]
*   错配 A[1…i-1, **A[i]** 和 B[1…j-1, **B[j]** ],是 A[i] ≠ B[j]
*   匹配 A[1…i-1, **A[i]** ]和 B[1…j-1, **B[j]** ],就是 A[i] = B[j]

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/cd972a95906e767bc3bd6bccd0b099dd.png)

4 ways to align

要计算 A[1…i]和 B[1…j]之间的距离 D(i,j),我们需要计算 D(i,j-1),D(i-1,j),D(i-1,j-1)。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8ae65680f3b5e1476e98f8681ec37dc3.png)

Edit distance recurrences.

因此,我们将使用二维数组来保存计算出的距离 D。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/42be82ca20a2f2266f8f51ba441e1db6.png)

很明显,如果 D(0,j) = j 和 D(i,o) = i。

为了计算 D(1,1),我们只需要从这个计算出的距离 D 中得到值 D(i,j-1),D(i-1,j),D(i-1,j-1),意味着 D(1,0),D(0,1),D(0,0)。

那么 **D(1,1) = min(2,2,1) = 1**

以此类推,我们计算 D(1,2),D(1,3)…。,D(8,7)。我们有了最终计算出的距离 d。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/88030af4dc9943df9808336cf3e922cf.png)

我们看到,编辑和距离的距离是 D(7,8) = 5。

从表中,我们可以很容易地得到距离的编辑和距离是 D(4,8) = 6。每个子问题都被计算、存储以便重复使用。这是动态编程。

编辑距离的伪代码:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/65096a6545baeb6b4b06f1234355ae3e.png)

## **回溯**

回溯是动态编程的一部分。我们在编辑和距离之间找到了编辑距离的答案,但是我们怎样才能打印出对齐的结果呢?

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e061e4200a1a78eef809820bd7608b51.png)

如果我们有缓存的结果——计算的距离 d,回溯就很容易。

我们将从 D(7,8)(最终结果),知道 D(7,8)是如何对齐的,我们只是检查我们如何计算 D(7,8)的值是从 D(7,7),D(6,8)和 D(6,7)。在这种情况下,D(7,8) = D(6,7) + 1 是不匹配运算符。继续这样做,我们找到最终的排列。

我们可以概括我们回溯结果的方式,要知道 D(i,j)是对齐的,我们需要检查**插入操作符** - D(i,j-1) + 1、**删除操作符**-D(I-1,j) + 1、**不匹配操作符**-D(I-1,j-1)和**匹配操作符** D(i-1,j-1)。D(i,j)是这些值中的一个。

回溯编辑距离的伪代码:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/831739ca75a742e0b324aa5eb3d6f4ae.png)

# 离散背包问题

在上一篇关于 Big-O 和 Greedy 算法的文章中,我们讨论了分数背包,也就是物品可以被分割。离散背包问题就不同了,每个物品要么拿,要么不拿。

有两种类型的离散背包:有重复和没有重复。

*   有重复:有无限的项目,你可以采取每个项目多次你想要的。
*   没有重复:每个项目都有一个,所以每个项目,你只能拿一个。

## 重复背包问题。

**输入:**我们有 n 个项目,权重为:w1,w2,…,wn,值为 v1,v2,…,vn。和总容量重量:w

**输出:**重量不超过 w 的物品的最大值**每个物品可以使用任意次。**

为了用动态编程解决这个问题,我们需要找出递归(子问题)并设计缓存结果。

正如我们很容易看到的,要找到项目 W(w1 + w2 + …wi)的最大值,我们需要找到(w2 + w3 + … + wi) + v1,(w1 +w3 + … + wi) + v2,…..、(w1+w2+…+w(i-1)) + wi。形式上,

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/3aa0681670b43e27aa4d0c45553064ad.png)

这就是我们需要的重现。所以我们计算 W = 1,W=2,…的最大值..直到我们得到 W = W。我们只需要一个数组 W[0…W]来保存缓存的结果。

重复背包问题的伪代码:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/cdd3bfe5e17266d4332ce32ce4ead651.png)

## 背包无重复问题。

**输入:**我们有 n 个项目,权重为:w1,w2,…,wn,值为 v1,v2,…,vn。和总容量重量:w

**输出:**重量不超过 w 的物品最大值**每件物品最多使用一次。**

为了找出这个问题的重复出现,我们需要澄清要点:

*   每一项要么拿,要么不拿。换句话说,W(w1,w2,…)的最大值。wi)由 W(w1,w2,…,wi)或 W(w1,w2,…)的最大值构成。w(i-1))。形式上:

**value(w,i)** = max{ **value(w-wi,i-1) + vi** , **value(w,i-1)** }

所以,我们需要计算(w = 1,2,…)的最大值。w, **i = 1** ),(w = 1,2…w, **i = 2** ),…。(w= 1,2,…w, **i = n** )得到结果**值(w,n)** 。我们需要一个二维数组来存储缓存的结果。

背包重复问题的伪代码。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/2788987ed4db33bcd8ee4a77c0320700.png)

**例子:**

我们有 4 个权重为(w1,v1)= (6,30),(w2,v2)= (3,14),(w3,v3)= (4,16),(w4,v4)= (2,9)的项目。

通过使用该策略,我们可以构建最终表:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/88aaacda030d3cf8b50719b943c40d1e.png)

我们可以看到,值[w=10,i=4]是 46。该值的计算公式如下

max{ value[(w=10) —(wi=2),i=2] + vi=9,value[w=10,i=2]}

= max{value[8,2] + 9,value[10,1] }

= max{ 46,30} = 46。

## **回溯**

在我们建立了这个表之后,它包含了所有的结果值。我们可以用它回溯来标记哪个项目被使用。像上面的例子一样,值(10,4) = 46 选自值(8,2) + 9。因此,我们可以标记第 4 项未使用。

# 放置括号的问题。

动态规划要解决的一个好问题是括号问题。

**输入:**一串数字 d1,.。。、dn 和一系列操作

op1,…,op(n 1)∈{+,,×}。

**输出:**应用这些操作的顺序

表达式的值。**D1 op1 D2 op2 op(n1)dn。**

**例如:**

如何在一个表达式 **1 + 2 — 3 x 4 — 5** 中放置括号使其值最大化?

答案是:((1+2)(3×(45)))= 6。

找出这个问题的正确重现。我们假设表达式**58+7×48+9**的最佳括号中的最后一个操作是 **x.**

所以目标是找出(58+7)**×**(48+9)的最大值

我们现在可以看到子问题,我们有两个子表达式:s1,s2。为了找到 s1 x s2 的最大值,我们将找到 max{ min(s1) x min(s2),min(s1) x max(s2),max(s1) x min(s2),max(s1) x max(s2) }。在此基础上,我们可以计算出**(58+7)×(48+9)**的最大值

min(58+7)=(5(8+7))= 10

max(58+7)=((58)+7)= 4

min(48+9)=(4(8+9))= 13

max(48+9)=((48)+9)= 5

显然,**max((58+7)×(48+9))= 130**

通常,为了找到子表达式 E(i,j)的最大值,我们遵循递归:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/233516c53e80e78fcd80247cd1b40f4f.png)

M(i,j)是 E(i,j)的最大值,m(i,j)是 E(i,j)的最小值。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/3f748f75859897538c4d37bd79576e6a.png)

这意味着为了计算 M(i,j ),我们需要首先计算并缓存结果 M(i,k)和 M(k+1,j)。为此,我们将按照(j — i)递增的顺序计算所有子问题。换句话说,我们需要先计算(j-i)= 1,然后(j-I)= 2……例如,为了计算**5 8 + 7×4 8+9,**我们需要先计算(**5 8),(8+7),(7 x 4),(4 8),(8 + 9)** 子问题,然后计算 **(5 — 8 +7),(8+7 x 4),(7 x 4–8),(4–8+9)**等等……

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/50b3cde055689202dfb71d67c8cd4554.png)

基于该策略,我们可以构建表达式表:**58+7×48+9**

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/be9caf30da7de302e7ec12ecdaa8317a.png)

理解每个问题的动态编程需要时间,但是一旦你理解了它并找到了正确的递归,代码就很容易实现了。让我们通过练习一些问题来全面理解动态编程。

# 问题 1:原始计算器

给你一个可以用当前数 x 进行以下三种运算的原始计算器:x 乘以 2,x 乘以 3,或者 x 加 1 .给你的目标是一个正整数 n,求从数 1 开始得到数 n 所需的最小运算次数。

**问题描述**

**任务。**给定一个整数 n,从数字 1 开始计算得到数字 n 所需的最少运算次数。

**输出格式。**在第一行,输出从 1 得到 n 所需的最小运算次数 k。在第二行输出一系列中间数字。也就是说,第二行应该包含正整数 a0,a2,…,a(k-1)使得 a0 =1,a(k-1) =n 并且对于所有 0≤i < k-1,ai + 1 等于 ai+1,2 x ai,或者 3 x ai。如果有许多这样的序列,输出其中的任何一个。

**样品 1。**

**输入:** 5

**输出:**

3

1 2 4 5

**解释:**
这里我们先把 1 乘以 2 两次,然后再加 1((1 x 2)x 2)+1)。还有一种可能是先乘以 3,再加两次 1。因此,在这种情况下,“1 3 4 5”也是有效的输出。

**样品 2:**

**输入:** 96234

**输出:**

14

1 3 9 10 11 22 66 198 594 1782 5346 16038 16039 32078 96234

**解释:**
同样,这种情况下的另一个有效输出是“1 3 9 10 11 33 99 297 891 2673 8019 16038 16039 48117 96234”。

你的目标是为这个问题设计并实现一个动态编程解决方案。在这种情况下,一个自然的子问题如下:C(n)是从 1 得到 n 所需的最小运算数(使用三个基本运算)。如何通过 C(n/3),C(n/2),C(n-1)表示 C(n)?

## **解决方案:**

首先要做的是找出这个问题的重现。很容易看出对于 n = 0,2,3。我们只需要一次手术。n = 1 成本 0 操作。

对于 n = 4,我们可以表示为 4 = 4/2 * 2 或 4 =(4–1)+1。

即 C(4) = C(n/2) + 1 或 C(4) = C(n-1) + 1。因为 4 不能被 3 除,所以我们忽略 C(n/3)。

通过这个观察,我们可以推广这个问题的递归性:

C(n) = min{ **C(n/3) + 1 如果 n ⋮ 3** , **C(n/2) + 1 如果 n ⋮ 2** , **C(n-1) + 1** }

我们有递归,我们可以很容易地实现它:

在我们计算出运算的最小值后,我们需要**回溯**来给出数列。正如我们之前所说的,在我们计算了最少的操作之后,我们得到了缓存的结果: **result** 。它包含了我们从 0,1,2,… n 需要达到的所有操作数。

例如,对于计算编号 n,我们有一个操作数组:

结果= [0,0,1,1,2, **3** ,2,3,3,2, **3**

如您所见,n = 10 => result[10] = 3。n = 5 = >结果[5] = 3。

为了计算结果[10],我们将基于结果[10/2] = 3 和结果[10–1]= 2。我们为前一个数字选择较小的运算。所以我们会选择 10 -1 = 9 为前面的数 10 — ( 9 -> 10)用-1 运算。

小心能除以 2 和 3 的数。因此,我们将选择除以 3,以获得更小数量的下一个 n = n/3

# 问题 2:尽可能多拿黄金

这个问题是关于实现无重复背包问题的算法。

**问题描述**

**任务。在这个问题中,给你一套金条,你的目标是将尽可能多的金子放进你的包里。每个条形只有一个副本,对于每个条形,你可以选择接受或不接受(因此你不能接受条形的一部分)。**

**输入格式。输入的第一行包含背包的容量 W 和金条的数量 n。下一行包含 n 个整数 w0,w1,.。。w(n-1)定义金条的重量。**

**输出格式。输出一个容量为 W 的背包所能装下的最大重量的黄金。**

**样品 1。**

**输入:**

10 3

1 4 8

**输出:**

9

**解释:**
这里我们有 W = 10,3 根金条:1,4,8。第一个和最后一个条形的权重之和等于 9。

要解决这个问题,只需仔细实现讲座中涉及的相应算法即可。

## 解决方案:

我们只是实现了背包,没有重复以上。但是我们需要澄清:金条的价值和金条的重量是一样的。

# 问题 3:计算两个字符串之间的编辑距离

两个字符串之间的编辑距离是两个字符串对齐中插入、删除和不匹配的最小数量。

**问题描述**

**任务。这个问题的目标是实现计算两个字符串之间编辑距离的算法。**

**输入格式。**两行输入的每一行都包含一个由小写拉丁字母组成的字符串。

**输出格式。**输出给定的两个字符串之间的编辑距离。

**样品 1。**

**输入**:

短裤

港口

**输出**:

3

说明:
总成本 3 的对齐:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/462f18862378ebffe281c1fb94a0655f.png)

要解决这个问题,只需仔细实现讲座中涉及的相应算法即可。

**解决方案:**

# 问题 4:最大化算术表达式的值

在这个问题中,你的目标是给一个给定的算术表达式加上括号,使其值最大化。

**问题描述**

**任务**。通过使用附加括号指定算术运算的应用顺序,找到算术表达式的最大值。

**输入格式**。输入的唯一一行包含长度为 2n + 1 的字符串 s,对于某些 n,符号为 s0,s1,.。。,s2n。s 的偶数位置上的每个符号是一个数字(即从 0 到 9 的整数),而奇数位置上的每个符号是{+、-、*}的三种运算之一。

**输出格式。**在应用算术运算的不同顺序中,输出给定算术表达式的最大可能值。

**样品 2。**

**输入:**

5–8+7*4–8+9

**输出:**

200

**解释:**

200 = (5 ((8 + 7) x (4 (8 + 9))))

要解决这个问题,只需仔细实现讲座中涉及的相应算法即可。

## **解决方案:**

# **高级问题 5:三个序列的最长公共子序列**

在这个问题中,你的目标是计算三个序列的最长公共子序列的长度。

**问题描述**

**任务。**给定三个序列 A = (a1,a2,…,an),B = (b1,b2,…,bm),C = (c1,c2,…,cl),求它们的最长公共子序列的长度。

**输入格式。**第一行:n .第二行:a1,a2,.。。,安。第三行:m .第四行:b1,b2,.。。,bm。第五行:l .第六行:c1,c2,…,cl。

**输出。**它们最长公共子序列的长度。

**样品 1。**

**输入:**

3

1 2 3

3

2 1 3

3

1 3 5

**输出:** 2

**解释:** 长度为 2 的一个常见子序列是(1,3)。

首先设计一个寻找两个(而不是三个)序列的最长公共子序列的算法可能更容易。为此,请复习课堂上计算编辑距离的算法。

## **解决方案:**

这个问题最难的工作是找出复发。在每一步,我们如何从 **D(x-1,y,z)** 、 **D(x,y-1,z)** 和 **D(x,y,z-1)之间找到 **D(x,y,z)** 的关系。**

# 最后的话:

这是**课程 1 —算法工具箱**系列的最后一篇文章。我们有 3 种解决问题的策略:贪婪,分而治之,动态规划。我们确实讨论了 Big-O,并解决了许多关于非常大的数据集和边的问题。通过小测试很容易,但是快速、有效地通过测试却一点也不容易。

感谢您抽出时间阅读。如果你觉得不错,你可以点击♡把我的文章推荐给你的朋友。

**如果你喜欢这篇文章,你可能会喜欢:**

[算法工具箱—第 1 部分:简介](https://medium.com/towards-data-science/course-1-algorithmic-toolbox-part-1-introduction-c29b8175430f)

[算法工具箱—第 2 部分:大 O 和贪婪](https://medium.com/towards-data-science/course-1-algorithmic-toolbox-part-2-big-o-and-greedy-6265d9065f05)

[算法工具箱—第三部分:分而治之](https://medium.com/towards-data-science/course-1-algorithmic-toolbox-part-3-divide-and-conquer-dd9022bfa2c0)

**资源:**

[1]:编辑距离:[DPV08]的第 6.3 节

变更问题:[CP]的“动态编程简介:变更问题”一节

背包:[DPV08]第 6.4 节

[麻省理工学院动态编程讲座。](https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/lecture-21-dp-iii-parenthesization-edit-distance-knapsack/#vid_related)

**进阶阅读:**【CP】第五章“我们如何比较生物序列”

**进阶阅读:** [高级动态编程讲义](http://jeffe.cs.illinois.edu/teaching/algorithms/notes/06-sparsedynprog.pdf)杰夫·埃里克森著

**参考文献:**

Sanjoy Dasgupta、Christos Papadimitriou 和 Umesh Vazirani。算法(第一版)。麦格劳-希尔高等教育。2008.

托马斯·h·科尔曼,查尔斯·e·莱瑟森,罗纳德·L·李维斯特,克利福德·斯坦。算法导论(第三版)。麻省理工学院出版社和麦格劳-希尔。2009.

菲利普·孔波夫,帕维尔·佩夫兹纳。生物信息学算法:一种主动学习方法。主动学习出版商。2014.

# 课程 2 —数据结构—第 1 部分:基本数据结构

> 原文:<https://towardsdatascience.com/course-2-data-structure-part-1-the-basic-data-structures-4f4b5bd380c1?source=collection_archive---------1----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9124a772ad68e0e4235f080ff969b91c.png)

algorithms + data structures version 2017

一句名言:**程序=算法+数据结构**。[在上一个系列](https://medium.com/towards-data-science/course-1-algorithmic-toolbox-part-1-introduction-c29b8175430f)中,我们讲了 3 种算法[贪婪](https://medium.com/towards-data-science/course-1-algorithmic-toolbox-part-2-big-o-and-greedy-6265d9065f05)、[分而治之](https://medium.com/towards-data-science/course-1-algorithmic-toolbox-part-3-divide-and-conquer-dd9022bfa2c0)、[动态规划](https://medium.com/towards-data-science/course-1-algorithmic-toolbox-part-4-dynamic-programming-223ffc01984a)来接近一个问题。在下一个系列中,我们将看看数据结构,以帮助我们解决更复杂的问题。让我们从最基本的数据结构开始:数组、链表。看看队列、栈是如何建立在这些数据结构之上的。什么是树?如何不用递归计算树的高度?

# **数组**

数组是由相同大小的元素组成的连续内存区域,这些元素由连续的整数索引。

在内存中,我们只是将指针地址保存到数组中,因为所有元素在内存中是大小相等且连续的,所以要得到第 I 个元素的地址,我们只需计算:**pointer _ address+element _ size * I .**

基于此定义,我们可以很容易地看到阵列上的操作成本:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/336ee2716851315674573126f503a8f4.png)

# 单链表

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/fdddbed3af62bf718c4ed34bc5898f21.png)

单链表有许多节点,每个节点包含键(数据)和指向下一个元素的指针。第一个节点叫头,最后一个节点叫尾。基于这个定义,我们可以很容易地实现单链表:

def push_front(key):
node <- new node
node.key <- key
node.next = head
head <- node
if tail == nil:
tail <- headdef pop_front():
if head == nil:
ERROR: empty list
head <- head.next
if head = nil:
tail <- nildef push_back(key):
node <- new node
node.key <- key
node.next = nil
if tail = nil:
head <- tail <- node
else:
tail.next <- node
tail <- nodedef pop_back():
if head == nil: ERROR: empty list
if head == tail:
head <- tail <- nil
else:
p <- head
while p.next.next != nil:
p <- p.next
p.next <- nil; tail <- pdef add_after(node, key):
node2 <- new node
node2.key <- key
node2.next = node.next
node.next = node2
if tail == node:
tail <- node2


# 双向链表

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/d27d3a8f4bbeb2b6703b914114a6fb9a.png)

在单链表中,我们只有一个节点指向下一个元素,像 PopBack 和 AddBefore 这样的操作需要 O(n)来运行。有了双向链表,我们可以用 O(1)来运行这些操作。我们有两个节点指向下一个元素和前一个元素。

def push_back(key):
node <- new node
node.key <- key; node.next = nil
if tail = nil:
head <- tail <- node
node.prev <- nil
else:
tail.next <- node
node.prev <- tail
tail <- nodedef pop_back():
if head = nil: ERROR: empty list
if head = tail:
head <- tail <- nil
else:
tail <- tail.prev
tail.next <- nildef add_after(node, key):
node2 <- new node
node2.key <- key
node2.next <- node.next
node2.prev <- node
node.next < node2
if node2.next != nil:
node2.next.prev <- node2
if tail = node:
tail <- node2def add_before(node, key):
node2 <- new node
node2.key <- key
node2.next <- node
node2.prev <- node.prev
node.prev <- node2
if node2.prev != nill:
node2.prev.next <- node2
if head = node:
head <- node2


# 堆

堆栈是具有以下操作的抽象数据类型。

Push(键):将键添加到集合中。

Top():返回最近添加的**键。**

Pop():删除并返回最近添加的**键**

Empty():有没有元素?

堆栈,也称为 LIFO 队列,可以用数组或链表来实现,对于 Push、Pop、Top、Empty 操作有 O(1)。

## 平衡支架问题:

比方说,我们想要一个文本编辑器的检查器。我们要检查文本输入是平衡的括号如下:

平衡的:

*   ([]) [] ()
*   ((([([])]))())

不平衡:

*   ([]]()
*   ][

有了堆栈数据结构,我们可以很容易地解决这个问题:

def is_balanced(str):
Stack stack
for char in str:
if char in [ '(', '[' ]:
stack.push(char)
else:
if stack.empty(): return False
top <- stack.pop()
if (top = '[' and char != ']') or
(top = '(' and char != ')'):
return False
return stack.empty()


# 队列:

队列是具有以下操作的抽象数据类型:

Enqueue(key):将关键字添加到集合中。

Dequeue():移除并返回**最近最少添加的**键。

Empty():有没有元素?

队列,也称为 FIFO(先进先出),可以用链表(带尾指针)或数组来实现,并有 O(1)用于入队、出队和空操作。

看这些可视化:[基于数组的栈](http://www.cs.usfca.edu/~galles/visualization/StackArray.html),[基于列表的栈](http://www.cs.usfca.edu/~galles/visualization/StackLL.html),[基于数组的队列](http://www.cs.usfca.edu/~galles/visualization/QueueArray.html),[基于列表的队列](http://www.cs.usfca.edu/~galles/visualization/QueueLL.html)。

# 树

树有一个根节点和许多其他节点,每个节点包含:键、子节点、父节点(可选)。对于二叉树,一个节点包含:key,left,right,parent(可选)。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/dfbbf4bdd3e66d69902e62213bc0b1c3.png)

这是一个二叉树的例子,我们有 Les 作为根和许多其他节点,每个节点有左和右像:Cathy(左),Sam(右)为根节点。

要计算树的高度,我们可以使用递归:

def height(tree):
if tree = nil:
return 0
return 1 + Max(height(tree.left), height(tree.right))


我们有两种方法来按特定顺序访问树的节点:

*   **深度优先:**在探索兄弟子树之前,我们完全遍历子树。

def post_order_traversal(tree):
if tree = nil:
return
post_order_traversal(tree.left)
post_order_traversal(tree.right)
print(tree.key)


*   **广度优先:**在前进到下一层之前,我们遍历一层的所有节点。

def level_traversal(tree):
if tree = nil: return
Queue q
q.Enqueue(tree)
while not q.empty():
node <- q.dequeue()
print(node)
if node.left != nil:
q.enqueue(node.left)
if node.left != nil:
q.enqueue(node.right)


# 问题 1:检查代码中的括号

在这个问题中,您将为文本编辑器实现一个功能,以查找代码中括号用法中的错误。

**问题描述**

**任务。你的朋友正在为程序员制作一个文本编辑器。他目前正在开发一个功能,可以发现不同类型括号使用中的错误。代码可以包含集合[]{}()中的任何中括号,其中左中括号是[、{、和(对应的右中括号是]、}、和)。**

为了方便起见,文本编辑器不仅应该通知用户括号的使用有错误,还应该指出有问题的括号在代码中的确切位置。首要任务是找到第一个不匹配的右括号,它要么前面没有左括号,如] in ](),要么结束了错误的左括号,如} in ()[}。如果没有这样的错误,那么它应该会找到第一个不匹配的左括号,后面没有对应的右括号,比如{}([]。如果没有错误,文本编辑器应该通知用户括号的用法是正确的。

除了括号,代码还可以包含大小拉丁字母、数字和标点符号。更正式的说法是,代码中的所有括号都应该分成匹配的括号对,这样每对括号中的开始括号都在结束括号之前,对于任何两对括号,要么其中一个嵌套在另一个中,如(foo[bar]),要么它们是分开的,如 f(a,b)-g[c]。中括号[对应于中括号],{对应于},和(对应于)。

**输入格式。**输入包含一个字符串𝑆,它由集合[]{}()中的大小拉丁字母、数字、标点符号和括号组成。

**输出格式。**如果𝑆的代码正确使用了括号,则输出“Success”(不带引号)。否则,输出第一个不匹配的右括号的从 1 开始的索引,如果没有不匹配的右括号,则输出第一个不匹配的左括号的从 1 开始的索引。

**样品 1。**

**输入:**

{}[]


**输出:**

Success


**解释:**括号使用正确:有两对括号——第一对{ and },第二对[and]——并且这两对不相交。

**样品 2** 。

**输入:**

{


**输出:**

1


**解释:**代码{没有正确使用括号,因为括号不能成对(只有一个括号)。没有右括号,第一个不匹配的左括号是{,它的位置是 1,所以我们输出 1。

**样品 3。**

**输入:**

{[}


**输出:**

3


**解释:**括号}不匹配,因为它前面最后一个不匹配的左括号是【and not】。它是第一个不匹配的右括号,我们的首要任务是输出第一个不匹配的右括号,它的位置是 3,所以我们输出 3。

**样品 3。**

**输入:**

foo(bar[i);


**输出:**

10


**解释:**)不匹配[,所以)是第一个不匹配的右括号,所以我们输出它的位置,是 10。

要解决这个问题,您可以稍微修改讲座中的 **IsBalanced** 算法,不仅考虑括号,还考虑代码中的其他字符,并且不仅返回代码是否正确使用括号,还返回代码第一个损坏的位置。

## **解决方案:**

在讲座中,IsBalanced 伪代码为我们提供了括号的解决方案:“()[]”。我们只需要检查“{}”并跟踪我们推入堆栈的每个括号的位置。

# **问题 2:计算树高**

树用于操作分层数据,如零售商的类别层次结构或计算机上的目录结构。它们还用于数据分析和机器学习,用于分层聚类和构建复杂的预测模型,包括一些实践中表现最好的算法,如决策树和随机森林上的梯度提升。在本课程后面的模块中,我们将介绍平衡二分搜索法树(BST),这是一种特殊的树,可以非常高效地存储、操作和检索数据。因此,平衡 BST 在数据库中用于高效存储,并且实际上在几乎任何重要的程序中使用,通常是通过手边编程语言的内置数据结构。在这个问题中,你的目标是适应树木。您需要从输入中读取树的描述,实现树数据结构,存储树并计算其高度。

**问题描述**

**任务。**给你一个有根树的描述。你的任务是计算并输出它的高度。回想一下,(有根的)树的高度是一个节点的最大深度,或者从一片叶子到根的最大距离。给你一棵任意的树,不一定是二叉树。

**输入格式。**第一行包含𝑛.的节点数第二行包含从 1 到𝑛1 的𝑛整数——节点的父节点。如果𝑖-th 其中之一(0≤𝑖≤𝑛1)为 1,则节点𝑖是根,否则它是𝑖-th 节点的父节点的从 0 开始的索引。保证正好有一个根。保证输入代表一棵树。

**输出格式。**输出树的高度。

**样品 1。**

**输入:**

5 4 -1 4 1 1


**输出:**

3


**解释:**输入表示有 5 个节点,编号从 0 到 4,节点 0 是节点 4 的子节点,节点 1 是根节点,节点 2 是节点 4 的子节点,节点 3 是节点 1 的子节点,节点 4 是节点 1 的子节点。为了看到这一点,让我们在一行中写下从 0 到 4 的节点数,并将输入中给出的数字写在下面的第二行中:

0 1 2 3 44 -1 4 1 1


现在我们可以看到,节点号 1 是根,因为 1 对应于第二行中的根。此外,我们知道节点 3 和 4 是根节点 1 的子节点。此外,我们知道节点 0 和 2 是节点 4 的子节点。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/1f46dfd8411aff3c588433eaa5ef67b6.png)

这棵树的高度是 3,因为从根 1 到叶 2 的路径上的顶点数是 3。

**样品 2。**

**输入:**

5 -1 0 4 0 3


**输出:**

4


**解释:**

输入意味着有 5 个节点,编号从 0 到 4,节点 0 是根,节点 1 是节点 0 的子节点,节点 2 是节点 4 的子节点,节点 3 是节点 0 的子节点,节点 4 是节点 3 的子节点。这棵树的高度是 4,因为从根 0 到叶 2 的路径上的节点数是 4。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/89d67b57d5fc576daf97e0c802407241.png)

为了解决这个问题,用一个适用于任意树的实现来改变课堂上描述的高度函数。请注意,在这个问题中,树可能非常深,所以如果您使用递归,应该小心避免堆栈溢出问题,并且一定要在具有最大可能高度的树上测试您的解决方案。

建议:利用每个树节点的标签都是 0 范围内的整数这一事实..𝑛−1:你可以将每个节点存储在一个数组中,数组的索引是节点的标签。通过将节点存储在一个数组中,您可以对给定标签的任何节点进行𝑂(1 访问。创建一个𝑛节点数组:

allocate 𝑛𝑜𝑑𝑒𝑠[𝑛]
for 𝑖 ← 0 to 𝑛 − 1:
𝑛𝑜𝑑𝑒𝑠[𝑖] =new 𝑁𝑜𝑑𝑒


然后,读取每个父索引:

for 𝑐ℎ𝑖𝑙𝑑_𝑖𝑛𝑑𝑒𝑥 ← 0 to 𝑛 − 1:
read 𝑝𝑎𝑟𝑒𝑛𝑡_𝑖𝑛𝑑𝑒𝑥
if 𝑝𝑎𝑟𝑒𝑛𝑡_𝑖𝑛𝑑𝑒𝑥 == −1:
𝑟𝑜𝑜𝑡 ← 𝑐ℎ𝑖𝑙𝑑_𝑖𝑛𝑑𝑒𝑥
else:
𝑛𝑜𝑑𝑒𝑠[𝑝𝑎𝑟𝑒𝑛𝑡_𝑖𝑛𝑑𝑒𝑥].𝑎𝑑𝑑𝐶ℎ𝑖𝑙𝑑(𝑛𝑜𝑑𝑒𝑠[𝑐ℎ𝑖𝑙𝑑_𝑖𝑛𝑑𝑒𝑥])


一旦你做好了树,你就需要计算它的高度。如果不使用递归,就不用担心堆栈溢出问题。如果没有递归,你将需要一些辅助数据结构来跟踪当前状态(例如,在讲座的广度优先搜索代码中,我们使用了队列)。

## 解决方案:

在我们构建了树之后,我们可以使用 queue 来计算树的高度。为了做到这一点,我们将使用广度优先的旅行,对于每一个水平,我们增加高度 1 多。

# 进阶问题 3:网络封包处理模拟

在这个问题中,你将实现一个程序来模拟网络数据包的处理

**问题描述**

**任务。给你一系列的网络数据包,你的任务是模拟它们的处理过程。数据包以某种顺序到达。对于每个𝑖数据包,你知道它到达𝐴𝑖的时间和处理器处理它𝑃𝑖的时间(都以毫秒计)。只有一个处理器,它按照数据包到达的顺序处理传入的数据包。如果处理器开始处理某个数据包,它不会中断或停止,直到处理完这个数据包,处理数据包𝑖正好需要𝑃𝑖毫秒。**

处理数据包的计算机有一个固定大小的𝑆.网络缓冲区当包到达时,它们在被处理之前被存储在缓冲器中。然而,如果当数据包到达时缓冲区已满(在此数据包之前已有𝑆数据包到达,而计算机尚未处理完其中的任何一个),它将被丢弃,根本不会被处理。如果几个数据包同时到达,它们首先都被存储在缓冲区中(其中一些可能会因此被丢弃,这些将在后面的输入中描述)。计算机按照信息包到达的顺序处理信息包,一旦处理完前一个信息包,就开始处理缓冲区中的下一个可用信息包。如果在某个时刻计算机不忙,并且缓冲区中没有数据包,计算机就等待下一个数据包到达。请注意,计算机一处理完数据包,它就会离开缓冲区并释放缓冲区中的空间。

**输入格式。**输入的第一行包含缓冲区的大小𝑆和传入网络数据包的数量𝑛。接下来的每一条𝑛线都包含两个数字。𝑖-th 线包含𝑖-th 数据包的到达时间𝐴𝑖和处理时间𝑃𝑖(均以毫秒计)。可以保证到达时间序列是不递减的(但是,它可以包含以毫秒为单位的完全相同的到达时间,在这种情况下,输入中较早的分组被认为较早到达)。

**输出格式。**对于每个数据包,输出处理器开始处理它的时刻(以毫秒为单位),如果数据包被丢弃,则输出 1(按照数据包在输入中给出的顺序输出数据包的答案)。

**样品 1。**

**输入:**

1 1 0 0


**输出:**

0


**解释:**唯一的数据包在时间 0 到达,计算机立即开始处理。

**样品 3。**

**输入:**

1 2 0 1 0 1


**输出:**

0 -1


**解释:**第一个数据包在时间 0 到达,第二个数据包也在时间 0 到达,但是被丢弃,因为网络缓冲区的大小为 1,并且它已经被第一个数据包填满。第一个数据包在时间 0 开始处理,第二个数据包根本没有处理。

**样品 4。**

**输入:**

1 2 0 1 1 1


**输出:**

0 1


**解释:**第一个包在时间 0 到达,计算机立即开始处理,在时间 1 结束。第二个数据包在时间 1 到达,计算机立即开始处理它。

要解决这个问题,您可以使用列表或队列(在这种情况下,队列应该允许访问它的最后一个元素,这样的队列通常称为 deque)。您可以在自己选择的语言中使用相应的内置数据结构。

一种可能的解决方案是在列表或队列 **finish_time** 中以递增的顺序存储计算机将完成处理当前存储在网络缓冲区中的分组的时间。当一个新的包到达时,你首先需要从 **finish_time** 的前面弹出所有在新的包到达时已经被处理的包。然后,您尝试在**完成时间**中添加新数据包的完成时间。如果缓冲区已满(在**完成时间**中已经有𝑆完成时间),则数据包被丢弃。否则,其加工完成时间加到**完成时间**上。

如果新数据包到达时**完成时间**为空,计算机将在新数据包到达时立即开始处理。否则,计算机将在处理完当前在 **finish_time** 中的最后一个数据包后立即开始处理新的数据包(此时您需要访问 **finish_time** 的最后一个元素,以确定计算机何时开始处理新的数据包)。您还需要通过将𝑃𝑖加到加工开始时间来计算加工完成时间,并将其推到**完成时间**的后面。

您需要记住输出每个数据包的处理开始时间,而不是存储在 **finish_time** 中的处理结束时间。

## 解决方案:

要解决这个问题,我们需要注意几点:

*   我们只需要记录完成时间。
*   当前处理时间是每个请求进入的时间。每次请求到达时,我们应该删除所有在**完成时间**队列中已经完成的请求。
*   当**完成时间**为空时,总是排队新请求。

## 阅读:

参见第 10.2 章[CLRS] —数组,链表

参见第 10.1 章[clr]—堆栈和队列

参见第 10.4 章[CLRS] —树

## 参考资料:

托马斯·h·科尔曼,查尔斯·e·莱瑟森,罗纳德·L·李维斯特,克利福德·斯坦。算法导论(第三版)。麻省理工学院出版社和麦格劳-希尔。2009.

# 课程 2 —数据结构—第 2 部分:优先级队列和不相交集

> 原文:<https://towardsdatascience.com/course-2-data-structure-part-2-priority-queues-and-disjoint-set-ed11a0383011?source=collection_archive---------0----------------------->

如果我们想知道后台工作是如何工作的,找到一个数组中 k 个最小元素的最快方法,数据库中的合并表是如何在后台工作的,请继续阅读。因为在本文中,我们将讨论优先级队列和不相交集。两种数据结构都很漂亮的解决了这些问题。最终,我们会解决以上这些问题。快乐阅读!

# 优先级队列

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/5846fde514d42ffa07d89b41c65f9948.png)

优先级队列是这样一种队列,其中每个元素被分配一个优先级,并且元素按优先级顺序出现。优先级队列的典型用例是调度作业。每个工作都有一个优先级,我们按照优先级递减的顺序处理工作。同时正在处理当前作业,新作业可能会到达。

优先级队列有一些主要操作:

*   **Insert(p)** :添加一个优先级为 p 的新元素。
*   **ExtractMax()** :提取优先级最高的元素。
*   **ChangePriority(it,p)** :将 **it** 指向的元素的优先级改为 **p** 。

优先级队列用于许多算法中:

*   Dijkstra 算法:在图中寻找最短路径。
*   Prim 的算法:构造一个图的最小生成树。
*   霍夫曼算法:构造一个字符串的最佳无前缀编码。
*   堆排序:对给定的序列进行排序。

您可以使用未排序/已排序的数组或列表来实现优先级,但是每一种都有一个权衡:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/98c683aed71c91dae000a67563724846.png)

但是通过使用二进制堆,我们可以用 O(logn)做 **Insert** ,用 O(logn)做 **ExtractMax** 。

# 二元堆

二进制堆有两种类型:二进制最小堆和二进制最大堆。在本文中,我们将讨论二进制最大堆。另一方面,二进制最小堆也有相同的实现方式。

二进制最大堆是一个二叉树(每个节点有零个、一个或两个孩子),其中每个节点的值至少是其孩子的值。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/235582bf46cf31a2b6511aaf9e9a1d54.png)

如您所见,每个子节点的值都不大于父节点的值。根节点具有最大值。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/4c2ee2fa5116e5fe3224c5db5c0dd03d.png)

Not a max-heap. Some children are bigger than parent.

Max-Heap 有一些基本操作:

*   **GetMax** :返回根值即可。成本 O(1)。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/135d9429ace6aeeb8f06cf4c259fb0a6.png)

*   **插入**:我们将把一个新节点附加到任何一个叶子上。如果它违反了堆属性,我们将冒泡(称为 **SiftUp** )新节点,直到满足堆属性。o(树高)

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/8f9810344b2b2002fd7ee57354098b03.png)

如您所见,我们将新节点 32 连接到节点 7,并将其冒泡到节点 29。

*   **ExtractMax** :根节点 R 是 max-heap 的最大值。要弹出根节点,我们只要把根节点 R 换成任意一个叶节点 A,去掉节点 R 就可以了,可能会违背 heap 的属性。我们将做 **SiftDown** 操作。作为父节点 A,我们要 **SiftDown** 到子节点 B 和 C,我们会选择子节点 B 和 C 的最大值,交换到节点 A,我们 SiftDown 直到满足堆的属性。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/df53021159a927f64d1c5c5ac1045e29.png)

我们首先交换根节点 42 和随机叶节点 12,并移除节点 42。现在违反了最大堆,我们需要将节点 12 向下筛选为子节点。在最后一步,节点 29 是根节点。

*   改变优先级:我们需要改变一个节点的优先级。根据新优先级的值,我们将使用 SiftDown 或 SiftUp 来限定堆属性。
*   **移除**:移除节点 a。我们分两步完成:

**步骤 1:** 使节点 A 值变得最大(无穷大)。我们将执行 **SiftUp** 操作,使节点 A 成为根节点。

**第二步:**我们执行上面描述的 **ExtractMax** 操作。

# 建筑堆

我们知道堆是如何工作的,我们需要找到建立一个堆的方法。为了构建一个堆并保持一个二叉堆树浅滩,我们必须在最后一层从左到右填充新的节点。叫做**完全二叉树**。形式上:

如果一棵二叉树的所有层次都被填满,除了可能是从左到右填满的最后一层,那么它就是完整的。所以一棵有 n 个节点的完全二叉树的高度最多是 O(logn)。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/87b969c824bc330d5ec3c4e03e4396db.png)

一个完整的二叉树有从左到右填充的元素,所以我们可以将树存储为一个数组,其中

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/ee9a4d29da292a1a8e9bc1134ac00c72.png)

因为我们以数组的形式存储,所以我们执行的每个操作都需要保持树的完整性。

## 最大堆的伪代码

we store elements in array Hdef parent(i):

return i/2def left_child(i):
return 2idef right_child(i):
return 2i + 1def sift_up(i):
while i > 1 and H[parent(i)] < H[i]:
swap H[parent(i)] and H[i]
i <- parent(i)def sift_down(i):
maxIndex <- i
left <- left_child(i)
if left <= size and H[left] > H[maxIndex]:
maxIndex <- left
right <- right_child(i)
if right <= size and H[right] > H[maxIndex]:
maxIndex <- right
if i != maxIndex:
swap H[i] and H[maxIndex]
SiftDown(maxIndex)def insert(p):
if size = maxSize:
return ERROR
size <- size + 1
H[size] <- p
SiftUp(size)def extract_max():
result <- H[1]
H[1] <- H[size]
size <- size - 1
SiftDown(1)
return resultdef remove(i):
H[i] = ∞
sift_up(i)
extract_max()def change_priority(i, p):
oldp <- H[i]
H[i] <- p
if p > oldp:
sift_up(i)
else:
sift_down(i)


# 堆排序

回到排序问题,我们观察到最大堆的根节点是最大值。如果我们做 **ExtractMax()** 并放入另一个数组,结果将是排序递减数组。这是选择排序的基础。它的价格为 O(nlogn)。

def heap_sort_selection_sort(A[1...n]):
create an empty priority queue
for i from 1 to n:
Insert(A[i]) # Insert operation to build max-heap.
for i from n downto 1:
A[i] <- ExtractMax()


事实上,我们可以做更好的堆排序算法,不使用任何额外的数组,它是就地堆排序算法。给定一个数组

A = [ 4, 1, 3, 2, 16, 9, 10, 14 , 8, 7]


我们将数组 A 表示为一个堆:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/a7dcec4f73d7d67d9eee579285b115bc.png)

现在我们需要修复所有节点以满足 max-heap 的属性。我们可以开始修复深度为 1 的所有子树中的节点(深度为 0 的是所有的叶子)。深度为 1 的节点从 n/2 到 1

def build_heap(A[1...n]):
size <- n
for i from n/2 downto 1:
SiftDown(i)


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/92cae4e409dd583cc0edaf7a47423395.png)

我们从父节点 5,4,3,2,1([16,2,3,1,4])开始。每一步,我们做 **SiftDown** 操作。最后,我们可以有一个最大堆(f)。

要对最大堆进行排序,我们只需交换节点 A[1]和 A[size],移除 A[size]和 SiftDown(1)。

def heap_sort(A[1...n]):
repeat (n-1) times:
swap A[1] and A[size]
size <- size - 1
SiftDown(1)


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/4ccf660d8935da08038f68ca539498e1.png)

# 部分排序

给定数组 A[1…n],并且 1 ≤ k ≤ n。输出 A 的排序版本的最后 k 个元素。

有了堆排序,我们很容易解决这个问题:

def partial_sorting(A[1...n], k):
build_heap(A)
for i from 1 to k:
extract_max()


运行时间为:O(n+klogn)。对于较小的数 k = O(n/logn),代价将为 O(n)。真令人印象深刻。

# 不相交集

不相交集有许多应用,其中之一是确定无向图的连通分量。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/e701f2cb8be1a55f8e145d186ad20eac.png)

不相交集合数据结构维护不相交动态集合的 S1、S2、…、Sk 的集合。我们把每个集合表示成一棵有根的树。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/20c284b360c0993c3d2e68d42650802c.png)

我们可以通过使用一个数组来存储这些集合(有根的树),该数组的值 A[i]是节点 I 的父节点,或者如果节点 I 是根节点,则它是节点 I 的父节点。

不相交集有一些主要操作:

*   MakeSet(i) :创建一个只包含一个 I 集合:{i}。

def make_set(i):
parent[i] <- i


*   **查找(i)** :在一个集合中查找 I,返回根索引

def find(i):
while i != parent[i]:
i <- parent[i]
return i


*   **联合(S2 S1)**:合并两个有根的树。这是不交集最重要的运算。

我们有两个有根的树,我们想联合这些树。通常,我们有两种方法可以做到这一点:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/3946d9b7cc6100f99f9c1b177bad3f1d.png)

好的那个是身高较矮的那个。我们如何知道为并集选择根节点?

为了回答这个问题,我们只需要使用一个等级作为高度来选择最终的根。换句话说,我们只是把矮的挂在高的根部。为了避免每次执行 union 操作时都要重新计算 rank(height ),我们需要将每个子树的高度存储在数组 **rank[1…n]** 中,其中 rank[i]是根为 I 的子树的高度。

我们改变了不相交集合运算的实现:

def make_set(i):
parent[i] <- i
rank[i] <- 0def find(i):
while i != parent[i]:
i <- parent[i]
return idef union(i, j):
i_id <- find(i)
j_id <- find(j)
if i_id == j_id:
return
if rank[i_id] > rank[j_id]:
parent[j_id] <- i_id
else:
parent[i_id] <- j_id
if rank[i_id] == rank[j_id]:
rank[j_id] <- rank[j_id] + 1


让我们把构建不相交集合的过程形象化,做并集(2,4),并集(5,2),并集(3,1),并集(2,3),并集(2,6)。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/fa4ce9f445bd5fc8d624eb715d7470fe.png)

每一步,我们需要选择较低等级的树,并将其附加到较高等级的树。当我们有两个相同等级的树时,我们只增加结果的等级。

## 通路压缩

在造树的时候,我们可以马上意识到我们的树会变得越来越高。让我们看看如何找到元素 6,我们确实遍历了所有父节点以得到根节点 5。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/52382bd039530adb21b446351a1c2516.png)

请注意,所有节点 6、12、3 都有相同的根节点 5,因此,如果我们能够将它们的所有父节点转换为直接父节点 5,就太好了,就像这样:

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/2b2f9fd5427a2910fb53a949715d7bef.png)

Path Compression

你可以看到,我们可以压缩高度,我们可以更快地找到元素。你会惊奇地发现,这种启发式的实现如此简单:

def find(i):
if i != parent[i]:
parent[i] <- find(parent[i])
return parent[i]


# 问题 1:将数组转换成堆

在这个问题中,你将把一个整数数组转换成一个堆。这是称为 HeapSort 的排序算法的关键步骤。它保证了最坏情况下𝑂(𝑛日志𝑛的运行时间,而不是快速排序的𝑂(𝑛日志𝑛).的平均运行时间快速排序通常在实践中使用,因为通常它更快,但是堆排序用于外部排序,当你需要排序不在你的计算机内存中的大文件时。

**问题描述**

**任务。**heap sort 算法的第一步是从想要排序的数组中创建一个堆。顺便说一句,你知道基于堆的算法被广泛用于外部排序吗?当你需要对不在计算机内存中的大文件进行排序时。

您的任务是实现这第一步,并将给定的整数数组转换成堆。为此,您需要对阵列应用一定数量的交换。Swap 是将数组𝑎中的元素𝑎𝑖和𝑎𝑗与一些𝑖和𝑗.进行交换的操作你需要将数组转换成一个只使用𝑂(𝑛交换的堆,正如在课堂上所描述的。请注意,在这个问题中,您需要使用最小堆而不是最大堆。

**输入格式。**输入的第一行包含单个整数𝑛.下一行包含𝑛空格分隔的整数𝑎𝑖.

**输出格式。**输出的第一行应该包含单整数𝑚——交换的总数。𝑚必须满足条件 0 ≤ 𝑚 ≤ 4𝑛.接下来的𝑚行应该包含用于将数组𝑎转换为堆的交换操作。每次交换都由一对整数𝑖,𝑗来描述,这是要交换的元素的从 0 开始的索引。在按指定顺序应用所有交换后,数组必须变成一个堆,也就是说,对于 0≤𝑖≤𝑛1 的每个𝑖,必须满足以下条件:

1. If 2𝑖+1 ≤ 𝑛−1,then 𝑎𝑖 < 𝑎(2𝑖+1).
2. If 2𝑖+2 ≤ 𝑛−1,then 𝑎𝑖 < 𝑎(2𝑖+2).


请注意,输入数组的所有元素都是不同的。请注意,任何长度不超过 4𝑛的交换序列,在此之后,您的初始数组成为正确的堆,都将被评定为正确。

**样品 1。**

**输入:**

5
5 4 3 2 1


**输出:**

3
1 4
0 1
1 3


**说明:**

After swapping elements 4 in position 1 and 1 in position 4 the array becomes 5 1 3 2 4.After swapping elements 5 in position 0 and 1 in position 1 the array becomes 1 5 3 2 4.After swapping elements 5 in position 1 and 2 in position 3 the array becomes 1 2 3 5 4, which is already a heap, because 𝑎0 =1 < 2 = 𝑎1, 𝑎0 =1 < 3=𝑎2, 𝑎1=2 < 5=𝑎3, 𝑎1=2 < 4=𝑎4.


更改讲座中的 **BuildHeap** 算法,以考虑**最小堆**而不是最大堆,并考虑基于 0 的索引。

## 解决方案:

在构建堆时,我们将从第 n/2 个节点向下到第 1 个节点进行 SiftDown 操作,以修复堆来满足最小堆属性。每次交换节点时,我们都需要跟踪它。

# 问题 2:并行处理

在这个问题中,您将模拟一个并行处理一系列作业的程序。像 Linux、MacOS 或 Windows 这样的操作系统都有特殊的程序,叫做调度程序,它对你计算机上的程序做同样的事情。

**问题描述**

**任务。**您有一个并行化的程序,它使用𝑛独立线程来处理给定的𝑚作业列表。线程按照输入中给出的顺序接受任务。如果有空闲线程,它会立即从列表中获取下一个任务。如果一个线程已经开始处理一个作业,它不会中断或停止,直到它完成处理该作业。如果几个线程试图同时从列表中获取作业,索引较小的线程会获取作业。对于每一个任务,你确切地知道任何一个线程处理这个任务需要多长时间,这个时间对于所有的线程都是一样的。您需要为每个作业确定哪个线程将处理它,以及它何时开始处理。

**输入格式。**输入的第一行包含整数𝑛和𝑚.

第二行包含𝑚整数𝑡𝑖——任何线程处理𝑖-th 作业所用的时间(秒)。时间的顺序与它们在线程获取作业的列表中的顺序相同。

线程从 0 开始进行索引。

**输出格式。**准确输出𝑚线。𝑖-th 行(使用基于 0 的索引)应该包含两个用空格分隔的整数——将处理𝑖-th 作业的线程的基于 0 的索引和它将开始处理该作业的时间(以秒为单位)。

**样品 1。**

**输入:**

2 5 1 2 3 4 5


**输出:**

0 01 00 11 20 4


**说明:**

1. The two threads try to simultaneously take jobs from the list, so thread with index 0 actually takes the first job and starts working on it at the moment 0.2. The thread with index 1 takes the second job and starts working on it also at the moment 0.3. After 1 second, thread 0 is done with the first job and takes the third job from the list, and starts processing it immediately at time 1.4. One second later, thread 1 is done with the second job and takes the fourth job from the list, and starts processing it immediately at time 2.5. Finally, after 2 more seconds, thread 0 is done with the third job and takes the fifth job from the list, and starts processing it immediately at time 4.


想想当其中一个线程空闲时的事件顺序(在开始时和完成某个任务后)。如何应用优先级队列来模拟按要求的顺序处理这些事件?记得考虑几个线程同时空闲的情况。

在这个问题中要小心 integer over ow:在 C++中使用 long long 类型,在 Java 中使用 long 类型,只要正则类型 int 能够覆盖 ow,就要考虑问题语句中的限制。

## **解决方案:**

这个想法是我们需要一个优先队列来留住工人。我们通过状态和指数比较两个工人。要选择下一个工人,我们只需选择根最小堆树。为了让它工作,我们需要改变根树和修复树的优先级。

## 问题 3:合并表格

在这个问题中,您的目标是模拟数据库中表的一系列合并操作。

**问题描述**

**任务。在某个数据库中存储有𝑛表。这些桌子从 1 到𝑛.编号所有表共享同一组列。每个表包含几行实际数据或指向另一个表的符号链接。最初,所有表都包含数据,𝑖-th 表包含𝑟𝑖行。您需要执行以下操作的𝑚:**

1.考虑𝑑𝑒𝑠𝑡𝑖𝑛𝑎𝑡𝑖𝑜𝑛(𝑖).号桌遍历符号链接的路径以获取数据。也就是说,虽然𝑑𝑒𝑠𝑡𝑖𝑛𝑎𝑡𝑖𝑜𝑛(𝑖)包含了象征性的联系而不是真实的数据 do𝑑𝑒𝑠𝑡𝑖𝑛𝑎𝑡𝑖𝑜𝑛(𝑖)←symlink(𝑑𝑒𝑠𝑡𝑖𝑛𝑎𝑡𝑖𝑜𝑛(𝑖))

2.考虑表格编号𝑠𝑜𝑢𝑟𝑐𝑒(𝑖)并从它开始以与𝑑𝑒𝑠𝑡𝑖𝑛𝑎𝑡𝑖𝑜𝑛(𝑖).相同的方式遍历符号链接的路径

3.现在,𝑑𝑒𝑠𝑡𝑖𝑛𝑎𝑡𝑖𝑜𝑛𝑖和𝑠𝑜𝑢𝑟𝑐𝑒(𝑖)是有真实数据的两个表的数字。如果𝑑𝑒𝑠𝑡𝑖𝑛𝑎𝑡𝑖𝑜𝑛(𝑖)!= 𝑠𝑜𝑢𝑟𝑐𝑒(𝑖),将𝑠𝑜𝑢𝑟𝑐𝑒(𝑖表中的所有行复制到𝑑𝑒𝑠𝑡𝑖𝑛𝑎𝑡𝑖𝑜𝑛(𝑖表中,然后清空𝑠𝑜𝑢𝑟𝑐𝑒(𝑖表,并在其中放置一个指向𝑑𝑒𝑠𝑡𝑖𝑛𝑎𝑡𝑖𝑜𝑛(𝑖的符号链接,而不是真正的数据。

4.打印所有𝑛表中的最大大小(回想一下,大小是表中的行数)。如果表格只包含一个符号链接,则其大小被认为是 0。

**请参见示例和解释以获得进一步的说明。**

**输入格式。输入的第一行包含两个整数𝑛和𝑚——分别是数据库中表的数量和要执行的合并查询的数量。**

输入的第二行包含𝑛整数𝑟𝑖——𝑖-th 表中的行数。

然后按照描述合并查询的𝑚线。每个都包含两个整数𝑑𝑒𝑠𝑡𝑖𝑛𝑎𝑡𝑖𝑜𝑛(𝑖和𝑠𝑜𝑢𝑟𝑐𝑒(𝑖)——要合并的表的编号。

**输出格式。**对于每个查询,打印一行包含单个整数的内容——相应操作后所有表的最大大小(根据行数)。

**样品 1。**

**输入:**

5 5
1 1 1 1 1
3 5
2 4
1 4
5 4
5 3


**输出:**

2
2
3
5
5


**说明:**

在这个示例中,所有的表最初都只有一行数据。考虑合并操作:

1.表 5 中的所有数据都被复制到表 3 中。表 5 现在只包含一个到表 3 的符号链接,而表 3 有 2 行。2 成为新的最大尺寸。

2.2 和 4 的合并方式与 3 和 5 相同。

3.我们正在尝试合并 1 和 4,但是 4 有一个指向 2 的符号链接,所以我们实际上是将 2 号表中的所有数据复制到 1 号表中,清除 2 号表并在其中放一个指向 1 号表的符号链接。表 1 现在有 3 行数据,3 成为新的最大大小。

4.从 4 开始遍历符号链接的路径我们有 4→2→1,从 5 开始的路径是 5→3。所以我们实际上合并了表 3 和表 1。我们将 1 号表中的所有行复制到 3 号表中,现在 3 号表有 5 行数据,这是新的最大值。

5.现在所有的表都直接或间接地指向表 3,所以所有其他的合并不会改变任何东西。

**样品 2。**

**输入:**

6 4
10 0 5 0 3 3
6 6
6 5
5 6
4 3


**输出:**

10
10
10
11


**解释:**

在这个例子中,表格具有不同的大小。让我们考虑一下操作:

1.将 6 号表与其自身合并不会改变任何东西,最大大小是 10(1 号表)。

2.在将表号 5 合并到表号 6 之后,表号 5 被清除并且大小为 0,而表号 6 的大小为 6。不过,最大尺寸是 10。

3.通过将 4 号表合并到 5 号表,我们实际上将 4 号表合并到 6 号表(现在 5 号表只包含一个到 6 号表的符号链接),所以 4 号表被清除,大小为 0,而 6 号表的大小为 6。不过,最大尺寸是 10。

4.通过将 3 号表合并到 4 号表,我们实际上将 3 号表合并到 6 号表(4 号表现在只包含一个到 6 号表的符号链接),所以 3 号表被清除,大小为 0,而 6 号表的大小为 11,这是新的最大大小。

思考如何使用带路径压缩的不相交集合并和按等级启发式的并来解决这个问题。特别是,您应该将执行 union/find 操作的数据结构从表的合并中分离出来。如果要求您将第一个表合并到第二个表中,但是第二个表的等级小于第一个表的等级,则在不相交集合联合数据结构中进行合并时,您可以忽略所请求的顺序,而是在不相交集合联合中将对应于第二个表的节点连接到对应于第一个表的节点。但是,您将需要存储实际的第二个表的编号,您被请求将第一个表合并到相应不相交集的父节点中,并且您将需要在不相交集联合的节点中附加一个 eld 来存储它。

## 解决方案:

我们使用 ***父*** 数组来存储不相交集合。

我们用 ***行*** 数组来存储根 I 的最大值

我们需要用耙集和路径压缩启发式建立不相交集。

## 阅读材料:

参见[CLRS] —优先级队列中的第 6.4 章

参见此[最小堆可视化](http://www.cs.usfca.edu/~galles/visualization/Heap.html)。

参见[DPVo8] —不相交集的第 5.1.4 节

另请参见本[教程](https://www.topcoder.com/community/data-science/data-science-tutorials/disjoint-set-data-structures/)中关于不相交集合数据结构的内容。

另请参见这个不相交集合的[可视化](http://www.cs.usfca.edu/~galles/visualization/DisjointSets.html),有和没有路径压缩和按等级试探法联合。

## **参考文献:**

Sanjoy Dasgupta、Christos Papadimitriou 和 Umesh Vazirani。算法(第一版)。麦格劳-希尔高等教育。2008.

托马斯·h·科尔曼,查尔斯·e·莱瑟森,罗纳德·L·李维斯特,克利福德·斯坦。算法导论(第三版)。麻省理工学院出版社和麦格劳-希尔。2009.

# 课程回顾:机器学习专业化的数学

> 原文:<https://towardsdatascience.com/course-review-mathematics-of-machine-learning-specialization-4c0771424b4e?source=collection_archive---------5----------------------->

机器学习的重要基础之一是数学。不懂机器学习数学的人永远不会理解各种 python/R API 底层上的概念。

当我第一次潜入机器学习的海洋时,我在 Coursera 上选择了吴恩达教授的机器学习课程。他是这个领域的优秀教师,有着多年的经验。

因为他在教机器学习,我想说从很久以前,他就已经很好地理解和学习了他的概念。但是,作为初学者和职场人士,我们是否已经很好地理解和学习了我们的概念?我会说不。大多数情况下,职场人士会忘记一些基本概念。

我在大学的时候对这些有相当的理解,但是那些知识已经消失了。不出所料,随着斯坦福 ML 课程的进展,我度过了一段艰难的时光,尤其是一些数学推导。为了让您更好地了解本课程的内容,每个子模块持续一周,但同一主题可以有自己的课程,时间跨度为 3-4 周。

于是我在第 6 周停了下来,开始上这门课:[伦敦帝国理工学院机器学习专精](https://www.coursera.org/specializations/mathematics-machine-learning)数学。

## **我们来复习一下这门课。**

该专业有三门课程,即。线性代数、多元微积分和主成分分析。这些课程的时间跨度为 4-6 周。

1.  **线性代数**
    这门课程讲的是解决机器学习问题所需的向量以及与向量相关的各种重要概念。正如老师自己所说,这门课不是要详细理解矢量,并掌握矢量。但是这门课是为了有足够的关于向量的知识,能够利用向量来解决机器学习的问题。
    讲师从什么是向量开始,到点积、点积直觉、基向量,改变基向量直到特征值和特征向量。本课程试图解决的第二部分是向量的矩阵表示、矩阵运算以及如何将基本向量的概念应用于矩阵。
    本课程有著名的谷歌页面排序算法练习,该练习展示了向量和矩阵对该领域的重要性。
2.  **多元微积分**
    顾名思义,这门课讲的是单变量微分,多变量微分,链式法则,雅可比行列式,黑森拉格朗日乘数,泰勒级数。这门课很容易理解,因为许多学生已经将这些概念作为本科学习要求的一部分。
    本课程有助于培养这些概念对高维度图形的直觉,以及如何使用它们找到最佳解决方案。
    许多学生可能一直想知道这些概念在未来生活中的应用。如果你是有抱负的数据科学家,本课程将帮助你回答这些问题。
3.  **主成分分析**
    我在理解这门课程时面临困难,但在看了 loop 上的视频和在讨论论坛上冲浪后,我扫清了所有的障碍。这门课很难,尽管与其他两门课相比内容较少。
    无论前两门课程中提到了什么概念,在这里都可以用来理解机器学习的一个重要概念:主成分分析。本课程要求你事先具备 Python 库 numPy 的知识。
    从一点点统计开始,向量之间的点和正交性,矩阵中的投影,多元微积分概念。这可能感觉像是前两门课程中的一门,但是随着课程的进展,它会变得有点困难,因为你需要解决编程作业。如果你不熟悉 numPy 和其他 python 库,你可能会在编程任务中遇到麻烦。我发现这两者之间令人困惑,但论坛讨论将会来拯救,只要你有毅力筛选它们。

# **我的拍摄:**

总的来说,课程非常棒。我感觉从机器学习开始所需要的数学概念都已经涵盖了。

我认为我很有资格欣赏课程内容,因为我知道这些概念在机器学习中的应用,而不是初学者。当我理解这门课中的一些推导/概念时,我确实有了一些顿悟。当我在斯坦福学习机器学习课程时,我对推导有一些疑问,我觉得这门课很好地澄清了这些疑问。

测验让你对所学的概念有更深入的了解,并期望你用跳出框框的思维来解决问题。

虽然有几次我不得不参考其他资源,如 youtube 等,来理解这些概念。然而,我不认为这降低了这门课程的魅力。

彻底总是好的——不同的来源提供不同的看法,并对主题提供更多的见解。如果没有别的,它们可以作为一个很好的修订。

# **结论:**

这个课程对一个刚开始学习机器数学的初学者来说非常好。我会给它 4.5/5 颗星。

我建议你学完这门课程后,也参考一下其他资料。

# Coursera 机器学习评论

> 原文:<https://towardsdatascience.com/coursera-machine-learning-review-c44b86f5a094?source=collection_archive---------4----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9c565c77e8e15400a6aac6f677219a32.png)

Aadam — Machine Learning Certificate

我一直想学完这门课,但每次都有其他吸引我的东西,比如其他 MOOC 或主题。我知道,由于受欢迎程度和众多积极的评论,这门课将会是一门好课,但每次都会有一个新的 MOOC 出现在一些我一直在等待的新主题上,我会冲向它。

但是现在,[吴恩达](https://medium.com/u/592ce2a67248?source=post_page-----c44b86f5a094--------------------------------)发布了一个关于[深度学习](https://www.coursera.org/specializations/deep-learning)的新专业,我渴望接下来开始这个专业,我有足够的动力完成这个课程,因为我认为这将被证明是一个无价的资源,也是[深度学习专业](https://www.coursera.org/specializations/deep-learning)将涵盖的主题的坚实基础。

我惊讶地发现,这门课比我想象的要有收获得多。所有的材料都经过精心挑选,并有直观的解释。吴恩达是我有幸学习过的最好的老师之一。他如此简单直观地解释困难而复杂的概念的方式是非凡的。很多以前很难理解的话题,听了安德鲁的解释后,似乎变得很容易理解,我想知道为什么我以前没有理解。

# 这是给谁的?

> 这个课程值得我浪费 11 周的时间吗?我应该期望从这门课程中得到什么?

如果你认真学习机器学习,并希望深入了解算法的内部工作,那么这门课程非常适合你。这门课程比今天提供的大多数机器学习课程要古老得多,但它在解释基本概念方面做得很好,以便更好地理解当今使用的机器学习算法。

如果你只想对机器学习如何工作有一个大致的了解,并学习一些库或工具来执行基本的机器学习任务,那么你最好使用一些其他资源,例如 Udacity 的[机器学习工程师 Nanodegree](https://www.udacity.com/degrees/machine-learning-engineer-nanodegree--nd009) 。

并不是说这门课不好或者不会让你受益,只是它的受众人群不同而已。它迎合更有技术头脑的人。它大量使用数学来解释算法。你需要对线性代数和微积分有一个基本的了解,以便对算法的内部工作有一个直觉。所有的作业都在 MATLAB 中,不像大多数新的机器学习课程使用 Python。

我强烈推荐这门课程,因为它对我非常有益。我学到了很多新东西,最重要的是,它消除了我对一些学习算法的许多困惑和误解。我知道如何使用这些算法,但我不明白它们是如何工作的。就像使用魔咒一样,我复制了别人说会起作用的东西,却不明白为什么会起作用,这让我很沮丧。这门课程让我明白了很多事情。现在,我将能够使用机器学习技术和算法,知道这是为什么和如何工作的,这将对我试验新技术和测试新想法有很大帮助。

我仍然对一些算法感到困惑,更具体地说是反向传播算法,但在我看来,根据[吴恩达](https://medium.com/u/592ce2a67248?source=post_page-----c44b86f5a094--------------------------------)的说法,它是机器学习中最复杂的算法之一。我凭直觉理解反向传播的工作方式和原因,但我对算法背后的数学原理有点困惑。我想我得再看一遍视频,一两遍。

[![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/406028406d3dc93544e4b09206ae1340.png)](https://www.deeplearning.ai/)

[https://www.deeplearning.ai/](https://www.deeplearning.ai/)

我已经在 Cousera 上开始了由[吴恩达](https://medium.com/u/592ce2a67248?source=post_page-----c44b86f5a094--------------------------------)提供的[深度学习专业](https://www.coursera.org/specializations/deep-learning)。我上了[神经网络和深度学习](https://www.coursera.org/learn/neural-networks-deep-learning)的最后一周,这是专业化的第一门课程。在接下来的帖子里,我会告诉你我在这个课程上的经历,安拉保佑。

# 人工智能的表亲

> 原文:<https://towardsdatascience.com/cousins-of-artificial-intelligence-dda4edc27b55?source=collection_archive---------2----------------------->

## 人工智能、机器学习和深度学习是全球的热门话题,其应用部署在所有主要的业务领域。这些术语让很多人感到困惑,并且经常互换使用。如果你是其中之一,那么让我们试着理解人工智能和它的兄弟姐妹的关系。

人工智能是机器学习(ML)和深度学习(DL)的更广泛的保护伞。图表显示,ML 是 AI 的子集,DL 是 ML 的子集。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/20c17b1042227c1a8428ece6861abd87.png)

Cousins of AI

**人工智能**

*“通过计算机程序对人类心理功能建模的研究。”——*[*柯林斯字典*](https://www.collinsdictionary.com/dictionary/english/artificial-intelligence)

人工智能由人工和智能两个词组成。任何非自然的和人类创造的东西都是人造的。智力意味着理解、推理、计划等能力。因此,我们可以说,任何使机器能够模仿、发展或展示人类认知或行为的代码、技术或算法都是人工智能。

人工智能的概念很古老,但最近开始流行。**但是为什么呢?**

原因是之前我们只有非常少量的数据来做出准确的预测。但是今天,每分钟产生的数据量都在急剧增加,这有助于我们做出更准确的预测。除了巨大的数据量,我们还有更先进的算法、高端计算能力和存储的支持,可以处理如此巨大的数据量。例子包括特斯拉自动驾驶汽车,苹果的 Siri 等等。

**机器学习**

我们已经看到了什么是人工智能,但是是什么问题导致了机器学习的引入?

**几个原因是:**

在统计学领域,问题是“如何有效地训练大型复杂模型?”在计算机科学和人工智能领域,问题是“如何训练更健壮的人工智能系统?

因为这些问题,机器学习被引入。

**什么是机器学习?**

"机器学习是让计算机在没有明确编程的情况下行动的科学."——[*斯坦福大学*](https://www.coursera.org/learn/machine-learning/lecture/Ujm7v/what-is-machine-learning)

它是人工智能的一个子集,使用统计方法使机器能够随着经验而改进。它使计算机能够采取行动并做出数据驱动的决策来执行某项任务。这些程序或算法的设计方式使得它们可以在接触新数据时随着时间的推移而学习和改进。

**例如:**

假设我们想创建一个系统,根据身高告诉我们人的预期体重。首先,我们将收集数据。这就是数据的样子(下图)。图上的每个点代表一个数据点。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/7fc3e9018f9c0b9196d0d2a7be2751b8.png)

首先,我们将根据身高画一条简单的线来预测体重。

一条简单的线可以是 W=H-100

在哪里

w =以千克为单位的重量

h =以厘米为单位的高度

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/f63f4ece603e5e3aeb9d0c4326c83549.png)

这条线可以帮助我们做预测。我们的主要目标是缩小估计值和实际值之间的距离。即误差。为了实现这一点,将绘制一条穿过所有点的直线。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/14b53dde84db813ec734598c8aba591e.png)

我们的主要目标是最小化误差,使它们尽可能小。减少实际值和估计值之间的误差可以提高模型的性能,而且我们收集的数据点越多,我们的模型就越好。

因此,当我们输入一个人的身高的新数据时,它可以很容易地告诉我们这个人的体重。

**深度学习**

*“深度学习是机器学习的一个子领域,涉及被称为人工神经网络的大脑结构和功能所启发的算法”。——*[*机器学习掌握*](https://machinelearningmastery.com/what-is-deep-learning/)

这是一种特殊的机器学习,它受到我们称为神经元的脑细胞的功能的启发,这导致了人工神经网络(ANN)的概念。使用人工神经元或计算单元的层来模拟 ANN,以接收输入并应用激活函数和阈值。

在简单模型中,第一层是输入层,接着是隐藏层,最后是输出层。每层包含一个或多个神经元。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/9112a5d467639c17ab6eb43b3ad7671f.png)

Layers in Artificial neural Network

**用简单的例子来理解概念层面的事情是如何发生的**

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/2d3da72f56fde276d27647ff2f317788.png)

**你如何从其他形状中识别正方形?**

我们要做的第一件事是检查图形是否有四条线。如果是,我们进一步检查是否所有线路都已连接和关闭。如果是,我们最后检查是否所有的都是垂直的,所有的边都是相等的。

如果图形满足所有条件,我们就认为它是正方形。

正如我们在示例中看到的,它只是概念的嵌套层次。所以我们接受了一个复杂的识别正方形的任务,并把它分解成几个简单的任务。深度学习也做同样的事情,但规模更大。

例如,机器执行识别动物的任务。机器的任务是识别给定图像是猫还是狗。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/674621c502ef978b1af7634b6dce27b6.png)

Differentiating between dog & cat using Deep Learning algorithm

如果我们要求我们使用概念机器学习来解决这个问题,那么我们会定义一些特征,例如检查它是否有胡须,检查尾巴是直的还是弯曲的,以及许多其他特征。我们将定义所有特征,并让我们的系统识别哪些特征在对特定动物进行分类时更重要。现在,当涉及到深度学习时,它向前迈出了一步。与我们必须手动给出特征的机器学习相比,深度学习会自动找到哪些特征对分类最重要。

所以到目前为止,我们知道人工智能是一个更大的图景,机器学习和深度学习是它的子部分。

**机器学习(ML) vs 深度学习(DL)**

理解机器学习和深度学习之间的区别的最简单的方法是“DL 是 ML”。更具体地说,这是机器学习的下一次进化。

我们取几个重要参数,对比一下机器学习和深度学习。

1.  **数据依赖**

两者之间最重要的区别是随着数据大小的增加,性能会有所提高。从下图中我们可以看到,由于数据的大小很小,深度学习的表现并不好,但为什么呢?

这是因为深度学习算法需要大量数据才能完美理解。另一方面,机器学习在较小的数据集上工作得非常好。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/ba8ff74f43c39c872d7ad8b852e01a43.png)

**2。硬件依赖性**

深度学习算法高度依赖高端机器,而机器学习算法也可以在低端机器上工作。这是因为深度学习算法的要求包括 GPU,这是其工作不可或缺的一部分。GPU 是必需的,因为它们执行大量的矩阵乘法运算,这些运算只有在使用 GPU 时才能得到有效优化。

**3。特征工程**

它是将领域知识用于降低数据复杂性并使模式对学习算法更可见的过程。就时间和专业知识而言,这一过程既困难又昂贵。在机器学习的情况下,大多数特征需要由专家识别,然后根据域和数据类型进行手工编码。机器学习的性能取决于如何准确地识别和提取特征。但是在深度学习中,它试图从数据中学习高层次的特征,因此它领先于机器学习。

**4。问题解决方法**

当我们使用机器学习解决问题时,建议首先将问题分解成子部分,单独解决它们,然后将它们组合起来以获得最终结果。另一方面,在深度学习中,它端到端地解决问题。

举个例子,

任务是多目标检测,即目标是什么以及它出现在图像中的什么位置。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b376b4e96b994eb81484ea110e9eba24.png)

所以让我们看看如何使用机器学习和深度学习来解决这个问题。

在机器学习方法中,我们将问题分成两部分。物体检测和物体识别。

我们将使用像包围盒检测这样的算法作为例子来扫描整个图像并检测所有对象,然后使用对象识别算法来识别相关对象。当我们结合两种算法的结果时,我们将得到最终结果,即什么是对象以及它在图像中的位置。

在深度学习中,它从头到尾执行这个过程。我们将把一幅图像传递给一个算法,我们的算法将给出物体的位置和名称。

**5。执行时间**

深度学习算法需要大量的时间来训练。这是因为深度学习算法中有太多的参数,需要比平时更长的训练时间。而在机器学习中,与深度学习相比,训练时间相对较少。

现在,在测试数据时,执行时间完全相反。在测试过程中,深度学习算法的运行时间非常短,而 KNN 等机器学习算法的测试时间会随着数据量的增加而增加。

**6。可解释性**

这是人们在行业内使用之前想了很多的主要原因。假设我们用深度学习给出自动化的作文评分。它的表现非常出色,与人类一样,但有些问题它没有告诉我们为什么它会给出这样的分数,事实上,从数学上来说,我们可以找出当时深层神经网络的哪些节点被激活,但我们不知道神经元应该模拟什么,以及这些层集体在做什么。所以我们无法解释结果,但在机器学习算法中,如决策树,给了我们一个清晰的规则,为什么它选择了它选择的东西,所以很容易解释背后的推理。

我希望现在你已经对这三者有了清晰的认识,他们之间有什么样的关系,他们之间有什么不同。

感谢阅读!

# 封面字母+数据科学=你需要知道的

> 原文:<https://towardsdatascience.com/covers-letters-data-science-what-you-need-to-know-2421ed6ec0c?source=collection_archive---------0----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/450179795d933b8e84a5741dab7a47ad.png)

超过字母是最糟糕的。它们揭示了候选人的弱点,雇主不喜欢阅读它们。如果一个申请系统要求你为一个你正在寻找的新职位写一封求职信,或者,你听说你必须在简历中附上一封求职信,这篇文章就是为你准备的。

在继续之前,让我们先关注一些四处流传的求职信建议。大多数求职信都有一个非常典型的结构:介绍你是谁;讨论你的经历;并解释你如何成为这份工作的合适人选。本质上,这是最基本的求职信,或者我所说的*基本求职信格式*。

这看起来很简单,但是大多数人都搞砸了,那些做对的人也没好到哪里去。想想看:如果几乎每个申请职位的人都遵循基本的求职信格式(因为他们都参加了相同的职业研讨会,浏览了相同的网站,并向相同的“职业顾问”寻求建议),那么求职信就不会引人注目——推而广之,他们就成了一个普通的候选人。

求职信总是在看简历之前被阅读。如果你给人的感觉是稳重和疲惫,那么这就是你简历中的感觉。所以,在坐下来写你的求职信之前,问问你自己——我希望招聘经理在阅读我的简历之前是什么样的心情?我们大多数人会说好奇、印象深刻、好奇等等。这是你的求职信应该做的,但是很多人没有做到。

# 少写,不多写

许多工程师、数据科学家、统计学家和数学家回避写作的想法,因为这不是他们的强项。如果那是你——很可能是——我有好消息要告诉你。首先,你不需要写一封杀手级的求职信。其次,这是这个领域深藏不露的秘密,只有极少数人会承认:*我们不喜欢读求职信,因为大多数都很糟糕。*

所以,如果你写了一封求职信,我们会阅读它——然后把它拆开。你只需要知道:在互联网上的每个求职网站上跟随长格式的“基本”求职信[你会让自己暴露在很大的风险中。你可能看起来很无聊,暴露出糟糕的写作技巧,用明显的复制/粘贴求职信模板来烦我们,看起来傲慢,看起来不知道自己在说什么。不过,最重要的是,如果写作对你来说不自然,你就有可能无法展示你真正的优势和个性。](https://resumegenius.com/cover-letters-the-how-to-guide)

你知道吗,你可以用这种超小的求职信来打发时间?

> 亲爱的招聘经理女士,
> 
> 我申请的是高级数据科学家的职位(工作 id: 1234534)。随函附上我的简历和详细的工作经历。根据工作描述,我认为这个职位看起来既有趣又有挑战性——也是我进一步发展职业生涯的好地方!如果您有时间,我想问您更多关于您正在使用的工具以及如果被聘用我可能运行的项目类型的问题。
> 
> 如果你对我的简历有任何问题,请随时通过电子邮件联系我,或者直接拨打我签名中的电话号码。
> 
> 希望尽快收到你的来信,
> 
> 约旦

就是这样!简短,甜蜜,切中要害。最重要的是,它让你看起来正常、有趣、有人情味。对我来说,这是一封完美的求职信。不要胡说什么公司是世界上最好的,或者为什么申请人认为他们比其他人都好。这封信只是表达了我们大多数人不喜欢阅读求职信的对这个角色的兴奋和敬意。看完心情很好。

这些信非常容易写,如果你遵循以下指导方针,你会发现你是最成功的:

## 健全的人类

如果你总是遵循样板求职信,你听起来会像个机器人。这并不是说招聘经理能看穿这一点,因为他们很聪明,*而是每个人都能看穿这一点,因为这很明显*。一遍又一遍地阅读相同的模板是令人讨厌和乏味的。帮自己一个忙,不要写别人认为你应该说的话,写自己认为应该用自己的声音说的话。

## 不要做马屁精

我无法告诉你看那些滴着没有实质内容的赞美的求职信有多讨厌。不要毫无必要地对招聘经理大加赞扬,希望这会给你带来好感,让你成为佼佼者。招聘经理实际上知道在那里工作是什么样的,在一个糟糕的工作日,你的奉承会让你大开眼界。

只要记住:你从未在那里工作过,真的不知道那里是什么样的。但你很想知道更多,对吗?所以提一下。

## 觉得你很适合吗?再想想,这不是你能决定的。

听着,那些求职信网站是完全错误的。你应该**肯定不是**包括这样的短语:“我认为我的背景和技能使我非常适合这个职位。”你对该职位的了解是基于人力资源编辑和该工作要求的浓缩版本。招聘经理决定谁是合适的人选,而不是你。

既然我已经挑战了你关于求职信应该是什么样子的观念,我想给那些真的真的讨厌写作的申请人一些建议。

# 不要写求职信

说真的,这是为大多数申请数据科学和分析职位的人准备的。我们讨厌读求职信(除非它们很棒),你讨厌写求职信。在我们看你的简历之前,一封糟糕的、令人讨厌的或者平庸的求职信会让我们心情不好。

许多申请人提供一封求职信,只是因为他们认为自己需要一封求职信,这严重伤害了他们自己。但是一份有趣的、精心制作的、有扎实工作经验的简历才是我们真正需要的。

如果你申请的是一家使用 Taleo 或类似工具的大公司,在需要求职信的地方,写一份类似上面的简短声明,甚至更短:

> 致相关人员,
> 
> 我很高兴申请首席数据科学家的职位。我的简历包含了详细的工作经历和我的成就。
> 
> 请让我知道你有什么问题,
> 
> 约旦

保存为 PDF 格式并上传。(我找到了一份做这个的工作。)

# 结论

求职信的建议包括从遵循上面描述的可靠格式到做一些古怪的事情(比如制作一封可视化的求职信,这是你绝对不应该做的)。请记住,你的竞争对手可能会读到同样的建议,这意味着你的求职信将会看起来像他们的。

我意识到你们中的一些人想要挑战我的建议。有成千上万的博客在讨论如何写一封完美的求职信,但只有我。但我不会给你关于求职信的一般性建议。我说的是我亲身经历的所见所闻。

看,你的求职信,如果你写的话,应该看起来像你。如果你喜欢写作——这是你的一部分——那就给自己写一封长信(无论你做什么,都要避免“基本”格式)。但是如果你在写作中感到不舒服,你会发现保持简短、直接、切中要点会让你更有自我。所以,如果你能像这篇文章中的段落一样写一封简洁的求职信,那就一定要写!但是,如果你不喜欢写作,那就跳过它吧!

除非他们很了不起,否则我们不喜欢看求职信。你不会因为没有包括一个而被评判。

# 留下评论(和/或掌声)

喜欢我的建议吗?或者,你认为我只是普通的坚果?请在评论中告诉我。还有别忘了留个掌声(或者更多!)如果你喜欢你读过的。

*乔丹·戈德梅尔是*[*Cambia Factor Consulting*](https://www.linkedin.com/in/jordangoldmeier/)*的首席数据治疗师,在那里他帮助公司将数据转化为不仅仅是另一个商业计划,并且是* [*Excel 的创始人。TV*](http://excel.tv) *,一个帮你超越自我的社区。过去的客户包括:亚马逊,原则金融,辛辛那提大学,北约训练任务,和空军研究实验室。* [*跟我合作。*](mailto:jordan@cambiafactor.com)

在简历或求职信方面寻求帮助?[让我知道](mailto:jordan@excel.tv)。

# 用 RNNs“破解”莫尔斯电码

> 原文:<https://towardsdatascience.com/cracking-morse-code-with-rnns-e5883355a6f3?source=collection_archive---------3----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/cb0ff97c771c0c93399b5865c6be454f.png)

剧透:莫尔斯电码其实不需要破解。它很有用,因为使用这种代码可以用最少的设备发送信息,我说它不需要*破解,因为这种代码是众所周知的,点和破折号的组合代表什么不是秘密。但是,理论上,它是一种替代密码——字母表中的每个字母(和每个数字)都用点和破折号来表示,如下图所示。*

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/7248a5e8e967d4a60b7e3243283f973d.png)

International Morse code

让我们暂停怀疑,假设我们收到了莫尔斯电码的信息,但我们不知道如何阅读它们。还假设我们有一些代码及其相应单词的例子列表。现在,我们可能会猜测这是一个替代密码,然后最终找出每个字母的代码;从而解码消息。

或者——我们可以构造一个 ***编码器-解码器模型*** 来猜测(几乎)所有的单词!!作为受虐狂,我们当然会选择后者。话虽如此,让我们鞭策马,踏上与风车作战的征程。

**这里是手头的问题**;我们有几个编码序列及其可理解对应物的例子。使用这些例子,我们必须学习一些模式,并使用这些信息来预测新的编码标记(单词)可能是什么。与我们预测数字结果的常见回归问题不同,我们手头有一个序列到序列的学习问题,其中数据中有时间结构。这是递归神经网络(RNNs)可能有所帮助的一个即时提示(格言是 RNNs 用于语音和语言数据,CNN 用于图像数据,以及组合 RNNs 和 CNN 用于图像字幕)。粗略地说,这属于包含机器翻译问题的一类问题;这个模型的结构是这里的灵感来源。关于这个主题的更多信息,请参考[1]。我们在这里不会花时间在 RNNs 的理论上,但是对于这个主题的清晰简明的介绍,请参考[2]中的一系列文章。

对于那些想知道这个问题是否可以用不同的方式解决的人来说;是的,马尔可夫链蒙特卡罗可以得到类似的结果。在这种情况下,我们将遵循优秀论文[3]第一个例子中提到的程序。

## 大意

粗略地说,我们想从一个输入序列(x1,…x_n)中预测某个输出序列(y1,…,y_m),这涉及到学习条件概率

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/38ef7a3e760974eef1d3cbf658528e99.png)

这里的一个主要障碍是从可变大小的输入预测可变大小的输出。在元级别上,这可以通过组合两个 rnn 来克服,第一个 rnn 将可变大小的输入映射到固定长度的输出,另一个 rnn 接受固定长度的输入并返回可变长度的输出。固定长度的中间向量称为上下文向量,它封装了来自输入序列的信息,每次输入一个字符。产生上下文向量的机制使得 RNNs 可用于捕获时间结构,上下文向量或者是最终时间步长后 RNN 的隐藏状态,或者是它的某个函数。使用*链规则*计算上述条件概率

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/a94bfa13ccc34a9cc25ad3f651fbfd8e.png)

其中 *h* 是上下文向量。最后,可以使用 softmax 函数来计算上述等式右侧的条件概率,该函数将字符 y_{i-1}、…、y_1 的独热编码向量、第二 RNN 中的递归层的输出和上下文向量作为输入。这里使用的特定类型的 RNN 是 LSTM,其有效地克服了简单 rnn 的局限性,简单 rnn 遭受消失梯度问题,并且更好地捕捉长程相关性。

## 数据准备

我们将引入一个特殊字符(*)来表示每个字母的代码之间的空格。例如,SOS 的代码将表示为。。。* — — —*.。.'(而不是’。。。— — — .。.').我们这样做是为了确保对应于给定代码的单词是唯一的。接下来,我们将使用由[编辑的数据集(words_alpha)中的英语单词作为我们数据的单词,而不是产生随机的字母集合。为了理解这些数据,考虑下面给出的单词长度直方图。从直方图可以明显看出,长单词(长度大于 5)比短单词多得多。](https://github.com/dwyl/english-words/blob/master/words_alpha.txt)

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/7f58650c98ef2734c2633c6d05aa6bd3.png)

根据包含长编码单词的数据训练的网络倾向于平均预测长单词。请记住,网络不会找出产生数据的“公式”,也就是说,它不会学习图 1 中的图表。

我们从构造一个函数开始准备数据,这个函数将把一个英语单词作为输入,并输出它的莫尔斯电码。

import random
import numpy as np
import matplotlib.pyplot as plt # construct the Morse dictionary alphabet = " ".join("abcdefghijklmnopqrstuvwxyz").split()values = ['.-', '-...', '-.-.', '-..', '.', '..-.', '--.', '....', '..', '.---', '-.-', '.-..', '--', '-.','---', '.--.', '--.-',
.-.', '...', '-', '..-', '...-', '.--', '-..-', '-.--', '--..']morse_dict = dict(zip(alphabet, values))def morse_encode(word):
return "*".join([dict_morse_encode[i]for i
in " ".join(word).split()]


出于说明的目的,我们将从给定固定长度的单词中产生训练和验证数据。这里我们将这个长度固定为 9,因为长度为 9 的单词的数量足够大(参考上面的直方图)。请注意,这意味着从网络输出的字将是固定长度的,但输入的莫尔斯电码不会都是相同的长度。我们采取的另一个自由是,我们假设我们知道每个字母表由长度最多为 4 的字符串编码(我们不需要做出这个特定的假设,我们可以改为选择训练数据中最长的莫尔斯码的长度作为要跟随的 max_length_x 值)。因此,如果单词 if 的长度为 *n* ,那么对应于它的莫尔斯电码的长度将最多为 4n+(n-1),其中 n-1 项对应于*s 的数量。我们在代码的左侧填充空格以使它们都具有相同的长度,这意味着我们的输入字符词汇表是{ '。'、“—”、“*”、“},并且为了一般性,我们让输出的字符词汇表是所有字母和空白的特殊字符。回到关于网络平均猜测长词的评论,我们的意思是网络将倾向于猜测更少的空格,因为长词数量造成的不平衡。在下面的代码片段中,output_list 将包含英语单词,input_list 将包含填充的莫尔斯电码。

import randomword_len = 9
max_len_x = 4*word_len + (word_len-1)
max_len_y = len_worddef data_gen(n):

with open('words_alpha.txt', 'r') as f:
    all_words = f.read().lower().split('\n')        
    words = [word for word in all_words if len(word)==n]

    *# Shuffle the list since the words are ordered*
    random.shuffle(words)

    g_out = lambda x: ' '*(max_len_y -len(x)) + x
    output_list = [g_out(word) for word in words]

    g_in = lambda x: morse_encode(x)+' '*(max_len_x
                                         - len(morse_encode(x)))
    input_list = [g_in(word) for word in words]

    return output_list, input_listoutput_list, input_list = data_gen(9)

现在,我们构建输入中字符的一个热编码向量,以使输入数据适合神经网络。为此,我们构建了一个类对象(类似于 Keras 文档中的例子),它将有助于把莫尔斯电码和英语单词编码和解码成数组。我们用适当的字符集将类分配给对象。

class CharTable(object): def init(self, chars):
self.chars = sorted(set(chars))
self.char_indices = dict((c, i) for i, c in
enumerate(self.chars))
self.indices_char = dict((i, c) for i, c in
enumerate(self.chars)) def encode(self, token, num_rows):
x = np.zeros((num_rows, len(self.chars)))
for i, c in enumerate(token):
x[i, self.char_indices[c]] = 1
return x def decode(self, x, calc_argmax=True):
if calc_argmax:
x = x.argmax(axis=-1)
return ''.join(self.indices_char[x] for x in x)# we include the white space as a character in both cases below.
chars_in = '*-. '
chars_out = 'abcdefghijklmnopqrstuvwxyz 'ctable_in = CharTable(chars_in)
ctable_out = CharTable(chars_out)


分割数据以从整个数据集 x,y 的四分之一中产生训练集 x_train,y_train,并且我们将保留剩余的四分之三作为验证集 x_val,y_val。请注意,理想情况下,我们应该将训练集的一部分作为验证集,其余部分作为测试集,但是考虑到我们的玩具设置,我们对模型构建比对参数调整更感兴趣。我们现在已经准备好了我们的训练和测试(验证)数据,可以继续修补网络了。

x = np.zeros((len(input_list), max_len_x, len(chars_in)))
y = np.zeros((len(output_list), max_len_y, len(chars_out)))for i, token in enumerate(input_list):
x[i] = ctable_in.encode(token, max_len_x)
for i, token in enumerate(output_list):
y[i] = ctable_out.encode(token, max_len_y) m = len(x)// 4
(x_train, x_val) = x[:m], x[m:]
(y_train, y_val) = y[:m], y[m:]


构建神经网络最简单的方法是使用 Keras 模型和顺序 API。因为我们不需要 TensorFlow 的全部功能和灵活性,所以让我们坚持使用 Keras。

## 模型构造(编码器-解码器模型)

我们选择的模型拓扑将包含简单 RNN 的一个强大变体,称为长短期记忆(LSTM)网络。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/b0a75bf5d71c87acc4a342fd16724a47.png)

第一个 LSTM 将作为编码器,接收可变长度的输入序列,一次一个字符,并将其转换为固定长度的内部潜在表示。另一个 LSTM 将作为解码器,将潜在表示作为输入,并将其输出传递到密集层,该层使用 softmax 函数一次一个字符地进行预测。

该模型的编码器和解码器组件可能具有多层 LSTMs,并且通常事先不清楚哪种拓扑工作得最好。对于机器翻译来说,深度网络通常工作得更好。根据经验,我们希望堆叠图层能够学习更高级别的时态表示,因此当数据具有某种层次结构时,我们会使用它。对我们来说,一层就够了。

该模型是使用 Sequential()构建的,每次添加一层。第一 LSTM 层将 3D 张量作为输入,并要求用户指定输入尺寸。这可以用代码中指定的 input_shape 简洁地完成,其中第一个组件表示时间步长的数量,第二个组件表示特征的数量。对我们来说,特征的数量是输入序列的词汇表中元素的数量,即 4,因为我们有“.”、“—”、“*”和空白字符“”。时间步长的数量是 max_len_x,因为我们一次馈送一个独热编码向量。我们还将指定层中存储单元(或块)的数量(这里由 latent_dim 参数表示,我们使用 256),这是潜在表示的维度。注意,我们想要返回 LSTM 的最终隐藏状态作为潜在表示,这将具有来自所有时间步骤的信息,即完整的输入序列。如果我们使用 *return_sequences = true* 选项,我们将获得每个时间步长的隐藏状态输出,但它将只包含到该步为止的序列信息。

model = Sequential()
model.add(layers.LSTM(latent_dim, input_shape=(max_x_length,
len(chars_in))))


这就结束了简单的编码器模型。接下来我们构建一个类似的层作为我们的解码器。但是,上述代码片段的输出将是一个 2D 数组。我们通过使用方便的 RepeatVector 层重复输出 max_len_y 次,将其转换为 3D 张量,并将其用作下一个 LSTM 层(解码器)的输入。现在,我们使用这个 LSTM 中的 *return_sequences=True* 选项来输出隐藏状态的序列,并且我们需要使用这个信息来进行预测。为此,我们使用时间分布密集层,它输出长度为 max_len_y 的向量,在该向量上,我们使用 softmax 激活函数来挑选最可能的字母。要快速了解时间分布层的用途,请参考这篇[博客文章](https://machinelearningmastery.com/timedistributed-layer-for-long-short-term-memory-networks-in-python/)。

model.add(layers.LSTM(latent_dim, return_sequences=True))
model.add(layers.TimeDistributed(layers.Dense(len(chars_out))))
model.add(layers.Activation('softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam',
metrics=['accuracy'])
model.summary()


这里是网络和各种输入和输出的尺寸的快速总结。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/6ee9d014e1e7cdbdbccc589230a7d49a.png)

我们将模型与数据进行拟合,在 x_train,y_train 集合上进行训练,并使用 x_val 和 y_val 来查看我们做得有多好。我们需要设置的最后一组参数是时期数和批量大小。批量大小是在梯度下降算法中通过网络的训练集部分的大小,在此之后对网络中的权重进行更新。通常批量大小被设置为你的计算机内存所能处理的最大值。一个历元是使用这些批次的训练数据的完整运行。这里,我们将批量大小设置为 1024,使用 120 个历元,从下图中可以看出,经过大约 100 个历元后,精度没有明显提高。一般来说,这是一个试验和错误的问题,看看哪些参数的工作。我们现在使用 fit()方法来拟合模型。

Epochs = 120
Batch_size = 1024hist = model.fit(x_train, y_train, batch_size=Batch_size, epochs=
Epochs, validation_data=(x_val, y_val))plt.figure(figsize=(20,5))
plt.subplot(121)
plt.plot(hist.history['acc'])
plt.plot(hist.history['val_acc'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'validation'], loc='upper left')plt.subplot(122)
plt.plot(hist.history['loss'])
plt.plot(hist.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'validation'], loc='upper right')
plt.show()


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/808a9439fbc0757c23be2cae374b8f94.png)

最后,从上面的图表可以看出,我们可以在验证集上获得大约 93%的准确率,这还不错。当然,如果我们增加训练数据的规模,我们可以做得更好。以下是对随机选择的一组单词的一些预测。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/f581ee282e3729bebcc5dc707955bdd5.png)

Input codes on the left, the corresponding words in the middle and the predictions on the right. The word is green if predicted correctly and red if not.

如你所见,错误的预测也不算太糟糕。我们必须提醒自己,破解密码,也就是说,找出每个字母代表什么,并不能破解密码。事实上,我们可以输入字母表的代码,看看网络对单个字母的代码预测是什么,正如你在下面看到的,我们错了!

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/6e3202a3d07bb47323804f26d1ee80d0.png)

作为编码器-解码器模型的另一个例子,你可以尝试使用[凯撒密码](https://en.wikipedia.org/wiki/Caesar_cipher)或另一种代码,看看这种方法有多有效。

**参考文献:**

[1][http://papers . nips . cc/paper/5346-用神经网络进行序列对序列学习. pdf](http://papers.nips.cc/paper/5346-sequence-to-sequence-learning-with-neural-networks.pdf)

[2]http://colah.github.io/posts/2015-08-Understanding-LSTMs/

[3][http://www . AMS . org/journals/bull/2009-46-02/s 0273-0979-08-01238-X/s 0273-0979-08-01238-X . pdf](http://www.ams.org/journals/bull/2009-46-02/S0273-0979-08-01238-X/S0273-0979-08-01238-X.pdf)

# 打造数据体验

> 原文:<https://towardsdatascience.com/crafting-data-experiences-37bbf7d19fc3?source=collection_archive---------22----------------------->

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/15ec421153b506eb6f230eb3586a4704.png)

以用户为中心的框架已经成为许多不同讨论的关键,包括但不限于软件设计、新兴商业模式、新的工作方式、新的白领工作,甚至我们如何组织我们的社会。尽管这种不断增长的以用户为中心的狂热的优点和需求不可否认是重要的,但它似乎确实把一切都放在了同一个盒子里。有时,我们必须打破已知,才能想象出真正独特的东西……尤其是在数据体验方面。

所以,问题来了:总有那么多事要做!总有一千种不同的事情可以轻松解决,具有巨大的价值并加速上市…因此,数据解决方案通常要么是组装仪表板的传统方法,要么是一种艰巨的努力…通过严格关注显而易见的需求/用户,我们总是努力攀登最高的山峰,而不是挑战极限以征服月球。

现在,当你在讨论中引入一些被错误地认为是“严格技术性”的东西,比如数据解决方案时,以用户为中心的框架通常会逐渐消失,变成标准的比萨饼、条形图和折线图,用花哨的颜色或图案显示用户习惯的确切信息。突发新闻:这既不应该被认为是以用户为中心的,也不是数据解决方案。

# 数据体验

在定义什么是数据体验之前,明确什么不是很重要。自动化不是数据体验!!虽然有人可能会说,收集或创建、处理和分析数据的纯粹事实是自动化的核心,但这里我们不包括使用复杂数据分析来取代人类的解决方案,从你的冰箱在牛奶用完时向市场下订单到无接触工业供应链管理。

在这种情况下,数据体验是那些利用数据的力量为用户提供对给定上下文的可操作理解的体验。通常被称为智能放大或增强智能,它们利用技术作为扩展一个人收集和处理信息的能力的手段,但最终它取决于一个人做出决定的责任(并最终改造系统以提高其准确性)。这意味着,为了恰当地制作增强智能解决方案,用户必须是该过程的关键部分,因为他的专业知识和经验是产品中利用的关键组件。否则就像设计一个手机壳,却不知道手机的大小和形状,也不知道摄像头、传感器、插头和按钮的位置。

通过利用以用户为中心的思维模式的核心,并接受共享人机知识体的可能性,就有可能设计出独一无二的数据体验。

# 从一个明确的目标开始

所以这就是棘手的地方,如果你的目标太宽泛,你的解决方案要么太复杂,要么不可行。另一方面,如果你设定了一个小目标,你要么限制了你的可能性,要么可能忽略了问题的真正根源。有鉴于此,一个明确的目标不是你的最终使命“世界和平”,而是你可以实现的目标“优化 X 地区的货物分配”。

# 创造环境意识

当谈到数据解决方案时,有些技术方面和解决方案的人的方面一样重要。请记住这一点:数据体验利用数据来增强采取行动的能力。有鉴于此,为了确保正确理解上下文,你应该能够回答几个问题:

*   谁是我的用户,他们的需求是什么?
*   他们目前是如何实现目标的?
*   影响他的不同方面有哪些,影响到什么程度?
*   你的用户对这种信息有多熟悉,他有多信任它?
*   我的用户需要多少信息/细节才能有信心采取行动?
*   哪些数据是可用的或可以创建的?

我也喜欢在这个列表中加上“技术上可能的”。但是需要强调的是,这不应该是对技术方面的深入调查。对不同类型的数据解决方案(包括描述性、诊断性、规定性、预测性和人工智能,有监督和无监督的)的关键方面以及总体数据策略方面的总体理解应该会让您有所了解。

# 在可能性上有分歧

在这种解决方案中,构思更多的是提出假设,而不是其他任何事情。如果你的目标是让你的电影获得最多的观众,这一刻应该是关于“天气影响人们对喜剧的开放程度”,而不是关于“在公交车站创造活动”。这是反映可能性或添加尚未关联的新信息的时刻。我记得有一次我们对 IT 部门提供的一些服务进行了调查。我们从单独的调查开始,然后决定一起分析他们的结果。我们发现的最有趣的事情之一是,wifi 的质量会影响员工对其电脑的满意度。现在,停下来想一想,现在大多数应用程序都在云上运行,而且您的大多数文件都存储在云驱动程序中……如果 wifi 速度很慢,这将影响应用程序的加载时间,或者您访问文件的时间,这可能会导致错误的看法,认为问题出在计算机本身。有鉴于此,wifi 升级可能是比电脑更新更好的投资。探索数据的可能性是关于探索不可预见的关系和看待假设已知事物的新方法。

# 就一个解决方案达成一致

选择你的开始假设,并开始测试它!!数据解决方案是关于集合重要的事物和加速采取行动的能力。这意味着,当你开始处理真实数据时,你很可能会推翻许多假设,或者你可能会放弃显而易见的陈述,为相关的发现留出空间。

请记住:你对自己的分析结果越有把握,就越没有必要,也越有可能忽略了一些重要的东西。很像这样:你应该专注于理解一个地方的地理位置和太阳升起的时间之间的关系,而不是太阳是否每天都会升起。

另外,带有可视化的原型制作极其重要!这既是在设想要测试哪些时,也是在用真实数据进行测试时。当对数据进行原型设计时,我喜欢开始理解它的整体属性(它的格式、它的含义以及它变化的频率),并在选择一两个用数据和用户进行测试之前画一些图表。

# 调整,增强,重复!

唯一不变的是一切都在变…这意味着你永远不会放弃数据体验!你应该不断测试看待同一事物的新方法,最重要的是,尝试新的事物来聚合和丰富你的工作。
最后但同样重要的是:数据体验不仅仅由可视化组成,它通常还需要开发额外的功能和动作。有鉴于此,确保在您的假设被测试之前,不要花费太多精力在与分析结果密切相关的特性上,否则您可能会在一些要丢弃的东西上浪费时间。

# 通过 Flask、ElasticSearch、javascript、D3js、异步请求(xml http 请求)和 Bootstrap 创建一个完整的搜索引擎

> 原文:<https://towardsdatascience.com/create-a-full-search-engine-via-flask-elasticsearch-javascript-d3js-and-bootstrap-275f9dc6efe1?source=collection_archive---------3----------------------->

一个**搜索引擎**是一个系统——显示一些**过滤器**——为了定制你的**搜索结果**,让你准确找到你想要的。当用户查询搜索引擎时,基于搜索引擎的算法返回相关结果。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/0cf095691e8bf30fcabc4bd8fde11476.png)

过滤器变成一个列表(下拉列表)、一个表格……任何允许**扩大**或**缩小搜索**的元素,以便获得**相关结果**(你实际感兴趣的结果)。

谷歌是“斯巴达搜索”的王者,也是世界上使用最多的搜索引擎。谷歌提供了一个完全免费的文本搜索过滤器。用户将关键词或关键短语输入到搜索引擎中,并接收个人的和相关的结果。在这篇文章中,我们将通过谷歌。

当谈到葡萄酒评论时,让我们考虑一个来自[葡萄酒爱好者](http://www.winemag.com/?s=&drink_type=wine)的数据集。刮刀的代码可在处[找到。搜索引擎仍然无法品尝葡萄酒,但理论上,它可以根据侍酒师的描述检索出葡萄酒。](https://github.com/zackthoutt/wine-deep-learning)

*   ***国家*** :葡萄酒来自的国家
*   ***描述*** :品酒师描述葡萄酒的味道、气味、外观、感觉等的几句话。
*   ***名称*** :酿酒厂内酿造葡萄酒的葡萄所来自的葡萄园
*   *:葡萄酒爱好者给葡萄酒打分的分数,范围从 1 到 100(尽管他们说他们只给得分> =80 的葡萄酒发评论)*
*   ****价格*** :一瓶酒的成本*
*   ****省*** :葡萄酒所来自的省或州*
*   ****品种*** :用来酿酒的葡萄种类(即黑皮诺)*
*   ****酒厂*** :酿造葡萄酒的酒厂*

*为了揭开旅程的序幕,我们将重点介绍 ***三个特定国家*** 和 ***三个地区*** 的葡萄酒。*

# *第一步:如何过滤来自国家和地区的葡萄酒数据集?*

*![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/c5ac1fae90cf9ee4d20085b7075d520d.png)*

*如果您知道 ***【波尔多】*** 或 ***【香槟】*** 是 ***法国*** 内的地区,知道 ***伦巴第*** 和 ***托斯卡纳*** 是 ***意大利*** 的地区,最后知道 ***安达卢西亚*** 和**的地区,这种方法就很有效如果你不这样做,你就被困住了,什么也得不到。这是一个巨大的问题,因为你必须尝试所有的可能性,才能幸运地得到一些东西。如何处理这个问题?通过在过滤器中实现父/子逻辑。***

*说到 app 本身——有后端(app.py)和前端(index.html)。当您在下拉字段中选择一个元素时,一个 XMLHttpRequest 被发送到服务器,并返回结果(从服务器)。XMLHttpRequest 对象可用于从 web 服务器请求数据。*

*App.py*

*XMLHttpRequest 对象是开发人员的梦想,因为您可以:*

*   *更新网页而不重新加载页面*
*   *从服务器请求数据—在页面加载后*
*   *从服务器接收数据—在页面加载后*
*   *在后台向服务器发送数据*

***onreadystatechange** 属性指定每当 XMLHttpRequest 对象的状态改变时要执行的函数:*

*模板/索引. html*

# *第二步:我们如何在 JS 中通过父属性过滤子属性?*

*当一个给定的下拉列表依赖于另一个下拉列表时,拥有所有可能的选项并不令人愉快。如果你点击法国,然后是加泰罗尼亚,我们就被困住了,什么也得不到,因为加泰罗尼亚不在法国…太糟糕了。要么你尝试所有的可能性来得到某些东西,要么我们实现如下的父子属性:*

*![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2016to2018/-/raw/master/docs/img/1bbff046bf5531cbfdf7b32139beaa1e.png)*

*父子属性:*

# *第三步:将数据加载到 ElasticSearch 中*

*将 ElasticSearch 数据库连接到 python。 **Elasticsearch** 是基于 Lucene 库的搜索引擎。它提供了一个分布式的、支持多租户的全文搜索引擎,带有 HTTP web 接口和无模式的 JSON 文档。Elasticsearch 已经迅速成为最受欢迎的搜索引擎,通常用于日志分析、全文搜索、安全智能、业务分析和运营智能用例。*

from time import time
from elasticsearch import Elasticsearch
from elasticsearch.helpers import bulk
import pandas as pdimport requests
res = requests.get(‘http://localhost:9200')
print(res.content)#connect to our cluster
from elasticsearch import Elasticsearch
es = Elasticsearch([{‘host’: ‘localhost’, ‘port’: 9200}])


*将一个 csv 文件转换成 json 格式放入 ElasticSearch。*

def index_data(data_path, index_name, doc_type):
import json
f = open(data_path)
csvfile = pd.read_csv(f, iterator=True, encoding=”utf8")
r = requests.get(‘http://localhost:9200')
for i,df in enumerate(csvfile):
records=df.where(pd.notnull(df), None).T.to_dict()
list_records=[records[it] for it in records]
try :
for j, i in enumerate(list_records):
es.index(index=index_name, doc_type=doc_type, id=j, body=i)
except :
print(‘error to index data’)


# *额外收获:烧瓶词汇*

## *1.路由技术*

*用于直接访问所需页面,而无需从主页导航。 **route()** 装饰器用于**将 URL** 绑定到**函数**。*

@app.route(‘/hello’)
def hello_world():
return ‘hello world’


*URL**‘/hello’**规则绑定到了 **hello_world()** 功能。因此,如果用户访问[**http://localhost:5000/hello**](http://localhost:5000/hello)URL,则 **hello_world()** 功能的输出将在浏览器中呈现。*

*应用程序对象的 **add_url_rule()** 功能也可用于绑定 url,功能如下:*

def hello_world():
return ‘hello world’
app.
add_url_rule*(‘/’, ‘hello’, hello_world)*


## *2.HTTP 方法*

*Http 协议是万维网数据通信的基础。*

*模板/索引. html*

*

Enter Name:

* ```

app.py

*from flask import Flask, redirect, url_for, request
app = Flask(__name__)

@**app.route**('/success/<name>')
def success(name):
   return 'welcome %s' % name

@**app.route**('/login',methods = ['POST', 'GET'])
def login():
   if **request.method** == '***POST***':
      user = request.form['**nm**']
      return redirect(url_for('success',name = user))
   else:
      user = **request.args.get**('**nm**')
      return redirect(url_for('success',name = user))

if __name__ == '__main__':
   app.run(debug = True)*

3.模板

可以以 HTML 的形式返回绑定到某个 URL 的函数的输出。

hello() 功能将渲染【Hello World】*并附上 < h1 > 标签。*

*from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
   return '**<html><body><h1>**'Hello World'**</h1></body></html>**'

if __name__ == '__main__':
   app.run(debug = True)*

从 Python 代码生成 HTML 内容非常麻烦,尤其是当需要放入变量数据和 Python 语言元素(如条件句或循环)时。这将需要频繁逃离 HTML。

这就是人们可以利用 Flask 所基于的 Jinja2 模板引擎的地方。可以通过 render_template() 函数来呈现 HTML 文件,而不是从函数中返回硬编码的 HTML。

*from flask import Flask, render_template
app = Flask(__name__)

@app.route('/hello/<user>')
def hello_name(user):
   return render_template('hello.html', name = user)

if __name__ == '__main__':
   app.run(debug = True)*

【Web 模板系统】指设计一个动态插入变量数据的 HTML 脚本。网络模板系统由模板引擎、某种数据源和模板处理器组成。

砂箱采用京 a2 模板发动机。web 模板包含 HTML 语法,其中散布了变量和表达式(在本例中为 Python 表达式)的占位符,当呈现模板时,这些占位符将替换为值。

以下代码保存为模板文件夹中的hello.html

*<!doctype html>
<html>
   <body>

      <h1>Hello **{{ name }}**!</h1>

   </body>
</html>*

jinga模板引擎使用以下分隔符来转义 HTML。

  • { %……% }用于语句
  • {{ … }}将表达式打印到模板输出
  • { #……# }对于模板输出中未包含的注释
  • #……# #用于行语句

hello() 函数的 URL 规则接受整数参数。传递给hello.html*模板。在其中,比较接收到的数字(标记)的值(大于或小于 50),并据此有条件地呈现 HTML。*

*from flask import Flask, render_template
app = Flask(__name__)[@app](http://twitter.com/app).route('/result')
def result():
   dict = {'phy':50,'che':60,'maths':70}
   return render_template('result.html', result = dict)if __name__ == '__main__':
   app.run(debug = True)<!doctype html>
<html>
   <body>

      <table border = 1>
         {% for key, value in result.iteritems() %}

            <tr>
               <th> {{ key }} </th>
               <td> {{ value }} </td>
            </tr>

         {% endfor %}
      </table>

   </body>
</html>*

示例:Jquery、AJAX 和烧瓶

[在建…待完成]

通过 3 个简单的步骤,从你的谷歌位置历史创建一个热图

原文:https://towardsdatascience.com/create-a-heat-map-from-your-google-location-history-in-3-easy-steps-e66c93925914?source=collection_archive---------0-----------------------

毫不奇怪,像谷歌这样的公司几乎在跟踪我们的一举一动——如果我们允许的话。然而,通常情况下,访问我们自己正在收集的数据,并以有趣的方式利用它似乎比它应该的更难。在这篇文章中,我将向你展示一个快速的方法,帮助你标出你曾经在世界上的什么地方。好吧,反正既然你开始用安卓手机了。

我一直对我们记忆的工作方式很感兴趣。有许多线索可以帮助我们从大脑深处找回记忆——气味、照片、声音和其他感觉。不同的暗示对不同的人起作用(我的女朋友因为我不记得某些美味晚餐的味道而生我的气。).我发现位置对我来说非常有用,这使得在地图上标出我的每一步行动的能力特别有吸引力。一旦我知道我去过哪里,我就能回忆起大部分时间我在那里做了什么。有许多方法可以做到这一点——草稿图是一种流行的方法。如果你想获得更高的保真度,你可能最终会进入数字世界。以下是方法:

  1. 首先,让你的头脑被你的谷歌时间表所震撼。一定要在笔记本电脑上检查一下,因为移动版本并没有给你所有的功能。这张整洁的地图显示了你去过的所有地方。即实际场所如酒吧、餐厅、公司等。这是一个好的开始,但我希望能够看到我去过的每一个地点,我在那里呆了多长时间或多长时间,以及我是如何在它们之间旅行的。因此,让我们利用我们的数据创建一个热图来实现这一点。

My Google Timeline — Yeah, I remember that drive down through Italy

2)到谷歌外卖,到下载你的位置数据。取消选择所有内容,然后检查“位置历史 JSON 格式”。在下一步中,将您的数据下载为 zip 存档,然后解压缩。现在,您的计算机上应该有一个 JSON 文件,其中包含您的手机存储您的位置时的每个实例(如果您启用了位置服务,这种情况会比您想象的更频繁)。请注意,所有这些可能需要一段时间,这取决于有多少关于你的数据。在我过去的三年里,这大约是 200mb。

Download your Location History from Google Takeout

3)从位置历史可视化工具转到免费使用的热图功能。他们也有一个付费产品,功能增强,你可以看看(注:我与这家公司没有任何关系。我通过谷歌搜索找到了他们,觉得他们的产品很酷。).类似的服务还有很多,但大多数都有非常低的文件大小限制。
上传你的 JSON 文件——et voilà,ready 就是你整个位置历史的热图。你可以像谷歌地图一样导航,还可以随意截图。

The heat map adjusts itself to the zoom factor. So many trips to Paris…

如您所见,这是一件相当简单的事情。你可以用它来回忆你徒步旅行的路线,你穿越欧洲的公路旅行,或者你在巴黎的观光旅行;或者向你的朋友炫耀你去过的所有地方。玩得开心!

@ Thomas _ Essl是写东西的设计师。他还是《七件事》时事通讯和《产品掘金》* 播客的制作人。在www.thomasessl.com*找到更多他的作品。

使用 Python 创建预测房价的模型

原文:https://towardsdatascience.com/create-a-model-to-predict-house-prices-using-python-d34fe8fad88f?source=collection_archive---------0-----------------------

你好,

上次我们看到了如何在泰坦尼克号数据集上进行逻辑回归,许多专业数据科学家认为这是进行数据科学项目的第一步。如果你没有,你可以在这里找到它,

[## 如何开始自己的数据科学之旅!

你好,

medium.com](https://medium.com/towards-data-science/how-to-begin-your-own-data-science-journey-2223caad8cee)

所以,我假设你知道 python 的基本库(如果不知道,那么浏览上面的教程)。我们将使用上次使用的相同库,并添加了 seaborn,这是另一个内置的 python 库,用于进行数据表示。

上一次,我们做了一个数据集,其中有泰坦尼克号乘客的数据,我们知道泰坦尼克号发生了什么,我们不需要浏览数据集。但是大多数时候,数据科学家得到的是他们不知道的数据。深入了解数据非常重要。

到目前为止还不错,今天我们要处理一个数据集,它包含了房子的位置、价格和其他方面的信息,如平方英尺等。当我们处理这类数据时,我们需要了解哪一列对我们来说是重要的,哪一列是不重要的。我们今天的主要目标是建立一个模型,它能根据其他变量给我们一个好的房价预测。我们将对这个数据集使用线性回归,看看它是否能给我们一个好的准确度。

什么是好的准确性?嗯,这取决于我们正在处理的数据类型,对于信用风险数据来说,80%的准确率可能不够好,但对于使用 NLP 的数据来说,这就很好了。因此,我们实际上不能定义“良好的准确性”,但任何高于 85%的都是好的。我们在这个数据集上的目标是达到 85%以上的准确率

让我们开始吧,数据和代码可以在我的 github 链接上找到

[## Shreyas3108 (Shreyas raghavan)

Shreyas3108 有 9 个可用的存储库。在 GitHub 上关注他们的代码。

github.com](https://github.com/shreyas3108)

首先,我们导入我们的库和数据集,然后我们看到数据的头部以了解数据的外观,并使用 describe 函数来查看百分点和其他关键统计数据。

Starting , by importing libraries and reading dataset

Knowing more about the dataset

从上面描述的函数中我们可以推断出什么?

  1. 看看卧室列,数据集有一所房子,房子有 33 间卧室,看起来是一所大房子,随着我们的进展,了解更多会很有趣。
  2. 最大平方英尺是 13,450,最小平方英尺是 290。我们可以看到数据是分布的。

同样,我们只看 describe 函数就能推断出这么多东西。

现在,我们将看到一些可视化,也将看到我们如何从可视化中推断出什么。

哪栋房子最常见(卧室方面)?

让我们看看哪个是最常见的卧室号码。你可能想知道为什么它很重要?让我们从建筑商的角度来看这个问题,有时对于建筑商来说,重要的是要看哪种房子卖得最好,这使得建筑商能够以此为基础建造房子。在印度,一个好地段的建筑商选择建造超过 3 间卧室的房子,这吸引了更高的中产阶级和社会的上层阶级。

让我们看看这些数据的结果如何?

House bedrooms and count

正如我们从可视化中看到的,三居室的房子最常出售,其次是四居室。那么怎么有用呢?对于一个拥有这些数据的建筑商来说,他可以建造更多的三居室和四居室来吸引更多的买家。

所以现在我们知道三居室和四居室的销量最高。但是在哪个地方呢?

基于纬度和经度可视化房屋的位置。

所以根据数据集,我们在数据集上有每栋房子的纬度和经度。我们要去看看共同位置,看看房子是怎么摆放的。

How houses are placed ?

我们使用 seaborn,我们得到他的美丽的可视化。Joinplot 函数有助于我们看到数据的集中和数据的放置,非常有用。让我们看看我们能从这个可视化中推断出什么。对于纬度在-47.7 和-48.8 之间有许多房子,这意味着它可能是一个理想的位置,不是吗?但是当我们谈到经度时,我们可以看到在-122.2 到-122.4 之间的浓度很高。也就是说大多数买家都去过这个地方。

影响房价的常见因素有哪些?

我们看到了共同的位置,现在我们将看到一些影响房价的共同因素,如果是这样的话?那是多少?

让我们从价格是否受到房屋居住面积的影响开始。

Price vs Square feet and Price vs Longitude

我们上面使用的图被称为散点图,散点图帮助我们看到我们的数据点是如何分散的,通常用于两个变量。从第一张图中我们可以看到,居住面积越大,价格越高。虽然数据都集中在某个特定的价格区域,但从图中我们可以看到,数据点似乎是在直线方向上。由于散点图,我们也可以看到一些不规则性,最高平方英尺的房子卖得很低,也许有另一个因素或可能数据一定是错误的。第二个数字告诉我们房子在经度上的位置,这给了我们一个非常有趣的观察结果,即-122.2 到-122.4 之间的房子售价更高。

Similarly we compare other factors

我们可以看到更多影响价格的因素

Total sqft including basement vs price and waterfront vs price

Floors vs Price and condition vs Price

Which location by zipcode is pricey ?

从上面的描述中我们可以看出,许多因素都在影响着房子的价格,比如平方英尺增加了房子的价格,甚至位置也影响着房子的价格。

现在,我们已经熟悉了所有这些表示,并可以讲述我们自己的故事,让我们移动并创建一个模型,根据其他因素(如平方英尺、滨水区等)来预测房子的价格。我们将会看到什么是线性回归,我们是如何做的?

线性回归:-

简而言之,统计学中的一个模型,它帮助我们根据变量的过去关系来预测未来。所以当你看到你的散点图有线性排列的数据点时,你知道回归可以帮助你!

回归的工作原理是直线方程,y=mx+c,趋势线是通过数据点设定来预测结果的。

Fitting line on the basis of scatter

我们预测的变量称为标准变量,称为 y。我们预测所基于的变量称为预测变量,称为 x。当只有一个预测变量时,预测方法称为简单回归。且如果存在多个预测变量,则进行多重回归。

让我们看看代码,

Linear regression on the data to predict prices

我们使用训练数据和测试数据,训练数据来训练我们的机器,测试数据来看看它是否已经很好地学习了数据。在任何事情之前,我希望每个人都记住机器是学生,培训数据是教学大纲,测试数据是考试。我们看到多少机器已经得分,如果它得分好,模型是成功的。

那我们做了什么?我们一步一步来。

  1. 我们导入依赖项,对于线性回归,我们使用 sklearn(内置于 python 库中)并从中导入线性回归。
  2. 然后,我们将线性回归初始化为变量 reg。
  3. 现在我们知道价格是可以预测的,因此我们将标签(输出)设置为价格列,我们还将日期转换为 1 和 0,这样就不会对我们的数据产生太大影响。对于 2014 年后新建的房屋,我们使用 0。
  4. 我们再次导入另一个依赖项,将数据分成训练和测试。
  5. 我把我的训练数据作为 90%,10%的数据作为我的测试数据,用 random_state 把数据的拆分随机化。
  6. 现在,我们有了训练数据、测试数据和标签,让我们将训练和测试数据拟合到线性回归模型中。
  7. 将我们的数据拟合到模型后,我们可以检查数据的得分,即预测。在这种情况下,预测值为 73%

这个模型的精确度低于我们 85 的目标。那么,我们如何实现 85%的目标呢?

我们使用不同的方法,这对于像这样的弱预测模型是非常重要的。

这可能看起来有点高级,但是如果理解的话,这是一个非常好的工具,可以实现更好的预测。

为了建立预测模型,许多专家使用梯度推进回归,那么什么是梯度推进呢?这是一种用于回归和分类问题的机器学习技术,它以弱预测模型(通常是决策树)的集合的形式产生预测模型。

现在为了简单起见,请记住我们在学生时代是如何映射机器的,训练数据是教学大纲,测试数据是考试数据。让我们试着理解使用相同的梯度推进方法。那么,我们来分析一下为什么我们的学生(机器)没有达到 85%以上?可能有很多原因,但很少有这样的原因

  1. 我们的学生在考试前忘记了一些题目,同样,机器读取的数据也会丢失。
  2. 可能是一个不通过阅读学习,但需要视觉化的弱学习者。我们的机器可能是一个弱学习者,可能需要决策树。
  3. 即使使用了更新的技术,我们的学生也可能记不住教学大纲,所以我们给学生时间去阅读和理解。机器也是如此。

因此,对于所有这些问题,有一个解决办法,梯度下降推进。

[## 机器学习的梯度推进算法简介-机器学习…

梯度推进是构建预测模型的最强大的技术之一。在这篇文章中,你会发现…

machinelearningmastery.com](http://machinelearningmastery.com/gentle-introduction-gradient-boosting-algorithm-machine-learning/)

此链接提供了对梯度推进算法的深入理解。

Gradient boosting graph for understanding

让我们看看我们是如何做到这一点的,然后我们可以深入了解正在发生的事情。

  1. 我们首先从 sklearn 导入库(相信我,它是所有统计相关模型的最佳库)
  2. 我们创建一个变量,在这里定义梯度推进回归变量并设置参数

n_estimator —要执行的升压阶段的数量。我们不应该把它定得太高,那会使我们的模型过拟合。

max _ depth 树节点的深度。

learning _ rate 学习数据的速率。

损失—要优化的损失函数。“ls”是指最小二乘回归

最小样本分割—学习数据时要分割的样本数量

3.然后,我们将训练数据拟合到梯度推进模型中,并检查准确性

4.我们得到了 91.94% 的准确率,这是惊人的!

我们可以看到,对于弱预测,梯度提升对相同的训练和测试数据起作用。

点击此链接了解更多关于梯度推进回归器的信息

[## 3.2.4.3.6.sk learn . ensemble . gradientboostingregressor-sci kit-learn 0 . 18 . 1 文档

class sk learn . ensemble . GradientBoostingRegressor(loss = ' ls ',learning_rate=0.1,n_estimators=100,子样本=1.0…

scikit-learn.org](http://scikit-learn.org/stable/modules/generated/sklearn.ensemble.GradientBoostingRegressor.html)

我们得到了我们想要的!准确率高达 91.94%。将此技术应用于各种其他数据集并发布您的结果。尝试放置随机种子,检查它是否改变了数据的准确性!如果是的话,请告诉我。谢谢你阅读它!

希望你喜欢它!再见,以后会有更多的模型和内容!请分享推荐给你的朋友。

更新:这个的代码可以在 https://github.com/Shreyas3108/house-price-prediction上找到

从零开始为你自己的棋盘游戏创造人工智能-alpha zero-第 3 部分

原文:https://towardsdatascience.com/create-ai-for-your-own-board-game-from-scratch-alpha-zero-part-3-f22761372245?source=collection_archive---------9-----------------------

用 AlphaZero 算法实现 AI 到 EvoPawness(暂名),一个来自我创意的棋盘游戏。

Photo by Maarten van den Heuvel on Unsplash

大家好,欢迎来到evopheness(暂名)上制作 AI 的第三部分。在本文中,我们将实现 AlphaZero 算法到游戏中。这篇文章将告诉你一个关于 AlphaZero 和 AlphaZero 实现的简短总结,尽管是简化的。我们将一步一步来做。我们将使用第 1 部分中提到的一些术语,如结果函数、可能的动作和终端函数。

实验还没有完成。训练这款游戏的代理需要很长时间,尤其是单机。我已经训练特工一天了。可惜代理还是不够好。即便如此,它还是学会了一些策略,比如如果骑士能攻击国王,就用骑士连续攻击国王。

我停止训练代理让我的电脑休息。我怕我的笔记本电脑强行训练一天以上就坏了。我还限制了 AlphaZero 的超参数,以加快训练时间。很抱歉我做这个实验的资源有限😿。

Just a random cat. Source : https://pixabay.com/en/cat-sad-cute-small-sweet-pet-3266673/

即使实验没有完成,我也会试着写下我是如何在这个游戏上实现 AlphaZero 的。我希望它能给那些想学习强化学习算法 AlphaZero 并在游戏中实现它们的人一点启发。

我对游戏规则做了几处改动。这个游戏仍然是决定性的。

这篇文章的目标读者是对人工智能和设计游戏感兴趣的人。如果你不是其中之一,当然你还是可以看到这篇文章。我希望通过发表这篇文章来提高我的写作技巧,并且内容对你有益😄。

振作起来,这将是一篇长文。它有 25 分钟的阅读时间!

概述

  1. 贮藏室ˌ仓库
  2. 关于 AlphaZero 的简要描述
  3. 改变规则
  4. 步伐
  5. AlphaZero 的组件
  6. AlphaZero 的实施
  7. 吸取的教训
  8. 结论
  9. 编后记

贮藏室ˌ仓库

如果你开始阅读这篇文章的游戏进展,这里是资源库:

[## 哈里欧/埃沃-波尼丝

自助项目 Evo Pawness(临时名称)。通过在…上创建一个帐户,为 haryoa/evo-pawness 的发展做出贡献

github.com](https://github.com/haryoa/evo-pawness)

我已经向存储库添加了几处更改:

  • 添加 AlphaZero 实现
  • 重新格式化模块或文件夹结构
  • 更改动作表示键。
  • 更改游戏模型和控制器以匹配新规则

注意:现在,我建议你不要试图训练模型,直到我重构和清理代码。还有,代码仍然超级乱,我会尽快清理和重构。在下一个星期六,我会努力做到这一点。

编辑(2018 年 3 月 12 日):我已经清理了代码。现在我们有 Config.py 来编辑配置。关于如何启动程序,请参见main.py -h。详情等我明天编辑完README.md

编辑 2 (10/12/2018) :我已经推送了更新的README.md

关于 AlphaZero 的简要描述

Source : https://en.chessbase.com/post/the-future-is-here-alphazero-learns-chess

AlphaZero 由 Deep Mind 创建,发表在以下论文中[来源 4]。它的特别之处在于,它可以在 24 小时的训练下击败国际象棋和松木的最佳 AI。它使这个算法成为当时 AI 游戏上最好的。要了解更多信息,你可以看看这篇关于这个算法有多强大的文章。

它也没有人类的专业知识作为输入(游戏规则除外)。它从零开始学习,成为最好的人工智能游戏程序,击败了当时棋盘游戏中最好的人工智能。

我还发现算法非常简单,但令人惊讶。该算法将利用当前知识探索有希望的可能路径。搜索到的路径将判断当前路径是否有利。其有利程度的分数被降低,并被用作是否应该再次探索这条道路的考虑因素。

在思考未来可能性的探索之后,采取探索最多的行动。这意味着,如果经常探索这条道路,这个行动可能是好的。

在游戏结束时,评估它选择的路径是否与游戏的结果相匹配。它告诉我们,在探索未来可能的道路时,知识是否误判了选择的道路。知识会根据比赛结果更新。

AlphaZero 要求博弈具有完全信息(博弈状态对双方都是完全已知的)和确定性。既然这个游戏两者都有,那么 AlphaZero 算法就可以用在这个游戏上。

在本文中,我们将简化本文中使用的架构。我们将基于我读过的文章使用一个简化的 AlphaZero 实现。在大卫·福斯特【来源 2】使用的实现中,他使用了 4 个连接到策略头和值头的剩余层。我使用了他的架构并改变了一些超参数。我还在[Source 1]中看到了 AlphaZero 的实现,并修改了实现以适应这个游戏。我遵循了那篇文章中的蒙特卡罗树搜索实现,并将实现从使用递归改为结构化数据树。

这些文章中使用的实现与本文一致,只是跳过了一些部分。我跳过了几个步骤,比如v resignation。在这两篇文章中,它们没有实现堆叠状态。在本文中,我实现了堆叠状态,并使用 140 个平面作为模型的输入。在本文中,AlphaZero 的实现还没有通过使用虚拟丢失来实现多线程 MCTS。

改变规则

  1. 符文将在 3 个不同的位置出现。有(在 y,x 轴上)(4,0),(4,4),和(4,8)。在(4,0)产生符文将会增加 2 点攻击点。在(4,4)产生的符文将提高 2 点生命值。在(4,8)处产生的符文将会增加 1 步点数。
  2. 当符文产生时间(每 5 回合)不会产生符文时,棋子所占据的符文位置。
  3. 步数、生命值和攻击点数是有限制的。步数 3,生命值 20,攻击点 8

步伐

本文将按照以下顺序实现 AlphaZero:

  1. 定义模型输入的状态表示。我们使用堆叠状态。
  2. 定义游戏的动作表现。
  3. 决定哪一方将用于代表所有玩家的状态(黑色或白色),并定义如何更改该方。
  4. 定义一些可用于增加模型输入的相同状态
  5. 定义奖励函数
  6. 实现用于输出策略评估的神经网络的体系结构
  7. 实现蒙特卡罗树搜索
  8. 添加 duel 函数来评估最佳模型。

AlphaZero 的组件

在我们进入这一部分之前,我建议如果你没有读过第一部分,读一读。它将涵盖游戏规则。

表示状态

在本节中,我们将看到神经网络输入的表示状态。神经网络的输入是代表状态的图像堆栈。实现中使用了 28 个输入特性。这里我们就用 5 步历史(注:在 AlphaZero 的论文中,步历史的个数是 8。这里,本文将尝试不同的数字)。从我们使用的步骤历史数来看,这意味着有 140 个输入特征(28 X 5)。

历史将保存前一回合的状态表示。如果我们使用 5 步历史,假设状态在 T 时刻,我们将获得 T、T-1、T-2、T-3、T-4 状态,这些状态将被堆叠并成为神经网络的输入。

以下是输入特征:

4-10,13 号使用二进制特征,而其他使用频率特征。

因为,棋盘是 9×9,如果我们有batch_size 作为神经网络输入的总实例,我们就有(batch_size, 9, 9, 140)形状作为神经网络的输入。因此,我们有 4 维数据作为输入。

对于代码,可以在下面的源代码中看到get_representation_stack()函数:

[## 哈里欧/埃沃-波尼丝

自助项目 Evo Pawness(临时名称)。通过在…上创建一个帐户,为 haryoa/evo-pawness 的发展做出贡献

github.com](https://github.com/haryoa/evo-pawness/blob/master/reinforcement_learning_train/util/stacked_state.py)

在源代码中,我们将使用包含我们在第 1 部分中定义的元素的AIElements 类。我们使用deque 作为我们的数据结构(像堆栈一样)。

在我们获得神经网络的输入表示之前,我们将把状态(我们在第 1 部分定义的状态表示)堆叠到一个具有期望的最大长度的deque 数据结构中(在本文中,我们将其设置为 5)。然后我们处理deque ,并将其转换为神经网络的输入表示。

动作表示

游戏中有 5 种动作可供选择。有激活、提升、移动、攻击和跳过。

对于激活动作,我们需要选择想要激活的棋子。我们需要棋子的坐标。因此,我们有 81 (9 x 9)个不同的独特激活动作。

注意:我们有一个不同于第 1 部分中所述的动作键表示,新的表示如下:

Action Key Representation for the activate action: a*y,x
y : pawn's axis-y
x : pawn's axis-x
example a*1,0

对于升级动作,我们需要选择想要升级的棋子,然后选择可能的选项来升级该棋子。我们需要棋子的坐标。我们有 9 x 9 种不同的独特行动来选择可能的棋子。有 4 种升级兵类型(女王、车、主教、骑士),因此有 324 种(9 x 9 x 4)独特的升级方式。

Action key representation for the promote action : p*y,x*choice
y : pawn's axis-y
x : pawn's axis-x
choice : promote choice, K for Knight, R for Rook, B for Bishop, and Q for Queen
example p*3,3*Q

对于攻击和移动动作,在这个游戏中,我们有 7 种类型的兵。移动的方向已在第 1 部分中定义。在第一部分中,我们将攻击和移动作为单独的动作。在本文中,我们将把攻击动作和移动动作合二为一(它们没有重叠,所以我们可以合并)。我们可以看到士兵、车、主教和国王的移动方向是女王的子集。它在 N、NE、E、SE、W、SW、W 和 NW 方向上垂直、水平和对角移动。只有骑士的招式不同。它在指南针的各个方向上呈 L 形移动。总之,我们有两种类型的移动,女王和骑士。

我们采取行动有两个步骤:选择棋子和根据可能的行动选择棋子的合法移动。在这种情况下,我们有 81 (9 x 9)个不同的选择棋子的动作。然后为了选择合法的移动,我们有 8 种不同的骑士移动类型和 24 种(8 x 3)女王移动类型(注意:我们有 3 作为步点的限制,所以女王移动可以由 24 移动类型组成)。为攻击和移动动作选择合法移动,可以进行的唯一动作总数为 2592 (81 x 32)。

Action Key Representation for the activate action: m*y1,x1*y2,x2
y1 : selecting the pawn in axis-y
x1 : selecting the pawn in axis-x
y2 : direction of the move in axis-y
x2 : direction of the move in axis-xExample m*0,1*2,1 (means that it will move from (0,1) to (2,2)

如果玩家不能做任何事情,跳过。

The action key representation for skip is skip

唯一动作的总数是 2998 (9 x 9 x 37 + 1)

动作表示用于编码概率分布,该概率分布用于随后进行蒙特卡罗树搜索(MCTS)时选择动作。稍后,在该状态下不可能做的动作被屏蔽,并且将概率设置为 0,并且重新归一化可能动作的概率分布。

有关如何生成所有可能的动作的实现,请参见存储库。我使用scikit-learn库中提供的LabelEncoderOneHotEncoder 将动作编码到一个热编码器中。

一个热编码器类别(见fit()):

[## 哈里欧/埃沃-波尼丝

自助项目 Evo Pawness(临时名称)。通过在…上创建一个帐户,为 haryoa/evo-pawness 的发展做出贡献

github.com](https://github.com/haryoa/evo-pawness/blob/master/reinforcement_learning_train/util/action_encoder.py)

生成所有独特的动作(见action_spaces() 功能):

[## 哈里欧/埃沃-波尼丝

自助项目 Evo Pawness(临时名称)。通过在…上创建一个帐户,为 haryoa/evo-pawness 的发展做出贡献

github.com](https://github.com/haryoa/evo-pawness/blob/master/reinforcement_learning_train/util/alphazero_util.py)

决定哪一方将用于代表所有玩家的状态(黑色或白色),并定义如何更改该方。

在 AlphaZero 的论文中,我们需要将棋盘的视角定位到当前玩家。我们必须决定哪一方对双方都有利。我选择白色作为双方玩家的视角或观点(POV)。所以如果当前回合是黑人玩家,我们会让黑人玩家视角变成白人玩家视角。如果当前回合是白牌玩家,我们不会改变玩家视角。

我们如何做到这一点?

如果这是一盘棋,那就好办了。只需颠倒棋子的颜色。在这场比赛中,我们不能这样做。国王被固定在那个位置,不能移动。所以,颠倒棋子会使状态无效。那么如何才能做到呢?

我们需要镜像或反映每个棋子的位置。之后,反转所有棋子的颜色。

下面是伪代码:

def mirror(self, pawn):
    pawn.y = pawn.y - 4 # 4 is the center position of the board
    pawn.y *= -1 # after we move the center to 0, we mirror it pawn.y += 4 # Then we move the center point to 4
    pawn.color = inverse(pawn.color)

我们对所有的兵都这样做(包括国王)。我们已经反映了棋子的位置。

Example of the original state and the mirrored one.

当我们通过改变棋子的位置来改变棋盘的视角时,我们也需要改变动作。如果动作是移动或攻击,我们需要镜像动作的坐标和方向。这类似于改变棋子的位置,我们将通过操纵 y 轴镜像坐标来改变动作的坐标。对于方向,我们只需要将 y 轴乘以-1。

例如:

{} = * means multiplication in this bracket
original = a*2,3
mirror = a*{(2-4)*-1+4},3 = a*6,3original = m*1,0*1,0
mirror = m*{(1-4)*-1+4,0}*{1*-1},0 = m*7,0*-1,0

这就是我们改变玩家视角的方式。

有关实现,请参见源代码中定义的所有函数:

[## 哈里欧/埃沃-波尼丝

自助项目 Evo Pawness(临时名称)。通过在…上创建一个帐户,为 haryoa/evo-pawness 的发展做出贡献

github.com](https://github.com/haryoa/evo-pawness/blob/master/util/state_modifier_util.py)

它本质上和伪代码一样,但是是用面向对象的方式。我们将改变所有棋子的属性。动作的镜像也在源代码中定义。

定义一些可用于增加神经网络输入的相同状态

我们应该定义一些相同的状态,用于神经网络的输入,如果游戏有它的话。随后,它可用于增加神经网络的输入。此外,在本文中,相同的状态将被用于评估 MCTS 的叶节点中的状态,其中它将被均匀地随机选择。相同的状态通常是原始状态的二面角反射或旋转。我认为,这个组件的目的是使培训更快,并确保包括应该有相同情况或评估的状态。

不幸的是,这个游戏没有相同的状态。所以我们不能在这个游戏中使用这个组件。

定义奖励函数

我们将使用奖励函数来告诉代理人最终的结果是赢还是输。由于我们使用蒙特卡罗树搜索,我们将在终端状态调用奖励函数。然后就会变成我们要优化的神经网络的输出。

奖励函数是我们在第 2 部分定义的效用函数。我们将把该值归一化到{-1,1}的范围内。下面是高级伪代码实现:

def reward_function(state, player):
   if state.win(player):
       return 1
   else
       return -1

由于只有在状态处于终态时才会调用奖励,所以这叫稀疏奖励。神经网络将被训练以评估和预测当状态不是终点时的奖励。

具体实现,可以看State.sparse_eval()函数。第 1 部分定义了如何调用终端状态。

[## 哈里欧/埃沃-波尼丝

自助项目 Evo Pawness(临时名称)。通过在…上创建一个帐户,为 haryoa/evo-pawness 的发展做出贡献

github.com](https://github.com/haryoa/evo-pawness/blob/master/model/state.py)

神经网络体系结构

在本节中,我们将创建 AlphaZero 中使用的神经网络的架构。在本文中,他们使用了 20 个剩余块,后跟策略头和值头。他们使用它是因为它是当时计算机视觉任务中最先进的深度学习架构。当然,我们不会用那个。尤其是对我这样一个预算很低、资源很少的人来说😢。

相反,我们将使用简化的架构。我们使用[Source 2]中定义的架构,但做了一些修改。这是它的架构:

Implementation of our neural network. x = 4, we use 4 residual layers.

因此,我们将有 4 个剩余块,2 个 CNN,后跟一个策略头和值头。

超参数也简化了。根据我的 GPU 减少了每一层的输出单位。我觉得那篇文章用的超参数就够了。因此,我们将遵循那篇文章中使用的超参数,只做一点点改变(比如稍微增加或减少输出单位)。

神经网络的输入是我们上面定义的状态表示。

神经网络有两个输出,标量特征 v 和移动概率矢量 p 。神经网络的输出范围对于 v 为{-1,1},对于 p 为{0,1}。这就是为什么我们将 tanh 用于 v 的激活功能,将 softmax 用于 p 的激活功能。

神经网络将最小化以下目标函数:

Loss/Objective function excluding the regularization

其中vθ(st) 是数值头的输出,一个标量,它评估当前状态的情况,而 pθ(st),策略头的输出,是来自状态st的预测策略。

vθ(st)会被训练得和zt一样接近,这是一个玩家相对于所选视点(POV)的视角,游戏的最终结局。在我们的例子中,视点是白色玩家。zt 根据游戏结果,值可以是-1、0 或 1。vθ(st)将被训练计算当前状态的评价。

πt 是来自状态 st 的策略的估计。我们还需要训练神经网络的参数,以使 pθ(st)πt足够接近。pθ(st)将是一个概率分布的向量,它告诉我们值越高,行动越好,被选中的机会越高。它会被训练得和πt一样近。如何获得πt将在下一节中定义。当然,它还需要与所选视点的视角相同。

优化程序正在以定义的学习率使用 Adam 优化程序。

因此,总的来说,我们将尽量减少对当前状态的评估和当前状态的政策的预测误差。设batch_size 是我们的神经网络的总输入实例,状态表示的输入形状是(batch_size, 9,9,140)。有两个输出,策略头和值头。策略头有(batch_size, 2998)(唯一动作的总和),值头有(batch_size, 1)形状。

之后,在蒙特卡洛树搜索模拟中使用神经网络模型来评估和预测状态的策略(策略评估)。该模型将被优化以最小化每集结尾的损失。

有关实现,请参见下文(类别PawnNetZero):

[## 哈里欧/埃沃-波尼丝

自助项目 Evo Pawness(临时名称)。通过在…上创建一个帐户,为 haryoa/evo-pawness 的发展做出贡献

github.com](https://github.com/haryoa/evo-pawness/blob/master/reinforcement_learning_train/alpha_zero/deep_net_architecture.py)

蒙特卡罗树搜索

Source : mcts.ai

在我们深入蒙特卡洛树搜索之前,我建议在这里阅读关于 MCTS 的简要描述。

我们将使用蒙特卡罗树搜索来提高策略估计质量(策略改进)。这将是预测给定状态下代理的动作的组件。MCTS 用于模拟游戏,这是一种自学式游戏(代理扮演两个轮流游戏的玩家)。对于每一集(一个完整的游戏)的每一步,MCTS 被模拟直到给定的模拟次数。它也是一种搜索算法,像我们以前使用的 minimax,但 MCTS 不会扩展所有可能的操作,而是使用引导式“启发式”来确定要扩展的节点。

MCTS 中的树将由代表板配置的节点组成。存在于每个节点之间的有向边表示状态的有效动作。边不同于极大极小树搜索,不仅要保存动作名称,边还有几个参数,每次模拟都会更新。

该边将包含我们将更新的几个参数:

  1. **Q(s,a)**:对状态s采取行动a的期望报酬或平均报酬,它将在备份步骤中更新。这将是由神经网络产生的所有预测的vθ(st)的评估或奖励的平均值,或者是叶节点(其是节点s的后代)中终端状态的实际奖励(-1,0,1)。
  2. **N(s,a)**:状态**s**下模拟动作**a**的次数
  3. **P(s,a)**:在状态s中采取行动a的估计概率,这是由神经网络的模型产生的策略。

而节点将包含几个参数:

  1. **N(s)**:模拟进入该状态的次数(s)。它等于状态s中每一个可能动作aN(s,a)之和。

在每一集的开始, MCTS单个根节点初始化。根节点在初始化时也作为叶节点。从这个根开始, MCTS 将扩展该树,直到达到模拟的极限数量。

初始化树后,在 AlphaZero 中进行 MCTS 有 4 个步骤:

  1. 挑选
  2. 展开并评估
  3. 支持

步骤 1–3 根据模拟次数进行迭代,然后执行步骤 4。

挑选

MCTS 中的模拟将在根节点(s0)开始,并且如果模拟在时间步长 l 遇到叶节点sL 则结束。在这些时间步长的每一个中,对于在扩展和评估步骤中已经扩展的每个节点,根据每个节点的边中的参数选择动作。这里,我们将选择状态s中的动作 a,其具有最高的U(s,a),使用 PUCT 算法的变体的置信上限。

Upper Confidence Bound

其中,cpuct 是确定勘探级别的超参数。N(s,b)之和等于N(s)

如果ss0(根节点),P(s,a)变为 P (s, a) = (1 — e)*pa + e*ηa

其中η是使用狄利克雷噪声和所选参数的概率分布,e是 0.25。这将使探索尝试根状态中的所有移动。

这个步骤将一直进行,直到找到叶节点。如果找到了叶节点,则调用展开和评估步骤。

展开并评估

如果当前节点是叶节点,将执行此操作。我们将使用我们在上面定义的表示来评估状态sL,作为神经网络的输入。它将输出策略和当前状态的评估值。

输入会以均匀概率分布随机变换成任意相同的状态(可以选择不变的那个)。因为我们在这个博弈里没有,状态不会转换,神经网络的输入永远不变。所以在evopheness(临时名)的实现中,函数 di 的输出将返回**sL** 而不转换形式。

输出是策略**p**和状态sL的值**v**,如前一节所述。如果p是转换状态的动作,那么这个动作应该转换回原始状态的方法。

**p** 中,无效动作概率将被屏蔽,有效动作将被归一化,这样向量将和为一。

然后,用包含该状态下所有可能动作的边来扩展叶节点。每个边和叶节点参数被初始化为:

N(sL,a) = 0, Q(sL,a) = 0, N(s) = 0, P = p, P(sL,a) = pa
Where **a** is a possible action in the state sL.

支持

在我们展开叶节点(sL)后,该参数将被反向传递到所有父节点,直到根节点,这是通过每一步t ≤ L完成的。这些参数将更新如下:

Q(st,at) = (Q(st,at)* N(st,at) + v)/ (N(st,at) + 1)
N(st,at) = N(st,at) + 1
N(st) = N(st) + 1

如果st 的玩家与sL不同,请注意v = v * -1。比如:st’ s 转黑,sL’ s 转白。由于这是一个零和游戏,对面玩家(v)的评价将是当前玩家的负面。

执行备份步骤后,如果尚未达到最大模拟次数,则从根节点再次执行选择步骤。

这些步骤将被重复,直到达到模拟的最大数量。如果已经达到,执行以下步骤:

搜索结束时,应根据模拟时更新的参数选择根位置 s0 中的动作**a**。动作概率给定根状态(πa|s)的选择与模拟时计算的访问次数N(s,a)的指数成比例。

我们将用下面的公式计算所有动作的策略:

其中τ是用于控制探测程度的温度。当游戏中的回合或步数低于 30 时,τ设置为1,否则无穷小。

MCTS 的高级伪代码实现如下:

实现可在此处找到:

[## 哈里欧/埃沃-波尼丝

自助项目 Evo Pawness(临时名称)。通过在…上创建一个帐户,为 haryoa/evo-pawness 的发展做出贡献

github.com](https://github.com/haryoa/evo-pawness/blob/master/reinforcement_learning_train/alpha_zero/mcts.py)

请注意,存储库中的实现会有所不同,但目标是相同的。在存储库中,我在'【T6]'函数中包含了选择、扩展和评估步骤。

竞技场

Source : https://pixabay.com/en/knight-sword-fighting-middle-ages-2551859/

为了保持模型的质量,我们必须确保所使用的模型是最好的。为此,AlphaZero 将比较当前最佳模型和当前模型的质量。做起来相当容易。我们需要两个互相争斗的特工。

这些模型通过选择的 max 模拟和max_step 限制来互相坑**n** 回合,以防止游戏无限循环而使终端状态不可达。这将返回决定最佳模型的分数。**n**可以由任意数字填充。

如果当前模型以选定的优势胜出(论文中为 55%),则最佳模型将被当前模型取代。最好的模型用于下一集。

如果最佳模型获胜,则最佳模型仍将用于下一集。

我们将启动两个 MCTS,一个使用最佳模型神经网络,另一个使用当前神经网络。播放器的颜色可以自己决定(比如:白色是最好的型号,黑色是当前型号)。

这是伪代码

实现可以在这里找到(fight_agent()函数):

[## 哈里欧/埃沃-波尼丝

自助项目 Evo Pawness(临时名称)。通过在…上创建一个帐户,为 haryoa/evo-pawness 的发展做出贡献

github.com](https://github.com/haryoa/evo-pawness/blob/master/reinforcement_learning_train/alpha_zero/train_module.py)

就是这样。我们已经为 AlphaZero 定义了所有的组件。现在,我们将连接所有已定义的组件,并执行 AlphaZero 算法。

AlphaZero 的实施

在我们定义了将用于 MCTS 的所有组件之后,让我们来总结一下。

基于我们定义的组件实现 AlphaZero 的步骤如下:

  1. 生成所有独特的行动,可以用于每个球员。
  2. 创建神经网络模型,该模型将用于根据生成的独特行动的输入在 MCTS 中进行评估。模型中的策略头将通过唯一动作的总数来调整其输出形状。
  3. 创建结构数据deque ,该数据将用于保存关于每次自我游戏结果的信息,并将用于神经网络的输入。必须定义deque 的最大长度。
  4. 对于每一集,我们创造新的 MCTS 实例,通过自我表演来训练我们的特工。我们将在下面看到这个步骤的更多细节。
  5. 自弹结束后,根据deque中自弹产生的数据训练神经网络模型。deque 中的实例总数受限于定义的deque的最大长度。
  6. 在我们训练模型之后,我们将检查当前模型是否比当前最佳模型更好。如果是真的,那么把最好的模型换成当前模型。最佳模型将用于下一集的评估。

每集模拟

当该步骤正在进行自我表演时,我们将进行如下步骤:

首先,我们需要初始化游戏状态和 MCTS。然后,执行以下操作:

首先,我们将参数填入 MCTS ( self_play()),然后我们得到根上的动作概率(play())。我们用动作、状态和玩家信息填充deque 。在状态到达终端或最大步骤后,我们最终将奖励信息添加到deque中。

在我们在deque上填充奖励之后,我们将把deque 的内容附加到全局deque 上,这将用于训练神经网络。

self_play 函数是一个 MCTS 模拟器,其工作方式如下:

就是这样,所以 MCTS 的每一次模拟都会模拟到找到叶节点为止。如果已经找到,那么进行扩展和评估。如果没有,进行选择。

就这样,我们已经定义了如何为游戏创建 AlphaZero 实现。

对于实现,您可以在存储库中的 train_module.py 上看到fit_train() 和。

[## 哈里欧/埃沃-波尼丝

自助项目 Evo Pawness(临时名称)。通过在…上创建一个帐户,为 haryoa/evo-pawness 的发展做出贡献

github.com](https://github.com/haryoa/evo-pawness/blob/master/reinforcement_learning_train/alpha_zero/train_module.py)

要运行训练,从一开始就使用main_from_start.py进行训练。而main_from_continue.py要从检查站训练。目前,我建议在我重构和清理代码之前,不要尝试训练模型。我计划下周六做这件事。

下面是main_from_start.py的代码

[## 哈里欧/埃沃-波尼丝

自助项目 Evo Pawness(临时名称)。通过在…上创建一个帐户,为 haryoa/evo-pawness 的发展做出贡献

github.com](https://github.com/haryoa/evo-pawness/blob/master/main_from_start.py)

吸取的教训

在实现 AlphaZero 的过程中,我学到了一些经验。

  1. 我已经运行了代码来训练模型。我明白了,一开始,很难获得一集的输赢。它通常以平局结束(达到最大步数)。我的结论是,我设置的最大模拟是不够的。我需要更高的 MCTS 最大模拟。我也尝试过另一种方式来解决。我试图通过让代理总是攻击敌人来破解 MCTS 的模拟。如果你不清楚 MCTS 中的greed 变量是什么,对于我设置的 1/8 最大集和选择的最小步长,模拟将总是使攻击动作和促进动作在置信上限上比其他动作有更大的 Q。当点蚀两个模型时,此功能将被禁用。我需要找到一个更好的方法来解决这个问题。
  2. 我需要实现模拟 MCTS 多线程。单线程仍然可以使用,但是非常慢。因此,在下一部分,我将尝试实现多线程 MCTS。
  3. 我还没有调整神经网络和 MCTS 的超参数。所以,我还是不知道该用多少层残留。目前,我使用 4 层残留层。我没有调,因为训练很慢😢。
  4. 一定要通读这篇论文。我已经修改了几次代码,因为我跳过了论文中的某些部分。

结论

在本文中,我们已经构建了将用于实现 AlphaZero 的所有组件。我们还实现了 AlphaZero 算法。这篇文章还没有告诉 AlphaZero 的结果,因为训练还没有完成。

本文仍然使用单个 GPU 来训练模型。它还使用单线程来运行 MCTS 的模拟。所以,训练会很慢。

编后记

Exhausted Source : https://pixabay.com/en/cat-cat-face-sleep-exhausted-1551783/

哇,看看我的 25 分钟阅读时间文章😆。最后,文章完成并发表。感谢我读过的几篇文章,让我可以实验 AlphaZero 算法,理解算法。

由于培训尚未完成,我计划创建下一部分,将重点放在改进和实施 AlphaZero 的结果。不幸的是,由于我的资源有限,我担心这个项目会被搁置,直到我得到一台新的电脑来试验它。嗯,你看,我失业了(有几个原因),所以我这里的钱有限😢。

我会试着修复混乱的代码,让它在下周更容易执行。目前,当程序运行时,代码非常混乱,而且非常冗长。

我欢迎任何可以提高我自己和这篇文章的反馈。我正在学习写作和强化学习。我真的需要一个反馈来变得更好。请确保以适当的方式给出反馈😄。

哦,我答应在最后部分告诉你项目结构和 GUI 的细节。抱歉,我忘记写了😅。如果我有时间,我会把它写在我的下一篇文章上。

对于我接下来的几个项目,我将专注于 NLP 或计算机视觉任务。我将写一些关于在这些任务中使用 GAN 的内容。我想学习 GAN 并实现它,因为它是目前深度学习的热门话题。

如果你想从我这里得到另一篇这样的文章,请鼓掌这篇文章👏 👏。这将提升我写下一篇文章的精神。我保证会做出更好的关于 AI 的文章。

在我的下一篇文章中再见!

Source : https://cdn.pixabay.com/photo/2017/07/10/16/07/thank-you-2490552_1280.png

系列文章

第 1 部分:从零开始为你自己的棋盘游戏创造人工智能——准备——第 1 部分

第 2 部分:为你自己的棋盘游戏从头开始创造人工智能——Minimax——第 2 部分

第 3 部分:为你自己的棋盘游戏从头开始创建人工智能

来源和致谢

[## [来源 1]简单阿尔法零

web.stanford.edu](https://web.stanford.edu/~surag/posts/alphazero.html) [## 【来源 2】如何使用 Python 和 Keras 构建自己的 AlphaZero AI

medium.com](https://medium.com/applied-data-science/how-to-build-your-own-alphazero-ai-using-python-and-keras-7f664945c188)

【来源 2】作者大卫·福斯特

【资料来源 3】西尔弗、大卫等《在没有人类知识的情况下掌握围棋游戏》。性质 550.7676 (2017): 354。

[资料来源 4] Silver,David,等人《用一般强化学习算法通过自我游戏掌握国际象棋和日本象棋》 arXiv 预印本 arXiv:1712.01815 (2017)。

感谢 Thomas Simonini 建议我继续将 Deep Q 网络应用于基于政策的学习。

从头开始为你自己的棋盘游戏创造人工智能——Minimax——第 2 部分

原文:https://towardsdatascience.com/create-ai-for-your-own-board-game-from-scratch-minimax-part-2-517e1c1e3362?source=collection_archive---------2-----------------------

用 Minimax 算法实现 AI 到 EvoPawness(暂名),一个来自我的想法的棋盘游戏。

“chess pieces on board” by Felix Mittermeier on Unsplash

嗨,生活怎么样?欢迎来到制作evopheness(暂名)桌游项目系列文章的第二部分。在这里,我们将实现如何将人工智能添加到游戏中。在本文中,我们将重点介绍一些经典算法的实现。是极小极大和阿尔法贝塔修剪极小极大。我们将回顾我们在上一部分所做的,以及我在关于棋盘游戏(GitHub)的代码库中所做的。

[Image 0] Source : https://pixabay.com/en/drop-of-water-water-liquid-1004250/

在我们深入讨论如何实现它之前,我要告诉你,这一系列文章将一步一步地讲述如何创建棋盘游戏。因此,我将尽可能详细地描述我是如何设计和编写游戏、GUI 和 AI 的。我想分享一下我的经验。所以,我会一步一步来,告诉你我创造人工智能的成功和失败的尝试。

我们还没有介绍 GUI。稍后,我将撰写关于如何制作和设计 GUI 的文章。现在,我们将跳过 GUI,先做 AI 部分。

这篇文章的目标读者是对人工智能和设计游戏感兴趣的人。如果你不是其中之一,当然你还是可以看到这篇文章。我希望通过发表这篇文章来提高我的写作技巧,并且内容对你有益😄。

概述

  1. 贮藏室ˌ仓库
  2. 前一部分概述
  3. 评估和效用函数工程
  4. 极大极小算法及其实现
  5. A-B 剪枝极大极小算法及其实现
  6. 结论
  7. 编后记

贮藏室ˌ仓库

这里是仓库

[## 哈里欧/埃沃-波尼丝

在 GitHub 上创建一个帐户,为 haryoa/evo-pawness 的发展做出贡献。

github.com](https://github.com/haryoa/evo-pawness)

我已经对存储库做了一些修改

  • 平衡游戏。我和朋友玩过几次,以前的游戏规则真的很不平衡。
  • 用 PyQt5 库实现图形用户界面
  • 为了这篇文章,改变规则使游戏确定。下一部分会有所改变。
  • 添加极大极小值、AB 修剪极大极小值和随机代理
  • 重新格式化文件夹的结构

如果您想在图形用户界面(GUI)版本中播放它,请在您的命令行界面(CLI)中使用pip install PyQt5命令安装 PyQt5。

前一部分概述

在前一部分中,我们已经定义了 AI 算法要使用的一些组件。我们已经陈述了游戏的规则、状态表示、结果函数、动作、结果函数和最终测试。我们也实施了这些措施。

在本文中,让我们利用之前实现的功能来制作一个代理,它可以通过看到对手可能的动作来选择动作。

评估和效用函数工程

让我们回顾一下,效用函数为玩家定义了游戏在终端状态下的最终数值。数值公式是我们定义的。评估函数为玩家定义了来自给定状态的期望效用数值的估计。这个函数在游戏还没有结束的时候被调用。接收状态和玩家的输入,并输出一个表示玩家状态的结果和合意性的数值分数。负的分数意味着玩家处于劣势地位。

没有任何规则来定义评价函数和效用函数。因此,我们需要考虑如何制定它。该函数应该区分玩家是否处于优势地位。

这部分真的很重要,因为这个功能将定义我们代理人的聪明度

这是我想出的公式:

效用函数

在效用函数中,我们将把效用定义为你想要的高度,并且应该高于我们将在此之后定义的评估函数。

我们在前面已经说过,我们的终态有两个条件:

  1. 当一个国王死了
  2. 当玩家的所有兵都死时

在这里,如果到达终端状态,它将返回-120(负表示不利),否则返回 120。

评价函数

在评估函数中,我们将通过考虑兵的 hp、atk、步数和死亡状态来制定它。每个属性的权重为:

  1. HP 的重量= 0.3
  2. ATK 的重量= 0.1
  3. 台阶重量= 0.1
  4. 死者棋子的重量= 10
  5. 国王生命值的重量= 1

我们给死者的棋子 10 的权重,因为在这个游戏中失去一个棋子是非常糟糕的。因此,我们希望通过给它一个高权重,人工智能将避免其棋子死亡。金在这里真的是一个重要的棋子。所以,我们会给它一个更高的分数。

我们将在调用类State中的评估函数的AIElements.evaluation_function()函数中定义效用和评估函数。

极大极小算法及其实现

最小最大值是一个决策规则,它模拟玩家的决策,以找到玩家的最优移动。它假设对手也会选择最优策略。迷你剧里有两个演员。是最大化器最小化器最大化器将尽可能搜索最高分,而最小化器将尽可能搜索最低分。每个玩家将看到对手的最佳移动,并为他/她得到最好的移动。通常,搜索用结构数据表示。

[Image 0.5]

这个例子说明了极大极小算法

[Image 1] Source: AI: A Modern Approach Book

看图 1,Max 会输出 B,C,D 动作。首先,最小化者会看到导致 B 的可能行动,得到最低分 min(3,12,8) = 3。然后对 C 和 d 也这样做。之后,最大化器将选择最高分。也就是max(3,2,2) = 3

在我们的棋盘游戏中,算法不会扩展状态/节点,直到终止状态。它将有内存问题或需要很长时间来处理。我们应该通过定义限制应该扩展的节点的max_depth来为我们的搜索算法定义一个截止点。它将调用计算条件的启发式的评估函数。

[Image 1.5]

让我们将它实现到我们的evopheness(临时名称):

我们将为 Minimax 算法定义一个类,它接收max_depthplayer_color作为它的输入。player_color 是指出这里谁是最大化者的参数。

让我们看看 Minimax 类:

如你所见,Minimax 代理将使用递归搜索进行深度优先搜索。它将输出分数(best_value)和最佳动作动作键名。如果轮到最大化者,它将得到最大的分数,如果轮到最小化者,它将得到最小的分数。它将递归搜索,直到达到终端状态或最大深度。

我们打乱列表,让代理随机选择得分相同的动作。

如果你想知道action_target的型号,那就是str 型。记住我们的游戏动作是一个dict 类型,它保存了动作信息和动作键名。例如:

‘p1a2’: 
{‘action’: ‘activate’,
 ‘pawn_atk’: 1,
 ‘pawn_hp’: 5,
 ‘pawn_index’: 2,
 ‘pawn_step’: 1,
 ‘pawn_x’: 4,
 ‘pawn_y’: 1,
 ‘player_index’: 1},

测试代理

我们将使用深度= 4 的MinimaxAgent

转到 controller/game_controller.py 中的第 52 行,将其改为

self.ai_agent = MinimaxAgent(max_depth=4, player_color=0)

[Image 2] I exit the game because I cannot stand waiting the AI’s move

用你的命令行界面运行游戏

python main_gui.py

人工智能会优先到达符文。然后,激活它的棋子。然后先攻击我们的兵。如果可能的话,人工智能会把它的兵进化成更高级的类型。当然也有一些瑕疵,比如让我们的士兵攻击他们的国王。也许有更好的评价函数可以让 AI 保护国王。

如果你尝试过人工智能,你会发现选择动作所消耗的时间非常长。让我们看看:

从上面我们可以看到,随着游戏的进行,expanded_node 也在增加,因为可能的动作数量。随着扩展节点的增加,算法的时间往往会增加。我们可以看到 24 号弯的时间是 21 秒。谁想等那么久?

这是该算法的弱点,因为它将搜索所有可能的状态,直到期望的截止或终止状态。幸运的是,我们可以有效地切割扩展的节点。我们将使用一种叫做修剪的技术来计算正确的极大极小决策,而不需要查看每个节点这叫做阿尔法-贝塔修剪。

阿尔法贝塔剪枝

阿尔法贝塔剪枝是一种极大极小算法的优化技术。这将删除一些不应该扩展的节点,因为已经找到了更好的移动。当应用于极大极小算法时,它将返回与极大极小算法相同的动作,但速度更快。它被称为阿尔法贝塔修剪,因为它需要两个新的参数称为阿尔法和贝塔。

Alpha:最大化者在当前状态或最大化者回合之前可以保证的最佳值

Beta:最小化者在当前状态或最小化者回合之前可以保证的最佳值

总的原则是:给定一个状态 s 在树的某处。如果玩家可以选择在找到 s 之前做出动作 m ,那么 s 将永远不会到达。

让我们以图片 1 为例:

[Image 3] AB Pruning Source: AI: A Modern Approach Book

记住搜索算法使用的是 DFS(深度优先搜索)!

让我们一步一步来:

  1. 将 Alpha 初始化为-inf,将 Beta 初始化为 inf。如果阿尔法在最大化回合,它将被更新;如果它在最小化回合,它将被更新。它们将被传递给状态/节点的子节点。
  2. 展开一个(初始状态)可能的动作。它会产生 B,C,D 状态。从第一个索引(B)开始
  3. 轮到迷你 B 了。展开 B 可能的行动,进入孩子并检查其分数。尽可能少地更新测试版。我们会发现 Beta = 3 = min(3,12,8,inf)。α仍然是-inf。
  4. 假设深度为 2,将最小分数(也就是 3)返回给 A,现在回到 Maximizer turn A ,更新 Alpha = 3 = max(-inf,3)。然后我们转到 C 状态/节点,第二个子节点:
  5. 现在转到状态 C,最小化回合 C 带有传递的阿尔法参数 3。在 C 中,我们发现 C 找到的第一个孩子的分数是 2。更新β= 2 = min(INF,2)。现在,让我们想想。最大化器会选择这个动作吗?最大化者已经找到了更好的移动,得分为 3。当然不是,如果是最优代理的话。所以,不要在 c 中检查另一个可能的动作,这是修剪过程。返回最好的分数(是 2)。如果β≤α那么就把它修剪掉。
  6. Maximizer Turn A :计算 max(alpha,2) = alpha。检查最后一个孩子 c。
  7. 极小化回合 C :计算 min(inf,14),将 beta 更新为 14,检查 beta 是否≤ alpha (14 ≤ 3)且未修剪。计算下一个孩子。β≤α(5≤3)且不修剪。最后,检查β≤α(2≤3)是否应该修剪。因为在这个节点之后没有任何子节点。没有可以修剪的节点。如果首先找到返回 2 的子节点,就可以删除它。在这之后,返回 2 到 a。
  8. Maximizer 转 A,计算 max(alpha,2) = alpha。然后就搞定了。返回 3 作为在树中找到的最佳分数。

将其实现到evopowness(临时名称)中。

这是用于 Minimax Alpha Beta 修剪的类:

(注:self.node_expanded用于调试目的)

我们添加 alpha 和 beta 作为参数,默认为-infinf。然后我们添加这些行:

用于最大化器

alpha = max(alpha, best_value)                                       
if beta <= alpha:                                           
break

针对最小化器

beta = min(beta, best_value)                                       
if beta <= alpha:                                           
break

就这些,我们来测试一下😃!

测试代理

我们将玩深度= 4 的MinimaxABAgent

转到 controller/game_controller.py 中的第 52 行,将其改为

self.ai_agent = MinimaxABAgent(max_depth=4, player_color=0)

[Image 4] : I lost to the AI 😢

其行为与 minimax 相同,但速度更快。让我们看看运行时间,直到 24 转。

我们可以看到,在不使用剪枝技术的情况下,花费的时间更快。直到最后,我发现搜索最佳动作的最差运行时间是 17s,有 2218 个扩展节点(66 转)。它的速度更快,因为修剪删除不必要的节点。

继续挑战人工智能。人工智能非常具有挑战性。有时它会在开始时瞄准国王。我输给人工智能一次是因为对国王的突然袭击,我没有准备好保护国王😢。

尽管如此,它是可以赢的。人工智能不为国王辩护。我认为它需要更多的深度来了解它。它将花费更长的运行时间。嗯,没有人会想测试他/她的耐心,玩一个需要太长时间的人工智能(你想等一分钟或更长时间才能轮到它?).

[Image 5] Source : https://pixabay.com/en/robot-future-modern-technology-1658023/

Minimax 也不考虑除了在所选深度中发现的状态之外的状态。极大极小只关心在其中找到的状态。不可能从开始就搜索所有节点到终端状态,因为这会导致内存错误。计算量大(O(action^depth))这是极小极大的弱点,即使使用剪枝技术进行了改进。

我们需要一些考虑状态而不是选择深度的技术,也可以让 AI 更快地选择动作。我们将在下一部分看到它😄。

结论

我们已经创建了 Minimax 算法使用的效用和评估函数。虽然性能不错,但是极大极小算法就是这么慢。为了弥补这个缺陷,我们对算法进行了修剪。这叫做阿尔法贝塔剪枝。动作的模式是相同的,并且不使用修剪会更快。即便如此,极大极小阿尔法贝塔剪枝也有它的缺陷。它只能考虑和观察 n 向前移动,即 n 需要变小。如果 n 很大,那么选择一个动作在计算上将是昂贵的。我们需要一些算法,考虑到进一步的转折,需要更快。

编后记

感谢您阅读我关于人工智能的第三篇文章。我需要一些建设性的反馈,以提高我的写作水平和人工智能知识。请手下留情😆!

我只是想把我的知识分享给读者。分享知识很好,因为它可以帮助有需要的人。我正在学习这个领域,想分享我所学到的东西。如果有人告诉我这篇文章有什么问题,那就太好了。

很抱歉在图形用户界面上的糟糕用户体验(UX)😞。它在代码中也有混乱和不够的文档。我希望我有时间修理它。也许有人能帮我修好它?

下一部分(第 3 部分)将进入强化学习。我们将逐一探讨各种强化学习。在此之前,我将创建一篇关于我如何设计 GUI 的文章,并展示这个项目中的类的结构。这将是 1.5 部分。希望我能在这个星期天发表文章。

我们将在强化学习中进行实验。恐怕要花很多时间来试验它。我想,两周是我能发表文章的最小间隔。有人想和我一起做实验吗?给我精神去写下一部分✌️.

如果你想从我这里得到另一篇这样的文章,请拍下这篇文章👏 👏。这将鼓舞我写下一篇文章。我保证会写出更好的文章。

下一篇文章再见!

“flat lay photography of coffee latte in teacup on table” by Hanny Naibaho on Unsplash

系列文章

第 1 部分:从头开始为你自己的棋盘游戏创造人工智能——准备——第 1 部分

第 2 部分:从零开始为你自己的棋盘游戏创造 AI——Minimax——第 2 部分

第 3 部分:从头开始为你自己的棋盘游戏创造 AI——alpha zero——第 3 部分

来源

拉塞尔,斯图尔特 j,和彼得诺维格。人工智能:现代方法。普伦蒂斯霍尔出版社,2010 年。

[## 博弈论中的极大极小算法|第一集(简介)- GeeksforGeeks

极客的计算机科学门户。它包含写得很好,很好的思想和很好的解释计算机科学和…

www.geeksforgeeks.org](https://www.geeksforgeeks.org/minimax-algorithm-in-game-theory-set-1-introduction/)

用 Python 和 Azure DSVM 创建 Azure 机器学习 Web 服务

原文:https://towardsdatascience.com/create-an-azure-machine-learning-web-service-with-python-and-azure-dsvm-f7a16a925c4b?source=collection_archive---------2-----------------------

上周,当我在阅读关于微软 CNTK 的文档,并从他们的 GitHub 库中检查代码样本时,我碰到了另一个 GitHub 库,它是关于 Azure 机器学习操作化。

Azure Machine Learning operational ization 是 Azure CLI 的一个组件,它支持您通过 Vienna 使用 CNTK、SPARK 和 Python 机器学习平台创建的模型的可操作性。

我最近一直在玩 Python 和机器学习,所以我想我应该试试这个,并使用用 Python 构建的机器学习模型创建一个 AzureML Web 服务。

重要提示:撰写本文时,Azure 机器学习操作化仍处于预览阶段。未来可能会发生一些突破性的变化。

入门指南

要开始 Azure 机器学习操作化,您需要以下内容:

  1. Azure 账户(如果你没有,你可以在这里获得免费试用
  2. azure Data Science Virtual Machine(DSVM)——虽然这并不是必须的,但我认为这是最快最简单的入门方式。你可以点击查看文档来创建一个。

更新(实际上是重新安装)Azure CLI 包

一旦你创建了 Azure DSVM,我们要做的第一件事就是通过卸载和安装来更新我们的azure-cliazure-cli-mlazure-ml-api-sdk包。

$ pip uninstall azure-cli azure-cli-ml azure-ml-api-sdk
$ pip install azure-cli azure-cli-ml azure-ml-api-sdk

以前,我尝试用pip install中的--upgrade选项更新软件包,但我一直在升级软件包时出错。因此,卸载并安装它可以解决问题,因为稍后,我们将使用这些包来生成一个模式,并将我们训练好的模型部署到我们的 web 服务中。

构建性别分类器模型

接下来,我们将使用scikit-learn机器学习库创建一个模型,该模型将根据给定的身体指标(身高、宽度和鞋码)来预测这个人是男是女。创建一个名为train.py的文件,您可以复制下面的代码:

运行代码,它会在你的项目文件夹中创建一个model.pkl

创建 score.py

要将我们的模型部署为 web 服务,我们需要创建一个用于生成模式 JSON 文件的score.py

运行score.py,您应该会看到service_schema.json已经创建。现在,我们的项目文件夹应该包含以下文件:

PROJECT_FOLDER
- train.py
- score.py
- model.pkl
- service_schema.json

使用 Azure CLI 创建和部署 Web 服务

现在我们已经设置好了一切,我们现在将通过执行以下操作来创建我们的 web 服务并部署模型:

在 Azure CLI 中验证您的帐户

为了能够使用azure cli创建我们的环境模型管理帐户,我们首先需要使用以下命令进行认证:

$ az login

这将显示一条信息,提示您需要打开网络浏览器,进入https://aka.ms/devicelogin并输入终端中提供的代码。

创造环境

一旦您能够使用azure cli验证您的帐户,键入以下命令来创建我们的环境:

$  az ml env setup -c -n YOUR_CLUSTER_NAME -l YOUR_LOCATION

这是我在本教程中使用的:

$ az ml env setup -c -n azmldemo -l australiaeast

记住现在支持的位置是westcentraluseastus2australiaeast也很重要。

创建模型管理帐户

我们的环境至少需要 10-20 分钟才能完成配置,因此在等待的同时,我们现在可以创建我们的模型管理帐户,这是一个一次性设置的事情。

$ az ml account modelmanagement create -l YOUR_LOCATION -n YOUR_MODEL_MANAGEMENT_ACCOUNT_NAME -g YOUR_RESOURCE_GROUP

这是我在本教程中使用的:

$ az ml account modelmanagement create -l australiaeast -n azmldemoacc -g azmldemorg

如果您想知道,azmldemorg资源组是在我们之前创建环境时自动创建的,它基本上是在我们的集群名称azmldemo上添加了一个 rg

设置模型管理帐户和环境

一旦成功创建了模型管理帐户和您的环境,我们现在将使用以下命令设置模型管理帐户和环境:

$ az ml account modelmanagement set -n YOUR_MODEL_MANAGEMENT_ACCOUNT_NAME -g YOUR_RESOURCE_GROUP$ az ml env set -n YOUR_CLUSTER_NAME -g YOUR_RESOURCE_GROUP

这是我在本教程中使用的:

$ az ml account modelmanagement set -n azmldemoacc -g azmldemorg
$ az ml env set -n azmldemo -g azmldemorg

同样重要的是要记住,您的环境供应状态应该显示成功,以便您能够成功设置它。您可以使用以下命令检查资源调配状态:

$ az ml env show -g YOUR_RESOURCE_GROUP -n YOUR_CLUSTER_NAME

创建 Web 服务

一旦您能够成功设置您的模型管理帐户和环境,我们现在将通过以下方式创建 web 服务:

  1. 转到我们已经创建了性别分类器模型和 JSON 模式的项目文件夹
  2. 键入以下命令创建 web 服务:
$ az ml service create realtime -f score.py --m model.pkl -s service_schema.json -n genderclassifier -r pythonor you can also use --help or -h to know the other arguments available$ az ml service create realtime -h

测试 Web 服务

一旦成功地创建了 web 服务,您就可以使用下面的命令来获取可以用来测试 web 服务的所有重要细节(像示例 CLI 命令、Swagger URL、授权载体密钥等等):

$ az ml service usage realtime -i YOUR_SERVICE_ID

您可以使用以下命令获得您的服务 Id :

$ az ml service list realtime

运行示例 CLI 命令

$ az ml service run realtime -i YOUR_SERVICE_ID -d "{\"input_df\": [{\"width\": 60, \"shoe_size\": 38, \"height\": 190}]}"

当您在终端中运行时,应该是这样的:

通过邮递员测试

这里有一个关于如何通过Scoring URL测试 web 服务的例子

我们已经完成了,那么你对 Azure 机器学习的可操作性有什么看法?同样,您可以在这里查看他们的 GitHub 库中的其他示例。

创建具有业务影响的更好的数据科学项目:使用 R

原文:https://towardsdatascience.com/create-better-data-science-projects-with-business-impact-churn-prediction-with-r-f609c23a6287?source=collection_archive---------9-----------------------

找份工作并不容易,你需要让自己与众不同。

仅仅展示你知道如何使用像 scikit-learnggplot2 这样的工具是不够的,这是已知的。

所以问题是,你能做什么?

我最喜欢的策略之一是展示商业影响

我所说的“业务影响”是什么意思

在一天结束时,你将直接或间接地从事与业务相关的工作。

那可能是降低成本增加收入改善客户体验等等。

在这篇文章中,我将一步一步地向你展示如何建立一个客户流失预测模型来显示重大的商业影响

这里有一个简单的概述:

  • 工作经历
  • 逻辑回归
  • 准备数据
  • 拟合模型
  • 做预测
  • 业务影响

工作经历

在任何现实世界的数据科学项目开始时,你都需要问一系列问题。

这里有几个好的开始:

  • 你想解决什么问题?
  • 你的潜在解决方案是什么?
  • 你将如何评价你的模型?

继续前面的客户流失想法,让我们假设你在电信行业工作。

有一天,你的老板找到你,问你,“我们如何利用现有的数据来改善业务?”

让我们来看一下如何详细回答每个问题,以提出解决老板问题的战略方法。

你想解决什么问题?

在查看了现有数据后,您发现获得新客户的成本大约是留住现有客户的五倍。

现在,更集中的问题变成了,“我们如何提高客户保持率以降低成本?”

你的潜在解决方案是什么?

由于我们可以访问现有的客户数据,我们可以尝试建立一个机器学习模型来预测客户流失。

为了避免过于抽象而无法向管理层解释的事情变得过于复杂,我们将使用逻辑回归模型。

你将如何评价你的模型?

我们将使用一系列机器学习评估指标 ( ROC ,AUC,灵敏度,特异性)以及业务导向指标(成本节约)。

逻辑回归

既然我们已经熟悉了业务背景,并且已经确定了问题的范围,那么让我们来看看我们潜在的解决方案。

我们可以尝试使用许多机器学习模型。都有利弊。

为了使事情简单,让我们只坚持逻辑回归。

逻辑回归是一个线性分类器。由于我们试图预测“流失”或“没有流失”,分类模型正是我们所需要的。

这是一个很棒的模型,因为它比许多其他模型更容易解释,比如随机森林。我所说的“解释”是指我们可以更容易地看到特征和输出之间的关系。

逻辑回归的缺点是它有一个偏向线性拟合的偏差。如果决策边界不是线性的,它的性能可能不如随机森林这样的模型。

基本上,我们是在用灵活性来换取可解释性。在实现机器学习模型时,这始终是一个需要考虑的问题。

准备数据

下一步是准备数据。

此工作流程因项目而异,但对于我们的示例,我将使用以下工作流程:

  1. 导入数据
  2. 快速看一眼
  3. 清理数据
  4. 拆分数据

我在工作流程中省略了一个完整的探索阶段。在以后的文章中,我会详细介绍这一点,因为我认为这与建模阶段同等重要,甚至更重要。

1)导入数据

让我们首先导入数据。你可以到我们的 github 页面去拿一份。

我们将利用 Tidyverse 库。这是使用 r 的数据科学家的必备库,请务必查看他们的文档。

library(tidyverse)

# setting the working directory
path_loc <- "C:/Users/Jonathan/Desktop/post"
setwd(path_loc)

# reading in the data
df <- read_csv("Telco Data.csv")

我喜欢在开始时设置工作目录的路径。确保将您的path_loc变量修改到您的代码和数据集所在的工作目录。

由于这是一个 csv 文件,我使用了read_csv函数将数据读入数据帧df

2)快速浏览一下

导入数据后,您可能想快速浏览一下。我认为“快速浏览”就是这样,而不是探索数据。

我喜欢从查看特性列的维度和名称开始。

# dimensions of the data
dim_desc(df)

# names of the data
names(df)> # dimensions of the data
> dim_desc(df)
[1] "[7,043 x 21]"
> 
> # names of the data
> names(df)
 [1] "customerID"       "gender"           "SeniorCitizen"    "Partner"          "Dependents"       "tenure"          
 [7] "PhoneService"     "MultipleLines"    "InternetService"  "OnlineSecurity"   "OnlineBackup"     "DeviceProtection"
[13] "TechSupport"      "StreamingTV"      "StreamingMovies"  "Contract"         "PaperlessBilling" "PaymentMethod"

我们可以看到有 21 个特征和 7,043 行。有各种各样的功能,如TotalChargestenure 。我们将要尝试和预测的输出是“流失”。

另一个很棒的功能是glimpse。这使我们能够更详细地快速查看我们的功能。

# taking a look at the data
glimpse(df)> glimpse(df)
Observations: 7,043
Variables: 21
$ customerID        "7590-VHVEG", "5575-GNVDE", "3668-QPYBK", "7795-CFOCW", "9237-HQITU", "9305-CDSKC", "1452-K...
$ gender            "Female", "Male", "Male", "Male", "Female", "Female", "Male", "Female", "Female", "Male", "...
$ SeniorCitizen     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1...
$ Partner           "Yes", "No", "No", "No", "No", "No", "No", "No", "Yes", "No", "Yes", "No", "Yes", "No", "No...
$ Dependents        "No", "No", "No", "No", "No", "No", "Yes", "No", "No", "Yes", "Yes", "No", "No", "No", "No"...
$ tenure            1, 34, 2, 45, 2, 8, 22, 10, 28, 62, 13, 16, 58, 49, 25, 69, 52, 71, 10, 21, 1, 12, 1, 58, 4...
$ PhoneService      "No", "Yes", "Yes", "No", "Yes", "Yes", "Yes", "No", "Yes", "Yes", "Yes", "Yes", "Yes", "Ye...
$ MultipleLines     "No phone service", "No", "No", "No phone service", "No", "Yes", "Yes", "No phone service",...
$ InternetService   "DSL", "DSL", "DSL", "DSL", "Fiber optic", "Fiber optic", "Fiber optic", "DSL", "Fiber opti...
$ OnlineSecurity    "No", "Yes", "Yes", "Yes", "No", "No", "No", "Yes", "No", "Yes", "Yes", "No internet servic...
$ OnlineBackup      "Yes", "No", "Yes", "No", "No", "No", "Yes", "No", "No", "Yes", "No", "No internet service"...
$ DeviceProtection  "No", "Yes", "No", "Yes", "No", "Yes", "No", "No", "Yes", "No", "No", "No internet service"...
$ TechSupport       "No", "No", "No", "Yes", "No", "No", "No", "No", "Yes", "No", "No", "No internet service", ...
$ StreamingTV       "No", "No", "No", "No", "No", "Yes", "Yes", "No", "Yes", "No", "No", "No internet service",...
$ StreamingMovies   "No", "No", "No", "No", "No", "Yes", "No", "No", "Yes", "No", "No", "No internet service", ...
$ Contract          "Month-to-month", "One year", "Month-to-month", "One year", "Month-to-month", "Month-to-mon...
$ PaperlessBilling  "Yes", "No", "Yes", "No", "Yes", "Yes", "Yes", "No", "Yes", "No", "Yes", "No", "No", "Yes",...
$ PaymentMethod     "Electronic check", "Mailed check", "Mailed check", "Bank transfer (automatic)", "Electroni...
$ MonthlyCharges    29.85, 56.95, 53.85, 42.30, 70.70, 99.65, 89.10, 29.75, 104.80, 56.15, 49.95, 18.95, 100.35...
$ TotalCharges      29.85, 1889.50, 108.15, 1840.75, 151.65, 820.50, 1949.40, 301.90, 3046.05, 3487.95, 587.45,...
$ Churn             "No", "No", "Yes", "No", "Yes", "Yes", "No", "No", "Yes", "No", "No", "No", "No", "Yes", "N...

3)清理数据

在开始使用逻辑回归模型之前,我们需要做一些小的清理。

让我们从将字符变量突变为因子开始。

# changing character variables to factors
df <- df %>% mutate_if(is.character, as.factor)

在这段代码中,我们使用 dplyr 的mutate_if函数将字符变量转换为因子类型。

%>%被称为管道操作员。它来自马格里特图书馆,也是 Tidyverse 的一部分。

这个操作符的基本思想是使你的代码更具可读性。你可以在这里了解更多关于管道运营商的信息。

让我们也将SeniorCitizen变量从一个整数改为一个因子。

# changing SeniorCitizen variable to factor
df$SeniorCitizen <- as.factor(df$SeniorCitizen)

接下来,我们将检查丢失的值。我们可以使用来自 purrr 库中的map函数来完成这项工作。如果你还没有猜到,这个库也是 Tidyverse 的一部分。

# looking for missing values
df %>% map(~ sum(is.na(.)))> df %>% map(~ sum(is.na(.)))
$`customerID`
[1] 0

$gender
[1] 0

$SeniorCitizen
[1] 0

$Partner
[1] 0

$Dependents
[1] 0

$tenure
[1] 0

$PhoneService
[1] 0

$MultipleLines
[1] 0

$InternetService
[1] 0

$OnlineSecurity
[1] 0

$OnlineBackup
[1] 0

$DeviceProtection
[1] 0

$TechSupport
[1] 0

$StreamingTV
[1] 0

$StreamingMovies
[1] 0

$Contract
[1] 0

$PaperlessBilling
[1] 0

$PaymentMethod
[1] 0

$MonthlyCharges
[1] 0

$TotalCharges
[1] 11

$Churn
[1] 0

我们可以看到TotalCharges有 11 个缺失值。为了替换这些丢失的值,我们只需要用中间值做一个简单的替换。

# imputing with the median
df <- df %>% 
  mutate(TotalCharges = replace(TotalCharges,
                                is.na(TotalCharges),
                                median(TotalCharges, na.rm = T)))

对于替换缺失值的更严格的技术方法,请查看来源。

我们要做的最后一件事是移除CustomerID特性。这是每个客户的唯一标识符,所以它可能不会给我们的模型添加任何有用的信息。

# removing customerID; doesn't add any value to the model
df <- df %>% select(-customerID)

如果你想测试这个,你可以一直保持这个特性,看看结果如何比较。

4)拆分数据

为了确保我们没有过度拟合我们的模型,我们将把数据分成训练集和测试集。这被称为交叉验证。

我们将在训练集上训练模型,然后在测试集上测试它的性能。我们将随机选择 75%的数据作为我们的训练集,25%作为我们的测试集。我鼓励你尝试不同的分割,看看你的结果如何比较(也许你的训练/测试集是 80%/20%和 60%/40%)。

为了创建分割,我们将使用插入符号包。让我们从导入 Caret 并设置种子开始,这样我们的结果是可重复的。

library(caret)

# selecting random seed to reproduce results
set.seed(5)

接下来,我们将使用createDataPartition函数选择 75%的数据用于训练集。这将随机选择 75%的行。

# sampling 75% of the rows
inTrain <- createDataPartition(y = df$Churn, p=0.75, list=FALSE)

最后,我们将使用上面的行样本创建我们的训练和测试数据帧。

# train/test split; 75%/25%
train <- df[inTrain,]
test <- df[-inTrain,]

还有其他方法,如 k 倍交叉验证,所以一定要仔细阅读这些方法。

有关如何在 Caret 中实现 k-fold 交叉验证的信息,请在 R 控制台中键入help(“createFolds”)

拟合模型

既然我们已经将数据分成了训练集和测试集,现在是时候拟合模型了。

为了实现逻辑回归模型,我们将使用广义线性模型 (GLM)函数glm

有不同类型的 GLMs ,其中包括逻辑回归。为了指定我们想要执行二元逻辑回归,我们将使用参数family=binomial

下面是拟合逻辑回归模型的完整代码:

# fitting the model
fit <- glm(Churn~., data=train, family=binomial)

在下一节中,我们将进行预测并评估我们的模型。

做预测

现在我们已经拟合了我们的模型,是时候看看它的表现了。

为此,我们将使用test数据集进行预测。我们将传递上一节中的fit模型。为了预测概率,我们将指定type=”response”

# making predictions
churn.probs <- predict(fit, test, type="response")
head(churn.probs)> head(churn.probs)
         1          2          3          4          5          6 
0.32756804 0.77302887 0.56592677 0.20112771 0.05152568 0.15085976

由于我们预测的Churn 变量是“是”或“否”,所以我们希望将这些概率转换成二元响应。

我不确定 R 是如何编码响应的,但是我可以使用contrasts函数快速检查这一点。

# Looking at the response encoding
contrasts(df$Churn)> contrasts(df$Churn)
    Yes
No    0
Yes   1

查看结果,我们可以看到Yes正在使用1进行编码。

现在我们知道了响应编码,我们可以将预测结果转换成YesNo响应。

我们将响应阈值设置为0.5,因此如果预测概率高于0.5,我们将把这个响应转换为Yes

最后一步是将角色反应转换成因素类型。这是为了使逻辑回归模型的编码是正确的。

# converting probabilities to "Yes" and "No" 
glm.pred = rep("No", length(churn.probs))
glm.pred[churn.probs > 0.5] = "Yes"
glm.pred <- as.factor(glm.pred)

稍后我们将进一步了解阈值,所以不要担心我们为什么将它设置为0.5。现在,我们将在这个例子中使用这个值。

进行预测的一个重要部分是评估和验证模型。

让我们详细看看一些评估指标,并以一种更严格的模型验证方法结束, k 倍交叉验证。

评估模型

在我们做出预测之后,是时候评估我们的模型了。

快速做到这一点的一个很好的工具是使用 Caret 中的confusionMatrix函数。

我们将输入我们的glm.pred预测数组,以及来自test$Churn的实际结果。

最后,我们将使用positive=”Yes”将正类指定为“Yes”。

# creating a confusion matrix
confusionMatrix(glm.pred, test$Churn, positive = "Yes")> confusionMatrix(glm.pred, test$Churn, positive = "Yes")
Confusion Matrix and Statistics

          Reference
Prediction   No  Yes
       No  1165  205
       Yes  128  262

               Accuracy : 0.8108          
                 95% CI : (0.7917, 0.8288)
    No Information Rate : 0.7347          
    P-Value [Acc > NIR] : 4.239e-14       

                  Kappa : 0.4877          
 Mcnemar's Test P-Value : 3.117e-05       

            Sensitivity : 0.5610          
            Specificity : 0.9010          
         Pos Pred Value : 0.6718          
         Neg Pred Value : 0.8504          
             Prevalence : 0.2653          
         Detection Rate : 0.1489          
   Detection Prevalence : 0.2216          
      Balanced Accuracy : 0.7310          

       'Positive' Class : Yes

这个函数产生一个混淆矩阵,以及其他感兴趣的统计数据。

混淆矩阵显示了每个类别中有多少正确和不正确的预测。

下面快速浏览一下我们模型中的混淆矩阵:

我们可以看到,该模型正确预测了 1165 次“否”,而在实际回答为“是”的情况下,错误预测了 205 次“否”。

同样,当实际答案是“是”的时候,该模型正确地预测了“是”262 次。与此同时,它错误地预测了“是”,而实际的回答是“否”128 次。

我们的总体准确率是 81%。一个简单的基线模型是预测多数类,在我们的例子中是“否”。

如果我们只是预测多数类,我们的准确率将是 73%。测试集中有 1760 个观察值,1293 个是“否”。如果你用 1293 除以 1760,你就可以得到 73%的准确率。

其他一些有用的指标是 灵敏度特异性 。由于我们的类别略有不平衡(约 73%=“否”,约 27%=“是”),这些指标可能更有用。

灵敏度表示“真阳性”率。这是对我们预测正类的准确程度的度量,在我们的例子中是“是”。

confusionMatrix函数直接报告这个,但是如果你想自己计算,用“真阳性”除以“真阳性”和“假阴性”之和。这里有一个形象化的例子来说明这一点:

另一个有用的指标是特异性,它表示“真阴性”率。这是对我们预测负类的准确度的一种衡量。这里有一个解释特异性的图像:

另一个有用的指标是受试者工作特征曲线下的面积,也称为 AUC。

ROC 最初在第二次世界大战期间用于分析雷达信号,它是一张真阳性率与假阳性率的曲线图。

回到我们最初的模型,我声明我将使用 0.5 作为做出“是”(或积极)预测的阈值。不过,我并没有很好的理由选择 0.5。

ROC 是一个很好的工具,因为它绘制了阈值变化时 TPR 与 FPR 的关系。我们可以使用 ROCR 库绘制 ROC 曲线。以下是完整的 R 代码:

library(ROCR)
# need to create prediction object from ROCR
pr <- prediction(churn.probs, test$Churn)

# plotting ROC curve
prf <- performance(pr, measure = "tpr", x.measure = "fpr")
plot(prf)

如前所述,另一个有用的指标是 ROC 曲线下的面积,称为 AUC 。

AUC 可以取 0 到 1 之间的任何值,1 为最佳值。这是一种将 ROC 归结为一个数字来评估模型的简便方法。下面是评估我们模型的 R 代码:

# AUC value
auc <- performance(pr, measure = "auc")
auc <- auc@y.values[[1]]
auc> auc
[1] 0.8481338

我们的模型的 AUC 为 0.85,相当不错。如果我们只是随机猜测,我们的 ROC 将是一条 45 度线。这相当于 AUC 为 0.5。

我们胜过随机猜测,所以我们知道我们的模型至少增加了一些价值!

k 倍交叉验证

既然我们已经训练、测试和评估了我们的模型,让我们更严格地评估模型。

我们将数据集分为训练数据集和测试数据集,这是一种防止过度拟合的好方法。

一个更好的方法是使用 K 倍交叉验证。

在这种模型验证技术中,我们通过指定一定数量的“折叠”将数据随机划分为测试集和训练集。在上面的例子中,折叠的数量是 k=4。

在我们对每个折叠运行模型后,我们对每个折叠的评估指标进行平均。因此,如果我们使用 ROC 运行模型四次,我们将对四个 ROC 值进行平均。这是尝试和防止过度拟合模型的好方法。

常用的折叠数是 10,所以我们将在数据中使用 10。我们还将重复这个过程 3 次,只是为了给我们的方法增加一点技术上的严谨性。以下是完整的 R 代码:

# setting a seed for reproduceability
set.seed(10)

# changing the positive class to "Yes"
df$Churn <- as.character(df$Churn)
df$Churn[df$Churn == "No"] <- "Y"
df$Churn[df$Churn == "Yes"] <- "N"
df$Churn[df$Churn == "Y"] <- "Yes"
df$Churn[df$Churn == "N"] <- "No"

# train control
fitControl <- trainControl(## 10-fold CV
  method = "repeatedcv",
  number = 10,
  ## repeated 3 times
  repeats = 3,
  classProbs = TRUE,
  summaryFunction = twoClassSummary)

# logistic regression model
logreg <- train(Churn ~., df,
                method = "glm",
                family = "binomial",
                trControl = fitControl,
                metric = "ROC")
logreg> logreg
Generalized Linear Model 

7043 samples
  19 predictor
   2 classes: 'No', 'Yes' 

No pre-processing
Resampling: Cross-Validated (10 fold, repeated 3 times) 
Summary of sample sizes: 6338, 6339, 6338, 6339, 6339, 6339, ... 
Resampling results:

  ROC        Sens       Spec   
  0.8455546  0.5500297  0.89602

我们从使用trainControl功能设置 k 倍 CV 开始。所有的输入都非常简单。正如我之前提到的,我们使用 10 次折叠,重复 3 次。

接下来我们训练我们的模型。正如我们前面所做的,我们使用“glm”方法中的“二项式”族。为了评估我们的模型,我们将使用“ROC”。该模型实际上是报告 AUC,但我们在train函数中指定的方式是用metric=”ROC”

您还会注意到,我在trainControl函数的代码前将正类更改为“Yes”。我这样做是为了将敏感性和特异性与我们以前的结果进行比较。这只是函数中的一个小问题,所以不要纠结于此。

结果与我们以前得到的相似。

和之前一样,我们的 AUC 是 0.85 。这在输出中报告为 ROC,但实际上是 AUC。

真阳性率(敏感性)为 0.55 ,而真阴性率(特异性)为 0.90

业务影响

到目前为止,我们已经使用了 k 倍交叉验证逻辑回归来预测客户流失。

我们已经查看了有用的评估指标,如 AUC灵敏度特异性

这很好,但现在怎么办?

如果我们去见首席执行官并单独展示这些结果,他或她会说“那又怎么样?”

我们开发该模型的实际目标是展示业务影响。在我们的案例中,这将是成本节约

让我一步一步地介绍一下我们如何节约成本。

首先,让我们对各种成本做一些假设。

我们假设电信行业的客户获取成本约为300 美元。如果我们预测某个客户不会流失,但实际上他们会流失(假阴性,FN),那么我们就必须出去花 300 美元为该客户找一个替代品。

现在让我们假设获得一个新客户比留住一个现有客户要贵 5 倍。如果我们预测某个客户会流失,我们需要花费60 美元来留住那个客户

有时我们会正确地预测客户会流失(真阳性,TP),有时我们会错误地预测客户会流失(假阳性,FP)。在这两种情况下,我们将花费 60 美元来留住客户。

最后,在这种情况下,我们正确地预测客户不会流失(真阴性,TN)。在这种情况下,我们不会花钱。这些是快乐的顾客,我们正确地将他们定义为快乐的顾客。

这里是成本的快速汇总:

  • FN (预测客户不会流失,但他们确实流失了): $300
  • TP (预测客户会流失,而实际上他们会流失): $60
  • FP (预测客户会流失,而实际上他们不会): $60
  • TN (预测客户不会流失,而实际上他们不会): $0

如果我们将每种预测类型(FN、TP、FP 和 TN)的数量乘以与每种类型相关联的成本,并将它们相加,那么我们可以计算出与我们的模型相关联的总成本。这个等式是这样的:

成本= FN($ 300)+TP($ 60)+FP($ 60)+TN($ 0)

作为一个例子,让我们假设我们对每一个做出如下数量的预测:

  • FN = 10
  • TP = 5
  • FP = 5
  • TN = 5

那么我们的总成本将是 3600 美元。

10 *(300 美元)+5 *(60 美元)+5 *(60 美元)+5 *(0 美元)= $3600

现在让我们将这个成本评估应用到我们的模型中。

我们将从拟合模型开始,并以概率的形式进行预测。

# fitting the logistic regression model
fit <- glm(Churn~., data=train, family=binomial)

# making predictions
churn.probs <- predict(fit, test, type="response")

接下来,让我们创建一个阈值向量和一个成本向量。

# threshold vector
thresh <- seq(0.1,1.0, length = 10)

#cost vector
cost = rep(0,length(thresh))

阈值向量将保存每个模型的阈值。之前,我们只是使用 0.5 作为阈值,但让我们看看阈值在 0 和 1 之间的增量为 0.1(例如,0.1、0.2、0.3…0.9、1.0)。

成本向量将是长度为 10 的向量,以零开始。我们将在循环函数时填充它,并在循环过程中评估每个阈值。为了评估成本,我们将使用刚刚讨论过的相同成本等式。

现在,我们将创建一个 for 循环,使用各种阈值进行预测,并评估每个阈值的成本。

# cost as a function of threshold
for (i in 1:length(thresh)){

  glm.pred = rep("No", length(churn.probs))
  glm.pred[churn.probs > thresh[i]] = "Yes"
  glm.pred <- as.factor(glm.pred)
  x <- confusionMatrix(glm.pred, test$Churn, positive = "Yes")
  TN <- x$table[1]/1760
  FP <- x$table[2]/1760
  FN <- x$table[3]/1760
  TP <- x$table[4]/1760
  cost[i] = FN*300 + TP*60 + FP*60 + TN*0
}

我没有使用 TN、FP、FN 和 TP 的每个结果的总数,而是使用了一个百分比。这就是为什么我用混淆矩阵取出每个值,然后除以 1760。

在我们的测试集中有 1760 个观察值,这就是这个数字的来源。通过这样做,我正在计算每位顾客的成本。

现在让我们假设我们的公司正在使用我们称之为“简单模型”,它只是默认为 0.5 的阈值。我们可以继续下去,拟合模型,进行预测,并计算成本。我们将这个模型称为cost_simple

# simple model - assume threshold is 0.5
glm.pred = rep("No", length(churn.probs))
glm.pred[churn.probs > 0.5] = "Yes"
glm.pred <- as.factor(glm.pred)

x <- confusionMatrix(glm.pred, test$Churn, positive = "Yes")
TN <- x$table[1]/1760
FP <- x$table[2]/1760
FN <- x$table[3]/1760
TP <- x$table[4]/1760
cost_simple = FN*300 + TP*60 + FP*60 + TN*0

最后,我们可以把所有的结果放在一个数据框中,并把它们画出来。

# putting results in a dataframe for plotting
dat <- data.frame(
  model = c(rep("optimized",10),"simple"),
  cost_per_customer = c(cost,cost_simple),
  threshold = c(thresh,0.5)
)

# plotting
ggplot(dat, aes(x = threshold, y = cost_per_customer, group = model, colour = model)) +
  geom_line() +
  geom_point()

查看结果,我们可以看到,在阈值为 0.2 时,每位客户的最低成本约为40.00 美元

我公司目前正在实施的【简单】模式每个客户的成本约为48.00 美元,门槛为 0.50

如果我们假设我们有大约 500,000 的客户群,从简单模型到优化模型的转换产生了 400 万美元的成本节约。****

# cost savings of optimized model (threshold = 0.2) compared to baseline model (threshold = 0.5)
savings_per_customer = cost_simple - min(cost)

total_savings = 500000*savings_per_customer

total_savings> total_savings
[1] 4107955

根据企业的规模,这种类型的成本节约可能会产生重大的业务影响。

结论

在这篇文章中,我们开发了一个机器学习模型来预测客户流失。

具体来说,我们完成了以下每个步骤:

  • 工作经历
  • 逻辑回归
  • 准备数据
  • 拟合模型
  • 做预测
  • 业务影响

最后,我们针对客户流失问题开发了一个优化的逻辑回归模型

假设公司使用的逻辑回归模型的默认阈值为 0.5 ,我们能够确定最优阈值实际上是 0.2 。

这将每位客户的成本从48.00 美元降至40.00 美元。由于客户群约为 50 万,这将产生每年 400 万美元的成本节约。这是一个重大的业务影响!****

尽管这是一个纯粹假设的例子,但它与您在现实世界中遇到的情况非常相似。

能够有效地识别问题并展示真实的业务影响将使你在就业市场上从其他数据科学家中脱颖而出。

能够实现机器学习模型并对其进行评估是一回事。如果你能做到所有这些,并像我们在这篇文章中所做的那样,以商业影响的形式展示结果,那么你就能顺利找到工作了!

从头开始为你自己的棋盘游戏创建人工智能——准备——第 1 部分

原文:https://towardsdatascience.com/create-your-own-board-game-with-powerful-ai-from-scratch-part-1-5dcb028002b8?source=collection_archive---------3-----------------------

基于我的一个小项目,我想出了一个棋盘游戏,EvoPawness(临时名称)

Photo by Trent Jackson on Unsplash

大家好!这是我在 medium 发表的第二篇关于人工智能(AI)的文章。我想和大家分享我在空闲时间做的小项目的进展。这是我想出的一个棋盘游戏。evopheness(暂名)灵感来源于一款象棋游戏。这是一个简单的游戏。然而,我在这一系列文章中的目的并不是告诉 step 如何有效地开发、设计游戏。而是将重点放在如何为 AI 算法的输入创造一个环境。我不会用 Unity 或者虚幻引擎之类的游戏引擎。但是,我希望我的代码可以帮助你用强大的人工智能设计你的棋盘游戏,不管你是否使用游戏引擎。

I do it in my free time. Source : https://pixabay.com/en/coffee-magazine-newspaper-read-791439/

:这部分还没有用到深度学习,但是会涉及到如何创建用于对抗性搜索的 AI 环境的基础 AI。这部分将集中在设计游戏的类上。如果你只想看到人工智能的运行,跳过这一部分。

很抱歉,我还不能涵盖深度学习部分。我怕会让我的文章很长,读起来没意思。我想一步步分享我的知识。所以我会写在后面的部分。

这篇文章的目标读者是对人工智能和设计游戏感兴趣的人。如果你不是其中之一,当然你仍然可以看到这篇文章(谁告诉你你不能😆).我希望通过发表这篇文章来提高我的写作技巧,并且内容对你有益😄。

简介

谁不喜欢游戏呢?这是一种人们用来解闷的活动。游戏很有趣,可以训练我们的大脑思考如何解决问题,尤其是棋盘游戏。棋盘游戏是一种在棋盘上移动棋子的策略游戏。有几种棋盘游戏,如跳棋、国际象棋和大富翁,需要两个或更多的玩家来玩。击败对手需要好的策略。制定一个击败对手的好策略需要时间。

Source : https://pixabay.com/en/chess-board-game-fireside-strategy-2489553/

现在,棋盘游戏是在电脑上玩的。它通常有一个人工智能代理,让玩家练习游戏。人工智能有一个策略来寻找最好的移动。当然,人工智能越聪明,我们就越喜欢打败人工智能。所以,我们在游戏中制造一个强大的人工智能来学习好的策略怎么样?它将使人工智能足够强大,让人类享受游戏或让他/她沮丧😆。如果你根据自己的游戏想法创造的人工智能被人们所喜欢,那感觉真好。

这篇文章将集中在我如何创造我自己的原创桌游创意evopheness(临时名称)。我还没有检查我的游戏是否已经存在。我认为没有。代码将专注于设计游戏的基本类,准备成为人工智能算法的输入。因为喜欢 Python,所以我会用 Python 语言来写。当然你可以用你喜欢的游戏引擎来写。如果你想实现它,你应该了解如何创建本文中描述的游戏的流程。环境是基于彼得·诺维格的人工智能书。

走吧,让我们创造游戏!

概述

本文将按以下顺序撰写:

  1. 技术
  2. 游戏的描述和规则
  3. 术语定义
  4. 我们的管道或步骤
  5. 定义我们的游戏
  6. 履行
  7. 结论
  8. 编后记

技术

在本文的项目中,我们将使用这些:

  1. Python 3.6.5
  2. PyCharm IDE 或首选文本编辑器,如 Atom 或 Sublime(可选)。

目前不需要其他库。稍后,在即将发布的文章中,我们将使用 TensorFlow(也可能是 PyTorch)来开发 AI agent。

游戏描述和规则

EvoPawness(暂名)是我想出来的一款桌游。这是一个由两个玩家玩的回合制游戏。这块木板有 9 x 9 块瓷砖。每个玩家有 6 个不同颜色(黑色和白色)的棋子。每个玩家有 5 个士兵棋子和 1 个国王。这个游戏的目标是摧毁对手的国王或所有敌人的棋子。

每个棋子都有生命值、ATK 和步数。HP 表明他们的功能或生存能力。ATK 是一个减少敌人设定数量的 ATK 点的属性。步点表示棋子基于其独特移动可以移动多少步。还有一个棋子状态,它表明棋子是否已经被激活。如果它没有被激活,它就不能移动、攻击或进化。

Image 1 : Illustration of the EvoPawness board game

士兵卒可以进化成更高等级的卒。士兵卒可以进化成骑士、车和主教。骑士和主教可以进化成女王。他们会有不同的能力。以下是每个棋子的能力:

士兵

Soldier Pawn. Source : https://pixabay.com/en/chess-pawn-white-chess-board-parts-56257/

移动:士兵只能向前移动。步数取决于步点。可以绕过对手的兵。

惠普,ATK,步骤:默认 3,1,1

攻击目标和范围:和动作一样,只能依靠步点向前攻击。棋子不会从原来的位置移动。

进化:可以进化成骑士、车和主教

:如果棋子被激活,可以移动、进化和攻击

骑士

Knight Pawn. Source : https://pixabay.com/en/graphic-sign-symbol-logo-icon-3608411/

动作:各个方向呈 L 形(像象棋版)。步数固定为 1(忽略步点数)。可以绕过对手的兵

生命值,ATK,第步:进化为骑士前的点数属性(如果是生命值,将是当前生命值而不是最大生命值)加上这些点数(0,4,0)

攻击目标和范围:同样喜欢运动。棋子不会从原来的位置移动。

进化:不能进化

Rook Pawn, Source : https://pixabay.com/en/chess-figure-rook-black-checkerboard-3413419/

移动:可以前、后、左、右移动(像下棋一样)。步数基于步点数。可以绕过对手的兵

生命值,ATK,第步:进化为骑士前的点数属性(如果是生命值,将是当前生命值,而不是最大生命值)加上这些点数(2,2,0)

攻击目标和范围:同喜欢运动。棋子不会从原来的位置移动。

进化:进化成女王

主教

Bishop Pawn, Source : https://cdn.pixabay.com/photo/2012/04/18/00/52/chess-36348_1280.png

移动:对角线向各个方向移动(像下棋一样)。步数基于步点数。可以绕过对手的兵

生命值,ATK,第步:进化为骑士前的点数属性(如果是生命值,将是当前生命值,而不是最大生命值)加上这些点数(2,1,1)

攻击目标和范围:同喜欢运动。棋子不会从原来的位置移动。

进化:进化成女王

女王

Queen Pawn, Source : https://pixabay.com/en/chess-chess-peace-game-3d-680492/

移动:对角线向各个方向移动,可以前、后、左、右移动(像下棋一样)。步数基于步点数。可以绕过对手的兵

生命值,ATK,第步:进化为骑士之前的点数属性(如果是生命值,则为当前生命值,而不是最大生命值)加上这些点数(2,2,0)

攻击范围:同喜欢运动。棋子不会从原来的位置移动。

进化:无法移动

国王

King, Source : https://pixabay.com/en/chess-king-figure-game-black-159693/

移动:不能移动

惠普,ATK,步长:默认值(15,4,1)

攻击范围和目标:像女王一样用一个范围攻击。棋子不会从原来的位置移动。

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

每个玩家都有一种特殊的力量,需要“法力”来实现。“法力”每回合产生一次(最多 10 次)。在第一轮,他们将有 5 个马纳。它将在每回合一次生成。这里有特殊的力量:

  • 激活一个士兵卒:需要 3 个马纳
  • 进化兵卒:需要 5 个玛纳斯
  • 进化一辆车和一个主教:需要 10 个马纳

最后,有两个“符文”随机产生。它会在每个玩家的区域附近随机产生。如果符文被棋子接近。卒可以在他们的一个属性上获得加值(随机)。奖金是:

  • 气血增加 2
  • 增加 1 级
  • 增加 ATK 2

符文可以在棋子的位置上产生。如果它在棋子所在的位置繁殖,它将立即获得奖励。这条规则会让游戏变得不确定。如果我们使用的算法需要游戏是确定性游戏,我们会改变这个规则。

每个玩家每回合只能选择一个动作。他们必须选择移动棋子或激活特殊异能。如果没有可以采取的行动。玩家必须跳过这个回合。

Source: https://pixabay.com/en/whistle-black-blow-referee-game-33271/

黑人玩家总是第一个移动。所以在第一个回合,轮到黑人玩家,然后轮到白人玩家。他们将交替轮流。

这些规则可以根据我们在下一部分使用的人工智能算法而改变。我以后会告诉你我们是否需要改变某个特定的部分。我还没有测试游戏的平衡。所以,不要指望游戏会保持平衡。可能有一种策略被压倒了(OP)。我们不会讨论游戏的平衡。但是如果你认为这个游戏真的不平衡,就告诉我。我会根据你的声明改变游戏规则。

这就是赢创(临时名称)的全部规则。如果您对这个名字有疑问,那么“(临时名称)”也是这个名字的一部分。我还没有决定游戏的名字,所以它将是临时的名字。

术语定义

初始状态

指定游戏开始时是如何设置的。

行动。输入=(状态)

返回一个州的一系列合法举措

结果函数。输入=(状态,动作)

一个转换模型,它将定义给定状态的动作结果。

终末试验。输入=(状态)

检查游戏的终端状态。如果游戏已经结束,则返回 true,否则返回 false

实用功能。输入=(状态,播放器)

定义玩家处于终端状态时游戏的最终数值。数值公式是由我们定义的。

评估功能。输入=(状态,播放器)

定义玩家在给定状态下对预期效用数值的估计。这个函数在游戏还没有结束的时候被调用。

我们的管道或步骤

我们的步骤如下:

Image 2 : our pipeline

我们已经陈述了游戏的描述和规则。我们仍然需要定义游戏的状态、初始状态、玩家、可能的动作、结果函数、最终测试、效用函数和评估函数。然后,我们需要定义游戏的元素或对象。然后我们设计类图并实现它。哦,我跳过了动作功能。我们将在初始状态之后定义它。

在本文中,我们将跳过效用函数和评估函数。我们将在下一篇文章中定义它们。

定义我们的游戏

在这一部分,我们将定义所有我们必须定义的游戏元素。如上所述,它是基于彼得·诺维格的书。让我们跟随它:

国家代表权

我们应该定义我们将如何表现游戏的状态。在此之前,我们应该决定在状态中应该保存哪些信息。在这个游戏中,有:

格式:游戏的对象(属性)

  1. 转动计数器
  2. 玩家(法力,颜色)
  3. 棋子(生命值,攻击力,位置,活动状态,彩色玩家,步,死亡)
  4. 王者(hp,atk,位置,彩民,步,死)
  5. 符文(奖励,职位)

好了,我想就这些了。让我们想想如何代表他们。

我们将需要一个 2D 名单或阵列的棋子,以保持坐标的棋子和国王的位置信息的游戏。它可以使我们更容易检查棋子的位置。因此,我们将把我们的棋子和国王的位置写入一个 2D 列表或棋子和国王的数组。

我们还需要一个玩家、兵、符文和国王的列表来跟踪每个对象。仅仅为了检查每个对象的位置而循环我们的 2D 数组是令人疲惫的。

最后还有一个转计数器。这将帮助我们决定现在轮到哪一轮了。如果该指示物能被 2 整除,则轮到白牌手,否则轮到黑牌手。

可能有比这更好的表述。但我认为这将足以代表我们的游戏。

初始状态(状态)

这是我们基于如何表示状态的初始状态:

Image 2: Initial state with 2d board

其中的棋子列表与我们的 2D 阵棋盘中的棋子相同。所有棋子的状态都是不活动的(状态布尔值为假)

玩家(功能(状态)->玩家 _ 索引)

有两个玩家,白人玩家和黑人玩家。玩家的回合决定于:
如果回合是偶数,则是白牌玩家回合。但如果回合是奇数,则轮到黑色玩家。

动作(功能(状态)->动作列表)

每个参与者都可以完成以下操作:

玩家

如果玩家有 5 个马纳,他/她可以将一个士兵卒进化成更高级的卒(女王和骑士除外)。

如果一个玩家有 3 个马拿,他/她可以启动一个兵卒。

如果一个玩家有 10 个马拿,他/她可以将车或象兵进化成女王。

如果可能的话,棋子可以根据棋子的种类移动到指定的方向。

如果敌人的兵在攻击范围内,兵可以攻击敌人的兵。

君王

如果敌人的兵在攻击范围内,盟友的王可以攻击敌人的兵。

Image 3: rough scratch action flow

通过

如果没有可用的动作,通过该回合。

所有可能的动作将被追加到一个将由函数返回的列表中。

结果函数(函数(状态,动作)->新状态)

这是我们行动的结果

有三位演员我们必须去看:

  • 玩家

玩家可以用马纳斯的费用来进化棋子。它会将棋子变成更高级的棋子。它将添加属性

玩家可以用马纳斯的费用启动兵。它会将棋子状态更改为活动。

兵可以根据类型移动。它将改变棋子在棋盘阵列中的位置。如果棋子命中一个符文,会随机增加其属性(只有一个)。

兵可以通过根据攻击者的 ATK 点减少生命值来攻击对手的兵。

  • 君王

棋子可以通过根据攻击者的 ATK 点数减少生命值来攻击对手的棋子

  • 过关

通过转弯

Image 4 : Result Function

完成上述动作之一后,回合增加一。如果该回合能被 5 整除,它将产生 2 个随机符文。

终端测试(Function (state) -> boolean true or false)

它检查游戏是否结束。

我们会检查每个国王的死亡属性。如果为真,那么函数将返回**true**。如果没有,我们将检查每个玩家的棋子。

如果玩家没有活着的棋子,该函数将返回**true**,否则返回**false**

效用和评价函数(函数(状态,播放器)->整数)

我们不会在这里公式化我们的评价或效用函数。在当前的文章中不需要。

履行

在我们编码之前,我们需要设计我们的类,使我们的代码变得更加结构化。我们需要设计这种关系。我们将在模型视图控制器(MVC)设计模式中完成。

Image 5: Class Diagram.

所有类型的棋子将继承棋子抽象类。国家将有符文,球员,和卒类。这些是我们的模型。我们定义的元素在游戏控制器中。

就是这样,状态意志包含符文,玩家,兵。游戏控制器将成为视图和状态之间的桥梁。控制器将生成我们上面定义的所有元素。状态是我们的模型,将由 GameController 类处理。

源代码将被上传到我的 GitHub 库。我们不会在这里讨论所有的代码。我们将讨论如何基于我们定义的元素进行编码。

国家代表权

以下是我如何对州进行编码的示例:

它具有我们上面定义的所有属性。

初态

这是如何定义初始状态的例子

演员

下面是决定谁该轮到谁的代码。

class State:
  def get_player_turn(self):
    return self.turn % 2class GameController:
 def player(self, state):
    return state.get_player_turn()

行动

下面是决定可能的操作的代码。我将向您展示如何生成可能的操作的示例。

它会返回玩家可能的动作。还有棋子可能行动的代码。但是,我不会在这里展示它,因为它很长。可能的动作将通过 dict 格式返回,其中包含棋子的信息和玩家的信息。它将包含有关该操作的所有信息。

这里有一个使用可能动作的例子。这是玩家可能的动作之一(记住,玩家有两个可能的动作,提升和激活) :

'p1a4’: {‘action’: ‘activate’,
 ‘pawn_atk’: 1,
 ‘pawn_hp’: 3,
 ‘pawn_index’: 4,
 ‘pawn_step’: 1,
 ‘pawn_x’: 8,
 ‘pawn_y’: 1,
 ‘player_index’: 1}}

其中“p1a4”是唯一键,用于标识唯一动作。

控制器将调用该函数。

class GameController:
def get_possible_action(self,state):
        all_possible_action = self.combine_action(self.state.get_possible_action() + self.state._get_possible_action_pawn() + self.state._get_possible_action_king())
        if len(all_possible_action.keys()) == 0:
            return [{'action': {'skip' : {'action': 'skip'}}}]
        return all_possible_action

结果函数

这是结果函数的例子

它将接收动作输入和当前状态。例如,我们想要激活索引为 1 的棋子,该函数将接收以下输入:

{‘action’: ‘activate’,
 ‘pawn_atk’: 1,
 ‘pawn_hp’: 3,
 ‘pawn_index’: 4,
 ‘pawn_step’: 1,
 ‘pawn_x’: 8,
 ‘pawn_y’: 1,
 ‘player_index’: 1}

它将激活一个棋子并返回新状态。小心,我们必须确保首先复制我们的状态,以避免对象引用(使用 deepcopy 库)。

该函数将响应控制器调用的方法。

终端功能

下面是如何检查游戏是否已经结束的例子

该功能将检查卒和王的死亡状态。它没有告诉我们谁是赢家。它只会检查游戏的终端状态。

测试游戏

我还没有创建 GUI 版本。目前,我们只能在终端或命令提示符下玩。在候机厅玩不舒服。下面是我玩游戏的截图:

Sorry for my bad writing >_<

状态、可能的动作将是我们视图的输入。

我们视图的代码将被放在 GitHub 存储库中。

结论

我们从零开始创造了一个新游戏。我们已经定义了一些元素,可以作为人工智能算法的输入。我们定义了状态、初始状态、玩家函数、结果函数和终端函数的表示,并用 Python 编程语言编写。

我还没有测试过代码是否能完美运行。所以,如果有人发现了 bug,可以在这里留言。

编后记

感谢您阅读我关于人工智能的第二篇文章。我需要一些建设性的反馈,让我在写作和人工智能方面做得更好。请手下留情😆!

我只是想把我的知识分享给读者。分享知识很好,因为它可以帮助有需要的人。我正在学习这个领域,想分享我所学到的东西。如果有人告诉我这篇文章有什么问题,那就太好了。

GitHub 库将于明天在创建。我需要先记录这个函数。

编辑:这是 GitHub 链接

我希望 GUI 能在下一篇文章中完成。在候机厅玩真的很难受。如果有人想为创建 GUI 做出贡献,我会非常感激😃。

原谅我低效的代码,我已经尽量让代码可读,高内聚,低耦合。因此,欢迎任何反馈来修复我的代码混乱。

如果你想从我这里得到另一篇这样的文章,请拍下这篇文章👏 👏这会鼓舞我写下一篇文章的精神。我保证会做出更好的关于 AI 的文章。

在下一篇文章中,我将分享一个关于对抗性搜索的传统算法。在此之后,我们将进入创建具有深度神经网络的代理。

下一篇文章再见!

Source : https://cdn.pixabay.com/photo/2017/07/10/16/07/thank-you-2490552_1280.png

系列文章

第 1 部分:从头开始为你自己的棋盘游戏创建人工智能——准备——第 1 部分

第二部分:从零开始为你自己的棋盘游戏创造人工智能——Minimax——第二部分

第三部分:为你自己的棋盘游戏从头开始创造人工智能——alpha zero——第三部分

来源

[## 棋盘游戏的定义

一种在棋盘上移动棋子的策略游戏(如跳棋、国际象棋或西洋双陆棋);一种游戏(如国际象棋)…

www.merriam-webster.com](https://www.merriam-webster.com/dictionary/board game) [## UML 类图教程

为每个想学习类图的人编写的全面的 UML 类图教程。阅读此 UML…

www.visual-paradigm.com](https://www.visual-paradigm.com/guide/uml-unified-modeling-language/uml-class-diagram-tutorial/)

拉塞尔,斯图尔特 j,和彼得诺维格。人工智能:一种现代方法。普伦蒂斯霍尔出版社,2010 年。

通过 Python 使用 API 创建数据集

原文:https://towardsdatascience.com/creating-a-dataset-using-an-api-with-python-dcc1607616d?source=collection_archive---------0-----------------------

“person using laptop” by rawpixel on Unsplash

每当我们开始一个机器学习项目时,我们首先需要的是一个数据集。虽然您可以在网上找到许多包含各种信息的数据集,但有时您希望自己提取数据并开始自己的调查。这时,网站提供的 API 就可以派上用场了。

应用程序接口(API)是允许两个软件程序相互通信的代码。API 为开发人员定义了编写从操作系统(OS)或其他应用程序请求服务的程序的正确方式。— 技术目标

API 实际上是一个非常简单的工具,允许任何人从给定的网站访问信息。您可能需要使用某些头,但是有些 API 只需要 URL。在这篇特别的文章中,我将使用由自由代码营 Twitch API 直通提供的 Twitch API。这个 API 路由不需要任何客户端 id 来运行,因此访问 Twitch 数据非常简单。整个项目在 Create-dataset-using-API 存储库中作为一个笔记本提供。

导入库

作为访问 API 内容和将数据放入. CSV 文件的一部分,我们必须导入一些 Python 库。

  1. 请求库通过使用get()方法帮助我们从 API 获取内容。json()方法将 API 响应转换为 JSON 格式,以便于处理。
  2. 需要 json 库,这样我们就可以使用从 API 获得的 JSON 内容。在这种情况下,我们为每个频道的信息(如名称、id、视图和其他信息)获取一个字典。
  3. pandas 库帮助创建一个数据帧,我们可以用正确的格式导出一个. CSV 文件,带有适当的标题和索引。

了解 API

我们首先需要了解从 API 中可以访问哪些信息。为此,我们使用 channel Free Code Camp 的例子来进行 API 调用,并检查我们获得的信息。

This prints the response of the API

为了访问 API 响应,我们使用函数调用requests.get(url).json(),它不仅从 API 获得对url的响应,还获得它的 JSON 格式。然后,我们使用dump()方法将数据转储到content中,这样我们就可以在一个更直观的视图中查看它。代码的输出如下所示:

Response for API

如果我们仔细观察输出,可以看到我们收到了大量信息。我们得到的 id,链接到各种其他部分,追随者,姓名,语言,状态,网址,意见和更多。现在,我们可以遍历一个频道列表,获取每个频道的信息,并将其编译成一个数据集。我将使用列表中的一些属性,包括 _id显示 _ 名称状态关注者视图

创建数据集

现在,我们已经知道了 API 响应的内容,让我们从一起编译数据和创建数据集开始。对于这个博客,我们将考虑我在网上收集的频道列表。

我们将首先从定义数组中的通道列表开始。然后,对于每个通道,我们将使用 API 获取其信息,并使用append()方法将每个通道的信息存储在另一个数组channels_list中,直到我们将所有信息收集到一个地方。请求响应是 JSON 格式的,所以要访问任何键值对,我们只需在JSONContent变量后面的方括号中写下键值名。现在,我们使用 pandas 库,使用 pandas 库中提供的方法DataFrame()将这个数组转换成 pandas 数据帧。dataframe 是一种类似于表格的表格形式的数据表示,其中数据以行和列的形式表示。该数据帧允许使用各种方法快速处理数据。

Create dataframe using Pandas

pandas sample()方法显示随机选择的数据帧行。在这个方法中,我们传递我们希望显示的行数。这里,让我们显示 5 行。

dataset.sample(5)

仔细观察,我们发现数据集有两个小问题。让我们逐一解决它们。

  1. 标题:目前,标题是数字,并不反映每列所代表的数据。对于这个数据集来说,这似乎不太重要,因为它只有几列。然而,当您要探索具有数百个列的数据集时,这一步将变得非常重要。这里,我们使用 pandas 提供的columns()方法定义列。在这种情况下,我们明确定义了标题,但在某些情况下,您可以直接将关键字作为标题。
  2. 无/空/空白值:某些行将会有缺失值。在这种情况下,我们有两种选择。我们可以删除任何值为空的整行,也可以在空白处输入一些精心选择的值。这里,status列在某些情况下会有None。我们将通过使用方法dropna(axis = 0, how = 'any', inplace = True)删除这些行,该方法删除数据集中包含空值的行。然后,我们使用方法RangeIndex(len(dataset.index))将数字的索引从 0 更改为数据集的长度。

Add column headings and update index

导出数据集

我们的数据集现在准备好了,可以导出到外部文件。我们使用to_csv()方法。我们定义两个参数。第一个参数指的是文件的名称。第二个参数是一个布尔值,表示导出文件中的第一列是否有索引。我们现在有了一个包含我们创建的数据集的. CSV 文件。

Dataset.csv

在本文中,我们讨论了一种使用 Twitch API 和 Python 创建我们自己的数据集的方法。您可以遵循类似的方法通过任何其他 API 访问信息。使用 GitHub API 试试看。

请随意分享你的想法,如果你有任何问题,请打电话给我。

为 Planet Coaster 中的平顺性动画创建重力分析工具

原文:https://towardsdatascience.com/creating-a-g-force-analysis-tool-for-flat-ride-animations-in-planet-coaster-f7e6fecc2002?source=collection_archive---------10-----------------------

“平板游乐设施”一词描述了主题公园中除过山车之外的所有游乐设施,包括摩天轮和其他大型液压怪兽等大型景点,这些怪兽会让人们在周围摇摆以取乐。重要的是,我们在制作《星球飞车》的时候把这些游乐设施做对了,不仅仅是造型细节,还有动作。

在开发的早期阶段,我们试验了各种物理驱动的系统,试图重现准确的平顺性运动,但最终结果往往是不可预测的,并且没有公正地对待我们用作参考的现实生活中的景点。我们很早就决定,最好的方法是手工制作平车(不是过山车)动画,尽可能让它们接近原始参考视频。这使我们能够复制每一个细微的细节,同时保持对所有循环动画的绝对控制,最终形成一个平稳的游乐设备运动序列。

A guest reacting to lateral G-force at speed

游乐设施开始进入游戏,很快我们就能看到游客坐在座位上的游乐设施。为了让这些游客栩栩如生,他们需要对游乐设施可能带他们经历的所有运动做出逼真的反应,为此,我们需要一种从他们的经历中提取详细遥测信息的方法。

This is what we’re going to work towards

我希望能够捕捉游客在游乐设施上感受到的垂直、横向和水平重力,以及其他有用的统计数据,如高度和速度。我需要的所有信息都出现在动画中,所以我开始编写一个工具,可以分析 Maya 场景并挖掘我们需要的信息来驱动一套反应性的来宾动画(向左倾斜、向右倾斜、加速、停止等)。这种工具还可以标记出不适合主题公园骑行的高重力热点。

This early development video gives you a glimpse of the tool we’ll be talking about today

一开始,我只是在游乐设施上选择一个可以代表游客的位置,并为其设置一个定位器。这将作为我的“客人”位置,我可以从中提取我需要的所有数据。

Set your calculators to ‘maths’

获得基础知识

所以,我有一个定位器,在这个场景的动画中,它代表一个游客被带到一个游乐设施上。最简单的形式是,这个工具需要在动画的每一帧上捕捉游客定位器的变换矩阵,然后我们就可以从那里计算出剩下的部分。

**for** i, time **in** enumerate(timeSliderRange):
    pm.currentTime(time)
    matrix = TransformationMatrix(obj.worldMatrix.get())
    position = xform(obj, q=True, rp=True, ws=True)
    positions.append(Vector(position))
    matrices.append(matrix)

我将定位器的转换矩阵存储在一个单独的列表中,与它的旋转-枢轴位置相对应,以使只需要位置值的代码更容易编写。我会在这里放一些代码片段,但是它真正的上下文在文章末尾的主代码块中有详细的描述。

载体

在接下来的一点,你会看到我使用了 PyMel 的‘Vector’类相当多,所以我想我应该给出它们是什么以及我为什么使用它们的粗略概述。

Not that Vector

简而言之,它们是代表空间位置的三个值(在 3D 中),例如 x,y,z

为了形象化,假设向量([0,0,0])表示场景中心的位置,但是向量(0,1,0)表示在 Y 轴上距离场景中心一个单位的位置。第二个向量与第一个向量的关系描绘了一条直线上升的轨迹。

在下面的例子中,我举例说明了一个值为 5、4 和 2 的向量,它转化为一条与读者成一定角度的直线。

所以使用多个向量,你可以计算出一个物体的位置和方向。对这个问题很有用。

物理时间

是时候重温普通中等教育证书物理了。我可以假设:

  • 重力=加速度/重力。
  • 为了得到加速度,我们需要算出速度/时间
  • 为了得到速度,我们需要计算距离/时间

我们有了计算距离/时间所需的所有位置,所以我从计算动画过程中游客的速度开始。

计算客人的速度

δt(δ时间)正好是 1.0 除以场景的帧速率,因此重新排列该公式的一个更简单的方法是。

δ表示“在 x 中的变化”,所以δt代表从时间轴的一帧到下一帧的变化。

行进的距离将是我们的定位器在当前帧上的位置减去其在前一帧上的位置。

oldPos = positions[0]
**for** i, position **in** enumerate(positions):
    **if** i == 0: **continue** velocity = (position - oldPos) * frameRate
    velocities.append(velocity)
    oldPos = position

我们只是遍历位置列表(排除第一个位置,因为它没有之前的帧可以比较),计算偏移量并将其乘以帧速率,然后将速度存储在列表中。

计算游客的加速度

让我们再一次重新排列这个公式来简化它,其中δv(速度的变化)是前一帧的速度与当前帧的速度之差。

velocities[0] = velocities[1]

# Acceleration
accelerations = [dt.Vector(0, 0, 0)]
oldVelocity = velocities[0]
**for** i, velocity **in** enumerate(velocities):
    **if** i == 0: **continue** acceleration = (velocity - oldVelocity) * frameRate
    accelerations.append(acceleration)
    oldVelocity = velocity

为了让这些数据有用,我需要更多关于游客加速方向的信息。要算出来,我需要客人的总体加速度的上、前、右矢量的“点积”。

fAcc = []
uAcc = []
rAcc = []

**for** i, acceleration **in** enumerate(accelerations):
    matrix = matrices[i]
    forwards = dt.Vector(matrix[0][0:3])
    up = dt.Vector(matrix[1][0:3])
    right = dt.Vector(matrix[2][0:3])
    fAcc.append(forwards.dot(acceleration))
    uAcc.append(up.dot(acceleration))
    rAcc.append(right.dot(acceleration))

什么是点积?

在这一阶段之前,我们计算了游客在世界空间中的加速度,这意味着我们知道他们在任何方向上都加速了一定的量。下一步是通过将其分解为相对于游客在每个方向上的加速度来解释这一加速度。

我做的第一件事是从访客定位器的转换矩阵中获取前向向量,并将其转换为向量数据类型。PyMel 的内置 vector 类型已经有了计算点积的方法。

forwards = dt.Vector(matrix[0][0:3])

在下一步中,我们使用这种方法沿着它的前向轴投射游客的加速度。产生的点积是客体在该轴上加速的量。

forwards.dot(acceleration)

Calculating the Forward and Up acceleration

关于的图解释了我在 2D 的情况下做了什么,其中游客的向量被描述为 v加速度被投影并绘制在向前的轴上。加速是点积,它向前加速的量,而加速是它向上加速的量。

好吧,回到获取重力的话题。

计算我们的客人受到的重力

最后一步是计算施加在游客身上的重力。我这样做是通过将定位器的加速度除以重力(9.81)。

使用矢量‘length()’方法计算速度和加速度

我在主代码块的末尾放了一些我还没有提到的东西,所以我将快速描述一下它的内容。

PyMel 的 Vector 类中还包含了一个非常方便的“length()”方法,用于计算向量的长度。如前所述,我们将速度和加速度值存储为向量,这允许我们对它们执行各种预建的数学函数。

如果我们以之前的向量为例,将其表示为直角三角形,我们可以将其转置到 2D 平面上,并将边表示为 a、bh (斜边)。从这里我们可以应用毕达哥拉斯定理,将这个向量的长度表示为:

通过找到一个向量的长度(或大小),我们可以将一个物体的速度和加速度解释为一个单浮点数而不是三个值。

代码时间

我制定并测试了所有这些不同的方法,所以我决定将所有这些组合成一个简单的 Python 类,名为“SceneAnalysis ”,它将有一个“analyse()”方法,用于在一个定义的对象上运行所有这些循环,并将结果存储在其自身中。

然后,我可以将这个类传递到一个 UI 中,让它在以后构建图形或导出元数据。

结论

The Flatride Analyser doing its thing

工具起作用了。一旦我收集了所有的数据,我花了一些时间在 PySide 上构建了一个 UI,它可以将所有的值显示为曲线。

最终,我们没有使用这个工具来生成动画系统解释的数据。我们决定,数学足够简单,可以在运行时计算,处理开销很小,并且仍然驱动反应式动画系统。这样做还允许我们将同样的数学方法应用于杯垫(可以是任何形状、速度或大小,因此无法预先计算),还允许我们迭代动画,而不需要维护单独的元数据文件。我们确实保留了用手写数据覆盖这个系统的能力,以纠正动画看起来不太正确的特殊情况,这在一些最复杂的游戏中派上了用场。

All the forces are displayed in this UI, with the Horizontal, Vertical and Forward G-Forces represented by Red, Green and Blue (x, y, z)

那么,制造这种工具值得吗?当然可以。鉴于我们的初始解决方案是手工创作这些数据,原型制作系统证明了它足够简单,可以自动完成,并为我们的动画师节省数小时的时间。

该工具也是一个优秀的明智检查器,允许我们分析我们的工作,标记任何客人的重力会飙升到致命程度的时刻,并给我们一个机会来相应地调整我们的骑行动画。

总的来说,这是一个非常有趣的制作工具(如果你和我一样喜欢图形),也是一个仔细观察我们手工制作的动画背后的物理原理的好方法。

一如既往,特别感谢詹姆斯·奇尔科特和蜜琪拉·斯特雷特菲尔德的数学专长

使用深度学习创建混合内容协作电影推荐器

原文:https://towardsdatascience.com/creating-a-hybrid-content-collaborative-movie-recommender-using-deep-learning-cc8b431618af?source=collection_archive---------2-----------------------

编剧:亚当·莱恩贝里和 T2 克莱尔·朗格

介绍

在这篇文章中,我们将描述我们如何使用深度学习模型来创建一个利用内容和协作数据的混合推荐系统。这种方法首先分别处理内容和协作数据,然后将两者结合起来产生一个两全其美的系统。

使用 MovieLens 20M 数据集,我们开发了一个项目到项目(电影到电影)推荐系统,它可以推荐与给定输入电影相似的电影。为了创建混合模型,我们将从标签数据中学习基于内容的电影嵌入的自动编码器和从收视率数据中学习基于协作的电影嵌入的深度实体嵌入神经网络的结果进行了集成。

我们提供了内容和协作推荐系统的简要总结,并讨论了混合模型的好处。我们将跟踪不同系统在我们最喜欢的电影之一中的表现:指环王:指环王联盟。

使用 PyTorch 0.4.1 在 Paperspace GPU 机器上执行深度学习工作。这个项目的代码可以在这个 GitHub 库中找到。

协作与内容推荐系统

协作推荐器依赖于用户在与项目交互时生成的数据。这方面的例子包括:

  1. 用户对电影的评分范围为 1-5
  2. 用户在在线零售网站上购买甚至查看商品
  3. 用户对在线音乐流媒体服务上的歌曲表示“赞成”或“反对”
  4. 在约会网站上向左或向右滑动

在电影推荐器的上下文中,协作过滤器基于评级简档发现相似用户如何对电影进行评级的趋势。可以使用各种技术来分解或处理评级数据,以最终找到用户和电影在共享潜在空间中的嵌入。电影嵌入描述了它们在潜在空间中的位置,然后可以用于进行电影到电影的推荐。

协作数据的一个好处是它总是“自我生成”——用户在与项目交互时自然地为您创建数据。这可能是一个有价值的数据源,尤其是在高质量项目特征不可用或难以获得的情况下。协作过滤器的另一个好处是,它帮助用户发现他们的历史档案所定义的子空间之外的新项目。

然而,协作过滤器也有一些缺点,例如众所周知的冷启动问题。协作过滤器也很难准确地推荐新颖或合适的项目,因为这些项目通常没有足够的用户-项目交互数据。

内容推荐器依靠项目特征来进行推荐。这方面的例子包括:

  1. 电影上用户生成的标签
  2. 项目颜色
  3. 项目的文本描述或用户评论

内容过滤器往往对流行性偏差和冷启动问题更具鲁棒性。他们可以根据小众口味轻松推荐新的或新奇的商品。然而,在项目到项目推荐器中,内容过滤器只能推荐与原始项目具有相似特征的项目。这限制了推荐的范围,也可能导致出现评分较低的项目。

在电影到电影推荐器的环境中,一个协作过滤器回答了这个问题:“什么电影有相似的用户评级简档?”,一个内容过滤器回答这个问题:“什么电影有相似的特征?”。通过创建一个混合推荐器,我们试图创建一个系统来推荐其他用户以类似方式评价的电影,同时仍然基于该电影的特征进行主题推荐。

什么是嵌入?

嵌入是机器学习的几个领域中的一个热门话题,例如自然语言处理、具有分类特征的预测模型和推荐系统。计算嵌入有许多方法,但最终目标是将“事物”映射到一个具有复杂和有意义维度的潜在空间。在电影推荐中,你可以想象潜在的维度来衡量诸如“科幻”和“浪漫”等类型以及诸如“对话驱动与动作包装”等其他概念。用户和电影通过它们与每个维度的关联程度被映射到这个空间。

在著名的 word2vec 例子中,习得的单词嵌入能够完成“男人对女人就像国王对 ___”的类比如下图所示,单词已经被映射到一个共享的潜在空间中,单词的意思以几何形式呈现。

Visualize Word Vectors (https://www.tensorflow.org/images/linear-relationships.png)

在机器学习和深度学习的情况下,人们可以选择使用一键编码(OHE)或学习嵌入来执行特定的任务。数据的 OHE 表示法有一个潜在的不良特性,即每一项都与所有其他项正交(因此在数量上完全不同)。当以单词为例时,这可能是数据的弱表示,因为类似单词如“alien”和“外星人”的相似性/可交换性由于正交性而完全丧失。因此,使用单词嵌入可以扩展某些模型的功能。

余弦相似性

我们使用余弦相似度来量化电影之间的相似性。余弦相似度的范围从-1 到 1,计算方法是两个向量之间的点积除以它们的大小。

Equation for calculating cosine similarity between two vectors

简而言之,指向相同方向的电影嵌入向量将获得高余弦相似性分数。这个想法是,通过潜在的概念空间的方向抓住了电影的本质。一个简单的例子来帮助形象化这一点:如果“科幻”和“浪漫”是潜在空间中的维度,那么科幻与浪漫比例相似的电影将指向相同的方向,从而获得高余弦相似性分数。

2D Visualization of vectors arranged in different ways and the resulting cosine similarities

这段代码从电影嵌入中构造余弦相似矩阵,并输出给定输入电影的前 n 个最相似的电影。

从内容数据中寻找电影嵌入

MovieLens 数据中包括一组大约 50 万个用户生成的电影标签。根据 MovieLens 的自述文件:“每个标签通常是一个单词或短语。特定标签的含义、价值和用途由每个用户决定。”

这些数据按电影分组,标签连接在一起,产生一个文档集。在这个语料库中,文档是特定电影的所有标签的组合。下面是“指环王:指环王联盟”标签文档的摘录。正如你所看到的,像“冒险”、“幻想”和“根据一本书”这样的词/短语频繁出现。这些数据还包括演员和作者的名字。

adventure characters epic fantasy world fighting photography Action adventure atmospheric based on a book based on book beautifully filmed ensemble cast fantasy fantasy world high fantasy imdb top 250 magic music nature nothing at all Oscar (Best Cinematography) Oscar (Best Effects - Visual Effects) scenic stylized Tolkien wizards adventure atmospheric ensemble cast fantasy fantasy world magic stylized wizards Watched adapted from:book author:J. R. R. Tolkein based on book epic fantasy middle earth faithful to book fantasy good versus evil high fantasy joseph campbell's study of mythology influenced magic atmospheric boring high fantasy Action adventure atmospheric based on a book beautifully filmed fantasy high fantasy magic music mythology romance stylized time travel Viggo Mortensen wizards Peter Jackson Peter Jackson music must see Tolkien high fantasy Myth Tolkien wizards Ian McKellen bast background universe

接下来,我们将标记文档转换为词频——逆文档频率(TF-IDF)表示。TF-IDF 将非结构化文本数据标记为数字特征,这些数字特征更容易被机器学习算法处理。粗略地说,文档中每个术语的出现频率取决于包含该术语的文档数量。结果,区分电影的词(例如,“外星人”、“幻想”)将比用于描述所有电影的词(例如,“电影”、“演员”)具有更高的权重。

sci-kit learn 中的 TF-IDF 矢量器允许您选择要考虑的 ngrams 范围。对于这个项目,我们发现 unigrams 是足够的,同时仍然保持我们的一些功能是可管理的。

在 TF-IDF 空间中,每个维度都代表了某个词对一部电影有多重要。这种表示不太理想,因为单个概念(例如,外星人/外星人)的编码是碎片化的,并且分散在多个维度上。我们希望将 TF-IDF 数据压缩到一个低维空间中,在这里概念被整合到共享维度中。在压缩的空间中,希望每个维度将代表复杂和健壮的概念。

为了执行压缩,我们选择使用自动编码器。自动编码器是输出与输入相同的神经网络。在我们的架构中(如下图所示),高维的 TF-IDF 输入被逐渐压缩成 100 维的中央隐层。网络的前半部分是“编码器”。网络的另一半,即“解码器”,试图重建原始输入。通过设置均方误差损失函数并执行反向传播,网络(尤其是编码器)学习一个函数,该函数将数据映射到较低维度的空间中,从而保留大部分信息。

Autoencoder Architecture

让我们从定义编码器和解码器网络开始。它们是单独定义的,以便在网络训练完成后易于对数据进行编码。

接下来,让我们定义一个 PyTorch 数据集类。注意在__getitem__方法中xy是等价的——这是自动编码器的基本概念。

有了这些构建块,让我们定义一个包装器类,它实例化一切,处理训练循环,并执行数据编码。

从这里开始,训练自动编码器和编码/压缩数据就很简单了:

下面是训练和验证损失随时间变化的曲线图。如您所见,该模型没有过度拟合或拟合不足,最终损耗收敛到一个非常低的值(大约 1e-4)。

使用自动编码器学习的基于内容的电影嵌入,按照余弦相似性的定义,与“指环王:指环王”最相似的前 20 部电影如下所示。

从协作数据中发现电影嵌入

完全连接的神经网络用于寻找电影和用户嵌入。在该架构中,大小为(n_users, n_factors)的用户嵌入矩阵和大小为(n_movies, n_factors)的电影嵌入矩阵被随机初始化,随后通过梯度下降学习。每个训练数据点都是一个用户索引、电影索引和一个评级(等级为 1-5)。用户和电影索引用于查找嵌入向量(嵌入矩阵的行)。这些向量然后被连接并作为输入传递给神经网络。

Embedding Net Architecture

这类似于杰瑞米·霍华德在 fast.ai MOOC 中教授的技术,我们也使用了 fastai 库。让我们读入数据,创建一个验证集,并构造一个 fastai ColumnarModelData 对象,它本质上是 PyTorch 的 Dataset 和 DataLoader 类的一个方便的包装器。

接下来,我们来定义一下神经网络。由于网络的输出被限制在 1–5 范围内,因此在输出层使用了一个缩放的 sigmoid 激活函数。通过 sigmoid 传递线性激活给了模型更多的灵活性,从而使其更容易训练。例如,如果模型试图输出一个 5,而不是强迫它从线性计算中输出一个非常接近 5 的值,使用 sigmoid 允许它输出任何高值(因为输入~6 或更大值都被 sigmoid 函数映射到~1.0)。另一种方法是在没有达到 sigmoid 时停止,让模型学习直接从线性输出中输出正确的评级。

fastai 库使得拟合模型变得非常简单。这里使用的fit函数负责训练循环,并提供一个漂亮的动画状态栏,显示每个迷你批次的剩余时间和损失值。

由于训练数据的规模很大(2000 万),该模型需要一段时间来训练。在整个训练的不同阶段,我们调整了辍学水平,以应对过度或不足。学习率也可以使用 fastai 库中的学习率查找工具中的信息进行定期调整:

在学习率查找程序中,学习率最初被设置为非常小的值(1e-5),并且在一个时期的过程中被迭代地增加,直到高的上限值(10)。在每个阶段,损失都被记录下来,这样就可以绘制出如下图。解释这个图是一个启发式的过程,在 fastai 课程中有更详细的解释,但要点是:选择一个学习率,在这个学习率上,损失仍在快速减少,尚未稳定下来。在这种情况下,选择 1e-2 将是合理的选择。

对于这种规模的数据集(以及相关的长训练时间),您可能需要保持和依赖已训练模型的能力,以便可以在以后继续训练。完成后,您还需要将学习到的嵌入保存到磁盘,以便进行后期处理。下面的代码将处理这些项目。

一旦网络得到了令人满意的训练,你将拥有健壮的电影和用户嵌入,随时可以用于各种实际任务。使用由该神经网络学习的基于协作的电影嵌入,由余弦相似性定义的与“指环王:指环王”最相似的前 20 部电影如下所示。

将这一切结合在一起

如上所述,《指环王》的合作推荐似乎主要是受欢迎和高度评价的大片,具有强烈的动作和冒险主题。内容推荐似乎受受欢迎程度的影响较小,可能包括一些奇幻类的隐藏宝石。

为了集成来自内容和协作模型的结果,我们简单地对余弦相似性进行平均。通过集合协作和基于内容的结果,我们能够提出有希望从两种方法的优势中吸取的建议。以下是《指环王》的合集推荐。

结论

在这篇文章中,我们开发了一个电影到电影的混合内容协作推荐系统。我们讨论并举例说明了基于内容和协作的方法的优缺点。我们还展示了如何使用深度学习而不是传统的矩阵分解方法来开发推荐系统。

在我们的观察中,协作过滤器擅长跨越不同类型的差距,并持续推荐高评级的电影。内容过滤器善于识别非常相似的风格(例如,以中世纪为背景的魔法王国)和较少观看的、潜在隐藏的宝石电影。混合方法的目的是结合两种系统的优势。

我们还找到了一些我们想看的电影!如果你想看你选择的特定电影的推荐,请随时在 Twitter、LinkedIn 或电子邮件上联系。

Adam Lineberry

Claire Longo

Adam 是丹佛的一名数据科学家,对深度学习、大数据、一级方程式赛车和滑雪感兴趣。

Claire 是丹佛的一名数据科学家,在时尚行业工作。她的研究兴趣是推荐系统。

使用卷积神经网络创建电影推荐器

原文:https://towardsdatascience.com/creating-a-movie-recommender-using-convolutional-neural-networks-be93e66464a7?source=collection_archive---------7-----------------------

在网飞上看完你最喜欢的电视剧或 YouTube 上的视频后,你必须做出一个重要的决定,接下来我该看什么?很多时候你都是从自己喜欢的视频点播平台的推荐系统得到一些帮助。这些平台花费大量的时间和精力(参见:网飞推荐系统:算法、商业价值和创新 & 用于 YouTube 推荐的深度神经网络)让你的用户体验尽可能愉快,增加你在平台上的总观看时间。但是即使有这些帮助,你如何选择呢?我通常会选择最吸引我的视频缩略图/海报。考虑到这一点,我构建了一个电影推荐器,它只接受电影缩略图/海报作为输入。让我们看看那是什么样子。

主旨

主要思想是创建电影海报图像数据集,并从在 ImageNet 上训练的预训练卷积神经网络 (ConvNet)中提取特征。我将使用提取的特征来推荐给定目标电影海报的 5 个最相似的电影海报。为了验证这种方法,我将使用鞋子图像数据集做同样的事情。

第一步:网络抓取电影海报

首先我需要一些电影海报,我决定使用 Kaggle 上的 TMDB 5000 电影数据集。有了数据集中提供的信息,我使用 web scraping 通过 Python 库 BeautifulSoup 从 IMDB 下载海报图像。我将海报 id 添加到每张图片的名称中,并将所有 4911 张成功下载的图片存储在一个文件夹中。

Image from: https://www.kaggle.com/tmdb/tmdb-movie-metadata/data

这就是完整的 WebScraping.ipynb 笔记本。

第二步:推荐人

我对基于海报图像的视觉方面寻找类似的电影海报感兴趣,因此我将使用 ConvNets。就视觉识别而言,ConvNets 目前是首选型号。对于我的推荐者,我不会从零开始训练一个 ConvNet。但是在 ImageNet 上使用预先训练好的模型。从而节省时间并具有开箱即用的最新模型。这就叫“迁移学习”。推荐者我会用 Inception-v3 模型。选择这种型号的原因之一是与 VGG16 或 VGG19 型号相比,输出阵列相对较小。使得处理内存中的所有内容变得更加容易。我感兴趣的是模型学习的特征,而不是类别概率。识别形状、图案等的预学习层。希望能提出有意义的建议。出于这个原因,我删除了输出层,并将 ConvNet 的其余部分作为电影海报的特征提取器。请参见此处的示例,了解每个图层或节点基于面部图像数据集可以了解的内容。

Image from: https://cdn.edureka.co/blog/wp-content/uploads/2017/05/Deep-Neural-Network-What-is-Deep-Learning-Edureka.png

对于推荐器的实现,我使用带有 TensorFlow 的 Keras 作为后端。对于数据集中的每个图像,我保存模型最后一个隐藏层的展平输出数组。有了这个新的特征数组,我可以根据数组之间的欧氏距离来计算目标图像/海报的 x 个最近邻。为了将结果与基线进行比较,我还将使用给定目标海报的原始展平图像数组显示 x 个最近邻。我不会分享我所有的代码,但会在这个博客中分享一些代码片段。

选择模型特征图层:

from keras.models import Model
from keras.applications.inception_v3 import InceptionV3
from keras.models import Modelselectedlayer = "..."
base_modelv3 = InceptionV3(weights='imagenet', include_top=False)
model= Model(inputs=base_modelv3.input,  outputs=base_modelv3.get_layer(selectedlayer).output)

从图像中提取特征:

from os import listdir
from keras.preprocessing.image import load_img
from keras.preprocessing.image import img_to_arraydef preprocess_input(x):
    x /= 255.
    x -= 0.5
    x *= 2.
    return xdef load_photos_predict(directory):
 images = []
 for name in listdir(directory):
  # load an image from file
  filename = directory + '/' + name
  image = load_img(filename, target_size=(299, 299))
  # convert the image pixels to a numpy array
  image = img_to_array(image)
  # reshape data for the model
  image = np.expand_dims(image, axis=0)
  # prepare the image for the  model
  image = preprocess_input(image)
  # get image id
  image_id = name.split('.')[0]
  feature = model.predict(image).ravel()
  images.append((image_id, image, feature))
 return images

查找最近邻居:

from sklearn.neighbors import NearestNeighbors
import pandas as pd
import numpynn_num = 6
X = list(results[“modelfeatures”])nbrs = NearestNeighbors(n_neighbors=nn_num, algorithm=’ball_tree’, metric=”euclidean”, n_jobs = -1).fit(X)

第三步:结果

让我们来看看基于詹姆斯·邦德电影《幽灵》的推荐。我将展示基于原始展平图像阵列的 5 个推荐,以及基于 Inception-v3 模型的提取特征阵列的 5 个推荐。

Recommender based on raw image array vs ConvNet features for James Bond — Specter

尽管我没有定义明确的评估标准,也没有利用 A/B 测试来确定哪种推荐方法是最好的,但直觉上,模型结果似乎稍好一些。该模型甚至推荐了一部额外的邦德电影《永不说永不》作为它的第四个推荐。让我们再看一部电影,印第安纳琼斯和水晶头骨王国。

Recommender based on raw image array vs ConvNet features for Indiana Jones and the Kingdom of the Crystal Skull

该模型推荐印第安纳琼斯和最后的十字军东征作为其第一推荐,这看起来很好。其他的就不太合适了,但是 ConvNet 特性的表现似乎又比只使用原始图像数组作为输入要好。

显示结果功能:

import matplotlib.pyplot as pltdef udfsimular(indices, table):
 neighbours = []
 for i in range(len(indices)): 
 t = indices[i]
 idv = table[(table.index == t)].iloc[0][‘ID’]
 neighbours.append(idv)
 return neighboursdef udfidfpathh(ids,directory):
 paths = []
 for i in range(len(ids)): 
 t = ids[i]
 filename = wdir + directory + t + “.jpg”
 paths.append(filename)
 return pathsdef show5recommendations(name, table, NearestN,  idnr, directory, columnfeature):
    key = table[(table.ID == idnr)].iloc[0][columnfeature]
    distances, indices = NearestN.kneighbors(key)
    listindices = pd.DataFrame(indices).values.tolist()
    listindices2 = listindices[0]
    ids = udfsimular(listindices2, table)
    paths2 = udfidfpathh(ids,directory)
    fig, ((ax1, ax2, ax3, ax4, ax5, ax6)) = plt.subplots(nrows=1, ncols=6, sharex=True, sharey=True, figsize=(14,3))
    # Doing each of these manually (ugh)
    ax1.imshow(mpimg.imread(paths2[0]))
    ax1.set_title(r"$\bf{" + str(name) + "}$"+"\n Targer:\n"+ ids[0])
    ax1.set_yticklabels([])
    ax2.imshow(mpimg.imread(paths2[1]))
    ax2.set_title("Rec 1:\n"+ ids[1])
    ax3.imshow(mpimg.imread(paths2[2]))
    ax3.set_title("Rec 2:\n"+ ids[2])
    ax4.imshow(mpimg.imread(paths2[3]))
    ax4.set_title("Rec 3:\n"+ ids[3])
    ax5.imshow(mpimg.imread(paths2[4]))
    ax5.set_title("Rec 4:\n"+ ids[4])
    ax6.imshow(mpimg.imread(paths2[5]))
    ax6.set_title("Rec 5:\n"+ ids[5])

步骤 4:使用鞋子图像验证推荐器

既然我们已经看到了它对电影海报的效果,让我们使用一个不同的数据集。像亚马逊、Zalando 和其他网上商店这样的网站使用类似的技术向你推荐产品。例如,你要找的商品缺货,他们想向你推荐类似的产品。所以让我们用鞋的图像。我使用的数据集是从 Zappos50K 的收集的目录图像的 UT Zappos50K 。我用了 1882 张鞋子图片。

因此,让我们在这个数据集上重复同样的方法,看看“黑色开口高跟鞋”的结果是什么:

Recommender based on raw image array vs ConvNet features for a black open high heel shoe

基于提取的特征,模型显然学会了区分鞋子的不同图案。而正常阵列的建议显然不知道什么是开鞋。运动鞋怎么样:

Recommender based on raw image array vs ConvNet features for a sneaker shoe

再次推荐。

那么,为什么这些结果比电影海报的结果要好呢?

Inception-v3 模型在 ImageNet 上进行训练,以区分 1000 个类预测。模型被训练的图像每个图像有一个对象/类。它接受训练的 1000 个班级中有一个甚至被称为“跑鞋”。预测每个图像的一个对象是模型被训练的目的,也应该是模型做得最好的。而电影海报在对象、文本等的数量上要复杂得多。因此,使用在不同图像数据集上训练的模型可以为电影海报产生更好的结果。

最后备注

正如我们所看到的,仅使用电影海报结合预先训练的 ConvNets 来创建电影推荐器确实会产生一些不错的推荐。结果略好于仅使用原始图像阵列。对于鞋子,这种方法已经显示了一些非常好的建议。看到 ConvNets 的纯视觉识别功能已经可以做什么是很有趣的。根据推荐器创建的预期目的或行业,这似乎是一个很好的附加功能,可以添加到用于开发最先进的推荐系统的功能集中。

在 Python 中使用 Tinn(C 语言中的微型神经网络)

原文:https://towardsdatascience.com/creating-a-python-interface-to-a-c-library-a-tutorial-using-tinn-nn-d935707dd225?source=collection_archive---------11-----------------------

使用 Python 和 ctypes 调用 Tinn(一个 200 行 c 语言的微型神经网络)的指南

Image by Author

在 SensiML 我们专注于构建机器学习工具,使开发人员能够轻松创建训练有素的模型并将其部署到嵌入式物联网设备上。在这篇文章中,我将向您展示如何将 Tinn(一个用标准 C 编写的微型神经网络)转换成一个共享库,然后从 Python 中调用它,就像它是一个原生 Python 函数一样。我们在 SensiML 使用这种方法来试验和构建面向嵌入式设备的 C 库,同时仍然在本地使用我们的 Python 数据科学工具包。

第一步。去下载 Tinn

如果你要完成这个教程,你需要 Tinn。可以从 GitHub 下载 Tinn。

如果您安装了 git,

git clone [https://github.com/glouw/tinn.git](https://github.com/glouw/tinn.git)

或者,访问该网站并通过单击克隆或下载它,然后选择下载 zip 文件来下载它。

第二步。将 Tinn 编译成共享库

为了调用 Tinn 函数,我们必须创建一个共享库。我已经创建了一个 makefile 来将 Tinn 库编译成一个共享库,你可以在这里找到它。用提供的文件替换 Tinn 文件夹中的 make 文件,将 cd 放入终端中的 Tinn 文件夹,然后运行 make。

cd tinnmake>>cc -std=c99 -fPIC -fno-builtin -Werror -I../include -I.   -c -o >>Tinn.o Tinn.c
>>making lib
>>ar rcs /Users/chrisknorowski/Software/tinn/libtinn.so Tinn.o
>>cc -shared -Wl,-o libtinn.so *.o

(注意:这是为 Linux/OSX 写的,如果你在 windows 上使用 ubuntu bash shell 来编译)

如果一切正常,目录中应该已经创建了一个名为 libtinn.so 的共享库文件。我们将在下一步中链接到这个共享库,以便直接调用这些函数。

第三步。使用 cTypes 创建 Python 接口

让我们首先看看 Tinn.h 文件,看看我们需要从 python 调用哪些函数。

我们将创建的第一个 python 接口是 Tinn 结构。我们可以使用 ctypes 做到这一点。结构类。该结构有两个属性需要填写, slotsfieldsslots 让我们将属性分配给 struct_tinn 类。fields 让我们描述每个变量使用哪种类型。

下面你可以看到 python Tinn struct 类,我称之为 struct_tinn 。使用 ctypes,我们可以指定 struct_tinn 中的所有变量类型,在本例中,是整数和指向浮点数组的指针。

既然我们已经创建了 Tinn 结构的 python 表示,我们需要导入并创建我们想要调用的 c 函数的 python 表示。对于本教程,它们是

有了这三个函数,我们将能够使用 Tinn 库进行初始化、训练和预测。接下来,我们使用 ctyles。CDLL 来导入我们在本教程的步骤 2 中创建的共享库。

对于我们想要调用的每个函数,我们必须将函数的输入和输出类型指定为 python ctypes。我们通过设置 cf_lib 函数的 argtypesrestype 属性来实现这一点。

第四步。使用 Python 训练 Tinn NN 识别数字

至此,我们已经创建了能够从 python 调用 Tinn 所需的所有 python 包装器对象。为了使用神经网络,我们仍然需要为 python 编写初始化、训练和预测方法。

让我们从用一组参数初始化 Tinn 对象的函数开始。我们将调用这个 init_tinn 并在我们的 nn 中传递我们想要的输入数、输出数和隐藏层数。

在这里你可以看到,在将所有的输入传递给 xtbuild 函数之前,我们将它们转换为 ctypes。现在可以通过调用我们的 python 函数来初始化 Tinn 对象。

Tinn = init_tinn(64,10,128)

因为它是一个 python 对象,所以我们可以动态地索引所有的 Tinn 属性。

Tinn.weights[0]
>> 0.15275835990905762
Tinn.biases[0]
>> -0.30305397510528564

接下来,让我们构建训练和预测函数。我们的 train 函数将接受 Tinn 对象、X 数字的数组、y 目标以及训练步长α。

预测函数将接受已定型的 Tinn 对象和要识别的输入向量,从 nn 返回具有最高可信度的预测值。

最后,让我们在标准的数据科学工作流中使用 python Tinn 函数来识别手写数字。我们将从 sklearn 导入数字数据,然后创建一个 80/20 分割的标准训练测试数据集。为了好玩,我们还循环学习率,看看什么样的学习率最适合这个数据集。最后,做一些预测来检验模型的准确性。

alpha: 0.1
             precision    recall  f1-score   support

          0       1.00      0.94      0.97        17
          1       0.80      0.73      0.76        11
          2       0.88      0.88      0.88        17
          3       0.71      0.71      0.71        17
          4       0.84      0.84      0.84        25
          5       1.00      0.95      0.98        22
          6       0.95      1.00      0.97        19
          7       0.94      0.84      0.89        19
          8       0.55      0.75      0.63         8
          9       0.81      0.84      0.82        25

avg / total       0.87      0.86      0.86       180

Image by author

总结

这篇博文到此结束。我们已经讨论了如何创建一个共享的 c 库,如何使用 ctypes 在 python 中实例化 c 结构和 c 函数。然后,我们使用 python shell 中的调用来初始化和训练 Tinn NN 模型。我希望你喜欢这个教程,如果你有任何问题,请随时给我留言或评论。如果您有兴趣为物联网设备构建智能传感器算法,并希望在传感器本地运行推理,请联系我们或访问我们的网站了解更多信息。

使用 TensorFlow 创建拼写检查

原文:https://towardsdatascience.com/creating-a-spell-checker-with-tensorflow-d35b23939f60?source=collection_archive---------2-----------------------

Soon typos will be a thing of the past!

机器学习最重要的一个方面是处理好干净的数据。自然语言进展项目存在使用人类书写的文本的问题,不幸的是,我们不擅长书写。想想在 Reddit 的帖子和评论数据集中会有多少拼写错误。出于这个原因,我认为一个非常有价值的项目是做一个拼写检查器,这将有助于缓解这些问题。

我们将在这个项目中使用的模型非常类似于我在我的文章“使用亚马逊评论的文本摘要”(两者都是 seq2seq 模型)中所写的模型,但是我添加了一些额外的代码行,以便可以使用网格搜索来调整架构和超参数,并且可以使用 TensorBoard 来分析结果。如果你想更详细地了解如何将 TensorBoard 添加到你的代码中,那么看看“用 TensorFlow 和 TensorBoard 预测电影评论情绪”。

本文的主要焦点将是如何为模型准备数据,我还将讨论模型的其他一些特性。我们将在这个项目中使用 Python 3 和 TensorFlow 1.1。数据由来自古腾堡计划的二十本流行书籍组成。如果你有兴趣扩大这个项目,使它更加准确,有数百本书可以在古腾堡项目上下载。另外,看看用这个模型可以做多好的拼写检查也是很有趣的。

查看完整代码,这里是其 GitHub 页面

为了让您预览这种模式的能力,这里有一些精选的示例:

  • 拼写 比较难,whch就是wyh你需要每天学习。
  • 拼写难,哪个为什么你需要每天学习。
  • 她在thcountryvrey为多莉卖命。
  • 对多莉来说,她在 T4 这个国家的最初几天非常艰难。
  • Thi 真是了不起impression ivthaat我们应该马上调查一下!**
  • 这个确实是令人印象深刻的**我们应该马上调查一下

为了让事情更有条理,我把所有我们会用到的书都放在他们自己的文件夹里,叫做“书”。下面是我们将用来加载所有书籍的函数:

***def load_book(path):
    input_file = os.path.join(path)
    with open(input_file) as f:
        book = f.read()
    return book***

我们还需要每本书的唯一文件名。

***path = './books/'
book_files = [f for f in listdir(path) if isfile(join(path, f))]
book_files = book_files[1:]***

当我们将这两个代码块放在一起时,我们将能够将所有书籍中的文本加载到一个列表中。

***books = []
for book in book_files:
    books.append(load_book(path+book))***

如果您想知道每本书有多少单词,您可以使用以下代码行:

***for i in range(len(books)):
    print("There are {} words in {}.".format(len(books[i].split()), book_files[i]))***

注:如果不包含 .split() ,则返回每本书的人物数量。

清理这些书的正文相当简单。因为我们将使用字符而不是单词作为模型的输入,所以我们不需要担心删除停用的单词,或者将单词缩短到它们的词干。我们只需要删除不想包含的字符和多余的空格。

***def clean_text(text):
    '''Remove unwanted characters and extra spaces from the text'''
    text = re.sub(r'\n', ' ', text) 
    text = re.sub(r'[{}[@_](http://twitter.com/_)*>()\\#%+=\[\]]','', text)
    text = re.sub('a0','', text)
    text = re.sub('\'92t','\'t', text)
    text = re.sub('\'92s','\'s', text)
    text = re.sub('\'92m','\'m', text)
    text = re.sub('\'92ll','\'ll', text)
    text = re.sub('\'91','', text)
    text = re.sub('\'92','', text)
    text = re.sub('\'93','', text)
    text = re.sub('\'94','', text)
    text = re.sub('\.','. ', text)
    text = re.sub('\!','! ', text)
    text = re.sub('\?','? ', text)
    text = re.sub(' +',' ', text) # Removes extra spaces
    return text***

我将跳过如何制作vocab_to_intint_to_vocab字典,因为这是非常标准的东西,你可以在这个项目的 GitHub 页面上找到。但是,我认为有必要向您展示输入数据中包含的字符:

***The vocabulary contains 78 characters.
[' ', '!', '"', '$', '&', "'", ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<EOS>', '<GO>', '<PAD>', '?', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']***

我们可以删除更多的特殊字符,或者让文本全部小写,但是我想让这个拼写检查器尽可能有用。

数据在输入模型之前会被组织成句子。我们将拆分每个时间段的数据,每个时间段后面跟一个空格(“.”).其中一个问题是,有些句子以问号或感叹号结尾,但我们没有考虑到这一点。幸运的是,我们的模型仍然能够学习问号和感叹号的用法,只要这两个句子加起来没有最大句子长度长。

仅举一个例子:

  • 今天是美好的一天。我想去海滩。(这将被分成两个输入句子)**
  • 今天天气好吗?我想去海滩。(这将是一个长输入句子)**
***sentences = []
for book in clean_books:
    for sentence in book.split('. '):
        sentences.append(sentence + '.')***

我在 loydhub.com 的 T2 使用 GPU 来训练我的模型(我强烈推荐他们的服务),这为我节省了几个小时的训练时间。尽管如此,为了正确地调优这个模型,仍然需要 30-60 分钟来运行一次迭代,这就是为什么我限制了数据,以便它不需要更长的时间。这当然会降低我们模型的准确性,但由于这只是个人项目,所以我不介意权衡。

***max_length = 92
min_length = 10good_sentences = []for sentence in int_sentences:
    if len(sentence) <= max_length and len(sentence) >= min_length:
        good_sentences.append(sentence)***

为了跟踪这个模型的性能,我将把数据分成训练集和测试集。测试集将由 15%的数据组成。

***training, testing = train_test_split(good_sentences, 
                                     test_size = 0.15, 
                                     random_state = 2)***

就像我最近的一些项目一样,我将按长度对数据进行排序。这导致一批句子的长度相似,因此使用较少的填充,并且模型将训练得更快。

***training_sorted = []
testing_sorted = []for i in range(min_length, max_length+1):
    for sentence in training:
        if len(sentence) == i:
            training_sorted.append(sentence)
    for sentence in testing:
        if len(sentence) == i:
            testing_sorted.append(sentence)***

也许这个项目最有趣/最重要的部分是将句子转换成有错误的句子的功能,这些句子将被用作输入数据。在这个函数中,错误以三种方式之一产生:

  • 两个字符的顺序将被交换(hlelo ~hello)
  • 会多加一个字母(heljlo ~ hello)
  • 一个字符不会打(helo ~hello)

三个错误中任何一个发生的可能性都是相等的,任何一个错误发生的可能性都是 5%。因此,平均每 20 个字符中就有一个包含错误。

***letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m',
           'n','o','p','q','r','s','t','u','v','w','x','y','z',]def noise_maker(sentence, threshold):

    noisy_sentence = []
    i = 0
    while i < len(sentence):
        random = np.random.uniform(0,1,1)
        if random < threshold:
            noisy_sentence.append(sentence[i])
        else:
            new_random = np.random.uniform(0,1,1)
            if new_random > 0.67:
                if i == (len(sentence) - 1):
                    continue
                else:
                    noisy_sentence.append(sentence[i+1])
                    noisy_sentence.append(sentence[i])
                    i += 1
            elif new_random < 0.33:
                random_letter = np.random.choice(letters, 1)[0]
                noisy_sentence.append(vocab_to_int[random_letter])
                noisy_sentence.append(sentence[i])
            else:
                pass     
        i += 1
    return noisy_sentence***

本文中我想向您展示的最后一件事是如何创建批处理。通常,人们会在训练他们的模型之前创建他们的输入数据,这意味着他们有固定数量的训练数据。然而,我们将在训练模型时创建新的输入数据,将noise_maker应用于每一批数据。这意味着对于每个时期,目标(正确的)句子将通过noise_maker被反馈,并且应该接收新的输入句子。使用这种方法,从某种稍微夸张的意义上来说,我们拥有无限量的训练数据。

***def get_batches(sentences, batch_size, threshold):

    for batch_i in range(0, len(sentences)//batch_size):
        start_i = batch_i * batch_size
        sentences_batch = sentences[start_i:start_i + batch_size]

        sentences_batch_noisy = []
        for sentence in sentences_batch:
            sentences_batch_noisy.append(
                noise_maker(sentence, threshold))

        sentences_batch_eos = []
        for sentence in sentences_batch:
            sentence.append(vocab_to_int['<EOS>'])
            sentences_batch_eos.append(sentence)

        pad_sentences_batch = np.array(
            pad_sentence_batch(sentences_batch_eos))
        pad_sentences_noisy_batch = np.array(
            pad_sentence_batch(sentences_batch_noisy))

        pad_sentences_lengths = []
        for sentence in pad_sentences_batch:
            pad_sentences_lengths.append(len(sentence))

        pad_sentences_noisy_lengths = []
        for sentence in pad_sentences_noisy_batch:
            pad_sentences_noisy_lengths.append(len(sentence))

        yield (pad_sentences_noisy_batch, 
               pad_sentences_batch, 
               pad_sentences_noisy_lengths, 
               pad_sentences_lengths)***

这个项目到此为止!尽管结果令人鼓舞,但这一模式仍有局限性。如果有人能按比例放大这个模型或改进它的设计,我将不胜感激!如果你有,请在评论中发表。新设计的一个想法是应用博览会的新 CNN 模式(它实现了最先进的翻译效果)。

感谢阅读,我希望你学到了新的东西!

从头开始用 Core ML 创建一个 IOS 应用程序!

原文:https://towardsdatascience.com/creating-an-ios-app-with-core-ml-from-scratch-b9e13e8af9cb?source=collection_archive---------1-----------------------

这个世界需要选择和决断——Re 创作者动漫。

在机器学习中,一切都始于模型,即进行预测或识别的系统。教计算机学习涉及一种机器学习算法,该算法具有要学习的训练数据。训练产生的输出通常被称为机器学习模型。有不同类型的机器学习模型解决相同的问题(例如物体识别),但使用不同的算法。神经网络、树集成、支持向量机就是这些机器学习算法中的一些。

机器学习就像一个迭代过程

首先,我们用公共模型进行实验,但是为了带来独特的市场价值和优势,我们希望我们的模型胜过其他模型。我们正在寻找的叫做 ML 反馈回路。Google 对 ML 特性的处理遵循以下模式:

  • 获取初始化数据(一次)
  • — — — — —
  • 标签数据
  • 火车模型
  • 试验模型
  • 在生产中运行模型
  • 获取新数据(并重复)

现在,对于一个移动应用程序,这个过程看起来像:

在上图中,移动应用程序似乎使用了 ML 创建的模型,但它是如何工作的呢?是的,这就是核心 ML 发挥作用的地方。

CoreML 怎么用?

核心 ML 是苹果公司的新机器学习框架。它为苹果设备带来了机器学习模型,并使开发人员能够轻松利用 ML。我们可以使用苹果或 c 准备的十几个模型转换来自流行的 ML 框架的开源模型,如 Keras 、 Caffe 或 scikit-learn 。

使用 CoreML 创建 IOS 应用程序的工作流程如下:

1-你需要使用一些 ML 框架创建一个数据模型,比如 Caffe,turi,Keras 等等。

2-为了安装名为 Core ML Tools 的 Python 框架,它将数据模型转换为 Core ML 格式。转换的结果是一个扩展名为 mlmodel 的文件。

3-就是这样,你用核心 ML 工具创建的模型,在你的移动应用程序中使用它。

软件要求

为了训练模型,我们需要一个 ML 框架。最流行的是谷歌开发的 Tensorflow 。它拥有来自社区、教程和大量开发者关注的最好支持。然而,当你更深入一点时,你可能会在 G ithub issue page 或处理一些数学问题或未记录代码的堆栈溢出中结束。与 web 应用程序或移动开发相比,ML 仍然是一个小婴儿,作为开发人员,你需要依赖它。安排一些额外的时间迷失在神秘的 ML 中。从像 Keras 这样的高级库开始也更容易。你可以在文章末尾查看一些培训教程的链接。

Tensorflow and Keras are the most common ML libraries

五金器具

很多人说我们需要一个 GPU 来训练一个模型。对于需要高精度或一些网络架构调整的项目来说,这是事实。如果我们需要 10 个类别的图像分类器,那么我们可以利用迁移学习,在标准 CPU 上在 10 分钟内微调我们的模型。然而,对于真正的生产应用程序,我们通常需要 GPU 的性能。我们已经尝试了几家云提供商,亚马逊 AWS g 2.2x 大型实例是一个不错的选择

开始吧!!

至此,您已经知道了使用机器学习创建 IOS 应用程序所需的必备工具,让我们开始吧!

管道设置:

要使用核心 ML 工具,第一步是在 Mac 上安装 Python。首先,下载 Anaconda (选择 Python 2.7 版本)。Anaconda 是一种在 Mac 上运行 Python 的超级简单的方法,不会引起任何问题。一旦安装了 Anaconda,就进入终端并键入以下内容:

conda install python=2.7.13conda update python

下一步是创建一个虚拟环境。在虚拟环境中,你可以用不同版本的 Python 或者其中的包来编写程序。要创建新的虚拟环境,请键入以下代码行。

conda create --name handwriting

当终端提示您时,

proceed ([y]/n)?

键入“y”表示是。恭喜你。现在,您有了一个名为手写的虚拟环境!

要激活该环境,请使用:source activate handwriting

最后,键入以下命令来安装核心 ML 工具和 Kera 的依赖项:

conda install pandas matplotlib jupyter notebook scipy opencvpip install -U scikit-learn==0.15
pip install -U coremltools
pip install tensorflow==1.1
pip install keras==2.0.6
pip install h5py

设计和培训网络

在这篇文章中,正如你注意到的,我将使用 Kera,就像我们例子中的 ML 框架一样。

好了,你应该创建一个名为train.py的 python 文件。在train.py内插入以下代码:

  • 首先让我们导入一些必要的库,并确保 TensorFlow 中的 keras 后端:
**import** numpy as np**import** keras**from** keras.datasets **import** mnist**from** keras.models **import** Sequential**from** keras.layers **import** Dense, Dropout, Flatten**from** keras.layers.convolutional **import** Conv2D, MaxPooling2D**from** keras.utils **import** np_utils# (Making sure) Set backend as tensorflow**from** keras **import** backend as KK.set_image_dim_ordering('tf')
  • 现在让我们为训练和测试准备数据集。
# Define some variablesnum_rows **=** 28num_cols **=** 28num_channels **=** 1num_classes **=** 10# Import data(X_train, y_train), (X_test, y_test) **=** mnist.load_data()X_train **=** X_train.reshape(X_train.shape[0], num_rows, num_cols, num_channels).astype(np.float32) **/** 255X_test **=** X_test.reshape(X_test.shape[0], num_rows, num_cols, num_channels).astype(np.float32) **/** 255y_train **=** np_utils.to_categorical(y_train)y_test **=** np_utils.to_categorical(y_test)
  • 设计培训模型。
# Modelmodel **=** Sequential()model.add(Conv2D(32, (5, 5), input_shape**=**(28, 28, 1), activation**=**'relu'))model.add(MaxPooling2D(pool_size**=**(2, 2)))model.add(Dropout(0.5))model.add(Conv2D(64, (3, 3), activation**=**'relu'))model.add(MaxPooling2D(pool_size**=**(2, 2)))model.add(Dropout(0.2))model.add(Conv2D(128, (1, 1), activation**=**'relu'))model.add(MaxPooling2D(pool_size**=**(2, 2)))model.add(Dropout(0.2))model.add(Flatten())model.add(Dense(128, activation**=**'relu'))model.add(Dense(num_classes, activation**=**'softmax'))model.compile(loss**=**'categorical_crossentropy', optimizer**=**'adam', metrics**=**['accuracy'])
  • 训练模型。
# Trainingmodel.fit(X_train, y_train, validation_data**=**(X_test, y_test), epochs**=**10, batch_size**=**200, verbose**=**2)
  • 通过移除缺失层为推理准备模型。
# Prepare model for inference**for** k **in** model.layers:**if** type(k) **is** keras.layers.Dropout:model.layers.remove(k)
  • 最后保存模型。
model.save('handWritten.h5')

然后,转到您之前打开的终端,键入以下内容:

python train.py

它将创建一个名为 手写的数据模型。

Keras 到 CoreML

要将您的模型从 Keras 转换到 CoreML,我们需要做一些额外的步骤。我们的深度学习模型期望 28×28 归一化灰度图像,并给出类别预测的概率作为输出。此外,让我们添加更多的信息到我们的模型,如许可证,作者等。让我们创建一个名为convert.py的文件,并插入以下代码:

import coremltoolsoutput_labels = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']scale = 1/255.coreml_model = coremltools.converters.keras.convert('./handWritten.h5',input_names='image',image_input_names='image',output_names='output',class_labels= output_labels,image_scale=scale)coreml_model.author = 'Gerardo Lopez Falcon'coreml_model.license = 'MIT'coreml_model.short_description = 'Model to classify hand written digit'coreml_model.input_description['image'] = 'Grayscale image of hand written digit'coreml_model.output_description['output'] = 'Predicted digit' coreml_model.save('handWritten.mlmodel')

回到终端,键入以下内容:python convert.py。它为我们创建了一个名为 handled . ml model 的文件(现在,我们可以在 IOS 应用程序中使用这个文件)。

将模型集成到 Xcode 中

现在我们到了最后一步,将刚刚转换的模型集成到 Xcode 项目中。打开 starter 项目,根据您目前所学,我要求您将核心 ML 模型集成到应用程序中。

第一步是将handWritten.mlmodel 拖放到我们的 Xcode 项目中。确保选中了目标成员资格框。

现在,转到下面实例化 IBOutlet 的 ViewController,并从手写类型创建一个对象:

let model = handWritten()

之后,进入let pixelBuffer = .....插件下方的 tappedDetect 功能:

let output = try? model.prediction(image: pixelBuffer!)

我们刚刚定义了常数预测等于模型预测的数据。

仅此而已!构建并运行应用程序。这些是应用程序在 iPhone 7 上测试的结果。

结论

既然您已经知道了如何转换数据模型,那么您可能想知道在哪里可以找到数据模型。一个简单的谷歌搜索会给你大量的结果。您可以找到几乎任何类别的数据模型,例如不同类型的汽车、植物、动物,甚至还有一个模型可以告诉您最像哪个名人。这里有几个地方可以让你开始!

  • 咖啡馆模型动物园
  • UCI 机器学习知识库
  • 深度学习数据集

如果您找不到支持您需求的模型,您可能想知道是否可以创建自己的数据模型。这是可以做到的,但很难做到。如果你觉得自己已经准备好迎接挑战,我建议你从 Scikit-Learn 或 TensorFlow 的主页开始。

谢谢分享,如果你能把这篇文章分享给其他人,我会很感激的。我希望你的掌声:D

用 MCMC 创建动画

原文:https://towardsdatascience.com/creating-animations-with-mcmc-4458ab2b6cc3?source=collection_archive---------7-----------------------

马尔可夫链蒙特卡罗(MCMC)是贝叶斯统计中一种广泛流行的技术。它用于后验分布抽样,因为分析形式通常是不可跟踪的。然而,在本帖中,我们将使用它从静态图像/徽标中生成动画。顺便说一句,这可能是对 MCMC 和拒绝采样的介绍。这个想法是基于一个伟大的开源包 imcmc ,它是建立在 PyMC3 之上的。

预赛

虽然 MCMC 适用于任何维度,但我们将集中讨论 2D 分布。为什么?好吧,看看下面的图表。

我们从一个 RGB 图像(最好是一个简单的标志)开始,将其转换为灰度,并根据强度为每个像素分配一个概率。在我们的例子中,像素越暗,概率越高。这导致了离散 2D 分布

一旦我们有了这个分布,我们就可以从中抽取样本(像素)。一般来说,我们寻找以下两个属性:

  1. 样本确实来自目标分布
  2. 样品的连续及其可视化在美学上是令人愉悦的

虽然第二个属性总是在旁观者的眼中,但第一个属性可以通过使用适当的采样算法来实现。具体来说,让我们仔细看看 3 种非常适合当前问题的采样算法:拒绝、Gibbs 和 Metropolis-Hastings 采样。

拒绝抽样

第一种方法属于 IID ( 独立同分布)抽样方法。这实质上意味着当前样本对下一个样本没有影响。该算法假设我们能够从所谓的建议分布中进行采样。可以使用许多不同的建议分布,但最常见的是均匀(有限支持)和正态(无限支持)分布。

为了最大限度地降低拒绝样本的可能性,我们必须选择与我们的目标尽可能相似的建议分布。此外,我们希望比例常数 M 尽可能低。见下图(1D)方案。

在我们的设置中,我们可以采用 2D 均匀分布作为建议。

在较低的维度中,拒绝采样表现得非常好。然而,随着维数的增加,它遭受了臭名昭著的维数灾难。幸运的是,2D 没有被诅咒,因此拒绝采样是一个很好的选择。

吉布斯采样

吉布斯采样属于第二类采样器,它们通过构建马尔可夫链来生成样本。因此,这些样本不是独立的。事实上,在链达到稳定分布之前,它们甚至不是同分布的。因此,通常的做法是丢弃前 x 个样本,以确保链“忘记”初始化(老化)。

算法本身假设我们能够沿着目标的每个维度从条件分布中抽取样本。每当我们从这些单变量分布中取样时,我们都以剩余成分的最新值为条件。

大都会-黑斯廷斯抽样

与 Gibbs 类似,Metropolis-Hastings 采样也创建了马尔可夫链。但是,它更一般(Gibbs 是特例),更灵活。

给定当前样本,建议分布为我们提供了一个新样本的建议。然后,我们通过检查它比当前样本的可能性大(小)多少来评估其合格性,并考虑建议分布对该样本的可能偏差。综合所有因素,我们计算接受新样本的概率,然后让随机性做出决定。

不言而喻,建议分配的作用是至关重要的,并将影响性能和收敛性。一个更常见的选择是使用当前状态中心的法线。

动画片

最后来看一些结果。对于每个徽标,运行 Rejection 和 Metropolis-Hastings(使用 PyMC3)并创建可视化(gif)。最重要的超参数:

  • 老化样品:500 个
  • 样品:10000 个

对于多模态分布,Metropolis-Hastings 可能会陷入某些区域。这是一个非常常见的问题,可以通过改变方案或增加/加长样品链来解决。自然,我们也可以冒险到野外使用一些其他的采样器。

如果你想更深入地挖掘并查看源代码,请查看本 笔记本

参考

1.imcmc:https://github.com/ColCarroll/imcmc)
2。pymc 3:https://github.com/pymc-devs/pymc3)3。凯文·p·墨菲。2012.机器学习:概率观点。麻省理工学院出版社。

原载于 2018 年 7 月 11 日jank repl . github . io

用 Conv 神经网络创作艺术

原文:https://towardsdatascience.com/creating-art-with-conv-neural-nets-3ea5ebd7ef?source=collection_archive---------1-----------------------

在这篇文章中,我正在使用卷积神经网络来制作一些精美的艺术品!

迄今为止,艺术一直是最好留给创意者的想象作品。艺术家有一种独特的方式来表达自己和他们生活的时代,通过独特的镜头,具体到他们看待周围世界的方式。无论是达芬奇和他令人惊叹的作品,还是梵高和他扭曲的世界观,艺术总是激励着一代又一代的人。

技术总是激励艺术家去突破界限,探索已经完成的事情之外的可能性。第一部电影摄影机不是作为辅助艺术的技术而发明的,而仅仅是捕捉现实的工具。显然,艺术家们对它的看法不同,它催生了整个电影和动画产业。对于我们创造的每一项主要技术来说都是如此,艺术家们总能找到创造性地使用这一新颖工具的方法。

随着机器学习的最新进展,我们可以在几分钟内创作出令人难以置信的艺术作品,而这在大约一个世纪前可能需要专业艺术家数年才能完成。机器学习创造了一种可能性,即在让媒体与艺术家合作的同时,以至少 100 倍的速度制作艺术作品的原型。这里的美妙之处在于,这一新的技术进步浪潮将通过升级手边的工具来增强艺术的创作和观赏方式。

介绍

在这里,我将使用 python 来获取任何图像,并将其转换为我选择的任何艺术家的风格。谷歌在 2015 年发布了一款名为“深度梦想”的类似产品,互联网对它产生了强烈的热情。他们本质上训练了一个卷积神经网络,该网络对图像进行分类,然后使用一种优化技术来增强输入图像中的模式,而不是基于网络所学的自身权重。此后不久,“Deepart”网站出现了,它允许用户点击鼠标将任何图像转换成他们选择的绘画风格!

那么这是如何工作的呢?

为了理解这个被称为样式转换过程的“魔法”是如何工作的,我们将使用 TensorFlow 后端在 Keras 中编写我们自己的脚本。我将使用一个基础图像(我最喜欢的动物的照片)和一个风格参考图像。我的剧本将使用文森特·梵高的《星夜》作为参考,并将其应用于基础图像。这里,我们首先导入必要的依赖项:

**from** **__future__** **import** print_function

**import** **time**
**from** **PIL** **import** Image
**import** **numpy** **as** **np**

**from** **keras** **import** backend
**from** **keras.models** **import** Model
**from** **keras.applications.vgg16** **import** VGG16

**from** **scipy.optimize** **import** fmin_l_bfgs_b
**from** **scipy.misc** **import** imsave

所以我们将这些图像输入神经网络,首先将它们转换成所有神经网络的实际格式,张量。Keras backend Tensorflow 中的变量函数相当于 tf.variable。它的参数将是转换为数组的图像,然后我们对样式图像做同样的事情。然后,我们创建一个组合图像,通过使用占位符将它初始化为给定的宽度和高度,可以在以后存储我们的最终结果。

以下是内容图片:

height = 512
width = 512

content_image_path = 'images/elephant.jpg'
content_image = Image.open(content_image_path)
content_image = content_image.resize((height, width))
content_image

Content Image (Elephants are cool)

在这里,我加载了样式图像:

style_image_path = '/Users/vivek/Desktop/VanGogh.jpg'
style_image = Image.open (style_image_path)
style_image = style_image.resize((height, width))
style_image

Style Image (gotta pull out the classic)

接下来,我们转换这两个图像,使它们具有适合数字处理的形式。我们添加了另一个维度(除了高度、宽度和正常的 3 个维度之外),这样我们以后可以将两个图像的表示连接成一个公共的数据结构:

content_array = np.asarray(content_image, dtype='float32')
content_array = np.expand_dims(content_array, axis=0)
print(content_array.shape)

style_array = np.asarray(style_image, dtype='float32')
style_array = np.expand_dims(style_array, axis=0)
print(style_array.shape)

我们将继续使用 VGG 网络。Keras 已经很好地包装了这个模型,以便我们在前进的过程中可以轻松使用。VGG16 是一个 16 层卷积网,由牛津大学的视觉几何小组创建,在 2014 年赢得了 ImageNet 竞赛。这里的想法是,对于成千上万不同图像的图像分类,预先训练的 CNN 已经知道如何在容器图像中编码信息。我已经了解了每一层的特征,这些特征可以检测某些一般化的特征。这些是我们将用来执行风格转移的功能。我们不需要这个网络顶部的卷积块,因为它的全连接层和 softmax 函数通过挤压维度特征图和输出概率来帮助分类图像。我们不仅仅是对转移进行分类。这本质上是一种优化,我们有一些损失函数来衡量我们将试图最小化的误差值。在这种情况下,我们的损失函数可以分解为两部分:

1)内容损失我们将总损失初始化为零,并将这些中的每一个加到其上。首先是内容损失。图像总是有一个内容组件和一个样式组件。我们知道 CNN 学习的特征是按照越来越抽象的成分的顺序排列的。由于更高层的特征更抽象,比如检测人脸,我们可以把它们和内容联系起来。当我们通过网络运行我们的输出图像和我们的参考图像时,我们从我们选择的隐藏层获得两者的一组特征表示。然后我们测量它们之间的欧几里德距离来计算我们的损失。

2)风格损失这也是我们的网络的隐藏层输出的函数,但是稍微复杂一些。我们仍然通过网络传递两幅图像来观察它们的激活,但是我们没有直接比较原始激活的内容,而是增加了一个额外的步骤来测量激活之间的相关性。对于网络中给定层的两个活化图像,我们采用所谓的格拉姆矩阵。这将测量哪些功能倾向于一起激活。这基本上代表了不同特征在图像的不同部分同时出现的概率。一旦有了这些,我们就可以将这种风格损失定义为参考图像和输出图像之间的 gram 矩阵之间的欧几里德距离,并将总风格损失计算为我们选择的每一层的风格损失的加权和。

既然我们有了损失,我们需要定义输出图像相对于损失的梯度,然后使用这些梯度迭代地最小化损失。

我们现在需要对输入数据进行处理,以匹配在 Simonyan 和 Zisserman (2015) 介绍 VGG 网络模型的论文中所做的工作。

为此,我们需要执行两个转换:

  1. 从每个像素中减去平均 RGB 值(之前在 ImageNet 训练集上计算的,并且很容易从谷歌搜索中获得)。
  2. 将多维数组的排序从 RGB 翻转到 BGR (文中使用的排序)。
content_array[:,:,:,0] -= 103.99
content_array[:, :, :, 1] -= 116.779
content_array[:, :, :, 2] -= 123.68
content_array = content_array[:, :, :, ::-1]

style_array[:, :, :, 0] -= 103.939
style_array[:, :, :, 1] -= 116.779
style_array[:, :, :, 2] -= 123.68
style_array = style_array[:, :, :, ::-1]

现在我们准备使用这些数组来定义 Keras 后端(TensorFlow 图)中的变量。我们还引入了一个占位符变量来存储组合图像,它保留了内容图像的内容,同时合并了样式图像的样式。

content_image = backend.variable(content_array)
style_image = backend.variable(style_array)
combination_image = backend.placeholder((1, height, width, 3))

现在,我们将所有这些图像数据连接成一个张量,用于处理 Kera 的 VGG16 模型。

input_tensor = backend.concatenate([content_image,
                                    style_image,
                                    combination_image], axis = 0)

如前所述,由于我们对分类问题不感兴趣,我们不需要完全连接的层或最终的 softmax 分类器。我们只需要下表中用绿色标记的模型部分。

对我们来说,访问这个截断的模型是微不足道的,因为 Keras 附带了一组预训练的模型,包括我们感兴趣的 VGG16 模型。注意,通过在下面的代码中设置include_top=False,我们不包括任何完全连接的层。

**import** **h5py**
model = VGG16(input_tensor=input_tensor, weights='imagenet',
              include_top=**False**)

从上表可以清楚地看出,我们正在使用的模型有很多层。Keras 对这些层有自己的名字。让我们把这些名字列一个表,这样我们以后可以很容易地引用各个层。

layers = dict([(layer.name, layer.output) **for** layer **in** model.layers])
layers

我们现在选择重量,可以这样做:

content_weight = 0.025
style_weight = 5.0
total_variation_weight = 1.0

我们现在将使用由我们的模型的特定层提供的特征空间来定义这三个损失函数。我们首先将总损耗初始化为 0,然后分阶段增加。

loss = backend.variable(0.)

现在内容丢失:

**def** content_loss(content, combination):
    **return** backend.sum(backend.square(combination - content))

layer_features = layers['block2_conv2']
content_image_features = layer_features[0, :, :, :]
combination_features = layer_features[2, :, :, :]

loss += content_weight * content_loss(content_image_features,
                                      combination_features)

和风格损失:

**def** gram_matrix(x):
    features = backend.batch_flatten(backend.permute_dimensions(x, (2, 0, 1)))
    gram = backend.dot(features, backend.transpose(features))
    **return** gram**def** style_loss(style, combination):
    S = gram_matrix(style)
    C = gram_matrix(combination)
    channels = 3
    size = height * width
    **return** backend.sum(backend.square(S - C)) / (4\. * (channels ** 2) * (size ** 2))

feature_layers = ['block1_conv2', 'block2_conv2',
                  'block3_conv3', 'block4_conv3',
                  'block5_conv3']
**for** layer_name **in** feature_layers:
    layer_features = layers[layer_name]
    style_features = layer_features[1, :, :, :]
    combination_features = layer_features[2, :, :, :]
    sl = style_loss(style_features, combination_features)
    loss += (style_weight / len(feature_layers)) * sl

最后是总变异损失:

**def** total_variation_loss(x):
    a = backend.square(x[:, :height-1, :width-1, :] - x[:, 1:, :width-1, :])
    b = backend.square(x[:, :height-1, :width-1, :] - x[:, :height-1, 1:, :])
    **return** backend.sum(backend.pow(a + b, 1.25))

loss += total_variation_weight * total_variation_loss(combination_image)

现在,我们继续定义解决优化问题所需的梯度:

grads = backend.gradients(loss, combination_image)

然后,我们引入一个赋值器类,它在一次运算中计算损失和梯度,同时通过两个独立的函数 loss 和 grads 检索它们。这样做是因为 scipy.optimize 需要单独的损失和梯度函数,但是单独计算它们是低效的。

outputs = [loss]
outputs += grads
f_outputs = backend.function([combination_image], outputs)

**def** eval_loss_and_grads(x):
    x = x.reshape((1, height, width, 3))
    outs = f_outputs([x])
    loss_value = outs[0]
    grad_values = outs[1].flatten().astype('float64')
    **return** loss_value, grad_values

**class** **Evaluator**(object):

    **def** __init__(self):
        self.loss_value = **None**
        self.grads_values = **None**

    **def** loss(self, x):
        **assert** self.loss_value **is** **None**
        loss_value, grad_values = eval_loss_and_grads(x)
        self.loss_value = loss_value
        self.grad_values = grad_values
        **return** self.loss_value

    **def** grads(self, x):
        **assert** self.loss_value **is** **not** **None**
        grad_values = np.copy(self.grad_values)
        self.loss_value = **None**
        self.grad_values = **None**
        **return** grad_values

evaluator = Evaluator()

现在我们终于准备好解决我们的优化问题。这个组合图像从(有效)像素的随机集合开始,我们使用 L-BFGS 算法(一种比标准梯度下降收敛快得多的拟牛顿算法)对其进行迭代改进。我们在 8 次迭代后停止,因为输出对我来说看起来很好,损失停止显著减少。

x = np.random.uniform(0, 255, (1, height, width, 3)) - 128.

iterations = 8

**for** i **in** range(iterations):
    print('Start of iteration', i)
    start_time = time.time()
    x, min_val, info = fmin_l_bfgs_b(evaluator.loss, x.flatten(),
                                     fprime=evaluator.grads, maxfun=20)
    print('Current loss value:', min_val)
    end_time = time.time()
    print('Iteration **%d** completed in **%d**s' % (i, end_time - start_time))

如果你像我一样在笔记本电脑上工作,那就去吃顿大餐吧,因为这需要一段时间。这是上一次迭代的输出!

x = x.reshape((height, width, 3))
x = x[:, :, ::-1]
x[:, :, 0] += 103.939
x[:, :, 1] += 116.779
x[:, :, 2] += 123.68
x = np.clip(x, 0, 255).astype('uint8')

Image.fromarray(x)

Merger!

整洁!我们可以通过改变两幅图像、它们的大小、我们的损失函数的权重等等来继续玩这个游戏。重要的是要记住,我的 MacBook air 仅运行了 8 次迭代就花了大约 4 个小时。这是一个非常 CPU 密集型的过程,因此在扩展时,这是一个相对昂贵的问题。

感谢阅读!

用 Python 创建漂亮的 Twitter 图表

原文:https://towardsdatascience.com/creating-beautiful-twitter-graphs-with-python-c9b73bd6f887?source=collection_archive---------11-----------------------

想学两个有用的 Python 包?想在一张痛苦而可悲的图表中想象出所有没有在 Twitter 上关注你的人吗?请继续阅读。

Follows between players on all 30 NBA teams

编程意义上的图是顶点和边的集合,而不是条形图或散点图——但是你已经知道了。图表是强大的工具,有可能直观地交流复杂的关系。此外,他们可以很漂亮。为什么不看看我们能从推特上推断出什么关系呢?

数据收集

Twitter 已经有一个 Python API,python-twitter,所以我们不必自己发出 HTTP 请求和解析响应。为了使用它,我们必须通过 Twitter 创建一些应用程序凭证。我发现这是一个相当轻松的过程,所以我会简单地让你参考 python-twitter 的优秀指南来了解如何做到这一点。你只需要一个 Twitter 账户,不需要任何特权。

有了凭证,我们就可以创建 Twitter API 的一个实例并开始工作。

Add your tokens to get an api instance

我是一名 NBA 球迷,所以在这次演示中,我将在 Twitter 上绘制球员之间的联系。这是一个有趣的数据集,因为联盟有一个非常活跃的社交媒体存在,每个球员都很容易获得属性,如合同大小,球队和位置。你可以对任何你感兴趣的 Twitter 用户遵循同样的步骤,无论是名厨、音乐艺术家还是#QAnon 海报。

NBA 有 30 支球队,每支球队大约有 15 名球员。我并不热衷于手动查找 450 个 Twitter 句柄来将它们输入 API,尤其是如果我可以通过编程来完成的话。Basketball-reference.com 来救我了。下面我找到了 2018-19 赛季所有签约球员的表格。

[## 2018-19 赛季 NBA 球员合同| Basketball-Reference.com

当前 NBA 球员合同概要

www.basketball-reference.com](https://www.basketball-reference.com/contracts/players.html#player-contracts::none)

有了这个玩家名字的列表,我使用 API 来搜索相应的 Twitter-id,并将每个玩家的所有属性捆绑在一个顶点列表中,称为 V. 如果你已经有了一个屏幕名称或 Twitter-id 的列表,这可能是多余的,但如果你没有,下面是代码:

毕竟, V 将是一个列表列表,我将其作为一个. csv 文件转储,其中包含| TWITTER ID |用户名|昵称|团队列。

nba_verts.csv42562446,Stephen Curry,StephenCurry30, GSW
53853197,Chris Paul,CP3, HOU
23083404,LeBron James,KingJames, LAL
...

我知道我在寻找知名度高的运动员,所以我只在搜索中包括经过验证的用户,并假设搜索的第一个结果是正确的——事实证明并不总是如此。最后,我迭代了 V ,并使用 GetFriendIDs() API 调用来查找每个 Twitter-id 的好友。朋友是用户正在关注的人。如果朋友处于 V 状态,这是我用字典检查过的一个条件,我将这个边输出给另一个。csv 文件。

nba_edges.csv42562446,53643297
42562446,59297233
42562446,28066436
42562446,46704247
42562446,34430522
...

图是顶点和边的集合,现在我们两者都有了;我们准备好尽我们的职责了。

igraph 可视化

此时,你可以简单地将你的顶点和边列表加载到你最喜欢的图形绘制软件中(我的是 cytoscape )。但那一点都不好玩。让我们看看在严格遵守 Python 的情况下我们能做些什么。

Python-igraph 是我选择的包。它有相当多的依赖项,安装起来可能不像它的 R 实现那样流畅,但是一旦你掌握了它的语法,它会得到很好的维护并且非常强大。我发现用 Python 2.7 安装是最容易的,在一个 Jupyter 笔记本中使用也是最容易的。

让我们用我们的边和顶点做一个图。出于演示的目的,我将只包括前 10 名收入最高的球员。

from igraph import *#this info read in from our .csv files
E          = [("42562446","53643297"), ("42562446","59297233"),...]
V          = ["42562446", "53853197", "23083404",...]
user_names = ["Stephen Curry", "Chris Paul", "LeBron James",...]
scrn_names = ["StephenCurry30", "CP3", "KingJames",...]g = Graph(directed=True)
g.add_vertices(V)
g.add_edges(E)
plot(g)

剧情()会产生这种美:

The graph, no styling

我的天啊。到底发生了什么事?什么顶点是什么 Twitter 用户?还有为什么这么多毛?

别担心,我们将应用一些样式来解决所有这些问题。

首先,让我们添加一些标签。然后,我们可以通过移除弯曲的边缘来拉直毛团,并添加一个边距来保护标签不会漂移到边界框之外。我们将把我们的样式决定保存在一个名为 style 的字典中,这样我们可以在以后重用它。

#add vertex attributes
g.vs["screen_name"] = scrn_names
g.vs["user_name"] = user_namesstyle = {}
style["edge_curved"] = False
style["margin"] = 100
style["vertex_label"] = g.vs["user_name"]
plot(g, **style)

我们使用。vs 图的成员,用于将属性与顶点相关联,每个顶点的属性值按照添加顶点的顺序进行映射。因为我们的用户名scrn_name 数组是与 V、并行的,所以这是一个非常简单的过程。

The graph with some more sane styling

这看起来还不错,但是我的 viz 目标之一是有效地交流什么是追随,什么是追随和追随回来。使用 igraph 很容易迭代边缘,检测这些情况,并相应地改变边缘样式。

for e in g.es:
    src, targ = e.tuple
    if g.are_connected(targ, src):
        e["color"] = "green"           #follow + followback
        e["arrow_size"] = 0                       
    else:
        e["color"] = "blue"            #follow
        e["arrow_size"] = 1
plot(g, **style)

Green for a mutual follow, blue for an unreturned follow

好了,伙计们,勒布朗已经离开了猛龙队的凯尔·洛瑞和凯尔特人队的戈登·海沃德。加油勒布朗!

在这里,您可以设置顶点的样式,使它们不会与标签发生太多冲突,调整标签与顶点的距离,尝试不同的布局(图表的结构),等等。我建议把你的情节保存为。svg 文件,这样你就可以用像 Inkscape 这样的编辑器准确地排列你的标签或顶点。

本文开头显示的 30 支 NBA 球队的图形使用了圆形图形布局,颜色来自每支球队的官方调色板( igraph 支持十六进制代码着色)。这是我最喜欢的球队金州勇士队的特写:

Dubs on Twitter

这不仅赏心悦目,而且也让我们对玩家之间的关系有了一些了解。库里和格林看起来整体联系最紧密,跟在后面也很大方。汤普森、杜兰特和利文斯顿稍微有点冷漠,有更多未回归的追随者。德马库斯·考辛斯,一个新来的球员,已经努力跟随他的新队友了(…或者已经跟随他们了)。

你可以更进一步,利用 igraph 内置的大量图形算法来回答如下问题:

  • NBA 哪个球员最中锋?
  • 谁有最好的跟随者-跟随者差异?
  • NBA 存在哪些派系?
  • 哪个团队的(边缘)密度最高,哪个团队的密度最低?

我会在 GitHub 上偶尔重构和更新我的代码,但我所展示的要点已经在那里了,还有完整的 NBA 图表。

我焦急地等待着你从 Twitter 上创建的漂亮的图表!如果你能和我分享,我会很高兴的。

[## kublasean/NBA-Twitter-分析

在 Twitter-kublasean/NBA-Twitter-analysis 上制作球员-球员关系图

github.com](https://github.com/kublasean/nba-twitter-analysis)

利用网络摄像头和深度学习创建定制的堡垒之夜舞蹈

原文:https://towardsdatascience.com/creating-custom-fortnite-dances-with-webcam-and-deep-learning-9b1a236c1b59?source=collection_archive---------9-----------------------

使用姿势估计和条件敌对网络创建和可视化新堡垒之夜舞蹈。

Recreating a Fortnite character’s dance moves using poses from my webcam video.

如果你知道游戏堡垒之夜,你可能也知道围绕着游戏中的庆祝/表情/舞蹈的狂热。游戏玩家已经花费了数百万美元通过应用内购买来购买舞蹈动作,这使得一些简单而愚蠢的事情成为游戏开发者的一大收入来源。这让我想到,如果开发者允许用户在游戏中创作这些舞蹈并收取额外费用,他们可能会赚更多的钱。对于用户来说,如果我们可以在网络摄像头上记录自己,并在游戏中创造自己的庆祝舞蹈,那将会非常酷。

目前这样做需要一个类似微软 Kinect 的设备,它有专用的硬件来感知我们的身体运动。然而,并不是每个人都想为此购买单独的设备。我认为随着深度学习的进步,很快就有可能只用一个很好的旧网络摄像头来实现类似的功能。让我们看看如何在深度学习算法的帮助下,在未来实现这样的事情。

从网络摄像头到堡垒之夜

为了验证这一点,我在这个项目中使用了两种深度学习技术。首先,我们将使用姿势估计算法从我们的网络摄像头记录中提取简笔画表示(姿势)。对于一个游戏开发人员来说,这种姿势表示足以使游戏中的角色产生动画效果,但是因为我不是开发人员,所以我只能简单地想象使用这种姿势创建的堡垒之夜舞是什么样子。为此,我使用了名为 pix2pix 的条件对抗网络来生成给定姿势的角色。

Pipeline to go from webcam to Fortnite involves two steps: (1) getting pose from webcam image, followed by (2) synthesizing Fortnite character in that particular pose.

从网络摄像头进行姿态估计

为了估计输入图像姿态,我已经使用了来自论文 [的算法,实时多人 2D 姿态估计使用部分亲和场](http://The image shown here is the output of a net trained to detect the right arm of the body shown as the highlighted area in this heatmap.) 曹等人【CVPR 17】。该算法使用多个尺度的卷积神经网络来识别身体的不同部分,如左臂、右臂、躯干等。这些检测到的部分代表单个节点。

This image shows the detection results of a Convolutional Neural Net trained to detect the right arm of a human body. The detection is shown by the hot region of this heatmap at multiple scales.

一旦检测到所有这样的身体部分,它就使用贪婪的解析算法来连接附近的节点,以形成一个连通图,给我们一个简笔画,这是姿势的表示。它实时运行,也可以与图像中出现的多人一起工作。

从姿态合成图像

一旦我们得到了这个姿势,我们想把它转换成堡垒之夜的角色。为此,我们将使用相同的姿态估计算法来生成我们的训练数据。我们用这个获得带标签的训练数据,其中姿势图是我们的输入,堡垒之夜角色是我们的目标标签。

Training data contains collection of paired images of the pose (input) and the targeted dance move (output).

然后,训练一个 pix2pix 网络将输入转换为输出。它使用一个生成的对抗网络来产生目标图像,条件是输入图像而不是随机噪声,因此我们可以实际生成堡垒之夜角色的图像,这些图像遵循作为输入给定的姿势。

在训练期间,输入图像和目标图像都可用于发生器和鉴别器网络。pix2pix 中的生成器网络显式地产生真实和虚假图像,以便鉴别器可以更快地学会区分这两者。

结果

发生器和鉴频器损耗收敛后,网络产生相当不错的结果。通过将堡垒之夜角色的每个身体部位与简笔画相关联,它已经学会很好地跟随输入姿势。不幸的是,产生的图像非常模糊,没有高层次的细节。如果你知道我可以如何改善 pix2pix 网络的结果,请在下面的评论区告诉我。

更多这样的结果可以在我的 YouTube 频道和下面嵌入的视频中找到。如果你喜欢它,请随意订阅到我的频道来关注我更多的作品。

用于姿态估计的代码可以在这里找到,用于图像合成的代码可以在这里找到。

感谢您的阅读!如果你喜欢这篇文章,请在媒体、 GitHub 上关注我,或者订阅我的 YouTube 频道。

用数据科学创造智能

原文:https://towardsdatascience.com/creating-intelligence-with-data-science-2fb9f697fc79?source=collection_archive---------9-----------------------

在这篇文章中,我将展示数据科学如何让我们通过人工智能创造智能。

什么是智能?

这个问题不容易回答。不久前我还在努力定义它是什么,但我在莱克斯·弗里德曼的课程中找到了一个我喜欢的简单短语。

所以让我们把智能定义为:

完成复杂目标的能力。

但是什么是复杂的呢?我们如何定义复杂的事物?如果你在互联网上查找,你会发现几个不同的定义,但我认为“主要的”一个接近我认为智力的定义是什么。

如果我们认为复杂的事物是由许多部分以难以理解的方式相互联系在一起的事物,那么我们可以说复杂的事物是事物或部分的混合,它们共同形成一个更大的事物,而这些部分的联系方式是不容易理解的。

例如,一辆汽车是一个复杂的东西,它有许多部件以一种不容易理解的方式协同工作。

但是!如果我们看一看,个别部分并不难理解。我并不是说它们很容易构建或者很容易看到它们到底在做什么,而是说更容易掌握它们在做什么。

所以我们现在可以说智能是:

通过理解构成主要目标的部分来完成困难目标的能力。

这些目标将在我们希望的背景下定义,但现在我们希望专注于人工智能(AI)领域。因此,当人工智能想要使用机器和计算来建立智能,试图模仿我们人类看、听、学习等方式时,这些目标将是看、学习、 听、移动、理解等。

什么是理解?

我们将从 Lex 的课上学到的另一个重要概念是理解。到目前为止,我们已经多次使用这个词,所以让我们来定义它:

理解是将复杂的信息转化为简单的有用的信息的能力。

我们需要这个,看到车的零件的时候就谈过了。当我们理解时,我们正在解码形成这个复杂事物的各个部分,并将我们一开始获得的原始数据转化为有用的、简单易懂的东西。

我们通过建模来做到这一点。这是理解“现实”,我们周围的世界,但创造一个更高层次的原型来描述我们所看到的,听到的和感觉到的东西的过程,但这是一个代表性的东西,而不是“实际”或“真实”的东西。

那么我们人类是如何创造智慧的呢?通过模拟我们周围的世界,了解它的组成部分,将我们得到的原始数据转化为有用和简单的信息,然后看看这些部分如何形成更复杂的东西,最终实现目标,“困难”的目标。

我们还要多久才能变得聪明?

这花了大约 380 万年的时间。我们希望这能在未来五年内实现:

即使我们用人工智能完成复杂的目标不会花那么长时间,也不会像我们想象的那么快。我们是一个正在成熟的领域,每天都在进步。但是 AI along 并不能解决所有的问题。

用 AI 创造智能我们需要什么?

我认为创造智慧的秘诀在高层次上并不难。这就是我提议我们需要做的事情:

大数据+ AI +数据科学=人工通用智能

我所说的人工通用智能(AGI)是这场革命的主要目标。AGI 是通用系统,其智能堪比人类思维(或者可能超越人类)。

我们需要大数据作为到达 AGI 的催化剂,因为有了更多的数据,加上分析数据的新方法,加上更好的软件和硬件,我们可以创建更好的模型和更好的理解。我们需要 AI 的当前状态,非常接近深度学习、深度强化学习及其周围环境(更多关于深度学习这里),然后我们需要数据科学作为这场革命背后的控制器和科学。

什么是数据科学?

这个定义可能会引起一些人的争议,但我认为这非常接近领导者(理论上和业务上)现在所说的。所以,

数据科学是通过数学编程科学方法来解决业务/组织问题,其中涉及通过分析数据生成预测模型来创建假设实验测试。它负责这些问题转化为适定问题,这些适定问题也能以创造性的方式对初始假设做出回应。还必须包括对所获结果的有效沟通,以及解决方案如何为企业/组织增加价值。

有了这个定义,我们可以定义谁是数据科学家:

数据科学家是一个人(或者系统?)负责分析业务/组织问题并给出结构化解决方案首先由问题转化为有效且完整的问题,然后使用编程计算工具开发代码使准备清理分析数据

我在这里说的是,数据科学与业务密切相关,但它最终是一门科学,或者正在成为一门科学,或者可能不是。我认为数据科学是一门科学是非常有用的,因为如果是这样的话,数据科学中的每个项目至少应该:

-可复制
-可伪造
-协作
-创造性
-符合法规

你可以在亨利·庞加莱的著作《科学与方法》中了解更多为什么这很重要,请点击此处:

** [## 科学与方法:庞加莱,亨利,1854-1912:免费下载

14 35

archive.org](https://archive.org/details/sciencemethod00poinuoft)**

本文的主要观点(未来可能会有更多相关内容)是向您展示这些是真正严肃的研究和开发领域,我们需要所有这些领域都进入 AGI,而数据科学对这一结局至关重要。

你可能会想,我们为什么需要 AGI?或者说为什么我们首先需要 AI?

我认为我们可以让世界变得更好,改善我们的生活,改善我们工作、思考和解决问题的方式,如果我们现在就调动我们所有的资源,让这些知识领域为更大的利益而共同努力,我们就可以对世界和我们的生活产生巨大的积极影响。

我们需要更多感兴趣的人,更多的课程,更多的专业,更多的热情。我们需要你:)

如果您有任何问题,请在 LinkedIn 上添加我,我们将在那里聊天:

** [## Favio Vázquez -数据科学家/工具经理 MX - BBVA 数据&分析| LinkedIn

查看 Favio Vázquez 在世界上最大的职业社区 LinkedIn 上的个人资料。Favio 有 13 个工作列在他们的…

www.linkedin.com](https://www.linkedin.com/in/faviovazquez)**

使用 Plot.ly 创建参数优化的交互式动画

原文:https://towardsdatascience.com/creating-interactive-animation-for-parameter-optimisation-using-plot-ly-8136b2997db?source=collection_archive---------6-----------------------

介绍

这篇文章演示了如何在一个变化的参数上创建一个交互式的动画结果图。

为此,我们复制了此处示例中提供的显示一段时间内各国人均 GDP 的示例,如下所示(https://plot.ly/python/animations/)。

实现这一点的先决条件是 Python 和 plot . ly(【https://plot.ly/python/getting-started/】T2)

这绝不是最干净或最漂亮的方法,我只是简单地将我的数据放入示例中,除了更改一些轴标签之外,没有对示例中的代码进行任何重大更改。我已经创建了可视化的想法,参数优化,它并不总是为了出版,而是分析的探索阶段,所以必须尽可能快速和容易地创建。

如果你想在你的报告中使用视觉效果,我建议稍微编辑一下格式,但在其他方面,创建方法也可以类似地使用。

如有疑问,欢迎在下方随意评论。

Gapminder 示例

我们将再次应用以下技术来创建交互式图表。首先,我们需要观察正在使用的数据。在此示例中,使用以下内容创建了散点图:

  • x 轴上的预期寿命
  • y 轴上的人均 GDP
  • 国家的大小与人口有关
  • 按洲着色
  • 跨年度跟踪(动画和滑块)

Plot.ly Gapminder AnimationExample

应用我们的数据

为此,我将我的结果导出到一个特定格式的数据表中。如下所示,有三列,Alpha、Epsilon 和 V。Alpha 是我正在更改的参数,Epsilon 是模型运行的第 n 次,V 是我的输出。

因此,当我的参数在 1 和 0 之间减小时,我的表由每次运行的输出构成。下图显示了前几行。

现在,我们将把已有的数据“混合”到 plot.ly 示例中。因为我们的数据有些简单,所以我们首先否定示例中使用的一些特征。理论上,你可以整理代码来删除它们,但是这需要更多的时间,而且这种方法更简单。

因此,我们在数据中添加了两列,每行都有一个虚拟值‘Test1’和‘Test2’。然后,我们添加第三列来对应所使用的人口特征。如前所述,该值对应于点的大小,我选择了一个提供适当大小的点的值,但如果需要,您可以编辑它。

然后,我重命名所有的列,使其完全符合示例中的命名约定。

在[21]中:

ourData['continent'] = 'Test'
ourData['country'] = 'Test2'
ourData['pop'] = 7000000.0ourData.columns = ['year', 'lifeExp', 'gdpPercap', 'continent', 'country', 'pop']ourData.head()

下一步有点棘手,在示例中,他们使用以下代码手动输入每年的数据:

years = ['1952', '1962', '1967', '1972', '1977', '1982', '1987', '1992', '1997', '2002', '2007']

因此,我不必每次都手动输入,只需找到每个唯一的 Alpha 值,按降序进行舍入和排序,然后生成一个唯一 Alpha 值的数组。然后,我将变量名改为‘Years ’,以与后面的代码相对应。

在[24]中:

alpha = list(set(ourData['year']))
alpha = np.round(alpha,1)
alpha = np.sort(alpha)[::-1]
years = np.round([(alpha) for alpha in alpha],1)
years

Out[24]:

array([ 1\. ,  0.9,  0.8,  0.7,  0.6,  0.5,  0.4,  0.3,  0.2,  0.1])

最后,我们将我们的数据重命名为与示例代码相对应的数据,并运行整个过程,以生成我们的参数优化动画和交互图。

在布局部分,我添加了一个主标题,并重新措辞了一些轴标签,以符合我的要求,但在其他方面没有改变。

在[27]中:

dataset = ourDatacontinents = []
for continent in dataset['continent']:
    if continent not in continents:
        continents.append(continent)
# make figure
figure = {
    'data': [],
    'layout': {},
    'frames': []
}# fill in most of layout
figure['layout']['title'] = "Parameter Optimisation using Interactive Animation <br> PhilipOsborneData.com"
figure['layout']['xaxis'] = {'range': [1, 100], 'title': 'Epsilon'}
figure['layout']['yaxis'] = {'range': [1,50],'title': 'Sum of V', 'type': 'linear'}
figure['layout']['hovermode'] = 'closest'
figure['layout']['sliders'] = {
    'args': [
        'transition', {
            'duration': 400,
            'easing': 'cubic-in-out'
        }
    ],
    'initialValue': '1952',
    'plotlycommand': 'animate',
    'values': years,
    'visible': True
}
figure['layout']['updatemenus'] = [
    {
        'buttons': [
            {
                'args': [None, {'frame': {'duration': 500, 'redraw': False},
                         'fromcurrent': True, 'transition': {'duration': 300, 'easing': 'quadratic-in-out'}}],
                'label': 'Play',
                'method': 'animate'
            },
            {
                'args': [[None], {'frame': {'duration': 0, 'redraw': False}, 'mode': 'immediate',
                'transition': {'duration': 0}}],
                'label': 'Pause',
                'method': 'animate'
            }
        ],
        'direction': 'left',
        'pad': {'r': 10, 't': 87},
        'showactive': False,
        'type': 'buttons',
        'x': 0.1,
        'xanchor': 'right',
        'y': 0,
        'yanchor': 'top'
    }
]sliders_dict = {
    'active': 0,
    'yanchor': 'top',
    'xanchor': 'left',
    'currentvalue': {
        'font': {'size': 20},
        'prefix': 'Alpha: ',
        'visible': True,
        'xanchor': 'right'
    },
    'transition': {'duration': 300, 'easing': 'cubic-in-out'},
    'pad': {'b': 10, 't': 50},
    'len': 0.9,
    'x': 0.1,
    'y': 0,
    'steps': []
}# make data
year = 1.0
for continent in continents:
    dataset_by_year = dataset[np.round(dataset['year'],1) == np.round(year,1)]
    dataset_by_year_and_cont = dataset_by_year[dataset_by_year['continent'] == continent] data_dict = {
        'x': list(dataset_by_year_and_cont['lifeExp']),
        'y': list(dataset_by_year_and_cont['gdpPercap']),
        'mode': 'markers',
        'text': list(dataset_by_year_and_cont['country']),
        'marker': {
            'sizemode': 'area',
            'sizeref': 200000,
            'size': list(dataset_by_year_and_cont['pop'])
        },
        'name': continent
    }
    figure['data'].append(data_dict)# make frames
for year in years:
    frame = {'data': [], 'name': str(year)}
    for continent in continents:
        dataset_by_year = dataset[np.round(dataset['year'],1) == np.round(year,1)]
        dataset_by_year_and_cont = dataset_by_year[dataset_by_year['continent'] == continent] data_dict = {
            'x': list(dataset_by_year_and_cont['lifeExp']),
            'y': list(dataset_by_year_and_cont['gdpPercap']),
            'mode': 'markers',
            'text': list(dataset_by_year_and_cont['country']),
            'marker': {
                'sizemode': 'area',
                'sizeref': 200000,
                'size': list(dataset_by_year_and_cont['pop'])
            },
            'name': continent
        }
        frame['data'].append(data_dict) figure['frames'].append(frame)
    slider_step = {'args': [
        [year],
        {'frame': {'duration': 300, 'redraw': False},
         'mode': 'immediate',
       'transition': {'duration': 300}}
     ],
     'label': year,
     'method': 'animate'}
    sliders_dict['steps'].append(slider_step) figure['layout']['sliders'] = [sliders_dict]iplot(figure)

输出显示在本文开头的动画中。

谢谢

哲学(philosophy 的缩写)

使用 Python、Pdfkit 和 Jinja2 模板创建 PDF 报告

原文:https://towardsdatascience.com/creating-pdf-reports-with-python-pdfkit-and-jinja2-templates-64a89158fa2d?source=collection_archive---------3-----------------------

作为一名数据科学家,有时您可能需要创建 PDF 格式的分析报告。如今,这似乎有点“老派”,但在以下几种情况下,你可能会考虑这样做:

  • 你需要制作易于打印的报告。人们通常想要他们正在运行的特定报告的“硬拷贝”,而不想在交互式仪表板中再现他们所做的一切。
  • 您需要匹配现有的报告格式:如果您要更换传统的报告系统,首先尝试匹配现有的报告方法通常是个不错的主意。这意味着如果遗留系统使用 PDF 报告,那么您应该强烈考虑在替换系统中创建该功能。这对于获得对旧系统感到满意的人的认同通常很重要。

我最近需要在工作任务中做 PDF 报告。我想到的特定解决方案使用了两个主要工具:Jinja2 和 Pdfkit。

我们将使用以下命令安装所需的软件包:

pip install pdfkit 
pip install Jinja2

请注意,您还需要安装一个名为 wkhtmltopdf 的工具,pdfkit 才能工作。

Jinja2 模板入门

Jinja2 是一个很好的熟悉的工具,特别是如果你用 Python 做 web 开发的话。简而言之,它允许您通过编程填充分配给文本文件模板的占位符值来自动生成文本文档。这是一个非常灵活的工具,广泛用于 Python web 应用程序中为用户生成 HTML。你可以把它想象成超级高能的字符串替换。

我们将使用 Jinja2 来生成报告的 HTML 文件,我们将使用其他工具将这些文件转换成 pdf。请记住,Jinja2 可以方便地用于其他报告应用程序,比如发送自动电子邮件或创建其他文本文件格式的报告。

使用 Jinja2 有两个主要部分:

  • 创建包含占位符值的文本文件 Jinja2 模板。在这些模板中,您可以使用各种 Jinja2 语法特性,这些特性允许您调整文件的外观以及它加载占位符数据的方式。
  • 编写 python 代码,将占位符值分配给 Jinja2 模板,并根据这些值呈现新的文本字符串。

让我们创建一个简单的模板作为示例。这个模板只是一个文本文件,它打印出一个名字的值。你所要做的就是创建一个文本文件(姑且称之为 name.txt)。然后在这个文件中,简单地添加一行:

Your name is: {{ name }}

在这里,“name”是我们将传递到模板中的 python 变量的名称,它保存了我们希望包含在模板中的字符串占位符。

现在我们已经创建了模板,我们需要编写 python 代码,用您需要的内容填充模板中的占位符值。您可以使用渲染函数来完成此操作。比方说,我们想要创建一个模板版本,其名称为“Mark”。然后编写以下代码:

现在,outputText 保存一个模板字符串,其中{{ name }}现在等于“Mark”。您可以通过在命令行上编写以下内容来确认这一点:

template.render()的参数是模板中包含的占位符变量,以及您要将它们赋给什么:

template.render(placeholder_variable_in_template1=value_you_want_it_assigned1, placeholder_variable_in_template2=value_you_want_it_assigned2, ..., placeholder_variable_in_templateN=value_you_want_it_assignedN)

使用 Jinja2 模板,您可以做更多的事情。例如,我们在这里只展示了如何呈现一个简单的变量,但是 Jinja2 允许更复杂的表达式,比如 For 循环、if-else 语句和模板继承。关于 Jinja2 模板的另一个有用的事实是,您可以传入任意 python 对象,如列表、字典或 pandas 数据框,并且您可以直接在模板中使用这些对象。查看 Jinja2 模板设计器文档以获得完整的特性列表。我也强烈推荐《T2》这本书:用 Python 开发 Web 应用,这本书包含了关于 Jinja2 模板(这是 Flask web 开发框架的内置模板引擎)的优秀指南。

创建 PDF 报告

假设您想要打印显示银行账户增长的 pdf 表格。每个表格都显示了 100 美元、500 美元、20,000 美元和 50,000 美元的年增长率。每个单独的 pdf 报告使用不同的利率来计算增长率。我们需要 10 份不同的报告,每份报告分别打印利率为 1%、2%、3%、10%的表格。

让我们首先定义我们需要的熊猫数据框架。

data_frames 包含 10 个字典,每个字典都包含数据帧和用于产生该数据帧的利率。

接下来,我们创建模板文件。我们将为上述 10 个数据框中的每一个生成一个报告,并通过将每个数据框连同所使用的利率一起传递给模板来生成它们。

创建这个模板后,我们编写下面的代码来为我们的报告生成 10 个 HTML 文件。

我们的 HTML 报告现在看起来像这样:

最后一步,我们需要将这些 HTML 文件转换成 pdf。为此,我们使用 pdfkit。您所要做的就是遍历 HTML 文件,然后使用 pdfkit 中的一行代码将每个文件转换成 pdf。

所有这些代码组合起来将弹出以下 PDF 版本的 HTML 文件:

然后你可以点击 1.pdf,看到我们得到了我们想要的结果。

我们已经给出了一个非常简单的例子,说明如何使用 python 以自动化的方式创建报告。通过更多的工作,您可以开发更加复杂的报表,只受 HTML 的限制。

原载于 2018 年 9 月 16 日【www.marknagelberg.com】。要访问我共享的 Anki deck 和 Roam Research notes 知识库,以及关于间隔重复和提高学习效率的技巧和想法的定期更新, 加入“下载马克的大脑”。

创建 R 包:你需要知道什么

原文:https://towardsdatascience.com/creating-r-packages-what-you-need-to-know-2a20233b328a?source=collection_archive---------6-----------------------

创作 R 包的主要经验和印象。本文还包含了一些链接,指向您需要阅读的书籍和文章,以便开始您自己的 R 包。

Pixabay.com

我和 R 的旅程是从 2012 年左右开始的。我在我们的共享服务中心组建了一个团队来提供分析服务。这是一个小团队,在很多方面我们都在开拓新的领域。我们的数据科学家采用 R 有几个原因,包括价格、软件包支持的范围和社区的规模。事实上,我们的团队帮助建立了菲律宾用户组,自 2013 年第一次会议以来,该用户组已经有了很大的发展。

我们最终应用 R 做了很多很酷的事情,比如时间序列预测(对于共享服务组织中的劳动力优化非常有用)、线索生成和预测建模。我们原型化地将 R 模型公开为 web 服务,供各种客户端使用。总的来说,我们考虑了 R 如何适应生产工作流程和服务。我们还做了很多数据“基础设施”工作:数据摄取、数据&元数据建模、数据仓库、数据质量以及其他被称为数据工程的事情。正如其他人注意到的,这种东西真的超过了分析价值链中 80%的工作。对我个人来说,我实际上把大部分时间花在了管理任务上:招聘和发展团队,销售团队服务,与公司领导层和我的同事一起制定战略,以展示分析的价值,并在组织中发展能力。然而,我总是试图接近设计和实现的细节,包括我们用 r 做的事情。

我们在 R 中所做的大部分工作都是针对我们公司和内部使用的应用程序的。我们写了很多 R 代码:主要是供我们的数据科学家交互使用的脚本,或者由其他脚本/程序调用的脚本。我知道我们可能会从开发 R 包中受益,以更好地形式化、管理和促进重用,即使是内部使用。然而,在我们开始探索这个选项之前,我已经转移到了另一个角色。

嗯,迟做总比不做好。几周前,我决定最终构建我的第一个 R 包。这是一个关于我为什么、做什么以及如何做这件事的故事。简而言之,我学到了很多,我想趁记忆犹新的时候,分享一下我一路上的发现。我也形成了一些早期的印象,也将分享其中的一些想法。

动机

我的动机有两个:在 R 社区层面上就代码重用进行合作,并在这个过程中自学。Airbnb 工程和数据科学团队( AirbnbEng )解释了他们为什么要构建 R 包:

我们构建包来开发针对常见问题的协作解决方案,标准化我们工作的视觉呈现,并避免重新发明轮子。

来源:在 Airbnb 使用 R 包和教育来扩展数据科学

说得好。就我而言,我一直在探索客户支持和案例管理工具。在这一领域,一个备受关注的网络应用(或软件即服务——SaaS——工具)是 Freshdesk 。将数据从流行的网络应用程序中取出,放入 R 中进行分析,这无疑是一个“常见问题”。当我搜索 R 包来从 Freshdesk 提取数据时,我没有立即找到任何包。这似乎是一个开发 R 包的好机会,我可以与社区中的其他人分享。

不是所有的 R 包都需要与应用程序编程接口(API)交互,但我的需要,因为这是 Freshdesk 让第三方程序访问自己的方式。以下是一些让我思考如何继续我的项目的文章和博客帖子。如果您计划创建一个以任何方式包装 API 的 R 包,这些都是很好的入门读物:

  • 快速编写并部署一个 R API 客户端
  • 【如何】在 10 分钟内构建一个 API 包装器包
  • API 包的最佳实践

我学到了什么

我们通过实践学得最好。我确实从自己的 R 包中学到了很多。其中一些是大画面的东西,而我认为一些是较小的,微妙的细节。后者往往是最引人注目的:魔鬼,成功和失败的区别,不可避免地在于细节。

以下是我写第一个包时最突出的部分。有些经验教训很笼统,有些则更微妙、更具体。他们都留下了深刻的印象。

阅读哈德利·韦翰的 R 包

我不认为我可以夸大每一个有抱负的软件包开发者阅读这本书的重要性。哈德利是 R 社区公认的专家之一。我认为他的书 R Packages 是在软件包开发方面的权威:

R Packages by Hadley Wickham: the authority on creating R packages

我无法告诉你我陷入了多少次,只是意识到我试图做的事情在这本书里有所涉及。尽管我从头到尾读了一遍,但在整个开发过程中,我还是不断地回头去看它。如果你想开发一个 R 包,没有借口不读这本书:它是免费的!读这本书。然后再读一遍。

阅读 API 文档

如果你正在构建一个依赖于 API 的包(像我一样),从头到尾阅读应用的 API 文档。如果你不熟悉基于 web 的 API,请先阅读 Zapier 的API 介绍(它也是免费的!).

所有 web 应用程序通常都会在某个地方提供对其 API 的引用。Freshdesk 提供了一个 API 引用。 Github 、 Airbnb 、 Salesforce 、 twitter 也是如此。基本上都是。如果您的 R 包要与它们交互,熟悉这些文档是有好处的。

依靠你的工具

很久以前,在我从事 Windows 编程的日子里,我学到了这一课:您的集成开发环境(IDE)将提供许多功能来改进您的工作流、检查您的代码质量,并为您节省大量时间。特别是对于 R 和包开发,您的 IDE 是 RStudio。使用它。

将帮助你开发 R 包的 R 包是 devtools。安装这个包,并按照 Wickham 的 R 包中的描述使用它。但是你已经知道了,因为你已经像我建议的那样阅读了 R 包!

最后,您必须使用版本控制。早在 2000 年,Joel Spolsky(Stack Overflow 和 Fog Creek 软件的联合创始人)定义了 Joel Test 来衡量开发团队的成熟度。Joel 测试的第一个问题是:你使用源代码控制吗?今天,这意味着 Git。如果你的项目像我一样是公共的,就用 Github(否则就用某种现代版本控制系统)。有很多关于使用 Git 和 Github 的好资源——我不会在这里重复这些内容。您真正需要做的是创建一个 Github 帐户,安装 git ,并在 RStudio 中选择文件|新项目…|新目录| R 包,并确保您点击了创建 git 库复选框。

Creating a new git repository when creating a new package in R

您可能已经猜到了,在 Wickham 的 R 包中介绍了如何将 git 和 Github 与 RStudio 一起使用。

使用持续集成

猜猜乔尔测验的第二个问题是什么:你能一步到位吗?今天,这将被称为持续集成。在构建您的包时使用持续集成。具体用特拉维斯 CI 。

如果你在 Github 上公开主持你的项目,Travis CI 是免费的。开箱即用,Travis CI 会在您每次将更改推送到 Github 时自动构建并检查您的包。我无法告诉你每次对 Github 进行修改时自动执行构建有多令人满意。它迫使你在推进到 GitHub 之前检查你的构建,并花时间调查和清理可能引起警告的小东西。

Travis CI automatically builds and checks your package

Travis CI 的基本设置包含在 R 包中。你也可以看看茱莉亚·西尔格的优秀作品的《Travis-CI 入门指南》。

学习如何处理日期和时间

不可避免地,你需要处理 r 中的日期和时间,学习如何处理日期和时间以及日期运算。R 中的日期和时间让我想起了我必须在中学习相同内容的时候。净年前。日期和时间处理起来很麻烦,会引起问题,而且经常被简单地篡改。追根究底。这里有一个好资源可以开始使用。不要敷衍了事,读一读吧! Kan Nishida 最近在他的 5 个在 R 中处理日期和时间时最实用的操作中涉及了相同的领域,我也推荐你看一下。

创建文档而不共享私有数据

当您依靠 RStudio 这样的工具时,可以使用 Rmarkdown 动态地创建文档,如项目的自述文件和简介。事实上,你应该避免编辑简单的降价。md 文件)。

当您使用 API 时,您不希望在文档中明确地进行身份验证,因为您会泄露像 API 密钥这样的私有信息。为了解决这个问题,将您的凭证存储在本地 R 环境变量中。通过这种方式,您可以使用 Rmarkdown 从环境变量中获取 API 键,而无需在源 Rmarkdown 文档中显示实际的键,并且如果您喜欢,也无需在呈现的普通 markdown 文件中显示从环境变量中检索键的代码:

```{r credentials, echo=FALSE}
my_domain <- Sys.getenv("FRESHDESK_DOMAIN")
my_api_key <- Sys.getenv("FRESHDESK_API_KEY")

然后,您可以在最终呈现的内容中显示代码和输出:

library(freshdeskr)# create a client
fc <- freshdesk_client(my_domain, my_api_key)# gets tickets 
ticket_data <- tickets(fc)
...

使用测试框架

当我开始开发这个包时,在每一次devtools::load_all(),检查,或者安装和重启之后(你会在 R 包中了解到所有这些),我会发现自己运行同样的代码来测试包中的功能。您希望将这种代码放入单元测试中,这样您就可以从一个命令中运行它,并将其作为持续集成过程的一部分。这样,每当您将新的更新推送到 Github 上的项目存储库时,您的测试就会自动运行。

Devtools 通过为您的项目设置 testthat 包使这变得容易,所以依靠您的工具并使用它吧!我遇到的一个问题是,我正在包装一个 web 应用程序的 API。我不想在测试中暴露我的 API 键(因为这些代码在 Github 上是公开的)。我不能像生成文档那样使用环境变量,因为当测试在持续集成服务器上运行时,这是行不通的。对于这种情况,你需要使用模拟。测试和使用测试包包含在 Wickham 的 R 包中,使用模拟包不包含在中。Mango Solutions 有一篇很好的文章:使用模拟函数在没有互联网的情况下进行测试向您展示了如何做到这一点。

在我的例子中,我需要模拟出在我的包的函数中发出的 http GET 请求。需要做一些设置工作,但是当您检查您的项目时,看到所有的单元测试自动运行是令人满意的!所以自动化你的测试。自动化一切。

名称冲突

当我开始从事我的项目时,我在包之外创建了一些函数,在将它们包含到包中之前,我可以先试用一下。当我在我的包中创建相同的函数时,我以名称冲突结束,因为全局环境在搜索路径中位于我的包之前(使用search()查看名称的搜索路径)。重启 RStudio 并没有解决问题。我最终关闭了 RStudio,并手动删除了项目的.Rdata文件以清除名称。

我现在明白了,还有其他更好的方法,例如:从控制台使用rm()或者取消选中 Restore。在重新启动 RStudio 之前,在 RStudio 菜单中的工具|全局选项… 下的启动选项中进入工作区。

Uncheck Restore .RData into workspace at startup to remove variables from Global Environment

大图:花些时间理解 R 中的环境的概念。

其他印象

整个练习给人留下了很多其他的印象。有三个很突出。一个是有点技术性的:依赖注入的处理。另外两个更一般:使用 Github 和如果有多个合作者,如何支持敏捷方法。

依赖注入

我在技术和设计方面遇到的一个问题是处理依赖注入。我的代码没有涉及太多的细节,而是将 API 返回的一些字段从代码转换成人类更可读的内容。例如,如果支持票据的状态为“打开”,则 Freshdesk API 会返回“2”。在检索票证数据的函数中,我想将 2 转换为“Open”。

我创建了一些查找表来处理这个问题。我不喜欢在函数中使用它们来将代码转换成可读的标签。它本质上等同于硬编码值。理想情况下,我会将这些查找表保存在包内部。但是,如果不能从外部访问它们,就不能将它们作为参数传递给任何导出的函数(并实现依赖注入)。最后,我向软件包的用户公开了这些查找表。另一方面,用户可能会修改这些表,并导致使用它们的函数出错。积极的一面是,用户可以更新表,例如,如果 Freshdesk 在 API 中更新了这些代码。阴阳……

开源代码库

这个项目的一个有趣和积极的副作用是我更多地了解了 Github。我以前用过 git,在 Github 上也有一些令牌回购(这是存储库的酷说法)。然而,在 Github 上托管我的包迫使我更好地理解这个工具。我学到了如何编写更好的提交消息,在编写我的包的过程中,我改编了一些指南。我还学会了真正利用 Github 作为问题跟踪器。您可以在提交中使用关键字来自动更新和/或关闭问题。自动化很好。我还意识到,Github 问题可以用来捕捉一切:不仅仅是 bug,还有想法、增强、重构机会等等。接下来也很好…

支持敏捷工作流程

如果你捕获了 Github 的所有问题,那么它基本上就成了你的产品积压。你甚至可以在 Github 中设置看板风格的板子。如果你采用某些惯例,那么 Github 可以支持敏捷方法。Github 问题的敏捷项目管理工作流程很好地描述了如何实现这一点,我打算继续下去。我甚至已经开始将我的问题写成用户故事,例如:作为一个用户,我希望能够有选择地检查到 Freshdesk 的连接,以便在我使用任何方法检索数据之前,我知道域和认证是有效的。

后续步骤

现在还为时尚早,我将继续开发仍处于初级阶段的 freshdeskr 包。我邀请任何人通过报告问题、发布功能请求、更新文档、提交请求或补丁以及其他活动来做出贡献。

你从创建 R 包中学到了什么?你会推荐什么书、文章或其他资源?

可以在 GitHub 上克隆或者分叉 freshdeskr 包。你可以在推特上找到我@ johnjanuszczak