TowardsDataScience 博客中文翻译 2016~2018(一百七十七)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

Keras 教程:Python 中的深度学习

原文:https://towardsdatascience.com/keras-tutorial-deep-learning-in-python-2caf497f8ca1?source=collection_archive---------4-----------------------

原载于https://www . data camp . com/community/tutorials/deep-learning-python

现在,你可能已经知道机器学习,这是计算机科学的一个分支,研究可以学习的算法的设计。今天,你们将关注深度学习,这是机器学习的一个子领域,是一套受大脑结构和功能启发的算法。这些算法通常被称为人工神经网络(ANN)。深度学习是数据科学中最热门的领域之一,在机器人、图像识别和人工智能(AI)方面有许多案例研究,取得了令人惊叹的成果。

一些你可能已经知道的成功故事是自动驾驶汽车、自动玩游戏(想想玩棋盘游戏围棋的 AlphaGo )、手写生成、…

用于开发和评估深度学习模型的最强大和易用的 Python 库之一是 Keras 它包装了高效的数值计算库 ano 和 TensorFlow。这样做的好处主要是,你可以用一种简单有趣的方式开始学习神经网络。

今天的 Keras 初学者教程将向您介绍 Python 深度学习的基础:一步一步地,该教程将向您展示如何使用 Python 及其库来探索您的数据,为分类和回归任务建立多层感知器,编译数据并使其符合这些模型,预测目标值并验证您已经建立的模型。

你想参加关于 Keras 和 Python 深度学习的课程吗?可以考虑上 DataCamp 的深度学习中的 Python 课程!

此外,不要错过我们的 Keras 备忘单,它通过代码示例向您展示了用 Python 构建神经网络需要经历的六个步骤!

在深入研究 Keras 以及如何使用它开始 Python 深度学习之前,您可能应该对神经网络略知一二。正如您在上一节中简要阅读的那样,神经网络找到了它们的灵感和生物学,其中术语“神经网络”也可以用于神经元。人脑就是这种神经网络的一个例子,它由许多神经元组成。

众所周知,大脑能够进行非常复杂的计算,这也是人工神经网络的灵感来源。网络作为一个整体是一个强大的建模工具。

最简单的神经网络是“感知器”,其最简单的形式是由单个神经元组成。非常像具有树突和轴突的生物神经元,单个人工神经元是简单的树形结构,其具有输入节点和连接到每个输入节点的单个输出节点。以下是两者的直观对比:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从图中可以看出,人工神经元有六个组成部分。从左到右,它们是:

  1. 输入节点。碰巧的是,每个输入节点都与一个数值相关联,这个数值可以是任何实数。记住实数构成了所有的数字:它们可以是正数或负数,整数或小数。
  2. 人脉。类似地,每个离开输入节点的连接都具有与之相关联的权重,并且这也可以是任何实数。
  3. 接下来,输入节点的所有值和连接的权重被集合在一起:它们被用作一个加权和 : y = f(sum_{i=1}^{D} w_ix_i)的输入,或者换句话说,y = f(w_1x_1 + w_2x_2 + … w_Dx_D)。
  4. 该结果将作为转移或激活功能的输入。在最简单但平凡的情况下,这个传递函数是一个恒等函数,f(x)=x,或者换句话说,y=x。在这种情况下,(x)是输入节点和连接的加权和。然而,就像生物神经元只有在超过某个阈值时才会触发一样,人工神经元也只有在输入的总和超过某个阈值时才会触发,比如说 0。这是你在标识函数中找不到的东西!人们能想到的最直观的方法是设计一个如下的系统:f(x) = 0 如果 x<0;如果 x=0,f(x)= 0.5;如果 x > 0,f(x) = 1。
  5. 当然,你已经可以想象输出不会是一条平滑的线:它将是一个不连续的函数。因为这会在数学处理中引起问题,所以经常使用连续变量,即 sigmoid 函数。你可能已经知道的 sigmoid 函数的一个例子是逻辑函数。使用这个函数会产生更平滑的结果!
  6. 因此,您有了输出节点,它与输入节点的加权和的函数(如 sigmoid 函数)相关联。注意,sigmoid 函数是一个数学函数,它会产生一条“S”形曲线;稍后你会读到更多相关内容。
  7. 最后,感知器可能是一个额外的参数,称为偏差,实际上您可以将它视为与永久设置为 1 的额外输入节点相关联的权重。偏差值很重要,因为它允许你将激活函数向左或向右移动,这可以决定你学习的成功与否。

请注意,这个模型的逻辑结果是,感知器只处理数字数据。这意味着您应该将任何名义数据转换成数字格式。

既然您已经知道感知器与阈值一起工作,那么将它们用于分类目的的步骤就不远了:感知器可以同意,高于某个阈值的任何输出都表明一个实例属于一个类,而低于阈值的输出可能导致输入成为另一个类的成员。输出等于阈值的直线就是两个类之间的边界。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

感知器的网络是多层感知器,这是本教程将在 Keras 的帮助下用 Python 实现的!多层感知器也被称为“前馈神经网络”。正如你现在已经猜到的,这些是比感知器更复杂的网络,因为它们由多层组织的多个神经元组成。层数通常限制在两三层,但理论上是没有限制的!

这些层的行为非常像你在上面读到的生物神经元:一层的输出作为下一层的输入。

在这些层中,您可以区分输入层、隐藏层和输出层。多层感知器通常是全连接的。这意味着某一层中的每个感知器与下一层中的每个感知器之间存在连接。即使连通性不是必需的,这也是典型的情况。

请注意,虽然感知器只能表示类之间的线性分离,但多层感知器克服了这一限制,还可以表示更复杂的决策边界。

对于本教程,您将使用葡萄酒质量数据集,您可以在 UCI 机器学习知识库的葡萄酒质量数据集中找到该数据集。理想情况下,您在较大的数据集上执行深度学习,但出于本教程的目的,您将利用较小的数据集。这主要是因为目标是让您开始使用这个库,并熟悉神经网络的工作方式。

你可能已经知道这个数据集,因为它是开始学习如何解决机器学习问题的最流行的数据集之一。在这种情况下,它将为您使用 Keras 开始深度学习 Python 提供服务。

我们现在就开始吧!

然而,在开始加载数据之前,检查一下您对葡萄酒的了解程度可能是个好主意(当然是与数据集相关的)。你们大多数人都知道,一般来说,有两种非常受欢迎的葡萄酒:红葡萄酒和白葡萄酒。

(我确信还有很多其他的,但为了简单起见,也因为我对葡萄酒的了解有限,我就只说这些了。如果让你们当中真正的鉴赏家失望了,我很抱歉:)。

知道这些已经是一件事了,但是如果你想分析这些数据,你需要知道更多一点。

首先,检查数据描述文件夹,查看包含了哪些变量。这通常是理解数据的第一步。前往本页查看描述或继续阅读以更好地了解您的数据。

该数据由两个数据集组成,分别与葡萄牙“Vinho Verde”葡萄酒的红色和白色变种相关。如描述中所述,你只会发现物理化学和感官变量包含在这个数据集中。数据描述文件仅列出了数据中包含的 12 个变量,但对于像我一样不是真正的化学专家的人,这里有每个变量的简短描述:

  1. 固定酸度:酸是葡萄酒的主要特性,对葡萄酒的味道有很大的贡献。通常,总酸度分为两组:挥发性酸和非挥发性或固定酸。葡萄酒中的固定酸有以下几种:酒石酸、苹果酸、柠檬酸和琥珀酸。该变量在数据集中以 g(酒石酸)/dm 表示。
  2. 挥发酸度:挥发酸度基本上就是酒变成醋的过程。在美国,红餐酒和白餐酒的挥发性酸度法定上限分别为 1.2 克/升和 1.1 克/升。在这些数据集中,挥发性酸度以 g(乙酸)/dm 表示。
  3. 柠檬酸是葡萄酒中的一种固定酸。在两个数据集中用 g/dm 表示。
  4. 残糖一般指发酵停止后剩余的糖,或被停止。在redwhite数据中用 g/dm 表示。
  5. 氯化物是葡萄酒中咸味的主要来源。在这里,你会看到它是用 g(氯化钠)/dm 表示的。
  6. 游离二氧化硫:添加到葡萄酒中并流失到其中的那部分二氧化硫称为结合态,而活性部分则称为游离态。酿酒师总是试图获得最高比例的游离硫来结合。该变量在数据中以 mg/dm 表示。
  7. 总二氧化硫是结合态和游离态二氧化硫(SO2)的总和。在这里,它是用毫克/分米表示的。葡萄酒中的硫含量有法律限制:在欧盟,红葡萄酒只能有 160 毫克/升,而白葡萄酒和玫瑰葡萄酒可以有大约 210 毫克/升。甜葡萄酒允许有 400 毫克/升。在美国,法律限制是 350 毫克/升,在澳大利亚,这是 250 毫克/升
  8. 密度通常用于衡量糖向酒精的转化。这里,用 g/cm 表示。
  9. pH 或氢的潜力是一个数字标度,用来表示葡萄酒的酸度或碱度。你可能知道,pH 值小于 7 的溶液是酸性的,而 pH 值大于 7 的溶液是碱性的。pH 值为 7 的纯水是中性的。大多数葡萄酒的 pH 值在 2.9 到 3.9 之间,因此是酸性的。
  10. 硫酸盐对于葡萄酒就像面筋对于食物一样。你可能已经知道亚硫酸盐会引起头痛。它们是世界各地酿酒的常规部分,被认为是必不可少的。在这种情况下,它们用 g(硫酸钾)/dm 表示。
  11. 酒精:葡萄酒是一种酒精饮料,正如你所知道的,酒精含量会因酒而异。这个变量包含在数据集中,并以% vol 表示,这并不奇怪。
  12. 品质:葡萄酒专家给葡萄酒质量打分,在 0(非常差)到 10(非常优秀)之间。最终的数字是由相同的葡萄酒专家做出的至少三个评价的中间值。

当然,这都是一些非常基本的信息,你可能需要知道开始。如果你是一个真正的葡萄酒鉴赏家,你可能知道所有这些,甚至更多!

现在,tt 是时候得到你的数据了!

这可以通过 Python 数据操作库 Pandas 轻松完成。您遵循导入惯例,在别名pd下导入包。

接下来,使用read_csv()函数读入存储数据的 CSV 文件。此外,使用sep参数指定本例中的分隔符是分号,而不是普通的逗号。

你可以在这个 DataCamp Light chunk 里试试。

厉害!那不是小菜一碟,不是吗?

到目前为止,你可能已经这样做了一百万次,但是这总是开始的必要步骤。现在,您完全可以开始探索、操作和建模您的数据了!

有了手头的数据,你很容易对这些酒有更多的了解!在本练习中,你可能想做的第一件事就是快速浏览你的两个数据帧。

现在是检查您的导入是否成功的时候了:仔细检查数据是否包含 UCI 机器学习存储库的数据描述文件向您承诺的所有变量。除了变量的数量,还要检查导入的质量:数据类型是否正确?所有的行都通过了吗?当你清理数据的时候,有什么空值需要考虑的吗?

除了info()之外,您可能还想检查您的数据。你可以在这个练习中完成。

简要回顾一下所有这些熊猫功能:你会发现head()tail()sample()非常棒,因为它们为你提供了一种快速检查数据的方式,没有任何麻烦。

接下来,describe()提供了一些关于您的数据的汇总统计,可以帮助您评估您的数据质量。你可以看到一些变量的minmax值有很大的不同。这是您稍后要处理的事情,但是此时,意识到这一点非常重要。

最后,在isnull()的帮助下,您已经仔细检查了red中是否存在空值。当你在阅读了info()的结果后仍有疑问时,这个函数总是能派上用场。

提示:还要检查葡萄酒数据是否包含空值。您可以通过使用 DataCamp Light chunk 的 IPython shell 来实现这一点,正如您在上面看到的。

既然您已经检查了您的数据以查看导入是否成功和正确,那么是时候更深入一点了。

一种方法是查看数据集变量的分布,并制作散点图来查看可能的相关性。当然,如果您将这些数据用于您自己的项目,您可以将这一切提升到一个更高的水平。

乍一看,你可能会觉得有趣的一个变量是alcohol。当你检查一个葡萄酒数据集时,这可能是最先引起你注意的事情之一。您可以使用任何数据可视化库来可视化分布,但是在这种情况下,本教程使用matplotlib来快速绘制分布。你可以在这里找到代码。

正如你在下图中看到的,你会发现红葡萄酒和白葡萄酒的酒精含量基本相同:它们的酒精含量都在 9%左右。当然,也有相当数量的观测值具有 10%或 11%的酒精百分比。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意,如果您使用numpy包中的histogram()函数来计算whitered数据的直方图,您可以仔细检查这一点。你可以在这里找到代码。

如果你对matplotlib教程感兴趣,请务必查看 DataCamp 的 Matplotlib 初学者教程和查看 3D 体积数据教程,它们向你展示了如何使用 Matplotlib 的事件处理程序 API。

接下来,我感兴趣的一件事是硫酸盐和葡萄酒质量之间的关系。正如你在上面读到的,硫酸盐会让人头疼,我想知道这是否会影响葡萄酒的质量。更有甚者,我经常听说女人特别不愿意喝酒正是因为它会引起头痛。也许这影响了红酒的收视率?

我们来看看:这里可以找到代码

正如你在下图中看到的,红葡萄酒似乎比白葡萄酒含有更多的硫酸盐,白葡萄酒含有的硫酸盐少于 1 克/分米。对于白葡萄酒来说,似乎只有几个例外略高于 1 g/(dm \ ),而对于红葡萄酒来说,这肯定更多。这也许可以解释为什么人们普遍认为红酒会让人头疼,但是红酒的质量如何呢?

你可以清楚地看到,有一种硫酸盐含量相对较低的白葡萄酒得到了 9 分,但对于其他葡萄酒,在这一点上很难正确解读数据。

当然,你需要考虑到观察结果的差异也会影响图表以及你可能如何解读它们。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

除了硫酸盐,酸度是葡萄酒的主要和重要的特征之一,是获得优质葡萄酒的必要条件。好的葡萄酒通常能平衡酸度、单宁、酒精和甜味。更多的研究告诉我,0.2 到 0.4 克/升的挥发性酸度不会影响葡萄酒的质量。然而,在更高的浓度下,挥发性的酸度会给葡萄酒带来强烈的酸味。极度易挥发的酸度表明葡萄酒有严重缺陷。

让我们对数据进行测试,并制作一个散点图,绘制酒精与挥发性酸度的关系。数据点应根据其等级或quality标签进行着色。你可以在这里找到代码和练习。

请注意,该图像中的颜色是在 NumPy random模块的帮助下随机选择的。您总是可以通过向redcolorswhitecolors变量传递一个列表来改变这一点。确保它们是相同的(除了 1,因为白葡萄酒数据比红葡萄酒数据多一个唯一的quality值),否则你的图例就不匹配了!

点击此处查看完整图表:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在上图中,你可以看到你在上面读到的水平尤其适用于白葡萄酒:大多数标签为 8 的葡萄酒具有 0.5 或更低的挥发性酸度水平,但它是否对质量有影响很难说,因为所有的数据点都非常密集地集中在图表的一侧。

这只是一个快速的数据探索。如果您有兴趣在自己的项目中详细阐述这一步骤,请考虑 DataCamp 的数据探索帖子,如 Python 探索性数据分析Python 数据剖析教程,它们将指导您了解 EDA 的基础知识。

这可能需要消化很多内容,所以稍微回顾一下您在 EDA 过程中所看到的内容永远不会太晚,这对本教程的后续课程可能很重要:

  • 数据集的一些变量的值相差很大。您可以在本教程的下一部分中处理这个问题。
  • 您有一个理想的场景:数据集中没有空值。
  • 数据集中包含的大多数葡萄酒的酒精含量在 9%左右。
  • 红葡萄酒似乎比白葡萄酒含有更多的硫酸盐,白葡萄酒的硫酸盐含量低于 1 克/干重。
  • 你看到大多数葡萄酒的波动酸度在 0.5 及以下。目前,这与葡萄酒的质量没有直接关系。

到目前为止,您已经分别查看了白葡萄酒和红葡萄酒的数据。当你近距离观察一些变量时,这两者似乎有些不同,而在其他情况下,这两者似乎非常相似。你认为有没有一种方法可以根据变量将条目分为白葡萄酒或红葡萄酒?

只有一种方法可以找到答案:对数据进行预处理,并以这样的方式建模,以便您可以看到会发生什么!

既然您已经探索了您的数据,是时候根据您获得的见解采取行动了!让我们对数据进行预处理,以便您可以开始构建自己的神经网络!

要看代码,去原教程

在这种情况下,您将ignore_index设置为True,因为您不希望在将数据追加到red时保留white的索引标签:您希望标签从它们在red中停止的地方继续,而不是将两个数据集连接在一起的重复索引标签。

现在你有了完整的数据集,做一个快速的数据探索是一个好主意;通过分别查看这两个数据集,您已经知道了一些东西,现在是时候收集一些更可靠的见解了。

由于解释图表可能有些困难,所以绘制相关矩阵也是一个好主意。这将更快地给出关于哪些变量相关的见解。你可以在这里找到代码。

如你所料,有一些变量是相关的,比如densityresidual sugar。此外,volatile aciditytype的联系比你最初通过分别查看这两个数据集所能猜到的更加紧密,而且free sulfur dioxidetotal sulfur dioxide会有关联也是意料之中的事情。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

很有意思!

不平衡数据通常指的是分类问题,其中类别没有被平等地表示。大多数分类数据集在每个类中的实例数量并不完全相等,但是微小的差异通常并不重要。因此,您需要确保所有两类葡萄酒都出现在训练模型中。此外,所有两种葡萄酒类型的实例数量需要大致相等,这样您就不会在预测中偏向某一类。

在这种情况下,似乎有一个不平衡,但你会暂时这样做。之后,您可以评估模型,如果它表现不佳,您可以求助于欠采样或过采样来掩盖观察结果的差异。

现在,从sklearn.model_selection导入train_test_split,并将数据和目标标签分配给变量Xy。您将看到,您需要展平目标标签的数组,以便完全准备好使用Xy变量作为train_test_split()函数的输入。开始工作,从这个数据营的小块开始。

你已经在建立你的第一个神经网络的路上了,但是还有一件事你需要注意!你还知道你在查看whitered数据集的摘要时发现了什么吗?

事实上,有些价值观相差甚远。在这里做一些标准化可能是有意义的。

标准化是处理这些相距甚远的价值观的一种方式。scikit-learn 包为您提供了一种快速标准化数据的方法:从sklearn.preprocessing导入StandardScaler模块,您就可以缩放您的训练和测试数据了!

# Import `StandardScaler` from `sklearn.preprocessing` 
from sklearn.preprocessing import StandardScaler # Define the scaler 
scaler = StandardScaler().fit(X_train) # Scale the train set 
X_train = scaler.transform(X_train) # Scale the test set 
X_test = scaler.transform(X_test)

既然您的数据已经过预处理,那么您可以继续进行真正的工作:构建您自己的神经网络来对葡萄酒进行分类。

在你开始建模之前,回到你最初的问题:你能通过观察葡萄酒的化学性质,如挥发性酸度或硫酸盐,来预测它是红还是白吗?

因为你只有两个类,即白色和红色,你要做一个二进制分类。你可以想象,“二进制”意味着 0 或 1,是或否,由于神经网络只能处理数值数据,所以你已经将红色编码为 1,白色编码为 0。

在这种问题上表现良好的一种网络是多层感知器。正如你在本教程开始时所读到的,这种类型的神经网络通常是完全连接的。这意味着您正在寻求构建一个完全连接的层的相当简单的堆栈来解决这个问题。至于你会用到的激活函数,出于熟悉 Keras 和神经网络的目的,这里最好用一个最常见的,就是 relu 激活函数。

现在你如何开始建立你的多层感知器?快速入门的方法是使用 Keras 顺序模型:它是层的线性堆栈。您可以通过向构造函数传递一个层实例列表来轻松地创建模型,这是通过运行model = Sequential()来设置的。

