HashGNN: 深入探讨 Neo4j GDS 的新节点嵌入算法
在这篇文章中,我们将通过一个小示例来探讨 HashGNN 如何将图节点哈希到嵌入空间。
·
关注 发表在 Towards Data Science ·9 分钟阅读·2023 年 8 月 10 日
–
如果你更喜欢观看视频,你可以点击这里。
HashGG(#GNN)是一种节点嵌入技术,它采用了消息传递神经网络(MPNN)的概念来捕捉高阶邻近性和节点属性。通过利用一种称为 MinHashing 的近似技术,它显著加快了计算速度,相比于传统神经网络。因此,它是一种基于哈希的方法,在效率和准确性之间引入了权衡。本文将深入理解这些内容,并通过一个小示例探索算法的工作原理。
节点嵌入:具有相似上下文的节点在嵌入空间中应当接近
许多图机器学习用例,如链接预测和节点分类,都需要计算节点的相似度。在图的背景下,当这些相似度捕捉到(i)邻域(即图结构)和(ii)待嵌入节点的属性时,才最具表现力。节点嵌入算法将节点投射到低维嵌入空间中——即它们为每个节点分配一个数值向量。这些向量——即嵌入——可以用于进一步的数值预测分析(例如,机器学习算法)。嵌入算法优化的指标是:具有相似图上下文(邻域)和/或属性的节点应当在嵌入空间中被映射得接近。图嵌入算法通常采用两个基本步骤:(i)定义一种机制来采样节点的上下文(node2vec 中的随机游走,FastRP 中的 k-fold 转移矩阵),和(ii)随后在保留成对距离的同时减少维度(node2vec 中的 SGD,FastRP 中的随机投影)。
HashGNN:绕过训练神经网络
关于 HashGNN 的线索是,它不要求我们基于损失函数训练神经网络,就像我们在传统的消息传递神经网络中需要做的那样。由于节点嵌入算法优化的是“相似的节点应该在嵌入空间中接近”,损失的评估涉及计算节点对的真实相似度。这被用作训练的反馈,来评估预测的准确性,并相应地调整权重。通常,余弦相似度被用作相似度度量。
HashGNN 规避了模型训练,实际上完全不使用神经网络。它不训练权重矩阵或定义损失函数,而是使用随机哈希方案,将节点向量哈希到具有相似度的相同签名,这意味着我们可以嵌入节点,而不需要直接比较节点(即无需计算余弦相似度)。这种哈希技术称为 MinHashing,最初定义为在不比较集合的情况下近似两个集合的相似度。由于集合被编码为二进制向量,HashGNN 需要二进制节点表示。为了理解如何将其用于嵌入一般图的节点,需要几种技术。让我们来看一看。
MinHashing
首先,让我们讨论一下 MinHashing。MinHashing 是一种局部敏感哈希技术,用于近似两个集合的 Jaccard 相似度。Jaccard 相似度通过将交集大小除以两个集合中存在的唯一元素数量(并集)来度量两个集合的重叠(交集)。它定义在编码为二进制向量的集合上:宇宙中的每个元素(所有元素的集合)都分配一个唯一的行索引。如果特定集合包含一个元素,则在集合向量的相应行中表示为值 1。MinHashing 算法独立地哈希每个集合的二进制向量,并使用K
个哈希函数生成K
维签名。MinHashing 的直观解释是随机选择非零元素K
次,选择哈希值最小的那个。这将产生输入集合的签名向量。有趣的是,如果我们对两个集合进行这种操作而不进行比较,它们将以其 Jaccard 相似度的概率哈希到相同的签名(如果K
足够大)。换句话说:概率趋近于 Jaccard 相似度。
Jaccard 相似度度量两个集合的相似度。通常,集合也可以编码为二进制向量。图像来源:作者。
在插图中:示例集合s1
和s2
被表示为二进制向量。我们可以通过比较这两个向量并计算两个向量都为 1 的行数,轻松计算 Jaccard 相似度。这些操作相当简单,但复杂性在于当我们有多个向量时,向量之间的成对比较。
MinHashing 算法生成集合特征的 k 个排列,并选择具有最小哈希值的特征以创建 minHash 签名向量。图像来源:作者。
我们的宇宙 U
大小为 6,我们选择 K
(哈希函数的数量)为 3。我们可以通过使用简单的公式和 a
、b
和 c
的边界来轻松生成新的哈希函数。现在,我们实际上做的是使用每个哈希函数对我们向量的索引(1-6
)进行哈希,每个索引与我们宇宙中的一个单一元素相关联。这将为我们提供 3 个随机排列的索引,从而得到我们宇宙中的元素。随后,我们可以将我们的集合向量 s1
和 s2
用作我们排列特征的掩码。对于每个排列和集合向量,我们选择集合中最小哈希值的索引。这将生成两个 3 维向量,每个集合一个,这就是集合的 MinHash 签名。
MinHashing 仅仅从输入集选择随机特征,我们只需使用哈希函数来在所有输入集上均等地再现这种随机性。我们必须在所有向量上使用相同的哈希函数,以便两个输入集的签名值在两个集合都包含所选元素时发生冲突。签名值将以集合的 Jaccard 相似度的概率发生冲突。
WLKNN(Weisfeiler-Lehman 核心神经网络)
HashGNN 使用如 Weisfeiler-Lehman 核心神经网络(WLKNN)中定义的消息传递方案来捕捉高阶图结构和节点属性。它定义了之前提到的 HashGNN 上下文采样策略。WLK 在 T
次迭代中运行。在每次迭代中,它通过将节点的当前向量与所有直接连接邻居的向量组合,为每个节点生成一个新的节点向量。因此,它被认为是沿边将消息(节点向量)传递给相邻节点。
WLK 在 T 次迭代中运行。在每次迭代中,它将节点信息沿边传递给相邻节点。图片来源于作者。
在 T
次迭代之后,每个节点包含 T
跳距离(高阶)的节点信息。迭代 t
中的新节点向量的计算本质上是将所有邻居消息(来自迭代 t-1
)聚合为单一邻居消息,然后与前一迭代的节点向量组合。此外,WLKNN 采用三个神经网络(权重矩阵和激活函数);(i)用于聚合邻居向量,(ii)用于节点向量,以及(iii)用于两者的组合。WLK 的一个显著特征是迭代 t
中的计算仅依赖于迭代 t-1
的结果。因此,它可以被视为一个马尔可夫链。
WLK:在每次迭代中,每个节点向量都会更新来自相邻节点的信息。因此,在 t 次迭代之后,每个节点包含来自 t 跳距离节点的信息。图片来源于作者。
HashGNN 示例
让我们探讨一下 HashGNN 如何结合这两种方法高效地将图向量嵌入到嵌入向量中。与 WLKNN 类似,HashGNN 算法在 T
次迭代中运行,通过聚合邻居向量和前一迭代的自身节点向量,为每个节点计算一个新的节点向量。然而,它不是训练三个权重矩阵,而是使用三种哈希方案进行局部敏感哈希。每种哈希方案包含 K
个随机构造的哈希函数,从二进制向量中提取 K
个随机特征。
HashGNN 算法:我们用其二进制特征向量初始化节点向量。图片由作者提供。
在每次迭代中,我们执行以下步骤:
第 1 步:计算节点的签名向量: 使用哈希方案 3 对来自前一迭代的节点向量进行最小哈希(随机选择 K
个特征)。在第一次迭代中,节点用其二进制特征向量初始化(稍后我们将讨论如何对节点进行二值化)。得到的签名(或消息)向量是沿着边传递给所有邻居的。因此,我们必须在每次迭代中首先对所有节点执行此操作。
HashGNN 第 1 步:在每次迭代中,通过使用哈希方案 3 计算每个节点的消息向量。图片由作者提供。
第 2 步:构建邻居向量: 在每个节点中,我们将接收来自所有直接连接邻居的签名向量,并将它们聚合成一个二进制向量。随后,我们使用哈希方案 2 从聚合的邻居向量中选择 K
个随机特征,并将结果称为邻居向量。
HashGNN 第 2 步:我们收集所有邻居的消息向量并将其聚合。图片由作者提供。
第 3 步:将节点向量和邻居向量合并成新的节点向量: 最后,我们使用哈希方案 1 从前一迭代的节点向量中随机选择 K
个特征,并将结果与邻居向量结合。得到的向量是新的节点向量,它是下一次迭代的起点。注意,这与第 1 步不同:在第 1 步中,我们对节点向量应用哈希方案 3 来构建消息/签名向量。
HashGNN 第 3 步:我们将最小哈希的节点向量与聚合的邻居向量结合,以得到该迭代的结果节点向量。图片由作者提供。
从图中我们可以看到,得到的(新)节点向量受自身节点特征(3 和 5)以及其邻居特征(2 和 5)的影响。经过第一次迭代后,节点向量将捕捉到来自距离 1 跳的邻居的信息。然而,当我们将其用作第二次迭代的输入时,它已经受到距离 2 跳特征的影响。
在第一次迭代后,新的节点向量受自身特征和邻近节点特征的影响。图片由作者提供。
Neo4j GDS 泛化
HashGNN 是由 Neo4j GDS(图数据科学库)实现的,并对算法进行了有用的泛化。
GDS 中一个重要的辅助步骤是特征二值化。MinHashing 和因此 HashGNN 需要二进制向量作为输入。Neo4j 提供了一个额外的准备步骤,将实值节点向量转换为二进制特征向量。他们使用了一种称为超平面取整的技术。算法的工作流程如下:
步骤 1:定义节点特征: 定义用于节点特征的(实值)节点属性。这是通过参数featureProperties
完成的。我们将其称为节点输入向量f
。
步骤 2:构建随机二进制分类器: 为每个目标维度定义一个超平面。结果维度的数量由参数dimensions
控制。超平面是一个高维平面,只要它位于原点,就可以仅通过其法向量n
来描述。n
向量垂直于平面的表面,因此描述了其方向。在我们的情况下,n
向量需要与节点输入向量具有相同的维度(dim(f) = dim(n)
)。为了构建一个超平面,我们简单地从高斯分布中抽取dim(f)
次。
特征二值化:我们使用超平面取整将实值输入向量构造为二进制特征。我们为每个目标维度使用一个随机高斯分类器。图片由作者提供。
步骤 3:分类节点向量: 计算节点输入向量和每个超平面向量的点积,从而得到超平面和输入向量之间的角度。使用threshold
参数,我们可以决定输入向量是高于(1)还是低于(0)超平面,并将相应的值分配给结果的二进制特征向量。这与二元分类中的过程完全一致——唯一的不同是我们不迭代优化超平面,而是使用随机高斯分类器。
使用 n 个超平面会导致 n 维的二进制节点签名。图片由作者提供。
本质上,我们为每个目标维度绘制一个随机高斯分类器,并设置一个阈值参数。然后,我们对每个目标维度的输入向量进行分类,并得到一个d
维的二进制向量,该向量将作为 HashGNN 的输入。
结论
HashGNN 使用局部敏感哈希将节点向量嵌入到嵌入空间中。通过使用这种技术,它绕过了计算密集型的神经网络(或其他优化)的迭代训练以及直接的节点比较。论文的作者报告称,与基于学习的算法如 SEAL 和 P-GNN 相比,其运行时间快 2 到 4 个数量级,同时仍能产生高度可比(在某些情况下甚至更好)的准确性。
HashGNN 比基于学习的算法快 2 到 4 个数量级,同时提供了可比的结果。图像来源:arxiv.org/abs/2105.14280.
HashGNN 在 Neo4j GDS(图数据科学库)中实现,因此可以直接在你的 Neo4j 图上使用。在下一篇文章中,我将详细讲解如何使用它以及需要注意的事项。
感谢你的光临,下次见。🚀
参考文献
现代推荐系统中的哈希:入门
原文:
towardsdatascience.com/hashing-in-modern-recommender-systems-a-primer-9c6b2cf4497a
理解应用机器学习中最被低估的技巧
·发表于Towards Data Science ·阅读时间 6 分钟·2023 年 3 月 28 日
–
(Midjourney)
哈希是工业机器学习应用中最常见的“技巧”之一,但它并没有得到应有的关注。
哈希的最大优势,特别是在现代推荐系统中,是其有限内存保证:如果没有哈希,在不耗尽内存的情况下,学习数十亿视频、新闻文章、照片或网页的相关性将是极其不切实际的。
但我们这里有些超前了。本文是一个入门介绍,让我们回到一切开始的地方:著名的 2009 年“哈希技巧”论文。
开创一切的论文
使用哈希作为处理特征的一种方法,以及“哈希技巧”这一术语,首次在 2009 年由雅虎的研究团队提出,该团队由 Kilian Weinberger 领导,背景是电子邮件垃圾邮件检测。毕竟,电子邮件是一系列单词,每个单词都可以被视为一个特征。作者解释说,通过哈希,我们可以将电子邮件表示为向量(其中每个索引对应一个特定的单词),并在垃圾邮件协同过滤模型中使用这些向量。
例如,“您的处方已准备好”这一短语在 20 维哈希空间中可能等同于
[0,0,0,1,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1]
以及其他,1 的位置取决于所使用的特定哈希函数。
然而,不同的词可能对不同的人具有不同的相关性。词“处方”对某些用户可能表示垃圾邮件,而对另一些用户则不然。为了考虑这种相关性的差异,作者引入了“个性化”标记,将用户 ID 与单词本身结合起来。对于上述短语,他们不仅哈希标记
"your", "prescription", "is", "ready",
还包括
"user123_your", "user123_prescription", "user123_is", "user123_ready",
等等。鉴于他们的数据集包含 4000 万唯一单词和 40 万用户,这种个性化标记化导致了总共 16 万亿个可能的特征。
通过将这些个性化特征哈希成 2²²的哈希大小,或大约 420 万(减少了 7 个数量级!),作者实现了 30%的垃圾邮件减少,相比于没有哈希个性化的基线模型,这是哈希在机器学习问题中有用性的首次明确展示之一。
现代推荐系统中的哈希
从 2009 年到今天,虽然许多机器学习技术已经发生了变化(深度神经网络在很大程度上取代了线性协同过滤器),但哈希仍然存在。
现代推荐系统通常是某种变体的双塔神经网络,其中一个塔从用户 ID 学习用户嵌入,另一个塔则从视频 ID 学习视频嵌入(对于视频推荐系统)。在训练时,模型会从历史互动数据中获得用户/视频对,例如带有点击的观看次数作为正样本,没有点击的观看次数作为负样本,从而形成一个共享的用户和视频嵌入空间。然后,我们可以将所有用户和视频的嵌入存储在一组嵌入表中,并在服务时使用这些表进行推荐,例如使用 kNN 算法。
到目前为止,一切还算顺利,但哈希在这里的作用是什么?
好吧,考虑一个存储 1B 视频和 1B 用户的 100 维嵌入的模型:这已经占用了 800GB 的内存。这是一个巨大的内存开销,模型将非常不切实际(如果不是不可能的话)且昂贵。通过哈希,我们可以首先将视频和用户的“词汇表”减少到,比如说,1000 万,从而使内存开销更易于管理,为 8GB。
换句话说,哈希允许我们将现代推荐系统扩展到数十亿用户和数十亿项目。如果没有哈希,推荐系统的规模将被我们能够承受的内存量所根本限制。
朝着无冲突哈希的方向前进
哈希的缺点是哈希冲突的存在,即多个不同的 ID 最终会映射到相同的哈希值,导致嵌入表中多个用户或项目共享相同的嵌入。显然,这种信息的“挤压”会降低推荐的质量。因此,当前推荐系统中最重要的研究问题之一是如何使其无冲突。
一种思路是使用“深度哈希嵌入”(DHE),这是由谷歌大脑的王成康团队于 2021 年提出的。他们使用大量的哈希函数,而不是单个哈希函数,并将所有哈希合并为一个稠密向量,然后输入到深度神经网络中,网络学习 ID 的高阶表示。关键思想是,如果哈希函数的数量足够大,那么哈希冲突在统计上变得不可能。
的确,他们的方法显示出前景:使用 1024 个哈希函数的 DHE,作者在一组公共基准数据集上看到 AUC 提高了 0.25%。不过,这种方法的一个缺点是它不适用于多值特征,只适用于直接的 ID。(例如,所提到的 DHE 不能编码过去 30 天你在 Netflix 上观看的电影列表,这将是一个多值特征。)
另一种有前景的无冲突哈希方法是“布谷鸟哈希”,由丹麦奥胡斯大学的拉斯穆斯·帕赫和弗莱明·罗德勒于 2001 年首次提出。布谷鸟哈希被用于字节跳动的在线推荐系统 Monolith,该系统在他们 2022 年的论文中介绍,由刘卓然领导。
在布谷鸟哈希中,我们不仅维护一个哈希表,而是多个哈希表:在字节跳动的论文中,他们使用了 2 个。当遇到新的 ID 时,默认情况下我们将其哈希到第一个哈希表中。如果第一个哈希表已被另一个 ID 占据,我们将逐出那个 ID(因此算法的名称)并将其重新哈希到第二个哈希表中的一个位置。这个过程会重复直到没有更多逐出,哈希表集合稳定且没有冲突。与常规哈希(有冲突)相比,作者发现他们的无冲突哈希方法在公共基准数据集上提高了 0.5% AUC。
代码:应用机器学习中最被低估的技巧
内存回顾:
-
哈希可以让我们将现代推荐系统扩展到数十亿用户和数十亿项目,同时提供有限内存保证。
-
在机器学习应用中使用哈希的想法可以追溯到 2009 年 Yahoo 的一篇论文,该论文展示了其在电子邮件垃圾邮件检测模型中的有效性。
-
然而,哈希会引入哈希冲突,这可能由于多个用户和项目共享相同的嵌入而降低推荐质量。
-
因此,最近推荐系统的研究集中在如何使哈希无冲突上。显著的例子有深度哈希嵌入(谷歌大脑)和布谷鸟哈希(字节跳动)。
多年来,哈希一直是应用机器学习文献中最被低估的技巧之一,大部分关注点集中在模型架构、数据选择或特征工程上。
这可能开始发生变化。正如谷歌大脑、字节跳动等最近的论文所示,优化推荐系统以减少哈希碰撞可以显著提升性能。TikTok(由字节跳动拥有)的惊人受欢迎程度,至少部分可以用更好的哈希解释。
请关注这个领域:新的突破确实在即将到来。
不想依赖 Medium 的算法?注册一下。通过注册我的电子邮件,确保你不会错过我的下一篇文章…
medium.com](https://medium.com/@samuel.flender/subscribe?source=post_page-----9c6b2cf4497a--------------------------------)
机器是否刚刚实现了在人的语言中进行进化性的飞跃?
图片来源:Adobe Stock。
评估我们在实现人与人工智能之间深层次、有意义的沟通的旅程中的位置
·
关注 发表在 Towards Data Science · 12 min read · 2023 年 4 月 17 日
–
人们与对话型人工智能(AI)系统互动时,清晰的沟通是获得最佳效果的关键因素,这将最有利于提升我们的生活。从更广泛的角度来看,应使用什么语言来控制系统和与机器对话?在这篇博客文章中,我们评估了基于最近技术创新的方法,例如 OpenAI 的 ChatGPT 和 GPT-4,来引导和对话机器的发展,并探讨对话 AI 在掌握自然对话方面的下一步所需的步骤。机器已经从提示工程跃升到“人类语言”,但其他智能方面仍在等待发现。
直到 2022 年之前,让 AI 做出正确响应并发挥其优势需要专业知识,如复杂的提示工程。ChatGPT 的推出是机器对话能力的重大进步,使得即使是高中生也能与高效能 AI 聊天并获得令人印象深刻的结果。这是一个重要的里程碑。然而,我们也需要评估在人与机器沟通的旅程中,我们处于什么位置,以及还需要什么来实现与 AI 的有意义对话。
人与机器的互动有两个主要目标:一是指导机器完成所需任务,二是在任务执行过程中交换信息和指导。第一个目标传统上是通过编程来实现,但现在正在演变为用户对话可以定义新任务,例如请求 AI 创建一个 Python 脚本来完成任务。在任务执行中的交流是通过自然语言处理(NLP)或自然语言理解(NLU)结合机器响应生成来实现的。我们可以假设,人机互动进展的核心特征——如果不是终点——是当人们可以像与老朋友一样与机器沟通,包括所有的自由形式的语法、语义、隐喻和文化方面的内容。在 AI 系统要全面参与这种自然交流时,需要创造什么?
机器已经从提示工程跃升到“人类语言”,但其他智能方面仍在等待发现。
过去的对话 AI:变换器架构重新定义了 NLP 性能
图 1. 机器编程和指令的发展。图片来源:经 Intel Labs 许可。
在计算初期,人类和机器只能通过机器码进行通信,这是一种低级的二进制计算机语言——由 0 和 1 组成的字符串,与人类通信几乎没有任何相似之处。过去一个世纪以来,我们逐渐实现了让与机器的交流更接近人类语言的旅程。如今,我们能够让机器生成一张猫在下棋的图片,这是巨大进步的证明。随着编程语言从低级到高级代码的演进,从汇编语言到 C 语言再到 Python,以及引入像 if-then 语句这样类似人类语言的结构,这种交流逐渐改善。现在的最终步骤是消除输入措辞微调的敏感性,使机器和人类可以以自然的方式互动。人机对话应该允许根据过去的“保存点”进行增量引用以继续对话。
自然语言处理(NLP)处理计算机与人类语言之间的交互,以处理和分析大量自然语言数据,而自然语言理解(NLU)则承担了检测用户意图的困难任务。像 Alexa 和 Google 这样的虚拟助手使用 NLP、NLU 和机器学习(ML)在运行时获取新知识。通过使用预测性智能和分析,AI 可以根据用户偏好个性化对话和响应。虽然虚拟助手像是人们家中的可信朋友,但它们目前仍然受限于基本的命令语言循环。人们已经适应了这一点,通过说出“关键字语言”来获得最佳结果,但他们的对话 AI 在理解自然语言交互方面仍然存在差距。当与虚拟助手出现沟通中断时,人们会使用修复策略,如简化话语、调整信息量的变化、语义和句法上的查询调整以及重复命令。
图 2。自然语言理解中的六个意图层级。图片来源:经由 Intel Labs 许可使用。
在理解意图方面,自然语言理解(NLU)至关重要。NLU 分析文本和语音以确定其含义(见图 2)。使用对人类语言的语义和语用定义的数据模型,NLU 专注于意图和实体识别。2018 年引入的 Transformer 神经网络架构使虚拟助手的自然语言处理(NLP)性能得到了提升。这些类型的网络利用自注意力机制来处理输入数据,从而有效捕捉人类语言中的依赖关系。由 Google AI Language 的研究人员 提出的 BERT 解决了一个模型中的 11 个最常见的 NLP 任务,改进了传统的为每个特定任务使用独立模型的方法。BERT 通过在大规模文本语料库(如维基百科)上训练通用语言理解模型,来预训练语言表示,然后将模型应用于下游 NLP 任务,如问答和语言推理。
除了虚拟助手之外,ChatGPT 的进步与 Transformer 模型在 NLP 性能上的提升是相辅相成的。GPT-3 Transformer 技术于 2021 年引入,但其在流行度和使用上的重大突破是通过 ChatGPT 实现的,其在人类对话界面的创新得以通过 来自人类反馈的强化学习(RLHF) 的应用实现。ChatGPT 使大型语言模型能够处理和理解自然语言输入,并生成尽可能接近人类的输出。
当前:大型语言模型主导对话式人工智能
自 2022 年 11 月由 OpenAI 发布以来,ChatGPT 以其似乎写得很好的语言生成能力和成功通过医学执照及 MBA 考试的表现主导了新闻。ChatGPT 在没有任何训练或强化的情况下通过了 美国医学执照考试(USMLE)的所有三个考试。这导致研究人员得出结论:“大型语言模型可能有潜力协助医学教育,并有可能辅助临床决策。” 一位 宾夕法尼亚大学沃顿商学院的教授 对 ChatGPT 进行了运营管理 MBA 期末考试测试,其成绩为 B 到 B-。ChatGPT 在基于案例研究的基础运营管理和过程分析问题上表现良好,提供了正确的答案和充分的解释。当 ChatGPT 未能将问题与正确的解决方法匹配时,人类专家的提示帮助模型纠正了答案。尽管这些结果令人鼓舞,但 ChatGPT 在达到人类水平的对话方面仍有局限性(我们将在下一节讨论)。
- 作为一个自回归语言模型,ChatGPT 拥有 1750 亿个参数,其庞大的模型尺寸帮助其在理解用户意图方面表现出色。基于图 2 中的意图水平,ChatGPT 可以通过分析文本提示的目标来处理用户包含开放参数集和灵活结构的实用请求。ChatGPT 能够撰写高度详细的回答和清晰的答案,展示出对医学、业务运营、计算机编程等不同领域的广泛和深刻的知识。GPT-4 也显示出了令人印象深刻的优势,例如添加多模态能力和在高级人类测试中提高得分。据报道,GPT-4 在统一巴尔考试中的得分为 90 分位数(ChatGPT 为 10 分位数),在美国生物奥林匹克竞赛中的得分为 99 分位数(ChatGPT 为 31 分位数)。
ChatGPT 也可以进行机器编程,尽管程度有限。它可以创建多种语言的程序,包括 Python, JavaScript, C++, Java,等等。它还可以 分析代码中的错误和性能问题。然而,目前看来,最好的利用方式似乎是作为程序员和人工智能的联合组合的一部分。
-
尽管 OpenAI 的模型吸引了很多关注,其他模型也在类似的方向上取得进展,例如 Google Brain 的开源 1.6 万亿参数的 Switch Transformer,该模型在 2021 年首次亮相,以及使用 LaMDA(用于对话应用的语言模型)技术的 Google Bard。Bard 目前仅对测试用户开放,因此其与 ChatGPT 的表现尚不为人知。
-
尽管大语言模型在与人类进行自然对话方面取得了巨大进展,但仍需要解决关键增长领域。
- 要达到下一个智能水平和人类级沟通,关键领域需要能力飞跃 —— 知识重组,多技能整合和提供上下文适应。
- 未来:人机对话缺少什么?
- 在将会话型人工智能推向下一个水平的过程中,仍然存在四个关键元素的缺失,这些元素包括达成自然对话的亲密性和共享目的。要达到这一水平,机器需要理解个体的象征性沟通的含义,并用有意义的、可信赖的定制回应来回应。
1) 生成可信的回应。 AI 系统不应该产生幻觉!认识论问题影响了 AI 构建知识的方式,以及区分已知和未知信息的能力。当机器在不了解的事物上提供答案时,可能会出错,产生有偏见的结果,甚至是幻觉。ChatGPT 在捕捉来源归因和信息出处方面存在困难。它可以生成听起来合理但错误或荒谬的答案。此外,它在处理物理、空间和时间问题以及数学推理方面缺乏事实的正确性和常识。据 OpenAI 称,它在处理“如果我把奶酪放进冰箱,它会融化吗?”这样的问题时表现不佳。在 MBA 期末考试 上测试时,它在六年级水平的数学中出现了令人惊讶的错误,可能导致操作上的重大错误。研究发现,“Chat GPT 在处理更复杂的流程分析问题时能力不足,即使这些问题基于标准模板。这包括具有多种产品和需求变异等随机效应的流程流程。”
2) 对人类符号和特殊习惯的深刻理解。 AI 需要在人类的完整符号世界内工作,包括能够进行抽象、定制和理解部分引用。机器必须能够解释人们言辞中的模棱两可和不完整的句子,以便进行有意义的对话。
图 3. 从 Switchboard 收集 中的一个例子,展示了来自美国各地发言者之间的双向电话对话。图片来源:经过 Intel Labs 的许可。
正如图 3 所示,人类的言语模式经常是难以理解的。AI 是否应该像人类一样说话?实际上,它可以与朋友一样使用松散结构的语言进行交流,包括合理数量的“嗯嗯”、“喜欢”、“不完整或不完整的句子”、“模棱两可”、“语义抽象”、“个人引用” 和常识推断。但这些人类言语的特殊习惯不应该使沟通变得难以理解。
3) 提供定制化响应。 AI 需要具备定制能力并熟悉用户的世界。ChatGPT 经常猜测用户的意图,而不是提出澄清问题。此外,作为一个完全封装的信息模型,ChatGPT 不具备浏览或搜索互联网以提供定制答案的能力。根据 OpenAI 的说法,ChatGPT 的定制答案有限,因为“它对每个标记的权重相等,缺乏对预测中最重要和较不重要内容的概念。通过自我监督目标,任务规格依赖于将期望任务强制变成预测问题,而最终,像虚拟助手这样的有用语言系统可能更应被视为采取目标导向的行动,而不仅仅是进行预测。”此外,拥有多会话的人工与机器对话的上下文,以及用户的心理理论模型会很有帮助。
4) 成为目标驱动。 当人们与伴侣合作时,协调不仅仅基于文本交换,而是基于共同的目标。AI 需要超越上下文答案,变得目标驱动。在不断发展的人工与机器关系中,双方需要成为实现目标、避免或减轻问题,或共享信息的过程的一部分。ChatGPT 和其他 LLM 尚未达到这种交互水平。正如我在之前的博客中探讨的那样,智能机器需要超越输入输出回复和对话作为聊天机器人。
为了达到下一水平的智能和人类级别的沟通,关键领域需要在能力上实现飞跃——知识的重组、技能的整合以及提供上下文适应。
通往对话 AI 下一水平的道路
像 ChatGPT 这样的 LLM 仍在认知技能上存在差距,这些技能是将对话 AI 提升到下一水平所必需的。缺失的能力包括逻辑推理、时间推理、数值推理以及整体上驱动目标的能力和定义子任务以完成更大任务的能力。ChatGPT 和其他 LLM 的知识相关限制可以通过Thrill-K 方法来解决,通过增加检索和持续学习。知识对于 AI 存在于三个地方:
1) 即时知识. 常用的知识和连续功能,可以在参数化内存中的最快最昂贵的层或其他 ML 处理的工作内存中有效近似,ChatGPT 目前使用这种端到端的深度学习系统,但需要扩展以包括其他知识来源,以便作为人类伴侣更有效。
2) 待机知识. 对 AI 系统有价值但不常用的知识,通过需要时从相邻的结构化知识库提取。它需要增强对离散实体的表示能力,或者需要保持广泛而灵活,以适应各种新的使用方式。基于待机知识的行动或结果需要处理和内部解决,使得 AI 能够像人类伴侣一样学习和适应。
3) 检索的外部知识. 来自广阔在线存储库的信息,供 AI 系统外检索时使用。这使得 AI 能够根据人类伴侣的需求定制答案,提供理性分析,并解释信息来源和达到结论的路径。
总结
从机器语言到人类语言的旅程已经从人类输入简单的二进制数字到计算机,发展到在家里使用虚拟助手执行简单任务,再到向像 ChatGPT 这样的 LLM 询问并接收明确答案。尽管 LLM 在近期的创新取得了巨大进展,但要达到下一个水平的对话型 AI,需要知识重组、多重智能和上下文适应,以构建真正的人类伴侣。
参考文献
-
Mavrina, L., Szczuka, J. M., Strathmann, C., Bohnenkamp, L., Krämer, N. C., & Kopp, S. (2022). “Alexa, You’re Really Stupid”: A Longitudinal Field Study on Communication Breakdowns Between Family Members and a Voice Assistant. 计算机科学前沿, 4.
doi.org/10.3389/fcomp.2022.791704
-
Devlin, J. (2018 年, 10 月 11 日). BERT: 深度双向转换器的预训练用于语言理解. arXiv.org.
arxiv.org/abs/1810.04805
-
Wikipedia 贡献者. (2023 年). 从人类反馈中的强化学习. 维基百科.
en.wikipedia.org/wiki/Reinforcement_learning_from_human_feedback
-
介绍 ChatGPT. (无日期).
openai.com/blog/chatgpt
-
Kung, T. H., Cheatham, M., Medenilla, A., Sillos, C., De Leon, L., Elepaño, C., Madriaga, M., Aggabao, R., Diaz-Candido, G., Maningo, J., & Tseng, V. (2022)。ChatGPT 在 USMLE 上的表现:利用大型语言模型进行 AI 辅助医学教育的潜力。medRxiv(冷泉港实验室)。
doi.org/10.1101/2022.12.19.22283643
-
Needleman, E. (2023)。Chat GPT 能获得沃顿 MBA 吗?Christian Terwiesch 新白皮书。Mack 创新管理研究所。
mackinstitute.wharton.upenn.edu/2023/would-chat-gpt3-get-a-wharton-mba-new-white-paper-by-christian-terwiesch/
-
OpenAI. (2023)。GPT-4 技术报告。arXiv(康奈尔大学)。
doi.org/10.48550/arxiv.2303.08774
-
Gewirtz, D. (2023 年 4 月 6 日)。如何使用 ChatGPT 编写代码。ZDNET。
www.zdnet.com/article/how-to-use-chatgpt-to-write-code/
-
ChatGPT 支持多少种语言?完整的 ChatGPT 语言列表。 (n.d.).
seo.ai/blog/how-many-languages-does-chatgpt-support
-
Tung, L. (2023 年 2 月 2 日)。ChatGPT 能编写代码。现在研究人员说它也擅长修复漏洞。ZDNET。
www.zdnet.com/article/chatgpt-can-write-code-now-researchers-say-its-good-at-fixing-bugs-too/
-
Fedus, W., Zoph, B., & Shazeer, N. (2021)。Switch Transformers:以简单有效的稀疏性扩展到万亿参数模型。arXiv(康奈尔大学)。
doi.org/10.48550/arxiv.2101.03961
-
Pichai, S. (2023 年 2 月 6 日)。我们 AI 旅程中的一个重要下一步。Google。
blog.google/technology/ai/bard-google-ai-search-updates/
-
Dickson, B. (2022 年 7 月 31 日)。大型语言模型无法计划,即使它们写出花哨的文章。TNW | Deep-Tech。
thenextweb.com/news/large-language-models-cant-plan
-
Brown, T., Mann, B. F., Ryder, N., Subbiah, M., Kaplan, J., Dhariwal, P., Neelakantan, A., Shyam, P., Sastry, G., Askell, A., Agarwal, S., Herbert-Voss, A., Krueger, G., Henighan, T., Child, R., Ramesh, A., Ziegler, D. M., Wu, J. C., Winter, C., . . . Amodei, D. (2020)。语言模型是少样本学习者。arXiv(康奈尔大学)。
doi.org/10.48550/arxiv.2005.14165
-
Singer, G. (2022 年 8 月 17 日). 超越输入输出推理:认知人工智能的四个关键特性。Medium.
towardsdatascience.com/beyond-input-output-reasoning-four-key-properties-of-cognitive-ai-3f82cde8cf1e
-
Singer, G. (2022 年 1 月 6 日). Thrill-K:下一代机器智能的蓝图。Medium.
towardsdatascience.com/thrill-k-a-blueprint-for-the-next-generation-of-machine-intelligence-7ddacddfa0fe
在理解量子机器学习时遇到困难吗?
原文:
towardsdatascience.com/having-trouble-understanding-quantum-machine-learning-ce8a941d8c70
使用函数式编程实现量子近似优化算法
Frank Zickert | Quantum Machine Learning
·发表于Towards Data Science ·阅读时间 7 分钟·2023 年 3 月 2 日
–
想要开始量子机器学习吗?请查看动手量子机器学习与 Python。
本文将解释量子近似优化算法(QAOA)中最重要的部分。QAOA 是一种机器学习算法,你可以用来解决组合优化问题。
特别之处在于,这个算法迎合了量子计算机的特性——一种承诺在解决问题上提供指数级加速的新型计算机。
尽管量子机器学习(QML)——即使用量子计算来解决机器学习算法——是最有前景的技术之一,它也同样具有挑战性!
因此,本文旨在以一种易于理解的方式解释 QAOA 的基本概念。
量子计算、优化和机器学习都在很大程度上依赖于数学。除非你是数学家,否则这将是一个令人望而生畏的任务。
幸运的是,一些 QML 库,比如 IBM Qiskit,解决了这个问题。它们提供了易于使用的接口,并将所有复杂性隐藏在你之外。
正如我在我之前的帖子中所展示的,它们甚至处理了问题的公式化。
在那篇帖子中,我使用了量子近似优化算法(QAOA)来解决一个组合优化问题——如何应对野火。
图片由作者使用 Stable Diffusion 生成
你唯一需要做的就是指定问题的个体值。
这简直太好了,不是吗?
尽管这些库让你可以使用量子机器学习而不必担心数学、量子力学或其他复杂的东西,但它们同样不会教你太多。
如果你想了解一个算法是如何工作的,你就会回到最初的地方。如果 Qiskit 库如此出色,为什么不查看他们的示例来理解 QAOA 是如何工作的呢?
下图展示了他们示例的一部分。
摘自 Qiskit 文档
我认为没有什么需要添加的,是吗?
…
也许一点点。
所以,让我给你提供一个替代的解释。一个不需要数学学位的解释。但一个利用函数式编程(在 Python 中)的表达力的解释。
函数式编程的故事讲起来很快。
函数式编程将应用程序分解为一组函数。理想情况下,函数只接收输入并产生输出,并且没有影响给定输入产生的输出的内部状态。
从这个意义上讲,QAOA 算法是一个通过optimize
一组params
来解决problem
的函数。换句话说,我们的目标是找到这些params
的最佳值。
为了决定哪些params
最佳,我们根据从compute
得到的结果来assess
这些params
,该结果来自使用这些params
编码问题(problem_circuit
)及其解决方案(ansatz_circuit
)的(量子)circuit
。
这就是 Qiskit 描述中提到的变分算法。它使用一种经典优化算法来向量子计算机发出查询。
这就是代码。
def qaoa(
problem, optimize, assess, compute,
to_circuit, problem_circuit, ansatz_circuit):
return optimize(
lambda params: assess(
problem,
compute(to_circuit(
problem, params,
problem_circuit, ansatz_circuit
))
)
)
相当不错,不是吗?
让我们继续深入到最内层的函数to_circuit
。
from qiskit import QuantumCircuit
def to_circuit(problem, params, problem_circuit, ansatz_circuit):
cnt_qubits = problem.size
qc_qaoa = QuantumCircuit(cnt_qubits)
# initial_state
qc_qaoa.h(range(cnt_qubits))
# append problem circuit
qc_qaoa.append(problem_circuit(problem, params[0]), range(cnt_qubits))
# append ansatz circuit
qc_qaoa.append(ansatz_circuit(problem, params[1]), range(cnt_qubits))
qc_qaoa.measure_all()
return qc_qaoa
这个函数接收problem
和params
。我们使用problem
的大小来确定量子电路中量子比特(qubits)的数量。
一个量子比特是量子计算机中的基本计算单位。尽管其内部状态非常复杂,但当你查看它时,它要么是 0,要么是 1——就像普通的比特一样。
我们从对所有量子比特应用 Hadamard 门(h
)开始。这将量子比特置于一个状态,其中它们有相等的可能性产生 0 或 1。
然后,我们使用problem_circuit
和ansatz_circuit
函数附加两个子电路。这就是 Qiskit 解释中提到的*“单位矩阵 U(β,γ)具有特定的形式,并由两个单位矩阵 U(β)和 U(γ)组成……”*
第一个函数problem_circuit
添加了一个量子电路,表示我们想要解决的问题。
def problem_circuit(problem, gamma):
qc_p = QuantumCircuit(problem.size)
for i, j in problem.relations:
qc_p.rzz(gamma, i, j)
qc_p.barrier()
return qc_p
在这种情况下,我们遍历problem
中的所有relations
。显然,我们期望一个relation
由一对整数值(i, j
)组成。我们在这些位置的两个量子比特上应用rzz
门。rzz
门是一个参数化(由参数gamma
)的旋转门,绕着两个量子比特系统的 ZZ 轴旋转。
第二个函数ansatz_circuit
添加了一个量子电路,表示我们问题的解决方案。
def ansatz_circuit(problem, beta):
qc_a = QuantumCircuit(problem.size)
for i in range(problem.size):
qc_a.rx(beta, i)
return qc_a
这一次,我们遍历问题的所有部分,并在相应的量子比特上应用rx
门。这是一个以参数beta
为参数的绕 X 轴的旋转。
from qiskit import Aer, execute
def compute(circuit):
return execute(
circuit,
Aer.get_backend('qasm_simulator'),
shots=1000
).result().get_counts()
本质上,这些电路使用两个params
(称为beta
和gamma
)来创建一个量子电路,该电路产生 Qiskit 生动描述的特定量子态**|𝜓(𝛽,𝛾)⟩**。这里,𝜓(“psi”)是量子态的占位符。𝛽和𝛾是定义此状态的参数。
这个量子电路创建了一个状态,可能产生任何值,无论好坏。当然,我们希望生成有意义的结果。因此,我们需要一个“好坏”度量。这就是assess
函数的目的。
def assess(problem, result):
avg = 0
sum_count = 0
for solution, count in result.items():
performance = 0
for i, j in problem.relations:
if solution[i] != solution[j]:
performance -= 1
avg += performance * count
sum_count += count
return avg/sum_count
根据我们的problem
,我们计算从执行量子电路得到的结果的performance
。我们查看problem
定义中的relations
,并在量子比特表示的relation
不相等时(solution[i] != solution[j]
)降低(注意,这里较低为更好)性能。记住,量子比特的结果是 0 或 1。所以,solution[i]
和solution[j]
要么是 0,要么是 1。
现在,通过创建电路并评估其结果,我们可以输入一个经典优化算法。这个算法重复评估不同的值及其结果,并基于此向产生更好结果的值移动。
from scipy.optimize import minimize
def optimize(f_params_to_problem):
return minimize(
# callable function
f_params_to_problem,
# initial guess on beta and gamma
[1.0, 1.0],
# optimization method
method='COBYLA')
那么,让我们看看问题的结构。我们使用了它的两个特征:size
和relations
。因此,让我们创建一个class
来存储这些数据。
class Problem():
def __init__(self, nodes, relations):
self._nodes = nodes
self._relations = relations
@property
def size(self) -> int:
return len(self._nodes)
@property
def relations(self) -> int:
return self._relations
最后,我们需要制定我们问题的实例并将其输入到qaoa
算法中。
problem = Problem([0, 1, 2], [(0, 1), (1, 2)])
result = qaoa(
problem, optimize, assess, compute,
to_circuit, problem_circuit, ansatz_circuit
)
我们定义问题由三个节点(0, 1, 2
)和几个关系组成。节点0, 1
和1, 2
是连接的。以下列表表示输出。
fun: -1.632
maxcv: 0.0
message: 'Optimization terminated successfully.'
nfev: 32
status: 1
success: True
x: array([1.05618646, 2.28854173])
nfev
表示迭代次数。最重要的是,x
表示产生最佳结果的params
值。
要了解这些值的意义,我们将这些值反馈到电路中并观察结果。
from qiskit.visualization import plot_histogram
plot_histogram(compute(to_circuit(
problem, result.x, problem_circuit, ansatz_circuit
)))
作者提供的图片
输出显示两个解决方案出现得更频繁:010
和101
。因此,这些表示了指定问题的解决方案。
当我们回顾assess
函数时,我们看到如果两个连接的节点具有不同的值,我们将每个relation
的值定为-1
。此外,我们定义了0, 1
和1, 2
为连接的。
因此,最佳解决方案是那些连接节点具有不同值的方案。这些是010
和101
。
这个问题被称为 Max-Cut。这是 Qiskit 示例中解决的相同问题,可以被认为是组合优化的“Hello World”。
结论
本文解释了量子近似优化算法(QAOA)的基本部分。虽然这不是第一个也不是唯一的解释,但它不要求你先学习数学。
非数学性的解释有相当多的优点。
-
这对于我们大多数人来说要容易得多。
-
这是一门实践课程。我们直接用它解决了一个问题。
-
你可以看到算法的各个部分如何配合在一起。
想要开始量子机器学习吗?看看用 Python 实践量子机器学习。
免费获取前三章 点击这里。
全新第 2 卷:组合优化完全讲解了如何使用变分量子本征求解器和量子近似优化算法来解决优化问题。
浓缩咖啡中的 Headspace:优化
咖啡数据科学
另一个隔离 headspace 的实验
·
关注 发表在 Towards Data Science ·3 分钟阅读·2023 年 4 月 21 日
–
Headspace 是指在制作浓缩咖啡时,咖啡床顶部与喷头屏幕之间的空间量。它是与研磨度、剂量和压实压力互动的一个变量。更具体地说,headspace 是指咖啡饼在萃取过程中膨胀的空间,这会影响水和气体在咖啡中的流动。
优化 headspace 的挑战在于它与研磨度、剂量和压实压力相互交织。我通常发现较少的 headspace 是最优的,但我希望通过更好的实验来隔离变量。
进入用过的咖啡
我决定在新鲜咖啡上添加用过的咖啡,这样它在膨胀和水流方面的作用类似于咖啡,但不会干扰萃取。
我在 Decent Espresso 机器上使用了萨拉米测量萃取的射击。萨拉米射击可以用来测量萃取,并更好地理解不同的口味成分何时萃取到杯中。
我也使用了距离烘焙后 3 个月的咖啡,这是我最近一些测试的实验咖啡。目的是使用已经脱气的咖啡,在更理想的环境中专注于 TDS 和 EY 作为数据指标。
总溶解固体(TDS)是使用折射计测量的,结合杯中的输出重量和咖啡的输入重量,用于确定萃取到杯中的咖啡的百分比,称为萃取收率(EY)。
小头部空间 vs 大头部空间
我使用的第一个测试集是 Blooming Ramp 轮廓,该轮廓在达到一定压力(4 bar)后开始绽放 30 秒,然后以 2.2 ml/s 注入。然而,这个轮廓对于较大的空间效果不佳,因为在压力建立起来时,目标输出已经达到。
所有图片由作者提供
所以我认为结果并不公平,因为更大的空间(使用了 3 克已用咖啡)是因为基准没有一个花期。
平面轮廓
我重新使用了平坦的 2 ml/s 轮廓进行了实验。这控制了花期,并允许我们只关注头部空间。这表明较小的头部空间更有利于压力的建立。
这些萃取收率的结果更加清晰。
空间是一个易于改变且不需要任何特殊工具的变量。我希望这个小研究能激发人们尝试减少空间,因为数据表明较少的空间是最佳的参数选择。
如果你喜欢,可以在Twitter,YouTube,和Instagram关注我,我在那里发布不同机器的浓缩咖啡视频和相关内容。你也可以在LinkedIn找到我。你还可以在Medium上关注我并订阅。
我更多的阅读:
医疗数据本质上是有偏的
原文:
towardsdatascience.com/healthcare-is-inherently-biased-b60bf00d4af7
这里是如何避免被它欺骗
·发表于Towards Data Science ·10 分钟阅读·2023 年 1 月 19 日
–
通常,当你想到偏差时,你可能会想到一个人的信念如何无意中塑造他们的假设和方法。这当然是一个定义。
但偏差也指的是我们用来获得洞察的数据本身可能被无意中扭曲和不完整,扭曲了我们观察一切的视角。
**许多——如果不是大多数——**我们在医疗领域使用的数据集本质上是有偏的,如果我们不小心,很容易使我们偏离正确的方向。
我将特别关注三个主要概念,这些概念困扰医疗数据,并且可能会低调地使你的整个分析失效。
由Kaleb Nimz拍摄,发布在Unsplash
在早前的文章中,我写到了我在医疗数据分析职业生涯中遇到的各种偏差类型,以及它们如何成为数据从业者面临的挑战。对我来说,最大的主题是:个人偏差、数据偏差和确认偏差。
在我发布那篇文章的四天后,一位导师给我发来了这篇关于医疗索赔数据中抽样偏差的 JAMA 文章,以及它如何在某些地区因社会健康决定因素(SDOH)而加剧。
文章的结论确实引起了我的注意:
[研究强调]了调查大型医疗索赔数据的抽样异质性的重要性,以评估抽样偏差如何影响结果的准确性和普遍性。
重要的是,调查这些偏差或准确地重新加权数据将需要来自索赔数据库本身之外的外部数据源。
TLDR:
在大型医疗保健数据集中,患者或成员的失衡可能使您的结果无法概括,甚至(在某些情况下)完全无效。
社会决定健康因素数据可能会有所帮助。
让我们来解开这个谜题。
但首先,引用杰出的 Cassie Kozyrkov 的话:“AI 偏差问题开始于——但并不止于——定义。‘偏差’是一个在不同背景下含义截然不同的过载术语。” 进一步阅读她的文章和相关的其他文章:什么是偏差?
作为医疗保健分析师,我们需要注意:
1. 抽样/选择偏差。
抽样偏差 是 当某些成员比其他成员更有可能被选入样本时。 选择偏差 可以通过选择[研究] 人群 的方式、抽样方法 或参与者的招募(即,选择包含内容的设计缺陷)而引入。[1]*
我使用这些术语有些口语化。在传统调查/样本/研究设计中,抽样/选择偏差的定义是有意义的。
但是……第一个警告:在数据中,“人口”有着非常特定的含义,在医疗保健中也是如此,虽然定义相似(但不同),它们可能相同,也可能不同,这取决于具体情况。
在医疗保健的背景下,它们可以指如何构建我们的分析和我们的包含/排除,以了解某个特定的“人口”。在医疗保健中,我们使用关注的人群来指代根据研究的不同而涉及的多个兴趣组,例如:
-
业务线(LOB),如由政府支付者(医疗补助、医疗保险)覆盖的患者;商业保险线(雇主团体、零售或通过交换购买的保险);自保(自筹资金团体,通常是大型雇主支付员工的医疗索赔)等。
-
子业务线或团体。例如,医疗补助可能包括多个子组,代表不同的资格、覆盖、福利或人群/他们为何符合该计划的类型(临时援助计划 (TANF) 与医疗补助扩展计划)
-
人口统计(特定群体、男性或女性、某些地区等)
-
条件(检查某些慢性病、关注的主要疾病状态或对系统成本最高的疾病、医疗保险关注的疾病等)
-
上述某些组合的子组,例如:医疗补助、临时援助、母亲与新生儿
-
对上述某些组合的跨组比较,例如:Medicaid、TANF、新妈妈及其成本/利用/结果趋势 vs. 商业、自保、新妈妈及其成本/利用/结果
-
上述任何子组合或交叉组合,加上更多内容
正如你所见,分析设计和“相关人群”可能会迅速变得复杂。这并不一定意味着数据不可用或结果总是会令人不安。它确实重申了,意识到这种偏差对于确保你从各个角度考虑和进行相应分析至关重要。
标记 #2:如果医疗行业的人说数据包含了所有感兴趣的群体……这真的是真的吗?也许……
对健康保险公司的一点警示: 一个常见的讨论点是,健康保险公司可以分析他们的“整个群体”,因为他们接收了他们负责的每个成员的索赔,因此可以避免抽样偏差。根据使用案例,这可能潜在地是一个合理的讨论点。但总体来说,我会提醒你要记住,即使这些数据本身也具有固有的偏差,因为它:a) 只包括那些实际发生了事件/事故并且保险公司处理了后续索赔的成员/患者,b) 数据本身往往会过度/不足地代表那些更容易有慢性健康问题、 adverse social determinants of health、寻求医疗、反映你的组织倾向于服务的类型人群或你拥有更大业务量的群体等。更多内容请参见下面的文章总结。
2. 覆盖不足偏差
覆盖不足偏差 发生在你的样本中排除了部分人群时。 [1]
再次强调,这一定义在传统的调查/抽样/研究设计中是合理的。在医疗背景下,如果我们不小心,它可能会以实际的方式影响我们(浪费金钱、浪费精力、羞耻、没有影响或改变结果,或…… 以上所有)。
除了上述最纯粹的定义外,我还考虑到这种情况,即看不到“你四面墙之外发生的任何事情。”我们(通常)只拥有我们组织生成的数据,这本质上只是整个情况的一部分。根据你所做的分析类型和原因,这可能不是决定性问题,但这是另一个需要注意的重要标记。我们的[患者/成员/员工/居民/等]可能不会以相似的方式行动,甚至不能代表[所有患者/其他群体/其他类型的雇主/其他地区/等]
标记 #3:你的数据仅显示了你的组织内部的行为,因此不能总是用来推断你的竞争对手的真实情况、你的社区的真实情况、患者在访问不同的医疗提供者(不是你)时的情况,或者任何一个健康计划成员的行为是否与另一个成员的行为相似,这些都可能受到个人、地区、社会、职业或行为差异(通常我们没有的数据)的影响。所有这些都必须在你寻求结论时予以考虑。
随着更多的医疗保健组织将焦点转向更全面地改善其服务的社区(而不是仅关注具体的患者),我们可能会再次错过大量的数据拼图——这些数据告诉我们“我们四面墙外发生了什么。”
尽管如此,我们的一些分析可能并不完全依赖于了解这些情况。可以通过用来自我们四面墙外的外部数据来增强我们自己的内部数据,从而填补更多的图景来解决这些问题。我们中的一些人已经认识到这一点,并正在进行数据共享/合作,或利用额外的数据源,如健康信息交换(HIE)或购买的基准数据,以比较我们的人群,以便理解我们的情况有何不同或行为如何。请记住,这些数据集可能在更广泛的范围内存在相同的偏见(实际上可以参见那篇JAMA 文章),但所有这些都是朝着至少理解和识别任何潜在“陷阱”的良好第一步。
3. 历史偏见或系统性偏见。
历史偏见发生在社会文化偏见和信仰反映到系统化的过程中[这些偏见随后会反映在数据中]。[3] 系统性偏见来源于那些以不利于某些群体的方式运作的机构。[2]
当来自历史上有偏见的来源的数据用于数据科学模型或分析时,这就变得特别具有挑战性。尤其是在你分析历史上存在缺陷的系统时,比如医疗保健系统,这一点尤其重要。
这是一个非常热门的话题,以至于 NIST 正在开发一个人工智能风险管理框架。NIST 在他们的特别出版物中讨论了人类和系统偏见,朝向人工智能中的偏见识别与管理标准,这张图片来源于此:
图片来源:N. Hanacek/NIST(公有领域)。
在医疗保健领域,某些疾病(或者一般的病人,因为他们是医疗保健的主要消费群体)、人口统计学特征、某个群体或子群体、使用模式(或缺乏使用)、健康/质量/死亡率/参与度/满意度以及我们在医疗数据中看到的许多其他趋势或结果都反映了破碎的医疗保健系统的运作方式。这是一个高度复杂的话题,具有许多不同的方面,将在后续文章中深入探讨。但可以说:
标志#4:医疗保健系统已经破碎,现有的社会结构极大地影响了健康和结果、机会(或缺乏机会)、障碍和行为。这在医疗数据中以多种不同方式不可否认地反映出来。
提到健康公平倡议。
SDOH 数据如何提供帮助?
这部分并不是解决我所概述的所有问题的灵丹妙药,但我认为它足够有趣,值得特别指出,因为我们最近在健康分析中越来越多地讨论健康公平和 SDOH。
JAMA 文章的作者试图理解大型商业数据库中潜在的偏见,这些数据库由多个商业保险公司汇总的索赔数据组成。这些类型的数据集通常被组织用于临床研究、竞争情报,甚至基准测试。作者分析了一个常见的数据集——Optum Clinformatics 数据市场 (CDM)——该数据集来源于几个大型商业和医疗保险优势计划,并商业授权用于各种用途。
在邮政编码级别,作者分析了 CDM 中个人的代表性,与普查估计和这些邮政编码的 SDOH 变异性进行比较。我会指出,他们使用的数据集来自 2018 年;这是你能找到的大多数医疗数据的另一个主要问题,但另当别论。
文章发现:
[即使在调整了州级变异后,我们的高级统计方法] 发现,CDM 中的纳入与拥有更富裕、更年长、更受教育且过度白人居民的邮政编码相关联。
与索赔数据中过度代表相关的社会经济和人口统计特征也已被证明是在广泛的健康结果中起作用的修饰因子。
重要的是,调查这些偏见或准确地重新加权数据将需要来自索赔数据库以外的外部数据来源。
TLDR(再说一遍):
索赔数据在代表更受教育、更富裕和更多白人的患者方面存在严重偏差。
影响邮政编码级别(及子邮政编码)人群的具体 SDOH 因素对健康结果的差异有着明确的影响。
重新加权/标准化数据需要除索赔之外的额外数据。
有些人可能会称那些数据为“在你的四面墙之外”。
那么,现在怎么办?
你的第一个目标:意识。
我提出这些想法仅作为指导,帮助你开始思考潜在的“陷阱”。首先问自己和他人问题。在设计下一个分析时开始思考它。确保你完全理解结果将如何以及在哪里使用、意图是什么,并提前列出风险。开始理解不同类型的偏差及其可能对你的影响。如果你想了解 29 种其他类型的偏差,进一步细分为更多子类型,请查看这篇有用的Scribbr 上的研究偏差知识库文章。
你的第二个目标:不要让这导致分析瘫痪。
虽然保持这些在心中很重要,但更重要的是我们不要被这些困扰到导致分析瘫痪。作为分析师,了解细微差别并决定在沙滩上划出那条线无疑是你工作的最难部分:决定哪些见解仍然有价值或有意义尽管存在这些细微差别,以及何时倾向于(或远离?)结论的可靠性,将对你的成功和组织的成功至关重要。
最有可能的是,结果可能会落在中间,所以你的目标是理解细微差别,清晰地传达它们,并在尽可能的范围内提供适当的指导,同时有效防范潜在的误解。
这篇文章突出了一个子集的偏差,这些偏差并非医疗保健所独有,但在医疗保健中应用的方式非常独特。
在你的医疗保健或分析职业中,你遇到过哪些其他类型的偏差?
Stefany Goradia是RS21健康实验室的副总裁,该公司是一家数据科学公司,拥有一个专注于医疗保健+社区的健康实验室。
她在医疗保健分析的前线工作,致力于为内部和外部客户提供价值。她撰写关于如何解读医疗保健数据、与利益相关者沟通,并利用数据支持知情决策和执行的文章。
喜欢健康数据和医疗保健分析?
喜欢我的风格?
了解更多关于我的背景以及对数据的热情,请访问stefanygoradia.bio。
其他一些关于偏差的有用参考文章:
研究偏差来源于任何偏离真相的情况,导致结果失真和错误结论。偏差可能会…
## [2] NIST 报告强调:人工智能偏见不仅仅是偏见数据
作为提升我们识别和管理人工智能中偏见有害影响能力的一个步骤……
你正试图做出一个好的决定,并决定查看你的数据来帮助你做出判断。你已经有了……
## 8 种可能毁坏你机器学习模型的数据偏见类型 - Statice
偏见数据:你很可能对这个术语很熟悉。也许你自己就是对使用存在偏见的数据非常怀疑的人……
大多数人缺乏的心理习惯,以及为什么你不能期望使用数据有效地指导你的行动……
在疫情期间你的大脑如何干扰你以及你可以采取的应对措施
“无论许多人是否意识到,偏见都是人类思想和经验的普遍方面。然而,曾经的……
排序算法:堆排序
了解堆数据结构及其在排序中的应用
·
关注 发表在 Towards Data Science · 7 分钟阅读 · 2023 年 1 月 3 日
–
介绍
堆是一种数据结构,表示以基于二叉树的格式组织的数组。堆对其结构施加了以下规则:
-
完整性。堆的每一层都被完全填满。然而,最后一层可能从左侧开始部分填满。
-
堆规则。任何父节点的值必须小于或等于其子节点的值。如果满足这一属性,则堆称为最小堆。还有一种最大堆变体,其中父节点的值必须大于子节点的值。
本文中的示例和代码将针对最小堆提供。最大堆的算法工作流程非常类似。下面展示了一个最小堆的示例。
堆通常以数组的形式存储。如果一个父节点的索引是 i,那么其左子节点和右子节点的位置分别是 2 * i + 1 和 2 * i + 2。反之,如果一个非根节点的索引是 i*,那么其父节点的索引是* (i - 1) // 2*。遵循这一原则,我们得到上述堆的数组表示:
操作
堆支持多种操作:
-
插入一个节点
-
从数组构建堆
-
提取具有最小值的节点
-
排序
由于堆数据结构具有多个操作,实际中将其实现为一个类更为方便。目前,我们将实现其基础部分。每次操作后,将提供相应的代码片段。
class Heap:
def _swap(self, i, j):
self.heap[i], self.heap[j] = self.heap[j], self.heap[i]
def _number_of_children(self, i):
if 2 * i + 2 < len(self.heap):
return 2
if 2 * i + 1 < len(self.heap):
return 1
return 0
-
heap 字段以堆的形式存储输入数组(稍后将实现)
-
_swap() 方法接受两个数组索引并交换其值。
-
number_of_children() 方法返回节点的子节点数量(0、1 或 2)
插入一个节点
新元素插入堆的最后位置。如果插入的元素低于父节点的值,可能会破坏堆规则。为避免此问题,新节点会递归向上传播,直到不违反堆规则为止。上述过程称为堆化(up)。
从上图中,我们插入了一个值为 3 的节点。
-
插入后,堆规则被破坏,因为 3 < 15(父节点)。我们交换元素 3 和 15。
-
现在节点 3 有了一个新父节点,值为 7。再次,堆规则未得到满足,因为 3 < 7。结果,我们交换了 3 和 7。
-
节点 3 位于索引 2,其父节点值为 1。由于 3 ≥ 1,堆规则是正确的。在这一阶段,插入过程结束。
让我们来探讨插入的时间复杂度。最坏的情况是需要将新节点从树的底部传播到顶部。由于任何树的高度与其元素总数 N 的对数关系成正比,而每次比较花费 O(1) 时间,最终估计结果为 O(logN) 时间。
class Heap:
def insert(self, value):
self.heap.append(value)
self._heapify_up(len(self.heap) - 1)
def _heapify_up(self, i):
if i != 0:
parent_index = (i - 1) // 2
if self.heap[i] < self.heap[parent_index]:
self.swap(i, parent_index)
self._heapify_up(parent_index)
# ... #
-
insert() 方法将值附加到堆中,然后调用堆化方法。
-
_heapify_up() 方法递归调用自身,直到堆规则正确。
构建堆
对于输入数组的每个元素,调用插入过程。这就是构建堆的方法。
说到复杂度,可能会觉得构建一个堆需要 O(N * logN) 的时间,因为对于每个 N 元素,我们调用一个耗时 O(logN) 的函数。然而,可以改进这个估计,并通过数学证明总时间是 O(N)。
class Heap:
def build(self, array):
self.heap = []
for value in array:
self.insert(value)
# ... #
- 对于传递给 build() 方法的数组,通过插入调用构建堆。
提取具有最小值的节点
最小节点位于堆的顶部。我们提取最小值,并用堆的最后一个节点替换顶部节点。由于违反了堆规则,我们将这个元素向下传播。该算法与我们上面使用的插入元素时相似(插入时元素是向上传播的):在每一步中,我们将当前元素与具有最小值的子节点交换。此过程持续到堆规则不再被破坏或当前元素没有子节点为止。
在上图中,值为 1 的节点被提取,值为 15 的最后一个节点取代了它的位置。
-
由于节点 15 违反了堆规则,我们将其与其最小的子节点 3 交换。
-
然后节点 15 有子节点 7 和 8,它们都较小。我们再次将 15 与最小的子节点 7 交换。
-
之后,15 位于索引 5,并且只有一个子节点 20。由于 15 ≤ 20,我们停止堆化过程。
类似于插入部分的堆化算法,该算法具有相同的渐进复杂度,并在 O(logN) 时间内进行。
class Heap:
def extract_min(self):
self._swap(0, len(self.heap) - 1)
min_element = self.heap.pop()
self._heapify_down(0)
return min_element
def _heapify_down(self, i):
if self._number_of_children(i) == 2:
if self.heap[i] > self.heap[2 * i + 1] or self.heap[i] > self.heap[2 * i + 2]:
if self.heap[2 * i + 1] < self.heap[2 * i + 2]:
self._swap(i, 2 * i + 1)
self._heapify_down(2 * i + 1)
else:
self._swap(i, 2 * i + 2)
self._heapify_down(2 * i + 2)
elif self._number_of_children(i) == 1 and self.heap[i] > self.heap[2 * i + 1]:
self._swap(i, 2 * i + 1)
self._heapify_down(2 * i + 1)
# ... #
排序
排序是通过提取最小节点来实现的。当堆不为空时,我们调用 extract_min() 函数,并将每个最小元素附加到新数组中。这样,数组将由排序的元素组成。
由于堆包含 N 个节点,并且 extract_min() 的时间复杂度为 O(logN),因此总排序时间为 O(N * logN)。
class Heap:
def sort(self):
array = []
while len(self.heap) > 0:
array.append(self.extract_min())
return array
# ... #
结论
我们已经覆盖了堆的所有四个主要操作。要使用堆数据结构对数组进行排序,必须首先构建堆,然后调用排序方法。构建堆需要 O(N) 时间,而排序需要 O(N * logN) *时间,这最终导致堆排序的 O(N * logN)*渐进复杂度。
堆类的完整实现如下所示。
class Heap:
def __init__(self, array):
self.build(array)
def build(self, array):
self.heap = []
for value in array:
self.insert(value)
def insert(self, value):
self.heap.append(value)
self._heapify_up(len(self.heap) - 1)
def sort(self):
array = []
while len(self.heap) > 0:
array.append(self.extract_min())
return array
def extract_min(self):
self._swap(0, len(self.heap) - 1)
min_element = self.heap.pop()
self._heapify_down(0)
return min_element
def _swap(self, i, j):
self.heap[i], self.heap[j] = self.heap[j], self.heap[i]
def _number_of_children(self, i):
if 2 * i + 2 < len(self.heap):
return 2
if 2 * i + 1 < len(self.heap):
return 1
return 0
def _heapify_down(self, i):
if self._number_of_children(i) == 2:
if self.heap[i] > self.heap[2 * i + 1] or self.heap[i] > self.heap[2 * i + 2]:
if self.heap[2 * i + 1] < self.heap[2 * i + 2]:
self._swap(i, 2 * i + 1)
self._heapify_down(2 * i + 1)
else:
self._swap(i, 2 * i + 2)
self._heapify_down(2 * i + 2)
elif self._number_of_children(i) == 1 and self.heap[i] > self.heap[2 * i + 1]:
self._swap(i, 2 * i + 1)
self._heapify_down(2 * i + 1)
def _heapify_up(self, i):
if i != 0:
parent_index = (i - 1) // 2
if self.heap[i] < self.heap[parent_index]:
self._swap(i, parent_index)
self._heapify_up(parent_index)
执行示例:
array = [14, 8, 1, 12, 6, 20, 7, 10, 18, 4, 5, 15]
heap = Heap(array)
sorted_array = heap.sort()
print(sorted_array) # [1, 4, 5, 6, 7, 8, 10, 12, 14, 15, 18, 20]
除非另有说明,所有图片均由作者提供
这是我使用 Apache Airflow 6 年学到的东西
原文:
towardsdatascience.com/here-is-what-i-learned-using-apache-airflow-over-6-years-15d88b9922d9
从实验到生产无忧的 Apache Airflow 之旅
·发表于Towards Data Science ·8 分钟阅读·2023 年 1 月 9 日
–
照片由Karsten Würth拍摄,来源于Unsplash
Apache Airflow 无疑是多年来最受欢迎的数据工程开源项目。它在与数据工程师崛起的关键时刻获得了流行,其核心理念是将代码作为一等公民,而不是数据管道(即 ETL)的拖放,这标志着一个里程碑。Apache Airflow 于 2016 年 3 月成为 Apache 孵化器项目,并于 2019 年 1 月成为顶级项目。我从 2017 年起作为用户使用 Apache Airflow。在此过程中,我也为 Apache Airflow 做出了贡献。今天,我想分享我与 Airflow 的旅程以及我在 6 年中学到的东西。
什么是 Airflow
Airflow 是由社区创建的平台,用于以编程方式创建、调度和监控工作流。 — Airflow 官方文档
Apache Airflow 由Maxime Beauchemin开发,他曾在 Airbnb 和 Facebook 工作。他在 Airbnb 开发了 Airflow(可以从项目名称中看出)。然而,激发他的核心想法是 Facebook 使用的内部工具。
Apache Airflow 的主要用户是数据工程师或需要调度工作流的工程师,主要是 ETL(提取、转换、加载)作业。这些 ETL 作业通常按日或按小时运行。作业本身执行数据操作,以从未结构化的原始数据中获取见解。
为什么 Airflow 如此受欢迎?
自 2017 年以来,我一直在使用 Apache Airflow,在它成为任何数据工程师必备技能之前。我仍然记得早期我们“黑客”式地将大量努力投入到 Airflow 调度程序系统中,以使其稳定运行的经历。如果你想了解更多,我几年前写了一篇文章。
[## Meetup 的数据管道基础设施,减少噩梦:在 Kubernetes 上运行 Apache Airflow
Meetup 数据平台基础设施——一个起点
medium.com](https://medium.com/making-meetup/data-pipeline-infrastructure-at-meetup-with-fewer-nightmares-running-apache-airflow-on-kubernetes-54cb8cdc69c3?source=post_page-----15d88b9922d9--------------------------------)
从一开始,Airflow 就以一个孵化项目的身份闪耀。它在不断发展和稳定。以下是我记录的几个让 Airflow 一开始就如此迷人的原因:
首先编写代码
编码(尤其是 Python)已成为数据工程师的新常态。然而,10 年前编写数据管道主要是通过像 SSIS 或 Informatica 这样的 UI 工具拖放完成的。拖放一开始很简单。然而,它很快会遇到开发周期难以扩展的情况,UI 有时也会变得更加棘手,并且拖放框时可能会出现问题。
编码是一种很好的抽象方式。我们可以用 Python 代码编写包含所有逻辑的有向无环图(DAG),分享并部署它。这很好地达到了目的。它迫使一些仅了解 SQL 的人学习 Python,但这是值得的。
良好的可视化
无论我们编写多少代码,作为一个协调者,它需要某种方式来可视化过程。最终,良好的可视化看起来既吸引人又解决了多个问题。它可以有效地捕捉故障、理解依赖关系和监控作业状态。
作为最终用户,可视化并不像仪表板那样需要打磨。一旦编码完成,Airflow 会处理剩余部分并为你提供可视化。你的 DAG 可以以七种不同的视图进行可视化,每种视图都有其重点。我每天使用的最常见视图是 Grid view(曾经是树形视图)和 Graph view。
在 UI 上,你可以执行诸如重启作业、重置 DAG 中某些任务的状态、检查失败日志或检查长期运行的作业等任务。虽然 UI 并没有提供 100% 的功能,但它覆盖了日常使用的场景。
扩展性
为了让更多人将其用作 ETL 编排工具,该项目必须具有可扩展性,以便其他项目能够轻松集成。集成的丰富性为 Airflow 成为顶级 Apache 项目之一奠定了基础。此外,Airflow 允许用户编写自己的 PythonOperator
,这进一步鼓励开发者通过代码构建自己的逻辑,而不是等待插件的新升级来满足他们的 ETL 需求。
出色的可扩展性还促进了更多创新项目和插件在 Airflow 生态系统中入驻。使用插件的核心思想是避免每个人都重新发明轮子来编写自己的逻辑。它还帮助服务或产品与最终用户无缝集成。
完整的 Airflow 插件使最终用户迁移到其他工具变得更难。例如,EDI 835:电子汇款通知是一种在医疗保健行业提供索赔支付信息的特殊格式。它需要特殊的解析逻辑才能正确读取。这些特定领域的用例使 Airflow 形成了一个很好的“护城河”,从而具备竞争力。
社区
最终,Airflow 是 Apache 基金会的一部分。我们都知道成为 Apache 顶级项目意味着什么。名声本身就能吸引更多的人尝试 Airflow。
社区正在健康增长。在 StackOverflow 上,你可以找到超过 9k 个标记为 [Airflow] 的问题。Airflow 每年都会举办 AirflowSummit。支持 Airflow 的公司 astronomer.io 也提供了出色的文档和认证。自从我在 2017 年加入以来,其 Slack 群组一直很活跃,现在已有 25k+ 人加入。
我使用 Airflow 的旅程
当我加入 Meetup 时,Airflow 是由一位首席工程师在 2017 年 4 月作为概念验证带来的实验工具。
当时引入 Airflow 的目的是解决我们使用自定义 Scala 基于 ETL 时遇到的多个问题。我仍然欣赏那些建立复杂 DAG 逻辑依赖关系来使 ETL 首先正常工作的人员。但我们都知道 ETL 有时会失败。这就是自定义 Scala 基于 ETL 的头疼之处。不容易调试和重新启动作业,并且你需要非常小心以确保作业是幂等的。
最终,我们找到了一种运行 Airflow 1.7 的方法,并部署了新的 Python 代码用于 POC。结果非常好。自从引入了 Airflow 1.9 和 Kubernetes 执行器,Airflow 已经处于一个更加成熟的阶段。我还为 Airflow 项目做出了贡献,并获得了对内部核心代码的深入了解。在生产环境中,我们遇到的问题比早期要少得多,我们可以更多地关注于提高可扩展性和增加更多用例。
不过,还有一些事情我希望我能早些知道,以避免一些耗时的调查。
学到的经验
一开始没有利用宏
你没看错。我在 Airflow 的早期阶段不知道宏的概念。因此,像 {{ ds }}
这样的 Jinja 模板我一开始并没有利用。在新的一天中,为了触发 ETL,它只是使用了 Python 函数 date()-1
。
最大的问题是回填,我使用了 Airflow 的参数进行处理,但用户必须提供这些信息,并且只能逐日回填。整个过程变成了一个不容小觑的过程。在仔细阅读 Airflow 文档后,发现宏非常适合获取作业的元数据。我们将回填过程简化为清除现有的 dag,而无需额外的参数修改。
ETL 在 EST
日期在任何数据系统中都是关键的。我们的数据系统按 EST 分区。是 EST!!! 如果你和我处于同样的情况,你会理解在日光节约时间更改期间我经历了多么痛苦。一切都崩溃了!
我们有多个 DAG 依赖于 external_task_sensor,它依赖于 execution_delta — 与之前执行的时间差来查看。想象一下,你有一些任务完成了,然后突然待处理的任务 +1/-1 小时变得混乱不堪。我必须构建一个解决方案来避免这种情况。这不是一个有趣的维护工作。如果你开始构建任何数据系统,我建议尽可能使用 UTC 时间,以简化工作和保持内心的平静。
在调度 DAG 时要温和。
Airflow 不是数据流处理解决方案。 这在 Airflow 官方文档的 Beyond The Horizon 部分开头就提到过。
一个原因是 Airflow 不会在任务之间传递数据。遇到这种情况时,你需要寻找外部存储。Airflow 的架构并没有以流式设计开始。如果你有一个需要每分钟触发的作业,Airflow 可以完成这个任务,但它的扩展性不好。对于数据流处理的用例,使用 Flink/Spark 进行数据流处理,不要把这个负担放到 Airflow 上。
另一个不常提及的原因是,Airflow 调度器在更新所有 DAG 状态时非常繁重。当 Airflow 调度器触及实时时钟时,它会扫描下一个潜在的 DAG 以触发其执行日期。即使 Airflow 调度器宕机,状态也必须被实际化以跟踪状态。大规模的状态更新在 OLTP 数据库中进行,Airflow 仍建议使用 MySQL 或 Postgres。 在这种情况下,scheduler_heartbeat_sec 需要正确配置,默认值为 5 秒。这个值可以作为调节的关键。
当我们面临数百个 DAG 和数千个任务时,作业逐渐被安排和运行。将此值增加到 60 秒以上会使 Airflow 调度器冷却。为了更好地了解 Airflow 调度器,我写了一篇文章 Airflow Schedule Interval 101 帮助你深入学习。
## Airflow Schedule Interval 101
气流调度间隔可能是一个具有挑战性的概念,即使对于正在使用 Airflow 的开发人员也是如此……
towardsdatascience.com
最终想法
Apache Airflow 是一个了不起的开源项目,专为数据工程师设计。回顾过去,许多概念是主导实施的基础,这些实施为其今天的流行和成功铺平了道路。许多公司将 Airflow 作为数据工程师招聘的必备技能。对我来说,早期参与 Apache Airflow 并见证项目的成长是一种幸运。一些新项目已开始挑战 Apache Airflow,例如 mage-ai 和 Perfect。我将在未来的帖子中介绍它们。
Airflow 的创建者,Maxime,也是 Apache Superset 的创建者,是这两个项目中最令人钦佩的数据工程师之一。以下是他在 2015 年关于 Airflow 最佳实践的演讲。这仍然是今天使用 Airflow 和了解 Airflow 设计理念的一个很好的参考。
与 Airflow 的最佳实践 - 一个开源的工作流和调度平台 | 作者 Maxime Beauchemin
我希望这个故事对你有帮助。本文是我工程与数据科学故事系列的一部分,目前包括以下内容:
数据工程与数据科学故事
查看列表 53 个故事
你还可以 订阅我的新文章 或成为 推荐的 Medium 会员,获取对 Medium 上所有故事的无限访问权限。
如果有任何问题/评论,请随时在此故事的评论区留言,或通过 Linkedin 或 Twitter 直接联系我。
我的数据科学工作流程模板
原文:
towardsdatascience.com/heres-my-data-science-workflow-template-caa83c1e3032
一份专注于数据科学方面的快速指南
·发布于 Towards Data Science ·阅读时间 6 分钟·2023 年 2 月 22 日
–
Campaign Creators 在 Unsplash 拍摄的照片 [1]。
目录
-
介绍
-
数据探索、数据汇总和特征工程
-
算法比较
-
摘要
-
参考文献
介绍
虽然我已经写了涵盖整个数据科学过程的文章,包括业务理解和利益相关者协作等方面,但我想专注于数据科学部分。在这篇文章中,我将提供一个供数据科学家使用和构建的模板。我假设更多的中高级数据科学家已经遵循这个通用模式,所以如果你在数据科学方面较为初级、刚刚入门或对数据科学感兴趣,这篇文章适合你。话虽如此,让我们更深入地探讨数据科学过程的主要部分,包括数据探索、数据汇总、特征工程和算法比较的步骤。
数据探索、数据汇总和特征工程
Jason Briscoe 在 Unsplash 拍摄的照片 [2]。
我们要记住,不同的公司和角色有所不同,因此这个模板可能需要根据你的情况进行更新。下面描述的步骤是当你已经定义了业务问题并与利益相关者会面后进行的步骤。根据你的情况,你也可能需要负责数据工程和数据分析的其他部分。
数据探索
-
定义你的初始数据集 → 你可能在训练模型之前已经对需要的数据有了假设,因此会从这些特征开始,并进一步迭代。
-
定义该数据集的来源 → 是来自当前的公司数据库表,还是一次性的 Google Sheet/Microsoft Excel 表格,或是第三方数据平台等?
-
数据目前是否存在? → 如果存在,则继续前进。如果不存在,则你可能需要与数据工程/数据分析等合作,以提供模型所需数据的准确来源。
— 你也可能需要向数据工程师提供理由,例如,请求特定数据点。例如:我希望得到按邮政编码分组的人口数据 — 先自行进行分组,并提供这种分组如何影响准确性以及最终业务问题的理由,以验证数据工程的时间。
数据汇总与特征工程
-
你的数据是否已经正确汇总? → 如果是,则继续前进。如果不是,则需要对数据进行汇总并以该格式存储。例如,如果你想预测县级人口,可能需要将以州(地理分组)为基础的训练数据行进行汇总。你可能能够在没有数据工程帮助的情况下进行数据存储,并使用如 Python 编程语言中的 pandas 等工具进行一些分组技术。
-
你是否想创建新的模型特征? → 如果是,则继续前进。如果不是,则需要通过相关性、自动特征选择、SHAP 特征重要性等工具,识别和测试更多可以解释你预测变异性的特征。
反思与可能的惊喜:
本节的关键点是,你可能在开始数据科学项目时并没有预期的那么多相关数据/特征。话虽如此,开始初步数据探索时,要对这种情况保持警惕。
算法比较
图片由Liz Sanchez-Vegas拍摄,发布在Unsplash [3]。
需要记住的一点是,最后一节的最后一步,特征工程,可以与算法比较的顺序来回切换,因为你可能会发现不同的特征对于某些算法更有用或更易于使用。
现在你已经有了你的特征(或至少是一个起点),你会想选择一个特定的算法来构建最终模型。
算法比较
-
你的目标是一个连续的数字还是一个类别? → 如果答案是一个连续的数字,那么你会比较回归算法;如果是一个类别,那么你会比较分类算法。
-
你选择什么算法? → 开始时最好选择尽可能多的你了解的算法,然后再从中筛选。比较算法有很多方法,例如分别运行每一个,或者创建一个循环比较你选择的所有算法,但我发现最简单的方法是使用一个已经能并排比较算法的库,如Moez Ali的 PyCaret 库——以下链接展示了极其有价值的 compare_models()函数:
一旦设置成功执行,它会打印出包含若干重要部分的信息网格……
- 比较不同算法时我应该关注什么? → 根据你的用例,你可以比较一些因素,但通常你会关注损失函数,比如 MAE 或 RMSE,例如,对于回归目标。你还可以查看训练所需的时间,以及是否需要添加超参数调整等。
反思与可能让你惊讶的事:
算法比较曾经是非常繁琐的,随着时间的推移,你可以利用越来越多的工具来轻松比较算法,这样你就可以花更多时间在你最终选择的算法上。你甚至可以在同一时间窗口内训练和比较多个算法,更轻松地针对特定的损失函数进行优化,这样你就不会局限于单一算法,因为你的基础数据和假设可能会发生变化。
总结
虽然公司和角色可能会有所不同,但数据科学流程中有一些关键步骤是被广泛实践的。我们讨论了几个涵盖常规项目的数据科学工作流的步骤。
总结一下,这里是我们在数据科学工作流模板中讨论的步骤:
-
定义你的初始数据集
-
定义此数据集的来源
-
数据是否已经存在?
-
您的数据是否已经正确汇总?
-
您是否想创建新的模型特征?
-
您的目标是连续数字还是类别?
-
您选择哪个算法?
-
在比较不同算法时,我需要关注什么?
我希望您发现我的文章既有趣又有用。如果您作为数据科学家的经验与我的相同或不同,请随时在下方评论,或者您对第一次进入数据科学有什么期望?为什么或者为什么不?您认为还应该讨论哪些其他内容,包括更多的步骤?这些内容肯定可以进一步阐明,但我希望能够为您提供一些关于典型数据科学工作流程的预期信息。
我与这些公司没有任何关联。
请随时查看我的个人资料, Matt Przybyla,以及其他文章,同时可以通过以下链接订阅以接收我的博客的电子邮件通知,或者 点击屏幕顶部关注图标旁的订阅图标,如果您有任何问题或评论,请在 LinkedIn 上与我联系。*
订阅链接: datascience2.medium.com/subscribe
推荐链接: datascience2.medium.com/membership
(如果您通过 Medium 注册会员,我将获得佣金)
参考文献
[1] 照片由 Campaign Creators 提供,拍摄于 Unsplash,(2018)
[2] 照片由 Jason Briscoe 提供,拍摄于 Unsplash,(2019)
[3] 照片由 Liz Sanchez-Vegas 提供,拍摄于 Unsplash,(2019)
[4] Moez Ali,PyCaret,PyCaret 回归教程 (REG101) — 初学者级别,(2023)
你错过了什么
原文:
towardsdatascience.com/heres-what-you-are-missing-cdf4a6294020
AI 行业中可见性的隐藏力量以及如何获得它
·发表于Towards Data Science ·阅读时间 11 分钟·2023 年 8 月 26 日
–
什么是行业可见性?
保持可见性 — 图片由 MidJourney 生成。
你多久会考虑一下你职业生涯中的非技术方面?
我们都在一个重视技术技能和生产力的领域工作,因此如果你没有把个人品牌和行业可见性等非技术活动视为首要任务,那也是可以理解的。但在这篇文章的结尾,作为 AI 从业者的你将会了解什么是行业可见性,它的好处,以及如何在 AI 领域建立和提升你自己的可见性。
行业可见性和个人品牌是两个密切相关的术语,在某些情况下,它们是可以互换使用的。你可能是一个中级、高级或经验丰富的 AI 从业者,并且从技术角度上看已经见多识广。你可能已经在不同的团队中工作过,这些团队有不同的数据工程师、科学家和产品经理的结构配置。你了解机器学习管道中的关键过程,包括 AI 模型的生产化。
现在你已经到了职业生涯的一个阶段,你对各种库、工具、框架以及整体技术栈有了较好的理解。
在某些圈子里,你可能(偷偷地)自称为 AI 专家。
还有什么?
还有什么在行业可见性之外?— 图片由 MidJourney 生成
接下来是什么?超越技术专长的领域是什么?
寻找上述问题的答案将引导你走上几条道路,而你的旅程中的一个主要途径就是行业可见性。
让我们正式定义一下这个术语。
行业可见性是指个人在特定行业内被同行认可、确认或重视的程度,通常由于知识共享。
从个人角度来看,当你把行业可见度视为某种声誉货币时,这一切都会变得有意义。
那我为什么要在乎呢?
行业可见度 == 职业声誉货币
在一个主要以技术为主的领域,最棒的事情之一就是你可以在 AI 领域取得成功和满足感,而不必担心行业可见度。那么你为什么还要在乎呢?
允许我带你回顾一下过往。
你可以把你的职业生涯视为一场长时间的社会实验。这就是我所做的。
在实验的早期阶段,我担任 Web 开发人员,翻阅前端框架的文档,寻找将更多字母添加到我的缩略技术栈(MEAN、MERN、LAMP 等)中的方法——这段时间非常有趣。
我忽略了行业可见度的观念,或者说,我从未想到过个人可以通过他们的工作和知识在一个组织、一个整个领域内展示自己。
我们可以在这里谈谈数字,对吧?
攀登企业阶梯——图像由 MidJourney 生成
我在 4 家公司作为 Web 开发人员工作了 2.5 年,并将薪水从每年£28,000(全职)提升到约£90,000+(承包商)每年。
然后,我被 AI/机器学习的兴趣所感染,不得不回到大学学习该领域的基础知识。当我完成学位后,我决定在实验的下一个阶段以不同的方式进行。
AI 是一个发展中的领域。早在 2018 年,我就意识到,掌握一些复杂的 AI 主题的技术知识,并仅在你的 9–5 工作中应用它,只能带你走这么远。
所以我决定再进行一次实验:我能否在 AI 领域超越作为 Web 开发人员的最高年收入?回答这个问题的最佳方法是以某种形式对外展示我的专业知识和学习历程。我本想告诉你,我曾战略性地计划写一些文章和教授课程以提升我在 AI 领域的可见度,但那将是不真实的。
我的做法并没有经过精心规划。正如我所说,这只是一个实验。
Medium 曾经(并且现在仍然是)一个很好的平台来展示我的技能。
就这样,我第一次体验了行业可见度。
结果
两年零三个月后,毕业获得硕士学位,这个实验成功了。
我作为 AI 从业者在比作为 Web 开发人员更短的时间内达到了更高的收入水平,并且职业转变更少。
如何做到的?
现在和我一起说……行业可见度。
通过在 Medium 上分享我在计算机视觉和机器学习方面的专业知识和学习内容,经过一段时间,我在 9–5 工作之外获得了一些可见度。这种可见度导致了以下结果:
-
AI 角色的面试过程较短
-
计算机视觉书籍的技术审阅者
-
播客邀请(KNN 和 SuperDataScience)
-
…并且它还在继续。
我是这样看的:你投入的努力来传达你的技能和专业知识,获取了一种专业货币,你可以用这种货币去获得未在招聘板上广告的人工智能职位、外部项目的机会、演讲机会以及那些无法通过行业可见性获得的职业利益。
在人工智能行业获得可见性的实用步骤
学习一些东西。构建一些东西。教授一些东西。
无论你是否意识到,你可能在人工智能领域的特定细分领域中拥有专业知识,无论是工具、行业还是领域相关的。
你的专业知识已经让你在职业生涯中达到了一个不错的水平。如果你需要掌握任何新的人工智能相关技能,成长型思维使得这成为一个周末活动。
“网络”,“个人品牌”,“行业领导者”和“思想领袖”等词汇开始越来越频繁地出现。你认识到这是你职业生涯的下一步。
如果我们将与科技行业网络相关的复杂性投射到一个由简单句子表示的较低维度,那就是:‘谁知道你知道你知道什么?’
学习一些东西
如果你以某种方式参与人工智能/机器学习/数据领域,你必须不断学习新的工具、技术和概念,以保持相关性和竞争力。
尽管近年来该领域取得了许多重要进展,人工智能仍在发展中。人工智能领域的行业可见性依赖于知识分享或对话题的独特观点。然而,最近出现了大量的“人工智能专家”,他们整理出了一份值得关注的人工智能工具清单。
这些人不是“人工智能专家”。他们更像是工具专家……这没关系。
老实说,我并不太担心你学习的能力。
要在人工智能领域拥有长期职业生涯,你需要具备的关键特质是成长型思维。
今天,生成性人工智能是年度热词。去年是 MLOps,几年前曾是大数据和物联网。
关键在于,人工智能领域的变化要求从业者学习和重新学习新的想法、概念和技术。为了为下一个变革——可能是量子人工智能或其他东西——做好心理准备,你需要具备成长型思维,并培养与这种思维相伴的习惯。
行动: 这篇文章由Ken Jee概述了从业者获取和保持成长心态的步骤。Ken 介绍了一些基本支柱,使从业者能够开始健康地获得成长心态。
建造一些东西
并非所有的 AI 从业者都能构建完整的产品。
团队成员之间的协作— 图片通过 MidJourney 生成
但这个数字每天都在减少,因为软件开发的各个环节之间的障碍正在崩溃。现在,有些 AI 从业者懂得网页开发。对于网页开发者来说,访问 AI 功能只需一次 API 调用。
你可能听说过一个运动。它叫做#buildinpublic。我并不是其中的大参与者,但我却是那些在公共场合构建产品的从业者的观众。
这一运动很简单。它概括了公开产品开发过程的理念,从编写第一行代码到获得第一个用户,所有这些都是为了透明和开放。
公共宣传方面是这一运动的关键部分,通过在社交媒体平台如 LinkedIn 或 Twitter 上定期发布产品开发更新来实现。
作为一个人,你生活中有许多繁琐且手动的方面;而作为一个 AI 从业者,你具备开发软件产品的技能和资源,这些产品可以处理你在日常活动中观察到的一些繁琐和重复的任务。作为数字原住民,你已经拥有社交媒体账户,这些账户充当你声音的放大器,你可以在上面分享你产品开发旅程的片段。
对于在公开场合构建产品的 AI 从业者有哪些好处?
-
公开构建是一种传播你的技术专长的方法,它超越了仅仅命名工具、库和包的层面;它展示了你所积累的技术知识的实用性。获得下一份工作或获得客户可能会成为由 LinkedIn 帖子引发的一系列事件。
-
社区是人际社会互动的一个重要方面,无论在线还是离线。公开构建为 AI 从业者提供了一条通向行业可见性和培养个人品牌的道路,其他从业者可以跟随并学习。
还有更多的好处,但这些将在未来的文章中讨论。
行动: 阅读《Build》由 Tony Fadell 著。
这本书让我以一种独特的方式意识到,构建意在被数百、数千甚至数百万人使用的产品,始于从构思之初就以设计为导向的思维。
对一些人来说,这可能听起来很明显。
但对于技术人员来说,我们问自己的是“我们要如何构建”,而不是“我们要构建什么或为什么构建”。
在书的最后,我明白了培养以产品为导向的思维方式所需的步骤,以及一些开发值得做的产品的实用步骤。
教授一些内容
你的声音和知识是提升你在行业中能见度的最佳工具。将这两者结合的一个活动就是教学。
在 AI 领域,教学有多种形式:
-
在线学习:这是如今最常见的教学形式之一。这种教学方法将课程内容结构化为几分钟或几小时的视频、教程、课程和长篇文章。有时,在线学习是互动的,并且有支持小组。
-
其他教学方法包括传统讲座和基于项目的学习。
作者对生成式 AI 和计算机视觉的教学同事
你可能在过去已经说服自己不要教学。也许你认为其他更有经验的专业人士已经多次讲解了你想覆盖的主题,你还能添加什么呢?
让我们把这种悲观的自我谈话扔到一边吧。
你拥有其他从业者无法提供的东西:你对某个话题的独特视角。我在 Medium 上表现最好的文章之一教导 AI 从业者 如何阅读研究论文,已有超过 20 万次浏览。而且,你猜怎么着?我不是 AI 研究员;我目前作为机器学习架构师的角色不需要阅读研究论文,但我对阅读技术研究论文的见解却与成千上万的 AI 从业者产生了共鸣。
当然,我多次说服自己不要写这篇文章。
这篇文章强化了 AI/ML 传奇人物 Andrew Ng 在他众多教学讲座中的观点。尽管如此,我对这个话题的见解仍对一些从业者有价值。
不用说,我期待在接下来的几周内看到你如何讲解生成式 AI 的技术概念。如果我激励了你采取行动,请随时发送你创作的文章或视频给我。
告诉别人(额外奖励)
LinkedIn、Instagram、Medium、Twitter、YouTube…Threads👀
选择以上的一种、两种或所有方法。
最关键的是,你需要放大你的声音来启动你的 AI 行业能见度。你的职业和社交档案展示了你的工作和职业努力,向该领域的其他人展示你的知识财富,包括你的热情、好奇心等。
我喜欢构建产品,我使用 Medium 和 LinkedIn 来分享我的旅程。
我对技术(AI)充满热情和好奇,喜欢讨论和学习相关内容。我使用 YouTube 作为与该领域思想领袖和杰出从业者交流的渠道。
我对产品构建的热情和对 AI 领域的好奇心已在多个社交平台上传达,现在,成百上千的人知道我对这个领域的好奇。
在本文早些时候,我提到过网络关系的核心在于谁知道你知道你知道什么? 本节为你提供了一个实际的方法,用于向外界展示你的专业知识、技能和经验。
让我们以 LinkedIn 作为选择的平台,向别人介绍你做的那些了不起的事情。
一开始,我就承认有一些比我更优秀的从业者在 LinkedIn 上表现出色。我只有几千个粉丝。但以下是对我有效的经验。
-
如果你在 Medium 上有文章,可以从这些文章中提取片段并分享到 LinkedIn。这可以为你旧的和新的文章带来新的流量。查看你文章中读者高亮的部分。被高亮的部分是 Medium 上的读者认为有用或有趣的内容;这些内容很可能也会对 LinkedIn 上的观众产生同样的效果。
-
批量处理你的 LinkedIn 帖子。我会在一些周六计划几篇我打算在一周或一个月内分享的帖子。这有助于我保持与观众的互动。但老实说,最近我确实有些掉队了。不过,我很高兴从今天开始与你重新投入其中。
-
对于帖子创意感到困惑?写一些关于你正在学习的内容的帖子,如果你已经采纳了前面提到的成长心态,你将有很多东西可以分享。写一些关于你职业经历、你读过的书籍和你正在收听的播客的帖子。
我在 LinkedIn 上关注的最优秀的 AI 从业者之一是Vin Vashista,他的 LinkedIn Top Voice 徽章和 150,000+粉丝证明了他几乎每天发布的高质量和信息丰富的帖子。
结论
让我们谈谈未来。
AI 行业的关注度和炒作波动是周期性的;任何行业资深人士都可以确认这一点。但在商业关注的高峰和低谷之间,以及耸人听闻的头条新闻背后,从业者如你我有机会在这些领域中奠定基础,建立职业和公众生涯,开辟路径并推动行业发展。
随着我们接近每一个高峰,新的思想领袖、影响者和公司出现在舞台上。而在降入低谷或 AI 寒冬时,能够提供真实价值的人才能保持相关性。
AI 的寒冬可能会来来去去,但真正进步的温暖从未消退。
在 AI 行业的可见性带来了一些责任和承诺;单纯的行业可见性与思想领导的道路相一致,而思想领导还附带了从业者需要提供价值驱动的成果和信誉的额外要求。
这段旅程本身并不容易。
但这是你在意图提升职业发展时一直缺失的。如果你能在追求工业 AI 可见性的过程中保持一致和稳定,所获得的好处是不可估量的。
如果你有一些有趣的观点想要分享,请使用评论区。
感谢阅读。
LinkedIn | Newsletter | YouTube | Support My Writing
这就是为什么你从数据中提取价值的努力没有进展
原文:
towardsdatascience.com/heres-why-your-efforts-extract-value-from-data-are-going-nowhere-8e4ffacbdbc0
数据驱动的领导力与职业生涯
行业内对数据设计和数据质量的忽视(以及你可以做些什么)
·发表于Towards Data Science ·8 分钟阅读·2023 年 2 月 25 日
–
如果数据科学是“让数据有用”,那么数据工程就是“让数据可用”。
这些学科非常令人兴奋,以至于我们容易超前,忘记在我们能够使数据可用(更不用说有用)之前,我们首先需要制作数据。
那么“制作数据”本身呢?
制作优质数据的艺术被严重忽视了。如果你没有数据——没有输入——可以使用,那么你的数据工程师和数据科学家也帮不了你太多。
即使你确实有一些数据,也有可能你忽视了一个重要因素:数据质量。如果你收集的是非常糟糕的数据,忘记从中提取价值吧。与自然界基本定律“垃圾进,垃圾出”作斗争是徒劳的。
作者从文章“为什么企业在机器学习上失败”中引出的人工智能类比
数据在数据科学和人工智能中的角色,就像食材在烹饪中的角色一样。即使你有一个现代化的厨房,也无法拯救你;如果你的食材是垃圾,那你最好放弃。不管你如何切割和处理,你都不可能做出有价值的东西。这就是为什么在你急于进入项目之前,首先需要考虑投资于优质数据。
如果你关心结果,在追求华丽的算法、模型和一系列数据科学家之前,先投资于优质数据。
说到垃圾进垃圾出,你的作者进入这个地方后出来时一模一样。¯_(ツ)_/¯
让我对你做个小小的猜测,亲爱的读者:你对垃圾进垃圾出(GIGO)并不陌生。或者对于那些性格乐观、看到半杯水的人(Q 是指质量),你可能更喜欢称之为 QIQO。你实际上在迫切希望我说一些你没听过的内容,但我仍在以 GIGO 的讨论磨练你的耐性。再一次。是的,我们都已经厌倦了重复 GIGO 原则。我和你一样厌倦。
但请解答我这个难题。如果我们拥有一整支尊重垃圾进垃圾出(GIGO)原则的行业,同时我们也明白设计高质量数据集并非易事,那我们有什么证据表明我们把钱投入到了我们口中的理念中呢?
如果数据质量如此显而易见地重要——毕竟,它是整个数十亿美元数据/人工智能/机器学习/统计/分析大工程的基础——那我们称负责这方面的专业人员为什么呢?这不是一个难题。我只想知道的是:
负责高质量数据集设计、收集、策划和文档工作的人的职位名称是什么?
不幸的是,这可能也是一个技巧性问题。每当我在会议上与数据专家聊天时,我都会尝试把这个问题偷偷问进去。而每次我问他们谁负责他们组织中的数据质量时,他们从未给出任何接近共识的答案。谁的工作是?数据工程师说是数据工程师,统计学家说是统计学家,研究人员说是研究人员,用户体验设计师说是用户体验设计师,产品经理说是产品经理……的确,GIGO 反复出现。数据质量似乎正是那种“每个人的工作”最终却变成没人做的工作,因为它需要技能(!),但似乎没有人故意在投资这些技能,更不用说分享最佳实践了。
数据质量正是那种“每个人的工作”最终却变成没人做的工作。
也许我对数据科学职业的关注稍显过多。如果我只是为了自己的职业发展,我会通过数据江湖术快速赚钱,但我希望数据职业总体上能有所意义,值得被重视,发挥作用,使世界变得比我们发现时更好。因此,当我看到两个最重要的前提条件(数据质量和数据领导力)被忽视时,我感到心碎。
如果{数据质量专业人员 / 数据设计师 / 数据策展人 / 数据收集员 / 数据管家 / 数据集工程师 / 数据卓越专家}这个职业甚至没有名字(看到了吧?)或社区,那么在简历或大学课程中找不到它也就不足为奇了。你的招聘人员会使用什么关键字来搜索候选人?你会用什么面试问题来筛选核心技能?祝好运找到卓越——你的候选人将需要一整套丰富的技能。
你的招聘人员会使用什么关键字来搜索候选人?你会用什么面试问题来筛选核心技能?
首先,我们要认识到我们不是在谈论你小表弟的“数据标注”暑期工作,这种工作涉及无脑的数据录入和/或从一堆烘焙缩略图中选择所有的杯子蛋糕照片和/或逐门逐户进行纸质调查。我提到这一点是因为“这不就是数据标注吗?”这是我被多次以礼貌的关切语气问到的问题。真是低估了整个天才领域。
“这不就是数据标注吗?”不是的。(真是低估了整个天才领域。)
不,我们说的是那种最初设计数据收集过程的人。它至少需要一点用户体验设计,一点决策科学,一点调查设计经验,一些心理学,一点有实地经验的实验社会科学(任何有实际经验的人都会在睡梦中预见费城问题),以及一块统计学培训(尽管你不需要整一个统计学家),再加上扎实的分析经验,大量的领域专业知识,一些项目/程序管理技能,一点数据产品管理经验,以及足够的数据工程背景来考虑大规模的数据收集。这是一种稀有的融合——我们迫切需要一种新的专业化。
要有任何希望建立成熟的数据生态系统,我们必须为新一代专家提供一个良好的环境,让他们的专业技能得到认可。
但在我们争取一个得到良好认可、良好管理和良好回报的数据制作职业之前,我们将陷入困境。那些对这一系列技能有天赋的新兴高手如果贸然投入其中,就像是跳入悬崖。现在,这几乎是个在地下室工作的职位,如果它还能算作工作的话。要有任何希望建立成熟的数据生态系统,我们必须为新一代专家提供一个良好的环境,让他们的专业技能得到认可。
那么你能做什么?
认可你的专家
如果你的组织中已经有那些有这些技能和才华的人,尽管历史上被忽视,他们仍在积极承担数据质量工作,你是否在鼓励他们?你是否在培养他们?你是否在奖励他们?我希望你是在。相反,如果你在创建奖励机制来追逐引人注目的MLOps或带有博士头衔的数据科学,你是在自毁(也是在害我们整个行业)。
贡献知识分享
Google 的 People + AI Research (PAIR)团队最近发布了数据卡片手册,以帮助培训社区进行数据设计、数据透明度、数据质量和数据文档最佳实践。我对我们的工作感到非常自豪,并且很高兴这些材料可以免费供大家使用,但还有很多需要学习的东西。如果你也在这条路上,并且热情支持数据卓越,请与世界分享你正在学习的课程。
获取它:bit.ly/datacardsplaybook(图片由Mahima Pushkarna,手册共同创作者,已获得许可)
告诉别人
如果一篇研究论文在森林里落下而没有人使用,它会发出声音吗?从好点子到建立一个卓越的学科是一个漫长的旅程……一个需要所有鼓励和宣传的旅程。如果你相信这一点,并且你能激励甚至一个人认真对待,你将为构建未来发挥重要作用。谢谢你提前传播这个消息。
摘要
我们的社区在庆祝数据科学家方面做得很好。我们在庆祝MLOps和数据工程师方面做得不错。但我们在庆祝所有其他数据职业依赖的那些人方面做得很差:那些设计数据收集并负责数据卓越、文档和策展的人。也许我们可以从命名他们(我很乐意听听你的建议)开始,并至少承认他们的重要性。从这里开始,我们会进步到培训他们、雇用他们并欣赏他们的专业技能吗?我真的希望如此。
感谢阅读!怎么样来一个 YouTube 课程?
如果你在这里玩得很开心,并且你正在寻找一个为初学者和专家设计的完整应用 AI 课程,这里有一个我为你的娱乐而制作的课程:
在 YouTube 上享受这个课程这里。
P.S. 你是否尝试过多次点击 Medium 上的拍手按钮看看会发生什么? ❤️
寻找动手的 ML/AI 教程?
这里有一些我最喜欢的 10 分钟教程:
别忘了访问数据卡片手册!
获取链接:bit.ly/datacardsplaybook(图片来源:Mahima Pushkarna,手册共同创作者,经授权使用)
虽然该网站强调数据文档和 AI(要抓住那个时代精神),但数据卡片手册远不止于此。这是我所知道的最强大的通用数据设计资源集合。预览:
获取链接:bit.ly/datacardsplaybook(图片来源:Mahima Pushkarna,手册共同创作者,经授权使用)
感谢阅读!怎么样,一门课程?
如果你在这里玩得开心,并且你在寻找一个有趣的、旨在愉悦 AI 初学者和专家的领导力导向课程,这是我为你准备的小东西:
课程链接:bit.ly/funaicourse
阅读 Cassie Kozyrkov(以及 Medium 上的其他数千位作者)的每一个故事。你的会员费直接支持…
P.S. 你是否尝试过在 Medium 上多次点击“点赞”按钮以查看会发生什么? ❤️
喜欢作者?与 Cassie Kozyrkov 联系
成为朋友吧!你可以在Twitter、YouTube、Substack和LinkedIn找到我。想让我在你的活动中演讲吗?使用这个表格联系我。
混合整数规划(MIP)模型的启发式方法
在 MIP 模型中设置起始解:一个调度应用程序
·发表于 Towards Data Science ·阅读时间 10 分钟·2023 年 4 月 6 日
–
图片由 Nils Geldner 提供,来源于 Unsplash
在计算机科学中,启发式方法是用于寻找给定问题的可行解的技术,通常比精确方法快,但不保证最优性。另一方面,精确方法计算成本高,但可以保证最优解。
将问题建模为混合整数规划(MIP)并使用求解器求解可能会给出最终解决方案。通常,这些求解器在后台使用分支定界算法。分支定界(B&B)被认为是一种解决优化问题的精确方法。
正如名字所示,它将解枚举为树状结构(分支)。B&B 和通常称为蛮力的穷举搜索的主要区别在于界限阶段。在过程中,每个节点(解)与解的下界和上界进行比较。如果分支被证明不能比已找到的最佳解更好,则会被剪枝,算法将转向另一个分支。
本文的目标不是详细介绍分支定界算法;更深入的解释可以在 这里 找到。但我们需要定义几个概念:
-
现有解:在算法的每一步找到的最佳可行解。如果这是一个最小化问题,它是上界。
-
最佳界限:解的有效下界。
-
Gap (%):最佳上界与现有解之间的差异。如果现有解等于最佳上界,则差距为 0,表明已找到最优解。
先进的求解器有不同的方法来计算任意 MIP 的现有解和最佳上界。但对于求解器而言,我们的 MIP 只是一个方程组和目标函数。既然我们知道问题的确切结构,那么开发一个专门算法并将结果作为现有解提供会不会更有帮助?是的,这就是启发式方法和热启动发挥作用的地方。
排列流车间调度问题是文献中最经典的优化问题之一。它可以简要描述为:给定一组机器 m 和一组作业 n,如何处理这些作业,使得每个作业必须按照相同的顺序经过所有机器。目标是最小化最后一个作业的完成时间。
假设有 3 台机器(M1、M2 和 M3)和 3 个作业(J1、J2 和 J3)。作业必须按照给定的顺序进行。下图显示了一个可能的可行解,形式为甘特图:
排列流车间的可行解
这个调度的完成时间,或称 Makespan,为 34,解决方案/顺序是 J1、J2、J3。
这个问题可以被表述为 MIP 模型。我们称 m 为机器集合,n 为作业集合。作业必须按照相同的机器顺序 {0,1,…,m} 进行。作业 j 在机器 i 上的处理时间由 p_ij 给出。我们来用 Python 创建这些参数。我们将从 15 个作业和 5 台机器开始。处理时间在 1 和 100 之间随机生成。M 是解的上界。一个好的上界是所有作业在所有机器上的处理时间之和。
import random
import numpy as np
random.seed(10)
# Number of jobs
n = 15
# Number of machines
m = 5
# Max time for random input generator
max_time = 100
# Generate processing times randomly
times = np.zeros((m, n))
tot_time_job = []
for i in range(m):
for j in range(n):
times[i][j] = random.randint(1, max_time)
# Total processing time per job - sum across machines
tot_processing_job = np.sum(times, axis=0)
# Upper bound of the solution - sum of transit matrix
M = sum(times[j][i] for i in range(n) for j in range(m))
变量有两组。变量 x_ij 是连续变量,表示作业 j 在机器 i 上的开始时间。变量 y_jk 是二进制变量,如果作业 j 在 k 之前执行,则等于 1,否则为 0。Cmax 是调度的 makespan。现在我们用 gurobipy 来定义这些。
opt_model = grb.Model(name="Flow shop scheduling")
# Start time of job j at in machine i
x = {(j, i): opt_model.addVar(vtype=grb.GRB.CONTINUOUS, lb=0, name="x_{0}_{1}".format(j, i))
for j in range(n) for i in range(m)}
# 1 if job j is executed before job k. 0 otherwise
y = {(j, k): opt_model.addVar(vtype=grb.GRB.BINARY, name="y_{0}_{1}".format(j, k))
for j in range(n) for k in range(n) if j != k}
# Makespan - Completion time of last job in last machine
c = opt_model.addVar(vtype=grb.GRB.CONTINUOUS, lb=0, name="c")
约束如下所示:
(1) 确保作业 j 在机器 i 上的处理只能在其在机器 i-1 上完成后开始。(2) 和 (3) 是选择约束——如果作业 j 在作业 k 之后执行,则在给定的机器上应该在其完成后开始。(4) 定义了 makespan,即在机器 m(最后一台机器)上最后一个作业的完成时间。
# Job j in machine i can start only when it is finished in machine i-1
c1 = {(j, i): opt_model.addConstr(x[j, i] - x[j, i - 1] >= times[i - 1][j],
name="c1_{0}_{1}".format(j, i))
for j in range(n) for i in range(1, m)}
# Disjunctive constraints - if job j is after k, it should start after its completion
c2 = {(j, k, i): opt_model.addConstr(x[j, i] - x[k, i] + M * y[j, k] >= times[i][k],
name="c2_{0}_{1}_{2}".format(j, k, i))
for j in range(n) for k in range(n) for i in range(m) if k != j}
c3 = {(j, k, i): opt_model.addConstr(-x[j, i] + x[k, i] - M * y[j, k] >= times[i][j] - M,
name="c3_{0}_{1}_{2}".format(j, k, i))
for j in range(n) for k in range(n) for i in range(m) if k != j}
# Makespan is the completion time of last job in last machine
c4 = {j: opt_model.addConstr(c >= x[j, m - 1] + times[m - 1][j],
name="c4_{0}".format(j))
for j in range(n)}
目标函数是最小化 makespan:
opt_model.ModelSense = grb.GRB.MINIMIZE
opt_model.setObjective(c)
opt_model.setParam('MIPGap', 0.018)
opt_model.optimize()
如果我们将 MIP Gap 设置为 1.8%,最佳目标是 832。分支定界算法的收敛情况见下图:
排列流车间示例的收敛曲线
现有解开始时约为 1,200(Gurobi 能找到的第一个解),并收敛到 832。如果我们能找到一个更好的初始解并将其作为现有解会怎样?
NEH 启发式算法是流车间调度问题中最著名的启发式算法之一。它以其作者(Nawaz, Enscore, 和 Ham)命名为 NEH。它是一种构造性启发式算法,因此,它从一个空解开始,并迭代地构建调度,直到所有任务都被分配。
排列流车间问题的解的表示是一个由 n 个元素组成的列表。列表按开始时间排序——列表中的第一个任务是最早执行的任务,而最后一个任务是最晚的任务。第一步是创建一个函数,该函数接收有序列表(一个解)作为输入,并返回与之相关的总工时。
def get_makespan(solution):
''' Calculate the makespan of a sequence of integer (jobs).
- A job can start only after the previous operation of the same job in machine j-1 ends and
machine is not processing any other job
- Finish time of a job in a given machine is its start time plus processing time in current machine
'''
end_time = np.zeros((m, len(solution) + 1))
for j in range(1, len(solution) + 1):
end_time[0][j] = end_time[0][j - 1] + times[0][solution[j - 1]]
for i in range(1, m):
for j in range(1, len(solution) + 1):
end_time[i][j] = max(end_time[i - 1][j], end_time[i][j - 1]) + times[i][solution[j - 1]]
return end_time
NEH 从按处理时间递减的顺序对任务进行排序开始(所有机器的处理时间之和)。第一次迭代将处理时间最长的任务添加到调度的开头。对于剩余的任务,它尝试将其插入当前解中的所有可能位置,比较每个位置的(部分)总工时,并存储找到的最佳解。这个过程会重复,直到所有任务都被分配。函数如下:
def neh():
''' Heuristic NEH (Nawaz, Enscore & Ham) for flow shop scheduling
1 - Start from an empty schedule
2 - Add first the job with highest sum of processing time
3 - Go through the list of the unassigned jobs, test all in all possible positions in the current solutions
4 - Assign the best job at the best position (with lowest makespan) at the final solution
5 - Repeat (3) and (4) until there are no job unassigned
'''
initial_solution = np.argsort(-tot_processing_job)
current_solution = [initial_solution[0]]
for i in range(1, n):
best_cmax = 99999999
for j in range(0, i + 1):
temp_solution = current_solution[:]
temp_solution.insert(j, initial_solution[i])
temp_cmax = get_makespan(temp_solution)[m - 1][len(temp_solution)]
if best_cmax > temp_cmax:
best_seq = temp_solution
best_cmax = temp_cmax
current_solution = best_seq
return current_solution, get_makespan(current_solution)[m - 1][n]
NEH 找到的结果的总工时为 891,比 Gurobi 作为第一个现有解找到的 1,200 要好得多。
如果我们想从现有解开始,Gurobi 的 B&B 方法可以跳过许多解较高于 891 的分支。如下面的图表所示:
对比 NEH 解的排列流车间示例的收敛曲线
在 Gurobi 中使用热启动相对容易。唯一的挑战是将启发式返回的数据结构转换为 MIP 变量的值。对于我们的问题,我们必须将有序列表和总工时值转换为 y、x 和 c 的变量值。
cmax_heuristic 和 c 变量具有相同的含义,因此唯一的任务是分配变量的初始值。
c.Start = cmax_heuristic
将有序列表(sequence_heuristic)转换为变量 y 也很简单。需要遍历列表,如果任务 j 在列表中排在任务 k 之前,则 y_jk = 1。否则为 0。
for j in range(n):
for k in range(n):
if j != k:
y[j, k].Start = 0
for i in range(0, len(sequence_heuristic) - 1):
for j in range(i + 1, len(sequence_heuristic)):
j1 = sequence_heuristic[i]
j2 = sequence_heuristic[j]
y[j1, j2].Start = 1
我们不需要分配变量 x 的值。Gurobi 可以从约束条件中推断出它们的值。一旦模型执行,用户将能够看到以下消息:
这意味着解是可行的,其目标是 891,并将作为 MIP 开始。那如果我们尝试插入一个不可行的 MIP 开始呢?例如,如果我们尝试将所有 i 和所有 j 的 y_ij = 0 作为开始。当然,这是不可行的,因为所有任务都必须在调度中。在这种情况下,用户将看到以下消息:
具有一个能为MIP找到良好起点的启发式方法并不是MIP启动功能的唯一用途。还有其他几个用途:
-
在实际问题中,用户可以用现实中发生的场景来为模型提供数据。在这种情况下,基准将是模型的起始点。例如,如果一家公司想要为其路线过程创建一个模型,并且有历史数据可用,那么一天运营所采取的路线可以作为MIP启动点,前提是它符合数学模型中给定的约束条件。这也是一个很好的练习,用于检查设计的约束在现实中是否得到遵守。
-
正如已解释的,对于求解器而言,我们的模型只是一些方程。如果用户不想花时间开发启发式方法,使用非常简单/天真的方法也可能有帮助。例如,按索引排序的作业列表是一个可行解([1,2,3,…,n]),如果求解器在寻找初始点时遇到困难,这可能会很有用。
-
如果有一个非常好的已知解,并且想要证明它是否最优,或者离最优解有多远。
最后但同样重要的是,不能保证即使MIP启动比求解器的初始解更好,也会改善收敛性和/或运行时间。这是因为求解器会走一条与原始不同的路径。我们之前展示的案例就是一个很好的例子。使用MIP启动的MIP收敛曲线如下所示:
使用 NEH 作为起点的排列流车间示例的收敛曲线
它确实从一个更好的解开始,但会卡在接近891的值附近。
最后,MIP启动是一个非常有用的资源,几乎所有商业求解器都有这一功能。但它的价值在很大程度上取决于问题、数据集以及开发一个好的启发式方法的时间。
除非另有说明,所有图片均由作者提供。完整代码可在GitHub获取。
嘿,GPU,我的矩阵怎么了?
原文:
towardsdatascience.com/hey-gpu-whats-up-with-my-matrix-cb7f6d7ae7d6
了解 GPU 如何执行矩阵乘法的温和指南
·发表于 Towards Data Science ·阅读时长 8 分钟·2023 年 6 月 13 日
–
图片来源:Thomas Foster 在 Unsplash
矩阵乘法;深度神经网络和现代语言理解巨头的圣杯。作为 MLE 或数据科学家,我们的手指太快了,以至于只需敲入 tf.matmul
或 torch.matmul
,然后从未回头。但不要告诉我你从未对矩阵进入 GPU 时可能发生的情况感到过一丝兴奋!如果你有,那你来对地方了。跟随我,一起探索 GPU 内部那些迷人的复杂性。
我将向你解释这些计算强者是如何处理数字的。你将了解 GPU 在面对矩阵时做的三件鲜为人知的令人印象深刻的事情。在本文结束时,你将对 GPU 内部的矩阵乘法有一个清晰的理解。
GEMM:一个真正的💎为 GPU 所用
GEMM 或广义矩阵乘法是当 GPU 执行矩阵乘法时运行的内核。
C = a (A.B) + b C
在这里,a
和 b
是标量,A
是一个 MxK
矩阵,B
是一个 KxN
矩阵,因此 C
是一个 MxN
矩阵。就是这么简单!你可能会好奇为什么会有那一个额外的加法。事实证明,这是一种在神经网络中非常常见的模式(例如,添加偏置、应用 ReLU、添加残差连接)。
技巧 #1:外积的效果出乎意料👽
如果你被要求从基本原理出发编写一个矩阵乘法算法,这里是你要做的(除非你天生聪慧,能用 GPU 代替大脑——那样不就能为 MLE 省下不少钱了!)。
for (int i = 0; i < M; ++i)
for (int j = 0; j < N; ++j)
for (int k = 0; k < K; ++k)
C[i][j] += A[i][k] * B[k][j];
这里有一个动画视觉效果,可以展示这是什么效果。
基于内积的两个矩阵的乘法(由作者重现 — 灵感来源:www.adityaagrawal.net/blog/architecture/matrix_multiplication
)
但你知道 GPU 讨厌这种实现吗🤔?要理解原因,你需要了解 GPU 内存架构,
所有比较和规格,我将使用 Nvidia A100 GPU 规格。
GPU 有三个主要的内存级别,
-
全局内存或 HBM(通常指的 GPU 内存,以及你在运行
nvidia-smi
时看到的内存) -
共享内存(专门分配给一个流式多处理器[或 SM]的本地内存,并在该 SM 中运行的线程之间共享)
-
寄存器(分配给线程以执行其工作负载)
这就是它的样子,
GPU 的典型内存层次结构(为了简便,忽略 L0/L1/L2 缓存)
首先要注意的是,共享内存(从现在起称为 SRAM)比 HBM 小得多,更不用说寄存器了。所以你的矩阵不可能适合其中(在大多数情况下)。如果我们回到我们的动画,对于A
的一行,需要检索B
的所有列,并对A
中的所有行重复此过程。这意味着,GPU 需要进行大量读取来计算输出。HBM(约 1.5TB/s)比 SRAM(约 19TB/s)慢几个数量级。
用数字来说,假设你想乘以一个10x20
和20x30
的矩阵,你需要读取B
的列10x30=300
次。有没有更好的方法呢?
事实证明一个简单的技巧能大有帮助!只需翻转循环的顺序,使k
成为最外层的循环。就完成了!😮
for (int k = 0; k < K; ++k)
for (int i = 0; i < M; ++i)
for (int j = 0; j < N; ++j)
C[i][j] += A[i][k] * B[k][j];
我们没有触及实际计算,只是调整了循环的顺序,因此结果应该和之前一样。现在矩阵乘法看起来是这样的!
基于外积的两个矩阵的乘法(由作者重现 — 灵感来源:www.adityaagrawal.net/blog/architecture/matrix_multiplication
)
你看,我们一次只带来A
的一个列和B
的一个行,并且不会回头看。这需要的读取次数比原始实现少得多。唯一的区别是,我们之前计算的是两个向量之间的内积,现在我们计算的是外积。
绿色显示的两个向量(蓝色和黄色)之间的内积和外积的区别。
但仍然,我们需要整个C
在 SRAM 中,这可能太大而无法容纳在 SRAM 中。那么 CUDA 是怎么做的呢?这就引出了第二个技巧。
技巧 #2:分而治之(并累积)
不用担心!我不会用复杂的数学或 Leetcode 算法轰炸你。主要要记住的是,矩阵是单独瓦片的二维布局。下面的动画很好地展示了我试图解释的内容。
你可以迭代 A 和 B 中的每个块,仍然可以计算 C 对应块的准确答案
绿色块💚的结果是浅蓝色的 A 条💙和浅黄色的 B 条💛。更进一步,为了计算输出,你可以一次带来一个 A 条的块和一个 B 条的块,计算输出并将结果累积在绿色框中。
这为我们提供了一个灵活的框架,我们可以加载任意大小的 A 和 B 的块(或瓦片),仍然能够计算最终的答案。我们不必止步于此,可以继续递归地将问题划分为更小的问题。即矩阵被拆分成瓦片,瓦片被拆分成片段,片段进一步拆分成单个值。
使用瓦片方法,问题可以递归地拆解。
这很好地适用于 GPU 中的过程执行架构。GPU 中的内核执行有三层。为了简化,我们说一个 SM 运行一个线程块(虽然实际上它们是并行执行的,以减少被称为尾效应的影响)。
-
线程
-
Warps(32 个线程的集合)
-
线程块(几个 warp 的集合)
线程块中的确切线程数量取决于特定的架构。例如,A100 具有以下规格。
-
每个 SM 最大支持 2048 个线程
-
每个块最大支持 1024 个线程
-
每个 SM 最大支持 32 个线程块
侧边栏 #2:2 的幂的魔力
回到瓦片方法,已发现(启发式地)每个线程块的矩阵瓦片大小为256x128
在大多数问题中能提供合理的效率。因此,这是 CUDA 常用的瓦片大小。
你可能听说过将批处理大小、隐藏维度大小保持为 2 的幂的最佳实践。这就是来源!当你的矩阵维度是 2 的幂时,它将被完全划分为一组没有余数的瓦片。如果不是,这会使你的代码效率更低。
当你的矩阵维度是 2 的幂时,GPU 计算会更高效
当不是 2 的幂时会发生什么?
侧边栏 #2:瓦片量化
这会导致一种叫做 tile quantization 的现象。换句话说,如果你的 tile 行维度为 128,但矩阵的行中有 257 个元素,你需要的不仅仅是两个,而是三个 tiles(即 256+1)。如下所示。
仅仅因为行中多了一个元素,我们不得不分配两个完整的线程块。
问题在于,无论线程块中有用的数据如何,线程块执行的计算量是相同的。因此,你会把执行有用计算的机会从 GPU 中拿走,导致效率低下。
类似的现象称为 wave 量化,即矩阵过大,SMs 无法一次性容纳。这时 GPU 需要进行 2 次“波”。不过,这对现代 GPU 影响较小,因为它们利用并发性来减少 wave 量化。
当线程块需要部分溢出数据时,就会发生 tile 量化;当 SMs 需要溢出数据时,就会发生 wave 量化。
技巧 #3:一个比两个好
最终的技巧是内核融合。通常情况下,将所有计算放在一个内核中要比一个接一个调用两个内核要快。为什么?因为一个内核需要将数据写入 HBM,另一个需要读取数据。我们已经讨论过这种操作有多慢。更好的方法是将两个操作合并为一个。以下是一些示例,
正如这里所看到的(我相信 Pytorch 也有类似的术语表),TensorFlow 提供了许多融合的内核,将便捷的操作合并到一个内核中。在代码中,这意味着类似于以下内容,
for (int m = 0; m < M; m += Mtile)
for (int n = 0; n < N; n += Ntile)
for (int k = 0; k < K; ++k)
float tmp = 0
for (int i = 0; i < Mtile; ++i)
for (int j = 0; j < Ntile; ++j)
int row = m + i;
int col = n + j;
tmp += A[row][k] * B[k][col];
// Do other things
C[row][col] = tmp
换句话说,我们会珍惜我们的 tmp
变量,直到完成所有计算后才将结果写回 C
。
结论
就这些了,希望这次通过 GPU 的“丛林探险”对你有所帮助。如果你对音频视觉版本感兴趣,这里是我的 YouTube 视频的链接。
总结一下,我们讨论了三件使 GPU 在矩阵乘法中非常快速的事情。
-
GPUs 摒弃了更友好的内积实现,转而采用更高效的外积实现。
-
GPUs 将矩阵分割成更小的块(块再分割成碎片),并将计算负载分配到线程块、warp 和线程上。
-
GPUs 使用内核融合将常见的功能组合在一起,提高 GPU 效率。
如果你喜欢这个故事,可以随时订阅Medium,这样你就会收到我的最新内容通知,并且能够解锁来自其他作者的成千上万的优质故事的完整访问权限。
[## 通过我的推荐链接加入 Medium - Thushan Ganegedara
作为 Medium 会员,你的一部分会员费用将会支持你阅读的作者,并且你可以完全访问每一篇故事…
thushv89.medium.com/membership?source=post_page-----cb7f6d7ae7d6--------------------------------
除非另有说明,否则所有图片均由作者提供
参考文献:
-
docs.nvidia.com/deeplearning/performance/dl-performance-matrix-multiplication/index.html
-
developer.nvidia.com/blog/nvidia-ampere-architecture-in-depth/
隐藏马尔可夫模型:通过实际例子和 Python 代码进行解释
·
关注 发表在 Towards Data Science ·14 分钟阅读·2023 年 11 月 5 日
–
作者提供的图片
隐藏马尔可夫模型是用于解决现实生活中的问题的概率模型,问题范围从每个人每周至少考虑一次的简单问题——明天的天气会如何?[1] — 到复杂的分子生物学问题,例如预测肽与人类 MHC II 类分子的结合位点[2]。
隐藏马尔可夫模型是马尔可夫链的近亲,但其隐藏状态使其成为确定随机变量序列概率时的独特工具。
在本文中,我们将拆解隐马尔可夫模型的所有不同组件,并一步步展示数学和 Python 代码,看看哪些情感状态导致了你狗狗在训练考试中的结果。我们将使用维特比算法来确定观察到特定观测序列的概率,并展示如何使用前向算法来确定观察到序列的可能性,当你给出一个隐藏状态序列时。
现实世界充满了这样的现象:我们可以看到最终结果,但无法实际观察生成这些结果的潜在因素。例如,根据过去的天气观测和不同天气结果的观察概率来预测天气,确定明天是雨天还是晴天。
尽管受我们无法观察的因素驱动,利用隐马尔可夫模型可以将这些现象建模为概率系统。
隐藏状态的马尔可夫模型
隐马尔可夫模型,简称 HMM,是一种统计模型,作为一系列标记问题进行工作。这些问题描述了可观察事件的演变,而这些事件本身依赖于无法直接观察的内部因素——它们是隐藏的[3]。
隐马尔可夫模型由两个不同的随机过程组成,这些过程可以定义为随机变量的序列——这些变量依赖于随机事件。
存在隐形过程和可观察过程。
隐形过程是一个马尔可夫链,如同将多个隐藏状态串联在一起,随着时间的推移以达到某个结果。这是一个概率过程,因为马尔可夫链的所有参数以及每个序列的得分实际上都是概率[4]。
隐马尔可夫模型描述了可观察事件的演变,这些事件本身依赖于无法直接观察的内部因素——它们是隐藏的[3]。
就像其他马尔可夫链一样,为了知道你接下来会进入哪个状态,唯一重要的因素是你现在的位置——即你目前处于马尔可夫链的哪个状态。你过去的历史状态对于理解你接下来要去的地方并没有意义。
这种短期记忆是 HMM 的关键特征之一,称为马尔可夫假设,表示到达下一个状态的概率仅依赖于当前状态的概率。
马尔可夫假设。(作者提供的图像)
HMM 的另一个关键特征是,它还假设每次观察仅依赖于产生该观察的状态,因此完全独立于链中的其他状态[5]。
马尔可夫假设表明,达到下一个状态的概率仅依赖于当前状态的概率。
这些都是关于 HMM 的极好背景信息,但它们实际上用于哪些问题类别?
HMM 帮助建模现象的行为。除了建模和允许进行模拟,你还可以提出关于这些现象的不同类型的问题:
-
似然性或评分,即确定观察到序列的概率
-
解码生成特定观察的最佳状态序列
-
学习 HMM 参数,这些参数用于观察到特定序列,该序列遍历了特定的状态集合。
让我们看看实际操作吧!
作为 HMM 的狗训练表现
今天你不太担心天气预报,思考的重点是你的狗是否可能从训练课程中毕业。经过所有的时间、精力和狗狗零食,你只希望它们能成功。
在狗狗训练过程中,你的四脚朋友会做一些动作或把戏,教练可以观察并评分它们的表现。通过合并三次试验的分数,他们会判断你的狗是否毕业或是否需要额外的训练。
教练只看到结果,但涉及到的几个因素是无法直接观察的,例如你的狗是否疲倦、快乐、是否不喜欢教练或周围的其他狗。
这些状态都无法直接观察,除非你的狗在特定情绪下做出明显的行动。如果它们能用语言表达自己的感受,那就太好了,也许未来能实现!
在脑海中还新鲜的隐马尔可夫模型,这看起来是预测你狗在考试期间感觉如何的绝佳机会。它们可能因为感到疲倦、饥饿或对教练感到烦恼而获得某个分数。
你的狗已经接受了一段时间的训练,基于训练过程中收集的数据,你拥有了建立隐马尔可夫模型所需的所有基础构件。
为了建立一个建模你狗在训练评估中表现的 HMM,你需要:
-
隐藏状态
-
转移矩阵
-
观察序列
-
观察似然矩阵
-
初始概率分布
隐藏状态是那些影响观察序列的不可观察因素。你只会考虑你的狗是疲倦还是快乐。
HMM 中的不同隐藏状态。(作者提供的图像)
了解你的狗后,非可观察因素可能影响它们的考试表现,仅仅是疲劳或快乐。
接下来你需要知道从一个状态转移到另一个状态的概率,这些信息被记录在转移矩阵中。这个矩阵也必须是行随机矩阵,意味着链中从一个状态到任何其他状态的概率,每一行的总和必须为一。
转移矩阵:表示从一个状态转移到另一个状态的概率。(图像来源:作者)
不论你解决的是什么类型的问题,你总是需要一个观测序列。每个观测值表示在马尔可夫链中遍历的结果。每个观测值都是从特定的词汇表中抽取的。
词汇表(图像来源:作者)
在你狗的考试中,你会观察到每次试验后的得分,这可能是失败、OK或完美。这些都是观测词汇表中的所有可能术语。
你还需要观测似然矩阵,即从特定状态生成观测值的概率。
观测似然矩阵。(图像来源:作者)
最后是初始概率分布。这是马尔可夫链在每个特定隐藏状态下开始的概率。
在马尔可夫链中,有些状态可能永远不会成为起始状态。在这些情况下,它们的初始概率为零。就像转移矩阵中的概率一样,这些初始概率的总和也必须加起来为一。
初始概率(图像来源:作者)
初始概率分布,加上转移矩阵和观测似然矩阵,组成了隐马尔可夫模型的参数。这些是你在拥有观测序列和隐藏状态的情况下,需要弄清楚的概率,并尝试学习哪个特定的 HMM 可能生成了这些数据。
将这些部分组合在一起,这就是表示你狗在训练考试中表现的隐马尔可夫模型的样子。
隐藏状态以及它们之间的转移概率。(图像来源:作者)
在考试期间,你的狗将进行三次试验,只有在这三次试验中不失败两次,才能毕业。
到头来,如果你的狗需要更多训练,你仍然会照顾它。你脑海中的大问题是它们在考试期间感觉如何。
想象一个场景,如果它们以OK — Fail — Perfect的顺序毕业,它们将处于什么样的情感状态?它们会大多数时间感到疲惫,还是饥饿,或者两者的混合?
这种问题正好属于 HMM 可以应用的解码问题。在这种情况下,你希望找出生成特定观察序列OK — Fail — Perfect的最佳状态序列。
解码生成给定观察序列的状态序列的问题利用了维特比算法。然而,值得稍微绕个弯子,了解一下如何使用前向算法计算给定观察序列的概率——这是一项似然任务。这将为更好地理解维特比算法的工作原理奠定基础。
前向算法
如果你将这个问题建模为常规的马尔可夫链,并希望计算观察序列OK, Fail, Perfect的似然,你需要通过每个特定的状态生成期望的结果。在每一步,你会根据前一个观察结果的条件概率来观察当前结果,并将该概率乘以从一个状态到另一个状态的转移概率。
大的区别在于,在常规的马尔可夫链中,所有状态都是已知和可观察的。但在隐马尔可夫模型中却不是这样!在隐马尔可夫模型中,你观察到的是一个结果序列,而不知道为了观察到这个序列需要经过哪个特定的隐藏状态序列。
大的区别在于,在常规的马尔可夫链中,所有状态都是已知和可观察的。但在隐马尔可夫模型中却不是这样!
此时你可能会想,好吧,我可以简单地遍历所有可能的路径,然后最终有一个规则来选择等效路径。 这种方法的数学定义如下所示。
计算观察结果序列的概率,遍历所有可能的隐藏状态序列。(图片来自作者)
这肯定是一种策略!你需要计算观察到序列OK, Fail, Perfect的概率,考虑所有可能生成该序列的隐藏状态组合。
当隐藏状态和观察结果序列的数量足够小时,可以在合理的时间内完成计算。
HMM 中可能路径的概述(图片来自作者)
值得庆幸的是,你刚定义的隐马尔可夫模型相对简单,包含 3 个观察结果和 2 个隐藏状态。
对于长度为 L 的观察序列,在具有 M 个隐藏状态的 HMM 中,你有“M 的 L 次方”种可能的状态,在你的例子中,这意味着2 的 3 次方,即 8 条可能的路径来生成序列OK — Fail — Perfect,涉及到的计算复杂度为 O(M^L L),在大 O 符号中描述。随着模型复杂度的增加,你需要考虑的路径数量呈指数级增长。
随着模型复杂度的增加,你需要考虑的路径数量呈指数级增长。
这就是前向算法的亮点所在。
前向算法计算观察序列中新符号的概率,而无需计算形成该序列的所有可能路径的概率[3]。
算法定义了前向变量,而不是计算所有形成该序列的可能路径的概率,前向变量的值是通过递归计算的。
前向变量是如何递归计算的。(图片来源:作者)
它使用递归是该算法比计算所有可能路径的概率更快的关键原因。实际上,它可以在仅需“L 乘以 M 平方”的计算中计算观察到序列x的概率,而不是“M 的 L 次方乘以 L”。
在你的例子中,使用 2 个隐藏状态和 3 个观察结果的序列,计算概率的次数为 O(MˆL L) = 2³x3 = 8x3 = 24 次,而不是 O(L Mˆ2)*=32²=3x4 = 12 次。
这种减少计算次数的方式是通过动态规划实现的,这是一种编程技术,使用辅助数据结构存储中间信息,从而确保不会多次进行相同的计算。
每次算法要计算新概率时,它会检查是否已经计算过,如果是,它可以轻松地在中间数据结构中访问该值。否则,计算概率并存储该值。
回到你的解码问题,使用维特比算法。
维特比算法
以伪代码的思维方式,如果你要用暴力破解的方法解码生成特定观察序列的隐藏状态序列,你只需:
-
生成所有可能的路径排列,以达到期望的观察序列
-
使用前向算法计算每个观察序列的可能性,对于每个可能的隐藏状态序列
-
选择概率最高的隐藏状态序列
所有可能的隐藏状态序列生成观察序列OK — Fail — Perfect(图片来源:作者)
对于你的特定 HMM,有 8 条可能的路径可以得到OK — Fail — Perfect的结果。再添加一个观察值,你将会有双倍数量的隐藏状态序列!与前向算法所描述的类似,你很容易会遇到指数级复杂度的算法,并达到性能瓶颈。
Viterbi 算法可以帮助你解决这个问题。
当 HMM 中的隐藏状态序列被遍历时,在每一步,概率vt(j)是 HMM 在观察后处于隐藏状态j的概率,并且是通过最可能的状态到达j的。
Viterbi 路径到隐藏状态j在时间步t。(图像来源:作者)
解码生成特定观察序列的隐藏状态序列的关键是最可能路径的概念。也称为Viterbi 路径,最可能路径是从所有可能的路径中,具有最高概率的路径。
解码生成特定观察序列的隐藏状态序列的关键是使用 Viterbi 路径。最可能的路径通向任何给定的隐藏状态。
你可以将前向算法和 Viterbi 算法进行对比。前向算法通过将所有概率相加来获得达到某个状态的可能性,考虑所有到达该状态的路径,而 Viterbi 算法则不想探索所有可能性。它专注于通向任何给定状态的最可能路径。
回到解码隐藏状态序列的任务,以获得考试中 OK — Fail — Perfect 的分数,手动运行Viterbi 算法将会像这样:
Viterbi 路径和解码序列。(图像来源:作者)
Viterbi 算法的另一个独特特点是它必须有一种方法来跟踪所有到达任何给定隐藏状态的路径,以便比较它们的概率。为此,它使用动态规划算法的典型辅助数据结构来跟踪回溯指针到每个隐藏状态。这样,它可以轻松访问过去遍历的任何 Viterbi 路径的概率。
回溯指针是确定通向观察序列的最可能路径的关键。
在你狗的考试示例中,当你计算 Viterbi 路径*v3(Happy)和v3(Tired)*时,你选择概率最高的路径,然后开始向后遍历,即回溯,通过所有到达你所在位置的路径。
Python 中的 Viterbi 算法
手动完成这些工作既费时又容易出错。错过一个重要的数字,你可能需要从头开始并重新检查所有的概率!
好消息是,你可以利用像 hmmlearn 这样的软件库,只需几行代码就可以解码隐藏状态的序列,使你的狗在试验中获得OK — Fail — Perfect,并且按照这个顺序。
from hmmlearn import hmm
import numpy as np
## Part 1\. Generating a HMM with specific parameters and simulating the exam
print("Setup HMM model with parameters")
# init_params are the parameters used to initialize the model for training
# s -> start probability
# t -> transition probabilities
# e -> emission probabilities
model = hmm.CategoricalHMM(n_components=2, random_state=425, init_params='ste')
# initial probabilities
# probability of starting in the Tired state = 0
# probability of starting in the Happy state = 1
initial_distribution = np.array([0.1, 0.9])
model.startprob_ = initial_distribution
print("Step 1\. Complete - Defined Initial Distribution")
# transition probabilities
# tired happy
# tired 0.4 0.6
# happy 0.2 0.8
transition_distribution = np.array([[0.4, 0.6], [0.2, 0.8]])
model.transmat_ = transition_distribution
print("Step 2\. Complete - Defined Transition Matrix")
# observation probabilities
# Fail OK Perfect
# tired 0.3 0.5 0.2
# happy 0.1 0.5 0.4
observation_probability_matrix = np.array([[0.3, 0.5, 0.2], [0.1, 0.5, 0.4]])
model.emissionprob_ = observation_probability_matrix
print("Step 3\. Complete - Defined Observation Probability Matrix")
# simulate performing 100,000 trials, i.e., aptitude tests
trials, simulated_states = model.sample(100000)
# Output a sample of the simulated trials
# 0 -> Fail
# 1 -> OK
# 2 -> Perfect
print("\nSample of Simulated Trials - Based on Model Parameters")
print(trials[:10])
## Part 2 - Decoding the hidden state sequence that leads
## to an observation sequence of OK - Fail - Perfect
# split our data into training and test sets (50/50 split)
X_train = trials[:trials.shape[0] // 2]
X_test = trials[trials.shape[0] // 2:]
model.fit(X_train)
# the exam had 3 trials and your dog had the following score: OK, Fail, Perfect (1, 0 , 2)
exam_observations = [[1, 0, 2]]
predicted_states = model.predict(X=[[1, 0, 2]])
print("Predict the Hidden State Transitions that were being the exam scores OK, Fail, Perfect: \n 0 -> Tired , "
"1 -> Happy")
print(predicted_states)
几秒钟内你就能得到一个与手工计算结果匹配的输出,速度更快,且错误空间更小。
运行上述代码的输出结果。(图片由作者提供)
结论
隐马尔可夫模型令人着迷之处在于,这一统计工具诞生于 20 世纪 60 年代中期[6],却如此强大,并且在从天气预报到找出句子中的下一个词等不同领域中应用广泛。
在这篇文章中,你有机会了解 HMM 的不同组件,它们如何应用于不同类型的任务,以及如何发现前向算法和维特比算法之间的相似性。这两个非常相似的算法使用动态规划来处理涉及的指数级计算。
无论是手工计算还是将参数输入到 TensorFlow 代码中,希望你享受深入探索 HMM 世界的过程。
感谢阅读!
参考文献
-
D. Khiatani 和 U. Ghose,“使用隐马尔可夫模型进行天气预报”,2017 年国际智能国家计算与通信技术会议(IC3TSN),印度古尔冈,2017 年,第 220–225 页,doi: 10.1109/IC3TSN.2017.8284480。
-
Noguchi H, Kato R, Hanai T, Matsubara Y, Honda H, Brusic V, Kobayashi T. 基于隐马尔可夫模型的抗原肽预测,这些抗原肽与 MHC II 类分子相互作用。《生物科学与生物工程杂志》。2002;94(3):264–70。doi: 10.1263/jbb.94.264。PMID: 16233301。
-
Yoon BJ. 隐马尔可夫模型及其在生物序列分析中的应用。《当前基因组学》。2009 年 9 月;10(6):402–15。doi: 10.2174/138920209789177575。PMID: 20190955;PMCID: PMC2766791。
-
Eddy, S. 什么是隐马尔可夫模型?。自然生物技术 22,1315–1316(2004)。
doi.org/10.1038/nbt1004-1315
-
Jurafsky, Dan 和 Martin, James H.,《语音与语言处理:自然语言处理、计算语言学和语音识别导论》。上萨德尔河,新泽西州:Pearson Prentice Hall,2009。
-
Baum, Leonard E., 和 Ted Petrie。“有限状态马尔可夫链的概率函数的统计推断。” 《数学统计年鉴》 37, no. 6 (1966): 1554–63。