关于压缩大数据的重要性
为什么以及如何最小化你的数据存储占用
·
关注 发表在Towards Data Science ·15 分钟阅读·2023 年 1 月 24 日
–
图片来源:Joshua Sortino在Unsplash
“数据是新的石油”,这是Clive Humby提出的一句话,用来描述现代许多公司在其发展和成功中对数据日益增长的依赖。公司们正收集大量数据,以至于像拍字节(petabyte)、艾字节(exabyte)和泽字节(zettabyte)这样的计量单位,已经在日常对话中取代了兆字节(megabyte)、千兆字节(gigabyte)和太字节(terabyte)。然而,无目的的数据收集是无用且浪费的。这个事实可以通过对 Humby 名言的以下扩展来最准确地总结:
数据就像原油。它有价值,但如果未经精炼,它实际上不能被有效使用。
— 迈克尔·帕尔默
为了使数据具有价值,收集数据的方式、使用目标及如何实现这些目标需要精心设计。这种设计的一个重要元素是如何以及在哪里存储收集的数据。鉴于今天收集的数据规模巨大,存储需要专门的解决方案。为了满足不断增长的需求,数据中心存储设施的规模不断扩大,基于云的对象存储服务如Amazon S3、Google Cloud Storage和Azure Blob Storage也越来越受欢迎。
数据存储的成本
数据存储涉及许多成本,有些成本比其他成本更为明显。如果管理不当,存储成本很容易成为你每月研发费用中的主导因素。以下是一些成本考虑因素。
直接存储成本:
尽管单位存储空间的成本多年来一直稳步下降(由于技术进步),但这种下降已被需求的增加所盖过。不论你使用的是本地数据中心还是云存储服务,直接存储成本可能迅速上升。
环境成本:
在过去的几年里,我们见证了对数据中心碳足迹意识的提高,以及对计算科学中增加可持续性的呼吁(例如,见这里)。虽然数据中心服务器的计算密集型工作负荷占据了碳足迹的最大部分,但存储也需要在电力、冷却和生命周期更换上进行大量投资。预计大约 10%的数据中心碳足迹——相当于一个中型西方国家的碳足迹——来自数据存储(例如,见这里和这里)。
数据流成本:
虽然不是直接存储成本,但必须关注与数据推送和拉取相关的成本。这些可能是与云存储服务相关的显性数据传输成本,或是与设计支持数据应用所需通信带宽的基础设施相关的成本。
对数据应用的影响: 还需要注意数据存储的方式,特别是其大小,对数据应用的影响。许多数据应用依赖于从存储中持续流动的数据。在理想情况下,数据应以足够快的速度流经系统,以使应用主机的所有计算资源得到充分利用。然而,如果您的存储解决方案设计不当,您的应用可能会在等待输入数据时处于空闲状态。这将增加运行数据应用所需的整体时间,并且增加计算成本。我们在本文的附录中更详细地描述了这一情况。
降低数据存储成本
我们的数据收集和存储策略解决方案必须考虑到数据存储的潜在高成本。一些良好的做法包括以下几点:
-
将数据的收集和存储限制在实际需要的范围内。
-
从存储中删除不再需要的数据。
-
许多云服务提供商提供多种存储类别—以应对不同类型的数据访问模式(例如,请参见 Amazon S3 Intelligent-Tiering、Google Storage Classes和 Azure Storage Access Tiers)。虽然标准存储选项(例如,Amazon S3、Google Cloud Storage和 Azure Blob Storage)推荐用于频繁访问或需要低延迟的数据,将其他数据分配给更具归档性质的存储类别,可以降低成本。
-
以压缩格式存储数据。
在本文中,我们将重点讨论数据压缩作为降低存储成本的一种手段。我们将讨论几种不同的压缩技术,并通过示例展示应用这些技术的潜在节省。
免责声明
在深入讨论之前,需要说明几点免责声明:
-
在我们的讨论中,我们将提到几种压缩技术和工具。这些仅作为示例。我们的意图是不要推广这些技术或工具,而是与许多其他替代技术或工具进行比较。适合您的最佳解决方案将极大地依赖于您的具体需求。
-
在决定使用某种压缩算法(或任何已发布的算法)之前,请确保您已阅读并理解相关的使用条款。
-
虽然我们的示例将重点放在图像数据上,但这些基本原则同样适用于其他领域。
数据压缩
在本节中,我们将回顾几种压缩技术并通过示例展示它们。我们将得出的主要见解是,通过将压缩算法适应于数据的特定属性,可以获得最佳结果。一个典型的例子就是有损和无损压缩方案之间的区别。
有损与无损压缩
在无损压缩方案中不会丢失数据。解压缩数据时,所有信息都会被恢复:X=uncompress(compress(X))。在有损压缩方案中,由于压缩的原因会丢失一些信息:X≠uncompress(compress(X))。虽然这种数据丢失一开始可能让人担忧,但在许多情况下,这种影响会很小。一个经典的例子就是图像压缩。许多图像压缩方法,如流行的 JPEG 压缩,涉及到一定程度的数据丢失,但(前提是使用合理的压缩配置)得到的图像与原始图像几乎无法区分。毫无疑问,尽管视觉上相似,但某些算法可能对这种数据丢失特别敏感。然而,更多情况下,这种影响不会显著。而且潜在的存储空间节省可以非常可观。我们将在下面进一步扩展图像压缩的话题。
如何衡量压缩质量
评估压缩方案质量的方法有很多种,包括:
-
压缩比:这衡量了压缩过程中数据大小的减少。
-
信息丢失:仅在有损压缩的情况下相关,这衡量了压缩导致的信息丢失对数据质量的影响程度。根据数据类型、领域、数据的用途等,有许多不同的方法来衡量这种质量损失。
-
压缩开销:使用压缩方案意味着需要在管道的不同阶段进行压缩和解压缩。这两项活动都需要一定的计算资源,并可能意味着一定程度的延迟。所需的计算量和延迟可以根据选择的压缩策略有所不同。
-
基础设施依赖:不同的压缩方案会因其基础设施依赖而有所不同。这些依赖可能是硬件依赖和/或软件依赖。
在接下来的示例中,我们将仅测量压缩比和质量损失。在实际操作中,应应用其他指标,以便对压缩策略做出全面的决策。
示例
为了方便讨论,我们将考虑一个玩具示例,其中每个数据样本包括:一个 800x534 RGB 图像、两个具有 16 个类别的像素级分类图,以及一个像素级深度图,包含从图像平面到场景中每个像素捕获的 3D 位置的距离(以米为单位)。如果您想跟随示例,可以将下面的代码片段应用于Unsplash上这篇博客文章顶部的图像。
from PIL import Image
import numpy as np
np.random.seed(0)
im = Image.open('image.jpeg', mode='r')
image = np.array(im)
H,W,C = image.shape
# create artificial labels from image color channels
label1 = image[:,:,0].astype(np.int32)//16
label2 = image[:,:,1].astype(np.int32)//16
depth = (image[:,:,2]+np.random.normal(size=(H,W))).astype(np.float32)
# write all data sample elements to file
with open('image.bin','wb') as f:
f.write(image.tobytes())
with open('label2.bin','wb') as f:
f.write(label2.tobytes())
with open('label1.bin','wb') as f:
f.write(label1.tobytes())
with open('depth.bin','wb') as f:
f.write(depth.tobytes())
测量原始数据文件的存储足迹(例如,通过在 Linux 中运行ls -l),我们发现图像需要 1.3 MB 的存储空间,而 3 个标签图需要每个 1.7 MB。
选择文件格式
设计数据存储策略的一个重要步骤是选择存储数据的格式。这个决定可能会对数据应用程序的访问简便性和速度产生重大影响。特别是,您的设计应考虑不同应用程序的不同访问模式。在上一篇文章中,我们讨论了选择文件格式对机器学习训练的一些潜在影响。为了简化起见,我们将数据样本存储在标准 tarball 中。
import tarfile
with tarfile.open("base.tar", "w") as tar:
for name in ["image.bin", "label1.bin", "label2.bin", "depth.bin"]:
tar.add(name)
结果文件为 6.2 MB。
使用 ZIP 变体进行压缩
所谓“ZIP 变体”,是指各种流行的通用文件格式及其相关压缩方案,包括ZIP、gzip、7-zip、bzip2、Brotli等。请注意,虽然我们将这些格式归为一类,但底层算法可能存在显著差异。许多文件格式包含用于自动压缩数据样本的 ZIP 变体标志。例如,使用bzip2压缩,我们可以将 tarball 的大小减少到 2.7 MB,减少了 2.3 倍。
import tarfile
with tarfile.open("base.bz2", "w:bz2") as tar:
for name in ["image.bin", "label1.bin", "label2.bin", "depth.bin"]:
tar.add(name)
使用 ZIP 变体进行压缩特别吸引人,因为它的通用性。它可以普遍应用,而无需了解底层数据的具体类型或领域。在接下来的章节中,我们将查看是否可以使用考虑到原始数据类型详细信息的压缩方案来改进这一结果。
使用低精度数据类型
通过将数据转换为使用低位精度数据类型,可以节省大量存储空间。在我们的例子中有两个优化机会。
-
将 int32 替换为 uint8:通过使用最小的整数类型来满足需求,可以节省大量的存储空间。在我们的例子中,32 位整数表示显然对于表示我们的 16 类标签图来说是过多的。这些可以很容易地适应 uint8 矩阵而不会丢失任何信息。
-
将 float32 替换为 float16:与整数精度减少相反,此操作会导致信息丢失(即,它是有损的)。此更改应仅在评估其对数据算法的潜在影响后进行。在下面的代码块中,我们演示了两种衡量数据质量变化的指标。这些可以用来预测对数据算法的影响。理想情况下,我们会发现这些指标与算法性能之间的某种关联,但这并不总是那么简单。
label1 = label1.astype(np.uint8)
label2 = label2.astype(np.uint8)
depth_new = depth.astype(np.float16)
# measure loss of quality
from numpy import linalg as LA
l_max = LA.norm((depth-depth_new.astype(np.float32)).flatten(),np.inf) # 0.12
l_2 = LA.norm((depth-depth_new.astype(np.float32)).flatten(),2) # 10.47
with open('label1.bin','wb') as f:
f.write(label1.tobytes())
with open('label2.bin','wb') as f:
f.write(label2.tobytes())
with open('depth.bin','wb') as f:
f.write(depth_new.tobytes())
仅这些优化就产生了 2.9 MB 的 tarball,减少了 2.14 倍。
使用按位操作合并元素
目前,每个分类图都存储在 8 位uint8
缓冲区中。然而,由于这些图像只包含 16 个类别,它们实际上每个只使用了四位。我们可以通过将两个图像合并成一个数据图来进一步压缩数据。
# compress
combined_label = (label2 * 16 + label1).astype(np.uint8)
# restore
label1 = combined_label % 16
label2 = combined_label // 16
结果的 tarball 大小为 2.5 MB,总体减少了 2.48 倍。
注意,我们也可以考虑将每个单独标签图中的相邻元素对合并,从而将其分辨率降至 400x534。在实践中,我们发现将单独的标签图合并有利于在后续处理阶段更好地压缩(如下一节讨论)。借用信息论中的术语,结果具有更低的熵。
图像压缩
各种图像压缩算法(有时称为编解码器)利用图像数据的独特统计特性来提高压缩率。虽然对图像压缩的全面概述超出了本文的范围,但我们将触及一些与当前问题相关的要点。
对图像编解码器进行初步搜索将返回各种选项,包括 PNG、JPEG、WebP、JPEG XL 等。这些编解码器在几个属性上有所不同,包括以下几点:
-
无损与有损压缩支持:一些编解码器支持无损压缩,一些支持有损压缩,还有一些支持两者。在大多数情况下,有损压缩的压缩率比无损压缩更好。
-
支持的输入格式:不同的算法支持不同类型的输入。典型的限制包括支持的颜色通道数量、支持的每像素位数等。
-
压缩质量控制:编解码器在允许控制结果压缩质量的程度和性质上有所不同。例如,通过调整质量控制,我们可以管理压缩率与编码/解码速度和/或信息丢失之间的权衡。
-
底层压缩算法:编解码器底层的算法行为各异,优化的功能不同,表现出的伪影也不同。例如,一些算法可能比其他算法更容易去除你所依赖的特定图像频率。
结构相似性指数测量(SSIM) 是一种常用的度量标准,用于评估图像压缩方案带来的图像质量退化。SSIM 值范围从 0 到 1,其中 1 表示与原始图像完全匹配。其他测量图像退化的指标包括 MSE 和 PSNR。如前所述,你的目标应是选择能够预测信息丧失对数据算法性能影响的指标。
关于使用有损图像压缩方案,有几点需要注意:
-
尽管大多数编解码器优化了视觉感知,你的算法可能对图像中那些不明显的元素较为敏感。特别是,依赖于高度视觉相似性来评估压缩方案不应替代深入评估。
-
当心那些给人以某个编解码器总是优于其他编解码器印象的网站。实际上,不同编解码器的相对性能在图像领域(例如,深空图像与医学图像)之间差异很大,甚至同一领域内的图像样本之间也会有所不同。强烈建议你对自己的图像数据集进行分析。
-
对于视频序列数据,你可能会觉得有必要采用视频压缩格式。视频压缩利用相邻帧之间的相似性来进一步提高压缩率。然而,这通常会导致与图像压缩相比显著降低的质量(例如,通过 SSIM 测量)。在某些情况下,你可能会发现独立压缩每一帧效果更好。
我们的示例包括两个应用图像压缩的机会。首先,我们使用经典的有损 JPEG 编解码器压缩图像图,并将压缩质量设置为 95(有关设置质量值的详细信息,请参见 这里)。接下来,我们压缩标签图。由于我们期望机器学习算法高度依赖数据标签的准确性,因此我们选择无损 PNG 压缩格式,以避免丢失任何标签信息。请注意,尽管 JPEG 和 PNG 都是极受欢迎的格式,但它们并不以提供最佳压缩率而闻名。虽然足够用于我们的演示目的,但你可能会发现使用更现代的图像压缩算法能获得更好的结果。
下面的代码块演示了使用Pillow 包(版本 9.2.0)进行图像压缩。我们使用scikit-image 包(版本 0.19.3)应用 SSIM 评分。
from PIL import Image
from skimage.metrics import structural_similarity as SSIM
Image.fromarray(combined_label).save('label.png')
Image.fromarray(image).save('image.jpg',quality=95)
decoded = np.array(Image.open('image.jpg'))
ssim = SSIM(image, decoded, channel_axis=2)} # 0.996
我们示例中的 SSIM 分数为 0.996,表明 JPEG 编码导致的信息质量损失相对较低。自然,这一水平的图像降级是否可接受将取决于消耗数据的 ML 算法的敏感性。请注意,我们选择的压缩质量相对较高。较低的质量率可能会导致更好的压缩,但以更大的图像细节损失为代价。
下图展示了原始输入、解码输出以及它们之间的绝对差异(为了增强效果,差异被缩放了 20 倍)。
JPEG 对Joshua Sortino在Unsplash上的照片的影响
结果
在这个阶段,图像、标签和深度图分别占用 341 KB、205 KB 和 835 KB 的存储空间。tarball 的大小为 1.2 MB。通过对 tarball 应用通用的 ZIP 算法,这一大小降至 1.1 MB。这比我们最初开始时的简单压缩结果小了一半。最终的压缩序列总结在下面的代码块中:
import tarfile
from PIL import Image
combined_label = (label2 * 16 + label1).astype(np.uint8)
Image.fromarray(combined_label).save('label.png')
Image.fromarray(image).save('image.jpg',quality=95)
depth_new = depth.astype(np.float16)
with open('depth.bin','wb') as f:
f.write(depth_new.tobytes())
with tarfile.open("final.tar.bz2", "w:bz2") as tar:
for name in ["image.jpg", "label.png", "depth.bin"]:
tar.add(name)
通过这个相对简单的序列,我们成功地将数据的大小减少了 5.64 倍。这意味着存储空间节省了超过80%。将其应用于你的完整数据集可能对存储成本产生深远影响。
我们可能还会发现额外的压缩机会。然而,每次额外操作的压缩率可能会降低。此外,使用不同的压缩序列可能会获得更好的压缩率。根据你现有的存储成本和潜在的节省,可能值得继续探索。
摘要
在这篇文章中,我们讨论了数据压缩对数据科学尤其是机器学习的重要性。我们演示了几个简单的压缩技术,并测量了它们对数据存储大小的影响。如上所述,我们选择的方法不一定适合你。找到一个好的解决方案的关键包括:对原始数据类型的深入了解、对数据消耗方式的深刻理解,以及对相关压缩方案的良好掌握。
随着 2023 年的开始,我们发现自己深陷于通常被称为大数据革命的过程中。正确的数据管理,包括使用数据压缩方案,仅仅是这一革命中许多重要组成部分之一。新年快乐。
附录:压缩对数据应用的影响
许多数据应用可以描述为在不同设备和设备组件之间连续流动的大量数据。例如,在深度学习训练负载中,原始训练数据从存储中流向 CPU 工作节点进行预处理和批处理,训练批次从 CPU 送入训练加速器,然后在前向计算图的不同阶段流动,梯度通过反向传播计算,而在分布式训练的情况下,数据在参与的加速器之间进行通信。
数据在典型深度学习训练步骤中的流动(作者提供)
在理想情况下,数据会在系统中足够快地流动,以保持所有计算资源的充分利用。然而,有时你可能会发现数据流受到通信通道带宽限制的约束。这可能导致应用中存在性能瓶颈,并导致计算资源的未充分利用。在这种不理想的情况下,昂贵的资源处于闲置状态,等待数据输入。这类问题可以通过多种方式解决,包括:增加通信带宽(例如,使用不同规格的实例类型)、改变应用架构和/或减少数据的大小。
如果数据的大小很大,并且存储与应用主机之间的通信带宽有限,你可能特别容易遇到数据流瓶颈。将数据存储在压缩格式中可以减少存储位置与应用之间接口的瓶颈潜力。
压缩的一种潜在权衡是压缩和/或解压数据所需的额外计算资源。如果你的数据应用已经计算密集,你可能会发现将数据存储在压缩格式中虽然在应用的某一部分释放了数据流瓶颈,却在其他地方引入了计算瓶颈。因此,数据应用管道中的数据压缩可能成为在不同资源的利用之间取得微妙平衡的艺术。
在 DAX 度量中使用中间结果
原文:
towardsdatascience.com/on-using-intermediary-results-in-dax-measures-9971efa72ae
我们在 DAX 中一直使用表变量。但当我们需要计算中间结果并在 DAX 度量中稍后重用它们时,该怎么办?这个挑战听起来简单,但实际上并不容易。
·发表于 Towards Data Science ·8 分钟阅读·2023 年 3 月 13 日
–
由 Mika Baumeister 提供的照片,来源于 Unsplash
介绍
我们在 DAX 中一直使用中间表变量。
例如,查看以下度量:
[SalesYTD] =
VAR YTDDates = DATESYTD('Date'[Date])
RETURN
CALCULATE(
[Sum Online Sales]
,YTDDates
)
在这种情况下,我们生成一个基于实际筛选上下文的年初至今表,借助于DATESYTD()函数,其中包含了从实际筛选上下文中的年初(即 1 月 1 日)到当前日期的所有日期(当然是基于当前的筛选上下文)。
但有时,我们需要做更多的事情。
例如,我们需要根据当前的筛选上下文查询一个表,并对结果进行进一步计算。
在这种情况下,我们必须生成一个中间表,并将结果分配给度量中的一个变量,以进行所需的计算。
由于我们需要筛选上下文,我们不能预先创建一个包含这些中间结果的表,因为这个表会非常庞大,以容纳所有可能的筛选组合。
所以,让我们来看看我们可以如何做到这一点。
基础查询
基础查询如下:
EVALUATE
ADDCOLUMNS(
VALUES(Customer[CustomerKey])
,"AverageSales", CALCULATE( AVERAGEX( 'Online Sales'
,('Online Sales'[UnitPrice] * 'Online Sales'[SalesQuantity])-'Online Sales'[DiscountAmount]
)
)
)
ORDER BY [AverageSales] DESC
这个查询的(截断)结果如下:
图 1 — 基础查询结果(图示作者提供)
但是如果这仅仅是一个起点,我们还需要在一个度量中基于这个结果进行进一步的计算,会发生什么呢?
例如,我们想要显示每个月所有行的总和。
在这种情况下,我们需要计算每个月的平均销售额,并对结果进行求和。
让我们看看我将如何直观地创建一个解决方案,它不起作用,然后,之后这个解决方案将如何有效。
不应该这样做——或者说它不工作的原因
解决上述需求的第一种直观方法是使用 ADDCOLUMNS() 函数生成一个变量,预计算表格中的平均销售额。然后对行进行聚合以获得所需的结果:
DEFINE
MEASURE 'All Measures'[AverageSalePerCustomer] =
VAR AverageSalesPerCust =
ADDCOLUMNS (
VALUES ( Customer[CustomerKey] ),
"AverageSales",
-- Calculate the Average Sales per Customer using the Filter Context provided by ADDCOLUMNS()
CALCULATE (
AVERAGEX (
'Online Sales',
( 'Online Sales'[UnitPrice] * 'Online Sales'[SalesQuantity] ) - 'Online Sales'[DiscountAmount]
)
)
)
RETURN
-- Calculate a sum of all the rows calculated in the previous step
SUM ( AverageSalesPerCust[AverageSales] )
EVALUATE
-- Get the list of all Months and call the Measures defined above for each month
ADDCOLUMNS (
SUMMARIZECOLUMNS (
'Date'[Year Month Short Name]
),
"AverageSalePerCustomer", [AverageSalePerCustomer]
)
ORDER BY 'Date'[Year Month Short Name]
在DAX Studio中执行此查询后,我得到以下错误信息:
图 2 — 第一次尝试的错误信息(作者提供的图)
问题在于 SUM() 无法与表变量一起使用。
让我们用稍微不同的方式尝试一下。
让我们在 RETURN 语句后用SUMX() 替换 SUM():
SUMX(AverageSalesPerCust
,[AverageSales])
错误信息如下:
图 3 — 使用第二种方法的错误信息(作者提供的图)
所以,SUMX() 也无法访问表变量中的列。
我尝试了其他方法来获得所需的结果:
-
使用CALCULATETABLE() 生成可以被 SUM() 或 SUMX() 使用的表
但这个函数也无法访问表变量中的列。
-
使用FILTER() 生成表并在 SUM() 中使用
尽管 FILTER() 没有问题,但 SUM() 无法访问表变量。
但等等…… FILTER() 没有生成任何错误。
我可以将 FILTER() 与 SUMX() 一起使用吗?
让我们看看这种方法是否有效。
有效解决方案 — FILTER() 和 SUMX()
第一个有效的方法是以下查询:
DEFINE
MEASURE 'All Measures'[AverageSalePerCustomer] =
VAR AverageSalesPerCust =
ADDCOLUMNS (
VALUES ( Customer[CustomerKey] ),
"AverageSales",
CALCULATE (
AVERAGEX (
'Online Sales',
( 'Online Sales'[UnitPrice] * 'Online Sales'[SalesQuantity] ) - 'Online Sales'[DiscountAmount]
)
)
)
VAR AvgSalesOver0 =
-- Wrap the intermediary table variable in FILTER() to make it usable by aggregation function
FILTER (
AverageSalesPerCust
,[AverageSales] > 0
)
RETURN
SUMX (
AvgSalesOver0,
[AverageSales]
)
EVALUATE
ADDCOLUMNS (
SUMMARIZECOLUMNS (
'Date'[Year Month Short Name]
),
"AverageSalePerCustomer", [AverageSalePerCustomer]
)
-- Order by the calculated column
ORDER BY [Year Month Short Name] DESC
第一个表变量 AverageSalesPerCust 仍然是一样的。
第二步是使用 FILTER() 函数来定义新的表变量 AvgSalesOver0。
更新 2024 年 1 月:正如AlexisOlson的评论中所述,FILTER() 并不是必需的。在另一个案例中,我使用了相同的技巧而不使用 FILTER(),效果良好。目前,我不知道为什么在这里需要 FILTER()。
在这种情况下,我使用 FILTER() 函数将表变量 AverageSalesPerCust 转换为可以被聚合函数使用的表变量。
由于 FILTER() 需要至少两个参数,我必须添加“Filter” [AverageSales] > 0 以确保 FILTER 函数能正常工作。
出乎意料的是,SUMX 在访问使用 FILTER() 函数构建的表变量时没有问题,我可以使用这个表变量来计算聚合。
查询的(截断)结果如下:
图 4 — 工作解决方案的结果(作者提供的图)
但执行需要一些时间。
当我查看性能指标时,我注意到一些问题:
图 5 — 第一个解决方案的性能指标(作者提供的图)
总执行时间超过三秒,其中超过两秒的时间花在了公式引擎(FE)上,因为它需要处理三百万行数据。
这需要更高效,我们必须尝试找到更高效的解决方案。
使用CALCULATETABLE()
的优化尝试
我将CALCULATETABLE()
与FILTER()
结合以获得以下解决方案:
DEFINE
MEASURE 'All Measures'[AverageSalePerCustomer] =
VAR AverageSalesPerCust =
CALCULATETABLE (
ADDCOLUMNS (
VALUES ( Customer[CustomerKey] ),
"AverageSales",
CALCULATE (
AVERAGEX (
'Online Sales',
( 'Online Sales'[UnitPrice] * 'Online Sales'[SalesQuantity] ) - 'Online Sales'[DiscountAmount]
)
)
)
-- Add a Dummy filter
,Customer[CustomerKey] <> 0
)
VAR AvgSalesOver0 =
FILTER (
AverageSalesPerCust,
[AverageSales] > 0
)
RETURN
SUMX (
AvgSalesOver0
,[AverageSales]
)
EVALUATE
ADDCOLUMNS (
SUMMARIZECOLUMNS (
'Date'[Year Month Short Name]
),
"AverageSalePerCustomer", [AverageSalePerCustomer]
)
-- Order by the calculated column is possible
ORDER BY [Year Month Short Name] DESC
区别在于我用CALCULATETABLE()
函数封装了ADDCOLUMNS()
。
我添加了一个虚拟筛选器,希望执行计划会有所改变。
但这个版本保持了一切不变。
让我们尝试其他方法。
使用虚拟筛选器的优化尝试
下一个想法是用虚拟筛选器替换FILTER
函数中的谓词:
VAR AvgSalesOver0 =
FILTER (
AverageSalesPerCust
-- Dummy-Filter
,1 = 1
)
假设没有列引用的谓词(1=1)可能会导致更高效的执行。
不幸的是,这并没有改变结果。
是否没有优化的可能或必要?
我尝试了其他表函数来构建第一个表变量,即每个客户的平均销售额,但一切保持不变。
经过一番思考,我意识到这个问题无法在 DAX 中解决。
存储引擎(SE)无法按照我们需要的方式工作,或者我未能找到正确的解决方案。
因此,DAX 引擎将依赖于公式引擎的能力,从而将三百万行数据加载到内存中并在其中汇总数据。
有时我们找到的解决方案已经是最好的了。
三秒是用户愿意等待结果的最大时间限制。
我们可以认为这个解决方案已经足够好。
但等一下。我使用包含十多年数据的整个数据集执行度量。这是一个现实的场景吗?
更现实的情况是用户只会分析一两年的数据。
使用一个筛选器将查询限制为仅一年,执行时间骤降至不到两分之一秒。
图 6 — 仅选择一年的性能指标(图由作者提供)
由于这将是报告中的常见场景,因此解决方案已准备好在报告中使用。
经验教训: 记得在测试复杂度量的性能时使用实际场景和用例。
Lucas Santos 拍摄,照片来源于 Unsplash
解决方案模板
根据这些结果,解决方案的模板如下:
DEFINE
MEASURE 'All Measures'[AverageSalePerCustomer] =
VAR <TableVariable> =
<Definition of the Table>
VAR <FilterOfTable> =
FILTER(
<TableVariable>
,1=1 - Dummy Filter
)
RETURN
<AggregationWithXFunction over <FilterOfTable> >
另外,我们可以使用这种形式:
DEFINE
MEASURE 'All Measures'[AverageSalePerCustomer] =
VAR <TableVariable> =
FILTER(
<Definition of the Table>
, 1=1 - Dummy Filter
)
RETURN
<AggregationWithXFunction over <TableVariable> >
第二种形式更短,并且一切都包含在一个变量中。
我鼓励你添加注释以解释为什么要在包含FILTER 1=1
的度量中添加FILTER()
。
对于不了解这种技术的人来说,这毫无意义。
参考文献
如果你不知道如何在 DAX Studio 中收集和解释性能指标,或者对那里显示的指标解释不确定,请阅读这篇文章,我将在其中详细探讨这个功能:
如何使用 DAX Studio 从 Power BI 获取性能数据
有时我们会遇到报告运行缓慢的情况,我们需要找出原因。我们将看到如何收集性能数据以及…
我使用了 Contoso 示例数据集,就像我之前的文章中一样。你可以从 Microsoft 这里 免费下载 ContosoRetailDW 数据集。
Contoso 数据可以在 MIT 许可下自由使用,如 这里 所述。
我扩大了数据集,以使 DAX 引擎的工作负荷增加。
在线销售表包含 7100 万行(而不是 1260 万行),零售销售表包含 1850 万行(而不是 340 万行)。
加入 Medium 并使用我的推荐链接 - Salvatore Cagliari
阅读 Salvatore Cagliari(以及 Medium 上其他成千上万的作者)的每一个故事。您的会员费用直接…
关于机器为何能够思考
我们如何以最简单的方式思考思维呢?
·
关注 发表在 Towards Data Science ·15 分钟阅读·2023 年 12 月 6 日
–
打开潘多拉的盒子(图片来源:作者)
在 17 世纪,勒内·笛卡尔提出了一个相对较新的思想——“我思故我在”。这一简单的表述成为了西方哲学的基础,并定义了几个世纪以来我们对人类本质的理解。
从那时起,我们对作为人类的意义的理解发生了变化。然而,实际上,许多人仍然认为思考的能力是人性的最重要标志之一。
因此,ChatGPT(及类似模型)发布的瞬间,我们开始被大量讨论“它是否能够思考”的文章轰炸。
例如,《纽约客》思考了“ChatGPT 有怎样的思维?”;《华盛顿邮报》宣称“ChatGPT 可以通过逻辑测试,但别指望它具有创造力”;《大西洋月刊》则得出结论称“ChatGPT 比你想象的更笨”。我个人最喜欢的是这个喜剧演员的视频,他试图向一位从事人力资源工作的人解释 ChatGPT 是什么。
与任何其他容易引发猜测的复杂话题一样,人们对于 AI 模型的思维能力既过分夸大又不足代表。因此,让我们深入探讨一下。
思考就是推理
思维是一个复杂的构造,已经代表了许多不同的事物。因此,为了简单起见,我们可以假设思维或多或少与推理同义。
推理是一个定义得更清晰的概念,巧合的是,它正被越来越多地用作AI 的未来。它也是笛卡尔(在很大程度上)谈论思维时的意思。
所以,不如问“AI 能思考吗?”,不如问“AI 能推理吗?”。
简短的回答是是的。长答案是——它可以推理,但仅限于某些方式。
推理不是一个单一的概念。根据她试图完成的任务类型,有多种推理方式。因此,在这篇文章中,我们将首先简要介绍三种关键的推理类型,并检查机器的表现。然后,我们将探讨为什么机器无法进行常识推理以及在它们能做到之前需要回答什么问题。
推理入门
通常,当我们“思考”时,会使用三种主要的推理类型:演绎、归纳和溯因。
演绎
简而言之,演绎是从给定的规则和被假定为真的案例中得出结论的能力。
想象一下:你在锅里加水,打开炉子,然后放入一个温度计。由于你在学校学到的东西,你知道水(通常)在 100°C 时沸腾。因此,当有人告诉你温度已达到 100°C 时,你可以安全地推断出水正在沸腾(你不必亲眼看到它发生也能“相当确定”它确实发生了)。
这里有一个有用的结构需要记住。
1. 规则: 水在达到 100°C 时沸腾
2. 案例: 水的温度是 100°C
3. 结果: 锅里的水在煮沸
因此,你从规则和案例推理到结果。
推理:从规则和案例推理到结果(图片由作者提供)
推理对我们进行科学研究至关重要。它也是机器最容易重现的推理类型。
按设计,几乎每台机器都执行某种形式的推理。你的简单的、毫不起眼的计算器每次你问 3+5 是多少时都会推导出答案。而它其中没有任何人工智能。
如果我们把它放在和上述水的例子相同的结构中,我们得到:
规则: 计算器已经“提供”了规则1+1 = 2
案例: 你问了问题3+5 = ?
结果: 根据规则,它可以计算/推导出3+5 = 8
简单。
归纳
归纳是从给定的观察集合中概括规则的能力。它对我们进行科学研究至关重要,因为它使我们能够定量地识别新的模式/规则。
让我们坚持水的沸腾例子。假设你从未被告知水在 100°C 沸腾。因此,每次你将一锅水加热到沸腾时,你都放入一个温度计并测量温度——100 次,1,000 次,10,000 次。然后,你的朋友们也做同样的事——无论你做多少次,温度总是 100°C。因此,你可以归纳出规则:“水在 100°C 沸腾”。
1. 结果: 水在沸腾
2. 案例: 每当你放入温度计时,它总是显示 100°C。
3. 规则: 水在 100°C 沸腾。
归纳:从结果和案例推理到规则(图片由作者提供)
然后,你就根据你观察到的模式定量地识别出了一个新的规则。为此,你从结果和案例推理到规则。
这种推理类型当然并不总是正确的。著名的是,欧洲人曾认为所有天鹅都是白色的,直到他们航行到了澳大利亚。我们也知道水的沸点并不总是 100°C(大气压力也起作用)。
仅仅因为某件事发生了 10,000 次并不意味着它总是正确的。但 10,000 次通常是一个安全的选择。
归纳对机器来说要困难得多。你的计算器当然无法执行归纳。然而,机器学习模型可以。实际上,这正是它们的主要目标:从一组给定的结果中进行概括。
让我们举一个简单的例子。假设我们有一个监督分类模型,将用于垃圾邮件检测。首先,我们有标记的训练数据集——垃圾邮件或非垃圾邮件(即结果)。在这个数据集中,我们为每个结果编制了多个案例。基于这些案例,模型会归纳出自己的规则,这些规则可以在以后应用于一个它从未见过的案例。
- 结果: 垃圾邮件或非垃圾邮件
2. 案例: 大样本,包括垃圾邮件和非垃圾邮件示例
- 规则: 包含“这些模式和词语”的邮件很可能是垃圾邮件(在一定的概率范围内)
同样,当处理无监督模型如推荐系统时,过程也类似。我们首先向模型提供一个数据集,关于人们在超市购物时的倾向(结果)。一旦开始模型训练,我们会期望它首先聚类重复的模式(案例),然后引导出自己的规则,这些规则可以在类似的背景中应用。
结果: 关于人们购买的未标记数据
案例: 模型在数据集中发现的类似购买(例如,每个人买鸡蛋的人也会买培根)。
规则: 买鸡蛋的人也会买培根(在一定的概率范围内)
在这两种情况下,这些规则不一定对人类是可理解的。也就是说,我们知道计算机视觉模型“关注”图像的某个部分,但我们很少知道为什么。实际上,模型越复杂,我们了解其使用规则的机会就越小。
所以,这里我们可以看到——机器可以同时执行归纳和演绎推理。
演绎推理和归纳推理——科学的基石
广泛认为,归纳和演绎的结合是我们推理能力的驱动力。正如我们的例子所示,当代的机器学习模型,即使是简单的模型,也能执行这两种操作。
它们首先利用归纳推理从给定的数据集中生成规则。然后,它们将这些规则应用于新的案例。例如,一旦我们向模型提供一张以前未见过的照片,它会利用其规则来推断出具体的结果(例如,它可以告诉我们提供的照片是倒置的)。
尽管如此,大多数数据科学家会同意,即使是最先进的机器学习模型也无法进行推理。为什么?
水煮沸的例子可以简单地说明为什么仅依靠演绎推理和归纳推理并不足够。确实,我们需要它们来生成规则(“水在 100°C 时沸腾”),然后在各种案例中进行验证。然而,这种结合不足以解释我们是如何猜测到煮沸的结果与温度有关的。
除此之外,归纳和演绎推理的额外局限性也变得显而易见——它们在特定的上下文中有所限制,缺乏在不同领域之间转移知识的能力。这正是演绎推理发挥作用的地方,它提供了一个更全面的视角,展示了使我们能够进行直觉跃迁并将洞察力连接到不同领域的认知过程。
演绎推理
演绎推理是从单一惊讶的观察(即结果)中生成新假设的能力。我们每次依赖经验来解释某些事物时,都会这样做。
我们出去看到一条湿街。我们用之前可能下过雨的猜测来解释。我们不需要看到 1 万条湿街就知道下雨时街道会变湿。从技术上讲,我们甚至不需要以前遇到过湿街——我们只需知道当水接触物体时,会使物体变湿。
这意味着,如果我们回到水煮沸的例子,我们将有不同的推理方式:
1. 结果:水在煮沸
2. 规则:水在 100°C 时煮沸
3. 案例:水的温度必须是 100°C
溯因推理:从规则和结果推断到一个案例(作者插图)
我们从结果开始(就像我们进行归纳推理时一样),但我们将其与我们已知的规则结合(基于我们的世界知识和经验)。这两者的结合使我们能够得出一个案例(即,水因温度变化而煮沸)。
溯因推理是所有推理类型中最不可靠的。通过溯因推理得出的假设很可能是不正确的。例如,“湿街”的结果可能与雨无关——也许某个地方的管道在夜间破裂,或者有人认真地用水喷洒了街道。然而,雨似乎是一个合理的解释。
因此,溯因推理允许我们在日常情况中顺利前行,而不会陷入困境。也就是说,我们不需要尝试 1 万次来做出简单的决策。
据我了解,目前没有任何 AI 模型/算法能够进行溯因推理。不是以我刚刚描述的方式。
那些对 1960 年代和 1970 年代基于规则的系统熟悉的人,当然可以提到MYCIN、XCON和SHRDLU,并声称它们能够进行溯因推理。其他人可能会提到斯坦福 AI 指数在2022和2023中引用的溯因推理例子,认为这是未来研究中最有前景的领域之一(即,溯因自然语言推理)。
所以,如果机器在 1970 年代能够进行“溯因推理”,为什么它们仍然不能做我所称的溯因推理(即常识推理)?
为什么溯因推理仍然难以捉摸
即使是最先进的模型也无法进行溯因推理的原因有两个:混淆和架构。
混淆:溯因推理与最佳解释推理(IBE)不同
历史上,在计算机科学领域,许多人将 IBE 和推断这两个术语互换使用。即使是 ChatGPT 也会告诉你这两者是相同的,或者推断是 IBE 的一个子集(取决于你如何提问)。斯坦福哲学百科全书也反映了这种观点。实际上,你在计算机科学的相关领域阅读的几乎每一篇关于推断的论文,都告诉你它与 IBE 相同。
然而,这两者是非常 不同 的构建。
一般来说,推断涵盖了生成新案例的行为(将学习转移到不同的背景中)。另一方面,IBE 是一种非常特殊且更具背景特定性的归纳形式,它不一定要求你定量地识别模式(即,你不需要观察一个模式 10,000 次来制定规则)。这些之间的具体区别是一个相当复杂的哲学讨论。如果你想深入了解这一点,我推荐这篇论文。
然而,就本文而言,帮助我们的是将其放在规则、案例和结果结构中进行思考,并使用像 MYCIN 和斯坦福 AI 指数引用的推断自然语言模型这样的具体例子。
MYCIN 是 20 世纪 70 年代在斯坦福开发的早期专家系统,旨在帮助医生诊断传染病。它依赖于一个知识库,其中每个规则都以条件(IF——即案例)和结论(THEN——即结果)的形式表达。然后它利用了逆向推理机制,使其能够从一组症状和病人数据(分别是结果和案例)中,向后推理以识别和分配从 0 到 1 的启发式确定性评分给那些可能最好地解释情况的规则。即,它从结果和案例推理到规则(即,归纳推理遵循的模式)。
斯坦福 AI 指数引用的作为推断自然语言推理的例子(无论是生成假设还是选择最合理的假设)有点棘手。但这仍然不是推断。事实上,我会说,它类似于 IBE,但它遵循与我们迄今讨论的其他机器学习模型相同的模式——归纳,接着是演绎。
背景:在 2020 年,Bhagavatula 及其同事*,对一个他们称之为 ART 的数据集(包含约 20K 由观察对(O1, O2)定义的叙事背景和 200K 解释性假设)训练了一个变换器模型。训练后,他们给模型提供了一组观察数据,并要求它生成一个合理的假设以匹配(见图 4)。
图 4:推测自然语言推理(该图取自 arXiv:1908.05739)
如图所示,当一个变压器模型(GPT-2+COMeT 嵌入)面对 O1(例如,“Junior 是一只 20***+*** 岁的老海龟”)和 O2(例如,“Junior 仍然很强壮”)时,它可以生成一个合理的假设(例如,“Junior 一直在池塘里和她的朋友们一起游泳”),这可能解释了为什么我们认为 Junior 仍然很强壮。
为什么这是 IBE 而不是推测?
让我们暂时从基础的 ML 模型中抽象出来,考虑一下人类如何执行这样的推理任务。首先,我们得到一个结果:Junior 仍然很强壮,并且我们被告知案例是什么(即,Junior 是一只相对年长的海龟)。然后,从这些中,我们会尝试找出一个潜在的(上下文相关的)规则,以解释这个案例和结果。例如,我们可以推导出一只年老却仍然强壮的海龟
-
趋向于和朋友们玩耍 或
-
有良好的食欲 或
-
具有良好的生命体征
依此类推。
然后我们可以选择最符合我们判断的规则,并将其应用于“一个老海龟”的情况。这将允许我们假设 Junior 可能一直在和她的朋友们一起游泳。
如前所述,从有限的观察中识别潜在规则表明了 IBE,而从这些规则中得出的结论则倾向于是一种较弱的推演形式。
我们作为人类理解到,当一个生物变老(无论是海龟还是人类)时,它们的活力往往会下降(可以说是)。这使我们能够生成相对“充满意义”的规则。然而,变压器模型无法做到这一点。它能做的,是通过归纳和推演来改善对最可能的单词组合的预测。模型没有基本理解,当 Junior 玩得开心时,她仍然很强壮。
实际上,有人甚至可以说推测自然语言推理的工作类似于 链式思维 提示。尽管如此,指令是以不同的方式呈现给变压器的。
希望所有这些例子突显出计算机科学所称的推测其实并不是推测。相反,它似乎是一种特定上下文的归纳变体。
架构:当代 ML 模型受限于归纳
状态最先进模型无法进行推测的第二个原因在于它们的架构。根据定义,机器学习模型是一个生成归纳的机器。这种倾向被它们所谓的归纳偏差进一步加强。
归纳偏差是机器学习中的一个重要概念,指的是模型对其应学习的函数类型所持有的固有假设或偏好。偏差通过限制可能的假设集来指导学习过程,从而提高学习的效率和准确性。
例如,决策树关注于层级结构和简单的决策边界。支持向量机旨在找到类别之间的宽边距。卷积神经网络强调图像中的平移不变性和层级特征学习。递归神经网络偏向于序列模式,贝叶斯网络建模概率关系,正则化线性模型通过惩罚大系数来偏好简单模型,而通用的变换器如 GPT-4 则以捕捉数据中的序列依赖性和关系为特征。这些偏差塑造了模型的行为及其对不同任务的适用性。它们还使得将学习成果从一个情境转移到另一个情境变得困难。
我们仍需的
好的,到目前为止,我们讨论了推理的基础知识,我们看到机器确实可以进行推理。它们能够执行演绎推理和归纳推理。然而,我们直观上所称为“思考”的过程是由归纳推理促进的,由于混淆和架构问题,它依然难以捉摸。
那么,我们还需要什么?
我们如何构建能够执行归纳推理的系统?
首先,我们需要能够准确地定义什么是归纳推理,并描述它是如何工作的。遗憾的是,这方面的研究不多。尤其是当涉及到归纳推理如何与演绎推理和归纳推理相关联时,或者它如何被机器操作化时。学者们唯一一致的观点是,归纳推理发生在演绎推理和归纳推理之前。
那么,什么是归纳推理?
归纳推理并不是一个单一的构造。我个人遇到过大约 10 种不同类型,具体取决于它们所涉及的科学领域。即使是引入归纳推理概念的哲学家查尔斯·皮尔斯,也并没有以一致的方式提及它。
然而,有三种主要类型可以描述归纳推理所服务的基本功能。确切的功能及其形成过程过于复杂,无法在此文中详细讨论。所以,以下是简要说明。
首先,我们有最直接的归纳推理类型——解释性。就是我们迄今为止讨论的那种。使用它时,我们从一个观察(结果)和一个易于识别的规则开始。然后,这两者的组合使我们能够对情况做出猜测。这在水煮沸的例子中得到了很好的说明。
然后,我们有了创新性推断——一种允许我们从一个(期望的)结果推理到一对案例和规则的推断。也就是说,我们只知道我们想要创造什么结果,然后我们需要逐步定义一个案例-规则配对,这样才能实现所述结果。这种类型的推断通常用于生成新颖的想法。
最后,我认为我们有了最有趣的一种推断——操控性推断。我们在唯一知道结果(期望的或其他)部分的情况下使用它。此外,这个结果“存在”的背景由多个隐藏的相互依赖关系定义。因此,不能立刻开始寻找/生成合适的案例-规则配对。相反,我们需要更好地理解结果以及它与环境的关系,以便减少不确定性。
这就是所谓的思维装置/认识中介发挥作用的地方。这可以采取例如基本草图、原型或 3D 模型的形式,用于增强我们对问题的理解。通过在目标环境中操控这个中介,我们能更深入地理解背景。因此,我们能够更好地探索规则和案例的潜在组合。此外,它还使我们能够建立关联,帮助将知识从一个领域转移到另一个领域。这种思维的简化版本通常在立体几何中应用。
正如我所说,仍需要做大量工作来解释这些推断类型之间的关系及其与其他推理方法的相关性。然而,这项工作变得越来越关键,因为它有可能为不同领域之间的洞察力转移提供宝贵的见解。特别是在我们看到该领域对推理的新兴趣的背景下——无论是通过 IBE、“通过模拟和例子进行推理”,还是系统 1 和系统 2 思维。
在所有这些情况中,理解如何区分机器可以进行的不同类型的推理似乎尤为重要。因为,确实,机器是可以进行推理的。它们只是无法进行全方位的推理。
关于 IBE 的其他有趣工作可以在这篇论文中找到(他们确实将推断等同于 IBE)。
在生成式 AI 时代发展数据职业
提高对学习三个基本数据概念的认识
·发表于Towards Data Science ·阅读时间 5 分钟·2023 年 7 月 6 日
–
图片由Brett Jordan拍摄,来源于Unsplash
作为一名数据专业人士,我对生成式 AI 领域的所有最新发展感到惊讶。
虽然一些人称其为炒作,并愿意迅速将其视为另一个技术趋势,但其他人则坚信它是一个游戏规则的改变者。
无论你支持哪个观点,都很难忽视生成式 AI 可能为未来教育和工作场所带来的变革性可能性。
为了支持这一说法,只需提到哈佛大学将在今年秋季(2023 年秋季)将 AI 聊天机器人引入课堂,以接近一对一的师生比例。学生们将使用哈佛开发的聊天机器人来引导他们找到解决方案,而不是直接提供简单的答案。
对我来说,这明显表明哈佛大学正在引发一波改变新一代学习和工作方式的变革。
这意味着,生成式 AI 不仅仅是一个过时的趋势,我们需要开始寻找适应这一技术的新方法。
尽管我对生成式 AI 持积极看法,但我从未有过如此强烈的FOMO。
换句话说,尽管我在过去的 12 年中经历了各种数据角色,并获得了机器学习概念的知识,但我无法跟上生成式 AI 领域的新发展。
新的术语、提示工程的概念、新的大型语言模型的开发、在其上构建的大量应用和解决方案、新的电子学习课程以及关于这个话题的大量帖子——这一切 都是 令人难以承受的。
此外,我无法摆脱这样的不安感——感觉我的一些数据技能现在已经,嗯,过时了。
我的业务同事用几个击键就取代我辛苦获得的查询技能的想法是令人害怕的。
然而,经过深思,我不得不承认,虽然有些(但仅有一些)技能会被取代,我并不介意。每周执行几次临时查询以回答相同的重复业务问题是我从不喜欢做的事情。
我意识到,“我”在数据仓库存储的数据和业务洞察生成之间只是拖慢了决策过程。
我还意识到,这种过渡,即我的替代,并不会一夜之间发生。
首先,当前的开发环境需要调整,即需要变得更加“业务用户友好”,而不是“开发者友好”。
其次,业务用户需要获得对“中心背后”是什么的技术理解。从自然文本条目生成分析洞察的自由也带来了同样的问题。
像慢速洞察生成、洞察生成不准确、在没有新输入(新数据源)的情况下丰富洞察,以及洞察质量检查的技术过程等问题仍将存在。
而且仍然需要有人来处理和“修复”这些问题,为业务用户服务。
换句话说,生成式 AI 无法轻易取代基础性的数据知识。
那么,我所说的“基础性”数据知识是什么意思?
为了支持我对上述问题的回答,归结为三个核心概念:
- 构建数据架构
论点: 技术知识和对如何在特定行业中设计适当数据架构的理解至关重要。
让我以金融科技行业为例。
在金融科技行业,构建数据平台时需要考虑严格的规定,即PCI 数据安全标准。在这些标准之上,有时还会有市场基础的标准。
例如,在瑞士,还有FINMA规定,需要考虑这些规定以使你的数据平台以及数据架构合规。
当然,规定可能会发生变化,这意味着数据架构需要跟随这些变化。这对生成式 AI 提出了真正的挑战。
生成式 AI 可以在一定程度上支持架构设计和开发。
但是,它无法在法规变化的行业中设计可定制的架构解决方案。
如果没有在类似的历史实例上进行训练,它不具备应用特定架构适应性的能力。
2. 数据质量管理
论点: “垃圾进 — 垃圾出” 的说法将始终有效,所有在数据领域工作的人都清楚低质量数据输入的成本。
使用生成式 AI 解决方案,低质量输出的成本更高。
例如,我需要参考一下我在《卫报》阅读的 最近一篇文章。那是一篇关于一位律师使用 ChatGPT 提供类似的过往法律案件例子的文章。他想要支持他的论点,即客户对航空公司提起的诉讼不应被驳回。
我想你可以想象故事的经过:当航空公司的律师检查了所引用的裁决和法律引文时,他们发现这些引用都不存在。简而言之,ChatGPT 正在 幻觉。
从这篇文章中得出的结论是,低质量的数据输出可能会使你失去业务,导致整个项目停摆,或者丧失客户和声誉。
因此,数据专业人员将更加忙碌于管理数据输入和输出的质量。
3. 数据隐私与安全
论点: 作为数据专业人员,你应该了解 SQL 注入 和 数据库安全 的概念。
随着生成式 AI 的发展以及简单提示的使用,数据仓库攻击和数据泄露场景比以往任何时候都更容易发生。
提示注入 的危险——例如,某人可能通过一次文本输入就能够删除整个数据库或检索机密记录——需要成为数据安全的核心问题。
这意味着数据和 IT 专业人员将继续在保护和安全数据方面发挥关键作用。
总结: 具备基础数据概念知识的数据专业人员 将会作为“常量”留在职场,高效管理数据,识别问题,并优化解决方案,以确保合规、安全和可靠。
这是生成式 AI 无法轻易替代的部分。
所以,如果你是一位年轻的专业人士,寻求在生成式 AI 时代如何发展数据职业的建议,那么首先要学习上述核心概念。
相信我:投资时间和资源来获得基础数据知识,将在你的数据职业生涯中带来长期的回报。
生成式 AI 将提升你在这些领域的学习曲线和工作表现,但它只能帮助你达到一定程度。那些“重要”的工作仍然取决于你和你的知识。
One Hot 编码
Scikit Learn 还是 Pandas?
·
关注 发布于 数据科学趋势 ·8 分钟阅读·2023 年 3 月 13 日
–
One hot 编码是一种流行的表示分类数据的方法(所有图片均由作者提供)
摘要
sklearn.preprocessing.OneHotEncoder和pandas.get_dummies都是流行的选择(实际上是唯一的选择,除非你想自己实现)来执行独热编码。大多数科学家推荐使用scikit
,因为它使用其 fit/transform 范式,提供了一种内置机制来学习训练集中的所有可能类别,并将其应用于验证或实际输入数据。因此,这种方法将防止在验证或实际输入数据中不包含所有类别或类别顺序不同引发的错误。
在本文中,我将争论,这场竞争没有明确的赢家。对于使用pandas
DataFrame 的数据科学家来说,使用原生的pandas get_dummies
函数有明显的好处,而且有一种非常简单的方法可以避免上述提到的问题。
介绍
什么是独热编码?
如果你已经知道这些内容,可以安全地跳过这一部分。
独热编码(以下简称OHE)是一种将分类数据编码为数值数据的技术。它主要用于机器学习应用。例如,假设你正在构建一个预测动物体重的模型。你的一个输入将是动物的类型,即猫/狗/鹦鹉。这是一个字符串值,因此像线性回归这样的模型无法处理它。
第一个想到的方法是给动物赋予整数标签,并用相应的整数表示替换每个字符串。但是,如果这样做,你会引入一些人为的排序(例如,鹦鹉对“动物”权重的影响是猫的三倍)。相反,OHE 为每种动物创建一个新的输入变量(即列),并根据动物是否是选定的那个,将该变量设置为 1 或 0。示例:
独热编码(所有图片由作者提供)
在这种分离之后,你的线性模型可以独立地为这些新列分配权重。实际上,你并不需要 3 列来表示这 3 种动物。你可以选择其中任何一列来丢弃。换句话说,如果它既不是狗也不是猫,那它就只能是鹦鹉。
Scikit与Pandas
scikit-learn
和pandas
都提供了执行此操作的方法,数据科学家之间关于使用哪个方法的争论已经有很长的历史。如果你搜索一下,会找到很多相关文章。我重新讨论这个话题的原因是这两个库都在不断发展,有一些新的功能在决策时值得考虑。
文章范围
在编码时,可以指定几个选项,比如是否使用稀疏或密集的数据表示,或者是否保留所有新列或删除其中之一。这两个库都支持许多这样的功能,但在这篇文章中我不会关注它们。本文的重点是类别的处理,如下所述:
如果你进行训练/测试拆分(无论是手动还是使用sklearn.model_selection.train_test_split
自动化),可能会出现训练数据集中不包含任何鹦鹉的情况。从理论上讲,这不一定是一个问题,如果某些类别缺失,你仍然可以进行预测,只是预测可能不够准确。但如果你的代码没有为这种差异做好准备,那么由于拟合数据中的列与用于预测的数据的列不一致,你的代码会出错。
在这篇文章中,我将关注以下几点:
-
如何告诉 OHE 所有类别的集合,并确保编码一致地应用于训练/测试/验证/实际数据?
-
如何将编码应用于 pandas DataFrame?
-
如何在 scikit 管道中集成编码器?
Scikit-learn
通常的做法是使用sklearn.preprocessing.OneHotEncoder
,因为通过其 fit/transform 范式,你可以使用训练数据集来“教会”类别,并将其应用于你的实际输入数据。
主要步骤如下:
其中 X_train 是你的训练输入数据,real_input 是你希望应用模型的真实输入数据(真是个惊喜!)。
如果你“幸运”,那么所有可能的类别都会出现在 X_train 中,编码器对象学习这些类别及其对应的映射,并会为真实输入生成正确的列和正确的列顺序。我们需要注意的是,sklearn.preprocessing.OneHotEncoder
产生的是一个 numpy 数组,所以列的顺序很重要。
但你不应该假设自己总是会幸运。例如,如果你使用交叉验证来随机重复地将数据拆分为训练和测试部分,你可能会发现实际的训练数据缺少一些类别。这会导致错误,因为你无法转换测试集中的数据。
sklearn 为这种情况提供的解决方案是明确地向 OneHotEncoder 对象提供可能的类别,如下所示:
你需要在类别参数中提供一个列表的列表,以便为每个输入列指定类别。
使用 scikit 的另一个常见步骤是进行原始 numpy 数组和 pandas DataFrame 之间的转换。你可以使用sklearn.compose.make_column_transformer
来实现,或者手动实现,使用 OneHotEncoder 的.get_feature_names_out()
方法来获取新特征的列名。让我们来看一下这两种方法的示例。我将添加另一列,Color,以使示例更加信息丰富。
指定输入和编码器
列转换器方法
我们可以看到列转换器完成了部分工作,但如果我们想使用 DataFrames,还需要做额外的工作。我也不太喜欢这些列名,但除了手动后处理,没有其他方法可以调整它们。注意,列是为所有可能的类别创建的,而不仅仅是那些出现在输入中的类别。
手动方法
我称之为手动方法,因为我们直接使用 OneHotEncoder 对象,并自己处理选择和追加列的操作。
我们不得不做一点额外的手动工作,但列名更友好。此外,在较新的 scikit 版本(1.3 及以上)中,我们可以微调这些名称。
管道
一个 scikit 管道 是一种方便的方式来顺序应用一系列转换。你可以使用它来组装几个步骤,这些步骤可以一起进行交叉验证,同时设置不同的参数。
手动/原始方法通常不适合包含在管道中,因为需要额外的步骤来选择和添加列。而列转换器方法则适用于管道。我们所做的额外步骤仅仅是将 numpy 数组转换为 DataFrame,这对管道来说不是必需的。
Pandas
pandas.get_dummies 函数不遵循 fit/transform 模型,也没有明确的输入参数来指定可用的类别。因此,可以得出结论,它不适合这个任务。然而,这个结论并不正确。
Pandas 本身支持通过 pandas.CategoricalDtype 处理分类数据。你需要做好功课,并正确设置列的类别。一旦一致完成这些操作,你就不再需要拟合步骤了。
使用分类类型有额外的好处,例如减少存储空间或检查拼写错误。让我们看看这是如何做到的:
现在我们需要做的就是调用 get_dummies 函数。
正如我们所看到的,在类别正确设置之后,不需要额外的工作就可以获得一个漂亮的 DataFrame。实际上,我在上面有点作弊:默认情况下,get_dummies 会转换所有具有对象、字符串或类别数据类型的列。如果这不是我们想要的,我们可以通过使用 get_dummies 的 columns 参数显式指定要转换的列列表:
我们在上面提到了 scikit 管道。为了使变换器适用于管道,它必须实现 fit 和 transform 方法,而 get_dummies 函数显然没有做到这一点。幸运的是,为此任务创建一个自定义变换器非常简单:
现在我们可以像使用其他 scikit 变换器一样使用我们新的类,我们甚至可以将其嵌入到管道中。
在编写这个变换器时,我们假设相关列已经具有分类数据类型。但是,只需添加几行代码到 GetDummiesTransformer 中即可在 init 函数中允许指定列。
结论
正如我们所见,明确指定 scikit OneHotEncoder 和 pandas get_dummies 方法的可用类别是可能的,也非常推荐。(记住:明确优于隐含!)这意味着这两种方法都非常适合这个任务,所以选择哪个方法是个人偏好。对于 scikit,明确的类别设置是通过将参数传递给 OneHotEncoder 类的构造函数实现的,而对于 pandas,我们必须设置分类数据类型。
-
使用**“原始”版本的 OneHotEncoder**(即没有列变换器)需要最多的手动调整,我在实际中仅在非常少见的情况下会使用这种方法。
-
如果你的过程依赖于 scikit 管道(这有许多优点),那么使用 scikit OneHotEncoder 和列变换器似乎是最自然的选择。
-
如果你喜欢逐步处理数据,从一个 DataFrame 到另一个 DataFrame(这在探索阶段可能是一个不错的选择),那么我一定会选择pandas.get_dummies方法。
就这样,希望你从我的帖子中学到了东西。像往常一样:点赞、订阅、分享、评论!
一步使决策树产生更好的结果
背景、实施和模型改进
·
关注 发表在 Towards Data Science ·7 分钟阅读·2023 年 11 月 23 日
–
在树木中(作者提供的照片)
决策树(DT)被放弃得太快了。
就像这样发生的:
DT 已训练。自然过拟合出现。超参数调整(令人不满意)。最后,树被替换为随机森林。
尽管这可能是性能上的快速胜利,但这种替代更注重“黑匣子”算法。这并不理想。只有决策树能产生直观的结果,为业务领导提供比较权衡的能力,并在流程改进中起到关键作用。
如果你无法理解甚至解释某件事情,那么它就不会进入生产环节。在那些即使小的失败也会带来极端风险的行业中,这一点尤为真实,比如医疗保健领域。
(旁注:人们经常问“随机森林生成特征重要性,这难道不解释了哪些特征是重要的吗?”并不完全如此。特征重要性几乎立即被解释为 因果 驱动因素(例如,具有与目标的依赖性的特征),但它们只是 模型 驱动因素。虽然在这方面对技术人员有所帮助,但特征重要性通常是:(1)在弱模型中无用(2)在具有高基数的特征中膨胀,以及(3)偏向于相关特征。这是另一条完全不同的探索路径,但基本上就是这样。)
决策树如何做出决策
坚持使用决策树将保留您有效沟通结果的能力,但如何使它们性能卓越呢?超参数调整只能帮助到一定程度。无论如何,都应该进行深思熟虑的特征工程。
事实证明,特征数据的特定结构可能使其更好地适应底层的决策树算法,从而使决策树能够做出更好的决策。
在底层,决策树通过在您提供的所有数据中创建正交决策边界(垂直分割)来分离类别。它以一种贪婪的算法方式进行这一操作 —— 首先选择最佳分割的特征,然后转向其他特征中不那么优化的分割。
我们可以直观地检查我们的特征以寻找正交的决策边界。让我们查看以下公开可用的乳腺癌数据集中的特征。在下面的顶部图中,绘制“最差周长”和“平均周长”可以产生良好的正交决策边界,可以很好地分离恶性和良性类别。因此,这些特征将是 DT 模型中的很好的选择。
作者提供的图片
上图显示的底部显示了“平均面积”和“平均周长”,DT 生成了正交决策边界(因其固有性质),但这些是不必要复杂的。也许,对角线分隔在这里会更好,但这不是 DT 分类器的分割方式。此外,DT 对训练数据中甚至是小变化(如异常值)非常敏感,这些变化已知会产生完全不同的树结构。
为了适应决策树的这种独特和基础机制 —— 并最终改善性能和泛化能力 —— 可以应用主成分分析(PCA)。
PCA 在两个重要方面提升了 DT 的性能:
(1) 将关键特征定向在一起(解释最大方差的特征)
(2) 减少特征空间
实际上,PCA + DT 过程自然地展现了上述顶部图中您看到的“最差周长”和“平均周长”特征。这两个是最具预测性的变量,毫不奇怪地具有出色的正交决策边界。
实施过程
请记住,PCA 适用于连续数据。乳腺癌数据集完全由连续变量组成。(另一方面的注释:我看到 PCA 被用于分类变量,不建议这样做。名义级别没有隐含的距离,序数级别并不总是等距离的,强制在离散特征上进行距离表示通常将变量重构为毫无意义的东西。另一个时间的另一个切入点。)
让我们开始下载所需的软件包,并将我们的乳腺癌数据集转换为特征X和目标变量y。
import numpy as np
from sklearn.tree import DecisionTreeClassifier
from sklearn.decomposition import PCA
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, confusion_matrix, precision_score, recall_score
import matplotlib.pyplot as plt
import seaborn as sns
# Load the Breast Cancer dataset
data = load_breast_cancer()
X = data.data
y = data.target
可以调用此数据集的数据框架头部进行检查。
cancer = load_breast_cancer()
df = pd.DataFrame(np.c_[cancer['data'], cancer['target']],
columns= np.append(cancer['feature_names'], ['target']))
df.head()
作者的图片
首先,在没有 PCA 的情况下训练 DecisionTreeClassifier,并收集这些预测(original_predictions)。
# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# Fit a Decision Tree Classifier on the non-PCA-transformed dataset
original_tree = DecisionTreeClassifier(random_state=42)
original_tree.fit(X_train, y_train)
# Predictions on the original dataset
original_predictions = original_tree.predict(X_test)
现在,应用 PCA 来选择能够解释训练集中大部分方差的最小维数。而不是任意选择这个维数,可以使用“拐点法”来确定能够解释 99%方差的维数(如下所示硬编码)。
# Finding the optimal number of PCA components using the elbow method
pca = PCA()
pca.fit(X_train)
explained_variance = pca.explained_variance_ratio_
cumulative_explained_variance = np.cumsum(explained_variance)
# Plot explained variance
plt.plot(range(1, len(cumulative_explained_variance) + 1), cumulative_explained_variance, marker='o')
plt.xlabel('Number of Components')
plt.ylabel('Cumulative Explained Variance')
plt.title('PCA Explained Variance')
plt.grid()
plt.show()
# Determine the optimal number of components (elbow point)
optimal_num_components = np.where(cumulative_explained_variance >= 0.99999)[0][0] + 1
print(f"Optimal number of PCA components: {optimal_num_components}")
基于图表形成“拐点”的视觉观察,发现 6 个 PCA 成分解释了训练集方差的 99%。
作者的图片
现在在训练集上应用 PCA 来捕获 6 个主成分。您可以使用奇异值分解(SVD)进行此操作,这是一种标准的矩阵分解技术(此处不涉及的过程)。与以前一样,在 PCA 转换的训练集上训练 DecisionTreeClassifier,并收集这些预测(pca_predictions)。
# Apply PCA with the optimal number of components
pca = PCA(n_components=optimal_num_components, svd_solver="full")
X_train_pca = pca.fit_transform(X_train)
X_test_pca = pca.transform(X_test)
# Fit a Decision Tree Classifier on the PCA-transformed dataset
pca_tree = DecisionTreeClassifier(random_state=42)
pca_tree.fit(X_train_pca, y_train)
# Predictions on the PCA-transformed dataset
pca_predictions = pca_tree.predict(X_test_pca)
# Confusion matrix
pca_cm = confusion_matrix(y_test, pca_predictions)
# Precision and Recall scores for the original dataset
original_precision = precision_score(y_test, original_predictions, average=’weighted’)
original_recall = recall_score(y_test, original_predictions, average='weighted')
original_accuracy = accuracy_score(y_test, original_predictions)
# Precision and Recall scores
pca_precision = precision_score(y_test, pca_predictions)
pca_recall = recall_score(y_test, pca_predictions)
pca_accuracy = accuracy_score(y_test, pca_predictions)
# Output precision and recall scores
print(f"Original Dataset - Precision: {original_precision:.4f}, Recall: {original_recall:.4f}, Accuracy: {original_accuracy:.4f}")
print(f"PCA-Transformed Dataset - Precision: {pca_precision:.4f}, Recall: {pca_recall:.4f}, Accuracy: {pca_accuracy:.4f}")
现在我们可以比较我们的原始预测(未经 PCA 转换)和 pca 预测(经 PCA 转换),观察我们评估指标(准确率、精确度和召回率)的任何相对改进。
与原始的决策树训练数据相比,当我们首先对数据集进行 PCA 转换,然后进行决策树训练时,我们在各方面都有所改进:
我们可以绘制混淆矩阵,显示两个决策树在恶性和良性肿瘤分类改进方面的相对改进。
# Plot the confusion matrices
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
sns.heatmap(original_cm, annot=True, fmt="d", cmap="Blues", xticklabels=data.target_names, yticklabels=data.target_names)
plt.title("Original Decision Tree Confusion Matrix\nPrecision: {:.2f}, Recall: {:.2f}".format(original_precision, original_recall))
plt.xlabel("Predicted")
plt.ylabel("True")
plt.subplot(1, 2, 2)
sns.heatmap(pca_cm, annot=True, fmt="d", cmap="Blues", xticklabels=data.target_names, yticklabels=data.target_names)
plt.title("Decision Tree with PCA Confusion Matrix\nPrecision: {:.2f}, Recall: {:.2f}".format(pca_precision, pca_recall))
plt.xlabel("Predicted")
plt.ylabel("True")
plt.tight_layout()
plt.show()
作者的图片
最后,识别生成 6 个主成分所使用的我们原始特征是很有价值的。从技术上讲,PCA 生成新的特征,这些特征是原始特征的线性组合。这些新特征彼此正交,并按解释的方差排序。但是,调用components_attribute可以识别用于创建这些组件的特征。
# Get the explained variance ratio of each principal component
explained_variance_ratio = pca.explained_variance_ratio_
# Get the PCA components
pca_components = pca.components_
# Create a DataFrame to display the contributions of original features to each principal component
df_components = pd.DataFrame(data=pca_components, columns=data.feature_names)
# Print the top features contributing to each principal component
for i in range(optimal_num_components):
print(f"Top features for PC{i+1}:")
sorted_features = df_components.iloc[i].abs().sort_values(ascending=False)
print(sorted_features.head())
print("\nExplained Variance Ratio:", explained_variance_ratio[i])
print("=" * 50)
因此,对于我们选择的 6 个主成分,模型使用以下 5 个特征创建了这些主成分:
作者的图片
结论
决策树往往被过早放弃,转而使用更高性能的算法。虽然最高性能很重要,但这可能不是最好的选择——这个决定最终取决于你的利益相关者需求以及解释模型为什么会建议特定结果(参见“可解释人工智能”)。
与其寻求最先进的技术算法,不如通过深思熟虑的特征工程和主成分分析来优化数据准备,从而给决策树提供最佳机会,以展示其直观的决策能力。
感谢阅读。很高兴在LinkedIn上与任何人建立联系!如果你想分享你当前面临的有趣的数据科学挑战,请留下评论或发私信,我会很乐意尝试探讨/撰写相关内容。
我最近的文章:
只有在你知道如何独立完成任务时才使用 LLMs
原文:
towardsdatascience.com/only-use-llms-if-you-know-how-to-do-the-task-on-your-own-0d56e0d07572
否则,你可能会遭遇无声的错误或严重的后果
·发表于 Towards Data Science ·阅读时间 5 分钟·2023 年 10 月 25 日
–
(图片由作者使用 Midjourney 创建)
对于我们大多数人(或所有人)来说,LLMs 是神秘的盒子,能够令人惊讶地快速完成复杂的事情。只要它们给我们所需的,我们通常不关心“如何”部分。
ChatGPT 和其他大型语言模型无疑是生产力的提升者。它们可以轻松处理各种任务,否则这些任务将会枯燥且耗时。
然而,我们不能完全依赖它们。例如,在数据分析方面,我们如何确保 ChatGPT 对数据的见解是准确的?是的,它知道 Pandas 这个流行的数据分析库,但如果它犯了错误呢?或者,如果它部分完成了任务而未能完成剩余部分会怎样?
互补 ChatGPT 的最佳解决方案就是你自己。你需要知道如何独立完成任务,这样:
-
你可以确保 ChatGPT 的解决方案是正确的。
-
当 ChatGPT 无法执行或不知道如何执行时,你可以替代它。
在本文中,我将展示三个示例来支持我之前提到的两个主张。
示例 1:使用 Pandas 进行数据清理
我有一个包含二手车价格和其他一些属性的数据集。这是一个有点混乱的数据集,需要大量清理。我使用了 ChatGPT 高级数据分析(ADA)插件来完成这项任务。
大部分情况下,它表现良好。然而,有一个非常特定的操作 ChatGPT ADA 未能完成。因此,如果我自己不知道如何做,这项任务将无法完成。
让我首先解释 ChatGPT ADA 无法处理的部分。
可能由于数据输入错误,make(即汽车品牌或制造商)列中的一些行包含年份值。当我调查这些行时,我发现 make 值被记录在 model 列中。为了修复这一问题,需要从 model 列中提取 make 部分,并用来替换 make 列中的年份值。
这是如何修复它的:
(图片由作者提供)
我向 ChatGPT ADA 询问了几次,但它未能提供修复此问题的方法。它的最佳尝试是用“未知”替换这些年份值。
然后,我详细编写了以下提示,以便让 ChatGPT ADA 给我提供解决任务所需的 Python 代码:
对于实际是年份值的 make 列中的行,model 列包括正确的 make 值。对于这些行,从 model 列中提取第一个词,并用它替换 make 列中的年份值。
这是 ChatGPT ADA 为这个特定问题编写的代码:
# Identify rows where 'make' is a year value
is_year = data['make'].str.isnumeric() & data['make'].str.len() == 4
# For these rows, split the 'model' column and get the first word
correct_make = data.loc[is_year, 'model'].str.split().str.get(0)
# Update the 'make' column with the correct values
data.loc[is_year, 'make'] = correct_make
虽然这不是最优解,但它完成了任务。
示例 2:替换缺失值
我试图清理的数据集中有缺失值(用“其他”、“未知”或 None 表示)在一列中。我要求 ChatGPT 用该列中最频繁的值替换它们。
(图片由作者提供)
这是 ChatGPT 的回复:
most_common_cylinder = data['cylinders'].mode()[0]
data['cylinders'] = data['cylinders'].apply(lambda x: most_common_cylinder if "cylinders" not in x else x)
这个方法是正确的,因为它用最常见的值替换了不包含“cylinders”的值。然而,它包含了 apply
函数的使用,这在处理大数据集时是不建议的。apply
函数不是向量化操作,可能成为性能瓶颈。
更好的方法是使用以下向量化操作:
df.loc[~df["cylinders"].str.contains("cylinders"), "cylinders"] = df["cylinders"].mode()[0]
如果我不知道 Pandas,我可能无法意识到 apply
函数的使用可能会导致性能问题,并寻找替代解决方案。
示例 3:以更符合 Python 风格的方式编写单元测试
我想测试 ChatGPT 是否能改进单元测试或使其更符合 Python 风格。
我编写了以下单元测试,实际上非常简单:
def test_query(submission):
query = submission.query
assert query.lower().count("where") == 1
当我要求 ChatGPT 改进它时,我期望的更新如下:
def test_query(submission):
assert submission.query.lower().count("where") == 1
第二个版本消除了创建不必要的中间变量 query
。
在第一次尝试中,ChatGPT 编写了如下单元测试:
# first solution
def test_query(submission):
query = submission.query
assert query.count("where", flags=re.IGNORECASE) == 1
这是错误的。count
方法没有 flags
参数。另外,这比我的第一次尝试更简单(或更符合 Python 风格)吗?
第二次尝试是正确的,但仍然没有更简单。
# second solution
import re
def test_query(submission):
query = submission.query
assert len(re.findall(r'where', query, flags=re.IGNORECASE)) == 1
然后,我告诉 ChatGPT 这不比我的解决方案更简单,并建议使用以下方法(这正是我所考虑的):
def test_query(submission):
assert submission.query.lower().count("where") == 1
ChatGPT 批准了我的新建议,接受了它更简洁且符合 Python 风格。
最后的思考
我在这篇文章中展示的示例用例并没有降低 ChatGPT 或其他 LLM 的实用性。我已经用它完成了许多不同的任务,并得到了令人满意的结果。
我想强调的是,他们可能会犯错误。这些错误有些是明显的,有些则可能是隐性的。为了确保你获得准确的结果,留意 ChatGPT 的操作方式。我建议不要完全依赖你不了解的工具。你仍然可以用它来学习新工具,但在进行任何重要操作之前,一定要进行测试。
如果你喜欢这篇文章,请记得点赞和评论,以帮助我获得更多的支持。 关注我 以获取更多关于 Python、数据科学、机器学习和人工智能的内容。
感谢阅读。如果你有任何反馈,请告诉我。
ONNX:用于可互操作深度学习模型的标准
原文:
towardsdatascience.com/onnx-the-standard-for-interoperable-deep-learning-models-a47dfbdf9a09
图片由Jonny Caspari在Unsplash上提供
了解使用 ONNX 标准在框架和硬件平台之间部署模型的好处
·发表在Towards Data Science ·5 分钟阅读·2023 年 1 月 24 日
–
我第一次听说 ONNX 是在 INRIA 实习期间。我当时在用 Julia 语言开发神经网络剪枝算法。那时还没有很多预训练模型可以使用,因此利用 ONNX 导入其他语言和框架开发的模型可能是一个解决方案。
在本文中,我想介绍 ONNX,并通过一个实际示例来解释其巨大的潜力。
ONNX 是什么?
ONNX,即开放神经网络交换,是一个用于表示深度学习模型的开源标准。它由 Facebook 和 Microsoft 开发,旨在使研究人员和工程师能够更轻松地在不同的深度学习框架和硬件平台之间迁移模型。
ONNX 的一个主要优势是它允许模型轻松地从一个框架(如 PyTorch)导出,并导入到另一个框架(如 TensorFlow)。这对于那些想尝试不同框架来训练和部署模型的研究人员,或者需要在不同硬件平台上部署模型的工程师尤其有用。
框架互操作性(图片由作者提供)
ONNX 还提供了一套工具,用于优化和量化模型,这有助于减少模型的内存和计算需求。这对于在边缘设备和其他资源受限环境中部署模型尤其有用。
另一个 ONNX 的重要特点是它得到了广泛的公司和组织的支持。这不仅包括 Facebook 和 Microsoft,还包括像 Amazon、NVIDIA 和 Intel 这样的公司。这种广泛的支持确保了 ONNX 将继续得到积极开发和维护,使其成为一个稳健和稳定的深度学习模型表示标准。
ONNX Runtime
ONNX Runtime 是一个开源推断引擎,用于执行 ONNX(开放神经网络交换)模型。它被设计为高性能且轻量级,使其非常适合在各种硬件平台上部署,包括边缘设备、服务器和云服务。
ONNX Runtime 提供了 C++ API、C# API 和 Python API 来执行 ONNX 模型。它还支持多种后端,包括 CUDA 和 OpenCL,这使得它可以在各种硬件平台上运行,如 NVIDIA GPUs 和 Intel CPUs。
ONNX Runtime 非常有用,因为你可以在任何硬件上使用模型进行推断,无论你使用的是 CPU、GPU、FPGA 还是其他设备,而无需实际重写代码!
ONNX Runtime(图片来源于作者)
ONNX Runtime 的主要优点之一是其性能。它使用多种技术,如即时编译(JIT)、内核融合和子图分区来优化模型性能。它还支持线程池和节点间通信进行分布式部署,使其成为大规模部署的合适选择。
我将在未来的文章中解释所有这些高级功能!
ONNX Runtime 还支持多种模型,包括传统的机器学习模型和深度学习模型。这使得它成为一个多功能的推断引擎,可以用于从计算机视觉和自然语言处理到语音识别和自动驾驶等各种应用。
让我们开始编码吧!
现在让我们来看一个示例,我们将使用经典的scikit-learn来创建一个机器学习模型,然后将这个模型转换为 ONNX 格式,以便我们可以与 ONNX Runtime 一起使用。
首先,我们导入必要的库,将模型拉入 sklearn 并导出为经典的 pickle 格式。我们将使用鸢尾花数据集。
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
import joblib
#import data
iris = load_iris()
x,y = iris.data, iris.target
x_train, x_test, y_train, y_test = train_test_split(x, y)
#train and save model
clr = RandomForestClassifier()
clr.fit(x_train, y_train)
joblib.dump(clr, 'model.pkl', compress = 9)
现在我们已经训练并保存了模型,我们可以重新导入它并将其转换为 ONNX 模型。每个框架都有其自己的转换库。因此,如果你是在 PyTorch 或 TensorFlow 中开发的模型,你需要使用其他库。在这种情况下,库叫做skl2onnx。
所以我们导入了必要的库。
%%capture
!pip install skl2onnx
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import joblib
现在我们终于可以进行转换了。我们应该指定inital_type
,然后可以创建一个名为model.onnx的文件,用于保存 onnx 模型。
clr = joblib.load('model.pkl')
initial_type = [('float_input', FloatTensorType([None, 4]))]
onx = convert_sklearn(clr, initial_types = initial_type)
with open('model.onnx' , 'wb') as f:
f.write(onx.SerializeToString())
现在我们已经有了 ONNX 格式的模型,我们可以导入它,并在一些数据上使用它进行推理。
然后我们安装 ONNX Runtime。
%%capture
!pip install onnxruntime
import onnxruntime as rt
import numpy as np
现在我们创建数据,并导入模型,从而创建一个会话。我们指定输入和输出名称(标签),然后在数据上运行会话!
data = np.array([[5.4, 6.3, 2.6, 7.4], [3.4, 6.2, 7.4, 2.3],[5.2, 6.4, 4.2,5.6]])
sess = rt.InferenceSession('model.onnx')
input_name = sess.get_inputs()[0].name
label_name = sess.get_outputs()[0].name
pred_onx = sess.run([label_name], {input_name: data.astype(np.float32)})[0]
print(pred_onx)
好吧,你通过利用 ONNX Runtime 得到了结果。这只需要几个简单的命令!
这只是对 ONNX 的一个介绍,你当然可以做更多,但我希望你发现这个例子有用。
最后的想法
ONNX 是一个开源标准,它使得在不同框架和硬件平台之间移动深度学习模型变得容易。它提供了一套优化和量化模型的工具,并且得到了众多公司和组织的支持。因此,ONNX 正在成为深度学习的重要标准,使得共享模型和跨平台部署变得简单。
结束
Marcello Politi
OpenAI API — ChatGPT 背后的模型介绍与实现
使用 ChatGPT 背后的模型的编程方法。
·发表于 Towards Data Science ·19 分钟阅读·2023 年 11 月 7 日
–
图片由 Freddy Castro 提供,来源于 Unsplash
现如今 ChatGPT 不需要进一步介绍,在这篇文章中,我们将更深入地探讨如何通过官方的 OpenAI API(OpenAI 是 ChatGPT 背后的公司)以编程方式与 ChatGPT(如 GPT-4、GPT-3.5、DALL·E 等)背后的模型和引擎互动。机器学习科学家和工程师通常更喜欢使用 API 而不是图形用户界面,例如 ChatGPT,因为 API 提供了更高的灵活性和定制性,正如我们将在实现示例中看到的,这在商业环境中是必需的。
为了使用 OpenAI 的 API,我们将设置并激活一个 Python 虚拟环境(这是一项推荐但可选的步骤),安装 OpenAI Python 库,并开始实现 11 个实际示例。这些示例是我在众多探索过的示例中最喜欢的,将包括以下内容:
-
解释代码
-
图像生成
-
表情符号翻译(即我们提供文本描述,模型返回描述该文本的表情符号!)
-
语法错误纠正
-
机场代码提取器
-
命名实体提取器
-
机器翻译
-
情感分析
-
文本摘要
-
解析非结构化数据
-
编写 SQL 查询
我会在逐步讲解每项任务时提供更多细节,但现在既然我们知道了大纲,让我们开始吧!
1. 设置 Python
这一步只是为了创建一个虚拟环境,以便你可以将本文中创建和使用的内容与其他 Python 工作隔离开来。正如我在文章中提到的,使用虚拟环境是可选的,但通常是机器学习从业者和程序员推荐的最佳实践之一。有多种方法可以创建虚拟环境,下面是我使用的一种方法。我们将创建虚拟环境,然后激活它,再安装 OpenAI 的 Python 库(即使你决定跳过虚拟环境步骤,安装 OpenAI 的 Python 库仍然是必需的步骤)。
Mac 用户打开你的终端,Windows 用户打开命令提示符(说明见下方,以防你对这一步不熟悉),并跟随操作!
提示: 如何打开“终端”(在 Mac 上)或“命令提示符”(在 Windows 上)如下:
- Mac 用户: 前往“应用程序”文件夹或使用“Spotlight”搜索“终端”(Command + Space 打开“Spotlight”)
- Windows 用户: 在开始菜单中搜索“cmd”以打开“命令提示符”
1.1. 虚拟环境
打开终端或命令提示符,我们可以使用以下命令创建名为openai-env
的虚拟环境:
python -m venv openai-env
一旦虚拟环境创建完成,我们可以使用下面的命令激活它:
source openai-env/bin/activate
现在我们在新创建并激活的虚拟环境中。接下来,我们将安装 OpenAI 的 Python 库。
1.2. 安装 OpenAI Python 库
请注意,虽然虚拟环境的使用是可选的,但安装 OpenAI Python 库是实现的必需步骤。以下命令安装最新的 OpenAI Python 库:
pip install — upgrade openai
2. 设置 API 密钥
使用 OpenAI API 需要设置一个 OpenAI 账户并获取 OpenAI API 密钥——我将带你完成这两个步骤。
设置 OpenAI 账户可以通过 OpenAI 注册网站 完成。创建 OpenAI 账户后,你可以访问 API 密钥页面 并点击“创建新的密钥”。你需要将其保存在安全的地方,并且通常不想与他人分享你的 API 密钥。
一旦设置好 API 密钥,我们将按如下方式导入它,将 YOUR_API_KEY
替换为你最近创建的 OpenAI API 密钥:
# Import libraries
import os
import openai
# Pass API Key
os.environ['OPENAI_API_KEY'] = 'YOUR_API_KEY'
openai.api_key = os.getenv("OPENAI_API_KEY")
在准备工作完成后,我们终于可以专注于创建一个函数来调用 OpenAI 的 API,并开始实现示例的有趣部分!
3. 调用函数
在本节中,我们将创建一个函数来调用 OpenAI 的 API,我将其命名为magicWand
!调用 OpenAI 的 API 需要传递一组变量(如下所述)。创建此函数将简化过程,以便我们不需要为每个示例重复相同的步骤。
对于所有示例,除了图像生成外,我们将使用 OpenAI 的聊天完成,并在请求中使用以下变量。目前无需了解这些变量的详细信息。我们将在逐步示例中学习它们的工作原理,但我已经提供了一个概述以供参考。
-
engine
:识别将要使用的模型,例如gpt-4
或gpt-3.5-turbo
-
system_prompt
:用于提供任务高层次指导的系统级提示 -
user_prompt
:用于提供任务更详细说明的用户级提示 -
temperature
:这是介于 0 和 2 之间的采样温度。较高的数字(例如 0.8)将确保输出更随机生成,而较低的数字(例如 0.2)将使其更具确定性 -
max_tokens
:模型将生成的最大令牌数量(这有助于限制响应长度)
我们将在示例中将这些变量作为配置字典的值,格式如下:
config = {
‘engine’: ‘ENGINE_NAME’,
‘system_prompt’: ‘SYSTEM_PROMPT’,
‘user_prompt’: ‘USER_PROMPT’,
‘temperature’: TEMPERATURE,
‘max_tokens’: MAX_TOKENS
}
让我们创建我们的函数!此时,不必完全理解函数的内容。我们将请求 GPT-4 解释代码,这是第一个示例!
# Import libraries
import openai
# Define the call function
def magicWand(config):
# Extract variables from the config dictionary
engine = config.get('engine', 'gpt-3.5-turbo') # Default to 'gpt-3.5-turbo'
system_prompt = config.get('system_prompt', '')
user_prompt = config.get('user_prompt', '')
temperature = config.get('temperature', 0.8)
max_tokens = config.get('max_tokens', 100)
# Create an array of message objects
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
]
# Make the API call
response = openai.ChatCompletion.create(
model=engine,
messages=messages,
max_tokens=max_tokens,
temperature=temperature
)
# Extract and return the generated text
return response.choices[0].message['content'].strip()
4. 任务实施
现在我们有了函数,让我们尝试第一个任务,即代码解释。
4.1. 解释代码
正如之前承诺的那样,让我们请 GPT-4 解释我们的代码,以便更好地理解该函数!
为了做到这一点,我们将使用刚刚创建的magicWand
函数,并按如下方式传递值。请注意,主要指令是system_prompt
,我们向 GPT-4 解释任务为:您将获得一段代码,您的任务是以简洁的方式解释它
。
让我们实施这个任务并查看结果。
# Create the config dictionary
config = {
'system_prompt': 'You will be provided with a piece of code, and your task is to explain it in a concise way.',
'engine': 'gpt-4',
'temperature': 0,
'max_tokens': 2000,
'user_prompt': '''
import openai
def magicWand(config):
engine = config.get('engine', 'gpt-3.5-turbo')
system_prompt = config.get('system_prompt', '')
user_prompt = config.get('user_prompt', '')
temperature = config.get('temperature', 0.8)
max_tokens = config.get('max_tokens', 100)
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
]
response = openai.ChatCompletion.create(
model=engine,
messages=messages,
max_tokens=max_tokens,
temperature=temperature
)
return response.choices[0].message['content'].strip()
'''
}
# Use the config to get the result
result = magicWand(config)
# Print the result
print(result)
结果:
GPT-4 对magicWand
函数的解释
我发现这些结果既令人印象深刻又迷人。它逐步解释了我们的magciWand
函数变量是什么以及它们的目的是什么。阅读完结果后,我们接下来就生成一个图像吧!
4.2. 图像生成
如名字所示,这次我们将生成一张图像。这是唯一一个我们不使用 OpenAI 的聊天完成,而是使用 OpenAI 的 DALL·E 模型的示例。使用案例非常直接——我们只需在prompt
中提供图像描述,n
为图像数量,size
为图像大小。让我们请求一只黑色的苏格兰折耳猫,带有浅金色眼睛,躺在白色床单上
,并查看生成的图像!
# Import libraries
from IPython.display import display, Image
import requests
# Generate the response
response = openai.Image.create(
prompt="A black Scottish fold cat with light golden eyes laying down on white sheets",
n=1,
size="512x512"
)
# Save the image URL
image_url = response['data'][0]['url']
# Fetch the image
response = requests.get(image_url)
# Display the image
img = Image(data=response.content)
display(img)
结果:
通过 OpenAI 的 API 生成的猫咪图像
这是一个相当不错的图像,并且与我们的提示一致。接下来,我们将处理一个有趣的请求——我们将要求 GPT-4 将自然语言输入(即文本)翻译成表情符号!
4.3. 表情符号翻译
这可能是我最喜欢的例子!我们将要求 GPT-4 使用我们自己的magicWand
函数将文本翻译成表情符号。我们将提供给 GPT4 的总体指示作为system_prompt
,即你将会收到文本,你的任务是将其翻译成表情符号。不要使用任何常规文本。仅使用表情符号尽力而为
,然后提供需要从文本翻译成表情符号的user_prompt
为数据科学文章很有趣
。让我们看看结果!
# Create the config dictionary
config = {
'engine': 'gpt-4',
'system_prompt': 'You will be provided with text, and your task is to translate it into emojis. Do not use any regular text. Do your best with emojis only.',
'user_prompt': 'Data science articles are fun',
'temperature': 0.8,
'max_tokens': 128
}
# Use the config to get the result
result = magicWand(config)
# Print the results
print(result)
结果:
GPT-4 的文本到表情符号的翻译
这非常有趣!我能看到前半部分的表情符号与数据和科学相关,后半部分的表情符号则与乐趣相关。
接下来,我们将要求 GPT 模型纠正给定句子中的语法错误。
4.4. 语法错误纠正
机器学习模型的一个应用场景是纠正句子中的语法错误。这在商业环境中可以带来许多好处。例如,处理客户沟通的企业需要人类代表阅读、审查并回复这些客户沟通。人类代表的成本相当高,如果收到的消息难以理解,这样的沟通将需要额外的工作来让人类理解和回复。作为替代方案,企业可以依赖语法错误纠正模型来首先清理传入的沟通,然后将修正后的沟通版本发送给人类代表进行审查和处理。我以前写过关于另一种语法错误纠正模型的独立帖子(见下文链接),所以我决定使用相同的句子来看看 GPT 模型的表现!
使用语法错误纠正:标记,而不是重写(GECTor)
towardsdatascience.com
我们将指示gpt-4
或gpt-3.5-turbo
纠正句子中的语法错误她昨天看天空时梳头
,然后比较它们的表现。请注意,句子中有故意的拼写和语法错误供模型纠正。为此,我们将使用system_prompt
为你将会收到陈述,你的任务是将其转换为标准英语
来给这两个模型,然后将句子提供为user_prompt
。
首先,让我们使用 GPT-3.5 实现并查看结果:
config = {
'engine': 'gpt-3.5-turbo',
'system_prompt':'You will be provided with statements, and your task is to convert them to standard English.',
'user_prompt':'she looks at sky yesterday whil brushed her hair',
'max_tokens':256
}
result = magicWand(config)
print(result)
结果:
GPT-3.5 修正后的句子
现在让我们用 GPT-4 来实现:
config = {
'engine': 'gpt-4',
'system_prompt':'You will be provided with statements, and your task is to convert them to standard English.',
'user_prompt':'she looks at sky yesterday whil brushed her hair',
'max_tokens':256
}
result = magicWand(config)
print(result)
结果:
GPT-4 修正后的句子
首先的观察是,两种模型在修正语法错误和提高原句可读性方面表现都非常好。第二个观察是,gpt-3.5-turbo
的表现几乎与gpt-4
一样,考虑到gpt-4
的更高成本,也许我们可以在未来的语法错误修正中仅使用gpt-3.5-turbo
。
在下一个例子中,我们将要求 GPT 识别句子中的机场代码!
4.5. 机场代码提取器
我必须承认这个任务有点奇怪,是我个人无法立即完成的,但我们将要求 GPT 从文本中返回机场代码,system_prompt
为你将获得一段文本,你的任务是从中提取机场代码
,user_prompt
为我在八月从西雅图飞往波士顿
。让我们使用gpt-4
或gpt-3.5-turbo
并比较结果,从 GPT-3.5 开始。
config = {
'engine': 'gpt-3.5-turbo',
'system_prompt':'You will be provided with a text, and your task is to extract the airport codes from it.',
'user_prompt':'I flew from Seattle to Boston in August.',
'max_tokens':256
}
result = magicWand(config)
print(result)
结果:
GPT-3.5 提取的机场名称
然后让我们用 GPT-4 来实现:
config = {
'engine': 'gpt-4',
'system_prompt':'You will be provided with a text, and your task is to extract the airport codes from it.',
'user_prompt':'I flew from Seattle to Boston in August.',
'max_tokens':256
}
result = magicWand(config)
print(result)
结果:
GPT-4 尝试提取的机场名称
正如你所看到的,结果在“技术上”都是正确的,但也非常不同。gpt-3.5-turbo
准确地返回了句子中西雅图和波士顿这两个城市名称的实际机场代码,尽管机场代码在提示中并未明确包含,正如gpt-4
所说。我的假设是gpt-3.5-turbo
经过微调,能够返回机场代码,而gpt-4
则更字面地对待任务,没有检索出机场代码——这两者都很有趣。
接下来,让我们继续识别提供文本中的命名实体。
4.6. 命名实体提取器
命名实体识别是一个常见的自然语言处理(NLP)任务,其中识别命名实体,如名称、地点、地址、组织等。通过一个例子,这将变得更容易理解。我们将给gpt-4
和gpt-3.5-turbo
提供system_prompt
为你将获得一段文本,你的任务是从中提取命名实体
和user_prompt
为我在八月从西雅图飞往波士顿。我记得我穿着崭新的耐克鞋,因为我对它们非常兴奋,以至于把我的 iPhone 忘在了黄色的凯美瑞出租车里
。我们期望模型能识别出如西雅图、波士顿、八月、耐克、iPhone 和凯美瑞等命名实体,但让我们先看看模型的表现,从 GPT-3.5 开始。
config = {
'engine': 'gpt-3.5-turbo',
'system_prompt':'You will be provided with a text, and your task is to extract the named-entities from it.',
'user_prompt':'I flew from Seattle to Boston in August. I remember I was wearing my brand new Nike shoes because I was so excited about them that I ended up leaving my iPhone in the yellow Camry cab.',
'max_tokens':256
}
result = magicWand(config)
print(result)
结果:
GPT-3.5 提取的命名实体
接下来让我们实现并查看 GPT-4 的结果:
config = {
'engine': 'gpt-4',
'system_prompt':'You will be provided with a text, and your task is to extract the named-entities from it.',
'user_prompt':'I flew from Seattle to Boston in August. I remember I was wearing my brand new Nike shoes because I was so excited about them that I ended up leaving my iPhone in the yellow Camry cab.',
'max_tokens':256
}
result = magicWand(config)
print(result)
结果:
GPT-4 提取的命名实体
结果非常好!两个模型都能够识别所有命名实体,而与gpt-4
不同,gpt-3.5-turbo
还能够返回命名实体的类型(例如,Seattle 和 Boston 是地点等)。因此,如果我有命名实体识别的任务,我更可能使用gpt-3.5-turbo
,因为它能够返回识别出的命名实体的类型,并且比gpt-4
便宜。
让我们进入下一个任务,请模型为我们进行翻译!
4.7. 机器翻译
这个任务不言而喻。我们将包含一个system_prompt
为You will be provided with a text, and your task is to translate it to French
,然后提供一个user_prompt
为Can you help me with this task?
,需要翻译成法语。我测试了两个 GPT 模型,结果相同,所以下面仅包含其中一个作为参考。
config = {
'engine': 'gpt-4',
'system_prompt':'You will be provided with a text, and your task is to translate it to French.',
'user_prompt':'Can you help me with this task?',
'max_tokens':128
}
result = magicWand(config)
print(result)
结果:
GPT-4 的英法翻译结果
这是一个预期中的好翻译!随意调整system_prompt
,让模型将user_prompt
翻译成其他语言吧!
接下来,我们将查看情感分析。
4.8. 情感分析
情感分析是另一项常见的 NLP 任务。在最基本的形式中,它告诉我们一个句子是带有积极、消极还是中立的情感。这对企业理解客户反馈非常有用。例如,一个模型可以处理所有的餐馆、产品或服务的客户评论,并返回正面、中性或负面的评论百分比,并用作该餐馆、产品或服务的总体评分!
让我们给gpt-4
一个system_prompt
为You will be provided with a text, and your task is to provide a nuanced sentiment analysis
和user_prompt
为That was such an exciting movie
。请注意,这句话听起来非常积极,因此我们想看看情感分析结果是否与预期一致。
让我们看看结果吧!
config = {
'engine': 'gpt-4',
'system_prompt':'You will be provided with a text, and your task is to provide a nuanced sentiment analysis.',
'user_prompt':'That was such an exciting movie!',
'max_tokens':128
}
result = magicWand(config)
print(result)
结果:
GPT-4 的情感分析结果
正如预期的那样,gpt-4
也认为这个句子是积极的,并添加了一个听起来像是模型决策背后理由的第二句。需要注意的是,“推理”可能真实也可能不真实,因为像 GPT-4 这样的 LLM 并不是完全确定性的——它仅仅表明模型将“激动人心”与享受、参与和积极情感关联起来——我们对于这些模型生成内容的方式知之甚少,但这是另一个话题。
接下来的任务,让我们请模型总结一个文本!
4.9. 文本摘要
文本摘要是另一项不言自明的任务。想象一下,当我们拨打客户支持热线时,代表希望阅读之前关于这个话题的客户支持与客户之间的通信。与其阅读整个通信记录,不如使用模型总结过去通信中的最重要部分,然后客户支持代表只需阅读模型提供的摘要。这为客户和服务提供商节省了宝贵的时间。
为了实现这一点,让我们向 GPT-4 提供 system_prompt
为 你将会收到一段文本,你的任务是对其进行总结,不使用原文中的词语
,并提供一段较长的文本作为 user_prompt
,其内容为 文本摘要是自动总结文本输入的任务,同时仍传达主要观点和要点。需要这种总结模型的商业直觉之一是人们阅读收到的文本通信(例如客户电子邮件),并且使用总结模型可以节省人工时间
。
让我们看看结果吧!
config = {
'engine': 'gpt-4',
'system_prompt':'You will be provided with a text, and your task is to provide a summary of it, without using the original words.',
'user_prompt':'Text summarization is the task of automatically summarizing textual input, while still conveying the main points and gist of the incoming text. One example of the business intuition behind the need for such summarization models is the situations where humans read incoming text communications (e.g. customer emails) and using a summarization model can save human time.',
'max_tokens':256
}
result = magicWand(config)
print(result)
结果:
GPT-4 的文本摘要结果
这似乎是对提供文本的一个很好的总结!我认为我自己也未必能做得更好。
接下来的任务是处理数据分析的那些人,即解析非结构化数据。
4.10. 解析非结构化数据
对于那些需要处理大量非结构化数据的用户,这个结果非常有用。我们可以让模型处理文本,然后将数据组织成不同的组。让我们看一个例子来更好地理解这一点。
我们将向 GPT-4 提供 system_prompt
为 你将会收到非结构化数据,你的任务是将其解析成一个 Pandas 数据框
,然后提供非结构化数据作为 user_prompt
,内容为 几天前我在火星上散步时,遇到了一群亚马逊员工。第一个人,杰克,来自波士顿,穿着黑色裤子、白色衬衫和黑色跑鞋。另一个人,吉尔,穿着长款靛蓝色连衣裙,脚踩浅蓝色凉鞋,来自西雅图。第三个人名叫约翰。我不记得他穿了什么,但我特别记得约翰来自新泽西州的纽瓦克。最后一个人是珍娜,她来自旧金山。珍娜穿着白色 T 恤和蓝色裤子,但我不记得她穿了什么鞋子
。
你可以看到 user_prompt
包括了个人的名字和他们穿的衣物。让我们看看模型如何组织这些信息。
config = {
'engine': 'gpt-3.5-turbo',
'system_prompt':'You will be provided with unstructured data, and your task is to parse it into a Pandas dataframe.',
'user_prompt':'As I was walking around Mars a few days ago, I came across a group of Amazon employees. The first one, Jack, was originally from Boston and wore black pants with a white shirt and black running shoes. Another one, Jill, had a long indigo-colored dress on with light blue sandals and was originally from Seattle. The third one was named John. I cannot remember what he was wearing but I particularly recall that John was from Newark, New Jersey. The last individual was Jenna and she was from San Francisco. Jenna had a white t-shirt and blue pants on but I cannot recall her shoes.',
'max_tokens':1024
}
result = magicWand(config)
print(result)
结果:
GPT-3.5 对非结构化输入的结构化输出
这非常令人印象深刻,做得很好!正如你所看到的,GPT-3.5 能够处理提供的文本,并将其组织(即解析)成每个人的相关列。这种解析在过去大多是手动完成的,当人们希望以表格格式分析数据时,这会非常有帮助。
作为最后的任务,我们将要求模型为我们编写一个 SQL 查询!
4.11. 编写 SQL 查询
我个人对 GPT 模型的 SQL 编写能力非常好奇,因为我在工作中频繁使用 SQL,并且在 Medium 上发布过以下 SQL 教程。
介绍
为了评估 GPT 模型的能力,我选择了我在 SQL 帖子中使用的一个示例,并要求 gpt-3.5-turbo
编写我自己准备的查询。为此,我们希望将要查询的表定义为 system_prompt
的一部分,然后像往常一样在 user_prompt
中定义任务。让我们看看这个示例的实现,然后是结果和与我自己编写的查询的比较。
config = {
'engine': 'gpt-3.5-turbo',
'user_prompt':'Write a SQL query which returns a rank of the salaries overall and also by gender from highest to the lowest salary.',
'max_tokens':1024,
'system_prompt':'''
Given the following SQL tables, your job is to write queries given a user’ s request.
DROP TABLE IF EXISTS salary;
CREATE TEMPORARY TABLE salary(city VARCHAR(30), average_salary int);
INSERT INTO
salary
VALUES
('san_francisco', '54500'),
('seattle', '54100'),
('new_york', '34400'),
('phoenix', '31800');
DROP TABLE IF EXISTS people;
CREATE TEMPORARY TABLE people(
person_id int,
name VARCHAR(30),
gender VARCHAR(30),
location VARCHAR(30),
birth_year int,
birth_month VARCHAR(30),
birth_day int,
job_title VARCHAR(30),
salary int
);
INSERT INTO
people
VALUES
(
'1',
'james',
'male',
'seattle',
'1984',
'9',
'15',
'software_developer',
'115000'
),
(
'2',
'mary',
'female',
'new_york',
'1992',
'1',
'13',
'financial_analyst',
'183000'
),
(
'3',
'john',
'male',
'san_francisco',
'1971',
'4',
'22',
'data_scientist',
'165000'
),
(
'4',
'patricia',
'female',
'phoenix',
'1971',
'8',
'15',
'physician',
'215000'
),
(
'5',
'michael',
'male',
'new_york',
'1966',
'1',
'13',
'retired',
'25000'
),
(
'6',
'jennifer',
'female',
'phoenix',
'1994',
'12',
'12',
'data_scientist',
'165000'
);
'''
}
result = magicWand(config)
print(result)
结果:
GPT-3.5 的查询
这非常令人印象深刻!这个查询涉及使用窗口函数,这些函数是 SQL 中较具挑战性的概念之一,但模型处理得相当好。以下是我在上面帖子中提供的解决方案作为参考,你可以看到模型的响应整体结构与我编写的查询非常相似!
我自己的查询
5. 结论
在这篇文章中,我们介绍了 OpenAI 的 API,它提供了对后台模型的访问,使 ChatGPT 能够执行各种任务。然后,我们使用 OpenAI 的 API 实现了 11 个聊天完成和图像生成的示例,并比较了 gpt-4
和 gpt-3.5-turbo
在这些任务中的表现。总体而言,我发现这两个 GPT 模型都非常强大,并认为它们是我个人使用的有用工具,而 DALL·E 是一个令人印象深刻的图像生成器。
感谢阅读!
如果你觉得这篇文章对你有帮助,请 关注我在 Medium 上 和 订阅 以接收我的最新帖子!
(所有图片,除非另有说明,均由作者提供。)
OpenAI 的网络爬虫和 FTC 失误
OpenAI 推出默认的自动同意爬虫以抓取互联网,而 FTC 进行了一项模糊的消费者欺骗调查
·
关注 发表在 Towards Data Science · 11 分钟阅读 · 2023 年 8 月 22 日
–
图片由 Giammarco Boscaro 提供,来源于 Unsplash
随着 AI 采用的急剧上升,数据专业人士越来越需要考虑数据来源。虽然最初一波高性能的 LLM 是使用一种普遍但有争议的数据抓取策略进行训练的,但这种有问题的做法最近受到关注,引发了诉讼和数据所有权的问题。本文提供了对这些法律概念的深入理解以及监管机构如何应对这一问题(剧透:效果并不显著)。
来自 Towards Data Science 编辑的说明: 虽然我们允许独立作者根据我们的 规则和指南 发布文章,但我们并不赞同每位作者的贡献。你不应在未寻求专业建议的情况下依赖某位作者的作品。详情请参见我们的 读者条款 。
上周,Open AI(ChatGPT 的制造商)正式宣布了他们的网络爬虫——这是一个从互联网上所有网站抓取内容的软件,这些内容随后用于 AI 模型训练。爬虫的存在并不令人惊讶,今天存在着几种合法的网络爬虫,包括索引整个互联网的 Google 爬虫。然而,这还是 OpenAI 首次明确宣布其存在,并提供了一个机制让网站选择退出被抓取。
请注意,爬虫程序默认是需要主动选择的,也就是说,你需要明确更改网站上的一段代码来要求爬虫程序不要抓取你的数据。主动选择/退出的默认设置是固定的,通常决定了大多数人的行为,因为大多数人不会费心去更改默认设置。这也是苹果 iOS14 隐私变更对数字广告行业产生重大影响的原因。
OpenAI 网络爬虫(来源:OpenAI)
那么,为什么还要提供退出选项呢?这可能是 OpenAI 对最近的诉讼采取的预防性措施,诉讼指控内容拥有者的版权受到侵犯(如果你想进一步了解,更多关于数据抓取的深度文章可以阅读)。ChatGPT 的竞争对手 Google Bard 面临类似挑战,但 Google 尚未宣布相应的解决方案——他们确实提出了如何升级robots.txt来解决这个问题的征求意见(以一些巧妙的公关写作呈现)。
在本文中,我们将深入探讨:
-
OpenAI 爬虫对内容拥有者的影响
-
FTC 目前对 OpenAI 的调查
-
我们目前所处的法律环境
-
为什么 FTC 追究 OpenAI 的做法是(又一个)错误的步骤
OpenAI 的爬虫对内容拥有者的影响
尽管公告为广告商提供了一个选项,可以阻止 OpenAI 的爬虫抓取他们的数据,但仍有几个问题:
-
默认情况下是选择加入的,这意味着 OpenAI 可以继续抓取,直到网站明确告诉他们不要抓取
-
关于在未经同意的情况下抓取数据用于模型训练时内容拥有者的权利,尚未有明确的法律裁决(这基本上适用于所有被迫默认选择加入的情况)
目前,有两个法律构架决定了语言模型是否可以在未获同意的情况下获取所有这些数据——版权和公平使用。
版权(在美国版权法第 102 条中)为特定类型的内容提供保护,但也有例外:
版权保护根据本标题存在于任何有形的表达媒介中固定的原创作品中,无论现在已知还是以后开发,从中可以被感知、复制或以其他方式传达,无论是直接还是借助机器或设备。作品的类别包括以下几类:(1)文学作品;(2)音乐作品,包括任何附带的文字;(3)戏剧作品,包括任何附带的音乐;(4)哑剧和舞蹈作品;(5)图画、图形和雕塑作品;(6)电影和其他视听作品;(7)声音录音;(8)建筑作品。
(b)在任何情况下,原创作品的版权保护不会扩展到任何想法、程序、过程、系统、操作方法、概念、原则或发现,无论以何种形式描述、解释、插图或体现于该作品中
例如,版权保护大多数原创作品(例如,如果你写了一篇原创的博客文章或书籍),但不保护广泛的思想(例如,你不能声称你是第一个写关于 AI 如何影响数据权利的人,因此这个想法属于你)。
版权保护的另一个例外是公平使用(美国版权法第 107 条):
对于受版权保护的作品的公平使用,包括通过复制或其他该节指定的方式使用该作品用于批评、评论、新闻报道、教学(包括用于课堂使用的多份副本)、学术研究或研究,并不构成版权侵权。
在确定任何特定情况下对作品的使用是否属于合理使用时,需要考虑的因素包括(1)使用的目的和性质,包括这种使用是否具有商业性质或是否用于非营利性教育目的;(2)受版权保护作品的性质;(3)使用部分相对于受版权保护作品整体的数量和实质性;以及(4)使用对潜在市场或受版权保护作品价值的影响。
例如,如果你从一篇研究论文中提取了内容并写了评论,这是可以的,并且你不会侵犯内容所有者的版权。当我从此页面链接到另一篇文章并添加该文章的引用文本时,情况也是一样的。
这两个概念的创建旨在保护内容所有者的权利,同时也允许信息的自由流动,特别是在教育、研究和评论的背景下。
我不是法律专家,但根据我对上述语言的研究/理解,在 AI 模型抓取训练内容时,情况变得模糊:
-
AI 公司通常会从内容所有者的网站上抓取全文(这些是受版权保护的),训练模型以学习“思想”/“概念”/“原理”(这些是不受版权保护的),然后模型最终会生成不同的文本。在这种情况下,内容所有者是否会获得版权保护?
-
由于训练后的语言模型现在最终用于商业目的(例如,ChatGPT Plus 是一款付费产品),这是否违反了内容所有者的版权(因为公平使用例外不再适用)?
目前尚未有法院对此作出裁决,因此很难预测结果。我这个非律师的观点是,第二种情况可能更容易解决:OpenAI 抓取了数据并用它创建了商业产品,因此他们不适用公平使用的例外。我想象第一种情况(模型是训练在“思想”上还是仅仅是原始文本上)则无人能知晓。请注意,这两个条件都需要对内容所有者有利,内容所有者只有在上述两个例外(“思想”例外或公平使用例外)都不适用于 OpenAI 时才能获胜。
我提到这个细微差别是因为在人工智能风险的范围内(并不详尽)——从内容所有者的权利、放大欺诈、自动化工作到 AGI / 人类毁灭——最紧迫的短期问题是内容所有者的权利,这一点可以从大量的诉讼和对内容平台的影响(例如 StackOverflow 的故事)中看出。
虽然像 FTC 这样的监管机构可以考虑真正的长期问题,并提出假设性/创造性的方法来应对这些风险,但它们的真正短期**潜力在于能够应对那些将在 5 到 10 年内影响我们的风险。例如版权侵权。**这引出了 FTC 在做什么。
FTC 对 OpenAI 的当前调查
在七月中旬,FTC 宣布正在调查 OpenAI。令其有趣(和令人沮丧)的地方在于FTC 调查的原因。ChatGPT 的制造商正在被调查,以评估该公司是否违反了消费者保护法律,将个人声誉和数据置于风险之中。这不合理?你并不孤单。让我们进一步了解一下这一情况的背景。
FTC 对 AI 监管的最明确立场在四月提出:“法律书中没有 AI 豁免条款,FTC 将积极执行法律,以打击不公平或欺骗性行为或不公平的竞争方式。” 随后出现了一些与诽谤相关的问题:电台主持人马克·沃尔特斯起诉 OpenAI因为 ChatGPT 指控他欺诈非营利组织,一位法学教授被ChatGPT 错误指控性骚扰。
这两种情况对相关人员来说都很糟糕,我对此表示同情。然而,语言模型(如 GPT)和基于它们的产品(如 ChatGPT)会“产生幻觉”,并且经常不准确。FTC 对调查的第一个前提是**—— ChatGPT 产生幻觉,从而造成声誉损害。**
在一次激烈的国会听证会上,一位代表(有理)询问 FTC为什么要追究诽谤和中伤,这些通常由州法律处理。FTC 主席丽娜·汗给出了一个复杂的论点:
韩表示,诽谤和中伤不是 FTC 执行的重点,但在 AI 训练中滥用个人私人信息可能是一种欺诈或欺骗行为,违反 FTC 法案。韩说:“我们关注的是,‘是否对人们造成了实质性的伤害?’伤害可以表现为各种形式。”
将整个论点总结起来——FTC 认为ChatGPT 的幻觉生成了不正确的信息(包括诽谤),这可能构成消费者欺骗。此外,敏感的用户私人信息可能被使用/泄露(基于一个漏洞 OpenAI 已迅速修复)。
作为调查的一部分,FTC 要求 OpenAI 提供一长串信息——包括他们的模型是如何训练的,使用了哪些数据来源,如何向客户展示他们的产品,以及因为识别到风险而暂停发布模型的情况。
问题是——在当前法律环境下,FTC 最好的做法是监管可能成为最大 AI 公司之一的 OpenAI 吗?
我们今天所处的法律环境
要批评 FTC 对 OpenAI 的战略,了解我们今天所处的法律环境是很有帮助的。我们不会深入细节,但可以简单地用反垄断历史作为例子:
-
在 1900 年代,巨大的企业集团(“信托”)出现,公共和私人权力的平衡转移到了这些公司手中。
-
为了应对这种情况,1890 年的《谢尔曼法》被通过,以对私人权力施加限制并维护竞争;这一法律被用于诉讼并打破从事反竞争行为(掠夺性定价、卡特尔交易、分销垄断)的“信托”。
-
大约在 1960 年代,法官们因根据法律精神而非法律字面进行裁决而遭遇大量反对。例如,解释《谢尔曼法》以确定一组公司是否“过度限制贸易”涉及主观性,法官们被指责参与司法激进主义。
-
为了引入客观性,芝加哥学派开创了消费者福利标准——“法院应 exclusively 以消费者福利为指导”(例如,垄断者明显提高价格是错误的,但对于其他行为,证明消费者伤害的责任在于监管者)。
-
这种标准今天仍然适用,并且是 FTC 和 DOJ 难以对付大型科技公司的原因之一——例如,FTC 无法主张 Google 提高价格,因为他们的大多数产品是免费的,即使 Google 从事了其他反竞争行为。
从中可以得出的结论是——我们今天继续在一个案件 heavily 基于“法律字面”而非“法律精神”的环境中运作。这一点,加上今天美国最高法院的组成,导致了对法律的相当保守的解释。
对于 FTC 来说,这意味着要接受这一现状并找出赢得案件的办法。FTC 和 DOJ 的运营模式(是合理的)是追求少数几个大案件并实施严格的执法,以便让其他公司在违法之前三思而后行。要实现这一点,FTC 需要在一些关键问题上取得重大胜利,并且需要在当前法律环境的限制下制定一个胜利策略。
为什么 FTC 对 OpenAI 的追击是(又一次)失误
FTC 在对抗大型科技公司方面屡次失败,我认为这些失败都可以归因于一种失败的“我们讨厌一切大型科技公司”的策略,类似于用锤子而非手术刀的方式来对付这些公司。
例如,FTC 采取了强硬手段阻止了$69B 的微软-动视收购案,但失败了(可以说是非常惨败)。FTC 辩称微软收购动视会杀死游戏市场的竞争。法官写了一份相当直白的判决,驳回了 FTC 的所有论点,这里有法官的评论之一:
没有内部文件、电子邮件或聊天记录与微软声明的意图相矛盾,即不将《使命召唤》独占到 Xbox 主机上。尽管 FTC 行政程序中进行了大量的发现,包括生产了近 100 万份文件和 30 份证词,FTC 没有发现任何一份与微软公开承诺将《使命召唤》提供给 PlayStation(和 Nintendo Switch)相矛盾的文件。
另一个强硬手段的案例是 FTC 试图阻止 Meta 收购 VR 公司 Within,他们失败了。他们为什么要这样做?他们想测试一下是否有意阻止在特定市场变得庞大之前的收购,而鉴于当前的法律环境,这个尝试不出所料地被驳回了。
FTC 对 OpenAI 的调查问题类似:
-
他们在追究(在我看来)一个相当琐碎的问题以及语言模型的已知局限——幻觉;他们应该将精力集中在 5 至 10 年内真正重要的人工智能问题上,例如版权。
-
尽管当前法律环境中多种“创造性”的法律途径被否决,他们仍在尝试另一种创造性的论点:幻觉 → 诽谤 → 消费者欺诈
对他们行动的宽泛解释是,他们想为他们的“人工智能不免于现有法律”立下先例,而这场徒劳的追逐战使他们从 OpenAI 那里获得了大量自我报告的数据(FTC 发布了20 页的要求)。
然而,鉴于他们一再采用暴力手段/任何大科技公司的非竞争性方法,并结合那些在法院被一再驳回的创意论点,我认为 FTC 在这个案件中并未赢得应有的信任。
结论
我绝对认为 OpenAI 应该受到监管。这不是因为他们的 LLM 会出现幻觉(当然会),而是因为他们公然未经许可使用创作者的内容。这不是因为它会改变过去,而是因为它将有助于为创作者建立一个健康的未来,在那里他们的内容所有权得到保护(法院是否会将现状视为版权侵权还有待观察)。
如果 FTC 继续重复其错误,采用“铁锤而非手术刀”的方法,这种情况不会改变。针对大科技公司的手术刀方法有明确的成功先例,其中最著名的是英国竞争与市场管理局。他们赢得的两个大案件集中在特定的反竞争机制上:阻止谷歌对其广告技术堆栈中的自身产品给予优待,以及允许其他支付提供商进行应用内支付。
如果 FTC 继续走当前的道路,他们的败绩将激励科技公司继续为所欲为,因为他们知道他们可以赢得官司。FTC 是时候反思自己的失败,向其他监管机构学习成功经验,并进行调整。
🚀 如果你喜欢这篇文章,可以考虑订阅我的每周通讯。 每周,我都会发布一篇深度分析 关于当前科技话题/产品策略的文章,阅读时间大约为 10 分钟。祝好,Viggy。
[## Unpacked | Viggy Balagopalakrishnan | Substack
针对当前科技和商业话题的深度分析,帮助你保持领先。每周送到你的邮箱…
thisisunpacked.substack.com](https://thisisunpacked.substack.com/?source=post_page-----a14047f4ff69--------------------------------)