接下来,最好回想一下多层感知器的结构,就像你在本教程开始时读到的那样:你有一个输入层,一些隐藏层和一个输出层。当你制作模型时,考虑到你的第一层需要使输入形状清晰是很重要的。模型需要知道预期的输入形状,这就是为什么在这些层的文档和这些层的实际示例中,您总是会找到input_shapeinput_diminput_lengthbatch_size参数。

在这种情况下,你将不得不使用一个Dense层,这是一个完全连接的层。密集层实现如下操作:output = activation(dot(input, kernel) + bias)。请注意,如果没有激活函数,您的稠密层将只包含两个线性操作:点积和加法。

在第一层中,activation参数取值为relu。接下来,您还会看到input_shape已经被定义。这是您刚刚看到的操作的input:模型将形状(12,)(*, 12)作为输入数组。最后,您会看到第一层将12作为Dense()units参数的第一个值,这是输出空间的维度,实际上是 12 个隐藏单元。这意味着模型将输出形状为(*, 12)的数组:这是输出空间的维度。如果您现在还没有完全理解,请不要担心,稍后您会读到更多相关内容!

units实际上代表上述公式的kernel或层创建的权重矩阵,由给予所有输入节点的所有权重组成。请注意,在下面的示例中,您没有包含任何偏差,因为您没有包含use_bias参数并将其设置为TRUE,这也是一种可能性。

中间层也使用relu激活功能。这一层的输出将是形状(*,8)的数组。

您将使用大小为 1 的Dense层结束网络。最后一层也将使用一个 sigmoid 激活函数,这样你的输出实际上是一个概率;这意味着这将产生一个介于 0 和 1 之间的分数,表明样品具有目标“1”的可能性有多大,或者葡萄酒是红色的可能性有多大。

你可以在这里找到代码和练习

总之,您会看到,在构建模型时,您需要做出两个关键的架构决策:您将使用多少层,以及您将为每层选择多少个“隐藏单元”。

在这种情况下,你为模型的第一层选择了12个隐藏单元:正如你在上面读到的,这是输出空间的维度。换句话说,当网络学习表示法时,你设置了你允许网络拥有的自由度。如果您允许更多的隐藏单元,您的网络将能够学习更复杂的表示,但这也将是一个更昂贵的操作,容易过度拟合。

请记住,当模型过于复杂时,过度拟合就会发生:它将描述随机误差或噪声,而不是它需要描述的基本关系。换句话说,训练数据建模得太好了!

请注意,当您没有那么多可用的训练数据时,您应该更喜欢使用具有很少隐藏层的小型网络(通常只有一个,就像上面的示例)。

如果你想获得一些关于你刚刚创建的模型的信息,你可以使用属性化的output_shape或者summary()函数,等等。下面列出了一些最基本的方法。

试着运行它们,看看你到底得到了什么结果,以及它们告诉了你关于你刚刚创建的模型的什么信息。点击进入练习

接下来,是时候编译你的模型并使模型符合数据了:再一次,利用compile()fit()来完成这项工作。

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) 
model.fit(X_train, y_train,epochs=20, batch_size=1, verbose=1)

在编译中,用adam优化器和binary_crossentropy损失函数配置模型。此外,您还可以通过将['accuracy']传递给metrics参数来监控训练过程中的准确性。

optimizerloss是编译模型时需要的两个参数。使用的一些最流行的优化算法是随机梯度下降(SGD)、ADAM 和 RMSprop。根据您选择的算法,您需要调整某些参数,比如学习率或动量。损失函数的选择取决于您手头的任务:例如,对于回归问题,您通常使用均方误差(MSE)。正如你在这个例子中看到的,你使用了binary_crossentropy来解决二元分类问题,即确定一款葡萄酒是红葡萄酒还是白葡萄酒。最后,通过多类分类,您将利用categorical_crossentropy

之后,您可以在X_trainy_train中的所有样本上训练模型 20 个历元或迭代,每批 1 个样本。您也可以指定verbose参数。通过将它设置为1,表明您希望看到进度条日志记录。

换句话说,您必须针对特定数量的历元或对训练数据集的暴露来训练模型。一个时期是对整个训练集的一次遍历,随后是对验证集的测试。您在上面的代码中指定的批处理大小定义了将通过网络传播的样本数。此外,通过这样做,您可以优化效率,因为您确保不会同时将太多的输入模式加载到内存中。

让我们使用您的模型吧!你可以用它来预测测试集的标签。只需使用predict()并将测试集传递给它,以预测数据的标签。在这种情况下,结果存储在y_pred中:

y_pred = model.predict(X_test)

在你去评估你的模型之前,你已经可以通过检查y_predy_test的比较来快速了解精确度:

y_pred[:5] 
array([[0], [1], [0], [0], [0]], dtype=int32) 
y_test[:5] 
array([0, 1, 0, 0, 0])

你可以看到这些值似乎加起来了,但是如果没有一些硬数字,所有这些又是什么呢?

现在,您已经构建了模型,并使用它对模型尚未看到的数据进行预测,是时候评估它的性能了。您可以直观地将预测与实际测试标签进行比较(y_test),或者您可以使用所有类型的指标来确定实际性能。在这种情况下,您将使用evaluate()来完成这项工作。传入测试数据和测试标签,如果需要,将verbose参数设为 1。当你这样做的时候,你会看到更多的日志出现。

score = model.evaluate(X_test, y_test,verbose=1) 
print(score) 
[0.025217213829228164, 0.99487179487179489]

分数是一个包含损失和准确性的列表。在这种情况下,你会发现两者似乎都很好,但在这种情况下,最好记住你的数据有些不平衡:你观察到的白葡萄酒比红葡萄酒多。精确度可能只是反映了数据的类别分布,因为它只是预测white,因为这些观察值大量存在!

在您开始重新安排数据并以不同的方式将其放在一起之前,尝试不同的评估指标总是一个好主意。为此,您可以依赖 scikit-learn(您将其导入为sklearn,就像之前您创建训练集和测试集一样)。

在这种情况下,您将测试一些基本的分类评估技术,例如:

  • 混淆矩阵,将预测分解成一个表格,显示正确的预测和错误预测的类型。理想情况下,你只会看到对角线上的数字,这意味着你所有的预测都是正确的!
  • 精度是对分类器准确性的度量。精度越高,分类器就越准确。
  • 召回是对分类器完整性的一种度量。召回率越高,分类器覆盖的案例就越多。
  • F1 分数或 F 分数是精确度和召回率的加权平均值。
  • Kappa 或 Cohen’s kappa 是通过数据中类别的不平衡进行标准化的分类准确度。
# Import the modules from `sklearn.metrics` 
from sklearn.metrics import confusion_matrix, precision_score, recall_score, f1_score, cohen_kappa_score # Confusion matrix 
confusion_matrix(y_test, y_pred) 
array([[1585, 3], [ 8, 549]]) # Precision 
precision_score(y_test, y_pred) 
0.994565217391 # Recall 
recall_score(y_test, y_pred) 
0.98563734290843807 # F1 score 
f1_score(y_test,y_pred) 
0.99008115419296661 # Cohen's kappa 
cohen_kappa_score(y_test, y_pred) 
0.98662321692498967

这些成绩都很不错!尽管事实上你有相当多的白葡萄酒类型的行,你还是做了一个相当精确的模型。

干得好!

您已经成功地构建了您的第一个模型,但是您可以使用这个模型走得更远。为什么不试试下面这些东西,看看它们的效果如何?正如您在上面读到的,您需要做出的两个关键架构决策涉及到层和隐藏节点。这些都是很好的起点:

  • 您使用了 1 个隐藏层。尽量用 2、3 个隐藏层;
  • 使用隐藏单元较多或较少的图层;
  • quality列作为目标标签,其余的数据(包括编码后的type列!)作为你的数据。你现在有一个多类分类问题!

但是为什么不试着改变激活函数呢?代替 relu,尝试使用 tanh 激活函数,看看结果是什么!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

您的分类模型在第一次运行时表现完美!

但是除了更上一层楼,尝试比多层感知器更复杂的结构之外,你还可以做更多的事情。为什么不试着做一个神经网络来预测酒的质量呢?

在这种情况下,本教程假设quality是一个连续变量:那么这个任务就不是一个二元分类任务,而是一个有序回归任务。这是一种用于预测序数变量的回归类型:quality值存在于任意尺度上,其中不同的quality值之间的相对排序是显著的。在这个等级中,质量等级 0-10 代表“非常差”到“非常好”就是这样一个例子。

请注意,您也可以将这类问题视为分类问题,并将质量标签视为固定类别标签。

要了解如何使用神经网络解决回归问题,请访问 原始教程

本教程只是您深入学习 Python 和 Keras 之旅的开始。还有很多要讲,为什么不上 DataCamp 的深度学习用 Python 的课程呢?同时,如果您还没有查看过 Keras 文档,请务必查看。你会找到更多关于所有函数、参数、更多层等的例子和信息。当你学习如何在 Python 中使用神经网络时,这无疑是一个不可或缺的资源!

原载于www.datacamp.com

核函数

原文:https://towardsdatascience.com/kernel-function-6f1d2be6091?source=collection_archive---------0-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

最近,我读了一些关于机器学习的资料,而内核恰好是分类问题的一个有趣部分,在我继续之前,这个主题是由艾伦自己动手为机器人开发者做 NLP写的一篇文章启发的。谢谢 a。

什么是核函数?

要说内核,我们需要了解类似SVM (支持向量机)——分类 - 监督学习 - 机器学习-等等等等等等。这么多术语,对吗?,但不要因此而气馁(在 DIY 练习之前,我对这些一无所知)。让我们一起走进去吧

那么到底什么是“机器学习(ML)”?事实证明,ML 实际上是一大堆东西,但最重要的主题可以用阿瑟·塞缪尔早在 1959 年就说过的一句话来概括:

“机器学习(Machine Learning)是在没有被 明确 编程的情况下,赋予计算机学习能力的研究领域。”

一个计算机程序被说成是从 经验中学习 E 关于某些 任务 T 和某些 性能度量 P,如果它在 T 上的性能,如由 P 所度量的,随着经验 E 而提高”——汤姆·米切尔,卡内基梅隆大学

因此,如果你希望你的程序预测,例如,一个繁忙的十字路口的交通模式(任务 T),你可以通过一个机器学习算法来运行它,该算法具有关于过去交通模式的数据(经验 E),如果它已经成功“学习”,那么它将在预测未来交通模式方面做得更好(性能指标 P)。

在不同类型的 ML 任务中,我们称之为**监督学习(SL)。**在这种情况下,你可以输入一些你已经知道答案的数据(例如,为了预测一只狗是否是一个特定的品种,我们输入了数百万条狗的信息/属性,如类型、身高、肤色、体毛长度等。在 ML 行话中,这些属性被称为“特性”。这些特征列表中的单个条目是一个数据实例,而所有内容的集合是训练数据,这些数据形成了你预测的基础,即如果你知道特定狗的肤色、体毛长度、身高等,那么你就可以预测它可能属于哪个品种。

在我们进入内核之前,我们需要理解什么是支持向量机。支持向量机或 SVM 是具有相关学习算法的监督学习模型,分析数据进行分类(分类意味着知道什么属于什么,例如“苹果”属于“水果”类,而“狗”属于“动物”类——见图 1)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Fig. 1

在支持向量机中,它看起来有点像下面的图 2:),它将蓝色的球与红色的球分开。

SVM 是一个由分离超平面正式定义分类器**。超平面是比它的周围空间小一个维度的子空间。数学空间(或对象)的维度被非正式地定义为指定其中任何点(如每个蓝色和红色点)所需的最小坐标数(x,y,z 轴),而周围空间是数学对象周围的空间。数学对象是数学中出现的抽象对象抽象对象是不存在于任何特定时间或地点的对象,而是作为一种类型的事物存在,即思想或抽象(维基百科)。**

因此,下面的二维空间的超平面(图 2)是划分红色和蓝色点的一维线。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Fig. 2

从上面试图预测一只特定狗的品种的例子来看,它是这样的

数据(所有品种的狗)→特征(肤色、毛发等)→学习算法

那么为什么是内核呢?

考虑下面的图 3

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Fig. 3

你能试着像图 2 那样线性地解决上面的问题吗?

不要!

红色和蓝色的球不能被一条直线分开,因为它们是随机分布的,这就是现实生活中大多数问题数据的随机分布。

在机器学习中,“内核”通常用于指内核技巧,即使用线性分类器来解决非线性问题的方法。它需要将线性不可分的数据(如图 3)转换成线性可分的数据(如图 2)。核函数是应用于每个数据实例的函数,用于将原始非线性观察值映射到高维空间,在该空间中它们变得可分离。

再次使用狗品种预测的例子,内核提供了一个更好的选择。你不用定义一系列的特征,而是定义一个单一的核函数来计算不同品种狗之间的相似度。你把这个内核,连同数据和标签一起提供给学习算法,就产生了一个分类器。

它是如何工作的?

为了更好地理解内核是如何工作的,让我们使用姜黎黎的数学

数学定义 : K(x,y) = < f(x),f(y) >。这里 K 是核函数,x,y 是 n 维输入。f 是从 n 维到 m 维空间的映射。< x,y >表示点积。通常 m 比 n 大很多。

直觉:正常计算< f(x),f(y) >需要我们先计算 f(x),f(y),然后做点积。这两个计算步骤可能相当昂贵,因为它们涉及 m 维空间中的操作,其中 m 可以是大的数字。但是在经历了所有去高维空间的麻烦之后,点积的结果真的是标量:我们又回到一维空间了!现在,我们的问题是:我们真的需要大费周章去得到这个数字吗?我们真的要去 m 维空间吗?答案是否定的,如果你找到一个聪明的内核。

简单例子: x = (x1,x2,x3);y = (y1,y2,y3)。那么对于函数 f(x) = (x1x1,x1x2,x1x3,x2x1,x2x2,x2x3,x3x1,x3x2,x3x3),核就是 K(x,y ) = ( < x,y >)。

让我们插入一些数字,让这个更直观:假设 x = (1,2,3);y = (4,5,6)。然后:
f(x) = (1,2,3,2,4,6,3,6,9)
f(y) = (16,20,24,20,25,30,24,30,36)
< f(x),f(y)>= 16+40+72+40+100+180+72+180+324 = 1024

代数很多,主要是因为 f 是 3 维到 9 维空间的映射。

现在让我们用核来代替:
K(x,y) = (4 + 10 + 18 ) ^2 = 32 = 1024
同样的结果,但是这个计算简单多了。

内核大概就是这样。干得好!你刚刚迈出了成为机器学习专家的第一步:)

额外说明: 了解更多,可以查看 我是如何在 Numerai ml 预测股市的 机器学习中有哪些内核以及

Pelumi Aboluwarin在阅读草案和建议这个主题方面做得非常出色。谢谢大家!

如果你像我喜欢写这篇文章一样喜欢读它,你知道该怎么做;)给它一些爱,如果你对你想让我写的话题有什么建议,请写在下面的评论区。感谢阅读:)

  • 所有图片均来自网络*

额外阅读

  1. https://en.wikipedia.org/wiki/Statistical_classification
  2. https://en.wikipedia.org/wiki/Supervised_learning

核机器学习——kernel ml——广义机器学习算法

原文:https://towardsdatascience.com/kernel-machine-learning-kernelml-generalized-machine-learning-algorithm-4800a4e05a33?source=collection_archive---------7-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

制作这种算法的动机是为分析师和数据科学家提供一种针对复杂损失函数和非线性系数的通用机器学习算法。优化器使用简单的机器学习和概率模拟的组合,使用损失函数、输入和输出矩阵以及(可选的)随机采样器来搜索最佳参数。

示例用例:

聚类方法(如 K-means)使用欧几里得距离来比较观察值。然而,经度和纬度数据点之间的欧几里德距离并不直接映射到哈弗森距离,即球体周围的距离。如果坐标在 0 和 1 之间标准化,则距离将不会在聚类分析模型中准确表示。一种可能的解决方案是找到纬度和经度的投影,使得到数据点质心的哈弗斯距离等于欧几里德空间中投影的纬度和经度的哈弗斯距离。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

此坐标变换的结果允许您将相对于中心的哈弗线距离表示为欧几里德距离,该距离可以在聚类解决方案中进行缩放和使用。

另一个更简单的问题是找到非线性系数的最佳值,即最小平方线性模型中的幂变换。这样做的原因很简单:整数幂变换很少能捕捉到最合适的变换。通过允许幂变换为任何实数,精确度将提高,并且模型将更好地概括验证数据。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为了阐明功率变换的含义,上面提供了模型的公式。

算法:

kernelml 背后的想法很简单。使用机器学习模型中的参数更新历史来决定如何更新下一个参数集。使用机器学习模型作为后端会导致偏差方差问题,具体来说,参数更新在每次迭代中变得更有偏差。这个问题可以通过在每次迭代之后在最佳记录参数集周围包括蒙特卡罗模拟来解决。

衔接问题:

该模型在每次迭代后保存最佳参数和用户定义的损失。该模型还记录所有参数更新的历史。问题是如何使用这些数据来定义收敛。一个可能的解决方案是:

该公式使用最后 10 个参数和最佳参数创建 Z 值。如果所有参数的 Z 分数都小于 1,则可以说该算法已经收敛。当存在理论上的最佳参数集时,这种收敛解决方案工作良好。当使用该算法进行聚类时,这是一个问题。请参见下面的示例。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1:用 kernelml 聚类,二维多元正态分布(蓝色),聚类解决方案(其他颜色)

我们不会讨论集群解决方案的质量,因为它显然不能代表数据。聚类解决方案最小化了多维直方图和 6 个正态分布(每个轴 3 个)的平均概率之间的差异。在这里,分布可以很容易地“交换”数据点,这可能会增加收敛时间。为什么不直接拟合 3 个多元正态分布?模拟分布参数有一个问题,因为有些参数有约束。协方差矩阵需要是正的,半正定的,并且需要有逆矩阵存在。正态分布中的标准偏差必须大于 0。该模型中使用的解决方案通过对每个单独的参数进行定制模拟来结合参数约束。我仍在寻找如何有效模拟多元正态分布的协方差矩阵的好公式。

为什么用 kernelml 代替期望最大化?

非正态分布(如泊松分布)可能不太适合多元正态聚类分析解决方案中的其他维度。此外,随着维数的增加,一个聚类是唯一具有非零值特征的聚类的概率也会增加。这给 em 算法提出了一个问题,因为它试图更新协变矩阵。唯一特征和其他维度之间的协方差将为零,或者另一个聚类接受具有该非零值的观测值的概率为零。

概率优化器优势:

参数的概率模拟比完全参数化的模型有更大的好处。首先,正则化包含在先验随机模拟中。例如,如果参数的先验随机模拟在-1 和 1 之间,则可以推断出参数将以同等的重要性更新。此外,当算法收敛时,每次迭代产生一组在全局或局部最小损失附近采样的参数。这样做有两个主要好处:1)可以为每个参数建立置信区间;2)每个参数集的预测输出可以是统一模型中的有用特征。KernelML 并不严格地从概率分布中生成样本。相反,它通过在引导绑定的小批量上更新参数来生成参数的分布。

集群示例的代码、其他示例和文档可以在 github 的中找到。

Tensorflow/sklearn 中的核 PCA vs PCA vs ICA

原文:https://towardsdatascience.com/kernel-pca-vs-pca-vs-ica-in-tensorflow-sklearn-60e17eb15a64?source=collection_archive---------9-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

GIF from this website

主成分分析对给定的数据执行线性变换,然而,许多真实世界的数据不是线性可分的。那么,我们能否利用更高维度的优势,同时又不大幅增加所需的计算能力呢?

请注意,这个帖子是给未来的自己看的,用来回顾和复习这个帖子上的材料。(还有自学)

讲座:内核主成分分析

PPT from this website

根据上面的 PPT,我会做一些对我有帮助的简短笔记。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

VAP Nik–Chervonenkis theory 告诉我们,如果我们将数据投影到一个更高维的空间,它会为我们提供更好的分类能力。(左图示例。)这可能类似于神经网络总体所做的,随着深度的增加,更多的抽象特征被提取,并且具有更好的特征来执行分类。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

