非时序数据的分类算法
“识别”的关键问题之一,无论是自然语言处理(NLP)——语音/文本还是像拼图一样从碎片中解决图像难题,都是理解单词或数据碎片以及上下文。单个的单词或片段没有任何意义,把它们绑在一起就有了上下文的概念。
现在数据本身有一些模式,大致分为时序或时间序列数据和非时间序列数据,非时间序列数据很大程度上是非时序或任意的。文本报告、文档和期刊、小说和经典作品的情感分析遵循时间序列模式,在这种意义上,单词本身遵循由语法和语言词典支配的优先顺序。股票价格预测问题也是如此,它有以前时间段预测和社会经济条件的先例。
然而,非顺序数据不遵循模式,单词或信息片段的出现不遵循先例,但可以共同给出上下文的含义。数据可能不是也不会是任何标准语言词典或图像库的一部分,但可能是一些特定的约定语言或行业规范,但它们将共同传达分类上下文(ML 术语中的标签)。
这种分析为不遵循任何标准语法教条或单词优先规则的语音或文本内容和/或消息提供了一种方法。将它扩展到图像是值得一试的,不在本文讨论范围之内。
在分析和预测的情况下,有很大的空间整合来自非结构化数据的数据湖的组织数据,这些数据来自各种来源,如供应商信息、区域销售、供应商、服务提供商、各种产品、原材料和服务的支出信息。通过为每条数据提供有意义的嵌入或向量表示,或通过如下所述将每条数据视为独立变量,组织和总结不同的信息并最终将它们关联起来,即。标记,预定义的业务指标,如价格趋势、区域业务表现、各种细分类别的支出、供应商等。,就有可能获得对商业各个方面的重要见解。
在一些使用案例中,例如药物评论分析和分类,其中药物反馈是来自患者身体状况参数和患者反应的收集,本解决方案允许研究人员和分析师分析和评定特定药物的功效。
该解决方案还可以用于其他用例中的产品评估,并可以从不同的产品属性(如定价、细分、评论、反馈和其他参数)来评估产品。例如,葡萄酒质量可以从不同的参数集进行评估,而不仅仅是单一的评级考虑。
整理碎片
这里要解决的第一个问题是,意义完全相同或上下文相似的单词或数据点需要聚集在一起,以便它们在以后评估时具有相同的效果。因为它们是不相关的,所以需要相对于给定的上下文以某种方式进行排列或放置。例如,缩写和拼写错误的单词、多语言内容、传达相似思想或意思的图像表示需要被提供相似的相关数学表示。
对于这一步,我们使用 word2vec 算法方法(glove 是另一种值得评估的方法)。这种聚类的最终结果是,我们在 n 维空间中获得相似单词或数据点的聚类。在给定的上下文中,这些词或数据点的集群中的每一个都可以被认为与相同集群中的其他数据点同义。对于 NLP 的具体情况,skip-gram 模型能够比 cbow 更好地对数据点进行聚类。
还有其他算法,尤其是文本算法,可以做到这一点。(单词距离算法,如 Hamming、Euclidean distance 和其他单词距离算法在识别同义词和缩写时都失败得很惨)。
单词嵌入算法为每个单词或数据点生成一个矢量表示,我们在这个过程中为每个数据点生成一个数据字典和一个矢量表示。
在应用一些降维技术(如 t-sne)后,数据聚类将出现在三维空间中,如下图所示。这只是为了表示和理解的目的。
整合各个部分
的第二步是巩固嵌入内容,学习嵌入内容的要点,并将其与标签匹配。这里,我们将嵌入的合并输出馈送到完全连接的激活层,并最终馈送到输出层,这是典型的神经网络架构。输出将用于训练,以评估交叉熵损失&的准确性。
现在,解决方案的关键是我们整合嵌入的方式。RNN 及其变体可以很好地处理时序数据。如前所述,这里事件的发生是任意的。
一种选择是使用 CNN 并对单词嵌入的 n 元语法进行卷积。但是主要的挑战是优先权。当数据与一组独立变量、完全不相关的属性相关时,CNN 的拥护者最终会提出对 N 个独立变量进行卷积。
一种简单的合并方法以及正确有效的方法是获取规范中单词嵌入的摘要(非顺序数据)并将该摘要馈送给完全连接的层。
让我们以笔记本电脑规格为例。
联想 V330 15" CPU : 英特尔酷睿 i5–7200 u。 GPU : 英特尔高清显卡 620。显示屏 : 15.6 寸、全高清 (1920 x 1080)、TN。内存 : 8 GB。
考虑到该数据点中的每个输入都是一个单词(1-gram 模型),并且每个单词都会增加笔记本电脑的规格。如果我们把这个规范分成空格分隔的单词,我们得到大约。20-25 个单词,每个单词都有一个附加的嵌入表示。
这里的想法提出取这些训练嵌入的归一化总和(摘要),并将该摘要馈送到完全连接的神经网络。
(CNN 主要用于图像处理,例如,在扫描面部图片时,我们在顶部获得头部,然后是前额、眼睛、鼻子、耳朵、嘴、下巴等。这里,为了训练各种图像,无论是人脸、汽车还是其他候选图片,在像素的出现上是有先例的,尽管图像可能有 n 种变化。这仍然是连续的。将 CNN 应用于所讨论的问题是一种矫枉过正,因为所有的数学计算,解决方案仍然可以归结为一个简单的数学总结,增加了训练 filers、kernel & steps 的开销,此外,单词的输入维度是可变的,即。,我们不能期望像图像一样的 64*64 dimn,而是会根据数据点中的文字而变化)
提取摘要
现在来做些数学。让我们考虑每个单词的 500 维向量嵌入。对于上述规范(数据点)中的大约 20 个字,输入将是 20×500 维的输入矩阵。归一化后的总和将减少到 1x500。该矢量规范将主要倾向于输出标签。
单词嵌入的总和 x / sqrt(sum(square(x)))
与上下文关联(标签)
此外,将这个向量规格馈送到完全连接的 2 层密集 NN,其中一个隐藏层和一个表示标签数量的输出层,给出了对非时间序列数据进行分类的良好模型。
The output classified
因此,我们在这里看到一个学习算法被执行的过程,包括:
- 从应用程序接收文本数据。
- 通过单词矢量化模型处理文本数据以生成 n 维形状的向量空间,其中文本数据中的每个唯一单词被分配向量空间中的对应向量表示,其中向量空间包括文本数据中同义的唯一单词的向量表示的聚类。
- 基于向量空间中的向量表示确定归一化总和,其中归一化总和表示文本数据或文档的全维度;和
- 通过简单的 2 层密集神经网络,基于总和,向应用提供文本数据的上下文含义。
结论
总之,许多分类问题,其中需要从一组独立变量中推断出一组离散变量,可以通过以下方法解决,这些方法可以总结为 3 个简单的步骤。
- 为每个变量提供一个向量表示,这里的变量可以是一个单词、一个图像图标或任何其他原子数据点,并使用矢量化技术,根据业务上下文在 n 维空间中排列/连接所有数据点。skip-gram、cbow 和其他类似算法可以方便地导出每个数据点的合理嵌入。
- 使用上述标准化总结技术总结嵌入(合并)。归一化平均值是另一种技术,在某些情况下也可能更合适。
- 将摘要与预定义的标签相关联,并使用简单的 2 层密集神经网络来训练机器学习模型。标签可以是业务指示符,或者在对象识别的情况下是图像名称,根据分类的上下文进行标记。
使用概率模式的分类算法
分类算法使用概率核(模式),该概率核是通过对图像滤波核的结构应用二进制矩阵方法而创建的。
对人工智能和人脑感兴趣的人,你们好。根据我的错觉,我模拟了人类大脑中的神经元。根据这个模型,我创建了一个分类算法。这种算法非常有效,而且是独一无二的。我用 Python 实现了这个算法,你可以看到下面给出的测试结果。我也分享了 GitHub 中的代码,并在这里添加了链接。
为了理解所有的概念,我建议你在开始之前阅读一些主题。如果你已经有了这些方面的知识,那就继续读下去吧。
必要的先前知识:
图像过滤,神经元,矩阵(这个我就不加链接了,你到处都能找到关于矩阵的信息)
值得一提的是:
机器学习中的分类,决策树,朴素贝叶斯分类器,AdaBoost,随机森林,神经网络
1。算法代码的 GitHub 链接。
2。算法代码 GitHub 链接(改进版)
摘要
本文主要研究图像处理中滤波算法的结构。该新算法基于输入数据的二进制矩阵来创建子聚类,这些子聚类在稍后的分类中用作过滤器。这些子聚类被称为概率核。所开发的算法使用不同的数据集进行测试,并显示出不同的准确率,这取决于数据集的多样性和用于矩阵比较的匹配值。使用糖尿病数据集、乳腺癌数据集和电离层数据集来比较所开发的算法和诸如朴素贝叶斯、AdaBoost、随机森林和多层感知器之类的已知算法的性能。关于准确率的测试结果证实了所提出算法的优势。
如果你想直接跳到算法,请转到“算法一般是如何工作的?”
算法是做什么的?
所提出的算法通过训练数据来创建模型(概率核),以预测测试数据的类别。
算法的哲学是什么?
该算法是由人类大脑如何工作的感觉创建的。下面提到了人脑如何工作的假设。
确定性方法认为,如果宇宙中的所有信息都是已知的,那么未来的情况就可以完全预测出来。该算法也采用这种方法。
大脑如何工作的假设:
Figure-1 Neurons and Their Connections
属性:
- A1,A2,…指属性。
- V1,V2,…指相关属性的值。
概念:
- C1,C2,…指的是概念。
- R1,R2,……指概念的结果。
- 例如:C1:天气预报 R1:多雨,C1:天气预报,R2:晴朗
连接:
连接显示了哪些属性是每个概念的条件。联系大多是在属性和概念之间。但是它们也可以在概念之间。一些概念可以作为其他概念的属性。在这种情况下,概念将是属性,概念的结果将是该属性的值。
- C1-R1: A1-V1,A2-V1,A3-V3/A3-V4,A4-V2
- C1-R2: A1-V2/ A1-V3,A2-V2,A3-V1/A3-V2,A4-V1
- C2-R1: A1-V1/ A1-V2,A2-V1,A3-V4,C1-R2
- C2-R2:A1-无,A2-V2,A3-V2,C1-R1
规则:
- 一个概念类型有确定的属性。概念的不同结果不能有不同的属性。正如我们在上面的例子中看到的,C1 概念有 A1、A2、A3 和 A4 属性。
- 可能缺少概念的属性信息。正如我们在上面的例子中看到的,C2-R2 没有关于属性 A1 的信息。
- 一个概念可以有相同属性的不同值。
- 正如上面在“连接”标题中所解释的,概念的行为可能类似于另一个概念的属性。
结论:
理论中的每个元素和属性都被假设为人脑中的一个神经元。每个连接指的是神经元之间的一个轴突。
大脑学习新概念的过程假设如下:
- 一个概念的条件被分配给未使用的神经元。如果这个概念的条件是已知的,那么这个条件就不会分配给一个新的神经元。
- 在将条件分配给神经元之后,将要学习的概念被分配给一个新的、未使用的神经元。
- 在所有赋值完成后,具有学习值的相关条件被连接到学习过程中的概念。
- 学习新概念。
使用新神经元的条件:新神经元的使用由“或”门决定。
使用新神经元的条件:新神经元的使用由“或”门决定:
Table-1: Conditions of Employing New Neuron
废除的概念和属性:
概念和属性的废弃指的是重新设置使用过的神经元。这种废除不是人为的。该过程是自动执行的。
当一个人用属性表学习一个概念时,属性表可能是错的。为了去除错误的属性,这个人需要重复几次真正的属性和所属的概念。由此,被记录为属性的错误属性属于概念,不会有任何电信号经过。人脑会重置不传递任何电信号的神经元。
这也与人脑中遗忘概念和属性有关。如果没有使用具有属性或概念的神经元,则重置该神经元。
算法一般是如何工作的?
1.算法将单行记录转换为二维矩阵。
2.算法将创建的二维矩阵相互比较。
2.1.只有属于同一类的矩阵才能相互比较。
3.根据比较结果,算法合并相似矩阵。
3.1.合并后的矩阵在算法中将表现得像一个过滤核心。在算法中,合并矩阵被称为概率核。
3.2.在矩阵合并过程完成后,创建的概率核用于测试。
4.在测试过程中,每个测试记录也被转换成图像,并与由训练矩阵生成的概率核进行比较。
5.在测试过程的最后,我们得到一组测试结果。只有错误分类记录的测试结果被存储用于训练校正。
6.从存储的测试结果中确定导致错误分类的概率核。
7.在误导性概率核从概率核列表中移除之后,我们返回到过程 4。
7.1.这个过程一直持续到我们不再有概率核。
7.2.然后,记录该过程中给出最佳精度结果的步骤。
8.再次创建上面确定的步骤中的概率核。
9.上面创建的最佳准确率概率核被保存为预测模型。
算法具体是如何工作的?
算法将单行记录转换为二维矩阵。
该算法采用二维矩阵作为输入。这些二维矩阵是二元矩阵。下面逐步解释了转换过程:
1.将记录中的每个值映射到不会导致大矩阵的范围。例如,记录中的最大值应为 100。
2.创建具有维度的零矩阵:行-记录中的最大值,列-记录中的列数。
3.选择一条记录,取第一列©的顺序和该列(V)中的值。将 1 放入索引中的矩阵:行—我们取的值(V),列—我们取的列的顺序。
4.继续上述过程,直到映射完记录中的所有值。
算法将创建的二维矩阵相互比较。
1.创建一个比较函数。
2.在比较函数中,你需要 2 个输入。输入是将被比较的矩阵。
3.在比较函数中,将输入矩阵相乘。
4.在比较函数中,对乘法创建的新矩阵求和。
5.在比较函数中,将求和创建的新矩阵除以第一个输入矩阵中 1 的数量。结果将介于 0 和 1 之间。1 表示“最相似”,0 表示“一点都不相似”。
根据比较结果,算法合并相似矩阵。
1.取相似的矩阵,对它们求和。
2.将所有相似矩阵相加得到的新矩阵除以同一矩阵中的最大值。
在测试过程中,每个测试记录也被转换成图像,并与由训练矩阵生成的概率核进行比较。
通过合并过程,我们创建了概率核。现在,让我们将它们与测试记录进行比较。该过程如下所示:
1.将测试矩阵与每个概率核相乘。
2.将上面乘法运算创建的新矩阵中的所有值相加。这将是匹配结果。
3.将匹配结果和概率核的类别添加到列表中。
4.根据匹配结果对列表进行降序排序。
5.以上面提到的排序列表(SL)中的第一个元素的类为例。
6.取的类是算法分类的结果。
在测试过程的最后,我们得到一组测试结果。只有错误分类记录的测试结果被存储用于训练校正。
1.存储错误分类的测试结果的匹配结果和来自 SL 的概率核顺序。
从存储的测试结果中确定导致错误分类的概率核。
1.确定在排序列表(SL)中给出真实类别的概率核(TPK)的索引。
2.从概率核列表中移除 SL 中给出比 TPK 更高匹配结果的所有概率核。
在从概率核列表中移除误导性概率核之后,我们返回到过程 4。继续这个过程,直到不再有概率核。然后,监控测试结果,并找到给出最佳准确度结果的步骤。这个步骤就是你需要停止消除概率核的步骤。
实验:
对于其他算法性能,使用工具 Weka 3.7 。
二手电脑的特点:
CPU:英特尔酷睿 i7–6700 HQ 2.6 GHz(6M 高速缓存,最高 3.5 GHz)
内存:16 GB DDR4
用于新算法的编程语言:
Python 中使用的模块:
用于实验的数据集:
糖尿病数据集:训练集 500 条记录,测试集 276 条记录
乳腺癌(原始)数据集:训练集 465 条记录,测试集 233 条记录
电离层数据集:训练集 234 条记录,测试集 116 条记录
实验结果如下所示:
Table-2:Experiment Results
算法的表格和图形:
Table-3: Image Conversion
Table-4: Creation of Probability Kernels
Table-5: Prediction and Test Process
Table-6: Elimination Process of Probability Kernels
分类:足球运动员位置预测(下)
根据身体属性创建模型预测比赛位置。
Photo by Izuddin Helmi Adnan
足球是数百万人的一种生活方式。国际足联在 2006 年进行的最近一次大规模调查统计了 2.65 亿男女球员,这个数字自 2000 年以来一直以平均每年 1.5%的速度增长(此处为)。对许多人来说,操场足球曾经是/现在是一种主食。从操场到球场,比赛位置的选择通常是试图模仿你最喜欢的职业球员,找出你喜欢的位置,或者简单地跟随你的教练所说的。
为什么没有一个量化的、更聪明的方法来做这个决定呢?通过调查职业选手的属性可以了解到什么?我们能利用物理属性使合适的位置显而易见吗?
**目标:**创建一个模型,根据身体属性推荐一个打法位置。
该任务通过第 1 部分中介绍的线性技术符合分类框架。即线性回归、线性判别分析 (LDA)和二次判别分析 (QDA)和new 多项 Logistic 回归 (MLR)。
我喜欢通过例子来学习,所以虽然这在技术上是第 2 部分,但我鼓励你使用第 1 部分作为所提到的技术的参考,而不是严格的先驱。这篇文章是供好奇者阅读的。写东西很有趣,写代码也很有趣,所以尽情享受吧!
#0 —多项逻辑回归分类
在开始之前,我们先介绍最后使用的技术。多项逻辑回归分类器**(MLR)。**
MLR 是逻辑回归的多类扩展,它通过使用基本逻辑函数估计概率来模拟输入变量和二元输出变量之间的关系。我们来分析一下。
“…模拟输入变量和二进制输出变量之间的关系…”—这是一项常见任务。一个例子是将通过/失败的结果与学习/睡眠/锻炼的时间联系起来。这种类型的问题和我们在第一部中看到的没有什么不同。
“…通过估计概率…”—这是半新的。统计理论告诉我们,我们的输出变量的最佳估计是给定所有信息的期望值(回归函数)。对于二进制(0/1)输出,该期望简化为一个概率(后验概率)。线性回归、LDA 和 QDA 不严格地利用这一特性来区分类别,但是这些方法都没有强调概率估计。相反,概率估计是逻辑回归的核心。**
“…使用其底层逻辑功能。”— 这绝对是新的。逻辑函数使得估算概率更加容易!*如何?*在典型的二进制分类问题中,我们期望我们的输出是 0 或 1。我们通过估计一个概率(在 0 和 1 之间)然后选择一个阈值,任何超过这个阈值的估计都被声明为 1,否则为 0。预测这种概率是一个困难的问题,部分原因是取值范围有限(0 到 1)。因此,逻辑函数被用来将我们的概率转换成对数概率。至关重要的是,这意味着我们从试图预测 0 到 1 之间的数字,到预测范围不受限制(从负无穷大到正无穷大)的数字!为什么这有帮助?我们现在可以将我们的扩充输出变量建模为输入的线性组合,即使用线性回归形式,并在预测时反转转换以返回概率。整洁!**
# 1——赛前准备
将我们的问题形式化为一个统计友好的目标,我们寻求解决一个多分类监督学习问题。为此,我们需要数据,并特别要求我们的数据集提供:
- 反映物理属性的一组连续变量。
- 分类输出变量,玩家位置。
我们希望对职业选手的数据集进行分析。当然,身体素质会因为位置的不同而有所提高,但是我们仍然应该能够找到一个“理想”的模板。这里的假设是,美国业余选手和职业选手是有联系的。
我现在要做一个飞跃,选择使用 FIFA19 视频游戏数据集(在这里找到)。这里的假设是 FIFA19 数据集是现实生活的一个公平代理。我认为如果这不是真的,这个游戏就不会像现在这样受欢迎。除了利用 EA 的数据收集、聚合和分析(这里讨论的是基于现实世界的性能),直接的好处是数据集为每个属性提供一个值。这极大地简化了分析,但确实需要以同样的方式计算任何新的观察值。
#2 —幻灯片处理数据集
在我们开始模型预测之前,让我们澄清一些事情:
- 输入变量 —“力量”和“耐力”都取 0 到 100 之间的值。
- 类别输出变量 —“位置”采用代表 3 个关键外场位置的值{“CB”:中后卫,“CM”:中场,“ST”:前锋}。
- 训练数据集(图 1 右侧) —完整数据集的随机选择子集,将用于训练我们的分类器。为每种分类方法引入的所有辅助变量将使用训练集进行估计,所有预测将在测试集上进行。我们的分析使用 50/50 的分割,即整个数据集的 50%将用作训练集,其余 50%用作测试集。
Figure 1 — Full & Training Dataset Coloured by Position {CB, CM, ST}
目测图 1 中的完整数据集,每个类似乎没有任何清晰的界限。然而,我们确实观察到中后卫的数据分布最小,而中场球员的数据分布最大。
在数据集的分区上训练模型的一个潜在问题是引入意外偏差。因此,我们希望测试集看起来与训练集相似——事实也确实如此。每个位置的玩家数量也相对平衡,如下图 2 中的条形图所示。
Figure 2 — Test Dataset Scatterplot & Position Distribution
#3 —模型预测
直入主题,图 3 显示了线性回归、LDA、QDA 和 MLR 的决策界限。虽然测试数据集是模糊的,因为没有明显的类,但所有四种方法都预测不相交(非重叠)的类。问题:我们能开发一个预测重叠类的分类器吗?
Figure 2 — Decision Boundaries for each Classification Method
QDA 模式的灵活性显而易见。不依赖于恒定方差假设显著地改变了从 LDA 到 QDA 的分类预测边界。问题:QDA 边界不同,但更好吗?
所有四个型号在中场球员的分类区域方面是一致的,在前锋类别方面有明显的区别。问题:前锋真的在力量和耐力上处于中间位置吗?
让我们用一些数字来进行目测。表 1 显示了一些关于预测类的描述性统计数据。每个方法旁边是每个类的质心。在这种情况下,质心代表由该方法预测的每个位置类别中运动员的平均力量和耐力对。我们并不期望与数据集平均值完全匹配,而是寻找偏离真实数据集平均值的模型平均值。这不是一个包罗万象的评估标准,但它确实提供了一个粗略的衡量方法,来衡量一种方法识别每个类的不同属性值的能力,更有趣的是,它还提供了哪些类很难被捕获。
Table 1 — Class mean pairs (Strength,Stamina) for the test dataset (blue) & 4 techniques
从该表中,我们得出以下与数据集平均值相比较的观察结果(括号中是它们相对于图 2 的空间解释):
- 所有四种模型确定的中后卫平均更强壮,耐力更差(蓝色区域太靠右下方)。
- 所有四个模型确定的中场球员平均较弱(橙色区域太偏左)。
- 前锋的特点是所有方法都能很好地捕捉到的(绿色区域在空间上大约是右边)。根据数据集,前锋的平均力量和耐力实际上介于中后卫和中场球员的平均水平之间。
但这与我们的目测不符!首先,尽管橙色区域在每个模型图中视觉上是一致的,但橙色的中场位置类似乎没有正确表示真实的数据集。其次,该表表明,尽管在每个模型图中覆盖了非常不同的区域,但绿色前锋位置类确实代表了真实数据集的平均特征。
是时候深入研究正式的模型诊断了。
#4 —预测性能
可以通过评估不同的分类率来评估模型预测。我们通过一个混淆矩阵来做到这一点。表 2 显示了每个分类器的混淆矩阵。测试数据集中的每个观察值只在表中 9 个可能位置之一计数一次。如果我们知道一个特定的球员确实是中后卫,并且通过我们的分类器被归类为中后卫,我们在“真正的 CB”行和“CB”列标记一个计数。
读取线性回归分类器的第一行,在测试数据集中的 880 名中后卫中,519 名被识别为中后卫,73 名被错误地分类为中场球员,还有 288 名被错误地分类为前锋。
Table 2— Absolute Confusion matrix for each technique
我们从混淆矩阵中提取的第一个指标是每个分类器正确的频率,即测试数据集中有多少真正的中后卫、中场球员和前锋被正确识别。这被称为模型的精度。**
线性回归= 1244/2662 = 46.0%
LDA = 1341/2662 = 50.4%
QDA =1355/2662 = 50.9%
MLR = 1325/2662 = 49.8%
虽然所有模型的表现都比随机猜测要好(33%的正确率),但在 50%左右,没有一个模型能给我们留下特别深刻的印象。QDA 无疑是最准确的模型,但是我们仍然对每个模型如何逐类预测知之甚少。
因此,下一个要检查的指标是每个模型的精度。当一个模型预测一个特定的球员是中后卫或中场球员或前锋时,它有多准确?
线性回归= 50.5% (CB: 52.9% CM: 56.6%,ST: 41.9%)
LDA = 51.0% (CB: 54.5%,CM: 57.0%,ST: 41.5%)
QDA = 51.3% (CB: 53.1%,CM: 57.4%,ST: 43.4%)
劳动生产率= 51.1% (CB: 54.2%,CM: 57.4%,ST: 41.7%)
同样,QDA 再次成为平均最精确的模型,与其他模型的不同之处在于对前锋的精确度更高。还要注意的是,所有的模型对于射手类来说精度都很差。
我们关注的最后一个指标是召回。回忆是真实比例中后卫/中场球员/前锋的正确认定。准确性代表模型正确捕获的事实的总体比例,但回忆识别在逐类水平上正确捕获事实的弱点。**
线性回归= 51.4% (CB: 59.0%,CM: 59.8%,ST: 35.4%)
LDA = 50.6% (CB: 47.8%,CM: 53.6%,ST: 50.5%)
QDA =53.1% (CB: 69.1%,CM: 58.1%,ST: 32.0%)
MLR = 50.3% (CB: 50.0%,CM: 53.4%,ST: 47.4%)
令人印象深刻的是,69.1%的真正中后卫被 QDA 分类器正确识别。然而,这是以落后于 32%的前锋识别为代价的。我们在线性回归中看到类似的模式,而 LDA 和 MLR 在所有位置上显示出一致的回忆。
QDA 的表现是这次测试中最极端的。我们早些时候质疑过 QDA 的灵活边界是否提供了更好的预测,如果我们更喜欢捕捉真正的中后卫而不是其他级别,QDA 将是我们检查的每个指标的突出赢家。在这种情况下,我们没有职业偏好,虽然平均 QDA 召回率是最高的,但我们可以看到该模型实际上很难识别真正的罢工者(32%)。考虑到这一点,除了 MLR、LDA 和 QDA 之间在精度和准确度上的微小差异之外,选择 MLR 或 LDA 作为这里的最佳模型同样是合理的。
准确度、精确度和回忆一起使用是强有力的指标——它们揭示了模型捕捉真相的程度和可变性。
# 5——力量、耐力、& …?
在任何情况下,大约 50%的得分是不够的。我们能通过加入更多的输入变量来改善这些平淡无奇的结果吗?考虑下面的模型,
- 输入变量 —力量、耐力、加速度、平衡、反应、冲刺速度、跳跃。每个取 0 到 100 之间的值。
- 类别输出变量 —位置:取代表 3 个关键外场位置的值{'CB ‘:中后卫,’ CM ‘:中场,’ ST ':前锋}。
Table 3— Absolute Confusion matrix for each technique
Table 4 — Accuracy, Precision & Recall
我们最初的输出保持不变,但是我们现在有了更多的特征供我们的模型学习。表 3 中的混淆矩阵显示了改进(在主对角线上有更多的数字)。我们的三个指标准确度、精确度和召回率都提高到了 66%-67%左右。
QDA 仍然是表现最好的模型,但四个模型的准确度、精确度和召回率之间的边际差异甚至更小(表 4)。
我们看到,只有两个输入变量,QDA 的回忆分解是差的前锋类。随着输入变量的增加,不同类别之间的回忆更加一致——CB:68.1%,CM: 71.5%,ST: 64.4%。
#6 —风险值(模型考虑)
给定 FIFA19 数据集,我们开发了 4 个模型来根据身体特征对足球运动员的位置进行分类。我们用 QDA 找到了最精确的模型。在测试数据集中,67.5%的玩家在他们的正确位置上被识别,并且模型预测在 67.7%的时候是正确的。
我们的 VAR 时刻到了。
回想一下,QDA 和 LDA 对基础数据集都有严格的分布假设(高斯生成分布),我们还没有验证这些假设是否成立。如果不这样做,我们必须谨慎地宣布最终的模型是最好的。我们知道我们的模型需要高斯数据,但实际上,我们的数据分布很少为人所知。这推动了正态性测试(此处)的发展,旨在验证一个给定的数据集是否可以被高斯分布很好地建模。只有到那时,我们才能给需要这种假设的统计模型开绿灯。
考虑到这一点以及我们目前缺乏正态性测试,我们宣布 MLR 为我们分析的匹配获胜者。只要给定 7 个物理属性,我们的模型就可以对玩家的位置做出大约 3 分之 2 的正确预测!这根本不需要在数据集上手动定义任何规则或限制。
# 7——赛后结论
- 假设 —一般来说,MLR 被认为比 LDA/QDA 更安全、更稳健,依赖于更少的假设。然而,在我们知道数据集满足这些假设的情况下,我们可以更有信心应用 LDA 或 QDA。我们已经看到两者给出了非常相似的结果。虽然我们认为 MLR 在这里是最合适的,但这绝对不是所有分析都适用的情况,每个数据集都有自己的特点。
- 线性与非线性决策边界 —线性决策边界由逻辑回归和 LDA 生成,因此当真正的决策边界是线性时,这两种方法往往表现良好。或者,QDA 提供非线性的二次决策边界。因此,当决策边界是适度非线性时,期望 QDA 给出更好的结果。
- 垃圾输入,垃圾输出 —我们模型的性能取决于我们提供给它们的数据。更多的数据通常意味着更好的性能。“更多”可能意味着额外的观察或额外的输入变量数据。仅仅通过加入更多的物理信息,我们就将模型的性能提高了近 20%!
扩展:有没有更好地处理重叠类的方法?哪些物理属性最重要?我们能扩大班级的数量以容纳更多的职位吗?
代号:https://github.com/93tilinfinity/Medium-Classification
Tensorflow 2.0 中的分类模型构建和跟踪
上个月发布了 Tensorflow 2,通过紧密集成高级 keras、清除冗余 API、将急切执行保留为默认、移除全局和使用函数而不是会话,使模型开发和部署更加容易。
在本文中,我们将使用 Tensorflow 2.0 为 MNIST 数据集构建一个 CNN 分类器,并使用 Tensorboard 跟踪模型性能,同时牢记所有最佳实践。
第一步是安装 Tensorflow 2.0。建议的方法是安装在虚拟环境或 docker 中。要通过虚拟环境安装,你可以按照这个链接。
现在我们将文章分成两部分:
- CNN 模型大楼。
- 用张量板跟踪模型。
CNN 模型构建
首先,我们需要导入所需的库。
from tensorflow.keras import Modelfrom tensorflow.keras.layers import Dense, Flatten, Conv2Dimport tensorflow as tf
确保您已经安装了 numpy 版本 1.16.4,否则会出现如下用户警告:
FutureWarning:不赞成将(type,1)或“1type”作为类型的同义词进行传递。在 numpy 的未来版本中,它将被理解为(type,(1,))/ '(1,)type '。NP _ resource = NP . dtype((" resource ",np.ubyte,1)) s
现在,我们将从 tf.keras API 中获取可用的 mnist 手写数字数据集。训练集和测试集分别有 6 万幅和 1 万幅图像。每张图片都是尺寸为 28*28 的黑白图片。然后,我们将样本从整数转换为浮点数,以提供给模型。
mnist = tf.keras.datasets.mnist(x_train, y_train), (x_test, y_test) = mnist.load_data()x_train, x_test = x_train / 255.0, x_test / 255.0print(x_train.shape)
# (60000, 28, 28)
因为我们要将输入馈送给 CNN,所以输入形状必须是特定的格式。对于 Tensorflow,格式应为(批次、高度、宽度、通道)。因此,我们将添加一个新的轴来格式化 CNN 的输入。
# Add a channels dimension
x_train = x_train[..., tf.newaxis]
x_test = x_test[..., tf.newaxis]print(x_train.shape)
# (60000, 28, 28, 1)
然后我们将使用 tf.data API 对数据集进行洗牌和批处理。这是一个非常方便的 API,可以设计到生产模型的输入数据管道。对于洗牌,我们将使用。来自 tf.data.Dataset API 的 shuffle()。它通过用buffer_size
元素填充一个缓冲区来随机打乱所提供的数据集的元素,然后从这个缓冲区中随机抽取元素,用新元素替换所选的元素。考虑到机器的内存限制,我们使用了大小为 10000 的缓冲区。
#tf.data to batch and shuffle the datasettrain_ds = tf.data.Dataset.from_tensor_slices((x_train,y_train))\
.shuffle(10000).batch(32)test_ds = tf.data.Dataset.from_tensor_slices((x_test,y_test))\
.batch(32)print(train_ds.element_spec)#(TensorSpec(shape=(None, 28, 28, 1), dtype=tf.float64, name=None), TensorSpec(shape=(None,), dtype=tf.uint8, name=None))
因为我们是以 32 为一批,所以最后一批将少于 32 个元素,如果你愿意,可以通过将drop_remainder=True
传递给。批处理()
现在我们有了数据,是时候定义模型架构了。为此,我们将使用 Keras 模型子类 API 。另一种方法是使用 keras functional API,尽管子类化 API 更适合生产。
class MyModel(Model):
def __init__(self):
super(MyModel, self).__init__()
self.conv1 = tf.keras.layers.Conv2D(32, 3, padding='same', activation='relu')
self.pool = tf.keras.layers.MaxPooling2D(pool_size=(2, 2), strides=2, padding='same')
self.flatten = tf.keras.layers.Flatten()
self.d1 = tf.keras.layers.Dense(256, activation='relu')
self.d2 = tf.keras.layers.Dense(128, activation='relu')
self.d3 = tf.keras.layers.Dense(10, activation='softmax') def call(self, x):
x = self.conv1(x)
x = self.pool(x)
x = self.flatten(x)
x = self.d1(x)
x = self.d2(x)
x = self.d3(x)
return x
现在,我们需要一个优化器和损失函数来训练模型。
# optimizer and loss function to train
loss_object = tf.keras.losses.SparseCategoricalCrossentropy()
optimizer = tf.keras.optimizers.Adam()
因为我们将标签作为整数提供,所以我们将使用稀疏分类交叉熵作为损失函数。
我们还将在每个时期后跟踪训练和测试数据的损失和准确性。
# metrics to measure the loss and the accuracy of the modeltrain_loss = tf.keras.metrics.Mean(name='train_loss')
train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(
name='train_accuracy')test_loss = tf.keras.metrics.Mean(name='test_loss')
test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(
name='test_accuracy')
现在,我们需要定义训练和测试方法来开始模型构建。
@tf.function
def train_step(model, optimizer, images, labels):
with tf.GradientTape() as tape:
predictions = model(images)
loss = loss_object(labels, predictions)
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
train_loss(loss)
train_accuracy(labels, predictions)@tf.function
def test_step(model, images, labels):
predictions = model(images)
t_loss = loss_object(labels, predictions)
test_loss(t_loss)
test_accuracy(labels, predictions)
在上面的代码中,有很多来自 Tensorflow 1 次的不同内容。
首先关于 tf.function,
在 TensorFlow 2.0 中,默认情况下会打开急切执行。用户界面直观而灵活(运行一次性操作更容易、更快),但这会以牺牲性能和可部署性为代价。
tf.function
构造一个 callable,它执行一个通过跟踪func
中的张量流操作创建的张量流图([tf.Graph](https://www.tensorflow.org/api_docs/python/tf/Graph)
)。这允许 TensorFlow 运行时在由func
定义的计算中应用优化和利用并行性。所以,建议用于生产。
第二件事是 tf。GradientTape(),
启用快速执行后,为了反向传播错误,您必须跟踪计算的梯度,然后将这些梯度应用到优化器。
显然,Tensorflow 可以跟踪每个 tf.Variable 上的每个计算的每个梯度。然而,这可能是一个巨大的性能瓶颈。它们公开了一个渐变带,以便您可以控制代码的哪些区域需要渐变信息。
现在,我们必须实例化一个模型对象并输入数据来训练模型。我们必须跟踪测试数据的损失和准确性来评估模型。出于演示目的,我们在此仅使用了 5 个时期。
EPOCHS = 5# Create an instance of the model
model = MyModel()
for epoch in range(EPOCHS):
for images, labels in train_ds:
train_step(model, optimizer, images, labels) for test_images, test_labels in test_ds:
test_step(model, test_images, test_labels) template = 'Epoch {}, Loss: {}, Accuracy: {}, Test Loss: {}, Test Accuracy: {}'
print(template.format(epoch+1, train_loss.result(),
train_accuracy.result()*100,
test_loss.result(),
test_accuracy.result()*100))#output
Epoch 1, Loss: 0.14411139488220215, Accuracy: 95.71333312988281, Test Loss: 0.04969984292984009, Test Accuracy: 98.44999694824219
2019-11-24 18:54:43.808050: W tensorflow/core/framework/cpu_allocator_impl.cc:81] Allocation of 376320000 exceeds 10% of system memory.
Epoch 2, Loss: 0.04587719216942787, Accuracy: 98.59500122070312, Test Loss: 0.06183547526597977, Test Accuracy: 98.04000091552734
Epoch 3, Loss: 0.029635081067681313, Accuracy: 99.05333709716797, Test Loss: 0.04673100262880325, Test Accuracy: 98.54000091552734
Epoch 4, Loss: 0.020683910697698593, Accuracy: 99.32167053222656, Test Loss: 0.04771297425031662, Test Accuracy: 98.6199951171875
Epoch 5, Loss: 0.01358255185186863, Accuracy: 99.57833099365234, Test Loss: 0.05094970762729645, Test Accuracy: 98.58000183105469
如果我们看到 5 个时期,它得到了大约 98%以上的准确性。
用张量板跟踪模型
Tensorboard 是一个非常方便的图形可视化工具,可以实时跟踪模型训练进度。它可以用于多种用途,从检查模型架构到跟踪模型训练中超参数的行为。这里,我们将通过一个简短的例子来检查模型在训练时的准确性和损失。
我们需要首先启动一个 Tensorboard 实例来显示数据。
import datetime
from tensorboard import programtb = program.TensorBoard()
tb.configure(argv=[None, ‘ — logdir’, “.”])
url = tb.launch()
print(“tensorboard url: “, url)#tensorboard url: [http://localhost:6007/](http://localhost:6006/)
现在可以在 http://localhost:6007/ 查看 Tensorboard。如果你去那里,你会看到一个像这样的屏幕:
这是因为 Tensorboard 通过读取所提供位置的日志目录来显示数据。在 tb.configure 中,我们已经提供了当前的工作目录。所以它会创建一个日志目录,并写入要在 Tensorboard 上显示的数据。因此,我们将首先为培训和测试创建一个日志记录器
current_time = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
train_log_dir = 'logs/gradient_tape/' + current_time + '/train'
test_log_dir = 'logs/gradient_tape/' + current_time + '/test'
train_summary_writer = tf.summary.create_file_writer(train_log_dir)
test_summary_writer = tf.summary.create_file_writer(test_log_dir)
然后,我们必须将每个 epoch 结果写入训练和测试中,以使它们可视化。
for images, labels in train_ds:
for images, labels in train_ds:
train_step(model, optimizer, images, labels)
with train_summary_writer.as_default():
tf.summary.scalar('loss', train_loss.result(), step=epoch)
tf.summary.scalar('accuracy', train_accuracy.result(), step=epoch) for test_images, test_labels in test_ds:
test_step(model, test_images, test_labels)
with test_summary_writer.as_default():
tf.summary.scalar('loss', test_loss.result(), step=epoch)
tf.summary.scalar('accuracy', test_accuracy.result(), step=epoch)
现在,当训练正在进行时,如果您想查看结果,只需访问 Tensorboard url,屏幕将如下所示:
在 Tensorboard 中可以看到许多关于模型的东西,这不是本文的一部分。
本文的代码可以在以下位置找到:
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/sambit9238/Deep-Learning/blob/master/mnist.py)
参考资料:
[## 面向专家的 TensorFlow 2 快速入门| TensorFlow 核心
这是一个谷歌合作的笔记本文件。Python 程序直接在浏览器中运行——这是学习和研究的好方法
www.tensorflow.org](https://www.tensorflow.org/tutorials/quickstart/advanced) [## 分析 tf.function 以发现签名的优势和微妙之处——第 1 部分
AutoGraph 是 Tensorflow 2.0 最激动人心的新特性之一:它允许转换 Python 语法的子集…
pgaleone.eu](https://pgaleone.eu/tensorflow/tf.function/2019/03/21/dissecting-tf-function-part-1/)
PyTorch 手机图库图像分类
A Meme which is going to be used to train an Image Classifier !
我经常发现自己的移动厨房一团糟。我喜欢我的移动画廊井然有序。这就是我脑海中出现构建移动图库图像分类器的想法的原因!
这个迷你项目对我来说听起来像是一个基于深度神经网络的图像分类器的很好的实际应用。这将是一个有趣的经验,建立自己的移动画廊图像分类器。
所以让我们开始吧!😄
步骤 1:构建自定义数据集
我们需要列出我们希望图像分类器输出结果的所有类别。
由于这是一个移动画廊图像分类项目,我会选择我经常遇到的类别,而通过我的移动画廊。
以下是我选择的课程-
- 汽车
- 模因
- 山脉
- 自拍
- 树
- Whataspp _ 截图
一旦我们有了所有需要的类的列表,我们就必须为这些类收集图像。
收集图像数据有几种不同的方式
- 手动收集-我们可以使用手机图库中的现有图片,也可以点击我们列为目标类别的图片。
- Web scraping——有很多方法可以从 web 上抓取图像。这是一个这样的 python 脚本,可用于下载特定类的图像。
一旦图像被下载,我们必须把它们分成不同的目录。因此,我们有 6 个目录,包含各个类别的图像。
上面提到的两种数据收集方法,我个人都用过。我确实用 python 脚本下载了大部分课程的图片,这种脚本可以很容易地在像 stackoverflow 这样的网站上找到。但由于我在网上找不到 whatsapp 截图的好看图片,我只好从手机里收集。
对你们来说是个好消息!我已经建立了一个移动图库图像数据集,我将在这个博客中使用它,并且我已经将它公之于众!所以你们可以免费使用它,而不用麻烦为自己做一个:D
下载数千个项目的开放数据集+在一个平台上共享项目。探索热门话题,如政府…
www.kaggle.com](https://www.kaggle.com/n0obcoder/mobile-gallery-image-classification-data)
您可以下载上面提到的数据集,并将其提取到根目录中,这样 python 脚本或 jupyter 笔记本文件就与数据集目录位于同一个目录中。确保数据集目录的路径如下所示。
以下是移动图库影像数据集中的一些样本。
These are few of the sample images taken from the training data from the Mobile Image Gallery Dataset. Their respective classes: Memes (top-left), Cars (top-right), Trees (bottom-right) and Mountains (bottom-left)
步骤 2:数据预处理和制作数据加载器
数据集准备好之后,我们需要做的下一件事就是做一些数据预处理。对于数据预处理,我的意思是执行一些简单的图像处理操作,如调整大小、在水平轴上随机翻转图像、将图像(具有范围从 0 到 255 的整数值的像素)转换为张量(具有范围从 0.0 到 1.0 的浮点数的像素值),以及最后但并非最不重要的,通过使用 ImageNet 统计数据(mean = [0.485,0.456,0.406),std = [0.229,0.224,0.225 请注意,我们正在处理 BGR (彩色)图像,而不是灰度(黑白)图像。
接下来,我们通过使用数据路径和我们想要应用于图像数据的转换/预处理来创建数据集对象。
我们通过定义分割百分比将数据集随机分割成训练数据集和验证数据集。
最后,我们通过定义批量大小来训练和验证数据加载器对象。通过利用矩阵乘法,这将使训练和验证过程惊人地快。
步骤 3:定义一个合适的模型并进行必要的调整
首先,我们将使用基于卷积神经网络的架构,因为在处理图像或任何具有空间关系的数据时,没有什么能打败 CNN。既然已经有这么多基于 CNN 的经过测试的架构,我们就不需要试验新的架构了。
我们不打算自己编写基于 CNN 模型的架构,而是使用现有的架构。这样做有两个主要原因-
- 这些体系结构已经在各种数据集上进行了成功的尝试和测试,并显示出了很好的结果。
- 这些架构已经在巨大的公共数据集上进行了训练,并且它们的预训练权重已经公开。
在火炬视觉中有很多预训练的模型可用,像 AlexNet、Resnet、VGG、InceptionNet、DenseNet 等。可供选择。我们选择名为 Resnet34 的型号,因为它既不太深也不太浅。如果你打算在一台没有 GPU 卡的机器上使用它,在训练时它应该不会占用很多计算能力。
但是有一点我们必须注意。这种基于 CNN 的架构的最后一个线性层中的神经元数量代表了我们的数据集中存在的所有类别。最初,Resnet34 用于在具有 1000 个类的 ImageNet 数据集上进行训练。但是我们希望这个模型只输出数据集中的类的数量的预测(在我们的例子中是 6 个)。因此,我们简单地用具有 6 个神经元的新线性层替换该模型的最后一个线性层,输出对 6 个类别的预测。
步骤 4:通过冻结和解冻层来转移学习
值得注意的是,由于我们使用的是预训练模型,它的过滤器或内核已经学会识别某些功能。让我更详细地解释给你听,这些过滤器已经学会识别的特征到底是什么。
初始卷积层中的滤波器学习简单和基本的特征,如边缘、颜色和纹理;中间层的孩子可能会学习圆形和多边形等形状;而较深层中的过滤器学习更复杂的图案,如脸或花瓣等。看下面的图片会更清楚。
Low-Level Features include edges, colors, textures. Mid-Level Features include simple shapes and geometries. High-Level Features include complex shapes and objects like faces, flowers etc.
显然,我们可以利用初始层和中间层中的过滤器,因为我们需要它们来识别输入图像中的边缘、颜色、纹理和简单形状。我们可能不想保留最后几个卷积和线性层中的滤波器。所以我们训练模型……或者我应该说,我们应该在我们的自定义数据集上对模型进行微调,只对最后几层(无论是卷积层还是线性层)进行少量学习。
我们看到所有的参数在开始时都是可训练的(requires_grad = True 表示参数是可学习的)
让我们看看层的名称是什么,以便我们可以冻结最后两个层
所以我们冻结了所有的层,只保留了网络末端的几层。这将确保只有网络末端的未冻结层得到微调,而其他层保持不变。
让我们打印出所有参数的 requires_grad,并确保已经进行了所需的更改
是的,改变已经发生了!(参见,“层 4”和“fc”中的参数 requires_grad = True,其余所有参数 requires_grad = False)
步骤 5:损失函数和优化器
好的。我们已经准备好将数据输入到模型中,模型将返回预测结果。但是我们如何知道预测是否正确呢?
这就是损失函数发挥作用的地方!😄
只有当我们有两个事物的标量表示时,我们才能比较它们。而损失函数给了我们一个标量,这让我们有可能进行比较。就这么简单: )
但是,我们如何确保在训练模型时损失不断减少,并在每次迭代中使预测越来越好呢?
是,你找对人了!优化程序来拯救 B)
交叉熵损失是全世界用于解决多类分类问题的标准损失函数。Adam optimizer 是最受欢迎的优化程序选择之一。
我们现在都准备好了。开始训练吧!!!
步骤 6:培训和验证
在完成了所有的数据预处理工作、挑选了一个合适的模型、冻结了一些层、选择了损失函数和优化器之后,我们终于准备好开始训练我们的神经网络了。
让我们释放 Resnet34,让它从训练数据中吸收所有它能吸收的东西!
查看 本博客 了解更多关于保存已学权重或整个训练模型(包括权重)的方法。
Loss Plots after training for 5 epochs
但是等等!我有一个小技巧想和你们分享。我们训练网络几个时期,然后我们冻结所有的层,除了最后的线性层。您可能想知道为什么需要这一步。
你们还记得吗,我们已经丢弃了预训练模型的最后一个线性层,并添加了一个新的层,其神经元数量等于我们自定义数据集中的类的数量。当我们这样做时,最后的线性层的权重被随机初始化,一旦所有的卷积层被训练(能够从输入图像中提取不同的特征),这需要被适当地训练。
以下是冻结第 4 层后的损失图。
Loss Plots after freezing Layer 4.
这一招在实践中很管用。训练网络有标准的方法,但没有硬性的规则。所以你可能想尝试一下训练程序。请在下面的评论区分享你们的方法,让我知道你们是否通过其他训练程序取得了好的结果。
第七步:测试时间到了!
我们已经在移动画廊图像的自定义数据集上训练了我们的神经网络,现在它应该将任何给定的图像分类到它已被训练的数据集中存在的 6 个类别之一。
现在,我们需要做的就是读取测试图像,对它进行我们在训练网络时对图像所做的同样的预处理,并希望看到一些好的预测从我们的网络返回。
Test Image
它似乎工作得很好!😄
恭喜你!你刚刚做了一个移动画廊图像分类器:D,这只是一个使用图像分类器的想法。您可以使用图像分类器来构建各种创造性的应用程序。唯一的限制是你的想象力。
我希望你喜欢读这篇博客,就像我喜欢写它一样: )
如果您有任何疑问或问题,或者如果您想与我合作某个计算机视觉项目,请随时联系我。
干杯,下次再见!😄
我强烈推荐你们叉这个public Kaggle kernel并摆弄代码,以获得它的感觉!
或
也许下载包含 jupyter 笔记本文件的 G itHub 仓库 。
我写这篇博客是因为我通过阅读别人的博客学到了很多东西,我觉得我也应该尽可能多地写下并分享我的学习和知识。所以请在下面的评论区留下你的反馈。此外,我是写博客的新手,所以任何关于如何提高我的写作的建议将不胜感激!😄
我也是一个独立的音乐艺术家,喜欢在空闲时间演奏和录制音乐。也许你可以看看我的艺人页面,表示支持:)
Spotify 上的 8 楼和声!
利用 Foursquare 数据对莫斯科地铁站进行分类
本帖是 Coursera IBM 数据科学专业专精 的压轴项目。完整的笔记本和数据在Github上有。
介绍
莫斯科地铁有 264 个车站,是世界上最大的公共交通系统之一。每天有超过 600 万人使用它。
对于这个项目,我们想看看地铁站周围的社区,并对它们进行分类。一些街区主要是住宅区,其他的周围有更多的商业区。离车站最近的场馆决定了人们使用它的原因和方式。例如,如果附近没有专业场所,居民可能会去其他地方工作。这造成了人们的日常迁移。
通过分析这些数据,我们可以根据站点的主要用途对其进行分类。这些数据有助于城市规划者确定人们最有可能去哪里工作和休闲,规划网络的进一步扩展和寻找新的发展空间。
数据
我们需要车站位置和离车站最近的场地的数据。
- 站点列表及其地理坐标——从这个维基百科页面上刮下来的。
Stations data
2.Foursquare API 探索每个车站周围的场地类型。Foursquare 用更多的子类别概括了这些高层次的场馆类别。
- 艺术和娱乐(4d4b7104d754a06370d81259)
- 学院和大学(4d4b7105d754a06372d81259)
- 事件(4d4b7105d754a06373d81259)
- 食品(4d4b7105d754a06374d81259)
- 夜生活场所(4d4b7105d754a06376d81259)
- 户外和娱乐(4d4b7105d754a06377d81259)
- 专业和其他地方(4d4b7105d754a06375d81259)
- 住所(4e67e38e036454776db1fb3a)
- 商店和服务(4d4b7105d754a06378d81259)
- 旅行和运输(4d4b7105d754a06379d81259)
我们将查询每个站点周围 1000 米半径范围内每个类别的场馆数量。选择这个半径是因为 1000 米是一个合理的步行距离。
方法学
我们可以使用 Foursquare explore API 和类别 ID 来查询特定半径内每个类别的场馆数量。响应包含指定坐标、半径和类别的 totalResults 值。样品要求(1000 米半径和类别专业&其他地方):
GET https://api.foursquare.com/v2/venues/explore?client_id={{client_id}}&client_secret={{client_secret}}&v={{v}}&ll=**55.7662,37.5692**&radius=**1000**&categoryId= **4d4b7105d754a06375d81259**
回应:
{
"meta": {
"code": 200,
"requestId": "5cfec0e31ed21914c1db7dd0"
},
"response": {
"suggestedFilters": {
"header": "Tap to show:",
"filters": [
{
"name": "Open now",
"key": "openNow"
}
]
},
"headerLocation": "Presnensky",
"headerFullLocation": "Presnensky, Moscow",
"headerLocationGranularity": "neighborhood",
"query": "professional",
**"totalResults": 132,
<...>
}**
我们已经获得了每个站点的数据。完整的 csv 可在 Github 上获得。
Count of venues of each category in a 1000m radius for each station
探索性分析和基本清理
我们来看数据。例如,我们可以看到,Turgenevskaya 站拥有最多的专业和其他场所(192),而 Belokamennaya 站则为 0。
让我们以箱线图的形式显示场馆的数量(显示平均数量、分布和异常值)。
Boxplots of number of venues in each category
我们可以看到,最常见的场所类别是食品、商店和服务以及专业和其他场所。事件只有很少的数据,所以我们将丢弃它。
数据准备
让我们使用最小-最大比例来标准化数据(场馆数量从 0 到 1 的比例,其中 0 是一组中的最低值,1 是最高值)。这既规范了数据,同时也提供了一个易于解释的分数。缩放后的图表如下所示:
Boxplots of scaled number of venues in each category
使聚集
我们将使用 k 均值聚类。这些是不同组数的初步结果:
- 两个集群显示了住宅区/商业区的分界线
- 3 个集群增加了市区内的集群
- 4 聚类还识别具有非常少数量的场所的邻近区域
- 5 和更多的集群很难解释
Clustering with 2, 3 and 4 clusters.
最后,让我们选择 4 个集群(0 到 3)。让我们用箱线图来直观地展示集群的轮廓。
Clusters and their relative count of venues
并将它们绘制在地图上(在https://theptyza.github.io/map_moscow_metro_foursquare/map/可获得完整的交互式地图)。
Clusters map. Cluster 0 is Blue, 1 is Green, 2 is Yellow and 3 is Red.
对于每个电视台,我们将显示排名前 3 的场馆类别及其在该类别中的 0 到 1 分。
Sample showing scores in top 3 venue categories
结果
下面是我们如何通过查看场地得分来描述集群的特征:
- 第 0 组(蓝色)在所有场馆类别中得分一直很高。这是这个城市发展最多样化的地区
- 群组 1(绿色)在专业和其他地方得分最高。这是城市的商业区。
- 第 2 组(橙色)得分较低,在职业、居住和商店及服务方面得分最高。
- 集群 3(红色)的分数普遍较低。这些似乎是不发达地区。
在地图上绘制集群向我们展示了:
- 0 区是这座城市最古老的中心部分
- 集群 1 也在市中心。这些车站大多位于环线内或附近,交通便利。
- 集群 2 和集群 3 的地理分布并不明显。第 3 组区域倾向于在郊区,但是一些区域位于更中心的位置。
一些站点被归类为第 3 类,尽管它们更靠近中心,更容易接近。这可能是关闭和废弃工厂的“锈带”的遗产。最近开通的莫斯科中央环线的许多车站都属于这一类。这些都是商业和住宅开发的黄金区域。
讨论
公平地说,Foursquare 的数据并不包罗万象。数量最多的场馆是在食品和商店及服务类。数据没有考虑场地的大小(例如,大学建筑吸引的人比热狗摊多得多——每个人仍然是一个正方形的“场地”)。
结论
Foursquare 的数据有限,但可以提供一个城市发展的洞察力。这些数据可以与其他来源(如城市居民人数数据)结合起来,以提供更准确的结果。
使用 CNN 对签名和文本图像进行分类,并在 Google Cloud ML 引擎上部署该模型
众所周知,对于任何具有法律重要性的文件,无论是合同、货物还是简单的表格,签名都是重要的组成部分。签名提供了识别和确认。目前,从印刷文本数据中识别签名的模型还不可用。所以这里介绍的工作是关于签名和文本数据的分类。
使用 TensorFlow 的高级 APIKeras构建分类模型,TensorFlow 是用于机器学习的开源库。该分类模型还可以帮助建立文档图像的签名检测模型。
数据准备
数据集是通过从文档中提取文本并从不同的文档中捕获签名样本而生成的。数据由两类组成:签名(类标签 0)和文本(类标签 1)。
文本图像的数据包含具有不同背景、高度、宽度和笔划粗细的独立单词的图像。文本的图像不限于一种独特的语言,还涉及多语言文本。该数据包含大约 2000 张不同的图像。
签名图像数据包含大约 1300 个不同背景、高度、宽度和笔画粗细的签名图像。
数据已经存储在谷歌云存储上。数据清理的初步步骤包括丢弃模糊的图像,并用适当的填充和边距重新排列文本。为了增加数据的大小,执行了一些运行时数据扩充,如旋转、重新缩放和缩放操作。数据集分为 70%用于训练,30%用于验证。除此之外,还有一个单独的看不见的数据集,在其上测试模型的准确性。
The sample of images in dataset
博客组织如下:
第一部分:独立的分类模型,可以在单独的系统上运行。
第二部分:通过在 GCP ML-Engine 上部署模型,使模型公开。
一.分类模式
深度卷积神经网络采用序列模型构建。有三个卷积图层以及一个完全连接的图层,后跟一个输出图层。CNN 参数,如最大池大小被设置为(2,2),内核大小被设置为(3,3)。最初,过滤器的数量设置为 32。在随后的卷积层中,过滤器的数量加倍。
使用的激活函数是 ReLU,最后一层激活函数是 Sigmoid。添加一个丢弃概率为 0.5 的丢弃层。该模型的架构如下:
模型概要给出了每一层的详细情况以及每一层中的参数总数。模型总结如下:
Summary of the model
接下来,以准确性作为评价指标,以损失作为二元交叉熵,以优化器作为 adam 优化器,对模型进行编译。
由于训练数据大小有限,在 ImageDataGenerator() 函数的帮助下增加了运行时图像扩充。在训练数据集中添加了图像增强,如旋转、缩放和缩放。
为了预测模型对测试数据集的输出,使用了 predict 方法。然后使用 sklearn.metrics 从预测中计算精度、召回率和测试准确度。
添加图像增强和剔除图层后的最终测试准确率为 94.29%。签名图像的准确率为 96.55%,召回率为 97.22%。下表给出了通过添加增强层和下降层的结果升级。
Results of various experiments
二。在 Google Cloud ML 引擎上训练和部署模型
Cloud ML Engine 有助于大规模训练您的机器学习模型,在云端托管训练好的模型,并使用模型对新数据进行预测。
数据
通过拍摄不同语言和不同背景的签名图像和文本图像来准备数据。如前所述,对数据进行同样的预处理。有两个类,签名和文本。
包装模型
准备在 ML 引擎上部署的模型的包架构如下所示。
Project structure for ML Engine
a) setup.py
setup.py 文件包含模型在 cloud ML 引擎上运行所需安装的依赖项和版本。云 ML 引擎内置 tensorflow 支持。需要安装所有其他要求。
b)任务. py
task.py 文件是模型的入口点。它包含运行模型时需要解析的参数列表。它还调用模型和其他相关文件(如果有的话)。训练好的模型以. hdf5 格式保存。task.py 文件的代码如下所示:
**注意:**保存的模型为. hdf5 格式。为了部署模型,我们需要。模型的 pb 格式。为此,我们需要导出带有 TensorFlow 服务的模型。
c)模型. py
model.py 包含要训练的实际模型。它将编译后的模型返回给调用函数。模型函数的代码如下所示。
utils.py
这个文件包含数据预处理的代码。传递包含图像文件的目录的位置,并生成可以提供给模型的带标签的数据。数据保存在中。npy 文件,然后用于模型训练。
训练模型
a)本地培训
在本地机器上训练模型有两种方法:使用 python 命令和使用 gcloud 命令。
$ export JOB_DIR=/path/to/job/dir
$ export TRAIN_DIR=/path/to/training/data/dir #either local or GCS#Train using pythonpython -m trainer.task \
--train-dir=$TRAIN_DIR \
--job-dir=$JOB_DIR#Train using gcloud command line tool$ gcloud ml-engine local train --module-name=trainer.task \
--package-path=trainer/ \
--train-dir=$TRAIN_DIR \
--job-dir=$JOB_DIR
b)向谷歌云提交作业
在本地成功训练模型后,下一步是将作业提交给云 ml-engine。从您的教练包所在的目录运行下面给出的命令。
$ export BUCKET_NAME="your GCS bucket name"
$ export JOB_NAME="name of your job"
$ export OUTPUT_PATH=gs://$BUCKET_NAME/$JOB_NAME
$ export TRAIN_DATA=/path/to/dataset#gcloud command line$ gcloud ml-engine jobs submit training $JOB_NAME \
--job-dir $OUTPUT_PATH \
--runtime-version 1.10 \
--module-name trainer.task \
--package-path trainer/ \
--region $REGION \
-- \
--train-dir $TRAIN_DATA \
--verbosity DEBUG
可以从 Google cloud ML engine 的仪表盘上查看日志。在作业成功提交之后,您可以在 GCS bucket 的 OUTPUT_PATH 中找到一个文件夹导出。
部署模型
培训之后,是时候为生产部署模型了。第一步是将保存的模型从. hdf5 格式转换为。pb(张量流模型格式)。分步指南以及必要的代码和 shell 命令可以在本笔记本中找到。
步骤 1 →创建模型
创建模型的 gcloud 命令如下。
$ export MODEL_NAME=<Name of the model>
$ export MODEL_PATH=/gcs/path/to/the/model#CREATE MODEL
$ gcloud ml-engine models create $MODEL_NAME
步骤 2 →为您刚刚创建的模型创建版本
运行下面的命令来创建模型的版本 version_1。
$ gcloud ml-engine versions create "version_1" --model $MODEL_NAME \ --origin $MODEL_PATH \
--python-version 3.5 --runtime-version 1.10
步骤 3 →为预测模型服务
对模型的预测请求可以作为 test.json 发送。为此,您需要将图像转换成. json 格式的请求,如下所示。
在线预测可以在 gcloud 命令的帮助下完成。
$ gcloud ml-engine predict — model $MODEL_NAME — version version_3 — json-instances test_data.json
你可以在这里找到代码文件。希望这篇文章对你有所帮助,并为你提供了一些有意义的见解!非常欢迎您的宝贵反馈。快乐学习!!!
原载于 2019 年 1 月 9 日medium.com。
不平衡数据集的分类
当数据集不平衡时,如何使用 sklearn 正确地进行分类分析,并改进其结果。
Photo by Brett Jordan on Unsplash
假设您有一个包含十几个特征的数据集,并且需要对每个观察值进行分类。可以是两类问题(你的输出不是 1 就是 0;对或错)或多类问题(可能有两个以上的选择)。然而,在这种情况下,有一个转折。数据不平衡。想想那些可能患有或可能没有癌症的病人(大多数可能不会)或延长信用额度的决定(大多数银行客户都获得了延期)。你的机器学习算法会对一个类“曝光过度”,对另一个类“曝光不足”。网上有很多关于这个问题的文章,采用了不同的方法。在这里,我将结合其中的一些来得到一个健壮的解决方案。
我们将从更直接的线性模型开始,然后添加一些调整,移动到提升树,最后到神经网络。
你可以从这里下载数据集和练习册。
我们将遵循以下步骤:
- 加载数据和一些最需要的依赖项
- 做一些最简单的预处理
- 建立几个简单的模型,作为进一步尝试的基础
- 花些时间在特征工程上
- 运用各种方法来帮助我们处理不平衡的数据
装载和理解
在这个练习中,我们将使用来自 Kaggle 的弗雷明汉心脏研究数据集。它提出了一个二元分类问题,其中我们需要预测变量“TenYearCHD”(零或一)的值,该值显示患者是否会患心脏病。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as st
import seaborn as sns
import pandas_profiling
%matplotlib inlinedf = pd.read_csv(r'path to dataset')
让我们使初步的数据探索更方便一点。我最近看到了一篇由 Parul Pandey 撰写的文章名为“用 Python 加速数据分析的 10 个简单技巧”,并安装了一个剖析包。
只需一个命令,它就能做一些非常方便的事情:
# looking at stats
pandas_profiling.ProfileReport(df)
The summary output after using Profiling
如您所见,数据相当干净:我们在这里和那里有丢失的值,但是我们将很快处理它们。
下图中的特征之间的相关性也没有说太多:
Pearson correlation from Profiling
吸烟和舒张压/收缩压周围的两个红色矩形相当明显:通常吸烟和高血压是相关的
大多数特性的名称都很好地解释了每个变量的含义,但有几个不太明显:
“教育”可以是:
1 —对于某些高中来说
2 —高中普通教育
3 —一些大学或职业学校
4 —学院
BPMeds 是一个二元变量,其中
0-表示患者不服用任何降压药
1-表示相反的意思
让我们手动检查目标变量:
df['TenYearCHD'].value_counts(normalize = True)
Only 15% of patients have been diagnosed with a decease
如果你是个视觉型的人:
sns.countplot(x='TenYearCHD',data=df)
Same information but shown as a bar chart
这不是一个严重不平衡的集合,但可能会扭曲线性和非线性算法。
数据预处理
我能想到几个迫在眉睫的问题:
- 缺失值和 NaNs
- “教育”列可能被视为序数,也可能不被视为序数
- 数据的规范化或标准化(通常属于这一部分,但我将在以后做,一旦我们开始构建一个需要规范化特征的模型)
“BPMeds”有一些缺失值,但是大约 96%的列为零(没有服用降压药)。所以,用零填充 NaNs 应该是公平的。
“葡萄糖”和“总胆固醇”都是连续变量,我们将使用平均列值来填充空单元格。
“身体质量指数”和“心率”似乎也能很好地适应平均值。
“吸烟日”需要一个两步走的方法。它有 29 个缺失值。你可能认为使用平均值就可以了。然而,“cigsPerDay”与另一个二元特征“currentSmoker”相关联。所以,你可以把一个不吸烟的人叫做 NaN,然后给这个人分配一些平均数量的香烟。我们希望避免这种情况。让我们看看如何做到这一点。
df['cigsPerDay'].value_counts(normalize = True).plot(kind="bar")
Distribution of the number of cigarettes smoked
大多数人不吸烟,我们不想给他们分配香烟。
df['cigsPerDay'][df['currentSmoker']==0].isna().sum()
该命令返回零,因此似乎我们没有针对任何非吸烟者的 NaNs。但是利用。mean()还是不好,因为它会偏向零。我们希望只对吸烟者应用 fillna()命令:
# creating a boolean array of smokers
smoke = (df['currentSmoker']==1)# applying mean to NaNs in cigsPerDay but using a set of smokers only
df.loc[smoke,'cigsPerDay'] = df.loc[smoke,'cigsPerDay'].fillna(df.loc[smoke,'cigsPerDay'].mean())
因此,现在吸烟的平均数量刚刚超过 18 支:
df['cigsPerDay'][df['currentSmoker']==1].mean()
如果我们决定走一条更简单的路,不排除不吸烟者,这个值将是 9 左右。
可以肯定的是,不吸烟的人不会被赋予任何价值:
df['cigsPerDay'][df['currentSmoker']==0].mean()
“教育”是一个有趣的话题。虽然它本身是一个分类变量*,*它已经为我们编码了,更高的教育水平对应着更大的数字。这里的一个潜在问题是,“大学”和“某所大学或职业学校”之间的“间隔”可能与“高中或 GED”和“某所大学或职业学校”之间的“间隔”不同然而,数字说的是相反的:四和三之间的距离与二和三之间的距离完全相同。你可以通过谷歌搜索“逻辑回归中的序数值”或者浏览这篇论文来了解这个问题。现在让我们假设距离是相似的。也就是说,我们将去掉“教育”中的 NaNs,以便回归可以与列一起工作。
df['education'].value_counts(normalize = True).plot(kind="bar")
Breakdown of “education”
在这里用“1”代替 NaNs 似乎是公平的。
立刻做出所有该做的改变:
# Filling out missing values
df['BPMeds'].fillna(0, inplace = True)
df['glucose'].fillna(df.glucose.mean(), inplace = True)
df['totChol'].fillna(df.totChol.mean(), inplace = True)
df['education'].fillna(1, inplace = True)
df['BMI'].fillna(df.BMI.mean(), inplace = True)
df['heartRate'].fillna(df.heartRate.mean(), inplace = True)
让我们检查它是否通过:
df.isna().sum()
我们准备好了。
No NaNs left
基本型号
我们将从一个随机的森林开始,也将看看我们的功能的重要性指标。
不要忘记将特征与目标变量分开:
features = df.iloc[:,:-1]
result = df.iloc[:,-1]
模型本身在下面。
依赖关系优先:
import sklearn
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
from sklearn.metrics import roc_auc_score
from sklearn.preprocessing import StandardScaler
训练/测试分割:
X_train, X_test, y_train, y_test = train_test_split(features, result, test_size = 0.2, random_state = 14)
配件:
rf = RandomForestClassifier()
rf.fit(X_train, y_train)# Making predictions on unseen data
predictions_rf = rf.predict(X_test)
特征的重要性(有时,为了加快速度,您希望使用较少的特征构建模型):
# what features are the most important?
plt.plot(rf.feature_importances_)
plt.xticks(np.arange(X_train.shape[1]), X_train.columns.tolist(), rotation=90)
Some features are definitely more important
如果您希望它是一个列表:
# View a list of the features and their importance scores
list(zip(features, rf.feature_importances_))
评估模型:
print(classification_report(y_test, predictions_rf))
print(confusion_matrix(y_test, predictions_rf))# Under ROC curve
prob_rf = rf.predict_proba(X_test)
prob_rf = [p[1] for p in prob_rf]
print(roc_auc_score(y_test, prob_rf))
这里有两个结论:
- 84.90%的准确率几乎无法击败随机猜测。记住,84.81%的数据被标记为零。所以我们比猜测高出 0.09%。
- 此外,混淆矩阵显示了大量的假阴性:
Lots of type II errors
我们希望避免这种情况,因为模型错误地指出患者没有问题,而实际上他或她有问题!我们将很快解决这个问题,但是现在,让我们只使用重要的特征来重建模型。
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier# Create a selector object that will use the random forest classifier to identify
# features that have an importance of more than 0.12
sfm = SelectFromModel(clf, threshold=0.12)# Train the selector
sfm.fit(X_train_std, y_train)feat_labels = list(features.columns.values) # creating a list with features' names
for feature_list_index in sfm.get_support(indices=True):
print(feat_labels[feature_list_index])importances = clf.feature_importances_
std = np.std([tree.feature_importances_ for tree in clf.estimators_],
axis=0)
indices = np.argsort(importances)[::-1]print("Feature ranking:")for f in range(X_train_std.shape[1]):
print("%d. feature %d (%f)" % (f + 1, indices[f], importances[indices[f]]))# Plot the feature importances of the forest
plt.figure()
plt.title("Feature importances")
plt.bar(range(X_train_std.shape[1]), importances[indices],
color="r", yerr=std[indices], align="center")
plt.xticks(range(X_train_std.shape[1]), indices)
plt.xlim([-1, X_train_std.shape[1]])
plt.show()# with only important features. Can check X_important_train.shape[1]
X_important_train = sfm.transform(X_train_std)
X_important_test = sfm.transform(X_test_std)clf_important = RandomForestClassifier(n_estimators=10000, random_state=0, n_jobs=-1)
clf_important.fit(X_important_train, y_train)predictions_y_4 = clf_important.predict(X_important_test)
print(classification_report(y_test, predictions_y_4))
print(confusion_matrix(y_test, predictions_y_4))
accuracy_score(y_test, predictions_y_4)
# Under ROC curve
prob_y_4 = clf_important.predict_proba(X_important_test)
prob_y_4 = [p[1] for p in prob_y_4]
print(roc_auc_score(y_test, prob_y_4))
我选择了重要性大于 0.12 的特性,并仅使用这些列重建了随机森林。结果非常相似,但我们能够节省一些计算能力。
逻辑回归
回归需要标准化的特征:
scaler = StandardScaler()
X_train_std = scaler.fit_transform(X_train)
X_test_std = scaler.fit_transform(X_test)
模型是:
logmodel = LogisticRegression(solver='liblinear')
logmodel.fit(X_train_std, y_train)
predictions_y_2 = logmodel.predict(X_test_std)
和评估结果:
print(classification_report(y_test, predictions_y_2))
print(confusion_matrix(y_test, predictions_y_2))# Under ROC curve
prob_y_2 = logmodel.predict_proba(X_test_std)
prob_y_2 = [p[1] for p in prob_y_2]
print(roc_auc_score(y_test, prob_y_2))
结果与 random forest 非常相似:
Just two fewer type II errors
它在假阴性上表现稍好。但这仍然不够。
LogisticRegression 有一个参数 class_weight ,这将有助于提高准确性。让我们看看:
logmodel = LogisticRegression(solver='liblinear', class_weight='balanced')
logmodel.fit(X_train_std, y_train)
predictions_y_3 = logmodel.predict(X_test_std) print(classification_report(y_test, predictions_y_3))
print(confusion_matrix(y_test, predictions_y_3))
accuracy_score(y_test, predictions_y_3)
# Under ROC curve
prob_y_3 = logmodel.predict_proba(X_test_std)
prob_y_3 = [p[1] for p in prob_y_3]
print(roc_auc_score(y_test, prob_y_3))
这实际上并没有什么帮助:我们减少了第二类错误的数量,但却牺牲了整体的准确性。看来我们需要转向一些不同的东西。
我们也可以手动实现 class_weight ,通过传递一个 0 和 1 值的分解,您可以使用 value_counts: 快速获得
df['TenYearCHD'].value_counts(normalize = True)weights = {0 : '0.848113', 1 : '0.151887'}
logmodel_auto = LogisticRegression(class_weight = weights, solver = 'liblinear')
logmodel_auto.fit(X_train_std, y_train)
predictions_std_auto = logmodel_auto.predict(X_test_std)print(classification_report(y_test, predictions_std_auto))
print(confusion_matrix(y_test, predictions_std_auto))
accuracy_score(y_test, predictions_std_auto)
# Under ROC curve
prob_y_4 = logmodel.predict_proba(X_test_std)
prob_y_4 = [p[1] for p in prob_y_4]
print(roc_auc_score(y_test, prob_y_4))
实际上,在第一类错误上做得很好:
Passing weights manually removes all Type I errors
你可以尝试不同的组合。通过反复试验(或在 GridSearch 的帮助下),您可以找到一个符合您目的的方法,例如:
weights = {0 : '0.09042', 1 : '0.90958'}
如果您决定使用 GridSearch 来寻找合适的权重,这是它的实现方式:
from sklearn.model_selection import GridSearchCV
weights = np.linspace(0.03, 0.97, 55)scaler = StandardScaler()
features_std = scaler.fit_transform(features)gsc = GridSearchCV(
estimator=LogisticRegression(solver='liblinear'),
param_grid={
'class_weight': [{0: x, 1: 1.0-x} for x in weights]
},
scoring='roc_auc',
cv=3
)
grid_result = gsc.fit(features_std, result)
在上面的代码中,我们测试了变量 TenYearCHD 从 3-97%崩溃到 97-3%崩溃的 0 和 1 的不同组合。 GridSearchCV 可以评估不同的评估者,它们是在现场选择和控制的*评分。*通常,您会使用类似于分类的精确度和回归的 R 平方,但在我们的情况下,精确度不会提供太多信息,所以我决定使用 AUC 曲线下的面积。此处描述了所有可用的评分参数。
然后我们可以打印出最佳参数,并将其传回模型:
print("Best parameters : %s" % grid_result.best_params_)# passing weights found above
rf_w = RandomForestClassifier(class_weight = {0:0.882962962962963, 1:0.11703703703703705})
rf_w.fit(X_train, y_train)print(classification_report(y_test, predictions_rf_w))
print(confusion_matrix(y_test, predictions_rf_w))
accuracy_score(y_test, predictions_rf_w)
或者到逻辑回归:
weights = {0 : '0.882962962962963', 1 : '0.11703703703703705'}
logmodel_auto_gridsearch = LogisticRegression(class_weight = weights, solver = 'liblinear')
logmodel_auto_gridsearch.fit(X_train_std, y_train)
predictions_std_auto_gridsearch = logmodel_auto_gridsearch.predict(X_test_std)print(classification_report(y_test, predictions_std_auto_gridsearch))
print(confusion_matrix(y_test, predictions_std_auto_gridsearch))
accuracy_score(y_test, predictions_std_auto_gridsearch)
# Under ROC curve
prob_y_3_gridsearch = logmodel_auto_gridsearch.predict_proba(X_test_std)
prob_y_3_gridsearch= [p[1] for p in prob_y_3_gridsearch]
print(roc_auc_score(y_test, prob_y_3_gridsearch))
专门解决不平衡的数据
最流行的方法之一是上(下)采样。你可能还记得,我们的数据集的问题是,在训练阶段,算法看到的负面(零)情况远远多于正面,这使得模型不太准确。下一个合乎逻辑的事情是在相同数量的正面(1)和负面(0)情况下训练模型。
from sklearn.utils import resampledf_minority = df[df.TenYearCHD==1]
df_majority = df[df.TenYearCHD==0]df['TenYearCHD'].value_counts()
在上面的代码中,我们将现有的数据集分成两部分:患心脏病的观察数据和其他数据。
# sample with replacement to match majority class and get #reproducible results
df_minority_upsampled = resample(df_minority,
replace=True,
n_samples=3596,
random_state=123)
# Display new class counts
df_upsampled.TenYearCHD.value_counts()
如您现在所见, df_upsampled 具有相同数量的零和一观察值。
现在,我们将对其进行标准化,并再次将其放入模型中:
# Train/test, normalize the new data set
features_upsampled = df_upsampled.iloc[:,:-1]
result_upsampled = df_upsampled.iloc[:,-1]X_train_upsampled, X_test_upsampled, y_train_upsampled, y_test_upsampled = train_test_split(features_upsampled, result_upsampled, test_size = 0.2, random_state = 14)X_train_std_upsampled = scaler.fit_transform(X_train_upsampled)
X_test_std_upsampled = scaler.fit_transform(X_test_upsampled)# new log model for upsampled data
logmodel_upsampled = LogisticRegression(solver='liblinear')
logmodel_upsampled.fit(X_train_std_upsampled, y_train_upsampled)
predictions_y_2_upsampled = logmodel_upsampled.predict(X_test_std_upsampled) print(classification_report(y_test_upsampled, predictions_y_2_upsampled))
print(confusion_matrix(y_test_upsampled, predictions_y_2_upsampled))
accuracy_score(y_test_upsampled, predictions_y_2_upsampled)
# Under ROC curve
prob_y_2_upsampled = logmodel_upsampled.predict_proba(X_test_std_upsampled)
prob_y_2_upsampled = [p[1] for p in prob_y_2_upsampled]
print(roc_auc_score(y_test_upsampled, prob_y_2_upsampled))
就我们而言,结果已经恶化。为什么这样我们将在下次探讨它。不过,通常会有帮助。
我们已经提到,在这种情况下,错误 II 比错误 I 更严重。另一种解决方法是移动阈值(现在设置为 0.5)。
logmodel_lowering = LogisticRegression(solver='liblinear')
logmodel_lowering.fit(X_train_std, y_train)from sklearn.preprocessing import binarize
for i in range(1,7):
cm2=0
predictions_y_2_lowering = logmodel_lowering.predict_proba(X_test_std)
y_pred2_lowering=binarize(predictions_y_2_lowering,i/10)[:,1]
cm2=confusion_matrix(y_test,y_pred2_lowering)
print ('With',i/10,'threshold the Confusion Matrix is ','\n',cm2,'\n',
'with',cm2[0,0]+cm2[1,1],'correct predictions and',cm2[1,0],'Type II errors( False Negatives)','\n\n',
'Sensitivity: ',cm2[1,1]/(float(cm2[1,1]+cm2[1,0])),'Specificity: ',cm2[0,0]/(float(cm2[0,0]+cm2[0,1])),'\n\n\n')
我们在这里写了一个循环,从 10%的阈值到 70%的阈值,然后显示结果。为了这个练习,我在这里拟合基本的逻辑回归模型。
迄今为止,我们只取得了有限的进展。可能是时候转向 XGBoost 了——一个最 Kaggle 比赛的首选武器。
import xgboost as xgb
from sklearn.metrics import mean_squared_errorxg_reg = xgb.XGBRegressor(objective ='binary:logistic', colsample_bytree = 0.3, learning_rate = 0.05,
max_depth = 9, alpha = 10, n_estimators = 20)eval_set = [(X_test_std, y_test)]
xg_reg.fit(X_train_std, y_train, eval_metric="error", eval_set = eval_set, verbose = True)rmse = np.sqrt(mean_squared_error(y_test, prediction_y_5))
print("RMSE: %f" % (rmse))
与其他方法不同,XGBoost 可以在 fit 阶段报告和评估测试集的性能。我已经创建了 *eval_set,*将其传递给 fit 方法,并设置 verbose = True 以实时查看详细程度。
开箱即用,它返回的均方根误差(RMSE)为 0.3678。让我们努力降低它。为此,我们需要调整算法中传递的众多参数中的一些。你真的需要在官方文档中阅读它们,因为这是实现你的模型的卓越性能的关键。今天,我将重点介绍三个可用参数。同样, GridSearchCV 会为我们做这件事。
我们将测试这些参数的各种输入:
- n_estimators (定义要训练的森林的大小)
- max_depth (基础学习者的最大树深度)
- learning_rate (嗯,是学习率)
n_estimators = [10, 20, 30, 40, 50, 60]
max_depth = [2, 4, 5, 6, 7, 8]
learning_rate = [0.0001, 0.001, 0.01, 0.1, 0.2, 0.3]
param_grid = dict(max_depth = max_depth, n_estimators = n_estimators, learning_rate=learning_rate)
kfold = StratifiedKFold(n_splits = 10, shuffle = True, random_state = 10)
grid_search_xg = GridSearchCV(xg_reg, param_grid, scoring = 'roc_auc', n_jobs = -1, cv=kfold, verbose = 1)
result_gcv_xgb = grid_search_xg.fit(X_train_std, y_train)
我们在这里使用 StratifiedKFold 和 GridSearchCV 迭代参数进行交叉验证。您想要测试的参数越多,您的计算机运行的排列就越多,花费的时间就越多。如果你是在笔记本电脑上做,要小心。
该过程完成后,让我们看看最佳参数是什么:
print("Best: %f using %s" % (result_gcv_xgb.best_score_, result_gcv_xgb.best_params_))
means = result_gcv_xgb.cv_results_['mean_test_score']
stds = result_gcv_xgb.cv_results_['std_test_score']
params = result_gcv_xgb.cv_results_['params']
GridSearchCV found the optimal parameters
然后,您可以使用上面得到的内容重新运行模型:
# rebuild using best params
xg_reg = xgb.XGBRegressor(objective ='binary:logistic', colsample_bytree = 0.3, learning_rate = 0.1,
max_depth = 2, alpha = 10, n_estimators = 50)
eval_set = [(X_test_std, y_test)]
xg_reg.fit(X_train_std, y_train, eval_metric="error", eval_set = eval_set, verbose = False)
prediction_y_5 = xg_reg.predict(X_test_std)
rmse = np.sqrt(mean_squared_error(y_test, prediction_y_5))
print("RMSE: %f" % (rmse))
我们可以看到一个进步:RMSE 下降到 0.3384。
XGBoost 返回概率,而不是实际预测。不过,我们需要实际的预测来建立一个混淆矩阵。
prediction_y_5_01 = prediction_y_5
prediction_y_5_01[prediction_y_5 > 0.5] = 1
prediction_y_5_01[prediction_y_5 <= 0.5] = 0print(classification_report(y_test, prediction_y_5_01))
print(confusion_matrix(y_test, prediction_y_5_01))
accuracy_score(y_test, prediction_y_5_01)
我们已经尝试使用一些传统的和更具体的方法来建立一个准确的预测模型。根据数据的性质,它可能已经足以提高模型的性能。在文章的第二部分,我们将继续我们的旅程,构建一条学习曲线,几个模型的集合,最后使用 Keras 重新运行分析。
分类问题和探索决策边界
Photo by @soymeraki (Unsplash)
分类问题是组织可以利用数据科学的力量创造潜在竞争优势的主要业务问题之一。许多算法输出一个概率(0 到 1),而不是一个硬的类别分类,因此必须根据业务问题开发一种“决策边界”,在沿着概率分布的某个地方设置决策边界之前,您需要考虑一些权衡。
例如,对于欺诈检测,这可能是一个双重问题。您可能希望考虑更严格的决策界限,因为欺诈是一种相对罕见的情况,您不希望出现许多会直接影响业务的误报。也许您会将它们转发给欺诈分析师,并且您要标记的每笔交易都会让您的一名员工在 2 小时内忙碌起来—时间就是金钱—但另一方面,您也不想将决策界限设置得太严格,以免无法标记潜在的欺诈交易。
你能想出一个商业问题,其中你可能有一个更软的决策边界,并且你不介意用你的模型“捕捉”一些误报吗?
举个例子:对于在线营销活动,你可能想放松控制,因为误报的代价可能会被你从你没有以 50/50 决策边界为目标的客户那里带来的额外收入抵消(假设你的问题是平衡的)。
在这篇文章中,我的目标是:
- 将一个分类问题的训练可视化;
- 绘制决策边界;
- 可视化移动决策边界的效果;
关于我们将用于分类的模型,我们将查看逻辑回归,但是您可以直观地想到输出连续概率的其他分类算法的相同原理。在“数据成熟”的公司中,你不会期望在生产中应用许多逻辑回归,但你肯定会看到它们被用作衡量标准和基线模型,因为它们有几个优点:
- 易于掌握变量对决策过程的影响(对于可解释性是关键的保守行业是强制性的);
- 比较快上手。
- 你可以很容易地评估你的分类问题的“线性度”。
使用来自 Andrew 的机器学习课程的数据(https://www . coursera . org/learn/Machine-Learning/home/welcome**)**我将阐述另一个业务问题(不同于课程练习),以便我们可以使用一个关于决策边界的实际示例。
假设你是一家以服装为中心的在线企业,你想向每位顾客发送一份促销活动,向他们提供独家优惠,将一件昂贵的外套降价 40%。
你联系的每个顾客将花费你 10€,大衣包括折扣在内要花费 50€。你想为你的全部客户群最大化这个优惠的利润,所以你对 100 个客户做一个实验,给他们发送折扣。
然后,你画出他们去年光顾你的商店的次数和累计花费(为了简单起见,假设这两个变量可以解释购买者的倾向),并有以下数据,用“+”标记购买你提供的外套的人:
Visualizing the outcome for the Coat Offer
在我们的样本中,60 名顾客购买了外套,40 名没有。计算我们的收入减去我们实验的营销成本:
(60 * 50€)-(100*10€) = 2000 €
但如果我们只选择了购买外套的 60 名顾客,我们的收入可能会增加 2.400 €(多 20%),因为我们避免了那些对我们的报价不感兴趣的顾客,从而节省了 400 欧元。
如果你想将这一优惠推广到所有顾客,你可能想为每位顾客节省 10 欧元。随着你的客户群越来越大,这个约束变得更加重要。
现在,分类算法帮助我们划分这些点。如果我让你在这个图中画一条线来做选择,你可能已经有了可以区分这两类的东西。
我们的分类算法将处理这两个类的分离,但让我们用参数 θ ( θ0,θ1 和θ2)绘制一个随机类。这些参数是通过我们的逻辑回归学习的参数(有时它们也被称为β0、β1 和β2,但它们表示完全相同的东西:每个变量的权重)——如果需要,请查看 Andrew 的学习材料,了解关于这些参数含义的更多解释。
好的,那么用下面的参数拟合一条随机线( θ0 = -5,θ1 = 0.01,θ2 = 0.05),称之为实验#1:
Experiment #1 with an improper decision boundary
这真的不适合我们的数据。这个决策边界是什么——我们正在绘制的这条线?这是我们将在 50%阈值内划分等级的界限。
根据上面的拟合线,我们将考虑 19 位有超过 50%的可能性购买外套的顾客——线上的顾客————但如果我们只选择这些人,我们最终会避开许多对外套感兴趣的顾客。
我们可以进一步改进我们的算法。只要看一下图表,你就会发现这并没有提供我们的类的最佳分离。从数学上来说,如果我们最小化代表这种分离的成本函数(在这种情况下,是二进制交叉熵),这条线甚至不在最小值附近。
回想一下,梯度下降的目标是找到与下面类似的函数的最小值(仅使用一个 θ):
Cost function example with a single independent variable (θ0)
我们的成本函数 **(把它想成是我们的误差或者我们对购买和不购买分类的错误程度)**应该慢慢下降到全局最小值(红点)——基本上是我们将要进行分类分离的误差(成本函数)的最小值。
那么,当我们越来越接近最小值时,我们的决策界限会发生什么变化呢?
看看下面的实验(突出显示改变了什么):
- (θ0 =-5, θ1 = 0.02, θ2 = 0.05 ):实验二
- (θ0 =-5, θ1 = 0.03, θ2 = 0.05 ):实验三
Experiment #2 — Decision Boundary
Experiment #3— Decision Boundary
仅仅通过稍微调整 θ1,我们就越来越接近我们想要的(一个最好划分类别的线性表示)。
通过优化我们的成本函数,我们找到了θs的最终最优组合:
- ( θ0 = -25.16,θ1 = 0.2062,θ2 = 0.2015)-最佳权重
这导致以下决策界限:
Optimal decision boundary (Minimizes the error)
我们找到了最佳的 50/50 线性边界! 但是…就业务问题而言,这是最佳决策边界吗?回想一下我们关于外套报价的问题,我们每个联系人的成本是 10€,每卖出一件外套的收入是 50€。
你会用什么作为接触该客户的门槛?
你们中的许多人可能会回答,“任何高于 50%的数字”T31,直觉上这基本上是正确的 T32。如果我们考虑这个默认的概率阈值,我们会认为每一个获得大衣的概率超过 50%的顾客都会被选为我们的目标联系人。这样做会产生以下指标:
模型指标:
- 准确率 89%;
- f1-0.91 分
业务指标:
- 联系 61 个客户,花费 610 €
- 其中 55 名顾客购买了 50€的外套,这意味着 2750 €的收入;
- 净收入 2140€;
当我们开始考虑另一个阈值(例如 30%)上的决策界限时,会发生什么?也就是说,有超过 30%的购买概率的顾客会被考虑联系?
决策边界移动!
New Boundary using a 30% threshold
红叉是 50%阈值中没有考虑的例子,现在在 30%阈值中考虑了。我们的指标会发生什么变化?
模型度量:
- 准确率 92%;
- f1-得分为 0.9365
业务指标:
- 联系 66 个客户,花费 660 €
- 其中 59 名顾客购买了 50€的外套,这意味着 2950€的收入;
- 净收入 2290€;
等一下…我们有更高的准确性和更高的 F1 分数!为什么?因为当我们开发该算法时,我们没有针对准确性和 F1 分数进行优化,我们针对 log-loss 函数进行了优化,该函数以相同的方式惩罚假阳性和假阴性情况。
你知道在日志丢失过程中什么会受到惩罚,从而提高我们的决策界限吗?这四个客户就在这里:
Examples causing a higher decision boundary
如果我们移除这些示例并重新训练我们的算法,则检查决策边界:
New Decision Boundary without extreme examples
通过移除这四个异常情况,我们已经降低了决策边界**(这意味着没有这四个客户,我们不会惩罚他们的损失函数)。**
**需要记住的一件重要事情是:也许这些例子属于我们的训练样本,我们不应该删除它们。**想象这是你真正客户的行为,也许他们不应该被视为离群值。
该决定将仅取决于业务问题以及您在将该信息整合到模型上时所付出的努力——此外,您还可以通过尝试不同的算法或多项式特性来改进模型本身。
回到我们所有客户的初始数据集,如果我们有一个更严格的决策边界,并且只联系概率超过 80%的客户,会发生什么?
New Boundary using a 80% threshold
模型指标:
- 准确率 86%;
- f1-得分为 0.8749
业务指标:
- 联系 52 个客户,费用为 520 €
- 这些顾客中有 49 人购买了 50€的外套,这意味着 2450€的收入;
- 净收入:1930€;
三个门槛比较表:
特别是对于这个问题,我们可以放宽我们的决策界限,将 30%的概率度量设置为阈值,这样我们就可以最大化净收入——假设完整的客户群反映了这些客户的行为。在这种情况下,即使模型度量被最大化,但这并不是所有问题的情况。
有趣的是,在评估模型时,一些指标可能会以决策边界无意义的方式使用(如 ROC-AUC ),因为这些指标评估的是连续变量,而不是二元目标。
结论:
- 决策边界可以被解释为业务度量;
- 在部署和讨论模型时,您应该检查结果的目的是什么,而不是按照标准假设 50%的决策边界;
- 当我们决定一个特定概率的阈值时,我们就离开了统计领域,进入了决策领域。大多数分类算法只输出概率,你对这些概率所做的更多的是与一个商业问题有关。
感谢您花时间阅读这篇文章!如果你有兴趣获得分析方面的培训,你可以访问我在 Udemy(【https://www.udemy.com/user/ivo-bernardo/】)上的页面,并在平台中查看我的课程或在 LinkedIn 上联系!()
机器学习分类项目:寻找捐赠者
Picture from Unsplash
介绍
在这个项目中,我们将使用许多不同的监督算法,使用从 1994 年美国人口普查中收集的数据来精确预测个人收入。
然后,我们将从初步结果中选择最佳候选算法,并进一步优化该算法以最佳地模拟数据。我们实现的目标是构建一个模型,准确预测个人收入是否超过 50,000 美元。
这种任务可能出现在非营利机构中,这些机构依靠捐赠生存。了解个人的收入可以帮助非营利组织更好地了解需要多少捐款,或者他们是否应该从一开始就伸出援手。根据我们之前的研究,我们发现最有可能向慈善机构捐款的人是那些年收入超过 5 万美元的人。
这个项目的数据集来源于 UCI 机器学习库。该数据集由 Ron Kohavi 和 Barry Becker 捐赠,发表在文章*“提高朴素贝叶斯分类器的准确性:决策树混合”*中。你可以在网上找到罗恩·科哈维的文章。我们在这里研究的数据由对原始数据集的微小更改组成,比如删除'fnlwgt'
特性和带有缺失或格式错误条目的记录。
数据
修改后的人口普查数据集由大约 32,000 个数据点组成,每个数据点有 13 个特征。该数据集是 Ron Kohavi 在论文*“提高朴素贝叶斯分类器的准确性:决策树混合”中发表的数据集的修改版本。你可以在网上找到这篇论文,原始数据集存放在 UCI 上。*
特性
age
:年龄workclass
:工人阶级(私营、自营企业、自营企业、联邦政府、地方政府、州政府、无薪、从未工作)education_level
:教育水平(学士、部分大学、11 年级、HS-grad、Prof-school、Assoc-acdm、Assoc-voc、9 年级、7-8 年级、12 年级、硕士、1-4 年级、10 年级、博士、5-6 年级、学前班)education-num
:完成的教育年数- 婚姻状况(已婚-同居-配偶、离婚、未婚、分居、丧偶、已婚-配偶缺席、已婚-配偶)
occupation
:工作职业(技术支持、工艺维修、其他服务、销售、行政管理、专业教授、搬运工人、清洁工、机器操作员、行政文员、农业-渔业、运输-搬运、私人服务、保护服务、武装部队)relationship
:关系状态(妻子、亲生子女、丈夫、非家庭成员、其他亲属、未婚)race
:种族(白人、亚洲太平洋岛民、美洲印第安爱斯基摩人、其他人、黑人)sex
:性别(女,男)capital-gain
:货币资本收益capital-loss
:货币资金损失hours-per-week
:每周平均工作时间native-country
:本土国家(美国、柬埔寨、英国、波多黎各、加拿大、德国、美国外围地区(关岛-USVI 等)、印度、日本、希腊、中国、古巴、伊朗、洪都拉斯、菲律宾、意大利、波兰、牙买加、越南、墨西哥、葡萄牙、爱尔兰、法国、多米尼加共和国、老挝、厄瓜多尔、台湾、海地、哥伦比亚、匈牙利、危地马拉、尼加拉瓜、苏格兰、泰国、南斯拉夫、萨尔瓦多、特立尼达和多巴哥&多巴哥、秘鲁、香港、荷兰)
目标变量
income
:收入阶层(< =50K,> 50K)
导入库并加载数据
我们将首先加载将要使用的 Python 库,以及人口普查数据。最后一列将是我们的目标变量,“收入”,其余的将是功能。
***# Import libraries necessary for this project***
import numpy as np
import pandas as pd
from time import time
from IPython.display import display
***# Import supplementary visualization code visuals.py***
import visuals as vs
***# Pretty display for notebooks***
%matplotlib inline
***# Load the Census dataset***
data = pd.read_csv("census.csv")
***# Success - Display the first record***
display(data.head(n=1))
探索性数据分析
对数据集的初步研究将向我们展示每个群体中有多少人,以及他们中收入超过 50,000 美元的比例。
***# Total number of records***
n_records = data.shape[0]
***# Number of records where individual's income is more than $50,000***
n_greater_50k = data[data['income'] == '>50K'].shape[0]
***# Number of records where individual's income is at most $50,000***
n_at_most_50k = data[data['income'] == '<=50K'].shape[0]
***# Percentage of individuals whose income is more than $50,000***
greater_percent = (n_greater_50k / n_records) * 100
***# Print the results***
print("Total number of records: {}".format(n_records))
print("Individuals making more than $50,000: {}".format(n_greater_50k))
print("Individuals making at most $50,000: {}".format(n_at_most_50k))
print("Percentage of individuals making more than $50,000: {}%".format(greater_percent))
准备数据
数据必须经过预处理才能用于机器学习算法。这个预处理阶段包括数据的清理、格式化和重构。
对于此数据集,既没有空条目也没有无效条目,但是有一些必须调整的要素。这项任务将极大地改善我们模型的结果和预测能力。
转换偏斜连续特征
Figure by Author
如果未对范围进行归一化,则要素值的偏态分布可能会使算法表现不佳。
在我们的数据集中,此分布有两个特征:
- 资本收益
- 资本损失
***# Split the data into features and target label***
income_raw = data['income']
features_raw = data.drop('income', axis = 1)
***# Visualize skewed continuous features of original data***
vs.distribution(data)
对于这种类型的分布,对数据应用对数变换是非常典型的,因此异常值不会对机器学习模型的性能产生负面影响。
但是,我们应该小心对待 0 值,因为 log(0)是未定义的,所以我们将这些值转换为大于 0 的一个小数值,以成功应用对数。
***# Log-transform the skewed features***
skewed = ['capital-gain', 'capital-loss']
features_log_transformed = pd.DataFrame(data = features_raw)
features_log_transformed[skewed] = features_raw[skewed].apply(lambda x: np.log(x + 1))
***# Visualize the new log distributions***
vs.distribution(features_log_transformed, transformed = True)
归一化数字特征
建议对数字特征执行某种类型的缩放。这种缩放不会改变要素分布的形状,但可以确保在应用监督模型时平等对待每个要素。
***# Import sklearn.preprocessing.StandardScaler***
from sklearn.preprocessing import MinMaxScaler
***# Initialize a scaler, then apply it to the features***
scaler = MinMaxScaler() *# default=(0, 1)*
numerical = ['age', 'education-num', 'capital-gain', 'capital-loss', 'hours-per-week']
features_log_minmax_transform = pd.DataFrame(data = features_log_transformed)
features_log_minmax_transform[numerical] = scaler.fit_transform(features_log_transformed[numerical])
***# Show an example of a record with scaling applied***
display(features_log_minmax_transform.head(n = 5))
预处理类别特征
如果我们看一下前面的表格,我们可以看到有一些特征,如“职业”或“种族”,它们不是数字,而是类别。机器学习算法期望与数值一起工作,因此这些分类特征应该被转换。
最流行的分类转换之一叫做“一键编码”。在一次性编码中,为分类特征的每个可能类别创建一个“虚拟”变量。
为了更好地理解,请看下表:
此外,我们应该转换我们的目标变量“收入”。它只能取可能的值,“<=50K” and “> 50K”,所以我们将避免一次性编码,并将类别分别编码为 0 和 1。
features_log_minmax_transform.head(1)
***# One-hot encode the 'features_log_minmax_transform' data***
features_final = pd.get_dummies(features_log_minmax_transform)
***# Encode the 'income_raw' data to numerical values***
income = income_raw.map({'<=50K':0,'>50K':1})
***# Print the number of features after one-hot encoding***
encoded = list(features_final.columns)
print("**{}** total features after one-hot encoding.".format(len(encoded)))
***# See the encoded feature names***
print (encoded)
混洗和分割数据
当所有的分类变量被转换,所有的数字特征被标准化后,我们需要将数据分成训练集和测试集。我们将 80%的数据用于训练,20%用于测试,
***# Import train_test_split*** from sklearn.model_selection import train_test_split ***# Split the 'features' and 'income' data into training and testing sets***
X_train, X_test, y_train, y_test = train_test_split(features_final, income, test_size = 0.2, random_state = 0) ***# Show the results of the split***
print("Training set has **{}** samples.".format(X_train.shape[0])) print("Testing set has **{}** samples.".format(X_test.shape[0]))
模型性能评估
在这一部分中,我们将研究四种不同的算法,并确定最擅长建模和预测数据的算法。这些算法中的一个将是天真的预测器,它将作为性能的基线,另外三个将是受监督的学习器。
指标和朴素预测器
该项目的目标是正确识别每年收入超过 5 万美元的个人,因为他们是最有可能向慈善机构捐款的群体。因此,我们应该明智地选择我们的评估指标。
注:评估指标提醒
当对事件进行预测时,我们可以得到 4 种结果:真阳性、真阴性、假阳性和假阴性。所有这些都表示在下面的分类矩阵中:
Figure by Author
准确度 衡量分类器做出正确预测的频率。它是正确预测的数量与预测总数(测试数据点的数量)的比率。
Figure by Author
精密 告诉我们被我们归类为某一类的事件的比例,实际上都是那个类。它是真阳性与所有阳性的比率。
Figure by Author
【回忆(敏感度) 告诉我们实际上属于某一类的事件有多大比例被我们归类为该类。它是真阳性与所有阳性的比率。
Figure by Author
对于像我们这样分类分布有偏差的分类问题,准确性本身并不是一个合适的度量。反而精度和召回率更有代表性。
这两个指标可以组合得到 F1 得分 ,这是精度和召回得分的加权平均值(调和平均值)。这个分数的范围从 0 到 1,1 是可能的最佳 F1 分数(我们在处理比率时采用调和平均值)。
Figure by Author
此外,由于我们正在搜索愿意捐赠的个人,模型精确预测那些收入超过 5 万美元的个人的能力比模型回忆这些个人的能力更重要。我们可以使用 F-beta 分数作为同时考虑精确度和召回率的度量。
具体来说,对于β = 0.5,更重视精度。
如果我们看一下“收入”变量的等级分布,很明显,年收入最多 5 万美元的人比年收入更多的人多得多。因此,我们可以做一个天真的预测,随机抽取一个人,预测他/她每年的收入不会超过 5 万美元。它被称为天真,因为我们没有考虑任何相关信息来证实这一说法。
这个天真的预测将作为一个基准来确定我们的模型是否表现良好。重要的是要注意,单独使用这种类型的预测是没有意义的,因为所有个体都将被归类为非供体。
天真预测者的表现
我们将运行以下代码来确定我们的朴素预测器性能:
***# Counting the ones as this is the naive case. Note that 'income' is the 'income_raw' data encoded to numerical values done in the data preprocessing step.***
TP = np.sum(income) ***# Specific to the naive case*** FP = income.count() - TP***# No predicted negatives in the naive case***
TN = 0
FN = 0
***# Calculate accuracy, precision and recall***
accuracy = TP / (TP + FP + TN + FN)
recall = TP / (TP + FN)
precision = TP / (TP + FP)
***# Calculate F-score using the formula above for beta = 0.5 and correct values for precision and recall.***
beta = 0.5
fscore = (1 + beta**2) * ((precision * recall) / ((beta**2) * precision + recall))
***# Print the results***
print("Naive Predictor: [Accuracy score: {:.4f}, F-score: {:.4f}]".format(accuracy, fscore))
监督学习模型
考虑到我们数据的形状:
- 45222 个数据点
- 103 个特征
数据点的数量不是很大,但是有大量的特征,并且不是所有的监督算法都适合适当地处理数量。
scikit-learn 中的一些可用分类算法:
- 高斯朴素贝叶斯
- 决策树
- 集成方法(Bagging、AdaBoost、随机森林、梯度增强)
- k-最近邻(近邻)
- 随机梯度下降分类器(SGDC)
- 支持向量机(SVM)
- 逻辑回归
考虑到他们的特点和我们的数据集,我们将选择以下三个:
a)高斯朴素贝叶斯
- 该模型的优势是:它是一个简单快速的分类器,只需对模型的超参数进行很小的调整就能提供良好的结果。此外,它不需要大量的数据来进行适当的训练。
- 该模型的弱点是:它具有很强的特征独立性假设。如果我们没有同时出现类别标签和某个属性值(例如,class =“nice”,shape =“sphere”),那么基于频率的概率估计将为零,因此给定条件独立性假设,当所有概率相乘时,我们将得到零,这将影响后验概率估计。
- 该模型可以应用的一个可能的真实世界应用是文本学习。
- 它是一个很好的候选对象,因为它是一个高效的模型,可以处理许多特征(数据集包含 98 个特征)。
b)随机森林
- 该模型的优势是:它可以很好地处理二元特征,因为它是决策树的集合。它不期望线性特征。它适用于高维空间和大量的训练样本。
- 主要的弱点是在处理噪声数据时可能会过拟合。
- 该模型的一个可能的实际应用是预测股票市场价格。
- 它是一个很好的候选者,因为它通常是一个非常准确的分类器,并且能够很好地处理二元要素和高维数据集。
c)支持向量机分类器
- 该模型的优势是:它在没有线性可分数据和高维空间的情况下工作良好。
- 主要的缺点是训练效率可能相当低,因此不适合“工业规模”应用。
- 该模型可以应用的一个可能的真实世界应用是对患有和不患有常见疾病的人进行分类。
- 它是一个很好的候选者,因为它通常是一个非常准确的分类器,并且能够很好地处理二元要素和高维数据集。
创建训练和预测管道
为了正确评估每个模型的性能,我们将创建一个训练和预测管道,使我们能够使用各种大小的训练数据快速有效地训练模型,并对测试数据执行预测。
***# Import two metrics from sklearn - fbeta_score and accuracy_score*** def train_predict(learner, sample_size, X_train, y_train, X_test, y_test):
*'''*
*inputs:*
*- learner: the learning algorithm to be trained and predicted on*
*- sample_size: the size of samples (number) to be drawn from training set*
*- X_train: features training set*
*- y_train: income training set*
*- X_test: features testing set*
*- y_test: income testing set*
*'''*
results = {}
***# Fit the learner to the training data***
start = time() *# Get start time*
learner = learner.fit(X_train[:sample_size], y_train[:sample_size])
end = time() *# Get end time*
***# Calculate the training time***
results['train_time'] = end - start
** *# Get the predictions on the test set***
start = time() ***# Get start time***
predictions_test = learner.predict(X_test)
predictions_train = learner.predict(X_train[:300])
end = time() ***# Get end time***
***# Calculate the total prediction time***
results['pred_time'] = end -start
***# Compute accuracy on the first 300 training samples***
results['acc_train'] = accuracy_score(y_train[:300], predictions_train)
***# Compute accuracy on test set using accuracy_score()***
results['acc_test'] = accuracy_score(y_test, predictions_test)
***# Compute F-score on the the first 300 training samples***
results['f_train'] = fbeta_score(y_train[:300], predictions_train, beta=0.5)
***# Compute F-score on the test set which is y_test***
results['f_test'] = fbeta_score(y_test, predictions_test, beta=0.5)
***# Success***
print("{} trained on {} samples.".format(learner.__class__.__name__, sample_size))
***# Return the results***
return results
初始模型评估
***# Import the three supervised learning models from sklearn***
from sklearn.ensemble import RandomForestClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
***# Initialize the three models***
random_state = 42
clf_A = RandomForestClassifier(random_state=random_state)
clf_B = GaussianNB()
clf_C = SVC(random_state=random_state)
***# Calculate the number of samples for 1%, 10%, and 100% of the training data***
samples_100 = len(y_train)
samples_10 = int(len(y_train)/10)
samples_1 = int(len(y_train)/100)
***# Collect results on the learners***
results = {}
for clf in [clf_A, clf_B, clf_C]:
clf_name = clf.__class__.__name__
results[clf_name] = {}
for i, samples in enumerate([samples_1, samples_10, samples_100]):
results[clf_name][i] = \
train_predict(clf, samples, X_train, y_train, X_test, y_test)
***# Run metrics visualization for the three supervised learning models chosen***
vs.evaluate(results, accuracy, fscore)
改善结果
最后,在本节中,我们将选择最佳模型用于我们的数据,然后,通过调整参数来提高模型的 F 值,在整个训练集(X_train 和 y_train)上对模型执行网格搜索优化。
选择最佳模特
根据评估结果,识别潜在捐赠者的最合适的模型是随机森林分类器,因为它产生与支持向量分类器相同的 F 分数,但时间要少得多。
这与我们对算法的了解是一致的,因为当处理高维数据集时,这是一个非常好的选择,换句话说,是具有大量特征的数据集。
因此,在评估的模型中,这是最有效的一个,也是最适合处理我们的数据集的。
通俗地描述随机森林
为了理解随机森林分类器,我们需要首先介绍决策树的概念。决策树是一种类似流程图的结构,其中每个内部节点代表对数据集属性的测试,每个品牌代表测试的结果,每个叶子代表一个类别标签。因此,算法将对数据进行测试,找出数据集的哪些特征与预测某个结果最相关,并相应地分离数据集。
下面是一个决策树的例子,用来决定你是否把你的车借给别人:
Figure by Author
随机森林是一种元估计器,它在数据集的各种子样本上拟合多个决策树分类器,并使用平均来提高模型的预测准确性,并通过防止模型变得过于复杂和无法对看不见的数据进行归纳来控制过度拟合。它随机选择一些特征,并在每个特征子集中训练每个决策树分类器。然后,它通过让每个决策树为正确的标签投票来做出预测。
Figure by Author
随机森林分类器因其使用简单、高效和良好的预测精度而在分类问题中得到广泛应用。
模型调谐
我们现在将使用 GridSearchCV 微调所选的模型。
***# Import the necessary libraries***
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import make_scorer
***# Initialize the classifier***
clf = RandomForestClassifier(random_state = 42)
***# Create the parameters list***
parameters = {
'max_depth': [10,20,30,40],
'max_features': [2, 3],
'min_samples_leaf': [3, 4, 5],
'min_samples_split': [8, 10, 12],
'n_estimators': [50,100,150]}
***# Make an fbeta_score scoring object using make_scorer()***
scorer = make_scorer(fbeta_score, beta=0.5)
***# Perform grid search on the classifier***
grid_obj = GridSearchCV(estimator=clf, param_grid=parameters, scoring=scorer)
***# Fit the grid search object to the training data***
grid_fit = grid_obj.fit(X_train, y_train)
***# Get the estimator***
best_clf = grid_fit.best_estimator_
***# Make predictions using the unoptimized and model***
predictions = (clf.fit(X_train, y_train)).predict(X_test)
best_predictions = best_clf.predict(X_test)
***# Report the before-and-afterscores***
print("Unoptimized model\n------")
print("Accuracy score on testing data {:.4f}".format(accuracy_score(y_test, predictions)))
print("F-score on testing data: {:.4f}".format(fbeta_score(y_test, predictions, beta = 0.5)))
print("\nOptimized Model\n------")
print("Final accuracy score on the testing data: {:.4f}".format(accuracy_score(y_test, best_predictions)))
print("Final F-score on the testing data: {:.4f}".format(fbeta_score(y_test, best_predictions, beta = 0.5)))
最终模型评估
***# Show the best classifier hyperparameters***
best_clf
观察结果
- 优化后的模型对测试数据的准确率和 F 值分别为:84.8%和 71.38%。
- 这些分数比未优化模型的分数稍好,但是计算时间要长得多。
- 精度和 F 值的朴素预测器基准分别为 24.78%和 29.27%,比用训练模型获得的结果差得多。
附加:特征重要性
在像他这样的数据集上执行监督学习时,一个重要的任务是确定哪些特征提供了最强的预测能力。通过只关注少数关键特征和目标标签之间的关系,我们简化了对现象的理解,这通常是一件有用的事情。
在这个项目的例子中,这意味着我们希望确定少量的特征,这些特征最强有力地预测一个人的收入是最多 50,000 美元还是超过 50,000 美元。
凭直觉,在原始数据的 13 个可用特征中,我们可以推断出预测收入最重要的特征是:
1)年龄
2)教育
3)母国
4)职业
5)每周小时数
等级的顺序逻辑如下:
- 一个人的收入可能会随着时间的推移而增加,老年人往往比年轻人挣得更多。
- 此外,受过高等教育的人往往会获得收入更高的工作,这也是一个与本国密切相关的因素,因为通常情况下,来自经济实力较强国家的人往往有机会接受高等教育。
- 职业是一个需要考虑的重要因素,因为年收入会因行业和部门的不同而有很大差异。
- 最后,每周工作时间通常情况下,工作时间越长的人收入越高。
现在,我们将通过一个具有 feature_importance_ 方法的模型来检查我们的逻辑的准确性:
***# Import Ada Boost Classifier***
from sklearn.ensemble import AdaBoostClassifier
***# Train the supervised model on the training***
model = AdaBoostClassifier().fit(X_train, y_train)
***# Extract the feature importances using .feature_importances****_*
importances = model.feature_importances_
***# Plot***
vs.feature_plot(importances, X_train, y_train)
上一节中的观点部分正确,因为 AdaBoost 测试表明,年龄、每周工作时间和教育程度等特征与预测收入密切相关。然而,我们没有确定资本损失和资本收益。
功能选择
现在有理由提出以下问题:
如果我们只使用数据中所有可用特征的一个子集,一个模型表现如何?
由于需要训练的特征更少,因此期望训练和预测时间更少——以性能指标为代价。从上面的可视化中,我们看到前五个最重要的特征占数据中所有特征重要性的一半以上。这暗示我们可以尝试减少特征空间并简化模型学习所需的信息。
***# Import functionality for cloning a model***
from sklearn.base import clone
***# Reduce the feature space***
X_train_reduced = X_train[X_train.columns.values[(np.argsort(importances)[::-1])[:5]]]
X_test_reduced = X_test[X_test.columns.values[(np.argsort(importances)[::-1])[:5]]]
***# Train on the "best" model found from grid search earlier***
clf = (clone(best_clf)).fit(X_train_reduced, y_train)
***# Make new predictions***
reduced_predictions = clf.predict(X_test_reduced)
***# Report scores from the final model using both versions of data***
print("Final Model trained on full data\n------")
print("Accuracy on testing data: {:.4f}".format(accuracy_score(y_test, best_predictions)))
print("F-score on testing data: {:.4f}".format(fbeta_score(y_test, best_predictions, beta = 0.5)))
print("\nFinal Model trained on reduced data\n------")
print("Accuracy on testing data: {:.4f}".format(accuracy_score(y_test, reduced_predictions)))
print("F-score on testing data: {:.4f}".format(fbeta_score(y_test, reduced_predictions, beta = 0.5)))
对特征选择效果的观察
- 精简数据的精度和 f 值都低于原始数据集。特别是 f 分数 71.38%对 67.57%
- 考虑到在对默认模型、优化模型和精简数据集进行评估时获得的指标,最佳选择是使用带有完整数据集的默认模型版本,因为它在良好的训练时间内产生了准确性和 f 分数的良好组合。
结论
在整篇文章中,我们做了一个端到端的机器学习分类项目,我们了解并获得了关于分类模型的一些见解以及开发一个具有良好性能的分类模型的关键。
这是本系列中解释的第三个机器学习项目。如果你喜欢,看看之前的:
- 回归项目
- 朴素贝叶斯项目
敬请期待下一篇文章!这将是关于无监督学习的理论和概念的介绍。
如果你喜欢这篇文章,那么你可以看看我关于数据科学和机器学习的其他文章 这里 。
如果你想了解更多关于机器学习、数据科学和人工智能的知识 请在 Medium 上关注我,敬请关注我的下一篇帖子!