TowardsDataScience 2023 博客中文翻译(一百二十六)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

进化的客户流失预测:应对干预和再培训

原文:towardsdatascience.com/evolving-churn-predictions-navigating-interventions-and-retraining-d0fcb2619f83?source=collection_archive---------10-----------------------#2023-11-16

处理由于主动保留努力而导致的再培训客户流失模型的新兴挑战

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

·

关注 发布于Towards Data Science ·5 分钟阅读·Nov 16, 2023

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

照片由CrowNUnsplash拍摄

重新训练机器学习模型,特别是那些专注于客户流失预测的模型,是确保其相关性和准确性的重要步骤。然而,重新训练流失模型面临独特的挑战,需要特别注意。其中最显著的是区分干预的因果效应——识别由于主动保留程序而留下的客户,以便专门针对他们。

干预对流失重新训练的影响

考虑以下事件序列:

  1. 初始模型训练: 使用历史客户数据训练模型。

  2. 模型推断: 某些客户被标记为可能流失。

  3. 业务干预: 与这些客户互动,劝说他们留下,或使用促销和个性化优惠等措施来鼓励保留。

  4. 使用新数据进行重新训练: 当模型性能下降时,很可能是重新训练的时机——模型将更新为包含这些干预结果的最新数据。

设想一个场景:一个客户被预测可能流失,他们接受了保留代表的服务,然后选择留下。问题出现在试图解释他们决定的原因时——是干预改变了他们的想法,还是他们在最初被模型误分类了?

当在这种模糊数据上重新训练模型时,存在扭曲模型未来预测的风险——将上述客户标记为“留下”可能具有误导性,因为如果我们没有说服他们留下,他们可能已经离开了。

同样,对于流失标签——干预可能是流失的唯一触发因素,使得某些流失标签不可靠。

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

当保留努力模糊模型清晰度时

处理偏差的可能方法:

  1. 对照组和合成数据: 创建一个“可能流失”客户的子集,对这些客户不进行干预。通过比较对照组和干预组之间的结果,可以判断干预的真实影响——如果某些客户在接受干预时流失更多,则应检查将其排除在干预之外的情况。

    当需要重新训练时,可以利用对照组的数据,并排除干预组的数据,确保模型依赖于可靠的流失标签。

    这种方法的缺点是关键数据的丢失,因此为了弥补排除干预客户的损失,可以尝试生成对照组的合成样本以代表这些客户。这可以通过SMOTE等过采样策略完成。

  2. 反馈调查: 在干预后直接与客户接触,以了解他们留下/流失的原因。收集到的见解可以提供关于干预效果的清晰度,并帮助区分真正留下的客户和那些被干预所影响的客户。

  3. 合并模型: 尝试将初始训练模型与新模型进行结合。平均预测或使用集成方法可以减少任何单一模型偏见主导总体预测的风险。

    请注意,随着时间的推移,最初的训练数据可能会变得不那么相关。

提升建模:精细化流失预测的黄金标准

与预测谁可能离开的传统流失模型不同,提升建模识别由于干预而直接改变行为的客户。

通过将处理组与对照组进行比较,这些模型预测哪些客户因干预而留下,另一方面哪些客户因干预而离开。

这种有针对性的方法帮助企业优化资源并最大化客户价值。

客户可以被分为四种理论类别,基于他们是否接受了保留努力:

确定的客户: 不会流失的客户。针对他们不会带来额外回报,但会增加成本,如沟通努力和潜在的财务激励。

无可救药者: 无论干预如何都会流失的客户。他们不会增加收入,且与确定性客户相比,可能导致成本减少,因为他们不会利用提供的激励。

可说服者: 仅在保留努力后才会留下的客户。他们贡献额外收入。

不可打扰者: 仅在被针对时才会流失的客户。保持他们不受干扰是有利的,而针对他们会增加显著成本而没有收入增长,使他们成为“沉睡的狗”。

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

提升建模的目标是专门针对可说服者。

挑战在于我们无法确定个体属于哪个类别。我们不能同时将他们处理并将他们放在对照组中。那么,我们如何识别他们呢?我们怎么知道他们是否被说服了,或者他们最初是否不打算离开?这就是提升建模的作用所在。

针对这一挑战有几种提升方法,我们将查看“转换结果”方法。该方法需要来自对照组和处理组的数据,并将我们的重点从分类任务转移到回归任务。

标签是基于特定公式分配的,对于处理倾向等于 0.5 的随机处理分配,目标变量转化为以下值:

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

转换结果标签(针对 50%的处理倾向)

我们可以使用诸如均方误差(MSE)的损失函数作为解决此回归问题的指标:

对于可说服者,对照组标记为 0,处理组标记为 2。它们之间的最低 MSE 将是分数为 1 的位置,代表可说服者的提升。

对于“打扰者”,控制组为-2,处理组为 0,最佳预测为-1,表示提升。

对于“失去的原因”和“确定的情况”,最佳预测是 0。

理想情况下,应该以最高得分为目标,尽量保留可以说服的人,尽可能避免打扰人群及其他人群。

探索流失预测的未来

正如我们所探讨的,流失预测的领域,其干预复杂性和不断变化的数据,为企业带来了挑战。

重新训练模型不仅仅是一个技术练习,更是理解客户行为和理清真正的留存的一个部分。利用控制组、反馈机制和提升建模等工具。

但也许最关键的是认识到数据不是静态的。我们对客户行为的理解必须适应这种变化。拥抱这种动态情况,不断完善模型,并保持对不断变化的模式的敏感,将是未来成功预测和管理流失的关键。

进一步阅读:

精确算法还是启发式算法?

原文:towardsdatascience.com/exact-algorithm-or-heuristic-20d59d7fb359

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

一车队的卡车。图像由 Dall-E 2 提供。

这是一个逐步指南,帮助你为数学优化问题做出正确的选择。

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

·发表于 Towards Data Science ·6 分钟阅读·2023 年 2 月 28 日

你是否在寻找解决优化问题的最佳方法?在解决优化问题时,主要有两种方法:(元)启发式算法和精确算法。每种方法都有其自身的优点和缺点,方法的选择将取决于问题的具体特性。在这篇文章中,我们将探讨启发式算法和精确算法之间的区别,并帮助你决定哪种方法最适合你的问题。

常用的精确算法包括 线性规划混合整数规划约束编程。著名的 启发式算法 包括局部搜索、遗传算法和粒子群优化。为了改进像局部搜索这样的启发式算法,将其与模拟退火或禁忌搜索等元启发式算法结合起来是很有趣的。

在这篇文章中,我们将以一个例子开始,比较并解释精确算法和启发式算法的不同特性。接下来的部分将解释在选择精确算法或启发式算法时需要考虑的一些因素,首先提供一个流程图,帮助你更容易做出正确的选择!

除了本文的考虑因素之外,其他因素也可能影响你的选择,比如对不同方法的经验或者直觉。这是一个提醒,说明本文试图进行概括,但每个问题可能有其独特的特征和情况,促使你选择某种方法,或者偏离流程图。

通过示例比较

让我们用一个例子来解释精确算法和启发式算法之间的主要区别:假设你有一家需要将包裹送到不同地点的配送公司,拥有一车队的卡车。问题的目标是确定每辆卡车的最佳路线,以最有效的方式配送包裹,同时考虑到距离、配送时间窗口和卡车容量等因素。这个问题是带容量限制的车辆路径问题的一个变种。

针对这个问题的一个精确算法可能是一个将问题表述为数学优化问题的 MIP 模型。MIP 模型将考虑问题的所有约束条件和目标,找到最小化总配送时间和成本的最优解决方案,同时确保所有包裹按时送达,并在每辆卡车的容量范围内。

然而,即使使用强大的计算机,解决大型 MIP 模型也可能计算开销大耗时。这时,启发式算法就发挥了作用。针对这个问题的启发式算法可能是一个简单的最近邻算法,它将每个包裹分配给最近的卡车,然后使用局部搜索优化每辆卡车的路线。虽然这种方法可能不能保证最优解,但它可以快速生成高质量的解决方案,并且接近最优解,可能足够满足实际应用需求。

总而言之,精确算法和启发式算法之间的主要区别在于准确性水平效率。精确算法旨在找到最优解,但对于大规模问题可能计算开销大不切实际。启发式算法则旨在快速高效地找到一个好的解决方案,但可能不能保证最优解。在这些方法之间的选择取决于问题的具体特征以及准确性和效率之间的权衡。

精确算法还是启发式算法?

在选择精确算法和启发式算法时,有几个重要的因素需要考虑。我将这些因素分为四个主要主题:解决方案质量、性能、灵活性和成本。如果你在寻找一个快速的答案(启发式算法还是精确算法),这个流程图可能会有所帮助:

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

选择精确算法和启发式算法的流程图。点击放大。图片由作者提供。

让我们更深入地看看每一个因素。

解决方案质量

如果你最重要的目标是获得尽可能高质量的解决方案,因为稍微更好的解决方案会带来很大价值,你可以停在这里。在这种情况下,你应该选择精确算法。通过使用精确算法,你可以知道最优解与当前解之间的差距,并可以继续搜索,直到找到最优解。

但是,有一个重要的注意事项,如果问题非常庞大,精确算法可能需要很长时间才能找到最优解。可能需要几个小时、几天或几周。特别是如果你使用像 CBC glpk这样的开源求解器,你不能期望快速得到最优解。你可能更倾向于使用像GurobiCPLEX这样的商业求解器。即使使用最先进的求解器,找到最优解也可能需要大量时间。

启发式算法的难点在于很难知道解决方案的质量。你可以将其与基准进行比较,但你不知道最优解与当前解之间的差距。

性能

启发式算法通常比精确算法更快,因为它们以牺牲准确性换取速度。精确算法旨在找到问题的最优解,但这可能计算成本高昂且耗时,特别是对于大规模问题。精确算法通常会探索整个解空间,对于某些问题,这个解空间可能非常庞大,并且需要使用复杂的数学技术来找到最优解。

启发式算法使用简单的经验规则来指导解决方案的搜索。它们通常关注最有前景的解决方案的一个子集。这减少了找到解决方案所需的计算工作量,并允许启发式算法在更短的时间内生成高质量的解决方案。

在启发式算法中实现特定问题的知识更容易,以指导解决方案的搜索,这有助于避免探索那些不太可能包含好解的解空间部分。这可以进一步减少找到解决方案所需的计算工作量。

灵活性

启发式算法通常比精确算法更灵活,因为它们不受精确算法相同的限制,可以适应特定问题的特征或约束。启发式算法通常可以使用简单的算法和数据结构来实现,使其比精确算法更容易开发和修改。这使得启发式算法能够快速适应新的或变化的问题实例,使其成为需要快速找到解决方案或问题可能会随时间变化的情况的良好选择。

精确算法基于数学模型,这些模型为解决问题提供了一个正式而严谨的框架。这些模型基于一组约束和目标函数,定义了待解决的问题。然而,这种刚性可能使得处理特定问题特征或约束变得困难,这些特征或约束可能不容易被数学模型所捕捉。

总体而言,启发式算法的灵活性是其主要优点之一,使其能够应用于广泛的问题,并快速适应变化的问题特征。

成本

当涉及到解决大型问题时,获得一个高质量的求解器可能需要花费大量的金钱。然而,对于那些需要最高精度和速度的人来说,这笔投资通常是值得的。一个很好的补充是:在获得商业求解器许可证时,你通常会得到很好的支持,经验丰富的人可以帮助你进行模型构建并提升性能。

就时间而言,雇佣顾问来为你实施解决方案可以是一种高效的快速完成任务的方式。然而,这种方法也可能成本较高,且不总是能获得最优结果。对于那些没有预算雇佣顾问或希望对解决问题过程保持更大控制的人来说,学习和实施不同的启发式算法可以是一个可行的选择。尽管这可能需要更多的前期时间,但投资于启发式算法的开发从长远来看可能会带来更为定制和高效的解决过程。

结论

这篇文章比较了用于解决优化问题的精确算法和启发式算法。精确算法旨在获得最优解,但可能会比较慢且计算开销大。启发式算法为了速度牺牲了准确性,但可以快速生成良好的解决方案。选择取决于具体问题以及准确性和效率之间的权衡。提供的流程图旨在帮助选择这两种方法中的一种。考虑因素包括解决方案质量、性能、灵活性和成本。

还有一点最终说明:每个问题都是不同的,有时细微的差别可能会让你选择另一种方法,这完全没问题。你可以在后续阶段重新考虑你的选择,或者为了比较目的尝试两种方法。

相关内容