内核技巧,一种在不牺牲太多计算时间的情况下将原始数据投影到更高维度的方法。(非线性特征映射)。和矩阵形式来归一化特征空间。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有效使用 KPCA 的例子,见上文。

KPCA 的不同使用案例

Paper from this website

Paper from this website

在第一篇论文中,作者使用 KPCA 作为预处理步骤,作为特征变换的手段,并与最小二乘支持向量机配对,对 DNA 微阵列进行分类。(微阵列数据具有高维度,因此在执行分类之前执行维度缩减技术是一个好主意。)在第二篇论文中,使用 KPCA 从功能性磁共振图像(fMRI)中提取特征,以对注意力缺陷多动障碍(ADHD)执行自动诊断。

tensor flow 中的 KPCA (RBF)层

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以像上面那样实现一个简单的前馈操作,在撰写本文时,我不会对输入数据实现反向传播。

KPCA vs PCA vs ICA

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

让我们从简单的开始,我们有一个线性不可分的 2D 数据点,现在为了验证我们的实现正在工作,让我们使用每个 KPCA、PCA 和 ICA 将我们的数据投影到二维空间中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

左图 →使用 KPCA 的投影
中图 →使用 PCA 的投影
右图→ 使用 ICA 的投影

从上面的例子我们可以看到,我们的实现工作正常,我们的数据现在是线性可分的。但是为了让事情变得更有趣,让我们看看这些方法在组织病理学图像上的效果。我正在使用来自骨髓活检组织病理学数据(HistBMP)的数据集。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如上所述,每幅图像都是 28*28 的灰度图像,我们将通过将 1000 幅图像压缩成 100 幅来找到特征图像。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

左图 →使用 KPCA
的投影中图 →使用 PCA
的投影右图→ 使用 ICA 的投影

总的来说,我们可以看到 PCA 试图捕捉全局变化,ICA 试图捕捉局部变化。但 KPCA 似乎首先捕捉到了全球变化,但当我们到达特征图像的下部时,我们可以看到它正在捕捉局部变化。

代码

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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

要访问这篇文章的代码,请点击这里

遗言

请注意,对于距离矩阵,我从这个网站借用了非循环形式,整体实现从 Sebastian Raschka 的“核技巧和通过 RBF 核 PCA 的非线性维度缩减”借用。

我一直想知道如何绘制每个特征值的方差,这是一篇很好的文章解释了其中的诀窍。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Image from this website

这也是我发现的一个有趣的视频。

video from this website

最后,有趣的是,主成分分析/ KPCA 受到方差膨胀和缺乏可推广性的影响,下面的论文提出了一个解决问题的方法。

Paper from this website

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

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

参考

  1. 主成分分析。(2015).塞巴斯蒂安·拉什卡博士。2018 年 9 月 7 日检索,来自https://sebastianraschka . com/Articles/2015 _ PCA _ in _ 3 _ steps . html
  2. 关于特征缩放和规范化。(2014).塞巴斯蒂安·拉什卡博士。检索于 2018 年 9 月 7 日,来自https://sebastianraschka . com/Articles/2014 _ about _ feature _ scaling . html
  3. 关于特征缩放和规范化。(2014).塞巴斯蒂安·拉什卡博士。检索于 2018 年 9 月 7 日,来自https://sebastianraschka . com/Articles/2014 _ about _ feature _ scaling . html
  4. 实施主成分分析(PCA)。(2014).塞巴斯蒂安·拉什卡博士。2018 年 9 月 7 日检索,来自https://sebastianraschka . com/Articles/2014 _ PCA _ step _ by _ step . html
  5. tf.ones | TensorFlow。(2018).张量流。检索于 2018 年 9 月 10 日,来自https://www.tensorflow.org/api_docs/python/tf/ones
  6. 距离矩阵矢量化技巧-流形博客-中。(2016).中等。检索于 2018 年 9 月 10 日,来自https://medium . com/data holiks-distillery/L2-distance-matrix-vectorization-trick-26aa 3247 ac6c
  7. matplotlib,P. (2018 年)。用 matplotlib 同时绘制两个直方图。堆栈溢出。检索于 2018 年 9 月 10 日,来自https://stack overflow . com/questions/6871201/plot-two-histograms-at-same-time-with-matplotlib
  8. tf.self _ 共轭 _eig | TensorFlow。(2018).张量流。检索于 2018 年 9 月 10 日,来自https://www . tensor flow . org/API _ docs/python/TF/self _ agreement _ EIG
  9. 核技巧和基于 RBF 核 PCA 的非线性降维。(2014).塞巴斯蒂安·拉什卡博士。检索于 2018 年 9 月 10 日,来自 https://sebastianraschka.com/Articles/2014_kernel_pca.html
  10. VAP Nik-Chervonenkis 理论。(2018).En.wikipedia.org。检索于 2018 年 9 月 10 日,来自https://en . Wikipedia . org/wiki/VAP Nik % E2 % 80% 93c hervonenkis _ theory
  11. Sidhu、n . as garian、r . Greiner 和 m . Brown(2012 年)。核主成分分析在基于 fMRI 的 ADHD 诊断中的降维作用。系统神经科学前沿,6。doi:10.3389/fnsys
  12. m .托马斯、k .布拉班特和 b .穆尔(2014 年)。核主成分分析的新带宽选择准则:降维和分类问题的方法。BMC 生物信息学,15(1),137。doi:10.1186/1471–2105–15–137
  13. t .亚伯拉罕森和 l .汉森(2011 年)。高维核主成分分析中方差膨胀的一种解决方法。机器学习研究杂志,12 期(6 月),2027–2044。从 http://jmlr.csail.mit.edu/papers/v12/abrahamsen11a.html取回
  14. j . tomczak(2018 年)。骨髓活检(HistBMP)的组织病理学数据。芝诺多。检索于 2018 年 9 月 10 日,来自https://zenodo.org/record/1205024#.W5bcCOhKiUm

Kickstarter 项目演练 Python 中的简单数据探索

原文:https://towardsdatascience.com/kickstarter-projects-walk-through-simple-data-exploration-in-python-c2302a997789?source=collection_archive---------7-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这篇文章分析了大约 380.000 个 Kickstarter 项目的数据集。我将带领您通过 Python 进行简单的数据探索,揭示 Kickstarter 项目中有趣的见解,以及在检查某个项目的成功(或失败)时哪些属性是重要的。总的来说,我们将看看 Python 的基本数据可视化和特征工程。

1.一般想法和想法

Kickstarter 是最著名的推广(主要是)创造性的、聪明的和有远见的想法和概念的平台之一。每个项目都寻求 Kickstarter 人群能够提供的资金,如果他们支持这个想法并希望看到它成功的话。然而,有许多项目失败了,即使最初的想法可能是令人信服的。这是为什么呢?本文试图深入研究与每个项目相关的属性,并揭示与 Kickstarter 项目相关的模式、见解和任何感兴趣的东西。

2.初始数据集和属性

该数据集包含来自 Kickstarter 的 378.661 个项目以及与每个项目相关的 12 个初始属性。让我们看看我们从每个项目中获得了哪些信息:


  1. 挺明显的,各自项目的名称。例如“日常冲泡咖啡”

  2. main_category 项目所属的主类别。例如“诗歌”、“美食”、“音乐”等等

  3. 类别
    对主要类别的更精确的描述。基本上,main_category 的一个子群(参见 2。).例如“饮料”,它是 main_category 属性中“食物”的子组。

  4. 货币
    项目的货币(如美元或英镑)

  5. 启动
    项目的启动日期。当我们稍后分析时间框架时,这将非常重要

  6. 截止日期
    项目的截止日期。正如上市日期一样,截止日期将变得和上市日期一样重要

  7. 美元 _ 质押 _ 真实
    项目在截止日期实现的美元金额

  8. usd_goal_real
    项目最初要求的美元金额

  9. 支持者
    实际投资该项目的支持者人数

  10. 国别项目原产国

  11. ID 项目 ID

  12. 状态
    项目最后成功了吗?状态是一个分类变量,分为成功*、失败、有效、取消、未定义和暂停等级别*。为了清楚起见,我们将只看一个项目是成功还是失败(因此,我们将删除未被归类为两者之一的所有项目)。失败或成功的项目约占所有项目的 88%。

让我们快速移除所有没有失败成功作为其状态的项目。

#return only successful and failed projects. This makes things more clear later on**data_kick = data_kick.loc[data_kick['state'].isin(
            ['successful', 'failed'])]**

让我们先来看看所有属性的数据类型,以便更好地理解数据,看看是否一切正常。

**data_kick.info()**ID                  378661 non-null int64
name                378657 non-null object
category            378661 non-null object
main_category       378661 non-null object
currency            378661 non-null object
deadline            378661 non-null object
goal                378661 non-null float64
launched            378661 non-null object
pledged             378661 non-null float64
state               378661 non-null object
backers             378661 non-null int64
country             378661 non-null object
usd pledged         374864 non-null float64
usd_pledged_real    378661 non-null float64
usd_goal_real       378661 non-null float64

可以看出,所有属性都倾向于采用精确的数据格式——要么是浮点型(因此是数字型),要么是对象型(因此是分类型)。唯一两个不符合期望格式的属性是我们的两个时间属性:发起截止日期。我们一会儿会处理这些。但是,让我们首先通过分析承诺的金额与项目的最初目标来了解数据集。

3.目标与承诺

让我们来看看一个项目的目标和实际承诺金额之间的关系。很明显,如果
承诺金额≥目标则一个项目被标记为成功,如果承诺金额<目标则被标记为不成功 下图显示了每个项目的目标和承诺金额以及项目的单个状态(仅显示了失败的成功的项目)。

#define colors (darkgreen for successful projects and darkred for failed ones**colors = ('darkgreen','darkred')**#create a plot using seaborn, adjust data to millions**ax = sns.scatterplot(data_kick.usd_pledged_real/1e6, data_kick.usd_goal_real/1e6, hue=data_kick.state, palette=colors)**#add blue line to better visualize the border between failed and successful projects**sns.lineplot(x=(0,50), y=(0,50), color='darkblue')**#set the axes from -1 to their maximum (-1 looks better than 0 actually)**ax.set(ylim=(-1,None), xlim=(-1,None))**#set labels and title**ax.set(xlabel='Amount Pledged in Millions', ylabel='Goal in Millions', title= 'Goal vs. Pledged')**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

直观地看,承诺超过/达到其目标的项目是成功的,显示为绿色,而失败的项目被标记为红色。看看与原点相交的蓝线:成功的项目总是在这条线的下面,而失败的项目在这条线的上面。这很有道理,不是吗?顺便说一句,你可以观察到,承诺金额最高的项目募集了大约 2000 万美元,目标最高的项目寻求超过 1.6 亿美元(但失败了)。是时候近距离观察了。

让我们放大一点,通过将两个轴的范围从 0 到 2000 万来更好地理解:

#we are using the same code as above expect for the axis limits, that are set from 0 Millions (-1 in the code) to 20 Millions**ax.set(ylim=(-1,20), xlim=(-1,20)**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这种放大允许更直观的方法:蓝线现在以 45°角与原点相交,因为我们相应地将轴调整到相同的范围。如前所述,线下的每个数据点(即项目)都成功了,而线上的每个数据点都失败了。

有趣的是,该图表明,不成功的项目通常没有接近目标就失败了,这意味着它们“没有水平移动到蓝线,而是停留在 x~0”。由此得出结论,Kickstarter 是一个“要么全有,要么全无”的平台。如果你没有成功,你可能根本就没有成功。另一方面,许多成功的项目远远超过了他们的既定目标,并保证是他们最初目标的数倍。

随着时间的推移,承诺金额和目标之间的关系是如何发展的?让我们看看按年份分组的整个时间框架内目标和承诺的发展情况。我们使用下面的代码来获得每年目标和承诺的累计金额。

#we choose the desired grouping intervall ('launched year') and the two variables pledge and goal that we are interested in and sum them up over the years.**data_kick_groupby_year = data_kick.groupby('launched_year')['usd_pledged_real', 'usd_goal_real'].sum()**#let's use this variable to plot the data over time:**ax = sns.lineplot(x=data_kick_groupby_year_sum.index, y= data_kick_groupby_year_sum.usd_pledged_real/1e9, linewidth= 4, label= 'USD Pledged', color= 'darkgreen')****sns.lineplot(x=data_kick_groupby_year_sum.index, y= data_kick_groupby_year_sum.usd_goal_real/1e9, linewidth= 4,label='USD Goal', color= 'darkred')**#set labels and title**ax.set(xlabel='Launch Year', ylabel='USD in Billion', title='Comparison Pledged vs Goal over Time')**

我们得到了这个图表:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

该图显示了各年度所有项目的目标和承诺的累计金额。例如,2013 年,所有已启动项目的财务目标总和略高于 10 亿美元。

可以看出,总目标的累积金额在 2013 年急剧增加,在 2015 年达到峰值,然后缓慢下降。另一方面,每年的累计认捐额稳步下降,但呈线性下降。实际上从 2015 年到 2017 年略有下降。

目标值剧增的原因是相当明显的,不是吗?项目数量急剧增加,如下图所示:

**data_kick['launched_year'].value_counts()**2015    65272
2014    59306
2016    49292
2017    43419
2013    41101
2012    38478
2011    24048
2010     9577
2009     1179

更多的项目,更多的目标。然而,这只是促成累积目标增加的一个因素。这些年来,不仅启动了更多的项目,而且每个项目的平均目标也在增加,如下所示:

#we group again. This time we take the average though:**data_kick_groupby_year_avg = data_kick.groupby('launched_year')['usd_pledged_real', 'usd_goal_real'].mean()****print(data_kick_groupby_year_avg)**launched_year  usd_pledged_real  usd_goal_real   
2009                2375.535335    6799.507430
2010                3041.302897   13270.062647
2011                4256.161398    9715.957062
2012                8247.532875   17862.738838
2013               11326.171820   24946.164929
2014                8466.032884   47346.942048
2015               10058.201943   70094.513735
2016               12847.626664   52879.135752
2017               13564.594251   39073.175276

平均目标随着时间的推移而增加(从大约。2011 年向 ca 支付 10,000 美元。2016 年为 53,000 美元,随后在 2017 年大幅下降)。因此,不仅项目数量增加了,每个项目的平均目标也增加了。随着时间的推移,这些因素的结合增加了累积目标图。现在,时间够了。让我们看看名字!

4.名字的作用

另一个可能影响项目结果的方面是它的名字。我们希望更短、更简洁的名字比冗长、模糊的短语更能吸引(潜在)投资者。

例如,一个像“Circa Vitae 要求群众基金会录制我们的全长专辑…到乙烯基!!!"不如“第 X 天”吸引人(第一个项目确实失败了,后者成功了)。数据支持这个假设吗?让我们做一点特性工程,创建一个包含项目名称的字符串长度的新列。

**data_kick['name_length'] = data_kick['name'].str.len()**

我们现在将每个项目名称的字符串长度保存在 *name_length 中。*让我们看看这个栏目是什么样子的。

**data_kick.loc[:,['name','name_length']].head(5)**name                                                  name_length
0                    The Songs of Adelaide & Abullah         31.0
1      Greeting From Earth: ZGAC Arts Capsule For ET         45.0
2                                     Where is Hank?         14.0
3  ToshiCapital Rekordz Needs Help to Complete Album         49.0
4  Community Film Project: The Art of Neighborhoo...         58.0

让我们创建一个散点图来显示 name_lengthUSD _ pleated _ real之间的关系。

#create scatterplot for pledged amount and name length**ax = sns.scatterplot(data_kick.usd_pledged_real/1e6, data_kick.name_length, hue=data_kick.state, palette=colors)**#set labels accordingly**ax.set(xlabel='Amount Pledged in Millions', ylabel='Character Length of Project Name', title= 'The Importance of Choosing the Right Name')**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有趣的是,如果一个项目的名称超过 60 个字符,承诺的金额将分布在 0 附近,并且(或多或少)所有项目似乎都会失败。让我们来看看这些项目的目标与<60 projects and check the following hypothesis:

  • Could it be that >相比,60 个角色的项目确实有明确的高目标值,他们的目标是收集?为了验证这个假设,让我们来看看 name_length ≥60 的平均目标name_length < 60 的平均目标

让我们来看看这个项目分组,根据它们的目标大小和它们的项目状态有超过 60 个字符:

#create a variable for all data points above a name length of 60**data_kick_60_up = data_kick.loc[data_kick.name_length >= 60, ['state','usd_goal_real']]**#create a variable for the data points below a name length of 60**data_kick_60_down = data_kick.loc[data_kick.name_length < 60, ['state','usd_goal_real']]**#compare the median of both groups. We see that the above 60 group actually has a higher median than the below 60 group.**print(data_kick_60_up['usd_goal_real'].median() > data_kick_60_down['usd_goal_real'].median())****True**

我们可以看到,对于≥60 岁的群体,他们的目标中值(以及均值)更高(布尔返回“真”)。因此,我们不能得出名称长度≥60 的项目仅仅具有较低的目标值的结论。相反,他们实际上比那些名字平均为 60 个字符的项目有更高的目标。这可能是他们失败的原因。那不是很了不起吗!

如前所述,基于项目名称长度的“成功细分”最直观的解释是,长名称有听起来不专业、不切题和不投入的倾向(显然这只是个人观点)。因此,他们承诺的少了,失败了。

5.探索时间

如前所述,除了时间变量,所有变量都是正确的类型。幸运的是,Pandas 允许我们快速地将日期变量更改为实际日期:

data_kick['launched'] = pd.to_datetime(data_kick['launched'],
format='%Y-%m-%d %H:%M:%S')data_kick['deadline'] = pd.to_datetime(data_kick['deadline'],
format='%Y-%m-%d %H:%M:%S')

如果您现在检查 data_kick.info() ,您将看到已启动截止日期具有正确的数据类型。现在我们已经将时间变量转换成了正确的格式,让我们更仔细地看看它们。特别是,我们有两个有趣的时间栏:项目在启动的确切日期以及每个项目的截止日期。现在怎么办?

也许项目启动的日期会影响它的成功,也许启动和截止日期之间的时间也会影响项目的结果。有人可能会说,上市和截止日期之间的时间间隔越长,就有越多的时间被关注和赚钱。因此,跨度越长,成功的机会就越大。此外,项目启动的月份(甚至时间)可能会影响其结果。但是数据支持这些假设吗?让我们先看看发射时间。

我们可以通过以下代码提取项目启动的时间:

**data_kick['launched_hour'] = 
                          data_kick.launched.apply(lambda x: x.hour)****data_kick['launched_month'] = 
                         data_kick.launched.apply(lambda x: x.month)**

我们刚刚创建了两个新列,分别包含项目启动的月份和时间。让我们看看这些新属性是否包含帮助我们理解项目是否成功的模式。让我们想象一下这一天的时刻和项目的成功:

#extract launched hour and state for all states 'successful' and append the same for all states 'failed'.**data_kick = data_kick.loc[data_kick.state == 'successful',['launched_hour', 'state']]
.append(data_kick.loc[data_kick.state == 'failed',   ['launched_hour', 'state']])**#plot the data**ax = sns.countplot(x=extracted_levels_hour.launched_hour, 
              hue = extracted_levels_hour.state)**#set labels and title**ax.set(xlabel='Launched Hour', ylabel='Number of Projects', title= 'Hour the Project was Launched')**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里有一些有趣的事情需要注意:

  1. 一般来说,与早上 7 点到下午 3 点之间的时间相比,在清晨(0 点到 6 点之间)和傍晚(下午 4 点到 23 点之间)启动的项目更多。
  2. 成功和失败项目之间的比率在一天中的不同时间有所不同。我们可以看到,对于在下午 2 点到 3 点之间启动的项目,失败和成功项目的比率几乎是 1(实际上有点小)。相反,在晚上 8 点以后启动的项目的比率要差得多(换句话说:失败的项目远远多于成功的项目)。

让我们看看项目启动的那个月是否揭示了它的状态:

#plot the data**sns.countplot(x=data_kick.launched_hour, 
              hue = data_kick.state)**#set labels and title**ax.set(xlabel='Month of the Year', ylabel='Number of Projects', title= 'Month the Project was Launched')**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

