基于量子增强迁移学习的文档分类
用变化的量子电路来补充预先训练的模型,将我们带到了量子增强的自然语言处理时代
最近,一个趋势非常明显:需要大型的预训练模型来实现计算机视觉和语言理解的最先进的性能。特别是,随着基于 Transformer 架构的模型的出现,自然语言处理(NLP)领域的能力和网络规模都在呈指数级增长(例如伯特、 GPT-2 )。在本文中,我将通过使用通用语句编码器(USE)嵌入和一种称为变分量子电路的量子机器学习操作符,重点关注短文档(科学论文的预印本)的分类,粗略地说,这相当于经典机器学习领域中的全连接(密集)层。关于单词和句子嵌入如何用于分类的介绍,参见我之前的文章用通用的句子嵌入对科学论文进行分类。如果你不熟悉量子计算的概念,我希望你会喜欢阅读我的文章CERN 的幽灵计算机。
量子机器学习
在我们深入研究实际代码(你可以在https://github.com/rdisipio/qnlp上找到)之前,需要几句话来解释什么是量子机器学习,以及它如何对这种应用有用。
首先,在量子计算中,人们处理称为量子位的等效位,由于量子系统的一种称为叠加的属性(有时据说它们可以同时代表 0 和 1),量子位众所周知可以处理非二进制状态的信息。量子电路是应用于量子位的一系列操作,通过改变它们的相对相位来改变它们的状态*,例如*。量子位可以用所谓的布洛赫球进行几何表示,因此对量子位的操作对应于这个虚拟空间中量子状态向量的旋转。
布洛赫球的图形表示。对量子位的量子操作对应于这个虚拟空间中量子状态向量的旋转。[鸣谢:维基百科]
根据计算的目的,有许多可能的量子电路。一般来说,量子电路实现一个量子算法。关于什么样的计算是可能的,请参考微软的斯蒂芬·乔丹维护的量子算法动物园。最著名的大概就是彼得·肖尔的因式分解算法。量子算法的一个关键概念是,由于固有的自然量子位,某些计算可以以比经典等价计算更小的复杂性来执行(这种特性被称为超多项式加速)。完全公平地说,也有很多正在进行的研究试图“去量子化”这种算法,并使经典版本和量子版本一样快——这是因为量子比特的初始状态必须在运行实际算法之前准备好,这降低了整体加速。这一方向的开创性论文是 Ewin Tang 的“量子启发的经典主成分分析和监督聚类算法”。
事实上,实际应用更可能是经典和量子运算的混合。尤其是在机器学习领域,人们可能会想,这两个领域是否可以结合在一起,联手合作,好消息是,事实上他们可以,他们也确实这样做了。加拿大初创公司 Xanadu 开发的一个名为 PennyLane 的软件库提供了常见 ML 框架如 PyTorch 和 TensorFlow 与量子计算生态系统之间的接口。主要的想法很简单,但非常强大:人们可以用量子对应物代替神经网络的一部分(比如说致密层),就是这样!你创造了一个混合经典量子神经网络。事实上,由于经典机器学习并不仅限于神经网络,量子机器学习也在各种方法中找到了它的优势,如量子支持向量机。为了全面了解什么是可能的,我建议关注 edX 上的在线课程,该课程由多伦多大学已故的彼得·魏特克教授。
变分电路(也称为参数化量子电路)是一系列混合量子经典算法,以类似于经典致密层的方式执行操作。该算法的核心是依赖于一组参数𝜃的量子电路 𝑈,以及定义待优化标量的目标函数。电路可以被认为是一个黑盒,优化通常是通过迭代方案实现的,例如通过基于梯度的优化。
变分量子电路示意图。[鸣谢:Xanadu.ai]
文件分类
正如我在用通用句子嵌入分类科学论文中解释的,谷歌在tensorflohub上发布了一个预训练版本的通用句子编码器(使用)模型。要使用它:
import tensorflow_hub as hub
embed = hub.load("https://tfhub.dev/google/universal-sentence-encoder/4")
embeddings = embed([
"The quick brown fox jumps over the lazy dog.",
"I am a sentence for which I would like to get its embedding"])
输出由一个 512 维的向量列表组成,每个向量对应于输入中的一个文本字符串(在上面的例子中例如“快速棕色狐狸…”)。按照迁移学习范式,这个模型可以作为一个更大的网络的一部分。人们可以直接使用它,其参数是固定的,或者通过允许优化者修改它们来微调它。反过来,使用模型的输出被传递到其他层,以便为给定的下游任务训练网络的其余部分。有几个来自官方文档的例子,但是文档分类可能是最直观的。在这个例子中,我们将把属于两个非常不同的类别(即astro-ph
和cs.AI
)的预印本的摘要转换成句子嵌入,并附加一个具有两个输出的密集层。每个输出节点对应一个类别,因此在将输出与实际标签进行比较之前,应用一个 softmax 激活功能。这是一个简单模型的样子:
有趣的是,人们可以在几秒钟内只用 1000 个例子训练这样的模型,但却获得 99.6%的准确率。
为了“量子化”我们的模型,我们必须用一个变分的量子电路来代替位于嵌入和输出之间的层。典型地,经典密集层具有 N 个输入和 M 个输出,因此在内部,它对应于矩阵乘法,随后是偏置的相加,以及激活函数的应用,,例如:
def call(self, inputs): x = tf.matmul(inputs, self.W1) + self.b1
x = tf.math.tanh(x) return x
不幸的是,量子层不能明确地做到这一点,而是需要修饰。这意味着量子变分层实际上由三个操作组成:
- 一个经典的密集层,将维度从 N_inputs 转换为 N _ qubits,并将输入缩放π/2(因此它可以表示围绕 Bloch 球的旋转)
- 改变量子位状态的量子层
- 一个经典的稠密层,将维度从 N 个量子位转换成 N 个输出
为了用 PennyLane 库做到这一点,必须定义一个执行量子操作的设备(例如模拟器default.qubit
,或者一个真实的设备,比如由 IBM Q 或者 Rigetti 提供的设备)。然后,实际电路被编码到 python 函数中,例如:
def _circuit(inputs, parameters): qml.templates.embeddings.AngleEmbedding(inputs, wires=list(range(self.n_qubits))) qml.templates.layers.StronglyEntanglingLayers(parameters, wires=list(range(self.n_qubits))) return [qml.expval(qml.PauliZ(i)) for i in range(self.n_qubits)]
我们可以将许多致密层堆叠在一起,以增加网络的深度,量子变分层也可以做到这一点。在这个例子中,我们将保持简单,用四个量子位和两层快速运行。
最后,有一个包装类QNode
负责将命令发送到后端设备,并得到结果。最值得注意的是,这个类通过简单地指定一个参数*,例如*,为 TensorFlow 和 PyTorch 提供了接口:
self.layer = qml.QNode(_circuit, self.dev, interface="tf")
整个量子模型看起来像这样:
结果和讨论
使用模拟器训练分类器的量子版本需要长得多的时间(每个时期大约一分钟,批量大小为 32)。这并不奇怪:如果我们有有效的量子过程模拟,我们首先就不需要量子硬件了!事实是,这种东西在原则上是不存在的,因为量子物理具有像纠缠这样的性质,而这在经典领域是没有对应的。因此,要传达的信息是,量子过程的模拟需要很长时间,并且随着量子比特数量的增加,变得越来越难以管理。另一方面,现实生活中的量子硬件是嘈杂的,因此约翰·普雷斯基尔定义我们目前生活在嘈杂的中等规模量子(NISQ)技术时代。事实上,热噪声限制了在不引入误差的情况下进行长时间计算的能力,并且可能是今天实现创建大规模、可靠的量子硬件的目标的主要障碍。
也就是说,与纯经典模型相比,混合网络能够以 98.8%的准确度执行分类。这也不应该太令人惊讶,因为繁重的工作实际上是由使用嵌入层完成的。然而,这实际上是一个了不起的结果:它意味着我们可以部署迁移学习,这样我们就不必为了同样的目的训练一个纯粹的量子网络,而只是将其用于下游的任务。在 PennyLane 网站上有另一个关于图像分类的迁移学习的例子,我鼓励大家去看看。
量子增强的自然语言处理时代即将到来!
时尚服装分类-计算机视觉入门
通过创建一个对时尚服装图像进行分类的模型,开始学习计算机视觉。
在本指南中,您将训练一个神经网络模型来对衬衫、外套、运动鞋等服装图像进行分类。
咻!这对于初学者教程来说听起来太多了,我的意思是我们才刚刚开始,对吗?
不要担心!不要不知所措,不了解全部细节也没关系。相信我,随着你对这篇文章的深入研究,你会了解到所有的细节。
如果你对机器学习完全陌生,我建议你看看我的初学者教程。
这里是完成的 Colab 笔记本和 GitHub 回购。
说完了,我们开始吧!
数据
我们将使用时尚-MNIST 数据集。它是由 10 种服装的 60,000 个正方形(28x28 像素)灰度图像组成的数据集。
每件衣服都有一个特定的标签:
0- T-shirt/top1- Trouser2- Pullover3- Dress4- Coat5- Sandal6- Shirt7- Sneaker8- Bag9- Ankle boot
时尚 MNIST 数据集|来源:ZALANDO RESEARCH
让我们来看看代码
我们将使用 TensorFlow 和 TensorFlow Keras 来构建我们的模型。
TensorFlow 是一个机器学习的端到端开源平台。它有一个全面、灵活的工具、库和社区资源的生态系统,让研究人员推动 ML 的最新发展,开发人员轻松构建和部署 ML 驱动的应用程序。
Keras 是 TensorFlow 用于构建和训练深度学习模型的高级 API。
你可以了解更多。现在对这些工具有一个基本的了解就足够了,因为随着时间的推移,你会学到更多关于 TensorFlow 和 Keras 的知识。
少说多码!!!
导入库
我们将使用 numpy 和 matplotlib 作为辅助库。
我们从导入所有必需的库开始
导入数据
我们从 Keras 数据集导入时尚 MNIST 数据
这将把fashion_mnist
数据加载到 4 个 NumPy 数组中:
train_images
和train_labels
数组是训练集——模型用来学习的数据。- 根据测试集、
test_images
和test_labels
阵列对模型进行测试。
探索数据
下面的代码显示有 6000 个训练图像和 1000 个 28x28 像素的测试图像。我们将在训练图像上训练模型,并通过在测试图像上执行预测来测试模型的性能。图像在train_labels
和test_labels
中有相应的标记。
我们发现我们加载的数据
Train Images Shape: (60000, 28, 28)
Train Labels Shape: (60000,)
Test Images Shape: (10000, 28, 28)
Test Labels Shape: (10000,)
现在让我们来看看刚刚加载的数据。
让我们来看看加载的图片!
从时尚 MNIST 数据集中加载的图像
由于像素值介于 0–255 之间,我们将其转换为介于 0 和 1 之间的值。即我们只是将像素值除以 255.0。
使图像正常化
创建模型
构建神经网络需要配置模型的层,然后编译模型。
层是神经网络的基本构建模块。他们从输入的数据中提取特征或表示。经过训练后,这些功能将帮助我们解决手头的问题——对时尚服装进行分类。
在这里,我们将链接一些简单的层来创建我们的模型。
我们通过链接层来构建我们的模型
网络的第一层,TF . keras . layers . flatten,将 2D 阵列(28×28 像素)的图像转换为 1D 阵列(大小为 28*28 = 784)。它基本上接受输入图像,将每行像素背靠背排列起来。该层仅用于转换数据。
一旦输入图像被展平层转换,网络就有两个 tf.keras.layers.Dense 层。
这些是井,密集连接或完全连接的层。
紧密相连的层
第一密集层有 128 个神经元,第二密集层是我们网络的最后一层,有 10 个神经元。网络的最后一层是输出层,它将提供模型的输出。10 个节点中的每一个都将包含指示当前图像属于 10 个类别之一的概率分数。(请记住,我们的数据中有 10 种服装分类)
编译模型
我们几乎准备好训练我们的模型了!在此之前,我们必须再配置一些设置。
损失函数:测量模型在训练过程中的精确度。您希望最小化该函数,以便将模型“导向”正确的方向。即,该模型试图通过训练的每一步来最小化损失函数,以改进该模型。
优化器:优化器更新权重参数,使损失函数最小化。
指标:指标是一个用来判断你的模型性能的函数。度量函数类似于损失函数,只是在训练模型时不使用评估度量的结果。以下模型使用准确度,即图像被正确分类的比例。
我们编译这个模型
你现在不需要知道损失函数sparse _ categorial _ cross entropy或 adam 优化器的所有细节。如果你需要了解更多,你可以查看文档。现在,掌握什么是损失函数和优化器就足够了。
训练模型
为了训练我们的模型,我们简单地将我们的训练数据和标签分别包含在train_images
和train_labels
中。
我们称之为model.fit
方法来“拟合”训练数据的模型。
最后我们训练我们的模型
Epoch 1/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.3768 - accuracy: 0.8636
Epoch 2/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.3394 - accuracy: 0.8762
Epoch 3/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.3145 - accuracy: 0.8851
Epoch 4/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2965 - accuracy: 0.8902
Epoch 5/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2818 - accuracy: 0.8957
Epoch 6/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2698 - accuracy: 0.9002
Epoch 7/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2582 - accuracy: 0.9043
Epoch 8/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2495 - accuracy: 0.9074
Epoch 9/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2409 - accuracy: 0.9095
Epoch 10/10
1875/1875 [==============================] - 4s 2ms/step - loss: 0.2324 - accuracy: 0.9137
我们可以看到模型训练时显示的损失和准确性指标。随着模型的训练,损耗降低,精度提高。太棒了。你的模特在学习!
评估模型
该模型对训练数据的准确率约为 90% (0.90)。您可能有大约 90%的值(不要担心它是否略有不同,因为它可能有一些随机性)
但这还不够!我们还没有测试这个模型。我们现在将在我们的测试数据上测试我们的模型,这是模型从未见过的!让我们看看它的表现如何。
让我们看看我们的模型在测试数据上表现如何!
313/313 - 1s - loss: 0.3366 - accuracy: 0.8838
Test accuracy: 0.8838000297546387
事实证明,测试数据集上的准确性比训练数据集稍低。这可能意味着我们的模型过度拟合了我们的训练数据。我们现在不用担心这个。在以后的文章中,我们将讨论是什么导致了过度拟合,以及我们如何防止它。
做预测
终于!我们现在可以使用我们的模型对图像进行预测。这里我们有一个函数来绘制 100 个随机测试图像和它们的预测标签。如果预测结果与test_labels
数据集中提供的标签不同,我们将用红色突出显示。
我们构建一个函数来查看模型的预测
我们有一些错误,但对于这样一个简单的模型,它做得相当好!
哇!你做到了!您已经成功地创建了一个模型,它可以查看时尚服装的图像,并以良好的确定性对它们进行分类!仔细想想,只需要几行代码。
我们看到了一些错误,但是对于我们的第一个模型,事情看起来相当不错!
完整的 Colab 笔记本可以在这里获得,代码也可以在 GitHub 获得。
有了 TensorFlow、Keras 和一般机器学习的新知识,您将能够为各种数据集创建自己的模型。此外,您在这里学到的工具和技术是实践中使用的复杂模型的基础。
在接下来的教程中,我们将看看卷积神经网络——一种广泛用于计算机视觉应用的神经网络。我们将看到,我们可以使用 CNN 进一步提高我们的模型的准确性。
快乐编码!
时尚服装分类-卷积神经网络入门
通过创建卷积神经网络(CNN)对时尚服装图像进行分类,开始学习计算机视觉。
在本指南中,我们将改进我们在上一节中训练的服装图像分类模型。
如果你是机器学习或使用 TensorFlow / Keras 的新手,我会建议你看看第一部分,在那里我们以一种更容易和初学者友好的方式解决了同样的问题。
那好吧。让我简单介绍一下我们将要做的事情:
我们将关注一种新的深度学习神经网络,称为卷积神经网络。它们最常用于计算机视觉应用,非常适合手头的任务。
简而言之,CNN 使网络能够将输入图像视为边缘、角落和颜色的组合,本质上捕捉了图像的内容。
要从更技术性的角度来看 CNN,我建议你浏览一下这篇文章。
在本指南中,我们不会过多地研究 CNN 是如何工作的,而是将重点放在如何使用 TensorFlow 和 Keras 在代码中实现它。
这里是完成的 Colab 笔记本和 GitHub 回购。
数据
像之前的指南一样,我们将使用时尚-MNIST 数据集。它是由 10 种服装的 60,000 个正方形(28x28 像素)灰度图像组成的数据集。
每件衣服都有一个特定的标签:
0- T-shirt/top1- Trouser2- Pullover3- Dress4- Coat5- Sandal6- Shirt7- Sneaker8- Bag9- Ankle boot
时尚 MNIST 数据集|来源: ZALANDO RESEARCH
代码
我们将使用 TensorFlow 和 TensorFlow Keras 来构建我们的模型。
我们将通过使用 CNN 作为我们的神经网络来扩展我们的知识。
我们将像在之前的教程中一样预处理图像。如果您不确定,请查看此链接。
完整的代码可以在 Colab 笔记本和 GitHub repo 中找到。
在这一部分中,我们将直接开始创建模型。本质上,我们正在将我们新的和改进的 CNN 模型插入到我们以前的代码中。
创建模型
我们的模型将一个 28px x 28px 的灰度图像作为输入,输出一个长度为 10 的浮点数组,表示图像是服装的概率。(数据集中有 10 种不同的服装,标记为 0-9)
让我们看看我们的模型,然后我们将通过每一层。
首先,我们添加一个与我们的输入形状匹配的输入层。
然后我们改变输入的形状,增加一个维度来表示我们图像的颜色通道。因为我们示例中的图像是灰度图像,所以颜色通道的数量是 1。
然后,我们有我们的第一个卷积 Conv2D 层。我们有 32 个内核大小为 3×3 的过滤器。
下面是一张 gif 图,展示了卷积是如何工作的。你可以查看这篇文章了解更多细节。( 嘘……如果你还没有 ,我建议你去看看)
卷积在行动!|来源: vdumoulin
接下来,我们有另一个 Conv2D 层,有 64 个滤镜和 3x3 内核。你可能已经从代码中猜到了。
接下来我们有新的东西,一个池层。
你问什么是池层?为什么说我什么时候可以给你看?我认为这张 gif 是不言自明的。
MaxPooling Layer |来源:谷歌
如果这还不能说明问题,是的,你猜对了,看看我经常提到的那篇文章吧!
接下来是辍学层。它所做的是在训练过程中随机忽略或“丢弃”网络中的一些层。这可以防止网络中的单元过度适应训练数据。
然后,我们展平前一层的输出,并将其传递给一个密集连接的网络,这将是我们的输出层。
最终层中的 10 个节点中的每一个都将包含指示当前图像属于 10 个类别之一的概率分数。(请记住,我们的数据中有 10 种服装分类)
然后,我们像在之前的指南中一样编译和训练模型。
完整的代码可以在 Colab 笔记本和 GitHub repo 中找到。
评估模型
我们的模型对测试数据有大约 92%的准确率。这比我们以前的型号大约增加了 5%。
313/313 [==============================] — 1s 2ms/step — loss: 0.3239 — accuracy: 0.9203 Test accuracy: 0.9203000068664551
模型似乎仍然过度拟合训练数据。但是我们会把它留到下一次。我们现在不想让事情变得太复杂。
但是有了这个指南,我希望你能够得到在 TensorFlow 和 Keras 中使用 CNN 的 101 指南。在接下来的指南中,我们将看看如何通过使用一种叫做迁移学习的技术来进一步改进我们的模型。
我们还将学习图像增强技术,以便我们不会用训练数据过度拟合我们的模型。在那之前,
快乐编码!
分类同质数据,你需要知道的!
过去一些研究人员做的很粗略,你应该做得更好!
我在北岭加州州立大学和刘莉教授一起做人机交互(HCI)的硕士论文。在此期间,我发现了一些我们作为数据科学家可以改进的地方。我没有统计学学位,也没有在网上找到以前的相关工作,但我在大量相关论文中看到了这个问题,并决定与大家分享我的发现。我认为我们需要重新考虑处理同质数据的方式。
图片来自 Pixabay
什么是同质数据?
如果一个数据集是由彼此相似的事物组成的,那么它就是同构的。在本文的范围内,它意味着来自完全相同来源的数据。在监督学习的典型场景中,这将导致数据集在整个集合中具有完全相同的标签。
人们是如何处理同质数据的?
费边论文中的非加权概率和
在大多数过去的研究中,比如法比安关于击键动力学的论文,研究人员会根据所有计算出的概率的总和来评分。
人工神经网络的 soft-max 层本质上做同样的事情。它向网络的下一层中的节点输出加权和。
这背后的直觉很简单:
直觉:如果更多的推理倾向于一个结果,那么这个结果最有可能是正确的。
但远非完美…
我们在处理同质数据时遇到了什么问题?
概率总和背后的直觉是强大的,但远非完美。考虑这样一种情况,我们有两类数据,分别标记为 A 和 B。数据的分布如下图所示,其中 B 的分布区域由 A 包围。现在,如果我们发现样本来自区域 B,则样本可能具有标签 A 或 B。
典型数据分布图
当两个标签的样本数量相似时,我们可以获取多个样本,然后判断它们属于 B 类。这是因为 B 区域较小,因此 B 类的密度较高。因此,在 B 区域中,B 类比 a 类更有可能被找到。概率总和仍然有效。然而,如果标签为 B 的样本少得多,或者如果区域大小相似且重叠而不是封闭,我们可能会从 A 类获得比 B 类更多的样本,并且无法正确地对其来源进行分类。
调整与偏差成比例的概率和的权重肯定会有助于这些情况,但这种近似是粗略的…
我们应该如何处理同质数据?
在我们开始之前,让我们做 3 个假设:
- 多个获得的样本来自相同的来源。
- 抽样过程在概率上是独立的。
- 每个等级在他们的区域内都有一个概率分布。
现在我们可以考虑下面展示的两种直觉。为简单起见,让我们假设在每个区域内均匀分布。
典型数据分布图
直觉 1:如果样本来自 A 区但在 B 区之外,那么来源就是 A 类。
直觉 2:如果样本总是在区域 B 内,那么源极有可能是 B 类。
据此,我们可以推导出以下等式:
具有特定标注的采样要素的概率
现在让我们假设两个圆内均匀分布的点。假设红色圆圈 A 覆盖的面积是蓝色圆圈 B 的两倍,并且每个标签的样本数量相等。然后,如果我们随机选择一个位于蓝色圆圈内的点,因为红色点的密度是一半,所以选择的点实际上有 2 / 3 的机会属于蓝色区域。
如果我们从蓝色圆圈内进行 10 次观察,总几率不会改变。但是如果我们应用我们的新技术,我们会得到:
当我们已经看到 10 个样本都来自蓝色圆圈内时,这是一个更有可能的估计。
变异 k-NN 的性能
为了测试这一点,我创建了自己版本的 k-NN。它是原始 k-NN 的变异版本。这里是如何在 Node.js 中基于 k-d 树构建自己的 k-NN 的教程。输入数据来自我最近的一个实验,正如你所看到的,这些点彼此重叠。
重叠数据
性能如下表所示,其中 n 是选择 u 样本的次数。由于变异的 k-NN 的性能由于数据量小而不稳定,所以用随机混洗的数据进行了多次实验。
变异 k-NN 的性能
正如我们所看到的,变异后的 k-NN 总体上确实有超越性的表现。只有当数据分布在如上所述的封闭或严重重叠的情况下,性能改进才会更好。这将为我们处理这些类型的数据提供一些见解。
注释在最后…
我希望这篇文章对你有所帮助。我是一名计算机专业的学生,对很多事情都感兴趣。写作也是我的爱好,因为我喜欢把事情弄清楚并与他人分享,所以如果你在 Medium 上跟随我,你会看到更多这样的事情。
如果你感兴趣的话,我还有一篇关于我在数据科学领域的发现的非常老的文章:
利用 fast.ai v2 对酒精饮料图像进行分类
实践教程
U sing fast.ai v2 和 Google Colab 提供令人陶醉的数据和 dram 混合服务
动机
我已经开始沉浸在更新的 fast.ai v2 深度学习课程中,我觉得应用和记录我迄今为止所学的东西将是理想的。在这篇文章中,我将分享我如何训练深度学习(CNN)分类器来区分不同类型的流行酒精饮料。
本演练的一个重要亮点是,它详细介绍了有关如何利用更新的 Microsoft Azure Bing Search V7 API 的最新说明,因为关键更改已于 2020 年 10 月 30 日实现。
链接
内容
第 1 节—设置
第 2 部分—下载图像数据
第 3 节—准备图像数据集
第 4 部分——训练模型
第 5 节—清理数据
第 6 节—使用图像分类器进行推理
第 7 部分—部署为 Web 应用
第 1 部分—设置
首先,我强烈推荐在 Google Colab 上运行这款笔记本。要了解更多关于 Google Colab 设置的信息,请访问此链接。
一旦这台笔记本在 Google Colab 上打开,打开笔记本的 GPU 加速器,方法是前往顶部菜单,单击**Runtime**
>**Change runtime type**
>,选择 **GPU**
作为硬件加速器。
接下来,在 Google Colab 运行时上安装 Google Drive。将出现一个链接,供您点击并检索您的授权码。授予 Google Drive 文件流的权限后,复制提供的代码,将其粘贴到“输入您的授权码”下的字段中,然后按 Enter 键。
from google.colab import drive
drive.mount('/content/drive/')
下一步是安装 fast.ai 依赖项。我发现这种依赖版本的组合在 Google Colab 上工作得很顺利。
!pip install fastai==2.0.19
!pip install fastai2==0.0.30
!pip install fastcore==1.3.1
!pip install -Uqq fastbookimport fastbook
from fastbook import *
fastbook.setup_book()
from fastai.vision.widgets import *
import warnings
warnings.filterwarnings("ignore")
import requests
import matplotlib.pyplot as plt
import PIL.Image
from io import BytesIO
import os
from IPython.display import Image
from IPython.core.display import HTML
然后,我们创建一个路径来存储将要下载的图像。注意,结果目录路径将是/content/images
。
try:
os.mkdir('images')
except:
pass
之后,我们需要检索 Azure Bing Search V7 的 API 密钥,因为我们将使用它从 Bing 中提取图像数据集。要了解更多关于在微软 Azure 门户中设置 Bing 搜索 API 密钥的信息,请查看我的 GitHub repo 中的自述文件,以及微软 Azure Bing 搜索 API 快速入门指南。
subscription_key = "XXX" #Replace XXX with your own API key
search_url = "https://api.bing.microsoft.com/v7.0/images/search"
headers = {"Ocp-Apim-Subscription-Key" : subscription_key}
一旦关键字被输入到subscription_key
变量中,我们就可以检索一组与您选择的关键字相关的图像 URL。例如,为了找到whisky
的一组图像,我们运行以下代码:
search_term = "whisky"#Add in count parameter so that max number of images (150) is #retrieved upon each API call. Otherwise, default is 35.params = {"q": search_term, "license": "public", "imageType": "photo", "count":"150"}response = requests.get(search_url, headers=headers, params=params)
response.raise_for_status()# Return json file
search_results = response.json()# Create a set of thumbnails for visualization
thumbnail_urls = [img["thumbnailUrl"] for img in search_results["value"][:16]]
我们可以创建一个 4 x 4 的缩略图网格图来可视化检索到的图像,允许我们验证这些图像确实代表了我们的关键字whisky
。从展示的内容来看,我觉得这的确很像上好的老威士忌。
f, axes = plt.subplots(4, 4)
for i in range(4):
for j in range(4):
image_data = requests.get(thumbnail_urls[i+4*j])
image_data.raise_for_status()
image = Image.open(BytesIO(image_data.content))
axes[i][j].imshow(image)
axes[i][j].axis("off")
plt.show()
下一步是整理来自搜索结果 JSON 文件的图像 URL 列表。与图像 URL 相关联的关键字是contentUrl
。
img_urls = [img['contentUrl'] for img in search_results["value"]]
len(img_urls)
len
函数应该会返回一个与关键字whisky
相关的 150 个图片 URL 的列表。然后,我们从 URL 下载并显示一张图片到我们的images
文件夹中名为whisky_test.jpg
的destining 文件中。
dest = 'images/whisky_test.jpg'
download_url(img_urls[1], dest)img = Image.open(dest)
img.to_thumb(224,224)
我们得到了上面的威士忌图片,显示上面的代码块完美地工作。我们现在准备好了主要的行动。
第 2 节—下载图像数据
更新:由于图片下载方式是动态的,请参考 fast.ai 图片部分 了解图片下载的最新方式
区分常见酒类的图像会很有趣,即威士忌、葡萄酒和啤酒。为此,我们首先在一个列表中定义三种酒精类型,并创建一个Path
来存储我们稍后将要下载的图像。
alcohol_types = ['whisky','wine','beer']
path = Path('alcohol')
对于三种酒精类型中的每一种,我们都创建了一个子路径来存储图像,然后从整理好的图像 URL 中下载图像。
if not path.exists():
path.mkdir()
for alc_type in alcohol_types:
dest = (path/alc_type)
dest.mkdir(exist_ok=True)
search_term = alc_type
params = {"q":search_term, "license":"public",
"imageType":"photo", "count":"150"}
response = requests.get(search_url, headers=headers,
params=params)
response.raise_for_status()
search_results = response.json()
img_urls = [img['contentUrl'] for img in
search_results["value"]]
# Downloading images from the list of image URLs
download_images(dest, urls=img_urls)
现在,我们应该将图像下载到带有酒精类型标签的相应文件夹中。为了证实这一点,我们可以利用get_image_files
函数。
img_files = get_image_files(path)
img_files(#445) [Path('alcohol/beer/00000069.jpeg'),Path('alcohol/beer/00000007.jpg'),Path('alcohol/beer/00000092.jpg'),Path('alcohol/beer/00000054.jpg'),Path('alcohol/beer/00000082.jpg'),Path('alcohol/beer/00000071.jpg'),Path('alcohol/beer/00000045.jpg'),Path('alcohol/beer/00000134.jpg'),Path('alcohol/beer/00000061.jpg'),Path('alcohol/beer/00000138.jpg')...]
另一种验证下载的方法是点击 Google Colab 屏幕左侧导航栏上的文件图标,并导航至content
> alcohol
以查看各自的文件夹。
之后,我们需要检查我们下载的文件是否损坏。幸运的是,fastai 通过verify_images
函数提供了一种方便的方法来实现这一点。
failed = verify_images(img_files)
failed
然后我们可以使用map
和unlink
方法从数据集中移除这些损坏的文件(如果有的话)。
failed.map(Path.unlink)
第 3 节-准备影像数据集
fastai 有一个叫做数据块 API 的灵活系统。有了这个 API,我们可以完全定制DataLoaders
对象的创建。DataLoaders
可以存储我们放置在其中的任何DataLoader
对象,并用于随后生成训练集和验证集。
在我的理解中,DataLoaders
本质上是一个对象,它存储了关于我们将要运行模型的数据的信息。DataBlock
基本上是一个创建DataLoaders
的模板函数。
alcohols = DataBlock(
blocks=(ImageBlock, CategoryBlock),
get_items=get_image_files,
splitter=RandomSplitter(valid_pct=0.2, seed=1),
get_y=parent_label,
item_tfms=Resize(128))
让我们一部分一部分地看一下上面的代码:
*blocks=(ImageBlock, CategoryBlock)*
这个blocks
元组分别表示我们的独立和相关变量的数据类型。因为我们的目标是对图像进行分类,我们的自变量是图像(ImageBlock
),而因变量是类别/标签(CategoryBlock
)
*get_items=get_image_files*
get_items
参数指定要使用的函数,以便提取数据集中图像的文件路径。回想一下,我们之前使用 fastai 内置的get_image_files
将文件路径放入变量img_files
。这个get_image_files
函数获取一个路径(我们将在后面指定),并返回该路径中所有图像的列表。
*splitter=RandomSplitter(valid_pct=0.2, seed=1)*
splitter
方法将把数据集分成训练集和验证集。我们指定RandomSplitter
来确保它被随机分割,其中的valid_pct
参数用于指示数据集的多大比例将被分配为验证集。也可以在RandomSplitter
中设置随机种子,以实现结果的再现性。
*get_y=parent_label*
parent_label
是 fastai 提供的一个函数,用来获取图像文件所在文件夹的名称。由于我们已经将图像放在具有各自酒类名称的文件夹中,这个parent_label
将返回文件夹名称威士忌、葡萄酒和啤酒。
*item_tfms=Resize(128)*
我们通常使用将图像调整为正方形,因为这样做更容易,因为原始图像可以有不同的高度/宽度和纵横比。为此,我们对每个项目(item_tfms
)执行转换,将它调整为 128x128 像素的正方形。
既然已经提供了所有的细节和参数,我们就可以用下面的单行代码启动数据加载器了。请注意,dataloaders
的参数是存储图像的path
,即alcohol
文件夹路径。
dls = alcohols.dataloaders(path)
让我们通过展示我们的验证集中的 10 个图像的子集来简要地看一下这些图像
注:以下显示的图像缩略图是验证数据下载是否正确的好方法。以前当我运行这段代码时,我意识到我没有相应地更新搜索词,导致所有图像都是威士忌。如果操作正确,接下来的一批应该会显示来自不同类别的大量图像。
dls.valid.show_batch(max_n=12, nrows=2)
演示数据增强的工作原理
在继续下一步之前,有必要讨论一下数据扩充的概念。为了丰富我们的训练数据集,我们可以创建输入数据的随机变化,使它们看起来不同,但实际上不会改变数据的原始含义和表示。
一种常见的方法包括RandomResizedCrop
,它抓取原始图像的随机子集。我们使用unique=True
用不同版本的RandomResizedCrop
变换来重复相同的图像。
发生的情况是,在每个时期(这是一个完整的通过我们在数据集中的所有图像),我们随机选择每个图像的不同部分。这意味着我们的模型可以学习识别我们图像中的不同特征。它还反映了图像在现实世界中的工作方式,因为同一件物品的不同照片可能会以略微不同的方式取景。使用项目转换的好处是,它反过来有助于防止过度拟合。
指定的min_scale
参数决定了每次最少选择多少图像。有了以上所有的决定,我们可以使用.new
方法创建一个新的DataBlock
对象,并运行它来给我们一个名为dls
的新的DataLoaders
对象。
alcohols = alcohols.new(item_tfms=RandomResizedCrop(128, min_scale=0.3))
dls = alcohols.dataloaders(path)
dls.train.show_batch(max_n=8, nrows=2, unique=True)
在项目转换(item_tfms
)之后,我们运行批量转换(batch_tfms
),在批量单个项目上应用一组标准的数据扩充转换(aug_transforms()
)。
item_tfms
和batch_tfms
的区别在于item_tfms
对每个单独的项目(如图像)进行转换,而batch_tfms
对整批项目进行转换。
以下代码说明了增强变换aug_transforms
对单个输入图像的影响。你将能够观察到某种形式的旋转、视角扭曲和对比度变化。
alcohols = alcohols.new(item_tfms=Resize(128), batch_tfms=aug_transforms())
dls = alcohols.dataloaders(path)
dls.train.show_batch(max_n=8, nrows=2, unique=True)
第 4 部分—训练模型
现在,图像已经准备好了(尽管还没有清理),我们可以开始训练,立即构建一个简单的深度学习模型。首先,我们用下面的代码准备好DataLoaders
对象。在这个迭代中,我们以 224x224 像素的尺寸调整和裁剪我们的图像,min_scale
为 0.5。
alcohols = alcohols.new(
item_tfms=RandomResizedCrop(224, min_scale=0.5),
batch_tfms=aug_transforms())
dls = alcohols.dataloaders(path)
卷积神经网络(CNN)是用于图像分类的事实上的神经网络类型,这就是我们将要使用的。架构方面,我对cnn_learner
fast.ai 函数任意选择了 resnet34(即 34 层深)。resnet 的细节可以在这里找到。
我们使用.fine_tune
方法而不是.fit
方法,因为我们利用预训练的 resnet 模型来执行迁移学习。我们指定历元的数量为 4(即.fine_tune
中的参数)。
learn = cnn_learner(dls, resnet34, metrics=error_rate)
learn.fine_tune(4)
从上面我们可以看到,在训练我们的 CNN 学习者几分钟后,我们获得了 0.229 的error_rate
(即 77.1%的准确率)。考虑到我们还没有清理数据集,这是一个不错的开始。
使用混淆矩阵可以更好地可视化结果。
interpretation = ClassificationInterpretation.from_learner(learn)
interpretation.plot_confusion_matrix()
fast.ai 还提供了一种简单的方法,让我们可以找出哪些图像的丢失率最高。如果模型是不正确的(特别是如果它也对其不正确的答案有信心),或者如果它是正确的,但对其正确的答案没有信心,则损失是较高的数字。这些帮助我们识别模型有问题的图像。
interpretation.plot_top_losses(5, nrows=1)
从上面来看,似乎有些问题源于几个实际标签贴错了,而不是预测错了。
例如,行中间的图像很明显是一品脱啤酒的图像(这正是模型所预测的)。然而,分配给它的实际标签是威士忌,这是不正确的。这凸显了在训练任何类型的机器学习模型之前,拥有正确标记的数据(尽可能多)的重要性。
第 5 节—清理数据
请注意,我们在清理数据之前运行了模型。事实上,这样做使得数据清理更加容易。如上图所示,plot_top_losses
已经可以指出哪些图像是模型最难处理的。
通过内置的 fast.ai ImageClassifierCleaner
图形用户界面(GUI)小部件,数据清理过程变得很容易。
cleaner = ImageClassifierCleaner(learn)
cleaner
显示的图像行是损失最大的图像,这个 GUI 为您提供了查看和修改它们的机会。
清理是通过为上面的每个图像选择一个下拉选项,然后反复运行“清理”代码来完成的。由于网上的解释不清楚,我花了一些时间来弄清楚这个问题,所以这里有一些进一步的细节:
- 步骤 1 :在
cleaner
输出单元中加载一行图像(如葡萄酒类别的训练集)后,根据您自己的判断,为您希望编辑的图像选择一个下拉选项。选项包括从数据集中删除图像,或将图像移动到新的类别中。如果不需要更改,则不需要选择任何选项,因为默认选项是<保持>。 - 步骤 2 :一旦你完成了显示的图像行的选项更新,运行下面的“清理”代码来执行更改。
# Delete images marked as delete
for idx in cleaner.delete(): cleaner.fns[idx].unlink()
# Update category of image to the newly specified category by moving # it into the appropriate folder
for idx,cat in cleaner.change(): shutil.copyfile(cleaner.fns[idx], path/cat)
cleaner.delete
删除您标记为<删除>的图像,同时cleaner.change
将图像转移到带有更新标签的文件夹中。
- 步骤 3: 再次返回到带有该行图像的
cleaner
单元格,并通过下拉菜单切换到一组新的图像,例如啤酒类别的验证组或威士忌类别的训练组 - 步骤 4: 加载新一行图像后,选择每个图像的相关下拉选项,然后重新运行“清理”代码
- 第 5 步:对每个数据集重复第 3 步和第 4 步,直到所有数据集至少被检查一次。
数据清理后重新训练模型
dls = alcohols.dataloaders(path)
learn = cnn_learner(dls, resnet34, metrics=error_rate)
learn.fine_tune(4)
通过一点点数据清理(主要是通过用.unlink
方法移除不相关的图像),我们看到了error_rate
的巨大改进(从 0.229 降低到 0.096)。这意味着准确率从更早的 77.1%(数据清理前)提高到了现在的 90.4%。
interpretation = ClassificationInterpretation.from_learner(learn)
interpretation.plot_confusion_matrix()
从上面的混淆矩阵中,我们可以清楚地看到,该模型在区分三种类型的酒精饮料方面变得更好。
训练完成后,我们希望导出模型,以便保存架构、训练参数和DataLoaders
设置。这些都要存到泡菜里(。pkl)文件。
learn.export()path = Path()
path.ls(file_exts='.pkl')(#1) [Path('export.pkl')]
第 6 节—使用图像分类器进行推理
在加载包含我们深度学习图像分类模型信息的 pickle 文件后,我们可以用它来推断(或预测)新图像的标签。模型现在被加载到学习变量learn_inf
中。
learn_inf = load_learner(path/'export.pkl')
我们使用一个样本图像来测试我们的模型
# Sample image
ims = ['https://alcohaul.sg/products/i/400/5f7edfe79ae56e6d7b8b49cf_0.jpg']
dest = 'images/test_whisky.jpg'
download_url(ims[0], dest)im = Image.open(dest)
im.to_thumb(224,224)
我们的样本图像是日比喜和谐威士忌的图像。让我们看看我们的模型是否能够识别它的类别。
learn_inf.predict('images/test_whisky.jpg')('whisky', tensor(1), tensor([4.1783e-04, 9.9951e-01, 7.0310e-05]))learn_inf.dls.vocab['beer', 'whisky', 'wine']
看起来一切都很好。该模型能够以高置信度确定测试图像代表威士忌的图像(概率为 99.95%)。
第 7 部分—部署为 Web 应用程序
让我们简单探讨一下模型的部署。我们首先为用户创建一个按钮,上传他们希望分类的新图像。
然后,我们利用PIL.Image.create
方法检索上传的图像,并将其存储在img
变量中
btn_upload = widgets.FileUpload()
btn_upload
# Retrieving the uploaded image
img = PIL.Image.create(btn_upload.data[-1])
然后我们设置一个Output
小部件来显示上传的图像。
out_pl = widgets.Output()
out_pl.clear_output()
with out_pl: display(img.to_thumb(224,224))
out_pl
用户上传了一张 Talisker 18 威士忌图片。现在是时候再次测试我们建立的模型的分类能力了。
pred,pred_idx,probs = learn_inf.predict(img)lbl_pred = widgets.Label()
lbl_pred.value = f'Prediction: {pred}; Probability: {probs[pred_idx]:.04f}'
lbl_pred
从上面可以看出,我们的模型确实预测图像是威士忌的图像(概率为 99.65%)。我们现在可以继续构建我们的 web 应用程序,包括一个运行按钮,供用户单击并启动分类过程。
btn_run = widgets.Button(description='Classify Image')
btn_runButton(description='Classify Image', style=ButtonStyle())
然后我们设置一个回调,这样上面的按钮可以在点击时执行特定的功能。我们想要的是,每当用户为他/她上传的图像单击“分类图像”时,分类模型就会运行,然后生成分类预测。
def on_click_classify(change):
img = PIL.Image.create(btn_upload.data[-1])
out_pl.clear_output()
with out_pl: display(img.to_thumb(128,128))
pred,pred_idx,probs = learn_inf.predict(img)
lbl_pred.value = f'Prediction: {pred}; Probability: {probs[pred_idx]:.04f}'
btn_run.on_click(on_click_classify)
现在,将所有这些放在一个 VBox 中,这样小部件就可以很好地排列在我们笔记本中的垂直 web 应用程序模板中。
VBox([widgets.Label('Select your alcohol!'),
btn_upload, btn_run, out_pl, lbl_pred]
进一步的步骤
为了在笔记本之外部署这个应用程序,我们可以利用Voila
来创建一个真正独立的应用程序(基于 Jupyter 笔记本)。
由于这方面超出了本笔记本的范围,请随意在这里探索 Voila 的细节。
结论
至此,本教程到此结束。我们讨论了端到端的建模体验,从 fast.ai 和 Google Colab 设置,到数据摄取,一直到为我们构建的模型设置一个简单的 web 应用程序。
随着我继续 fast.ai 学习之旅,我将继续发布进一步的演练。同时,喝一杯的时间到了。干杯!
在你走之前
欢迎您**加入我的数据科学学习之旅!**点击此媒体页面,查看我的 GitHub ,了解更多令人兴奋的数据科学内容。
利用深度迁移学习对大众射手宣言进行分类
使用 TextCNN 和 ManifestoCorpus 的深度迁移学习教程
蒂姆·马德在 Unsplash 上的照片
简介
在美国,大规模枪击事件以惊人的速度发生。尽管存在与大规模枪击事件和公众反应间接相关的分类任务,但没有太多关注大规模枪击事件宣言本身。Mass shooter manifestos 的规模天生有限,这就是使用 TextCNN 等深度学习模型的迁移学习如何帮助正确分类小型数据集,如 ManifestoCorpus,这是一种为此目的制作的新数据集。Keras 发布了一个开发者指南,用于迁移学习和微调,供那些希望进一步了解最新实现的人使用。我在乌普萨拉大学的顶点项目的所有代码都可以在我的 GitHub 上找到,包括其他分类任务和模型。
迁移学习
NLP 有不同类型的迁移学习,可以根据源域和目标域是否完成相同的任务、源域和目标域的确切性质以及学习任务的精确顺序来粗略定义。任何迁移学习背后的直觉是,我们正在利用来自基础模型的知识来从受约束的数据集中获得洞察力。
出于 NLP 的目的,两个不同的领域朝着一个共同的任务努力,在源领域包含唯一的标记数据的情况下,领域适应是最常见的迁移学习类型。出于这里的目的,ManifestoCorpus 是由源数据集设置的标准手动标记的。在迁移学习中,通常还会改变基本模型,如层和参数,以适应再训练期间的特定目标数据集。这是通过在预训练期间冻结基本模型(即特征提取器)层和在再训练期间解冻微调模型来实现的。
数据
源数据 使用 Kaggle 提供的sensition 140 数据集作为源数据集。最初的数据集包括 160 万条推文,但我在训练中只包括了 16 万条推文。我将正面、负面和中性推文的标签二值化为非负面和负面的标签。
目标数据 ManifestoCorpus 包含 425 段大规模射手宣言和 425 段普通宣言。对于目标数据集,为每个分类任务手动标记文本,以最好地反映其源数据集的注释,交叉引用相关作品的人类注释者作为标记的动机。有二进制、多类和多标记数据集,但这里我们只看否定和非否定段落的二进制分类。
大众射手常用词
实现
首先,必须为基础模型准备好源数据集。我们在这里采取了一些额外的步骤来移除来自 Twitter 数据的杂波文本。
之后,我们为微调后的模型准备目标数据集。不像手动管理的数据那样需要清理。
我们将希望建立将在 TextCNN 的嵌入层中使用的单词嵌入。这里我们用手套。
现在,我们可以开始为一个为情感分析而设计的 CNN 构建基础和微调模型。实现遵循 Géron 的优秀动手机器学习中的迁移学习教程,使用 Scikit-Learn、Keras 和 TensorFlow。
重要的变化是我们选择在模型之间解冻哪些层。我们通过将感兴趣的层的可训练权重参数从假变为真来冻结和解冻层。我们解冻前四层,并在全连接层之前向微调模型添加几个卷积层。
最终评估是通过 Scikit-Learn 中的精度、召回率和 F1 的分类报告进行的。
结果
在我的发现中,阴性标签产生了 73%的准确率、66%的召回率和 69%的 F1,而非阴性标签产生了 71%的准确率、78%的召回率和 75%的 F1。非负面标签更容易分类和概括。这并不奇怪,因为非否定性文本的宽度比否定性文本宽。宣言和推文的领域可能太不一样了,也不适合更强的表现。ManifestoCorpus 也有其固有的偏见和局限性,但据我所知,它是唯一一个将 NLP 应用于大量射手文本的数据集。我们可以预期,伯特或 UMLFit⁴等最先进的语言模型将通过简单的软件包在迁移学习中取得更有前途的结果。自从这个顶点项目完成以来,NLP 的迁移学习在很短的时间内取得了很大的进展。请随意使用 ManifestoCorpus!
[1]:杰森·布朗利。(2017 年 12 月 20 日)。深度学习的迁移学习的温柔介绍https://machinelingmastery . com/Transfer-Learning-for-Deep-Learning/
[2]:奥雷连·盖伦。(2019 年 9 月)。使用 Scikit-Learn、Keras 和 TensorFlow 进行机器实践学习,第二版
[3]:威廉·法尔孔。(2019 年 11 月 25 日)。 Lit BERT: NLP 迁移学习 3 步走。https://towards data science . com/lit-Bert-NLP-transfer-learning-in-3-steps-272 a 866570 db
[4]: Akhilesh Ravi。(2019 年 11 月 23 日)。了解 ul mfit—NLP 中向迁移学习的转变*。*https://towards data science . com/understanding-ul mfit-and-elmo-the-shift-forward-transfer-learning-in-NLP-b 5d 8 e 2e 3 f 664
评估机器学习模型的性能
评估模型的最常见和最快速的方法之一
凯文·Ku 在 Unsplash 上的照片
有多种方法可以评估机器学习模型的表现。然而,本文将介绍一种最常见、最快速的方法来评估模型的性能,这种方法被称为 Precision 和 Recall。
分类模型结果
在我们进入精确和回忆之前,我们必须首先理解真正的阳性、真正的阴性、假阳性和假阴性结果分类的含义。
为了更好地理解这些结果分类,假设我们正在建立一个分类模型来确定患者是否患有癌症。在这种情况下,因为我们的模型的目的是试图预测患者是否患有癌症,所以如果患者患有癌症,则阳性结果将是,并且可以理解的是,如果患者没有患有癌症,则阴性结果将是。
真阳性
真阳性(TP)是模型正确预测的 阳性结果 。
在我们的例子中,这意味着被模型预测为患有癌症的患者确实患有癌症。
真正的否定
真阴性(TN)是模型正确预测的 阴性结果 。**
在我们的例子中,这意味着被预测为健康的患者确实没有患癌症。
假阳性
假阳性(FP)是模型错误预测的 阳性结果 。这也称为 I 型误差。
在我们的例子中,这意味着被预测患有癌症的患者实际上是健康的。
假阴性
假阴性(FN)是模型错误预测的阴性结果。这也被称为第二类错误。**
在我们的例子中,这意味着被预测为健康的患者实际上患有癌症。
精确度与召回率
尽管 TP、TN、FP 和 FN 值可以为数据科学家提供模型性能的一些指示,但很难在不参考其他值的情况下单独对每个值进行数值评估。这就是为什么数据科学家经常使用精度和召回来评估模型。**
精确
精确度是积极的(或相关的)结果的百分比。计算如下:
在我们的例子中,精确度是实际患有癌症并被检测出患有癌症的患者人数与预测患有癌症的患者人数的比例。
回忆
召回率是被正确识别的实际阳性的百分比。计算如下:
在我们的例子中,回忆将是实际患有癌症和被预测患有癌症的患者数量占实际患有癌症的患者总数的比例。
精确度和召回率
理想情况下,一个性能良好的模型应该产生高精度值和高召回值。
拥有高精度但低召回意味着虽然模型擅长预测正面类,但它只检测到正面结果总数的一小部分。在我们的例子中,这将意味着被预测患有癌症的患者最有可能患有癌症,但是在所有患有癌症的患者中,该模型仅预测其中一小部分患有癌症。因此,该模型低估了*。***
具有低精度但是高召回意味着尽管模型正确地预测了大多数肯定的情况,但是它也预测了许多否定也是肯定的。在我们的例子中,这意味着在所有实际患有癌症的患者中,大多数被正确地预测为患有癌症,也有许多没有患癌症的患者被模型预测为患有癌症。所以模型是过预测*。***
结论
这是一个评估模型的简单方法的快速介绍。这些值收集和计算起来真的很快,并且可以给出一个模型表现如何的估计。
对网上商店顾客进行分类
不同机器学习分类器的可视化方法
作者图片
在这篇文章中,我想展示如何使用不同的机器学习方法,利用在线商店的跟踪数据将客户分为购买和不购买。利用从原始数据中汇总的特征,例如访问次数和页面浏览次数,可以训练和可视化预测模型。
特别注意借助于二维图和决策边界的着色的预测模型的视觉呈现。不同方法的特点以及模型调整不足和过度的情况变得很明显。为了使可视化成为可能,我考虑只有两个特征的非常简化的模型。
数据导入
让我们从 CSV 文件加载原始数据。这些来自 Kaggle 数据集https://www . ka ggle . com/mkechinov/ecommerce-behavior-data-from-multi-category-store。这与我在关于分段和可视化的文章中使用的数据源相同:
https://towards data science . com/segmentation-of-online-shop-customers-8c 304 a2 d 84 b 4
为了演示和分析,我只使用了一部分数据。
跟踪网上商店的数据
在线商店的产品页面上有 1,000,000 条数据记录,具有产品类别、品牌、价格、用户会话 id 等特征。
为了进一步处理数据,我们形成了一个数据框架,该数据框架聚集了各个匿名访问者的数据,并计算了在线商店的页面浏览量和访问量。此外,为每个访问者计算购买物品的数量,从而创建一个标签,无论该顾客是否是购买者。
为了简化下面的演示,从分析中删除了一些访问者的数据。
网上商店用户的汇总数据
这为我们提供了来自 121 个网站访问者的数据。
数据的图形表示
在我们开始分析之前,让我们来看一个访问者的二维图,它使用访问次数和页面浏览量作为坐标轴。是否是买家由颜色代码表示。图书馆“Plotly”(【https://plotly.com/】)是用来搞阴谋的。
网店用户的特征空间/作者提供的图片
红点和绿点大多集中在这个“特征空间”的某些区域。在下文中,我们尝试创建分类模型,根据访问者在特征空间中的位置,正确地将他们识别为购买者。
为了能够可视化模型和它们的预测,我们需要一个辅助函数,它也可以在二维图中绘制决策边界。训练完模型后,该函数对一个狭窄的点网格执行分类预测,然后使用此信息根据预测对区域进行着色。
按会话和页面浏览量对访问者进行分类
对于分类模型的实现,我们使用库“Scikit Learn”(https://scikit-learn.org/)。我们考虑不同的方法,包括我们的可视化功能的参数。
朴素贝叶斯分类
贝叶斯分类器是从贝叶斯定理导出的分类器。它将每个对象分配给它最可能所属的类。形式上,它是一个数学函数,将一个类分配给特征空间中的每个点。
朴素贝叶斯分类器因其快速的可计算性而非常流行。天真的基本假设是属性是独立的。在我们的例子中,这并没有实现,页面浏览的数量取决于访问的数量。
如下图所示,结果的质量也一般。分类器只是粗略地划分了红点和绿点的区域。很多点都在错误的区域。
作者使用朴素贝叶斯/图像进行分类
关于朴素贝叶斯的更多信息:
[## 第 1 章:监督学习和朴素贝叶斯分类—第 1 部分(理论)
欢迎来到监督学习的踏脚石。我们首先讨论一个小场景,它将构成…
medium.com](https://medium.com/machine-learning-101/chapter-1-supervised-learning-and-naive-bayes-classification-part-1-theory-8b9e361897d5)
支持向量机分类
我们从线性支持向量分类开始。对于线性内核,最佳可能分离由直线实现。在图中,您可以看到直线是如何放置的,以保持到分类边界附近的点的距离尽可能大。这种线性模型定义复杂区域的可能性自然是有限的。
作者用 SVM /图像分类
关于支持向量机的更多信息:
机器学习被认为是人工智能的一个分支,它与人工智能的发展密切相关
towardsdatascience.com](/a-friendly-introduction-to-support-vector-machines-svm-925b68c5a079)
在具有多项式核的变体中,多项式被使用到指定的次数作为分离。该图显示了高达 4 次多项式的使用,这使得该方法有更多的可能性来适应数据。
作者用多项式 SVM /图像分类
如果程度设置得高,则可以在特征空间中定义更复杂的区域。具有混合点的区域仍然没有被更好地解决。然而,随着多项式次数的增加,有过度拟合的危险。
作者用多项式 SVM-10 次/图像进行分类
神经元网络
有了神经网络,可以定义任何复杂的功能和区域。这是由层数和神经元数定义的。在该图中,使用了具有 3 层,每层 25 个神经元的神经网络。
作者用神经网络/图像分类
关于神经网络分类的更多信息,请参见:
Iris flowers 数据集是分类文献中最著名的数据集之一。目标是…
medium.com](https://medium.com/gadictos/iris-data-classification-using-neural-net-335d3303abd8)
决策树
根据允许的最大深度,决策树可以形成或多或少复杂的分类区域。然而,总是仅在轴方向上具有决策限制。这导致区域呈阶梯状。
作者用决策树/图片分类
在深度很深的地方,决策树很快就会过度适应。这可以在图中通过窄条纹清楚地看到,每个窄条纹仅分类一个数据点。
有关决策树的更多信息,请访问:
决策树是一类非常强大的机器学习模型,可以在许多任务中实现高精度,同时…
towardsdatascience.com](/a-guide-to-decision-trees-for-machine-learning-and-data-science-fe2607241956)
随机森林——决策树的集合
如果不使用单个决策树,而是使用多个决策树,然后让大多数单个分类器来决定整体分类,我们就称之为随机森林。这些具有决策树的一些优点,例如简单性和良好的可解释性,但是它们可以更灵活地划分分类区域,并且不会很快过度适应。
作者用随机森林/图像分类
该图显示了非常灵活的决策限制,但也显示了过度适应的方法。
有关随机森林的更多信息,请访问:
机器学习从业者有喜欢的算法是很常见的。这有点不合理,因为没有…
medium.com](https://medium.com/rants-on-machine-learning/the-unreasonable-effectiveness-of-random-forests-f33c3ce28883)
使用这个只有两个特征的买家分类的例子,我们直观地展示了一些非常普遍的分类方法的典型属性。这有助于发展对方法的直觉,并更好地评估风险,如过度适应。
通过图像分析对帕金森病进行分类:第一部分
应用计算机视觉
预处理和探索性数据分析
我简介
阿金森氏病通常伴有震颤和僵硬等运动障碍症状。这些会对患有早期疾病的人的书写和素描产生明显的影响[1]。缩微图是一个人的笔迹中异常小的波动,然而,由于一个人发展的笔迹、语言、熟练程度和教育等的可变性,已经声称难以解释[1]。因此,2017 年进行的一项研究旨在通过使用螺旋和波浪的标准化分析来改善诊断。在这一系列的帖子中,我们将分析该研究中收集的原始图像,看看我们是否可以为一名帕金森患者创建一个分类器,并在此过程中得出一些结论。我们将使用的数据托管在 Kaggle [2]上,特别感谢 Kevin Mader 分享数据集上传。
图片来自作者。我们将使用的数据的样本图像。
在第 1 部分中,我们将进行一些探索性的数据分析,并对图像进行预处理,以创建一些有望有助于分类的特征。我选择不使用卷积神经网络(CNN)来简单地对图像进行分类,因为这将是一个黑盒——没有任何衡量曲线/草图之间潜在差异的指标。相反,我们不是简单地执行分类任务,而是试图使用图像处理来理解和量化差异。在随后的帖子中,我将与 CNN 进行比较。
来自吉菲
在我们开始之前,声明这并不意味着任何形式的医学研究或测试。请参考原始论文了解实际实验的细节,我并没有参与其中。
Zham P,Kumar DK,Dabnichki P,Poosapadi Arjunan S,Raghav S. 利用画螺旋的速度和笔压综合指数区分帕金森病的不同阶段。前神经元。2017;8:435.发布于 2017 年 9 月 6 日。doi:10.3389/fneur.2017.00435
探索性数据分析
首先,让我们看一下图像,执行一些基本的分割,并开始探索一些感兴趣的潜在特征。我们将全程使用熊猫来存储图像和信息。对于那些质疑你是否会阅读这一部分的人,这里是我们将要进入的内容:
-阈值处理和清理
-通过最近邻的厚度量化
-骨架化
-交叉点和边缘点
阈值处理和清洗
我们使用一个修改的读取和阈值函数,主要是从 Kevin Mader 在 Kaggle 上的原始笔记本中获得的[2]。在这里,当我们想要查看蒙太奇风格的图像时,可以选择调整大小,如上图和下图所示。我们首先读入并反转图像,使图形在黑色背景上是白色的,如果需要还可以调整大小。我们还应用了一个小的中值滤波器。
这个项目有相当多的代码,我不会把它们都放在这里,所以如果你想查看笔记本或 python 脚本以了解更多细节,请查看 github 链接。
from skimage.io import imread
from skimage.util import montage as montage2d
from skimage.filters import threshold_yen as thresh_func
from skimage.filters import median
from skimage.morphology import disk
import numpy as npdef process_imread(in_path, resize=True):
"""read images, invert and scale them"""
c_img = 1.0-imread(in_path, as_gray=True)
max_dim = np.max(c_img.shape)
if not resize:
return c_img
if c_img.shape==(256, 256):
return c_img
if max_dim>256:
big_dim = 512
else:
big_dim = 256
""" pad with zeros and center image, sizing to either 256 or 512"""
out_img = np.zeros((big_dim, big_dim), dtype='float32')
c_offset = (big_dim-c_img.shape[0])//2
d_offset = c_img.shape[0]+c_offset
e_offset = (big_dim-c_img.shape[1])//2
f_offset = c_img.shape[1]+e_offset
out_img[c_offset:d_offset, e_offset:f_offset] = c_img[:(d_offset-c_offset), :(f_offset-e_offset)]
return out_imgdef read_and_thresh(in_path, resize=True):
c_img = process_imread(in_path, resize=resize)
c_img = (255*c_img).clip(0, 255).astype('uint8')
c_img = median(c_img, disk(1))
c_thresh = thresh_func(c_img)
return c_img>c_thresh
最后,对于读入的内容,我们还通过移除任何与主草图无关的小对象来清理图像。
from skimage.morphology import label as sk_labeldef label_sort(in_img, cutoff=0.01):
total_cnt = np.sum(in_img>0)
lab_img = sk_label(in_img)
new_image = np.zeros_like(lab_img)
remap_index = []
for k in np.unique(lab_img[lab_img>0]):
cnt = np.sum(lab_img==k) # get area of labelled object
if cnt>total_cnt*cutoff:
remap_index+=[(k, cnt)]
sorted_index = sorted(remap_index, key=lambda x: -x[1]) # reverse sort - largest is first
for new_idx, (old_idx, idx_count) in enumerate(sorted_index, 1): #enumerate starting at id 1
new_image[lab_img==old_idx] = new_idx
return new_image
这是通过仅保留大于 1%的激活像素的足够大的分量来实现的;由截止值定义。首先标记图像中的每个单独的对象,并将每个标记的面积相加(不是 0)。如果计数超过总数的 1%,则保留索引。执行负排序,使最大的对象具有标签 1。用新的订购 id 替换旧的标签号。
图片来自作者。阈值处理和清理后的数据样本图像。
作为绘图差异的初始视图,我们可以创建一个骨架图像并形成一个新的数据框,其中每一行都是每个图像中非零像素的单个像素坐标。然后,我们可以将这些曲线中的每一条绘制在一个图表上——在归一化位置之后。我们不会使用这种格式的数据框,这只是为了可视化。
图片来自作者。一个地块上的所有图纸。
正如我们所看到的,健康的草图之间有很强的一致性。考虑到帕金森症状可能导致的随机运动,这是有意义的。
厚度量化
N ext,我们将尝试量化厚度。为此,我们将使用距离图来给出图纸宽度的近似值。中轴也返回一个距离图,但是骨骼化更干净,因为它做了一些修剪。
from skimage.morphology import medial_axis
from skimage.morphology import skeletonizedef stroke_thickness_img(in_img):
skel, distance = medial_axis(in_img, return_distance=True)
skeleton = skeletonize(in_img)
# Distance to the background for pixels of the skeleton
return distance * skeleton
图片来自作者。线条粗细计算。
通过绘制平均值和标准偏差,我们看到了这些图之间的一些相关性。主要是在标准差中,考虑到随机影响,这也是有意义的,事实上它是巨大的,并不小于健康也是有意义的。
图片来自作者。图纸的厚度。
交叉点和边缘点
D 由于图像骨架化的工作方式,根据线条的“平滑度”,更多起伏的曲线会有更多的端点。因此,与平滑线相比,这些是随机运动的一些度量。另外,我们可以计算交点的数量;完美的曲线没有交点,只有两个端点。这些在其他图像处理应用中也是有用的,例如道路绘图。
我不会在这里过多地讨论代码,因为需要进行相当多的清理,但是,我会尝试使用一些图像来解释我所做的事情。首先,我们计算曲线骨架的最近邻图像。除了在边缘点的值为 1 和在交叉点的值为 3 之外,这给了我们在任何地方的值为 2;这是在使用连通性 2(8 个最近的邻居)时。这是放大后的结果图像。
图片来自作者。交点和边缘点。
正如你所看到的,这和预期的一样,除了我们遇到了一些问题,如果我们想得到一个像素交叉点来正确量化交叉点的数量。右边的图像有三个值为 3 的像素,尽管这只是一个交集。当这些交点的总和大于“正确的”交点时,我们可以通过隔离这些区域,用下面的伪算法来清理这个问题。我们可以将最近邻(NN)图像和阈值相加来隔离它们。连通性 1 有直接邻居,连通 2 包括对角线。
- 从原始神经网络,分别使用连接 1 和 2 求和。
- 隔离连通性 2 中值≥ 8 的交叉点。
- 标记连接到交叉点像素的每条边。
- 对于连通性 1 图像,隔离总和在 3 和 5 之间的交叉点像素。这些是我们不想要的。
- 覆盖不正确的交叉点像素。
结果如下:
图片来自作者。交叉点和边缘点,已更正。
如你所见,我们现在在交叉点位置有一个值为 3 的像素。我们现在可以简单地将这些位置相加,以量化交点的数量。如果我们把这些画在一条曲线上,我们可以看到结果,黄色下面是交叉点,绿色是边缘点。
图片来自作者。绘制了交点和边缘点的骨架曲线。
如果我们画出每种绘画类型的数量,我们可以看到相当强的相关性:
图片来自作者。平均边缘点数。
图片来自作者。平均相交数。
我们还可以看到,我们对具有大约 2 个边缘点的健康曲线的初始估计也是正确的。帕金森波图有非常多的边缘点,因为这些通常非常“尖锐”而不是平滑地弯曲,这在这些波的尖端产生了大量的边缘点。
最后,我们还可以检查骨架图像中像素总数的相关性。这与图纸的“间接”性质有关。这种量化非常简单,我们只需对骨架图像中大于零的像素求和。
图片来自作者。平均像素数。
没有一段感情那么牢固,但还是有意义的。
摘要
因此到目前为止,我们已经读入、清理并获得了一些潜在有用的指标,这些指标不仅有助于我们了解缩微程度,还可以用作分类器(如逻辑回归或随机森林)的输入。在本系列的第 2 部分中,我们将使用这些度量标准进行分类,并与一个更强大的黑盒神经网络进行比较。使用随机森林的优势在于,我们还可以看到哪些功能对模型的影响最大。根据以上观察,你是否已经有了直觉,知道哪些特性可能是最重要的?
如果你觉得这篇文章的任何部分提供了一些有用的信息或一点灵感,请关注我。
你可以在我的 github 上找到源代码。该项目目前仍在建设中。
链接到我的其他帖子:
- 计算机视觉和终极 Pong AI——使用 Python 和 OpenCV 在线玩 Pong
- 《我的世界》测绘仪 —计算机视觉和光学字符识别从截图和绘图中抓取位置
奖金
这里不使用,但为了好玩,我们也可以创建一个图形来表示每个图像,因为我们有节点和边,其中节点是交叉点或边点,边是连接这些点的绘图部分。
图片来自作者。网络图的绘制。
我们为此使用了 Networkx 库,其中每个数字要么是一个节点,要么是交叉点,其颜色对应于连接节点的绘图部分的长度。
参考
[1] Zham P,Kumar DK,Dabnichki P,Poosapadi Arjunan S,Raghav S. 利用画螺旋的速度和笔压综合指数区分帕金森病的不同阶段。前神经元。2017;8:435.发布于 2017 年 9 月 6 日。doi:10.3389/fneur . 2017.00435
【2】Mader,K. 帕金森素描概述。https://www.kaggle.com/kmader/parkinsons-drawings。获取日期:2020 年 7 月 9 日
通过图像分析对帕金森病进行分类:第二部分
应用计算机视觉
比较随机森林、ResNet50 特征提取和定制卷积神经网络
在我之前的帖子中,我概述了一些手动获取的特征,如交叉点和端点的数量、线条粗细、标准偏差等。在这篇文章中,我们将应用这些特征,尝试将两组图画分为健康型和帕金森型。此外,我们还将使用更高级的机器学习方法来研究其他两种分类方法:即结合逻辑回归的特征提取;以及定制的、从零开始训练的卷积模型。
在我们开始之前,声明这并不意味着任何形式的医学研究或测试。关于实际实验的细节请参考的原始论文,我并没有参与其中。
Zham P,Kumar DK,Dabnichki P,Poosapadi Arjunan S,Raghav S. 利用画螺旋的速度和笔压的综合指数区分帕金森病的不同阶段。前神经元。2017;8:435.发布于 2017 年 9 月 6 日。doi:10.3389/fneur.2017.00435。
在做这样一个项目时,重要的是要决定目标是什么?这里有两个:
1。了解波形和螺旋图像的草图类型(健康或帕金森)之间的可观察差异。
2。创建一个高精度的分类器来确定患者是否可能患有帕金森氏症。
正如我在以前的帖子中提到的,直接进入神经网络模型的一个限制是,它在很大程度上是一个黑盒分类器。我们失去了对帕金森氏症引起的绘画中的根本差异的理解。这就是我们的第一个分类器被使用的地方,它实际上同时针对这两个目标。我们将从那里开始。
来自吉菲
来自手工构建特征的随机森林分类器
这一部分的标题说明了一切。我在上一篇文章中对获得的特征使用了随机森林分类器。显而易见,我们将使用的特征是:
-【平均厚度】;标准厚度’;number _ pixels ‘;数字 _ 边缘点’;“数量 _ 交叉点”。
我发现的一个技巧是通过创建交互特征来合并一些非线性,其中每个特征与每个其他特征相乘。例如:均值 _ 厚度*数量 _ 交点。这将特征的数量增加到 15 个。
我们还必须记住标准化每个特性。这使得平均值为 0。本质上,我们减去平均值,然后除以每个特征列的标准偏差。
最后,我们必须为我们的目标类创建一个一次性编码。这只是一个二元分类,所以这就足够了。这将把我们的“健康”或“帕金森”标签转换为 0 或 1。我们还确保将培训数据与测试数据分开。
就是这样!我们可以将我们的特征列传递给一个随机的森林分类器。这里还需要注意的是,我们需要波浪和螺旋数据集的独立模型。一个明显但重要的注意事项。以下是 wave 的结果:
测试的波形准确度:80%
+--------------+-----------+----------+-------------+---------+
| Wave | precision | recall | f1-score | support |
+--------------+-----------+----------+-------------+---------+
| healthy | 0.74 | 0.93 | 0.82 | 15 |
| parkinson | 0.91 | 0.67 | 0.77 | 15 |
| | | | | |
| accuracy | | | 0.80 | 30 |
| macro avg | 0.82 | 0.80 | 0.80 | 30 |
| weighted avg | 0.82 | 0.80 | 0.80 | 30 |
+--------------+-----------+----------+-------------+---------+
随机森林分类器提供了许多很好的优势,我在这里使用的主要优势是能够给出最有影响力的特征列表。以下是它们出现的顺序:
Feature impact (in order of importance):
['std_thickness_num_ep' 'std_thickness' 'num_ep_num_inters'
'num_pixels_num_ep' 'num_pixels_num_inters' 'mean_thickness_num_inters'
'mean_thickness_num_ep' 'std_thickness_num_inters'
'std_thickness_num_pixels' 'mean_thickness' 'num_ep' 'num_inters'
'num_pixels' 'mean_thickness_std_thickness' 'mean_thickness_num_pixels']
对于螺旋:
测试的螺旋精度:67%
+--------------+-----------+----------+-------------+---------+
| Spiral | precision | recall | f1-score | support |
+--------------+-----------+----------+-------------+---------+
| healthy | 0.63 | 0.80 | 0.71 | 15 |
| parkinson | 0.73 | 0.53 | 0.62 | 15 |
| | | | | |
| accuracy | | | 0.67 | 30 |
| macro avg | 0.68 | 0.67 | 0.66 | 30 |
| weighted avg | 0.68 | 0.67 | 0.66 | 30 |
+--------------+-----------+----------+-------------+---------+Feature impact (in order of importance): ['num_pixels_num_ep' 'num_pixels' 'std_thickness_num_ep'
'num_pixels_num_inters' 'std_thickness_num_pixels'
'mean_thickness_num_ep' 'mean_thickness' 'std_thickness_num_inters'
'std_thickness' 'mean_thickness_std_thickness'
'mean_thickness_num_pixels' 'mean_thickness_num_inters'
'num_ep_num_inters' 'num_ep' 'num_inters']
在我的上一篇文章中,我问,“根据我们看到的结果,我们认为哪个是最重要的?”。机器学习的一个优势是,我们可以分配这些非线性的相互作用,这可能是我们难以预测的。此外,我们可以看到每种类型的绘画的影响是不同的。在波动集上,准确率还不错,为 80%,但在螺旋集上,准确率很低,为 67%——仍然比机会集(50%)好。显然,可以对特征进行一些微调,从而使螺旋集达到更好的精度。然而,使用随机森林,我们现在可以观察到绘图的某些部分有多重要。当然,通过端点和像素数识别的波纹在波浪和螺旋模型中都很突出。
用 ResNet50 进行特征提取,用逻辑回归进行分类
在下一节中,我们将首先通过在 ImageNet 上训练的 ResNet50 模型传递我们的图像,但保留图层的顶部。我们可以使用 tf.keras 轻松做到这一点:
model = ResNet50(weights="imagenet", include_top = False)
当一幅图像通过这个时,它将产生 2048 * 7 * 7 的“特征”——对图像进行分类的数字。这是 ResNet50 最后一层的数字。然后,我们可以通过逻辑回归来传递这个大向量,以获得分类——这是一种非常快速有效的方法,可以获得相当不错的准确性,而无需进行任何真正的训练。一个重要的注意事项是,您必须首先通过一些预处理来将图像转换成合适的格式。这个型号需要的尺寸是 224 x 224。张量形状应该是(N,224,224,3),其中 N 是样本数。有一个预定义的前置函数,用 imagenet _ utils . preprocess _ input 减去均值。值得一提的是,我从 Adrian Rosebrock 的《计算机视觉深度学习实践者》一书中学到了这项技术。
bs = 16
# loop in batches
for i in np.arange(0, len(imagePaths), bs):
batchPaths = imagePaths[i:i + bs]
batchLabels = labels[i:i + bs]
batchImages = []
# preprocess each image
for j, imagePath in enumerate(batchPaths):
image = load_img(imagePath, target_size=(224, 224), interpolation='bilinear')
image = img_to_array(image)
# expand dims and subtract mean RGB
image = np.expand_dims(image, axis=0)
image = imagenet_utils.preprocess_input(image)
if j==0 and i==0:
fig, axs = plt.subplots()
plt.imshow(image.reshape((224,224,3)).astype(np.uint8),clim=(0,255), interpolation=None)
plt.show()
batchImages.append(image)
batchImages = np.vstack(batchImages)
# extract features
features = model.predict(batchImages, batch_size=bs)
features = features.reshape((features.shape[0], 100352))
一旦我们将我们的特征和相关标签存储在数据帧中,我们就可以将它们传递给逻辑回归。本质上,这里的想法是,在图像的这个非常高的维度表示中,应该有一个清晰的划分。我认为这是正确的,因为它很精确:
测试的波形准确度:87%
+--------------+-----------+----------+-------------+---------+
| Wave | precision | recall | f1-score | support |
+--------------+-----------+----------+-------------+---------+
| healthy | 0.92 | 0.80 | 0.86 | 15 |
| parkinson | 0.82 | 0.93 | 0.87 | 15 |
| | | | | |
| accuracy | | | 0.87 | 30 |
| macro avg | 0.87 | 0.87 | 0.87 | 30 |
| weighted avg | 0.87 | 0.87 | 0.87 | 30 |
+--------------+-----------+----------+-------------+---------+
测试的螺旋精度:83%
+--------------+-----------+----------+-------------+---------+
| Wave | precision | recall | f1-score | support |
+--------------+-----------+----------+-------------+---------+
| healthy | 0.81 | 0.87 | 0.84 | 15 |
| parkinson | 0.86 | 0.80 | 0.83 | 15 |
| | | | | |
| accuracy | | | 0.83 | 30 |
| macro avg | 0.83 | 0.83 | 0.83 | 30 |
| weighted avg | 0.83 | 0.83 | 0.83 | 30 |
+--------------+-----------+----------+-------------+---------+
与我们手动创建的只有 15 个特征的模型相比,这是一个明显的改进,也是需要最少努力的一个——但是除了分类之外,我们也没有获得太多信息。
定制卷积神经网络
在我们的最后一种方法中,我们从头开始构建一个定制模型并进行训练。我们只有很少的训练样本——只有 72 个,所以我们使用增强。在训练期间,我创建了一个占训练集 20%的验证集,并对迄今为止我一直在比较的 30 幅未接触过的图像进行了预测。
下面是螺旋模型的总结。我将图像缩减为原来的 2 倍,并转换成灰度。波浪模型完全相同,但图像大小不同。我还为对比度拉伸和标准化应用了一个预处理函数。有关增强、回调等的详细信息。请参考我的代码,因为这超出了本文的范围。优化是使用 Adam 完成的。
Model: "SpiralNet"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 128, 128, 32) 320
_________________________________________________________________
activation (Activation) (None, 128, 128, 32) 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 128, 128, 32) 9248
_________________________________________________________________
activation_1 (Activation) (None, 128, 128, 32) 0
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 64, 64, 32) 0
_________________________________________________________________
dropout (Dropout) (None, 64, 64, 32) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 64, 64, 64) 18496
_________________________________________________________________
activation_2 (Activation) (None, 64, 64, 64) 0
_________________________________________________________________
conv2d_3 (Conv2D) (None, 64, 64, 64) 36928
_________________________________________________________________
activation_3 (Activation) (None, 64, 64, 64) 0
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 32, 32, 64) 0
_________________________________________________________________
dropout_1 (Dropout) (None, 32, 32, 64) 0
_________________________________________________________________
conv2d_4 (Conv2D) (None, 32, 32, 128) 73856
_________________________________________________________________
activation_4 (Activation) (None, 32, 32, 128) 0
_________________________________________________________________
conv2d_5 (Conv2D) (None, 32, 32, 128) 147584
_________________________________________________________________
activation_5 (Activation) (None, 32, 32, 128) 0
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 16, 16, 128) 0
_________________________________________________________________
dropout_2 (Dropout) (None, 16, 16, 128) 0
_________________________________________________________________
flatten (Flatten) (None, 32768) 0
_________________________________________________________________
dense (Dense) (None, 128) 4194432
_________________________________________________________________
activation_6 (Activation) (None, 128) 0
_________________________________________________________________
dropout_3 (Dropout) (None, 128) 0
_________________________________________________________________
dense_1 (Dense) (None, 1) 129
_________________________________________________________________
activation_7 (Activation) (None, 1) 0
=================================================================
Total params: 4,480,993
Trainable params: 4,480,993
Non-trainable params: 0
_________________________________________________________________
这是训练板:
来自作者。每个模型的训练和验证曲线。
最后,指标:
测试的波形准确度:87%
+--------------+-----------+----------+-------------+---------+
| WaveNet | precision | recall | f1-score | support |
+--------------+-----------+----------+-------------+---------+
| healthy | 0.92 | 0.80 | 0.86 | 15 |
| parkinson | 0.82 | 0.93 | 0.87 | 15 |
| | | | | |
| accuracy | | | 0.87 | 30 |
| macro avg | 0.87 | 0.87 | 0.87 | 30 |
| weighted avg | 0.87 | 0.87 | 0.87 | 30 |
+--------------+-----------+----------+-------------+---------+
测试中的螺旋精度:90%
+--------------+-----------+----------+-------------+---------+
| SpiralNet | precision | recall | f1-score | support |
+--------------+-----------+----------+-------------+---------+
| healthy | 0.93 | 0.87 | 0.90 | 15 |
| parkinson | 0.88 | 0.93 | 0.90 | 15 |
| | | | | |
| accuracy | | | 0.90 | 30 |
| macro avg | 0.90 | 0.90 | 0.90 | 30 |
| weighted avg | 0.90 | 0.90 | 0.90 | 30 |
+--------------+-----------+----------+-------------+---------+
虽然我们在波集上实现了与 ResNet50 特征提取相同的精度,但是我们在螺旋集上获得了更好的改进。我们的训练集中也只有 30 幅图像,非常小。随着数字的增加,我们可以看到两者的验证精度都更接近,最佳模型的验证精度都在 95%左右。
摘要
下面是对不同方法的相同测试集的结果的最终总结:
+------------------------+------+--------+
| Test Accuracy % | Wave | Spiral |
+------------------------+------+--------+
| Random Forest Manual | 80 | 67 |
| ResNet50 feat. Log Reg | 87 | 83 |
| Custom Nets | 87 | 90 |
+------------------------+------+--------+
看着这些图片,我对它们的表现很满意,有些图片我看不出有什么不同…一些更多的功能工程可能会影响随机森林手动模型,但我会把这个交给你。
来自吉菲
如果你觉得这篇文章的任何部分提供了一些有用的信息或一点灵感,请关注我。
你可以在我的 Github 上找到源代码。
链接到我的另一篇文章:
- 通过图像分析对帕金森病进行分类:第一部分 —预处理和探索性数据分析
基于通用句子嵌入的科技论文分类
如何教计算机区分天体物理学和计算机科学?
几年来,由于计算机视觉的惊人进步,计算机已经获得了几乎像人类一样的识别建筑物、动物和其他物体的能力。使这成为可能的突破是理论直觉(即上世纪 80 年代卷积神经网络的引入)和硬件(得益于 GPU 卡的广泛和经济高效的可用性)的结合,这使得在合理的时间内执行复杂的算法成为可能。这一类别的缩影可能是 VGG 类的模型,即公开可用的部署,因为大多数预先训练的模型。像它的大多数兄弟一样, VGG -19 需要训练一个强大的计算机集群,但是一旦初始阶段完成,它可以部署在不太强大的机器上,在许多情况下甚至是移动设备上(例如参见 MobileNet )。然而,虽然计算机视觉领域在相当长的一段时间内一直处于黄金时代,但对于另一项任务:书面文本的解释来说,情况并非如此。
现在,计算机非常善于识别常见的物体。但是他们足够好去阅读一篇科学论文吗,更不用说去理解它的意思了?
可以说,如果人类不能在为时已晚之前识别出狮子、鬣狗和其他食肉动物的威胁,他们就不会在生物进化的早期阶段幸存下来。另一方面,人类学家认为,随着我们语言能力的提高,人类社会变得越来越复杂。虽然语言最初是如何成为一种东西的还没有被完全理解,但是很明显,交流基本信息并不需要一种具有语法的完全成熟的语言。这个概念由诺姆·乔姆斯基在他的语法等级理论中进行了著名的阐述。因此,期望机器能够理解人类语言,或者至少通过口头交流的方式与我们互动,这并不太遥远,尽管在开始时是有限的。长话短说,我们正在努力,但人类语言的复杂性是一个非常难啃的骨头。最著名的问题可能是共指消解、即寻找文本中引用同一实体的所有表达式的任务。拿下面这句话来说:
“我让一个铁球滚下斜坡,当它碰到一扇玻璃窗时,就碎了。”
任何人都可以看出,第一个代词 it 指的是球,而第二个代词指的是玻璃,反之亦然。从语法上来说,这两种选择都是完全正确的,但是物理世界的知识和一些直觉引导我们做出正确的匹配。这对机器来说绝非小事。当前的范例需要在大量文本(称为语料库)上训练大型神经网络模型,从这些文本中估计单词之间关联的概率( n -grams)。一旦模型被训练,它随后被微调到某个任务,例如 问答,语言生成,命名实体识别。在下文中,我们将关注一个相对简单的任务:文档分类。我们将训练一台机器根据摘要来识别给定科学论文的类别。
文本嵌入
虽然用计算机可以处理的数字对图像进行编码非常简单(即通过将其像素表示为矩阵),但对文本来说却不是这样。其实不同的可能性都是存在的,最好的大多取决于任务本身。最基本的方法包括创建一个字典,,即语料库中出现的所有单词的索引集合。然后,给定的文本由一个向量表示,其中每个元素都是相应单词的索引。
*dictionary = ["apple", "banana", "zucchini"]"banana banana apple" -> [1, 1, 0]
"zucchini apple apple" -> [2, 1, 1]*
这种方法的问题是向量的长度是可变的(事实上相当于文本中的字数),这对于数字处理来说不是一个理想的特性。另一种常见的选择是将文本表示为矩阵,其中每行代表一个单词。反过来,每个单词由一个固定长度的二进制向量表示,其中除了唯一指向字典中相应单词的一个元素之外,所有元素都为零,例如:
*[1,0,0] -> "apple"
[0,1,0] -> "banana"
[0,0,1] -> "zucchini"*
这种方法叫做单热向量*,也不是特别适合,因为它需要大量内存,这基本上是浪费了,因为大多数元素都是零。这个问题的常见解决方案是使用稀疏矩阵来代替。*
近年来,人们已经意识到,更好的解决方案是将每个单词表示为固定长度的密集向量,例如:
*[0.1, -0.1] -> "apple"
[0.3, 0.1] -> "banana"
[-0.2, 0.1] -> "zucchini"*
最明显的优势是编码每个单词需要更少的维度。字典基本上变成了单词向量的查找表*。然而,真正的交易是将语义意义附加到嵌入空间的每个维度的能力。最辉煌的例子如下:给定四个代表国王*、王后、男性、女性的向量,人们发现:**
*king - male + female = queen*
或者类似地:
*france - paris + rome = italy*
这是如何实现的超出了本文的范围,但足以说明的是,一个神经网络( word2vec 或 skip-gram 模型)是在一个语料库上训练的,以预测最可能的上下文,即给定单词之后的单词,或者两个单词更有可能在给定单词之前和之后找到。
在最近,这种和其他类似的技巧已经被用来通过爬行数百万网页(包括维基百科的完整转储)来训练巨大的网络,如伯特和 GPT-2 。在这一点上,语言处理网络正在做着与计算机视觉网络相同的事情:如果不能访问庞大的样本数据库,它们的训练就不可能进行,而这反过来只有在互联网出现后才成为可能。
最后,对我们的目标来说最重要的是,一个类似的网络已经通过不仅考虑单个单词,而且考虑句子和短文段而得到训练。我们将部署由一个名为通用句子编码器 (USE)的预训练网络创建的嵌入向量,用数字表示文档,并应用一些简单的分类技术。
文件分类:微调神经网络
有了句子嵌入,我们现在可以把注意力转向实际的分类任务。对于这个例子,我们将通过下载 arXiv 服务器上出现的预印本摘要来创建一个用于训练/测试的小型数据库。关于如何下载这些文件的很好的介绍可以在这里找到。
将摘要保存在数据帧中后,我们可以稍后加载该文件,并使用带标签的信息来训练一个简单的网络。
请注意代码的以下部分:
- 输入由作为文本存储的摘要序列组成。然后,输入被传递到第一层(UniversalEmbedding ),该层本质上是预训练的深度网络。其输出被传递到一个密集层,激活 softmax 来预测类别
- 为了支持多个类别,输出是一个向量,其中每个元素对应一个给定的类。激活 softmax 使输出标准化,以便可以根据输入属于给定类别的概率进行解释。为了一致性,损失函数是分类交叉熵*。*
*There are 2 known categories: ['cs.AI', 'astro-ph']
Training set has 14550 samples
Testing set has 4850 samplesModel: "AbstractClassifier"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
text_in (InputLayer) [(None, 1)] 0
_________________________________________________________________
lambda (Lambda) (None, 512) 0
_________________________________________________________________
dense (Dense) (None, 16) 8208
_________________________________________________________________
dense_1 (Dense) (None, 2) 34
=================================================================Total params: 8,242
Trainable params: 8,242
Non-trainable params: 0
_________________________________________________________________Training...
Train on 11640 samples, validate on 2910 samples
.
.
.
Epoch 19/20
11640/11640 [==============================] - 11s 952us/sample - loss: 0.0143 - acc: 0.9962 - val_loss: 0.0255 - val_acc: 0.9897
Epoch 20/20
11640/11640 [==============================] - 11s 978us/sample - loss: 0.0139 - acc: 0.9964 - val_loss: 0.0254 - val_acc: 0.9911Done training
Testing...
4850/1 - 4s - loss: 0.0097 - acc: 0.9938*
尽管相对简单,该代码能够区分 astro-ph 和 cs 之间的论文。具有 99%准确度和非常少的过度训练的 AI 类别(验证损失稳定在比训练损失 0.0139 更高的值 0.0254,训练损失 0.0139 持续下降更长时间)。事实上,这一壮举是可能的,由于使用嵌入做了繁重的工作。你刚刚发现了迁移学习的乐趣:一旦一个模型已经在一个大型数据库上预先训练好,你所要做的就是根据你的目的对进行微调!
信息太多!
这些单词或句子嵌入的一个方面是嵌入空间的绝对大小,这里是 512 维,这对于实际应用来说是相当麻烦的。最有可能出现的问题是缺乏大型数据库,甚至无法执行微调步骤—将嵌入层连接到具有 64 个输出节点的密集层将需要拟合 51264+64 = 32,832 个参数。根据经验,应该有至少 10 倍以上的训练示例,,即大约 300k,这对于许多应用来说可能很大。*
人们很容易理解,并不是所有的维度都携带有用的信息来完成手头的任务。一种在保留信息的同时减小嵌入空间大小的简单而有效的方法是应用主成分分析 (PCA)。想法是查看所有嵌入向量的变化,并丢弃存在大变化的维度(代表噪声),同时保留具有更强关联的维度(主分量)。
由于实际上不可能将 512 维中发生的事情可视化,一个好的方法是计算这些向量之间以及不同类别之间的余弦相似性(即归一化点积)。这种操作背后的思想是,属于相似类别的向量应该彼此靠近,因此具有小角度,而属于不同类别的向量应该远离,因此具有大角度。余弦运算将角度压缩到 0 到 1 之间,使比较更容易。如果 PCA 正在做它的工作,人们应该期望在由于 PCA 的维数减少之后不同类别之间的分离有所改善,,即通过丢弃噪声应该出现“信号”。
这正是下面的图所显示的。蓝色直方图显示了使用完整的 512 嵌入空间的余弦相似性的分布,而红色直方图表示 PCA 后的相同分布。在对角线上,属于同一类的文档应该有一个非常接近 1 的余弦,但似乎不是这样(它们大多分布在 0.5 左右)。此外,通过查看不同类别之间的乘积(非对角线直方图),可以看到很少的分离,即一些直方图峰值低于 0.5,但效果并不显著。如果我们使用完整的嵌入空间,对文档进行分类的希望很小!
PCA 前后文档嵌入的余弦相似性。
然而,在 PCA(红色直方图)之后,可以看到一些非常有趣的特征。首先,对角线上的直方图被推向 1,这是一个好现象:同一类别中的论文由彼此接近的向量表示!此外,现在查看非对角线直方图,很明显直方图被推向 0,,即*,类别之间的相似性相当低。最后,有趣的是,即使在 PCA 之后,两个类别仍然难以区分:hep-ex(实验高能物理)和 astro-ph(天体物理)。这令人惊讶吗?数据基本上是在告诉我们,这两个科学分支比人工智能(cs)等其他分支有更强的相关性。AI)和数论(数学。NT)?*
总而言之,正如许多人在温哥华 NeurIPS2019 大会上所争论的,自然语言处理(NLP)领域正在进入一个黄金时代。NLP 基本上正在经历与 2010 年计算机视觉相同的革命。迁移学习和预训练模型基本上为任何人提供了一个机会,为特定任务(如文档分类)微调简单模型。
保持(微调)调谐!
用🕸网络对莎士比亚进行分类
没有 NLP 是什么造就了喜剧或悲剧(+代码!)
所有莎士比亚的戏剧,以网络形式
威廉·莎士比亚是我们最著名和最受称赞的剧作家之一。他的写作生涯跨越了 36 部戏剧(很容易归因),他作品的一个有趣的方面是这些戏剧可以被分成明确的类别:喜剧、悲剧和历史。喜剧有快乐的结局,令人振奋,其中主要人物克服了一些障碍。悲剧通常以有缺陷的主角的死亡而告终。然而,我们怎样才能正式区分喜剧和悲剧呢?这就是我们将在本文中解决的问题,但我们不会以您可能期望的方式去做。
我的研究(见: cameronraymond.me )专注于网络以及我们之间的联系如何塑造我们的世界。虽然很多人分析了莎士比亚戏剧中的对话,但很少有人费心去研究人物之间的不同之处。喜剧和悲剧是非常不同的——不仅在对话方面,而且在人物和场景的相互关系上。我要证明的是,只看莎士比亚戏剧的结构,而不看任何对话,我们就能确定哪些戏剧是喜剧,哪些是悲剧。
诚然,这乍一看可能有些奇怪。如果你想区分喜剧和悲剧,为什么不看真实的对话呢?
我认为而不是 依赖语言是一种优势,因为虽然不是每个人都说同一种语言,但大多数故事都是由人物关系驱动的。“网络优先”的方法让我们可以不受语言障碍的阻碍,对讲故事进行更普遍的比较。依赖自然语言处理(NLP)的方法需要知道如何解释不同的语言以及同一种语言的不同用法!一个从《西区故事》中学习的 NLP 模型可能很难解释《罗密欧与朱丽叶》,尽管它们都是英文的,并且遵循相同的故事情节。尽管存在代沟,但网络优先的方法有可能利用两者之间的关系相似性。因此,目的是测试莎士比亚的喜剧和悲剧的结构是否清晰到足以区分两者,而不依赖于对语言的任何理解。
数据
谢天谢地,莎士比亚的大部分戏剧都被数字化了。使用一个 Kaggle 数据集,我们可以下载所有 36 个剧本,并将它们的脚本加载到一个数据帧中。由于脚本包含舞台指示,它没有给出角色如何相互关联的信息,我们将删除这些行。既然我们不会分析莎士比亚的历史,我们也可以放弃这些。
既然数据是可用的格式,我们就可以开始构建我们的网络(每个剧本一个)。因为我们对角色之间的关系感兴趣,我们将在两个角色之间添加一个链接,也称为边,如果他们在同一个场景中说话。经常在一起说话的字符可能以某种有意义的方式连接在一起。这将形成我们的网络优先分类模型的基础。
让我们以莎士比亚 1623 年的戏剧为例,结局好的就都好。首先,我们将为剧中对话进行隔离。然后我们再计算每个角色说话的次数,把那些对剧的结构有意义贡献的(说话超过 5 次的)保留下来。
接下来,我们将创建网络。我们可以通过一个场景接一个场景,如果两个角色在同一个场景中说话,在他们之间添加一个链接来做到这一点。这些角色在不同场景中一起说话的次数越多,他们之间的联系就越紧密。
重复所有 27 个剧本,想象一下,这些就是最终的网络!现在我们需要一种只使用这些网络来区分喜剧和悲剧的方法。
如何比较网络
比较网络的传统方法。
现在我们已经为莎士比亚的戏剧建立了网络,我们需要一些方法来区分它们。不幸的是,没有简单的方法来比较两个网络的结构。传统上,最主要的比较方法被称为图形编辑距离(GED)。该方法计算在两个网络变得相同之前,需要在这两个网络之间重新分配多少条边。这在上图中可以看到。不幸的是,这依赖于每个图都有相同数量的节点——否则它们永远不会相同。因为我们的戏剧有各种各样的角色,我们需要转向别的东西。 GED 也比较简单,很少抓住一个网络的底层结构,所以需要更细致入微的比较方法。
密度不同但结构相似的两个图。来源。
为了解决这个问题,我们求助于 2018 年发表在知识发现国际会议上的一篇论文。这些研究人员定义**如果信息在一段时间内以相似的方式流经两幅图,那么这两幅图就是相似的。**由此产生的方法称为网络拉普拉斯谱描述符,简称 NetLSD。他们的假设是,信息通过一个网络流动,就像热量通过你的空气管道扩散一样。如果网络中的每个节点随着时间的推移释放一些“热量”,并沿着边缘传递给邻居,那么随着时间的推移,它将产生一个捕捉网络结构的签名。所以在每一个时间步,你要做的就是把网络中的“热量”加起来。这由 h(t) 表示,并在下图中显示为 y 轴。由于每个热追踪信号使用相同数量的时间步长,因此比较不同规模的网络要容易得多。通过使用这种技术,他们能够以较高的准确度(94–95%)识别不同类型的蛋白质和酶。
两个相似图形的 NetLSD 热特征:来源:Tsitsulin 等人。
虽然实现 NetLSD 相对简单(特别是如果你使用他们的 Python 包),但是它需要一些关于计算机如何表示网络和线性代数的背景知识。对那些感兴趣的人来说,这里有原文。由于 NetLSD 的内部工作对我们分类喜剧和悲剧的任务并不重要,我将继续。
把所有东西放在一起
现在我们有了一种有意义地比较网络的方法,我们可以对不同的戏剧进行分类。首先,我们将把这 27 部戏剧分成两个部分:我们的训练数据,我们已知类型的观察戏剧,以及测试数据,将被分类的未知戏剧。我们将利用我们以前观察到的戏剧知识来帮助分类未知的戏剧。由于剧本很少,我将保留三分之一的剧本作为测试数据来验证模型。
接下来,我们将采用我们所有的训练数据,我们知道流派的戏剧,并计算它们的热跟踪签名。
让我们看看喜剧和悲剧之间的热量传递是否有明显的不同。这有点混乱,但似乎悲剧比喜剧平均保留了更多的“热度”。使用这些签名,我们可以开始对未知的戏剧进行分类。
先前观察到的活动的热特征(也称为“训练数据”)
对新剧本进行分类的过程相对简单。我们计算未观察到的游戏的热迹特征,然后找到具有最接近的热迹特征的五个先前观察到的游戏。如果这五部中的大部分是喜剧,那么我们就把这部剧标为喜剧,否则我们就把它标为悲剧。这就是所谓的 k 近邻(KNN)分类器。KNNs 并不真正对每一个说法进行建模——这就是为什么我更喜欢先验这个术语而不是训练数据——但是与 KNN 一起工作的游戏如此之少,这是一个合理的选择。
我们表现如何?嗯,在我们的测试数据中,我们能够正确分类 9 部戏中的 7 部,原始准确率为 77.8%。测试集包含 7 部喜剧和 2 部悲剧,我们能够正确分类所有的悲剧和 5/7 部喜剧。这表明,喜剧和悲剧产生了足够不同的结构,我们可以通过查看网络来区分哪个是哪个。
喜剧/悲剧分类混淆矩阵。
那又怎样?
虽然 27 次播放是一个小样本,但我认为这个实验的原则是合理的。网络优先的方法让我们做一些乍一看似乎不太可能的事情:我们能够在不看一段对话的情况下判断出一部剧的类型。我个人认为这很酷。
你是说这意味着我们不再关心 NLP 了?
显然不是。当像戏剧这样的东西被抽象成一个网络时,语言中有大量丰富的信息丢失了。然而,网络给了研究人员不依赖语言或不受语言束缚的灵活性。这又回到了比较不同语言或不同时期戏剧的例子。这些应用程序不会停留在比较戏剧或电影上。虚假信息通常通过创建社交媒体机器人网络来大量传播,这些机器人转发和放大彼此的假新闻,以给人以合法性的错觉。与其关注一个账户在说什么,不如看看它的连接网络是否与已被关闭的虚假信息机器人网络相似,这可能更有帮助。由于网络的可塑性很强,几乎可以用来表示任何东西,因此有很多潜在的应用。如果你想了解更多关于网络科学/图论及其应用的知识,请访问我下面的网站!它链接到我所有的作品。
最初发布于https://cameronraymond . me。
[1]: Tsitsulin、Anton、Davide Mottin、Panagiotis Karras、Alexander Bronstein 和 Emmanuel Müller。"倾听图形的形状."在第 24 届 ACM SIGKDD 知识发现国际会议&数据挖掘的会议记录中,第 2347–2356 页。2018.
歌曲类型分类:使用 Spotify 的内置功能还是提取我自己的功能
实践教程
我总是很难仅凭声音来区分音乐中真正特定的流派——我正在看着你,金属!所以,我把体裁分类的问题变成了数字信号处理问题。我们去处理吧!
【我应该先说我有音乐表演硕士学位,是一名录音艺术家,并且自己出版我的音乐,所以这是我对这个主题的兴趣开始的地方。]
照片由 Adi Goldstein 在 Unsplash 上拍摄
问题是什么,为什么重要?
流派分类对于音乐分发平台很重要( CD Baby 、 Distrokid 和 Songtradr 是一些你可能很熟悉的)。好的流派分配有助于艺术家和联系新的观众,并让听众高兴(你的朋克摇滚播放列表中没有意外的圣诞歌曲)。如果顺利,大家都赢了!
对于这个项目,我想用下面的问题陈述来调查流派分类的问题:
使用来自音频源的特征,分类模型能否以足够高的准确度预测一个 30 秒音频剪辑的流派,以自动将歌曲组织成它们各自的流派?
我们在讨论多少数据?
好问题,读者!这是我们得到的信息:
- 总共 9365 首歌曲
- 10 种类型
我把这些歌曲分成简单的 5 种风格集和复杂的 10 种风格集。难度水平是由体裁的紧密程度决定的。下面的橙色和粉色是相互关联的。
我训练和测试模型的流派列表。图片作者。
我从探索数据中发现了哪些有趣的事情?
当我开始使用 Spotify API 的spotipy
Python 库挖掘数据时,我发现 Spotify 让你可以访问一些有趣的预设计功能。我将着眼于舞蹈性、乐器性和节奏,但还有许多其他有趣的功能可用!
可舞性是一首歌的可舞性从 0 到 1 的评级。这结合了从歌曲中提取的许多不同的节奏元素。我们看到前五大适合跳舞的类型正是我们所期待的。对不起古典,你的华尔兹不适合跳舞!图片作者。
乐器性是衡量一个音轨是否不包含人声的指标。我们可以看到,古典音乐、序列音乐和巴洛克音乐的平均器乐化程度远远高于说唱音乐、R&B 音乐和嘻哈音乐。这很有道理!图片作者。
这很有趣,不同流派之间的节奏差异很大,但看起来热带小屋的四分位数范围最窄。朋友们,保持你的热带音乐在 120-130 BPM 之间!图片作者。
现在挑战来了:我能打败 Spotify 的功能吗?
Spotify 已经做了一些很好的工作,从音频来源中提取可解释的、有意义的数据,但我想看看我是否可以创建自己的数据。我花了一些时间四处打听和寻找资源(感谢诺亚·克里斯蒂安森和萨拉·苏伊丹!),研究librosa
,通过音乐信息检索网站挖掘一些思路。
我用librosa
设计了一些东西:
- 能量:响度的量度
- 均方根能量:响度的另一种度量
- 梅尔频率倒谱系数(MFCC) :特定频率范围内响度(功率)的更详细测量。
我不想深究数学,但 MFCC 类似于声波指纹。每一个声音都是特别而独特的,就像你们每一个人一样。
看看这些独一无二的小曲片段!X 轴是时间,Y 轴是频率范围,颜色越亮,该点的频率范围越强大。图片作者。
建模阶段
因为每一个不正确的估计都同样糟糕,所以我使用准确性(10 种类型集的基线 10%)作为我的模型成功的衡量标准。我测试了几个不同的模型,最后使用了一个支持向量分类器(SVC) ,原因如下:
- 最终,的表现超过了我测试的其他模型(包括一个卷积神经网络)
- 用当前数据集来拟合模型只需要几秒钟
所以这是更好更快——你还能要求什么呢?唯一的问题是,如果我有数百万首歌曲,这个模型可能无法很好地扩展。
下面是我的模型——用新设计的功能构建——与另一个用 Spotify 的预设计功能训练的 SVC 相比的表现:
5 个流派的集合是更容易的集合,与 10 个流派的集合相比,它们之间的关联更少。图片作者。
突击测验:你能猜出这些歌曲的流派吗?我的模型不能。
FDVM 再爱
- 预测类型:流行音乐
- 实际类型:热带住宅
我唯一能猜到的是,这里的鼓点不像其他热带屋的曲目那样存在。
美利坚合众国总统的小猫
- 预测类型:热带住宅
- 实际类型:摇滚
这首每段结尾都有动态滴,类似热带屋歌。
西海岸的派对(壮举。费丝·伊文斯马托马和臭名昭著的 B.I.G
- 预测类型:热带住宅
- 实际类型:说唱
这首歌刚刚得到了一个糟糕的 30 秒抽签,Spotify 为我们的 30 秒预览随机选择的歌曲片段是合唱部分,在我的人类耳朵里听起来更像是热带房屋的曲调。
如果你想玩玩,看看更多不正确的猜测,我已经在我的公共 Github 中构建了一些代码,它将从你选择的任何流派中返回一首随机错误预测的歌曲!
结果
图片作者。
我从这个过程中学到的是子流派在声音层面上没有很好的定义。可能还有其他因素影响到次类型的创作。在这一点上,我不能预测足够准确的类型来进行自信的部署,但我希望在未来尝试一些事情:
- 从音频中提取歌词。我认为对歌词的分析有助于预测子流派。例如,我认为说唱歌曲的歌词总数会比 r & b 歌曲多,这可能是一种在这两种风格之间做出决定的方式。
- 无监督学习模型。我很想看看当计算机对它们进行分类时会出现多少流派,然后使用迁移学习将结果反馈到另一个模型中。
感谢阅读!如果您想查看该项目,请前往我的公开回购。由于使用的一些数据集超过 100MB,我无法将它们全部上传到 GitHub,但你应该可以用我的代码重建它们。我很乐意更多地谈论这个项目——留下评论或在 LinkedIn 上找到我!
来源和进一步阅读/观看
- 【https://musicinformationretrieval.com/energy.html
- https://musicinformationretrieval.com/mfcc.html
- 医学博士 SahidullahSaha,Gou tam(2012 年 5 月)。“说话人识别 MFCC 计算中基于块的变换的设计、分析和实验评估”。言语交流。54 (4): 543–565.doi:10.1016/j . specom . 2011 . 11 . 004
- 亚当斯塞斯。“DSP 背景——音频分类的深度学习”。
https://www.youtube.com/watch?v=Z7YM-HAz-
IY&list = pl ha 3 b 2k 8 r 3t 2 ng 1 ww _ 7 mixeh 1 pfqjqi _ P
用 Sklearn 对足球运动员的位置进行分类。
使用 Fifa 18 比赛数据,将足球运动员分类为 CM、ST、CB 和 GK。
来源 : Freepik
感谢‘Aman Shrivastava’分享数据。
我们将使用所提供的数据集,该数据集已经过处理并准备就绪。
**用例:**作为一名用户,我有一名球员的统计数据,我想知道这名球员应该打前锋(ST)、中场(CM)、中后卫(CB)还是守门员(GK)。
我用过的数据是 FIFA 2018 比赛中所有球员的属性得分。数据的尺寸是 14045 x 74,但我使用的是专业球员的数据,他们被选择作为前锋、中后卫、中场和门将。经过此筛选后,剩余的数据集有 7587 x 77 个维度。
数据分析
当我们检查过滤后的数据时,我们注意到 CM 位置的样本量与其他位置相比较小。这将影响偏向重样本的预测。
有些球员根据他们踢的位置而擅长一些技能。举例来说,ST 必须进行更少的铲球,所以他们的铲球能力将低于 CB,反之亦然。
通过检查变量“PrefPos”的皮尔逊相关性,也分享了这一证据。
为了自己找到相关性,首先使用 cat.codes 方法将 PrefPos 变量更改为 numeric category。
下面的形象化比较了铲球能力和终结能力,但是有一个可变的“年龄”,一个球员是否有可能在早年成为一个糟糕的终结者,随着时间的推移,他的技能会有所提高?
本页底部有一些更有趣的方框图。
培训和测试取样
我们希望通过为具有训练集和测试集的样本创建子群体来创建具有相等样本比率的样本,以避免出现 CM 位置的样本量更少的情况,然后我们选择并分配变量[‘PrefPos’]作为目标。
训练模型和预测
因为我们的目标是多类分类,所以 sklearn 提供了一些选项。我使用 SGD 分类器,因为它本身能够处理多类,如下所示:
让我们在训练好的模型上拟合测试集,并得到预测
交叉验证和准确性
显然,目前我们得到了 42%的总体精度,但正如我们在上面检查的那样,该精度因位置而异,CM 位置的精度远低于 42%。
多类混淆矩阵
我们现在来看看错误率。
结论:
对模型的初步检验表明,在根据运动员的表现对他们进行分类的情况下,有机会进一步改进模型的性能。有机会做进一步的特征工程,并使用深度学习算法来进一步提高模型的性能。
完整代码:
从这里下载数据。
我还研究了年龄对专业运动员某些技能的影响,结果如下:
ST 平均具有较好的整理能力,并随着年龄的增长而提高
反应是任何运动的一个必要方面,但 CM 往往略胜一筹。
CBs 擅长滑铲。
受年龄影响的一个属性是冲刺速度。
感谢您的阅读,我打算在下一篇博客中讨论初级强化学习。
**数据来源:**再次特别感谢 Aman Shrivastava 分享数据。
在线评论论坛中的毒性分类:端到端项目
利用自然语言处理(NLP)和深度学习对用户提交的评论中的毒性进行分类和跟踪。
脸书评论按钮
一个完整的交互式 Jupyter 笔记本,包含代码和解释,可以在我的 Github 这里找到。
介绍
自从社交媒体和在线对话成为日常生活的一部分以来,在线处理毒性和遏制骚扰已经成为一个日益严重的问题。几乎不可能在没有目睹不必要的骚扰或不尊重等有害行为的情况下进行在线对话。数字世界有潜力成为一个通过向人们学习来促进增长、同情和教育的社区,但受到利用这种面对面断开的用户的阻碍。
我相信每个人都有权利参与在线对话,而不必担心被骚扰或成为无端辱骂的目标,这让我想起了不久前我在脸书无意中发现科林·卡佩尼克帖子上一些关于被邀请回 NFL 试训的粗鲁评论。
谁是科林·卡佩尼克?他是旧金山 49 人队的四分卫,在国歌声中跪下抗议美国的警察暴行和种族不平等。
下面的评论来自他的帖子,并启发我使用我在数据科学方面的技能来有效地检测和跟踪用户评论的毒性,如下所示。
方法学
我使用的数据来自 Kaggle,由用户在在线论坛上提交的大约 180 万条评论组成。每条评论都标有让另一个人退出对话的可能性。我把毒性定义为任何大于 0.5 的概率。数据集可以在这里找到。
在将我的数据转换成分类算法可以使用的格式之前,我对原始文本数据进行了预处理。这包括:
- 删除不需要的字符
- 标记化
- 删除停用词和缩写(我创建了自己的停用词列表)
- 词性标注
- 词汇化
为了更多地了解我的数据,我计算了词汇化后的单词数和我们的词汇量(独特的单词)。此外,知道我们最长的句子有多长可能会有帮助。
现在我准备开始建模过程。每个模型都是不同的单词嵌入、维度缩减和分类算法的组合。我使用了如下所示的工具和受试者操作特征(ROC)曲线下面积(AUC)来对每个模型的性能进行评分。
结果
我最好的模型是通过深度学习获得的。我使用了带有 Word2Vec 嵌入的双向 LSTM。这意味着我用预先训练好的单词向量将我所有的单词嵌入到谷歌的 Word2Vec 词汇表中,并让我的模型学习其余的。我的 vocab 有 2 亿个单词在谷歌的 Word2Vec vocab 中,这使得我的模型只剩下 20 万个嵌入来训练。使用预先训练的单词向量大大减少了计算时间并显著提高了性能。
网络架构:双向 LSTM
双向长短期记忆(LSTM)是一种递归神经网络(RNN)架构,围绕记忆更新策略增加了复杂性,允许我们捕捉单词使用的空间模式,这与人类如何交流极其相关。通过将文本视为单词序列,并以明确的顺序方式处理这些单词,我们可以在模型中获得很大的预测能力。
双向仅仅意味着我们从前到后和从后到前处理文本,允许我们捕捉丰富的上下文。利用 Keras,以下是我使用的超参数。
受试者工作特征曲线
利用预先训练的词向量和深度学习的力量,我的最终模型在性能上给了我一个显著的提升,结果是
ROC AUC=0.951
利用深度学习能力的唯一问题是我们在模型中失去了可解释性。我无法看到特性的重要性来检查我的模型是否做出了正确的决策。相反,我去了在线评论论坛,让我的模型实时预测评论,供我观察。
所以回到科林·卡佩尼克之前的帖子,使用我的模型,我们看到毒性评论被标记为 99.5%的毒性。而“头号粉丝”的评论毒性为 0.2%。
结论
回到最初的问题,这种模式如何在我们的在线对话中增加礼貌?嗯,要看平台。例如,在线游戏,你可能想检测有毒用户,然后根据违规次数或违规严重程度实施某种限制。
社会化媒体
然而,对于像脸书和推特这样的社交媒体平台,你可能不想限制人们的言论自由。如果我们在人们打字时在评论上方放置实时毒性追踪器,他们可以对自己说的话更加自觉,并有希望第二次猜测他们将要发布的毒性评论。
毒性追踪器只需要放在有上网不礼貌历史的用户身上。与游戏类似,我们可以使用人工智能来检测这些用户,然后对他们使用毒性跟踪器。进行一项案例研究,看看有中毒史的用户在开启网络聊天时是否会改变他们的行为,这将是一件有趣的事情。
参考
使用机器学习来理解和利用文本。
blog.insightdatascience.com](https://blog.insightdatascience.com/how-to-solve-90-of-nlp-problems-a-step-by-step-guide-fda605278e4e)
分析数据的清晰架构
简化数据分析过程
不管你来自什么职业,如果你的企业或服务有客户,那么你肯定是在分析数据。这种分析是你对未来发展充满信心的根源。在这篇文章中,我将深入分析数据的过程以及从中获取最大收益的方法。让我们深潜。
数据分析不仅仅与在硅谷工作的科技公司相关。它是每个行业的一部分,适用于所有产品。医药、杂货店、大学、气象站以及几乎所有你能想到的行业每天都要处理大量的数据。我将解释并演示一个典型的数据分析过程是如何工作的。但是,这些步骤并不详尽,并且可能不会按照所述的直线顺序发生。这只是勾勒出数据分析作为架构的整体图景。
解决任何分析问题的一般思维模式是通过假设检验的结果来看待它。什么是假设检验?它是验证结果的程序,从结果开始。我们形成一个结果(称为假设),然后解决问题以证明假设是真的。类似地,当解决一个分析问题时,我们从决策开始,从结果中需要什么开始,然后形成我们达到这个结果的策略。不断回顾这一结果并反思方法是否正确总是一个好主意。原始流程是这样的,我们从定义决策和分析所需的输出开始。然后是可以使用的分析方法,项目有什么数据要求。下一步是获取数据,收集数据并将其存储在一个可访问的位置,并进一步使用它来执行分析。这种方法通常回答有关项目可行性的问题。
这是建筑的草图。虽然它并不详尽,可能会根据项目的需求不断变化
1.问题/需求收集
理解需求是关键。重要的是要找出你想要解决的业务问题是什么,以及你想要衡量的 KPI 是什么。下一步是规划前进的道路。我推荐的一个非常有用的策略是花些时间从给你的信息中找出数据指针。这些数据指针是性能指标,它们将构成您需要关注的信息集。问具体的问题很重要。这个阶段也为您的数据分析解决方案奠定了基础。从数据中记下你认为会对决策产生影响的关键领域。一旦完成并理解了这些,你现在就可以进入数据探索和收集了。
2.数据采集
在加载数据和执行前两步的过程中,涵盖了数据采集的主要部分。但是,作为一个过程,获取不仅仅是将数据从一个资源带到您的工作空间。关于这次收购,一直存在术语上的混乱。首先,“数据获取”有时用于指组织产生的数据,而不是(或同时)来自组织外部的数据。这是一个谬论,因为组织产生的数据是已经获得的。因此,我们只考虑从其他来源和流程中获取的数据。
这方面的一个重要步骤是识别数据的来源和类型。数据可以多次从云服务移动到本地主机,或者来自 API 调用。我的建议是,如果数据源在云上,并且是静态数据(在一段时间内保持不变,不会改变),那么在这段时间内在本地下载数据、执行分析并发布报告将是一个不错的选择。另一方面,如果数据是一个连续的流(刷新数据),我建议在云本身上执行分析。
使用 Panda 的 read_csv 函数将本地文件导入为 Panda 数据框[图片由作者提供]
3.数据争论/数据准备
根据 Aberdeen Group 的一份报告,数据准备“是指任何旨在提高数据质量、可用性、可访问性或可移植性的活动。这一步从收集数据并将其导入到您的工作区开始。从现在开始,由于有可能向您展示数据,我将使用一个数据集来解释进一步的步骤。它将首先以大多数代码能够理解的格式接收数据。如果使用基于查询的程序来处理数据,任何 SQL 语法都可以。我将在一个 CSV 文件上继续使用 Python。这里需要注意的一件重要事情是,数据是 CSV 格式,TSV 格式对于 Python,R 来说更容易理解和解释。同时,这些格式可以很容易地在 Excel 上运行以可视化,但同时,XLS 格式的文档很难理解和运行。您可以使用各种语句将数据加载到 Python 环境中。
导入数据的另一种方式。打开文件并创建一个流以写入文件。“打开”函数以只读、二进制读写和其他方式打开文件
一旦数据被导入,一些方法有助于理解存在的变量类型和可用的数据量。这可以使用一些 Pandas 函数来完成,如“数据帧”。Info()”或“DataFrame.head()”。下一步是试图找到这些数据中的异常。这些异常可能是空白列、缺失值,或者在某些情况下是异常值。对于许多分析师来说,处理异常值的概念非常不同,处理异常值也主要取决于手头的问题。有时删除离群值可能是有意义的,有时我们可能希望为所有列创建一个标准化的表格来弥合离群值,这需要标准偏差和方差。这里的一般规则是,如果数据点距离数据的平均值±3 SD,建议忽略该异常值,不考虑进行进一步分析。
4.数据清理
数据清理部分初始化一些可视化库,以检查数据差异。这很重要,因为异常值会极大地导致我们对分析的误解。Pandas、Matplotlib 和 Seaborn 中有一些公共库可用于识别这些异常值。让我们看一些代码,这些代码将确定数据中是否有需要清理的值。我在这里解释实现的两个部分,一个是寻找缺失值,另一个是寻找异常值。
要查找缺失值,我们可以使用下面的函数:IsNull()或 isna()。下面的热图显示了在列中显示为 Null 的数据点。然后,可以使用插补技术(如均值/中值插补)对其进行填充(尽管生产级代码不考虑均值插补)
热图用于查找数据中的空值/缺失值[图片由作者提供]
另一个重要的技术是绘制一个箱线图。这些图通常揭示了我们正在尝试处理的数据的大量信息,并且可以很容易地用于识别风险因素,如数据中的偏斜度和数据中的异常值。
方框图的示例实现[图片由作者提供]
箱线图的解释可通过以下方式完成:
图片由 SimplyPsychology 解释箱线图的解释[1]
**最低分:**最低分,排除离群值(显示在左须末端)。
**下四分位数:**25%的分数低于下四分位数(也称为第一四分位数)。
**中位数:**中位数标志着数据的中点,由将方框分成两部分的线表示(有时称为第二个四分位数)。一半分数大于或等于该值,一半分数小于该值。
**上四分位:**百分之七十五的分数落在上四分位值以下(也称为第三四分位)。因此,25%的数据高于该值。
**最高分:**最高分,排除离群值(显示在右须末端)。
**触须:**上下触须代表中间 50%以外的分数(即较低的 25%分数和较高的 25%分数)。
四分位范围(或 IQR): 这是显示中间 50%分数的箱形图(即第 25 和第 75 百分位之间的范围)。
处理有偏差或缺失的数据将根据不同的业务场景而改变。正如我之前解释的一般规则,任何超过 3 个标准差阈值的数据点都可以被删除,因为这是一个无法解决的直接异常情况。对于插补,使用了许多技术,如均值插补、热箱插补等。
5.数据探索
数据的探索性分析不仅令人着迷,而且也是收集数据中的架构和依赖关系的最佳形式之一。这个阶段可以或者有时不可以与手头的主要问题相关联,但是仍然在我的分析解决方案的干净架构中占有一席之地。这样做的主要原因是从业务需求中形成的用例经常会回答所有的问题,尽管有时我们可能会遗漏一些数据。这些数据对于解决手头的问题可能是必需的,也可能不是必需的,但是对于掌握数据集的结构非常有用。
这可以由各种步骤和图表组成,您可以使用这些步骤和图表来分析数据,并探索不同数据值之间的联系和意义。这里的目标应该是彻底理解表格列的工作方式以及它们所包含的值。这是实际预测分析和得出结论之前的阶段,因此应该利用它来清除与正在分析的数据相关的所有形式的疑问。我列出了几个可以使用的基本图,以及从中可以得出什么样的衍生品。
绘制像箱线图、分布图和散点图这样的图表将向您展示数据曲线的样子,它在哪里是偏斜的,以及可能的异常值是什么。帕累托图和直方图有助于分析数据的置信区间以及最常见的值。如果数据是多维的,并且变化超出了理论上的认识,我们可以使用类似主成分分析(PCA)的东西来执行维数减少。
6.预测和结论
我们正慢慢接近这一进程的尾声,因为大部分分析现已结束,但解决方案仍未完成。这里要考虑的一个重要方面是,对于任何数据分析解决方案;解决方案的交付通常不会交给其他数据分析师。因此,我们从数据中得出的结论需要用大多数人都能理解的语言来表达。这可以在可视化阶段得到完善,但尽管如此,越容易理解的东西,就越容易呈现。对于根据数据做出的预测,请确保仅根据第一步的要求做出预测。很少有分析师喜欢将额外的信息可视化,假设大量的数据是一个好的预测。理论上,这可能证明你在分析过程中做得很好,但最终如果消费者不需要额外的数据,这些数据就没有任何用处。因此,要记住的重要一点是尽可能保持简单明了。
7.数据可视化
我们现在来看架构中的最后一部分,数据可视化更像是为您的数据将要提供的通信添加一个额外的验证层。现在,可视化本身是一个巨大的进步,包含了许多概念,也可以有自己的架构。通过认识到数据可视化本身是一项全职工作,不像架构图的所有其他步骤,可以理解这一点的深度。
我已经创建了一个单独的故事来解释数据可视化实际上有多重要,我建议大家浏览一下。
https://medium . com/analytics-vid hya/the-science-of-data-visualization-995 e 45238354
8.交流(讲故事)
尽管数据可视化涵盖了分析的大部分图形信息,但架构的最后一部分是讲故事的艺术。一旦分析、预测和可视化完成,我们就回到任务的第一步,收集需求并理解我们需要回答的问题。沟通包括给最终用户答案。在得出这个结论的过程中,我们可能会遇到不需要放入结论中的各种数据段。在我看来,给出结果的最好形式是一页或者最多两页的报告仪表板。[2]该报告将包含我们正在解决的问题的准确、中肯的答案,并且只包含最终用户需要的数据。在沟通过程中使用的语言应该从业务用户的角度出发,而不是从数据科学家的角度出发。有一些工具可以用来以创造性的方式传达这一信息——tableau、adobe analytics 是一些非常棒的仪表板工具。我将为如何开始讲故事的艺术写一篇单独的文章。但是,同时让我向您展示几个很棒的仪表板示例。
来自公众的图片 Tableau Viz Gallery 作者尤里·法尔展示了 AAPL(苹果)的股价走势
并不总是需要使用强大的工具和复杂的算法来解决数据分析问题。虽然,当需要时,许多工具将为您提供先进的技术和选择,但代价是过于复杂。将解决方案复杂化的一个缺点是将这些结果呈现给涉众。你的程序越复杂,就越难提出来。
数据驱动项目的当前状态需要改变,因为敏捷框架已经为软件工程创造了奇迹,所以是时候采用迭代方法来解决数据科学问题了。我相信这个提议的解决方案有助于缓解项目的生命周期,并且到目前为止已经为我展示了清晰的结果。
请在下面的评论中让我知道你对遵循一种架构进行数据科学项目的想法,或者在 Twitter 上与我联系。
参考文献:
[1]关于箱形图的细节和解释—https://www.simplypsychology.org/boxplots.html
[2]用于单页报告仪表板的 Tableau 博客—https://www . Tableau . com/about/blog/2017/7/viz-gallery-contest-winners-prove-visual-analytics-art-73390
像专家一样预处理数据:Scikit 简介-学习管道
用于估算、缩放、编码和转换数据的可重复使用的函数
20 年代的数据争论照片由马希尔·云萨尔在 Unsplash 上拍摄
“如果没有一个系统的方法来启动和保持数据干净,坏数据就会发生.”
—多纳托·迪奥里奥
您编写了所有的查询,收集了所有的数据,并全力以赴实现您在 Medium 上读到的最新机器学习算法。等等!你很快意识到你需要处理缺失数据、插补、分类数据、标准化等。
您可以开始编写可应用于任何数据集的函数和数据管道,而不是“手动”预处理数据。幸运的是,python 的 Scikit-Learn 库有几个类可以让这一切变得轻而易举!
在本文中,您将学习如何:
- 在任何数据集上轻松再现转换。
- 轻松跟踪应用于数据集的所有变换。
- 开始构建您的转换库,您可以在以后的不同项目中使用它。
tl:dr :让我们建立一个管道,在那里我们可以像这样估算、转换、缩放和编码:
**from sklearn.compose import ColumnTransformer**data_pipeline = ColumnTransformer([
('numerical', num_pipeline, num_vars),
('categorical', OneHotEncoder(), cat_vars),
])airbnb_processed = data_pipeline.fit_transform(airbnb_data)
不需要了解太多,你就可以推断出不同的变换应用于数值变量和分类变量。让我们进入众所周知的杂草,看看我们如何结束这个管道。
数据
对于所有这些例子,我将使用来自 insideairbnb.com 的 airbnb 纽约房源数据集。这是一个真实的数据集,包含从 airbnb 搜集的信息,并拥有与该网站上的一个房源相关的所有信息。
假设我们想要预测一个列表的价格,给定一些变量,如财产类型和邻居。
raw_data = pd.read_csv('[http://data.insideairbnb.com/united-states/ny/new-york-city/2020-07-07/data/listings.csv.gz'](http://data.insideairbnb.com/united-states/ny/new-york-city/2020-07-07/data/listings.csv.gz'),
compression='gzip')
让我们从获取我们想要使用的分类变量和数值变量开始。为了简单起见,我们将删除我们感兴趣的分类变量中缺失值的数据,并且不进行审查。
归罪
如果数据集没有丢失数据,它是真实的吗?现实情况是,我们必须一直处理缺失数据。您必须决定如何为您的特定用途处理丢失的数据
- 您可以删除带有缺失数据的 na()行。可能会丢失太多数据。
- 删除缺少数据的变量。如果你真的想要那个变量呢?
- 将 NAs 替换为零、平均值、中值或其他计算值。
Scikit-Learn 为我们提供了一个简单的类来处理缺失值。
让我们用中位数来估算数字变量,如价格或保证金。为简单起见,我们对所有数值变量都这样做。
**from sklearn.impute import SimpleImputer**imputer = SimpleImputer(strategy="median")# Num_vars is the list of numerical variables
airbnb_num = airbnb_data[num_vars]airbnb_num = imputer.fit_transform(airbnb_num)
SimpleImputer
类将用中值替换所有缺失的值。.fit_transform()
方法将返回一个很好的 numpy 数组对象,可以用于任何机器学习方法。您可以选择不同的指标,并将其作为参数传递给。
编码分类变量
数字变量非常简单。让我们处理通常以字符串形式出现的分类数据。在这种情况下,我们必须处理诸如邻居、房间类型、床类型等变量。机器学习算法更好地处理数字,所以我们将转换我们的分类变量。
我们的分类数据看起来如何:
我们将转换所有的文本变量。
我们使用OrdinalEncoder
将字符串数据转换成数字。变量中的每个唯一值将被映射到一个数字。例如,公寓=0,公寓=1,等等。
**from sklearn.preprocessing import OrdinalEncoder**ordinal_encoder = OrdinalEncoder()airbnb_cat_encoded = ordinal_encoder.fit_transform(airbnb_cat)
airbnb_cat_encoded[:,1:10]
编码后,我们得到:
我们所有的文本数据现在都被编码了。
完事了吗?还没有。ML 算法可以从字面上理解事物,认为类别 1 和类别 2 比类别 1 和类别 19 更相似。这可能有利于评级或有秩序的事物,但邻里关系呢?这个简单的编码器能区分 SoHo 和 TriBeCa 的氛围吗?它知道所有的正常人都在市中心工作吗?
曼哈顿室友类型 via 6sqft
我们可以将数据转换为每个类别都有一个单一的属性。例如,当“property_type”为“House”时,我们创建一个等于 1 的属性,否则创建一个等于 0 的属性。对所有其他类别重复上述步骤。
这个过程被称为一键编码。如果你来自统计学或计量经济学背景,这些被称为虚拟变量/属性。
**from sklearn.preprocessing import OneHotEncoder**cat_encoder = OneHotEncoder()airbnb_cat_hot_encoded = cat_encoder.fit_transform(airbnb_cat)
airbnb_cat_hot_encoded<48563x281 sparse matrix of type '<class 'numpy.float64'>'
with 388504 stored elements in Compressed Sparse Row format>
出现一个野稀疏矩阵!编码器返回的不是 NumPy 数组,而是一个稀疏矩阵,这在我们有数百个类别的多个分类属性时非常方便。
除了每行有一个 1 之外,矩阵中全是 0。这将使用大量的存储器来存储,但是备用矩阵是智能的,并且只存储非零元素的位置。
特征缩放
在使用机器学习算法时,缩放我们的数据非常重要。也有例外,但是当变量处于不同的尺度时,我们通常需要对它们进行缩放。你可以在这里阅读全部内容。
有几种方法可以做到:
- 最小-最大缩放:减去最小值并除以范围(最大-最小)。值的范围从 0 到 1。MinMaxScaler 就是这样做的。
- 标准化:减去平均值,除以标准差。你最终得到的是 0 均值和单位方差。在这种情况下,值是不受限制的。标准化受异常值的影响较小。我们可以使用 StandardScaler。
**from sklearn.preprocessing import StandardScaler**StandardScaler().fit_transform(airbnb_num)
那很容易!
自定义转换
Scikit-Learn API 非常灵活,允许您创建自己的自定义“转换”,您可以轻松地将它合并到您的流程中。您只需要实现 fit()、transform()和 fit_transform()方法。
添加TransformerMixin
作为基类会自动获得 fit_transform()方法。
这里,我们有一个非常简单的转换器,它创建了评级与评论数量的比率。你可以把这些弄得尽可能复杂。
查看 Scikit-Learn 文档了解更多详细信息和示例。
管道
我们终于可以把所有东西放在一起了!一个数据集可能需要多次转换,这些转换根据变量类型而有所不同。这些也必须以正确的顺序执行。
Scikit-Learn 为我们提供了 Pipeline 类来帮助这个无处不在的过程编写干净高效的代码。
把所有的放在一起
我们为数字变量创建了一个管道。管道构造函数接受一个(’ Estimator Name ',Estimator())对列表。除了最后一个估算器,所有估算器都必须有 fit_transform()方法。他们一定是变形金刚。名字应该是信息丰富的,但你可以把任何你想要的。
让我们用分类数据完成我们的管道,并创建我们的“主”管道
我们可以组合应用于不同变量集的不同管道。在这里,我们将数字管道(Impute、Transform、Scale)应用于数字变量(num_vars 是列名列表),并对分类变量(cat_vars 是列名列表)进行热编码。
ColumnTransformer
类将完全按照名字所暗示的那样去做。它会将管道或转换器应用于指定的变量列表。
它接受一个元组列表(“名称”,转换器/管道,“变量列表”)
一旦我们建立并定义了我们的管道流程,我们就可以使用fit_transform()
方法轻松地将其应用于任何数据集或新数据。
结论
现在,您已经了解了实现干净高效的管道的基础知识,这些管道可以在不同的数据集上轻松地组织和利用。有关更多详细信息,请参考 Scikit-Learn 文档
为什么止步于预处理?在管道中,你可以完成你的模型的训练。下面是一个快速完整的示例:
一旦您有了数据转换设置,您就可以将培训作为另一个“评估者”包含在您的管道中。您的管道可以用作任何其他估计器。SVC()
包含在末尾,将使用传递给它的缩放数据。然后,您可以将此管道重新用于许多其他机器学习算法或数据集。
希望这个框架能帮助你写出易于维护的简洁代码。你可以在这个 git 库的 jupyter 笔记本中找到这个 airbnb 完整的工作示例。
如果你觉得这是有价值和有帮助的,请鼓掌并分享。关注我或在 linkedin 上随意联系。