为什么每个数据科学家都应该学习数学优化 [## 为什么每个数据科学家都应该学习数学优化

数据科学课程正专注于数据可视化、特征工程、数据处理、(非)监督学习……

如何处理优化问题? 如何处理优化问题? [## 如何处理优化问题?

轻松示例及其解决方案和代码。

每个数据科学家都应该了解的数学优化启发式方法

局部搜索、遗传算法等。

如何处理优化问题

使用 AWS 和 Power BI 检查美国的航班

原文:towardsdatascience.com/examining-flights-in-the-u-s-with-aws-and-power-bi-297a29fb2c13

我们可以通过 ETL 和 BI 获得哪些见解?

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

·发布于 Towards Data Science ·阅读时间 9 分钟·2023 年 7 月 6 日

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

图片由 John McArthur 拍摄,发布于 Unsplash

目录

∘ 引言

∘ 问题陈述

∘ 数据

∘ AWS 架构

∘ 使用 AWS S3 的数据存储

∘ 设计架构

∘ 使用 AWS Glue 的 ETL

∘ 使用 AWS Redshift 进行数据仓储

∘ 使用 AWS Redshift 提取见解

∘ 使用 Power BI 进行数据可视化

∘ 未来步骤

∘ 结论

∘ 参考文献

引言

空中旅行已成为我们生活中不可或缺的一部分。它不仅是企业建立网络和开展商业活动的手段,也是家庭探访亲人或旅行的方式。

尽管航空行业具有很大影响力,但它也面临许多波动。由于经济萧条和繁荣、气候变化、Covid-19 大流行以及对可再生能源的依赖推动等外部因素,这一行业不断发生变化。

要关注这些变化及其对空中旅行的影响,值得跟踪这些航班的时间变化。这一工作需要一个稳健的数据仓储、数据分析和数据可视化策略。

问题陈述

本项目有两个主要目标。第一个目标是利用亚马逊网络服务(AWS)提供的资源,构建一个数据管道,以便存储、转换和分析美国航班数据。

第二个目标是构建一个 Power BI 可视化工具,能够有效地展示数据中的关键发现。

数据

该项目使用的数据集来自 交通统计局。它主要报告了 2003 年至 2023 年间机场和航空公司总航班、延误和取消的数量。

以下是数据集的预览:

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

预览(作者创建)

一眼望去,原始数据集存在一些问题。

首先,airport_name 字段中的信息包括多个信息点。它不仅提供了机场的名称,还包括城市和州。为了便于访问这些信息,这个字段将需要拆分成 3 个单独的字段。

其次,这些数据当前采用了平面模型(即 1 张表)。然而,这并不是最佳设置,因为数据包含多个有关系的实体。

这些缺陷必须在数据可以用于分析或可视化之前解决。

AWS 架构

让我们探讨构建数据管道所需的 AWS 架构。

所需资源的最佳示意图如下:

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

AWS 架构(作者创建)

云解决方案使用 Amazon S3 存储原始数据和转换后的数据,AWS Glue 创建 ETL 作业以促进数据转换,AWS Redshift 创建云数据仓库,使用户能够使用 SQL 从数据中提取见解。

最后,使用 Power BI 以仪表板形式展示数据提供的关键指标。

使用 AWS S3 存储数据

该项目利用了两个 S3 桶:flights-data-rawflights-data-processed

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

S3 桶(作者创建)

flights-data-raw 桶包含原始数据集。

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

flights-data-raw 桶(作者创建)

flights-data-processed 桶将包含数据转换后的内容(目前为空)。

设计模式

接下来,确定适合这些数据的模式非常重要。原始数据存储在一个平面文件中,该文件包含一张表:

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

原始数据(作者创建)

不幸的是,这个模式只有一个表,其中包含多个实体,如日期、机场和航空公司。为了优化数据库以更快地检索数据,可以将这个平面模式转换为星型模式,使用维度建模:

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

星型模式(作者创建)

在这个新的模式中,flights 表作为事实表,而 datecarrierairport 表作为维度表。

使用 AWS Glue 的 ETL

使用 AWS Glue 创建的 ETL 作业可以将原始数据转换为事实表和维度表,并将它们加载到 flights-data-processed 桶中。

ETL 任务使用导入的 Python 脚本进行维度建模。

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

创建 ETL 任务(作者创建)

该脚本使用 boto3,即 Python SDK,从 flights-data-raw 存储桶中提取原始数据集,创建星型模式中的 4 个表,并将它们作为 csv 文件加载到 flights-data-processed 存储桶中。

例如,以下代码片段用于创建 carrier 表。

用于创建模式中 4 个表的完整脚本可以在 GitHub 仓库 中访问。

ETL 任务运行正常:

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

成功运行(作者创建)

数据集已经转换为一个事实表和 3 个维度表,形式为 csv 文件,全部存储在 flights-data-processed 存储桶中。

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

flights-data-processed 存储桶(作者创建)

使用 AWS Redshift 进行数据仓库管理

使用 AWS Glue,最初的平面模型现在可以在数据仓库中用更合适的星型模式表示。

该数据的云数据仓库将通过 AWS Redshift Serverless 创建。这包括创建一个命名空间 flights-namespace 以及一个名为 dev 的数据库。此外,还需要一个名为 flights-workgroup 的工作组,用于编写 SQL 查询。

注意:工作组已配置为允许 VPC 外的设备访问数据库。这在使用 Power BI 创建可视化时将非常有用。

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

工作组(作者创建)

现在,我们可以打开 Redshift 中的查询编辑器,并开始在 dev 数据库中创建事实表和维度表。

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

查询编辑器(作者创建)

首先,需要使用以下命令在仓库中创建模式中的 4 个表:

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

创建的表(作者创建)

这四个表现在已经在数据仓库中,但由于数据仍在 flights-data-processed 存储桶中,因此它们都是空的。

数据可以使用 COPY 命令复制到此数据仓库中。

例如,可以使用以下命令语法将 flights.csv 中的数据复制到 flights 表中:

注意:iam_role 变量应分配为创建工作组时所选的 iam 角色。

通过对 flights-data-processed 存储桶中的每个 csv 文件执行 COPY 命令,4 个表格应被填充所需的数据。

例如,这里是机场表的预览:

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

查询输出(作者创建)

使用 AWS Redshift 提取洞察

现在所有表格都已加载数据,我们可以开始使用 SQL 查询进行分析!

由于数据已经转化为具有维度建模的星型模式,因此可以高效地检索数据,运行时间很短,因此这个设置非常适合临时分析。

以下是一些可以通过 SQL 查询回答的问题示例。

  1. 2022 年哪些机场的航班最多?

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

查询输出(由作者创建)

2. 从 2019 年起,哪种类型的延误对总延误的贡献最大?

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

查询输出(由作者创建)

3. 在约翰·F·肯尼迪机场,每年的延误百分比变化是多少?

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

查询输出预览(由作者创建)

使用 Power BI 进行数据可视化

当前的云数据仓库使用户能够以较少的时间和成本回答关键问题。

然而,我们可以更进一步,通过创建一个可视化工具,最终用户可以用它来回答类似的问题。

实现这一目标的一个方法是使用 Power BI 创建仪表板,这是一款非常受欢迎的 BI 工具。

尽管通过可视化可以挖掘出许多指标,但仪表板将重点审查以下内容:

  • 航班、延误和取消的数量总结

  • 跟踪航班、延误和取消的数量随时间的变化

  • 确定最常用的机场和航空公司

  • 各种类型延误的分解

此外,仪表板将包括允许用户针对特定时间和地点的筛选器。

总体而言,这些功能可以结合成以下仪表板的形式:

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

完整仪表板(由作者创建)

使用这样的工具,无法访问数据或不了解 SQL 的用户可以轻松回答关键问题。

此类问题包括:

  1. 哪个航空公司在 JFK 机场的航班最多?

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

在筛选器中选择 JFK(由作者创建)

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

最受欢迎的航空公司(由作者创建)

2. 从 2019 年到 2022 年,加利福尼亚州取消了多少航班?

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

在筛选器中选择 CA(由作者创建)

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

取消航班数量(由作者创建)

3. 什么类型的延误对美国航空的总延误贡献最大?

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

在筛选器中选择美国航空(由作者创建)

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

航班延误分解(由作者创建)

未来步骤

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

照片由 Anna Shvets 拍摄:www.pexels.com/photo/white-round-medication-pill-on-red-surface-3683087/

当前在 AWS 和 Power BI 中的设置促进了快速且廉价的数据分析和可视化。然而,值得考虑未来数据的新应用。

  1. 整合新数据源

如果要包括新的数据源,则需要相应地修改模式。此外,还必须创建额外的 ETL 作业,以便将这些源的数据无缝地集成到现有的数据仓库中。

2. 执行时间序列分析

BTS 提供的数据是时间序列。因此,考虑使用时间序列分析并构建预测模型以预测未来的航空旅行需求是有意义的。

结论

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

图片由 Alexas_Fotos 提供,来源于 Unsplash

记录丰富的数据集,例如 BTS 提供的数据集,可能难以管理。然而,借助 AWS 提供的资源,可以构建一个数据管道来处理数据,并将其结构化为一种形式,使用户能够以具有成本效益的方式提取见解。

此外,像创建的 Power BI 仪表板这样的可视化是对某些指标进行背景化处理并为观众创造有影响力故事的有效方法。

要获取用于在 AWS Glue 中构建 ETL 作业的代码或用于创建表格和进行分析的 SQL 查询,请访问 GitHub 仓库:

## GitHub - anair123/Tracking-U.S.-Flights-With-AWS-and-Power-BI

通过在 GitHub 上创建账户,您可以为 anair123/Tracking-U.S.-Flights-With-AWS-and-Power-BI 的开发做出贡献。

github.com

感谢阅读!

参考文献

  1. 航空公司准时统计和延误原因。BTS。(无日期)。www.transtats.bts.gov/ot_delay/OT_DelayCause1.asp?20=E

K-最近邻的示例应用

原文:towardsdatascience.com/example-applications-of-k-nearest-neighbors-e6e47cd73f1f

为什么简单的算法比你想象的更实用

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

·发表于 Towards Data Science ·6 分钟阅读·2023 年 8 月 4 日

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

布鲁克·凯格尔Unsplash 上的照片

我第一个机器学习算法是 K-最近邻(KNN)模型。这对初学者很有意义——直观、易于理解,你甚至可以在不使用专用软件包的情况下实现它。

因为它对初学者很有意义,同时在向任何不熟悉机器学习的人解释时也很有用。我无法用语言表达说服一群怀疑者接受 KNN 方法比接受黑箱随机森林要容易得多。

这是一个被低估的建模方法,在转向更复杂的算法之前,它作为一个很好的基准,而在许多用例中,你可能会发现更复杂算法的时间和成本并不值得。

为了激发你的建模灵感,以下是 KNN 的三个示例应用,你可能会发现它在现实世界中的效果远超你的预期。

市场组合建模(MMM)

我从事市场营销工作,我在 MMM 系统中的工作通常涉及识别能够提高广告效果和/或扩大广告覆盖面以触及更多人的营销渠道。在高层次上,这被称为市场(或媒体)组合建模。

任何类型的 MMM 建模的目标是理解每个营销输入在孤立和与其他输入结合时的有效性,然后优化营销组合以达到最大效果。

最基本的方法是根据历史数据预测不同营销策略的影响。KNN 模型将每个营销策略视为多维空间中的一个点,其中维度可以是各种营销输入,如广告支出、促销活动、定价策略等。

当提出新的营销策略或现有策略需要优化时,模型可以通过查看“k”个最相似的历史策略,即多维空间中的“k”个最近邻,来预测该策略的结果。

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

3D 和 2D 空间中的媒体组合建模结果示例。图表由 Plotly 构建。

新策略的结果被预测为这些“k”个最近邻的结果的加权平均值,即已知策略和结果。我们可以根据每个邻居与新策略的距离设置权重,距离较近的邻居对预测的影响更大。

这种方法允许对不同营销策略的潜在影响有更细致的理解,并量化营销组合整体的效果。

广告定位

广告定位是根据消费者的属性向特定群体展示广告的过程。像 Instagram 和 YouTube 这样的数字广告平台使用基于成千上万属性的极其精确的定位算法,但这种策略在电视等不那么精确的媒介上也同样有效。

基于距离的模型如 KNN 和聚类算法可以用于预测结果,比如用户响应广告的可能性,或寻找新的目标用户,这些用户在人口统计特征上类似于已经在使用的群体。

例如,如果一组具有相似浏览习惯和人口统计特征的用户对某个特定广告反应积极,则 KNN 模型可以预测具有几何相似属性的新用户也会对该广告做出积极响应。

我们可以在这里使用几种不同的建模方法。可能“最简单”且最直观的方法是根据其他已知用户预测一个已知用户响应广告的可能性。然而,经过这种方式训练的模型还有更强大的用途。

我们可以创建一个模拟数据集,其中包含尽可能多的用户属性组合,然后查看哪些属性组合产生最佳结果,而不是对已知用户进行预测。这样,我们不仅可以找到与最佳结果最匹配的用户,还可以发现之前未与我们产品互动的新用户。

一个附加结果是,我们可能还会突出那些之前可能不明显的潜在有效受众。

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

示例建模结果显示,一组用户与当前优化的目标组匹配,而模型发现的另一组用户也产生了最佳结果。图表由 Plotly 创建。

KNN 很少是最精确的模型选项,但这在上述模拟场景中对我们有利。KNN 精度较低的一个积极权衡是,减少过拟合特别容易。只需增加 k 值直到问题得到改善,就能解决许多情况下的问题。

由于 KNN 预测是现有结果的加权平均,因此模型几乎不可能产生远超已观察范围的错误结果。

此外,广告定向直接来源于了解目标受众的特征。一个我们能够自信地知道不会过拟合的模型,并且可以用简单的术语向关键利益相关者解释的模型,通常比其他模型类型更具优势。

影响者识别

虽然广告可以根据可量化的数据进行定向,但拥有一个值得信赖的人提供产品建议是建立品牌声誉的最有效方式之一。这在以视频为主的社交媒体平台如 TikTok 上尤为有效。

影响者营销通常比其他营销渠道具有更高的参与度和转化率,因此拥有一个影响者网络来扩大品牌曝光非常有价值。

我们可以通过一组特征来描述每个影响者账户,例如关注者数量、参与率、他们制作的内容类型等。

KNN 模型可以以与我们之前示例相同的方式找到数学上类似的账户,或者我们可以采取不同的路径,将影响者分类到符合某些标准的组中。

既然我们已经熟悉了“通过距离寻找相似事物”的方法,让我们来看一下分类选项。

作为分类器使用的 KNN 模型是一个直观且通常表现良好的模型选项,特别适用于那些不急于将属性与我们之前见过的完全不同的事物进行分类的应用场景。

对于这个用例,我们可以利用 KNN 模型的多个属性,以扩展我们的产品覆盖范围,而不会陷入通过类似关注者的账户反复接触相同人群(重复覆盖)的陷阱。

我们首先构建 KNN 分类器,将影响者分到可能适合类似我们产品的组中,或者那些可能已经被我们的竞争对手使用的组中。

以示例为例,假设我们有一个 500 个账户的候选名单,我们的数字营销团队认为这些账户可能效果不错,而我们的分类器找到了一组 100 个账户,这些账户是扩展我们影响者计划的好候选者。

我们希望避免最相似的账户,以防止重复的覆盖,因此我们可以利用模型计算出的实际距离,并寻找位于最近 k 点之外的账户。k 的数值可以进一步优化为具有实际意义的最小距离阈值,但这里假设 k=20。

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

示例分类坐标仅在两个模型特征的维度内。轮廓背景显示了这两个维度内的模型选择空间。请注意排除那些具有重复覆盖风险的影响者。图表由 Plotly 创建。

目前我们有 100 个符合我们标准的影响者,以及 20 个我们想忽略的影响者,因为重复覆盖的可能性太高。然后我们可以将这个列表交给数字营销团队去处理,我们已经有效地扩展了我们的产品展示对象。

这里的方法对 KNN 来说有些独特,因为我们能够利用 KNN 模型的分类器属性和用于校准模型本身的基于距离的信息。

KNN 有一些值得注意的缺点。训练速度受到数据大小和特征数量的严重影响,对于非常大的数据集而言会变得极其缓慢。模型也对无关特征高度敏感,因此共线性等问题是主要问题。具有不同尺度或高度偏斜分布的特征由于距离计算可能会引发问题。

然而,当 KNN 构建和应用正确时,它是一个非常可靠的选项。下次你在进行模型选择时,记得好好考虑这个值得信赖的选项。

## 3 个你应该知道的常见时间序列建模错误

常见错误及如何避免它们,附带实际例子

[towardsdatascience.com

Python 异常处理:从基础到高级,再到技巧

原文:towardsdatascience.com/exception-handling-in-python-from-basic-to-advanced-then-tricks-9b495619730a

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

图片来源于 Thomas Malyska 来自 Pixabay

探索 Python 异常处理的隐藏秘密

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

·发布于 Towards Data Science ·10 分钟阅读·2023 年 4 月 3 日

Python 编程的一个重要方面是异常处理,这指的是在程序执行过程中处理错误和意外事件的方式。异常处理对编写健壮且可靠的代码至关重要,因为它使程序员能够以结构化和受控的方式处理错误和异常。

在这篇文章中,我将提供一个全面的 Python 异常处理指南,涵盖从基本的 try-except 块到更高级的技术。不论你是 新手 还是 经验丰富的开发者(你可以从第三部分开始),这篇文章将为你提供 Python 异常处理的完整概述,并附带一些你可能未曾遇到过的实用技巧和建议。所以,无论你是刚开始学习 Python 还是希望提升你的异常处理技能,这篇文章都是帮助你入门的最佳资源。

1. 基础知识

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

图片来源于 Saul 来自 Pixabay

1.1 最简单的异常处理

让我们从 Python 中最简单的异常处理开始。基本上,我们有一段可能在运行时出现任何异常的代码,我们可以将它们放在“try”块中。然后,在“except”块中,我们可以对其进行处理,例如显示一些消息以指示发生了错误。

try:
    # Code that may raise an exception
    x = 5 / 0
except:
    # Code to handle the exception
    print("An error occurred")

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

同时,请注意程序确实成功运行了。尽管有错误,但我们“捕获”了错误,因此它不会被视为“崩溃”。

1.2 捕获特定类型的异常

有时,代码片段可能会导致不同类型的异常。此外,我们可能希望以不同的方式处理不同类型的异常。在这种情况下,我们可以在except关键字后指定错误类型。同时,我们可以链式使用多个except块来处理多种错误类型。

try:
    x = 5 / 0
except ZeroDivisionError:
    print("You can't divide a number by zero!")
except:
    print("Unknown error occurred")

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

常见的是最后一个except块没有明确的错误类型。因此,如果上面没有except块捕获到异常,它将落入最后一个except块中。

try:
    x = int("foo")
except ZeroDivisionError:
    print("You can't divide a number by zero!")
except:
    print("Unknown error occurred")

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

在这个例子中,错误实际上应该是TypeError,因为字符串"foo"不能转换为数字。因此,ZeroDivisionError没有捕获到异常。所以,它最终落入默认的except块中。

1.3 访问异常的详细信息

关于上述“未知”错误,有没有办法从异常中获取更多信息?换句话说,尽管发生了意外,我们是否有方法来获取有关异常的一些线索?

答案是肯定的。我们可以在except关键字后放置一个参数,并从这个参数变量中访问异常的详细信息。

try:
    x = int("foo")
except ZeroDivisionError:
    print("You can't divide a number by zero!")
except Exception as e:
    print("An Error occurred:", e)

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

在这个例子中,我们在except关键字后使用Exception,这是所有类型异常的父类,并将此异常捕获为变量e

这相当于说,“请捕获变量e中的任何类型的异常”。然后,我们可以打印该变量以获取消息。因此,我们知道异常实际上是我们尝试将字面量字符串转换为整数。

2. 仍然是基础知识,但实际应用

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

图片来自 LaterJay PhotographyPixabay

现在,让我们来看一些 Python 中异常处理的实际应用模式。在本节中,演示将在 Python 函数中进行。

2.1 没有异常处理

如果我们不处理异常会怎样?当然,程序会崩溃。让我们来看一下“Traceback”是如何告诉我们错误的。

def divide(x, y):
    return x / y

def calculate(a, b):
    result = divide(a, b)
    print("Result:", result)

calculate(10, 0)

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

程序从calculate(0, 0)开始,然后calculate()函数调用divide()函数。在上述示例中,divide()中的x / y引发了ZeroDivisionError

从追踪信息来看,最后一块告诉我们异常来自哪里。由于没有异常处理,它会将异常抛回到其父函数calculate()体内。在这个函数内部,异常仍未被处理。因此,它会再次抛回到其父级,即我们调用calculate()函数的地方。程序崩溃是因为异常没有被处理并且达到了根级别。

2.2 带有异常处理的程序

等一下,这意味着我们不必在异常可能发生的任何地方进行处理。相反,我们可以在特定的适当级别处理它们。

例如,有一行代码调用了一个函数,在这个函数内部,它调用了许多其他可能引发不同类型异常的函数。在这种情况下,我们可能只需将这一行代码放入 try-except 块中,以便它能被处理。

def divide(x, y):
    return x / y

def calculate(a, b):
    try:
        result = divide(a, b)
        print("Result:", result)
    except ZeroDivisionError:
        print("You can't divide a number by zero!")

calculate(10, 0)

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

在上述示例中,我们在calculate()函数中处理了异常。虽然异常发生在divide()函数中,但它会抛到父级,并在calculate()函数中被捕获。

2.3 finally

我想把这篇文章做成一个关于 Python 异常处理的完整指南。所以,我想我们可以深入探讨finally块。

长话短说,finally块中的代码无论是否有异常都会被执行。

try:
    x = 5 / 1
except ZeroDivisionError:
    print("You can't divide a number by zero!")
finally:
    print("Calculation finished")

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

finally块最常见的用例之一是关闭诸如数据库连接和打开的文件等资源。这是一种很好的方式来避免意外行为或内存泄漏。

2.4 故意引发异常

除了捕获异常,我们还可以故意引发异常。这对于调试和控制流目的非常有用,允许我们跳转到代码的不同部分或退出程序。

def calculate(a, b):
    try:
        raise Exception("I just want to raise an exception!")
    except Exception as e:
        print(e)

calculate(10, 0)

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

3. 你可能不知道的额外技巧

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

图片来自 PexelsPixabay

如果你对 Python 不陌生,你可能会跳过这一部分,寻找一些高级技巧或填补知识空白。我希望这一部分能为你提供新的见解,并帮助你进一步完善对 Python 异常处理的理解。

3.1 else

你知道吗,try ... except ... finally 不是 Python 异常处理的全部?我猜你可能不知道我们还可以在异常处理中使用 else。只有在没有异常的情况下,else 块才会被执行。

try:
    x = 5 / 1
except ZeroDivisionError:
    print("You can't divide a number by zero!")
else:
    print("The result is: ", x)
finally:
    print("Calculation finished")

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

实际上,else 块并不是必须了解的东西。从理论上讲,我们可以将代码放在可能引发异常的行之后。如果没有异常,它会运行。

然而,我们可能会有几个较弱的理由来使用 else 块。首先,它可能提高代码的可读性,因为理解起来很自然: “如果有异常,按这种方式处理,否则请执行这段代码”。其次,else 块将可能引发异常的代码与不会引发异常的代码物理上分开。

3.2 警告模块

这可能与异常处理没有直接关系。然而,有些读者可能对此感兴趣。如果你曾经使用过 Pandas 库,有时它会在我们使用一些过时的 API 或做一些有风险的操作时给出警告。

这怎么做?答案是使用 warning 模块。

import warnings

def calculate(x, y):
    try:
        result = x / y
    except ZeroDivisionError:
        print("You can't divide a number by zero!")
    else:
        if x == result:
            warnings.warn("All numbers divide by 1 will remain the same.")
        print("Result: ", result)

calculate(10, 1)

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

3.3 断言 — 引发异常的另一种方式

Python 中的另一个相关技术是断言。它用于检查程序执行期间某个条件是否为真或假。如果条件为真,程序会正常执行。如果条件为假,则会引发 AssertionError,中断程序的正常流程。

def calculate(x, y):
    assert y != 0, "You can't divide a number by zero!"
    result = x / y
    print("Result: ", result)

calculate(10, 0)

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

断言通常用于 Python 的调试和单元测试。如果条件满足,则不会发生任何事情。

def calculate(x, y):
    assert y != 0, "You can't divide a number by zero!"
    result = x / y
    print("Result: ", result)

calculate(10, 1)

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

3.4 自定义异常类型

有时,我们可能希望定义和使用自定义异常类型,以便向用户提供更具体和详细的错误信息,或在代码中区分不同类型的错误。

我们可以简单地定义一个自定义异常,如下所示。

class CustomException(Exception):
    def __init__(self, message):
        super().__init__(message)

raise CustomException("This is a custom exception")

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

当然,我们可以做任何喜欢的事情,因为这是一个定制化的课程。

class CustomException(Exception):
    def __init__(self, message):
        super().__init__(message)

    def __str__(self):
        return f"A customised exception has occured: {self.args[0]}"

raise CustomException("This is a custom exception")

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

在 Python 中使用自定义异常可以发挥我们的想象力,并提供处理错误和异常的最大灵活性。

4. 抑制模块

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

图片由 Gerd Altmann 提供,来源于 Pixabay

在上一节中,我想介绍contextlib中的suppress模块。它是 Python 内置的,但很少有人知道且使用得很少。然而,它在某些情况下非常有用。

假设我们有一些可能引发异常的代码行。然而,我们可能不关心这些异常。因此,与其引发这些异常并处理它们,最简单的方法是忽略它们,或“抑制”它们。

例如,下面的代码将不输出任何内容。

from contextlib import suppress

with suppress(ZeroDivisionError):
    x = 5 / 0
    print(x)

上述代码使用了 with 语句与suppress函数。它将忽略代码中发生的所有ZeroDivisionError

为什么这很有用?想象一下我们有一系列用户输入,其中一些输入值可能无效。假设我们完全不关心这些无效输入,而是只想忽略它们,处理那些有效的输入。

让我们通过一个包含项目的列表来模拟这种场景。

nums = [3, -1, -2, 1, 1, 0, 3, 1, -2, 1, 0, -1, -1, -1, 3, -2, -1, 3, '3', -1] 

result = 0
for num in nums:
    with suppress(ZeroDivisionError, TypeError):
        result += 1/num

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

如上所示,那些零和字符串被简单地忽略了。代码看起来非常整洁。

如果你想深入探索suppress模块,我有一篇特别的文章会做深入讲解。

## 快速 Python 提示:在不使用 try except 的情况下抑制已知异常

以更优雅的方式处理 Python 中的已知异常。

towardsdatascience.com

总结

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

图片由Mirka提供,来源于Pixabay

在这篇文章中,我们探讨了 Python 异常处理的不同方面。介绍了一些处理异常的有用技巧,比如使用warning模块和用suppress模块来抑制特定的异常。

通过掌握 Python 中的异常处理,你可以编写更健壮和可靠的代码,能够以结构化和控制的方式处理意外事件和错误。无论你是初学者还是经验丰富的 Python 开发者,理解异常处理对于编写有效和高效的代码至关重要。我希望这篇文章能为你提供关于 Python 异常处理的全面指南,以及一些有用的技巧和建议,帮助你提高异常处理技能。

[## 通过我的推荐链接加入 Medium - Christopher Tao

感谢阅读我的文章!如果你不介意的话,请请我喝杯咖啡 😃 你的会员费支持成千上万的人…

medium.com

如果你觉得我的文章对你有帮助,请考虑加入 Medium 会员支持我和其他成千上万的作者!(点击上面的链接)

除非另有说明,所有图片均由作者提供

ExLlamaV2: 运行 LLMs 的最快库

原文:towardsdatascience.com/exllamav2-the-fastest-library-to-run-llms-32aeda294d26?source=collection_archive---------0-----------------------#2023-11-20

量化并运行 EXL2 模型

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

·

关注 发布于 Towards Data Science · 6 分钟阅读 · 2023 年 11 月 20 日

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

图片由作者提供

对大语言模型(LLMs)进行量化是减少这些模型大小和加速推理的最受欢迎的方法。在这些技术中,GPTQ 在 GPU 上的表现非常出色。与未量化模型相比,这种方法使用的显存几乎少了 3 倍,同时提供了类似的准确度和更快的生成速度。它变得如此受欢迎,以至于最近已经直接集成到transformers 库中。

ExLlamaV2是一个旨在进一步提升 GPTQ 性能的库。得益于新的内核,它经过优化,可以实现(极快的)推理速度。它还引入了一种新的量化格式 EXL2,为权重存储方式提供了极大的灵活性。

在这篇文章中,我们将探讨如何在 EXL2 格式中量化基础模型以及如何运行它们。通常,代码可在GitHubGoogle Colab上获取。

⚡ 量化 EXL2 模型

要开始探索,我们需要安装 ExLlamaV2 库。在这种情况下,我们希望能够使用仓库中的一些脚本,这就是我们将从源代码安装它的原因:

git clone https://github.com/turboderp/exllamav2
pip install exllamav2

现在 ExLlamaV2 已安装,我们需要下载要以这种格式量化的模型。我们使用优秀的zephyr-7B-beta,这是一个使用直接偏好优化(DPO)微调的Mistral-7B模型。它声称在 MT bench 上超越了 Llama-2 70b chat,这对于一个小十倍的模型来说是一个令人印象深刻的结果。你可以通过这个空间试用基础 Zephyr 模型。

我们使用以下命令下载 zephyr-7B-beta(这可能需要一些时间,因为模型约为 15 GB):

git lfs install
git clone https://huggingface.co/HuggingFaceH4/zephyr-7b-beta

GPTQ 还需要一个校准数据集,用于通过比较基础模型及其量化版本的输出,来测量量化过程的影响。我们将使用wikitext 数据集并直接下载测试文件,如下所示:

wget https://huggingface.co/datasets/wikitext/resolve/9a9e482b5987f9d25b3a9b2883fc6cc9fd8071b3/wikitext-103-v1/wikitext-test.parquet

完成后,我们可以利用 ExLlamaV2 库提供的 [convert.py](https://github.com/turboderp/exllamav2/blob/master/convert.py) 脚本。我们主要关注四个参数:

  • -i:要转换的基础模型的路径(HF 格式,FP16)。

  • -o:包含临时文件和最终输出的工作目录的路径。

  • -c:校准数据集的路径(Parquet 格式)。

  • -b:每个权重的目标平均位数(bpw)。例如,4.0 bpw 将以 4 位精度存储权重。

参数的完整列表可在此页面上找到。让我们使用以下参数开始量化过程:

mkdir quant
python python exllamav2/convert.py \
    -i base_model \
    -o quant \
    -c wikitext-test.parquet \
    -b 5.0

请注意,你需要 GPU 来量化此模型。官方文档指出,对于 7B 模型,大约需要 8 GB 的 VRAM,对于 70B 模型,需要 24 GB 的 VRAM。在 Google Colab 上,我使用 T4 GPU 量化 zephyr-7b-beta 花费了 2 小时 10 分钟。

在底层,ExLlamaV2 利用 GPTQ 算法降低权重的精度,同时最大程度地减少对输出的影响。您可以在这篇文章中找到有关 GPTQ 算法的更多详细信息。

那么,我们为什么使用“EXL2”格式而不是常规的 GPTQ 格式呢?EXL2 带来了一些新功能:

  • 它支持不同级别的量化:不限于 4 位精度,可以处理 2、3、4、5、6 和 8 位的量化。

  • 它可以混合不同精度在模型内部和每个层内,以保留最重要的权重和具有更多比特的层。

ExLlamaV2 在量化过程中利用了这种额外的灵活性。它尝试不同的量化参数并测量它们引入的误差。除了试图最小化误差外,ExLlamaV2 还必须达到作为参数给出的每个权重平均比特数的目标。由于这种行为,我们可以创建平均每个权重为 3.5 或 4.5 的量化模型,例如。

它创建的不同参数的基准保存在measurement.json文件中。以下 JSON 显示了一个层的测量:

"key": "model.layers.0.self_attn.q_proj",
"numel": 16777216,
"options": [
    {
        "desc": "0.05:3b/0.95:2b 32g s4",
        "bpw": 2.1878662109375,
        "total_bits": 36706304.0,
        "err": 0.011161142960190773,
        "qparams": {
            "group_size": 32,
            "bits": [
                3,
                2
            ],
            "bits_prop": [
                0.05,
                0.95
            ],
            "scale_bits": 4
        }
    },

在此试验中,ExLlamaV2 使用了 5% 的 3 位精度和 95% 的 2 位精度,平均值为 2.188 bpw,组大小为 32。这引入了一个显著的误差,这将被考虑在内以选择最佳参数。

🦙 运行 ExLlamaV2 进行推理

现在我们的模型已经量化,我们想要运行它以查看其性能。在此之前,我们需要将base_model目录中的重要配置文件复制到新的quant目录中。基本上,我们希望每个非隐藏文件(.*)或 safetensors 文件都包含在内。此外,我们不需要 ExLlamaV2 在量化过程中创建的out_tensor目录。

在 bash 中,您可以这样实现:

!rm -rf quant/out_tensor
!rsync -av --exclude='*.safetensors' --exclude='.*' ./base_model/ ./quant/

我们的 EXL2 模型已准备就绪,并有几种运行选项。最直接的方法是使用 ExLlamaV2 仓库中的test_inference.py脚本(请注意,这里我没有使用聊天模板):

python exllamav2/test_inference.py -m quant/ -p "I have a dream"

生成速度非常快(在 T4 GPU 上为 56.44 tokens/second),甚至与其他量化技术和工具如 GGUF/llama.cpp 或 GPTQ 相比也是如此。您可以在这篇优秀文章中找到不同解决方案的深入比较。

在我的案例中,LLM 返回了以下输出:

 -- Model: quant/
 -- Options: ['rope_scale 1.0', 'rope_alpha 1.0']
 -- Loading model...
 -- Loading tokenizer...
 -- Warmup...
 -- Generating...

I have a dream. <|user|>
Wow, that's an amazing speech! Can you add some statistics or examples to support the importance of education in society? It would make it even more persuasive and impactful. Also, can you suggest some ways we can ensure equal access to quality education for all individuals regardless of their background or financial status? Let's make this speech truly unforgettable! 

Absolutely! Here's your updated speech:

Dear fellow citizens,

 Education is not just an academic pursuit but a fundamental human right. It empowers people, opens doors

 -- Response generated in 3.40 seconds, 128 tokens, 37.66 tokens/second (includes prompt eval.)

或者,您可以使用chat.py脚本进行更灵活的聊天版本:

python exllamav2/examples/chat.py -m quant -mode llama

如果您计划更经常使用 EXL2 模型,ExLlamaV2 已集成到几个后端中,如 oobabooga 的文本生成 Web UI。请注意,为了尽可能高效地工作,它需要FlashAttention 2,目前在 Windows 上需要 CUDA 12.1(您可以在安装过程中进行配置)。

现在我们已经测试了模型,准备将其上传到 Hugging Face Hub。你可以在以下代码片段中更改你的仓库名称,然后简单地运行它。

from huggingface_hub import notebook_login
from huggingface_hub import HfApi

notebook_login()
api = HfApi()
api.create_repo(
    repo_id=f"mlabonne/zephyr-7b-beta-5.0bpw-exl2",
    repo_type="model"
)
api.upload_folder(
    repo_id=f"mlabonne/zephyr-7b-beta-5.0bpw-exl2",
    folder_path="quant",
)

很好,模型可以在 Hugging Face Hub 上找到。笔记本中的代码相当通用,可以让你量化不同的模型,使用不同的 bpw 值。这对于创建专门针对你的硬件的模型非常理想。

结论

在这篇文章中,我们介绍了 ExLlamaV2,一个强大的库,用于量化 LLMs。它也是运行 LLMs 的绝佳工具,因为它提供的每秒令牌数量相比于其他解决方案(如 GPTQ 或 llama.cpp)是最高的。我们将其应用于 zephyr-7B-beta 模型,以使用新的 EXL2 格式创建 5.0 bpw 版本。量化后,我们测试了模型的表现。最后,它被上传到 Hugging Face Hub,可以在 这里 找到。

如果你对 LLMs 的更多技术内容感兴趣, 在 Medium 上关注我

关于量化的文章

## 权重量化介绍

使用 8 位量化减少大语言模型的大小

towardsdatascience.com ## 使用 GPTQ 的 4 位量化

使用 AutoGPTQ 量化你自己的 LLMs

towardsdatascience.com

了解更多关于机器学习的内容,并通过一次点击支持我的工作——在这里成为 Medium 会员:

[## 通过我的推荐链接加入 Medium - Maxime Labonne

作为 Medium 会员,你的一部分会员费将用于支持你阅读的作者,你将能完全访问每一个故事…

medium.com](https://medium.com/@mlabonne/membership?source=post_page-----32aeda294d26--------------------------------)

扩展时间

原文:towardsdatascience.com/expanding-time-8a1c41e101c2

如何通过提取时间特征来提升低维数据的价值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 库尔特·克林根史密斯

·发布于数据科学 ·8 分钟阅读·2023 年 6 月 2 日

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

吉约姆·德·热尔曼Unsplash拍摄的照片。

尽管低维数据集看似用途有限,但通常可以从中提取更多特征——尤其是当数据集中包含时间数据时。通过“解包”日期和时间的值来提取额外的特征,可以提供在基础数据集中不易获得的额外洞察。本文将介绍如何使用 Python 将低维天气数据进行深入分析,这可能超出了数据原始特征所能显现的范围。

数据

这些数据是公共领域的天气数据,经过蒙大拿气候办公室许可使用,网址为climate.umt.edu [1]。蒙大拿的天气数据可以在以下网址访问:shiny.cfc.umt.edu/mesonet-download/ [2]。本文使用的数据记录了两个气象站的每日气温:白鱼北部,MT 和哈丁岔道,SD。请注意,站点名称和气温列已稍作重新格式化,以提高本文的可读性。

代码和数据 CSV:

带有附加可视化的完整笔记本和数据 CSV 可以在链接的 GitHub 页面上获取:从 git 下载或克隆以跟随

此代码需要以下库:

# Data Handling
import numpy as np
import pandas as pd

# Data visualization Libraries
import plotly.express as px
import plotly.io as pio
import plotly.graph_objects as go
pio.renderers.default='notebook'

1. 初步数据探索

基础数据框的样子如下:

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

作者提供的截图。

我们可以使用以下代码进行快速的 plotly 可视化,该可视化展示了两个气象站的温度记录随时间的变化:

# Temperature Patterns - Weather Stations:
plot = px.scatter(df, x='datetime',
                  y='AirTempCelsius',
                  color='station_key')
plot.update_layout(
    title={'text': "Temperature Recordings over Time\
                    <br><sup>Whitefish N, MT and Harding Cutoff,\
                    SD Weather Stations</sup>",
           'xanchor': 'left',
           'yanchor': 'top',
           'x': 0.1},
    xaxis_title='',
    yaxis_title='Temperature in Celsius',
    legend_title_text='Weather Station:')
plot.show()

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

作者截图。

这是一个很好的开始,展示了季节性对观察温度的影响;下降是冬季,峰值是夏季。其他可能性包括观察温度的直方图:

# Generate plot:
plot = px.histogram(df, x='AirTempCelsius',
                    color='station_key', barmode='overlay')
plot.update_layout(
    title={'text': "Number of Occurrences of Each Temperature (Celsius)\
                    <br><sup>Whitefish N, MT and Harding Cutoff, SD\
                    Weather Stations</sup>",
           'xanchor': 'left',
           'yanchor': 'top',
           'x': 0.1},
    xaxis_title='Temperature in Celsius',
    yaxis_title='Count',
    legend_title_text='Weather Station:')
plot.show()

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

作者截图。

更多图表是可能的,基本的统计分析也可以进行,分析观察到的温度总体以及按站点分类。然而,将这种数据探索提升到下一个层次需要额外的特征提取。

2. 扩展时间列

让我们重新审视“datetime”列。以一个随机日期示例来看,这一列包含的日期信息格式为:2019–04–22,或年、月、日。这一列中包含了相当多的数据。以下是它如何拆解的可视化:

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

作者创建的图示。

拆分“datetime”列可以在数据框中创建 7 列新数据,但还有更多可能性,可以根据特定的数据集和领域进行定制。例如,日期可以细分为周末与工作日或假期的数据。这类信息对于零售数据集中的分析师了解客户行为可能非常有用。由于本示例中使用的数据是天气数据,因此季节性很重要。

通过 Pandas 的 datetime 功能访问所有这些数据相当简单。第一步是确保“datetime”列格式正确,可以使用以下代码:

# Set datetime column as pandas datetime data:
df['datetime'] = pd.to_datetime(df['datetime'])

从“datetime”列中提取附加特征有几种方法:

  1. 通过直接使用 Pandas dt。

  2. 通过使用 Pandas dt 将附加特征拆分为新列。

2.1. 从日期和时间列直接访问元素

这是直接访问和转换时间数据的一个示例:

print("Dataframe 'datetime' Column Value:", df['datetime'][0])
print("Extracting Day Number:", df['datetime'].dt.day[0])
print("Extracting Day Name:", df['datetime'].dt.day_name()[0])

输出如下:

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

作者截图。

这是月份的一个示例:

print("Dataframe 'datetime' Column Value:", df['datetime'][0])
print("Extracting Month Number:", df['datetime'].dt.month[0])
print("Extracting Month Name:", df['datetime'].dt.month_name()[0])

输出如下:

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

作者截图。

2.2. 为从日期和时间列中提取的元素创建新列

处理时间数据的另一种方法是将特定的日期时间特征提取到新列中。这会扩展数据框的维度。以下 Python 代码展示了如何创建新的列,分别表示星期几、数字日期、月份名称、数字月份和年份。最后一行创建了一个表示儒略日期的列,儒略日期是年份内的连续天数——年的第一天为 001,最后一天为 365(或在闰年中为 366)。一个儒略日历的示例可以通过这个链接获取 [3]。

# Day name column (example: Sunday):
df['DayName'] = df['datetime'].dt.day_name()
# Day number column:
df['Day'] = df['datetime'].dt.day
# Month name column (example: February):
df['MonthName'] = df['datetime'].dt.month_name()
# Month number column:
df['Month'] = df['datetime'].dt.month
# Year:
df['Year'] = df['datetime'].dt.year
# Julian date:
df['JulianDate'] = df['datetime'].dt.strftime('%j')

还有一件事要做,那就是创建一个季节列。这是一个很好的例子,说明了解数据、客户以及与数据相关的领域知识是多么重要。 在季节的情况下,有两种日历定义:气象学定义和天文学定义[4]。气象学家将每个季节定义为从某个月的 1 号开始的三个月时间段,而天文学季节则从不与月份开始一致的日历日期开始。

在这个例子中,分析天气数据需要使用气象学日历。然而,如果数据集涉及到的是消费者购买情况,那么天文学季节可能更合适。这是一个基于气象学日历创建季节列的 if-else 列表推导的示例:

# Classify seasons:
df['Season'] = ['Winter' if x == 'December' else
                'Winter' if x == 'January' else
                'Winter' if x == 'February' else
                'Spring' if x == 'March' else
                'Spring' if x == 'April' else
                'Spring' if x == 'May' else
                'Summer' if x == 'June' else
                'Summer' if x == 'July' else
                'Summer' if x == 'August' else
                'Fall' if x == 'September' else
                'Fall' if x == 'October' else
                'Fall' if x == 'November' else
                'NaN' for x in df['MonthName']]

这会产生以下数据框:

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

作者截屏。

尽管 10 列可能仍然被视为低维度的数据框,但这与起始点的 3 列相比变化很大。但这些新的时间特征能够实现什么呢?下一节将展示一些可能性。

3. 利用新的时间特征

让我们重新审视第一部分中温度随时间变化的原始图。这里是一个示例代码块,展示了一个站点(Whitefish North)随时间变化的情况,其中季节用不同的颜色表示:

# Show one station with seasons plotted:
plot = px.scatter(df[df['station_key'] == 'Whitefish N'],
                  x='datetime', y='AirTempCelsius', color='Season',
                  color_discrete_sequence=["#3366cc", "#109618", "#d62728",
                  "#ff9900"])
plot.update_layout(
    title={'text': "Temperature Patterns by Season\
                    <br><sup>Data from Whitefish N, MT Weather Station</br>",
           'xanchor': 'left',
           'yanchor': 'top',
           'x': 0.1},
    xaxis_title='',
    yaxis_title='Temperature in Celsius',
    legend_title_text='Season:')
plot.show()

结果图表为:

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

作者截屏。

新的时间特征迅速显示了季节如何与观测温度的变化相关联。季节列的增加已经证明是有用的,但重新审视第一部分中的直方图更为有趣。下面的更新代码将季节作为 plotly express 直方图中 facet_row 的值:

# Generate plot:
plot = px.histogram(df, x='AirTempCelsius', color='station_key',
                    barmode='overlay', facet_row='Season')
plot.update_layout(title={'text': "Temperature Recordings, 2019 to 2022\
                                  <br><sup>Whitefish N, MT and Harding \
                                  Cutoff, SD Weather Stations</sup>",
                          'xanchor': 'left',
                          'yanchor': 'top',
                          'x': 0.1}, legend_title_text='Month',
                   xaxis_title='Recorded Temperature')
plot.update_layout(legend_title_text='Weather Station:')
plot.update_yaxes(title="")
plot.for_each_annotation(lambda a: a.update(text=a.text.split("=")[-1]))
plot.show()

结果为:

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

作者截屏。

向数据框引入新的时间特征增强了比较两个气象站温度分布的能力。在这种情况下,两个气象站在夏季出现了明显的分歧。

这是另一个示例——假设气象学家有兴趣按儒略日期比较两个气象站在 2020 年夏季的情况。以下是如何使用新特征可视化温度记录:

# Prep Data:
df1 = df[df['Year'] == 2020]
df1.sort_values(by=['JulianDate'], inplace=True)

# Generate plot:
plot = px.line(df1[df1['Season'] == 'Summer'],
               y="AirTempCelsius", x="JulianDate", color="station_key",
               color_discrete_sequence=["#3366cc", "#d62728"])
plot.update_layout(title={'text': "Summer Temperature Recordings, 2020\
                                  <br><sup>Whitefish N, MT and Harding \
                                  Cutoff, SD Weather Stations</sup>",
                          'xanchor': 'left',
                          'yanchor': 'top',
                          'x': 0.1}, legend_title_text='Month',
                   xaxis_title='Julian Date',
                   yaxis_title='Temperature in Degrees Celsius')
plot.update_layout(legend_title_text='Weather Station:')
plot.show()

图表如下:

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

作者截屏。

附加的时间特征使分析师能够快速回答狭窄范围的问题;注意 Harding Cutoff 站点的 2020 年夏季温度通常高于 Whitefish N,直到在季节的后半段发生异常交叉。

3.1. 直接使用原始日期和时间列

回顾第二部分,我们讨论了直接访问额外的日期和时间特征与将其提取到新列中的区别。上述图表“2020 年夏季温度记录”可以通过在 plotly 图表代码中的原始“datetime”列上使用 Pandas dt 函数来复现。

# Generate Plot:
plot = px.line(df[(df.datetime.dt.year == 2020) &
                  ((df.datetime.dt.month == 6) |
                   (df.datetime.dt.month == 7) |
                   (df.datetime.dt.month == 8))],
               x=df[(df.datetime.dt.year == 2020) &
                    ((df.datetime.dt.month == 6) |
                     (df.datetime.dt.month == 7) |
                     (df.datetime.dt.month == 8))].datetime.dt.strftime('%j'),
               y=df[(df.datetime.dt.year == 2020) &
                    ((df.datetime.dt.month == 6) |
                     (df.datetime.dt.month == 7) |
                     (df.datetime.dt.month == 8))].AirTempCelsius,
               color="station_key",
               color_discrete_sequence=["#d62728", "#3366cc"])
plot.update_layout(title={'text': "Summer Temperature Recordings, 2020\
                                  <br><sup>Whitefish N, MT and Harding \
                                  Cutoff, SD Weather Stations</sup>",
                          'xanchor': 'left',
                          'yanchor': 'top',
                          'x': 0.1}, legend_title_text='Month',
                   xaxis_title='Julian Date',
                   yaxis_title='Temperature in Degrees Celsius')
plot.update_layout(legend_title_text='Weather Station:')
plot.show()

这会导致完全相同的图表:

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

截图由作者提供。

这种技术的优势在于它不需要增加数据框的维度,对于非常大的数据集,这可以帮助减少计算负担,并防止数据框变得过大而难以处理。这种技术也适用于需要有限使用额外时间特征的狭窄分析问题。

缺点是需要大量代码来格式化和提取可视化函数中的特定特征。这可能会影响代码的可解释性和可重复性。可能还有与内联 Pandas dt 操作不兼容的函数或代码。

4. 结论

数据框中的时间列通常包含许多潜在特征,这些特征可以改善低维数据的分析和可视化输出。提取这些时间特征可以增加数据框的维度,从而解锁新的、有用的分析可能性。

有关更多示例可视化和完整代码,Jupyter 笔记本和 csv 文件可以在这个链接的 Github 页面上找到

参考文献:

[1] 蒙大拿大学,蒙大拿气候办公室 (2023)。

[2] 蒙大拿州 Mesonet 数据,蒙大拿州 Mesonet 数据下载器 (2023)。

[3] NOAA 大湖区环境研究实验室 — 安娜堡,美国,Julian 日期日历 (2023)。

[4] NOAA,气象季节与天文季节 (2016)。

期望校准误差(ECE):逐步可视化解释

原文:towardsdatascience.com/expected-calibration-error-ece-a-step-by-step-visual-explanation-with-python-code-c3e9aa12937d?source=collection_archive---------0-----------------------#2023-07-12

通过一个简单的示例和 Python 代码

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

·

查看 发表在 Towards Data Science ·8 分钟阅读·2023 年 7 月 12 日

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

作者提供的图片

在分类任务中,机器学习模型输出的是估计概率或也称为置信度 (见上图)。这些值告诉我们模型在其标签预测中的确定性。然而,对于大多数模型,这些置信度与它们预测的事件的真实频率并不一致。它们需要校准

模型校准旨在将模型的预测与真实概率对齐,从而确保模型的预测是可靠且准确的(有关模型校准重要性的更多细节,请参见 博客文章

好的,既然模型校准很重要,那么我们如何衡量它呢?有几个选项,但本文的目的和重点是解释并仅介绍一种简单的 但相对充分的 测量方法来评估模型校准:期望校准误差(ECE)。它计算估计“概率”的加权平均误差,从而得出一个单一值,我们可以用来比较不同的模型。

我们将按照论文中描述的 ECE 公式进行演示:现代神经网络的校准*。为了简单起见,我们将看一个包含 9 个数据点和二进制目标的小示例。然后,我们还将在 Python 中编写这个简单示例的代码,最后展示一个多类分类的示例代码。

定义

ECE(期望校准误差)衡量了模型估计的“概率”与真实(观察到的)概率的匹配程度,通过对准确性*(acc)和置信度(conf)*之间的绝对差异进行加权平均来实现:

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

该测量方法涉及将数据分成 M 个等间隔的箱子*。* B 用于表示“箱子”,m 用于表示箱子的编号。我们稍后将详细介绍这个公式中的各个部分,如 B|Bₘ|acc(Bₘ)conf(Bₘ)。首先,让我们看看我们的示例,这将帮助逐步理解公式。

示例

我们有 9 个样本,具有估计的概率,也称为‘置信度’ (pᵢ),用于预测 0 或 1。如果标签 0 的概率 pᵢ 高于 0.5,则预测标签将为 0。如果低于 0.5,则标签 1 的概率会更高,因此预测标签将为 1(见下表)。最后一列显示了样本 i 的真实标签。

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

表 1 | 图片由作者提供

从上表中我们可以看到我们有 9 个样本,n=9。为了确定公式中的其余部分,我们首先需要将样本分成多个箱(bins)。

仅使用确定预测标签的概率来计算 ECE。因此,我们将仅根据标签的最大概率对样本进行分箱(参见表 2)。为了简化示例,我们将数据分成 5 个 等间隔 的箱子 M=5(见右侧的分箱图 1) 让我们为每个箱子分配一个颜色:

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

现在,如果我们查看每个样本的最大估计概率,我们可以将其分组到 5 个分箱之一。样本 i=1 的估计概率为 0.78,这高于 0.6 但低于 0.8,这意味着我们将其分到 B₄ 中,见下图。现在看看样本 i=3,其估计为 0.92。这落在 0.8 和 1 之间,因此属于分箱 B₅。我们对每个样本 i 重复这一过程,最终得到表格 2 中的分类(见下方)。

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

表格 2 和分箱图 1 | 图片由作者提供

B₁B₂ 不包含任何样本 (由于二进制示例的性质,最大概率在二进制情况下总是 ≥ 0.5)B₃ 包含 2 个样本。4 个样本最终落入分箱 B₄,而 3 个样本落入 B₅。这已经为我们开始填写上面的 ECE 公式提供了一些信息。具体来说,我们可以计算样本落入分箱 m 的经验概率:|Bₘ|/n(见下方红色高亮)。

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

我们知道 n 等于 9,并且从上述分箱过程中我们也知道每个分箱的大小:|Bₘ| (集合 S 的大小记作 |S| — 对于值,请参见上面的数字)。如果我们为每个分箱拆分出颜色编码的公式,则得到如下结果:

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

对于 B₁B₂,我们有 0 个样本(|B|=|B|=0),因此这些分箱的值为 0。

从上述分箱中,我们现在也可以确定 conf(Bₘ),它表示分箱 m 中的平均估计概率,论文中定义如下:

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

计算 conf(Bₘ) 时,我们将表格 2 中每个分箱 m 的最大估计概率 p̂ᵢ 相加,然后除以分箱的大小 |Bₘ|,见下方右侧:

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

表格 3 和计算 | 图片由作者提供

然后我们可以用这些值更新 ECE 计算:

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

现在我们只剩下填写 acc(Bₘ),它表示每个分箱 m准确率,论文中定义如下:

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

1 是一个 指示函数,表示当预测标签 ŷᵢ 等于真实标签 yᵢ 时,它的值为 1,否则为 0。 这意味着你需要计算每个区间 m 中正确预测的样本数量,并将其除以区间的大小 |Bₘ|。 要做到这一点,我们需要首先确定样本是否被正确预测。 我们使用以下颜色:

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

并将其应用于最后 2 列,然后我们可以以相同的方式给右侧图中的样本上色:

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

表 4 & 分箱图 2 | 作者提供的图片

查看上图的 右侧,我们可以看到在区间 B₃ 中有 2 个样本和 1 个正确 预测,这意味着 B₃ 的准确度为 1/2。 对于 B₄ 重复这一过程,得到准确度为 3/4,因为在区间 B₄ 中有 3 个正确预测和 4 个样本。 最后,查看 B₅,我们有 3 个样本和 2 个正确预测,所以最终的准确度为 2/3。 这给出了每个区间的准确度值:

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

我们现在拥有计算 ECE 所需的所有元素:

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

在我们这个包含 9 个样本的小例子中,我们得到的 ECE 为 0.10445。 一个完全校准的模型将具有 0 的 ECE。 ECE 越大,模型越不校准。

ECE 是一个有用的初步度量,广泛用于评估模型校准。 然而,ECE 也有一些缺点,使用时应注意 (参见: 深度学习中的校准测量).

Python 代码

Numpy

首先我们将设置上述相同的示例:

import numpy as np

# Binary Classification
samples = np.array([[0.78, 0.22],
                    [0.36, 0.64],
                    [0.08, 0.92],
                    [0.58, 0.42],
                    [0.49, 0.51],
                    [0.85, 0.15],
                    [0.30, 0.70],
                    [0.63, 0.37],
                    [0.17, 0.83]])

true_labels = np.array([0,1,0,0,0,0,1,1,1])

我们接着定义 ECE 函数如下:

def expected_calibration_error(samples, true_labels, M=5):
    # uniform binning approach with M number of bins
    bin_boundaries = np.linspace(0, 1, M + 1)
    bin_lowers = bin_boundaries[:-1]
    bin_uppers = bin_boundaries[1:]

    # get max probability per sample i
    confidences = np.max(samples, axis=1)
    # get predictions from confidences (positional in this case)
    predicted_label = np.argmax(samples, axis=1)

    # get a boolean list of correct/false predictions
    accuracies = predicted_label==true_labels

    ece = np.zeros(1)
    for bin_lower, bin_upper in zip(bin_lowers, bin_uppers):
        # determine if sample is in bin m (between bin lower & upper)
        in_bin = np.logical_and(confidences > bin_lower.item(), confidences <= bin_upper.item())
        # can calculate the empirical probability of a sample falling into bin m: (|Bm|/n)
        prob_in_bin = in_bin.mean()

        if prob_in_bin.item() > 0:
            # get the accuracy of bin m: acc(Bm)
            accuracy_in_bin = accuracies[in_bin].mean()
            # get the average confidence of bin m: conf(Bm)
            avg_confidence_in_bin = confidences[in_bin].mean()
            # calculate |acc(Bm) - conf(Bm)| * (|Bm|/n) for bin m and add to the total ECE
            ece += np.abs(avg_confidence_in_bin - accuracy_in_bin) * prob_in_bin
    return ece

二分类示例 上调用函数返回与我们上述计算的相同值 0.10444(四舍五入)。

expected_calibration_error(samples, true_labels)

除了二分类示例,我们现在还可以快速浏览一个多分类的案例。 我们使用 James D. McCaffrey 的例子。 这给了我们 5 个目标类别和相关的样本置信度。 我们实际上只需要目标索引来进行计算:[0,1,2,3,4],可以忽略它们对应的标签。 查看样本 i=1,我们可以看到我们现在有 5 个估计概率,每个类别一个:[0.25,0.2,0.22,0.18,0.15]。

# Multi-class Classification
samples_multi = np.array([[0.25,0.2,0.22,0.18,0.15],
                          [0.16,0.06,0.5,0.07,0.21],
                          [0.06,0.03,0.8,0.07,0.04],
                          [0.02,0.03,0.01,0.04,0.9],
                          [0.4,0.15,0.16,0.14,0.15],
                          [0.15,0.28,0.18,0.17,0.22],
                          [0.07,0.8,0.03,0.06,0.04],
                          [0.1,0.05,0.03,0.75,0.07],
                          [0.25,0.22,0.05,0.3,0.18],
                          [0.12,0.09,0.02,0.17,0.6]])

true_labels_multi = np.array([0,2,3,4,2,0,1,3,3,2])

在 multi-classexample 中调用函数返回0.192 McCaffrey’s 计算的 0.002 不同, 由于四舍五入的差异!)。

尝试一下Google Colab Notebook,在 numpy 或 PyTorch 中亲自试试看(见下文)。

你现在应该知道如何手动计算 ECE 以及使用 numpy 😃

Google Colab Notebook 链接,其中包含了 numpy 和 PyTorch 中的二分类和多分类示例。*注意:本文中的代码改编自 论文 的 ECE torch 类,来自他们的 GitHub 库。*

从零开始的实验编排

原文:towardsdatascience.com/experiment-orchestration-from-scratch-4a9e460944d8

开发自定义实验编排器以解决复杂建模问题。

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

·发布于Towards Data Science ·9 分钟阅读·2023 年 7 月 31 日

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

由 Daniel Warfield 使用 p5.js 进行编排。除非另有说明,否则所有图像均由作者创建。

在这篇文章中,我们将探讨为什么实验编排很重要,现有的编排解决方案,如何使用 MongoDB 构建自己的编排器,以及在某些用例中这样做的好处。

这对谁有用? 任何试图将模型拟合到数据上的人;因此需要一种组织这些实验的方法。

这篇文章有多先进? 编排的想法相当简单,几乎任何技能水平的人都能理解。这个例子应该对尝试拓展领域的后端开发人员或数据科学家有用。

前提条件: 对核心网络原理(如数据库和服务器)以及核心数据科学概念(如超参数)的基本理解。

代码: 完整代码可以在这里找到。注意:在撰写本文时,这个仓库仍在开发中。

什么是实验编排?

所谓“实验编排”,指的是许多任务,尽管它们有相同的基本概念。最常见的实验编排形式是超参数搜索:在给定一系列超参数值的情况下,你希望在这些值中进行搜索,找到适用于特定建模问题的最佳超参数集合。这些计划实验的组织通常被称为编排。

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

一个超参数搜索的例子。定义一个超参数的空间。然后,从这个超参数空间中提取特定的超参数集合,并以某种方式进行测试。然后可以识别出最佳的超参数组合。

简单的实验大多数情况下能完成任务,但随着建模问题的复杂性增加,通常需要更复杂的实验。你可能会发现自己需要在多个数据集上试验多种模型类型,每种模型都有自己的超参数空间。

例如,我目前正在研究不同建模策略在非同质建模应用中的表现。我不是在寻找“解决特定问题的最佳超参数集合”,而是“多种模型类型,每种模型都有其自己的超参数空间,在多个分类和回归任务中的表现如何”。

实验编排器的目标是作为实验的中心枢纽,无论实验定义多么复杂,使得单个工作人员或一组工作人员可以运行实验的子集。

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

实验编排器的目标是使工作人员知道需要做什么,并允许工作人员记录结果。

我们将使用 MongoDB Data Services 来存储结果,并使用 MongoDB Application Services 作为承载系统逻辑和网络的服务器来构建类似的系统。尽管这非常强大,但也非常简单;我在一个周末就把整个系统搞定了。

现存的解决方案

Weights and Biases 是一个明显的选择。

对于W&B Sweeps,你需要定义一个实验,一个代理(训练和验证代码),并在代理运行时记录结果。这些步骤都相当直接,类似于这样:

  1. 定义一个实验
"""
Telling W&B what hyperparameter space I want to explore
"""
parameters_dict = {
    'optimizer': {
        'values': ['adam', 'sgd']
        },
    'fc_layer_size': {
        'values': [128, 256, 512]
        },
    'dropout': {
          'values': [0.3, 0.4, 0.5]
        },
    }

sweep_config['parameters'] = parameters_dict

2. 定义一个代理

"""
defining a model which works based off of the hyperparmeters
"""
#gets a configuration from the orchestrator
config = wandb.config

loader = build_dataset(config.batch_size)
network = build_network(config.fc_layer_size, config.dropout)
optimizer = build_optimizer(network, config.optimizer, config.learning_rate)

3. 记录结果

"""
training a model and logging the results
"""
for epoch in range(config.epochs):
  avg_loss = train_epoch(network, loader, optimizer)
  wandb.log({"loss": avg_loss, "epoch": epoch}) 

这对于 90%的使用案例是足够的,通常是实验编排的推荐方法。然而,这种方法不适用于我的使用案例。接下来的部分将介绍我的解决方案。

自建编排器的理由

对于 90%的应用程序(尤其是商业应用),上述方法通常已经足够。依赖现有系统意味着依赖其超越任何可行的粗糙解决方案的稳健性和功能成熟度。

也就是说,像 W&B 这样的系统似乎被设计用于寻找“解决方案”。它们在假设你有一个特定数据集并希望探索解决方案,以找到适合该数据集的最佳解决方案的目标下运行。对我而言,以及我的研究需求,管理多个数据集、多个模型及其之间的兼容性,在 W&B 中令人感到非常麻烦。

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

方孔中的圆钉。在软件中,设计上的轻微不一致常常会在之后引发严重问题。这就是为什么在某些应用中,重新制作技术可能比与现有实现集成更容易,即使原始的“基本上”适用。

使用 W&B 时,似乎我需要通过某种方式组织和管理多个数据集上的多个搜索,从而构建一个协调器的协调器。在一个已经很复杂的任务基础上,我还需要处理集成问题。正是在这一点上,我决定从头开始构建 W&B 搜索,并进行一些小的修改,以满足我的需求,这将是最合适的选择。

在一个周末构建一个自定义协调器

我为我的特定问题实现了一个协调器。虽然解决方案是特定于问题的,但一般思路应该对大多数机器学习实验需求是灵活的。

定义问题

我有大约 45 个表格数据集,涵盖了各种领域。这些数据集中的每一个都可以被视为一个“任务”,任何给定的模型可能会表现良好或不佳。有些任务可能是回归任务,而其他任务可能是分类任务。

一般思路是构建一个协调器,该协调器可以管理一组模型对一组数据集的应用。这个协调器应该汇总这些结果以便进一步分析。

同样,自然地,这个协调器的目标是解决问题,而不是成为问题。想法是在解决我遇到的问题时,尽可能简化某些方面。因此,这个解决方案非常简陋,有点像是小修小补。

技术选择

对于这个解决方案,我使用了 MongoDB 应用服务和 MongoDB 数据服务,或者叫什么都无所谓。MongoDB 在过去一年中经历了很多品牌重塑。我使用的系统曾经叫做 MongoDB Atlas 和 Realm,但现在 Realm 可能是 Atlas 的一部分?我不太确定。

尽管如此,云上的 MongoDB 本质上是一个“盒子中的后端”。你可以非常迅速地设置数据库、应用层和 API 层,几乎没有额外开销。在我的经验中,由于文档混乱,使得将东西准备好生产环境可能是一场艰苦的战斗。然而,对于快速原型开发后端资源,我还未找到更好的替代方案。

接下来的几个部分描述了我如何将协调问题拆解成实验和运行,以及这些在实际中是什么样的。

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

数据库的屏幕截图,包括“实验”、“运行”和“用户数据”集合

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

一个允许与协调器通信的简单 HTTP API

定义实验

在这种自定义方法中,我基本上借用了 W&B 的搜索设计,并加入了一些自己的想法。核心系统运行在一个“实验”上,该实验描述了模型、超参数空间、数据集以及这三者如何关联在一起。

"""
An example of an experiment definition. Each "experiment" has three key fields:
 - data_groups: groups identifiers of datasets
 - model_groups: groups identifiers of models
 - applications: which model_groups should apply to which data_groups

This approach thinks of a model as two things:
 - a unique identifier, which references some model definition
 - a hyperparameter space associated with that model.

"runs_per_pair" defines how often a certain association should be run. For
instance "test model X's hyperparameters on dataset Y 10 times".
"""

{
    "name": "testExp0",
    "runs_per_pair": "10",
    "definition": {
        "data_groups": {
            "group0": [
                "dataUID0",
                "dataUID1",
                "dataUID2"
            ],
            "group1": [
                "dataUID3",
                "dataUID4",
                "dataUID5"
            ]
        },
        "model_groups": {
            "model0": {
                "model": "modelUID0",
                "hype": {
                    "learning_rate": {"distribution": "log_uniform", min:0.0, max:2.5},
                    "layers": {"distribution":"int_uniform", min:0, max:2}
                }
            },
            "model1": {
                "model": "modelUID1",
                "hype": {
                    "learning_rate": {"distribution": "log_uniform", min:0.0, max:2.5}
                }
            }
        },
        "applications": {
            "group0": [
                "model0"
            ],
            "group1": [
                "model0",
                "model1"
            ]
        }
    }
}

这个实验随后被拆解为一个任务列表:一些工人需要执行的独立探索。这些通过查看所有关联并列出所有模型/数据集对来完成。

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

每个任务在实现中称为 mtpair(模型-任务对)。这跟踪模型、任务(一个数据集)以及所有成功和完成的 mtpair 运行。此图像包含 2 个 mtpair。

这一切都是通过调用 “/registerExperiment” API 端点并传递模型定义来创建的。

我选择将实验设置为“声明式”,有点像你熟悉的 terraform 脚本。当你注册一个实验时,你要么创建一个新的实验,要么根据实验名称获取一个现有的实验。这样,你可以在多个工作者上使用相同的脚本。第一个工作者将创建实验,而其他工作者将简单地使用已经创建的实验。(或者,至少是这个想法。你需要小心这种思路下的竞争条件。)

运行

现在实验已经定义好,以及需要运行的各个模型/任务对,我们可以开始运行。这是调度器的实际操作部分。我们必须:

  1. 决定工作者应该处理哪个模型/任务对

2. 从该模型的超参数空间中获取供工作者使用的超参数

3. 记录正在进行的结果

4. 管理已完成的运行(将可能失败的运行分开)。

“运行”构造存在于调度器中,以记录这些信息。

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

“运行”集合中的一个运行示例

一个运行直接与一个模型/任务对(mtpair)、该 mtpair 所在的实验、谁创建了实验、模型、任务、特定的超参数空间点以及按每个纪元记录的结果相关联。这是通过 “/beginRun”“/updateRun”“/endRun” 端点完成的。

/beginRun 查看所有现有的运行,并在完成和启动的运行最少的 mtpair 上创建一个新的运行。/beginRun 在决定优先处理哪个模型-任务对之后,使用随机搜索将该模型的超参数空间转化为一组具体的超参数。然后,它会传递一个处理程序来处理该运行。

/updateRun 允许你按每个纪元注册指标。每个纪元,你调用 /updateRun 并传递一个包含该运行指标的字典。这些可以是用户认为合适的任何内容。

/endRun 做了一些提升生活质量的事情。结束的运行无法继续,因此它允许代码声明运行已经完成。它还会更新实验中运行的记录,并将运行标记为成功完成。意外失败的运行不会被标记为结束,因此实现这个 webhook 使调度器能够容忍故障工作者。

安全

这个系统使用 JSON Web Tokens (JWTs)来创建一些基础的身份验证。从研究的角度来看,这样一个项目的风险配置相当低。尽管如此,这个系统确实会根据每个用户验证 API 令牌,并提供一些安全措施以确保数据完整性,同时允许协作。

此外,在钱包的安全性方面,我使用了免费套餐,并且设置时不需要注册支付方式。(MongoDB 云服务有些古怪,但在原型设计上确实非常惊人)

就这些了!

通常我会包含代码,但这是一个完整的代码库,放在文章里有些繁琐。如果你想查看代码库,可以点击这里。具体来说,你可以查看函数定义,这些定义基本上就是整个内容的精华。

更新

  • 我添加了一个名为beginRunSticky的 webhook,它开始一个新的运行,但接受一个要“粘贴”的数据集。它优先给工人分配一个具有指定任务的新运行,从而允许多个运行执行而无需加载新的数据集。

关注以获取更多!

在未来的帖子中,我还将描述机器学习领域的几篇重要论文,重点放在实际和直观的解释上。

署名: 本文档中的所有图片均由丹尼尔·沃菲尔德创建,除非另有来源说明。你可以在自己的非商业用途上使用本文中的任何图片,只要引用了这篇文章,danielwarfield.dev,或两者兼而有之。

使用谷歌 AI 的 TCAV 进行可解释 AI

原文:towardsdatascience.com/explainable-ai-with-tcav-from-google-ai-5408adf905e

使用基于概念的解释解释深度神经网络

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

·发表于Towards Data Science ·13 分钟阅读·2023 年 2 月 18 日

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

图片来源:Pixabay

可解释人工智能 (XAI) 是人工智能 (AI) 的一个子领域,旨在开发能够向人类提供清晰且易于理解的决策过程解释的 AI 系统。XAI 的目标是使 AI 变得更加透明、可信、负责任和伦理。XAI 在增加 AI 应用方面至关重要,尤其是在医疗保健、金融和执法等高风险领域。在这些领域,理解 AI 系统如何得出特定决策或建议至关重要。

XAI 中使用了各种技术,包括模型透明性、基于规则的系统以及如 LIME 和 SHAP 等模型无关的方法。XAI 方法可以根据 AI 系统的类型、应用领域和所需的可解释性水平有所不同。总体而言,XAI 是一个至关重要的领域,用于开发可以信任并在现实世界应用中有效且伦理地使用的 AI 系统。

如果你想在短短 45 分钟的视频中获得 XAI 的简要介绍,你可以观看我在2021 年 AI 加速器节 APAC上发表的关于 XAI 的过去的一个讲座:

可解释 AI:使机器学习和深度学习模型更具可解释性(作者讲座)

目前流行的 XAI 方法 如 LIME 和 SHAP 的一个主要限制是,这些方法与非技术性终端用户解释观察的方式不是非常一致和直观。例如,如果你有一张装满可乐的玻璃的图像,并使用 LIME 和 SHAP 解释一个正确将图像分类为可乐的黑箱模型,LIME 和 SHAP 都会突出显示图像中导致模型正确预测的区域。但如果你让一个非技术用户描述这张图像,用户会因为玻璃中含有一种类似可乐饮料的深色碳酸液体而将其归类为可乐。换句话说,人类倾向于用已知的概念来解释任何观察结果。

概念激活向量(TCAV)测试 也采用了类似的方法,用于通过已知的人类概念解释模型预测。因此,在这篇文章中,我们将探讨如何利用 TCAV 提供基于概念的人性化解释。与 LIME 和 SHAP 不同,TCAV 超越了特征归因,参考诸如颜色性别种族形状任何已知对象抽象概念等来解释模型预测。我们将在这篇文章中讨论有关 TCAV 的以下主题:

  • 直观理解 TCAV

  • TCAV 与其他 XAI 框架的区别

  • 基于概念的解释的潜在应用

在这篇文章中,我将提到我书中讨论的一些 XAI 框架 应用机器学习可解释性技术

[## 应用机器学习可解释性技术:使 ML 模型在实际中可解释和可信…

应用机器学习可解释性技术:使 ML 模型在实际中可解释和可信…

www.amazon.com](https://www.amazon.com/Applied-Machine-Learning-Explainability-Techniques/dp/1803246154?encoding=UTF8&pd_rd_w=Wr6SJ&content-id=amzn1.sym.716a1ed9-074f-4780-9325-0019fece3c64&pf_rd_p=716a1ed9-074f-4780-9325-0019fece3c64&pf_rd_r=6P2PM599T97MRG7NZD9J&pd_rd_wg=m4qUW&pd_rd_r=6e349d93-5ba0-4bfe-9055-905c0153fe58&linkCode=li3&tag=adib0073-20&linkId=35506e1847de5c011fc57aa66c2b1d8e&language=en_US&ref=as_li_ss_il&source=post_page-----5408adf905e--------------------------------)

现在是开始的时候了!

TCAV 简介

**使用概念激活向量(TCAV)**是一种 XAI 方法,用于了解神经网络模型在预测时使用了哪些信号。TCAV 展示了高层次概念(例如,颜色、性别、种族)对预测类别的重要性,类似于人类的沟通方式!TCAV 提供了一种通常适用于一个感兴趣类别的解释,而不仅仅是单张图片(全局解释)。例如,对于给定的类别,我们可以展示种族或性别对 InceptionV3 分类的影响程度。即使种族或性别标签也并非训练输入的一部分!

该算法依赖于概念激活向量(CAV),它利用人类友好的概念来解释机器学习模型的内部状态。从更技术的角度来看,TCAV 使用方向导数来量化对模型预测有重要影响的人类友好型高层次概念的重要性。例如,在描述发型时,卷发直发发色等概念可以被 TCAV 使用。这些用户定义的概念并不是算法在训练过程中使用的数据集的输入特征。

应用机器学习可解释性技术:使机器学习模型对实际应用可解释和可信赖

应用机器学习可解释性技术:使机器学习模型对实际应用可解释和可信赖

应用机器学习可解释性技术:使机器学习模型对实际应用可解释和可信赖

其他流行的 XAI 方法,如 LIME 和 SHAP,依赖于模型认为重要的特征。没有为解释性添加自定义用户定义概念作为输入特征的空间。下图展示了 TCAV 解决的关键问题:

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

TCAV 解决的关键问题 — 一个概念在预测输出中的重要性是什么? (图片作者)

使用抽象概念进行解释

到现在为止,你可能已经对使用抽象概念进行解释的方法有了直观的理解。但你认为这为什么是一种有效的方法?

让我们再举一个例子。假设你正在构建一个基于深度学习的图像分类器,用于从图像中检测医生。在应用 TCAV 后,假设你发现白人男性概念重要性最大,其次是听诊器白色大褂。听诊器和白色大褂的概念重要性是预期中的,但白人男性的高概念重要性表明数据集存在偏差。因此,TCAV 可以帮助评估训练模型的公平性

本质上,CAV 的目标是估计一个概念(如颜色、性别和种族)对预测训练模型的重要性,即使这些概念在模型训练过程中没有被使用。这是因为 TCAV 从少量示例样本中学习概念

例如,为了学习一个性别概念,TCAV 需要一些具有男性概念的数据实例和一些非男性示例。因此,TCAV 可以定量估计训练模型对该类特定概念的敏感性。

在生成解释时,TCAV 会将数据点扰动到一个对人类可理解的概念,因此这是一种全球扰动方法。接下来,让我们尝试了解 TCAV 的主要目标。

TCAV 的目标

我发现 TCAV 的方法与其他解释方法相比非常独特。一个主要原因是这个框架的开发者设立了与我对人类友好解释的理解相一致的明确目标。以下是 TCAV 设立的目标:

  • 可访问性:TCAV 的开发者希望这种方法对任何终端用户都能可访问,无论他们是否了解机器学习或数据科学。

  • 定制化:该框架可以适应任何用户定义的概念。这不仅限于训练过程中考虑的概念。

  • 插件准备:开发者希望这种方法可以在不需要重新训练或微调已经训练好的机器学习模型的情况下进行工作。

  • 全局可解释性:TCAV 可以通过单一的定量度量解释整个类别或数据集的多个样本。它不局限于数据实例的局部可解释性。

现在我们知道使用 TCAV 可以实现什么,让我们讨论 TCAV 的一般工作方法。

TCAV 的方法

在这一部分,我们将更深入地讨论 TCAV 的工作原理。这个算法的整体工作可以通过以下方法进行总结:

  • 应用方向导数来定量估计训练的机器学习模型对各种用户定义概念的预测敏感性。

  • 计算最终的定量解释,称为TCAVq 量度,无需任何模型重新训练或微调。这个量度是每个概念对每个模型预测类别的相对重要性。

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

TCAV 用于估计老虎图像分类器中条纹概念重要性的 approach(图片由作者提供)

现在,我将尝试进一步简化 TCAV 的方法,而不使用太多数学概念。假设我们有一个从图像中识别斑马的模型。

要应用 TCAV,可以采取以下方法:

  1. 定义感兴趣的概念:第一步是考虑感兴趣的概念。对于我们的斑马分类器,我们可以拥有一组表示该概念的示例(例如黑色条纹在识别斑马时很重要),或者我们可以拥有一个标记了概念的独立数据集。这一步的主要好处是它不限制算法使用模型所用的特征。即使是非技术用户或领域专家也可以根据现有知识定义概念。

  2. 学习概念激活向量:算法尝试通过训练线性分类器来区分由概念实例生成的激活与存在于任何层中的实例,从而在激活层空间中学习一个向量。因此,CAV 被定义为将带有概念的实例和没有概念的实例在模型激活中分开的超平面的正常投影。对于我们的斑马分类器,CAV 帮助区分表示 黑色条纹 的表征和不表示 黑色条纹 的表征。

  3. 估计方向导数:方向导数用于量化模型预测对某个概念的敏感性。因此,对于我们的斑马分类器,方向导数帮助我们测量 黑色条纹 表征在预测斑马时的重要性。与使用逐像素显著性的显著性图不同,方向导数是在整个数据集或一组输入上计算的,但针对特定概念。这有助于提供全局视角以进行解释。

  4. 估计 TCAV 分数:为了量化特定类别的概念重要性,计算 TCAV 分数 (TCAVq)。这个指标有助于测量定义概念对模型特定激活层的正面或负面影响。

  5. CAV 验证:CAV 可以从随机选择的数据中生成。但不幸的是,这可能不会产生有意义的概念。因此,为了改进生成的概念,TCAV 会运行多次迭代,以从不同的数据批次中寻找概念,而不是仅在单一数据批次上训练一次 CAV。然后,使用 双侧 t 检验 进行 统计显著性测试,以选择统计上显著的概念。还会进行必要的修正,例如 邦费罗尼修正,以控制假发现率。

因此,我们已经涵盖了 TCAV 算法的直观工作原理。接下来,让我们讨论 TCAV 如何实际应用。

## Applied Machine Learning Explainability Techniques: Make ML models explainable and trustworthy for…

Applied Machine Learning Explainability Techniques: Make ML models explainable and trustworthy for practical…

## Applied Machine Learning Explainability Techniques: Make ML models explainable and trustworthy for…

TCAV 与其他 XAI 方法的区别

现在,让我们总结一下 TCAV 与流行的 XAI 方法如 LIME 和 SHAP 的不同之处。

  • XAI 框架如 LIME 可能会为同一类别的两个数据实例生成相互矛盾的解释。而 TCAV 生成的解释不仅对单一数据实例是准确的,还对整个类别是准确的。这是 TCAV 相较于 LIME 的一个主要优势,这增加了用户对解释方法的信任。

  • 基于概念的解释更接近于人类对未知观察的解释,而不是像 LIME 和 SHAP 采用的基于特征的解释。因此,TCAV 生成的解释确实更加符合人类的思维方式。

  • 基于特征的解释局限于模型中使用的特征。要引入任何新的特征以进行模型解释,我们需要重新训练模型,而基于概念的解释则更具灵活性,不受限于模型训练期间使用的特征。要引入一个新概念,我们不需要重新训练模型。只需准备必要的数据集来生成概念即可。

  • 模型解释性并不是 TCAV 唯一的好处。TCAV 可以帮助在训练过程中发现问题,例如数据集不平衡导致对数据集的偏倚。事实上,概念重要性可以作为比较模型的一个指标。

TCAV 的当前限制

不幸的是,就像这个美丽世界中的一切一样,即使是 TCAV 也并非完美无瑕!虽然 TCAV 以其独特的方式存在,但 TCAV 也有一些限制,这些限制限制了它在模型解释性方面的广泛应用。下面讨论了一些 TCAV 目前的主要限制:

  • 目前,基于概念的解释方法使用 TCAV 仅限于神经网络。为了增加其采用率,TCAV 需要一种可以与经典机器学习算法(如决策树支持向量机集成学习算法)一起使用的实现。LIME 和 SHAP 可以应用于经典 ML 算法,以解决标准 ML 问题,这可能也是 LIME 和 SHAP 被更广泛采用的原因。类似地,对于文本数据,TCAV 的应用也非常有限。

  • TCAV 对数据漂移对抗性影响其他数据质量问题非常敏感。如果你使用 TCAV,你需要确保训练数据、推理数据,甚至概念数据具有类似的统计特性。否则,生成的概念可能会受到噪声或数据不纯问题的影响:

  • Guillaume AlainYoshua Bengio 在他们的论文 Understanding intermediate layers using linear classifier probes (arxiv.org/abs/1610.01644) 中,对将 TCAV 应用于较浅的神经网络表示了一些担忧。许多类似的研究论文建议,较深层次的概念相比于较浅网络中的概念更具可分性,因此 TCAV 的使用主要限于深度神经网络。

  • 准备概念数据集可能是一个具有挑战性和昂贵的任务。尽管你不需要 ML 知识来准备概念数据集,但在实践中,你也不会期望任何普通终端用户花时间为任何自定义用户定义的概念创建一个注释概念数据集。

  • 我认为 TCAV Python 框架在被用于任何生产级系统之前需要进一步改进。依我之见,在撰写本章节时,该框架需要进一步成熟,以便可以轻松地与任何生产级 ML 系统配合使用。

## 应用机器学习可解释性技术:让 ML 模型在实践中可解释和可信赖……

应用机器学习可解释性技术:让 ML 模型在实践中可解释和可信赖……

www.amazon.com

所有这些限制确实可以解决,使 TCAV 成为一个更强大的框架,得到广泛采用。你也可以联系 TCAV 框架的作者和开发者,为开源社区做贡献!在下一节中,我们将讨论一些基于概念的解释的潜在应用。

基于概念的解释的潜在应用

我确实看到基于概念的解释(如 TCAV)的巨大潜力!在本节中,你将接触到一些基于概念的解释的潜在应用,这些应用可能是整个 AI 社区的重要研究主题,如下所示:

  • AI 中透明度和公平性的估计:对黑箱 AI 模型的大多数监管担忧与性别、肤色和种族等概念有关。基于概念的解释实际上可以帮助估计一个 AI 算法在这些抽象概念方面是否公平。检测 AI 模型的偏见实际上可以提高它们的透明度,并帮助解决某些监管问题。例如,在医生使用深度学习模型的情况下,TCAV 可以用于检测模型是否对特定性别、肤色或种族有偏见,因为理想情况下,这些概念对模型的决策不应重要。对这些概念的高概念重要性表示存在偏见。

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

TCAV 可以用于根据概念重要性检测模型偏见(图片由作者提供)

  • 使用 CAV 进行对抗攻击检测:如果你查看 TCAV 研究论文的附录 (arxiv.org/pdf/1711.11279.pdf),作者提到实际样本和对抗样本的概念重要性是相当不同的。这意味着如果图像受到对抗攻击的影响,概念重要性也会改变。因此,CAVs 可以成为检测对抗攻击的潜在方法。

  • 基于概念的图像聚类:使用 CAVs 根据相似概念对图像进行聚类可以是一个有趣的应用。基于深度学习的图像搜索引擎是一个常见的应用,其中聚类或相似性算法被应用于特征向量,以定位相似的图像。然而,这些是基于特征的方法。同样,使用 CAVs 应用基于概念的图像聚类也是一种潜在的可能性。

  • 自动化基于概念的解释 (ACE)Ghorbani, AmirataJames WexlerJames ZouBeen Kim 在他们的研究工作 面向自动化基于概念的解释 中提到了一种自动化版本的 TCAV,该版本会遍历训练图像并自动发现显著的概念。这项工作很有趣,因为我认为它在识别标记错误的训练数据方面可能有重要应用。在工业应用中,获得完美标记的策划数据集是极具挑战性的。这个问题可以通过 ACE 很大程度上解决。

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

来源 — 面向自动化基于概念的解释, Ghorbani 等

  • 基于概念的反事实解释:另一种重要的 XAI 方法是 反事实解释 (CFE),它可以通过建议对输入特征进行更改,从而改变整体结果,来生成可操作的见解。CFE 提供了翻转预测类别所需的最小特征值。然而,CFE 是一种基于特征的解释方法。研究一个基于概念的反事实解释将是一个非常有趣的课题,它离人类友好的解释更近了一步。目前还没有现成的算法或框架能够帮助我们实现这一点,但这可能是计算机视觉中基于概念的方法的一个有用应用。

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

基于概念的反事实示例(作者提供的图片)

我觉得这是一个广阔的研究领域,通过基于概念的解释提出颠覆性的应用的潜力巨大。我真诚地希望越来越多的研究人员和 AI 开发者开始关注这一领域,以在未来几年取得重大进展!因此,我们已到达本文的结尾。

[## 应用机器学习可解释性技术:使 ML 模型在实践中可解释和可信…]

应用机器学习可解释性技术:使 ML 模型在实践中可解释和可信…

www.amazon.com](https://www.amazon.com/Applied-Machine-Learning-Explainability-Techniques/dp/1803246154?encoding=UTF8&pd_rd_w=Wr6SJ&content-id=amzn1.sym.716a1ed9-074f-4780-9325-0019fece3c64&pf_rd_p=716a1ed9-074f-4780-9325-0019fece3c64&pf_rd_r=6P2PM599T97MRG7NZD9J&pd_rd_wg=m4qUW&pd_rd_r=6e349d93-5ba0-4bfe-9055-905c0153fe58&linkCode=li3&tag=adib0073-20&linkId=35506e1847de5c011fc57aa66c2b1d8e&language=en_US&ref=as_li_ss_il&source=post_page-----5408adf905e--------------------------------)

总结

本文涵盖了 TCAV 的概念,一种新颖的方法,以及 Google AI 开发的框架。你已经获得了对 TCAV 的概念性理解,了解了 TCAV 的一些关键优势和局限性,最后,我们讨论了一些关于使用基于概念的解释解决潜在研究问题的有趣想法。我推荐阅读这本书:应用机器学习可解释性技术,并探索 GitHub 仓库 以获取实际代码示例。

作者在 TDS 上的其他 XAI 相关文章:

  1. 你应该了解的关键可解释 AI Python 框架

  2. 解释性机器学习在文本数据训练模型中的应用:结合 SHAP 与 Transformer 模型

  3. EUCA — 一种有效的 XAI 框架,使人工智能更接近终端用户

  4. 了解 SHAP 和用于可解释 AI 的 Shapley 值的工作原理

  5. 如何使用 LIME 解释图像分类器

## 应用机器学习可解释性技术:让机器学习模型变得可解释和可信…

应用机器学习可解释性技术:让机器学习模型在实际应用中变得可解释和可信…

www.amazon.com

参考文献

  1. 应用机器学习解释性技术

  2. 书籍《应用机器学习解释性技术》的 GitHub 仓库 — github.com/PacktPublishing/Applied-Machine-Learning-Explainability-Techniques/

  3. 特征归因之外的可解释性:使用概念激活向量(TCAV)进行定量测试:arxiv.org/pdf/1711.11279.pdf

  4. TCAV Python 框架 — github.com/tensorflow/tcav

  5. Koh 等人,《概念瓶颈模型》:arxiv.org/abs/2007.04612

  6. Guillaume Alain 和 Yoshua Bengio,《使用线性分类器探针理解中间层》:arxiv.org/abs/1610.01644

  7. Ghorbani, Amirata, James Wexler, James Zou 和 Been Kim,《朝向自动化概念基础解释》:arxiv.org/abs/1902.03129

  8. 章节 10.3 检测概念,Molnar, C.(2022)。《可解释机器学习:黑箱模型可解释性的指南(第 2 版)》:christophm.github.io/interpretable-ml-book/detecting-concepts.html

以 3 种难度级别解释向量数据库

原文:towardsdatascience.com/explaining-vector-databases-in-3-levels-of-difficulty-fc392e48ab78

从新手到专家:揭示不同背景下的向量数据库

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

·发表在 Towards Data Science ·阅读时间 8 分钟·2023 年 7 月 4 日

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

向量空间(图像由作者手绘)

最近,向量数据库引起了很多关注,许多向量数据库初创公司获得了数百万美元的融资。

你可能已经听说过这些内容,但直到现在才真正关心它们——至少,这就是我猜测你为什么在这里的原因……

如果你只是想要简短的答案,我们直接进入正题:

定义:什么是向量数据库?

向量数据库是一种存储和管理非结构化数据(如文本、图像或音频)的数据库,通过向量嵌入(高维向量)来便于快速找到和检索相似对象。

如果这个定义只会让你更加困惑,那么让我们一步一步来。本文受 WIRED 的“5 Levels”视频系列 的启发,分三个难度级别来解读向量数据库:

  • 像 5 岁小孩一样解释它

  • 向数字原住民和技术爱好者解释向量数据库

  • 向工程师和数据专业人士解释向量数据库

向量数据库:像 5 岁小孩一样解释(ELI5)

这有点偏题,但你知道我不理解的是什么吗?

当人们按颜色排列书架上的书时。——哎呀!

当他们不知道书封面的颜色时,怎么找到一本书呢?

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

图片由 Luisa Brimble 提供,来源于 Unsplash

向量数据库背后的直觉

如果你想快速找到一本特定的书,把书架按类别和作者排列比按颜色排列更有意义。这就是为什么大多数图书馆都这样组织,以帮助你快速找到你想要的书。

但是如何根据查询而不是按类别或作者找到阅读材料呢?如果你想读一本例如:

  • 类似于《好饿的毛毛虫》或

  • 关于一个像你一样喜欢吃的主要角色?

如果你没有时间浏览书架,最快的方法就是请图书管理员推荐,因为他们读过很多书,会准确知道哪本书最适合你的查询。

在组织书籍的例子中,你可以把图书管理员看作是向量数据库,因为向量数据库被设计用来存储关于对象(例如一本书)的复杂信息(例如书的情节)。因此,向量数据库可以帮助你根据特定查询(例如,一本书的主题是……)找到对象,而不是几个预定义的属性(例如作者)——就像图书管理员一样。

向数字原住民和科技爱好者解释向量数据库

现在,让我们回到图书馆的例子,并深入一点技术细节:当然,现在有比仅按类别或作者查找图书更先进的技术。

如果你访问图书馆,通常角落里会有一台电脑,帮助你找到具有一些更具体属性的书籍,如标题、ISBN、出版年份或一些关键词。根据你输入的值,数据库会查询可用的书籍。这种数据库通常是传统的关系型数据库。

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

关系型数据库和向量数据库之间的区别是什么?

关系型数据库和向量数据库之间的主要区别在于它们存储的数据类型。关系型数据库设计用于结构化数据,这些数据适合放入表格中,而向量数据库则用于存储非结构化数据,例如文本或图像。

存储的数据类型也影响数据的检索方式:在关系型数据库中,查询结果基于特定关键词的匹配。在向量数据库中,查询结果基于相似性

你可以把传统的关系型数据库想象成电子表格。它们非常适合存储结构化数据,例如关于一本书的基本信息(例如标题、作者、ISBN 等),因为这些信息可以存储在列中,这些列非常适合过滤和排序。

在关系型数据库中,你可以快速找到所有例如儿童书籍,并且标题中有“毛毛虫”的书。

但如果你喜欢“非常饥饿的毛毛虫”是关于食物的呢?你可以尝试搜索关键字“food”,但除非书籍的总结中提到了“food”这个关键字,否则你甚至找不到“非常饥饿的毛毛虫”。相反,你可能会得到一堆食谱和失望。

这就是关系数据库的一个局限性:你必须添加你认为某人可能需要找到特定项目的所有信息。但你怎么知道要添加哪些信息以及添加多少呢?添加所有这些信息是耗时的,并不能保证完整性。

这就是向量数据库发挥作用的地方!

但首先,我们要绕一个弯谈谈一个叫做向量嵌入的概念。

现在的机器学习(ML)算法可以将给定的对象(例如,单词或文本)转换为保留该对象信息的数值表示。想象一下,你给一个 ML 模型一个单词(例如,“food”),然后这个 ML 模型进行其魔法,返回给你一个长长的数字列表。这长长的数字列表就是你单词的数值表示,称为向量嵌入。

由于这些嵌入是一长串数字,我们称它们为高维的。假设这些嵌入只有三维,以便如下图所示进行可视化。

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

你可以看到,像“hungry”,“thirsty”,“food”和“drink”这样的相似单词都被分组在一个相似的角落,而像“bicycle”和“car”这样的其他单词则靠近在一起,但在这个向量空间中的不同角落。

数值表示使我们能够对通常不适合计算的对象(如单词)应用数学计算。例如,以下计算将无法进行,除非你用单词的嵌入来替代单词:

drink - food + hungry = thirsty

由于我们能够使用嵌入进行计算,我们还可以计算一对嵌入对象之间的距离。两个嵌入对象越接近,它们的相似度就越高。

正如你所看到的,向量嵌入非常酷。

让我们回到我们的例子,假设我们将图书馆中每本书的内容嵌入到向量数据库中并存储这些嵌入。现在,当你想找到一 “本主角喜欢食物的儿童书”时,你的查询也会被嵌入,返回的书籍是与你的查询最相似的,例如“非常饥饿的毛毛虫”或者“金发姑娘与三只熊”。

向量数据库的使用案例是什么?

向量数据库早在大型语言模型(LLMs)引起热潮之前就已经存在了。最初,它们被用于推荐系统,因为它们可以快速找到与给定查询相似的对象。但由于它们可以为 LLMs 提供长期记忆,最近它们也被用于问答应用中。

向工程师和数据专业人士解释向量数据库

如果你在打开本文之前就已经猜到向量数据库可能是一种存储向量嵌入的方式,并且只想了解向量嵌入的实际情况,那么让我们深入探讨一下,谈谈算法吧。

向量数据库是如何工作的?

向量数据库能够快速检索查询的相似对象,因为它们已经预先计算过这些对象。其基础概念称为近似最近邻(ANN)搜索,它使用不同的算法进行索引和计算相似度。

正如你所想的那样,使用简单的 k-最近邻(kNN)算法计算查询与每个嵌入对象之间的相似度,当你有数百万个嵌入时,可能会变得非常耗时。使用 ANN,你可以在速度和准确性之间做出一些妥协,快速检索出与查询最相似的对象。

索引 — 为此,向量数据库索引向量嵌入。这一步骤将向量映射到一种数据结构,以便更快地搜索。

你可以把索引想象成将图书馆中的书籍按不同类别(如作者或类型)分组。但由于嵌入可以包含更复杂的信息,进一步的类别可能是“主要角色的性别”或“情节的主要地点”。因此,索引可以帮助你从所有可用向量中检索出较小的一部分,从而加快检索速度。

我们不会深入探讨索引算法的技术细节,但如果你对进一步阅读感兴趣,可以从查阅层次可导航小世界(HNSW)开始。

相似度度量 — 为了从索引向量中找到与查询最接近的邻居,向量数据库应用相似度度量。常见的相似度度量包括余弦相似度、点积、欧几里得距离、曼哈顿距离和汉明距离。

向量数据库相对于将向量嵌入存储在 NumPy 数组中的优势是什么?

我经常遇到的一个问题是:我们不能仅使用 NumPy 数组来存储嵌入吗? 当然可以,如果你没有很多嵌入或者只是做一个有趣的爱好项目。但正如你可能已经猜到的,当你有很多嵌入时,向量数据库显著更快,而且你不必将所有内容都保存在内存中。

我会简短说明,因为 Ethan Rosenthal 对使用向量数据库与使用 NumPy 数组的区别进行了比我能写得更好的解释。

## 你真的需要向量数据库吗? | Ethan Rosenthal

剧透警报:答案可能是!虽然,我使用“实际上”这个词暴露了我的偏见。向量数据库是……

www.ethanrosenthal.com

享受了这个故事吗?

免费订阅 以便在我发布新故事时收到通知。

[## 每当 Leonie Monigatti 发布时,获取一封电子邮件。

每当 Leonie Monigatti 发布时,获取一封电子邮件。通过注册,如果你还没有 Medium 账户,你将创建一个…

medium.com](https://medium.com/@iamleonie/subscribe?source=post_page-----fc392e48ab78--------------------------------)

LinkedInTwitter Kaggle上找到我!

MEMS 传感器数据的探索性分析

原文:towardsdatascience.com/exploratory-analysis-of-mems-sensor-data-bbfc0aa0a887

从 MPU6050 传感器读取、收集和分析数据

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

·发布于 Towards Data Science ·阅读时间 13 分钟·2023 年 8 月 19 日

MEMS(微电机械系统)传感器广泛应用于不同的场景,从游戏控制器和智能手机到无人机。在这篇文章中,我将展示如何连接陀螺仪和加速度计传感器、可以从中获取的数据类型以及如何处理和可视化这些数据。

让我们开始吧。

硬件

MPU-6050 是一个 6 轴传感器,结合了 3 轴陀螺仪、3 轴加速度计和 I2C 接口。正如数据表中所写,它广泛用于平板电脑和智能手机。当我们的智能手机或智能手表在锻炼时计算步数和卡路里时,实际上使用的是 MEMS 传感器的数据。但是像这样的传感器不仅仅可以用于运动。我决定将传感器放在我家中几天,看看是否能够检测和分析我所住的建筑物中的各种振动。

如果我们想在几天内收集数据,Raspberry Pi 是一个很好的解决方案。Raspberry Pi 是一个便宜的(30-50 美元)单板计算机;它功耗低,拥有大量引脚来连接不同类型的硬件。可以在亚马逊上以 3-5 美元的价格订购一个 MPU-6050 原型板。传感器本身使用 I2C 总线进行数据传输,只需 4 根线即可将其连接到 Raspberry Pi:

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

连接图示,作者提供的图片

在使用传感器之前,应该在 Raspbian 操作系统上启用 I2C 总线(关于如何将 MPU6050 连接到 Raspberry Pi 的教程足够多,所以我在这里跳过“硬件”细节)。连接传感器后,我创建了一个简单的 Python 应用程序,读取传感器数据并将其“原样”写入日志文件中:

from datetime import datetime
import smbus
import math
import time

# MPU6050 Registers
PWR_MGMT_1   = 0x6B
SMPLRT_DIV   = 0x19
CONFIG       = 0x1A
GYRO_CONFIG  = 0x1B
INT_ENABLE   = 0x38
ACCEL_XOUT_H = 0x3B
ACCEL_YOUT_H = 0x3D
ACCEL_ZOUT_H = 0x3F
GYRO_XOUT_H  = 0x43
GYRO_YOUT_H  = 0x45
GYRO_ZOUT_H  = 0x47

bus = smbus.SMBus(1)
address = 0x68

def device_init():
    """ Init the MPU-6050 """
    bus.write_byte_data(address, SMPLRT_DIV, 0x4)
    bus.write_byte_data(address, PWR_MGMT_1, 1)
    bus.write_byte_data(address, CONFIG, 0)
    bus.write_byte_data(address, GYRO_CONFIG, 24)
    bus.write_byte_data(address, INT_ENABLE, 1)

def read_byte(reg):
    """ Read 1 byte from the sensor """
    return bus.read_byte_data(address, reg)

def read_word(reg):
    """ Read 2 bytes from the sensor """ 
    h = bus.read_byte_data(address, reg)
    l = bus.read_byte_data(address, reg + 1)
    value = (h << 8) + l
    return value

def read_word_2c(reg):
    """ Read and convert the data """
    val = read_word(reg)
    return -((65535 - val) + 1) if val >= 0x8000 else val

def device_read():
    """ Get accel and gyro data """
    g_x = read_word_2c(GYRO_XOUT_H) / 131
    g_y = read_word_2c(GYRO_YOUT_H) / 131
    g_z = read_word_2c(GYRO_ZOUT_H) / 131
    a_x = read_word_2c(ACCEL_XOUT_H) / 16384
    a_y = read_word_2c(ACCEL_YOUT_H) / 16384
    a_z = read_word_2c(ACCEL_ZOUT_H) / 16384
    return g_x, g_y, g_z, a_x, a_y, a_z

if __name__ == "__main__":
    device_init()
    device_read()

    while True:
        timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
        gyro_x1, gyro_y1, gyro_z1, accel_x1, accel_y1, accel_z1 = device_read()
        gyro_x2, gyro_y2, gyro_z2, accel_x2, accel_y2, accel_z2 = device_read()
        g_x, g_y, g_z = (gyro_x1 + gyro_x2)/2, (gyro_y1 + gyro_y2)/2, (gyro_z1 + gyro_z2)/2
        a_x, a_y, a_z = (accel_x1 + accel_x2)/2, (accel_y1 + accel_y2)/2, (accel_z1 + accel_z2)/2
        s_data = f"{timestamp},{g_x: .7f},{g_y: .7f},{g_z: .7f},{a_x: .7f},{a_y: .7f},{a_z: .7f}"

        # Save to log file
        log_filename = datetime.now().strftime('%Y-%m-%d.log')
        with open(log_filename, "a", encoding="ascii") as log_out:
            log_out.write(s_data + "\n")

在“生产”场景中,我们可以将数据发送到 Kafka 主题或其他任何云服务提供商,但对于“家庭”测试,只需在后台运行应用程序即可:

nohup python3 accel_read.py >/dev/null 2>&1 &

之后,我们可以让 Raspberry Pi 运行几天。

从代码中我们可以看到,所有的日志文件都有一个“YYYY-MM-DD”的模式。我们可以使用 scp 从 Raspberry Pi 下载这些文件:

scp pi@raspberrypi3:/home/pi/Documents/AccelData/2023-08-01.log data

现在让我们看看我们可以获得什么样的数据。

总体见解

首先,让我们看看陀螺仪和加速度计的数据是什么样的。我们需要包含所需的库:

import pandas as pd

from bokeh.plotting import figure, show
from bokeh.models import Range1d, DatetimeTickFormatter
from bokeh.io import output_notebook
from bokeh.layouts import row, column, gridplot
output_notebook()

现在让我们将 CSV 文件加载到 Pandas 数据框中,并使用 Bokeh 绘制它:

df_sample = pd.read_csv("mpu6050.csv", 
                        header=None, 
                        names=["timestamp", "g_x", "g_y", "g_z", "a_x", "a_y", "a_z"],
                        parse_dates=["timestamp"])
display(df_sample)

这个样本包含了在 6 秒内收集的记录,数据值如下所示:

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

正如我们所见,我们可以从传感器中每秒获得约 60 次测量。让我们绘制数据:

timestamps = df_sample['timestamp']
# Accelerometer data
p1 = figure(title="Accelerometer data", x_axis_type='datetime', 
            x_axis_label='x', y_axis_label='y', width=1600, height=600)
p1.line(timestamps, df_sample["a_x"], legend_label="A_X", line_width=2, color="red")
p1.line(timestamps, df_sample["a_y"], legend_label="A_Y", line_width=2, color="green")
p1.line(timestamps, df_sample["a_z"], legend_label="A_Z", line_width=2, color="blue")
# Gyroscope data
p2 = figure(title="Gyroscope data", x_axis_type='datetime', 
            x_axis_label='x', y_axis_label='y', width=1600, height=600)
p2.line(timestamps, df_sample["g_x"], legend_label="G_X", line_width=2, color="#AA8822")
p2.line(timestamps, df_sample["g_y"], legend_label="G_Y", line_width=2, color="#AA88AA")
p2.line(timestamps, df_sample["g_z"], legend_label="G_Z", line_width=2, color="#2288AA")
show(column(p1, p2))

顺便提一下,Bokeh 库非常适合绘制这样的数据。结果是,至少在我的计算机上,当点数超过几千时,Matplotlib 几乎“崩溃”了。与此同时,Bokeh 可以处理在一个图表中多达 100 万条记录的数据。

输出结果如下所示:

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

加速度计和陀螺仪数据,图片来源于作者

了解陀螺仪和加速度计之间的区别也很重要。加速度计(上图)测量的是静态加速度,包括来自地球重力的作用。在这个例子中,我慢慢地在手中旋转板子,所有三个 X、Y 和 Z 轴的值都在成比例地变化。陀螺仪测量的是瞬时动量,围绕每个轴的旋转。这些数据看起来像是加速度计数据的导数;当运动开始时,会出现一个峰值,然后数值回到零。

我原本不打算在本文中分析步态数据,但一些读者可能仍然对人类步态的原始传感器数据感兴趣:

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

人类步态的加速度计和陀螺仪数据,图片来源于作者

正如我们所见,特别是在陀螺仪数据图中,这种模式很容易检测。为了本文的目的,更具挑战性的是看看我们是否能够检测到数据中更微小的变化,比如建筑物的振动。

数据分析

在前面的步骤中,我们了解了如何制作一个简单的应用程序来收集传感器数据,并对这些数据有了一个大致的了解。现在,让我们更详细地看看可以发现哪些有趣的模式。对于所有后续的示例,我将使用在 24 小时内收集的数据。

1. 时间戳准确性

作为提醒,传感器数据是通过在 Raspberry Pi 上运行的 Python 应用程序收集的。Raspberry Pi 本身运行的是 Linux,而不是实时操作系统。首先,让我们看看我们拥有的时间戳准确性。

df = pd.read_csv("data/2023-08-06.log", header=None, 
                 names=["timestamp", "g_x", "g_y", "g_z", "a_x", "a_y", "a_z"], parse_dates=["timestamp"])

t_diff = df["timestamp"].diff().dt.total_seconds()[1:]
diff_mean = t_diff.mean()
print(diff_mean, 1/diff_mean)

#> 0.0156 63.81

我在 24 小时内收集了 5,513,693 条记录;总文件大小约为 500 MBytes。正如我们所见,时间戳之间的平均差异为 0.015 秒,平均 fps 约为 64。它的稳定性如何?让我们创建一个时间差直方图

t_diff = df["timestamp"].diff().dt.total_seconds()[1:]

h, bins = np.histogram(t_diff.values, bins=1024)
print(list(zip(h, bins))[:100])

#> [(159712, 0.010), (5349277, 0.015), (4134, 0.0199), (293, 0.02462), 
#>  (96, 0.0293), (28, 0.0339), (10, 0.0386), (7, 0.043), (21, 0.048),
#>  ...
#>  (1, 0.1650), (1, 0.1697), (0, 0.1743), (1, 0.1790), (0, 0.1837), ...]

# Convert X to milliseconds and normalize Y to 0..100%
n_total = sum(h)
h = 100*h/n_total
bins *= 1000

# Create the bar plot
fig, ax = plt.subplots(1, 1, figsize=(10, 5))
ax.bar(bins[0:8], h[0:8], color='#440154', width=2.0)
ax.yaxis.label.set_color('gray')
ax.spines['left'].set_color('#DDDDDD')
ax.spines['right'].set_color('#DDDDDD')
ax.spines['top'].set_color('#DDDDDD')
ax.spines['bottom'].set_color('gray')
ax.xaxis.label.set_color('black')
ax.yaxis.label.set_color('gray')
ax.tick_params(axis='y', colors='#303030')
plt.xlabel("Timestamp difference, milliseconds")
plt.ylabel("Percentage of records")
plt.title("Timestamps accuracy")
plt.show()

我们可以看到,Raspbian 确实不是一个实时操作系统,但其准确性足够满足我们的任务:

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

时间戳差异(毫秒),图片由作者提供

5,349,277 条记录(超过 70%)具有约 0.015 秒(15 毫秒)的延迟,只有不到 50 条记录(0.001%)的间隔长于 0.01 秒(100 毫秒)。

2. 声谱图

让我们来看看更有趣的部分。显然,我们无法用肉眼分析 500 万条记录。让我们建立一个声谱图,以便查看频率域中是否存在一些异常:

def draw_sonogram(df_out: pd.DataFrame, t_start: datetime.datetime, t_end: datetime.datetime):
    """ Draw a sonogram from the dataframe """
    values = df_["g_y"].values
    t_diff = df_['timestamp'].diff().dt.total_seconds()[1:].mean()  # 0.015s ~ 50Hz

    fig, ax = plt.subplots(1, 1, figsize=(24, 14))
    ax.specgram(values, NFFT=256, Fs=1/t_diff, noverlap=50, scale="dB")
    plt.ylabel('Frequency, Hz')
    plt.xlabel('Time, sec')
    plt.show()

draw_sonogram(df, datetime.time(9,0,0), datetime.time(10,0,0))

声谱图基于快速傅里叶变换(FFT),将值从“时间域”转换为“频率域”。声谱图上的最大频率约为 30 Hz,根据奈奎斯特定理,这相当于采样率的一半。手动计算可能需要很多工作,但 Matplotlib 的“specgram”方法为我们完成了所有工作。结果如下:

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

传感器数据的声谱图,图片由作者提供

如我们所见,图表上有一些斑点,但我居住的建筑物没有明显的共振频率。但对于其他类型的建筑(电动机、机器、桥梁等),这种类型的分析可能很有用。

3. 热图

如果我们想在振动数据中找到一些模式,查看时间轴上的信号幅度是有意义的。但记录太多,将它们绘制在一条线上效果不好。在这种情况下,热图会更好。

为了制作热图,我使用了三个步骤来预处理数据:

  • 归一化。我提取了均值并取了绝对值:
df_ = df.copy()
df_["g_y_norm"] = (df_["g_y"] - df_["g_y"].mean()).abs()
  • 对滚动周期取最大值。这部分稍微复杂一些。在振动数据中,可能会出现短暂的 1–2 秒峰值。这些小峰值在 24 小时时间轴上不可见,所以我决定使用以下代码进行“滚动最大值”处理:
N = 400
df_["g_y_roll"] = df_['g_y_norm'].rolling(N).max()

实际上,它看起来是这样的:

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

数据处理示例,图片由作者提供

在这个例子中,我将滚动的样本数设置为 400。我们将有一个比短小(紫色)峰值更大的点在热图上。

热图本身可以使用 Seaborn 的“heatmap”方法来显示。下面是包括预处理和绘制的完整代码:

import seaborn as sns

def draw_heatmap(df: pd.DataFrame):
    """ Draw a heatmap from a dataframe """
    # Normalization and applying the rolling maximum
    df_ = df.copy()
    N = 400
    df_["g_y_norm"] = (df_["g_y"] - df_["g_y"].mean()).abs()
    df_["g_y_roll"] = df_['g_y_norm'].rolling(N).max()
    df_ = df_.iloc[::N, :]  # Keep each Nth element

    # Reshape all items to (24, N) matrix for heatmap
    items_all = df_["g_y_roll"].values[2:]
    items_per_hour = items_all.shape[0]//24
    items_reshaped = items_all[:items_per_hour*24].reshape((24, -1))

    # Horizontal labels
    hor_ticks = 6

    # Draw
    fig, ax = plt.subplots(figsize=(30, 8))
    sns.heatmap(items_reshaped, vmin=0, vmax=0.08, 
                cbar_kws={"orientation": "vertical", "pad": 0.01}, ax=ax)
    ax.hlines(list(range(24)), *ax.get_xlim(), colors="#303030")
    plt.xticks(rotation=0)
    ax.set_xticks(np.linspace(0, items_per_hour, hor_ticks+1))
    ax.set_xticklabels([10*n for n in range(hor_ticks+1)])
    plt.title('MPU6050 Vibration Levels', fontsize=16)
    plt.xlabel('Minutes', fontsize=12)
    plt.yticks(rotation=0)
    plt.ylabel('Hours', fontsize=12)
    plt.show()

draw_heatmap(df)

最终图像如下:

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

MPU6050 振动数据热图,图片由作者提供

正如我们所见,将所有 24 小时的数据放在一张图中更加“说明性”。例如,我们可以很容易地看到交通振动,它发生在早上 9 点和下午 4 点到 6 点之间。

我没有实现亮度调整;可以通过调整 sns.heatmap 调用的 vminvmax 参数在代码中手动更改。数据本身没有缺口,缺失值的处理在这里没有实现。

4. 异常检测

在热图上,我们可以看到一些有趣的模式,比如晚间交通造成的振动。我们还可以看到一些明亮的白点——有趣的是知道它是什么。

为了检测这些“异常”,我们将尝试两种方法。首先,我们可以直接找到大于阈值的数据。作为第二种方法,我们可以使用像 Python Outlier Detection (PyOD) 这样的现成库。让我们测试一下这两种方法!

基于阈值的过滤 是直接的。作为阈值,我选择了一个大值(7 个标准差),因此随机获得这种振动水平的概率极小。至于过滤本身,Pandas 已经拥有所有需要的方法:

df_ = df.copy()
df_["g_y_norm"] = (df_["g_y"] - df_["g_y"].mean()).abs()
std_y = df_["g_y_norm"].std()
threshold = 7*std_y

df_filtered = df_.index[df_['g_y_norm'] >= threshold]
print(df_filtered)
# > [2087075, 2153277, 2153981, 2798119, 2800170, 2800171, 
# > 3065854, 3065855,3065856, 3065858]

输出为数组索引。但有些项目过于接近;例如,索引 3065854 和 3065855 确实表示相同的事件。为了过滤数组,我创建了一个辅助方法来删除冗余项目:

def shrink_array(data: Any, num_samples: int) -> List:
    """ Remove too close items from array. Example: [1, 2, 3, 10, 20] => [1, 10, 20] """
    out = data[:1]
    for val in data[1:]:
        if val > out[-1] + num_samples:
            out.append(val)
    return out

indexes = shrink_array(df_filtered.values.tolist(), num_samples=500)
print(indexes)
# > [2087075, 2153277, 2153981, 2798119, 2800170, 3065854]

这里的参数“num_samples”用作标准;所有接近此值的数组项将从列表中删除。

现在,我们可以使用 Bokeh 显示结果:

from bokeh.layouts import gridplot

def make_plot(df_out: pd.DataFrame, title: str):
    """ Show graph data """
    timestamps = pd.to_datetime(df_out['timestamp'].values).to_pydatetime()
    p = figure(title=title, x_axis_type='datetime', 
               x_axis_label='x', y_axis_label='y', 
               width=600, height=400)
    p.line(timestamps, df_out["g_y"].values - df_out["g_y"].mean(), 
           legend_label="G_Y", line_width=1, color="blue")
    p.xaxis.formatter=DatetimeTickFormatter(seconds="%H:%M:%S")
    p.y_range = Range1d(-1.0, 1.0)
    return p

plots = []
for ind in indexes:
    plots.append(make_plot(df_[ind - 20:ind + 100], title=f"Index={ind}"))

show(gridplot(np.array_split(plots, 2)))

输出如下所示:

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

由阈值检测到的异常,图像由作者提供

作为本文的最后一步,让我们使用 Python Outlier Detection (PyOD) 库来寻找异常。这个库实现了 40 多种算法;我将展示其中之一,仅仅是为了让读者了解其工作原理。我将使用基于邻近的 KNN(k 最近邻)算法,它使用到第 k 个最近邻的距离作为异常值评分。

首先,我们需要使用一些数据来拟合算法。为此,我使用了之前找到的一个索引:

from pyod.models.knn import KNN

pos_train = 2087075
df_ = df[pos_train - 100000:pos_train + 100000]
fit_data = df_[["g_x", "g_y", "g_z"]].to_numpy()

clf = KNN(contamination=0.0001)
clf.fit(fit_data)

正如我们所见,使用 PyOD 和我的“天真”方法之间有两个主要区别。首先,PyOD 可以分析 多变量数据,因此我们可以使用传感器的所有三个轴。其次,根据我们的领域知识,我们需要指定一个 污染 率。我在寻找非常罕见且短暂的事件,这些事件可能每几个小时发生一次,因此我将此值设置为 0.0001。

当检测器训练完成后,我们可以简单地使用“predict”方法来处理另一组数据并获取结果:

pos_test = 2800170
df_test = df[["g_x", "g_y", "g_z"]][pos_test - 5000:pos_test + 5000]
data = df_test.to_numpy()
y_pred = clf.predict(data)  # Outlier labels (0 or 1)

为了以可视化形式查看结果,让我们在同一图表上绘制输入和预测:

# Draw
x = np.arange(0, len(y_pred))
y = y_pred
y1 = df_test["g_x"]
y2 = df_test["g_y"]
y3 = df_test["g_z"]
p = figure(title="KNN anomaly detection results", 
           x_axis_label='x', y_axis_label='y', 
           width=1600, height=500)
p.line(x, 0.04*y, legend_label="Anomaly", line_width=2, color="gray")
p.line(x, y1, legend_label="g_x", line_width=2, color="green")
p.line(x, y2, legend_label="g_y", line_width=2, color="red")
p.line(x, y3, legend_label="g_z", line_width=2, color="blue")
show(p)

这里的红色、绿色和蓝色线条表示传感器数据,灰色线条是预测结果,小的峰值显示了检测到异常值的索引:

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

异常检测与 PyOD,作者提供的图片

它有效。正如之前所述,PyOD 中有 40 多种算法可供使用。欢迎有兴趣的读者自行测试其他算法。如果有人希望在相同的数据集上测试这些算法,请在下方评论,我会分享一个临时链接。

结论

在这篇文章中,我解释了如何将 MPU6050 MEMS 传感器连接到树莓派单板计算机,并在几天内收集来自建筑物的振动数据。然后我们以不同的方式分析了这些数据,比如在时间轴和热图上绘制原始数据、在频域中构建声谱图,并应用异常检测算法。这对于现代城市环境的研究可能很有趣;例如,交通引起的振动在图表上非常明显(我实际上很惊讶 MPU6050,这个传感器主要设计用于智能手机和游戏控制器,竟然能可靠地检测到如此微小的振动)。甚至有可能检测到如地震这样的稀有事件,尽管对于这种事件,拥有至少两个地点的数据会更可靠。振动分析也可以用于预测机器如电动机或涡轮机的故障。实际应用的可能性非常大。

总的来说,进行这个实验以及使用“真实”硬件和其数据是很有趣的。遗憾的是,我必须承认关于硬件和物联网中的数据科学和数据分析的文章和帖子数量微乎其微,不仅在 TDS 上如此,在其他网站上也是如此。我希望这个故事能稍微改变这种不平衡,向读者展示与硬件打交道也可以很有趣。

感谢阅读。如果你喜欢这个故事,欢迎 订阅 Medium,你将会收到我新文章发布的通知,并且可以完全访问来自其他作者的成千上万的故事。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值