与一天中的某个时刻相比,月份似乎并没有显示出任何令人惊讶的见解。但是,请注意,与所有其他月份相比,12 月启动的项目要少得多。

最后,让我们看看推出截止之间的跨度。通过从 deadline 中减去 launched,我们可以很容易地获得这个跨度(这是我们之前完成的 date_time 转换的一个有用特性)。在下面的代码中,我们取 launched 和 deadline 之间的差值,并立即提取日期(’ timedelta64[D]'):

**data_kick['project_time'] =  
(data_kick['deadline'] -data_kick['launched']).astype('timedelta64[D]').astype(int)****data_kick['project_time'].head(5)**0    58
1    59
2    44
3    29
4    55

您可以看到,我们的新列 project_time 以天为单位来度量启动和截止日期之间的时间跨度。例如,数据集中的第一个项目([0])在 Kickstarter 上呆了将近两个月(58 天)。在我们想象一个项目的跨度之前,让我们先看看结果。*data _ kick[’ project _ time ']*的最大值是 91,因此我们可以按周对数据进行分类,以获得更直观的概览,而不是获得 91 个不同的结果。让我们首先编写一个允许我们有效绑定数据的函数:

**def discretizer(data, binning_values, labels):
**    *"""
    Input: data series that should be discretized
    bininningvalues: enter numerical array of binning cuts
    labels: enter string array of names for bins**Output: gives you a list of binned data
    """
   ***data = pd.cut(data, bins=binning_values, labels=labels)
    return data**

让我们解析 project_time 并按周绑定它:

#save the binned data as new column
**data_kick["project_time_weeks"] =**#call the discretizer funtion
**discretizer(**#first thing we pass is the column we want to be discretized, project_time in this case**data_kick['project_time'],**#next, we create a list with the different binning intervals (starting at 0 and then going in steps of 7 until the maximum value of project_time (+7)**list(range(0,max(data_kick['project_time'])+7,7)),**#last but not least, we need to label the bins. We number them by creating a list from 1 (first week) to the last week depending on the maximum value of project_time**list(range(1,max(list(range(0,max(data_kick['project_time'])//7+2))))))**

那么新列 project_time_weeks 是什么样子的呢?

#print first 5 rows of both columns:**data_kick[['project_time', 'project_time_weeks']].head(5)**#resulting in:**project_time  project_time_weeks
          34                  5
          19                  3
          29                  5
          27                  4
          14                  2**

我们可以看到日子被成功地分成了几个星期。例如,19 天转换为第 3 周。现在,最后,让我们看看发布截止日期之间的时间是否会影响项目的成功:

#plot the data
**sns.countplot(x=data_kick.project_time_discret, 
              hue = data_reduced.state, palette=colors)**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们立刻注意到,大多数项目在 Kickstarter 上放了大约 5 周,几乎所有项目都在 1 到 9 周之间(所以比 2 个月多一点)。

成功和失败项目的比例随着时间的推移而变化:运行时间短(1-4 周)的项目比运行时间中长(5-13 周)的项目更容易成功。这种见解可以支持这样一种假设,即项目存在的时间越长,成功的可能性就越大,正如人们可能事先假设的那样(->项目存在的时间越长,人们有更多的机会看到它,它就越有可能成功。这似乎不是真的)。

6.支持者的角色

现在我们来看看支持每个项目的支持者的角色。总的来说,我们期望大量的支持者对特定项目成功的可能性做出积极的贡献。但事实真的是这样吗?

让我们想象一下支持者的数量、承诺的金额和项目的状态。

**ax = sns.scatterplot(x=data_kick.backers, y=data_kick.usd_pledged_real/1e6, hue=data_kick.state, palette=colors)**#we set the xlim to 100,000 backers to get a better overview
**ax.set(ylim=(-1,None), xlim=(-1,100000))**#set labels and title**ax.set(xlabel='Number of Backers', ylabel='USD Pledged in Million', title= 'Backer vs Pledge')**

该代码会生成下图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个结果并不令人惊讶,是吗?我们可以清楚地看到,支持者的数量与项目的成功相关。大多数失败的项目也有相对较少的支持者。然而,让我们更进一步:不仅每个项目支持者的绝对数量是有趣的,而且每个项目每个支持者花费的平均金额也是有趣的。为了得到这个属性,我们需要将质押的金额除以实际支持者的数量。让我们快点做那件事。

#divide usd_pledged_real by number of backers (note that a Zerodivisionerror will occur if we divide by zero resulting in a NaN. We replace these with 0**data_kick['backer_ratio'] = (data_kick['usd_pledged_real']//data_kick['backers']).fillna(0)**#set label and title**ax.set(xlabel='Pledge/Backer Ratio (funding per backer)', ylabel='USD Pledged in Million', title= 'Backer Ratio vs Pledge')**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

看一下资助者比率(即认捐总额除以资助者总数),我们可以看到大多数成功项目的资助者比率相对较低,这意味着每个资助者贡献的金额相对较小。根据图表,该金额在略高于 0 美元到大约 700 美元之间变化。我们可以看到,大多数支持者比率相对较高(< 2000 美元)的项目都失败了。

既然我们已经看了时间框架、名称、支持者和许多其他细节,是时候停止我们的小演练了。仍然有许多可能的见解和模式要揭示,但我把这个留给你;-)

6.结论

这篇关于 Python 中基本数据可视化的介绍性文章揭示了一些关于 Kickstarter 项目的有趣事实。我们总结一下:

  1. 失败的项目,大多数时候都是惨败。如第三部分所述,他们“不能向蓝线移动”。要么全有,要么全无;-).
  2. 随着时间的推移,项目的数量和平均目标急剧增加(至少到 2015 年),而提供的承诺仅缓慢增加。
  3. 名字起着很大的作用。如果你想启动一个项目,确保它的名字准确而简短。“顽固的独家斯科特·托尔森叔叔 Argh NOIR Mini Qee”听起来可能很好笑(实际上并不好笑),但不会让你去任何地方。
  4. 尝试在下午 12 点到 4 点之间启动你的项目,不要在 12 月启动(好吧,我不是 100%认真的)。

我希望你喜欢这本书。我感谢任何形式的反馈,(建设性的)批评或其他建议。谢谢!

用于分类的 k 均值聚类

原文:https://towardsdatascience.com/kmeans-clustering-for-classification-74b992405d0a?source=collection_archive---------2-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

背景

聚类作为一种在观察值中寻找子群的方法,广泛应用于市场细分等应用中,在这些应用中,我们试图在数据中找到一些结构。虽然是一种无监督的机器学习技术,但聚类可以用作有监督的机器学习模型中的特征。

聚类是一种无监督的机器学习,其目的是找到同质的子组,使得同一组(聚类)中的对象彼此比其他对象更相似。

KMeans 是一种聚类算法,它将观察值分成 k 个聚类。因为我们可以指定聚类的数量,所以它可以很容易地用于分类,在分类中,我们将数据分成等于或大于类数量的聚类。

我将使用 scikit learn 附带的 MNIST 数据集,它是一个带标签的手写数字的集合,并使用 KMeans 在数据集中查找聚类,并测试它作为一个特征有多好。

履行

为此,我创建了一个名为 clust 的,它在初始化时接收一个 sklearn 数据集,并将其分为训练和测试数据集。

函数 KMeans 将 KMeans 聚类应用于训练数据,将类的数量作为要形成的聚类的数量,并为训练和测试数据创建标签。参数输出控制我们希望如何使用这些新标签,“添加”将标签作为特征添加到数据集中,“替换”将使用标签而不是训练和测试数据集来训练我们的分类模型。

结果

在第一次尝试中,仅使用 KMeans 找到的聚类来训练分类模型。仅这些聚类就给出了准确度为 78.33%的像样的模型。让我们将其与开箱即用的逻辑回归模型进行比较。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这种情况下,我只使用特征(灰度强度值)来训练逻辑回归模型。其结果是具有 95.37%准确度的好得多的模型。让我们将分类添加为一个特征(列)并训练相同的逻辑回归模型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在我们的最后一次迭代中,我们使用聚类作为特征,结果显示比我们以前的模型有所改进。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外卖食品

聚类除了是一种无监督的机器学习之外,还可以用于创建聚类作为改进分类模型的特征。结果显示,它们本身不足以进行分类。但是当用作特征时,它们提高了模型的准确性。

你可以使用我创建的类来调整和测试不同的模型,例如测试一个随机的森林分类器,分享一些我在评论中没有找到的东西。

kMeans 对地图数据进行哈希搜索

原文:https://towardsdatascience.com/kmeans-hash-search-map-search-in-o-n²lgn-33743ece434f?source=collection_archive---------11-----------------------

kMeans 聚类算法因其作为“无监督人工智能”学习算法的分类而非常受欢迎。这种聚类的工作原理是将数据集的要素组合在一起,以区分实体。实体可以是地理上的一个地方,例如加利福尼亚的旧金山。

当你长时间使用谷歌地图 API 处理街道数据时,你会产生一种新的语感。纬度/液化天然气坐标系统是观察庞大社会的一种不可思议的方式。您甚至可以立即认出下图中的 JSON 坐标是在美国还是在英国。

{ lat: 36.778, lng: -119.4179 }

当我们试图理解像旧金山湾区这样的大规模道路网络时,(如下图)。我们意识到我们需要开发二维(lat/lng)空间的搜索算法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

San Francisco Road Map

旧金山有很多很酷的地方可以看,初创公司也有很多机会找到很酷的办公场所并招募他们的团队。然而,对于我们当前的算法来说,搜索给定半径内的地点并计算最短路径并不容易。

从计算复杂性的角度来看,使用地图数据和创建地图用户界面的问题在于这些数据的巨大复杂性。以运行在 O(n log n)中的 mergsort 等传统排序算法为例。这个算法只适用于一维向量空间。而使用某种径向基函数将 lat / lng 映射到一维坐标平面可能是一种有效的解决方案。本文将讨论开发一种搜索算法,同时保持更自然的二维 lat/lng 数据集。

在现代计算机系统中对道路网络数据建模的标准是图形数据库。图被表示为 n×n 矩阵,其中 n[i][j]处的值包含节点之间的距离。然而,典型的道路数据被编码为一个非常精细的矩阵,lat/lng 十进制值跟踪到 7 位。L1/曼哈顿纬度/液化天然气距离将唐人街(SF)和恩巴卡德罗路(SF)区分为. 000008,这并不罕见。

由于这个原因,在将 lat/lng 全部乘以大约 10,000,000 后,在我们的数据集中开始编码可能是有用的。这样,在使用图形数据库搜索道路地图的几何图形之后,我们可以很容易地用“500 英尺”来表示距离。

怎样才能用拓扑几何加速空间查询?

旅行商问题可以说是计算机科学中最著名的 NP 难问题。此问题计算通过迷宫(如道路网络)并到达最终目的地所需的最短时间。

正如许多旅行者所知,在一个大城市的道路网络中导航以计划最终的假期是非常困难的。

以旧金山为例:

第一天:{观景廊/渔人码头/日本广场/小意大利/唐人街/甲骨文球馆/美国电话电报公司球馆/金门公园}

这些令人惊叹的旅游目的地都可以组织成一系列

{ lat: (float), lng: (float) }

使用地图需要大量的数据工程和管道来帮助纠正维度失衡和填充缺失的数据点。

数据工程:将 XML 转换成 JSON。我们的大部分处理都是在 csv 文件和 JSON 文件上完成的,前者在 Python 中容易阅读,后者在 JavaScript 中容易阅读。JavaScript 特别有用,因为它可以连接前端、后端和数据库逻辑,构建强大的 web 应用程序。

研究地理边界数据时,您可能会发现 XML 格式的数据,如下所示:

<state name="Minnesota" colour="#0000ff">
<point lat="43.5008" lng="-96.4517"/>
<point lat="43.5017" lng="-91.2195"/>
<point lat="43.8226" lng="-91.3101"/>
<point lat="43.9651" lng="-91.4914"/>
<point lat="44.1113" lng="-91.7084"/>
<point lat="44.2806" lng="-91.8951"/>
<point lat="44.3710" lng="-91.9556"/>
<point lat="44.4357" lng="-92.2083"/>
<point lat="44.5513" lng="-92.3360"/>

我们需要编写一个 python 脚本来将它转换成一个字符串列表,我们可以将它硬编码成 JavaScript 来概括这些状态。

var MinnesotaCoords = [
 {lat: 36.9971, lng: -109.0448 },
 {lat: 31.3337, lng: -109.0489 },
 {lat: 31.3349, lng: -108.2140 },
 {lat: 31.7795, lng: -108.2071 },
 {lat: 31.7830, lng: -106.5317 },
 {lat: 32.0034, lng: -106.6223 },
 {lat: 31.9999, lng: -103.0696 },
 {lat: 36.9982, lng: -103.0023 },
 {lat: 36.9982, lng: -109.0475 },
];

一旦你有了这些,你将能够使用 kMeans 算法来组织地理信息,比如下面用 Python 写的大量代码块。

pointSet = [["Arizona (AZ)", 34.054455, -111.293573], ["Arkansas (AR)", 35.201050, -91.831833],
 ["California (CA)", 36.778261, -119.417932], ["Colorado (CO)", 39.550051, -105.782067], 
 ["Connecticut (CT)", 41.603221, -73.087749], ["Florida (FL)", 27.664827, -81.515754], 
 ["Georgia (GA)", 32.165622, -82.900075], ["Illinois (IL)", 40.633125, -89.398528], 
 ["Maryland (MD)", 39.045755, -76.641271], ["Michigan (MI)", 44.314844, -85.602364] ];
cluster1 = []
cluster2 = []
cluster3 = []
cluster4 = []
cluster5 = []
def calculateDistance(a,b):
    return (abs(a[1] - b[1]) + abs(a[2] - b[2]))
def getMeanValue(pointSet):
    xSum = 0
    ySum = 0
    for i in range(len(pointSet)):
        xSum += pointSet[i][1]
        ySum += pointSet[i][2]
    xAvg = 0
    yAvg = 0
    if (len(pointSet) != 0):
        xAvg = xSum / len(pointSet)
        yAvg = ySum / len(pointSet)
    return ["DimensionFill", xAvg, yAvg]
def initializeCentroids(pointSet):
    mean = getMeanValue(pointSet)
    centroid1 = ["DimensionFill", mean[1] + 10, mean[2] + 10]
    centroid2 = ["DimensionFill", mean[1] + 5, mean[2] - 5]
    centroid3 = ["DimensionFill", mean[1], mean[2]]
    centroid4 = ["DimensionFill", mean[1] - 5, mean[2] - 5]
    centroid5 = ["DimensionFill", mean[1] - 10, mean[2] - 10]
    return [centroid1, centroid2, centroid3, centroid4, centroid5]

centroids = initializeCentroids(pointSet)
centroid1 = centroids[0]
centroid2 = centroids[1]
centroid3 = centroids[2]
centroid4 = centroids[3]
centroid5 = centroids[4]
j = 1
for i in range(len(pointSet)):
    dist1 = calculateDistance(pointSet[i], centroid1)
    dist2 = calculateDistance(pointSet[i], centroid2)
    dist3 = calculateDistance(pointSet[i], centroid3)
    dist4 = calculateDistance(pointSet[i], centroid4)
    dist5 = calculateDistance(pointSet[i], centroid5)
    if (dist1 < dist2 and dist1 < dist3 and dist1 < dist4 and dist1 < dist5):
        cluster1.append(pointSet[i])
    elif (dist2 < dist1 and dist2 < dist3 and dist2 < dist4 and dist2 < dist5):
        cluster2.append(pointSet[i])
    elif (dist3 < dist1 and dist3 < dist2 and dist3 < dist4 and dist3 < dist5):
        cluster3.append(pointSet[i])
    elif (dist4 < dist1 and dist4 < dist2 and dist4 < dist3 and dist4 < dist5):
        cluster4.append(pointSet[i])
    else:
        cluster5.append(pointSet[i])
# j is number of iterations for kMeans
j = 5
while (j > 0):
    newCluster1 = []
    newCluster2 = []
    newCluster3 = []
    newCluster4 = []
    newCluster5 = []
    for k in range(len(cluster1)):
        dist1 = calculateDistance(cluster1[k], centroid1)
        dist2 = calculateDistance(cluster1[k], centroid2)
        dist3 = calculateDistance(cluster1[k], centroid3)
        dist4 = calculateDistance(cluster1[k], centroid4)
        dist5 = calculateDistance(cluster1[k], centroid5)
        if (dist1 < dist2 and dist1 < dist3 and dist1 < dist4 and dist1 < dist5):
            newCluster1.append(cluster1[k])
        elif (dist2 < dist1 and dist2 < dist3 and dist2 < dist4 and dist2 < dist5):
            newCluster2.append(cluster1[k])
        elif (dist3 < dist1 and dist3 < dist2 and dist3 < dist4 and dist3 < dist5):
            newCluster3.append(cluster1[k])
        elif (dist4 < dist1 and dist4 < dist2 and dist4 < dist3 and dist4 < dist5):
            newCluster4.append(cluster1[k])
        else:
            newCluster5.append(cluster1[k])
    for k in range(len(cluster2)):
        dist1 = calculateDistance(cluster2[k], centroid1)
        dist2 = calculateDistance(cluster2[k], centroid2)
        dist3 = calculateDistance(cluster2[k], centroid3)
        dist4 = calculateDistance(cluster2[k], centroid4)
        dist5 = calculateDistance(cluster2[k], centroid5)
        if (dist1 < dist2 and dist1 < dist3 and dist1 < dist4 and dist1 < dist5):
            newCluster1.append(cluster2[k])
        elif (dist2 < dist1 and dist2 < dist3 and dist2 < dist4 and dist2 < dist5):
            newCluster2.append(cluster2[k])
        elif (dist3 < dist1 and dist3 < dist2 and dist3 < dist4 and dist3 < dist5):
            newCluster3.append(cluster2[k])
        elif (dist4 < dist1 and dist4 < dist2 and dist4 < dist3 and dist4 < dist5):
            newCluster4.append(cluster2[k])
        else:
            newCluster5.append(cluster2[k])
    for k in range(len(cluster3)):
        dist1 = calculateDistance(cluster3[k], centroid1)
        dist2 = calculateDistance(cluster3[k], centroid2)
        dist3 = calculateDistance(cluster3[k], centroid3)
        dist4 = calculateDistance(cluster3[k], centroid4)
        dist5 = calculateDistance(cluster3[k], centroid5)
        if (dist1 < dist2 and dist1 < dist3 and dist1 < dist4 and dist1 < dist5):
            newCluster1.append(cluster3[k])
        elif (dist2 < dist1 and dist2 < dist3 and dist2 < dist4 and dist2 < dist5):
            newCluster2.append(cluster3[k])
        elif (dist3 < dist1 and dist3 < dist2 and dist3 < dist4 and dist3 < dist5):
            newCluster3.append(cluster3[k])
        elif (dist4 < dist1 and dist4 < dist2 and dist4 < dist3 and dist4 < dist5):
            newCluster4.append(cluster3[k])
        else:
            newCluster5.push(cluster3[k])
    for k in range(len(cluster4)):
        dist1 = calculateDistance(cluster4[k], centroid1)
        dist2 = calculateDistance(cluster4[k], centroid2)
        dist3 = calculateDistance(cluster4[k], centroid3)
        dist4 = calculateDistance(cluster4[k], centroid4)
        dist5 = calculateDistance(cluster4[k], centroid5)
        if (dist1 < dist2 and dist1 < dist3 and dist1 < dist4 and dist1 < dist5):
            newCluster1.append(cluster4[k])
        elif (dist2 < dist1 and dist2 < dist3 and dist2 < dist4 and dist2 < dist5):
            newCluster2.append(cluster4[k])
        elif (dist3 < dist1 and dist3 < dist2 and dist3 < dist4 and dist3 < dist5):
            newCluster3.append(cluster4[k])
        elif (dist4 < dist1 and dist4 < dist2 and dist4 < dist3 and dist4 < dist5):
            newCluster4.append(cluster4[k])
        else:
            newCluster5.push(cluster4[k])
    for k in range(len(cluster5)):
        dist1 = calculateDistance(cluster5[k], centroid1)
        dist2 = calculateDistance(cluster5[k], centroid2)
        dist3 = calculateDistance(cluster5[k], centroid3)
        dist4 = calculateDistance(cluster5[k], centroid4)
        dist5 = calculateDistance(cluster5[k], centroid5)
        if (dist1 < dist2 and dist1 < dist3 and dist1 < dist4 and dist1 < dist5):
            newCluster1.append(cluster5[k])
        elif (dist2 < dist1 and dist2 < dist3 and dist2 < dist4 and dist2 < dist5):
            newCluster2.append(cluster5[k])
        elif (dist3 < dist1 and dist3 < dist2 and dist3 < dist4 and dist3 < dist5):
            newCluster3.append(cluster5[k])
        elif (dist4 < dist1 and dist4 < dist2 and dist4 < dist3 and dist4 < dist5):
            newCluster4.append(cluster5[k])
        else:
            newCluster5.append(cluster5[k])
    cluster1 = newCluster1
    cluster2 = newCluster2
    cluster3 = newCluster3
    cluster4 = newCluster4
    cluster5 = newCluster5
    centroid1 = getMeanValue(cluster1)
    centroid2 = getMeanValue(cluster2)
    centroid3 = getMeanValue(cluster3)
    centroid4 = getMeanValue(cluster4)
    centroid5 = getMeanValue(cluster5)
    j -= 1print("Cluster1")
print(cluster1)
print("Cluster2")
print(cluster2)
print("Cluster3")
print(cluster3)
print("Cluster4")
print(cluster4)
print("Cluster5")
print(cluster5)
print("Centroid 1")
print(centroid1)
print("Centroid 2")
print(centroid2)
print("Centroid 3")
print(centroid3)
print("Centroid 4")
print(centroid4)
print("Centroid 5")
print(centroid5)

如果你觉得这段代码有用,请查看我的开源 Github 库和我参与的其他代码项目:https://github.com/CShorten

另外,请在媒体上关注我,获取更多类似的文章,发表在值得关注的

CShorten

是佛罗里达大西洋大学的计算机科学学生。对软件经济学、深度学习和软件工程感兴趣。请关注我的频道,获取这些领域的更多文章。

KNN # 3——编码我们的乳腺癌分类器

原文:https://towardsdatascience.com/knn-3-coding-our-breast-cancer-classifier-503b804988f8?source=collection_archive---------9-----------------------

给我看看代码!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为了开始这个项目,我们需要数据,让我们下载在之前的文章中看到的乳腺癌威斯康星州数据集。

阅读上一篇文章,您可以知道如何下载数据文件

在我们开始之前的一些观察,在我找到那个数据集之后,我在那里做了一些非常简单的修改:

  • 我删除了 ID 列,因为它不能帮助我们对癌症类型进行分类;
  • 我把班级列的位置改到了最后一列;

下载后,您有两个选择:

—通过此链接下载我修改的一个数据集,方便 KNN 开发;

或者

—按部就班地执行,并使代码适应数据集;

好了,我们开始吧!首先,本教程的完整代码在 github 的这个链接上。

当您有了数据集后,让我们在项目文件夹中创建一个名为“datas”的文件夹,并将所有数据集放在那里。

在根文件夹中,我们将创建名为 main.go 的文件

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我使用 VS 代码编辑器来开发这个算法,但是你可以使用任何你想要的其他文本/代码编辑器。

让我们定义包名并创建主函数。

为了开始我们的项目,我们需要将数据集加载到内存中,所以让我们创建一个方法来读取 csv 并返回一个数组给我们。

现在,我们的整个数据集都在变量" records "
中,如果我在终端中打印变量的值,我们将得到以下输出。

fmt.Println(records)…[7.76 24.54 47.92 181 0.05263 0.04362 0 0 0.1587 0.05884 0.3857 1.428 2.548 19.15 0.007189 0.00466 0 0 0.02676 0.002783 9.456 30.37 59.16 268.6 0.08996 0.06444 0 0 0.2871 B]]

