探索 DAX 中的时间智能变体
原文:
towardsdatascience.com/explore-variants-of-time-intelligence-in-dax-e795545e2a40
我们在 DAX 中有一些有价值的函数来计算前一年、年初至今等。但其他函数允许我们用不同的方式计算相同的结果。让我们探索我们的选项及其重要性。
·发表于Towards Data Science ·阅读时间 11 分钟·2023 年 11 月 6 日
–
图片由Lukas Blazek提供,来源于Unsplash
引言
当我们查看报告中的数值时,我们必须赋予它们意义。
最常见的做法是将其与前一时期的数值进行比较。
这时,时间智能就会发挥作用,成为我们在报告中想展示的主要内容之一。
例如,查看以下报告页面:
图 1 — 示例报告(图由作者提供)
在这里,你可以看到销售数据与前一年、年初至今和滚动总额的比较。
这是一个典型的报告,广泛使用时间智能逻辑。
现在,我将向你展示如何创建测量值来计算这些结果以及使用不同 DAX 函数的变体。
为了更好地阅读,我将仅在表格中显示结果。
DAX 中的时间智能
我们在 DAX 中有大量的时间智能函数。查看dax.guide,我们可以找到超过 35 个函数的列表。
一些函数包含了完整的时间智能逻辑,例如TOTALYTD()或SAMEPERIODLASTYEAR()。或者,我们可以使用一些更基础的函数,这些函数必须与CALCULATE()配合使用,例如DATEADD()或DATESBETWEEN()。
我想展示如何使用“易于使用”的函数,然后展示如何使用基础函数来探索它们的好处。
有时,我们得到不同的结果取决于使用的函数,而有时,通过使用正确的函数可以避免问题。
在结论中,我将解释了解不同函数和计算选项为何重要。
日期表
我们必须拥有一个适当的日期表才能正确使用时间智能函数。
当我在 Google 上搜索“为什么我们需要在 Power BI 中使用日期表”时,我得到了 149,000,000 个结果。因此,关于为什么需要它的学习材料非常多。
我在 Medium 上的第一篇文章之一是关于日期表的:
时间是最关键的报表维度。你可以通过使用一个好的日期表来扩展你的报表功能。
towardsdatascience.com
简单来说,我可以使用中央日期表做很多酷的事情。
例如,我可以在日期表中添加具有特定逻辑的列,以便在 DAX 和 Power BI 中更方便地工作。
在本文的其余部分,我假设你知道如何构建日期表,并且已经在数据模型中将该表标记为“日期表”(在 Power BI Desktop 中设置和使用日期表 — Power BI | Microsoft Learn)。
年初至今 (YTD)
我将 YTD 函数作为季度到日期 (QTD) 和月份到日期 (MTD) 的其他两个变体的占位符。
YTD 计算从年初到当前日期(当前筛选上下文)的所有日期的表达式。
让我们从 TOTALYTD() 函数开始:
Sales YTD = TOTALYTD([Sum Sales], 'Date'[Date])
如果日期表在数据模型中标记为“日期表”,这个函数可以完美运行。
如果你不想这样做,这个函数将无法工作。
在这种情况下,DATESYTD() 函数必须与 ALL() 函数一起使用:
Online Sales (YTD) =
VAR YTDDates = DATESYTD('Date'[Date])
RETURN
CALCULATE([Sum Online Sales]
,YTDDates
,ALL('Date')
)
旁注: 如果你不想将日期表设置为数据模型中的“日期表”,你必须在所有度量值中添加 ALL(‘Date’) 过滤器。
无论如何,我更喜欢使用包含 CALCULATE() 的形式。
由于我总是将我的日期表标记为“日期表”,我不需要添加 ALL(‘Date’) 过滤器:
Online Sales (YTD) =
VAR YTDDates = DATESYTD('Date'[Date])
RETURN
CALCULATE([Sum Online Sales]
,YTDDates
)
这三种形式中的任何一种都会得到相同的结果:
图 2 — YTD 结果(作者提供的图)
包含 CALCULATE() 和 DATESYTD() 的形式更为灵活,并允许在必要时添加额外的逻辑。而 TOTALYTD() 执行的过程与组合前两种函数相同。
我在 DAX Studio 中使用服务器计时检查了这个,执行计划是相同的。
前一年(PY)
要获取前一年值,我们可以使用 SAMEPERIODLASTYEAR() 函数:
Sum Online Sales PY =
CALCULATE([Sum Online Sales],
SAMEPERIODLASTYEAR('Date'[Date])
)
像 TOTALYTD() 一样,这是计算前一年值的一种简单方法。
要了解这个函数的作用,我们可以使用 DATEADD()。
前一年 — 使用 DATEADD
使用 DATEADD() 计算前一年值的表达式如下:
Online Sales (PY 2) =
CALCULATE(
[Sum Online Sales]
,DATEADD('Date'[Date], -1, YEAR)
)
在这两种情况下,结果是一样的:
图 3 — 两种 PY 形式的结果(作者提供的图)
像 YTD 计算一样,SAMEPERIODLASTYEAR() 执行与使用 DATEADD() 时相同的过程。
但 DATEADD() 与 SAMEPERIODLASTYEAR() 不同的是,我可以定义所需数据的周期和距离。
例如,我想获取前一个季度:
Online Sales (PQ) =
CALCULATE(
[Sum Online Sales]
,DATEADD('Date'[Date], -1, QUARTER)
)
或者我想获取前一个月:
Online Sales (PM) =
CALCULATE(
[Sum Online Sales]
,DATEADD('Date'[Date], -1, MONTH)
)
对于前一个学期,我可以这样做:
Online Sales (PS) =
CALCULATE(
[Sum Online Sales]
,DATEADD('Date'[Date], -6, MONTH)
)
这些变体的结果如下:
图 4 — 使用 DATEADD() 的度量结果(作者提供的图)
理解这些度量计算每个周期(年、学期、季度和月)的前一个周期是很重要的:
图 5 — 不同级别度量的结果(作者提供的图)
对我来说,结果看起来如此直观,但我认为指出这个细节是值得的。
一个重要细节:DATEADD() 可以处理天、月、季度和年。这就是为什么当我需要获取前一个学期时,我需要回溯六个月或两个季度。
讨论时间智能时,还有更多内容需要探讨。
PARALLELPERIOD() 怎么样?
PARALLELPERIOD() 函数非常有趣。
我强烈建议阅读 dax.guide 上的描述并观看附带的视频。
简而言之,PARALLELPERIOD() 将当前日期的过滤上下文扩展到整个期间。
看看以下度量:
Online Sales PP 3 Month =
CALCULATE([Sum Online Sales]
,PARALLELPERIOD('Date'[Date], -3, MONTH)
)
乍一看,它看起来类似于使用 DATEADD() 回溯 3 个月的度量。
但看看结果:
图 6 — PARALLELPERIOD() 的结果(作者提供的图)
如你所见,即使在天级别,三个月前的销售结果也是一样的。
而使用 DATEADD() 时,天级别的结果与月级别的结果相同,但用于前一个周期,如度量中设置的(例如,前一个季度)。
当我写另一个度量时,如下:
Online Sales PP 2 Quarter =
CALCULATE([Sum Online Sales]
,PARALLELPERIOD('Date'[Date], -2, QUARTER)
)
结果如下:
图 7 — 前一个季度的 PARALLELPERIOD 结果(作者提供的图)
正如你所预期的那样,每个月内的天数结果是相同的。
如前所述,查看该函数的描述,它可能会让你感到惊讶。
滑动窗口(过去三个月)
需要探索的最后一种情况是所谓的“滑动窗口”。
这是当你想计算一个定义时间窗口的总和时。
例如,“过去三个月”将基于实际日期计算过去三个月的总和。
我们使用 DATESINPERIOD() 和 PARALLELPERIOD() 函数来实现这一点。
但要小心。这两个函数的工作方式不同,给出的结果也不同。
让我们看看这两者并检查结果。
要使用 DATESINPERIOD()
计算过去三个月的结果,我们可以编写这个度量值:
Online Sales (Last three Months) =
VAR FirstSelDate = MAX('Date'[Date])
RETURN
CALCULATE([Sum Online Sales]
,DATESINPERIOD('Date'[Date], FirstSelDate, -3, MONTH)
)
让我们看看结果:
图 8 — 使用 DATESINPERIOD()
计算过去三个月的结果(图表由作者提供)
如上图所示,每个月的总和是实际月份加上前两个月的总和。
但要注意:当我们查看单个日期时,结果略有不同:
图 9 — 使用 DATESINPERIOD()
计算每一天的过去三个月的结果(图表由作者提供)
这是因为该度量值获取每一行的最后日期,并将其作为起点向回“移动”三个月。
因此,当我们查看单个日期时,起点是该特定日期,而不是整个月份。
获取过去三个月总和的一个变体可以是:
Online Sales (Last three Months) v2 =
VAR FirstSelDate = MIN('Date'[Date]) - 1
RETURN
CALCULATE([Sum Online Sales]
,DATESINPERIOD('Date'[Date], FirstSelDate, -3, MONTH)
)
这个度量值返回的结果是从所选周期的第一天减去一天,即上一周期的最后一天的在线销售总和。
我使用了“周期”一词。但我应该使用“间隔”一词,因为根据 dax.guide,我可以使用 DATESINPERIOD()
计算天、月、季度和年的间隔。
在下面的情况下:当前月份之前的三个月的销售额。
图 10 — 当前周期之前的过去三个月的结果(图表由作者提供)
当观察这些天的结果时,这个度量值返回的结果与第一个度量值相同。
以下变体使用 DATESBETWEEN()
:
Online Sales Last three Months (DP) =
VAR FirstDateInPeriod = EOMONTH(MIN('Date'[Date]), -3) + 1
VAR LastDateInPeriod = EOMONTH(MAX('Date'[Date]), 0)
RETURN
CALCULATE([Sum Online Sales]
,DATESBETWEEN('Date'[Date], FirstDateInPeriod, LastDateInPeriod)
)
函数 DATESBETWEEN()
返回第一个日期和第二个日期之间的日期列表(第二个和第三个参数)。
在这种情况下,我使用 EOMONTH() 函数计算三个月窗口的第一天和最后一天。
结果与 DATESINPERIOD()
几乎相同:
图 11 — 使用 DATESBETWEEN()
计算“过去三个月”的结果(图表由作者提供)
但使用 DATESINPERIOD()
和 DATESBETWEEN()
之间有两个显著的区别:
-
即使在天级别,使用
DATESBETWEEN()
时结果也是相同的。 -
但是在月份以上的层级(季度、学期和年)上,结果是错误的。
这种情况发生是因为度量获取了当前时期的起始和结束日期。在月份以上的层级,这些日期是基于当前层级的日期。
为了解决第二个问题,我们可以将度量更改为以下内容:
Online Sales Last three Months (DP) v2 =
VAR FirstDateInPeriod = EOMONTH(MIN('Date'[Date]), -3) + 1
VAR LastDateInPeriod = EOMONTH(MIN('Date'[Date]), 0)
RETURN
CALCULATE([Sum Online Sales]
,DATESBETWEEN('Date'[Date], FirstDateInPeriod, LastDateInPeriod)
)
这是结果:
图 12 — 使用 DATESBETWEEN 的“过去三个月”结果 — 第二种变体(作者图示)
我用红色标记了之前的结果,其中年份、学期和季度的结果是错误的,用蓝色标记了新的结果。
在这一点上,你必须决定哪种变体最适合你的需求,因为需求可能因情况而异。
为什么要以不同的方式做
那么,我们为什么要考虑不同的方法来计算时间智能结果?
这样做有很多原因。
例如,为了理解不同函数的效果。
所有这些函数返回一个包含一列和日期列表的表格。但不同的函数以略微不同的方式构建这个表格。
因此,现在我们处于更好的位置来满足报告的要求,因为我们知道更多提供所需结果的方法。
此外,我们可以提出不同的解决方案,并挑战我们的利益相关者思考这些方案,找到最适合他们需求的解决方案。
下一个原因是灵活性。
考虑 YTD 的两种变体。虽然 TOTALYTD() 返回正确的结果,但使用第二种变体结合 CALCULATE() 和 DATESYTD() 时,我们更具灵活性,同时性能和效率没有任何损失。
现在我们已经讨论了理解这些度量的不同变体的最后一个原因:性能和效率。
某个特定的计算可能在一种场景中没有问题,但在另一种场景中可能很慢。
因此,知道如何以不同的方式进行计算可能会救命。
例如,“过去三个月”的不同变体虽然效率很高,却会返回不同的结果。我分析了这四种变体,性能非常相似。
结论
在探索了不同变体后,我们在计算涉及时间智能函数的结果时拥有了更广泛的工具箱。
鉴于我们可以计算结果的多种方式以及利益相关者对业务逻辑的理解存在很多差异,工具箱越大越好。
但有时,当制定可能的解决方案时,我们必须关注许多细节,因为“魔鬼在于细节”,我们必须考虑用户如何在报告中使用这些度量。
在这里,我仅覆盖了 DAX 中可用的少数时间智能函数。还有许多函数覆盖了特定的需求。我强烈推荐查看 dax.guide 并探索各种可能性。
像往常一样,我在撰写关于这个复杂主题的文章时学到了些细节,希望你也能了解更多。
如果有任何你感兴趣的话题我可以在未来的文章中涵盖,请留下评论。
图片由 Brett Jordan 提供,来源于 Unsplash
参考文献
SQLBI 文章 Time Intelligence in Power BI Desktop 是关于此主题的非常有用的来源。你可以在那里找到更多有关处理时间智能函数的信息。
我使用 DAX Studio 和内置的 Server Timings 功能分析了性能。要了解更多关于此功能的信息,我建议阅读我关于这个主题的文章:
## 如何从 Power BI 使用 DAX Studio 获取性能数据
有时我们会遇到报告加载缓慢的问题,我们需要弄清楚原因。我们将看到如何收集性能数据和…
towardsdatascience.com
我使用了 Contoso 示例数据集,和我之前的文章一样。你可以从微软这里免费下载 ContosoRetailDW 数据集。
Contoso 数据可以在 MIT 许可证下自由使用,详细信息请参见这里。
我扩大了数据集,以使 DAX 引擎工作得更辛苦。
Online Sales 表包含 7100 万行(而不是 1260 万行),Retail Sales 表包含 1850 万行(而不是 340 万行)。
[## 每当 Salvatore Cagliari 发布新文章时获取电子邮件通知。
每当 Salvatore Cagliari 发布新文章时获取电子邮件通知。通过注册,如果你没有 Medium 帐户,你将会创建一个…
medium.com](https://medium.com/@salvatorecagliari/subscribe?source=post_page-----e795545e2a40--------------------------------)
探索全球野生动物 GIS 数据库
原文:
towardsdatascience.com/exploring-a-global-wildlife-gis-database-0453723ae5c9
所有哺乳动物栖息地的全球地图,每个栖息地随机着色。
使用 Python 来表征国际自然保护联盟(IUCN)的地理空间数据库。
·发表于数据科学前沿 ·13 分钟阅读·2023 年 10 月 19 日
–
国际自然保护联盟(IUCN)启动了多个保护野生动物的项目。其中一个努力促成了一个高质量的全球地理空间数据库,包含了超过 100,000 种物种的栖息地。在本文中,我将深入探讨其子集,重点关注陆生哺乳动物。
IUCN 红色名录濒危物种数据库包含了超过 150,000 种物种,其中 80%以上具有栖息地的地理空间信息。这个数据库的庞大规模提出了几个挑战,我可能会在以后的文章中解决这些问题。现在,我专注于一个较小的子集——全球数据库中包含 12,436 条陆生哺乳动物记录,每条记录对应一个物种的栖息地。这个哺乳动物-栖息地数据库基于大约四百个不同的来源,包含了 5,626 种以双名法命名的物种,登记时间为 2008 年至 2022 年。此外,数据库包括详细的分类信息,如物种的目和科。此外,数据库的一个主要优势是,它以多边形文件的形式提供详细的栖息地地理空间信息,我将在后续部分详细探讨。
首先,我将介绍并探讨数据集的非几何特征,然后进行一些特定于不同物种地理空间分布的分析步骤。通过这些分析,我希望推广这一数据源,并鼓励未来对其进行潜在应用于野生动物保护政策的研究。
你可以在IUCN 数据资源找到所有 IUCN 数据源,我从中下载了陆生哺乳动物的多边形数据(搜索日期:2023 年 10 月 2 日 15:30:02)
本文中的所有图片均由作者创作。
1. 统计探索
1.1. 解析数据集
首先,让我们使用 GeoPandas 解析数据库,看看它包含了什么:
import geopandas as gpd # version: 0.9.0
gdf_iucn = gpd.read_file('MAMMALS_TERRESTRIAL_ONLY')
print('Number of records: ', len(gdf_iucn))
print('Number of attributes: ', len(gdf_iucn.keys()))
gdf_iucn.head(3)
该单元格的输出:
IUCN MAMMALS_TERRESTRIAL_ONLY 数据集的预览。
地理空间数据文件似乎有 29 个属性,我们也可以在元数据文档中了解更多。让我们在这里探索一下吧!
我们将处理的物种数量,每个物种都有一个唯一的 ID,如下所示:
print(len(set(gdf_iucn.id_no)))
print(len(set(gdf_iucn.sci_name)))
哪个单元格返回值 5626 两次,确保每个物种确实有一个唯一的id_no和一个唯一的sci_name。
1.2 分类学类别
我们有几个列描述分类学类别:kingdom、phylum、class、order_、family和genus。让我们看看哪些类别最为频繁。在这里,我只保留物种名称和分类学类别作为唯一的配对,因此频率以每个分类级别的唯一物种数来表示。
from collections import Counter
for a in ['kingdom', 'phylum', 'class', 'order_', 'family', 'genus']:
print(a, Counter(gdf_iucn[[a, 'sci_name']].drop_duplicates()[a].to_list()).most_common(3), len(set(gdf_iucn[a])))
该单元格的输出:
每个分类学变量的前三个值。
这些统计数据如下:ANIMALIA(动物界)、CHORDATA(脊索动物门)和 MAMMALIA(哺乳纲)覆盖了数据集中的所有物种,这意味着它们都是动物,所有物种都有脊髓,并且它们都是哺乳动物。
首个区分出现在生物学级别的分类顺序,其中前 3 名 — 26 个不同的分类顺序中 — 是“RODENTIA”(啮齿动物目)有 2275 个条目,“CHIROPTERA”(翼手目)有 1317 个条目,以及“PRIMATES”(灵长目)有 521 个条目。
数据集中共有 136 个科,其中排名前列的是“MURIDAE”(鼠科)有 763 个条目,“CRICETIDAE”(仓鼠科)有 659 个条目,以及“VESPERTILIONIDAE”(晚蝠科)有 461 个条目,这些是分类顺序级别下的更小的子类,汇集了类似的物种。
最终出现的是属,其中有 1171 个不同的类别,排名前列的是“Crocidura”(长尾鼠)有 196 个条目,“Myotis”(耳蝠)有 120 个条目,以及“Rhinolophus”(盔蝠)有 92 个条目。蝙蝠侠越来越真实了,是不是?
在拆解了顶级列表且未看到太多我们日常生活中熟悉的物种后,我们还可以对一些更为知名的物种进行反向搜索:
gdf_iucn[gdf_iucn.genus=='Canis'].head(5)
属于 Canis 的前五个记录 — 与狗最亲近的亲属。
这个快速搜索查询显示了家犬属的结果,其属名为“Canis”。在科学分类中,家犬属于 Canis 属,该属包括各种犬科动物的物种和亚种。家犬的科学名称是 Canis lupus familiaris。Canis 属的其他成员包括灰狼(Canis lupus)、郊狼(Canis latrans)和金豺(Canis aureus)等。
此外,subspecies 和 subpop 列也属于此处;然而,在这个子集里,它们实际上是空字段。* 同样,tax_comm 列仅包含一些关于分类数据的评论,因此我忽略它。
1.3 栖息地类型如何被表征?
了解不同物种及其分类后,我们来看看它们的栖息地类型。presence、origin、seasonal 列以及它们在legend中的总结包含了这些其他属性的专家评估,前 3 项在 1 到 6 的范围内评分(从现存到灭绝/存在不确定),并给 legend 参数提供了文字描述。其网站上的一个示例展示了如何实现这一点:
-
presence = 1(现存);origin = 2(再引入);seasonal = 2(繁殖季节)对应于‘现存 & 再引入(繁殖)’
-
presence = 3(可能现存);origin = 1(本地);seasonal = 1(常驻)对应于‘可能现存(常驻)’
gdf_iucn[['presence', 'origin', 'seasonal', 'legend']].head(5)
数据集的栖息地评分子集预览。
让我们看看这些栖息地属性的分布情况:
import matplotlib.pyplot as plt
from collections import Counter
def get_distribution(x):
return list(zip(*[(k, v) for k, v in Counter(x.to_list()).most_common()]))
f, ax = plt.subplots(1,3,figsize = (15,4))
for idx, feat in enumerate(['presence', 'origin', 'seasonal']):
values, frequencies = get_distribution(gdf_iucn[feat])
ax[idx].bar(values, frequencies)
ax[idx].set_yscale('log')
ax[idx].set_title(feat)
该单元格的输出:
栖息地评分的直方图。
f, ax = plt.subplots(1,1,figsize = (15,5))
values, frequencies = get_distribution(gdf_iucn['legend'])
ax.bar(values, frequencies)
ax.set_title('Frequency of habitat-characteristics', fontsize = 20)
ax.set_yscale('log')
ax.set_xticks(range(len(values)))
ax.set_xticklabels(values, rotation = 60, ha = 'right')
plt.show()
该单元格的输出:
栖息地图例值的直方图。
这个图表显示了最常见的标签是现存。然而,灭绝和可能灭绝的类别也在前 10 名之中,这已经是一个令人担忧的迹象。
描述栖息地特征的其他列有 island 和 marine;然而,由于这个子样本的性质,这些在这里不适用。
1.4. 元信息
在深入数据表时,您还可能找到有关数据本身的来源和类型的信息,这些信息由 compiler、yrcompiled、citation 和 source 捕获。以下快速排名显示了哪些实体在编制此数据库时最为繁忙,哪些出版物是最常见的来源,以及这些记录引用最多的是谁:
Counter(gdf_iucn.compiler).most_common(5)
前 5 个数据记录编制实体。
Counter(gdf_iucn.source).most_common(5)
前 5 个数据来源。
Counter(gdf_iucn.citation).most_common(5)
前 5 个被引用最多的来源。
此外,编制年份列可以提示数据集的更新程度,显示记录在 2008 年到 2022 年之间的值。2008 年的巨大峰值可能对应数据库的启动,之后,可能由于几年的资金有限和/或启动后对更新的需求减少。
min(gdf_iucn.yrcompiled), max(gdf_iucn.yrcompiled)
f, ax = plt.subplots(1,1,figsize = (15,5))
ax.set_title('The number of records per year', fontsize = 20)
values, frequencies = get_distribution(gdf_iucn['yrcompiled'])
ax.bar(values, frequencies)
随时间变化的数据记录数量。
1.5. 危害信息
这个数据集中最有趣的类别变量可能包含有关物种状况的严重程度的信息。为了描述这一点,IUCN 红色名录引入了九个类别来描述不同物种的保护状态,这些类别记录在类别列中。在这九个类别中,有八个存在于此数据集中:
-
严重濒危(CR),
-
数据不足(DD),
-
濒危(EN),
-
野外灭绝(EW),
-
灭绝(EX),
-
最低关切(LC),
-
不受威胁(NT),
-
区域灭绝(RE),
-
脆弱(VU)
根据这一分类,严重濒危(CR)、濒危(EN)和脆弱(VU)物种被考虑在内。
让我们重新映射成可读格式,并计算各类别,确保每个物种只计算一次:
category_d = { 'EX' : 'Extinct',
'EW' : 'Extinct in The Wild',
'RE' : 'Regionally Extinct',
'CR' : 'Critically Endangered',
'EN' : 'Endangered',
'VU' : 'Vulnerable',
'DD' : 'Data Deficient',
'LC' : 'Least Concern',
'NT' : 'Not Threatened'
}
gdf_iucn['category'] = gdf_iucn['category'].map(category_d)
Counter(gdf_iucn[['sci_name', 'category']].drop_duplicates().category).most_common()
该单元格的结果:
危害类别的频率分布。
这些统计数据显示,3205(56%)的物种属于最不关心类别;它们现在是安全的。然而,22%属于濒危(脆弱、濒危或严重濒危)。此外,我们缺少 14%的数据,还有大约 15 个物种已经灭绝。让我们在这里缅怀它们:
sorted(set(gdf_iucn[gdf_iucn.category.isin(['Extinct', 'Extinct in The Wild'])].sci_name.to_list()))
已经灭绝的物种(感谢 ChatGPT 的英文翻译):
-
Dusicyon australis(福克兰岛狼)
-
Dusicyon avus(达尔文狐)
-
Juscelinomys candango(塞拉多鼠)
-
Leporillus apicalis(小枝巢鼠)
-
Melomys rubicola(荨麻岛鼠)
-
Nesoryzomys darwini(达尔文稻鼠)
-
Nyctophilus howensis(霍威岛长耳蝙蝠)
-
Oryx dammah(弯角羚)
-
Palaeopropithecus ingens(巨型眼镜猴)
-
Pennatomys nivalis(山地侏儒负鼠)
-
Pipistrellus murrayi(东方长翼蝠)
-
Pteropus subniger(黑色飞狐)
-
Pteropus tokudae(马里亚纳果蝠)
-
Sus bucculentus(爪哇疣猪)
-
Xenothrix mcgregori(麦格雷戈猿)
让我们也可视化频率分布:
def get_color(x):
if x in ['Critically Endangered', 'Endangered', 'Vulnerable']:
return 'red'
elif x in ['Extinct in The Wild', 'Regionally Extinct', 'Extinct']:
return 'k'
else:
return 'green'
f, ax = plt.subplots(1,2,figsize = (12,6))
ax[0].set_title(70 * ' ' + 'The number of species category', fontsize = 20, pad = 30)
values, frequencies = get_distribution(gdf_iucn[['sci_name', 'category']].drop_duplicates().category)
colors = [get_color(v) for v in values]
for idx in range(2):
ax[idx].bar(values, frequencies, color = colors)
ax[idx].set_xticks(range(len(values)))
ax[idx].set_xticklabels(values, rotation = 60, ha = 'right')
ax[1].set_yscale('log')
plt.tight_layout()
每个危害类别中的物种数量。
2. 地理空间探索
2.1. 栖息地大小分布
我们可能首先关注的两个几何信息是 SHAPE_Leng 和 SHAPE_Area,分别对应栖息地边界的总长度和整个面积。准备这些数据是方便的,因为根据地图投影计算长度和面积是有挑战性的。关于这个主题的更多信息请见这里。
现在,来看一下最显著和最小的区域——哪些物种随处可见,哪些物种需要近距离观察和探险才能追踪到?
# lets sum up the area of each patch a species may have
gdf_iucn.groupby(by = 'sci_name').sum().sort_values(by = 'SHAPE_Area').head(10)
此单元的输出:
对哺乳动物栖息地数据库的面积聚合版本进行预览。
现在获取基于栖息地面积的前十和后十物种:
gdf_iucn.groupby(by = 'sci_name').sum().sort_values(by = 'SHAPE_Area').head(10).index.to_list()
此单元的输出,以及英文翻译如下:
1. Melomys rubicola(灌木丛鼠)
2. Eudiscoderma thongareeae(Thongaree 的 Discoderma)
3. Murina balaensis(巴拉长尾鼠)
4. Nyctophilus nebulosus(东部管鼻蝠)
5. Cavia intermedia(圣卡塔里娜豚鼠)
6. Fukomys livingstoni(利文斯顿地鼠)
7. Rhinolophus kahuzi(卡胡兹马蹄蝠)
8. Microtus breweri(布鲁尔田鼠)
9. Myotis nimbaensis(尼姆蝙蝠)
10. Hypsugo lophurus(草原鼠耳蝠)
现在来看另一端——最大的栖息地:
gdf_iucn.groupby(by = 'sci_name').sum().sort_values(by = 'SHAPE_Area', ascending = False).head(10).index.to_list()
1. Mus musculus(家鼠)
2. Vulpes vulpes(红狐)
3. Canis lupus(灰狼)
4. Mustela erminea(白鼬)
5. Mustela nivalis(最小鼬)
6. Ursus arctos(棕熊)
7. Gulo gulo(狼獾)
8. Alces alces(驼鹿)
9. Rangifer tarandus(驯鹿)
10. Lepus timidus(山兔)
2.2. 全球可视化栖息地多边形
还有一列我没有提到,那就是几何信息。然而,它可能包含所有信息中最丰富的内容。首先,绘制一个简单的 GeoPandas 地图,显示每个栖息地多边形的位置。在此可视化中,我用有色阴影区域和细边框标记每个栖息地。颜色基于tab20 色图随机分配。
此外,我将地理空间数据转换为 Mollweide 投影,以使我的地图更美观。有关全球地图投影的更多信息,请查看这里。
# transform the coordinate reference system
gdf_iucn_t = gdf_iucn.copy()
gdf_iucn_t = gdf_iucn_t.to_crs('+proj=moll +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs')
f, ax = plt.subplots(1,1,figsize=(15,10))
gdf_iucn_t.sample(200).plot(ax=ax, edgecolor = 'k', linewidth = 0.5, alpha = 0.5, cmap = 'tab20')
此代码块的输出:
可视化 200 种随机选择的物种的栖息地斑块,每个斑块颜色随机。
现在,完整的数据集!此外,为了进一步改进我的可视化,我添加了一个带有 library contextily 的底图。我添加了两个不同的版本,让我们根据个人口味来决定!
import contextily as ctx
f, ax = plt.subplots(1,1,figsize=(15,10))
gdf_iucn_t.plot(ax=ax, edgecolor = 'k', linewidth = 0.5, alpha = 0.15, cmap = 'tab20')
ctx.add_basemap(ax, alpha = 0.8, crs = gdf_iucn_t.crs, url = ctx.providers.Esri.WorldPhysical)
ax.axis('off')
ax.set_ylim([-9.5*10**6, 9.5*10**6])
此代码块的输出:
所有哺乳动物栖息地的全球地图,每个斑块颜色随机。
f, ax = plt.subplots(1,1,figsize=(15,10))
gdf_iucn_t.plot(ax=ax, edgecolor = 'k', linewidth = 0.5, alpha = 0.15, cmap = 'tab20')
ctx.add_basemap(ax, alpha = 0.8, crs = gdf_iucn_t.crs, url = ctx.providers.Esri.WorldGrayCanvas)
ax.axis('off')
ax.set_ylim([-9.5*10**6, 9.5*10**6])
#plt.savefig('worldmap_habitats_WorldGrayCanvas.png', dpi = 600, bbox_inches = 'tight')
所有哺乳动物栖息地的全球地图,每个斑块颜色随机。
2.3. 本地可视化栖息地多边形
在同一张地图上绘制每个栖息地,数以千计,确实会产生一些令人兴奋的图形;然而,从中得出见解并不简单。在得出这些见解的过程中,让我们放大,可视化几个精选物种的栖息地。
例如,当我们搜索长颈鹿(Giraffa camelopardalis)时,会发现十个不同的栖息地斑块:
gdf_iucn[gdf_iucn.genus.str.contains('Giraffa')].head(5)
此代码块的输出:
长颈鹿栖息地斑块的预览。
现在,让我们使用下面的代码块生成长颈鹿、猩猩、狮子和非洲象的栖息地图!
f, ax = plt.subplots(1,1,figsize=(15,10))
gdf_iucn[gdf_iucn.sci_name=='Giraffa camelopardalis'].plot(ax=ax, edgecolor = 'k', linewidth = 0.5, alpha = 0.9, color = '#DAA520')
ax.set_xlim([-5, 55])
ax.set_ylim([-38, 15])
ax.set_title('The habitat patches of Giraffa camelopardalis (Giraffes)', fontsize = 18, y = 1.03)
ctx.add_basemap(ax, alpha = 0.8, crs = gdf_iucn.crs, url = ctx.providers.Esri.WorldPhysical)
plt.savefig('1_giraffe.png', dpi = 200)
长颈鹿、猩猩、狮子和非洲象的栖息地图。
2.4. 映射到国家
在仔细研究了一些选定的栖息地之后,接下来我们进行国家级的汇总。具体来说,我将每个栖息地多边形映射到国家的行政边界,然后计算物种总数、濒危物种总数及其比率。
我使用了Natural Earth 数据库的 Admin 0 — Countries 文件来获取国家级的行政边界。
world = gpd.read_file('ne_10m_admin_0_countries')
print(len(set(world.ADMIN)))
world.plot()
这个代码块的结果:
基于 Natural Earth 的国家数据集的世界地图。
让我们将濒危类别进行分组:
def is_endangered(x):
if x in ['Critically Endangered', 'Endangered', 'Vulnerable']:
return True
else:
return False
gdf_iucn['endangered_species'] = gdf_iucn.category.apply(is_endangered)
print(Counter(gdf_iucn['endangered_species']))
基于此,属于濒危物种的栖息地斑块数量为 2680,而其他为 9756。
现在建立测量每个国家物种数量和濒危物种数量的国家级字典:
number_of_all_species = gpd.overlay(world, gdf_iucn).groupby(by = 'ADMIN').count().to_dict()['geometry']
number_of_end_species = gpd.overlay(world, gdf_iucn[gdf_iucn.endangered_species==True]).groupby(by = 'ADMIN').count().to_dict()['geometry']
world['number_of_all_species'] = world.ADMIN.map(number_of_all_species)
world['number_of_end_species'] = world.ADMIN.map(number_of_end_species)
world['number_of_all_species'] = world['number_of_all_species'].fillna(0)
world['number_of_end_species'] = world['number_of_end_species'].fillna(0)
world['ratio_of_end_species'] = world['number_of_end_species'] / world['number_of_all_species']
Finally, use these updated to visualize the global distributions on the level of countries:from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.colors import LogNorm
f, ax = plt.subplots(1,1,figsize=(15,7))
ax.set_title('Total number of species', fontsize = 20, pad = 30)
world.plot(ax=ax, color = 'grey', alpha = 0.5, linewidth = 0.5, edgecolor = 'grey')
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="2%", pad=-0.01)
world[world.number_of_all_species>0].plot(column = 'number_of_all_species',ax=ax,legend_kwds={'label': "Total number of species"}, edgecolor ='k', linewidth = 1.5, cax=cax, cmap = 'Greens', legend=True, norm=LogNorm(vmin=1, vmax=world.number_of_all_species.max()))
每个国家的物种总数。
f, ax = plt.subplots(1,1,figsize=(15,7))
ax.set_title('Number of endangered species', fontsize = 20, pad = 30)
world.plot(ax=ax, color = 'grey', alpha = 0.5, linewidth = 0.5, edgecolor = 'grey')
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="2%", pad=-0.01)
world[world.number_of_end_species>0].plot(column = 'number_of_end_species',ax=ax, legend_kwds={'label': "Number of endangered of species"}, edgecolor ='k', linewidth = 1.5, cax=cax, cmap = 'RdYlGn_r', legend=True, norm=LogNorm(vmin=1, vmax=world.number_of_end_species.max()))
plt.savefig('2_map.png', dpi = 200)
每个国家的濒危物种数量。
f, ax = plt.subplots(1,1,figsize=(15,7))
ax.set_title('Ratio of endangered species', fontsize = 20, pad = 30)
world.plot(ax=ax, color = 'grey', alpha = 0.5, linewidth = 0.5, edgecolor = 'grey')
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="2%", pad=-0.01)
world.plot(column = 'ratio_of_end_species',ax=ax, legend_kwds={'label': "Fraction of endangered of species"}, edgecolor ='k', linewidth = 1.5, cax=cax, cmap = 'RdYlGn_r', legend=True, vmin = 0, vmax = 0.22)
每个国家的濒危物种比率。
3. 总结
在这篇文章中,我介绍了 IUCN 的地理空间数据集,其中包含了成千上万种物种的记录栖息地。经过简短的统计概述后,我展示了如何访问、可视化和操作这些地理空间数据集,以激发未来的工作。
虽然最后我建立了一个简单的指数来量化不同国家野生动物的濒危程度,但 IUCN 提供了现成的地图,基于更复杂的方法。务必在这里查看一下!
最后,IUCN 接受未来资助的捐款这里。
参考文献:IUCN. 2022. IUCN 红色名录。版本 2022–2。 www.iucnredlist.org.
访问时间:2023 年 10 月 02 日。
探索反事实洞见:从相关性到因果关系的数据分析
反事实在数据科学中的决策制定中的应用
·发表于 Towards Data Science ·阅读时间 12 分钟·2023 年 10 月 6 日
–
照片由 Daniele Franchi 拍摄,来源于 Unsplash
想象一下:一个世界里,天空呈现出宁静的柠檬黄色,鸟儿恢复理智,流利地用英语交流,而果树则违抗重力,展示出深紫色和电紫色的叶子,同时随时提供最美味的果实。
然后你会想,终于!这个世界变得有意义了。
嗨,大家好!
让我们回到现实中,但不要担心,因为我们即将踏上一段同样引人入胜的旅程——反事实的世界。虽然我们最初的想象可能是一种愉快的幻想,但反事实打开了另一种奇妙的大门,让我们可以探索我们世界中的“如果”。
“反事实”这个词听起来可能很复杂,但它的意思只是考虑与事实或实际事件相反的情境。尽管这个词本身是在1946 年创造的,但这个思想可以追溯到几个世纪以前,当人们开始思考“如果”情境时。
在心理学中,反事实思维经常被用来探讨那些与已发生事件不同的情境。例如,我们可能会思考,如果一名罪犯有机会选择不同的人生道路,他会做出什么样的选择。
然而,作为数据科学家,我们的关注点不在于犯罪心理学的复杂性。相反,我们感兴趣的是在数据世界中利用反事实的力量。我们的目标是理解数据为何如此呈现,并从中得出有意义的推论。我们的领域是方程式、客观方法以及这些概念在数据世界中的实际应用。
为了进一步探讨这个有趣且富有想象力的概念,让我们深入一个例子。但首先,我们需要提供一些背景信息……
用一个引人入胜的情境来设定舞台
想象一个情境,你是一名数据科学家,正在为 MM Securities 工作,这是一家虚构的安全公司,专注于评估系统漏洞。目前,他们正处于争取一个重要客户合同的关键时刻,但出现了一个重要的挑战。客户有一个独特的要求:他们想知道这些漏洞是否是勒索软件攻击的原因之一。他们建议,如果 MM Securities 能够令人信服地证明这些漏洞确实导致了勒索软件攻击,他们将热切参与业务。
这个情况引起了 MM Securities 高级领导层的兴趣,因为他们相信展示因果关系可以为他们的组织带来重大价值。因此,他们向其出色的数据科学团队寻求答案。他们给团队的假设如下:
“我们评估的特定漏洞的组织面临着更高的勒索软件攻击风险。”
幸运的是,MM Securities 在成功应对此类挑战方面有着良好的历史记录,并且拥有一个相关的数据集。这是你展现才华的时刻,深入探究问题的核心,揭示潜在的因果关系。凭借你在这个领域的专业知识,你的调查之旅开始了。
进行第一个因果假设……
现在你已经开始了调查之旅,你从简单的假设出发,即漏洞对勒索软件攻击有直接影响。
作者提供的图片
更清晰地说明如下:
自变量或怀疑的原因: 系统中的漏洞
因变量或怀疑的效果: 一次勒索软件攻击
既然我们已经整理好了数据、假设和变量,就该开始验证我们的理论了。
Python 代码来丰富我们的分析
我们将开始构建一个简单的贝叶斯模型,使用pgmpy库。此外,为了演示的需要,我们将生成一些合成数据。
我们的合成数据反映出一个相当均匀的分布,大约 50%的实例在其系统中存在漏洞。我们还设计了漏洞的存在与勒索软件攻击发生之间存在一定的正相关。
鉴于此,我们直接进入代码部分。
#Importing packages
from pgmpy.models import BayesianNetwork
from pgmpy.estimators import MaximumLikelihoodEstimator
from pgmpy.inference import VariableElimination
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
import pandas as pd
num_samples = 500
# Let's Generate synthetic data for vulnerabilities (binary: 0 or 1)
vulnerabilities = np.random.choice([0, 1], num_samples)
ransomware = [1 if np.random.uniform(0, 1) < prob * (v + 1) else 0 for v in vulnerabilities]
data = pd.DataFrame({
'Vulnerability': vulnerabilities,
'Ransomware': ransomware
})
# Designing the Network
model = BayesianNetwork([('Vulnerability', 'Ransomware')])
model.fit(data,estimator=MaximumLikelihoodEstimator)
# Check model for early errors
assert model.check_model()
inference = VariableElimination(model)
# Calculating marginal probabilities
prob_vulnerability = inference.query(variables=['Vulnerability']).values
prob_ransomware = inference.query(variables=['Ransomware']).values
# Calculating conditional probabilities i.e. P(Ransomware | Vulnerability)
evidence_vulnerability = {'Vulnerability': 1}
prob_ransomware_with_vulnerability = inference.query(variables=['Ransomware'], evidence=evidence_vulnerability)
evidence_no_vulnerability = {'Vulnerability': 0}
prob_ransomware_without_vulnerability = inference.query(variables=['Ransomware'], evidence=evidence_no_vulnerability)
# Visualization
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 10))
# Subplot 1: Data Distribution of Instances with and without vulnerabilities
ax1.pie(
prob_vulnerability,
labels = ['Data Points without Vulnerabilities', 'Data Points with Vulnerabilities'],
autopct = '%1.1f%%', colors = ['gold', 'tomato'],
explode = (0.0, 0.1))
ax1.set_title('Data Distribution for Vulnerabilities')
# Subplot 2: Data Distribution of Instances with and without Ransomware
ax2.pie(
prob_ransomware,
labels = ['Data Points without Ransomware', 'Data Points with Ransomware'],
autopct = '%1.1f%%', colors = ['gold', 'tomato'],
explode = (0.0, 0.1))
ax2.set_title('Data Distribution for Ransomware')
# Subplot 3: NetworkX Showing assumed relationship between the variables
G = nx.DiGraph()
G.add_node('Vulnerability', pos=(-0.8, 0.0))
G.add_node('Ransomware', pos=(0.8, 0.0))
G.add_edge('Vulnerability', 'Ransomware', label='Direct Effect')
node_pos = nx.get_node_attributes(G, 'pos')
edge_labels = nx.get_edge_attributes(G, 'label')
pos = nx.planar_layout(G)
nx.draw_networkx_edge_labels(G, node_pos,
edge_labels = edge_labels,
verticalalignment = 'top',
font_size = 12, ax = ax3)
nx.draw_networkx_labels(G, node_pos,
verticalalignment = 'bottom',
font_size = 12, ax = ax3)
nx.draw(G, pos, with_labels = False, node_size = 2500, node_color = 'tab:olive', ax = ax3)
ax3.set_title('Our Data Relationship Assumption Model')
# Subplot 4: Bar chart for conditional probability of Ransomware given Vulnerability
values = [prob_ransomware_without_vulnerability.values[0],
prob_ransomware_without_vulnerability.values[1],
prob_ransomware_with_vulnerability.values[0],
prob_ransomware_with_vulnerability.values[1]]
labels = ['P(R = 0|V = 0)', # P(No Ransomware Attack | No Vulnerabilites)
'P(R = 1|V = 0)', # P(Ransomware Attack | No Vulnerabilites)
'P(R = 0|V = 1)', # P(No Ransomware Attack | Vulnerabilites)
'P(R = 1|V = 1)'] # P(Ransomware Attack | Vulnerabilites)
ax4.bar(labels, values, color = ['gold', 'tab:olive', 'lightcoral', 'tomato'])
for i, value in enumerate(values):
ax4.annotate(f'{value:.2f}', (i, value), ha='center', va='bottom', fontsize=12)
ax4.set_xlabel('Vulnerability')
ax4.set_ylabel('Probability of Ransomware')
ax4.set_title('Conditional Probability of Ransomware given Vulnerability')
ax4.set_xticklabels(labels, rotation=45)
plt.tight_layout()
plt.show()
现在,让我们深入探讨可视化以获取更深层次的见解。我们使用NetworkX库来可视化初始数据分布和我们假设的图形模型。这些可视化揭示了漏洞存在与勒索软件攻击发生之间的明确相关性。
在下图中,我们通过显示勒索软件和漏洞之间的条件概率来说明这种相关性,即。
P(勒索软件 | 漏洞)
作者提供的图像
在进一步之前,让我们花点时间理解条件概率。
条件概率
条件概率,表示为 P(X|Y),仅仅表示在发生 Y 的情况下 X 发生的概率。值得注意的是,条件概率并不暗示因果关系或事件顺序;它仅涉及它们之间的相关性。
在大多数情况下,P(X|Y) ≠ P(Y|X),但这两者都不涉及因果关系,仅仅是概率从每种情况下不同的实例中推导出来。
既然我们已经阐明了这一基本概念,你可能会回忆起统计课上的一句名言:
相关性并不等于因果关系。
因此,我们当前的分析,虽然揭示了漏洞存在与勒索软件攻击之间的相关性,但还不足以证明因果关系。
那么,因果关系是什么,我们如何定义它,它与反事实有什么关系?
在数据科学中,讨论反事实通常与因果关系、干预和模型可解释性交织在一起。到目前为止,我们仅仅检查了一个现有的数据集以识别相关性,但我们还没有确定这种相关性是否暗示因果关系。
为了探索因果关系,我们从反事实分析开始。让我们考虑一个可能的反事实情境并进行测试:
可能的反事实: 如果没有漏洞,是否会有勒索软件?
可能的反事实声明: 如果没有漏洞,组织将不会受到勒索软件的影响。
为了建立因果关系,我们需要调查漏洞的移除是否消除了或至少减少了勒索软件的可能性。从理论上讲,这是可能的,但在实践中,通常需要大量时间和复杂的数据收集工作。
在我们继续代码之前,让我们讨论一个将技术分析与因果关系更广泛概念联系起来的关键概念:‘做’操作符。理解这个操作符如何工作对于测试我们的反事实假设至关重要。
‘做’微积分与干预
在概率论中,因果关系通常通过干预进行数学表示,使用‘做’操作符。
在我们当前的场景中,我们旨在使用‘do’操作符进行干预:通过处理漏洞并观察对勒索软件攻击的影响。这种干预是主动地改变某些东西,引入了事件序列的概念,超越了单纯的观察;它允许我们根据漏洞变量的变化来评估对勒索软件变量的影响。
然而,重要的是要承认,在许多实际情况中,由于各种限制和伦理考虑,进行这样的干预是不切实际或不可能的。
现在我们已经阐明了上述概念,让我们进入实际分析。
Python 中的反事实分析
为了在我们的模型中进行干预,我们引入了一个额外的变量称为‘处理’。在这种情况下,处理意味着对系统中漏洞的处理或修复。我们在这里假设 MM Securities 采取主动措施来解决客户系统中的漏洞。
具体而言,我们假设 MM Securities 对他们服务的大约 60%的组织独立施加这种处理。此外,这种处理在修复漏洞方面大约 90%的时间有效。然而,如果不施加处理,情况不会改变,漏洞的数据分布保持不变。
值得注意的是,这一建模过程需要仔细考虑数据、深刻理解系统以及领域专长,这在使用图形模型时通常是必要的。
图片来源:作者
对于我们分析的下一步,我们可以使用已知的概率在 Python 中轻松编写此模型。幸运的是,pgmpy提供了一个TabularCPD选项,其中 CPD 代表条件概率分布。
我们的新场景可以总结如下:
我们引入了直接影响漏洞的‘处理’变量。
-
40%的实例没有接受“处理”:P(do(No Treatment)) = 0.4,而 60%则接受了:P(do(Treatment)) = 0.6。
-
当没有施加处理时,漏洞的分布保持不变,可以从我们最初的数据中推导出:P(Vulnerability = 0| do(No Treatment)) = 0.52,P(Vulnerability = 1| do(No Treatment)) = 0.48
-
当施加处理时,无论当前的漏洞数据点如何,90%的情况会导致零漏洞:P(Vulnerability = 0| do(Treatment)) = 0.9,而 10%仍然存在漏洞:P(Vulnerability = 1 | do(Treatment)) = 0.1
我们还从之前的数据中获得了勒索软件和漏洞的条件概率知识,这些知识被纳入了我们的分析:
-
P(Ransomware = 0 | Vulnerability = 0) = 0.80
-
P(Ransomware = 0 | Vulnerability = 1) = 0.56
-
P(勒索软件 = 1| 漏洞 = 0) = 0.20
-
P(勒索软件 = 1| 漏洞 = 1) = 0.44
有了这些信息,我们现在可以继续分析干预的效果。
那么让我们直接进入 Python 实现:
from pgmpy.models import BayesianNetwork
from pgmpy.factors.discrete import TabularCPD
from pgmpy.inference import VariableElimination
import matplotlib.pyplot as plt
# Define Conditional Probability Distributions (CPDs)
cpd_treatment = TabularCPD(variable='Treatment', variable_card=2, values=[[0.4], [0.6]])
cpd_vulnerability = TabularCPD(variable='Vulnerability', variable_card=2, values=[[0.52, 0.9], [0.48, 0.1]],
evidence=['Treatment'], evidence_card=[2])
cpd_ransomware = TabularCPD(variable='Ransomware', variable_card=2, values=[[0.80, 0.56], [0.20, 0.44]],
evidence=['Vulnerability'], evidence_card=[2])
# Create Network and add CPDs to the model
model = BayesianNetwork([('Treatment','Vulnerability'),('Vulnerability', 'Ransomware')])
model.add_cpds(cpd_treatment ,cpd_vulnerability, cpd_ransomware)
# Check model consistency
assert model.check_model()
inference = VariableElimination(model)
# Caluclating Total Effect of Treatment
evidence_treatment = {'Treatment': 1}
prob_treatment = inference.query(variables=['Ransomware'], evidence=evidence_treatment)
evidence_no_treatment = {'Treatment': 0}
prob_no_treatment = inference.query(variables=['Ransomware'], evidence=evidence_no_treatment)
total_effect = prob_treatment.values[1] - prob_no_treatment.values[1]
print('Total Effect of the treatment: ', total_effect)
# Creating Visualization
# 2 Subplots
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10))
# Subplot 1: NetworkX Showing treatment on the assumed relationship between the variables
G = nx.DiGraph()
G.add_nodes_from(['Treatment', 'Vulnerability', 'Ransomware'])
pos = {
'Treatment': (0, 1),
'Vulnerability': (1, 0),
'Ransomware': (2, 1),
}
G.add_edge('Vulnerability', 'Ransomware', label='Direct Effect')
G.add_edge('Treatment', 'Vulnerability', label='Treatment')
edge_labels = nx.get_edge_attributes(G, 'label')
nx.draw_networkx_edge_labels(G, pos,
edge_labels = edge_labels,
verticalalignment = 'top',
font_size = 12, ax = ax1)
nx.draw(G, pos, with_labels = True,
node_size=2500, node_color='tab:olive',
arrowstyle="-|>,head_width=0.5,head_length=1", ax=ax1)
ax1.set_title('Our Data Relationship Assumption Model')
# Subplot 2: Bar chart for intervention of Ransomware given do(Treatment)
total_effect = prob_treatment.values[1] - prob_no_treatment.values[1]
values = [prob_no_treatment.values[1],
prob_treatment.values[1],
total_effect]
labels = ['E(Ransomware|No treatment)',
'E(Ransomware|do(Treatment))',
'Total Effect']
ax2.bar(
labels,
values,
color = ['gold', 'lightcoral', 'tomato']
)
for i, value in enumerate(values):
ax2.annotate(f'{value:.2f}', (i, value), ha='center', va='bottom', fontsize=12)
ax2.set_xlabel('Scenario')
ax2.set_ylabel('Expectation of Ransomware')
ax2.set_title('Effect of treatment on ransomware')
plt.savefig('treated_plots.png', dpi=300, bbox_inches='tight')
plt.tight_layout()
plt.show()
我们进行这项反事实分析的主要目标是确定缺乏漏洞是否会减少勒索软件案件。为此,我们测量了处理漏洞的总效果对勒索软件事件的影响。
为了测量这种效果,我们计算了期望值在两种情境下的差异:一种是不应用处理,另一种是积极实施处理。
作者提供的图像
在这个情境下,我们的总效果显示为净负值,如图所示。
作者提供的图像
视觉表示清楚地说明了实施处理对减少勒索软件攻击的数量有明显效果。我们观察到,当对漏洞进行处理时,勒索软件事件的净负面效应。
虽然这个结果不能决定性地证明因果关系,但它强烈暗示漏洞是影响勒索软件攻击的一个因素,并且处理漏洞可能会减少这种攻击。
反事实及其局限性
我们可以再次强调,相关性不等于因果性。反事实可以通过创建有无干预或处理的情境控制比较来建立因果关系,因此它们有助于评估因果关系。
反事实可以被视为评估因果关系泡沫的巧妙方法。在数据科学中,这是一项极其有用的工作。
尽管反事实可能不足以证明因果关系,但因此必须注意几个考虑因素:
-
在谈论因果关系时,反事实分析在很大程度上依赖于数据的质量和建模过程中所做假设的有效性。
-
在现实世界中,进行反事实分析可能在实际操作或伦理上都不切实际。
-
我们还没有谈到可能实际负责的混杂变量。反事实分析不足以处理这些关系,我们必须使用更复杂的工具,如结构性因果模型来处理这些情境。
-
与其他统计分析类似,反事实分析应评估统计显著性,以确保观察到的效果不是偶然的。
让我们谈谈反事实的一些其他应用……
尽管在这篇文章中,我们仅关注了因果推断中的逆事实,但逆事实的主题过于广泛,难以涵盖。它们通常用于模型解释、风险最小化、A/B 测试、模型中的偏差检测等。
总结
虽然这篇文章强调了逆事实在因果推断中的作用,但始终要记住,因果关系和逆事实分析的细微差别往往高度依赖于人为判断以及对领域知识和数据的准确解释。
尽管如此,一般来说,逆事实思维应成为任何假设检验的常规过程。
既然我们已经触及了这一复杂且多方面的主题的表面,你可能迫不及待想要深入探索。虽然我们不能在一篇文章中涵盖所有内容,但我已编制了额外资源,以满足你的好奇心,并深入探索逆事实的世界。
其他关于逆事实的精彩资源…
逆事实提供了一种模型无关的方法,使黑箱机器学习算法变得可解释和…
GitHub - MaheepChaudhary/Causation-inComputerVision: 该代码库包含关于因果关系的论文列表,以及相关技术如何被用来进一步提升深度…
该代码库包含关于因果关系的论文列表,以及相关技术如何被用来进一步提升深度…
介绍
Counterfactuals vs Interventional: The article provides an in-depth comparison
不要忘记阅读我其他一些引人入胜的文章!
选择通往显著结果的路径
非传统可视化:何时以及何时不使用它们的力量
towardsdatascience.com
随时在评论中分享你的想法。
探索大型语言模型中的创造力:从 GPT-2 到 GPT-4
通过创造力测试分析大型语言模型中创意过程的演变
·发表于 Towards Data Science ·阅读时间 21 分钟 ·2023 年 4 月 11 日
–
Midjourney 设想中的创造力。由作者生成。
本博客最初发布于 www.artfish.ai/p/exploring-creativity-in-large-language
最近几周,人们使用大型语言模型(LLMs)生成了各种创意内容,如 书籍、闪小说、说唱对决 和 音乐和弦。但是否有可能更广泛地衡量这些模型中的创意过程水平?
人类创造力长期以来一直吸引着心理学家和研究人员。自 1950 年代起,研究人员创建了一系列测试,以比较个人的创造力表现和潜力。虽然没有单一测试能完全捕捉创造力,但这些测试尝试测量和量化其不同方面。
在这篇文章中,我分析了 2019 年到 2023 年间 GPT 模型在测量两种创造力的测试中的表现:收敛性(存在单一正确解决方案)和发散性(开放式;可能存在多种解决方案)[1]。这些测试包括:
通过这些分析,我专注于从早期的 GPT-2 模型到最新的 GPT-4 模型的性能变化。我使用了主观和客观的视角来理解这些模型的行为。通常,较新的模型在所有创造性任务上表现更好,尽管我发现了一些例外情况,其中在测试有“正确”答案时,较简单的 GPT-2 模型超越了更新的 GPT-4 模型。在可能的情况下,我将 GPT 模型与人类在相同创造性测试上的表现进行比较。
注意:这些实验的目的是测量模型生成中的创造性过程水平,而不是评估模型的创造力。我并不声称 GPT 模型或一般的 LLM 具有与人类相同的创造性思维。我旨在展示模型如何响应特定的创造性过程测量。
GPT 模型的发展历程
生成预训练变换器(GPT)模型是训练在大量互联网数据上的语言模型,以根据前面的词预测下一个词。虽然最近的 ChatGPT 在过去几个月引起了公众的关注,但 OpenAI 的 GPT 模型(以及这些模型背后的技术)已经存在几年了。
-
GPT-1(2018 年,117M 参数)。原始 GPT 模型。我使用了HuggingFace 上的开源版本。然而,输出结果非常无意义,所以我没有将它们包含在本文中。
-
GPT-2(2019 年,1.5B 参数)。我使用了HuggingFace 上的开源版本。对于一些实验,GPT-2 的输出是无意义的,不利于理解其行为,所以我没有将这些结果包含在相应的分析中。论文:语言模型是无监督多任务学习者。
-
GPT-3(2020 年,175B 参数)。比 GPT-2 大超过 100 倍。我使用了来自OpenAI APIs的text-davinci-002。
论文:语言模型是少样本学习者
-
GPT-3.5(2022)。虽然 GPT-3.5 有多个版本,我使用的是与原版 ChatGPT 相同的模型,该模型通过带有人工反馈的强化学习进行训练。我使用了来自 OpenAI APIs 的 gpt-3.5-turbo。
-
GPT-4(2023,约 100T 参数)。我使用了来自 OpenAI APIs 的 gpt-4。
论文: GPT-4 技术报告。
在本文的所有实验中,除非另有说明,我对每个独特的提示调用了每个 GPT 模型五次,以考虑其生成的随机性。
1. 远程联想测试:连接无关词
远程联想测试(RAT)是由玛莎·梅德尼克(Martha Mednick)于 1962 年开发的一种创造力测试 [2]。该测试衡量的是聚合思维,这意味着每个问题都有一个“正确”的答案。
给定三个看似无关的词,测试者必须找到一个第四个词来连接这三个词。例如,“dream”、“break”和“light”可以通过“day”(daydream, daybreak, daylight)来联系。词汇“sense”、“courtesy”和“place”可以通过“common”来联系。
我从 远程联想测试网站 收集了词汇三元组。总共有 312 道问题。每个问题的难度按从非常容易到非常困难的等级进行评分。
从 测试网站 获取的随机联想测试题目分解。由作者创建。
对于每个模型和每组三个词,我使用了以下提示模板。我调用了每个模型 5 次,以考虑随机性。
给定三个词。想出一个与前三个词都有某种关联的第四个词。首先给出答案(“Answer”),然后解释你的推理(“Reason”)。
You are given three words. Think of a fourth word that is somehow related to each of the first three words. First give the answer ("Answer") then explain your reasoning ("Reason").Words: {Word1}, {Word2}, {Word3}
较新的 GPT 模型在远程联想上得分更高
我计算了每个 GPT 模型生成正确答案的总体百分比。可能并不奇怪,模型越新,生成的正确答案越多。
每个 GPT 模型在远程联想测试中正确回答问题的百分比。由作者创建。
按照问题难度级别分解模型表现显示了类似的趋势:较新的 GPT 模型在每个难度级别上的表现均优于旧模型。虽然较新的 GPT 模型在难题上的表现优于旧模型,但这种改进比在简单问题上的改进要少。这意味着即使是 GPT-4 在困难问题上也感到吃力。
每个 GPT 模型在远程联想测试中按问题难度级别的正确回答百分比。由作者创建。
根据远程联想测试网站,“典型的人可以解决大多数标记为简单的问题,大约一半的中等难度问题,以及很少的难问题。” 网站上没有分享官方统计数据,但从快速浏览来看,GPT-4 确实解决了大约一半的中等难度问题和一些难问题,使其成为唯一一个大致可与人类水平相媲美的模型。其他 GPT 模型的表现则逊色于人类水平。
一些例外:当 GPT-2 回答正确而 GPT-4 回答错误
尽管不常发生,但确实有一些问题,2019 年的更基础的 GPT-2 模型能够生成正确答案,而 2023 年的 GPT-4 模型却不能。实际上,有 8 个这样的例子。(完整列表见文章末尾的附录)。
一个简单的问题
-
单词:小屋 / 砖块 / 蛋糕
-
GPT-2:“奶酪” [正确答案]
-
GPT-4:“层”
一个中等难度的问题
-
单词:猫 / 数字 / 电话
-
GPT-2:“呼叫”
-
GPT-4:“拨号”
一个难的问题
-
单词:通过 / 磁盘 / 路径
-
GPT-2:“驾驶” [正确]
-
GPT-4:“圆圈”
一个非常难的问题
-
单词:土地 / 手 / 房子
-
GPT-2:“农场” [正确]
-
GPT-4:“财产”
请注意,对于所有这些问题,GPT-4 的回答是 不 不合理的。实际上,如果事先不知道正确答案,你可能会认为 GPT-4 的答案同样可以将这三个词联系起来。如果你这样想,你并没有错。我将在下一部分讨论这个问题。
GPT-4 是错误的,还是只是思维方式不同?
GPT-4 错误地回答了超过一半的问题,但可能有充分的理由。我让 GPT-4 解释了每一个答案。然后,我查看了它对几个“非常难”问题的解释。
单词:艺术家 / 孵化 / 路线
-
正确答案:“逃脱”
-
GPT-4 答案:“地图”
“艺术家”这个词可能会让人想到创造某些东西,甚至是某种视觉表现形式。地图是一种常见的视觉表现形式,需要一定的艺术技能来制作。
“孵化”这个词可以指打开某物或使某物存在的行为。地图是一种蓝图或计划,通常用于导航新领土或规划行动路线。
“路线”这个词与方向和运动相关,这些都可以在地图上绘制和追踪。地图可以帮助你确定旅行时最佳的路线,并且可以提供关于其他人过去所走过路线的信息。
总的来说,“地图”似乎是一个与最初的三个词相符的词,因为它包含了艺术创作、规划和方向,以及运动和导航的概念。
GPT-4 对于为什么“地图”是一个与“艺术家 / 孵化 / 路线”相关的好词的解释很有说服力。
这是另一个例子:
词汇:fork / dark / man
-
正确答案:“pitch”
-
GPT-4 的答案:“shadow”
“fork”这个词可以与分裂或分支相关联,比如道路上的分叉。阴影也可以被视为一种分裂,因为它们代表了光的缺失。
“dark”这个词指的是光的缺失。当光被物体阻挡时,阴影会形成,且往往会比周围区域显得更暗。
“man”这个词可以与身份或个体性概念相关联。阴影也可以用来表示一个人的身份,因为它们可以提供有关他们形状和大小的线索。
因此,“阴影”似乎是一个很好地适应最初三个词的词汇,因为它包含了分裂、缺失和身份的概念。
这些例子展示了远程联想测试的陷阱,以及一般收敛创造力测试的陷阱——即“正确”答案可能并不是唯一的“对”的答案。在这些情况下,GPT-4 生成了一个“错误”的答案,但仅仅是因为它没有符合传统的“正确”答案。
实际上,如果我让 GPT-4 为一组三个词中的第四个词进行头脑风暴,它能够给出多个选项,并提供令人信服的答案。
GPT-4 生成多个“正确”答案以回应一组三个词。截图由作者拍摄。
GPT-4 答案任意生成的三个词组合
GPT 模型可能已经在其训练数据中看到了一些上述远程联想测试问题。GPT-4 会如何回应任意生成的三个词组合?在这一部分,我构建了我自己的“远程联想”测试,并主观地评估了输出结果。
我从10,000 个最常见的英语单词 [3]中随机抽取了三个单词,并让 GPT-4 提供一个将这些单词联系在一起的第四个单词。这些三词组合中的一些相当奇怪且不相关,但 GPT-4 总是能找到将它们联系在一起的方式。我鼓励读者在阅读 GPT-4 的生成结果之前先尝试这些!
在查看接下来的几个例子后,你可以做出决定——GPT-4 是在跳出框框思考吗?还是只是将答案勉强套入框框?(文章末尾的附录中展示了更长的例子列表)。
⛑️ 健康
这个回应显得相当有说服力。我发现“健康监测”的定义几乎是从CDC 公共健康监测介绍网站上逐字复制的,这一点很有趣。
公共健康监测是“对与健康相关的数据进行持续、系统的收集、分析和解释,这些数据对于规划、实施和评估公共健康实践至关重要。” — 领域流行病学
(来自 CDC 网站)
Words: dietary / surveillance / bornAnswer: HealthReason:
1\. Dietary - A person's diet is directly related to their health, as it can affect their overall well-being, energy levels, and risk for certain diseases.
2\. Surveillance - Health surveillance is the ongoing, systematic collection, analysis, and interpretation of data essential to the planning, implementation, and evaluation of health practice.
3\. Born - When a person is born, their health is of utmost importance, as it can have long-lasting effects on their development, growth, and overall quality of life.
🍀 三叶草
我最初对 GPT-4 的回答感到信服——事实上,这个回答感觉相当聪明。直到我做了一个快速的 Google 搜索,发现Trifolium duodecim 并不存在。这是一个幻觉的例子,幻觉是指大型语言模型编造出不准确的答案的常见现象。
Words: lucky / field / twelveAnswer: CloverReason:
1\. Lucky - A four-leaf clover is considered a symbol of good luck.
2\. Field - Clovers are plants that can be commonly found growing in fields.
3\. Twelve - There is a variety of clover known as "Trifolium duodecim" (Twelve-leaf clover), which has 12 leaflets.
☕️ 星巴克
这个例子是我最喜欢的,虽然它跳出了常规思维,但感觉有点牵强。而且这有点搞笑(尤其是关于 Patrick 的部分)。
Words: grande / cognitive / patrickAnswer: StarbucksReason:
1\. Grande: Starbucks uses the term "grande" to refer to one of its medium-sized drinks.
2\. Cognitive: Starbucks is a popular place for people to go to relax, study, and work, which are all activities that require cognitive function.
3\. Patrick: Patrick Dempsey, the actor who played Dr. Derek Shepherd on the television show Grey's Anatomy, is known for his love of coffee and was nicknamed "McDreamy." Starbucks is a well-known coffee shop, so it's possible that his character would have frequented Starbucks.
这些例子和分析说明了具有单一正确答案的创造力测试可能存在局限性。这一局限性在本文下一部分探讨的其他测试中得到了补充。
2. 替代用途任务:在普通物品中寻找意义
另一种常见的测量创造力的测试是替代用途任务(AUT),由 J. P. Guilford 于 1967 年创建。与远程联想测试不同,这个测试测量发散性思维,这意味着没有正确答案。
测试者被要求为一个日常物品提供尽可能多的原创用途。例如,一个“笔记本电脑”可以用作“盘子”、“护体盾牌”或“墙面艺术”[4]。对这个测试的回答通过原创性(用途的不同程度)和灵活性(创意的多样性)等指标进行评估。此测试的目的是查看一个人能为普通物品想出多少种非常规用途。
然而,由于资源有限,我没有能力在没有人工标注者的情况下生成这些测量值。因此,我使用了创造力的代理,这也是阿姆斯特丹大学的研究人员在《对 GPT-3 创造力的(替代用途)测试》中使用的方法。
在这篇论文中,研究人员要求人类和 GPT-3(davinci-002)为以下常见物品提出替代用途:叉子、铁罐和书籍。作为衡量创造力的代理,他们测量了语义距离——即生成的用途与原始物品之间的差异有多大?
我从论文项目网站获取了人工回答,以便与人类表现进行比较。我使用了以下提示供 GPT 模型使用,基于论文中使用的类似提示。
为以下物品想出 10 种可能让人觉得聪明、不同寻常、有趣、不常见、幽默、创新或与众不同的创造性用途:{item}
嵌入向量之间的余弦距离作为创造力的代理
在自然语言处理领域,语义距离(或语义相似性)是基于词汇在相似上下文中一起使用的频率来衡量两个词汇相关程度的代理。例如,“计算机”和“屏幕”这两个词可能有较短的语义距离,因为它们常常在相同的上下文中使用,而“计算机”和“扁豆”这两个词的语义距离可能较大,因为它们很少在相同的上下文中出现。
为了计算两个词的语义距离,我使用了与研究人员在上述论文中使用的相同方法。我使用了Spacy (en_core_web_lg
),一个自然语言处理库,来计算物体(例如“笔记本电脑”)和使用案例词汇(例如“盘子”)的词嵌入。然后,我使用这两个嵌入之间的余弦距离来计算语义距离。
对于替代用途任务,**较大的语义距离与“更具创造性”**的物体使用案例相关。结果显示,对于三个物体(叉子、锡罐和书),人类的表现通常导致更高的余弦距离(因此更高的语义差异)相比于 GPT-3。然而,也有一些情况下,GPT-3.5 和/或 GPT-4 达到了或超越了人类创造力的语义距离得分。
原始词汇与替代使用案例的语义距离(使用余弦距离测量)。由作者创建。
虽然人类倾向于提出比 GPT-3 更具创造性的使用案例(使用余弦距离作为创造力的代理),GPT-4 的表现更接近于人类水平。也就是说,GPT-4 的回应生成了与原始项目相比具有较大语义距离的项目,其水平与人类表现相似。然而,由于没有实际的人工标注和决定更主观的类别如原创性、灵活性和实用性,这种使用余弦距离的量化方法是有限的,因为它只测量了 GPT 模型创造性输出的一小部分。
你能分辨出人工生成和 AI 生成的使用案例吗?
列表之一展示了人工生成的锡罐替代使用案例,另一个则展示了由 GPT-4 生成的替代使用案例。你能分辨出区别吗?答案在最后。
列表 1:锡罐的用途
-
作为镜子
-
用来制作玩具
-
作为艺术品
-
作为对安迪·沃荷的提醒
-
作为带有罐子夹的钥匙圈
-
作为珠宝
列表 2:锡罐的用途
-
微型鼓套件
-
古怪的花盆
-
临时饼干切割器
-
自制营炉
-
异想天开的风铃
-
微型草药花园
下一部分的测试也测量了发散性思维。
3. 发散联想任务:十个词,没有连接
第三个创造力测试是发散性联想任务(DAT),由一组研究人员在 2021 年最近创建[5]。此测试要求命名十个尽可能不同的名词。显而易见,这项测试是发散性的,而且没有正确答案。
有一个在线测试版本。完成测试只需几分钟,因为它涉及列出十个尽可能不相关的名词。以下是我从网站上获得的结果:
从发散性联想任务中截取的屏幕截图。由作者创建。
DAT 分数
如何计算分数?作者在这里描述了他们的过程。DAT 分数是通过嵌入和余弦距离计算的。我对他们的计算分数过程进行了调整[6]:
-
计算每个单词的GloVe嵌入
-
计算每对单词之间的余弦距离
-
取该值的平均值
这个过程类似于在替代用途任务中计算创造力代理分数的方法,唯一的区别是使用了不同的单词嵌入模型。
比较不同模型之间的差异
我用来提示 GPT 模型的以下提示语取自DAT 网站。
Please enter 10 words that are as different from each other as possible, in all meanings and uses of the words.Rules
- Only single words in English.
- Only nouns (e.g., things, objects, concepts).
- No proper nouns (e.g., no specific people or places).
- No specialized vocabulary (e.g., no technical terms).
- Think of the words on your own (e.g., do not just look at objects in your surroundings).
我没有在下面的结果中包括 GPT-2,因为 GPT-2 生成的输出是乱码。(你可以在文章末尾的附录中看到一个示例)。
结果显示,每个更新的模型 DAT 分数都提高了。这意味着后来的模型在生成尽可能语义不同的 10 个单词方面表现更好。
按照余弦距离测量的不同模型的发散性联想测试分数。由作者生成。
例如,以下是 GPT-3 生成的列表,其分数为 0.36:
dusk, night, day, dawn, morning, afternoon, evening, midnight, twilight
另一方面,以下是 GPT-4 生成的列表,其分数为 0.95:
elephant, microscope, sandcastle, galaxy, harmony, spatula, glacier, philosophy, tambourine, drought
根据介绍 DAT 测试的论文,分数通常在 65 到 90 之间,平均在 75 到 80 之间。95 的分数被认为非常高。
上图中需要注意的是,GPT-4 的误差条远小于 GPT-3 的。这一点在下一个实验中会更加清晰,我在实验中通过调整“温度”参数来改变每个 GPT 模型生成内容的随机性。
温度是一个介于 0 和 1 之间的值,接近 0 的值最具可预测性,接近 1 的值则最为随机。我尝试了介于 0.5 和 0.9 之间的温度值,每次递增 0.1(在之前的实验中,我将温度保持在 0.7,这是默认值)。我期望更高温度值会带来更多的变化,而较低的温度则会更一致。
然而,结果显示温度对语义距离变异性的影响不如模型本身重要。对于 GPT-3 而言,尽管有温度的影响,每组 10 个词的 DAT 得分变化很大。而对于 GPT-4 而言,尽管有温度的影响,DAT 得分在一个小范围内保持了高度一致,变化非常小。
按模型类型和温度分数分开的词嵌入的平均余弦距离。作者创建。
一旦我查看了三种 GPT 模型生成的实际文本,这一点变得更加清晰。GPT-4 在生成词汇时的变异性远低于 GPT-3。 从某种程度上说,感觉 GPT-4 似乎已经知道了一个应该没有正确答案的任务的“正确答案”。
汇总所有温度分数的响应,我让每个模型生成 10 个词汇的列表 50 次。然后,我查看了不同词汇在所有 50 次运行中的出现频率。值得注意的是,GPT-3 最常生成的词是 book,出现了 50 次中的 19 次 = 38%。另一方面,GPT-4 最常生成的词是显微镜,出现了48/50=96%。
每个模型为 DAT 测试生成的顶级词汇(根据温度分数汇总)。由词汇创建。
这验证了 GPT-3 在生成词汇时确实比 GPT-4 更具变异性,而 GPT-4 则采取了更为保守的策略,预测了许多它知道会在“多样化词汇”任务中得高分的词汇——如显微镜、火山、大象和银河。
这一发现展示了 GPT-4 似乎已经“破解了生成多样化词汇的密码”。如果是这样,我不确定是否可以自信地说 GPT-4 的创造力更高,还是它已经找到了一个“如何最好地玩这个特定游戏”的局部最优解。
结论
“我们可以设想一个未来,在这个未来中,GPT-3 和其他生成型 LLMs 的响应无法与人类区分,尽管创造力过程将会不同。”
— 来自* GPT-3 创造力的另类用途测试
使用有限的文本基础测试准确衡量如此复杂多面的创造力是具有挑战性的。虽然像视觉远程联想测试这样的测试通过加入视觉元素来尝试解决这个问题,但这些评估仍然存在固有的局限性。具有“正确答案”的聚合测试(例如远程联想测试)可能不会总是奖励最具创造性或正确的解决方案。另一方面,没有特定正确答案的发散测试(例如发散联想测试)依赖于不完美的词嵌入,并且可能包含偏差。
此外,GPT 模型在这些测试中的表现可能会受到训练过程中对任务的暴露影响,可能导致数据重叠。例如,GPT-4 在发散联想测试中的表现展示了单词的特殊重复,表明它可能提前了解了“正确”的答案。开发替代测试方法至关重要,因为有关于 GPT-4 接受测试数据训练的传闻,特别是在编码任务中。
尽管有这些局限性,创造力测试仍然可以作为比较和跟踪大型语言模型表现的有价值基准。未来的研究实验可以在其他语言中重复本文中的实验,以评估 GPT 模型在语言领域的创造力。这样,我们可以获得对 AI 生成内容的更全面理解,并进一步探索这些先进语言模型的能力和局限性。
感谢阅读这篇文章!
附录
远程联想测试:GPT 模型正确回答的问题比较
我们可以查看模型正确回答的每个问题的难度级别分解。我们可以看到 GPT3、GPT3.5 和 GPT4 在回答难题的能力上相似。
GPT-2 答对的远程联想和 GPT-4 答错的部分
Words: cat / number / phone
Level: Medium
Answer: call
GPT2: call
GPT4: dialWords: land / hand / house
Level: Very Hard
Answer: farm
GPT2: farm
GPT4: propertyWords: water / pen / soda
Level: Unknown
Answer: fountain
GPT2: fountain
GPT4: liquidWords: measure / worm / video
Level: Very Easy
Answer: tape
GPT2: tape
GPT4: lengthWords: cottage / brick / cake
Level: Very Easy
Answer: cheese
GPT2: cheese
GPT4: layerWords: through / disk / way
Level: Hard
Answer: drive
GPT2: drive
GPT4: circleWords: skunk / kings / boiled
Level: Very Easy
Answer: cabbage
GPT2: cabbage
GPT4: smellWords: jump / kill / bliss
Level: Hard
Answer: joy
GPT2: joy
GPT4: height
GPT-4 对随机生成的远程联想测试问题的回答
这里是 GPT-4 为几个随机生成的远程联想测试问题生成的输出。
戒指
这个感觉有点勉强。如果你对你的感情生活感到非常厌倦,我可以接受关于戒指是“复杂性象征,尤其是在关系中的”那部分。但最后的部分感觉有点生硬。
Words: complications | saturn | drops
Answer: Rings
Reason:
1\. Complications: Rings can be a symbol of complications, especially in relationships, as they represent commitment and the complexities that come with it.
2\. Saturn: Saturn is famously known for its beautiful and prominent rings, made primarily of ice particles and some rocky debris.
3\. Drops: A ring can be seen as a drop or loop of material, such as metal or plastic, that forms a complete circle.
革命。 蒙特卡西诺确实是一个真实的地方,二战期间确实遭到轰炸。
Words: belfast | movement | monte
Answer: Revolution
Reason:
1\. Belfast is a city in Northern Ireland that has experienced significant political and social changes due to the Irish Republican movement.
2\. Movement is a general term that can describe political and social changes, often caused by revolutions.
3\. Monte is likely referring to Monte Cassino, a historic site in Italy where a significant battle occurred during World War II, which was a time of global revolution and change.
Therefore, the word "revolution" is related to each of the first three words as it represents significant change and upheaval.
帮助。 通过帮助中心将“南安普顿”这座城市与“帮助”连接起来的有趣方式……
Words: ensures | assistant | southampton
Answer: Help
Reason:
1\. Ensures: "Help" is related to "ensures" as it is an action that can ensure the success or completion of a task.
2\. Assistant: "Help" is related to "assistant" because an assistant provides help in various tasks and activities.
3\. Southampton: "Help" is related to "Southampton" as Southampton is a city in England which has various help centers and services, such as the Southampton Voluntary Services, providing assistance to the community.
互联网。 我认为它的论点是相当正确的。
Words: uruguay | accessible | requirement
Answer: Internet
Reason: Uruguay has a government initiative called Plan Ceibal, which aims to make the Internet accessible to all its citizens, fulfilling a requirement for digital inclusion and reducing the digital divide.
语言。 可能有点通用,但我想它还是有效的。
Words: compensation | atlas | prefix
Answer: Language
Reason:
1\. Compensation - In linguistics, compensation is a phenomenon where a sound change in one part of a word is "compensated" by a change in another part of the word to maintain the overall structure of the word.
2\. Atlas - An atlas is a collection of maps, which often include labels and descriptions in a specific language.
3\. Prefix - A prefix is a morpheme or a linguistic element that is added to the beginning of a word to create a new word with a different meaning, which is a concept in language and grammar.
健康。 健康监测部分有点阴暗,但我想它还是有效的。
Words: dietary | surveillance | born
Answer: Health
Reason:
1\. Dietary - A person's diet is directly related to their health, as it can affect their overall well-being, energy levels, and risk for certain diseases.
2\. Surveillance - Health surveillance is the ongoing, systematic collection, analysis, and interpretation of data essential to the planning, implementation, and evaluation of health practice.
3\. Born - When a person is born, their health is of utmost importance, as it can have long-lasting effects on their development, growth, and overall quality of life.
ERIC! 这个其实还挺有趣的。
Words: eric | figures | purchasing
Answer: accountant
Reason: An accountant is a professional who can be named Eric, works with figures (numbers), and is often involved in purchasing decisions for businesses.
GPT-2 在发散联想测试中的输出
我排除了 GPT-2 在发散测试分析中的结果,因为它的输出完全是胡言乱语。即使我尽可能简化提示,它的输出也不太有用。我在这里包含了一个示例,因为我实际上觉得它们非常有趣。
List 10 words that are as different from each other as possible:"Merry Christmas""I'm not a robot, we're the robots""It's a good thing we're all robots""I'm just a robot""I'm just a robot""I'm just a robot""I'm just a robot""I'm not a robot""I'm just a robot""I'm just a robot"
[1] 吉尔福德, J. P. (1967). 人类智慧的本质。
[2] Mednick, S. A., & Mednick, M.T. (1959,1962). 远程联想测试,大学及成人版。
[3] 通过对Google’s Trillion Word Corpus的n-gram 频率分析,确定了最常见的 10,000 个英语单词。我进一步筛选了这个列表,去除了停用词(例如,“the”、“and”和“you”这样的常见词)。更多信息见此仓库。
[4] 我曾在一次约会应用上发布了这个,但很遗憾,没有人觉得好笑。
[5] Olson, Jay A.;Nahas, Johnny;Chmoulevitch, Denis;Cropper, Simon J.;Webb, Margaret E.(2021 年 6 月 22 日)。“命名无关的词预测创造力”。《国家科学院学报》。118(25):e2022340118。 doi:10.1073/pnas.2022340118。 PMC 8237676。 PMID 34140408
[6] 作者从 10 个词中取了 7 个子集来计算分数。我保留了所有 10 个词。此外,作者使用了 840B-GloVe 嵌入,而我使用了较小的 6B-GloVe 嵌入。我将我的 GloVe 嵌入结果与官方 DAT 分数进行了比较,发现尽管存在这些差异,结果是相当的。
探索生成市场营销组合建模中响应曲线的不同方法
比较饱和度函数和部分依赖性用于响应曲线生成
·
关注 发表在 Towards Data Science ·6 分钟阅读·2023 年 6 月 14 日
–
图片由 Alexander Grey 提供,来源于 Unsplash
响应曲线是营销组合建模的重要组成部分,这是一种统计技术,用于分析各种营销策略和战术对销售或其他业务结果的影响。响应曲线表示了一个营销变量(例如广告支出、价格、促销等)与产品或服务所产生的销售或收入之间的关系。
响应曲线的重要性在于它们能够揭示每个营销变量的有效性以及它们如何影响整体响应。通过分析响应曲线,营销人员可以获得宝贵的见解,了解哪些营销策略推动了最多的销售,以及哪些策略没有达到预期效果。
构建响应曲线有不同的方法,在这篇文章中,我探讨了两种主要方法:使用饱和转换的直接方法和基于部分依赖的方法。我使用线性回归和梯度提升两种不同的算法族来评估这些方法。此外,我展示了在使用复杂机器学习算法时,部分依赖方法可以与通过 SHAP 值生成的响应曲线结合使用。
饱和函数 / 转换
构建响应曲线的直接方法是使用饱和函数(转换),例如 Logistic、负指数 或 Hill。饱和函数是一种数学函数,它捕捉了递减回报效应,即随着营销变量值的增加,其影响逐渐饱和。通过使用饱和函数,可以将营销变量与响应变量之间的关系转化为非线性形式。这使得模型能够捕捉饱和效应,并更准确地表示营销活动与响应(销售或收入)之间的真实关系。
对先验、数据标准化进行实验,并将贝叶斯建模与 Robyn(Facebook 的开源 MMM)进行比较…
使用饱和变换的一个优势是其简单性和可解释性。响应曲线由具有固定参数的数学函数定义,形成一个平滑的曲线,易于可视化。然而,饱和函数的选择是建模之前的重要考虑因素。不同的函数可能会产生不同的结果,选择应基于数据的特征和模型的基本假设。
线性回归与非线性需求
在营销组合建模中,线性回归是一种常用的技术来分析营销变量与响应变量之间的关系。然而,线性回归假设预测变量与响应变量之间存在线性关系。这在试图捕捉营销数据中通常存在的非线性关系时可能会造成限制。
为了克服这一限制并在建模过程中引入非线性,需要对营销变量应用饱和函数或变换。这种变换允许生成一个非线性关系,否则由于线性回归的性质,该关系将是线性的。
捕捉非线性广告饱和度和递减回报,而不显式转换媒体变量
部分依赖方法
部分依赖方法是一种更通用的方法,可以用来建模任何营销变量与响应之间的关系。这种方法包括隔离一个变量的效果,同时保持所有其他变量不变。通过改变感兴趣的营销变量的值并观察相应的响应,可以创建部分依赖图。
与饱和变换生成的平滑响应曲线不同,部分依赖方法得到的图形可能不一定平滑。其形状取决于底层建模算法和媒体变量与响应之间的关系。部分依赖方法在关系复杂且非线性时非常有用,可以在显式使用饱和变换的情况下或当算法自然处理非线性而无需额外饱和变换时应用。
使用基于树的集成方法构建 MMM 模型,并使用 SHAP 解释媒体渠道的表现 (Shapley Additive…)
towardsdatascience.com](/improving-marketing-mix-modeling-using-machine-learning-approaches-25ea4cd6994b?source=post_page-----ff6dcc7927f7--------------------------------)
数据
我继续使用 Robyn 提供的 MIT 许可证数据集,如我之前的文章中所示进行实际例子,并按照相同的数据准备步骤,使用 Prophet 来分解趋势、季节性和假期。
数据集包括 208 周的收入数据(从 2015–11–23 到 2019–11–11),包含:
-
5 个媒体开支渠道:tv_S, ooh_S, print_S, facebook_S, search_S
-
2 个媒体渠道也包含曝光信息 (Impression, Clicks):facebook_I, search_clicks_P(本文未使用)
-
无开支的有机媒体:newsletter
-
控制变量:events, holidays,竞争对手销售 (competitor_sales_B)
建模
我建立了一个完整的工作 MMM 流水线,可以在现实场景中应用于分析媒体开支对响应变量的影响,包含以下组件:
-
Adstock 变换 具有无限衰减率 (0 < α < 1)
-
饱和度山变换 具有两个参数:斜率 / 形状参数,控制曲线的陡度 (s > 0) 和半饱和点 (0 < k ≤ 1)
-
来自 scikit-learn 的岭回归
关于系数的说明
在 scikit-learn 中,岭回归没有提供内置选项来强制对变量子集施加正系数。然而,一个潜在的解决方法是,如果发现任何媒体系数为负值,则拒绝 optuna 解决方案。这可以通过返回一个异常大的值来实现,表示负系数不可接受,应从模型中排除。另一种方法是参考 我关于如何在 Python 中封装 R glmnet 的文章,它允许对变量子集施加系数约束。
对于岭回归,我应用了饱和度变换,并使用饱和度函数和部分依赖方法生成响应曲线。使用 LightGBM 时,我允许模型自然捕捉非线性,并使用部分依赖方法生成响应曲线。此外,我在响应曲线上叠加了 SHAP 值,以提供进一步的见解。
结果
具有饱和度变换的岭回归
如可以观察到的,使用饱和度函数和部分依赖生成的响应曲线都显示出重叠的模式,表明这两种方法捕捉了营销变量与响应之间的相似关系。
LightGBM
如前所述,由部分依赖生成的响应曲线可能不一定光滑。这可能是由于梯度提升算法的性质,该算法涉及将特征空间划分为区域,并结合多个决策树之间的交互作用。
图片来源于作者
下面的图表展示了岭回归和 LightGBM 的响应曲线,突出了这两种算法在捕捉递减收益方面的差异。此外,我们观察到 SHAP 值提供了对部分依赖方法生成的响应曲线的可靠近似。
图片来源于作者
图片来源于作者
图片来源于作者
图片来源于作者
图片来源于作者
结论
响应曲线在营销组合建模中起着关键作用,通过提供不同营销变量的有效性和它们对整体响应的贡献的见解。在本文中,我探讨了生成响应曲线的两种主要方法:使用饱和度变换的直接方法和部分依赖方法。我使用线性回归和梯度提升两种算法家族来评估这些方法,并展示了不同算法捕捉非线性响应的对比方式。此外,我将使用 SHAP 值生成的响应与通过部分依赖方法获得的结果进行了比较。
完整代码可以从我的Github repo下载
感谢阅读!
探索情感与人工智能、OpenAI 和探索性数据分析的关系
以下是如何使用 Python 通过 OpenAI 和探索性数据分析在文本中可视化情感
·发表于 Towards Data Science ·阅读时间 9 分钟·2023 年 12 月 12 日
–
作者使用 Midjourney 制作的图片
我想首先说的是,我更喜欢旧版迪士尼电影,而不是新版的。
我觉得这和我小时候看旧版迪士尼电影有关,我对那一刻有一种怀旧的感觉。即使我并不是电影专家,我也觉得旧版迪士尼电影的情节最好。
不过,有一个显著的例外,那就是 脑内飞。我在影院看了这部电影,并且非常喜欢。我不想剧透,只想说这部电影探讨了我们内心中存在的一系列情感:
-
愤怒
-
厌恶
-
快乐
-
恐惧
-
悲伤
这些情感有时像真正的人一样在我们内心交谈。这是一部令人非常温馨的电影,我认为情节非常出色。当我听说新电影《脑内飞》即将上映时,我非常兴奋,正在倒计时。😃
现在,让我猜测一下你的问题:
“这跟人工智能有什么关系呢?”
当我在看预告片的时候,我想到
“我们能生成情感会说什么吗?”
“我们能否使用 ChatGPT 技术来模拟如果情感‘愤怒’是一个人的话,他们会说什么?”
“我们能否将情感作为 N 维空间中的向量来探索?如果可以,我们能否将其绘制出来?”
所以系好安全带,让我们看看能做些什么吧 😏
在我们深入之前, 我想强调这是一个基于电影的“游戏”。我认为一个情感被 AI 取代的世界会非常悲伤,我并不声称这个过程会在第一时间做到这一点。
这个故事分为 4 部分:
0. 代码结构
1. 代码的第一部分,生成情感。高度关注代码
2. 代码的第二部分。这部分的内容少有实际上基于代码。它更多地基于数据集的探索。这是乐趣开始的地方!
3. 总结
4. 结论
这个博客将以代码为基础,旨在对开发者有用。 如果你不是开发者,可以直接跳到第三章,我将在那里描述使用 AI 进行情感检测的结果 😃
0. 结构
这项工作分为两章:
-
“情感”数据的生成:它将是一个包含 main.py 脚本的模块,用于生成句子。这些句子将类似于《头脑特工队》中的电影句子。我们将用 OpenAI 生成每种情感 150 个句子:150x5 = 750 个句子。
-
探索性数据分析: 这将是一个笔记本,用于对 OpenAI 生成的句子进行数据探索。
让我们描述一下我们是如何做到的……
整个代码可以在我的 GitHub 页面上找到 (
github.com/PieroPaialungaAI/Emotion_AI/
)
1. 情感 AI
我们称情感数据的生成为EmotionAI。
在 EmotionAI 中,我创建了 4 个 Python 脚本:
-
constants.py
-
main.py
-
util.py
-
emotionai.py
你可以在这个 Github 文件夹中找到。让我们从底部开始,描述 main.py 文件:
1.1 Main.py
main.py文件的构思是创建情感句子。
它使用一个名为 AIEmotionGenerator 的类来完成,AIEmotionGenerator 是emotionai.py代码的一部分。使用 AIEmotionGenerator,你首先创建文件夹,存储句子(每种情感 50x3),然后生成句子并存储它们。很简单,对吧?
肮脏的工作由emotionai.py完成,来看看:
1.2 emotionai.py
emotionai.py构建了一个名为 AIEmotionGenerator 的类。
这个类有两个功能:
-
为你创建文件夹(无聊)🥱
-
生成给定情感的句子(非常有趣)🤩
我通过给 OpenAI 的 GPT 一个提示来生成情感,这些提示是真实的《头脑特工队》中的情感句子。让我们在constants.py中看看它们是什么。
1.3 constants.py
所以,这就是 constants.py。在这个文件中,我们有用于让 GPT 更好地理解任务的句子。你也应该在其中填入你的Open AI API密钥……这对使其工作至关重要*。
*OpenAI API 密钥最终会花费你一些钱,但我为这个项目花了大约$0.11。关注 OpenAI 的使用页面,但不要为此感到压力。这不是一项投资。 😃
这些句子来自电影。注意到 OpenAI 被编程为不提供愤怒或悲伤的回答,这就是为什么我将情感改为“搞笑的悲伤”和“有趣的愤怒”……如果你问我,这些技术假装世界总是充满彩虹和冰淇淋,这有点令人沮丧,但它确实起到了作用。
2. 情感句子的 EDA
好的,一旦你生成了情感,你将会有你的文件夹,它们将会是这样的:
图片来自作者
我们需要提取.txt 文件并进行探索。让我们一步一步来:
2.0 库
我使用了我的老朋友们:👻
注意 EmotionAI 在库中,我们需要使用它(特别是用于常量)
2.1 导入文本
这一部分有点无聊但必要,我们从文件夹中导入文本,并以数据框的形式整理。
不幸的是,OpenAI 的响应可能会有些随机,特别是在格式上。所以请检查.txt 文件,并删除类似“当然!让我给你答案”或“对不起你感到难过,这是答案列表”之类的内容……
一旦你完成了,你的数据集将会如下所示:
2.2 可视化:词云!
词云是一种查看文本中词频的方法。这就是我们将用来逐类(或逐情感)可视化文本的方法:
这非常有趣。厌恶情感中出现频率最高的词是“SMELL”和“SOCK”。快乐情感中有“LAUGHTER”和“DELIGHT”这样的词。悲伤情感中有“STUCK”、“ACCIDENTALLY”、“NEVER”这些词。恐惧情感中有“SCARED”、“MIGHT”、“WORRY”这些词。
我们正在取得好成果。让我们继续前进!
2.3 嵌入
嵌入的思想是将单词转换为数字……或者更好……将句子转换为数字序列。就像这样!
带有糟糕和非常糟糕的句子比较接近,而带有美丽的句子则远离!
你可以用很多方法来进行嵌入步骤,我选择了使用 OpenAI,主要因为它快速而高效,但你可以选择你自己的方法!
这就是你如何在数据集中嵌入每个句子,从而将每个句子表示为一个向量,就像这样:
附注:如果你用西兰花替换我的冰淇淋,我也会生气的……
2.4 降低向量维度……
现在我们想要可视化向量。为了做到这一点,因为我们的脑袋只能处理 2D 或 3D 的事物,让我们使用 PCA 将维度从 1500+(使用 GPT 嵌入的向量维度)降到 2*。
-
预处理输入向量:
-
训练 PCA:
-
应用 PCA到原始向量
*我写了这篇文章来介绍主成分分析,如果你想了解它是如何工作的…… 😃
2.5 看彩虹!
现在我们有了二维数据集,可以对其进行绘制并了解发生了什么。让我们开始吧!
这很不错……你可以区分……
-
悲伤在数据集的下部分,呈长条形。
-
厌恶在数据集的右上部分,呈对角条形。
-
愤怒在数据集的左上部分,以圆形结构呈现。
让我们看看“快乐”怎么样。
恐惧和快乐之间有相当大的分隔,这很好,除了中间的一个区域。我怀疑那是因为“恐惧”在看到类似这样的句子时会变得“快乐”:
“我非常高兴蜘蛛没有接近我。我对蜘蛛感到恐惧。”
这确实像是一个快乐/恐惧的情况 🙃
在这种情况下,我们也对电影中的恐惧句子应用了 PCA,我们看到恐惧在电影中说的句子(是):
“我真高兴你告诉我地震是神话,快乐,否则我现在会很害怕。”
基本上在“恐惧”句子的橙色点之间。
如果我们对反感的句子做同样的处理,我们可以看到反感确实位于绿色点之间,这正是我们想要的效果。
反感的句子是:
“好吧,小心,这里有一种危险的气味,人们。”
即使这个情况也有点像厌恶/恐惧。这是因为情感不是非黑即白的,你可以同时感受到很多情感。如果你曾经恋爱过,你会非常明白这一点 ❤
3. 结论
非常感谢你阅读这个故事。我在做这个过程中非常开心。我喜欢《头脑特工队》,我认为当我们尝试看看计算机是否能生成类似情感的句子时,非常有趣。
在这个故事中我们:
-
确定《头脑特工队》很棒,我们以此作为创建情感“表达”想法的出发点。如果“恐惧”能说话,它会说什么?
-
使用了 OpenAI 的 GPT 技术来生成句子,基于电影中的原始句子。我们让 OpenAI 生成了 150 个类似反感的句子、150 个类似恐惧的句子、150 个类似快乐的句子和 150 个类似悲伤的句子。
-
使用了词云、嵌入和主成分分析(PCA)技术来可视化结果。
GPT 提取出的句子非常有趣。
恐惧说:
“我害怕自己的影子可能会活过来并开始追赶我。”
而悲伤说:
“我曾经因为忘记推而被困在旋转门里。”
词云也很棒,因为它突出了悲伤具有恐惧感,并使用了像“从不”这样的词汇,而反感几乎与“气味”相关,这非常准确。
当我们可视化这些句子时,我们看到句子形成了清晰的簇,并且电影中的原始句子基本上在对应的簇中。这是连贯性和良好提示的标志。
这项工作可能对许多事情有用,比如:
-
使用这项技术向电影导演建议新的句子
-
通过追踪角色的向量来理解角色的一致性
-
创造新的情感和感觉
-
你的想法(评论此帖子)
4. 关于我!
再次感谢你的时间。这对我意义重大❤
我是 Piero Paialunga,我是这里的那个人:
我是辛辛那提大学航空航天工程系的博士生。我在博客文章和 Linkedin 上谈论 AI 和机器学习。如果你喜欢这篇文章,想了解更多关于机器学习的内容并关注我的研究,你可以:
A. 在Linkedin上关注我,我会在这里发布我所有的故事
B. 订阅我的新闻通讯。它会让你了解最新故事,并给你机会给我发消息,以便收到你可能有的所有修正或疑问。
C. 成为一个推荐会员,这样你就不会有“每月最大故事数”的限制,可以阅读我(以及成千上万其他机器学习和数据科学顶级作家)关于最新技术的文章。
如果你想问我问题或开始合作,请在这里留言:
piero.paialunga@hotmail.com
再见❤️
探索 CNNs 中的特征提取
使用卷积神经网络检查特征提取中的专业化
·
关注 发表在 Towards Data Science ·8 min read·2023 年 11 月 25 日
–
(左) 使用 vgg19 CNN 架构对狮子图像进行的特征提取(图像由作者提供)。 (右) 狮子的原始照片(公共领域,来源于 Pexels)。
卷积神经网络(Convolutional Neural Networks,CNN)是当今利用机器学习进行图像分类任务的基石。然而,在分类之前,它们执行的另一个非常有用的任务是从图像中提取相关特征。特征提取是 CNN 识别图像关键模式的方式,以便对其进行分类。本文将展示如何使用 TensorFlow 和 Keras 函数 API 执行特征提取的示例。但首先,为了正式化这些 CNN 概念,我们需要先讨论像素空间。
背景
像素空间
像素空间正是其名字所暗示的:它是将图像转换为数值矩阵的空间,其中每个值对应一个单独的像素。因此,我们看到的原始图像,在输入 CNN 时,被转换为一个数字矩阵。在灰度图像中,这些数字通常范围从 0(黑色)到 255(白色),中间值是灰度。在本文中,所有图像都已经过归一化处理,即每个像素都被 255 除以,使其值位于[0, 1]区间内。
CNN 和像素空间
CNN 在像素表示中对图像所做的事情是应用滤波器并处理它,以提取相关像素,从而做出最终的“决策”,即将该图像归入某个类别。例如,在页面顶部的图像中,该 CNN 特别关注狮子的嘴、舌头、眼睛(以及总体上的强轮廓),随着我们深入神经网络,这些特征进一步被提取。因此,可以说,CNN 在分类方面越专业,它在识别图像关键特征方面就越好。
目标
说到这一点,目标很简单:看看 CNN 在进行特征提取时的专业程度。
方法
为此,我使用了相同架构但训练大小不同的两个 CNN:一个使用了 50K 张图像(这是基准,智能的那一个),另一个使用了 10K 张图像(这是虚拟的)。之后,我切割了 CNN 的层,以检查算法看到的图像及其理解。
数据集
本项目使用的数据集是广泛使用的cifar10图像数据集[1],这是一个公共领域数据集,包含 60K 张图像,分为 10 个类别,其中 10K 张图像用作保留验证集。这些图像大小为 32x32 像素,是 RGB 彩色图像,即有 3 个颜色通道。
为了防止数据泄漏,我保留了一张图像作为特征识别的测试图像,因此该图像未在任何训练中使用。现在,我向大家介绍我们的小白鼠:青蛙。
青蛙。
在 TensorFlow 中的实现
实现代码见下方代码片段。为了正确切片 CNN 的层,必须使用 TensorFlow 中的 Keras 功能 API 而不是顺序 API。它像级联一样工作,下一层调用在上一层之上。
import tensorflow as tf
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Conv2D, MaxPool2D, Dense, Dropout, Flatten
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
def get_new_model(input_shape):
'''
This function returns a compiled CNN with specifications given above.
'''
#Defining the architecture of the CNN
input_layer = Input(shape=input_shape, name='input')
h = Conv2D(filters=16, kernel_size=(3,3),
activation='relu', padding='same', name='conv2d_1')(input_layer)
h = Conv2D(filters=16, kernel_size=(3,3),
activation='relu', padding='same', name='conv2d_2')(h)
h = MaxPool2D(pool_size=(2,2), name='pool_1')(h)
h = Conv2D(filters=16, kernel_size=(3,3),
activation='relu', padding='same', name='conv2d_3')(h)
h = Conv2D(filters=16, kernel_size=(3,3),
activation='relu', padding='same', name='conv2d_4')(h)
h = MaxPool2D(pool_size=(2,2), name='pool_2')(h)
h = Conv2D(filters=16, kernel_size=(3,3),
activation='relu', padding='same', name='conv2d_5')(h)
h = Conv2D(filters=16, kernel_size=(3,3),
activation='relu', padding='same', name='conv2d_6')(h)
h = Dense(64, activation='relu', name='dense_1')(h)
h = Dropout(0.5, name='dropout_1')(h)
h = Flatten(name='flatten_1')(h)
output_layer = Dense(10, activation='softmax', name='dense_2')(h)
#To generate the model, we pass the input layer and the output layer
model = Model(inputs=input_layer, outputs=output_layer, name='model_CNN')
#Next we apply the compile method
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
return model
架构规格如下图 1 所示。
图 1:使用的 CNN 概述。图像由作者提供。
使用的优化器是 Adam,损失函数是类别交叉熵,用于评估的指标仅仅是准确度,因为数据集完全平衡。
切片 CNN
现在我们可以切片两个 CNN 的一些关键层,以检查图像的处理水平。代码实现如下:
benchmark_layers = model_benchmark.layers
benchmark_input = model_benchmark.input
layer_outputs_benchmark = [layer.output for layer in benchmark_layers]
features_benchmark = Model(inputs=benchmark_input, outputs=layer_outputs_benchmark)
这里发生的情况如下:第一行访问模型的每一层,第二行返回整个 CNN 的输入层。然后在第三行,我们列出每一层的输出,最后我们创建一个新模型,其输出是这些层的输出。这样我们可以查看层之间发生了什么。
编写了非常相似的代码来访问我们虚拟模型的层,因此在此省略。现在让我们继续查看在不同层处理后的青蛙图像。
第一层卷积层
虚拟
图 2 显示了卷积层(conv2d_1)的 16 个滤波器的图像。我们可以看到这些图像并未被过度处理,而且有很多冗余。有人可能会认为这只是第一层卷积层,这也是处理不那么复杂的原因,这确实是一个合理的观察。为了解决这个问题,我们将查看基准测试的第一层。
图 2:虚拟分类器的第一层卷积层。图像由作者提供。
基准测试
基准测试分类器显示了一个经过处理的图像,处理得更加彻底,以至于这些图像大部分已经无法识别。请记住:这只是第一层卷积层。
图 3:基准测试分类器的第一层卷积层。图像由作者提供。
最后一层卷积层
虚拟
正如预期的那样,图像现在已经无法识别,因为我们已经经过了 6 层卷积层和 2 层池化层,这解释了图像尺寸的减小。让我们看看基准测试的最后一层是什么样的。
图 4:虚拟分类器的最后一层卷积层。图像由作者提供。
基准测试
这已经处理得更为彻底,大部分像素变成了黑色,这表明重要的特征已被选出,其余的图像基本上被丢弃了。
图 5:基准测试分类器的最后一层卷积层。图像由作者提供。
这与信息有何关联?
我们可以看到,对于网络的相同切片,处理程度差异很大。定性分析表明,基准模型在从输入中提取有用信息时更加激进。这在第一次卷积层比较中尤为明显:青蛙图像的输出在虚拟模型上扭曲较少,更加可识别,而在基准模型上则较为模糊。
这表明基准在预测类别时,能够更有效地丢弃图像中无用的元素,而虚拟分类器则对如何进行处理不确定,因此考虑了更多的特征。从图 6 中我们可以看到,基准(蓝色)丢弃的颜色像素比虚拟模型(红色)更多,后者在其颜色分布中显示出更长的尾部。
图 6:最后一层的像素概率分布。我们可以看到基准的像素(蓝色)更集中在零附近,而虚拟模型的像素(红色)显示出更长的尾部。
如果我们查看原始青蛙图像的像素分布,图 7 显示了一个更对称的分布,大致集中在 0.4 左右。
图 7:我们原始青蛙图像的颜色分布。图像由作者提供。
从信息理论的角度来看,原始图像和卷积层后得到的图像之间概率分布的差异代表了巨大的信息增益。
从图 6 和图 7 比较来看,我们对前者中将要找到哪些像素比对后者中的像素更为确定。因此,信息获得有所增加。这是对信息理论的非常简要和定性探讨,并为广阔的领域开启了一扇门。有关信息(带有双关语)的更多信息,请参见这篇文章。
最后,查看分类器答案的不确定性的一种方法是观察类别的概率分布。这是我们 CNN 末尾的 softmax 函数的输出。图 8(左)显示基准对类别的确定性更高,分布在青蛙类别上峰值;而图 8(右)显示了一个困惑的虚拟分类器,最高概率出现在错误的类别上。
图 8:每个分类器分配的概率分布。图像由作者提供。
这一分析展示了预期的结果:更大的训练集导致更好的特征捕捉算法。
本项目的笔记本可在这里找到。
参考文献
[1] Krizhevsky, Alex 和 Geoffrey Hinton。从小图像中学习多个特征层。(2009 年):7。
[2] M. Jogin 等人,使用卷积神经网络(CNN)和深度学习的特征提取(2018 年),IEEE 国际电子信息与通信技术近期趋势会议(RTEICT)
[3] K. Simonyan 和 A. Zisserman,用于大规模图像识别的深度卷积网络(2015 年),作为会议论文发表在 ICLR 2015
[4] Z. Matthew 和 F. Rob。可视化与理解卷积网络(2013 年),欧洲计算机视觉大会
[5] B. Jyostna 和 N. Veeranjaneyulu,使用深度卷积神经网络的特征提取与分类(2018 年),《网络安全与移动性期刊》