然后我们有我们的数据集,我们将进行几次迭代,并应用我们在文章# 1 中看到的所有理论。
得到数据集后的下一步,将把它分割成 2 部分,第一部分是训练数据,第二部分是测试数据。
训练数据:这是已经分类的数据,我们将用它来训练我们的算法;
**测试数据:**是我们用来验证我们算法准确性的分类数据;
让我们将数据集划分如下,对于每个类,我们将获得 70%的数据训练,每个类的剩余部分(30%)用于测试。

首先让我们从数据集中提取唯一的类,为此我们将使用以下逻辑;

  • 从我们的数据集(记录)中提取包含类值的列;
    ->Make distinct()清除重复值从而得到我们唯一的类;

【getcolum(…)】

getCollum 方法接收一个矩阵和一个列索引作为参数,所以我们使用 column index 变量进入矩阵的列索引,并且对于该列中的每一行,我们将它添加到一个我们将要返回的新数组中。

【独特(……)】

通过 getcolum 返回,我们将把这个值传递给 distict()方法,该方法接收一个数组作为参数,并且对于这个数组的每个元素:

  • 验证这个相同的元素是否在“en centred”映射(字典)

  • 中,如果它不存在,它将把这个元素添加到“en centred”映射变量

  • 中,如果它存在,什么也不做;最后,我们将把地图放回没有重复分类的地方。
    当我们打印值“classes”时 fmt。Println (classes) 我们将拥有一个包含两个值的数组【M,B】
    让我们遍历这些类,从每个类中获取 70%的数据用于训练,30%的数据用于测试。

让我们用 70%和 30%的数据创建我们的训练矩阵和测试矩阵;

getValuesByClass()方法接收一个数组和一个类,所以我们将过滤我们的数组以获得特定的类数据。
在第一次迭代中,数组中的值对应于单个类,例如:m。
在第二次类循环迭代中,变量“values”将包含对应于类:b 的数据。
在我们仅过滤了对应于某个类的数据后,我们需要将它划分为训练数据和测试数据,我们将在以下代码中执行此操作:

当我们将测试和训练数据放入循环的范围时,让我们将数组放入循环范围之外。

我知道,用一个循环来连接一个数组/片并不是最好的方法,但是请记住,我是以我所能做到的最有启发性的方式来编码的,所以请随意修改它。
分离数据后,我们来测试一下!
让我们对测试数据集中的每一行进行排序,并统计我们命中了多少行,错过了多少行。

最后我们可以打印一些小东西:

fmt.Println(“Total de dados: “, len(records))
 fmt.Println(“Total de treinamento: “, len(train))
 fmt.Println(“Total de testes: “, len(test))
 fmt.Println(“Total de acertos: “, hits)
 fmt.Println(“Porcentagem de acertos: “, (100 * hits / len(test)),   “%”)

最后,当我们在终端中运行“go run main.go”时,我们将得到以下结果:

Total de dados: 569
Total de treinamento: 513
Total de testes: 513
Total de acertos: 484
Porcentagem de acertos: 94 %

在这种情况下,人们对哈林 22 岁以下的孩子进行了分类:

如果你想看看你的算法的命中和错误数据,我们可以通过取消注释上面要点的第 22 行来打印测试数据的分类:

//fmt.Println(“tumor: “, test[i][columnIndex], “ classificado como:

e .泰雷莫斯·塞吉特·萨伊达:

我们将得到以下输出:

tumor: M classificado como: B
tumor: M classificado como: M
tumor: M classificado como: M
tumor: M classificado como: M
tumor: M classificado como: M
tumor: M classificado como: M
tumor: B classificado como: B
tumor: B classificado como: B
tumor: B classificado como: B
tumor: B classificado como: B
tumor: B classificado como: B
tumor: B classificado como: B
tumor: B classificado como: B
tumor: B classificado como: B
Total de dados: 569
Total de treinamento: 513
Total de testes: 513
Total de acertos: 479
Porcentagem de acertos: 93 %

感谢阅读所有内容,希望你觉得很有用。

蟒蛇皮 KNN

原文:https://towardsdatascience.com/knn-in-python-835643e2fb53?source=collection_archive---------7-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by <a href=”https://stocksnap.io/author/11667">Henry Lorenzatto from <a href=”https://stocksnap.io">StockSnap

摘要

在这篇文章中,你将了解到一个非常简单的但强大的算法,叫做 KNNK-最近邻。第一部分将包含该算法的详细而清晰的解释。在本文的最后,你可以找到一个使用 KNN(用 python 实现)的例子。

KNN 解释道

KNN 是一种非常流行的算法,它是十大人工智能算法之一(参见十大人工智能算法)。它的流行源于这样一个事实,即它非常容易理解和解释,但很多时候它的准确性与其他更复杂的算法相当,甚至更好。

KNN 是一种监督算法(这意味着训练数据被标记,参见监督和非监督算法),它是非参数懒惰(基于实例)

为什么是懒?因为它不显式学习模型,但它保存所有训练数据并使用整个训练集进行分类或预测。这与其他技术形成对比,如 **SVM,**你可以毫无问题地丢弃所有非支持向量。

这意味着训练过程非常快,它只是保存数据集中的所有值。真正的问题是巨大的内存消耗(因为我们必须存储所有的数据)和在测试时间的时间复杂度(因为给定的观察分类需要运行整个数据集)。但总的来说,在小数据集的情况下(或者如果你有大量的时间和内存)或出于教育目的,这是一个非常有用的算法。

另一个重要的假设是,该算法要求数据在度量空间中。这意味着我们可以定义 度量来计算数据点之间的距离。定义距离度量可能是一个真正的挑战(参见最近邻分类和检索)。一个有趣的想法是使用机器学习来寻找距离度量(主要是通过将数据转换到向量空间,将对象之间的差异表示为向量之间的距离并学习那些差异,但这是另一个主题,我们稍后会谈到这一点)。

最常用的距离度量是:

  • 欧几里德距离:

这是我们在日常生活中使用的几何距离。它的计算方法是两个感兴趣点的平方差之和的平方根。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

公式在 2D 空间中:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 曼哈顿距离:

使用绝对差的和计算实向量之间的距离。也叫城市街区距离。你可以把这想象成在一个组织成矩阵的城市中行走(或者在曼哈顿行走)。街道是矩阵中小方块的边缘。如果你想从方块 A 到方块 B,你必须走在小方块的边缘。这比欧几里得距离要长,因为你不是从 A 到 B 一直走,而是之字形。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

公式在 2D 空间中:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 闵可夫斯基距离:欧几里德距离和曼哈顿距离的推广。计算 N 维距离的通用公式(见闵可夫斯基距离)。
  • 汉明距离:计算二元向量之间的距离(参见汉明距离)。

KNN 用于分类

非正式的分类意味着我们有一些标记的示例(训练数据),对于新的未标记的示例(测试集)我们希望基于训练集的经验教训分配标签

正如我前面所说,KNN 算法是懒惰的,在训练阶段没有学习或推广。实际工作是在分类或预测时完成的。

KNN 算法的步骤是(形式伪代码):

  1. 为来自训练集的所有 i 数据点初始化 selectedi = 0
  2. 选择一个距离度量(假设我们使用欧几里德距离)
  3. 对于每个训练集数据点 i 计算distance ei=新数据点与训练点 i 之间的距离
  4. 选择算法的 K 参数( K =考虑的邻居数量),通常是奇数,这样可以避免多数投票中出现平局
  5. 对于 j = 1 到 K 循环通过所有的训练集数据点**,并在每一步中选择与新观测值具有最小距离的点**(最小 距离 )
  6. 对于每个现有类别**计算 K 个选定数据点中有多少是该类别的一部分(投票**
  7. 将具有最大计数(最高票数)的班级分配给新的观察——这是多数投票。****

好吧,可能上面的正式伪代码有点难以理解,所以我们来看看非正式解释**。**

主要思想是,对于新的观察,我们搜索 K 个最近点(具有最小距离)。这些点将通过多数表决来定义新观察的类别。

例如,如果我们有两个类,红色和绿色,在计算距离并获得 3 个最近的点之后,其中 2 个是红色,1 个是绿色,那么通过多数投票选择的类是红色(2 > 1)。

如果我们不得不面对以下情况:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们有两个班,红色和绿色和一个新的观察,黑色的星星。我们选择 K = 3,因此我们将考虑距离新观测值最小的 3 个点。该恒星接近 3 个红点,因此很明显,这个新观测结果将被归类为红点。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在上面的图片中,我把星星移近了绿点。在这种情况下,我们选择了 2 个红色点和 1 个绿色点。因为 2 > 1,所以这颗星仍然被归类为红色。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

随着我们越来越靠近绿点,选择红色作为标签的信心下降,直到新的观察结果将被分类为绿色。这是红色阶级和绿色阶级的界限**,不同国家的情况也是如此。所以从不同的角度来看,我们可以说用这个算法我们建立了类的边界。边界由 K 值控制**。小 K 将导致清晰的边界,而大 K 将导致平滑的边界。最有趣也是最重要的部分,就是在你的特定数据集的背景下,如何选择 K 的最佳值。在下面几节中,我们将看到如何选择 k 的最佳值。****

我在上面描述的并不一定意味着KNN 算法将总是线性比较测试样本和训练数据,就好像它是一个列表一样。训练数据可以用不同的结构表示,如 K-D 树球树

另一个改进是我们可以为分类中更重要的属性分配权重。这样,如果我们知道在我们的数据集中有一些重要的属性需要考虑,我们可以给它们分配更高的权重。这将导致它们在给新的观察分配标签时具有更大的影响。权重可以是统一的(所有邻居的同等优势)或与邻居到测试样本的距离成反比。你也可以设计自己的权重分配算法(例如使用另一种人工智能算法来寻找最重要的属性,并给它们分配更高的权重)。****

KNN 预测

KNN 算法也可用于预测新值**。最常见的例子是用 KNN 预测某物(房子、汽车等)的价格。)基于训练数据。为了预测新值,KNN 算法几乎是相同的。在预测的情况下,我们计算 K 个最相似的点(相似性的度量必须由用户定义),并且基于这些点,我们可以使用公式预测新的值**,例如平均加权平均等。所以思路是一样的,定义度量计算距离(这里是相似度),选择 K 个最相似的点然后使用一个公式基于选择的 K 个点预测新值。****

计算的复杂性

为了计算 KNN 的计算复杂度,让我们考虑一个 d 维空间, k 是邻居的数量, n 是训练数据点的总数。

要了解我们如何计算这个算法的复杂度,请看看正式的伪代码!每次距离计算都需要 O(d) 运行时,所以第三步需要 O(nd) 工作。对于第五步中的每次迭代,我们通过循环训练集观察来执行 O(n) 工作,因此整个步骤需要 O(nk) 工作。第一步只需要 O(n) 工作,所以我们得到一个 O(nd + kn) 运行时。如果我们使用快速选择算法来选择具有最小距离的 K 个点,我们可以将这个运行时间复杂度降低到 O(nd)

如何为你的数据集选择最好的 K

您可能想知道,如何找到 K 的最佳值来最大化分类或预测的准确性。首先我必须提到的是, K 是一个 超参数,这意味着这个参数是由你,开发者选择的。我前面说过,你可以把 K 想象成控制决策边界的参数。例如:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正如你所看到的,如果 K=1,边界非常尖锐,呈之字形,但在 K = 7 的情况下,边界更平滑。因此,随着 K 值的增加,边界变得更加平滑。如果 K 是无穷大,那么所有的东西都是蓝色或红色的,基于总多数。

****训练误差率验证误差率是我们在选择正确的 k 时需要考虑的两个参数,如果训练误差很低但测试误差很高,那么我们就要讨论关于过拟合的问题了。

过拟合发生在模型学习训练数据中的细节 和噪声到了对模型在新数据上的性能产生负面影响的程度。这意味着训练数据中的噪声或随机波动被模型拾取并学习为概念

比较过度拟合和常规边界:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

绿色 线代表过拟合 **模型**黑色** 线代表规则化模型。虽然绿线最符合训练数据,但它过于依赖该数据,并且与黑线相比,它可能对新的、看不见的数据具有更高的错误率。**

****欠拟合是指既不能对训练数据建模,也不能推广到新数据的模型。例如,对非线性数据使用线性算法将会有很差的性能。

我们必须找到中庸之道,有一个模型,可以很好地推广到新的数据,但不是太好,避免学习噪音和避免过度拟合。

如果我们表示训练错误率和测试错误率,我们将得到如下一些图表:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如您所见,当 K=1 时,训练误差为 0,因为任何训练数据点的接近点都是其自身。如果我们看看测试误差,当 K=1 时,误差非常高,这是正常的,因为我们有过拟合。随着 K 值的增加,测试误差率下降,直到达到最小值,之后误差开始再次增加。所以基本上寻找最佳 K 的问题是一个最优化问题,在曲线上寻找最小值。这种叫做弯头T42 的方法,因为测试误差曲线看起来像一个弯头。

结论是为了找到最佳 K 值,使用肘形法并在曲线上找到最小值。这可以通过蛮力很容易地完成,通过多次运行模型,每次增加 K 的值。找到最佳 K 的有效方法是使用 K-Fold 交叉验证,但我们将在最后一章(提升 AI 算法)中讨论这一点。

使用 Python 的 KNN 示例

在本例中,我们将使用 Social_Networks_Ads.csv 文件,该文件包含有关用户的信息,如性别、年龄、工资。购买栏包含用户的标签**。这是一个二元分类(我们有两个类)。如果标签为 1 则意味着用户已经购买了产品 X ,而 0 则意味着用户还没有购买该特定产品。**

这里可以下载文件:社交 _ 网络 _ 广告

在这个例子中,我们将使用以下库: numpy、pandas、sklearn 和 motplotlib。

第一步是导出数据集。

# Importing the libraries
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd# Importing the dataset
dataset = pd.read_csv('Social_Network_Ads.csv')
X = dataset.iloc[:, [2, 3]].values
y = dataset.iloc[:, 4].values

这是一个简单的任务,因为 pandas 库包含了 read_csv 方法,该方法读取我们的数据并将其保存在一个名为 DataFrame 的数据结构中。

来自 sklearn的大多数算法要求属性和标签在单独的变量中,所以我们必须解析我们的数据。

在本例中(因为我们想用二维图表表示数据),我们将只使用年龄和工资来训练我们的模型。如果打开文件,可以看到前两列是用户的 ID 和性别。我们不想考虑这些属性。

X 包含属性。因为我们不想考虑前两列,所以我们将只复制第 2 列和第 3 列(见第 8 行)。

****标签在第 4 列,所以我们将把这一列复制到变量 y 中(见第 9 行)。

下一步是将我们的数据分成两个不同的块**,一个将用于训练我们的数据,另一个将用于测试我们模型的结果(测试属性将是新的观察结果,预测标签将与测试集中的标签进行比较)。**

# Splitting the dataset into the Training set and Test set
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.25, random_state = 0)

这是另一个简单的任务,因为 sklearn 有一个名为 train_test_split 的方法,它将拆分我们的数据集,返回 4 个值,即火车属性(X_train)、测试属性(X_test)、火车标签(y_train)和测试标签(y_test)。通常的设置是将数据集的 25%用于测试,75%用于训练**。如果您愿意,可以使用其他设置。**

现在再看一遍数据集。您可以观察到“薪水”列中的值比“年龄”列中的值高得多。这可能是个问题,因为薪水栏的影响会高得多。你想想看,如果你有 10000 和 9000 这两个非常接近的工资,计算它们之间的距离,结果是 10000–9000 = 1000。现在,如果您将年龄列的值设为 10 和 9,则差值为 10–9 = 1,影响较小(与 1000 相比,10 非常小,就像您有加权属性一样)。

from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)

为了解决这个量级问题,我们必须缩放属性。为此我们使用了 sklearn 的standard scaler。****

下一步是训练模型:

**# Fitting classifier to the Training set
from sklearn.neighbors import KNeighborsClassifier
classifier = KNeighborsClassifier(n_neighbors = 2)
classifier.fit(X_train, y_train)**

我们从 sklearn 导入KNeighborsClassifier。这需要多个参数。最重要的 参数有:

  1. n _ neighbors:k 的值,考虑的邻居数量
  2. 权重:如果您想使用加权属性,您可以在这里配置权重。这需要像统一、距离(到新点的反距离)或可调用这样的值,这些值应该由用户定义。默认值是统一的。
  3. 算法:如果你想要一个不同的数据表示,这里你可以使用 ball_tree,kd_tree 或者 brute 这样的值,默认为 auto,试图自动选择当前数据集的最佳表示。
  4. 度量:距离度量(欧几里德、曼哈顿等),默认为欧几里德。

我们保留所有默认参数,但对于 n_neighbors,我们将使用 2(默认值为 5)。

如果您想要预测新观察值的类别,您可以使用以下代码:

**# Predicting the Test set results
y_pred = classifier.predict(X_test)**

下一步是评估我们的模型。为此,我们将使用混淆矩阵

**# Making the Confusion Matrix
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, y_pred)**

混淆矩阵的结果是:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正如你所看到的,我们只有 5 个假阳性和 7 个假阴性,这是一个非常好的结果(这里的准确率是 80%)。

最后一步是可视化决策边界。让我们从训练集的决策边界开始。

**# Visualising the Training set results
from matplotlib.colors import ListedColormap
X_set, y_set = X_train, y_train
X1, X2 = np.meshgrid(np.arange(start = X_set[:, 0].min() - 1, stop = X_set[:, 0].max() + 1, step = 0.01),
                     np.arange(start = X_set[:, 1].min() - 1, stop = X_set[:, 1].max() + 1, step = 0.01))
plt.contourf(X1, X2, classifier.predict(np.array([X1.ravel(), X2.ravel()]).T).reshape(X1.shape),
             alpha = 0.75, cmap = ListedColormap(('red', 'green')))
plt.xlim(X1.min(), X1.max())
plt.ylim(X2.min(), X2.max())
for i, j in enumerate(np.unique(y_set)):
    plt.scatter(X_set[y_set == j, 0], X_set[y_set == j, 1],
                c = ListedColormap(('red', 'green'))(i), label = j)
plt.title('Classifier (Training set)')
plt.xlabel('Age')
plt.ylabel('Estimated Salary')
plt.legend()
plt.show()**

meshgrid 函数用一个 x 值数组和一个 y 值数组创建一个矩形网格(这里 x = X1,y = X2)。****

contourf 方法绘制填充轮廓,所以我们用这个方法用与之相关的类的颜色填充背景。诀窍在于,对于每个像素 ,我们进行预测,并用与预测类别相关的颜色对该像素进行着色。这里我们有两个类,我们用红色和绿色表示 0 和 1。****

在行 10 和 12 a 之间,我们循环所有训练数据点,我们预测它们的标签,如果预测值为 0,将它们涂成红色,如果预测值为 1,将它们涂成绿色。

这段代码的结果是:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

****为了可视化测试集的边界,我们使用相同的代码,但是将 X_set,y_set 改为 X_test,y_test。运行代码来可视化测试集的边界的结果是:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正如你在上图中看到的,我们有一个非常好的模型,只有几个点被错误分类。

结论

  1. KNN 算法非常直观易于理解
  2. 训练时间最短,模型实际上不学习或推广
  3. 测试时间可能会很长,因为算法在整个训练数据集上循环并计算距离(基于距离度量的类型和数据集的类型,距离计算可能是一项艰巨的工作)
  4. 对于小 K 值,算法可能对噪声敏感并且容易过拟合
  5. 数据集应该是数字的或一个距离度量应该存在以计算点之间的距离****
  6. 在不平衡数据上表现不佳

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我真的很喜欢咖啡,因为它给了我写更多文章的能量。

如果你喜欢这篇文章,那么你可以请我喝杯咖啡来表达你的欣赏和支持!

成为媒介上的作家:【https://czakozoltan08.medium.com/membership】T2

参考

  1. KNearestNeighbors 解释
  2. KNN 完整指南
  3. 机器学习中的过拟合和欠拟合
  4. KNN 详细介绍
  5. Udemy —从 A 到 Z 的机器学习
  6. KNN 计算复杂度

如果你喜欢这篇文章,并且你想免费学习更多有趣的 AI 算法,请订阅

KNN (K 近邻)#1

原文:https://towardsdatascience.com/knn-k-nearest-neighbors-1-a4707b24bd1d?source=collection_archive---------1-----------------------

它是如何工作的

KNN 是什么?

KNN(K-最近邻)是数据挖掘和机器学习中使用的许多(监督学习)算法之一,它是一种分类器算法,学习基于数据(向量)与其他数据(向量)的“相似程度”。

它是如何工作的?

KNN 非常简单,假设你有一个关于彩球的数据:

  • 紫球;
  • 黄色的球;
  • 还有一个球,你不知道它是紫色还是黄色,但是你有这个颜色的所有数据(除了颜色标签)。

那么,你怎么知道球的颜色呢?想象你喜欢一台只有球的特征(数据),没有最终标签的机器。你想知道球的颜色(最终标签/你的班级)吗?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Obs:让我们假设编号为 1(和标签 R)的数据指的是紫色的球,编号为 2(和标签 A)的数据指的是黄色的球,这只是为了使解释更容易,在以后的文章中我们将使用真实的数据。

每行表示一个球,每列表示一个球的特征,最后一列是每个球的类别(颜色):

  • R - >紫色;
  • 一种 - >黄色

我们有 5 个球(5 行),每个球都有你的分类,你可以尝试用 N 种方法来发现新球的颜色(在这种情况下是类别),这 N 种方法中的一种是将这个新球的特征与所有其他球进行比较,看看它看起来最像什么,如果这个新球(你不知道正确的类别)的数据(特征)与黄色球的数据相似, 那么新球的颜色是黄色,如果新球中的数据更类似于紫色然后是黄色的数据,那么新球的颜色是紫色,看起来很简单,这几乎就是 knn 所做的,但以最复杂的方式。

在 KNN 的例子中,实际上它并不“比较”新的(未分类的)数据和所有其他的,实际上他执行一个数学计算来测量数据之间的距离来进行分类,(那几乎是同样的事情)。我所说的这个计算可以是测量两点之间距离的任何其他计算,例如:欧几里得、曼哈顿、闵可夫斯基、加权…

KNN 的步骤是:

1 —接收未分类的数据;

2-测量新数据与所有其他已分类数据之间的距离(欧几里德距离、曼哈顿距离、闵可夫斯基距离或加权距离);

3-获取 K(K 是您定义的参数)较小的距离;

4-检查具有最短距离的类别列表,并计算出现的每个类别的数量;

5-将出现次数最多的类别视为正确类别;

6-用您在步骤 5 中采用的类对新数据进行分类;

下面我们有一张图片,上面是我们在本文中讨论的所有过程,您有一个未分类的数据(红色)和所有其他已分类的数据(黄色和紫色),每个数据都属于您的类别(A 或 B)。因此,您计算新数据与所有其他数据的距离,以了解哪些数据具有最小的距离,因此您获得 3(或 6)个最接近的数据,并检查哪个类出现得最多,在下图的情况下,与新数据最接近的数据是在第一个圆圈内(圆圈内)的数据,在这个圆圈内有 3 个其他数据(已经用黄色分类), 我们将检查哪个是主要的类,看,它是紫色的,因为有两个紫色的球,只有一个黄色的,所以这个以前没有分类的新数据,现在将被分类为紫色。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

计算距离:

计算两点(您的新样本和数据集中的所有数据)之间的距离非常简单,如前所述,有几种方法可以获得该值,在本文中我们将使用欧几里德距离。

欧几里德距离的公式如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

等等,不要担心,它并没有听起来那么复杂,读完这篇文章并继续发现它很复杂,当我们进入代码时,你会看到它是多么简单。

使用此公式,您将检查数据集中 1 个点(您的未分类样本)和 1 个其他点(1 个其他已分类数据)之间的距离,在您的所有数据集中,一个接一个,计算结果越小,这两个数据之间最相似。是的,你将运行这个计算很多次,直到你已经在所有数据中运行它,当你这样做时,你将有一个数组(让我们用一个数组作为例子)你的未分类数据到另一个已经分类的数据的结果。

好,我们开始吧!这很简单,但慢慢读,基本上你会:

  • 从数据集中获取每个特征;
  • 减去每一个,例,(第 1 行,第 5 列)—(第 1 行,第 5 列)= X …(第 1 行,第 13 列)—(第 1 行,第 13 列)= Z;
  • 在得到所有列的减法后,你将得到所有的结果,并将其 X+Y +Z 相加…
  • 所以你会得到总和的平方根。

让我们使用前面工作表中的示例,但现在有 1 个未分类数据,这是我们想要发现的信息。

在这个例子中,我们有 5 个数据(行),每个样本(数据/行)都有你的属性(特征),让我们想象所有这些都是图像,每一行都是图像,每一列都是图像的像素。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以让我们从上面解释混乱开始。

让我们取第一行,这是我们要分类的数据,让我们测量到第二行的欧几里德距离

1 —减法

让我们用第 2 行的属性减去第 1 行的每个属性(列),例如:

(1–2) = -1

2 —取幂:

从第 1 行减去第 1 列,从第 2 行减去第 1 列,我们将得到平方根,因此结果数字始终为正数,例如:

(1–2)² = (-1)²(-1)² = 1

3 —总和

完成第 2 步后,对于第 1 行的所有列和第 2 行的所有列,我们将对所有这些结果进行求和,让我们使用电子表格的列图像做一个示例,我们将得到以下结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(1–2)² + (1–2)² + (1–2)² + (1–2)² + (1–2)² + (1–2)² + (1–2)² + 
(1–2)² = 8

请注意,我们在第 1 行和第 2 行都有 8 个属性列,我们为每个数据集的属性执行了第 2 步,因此最终结果是 8,但为什么是 8 呢?因为每次我们运行第 2 步,结果都是 1,出于“巧合”,我们在所有列中都有相同的数据,并且(1–2)的结果等于 1,所以我在这里使用这些值来简化数学,但是不,这个属性并不需要总是相同的数字,以后我们将在使用此算法实现代码时看到这一点。

4—平方根:

在执行了第 3 步之后,我们将得到减法的和的平方根。在第 3 步中,结果是 8,所以让我们取数字 8 的平方根:

√8 = 2,83 ou 8^(½) = 2,83

太棒了。现在你有了从第 1 行到第 2 行的欧几里得距离,看,它并没有那么难,你可以在一张简单的纸上完成!

现在,您只需要为数据集的所有行(从第 1 行到所有其他行)创建这些,在执行此操作时,您将获得第 1 行到所有其他行的欧几里德距离,然后您将对其进行排序以获得“k”(例如,k = 3)最小距离,因此您将检查哪个是出现最多的类,出现最多的类将是用于对第 1 行进行分类的类(以前没有对其进行分类)。

简单对吧。在接下来的文章中,我们将开始在 GO 中实现这个算法!不过不用担心,Go 的语法非常简单,您可以按照教程的内容,用自己的语言来应用它。

TKS!

KNN (K 近邻)#2

原文:https://towardsdatascience.com/knn-k-nearest-neighbors-2-169d667bcee5?source=collection_archive---------10-----------------------

获取数据集

如果你还没有阅读,或者不知道 KNN 是如何工作的,我建议你在继续阅读这篇文章之前,先阅读这篇[帖子](http://post antigo),这样你会发现没有代码的算法解释。

从何说起?

开始吧!首先,我们需要数据,因为没有数据,世界上就没有有效的人工智能!(如果存在,请在评论中发表)

我从哪里得到这些数据?

如果你没有阅读这篇文章来在你工作的公司实施它(你可以做到的!),你很可能没有任何数据,而且就像我之前说的,没有数据,就没有机器学习。

但是不用担心!我们有一个解决方案,数据是当今最容易获得的东西(取决于数据类型),我们将在本文中使用的格式是其中最容易的,在互联网的世界中有一个神奇的地方,那里有 N 个大小的 N 个数据集,这个地方就是 UCI 机器学习库,当我写这篇文章时,UCI 在那里维护着 425 个数据集,下载它吧!

我如何选择数据集?我对 UCI 界面一点也不了解

不要担心,这非常简单,在初始页面上,您会看到一些数据集,我将向您介绍一点平台,其余的您可以随意浏览。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在上图中,用红色标记的是 UCI 的主要储藏室,为什么它们是主要储藏室?因为它们是最容易工作的数据集,没有丢失的数据,数据分布对于机器学习等非常有用。好了,让我们进入我们将在 KNN 实现中处理的数据集,乳腺癌威斯康星州(诊断)包含乳腺癌活检诊断数据。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

打开链接,你会看到一些数据集的信息,像上面的图片,如实例数(行),数据集有多少属性(列),如果有丢失的数据,数据连接到哪个区域,等等。

在同一页上,你还会发现数据集的描述,引用该数据集的论文,它包含的列,等等。

在下图中,我们有乳腺癌数据集列。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正如你所看到的(或者没有看到),我们有我们将要工作的列描述。

我们有 id(在我们的分类场景中没有用)、诊断(我们的分类数据)、半径、纹理、周长等,这些其他数据是我们将用于进行肿瘤分类的属性,无论它是恶性还是良性。

但是在哪里可以下载这些数据呢?

在数据集名称旁边,你将有两个链接,数据文件夹数据集描述

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从左边开始,数据集描述基本上是关于数据集的信息,通常是纯文本格式,数据文件夹链接是数据集本身所在的位置。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

红色选项是要下载的正确文件。

如果你一步一步地阅读这个关于 knn 的系列文章,我建议你不要下载 uci 库中包含的文件,在下一集里,我们将对这个数据进行一些处理,我将为我们的 knn 提供一个准备好的数据集。

机器学习— KNN 使用 scikit-learn

原文:https://towardsdatascience.com/knn-using-scikit-learn-c6bed765be75?source=collection_archive---------1-----------------------

KNN(K-最近邻)是一种简单的监督分类算法,我们可以使用它来为新的数据点分配类别。它也可以用于回归,KNN 没有对数据分布做任何假设,因此它是非参数的。它保留所有训练数据,通过计算输入样本和每个训练实例之间的相似性来进行未来预测。

KNN 可以概括如下:

  1. 计算每个训练示例的新数据点之间的距离。
  2. 为了计算距离,将使用欧几里德距离、汉明距离或曼哈顿距离等距离度量。
  3. 模型在数据库中挑选 K 个最接近新数据点的条目。
  4. 然后,它进行多数表决,即这 K 个条目中最常见的类别/标签将是新数据点的类别。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

With K=3, Class B will be assigned, with K=6 Class A will be assigned

关于 KNN 的详细文件可以在这里找到。

以下示例显示了使用 scikit-learn 库在 iris 数据集上实现 KNN。Iris 数据集对每种不同的鸢尾花有 50 个样本(总共 150 个)。对于每个样本,我们有萼片长度、宽度、花瓣长度和宽度以及物种名称(类别/标签)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Iris flower: sepal length, sepal width, petal length and width

  • 150 观察
  • 4 特征(萼片长度、萼片宽度、花瓣长度、花瓣宽度)
  • 响应变量是鸢尾物种
  • 分类问题,因为响应是分类的。

我们的任务是建立一个 KNN 模型,根据萼片和花瓣的测量结果对新物种进行分类。Iris 数据集在 scikit-learn 中可用,我们可以利用它来构建我们的 KNN。

完整的代码可以在 Git Repo 中找到。

第一步:导入所需数据并检查特征。

从 scikit-learn datasets 模块导入 load_iris 函数,并创建一个 iris Bunch 对象(Bunch 是 scikitlearn 用于存储数据集及其属性的特殊对象类型)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每次观察代表一朵花,4 列代表 4 次测量。我们可以在“数据”属性下看到特征(测量值),而在“特征名称”下看到标签。正如我们在下面看到的,标签/响应被编码为 0、1 和 2。因为对于 scikit-learn 模型,特征和静止应该是数字的(Numpy 数组),并且它们应该具有特定的形状。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第二步:拆分数据,训练模型。

对相同的数据进行训练和测试并不是一种最佳的方法,所以我们将数据分成两部分,训练集和测试集。我们使用‘train _ test _ split’函数来拆分数据。可选参数“测试大小”决定了分割百分比。“random_state”参数使数据在每次运行时以相同的方式分割。由于我们是在不同的数据集上进行训练和测试,因此得到的测试精度将更好地估计模型在看不见的数据上的表现。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Scikit-learn 被仔细地组织成模块,这样我们就可以很容易地导入相关的类。从“NeighborsClassifer”模块导入类“KNeighborsClassifer”并实例化估计器(“估计器”是 scikit-learn 对模型的术语)。我们称模型为估计器,因为它们的主要作用是估计未知量。

在我们的示例中,我们创建了一个“KNeighborsClassifer”类的实例(“knn”),换句话说,我们创建了一个名为“knn”的对象,它知道在我们提供数据后如何进行 KNN 分类。参数“n_neighbors”是调整参数/超级参数(k)。所有其他参数都设置为默认值。

“拟合”方法用于根据训练数据(X_train,y_train)训练模型,而“预测”方法用于根据测试数据(X_test)进行测试。选择 K 的最佳值至关重要,因此我们使用 for 循环来拟合和测试不同 K 值(从 1 到 25)的模型,并在变量(分数)中记录 KNN 的测试精度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 matplotlib 库绘制 K 值与相应测试精度之间的关系。正如我们所看到的,精确度有升有降,这在用精确度检查模型复杂性时非常典型。一般来说,随着 K 值的增加,精度似乎会提高,但又会下降。

一般来说,训练精度随着模型复杂性的增加而提高,对于 KNN,模型复杂性由 K 值决定。K 值越大,决策边界越平滑(模型越不复杂)。较小的 K 导致更复杂的模型(可能导致过度拟合)。测试准确性会对过于复杂(过度拟合)或不够复杂(拟合不足)的模型造成不利影响。当模型具有适当的复杂程度时,我们得到最大的测试精确度,在我们的例子中,我们可以看到,对于 3 到 19 的 K 值,我们的模型精确度是 96.6%。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对于我们的最终模型,我们可以选择 K 的最佳值为 5(介于 3 和 19 之间),并用所有可用的数据重新训练该模型。这将是我们最终的模型,可以做预测了。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

感谢您的阅读,如果您有任何建议/修改,请告诉我们。

了解你的对手:理解对手的例子(第 1/2 部分)

原文:https://towardsdatascience.com/know-your-adversary-understanding-adversarial-examples-part-1-2-63af4c2f5830?source=collection_archive---------0-----------------------

这可能只是一个不可避免的焦点偏差:任何你盯着看足够长时间的问题都会因为你的关注而变得更有兴趣和意义。但是,也就是说,在过去的一周里,我花了很多时间研究对立的例子,我开始将它们视为机器学习世界核心问题的迷人症结:将学到的知识映射到人类概念的困难,一个人的训练集带来的内在限制,以及对在安全化设置中用原则理解换取数字优化的担忧。

我对这一主题的探索是由一个关键问题引发的:对立的例子仅仅是研究人员的一个有趣的玩具问题,还是我们的模型中更深层和更长期的弱点的一个例子?然而,要以任何连贯的方式回答这个问题,首先需要建立一些背景知识。这篇文章是两篇文章中的第一篇,致力于建立对这些攻击如何以及为什么起作用的直觉;第二篇文章将在本周晚些时候发表,重点是思考防御和实际攻击场景。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对立的例子是如何制作的?

曾几何时,在 2013 年的太平日子里,早在 Batch Norm 和 ResNets 和 GANs 之前,谷歌的一个小组发表了“神经网络的有趣特性”,其中作者发现了他们正在试验的模型的一个令人担忧的事实:你经常可以诱导网络改变标签的预测类别,而不改变人类对图像的感知方式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

And in the right-hand column we have: entirely giraffes. According to the network, at least.

让这些例子具有对抗性的特殊因素是,只需要施加很少的扰动,就可以让网络改变对正确分类的想法。一般来说,训练有素的模型的一个特点是,它们对少量的噪声相对不变。而且,当涉及到随机噪声时,事实上通常是这样的——实验通常已经证实,向图像添加真正的“白噪声”通常不会影响性能良好的模型的预测。但是,当涉及到非随机噪声,即专门设计来“愚弄”网络的噪声时,这种噪声的数量少得惊人,远远小于人眼可察觉的数量,可以有意义地改变网络的最终输出。

在过去的四年中,新的攻击技术被添加到文献中,每种技术都有自己的细微差别、权衡和怪癖。但所有对立的例子都有一个基本的概念基础:使用(可能是近似的)模型内部状态的知识来寻找输入像素的小修改,这将导致模型有最大的错误机会。

如前所述,使一个例子具有对抗性的关键特征,至少在通俗的说法中,是人类对变化的不易察觉性。显然,当你自动创建示例时,让一个人陷入循环的代价是惊人的,所以研究人员不得不开发“难以察觉的差异”的代理,基于捕捉原始图像和它的扰动邪恶双胞胎之间的差异的度量。

  • **L⁰距离:**图像 x 和图像 z 之间数值不同的像素总数是多少?
  • **L 距离:**图像 X 和图像 Z 的总和绝对值差是多少?
    (对于每个像素,计算差值的绝对值,并对所有像素求和)
  • **L 距离:**图像 X 和图像 Z 的平方差是多少?(对于每个像素,取图像 X 和 Z 之间的距离,将其平方,并对所有像素求和)
  • **L^infinity 距离:**图像 x 和图像 z 的最大像素差是多少?如果你通读了这篇文章链接的一些论文,你会听到这被称为“最大常态”。
    (对于每个像素,取 X 和 Z 之间的绝对值差,并返回在所有像素上找到的最大距离)

你会注意到所有这些距离都用 L 和一个数字来表示。理解这个数字背后的精确数学逻辑是不必要的,但一个有用的启发是,数字越高,距离受异常值的影响就越大。在 L⁰距离中,变化 0.001 的像素与变化 10 的像素的影响一样大,因为该度量仅考虑有任何变化的像素总数。相比之下,在 L-infinity 距离中,唯一重要的是具有最大变化量的像素,而不考虑可能已经被修改的其他像素的数量。L-infinity 或最大范数距离是最常用的,但其他距离偶尔也会出现。有了这些直觉,我们可以继续讨论一些攻击方法。

最早也是最简单的技术叫做快速梯度符号法。在这个攻击中,第一步是计算你的成本相对于输入像素的梯度。起初,这可能是一个有点奇怪的概念,因为在反向传播中,我们通常根据模型参数来考虑梯度:如果我们修改这个权重或那个权重,这种变化会对总损失产生多大影响?但是网络的输出是其输入像素的函数,也是其参数的函数;我们通常只考虑修改后者,因为数据是固定的,我们正在更新参数以适应它。但是,如果我们想象一个已经训练好的模型,我们可以翻转问题:不是优化参数来减少损失,保持图像不变,我们可以优化图像像素来增加损失,保持参数不变。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一旦我们获得了输出相对于像素的梯度,我们将最终得到一个像素矩阵(或张量),其大小与输入图像相对应,填充有指示如果像素值更新一个单位,损失会改变多少的值。然后,FGSM 取梯度矩阵,并取它的符号。也就是说:它减少了浮点值的矩阵,使得它完全包含-1(对于所有负值)或+1(对于所有正值)。一旦你有了符号矩阵 S,这个方法选择某个值ε,并将两者相乘,这样你就有了一个充满+ε或-ε的矩阵。然后,你把这个矩阵加到输入像素矩阵中,你就有了一个对立的例子。

这个解释有点复杂,但我认为对这种攻击有一个过于清晰的理解有助于为其他人建立直觉,所有这些都有一个广泛的概念方法,即“使用内部模型参数的知识,以一种明确优化的方式干扰您的更新,以降低正确分类的可能性”。已经开发的一些其他攻击包括:

  • 应用 FGSM,但是多次执行计算梯度的步骤,这样我们在恶化模型的方向上采取多个“步骤”。通常,完成此操作后,总的可能距离是有上限的,因此即使您采取了多个步骤,您与输入的 L-infinity 距离也不会超过一个固定值。
  • Deep Fool ,其迭代地“线性化”输入点处的损失函数(取该点处损失函数的正切值),并且如果该线性近似是正确的,则应用切换类所必需的最小扰动。它多次执行该过程,每次使用先前的输入+先前的扰动作为输入,直到网络选择的类别切换。(对于倾向于线性代数的人来说,线性情况下的“最小必要扰动”相当于将输入向量投影到局部线性化模型的“决策边界”上)
  • Carlini 的攻击,一种在样本被原模型误分类的约束下,直接优化与原样本距离最小的方法。这是一种代价更高的攻击,因为它涉及到解决一个重要的优化问题,即使使用 Carlini 在论文中建议的松弛方法。然而,这是非常有效的,包括对防御,阻止早期品种的对抗性的例子。据我所知,这是目前的重量级攻击冠军。

令人惊讶的罪魁祸首:模型线性

有了对什么是机械的、对立的例子的清晰理解,你就可以进入真正有趣的问题:它们为什么存在?如果有的话,它们的存在对现代模型的质量和稳定性说明了什么?

我试图回答这些问题,我是如何了解到一些关于对立例子的真正令人惊讶的事情的:当前研究人员的共识是,对立例子不是过度拟合的产物,而是高维输入和现代模型的内部线性的产物。

这个断言的一些部分很奇怪,不直观有点轻描淡写,但让我们从对立的例子与高维线性相关的评估开始。出现这种情况的基本逻辑是:线性模型进行推断,在训练数据集中的区域之外,它们的行为可能非常病态。这种外推是由以下事实引起的:根据线性模型的线性定义,每个要素都具有相同的部分斜率,而不管其值或其他要素的值如何。也就是说:模型不会因为到达了从未看到训练数据的区域而变平。如果您设法在与决策边界完全一致的方向上将您的输入从训练数据区域推开,您可以到达决策空间的一部分,该部分模型非常确定对应于不同的类别。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

In the example above, if we move in a direction perpendicular to the decision boundary, we can, with a relatively small-magnitude vector, push ourselves to a place where the model is very confident in the wrong direction

此外,在高维空间中,每个单独的像素可能仅增加非常小的量,但是这些小的差异导致权重*输入点积的显著差异,因为所有这些小的差异都乘以权重,然后求和在一起。Andrej Karpathy 关于主题的博客文章进一步阐述了这种直觉,并证实了用单层线性逻辑分类器观察对立例子的可能性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Seems legit

现在,让我们把注意力转向这句话的另一个令人惊讶的部分:断言深度模型的问题是它们太线性了。乍一看,这似乎很荒谬:深度网络从定义上来说是非线性的。如果深度网络没有非线性激活函数,它们不会比单层线性函数更复杂,也没有希望学习它们实际学习的函数。此外,深度网络需要非线性才能工作,这是正确的。但是,在可能的非线性激活函数的空间内,现代深度网络实际上已经确定了一个非常接近线性的激活函数:ReLu。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The fault is not in our stars, but in our activation functions, for they are linear

由于 ReLu 具有相当大的置零区域,因此可以称之为非线性,但它也有很大的空间,对于大于 0 的输入,它实际上只是作为线性函数工作。与 sigmoid 或 tanh 激活相比,它们在高激活时会简单地饱和到一个上限值,使用前面部分概述的逻辑,可以将 ReLu 激活推到任意高的值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

关于激活的选择,一个有趣的问题是在可训练性和对抗攻击的鲁棒性之间的权衡。tanh 和 logistic 函数难以训练的一个关键原因是,尾部的饱和区域,激活的斜率(以及梯度值)可能会“卡”在非常接近 0 的位置。这将使网络中来自后面点的任何梯度归零,并使网络的前面位没有任何信号可供学习。当用 tanh 和 sigmoid 训练时,游戏的名字是试图以某种方式保持你的激活在激活的中间区域,在那里梯度大致是线性的,所以你保持你的梯度活着。

相比之下,ReLu 在 0 右侧的任何地方都有非零梯度,这使得它的训练更加稳定和快速。计算起来也更容易,因为您只需要计算一个“检查符号”操作,并根据该操作的结果用 0 或激活的当前值来响应。

然而,从对立例子的角度来看,tanh 和 logistic 的饱和的有益副作用是激活被非线性地限制,使得更难以激发在线性激活中看到的病理性高水平的置信度。

详细引用 Ian Goodfellow 关于主题的关键论文:

使用设计成足够线性的网络——无论是 ReLU 或 maxout 网络、LSTM 还是经过精心配置不会过度饱和的 sigmoid 网络——我们都能够解决我们关心的大多数问题,至少在训练集上是如此。对立例子的存在表明,能够解释训练数据,甚至能够正确标记测试数据,并不意味着我们的模型真正理解我们要求它们执行的任务。相反,他们的线性响应在数据分布中不存在的点上过于自信,而这些自信的预测往往是非常不正确的。…人们也可能得出结论,我们使用的模型族存在固有缺陷。优化的简易性是以模型容易被误导为代价的。

在这种框架中,对立的例子呈现出一种奇怪的悲剧叙事:它们是隐藏在一种技术中的意想不到的弱点,而这种技术至少目前是现代网络的基本必需品。那么,这个数学上的致命弱点有多严重呢?有没有任何现有的防御措施成功地使模型变得健壮?哪种攻击场景或威胁模型最有可能或最有效?我们怎么能想象这种数字漏洞会在现实世界中被利用呢?这些是我将在本系列的下一篇文章中重点讨论的问题。

了解您的数据|第 1 部分

原文:https://towardsdatascience.com/know-your-data-part-1-c6bd56553ae8?source=collection_archive---------12-----------------------

在我之前的文章中,我们讨论了大数据和机器学习之间的化学反应。我们还得出这样一个事实:为了建立一个好的学习模型,我们首先需要很好地理解我们的数据。

下图代表了机器学习和数据挖掘的一般过程。数据清理和特征提取是最繁琐的工作,但你需要擅长它,使你的模型更加准确。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

什么是数据或数据集?

数据对象及其属性的集合。属性捕获对象的基本特征。让我们检查一下著名的泰坦尼克号数据集,这里有。它表示乘客信息及其生存状态的列表(survived=0/1)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每行代表一个乘客对象,列基本上是该乘客的特征,也称为属性。属性值是数字或一些类别。Ex Age 是数字,但 Pclass 基本上代表乘客级别(1 表示头等舱,2 表示二等舱……)

属性类型?

要理解数据,首先需要理解不同类型的属性。属性的类型取决于它拥有以下哪些属性:

清晰度:=,!=

订单:< >

追加:+ -

乘法: /*

Nominal

遵循 distinctness 属性,例如:ID 号、眼睛颜色、邮政编码。

最终的结果

遵循独特性和顺序,例如:等级(例如,从 1-10 分制的薯片味道)、级别、身高(高、中、矮)

间隔

遵循区别,顺序和附加,例子:日历日期,摄氏或华氏温度。

比率

遵循所有四个属性,例如:开尔文温度,长度,时间,计数

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

根据属性值,我们可以说属性有两种类型。

离散属性: 它会有有限或可数无限的值集合,邮政编码、Pclass 都是这方面的完美例子。二进制属性是离散属性的特殊形式(大数据集中的幸存属性就是二进制属性的一个例子)

连续属性: 它会有实数作为值,例如:泰坦尼克号数据集中的票价。

数据集的类型?

  • **记录数据集:**由具有固定属性集的记录集合组成,也称为结构化数据。下面是几个记录数据集的例子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Data Matrix

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Document Data

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Transaction Data

  • 图表数据集:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

World Wide Web

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Molecular Structures

  • 有序数据集:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Sequential Data

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Temporal Data

在本文中,我们了解了不同类型的数据集、数据对象和属性。在我的下一篇文章中,我们将了解与数据集相关的问题,以及如何识别和处理它。我们还将通过一个例子演示如何在 Titanic 数据集上进行特征提取。

感谢您的阅读,请在评论中分享您的想法、反馈和想法。你也可以通过 twitter 上的@ simplykk87 和 linkedin 上的联系我。

参考文献

数据挖掘导论,庞-谭宁,密歇根州立大学,
迈克尔·斯坦巴克,明尼苏达大学维平·库马尔,明尼苏达大学

利用神经网络挖掘黑暗知识——知识提炼

原文:https://towardsdatascience.com/knowledge-distillation-and-the-concept-of-dark-knowledge-8b7aed8014ac?source=collection_archive---------5-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

动机:任何 ML/AI 模型的主要目标都取决于它对未知数据的概括能力,而不是它对训练数据的表现。如果我们更仔细地观察这个目标,那么为了训练和推理的目的,最好有不同的模型适应。在训练期间,我们需要深度和复杂的模型,以便在大量训练数据上进行训练,但在推理期间,我们只需要一个较轻的模型,它可以很好地概括任何看不见的数据。在生产中,较轻的模型显然在推理时间具有良好的性能。因此,本文的背景设置是要看看是否有一种有效的方法来将这些一般化的知识提取到一个更轻量级的模型中,以便两全其美。

注意:神经网络和 MNIST 数字识别任务在本文中被用作参考来说明知识提炼的概念,但是相同的概念可扩展到任何 ML/AI 模型。

【https://arxiv.org/abs/1503.02531】参考:这里所说的想法和概念都出自这篇参考文章: 参考:

Keras 中这篇文章的完整代码:https://tinyurl.com/yb4en6e3

**迁移学习的区别&知识提炼:**迁移学习和知识提炼的目的有很大不同。在迁移学习中,权重从预训练的网络转移到新的网络,并且预训练的网络应该精确地匹配新的网络架构。这意味着新的网络本质上和预先训练的网络一样深入和复杂。

然而,知识提炼的目的是不同的。目标不是转移权重,而是将复杂模型的一般化转移到更轻的模型。但是等等…我们知道权重是如何转移的…但是我们如何转移一般化呢?如何量化一个复杂网络的一般化?进入师生网的术语,softmax 激活中的 concept of temperature暗知和软化概率……让我们一个一个来了解这其中的每一个。

**师生模型:**参考下图 1。在本例中,教师是一个深度神经网络,它已经用足够好的正则化对大量数据进行了训练(或者它可以是任何其他集成模型),因此它的主要目标是可以对看不见的数据进行很好的概括。学生网络是一个浅浅的网络,将由老师训练,其主要目标是——学习大多数老师的概括,并且仍然更浅。考虑到严格的生产限制,更轻的模型显然在生产中更受欢迎,因为它可以快速预测。在掌握了剩余术语后,我们将回到这个主题。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Fig : 1, Teacher-Student architecture for knowledge distillation — image credits Ravindra Kompella, Dhee Yantra solutions

soft max 激活中的温度概念:

使用 softmax 的主要优势是输出概率范围。输出概率的范围将从 0 到 1 ,所有概率的总和将是等于 1。它返回每个类的概率,目标类将具有最高的概率。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Fig 2a : Regular Softmax function

该公式计算给定输入值的指数(e 次方)以及输入中所有值的指数值之和。那么输入值的指数与指数值之和的比值就是 softmax 函数的输出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Fig 2b : High temperature softmax

当我们将输入—‘z’(在神经网络中也称为 logit)除以‘T’(温度)时,获得高温 softmax 激活函数。

为了说明温度对 softmax 激活的影响,考虑下面图 3 中的曲线。正如可以观察到的,预测数字‘7’的概率变得软化,并且随着温度的升高而软化。我们所说的软化概率是什么意思?

如果您沿着绿线(在更高的温度=7 时绘制)并仔细观察概率值,您可以看到该模型清楚地显示出一个事实,即它预测的数字“7”看起来更像 9 或 1,而不是 6(6 的预测概率小于 0.01,而 1 的预测概率约为 0.075)。

如果您沿着橙色/蓝色线(在较低温度下绘制)并仔细观察概率值,您可以看到模型预测数字“7”的可信度非常高,但无法区分预测的数字 7 更接近 1 还是 6(它们都具有“非常接近零”的概率)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Probability prediction for the number ‘7’ digit at varied temperatures, Image credits Ravindra Kompella, Dhee Yantra solutions

**黑暗知识:**我们可以通过查看手写数字“7”(如下图所示)来很好地关联我们自己的猜测,并判断它是否与 1 相似——这很像这个模型,它在高温下预测数字“7”时输出“1”的概率更高。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

number one

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

number seven…oh wait…or is it number 1 ?

唯一的区别是,我们人类无法量化“7”看起来有多接近 1,而高温模型却可以很准确地做到这一点。因此,高温模型被认为具有黑暗知识——即,除了预测数字“7”之外,它还存储了数字“7”与数字“1”有多相似的信息。

低温模型(我们在 softmax 中通常遇到的没有温度的模型)通常擅长硬预测,我们会失去这些黑暗的知识。知识提炼背后的主要思想是将这种黑暗的知识从训练有素的教师转移到更轻松的学生模型中。

**师生培训:**现在我们已经理解了上下文和所有其他重要术语,让我们回到师生模型中我们离开的地方。将这些概括传递给学生的工作原理非常简单,如图 1 所示。在训练学生时,不是将一次性编码值作为硬目标,而是将这些软化概率(通过应用高温 softmax 收集的输出)作为目标。我们还可以定制知识提取损失函数(参见图 1 ),将知识提取损失计算为软目标+硬目标的连接向量的相应分量之间对数损失的加权平均值。

结果表明,以这种方式从老师那里训练的学生(观察到精度提高了 1%到 2%)能够比在相同数据上训练的独立学生更好地对看不见的数据进行概括。这里有几个重要的注意事项—

  • 学生在和老师一样的高温下接受训练
  • 学生模型中的推论将通过通常的 softmax 激活来完成(即没有温度)。

https://tinyurl.com/yb4en6e3:的这篇文章的全部代码

**知识提炼的优势:**可以看出,这种知识提炼过程有几个优势:

  • 使用较轻的型号
  • 在严格的生产限制下,降低计算要求并提供卓越的性能
  • 比独立模型精度更高
  • 即使学生模型的可用训练数据较少,也可以使用。有一个训练有素的老师就足够了。

科尔莫戈罗夫-斯米尔诺夫试验

原文:https://towardsdatascience.com/kolmogorov-smirnov-test-84c92fb4158d?source=collection_archive---------0-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据科学工具箱中的必备工具

最近,在工作中,我们不得不做许多无人监督的分类。我们基本上必须从样本人群中区分出 N 个类别。我们大概知道有多少个类别,但没有什么是确定的,我们发现了Kolmogorov-Smirnov 测试这是一种非常有效的方法,可以确定两个样本之间是否存在显著差异。

我将向您介绍一下 Kolmogorov-Smirnov 测试的背景,并向您介绍我们用它解决的一个问题。

coffeeanddata.ca 上的原始帖子

一点理论

拒绝零假设。这听起来像是大学统计课上的痛苦回忆,但实际上这正是我们想要做的。我们要排除两个样本来自完全相同的分布的可能性。让我们来看看一些可用测试的非常高层次、非数学的概述。如果你想很好地理解所有这些测试背后的数学原理,可以使用所有章节中提供的维基百科链接。

学生的 T 检验

学生的 T 检验可能是最广为人知的拒绝零假设的方法。该测试计算一个样本相对于正常总体或另一个样本的 P 值。结果 P 值告诉您这些样本来自完全相同的分布的可能性。

当获得 P 值时,可以将其与统计显著性阈值(例如. 05)进行比较,如果 P 值更小,我们可以拒绝零假设。

学生的 T 检验有问题,样本必须是正态的(正态分布)。这对我们来说是一个问题,因为我们做了很多泊松分布的工作。

科尔莫戈罗夫-斯米尔诺夫试验

Kolmogorov-Smirnov 测试 (KS 测试)稍微复杂一点,可以让你检测出用学生的 T 测试无法检测出的模式。

来自维基百科:

“Kolmogorov–Smirnov 统计量量化样本的 经验分布函数 与参考分布的 累积分布函数 之间的距离,或者两个样本的经验分布函数之间的距离。”

这里有一个例子来说明学生的 T 检验和 KS 检验之间的区别。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

stackexchange.com

因为样本均值和标准差非常相似,所以学生的 T 检验给出了非常高的 p 值。KS 检验可以检测方差。在这种情况下,红色分布具有 KS 检测到的略微二项式分布。换句话说:

  • 学生的 T 检验表明,两个样本有 79.3% 的几率来自同一分布。
  • KS 检验表明有 1.6% 的几率两个样本来自同一个分布。

其他测试

有许多其他测试和算法来做这类工作。夏皮罗-维尔克测试 安德森-达林测试 是被认为比 KS 测试更强大的两种测试。这两种测试有一个主要的缺点,它们不允许你比较两个样本,它们总是比较一个样本和一个标准分布。

编辑:我的一位同事向我展示了 Anderson-Darling 也可以用于双向测试(比较样本)。

“双样本 K–S 检验是比较两个样本的最有用和最通用的非参数方法之一”——维基百科。

检测设备用户

对于这个特定的任务,我们必须检测哪个用户正在使用特定的设备。每个设备由一个或多个不同的用户使用,我们必须想出一种技术来识别是否有一个或多个用户。在多个用户的情况下,我们希望确定哪个用户使用了哪些内容。

我们的策略

我们决定混合使用图网络和 KS 测试来识别潜在的集群。这个演示背后的想法是想象一个图形网络,其中每个节点(样本)都与其他每个节点(样本)相连。这些节点之间的顶点或链接将进行测试,换句话说,这两个节点有多近。因此,具有低 KS P 值的两个节点会很近,而具有高 P 值的两个节点会很远。这有望创建可区分的集群。

数据集

以下是我们的数据:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正如你所看到的,我们所有的样本看起来像一个标准偏差非常低的正态分布。我们已经对该设备的所有 82 个不同使用时段进行了采样。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从这张照片中我们可以看到不同的图案。所有的样本不具有相同的直方图分布。这是一个非常好的开端。在此之后,我们看了明显的分布集群。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为了做到这一点,我们做了 KS 矩阵,它由每个样本分布与每隔一个样本的 KS 双向检验组成。快速查看 KS Matrix 热图并不能发现明显的结果(如您在右图中所见)。

经过这样的层次聚类,我们已经得到了一些较好的结果。(如下图所示。)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这两个树状图可视化中,我们可以看到一些潜在的(3)集群。经过审查,这些集群是无关紧要的。

网络图

在不成功的树状图聚类之后,我们尝试了所提出的图方法。如前所述,这里的目标是绘制所有可能的节点和顶点。顶点长度是 KS 测试值。我们不得不移除自我参照(它总是 0(显然你和你自己非常相似)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后我们得到了一个网络图,其中每个人都与其他人相连,这不是特别有用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下一步是只保留重要链接(低于某个阈值)

正如我们在这张图片上看到的,我们得到了一个非常令人兴奋的结果。我们可以清楚地看到两个不同的集群和三个异常值。

这完全符合我们的模型,大集群必须是主要用户,第二个集群可以是替代用户。

在有效性验证之后,我们发现集群在集群 1 和集群 2 的使用之间发现了一些差异,但不是我们要找的那个。换句话说,它没有解决我们的问题,但它实际上找到了另一种模式,在另一种情况下可能有用。

结论

经过这项工作,我们得出结论,KS 检验是自动区分不同分布的样本的一种非常有效的方法。它并没有完全解决我们的问题,但它确实表明它可以很容易地用在数据科学环境中。

今天我向你展示了我们用 KS 测试解决的一个问题,但我们也用它来解决其他问题。KS 测试真的成为我们数据科学瑞士刀里的好测试了。

想要阅读更多内容

在我的博客上关注我: coffeeanddata.ca

韩国的生育率暴跌——这意味着什么?

原文:https://towardsdatascience.com/koreas-tanking-fertility-rate-what-does-it-mean-380a2477b4d6?source=collection_archive---------8-----------------------

今年,韩国的生育率预计将降至 1.00 以下。

这是一个相当了不起的统计数字。首先,低于 1.00 的平均生育率从数学上证明韩国有许多育龄妇女不会生育。第二,近在五十年前,生育率几乎为 5.00,在此之前甚至更高。下降幅度很大——50 年内下降了 80%,而全球平均下降幅度约为 50%。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

This is becoming a less frequent sight in Seoul

为什么会出现这种情况?

虽然它还不是生育率最低的国家(这一荣誉属于新加坡),但在如此大规模下降的背景下,人们很自然会问为什么会出现这种情况。大多数专家认为,这种下降的原因是经济、社会和文化因素的结合,主要有以下几个方面:

  1. 韩国的成本一直在快速增长。以首尔为例,这里居住着 20%的韩国人口,的房价每年上涨约 5%。
  2. 这些成本,加上居高不下的年轻人失业率,导致与父母同住的成年人数量大幅增长——这一群体被命名为“袋鼠部落”。在 1985 年到 2010 年间,至少有一个 25 岁以上未婚子女的家庭比例从 9%激增到 25%。
  3. 在韩国,工作场所对怀孕的态度阻碍女性生孩子,许多报道称女性因休产假和工作受到威胁而感到内疚。

生育率低不好吗?

是的,它是。低生育率导致经济衰退。这种观点可能有悖常理,因为最贫穷的国家往往出生率最高。然而,如果我们停下来想一想,问题是什么就变得非常清楚了。

低生育率意味着人口会随着时间的推移而下降。根据经合组织的说法:“假设没有净移民和死亡率不变,每个妇女 2.1 个孩子的总生育率确保了人口的大体稳定。”韩国人口增长率从 1960 年的 3.2%下降到 2016 年的 0.4%。假设生产率稳定,低人口增长的后果就是低经济增长。

低生育率导致年龄失衡,这一问题因人们寿命延长而加剧。随着人口中老年人比例的增加,这导致了医疗保健和社会护理相关成本的增加,以及养老金结构的不平衡。简而言之,越来越少的年轻人需要为越来越多的老年人提供资金。

数据怎么说?

为了让自己放心,我做了一个快速分析,以确定生育率和经济增长之间的联系强度。

我的第一站是精彩的经合组织数据门户,在那里我下载了 2006 年至 2016 年间所有可用国家生育率的 csv 文件,以及 2015 年至 2030 年实际 GDP 预测csv 文件。

我用 R 编程语言做了一些非常简单的计算,并创建了一个图表,向我展示了生育率和实际 GDP 增长之间的关系。首先,我加载了两个数据集,并使用dplyr软件包计算了 2016 年至 2030 年间实际 GDP 的 CAGR(复合年增长率):

gdp <- read.csv("GDP.csv")
fertility <- read.csv("fertility.csv")gdp_by_country <- gdp %>% dplyr::group_by(LOCATION) %>% dplyr::summarise(cagr = (tail(Value, n = 1)/head(Value, n = 1))^(1/15) - 1)

这给了我一个 49 个国家和他们的实际 GDP CAGR 的列表。接下来,我计算了生育率数据集中的国家在 2006 年至 2016 年之间的平均生育率,并将其加入到 GDP 数据集,这样我就有了真实的 GDP 统计数据和各国的生育率统计数据。

fertility_by_country <- fertility %>% dplyr::group_by(LOCATION) %>% dplyr::summarise(avg_rate = mean(Value))full_data <- dplyr::inner_join(gdp_by_country, fertility_by_country)

不是所有的国家都匹配,所以在我的最后一组中,我总共有 46 个国家。现在我想绘制 GDP 增长与生育率的关系图,并确定两者之间的关系。在 R 中计算线性模型非常容易,我保存它是为了以后绘图:

fit <- lm(cagr~avg_rate, data = full_data)

现在,我将在我最喜欢的绘图包plotly中绘制一个散点图,并将线性模型覆盖在上面:

# plot basic scatter
plotly::plot_ly(data = full_data, x = ~avg_rate, y = ~cagr, 
type = 'scatter', name = "OECD countries") %>% # add country labels to markers
  plotly::add_annotations(data = full_data, x = ~avg_rate, 
                          y = ~cagr,
                          text = ~LOCATION, 
                          showarrow = FALSE, xanchor = 'right',
                          yanchor = 'center') %>% # add linear model 
  plotly::add_lines(y = ~fitted(fit), name = "Linear Model") %>% # label axes and title
  plotly::layout(xaxis = list(title = "Average fertility rate 2006-16"),
                 yaxis = list(title = "Real GDP CAGR 2015-30"),
                 title = "Fertility Rate vs Real GDP Growth for OECD countries")

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这证实了近期生育率和未来经济增长之间的关系。报告还显示,在样本国家中,韩国的近期生育率最低,尽管其经济增长预计不会像生育率数据让人相信的那样令人沮丧。

最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在LinkedInTwitter上找我。

科特林和数据科学:一个萌芽的爱情故事

原文:https://towardsdatascience.com/kotlin-data-science-a-budding-love-story-ad366a633213?source=collection_archive---------12-----------------------

Kotlin 于 2011 年首次出现,并席卷了编码界——我第一次听说这种语言是在一年前,巧合的是在谷歌宣布 Kotlin 是官方 Android 语言之后不久。自那以后,又发生了几次“大爆炸”,目前的预测预测全球使用量将继续增加。下面是今年 Github 库的语言分类:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Credit & thanks to: Rachel Stephens for extracting rankings from GitHub data and rendering the grid & Stephen O’ Grady for presenting RedMonk findings on Programming Language Rankings

由圣彼得堡的一个小型 JetBrains 团队开发的 Kotlin 是一种静态类型语言,它:

  • 在 JVM 上运行
  • 可与 Java 互操作
  • 表示面向对象和函数范例之间的流动性

我一直认为自己是一个有着必要背景的程序员,我永远不会达到功能性编程的地位。当然,总有这样一个笑话:如果你真正理解了函数式编程,你就无法向别人解释它。我从来没有想象过一种语言可以允许流动性和变形为两个世界的一个实用和兼容的版本。

结果是在一个努力理解两个世界的社区中有明显更多的用户。机构支持有助于承担为爆炸式增长的社区提供支持的重担并且有助于在大型软件 系统中使用 Kotlin 赢得行业信心,这些公司包括:

  • Pinterest
  • 格雷尔
  • 优步
  • 枢轴
  • 亚特兰蒂斯

今年我有幸参加了 KotlinConf,但我感到兴奋的不是我自己的内容。随着我参加的每一次演讲,我一点一点地意识到因为 Kotlin 是一种如此强大的语言,人们已经能够开始并超越以前在计算机科学中设定的限制。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

也许是来自不断增长的社区的越来越多的嗡嗡声:也许从 KotlinConf 内容的演变中可以明显看出这一点——但我有种悄悄的感觉 Kotlin 可能是下一个数据科学栈

我很遗憾地说,不得不在我能参加的讲座之间做出选择是非常痛苦的(幸运的是,所有的讲座都被记录了下来)。

Kevin Most 发现,与注释处理器不同,编写自己的编译器插件允许你在运行时修改现有代码。

Thomas Nield 找到了解决调度问题的方法:通过在达到局部最小值后重新加热温度,在优化和贪婪算法之间切换。

Roman Elizarov 展示了新的 Kotlin 1.3 特性协程如何帮助管理现实生活中的并发和调度问题。

FP 帮助维护可扩展的软件

函数式编程(FP)的优势在于为规模和复杂性不断增长的软件创建更稳定的数据堆栈:

  1. 纯函数容易测试。因为纯函数可能独立于其他纯函数,所以为算法编写单元测试比以往任何时候都更容易。
  2. **由于函数式编程的确定性,它适合于并发和分布式编程。**这些功能对机器学习也很有用!

静态类型和空安全增加了运行时的稳定性

类型安全和空安全对于大型企业级应用程序尤其重要。作为一种静态类型的语言,Kotlin 已经有了一个用于类型检查的编译器,这让位于更稳定的系统。编译语言让位于更好的性能以及处理大型数据系统。

Kotlin 也有 null-safety,这是 Java 至今仍在经历的一个十亿美元的错误,NPE 会导致运行时崩溃。

安全带?检查。头盔?检查。

Kotlin 致力于并发性和并行性

并发性和并行性对于大型软件系统中的数据健康和效率都是至关重要的。并发性描述了一个可以同时处理多个线程的系统,以及它无序执行功能并期待相同输入的能力*。*

一年前,我可能会傻傻地看着人们争取独占val而不是var的想法——但是不变性可以使处理并发线程更容易维护。

假设你在一次长途飞行中,你必须和其他的机组人员和乘客共用洗手间。通常情况下,一个洗手间的规则是一次只能有一个人在洗手间。或许去洗手。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Flickr/veganstraightedge

你不会希望别人撞见你使用洗手间。一些程序员可能用 TDD 管理并发性(洗手间有人吗?)。其他人可能使用锁(门锁)或信号量(VACANT / IN USE )告诉人们是否尝试开门来处理访问单个资源的多个线程或人员。

同样,如果两个线程访问同一个可变的资源,资源可能会被修改,导致访问不再可用的内容失败,对相同的输入获得不同的结果,甚至在计算中遇到越界异常。

并发性与并行性并不完全相同:虽然并发性与确定性有关,但是并行性与如何同时完成多个任务有关。

Kotlin 闪亮的新特性协程有助于在异步环境中管理并行性*。*协同程序通过自动调度优化来管理并行性。他们还可以用作用域构造并发性。常规线程的线程数量有限,因此它们可能会在没有调度的情况下被阻塞。有了协程,我们就不必担心共享的可变状态和同步,并且您可以根据需要派生出任意多的协程。

那么下一步是什么?

如果你碰巧好奇,Kotlin 中有一个流行的库和框架的列表,你可以在这里探索任何你感兴趣的东西。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Flickr/Adrian Scottow

事实上,程序员们正着手用科特林解决不可能的事情,这绝非巧合。我们还没有完全准备好称自己为数据堆栈,但是 Kotlin 已经开始处理处理大型数据系统所需的健壮和稳定的软件的必要组件。我不知道一年后 Kotlin 会在哪里,但我当然期待继续与社区一起成长,尽我所能做出贡献!

Kotlin:现代(元)编程的下一个前沿

原文:https://towardsdatascience.com/kotlin-the-next-frontier-in-modern-meta-programming-8c0ac2babfaa?source=collection_archive---------12-----------------------

今天,我为我的 KotlinConf 演讲写一篇关于 Kotlin、TornadoFX 和元编程的后续文章。在这次演讲中,我总结了横切的广义定义,并探讨了元编程的常见形式。我们研究了用 Kotlin 编写的 TornadoFX 特性,以检验该语言的功能特性。回想起来,我希望我能更专注于谈论我的应用研究,所以我在这里谈谈我已经走过的路和我将要去的地方!

简要回顾

元编程有三种常见形式:

  • 向导 —也被称为“猴子补丁”,向导通常是用户生成的输入,仅限于 HTML 或 XML 等标记语言。Android Studio 的设计功能是向导的一个很好的例子。向导容易导致用户输入错误,这使得它成为元编程的最低级形式。
  • 面向方面编程 — AOP 是一种思想,在这种思想中,你将关注点提取出来,放到一个更高的抽象层次,以便将其他关注点编织成一个更兼容的共存。 AOP 通常局限于报告元数据,缺乏动态属性。
  • 特定领域语言 —传统的 DSL 是一种完成特定任务的语言,但放弃了任何与特定类别无关的东西。您可以用 SQL 操作数据集,但不能用它们创建整个应用程序。因为 DSL 通常不同于 Java、C、Kotlin 等通用语言。所以结果是 DSL 通常存储在不同的文件或字符串中,产生了大量的开销。

巧合的是,Kotlin 包含了所有三种形式的能力,甚至改进了每种类型当前的缺点,作为一个功能范例。通过内部 DSL 和具体化的泛型,Kotlin 找到了一种方法来克服 JVM 堆栈中的限制,这些限制会阻止像函数这样的功能支柱成为一等公民。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Kotlin 在函数式和面向对象的范例之间不断变化,但是作为一种静态类型的语言,在尝试完成其他静态类型语言无法完成的真正的元编程方面存在一些挑战。

  1. 真正的元编程不考虑封装。
  2. 真正的元编程不需要编译就能生成代码,所以不需要类型检查。

话虽如此,我尝试了一些我不确定是真的真的勇敢还是真的真的愚蠢的事情。

这可能并不愚蠢。

开发中的测试

软件质量保证包括监控软件工程过程的手段和用于确保质量的方法。当然,这个过程的一部分是为编写的代码编写测试,并确保产品从用户的角度来看是按预期工作的。

当我意识到我需要 UI 测试时,我有点惭愧,在 JUnit 测试之后,我从来没有接受过自我测试的培训。但是我也意识到,有许多其他的工程师也避免测试,并且在实践中,许多策略对于回归测试来说更加麻烦。

大约一年前,我为销售人员编写了一个程序向导,为潜在客户生成现场演示,因为我很懒。巧合的是,这个项目是一种猴子补丁!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

TornadoFX-Dnd-TilesFX is open source here

我最终编写了自己的拖放功能,因为我不知道如何序列化我的自定义对象;问题是我在调试我的微管理事件时遇到了困难,我意识到我需要测试 UI。另一个问题?我不知道如何编写 UI 测试。我想——如果我使用元编程来为我生成这些测试会怎么样?

所以我开始询问其他开发人员和 QA 工程师:

  • 什么是好的测试?
  • 什么是糟糕的测试?

我收到了数百个答案——从不同的框架到不同的策略到不同的风格。

龙卷风套件:不仅仅是应用研究

TornadoFX-Suite 从简单地生成 TornadoFX UI 测试开始。但事实远不止如此。如果这个项目可以被多个人用于多种框架——我们可以收集这些数据并使用机器学习来找到这些答案吗?

让我们看看这个项目现在的样子,看看它做了什么。

TornadoFX-Suite is open source and can be found here

您会注意到这个应用程序检测 UI 输入——从那里, kscripting 将被实现来生成那些测试。

检测用户界面输入

到目前为止,这是项目中最难克服的困难。使用 Kotlin 编译器的包装器 kastree ,我能够将我扫描的 Kotlin 文件分解成抽象语法树(AST)。我选择 ASTs 的原因是我们能够保持代码分析的不可知论,以便在未来的框架中使用。在创建所需的映射时,我遇到了现实生活中的元编程挑战——在语法分析中,当您的静态系统关心您必须转换的类型时,很难递归地映射潜在的无限树(无论是高度还是宽度)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Finally bumped into an identified challenge where true metaprogramming and statically-typed languages like @kotlin don’t play where together — being able to inspect AST breakdowns recursively without caring about the type system.

特别是,分解 Kotlin 语言的属性被证明是困难的。属性可以是集合、变量、独立于类的函数或成员。这些项目中的每一个都可以在访问级别、类型方面有进一步的分类,并且可以在那些属性中包含额外的属性。

我不是说这是最好的解决方案。我不是说这是一个好的。但是我找到了一个!将这些 ast 转换成 JSON 对象/数组使得造型更容易以更不可知的方式递归:

性能差吗?*地狱是的,它是。*这个实现烂不烂?当然有。 但是这管用吗?如果行得通,那就不傻。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以我们有这些故障。我如何知道我应该关心哪些元素?这很容易用 Kotlin 枚举类为每个框架定制:

enum class INPUTS {
    Form, TextField, DateField, Button, Action,
    RadioButton, ToggleButton, ComboButton, Checkbox,
    Item, Paginator, PasswordField
}

好吧,我们有要注意的关键词。我们可以使用具体化的泛型,通过枚举类遍历我们的控件属性集合:

这是我们可以在任何框架中检测所需控制的概念验证。我们正在努力创建一个不可知论的解析系统,最终,当它成长到足以处理这样的责任时,让它存在于自己的库中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下一步是什么?

行动是影响环境的东西。

这就是测试的作用。

测试是一个探索环境的主动过程,确定它如何响应各种条件。

测试人员从这些交互中学习。

因此,如果我们在谈论一个生成型机器学习工具来“做测试”,根据定义,它必须学习。它必须通过采取行动并能够处理这些行动的反馈来做到这一点。我们可以从创建生成的用户输入的排列开始,这些排列可以覆盖人类可能永远没有能力覆盖的情况,但我们会增加测试许多东西所需的时间。如果我们把这些排列缩短成有价值的测试会怎么样?

元编程的自然发展是机器学习。

我们可以从用户那里收集数据——为什么生成了什么测试,最初通过/失败了什么测试,有多少用户参与了项目?从那里,有可能检测出已知有问题的组件,并为此提供更智能的测试。我们甚至可以找到数据来说明什么是好的 UI/UX。我甚至不认为我在这里抓到表面。

你有兴趣投稿吗?我经常放松自己,当然,我也可以在推特github 上联系。项目可以在这里找到

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值