TowardsDataScience-博客中文翻译-2021-三十六-
TowardsDataScience 博客中文翻译 2021(三十六)
用字符错误率(CER)和单词错误率(WER)评估 OCR 输出质量
测量光学字符识别输出质量的关键概念、示例和 Python 实现

布雷特·乔丹在 Unsplash 上的照片
内容
(1)评估指标的重要性(2)错误率和 Levenshtein 距离(4)单词错误率()
评估指标的重要性
成功地从您的 OCR 模型生成输出的伟大工作!您已经完成了标记和预处理图像、设置和运行神经网络以及对输出应用后处理的艰苦工作。
现在的最后一步是评估你的模型表现如何。即使它给出了很高的置信度,我们也需要用客观的标准来衡量性能。由于您不能改进您没有度量的东西,这些度量标准是 OCR 模型迭代改进的重要基准。
在本文中,我们将研究用于评估 OCR 输出的两个指标,即字符错误率(CER) 和单词错误率(WER) 。

弗勒在 Unsplash 上的照片
错误率和 Levenshtein 距离
评估预测输出的通常方法是使用准确度度量,其中我们表示匹配( 1 )或不匹配( 0 )。但是,这并没有提供足够的粒度来有效地评估 OCR 性能。
我们应该使用错误率来确定 OCR 转录的文本和基础事实文本(即参考文本被手动标记)彼此不同的程度。
一个常见的直觉是查看有多少字符被拼错。虽然这是正确的,但实际的错误率计算要比这复杂得多。这是因为 OCR 输出可能与基本事实文本的长度不同。
此外,还有三种不同类型的误差需要考虑:
- 替换 错误:拼错的字符/单词
- 删除 错误:丢失或遗漏字符/单词
- 插入 错误:包含不正确的字符/单词

三个基本错误的例子 | 图片作者
现在的问题是,你如何测量两个文本序列之间的错误程度?这就是 Levenshtein 距离出现的原因。
Levenshtein 距离是测量两个字符串序列之间差异的距离度量。它是将一个单词(或句子)变成另一个单词(或句子)所需的最少 单个字符(或单词)编辑(即插入、删除或替换)次数。
例如,“连指手套和“配件之间的 Levenshtein 距离是 3 ,因为将一个转换成另一个至少需要 3 次编辑。
- mitten→fitten(用 f 代替 m
- fitten→fittIn(用 i 代替 e )
- fittin→fittingg(在末尾插入 g
两个文本序列的差异越大,所需的编辑次数就越多,因此 Levenshtein 距离就越大。
字符错误率(CER)
(一)方程式
CER 计算基于 Levenshtein 距离的概念,其中我们计算了**将基本事实文本(又名参考文本)转换为 OCR 输出所需的最小**字符级操作数。
它用以下公式表示:

字符错误率(CER)公式
其中:
- SS=SS*替换的数量*
- D=DD*删除数*
- I = 第I*n 次*
- N=**N**参考文本中的字符数(又名地面真相)
加成提示:分母 N 也可以用以下公式计算: N=S+D+C(其中C**
该等式的输出代表参考文本中在 OCR 输出中被错误预测的字符的百分比。CER 值越低( 0 为满分),OCR 模型的性能越好。
(二)举例说明
让我们看一个例子:

手写 ID 样本|来源:https://commons . wikimedia . org/wiki/File:Test-OCR-handled . jpg
- 地面真相 参考正文 : 809475127
- OCR 转录输出文本 : 80g475Z7
有几个错误需要编辑将 OCR 输出转换成真实情况:
- g 代替 9 (参考文本字符 3)
- 缺少 1 (在参考文本字符 7 处)
- Z 而不是 2 (参考文本字符 8)
这样,下面是输入到等式中的值:
- 替换数量=2****
- 删除数( D ) = 1
- 插入次数( I ) = 0
- 参考文本中的字符数( N ) = 9
基于以上,我们得到(2+1+0)/9 =0.3333*。当转换为百分比值时,CER 变为 33.33% 。这意味着序列中的每个第三个字符都被错误转录。***
我们对所有成对的转录输出和相应的基础事实重复该计算,并且取这些值的平均值以获得总的 CER 百分比。**
㈢CER 正常化
需要注意的一点是,CER 值可以超过 100%,尤其是有很多插入的时候。例如,地面实况' ABC' 和更长的 OCR 输出' ABC12345 '的 CER 是 166.67% 。
让我感到有点奇怪的是,误差值可以超过 100%,所以我四处看了看,设法找到了 Rafael C. Carrasco 写的一篇文章,讨论了如何应用归一化:
有时错误数除以编辑操作数(
i + s + d)和正确符号数c之和,总是大于分子。
上述归一化技术使 CER 值始终落在 0–100%的范围内。它可以用这个公式来表示:

归一化 CER 公式
其中 C =正确的字符数
(四)什么是好的 CER 值?
没有单一的基准来定义一个好的 CER 值,因为它高度依赖于用例。不同的场景和复杂性(例如,印刷文本与手写文本、内容类型等。)会导致不同的 OCR 性能。尽管如此,还是有几个来源可供我们参考。
2009 年发表的一篇关于审查大规模澳大利亚报纸数字化项目中 OCR 准确性的文章提出了这些基准(针对印刷文本):
- 良好的 OCR 准确率:CER 1‐2% (即 98–99%的准确率)
- 平均 OCR 准确率:CER 2-10%
- 差 OCR 准确率:CER10%(即准确率低于 90%)**
对于涉及手写文本且具有高度异构和词汇外内容的复杂案例(如申请表),高达约 20% 的 CER 值可视为令人满意。**

单词错误率(WER)
如果你的项目涉及特殊序列的转录(例如,社会安全号码,电话号码等。),那么 CER 的使用将是相关的。
另一方面,如果单词错误率涉及有意义的单词的段落和句子的转录(例如,书页、报纸),则单词错误率可能更适用。

单词错误率(WER)公式
WER 的模式与 CER 相同,但 WER 的运作方式却不同。它表示将一个句子转换成另一个句子所需的单词替换、删除或插入的数量。**
WER 通常与 CER 有很好的相关性(假设误差率不是非常高),尽管绝对 WER 值预计会高于 CER 值。
例如:
- 地面真相:‘我叫肯尼斯’****
- OCR 输出: 'myy nime iz kenneth'
由上可知, CER 为 16.67%,而 WER 为 75% 。75%的 WER 值很容易理解,因为句子中 4 个单词中有 3 个被错误转录。
Python 示例(使用 TesseractOCR 和 fastwer)
我们已经介绍了足够多的理论,所以让我们来看一个实际的 Python 代码实现。
在演示笔记本中,我运行开源的tesselectr模型,从几个手写文本的样本图像中提取输出。然后,我利用 fastwer 包,从转录的输出和地面真相文本(我手动标记)中计算 CER 和 wer。**

示例 Python 实现的输出|作者提供的图片
总结一下
在本文中,我们介绍了 CER 和 WER 的概念和例子,以及如何在实践中应用它们的细节。
虽然 CER 和 WER 很方便,但他们不是 OCR 模型的防弹性能指标。这是因为原始文档的质量和状况(例如,手写清晰度、图像 DPI 等。)的作用与 OCR 模型本身同等重要(如果不是更重要的话)。
欢迎您加入我的数据科学学习之旅!关注此媒体页面以了解更多数据科学内容,或者在 LinkedIn 上联系我。享受评估您的 OCR 模型的乐趣!
*** ***
用 R 评估文本数据中的情感
对于处理文本数据来说,r 是一种极好的语言。处理文本数据时,一个有用的工具是确定正在评估的文本的情感。使用 Tidyverse 和 Sentimentr 包,我们甚至不用阅读就可以获取文本的情感!

爱德华·乔佩蒂亚在 Unsplash 上拍摄的照片
让我们直入主题,评价以下三个句子:
- “这句话里一切都是中性的,普通的。”
- “这句话里的一切都是伟大而美好的。”
- “这句话里的一切都是恐怖而痛苦的。”
显然,第一句有中性情绪,第二句有正面情绪,第三句有负面情绪。使用 R 中的包 sentimentr ,我们可以用下面的代码获得每个句子的情感分数:
每个句子的情感得分如下:
在解释输出时,我们可以看到所有文本字符串都有 1 个元素 id、1 个句子、8 个单词和一个情感分数。注意,句子 1 具有 0 的情感分数,而句子 2 和 3 分别具有正和负的分数。如果我们将第三句中的“可怕”一词替换为“有趣”,新的情绪得分将为 0.088。由此看来,“有趣”似乎比“可怕”具有更高的情感分数,从而提高了句子的平均情感。
接下来,让我们使用一个文本字符串,它由几个带有不同情绪的句子组成。
考虑以下来自(虚构的)季度收益报告的文字:
“第四季度的收益为负七千万欧元。然而,相对于市场竞争而言,运营仍然保持强劲,并取得了多项 R&D 突破。在未来的时间里,我们将开始重组薄弱的业务,增加或专注于最大限度地降低管理费用和行政成本,以保持竞争力。”
将情感()函数应用于这些句子并评估情感:
输出显示我们有三个句子,字数分别是 12、21 和 26。不出意外,第一句由于亏损公告,平均情绪为负。接下来的两句话似乎更乐观一点。
此外,我们想要检查获得了情感分数的单词。有不同的词汇奖励不同的单词,选择正确的单词对匹配上下文至关重要。让我们来看看 M. L. (2017)的 Jockers 单词表。下面的代码将所有字符设置为小写,去掉标点符号,拆分字符串,并显示情感得分不为零的单词:
总共有 9 个单词获得了情感得分。有些情绪得分是有道理的(比如“突破”和“弱”)。然而,像“关注”和“竞争”这样的词不一定是积极的或消极的,因为它取决于上下文。事实上,相对于他们的竞争对手,这家公司做得很好!
让我们应用 Loughran &麦当劳金融词汇列表(2016)中哪个更适合金融环境。该数据集发表在 Loughran,t .和 McDonald,B. (2011 年)《什么时候负债不是负债?文本分析、字典和 10-k。”金融杂志,66:35–65。
有趣的是,当应用新的更适合的单词列表时,4 个单词被删除。还要注意单词的奖励/惩罚比前一个例子更重。这表明选择与上下文相匹配的单词列表是很重要的。
最后,让我们用更多的句子来扩展文本,并对收益报告进行一些有趣的数据可视化(仅限 base R 图)。
- 这篇课文倾向于重复很多单词吗?

正如我们所看到的,这篇文章中的大多数单词都是不同的(只出现一次)。出现了 8 次的单词是“The”。
2.每个句子中的情感是如何演变的?

这篇文章以消极情绪开始,在第 5 句达到顶峰。在阅读报告之前,这可能是非常有趣的信息。
3.情感分数的汇总统计看起来怎么样?

该文本的中间情感值约为 0.2,胡须也非常接近中间值,异常值为-0.2 和 0.8。这表明该文本有一点积极的情绪。
总之,情感分析是分析文本数据的一个非常强大的工具,使用 r 中的包 sentimentr 和 tidyverse 应用起来非常简单。在评估情感得分时考虑上下文很重要,应该使用正确的单词列表/词典来更有效地捕捉文本的上下文。最后,在语料库上使用数据可视化可以给你提供有用的信息,例如单词是如何分布的,句子中的情感是如何演变的,以及摘要统计看起来如何。
用张量流估算盘状星系的空间倾角

螺旋星系从正面到侧面排序。所有的图像都被旋转以使星系的半长轴水平对齐。从左到右,星系分别是(1) NGC 3982 演职员表: 哈勃太空望远镜 (HST),(2) M81 (NGC 3031),图像演职员表: NASA ,ESA,以及 哈勃遗产 团队(【T25 图片鸣谢: 【刘晨】 (4)草帽星系(M101),鸣谢: 哈勃太空望远镜 ,(5)NGC 4565:肯克劳福德 (许可证: CC BY-SA 3.0 )。 HST 图片在“公共领域”,从维基百科下载。
盘星系的分类在天文分析中起着重要的作用,例如测量它们的距离或它们的光被光环中的尘埃衰减。如下图所示,“倾角”是观测者视线与星系盘法向量之间的夹角。

盘状星系的倾角可以粗略地从定义其边界的投影椭圆的轴比中推导出来。虽然这种近似法可能提供足够好的倾角估计,但在大约 30%的情况下,由椭圆度得出的倾角由于各种原因是有问题的,例如存在支配轴比测量的突出凸起。由于潮汐效应,一些星系甚至可能不是轴对称的。

鸣谢:银河倾斜动物园
我的博士研究涉及测量大约 20,000 个螺旋星系的空间倾角。我设计了一个名为星系倾角动物园 ( GIZ )的交互式 GUI,通过可视化地将每个星系与一组已知倾角的标准星系进行比较,找到目标星系的倾角。众包这个项目帮助我在 9 个月内获得了每个星系的至少 3 次独立测量。
GIZ 项目的成果为我提供了足够大的样本来调查我最喜欢的机器学习算法的潜力,如卷积神经网络(CNN),以避免未来类似的繁琐任务。在这里,我的主要目标是从光学图像中评估螺旋星系的倾角,无论它是以灰度还是彩色格式呈现。
数据
为了获得每个星系的剪切图像,我从 SDSS DR12 数据库下载了所有可用的校准单次曝光,然后我将它们转换成全曝光的灰度图像。另外,我直接从 SDSS 快看图片服务器查询了彩色剪影。虽然我以 512x512 向 GIZ 的用户提供了所有的图片,但是为了这个项目,我将图片下采样到 128x128 来构建我的 CNN 模型。这使得模型的复杂性可以用我可用的计算资源来管理。
最初的研究要求我只包括空间倾角在 45 到 90 度之间的星系。每个星系的标签是人类用户在应用一些微小调整后独立测量的平均值。我将用户分为两组,我比较了他们对相似星系的测量,以更好地了解他们的典型表现。我发现两组之间差异的均方根是~2.6ᵒ.任何达到类似或更高精度的最大似然模型都有可能被用于未来的天文研究。

增强图像的例子。在每个面板中,银河 PGC ID 都是青色的。红色是倾斜度,品红色是图像通带,即 r、I 和 c,其中 c 代表 RGB。
数据扩充
为了避免过度拟合,我通过利用增强方法来增加样本量。每个星系的倾角与其在图像上的投影位置角度和图像质量无关。因此,我通过组合变换(如平移、旋转、镜像、添加高斯噪声、改变对比度、模糊)来运行样本,从而生成更多的图像。所有这些变换都保持图像的长宽比不变,以确保星系的椭圆形状及其倾角得以保留。因为我的样本是不平衡的,所以我在不同的倾斜间隔调整增加率,以达到标签几乎均匀分布的结果。

左:原始样本星系的倾角分布。右图:增强样本倾向的分布
卷积神经网络模型
在探索不同的网络架构时,我发现位于 VGG 的模型——卷积过滤器的大小为 3×3——最简单,但在解决这个问题时仍然很强大。其他知名架构如 ResNET 也很有吸引力。然而,在这种情况下,它们需要大量的计算资源来达到满意的结果。迁移学习是另一种方法,在这种方法中,预先训练好的网络的最后几层被去掉,并用新的几层代替,以适应问题的要求。我发现由庞大的数据集训练的网络比我所需要的更复杂,如 ImageNet 。对于我的应用程序来说,即使重新训练这种网络的最后几层,通常也比从头开始设计和训练一个更简单的网络需要更长的时间。星系图像的本质比日常生活中可能包含许多物体、形状和颜色的照片更简单。
由于缺乏足够的计算能力,我采取了试错法来寻找令人满意的架构。为了探索许多模型,我利用了一小部分原始数据。我调查了三个表现良好的 CNN,它们在层数和自由参数方面略有不同。

本研究中使用的最简单的卷积神经网络
在这里,我只呈现我研究的三个 CNN 中最简单的一个。该模型的自由参数总数约为 160 万。它由两组双卷积层组成,后面是 Maxpooling 和dropooling层。我选择**Tanh**作为最后一层的激活函数,因为它输出-1 和 1 之间的数字,这与我的样本中的有限倾斜范围更兼容,即 45 到 90 度。
下面是我如何在 TensorFlow 中实现这个模型:
下表显示了模型摘要:

培养
在训练过程之前,我留出 10%的样本用于测试和模型评估。我使用“ 亚当 ”优化器和“ 均方误差 ”作为损失函数,并在训练过程中跟踪均方误差(MSE)和平均绝对误差(MAE)指标。我通过将所有图像除以 255 来标准化它们,并将倾斜度线性映射到-1 和 1 之间的值。
我使用 Google Colab Pro 服务进行培训。对于 128x128 图像和增强,打开整个增强样本所需的内存超出了所选服务的能力。我试图通过在磁盘上存储 50 个随机批次的训练样本来解决这个问题。每个训练步骤都从加载随机训练批次开始。然后,它继续根据前一步骤重建网络,并将训练过程推进一个时期。在每一步结束时,我都会存储一个网络快照,供下一步使用。我根据需要不断更新网络,多次覆盖所有的训练批次。
在下面的代码中,trainer是将训练过程推进一个时期并返回性能指标的函数。
下图说明了不同训练时期的训练集和测试集的网络评估指标。训练过程显然可以在大约 1000 次迭代时停止。然而,我的实验表明,一些过度训练有助于消除预测中的倾向依赖偏差。此外,过度拟合降低了 MSE 和 MAE 指标的波动幅度。

以下是我的训练方法的一些利弊:
专业版
- 生成尽可能多的训练星系的能力,而不用担心内存大小
- 在任何点停止训练过程,并从离开的地方继续该过程。如果由于缺少足够的内存而导致执行崩溃,这一点尤其有用,如果其他不相关的进程使系统变得混乱,这种情况更有可能发生
- 监控培训过程,并在培训过程中做出决策
Con(s)
- 训练时间由 i/o 过程支配,而不是在每个时期更新网络参数所需的时间
测试
现在,是时候用我的~ 2000 个测试星系来评估训练好的模型的性能了。
我根据模型预测的准确性来评估模型的性能。下图显示了预测倾角和测量值之间的差异,即Δi=iₘ-iₚ。每个点代表测试样本中的一个星系。预测值iₚ通过将训练好的网络应用于测试样本而直接生成。红色实线显示数据点的最小二乘线性拟合结果。
为了改善最终预测,我多次训练每个模型,以探索引导聚合在改善结果方面的能力。为了简单起见,我将每个训练好的模型标记为Model_vgg (m),其中m表示模型的味道。m=0表示使用整个训练样本训练的模型,m≠0表示基于 67%的数据训练的模型。每个面板显示一个模型的结果。RMS 和 MAE 表示δI 在零附近波动的“均方根”和“平均绝对误差”。
如图所示,在所有情况下,差异的均方根都比3.5ᵒ差。回想一下,基于相同指标的平均人类绩效是2.6ᵒ.在最右边的面板中,绿点是使用所有模型口味的预测中值计算的。正如预期的那样,平均模型的 RMS 和 MAE 度量都得到了改进。希望有足够的计算资源和训练更多不同架构的模型,我们可以达到人类水平的精度。

预测值和测量值与测量值之间的差异
与基于椭圆度的倾角的比较
作为基线,我从螺旋星系投影图像的椭圆度推导出它们的倾角。倾角由观察到的轴比 b/a 确定,通过

其中r是从侧面观察时星系的固有轴比。为了我们的目的r=0.3产生更真实的倾向。不过,该值不一定在整个样本中保持不变,可以低至r=0.1。下面,我绘制了i与实际倾角iₘ的关系图,这是我研究的所有 CNN(左)和我使用星系椭圆率计算的那些 CNN(右)的预测中值。

显然,CNN 模型能够产生比那些从轴比中粗略得出的更可靠的倾角。
Web 应用程序和 API
我已经以一个 web 应用程序和一个 REST API 的形式部署了这个项目的最佳生成模型,以满足未来的所有需求。

信用:倾斜
这个在线 GUI 允许用户通过四种不同的方法提交 galaxy 图像。SDSS 覆盖的星系可以使用它们的 PGC ID (1),通用名(2)和赤道坐标(3)来查询。用户还可以上传他们感兴趣的任何星系图像(4)。
有关如何使用 API 的完整文档,请参考:https://edd.ifa.hawaii.edu/static/html/tutorial.html#API
下面,我列出了使用这个 API 的三种不同方式。要么在您最喜欢的互联网浏览器的地址栏中输入 URL,要么简单地使用curl或任何您最喜欢的工具向该应用程序发送发布请求。应用程序以 JSON 格式返回评估的倾斜度。
$ curl http://edd.ifa.hawaii.edu/inclinet/api/pgc/<pgc_id>$ curl http://edd.ifa.hawaii.edu/inclinet/api/objname/<galaxy_name>$ curl -F ‘file=@/path/to/galaxy/image.jpg’ http://edd.ifa.hawaii.edu/inclinet/api/file
摘要
我研究了三个 CNN 模型从它们的光学图像评估螺旋星系倾角的能力。所有这些模型都表现出比基于椭圆率的倾角更好的精度。我注意到,对多个训练场景和模型架构的结果进行平均(bagging 方法)可以提高整体评估性能。
我以 web GUI 和 API 的形式在线展示了构建得最好的模型。
为了进一步改进,我建议使用来自大星系模拟的已知倾角的合成星系,如 Illustris 。还可以通过引入更多的复杂性来改进数据扩充,这允许网络获得关于星系的各种实例的足够的专业知识。
如果你对更多细节感兴趣,我的代码和部署计划可以在我的 GitHub 上找到,地址是https://github.com/ekourkchi/inclinet_project
数据可用性
我在这篇文章中使用的所有倾向都在 Kourkchi 等人的表 1 中列出..145K 。该表的完整版本也可以在河外距离数据库(EDD) 的公共领域中以“ CF4 初始候选者”的标题获得。通过 SDSS 成像数据库(数据发布 12) ,潜在的星系图像可以公开。
参考
- Cosmicflows-4:约 10,000 Tully-Fisher 距离的目录 (Kourkchi 等人,2020,ApJ,902,145, arXiv:2009.00733 )
- 螺旋星系在光学和红外波段的整体衰减 (Kourkchi 等人,2019,ApJ,884,82, arXiv:1909.01572 )
- 河外距离数据库, (塔利,R. B .等人,2009,AJ,138,323)
- 旋涡星系中的全球性灭绝, (Tully et al. 1998,AJ,115,2264)
- 银河倾斜动物园 (GIZ)
新冠肺炎期间学生学习成绩的评估与预测
实践教程
使用数据挖掘技术预测学生成绩的语义方法
又到了一年中的这个时候。考试当天早上翻阅最后几分钟的笔记,像猿猴一样狼吞虎咽地吃着早餐去赶去考场的最后一班车,到达指定的考场,无意中听到有人在讨论一个你完全不知道的话题,最后坐在长凳上,被 30 个紧张的学生围着写试卷。如果这种考前惯例对许多人来说听起来很陌生,我道歉。但是对于许多在新冠肺炎之前的学生来说(还记得吗?)我们的考试就是这样进行的,最终根据分数来评判我们的表现。但是,在这个世界上,被 30 个人包围在一个房间里听起来令人憎恶,闻所未闻,考场已经被我们的绘画室取代,教室从来没有如此错过,有没有一种方法可以让我们分析和预测学生的表现?疫情及其后遗症改变了学生的学习方式吗?如果是的话,它们以何种方式影响了学生的学业表现?
进入教育数据挖掘(EDM)。数据挖掘本身就是从庞大而复杂的数据集中提取模式并得出推论的方法。把单词教育放在它前面,它就变成了从来自教育系统的数据中寻找模式并做出推论。教育数据挖掘杂志将 EDM 定义为-
“……一门新兴学科,致力于开发方法来探索从教育环境中获得的独特且日益庞大的数据,并使用这些方法来更好地了解学生及其学习环境”
EDM 最重要的功能之一是根据过去的活动预测学生的表现。现在,活动可以意味着很多事情,多年来,许多研究人员已经使用不同的指标来评估学生的表现。这可能包括查看过去的 CGPA 分数或内部评估、学生人口统计(收入水平、性别、学校类型等)。)或者课外活动。所有这些都是预测学生学习成绩的重要指标,以前也曾成功地预测过。但我们想知道的是,当学生被带离他/她的传统学习环境时会发生什么,就像禁闭期间发生的一样。这些数据挖掘技术仍然有效并能准确预测学生的表现吗?如果是这样,我们应该用什么指标来建立一个可行的模型?
1.资料组
有一件事变得很清楚,那就是我们需要更准确地考虑反映学生学习环境的因素。为此,我们使用约旦大学学生的数据集作为我们的主要数据集,该数据集以 Likert 类型的问卷形式记录了学生的回答。这个数据集非常有用,因为它不仅引发了学生对他们使用数字工具进行学习的反应,而且还考虑了他们过度使用所造成的心理影响,这反过来又成为学生学习成绩的一个关键因素。
https://www.sciencedirect.com/science/article/pii/S2352340920309987
此外,受上述调查的启发,我们还创建了一个谷歌表单链接,并将其分发给印度各大学的本科生。该表格还向学生提出了类似的问题,以及他们的人口统计信息,如年龄、级别/年级、性别和平均绩点(就像在约旦数据集中一样),然后被添加到我们的原始数据集中。由于这两个数据集都是在封锁宣布后编译的,学生们已经习惯了使用数字工具进行学习。
下面给出了实际数据集的一小部分
超过 801 排,我想我们已经准备好了。
2.数据准备
在蝙蝠的右边,我们注意到许多值需要被机器翻译来理解它们。在 30 个特征列中,22 个是基于李克特量表的调查条目,1 个是二元列(性别),其余是基于不同分类值的条目,包括我们的目标值(GPA)。为了使数据机器可读,我们将以下值转换成数字或二进制值-
- 将所有 Likert 响应转换为数值(“强烈不同意”:0,“不同意”:1,“强烈同意”:4,“不确定”:2,“同意”:3)
- 对所有分类特征执行一次性编码。
- 以及我们的目标值的标签编码(GPA:'低于 60 /低于 2.0':0,' 60–69/2–2.49 ':1,' 70–79/2.5–299 ':2,' 80–89/3–3.49 ':3,'+90 / +3.5':4)和小时值(“3–6”:1,“6–9”:2,“9–12”:3,“+12”:4,“1–3”:0)。
- 由于只有 4 行 GPA 低于 2.0,我们将其删除。
- 移除所有 NaN 值后,我们剩下 781 行和 46 列。最后,我得到了如下所示的清理后的数据集:
已清理数据集的小样本
3.EDA 和特征选择


新冠肺炎前后花在数字工具上的时间
正如所料,covid 前后使用数字工具学习的时间形成了鲜明的对比。使用数字工具超过 3-6 小时的学生比例增加了 22.6%,而使用数字工具超过 9-12 小时的学生比例增加了 16.6%。但更有趣的是,我们可以看到这种变化对学生 GPA 分数的影响。为了理解这一点,我们使用核密度估计(KDE) ,它可以被定义如下
在统计学中,核密度估计是一种估计随机变量概率密度函数的非参数方法。
我们基本上可以将其解释为基于特定值的事件发生的概率密度或可能性的度量。

用于学习的数字工具上花费的时间的 KDE 图之间的比较(Covid 之前和之后)v/s CGPA
在上图中,我们看到,如果学生在疫情后花 1-3 个小时在数字工具上学习,他们有更好的机会获得更高的 CGPA。然而,在两条曲线中,过度使用在线学习工具导致学习成绩稳步下降。因此,直觉上我们可以说,过度使用数字工具可能会损害学生的学习成绩。稍后将详细介绍。
接下来,为了防止模型中的过度拟合,我们必须执行特征选择。现在,我们可以根据两个原则减少列数-
- 如果特征本身和我们的目标值相关性不大。
- 或者当两个特征高度相关时,我们只保留一个特征,以减少测试数据集中的差异。
为此,我们可以使用相关热图或 KDE 图。例如-

在特征‘after _ laptop’上,KDE 分布对于‘0’和‘1’是相同的,这表明它与目标的相关性很差,因此被移除。
4.重击

学生人数
在我们进入模型选择之前,我们需要考虑数据集中普遍存在的类别不平衡。处理不平衡数据集的挑战是,大多数机器学习技术将忽略少数类,从而导致少数类的性能较差,尽管通常少数类的性能最重要。为了解决这个问题,我们使用合成少数过采样技术或 SMOTE。SMOTE 的基本解释是,它会通过选择那些接近其特征空间的例子来合成新的例子,从而使少数类人口过多。或者简单地说,它将为在我们的数据集中代表不足的类生成新的行。
5.型号选择
在确定对数据集最具预测性的分类模型之前,我们按 0.33 的比率将数据分为测试集和训练集。之后,我们使用了 5 种数据挖掘技术,根据它们在之前的论文中对学生成绩预测的有效性-
- 支持向量机
- 决策图表
- 随机森林
- k-最近邻
- 人工神经网络
为了评估该模型,我们使用【准确性】作为我们的评分标准,它给出了所有数据点中正确预测的数据点的数量。

6.结论
当我们开始这个项目时,我们想回答两个问题-
- 我们可以使用反映当前情况的非传统指标来预测学生的表现吗?
- 疫情和随后的封锁对学生的学术生活有什么影响?
为了回答第一个问题,我们看到所有的分类技术都给了我们相当好的性能,其中人工神经网络是最准确的。因此,我们甚至可以进一步改进我们的模型,这可能会给我们带来更好的结果。
现在,要回答第二个问题,请看下表-

相关系数 b/w 自变量和目标值
这些是表格中被问到的一些问题以及它们在 GPA 中对应的系数值。这张表基本上告诉我们,同意上述问题的学生有更高的机会获得更低的 GPA 分数。这与我们在进行 EDA 时发现的数字一致,即 72.5%的学生不建议继续在线学习模式,因为“这对社会和心理都不健康”。因此,根据这项研究,我们可以得出结论,疫情效应不仅对学生的心理健康产生了负面影响,而且对他们的学习成绩也产生了不利影响。
最后,我希望这个模型可以作为任何有兴趣研究新冠肺炎以及随后对学生和教育机构实施的措施的影响的人的参考。
这里是到我的 GitHub 的链接,它包含了这个项目使用的代码和数据集。
评估偏差
入门
您是否无意中对整个数据集进行了训练?

图片来源:http://bit.ly/pexels_artichokes 安民()
通过使用从模型对测试数据的性能中获得的洞察力,我们有效地使用了我们的整个数据集来调整模型,而无需直接在测试样本上训练模型。
现状。
70:30 的比例,我们随处可见。其中 70%的样本用于训练模型,30%留出来测试该模型:
features_train, features_test, labels_train, labels_test = sklearn.preprocessing.train_test_split(
features, labels,
test_size=0.30
)
默认情况下,这种双向分割被硬编码到大多数机器学习工具中。然而,通过思维实验,我们将探究它是如何将偏见引入我们的模型的。
对学术最佳实践的怀疑。
作为机器学习的新手,我担心让太多象牙塔中的最佳实践只是为了表面而悄悄进入我的工作流程。
百分之三十?!为什么会丢弃近三分之一的数据!这怎么被认为是数据“科学”呢?
因此,我花了一段时间来适应留出更多数据的想法,希望训练一个更具普遍性的模型。
在我想出如何在我自己的数据集上产生高精度模型之后,我对更先进技术的怀疑只是加强了——特别是当它引入第三个 验证分割时。拒绝更多来自我的训练分割训练的数据怎么可能产生更好的结果?
另外,我喜欢我的垃圾模特!他们成功了!“这里有人动过测试数据吗?”我问自己。“没有,没有人在碰测试数据。”我推理道。拆分发生在工作流的早期。模特是不可能看到的。那么所有的验证大惊小怪的是什么呢?
有缺陷的培训流程示例。
让我们角色扮演一个假设的和过度简化的 2 分裂训练过程的一些迭代,看看我们是否能发现关于我们的测试数据的信息在哪里泄漏。
Run 1
+-------+-----------+------+
| Train | (acc) | 0.88 |
+-------+-----------+------+
| Test | (val_acc) | 0.74 |
+-------+-----------+------+
看起来我们的模型在学习。很好。鉴于这是我们的第一次运行,我们可能会从这些数据中获得更多的准确性,所以让我们更改批量参数,看看会发生什么。
Run 2
+-------+-----------+------+
| Train | (acc) | 0.94 |
+-------+-----------+------+
| Test | (val_acc) | 0.87 | # increased batch size.
+-------+-----------+------+
是的,当然模型那样表现更好。好的,好的,快到了。现在添加另一层,真正提取最后一层细节。
Run 3
+-------+-----------+------+
| Train | (acc) | 0.98 |
+-------+-----------+------+
| Test | (val_acc) | 0.63 | # extra layer of depth.
+-------+-----------+------+
邦克!嗯,很明显,额外的一层覆盖了训练数据。因此,让我们回滚这一变化,并与我们现有的层,而不是玩神经元的数量。
Run 4
+-------+-----------+------+
| Train | (acc) | 0.95 |
+-------+-----------+------+
| Test | (val_acc) | 0.94 | # increased neurons.
+-------+-----------+------+
尤里卡—数据世界的真正主人!宇宙中没有任何数据能逃过我们的推断!
反思。
踩刹车。这里刚刚发生了什么?我们模型的改进实际上来自哪里?
好吧,纵观所有的运行,有什么共同点?我给你一个提示——在这个工作流程中实际上有第二个神经网络在执行一个隐藏的回归分析。不,它没有深埋在反向传播的源代码中。它一直就在我们眼皮底下。是你的大脑!
作为数据科学家,在评估模型时,我们正在根据我们的性能指标执行我们自己的内部超参数扫描。我们是整个培训过程的共同点。
事实上,由于神经网络仅使用损失作为其改进的指导指标,其他指标如准确性和 R 的存在只是为了帮助我们了解我们的模型表现如何。
T 因此,当我们根据我们所了解到的以前的拓扑结构和超参数如何影响测试数据的准确性来改变我们的模型时,我们就在我们的模型中引入了巨大的偏差。通过使用从模型对测试数据的性能中获得的洞察力,我们有效地使用了我们的整个数据集来调整模型,而无需直接在测试样本上训练模型。
怎么修。
那么,我们如何才能防止自己将这种偏见引入我们的工作流程呢?
良好的第一步是开始使用第三个 验证分割 来评估您的训练跑步。这允许你真正地将你的测试数据作为保持分割。只有当您认为您已经有了一个模型,并且该模型能够根据它在验证数据上的表现很好地进行概括时,您才可以使用这种维持分离来进行评估。
请记住,我们使用第三次验证分割的根本原因是不要对算法隐藏样本。相反,我们这样做是为了在多次运行中重新设计算法时,对作为性能指标评估者的我们自己隐藏这一点。
跨拆分的数据分布如下所示:
splits = {
"train": 0.67,
"validation": 0.13,
"test": 0.20
}
你会问,如何确定尺寸分割?这完全取决于你有多少数据。如果您有很多数据,那么您可以让您的验证和测试分割侵蚀您的训练集。然而,样本的数量(100、500、1000)实际上并不重要。这一切都是为了能够确保每一次分割都能代表更广泛的样本群体。这意味着每个分割包含相同程度的可变性。15 个学生的教室代表一个学校吗?来自日内瓦的 10,000 人的样本集准确地反映了整个欧盟吗?也许是/也许不是。
分层。
这种更广泛的代表性是通过将数据分层实现的;确保每个分割均匀分布。数据在每次分割中是否具有相同的“形状”?

图片鸣谢:Mike Yi,直方图完全指南 (2019) ChartIO
然而,即使我们根据我们的标签和我们所有的特征进行分层,也有可能在每次分割中存在一些看不见的可变性。我说的不仅仅是离群值。请记住,实际收集的特征只是可用于描述样本的无数特征中的一小部分。我们的模型试图在训练中梳理出一些潜在的、看不见的特征。
侧边栏 :如果我们使用 sklearn 的 StratifiedKFold() 方法进行留一法交叉验证,实际上是免费得到验证折叠和分层。
然而,这意味着在将它们输入到深度学习库中时,你必须跟踪每个折叠分组的样本索引。此外,您可能没有足够的数据来确保每个文件夹中都有来自每个类的样本,这导致在计算度量时非常痛苦。它也不处理连续变量的分层。您也许可以通过一个简单的验证分割来逃脱。
解决方案。
我们已经看到,引入验证分割或折叠给我们的工作流增加了更多的移动部分,特别是如果我们执行交叉验证的话。
幸运的是,开源的 AIQC 框架,用于可重复的深度学习数据准备和批量模型调整,可以为您处理这些!
在这里,我们可以看到高级 API 如何使分层数据的拆分和折叠变得轻而易举:
splitset = aiqc.Dataset.Tabular.make( dataFrame_or_filePath = df
, dtype = None *# option to force a specific data type.* , label_column = 'object'
, features_excluded = None **, size_test = 0.22
, size_validation = 0.12
, fold_count = 5** *# number of folds for cross-validation.* **, bin_count = 3** *# number of bins for stratification.* *, label_encoder = None
, feature_encoders = None #see next blog!*
)
如果您需要手动访问拆分/折叠,可以通过以下方法获取:
Foldset.to_numpy(
id:int
, fold_index:int
, fold_names:list #['folds_train_combined', 'fold_validation']
, include_label:bool
, include_featureset:bool
, feature_columns:list
)
之后,您甚至可以设置hide_test=True来防止关于您的维持集的自动化性能指标和图表被泄露。

AIQC:批量中每个模型的每个分割/折叠的自动化度量。按性能阈值过滤。在这里,测试分割是隐藏的。

AIQC:一个用于分类分析的自动化图表的例子。

AIQC:分类分析的自动化度量。
外卖。
- 当基于旧模型在测试/维持数据上的性能调整新模型时,实践者将偏差引入他们的模型。
- 添加验证分割/折叠作为缓冲,保护测试/维持数据免受审查。
- 对数据集进行切片需要分层,以确保它能够代表总体。更复杂的是,这也导致需要计算更多的指标和图表。
- 与大多数机器学习工具不同,AIQC API 可以轻松使用验证拆分/折叠,动态跟踪每个拆分/折叠,并自动计算它们的指标。
期待。
现在,你可能想知道,既然我们已经对数据进行了大量的分割,我们如何对每个分割/折叠进行编码呢?这是我们下一篇博客的主题!
评估指标
探索不同的方法来评估分类问题的机器学习模型。

这是 2 篇文章系列的第 1 部分,其中我们讨论了机器学习(ML)问题的不同评估指标。评估算法的输出与对算法本身建模一样重要。评估一个项目有助于确定该项目的影响力如何,以及如何对其进行改进。在本文中,我们将回顾 分类 的评估指标。那么,我们开始吧。
混淆矩阵
混淆矩阵 是一个 N x N 矩阵,其中 N 表示目标变量中类别的数量(例如 1 和 0 是的幸存列中的两个类/类别

图一。混乱矩阵布局(图片由作者提供)
我们先写出预测是正 ( P )还是负 ( N ),然后根据实际值决定是真 ( T )还是假 ( F )。
╔═══════════╦════╦════╦════╦════╦════╦════╦════╦════╗
║ Actual ║ 1 ║ 1 ║ 1 ║ 0 ║ 1 ║ 1 ║ 0 ║ 0 ║
╠═══════════╬════╬════╬════╬════╬════╬════╬════╬════╣
║ Predicted ║ 0 ║ 1 ║ 0 ║ 0 ║ 1 ║ 1 ║ 1 ║ 0 ║
╠═══════════╬════╬════╬════╬════╬════╬════╬════╬════╣
║ ║ FN ║ TP ║ FN ║ TN ║ TP ║ TP ║ FP ║ TN ║
╚═══════════╩════╩════╩════╩════╩════╩════╩════╩════╝
*Table 1\. Sample Data*
考虑上表。对于第一种情况,预测值为 0,因此我们将写成负,由于实际值与预测值矛盾,我们将写成假,使其成为假负 ( FN )。同样,评估每一个案例,计算它们,并填写混淆矩阵布局。
# Confusion matrix of sample data (Table 1)
from sklearn.metrics import confusion_matrix
confusion_matrix(Actual, Predicted)╔═══╦═══╗
║ 3 ║ 2 ║
╠═══╬═══╣
║ 1 ║ 2 ║
╚═══╩═══╝
分类成本
分类成本 (CoC)是对分类模型计算成本的一种度量。我们计算成本的方法是将权重分配给混淆矩阵。简单来说,如果分类正确,我们奖励奖励,如果分类错误,我们处罚。
╔════════╦═══════════════════╗
║ ║ PREDICTED CLASS ║
╠════════╬════════╦════╦═════╣
║ ║ C(*i*|*j*) ║ + ║ - ║
║ ACTUAL ╠════════╬════╬═════╣
║ ║ + ║ -1 ║ 100 ║
║ CLASS ╠════════╬════╬═════╣
║ ║ - ║ 1 ║ 0 ║ C(*i*|*j*): Cost of misclassifying class
╚════════╩════════╩════╩═════╝ *j* example as class *i
Table 2*
这里, -1 是奖励, 100 是惩罚。因此,对于上述混淆矩阵,CoC 为:
# Cost of sample data (Table 1)
(3 x (-1)) + (2 x 100) + (1 x 1) + (2 x 0) = **198**
成本越低,型号越好。
准确(性)
最常用的评估方法是。它是正确预测值与总预测值之比。
**
*# Accuracy of sample data (Table 1)
from sklearn.metrics import accuracy_score
accuracy_score(Actual, Predicted)0.625*
准确性与 CoC
选择一个精度或 C ost 取决于我们为其建模 ML 解决方案的领域。考虑两个模型, M₁ 和 M₂ ,以及它们与以下成本和精度的权衡。
*╔════════╦══════════════════╗ ╔════════╦══════════════════╗
║ M₁ ║ PREDICTED CLASS ║ ║ M₂ ║ PREDICTED CLASS ║
╠════════╬══════╦═════╦═════╣ ╠════════╬══════╦═════╦═════╣
║ ║ ║ + ║ - ║ ║ ║ ║ + ║ - ║
║ ACTUAL ╠══════╬═════╬═════╣ ║ ACTUAL ╠══════╬═════╬═════╣
║ ║ + ║ 150 ║ 40 ║ ║ ║ + ║ 250 ║ 45 ║
║ CLASS ╠══════╬═════╬═════╣ ║ CLASS ╠══════╬═════╬═════╣
║ ║ - ║ 60 ║ 250 ║ ║ ║ - ║ 5 ║ 200 ║
╚════════╩══════╩═════╩═════╝ ╚════════╩══════╩═════╩═════╝
*Table 3* *Table 4*
Accuracy = 80% Accuracy = 90%
Cost = 3910 Cost = 4255*
选择哪种模式取决于一家公司在精确度和成本方面的灵活程度。让我们考虑这样一个场景,您正在开发一个模型来对患有https://github.com/codebankss/liver-disease-detection肝病的患者进行分类。在这里,我们可以通过几个假阳性**(1 型错误)来使它工作,但是我们不能承受假阴性**(2 型错误)。因此,我们希望选择一个精确度更高的模型。或者,在制造公司,我们可以承受一些错误,但成本是至关重要的,成本较低的模型将是我们的选择。**
在以下情况下,成本与精度成正比:
C(+|-)=C(-|+)= q,C(+|+)= C(-|-)= p
**╔════════╦═════════════════╗
║ ║ PREDICTED CLASS ║ N = a + b + c + d
╠════════╬═══════╦════╦════╣
║ ║ ║ + ║ - ║
║ ACTUAL ╠═══════╬════╬════╣ Accuracy = (a + d)/N
║ ║ + ║ a ║ b ║
║ CLASS ╠═══════╬════╬════╣
║ ║ - ║ c ║ d ║
╚════════╩═══════╩════╩════╝
*Confusion Matrix*╔════════╦═════════════════╗
║ ║ PREDICTED CLASS ║ Cost = p(a+d) + q(b+c)
╠════════╬═════════╦═══╦═══╣ = p(a+d) + q(N-a-d)
║ ║ C(*i*|*j*) ║ + ║ - ║ = qN - (q - p)(a + d)
║ ACTUAL ╠═════════╬═══╬═══╣ = N[q-(q-p)x Accuracy]
║ ║ + ║ p ║ q ║
║ CLASS ╠═════════╬═══╬═══╣
║ ║ - ║ q ║ p ║
╚════════╩═════════╩═══╩═══╝
*Cost Matrix***
比率比较
我们不能完全依赖准确性进行评估。考虑以下数据不平衡的情况。
**╔════════╦═════════════════╗
║ ║ PREDICTED CLASS ║
╠════════╬═══════╦═══╦═════╣
║ ║ ║ + ║ - ║
║ ACTUAL ╠═══════╬═══╬═════╣
║ ║ + ║ 4 ║ 2 ║
║ CLASS ╠═══════╬═══╬═════╣
║ ║ - ║ 8 ║ 486 ║
╚════════╩═══════╩═══╩═════╝
*Table 5***
上述模型的预测能力绝对差,然而我们有(486+4)/(4+2+8+486)=98%。对于这种类型的分类,精确度是不够的。它只是不够健壮。为了克服不平衡数据的问题,可以使用以下指标。
真实阳性率

它的值介于 0 到 1 之间。的 TPR 值越高,的型号越好。它代表了预测因子中真正的阳性率。
假阴性率

就像 TPR 一样,它的范围是 0 到 1。它代表预测值中的假阴性率。降低,使更好。
同样,我们有 TNR 和 FPR。
真实负利率

假阳性率

精确
精度 是一个评估指标,它告诉我们在所有正面预测中,有多少实际上是正面的。当我们不能承受假阳性(FP)时,就使用它。

回忆
回忆 告诉我们,在所有实际阳性中,有多少是预测阳性。当我们不能承受假阴性时,就使用它(FN)。较低的回忆值告诉我们,我们在数据中缺少好的例子。

精确度与召回曲线

图二。精度与召回曲线(图片由作者提供)
f1-分数
有时候,精确和回忆孰轻孰重并不明确。因此,两者可以结合起来,以获得一个良好的模型评估方法。它叫做F1——分数* 。F1 是的谐音和的意思是的精确和召回。*

当精度变为等于时,达到最大值,以便调用。
*# Classification report of sample data (Table 1)
from sklearn.metrics import classification_report
classification_report(actual, predicted)╔══════════════════╦═══════════╦════════╦══════════╦═════════╗
║ ║ precision ║ recall ║ f1-score ║ support ║
╠══════════════════╬═══════════╬════════╬══════════╬═════════╣
║ 0 ║ 0.50 ║ 0.67 ║ 0.57 ║ 3 ║
╠══════════════════╬═══════════╬════════╬══════════╬═════════╣
║ 1 ║ 0.75 ║ 0.60 ║ 0.67 ║ 5 ║
╠══════════════════╬═══════════╬════════╬══════════╬═════════╣
║ ║ ║ ║ ║ ║
╠══════════════════╬═══════════╬════════╬══════════╬═════════╣
║ accuracy ║ ║ ║ 0.62 ║ 8 ║
╠══════════════════╬═══════════╬════════╬══════════╬═════════╣
║ macro average ║ 0.62 ║ 0.63 ║ 0.62 ║ 8 ║
╠══════════════════╬═══════════╬════════╬══════════╬═════════╣
║ weighted average ║ 0.66 ║ 0.62 ║ 0.63 ║ 8 ║
╚══════════════════╩═══════════╩════════╩══════════╩═════════╝
*Classification Report**
注
- **精度、召回和F1-得分是 成本敏感 。你不必同时考虑准确性和成本。
- **精度偏向 C(+|+) 和 C(+|-) 。
- **回忆偏向 C(+|+) 和 C(-|+) 。
- **F1-评分偏向除 C(-|-) 以外的所有。
- 精心策划的算法的性能还取决于目标变量的【错误分类成本】以及训练集和测试集的 大小。**
- F1 分数缺乏可解释性,因此应在中与其他评估指标结合使用。根据用例,两个指标的组合就足够了。
阈值处理
当我们有一个模型来预测类的概率(预测是 0/1),而不是类本身。因此,可以设置 阈值 值来根据概率将预测分类为 0 或 1。让我们更加了解。
AUC-ROC
AUC-ROC 是曲线下面积—受试者工作特性的缩写。这个名字来源于信号检测理论,最初用于区分 噪声 和 非噪声 。它是一个二元分类的评估度量,给出了 假阳性率 和 真阳性率 之间的权衡。
ROC 曲线上的每个点都是模型分类的性能的表示。改变算法、样本分布或成本矩阵的阈值会改变点的位置。考虑包含两个类的 1D 数据集— 0 和 1 。

图三。ROC 曲线 1(图片由作者提供)
任何值大于 t 的点被归类为 类-1 。 ROC 曲线 2 的曲线下面积为 AUC-ROC 值。AUC-ROC 越高,模型越好。

图 4。ROC 曲线 2(图片由作者提供)
图 4 中∏ABC的面积为 0.5(边长为 1 的正方形面积的一半)。因此,AUC-ROC 值总是 大于(或等于)0.5。
如何绘制 ROC 曲线
**╔══════════╦════════╦════════════╗
║ Instance ║ P(+|A) ║ True Class ║ Steps:
╠══════════╬════════╬════════════╣ 1\. Calculate P(*+*|*A*) for each
║ 1 ║ 0.95 ║ + ║ instance and sort them in
╠══════════╬════════╬════════════╣ descending order.
║ 2 ║ 0.93 ║ + ║
╠══════════╬════════╬════════════╣ 2\. Take first probability as
║ 3 ║ 0.87 ║ - ║ threshold and calculate ***TPR***
╠══════════╬════════╬════════════╣ and ***FPR***.
║ 4 ║ 0.85 ║ - ║
╠══════════╬════════╬════════════╣ 3\. Repeat calculation of TPR and
║ 5 ║ 0.85 ║ - ║ FPR with every value of
╠══════════╬════════╬════════════╣ P(*+*|*A*) as threshold.
║ 6 ║ 0.85 ║ + ║
╠══════════╬════════╬════════════╣ 4\. Plot FPR vs TPR.
║ 7 ║ 0.76 ║ - ║
╠══════════╬════════╬════════════╣
║ 8 ║ 0.53 ║ + ║
╠══════════╬════════╬════════════╣
║ 9 ║ 0.43 ║ - ║
╠══════════╬════════╬════════════╣
║ 10 ║ 0.25 ║ + ║
╚══════════╩════════╩════════════╝**
现在,我们可以用这个方法比较两个型号吗?AUC-ROC 的问题是只考虑了概率的顺序。因此,它不能用于比较两个模型。如果我们使用 ROC 比较两个模型,它将考虑来自单个预测的阈值。因此,两个模型的 TPR 和 FPR 值将是相同的,它们的曲线也是如此,而两个模型的表现并不相同。它的意思是,考虑模型 1 预测类 1 的概率为 96% ,而模型 2 ,对于相同的值,预测类 1 的概率为 89% 。因此,概率的顺序很重要。
原木损失
Log Loss 是每个实例的校正预测概率的对数的负平均值。对于实际分类值为 0,它们的预测值从 1 中减去。(注意,对于 Actual =1,我们不计算校正预测概率)
**╔════════╦═════════════╦════════════╦═════════╗
║ Actual ║ Predicted ║ Corrected ║ log ║
║ ║ Probability ║ Predicted ║ ║
╠════════╬═════════════╬════════════╬═════════╣
║ 1 ║ 0.94 ║ 0.94 ║ -0.0268 ║
╠════════╬═════════════╬════════════╬═════════╣
║ 0 ║ 0.56 ║ 1 - 0.56 = ║ -0.3565 ║
║ ║ ║ 0.44 ║ ║
╠════════╬═════════════╬════════════╬═════════╣
║ 0 ║ 0.1 ║ 1 - 0.1 = ║ -0.0457 ║
║ ║ ║ 0.9 ║ ║
╚════════╩═════════════╩════════════╩═════════╝
*Table 6***
****
这里,是实际类, p(yᵢ) 是实际类的概率,是修正-预测概率*。它是一个用于比较两个模型的 健壮 度量。***
接下来,我们将讨论关于 回归 问题的评价指标。
评价指标:评估 NLG 产出的质量
使用评估指标自动化 NLG 输出的评估流程,以节省时间并增加评分实例的容量。

詹妮弗·伯克在 Unsplash 上的照片
在我们开始之前;对于那些想直接跳到计算中并在他们的语料库上看到结果的人;最近,我们开源了一个用于评估 NLG 系统的 Python 包“Jury”。要为您的 NLG 模型计算指标,并对它们进行比较,您可以看看这个项目,并且您可以用几行代码轻松地进行设置:)。也可以阅读评审团的官方博文。
评委:https://github.com/obss/jury。
在机器学习领域,就像在最不相关的领域一样,我们需要某种评估。你可以想到一个学生参加考试,一辆汽车进行碰撞测试,一个 web 服务器进行负载测试,以及在 AI 中对一个模型进行性能评估。这些领域中的评估方法不同,进化标准设计也不尽相同。此程序主要用于评估模型输出的质量,以及在不同模型或不同设置之间进行比较等。
自然语言生成(NLG)是自然语言处理(NLP)的一个领域,是人工智能的一个应用子领域,其目标是产生文本输出。它有大量的子任务,如机器翻译(MT)、问题回答(QA)、摘要、问题生成(QG)等。这里,讨论是围绕输出为文本的模型的性能。NLG 主要是文本到文本,但一些 NLG 度量也可以应用并已经被采用,例如,评估语音到文本模型(如音素级别的编辑距离)。
虽然像[3]、[4]和[6]这样的早期作品是自动化评估度量发展的先驱,但它们是原始和简单的度量,还不能捕捉语言特征的一些复杂方面。他们开创的时代很快带来了改进的度量标准,其中一些被许多 NLG 任务广泛采用,但仍有关于它们是否足够的讨论。
有两种常见的方法来评估生成文本的质量(实际上有三种根据这篇维基百科的文章):
人工评估:由人工评定者对生成的文本的质量进行评定的过程。通常,生成的输出被分发给一组人类评定者以增加多样性。
ii)度量:通过自动度量对生成的文本进行评级的过程,这可能需要在创建之前进行人工干预(我稍后会谈到这一点)。虽然它们中的许多都是针对特定的任务(如机器翻译),但它们普遍适用于其他 NLG 任务。
这两种方法的主要区别在于保真度和努力/时间之间的权衡。人工评估是衡量生成文本质量的一种更好的方法,但它的成本很高。另一方面,度量具有成本效益,可以应用于大量生成的语料库,尽管它们可能不如人工评估好,并且还需要正确的参考语料库来比较生成的文本。
由[13]提出的 NLG 评估指标的一个很好的分类法(如果你想深入研究一篇调查论文,我强烈推荐你看一下这篇论文),其中任务不可知的或任务相关的指标也被区分如下:

自动评估指标的分类[13]。
但是,在这里,我将评估指标分为 4 组,以进行总结并保持易读性:
- 字符串度量
- 基于 n 元语法的度量
- 基于嵌入的度量
- 学习功能
1。字符串度量
这些是人工智能领域中用于文本输出的最早的度量标准。他们在字符或音素水平上工作。通常,这些指标属于编辑距离系列。这一组中的大多数指标利用了编辑距离的三个主要组成部分:插入(I)、删除(D)和替换(S)。

编辑将“裁缝”转换为“帆船”,ED = 4。
上面的例子被广泛称为 Levenshtein 距离[6]。如果指标不使用替换,那么替换将计为 2 次操作,替换为序列删除和插入(反之亦然),参见最长公共子序列(LCS)距离。
该组指标的早期版本基本上不考虑流畅性、句法和语义完整性,仅考虑词汇一致性。然而,改进的版本试图通过考虑短语转换、释义、同义词等来填补这一空白。
有许多利用编辑操作的推导,Wagner-Fischer 算法[11],Levenshtein 距离[6],Hamming 距离[3]等。除了在文本到文本的任务中使用之外,这些指标也被语音识别应用程序所采用。您可以看到一个字符串指标列表。
2.基于 n 元语法的度量
这些度量使用 n-grams 在生成的文本和参考语料库之间进行计算。这一组中广泛使用和众所周知的例子是 BLEU,它在参考语料库中寻找 n 元语法的对应,但是不考虑句法完整性和语法正确性。例如,BLEU 使用 n 元语法的顺序参数来计算 n 元语法的精度。作者提出简短惩罚来近似人类对短句的判断。
作为这个集合中的一个示例成员,您可以看到候选项 2 的二元模型精度的计算,这是计算 n 元文法顺序≥ 2 的 bleu 分数所需要的。

来源:https://present 5 . com/cs-388-自然语言-处理-机器翻译-raymond/
早期的实验表明,BLEU 是许多 NLG 任务的良好指标,因为它与人类的判断密切相关。然而,目前人们认为 BLEU 可能会带来误导性的结果。这里是一篇详细的文章,聚焦于 BLEU 引起的潜在副作用,并提到了 Rachael Tatman 提出的替代方法。
这个群体的一些成员有 BLEU [10],METEOR [1],ROUGE [7],NIST [2],单词错误率(WER)[19],翻译编辑率(TER) [16],TER-Plus (TERP) [17]等。虽然像 WER 和 TER 这样的度量标准是从编辑距离中派生出来的,但它们是在单词级别上工作的,而不是在字符或音素级别上。
3.基于嵌入的度量
这组指标利用语言模型(LM)表示来计算相似性或不相似性的得分。通过 LM 获得生成的文本和参考语料库的嵌入,然后使用余弦相似性或相似性度量来计算相似性或不相似性。使用合适的 LM 可以在字符、单词、句子、段落或语料库级别上获得嵌入。人们可以简单地创建带有嵌入和某种相似或相异度量的定制计算。
由于嵌入和 LMs 的众多级别,该组中的指标相当多样化。通过不同级别嵌入和不同 LMs 的组合,可以设计通用的度量或用于特定的任务。
这组中的几个度量是嵌入平均[5],贪婪匹配[12],BERTscore [20],YiSi [8]。这一组中常用的成员是 BERTScore,它使用 BERT 单词嵌入来计算候选单词和参考单词相对于候选单词和参考单词彼此的相似性(余弦相似性)。以这种方式可以看出,它类似于 F1 分数。我不打算在这里深入讨论细节,但简单地说,它还可以选择使用逆文档频率(IDF)对每个单词嵌入的余弦相似性得分进行加权。这意味着对稀有单词的匹配奖励更多,对常用单词的匹配奖励更少。BERTScore 计算阶段如下所示。

来源:https://github.com/Tiiiger/bert_score
一艘宇宙飞船是一艘被设计用来在外层空间飞行的运载工具或机器。一种类型的人造卫星,宇宙飞船被用于一多种用途,包括通讯、地球观测、气象、导航、太空殖民、行星探索以及运输人类和货物。
例如,单词“a”由于经常出现而具有低权重,而单词“exploration”由于很少出现在语料库中而具有较高权重。
4.学习功能
这组指标的目的是找到一个映射 f: (P,R)>人类评级,其中 P 是预测(或生成的文本),R 是引用。这些通过预训练回归模型提供预测和参考的端到端评估。
这些模型的输入可以有很大的不同,有些使用单词或文本嵌入,有些使用提取的统计数据和/或简单的指标(准确性、F1 等。)来自预测和参考等等。
这个群体的一些成员有格勒乌[9],比尔[18],鲁斯[15],布莱特[14]等。例如,啤酒使用其他单词级别的指标,如召回率、精确度和 F1 分数,将生成的文本映射到人类评级。另一方面,RUSE 使用预测和参考的句子级嵌入来计算分数。BLEURT 是一个预训练的 BERT 模型,顶部有一个线性层。

来源:https://ai . Google blog . com/2020/05/evaluating-natural-language-generation . html
正如你所看到的,BLEURT 有两个预训练阶段,然后在公共人类评级上进行微调。可选地,它可以针对应用特定的人工评级进行微调,以用于手边的特定任务。下面,你可以看到 BLEURT 评价的几个候选句子。

来源:https://ai . Google blog . com/2020/05/evaluating-natural-language-generation . html
最后的话
我试图总结用于 NLG 系统的评估指标。到目前为止,我们涵盖了 4 个主要类别,它们(在某种意义上)是按时间顺序排列的。尽管这些度量标准简化了评估过程并减少了工作量,但是您应该使用相对更适合您的任务的度量标准。[13]举例说明了一些任务中几个指标的用法,如下所示。

任务间自动评估度量的使用。[13]
承认
特别感谢 Cemil Cengiz 的宝贵反馈,以及所有 obss ml 团队的讨论和支持。
参考文献
班纳吉,s .,&拉维,A. (2005 年 6 月)。METEOR:一种与人类判断有改进相关性的机器翻译评估的自动度量。在关于机器翻译和/或摘要的内在和外在评估措施的 acl 研讨会会议录(第 65–72 页)。
多丁顿,G. (2002 年 3 月)。基于 n 元语法共现统计的机器翻译质量自动评估。第二届人类语言技术研究国际会议论文集(第 138-145 页)。
【3】海明,R. W. (1950)。错误检测和纠错码。贝尔系统技术期刊, 29 (2),147–160。
【4】亨特,M. J. (1990)。评估连接词识别器的品质因数。言语交际, 9 (4),329–336。
【5】兰道尔,t . k .&杜迈斯,S. T. (1997)。柏拉图问题的解决方案:知识的获取、归纳和表征的潜在语义分析理论。心理复习, 104 (2),211。
【6】Levenshtein,V. I. (1966 年 2 月)。能够纠正删除、插入和反转的二进制代码。在苏联物理学多克拉迪(第 10 卷第 8 期第 707–710 页)。
林春燕(2004 年 7 月)。Rouge:一个自动评估摘要的包。在中,文本摘要分支出(第 74-81 页)。
【8】Lo,C. K. (2019,8 月)。YiSi --一个统一的语义机器翻译质量评估和估计标准,适用于具有不同级别可用资源的语言。在第四届机器翻译会议论文集(第二卷:共享任务论文,第一天)(第 507–513 页)。
【9】羊肉,a,德拉斯,m,万,s,&戴尔,R. (2007,6 月)。GLEU:句子级流利度自动评测。在计算语言学协会第 45 届年会的会议录(第 344-351 页)。
【10】帕皮尼,k,鲁科斯,s,沃德,t,&朱,W. J. (2002 年 7 月)。Bleu:一种自动评估机器翻译的方法。计算语言学协会第 40 届年会论文集(第 311-318 页)。
罗伯特·a·瓦格纳和迈克尔·j·费舍尔。1974.串对串校正问题。《美国计算机学会杂志》第 21 卷,第 1 期(1974 年 1 月),第 168-173 页。DOI:https://DOI . org/10.1145/321796.321811
【12】俄罗斯,v .&林泰恩,M. (2012 年 6 月)。使用单词到单词的相似性度量对自然语言学生输入的最佳评估。在智能教学系统国际会议上(第 675–676 页)。斯普林格,柏林,海德堡。
【13】赛,A. B .,莫汉库马尔,A. K .,&哈普拉,M. M. (2020)。用于 NLG 系统的评价标准综述。arXiv 预印本 arXiv:2008.12009
【14】t . Sellam,d . Das,& Parikh,A. P. (2020)。BLEURT:学习文本生成的健壮度量。arXiv 预印本 arXiv:2004.04696
【15】岛中香,h .,Kajiwara,t .,&小牧,M. (2018,10 月)。Ruse:使用句子嵌入进行自动机器翻译评估的回归器。在第三届机器翻译会议论文集:共享任务论文(第 751–758 页)。
斯诺弗,m .,多尔,b .,施瓦茨,r .,米丘拉,l .,&马霍尔,J. (2006 年)。有针对性人工标注的翻译编辑率研究。在美洲机器翻译协会第七届会议论文集:技术论文(第 223–231 页)。**
斯诺弗,马修&马德纳尼,尼廷&多尔,邦妮&施瓦茨,理查德。(2009).TER-Plus:翻译编辑率的释义、语义和对齐增强。机器翻译。23.117–127.10.1007/s 10590–009–9062–9。
斯塔诺耶维奇,m .&西玛安,K. (2014 年 6 月)。啤酒:作为排名更好的评价。第九届统计机器翻译研讨会论文集(第 414-419 页)。
****,[19],苏桂英,吴明伟,&常建生(1992)。机器翻译系统的一种新的定量质量度量。载于【1992 年第 2 卷:第 14 届计算语言学国际会议。
****【20】张,t,基肖尔,v,吴,f,温伯格,k . q .&阿奇,Y. (2019)。bertscore:用 Bert 评估文本生成。 arXiv 预印本 arXiv:1904.09675 。
聚类模型的评估指标
解释了 3 种不同的聚类指标

照片由 Howard Bouchevereau 在 Unsplash 上拍摄
聚类是机器学习中的一项基本任务。聚类算法以将相似数据点分组在一起的方式将数据点分组在聚类中。
聚类算法的最终目标是实现高的类内相似性和低的类间相似性。换句话说,我们希望同一个集群中的数据点尽可能相互靠近。不同集群之间的距离需要尽可能大。
有不同的度量用于评估聚类模型的性能或聚类质量。在本文中,我们将介绍以下指标:
- 纯洁
- 标准化互信息
- 兰德指数
纯洁
纯度很容易计算。我们根据最常见的类别为每个聚类分配一个标签。那么纯度就变成正确匹配的类和聚类标签的数量除以总数据点的数量。
考虑这样一种情况,我们的聚类模型将数据点分成 3 个聚类,如下所示:

(图片由作者提供)
每个聚类被分配有最频繁的类别标签。我们将每个聚类中正确类别标签的数量相加,然后除以数据点的总数。

(图片由作者提供)
一般来说,纯度随着簇数的增加而增加。例如,如果我们有一个模型,将每个观察值分组到一个单独的簇中,那么纯度就是一。
正是因为这个原因,纯度不能作为聚类数量和聚类质量之间的折衷。
标准化互信息
NMI 与信息论有关。我们需要理解熵是什么,所以我先简单解释一下。
熵是一种量化不确定性的度量。

熵公式(图片作者提供)
Pi 是标签 i (P(i))的概率。让我们计算前面例子中的类标签的熵。
我们可以通过将属于该类别的数据点的数量除以数据点的总数来计算类别标签的概率。例如,A 类的概率是 6 / 18。
在我们的例子中,熵的计算如下。如果你运行计算,你会看到结果是 1.089。

(图片由作者提供)
标签在类之间近似平均分布,因此我们有相对高的熵。
熵随着不确定性的降低而降低。考虑这样一种情况,我们有两个类别(类别 A 中有 9 个数据点,类别 B 中有 1 个数据点)。在这种情况下,如果我们要预测随机选择的数据点的类别,我们比前一种情况更有把握。这种情况下的熵计算如下,结果为 0.325。

(图片由作者提供)
我们现在对熵有了基本的了解。当给定聚类标签时,归一化互信息(NMI)给出了类别标签的熵的减少。
在某种意义上,NMI 告诉我们当我们知道聚类标签时,关于类标签的不确定性减少了多少。
它类似于决策树中的信息增益。在构建决策树的过程中,每一次拆分都会给模型增加信息增益。事实上,选择了导致最高信息增益的分割。
回想一下有三个集群的案例。由于每个类别中的数据点数量大致相等,因此我们无法确定随机选取的数据点的类别。

(图片由作者提供)
然而,如果我们知道一个数据点属于聚类 A,则该点很可能属于 A 类。因此,我们的不确定性降低了。NMI 测量这种不确定性的减少。因此,它是聚类质量的度量。
NMI 的一个优点是,我们可以用它来比较具有不同聚类数的不同聚类模型,因为 NMI 是归一化的。
scikit-learn 的normalized _ mutual _ info _ score函数可用于计算 NMI。
兰德指数
Rand 指数是两个聚类之间相似性的度量。我们可以用它来比较实际的类标签和预测的聚类标签,以评估聚类算法的性能。
第一步是创建一组无序的数据点对。例如,如果我们有 6 个数据点,该集合包含 15 个无序对,也称为二项式系数。使用 Python 的 scipy 包可以很容易地计算出二项式系数的数量。
import scipy.specia
scipy.special.binom(6,2)
15
考虑我们有以下数据点。

(图片由作者提供)
数据点的无序对是{a,b}、{a,c}、{a,d}、{a,e}、{a,f}、{b,c}、{b,d}、{b,e}、{b,f}、{c,d}、{c,e}、{c,f}、{d,e}、{d,f}、{ d,f}、{e,f }。
为了计算 rand 指数,我们对两个值感兴趣:
- 对于实际聚类和预测聚类,一对元素在同一个聚类中的次数。
- 对于实际聚类和预测聚类,一对元素不在同一个聚类中的次数。
对于实际和预测,对{a,b}中的元素在同一个群集中。另一对符合这个描述的是{e,f}(共 2 对)。
对于实际聚类和预测聚类,对{a,d}中的元素位于不同的聚类中。符合该描述的其他对是{a,e}、{a,f}、{b,d}、{b,e}、{b,f}、{c,e}、{c,f}(总共 8 对)
我们现在可以介绍兰德指数的公式:

兰德指数(作者图片)
- a 是一对元素在实际和预测聚类的同一个聚类中的次数,我们计算为 2。
- b 是一对元素不在实际和预测聚类的同一个聚类中的次数,我们计算为 8。
- 分母中的表达式是二项式系数的总数,即 15。
因此,这种情况下的 rand 指数是 10 / 15 = 0.67
scikit-learn 的 rand_score 函数可用于计算 rand 指数。
结论
我们已经讨论了聚类模型的 3 个常用评估指标。评估模型和创建模型一样重要。如果没有可靠和全面的评估,我们可能会在模型部署后得到意想不到的结果。
对评估指标的全面理解对于有效和恰当地使用它们至关重要。
感谢您的阅读。如果您有任何反馈,请告诉我。
机器学习的评价标准
某些度量比其他度量更好地度量模型性能。

艾萨克·史密斯在 Unsplash 上拍摄的照片
有太多的指标来确定机器学习模型的性能。了解哪种评估指标将正确地度量您的模型性能是有益的。某些度量比其他度量更好地度量模型性能,这取决于用例。我们将回顾回归和分类模型的一些通用评估指标。
回归
回归用于预测连续输出并使误差最小化。例如,您可以使用回归模型来预测销售额或收入。回归模型的两个基本评估度量是平均绝对误差和均方误差。
注意:计算回归度量的一个技巧是逆向工作。让我们以平均绝对误差(MAE)为例。首先计算误差,然后是绝对值,最后是平均值。我们基本上是把 MAE 倒过来算成了“EAM”。
平均绝对误差
平均绝对误差计算实际值和模型预测值之间的绝对差值。MAE 很适合用作基线,因为它考虑了绝对误差。使用 MAE 的问题在于,度量是相对于值和残差的。例如,什么被认为是好的或坏的 MAE?显然,您希望 MAE 接近 0,但是当您的误差值较大时,使用 MAE 评估您的模型会变得更加困难。
注意:使用绝对误差也用作套索回归的 L-1 归一化
计算 MAE 的公式:

梅公式
均方误差
均方误差计算误差,然后计算差值的平方,然后计算平均值。MSE 是另一个用作基线的好指标,因为它是一个像 MAE 一样的基本评估指标。然而,由于每个值都是平方的,所以 MSE 会增加误差。再次导致评估你的模型变得困难。
注意:使用 sqaured 值也用作岭回归的 L-2 归一化
计算 MSE 的公式:

MSE 公式
RMSE(均方根误差)
均方根误差就像 MSE 一样,但是取输出的平方根。RMSE 是另一种基于残差平方的基本评估指标,但对较大误差的惩罚更大。
计算 RMSE 的公式:

RMSE 公式
MAPE(平均绝对百分比误差)
平均绝对百分比误差尝试用 MAE 解决问题。其中 MAE 是相对的,基于残差的大小。MAPE 会将误差转换为百分比,其中您希望您的 MAPE 尽可能接近 0。还有其他使用绝对值和百分比的指标。(如猿、加权 MAPE、对称 MAPE 等。)
计算 MAPE 的公式:

MAPE 公式
平均绝对标度误差
平均绝对比例误差是一个允许您比较两个模型的指标。使用每个模型的 MAE,您可以将新模型的 MAE 放在分子中,将原始模型的 MAE 放在分母中。如果 MASE 值小于 1,则新模型的性能会更好。如果 MASE 值等于 1,则模型执行相同的操作。如果 MASE 值大于 1,则原始模型的性能优于新模型。
计算 MASE 的公式:

梅斯公式
分类
分类用于预测离散输出。一个流行的例子是“热狗/不是热狗”的概念。分类器旨在最大化可能性,以确定样本应如何分类。
准确(性)
准确性是最基本的指标之一,但经常会产生误导。原因是由于不平衡的阶级。如果你有一个达到 98%准确率的分类器,但是 98%的数据被归类为同一个标签,那么你的模型不一定很好。这是因为您的模型只是将所有东西都标记为同一个类。
举例:你有 98 个“热狗”,2 个“不是热狗”。你的模型会把所有东西都标上“热狗”的标签,不管它是什么,但仍然有 98%的准确率。即使这个模型完全忽略了其他的一切。
因此,您应该添加平衡数据的方法。(例如,上/下采样、合成数据等。)

精确度公式
精确
精度评估您的模型在进行预测时的精度。如果您希望您的模型在标记数据时保持保守,这是一个值得注意的好指标。明智地使用精度的一个例子是,当你的模型产生的每个“正面”标签都有很高的成本时。

精确公式
回忆
召回评估模型的敏感度。基本上,它检查您的模型在标记相关样本方面有多成功。

回忆公式
f 分数
F 值,也称为 F 值,是精确度和召回率的调和平均值。如果您想在模型的精确度和召回率之间找到平衡,F 值是一个很好的衡量标准,应该用来找到一个总体上较好的模型。
注:在下文中,下标“p”表示被归类为“肯定”的预测。

f 分数公式
我可以使用哪些工具来帮助生成评估指标?
一般来说,Scikit Learn 有很棒的评估度量函数。对于分类度量标准,分类报告功能为您处理了很多事情,并快速显示了模型性能。特别列出了精确度、召回率和 F 值。
摘要
根据您的用例选择评估指标。不同的指标更适合不同的目的。选择适当的指标还可以让您在向他人展示您的数据和发现时,对您的模型更有信心。
另一方面,使用错误的评估指标可能对机器学习用例有害。一个常见的例子是关注准确性,使用不平衡的数据集。
需要一个起点并希望获得更多 Python 经验?查看 Scikit Learn 的评估指标函数。
链接
评估指标,续
探索评估机器学习模型的不同方法系列的第 2 部分。

图像由https://unsplash.com/@chrisliverani上下
在文章的第二部分,评估度量,我们将讨论不同的度量来评估 回归 算法。(第一部分可以在这里找到。)****
在回归中,我们通过比较预测值和实际值来计算误差。误差决定了预测值与实际值的差距。误差的符号( + 或 - )让我们知道误差从最佳拟合回归线变化的方向。****
***+--------------+-----------------+----------------------+
| Actual Value | Predicted Value | Error |
| | | (Predicted - Actual) |
+--------------+-----------------+----------------------+
| 19 | 28 | 9 |
+--------------+-----------------+----------------------+
| 37 | 33 | -4 |
+--------------+-----------------+----------------------+
| 25 | 20 | -5 |
+--------------+-----------------+----------------------+
| 9 | 16 | 7 |
+--------------+-----------------+----------------------+
| 22 | 15 | -7 |
+--------------+-----------------+----------------------+
*Table 1\. Sample Values and Error****
对于一个 分类 模型,如果它的评价度量得分是 0.8,那么它比一个随机基线模型的得分 0.5 要好得多(假设两个类的概率相等)。同样,在回归中,基线可以被认为是用于评估目的的'平均值'或'中位数'。
均方误差
MSE 因为简单,所以是回归的基本评价指标之一。它是目标值与预测值的平方 差值的平均值。******

图一。MSE 公式
MSE 去除了误差的负号,甚至惩罚了一个小误差,但是因为我们取误差的平方,它改变了误差的单位(变成平方)。**
为使 MSE 准确工作,数据应遵循正态分布无异常值 ,误差应无偏。MSE 越低,模型越好。********
目标均值 值最小化 MSE。考虑一个例子。给我们成对的数据:特征 𝑥ᵢ 和对应的目标值 𝑦ᵢ ∈ ℝ 。让我们将目标向量表示为𝑦ᵢ∈ℝ𝑁t15】,这样 𝑦ᵢ 就是对象 𝑥ᵢ 的目标。同样,𝑦̂∈ℝt23】表示对物体的预测: 𝑦̂ᵢ 表示物体 𝑥ᵢ.**********
现在,如果所有 𝑥ᵢ 的预测值都等于 𝛼: 𝑦̂ᵢ=𝛼 ,那么 𝛼 的值会使 MSE 误差最小化?

图二。最小化 MSE
我们需要最小化函数 𝑓(𝛼) 。相对于 𝛼 来说是平滑的。因此, 𝛼 成为局部最优的必要条件是*****

让我们找到满足条件的点:

由于二阶导数 ( d f/d𝛼 )在点 𝛼 处为正,那么我们发现的就是局部极小值。因此,我们可以得出结论,MSE 度量的最优常数是 目标均值 值。***
平均绝对误差
MAE 是绝对值的平均值误差。MAE 的基线模型为 中位数 。如果 MAE 是小,这意味着模型预测的是中值。****

图三。梅公式
MAE 是最基本的,非负误差计算方法。在计算误差时,必须使数值为非负值,否则,在大多数情况下,负值会抵消正值。MAE 的输出是线性。因此,大误差不会盖过小误差,并给出模型的无偏视图。或者,由于 MAE 是线性的,它不能(通过平方)给大或小的误差加权。
目标中值 最小化 MAE。就像我们为 MSE 做的一样,我们能找到 MAE。这里,我们需要回答什么样的 𝛼 值会最小化 MAE 误差?

图 4。最小化 MAE
回想一下(∂|𝑥|/𝑑𝑥) = 𝑠𝑖𝑔𝑛(𝑥),其中𝑠𝑖𝑔𝑛代表符号功能。因此,

所以我们需要找到这样的𝛼*

注意 **𝑔(𝛼) 是一个分段常数非减函数。**𝑔(𝛼)=-1为小于最小值的 𝛼 的所有值 𝑦ᵢ 和 𝑔(𝛼*) = 1 为 𝛼 >最大值 ᵢ 𝑦ᵢ 。功能通过 2/𝑁 在每一点 𝑦ᵢ 跳转。这里有一个例子,这个函数对于𝑦**=[-0.5,0,1,3,3.4 😗***

图五。 𝑔(𝛼) vs 𝛼 剧情
基本上同样大小的 𝑁 跳跃,从1开始,到 1 结束。很明显,你需要做的是关于𝑁/2t40】跳跃来撞击零点。这恰好发生在目标向量 𝑔(𝑚𝑒𝑑𝑖𝑎𝑛(𝑦))=0 的 中值 处。我们要小心区分两种情况:当有 偶数 点数和奇数时,但直觉不变。****
均方根误差(RMSE)
RMSE 是评估回归模型最常见的度量。就是均方误差的平方根。RMSE 用平方根将数值换算回原来的单位。如果 RMSE 高,那么中的 与中的 实际值与预测值有较大偏差。

图六。RMSE 公式
RMSE 对大的错误很敏感。因此,每当您想要检测预测值和实际值之间的巨大差异时,它就非常有用。
尽管 RMSE 是一个很好的指标,但它在某些情况下不能一概而论。就像 MSE 一样,RMSE 对 离群值 很敏感。RMSE 也要求数据遵循正态分布。考虑下表。**
*+--------+--------+-----------+
| | Actual | Predicted |
+--------+--------+-----------+
| Case 1 | 1 | 401 |
+--------+--------+-----------+
| Case 2 | 10001 | 10401 |
+--------+--------+-----------+
Table 2.*
在案例 1 中,我们可以得出结论,模型一定有问题,因为实际值和预测值的比例有很大差异,因此预测不正确。**
在案例 2 中,相对于比例,差异不是太大,因此它是一个相当精确的模型。**
在这两种情况下,RMSE 是相同的,因为它只考虑预测值和实际值之间的差异。
均方根对数误差
【RMSLE】是 RMSE 的一个稍微修改但完全不同的版本,其中我们在平方之前取实际和预测值的日志。**

图 7。RMSLE 公式
Log 按比例缩小输出值。如果数据中有异常值,RMSE 的大小会急剧增加,而 RMSLE 则保持在附近的范围内。所以比 RMSE健壮*** 多了。***
相对平方误差
RSE 是训练模型的均方差与基线模型的均方差之比。****

图 8。基本 RSE 公式
为了计算基线模型的 MSE,我们将考虑实际值和实际值的总平均值之间的差。****

图九。扩展 RSE 公式
当 MSE(模型)变得等于MSE(基线)时,那么 RSE = 1 ,这意味着我们的模型与预测所有值的平均值一样好。**
如果 RSE > 1 ,则该模型比基准模型差。因此,MSE(模型)越低,模型越好。这与数字越高,模型越好的观点相矛盾。
r 平方
R 又称 决定系数 。r 告诉我们独立变量中的变化有多少是由从属变量解释的。这些信息让我们了解与基线模型相比,我们的模型的*符合度。***

图 10。r 公式
因为它是一个比率,所以没有标度,R 的值在[0,1]之间,R 越高,模型越好。
每当你添加更多的 特征 到数据中,R 要么增加要么根本不改变而不考虑特征。有时,它可能会将结果导向相反的方向。它不会受到惩罚,因此 R 对于样本或特征数量的变化不是很鲁棒。
调整后的 R 平方
为了克服 R 的缺点, 调整后的 R 考虑了' k ',其中' k '是特征数 ( 变量),' N '是样本数。****

图 11。调整后的 R 公式
N-k-1 是自由度,其有助于提供关于估计回归模型所需的最小样本数的信息。理想情况下,当有大量特征时,需要有一个大的样本来进行良好的拟合。****
现在,如果添加特征和样本,分母( N-k-1 )增加。如果 R 显著增加,这种变化可以得到补偿。但是,如果 R 不变,我们从调整后的 R 的等式中的 1 减去一个更大的值。因此,调整后的 R 减小。**
调整后的 R 值越高,模型越好。
结论
评估统计模型对于识别 未见数据 上模型的 功效 非常关键。确定评估模型的正确指标甚至更为关键。此外,为了获得全面的理解,在不同的评估约束下测试模型变得非常重要。
选择正确的指标是关于理解 业务需求 和 预期 期望的结果。基于各种目标,可以制定不同的新的评估度量,以获得 最优 结果。否则,可能会导致结果不明确。
[1]:张若池。(2019 年 7 月 14 日)。度量https://zhangruochi.com/Metrics/2019/07/14/**
强化学习策略的评估
在强化学习中,主体通常处理顺序的、评估的和抽样的反馈。
在这篇文章中,我们将关注能够同时处理顺序反馈和评估反馈的代理。甚至大多数人在同时平衡当前和长期目标以及收集和利用信息方面都存在问题。
- 顺序意味着您的代理可以接收延迟的信息。延迟的反馈使得解释反馈的来源变得棘手。顺序反馈引起了时间信用分配问题,这是确定哪个状态、行为或状态-行为对奖励负责的挑战。
- 评价性意味着反馈只是相对的,因为环境是不确定的。我们不知道环境的实际动态;我们无法访问转换函数和奖励信号。因此,作为不确定性的一个持续副产品,出现了勘探-开采权衡。
- Sampled 表示代理需要使用收集到的反馈进行归纳,并基于该归纳做出明智的决策。

来源:维基百科(https://it.wikipedia.org/wiki/Robot)
我们的目标是估计一份保单的价值,也就是说,了解从一份保单中期望得到多少总回报。更确切地说,目标是估计策略π的状态值函数 vπ(s)。换句话说,这就是预测问题。
一般来说,所有学习价值函数的方法都是逐步将估计误差的一部分移向目标。大多数学习方法遵循的一般等式是估计=估计+步长*误差。误差就是采样目标和当前估计值之间的差值:(target-estimate)。计算这些目标的两种主要且相反的方法是蒙特卡罗和时差学习。这两种方法是可以用 n 步 TD 法概括的两个极端。仅仅通过改变步长,你就可以得到介于两者之间的任何代理。通过 TD(λ),单个代理可以以一种非常创新的方式将这两个极端以及两者之间的任何事物结合起来。
蒙特卡洛
MC 很直观。如果你想知道从一项政策中期望得到多少总回报,你只需用这项政策运行几集,收集数百条轨迹,然后计算每个州的平均值。
一点背景:状态、奖励、动作、下一个状态是一个体验元组。一系列的经历被称为轨迹。
- 使用策略π与环境进行交互,直到代理达到终止状态。
- 你计算遇到的每个状态的回报。你从状态 1 开始,增加和减少沿途收到的奖励,然后重复状态 2,直到最后一个状态。
- 现在,您可以通过对从每个状态获得的回报进行平均来估计每集结束时的状态值函数。换句话说,您可以用平均值来估计期望值。
有趣的是,实现 MC 有不同的方式。单个轨迹可能包含对同一状态的多次访问。如果你独立计算每次访问后的回报,那么你使用的是每次访问 EVMC;另一方面,如果您只对每个州使用首次访问,则使用首次访问 MC (FVMC)。
时差
一方面,MC 具有可靠的收敛性质,因为它向实际回报更新价值函数估计,这是真实状态-价值函数 vπ(s)的无偏估计。另一方面,对于 MC,代理必须等到一集结束时才能获得实际回报,然后才能更新状态值函数估计。此外,实际回报是准确的,但方差高,因此样本效率低——因为它们在同一轨迹上累积了许多随机事件。
对于 TD,你使用单步回报,一旦你观察到下一个状态,你可以使用状态值函数估计作为下一步的回报估计。换句话说,TD 方法使用 vπ(s)的估计值来估计 vπ(s)。它启动并根据猜测进行猜测;它使用估计回报而不是实际回报。
TD 背后的大直觉是,从回报中,我们可以通过将一些项分组来重写等式。由此可知:

这种递归形式:

因此,这允许我们这样写状态值函数:

然后,我们可以估计每个时间步上的状态值函数。事实上:
- 我们使用我们的策略推出单个交互步骤。
- 我们在一步期望的样本上估计状态值函数。
TD 目标是真实状态值函数 vπ(s)的有偏估计,因为我们使用状态值函数的估计来计算状态值函数的估计。这也称为引导。值得注意的是,这一估计值的方差要低得多,因为 TD 目标只取决于一次经验。
N 步 TD 学习
MC 和 TD 将实际回报分散到所有状态。但是,我们可以把这两种方法推广成一种 n 步法,用 n 步来计算值函数。但是什么是好的 n 值呢?高于
1 的 n 值通常更好,但是我们也不应该去追求实际回报。自举有所帮助,但它的偏见是一个挑战。
- 前视 TD(λ) 使用所有 n 步返回,直到最后一步 T,然后用指数衰减值加权。在这个特定的版本中,代理必须等到一集结束后才能更新状态值函数估计。但至少我们有所收获:如果我们愿意接受偏差,我们可以得到更低方差的目标。
- 后视 TD(λ) 除了能够在每个时间步长应用更新之外,还可以调整偏差/方差权衡,就像 TD 一样。该方法跟踪一个状态是否合格以及合格多少,以便相应的值函数更新被正确地应用于合格的状态。它使用资格跟踪来实现,资格跟踪是一个内存向量,用于跟踪最近访问过的状态。在更新之后,合格跟踪向量被λ(权重混合因子)和γ(折扣因子)衰减,使得未来的加强事件对早期状态的影响较小。通过这样做,假设λ没有被设置为 1,则最近的状态在最近的过渡中遇到的奖励比在该集早期访问的那些状态获得更多的积分;否则,这类似于 MC 更新,其给在该集期间访问的所有州相同的信用(假设没有折扣)。
结论
在本文中,我们只处理预测问题,它包括估计代理的行为值。我们谈到了 MC 预测和 TD 学习。这两种方法是可以用 n 步 TD 法概括的两个极端。有了 TD(λ),单个智能体就可以结合这两个极端和两者之间的一切。
随时给我留言或:
参考
事件驱动架构和语义耦合
解决事件驱动架构中的语义耦合对于真正实现松散耦合至关重要

图片来自 Pixabay 许可下的 Pixabay
事件驱动架构(EDA)是构建松耦合应用程序(微服务与否)的关键。这是一种架构风格(参见这里的和这里的和),其中组件通过发出和响应事件来异步通信。
净化 1:一个事件是过去发生的事情。事件通知(或称事件消息)包含事件的描述。但是在大多数情况下,以及在本文中,事件指的是事件消息。
净化 2:一个生产者是一个应用程序或应用程序组件,它发布指示其中一些状态变化的事件。
净化 3:一个消费者是一个应用程序或应用程序组件,它监听事件并对它们做出反应。
应用程序是松散耦合的,
- 可以在不知道源或目标的情况下开发和运行单个组件。正是信息层上的交互需求为集成奠定了基础。
- 单个组件较少受限于平台、编程语言以及构建和运行环境。因此,每个应用程序组件都可以用最合适的技术来构建。有些甚至可能是商业现货(COTS)产品。
- 单个组件可以独立地进行扩展、配置、审计和合规性检查。
正如 Martin Fowler 在这里所解释的,组件需要了解外部系统才能完成内部事务,这就产生了依赖性,导致了紧密耦合。这种知识例如可以是不同组件(例如,另一个应用)中的 API 端点是否存在并且正在运行和响应。当应用程序被设计为以被动-主动的方式工作时,这是可以避免的,在这种方式下,它们只对事件消息/通知做出反应。
松耦合?还没有!
然而,在逻辑/语义层面上仍然存在微妙的耦合。如果我们不小心,这可能会导致更复杂的问题。
- 使用者要求事件消息具有特定的模式。如果生产者删除了一些字段,它会影响消费者,因为他们可能会围绕这些字段构建一些逻辑。同样,如果字段的数据类型发生变化,也可能会影响消费者。因此,生产者需要一个预先商定的模式。
- 由于生产者或消息代理/队列中的错误,事件可能会被重播或发布多次。这并不意味着事件发生了两次。消费者需要意识到这些小故障,并能够绕过它们。
- 消费者还需要确定发布这些事件的是真实的生产者,并且这些事件在发布过程中没有被操纵。
- 生产者和消费者都可能维护一个事件日志,其中存储了所有发布或消费的事件。如果组件利用事件源,这样的事件日志甚至可能是应用程序逻辑的基础。 Event sourcing 扩展了事件驱动架构,其中应用程序状态完全作为事件存储在事件日志中。该日志是事实的来源,任何进一步的更新也作为事件存储。日志本身是仅附加的,重放是恢复状态的一种方法。然后,重放事件的组件需要在表示同一数据实体中的状态变化的事件中适应历史模式变化。
通过移除同步 API 调用,我们实现了某种程度的解耦。但是对参与应用程序产生负面影响的依赖性并没有消失。
处理语义耦合
因为语义耦合是由信息交换产生的,因此需要在信息层面上解决。
让我们考虑下面的事件。
{
"header":{
"application-id":"CRM-application-id",
"schema-url":"json-schema-url",
"timestamp":1625507375542,
"message-id":"9aeb0fdf-c01e-0131-0922-9eb54906e209",
"event-descriptor":"customer.added"
},
"customerid":"cust-id",
"firstname":"some firstname",
"lastname":"some lastname"
}
此事件提供以下信息。
- 发布它的应用程序
- 消息的模式(指向相应 JSON-schema 的 URL 链接)
- 发布的时间戳以及全局唯一的消息 id。
- 事件描述符,说明事件是什么。在本例中,添加了一个新客户。事件描述符和应用程序 id 的组合必须是全局唯一的。
- 添加的客户数据的详细信息。
这个事件组合可以如下图所示。

事件消息撰写
那么这如何减少语义耦合导致的依赖性呢?
每个事件都包含一个提供几个关键信息的标题。
- 模式 URI 指向该消息所遵循的特定模式(例如 JSON 模式)。任何消费者都有可能按照模式动态解析消息。这可能需要每条消息处理两次,首先读取头,根据模式选择正确的解析器,然后读取整个消息。但是好处是消费者可以按照模式解析消息。
- 该架构有助于在重播事件日志中的旧事件时解决历史架构更改,该事件日志可能与当前事件日志具有不同的架构。
- 全局唯一的消息 id 有助于识别无意的重放。重放的消息将具有与先前处理的消息相同的消息 id。因此,消费者可以是等幂的。
- 事件描述符是事件的特定于域的名称,在该域中是唯一的。上面的例子使用点符号来分隔主语和动词(总是用过去时态)。
事件的其余部分是数据。一般来说,它应该是胖的,这样每条消息本身就尽可能完整。fat 事件携带的数据足以保证不会回调其原始应用程序。但是胖事件并不意味着将所有数据放在一个事件中。这可以通过让 id/URIs 指向其他相关的数据对象来实现。主要的想法是使它在上下文中完整。
一种特定的技术或一种序列化的选择能对它有所帮助吗?当然,例如,Avro 有助于检测模式变化。Confluent 提供的模式注册表完全支持它。但是依赖于特定的技术会将解决方案与该特定的技术联系起来。这样做可能会有一些好处,但我更倾向于尽可能保持技术独立。
云事件
云事件是一个符合上述事件组合模式的具体标准例子。
上述相同的事件可以按照下面的云事件规范来表达。
{
"specversion": "1.0",
"type": "customer.added",
"source": "CRM-application-id",
"id": "9aeb0fdf-c01e-0131–0922–9eb54906e209",
"time": "2021–07–05T20:13:39.4589254Z",
"dataschema": "json-schema-url",
"data": {
"customerid":"cust-id",
"firstname":"some firstname",
"lastname":"some lastname"
}
}
这个事件的变体携带相同的信息,但是在组成上有一些不同。例如,没有显式标头。我通常将大部分字段归类为标题字段,作为消息的一部分。数据或核心状态变化在“数据元素内。“ dataschema ”指的是这个“ data ”元素的 JSON 模式,而不是整个事件。相反,整个事件的规范由“ specversion ”提供,只是作为一个版本号,在本例中为 1.0。
云事件中的事件组成可以如下图所示。

“云事件”中的事件消息组成
事件的真实性
通过给每条消息附加一个签名的散列,将有可能验证每个事件的真实性。
{
"header":{
…
"message-hash":{
"alg": "some algorithm",
"salt": "some salt",
"signed-hash": "0xa3f20717a25…fcfc994cee1b"
}
},
…
}
这样做的代价显然是每次处理消息时都需要对消息进行哈希和签名验证。因此,只有在消息真实性非常重要的情况下,才应该使用这种方法。
版本控制
就像 REST API 版本一样,事件也可以有版本。我认为事件也是 API,因为它们在消费者中引发反应。
在上面的例子中,事件的版本是隐式的。事件的模式包含版本变更。我的例子中的“ schema-url ”或者云事件中的“ dataschema ”和“ specversion ”的组合处理它。
但是在某些情况下,显式事件版本是必要的。一个简单的方法是将版本附加到事件描述符上。
"event-descriptor":"customer.added.v1"
或者在云事件中:
"type":"customer.added.v1"
需要注意的是,事件的新版本必须与旧版本具有相同的语义和含义。它应该指示完全相同的数据实体/主题的状态变化。如果不是,那就不是事件的新版本,而是新事件。
结论
任何技术或架构风格都可以被使用和误用。可以理解也可以误解或者只是部分理解。对于事件驱动的架构也是如此。要创建真正松耦合的应用程序和系统,关注事件、事件的组合、模式并接受它们将会改变的事实是至关重要的。
图式进化既是事实,也是必然的恶。一个独立于技术的解决方案将使一个持久的解决方案能够经受住组织中应用程序环境的变化和底层技术体系的变化。
为什么是自然的照片?我认为自然也是由事件驱动的。行动和反应无处不在,无时不刻不在发生。实际上,自然是事件驱动的生态系统的完美例子。
每个数据科学家都应该使用 PyCaret
意见
这就是为什么…机器学习的一站式商店

帕布鲁·阿罗约在Unsplash【1】上拍摄的照片。
目录
- 介绍
- 导入库和读取数据
- 设置模型参数
- 比较机器学习算法
- 创建、解释和预测最终模型
- 摘要
- 参考
介绍
尽管过去的数据科学家不得不使用相当多的代码来测试、比较和评估机器学习算法,但最近出现了 Python 中的库,大大减少了这项工作。其中一个库是由 Moez Ali 开发的py caret【2】,这是一个只需要少量代码的开源库,最终允许您快速准备数据,在几分钟内部署您的最终模型。PyCaret 函数有几个固有的好处。这些好处包括易用性、效率和学习新的机器学习算法。除了这些更多的好处之外,所有 PyCaret 模型都遵循四个主要步骤,这些步骤是执行一个进程的简单方法,否则,如果没有这个库,可能会花费更多的时间。也就是说,我将在下面讨论这四个步骤及其各自的好处和优势。
导入库和读取数据

与大多数数据科学项目一样,您将导入各自的库并读入数据。当然,我们将使用 PyCaret 的主库,以及另一个流行的库。
# import librariesfrom pycaret.regression import *
import pandas as pd
在这段代码中,您只需导入 PyCaret 库和回归模块,以及用于读取数据的熊猫。就 Python 代码而言,这几行代码是掌握大部分数据科学过程的开始。
# read in your datadata = pd.read_csv(‘file location of your data on your computer.csv’)
您可以导入的其他模块包括分类、聚类、异常检测、自然语言处理和关联规则挖掘。正如你所看到的,这个库包含了大多数类型的数据科学主题和特定的机器学习算法。
在这个回归模块中,您可以比较以下算法:
Linear RegressionLasso RegressionRidge RegressionElastic NetOrthogonal Matching PursuitBayesian RidgeGradient Boosting RegressorExtreme Gradient BoostingRandom Forest RegressorDecision Tree RegressorCatBoost RegressorLight Gradient Boosting MachineExtra Trees RegressorAdaBoost RegressorK Neighbors RegressorLasso Least Angle RegressionHuber RegressorPassive Aggressive RegressorLeast Angle Regression
利益
导入这个模块的好处是有大量的选项,而不必担心一个接一个地单独导入每个算法,最终节省时间并删除不必要的代码。
设置模型参数

约书亚·阿拉贡在Unsplash【4】拍摄的照片。
现在我们知道了我们正在使用的算法类型,我们可以启动 PyCaret 的 setup 函数。该功能包括数据类型推断、清理和预处理数据、数据采样、训练-测试分割以及为再现性分配会话 ID 等任务。
- 数据类型推断
设置功能可以允许您包括您的数据和目标,这是数据科学和机器学习算法的最基本的方面。数据类型的例子包括categorical、numeric和label。运行设置后,只需按 enter 键即可检查和确认列/要素的数据类型。
- 数据清洗和数据预处理
在此步骤中,将自动应用缺失值插补,以及分类编码。对于数值特征,平均值填充缺失值,而对于分类特征,缺失值填充该特征的模式。
—更多预处理包括序数编码、基数编码、修复不平衡、标准化、变换函数
—对于特征工程,有目标变换、特征交互组特征、宁滨数值特征和组合稀有水平函数
- 数据采样
这个特性与致力于改进数据科学过程的库相比是非常独特的。如果样本大小超过 25,000,将构建一个线性模型来显示大小如何影响模型的性能。
- 训练和测试分割
一个常见的函数,PyCaret 包括在其设置步骤。默认的拆分比例是 70:30。
- 会话 ID
这个特性的另一个词或短语是随机种子,或者有时是随机状态。它被包括在内,以便如果正在使用不同的环境,或者您将来正在从事这个项目,您可以重现您的发现。
# setup your regression parameters regression = setup(data = data,
target = 'what you are trying to predict’,
session_id = 200,
)
如您所见,上面的代码很简单,您已经完成了几乎一半的数据科学建模工作,实际上只有两部分代码。
利益
- 主要好处是您可以看到数据采样大小对常见指标(如准确性、AUC、召回率、精确度和 F1)的影响
- 无需转换即可自动推断数据类型(有时)
- 数字和分类特征的缺失值插补
比较机器学习算法

比较模型。作者截图[5]。
这个特性是 PyCaret 真正闪光的地方,它允许你比较几种机器学习算法,包括一些你可能还没有听说过的算法。有些算法在第一部分中已经提到,您可以对这些算法进行比较——并排比较准确度和误差指标,突出显示哪些算法是最好的。以下是其中的一些指标:
分类比较
- 准确性、AUC、召回率、精确度、F1 以及 Kappa 和 MCC
回归比较
- MAE,MSE,RMSE,R2,RMSLE,MAPE
compare_models()功能的一些重要特征是n_select、sort、include和exlucde。
- n_select :允许您根据精确度之类的指标返回最佳型号
- 排序:允许你根据你的度量进行排序,比如 MAE
- include :在您的比较中仅使用某些算法—如果您知道某些算法耗时较长且不太有用,您可以使用以下参数
- 排除:从您的分析中移除不必要的或更长的训练模型
# compare modelscompare_models(n_select = 3, sort = 'MAE')
以上是如何将这些参数应用于比较模型特性的示例代码块。
利益
- 减少分离或删除你所知道的某些算法的时间(从以前的经验中或从对你的数据的采样中)——比如说,比较 20 个算法可能需要几个小时,这取决于包括哪些算法
- 便于比较的排序( PyCaret 应自动突出显示哪个型号最好)
创建、解释和预测最终模型

举例 SHAP 解释情节。作者截图[6]。
在 PyCaret 过程的最后一部分,你可以在看到几个模型的比较后创建一个最好的模型(准确性,训练时间——这最终取决于你 ***best***)。
创建模型功能的一些关键参数如下:
- 折叠
- 交叉验证
- 学习率
- 最大深度
- 等等。
模型或机器学习名称被简化,例如,决策树是‘dt’,所以在这种情况下,让我们假设我们选择它来创建、预测和解释我们的最终模型。
在解释您的模型时,您可以查看无数的函数,包括:
- 汇总图
- 相关图
- 观察级别的原因图
这些情节是从广受欢迎的 SHAP 图书馆(,在我看来,这是最好的功能重要解释库
# create a model
dectrees = create_model('dt')# interpreting model
interpret_model(dectrees)# predict on test set
predictions = predict_model(dectrees)
利益
- 您可以轻松选择想要创建的模型
- 您可以轻松访问 SHAP 函数和相应的图
只需几行代码,您就可以选择想要使用的模型,解释特性和结果,最后根据一些测试数据进行预测。您甚至可以使用 PyCaret 和 AWS 部署模型。与 AutoML 和 MLFlow 的集成也很有用。
摘要
如您所见,PyCaret 的每个主要步骤都有各自的好处。虽然这篇文章出于解释的目的很长。我使用的代码非常简单,用于比较几个模型和预测测试数据。
总而言之,我们看了几个步骤,包括:
- 导入库和读取数据
- 设置模型参数
- 比较机器学习算法
- 创建、解释和预测最终模型
这些相同的步骤通过下面的代码表达出来:
# import libraries
from pycaret.regression import *
import pandas as pd# read in your data
spotify = pd.read_csv(‘file location of your data on your computer.csv’)# setup your regression parameters
regression = setup(data = data,
target = 'what you are trying to predict’,
session_id = 200,
)# compare models
compare_models()# create a model
dectrees = create_model('dt')# predict on test set
predictions = predict_model(dectrees)# interpreting model
interpret_model(dectrees)
其他一些有用的特性(,因为我还没有涵盖 PyCaret 的所有方面),包括但不限于消除多重共线性、主要胜任分析、忽略低方差和消除异常值
我希望您喜欢这篇关于每个人都应该知道的库的文章,PyCaret。现在,我将问你一些问题。你以前用过这个图书馆吗——为什么或为什么不用?你听说过吗?您是否喜欢更加人工化的数据科学过程,或者更具体地说,没有更像一站式商店的图书馆?使用这个强大的数据科学和机器学习库,你还体验到了哪些好处?谢谢你的阅读,我很感激!
请随时查看我的个人资料和其他文章, Matt Przybyla ,也可以在 LinkedIn 上联系我。
我不隶属于上述任何公司。
参考
[1]巴勃罗·阿罗约在 Unsplash 拍摄的照片,(2021)
[2] Moez Ali , PyCaret 主页,(2021)
[4]Joshua Aragon 在 Unsplash 上拍摄的照片,(2019)
[5] M.Przybyla,比较模型截图,(2021 年)
[6] M.Przybyla,示例 SHAP 解读情节截图,(2021)
你可以(应该)使用的每一个熊猫函数来操纵时间序列
从基本的时间序列度量到窗口函数

时间序列预测项目简介
最近,在 Kaggle 上发起了 Optiver Realized 波动率预测竞赛。顾名思义,这是一个时间序列预测的挑战。
我想参加,但事实证明我在时间序列方面的知识甚至不足以参加如此大规模的比赛。所以,我接受了这一点,认为这是我需要开始认真关注 ML 这个大领域的‘刺激’。
作为第一步,我想学习和教授你可以用来操作时间序列数据的每一个熊猫函数。这些函数是处理您在野外遇到的任何时间序列数据的基本要求。
我已经为这个话题准备了相当酷和有趣的文章,今天,你将会读到即将到来的第一个味道。尽情享受吧!
https://ibexorigin.medium.com/membership
获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:
https://alphasignal.ai/?referrer=Bex
目录
这篇文章的笔记本可以在 Kaggle 上这里阅读。
- 基本数据和时间函数
- 时间序列中缺失数据的插补/插值
- 基本时间序列计算和指标
- 重采样—上采样和下采样
- 比较多个时间序列的增长
- 窗口功能
- 摘要
1.熊猫的基本日期和时间功能
1.1 导入时间序列数据
当使用pd.read_csv函数导入时间序列时,有两个参数你应该一直使用- parse_dates和index_col:
由于出版政策,数据集已被匿名化。如果你感兴趣,数据集的真实版本保存在笔记本中。请注意,这种匿名不会妨碍您的体验。


parse_dates将类似日期的字符串转换为 DateTime 对象,index_col将传递的列设置为索引。该操作是对熊猫进行所有时间序列操作的基础。
当您在导入时不知道哪一列包含日期时,您可以在之后使用pd.to_datetime功能进行日期转换:
现在,检查日期时间格式字符串:
>>> data2.head()

它的格式是" %Y-%m-%d "(日期时间格式字符串的完整列表可以在这里找到)。把这个传给pd.to_datetime:
向pd.to_datetime传递一个格式字符串可以显著加快大型数据集的转换速度。将errors设置为“强制”,将无效日期标记为NaT(不是日期,即-缺失)。
转换后,将 DateTime 列设置为索引(这是最佳时间序列分析的严格要求):
>>> data2.set_index("date", inplace=True)
1.2 熊猫时间戳
Pandas 中的基本日期数据结构是时间戳:
您可以使用正确的格式制作更精细的时间戳,或者更好的是,使用datetime模块:
完整时间戳具有如下有用的属性:

1.3 日期序列(时间戳)
pandas 中的一个DateTime列/索引被表示为一系列TimeStamp对象。
pd.date_range返回一个特殊的DateTimeIndex对象,它是TimeStamps的集合,具有给定范围内的自定义频率:
在指定日期范围(从 2010 年 10 月 10 日到 2020 年的同一天)后,我们告诉熊猫每月生成TimeStamps和freq='M':
>>> index[0]Timestamp('2010-10-31 00:00:00', freq='M')
创建日期范围的另一种方法是传递开始日期,告诉您需要多少个周期,并指定频率:
因为我们将频率设置为年,所以带有 5 个周期的date_range返回 5 年/时间戳对象。可以传递给freq的频率别名列表很大,所以我在这里只提到最重要的几个:

也可以通过自定义频率,如“1 小时 30 分”、“5D”、“2W”等。再次,检查这个链接的完整信息。
1.4 切片
如果索引是一个DateTimeIndex,切片时间序列数据会非常直观。你可以使用部分切片:


如果日期时间足够精确,甚至可以达到小时、分钟或秒的级别。
请注意,熊猫切片日期在封闭的时间间隔。例如,使用“2010”:“2013”返回所有 4 年的行—它不像整数切片那样排除期末。
这个日期切片逻辑适用于其他操作,如在切片后选择特定的列:
2.缺失数据插补或插值
无论数据集的类型如何,缺失数据都是普遍存在的。这一部分是关于在时间序列的背景下对其进行估算。
你也可能在时间序列行话中听到它被称为缺失数据的插值。
除了基本的均值、中值和众数插补之外,一些最常用的技术包括:
- 正向填充
- 反向填充
- 使用
pd.interpolate进行中间插补
我们还将讨论基于模型的插补,如 KNN 插补。此外,我们将探索比较技术效率的可视化方法,并选择最适合底层分布的方法。
2.1 均值、中值和众数插补
让我们从基础开始。我们将在第一个匿名数据集中随机选择数据点,并将其转换为 NaN:
我们还将创建一个函数,绘制插补执行前后的原始分布:
我们将从 Sklearn 的SimpleImputer开始尝试技巧:
让我们针对我们刚刚创建的 3 个估算特征绘制原始feature_2分布图:

很难说哪条线最像黑线,但我会选绿色。
2.2 向前和向后填充
考虑这个小分布:

我们将使用向前和向后填充,并将它们作为单独的列分配回数据帧:

一旦检查了上面的输出,这些方法是如何工作的就应该很明显了。
现在,让我们对印度的空气质量数据集执行这些方法:

尽管非常基本,向前和向后填充实际上对气候和股票数据非常有效,因为附近数据点之间的差异很小。
2.3 使用pd.interpolate
Pandas 在pd.interpolate功能中提供了一整套其他统计插补技术。它的method参数接受技术名称作为字符串。
最流行的是“线性”和“最近”,但是你可以从函数的文档中看到完整的列表。在这里,我们将只讨论这两个。
考虑这个小分布:

我们再次应用这些方法,并将它们的结果赋回来:

很整洁,是吧?线性方法将任意两个非缺失点之间的距离视为线性间隔,并找到连接它们的直线(如np.linspace)。“Nearest”方法应该可以从它的名字和上面的输出中理解。
2.4 基于模型的 KNN 插补
我们将看到的最后一种方法是 K-最近邻算法。我不会详细说明算法是如何工作的,而只是展示如何在 Sklearn 中使用它。如果你想知道细节,我有一个单独的文章在这里。
KNN 最重要的参数是k——邻居的数量。我们将把该技术应用于具有几个值k的第一个数据集,并像我们在前面章节中所做的那样找到最佳的一个:

3.基本时间序列计算
Pandas 提供了计算最常见的时间序列计算的基本函数。这些被称为转移,滞后,以及所谓的百分比变化。
3.1 换档和滞后
时间序列中的一个常见操作是将所有数据点向后或向前移动一个或多个周期,以比较过去和未来的值。你可以使用熊猫的shift功能来完成这些操作。让我们看看如何将数据点 1 和 2 移至未来:


向前移动使您能够将当前数据点与一个或多个期间之前记录的数据点进行比较。
你也可以向后移动。这种操作也称为“滞后”:

向后移动使我们能够看到当前数据点和一个或多个周期后的数据点之间的差异。
换档或滞后后的常见操作是找出差异并绘制出来:


由于这种操作非常常见,Pandas 提供了diff函数来计算基于周期的差异:

3.2 百分比变化
可以从时间序列数据中得出的另一个常见指标是每日百分比变化:

要计算每天的百分比变化,向前移动一个周期,将原始分布除以移动后的分布,然后减去 1。结果值以前一天的比例给出。
因为这是一个常见的操作,Pandas 用pct_change函数来实现它:

4.重采样
通常,您可能希望增加或减少时间序列的粒度,以生成新的见解。这些操作被称为重采样或改变时间序列的频率,我们将在本节中讨论与它们相关的 Pandas 函数。
4.1 用asfreq改变频率
第二个数据集没有固定的日期频率,即每个日期之间的周期差异不相同:
data2.head()

让我们通过给它一个日历日频率(每日)来解决这个问题:
data2.asfreq("D").head(7)

我们只是把约会的频率变得更细了。结果,添加了新的日期,导致更多的值丢失。现在,您可以使用我们之前讨论过的任何技术对它们进行插值。
你可以在这里看到的内置频率别名列表。更有趣的场景是使用自定义频率:


# 10 month frequency
data2.asfreq("10M", method="bfill").head(7)

还有一个reindex函数,操作类似,支持额外的缺失值填充逻辑。我们不会在这里讨论它,因为我们会考虑更好的选择。
4.2 使用resample进行下采样并聚合
在时间序列行话中,降低DateTime的频率被称为下采样。例如,将频率从每小时更改为每天,从每天更改为每周,等等。
我们看到了如何使用asfreq进行下采样。一个更强大的选择是resample,它的行为类似于pd.groupby。就像groupby根据分类值对数据分组一样,resample根据日期频率对数据分组。
让我们按月末频率对第一个数据集进行缩减采样:
与asfreq不同,使用重采样只返回重采样状态的数据。要查看每个组,我们需要使用某种类型的函数,类似于我们使用groupby的方式。
由于下采样减少了数据点的数量,我们需要一个聚合函数,如 mean、median 或 mode:
data1.resample("M").mean().tail()

还有一些函数返回一个组的第一条或最后一条记录:


也可以使用agg使用多个聚合功能:

4.3 使用resample进行上采样和插值
缩减采样的反面是使DateTime更加精细。这称为上采样,包括将频率从每天一次改为每小时一次、每小时一次改为每秒一次等操作。
向上采样时,您会引入新的日期,导致更多的值丢失。这意味着你需要使用某种类型的插补:


4.4 绘制重采样数据
如果不绘制结果,重采样不会产生太多效果。
在大多数情况下,进行缩减采样时,您会看到新的趋势和模式。这是因为下采样降低了粒度,从而消除了噪声:


绘制上采样分布只会引入更多噪声,所以我们在这里不做。
5.比较多个时间序列的增长
比较两个或多个随时间变化的数值是很常见的。例如,我们可能希望看到第一个数据集中要素 1 和 2 的增长率。但问题是:
第二分布具有高得多的值。将它们绘制在一起可能会将 feature_1 挤压成一条直线。换句话说,这两种分布具有不同的规模。
为了解决这个问题,统计学家使用标准化。最常见的变化是选择第一个记录的值,然后将其余的样本除以该值。这显示了每个记录与第一个记录相比是如何变化的。
这里有一个例子:

现在,上面输出中的每一行都显示了与第一行相比的百分比增长。
现在,让我们绘制它们来比较增长率:

从 2011 年到 2017 年,这两项功能都实现了超过 300%的增长。您甚至可以从其他数据集绘制时间序列:

如您所见,数据集 1 中的要素比第二个数据集中的另一个要素具有更高的增长率。
6.窗口功能
还有另一种类型的函数可以帮助您以新颖的方式分析时间序列数据。这些被称为窗口函数,它们帮助您聚合自定义数量的行,称为“窗口”
例如,我可以在我的中型订户数据上创建一个 30 天的窗口,以查看过去 30 天中任何一天的订户总数。或者,餐馆老板可以创建一个每周窗口来查看上周的平均销售额。例子不胜枚举,因为您可以在数据上创建任意大小的窗口。
让我们更详细地探讨这些。
6.1 滚动窗口功能
滚动窗口功能将具有相同的长度。当它们滑过数据时,它们的覆盖范围(行数不变)。以下是一个包含 5 个时间段的数据滑动窗口示例:

下面是我们如何在 pandas 中创建滚动窗口:
>>> data1.rolling(window=5)Rolling [window=5,center=False,axis=0]
就像resample一样,它处于只读状态——为了使用每个窗口,我们应该链接某种类型的函数。例如,让我们为过去 5 个周期创建一个累计和:

显然,前 4 排是 NaNs。任何其他行将包含前 4 行和当前行的总和。
请注意窗口参数。如果传递一个整数,窗口大小将由该行数决定。如果您传递一个频率别名,如月、年、5 小时或 7 周,则窗口大小将是包含传递频率的单个单位的任意行数。换句话说,5 周期窗口可能与 5 天频率窗口大小不同。
例如,让我们绘制特性 2 的 90 和 360 天移动平均线,并绘制它们:

就像groupby和resample一样,您可以使用agg函数为每个窗口计算多个指标。
6.2 扩展窗口功能
另一种类型的窗口函数处理扩展窗口。每个新窗口将包含截至当前日期的所有记录:

扩展窗口对于计算“运行”指标非常有用,例如,运行总和、平均值、最小值和最大值、运行回报率等。
下面,您将看到如何计算累积和。累积和实际上是一个窗口大小为 1:

expanding函数有一个min_periods参数,决定初始窗口大小。
现在,让我们看看如何绘制跑步最小值和最大值:

摘要
我想祝贺是应该的!
现在,您知道了可以用来操作时间序列数据的每一个 Pandas 函数。这是一篇非常长的帖子,但绝对值得,因为现在你可以处理任何扔给你的时间序列数据。
这篇文章主要关注数据操作。该系列的下一篇文章将是关于更深入的时间序列分析,你可以在时间序列上创建的每个单独的图的类似文章,以及关于预测的专门文章。敬请期待!
您可能也会感兴趣…
每个人都可以很容易地学习编程——如果他们懂英语的话
说外语的人在学习编程时有一些障碍,我们能做些什么来帮助他们

安妮·斯普拉特在 Unsplash 上的照片
随着技术颠覆一个又一个行业,世界各地的人们都在学习计算机编程。第一个万维网的愿景是在互联网上分享知识和建立全球信任。在谷歌和其他应用程序的帮助下,我们都可以通过互联网学习如何编写程序。
虽然互联网有助于创建一个协议和平台,以方便共享知识,但大多数编程语言、文档资料和在线教学都是英语。
这使得非英语母语者学习计算机编程和在世界范围内分享他们的知识成为一个障碍。
Imagine if the most popular programming language you are using right now to create your website, HTML, is not English but Chinese. When you are about to inspect a beautiful website by inspecting its source code online — it is all in Kanji. Instead of having a <title> tag, it will have <稱號> tag. Instead of having <article>, it will have <文章>. You still feel motivated and want to tinker with the web. Therefore, you look up material online. However, you realized that the answer to the most popular programming community's questions is also in Chinese. You feel frustrated and drop learning HTML and learn Kanji first.
理论上,计算机不关心我们用什么语言编码,只要那些语言能编译成机器能理解的 0 和 1 的字节码。if语句、while语句和for语句帮助人类轻松地设计计算机指令,以便编译器可以将这些指令翻译成字节码。
但是我们中只有一些人知道所有这些命令——那些说英语的人。95%的世界人口的母语不是英语。世界上的开源学习课程材料都是英文的。大多数流行的编程语言都使用英语中的例子。甚至搜索流行的编程社区 Stack Overflow 也需要英文。虽然你可以使用谷歌翻译来翻译每个句子,但翻译质量可能会误导或混淆。这给非英语母语者获取编程知识和向技术社区表达他们的意图造成了巨大的障碍。
这篇文章想分享的是非英语母语的人学习计算机编程有两个障碍。之后,我将提到三个解决方案,作为一名软件工程师,我们可以创建一个更好的社区,邀请非英语母语者轻松导航并拓宽世界各地人们的数字素养和工作机会。
文档大部分是英文的
不管是好是坏,我们必须承认英语是科技领域的主导语言。大多数流行的编程语言及其文档都是英文的。尽管在一些广泛传播的文档中有几种翻译,但翻译深奥的技术术语通常会让学习者更加困惑。
一项研究表明,与一般阅读综合的交流显著影响一个人掌握新知识的能力——他们必须用一半的脑力来解释一个英语句子,另一半的脑力来学习编程语言的新术语[4]。
非英语母语者希望将他们的编程思想作为文档来表达,也很难描述它们。例如,Redis 的创始人 Salvatore Sanfilippo 谈到了他在用英语表达思想方面的挣扎。他们将要进行一次 TCP/IP 攻击,但是他们不能用英语写一篇关于它的帖子,因为他已经投入了 50%的精力来理解他写的句子[2]。
记得追到了儿时的好友,台湾本地一所大学的计算机专业。他说,每个需要学习编程的人都需要有一个中级英语,因为跟上不是用母语教授的课程可能是一个挑战。
他们很难读写代码
我们鼓励开发人员编写可读和可维护的代码。
然而,这段可读代码要求用英语创建代码标识符、变量、函数和类名。大多数编程语言的关键词,比如if、else、while都是基于英文的。你可以说,说技术英语的人在学习大多数编程语言时并不比说英语的人更困难,因为大多数语法都来自数学。你可以说大多数关键词- if、else、while、type def、【阶级】或“类型”并不完全是它们在英语中的意思。这些“变量”在编程中都有特定的含义。然而,其他非英语母语的人很难理解while、class 或instances的意思,因为这些关键词在他们的母语中可能有更重要的意义。例如,while在葡萄牙语中的含义比《法典》中使用的含义更多[4]。
除了标准的编程语言关键字,非英语母语的人很难读懂缩写的函数名——像 C 语言中的getCh()或strstr()这样的东西很难理解。
有些语言不遵守主谓关系。日语的语法是主语-宾语-动词。因此,他们很难写出 getter 和 setter,因为语法成分与日语不同。此外,许多行为驱动的测试场景模仿完整的英语句子来测试应用程序中的特定行为。例如,如果你想用 Scala 编写一个单元测试,一个Matcher类可以让你构建测试用例,就像用英语写一个句子一样。
在编写代码方面,我发现大多数说外语的人在用英语命名事物方面做得很好,但是他们很难调用其函数的行为。一些函数可能有一个没有意义的名字——在英语中没有意义的东西。一些函数名甚至可能是误导性的——这是很危险的——因为用母语命名不同于用英语命名。
既然第一个万维网和所有的协议都是用字母和英文单词写的,那是不是意味着每个想学编码的人都需要先学英语?
可能吧。学习英语会帮助你更容易理解专业术语。然而,也有一些方法可以帮助其他人更轻松地理解您的库或源代码。
使用完整的变量名,避免任何来自你的文化的俚语
我们需要编写尽可能文化不可知论的文档。
命名函数不应缩写。使用像numList或getCh这样的缩写词会让非英语母语的人感到困惑。因为这种语言不通过其语法表达任何其他行为,所以开发人员需要理解函数的名称来了解过程的意图。在不知道函数名的情况下,你只能猜测函数输出会是什么。因此,最好用一个完整的句子来描述你的函数名,比如getCharacter或者listOfNumber。
一些编程语言,如 Clojure 或 Haskell,有选择退出的命名变量,并将它们命名为f或g,因为它的函数定义已经描述了它的作用。
Clojure API 中的一些变量命名约定有特定的函数式编程技术术语和数学术语函数名,如reify或transducer,以避免给客户带来任何误解。
函数式编程语言的一个强大工具是它们的语法就像一个数学公式。不用看过程的名字就可以知道函数在做什么。例如,如果我们想在一个元素的列表上做一些组合。我们可以这样写:def f[A:Monoid](list:List[A]): A,它告诉我们“如果我把 A 的一个列表作为输入,函数会返回 A”当然,不得不说f是很笼统的,不适合对一个列表求和的用例。然而,函数定义本身和一个简单的注释可以帮助非英语母语的人了解对f的期望以及他们如何实现它们。
例子多,文字少
文档应该展示比文本更多的例子。在节目文档中加入任何美国名人的笑话或电视节目,以使阅读文档变得更容易管理,这对观众没有帮助。
大多数观众不知道美国名人,也不知道《T4》中的任何笑话。因此,它将有助于解释如何处理客户使用该库时出现的问题——解释错误消息的含义以及它们最初出现的原因——此外,通过示例代码为客户解决遇到的问题提供指导。
例如,大多数图书馆都有一本“入门”指南,上面有一个使用图书馆的简单例子。一开始解释设计库和所有其他特定功能背后的哲学会让客户更加困惑。
如果你解释一些难以理解的关键词,一本解释这些词的意思的词典可以帮助你的读者在阅读上下文时理解那些不熟悉的术语。例如,具体化应该有一个内联字典说明它在数学上下文中的含义。在这种情况下,reify 是一个计算机编程术语,表示运行时类型的抽象概念。
你也可以给出这个概念的外部链接,这样你的读者就可以通过点击外部链接来搜索这些术语。
在技术指导材料中使用更多的插图和可视化
一项研究表明,确保学习者将他们的信息储存在长期记忆中的最简单的方法是将课程材料与某种形式的可视化结合起来[1]。单词只保留在我们的短时记忆中,我们只能保留 7 比特的信息。一幅图像帮助我们将信息立即转移到长期记忆中。
可视化可以很快超越语言。例如,如果没有任何可视化和一步一步的演示,理解 Java 中的递归是如何工作的会很有挑战性。通过适当的绘图,我们可以说明函数的每个堆栈在每次递归调用时的行为,以及当过程返回时每个堆栈帧的行为,这可以帮助学习者可视化和跟踪递归函数。
插图和可视化图像有助于图书馆作者阐明他们的设计理念。例如,解释 React DOM 的定义和用途在图像中比在文本中更容易可视化。
结论
对于我们这些精通英语的人来说,编程很难。对于那些完全不懂英语的人来说,编程更具挑战性。
对于非英语母语的人来说,编程更难的一个原因是,学习编程的所有相关材料都不是他们的母语。
当编写或阅读源代码时,他们很难构建一个可读的代码,因为他们不能像英语母语的编码人员那样有表现力地命名函数。
然而,我们可以通过在我们的代码、文档和社区中创建一种更具包容性的语言,为非英语母语者学习编程创造更顺畅的体验。此外,有更多的图像和视频来说明一个编码概念可以快速帮助其他人了解这个想法。
了解这些问题有助于我们编写更多包含语言的文档,并在与非英语母语者分享知识时更有耐心和同情心。
假设我们想让编程成为表达想法和思想的通用工具。在这种情况下,我们必须关心我们在常规博客帖子中提到的内容,以及我们的文档、函数和代码,以便让每个人都能普遍使用。换句话说,我们的计算机必须学习通用语言:移情。
参考
[1]管理员,管理员。研究证实了视觉在电子学习中吸引观众的力量,2014 年 7 月 9 日,www . shiftlearning . com/blog/bid/350326/studies-Confirm-the Power of visual-ELearning。
[2]基斯蒂亚科夫,阿尔特姆。编程语言,2017 年 6 月 28 日,temochka . com/blog/posts/2017/06/28/the-Language-of-Programming . html。
[3]麦卡洛克,格雷琴。"编码是为每个人准备的——只要你会说英语."Wired,Conde Nast,2019 年 8 月 4 日上午 10 点,[www . wired . com/story/coding-is-for-every one as-long-as-you-speak-English/#:~:text = In % 20 addition % 20 to % 20 these % 20 four,System%20(孟加拉语%2C%20Gujarati%2C%20and。](http://www.wired.com/story/coding-is-for-everyoneas-long-as-you-speak-english/#:~:text=In addition to these four,System (Bengali%2C Gujarati%2C and.)
[4]郭敬明。非英语母语者学习计算机编程:障碍、愿望和设计机会。检索自https://pg . ucsd . edu/publications/non-native-English-speakers-learning-programming _ CHI-2018 . pdf
感谢阅读!如果你喜欢这篇文章,你可以关注我在媒体上的更多文章。我期待着与你通信。万事如意。
【https://edward-huang.com】最初发表于https://edward-huang.com/ideas/life-lesson/2021/02/23/everyone-can-learn-programming-easily-if-they-know-english/。
关于 Python 字典数据结构的一切:初学者指南
在本文中,我们将重点介绍 Python 字典数据结构的完整演示。

目录
- 什么是 Python 字典?
- 如何创建 Python 字典
- 如何从 Python 字典中访问值
- 如何向 Python 字典添加元素
- 如何从 Python 字典中移除元素
- 如何更改 Python 字典中的元素
- 如何迭代 Python 字典
- Python 中的嵌套字典
什么是 Python 字典?
Python 字典是用于存储对象组的数据结构。它由键-值对的映射组成,其中每个键都与一个值相关联。它可以包含相同或不同数据类型的数据,是无序的,并且是可变的。
如何创建 Python 字典
空字典
要在 Python 中初始化一个空字典,我们可以简单地运行下面的代码并打印其内容:
您应该得到:
{}
带值的字典
当我们想要创建一个包含一些想要填充的值的字典时,我们将这些值作为一系列逗号分隔的键-值对来添加。例如,假设我们想要创建一个以国家为键、以人口为值的字典:
您应该得到:
{'China': 1439323776, 'India': 1380004385, 'USA': 331002651, 'Indonesia': 273523615, 'Pakistan': 220892340, 'Brazil': 212559417}
如何从 Python 字典中访问值
为了访问存储在 Python 字典中的值,我们应该使用与值相关联的键。例如,假设我们想从上面的国家字典中得到美国的人口。我们知道人口值的关键字是“USA ”,我们用它来访问人口值:
您应该得到:
331002651
注意:与 Python list 不同,您不能使用索引从字典中访问值。访问这些值的唯一方法是搜索字典中存在的关键字。
如何向 Python 字典添加元素
在本节中,我们将继续使用 countries 字典,并讨论向 Python 字典添加元素的方法。
添加单个元素
假设我们想要将另一个国家及其人口添加到我们的国家字典中。例如,我们要添加日本的人口 126,476,461。通过将它作为一个额外的键值对添加到字典中,我们可以很容易地做到这一点:
您应该得到:
{'China': 1439323776, 'India': 1380004385, 'USA': 331002651, 'Indonesia': 273523615, 'Pakistan': 220892340, 'Brazil': 212559417, 'Japan': 126476461}
您可以看到,我们已经成功地向字典中添加了一个新元素。
添加多个元素
现在,如果我们想要添加多个国家呢?假设我们现在想在字典中再添加两个国家及其人口。例如,俄罗斯和墨西哥的人口分别为 145,934,462 和 128,932,753。
相同的语法会起作用吗?不完全是。所以我们需要这个。update()Python 字典数据结构的方法。它允许向字典中添加多个逗号分隔的键值对。
逻辑是从新的键-值对创建一个新字典( new_countries ),然后将其合并到 countries 字典中:
您应该得到:
{'China': 1439323776, 'India': 1380004385, 'USA': 331002651, 'Indonesia': 273523615, 'Pakistan': 220892340, 'Brazil': 212559417, 'Japan': 126476461, 'Russia': 145934462, 'Mexico': 128932753}
您可以看到,我们已经成功地向词典中添加了新元素。
如何从 Python 字典中移除元素
在本节中,我们继续使用 countries 字典,并讨论从 Python 字典中移除元素的方法。
移除单个元素
假设我们需要做一些修改,从字典中删除一个针对中国及其人口的键-值对。我们可以使用轻松移除它。pop() 方法:
您应该得到:
{'India': 1380004385, 'USA': 331002651, 'Indonesia': 273523615, 'Pakistan': 220892340, 'Brazil': 212559417, 'Japan': 126476461, 'Russia': 145934462, 'Mexico': 128932753}
您可以看到,我们已经成功地从字典中删除了一个元素。
移除多个元素
下一步是探索如何从 Python 字典中移除多个元素。假设我们想从国家字典中删除日本和墨西哥及其各自的人口。
我们知道。pop() 方法允许在每个函数调用中移除单个元素,这给了我们一个想法,如果我们迭代一个包含我们想要移除的键的列表,我们就可以成功地调用。pop() 对于每个条目:
您应该得到:
{'India': 1380004385, 'USA': 331002651, 'Indonesia': 273523615, 'Pakistan': 220892340, 'Brazil': 212559417, 'Russia': 145934462}
您可以看到,我们已经成功地从字典中删除了元素。
如何更改 Python 字典中的元素
另一个功能是改变 Python 字典中的元素。您将在以下章节中看到,更改元素的功能与添加元素的功能相同。
这是为什么呢?这是因为当我们试图向字典中添加新元素时,Python 会查找我们试图添加的特定键,如果字典中存在该键,它会覆盖数据;但是如果键不存在,它会向字典中添加一个新的键-值对。
更改单个元素
假设我们想在国家字典中将巴西的人口值更新为 212560000:
您应该得到:
{'India': 1380004385, 'USA': 331002651, 'Indonesia': 273523615, 'Pakistan': 220892340, 'Brazil': 212560000, 'Russia': 145934462}
更改多个元素
现在,假设我们想在 countries 字典中将印度尼西亚和巴基斯坦人口的值分别更新为 273530000 和 220900000。
逻辑是从新的键值对创建一个新的字典( update_countries ),然后更新 countries 字典中现有的键值对:
您应该得到:
{'India': 1380004385, 'USA': 331002651, 'Indonesia': 273530000, 'Pakistan': 220900000, 'Brazil': 212560000, 'Russia': 145934462}
如何迭代 Python 字典
在这一节中,我们将关注 Python 字典的不同迭代方式。
迭代字典键
假设我们想要迭代 countries 字典中的键,并在单独的行上打印每个键(在我们的例子中是每个国家)。
我们将简单地使用一个和一起作为循环。keys()【字典法:
您应该得到:
India
USA
Indonesia
Pakistan
Brazil
Russia
迭代字典值
另一个用例可能是我们想要找到存储在 countries 字典中的所有国家的人口总数。
正如你所想象的,我们将需要再次使用作为循环,现在我们也将使用。字典法取值():
您应该得到:
2563931498
迭代字典条目
Python 字典的一项是它的键值对。这允许我们一起对键和值进行迭代。
我们如何使用它?假设您想从 countries 字典中找到人口最多的国家。迭代字典的每一项允许我们同时跟踪键和值:
您应该得到:
India 1380004385
Python 中的嵌套字典
嵌套字典是由其他字典组成的字典。
你可以用类似于创建字典的方式来创建嵌套字典。
例如,假设我们想要创建一个包含每个国家首都及其人口信息的字典:
您应该得到:
{'China': {'capital': 'Beijing', 'population': 1439323776}, 'India': {'capital': 'New Delhi', 'population': 1380004385}, 'USA': {'capital': 'Washington, D.C.', 'population': 331002651}, 'Indonesia': {'capital': 'Jakarta', 'population': 273523615}, 'Pakistan': {'capital': 'Islamabad', 'population': 220892340}, 'Brazil': {'capital': 'Brasilia', 'population': 212559417}}
结论
这篇文章是关于 Python 字典及其方法的介绍性演练,学习这些方法很重要,因为它们被用于编程和机器学习的许多领域。
如果你有任何问题或者对编辑有任何建议,请在下面留下你的评论。
原载于 2021 年 11 月 21 日【https://pyshark.com】。
关于 Python 集合数据结构的一切:初学者指南

杰斯·贝利在 Unsplash 上的照片
在本文中,我们将重点介绍一个完整的 Python 集合数据结构。
目录
- 什么是 Python 集合
- 如何创建 Python 集
- 如何向 Python 集添加元素
- 如何从 Python 集合中移除元素
- 如何迭代 Python 集
- 结论
什么是 Python 集合
Python 集合是一种数据结构,用于存储唯一元素的无序集合。集合本身是可变的,但由不可变数据类型的元素组成(如整数、浮点、布尔、字符串、元组等)。集合不能由可变数据类型的元素组成(比如 list 、 dictionary 等等)。
如何创建 Python 集
空集
要在 Python 中初始化一个空集,我们可以简单地运行下面的代码并打印其内容:
您应该得到:
set()
用整数元素设置
当我们想要创建一个包含元素的集合时,我们可以用花括号创建一个逗号分隔的元素序列:
您应该得到:
{1, 2, 3, 4, 5}
用字符串元素设置
另一个例子是一组字符串数据类型的元素:
您应该得到:
{'Car', 'Apple', 'Hello'}
用布尔元素设置
当处理布尔数据类型的元素时,集合中唯一元素的属性非常重要。假设我们有一些想要存储的元素,这是 5 个真/假元素。注意,在元素中,只有两个唯一的:True 和 False。如果我们将这些元素存储在一个集合中,它将自动进行重复数据删除:
您应该得到:
{False, True}
请注意重复元素是如何从集合中删除的。
具有混合类型元素的集合
另一种选择是让集合包含不可变数据类型的不同元素。例如,它可以是字符串、整数、布尔值和浮点数:
您应该得到:
{True, 2, 3.01, 'Hello'}
请注意,该集合基于元素值是无序的。然而,我发现有趣的是,它是根据数据类型名称的第一个字符排序的。看这个顺序:{ b oolean, i nteger, f loat, s tring},有一个字母的模式。
如何向 Python 集添加元素
在这一节中,我们将讨论向 Python 集合中添加元素的方法。
在本节中,让我们使用下面创建的 Python 集合:
添加单个元素
假设我们想在上面的集合中添加另一个整数(8)。我们将需要使用。Python 集合的 add() 方法,并将新元素传递给它:
您应该得到:
{8, 1, 5, 6}
注意:您只能添加集合中不存在的元素。如果您尝试添加一个已经存在的元素,比如 integer 5,您将简单地得到相同的集合,因为该元素已经存在于其中。
添加多个元素
现在让我们来考虑一个例子,当我们想在上面的集合中添加多个新元素时,比如整数 3 和 9。
相同的语法会起作用吗?不完全是。我们将需要使用。update() 方法,并将新元素作为集合或列表传递给它。
方法一:
方法二:
这两种方法将产生相同的输出:
{1, 3, 5, 6, 8, 9}
如何从 Python 集合中移除元素
在这一节中,我们将讨论从 Python 集合中移除元素的方法。
首先,让我们创建一个将要使用的集合:
移除单个元素
假设我们想要删除这个集合中的一个元素,例如整数 8。我们可以很容易地做到这一点使用任何一个。丢弃()或。Python 集合的 remove() 方法。
现在这两者的区别是什么?
。remove() 方法从集合中删除一个元素,如果该元素存在于集合中,如果不存在,那么它将引发一个错误(KeyError)。
另一方面,。discard() 方法做完全相同的事情,但是不会引发错误,并且保持集合不变。
方法一:
方法二:
在我们的例子中,这两种方法将产生相同的输出:
{1, 3, 5, 6, 9}
移除多个元素
如果我们想删除多个元素,比如整数 3 和 5,我们可以使用类似的语法。唯一的问题是。移除()和。Python 集合的 discard() 方法每次调用只接受一个参数。
如果我们创建一个要删除的元素列表,并遍历它,从一个集合中一个接一个地删除它们,这两种方法都可以工作。
方法一:
方法二:
在我们的例子中,这两种方法将产生相同的输出:
{1, 6, 9}
移除所有元素
如果我们想从任何 Python 集合中移除所有元素并得到一个空集,我们可以简单地调用。clear() 将删除所有元素的方法:
你应该得到一个空集:
set()
如何迭代 Python 集
在这一节中,我们将讨论如何迭代一个 Python 集合。
首先,让我们创建一个我们将使用的样品组:
与列表和字典不同,在迭代一个 Python 集合时没有太多选项。最主要的是简单地使用 for 循环:
您应该得到:
1 3 5 6 8 9
结论
这篇文章是关于 Python set 及其方法的介绍性演练,学习它们是很重要的,因为它们在编程和机器学习的许多领域中被使用。
如果你有任何问题或者对编辑有任何建议,请在下面留下你的评论。
原载于 2021 年 12 月 6 日 https://pyshark.comhttps://pyshark.com/everything-about-python-set-data-structure/。
产品人员需要知道的关于变形金刚的一切(第三部分:伯特)
确定变压器供电产品范围所需的一切
这是面向产品人员的变形金刚系列的第 3 部分。点击此处查看第一部分。本文依赖于前几篇文章中介绍的概念和信息。如果你对变形金刚和 GPT 不熟悉,建议从第 1 部分开始。
如果你正在读这篇文章,你正在设计、管理或投资技术产品。你也可能只是一个聪明的工程师或数据科学家,他们发现这些文章对于理解新的研究非常有用。那么,你真的需要了解变压器模型吗?我们的变形金刚模型有什么好的吗?
号码

这家伙明白了
要打造尖端产品,了解变压器模型。谷歌的产品人员非常了解变形金刚,带领他们改进他们核心业务的产品线:搜索。哼唱查找歌曲—由 BERT 提供支持。特色片段——由 BERT 提供。视频中的关键时刻——由 BERT 提供。这些功能您可能已经很熟悉了:

熟悉的基于变形金刚模型的谷歌特性
要理解变压器模型,就要理解伯特和 GPT。每个都有一个独特的架构,为特定的训练任务进行了优化。虽然我现在可以告诉您每个型号最适合哪些应用,但解释设计选择和型号之间的差异将使您能够识别尚未尝试的新应用。
以下部分将介绍 BERT,这需要了解生成性预培训(GPT)。在第 2 部分中,我深入解释了 GPT 模型架构,我建议在从这里继续之前先阅读那篇文章。警告:如果你还没有阅读第 2 部分,下面的部分会让你困惑,这是你的错。在解释了 BERT 架构之后,我将把这个模型放在我的关于 transformer 模型的更大系列中,以便能够完全识别和评估潜在的应用。
伯特解释道
在 OpenAI 推出 GPT 四个月后,谷歌发布了《变形金刚:双向编码器表示》。BERT 利用了预先训练的变压器的能力,同时解决了 GPT 架构带来的一些限制。通过这样做,BERT 极大地扩展了变形金刚可以有效处理的任务集。
我们将从总结开始,只总结伯特创新的 GPT 的那些组成部分:
1.GPT 的设计方法是从变压器(T-ED)中去掉编码器组件,只保留解码器(T-D)。
2.解码器有时被称为生成器,其功能相当于语言模型,这意味着它被优化来预测句子中的下一个单词。
3.在这个模型中,注意力的作用是单向的,这意味着该模型在预测下一个单词时只能查看前面的单词,而不能查看将跟随神秘标记的单词。
GPT 是第一个建立在 transformer 架构上的基于微调的语言模型,这意味着它创建了一个使用下一个单词预测训练的可预训练的 transformer。基于下一个单词预测语言建模产生了一个主要的限制:只有先前的上下文可以用来理解意思。伯特的作者指出:
[单向注意力的局限性]对于句子级任务来说是次优的,并且在将基于微调的方法应用于标记级任务(如问答)时可能是非常有害的,在标记级任务中,从两个方向结合上下文是至关重要的。
本质上,如果需要理解一个句子中的,那么仅仅依靠句子的第一部分将会太受限制。这里有一个例子可以说明这个问题:“今天,我去超市买了一些面包和花生酱。”自动更正有两个潜在的候选者:, 流 。两者在拼写上同样接近于“strome ”,当只考虑到这一点的句子时,两者都是可信的。建立在 GPT 上的自动更正工具将依赖于文档上下文,直到“strome”作出决定。如果批判性语境直接跟着,我们就不能做得更好吗?**
Transformer 模型需要双向关注,以便将来自两个方向的上下文合并到模型决策中。变压器-解码器(T-D)适合语言建模,因此预先训练,只有单向注意。双向注意力的预训练需要利用转换器-编码器(T-E),这是双向注意力在 T-ED 中发生的组件。
因此,BERT 架构基于仅保留 T-ED 的编码器组件。与 GPT 的 T-D 相比,伯特是一个 T-E。训练一个 T-E 不如训练一个 T-D 简单,我们会看到。尽管如此,现在是回顾我们在过去三篇文章中看到的不同变压器架构的好时机。简而言之,三个占主导地位的 NLP 模型是:
- 变形金刚:T 型
- GPT: T-D
- 伯特:T-E
而其他型号的名称有 RoBERTa、DistilBert、T5、DialoGPT 等。所有这些模型都具有上述三种架构之一。这就是你需要了解的最高层次的变压器架构。
训练伯特
请特别注意这里描述的语言建模任务,因为它们直接决定了这些模型的潜在应用。
T-E 花了最长的时间才被发表(几乎比 T-ED 晚了一年),因为它最不容易被训练。这个问题源于给 T-E 提供单语任务的全文。如果 T-E 被输入了完整的输入句子,你能用什么训练任务来挑战它?如果一开始就给它输入了正确的句子,你怎么能让它决定“流”和“存储”中的哪一个构成正确的句子呢?为了解决这个问题,BERT 发布了两个不同的训练任务。
伯特首先被训练成一个蒙面语言模型(MLM)。MLM 需要递给伯特一句话,比如“我坐在椅子上[戴着面具]”,并要求伯特预测这个带面具的单词。下一个单词预测语言建模可以被认为是 MLM 的一个特例,其中句子中的最后一个单词总是被屏蔽的单词。因此,MLM 可以被认为是一种比训练 GPT 的任务更一般化的语言模型。
在 MLM 之后,伯特接受了一项名为“下一句话预测”的任务训练。在这个任务中,向 BERT 传递由指示符分隔的句子对。伯特被训练来预测第二句话是否应该跟在第一句话后面,或者实际上是不相关的。一个例子是这样的:

训练前的下一句预测任务
伯特和你一样,应该预测“低概率”。
这两项任务构成了使 BERT 容易适应新任务的预训练。
BERT 的应用
理解 BERT 训练任务对于确定其应用是至关重要的。它是这样工作的:如果你能表明你希望你的产品为客户执行的任务可以被框定为这些培训任务中的一个,那么你的任务就是一个可行的应用。让我们看看谷歌的特色片段,并以谷歌产品经理(PM)鲁本为例。
鲁本是一名在谷歌搜索工作的项目经理,他对伯特很熟悉。鲁本注意到,许多用户在谷歌搜索栏中输入完整的问题,然后浏览建议的结果来找到他们的答案。他已经确定了一个客户需求:用户有他们需要回答的问题,但他们希望保持他们现有的查询 Google 搜索栏的行为。
Reuben 密切跟踪一个用户的会话,并观察到以下行为:他的用户在谷歌搜索中键入以下问题:“美国有多少数据科学家。”用户然后点击第一个网站,看到下面的句子:“根据 Glassdoor 的数据,2019 年至 2020 年,美国数据科学家职位的增长持平,约为 6500 个。”用户复制这句话,并粘贴到他的谷歌文档。
考虑到他目前的产品,Reuben 注意到 Google 的解决方案检索了可能包含用户问题答案的文档集。然后,用户需要筛选这些文档来找到答案。用户以这样一句话开始:“美国有多少数据科学家。”然后选择句子:“根据 Glassdoor 的数据,2019 年至 2020 年,美国数据科学家职位的增长持平,约为 6500 个。”鲁本意识到这项任务可以转化为下一句预测的伯特训练任务。他可以使用 BERT 基于句子将“跟随”查询句子的概率来对检索到的语料库中的句子进行排序。
实际上,他希望他的 BERT 输出看起来像这样:

用于微调的 BERT 问答任务
将一个领域特定的任务转化为一个 transformer 培训任务本质上是对您的模型进行微调的过程。为了开发有特色的片段,Reuben 希望对 BERT 进行微调,将它的行为从查找最有可能跟随查询的句子转变为查找最有可能回答用户问题的句子。鲁本可以通过在一组更直接符合这种模式的句子对上进一步训练伯特来做到这一点。
现在都在一起
同样的过程可以用来理解变压器模型的一般应用。让我们快速回顾一下我们所涉及的三种模型架构的培训任务及其关注机制:
- T-ED。任务:翻译。
- 任务:下一个单词的预测。
- 任务:1。MLM。2.下一句预测。
在句子中间进行拼写检查:用掩码替换拼错的单词:MLM。将非正式英语转换成正式英语:翻译。建议如何回复短信:下一个单词预测。等等。等等。
确定应用与型号选择相关,但并不相同。正如谷歌研究人员在 T5 型号(T-ED)的发布中指出的:
我们建议将所有 NLP 任务重新构建为统一的文本到文本格式,其中输入和输出始终是文本字符串…我们甚至可以将 T5 应用于回归任务,通过训练它来预测数字的字符串表示而不是数字本身。
正如作者指出的,在某种程度上,所有的任务都可以用文本到文本的格式来组织。也许 T-ED 可以用于所有的任务,而且任何任务都应该适合 T-ED 模型。虽然这在数学上有意义,但在商业上却常常没有意义。你可以把写博士论文框定为一项下一个单词的预测任务,进入 GPT:“这是我关于人工智能的博士论文”,然后等着 GPT 完成剩下的工作。不幸的是,他们最终可能会上交垃圾,让他们的项目失败,然后质疑他们的未来。
那么,如何才能知道一个应用程序对于一个 transformer 模型是否真的可行呢?根据经验,应用程序任务越直接转化为训练任务,模型的性能就越好。如上所述,每个下一句预测任务都可以被公式化为一个翻译任务,但是 T-E 会以更低的成本产生更好的结果。
总之,请记住,这些模型通过利用在来自训练的大量文本语料库中检测到的模式来确定新的基于文本的任务中最有可能的输出。模型性能受限于训练语料库中包含的内容。变形金刚模型不会有所发现或者搞清楚事情。它们自动执行基于文本的任务,这些任务通过包含在数百万现有文档中的模式变得可预测。尽管如此,他们承诺彻底改革我们所知的人工智能。
参考
[1]纳亚克,潘杜。“比以往任何时候都更好地理解搜索。”谷歌,谷歌,2019 年 10 月 25 日,blog . Google/products/search/search-language-understanding-Bert/。
[2] Jacob Devlin、张明蔚、Kenton Lee 和 Kristina Toutanova。Bert:用于语言理解的深度双向转换器的预训练。arXiv 预印本 arXiv:1810.04805,2018。
[3]“用 T5 探索迁移学习:文本到文本的迁移转换器。”谷歌人工智能博客,2020 年 2 月 24 日,AI . Google Blog . com/2020/02/exploring-transfer-learning-with-t5 . html
作者创作的所有图像
关于卷积神经网络的所有知识

克林特·王茂林在 Unsplash 上拍摄的照片
数据科学
理解卷积神经网络的概念及其在构建图像分类模型中的实现
“从计算机视觉的角度来看,毫无疑问,深度卷积神经网络是当今处理感知数据的‘主算法’。”
托马斯·马利西维茨
如今,我们都必须看到和使用各种图像效果和滤镜,以及我们的计算机和智能手机如何检测和识别照片和视频中的人脸。所有这些都可以通过“计算机视觉”来实现,而“计算机视觉”只不过是使用卷积神经网络的机器学习。
计算机视觉类似于人的视觉,它帮助系统识别、分类、检测数据中的复杂特征。它的一些应用可以在自动驾驶汽车、机器人视觉、面部识别中看到。
但是这种计算机视觉与我们人类的视觉并不完全相同,不像我们,计算机看到的图像是像素矩阵的形式。
图像是由像素组成的。并且每个像素值可以取从 0 到 255 的值。
什么是卷积神经网络
卷积神经网络或 CNN 是一种用于处理输入形状为 2D 矩阵形式(如图像)的数据的神经网络。
卷积神经网络的结构是具有若干隐藏层的前馈,主要是卷积和汇集层,随后是激活层。通过这个 CNN 模型,我们可以识别手写字母和人脸(取决于图像的层数和复杂程度)。

(图片由维基共享资源 ) 卷积神经网络模型
在本文中,我们将学习 CNN 的概念,并建立一个图像分类器模型,以便更好地掌握这个主题。
在建立模型之前,我们需要理解和学习卷积神经网络的一些重要概念。
- 众所周知,计算机将图像视为像素矩阵形式的数字。CNN 将图像视为三维对象,其中高度和宽度是前两个维度,颜色编码是第三个维度(例如,3x3x3 RGB 图像)。
现在想象一下,处理一幅 4K 图像(3840 x 2160 像素)的计算量会有多大。
盘旋
- 因此,卷积网络的主要目标是将图像简化为更容易处理的形式,同时保留特征并在预测时保持良好的准确性。
在卷积神经网络中有三个主要的重要单元,即输入图像、特征检测器和特征映射。
- 特征检测器是过滤器的核心(一个数字矩阵,通常是 3x3)。这里的想法是将图像的矩阵表示与核逐元素相乘,以获得特征图。在这个步骤中,图像的尺寸被减小,以便更快和更简单地处理。保留了图像的重要特征(如图像/对象特有的特征,即识别所必需的特征)。但是,在此步骤中会丢失一些功能。

(图片由维基共享 ) 卷积层——特征图
- 例如,如果我们有一个 5x5x1 维的输入图像,而我们应用于图像的卷积核/滤波器是 3x3x1 维的:
**Image matrix:**
1 1 0 1 1
1 0 1 0 1
1 1 1 1 0
0 0 1 1 0
1 1 0 0 0**Kernel matrix:** 1 0 1
0 1 0
1 1 0
那么核矩阵与图像矩阵的每个元素相乘后得到的卷积特征将是:
**Convolved matrix:**
3 5 3
3 2 5
4 4 2
这里,内核移动 9 次,因为步长为 1(即滤波器将在图像矩阵的每个元素后滑动)。
ReLu 激活功能
应用此 ReLu 函数(整流线性单元)的目的是增加模型中的非线性。因为图像/物体具有几个彼此非线性的特征。我们应用这个函数,以便我们的模型不会将图像分类视为线性问题。
汇集层
池层类似于卷积层,它负责减少卷积矩阵的大小。

(图片由 Wikimedia Commons ) 共享图层-要素地图
这是卷积神经网络过程中的一个重要步骤。汇集对于从图像中检测和提取显著特征是至关重要的,而不管不同的位置、角度、不同的光照等。同时保持训练模型的准确性和效率。
此外,随着图像数据的尺寸减小(同时保留主要特征),处理数据所需的计算能力也降低。
有不同类型的池:最大池,最小池,平均池。
- 最大池从内核覆盖的特征映射矩阵部分提取最大值(具体池大小如 2x2)。
- Min pooling 从内核覆盖的特征映射矩阵部分提取最小值(具体池大小如 2x2)。
- 而平均池所有值的平均值是从内核覆盖的特征映射矩阵的部分中选择的(特定池大小如 2x2)。
最大池化是所有池化方法中最有效的(因为它将包含卷积特征图中最主要的特征)。
**Convolved matrix:**
3 5 4 1
2 2 5 6
4 4 2 5
1 3 5 4**Max pooled matrix:** 5 6
4 5**Min pooled matrix:** 2 1
1 2**Average pooled matrix:** 3 4
3 4
以上是汇集的要素地图。
根据输入图像的复杂程度以及必须提取的细节和特征的级别,可以增加或减少这些卷积层和池层的数量。但是请记住你在模型中增加的层数,所需的计算能力也会增加。
有了这些卷积和池层,我们的模型可以理解提取图像的特征。
展平
下一步是展平所获得的池特征图,即将多维池特征图矩阵转换成一维阵列(线性向量或列),以将其馈送到神经网络进行处理和分类。
全连接层—分类
在我们以列向量的形式获得我们的数据之后,我们将通过前馈神经网络传递它,在训练过程中,在每一次迭代中实现反向传播(提高预测的准确性)。
经过几个时期的训练,我们的模型将能够识别和区分图像的突出和低级特征。
从神经网络获得的最终输出值的总和可能不等于 1,但是有必要使这些值介于 0 和 1 之间。这将表示每个类别的概率,并进一步使用 softmax 技术(用于多类别分类的激活函数)对它们进行分类。
利用 MNIST 数据集实现 CNN
在本文中,我们将使用 MNIST 数据集,即一个由 70,000 张 (60,000 张训练图像和 10,000 张测试图像)0 到 9 之间的手写单数字的小正方形 28×28 像素灰度图像组成的数据集。
这里我们模型的目标是将一组给定的手写数字图像分类为 1 到 10 (代表从 0 到 9 的整数)。
我们将在本文中使用 Keras 和 matplotlib 库。
以下代码将使用 Keras API 加载 MNIST 数据集的前九幅图像,并使用 matplotlib 库绘制这些图像。
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import Dense
from keras.layers import Flatten
from keras.optimizers import SGD
from keras.datasets import mnist
from matplotlib import pyplot
# load dataset
(trainX, trainy), (testX, testy) = mnist.load_data()# plot first 9 images
for i in range(9):
pyplot.subplot(330 + 1 + i)
pyplot.imshow(trainX[i], cmap=pyplot.get_cmap('gray'))**pyplot.show()**
训练和测试图像(已经被模型很好地定义了)被分别加载,如上面的代码所示。

(图片由作者提供)MNIST 数据集中的第个九位手写数字图片
现在,我们将加载完整的数据集,并在将数据输入神经网络之前对其进行预处理。
(trainX, trainY), (testX, testY) = **mnist.load_data()**trainX = trainX.**reshape**((**trainX.shape**[0], 28, 28, 1))
testX = testX.**reshape**((**testX.shape**[0], 28, 28, 1))trainY = **to_categorical(trainY)**
testY = **to_categorical(testY)**
在上面的代码中,我们对数据进行了整形,使其具有单一的颜色通道(因为图像具有相同的 28x28 像素和灰度形式)。
此外,我们有一个热编码的数据集值(使用to_categorical,一个 Keras 函数),因为我们知道有 10 个不同的类都由唯一的整数表示。这里,每个整数样本都被转换为一个十元素的二进制向量,其中 1 表示类值的索引,0 表示所有其他类的值。
完成此操作后,我们将不得不对数据集进行归一化,因为我们知道图像的像素值在 0 和 255(黑白)之间变化。为此,我们将该数据缩放至范围【0,1】。
trainX = trainX.**astype('float32')**
testX = testX.**astype('float32')**trainX = **trainX / 255.0**
testX = **testX / 255.0**
在上面的代码中,我们首先将 pixel 的整数值转换为浮点数。之后,我们将这些值除以最大数(即 255),这样所有的值都将在[0,1]的范围内缩放。
现在我们将开始构建我们的神经网络。
model = Sequential()
model.add(**Conv2D(32, (3, 3), activation='relu',
kernel_initializer='he_uniform', input_shape=(28, 28, 1))**)
model.add(**MaxPooling2D((2, 2))**)
model.add(**Flatten**())
model.add(Dense(100, activation='relu',
kernel_initializer='he_uniform'))
model.add(Dense(10, **activation='softmax'**)) opt = SGD(lr=0.01, momentum=0.9)
model.compile(optimizer=opt, loss='categorical_crossentropy',metrics=['accuracy'])
在上面的代码中,我们使用了用于逐层创建模型的Keras API sequentaial()。之后,我们为我们的模型添加了一个卷积层,内核大小为 3x3,有 32 个过滤器。接下来是内核大小(2x2)的单个MaxPooling()层。然后输出特征图被展平。
由于我们知道有 10 个类,所以在输出层中需要 10 个节点用于每个类的预测(多类分类)以及 softmax 激活函数。在特征提取层和输出层之间,我们添加了一个具有 100 个节点的密集层,用于模型的特征分析和解释。
模型中使用了**Stochastic gradient descent**(学习率为 0.01,动量为 0.9) 优化器和**categorical_crossentropy** 损失函数(适用于多类分类模型)。
最后,在编译我们的模型之后,需要在训练数据集上对它进行训练,在测试数据集上对它进行测试,并进一步评估它的结果(即准确性和损失)。
batch_size = 128
num_epoch = 10
#model training
model_log = **model.fit(trainX, trainY,
batch_size=batch_size,
epochs=num_epoch,
verbose=1,
validation_data=(testX, testY))**
在上面的代码中,我们使用了 10 个epochs和 128 个batch_size(批量大小是一次迭代中训练的样本数)。下面是模型的训练输出:

(图片由作者)CNN 模型的训练输出
上述结果可以根据性能进行评估:
**score = model.evaluate(testX, testY, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])**

(图片由作者)CNN 模型的训练输出
测试准确度> 98%时,我们可以说我们的模型在准确预测方面训练有素。您也可以使用matplotlib库来可视化这些结果!
结论
我希望通过这篇文章,你能够理解和掌握卷积神经网络的概念。
为了更好地理解这些概念,我建议您尝试编写这些代码。继续探索,我相信你会发现新的特性。
如果你有任何问题或意见,请发表在评论区。
查看完整的数据可视化指南:
最初发表于:www.patataeater.blogspot.com
你想知道的关于 Python 中决策树的一切
从基本原则到生产环境中的部署,包括工作示例和易于理解的解释

沙哈达特·拉赫曼在 Unsplash 上拍摄的照片
介绍
我在各种媒体上看到了许多关于 Python 中决策树机器学习算法的文章,但它们总是让我想要更多。
它们要么看起来在过程中中途跳跃,要么当我将代码应用到我的数据时它不工作,或者它们忽略了过程中的重要部分。
由于我找不到完全符合要求的东西,所以我想我自己也要试一试,这就产生了写这篇文章的想法。
背景
如果有人需要关于如何使用决策树来预测数据中的分类的介绍或复习,那么 R2D3 的这篇优秀文章提供了关于它如何工作的一步一步的可视化,绝对精彩!
【http://www.r2d3.us/visual-intro-to-machine-learning-part-1/
如果你想加深你的理解,包括一些数学知识和熵是如何工作的,那么福斯特·普洛沃斯和汤姆·福西特的这本书是一个主要书籍,它以一种简单易懂的方式解释了更复杂的主题
https://www . Amazon . co . uk/Data-Science-Business-Data-analytic-thinking/DP/1449361323
除了这些资源,还有大量关于 https://towardsdatascience.com/和 https://medium.com/的进一步阅读。
入门指南
有相当多的外部库涉及到决策树模型的开发和可视化,所以让我们从导入它们并设置几个配置参数开始…
导入和塑造数据
我选择处理的数据集是 UCI 机器学习库中的红酒和白酒数据集。这是一个非常好的数据集,因为我们可以查看代表葡萄酒的数据,然后训练决策树模型来预测葡萄酒是白还是红。
数据集可以在这里找到…https://archive.ics.uci.edu/ml/datasets/wine+quality
…这是符合 https://archive.ics.uci.edu/ml/citation_policy.htmlUCO 引文政策的参考文献…
Dua d .和 Graff c .(2019 年)。UCI 机器学习资源库【http://archive.ics.uci.edu/ml】。加州欧文:加州大学信息与计算机科学学院
首先,我们将读取来自两个不同的逗号分隔文件中的红葡萄酒和白葡萄酒数据,设置一个标签或目标变量 1 =红,0 =白,然后从这两个源生成一个单独的DataFrame,并查看数据...

作者图片
接下来,我们将看看数据集中白葡萄酒和红葡萄酒的相对数量…
0 0.753886
1 0.246114
Name: label, dtype: float64
所以我们的葡萄酒含 75.4%的白葡萄酒和 24.6%的红葡萄酒。根据对超市货架的一瞥,这听起来似乎是正确的,但为了我们的决策树机器学习算法的目的,它需要重新塑造,该算法将预测葡萄酒的颜色(红色或白色)。
对于这些类型的算法来说,目标变量的命题需要被平衡,我们的是 75.4% / 24.6%。
一种常见的技术是“下采样”,其中较小的部分被全部使用,而来自较大数据集的相同数量的数据点被随机采样。
在下面的代码中,我们简单地抽取所有的红葡萄酒,然后随机抽取白葡萄酒来匹配红葡萄酒的数量…

作者图片
让我们再检查一下比例,确保我们达到了平衡…
1 0.5
0 0.5
Name: label, dtype: float64
将数据分成训练和测试数据集
下一个代码块复制了所有的“特征”(酸度、氯化物等。)转换为 X,将“目标”变量(标签为红/白)转换为 y。
然后,它将 X 和 y 分成训练和测试数据。训练数据将用于训练模型,测试数据将用于评估模型。
这种分离的原因是将测试数据排除在训练之外,以便模型避免“过拟合陷阱”,即模型对训练数据表现良好,但对从未见过的新数据表现不佳。
开始了…
调整模型
在我们继续创建决策树模型之前,我们需要根据与我们的红葡萄酒和白葡萄酒数据最匹配的值来调整“超参数”,也就是说,我们需要为模型选择参数,使其在进行预测时具有最高的准确性。
如果我们手动这样做,我们可以不断调整设置,重新运行模型和分数,但这非常耗时,也不太科学。
相反,我们可以使用方便的sklearn.model_selection.GridSearchCV函数,该函数将尝试我们传递给它的所有超参数组合,并让我们知道哪些是最有效的。
我将它封装在dtree_grid_search函数中,该函数接受一个包含所有特性的X参数、一个包含所有标签的y参数和一个表示应该使用多少 X 和 y 数据分割来测试超参数的nfolds。
它在这里..

作者图片
创建决策树模型
我们可以看到网格搜索确定为“最佳”或“最准确”的参数,包括criterion = 'entrophy'、max_features='auto'等。
请注意,我选择了max_depth=3,而不是优化值 9,以减少树中的级别数,从而更容易为本文的目的可视化。
我有意识地选择牺牲准确性(在 9 个级别上,准确性约为 93%,而不是 95%)来换取简单性和可读性,这是一件完全有效的事情,事实证明,模型设计师在现实世界中构建模型时一直在进行这些权衡。
下一个代码块使用优化的超参数构建我们的决策树模型,使用训练数据训练模型,然后使用训练数据对我们模型的有效性进行评分,结论是我们的决策树模型可以基于葡萄酒特征值预测目标葡萄酒颜色,准确率为 92.8% …
0.9275
特征重要性
花两行代码来快速了解这些特性的相对重要性是值得的..

作者图片
原来total sulfur dioxide是最重要的特征,重要性为 0.6。volatile acidity和chlorides分别以 0.19 和 0.17 紧随其后。在此之下,其余特征的重要性得分较低。
这向我们展示了哪些特征影响了模型,并且可能需要去掉chlorides下面的特征并重新运行模型。如果精确度的损失可以忽略不计,但是复杂度的降低是显著的,那么移除它们可能是一个很好的折衷。
此外,即使我们拒绝我们的决策树,并探索不同的机器学习算法,如逻辑回归,关于哪些特征有影响的信息仍然会有用。
可视化决策树
将决策树转化为直观的可视化表示是帮助我们理解它如何工作以及它在做什么的关键一步。
有很多可视化决策树的库,这里我展示了一些我最喜欢的。
我从dtreeviz开始,它可以通过运行下面的命令行来安装
pip install dtreeviz
…您可以在此查看完整文档—https://github.com/parrt/dtreeviz
我已经在网上找到了许多绘制 Seaborn 包附带的企鹅数据集的例子,但我很难找到任何有助于我在自己的数据帧上使用它的例子。
玩了一会儿之后,我想出了下面的代码,它可以在任何 X 特征DataFrame和 y 目标Series上工作。
在我看来,dtreeviz不仅提供了模型的视觉效果,还提供了特征的分布,以及算法如何做出分叉树和划分数据的决定

作者图片
这个dtreeviz真的很美是不是!从顶部读取决策树机器学习算法选择基于total sulfur dioxide less than 74.50进行第一次数据拆分。
这导致了我们在第二行中看到的数据分裂,我们可以很容易地看到和理解剩余的分裂,直到算法在深度为 3 时结束,其中 3 组被分类为白葡萄酒,5 组被分类为白葡萄酒。
然而,尽管dtreeviz在视觉上令人惊叹,但一些已经存在了一段时间的树可视化库中却缺少一些信息。
例如graphviz虽然在视觉上不那么吸引人,但确实包含了关于每个节点熵(即杂质)的附加信息,以及每个节点中出现的每个目标变量(红葡萄酒和白葡萄酒)的数量。
因此,我的偏好是使用两个库和两个版本的树可视化,我发现这有助于我完全理解模型在做什么。
请注意,我在下面添加了几行额外的代码,将树图像写出到一个.png文件中,然后再读回来。在这里,完全可以显示树,而不用用更少的代码行保存它,除非保存它,否则不可能控制比例,并且默认比例太大,不容易查看,因此我选择将其写入文件...

作者图片
使用模型进行预测
当我第一次学习决策树时,我想知道如何使用该模型对一组新数据进行预测,我努力在网上找到答案。
如果我只有一瓶我想预测颜色的葡萄酒,该怎么办?我会怎么做?
事实证明这相当简单,下面的函数将获取一组葡萄酒数据,进行预测,然后返回结果…
下面的两行代码只是从完整的数据集中挑选顶部和底部的数据行,这样我们就可以运行一个预测,看看它是否与实际标签相匹配…

作者图片

作者图片
我们走吧。我们使用第一行和最后一行的值调用单个预测函数,我们可以看到,在每种情况下,预测都与实际标签匹配(1=“红色”,0=“白色”),这就是我们使用模型进行单个预测所需要知道的全部信息。
('red', 'white')
我们可能想做的另一件事是对整个文件或整个DataFrame进行预测,结果证明这也非常容易。
下面的代码将 X_test 和 y_test 数据放在一起,显示带有实际标签的数据集,然后调用模型对每一行进行预测。
然后它显示数据,我们可以看到实际标签和预测标签并排。

作者图片
我们还可以使用两种略有不同的方法来验证我们的模型的准确性。
第一个是将那些label与predicted_label匹配的行表示为行数的百分比,这给出了 92.3%的准确度。
然后运行model.score,这正好显示了这个库函数做了什么以及它是如何工作的。它只是计算实际值和预测值匹配的行的百分比...
(0.9275, 0.9275)
在生产环境中部署和实现决策树
一旦所有数据科学工作结束,就不可避免地需要在生产环境中实施,而该环境可能不是 Python,并且可能无法访问我们在本例中使用的所有专业建模库。
树形可视化让我们了解了算法正在做什么,但幸运的是,最简单的可视化技术——文本表示——以一种可以直接翻译成一系列if else语句的格式显示树形图,这些语句可以很容易地用任何编程语言进行编码。
在我的环境中,生产系统是用 C#或 VB 脚本编写的,因此能够执行这最后一步以“操作化”所有数据科学工作至关重要。
sklearn.tree.export_text方法可以用来给我们的模型一个非常有用的表示...

作者图片
这个输出可以很容易地转换成一个函数,这个函数可以很容易地用 C#、VB 脚本、Java 或任何其他编程语言编写…
最后,我们在下面测试了操作化函数,以验证标签的预测方式与使用上面的单行或整个数据帧方法完全相同…
('red', 'white')
结论
我们从一些在线资源开始,这些资源解释了决策树在提取和形成公共数据集之前如何从基本原则开始工作。
然后,我们将数据分为训练和测试两部分,创建了一个优化的决策树模型,将我们的数据拟合到该模型中,并评估该模型的准确性。
然后,我们使用两个不同的强大库来可视化我们的决策树,并对单个案例和整个数据文件进行预测。
最后,我们展示了如何将决策树模型转换为 if-then-else 算法,该算法可以轻松部署到生产环境中,即使该环境使用不同的编程语言(例如 C#)。NET、VBScript、Java 等。
完整的源代码可以在这里找到:
https://github.com/grahamharrison68/Public-Github/tree/master/Decision Trees
关于 Python 中的字典,你需要知道的一切
Python — 30 天挑战
在本文中,我们将从基础开始,深入探究字典在 Python 中能做的一切
Python 中的字典或其他编程语言中的哈希表是存储和检索值的最有效的数据结构之一。它们根据一个键存储每个值,形成一个键-值对。在本文中,我们将学习开始使用 Python 时应该知道的关于字典的一切。

图片来自像素
以下是我们今天将要讨论的关于字典的话题列表:
- 字典介绍
- 如何定义字典
- 如何使用字典
- 如何在字典中赋值
- 词典研究方法
- 对字典键/值的限制
- 结论
这是我 30 天文章写作挑战的一部分。请随意查看我的新人帖子上的文章:
https://nouman10.medium.com/lets-publish-30-articles-in-30-days-a0b8111855bb
字典介绍:
Dictionary 是 Python 中的一种复合数据结构,基本上由一系列键值对组成。键值对中的每个键都将该键映射到其关联值。
如何定义字典:
在 Python 中,字典通常由一列用大括号({})括起来的逗号分隔的键值对来定义。每个键都用冒号(:)与其关联的值隔开。
以下代码片段构建了一个足球运动员及其相关俱乐部的字典:
football_players_club = {
"Cristiano Ronaldo": "Juventus",
"Bruno Fernandes": "Manchester United",
"Lionel Messi": "Barcelona",
"Sadio Mane": "Liverpool",
"Harry Kane": "Tottenham Hotspur"
}
还可以通过使用字典的构造函数dict()来定义字典。它可以接受一组键值对。因此,包含键-值对元组的列表是通过构造函数构造字典的有效方式,包含键-值对列表的元组也是如此。

作者图片
尽管可以像显示列表一样显示词典,但不能使用数字索引来访问它们。字典中没有顺序,你只能使用它的键来访问一个值。使用索引会导致一个KeyError。
如何访问字典值:
为了访问字典值,我们使用与值相关联的键。例如,要访问上面字典中的“巴塞罗那”和“利物浦”,我们使用:
print(football_players_club["Lionel Messi"])
print(football_players_club["Sadio Mane"])

作者图片
使用字典中不存在的键会导致一个KeyError:
football_players_club['Muhammad Salah']
football_players_club[1]
这两种情况都会导致以下错误:


作者提供的图片
请注意,尽管我们在第二个示例中使用了数字索引,但两者的误差是相同的。这是因为 Python 认为您在方括号内传递的任何内容都是字典的键。事实上,Python 实际上允许字典有如下的数字键:
numerical_keys = {
1: 'a',
0: 'b',
4: 'c'
}numerical_keys[0]

作者图片
当我们使用“0”作为键时,我们不是说获取索引为 0 的值,而是将 0 作为键的值,这就是预期的“b”。
如何在字典中赋值:
我们可以为一个键指定新的值,甚至更新字典中现有键的值,只需简单地用这个键访问字典并将其设置为所需的值,如下所示:
football_players_club['Luis Suarez'] = 'Athletico Madrid'
football_players_club['Harry Kane'] = 'Manchester City'
词典方法:
词典的常用方法:
在这里,我们将研究 python 中应用于词典的一些常用方法,最后,我们将看到所有这些方法的演示:
注意:*dict* 、 *dict_1* 、 *dict_2* 都代表某种任意的字典。
len:
顾名思义,它返回字典中键值对的长度。
语法:len(*dict)*
在:
它允许我们检查一个键是否存在于字典中
语法:key in *dict*
我们可以利用这一点来避免访问字典中不存在的键。例如,如果关键字不在字典中,使用dict[key]将引发一个KeyError,但是使用下面一行代码不会,因为 python 短路评估将阻止它评估and语句的第二部分,以防key in *dict* 返回 false。
key in *dict* and *dict*[key]
获取:
它获取与给定键相关联的值。它还接受一个可选的默认参数,如果字典中没有这个键,就会返回这个参数。但是,如果键不存在,并且也没有传递默认值,则该函数返回 None。
语法:*dict*.get(key)或*dict*.get(key, default_value)
按键:
它返回字典的键
语法:*dict*.keys()
价值观:
它返回字典的值
语法:*dict*.values()
项目:
它将字典中的条目作为一个键值元组序列返回。
语法:*dict*.items()
注意:在字典中使用 for 循环会在每次迭代时返回键,即for key in *dict* 将遍历字典中的键列表
popitem:
它从字典中删除最后一个键-值对并返回它。
语法:*dict*.popitem()
流行音乐:
它移除与给定键关联的键-值对。它还接受一个可选的默认参数,如果字典中没有这个键,就会返回这个参数。但是,如果键不存在并且默认值也没有被传递,那么该函数会引发一个KeyError.
语法:*dict*.pop(key)或*dict*.pop(key, default_value)
清除:
顾名思义,它清除字典(清空字典)
语法:*dict*.clear()
更新:
它将一个字典与另一个字典或一个键值对的 iterable 合并,就像我们在定义字典一节中看到的那样。两个字典中的所有键值对都被添加到第一个字典中。如果一个关键字已经存在于第一个字典中,则它的值由第二个字典中与该关键字相关联的值来更新。如果一个关键字不在第一个字典中
语法:*dict_1*.update(*dict_2*)(注意:只有*dict_1*被更新)
演示:

作者图片
对字典键/值的限制:
字典键:
正如我们之前看到的,您可以将字符串和整数设置为字典值中的键。您甚至可以像这样将 Python 中的内置对象设置为键:
dictionary = { int: 8, float: 9 }
dictionary[str] = 10
print(dictionary)

作者图片
然而,也有一些规则。给定的键在字典中只能出现一次,也就是说,每次试图为字典中已经存储的键设置值时,都会覆盖以前的值。最新的是存储的值。
此外,您只能使用不可变类型作为键。这意味着元组是一个非常好的键,而列表则不是。
dictionary = {(1, 1): 'a', (1, 2): 'b', (2, 1): 'c', (2, 2): 'd'}
print(dictionary)

这运行得很好,但是下面给出了一个错误:
dictionary = {[1, 1]: 'a'}
print(dictionary)

作者图片
字典值:
另一方面,字典值没有任何限制。任何值都是合适的字典值,不管它是可变的还是不可变的,是内置的还是用户定义的。字典值也可以重复,次数不限。
结论:
总结一下:
- 我们从 Python 中字典的基本介绍开始,包括定义、访问和赋值
- 然后我们研究了字典的一些内置方法
- 最后,我们研究了字典键和值的限制。
如果您觉得以上内容对您有用,请分享并随时支持我-->
关于 Python 中的函数,你需要知道的一切
Python — 30 天挑战
在本文中,我们将从基础开始,深入探究使用 Python 中的函数可以做的一切
函数是任何编程语言中模块的基本构建块,是理想情况下执行单一任务的单一自包含代码块。本文为初学者提供了一种从 Python 中的函数开始的友好方式。我们将在这篇文章中讨论很多关于函数的内容,所以让我们开始吧!!

图片来自像素
以下是我们将讨论的函数主题列表:
- 在 Python 中定义您的第一个函数
- Python 中的参数
- 位置参数
- 关键字参数
- 默认参数
- 可变长度位置参数
- 可变长度关键字参数
- 结论
这是我 30 天文章写作挑战的一部分。请随意查看我的新人帖子上的文章:
https://nouman10.medium.com/lets-publish-30-articles-in-30-days-a0b8111855bb
在 Python 中定义您的第一个函数
让我们从定义第一个函数开始,这个函数完成将“Hello,world”打印到屏幕上的简单任务。
def print_hello_world():
print("Hello, world")print_hello_world()
让我们来分解一下:
- 我们首先告诉解释器我们正在使用关键字
def定义一个函数 - 然后是函数的名字,在我们的例子中是
print_hello_world。你可以随心所欲地定义它 - 然后是两个圆括号。目前,它们是空的,但是它们包含了您想要传递给函数的任何参数(下一节将详细介绍)
- 然后是一个缩进的代码块,包含函数中要执行的代码。在我们的例子中,它只是打印“Hello,world”。
- 到目前为止,我们已经定义了我们的功能。它不会打印任何东西,除非我们在最后调用它。
- 类似地,我们可以调用该函数 10 次,它将打印 10 次“Hello,world ”,但定义将只编写一次。这就是功能在可用性方面的力量
注意,一个函数通常也有一个 return 语句。这是函数返回的值,可以按照您的意愿存储。
函数中的参数
参数是可以传递给函数的值,这些值将决定函数的执行方式。我们可以通过不同的方式传递参数:
位置参数:
传递参数最常见的方式是调用一个函数,并在与函数定义相同的位置传递参数。让我们以除法函数为例:
def integer_division(num_1, num_2):
return num_1 // num_2
如果我们想将数字 10 除以数字 2,我们需要调用如下函数:
integer_division(10, 2)
这会返回 5。但是如果我们像这样改变位置:
integer_division(2, 10)
那么函数将返回 0,所以在传递位置参数时位置是很重要的。注意,我们需要在这里传递两个必需的参数,否则我们将得到一个TypeError,表明我们传递了不正确数量的参数
关键词参数:
我们也可以在调用函数时以key=value格式传递参数。这意味着我们不需要记住顺序。考虑与上面相同的函数:
def integer_division(num_1, num_2):
return num_1 // num_2
我们可以像这样使用关键字参数来调用它:
integer_division(num_1=10, num_2=2)
或者像这样:
integer_division(num_2=2, num_1=10)
这两个都将返回 5。我们也可以传递位置参数和关键字参数的组合。但条件是关键字参数将出现在所有位置参数之后,如下所示:
integer_division(10, num_2=2)
这是必需的,以便 Python 解释器能够理解序列并为每个参数分配正确的值
默认参数:
Python 也允许我们在定义函数时定义默认参数。我们可以在函数定义期间这样定义它们:
def integer_division(num_1=12, num_2=2):
return num_1 // num_2
现在我们可以如下调用该函数:
integer_division()
这将返回 6,但我们也可以传递自定义值(所有都是有效的选择):
integer_division(10) -> returns 5
integer_division(12, 3) -> returns 4
integer_division(num_2=4) -> returns 3
integer_division(10, num_2=5) -> returns 5
注意:当你将一个可变对象(比如一个列表)作为默认参数传递给一个函数时,默认参数有一个常见的问题。考虑以下函数:
def append_item(a_list=[]):
a_list.append('item')
print(a_list)
您可以使用列表调用如下函数:
append_item(['item_1'])

作者图片
append_item(['item_2'])

作者图片
这是意料之中的。每当一个新的列表被创建,我们添加一个项目到它。但是让我们看看当我们使用默认参数时会发生什么:
append_item()
append_item()
append_item()

作者图片
震惊吧?我也是。!。它应该每次只打印一个项目的列表。
这背后的原因是,默认参数在函数定义时创建一次,并存储在内存中。这就是为什么使用可变对象会修改内存中的对象,并可能导致一些意想不到的结果。实现此功能的更好方法如下:
def append_item(a_list=None):
if a_list is None:
a_list = []
a_list.append('item')
print(a_list)
这将确保每次使用默认参数时都创建一个新的空列表。
可变长度位置参数(*args):
你可能以前见过这个(*args),但不知道它是什么意思。当我们不知道要传递的参数的确切数量,但需要以类似的方式处理它们时,就使用这种方法。
例如,你想写一个函数来增加一些数字。你事先不知道你要加多少个数。在这种情况下这很方便:
def add(*args):
total = 0
for num in args:
total += num
return total
当您使用这种语法时,args基本上是所有位置参数值的元组。让我们调用这个函数:
add(1, 2, 3, 4, 5)
这将像预期的那样返回 15。我们甚至可以打印元组并自己检查。另外,args这个名字不是必须的,你可以随意定义,但是要加一个星号(*)。
def add(*list_of_numbers):
total = 0
print(list_of_numbers)
for num in list_of_numbers:
total += num return total

作者图片
可变长度关键字参数(**kwargs):
我们还可以传递任意数量的关键字参数,函数将接收这些参数作为一个字典,从中我们可以提取我们想要的值:
def print_kwargs(**kwargs):
print(kwargs)
我们可以用多种方式调用这个函数:
print_kwargs(num_1=10, num_2=5, num_3=1)

作者图片
print_kwargs(name='Nouman', age='23', profession='Software Engineer')

作者图片
结论:
总结一下:
- 我们从用 Python 编写第一个函数开始
- 然后我们研究了位置和关键字参数
- 最后,我们研究了缺省参数和可变长度参数
如果您觉得以上内容对您有用,请分享并随时支持我-->
关于梯度下降你需要知道的一切

第 1 部分—详细解释算法
梯度下降是一种使用一阶迭代来解决优化问题的算法。由于梯度下降被设计成寻找微分函数的局部最小值,所以梯度下降被广泛用于机器学习模型中,以寻找使模型的成本函数最小化的最佳参数。本文将详细讨论梯度下降算法,下一篇文章将讨论它在线性回归模型中的应用。
算法
如上所述,梯度下降解决最小化问题,假设我们有一个优化问题:

我们想找出使函数 J(θ_0,θ_1)最小的正确参数θ_0 和θ_1。如果函数 J(。)是可微的,我们既可以基于微积分求解一阶条件,也可以使用梯度下降找到正确的θs,如果使用微积分,我们知道需要求解:

一阶条件
直觉是,函数的“斜率”在最优点将是水平的。如果函数是凸的,它将是最小值。下图说明了只有一个参数的函数的一阶条件。

求解最小值
尽管这种分析方法执行最小化而无需迭代,但它通常不用于机器学习模型。当参数数目太大时,效率不够高,并且如果函数太复杂,有时我们不能容易地求解一阶条件。因此,对于具有众多特征的模型,我们通常使用梯度下降算法来最小化成本函数。为了找到使 J(θ_0,θ_1)最小的θ_0 和θ_1,梯度下降操作如下:
- 1,对θ_0 和θ_1 进行初步猜测
- 2、检查最初猜测时的偏导数。偏导数在梯度下降中称为梯度:

偏导数给出了方向
- 3、基于偏导数和学习率𝛂:同时更新θ_0 和θ_1

右边的θ是旧值,而左边的θ是更新值
- 4、重复第 2 步和第 3 步,直到 J(θ_0,θ_1)不再显著下降。该算法输出使 J(θ_0,θ_1)最小的估计θ_0 和θ_1。
在下面的部分,我将解释为什么这个算法找到最小点,进行初步猜测,并选择正确的学习率。
为什么有效?
梯度下降的关键直觉是,从每一步到最小点,它采取最快的路线快速收敛。这是通过在每一步取偏导数来找到局部最小值的方向。下图解释了只有一个参数的函数的直觉。左侧面板显示了初始猜测位于最佳点左侧的时间。因为函数 J(。)是凸的,初始猜测的导数将是负的(初始点的斜率将是负的)。因此,我们需要增加θ,以达到局部最小值θ。再看更新函数,由于 dJ(θ)/dθ为负,θ-𝛂dJ(θ)/dθ会大于θ,从而向局部极小值θ移动。右图显示了当初始猜测位于局部最小值θ的右侧时的相反情况。由于 dJ(θ)/dθ为正,θ-𝛂dJ(θ)/dθ将小于θ,从而向θ移动。

梯度下降如何向θ*方向更新
你可能会注意到,当提到函数的最小点时,我使用局部最小值而不是全局最小值。这是因为梯度下降只能找到局部最小值。如果一个函数有多个局部极小值和一个全局极小值,不保证梯度下降会找到全局极小值。此外,它找到哪个局部最小值将取决于初始猜测的位置。以下图为例。b 是全局最小值,而 A 和 C 是 J(θ)的局部最小值。如果初始猜测位于区域 A 或区域 C,梯度下降将在局部最小值 A 或 C 处取最优θ。只有当初始猜测位于区域 B 时,算法才会找到使 J(θ)达到全局最小值 B 的最优θ。

局部最小值与全局最小值
如何进行初步猜测?
如上所述,进行初步猜测对于找到正确的最小值非常重要。从上图可以看出,C 点是一个局部极小值,它可能不能很好的代表 J(θ)的极小点。因此,如您所见,在大多数情况下,0 可能不是一个好的初始猜测。实际上,最初的猜测通常是通过应用随机函数来完成的。它可以是基于所有θ可能值范围内的均匀分布的随机函数。如果可能的话,您可以用不同的初始猜测运行算法,并比较 J(θ),以查看它们彼此之间是否有显著差异。
什么是好的学习率?
学习率𝛂在整个过程中是一个常数,它定义了算法更新的速度。使用正确的𝛂是有效运行梯度下降的关键。如下图所示,如果𝛂太小,算法就会太慢而无法收敛,因为它需要很多步骤。如果𝛂太大,它可能“跳过”局部最小值,有时甚至可能不收敛。实际上,对于大多数情况,0.01 是一个很好的学习率。

左:𝛂太小;右:𝛂太大
批量梯度下降和随机梯度下降
我们拥有的数据点越多,在训练机器学习模型时,我们给算法带来的计算负担就越大。在梯度下降的更新步骤中,我们可以在计算偏导数时取全部训练样本或部分训练样本。前者称为批量梯度下降,后者称为随机梯度下降。批量梯度下降更准确地找到正确的局部最小值。但是,如果训练样本很大,就太慢了。随机梯度下降仅单独考虑每个训练观察。它不是计算成本函数的精确偏导数,而是使用每个观察值来估计偏导数,然后在该方向上迈出一步。虽然每个观察将提供真实偏导数的差的估计,但是给定足够的随机性,参数将以快得多的速度收敛到良好的全局估计。此外,由于它只需要考虑一个单一的观察,随机梯度下降可以处理数据集太大,不适合在内存中。
本文详细解释了梯度下降算法。具体来说,它比较梯度下降和一阶条件来解决优化问题;梯度下降怎么操作,为什么有效;如何做好初始猜测,找到好的学习率;批量下降和随机梯度下降有什么区别?在下一篇文章中,我将讨论梯度下降在线性回归模型中的应用。
感谢您阅读这篇文章。这是我所有博客帖子的列表。如果你感兴趣的话,可以去看看!
https://zzhu17.medium.com/my-blog-posts-gallery-ac6e01fe5cc3 https://zzhu17.medium.com/membership
关于 Python 中的索引,您需要知道的一切
Python 中不同数据类型和结构的索引概述。

(src =https://pixabay.com/images/id-1690423/
介绍
在通用编程中,使用数据结构是很常见的。数据结构是由更小的数据类型组成的类型。数据结构的一个例子是列表或字典。数据结构允许我们方便地将几个组件作为同一个一致变量的成员进行组织和工作。正如您所想象的,这使得数据结构成为数据科学中非常重要的组成部分。假设数据结构是由更小的组件组成的,那么肯定有一种方法可以基于某些特性来访问各个组件。为此,我们使用索引。
索引很重要,因为它允许我们毫不费力地调用数据结构的一部分,以便单独处理结构内部的组件。当然,对于数据科学来说,掌握这一点是非常重要的,因为数据科学家可能会经常使用数据结构。
索引类型
在我们开始使用索引之前,让我们看看哪些类型实际上可以被索引。在 Python 编程语言中,可以使用给定类中的 getitem()方法对类型进行索引。这意味着我们可以将索引方法应用于任何类型,只需简单地添加这个方法,并返回准确的结果。为了尝试这一点,我们将首先创建一个类。考虑下面的例子:
class Winners: def __init__(self, first, second, third): self.first, self.second = first, second self.third = third
现在我们可以将 getitem()方法添加到这个新类中。这将增加使用简单索引从类中轻松获取我们的位置的能力。
def __getitem__(self, x):
对于这个例子,我认为最好的攻击方法是在初始化这个类的构造函数时创建一个字典。使用这种方法,我们将能够简单地调用基于数字的字典索引,以便接收这场比赛的位置。
class Winners: def __init__(self, first, second, third): self.first, self.second = first, second self.third = third self.index = dict({1 : self.first, 2 : self.second, 3 : self.third})
最后,我们将结束 getitem()方法,只需用提供的数字调用字典键:
class Winners: def __init__(self, first, second, third): self.first, self.second = first, second self.third = third self.index = dict({1 : self.first, 2 : self.second, 3 : self.third}) def __getitem__(self, x): return(self.index[x])
虽然这当然很好,但是让我们记住,我们只是这里的日志赢家,所以任何超过 4 点的人都不包含在这个类中。记住,如果我们在这个类上调用索引 4,我们将得到一个 KeyError 作为回报。每当我们创建软件时,尤其是在我们的软件用户可能永远不会看到的类中,我们会希望抛出一些错误,使错误比这更明显一些。考虑到这一点,我们将在这个方法中添加一个 try 和 catch,以便打印出更详细的错误。最终结果看起来有点像这样:
class Winners: def __init__(self, first, second, third): self.first, self.second = first, second self.third = third self.index = dict({1 : self.first, 2 : self.second, 3 : self.third}) def __getitem__(self, x): try: return(self.index[x]) except KeyError: print("""KeyError!\nOnly keys 1-3 are stored in this class!""")
现在让我们试着索引这种类型。首先,我们当然需要初始化这个对象的一个新实例,然后我们将索引它。这是通过[]语法完成的:
race_winners = Winners("Nancy", "Bobby", "Reagan")
首先让我们试着用 4 来表示它,看看我们得到什么样的回报!
race_winners[4]KeyError!
Only keys 1-3 are stored in this class!
现在我们将在该类的索引中打印 1:3:
print(race_winners[1])print(race_winners[2])print(race_winners[3])Nancy
Bobby
Reagan
Python 中的可索引类型
Python 编程语言提供了几种可以立即索引的数据类型和数据结构。本文中我们首先要看是字典数据结构。
dct = dict({"A" : [5, 10, 15], "B" : [5, 10, 15]})
我们可以使用相应的字典键来索引字典。这将给出给定键的值对。这将方便地为我们提供下一个数据结构,列表:
lst = dct["A"]
列表可以用我们想要访问的元素的位置来索引。例如,我们新列表的第二个元素是 10。我们可以用这个来称呼它
lst[1]
当然,这是一个,而不是两个,因为在 Python 中索引是从零开始的。不用说,使用列表索引肯定会派上用场。我们可以索引的另一种类型是字符串:
"Hello"[1]'e'
设置索引
与调用索引同等重要的是设置索引。设置索引不仅允许我们在列表和其他可迭代的数据结构上创建新的位置,还允许我们改变数据结构内部的现有值。此外,我们可以使用这种方法将关键字放入字典,将列添加到 Pandas 数据帧,等等。在 Python 中,索引设置调用 setitem()方法。让我们继续为此编写一个函数:
def __setitem__(self, x, y):
pass
现在,我们将在这个新函数中编写一点逻辑,允许它将相应的字典键的值设置为新的输入值:
def __setitem__(self, x, y):self.index[x] = y
现在,我们可以将比赛位置的指数设置为新值:
print(race_winners.index.values())dict_values(['Nancy', 'Bobby', 'Reagan'])race_winners[2] = "John"print(race_winners.index.values())dict_values(['Nancy', 'John', 'Reagan'])
当然,同样的概念也适用于 Python 中的所有数据结构。我们可以将此应用于列表、字典值,但不能应用于字符串,如下所示:
z = [5, 10]z[1] = 1d = dict({"h" : 5, "z" : 6})d["z"] = 5assert d["z"] == d["h"]assert z[1] < z[0]
重要功能
现在我们已经了解了索引的基本知识,让我们来看一下在处理数据结构时可能会用到的一些重要函数。这些函数中有许多对于处理大量数据非常有用。应该注意的是,这些函数主要用于列表和矩阵,尽管有些函数可能适用于其他结构。这将是我们要使用的这些函数的列表:
lst = [5, 1, 6, 4, 5, 7, 3,5, 4, 3, 1, 2, 3, 4,]
1.插入()
第一个有用函数是 insert。Insert 将允许你在一个数组中的任何索引处放置任何值。如果您希望特定数组在某个位置包含特定组件,这将非常有用。它非常容易使用,只需添加两个参数,位置,然后是你想要添加的值。
lst.insert(5, 3)
2.追加()
下一个函数是 append 函数。这个函数经常用于生成列表,通常是在迭代中。这将为下一个可用索引添加一个值,并且只需要一个值作为参数。
lst.append(5)
对于这种特殊情况,更好的方法可能是 lambda with mapping,或者如果是 series 类型,可能是 apply 方法,在这种情况下,我将使用迭代循环和 append 函数来演示如何以这种方式使用它:
lst2 = []for z in lst: lst2.append(z * 3 + 2)
3.移除()
Remove 将从给定列表中删除一个值。注意,这需要一个值,而不是一个索引!它也只会移除给定值的一个实例。
lst.remove(1)
4.扩展()
extend 函数本质上只是 append()方法,但是允许我们将一个列表追加到列表的末尾:
lst.extend([101, 503])
5.计数()
Count 将返回一个整数,它是给定列表中某个元素的所有实例的计数。
lst.count(5)4
这个函数的一个很好的用例是计算模式。观察我如何使用 count 和字典在迭代循环中获得模式:
digit_n = {}for x in set(lst): cnt = lst.count(x) digit_n[cnt] = x mode = digit_n[max(digit_n.keys())]print(mode)5
6.排序()
我想讨论的最后一个重要列表函数是 sort()函数。该函数不带参数,将对列表进行逻辑排序,或者按照关键字参数的指定进行排序。在下面的例子中,我使用 sort 来查找列表的中值:
lst.sort()
medianindex = int(len(lst) / 2)print(medianindex)print(lst[medianindex])
结论
不用说,在编写任何涉及数据的代码时,索引是一件非常重要的事情。list 类的标准函数对于操纵、创建和从数据中获得洞察力也非常有用。我认为这是所有人都想知道的关于 Python 中索引的内容。非常感谢你的阅读,我希望你有一个美好的白天或夜晚休息!
关于 Python 中迭代列表的所有知识
Python — 30 天挑战
在本文中,我们将研究 Python 中迭代器的各种函数和循环方式
想了解 Python 中的高级迭代方式吗?你来对地方了。许多 Python 开发人员在遍历各种迭代器时使用简单的for和while循环,但是 Python 中有许多帮助函数可以让你的代码更简单甚至更快。

图像来自像素
在本文中,我们将了解以下内容:
for回路while循环enumeratezip- 列出理解
lambdamapfilterreduce
这是我 30 天文章写作挑战的一部分。请随意查看我的新人帖子上的文章:
https://nouman10.medium.com/lets-publish-30-articles-in-30-days-a0b8111855bb
For循环
让我们从基础开始。For 循环几乎出现在每一种编程语言中,它们的用法都非常相似。但是在 Python 中,for 循环可以以多种方式使用:
按值:
我们可以遍历一个列表,每次都得到它的值,如下所示:
输出将是:

作者图片
按索引:
我们可以通过使用range()函数并传递列表的长度来遍历列表。它将返回从 0 到列表末尾的索引。
输出将与上面相同。
While循环
与 for 循环类似,while循环也很常见,在各种语言中的用法也很相似。我们通常从定义一个带有初始值的计数器和基于该计数器的条件开始,当条件为真时,允许 while 循环运行。然后我们以某种方式改变 while 中的计数器,在某个时刻条件变为假。
输出将与上面相同。
列举:
enumerate函数让我们可以同时访问索引和列表中的值,如下所示:
这将给出如下预期的输出:

作者图片
邮编:
有时,我们需要同时遍历两个或更多的列表。zip是一个很棒的函数,它允许我们访问两个列表中相同索引处的值。在不相等列表的情况下,较小列表的长度被认为是最大索引。zip函数基本上通过从每个列表中选择一个值来创建元组的迭代器,如下所示。如果我们尝试将输出转换为列表,它基本上是一个元组列表:

作者图片
列表理解:
列表理解是以更紧凑的格式使用 for 循环的另一种方式。它们允许你在一个列表的每个元素上执行一个操作,例如转换。它们还可以用于过滤目的,因为您可以在其中包含 if…else 语句。
转型:

作者图片
过滤:
只有当数组中的元素是偶数时,下面的代码才会将该元素的值加倍:

作者图片
我们还可以使用 if…else 语句,如果数量是奇数,则将其增加三倍:

作者图片
λ:
lambda功能不用于迭代,但在使用map、filter和reduce功能时很有帮助。它创建了一个匿名函数,您可以将它应用于一个值列表,即任何值。其格式可以定义为:
lambda argument(s): return value

作者图片
为了更好地理解它,这个函数也可以写成如下形式:
地图:
顾名思义,map函数将一个函数映射到列表中的每个值。它接受函数作为第一个参数,数组作为第二个参数。它返回一个 map 对象,该对象可以被迭代,但不能打印为一个完整的列表,但可以转换为一个列表

作者图片
过滤器:
filter函数将一个函数映射到列表中的每个值,如果该函数为该值返回 true,则返回该值,否则跳过它。以下函数仅在偶数时返回值:

作者图片
减少:
reduce function 基本上将一个元素列表简化为一个元素。您从一个默认值开始,然后通过reduce传递的函数应用于列表中的每一项。关于reduce函数的一个棘手的概念是reduce中使用的函数接受两个参数。第一个是到目前为止的累积值,下一个是列表中的当前元素。第一个参数是开始时的默认值。我们可以使用下面的代码来计算一个使用reduce的数组的和:
我们从默认值 0 开始,并不断添加元素。

作者图片
结论:
总结一下:
- 我们从几乎每个开发人员都使用的基本 for 和 while 循环开始。
- 然后,我们研究了一些更有效、更简单的迭代函数以及列表理解。
- 最后,我们在各种函数中使用了
lambda,用于转换、过滤和各种目的。
如果您觉得以上内容对您有用,请分享并随时支持我-->
关于 Python 中的列表,您需要知道的一切
Python — 30 天挑战
在本文中,我们将从基础开始,深入探究列表在 Python 中能做的一切
Python 中的列表或其他编程语言中的数组基本上是一种复合数据结构,它基本上可以保存一个项目列表。本文为初学者提供了一种在 Python 中使用列表的友好方式。我们将在这篇文章中讨论很多关于列表的内容,所以让我们开始吧!!

图片来自 Unsplash
以下是我们将在列表上讨论的主题列表(双关语 xD):
- 列表介绍
- 列表的常用方法
- 列表迭代
- 将列表用作堆栈
- 将列表用作队列
- 结论
这是我 30 天文章写作挑战的一部分。请随意查看我的新人帖子上的文章:
https://nouman10.medium.com/lets-publish-30-articles-in-30-days-a0b8111855bb
列表介绍
Python 有很多可以用来存储条目列表的数据结构,但是列表是最通用的一种。它被写成用方括号括起来的逗号分隔的值列表。
array = [1, 2, 3, 4, 5, 6, 7]
array

作者图片
python 中的列表可以进行索引(0 索引,允许负索引)和切片,如下所示:
array = [1, 2, 3, 4, 5, 6, 7]
print(array[0])
print(array[-1])
print(array[2:5])

作者图片
列表也允许如下连接(但不会像这样修改原始列表,除非您将值存储在该变量中):
array = [1, 2, 3, 4, 5, 6, 7]
print(array + [8, 9, 10, 11])

作者图片
列表是可变的,也就是说,可以像下面这样改变它们的内容:
array = [1, 2, 4, 4, 5, 6, 7] # 4 instead of 3 on index 2
array[2] = 3
print(array)

作者图片
我们也可以给列表的片段赋值(这可能会也可能不会改变它们的长度):
array = [1, 2, 3, 4, 5, 6, 7]
array[2:5] = [5, 4, 3]
print(array)
array[2:5] = []
print(array)

作者图片
列表可以包含不同类型的对象,甚至列表中的列表!(嵌套列表)
array_1 = [1, 2, 3, 'a', 'b', 'c']
array_2 = [4, 5, 6, 7]
combined = [array_1, array_2]
print(combined)

作者图片
关于列表的介绍就到此为止。让我们看看列表中常用的一些方法
列表的常用方法:
在这里,我们将研究 python 中应用于列表的一些常用方法,最后,我们将看到所有这些方法的演示:
注:*list* 、 *list_1* 、 *list_2* 均代表某种任意的列表。
len:
顾名思义,它返回一个列表的长度。
语法:len(*list)*
追加:
它允许我们将任何项目添加到列表的末尾。
语法:*list*.append(item)
扩展:
它将一个列表中的所有项目追加到另一个列表中。
语法:*list_1*.extend(*list_2*)
插入:
它在提到的索引中插入给定的项目
语法:*list*.insert(index, item)
移除:
它从列表中移除给定项的第一个匹配项。如果项目不存在,它会引发一个ValueError
语法:*list*.remove(item)
流行音乐:
它从给定的索引中移除该项并返回该项。在没有给定索引的情况下,它从列表中删除最后一项并返回。
语法:*list*.pop()或*list*.pop(index)
清除:
顾名思义,它清除列表(清空列表)
语法:*list*.clear()
索引:
它在列表中搜索给定的项,并返回它在列表中第一次出现的索引,如果它不在列表中,则返回-1。它还有可选参数, start,和 end,限制列表内的搜索空间
语法:*list*.index(item)
计数:
返回给定项目在列表中出现的次数
语法:*list*.count(item)
排序:
就地排序列表,即修改原始列表
语法:*list*.sort()
反转:
原地反转列表
语法:*list*.reverse()
副本:
返回列表的一个浅拷贝
语法:*list*.copy()
演示:
输出:

作者图片
列表迭代:
列表迭代是一个独立的主题,我在以前的一篇文章中详细讨论过:
将列表用作堆栈:
堆栈根据后进先出算法执行操作。通过使用内置方法,列表可以很容易地充当堆栈。要将任何元素追加到列表的末尾,我们可以使用list.append(item)并从列表中检索任何元素,我们可以使用list.pop()而不使用任何索引,这将移除并返回列表中的最后一个(追加的)元素。

作者图片
将列表用作队列:
队列根据 FIFO(先进先出)算法执行操作。为此,我们可以通过使用列表的索引方法pop和insert来使用列表,但这样效率不高,因为从列表末尾开始的追加和弹出非常快,而在列表开头的弹出和插入效率不高,因为所有元素都必须移动一个索引。
幸运的是,Python 有一个内置的collections.deque,可以实现快速追加和从两端弹出。

作者图片
结论:
总结一下:
- 我们从 Python 中列表的基本介绍开始
- 然后我们研究了列表的一些内置方法
- 最后,我们研究了如何将列表用作堆栈和队列。
如果您觉得以上内容对您有用,请分享并随时支持我-->
了解多重共线性以及如何在 Python 中检测多重共线性
它是什么,它从哪里来,为什么不好,以及如何处理它。

Amir-abbas Abdolali 在 Unsplash 上拍摄的照片
介绍
在接下来的几篇文章中,我想写一些与回归分析相关的真正强大的主题。在很长一段时间里,我不认为线性回归有什么大不了的——我只是认为它是最简单的机器学习模型,只不过是一条最佳拟合线。
然而,随着我对回归分析和它所能提供的东西了解得越来越多,我意识到有很多强大的工具和技巧是大多数人不知道的。
所以,作为开始,我想谈谈多重共线性。具体来说,我要涵盖:
- 什么是多重共线性
- 什么导致多重共线性
- 为什么它对线性回归模型不好
- 如何检测和消除多重共线性
- 如何检测 Python 中的多重共线性
说了这么多,让我们开始吧!
什么是多重共线性?
多重共线性(或共线性)当回归模型中的一个独立变量与另一个独立变量线性相关时发生。
例如,如果我们在回归模型中使用“年龄和“年轮数”来预测树的重量。
因为树的年龄和树的年轮数(通常每年一个年轮)之间有很高的相关性,所以多重共线性将出现在该模型中。
什么原因导致多重共线性?
为了理解为什么多重共线性不好,我们必须看看回归系数(或模型参数)是如何估计的。这一部分涉及到一点线性代数,但是如果你不感兴趣,可以跳过这一部分。
注:跳过这一部分也不用担心,只要把重点放在为什么多重共线性不好以及如何消除多重共线性上就可以了。😃
请注意,回归系数是指每个变量的“斜率”,在方程 y = B0 + B1x+ B2x2 中,B1 和 B2 是回归系数。请记住,线性回归模型的要点是找到代表数据的最佳回归系数。
为了找到最佳回归系数,我们希望找到使平方误差最小的值。做了一点数学计算后,你可以用下面的等式找到最佳参数(B1,B2,…,Bp):

其中 B_hat 是包含所有单个回归系数的向量,X 是由预测变量组成的设计矩阵。
注意,为了估计 B_hat,我们在方程中假设(XTX)是可逆的。

如果 X 的列彼此线性相关(即,如果存在多重共线性),XTX 是不可逆的,这将导致几个后果,您将在下一节中看到。
多重共线性产生的问题
概括地说,为了正确估计多元回归模型的回归系数,XTX 必须是可逆的。如果 XTX 是不可逆的,这意味着 X 的列彼此线性相关,并且存在多重共线性。
最终,多重共线性的存在会导致几个问题:
- 如果其中一个 x 变量的其中一个值仅稍有改变,则拟合的回归系数(hat)将会显著改变。
- 估计系数的方差将被夸大,这意味着难以检测统计显著性。此外,有可能 F 统计量是显著的,但单个 t 统计量不是显著的。
- 最终,多重共线性会降低预测的准确性。对于给定的模型,潜在的假设是预测变量之间的关系以及它们与目标变量的关系是相同的。但是,当存在多重共线性时,这种情况不太可能出现。
如何检测和消除多重共线性
检测模型中多重共线性的一种简单方法是对每个预测变量使用称为方差膨胀因子或 VIF 的东西。

VIF 测量模型中只有该变量的给定回归系数的方差与模型中所有变量的给定回归系数的方差之比。
VIF 为 1(最小可能 VIF)意味着测试的预测值与其他预测值不相关。VIF 为 1(最小可能 VIF)意味着测试的预测值与其他预测值不相关。
VIF 越高,
- 一个预测值与其他预测值的相关性越大
- 标准误差被夸大得越多
- 置信区间越大
- 系数被评估为统计显著的可能性越小
可接受的 VIF 小于 10 和 1/1-R 模型的最大值(见下文):

如何用 Python 实现 VIF
举个例子,我要用 Kaggle 的加州房价数据集。
首先,我导入了所有相关的库和数据:
import pandas as pd
import numpy as np
from statsmodels.stats.outliers_influence import variance_inflation_factor
接下来,为了简单起见,我只选择了 3 列作为我的特征(X 变量),并删除了所有的空值和无穷大值:
df = pd.read_csv('housing.csv')
df = df[~df.isin([np.nan, np.inf, -np.inf]).any(1)]
X_variables = df[['total_rooms','total_bedrooms','median_income']]
最后,我计算了 X 变量的 VIF:
vif_data = pd.DataFrame()
vif_data["feature"] = X_variables.columns
vif_data["VIF"] = [variance_inflation_factor(X_variables.values, i) for i in range(len(X_variables.columns))]
就这样,我们得到了最后的结果:

直觉上,这完全说得通。房间总数和卧室总数远远高于 VIF 阈值,表明这些变量之间存在高度共线性。我们可以直观地理解这一点,因为房间数和卧室数之间有很强的相关性(卧室越多,房间越多,反之亦然)。
感谢阅读!
我希望您发现这很有用,并且您学到了一些新东西!多重共线性是回归分析中一个极其重要的概念,因此它绝对是一个需要理解的重要概念。
不确定接下来要读什么?我为你挑选了另一篇文章:
</40-statistics-interview-problems-and-answers-for-data-scientists-6971a02b7eee>
又一个!
[## 六分钟内解释所有概率分布
towardsdatascience.com](/all-probability-distributions-explained-in-six-minutes-fe57b1d49600)
特伦斯·申
关于 Push 你需要知道的一切!在朱莉娅
用力!()方法是你需要了解的 Julia 基础的重要部分。让我们来看看它能做什么。

(图片由蒂姆·莫斯霍尔德在【Unsplash.com】T2 拍摄)
Julia 编程语言有一个丰富和扩展的基础,它本身能够做很多事情。Julia 与许多其他编程语言的不同之处在于它健壮的类型系统。方法系统当然伴随着这个很好,然而类型系统才是真正的抢镜。还有一些很棒的方法可以处理 Julia 中的所有类型。如果您使用 Julia 已经有一段时间了,那么您很可能已经遇到了推送!()野外法。该方法很常用,因为它是 Julia 的核心特性。也就是说,那些对 Julia 感兴趣的人可能想获得一些使用它的经验。
Julia 语言的一个优点是一个方法定义本质上有无限的能力。这是一个非常朱利安的事情进口推动!()方法,并扩展它以处理您环境中的类型。方法中也有一些不规则和奇怪的东西,可能很重要,其中一些甚至是一些有经验的 Julian 程序员也没有经历过。今天,我们将从核心 Julia 数据类型的基础开始,介绍该方法的所有细微差别,然后我想介绍如何扩展该模块以使用类型的示例实例。这个项目还有一个笔记本,您可以在这里查看:
https://github.com/emmettgb/Emmetts-DS-NoteBooks/blob/master/Julia/Push in julia.ipynb
用力!:基础知识
我们将从一个简单的数组开始:
example = [9, 7, 4, 28]
关于数组,有一些特殊的事情需要注意:
- 它们是可重复的。
- 它们是 base Julia,所以 base-Julia 方法与它们一起工作。
与许多其他语言不同,我们有一个相当健壮的索引接口。既然这些都是可重复的,只要一推!()方法知道如何处理我们传递的 iterable,它可以将我们想要的任何类型放入其中——这很方便,因为像 append!()方法只方便某些数据类型的使用。我们可以用?(推!)来了解关于这个函数的更多信息,这也将给出这个函数的所有不同调用:
?(push!)
这在朱莉娅是一个很好的实践。那个?()方法允许我们尽可能深入地快速浏览文档。与传统上可能在 web 浏览器中查找一些文档相比,这种文档字符串系统有很多非常好的优点。实际上,我写了一整篇文章,更详细地讨论了 Juliahub 系统的文档,如果你不知道这两件事,你可能会感兴趣,下面是这个故事的链接:
从中我们可以看出一点,它将会变异成我们提供的第一个论点。在深入研究之前,我们可以立即指出这一点,因为这个方法在一个解释点结束。这是另一个深入到背后故事的很酷的故事:
回到我们的笔记本,我们已经对这个方法有了很好的了解,但是让我们看看实际的文档能告诉我们什么。

(图片由作者提供)
该文档为我们提供了一个 REPL 绑定的示例、详细描述的参数,甚至一些在不同情况下使用的其他方法。前面提到的追加!()在这里做了一个重要的注释,作为 append!如果追加是我们真正想要做的,那么()可能是某些结构的更好应用。我们将遵循这些参数,并为我们的旧示例数组加入一些新数据。
push!(example, 5)5-element Vector{Int64}:
9
7
4
28
5
这将产生一个 5 元素的数组,一个整数向量,它将改变数组的维数和数组中的可迭代内容。奇怪的一点是,有一些细致入微的类型是不能推的。我们通常可以很好地将结构推进这里。然而,对于某些数据类型,甚至是构造类型,它将不起作用。
多带推!()
正如我前面提到的推!(),当涉及到类型时,这个方法有一些细微的差别。我发现提供范围生成器的例子很奇怪:
**push!(example, 1:5)**MethodError: Cannot `convert` an object of type UnitRange{Int64} to an object of type Int64
Closest candidates are:
convert(::Type{T}, ::T) where T<:Number at number.jl:6
convert(::Type{T}, ::Number) where T<:Number at number.jl:7
convert(::Type{T}, ::Base.TwicePrecision) where T<:Number at twiceprecision.jl:250
这是一个很难辨别的错误,我觉得可能会让初学者感到困惑。这里的 stack-trace 并没有很好地展示出做错了什么,除非人们意识到这里的问题是键入。我们得到这个错误的原因是因为我们提供的类型是一个 UnitRange,但是每当它被添加到 vector 时,我们都会得到一个转换尝试。这是因为向量只能包含整数,因为它的类型是
Vector{Int64}
我们当然可以改变这一点。在 Julia 中,这可以通过将这个向量转换成一个可以包含任何类型的值的数组来实现。但是,这将改变行为,这可能是范围生成器所不希望的,例如:
push!(Array{Any}(example), 1:5)6-element Vector{Any}:
9
7
4
28
5
1:5
现在我们有一个 6 元素数组,里面有不同的类型,单位范围和那些整数在一起——我们可能期望在数组的末尾添加 1、2、3、4 和 5。这可能看起来更像是预期的行为,但通常这就是推动的方式!()方法会起作用。
延伸推!()
在一个真正的朱利安时尚,我们现在要延长推!()更好地配合我们的 UnitRange。每当我们想通过调度来扩展某种功能时,第一步就是直接导入它:
import Base: push!
如果你不知道如何扩展方法,就像这样,我就不在这里赘述了,但我有两篇文章,其中一篇有视频,在 Julia 中详细介绍了这一点:
https://medium.com/chifi-media/extending-julias-base-for-our-superframes-jl-package-4574555ff6ae
希望现在这个概念在你的头脑中是新鲜和熟悉的,这将更容易理解我到底想做什么。我们的目标输入参数将是:
- 我们的收藏
- 单位范围
我们想要的输出是集合中的所有元素。首先考虑我们的输入及其类型,让我们定义这个新函数:
function push!(collection::Vector{Int64}, range::UnitRange)
[push!(collection, i) for i in range]
end
更快的方法可能是使用 collect()方法。
在函数内部,我有一个简单的迭代循环,它解包范围内的单个值,以便提供这个功能。每当将此新定义加载到环境中时,都可以将其添加到此方法定义的文档中:
"""## push!(collection::Vector{Int64}, range::UnitRange)
This method can be used to unpack a range generator and return its elements pushed into the collection. \
julia> push!([2, 3] 1:2)4-element Vector{Int64}
2
3
1
2
function push!(collection::Vector{Int64}, range::UnitRange)
[push!(collection, i) for i in range]
end
现在让我们看看我们的?()前一个示例的文档调用:
?(push!)

(图片由作者提供)
幸运的是,我们的新定义被附加到其他文档中。我相信在这两组文档之间使用某种区分是有用的,因为 push!(::Vector{Int64},::UnitRange)调用增加了一些不同的功能。这也是我们直接引用该调用的方式,例如:
?(push!(::Vector{Int64}, ::UnitRange))

(图片由作者提供)
让我们最后使用这个函数,看看将要进行的最后一次调用:
push!(example, 1:5)10-element Vector{Int64}:
9
7
4
28
5
1
2
3
4
5
这可能看起来工作量很大,为此要编写一个完整的方法扩展。然而,在您的编程语言中,在整个软件包生态系统中使用相同的方法调用简直令人惊奇。我们避免了命名冲突,并且能够根据类型反复使用相同的别名。虽然您可能希望编写一次 for 循环,但您可能不希望在这段代码的每个实例中反复编写它。也就是说,这就是函数的用途,把你的新函数写成一个众所周知的定义可能会让你的用户猜测如何更有效地使用你的包!
结论
右推!()挺牛逼的。这个方法的伟大之处在于它有许多不同类型的功能。我相信这是通过抽象来实现的,因为一般来说,Julia 中的大多数类型都是一个更大的类型层次结构的一部分,这个层次结构可以限制方法的行为。Julia 有很多优点,但有一点对我来说非常突出,那就是它的可扩展性。很少有人模仿 Julia 对多个分派和文档的处理,而且最重要的是,Julia 中的整个名称定义都可以重用和扩展。厉害!谢谢你的阅读,我希望你有一个可爱的一天!
关于 Python 中的 Zip,您需要知道的一切
Python 的 zip 类型的所有重要内容的概要。

(src =https://pixabay.com/images/id-2923487/
介绍
迭代是程序员在处理可迭代数据结构时非常常用的技术。迭代可能相当复杂,有时相当困难。有些情况下,迭代 for 循环可能无法非常有效地完成工作。也就是说,对于一次迭代多个元素的情况,列表的典型迭代可能会有问题。幸运的是,Python 为这个问题提供了一个很好的解决方案,叫做 zip。
Zip 很棒,因为它允许我们同时处理两个或更多的数据结构。使用 zip,我们可以在 Python 中同时迭代遍历多个对象。不用说,这在很多情况下都会派上用场。我还擅自将这款笔记本添加到了 Github 库,您可以在这里查看:
Zip 是什么?
Python 中的 zip 对象就是所谓的迭代器。迭代器在循环中使用,通常是基于元素的循环。给定名称,我们可以假定这些通常与迭代一起使用。Zip 是由 Python 的基础提供的,所以我们可以在语言的任何地方调用它。为了创建一个 zip 对象,我们简单地将两个 iterables 传递到它的初始化函数中:
x = [5, 10, 15]
y = [5, 10, 15, 20]
zipper = zip(x, y)
现在让我们来看看这个新迭代器的类型:

type(zipper)
现在我们可以在 for 循环中调用这个新的迭代器,以便同时遍历这两个列表中的元素。我不记得 Python 是否接受这个或一个元组的参数,因为没有答案的都是我们在 Julia 中做的。但是,我很确定这是 Python 中的参数,所以让我们试试:

[print(i, c) for i, c in zipper]
通常,该对象不会在迭代循环调用之外构造,因此使用 zip 类型的典型迭代循环通常是这样构造的:
for i, c in zip(x, y):
print(i, "\n")
print(c)

作者图片
重要细节
既然我们已经有了 zip down 的基础,现在让我们回顾一下关于这个类的所有重要细节。首先,我们需要理解为什么需要调用这个迭代器来进行实际迭代。通常在这种情况下,我们的迭代器会自动开始循环,而不需要创建 for 循环。
这是因为 zip 对象就是我们所说的惰性迭代器。懒惰迭代器之所以懒惰,是因为它们自己不做太多事情。尽管如此,当与 for-loop 语法结合使用时,它们会非常强大。接下来我要讲的是数组中元素的数量。
你可能已经注意到,在上面的例子中,x 和 y 是不同的维度。这是有意的,因为接下来我想演示的是当我们滑过数组时会发生什么,这就是情况。当然,在典型的应用程序中,情况并非如此,因为在 zip 迭代器中遇到这种情况的可能性似乎要小得多,然而,关于对不同维度的数组使用 zip,您可能需要知道一些事情。
您应该知道的第一件事是,zip 迭代器只会迭代较短的 in 元素。这意味着更大的数组中的任何元素都将被截掉,如下面的演示所示,我们看到当 y 的长度为 4 时,我们只得到三个不同的打印输出。
for i, c in zip(x, y):
print("I:")
print(i)
print("\n")
print("C:")
print(c)
print("\n")
print(len(x))
print(len(y))

如果我们想改变这一点,我们可以使用 itertools 模块中的 zip_longest。这个模块在标准库中,提供了一系列不同的迭代工具和迭代器,可以用来加速 Python,或者执行不编写自己的迭代器就不可能执行的迭代。如果你想了解更多关于 Itertools 的功能,我有一篇文章可以提供给你:
zip_longest 迭代器将参数迭代到最长元素的末尾。如果您不想遗漏任何值,这可能会很有用。任何没有被循环的元素都将被返回为 None,如下例所示:
from itertools import zip_longest
for i, c in zip_longest(x, y):
print("I:")
print(i)
print("\n")
print("C:")
print(c)
print("\n")
print(len(x))
print(len(y))

结论
当我们使用多个数组并希望以某种形式使用迭代时,Zip 是一个很好的工具。迭代器有一些细微差别,但一般来说,这是一个非常好的概念,许多高级程序员都习惯和熟悉它。也就是说,从各个方面了解它可能会在找出一些奇怪的输出或错误时派上用场。感谢您的阅读!
关于深度学习模型的“激活函数”,你需要知道的一切
想知道什么是激活功能,为什么它们必不可少?

艾莉娜·格鲁布尼亚克在 Unsplash 上的照片
我们知道深度学习模型是激活函数、批量归一化、动量、梯度下降等不同成分的组合。因此,在这篇博客中,我选择了 DL 的一部分,通过回答以下问题给出了关于激活功能的详细解释:
- 什么是激活函数?
- 为什么我们在神经网络中需要一个激活函数,如果我们不使用,会发生什么?
- 激活函数的期望属性是什么?
- 有哪些不同类型的激活功能及其用途?
对于这个博客,我假设你们都对神经网络有一个基本的了解。所以事不宜迟,让我们更深入地研究激活函数。
激活功能
激活函数是一个函数,用于变换神经网络中前一节点的输出信号。这种转换有助于网络学习数据中的复杂模式。就是这样!一开始我也不相信事情会这么简单,但这是真的。

激活函数转换前一个节点的输出(图片由作者提供)
需要激活功能
是的,你现在已经知道基本原因了。有必要使神经网络能够学习数据中的复杂模式。但是它是如何实现的呢?激活函数通常是非线性函数,它给神经网络增加了非线性,从而允许它们学习更复杂的模式。还不清楚?让我们看一个神经网络的例子来理解这一点。

神经网络(图片作者提供)
对于这个例子,请假设我们没有添加偏差。我们可以将输出 Y 写成:

作者图片
其中 Act 表示我们的激活函数的输出,(这是一个非线性变换)。现在假设我们没有网络的任何激活函数,那么我们的 Y 看起来像这样:

作者图片
如果你仔细看上面的等式,那么

作者图片
这意味着,即使我们的网络中有两层,输入和输出关系实际上也可以由单个权重矩阵来定义(这是两个权重矩阵的乘积)。因此我们看到,在没有激活函数的情况下;我们给神经网络增加多少层并不重要,它们都可以被简化,只用一个权重矩阵来表示。但是,当我们添加激活函数时,它们会添加非线性转换,这使得我们无法简化多层神经网络。
激活函数的另一个重要性是,它有助于将神经元的输出值限制在我们需要的范围内。这一点很重要,因为激活函数的输入是 W*x + b ,其中 W 是单元的重量,而 x 是输入,然后还有偏置 b 加入其中。如果不受限制,该值可以达到非常高的量级,尤其是在处理数百万个参数的非常深的神经网络的情况下。这进而导致计算问题以及值溢出问题。
我知道没有激活函数的例子,很难理解我扔给你的所有事实,但是请耐心听我说。我们将很快在一个例子中回顾所有这些概念,但是为了你更好的理解,它们需要在这里被覆盖。
激活函数的期望属性
- 从上面可以清楚地看出,激活函数应该是非线性的。
- 在神经网络的每一层之后使用激活函数,因此,总是希望激活函数在计算上高效。
- 对神经网络的所有组件的主要要求是它们应该是可微分的,因此激活函数应该也是可微分的。
- 设计激活函数的一个重要方面是防止消失梯度问题。深入解释这个问题超出了本博客的范围,但让我给你要点。为了防止消失梯度问题,要求激活函数 w.r.t 输入参数的导数在理论上是不有界在 -1 到 1 之间。
不同类型的激活功能
热卢
ReLU 代表RectivedLlinearUnit,定义为 f(x) = max(0,f(x))

ReLU 图(图片由作者提供)
这是一个广泛使用的激活函数,尤其是与 CNN(卷积神经网络)一起使用。它易于计算,不会饱和,也不会导致梯度消失的问题。但是,它有一个问题,即对于负输入,它的值变为零。由于所有负输入的输出都是零,这导致一些节点完全死亡,什么也学不到。为了处理这个问题,使用了漏 ReLU 或者参数 ReLU,即 F(x) = max(αx,x)。

泄漏的 ReLU(图片由作者提供)
乙状结肠的
这个激活函数在计算上是昂贵的,导致消失梯度问题,并且不是零中心的。这种方法通常用于二进制分类问题,并且仅在神经网络的末端使用,以将输出转换为范围[0,1]。这个函数一般不在神经网络内部使用。

Softmax
用于多类分类问题。像 sigmoid 一样,它产生 0-1 范围内的值,因此,它被用作分类模型中的最后一层。
结论
我希望在这个解释之后,你现在能更好地理解为什么神经网络需要激活函数,以及激活函数的性质和类型。如果你觉得有帮助,请在这个博客上发表评论,让我们知道。
关注我们的 medium 了解更多此类内容。
成为 介质会员 解锁并阅读介质上的许多其他故事。
自动化 OSX 所需的一切

当我们想到自动化工作负载、可重复流程、“一键式”部署或类似的东西时,我们通常会想到笔记本电脑。我们在做任何事情的时候都在练习 DevOps,为什么我们的笔记本电脑会被忽视呢?毕竟,我们都同意“宠物”是坏的,“羊”是好的。如果您的笔记本电脑现在没电了,您需要多长时间才能将一台新电脑完全设置到正常工作状态?
对我来说,答案大概是 10-15 分钟的工作和 30 分钟的等待(咖啡休息时间!万岁。).
除非你运气特别差,否则你不需要经常更换你的工作笔记本电脑。但是如果你的笔记本电脑升级到新的型号呢?如果你的笔记本电脑坏了怎么办?如果你买了一台新的个人笔记本电脑会怎么样?如果你不得不做一件事不止一次,为什么不自动化呢?
这是我大约两年前经历的思考过程,从那以后,设置一台新的笔记本电脑就不再是一个问题。让我向您介绍我的过程,并向您展示如何自动化您的 OSX 设置。
先决条件
要使这一过程成功,需要做好几件事情:
- 您需要在笔记本电脑上登录您的 iCloud 帐户
- 你需要一个云存储服务(例如 Dropbox、iCloud、Google Drive)
- 您的引导脚本(我们将一起创建)和配置文件应该存储在这个云存储服务中。
就是这样!这就是使用此流程所需的全部内容。
过程
在我开始向您展示代码片段之前,我想先介绍一下我们正在努力实现的目标,以及我们将如何实现它。
我们将编写并运行一个 bash 脚本,它将:
- 检查
xcode、&、brew是否安装,如果没有安装。 - 使用
brew,我们还将安装一个名为mas的应用。此应用程序允许您通过 CLI 从 App Store 安装应用程序。 - 使用
brew和mas安装我们想要的任何东西。 - 使用云存储服务上的配置文件配置笔记本电脑。
当我们完成时,我们所有的键绑定、别名、AWS 配置文件等…都将自动设置在我们的笔记本电脑上。新的笔记本电脑应该处于与之前的硬件相同的状态。
让我们开始编码吧!
现在你知道我们在做什么,编码将会很容易。我们从以下内容开始:
**#!/usr/bin/env bash** if [ -f ~/.osx-bootstrapped.txt ]; then
cat << **EOF** ~/.osx-bootstrapped.txt FOUND!
This laptop has already been bootstrapped
Exiting. No changes were made.
**EOF** exit 0
fi
CURRDIR=`pwd`
BREWINSTALLED=`which brew`
XCODEINSTALLED=`which xcode-select`
# Install Xcode
if [[ ${XCODEINSTALLED} == "" ]]; then
echo "Installing Xcode"
xcode-select --install
fi
# Install Brew
if [[ ${BREWINSTALLED} == "" ]]; then
echo "Installing Brew"
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
fi
我们做的第一件事是检查这个脚本以前是否被执行过,如果是,我们不希望它再次运行,并将优雅地退出。检查完成后,我们开始工作。
接下来,我们检查是否使用which命令安装了xcode和brew。如果在本地系统上找不到它们,我们将安装它们。
下一部分将因人而异,因为并非所有人都使用相同的应用程序。
#Required App
brew tap homebrew/cask
brew install mas#List your preferred applicationsbrew install --cask intellij-idea
brew install --cask google-chrome
brew install --cask slack
brew install --cask spotify
brew install --cask spectacle
brew install --cask karabiner-elementsbrew install jq
brew install awscli
brew install terraform
brew install packer
brew install docker-compose
brew install mysql-clientmas install 1295203466 #Remote Desktop
以上大部分都很容易理解。我们设置 brew tap/repo,并安装mas。除此之外,我正在安装我在笔记本电脑上使用的所有应用程序。我的清单要长得多,但是为了这个例子,上面的应该足够了。
节日
我想扩展的一件事是mas,因为这可能不是主流的工具。如前所述,mas 允许您使用 CLI 与 Apple App store 进行交互。这是我们将自动安装任何应用商店应用的机制。
一旦你在当前的笔记本电脑上安装了这个工具,你应该运行mas list。它将输出一个列表,列出所有安装在你的笔记本电脑上的应用程序及其 id。
➜ mas list | egrep 'Numbers|Key|Remote'
1295203466 Microsoft Remote Desktop (10.5.1)
409183694 Keynote (10.3.9)
409203825 Numbers (10.3.9)
有了这些 id,您现在可以通过运行mas install {IDNUMBER}来安装它们。关于 mas 的更多信息,请查看他们的 Github repo 。
编码继续
既然我们已经讨论了 mas,并且安装了我们的应用程序,那么我们继续配置。和以前一样,这一步对每个人来说都是不同的,但是这应该会让你知道我们在努力做什么以及如何做。
# Symlink my configs
ln -s $CURRDIR/aws/ ~/.aws
ln -s $CURDDIR/bash_profile-config ~/.bash_profile
#Remove default karabiner dir since we are providing our own
rm -rf ~/.config/karabiner
ln -s $CURRDIR/karabiner-config ~/.config/karabiner# ln -s $CURRDIR/karabiner-config/karabiner.json ~/.config/karabiner/karabiner.json
# Remove Spectable default shorcuts json since we are providing our own
rm -f ~/Library/Application\ Support/Spectacle/Shortcuts.json
ln -s $CURRDIR/spectacle/Shortcuts.json ~/Library/Application\ Support/Spectacle/Shortcuts.json
因为所有的配置文件都在云中,所以这一步就像创建一个符号链接一样简单。您可以复制文件,而不是创建符号链接。然而,我更喜欢符号链接,因为这意味着我所有的笔记本电脑都使用相同的配置。如果我在我的一台笔记本电脑上添加别名,它将在我所有的笔记本电脑上可用。
我们的最后一步是安装‘哦,我的 Zsh’并创建我们的osx-bootstrapped.txt文件。
if [ ! -d ~/.oh-my-zsh ]; then
sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
rm ~/.zshrc
ln -s $CURRDIR/zshrc-config ~/.zshrc
fi
touch ~/.osx-bootstrapped.txt
你可以传递“哦,我的 Zsh ”,但是如果你还没有使用它,我强烈建议你去看看。
结论
我们都同意“宠物”是坏的,“羊”是好的。我们的笔记本电脑不应该有任何不同。鉴于笔记本电脑的重要性,这一点尤为重要。如果您按照上面的代码片段创建了自己的脚本,您就再也不用担心您的笔记本电脑会跑到天上的农场了。您还可以获得在所有笔记本电脑上获得一致体验的额外好处。只要 10 到 20 分钟的工作,你就能得到所有这些。非常值得投资回报!
为了获得无限的故事,你还可以考虑注册https://blog.rhel.solutions/membership*成为中等会员,只需 5 美元。如果您使用* 我的链接 注册,我会收到一小笔佣金(无需您额外付费)。
家得宝数字化转型的证据
一次周末的零售之旅激发了人们对数字化转型典范的关注。

布鲁克·拉克在 Unsplash 拍摄的照片
这个周末,当我在家得宝(Home Depot)闲逛,为我的公寓寻找新的外门时,我想到了一个组织,它是从我小时候在佛罗里达州莱克兰(Lakeland)认识的大盒子零售商演变而来的。我的高中朋友会在夏天在那里工作,我一直认为这是一个令人无法抗拒的地方。
我注意到……越来越多的时候,我能够不去家得宝零售点的大型仓库,而是雇佣专业人员使用应用程序和他们的网站来安装我的家居项目。我的整个外门项目都是通过他们的网站、呼叫中心和店内测量预订的。他们是如何变得如此擅长跨越不同接触点创造无缝体验的?
答案是:数字化转型。
数字化转型正在“融合物理、生物、化学和信息世界。”托尼·萨尔达尼亚认为这是第四次工业革命,他是《为什么数字化转型会失败》一书的作者,这是一本关于数字化转型最佳实践的非常成功的书。
Saldanha 提到“零售天启”或许多主要依赖实体销售的零售商申请破产的事实是第四次革命正在影响当今企业的一个征兆。(萨尔达尼亚,2019 年)
在这篇博客中,我将介绍美国主要家装零售商家得宝的一个例子,并描述他们正在进行的数字化转型及其成功的迹象。
是什么让数字化转型成功?

成功的数字化转型意味着让您的企业能够创造永恒的运动。这意味着您正在不断发展,以便在客户需要您的产品/服务时,随时随地满足他们的需求,并以最有效的方式提供他们所寻求的价值。这意味着您已经接受了技术,使组织的所有部分都能够利用这项技术,并创建了允许您根据客户和潜在客户的速度调整业务模式的流程。
在他的第二章中,Saldanha 将一个成功的转型描述为一个连续体中五个发展阶段的顶点。他的第五个阶段,“活的 DNA”,被描述为一个不断运动的组织。他们的员工精通数字技术,他们的系统相互连接,他们采用了最具创新性的商业模式,并将继续这样做,因为他们正在以精益敏捷的方式进行测试、学习和适应。(Saldanha,2019)这个想法描述了成功的数字化转型。
家得宝的商业问题
家得宝(Home Depot)是一家总部位于美国的零售商,其成立的目的是为“自己动手”的人提供服务,其大型仓储式商店以卓越的选择、乐于助人的员工和一站式商店的优质产品使当地五金店相形见绌。(家得宝,2021 年)
该模式非常成功,但随着时间的推移,新类型的客户出现,减缓了他们的增长能力。2016 年,家得宝(Home Depot)电子商务总裁凯文·霍夫曼(Kevin Hoffman)描述了新兴的“为我动手型”消费者,他们寻求全方位服务的安装、优质产品和较少动手的家装体验。为了给顾客提供他们想要的体验,家得宝需要解决遗留技术问题,并在购物体验之初就接受电子商务和数字财产。(劳克伦,2015 年)
解决方案:一个家得宝

2018 年,家得宝公布了他们的零售体验转型计划,以专注于其电子商务平台和店内的数字化、单一、互联的体验。该系统的引入是为了给消费者提供一种“无摩擦、互联的购物体验”(家得宝,2019 年)
尝试连接不同平台和商业模式的无摩擦购物体验的想法符合萨尔达尼亚概述的数字化转型的要求。就家得宝而言,它正在将店内购物体验和员工的帮助转化为电子商务和数字体验。转型需要以数字形式反映品牌,并确保供应商/商店能够交付在这些新渠道购买的服务。
解决的关键系统,使用的技术
为了开始转型,家得宝的首席执行官推出了 One Home Depot,这是一项创造互联体验的重大举措。高层领导的这种支持,以及在季度电话会议中讨论的目标和进展,为组织的其余部分定下了基调。变革需要发生,领导力需要投入。(邦德等人,2019 年)
为了实现转型,该组织对其 IT、开发人员、数据和分析能力进行了投资。2018 年,他们在美国各地的技术中心雇用了大约 1,000 名技术专业人员。这转变了公司的能力,使他们能够开发专为新的“为我做”细分市场构建的新的移动和电子商务客户体验。(家得宝,2018 年)
家得宝还更新了其在持续集成方面的能力和来自敏捷软件开发的想法。“OrangeMethod”是该公司教育员工新的思维方式,超越他们的核心能力,使数字化转型在他们的组织中发生。它还设定了让新技术员工能够建造一个家得宝的流程。(家得宝职业生涯,2018 年)
对家得宝的影响

由于积极主动的数字化转型,该公司为应对新冠肺炎的影响做好了充分准备。尽管商店关门,这家零售商在利用消费者趋势方面取得了巨大的成功,使家装零售商受益。根据他们的 2020 年年度报告,数字销售额比上一年增长了 86%,他们的数字资产全年的流量创下纪录,整体销售额增长了 20%。(家得宝,2020 年)
正如这位首席执行官所言,“创造无缝、互联体验,融合实体和数字世界的零售商将在市场中占据有利位置。”他将他的组织 2020 的成功归功于对数字化转型的投资,正如他们在 One Home Depot 愿景中实现的那样。(家得宝,2020 年)
参考资料:
t .萨尔达尼亚(2019)。在为什么数字化转型会失败:如何起飞并保持领先的惊人法则(第 3-31 页)。散文,Berrett-Koehler 出版社,BK 商业书籍。
关于我们。家得宝。(2021).https://corporate.homedepot.com/about.
劳克伦,S. (2019,4 月 29 日)。家得宝——打造“自己动手型”顾客体验。数码杂志。https://diginomica . com/the-home-depot-building-the-do-it-for-me-customer-experience。
家得宝将提供“一个家得宝”投资战略的更新,以扩大市场领导地位。(2019 年 12 月 11 日)。家得宝。https://IR . home depot . com/~/media/Files/H/home depot-IR/2019% 20 IAC/Press % 20 release _ VF . pdf
Bonde,a .,Chirokas,m .,Shwerdlow,f .,& Beeson,M. (2019 年 7 月 19 日)。家得宝如何成为数字发电站?埃文斯顿。https://www.forrester.com/go?objectid=RES155235.
家得宝雇佣 1000 名技术专业人员。家得宝。(2018 年 4 月 18 日)。https://IR . home depot . com/news-releases/2018/04-18-2018-130228118。
家得宝职业生涯。(2018).家得宝的 OrangeMethod。Vimeo.com。https://vimeo.com/272596009.
家得宝。(2020).年报。佐治亚州亚特兰大:家得宝投资者关系。
进化计算(完整课程)概述

https://unsplash.com/photos/FHnnjk1Yj7Y
进化计算课程
关于我将在这个全新的系列中涉及的材料、概念和应用的介绍性帖子!
大家好!我决定开设一门关于进化计算的课程。在这篇文章中,我将只给出课程的简要概述!
进化计算是计算智能的一个子领域,是机器学习和人工智能的一个分支。进化计算的应用很多,从解决优化问题、设计机器人、创建决策树、调整数据挖掘算法、训练神经网络和调整超参数。
如果你熟悉数据科学和机器学习模型,所有的统计和“黑箱”模型都是为了解决优化问题而建立的。你可能熟悉这些问题,如 MSE、交叉熵、MAE 等,在这些情况下,我们希望最小化这些值。进化计算是优化理论的一个领域,它不是使用经典的数值方法来解决优化问题,而是使用生物进化的灵感来“进化”出好的解决方案。当没有适应度函数的已知导数(强化型学习)时,或者当适应度函数具有许多可能陷入序列方法的局部极值时,通常使用进化计算来代替标准数值方法。
目录
- 要覆盖的材料
- 文献应用实例
- 预赛
- 结论
要覆盖的材料
- 单元 1)最优化理论
- 单元 2)进化计算简介
- 单元 3)遗传算法
- 第 4 单元)遗传规划
- 第 5 单元)进化规划
- 第 6 单元)进化策略
- 第 7 单元)差异进化
- 单元 8)共同进化
本课程将是一个简短而深入的系列教程,涵盖上述主题。会有很多帖子,会持续很久。如果你时间紧迫,只需阅读单元 1 至 3,因为它会给你一个优化理论,整体进化计算和遗传算法的基本概述。其余的单元,即单元 4-8,实际上都是遗传算法本身的不同变体。唯一不同的是,它们针对不同的问题进行了超参数化,具有独特的特征;然而,它们都有相似的形式,都属于遗传算法的范畴。
文献论文示例
在每个单元的结尾,我们将回顾一个在同行评议文献中发现的真实世界的例子。以下是我们将涉及的各种示例:
- 单元 3)训练用于时间序列分析的前馈神经网络
- 单元 4)发展相同时间序列问题的基本方程/结构
- 单元 5)求解约束非线性规划问题
- 单元 6)求解多目标 Pareto 前沿的算法
- 单元 7)自动机器学习:设计 CNN 的架构
- 单元 8)设计玩月球着陆器的游戏 AI
预赛
下面是一些预备知识,我希望你应该知道,以便最好地应用材料和理解概念:
- 基本统计—概率分布
- 线性代数(基本概念:矩阵乘法,欧几里德距离等…)
- 数值方法(基本概念:为什么使用它们,为什么需要它们)
- 如何用 Python 编程—使用数据结构和算法
- 了解 Python 科学库——Numpy 和 Scikitlearn
结论
进化计算是解决优化问题的方法论。优化问题在机器学习和人工智能领域中大量存在。EC 通常用于经典数值方法无法找到足够好的解的情况。
好了,这应该完成了课程的基本概述!如果您对迄今为止讨论的任何主题或应用示例感兴趣,那么请不要离开,因为我们将涵盖所有这些以及更多内容!在下一篇文章中,我们将从单元 1)最优化理论开始
机器学习的进化特征选择
了解如何使用进化模型为机器学习算法选择特征

厄瓜多尔皮钦查省。Alexander Schimmeck 在 Unsplash 上拍摄的照片。
在之前的一篇帖子中,我讨论了为什么我们应该使用基于优化的算法来微调机器学习模型的超参数。这也适用于特征选择;如果你有兴趣,可以在这里查看。
这篇文章将展示如何使用 sklearn-genetic-opt 快速找到最大化交叉验证分数的特征,同时删除非信息变量。
一般来说,使用强力方法来优化模型并不是一个好主意,在特征选择的情况下,使用向前选择或向后排除等方法,一次只能改变一个特征,并且在查看不同的特征子集(具有相同大小)如何一起工作时往往会有问题。
即使我们不必从头开始编写代码,因为像 sklearn-genetic-opt 这样的包已经实现了它,我还是想解释一下我们如何使用进化算法来找到这些特性的一般思想;如果你不熟悉这类算法,你可以查看我的另一篇文章,我在那里一步一步地解释了它们是如何工作的,以及如何使用它们进行超参数调整;或者想看代码,可以去下一节。
1。模型表示:
我们可以对这些特征建模如下:
- 群体中的每个个体代表特征的总子集。
- 个体的性别代表了一个特定的特征。
- 每个 gen 值可以是 0 或 1;零表示算法没有选择该特征,一表示包括该特征。
- 该突变与在突变概率内淹没随机选择的位置中的比特值相关联。

特征选择表示。图片由作者提供。
这一代有十个特点:该算法选择了功能 1、3、5 和 10,但不包括其他功能。

随机突变。图片由作者提供。
然后以足够低的概率,可能发生随机突变,现在在去除特征三的同时也包含了变量六;这是有帮助的,因此我们帮助模型不陷入局部最小值,并且探索更多的 bast 区域(特征子集),而不检查所有可能的特征组合。
有了这种表示,我们现在可以应用常规的进化算子来寻找新的解;这些新的解决方案将优化交叉验证分数,同时最大限度地减少特征的数量;我们通过使用多目标适应度函数来实现这一点。
2.Python 代码:
在这个实验中,我将使用一个分类数据集。尽管如此,我还是要添加随机噪声作为新的“垃圾特征”,这对模型没有用,并且增加了更多的复杂性。我希望模型能移除它们,可能还会移除一些原件。因此,第一步是导入数据并创建这些新特征:
数据设置。作者代码。
从前面的代码中,您可以看到有九个特性、四个原始特性和五个虚拟特性;我们可以绘制它们来检查它们与我们想要预测的“y”变量的关系。每种颜色代表一个类别。

数据分发。图片由作者提供。
我们可以看到,原始特征有助于区分每一类的观察值,每一类都有一个分隔它们的边界。尽管如此,新功能(虚拟)并没有增加价值,因为它们不能像预期的那样“分割”每个类别的数据。
现在,我们将把数据分成训练、测试和导入我们想要用来选择特征的基本模型,在这种情况下,是决策树。
数据分割和基本模型。作者代码。
下一步,让我们导入并拟合特征选择模型;如前所述,它使用进化算法来选择特征;它通过优化交叉验证分数来使用多目标函数,同时还最小化所使用的特征的数量。
一定要安装 sklearn-genetic-opt
pip install sklearn-genetic-opt
特征选择模式。作者代码。
我们将模型设置为基于交叉验证准确性来选择特征,但是任何 scikit-learn 度量都是可用的,我们还将 cv 策略设置为分层 k 倍。
当您开始拟合模型时,屏幕上会显示一个日志以查看优化进度,这里有一个它的样本,在这种情况下,“适应度”相当于跨代(行)的平均交叉验证分数,我们还会得到标准差、最大值和最小值。

培训日志。图片由作者提供。
模型完成后,我们可以使用 best_features_ property 检查它选择了哪些变量,它将获得一个 bools 数组,其中 true 表示选择了该索引处的特性。
evolved_estimator.best_features_[False True True True False False False False False]
在这次特别的运行中,该模型获得了大约 97%的准确率。它选择特征 2、3 和 4,它们是原始信息特征的一部分;它会丢弃第一个特征的所有伪特征。
如果你想了解这个包的更多信息,请告诉我,你也可以在这里查看文档和源代码:
https://sklearn-genetic-opt.readthedocs.io/en/stable/ https://github.com/rodrigo-arenas/Sklearn-genetic-opt
进化策略:理论实施指南

Sebastian Unrau 在 Unsplash 上的照片
在这里,我将谈谈 OpenAI 在 本文 中描述的进化策略背后的直觉和一些统计数据。
如果你想要一些代码, 这里就是 !
优化简介
在传统的优化设置中,目标是找到某个函数的全局最小值。例如,函数 f(x) = x 的最小值在 x = 0 处:

图片作者:f(x) = x 的图形很明显,当 f(x) = 0 时,这个函数的极小点在 x = 0 处
这个例子的解决方案相当明显,但是这些函数可以变得更加复杂,并且存在于更高维度中,在那里解决方案不是如此微不足道。传统优化方法背后的思想是使用函数的梯度来尝试“滑下斜坡”,直到到达底部。可以想象,这些算法都要求函数的梯度是明确定义的;事实上,一些像牛顿-拉夫森方法甚至需要二阶导数信息!通常,这些计算起来非常困难或耗时。有没有一种不需要这些额外信息就能优化函数的方法?
ES 背后的一些直觉
ES 是另一种优化方法。将这种方法与更传统的优化算法分开的是缺少关于策略所需的函数本身的信息,es 根本不需要直接的梯度信息!这意味着我们可以将它应用于更抽象的优化问题,在这些问题中,被优化的函数在某种程度上是一个黑盒。
那么这实际上是如何工作的呢?假设有一个函数我们想要针对某些参数进行优化。这是一种迭代算法,其中我们不断更新由参数给出的位置,以有希望地收敛到一个最佳点(在这种情况下,算法优化最大值而不是最小值)。从一个开始位置,我们随机采样点,并计算这些点的函数值,以收集关于函数本身的信息。利用这些信息,我们可以更新自己,朝着上升的方向前进!基本上,我们在从采样点计算出更多大值的方向上移动一段距离。
方程式
让我们简单地浏览一下更新方程,看看发生了什么,我们想要优化一些函数, F 关于一些参数, θ :

图片作者:ES 更新方程式
在每个时间步 t ,我们更新上面定义的参数。这里最重要的特征是得到上升方向的和项。这里,我们计算围绕当前位置随机采样的 n 个点的总体的加权和 θ_t ,其中权重是这些点的函数值。采样是通过高斯分布完成的:

作者图片:高斯分布样本
使得更新等式中的 σ 表示该噪声的标准偏差。最后, α 代表学习率,控制我们在求和项确定的方向上行进多远。
先说统计!
这里的目标是更深入地理解方程,并预测算法的一些行为。这里又是等式,所以你不需要向上滚动来查阅😊

图片作者:ES 更新方程式
一、 θ_{t+1} 的分布是什么样子的?不幸的是,这取决于被优化的函数 F 。然而,如果我们假设总和中的项无论如何都具有有限方差,那么该方程遵守中心极限定理,并且我们可以假设当 n 增加到无穷大时是高斯分布!
在这种情况下,我们可以简单地得到期望和方差的项,并对分布感到相当有信心(注意:我们也假设独立抽样)。


作者形象:转型的期待与变异
你可能想知道这一切有什么意义。这些方程似乎没什么用,而且函数本身也有很多不确定性。然而,这也让我们对超参数调整和算法行为有了一些直觉:
- 较大的 n 不会对下一个状态的期望值产生影响,但会减少该期望值周围的方差,从而带来更大的稳定性。这可以解释为增加了搜索的信息密度,尽管这样做的代价是增加了计算时间。
- 增加 α 推动我们沿着上升方向走得更远,这可以允许更快的收敛。然而,它也增加了更新的方差,导致最优解附近的更大抖动。如果太大,甚至会导致算法发散时不稳定。
- 改变 σ 不太确定,因为它被输入到 F 。然而,直觉上它增加了搜索空间,这允许算法逃脱局部最大值陷阱。这是以降低信息密度为代价的,因此增加 σ 可能会受益于 n 的增加。
实验
为了简单起见,让我们在测试中使用函数, f(x) = -x 。作为参考,下面是学习曲线的大致情况:

图片作者:ES f(x)=-x 的学习曲线
该算法有两个阶段,过渡状态和稳定状态,在过渡状态下,算法移向解,在稳定状态下,算法已经到达解。理想情况下,我们希望过渡状态尽可能短,同时在稳定状态下具有最小方差。让我们看看当我们改变超参数时,这些指标是如何变化的。

图片由作者提供:收敛时间和变化的稳态标准偏差 α
正如所料,增加学习率 α ,以增加稳态标准偏差为代价,减少了收敛时间。由于收敛时间似乎趋于平稳,增加 α 超过一个点将导致收敛时间仅有微小的改善,代价是稳态性能下降。

作者图片:收敛时间和稳态标准偏差,用于改变 σ
增加采样噪声标准偏差, σ ,会增加收敛时间。这是预料之中的,因为每次更新的信息密度较小,这也反映在总体增加的稳态标准偏差中;尽管在非常小的 σ 下偏差减小是出乎意料的。

作者图片:收敛时间和稳态标准偏差,用于改变 n
增加人口规模, n ,也符合预期。减少收敛时间和稳态标准偏差与增加信息密度的想法一致,尽管与其他超参数的变化相比,这种影响很快达到平稳状态。
ES 实现起来非常简单,并且可以产生很好的效果。下次你需要优化一个函数(提示:机器学习👀),记住这个工具!
我希望你觉得这篇文章很有见地,并让我知道你的想法😁
使用 Python 进化机器人行走
让机器人教会自己走路
机器人行走是一项复杂的任务,要将运动的每一种可能性都写入一个程序中。随着马达和肢体的增加,在平衡底盘的同时必须理解硬件运动变得越来越困难。如果机器人能自己编代码走路不是更简单吗?这可以通过使用遗传算法和强化学习来实现。我们将使用 Python 开发一系列伺服电机,以一种创造行走运动的方式移动。
遗传算法
遗传算法(GAs)是蛮力和贪婪方法的替代品。他们使用进化理论,通过几代人来评估一个基因型的突变。思考这个问题的一个简单方法是,用一系列随机生成的步骤来制作一架纸飞机(我们的基因型),然后执行这些步骤,看看飞机能飞多远。然后你改变一些步骤(变异)并重新制作飞机。如果这种改变允许飞机飞得更远,那么它就比上次更适合,如果不是,那么我们就放弃它,保留原来的。这种情况会重复一定数量的世代,或者直到满足某个条件。
遗传算法可以以不同的方式执行,例如使用简单的爬山算法,其中它突变一种基因型并在每一代评估适合度。这也可能是利用微生物气体,让一群基因型在比赛中相互竞争。根据任务选择不同的算法。我们将在这个项目中使用微生物算法。
算法 1 微生物
算法要求:人口
current 1←人口[random nt()]
current 2←人口[random nt()]
性能操作(当前 1)适合度 1←适合度()
性能操作(当前 2)适合度 2←适合度()
如果合适 1 >不合适 2 则
人口。AtIndex(f itness2)←f itness1
其他
人口。AtIndex(f itness1)←f itness2
结束 if
神经网络遗传优化
代理是环境中执行动作的实体。使用神经网络可以根据某种形式的输入做出决策,这种输入告诉智能体它自己的环境状态。这方面的一个例子是游戏 Flappy Bird 自己玩。简单地通过遗传算法向上或向下进化不会帮助智能体在不断变化的环境中做出决策。本报告中使用的优化方法是一个不断进化权重和偏差的神经网络,以最佳地执行任务。
下面的文章对 RL 做了更详细的解释:
https://www.guru99.com/reinforcement-learning-tutorial.html
方法
在本节中,我们将介绍所需的器件,并概述我们为硬件做出的选择。你不必遵循我们的设计。
零件清单:
一个控制器如 树莓派
伺服电机控制器如Adafruit 16-伺服帽
伺服电机 (我们将用 4)
伺服硬件组成 两足动物底盘
倾斜传感器如 MPU 6050
距离传感器如 HC-SR04
两个 脂电池
对于硬件的一体化套件,我们建议看看 Eleego 企鹅机器人。我们的设计基于 Bob Biped 底盘,但是,我们使用铝型材来构建它。如果你使用下面链接的底盘,树莓派 Zero 更便宜,更小,更适合 Elegoo Penguin 机器人。我们用的是树莓 Pi 型号 b。
注意:这个硬件使用微伺服系统,而不是我们使用的伺服系统。确保获得与这些设备上的电流相匹配的电池。我们推荐 4 节可充电 AA 电池用于 4 个微型伺服系统。
值得投资一个 Raspberry Pi 原型帽,您可以在其上焊接传感器并安排您的引脚排列。或者,你可以使用内置陀螺仪的传感帽,但你仍然需要连接距离传感器和电池。

图 1:我们的两足动物底盘。图片由作者提供。
选择电池
伺服系统的电池需要在激活时为所有伺服系统提供足够的电流。伺服系统有两个我们需要关注的工作电流,一个是空闲电流,一个是活动电流。当伺服系统没有被命令时,空闲是牵引,而当伺服系统移动时,激活是牵引。最坏的情况是所有的伺服系统同时移动。经验法则是用有效电流乘以伺服系统的数量。你需要一个能提供合成电流的电池。
对我们来说是:
4*500 = 2000 毫安
我们将需要一个至少能提供 2000 毫安的电池,我们稍微检查了一下,购买了一个 3700 毫安和 3.7V 的电池。Raspberry Pi 对电池有自己的要求,应该与硬件电池分开,以防止电池产生过多的“噪声”。我们为 Pi 使用了 3000 毫安的 3.7V 电池。建议您使用 Lipo 电池,因为它们更安全,并且可以充电(节省您的电池费用)。
对于圆周率电池,我们使用了一个电池帽,它也来自圆周率零形式。这使你可以充电,而不必从机器人身上取出来。
布线
接线的格式如下:
传感器引脚= Raspberry Pi GPIO 引脚
buzzer = 23
#Distance sensor
Trigger pin = 16
Echo pin = 12
#MPU tilt sensor
SDA = SDA
SCL = SCL
传感器上的所有接地和 5v 引脚都连接到 Raspberry Pi 引脚。如果传感器没有出现,您可以使用 Sudo 命令:
sudo i2cdetect -y
您应该会看到一个 0x68 设备,这是您的倾斜传感器。超声波测距仪可以通过使用 Bluetin_Echo 库进行测试。如果值不符合预期,则检查您的接线。如果问题仍然存在,很可能是传感器损坏。
图书馆
我们将混合使用硬件库(仅用于 Raspberry Pi)和机器学习库。您需要以特定的方式安装 PyTorch 才能使其工作。其余的可以通过 pip 完成。
import numpy as np
import torch
import random
import copy
import matplotlib.pyplot as plt
#libraries for the biped
from adafruit_servokit import ServoKit
from mpu6050 import mpu6050 as MPU
from Bluetin_Echo import Echo
import RPi.GPIO as GPIO
import time
神经网络的使用
就神经网络参数而言,你可以按照你希望的任何方式来设计你的代理。通过实验,我们发现在我们使用的样本中,2 层(每层 5 个节点)是表现最好的架构。对于输入层,它由当前伺服位置以及回转仪数据中的 x、y 和 z 位置组成。这然后被用来预测下一个伺服电机位置。

图 2:作者用 draw.io. Image 设计的图。
我们将输出编码为 1、0 或-1,表示向前移动 x 度、不移动或向后移动 x 度。我们的 x 将在每个伺服系统上旋转 30 度,以产生足够的移动而不会移动太多。
class Agent:
def __init__(self, num_input, num_hiddenLayer, num_hiddenLayer2, num_output):
self.num_input = num_input #set input number
self.num_output = num_output #set ooutput number
self.num_genes = (num_input * num_hiddenLayer) + (num_output) + (num_hiddenLayer * num_hiddenLayer2) + (num_hiddenLayer2*num_output)
self.num_hidden=num_hiddenLayer
self.num_hidden2=num_hiddenLayer2
self.weights = None
self.weights2=None
self.weights3=None
self.bias = None
def set_genes(self, gene):
weight_idxs = self.num_input * self.num_hidden #size of weights to hidden
weights2_idxs = self.num_hidden * self.num_hidden2 + weight_idxs #size and position
weights3_idxs = self.num_hidden2 * self.num_output + weights2_idxs + #weight_idxs #size and position
bias_idxs = weight_idxs + weights2_idxs + self.num_output #sizes of biases
w = gene[0 : weight_idxs].reshape(self.num_hidden, self.num_input) #merge genes
w2 = gene[weight_idxs : weights2_idxs].reshape(self.num_hidden2, self.num_hidden) #merge genes
w3 = gene[weights2_idxs: weights3_idxs].reshape(self.num_output, self.num_hidden2)
b = gene[weights3_idxs: bias_idxs].reshape(self.num_output,) #merge genes
self.weights = torch.from_numpy(w) #assign weights
self.weights2 = torch.from_numpy(w2) #assign weights
self.weights3 = torch.from_numpy(w3) #assign weights
self.bias = torch.from_numpy(b) #assign biases
def forward(self, x):
x = torch.from_numpy(x).unsqueeze(0)
x=torch.mm(x, self.weights.T) #first layer
x =torch.mm(x,self.weights2.T) #second layer
return torch.mm(x,self.weights3.T) + self.bias #third layer
def get_action(self, x):
items=list(self.forward(x)[0]) #get predictions
arr=[]
for i in items:
if i>0.1: #if over 0.1 its on
arr.append(1)
elif i<=-0.1: #if in lower than -0.1 its off
arr.append(-1)
else: #if in middle bracket then
arr.append(0)
return arr
设置
我们将首先设置一个包含约束的伺服对象。这将阻止机器人移动伺服系统相互碰撞并造成损坏。校准的方法是通过手动移动你的伺服系统,并在潜在的损坏之前读取什么角度是极限。我们还想找到每个伺服机构的静止位置,以使机器人站立。这将被保存为起始位置。
class servoMotor:
def __init__(self,servoObj,start,Min,Max): #initialize with all values and constraints
self.servo=servoObj
self.min=Min
self.max=Max
self.start=start
def move(self,angle): #only move within a set space to avoid damage
current=self.servo.angle
if current+angle>=self.min and current+angle<=self.max:
self.servo.angle=current+angle #increase by only if within constraint
def startPos(self): #set servo to the start position (which will be standing)
self.servo.angle=self.start
接下来,我们将需要定义所有必要的数据,如基因型、伺服电机和算法中使用的其他参数。
"""
define needed variables
"""
num_obs = 7 # depends on your environment
num_actions = 4 # depends on your environment
epochs, pop_size, mutation_std = 150, 15, 0.02 #define data for training
#create out agent
agent = Agent(num_obs, 5, 5, num_actions)
# Create our gene population
gene_pop = []
for i in range(10): #vary from 10 to 20 depending on purpose of robot
gene_pop.append(np.random.normal(0, 0.1, (agent.num_genes)))#create
GPIO.setmode(GPIO.BCM)
NumServos=8
GPIO.setwarnings(False)
buzzer=23
#set up sensors
sensor = MPU(0x68)
sonar=Echo(16,12,315) #trigger pin, echo pin, speed
#set up outputs
kit = ServoKit(channels=16)
#lcd = lcddriver.lcd()
GPIO.setup(23,GPIO.OUT) #buzzer on 23
servos=[] #attach all the servo motor objects
servos.append(servoMotor(kit.servo[0],90,40,130))
servos.append(servoMotor(kit.servo[1],130,20,10))
servos.append(servoMotor(kit.servo[2],100,0,180))
servos.append(servoMotor(kit.servo[3],30,0,80))
您将需要更改伺服系统的参数,以匹配您在硬件中安装它们的方式。这些伺服系统连接到伺服帽的位置 0,1,2,3。这在以下文件中声明:
kit.servo[pin]
GA 的使用
我们将使用初始种群为 20 个基因的微生物算法。这将提供足够的变化,以帮助找到更好的解决方案,但足够小,以最终收敛于解决方案。
健身
健康将通过几个属性来评估。运动后获得的距离、末端倾斜位置和运动的多样性。获得的距离是在执行指令的开始和结束时从超声波测距仪收集的。开始-结束应为正值,否则将返回 0 适合度。如果终点位置没有站直,这个距离会被扣分。这将防止机器人到处倾斜。运动的多样性奖励创造大量运动而不是在最后一刻缓慢移动或跳跃的基因型。这有助于进化出能够行走的基因型。
def readDist():
return int(sonar.read("mm",15)) #read the distance from ultrasound range finder
def readAcc():
while True:
try:
d=sensor.get_accel_data() #read the accelerometer
return float(d["x"]),float(d["y"]),float(d["z"]) #return values from gryoscopic data
except:
pass #return nothing if not plugged in
def withinBoundary(num,value,minus,plus): #whether or not a number is withi a value bounds
if num>=value-minus and num<=value+plus: #get boundaries
return True
return False
def isReady():
#check accelerometer values are within boundaries
x,y,z=readAcc()#get gyroscope values
if withinBoundary(x,-9.5,2,2) and withinBoundary(y,0.2,2,2): #get within bounds of standing
return True
return False
def getDiverseScore(positions):
#given all the positions predicted, reward those with many changes
counter=0
for i,pos in enumerate(positions[1:]): #loop through positions
for j in range(len(positions[0])): #loop through each
if positions[i][j]!=pos[j]:
counter+=1 #increase if different to last
return counter
def fitness(startDist,positions):
#get the fitness of the bots current position
distance=readDist()
diversityScore=getDiverseScore(positions) #gather a diversity of movement score
x,y,z=readAcc()
#get the distance score
#get the gyro score
#combine scores
penalty=0
if not withinBoundary(x,-9.5,2,2): #assign penalties for data not within boundary
penalty=abs(max(-9.5,x)-min(-9.5,x))
if not withinBoundary(y,0.2,2,2):
penalty=abs(max(0.2,x)-min(0.2,x))
if isReady(): #if in a near position
if startDist-distance>0 and startDist-distance-penalty>0:
return (startDist-distance-penalty)+diversityScore
return 0
突变
变异是通过正态分布增加/减少权重和偏差来完成的。这被限制在 4 到-4 之间,因此权重不会变得太大/太小而支配网络。在实验中,使用 20%的标准差效果很好。
def mutation(gene, mean=0, std=0.1):
gene = gene + np.random.normal(mean, std, size=gene.shape) #mutate the gene via normal
# constraint
gene[gene > 4] = 4
gene[gene < -4] = -4
return gene
主 GA 循环
选择 150 个时代/代是因为虽然这需要很长时间,但它应该在这段时间内收敛到一个好的解决方案。每一代有 20 个动作,因此必须快速进化行走模式。
def output_step(servos,motorGenes): #move servos by given amounts
for i,gene in enumerate(motorGenes):
if gene==1:
servos[i].move(30) #move by 30 degrees
elif gene==-1:
servos[i].move(-30) #move by -30 degrees
主循环将组合所有的代码,并产生一个适合度数组。您可以将这些拟合度保存到文本文件中,以便进行绘图。
prev_fitness = [0]
fitnesses=[0]
fittest=[0,0]
storedFav=-1
print("initial reading",readDist())
# Main loop performing Microbal GA
behaviour=[[0],[0],[0],[0]]
for epoch in range(epochs):
print("Generation:",epoch)
for i in servos:
i.startPos()
while isReady()==False: GPIO.output(buzzer,GPIO.HIGH) #wait for ready
GPIO.output(buzzer,GPIO.LOW)
time.sleep(2)
startDist=readDist() #get sensor reading
n1=random.randint(0,len(gene_pop)-1) #get random gene
g1=copy.deepcopy(gene_pop[n1])
g1=mutation(g1,std=0.2) #mutate this random gene
positions=agent.set_genes(g1) #set the genes
currentMotors=[servos[i].servo.angle for i in range(len(servos))] #set up current angles
gathered=[]
for i in range(20): #20 steps to get it right
positions=agent.get_action(np.array(currentMotors+list(readAcc()))) #get random gene
gathered.append(positions.copy())
currentMotors=[servos[i].servo.angle for i in range(len(servos))] #set up current angles
output_step(servos,positions) ######output steps
time.sleep(0.3)
if not isReady(): break #break if not standing up
g1_fit = fitness(startDist,gathered) #gather the fitness
for i in servos: #reset to start positions
i.startPos()
while isReady()==False: GPIO.output(buzzer,GPIO.HIGH) #wait for ready
GPIO.output(buzzer,GPIO.LOW)
time.sleep(2)
startDist=readDist() #get sensor reading
n2=random.randint(0,len(gene_pop)-1) #select a random gene
g2=copy.deepcopy(gene_pop[n2])
g2=mutation(g2,std=0.2) #mutate this random gene
positions=agent.set_genes(g2) #set the genes
currentMotors=[servos[i].servo.angle for i in range(len(servos))] #set up current angles
gathered=[]
for i in range(20): #20 steps to get it right
positions=agent.get_action(np.array(currentMotors+list(readAcc()))) #get random gene
gathered.append(positions.copy())
currentMotors=[servos[i].servo.angle for i in range(len(servos))] #set up current angles
output_step(servos,positions) ######output steps
time.sleep(0.3)
if not isReady(): break
g2_fit = fitness(startDist,gathered)
if g1_fit>g2_fit: gene_pop[n2]=copy.deepcopy(gene_pop[n1]) #copy over if fitter
else: gene_pop[n1]=copy.deepcopy(gene_pop[n2]) #copy over if not fitter
fitnesses.append(max([g1_fit,g2_fit])) #save best fitness out of two
print(fitnesses)
结果

图 3:两层网络的结果。图片由作者提供。

图 4:三层网络的结果。图片由作者提供。
如图所示,经过 3 次试验,我们发现平均而言,2 层网络优于 3 层网络。
结论
在本文中,我们开发了一种算法,可以让机器人进化出行走的动作。我们已经使用了一个 4 伺服两足动物的例子,但这可以使用一个完全不同的设计与更多的伺服,如人形两足动物。
进一步的实验可能包括添加更多的伺服系统,更多的传感器,或使用不同的遗传优化方法。这篇文章为你提供了发展机器人技术的基础。
附录
2 层代理代码可在此处找到:
https://raw . githubusercontent . com/shepai/Biped/main/robot % 20 code % 20 deep . py
可以找到更多的实验和代码:
https://github.com/shepai/Biped
更多类似的项目可以在我们的网站上找到:
JAX 进化中的神经网络
实践教程
用vmap和jit的功率缩放 CMA-ES

"那么,我为什么要从<insert-autodiff-library>转到 JAX 呢?"。谈论新电影《街区小子》时,第一个典型的消极攻击性问题。以下是我的答案:JAX 不仅仅是一个自动分化的快速库。如果你的科学计算项目想从 XLA、JIT 编译和批量数组编程范例中获益,那么 JAX 提供了一个极好的 API。虽然 PyTorch 依赖预编译内核和快速 C++代码来实现最常见的深度学习应用,但 JAX 允许我们利用高级接口来编写您最喜欢的加速器。vmap、pmap、jit跨阵列维度/计算设备加速和向量化,而无需处理进程的异步簿记。但是这不限于标准的基于梯度的优化设置。它也适用于许多进化方法。因此,在本帖中,我们将探讨 JAX 如何推动下一代可扩展神经进化算法:
- 我们将介绍协方差矩阵适应进化策略(CMA-ES,例如 Hansen,2016 ),并讨论“维数灾难”和高维协方差矩阵的统计估计等挑战。
- 我们将在 JAX 实施 CMA-ES 更新方程。我们将展示如何最大限度地利用
vmap并对专家系统的两个关键维度进行矢量化:世代群体规模和每个群体成员的适应性评估数量。 - 我们将使用 CMA-ES 发展一种前馈策略来平衡钟摆。之后,我们探索了不同的超参数(神经网络规模、平均学习速率和选择度),以便对专家系统中的关键权衡有更好的直觉。
- 最后,我们将分析 CMA-ES 生成迭代和跨不同硬件平台的运行和编译时间。我们将看到
vmap的 XLA 编译和矢量化在不同平台(CPU/不同 GPU)上平滑扩展。
TL;博士 : JAX 在扩展神经进化算法方面令人敬畏。我们可以
vmap对所有群体成员的参数化和他们的随机适应性评估。通过消除多处理/MPI 通信诡计,我们可以在现代加速器(GPU/TPU)上运行神经进化实验,并且几乎没有工程开销。如果你想知道 CMA-ES 是什么样子,那就来一趟吧。
注意:在整篇文章中,我们假设你已经知道 JAX 的基本元素,比如jit、vmap和lax.scan。如果你觉得你需要补上这些,查看一下 JAX 快速入门指南或者我的 JAX 介绍博客文章。
让我们从安装和导入几个包开始。
try:
import jax
except:
!pip install --upgrade jax
!pip install --upgrade jaxlib%matplotlib inline
%load_ext autoreload
%autoreload 2
%config InlineBackend.figure_format = 'retina'
import jax
import jax.numpy as jnp
from jax import jit, vmap, lax
import functools, time
import matplotlib.pyplot as plt
进化策略和提问-评估-回答 API
进化策略的目标是在不使用显式解析导数或梯度估计的情况下最小化黑箱函数。相反,他们依赖于感兴趣的(潜在的噪声)函数的随机评估。先前积累的信息然后被巧妙地整合以通知搜索分布 π 。第个连续批次的建议方案 x 也称为一代个体 g 。在神经进化中, x 对应于神经网络的参数,而 f 表示某种性能度量。重要的是,我们不依赖于反向投影和产生的梯度,目标不必是平滑的和可微分的。这有几个实际影响:
- 我们有更多的自由来设计神经网络。大多数常见的深度学习构建模块,如卷积层、线性层和注意力层,都是在我们的脑海中以梯度下降的方式设计的。但是,如果我们不需要梯度来学习,我们可以使用不可微的非线性,如尖峰和基于阈值的激活。
- 我们不必精心手工制作目标函数,它支持梯度下降所规定的学习动力学。而是我们可以直接优化感兴趣的函数。例如,在 RL 中,我们可以直接以最大化剧集回报为目标,而不需要诸如均方贝尔曼误差之类的替代目标。
- ES 不是优化解参数的单点估计,而是保持解的搜索分布。这种客观景观的替代物可以用来对解决方案进行采样,这些解决方案在解决任务的方式上可能是不同的。例如,这种异质性可以用于集合预测。
ES 可以被认为是搜索算法,它使用一种记忆或者目标的代理模型。替代项 π 的具体形式因 ES 而异(例如,对于 CMA-ES,它将是缩放的多元高斯函数)。一般程序有三个重复步骤:

- 问:给定当前的代理模型,我们“问”出一组评估候选或者新的一代,x~π()。
- 评估:我们使用目标 f 评估提议的代中每个成员 x 的适合度,这返回一个“效用”标量。对于随机目标,我们可能需要多次这样做,以获得可靠的适合度估计。
- 告诉:接下来我们用 (x,f(x)) 更新模型θ→θ’。搜索分布将被调整以增加表现良好的候选人的可能性。这次更新详细说明了如何应对勘探开发的权衡。
我们迭代这三个步骤,得到一组世代 g = 1,…,G 。这三步程序通常在超参数优化工具箱(如[scikit-optimize](https://scikit-optimize.github.io/stable/auto_examples/parallel-optimization.html#sphx-glr-auto-examples-parallel-optimization-py))中实现,并提供了与专家系统接口的最小 API(如参见 David Ha 的博客)。
CMA-ES 及其更新规则
……以及如何在 JAX 实施
在 CMA-ES 中,搜索分布 π 是具有均值 m 和缩放协方差矩阵σ C 的多元高斯分布。更新步骤调整高斯和步长标准偏差σ的均值和协方差。不严格地说,平均值将被拉向最佳执行候选的方向,而协方差更新将旨在将采样分布的密度轮廓与目标的等高线对齐,从而与最陡下降的方向对齐。这听起来似乎合理——但是高效搜索算法的理想特征是什么呢?它应该在广泛的函数范围内通用,并且对输入和输出的各种变换都是健壮的。这种鲁棒性元目标可以在不变性属性的概念下进行重铸,我们将看到其中几个应用于 CMA-ES 的解决方案质量:
- 通过“尺度无关”的秩截断选择对适应度函数的保序变换的不变性。例如,如果我们向目标函数添加一个常数,解决方案不应改变。
- 如果初始搜索点被相应地变换,则对搜索空间的保角变换的不变性。
现在让我们看看 CMA-ES 是如何实现这些元目标的,并研究 CMA-ES 的各个更新方程以及如何在 JAX 实施它们。我自由地编写了一些助手函数,这些函数对于我们理解 JAX 或 CMA-ES 并不重要。这包括我们如何初始化和终止策略以及一些日志记录工具。请随意检查链接存储库中的源文件。
from helpers.es_helpers import (init_cma_es,
eigen_decomposition,
check_termination,
init_logger,
update_logger)
from helpers.viz_helpers import plot_fitness, plot_sigma
问答界面
第一步是定义 CMA-ES 的核心功能以及我们与搜索算法交互的方式:通过从搜索分布中向请求一组候选建议,然后评估这些候选建议。只有在之后,我们才能用评估期间收集的信息更新战略。在 CMA-ES 中,ask-step 是从一个多元高斯样本中选取的,其中充分的统计量定义了搜索的方向。直觉上,平均值应该更接近表现最佳的候选者,而协方差应该将我们的搜索指向最陡下降的方向。为了有效地从这个潜在的高维高斯样本中进行采样,我们将使用 重新参数化技巧 :

C 的本征分解将协方差分解为 B 和 D 。 B 是一个正交矩阵,它的列构成了特征向量的标准正交基。 D 是 C 对应特征值的平方根对角矩阵。直观地说, D 缩放球形‘基本’高斯分布,并可视为维度特定的步长矩阵。对角线条目对应于我们的优化问题的各个\(d\)维度的标准偏差。因此,它控制着搜索分布沿着特定的轴“延伸”多远。另一方面, B 定义了这些主轴的方向。简而言之: D 音阶, B 定向。最后,σ是一个与尺寸无关的步长。在代码中,如下所示:
def ask(rng, params, memory):
""" Propose parameters to evaluate next. """
C, B, D = eigen_decomposition(memory["C"], memory["B"], memory["D"])
x = sample(rng, memory, B, D, params["n_dim"], params["pop_size"])
memory["C"], memory["B"], memory["D"] = C, B, D
return x, memory
@functools.partial(jit, static_argnums=(4, 5))
def sample(rng, memory, B, D, n_dim, pop_size):
""" Jittable Multivariate Gaussian Sample Helper. """
z = jax.random.normal(rng, (n_dim, pop_size)) # ~ N(0, I)
y = B.dot(jnp.diag(D)).dot(z) # ~ N(0, C)
y = jnp.swapaxes(y, 1, 0)
x = memory["mean"] + memory["sigma"] * y # ~ N(m, σ^2 C)
return x
memory存储所有暴露于来自 es 的更新的变量。另一方面,params是固定的超参数,例如不同的学习规则或群体规模。我们稍后将重用协方差的特征分解,以便我们可以通过将 C,B,D 存储在我们的记忆字典中来缓冲计算。从一个ask函数调用中获得一组λ个候选函数后,我们可以使用目标函数来评估它们的fitness。
一个 CMA-ES 代的更新由 5 个顺序更新方程 : m ,p_σ,σ,p_c,和 C 更新组成。这些是存储在memory中的。由此产生的动态规定了我们的搜索分布如何在连续几代中演变。示意性地,这看起来如下:

等等!但是 p_σ 和 p_c 从何而来?从单个世代中对 C 进行经验估计是困难的,尤其是当参数的数量大于群体规模d>N时(进化神经网络时通常会出现这种情况)。因此,我们希望通过利用前几代人积累的信息来获得可靠的估计。这就是不同进化路径发挥作用的地方。他们的角色是跟踪均值统计的变化,以及不同的更新如何影响下一代的性能。 p_c 然后用于通知总体方差的各向异性部分的更新(so C )和用于更新各向同性部分的p _ C(soσ)。先不要担心方程,我们很快就会看到所有这些是如何详细工作的。现在只要记住 p_c 和 p_σ 提供了一个整合了过去更新的内存跟踪。下面的函数包含了所有五个更新步骤以及呼叫中解决方案的初始排序:
def tell_cma_strategy(x, fitness, params, memory):
""" Update the surrogate ES model. """
# Update/increase the generation counter
memory["generation"] = memory["generation"] + 1
# Sort new results, extract parents, store best performer
concat_p_f = jnp.hstack([jnp.expand_dims(fitness, 1), x])
sorted_solutions = concat_p_f[concat_p_f[:, 0].argsort()]
# Update mean, isotropic path, stepsize, anisotropic path, cov.
mean, y_k, y_w = update_mean(sorted_solutions, params, memory)
memory["mean"] = mean
p_sigma, C_2, C, B, D = update_p_sigma(y_w, params, memory)
memory["p_sigma"], memory["C"], memory["B"], memory["D"] = p_sigma, C, B, D
sigma, norm_p_sigma = update_sigma(params, memory)
memory["sigma"] = sigma
p_c, ind_sigma = update_p_c(y_w, norm_p_sigma, params, memory)
memory["p_c"] = p_c
C = update_covariance(y_k, ind_sigma, C_2, params, memory)
memory["C"] = C
return memory
# JIT-compiled version for tell interface
tell = jit(tell_cma_strategy)
让我们仔细看看各个更新步骤,以及它们在 JAX 是如何实施的:
更新 1:通过截断选择和重新加权进行均值更新
我们从平均值开始,它依赖于截断选择。不是让所有候选人平等地拉动均值更新,我们将只考虑群体中表现最好的人(或父母μ)的子集来影响 m 的更新。通常父母的集合被选择为整个人口的大约 50%。随着父母在群体中的排名降低,每个父母的权重也降低。

其中 x_:λ 表示世代 g 的适应度排序候选,而 c_m 表示均值更新的学习速率。权重 w_i 通常被选择为递减的,使得表现最好的解决方案被给予更大的影响。这是群体大小 λ = 100 和 μ = 50 的默认情况:

在下面的代码中,我们还定义了 z 得分参数以及所选的均值归一化父参数的加权和。update_mean返回更新后的平均值以及 y_k 和 y_w 。这些将在协方差更新步骤中重复使用。
def update_mean(sorted_solutions, params, memory):
""" Update mean of strategy. """
x_k = sorted_solutions[:, 1:] # ~ N(m, σ^2 C)
y_k_temp = (x_k - memory["mean"]) # ~ N(0, σ^2 C)
y_w_temp = jnp.sum(y_k_temp.T * params["weights_truncated"], axis=1)
mean = memory["mean"] + params["c_m"] * y_w_temp
# Comple z-scoring for later updates
y_k = y_k_temp / memory["sigma"]
y_w = y_w_temp / memory["sigma"]
return mean, y_k, y_w
更新 2:各向同性进化路径更新
在接下来的两步中,我们将推导协方差矩阵各向同性部分的更新,也称为“步长”σ的缩放。CMA-ES 使用一个进化路径 p_σ ,它集成了前面的步骤来执行累积步长自适应:

def update_p_sigma(y_w, params, memory):
""" Update evolution path for covariance matrix. """
C, B, D = eigen_decomposition(memory["C"], memory["B"], memory["D"])
C_2 = B.dot(jnp.diag(1 / D)).dot(B.T) # C^(-1/2) = B D^(-1) B^T
p_sigma_new = (1 - params["c_sigma"]) * memory["p_sigma"] + jnp.sqrt(
(1 - (1 - params["c_sigma"])**2) *
params["mu_eff"]) * C_2.dot(y_w)
_B, _D = None, None
return p_sigma_new, C_2, C, _B, _D
不严格地说,这意味着在以下两种情况下调整勘探-开采权衡:
- 如果两个更新步骤是反相关的(它们指向相反的方向),那么我们在参数空间中并没有真正移动。更新来来回回,没有明确的前进方向,这表明收敛。在这种情况下,累积步长自适应将降低σ。
- 另一方面,如果步骤指向相同的方向,这将增加步长,使得搜索在共识的方向上进行得更快。直观上,这种行为类似于动量在基于梯度的优化中的工作方式。
适应的速度和整合的时间尺度取决于两个关键因素:学习率 p_σ , c_σ 和特征值 C 的大小。 c_c 越大, p_σ 响应越快,但积分时间尺度也越小。另一方面,协方差的精度提供了额外的重新标度,这种重新标度以非平凡的方式相互作用。那么我们实际上如何更新σ呢?
更新 3:累积步长自适应
步长是标量,而 p_σ是 d 维。所以我们需要减少事情。 p_σ 的范数提供了一个聚合步长的度量和一个简单的移动统计量,用于决定是增加还是减少σ。这里我们将跳过一些数学,但是可以表明 p_σ 处于期望标准正态分布。然后,如果||p_σ||偏离其期望值,我们可以使用指数比例更新:

def update_sigma(params, memory):
""" Update stepsize sigma. """
norm_p_sigma = jnp.linalg.norm(memory["p_sigma"])
sigma = (memory["sigma"] * jnp.exp((params["c_sigma"] / params["d_sigma"])
* (norm_p_sigma / params["chi_d"] - 1)))
return sigma, norm_p_sigma
注意,如果||p_σ||等于期望值,则步长不会有任何变化。d_σ ~ 1 是所谓的阻尼参数,重新标度了 ln σ的大小变化。
更新 4:各向异性进化路径更新
到目前为止一切顺利。现在我们有了一个更新的公式来计算方差的均值和各向同性部分。最后,我们需要一个估计协方差的过程。一个自然的起点可以是基于当前 x 的样本估计。但是对于参数的数量远大于父节点的数量 d > > λ的情况,这是非常不可靠的。这是维度诅咒统计挑战的另一个例子。CMA-ES 不再仅仅依赖于最新一代,而是再次使用了一个自适应过程,该过程在连续的更新步骤中利用了该结构:

乍一看,这个等式与 p_σ的各向同性路径更新非常相似。但是有两个显著的区别:
- 我们不通过协方差矩阵 C 的平方根来重新调整归一化的均值变化。因此,它仍然是一个各向异性变量。
- 更新依赖于布尔值。如果 p_σ 的范数变得太大,指示器功能会“停止”p_c 更新。当步长过小时,这可防止 C 的轴过冲。Hansen (2016)指出,当初始σ选择得太小或目标函数不稳定时,这尤其有用。
def update_p_c(y_w, norm_p_sigma, params, memory):
""" Update evolution path for sigma/stepsize. """
ind_sigma_cond_left = norm_p_sigma / jnp.sqrt(
1 - (1 - params["c_sigma"]) ** (2 * (memory["generation"] + 1)))
ind_sigma_cond_right = (1.4 + 2 / (memory["mean"].shape[0] + 1)) * params["chi_d"]
ind_sigma = 1.0 * (ind_sigma_cond_left < ind_sigma_cond_right)
p_c = (1 - params["c_c"]) * memory["p_c"] + ind_sigma * jnp.sqrt((1 -
(1 - params["c_c"])**2) * params["mu_eff"]) * y_w
return p_c, ind_sigma
更新 5:协方差自适应步骤
我们现在可以将演化路径 p_c 用于协方差矩阵自适应步骤的一部分:由外积 p_c p_c^T 给出的秩 1 更新。该更新由从最近一代评估的加权样本协方差估计构建的秩μ更新来补充:

直观上,自适应步骤的目标是增加采样 p_c 和归一化参数的机会。再次, c_1 、 c_μ 和 c_s 表示一组学习率。要更好地了解 CMA-ES 中的超参数,请查看博客末尾的概述表。总的协方差矩阵自适应步骤如下:
def update_covariance(y_k, ind_sigma, C_2, params, memory):
""" Update cov. matrix estimator using rank 1 + μ updates. """
w_io = params["weights"] * jnp.where(params["weights"] >= 0, 1,
memory["mean"].shape[0]/
(jnp.linalg.norm(C_2.dot(y_k.T), axis=0) ** 2 + 1e-20))
c_s = (1 - ind_sigma) * params["c_1"] * params["c_c"] * (2 - params["c_c"])
rank_one = jnp.outer(memory["p_c"], memory["p_c"])
rank_mu = jnp.sum(
jnp.array([w * jnp.outer(y, y) for w, y in zip(w_io, y_k)]), axis=0)
C = ((1 - params["c_1"] - params["c_mu"] * jnp.sum(params["weights"]) + c_s ) * memory["C"]
+ params["c_1"] * rank_one + params["c_mu"] * rank_mu)
return C
在 ES 中利用vmap的全部功能
现在,让我们看看如何扩展 CMA-ES 来优化一个经典强化学习任务的小型神经网络。传统上,这将涉及大量代码和涉及多处理和 OpenMPI 的通信管道。另一方面,在《JAX》中,我们将使用vmap通过以下方式处理大量的工程复杂性:1)我们将在使用lax.scan &重写健身房环境后jitRL 插曲循环。2)我们vmap通过评估片段来评估代理的健康状况。3)最后,我们还vmap在一代人内的不同提议网络上。图片显示如下:

不涉及多处理工作队列,我们可以轻松地将其扩展到 GPU 甚至 TPU 等加速器。我已经有了在 JAX 重写 OpenAI 的 [Pendulum-v0](https://github.com/openai/gym/blob/master/gym/envs/classic_control/pendulum.py) NumPy 环境的自由。对于简单的钟摆-ODE 情况,它基本上归结为用等价的jnp.<op>操作替换所有的np.<op>操作,并避免布尔的显式使用(例如,通过使用掩码代替)。此外,RL step现在将把环境变量的附加字典和环境的当前状态作为输入。这允许我们在lax.scan原语的帮助下jit整个剧集的展示。我们现在导入简单的环境助手。接下来,我们定义一个单隐藏层 MLP 策略和一个策略卷展包装器。为简单起见,我们假设策略确定性地从观察映射到行动:
from helpers.pendulum_jax import reset, step, env_params
def ffw_policy(params, obs):
""" Compute forward pass and return action from deterministic policy """
def relu_layer(W, b, x):
""" Simple ReLu layer for single sample """
return jnp.maximum(0, (jnp.dot(W, x) + b))
# Simple single hidden layer MLP: Obs -> Hidden -> Action
activations = relu_layer(params["W1"], params["b1"], obs)
mean_policy = jnp.dot(params["W2"], activations) + params["b2"]
return mean_policy
def policy_pendulum_step(state_input, tmp):
""" lax.scan compatible step transition in jax env. """
obs, state, policy_params, env_params = state_input
action = ffw_policy(policy_params, obs)
next_o, next_s, reward, done, _ = step(env_params, state, action)
carry, y = [next_o.squeeze(), next_s.squeeze(),
policy_params, env_params], [reward]
return carry, y
def pendulum_rollout(rng_input, policy_params, env_params, num_steps):
""" Rollout a pendulum episode with lax.scan. """
obs, state = reset(rng_input)
_, scan_out = jax.lax.scan(policy_pendulum_step,
[obs, state, policy_params, env_params],
[jnp.zeros(num_steps)])
# Return the sum of rewards accumulated by agent in episode rollout
return jnp.sum(jnp.array(scan_out))
最后,是 JAX 终极魔术的时候了。我们vmap在我们当前群体中的不同评估事件和所有不同神经网络的数量上。助手v_dict表示我们不同字典参数条目的第一维对应于我们想要矢量化的人口维度。之后,我们jit了vmaped 批处理卷展栏,并指出环境参数以及剧集步骤的数量是静态的:
# vmap over different MC fitness evaluations for single pop. member
batch_rollout = jit(vmap(pendulum_rollout, in_axes=(0, None, None, None),
out_axes=0), static_argnums=(3))
# vmap over different members in the population
v_dict = {"W1": 0, "b1": 0, "W2": 0, "b2": 0}
generation_rollout = jit(vmap(batch_rollout,
in_axes=(None, v_dict, None, None),
out_axes=0), static_argnums=(3))
我们需要最后一个要素:当向 CMA-ES 请求下一代的参数样本时,它将对参数的平面向量进行采样。但是评估过程需要特定于层的权重数组的字典。因此,我们需要一个助手来将扁平建议向量重新组装到 JAX 的权重和偏差的适当参数字典中。下面是一个简单的函数,它为我们的单个隐藏层 MLP 完成了这项工作:
def flat_to_network(flat_params, layer_sizes):
""" Reshape flat parameter vector to feedforward network param dict. """
pop_size = flat_params.shape[0]
W1_stop = layer_sizes[0]*layer_sizes[1]
b1_stop = W1_stop + layer_sizes[1]
W2_stop = b1_stop + (layer_sizes[1]*layer_sizes[2])
b2_stop = W2_stop + layer_sizes[2]
# Reshape params into weight/bias shapes
params = {"W1": flat_params[:, :W1_stop].reshape(pop_size,
layer_sizes[1],
layer_sizes[0]),
"b1": flat_params[:, W1_stop:b1_stop],
"W2": flat_params[:, b1_stop:W2_stop].reshape(pop_size,
layer_sizes[2],
layer_sizes[1]),
"b2": flat_params[:, W2_stop:b2_stop]}
return params
现在,我们准备好将所有东西放入钟摆任务的 CMA 搜索循环和一个具有 48 个隐藏单元的多层感知器中:我们首先初始化策略超参数、搜索分布、进化路径和跟踪策略进展的记录器。然后,我们在不同的世代迭代中运行ask - evaluate - tell循环。
# Setup the ES hyperparameters
num_generations = 200
num_evals_per_gen = 50
num_env_steps = 200
pop_size, parent_size = 100, 50
# Setup the random number gen., init ES and the logger
rng = jax.random.PRNGKey(0)
net_size = [3, 48, 1]
num_params = 3*48 + 48 + 48*1 + 1
mean_init, sigma_init = jnp.zeros(num_params), 1
params, memory = init_cma_es(mean_init, sigma_init, pop_size, parent_size)
top_k = 5
evo_logger = init_logger(top_k, num_params)WARNING:absl:No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)# Loop over different generations in evolutionary strategy
start_t = time.time()
for g in range(num_generations):
rng, rng_ask, rng_eval = jax.random.split(rng, 3)
# Ask for set of proposal param candidates and reshape
x, memory = ask(rng_ask, params, memory)
generation_params = flat_to_network(x, net_size)
rollout_keys = jax.random.split(rng_eval, num_evals_per_gen)
# Evaluate generation population on pendulum task - min cost!
population_returns = generation_rollout(rollout_keys, generation_params,
env_params, num_env_steps)
values = - population_returns.mean(axis=1)
# Tell the results and update the strategy + logger
memory = tell(x, values, params, memory)
evo_logger = update_logger(evo_logger, x, values, memory, top_k)
if (g+1) in [15, 30, 75, 200]:
jnp.save("gen_" + str(g+1) + ".npy", evo_logger["top_params"])
if (g + 1) % 15 == 0:
print("# Generations: {} | Fitness: {:.2f} | Cum. Time: {:.2f}".format(g+1, evo_logger["top_values"][0],
time.time()-start_t))
if check_termination(values, params, memory):
break# Generations: 15 | Fitness: 923.41 | Cum. Time: 9.43
# Generations: 30 | Fitness: 318.41 | Cum. Time: 13.25
# Generations: 45 | Fitness: 318.41 | Cum. Time: 16.62
# Generations: 60 | Fitness: 269.11 | Cum. Time: 20.07
# Generations: 75 | Fitness: 197.12 | Cum. Time: 23.36
# Generations: 90 | Fitness: 165.14 | Cum. Time: 26.74
# Generations: 105 | Fitness: 138.38 | Cum. Time: 30.10
# Generations: 120 | Fitness: 136.69 | Cum. Time: 33.19
# Generations: 135 | Fitness: 136.69 | Cum. Time: 36.39
# Generations: 150 | Fitness: 136.69 | Cum. Time: 39.50
# Generations: 165 | Fitness: 136.69 | Cum. Time: 42.76
# Generations: 180 | Fitness: 131.25 | Cum. Time: 45.95
# Generations: 195 | Fitness: 123.69 | Cum. Time: 49.10# Plot the results
fig, axs = plt.subplots(1, 2, figsize=(15, 4))
plot_fitness(evo_logger, title="Evolved Pendulum MLP - Performance", ylims=(90, 1600), fig=fig, ax=axs[0])
plot_sigma(evo_logger, title="Evolved Pendulum MLP - Stepsize", ylims=(0.8, 1.5), fig=fig, ax=axs[1])(<Figure size 1080x288 with 2 Axes>,
<matplotlib.axes._subplots.AxesSubplot at 0x7fe2ba8ca048>)

在上面的左图中,我们看到该策略能够解决钟摆任务(适应值约为。120),耗时不到 50 秒(在 2,7 GHz 四核英特尔酷睿 i7 芯片上)。该图显示了到目前为止找到的总体最佳性能解决方案(“前 1”)以及搜索迭代中前 5 个解决方案的平均值(“前 k 个平均值”)。我们总共经历了 200 代,每代有 100 个网络,用 200 个连续步骤评估了 50 集的每一个网络。在标准 CPU 上,在不到一分钟的时间内,总共进行了 2 亿次阶跃转换和 200 次网络采样/协方差自适应步骤。这甚至包括了jit——编译时间。如果你问我的话,我会觉得很蠢。现在让我们来看看进化轨迹不同阶段的进化行为:

CMA-ES 的超空间漫游
当周围有额外的计算机时,我喜欢做的一件事是弄清楚什么时候会出问题。对我来说,这意味着运行网格搜索,并对我们算法的“白盒”动力学建立一些直觉,这可能无法直接从更新方程中看到。我特别感兴趣的是 CMA-ES 如何扩展到更大的网络,截断选择(λ > μ)如何影响性能,以及平均学习速率(c_m)有多大的回旋余地。因此,我运行了与上面相同的配置,但是更改了各个参数。为了得到更多的统计数据,我对 10 种不同的种子重复了这个实验。在下图中,您可以看到这 3 个参数之间的性能差异(越小越好):

- 左:我们用单个隐藏层和不同的隐藏维度训练简单的前馈策略。自适应搜索后的最终累积成本随着网络容量的增加而增加。 C 的估计变得更加困难,性能下降。当在恒定的绝对截断强度下增加群体规模时,这可能变得更好。
- 中间:100 人总体的最优截断选择约为 50%。精英主义或多或少都会降低绩效。一方面,你想积极利用这一代人的最新信息。另一方面,只有 20 集的有限评估也存在一些风险。
- 我们可以选择一个相当大的平均学习率,而不会影响 CMA-ES 的性能。虽然 Hansen (2016)的教程建议将其设置为小于 1,但我们并没有看到较大学习速率的显著性能下降。这可能是因为我们的政策和目标都是确定的。
跨设备比较单代运行时
最后但同样重要的是,我们必须看看我们可以在 GPU 上扩展它有多难。在下图中,您可以找到一个运行时(左)和编译时(右)基准,用于单摆任务上的单个ask - eval - tell迭代以及不同的群体规模(和 50 个 MC 适应性评估):

测量时间是 1000 次迭代的平均值,是在三个不同的设备上获得的。在 CPU 上,我们可以看到随着群体规模的增加,每代时间也在增加。另一方面,两个 GPU 都使用简单的并行化来轻松处理增加的群体规模。但是这似乎伴随着一个小警告:增加 XLA 编译时间。我不确定这是怎么回事,但是考虑到你只需要在第一次函数调用时编译一次,这似乎可以忽略不计。
结论和最终要点
在这篇文章中,我们了解了 CMA 进化策略,并体验了jit、vmap和lax.scan组合的力量是如何扩展神经进化方法的。通过两行代码,我们能够对随机适应性评估和群体成员进行矢量化。XLA 的强大功能让我们可以在加速硬件上运行整个询问-评估-告知过程。最终,这些是软件和硬件发展的类型,它们使新类型的研究成为可能(使人想起 Sara Hooker 的‘硬件彩票’中的论点),并有可能复兴被遗忘的技术。在这里,我们研究了一种无梯度优化,它可以把我们从处处使用可微函数的要求中解放出来。我们可以使用尖峰信号(就像大脑一样!).在未来,我们可能会梦想完全不同类型的神经网络架构,它们利用可扩展的随机搜索/0 阶方法。
最后,本教程的全部代码都可以在这个库中找到,供您深入研究。此外,我将整个管道放入一个 120 行片段。我希望得到一些反馈,⭐ CMA-ES 显然不是唯一的 ES。因此,如果您想了解更多关于不同 ES 的信息,或者对 CMA-ES 有不同的看法,我建议您查看以下链接:
- 尼古拉·汉森伟大的关于 CMA-ES 和文档页的教程论文。
- David Ha 的 estool 工具箱和 ES 的可视化介绍。
- Lilian Weng 关于进化策略的博客/调查。
- 带有 Numpy CMA-ES 实现的 CyberAgents Github repo。
我要感谢 Joram Keijser 帮助我使这个博客成为一个更好的版本,也感谢他是我在《JAX-爱情犯罪》中的伙伴。
附加:CMA-ES 的超参数概述
我们只是浏览了如何初始化/设置 CMA-ES 的不同参数初始化的细节。这主要是因为我想把重点放在高层次的直觉上,而不是让你过多地沉浸在数学中。如果你想了解更多关于 nitty 细节的信息,我推荐你查阅 Hansen (2016)的教程。下面我收集了一个关键超参数的表格,以及它们在参考教程中是如何设置的:

跨多个 Kafka 实例的“恰好一次”语义是可能的
用代码解决 Kafka 中的跨集群事务问题

“恰好一次”语义在分布式系统中是一个具有挑战性的问题。为了解决这个问题,一些著名的协议和算法有:两阶段提交、 Paxos 和 Raft 。当跨越分布式系统的两个实例时,这个问题变得更加困难。
Apache Kafka 已经在三年前在一个实例或一个集群的上下文中支持“恰好一次”(又名事务),并且在那段时间内保持迭代:KIP-447KIP-360KIP-588。
大型企业用例通常不会只运行一个 Kafka 实例,托管多个 Kafka 实例的常见场景包括(不限于):
(1) 灾难恢复
(3)针对不同目的的特殊调整的实例:“摄取”实例针对高吞吐量摄取进行供应和调整,而“处理”实例针对计算密集型作业。
因此,在多个实例的上下文中,自然问题变成:
在一个集群中,我们还能拥有与我们期望的“事务性”或“恰好一次”数据处理相同的“恰好一次”语义吗?
本文将分享关于如何基于新的 MirrorMaker (或 MirrorMaker 2),Kafka 生态系统中新的跨数据中心复制工具,在 2 个集群上实现上述功能的高级想法。最后,发布代码实现。
(免责声明:在我写这篇博客的时候,下面的“恰好一次”功能处于“审查”状态。虽然它已经引起了很多关注,并在某些环境下经过了验证正确性的繁重工作测试,但它可能会发生变化,并遵循与Apache License 2.0)相同的保修
为什么跨多个实例的恰好一次语义很难
为简单起见,我们以 MirrorMaker 2 为例。
Kafka 消费者从一个集群(称为“源”)消费一批数据,然后 Kafka 生产者立即将其生产到另一个集群(称为“目标”)。为了确保“恰好一次”的交付,生产者每次从消费者那里收到一批数据时,都会通过“协调者”创建一个新的事务。通过“恰好一次”协议,协调器位于生产者指向的集群(“目标”)中。
如果一切正常,在新生成的数据在目标集群上可见以供消费之前,完成事务的最后一步是让生产者将消费者补偿提交给一个“补偿”主题(通常该主题被命名为 consumer_offsets ),因此消费者将知道在下一批中在哪里消费。然而,消费者偏移是由消费者初始化的,并且偏移主题必须位于源集群中。指向目标集群的生成器无法生成/写入位于不同集群中的偏移主题。
如何跨多个集群支持恰好一次
KIP-656 就是整个提案。简而言之,我们仍然利用当前为一个集群设计的一次性框架,但是在将它应用于多个集群时解决了上述挑战。
关键点是:关于消费者补偿和补偿主题,它的单一真值来源由生产者管理、提交并存储在目标集群上。
等一下——在上面的部分中,消费者偏移和偏移主题必须在源集群上?
为了从源集群中提取数据,消费者仍然必须生活在源集群中,但是“真实的源”消费者偏移量不再存储在源集群中。当数据传输作业(在当前上下文中,作业是 MirrorMaker)重新启动或重新平衡时,我们建议使用以下想法来正确地回滚消费者,同时“真实来源”消费者偏移存储在目标集群中:
- 消费者偏移量使用一个“假”消费者组存储在目标集群中,只要我们知道消费者组的名称,就可以通过编程方式创建消费者组。“假的”意味着该组没有使用实际的记录,只有存储在 __consumer_offsets 主题中的补偿。然而,目标集群上的 __consumer_offsets 主题(由“假冒”消费者组管理)是“真实来源”的消费者补偿。
- 有了目标集群上的“假”使用者组,MirrorMaker 中的使用者不依赖于源集群上的 Connect 的内部偏移量跟踪或 __consumer_offsets 。
- 与“假”消费者组相关联的消费者偏移量仅由生产者写入目标集群。
- 所有记录都写入一个事务中,就像在单个集群中一样。
- 当 MirrorMaker 重新启动或重新平衡时,它会在目标群集上加载来自 __consumer_offsets 主题的初始偏移。
以上想法的结果:
- 如果交易成功,目标集群上的 __consumer_offsets 主题将按照当前的一次性框架协议进行更新。
- 如果事务中止,所有数据记录都会被丢弃,目标集群上的 __consumer_offsets 主题不会更新。
- 当 MirrorMaker 重新启动/重新启动时,它将在目标群集中存储的最后提交的偏移量处恢复。
https://github.com/apache/kafka/pull/9451 是体验这种乐趣的地方!
摘要
多个 Kafka 实例已经成为大规模企业的流行部署,对于许多流用例来说,恰好一次语义是强烈首选的,甚至是必需的。我希望 KIP-656 将有助于在 Kafka 生态系统中启用多集群恰好一次语义。
用于处理数据的多光标示例
在 VS 代码中使用多光标和选择特性进行数据分析时,如何节省时间和精力

处理数据可能是非常动态的,需要在代码中反复向前和向后移动,以调整和复制代码片段,引入新的假设、过滤器或步骤,从而对下面的方式产生影响。当进行 EDA 或开发处理流水线时,这种情况经常发生,你必须通过实验找到解决方案的路径,问自己:这将如何寻找不同的变量?如果调整到第 99 百分位,它会有什么变化?如果我提前两步过滤,会不会更快并且仍然有效?这个过程被称为发现是有原因的,但如果我们对自己诚实,那就是我们喜欢它的原因。在这个过程中,新的想法会很快出现。然而,改变代码来迎合这些想法可能是一件苦差事。这就是我看到多光标和高级选择功能的好处。
我读过一些关于多光标是非常高级的开发人员的工具的评论,指出缺乏初学者的用例。我想证明这是错误的,因为这是关于这些特性的智能使用为每个级别的编码人员创建用例。一方面,它为我节省了大量时间。但它也有助于减少手动更改六七处变量名、测试微小调整或纠正错误时的麻烦。我觉得手动进行这些更改会导致您的分析工作流出现某种门道效应,您在调整代码时忘记了您想要分析的内容。我希望看到下面的功能可以帮助你更加专注。我在 VS 代码中使用它们,但是如果您使用任何现代的 IDE,我相信您也可以获得类似的特性。
插入多光标
让我们从简单明了的开始。您希望看到数据集的头部和尾部,但是,当然,您忘记了只有在调用没有被print()或display()函数包装的情况下,Jupyter 才会显示最后一次调用的输出。您可以手动编写这两个显示函数,或者手动复制&粘贴display(部分并在第二行添加)。或者使用多光标和“alt+lmb ”( LMB——鼠标左键)同时书写。

插入多光标—按作者排列的图像
将光标插入上方/下方
我知道,我刚刚犯了最大的错误,用了鼠标。但是请继续关注,对于上面的用例,我通常不会这样做,因为您可以使用“Ctrl + Alt+ ↑ / ↓”来完成。但是让我们把这个变得复杂一点,让你更好地理解我所说的聪明用法的意思。您可以使用“pos1”、“end”或“ctrl + ←/→”来跳转一行代码,并在两行(或更多行)不同的代码中移动,同时实现类似的功能。假设我们只需要头和尾的 3 行,我们的 head 函数由于某种原因被一个.query()方法过滤了,我们在第一行的开始。我们现在可以用上面的键盘快捷键得到第二个光标,用' end '跳到两行的末尾,返回两个字符,并将我们的 3 作为参数添加到函数中。

将光标插入上方/下方—按作者排列的图像
使用多光标复制和粘贴
如果你曾经打错了过滤值或列名,你会喜欢这个!当然可以用多光标复制粘贴。下一个示例将上面的两个想法放在一起,通过复制和粘贴从 Dataframe 获取列名,并填充绘图函数的 x 和 y 参数。使用下面的“添加选择”功能,你可以更有效地操作,但是让我们一步一步来。

使用多光标复制和粘贴—按作者分类的图像
将所选内容添加到下一个查找匹配项
这是迄今为止我最喜欢的功能,因为它使上述所有功能更加通用。如果一个会非常准确,“添加选择”功能属于“搜索和替换”功能组,但它允许添加多个光标,然后做你的事情。选择几个出现的逗号或引号可以让你非常有效地做事(见下面的“所有事情放在一起”一节)。具体来说,如果您没有选择任何内容,则“Ctrl + d”将选择当前单词,再次按下快捷键将选择该“单词”的下一个出现位置,依此类推。如果您选择了某个内容,它会找到该特定选择的下一个内容。举个例子,出于我不记得的原因,我更喜欢双引号而不是单引号,为了保持一致,我每次都要改变它们。

添加选择(简单)-按作者排列的图像
但是让我们来看一个真实的用例:改变一个被绘制的变量。如果您正在创建一个更高级的绘图,该变量的名称也可能包含在其他地方—如标题或轴标签。这个例子的额外好处是:如果你移动箭头键,你将取消选择这个词,只有多光标。此时,您可以使用“Alt + LMB”来删除选择之间不需要的多光标。如果你选择了很远,你可以用“Ctrl + k,Ctrl + d”返回。

添加选择(用于编辑)—按作者分类的图像
顺便说一句,使用“Ctrl + F2”可以一次找到所有事件,这对于使用笔记本来说非常好,因为它被限制在一个单元格内。不过,在较长的脚本中我会小心使用它。我刚刚对不同的位置做了一个非常耗时的分析,这个特性真的为我节省了一些对不同位置的变量应用相同函数的时间。
额外奖励:上下移动线条和上下复制线条
你已经成功了,让我给你看一个额外的。我想展示的这些最终功能实际上属于“基本编辑”功能,但我体验到我通常将它们与多光标编辑结合使用。因此,我认为他们非常适合这里。先说 copy line up/down,用` Shift + Alt + ↑ / ↓'得到。当我测试调整后的输入和参数,但想确保快速返回到之前的状态时,比如测试图形大小时,我会使用这个方法。

向上/向下复制行—按作者复制图像
向上/向下复制不限于一行。只需在选定的多行上按“Shift + Alt + ↑ / ↓”键,它就可以复制上面或下面的整个部分。它允许你很快地做两件相似的事情。在下面的例子中,我在 30 秒内将 iris 数据集中的另一个物种放入了图中。复制&粘贴所有这些会花费更长的时间。为了补充我关于智能用法的观点,请注意,我在两个函数调用中使用 color 参数作为锚点来获得多光标,以便在 color 参数下面添加相同的新行——使用代码中的内容来充分利用这些工具。

向上/向下复制多行—按作者复制图像
最后但也是最基本的,我想介绍的一个特性是上移/下移线特性,只是为了确保您了解它。非常简单,它允许您更改代码的顺序,从而更改数据处理步骤。它也适用于多行选择。例如,我真的很难记住,如果我合并两个图,并想改变轴标签,我必须在最后一个图函数调用中这样做才能产生效果。

向上/向下移动线条—按作者排列图像
所有的事情放在一起
一个特别的操作是创建从数据集列或唯一列值到其他值的映射,以分配函数或分类等,我觉得它真正减少了我们在上面学到的这些特性的麻烦。例如,当我构建数据管道时,我用它来构建将列名从数据框映射到 SQL 数据类型的“create table”SQL 语句。我这样做也是为了设置存储某些值的配置文件,这些值应该被映射到特定的变量值。我最近读到的一个有趣的例子是,通过将一个字典映射到一个字符串变量(尚未排序的类别),将该字符串变量转换为预测模型的工程特征,从而创建数字排序值。过去,我在这种操作上浪费了大量时间和精力,所以我想通过使用上面的特性,以 diamonds 数据集的“cut”列为例(其中“好”和“非常好”没有正确排序),快速展示这是如何工作的。

将变量值映射到有序数值-作者图片
结论
最后,我想回到我关于门口效应的观点。用代码分析数据必须做的一些事情可能会很麻烦,而且做这些事情的努力会分散你的注意力,打断你的思维分析流程。我希望这能帮助你坚持下去。编码快乐!
Excalidraw:如何在草图中将复杂的数据科学理念变为现实
通过有趣的自定义视觉效果解释硬数据科学主题

所有图片和 gif 都是作者的
介绍
写了 100 多条,我悟出了一个硬道理。我可以是一个漫谈 5 分钟的作家,解释一个困难的话题,倾吐我的心声。我也可以是一个写得不多,但展示简单视觉效果的作家,就像下面这样,既节省了我的时间,也节省了读者的时间:

尽管你已经听过无数遍了,我还是要重复一遍——一张图胜过千言万语。在数据科学交流中,这句话在字面上、比喻上、口头上都是正确的。
你不想让你的读者、经理、老板或任何阅读你的作品的人用大术语和长句来烦你。取而代之的是,通过杀手级的视觉效果来传达你的想法,这种视觉效果可以穿透任何人无聊的眼睛,直入主题。
不知道怎么做,还是觉得没必要的难?嗯,你显然没有听说过 Excalidraw 。
https://ibexorigin.medium.com/membership
获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:
https://alphasignal.ai/?referrer=Bex
图纸、数字和文本
Excalidraw 是一个在线画板,你可以在这里绘制自定义的视觉效果。它受欢迎的部分原因是它能够创造出类似潦草手绘的图形。

您可以从顶部的工具栏开始绘制基本图形:

使用这些选项中的任何一个都会在左侧弹出一个菜单,您可以在其中更改图形:
- 中风
- 背景
- 充满
- 笔画宽度/样式
- 马虎
- 不透明
这是一张 gif 图,展示了当你使用这些设置时,体形是如何变化的:

Excalidraw 的另一个很酷的功能是智能箭头。将箭头附加到对象后,箭头会随着对象的移动而移动:

此外,您可以随意更改和弯曲这些箭头:

一旦你开始画箭头,你可以左键点击你的鼠标来创建断点。完成后,您可以双击箭头来更改断点。
在 Excalidraw 中导航
右键单击任何对象都会弹出一个包含许多有用选项的上下文菜单:

更改显示顺序、组合多个对象、将选择内容复制到剪贴板都是一些例子。
接下来,右键单击任何空白区域都会出现一个不同的上下文菜单,其中有几个更改编辑模式的选项:

您可以选择启用“网格”和“我的最爱”——进入查看模式,该模式显示项目保存到图像时的样子。
此外,您可以在视觉工作时打开/关闭黑暗模式。最棒的是,在将项目存储到图像时,您可以使用相同的选项:

Excalidraw 还有一些特性,使同时控制多个对象变得轻而易举。例如,您可以控制显示顺序:

或者更改多个图形的对齐方式:

最棒的是,图标库
最后,我最喜欢的 Excalidraw 特性是它广泛的现成图标草图套件,这些图标与编程、数据科学和技术相关:

将这些图标添加到资源库后,您可以在项目中以任何喜欢的方式添加和编辑它们:

此外,如果您想要将视觉效果存储为形状,您可以将它们添加到此图标资源库,以便在其他项目中使用它们:

摘要
我希望我能早点找到 Excalidraw。它可以让我以前的帖子更有趣,更有信息量,更重要的是,不那么罗嗦。
下次当你发现自己处于无法用语言表达的情况时,请使用 Excalidraw。这对自己和他人都是一种恩惠。
您可能也会感兴趣…
</6-pandas-mistakes-that-silently-tell-you-are-a-rookie-b566a252e60d> </10-advanced-matplotlib-concepts-you-must-know-to-create-killer-visuals-9a32d241e31d> </5-cross-validation-techniques-you-need-to-create-models-that-people-trust-62c4629a678b> https://ibexorigin.medium.com/how-i-earn-thousands-for-learning-data-science-with-no-degree-and-experience-3481e09dddd
Excel: 2 个常见错误,1 个罕见错误
每个人都在使用它,确保他们知道如何使用!

约书亚·科尔曼通过 Unsplash 拍摄的照片
Excel 才是王道。它可能被你公司的许多利益相关者使用。它通常用于快速分析。与编码相比,它的门槛相当低。这么多人在这么多报告中使用它,你几乎肯定会发现一个 bug。
我将介绍两个常见的错误和一个不太常见的例子。这篇文章既可以帮助你避免这些情况,也可以帮助你为那些使用不符合事实的数字的报告的人提供指导。
(少数派报告的红球滚下来)有数据不符!!!
你戴上手套,这样你就可以浏览录像,找出问题的根源。你所有的监视器都装满了证据。你在执行任务!
有人指出,他们得出的“average_$_per_cust”值与他们在其他地方看到的值不同。

作者照片
此报告采用了一个已经按商店按月计算了“average_$_per_customer”的列,现在它正在计算每个月的平均值。平均是不允许的!当使用“销售额”的总和/创建的公式“客户”的总和时,请注意上面数据透视表中的差异。
这可能看起来没有很大的区别,但当涉及到一些重要的决定时,这可能是一个项目的结束,这个项目实际上做得刚刚好,足以生存。
求平均值会带来多大的麻烦?
说到平均值,最棘手的是分母的值会对结果产生相当大的影响。在这个例子中,商店 10 去年还没有开业,所以没有相关的销售。在我们的左栏中,我们将销售额记为 0。在我们的右栏中,我们没有值。使用 0 将在每个观察实例的分母上加 1。

作者照片
与计算中实际需要的 9 家商店相比,在 10 家商店之间分摊的 905 美元(我们假设以千计)非常不同。
这个有点鬼鬼祟祟
我们要换个场景,请来了我们的老朋友:V-Lookup。这种情况有点古怪。我想展示一下,以防你遇到类似的情况。首先,看一下靠近输出中间的两列。

作者照片
在这个场景中,我们使用 V-Lookup 引入最右边的“客户”值,以匹配“销售”值。我们没有唯一的标识符来加入,所以创建了一个。“year_week_store_concat”列似乎不言自明。它连接了 year、week 和 store 列。这会给我们一个独特的价值,对不对?
请注意,在我们的“cust_vlookup”列中,我们看到了值 52 和 50 的副本。如果您查看我们的“year_week_store_concat”列,您会注意到对于 52 的两行,这两行的查找都是 2021111。第 1 周,商店 11 看起来就像第 11 周,商店 1。使用 V-Lookup,它首先找到的任何值都将被引入。
解决办法?使用分隔符。请注意,现在我们有 2021–1–11 对 2021–11–1。使用此方法,“delim_cust_vlookup”将正确匹配这些值!
最后的想法
因为将数据放入 Excel 文件并在几秒钟内创建数据透视表是如此容易,人们可能会忘记较小的细节。我希望你不会遇到这些问题,但是如果你遇到了,也许这些会在调试过程中有所帮助并节省你的时间。
这些都是帮助他人避免问题的很好的小技巧。理想情况下,你们将能够像一个团队一样前进,花更少的时间比较结果。一如既往,继续学习!
对于那些使用 SQL 的人来说,在对两个列求和时检查这个可能的错误,并解释它是如何工作的:
这将帮助您在 SQL 中使用 DateDiff()时避免年份计算错误:
在电子表格上建立一台单指令集计算机(OISC)
Excel 的乐趣:让我们看看一个电子表格如何在指令集层面上模拟一台完整的计算机

在数据科学中,我们经常使用电子表格来分析数据和原型算法,但是电子表格还能做什么呢?让我们看看电子表格是如何在指令集层面上模拟整个计算机的。
我们必须支持多少条指令?嗯,Intel x86 芯片(大多数 PC 用的)理解 1500 条指令。ARM 芯片(用于大多数手机、平板电脑和新 MAC 电脑)将这一数字减少到 50 左右。然而,一台可行的计算机只需要一条叫做 SUBLEQ 的指令就可以制造出来。我们将使用 Excel 来看看这是如何工作的。(这里是 OneDrive 上的完整电子表格。)
典型的计算机芯片提供指令来
- 加载和复制内存位置中的值
- 比较两个存储单元中的值,
- 跳转到内存的新部分,并开始评估那里的指令。
- 对整数、浮点数,有时甚至是数字数组进行加法和乘法等数学运算
一些计算机芯片提供指令来做更多的事情,例如,与人工智能、3D 图形、密码学相关的指令。
但是,如果我们不添加更多的指令,而是删除指令呢?我们能逃脱多少指令?包括奥列格·马宗卡和亚历克斯·科洛金在内的计算机科学家说,答案是一个。马宗卡和科洛金建议选择 SUBLEQ 作为一个指令,因为它
“是最古老、最流行、也可以说是最高效的[单指令集计算机]”[基于 sub leq(arxiv.org)的简单多处理器计算机
SUBLEQ
SUBLEQ 指令包含三个部分:
A的存储位置,要减去的值B的存储位置,从中减去A的值- 如果
B小于或等于 0,跳转到的内存位置。如果B是正的,我们向前跳 3 个记忆位置。
让我们来看一个基于sub leq—Esolang(esolangs.org)的 Excel 示例。我们把数字 3,5,6,7,7,7,3,4,0 存入内存。

计算机的指令指针指向下一条要执行的指令的存储位置。假设我们的指令指针指向内存位置 0,那么计算机查看三个值,从内存位置 0 开始,即 3、4 和 6。这意味着 3 是A的存储位置,要减去的值是 7。而且,4 是B的存储位置,是要从中减去 a 的值。该值从 7 开始,但减去A后将变为 0。最后,如果B变为 0 或更小,6 是要跳转到的存储器位置,因此指令指针将变为 6。
设置 Excel
在 Excel 中,我们添加以下内容:

我们希望能够将指令指针设置到任何内存位置,并让 Excel 填充该表的其余部分。这怎么可能呢?以下是我对part1使用的公式:
=INDIRECT(ADDRESS(ROW(D19)+D9,COLUMN(D19)))
其中D9是Instruction Pointer的单元,而D19是存储单元 0 的单元。该公式要求从内存开始向下查找值Instruction Pointer。由于Instruction Pointer为 0,这将找到包含值 3 的内存位置 0。part2的公式是一样的,只是我们在行中加了 1。对于part3我们加 2。A的公式几乎相同,即:
=INDIRECT(ADDRESS(ROW(D19)+D10,COLUMN(D19)))
其中D10是part1的单元格。所以,这表示从内存的开始向下查找值part1。那是内存位置 3,包含 7。对于B,我们使用A公式,但用D11代替 D10。最后,当然,B-A单元格的值就是B单元格的值减去A单元格的值。
走向
下一个问题是我们如何用它们的新值更新Instruction Pointer和B?我们可以想象关闭 Excel 的 Recalc 设置,就地更改它们的值,然后使用 F9 键将程序向前推进。我没那么做。相反,我为程序的下一步创建了一个新的专栏。我把程序的原步骤称为“0 步”,下一步称为“1 步”。
电子表格看起来像这样。

黄色显示用户输入(原始存储值和Instruction Pointer)。注意,在步骤 1 中,Instruction Pointer的变化和存储器位置 4 中的值(显示为红色)。Instruction Pointer的新值的公式是:
=IF(D9<0,D9,IF(D15<=0,D12,D9+3))
其中D9是Instruction Pointer的旧值,D15是B-A的值,D12是前一条指令的part3。上面写着:
- 如果
instruction pointer为负,保持不变(表示程序完成)。 - 否则,如果
B-A小于或等于 0,则将Instruction Pointer设置为part3。 - 否则,将
Instruction Pointer增加 3。
存储单元 4 的新值的公式为
=IF($C23=D$11,D$15,D23)
其中C23是包含位置号的单元格,即 4。单元格D11是part2。单元格 D15是B-A。单元格D23是该存储单元中的前一个值。所以,这表示:如果这个内存位置是前一步的B,那么将其值设置为B-A,否则保持值不变。所有的存储单元都有一个类似的公式。
我们可以将步骤 1 的列复制到右边,以获得任意数量的未来步骤的列(最多到 Excel 的限制,大约 16,000)。我们可以向下复制最后一个内存行,以获得任意数量内存的位置(最多可达 Excel 的 1,000,000 个内存的限制)。
你好世界!
最后一个大问题仍然存在:我们如何输出任何东西?按照惯例,SUBLEQ 通过向特殊内存地址-1 写入一个数字来创建输出。我们可以将该数字解释为 ASCII 字符。下面是一个来自sub leq—Esolang(esolangs.org)的输出“Hi”的示例:

在第 0 步,Instruction Pointer表示从内存位置 0 开始,这给了我们9, -1, 3。因为第二个数字是-1,这意味着输出存储单元 9 的值,即 72,然后将Instruction Pointer设置为 3。ASCII 72 是字符“H”。
在步骤 1 中,Instruction Pointer表示从内存位置 3 开始,这给了我们10, -1, 6。这意味着输出存储单元 10 的值,即 105 (ASCII 表示“I”),并将指令指针设置为 6。
在第 2 步,Instruction Pointer告诉我们从内存位置 6 开始,这给了我们0, 0, -1。这意味着从自身减去内存位置 0(即 9)的值,并将Instruction Pointer设置为-1。
在步骤 3,Instruction Pointer表示从-1 开始,这是一个负数,所以程序完成。
为了支持输出,我对电子表格做了以下更改:
- 添加了存储位置“-1”。第一步的公式是
=IF(D11=-1,D13,””),如果part2为-1,则输出A,否则为空。 - 在
B-A下方增加了一行Output。它只是存储单元-1 的 ASCII 版本。其步骤 1 的公式为=IF(E18<>””,CHAR(E18),””),表示如果内存位置-1 不为空,则显示其 ASCII 值,否则为空。 - 将
B-A的公式改为=IF(E9<0,”HALT”,IF(E11>=0,E14-E13,E13)),公式为:1)如果Instruction Pointer小于 0,显示“HALT”,2)如果A的存储位置至少为 0,从B中减去A,3)否则为B。我还更改了part1、part2、part3、A和B的公式,以便在指令指针为负时显示“暂停”。
有了这个,我们可以运行更多有趣的 SUBLEQ 程序。例如,“你好,世界!” Subleq 示例—Esolang(esolangs.org)使用循环将所需字符串复制到输出,并在到达字符串末尾时停止。这是电子表格的顶角。您可以在输出行中看到“H”和“e”。

在 OneDrive 上找到完整的电子表格。在网上或下载后,你可以玩电子表格。你也可以放入你自己的 SUBLEQ 程序,或者看看如果你改变初始的Instruction Pointer值会发生什么。
讨论
在电子表格上实现 SUBLEQ 让我们看到如何只用一条指令就能设计出计算机芯片。用更传统的语言(比如 Python)演示 SUBLEQ 会更容易,但是电子表格提供了两个好处:
- 首先,把计算的每一步放入它自己的列,让我们以一种新的方式来看一个程序。我们不再把程序和计算机看作是随时间变化的一维向量,而是把程序的整个历史看作是一个二维数组。
- 其次,我们可以更改单个输入单元格,Excel 将只有效地更新那些需要更改的单元格。例如,在完整的“Hello World!”例如,将初始的
Instruction Pointer从 0 改为 1。令人惊讶的是,该程序仍然有效。把它改成 3,你会发现程序不仅还能工作,甚至运行得更快了。
如果你想要更多的 Excel 乐趣,请参阅我的文章 Excel 乐趣——从电子表格构建 3D 图形——微软 365 博客。我展示了如何实现和理解 3D 图形。您可以通过更改电子表格值来交互式旋转正方形和立方体。

使用 R 的 Excel:自动化 Excel
使用 R 自动读取、写入、合并和拆分多个 Excel 文件

数据分析领域的魅力之一是它对多种工具的开放性。这个领域已经表明,只知道一种工具是一种障碍。为了在这个领域生存和发展,你应该:
样样精通,样样精通。—未知
在大多数企业中,Excel 是处理数据的重要工具之一。我自己有使用 Excel 进行数据分析的经验,并没有发现什么缺点。我面临的问题不是我自己的问题,而是大多数用户面临的问题。
在这里,我提出了从 Excel 转换到开源编程语言的理由,或者更确切地说是利用它们的力量:
- Excel 列不区分输入和输出。
- 用户定义的公式不直观,因为单元格位置被标记而不是变量名。
- 打开/处理大文件很慢,系统经常崩溃(企业不能经常升级计算机)。
- 将不同的 Excel 文件合并到一个文件中是很麻烦的,同样,在一个文件中处理不同的工作表也很麻烦。
- Excel 在付费墙后面。
正如已经提到的,开源编程是选项之一,这里我将使用 R 来自动化简单的 Excel 文件处理。
涵盖的主题有:
- 机器可读文件命名
- 读取 Excel 文件
- 合并 Excel 文件
- 拆分 Excel 文件
- 创建多个 Excel 文件
目标
目标是两个读取三个 Excel 文件,其中包含与预期寿命、GDP 支出、天然燃料使用和水可用性相关的全球数据。将这些文件合并成一个包含三个工作表的 Excel 文件,并保存在工作文件夹中。使用 *Outer Join* 将三张表格合并成一张,并将每个国家的数据保存到相应的洲文件夹中。

作者 Gif
那么,我们开始吧。
机器可读文件命名
正确命名文件很重要,因为在处理自动化时它会很方便。命名文件的最好方式应该是机器可读的。命名文件时,请尝试执行以下规则:
- 避免重音字符、空格,避免区分大小写的文本。
- 使用分隔符或分隔符,如“-”或“_”。
- 使用机器可读的日期格式,如 YYYY-MM-DD。
- 在末尾添加版本控制以便于导航(例如-01,-02,…,-10)。
命名文件的示例:2021–07-15 _ Excel _ Automation-01 . xlsx
读取 Excel 文件
为了读取文件,使用了xlsx包。有两个功能:read.xlsx()和read.xlsx2()读取xlsx包中的文件。read.xlsx2()在处理大型数据集时快速高效。下面的代码是用来读取 Excel 工作表的。
# reading individual files
fuel <- read.xlsx2("global-fuel-vs-gdp.xlsx",sheetIndex = 1)
life_exp <- read.xlsx2("global-life-expectancy.xlsx",sheetIndex = 1)
water <- read.xlsx2("global-water-share.xlsx",sheetIndex = 1)
因为我们在这里是为了自动化,所以让我们研究一下。要解决读取多个文件的问题,使用list.files()功能创建一个包含所有感兴趣的文件名的列表。将单个工作表中的 Excel 数据存储到数据框中。为定义的数据框对象使用正确的命名约定,因为这将有助于自动化。通过两个 For 循环和assign()功能将数据帧分配给单个页面。
# set the working directory
setwd("C:/Users/amalasi/Documents/R/Blog post/Blog17-Excel1")
合并 Excel 文件
一旦加载了 Excel 数据,数据就可以进行处理了。这里讨论两种可能的数据合并方法。第一个路径是创建一个包含多个工作表的工作簿,这些工作表对应于所有工作簿的工作表。第二个路径是将第一个路径中创建的工作簿中的所有工作表合并成一个工作表。
创建具有对应于所有工作簿的多个工作表的多个工作表的单个工作簿
通过在 for 循环中使用write.xlsx()函数,可以帮助将包含多个工作表的多个工作簿作为不同的工作表添加到一个工作簿中。使用write.xlsx()功能时应小心,第一次确保将append参数值设置为默认值,即 FALSE。这将创建工作簿,要向该工作簿添加单个工作表,只需设置参数append=TRUE。
通过合并工作簿的多个工作表来创建单个工作表
这里的技巧是记住命名所有在自动化过程中创建的临时数据帧,使其具有固定的模式(记住机器可读文件命名中定义的约定)。至少在这一部分,这非常有帮助。
本练习的目标是使用外部连接合并工作表。为此,请创建所有相关数据框的列表,这些数据框将被合并以创建单个数据框或表。使用ls()和mget()函数,创建临时数据帧列表,并将其分配给名为 local_list 的对象。通过定义 list 对象,可以在 For 循环中轻松操作数据帧。这段代码将创建一个名为 global_final 的对象,并将所有数据合并到一个工作表中。
拆分 Excel 文件
当处理包含多个工作表的大型工作簿时,可以使用下面的代码提取所有单个的工作表并将其分配给单个的数据框。这是通过使用getSheets()功能实现的。
创建多个文件和文件夹
最后一个目标是创建特定于国家的文件,并将它们保存在对应于各自大陆的文件夹中。这里有两个步骤是完全实现目标所必需的。
第一步是创建以大陆命名的文件夹,这是通过使用dir.create()功能实现的。
第二步是将工作表分割成多个包含各个国家信息的数据框,并保存它们。从索引单个国家开始,因为这有助于创建 For 循环。接下来,使用filter(),提取国家信息并将其存储在一个临时对象中。然后创建一个工作簿,定义一个工作表并将数据写入工作表。最后一步是将文件保存在目标文件夹中。


作者图片
完整代码的链接是这里的。
结论
这篇文章清楚地展示了花几个小时编写 R 代码脚本,从长远来看,在合并和拆分多个 Excel 文件时可以节省大量时间。
上述代码与工作簿中的工作表数量无关,可以处理具有不同数据列的工作表。
关键的学习是:定义机器可读的文件命名约定,以及读取、写入、合并和拆分多个 Excel 表格和工作簿。
下一步将是使用数据透视表、Vlookup 和其他使用 r 的 Excel 函数。
对我写的更多自动化相关的博客感兴趣,请查看这个博客:
关于 R 和用 R 绘图的其他博客:
💔-lesser-known-pipe-operators-in-tidyverse-111d3411803a> </7-shortcuts-worth-knowing-for-efficient-workflow-in-r-cc52c351cb73>
数据源
- https://ourworldindata.org/grapher/life-expectancy
- https://ourworldindata.org/water-sanitation-2020-update
- 【https://ourworldindata.org/energy-poverty-air-pollution
链接到 Github 库。
可以在LinkedIn和Twitter上与我连线,跟随我的数据科学和数据可视化之旅。
面向非 Python 爱好者的 Excel-Python 应用程序
让您的非编码同事预测 Python 在 Excel 中的威力(用 Xlwings 和 PyInstaller 释放野兽)

N.Y.T .(突然……),水彩纸上的丙烯颜料和蜡笔,马蒂亚斯·布勒(得到我朋友马蒂亚斯的友好许可)
动机:
我们经常进行令人印象深刻的 Python 分析,但我们的同事所要求的只是在 Excel 中获得最终结果。我们的许多同事不想使用 Jupyter Notebook、Python 脚本之类的东西。他们只想坚持使用他们钟爱的电子表格工具(比如微软的 Excel)。在这个故事中,我们将学习如何构建这样一个 Excel 解决方案,它在后台使用 Python。因此,我们的非 Python 专家同事可以直接从 Excel 中启动 Python 代码,不需要任何 Python 知识(也不需要处理任何技术问题,如包安装或版本故障排除)。
解决方案:
现在让我们一步一步地研究如何在 Excel 中运行 Python。
首先你需要 pip 安装 xlwings。如果您尚未安装 Xlwings ,请现在安装,例如通过 pip install :
pip install xlwings
之后,我们导入 Xlwings 进行一个简短的测试:
import pandas as pd
import xlwings as xw
df = pd.read_excel('InputOutput.xlsm')
xw.view(df)

这就是我们在 Excel 中的输入选项卡的样子。
与熊猫相反,Xlwings 打开这个 Excel 文件(。view ),这样我们就可以直接在 Excel 电子表格中查看数据框架。这在检查大数据帧时非常方便(比在 Juypter 笔记本单元格中直接检查大数据帧更方便,尤其是在交叉检查大量计算时)。
多亏了 Xlwings,我们可以在 Jupyter 笔记本上做几乎所有的事情,并最终将数据帧写回到 Excel 表中。基本概念就是简单地将你的代码编写到一个主函数中。让我们先来看一个简单的例子:
# Make sure, that the Excel file is located in the same path as this file.import pandas as pd
import xlwings as xw
def main():
wb = xw.Book.caller()
sht = wb.sheets['Input']
df = sht.range('A1').options(pd.DataFrame, header=1, index=False, expand='table').value
counter = []
# add Counter starting from one until max counter is reached
for index, row in df.iterrows():
for x in range(int(row['Counter'])):
counter.append(x+1)
# duplicate the rows according to Item specific max counter
df = df.loc[df.index.repeat(df.Counter)]
# Add the counter number per row
df['CounterDuplRow'] = counter
df['Total']=(df['Column A']+df['Column B'] + df['Column C']) *df['CounterDuplRow']
df2 = df.sort_values("Total").groupby("Item", as_index=False).last()
shtout = wb.sheets['Output']
shtout.range('a1').options(pd.DataFrame, index=False).value = df2
[@xw](http://twitter.com/xw).func
def hello(name):
return f"Hello {name}!"
if __name__ == "__main__":
xw.Book("InputOutput.xlsm").set_mock_caller()
main()
# in case this Jupyter Notebook is working well, you can download it as a Python file (.py)
# to do so, just click on "file" in the menu above and choose "download as py"
请注意,Excel 文件必须与您的 Jupyter 笔记本位于同一文件夹中才能正常工作。这个脚本主要做的是从 Excel 的 input 选项卡中获取输入,遍历它并进行一些计算(出于训练目的,我们添加了一个循环)。结果数据帧被写回到同一个 Excel 文件中的“输出”选项卡中:

我们将 df 写入 Output 选项卡,从单元格 A1 开始。
如果一切正常,我们可以将 Jupyter 笔记本保存为 Python 脚本(只需在 Jupyter 笔记本菜单中单击“文件”并选择“下载为 py”),并确保将其命名为与我们的 Excel 文件相同的名称:

我们现在能够在 Jupyter 笔记本上开始计算,从 Excel 中读取和写入数据帧。到目前为止,没什么新发现。但多亏了 Xlwings,我们现在可以在 Excel 中添加 VBA 宏,这样我们就可以在 Excel 中启动 Jupyter 笔记本了。好吧,越来越有意思了。但我们真正寻求的是将一个 Excel-Python 应用程序交给我们的同事,而不需要他们在自己的机器上设置 Python(或 Excel 中的 Xlwings 插件)。我们现在将学习如何到达那里。我们的解决方案是通过 PyInstaller 导出所有需要的 Python 包。同样,如果您还没有 PyInstaller,请立即安装:
pip install pyinstaller
然后我们可以使用 PyInstaller 为我们的 Excel-Python 应用程序创建所有需要的文件。我们只需进入终端,转到我们的两个文件(InputOutput.py 和 InputOutput.xslm)所在的同一个文件夹,然后输入:
pyinstaller –-onefile InputOutput.py

根据您的环境,这可能需要一些时间。但是最后终端应该告诉你一个 Exe 文件已经成功构建:

现在,当我们再次查看我们的文件夹时,我们会发现为我们的 Excel-Python 应用程序部署的所有必需的包:

在“dist”文件夹中,我们会找到 exe 文件:

现在,您可以双击这个可执行文件 InputOutput.xlsm,然后您可以检查您的 InputOutput.xlsm:您将看到 Output 选项卡已经被计算出来。这意味着,您可以将完整的文件夹“InputOutput”交给您的同事,他们的计算机上没有安装 Python。您将看到他们也可以启动 exe,并且他们将在 Excel 中收到与您刚才所做的相同的输出选项卡。
为了更方便我们的同事,我们将做最后的修改。我们将在 Excel 中添加一点 VBA,这样我们的同事可以直接从 Excel 中启动代码:
Sub ExeExecute()
‘ Windows-Editor start
Dim TaskID As Long
TaskID = Shell(“C:\Users\Jesko\InputOutput\dist\InputOutput.exe”, vbNormalNoFocus)
End Sub
恭喜,我们已经成功地创建了一个完整的 Excel-Python 应用程序,它甚至可以在不需要额外安装 Python 的计算机上运行 Excel。你可以在我的 Github 中找到 Jupyter 笔记本和 Excel 的例子。
非常感谢您的阅读!希望这篇文章对你有帮助。请随时在 LinkedIn 、 Twitter 或工作室与我联系。
https://jesko-rehberg.medium.com/membership
最初发布于我的网站 DAR-Analytics 。
Excel 不会消失
关于如何拥抱它的一些想法
Excel 是世界上使用最广泛的软件包之一。它从 1985 年开始出现,大约有 7 . 5 亿用户。微软喜欢称 Excel 公式为“世界上最广泛使用的编程语言”。
Excel 擅长进行简单的分析,帮助人们理解数字。它是惊人的灵活和多才多艺。Excel 很受用户欢迎,但在 IT 部门却不那么受欢迎。这种流行的一个例子是对《华尔街日报》的一篇文章的反弹,这篇文章要求停止使用 Excel——“你可以拿走我的 Excel,在你从我冰冷、死气沉沉的手中拿走它之后。”

这是什么意思?
所有这些灵活性的缺点是 Excel 的范围已经扩大,只受到想象力的限制。需要建立一个会计系统,一个电网模型,创建一个数据存储系统…你可以在 Excel 中完成。但是你能做一些事情并不意味着你应该做。在许多方面,Excel 是企业 IT 部门的祸根,存在安全和存储问题。电子表格允许敏感数据被容易地共享,但是它们提供很少或没有保护。随着公司中的多个用户保存同一 Excel 文件的各自版本,不断膨胀的存储会产生问题。
这有什么关系?
你会碰到无数的文章告诉你现在就停止使用 Excel。Python 对于执行分析来说更好/更快/更可靠。所有这些都是真的。然而,大约有 1000 万 Python 开发人员和 7 . 5 亿 Excel 用户,你可能不得不在某种程度上使用 Excel。
如果我们作为数据科学家和分析师的目标是让数据变得有用,我们需要用利益相关者或决策者想要使用的工具来交付我们的分析。这些工具中通常会有一个是 Excel。

正确的工具|照片由 Unsplash 上的 Oxa Roxa 拍摄
是什么让 Excel 如此受欢迎?
Excel 与 MS Office 打包在一起,这帮助它成为全球办公室的默认分析工具。有大量的 Excel 用户,这意味着有大量的资源可以帮助你。在线或 office Excel 专家提供了大量帮助。
Excel 很直观,基本步骤很容易学会。数据立即可见,并且没有要执行的代码。可以直接录入信息,给人一种掌控感。在 Excel 中,调整数据、排序、过滤都是毫不费力的。您可以通过单击鼠标来更改格式。你甚至可以创建令人惊讶的吸引人的图表和仪表盘。
使用 Excel 比学习编码更容易。你可以剪切和粘贴,拖放,最重要的是,ctrl-z(撤销)删除任何最近的错误。有许多公式可供你使用。数据透视表、SUMIF 和 VLOOKUP 语句以及迷你图使汇总数据变得容易。灵活性意味着您可以自己进行研究或回答特定的查询,而完全不需要 IT 或商业智能的支持。您可以运行简单的回归模型而不会有太多的麻烦,运行更复杂的统计模型会有很大的麻烦。
Excel 在不断改进。像 Power Query 这样的工具使加载和转换数据变得更加容易,而无需人工操作。与 PowerBI 的接口使 Excel 成为一个更强大的可视化工具。在过去的几个月里,微软发布了 LAMBDA ,它允许你使用 Excel 的公式语言定义自己的自定义函数。

热门|照片由在 Unsplash 上的 Humantra 拍摄
【Excel 有什么问题?
Excel 的灵活性成本很高。破坏一个公式真的很容易。在这里插入一行,在那里删除一个单元格,或者不小心在公式上键入数据,突然间,电子表格出现错误。更糟糕的是,电子表格在隐藏错误的情况下继续工作。互联网上流传的一些统计数据声称,80%-90%的电子表格都有错误。(我找不到可靠的消息来源。可能这些百分比是基于他们自己的 Excel 错误)。
与其他电子表格和文档链接的能力使 Excel 更加强大,但也使错误跟踪更加困难。具有多个函数或层的公式阅读起来很复杂,这增加了错误跟踪的麻烦。任何收到别人的电子表格并被告知修改#REF 的人!错误会确切地知道我的意思。
使用 Excel 可能需要大量的手动工作来清理数据和格式。这种努力通常是不可重用的。像 Power Query 和宏这样的工具会有所帮助,但是你需要成为一个更加老练的用户才能使用它们。
当处理大型数据集时,一些计算(如 SUMIFS 和 VLOOKUP)需要很长时间才能完成。你的电脑在计算的时候基本上是锁定的。您可以将计算从自动切换到手动,但这会产生一系列新问题。现在,您可能正在用陈旧的数据做决策。在计算过程中,您的计算机也有可能冻结或崩溃,所有工作都将丢失。
我们已经讨论了 Excel 的一些 IT 问题。数据和存储需求的过度共享。毫无疑问,如果我们真的知道关键基础设施在其处理过程中的某一点上对 Excel 的依赖程度,我们会感到震惊。

马库斯·温克勒在 Unsplash 上的照片
前途如何?
最佳解决方案可能是混合模式。Python 是复杂数据分析的更好选择。但是,如果你的老板或股东是《华尔街日报》Excel 的死硬派,该怎么办?有越来越多的工具可以用来连接 Python 和 Excel。如果您需要比将最终数据框导出为 Excel 文件或 CSV 文件更高级的工具,那么这里有一些值得研究的工具。
这三个包可以读写 Excel 文件,执行操作,绘制图形。
接下来的两个包:xlwings 和 pyxll,允许您从 Excel 中调用 Python。Pyxll 使用 Excel 作为前端,在 Excel 中运行 Python 代码。Xlwings 使用 Excel 作为用户界面来运行 Python 脚本。请注意,pyxll 有每月订阅费用。Xlwings 基础款开源,pro 版有授权费。
我希望这些工具在功能和易用性方面都有所进步,从而更容易两全其美。
关于卫星图像的激动人心的机器学习项目
使用机器学习从卫星图像中检测和分类船只,并在 Web 应用程序中部署该模型

美国地质勘探局在 Unsplash 上拍摄的照片
介绍
处理卫星图像很有意思。就我个人而言,如果有人问我为什么选择在数据科学领域。我必须告诉你,在数据科学中有太多的选项可供你选择。简而言之,基本的咒语是-
因为数据无处不在,你可以选择成为任何东西。
我觉得数据科学比其他领域给你更多的自由。例如,从卫星上获得的图像,如果我必须戴上太空研究科学家的帽子来识别海里的船只,我可以做到。在数据科学领域,如果我得到正确的数据,我肯定可以成为一名太空研究科学家。
当我偶然发现来自欧洲航天局的数据集时,我很好奇将机器学习应用到真实的卫星图像上,看看我能从中获得什么。
这是一个简单的项目,获取图像,可视化的图像和训练神经网络模型。我们将使用哨兵 2 号的卫星图像。
在这个项目中,你不仅要建立一个模型来分类船只,还要部署这个模型并围绕它建立一个有趣的网络应用程序。
如果你好奇,下面是用来跟踪地球的主要卫星
哥白尼哨兵 1 号卫星——监测海冰、海风、石油泄漏和海上航行。
哥白尼哨兵 2 号卫星——监测海洋和沿海地区
哥白尼哨兵 3 号——收集海洋颜色和表面温度
哥白尼哨兵 6 号和杰森 3 号卫星——用于监测气候
现在对于这个项目,我们将使用来自 Sentinal-1 的图像,我们将尝试检测和分类船只类型。这个项目有许多用例。例如,您可以随时估计海上交通,从而以更好的方式设计海上导航系统。
深蓝全球是一家初创公司,在哥白尼黑客马拉松[1]中做了类似的工作。
戏弄者
如果你想知道最终会是什么样子,请看下面的视频,完整的代码可以在 Github 上找到
Wedapp 跑步视频
数据
来自 sentinal-1 卫星的图像已经收集并作为 OPENSAR 船舶数据集公开。你可以从 Wekeo 链接这里下载数据集。(您可能需要注册才能访问数据)
数据存取
“哥白尼提供的绝大多数数据/信息都是免费、完整和开放的,任何公民和世界各地的任何组织都可以获得。”【 哥白尼官网】
您可以前往此链接查看如何访问打开的数据集。由于卫星每天产生万亿字节的数据,你可以去 Wekeo 网站注册获取这个项目的处理和压缩图像。
该数据集由 2805 幅图像组成,其中每幅图像都被标记为下面三艘船中的一艘。
- 散装货轮
- 货柜船
- 邮轮;罐车;坦克手
样本图像可以在下面看到。虽然看起来图像中只有细微的差别,但我们希望我们的 ML 模型能够比人类更准确地检测和分类船只。

样本图像——来自 Sentinal-1 的真实卫星图像[ 来源
如果你在寻找类似的项目和不同的数据集,你可以看看 Kaggle 数据集。

分割和船只检测[ 来源
Abhinav Sagar 发布了一个卫星图像模型,你可以通过分割技术检测卫星图像中的船只。
但是在这个项目中,我们不仅要检测船只,还要对船只进行分类。这是可能的,因为我们这里有一个带标签的数据集,因此我们将实现一个监督模型。
履行
该项目的实施分为两个部分
- 分类
- Web 应用程序
在分类中,我们将研究分类神经网络模型的代码和实现技术,在 web 应用程序部分,我们将研究基于 streamlit 的 web 应用程序的构建,用户可以在该应用程序中与我们开发的 ML 模型进行对比,从卫星图像中检测船只。
您可以通过点击这里的直接跳转到 Colab 文件中的实现
https://github.com/tejeshb/Satellite_Imagery_ML/blob/main/Satellite_imagery.ipynb
我使用 VGG16 模型进行迁移学习,该模型已经在图像上进行了预训练。在导入必要的库之后,您可以跳到 ML 模型的实现,因为所提供的数据集中的数据已经被清理,并且所有图像都是大小为(128,128)的 numpy 格式
要实例化 VGG16,请确保设置了以下参数
vgg = VGG16(include_top=False,weights=None,
input_tensor=inputs,pooling='avg')
由于图像是(128,128)形状,将输入设置为如下
inputs = tf.keras.layers.Input(shape=(128,128,1))
之后,确保创建 2 密集和辍学层如下
dense1 = tf.keras.layers.Dense(512, activation='relu')(vgg.output)dropout1 = tf.keras.layers.Dropout(0.5)(dense1)dense2 = tf.keras.layers.Dense(128, activation='relu')(dropout1)dropout2 = tf.keras.layers.Dropout(0.5)(dense2)pred = tf.keras.layers.Dense(3, activation='softmax')(dropout2)
对于密集层,使用“relu”激活函数,而对于预测或输出的最后一层,使用“softmax”激活函数。
请参见下面的完整型号摘要
Model: "model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, 128, 128, 1)] 0 block1_conv1 (Conv2D) (None, 128, 128, 64) 640 block1_conv2 (Conv2D) (None, 128, 128, 64) 36928 block1_pool (MaxPooling2D) (None, 64, 64, 64) 0 block2_conv1 (Conv2D) (None, 64, 64, 128) 73856 block2_conv2 (Conv2D) (None, 64, 64, 128) 147584 block2_pool (MaxPooling2D) (None, 32, 32, 128) 0 block3_conv1 (Conv2D) (None, 32, 32, 256) 295168 block3_conv2 (Conv2D) (None, 32, 32, 256) 590080 block3_conv3 (Conv2D) (None, 32, 32, 256) 590080 block3_pool (MaxPooling2D) (None, 16, 16, 256) 0 block4_conv1 (Conv2D) (None, 16, 16, 512) 1180160 block4_conv2 (Conv2D) (None, 16, 16, 512) 2359808 block4_conv3 (Conv2D) (None, 16, 16, 512) 2359808 block4_pool (MaxPooling2D) (None, 8, 8, 512) 0 block5_conv1 (Conv2D) (None, 8, 8, 512) 2359808 block5_conv2 (Conv2D) (None, 8, 8, 512) 2359808 block5_conv3 (Conv2D) (None, 8, 8, 512) 2359808 block5_pool (MaxPooling2D) (None, 4, 4, 512) 0 global_average_pooling2d (G (None, 512) 0 lobalAveragePooling2D) dense (Dense) (None, 512) 262656 dropout (Dropout) (None, 512) 0 dense_1 (Dense) (None, 128) 65664 dropout_1 (Dropout) (None, 128) 0 dense_2 (Dense) (None, 3) 387 ================================================================= Total params: 15,042,243 Trainable params: 15,042,243 Non-trainable params: 0 _________________________________________________________________
使用下面的代码训练模型
import multiprocessing
from timeit import default_timer as timerstart = timer()cpu_count = multiprocessing.cpu_count()
print(f"cpu: {cpu_count} found")
model.fit(X_train, y_train,
batch_size=12,
epochs=50,
verbose=1,
validation_data=(X_test, y_test),
steps_per_epoch = 25,
max_queue_size=10,
workers=cpu_count,
use_multiprocessing=cpu_count > 1,
callbacks=[check, log])end = timer()
print('Elapsed time: ' + str(end - start))
在模型训练之后,确保下载模型— vgg.h5。
为了测试模型是如何工作和执行的,我们必须检查模型指标,比如准确性。但是我现在不在这个项目中讨论它,因为 1。主要的想法不是得到最好的模型,而是学习过程。2.我有一个更好更有趣的想法来测试这个模型。
有趣的部分—网络应用
想象你自己是一名数据科学家,必须帮助飞行员,他试图通过发现正确的船只种类来拯救世界
你得到以下信息-
1.图片中有 3 种类型的船
2。你的任务是正确发现船只,这样飞行员就可以只攻击战列舰。
现在,你认为什么是最好的主意?使用你建立的模型或者依靠人类的专业知识。
让我们使用一个 web 应用程序来测试一下,您可以上传模型和您想要检查的图像。
允许用户猜测图像中的船是哪一种,然后运行模型来检查谁是正确的。
为这项有趣的活动设计的网络应用程序如下所示

Web 应用程序—船舶分类器
为了构建和部署 web 应用程序,我使用了 Streamlit 库,实现代码如下所示。
在这里,我上传了一个 h5 格式的模型压缩文件,并对文件进行了解压缩。使用load_model()加载模型
之后,使用下面的代码完成图像上传后的模型预测。在这里,调整图像的大小非常重要,因为我们有模型的输入形状(128,128),你需要确保模型预测的图像应该是准确的形状。
让我们来测试这个模型,你可以在这个视频中看到最终的结果
Wedapp 跑步视频
我希望你有这个有趣的项目建设的乐趣,如果你对此有任何问题,请随时留下评论。
参考:
- https://www . eumetsat . int/science-blog/inspiring-ocean-projects-developed-Copernicus-hackathon-weekend
- 数据来源—https://www.copernicus.eu/en/access-data
- 数据集使用许可-https://www.copernicus.eu/en/access-data/dias
在无服务器 GCP 产品上运行 Jupyter 笔记本

亚历杭德罗·埃斯卡米拉在 Unsplash 上的照片
Jupyter 笔记本是数据科学和分析的绝佳工具。它们是在 Jupyter 笔记本服务器上开发的,这些服务器可以在本地安装和运行以进行开发。它通常始于探索、开发、制作原型、摆弄数据。例如,探索 Covid 数据加载为 CSV 文件,来自我们的世界数据。您会看到这里有全世界的统计数据,您希望跟踪这些数据,并每天将其写入 BigQuery 表:)这样的笔记本看起来像这样:
好了,您已经完成了开发,您已经进行了测试,它运行正常,所以现在您要设置一些日常任务来每天运行它。由于这不是一个关于计算消费的复杂任务,它可以部署在一些无服务器的服务上,如云运行或云功能,因为这些产品有按使用付费的计费和慷慨的免费层,所以我们无论如何都不会为此付费。我们将使用库 Papermill ,它支持 Jupyter 笔记本的执行以及输入参数的支持。
完整的 Github 库在这里是。
设置这一点需要几个步骤:
1.在 web 应用程序中包装执行
Jupyter Notebook 将在一个 HTTP 请求中执行,所以我添加 Flask 作为轻量级 web 框架来包装执行。使用 Papermill 执行笔记本也很简单。由于“日期”是将被更改并在笔记本中用作输入变量的东西,因此需要在“参数”变量中传递它。输入日期可以作为请求中的参数传递,如果没有输入日期,则使用“昨天”的日期。此类 web 应用程序的代码如下所示:
import datetime
import logging
from flask import Flask
import papermill as pm
app = Flask(__name__)
@app.route('/')
def main(request=None):
logging.info("starting job")
input_date = ''
if request:
input_date = request.args.get('input_date', '')
if not input_date:
input_date = (datetime.datetime.now() - datetime.timedelta(days=1)).strftime('%Y-%m-%d')
parameters = {
'date': input_date
}
pm.execute_notebook(
'covid_stats.ipynb',
'/tmp/covid_stats_out.ipynb',
parameters=parameters
)
logging.info("job completed")
return 'ok'
if __name__ == '__main__':
import os
port = os.environ.get('PORT', '8080')
app.run(port=port)
paper millexecute _ notebook函数的输入参数为输入输出笔记本以及笔记本“参数”。我将输出笔记本保存到“/tmp”文件夹中,因为在谷歌云无服务器产品中,“/tmp”通常是唯一可以写文件的地方。
2.在笔记本中设置参数
为了让 Papermill 识别笔记本中的输入参数,需要创建一个包含参数的特殊单元格。需要做的第二件重要的事情是为这个单元格添加标签“参数”。根据您使用的是 Jupyter 还是 JupyterLab,这可以通过多种方式实现:
首先,选择带有输入变量的单元格
如果使用 Jupyter,点击视图- >单元格工具栏- >标签。你会看到在单元格顶部的右角文本字段中你可以输入一个标签或者在单元格左上角现有的标签。

如果你正在使用 JupyterLab ,点击右上角的设置,

然后点击添加标签按钮,并输入“参数”。另一种方法是以 JSON 格式直接写入单元格元数据。

3.部署
正如我在开始时提到的,有几个选项可以部署这些代码。由于这是一个简单的、不需要太多时间和资源消耗的任务,我们可以在 Google Cloud 上部署基本上任何无服务器的计算产品。
云函数
用于部署的 bash 脚本可能如下所示:
#!/bin/bash
# set GCP_PROJECT (env) variable
CLOUD_FUNCTION_NAME=cf-jn
gcloud functions deploy $CLOUD_FUNCTION_NAME \
--project $GCP_PROJECT \
--entry-point main \
--runtime python37 \
--trigger-http \
--memory 512Mi \
--timeout 180 \
--allow-unauthenticated
云运行
部署脚本
#!/bin/bash
# set GCP_PROJECT (env) variable
CLOUD_RUN_NAME=cr-jn
gcloud beta run deploy $CLOUD_RUN_NAME \
--project $GCP_PROJECT \
--memory 512Mi \
--source . \
--platform managed \
--allow-unauthenticated
我省略了 应用引擎 ,因为它需要一个额外的配置文件:)但是部署命令是类似的。我在两个部署中都设置了未经认证的访问,这意味着它们可以从整个互联网上访问,当然在现实生活中,只允许某些帐户访问是件好事。
结论
Jupyter 笔记本通常用于处理大型数据集,因此需要大量的 RAM 内存和 CPU 资源,并为此需要专用的虚拟机。另一方面,有些情况并非如此,因此在一些 Google Cloud 无服务器计算产品上部署和执行它们非常有意义,因为它隐藏了资源供应,提供了简单的部署,并且节省了资金。
最后,在 Google Cloud 上, Cloud Scheduler 可用于定期(每天)调用带有 HTTP 请求的已部署 web 应用程序。
人工智能的生存风险:一个怀疑的视角
播客
梅勒妮·米切尔解释了为什么超人人工智能不会出现的原因
编者按:这一集是我们关于数据科学和机器学习新兴问题的播客系列的一部分,由 Jeremie Harris 主持。除了主持播客,Jeremie 还帮助运营一家名为sharpes minds的数据科学导师初创公司。
随着人工智能系统变得越来越强大,越来越多的人对其潜在的长期风险发出了警告。正如我们之前在播客中报道的那样,许多人现在认为,这些风险甚至可能延伸到与人类价值观略有偏差的超人人工智能系统对我们物种的灭绝。
严肃对待这一风险的作家、研究人员和技术专家不乏其人——他们包括像埃利泽·尤德科夫斯基、埃隆·马斯克、比尔·盖茨斯图尔特·罗素和尼克·博斯特罗姆这样的杰出人物。虽然我认为人工智能存在风险的论点是合理的,并且没有得到足够广泛的理解,但我也认为探索更多怀疑的观点是重要的。
梅勒妮·米切尔是这场争论中持怀疑态度的一方的重要声音,她非常友好地和我一起参加了本期播客。梅勒妮是圣达菲研究所(Santa Fe Institute)戴维斯复杂性教授、波特兰州立大学(Portland State University)计算机科学教授,也是《人工智能:思考人类指南》(Artificial Intelligence:a Guide for Thinking Humans)的作者,在这本书里,她通过批判性的视角探讨了人工智能存在风险的论点。她是存在主义风险对话中的积极参与者,最近参加了与斯图尔特·罗素的高调辩论,反对他的人工智能风险立场。
以下是我在对话中最喜欢的一些观点:
- 梅勒妮怀疑我们是否应该担心来自人工智能的存在风险,原因有几个。首先,她怀疑我们是否理解什么样的智能是足以创造超智能人工智能的智能——与许多人工智能风险倡导者不同,她认为如果没有对智能的正确理解,我们将无法制造真正智能的系统。
- 她持怀疑态度的第二个原因是:梅勒妮认为智力不能与社交分开。可以说,人类的大部分智力是通过社会压力进化而来的,人类智力的发展从出生开始就围绕着社会互动。因为人工智能最终将被构建为向人类传递价值,所以 Melanie 相信它们也将被“社会化”。因此,她认为,真正智能的人工智能系统可能会获得“常识”和“伦理”作为其发展的副产品,因此可能是安全的。
- 然而,Melanie 确实承认,开发能够提出危险的创造性解决方案的人工智能系统是有风险的,这些解决方案是实现人类程序员可能没有预料到或希望实现的目标的方法。虽然她同意这可能是严重风险的来源,但她不同意斯图尔特·拉塞尔(Stuart Russell)等人对人工智能风险的描述,她认为,这些人错误地认为智力可以在没有社会化的情况下发展。
- 虽然 Melanie 不认为存在性人工智能风险是值得担心的事情,但她确实同意人工智能技术在短期内可能会产生重大的非存在性风险。恶意使用、事故或人工智能驱动的自动武器系统的部署都可能给我们带来严峻的挑战 Melanie 认为有理由在此基础上呼吁更多的人工智能监管。
- 梅勒妮担心人工智能技术的发展速度,并不认为法律和监管将能够跟上。出于这个原因,她认为研究人员有更大的负担来确保他们正在尽他们所能引导人工智能技术朝着安全和积极的方向发展。
播客中引用的链接:

章节:
- 0:00 介绍
- 1:07 人工智能能力的盲点
- 9:00 人类和灾难
- 11:44 理解智能
- 17:58 人类与情感
- 23:09 存在风险
- 26:32 需要与充足
- 28:42 图灵测试
- 34:19 易受恶意攻击
- 40:02 让社会适应变化
- 44:04 总结
请查看下面的文字记录:
杰里米·哈里斯(00:00):
大家好,我是杰里米。欢迎回到迈向数据科学播客。今天的这一集会有点特别,因为我们要和 Melanie Mitchell 谈谈,她除了是圣达菲研究所的戴维斯复杂性教授和波特兰州立大学的计算机科学教授之外,还是人工智能安全领域的著名作者,也是我们在播客中探讨的许多人工智能存在风险论点的著名怀疑论者。正如你们中的许多人所知,特别是如果你是播客的长期听众,我本人实际上非常担心高级人工智能系统的存在风险,这正是为什么我发现这次对话如此有用和富有成效。和与你意见不同的人交谈总是很棒的,尤其是如果他们像梅勒妮一样体贴的话。
Jeremie Harris (00:43):
最后,如果你在听完这个播客后,想更深入地了解 Melanie 对人工智能存在风险的看法,我推荐她 2019 年的书《人工智能:思考人类的指南》和她在 Munk 辩论上与 Stuart Russell 的播客。所以没有其他办法,我会站到一边,让你享受谈话。好的,梅勒妮,非常感谢你参加我的播客。
梅勒妮·米切尔(01:05):
哦,我很高兴来到这里。谢谢邀请我。
Jeremie Harris (01:07):
你能来我真的很兴奋。你 2019 年的书《人工智能:思考人类的指南》提供了一个视角,我认为这与我们迄今为止在播客上看到的许多观点截然不同。我认为你真的是人工智能存在风险论点中最有意思的怀疑论支持者之一,这也是为什么今天你能来这里让我如此激动。现在,AI 安全是一个大话题。我认为我们可以从很多地方开始,但我想从你对我们今天在人工智能领域所处位置的评估开始,人工智能今天可以做的事情有哪些可能让你印象深刻,但也有哪些是我们在人工智能能力方面的盲点?
梅勒妮·米切尔(01:47):
所以人工智能显然在许多相对狭窄的领域表现出色,语音识别,机器翻译,我说的狭窄,是指与人类智力的普遍性相比的狭窄,对吗?因此,这些系统中的每一个都能够完成特定的任务,并且能够做得很好,但具有挑战性的事情是让机器能够将它们的知识用于一种任务,并将其应用于另一种任务,或者能够处理它们的训练数据分布之外的新情况等等。因此,这些是该领域众所周知的挑战,它们确实会产生一些人们所说的脆性,这意味着经过训练来完成某些任务的机器可能会出现不可预测的故障,非常不人道的故障,这可能会导致风险。这可能会导致风险,就像我们在自动驾驶汽车中看到的那样,例如,可能会犯错误,比如看到公共汽车后座上的一张照片,认为这是一个真实的行人,或者识别,认为一辆侧着的半挂卡车实际上只是地平线,或者其他任何不同的错误,人类不会犯的错误。当然,人类在驾驶时会犯不同种类的错误。
Melanie Mitchell (03:21):
但这只是给你一个例子,说明了让我们很难在生活中信任这些人工智能系统的风险。此外,我们还看到了对抗攻击的漏洞,这使得现在在任何生命危急的情况下使用这些系统都有些困难,因为这些系统确实存在漏洞。
Jeremie Harris (03:55):
如果人工智能技术的故障模式更像人类,您会对它印象更深刻吗?
Melanie Mitchell (04:00):
如果它的故障模式更像人类,我会印象更深刻,因为这样我就能更好地知道什么时候该相信它,什么时候不该相信它。这将使我们更容易找到如何安全使用它的方法。
杰里米·哈里斯(04:16):
正是如此。
梅勒妮·米切尔(04:16):
人类有自己的失败模式和偏见等等,但是如果你愿意,我们已经开发了一些基础设施来处理其中的一些问题。现在,我们采用人工智能系统,并试图重组我们的基础设施,以处理它们的各种故障模式。因此,如果机器能够更好地像人类一样,显然是为了…
Jeremie Harris (04:43):
这些都暗示了这些人工智能系统对世界的推理方式,这很有趣,如果这是一个合适的词的话。事实上,这些故障模式是如此不同,似乎暗示着还有别的事情在发生。也许这将是我接下来要问的问题的一个很好的基础,这是关于人工智能存在风险的论点。所以我之前提到过,我的意思是,你确实是一个众所周知的人工智能存在风险怀疑论者,尽管你肯定已经强调了这项技术的一些风险。你对存在风险的论点有什么看法?在你的世界模型中,为什么人们会关注存在风险,而这些人又忽略了什么?
Melanie Mitchell (05:22):
我对一些担心存在风险的人的理解是……这是我对他们观点的一些看法。我们不知道多久我们会得到人类水平的人工智能或超级智能人工智能,有人担心这样的系统可能会做出影响人类生存的决定。存在风险是因为他们不认同我们的价值观,或者不具备我们的常识能力。所以我们现在应该开始为此做准备。这是我对斯图尔特·罗素的书《人类相容性》的总结,还有博斯特罗姆和其他一些著名的存在主义风险人士的工作。
Jeremie Harris (06:18):
从表面上看,这听起来让我夜不能寐。你强调了很多非常有趣的地方,我认为以这种方式思考的人往往对他们的考虑不太了解,他们没有尽可能地重视。我很想听听你在这方面的想法。
Melanie Mitchell (06:34):
我对一些存在主义风险的作品中的例子感到特别震惊,我不知道该怎么称呼他们,支持者战士。他们有这样的例子,这里有一个场景,我们有一个超级智能机器,我们委托它解决气候变化的问题。它认为解决碳排放问题的最好方法是杀死所有的人类。所以对我来说,这看起来很疯狂。这就像说你可以拥有一台超级智能机器,但它没有任何常识。人类的生命对人类来说是有价值的,这是毫无意义的。对我来说,要使这些事情正交,误解了智慧的含义。
梅勒妮·米切尔(07:31):
所以这真的是…我觉得超级智能本身,这个概念很难定义,人们的意思是什么,以及是否有可能像人们所说的那样,比任何人都聪明得多。因为智能是如此复杂,如此多维。它的维度是如此纠缠不清,或者我相信,我不认为你能把这些东西分开。比如波士顿,有他所谓的正交原则,即智力与目标和价值观是正交的,我根本不相信。
Jeremie Harris (08:20):
关于你提到的 Stuart Russell 和气候变化人工智能的例子,我想知道的一件事是,它会摧毁一切。我解释他的说法的方式不一定是人工智能犯的错误会那么明显,而是系统非常复杂。一般来说,一个人开发的人工智能可以用我们想不到的方式解决问题,风险是这些解决方案有点危险的创造性,我们可能会指定人工智能要完成的目标,但有点像迈达斯国王,他触摸的任何东西都变成黄金,这是他想要的,但他最终会把他的整个家庭变成黄金,等等。
Jeremie Harris (09:00):
你最终会遇到这样一种情况,由于微妙的原因,不一定像减少二氧化碳排放那样明显,好吧,让我们杀死所有人,但是人工智能提出了一个复杂的解决方案,在解决问题的可能方法中,我想,当你根据一个指标进行优化时,这个空间中的绝大多数点实际上会导致人类的灾难。我很想听听你的想法,因为我肯定你有一些想法。
Melanie Mitchell (09:29):
我在 Russell 的论点中没有看到这种微妙之处,但我认为这是对我所说的很好的反驳。我认为我们确实看到了那种事情,不仅是机器,还有人类,那种,例如,比特币的现象,它被创造来分散货币,增强自由和隐私等等。但有一个副作用,我认为人们直到很久以后才真正理解,那就是正在进行的所有比特币采矿对环境的影响。这可能会对生存构成威胁。所以开发比特币的人,显然是超级聪明的人,我不知道他们是否预见到了这一点。所以你可以说,机器可以做类似的事情。它可以想出一个解决这些意想不到的负面影响的方法。
梅勒妮·米切尔(10:31):
但是今天机器可以做到这一点,我们看到今天发生的事情,如股票市场的闪电崩盘,以及我们给机器设定的某些目标,消极的事情可能会发生。我的反对意见更多的是关于超级智能的想法以及这意味着什么,如果…当然我们应该对机器如何运行以及我们给它们的目标如何产生不同的效果有更多的了解,但我不认为争论在于将会有一些引用的,未引用的,超级智能人工智能。我们说,哦,让我们把它释放到这个世界上,让它拥有摧毁一切的力量。我只是不认为这是对社会的直接威胁。这更像是使用这些非智能的,一点也不智能的机器,让它们拥有某种自主性。这是更大的威胁。
耶雷米·哈里斯(11:44):
对。我认为,这是两种风险类别之间的一种有趣的区别,你很擅长区分这两种问题,我认为这也是健康的,因为很容易将无人驾驶汽车的风险与人们在这一领域谈论的更多存在性风险相混淆。当谈到存在主义风险时,我想我倾向于看到的论点,确实,你是对的,来自一个固有的地方,我不想说技术乐观主义,但至少相信超级智能是可能的,这绝对是一个核心假设。是什么原因让你认为超级智能是一种极不可能的事情?这是一个公平的描述吗?
Melanie Mitchell (12:24):
嗯,我想说的是更不明确。我不认为我们在科学上非常了解智力。这个词有很多含义。在计算机科学领域有这样一种观点,我们可以把人工智能发展成缸中的大脑,如果你愿意的话,它可以从一切事物中分离出来,获得一些感官输入,然后进行一些输出。没有发展的概念,我们让孩子学习和社会互动的方式,成为文化的一部分,作为文化的一部分成长。我认为当你谈论智力的时候,你不能轻易地把这些问题分开。从某种意义上说,所有的智能,至少是我们希望人工智能模仿的那种智能,都是基于社会的。我们人类大脑大的原因很可能是为了应对复杂的社会环境。
Melanie Mitchell (13:34):
人工智能领域的人们认为,这可以从更纯粹的智能概念中分离出来。应该说,人们会说,“哦,我们将拥有这些人工智能系统。他们将会超级聪明,但是他们不会有任何我们的认知偏见。他们不会有我们的任何缺点,比如疲倦、睡觉、情绪化或诸如此类的事情。”我对智力的所有这些方面之间存在这种可分性的观点持怀疑态度。也许我错了,但我觉得我们对智能的理解还不够,无法假设存在某种纯粹的智能概念,可以被灌输或让机器以某种方式学习,这与所有其他方面都是分开的。
Jeremie Harris (14:34):
听起来你非常怀疑要达到一般的智力需要一定程度的社会化。这样说公平吗?
梅拉妮·米切尔(14:43):
这是我的怀疑。
杰里米·哈里斯(14:46):
好的。
梅拉妮·米切尔(14:46):
绝对是。一般智力也是很难定义的术语之一,因为我们并不知道自己的意思。有人说没有这回事。有些人认为不存在一般智力。人类有一种特殊的智能,或者他们有某种特定的技能,没有一般性的概念。现在,我不一定同意,但我认为这个术语本身确实需要充实。
杰里米·哈里斯(15:12):
在人工智能风险方面,有点像我们应该做什么,我想这在某种程度上变成了一个概率游戏。所以我们赋予智力需要社会化这个论点多大的概率呢?我们赋予相反的论点多大的概率,“是的,实际上我们可以用任何东西建造任何东西?”然后我们应该如何根据,我想类似于预期收益来行动呢?如果有 1%的可能性出现类似人工智能的启示录,那么,很可能这是值得采取行动的,而且这种可能性很重要。所以为了谈论概率,你对没有社交的超级智能是否可能的直觉是什么?
梅勒妮·米切尔(15:51):
当我甚至不确定超级智慧是什么意思的时候,很难去推测它。但我认为波士顿有一个定义,我不能准确引用,但它就像一个系统,能够做任何人类可以做的事情,并超越,做得更快,更好,更准确等等。所以我相信,如果没有生活在人类的社会、文化世界中,没有在其中成长,我敢打赌,不可能达到那样的人类智慧。
杰里米·哈里斯(16:44):
你认为反社会者会是一个很好的对比吗?在这一点上,我不直接说希特勒,但是阿道夫·希特勒,很不幸,可能是一个非常聪明的人,但是很明显他有各种各样的…他不像一个人应该做的那样与人相处。
梅勒妮·米切尔(17:00):
我不认为这是一个有效的观点,因为即使是一个反社会的人,如果你愿意的话,我肯定不是这方面的专家,但他们并不是没有情感。他们并不缺乏社会性。他们并不是没有像其他人类那样成长和发展。只是有些赤字。我不知道它到底是什么,但他们,像那样的人可以在社会中很好地发挥作用,可以相当聪明,但他们也可以造成巨大的破坏。但我认为这不是一个很好的机器类比。机器至少现在他们什么都没有,我的意思是,他们只是远离任何生物智能。
Jeremie Harris (17:58):
我想我只是想知道社交和关心之间的脱钩。是关心别人吗?那会是你认为是这种普遍智慧的关键部分的主要东西吗?
Melanie Mitchell (18:10):
更多的是理解,关心他人,但也理解他人的一种心理理论,理解他们的目标,理解他们的动机,理解为什么,能够预测他们,为他们建模。我想很多人都认为这就是我们人类变得如此聪明的原因,这是为了能够成功地模拟和预测我们群体中的其他人在做什么,或者为什么他们在做他们正在做的事情。这是机器所缺乏的,当然也是机器所缺乏的。关于机器的事情是,我们希望它们做需要与我们互动的事情,比如驾驶或打扫我们的房子,或者一个可怕但非常及时的例子,打我们的战争。有很多关于自主战斗机和诸如此类的东西的讨论。从根本上涉及到与人交往和理解他人。所以,我认为这是人们现在试图让人工智能系统做的事情,但这非常困难,没有人真正知道如何做好。
Jeremie Harris (19:35):
在很多这种灾难性的场景中,当你把人工智能放在盒子里时,它肯定会找到一种方法爆发出来,这是她在人工智能安全方面的经典爆发场景,这确实需要某种思维理论,就像你说的,因为你要如何操纵?我想其中一个经典的例子,可能是 Max Tegmark,可能用过这个,但是一个经过的看门人,然后被拉去帮助这个东西爆发,你需要一个看门人的思维理论来说服他们做 X,Y,Z 等等。在某种程度上,人工智能系统可以做到这一点,如果一个人工智能系统可以预测的行为,这也是模糊的,但在一些有意义的背景下,人类的中位数或类似的东西,会为你移动指针吗?如果你看到这一点,你会说,“好吧,我认为这是可以做到的,然后脱离社交方面的排序。”
Melanie Mitchell (20:22):
我们已经有了可以对我们进行某种预测的系统,对吗?
耶雷米·哈理斯(20:25):
对。
Melanie Mitchell (20:26):
我的意思是,就像我们的社交媒体平台一样,它们对我们的预测太准确了。但我认为他们是以这种联想的方式来做的,他们没有丰富的人类行为模型。我不知道这是否会改变现状。我认为这对智力是必不可少的,对在这个世界上聪明地存在和行动是必不可少的。所以我认为我们必须能够让我们的系统做到这一点。我们必须让我们的自动驾驶汽车能够预测人类行人将要做什么,或者他们为什么要这样做。我会觉得这样更冒险吗?嗯,我想是,随着系统变得越来越智能,我们必须考虑它们能自主做什么,以及我们能在多大程度上信任它们。
Melanie Mitchell (21:25):
但我想我的观点是,为了让系统能够做到这一点,我认为在某种意义上,它们必须像我们的孩子一样成长,在一种文化和社会中成长。在这样做的时候,他们会吸收自己的价值观和目标。我不认为你可以拥有这种超级智能但奇怪的被动可编程系统,这种想法,就像斯图尔特·罗素的想法,我们将拥有一个超级智能系统,但我们必须让它吸收,学习我们的价值观。嗯,有一种方法可以做到这一点,比如对孩子,对吗?那叫养孩子。但是抚养孩子需要一个有合适的架构和合适的学习环境的代理,并允许他们自主。他们不是被动的,他们是主动的,他们不只是等着模仿我们。他们有自己的自主权。
杰瑞米·哈里斯(22:38):
我想,在某种程度上,你有,让我们说超级智能系统正在兴起,我在这里用了很多引号,因为我不知道我在说什么。但不管怎样,这很符合你的观点。在某种程度上,我们有这样的系统,以这种方式培养,我想我们确实看到了类似的不端行为,同样,阿道夫·希特勒有父母,保罗[听不清 00:22:57]也有父母,你能看到一个满足所有这些标准的超级智能系统产生的存在风险吗,但只是让我们说不恰当地培养,这可能是不恰当地结合的另一种说法?
梅勒妮·米切尔(23:09):
有可能。我只是认为这是一种非常遥远的风险。我当然不认为这种存在的风险是不可能的,任何技术都是如此,对吗?但我只是认为,我们应该关注许多短期风险,而不是我认为的非常长期的风险,我们甚至不知道如何准确地描述这个问题。
Jeremie Harris (23:44):
我认为有一件事可能会发生,我想听听你对我们应该关注的近期风险的看法。我怀疑我们会在很多事情上达成一致。但对我来说,跨越近和远之间界限的东西,我认为是非常有趣的,看到人们对它的反应是很有趣的,这就是 GPT3,开放人工智能的大型语言模型,显然从那以后谷歌推出了一个更大的模型等等。但是这种缩放模型的想法似乎会带来更多的通用性,更多的灵活性,至少在用例方面。我不相信 GPT3,我不知道告诉医生做手术的步骤,但你可以想象一些[听不清 00:24:23] GPT4 系统会改善。对一些人来说,他们已经看到了这一点,好吧,看起来我们只是通过扩大现有系统的规模,并可能在其中添加一些香料和盐来增加额外的味道,就可以实现通用性。但是你看到这种情况发生了吗?这有说服力吗?你对 GPT3 的表现感到惊讶吗?
Melanie Mitchell (24:39):
我对 GPT3 并不感到惊讶。我在某些方面对它感到惊讶,因为我认为 GPT3 是一种语言模型。它没有任何丰富的世界内部模型。它不知道这个世界是如何运作的。你可以看到,通过对这个世界的质疑,许多人已经发表了关于这类事情的论文,它可以说出完全和完全的废话和关于这个世界的事实上错误的事情,没有孩子会说。我对它在生成看似连贯的文本方面的出色表现感到惊讶。那就是…我认为它是通过统计关联来完成的,就像你手机上的语音识别程序已经学习了很多声音和单词之间的统计关联。我很惊讶基于这种方法他们能做得这么好。
Melanie Mitchell (25:44):
所以我总是对这种将这些模型扩展到非常大的数据集和非常大的网络可以改善它们感到惊讶。我不相信如果你继续扩展 GPT3,让它记住所有的人类语言,无论什么,它会成为一种普遍的智能。我认为这还不够。也许这听起来很有说服力,但是它不能做我们人类能做的事情。它将无法像我们人类那样交流。所以还有待观察,但我不相信会发生。
耶雷米·哈里斯(26:32):
这是一个有趣的问题。我们能说的标准是什么,这里有实际的推理在进行?这是否足以说明一些例子,GBT3 似乎可以在训练集之外进行推断,似乎可以用三位数做数字加法之类的事情,尽管它在更多位数时会失败,但在一些情况下,它似乎很明显没有看到某些东西的组合,但它仍然可以正确地完成这些事情。但是就像你说的,有一些明显的反例,你连续给它两个事实,然后它不能以不同的方式把两者联系起来。所以你认为这个必要性和充足性的问题是我们应该讨论的重要问题吗?在我们宣布这些模型正在做一些有趣的事情之前,它们表现出一些推理能力就足够了吗,或者它们需要更健壮吗?
梅勒妮·米切尔(27:22):
我想这取决于你的目标是什么。如果你的目标是构建能够转化为产品的人工智能系统,那么对于某些事情来说,这可能就足够了。它可能不像你希望的那样可靠,但它在很多时候是可靠的,如果有人在循环中,那就太好了。但是,如果你的目标是理解智能或获得某种一般智能,这是不够的,它必须更加强大。这就带来了一个问题,我们如何判断,我们如何知道某样东西通常是智能的?我在人工智能领域的大多数同事绝对讨厌图灵测试,但实际上我比我认识的大多数人更喜欢它。
耶雷米哈里斯(28:13):
哦,酷。好吧。我很想听听你的[听不清 00:28:15]。
Melanie Mitchell (28:17):
到目前为止,我认为它的执行方式有很大的缺陷。很容易忽悠人。很容易糊弄法官。几十年来,我们在 AI 中已经看到了这一点,追溯到伊莱扎,它愚弄了很多人。
杰瑞米·哈里斯(28:31):
对于那些可能没有深入研究过图灵测试的人来说,我想大多数人对它的工作原理都有一个模糊的概念,但是你能不能介绍一下图灵测试的标准版本,也许是伊莱扎版本?
梅勒妮·米切尔(28:42):
对。所以图灵自己写了一篇关于这个的论文,提出你有一台机器和一个人,他们互相竞争来说服法官他们是人类。好吧。他们只能用语言交流,你也看不见,因为你不想让他们的样子影响你。所以法官图灵提出,在他的论文中,他认为五分钟的谈话可能会有所启发。事实证明,聊天机器人很容易就能创造出五分钟的对话,而不一定是专家判断,也不一定能骗过法官。伊莱扎是 20 世纪 70 年代发展起来的模拟精神分析学家,我想,只是用了非常简单的小模板和回复。这是最早的聊天机器人之一。实际上,人们和它交谈,他们认为它在理解他们。因为人们非常倾向于拟人化,并给他们交流的东西某种代理。
梅勒妮·米切尔(30:05):
所以我不认为那种图灵测试能告诉我们太多,除了关于人类的易受骗性。但我确实认为,我们也许能够构建出一些图灵测试的版本,它们将会告诉你更多的东西。雷·库兹韦尔和米切尔·卡泊尔之间有一个著名的长期赌注。因此,到 2029 年,一个人工智能系统是否能够通过图灵测试,他们将每个人赌 20,000 美元或类似的东西。这是很多年前,他们打赌。但是他们用非常专业的评委进行了这个极其严格的图灵测试,它会持续几个小时,你可以用许多不同的方法来探索它。我读了他们的图灵测试的全部描述,我想,有东西通过了那个测试,我会非常惊讶,但这也让我相信这个系统有一些普遍的智能。
Jeremie Harris (31:09):
事实上,你是在考虑哪些事情可以说服我?我的意思是,这是我希望在这个领域看到更多的东西,因为人们的审美偏好往往是主导因素。我是一个倾向于担心未来的人。因此,我当然会对 AGI 的生存风险感到担忧。我更容易接受这种观点。在那里看到它真的很酷。而 GPT3 的角度,我的意思是,在我看来,这似乎是一个有趣的联系到你的一些更当前的问题。因此,在某种程度上,我们担心人工智能的存在风险,这确实会将资源从当前的关注中抽离出来,我认为强调这种权衡是重要的。你是否介意列出你对当前技术使用的最突出的担忧,最严重的担忧?
Melanie Mitchell (31:54):
我的意思是,GPT3 是一个很好的起点,因为这种创造虚假媒体、虚假文本或图像、视频、音频等的能力正在以惊人的速度变得越来越好。我们已经看到这些深层的假货有了很大的改进,并且很容易欺骗人们。因此,这可能是一个真正的…我觉得这将是一个非常困难的问题,传播虚假信息,宣传,等等,很快,也许它已经开始了。所以我认为这是一个迫在眉睫的风险。而这些,创造这些假媒体的机器一般都不是智能的,它们不是超级智能的,它们只是日常的深度神经网络。所以我觉得这是个大问题。在这些系统中还有一个很大的问题,种族和性别偏见以及其他种类的偏见,很多人都在试图找出现在该怎么办。
梅勒妮·米切尔(33:09):
我认为,还有一个问题是我们对这些系统的可靠性过于信任和乐观。最近有一个委员会发布了一份重要报告,该委员会正在研究自主武器,其中包括谷歌前首席执行官埃里克·施密特和其他技术专家,他们真的觉得我们就要到了,我们几乎到了可以部署自主武器的地步,我们将找出如何证明它们是可靠的等等。我觉得这种过度的技术乐观主义可能会给我们带来一些真正的问题。所以我认为这些是更直接的风险。还有其他类型的关于隐私和监控的公民权利问题,以及许多正在发生的和与国际打交道的事情,弄清楚关于这些技术的国际法规是一个大问题。
Jeremie Harris (34:19):
实际上,我想这让我想起了另一个关于存在主义风险论点的变体,你可能会觉得更有说服力或更有趣。这只是一个想法,随着这项技术的改进,恶意行为者的破坏性足迹开始增加,我们作为一个物种对自己做可怕事情的能力就像核武器给了我们一种不公平的方式来杀死彼此,也许这最终会导致一些真正灾难性的事情。你认为这可能吗?
梅勒妮·米切尔(34:48):
当然。我认为存在对恶意行为者的脆弱性。我们已经一次又一次地看到这一点,像网络安全和在我们的汽车、电网和一切事物中部署所有这些人工智能系统和我们所有的设备,这只是让我们暴露在许多漏洞面前。
Jeremie Harris (35:08):
你如何看待【听不清 00:35:10】?你提到了监管,我认为,我非常支持我们必须以某种方式监管这个空间的想法。我想,如何做到这一点是一个复杂的问题,但也是在这个可怕的博弈论困境的背景下,你有美国,你有中国,各种其他全球大国,都在争夺主导地位,都在发展这项技术。他们中的一个当然可以后退一步说,“我们不会发展自主武器,”但当然为什么其他人?这可能是一个像人工智能政策行走一样的问题,但正因为你在这个领域做了这么多思考,你是否有任何直觉,知道在解决这个问题上可能会有一些牵引?
梅勒妮·米切尔(35:49):
这是一个非常困难的问题。正如你所说,这不是我的专业领域,但我认为,就像核武器一样,你必须使用……我们必须有条约、外交和国际压力。对我来说,这有点类似于生物工程,基因工程,这是另一个可能存在风险的来源,如果你愿意的话。我们正在努力解决如何监管这一问题,如何利用联合国和其他国际机构进行国际监管。我认为同样的事情也会发生在人工智能技术上,但这是一个非常困难的问题。
Jeremie Harris (36:42):
技术人员可以做些什么,比如研究人员,将他们的研究工作集中在你认为可能有帮助的特定方向上吗?人工智能能像解决问题一样成为解决方案的一部分吗?
梅勒妮·米切尔(36:53):
是的。我认为人们可以做很多事情,让人工智能更强大,更值得信赖,这是多维度的,但更透明。其中一个问题是,这些人工智能系统是黑匣子。很难弄清楚他们在做什么,如何证明他们和你一样。有人提议让食品和药物管理局负责算法。你如何证明一种算法是安全的,就像你对一种新药或类似药物所做的那样?这将涉及到很多突破,这将是做那种事情所需要的。但这是计算机科学已经研究了很长时间的东西,如何对算法进行验证,并证明他们正在做他们应该做的事情。在这个领域有很多研究可以做。
Jeremie Harris (38:01):
在你的书里,我想你也提到了 GDPR,也提到了解释权在其中的作用。我很想听听你对此的看法,因为我认为有些人认为 GDPR 的事情很霸道,但其他人认为这是必要的。然后,看起来你对什么是解释有了一个非常微妙的理解,并由此引发了很多哲学问题。
梅勒妮·米切尔(38:22):
嗯,这是一个困难的问题,因为 GDPR 是欧盟的法律,其中一部分说,“如果一个算法将影响你的生活,决定你是否会获得贷款,或者你是否会获得住房批准,或者你是否会进监狱或其他什么,它需要能够解释它的推理。”所以如果你有一个十亿参数的深度神经网络,很难说这个东西除了给,还有什么推理,这里是所有的权重。我就知道这么多。对吗?
杰里米·哈里斯(38:57):
【听不清 00:38:57】。
梅勒妮·米切尔(38:58):
对。这对我们没有帮助。我们不是这样理解事物的。这种解释必须适合特定人的理解。这是一个哲学问题,对吗?我不认为这对法律职业来说是一个真正的困境,如果他们要提起诉讼,说,“这个算法必须解释它自己。”那到底是什么意思?让我们找一些专家证人哲学家来谈谈解释的本质和[听不清 00:39:30]等等。会很乱的。我不知道事情会如何发展。真的很有意思。但他们谈论人工智能夺走工作,但我认为它将创造许多新的工作,因为它将创造一个全新的法律、哲学领域,一种人们评估风险的道德哲学,诸如此类。
Jeremie Harris (40:02):
我经常听到这样的说法,我想软件会吃掉这个世界,不要担心,我们会有更多的开发人员,但不可避免的是,开发人员的时间比它自动化的任务更有价值,这就是为什么它在经济上有利可图。但在这种情况下,我想我们真的发现我们必须在一个期限内完成哲学,我们必须开始加大努力。你乐观吗,总的来说,我的意思是,你沉浸在技术方面,但在人类方面,你认为我们能够及时适应我们的社会来适应这些变化吗?
梅勒妮·米切尔(40:36):
不,我的意思是,已经表明我们不能。
杰瑞米·哈里斯(40:39):
嗯,我希望能有更乐观的结果。
Melanie Mitchell (40:42):
不,不,我们还没有适应我们的社会。我们遇到了社交媒体上这种虚假信息的流行,这在现实世界中产生了极端的后果。我们还没能适应。甚至科技公司也在努力适应。他们试图规范自己。这比任何人想象的都要困难得多。光靠技术是解决不了问题的,我认为我们必须有政策和法规。但这意味着要教育一大批非技术人员,让他们能够理解这些领域正在发生的事情。我刚刚在一所法学院的课堂上做了一个客座演讲,学生们正在思考技术和法律,并意识到这在不久的将来将是一个爆炸性的领域。所以他们必须对此有所了解。
Jeremie Harris (41:45):
每次我和人工智能政策方面的人交谈时,我们都会做一些播客,我想他们称之为节奏问题。只是认为政策和监管很难跟上技术的步伐,尤其是当它驾驭这些指数曲线时。似乎在很多情况下,人们都想知道,在系统如何为政策制定工作方面,一个非常基本的界限的重新划定,只是为了使它们更具响应性。你认为这样的事情会发生吗?我们是不是必须以一种启动的速度重复我们的法律,这听起来真的很可怕,但这是你看到发生的事情吗?
梅勒妮·米切尔(42:22):
我不知道,因为法律法规似乎进展得非常非常慢。你会看到国会关于社交媒体和算法偏见等的听证会。议员们只是抓耳挠腮,因为他们一点也不明白。他们不知道它是如何工作的。
耶雷米哈里斯(42:41):
非常令人欣慰。
梅勒妮·米切尔(42:44):
所以我无法想象法律法规能跟上技术的发展。
耶雷米·哈里斯(42:53):
既然如此,研究人员的肩上似乎肩负着很大的责任,因为他们似乎是最适合做这方面工作的人。作为一名研究人员,这种想法如何影响你的注意力,你试图引起注意的事情的种类?
Melanie Mitchell (43:10):
首先,这让我意识到我作为一名技术人员所受的教育几乎完全没有关于技术的社会影响的任何内容。我认为,也许比法律法规更快的是,技术教育将尝试更多地强调社会影响。我看到全国各地的计算机科学系都在这样做,至少,伦理和社会影响正在成为课程的一部分。人们开始真正关注这一点。我不知道这会有多大帮助。我想我们会看到的。但是至少让人们意识到这些问题是非常重要的。
杰里米·哈里斯(44:04):
我想,就像你说的法律,教育进展缓慢,在某些情况下,有点像一代人的时间。希望我们会继续下去。希望我们的人工智能可以帮助我们学得更快。伙计,这就成了一整件事。梅勒妮,非常感谢。我真的很感激。我真的很欣赏你所有的见解。顺便说一句,我想推荐你的书,特别是对那些正在听播客的人,他们是你的长期听众。我们确实谈论了很多关于人工智能风险方面的事情。如果你想换个角度,我真的建议你出去买本书。它叫做《人工智能:思考人类的指南》。我想,梅勒妮,你的网站上有。
Melanie Mitchell (44:40):
他们可以找到从哪里到哪里得到它。是的,绝对的。
耶雷米·哈里斯(44:43):
好的。所以我们会在博文中提供一个链接,还会附带一个播客。梅勒妮,再次感谢你。
梅勒妮·米切尔(44:48):
谢谢。这是一次很棒的谈话。
在马登扩展审查不平等
在独家NFL 足球模拟游戏中,用户和评论家的分数是如何产生分歧的

2004 年,艺电公司(Electronic Arts)签署了一份为期 10 年的独家协议,获得了在游戏机上制作 NFL 和 NFLPA 授权视频游戏的权利。这笔交易消除了 2K 等其他发行商的模拟风格竞争。自 2015 年以来,该交易已被永久续约,最近一次续约发生在 2020 年 5 月。2015 年续签独家授权时,SB Nation 的《英里高报告》写道,“EA 体育的好消息,游戏玩家的 坏消息”。对于那些寻求沉浸式足球模拟体验的人来说,这句话可能低估了悲伤的现实。
我坚信大众的智慧能够判断许多事物的价值。为了了解每年发布的 Madden 游戏的质量,我利用了来自 Metacritic 的数据。本文中使用的数据是直接从 Metacritic 获得许可使用的。Metacritic 对专业游戏评论家和个人用户都进行评分。数据中的偏差包括缺乏用户侧的验证购买,以及由于用户和评论家分数上的“影响者”或“购买评论”而导致的潜在分数膨胀。该数据集并不完美,但足以描绘出一幅画面,说明专业评论家认为是一款完美游戏的东西与用户想要的东西之间的鸿沟越来越大。生成这些数字的数据和代码可以在 GitHub 上获得。
数据

为了获取数据,我访问了从 2001 到 2022 的《疯狂游戏》的 Metacritic 。我选择 PlayStation 游戏机在我的分析中保持一致,因为它们往往比 Xbox 有更多的评论。如果新一代游戏机在 2006 年末(发布年)上市,我会等到下一年(在这种情况下是发布年)。我这样做是为了让开发者有时间赶上新系统的性能。从 PlayStation 2 到今天的 PlayStation 5,经历了四代游戏机。生成的数据表示例如下所示,完整的表格可从 GitHub 下载:

作者提供的资料图片截图
Metacritic 颜色代码游戏分为三层:好(绿色),meh(黄色),坏(红色)。一个好的游戏被定义为得分在 75-100 之间。一般的游戏得分在 50-75 之间,糟糕的游戏得分低于 50。
这些年来马登
虽然 Madden 游戏自 90 年代中期就已经发布,但 Playstation 2 的年度发布分数从 2001 年开始(Metacritic 始于 1999 年)。

从 Madden 2001 年到 Madden 22 年的平均用户和评论家分数。彩色水平线代表元符号颜色代码。作者创建的图像。
对于 PlayStation 2 上的游戏,用户分数和评论家分数都在良好范围内。许多在 PS2 时代让游戏如此受欢迎的功能在过渡到 PS3 时代时被删除了。由于没有竞争推动创新,EA 将逐步添加回这些 PS2 功能,并将其作为前一年发布的新功能进行宣传。这是推动销售的方法,直到麦登终极团队(MUT)成熟。在过去的几年里,MUT 一直是 EA 的主要赚钱机器。MUT 在 2010 首次引入 Madden。era pre-MUT 和 with MUT 的用户和评论家评分对比如何?

箱线图比较了有和没有 Madden Ultimate Team 的时代的平均用户和评论家分数。MUT 前后评分差异有显著性(p=0.001,H=10.72,Kruskall-Wallace)。作者创建的图像。
此外,每一代新游戏机的用户评分都在下降。PS2 时代存在于一个竞争和创新的时代。PS3 时代见证了从 PS2 时代移除的功能的重新引入,成为销售的主要推动力。当 Madden 来到 PS4 时,MUT 是开发者的主要焦点,这可以通过复制/粘贴元素来证明,比如连续 5 年的完全相同的冠军庆典。PS5 上的 Madden 22 在 MUT 上遭受了同样的重视(PS5 上 n=1)。

4 代游戏机的评论家和用户评分。每一代控制台的得分差异显著(p=0.002,H=14.73,Kruskall-Wallace)。图片作者。
用户分数下降和糟糕的游戏体验的另一个因素可能归因于更加动画驱动的游戏引擎,这导致了一些非常有趣的错误和故障。在 Madden 2018 中引入冻伤引擎是因为全公司要求使用相同的游戏引擎。冻伤最初是为第一人称射击游戏设计的。冻伤引擎的引入导致了用户分数的大幅下降。

采用 frosty 引擎之前和实施 frosty 引擎之后的用户和评论家分数。发动机之间的差别是显著不同的(p=0.0039,H=8.34,克鲁斯卡尔-华莱士)
用户和评论家之间的差距

近年来,用户对《疯狂游戏》的评价大幅下降。评论家评论保持相对稳定,略微下降到一般的元评论类别。Reddit 用户 lyac8 的一项伟大研究显示,用户和评论家之间的分数差异在过去几年一直在增加,在 2019 年和 2020 年,评论家平均比平时高出约 10 分。lyac8 表示,评论家通常会给糟糕的游戏更高的分数,因为他们不想失去从工作室获得未来发展的机会。我理解这种情绪,但我认为马登分数有些可疑。2019 年,GameRant 发表了一篇报道,强调了 Metacritic 上评论家和玩家之间差距最大的 10 款游戏。一些游戏之所以会出现在这个列表中,是因为评论家的分数很高,而用户的分数虽然仍然很高,但却远远低于按比例调整后的数值。红色死亡救赎 2 就是一个很好的例子,Xbox One 上的评论家分数为 97 分,用户分数为 80 分,相差 17 分(它在 PS4 上的用户分数为 85 分)。GameRant 的文章中最大的差异是命运 2 ,评论家得分为 87,用户得分为 43,结果相差 44 分。看起来很多,但是等等,这是马登!

在 Madden 中评论家分数和用户分数之间的差异。图片作者。
GameRant 的文章写于 2019 年。我很惊讶这篇文章没有提到马登 17 或马登 1842 和 48 的惊人差异!?!分别是。然后情况变得更糟,糟糕得多。从 Madden 19 到 Madden 22 的 Madden 游戏的评论家和用户之间的得分差异已经大于 60!!!!!对于最近 4 个版本【60,62,61,63】。如果这些巨大的差异(> 40)是零星的,就像我们在 PS3 时代看到的那样,我们可以将其填补到一个变得陈旧的专营权或堆积效应(鉴于用户评论的巨大数量,我们确实看到了 Madden 21 ),然而这种一致的差异让我猜测,EA 要求元批评或“购买”评论的“好类别”级别评论略高于其他情况。
结论

从 Madden 2001 年到 Madden 22 年的平均用户和评论家分数。彩色水平线代表元符号颜色代码。作者创建的图像。
在这里,我们展示了用户和评论家之间的差距从未如此之大。代码和数据都可以在 GitHub 上找到。马登不再是每个人的足球模拟,然而,由于独家许可,它是唯一可用的 AAA 足球模拟。我个人仍然觉得每年秋天的几个小时里马登是令人愉快的;虽然由于我的人生阶段,功能的下降,或者两者的结合,我发现自己不像以前在专营权中那样投入。我的名字是科迪·格利克曼,可以在 LinkedIn 上找到我。一定要看看我下面的其他文章:
https://medium.com/swlh/exploring-college-football-salaries-dc472448684d
一般期望最大化和高斯混合
入门
EM 背后的理论和直觉以及 Python 中的例子

在统计推断中,我们希望找到给定观测数据的最佳模型参数。在频率主义者看来,这是关于最大化可能性(MLE)。在贝叶斯推断中,这是在最大化后验概率。当我们最大化似然性(通常是对数似然性,因为它具有单调变换和数值稳定性的优点,所以更容易优化)时,我们会问,

然而,如果存在潜在的变量呢?意思是如果用 θ 参数生成隐变量 z 和观测数据 x 的模型?我们需要解决表单的 MLE,

我们现在需要边缘化 z,所以我们最大化边际对数似然。让我们用一个例子来看看为什么这很难。
动机
考虑下面的高斯混合,包括 K 高斯分布,

其中,π是混合系数,表示每个分量的高斯比例。 π 具有和等于 1 的性质,即 ∑πₖ=1 。这确保了 p(x) 是一个概率密度函数。
似然函数和对数似然是,

其中θ∈{πₖ,μₖ,σₖ},k∈{1..K}
我们可以通过求导来最大化对数可能性。让我们试试这个模型的一个参数, μₖ 。我们将使用ϕₖ(xᵢ)=n(xᵢ;μₖ,σ ₖ) 为了简化下面的注释,

我们看到有一个问题。表达式∂lnϕₖ(xᵢ)/∂μₖ很熟悉,它只是单高斯对数似然的导数——我们可以用它来估计模型参数。然而,现在前面有一个权重项 πₖϕₖ(xᵢ)/∑ₖπₖϕₖ(xᵢ) ,所以这就好像我们在最大化一个加权对数似然。不幸的是,这个权重项依赖于我们也试图估计的模型参数——这就是问题所在。
我们将以不同的方式重构高斯混合,并直观地看到我们如何潜在地估计模型参数。我们将创建一个潜在变量 z∈{1..K} 表示给定的数据点来自第 k 个高斯。我们定义,

这样,条件分布、联合分布和边际分布就是,

这将高斯混合分解为潜在变量 z 和模型参数 θ 。这似乎使模型更加复杂了。但是现在,有了这个公式,我们看到,如果我们知道这些变量中的一个,我们就可以推导出另一个。
知道 θ :如果我们确切知道模型参数 θ ,就可以算出每个数据点来自哪个高斯。数据点来自第 k 个高斯的概率是,

事实上,这是我们之前导数中的权重项,是我们的 z 的后验概率。
知道 z :如果我们知道 z ,即数据来自哪个高斯,我们不再需要对所有的 K 高斯(σp(x,z) )求和来最大化我们的边际似然。相反,我们关注来自第 k 高斯的 x 的每个子集,并且可以用 MLE 估计 θₖ ,

然而,由此我们看到了第 22 条军规,即。如果我们知道数据点来自哪个高斯(隐变量 z ),那么我们就可以最大化我们的对数似然,得到我们的模型参数的估计;如果知道了模型参数,就可以计算出 z 的后验概率来估计每个数据点来自哪个高斯。
事实上,这是 EM 背后的直觉,我们将在下一节中形式化它。不是同时导出最优隐藏变量 z 和模型参数 θ ,而是轮流优化每一个,直到我们收敛。
Python 示例
作为一个例子,让我们生成下面的两分量高斯混合,
视觉上,这看起来像

观察数据的密度直方图以蓝色显示。橙色/蓝色代码显示每个数据点来自哪个高斯分量(两个分量中的一个)。图片作者。
利用已知的 θ ,我们看到得到的预测标签(颜色编码)与我们预期的相匹配,

基于已知模型参数对 z (每个数据点来自哪个分量高斯)的估计。橙色/蓝色表示第一/第二分量高斯。图片作者。
利用已知的 z ,我们可以重构概率密度函数,


基于已知 z 的模型参数估计(基于红色显示的估计参数生成的 PDF)。观察数据的密度直方图以蓝色显示。图片作者。
EM 算法
我们的目标是最大化具有潜在变量的模型的对数边际似然。因为这个表达式在对数变换中产生一个积分(或者如果离散的话产生一个和),所以我们在 z 上引入一个任意分布 q ,并应用詹森不等式来构造一个似然性的下界,

术语 F(q,θ) 包括,

表达式 F(q,θ) 是我们边际可能性的一个下界,取决于我们对分布 q 的选择和我们对 θ 的估计。为了在我们当前估计的 θ 处有一个最紧密的界限,我们想要相对于 q 最大化 F(q,θ ⁽ᵗ⁾ ) ,以导出分布 q ⁽ᵗ⁺ ⁾.可以看出,在我们当前的 θ 估计中,最紧密的界限是当我们用等式保持上述不等式时,导致最优的 q 是给定 x 和 θ ⁽ᵗ⁾.的 z 的后验分布有了这个界限,我们就可以相对于 θ 最大化 F(q ⁽ᵗ⁺ ⁾ 【T36,θ】,从而得到对 θ 的更好估计,这也是对我们边际似然的更好估计。这实际上是 EM 算法中的期望和最大化步骤。
在形式化每个步骤之前,我们将引入以下符号,

其中Q(θ;θ ⁽ᵗ⁾ ) 是给定的 θ ⁽ᵗ⁾相对于给定的zx 和 θ ⁽ᵗ⁾.的后验分布的边际似然的下界这个表达式可以被认为是预期的完全对数似然。由于 H(q) 不依赖于 θ ,相对于 θ 最大化 F(q,θ) 相当于最大化完全对数似然。
期望步骤:相对于 q 最大化 F(q,θ) 。在给定 x 和 θ ⁽ᵗ⁾.的情况下,我们当前估计的 θ 的解(即最紧密的界限)是 z 的后验分布因此,在实践中,我们需要计算,

最大化步骤:相对于 θ 最大化 F(q,θ) 。换句话说,最大化期望的完全对数似然,

EM 高斯混合
回到我们的高斯混合模型的例子,基于我们上面对 EM 的讨论,我们看到,
期望步骤:我们计算 γ=q(z) ,数据点 x 属于每个分量高斯分布的概率。 γ 也被称为责任,即每个 k 高斯对数据负责的程度。
数据点 xi 来自第 k 个高斯的概率是,

最大化步骤:我们最大化完全对数似然

为了最大化每个参数的表达式,我们取导数,

解决方法是,

其中 n 表示观察到的数据点的总数。
Python 示例
使用前面的例子,我们的 Python 实现如下,


选择 EM 的迭代,直到收敛。红线表示给定迭代的估计 PDF。观察数据的密度直方图以蓝色显示。图片作者。
上面我们展示了在选择迭代直到收敛的估计 PDF 的快照。我们得到的最终近似值与用于生成该数据的原始模型参数非常接近。
参考资料/资源
有无数的在线资源以不同的清晰程度讨论期望最大化。下面是我发现的一些特别好的,这篇文章大致基于这些,
- 科林(2015) CSE586 讲义
- Ng (2019) CS229 讲义
- 辛格(2005)15–781 转让说明
- 里奇(2019)的帖子
- 塔尔沃卡和史密斯(2019)10–701 讲座幻灯片
- 沙立子(2011)36–402 讲义
原载于 2021 年 2 月 1 日https://boyangzhao . github . io。
预期目标:黑匣子后面藏着什么?
一瞥专有跟踪数据能告诉我们什么。
注:本文是我为 2021 大数据杯 投稿。在我继续之前,我想感谢统计学家主办这次活动,并给我机会使用他们的数据集!
从 10 月到 1 月,我广泛地研究了美国曲棍球联合会(NHL)的比赛数据,并建立了各种分析模型。这些包括但不限于一个确定射门成为进球概率的预期进球模型,一个胜于替换模型,它提供了一个球员在一个赛季中贡献的价值的点估计,以及一个游戏模拟模型,我用它来预测 2021 年 NHL 赛季并获得每日获胜概率。
后两种模型严重依赖于预期目标模型,我相信这是它们最大的局限性。虽然我喜欢预期目标模型,并相信我在我可以访问的数据方面做得非常好,但它受到了 NHL PBP 数据问题的影响。这些数据不包括有价值的背景,如屏幕或射门前传球,射门坐标是众所周知的不准确,部分原因是记分员偏差,我在之前的更长的篇幅中提到过这个问题。在我的整个工作过程中,我经常想知道,如果我能够访问一个更细粒度的数据集,使我能够建立一个更准确的预期目标模型,我的模型输出会更加准确,以及哪些球员会看到他们的估计变化最大。
举一个让我特别困惑的模型输出的例子,我的工作发现,在同等强度下,帕特里克·凯恩在过去三个赛季对预期目标的孤立影响大致是平均的。这对于任何一个曲棍球迷来说都是一个极其难以调和的结论,因为帕特里克·凯恩被认为是世界上最好的进攻球员之一,即使在他的个人进球之外,他的 5 对 5 一级助攻率和高危险传球率(由 Corey Sznajder 手动跟踪)都接近联盟最高。我的假设是,帕特里克·凯恩一贯创造得分机会,这些机会具有某些因素,如投篮前的移动,这些因素没有被 NHL 的 PBP 数据所发现,但增加了这些投篮成为进球的概率。换句话说,帕特里克·凯恩做了一些很少 NHL 球员做的特别的事情,这一直导致黑鹰队在冰上投篮的预期目标值被低估。不过,这只是一个假设;我也接受这样一种观点,即他的估计不准确,因为我的岭回归错误地“归功于”他对其他球员的进攻影响,或者我的模型的输出实际上是准确的,我在追逐一条红鲱鱼。但是没有追踪数据,我只能假设。
当我读到大数据杯并得知我可以访问统计运动员的跟踪数据时,我毫不怀疑我所做的任何事情都将涉及预期目标。坦率地说,考虑到两个数据集之间 53 场比赛的总样本量,这个数据集可能更适合于更系统化的微观层面的分析,如“在力量比赛中从那个点投篮是理想的,还是应该总是传球?”我可能没有用我能得到的数据做“最好的”事情。但是没有什么比预期目标更让我兴奋的了,所以我开始回答这个问题:访问统计运动员的跟踪数据会在多大程度上改善预期目标模型,以及由于它的问题,我们应该对从 NHL 的 PBP 数据中导出的预期目标模型的输出应用多大的不确定性?
我选择专门处理潜在客户数据集,因为样本大小大约是原来的 3 倍,而且我不喜欢合并这两个数据集。我最初试图使用极端梯度提升从前景数据集建立一个预期目标模型,这是一种超高效的机器学习技术,我用于我的 NHL 预期目标模型,但我得出的结论是,40 场比赛中 3808 次未受阻投篮尝试(扇形投篮除外)的样本量不足以进行极端梯度提升,逻辑回归会表现得更好。为了避免过度拟合,对于数据集中的每一个游戏,我都从数据集中去掉一个目标游戏,用逻辑回归在另外 39 个上训练一个预期目标模型,然后在目标游戏上对它们进行测试。
以下是我用来建立模型的变量,以及对所有 40 场比赛进行逻辑回归得出的系数估计值:

影响进球概率最大的因素是是否在空门射门,这很有意义;没有守门员在网上更容易得分。虽然独家的运动员信息,如射门是否是一次性的,是在皇家道路传球后发生的,还是伴随着交通事故,都显著增加了进球概率,但这些都没有像之前的事件是否是失误射门那样增加那么多;我们仍然可以从 NHL 的 PBP 数据中得到一些东西。
这些系数可能会有点误导,因为与大多数变量不同,射击距离和角度是具有广泛观察数字的连续变量,所以这些数字减少一个只会略微增加进球概率,但这是实际上对进球概率有最大影响的两个因素,正如他们对 NHL 的 PBP 数据所做的那样。
对于在所有情况下拍摄的照片,该预期目标模型的曲线下面积(AUC)为 0.787。它看起来是这样的:

截至 1 月 31 日,我的 NHL 模型目前在 2021 赛季的所有情况下都表现出非常相似的 AUC 0.782,与前几个赛季的表现持平。曲线看起来是这样的:

这些曲线看起来几乎相同,AUC 值的差异仅为 0.005。这是否意味着跟踪数据实际上并没有改善事情?不,一点也不。这种比较本质上是不公平的,因为 NHL 预期目标模型是通过 3624 场比赛的样本量的极端梯度提升来训练的,而 OHL 预期目标模型是通过 40 场比赛的样本量的逻辑回归来训练的。NHL 模型应该有高得多的 AUC,而事实是没有告诉我们,几乎可以肯定的是提供的额外数据会产生很大的影响。
为了在这两个模型之间进行适当的比较,我选择在相同的基础上进行比较,从 NHL 的 PBP 数据中随机抽取 2017-2018 年至 2019-2020 年的 40 场比赛,通过对这 40 场比赛进行逻辑回归来训练和测试预期目标模型,并获得该预期目标模型的 AUC。然而,我必须确保我不只是侥幸获得了特别高或特别低的 AUC 值,所以我重复了这个过程 1000 次,每次随机选择 40 个不同的游戏。从这些测试中获得的 AUC 值最终代表了一个相当正态的分布:

平均 AUC 值为 0.737,AUC 的标准偏差为 0.018。这意味着从 Stathletes 数据集获得的 AUC 比从 NHL 的 PBP 数据获得的平均 AUC 高 7%,并且我们可以 99.73%的信心声明,从该 Stathletes 数据集获得的 AUC 高于从 NHL 数据集获得的真实 AUC。
如果我们假设对跟踪数据集的访问将为更稳健的 NHL 预期目标模型的 AUC 提供相同的 7%的改善,该模型使用极端梯度推进在 3,624 场比赛中进行了训练,我们预计 2021 赛季使用 Stathletes 跟踪数据构建的 NHL 模型的 AUC 约为 0.835。然而,这个数字也有一些注意事项:
- 这些跟踪数据都来自 OHL 游戏,并正在与 NHL 游戏进行比较。有可能预测 OHL 的射门是否成为进球更容易或更难,这种差异扭曲了数据,但我怀疑如果是这样的话,差异仍然是微不足道的。
- OHL 数据的样本量只有 40 场比赛和 3808 次未被阻挡的投篮尝试,每一场比赛都以伊利水獭队为特色。这可能只是一个好或坏的 40 场比赛。
- Stathletes 提供的跟踪数据的粒度意味着,如果样本量足够大,以使梯度增强稳定,那么与 NHL 的 PBP 提供的粒度较小的数据相比,数据几乎肯定会从逻辑回归到梯度增强的转换中更多地受益于,因为机器学习比逻辑回归更适合处理不同事件之间的独特关系。换句话说,我实际上期望收益增加——而不是收益减少——通过使用这个统计数据跟踪数据使用梯度推进来训练预期目标模型。
尽管有这些警告,我们从那些可以访问跟踪数据的人那里听到的事情使得这个预测的 0.835 AUC 数字听起来是合理的。 Alex Novet 使用 Corey Sznajder 提供的跟踪数据对预期目标模型的表现进行了更稳健的测试,并报告了 0.797 的 AUC 值,但他的测试仅使用了 1,085 场比赛的样本量;不到一整季的量。根据我的经验,在梯度推进上训练的预期目标模型在多个完整赛季的样本量下表现得更好,所以我怀疑如果这个实验在更大的样本量下重新运行,就像我使用 NHL 的 PBP 数据建立我的模型时使用的 3624 场比赛一样,AUC 数字会明显更高。在另一份报告中,保罗·莫里斯说温尼伯喷气机队有一个专有的预期目标模型,“精确到大约 85%。”我假设他只是将 AUC 值大约 0.85 解释为这个意思;我以前见过别人用 AUC 做“百分比”。“虽然我通常对 NHL 高管关于其专有模型和数据的说法持怀疑态度,但我认为温尼伯喷气机队拥有 AUC 约为 0.85 的预期目标模型是合理的。
诚然,我承担这个项目是为了给本人寻找可操作的信息,而不是为其他任何人。我只是想更好地了解我应该使用公共数据对预期目标模型应用多少不确定性,答案大约与我之前应用的一样多,但现在我可以将一个有形的数字与该不确定性相匹配,并声明根据 NHL 的 PBP 数据构建的预期目标模型比使用更准确的跟踪数据构建的预期目标模型精度低 7%左右。然而,我的研究中仍然有一些可操作的见解可供他人使用:
- 粉丝和分析师在引用公共预期目标模型的输出或从这些模型中得出的数据时,应注意这个不确定性数字。
- 团队应该注意到来自美国曲棍球联合会 PBP 的数据的不确定性,如果他们还没有这样做的话,应该投资于更准确和更精细的跟踪数据。然而,他们也应该注意到,不确定性还不足以颠覆一切;如果你的团队每晚得分超过 15-10,公众预期目标模型都说你负债累累,你的分析部门说你统治了每场比赛,那么很有可能你的分析部门是错的。
- 教练和球员应该注意的是,除了射空门之外,射偏后是否射门是增加进球概率最大的二元变量;不仅仅是皇家道路通行证后的镜头,一次性的镜头,或者伴随交通的镜头。球队计划制造更多的皇家道路通行证,采取更多的一次性措施,并在网前获得更多的流量,但他们中有多少人计划错过网并利用错过投篮造成的混乱?基于我的研究结果和我从 NHL 团队中看到的,我认为他们做得不够频繁。这是我想在以后用更大的样本量更深入地探索的事情。
数据科学招聘双方的经验

作者照片
你花了几年时间接受教育,几个月时间接受技能培训,几周时间写求职申请,几天时间进行电话筛选。(至少!)最后,你被邀请参加面试。你付出了多少努力来为自己做最好的准备,这足够了吗?要诚实…
作为科学家,我们经常努力突出我们可转移的技能,或者为商业角色定制我们的学术经验。这可能会让受邀参加面试变得很困难,所以我们倾向于推迟面试准备,同时努力弥补我们的技能差距和简历。或者更糟的是,我们认为没有必要,因为我们听信了“做你自己”和“表现自然”的陈词滥调。但是认为面试是你擅长或者不擅长的事情是天真的。或者这只是那天的运气。如果面试官的问题让你措手不及,你也没什么可做的,对吧?不对。我已经意识到,知道如何为面试做最好的准备可以决定你当天的机会。“熟能生巧”是用来武装自己的更好的陈词滥调。虽然我从未听说过完美的面试,但我想利用我迄今为止在招聘双方的经历,来帮助你准备一次完美的面试。
你可能听说过,人们会在见面的前七秒钟内形成持久的判断。我想马上驳回这种哲学观点,至少在采访中是这样的。在整个面试过程中,你都在构建一幅你是谁以及你如何适合这个角色的画面。这是一个建立你的能力、技能和个性的旅程。如果你运气好,一帆风顺,那么你就能做得很好。如果你开局不利,你还有剩余的时间来弥补。即使你犯了大错;从像误解问题这样的人为错误到像笔记本电池没电这样的技术错误,你仍然可以通过展示你在逆境中的应变能力来挽救面试。但是只有通过为各种各样的场景练习,你才能给自己最大的成功机会。
展示你的优势
根据我的经验,最强的面试者会抓住每一个机会展示他们的技能和知识。他们已经做好了准备,并准备展示这一点,但他们的回答具有灵活性,保持相关性和吸引力。他们把每一行问题都当作展示自己优势的受欢迎的提示,而不是审问。他们的回答很容易,因为他们对自己有信心,这是充分准备的结果。当候选人准备好并愿意带我经历他们的经历时,我总是很感激。尽管有些人天生擅长讲故事,但我确实认为我们其他人有可能学会如何讲故事。
例如,花时间准备案例研究来证明你的技能,并写出你想抓住的要点。在学校里,我被教导通过为每个主题准备一个“观点、证据和结论”来进行论文写作。在面试中,同样的逻辑也可以运用在明星技巧上:建立情境,概述任务,描述你的行动,解释你的结果。需要向面试官展示你认识到了必要的技能,你有运用这些技能的经验,并且你知道如何从这些技能中实现价值。这一点的关键是要确保你是真诚的、有亲和力的,这样面试官才能想象出你在这个角色中的样子。试着把每个答案放在你所在的团队或工作场所的背景下,以表明你理解你在大局中的角色。如果你已经做了功课,并练习了这种说话方式,那么当他们听你说话时,他们会更容易想象你的角色。
排练你的回答
我们可能会觉得这是一种力量,让你看起来自然,而不是听起来像是排练过的,但关键是,当你紧张时,知道你想说什么真的会有帮助。想象一下,你可能正在进入一座陌生的建筑,在一个全新的环境中与陌生人见面。可以理解,这可能会让人神经紧张。所以,提前准备好你能控制的部分,这对你自己也有好处。如果这让你感到轻松,那么根据你可能会想到的各种问题,记住一些关键短语和讨论要点。如果你认为这不是自然的,不要担心,因为整个情况都是不自然的。例如,你很可能会被问到为什么申请这家公司和这个职位。知道了这一点,一个排练过的答案可以帮助隐藏紧张,避免漫无边际,同时表明你已经准备好并做了功课。这也有助于保持你的自信,因为你可以最大限度地减少一个问题让你措手不及的机会。这样做,你也将激励你的面试官对你有信心。
感同身受
试着对你的面试官有点同情心。在可能的情况下,你也想让他们感到顺利和愉快。通过练习暂停和思考问题的方法来积极倾听。大声重述,当你需要的时候要求澄清。注意你的回答的长度和相关性,注意你已经说得够多了。如果你觉得你开始东拉西扯了,停下来问问你是否应该继续。这有助于让面试官参与进来,并表明你欢迎他们的指导。或者当你不知道如何回答某个问题时,不要害怕强调它是当前的一个局限。诚实是一种非常重要的品质。另外,它显示了自我意识和谦逊。无论是面试官还是你都不会从试图掩盖真相中获益,这很少奏效,但另一种选择会给你带来好的影响。
提问
最后,认识到你在整个过程中提问的机会。面试毕竟是双向的。从你到达的那一刻起,你就应该表现出热情,对你遇到的人、他们的团队活力和工作场所表现出兴趣和好奇。可能会很累,但是很有价值。这再次表明你对这个机会感到兴奋,并且你已经花时间准备了。
虽然这些例子有些笼统,但我希望它们能帮助你认识到扎实的面试准备的好处。和任何个人技能一样,它很难衡量,需要时间来适应。但是,一旦你愿意花一些时间和精力去迎接挑战,有许多不同的资源可以帮助你。所以,不要只做自己,做最好的自己。你未来的自己会感谢你。
使用 Keras 和 Mlflow 的实验跟踪模板

使用 Keras 和 Mlflow 实现简单的实验跟踪工作流
当训练用于生产的机器学习模型时,我们都需要实现某种实验跟踪,以保证要部署的模型的质量和功效。
在本文中,我将向您展示如何使用 mlflow 和 Keras 实现一个简单的实验跟踪工作流。
【2021 年 12 月更新】
如果你喜欢视频,可以看看我在 Youtube 上的视频:
台阶
为此,我们需要:
- 导入依赖关系
- 用 mlflow 设置自动日志
- 设置要跟踪的超参数
- 设置数据
- 建立模型
- 训练和测试
- 使用 mlflow 浏览器用户界面可视化实验
导入依赖项
import numpy as np
from tensorflow import keras
from tensorflow.keras.datasets import reuters
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Activation
from tensorflow.keras.preprocessing.text import Tokenizer
import mlflow.tensorflow
使用 mlflow 设置自动日志
mlflow.tensorflow.autolog()
设置超参数
在本例中,我们进行了一个非常简单的实验,其中我们只改变了用于训练模型的时期数和批量大小。
epochs = 10
batch_size = 16
设置数据
对于我们的数据集,不使用 MNIST 或其他一些经典的人工智能数据集,让我们保持简单,只使用sklearn.datasets.make_blobs函数创建一个假的二进制分类数据集。
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
X,y = make_blobs(n_samples=1000, n_features=2, centers=2, random_state=42)
plt.scatter(X[:,0], X[:, 1], c=y, marker="o")
plt.show()

作者图片
现在,让我们将数据集分成训练组和测试组:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)
建立模型
对于模型,让我们创建一个简单的Sequential模型,其中一层有 128 个单元,最后一层运行 sigmoid 函数。
model = Sequential()
model.add(Dense(128, input_shape=(2,)))
model.add(Activation("relu"))
model.add(Dense(1))
model.add(Activation("sigmoid"))
model.compile(loss="binary_crossentropy" , optimizer="adam", metrics=["binary_accuracy"])
培训和测试
现在模型已经编译好了,让我们将它与训练数据集相匹配,为它提供最初定义的参数。
history = model.fit(X_train,y_train, batch_size=batch_size, epochs=epochs, verbose=1, validation_split=0.1)# Output:
Epoch 1/10
1/38 [..............................] - ETA: 0s - loss: 4.8758e-04 - binary_accuracy: 1.0000WARNING:tensorflow:Callback method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0007s vs `on_train_batch_end` time: 0.0014s). Check your callbacks.
38/38 [==============================] - 0s 2ms/step - loss: 4.3713e-04 - binary_accuracy: 1.0000 - val_loss: 1.9682e-04 - val_binary_accuracy: 1.0000
Epoch 2/30
38/38 [==============================] - 0s 1ms/step - loss: 3.7183e-04 - binary_accuracy: 1.0000 - val_loss: 1.6366e-04 - val_binary_accuracy: 1.0000
Epoch 3/30
38/38 [==============================] - 0s 1ms/step - loss: 3.2540e-04 - binary_accuracy: 1.0000 - val_loss: 1.4324e-04 - val_binary_accuracy: 1.0000
.
.
完美!现在让我们在测试数据集上评估模型。
score = model.evaluate(X_test, y_test, batch_size=batch_size, verbose=1)21/21 [==============================] - 0s 686us/step - loss: 1.2186e-05 - binary_accuracy: 1.0000print(f"Loss: {score[0]}")
print(f"Accuracy: {score[1]}")Loss: 0.002690241439267993
Accuracy: 1.0
由于所使用的假数据集的简单性质,我们的模型具有不寻常的高准确性。
使用 mlflow 浏览器用户界面可视化实验
现在我们可以对这个实验进行更多的变化。在这种情况下,我将使用不同的时期数和批次大小再做几个例子:
- 实验二:历元= 20;batch_size=32
- 实验三:历元= 30;批处理大小=16
现在我可以在终端中运行这个命令,
mlflow ui
并获得以下内容:
[2021-11-20 18:42:20 +0000] [105270] [INFO] Starting gunicorn 20.1.0
[2021-11-20 18:42:20 +0000] [105270] [INFO] Listening at: http://127.0.0.1:5000 (105270)
[2021-11-20 18:42:20 +0000] [105270] [INFO] Using worker: sync
...
现在,我们可以访问 mlflow 的用户界面来比较这些实验:

作者图片
我们可以选择我们的实验并点击Compare按钮,这将显示:

作者图片
如我们所见,mlflow 存储了不同的参数组合,我们可以探索一些简洁的可视化方式:

作者图片
我个人最喜欢的是平行坐标图,它显示了不同的参数组合如何产生给定的性能指标。
关于实验跟踪的最后思考
实验跟踪对于机器学习工程来说至关重要,以保证良好的监控和再训练实践,以及验证在给定手边计算能力的情况下,正在使用最佳可能的模型。
使用 jupyter 笔记本进行实验跟踪的另一个很好的参考可以在这篇博文中找到:
https://neptune.ai/blog/how-to-keep-track-of-deep-learning-experiments-in-notebooks
在本文中,我展示了如何使用开源的 Mlflow 框架实现一个简单的实验跟踪工作流,并使用keras框架作为我们的玩具示例,将其嵌入到您的训练代码中。
如果你喜欢这个帖子,加入媒体,关注,订阅我的简讯。还有,在 Twitter 、 LinkedIn 和 Instagram 上和我联系!谢谢,下次再见!😃
实验:备份
建立基础设施以推动有效分析的文化

里卡多·戈麦斯·安吉尔在 Unsplash 上的照片
在之前的一篇文章https://prischcheung.medium.com/experimentation-to-test-or-not-to-test-547b10385939中,我概述了实验文化的驱动力,以及在整个企业中巩固这种思维转变所需的分析原则。然而,是时候改变方式了——在通过实验推动持续学习之前,我们需要回到数据捕获和转换的基础上。
你可能会认为,作为产品开发不可或缺的一部分,应该更加重视将实验与数据基础设施相结合。当然,你会看到像网飞和 LinkedIn 这样的公司引领着他们自己的实验平台——但是小鱼呢?我想探索更小的公司如何克服开箱即用的实验解决方案的限制,并朝着建立类似的内部平台前进。
旧买旧建格言
为什么对于规范分析的许多应用,我们乐于设计从原始摄取到转换的数据,但对于实验(可以说是将描述性和预测性分析结合到智能决策中的关键),我们满足于即插即用的解决方案?为了实现将实验与内部数据基础设施相结合的力量,我们需要首先理解现有优化工具的局限性。
插入 Google Optimize 或 Optimizely 等现成的实验解决方案具有典型的开箱即用优势:易于设置、实施和洞察。但是,请考虑这些限制:
- 数据输入——开箱即用的解决方案是为跨网站和应用的客户体验数字实验而构建的。一旦您包含了任何离线发生的客户接触点(例如,通过电话转换的应用程序),那么实现就不那么简单了。您可以通过 API 配置将离线接触点作为转换上传,尽管这开始降低这些解决方案提供的易实现性;
- 数据输出——现有的实验平台因其提供的见解而蓬勃发展,即主要围绕实验分析(这是正确的)。但是,只有从平台导出事件数据,才能查询实验数据以进行其他形式的高级分析和可视化。
坦率地说,用来自其他数据源的输入来设置您的实验解决方案,然后导出这些数据输出以供进一步分析,您还不如将实验构建到您自己的数据堆栈中。不仅如此——将你的实验数据与你客户的数据端到端地整合不仅有益于你的实验文化,而且丰富了你对客户的整体看法;实现更敏捷的分析和大规模个性化。
建立实验数据基础设施
因此,如果我们要为有效的实验文化培养一个迭代的、敏捷的环境,我们需要一个将实验解决方案集成到我们的数据基础设施中的框架。尽管这个框架需要与支持随机分配、运行并发实验和变体检索请求的后端一起考虑——鉴于这一点已经得到了更广泛的探索,我想把重点放在这个框架中与数据相关的组件上。有五个关键组件:
- 数字分析测量计划—制定关键用户参数(例如,收购来源:他们是新客户还是现有客户?他们是否被特定的营销渠道所驱动?)和发生在您的数字世界中的事件接触点。请确保您跟踪的用户 ID 可以跨多个会话连接单个用户的事件;
- 端到端数据蓝图—一旦实施了您的测量计划,下一步就是将您的数字环境与离线的客户接触点连接起来,使用个人 ID或类似的东西作为您整个服务蓝图的唯一密钥。这将允许您为每个人规划整个旅程,实现客户的单一视图;*
- 实验数据模型——概述要收集的数据,以了解一个人接触到的每个实验和变量,以及它们是否转换了主要和次要指标。然后将其映射到数据蓝图的其余部分,并自动接收和转换这些数据以进行分析;

概述要收集和绘制的实验数据的关系图[图片由作者提供]
- 前端实验界面——开发一个工具,分析变量之间的统计显著差异。其中一部分包括当样本量较低且统计显著性难以实现时,浮现贝叶斯分析的原理。有效地可视化这种分析对于推动实验文化也是至关重要的,这样任何人都能够理解结果并采取行动;
- E2E 实验战略——与任何其他产品一样,数据产品除非被最终用户采用,否则是不成功的。培训团队如何使用实验界面,开发和维护 ideas 注册表,这些都是培养跨职能实验势头的一部分。
成功的实验分析为繁荣的测试和学习文化铺平了道路,它依赖于将实验数据与数据基础设施的其余部分集成的框架。将你的实验数据的收集、转换和分析与你的客户数据的其余部分整合起来,最终使得实验被植入你的组织的文化中,成为你的产品策略的关键。
- 在这篇文章中,我们面对的是一个真实的人,也就是有心跳的人。如果你是 B2B 交易,这可能看起来不同。
浓缩咖啡用透明移动式过滤器的实验
咖啡数据科学
多个奇怪的实验
我发现我可以用 Kompresso 作为一个透明的移动式过滤器,我做了一些实验来了解镜头里发生了什么。这非常有趣,我决定把这些实验放在一起,以便于观看。我的希望是,它们将催生更多我自己和其他人的实验。
下面是我最近的六个实验:
- 显示了通过气流的通道
- 断奏击球和预灌注中的通道
- 完整的三层断奏镜头
- 咖啡中的气泡
- 有意通灵
- 侧沟可视化
显示通过空气的通道
当我注意到一些特别有趣的事情时,我以这种方式进行了几次拍摄。当我拔出活塞时,空气似乎会流过特定的区域,这是阻力最小的路径。
这是一个图像,其中冰球的上半部分和下半部分是不同的研磨。下半部分是更精细的研磨。随着画面的进展,我把活塞拉出来,在右下角,一个小树开始形成并展开。

左边的另一个也开始,然后很快,通路打开,几乎像玻璃破裂和蜘蛛网一样,直到上半部分分开。
断奏镜头中的通灵
我对浓缩咖啡的热爱一直是断奏。这是一个三层的镜头,我想看看地面是如何被水影响的。我做了一个特殊的,只集中在精细层和未筛过的层上,上面有一个小的粗糙层,以限制水流入的通道。

您可以在预输注过程中看到一条黑线,这条线一直延伸到预输注结束。深色垂直线是可见度,表示圆盘中的流动较慢或缺乏通道。
这在下面继续,在水平穿过镜头的线中,小的不平坦变成了更大的缓冲器。

有一个有趣的颜色变化,再一次,有许多小的深色垂直线显示水不动的地方。
让我们放大。

让我们靠近一点。

你可以在黑线下面看到,水没有像周围区域那样渗透到咖啡中,这些开始对流动产生影响。
完整的三层断奏镜头
在这里,我有一个正常的断奏镜头,像往常一样组织。然而,这个镜头是在我使用纸质过滤器帮助水不要自己引起沟流之前。
有趣的是,当水位线碰到细粒层时,由于水的涌入,不均匀的水流开始变得均匀。这种方式的精细层有助于纠正错误。

粗糙层中另一个有趣的部分是气泡。Tt 在粗糙层中最明显,但是在所有层中都有气泡。其中一部分来自于二氧化碳的释放,但是大量的空气只是来自于咖啡磨粒之间的气穴。
气泡
我看了更多的镜头,气泡开始越来越突出。这是一个中间有纸过滤器的镜头。它有助于使流动均匀,但气泡更有趣。

放大后,我们可以看到气泡非常大,遍布整个冰球。这些照片显示了气泡被压缩和释放。

有意通灵
如果我们用牙签引起有意通灵会发生什么?你会认为频道会填充一些,但这应该会给出更清晰的数据。

河道很快就被填满了,但是到最后,水位线基本上恢复了。原始洞周围仍然有一个黑暗的区域,很可能是从周围区域提取的不多。


侧沟可视化
我想更好地了解侧沟,似乎光滑的金属为水提供了更快的路径。所以我在透明的 portafilter 中间放了一片金属铝箔来模拟过滤篮的侧面。



起初,我以为实验失败了,因为我看不到水顺着金属片往下流。然后,底部出现了圆点,这表明水沿着金属找到了内部的通道。


这甚至向上扩展,直到水位线赶上它。
然后,我切开冰球,看看我是否能看到黑点,表明强烈缺乏水流。




在冰球的一半比另一半更暗的意义上,有很大的沟道效应。这很可能是由金属片略微倾斜造成的。
当我最初购买 Kompresso 时,我只是想得到一台更好的旅行浓缩咖啡机。我不认为我会得到一个实验平台,看到比我曾经认为我想看到的更多的浓缩咖啡。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以关注我中。
我的进一步阅读:
专家揭穿数据科学的神话
来自数据科学混合播客专家的替代假设

在 Data Science Mixer 播客中,我总是会问我们的专家嘉宾同样的问题,关于我们的“替代假设”部分:
“关于数据科学,人们 认为 是正确的,或者关于成为一名数据科学家,你发现 不正确的 是怎么一回事?”
客人对这个问题的回答多种多样,引人入胜。他们很乐意打破一点数据科学的神话,并根据经验提供深思熟虑的理由来反驳传统智慧。
以下是我们最近几集的部分回应,更多的还在后面。请务必查看完整的剧集,了解这些杰出的数据科学专家的更多信息!(更新:我们已经为你的阅读乐趣准备了第二本的“另类假设”!)
泰莎·琼斯 数据科学总监卡利戈
你最终构建了很多非常酷的东西,但最终,它们并不总是对业务非常有用。
我可能有五个不同的答案,因为我认为对数据科学有太多的误解。
一个主要的误解是,数据科学家的主要技能是能够编写机器学习算法,而不是为业务成果构建机器学习解决方案。那是两码事。然后我会谈到光谱的另一端:我听人们说过,数据科学家是一个真正擅长从不同的来源提取数据,并制作酷的视觉效果,使数据对业务利益相关者有意义的人。我已经看到数据科学家在这两种情况下的定义,我觉得这更像是两者的融合。
似乎过分强调了了解要运行的包的细微差别:“哦,这是用于构建 XGBoost 模型或构建集群模型的最佳包。”不太强调什么时候重要,为什么重要,或者科学的细微差别。…如果没有这个组件,你最终会构建出很多非常酷的东西,但最终,它们并不总是对业务非常有用。…不幸的是,它们并不总是被欣赏,因为这些世界之间的联系并没有建立起来。
玛戈 ,斯坦福大学教授、 数据科学女性的联合创始人
如果你认为天生的能力真的非常非常重要,而且你认为你不具备这种能力也是因为你是女人——那么,这种组合真的是致命的。
一个神话是,要成为一名成功的数据科学家,你必须有真正强大的先天能力。许多人认为,如果你没有很高的天赋,你永远不会成功。现在,我并不怀疑拥有高水平的天赋会有所帮助。但是有这样一个神话,没有它,你不可能成功。这是错误的,因为它忽略了人的成长方面。
第二个神话对女性来说非常非常不幸,那就是这种天生的能力在男性身上比在女性身上更普遍。这是经过几十年的揭露和证明这不是真的。尽管如此,许多人相信它,许多妇女也相信它。如果你认为天生的能力非常非常重要,而且你认为你不具备这种能力也是因为你是女人——那么,这种组合真的是致命的。
他们两个都被揭穿了,但他们仍然导致许多女性放弃或不进入这个领域,因为她们觉得自己没有这个能力。如果你进入了一种有点不同的文化,你感到有点被冷落,很容易得出结论,你确实不具备所需的条件。
克里斯汀·沃纳 数据科学与工程总监,雪花
有些人类观察到的事情是我们无法用数学和统计学解决的。
有一种感觉,如果你雇佣一个数据科学家…哦,伙计,他们会建立一个模型,告诉你下一步该做什么。我明白了。人们谈论预测性和规范性分析,并加大从人工智能到人工智能的时髦词汇的赌注。
如果你雇佣一个数据科学家,他们会通过合作来帮助你解决问题,也许这需要一个模型。但无论如何,它总是需要后退一步,分析和解释该模型——弄清楚它与业务的关系,并与业务互动以获得反馈。有些人类观察到的事情是我们无法用数学和统计学解决的。仍然有这种期望,即数据科学中有一些不存在的固有魔力。
为了篇幅和清晰起见,我们对这些回复进行了略微编辑。
在 Alteryx 社区 上可以获得数据科学混合器播客的展示笔记和完整文稿。
专家揭穿(更有!)数据科学的神话
更多来自数据科学专家的另类假说混搭播客

照片由塞尔吉奥·卡普齐马蒂在 Unsplash 拍摄
机器学习是数据科学家日常工作的实际重点吗?成为一名数据科学家,你需要学习所有的东西吗?最重要的是:数据科学家有幽默感吗?
在 Data Science Mixer 播客上,我总是在我们的“替代假设”部分问我们的专家嘉宾同一个问题:“关于数据科学或作为数据科学家,人们认为正确的一件事是什么,而你发现它是不正确的?”(一定要看看被揭穿的神话的第一次综述。)
令人惊讶的是,我们总能得到新的回应。似乎有足够多关于数据科学的神话,总有一些新的东西需要我们的客人来强调。
看看我们最近几集的回应。看到这些专家从他们在数据科学方面的经验中强调了什么是很有趣的。
Danielle Lyles 博士,数据和评估科学家,科罗拉多大学博尔德分校
机器学习部分,当你理解的时候,其实很容易,也很快。
人们经常认为正确的一点是,如果你是一名数据科学家,你只是一直在做机器学习,仅此而已。我要说的是,我已经做了大量探索性的数据分析,也对外部公司建立的模型做了质量分析。
机器学习的部分,当你理解的时候,其实很容易,也很快。难的部分是理解和学习数据,正如我的老板所说…有时被称为清理数据!但这实际上是关于理解它和学习它——当你在那里的时候,你最终也要清理它。
Robbie Booth,Veritone 认知人工智能引擎高级总监
当你在噪音中寻找信号时,你的噪音不可能是垃圾。
对我来说,数据科学仍然不是魔法。不同的公司有不同的成熟度。我们会和一家公司谈,他们会有以极低的比特率录制的音频,带有大量的背景噪音。你几乎听不懂别人在说什么。人们期望你能从中获得准确的转录。
很明显,有些事情你可以做。可以跑一堆降噪。你可以试着放大一点信号。但最终,它是一种有人编写、操作、转化为模型、训练的算法——因此,当你在噪音中寻找信号时,你的噪音不可能是垃圾。
我想说的是,大部分时间都有一堆腐败的东西,你对此无能为力。我认为处理这些类型的数据源将是一件真正的事情——比如粒状视频馈送。在未来相当长的一段时间内,这将成为现实。
helio campus 数据科学总监 Renee Teate
学习很多基础知识,在特定领域学习很多深度——但是你不能也不应该期望在所有领域都有深度。
我看到的最大误区是,人们认为在你可以称自己为数据科学家之前,你需要学习一切。而且也没有什么都学的。我这样做已经很多年了,我书签上要学的东西越来越多,而不是越来越少。学习基础知识,理解如何评估模型,理解你正在做的事情背后的统计数据都非常重要。理解进入模型的数据并拥有该领域的知识非常重要。
但是你不需要知道每一个技巧。你可以找到你想变得擅长的技能的子集。如果你想进入某个行业,确保你拥有该领域的知识并专攻该行业。
所以学很多基础,在某个特定领域学很多深度,但是你不能,也不应该,指望在所有领域都发展深度。不可能的。你不需要征服所有这些。
Alan Jacobson,Alteryx 首席数据和分析官
我们意识到拥有计算器可能意味着更少的错误,而不是更多。
我经常听到一种观点,认为我们无法用数据科学工具和能力来武装知识工作者-不知何故,为人们提供工具会导致他们给自己的业务带来损害和混乱。我有时会从 IT 团队那里听到这种说法。我有时甚至从数据科学团队那里听到这种说法。我发现很少有例子能证明这是真的。
事实是我们在这里谈论的是数学。这就像在想计算器刚出来的时候。可能有人会说,“我们不能给人们计算器。他们可能会犯可怕的错误,因为他们不理解计算器内部的数学运算。我们应该让他们继续手工计算,就像会计使用算盘一样。”我们大多数人现在使用计算器,并且已经用了很长时间了。我们意识到拥有计算器可能意味着更少的错误,而不是更多。不是说用计算器不会出错。但是你用计算器可能会比用算盘犯更少的错误。我认为数据科学非常相似,有太多的人担心它。
Abhishek Gupta,蒙特利尔人工智能伦理研究所创始人兼首席研究员,微软机器学习工程师
我们需要记录我们从所有这些所谓的“繁重工作”中获得的真正价值
很多人认为数据科学只是为了研究,为了发现新的模型。这并不是说这种情况不会发生,但在进入建模阶段或数据预处理之前,整个过程中还有很多工作要做。人们有一种观念,认为这是繁重的工作,也许他们会因此感到沮丧,尤其是那些刚进入这个领域的人——他们称之为繁重的工作占了 80%或 90%。
我们需要开始一点点揭穿这一点,对我们的生活变得更加透明——记录我们从所有这些所谓的“繁重工作”中获得的真正价值这是我发现比尝试一大堆模型更多的见解的地方。只是真正地获取数据,看看哪里缺少信息,为什么要做出某些假设,这些数据是如何获取的。
CSL Behring 高级分析和人工智能全球主管 John K. Thompson
数据科学家没有幽默感。
人们经常认为数据科学家没有幽默感,他们都很古板,很书呆子气,但事实并非如此。我和一些数据科学家混在一起,他们很有趣,行为有点古怪。所以这些刻板印象开始被打破。
为了篇幅和清晰起见,这些采访回复被稍微编辑了一下。查看 Alteryx 社区 上的所有采访笔录和展示笔记。最初发表于 输入 博客。
并非所有的记忆都是生来平等的
通过过期学会遗忘。有时候,为了更好地完成一项任务,应该删除无关紧要的记忆?

介绍
F acebook AI Research 提出了一个名为 Expire-Span [1]的新架构,它可以删除在注意力机制中被视为无关信息的上下文数据。凭直觉,它应该工作得很好,因为我们不希望不相关的信息被建模和记忆,特别是当序列长度相当长的时候。让我们快速进入细节,因为 Expire-Span 训练更快,使用更少的内存,并且可以扩展到非常大的序列长度而不降低性能!
背景
注意力机制在深度学习领域掀起了风暴,发现自己出现在过多的领域,主要是 NLP 和计算机视觉。它在很好地模拟长序列方面非常出色,并且在大多数 NLP 任务中设置了基准。最近,它在计算机视觉领域取得了进展——视觉变形金刚[3]和 GANs——生成对抗变形金刚[4]和 TransGAN [5]。
这种性能是有代价的,需要大量内存和计算资源!研究现在已经从注意力机制转移到减少体系结构中注意力模块的使用,因为当用更长的序列缩放时,它在计算上二次增加。MLP 混合器向我们展示了如何摆脱计算机视觉任务中的注意力机制。我已经写了一篇关于 MLP 混合器的博客,在这里找到它。但是在 NLP 任务中去除注意力可能是灾难性的,因为注意力机制已经一次又一次地证明了它们是最好的。有几种改进的变形器,如 Adaptive-Span [7]、Compressive Transformers [8]和 Performers [9],它们试图减少较长序列的计算和内存开销。请随意阅读。研究这项研究是如何围绕注意力机制进行的是非常有趣的。
在上面列出的体系结构中,有一点是不变的,
所有的记忆都被同等对待,不考虑它们对任务的重要性
Expire-Span 的作者在着手构建一个包含变压器的架构时引用了这句话,但它使架构能够忘记与特定任务无关的事情。这将显著减少存储器和计算开销,从而产生更快的架构,有时比传统的基于注意机制的变压器架构更好。他们正在比较的架构是 Transformer XL [10],它将序列划分为块,并从一个容易获得的向量关注过去,该向量存储来自过去块的注意力信息。比较中还使用了自适应跨度和压缩变压器。
注:这篇文章建议好好理解一下注意力机制。
理念与建筑
这个时间步骤是否值得保留?如果不是,那么为什么不删除它,把更多的重点放在对任务更重要、更有价值的上下文上呢?
这是导致 Expire-Span 概念化的主要思想。交易破坏者不是吗?让我们试着用图表来理解这个想法。

到期跨度的概念化。图像学分—过期时间论文
我们可以看到一些时间步 h 1、 h 1、 h 3 等等,直到 h t 和 5 个具有到期常数 e 的令牌这个到期值' e '表示时间步的数量,在此之后上下文信息被删除。在上图中, e 2 和 e 3 不是模型中的有效信息,在时间步长 ht 时,在该特定步长 t 的计算中不考虑或参与这些信息。他们只是没有进一步添加足够的上下文信息,并被删除。
传统上,变形金刚关注所有先前的上下文信息标记,对于 n 的序列长度,计算复杂度为 O(n)。如果我们考虑一个小例句“坐在地图上的狗”,我们不想携带没有添加太多信息的单词的上下文,而是记住单词 dog 和单词 sitting 的单词 mat。这将导致复杂度小于 O(n),这也是时下追捧的热点。Expire-Span 就是这么做的,给单词“dog”和“sitting”比给单词“the”更多的时间步长。“the”的 e 值将低于“dog”的值。令牌的这种到期与固定窗口注意完全不同,固定窗口注意在序列上滑动并注意固定长度的令牌。
这与变形金刚和 LSTMs [11]都密切相关,在这两种情况下,我们关注所有的记号,而不管它们的重要性,并且聪明地分别遗忘和保留序列中的事物。我们可以称 Expire-Span 为两者的混合版本:P
到期跨度值 e 通过下面给出的公式来计算,

过期-跨度公式。作者图片
其中 h i 是记忆步长,w 和 b 是可训练参数,L 是最大跨度。这最终将决定我应该在上下文中保持多长时间。还有一个等式rti =eI(t I)。每当 r ti 值为负时, h i 就过期,可以从 C t 中删除。它在关注层中的实现方式是通过二进制掩码。我们都知道,在训练变形金刚时,有一个注意力矩阵涉及对上三角中的值进行掩蔽。这种屏蔽也扩展到矩阵的下三角形。我们还需要屏蔽那些对于注意力矩阵中的特定令牌已经过期的单元。这个二进制掩码函数 m ti 的公式如下所示,

二进制掩蔽函数。作者图片
这是不可微的,并且没有梯度来训练,因此使用了软最大化函数,公式如下所示。

软屏蔽功能。作者图片
在上面的公式中,R 是一个超参数,它是由 0 到 1 限定的斜坡长度。这将确保掩码在特定时间步长到期后线性减少。这是在训练期间调整 e 值所需的梯度。

软遮罩可视化。图片致谢—过期时间论文
还有一个 L1 惩罚加到期满跨度值 e 上,以减少对主任务没有贡献的存储器的跨度,这导致高度集中于相关信息的较小的存储器使用。
变压器现有自关注层的过跨度调节
期满-跨度机制可以被包括在自我关注层中,具有下面描述的一些小的改变。
- 自我注意的头部共享相同的底层内存,因此可以计算每层的到期时间,并在头部间共享
- 这里采用了一种并行块机制,该机制允许并行计算,并且隐藏状态值被缓存,并且可以被未来的块所关注
- 当网络在序列长度上可以成千上万地扩展时,过度适应的机会很高,这通过在训练期间随机缩短记忆跨度作为正则化因素来对抗
- Expire-Span 公式中的 L 值可以取一个非常大的数,并且可以对 e 施加很大的影响。对于非常大的 L 值,如下所示修改公式以稳定训练

更新的到期跨度公式。作者图片
结果&基准
该模型在各种任务上进行训练,如走廊任务、入口任务、指令任务(都是强化学习任务)、超长拷贝、字符级建模和逐帧处理:物体碰撞任务。我不会详细讨论这些任务,因为这篇文章会变得太长太详尽。相反,我将分享 Expire-Span 的一个亮点和一些数字,这些数字证明了 Expire-Span 的使用优于上述任务的其他架构。

Enwik8 人物级造型注意图。图像学分—过期-Span 论文
从上图中我们可以看到,模型可以记住专有名词“埃及”和“矮胖子”,如果“埃及”被单词“某处”代替,它不会被给予更高的关注,最终不会被未来的标记所关注(它被遗忘得更快)。

Expire-Span 在 Enwik8、PG-19 和物体碰撞任务上的性能基准。图像积分-过期-Span 论文
Expire-Span 几乎与 Transformer XL、Compressive Transformer 和 Adaptive-Span 的性能水平相当,时间/批量训练时间和 GPU 内存相对较低。同样,对于超长副本、走廊和指令任务,也给出了基准。在准确性、内存和可伸缩性方面,Expire-Span 比其他模型表现得更好。
我的想法
当遇到一个单词时,模型必须给出一个正确的值。expire 值可能无法有效地模拟整个文档中的第一个单词。比如说,我有一个词,它是一个名字,在文件的结尾非常重要。如果模型给出了一个有限的 expire 值,该单词的上下文信息可能会完全丢失。
有时你可能会以这样的方式推断,这个词在本文中并不那么重要。但是过了一会儿,你忘记的那个单词变得非常重要,你试图记起你第一次在哪里看到它,当时它的上下文是什么。该模型独立于未来发生的事情分配 e 值。
结论
Expire-Span 是一种架构,它可以忘记对给定数据和任务无用和不相关的信息。这导致比传统变压器更低的内存和计算要求,使架构能够扩展到数十和数千的长度!尽管消耗的内存和计算更少,但它可以匹配并且很少有情况下超过最新变压器架构的性能,如 Transformer XL、Adaptive Span 和 Compressive Transformers。该领域的研究正朝着减少或消除可伸缩性、内存和计算要求的注意机制的方向发展(训练使用注意机制的大规模语言模型所留下的碳足迹是巨大的,我们也需要考虑这一点)。
高效变形金刚:一项调查[12]是关于如何最好地跟上这些年来变形金刚领域的所有研究。我强烈推荐你阅读这份报纸。
页(page 的缩写)更多关于人工智能、物联网和生物的精彩技术博客,请访问 https://appliedsingularity.com/blog/!!!
参考
[1]并非所有的记忆都是平等的:通过过期学会遗忘:【https://arxiv.org/pdf/2105.06548.pdf】T2
[2]你所需要的只是关注:【https://arxiv.org/pdf/1706.03762】T4
[3] ViT(视觉变压器):https://arxiv.org/pdf/2010.11929
[4]生成式对抗性变压器:https://arxiv.org/pdf/2103.01209
[5]trans gan:https://arxiv.org/pdf/2102.07074
[6]https://arxiv.org/pdf/2105.01601MLP-混频器
[7]适应跨度:https://arxiv.org/pdf/1905.07799
[8]压缩变形金刚:https://arxiv.org/pdf/1911.05507
[9]表演者:https://arxiv.org/pdf/2009.14794
[10]变压器 XL:https://arxiv.org/pdf/1901.02860
[11]https://www.bioinf.jku.at/publications/older/2604.pdfLSTM
[12]高效变形金刚:一项调查:【https://arxiv.org/pdf/2009.06732.pdf
Expire-Span:忘记无关信息的新人工智能算法
人工智能新闻
脸书提出的最新人工智能算法的快速描述

图片由皮克斯拜的 Gerd Altmann 提供
5 月 14 日,AI 脸书研究团队发表了一篇关于 Expire-Span 的文章,Expire-Span 是一种类似人脑的算法,它学会忘记不相关的信息。在主论文中,作者提出:
Expire-Span,一种学习保留最重要信息,让无关信息过期的方法。
到底是什么意思?
在实践中,Expire-Span 是一种深度学习算法,它首先预测与给定任务最相关的信息,然后为每条信息配备一个截止日期,即截止日期。当日期到期时,相关信息被遗忘。
一个信息越相关,有效期越长。
这一点使得 Expire-Span 算法在内存方面非常具有可伸缩性。根据作者的说法,Expire-Span 的准确性令人难以置信地超过了最初的 Transformer-XL 算法,它是从该算法中派生出来的。
为什么 Expire-Span 效果更好?
Expire-Span 是一种神经网络,它试图像人脑一样工作,人脑只记得有用的信息,忘记其余的信息。实际上,Expire-Span 只记住执行任务所需的信息。
基本思想是为了执行任务,只需要它的上下文。
Expire-Span 是如何工作的?
想象一下,一个人工智能代理必须学会如何到达一扇黄色的门,沿着一条走廊,那里还有其他的门。许多现有的神经网络存储了寻找黄门所需的所有中间信息和框架。相反,Expire-Span 只存储第一帧,其中有任务描述(即找到黄色的门)。因此,所有中间帧都可以被遗忘,因为它们存储了不相关的信息,即学习如何找到黄色门所需的步骤。
这允许 Expire-Span 更有效地利用内存。
脸书也发布了 Python 代码来文本 Expire-Span。因此,如果你想,你可以玩它:)
摘要
在这篇简短的文章中,我简要介绍了脸书开发的最新人工智能算法 Expire-Span。
Expire-Span 依赖的概念是,为了降低内存成本,神经网络必须只保留相关信息。这是通过给每条信息分配一个截止日期来实现的,就像一瓶牛奶的截止日期一样。
举例说明:Synapse 分析

图片由米歇尔·谢提供
当我去年发布了我和 CosmosDB 的约会时,每个人都不停地问我,“下一次约会是什么时候?”但是你知道,有时候一个巴掌拍不响。Cosmos 和我决定结识新的人来扩展我们的社交网络,所以今天我决定和我们共同的朋友 Synapse Analytics 约会。
谁是 Synapse Analytics?
当 Cosmos 第一次告诉我他们共同的朋友 Synapse Analytics 时,我有点害怕。原来 Synapse 在数据世界里是一件大事。Synapse 开始做了一段时间的 SQL 数据仓库工作,但后来 Synapse 的业务真正起飞了。他们从这家名为微软 Azure 的大型科技公司获得了大量投资,并设法与 Apache Spark 、 CosmosDB 、Data Factory、机器学习和 Azure 权限建立了合作伙伴关系。无论如何,Synapse 非常忙,为大型数据项目运行所有这些大数据处理工作,因此很难安排时间进行约会。我们都同意我可以在 Synapse 的工作室见 Synapse,这样我可以更多地了解 Synapse,因为他们所做的一切都在工作室。
参观 Synapse 的工作室
由于 Synapse 业务的大规模性质,我必须先向微软 Azure 注册我的详细信息,这样他们才能在我进入 Synapse 的工作室之前发给我一个工作区徽章。这花了一些时间。我必须填写一些基本信息,然后是一些安全信息和网络信息,然后他们才会发给我工作区徽章:

图片由米歇尔·谢提供
然后我被允许进入 Synapse 在 https://ms.web.azuresynapse.net/的工作室,Synapse 是我工作室的导游,所以我戴上好奇的帽子,开始问很多问题。
我进入的第一个房间是 SQL pools 房间。在这里,我可以看到一个无服务器的 SQL 池已经设置好并准备好了。Synapse 转向我,问我:“你想设置一个专用的 SQL 池吗?”

图片由米歇尔·谢提供
我不知道什么是专用 SQL 池,所以我说,“当然,为什么不呢”,并完成了创建专用 SQL 池的过程:

图片由米歇尔·谢提供
一旦创建了专用的 SQL 池,我决定询问一下它…
“专用 SQL 池和无服务器 SQL 池是什么意思?”
Synapse 震惊地看着我,“你是在告诉我你不知道专用 SQL 池是什么意思吗?”
“很公平,我们确实对其进行了重命名,因为我们经历了几次品牌重塑……总之,专用 SQL 池就是 SQL 数据仓库。专用和无服务器之间的区别实际上是用于运行 SQL 数据仓库作业的资源。
专用意味着您创建专用于运行您的工作的资源。无服务器意味着您可以与其他人共享您用来运行作业的资源。"
我点点头,“如此专用就像有一个我可以使用的私人池,而无服务器就像有一个我与他人共享的公共池?”
“不完全是”Synapse 开始说,“如果我们不使用您正在使用的池类比,您可以考虑专用的 SQL 池,就像在游泳池预留泳道一样。这意味着在一段时间内,你只为你的游泳活动保留那条泳道,其他人不能进入你的泳道。这并不意味着游泳池属于你,有些泳道是为你保留的。

图片由米歇尔·谢提供
无服务器就像去游泳池,进入一个可用的泳道。现在,如果你开始游得非常快,他们可能会给你更多的泳道空间或者把你移到快车道,如果你开始游得慢一些,他们可能会拿走你的一些泳道空间或者把你移到慢泳道,但是没有泳道是留给你的。这有意义吗?"
是的,我认为这很有道理。但是这些 SQL 池是如何处理事情的呢?
“好问题,我们需要做的第一件事是看看 Synapse SQL 池的架构。专用 SQL 池和无服务器 SQL 池都有控制节点和计算节点的概念。
现在,控制节点和计算节点之间的区别在于,控制节点可以控制如何使用计算节点。

图片由米歇尔·谢提供
您可以将控制节点视为游泳教练,将计算节点视为游泳者。游泳教练不做任何游泳,但他们通常会想出一个策略来指导游泳者。
控制节点也是如此,他们不做任何工作,但他们知道如何让所有计算节点以最有效和高效的方式完成所有工作。"
好的,专用 SQL 池处理事情的方式和无服务器 SQL 池处理事情的方式有什么区别?
“你喜欢开门见山,不是吗?好了,既然快到午餐时间了,让我们用做三明治的比喻来描述专用 SQL 池与无服务器 SQL 池的不同之处。
想象一下,如果我走过来对你说,我们需要做 100 个三明治来喂一些非常饥饿的孩子。这是所有的三明治原料,还有一群三明治工人来帮你。现在,如果你是这个三明治制作请求的控制节点,你会怎么做?"
嗯,我会…嗯…

图片由米歇尔·谢提供
“如果您以类似于专用 SQL 池体系结构模型的方式运营,您可能会看到您拥有的三明治工人数量。假设这个数字是 10。
所以你有 10 个三明治工人。然后,你要做的是查看要求,上面说你需要做 100 个三明治。你可能会告诉你的三明治工人每人做 10 个三明治。
他们都开始做三明治,在做 100 个三明治的标准时间的十分之一内,你就可以同时完成 100 个三明治。这是分工的一种方式。
如果我们看一下专用的 SQL 池架构,它的运行方式几乎完全相同。除了三明治。我们有一个接受主请求的控制节点。控制节点运行分布式查询引擎,以确定如何在所有可用的计算节点上分发请求。然后,计算节点并行处理所有工作。"
哦,有道理。无服务器版呢?
“等一下,我正要说到这一点。
另一种方法是遵循无服务器 SQL 池架构模型。假设制作三明治需要完成大约 5 项任务,例如:

图片由米歇尔·谢提供
- 奶油面包
- 剥莴苣
- 奶酪切片
- 火腿切片
- 把它们放在一起
现在,不是让一个三明治工人完成任务 1 到 5。我们可以开始给每个工人分配“专门的”任务。工人 1 和 2 可能只负责给面包涂黄油。工人 3 和 4 可能只剥生菜。工人 5 和 6 可能只切奶酪。工人 7 和 8 可能只负责切火腿。工人 9 和 10 把所有的材料放在一起。这也是我们分工的另一种方式。
现在,无服务器 SQL pool 以类似的方式处理请求。控制节点仍然接受主请求,但是控制节点运行分布式查询处理引擎,以找出如何将这个大请求分割成较小的任务,并将这些任务中的每一个交给计算节点来完成。一些计算节点可能需要等待其他计算节点先完成它们的工作,就像工人 9 和 10 必须等待所有其他节点完成,然后才能将它们放在一起,但在最后,我们仍然可以得到 100 个完整构建的三明治,来喂饱我们非常饥饿的孩子。
说到这个,我们是不是该出去吃午饭,讨论点别的?"
当然,说了这么多三明治的话,我真的很饿了。
“我相信你还有很多其他问题,比如我们为什么要在工作室与 Apache Spark 建立合作伙伴关系。”
嗯,是的,我做了。但是首先,什么是 阿帕奇火花 我不太确定我明白你刚才说的话。?为什么有人想要建立一个数据系统来回答自己的查询?
" Apache Spark 实际上是由大约 1200 人组成的大约 300 家公司创建的,他们聚集在一起构建了这个大规模分布式大数据处理引擎,他们将这个大数据处理框架整合在一起,可以大规模处理大量的内存数据,用于数据准备、数据处理和机器学习,我认为这非常了不起。无论如何,我们决定与他们合作,让 Apache Spark 在我们的 Synapse 工作室中运行,因为 Apache Spark 需要我们工作室可以提供的强大硬件工具。他们也有一个相当大的粉丝群,因为他们最近在数据领域非常受欢迎,所以我们一起为 Synapse 开发了 Apache Spark。”
“哦。嗯,我想我还没有告诉你我们为什么存在,”Synapse 说。“简而言之,我们的存在是为了帮助用户找到问题的答案。我们不是像谷歌或阿炳那样的搜索引擎,而是允许我们的用户通过使用我们的服务和他们的数据来建立他们的大数据系统。所以我们正在做的是为我们的用户提供所有必要的工具来构建他们自己的数据系统,以帮助找到他们自己的问题的答案。”
“好吧,想想这个。下班后你回到家,想做点晚餐,但是因为你不擅长烹饪,你不小心把晚餐烧焦了,所以你叫了外卖。当你在等食物的时候,你决定放一部电影。你怎么决定看什么?”
嗯,我可能会去网飞,浏览一下推荐名单。
“没错,但是如果网飞没有那个推荐名单呢?
你必须做一些调查,找一些资源来帮助回答你自己关于看哪部电影的问题。例如,你可能会去 YouTube 看电影预告片频道,或者你可能会去一个电影评论网站看一些评论,或者你可能会打电话给一个这样的朋友看如何成为百万富翁的游戏节目。

图片由米歇尔·谢提供
现在,这些都是不同形式的数据来源,帮助你解决选择看哪部电影的问题。你可能想把这些资源保存在某个地方。可以是 YouTube 视频的网址,也可以是评论的截图,或者是记下你朋友在电话中建议的电影名称。这些都是你可以收集来帮助回答你的问题的数据来源。您可能希望将这些数据源保存在某个地方。
但你真的想看每一部电影预告片,或者在每次想找一部新电影看的时候重读每一篇电影评论吗?"
号
“准确地说。因此,您可能希望将所有这些数据源归纳到一个电影列表中,并可能将您最感兴趣的那些放在列表的顶部。将所有数据源归纳到这个电影列表中的过程称为数据处理。

图片由米歇尔·谢提供
你基本上已经把所有的数据处理成了更有用的东西,所以下次你想找到要看的电影时,你只需查询你的总电影列表,而不是做所有的准备工作。如果你愿意,你可以扔掉所有这些网址和截图,因为你可以从你的电影列表中找到你需要的一切。这有意义吗?"
是啊,有道理。
因此,组织处理数据和建立这些数据仓库系统的原因是一样的。还没到找出接下来看哪部电影的程度,但他们建立了这些数据仓库和数据分析系统,以帮助他们找到问题的答案,并做出更好的商业决策。
现在,通常情况下,他们在数据系统中处理更多的数据和更复杂的查询,所以你经常会听到一些可怕的词汇,如大数据处理、大规模并行处理引擎和框架,但它们都只是帮助你构建系统来回答查询和做出更好决策的工具。"
啊,我明白了。
“好吧,回到阿帕奇火花。Apache Spark 是一个相当复杂的组织,他们做很多事情。如果你去他们的官网,这是他们提供的描述:
*Apache Spark is a unified analytics engine for large-scale data processing. It provides high-level APIs in Java, Scala, Python and R, and an optimized engine that supports general execution graphs. It also supports a rich set of higher-level tools including Spark SQL for SQL and structured data processing, MLlib for machine learning, GraphX for graph processing, and Structured Streaming for incremental computation and stream processing.*
我知道这包含了很多技术术语,但你可以基本上把 Apache Spark 看作是一个已经提出了一个非常非常好的数据处理框架的组织,它们很受欢迎,因为它们支持相当多的流行编程语言,如 Java、Scala、Python 和 R,人们可以用它们来处理数据。他们还提出了进行机器学习和 g 图形处理的框架,我甚至不想深入研究,因为就像我说的,他们做了很多。
在某种程度上,我们只是决定,嘿,与其试图找出我们自己的框架并与他们竞争,为什么我们不与 Apache Spark 合作,利用他们的框架并在我们的工作室中运行它。不难说服执行委员会让我们这样做,所以现在我们在 Synapse studio 中完全支持 Apache Spark。"
我明白了,聪明之举。
“是的,现在这也使我们能够扩展我们的能力,因为人们不仅可以使用我们的 studio 来构建 SQL 数据仓库,他们现在还可以使用 Apache Spark 的框架进行数据准备、数据处理,甚至构建机器学习模型。在我看来,这是世界上最好的。”
那么 Apache Spark 在 Synapse 中是如何工作的呢?
“好了,我们开始告诉我们的用户创建一个 Apache Spark pool 。这个池就像一个配置文件。它允许我们的用户指定池中将包含多少节点、这些节点的大小、何时暂停池、使用哪个 Spark 版本等等。
这些节点本质上是 Spark 池中的工作节点,它们将作为 Spark 作业的一部分完成分配给它们的任务。

图片由米歇尔·谢提供
显然,节点大小取决于用户的数据处理需求,因此我们让用户能够选择。
我们也给他们其他选择,比如他们是否希望我们为他们处理自动缩放他们的节点数,所以基本上他们可以设置一个范围。假设范围是 3 到 10,这意味着池中至少会有 3 个节点,但是如果情况开始变得有点疯狂,更多的节点会自动添加到池中,直到达到 10 个节点。"
我一次只能有一个游泳池吗?
绝对不是,您可以使用不同的配置创建任意多个池
但是拥有多个池的成本不是更高吗?
“嗯,从技术上来说不是。在你开始运行 Spark 作业之前,Spark pool 不需要任何成本,但一般来说,我们建议我们的用户从小规模开始,创建一个小池来玩它,并进行测试,直到他们发现他们需要的是什么。”
那么火花池的使用怎么收费呢?
“这个问题问得好。当您想要运行一个 Spark 作业时,您必须首先连接到 Spark 池。这就是我们所说的 Spark 实例。现在,一个池可以有多个用户的多个连接,所以我们称每个连接为一个 Spark 实例。
一旦你有了一个 Spark 实例,你的 Spark 任务就交给了一个叫做 SparkContext 的东西,它位于 Spark 应用程序的主驱动程序中。把 SparkContext 想象成一个协调者,有点像之前的游泳教练。

图片由米歇尔·谢提供
现在,您的 SparkContext 负责连接到一个集群管理器,您可以将这个集群管理器看作是一个游泳代理机构,它将游泳者的工作外包出去。我们称这个游泳代理为' YARN '因为它是我们使用的集群管理器。
YARN 有它们自己的方式来分配或外包它们的工作节点给我们,所以为了简单起见,我们称它为 YARN magic 。
所以基本上,SparkContext 会去 YARN 并请求一些节点。然后,YARN 在幕后进行一些 YARN 魔术,然后返回 SparkContext 并说,“嘿,伙计,你可以使用节点 1 和节点 2”。

图片由米歇尔·谢提供
然后,SparkContext 将应用程序代码和分解成任务的作业发送到可用节点 1 和 2。

图片由米歇尔·谢提供

图片由米歇尔·谢提供
这些节点运行一个负责处理任务的执行器。节点还包含一个缓存,以便它可以引入数据并将其存储为 rdd(弹性分布式数据集)进行内存处理,这比典型的磁盘处理要快得多,因为您不必对磁盘进行太多的读写操作。
一旦任务被处理,结果就被发送回 SparkContext。
然后 SparkContext 将所有东西放在一起,就像那些三明治制作者一样,他们的任务是在最后将整个三明治放在一起。

图片由米歇尔·谢提供
SparkContext 知道如何将所有东西放在一起,因为它使用有向无环图( DAG )来跟踪任务。
无论如何,这是一种非常冗长的方式来说,我们对 Spark 实例收费,而不是 Spark 池,所以除非我们的用户正在使用我们的资源来处理东西,否则我们不会向他们收取不必要的费用来为他们保留资源。"
那么,星火池的使用怎么收费呢?
"让我们结束午餐,然后回去检查管道室."
当然。
“你以前听说过 Azure 数据工厂吗?”
不,我没有。
“哦。如果你有,我们工作室的管道室看起来很像数据工厂,那是因为我们决定不重新发明轮子。我们决定与 data factory 合作,引入他们令人惊叹的数据移动、编排、转换和集成服务,其中一些我们在工作室的 Synapse 管道下提供支持。”
管道到底是如何工作的?
“假设我们想烤一批饼干,搭配我们之前做的三明治,喂我们饥饿的孩子。现在,为了烘烤一批饼干,我们需要完成一系列活动,对吗?”
对。

图片由米歇尔·谢提供
这组活动在数据工厂术语中称为 p ipeline 。管道支持 3 种类型的活动,它们是:
通常使用的移动活动称为复制活动,它负责采购制作饼干所需的所有饼干原料,并将烘焙食品分发给我们的小饼干怪兽。

图片由米歇尔·谢提供
采购 cookie 配料是一个将数据从数据源读入数据工厂的例子,这样我们就可以将它处理成 cookie。
现在,为了引进饼干配料,这需要我们首先与一些饼干配料供应商联系。这在数据工厂中被称为链接服务。链接的服务指定了我们希望与哪个数据源建立连接。世界上有很多饼干配料供应商,所以我们需要指定我们想要联系的供应商。链接服务还用于建立到您想要使用的计算资源的链接。例如,你可以在家里或者像 Azure 这样出租的商业厨房里烘烤饼干。

图片由米歇尔·谢提供
现在,饼干的配料可能有各种不同的类型,所以我们需要具体说明,比如所有的“白色”配料是面粉,所有的“黄色”配料是黄油,所有的“液体”配料是牛奶等等。这在数据工厂中被称为数据集,因为它定义了我们引入的配料数据的结构。
一旦我们有了配料或我们的 cookie 数据集,我们就可以开始把它们变成真正的 cookie。这就是我们的数据转换活动的用武之地。
如果你是一个简单的曲奇烘焙师,也就是说,你将采取的大多数步骤都是相当标准的曲奇制作技术,你可能会对数据流感兴趣。把数据流想象成一种烘烤饼干的视觉方式。因此,cookies 指令不是以文本形式写出来,而是以可视图表的形式画出来。这就是 data factory 中的数据流允许您对数据做的事情。您可以设计数据转换逻辑,而不必编写代码。
像复制活动(一种数据移动活动)或数据流(一种数据转换活动)这样的活动需要在集成运行时(IR)上运行。"
什么是集成运行时?
我就知道你会问我这个。

图片由米歇尔·谢提供
一个集成运行时是执行活动的计算环境。你可以把它想象成让你做饼干的厨房基础设施。同样,这个集成运行时可以是你自己的自托管厨房或 Azure 管理的厨房。
好的,这里有一个问题:“如果我们有两个管道,比如说,一个三明治管道和一个饼干管道,我们想在制作饼干之前先制作三明治。你觉得我们能做什么?”
我们能否建立三明治管道,然后摧毁它,建立曲奇管道?
“不完全是。我们可以构建两条管道,然后使用我们的控制流活动来触发cookie 管道在三明治管道之后运行。”
触发?
“是的,所以通常数据工厂中的管道在分配给它的触发器被调用时运行。您也可以手动运行管道,但触发器允许您自动运行管道。例如,您可能希望使用调度触发器调度管道在某个时间运行,或者您希望使用基于事件的触发器调用管道在某个事件之后运行。
嘿,说到日程安排,我还有一个会议要赶去,所以我可能不得不在这里结束我们的约会,但如果你想了解更多,就伸出手来,我们可以为下次预订更多的时间。"
**作者:谢蜜儿
像我五岁一样解释:激活功能

这项工程由来自 unsplash.co 的 RAEeng 完成
用尽可能少的数学对激活函数进行简单易懂的解释
我最近写了一篇关于人工神经元如何工作的短文,一些人问我,我是否可以对激活功能做一些类似的事情。所以在这里,我对这个话题的两个美分!
什么是激活功能?

人工神经元及其元素的表示。Geshenson 2003。作者插图。
如果你读过我以前的文章,你还记得生物神经元在将信号传递给下一个神经元之前是如何充电的吗?我们的每个生物细胞都有一个特定的阈值,它决定神经元是否“触发”并将其信号传递给下一个神经元。在一个人工神经元中,我们通过实现一个拥有特定数学阈值的激活函数来复制这种行为。如果我们加权输入的总和超过激活函数阈值的值,信号将被传递到下一个神经元。在这种情况下,人工神经元将被认为是“激活的”。
为什么激活函数如此重要?
我说过我们会尽量少用数学。因此,我们将进行一个简短的思维实验,而不是给你一些奇特的数学方程式:
经过漫长的一天工作,你回到家,你的另一半已经准备好了你最爱吃的菜。香味太棒了!它看起来棒极了,天哪——尝起来像天堂!你大吃大喝,吃得比你能吃的还要多,你很高兴你有如此美好的生活。
那么此刻你的大脑在发生什么呢?对,负责识别这个盘子的神经元在拼命工作。如果我们试图在人工神经元网络中复制这种行为,我们可以确定神经元的激活功能,这样每当你闻到、看到或尝到这道菜时,它们就会激活。这种行为将是逐步的,这意味着人工神经元要么激活,要么不激活。
但这是对的吗?你真的总是对你最喜欢的菜有同样的感觉吗?你是否只想在生命中的某个时刻品尝这道菜?让我们在思想实验中更进一步:
你刚刚吃了一大堆食物,因为你的一个同事当了爸爸,并赞助了每个人的披萨和饮料。你回到家,你的另一半会送你最喜欢的菜。你很感激这种姿态,你喜欢这道菜,但你只是不能再吃了。
这是否意味着你不喜欢你最喜欢的菜?当然不是,它闻起来仍然很香,你知道你喜欢它,但你的胃告诉你,你不能再吃了。所以你在这一刻的感觉比爱或不爱它更复杂一点,对吗?
所以我们必须调整激活函数,让老鼠能够复制这种行为。我们可以使激活函数线性。在这种情况下,我们的人工神经网络将能够复制出你爱你的菜 a 一点点,但不是那么多,因为你现在已经吃饱了。
现在,线性函数有一个数学问题。如果你将一个线性函数的结果应用于另一个线性函数,该运算的输出仍然是线性的。换句话说,结果总是以相同的比例增长。就我们的小例子而言,这意味着即使你已经吃饱了,你也会对你的菜感到完全相同的“一点点”兴奋。这听起来对吗?让我们在思想实验上做最后一次飞跃:
你在同事的庆功宴上吃了很多披萨,但你已经一个多月没吃你最喜欢的菜了。你吃饱了,但是你非常兴奋,因为你从对你来说最重要的人那里得到了你最喜欢的菜!
你是因为吃饱了而对你的菜感到有点兴奋,还是对它感到更兴奋?没错,与之前的场景相比,你更喜欢这个场景。我们可以得出结论,我们对最喜欢的菜的感觉是不成比例的。结果,线性激活函数不再是合适的选择——我们需要切换到非线性激活函数。
那么为什么激活函数如此重要呢?没有它们,人工神经元的输出将等于其加权输入的总和。信号将总是传递到下一个神经元,并且所有神经元将一直被激活。通过使用激活函数,我们可以将阈值引入到我们的人工神经元中,阻止神经元被激活,如果它们的信息可以被认为是不相关的。激活功能还通过引入非线性使人工神经元能够解决更复杂的问题。
常见的激活函数是什么样子的?
本节将为您提供不同激活功能的更详细解释,以及它们的使用案例和限制。
逐步地
你可能已经猜到了——最简单的激活函数是所谓的阶跃函数。

阶跃函数。图片作者。
阶跃函数给激活函数增加了一个阈值(因此也称为“阈值函数”)。如果输入的加权和高于某个值,则该神经元被宣布为激活的。如果总和低于这个值,它就不会被激活。因此,这些功能仅限于二元分类问题(是或否),但不能复制更复杂的情况。
线性的
那么我们能做些什么来解决这个问题呢?正确,我们可以用一个线性函数。线性函数可以提供中间激活值,如“50%激活”或“20%激活”。通过这种方式,人工神经元网络可以区分不同的输出,并决定最高的激活值。

线性函数。图片作者。
然而,如前所述,如果将几个线性函数的输出相乘,第一次乘法的输出将增加与最后一次结果相同的数量。这不一定是个问题,但是人工神经元网络通常由多层人工神经元组成。简单来说,人工神经元网络是通过在信息通过网络时错误地计算变化来学习的(也称为 梯度下降 )。当在这样的结构中使用线性函数时,在信息通过第一层后,误差的变化将总是相同的。为什么?因为结果总是会增加相同的方式,因此误差总是会增加相同的方式。结果,使用线性激活函数使得多层网络不可能在第一层之后学习任何新的东西。
逻辑乙状结肠
你可能已经猜到了:为了克服这些问题,可以使用一个非线性函数。非线性函数的一个例子是所谓的“逻辑 sigmoid 函数。由于它们的非线性性质,逻辑 sigmoid 函数能够在人工神经元网络中堆叠无限数量的层,这使得该函数适合于解决更加复杂的问题。通过给予中间激活,它还结合了线性函数的优点。如图所示,逻辑 sigmoid 函数呈“S”形,这意味着误差的变化随着函数向 x = 0 方向变陡而增加。因此,该区域中 x 的小变化也会带来 y 值的大变化,因此有利于基于梯度下降的训练算法。

逻辑 s 形函数。图片作者。
由于其优势,逻辑 sigmoid 函数很可能是最常用的激活函数之一。然而,sigmoid 函数的缺点是在 x 定义范围的末端非常平坦(本例中为-1 和 1)。这意味着一旦函数落在这些区域之一,梯度变得非常小。结果,梯度接近零,并且网络不能学习。
sigmoid 函数的另一个问题是 y 值只能是正的因为函数不是关于原点对称的。因此,下面的神经元只能接收正值形式的输入,这(取决于问题陈述)是不期望的。如果一个网络将在例如具有变化的正负过程的价格数据上被训练,该网络将能够解释负的输入价格,但是不能在其输出中相应地将价格表示为负。****
双曲正切
然而,这个问题可以通过对 sigmoid 函数进行缩放来解决。这种类型的激活函数称为双曲正切函数。

双曲正切函数。图片作者。
双曲正切函数的工作方式类似于 sigmoid 函数,但是它的中心相对于原点是对称的。当它的范围从-1 到 1 时,它解决了正输入的问题。然而,即使它具有 sigmoid 函数的所有正面好处,它仍然遭受在曲线的平坦水平上被称为“消失梯度问题。
简而言之,它描述了这样一个问题,即较高一个输入,较小这个梯度。所以你的网络层数越多,你的跟随输入就越高/越小,因此你的梯度就越低。由于小梯度意味着网络没有学习很多,这也意味着这种函数不适合于具有大量层的网络。
热卢
双曲正切函数的替代方法是整流线性单位或“ReLu”。

ReLu 函数。图片作者。
ReLu 函数是非线性,这使得训练更加有效,并且使得多层神经元能够被该函数激活。然而,使用 ReLu 功能相对于其他功能的主要优势是它将所有的负输入转换为零。结果,网络仅激活具有正和的 nurons,这使得它的计算效率更高。然而,ReLu 函数也有消失梯度问题和,因为图形负侧的梯度等于零。因此,如果在激活期间梯度碰巧在这个区域,网络将不学习。这种现象也被称为“ ”垂死的 ReLu ”。
泄漏 ReLu
漏 ReLu 函数通过将负输入表示为函数输入的线性分量来解决垂死 ReLu 问题。因此,负输入保持,零梯度问题被移除,因为图表左侧的梯度不再等于零。

漏 ReLu 函数。图片作者。
总结时间!
激活函数在人工神经元中用于复制生物神经元的行为,方法是实现一个拥有特定数学阈值的激活函数。如果阈值超过,信号将传递到下一个神经元。在这种情况下,人工神经元将被视为“激活”。
激活功能很重要,因为它们赋予人工神经元只有在呈现的输入有意义时才被激活的能力。它们还让我们有可能调整人工神经元的学习能力,并给网络增加更多的复杂性。
有许多不同的激活功能,各有其优点和局限性。研究表明,没有一个通用的激活函数可以完美地解决所有问题。今天,有超过 27 种不同的标准激活函数可以在人工神经网络中使用。一般来说,人工神经元用非线性激活函数训练更好。然而,选择右激活函数高度取决于你试图解决的数学问题。据说 Sigmoid 和 Tanh 在分类问题的情况下工作得很好,尽管它们的渐变脆弱性消失。另一方面,使用 ReLu 或泄漏 ReLu 函数的神经网络已被证明在不同类型的问题上表现良好。而使用更专业的方法(即 ELUs、SELUs、SoftExponential 等)的网络可以胜过它们。)它们是最常用的类型的激活函数,因为它们比 sigmoid 更有优势,而且训练效率也更高。
如果你想了解更多关于激活函数、梯度下降、人工神经元网络如何学习的知识,我强烈推荐你详细看看 这篇文章 。但是要注意——这涉及到一些数学知识!;-)
来源
- 鲁兰德。2004.神经网络中的 Einführung,2–13。乌尔姆:乌尔姆大学。
- 卢哈尼瓦尔。2019.“分析神经网络中不同类型的激活函数——选择哪一种?“走向数据科学。最后修改时间 2019 年 05 月 07 日。https://towards data science . com/analyzing-different-type-of-activation-functions-in neural-networks-one-to-preferred-e 11649256209
- 古普塔。2020."深度学习的基础——激活功能和何时使用它们?"分析 Vidhya。最后修改时间 2020 年 1 月 30 日。https://www . analyticsvidhya . com/blog/2020/01/fundamentals-deep-learning-activation-functions-when-to-use-them/
像我五岁一样解释:人造神经元
一点人工智能知识花絮
不久前,我不得不阅读神经网络,发现自己被提醒数学相关的文档是多么枯燥。所以我决定写下我自己对这个主题的简单解释——让我知道你的想法!
人工神经元到底是什么?
人造神经元受到生物学的启发,试图复制我们大脑的工作方式。我们的大脑有数十亿个神经细胞帮助我们解读所谓的信号。你可以把信号想象成任何东西,从射到我们眼睛的一束光线到我们想要移动手臂的想法。神经元帮助我们以正确的方式解读这些信号。例如,当一束光线照射到我们的眼睛时,神经元会知道这束光线可以被解释为蓝色、绿色或黄色。现在,人工神经元是通过以代码的形式复制一个生物神经元来将这一原理转移到计算机中的方法。
如何用代码复制生物神经元?
你可能会问自己,怎么可能再造一个有生命的东西,并把它放进一个基于你从《黑客帝国》电影中知道的这些 1 和 0 的代码里。让我带你进一步深入兔子洞,一步一步给你解释。
生物神经元是如何运作的?

“解剖学和生理学”由美国国家癌症研究所的监测、流行病学和最终结果(SEER)项目提供,摘录自 Wikipedia.com
看一看生物神经元的插图,记下所有不同的术语,然后忘掉它们。你不需要详细了解它们,但重要的是它们的功能。神经元通常由三部分构成。第一部分是接收信号并由处理信号的细胞体。如果信号是相关的,细胞体将得到激发并充电。如果电流达到某个阈值,细胞体会允许他的兴奋转移到下一个细胞。这种转移由轴突执行,轴突通过轴突末端将连接到下一个细胞。每个神经元都有自己独特的设置,这意味着不同细胞的兴奋程度不同。当一些细胞对任何事情都感到兴奋时,也有一些细胞不兴奋,导致抑制效应。
我们如何在人工神经元中复制这种行为?
现在,让我们把这些东西分解,并尝试用计算机能理解的方式来表达它们。我们从观察我们的信号开始。无论是一束光线还是某种思想,我们都可以通过赋予它某种数值来表达这些信息。黄色可能是 1,蓝色可能是 2,绿色可能是 3。通过这种方式,我们可以让机器理解这些信息。在计算机科学方面,我们也称这些信号为我们的输入。

人工神经元输入元素的表示。Geshenson 2003。作者插图。
还记得一个信号可以让一个真实的神经元兴奋与否吗?人工神经元也有这种能力!我们可以通过引入所谓的权重来模拟真实神经元的自然属性。一个权重被乘以输入,并产生一个新的值。回到我们的颜色检测例子,我们可以设置人工神经元的属性,使它总是对黄色感到兴奋,但对绿色和蓝色不那么兴奋。以一种简化的数学方式,人工神经元用 10 的权重乘以 1,但只用 1 的权重乘以绿色和蓝色。结果,这种特殊的人工神经元总是对黄色比其他颜色更加兴奋。

人工神经元的输入和权重元素的表示。Geshenson 2003。作者插图。
现在,一个生物神经元积累所有这些信息,并给自己充电。用代码实现的人工神经元试图通过对每个输入的所有不同加权值求和来复制这种行为。我们称之为传递函数。所有新计算的加权值之和称为网络输入。

人工神经元的传递函数和其他元素的表示。Geshenson 2003。作者插图。
在一个神经元被充电后,如果超过某个阈值,它会将电释放给下一个神经元。我们可以通过向我们的人工神经元引入所谓的激活函数来实现这种行为。激活功能获取网络输入并检查其是否超过其给定的阈值。如果大于阈值,人工神经元将决定“开火”,并将他的信息传递给下一个神经元。我们称这种状态为被激活的人工神经元。

人工神经元及其元素的表示。Geshenson 2003。作者插图。
是总结的时候了!
总之,人工神经元试图模仿现实生活中我们每个人大脑中神经元的行为。我们可以通过用数字形式表达分类信息并把这个输入乘以权重来实现这种复制。这些权重的总和然后被传送到激活函数,该激活函数将该总和与其给定的阈值进行比较。如果超过阈值,该函数将激活神经元,并将其信息传递给下一个人工神经元。
激活功能是人工神经元的核心。没有它们,神经元将传递它们的加权输入,而不事先过滤信息。因此,更好地理解它们的功能以及可供选择的不同类型的激活功能是至关重要的。因为它们都有不同的属性,所以对不同类型的输入会有不同的影响。为正确的输入数据和任务选择正确的激活函数是很重要的,否则人工神经元不会产生有意义的完整输出。如果你想了解更多关于激活功能的信息,请随意查看这篇文章。
像我五岁一样解释:反向传播

尼古拉斯·卡佩罗在 Unsplash 上拍摄的照片。
对人工智能初学者主题的温和介绍
我最近写了一篇关于人工神经元网络如何学习的短文。虽然这篇文章为您提供了主要概念的简要概述,但它并没有向您解释学习实际上是如何工作的。本文将尝试回答一些未解决的问题,并通过简单解释人工神经网络监督学习最常用的方法,带您深入兔子洞:“反向传播”。
什么是反向传播?
反向传播是一种算法,它使人工神经网络根据它产生的预测误差进行学习。该算法可以总结为三个步骤:
- 输入数据通过网络向前传递,人工神经网络根据其初始权重进行预测。
- 然后将网络的输出与期望的结果(也称为目标值)进行比较。两者之间的差异被认为是网络的误差。
- 然后误差通过网络反向传递,神经元连接的权重根据它们对误差的影响进行调整。
通过这种方式,反向传播算法保证了每次学习的输出都接近期望的输出。
我们如何测量人工神经网络误差?
反向传播解决了人工神经网络的所有错误。该误差由所谓的“损失函数计算,该函数有时也称为“成本函数”。损失函数将实际值与神经网络计算的值进行比较,并计算它们的差值。这种差异就表示为网络损耗。根据网络要解决的问题,必须使用不同的损失函数。
二元分类问题是建模问题,其中示例被分配给两个标签中的一个(即是或否)。应用于二元分类问题的流行损失函数是二元交叉熵损失、铰链损失和平方铰链损失函数。
多类分类问题是指将样本分配给两个以上标签中的一个(即动物的图像识别)。这类问题常用的损失函数有多类交叉熵损失、稀疏多类交叉熵损失和 Kullback Leibler 散度损失函数。
对于回归问题(即时间序列预测)最常用的损失函数有均方误差、均方对数误差、平均绝对误差和平均绝对百分比误差。
如果你想更深入地研究其中的任何一个,我强烈推荐你阅读杰森·布朗利关于“ 在训练深度学习神经网络 时如何选择损失函数”的博客。虽然博客不仅给你更详细的解释,作者还包括实际的代码片段,我总是觉得非常有帮助。
如何根据误差调整权重?
反向传播算法本身基于所谓的“梯度下降技术,这是一种旨在优化特定函数的算法。在人工神经网络的情况下,梯度下降技术用于调整人工神经网络的权重以最小化损失函数。
为了最小化损失函数,该算法测量函数的梯度并在相反方向更新权重(w ij) ,使得网络的损失移动到函数的平坦区域。重复该过程,直到算法通过找到损失函数的局部最小值而实现最低误差( E )。

权重以与渐变相反的方向更新。图片作者。
然而,大多数人工神经网络是多维的,这意味着它们可以有多个局部最小值。你可以想象这一点,想象一下下面的三维图中展示的风景。
红色的山顶是我们网络误差最高的起点。图形边缘的紫色区域分别代表该模型的最佳局部最小值。因此,例如,如果梯度下降,算法在损失景观最陡的山的后面向下移动,它将迅速找到一个最优的局部最小值。我们可以得出结论,无论算法朝哪个方向移动,都会找到一个最优设置——对吗?

来自 wikimedia.org的山农鲍林的三维等高线图
不幸的是,它没有那么防弹。让我们假设算法选择沿着山的前面走,向图的中间移动,发现自己在一个山谷中。因此,误差将开始变平,表明算法已经找到局部最小值。由于误差将再次开始增加,当算法继续沿着山谷的山坡向上走时,看起来找到的局部最小值是最优的。然而,实际上,该算法只找到了(潜在的)许多不代表最优的局部最小值中的一个。
你们中的一些人现在会问:为什么不简单地告诉梯度下降,走哪条路?这是一个有效的问题,如果可能的话,这将解决安的许多问题。然而,根据所选择的损失函数、网络的规模和复杂性,我们可能不可能提前模拟人工神经网络的损失维度。
如您所见,使用梯度下降算法可能会有一些陷阱。根据手头的问题和您选择的设置,您可以通过调整反向传播算法本身来尝试减轻一些问题。如果你有兴趣了解更多,我可以向你推荐这篇文章。
结论
简而言之,反向传播是一种技术,可用于根据人工神经元网络产生的误差,通过相应地调整网络的权重来训练人工神经元网络。
误差就是人工神经网络的预测值和给定的目标值之间的差异。误差的计算取决于用于反向传播的损失函数。损失函数的类型可以变化,这取决于人工神经网络要解决的问题。
在反向传播期间,使用所谓的梯度下降技术来调整权重,梯度下降技术是一种优化损失函数的算法。为此,该算法在相反方向上更新权重,以便网络的损耗随着每次学习而减少。重复该过程,直到找到局部最小值,这表示网络损耗被最小化到最优。然而,取决于网络的复杂性以及手头的问题,由于可能存在多个局部最小值,找到最优局部最小值可能是棘手的。
来源
- 梁和海金。1991.复杂的反向传播算法,2101–2104。新泽西:电气和电子工程师协会
- 布朗利。2019.“训练深度学习神经网络时如何选择损失函数”。最后更改 2019 年 1 月 30 日。https://machine learning mastery . com/how-to-choose-loss-functions-when-training-deep-learning-neural-networks/
- 金内布洛克。1994.Neuronale Netze: Grundlagen,Anwendungen,Beispiele,19–162 页。奥尔登堡:德·格鲁埃特。
像我五岁一样解释:人工神经网络如何学习

凯文·Ku 在 Unsplash 上拍摄的照片。
人工神经网络学习范式简介。
人工神经网络是如何学习的?
人工神经网络(“ann”)的学习能力属于机器学习的科学领域。机器学习是从经验中人工生成知识的通称。更具体地说,人工神经网络从历史实例中学习,并且可以在学习阶段之后通过学习实例中包含的模式来概括这些实例。在机器学习中,有三种学习范式。其中包括监督和非监督学习以及强化学习。
监督学习
在监督学习中,人工神经网络定义一个目标函数,尽可能准确地预测目标值。人工神经网络通过创建一个目标函数来实现这一点,该目标函数将假定的输出值分配给每个输入值。然后,它将其输出值与给定的目标值进行比较,并调整其目标函数,直到达到期望的精度。因此,这种方法是基于一个预先确定结果的方案。这里有一个例子:
假设,我们玩一个电子游戏,在这个游戏中,我们在港口驾驶一艘船。游戏的主要目标是当你在港口时不要让船沉没,或者征服所有后续的关卡(每一关都会更难)。第二个目标是在途中收集尽可能多的黄金。当你完成最后一关时,金币会重新出现,游戏结束。⁴
在监督学习中,人工智能将试图找到一个数学函数,使其能够完成所有关卡,同时收集最多的黄金。人工智能通过从一个简单的功能开始实现这个目标,这个功能不断失败并使船沉没。然而,每次失败时,它都会检查游戏的最优路径,并调整其功能以尽可能接近最优。当提供的目标值以用最大数量的黄金结束游戏时,AI 也将总是试图结束游戏。⁴
无监督学习
在无监督学习中,人工神经网络识别输入模式,而无需访问先前已知的目标值或奖励。AI 根据输入值的相似性来确定自己的方向,并相应地调整权重。因此,学习过程的结果不能与已知的结果相比较。让我们看看这会如何改变我们的示例:
我们的人工智能还在玩同样的游戏。首要目标是活下去,而次要目标是收集尽可能多的黄金。然而,这一次人工智能不知道击败游戏的最佳路径。它没有目标值来比较它的方法,并告诉它必须完成游戏。⁴
在无监督学习中,人工智能被传递一个或多个目标,但不被告知如何实现这些目标。结果,人工智能创建了一个目标函数,并试图自己优化它,以实现其给定的目标。因此,无监督学习的结果变得不可预测。⁴
在这种情况下,一个可能的结果是人工智能学会驾驶船只并在港口收集黄金。它进入下一个级别,但决定风险回报率不够高。因此,人工智能决定留在港口,并在其中永远巡航,同时不断收集重生的黄金。这样,它以一种最佳的方式实现了它的目标:船永远不会沉,人工智能收集无限量的黄金。⁴
强化学习
在强化学习中,人工智能独立学习一种策略,以最大限度地获得回报。AI 不会显示在什么情况下什么动作是最好的。它会在特定的时间获得回报,这也可能是负面的。基于这个奖励,人工智能然后估计一个效用函数,它决定了具体行动的价值。关于我们的例子,我们可以得出以下结论:
人工智能用同样的规则玩同样的游戏。然而,这一次没有给它任何目标。它不知道它应该做什么,但它从游戏中收到两个反馈:它要么死亡并失去所有(负奖励),要么它活着并积累黄金(正奖励)。⁴
与无监督学习类似,强化学习的结果可能是不可预测的,因为很难判断人工智能将如何对呈现的奖励做出反应。在我们的游戏场景中,结果可能与无人监管的 learning:⁴相同
人工智能开始在港口巡航,击沉船只,并开始收集黄金。它会尝试进入下一个层次,但会发现负面奖励在增加,而正面奖励保持不变。结果,它会试图通过永远停留在最低水平来最小化负面回报。⁴
结论
监督学习是人工神经网络最常用的学习方法之一。在监督学习中,人工智能通过形成目标函数来学习,该目标函数试图预测与呈现给它的目标值尽可能相似的值。如果你想了解更多关于监督学习与非监督学习的区别,你可以在这里继续阅读。
尽管在我们的例子中,无监督学习和强化学习的结果可能是相同的,但这两种学习方法是根本不同的。虽然无监督学习是基于目标得出结论的,但强化学习侧重于最大化/最小化回报。如果你有兴趣了解更多关于强化学习的知识,推荐你阅读这篇文章。
来源
- 赖特迈尔。2015.结构信息下的分类问题,18–167。卡塞尔:卡塞尔大学。
- Mohri,Rostamizadeh 和 Talwalkar。2012.机器学习基础,21–112。剑桥:麻省理工学院出版社。
- 拉戈,德·里德和德·舒特。2018.预测现货电价:深度学习方法和传统算法的经验比较,403。阿姆斯特丹:爱思唯尔
- 弗里德曼,莱克斯。“麻省理工学院 6。S094:深度学习和自动驾驶汽车介绍。”2017 年 1 月 16 日。教育视频,38:45。https://youtu.be/2UElC_YZ0Eo.
解释机器学习模型:部分依赖
模型可解释性
让黑盒模型成为过去

来源( Unsplash
随着开发机器学习模型带来的所有复杂性,毫不奇怪,当用简单的英语解释时,其中一些模型翻译得不太好。模型输入进去,答案出来,没有人知道模型是如何得出这个结论的。这可能会导致在同一个团队中工作的不同成员之间出现某种程度的脱节或缺乏透明度。随着近年来机器学习的普及,使用复杂模型时这种缺乏可解释性的情况变得更加严重。在这篇文章中,我将讨论一些方法,使你的模型对普通人更容易解释,不管他们是你的非技术经理还是一个好奇的朋友。
为什么可解释性很重要?
随着时间的推移,机器学习模型的责任只会越来越大。他们负责从过滤你邮件中的垃圾邮件到决定你是否有资格获得你一直在寻找的新工作或贷款的一切事情。当这些模型不能用简单的英语解释时,信任的缺乏随之而来,人们变得不愿意在任何重要的决策中使用你的模型。如果你辛辛苦苦创建的模型最终没有被丢弃,因为没有人能理解它在做什么,那将是一种耻辱。在能够解释模型并展示来自模型的见解时,人们(尤其是那些没有数据科学背景的人)将更有可能信任和使用您创建的模型。
解释系数
在光谱的一端,我们有简单的模型,如线性回归。像这样的模型很容易解释,每个系数代表一个特性对我们目标的影响程度。

y = 2x 的简单线性回归
上图显示了由公式 y=2x 表示的模型的曲线。这意味着特性 x 增加 1,目标变量将增加 2。你可以像这样拥有多个特性;每一个都有自己的系数来表示对目标的影响。
另一方面,我们有像神经网络这样的“黑箱”模型,我们只能看到输入和输出,但从输入到输出的意义和步骤实际上被一大堆难以理解的数字所阻挡。
部分依赖
部分相关性显示特定特征如何影响预测。通过使所有其他特征保持不变,我们想要找出正在讨论的特征如何影响我们的结果。这类似于解释前一节中解释的系数,但部分相关性允许我们将这种解释推广到比简单线性回归更复杂的模型。
作为一个例子,我们将在 Kaggle 上的这个心血管疾病数据集上使用决策树。我们将用来绘制部分依赖关系的库是 pdpbox 。让我们训练模型,看看这一切是如何工作的。
创建部分从属图

特征年龄的部分相关性图
上图显示了特性age的部分相关性图。我们试图预测的目标变量是心血管疾病的存在。我们可以看到,随着age特征超过 19000 天(约 52 年),它开始以积极的方式影响预测(更高的年龄意味着更高的心血管疾病概率)。当直觉地思考这种洞察力时,模型是有意义的,我们更有可能相信它的预测。
我们使用的决策树仍然相对简单,它的部分依赖图可能无法描绘出全貌。让我们再试一次,这次使用随机森林模型。
使用随机森林分类器创建部分依赖图

使用随机森林分类器的部分相关图
使用一个更复杂的模型,如随机森林,我们看到age特征更线性地影响我们的预测,这与我们使用更简单的决策树时看到的“阶梯式”预测效果相反。
它是如何工作的?
部分相关图依赖于一个模型,该模型与我们正在处理的数据相吻合。让我们以数据集的一行为例。

数据集的一行
我们这里的age变量的值是 14501。该模型将从这一行数据中预测心血管疾病的概率。我们实际上会多次这样做,每次做预测时都改变变量age的值。age为 12000 时,患心血管疾病的概率有多大?16000?20000?我们跟踪这些预测,看看改变这个变量如何影响预测。最后,我们对几行执行此操作,并对不同的age值进行平均预测。然后,我们将这些绘制出来,并得出上面看到的部分依赖图。
更进一步
既然我们已经看到了部分依赖是如何与单个变量一起工作的,那么让我们看看它是如何与特征交互作用一起工作的!假设我们想看看height和weight是如何相互影响我们的预测的。我们也可以使用部分依赖图来观察这种相互作用。我们将使用与上一节相同的随机森林模型。通过一点点改变我们的代码,我们将能够得出一个完全不同的外观图,帮助我们看到功能交互。
为特征交互创建部分从属图

身高和体重的部分相关图
这个图不仅看起来很漂亮,而且它也给了我们很多关于height和weight如何相互影响我们预测的信息。变量height的影响较小,因为当我们在 x 轴上移动时,图的颜色变化不大。weight似乎对心血管疾病的概率有更强的影响,因为随着我们沿 y 轴向上移动,预测会受到积极影响。再一次,直觉地认为这是有意义的。体重较高的人更有可能患心血管疾病。有了来自我们模型的这种洞察力,我们更倾向于相信它的预测。我们可以用任何两个特征来做到这一点,我们希望能够回答关于我们数据的不同假设。
结论
我们看到了能够向非技术观众解释机器学习模型的重要性。当一个模型被提炼为易于理解的见解时,人们更有可能相信它的预测,并长期使用它们。当试图为人们可能会持怀疑态度的机器学习项目赢得牵引力时,这可能非常有帮助。
我们看到了简单的模型,如线性回归,其中预测可以使用模型系数来解释。我们能够使用部分依赖图在更复杂的模型中看到相同的洞察力。我们甚至能够使用交互图来观察两个特征如何相互作用以影响预测结果。记住这些知识,让我们去掉机器学习模型变得太复杂以至于人类无法理解的污名!
感谢您的阅读!
您可以通过以下渠道与我联系:
向一个 5 岁的孩子解释机器学习中有监督的和无监督的学习
为什么回归基础总是有帮助的?监督和非监督学习的基础。
机器学习
在机器学习的帮助下,系统可以做出与人类决策相关的决策。机器学习具有从其输入的数据中学习的能力。目前,机器学习正试图以计算高效的方式模仿人类的学习方式。
监督学习
监督学习包含具有一组特征和相应目标标签的数据集。这些特征集也称为预测器,因为它们有助于基于每个数据样本预测目标标签。
举个例子:想象一下,每当一个孩子吃完饭,吃了一个水果,完成了家庭作业,还帮忙打扫房间时,给他一个冰淇淋。然而,每当孩子长时间看电视,只是和朋友一起玩而没有完成作业,或者吃饭时已经吃了一块巧克力,他肯定不会吃冰淇淋。他会给买一种蔬菜作为零食。这里, 冰淇淋、胡萝卜和绿色蔬菜 是目标变量。

监督学习最后一栏是目标变量
监督学习中有各种各样的技术:
- 分类技术
顾名思义,每当我们有一个类别要分类时,我们就使用分类技术。例如: 冰淇淋、胡萝卜和青菜 是我们的课。
2。回归技术
如果标签不是类别,我们使用回归技术。例如:如果我们必须预测孩子每次遵循某个模式时会得到多少胡萝卜、多少冰淇淋,我们会使用回归技术。
监督学习技术广泛用于预测未来值。它们可以用来预测天气,预测某人是否会通过这门课,等等。
无监督学习
顾名思义,无监督学习是没有监督的,我们只有一个输入变量列表,没有目标标签。这种算法的主要目的是 了解数据中的潜在分布,并收集可能彼此相似的数据 。

示例:聚类无监督学习
为了让孩子理解无监督学习,我们可以给孩子看上面的图片,让他们从每条线上圈出一个奇怪的成员[就像圈出这个奇怪的人一样]。通常,他们会圈出不是冰淇淋的图片。这是一种 聚类 的手法。我们作为人类,正在收集和记忆与我们相似的物体。这也是我们期望我们的模型所做的。
聚类是一种无监督的技术,我们试图 将信息收集到不同的组 中,每个组都有与任何其他组中的数据点最相似的数据点。
无监督的机器学习帮助你在数据中发现各种未知的模式。无监督学习技术在推荐系统中被广泛使用,推荐系统使用聚类技术根据用户看到的内容等来发现相似的用户。
你应该了解的 5 个可解释的机器学习模型
当简单的方法奏效时,为什么要使用复杂的模型?

尼克·莫里森在 Unsplash 上拍摄的照片
介绍
众所周知,机器学习在我们的日常生活中无处不在。从亚马逊上的产品推荐,定向广告,看什么的建议,到搞笑的 Instagram 滤镜。
如果这些出了问题,大概不会毁了你的人生。也许你不会得到完美的自拍,或者也许公司将不得不在广告上花更多的钱。
执法中的面部识别怎么样?贷款或抵押申请?无人驾驶车辆?
在这些高风险的应用中,我们不能盲目的进去。我们需要能够剖析我们的模型,我们需要能够在模型接近生产系统之前理解和解释我们的模型。
当我们对人们做出可能对他们的生活产生负面影响的决策时,如抵押贷款或信用评分,可解释的机器学习是必不可少的。
使用可解释的模型还允许更有效的调试,以及更好地理解模型中的公平性、隐私、因果关系和更多信任。
目录
可解释的类型
- Post-hoc: 这是指我们在预测完成后或模型训练完成后解释模型。这很棒,因为这些方法允许我们解释高度复杂的模型。然而,这些方法可能被愚弄,在特定条件下有缺陷,并且需要额外的复杂性来产生解释。常见的例子是 SHAP 和莱姆 Python 包。
- 固有:有些模型可以开箱即用,无需额外的模型或库。这些通常更简单,在某些情况下可能预测能力更低——尽管一些研究人员认为情况并不总是如此!
本文将涵盖内在可解释的模型。
内在可解释的模型
利普顿,2016(https://arxiv.org/abs/1606.03490)使用 3 个标准定义模型可解释性:
- 模拟能力:人类能在“合理的”时间内完成模型的步骤吗?
- 可分解性:模型的各个方面,包括它的特征、参数、权重,都可以分解吗?
- 算法透明性:我们能否理解模型将如何对看不见的数据做出反应。
您可能已经知道符合这个标准的模型;决策树和逻辑回归。只要模型没有使用太多的特征,两者都满足所有 3 个标准。
即使你使用一个可解释的模型,使用太多的特性或者高度工程化的特性也会降低你的模型的可解释性。我们也需要保持我们的数据和预处理是可解释的。
然而,有几个鲜为人知的可解释模型非常适合放在您的工具箱中。这些方法比决策树和逻辑回归更有预测能力,同时仍然保持一定程度的可解释性,允许你在下一个项目中平衡可解释性和准确性。
广义线性模型
这是什么?它是如何工作的?
一个 GLM 实际上只是一种谈论线性或逻辑 回归的花哨说法。这里的关键概念是,还有其他的线性模型使这些更加灵活。
GLM 有三个组成部分:
- 线性预测器:这只是回归方程——变量和一些预测变量的线性组合。

线性回归方程。图片作者。
2.链接功能:将变量的线性组合链接到概率分布。在线性回归中,这只是恒等式的链接函数。
3.概率分布:我们的 y 变量就是这样产生的。在线性回归中,这是一个正态分布。
通过改变这些,我们可以得到不同的模型。使用具有伯努利分布的 Logit 链接函数,我们得到了一个逻辑回归。
一个不太为人所知的版本是泊松回归,它使用泊松分布。这假设我们的变量线性组合与 y 的对数相关。
要了解更多关于逻辑回归的知识,请点击这里查看我的文章:
为什么可以解释?
- 因为这只是简单的数学
下图显示了一个简单的一元线性回归方程。我们知道,对于 x 的任何值,我们都可以用这条线计算出 y,我们的目标。我们也知道这条线是如何计算的(通过最小化误差)。
我们可以把它放大到我们想要的变量,数学仍然成立。我们只需将各项相加就能产生一个结果。
逻辑回归或泊松回归更复杂,但核心概念是正确的。我们对变量的线性组合求和。

简单的单变量线性回归。图片作者。
- 系数意味着什么
在线性回归中,我们的系数是根据目标变量给出的。这使我们能够做出如下声明:
增加 5000 万平方英尺将使我们的房价增加 1000 英镑
在逻辑回归中,这些是以对数概率表示的,我们可以将其转换成概率。
事实上,这些系数可以转换成人类可以理解的陈述,真正增加了线性模型的解释能力。
- 交互必须明确编程
基于树的模型将计算出变量之间的相互作用,这会增加复杂性。许多可解释性方法没有很好地涵盖交互,并且很难解决。例如
每增加 50 平方米,我们的房价就会增加 1000 英镑,直到我们达到 200 平方米,每 50 平方米*卧室数量,我们的房价就会增加 1000 英镑。
这种复杂关系只有在线性模型中才能找到,如果它在特征工程期间在中被明确地计算和编程。
实施
- scikit-learn 提供了一个 GLMs 的实现
- Statsmodels 也提供了一个很好的实现
决策树

用于水果分类的决策树示例。图片作者。
这是什么?它是如何工作的?
大多数人应该在一生中的某个时候见过决策树!算法版本使用一些简单的数学来生成“最优”决策树,即最好地分割我们的数据的树。
要了解它是如何工作的,请看这里:
为什么可以解释?
决策树的伟大之处在于,我们可以真正地提取整棵树,并理解为什么模型会对数据集中的任何样本做出预测。
一旦树木变得非常大( max_depth = 7+),由于树叶数量的指数增长,人类就很难跟踪它们。然而,在这一点上,我们仍然可以编写一些基本代码来突出显示我们的数据达到其预测的路径,以及测试模型将如何对看不见的数据做出反应。
我发现这是最容易解释的模型之一,因为几乎没有数学知识,而且这个概念在世界其他地方都可以找到。
实现
像往常一样, scikit-learn 将是我对这个模型的首选。
要绘制树,有许多不同的选项,这里有一个很好的列表。
广义可加模型

GAMs vs GLMs。图片作者。
它是什么,如何工作?
广义加性模型(GAMs)是 GLM 模型的扩展,去掉了一个主要的限制;我们现在可以对数据中的非线性关系进行建模。
gam 通过使用一系列被称为样条的复杂函数来估计每个变量来完成这些工作。我们仍然对我们的变量求和,但是样条意味着变量和目标值可以具有非线性关系。
要了解更多关于 GAMs 的信息,请点击这里:
[## 什么是广义加性模型?
towardsdatascience.com](/generalised-additive-models-6dfbedf1350a)
为什么可以解释?
毫无疑问,GAMs 比逻辑回归或线性回归更难解释。这个模型要复杂得多,其背后的数学原理也是如此。然而,当你考虑到它们的灵活性时,它们仍然保持一定程度的可解释性,这是一个很好的权衡。
- 非线性变量的组合?
目标变量仍然是所有其他变量的和一些权重,我们现在有一个复杂的函数来模拟每个变量。我们仍然可以提取和可视化每个变量的函数,大多数 GAM 包使用部分依赖图来对所有特性进行提取和可视化。
交互必须手动编程,这限制了复杂性。我们还可以大致了解模型在看不见的数据上的表现,因为我们知道每个特征的样条函数。
实施
根据我的研究,R 中的 mgcv 包似乎是最适合 GAMs 的。但是,我更喜欢 Python 两个最佳选项是 Statsmodels 和 PyGAM 。
微软研究院已经开源了他们的 InterpretML 包,其中包括他们的可解释增强机器,他们称之为 GAM 2.0,因为它使用具有自动交互术语和梯度增强的 GAM 来保持可解释性,提高性能,并减少数据科学家深入模型的需要。
单调梯度增强

单调与非单调关系。图片作者。
它是什么,如何工作?
梯度推进模型被认为是表格数据的最佳模型,但由于推进的性质,它们是不可解释的。这些模型可以使用数百棵不同权重的单棵树。他们也倾向于自己计算出交互项,而我们对这些交互项几乎没有透明度。通常使用 SHAP 或石灰来提高这些模型的可解释性。
单调关系是指目标和特征具有线性关系,例如:
- 你的身体质量指数增加,你患心脏病的风险增加。
- 你的信用评分降低,你获得贷款的可能性降低。
- 降雨量增加自行车租赁数量减少。
线性模型是完全单调的,但是由于梯度增强包括相互作用并且可以模拟非线性关系,它们通常不产生单调关系。
XGBoost、LightGBM 和 Catboost 都有一个简单的超参数,强制变量具有正或负的单调关系。
为什么可以解释?
使用单调关系意味着我们可以使用上述语句来解释我们的模型。它导致模型满足算法透明性标准,因为这种关系是固定的。我们还可以将一些现实世界的知识构建到模型中,使其更容易被商务人士理解,从而为生产带来更多变化。
实现
- XGBoost
在 XGBoost 中,我们将 monotone_constraints 参数指定为一个字符串元组(语音标记内的括号),在我们的数据集中每个特征有一个数字,因此“(1,0,-1)”表示特征 1、2 和 3。1 为正单调关系,-1 为负,0 为无关系。
import xgboost as xgbparams = {'monotone_constraints':'(1,0,-1)'}
model **=** xgb**.**train(params,
X_train,
num_boost_round **=** 1000,
early_stopping_rounds **=** 10)
- LightGBM
LightGBM 大体上与 XGBoost 相同,但是我们需要以列表而不是字符串/元组的形式传递我们的特性。LightGBM 还为方法提供了一个额外的参数。使用这种方法,我们可以选择模型试图坚持约束的力度。
文件规定:
basic,最基本的单调约束法。它根本不会降低库的速度,但会过度限制预测
intermediate,一个更高级的方法,可能会稍微减慢库的速度。但是,这种方法比基本方法限制少得多,应该可以显著改善结果
advanced,一个更高级的方法,可能会拖慢库。然而,这种方法甚至比中间方法更少约束,并且应该再次显著改善结果
import lightgbm as lgbparams = {'monotone_constraints': [-1, 0, 1],
'monotone_constraints_method':'basic'}
model = lgb.train(params,
X_train,
num_round = 1000,
early_stopping_rounds = 10)
- Catboost
Catboost 与其他的非常相似,但提供了更多的灵活性,因为我们可以将约束作为数组传递,使用切片并显式命名一个特性。
这个参数叫做monotone_constraints,你可以在这里查看的 Catboost 文档。
TabNet
这是什么?它是如何工作的?
TabNet 由谷歌大脑研究人员于 2019 年发表。传统上,当处理表格数据时,神经网络方法在梯度提升方面没有显著改进。然而,Tabnet 能够在各种基准测试中超越领先的基于树的模型。它比增强树模型更容易解释,因为它有内置的解释能力。也可以不用任何特征预处理使用。

TabNet 模型架构。图片作者。灵感来自https://arxiv.org/pdf/1908.07442.pdf。
我在 TabNet 上的文章更详细地介绍了这个模型,请看这里:
[## TabNet:梯度推进的终结?
towardsdatascience.com](/tabnet-e1b979907694)
为什么可以解释?
TabNet 使用顺序注意机制来选择最重要的特征,这影响了掩盖最不重要特征的“面具”。我们可以使用该掩膜的权重来了解哪些特征比其他特征使用得更频繁,这实质上允许我们了解模型使用哪些特征来进行预测。
要素选择是在数据集的行级别执行的,这意味着我们实际上可以探索为单个预测选择了哪些要素。掩模的数量是模型的超参数
实施
使用 TabNet 的最佳方式是使用 Dreamquark 的 PyTorch 实现。它使用 scikit-learn 风格的包装器,并且与 GPU 兼容。Dreamquark 还提供了一些非常棒的笔记本,它们完美地展示了如何实现 TabNet,同时也验证了原作者关于模型在某些基准上的准确性的声明。
分类
https://github.com/dreamquark-ai/tabnet/blob/develop/census_example.ipynb
回归
https://github.com/dreamquark-ai/tabnet/blob/develop/regression_example.ipynb
比较模型
让我们回到利普顿的 3 个标准,并应用于每个模型。提醒一下,标准是…
- 模拟能力:人类能在“合理的”时间内完成模型的步骤吗?
- 可分解性:模型的各个方面,包括它的特征、参数、权重都可以分解吗?
- 算法透明性:我们能否理解模型将如何对看不见的数据做出反应。
我们还要考虑局部可解释性;根据模型使用了哪些特征以及在何种程度上使用了每个特征来做出决策,模型可以在多大程度上做出单个预测。
我在下面的图表中总结了这一点,根据每个标准对每个模型进行了低、中或高评分。这不是一门精确的科学,但是你可以考虑每个分数与线性回归的关系。
记住,任何特性工程都可能完全打乱这些分数。从神经网络中创建复杂的交互项、数学变换或特征可能会提高准确性,但肯定会降低可解释性。更多的特性也会降低可解释性。

根据每个模型的可解释性标准评分。图片作者。
结论
围绕模型可解释性的驱动力和讨论只会增加。随着人工智能被用于语言建模、面部识别和无人驾驶汽车,拥有能够在决策背后进行推理的模型比以往任何时候都更加重要。
在你的下一个项目中尝试这些可解释的模型中的一个,让我知道结果如何。
了解更多信息
[## 在家创建一个现代的、开源的 MLOps 堆栈
towardsdatascience.com](/mlops-at-home-part1-4c60db29d4a2)
将我的内容直接发送到您的收件箱!
https://adamsh.substack.com/p/coming-soon
可解释的人工智能:黑盒机器学习领域的一个亮点
不同的机器学习可解释性工具如何用于解释的概述

威廉·戴尼奥在 Unsplash 上拍摄的照片
随着机器学习领域的发展,许多新的复杂机器学习模型被广泛应用于各个领域。特别是,随着深度学习的进步,数据正被用于做出一些关键决策。但有时,即使是人工智能专家也很难解释所谓的“黑箱模型”所做的某些预测。当涉及到医疗保健、自动驾驶汽车等高风险领域时,了解这些信息变得非常重要
- 我们的模型学习是什么?
- 模型的哪些部分负责做出某些预测?
- 模型是否稳健?
不同的模型可解释性技术有助于回答这些问题。在本文中,我分享了一些常用的可解释性工具的概述。
让我们让可解释性工具对所有人都是可解释的😀。
在开始之前,最好先了解一些本文中会用到几次的术语。

作者图片
解释特定预测的最佳方式是,是否有任何可解释的机器学习技术可用于决策。可解释的 ML 技术的一些例子是:

一些可解释的 ML 技术的例子[ 来源
广义线性模型(GLM)家族的所有成员都非常容易解释。在本文中,我不会深入研究可解释的 ML 算法。要知道 GLM 是如何工作的,去看看我的另一篇文章。
排列特征重要性:
特征重要性为每个特征给出一个分数,它告诉我们这些特征中的哪些对我们的模型是重要的,这些特征中的哪些在驱动我们的模型预测中起着至关重要的作用。存在一些特定于模型的特征重要性度量,例如对于 GLMs,由特征的标准偏差缩放的参数系数作为特征重要性。类似地,对于基于树的模型,节点中分裂的杂质减少给出了特征重要性的度量。
但是置换特征重要性是模型不可知的特征重要性度量之一,其中我们通过置换其值来计算特征的重要性。
如果预测误差增加,则我们可以说该特定特征是重要的。

排列特征重要性示例[图片由作者提供]
特征重要性计算如下:

使用排列特征重要性的特征重要性[图片由作者提供]
如果 Fᵢⱼ的值大于 1,则可以认为某个要素是重要的。一个特性的值的排列可以用不同的方式完成,特别是 n!因此,置换特征重要性给出了作为输出的置信区间。
部分相关图(PDP):
部分相关图是一种全局方法,它考虑了所有数据实例,并给出了预测因子/特征和结果变量之间的全局关系。
PDP 计算一个/两个特征对结果的边际影响。它没有捕捉到特征之间的相互作用及其对预测结果的影响。
为了绘制 PDP,我们有两组功能:
- 我们要为其绘制 PDP 的功能
- 机器学习模型中使用的其他特征。
为数字特征绘制 PDP 的机制与为分类特征绘制 PDP 的机制略有不同。
为了绘制数字特征 X₁的 PDP,
- 在原始数据上拟合机器学习模型 f 。
- 为了获得特定数量的 PDP,比如说 X₁₁的特征 X₁,通过将所有数据实例的值改变为 X₁₁来创建人工数据。
- 使用已经拟合的机器学习模型对每个数据实例进行预测。
- 要素 X₁的特定 X₁₁量的 PDP 值将是步骤 3 中完成的所有预测的平均值。

特性特定值的 PDP 图片由作者提供]
5.遵循上述过程来覆盖 X₁的整个区间,并给出 X₁的 PDP 图。
类似地,为了绘制分类特征的 PDP,通过用任何一个可能的类别改变特征的值来创建人工数据,并且对该特征的所有类别继续该过程。数字和分类特征的 PDP 分别如下所示。

数字特性和分类特性的 PDP[来源
所以,我们已经学会了如何绘制 PDP。但是,通过观察这些图,我们能对特性的重要性说些什么呢?
主要的想法是,平板 PDP 告诉我们,功能并不那么重要。
PDP 的变化越多,特性就越重要。
对于数字特征,特征重要性的值通过以下公式计算,该公式基本上是该特征的 PDP 值的标准偏差。

数字特征的特征重要性[ 来源
类似地,对于分类特征,重要性分数通过以下方式计算:

分类特征的特征重要性[ 来源
什么是代孕模型?
代理模型是一种可解释的模型,其训练方式使得它能够以最大的解释近似底层黑盒模型的预测。它使用黑盒模型做出的预测作为输入,并尝试拟合一个可以近似黑盒模型的可解释模型(在上一节中讨论过)。
- 如果 R 值很高,我们可以说代理模型是底层黑盒模型的近似。我们可以通过以下方式计算 R 平方值:

代理模型的 r 平方值[图片由作者提供]
- 训练代理模型是一种模型不可知的方法,因为它不需要关于底层模型的任何信息,所需要的只是每个数据实例的特征值和底层模型的预测。
- 全局代理模型是整个黑盒模型的近似。
- 局部代理模型用于通过底层黑盒模型来近似特定实例的预测。
LIME(本地可解释模型不可知解释):
LIME 使用局部代理模型来对单个预测进行具体解释,而不是关注整个模型的解释。
LIME 的假设是,我们可以任意多次探索黑盒模型。此外,LIME 认为任何算法都是黑盒(即使我们将 LIME 应用于线性回归,它也会假设线性回归是黑盒)。
我们的目标是知道为什么黑箱模型做出了某种预测。

用于解释的感兴趣的数据点[图片由作者提供]
从上面的图片中,我们可以看到存在一个复杂的决策边界,这是两个类别的分类。但是,我们想知道为什么模型为突出显示的数据实例做出某个决定,哪些特性对做出这样的决定更负责。
为此,我们通过扰动来自原始数据集的样本并由黑盒对这些扰动样本进行预测来创建新数据集。

为 LIME 创建的新数据集[图片由作者提供]
创建的扰动样本根据它们与感兴趣的数据点的接近程度进行加权。

感兴趣的数据点附近的加权样本[图片由作者提供]
从上图中我们可以看到,靠近感兴趣实例的样本被赋予较高的权重(圆圈越大,权重越大),而远离感兴趣点的样本被赋予较低的权重。
在这个新的数据集上训练本地代理模型。这个本地代理模型应该是上面列出的可解释模型家族中的任何模型。
对于数据实例(x)的解释,我们想要最小化损失 L,该损失 L 通过保持 g 的复杂度低(对于线性回归较少特征;对于决策树,树的深度更小)。这是 g 的复杂性和 f 的近似性之间的权衡。这种权衡被称为保真度-可解释性权衡。

石灰的优化功能[图片由作者提供]
其中 G 是可解释模型族。
在训练时,记住给予扰动样本的局部可解释模型权重。
我们正在 x 的邻域中搜索复杂模型的最佳近似模型
指数平滑核用于定义 x 的邻域:较小的核宽度意味着样本必须非常接近感兴趣的点才能影响局部拟合的模型。我不会说太多细节。更多细节你可以查看这本书。

使用局部代理模型解释感兴趣的数据点[图片由作者提供]
从上面的图像中,你可以看到一个局部可解释的线性模型被拟合。一般这类线性模型本质上是稀疏的(套索回归由 Saptashwa Bhattacharyya 精彩解释)。通过解释这个局部模型,我们可以对复杂模型对感兴趣点所做的预测做出某些解释。
我们已经了解石灰的工作原理,但这还不够。让我们看看如何解释一个 LIME 输出。

特定数据点的石灰输出[ 源
假设我们想要解释一个表格数据的特定数据实例的石灰输出,该表格数据有两个类别“可食用”和“有毒”。在这里,我们希望了解特定数据实例的不同特征的值如何影响预测。橙色高亮支持有毒,蓝色支持食用。局部拟合模型预测该数据实例有毒,概率得分为 1。上图中间部分给出的权重是局部拟合模型的参数。这里我们可以看到“气味=恶臭”这个值是增加被归类为有毒的机会的最重要的值,而“鳃大小=宽”是唯一减少被归类为有毒的机会的值。
要了解 LIME 如何处理图像和文本数据,请查看此源。
SHAP(沙普利附加解释):
沙普利加法解释或 SHAP 是基于博弈论中沙普利值的概念。SHAP 背后的主要思想是知道每个单独的特征对做出某个预测有多大的贡献。这可能很难计算单个的贡献,因为在特征之间可能存在相互作用。
SHAP 是既可用于本地解释又可用于全球解释的度量之一。
所以,我们理解为什么沙普利和为什么解释但为什么加法??
让我们看看。
Shapley 值使特定数据点的输出不同于基础模型的基线输出。假设φᵢⱼ是特征 j 的 iᵗʰ数据实例的沙普利值。这些值或正或负地使输出不同于基线输出。因为 Shapley 值对基线输出的影响的增加,所以它是“相加的”。

SHAP 解说[图片由作者提供]
现在让我们看看这些φᵢⱼ是如何计算的。
iᵗʰ数据实例的 jᵗʰ要素的沙普利值φᵢⱼ按以下方式计算

iᵗʰ数据实例的 jᵗʰ特征的沙普利值计算公式[图片由作者提供]
在哪里,
s 是特征的子集。
f 是底层黑盒模型。
f(SU{j})是具有感兴趣特征的子集 S 的黑盒模型的预测。
f(S)是没有感兴趣特征的子集 S 的黑盒模型的预测。
假设我们总共有 p 个特征。因此,每个 S 都是 p-1 个特征的子集。
例如,假设我们要计算φ₁₁,那么 s 就是绿色高亮显示的特征值的子集。在这里,我们的目标是了解特性的特定值是如何影响输出的。我们考虑不同的组合来捕捉特征之间的交互作用。

用于计算 Shapley 值的子集选择[图片由作者提供]
根据子集 s 中存在多少特征来给出加权项。其背后的直觉是,如果子集 s 中已经包括了许多特征,则对添加 jᵗʰ特征的贡献给予更大的加权。这表明即使已经包括了许多其他特征,jᵗʰ特征也会对预测产生强烈的改变。
现在让我们看看如何解释特定数据点的 SHAP 输出。

特定数据点的 SHAP 输出[ 来源
对于这个特定的例子,底层黑盒模型的基线输出是 22.841,这个特定数据点的输出是 16.178。红色高亮显示的条表示有助于输出向基线输出移动的特征值,蓝色条表示有助于向相反方向移动的特征值。
要知道 SHAP 如何帮助解释全球,我们必须知道 SHAP 功能的重要性和 SHAP 依赖图。
SHAP 特征重要性:
jᵗʰ 特征的沙普利值按以下方式计算。

使用 Shapley 值计算全局特征重要性的公式[图片由作者提供]
特征重要性值越高,该特征越重要。

全球特征重要性图[ 来源
对于这个特殊的例子,使用激素避孕药的年数是最重要的特征,x 轴上的值代表这些特征对产出的平均影响的绝对量。
SHAP 依赖情节:
SHAP 依赖图可以被认为是 PDP 的替代方案。使用 SHAP 依赖图,我们还可以推断出特征之间的相互作用对模型预测的影响,这对于 PDP 是做不到的。它基本上是通过绘制 x 轴上的特征值和 y 轴上相应的 Shapley 值来创建的。
对于 jᵗʰ特征形状依赖图是通过绘图创建的

绘制 SHAP 依赖图的数据[图片由作者提供]
现在让我们来看看它的样子,以及如何解读它。

SHAP 依赖情节[ 来源
对于这个例子,当年龄在 20 到 40 岁之间时,这在模型预测中起着至关重要的作用,在此之后,年龄对模型输出的影响相当稳定。年龄与另一个特征----教育---- Num 的交互影响也可以从该图中得到解释,因为它显示了在接近 20 岁时,较少的教育比高等教育对产出的影响更大。
注:为了更好地理解这些方法并尽可能清晰地解释结果,本文中使用的图是从不同来源收集的。
结论
在本文讨论的所有可解释性工具中,SHAP 是最常用的,因为它的粒度以及它在本地和全局解释中的实现。虽然与其他技术相比,它需要一点额外的时间。因此,在使用任何可解释性工具之前,知道解释的目的是什么是非常重要的。
如果你喜欢这篇文章请点击推荐。那太不可思议了。
关注我在 Medium 和 LinkedIn 上的博客帖子和更新。
参考文献
- C.莫尔纳尔。可解释的机器学习。https://christophm.github.io/interpretable-ml-book/
- https://shap.readthedocs.io/en/latest/index.html SHAP 文件
- 米(meter 的缩写))t .里贝罗,s .辛格,c .盖斯特林。“我为什么要相信你?”解释任何分类器的预测。https://arxiv.org/pdf/1602.04938v3.pdf
让我们把它包起来。非常感谢你的阅读和快乐学习。
多输出回归的可解释人工智能
模型可解释性
用 SHAP 透视多输出回归模型的“黑箱”。

多输出回归的 SHAP 值(图片由作者提供)
打开机器学习模型的“黑匣子”不仅对理解我们创建的模型至关重要,而且对向他人传达机器学习模型揭示的信息也至关重要。我见过几个项目失败,因为他们不能很好地向其他人解释,这就是为什么为了增加机器学习项目的成功实施,理解我们建立的模型是必要的。
最近我在做一个 ML 项目,需要多输出回归(预测不止一个输出/标签/目标),很难找到实现可解释性的可靠例子或资源。解决如何解释多输出回归模型的挑战需要大量的反复试验。最终,我能够分解我的多输出回归模型,并且我获得了一些值得分享的“经验教训”。
完整的代码可以在 GitHub 的多输出回归模型的 SHAP 值中找到,并且可以通过 Google Colab 在浏览器中运行。
设置和代码重点
创建数据
为多输出回归创建数据模型,以通过 SHAP 展示可解释性。
下面的代码创建了包含 1000 个样本和 10 个特征的数据,其中只有 7 个与我们试图预测的输出/标签有意义地关联。(稍后我们将看到 SHAP 价值观如何帮助我们看到信息丰富的特征。)最后,我们有 5 个输出/标签,使这成为一个多输出回归问题。
X, y = make_regression(n_samples=1000, n_features=10, n_informative=7, n_targets=5, random_state=0)
创建模型
为了创建多输出回归模型,我使用 Tensorflow/Keras 模型,因为它允许用户轻松地将输出/标签的数量设置为他们试图从数据中预测的标签数量。
model = Sequential()
model.add(Dense(32, input_dim=10, activation=’relu’))
model.add(Dense(5))
model.compile(loss=’mae’, optimizer=’adam’)
model.fit(X, y, verbose=0, epochs=100)
解释模型
导入 SHAP 并选择要生成解释的数据后,我们现在可以绘制各个输出/标签的解释。完整代码见多输出型号的 SHAP 值笔记本。
shap.force_plot(base_value = explainer.expected_value[current_label.value],shap_values = shap_value_single[current_label.value], features = X[list_of_labels].iloc[X_idx:X_idx+1,:])
下图显示了每个特征/输入如何影响整体预测。(红色为较高值,蓝色为较低值。)现在,我们从一个“黑箱”模型中获得了一个个体预测的详细解释。通过选择不同的标签,我们可以看到相同的输入特性是如何独立地影响每个输出/标签的(参见动态下拉列表的完整代码笔记本)。

多元回归的 SHAP 值(图片由作者提供)
与许多 ML 项目一样,很难判断我们使用的哪些输入/特征真正影响了预测,但我们可以再次使用 SHAP 和内置的汇总图来显示每个单独输出/标签的模型级汇总。

根据之前的数据设置,我们知道有 3 个特性对输出没有任何影响,我们现在可以很容易地看到,底部的 3 个特性(03、07 和 01)就是这些特性。
关键要点
1)单独考虑每个输出/标签
由于有更多的可用资源,通过尝试解释模型的单个输出/标签,将问题分解为更简单的问题。然后回到最初的问题(这个例子使用下拉菜单动态选择多个输出/标签中的任何一个来研究)。
2)创建一个学习教程
将教程创建为一个独立的笔记本,它可以在浏览器中运行并选择单独的输出/标签,这迫使我在一个超越复制和粘贴代码的层次上理解,以获得快速的解决方案。
如果你正试图打开多输出机器学习模型的黑匣子,我希望这展示了 SHAP 的价值,并帮助你解释你的模型。
可解释的人工智能:第一部分——简介
模型可解释性
作者: 海伦娜·弗利,机器学习研究员 马克斯·凯尔森

马库斯·斯皮斯克在 Unsplash 上拍摄的照片
将机器学习(ML)实际应用于医疗数据的道路是漫长的,最终,终点可能就在眼前!它已经成功应用于染色肿瘤组织微阵列(TMA)样本(比契科夫等人,2018 年)、全切片图像(Ehteshami Bejnordi,2017 年)和皮肤癌图像(Haenssle 等人,2018 年),在诊断准确性方面取得了巨大成功,有时甚至超过了临床专家!然而,在它能够真正在更广泛的医疗保健领域实施并有能力改变人们的生活之前,我们必须首先跨越我们的 Ts 并在其透明度上打点我们的 Is。这篇博客是两篇博客中的第一篇,它将向你简要介绍可解释人工智能的世界。
传统的模型,如线性回归,是直观的和高度可解释的。然而,由于数据的高维度,它们对基因组数据的效用是有限的(我们说的是每个样本都有几万甚至几十万个特征!).如果样本量足够大,更先进的模型,如卷积神经网络(CNN)可以产生更准确的结果(Akkus,2017)。然而,这些高级模型具有复杂的结构,具有多个隐藏层、非线性激活函数和反向传播规则,使得输入和输出之间的过程难以跟踪和解释。
作为科学家,我们不能盲目相信我们的模型,不管它们表现得多好。虽然我们知道我们向模型提供了什么信息,以及它输出了什么信息,但我们对所述模型的内部决策过程视而不见。这种盲目性导致了一个新名词,黑盒模型。这是一个非常令人担忧的原因,因为我们碰巧在一个数据集中找到的正确答案可能在其他地方的另一个数据集中并不正确。因此,除非我们看到模型使用什么信息来学习“正确”的答案,否则我们不会知道这些答案是否真正正确。这些模型倾向于选择阻力最小的路径,而这一路径往往是由不想要的人为因素驱动的,如批量效应,而不是真正的生物信号。虽然这在首次开发模型时可能没有明显的问题,但是一旦要求模型根据新信息进行预测,这可能导致错误的预测。在临床环境中,这可能非常危险。
这个问题的解决方案是撬开这个黑盒,看看模型认为哪些特征对其决策过程“重要”,这样我们就可以验证模型是否在做我们期望它做的事情,或者被告知我们未来的研究应该关注哪些特征(这些可能是重要的环境影响、生物过程或基因突变)。
显著性映射已经尝试了多种方法,包括:Shapley 采样(Strumbelj & Kononenko,2010),树解释器(Saabas,2014),相关性传播(Bach 等人,2015),量化输入影响(达塔等人,2016),LIME (Ribeiro 等人,2016),以及 DeepLIFT (Shrikumar,2017)。这些方法中的每一种都提供了改进,但是保持结果的统计稳定性和通用性的能力,以及全球与局部水平的一致数值,仍然没有解决。
如前所述,简单的模型,如线性回归是透明的,完全可以解释,而深度模型充满了复杂的非线性函数。Ribeiro 及其同事(2016)表明,线性模型的局部近似可用于解释复杂的非线性模型。然而,这种局部近似缺乏与模型整体水平的稳定联系。深度模型(如神经网络)的内部过程可以被视为竞争信息的过程。在《N 人游戏的价值》(1953)中,Lloyd Shapley 概述了为了向参与游戏的所有玩家公平分配收益,我们需要三个公理:可加性、一致性和虚拟玩家(T3)。通过将这种局部近似与 Shapley 理论相结合,Lundberg 和 Lee(2017;https://arxiv.org/abs/1705.07874交付了一个概念验证设计,提供了从局部到全局限制所需的稳定性的数学保证。这项工作引入了 SHAP(SHapley Additive exPlanations)值,并提供了优于传统的、可解释的模型(如前面提到的线性回归)的显著优势(我们不会在这里深入讨论技术细节,因为这些在这里的以及原作者的博客中有全面的介绍)。与之前的可解释模型相似,SHAP 值的正估计和负估计允许解释学习或预测给定类别的信心的增加或减少。然而,与传统模型不同,在传统模型中,显著性映射只能在整个群体中进行估计,SHAP 值还提供了所有特征和所有样本的粒度显著性估计(图 1),从而允许更精确地理解内部学习过程。

图一。使用显著性映射将“黑盒”变成“白盒”的视觉表示
在马克斯·凯尔森研究实验室,我们的使命是通过使用尖端的 ML 技术解决困难的挑战,如癌症,为科学做出积极的贡献。我们认识到 Lundberg 和 Lee (2017)的贡献有可能最终使先进的 ML 模型,如神经网络(NNs),在临床设置中有用。例如,尽管在人类转录组上训练神经网络模型以准确预测癌症类型很容易实现,但知道哪些基因促成了这种分类是对临床医生和研究人员很重要的下一个方面,因为它可以为更好的治疗方法的开发提供信息。
这一系列博客文章旨在概述 SHAP 价值观的优势和局限性,以及它们在生物学和癌症方面的实际应用。请关注我们的媒体或 LinkedIn ,继续收看本系列的第二部分。
参考文献
安多·萨巴斯。2014.解读随机森林。http://blog.datadive.net/interpreting-random-forests/.(2014)。访问时间:2017 年 6 月 15 日。
2019 年算法问责法案。可查阅 https://www . congress . gov/bill/116 th-congress/house-bill/2231/all-info(查阅时间:2020 年 2 月 8 日)。
Bach,S. 等人关于通过逐层相关性传播对非线性分类器决策的逐像素解释。 PLoS One 10 ,1–46(2015)。
Bejnordi,B. E. 等对深度学习算法检测乳腺癌女性淋巴结转移的诊断评估。美国医学会杂志。医学。协会 318 ,2199–2210(2017)。
比契科夫,D. 等基于深度学习的组织分析预测结直肠癌的结果。 Sci。代表 8 ,1–11(2018)。
通过定量输入影响的算法透明度。71–94 (2017).doi:10.1007/978–3–319–54024–5 _ 4
Haenssle,H. A. 等人对机器:深度学习卷积神经网络用于皮肤镜黑色素瘤识别的诊断性能与 58 名皮肤科医生的比较。安。肿瘤科。 29 ,1836–1842(2018)。
解释模型预测的统一方法。神经 Inf。过程。系统。 2017 年 12 月,4766–4775(2017 年)。
"我为什么要相信你?"解释任何分类器的预测。继续进行。ACM SIGKDD Int。糖膏剂知道了。Discov。数据最小值。2016 年 8 月 13 日至 17 日,第 1135 至 1144 页。
Smilkov,d .,Thorat,n .,Kim,b .,Viégas,F. & Wattenberg,m.《smooth grad:通过添加噪声消除噪声》。(2017).
通过传播激活差异学习重要特征。第 34 国际。糖膏剂马赫。学习。ICML 201774844–4866(2017)。
trumbelj,e .和 Kononenko,I .使用博弈论对个体分类的有效解释。 J .马赫。学习。第 41 号决议第 11、1 至 18 段(2010 年)。
深层网络的公理化归因。(2017).doi:10.1007/s 10144–009–0162–4
可解释的人工智能:第二部分——研究 SHAP 的统计稳定性
模型可解释性
作者: Helena Foley,机器学习研究员at Max Kelsen,根据 SHAP 论文byMelvyn Yap 博士,高级机器学习研究员 at Max Kelsen

在我们的之前的博客中,我们介绍了 SHAP 值的概念(4)及其相对于其他显著性映射方法(如 LIME (5))的优势。我们还提出,将这种方法应用于遗传数据可以用于探索生物学,并使深度模型的临床效用成为可能。然而,考虑到前一篇博客以及(1,9)中提到的可靠性问题,我们在信任深度学习结果的解释之前谨慎行事是绝对重要的。安全导航的一种方法是通过所发现特征的已知生物学相关性,以及将结果与成熟可靠的传统生物信息学方法进行比较。
神经网络模型
首先,我们需要一个模型和一个目标假设。为此,我们训练了一个卷积神经网络(CNN 图 1)使用来自基因型-组织表达(GTEx)项目的 RNA-seq 数据预测组织类型。该 GTEx 数据集包括来自 16,651 个样本和 47 类(组织类型)的 18,884 个特征(基因)。

图一。|我们卷积神经网络的架构。
可解释模型
我们将 GradientExplainer SHAP 拟合到我们模型的 softmax 层,并计算了测试集的 SHAP 值(图 2)。这是使用 Lundberg 的 SHAP GitHub 库提供的库完成的。通过 SHAP 中值对基因进行排序,分别产生了每种组织类型的顶级基因。
我们选择这个可解释性模型是因为 GradientExplainer 利用了适合神经网络模型的统计方法。它也是用户友好的,适合在任何主要的 Python 深度学习框架上使用。Tensorflow、Keras 和 Pytorch,最重要的是,解决了前面提到的“健全性检查”出版物中提出的许多限制。 GradientExplainer 是将综合梯度 (7)、 SHAP (3)和 SmoothGrad (7)合并成一个等式,该等式计算神经网络原始输入中每个特征的显著性得分。每个改进的细节在原始出版物中都有描述,但都很简短。
通过 CNN 模型的传播必须依赖于基于梯度的方法,因此为 CNN 开发的大多数显著性映射技术必须基于梯度。然而,由于普通激活函数(如 ReLU)的性质,这些方法的灵敏度大打折扣(因为一旦激活,梯度不会改变),甚至经常遗漏非常显著的特征(1,9)。解决方案是一个简单的过程,即整合基线模型和输入之间路径上的所有梯度(等式 1),并通过采用来自无限博弈定理的沙普利值(等式 2)将其与 SHAP 值相联系。通过采用 SmoothGrad 技术对灵敏度图进行了进一步改进,该技术将高斯噪声引入梯度和输入数据(7)。
方程式 1。综合梯度(Sundararajan 等人,2017 年的等式 1)
integrated gradsi(x)::=(Xi-Xi ')= 01F(x '+(x-x '))xid
方程式 2。无穷小选手的奥曼-沙普利值
(Sv)(ds)=01(v(tI+ds)-v(tI))dt
其中 Sv 为 ds 无穷小播放器的 Shapley 值;t I 是包含所有玩家比例 t 的所有玩家集合 I 的完美样本,tI+ds 是 ds 加入 tI 后得到的联盟(来源于维基百科)
特征选择

图二。| SHAP 值矩阵及其内容的可视化表示。 背景代表一个具有 SHAP 值的全矩阵。前景插页显示该矩阵适用于所有 47 种可用的组织类型。
SHAP 值的分布特性尚未得到数学证明,因此无法计算出 p 值。因此,“最重要”特性的选择仍然有些随意,可以通过几种方式来实现。例如,SHAP 可以用数字转换成排名,提供了一个非参数化的途径。然而,我们决定将截止点定义为组织排他性的函数,在 50%的顶级基因是组织排他性的情况下划线。这导致了我们的 2423 个 SHAP 鉴定的基因,此后被称为“SHAP 基因”。值得注意的是,在每种组织类型的前 1 个基因中(n=47),我们发现其中 44 个是独特的。这表明,SHAP 确实正在确定与我们正在处理的问题相关的独特特征。
作为 SHAP 用于健全性检查的一个说明性例子,我们在撰写科学手稿的过程中发现,如果我们的模型是根据错误转换或非标准化的数据训练的,我们会观察到一些特征,如线粒体基因(线粒体是大多数真核细胞内的膜结合结构,被认为源于细菌,可以被视为化学能量发电站)显示为最显著的特征。然而,这种“显著性”并不是由组织分化的生物学相关性驱动的,而是一种技术人工制品的副产品。这是因为这些基因总是被表达,并且它们的原始表达值比“人类”基因的表达值大得多。因此,在原始尺度上,这些表达方式的任何变化都会被不公平地夸大。然而,在标准化尺度上,相对变化会变得小得多,从而降低各自的显著性。如果我们想象我们想要找到基因作为某些特定疾病的未来治疗靶点,这样的错误会导致灾难性的后果,无论是在经济上还是以牺牲患者的福祉为代价。
普遍性
几十年来,人工智能技术一直受到训练数据过度拟合的影响,这在医疗保健等关键领域可能非常危险。尽管训练/验证分裂加上技术,例如退出、批量标准化等。,可以使问题最小化,但普遍性问题仍然存在。这是因为用于训练/验证的样本来自单一来源,因此可能与另一个独立的收集来源有所不同。这可能是由于收集技术、使用的技术甚至人群中的流行病学差异等因素造成的。出于这个原因,没有什么能像独立的复制一样强有力地检验模型的通用性。因此,我们在完全独立于 GTEx 数据的样本外数据集中重新计算了 SHAP 值。独立的表达谱来源于人类蛋白质图谱(HPA)数据集(21 种组织类型)。当比较两组的结果时,我们发现来自 GTEx 和 HPA 样品的 SHAP 基因之间有很大的重叠。每个组织的这种重叠范围为我们重要基因的 20-60 %,这提供了强有力的证据,表明已识别的特征概括了我们的训练数据集,因此更有可能是组织分化的“真正”因果驱动因素。
生物学相关性
我们还使用 v11 序列研究了 SHAP 基因中每个组织的蛋白质相互作用的数量。我们的发现很有希望!与随机选择的 103 个基因相比,每种组织类型的 103 个 SHAP 基因的蛋白质之间的相互作用数量明显更高!
除此之外,我们还探索了这些基因列表中丰富的生物过程。我们发现这些基因中丰富的过程很大程度上反映了对那些组织应该是重要的过程。例如,心脏组织的富集过程包括心肌纤维,甲状腺的发育途径包括甲状腺激素生成(图 3)。

图三。反应体途径分析。 这种丰富的通路为心脏——AA(心房附件)和甲状腺组织。
与传统方法的比较
edgeR 是用于分析计数数据的差异表达以确定哪些基因显著区分感兴趣的表型(例如组织类型)的当前标准。我们使用这个软件包识别了 30,352 个差异表达的基因,然而,这些基因中的许多在组织中出现不止一次。为了将其与我们独特的 SHAP 基因进行比较,我们折叠了 edgeR 鉴定的基因,仅包括独特的基因,产生了 7854 个基因(称为“edgeR 基因”)。当我们将这些与我们的 SHAP 基因比较时,我们发现 98.6%的重叠(图 4)。

图 4。基因集合的重叠。
有趣的是,我们发现许多顶级 SHAP 基因在个体组织类型中没有通过 logFC edgeR 过滤器。请注意,edgeR 被设计为仅检测具有大效应大小的线性关联,这表明神经网络能够检测组织类型的更微妙的差异。这可能是因为 ML 模型能够同时分析多类数据(“同时看到”所有类),而 edgeR 仅设计用于成对微分。
用 SHAP 分类
如果这些 SHAP 基因真的是与特定组织最相关的基因列表,那么我们会期望它们包含区分组织所需的所有信息。为了测试这一假设,我们使用一种叫做 UMAP 的降维工具来评估我们的重要特征产生的聚类质量,并比较各种控制特征集之间的结果。用于比较聚类质量的特征集包括所有基因、edgeR 基因、2423 个基因的随机子集和 SHAP 基因(图 5)。

图 5。使用不同基因子集的保留 GTEx 样本的 UMAPs。a 所有基因(18884 个基因) b edgeR 基因(7854 个基因) c 随机基因(2423 个基因;代表 UMAP 从 10 个单独的运行),和dSHAP 基因(2423 个基因)。用颜色表示的组织类型(底部图例)。
我们通过在 UMAP 投影上应用 k-means 聚类,然后计算 V-Measure 来评估这些聚类的质量,V-Measure 本质上不仅是对相同标签的样本分组的好坏的度量,也是对每个聚类的同质性的度量。

图六。UMAP k-means 聚类分析的| V-测度。 使用 SHAP 基因进行 k-均值聚类(蓝色;2423 个基因),10 组随机的 2423 个基因(灰色),edgeR 基因(红色;7854 个基因),以及所有基因(黄色;18884 个基因)。
我们的结果表明,即使 SHAP 基因列表少于其他基因列表,它也能够为所有组织类型产生更高质量的聚类(图 6)。
总之,我们展示了如何整合来自 SHAP 方法家族的梯度,不仅通过与传统的生物信息学方法比较而言是可靠的,而且在应用于 RNAseq 数据时具有生物学信息和统计学效果。这项工作是一个巨大的进步,表明精心设计和安全实施的可解释模型有可能促进临床决策,并提供新的生物学见解。
欲了解更多信息,请阅读我们在《自然科学报告》网站上的完整出版物。
参考
- Adebayo,j .,Gilmer,j .,Muelly,m .,Goodfellow,I.J .,Hardt,m .,和 Kim,B. (2018)。显著图的健全性检查。神经炎。
2.加尔格,M. (2009 年)。网络中心性的公理基础。SSRN 电子杂志。
3.刘易斯,f .,巴特勒,a .和吉尔伯特,L. (2010 年)。使用似然比检验的模型选择的统一方法。生态学与进化方法,2(2),第 155-162 页。
4.解释模型预测的统一方法。神经 Inf。过程。系统。2017 年 12 月,4766–4775(2017 年)。
5."我为什么要相信你?"解释任何分类器的预测。 Proc。ACM SIGKDD Int。糖膏剂知道了。Discov。数据最小值。2016 年 8 月 13 日至 17 日,1135 日至 1144 日。
6.Shrikumar,a .,Greenside,p .和 Kundaje,A. (2019 年)。通过传播激活差异学习重要特征。[在线]arXiv.org。上市地点:【https://arxiv.org/abs/1704.02685【2019 年 9 月 4 日上市】。
7.Smilkov 博士、Thorat n、Kim b、VIégas f .和 Wattenberg m .(2019 年)。SmoothGrad:通过添加噪声来消除噪声。[在线]arXiv.org。可在:https://arxiv.org/abs/1706.03825【2019 年 9 月 4 日获取】。
8.深层网络的公理化归因。(2017).doi:10.1007/s 10144–009–0162–4
9.Tomsett,Harborne,d .,Chakraborty,s .,Gurram,p .,& Preece,A. (2020 年)。显著性度量的健全性检查。ArXiv,abs/1912.01451。
10.Wang,q .,Armenia,j .,Zhang,c .,Penson,a .,Reznik,e .,Zhang,l .,t .,Ochoa,a .,Gross,b .,Iacobuzio-Donahue,c .,Betel,d .,Taylor,b .,Gao,j .和 Schultz,N. (2018)。统一来自不同来源的癌症和正常 RNA 测序数据。科学数据,5(1)。
11.Way,g .和 Greene,C. (2017 年)。用变分自动编码器从癌症转录组中提取生物学相关的潜在空间。生物计算 2018。
12.Robinson,M. D .,McCarthy,D. J. & Smyth,G. K. edgeR:用于数字基因表达数据差异表达分析的生物导体包。生物信息学。26, 139–140 (2010).
可解释的 AI:机器学习中的物理学?
如何用物理原理约束你的 ML 模型将使你的系统更具概括性和可解释性
想象一下,给你一个任务,预测一个足球明星在下一场比赛中会进多少个球。一旦你得到了结果,你急忙向你的经理喊出答案:减 3 。在一瞬间,你意识到那个预言的不可思议和荒谬。

由 Linus Mimietz 在 Unsplash 上拍摄的照片
大多数 ML 模型主要从数据中学习。正如流行的说法:垃圾输入,垃圾输出——您输入的数据将被复制到您稍后想要检索的目标输出;如果您的输入数据集有偏差、不一致,甚至更糟、不准确,那么您就不能指望有好的预测。
但是,如果我们可以通过强加一些符合自然法则的物理公式来约束(或引导)这些 ML 模型,会怎么样呢?
数据驱动与理论驱动的建模
在试图描述现实世界中的现象时,我们需要建立能够精确复制这些事件的模型。一般来说,大多数建模方法可以分为两大类:数据驱动或理论驱动的解决方案。
数据驱动方法依赖于使用数据来理解周围的现象,但通常对潜在的理论解释理解有限。例如,你被告知要预测某个特定社区的房价。你有一个很好的工作假设,例如,受欢迎的服务设施的大小和距离将对房价有一定的影响。这肯定是有意义的,但是给定必要的输入,很少有第一原理物理理论可以决定性地提供最终结果。
另一方面,理论驱动的方法试图根据一阶原理构建模型(例如,作用于物体的力由质量及其产生的加速度来描述——F = ma)。模型是有确定性的,只要看数学公式就可以描述系统。
混合方法:物理学指导的机器学习
但是如果你能结合两者的优点呢?
如果你试图解释的现象有足够多的数据,并且可以用物理第一原理部分解释,那该怎么办?
最近的文献中广泛记载了混合方法的一些优点:
- 实现一般化尽管必须预测之前未见过的数据集,但您的模型能够始终表现良好,因为我们已经将“知识”嵌入其中。
- 实现可解释性因为物理公式是可预测的,你可以为一个“黑盒”ML 模型提供洞察力和一致性。
那么如何将物理学结合到我们的 ML 模型中呢?这一领域已经取得了一些进展,大部分集成可以分为三大类:
- 基于物理学的模型作为一个或多个基于 ML 的模块的替代品。
在这个类别中,您包括了一个物理公式作为更大的基于 ML 的模型的一部分。例如,物理公式可以修改输入参数,操纵中间嵌入,或者甚至约束输出预测,如下图所示。

作为 ML 模型的子模块之一的物理模型
2。惩罚物理不一致输出的基于物理的模型
想象一下早先关于预测一个足球明星将要进多少个球的小例子。如果结果低于零(即物理上不太可能),那么该模型可能会极大地惩罚这种预测。这是为了确保模型正确地“学习”与自然物理一致的解释(例如,温度不能低于 0 开尔文,速度不能超过光速,等等)。

物理一致损失函数
3。ML 参数化物理模型
有些问题,如预测气候变量(如降水等)的任务,计算量很大。大多数基于物理学的气候模型往往是全球范围的,具有非常低的空间分辨率(例如每单位像素 25 公里乘 25 公里的面积)。
想象一下,你必须预测下一次洪水何时会在你的地区发生?如果有足够数量的历史数据集,你可以使用纯数据驱动的方法。但是,你可能还想利用一个物理模型,将你的预测与地区/全球正在发生的事情联系起来(因为气候事件毫无疑问是相互关联的)。
一个解决方案是运行你的基于物理的模型,但是你使用 ML 参数化一些计算成本更高的变量(例如降雨量)。
让我们看看如何将这一概念转化为现实世界的案例研究。
应用:湖水温度
在[1]中,作者试图通过结合物理原理和递归神经网络(RNN)来模拟湖水温度。然而,问题是训练数据的稀疏可用性,这可能导致任何数据驱动的 ML 模型表现不佳。
这就是物理学可以填补空白的地方。
首先,论文将一个物理模型集成到 RNN 中。如下图所示,该模型将能量守恒原理(净输入能量将使湖泊变暖,反之亦然)引入 RNN。

RNN 蕴含的节能原则
第二,这种能量守恒的物理现象被用来惩罚不遵循这一规律的预测。

物理一致损失函数
其中最终的损失函数是从典型的 RNN 模块的损失和能量守恒原理的损失相加得到的。

RNN 和混合 PGRNN 模型的误差分析
混合物理引导的 RNN (PGRNN)模型在性能上比裸 RNN 模型提高了近 20%。这显示了物理引导的 ML 模型如何通过服从真实的物理世界来改进基线。
离别的思绪
在未来,我相信如果我们继续将我们的领域知识编码到机器学习中,它会变得“更聪明”。你认为物理引导的 ML 模型还能在哪些领域发展?
做订阅我的邮件简讯:【https://tinyurl.com/2npw2fnz】在这里我定期用通俗易懂的英语和漂亮的可视化总结 AI 研究论文。
参考文献
[1]https://arxiv.org/pdf/2001.11086
回归问题的可解释人工智能(XAI)
许多 XAI 工具的实用指南

机器学习可解释性是什么意思?
2016 年,里贝罗等人发表了他们迄今为止享有盛誉的论文:*"Why should I trust you?": Explaining the Predictions of any Classifie*r。著名的实验*"Husky or Wolf"*指出,尽管在设计一个精确的模型并对其进行适当训练方面付出了巨大的努力,但该模型仍然可以学习不可预测的相关性。例如,如果背景中有雪,模型学会预测照片中的狼。虽然这很有道理,但它并没有学会根据狼的特征来区分狼,而是从图像的背景来区分。
人工智能应用在当今世界蓬勃发展,所有决策权都交给了人工智能。考虑到这一巨大的数量以及需要通过的审查决定,弄清楚为什么做出某个决定变得至关重要。这让位于可解释的人工智能(XAI)。XAI 专注于(从视觉上)弄清楚什么样的特定特征以及在多大程度上影响了预测。这个领域正在开发许多工具和技术,这篇文章将在实践中看看它们。
这篇文章深入探讨了在机器学习工作流程中集成 XAI 工具。我的目标是给出代码示例,并展示一个模型的结果,这在其他文章或工具文档中并不常见。
这里涵盖的工具有:石灰、 SHAP 、谢顿不在场证明和 InterpretML 。
关于“黑盒与玻璃盒”的快速说明
虽然将它们相互对立是不正确的,但是它们是导致不同 ML 算法的两种不同类别的模型。玻璃盒算法类似于线性回归(或其他线性模型)、决策树、贝叶斯分类器,它们产生模型,可以根据模型的组成部分(分配给特征的权重或决策树中遵循的路径)相当容易地检查模型的预测。通过检查模型组件的组合,可以找到特定预测的解释。
黑盒模型通常与深度学习算法相关联。他们倾向于产生更准确的预测,但是牺牲了预测的清晰性和可追溯性。黑盒模型的常见例子是卷积神经网络(CNN),广泛用于计算机视觉、图像中的对象检测和人脸识别领域。
这篇文章中描述的工具和技术是模型不可知的,也就是说,不管模型是玻璃盒模型还是黑盒模型,它们都能够为所做的预测提供清晰度。所有的工具都被设置来解释同一个玻璃盒子模型(即一个 TweedieRegression 模型)。
在另一篇文章中,我将重点介绍这些工具如何用于黑盒模型。
模型
本文中使用的模型是受 scikit-learn 文档中的一个例子的启发,即TweddieRegression。这篇文章关注的是保险领域,特别是汽车保险的索赔金额。通常有多种方法来预测每单位风险(即给定保单的保险期限)的总索赔额。一种方法是在两个不同的模型中模拟索赔频率和遣散费(索赔金额),并计算总索赔金额作为它们的乘积。频率通常通过泊松分布建模,因为有许多保单记录的索赔总数为 0。并且严重性用伽马分布来建模,因为经验表明它具有这样的分布。
或者,当功率参数在 1 和 2 之间时,Tweedie 分布直接模拟总索赔额。网格搜索可以用来找到正确的参数,但在这篇文章中,1.9 被选为功率值。
我决定选择这种模式是因为:
- 这是我想玩的模型。
- 它确实简化了这篇文章的内容。
- 它很好地捕捉了数据。
- 这不是你在在线教程中看到的典型模型,它感觉更接近真实世界的生产场景。
下面是获取数据、执行特征工程以及最终训练模型的完整代码。
设计和训练模型
功能名称
应用ColumnTransformer后,特征名称不再容易阅读。One Hot Encoding (OHE)创造了一整套新功能。此外,KBinsDiscretizer为连续数据装入的每个区间创建一个新特征。对于当前数据点来说,要弄清楚什么是特性名称以及该特性的值是多少有点困难。在我的另一篇文章中,我写了如何从ColumnTransformer中获取可读的特性名称。
讲解人工智能 (XAI)工具
这篇文章集中在这四个 XAI 工具:石灰,SHAP,谢顿不在场证明,和解释。对于它们中的每一个,我将介绍如何将其与模型集成,以及如何针对给定的数据点explain某个预测。
LIME(局部可解释的模型不可知解释)
LIME 是一个模型不可知的工具,它可以对任何给定的监督学习模型给出解释。它提供的解释在被观察的数据点附近是局部可信的。它的简单性和直观性使它成为最流行的 XAI 工具之一。
下面的代码创建了一个explainer,并使用它来识别对给定数据点的预测有贡献的主要特征(在X_test[42]的情况下)。
石灰解释器
结果看起来像这样:
Intercept 211.86354640053418 // Lime model intercept Prediction_local [146.38162887] // LIME model prediction Right: 133.80831374760626 // value from glm_pure_premium.predict
然而,这种文字表述不是很有描述性。为了可视化预测和对其影响最大的特性,我需要运行:explain_data_point.as_pyplot_figure()或explain_data_point.as_list()。结果直观地(或以文本形式)显示了给定数据点的某个特征值如何影响预测(积极或消极)。
explain_data_point.as_pyplot_figure()

给定数据点的局部解释的可视化
explain_data_point.as_list()
[('Category__VehPower_4.0', -118.60886335511677), ('Category__VehBrand_B2', -59.89935149820192), ('Category__VehGas_Regular', -34.65470430935958), ('Category__Area_C', 31.8465417201858), ('Log__Density', 30.911940033440064), ('Segment__DrivAge: [39.0, 43.0)', 28.24619186526209), ('Category__Region_R24', -27.9788217749691), ('Segment__VehAge: [12.0, 15.0)', -19.304050151977467), ('BonusMalus', 0.02413232592200181), ('Segment__VehAge: [2.0, 4.0)', 0.0)]
SHAP ( 沙普利加法解说 )
SHAP 使用博弈论中的沙普利值来解释模型和它们的预测。在 ML 意义上,游戏是预测,玩家是一个数据点的特征及其值。SHAP 解释了平均预测(通常来自训练数据)和给定数据点(或数据点集)的预测结果之间的差异。
SHAP 解说者
SHAP 库提供了不同的方式来可视化每个要素及其在数据点中的相应值如何影响预测。条形图、瀑布图或决策图都是预测如何实现的可视化工具。E[f(X)]是针对给定模型计算的期望值,基本上是来自训练数据集的所有目标值的平均值。对于给定的数据点,其维度会影响预期值(有些多一些,有些少一些),最终得到 f(x)值。
shap.plots.waterfall(shap_values[i], max_display=12)

按重要性排序的要素形状值条形图
期望值通常是explainer.expected_value,并且可以通过explainer.shap_values(X_test[i])检索给定数据点的所有 shapley 值的完整列表。
explainer.shap_values(X_test[i])
array([[-3.57367972e-02, 2.37260265e-05, -3.62400872e-03, 9.99498571e-03, 1.61319891e-02, -1.33205559e-02, -1.70261858e-03, -5.77566345e-02, 2.10600674e-02, -7.60667267e-04, 2.20831452e-02, 1.63428026e-02, 8.70301560e-03, 1.39718551e-01, -6.68123861e-03, -1.28186673e-02, -9.83389108e-03, 2.70578702e-02, -2.45288066e-02, 2.43979909e-02, -6.97570218e-04, 1.84981886e-04, 2.72899951e-02, 0.00000000e+00, -1.75415087e-04, -1.24097019e-01, 1.17185801e-02, -1.13515675e-02, -1.44770143e-02, 3.56013603e-03, -2.78724434e-01, 3.47278038e-02, -2.96024571e-02, 1.28822536e-02, -4.03941874e-03, 7.75558604e-04, 4.69723415e-03, -3.04630041e-03, -6.58506613e-04, 0.00000000e+00, 3.73403677e-04, 0.00000000e+00, -4.67284073e-02, -4.67924568e-02, -7.21878931e-03, -0.00000000e+00, -8.58881460e-06, -0.00000000e+00, -7.78920879e-02, 4.28093791e-03, 4.82906634e-04, -2.98388445e-03, 8.30043843e-03, 0.00000000e+00, -0.00000000e+00, 8.68300158e-03, -2.19710240e-02, 5.07694776e-03, -3.22590630e-04, -9.55616865e-05, 0.00000000e+00, -9.36496765e-05, -0.00000000e+00, 1.53178292e-03, -3.53638524e-02, 1.12146623e-04, -2.42871808e-02, 9.61732427e-03, 5.28887846e-02, 1.01917176e-02, -3.19149583e-03, 1.07590850e-03, 4.24827082e-01, -4.42527041e-02]])
谢顿的不在场证明
谢顿的不在场证明,类似于莱姆和 SHAP,能够为几乎任何 ML 模型提供解释。不过 GitHub 回购似乎并不是很活跃。从我的角度来看,我有一种感觉,它用一个更加一致和更加开发友好的 API 包装了 SHAP 工具和其他一些解释方法,这是由scikit-learn启发的。所以,我并没有在这上面投入太多的精力。尽管如此,以下是如何集成它以及如何可视化结果:
确定特征对模型影响的累积局部效应法

随着 BonusMalus 值的增加,该特征的累积局部效应也增加。
InterpretML
InterpretML 是一个交互式工具,允许用户在探索阶段可视化多个仪表板并获得洞察力。此外,它还提供了以文本(字典)格式返回结果的data方法,这对于工程师来说非常方便。
InterpretML 是一个有趣的工具,因为它已经包括了黑盒解释器下的莱姆和 SHAP 解释器等等。它包括玻璃盒模型(线性模型、决策树)的解释器,在黑盒模型的类别中,它还包括部分依赖图(用于可视化目标和(一组)输入特征的依赖关系),以及莫里斯敏感度分析(用于筛选哪些特征对未来分析足够重要)。
部分相关和莫里斯敏感性解释器

BonusMalus 的部分依赖解释器

莫里斯敏感度解释器
MLOps .玩具
了解更多关于 ML/AI 中可解释工具的一个很好的资源,以及许多对你的 MLOps 之旅有帮助的很酷的工具是 MLOps.toys 。它是由 Aproia 维护的,也是我写这篇文章的原始资料之一。
最后的想法
这篇文章中提到的工具主要面向数据科学家,帮助他们进行数据探索、模型设计或试图找出哪些功能是重要的,哪些是不相关的。它们使组织输出和可视化信息变得容易。
如果这些工具被用在生产环境中,在那里所有的预测/决策都会产生一个解释,它们需要对我来说更加开发友好。这意味着输出应该更多地以字典的形式出现,以便通过 JS 框架更容易地进行格式化、处理和可视化。
interpret ml 是个例外,它提供了用于在字典中生成仪表板的数据,而 Seldon Alibi 在提供一致的 API 方面做了很大的努力。
和任何事情一样,这些工具都不是灵丹妙药。他们不会保护模型免受偏见,他们只能指出这一点。通过足够的努力,误导性的解释可以被创造出来,这可以掩盖偏见。快速的谷歌搜索可以告诉你如何使用酸橙和 SHAP 达到对抗的目的,然而这篇文章的重点是让你更容易专注于正确的方向。
增加对这些工具和技术的信心的重要一点是一致性。根据我对莱姆和 SHAP 的评估,对于相同的模型和相同的数据点,他们对哪个特征(或给定数据点的特征值)在预测中具有最强的影响提供了不同的解释。但总的来说,他们的解释是有道理的,这很好。
终于可以通过 LinkedIn 和我联系了。
可解释的人工智能——打开“黑匣子”的钥匙
人工智能无处不在,需要可解释性
你可能已经注意到了,机器学习算法已经在我们的日常生活中无处不在。他们管理我们看到的内容,建议去办公室的理想路线,决定我们是否是公司职位的好候选人,等等。这种普遍性继续增长,因为它们很快将管理更广泛的基本活动,包括教育、工作、交通和医疗。
当然,这些算法是由人类构建和监督的(在大多数情况下)。但是目前使用的模型,本质上是深度神经网络,是如此复杂[1],以至于它们的创造者几乎不可能理解它们的内部工作,从而产生了术语“黑盒”。正如我们将通过下面的例子看到的,能够解释他们的决定会带来多种好处。

神经网络:受人类大脑的启发,人工神经元最初接收一个值,并将其传输给下一层与之相连的神经元。因此,每个神经元都接收到一组值,这些值是根据每个神经连接的强度聚集起来的。然后,它应用非线性函数,并将获得的值传输到下一层;诸如此类。最终值对应于期望的预测,例如图像显示为狗的概率。
想象一下获得一笔银行贷款。你的申请文件可能会被机器学习模型研究,至少作为第一次筛选。后者使用你事先提供的数据(年龄、性别、工资、房租等。)以及外部数据(父母的职业,按时缴纳的账单等。)以决定是否同意向您提供这笔贷款,或者相反,是否应该拒绝。在这种情况下,能够理解算法的决策首先允许其创建者(例如数据科学家)验证它是否正确工作,并在必要时调试****。事实上,在所有情况下,算法出于正确的原因做出正确的决定是至关重要的。特别是,确保没有偏差是至关重要的,因为算法可能会获得出色的结果,但使用虚假或歧视性的相关性。例如,如果在训练数据中,外国血统的人获得的贷款少于平均水平,则该算法可能已经学会降低如果该人是外国血统的人授予贷款的概率,这既不符合逻辑也不可取[2]。除了设计之外,理解模型的决策使得促进其被企业接受和使用成为可能;因为他们可以由此验证其行为是否符合公司的指导思想。最后,理解模型还允许向客户提供信息反馈,这具有真正的附加值。例如,候选人将被告知,如果有 5k /年的较高工资,并且在接下来的 6 个月里按时支付账单,贷款将被批准。
我借此机会推荐凯茜·奥尼尔(Cathy O'Neil)的《数学毁灭武器》(Weapons of mathematics Destruction)一书,该书通过多种日常情况展示了人工智能如何加剧社会中的不平等并歧视某些人群。
一个可解释的人工智能的好处显然不止于这个特定的用例。无论我们是在看自动驾驶汽车还是人工智能辅助医生检测癌症,我们都需要对这些模型**有完全的信心 才能大规模分发它们,同时确保性能、安全、可靠和公平。只有可解释性允许这一点。**
简而言之,一个可解释的人工智能可以改善算法在某些情况下的行为,避免偏见和歧视,通过产生信心来增加公司内部的采用;向用户提供建设性的个性化反馈。
这一主题的重要性使得欧洲立法对其非常感兴趣。通过一般数据保护条例( GDPR ),欧盟通过排除某些关键算法决策在没有人类监督的情况下做出的可能性,使数据处理行为者承担责任——因此隐含地强加了可解释性。
但是,什么是可解释性?
从一开始,我们就在讨论机器学习模型的可解释性。但实际上,这个概念是什么意思呢?你如何让一个算法变得可解释?
首先你要知道机器学习模型有两大类:
- ****模型因其琐碎的内部工作而被认为是内在可解释的——不需要额外的解释。例如,线性回归或决策树,对于它们,人类很容易理解模型的行为。不幸的是,对于许多应用程序来说,考虑到它们的简单性,它们的性能不够好,因此会一直没有被使用。
- 由神经网络领导的复杂模型通常更有效,但它们的预测很难解释。对于后者,我们应用所谓的“事后可解释性** 方法”,这发生在模型的训练和预测阶段之后,并描述其功能。这个概念和我们人类所做的非常相似。我们不是真正容易理解的个体,并且以一种复杂的方式运作。然而,我们能够在事后通过各种方式解释我们的决定。**
正如你所想象的,我们对第二类特别感兴趣,因为它允许我们获得更好的结果,同时保持算法决策的一定透明度,从而限制了可解释性和性能之间著名的权衡。****

该图简要说明了可解释性和性能之间的权衡,由机器学习模型的选择产生。事后可解释方法使得用线性回归的可解释性获得神经网络(NN)的性能成为可能。来源:[3]
事后方法可以是全局(针对所有实例的模型的一般功能)或局部(针对单个预测的模型功能)。它们适用于任何类型的自动学习算法(模型不可知的)或者特定于精确的架构(模型特定的)。
产生的解释可以采取各种形式,只要它忠实地描述了模型的功能,同时能被人类理解。其中,可能是[3]:
- 衡量模型中每个变量的重要性
- 数据点列表(最有影响力、最具代表性的…)
- 文本解释
- 视觉化
- 局部近似复杂模型的可解释模型
一些事后解释方法
有各种各样的方法导致上述各种形式的解释。你会在下面找到 3 个例子,我希望它们能给你一个更精确的概念,解释方法是如何工作的。
显著图**是对模型内部参数使用数学运算的一系列方法,目的是解释神经网络的功能。仅举几个例子:灵敏度分析、深度提升、Grad-CAM 或 GuidedBackpropagation 。它们都反向传播神经网络中的梯度,以便估计每个输入变量对模型预测的影响。因此,解释是每个变量的重要性分数,或所谓的显著性图(见图片)。这些方法之间的差异体现在反向传播过程中的细微变化上。

来源:https://bdtechtalks . com/rise-explable-ai-example-studential-map/
****LIME【4】围绕它希望解释的实例创建一个新的数据集。对于一个图像,它随机地去除几个超像素,并设计 N 个这样的新图像。然后,它在这组图像上建立一个可解释的模型(即线性回归或决策树)。该模型易于理解,并被用作对起始图像的预测的解释。换句话说,LIME 的目标是干扰已解释的实例,并使用简单的替代模型研究这些干扰对模型预测的影响。导致模型预测发生巨大变化的变量被认为是重要的。
注意:对于线性回归,对应于每个超像素的系数表示其对于该图像分类的重要性。

来源:本地可解释的模型不可知的解释文件,里贝罗
SHAP [5]建立在 Shapley 值[6]的基础上,Shapley 值源于博弈论,描述了如何在玩家之间“公平”地分配游戏的奖金,知道他们都合作了。例如,假设一个由 3 名玩家组成的团队在一次比赛中赢得了 100€,我们希望根据他们各自对项目的贡献在他们之间分配这一收益。事实上,在所有的团队工作中,经常有人比其他人投入更多,或者有独特的技能帮助团队达到另一个水平;所以谁的贡献更高。因此,当每个玩家加入任何可能的玩家联盟时,该方法计算每个玩家对所获得的支出的增加值。在这种特定情况下,我们计算(J1 和 J2 对 J1),(JBOY3 乐队和 J2 对 JBOY3 乐队),(J2 对无人)和(J1 和 JBOY3 乐队和 J2 对 J1 和 JBOY3 乐队)的增益。我们取一个加权平均值,找出 J2(玩家 2)对团队报告的总收益的“公平”贡献。
为了解释机器学习模型,我们通过考虑解释为游戏增益的预测来扩展 Shapley 值,其中每个变量都是该游戏的参与者。所以对于一个模型 f 对一个实例 X 的预测,记为 f(x),一个变量 j 对一个变量联盟 S(变量子集)的边际贡献记为:val(S U j)-val(S)= E[f(X)| X _ S = X _ S,X_j=x_j] - E[f(X)|X_s=x_s] 。变量 j 的 Shapley 值的公式如下(已知有 F 个特征):

公平的概念由 4 个公理定义,Shapley 值是满足它们的唯一解。在实践中,上述总和是不可能计算的,我们求助于近似值,如 SHAP,它利用线性回归来估计每个变量的 Shapley 值,然后将它们作为解释。

SHAP 的输出说明示例。来源:[5]
这些方法必须适应它们接收的数据的不同形态,即:文本、图像、表格或图形。
提供良好的解释
既然我们已经看到了为什么可解释性是必要的,以及它由什么组成,剩下的就是让我们接近如何评估这些方法,以便提出一个模型的好的解释。
从理论角度来看,通常很难定义什么是好的解释。一些研究人员研究了这个问题,并定义了一个理想属性的列表。
- ****精度&保真度:解释是相关的,并且完全符合“黑盒”模型的预测。
- ****稳健性:实例特征或模型运行中的微小变化不会实质性地改变解释。
- ****确定性:解释反映了机器学习模型的确定性。换句话说,解释表明了模型对于所解释的实例的预测的置信度。
- ****含义:解释反映了每个变量的重要性。
- ****代表性:解释涵盖了许多案例或实例,因此模型的功能更一般。
- 可理解性:这个解释很容易让人理解。该属性包括几个方面,下面将更详细地讨论。
的确;从人类理解的角度来看;一个好的解释往往是:
- 选择性:人们不期望解释涵盖一个事件的所有原因,而是希望给出两三个关键因素。
例如:“法国足球队在一场势均力敌的比赛后以 1 比 0 击败了德国队,因为他们在两个罚球区都更加冷静”。 - 对比:人们通常不会问为什么会做出预测,而是问为什么是这个预测而不是另一个预测。
举例:为什么贷款的是他,不是我? - ****社交:讲解是讲解者和接受者互动的一部分,所以必须适应受众。换句话说,面向数据科学家的解释必须不同于面向申请贷款的银行客户的解释。
未来前景
尽管已经有一些可靠且易于使用的解释方法,这方面的研究仍在兴起。因此,在未来几年中,尤其是由于该领域的日益普及,可以预期会有许多改进。除其他事项外,最好更加坚持所提供的解释的性质,发展支持现有方法的先进理论,以及改进这些方法的评估过程,以保证它们在各种情况下的可靠性。最终目标是为任何受众提供可靠、可扩展且易于理解的解释。
本文最初是为“AI for Tomorrow”而写,也可在这里获得(法语)。
参考文献
[1] 3Blue1Brown —但是什么是神经网络呢?|第 1 章,深度学习—https://www.youtube.com/watch?v=aircAruvnKk
[2]凯茜·奥尼,《数学破坏的武器》。2016
[3]杜瓦尔,A. (2019)。可解释的人工智能(XAI)。 MA4K9 学术报告,华威大学数学研究所。
[4]里贝罗、马尔科·图利奥、辛格、萨梅尔和盖斯特林。我为什么要相信你?解释任何分类器的预测。2016 年在 KDD。
[5] Lundberg,s .,& Lee,S. I. (2017 年)。解释模型预测的统一方法。 arXiv 预印本 arXiv:1705.07874 。
[6] Shapley,Lloyd s.《N 人游戏的价值》.对博弈论的贡献 2 (28):第 307-317 页。1953.
[7]莫尔纳尔。可解释的机器学习。2018

可解释的人工智能:为什么企业领导人应该关心?
可解释的机器学习模型如何为企业提供战略利益。

Gert RDA valasevi it 在 Unsplash 上拍摄的照片
人工智能与模型可解释性的挑战
人工智能(AI)已经变得越来越普遍,正在所有行业中被广泛采用。面对越来越大的竞争压力和观察同行的人工智能成功故事,越来越多的组织正在他们业务的各个方面采用人工智能。机器学习(ML)模型是驱动人工智能系统的关键组件,正变得越来越强大,在大多数任务中显示出超人的能力。然而,这种性能的提高伴随着模型复杂性的增加,将人工智能系统变成了一个黑匣子,其决策可能很难被人类理解。采用黑盒模型可能会产生严重的后果,因为系统做出的决策不仅会影响业务成果,还会影响许多人的生活。从驾驶汽车和预防犯罪到产品推荐、做出投资决策、批准贷款和雇用员工,ML 模型越来越多地被用来代替人类决策。因此,对于利益相关者来说,了解这些算法如何做出决定以获得对人工智能在运营中的使用的信任和信心变得越来越重要。因此,对可解释的人工智能(XAI)的兴趣越来越大,这是一个与解释和帮助解释机器学习模型的方法的发展有关的领域[1]。
什么是可解释的 AI?
可解释人工智能(XAI)领域专注于开发工具、框架和方法,以帮助理解机器学习模型如何做出决策。它的目标是提供对复杂的 ML 模型内部工作的洞察,并帮助理解模型决策的逻辑。XAI 有助于为人工智能带来透明度,使其有可能打开黑盒,并以人类容易理解的方式揭示决策过程。模型解释通常是一些视觉或文本指南形式的额外元数据信息,这些信息提供了对特定人工智能决策的洞察或揭示模型整体的内部功能[2]。表达元数据的机制包括文本解释、视觉解释、示例解释、简化解释和特征相关性解释。XAI 是一个快速发展的领域,已经有大量关于可解释机制和技术的文献。我在本文末尾提供了一些参考资料。本文的重点是为可解释的人工智能构建商业案例。
为什么模型的可解释性很重要?
公平、信任和透明是驱动可解释性需求的三个主要关注点。已经发现人工智能系统在许多情况下会产生不公平、有偏见和不道德的决策[3]。例如,人工智能系统筛选申请人已被证明对雇用女性和其他少数族裔有偏见,如亚马逊的招聘引擎显示出对女性申请人的偏见(亚马逊废弃了显示对女性有偏见的秘密人工智能招聘工具)。当管理人员盲目依赖人工智能的输出来增加或取代他们的决策时,公平性就会受到破坏,而不知道模型如何以及为什么做出这些决策,模型是如何训练的,所使用的数据集的质量如何,或者模型何时以及何时效果不好。通过提供对模型工作的深入了解,XAI 促进了公平性,并有助于减轻可能由输入数据集或糟糕的模型架构引入的偏差。
随着模型的复杂性及其决策影响的增加,信任是另一个重要因素。很难相信一个人无法观察和理解的系统的决策。例如,在不清楚算法为什么会提出这些建议的情况下,医生或患者对遵循人工智能算法的建议做出诊断有多大信心?人工智能诊断可能被证明更准确,但缺乏可解释性会导致缺乏信任,从而犹豫使用。模型的可解释性可以帮助建立对其结果的信任,并巩固利益相关者对其使用的信心。
透明度是驱动可解释性需求的第三个关键因素。透明度有助于评估输出预测的质量,了解与模型使用相关的风险,并了解模型可能表现不佳的情况。通过获得对模型行为的直观理解,负责模型的个人可以识别模型可能失败的场景,并采取适当的行动。它还可以通过让业务用户了解可以操纵模型输入来影响输出的方式,来帮助阻止敌对攻击。
除了提高公平性、信任度和透明度,可解释性还可以通过提供对潜在弱点的理解来帮助提高模型性能。理解模型为什么和如何工作以及为什么它有时会失败使 ML 工程师能够改进和优化它。例如,了解不同输入数据分布的模型行为可以帮助解释输入数据中的偏斜和偏差,ML 工程师可以使用这些数据进行调整,并生成更稳健和公平的模型。
可解释人工智能的商业价值
可解释的人工智能对商业领袖也有战略价值。可解释性可以加速人工智能的采用,实现问责制,提供战略洞察力,并确保道德和合规性[4]。由于可解释性有助于在 ML 中建立利益相关者的信任和信心,它增加了 AI 系统在组织中的采用,为其提供了竞争优势。可解释性给了组织领导者信心,让他们在业务中接受人工智能系统的责任,因为这让他们更好地理解系统的行为和潜在风险。这促进了对人工智能项目的更大的执行买入和赞助。在关键利益相关方和高管对人工智能的支持下,该组织将更好地促进创新、转型和开发下一代能力。
可解释的模型还可以帮助提供对关键业务指标的有价值的见解,如销售、客户流失、产品声誉、员工流动等。,从而可以改善决策和战略规划[4]。例如,许多公司采用机器学习模型来衡量客户情绪。虽然理解客户情绪是有价值的,但模型解释也可以提供对价格、客户服务、产品质量等情绪驱动因素的洞察。,以及它们对客户的影响,允许企业采取适当的措施来解决这些问题。同样,许多公司使用销售预测模型来预测销售和计划库存。如果预测模型也能显示价格、促销、竞争等关键因素。有助于销售预测,该信息可用于促进销售。
监管合规性正迫使一些企业采用可解释的人工智能实践(新的人工智能法规即将出台。你的组织准备好了吗?)。组织面临来自客户、监管机构和行业联盟的越来越大的压力,以确保他们的人工智能技术符合道德规范,并在公众可接受的范围内运行。监管重点包括保护弱势消费者、确保数据隐私、促进道德行为和防止偏见。表现出非故意的人口统计偏差的模型尤其令人担忧。使用可解释的模型是检查偏见和决策的一种方式,这种方式不会违反商业道德规范并防止声誉损失。从数据隐私的角度来看,XAI 可以帮助确保只有被允许的数据被用于模型训练的商定目的,并使之有可能在需要时删除数据。重要的是从一开始就在人工智能训练中建立道德指南针,并通过 XAI 评估监控人工智能的行为。
可解释的人工智能应该是一个组织的人工智能原则的必要元素。
由于可解释性是一个如此关键的要求,可解释的人工智能必须包含在每个组织的人工智能原则中,并成为他们人工智能战略中的一个关键考虑因素。可解释性不能是事后的想法,必须从一开始就计划好,并集成到整个 ML 生命周期中。一种将公司的人工智能设计和开发与其道德价值观、原则和风险偏好相结合的正式机制可能是必要的。确保业务经理理解风险和无法解释的模型的局限性,并能够对风险负责,这一点很重要。
参考文献:
【1】Linardatos,p .,Papastefanopoulos,v .,& Kotsiantis,S. (2021)。可解释的人工智能:机器学习可解释性方法综述。熵,23(1),18。
【2】达什·阿勒德(2020)&拉德普。可解释人工智能(xai)的机遇和挑战:一项调查。预印本 arXiv:2006.11371。
[3]罗伯特,L. P .,皮尔斯,c .,马奎斯,l .,金,s .,T12 阿拉马德,R. (2020)。为管理组织中的雇员设计公平的人工智能:回顾、评论和设计议程。人机交互,35(5–6),545–575。
[4]奥克斯伯勒,c .、卡梅伦,e .、拉奥,a .、伯查尔,a .、汤森,a .、&韦斯特曼,C. (2018)。可解释的人工智能:通过更好的理解驱动商业价值。检索自普华永道网站:https://www . PWC . co . uk/audit-assurance/assets/explable-ai。pdf 。
【5】阿里埃塔,A. B .,迪亚斯-罗德里格斯,n .,德尔塞尔,j .,本尼托,a .,塔比克,s .,巴尔巴多,a,...&埃雷拉,F. (2020)。可解释的人工智能(XAI):面向负责任的人工智能的概念、分类法、机遇和挑战。信息融合,58,82–115。
用线性树解释人工智能
模型可解释性
提取符合线性模型的有意义的决策规则

zbynk skr enn在 Unsplash 上的照片
在人工智能领域,在开发机器学习管道时,准确性和可解释性之间的权衡是一个至关重要的方面。准确性是指模型预测的正确程度,而可解释性规定了人类理解结果的难易程度。两者之间的平衡关系到最终的业务需求。
当我们试图解释我们的 ML 管道的输出时,我们应该考虑一个重要的方面。可解释性不一定导致可解释性。如果人们没有足够的知识来理解预测,从预测中提取洞察力的可能性可能是无用的。一些模型不可知的技术可能就是这种情况,这些技术可以附加在流程的最后一步,以提供可解释性的结果。尽管它们可能是非常强大的工具,但如果使用不当,它们对于非技术人员来说可能太多了。如果我们试图解释可解释性得分计算背后的启发性,就会遭到致命一击。
最好的解决方案是保持简单。表现出最佳和最简单的解释洞察力的模型是线性或基于树的算法。它们也是最著名和最容易理解的两种算法。线性回归(分类上下文中的逻辑回归)提供了检查系数大小的可解释性结果。同样,我们可以从决策树中提取决策规则形式的学习历史。
在大多数情况下,简单是低预测能力的同义词。这是一个错误和草率的考虑。性能是相对的,取决于分析领域、数据质量和正确选择适当的验证策略。不变的是我们所掌握的工具的解释能力。因此,为了从最简单的方法中获得最佳效果,无论是在性能上还是在解释力上,我们试图将它们结合起来。
在这个背景下,我们引入了 线性树 : 一个 python 库来构建在叶子处具有线性模型的模型树。该软件包以 sklearn 风格提供了简单的基本估计器,以包装sklearn.linear_model中可用的每个线性估计器,并构建一个最佳树结构。在训练过程中,最佳分裂被评估为在所接收的数据集上拟合线性模型。最终模型由基于树的结构组成,在叶子中具有线性模型。换句话说,多元线性回归是根据简单的决策规则对数据进行分区计算的。
在这篇文章中,我们利用 线性树 的能力来提供有用的解释。我们通过执行一项预测任务使之成为可能。作为训练过程的结果,我们可以完全免费地检索一些非常简单和强大的可解释的结果。
数据
我们从 Kaggle 收集了一个金融数据集。有了这些数据,我们的目标是建立一个预测算法来量化某人在未来两年经历财务困境的概率。我们有不同的数值变量,可以用于我们的模型:
RevolvingUtilizationOfUnsecuredLines:信用卡和个人信用额度的总余额除以信用额度百分比之和;age:借款人年龄,单位为年;NumberOfTime30-59DaysPastDueNotWorse:借款人在过去 2 年中逾期 30-59 天的次数;DebtRatio:每月债务偿还额除以每月总收入;MonthlyIncome月收入;NumberOfOpenCreditLinesAndLoans:未结贷款数量(分期付款,如车贷或按揭)和信用额度(如信用卡);NumberOfTimes90DaysLate:借款人逾期 90 天或以上的次数;NumberRealEstateLoansOrLines:抵押和房地产贷款笔数;NumberOfTime60-89DaysPastDueNotWorse:借款人在过去 2 年中逾期 60-89 天的次数;NumberOfDependents:家庭中不包括自己的被赡养人数。
外生变量是可以自我解释的,并且非常适合我们实验的目的。
建模和解释
我们的范围是拟合一个线性树,以产生易于解释的结果。在这个模拟中,我们使用所有的数据进行训练。我们使用来自 sklearn 的RidgeClassifier作为线性估值器来构建我们的树形结构。不同的参数配置可能产生不同的树结构,从而导致不同的结果。选择和验证最好的一个是我们的责任。

从拟合线性树得到的树结构(图片由作者提供)
我们对树叶特别感兴趣。与经典决策树一样,每片叶子都是在每条探索路径中应用的修剪过程的结果。当不再有收益时,就通过将数据分割成更多分区来减少损失而言,增长过程会停止。我们以根据简单的决策规则划分成不同组/叶的训练数据的“聚类”版本结束。

将训练样本分发到树叶中(图片由作者提供)
每个叶片还包含一个拟合的线性模型。我们正在构建一个线性树,并且考虑来自左右子候选的训练损失的加权和来评估分裂。我们最终在每片叶子中有一个拟合的线性模型,该模型预测满足一组给定决策规则的所有样本。

从所有叶子的线性模型中提取的系数(图片由作者提供)
此时,我们可以检查结果。查询线性模型,我们提取系数,并查看树叶中每个变量的影响。这还不是全部。我们还可以获得在训练期间建立的决策规则集。它们有助于识别给定样本属于哪片叶子。通过决策规则和系数的组合,我们对模型遵循的决策路径有了一个完整的概述。

树叶 3、4、7 的系数(左边)和决策规则(右边)(图片由作者提供)

来自叶子 8、10、11 的系数(左边)和决策规则(右边)(图片由作者提供)

来自叶子 16、17、19 的系数(左边)和决策规则(右边)(图片由作者提供)

来自叶子 20、21、22 的系数(左边)和决策规则(右边)(图片由作者提供)
摘要
在这篇文章中,我们介绍了作为构建线性树工具的https://github.com/cerlymarco/linear-tree线性树包。它们可以被认为是理解特征之间的关系并提供可解释结果的有价值的解决方案。线性树利用决策树和线性模型的组合来帮助我们更好地解释我们的预测。然而,像所有其他算法一样,它们需要适当的调整和数据理解,以实现所需的性能。
保持联系: Linkedin
可解释的人工智能(XAI)——用 Python 写的 7 个包来解释你的模型的指南
介绍各种框架和 web 应用程序,解释和说明 Python 中的机器学习(ML)模型

凯文·Ku 在 Unsplash 上的照片
在过去的几年里,可解释的人工智能已经取得了重大进展。将这些黑盒模型转换成透明和可解释的算法的追求已经在学术界和商界获得了牵引力。许多文章、出版物和开源贡献现在使得解密这些复杂的模型变得容易,并把它们变成白盒,特别是对于商业用户、经理和领域专家。今天涉及生产 ML 模型的分析项目将这种可解释的人工智能作为交付的关键组件,以支持用户对可理解性和透明性的需求。
不久之前
在 SHAP 和时间等方法流行起来之前,标准模型解释(在全球层面上)具有一定的可解释性,包括可变重要性(基于 ML 算法的不同方法)、决策树、部分依赖图(PDP)、个体条件期望图(ICE) 和经典回归系数等方法。在我们获得安慰和信任之前,人类需要了解事物是如何运作的。更高的模型复杂性导致更少的可解释性,这种经典的权衡现在已经得到控制。这些新的方法应用了诸如创建复制模型的方法,这些方法在局部范围内模拟原始模型的行为,并有助于解释某种预测是如何做出的。
XAI 新时代
虽然近年来开发了许多包/方法,但本文旨在简要介绍 Python XAI 版中一些流行的包/框架,包括它们的基本特性和规范。下面还提供了代码片段示例和可视化效果,以提供输出的要点。请注意,这些包具有不同的依赖性,可能会在 Python 环境中产生冲突。建议设置不同的环境来运行这些代码,避免版本冲突。还要注意,我并不提倡这里的任何包,下面的一些评论是我基于使用和经验的个人意见。由于本文仅提供了一个介绍性的概述,因此每一节都提供了多个参考资料和文章链接,以供进一步阅读。
在这篇文章中,我们将使用乳腺癌数据集作为一个例子来描述来自各种包的一些输出。在该数据集中,从乳腺肿块细针抽吸(FNA)的数字化图像中计算特征。它们描述了图像中出现的细胞核的特征。任务是进行二元分类预测。
我们开始吧!
首先,让我们设置数据集。

图片来源:作者
让我们构建一个基本的 Xgboost 模型,我们将在各个包中使用它。

现在我们开始分析来分解这个模型,并使它的运行变得透明。
1。SHAP
今天最流行的方法之一,SHAP (SHapley 附加解释)是一种基于博弈论的方法来解释任何 ML 模型的输出。它将最优信用分配与使用博弈论及其相关扩展的经典 Shapley 值的本地解释联系起来。

图片来源:SHAP Github 页面
SHAP 帮助你理解影响的大小(条形的长度)和方向(颜色)。

图片来源:SHAP Github
SHAP 是一个模型不可知的框架。关于方法论的更多细节可以在他们的第页和论文中找到,比如由伦德伯格和李撰写的论文。Ula La Paris 的另一篇理解数学的好文章是一个面向例子的解释,可以在这里阅读。这个包现在很容易成为最受欢迎的选择之一,在过去几年中,它在模型可解释性的各个方面都有直观和引人入胜的可视化。下面给出了一些可视化效果,以及生成这些效果的代码。

图片来源:作者
对于数据科学家来说,这是一个很好的起点。围绕使用这些值来生成更吸引人的可视化和洞察力的创造性方面在于数据科学家。下面的文章就是一个例子。
这个包充当了许多基于仪表板和应用程序的框架的基础,这些框架将在本文后面介绍。
2.石灰
Lime 是第一个在可解释性领域流行起来的技术之一。Lime 代表本地可解释的模型不可知解释。本论文涵盖了 Lime 背后的方法论。目前,Lime 有助于解释表格数据、图像和文本分类器的预测。
Lime 基本上试图通过创建局部代理模型来给出模型行为的局部线性近似,该局部代理模型被训练来局部模拟 ML 模型的预测。虽然全球重要性显示了对整个数据集的平均影响,但每个变量可能以不同方式影响局部观察。这个代理模型可以是从 GLMs 到决策树的任何东西,它试图理解局部重要性可能如何不同。Christoph Molnar 在 interpretable-ml-book 中提到的一般框架是:
- 选择您感兴趣的实例,您希望了解其黑盒预测的解释
- 扰动数据集并获得这些新点的黑盒预测
- 根据新样本与感兴趣的实例的接近程度对其进行加权
- 在具有变化的数据集上训练加权的、可解释的模型
- 通过解释本地模型来解释预测
但是石灰有一定的局限性,应该谨慎使用(更多细节请参考本文文章)。这篇文章为想要深入研究石灰的人提供了有趣的实验和进一步的参考。
在我们的数据集上,我们解释观察 79。

图片来源:作者
3.沙帕什
交互性和引人入胜的视觉效果是传达数据故事、见解和模型结果的关键。将这些编译到笔记本或网络应用程序中代表了商业和数据科学家/分析师应该如何呈现 AI/ML 成果并与之交互的理想方式。沙帕什朝那个方向迈出了一步。这是一个由法国保险公司 MAIF 的数据科学家构建的 Python 库。这个软件包围绕 SHAP/莱姆可解释性编译了各种可视化,并发布了一个易于使用的交互式仪表板作为一个 web 应用程序。
它使用 Shap 或 Lime 后端来计算贡献。Shapash 依靠不同的必要步骤来构建一个 ML 模型,以使结果可以理解。

图片来源:shapash
它适用于回归、二元分类或多类问题,并与许多模型兼容: Catboost 、 Xgboost 、 LightGBM 、 Sklearn Ensemble 、线性模型和 SVM 。

图片来源:shapash
我们的数据集和相关代码的一些可视化分享如下。我们继续使用我们的 Xgboost 模型作为与这个包兼容的例子。

图片来源:作者
Shapash 最近还在软件包中添加了报告和文档功能,这将在本文的中详细介绍。这个包越来越受欢迎,被许多媒体文章和 YouTube 视频所覆盖。
4.解释器仪表板
由 Oege Dijk 构建的 ExplainerDashboard 是一个广泛的、引人入胜的交互式仪表板库,用于解释各种频谱和方法中的 ML 模型。与 Shapash 相比,这要详细得多,即不仅限于 SHAP 或石灰。

图片来源: Explainerdashboard
这个仪表板涵盖了 ML 可解释性的各个方面,例如:
- 特征重要性
- 度量和评估
- 局部预测可解释性
- 如果分析呢
- 决策树
- 功能依赖关系
- 相互作用
作者撰写的一篇中型文章详细介绍了软件包和各种输出。
还有作者的 PyData GLobal 2020 talk 。
虽然这个包有许多可视化和输出。这个仪表板可能会有各种各样的组件,但它是模块化的,允许在创建自己喜欢的仪表板版本时有很大的灵活性和定制性。还有内联解释器来查看单个组件。下面是一些例子,后面是代码。

图片来源:作者
您可以用下面的一行代码创建预构建的仪表板。您可以根据需要切换和打开/关闭各种选项卡。

关于这个包的更详细的指南可以在 Ravi 的这篇文章中找到。
5.Dalex
DrWhy 的一部分。AI , Dalex 是一个流行的库,它提供了各种 ML 框架的包装器。然后可以研究这些包装器,并与一组本地和全局解释器进行比较。
这本电子书解释了 Dalex 的哲学和方法论细节。它涵盖了模型可解释性的各个方面,如下图所示。

图像来源于 DALEX
Python 和 R 版本的软件包涵盖了可变重要性、PDP 和 ALE 图、分解图和 SHAP 瀑布图。它还包含一个用 Python 包装的原生 SHAP 包。这个包可以和各种 ML 框架一起工作,比如scikit-learn、keras、H2O、tidymodels、xgboost、mlr或者mlr3.

图像来源解释性模型分析
电子书中介绍了故障和实时方法,在此文章中进一步解释了是此软件包的独特之处。这不同于通常的 SHAP 法和石灰法。这个包是为数不多的几个也有公平模块的包之一。笔记本示例可在这里找到。一些模块如其他条件不变有一些吸引人的视觉效果,如下图所示。

图像来源解释性模型分析,泰坦尼克号数据集
在我们的数据集上使用这个包,我们用下面给出的代码片段生成了一些视觉效果。

图片来源:作者
所有的情节都是互动的,与 Plotly 有一个整洁的集成,可以很容易地保存。Dalex 也不会落在仪表板空间的后面。 Arena 是 Dalex 的交互式拖拽仪表盘。视觉效果相当不错,界面也很整洁。下面是我们数据集中的一张快照,您可以从中一窥仪表盘。代码片段只有几行,如下所示。

图片来源:作者
6.可解释的增压机(EBM)
这是街区里新来的孩子。由 Interpret ML 创建,Interpret 是微软的一个开源包,它有一个“玻璃盒子”模型模块,可以解释。这并不意味着性能上的损失,因为 EBM 在一些数据集中表现出与其他增强方法相当的性能。
基于加性模型的思想,这种方法改变了训练过程,从训练所有特征的决策树到一次一个特征的决策树。以小深度和增强方式为每个特征构建多个树。基于其所有树的求和,我们可以用输出变量来估计输入变量的函数( f) 。这里有一些作者的好视频。论文也可以在这里获取。每个特征对最终预测的贡献可以通过其 f 来理解。由于 EBM 是一个附加模型,每个特征以模块化的方式对预测做出贡献,这使得推理每个特征对预测的贡献变得容易。
代码架构非常简单。下面这张来自官方论文的图片给了我们一瞥。

图片来源:由解读 ML
除了玻璃盒模型,人们还可以使用通常的 SHAP,石灰,PDP 和包装的灵敏度组件。这个也兼容scikit-learn。这篇 Medium 文章为 SVM 和 Xgboost 提供了一个很好的使用这个包的例子。这个包还允许您将各种交互式小部件编译成一个简洁的仪表板。在他们的 github 页面中有很多例子。然而,我发现这个包仍然处于早期阶段,并且在一般的东西上是有限的,例如,模型度量等等。相对于其他软件包,可视化效果也没有那么吸引人。然而,这种方法有望获得相当的性能。
在我们的数据集上,下面给出了一些可视化和代码。

图片来源:作者
这些都可以打包到一个仪表板中。

图片来源:作者
7.ELI5
ELI5 是麻省理工学院的另一个可解释性包,支持 ML 框架和包,如 Scikit-learn、Keras、Xgboost、等。有了统一的 API,跨包和框架比较模型变得更加容易。还有许多其他组件,如文本解释器,它使用 Lime 算法和置换重要性。这个包巧妙地将解释分为两个部分:
- Global 是
[show_weights(](https://eli5.readthedocs.io/en/latest/autodocs/eli5.html#eli5.show_weights)),您可以在其中提供所需的参数,以指定您希望看到的全局重要性类型(例如,“增益”、“重量”、“增益”、“覆盖”、“总增益”、“总覆盖”) [eli5.show_prediction()](https://eli5.readthedocs.io/en/latest/autodocs/eli5.html#eli5.show_prediction)给出了局部水平解释,解释了局部水平预测
其他函数如[eli5.formatters](https://eli5.readthedocs.io/en/latest/autodocs/formatters.html#module-eli5.formatters)可以用来获取 HTML、文本、JSON 或 PIL 图像等。解释的表示。
我在这个包里找不到引人入胜的视觉效果。跨各种 ML 包/框架的统一平台是它的主要亮点。下面给出了一些输出和代码片段。

图片来源:作者
8。这还不是全部!
虽然这篇文章涵盖了使你的模型透明和可解释的最流行的选项,但还有其他可以进一步探索的包,如涵盖一些不同方法的 Alibi ,如 Anchors 。 Skater 是另一个软件包,它以一个酸橙叉开始,但后来作为一个独立的框架爆发。还有其他一些有趣的项目值得探索,比如 IBM 的 EhticalML 、 Aix360 、ML 的多样性反事实解释(DiCE) ,它来自于我们上面为 EBMs 介绍的 interpret ML。Ai 等。对于那里的 R 爱好者来说,在文章 可解释人工智能的 R 包景观 中可以找到 XAI 上类似的精选包列表。
结论
这一研究领域和开源贡献正在快速发展,这与解释、调试和验证我们的模型决策是多么重要是一致的,特别是当实际的人工智能模型部署呈上升趋势时,以及这些算法在我们的日常生活中变得多么有影响力。上述软件包只是一个起点,可供数据科学家、工程师和分析师进一步利用和构建。这将提高对基于人工智能的解决方案的信任,从而加快其采用。
参考文献:
[1]https://towardsdatascience.com/tagged/xai
[2]https://towards data science . com/unbox-the-black-box-using-lime-5c 9756366 faf
[3]https://www . kdnugges . com/2021/04/shapash-machine-learning-models-understand . html
【https://pypi.org/project/explainerdashboard/
https://dalex.drwhy.ai/python-dalex-xgboost.html
[9]https://uc-r.github.io/dalex
[10]https://towards data science . com/decrypting-your-machine-learning-model-using-lime-5 ADC 035109 b5
[11]https://towards data science . com/explaining-machine-learning-classifiers-with-lime-def 5 adaacfea
[12]https://towards data science . com/whats-错在哪里-lime-86b335f34612
[13]https://towards data science . com/the-explaible-boosting-machine-f 24152509 ebb
[14]https://towards data science . com/interpret ml-analysis-of-SVM-and-xgboost-models-e 68062 f 7299 f
[15]https://www . analyticsvidhya . com/blog/2020/11/demystifying-model-interpretation-using-Eli 5/
[16]https://Eli 5 . readthe docs . io/en/latest/overview . html #特性
[17]https://medium . com/analytics-vid hya/why-should-I-trust-your-model-bdda 6 be 94 c6f
[19]https://awesomeopensource.com/projects/xai?categoryPage=7
【http://xai-tools.drwhy.ai/ 20】
无监督深层异常探测器的可解释人工智能(XAI)设计
用于检测非分布样本和敌对攻击的可解释原型
无监督深度卷积神经网络的可解释原型&基于 lstm 自动编码器的高维异构/同构时间序列多传感器数据实时异常检测
你好,朋友们。在这篇博文中,我将带您了解包“ msda ”的新特性。更多细节可以在 GitHub 页面这里找到
MSDA 1 . 10 . 0 版有什么新功能?
MSDA 是一个用 Python 编写的开源low-code多传感器数据分析库,旨在减少时序多传感器数据分析&实验中从假设到洞察的周期时间。它使用户能够快速高效地执行端到端的概念验证实验。该模块通过捕捉变化和趋势来识别多维时间序列中的事件,以建立旨在识别相关特征的关系,这有助于从原始传感器信号中选择特征。此外,它还提供了一个无监督的深度卷积神经网络&来精确检测实时流数据中的异常。此外,还设计了一个基于 lstm 自动编码器的检测器,可在 GPU/CPU 上运行。最后,用博弈论的方法来解释所建立的异常检测器模型的输出。
该套餐包括:-
- 时间序列分析。
- 每个传感器列 wrt 时间的变化(增加、减少、相等)。
- 每列值相对于其他列如何变化,以及每列相对于其他列的最大变化率。
- 与趋势阵列建立关系,以确定最合适的传感器。
- 用户可以选择窗口长度,然后检查每个传感器列的每个窗口的平均值和标准偏差。
- 它为高于或低于阈值的每个传感器列值提供增长/衰减值的计数。
- 特征工程 a)涉及各种汇总窗口值趋势的特征:平均值的变化和变化率,标准偏差。窗口偏差。b)变化率、增长率与标准偏差。偏差。c)随时间变化。d)随时间的变化率。e)增长或衰退。f)增长率或衰减率。g)高于或低于阈值的值的计数。
- ** 无监督深度时序异常检测器。 **
- ** 用博弈论的方法解释时序数据模型。 **
MSDA 是simple、easy to use和low-code。
谁应该使用 MSDA?
MSDA 是一个任何人都可以使用的开源库。在我们看来,MSDA 的理想目标受众是:
- 快速概念验证的研究人员。
- 希望提高工作效率的经验丰富的数据科学家。
- 偏好低代码解决方案的公民数据科学家。
- 数据科学的学生。
- 参与构建概念验证项目的数据科学专业人员和顾问。
异常重访
什么是异常现象,为什么会引起关注?通俗地说,“异常”或“异常值”是一个数据空间中的数据点,这些数据点是异常的,或脱离趋势的。异常检测侧重于识别数据中以某种方式偏离预期或典型的例子。现在,问题是,“你如何定义某件事是不正常的或异常的?”快速基本原理答案是样本空间中所有不跟随相邻点趋势的点。
对于任何业务领域,从大量数据中检测可疑模式都非常关键。比方说,在银行领域,欺诈性交易对银行构成了严重的威胁&损失/责任。在这篇博客中,我们将尝试学习如何在没有事先训练模型的情况下从数据中检测异常,因为你无法在数据上训练模型,而我们对此一无所知!这就是无监督学习的整个想法有所帮助的地方。我们将看到两种用于构建实时异常检测器的网络架构,即 a)深度 CNN b) LSTM 自动编码器
这些网络适合于检测大范围的异常,即时间序列数据中的点异常、上下文异常和不一致。由于这种方法是无人监督的,它不需要异常标签。我们使用未标记的数据来捕获和学习用于预测时间序列正常行为的数据分布。第一种架构的灵感来自 IEEE 论文 DeepAnT ,它由两部分组成:时间序列预测器和异常检测器。时间序列预测器使用深度卷积神经网络(CNN)来预测定义的地平线上的下一个时间戳。该组件获取一个时间序列窗口(用作参考上下文),并尝试预测下一个时间戳。预测值随后被传递给异常检测器组件,该组件负责将相应的时间戳标记为非异常或异常。
第二种架构的灵感来自这篇自然论文基于深度 LSTM 的多变量时间序列堆栈自动编码器
先简单了解一下什么是自动编码器神经网络。自动编码器架构用于以无监督的方式学习有效的数据表示。一个自动编码器有三个组件:压缩数据的编码(输入)部分,在该过程中学习数据集的表示(编码),处理压缩数据(尺寸减小)的组件,以及从压缩数据中重构尽可能接近原始输入的学习表示同时最小化总损失函数的解码器(输出)部分。因此,简单地说,当数据被送入自动编码器时,它被编码,然后被压缩到更小的尺寸,并且更小的表示被解码回原始输入。接下来,让我们理解,为什么 LSTM 在这里是合适的?什么是 LSTM ?长短期记忆 ( LSTM )是一种能够学习序列预测问题中顺序依赖关系的神经网络架构。LSTM 网络是一种递归神经网络 (RNN)。RNN 主要遭受消失梯度。渐变包含信息,随着时间的推移,如果渐变消失,那么重要的局部信息就会丢失。这就是 LSTM 的难处,因为它有助于记忆保存信息的细胞状态。其基本思想是,LSTM 网络内部有多个带有训练参数的“门”。其中一些门控制模块的“输出”,另一些门控制“遗忘”LSTM 网络非常适合基于时间序列数据进行分类、处理和预测,因为时间序列中的重要事件之间可能存在未知持续时间的滞后。
LSTM 自动编码器是使用编码器-解码器 LSTM 网络架构的用于序列数据的自动编码器的实现。
现在,我们已经了解了每个网络的基本概念,下面让我们来看看两个网络的设计,如下所示。DeepCNN 由两个卷积层组成。通常, CNN 由一系列层组成,包括卷积层、汇聚层和全连接层。每个卷积层通常有两个阶段。在第一阶段,该层执行称为卷积的数学运算,这导致线性激活。在第二阶段,非线性激活函数被应用于每个线性激活。像其他神经网络一样,CNN 也使用训练数据来调整其参数(权重和偏差)以执行学习任务。使用 ADAM 优化器优化网络参数。内核大小和过滤器数量可以根据数据集进一步调整,以获得更好的性能。此外,辍学率、学习率等。可以通过微调来验证网络的性能。使用的损失函数是 MSELoss (平方 L2 范数),用于测量输入“x”和目标“y”中每个元素之间的均方误差。 LSTMAENN 由多个 LSTM 层堆叠而成,具有 input_size —输入 x 中的期望特征数、 hidden_size —隐藏状态下的特征数 h、 num_layers —递归层数(默认为 1)等。更多详情请参考此处。为了避免将数据中检测到的噪声解释为异常,我们可以调整额外的超参数,如“回看”(时间序列窗口大小)、隐藏层中的单位等。
无监督深层异常模型
- ** 深度卷积神经网络 **
DeepCNN(
(conv1d_1_layer): Conv1d(10, 16, kernel_size=(3,), stride=(1,))
(relu_1_layer): ReLU()
(maxpooling_1_layer): MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(conv1d_2_layer): Conv1d(16, 16, kernel_size=(3,), stride=(1,))
(relu_2_layer): ReLU()
(maxpooling_2_layer): MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(flatten_layer): Flatten()
(dense_1_layer): Linear(in_features=80, out_features=40, bias=True)
(relu_3_layer): ReLU()
(dropout_layer): Dropout(p=0.25, inplace=False)
(dense_2_layer): Linear(in_features=40, out_features=26, bias=True)
)
- ** LSTM 自动编码器 **
LSTMAENN(
(lstm_1_layer): LSTM(26, 128)
(dropout_1_layer): Dropout(p=0.2, inplace=False)
(lstm_2_layer): LSTM(128, 64)
(dropout_2_layer): Dropout(p=0.2, inplace=False)
(lstm_3_layer): LSTM(64, 64)
(dropout_3_layer): Dropout(p=0.2, inplace=False)
(lstm_4_layer): LSTM(64, 128)
(dropout_4_layer): Dropout(p=0.2, inplace=False)
(linear_layer): Linear(in_features=128, out_features=26, bias=True)
)
现在,我们已经设计了网络架构。接下来,我们将通过下面给出的实际操作演示来完成进一步的步骤。
入门
1)安装软件包
安装 msda 最简单的方法是使用 pip。
pip install msdaOR$ git clone https://github.com/ajayarunachalam/msda
$ cd msda
$ python setup.py install
笔记本
!pip install msda
2)导入时间序列数据
这里,我们将使用和的气候数据。该数据集由几个公共来源编译而成。该数据集由加拿大 13 个中心的每日气温和降雨量组成。降水不是雨就是雪(冬天可能会下雪)。1940 年,13 个中心中有 7 个有每日数据,但到了 1960 年,所有 13 个中心都有每日数据,偶尔会有缺失值。我们有大约 80 年的记录(数据的每日频率),我们希望从这些气候数据中识别异常。如下图所示,该数据有 27 个特征,大约 30K 条记录。
df = pd.read_csv('Canadian_climate_history.csv')
df.shape
=============(29221, 27)
3)数据验证、预处理等。
我们首先检查缺失值,并估算这些缺失值。
预处理& ExploratoryDataAnalysis 类中的 missing() 和 impute() 函数可用于查找缺失值,并填充缺失信息。我们用平均值替换缺失值(因此,modes=1)。这些类中有几个实用函数,可用于分析数据集、手动过滤异常值等。此外,提供的其他选项包括日期时间转换、获取数据的描述性统计、正态分布测试等。更多细节请看这里
'''
Impute missing values with impute function (modes=0,1, 2, else use backfill)
0: impute with zero, 1: impute with mean, 2: impute with median, else impute with backfill method'''
ExploratoryDataAnalysis.impute(df=df, modes=1)
4)后处理数据以输入到异常检测器中
接下来,我们输入没有丢失值的数据,删除不需要的字段,断言时间戳字段,等等。在这里,用户可以输入要删除的列和它们的索引值,还可以用它们的索引值断言时间戳字段。这将返回两个数据帧,一个包含所有不带时间戳索引的数字字段,而另一个包含所有带时间戳索引的数字字段。我们需要使用一个带有时间戳的数据库作为后续步骤的数据索引。
Anamoly.read_data(data=df_no_na, column_index_to_drop=0, timestamp_column_index=0)
5) 用户输入时间窗口大小的数据处理
时间窗口大小(回看大小)作为 Anamoly 类的函数 data_pre_processing 的输入给出。
X,Y,timesteps,X_data = Anamoly.data_pre_processing(df=anamoly_df, LOOKBACK_SIZE=10)
通过这个函数,我们还可以对范围[0,1]内的数据进行标准化,然后通过将“时间步长”作为另一个额外的维度来修改数据集。其思想是将二维数据集的维度从[Batch Size, Features]转换为三维数据集[Batch Size, Lookback Size, Features].,更多细节请查看这里的。
6)选择定制用户选择输入配置来训练异常模型
使用 set_config()函数,用户可以从深层网络架构中进行选择,设置时间窗口大小,调整内核大小。可用的模型-深度卷积神经网络,LSTM 自动编码器,可以给出可能的值['deepcnn ',' lstmaenn']。我们选择时间序列窗口大小=10,对于卷积网络使用核大小 3。
MODEL_SELECTED, LOOKBACK_SIZE, KERNEL_SIZE = Anamoly.set_config(MODEL_SELECTED='deepcnn', LOOKBACK_SIZE=10, KERNEL_SIZE=3)
==================MODEL_SELECTED = deepcnn
LOOKBACK_SIZE = 10
KERNEL_SIZE = 3
7)训练所选择的异常检测器模型
根据可用性,可以使用 GPU/CPU 来训练模型。计算功能将使用 GPU(如果可用),否则,它将使用 CPU 资源。 google colab 使用的 NVIDIA TESLA K80 是最受欢迎的 GPU,而 NVIDIA TESLA V100 是第一款张量核心 GPU。训练的次数可以自定义设置。正在使用的设备将在控制台上输出。
Anamoly.compute(X, Y, LOOKBACK_SIZE=10, num_of_numerical_features=26, MODEL_SELECTED=MODEL_SELECTED, KERNEL_SIZE=KERNEL_SIZE, epocs=30)
==================Training Loss: 0.2189370188678473 - Epoch: 1
Training Loss: 0.18122351250783636 - Epoch: 2
Training Loss: 0.09276176958476466 - Epoch: 3
Training Loss: 0.04396845106961693 - Epoch: 4
Training Loss: 0.03315385463795454 - Epoch: 5
Training Loss: 0.027696743746250377 - Epoch: 6
Training Loss: 0.024318942805264566 - Epoch: 7
Training Loss: 0.021794179179027335 - Epoch: 8
Training Loss: 0.019968783528812286 - Epoch: 9
Training Loss: 0.0185430530715746 - Epoch: 10
Training Loss: 0.01731374272046384 - Epoch: 11
Training Loss: 0.016200231966590112 - Epoch: 12
Training Loss: 0.015432962290901867 - Epoch: 13
Training Loss: 0.014561152689542462 - Epoch: 14
Training Loss: 0.013974714691690522 - Epoch: 15
Training Loss: 0.013378228182289321 - Epoch: 16
Training Loss: 0.012861106097943028 - Epoch: 17
Training Loss: 0.012339938251426095 - Epoch: 18
Training Loss: 0.011948177564954476 - Epoch: 19
Training Loss: 0.011574006228333366 - Epoch: 20
Training Loss: 0.011185694509874397 - Epoch: 21
Training Loss: 0.010946418002639517 - Epoch: 22
Training Loss: 0.010724217305010896 - Epoch: 23
Training Loss: 0.010427865211985524 - Epoch: 24
Training Loss: 0.010206768034701313 - Epoch: 25
Training Loss: 0.009942568653453904 - Epoch: 26
Training Loss: 0.009779498535478721 - Epoch: 27
Training Loss: 0.00969111187656911 - Epoch: 28
Training Loss: 0.009527427295318766 - Epoch: 29
Training Loss: 0.009236675929400544 - Epoch: 30
8)发现异常
一旦训练完成,下一步就是发现异常。现在,这将我们带回我们的基本问题,即,我们如何准确地估计&追踪什么是异常?。可以使用异常得分、异常可能性、和一些最近的度量,如 Mahalanobis 基于距离的置信度得分 等。 Mahalanobis 置信度得分假设预训练神经分类器的中间特征遵循类别条件高斯分布,其协方差与所有分布相关,新输入的置信度得分被定义为距最近类别条件分布的 Mahalanobis 距离。异常分数是未被正确预测的活动列的分数。相反,异常可能性是给定异常分数代表真实异常的可能性。在任何数据集中,都会有一个自然的不确定性水平,它会在预测中产生一定数量的“正常”错误。异常可能性解释了这种自然水平的误差。因为,我们没有地面真实异常标签,所以在我们的情况下,我们不能使用这个指标。find_anamoly()用于通过生成假设和计算损失来检测异常,损失是数据集中给定的各个时间戳的异常置信度得分。
loss_df = Anamoly.find_anamoly(loss=loss, T=timesteps)
假设=模型(torch . from _ numpy(x . as type(NP . float 32))。到(设备))。分离()。cpu()。numpy()
loss = np.linalg.norm(假设-Y,轴=1)
return loss . shape(len(loss),1)
9)绘制具有置信度得分的样本:DeepCNN 示例
接下来,我们需要可视化异常,为样本分配每个时间戳记录的异常置信度得分。plot_anamoly_results 函数可用于绘制每个时间戳记录相对于频率(bin)和置信度得分的异常得分。
Anamoly.plot_anamoly_results(loss_df=loss_df)


从上面的图表中,可以预先假定异常置信度得分大于等于 1.2 的时间戳/实例很可能是偏离预期或典型的示例,因此可以被视为潜在异常。
10)异常检测器预测的可解释结果— DeepCNN
最后,为所建立的时间序列预测器设计了可解释人工智能的原型。在我们进行这一步之前,让我们了解什么是可解释的模型。
为什么《可解释的人工智能》( XAI)是当前的热门话题和需求?
数据无处不在,机器学习可以从中挖掘信息。如果机器学习模型生成的结果能够被人类容易地理解、解释和信任,那么表征学习将变得更有价值和非常重要。这就是可解释人工智能的用武之地,从而使事物不再是一个黑匣子。
explainable _ results()使用博弈论方法来解释模型的输出。为了在个体/样本水平上理解、解释和信任深度模型的结果,我们使用了内核解释器。Shapley 值的一个基本属性是,它们总是对所有玩家都在场时的游戏结果和没有玩家在场时的游戏结果之间的差异求和。对于机器学习模型,这意味着所有输入特征的 SHAP 值将总是总和为基线(预期)模型输出与正在解释的预测的当前模型输出之间的差异。explainable _ results 函数接受要解释的特定行/实例/样本预测的输入值。它还需要输入要素的数量(X)和时间序列窗口大小差(Y)。我们可以得到单个实例级别的可解释结果,也可以得到批量数据的结果(比如前 200 行,后 50 个样本,等等。)
Anamoly.explainable_results(X=anamoly_data, Y=Y, specific_prediction_sample_to_explain=10,input_label_index_value=16, num_labels=26)

上图是第 10 个示例/样本/记录/实例的结果。可以看出,对相应的异常置信分数有显著贡献的特征是由于来自温哥华、多伦多、萨斯卡通、温尼伯、卡尔加里气象站的温度读数。
重要链接
- 示例无监督特征选择演示笔记本:https://github . com/ajayarunachalam/msda/blob/main/Demo . ipynb
- 示例无监督异常检测器&可解释 AI 演示笔记本:https://github . com/ajayarunachalam/msda/blob/main/Demo 1 _ v1 . ipynb
接触
你可以在 ajay.arunachalam08@gmail.com 找到我
感谢阅读。一直,不断学习:)
参考
https://www.nature.com/articles/s41598-019-55320-6 https://www.nature.com/articles/s42256-019-0138-9 https://machinelearningmastery.com/lstm-autoencoders/ https://ieeexplore.ieee.org/document/8581424 https://en.wikipedia.org/wiki/Autoencoder https://en.wikipedia.org/wiki/Long_short-term_memory https://enterprisersproject.com/article/2019/5/what-explainable-ai https://www.analyticsvidhya.com/blog/2017/12/fundamentals-of-deep-learning-introduction-to-lstm/ https://machinelearningmastery.com/gentle-introduction-long-short-term-memory-networks-experts/ https://en.wikipedia.org/wiki/Recurrent_neural_network https://en.wikipedia.org/wiki/Time_series https://www.analyticsvidhya.com/blog/2018/09/multivariate-time-series-guide-forecasting-modeling-python-codes/
https://en.wikipedia.org/wiki/Convolutional_neural_network
https://en.wikipedia.org/wiki/Artificial_neural_network https://pytorch.org/
https://machine learning mastery . com/stacked-long-short-term-memory-networks/
https://en.wikipedia.org/wiki/Rectifier_(neural_networks) https://climate.weather.gc.ca/ https://www.kaggle.com/aturner374/eighty-years-of-canadian-climate-data https://en.wikipedia.org/wiki/Graphics_processing_unit https://www.nvidia.com/en-gb/data-center/tesla-v100/ https://www.nvidia.com/en-gb/data-center/tesla-k80/
【https://arxiv.org/abs/2003.00402
<https://www.nature.com/articles/s42256-019-0048-x> <https://www.nature.com/articles/s42256-019-0138-9> <https://en.wikipedia.org/wiki/Game_theory>
https://www.nature.com/articles/s41551-018-0304-0
https://shap.readthedocs.io/en/latest/ https://en.wikipedia.org/wiki/Shapley_value
可解释的人工智能(XAI):莱姆和 SHAP,两个伟大的候选人来帮助你解释你的机器学习模型
如何使用两个代理工具来更好地理解模型的预测。

介绍
作为数据科学家或机器学习实践者,将可解释层集成到您的机器学习模型中可以使它们值得信赖。它可以帮助决策者和其他利益相关者获得更多的可见性,并理解导致模型输出的决策的解释。
在本文中,我将带您了解两个代理模型,莱姆和 SHAP ,帮助您了解您的模型的决策过程。
模型结构
我们将使用来自 Kaggle 的糖尿病数据集。主要的焦点是可解释性,所以我们不会花太多时间去尝试花哨的模型。
模型可解释性
SHAP
它代表沙普利加法解释。该方法旨在通过计算每个特征对预测的贡献来解释实例/观察的预测。
SHAP 有许多模型解释的可视化工具,但是我们将讨论其中几个,每一个都有其特殊性。
- 带汇总图的变量重要性

图 1:显示重要变量的汇总图
观察:以下是我们从之前的剧情中可以推断出来的:
- 它显示了重要特性的列表,从最重要到最不重要(从上到下)。
- 特征分析
- 所有的特征似乎对被诊断为糖尿病(标签=1)或未被诊断为糖尿病(标签= 0)的两个类别都有同等的贡献,因为颜色占据了矩形的 50%。
- 根据该模型,葡萄糖是最具有预测能力的一种。
- 年龄是第二个最有预测力的特征。
- 怀孕是最有预测力的第五个特征。
2。特定标签上的汇总图

图 2:标签= 1 上的深度摘要图
观察:对于分类问题,每个可能的标签都存在 SHAP 值。在我们的例子中,我们决定得到预测值 1(真)。以下是从图中可以推断出的主要特征:
- 对于重要特性 ,同样从变量重要性情节进行分析。
- 每个点代表单个数据实例的特征值。
- 颜色表明该特征是高值(红色)还是低值(蓝色)。
- X 轴显示对预测输出的积极或消极贡献
当我们将这些分析应用于特征时,我们得到以下解释:
- 对于葡萄糖:我们看到大多数高值(红点)对预测输出有正贡献(在 X 轴上为正)。换句话说,如果单个数据实例的葡萄糖量很高,其具有阳性结果(被诊断为糖尿病)的可能性会大大增加,而低量(蓝点)会降低(负 X 轴值)被诊断为糖尿病的可能性。
- 对于年龄:对年龄进行同样的分析。年龄越高,数据实例(患者)最有可能被诊断为糖尿病。另一方面,当涉及到未成年时,该模型似乎很混乱,因为我们可以在垂直线(X 轴=0)的每一侧观察到几乎相同数量的数据点。由于年龄特征对于分析来说似乎是令人困惑的,我们可以使用 依赖关系 情节 来获得更细粒度的信息。
3。依赖图

图 3:年龄特征的依赖 图
观察:从依赖关系图中,我们可以清楚地看到,30 岁以下的患者被诊断为糖尿病的风险较低,而 30 岁以上的患者被诊断为糖尿病的风险较高。
石灰
它代表局部可解释模型不可知解释。 局部 方面意味着它用于解释机器学习模型的个体预测。
设置讲解人
使用两个非常简单的步骤来构建解释器:(1)导入模块,(2)使用训练值、特征和目标来适应解释器。
第 5 行 :我用最容易理解的方式创建了两个标签,而不是 1 和 0。
单实例解释
这里,对来自测试数据的单个实例进行解释

图 3:单个实例的时间解释能力
观察:t 该模型以 73%的置信度预测该特定患者患有糖尿病,并且解释了该预测,因为葡萄糖水平高于 99,血压高于 70。在右边,我们可以观察到真实特征对患者的价值。
文章结尾
恭喜你!你刚刚学会了如何用 SHAP 和莱姆来解释你的机器学习模型。现在,您可以构建可信的模型,帮助决策者和其他利益相关者获得更多的可见性,并理解导致模型输出的决策的解释。本文只介绍了这些工具的一小部分,您可以在下面的附加资源中进一步分析。
在 YouTube 上关注我,了解更多互动会话!
额外资源
再见🏃🏾
可解释的人工智能(XAI)方法第一部分——部分相关图
部分依赖情节的入门,它的优点和缺点,如何利用和解释它

来自像素的照片。归入“免版税”部分的照片。还贴着“免费使用”的标签。
可解释的机器学习(XAI)
可解释的机器学习(XAI)指的是努力确保人工智能程序在目的和工作方式上是透明的。[1]这是最近几年数据科学和人工智能领域最热门的关键词之一。这是可以理解的,因为许多 SOTA(最先进的)模型是黑盒,尽管它们具有顶级的预测能力和性能,但很难解释或说明。对于许多组织和公司来说,分类准确度提高几个百分点可能没有回答诸如“特征 A 如何影响结果?”这样的问题重要这就是为什么 XAI 一直受到更多的关注,因为它大大有助于决策和执行因果推理。
在接下来的一系列文章中,我将介绍目前在数据科学界广泛使用的各种 XAI 方法。我将介绍的第一种方法是部分相关图,简称 PDP。
部分相关图
部分依赖(PD)是一种全局和模型不可知的 XAI 方法。全局方法对整个数据集给出了全面的解释,描述了特征在整个数据环境中对目标变量的影响。另一方面,局部方法描述了特征对观察水平的影响。模型不可知意味着该方法可以应用于任何算法或模型。
简而言之,PDP 显示了单个特征对黑盒模型预测值的边际效应或贡献[2]。对于更正式的定义,回归的部分相关函数可以定义为:

PDP 函数公式来自本书可解释机器学习第 8.1 章
上面的部分函数是通过计算训练数据的平均值来估计的,如下所示:

来自可解释机器学习第 8.1 章的 PDP 的部分函数估计公式
在上面的公式中,S 表示包含感兴趣的特征(即,我们想要了解对目标变量的影响的特征)的集合,C 表示包含不在集合 S 中的所有其他特征的集合
我们对分类变量有什么看法?这种情况更简单,因为我们只需要用分类变量中的每个类别替换所有数据实例,并对预测进行平均。例如,如果您有兴趣查看性别/性别的 PDP,那么您需要将性别/性别变量替换为“男性”类别,并对预测进行平均。计算“女性”类别的部分依赖性也是如此。
总的来说,PDP 很好,因为它以非常直接的方式显示了目标和特性之间的关系。例如,当应用于线性回归模型时,PDP 总是显示线性关系。[2]它还能够捕捉单调或更复杂的关系。
假设、限制和缺点
不幸的是,PDP 不是你在任何场合都可以挥舞的魔杖。它有一个重要的假设。所谓的独立性假设是 PD 图的最大问题。假设为其计算部分相关性的特征与其他特征不相关。
Christoph Molnar 的可解释机器学习书籍提到了这一假设
“如果您计算的 PDP 的特征与其他特征不相关,则 PDP 完美地代表了该特征如何影响平均预测。在不相关的情况下,解释是清楚的:部分相关性图显示了当第 j 个特征改变时,数据集中的平均预测如何改变。
如果这个假设不成立,对情节的解释可能不太可靠。例如,为部分相关性图计算的平均值将包括非常不可能甚至不可能的数据点。[2]
这种方法还有三个额外的限制或问题。
首先,尽管可以计算,但两个以上特征的 PDP 很难绘制和解释。我个人认为,PDP 最多可以使用两个功能,超过这个数目我们就无法理解了。
第二,对于数据很少的区间内的值,PDP 可能不准确。因此,经常检查特征的分布是一种很好的做法,例如,通过直方图可视化它们。
第三,异质效应可能无法在图中捕捉到。[2]例如,如果某个特征根据不同的值区间与目标变量既有积极的关联又有消极的关联,那么这两种反作用力可能会相互抵消,误导用户总体边际效应为零。这会让用户误以为该特性对目标变量影响很小或没有影响。防止这种情况的一种方法是绘制个人条件期望(ICE)曲线(将在下一篇文章中讨论)和 PDP,以揭示异质效应。
履行
我们可以使用多个包和库来绘制 PDP。如果你用的是 R,pdp 有 iml 、 pdp 和 DALEX 等包。对于 Python 来说, PDPBox 包和 sklearn.inspection 模块中的partialdependenciedisplay函数是最好的。
让我们来看看 Kaggle 学习部分的可解释的机器学习教程中的一个例子。[3]它使用了 PDPBox 软件包。
您首先阅读所有必要的库和包。
**import** numpy as np
**import** pandas as pd
**from** sklearn.model_selection **import** train_test_split
**from** sklearn.ensemble **import** RandomForestClassifier
**from** sklearn.tree **import** DecisionTreeClassifier
接下来,我们读入数据,将其分为训练和测试数据,并训练一个决策树分类器。
data = pd.read_csv('../input/fifa-2018-match-statistics/FIFA 2018 Statistics.csv')y = (data[‘Man of the Match’] == “Yes”) # Convert from string “Yes”/”No” to binaryfeature_names = [i for i in data.columns if data[i].dtype in [np.int64]]X = data[feature_names]train_X, val_X, train_y, val_y = train_test_split(X, y, random_state=1)tree_model = DecisionTreeClassifier(random_state=0, max_depth=5, min_samples_split=5).fit(train_X, train_y)
请记住,只有在模型被定型后,才能计算部分相关性。在上述决策树分类器被训练之后,我们读入我们的可视化库(matplotlib)以及用于绘制 PDP 的 pdpbox 包。
**from** matplotlib **import** pyplot as plt**from** pdpbox import pdp, get_dataset, info_plots# Create the data that we will plotpdp_goals = pdp.pdp_isolate(model=tree_model, dataset=val_X, model_features=feature_names, feature=’Goal Scored’)# plotpdp.pdp_plot(pdp_goals, ‘Goal Scored’)plt.show( )

XAI ka ggle 学习部分的 PDP 教程
来自 PDPBox 包的 PDP 图的好处是它还显示了置信区间(即上面图中的浅蓝色阴影)。
如果您想要绘制两个特性的 PDP,您可以使用 pdp_interact 和 pdp_interact_plot 功能。
# Similar to previous PDP plot except we use pdp_interact instead of pdp_isolate and pdp_interact_plot instead of pdp_isolate_plotfeatures_to_plot = [‘Goal Scored’, ‘Distance Covered (Kms)’]inter1 = pdp.pdp_interact(model=tree_model, dataset=val_X, model_features=feature_names, features=features_to_plot)pdp.pdp_interact_plot(pdp_interact_out=inter1, feature_names=features_to_plot, plot_type=’contour’)plt.show( )

2D PDP 从 XAI 教程在 Kaggle 的学习部分
如果有兴趣使用 Sklearn 中的另一个 Python 库(sklearn.inspection),可以参考文档。[4]
感谢您阅读我的帖子!下一个 XAI 方法是冰曲线。
参考文献
[1] 可解释的人工智能(XAI) (2019),Technopedia
[2] C .莫尔纳尔、、可解释机器学习 (2020)
[3] D .贝克尔,部分情节,卡格尔学
[4] 部分依赖,Sklearn 文档
可解释的人工智能(XAI)方法第二部分——个体条件期望(ICE)曲线
关于个人条件期望(ICE)曲线的教程,它的优点和缺点,它与 PDP 有何不同,以及如何使用和解释它

免费使用来自像素的照片。
回顾以前的帖子
可解释的机器学习(XAI)指的是努力确保人工智能程序在目的和工作方式上是透明的。[1]这是我打算写的 XAI 系列文章中的第二篇。
在我之前的帖子中,我介绍了部分相关(PD)和部分相关图(PDP)的概念,这是一种使用 PD 值来显示特征对目标变量的边际平均影响的可视化方法。我建议在继续之前先看一下的帖子!
在这篇文章中,我将讨论什么是个体条件期望(ICE)曲线,它的优点和缺点是什么,以及如何解释和使用它来提高模型的可解释性。
个体条件期望(ICE)曲线/图
在我之前的文章中,我解释过部分依赖(PD)是一种全局和模型不可知的 XAI 方法。回顾并直接引用我之前的帖子:
全局方法给出了对整个数据集的全面解释,描述了在整个数据环境中特征对目标变量的影响。另一方面,局部方法描述特征对观察水平的影响。模型不可知意味着该方法可以应用于任何算法或模型。[3]
ICE 还是一种模型不可知的方法,可以应用于任何模型。事实上,它与 PD 的概念基本相同,但不同之处在于它显示了每个实例的特征的边际效应,而不是像部分相关图(PDP)那样计算整体数据环境中的平均效应。因此,它可以理解为单个数据实例的 PDP 的等价物。从视觉上看,ICE 图分别显示了每个实例的预测对某个特征的依赖关系,结果是每个实例一条线。[2]
不同类型的冰地块
居中的冰剧情
在典型的冰图中,通常很难跨不同的冰线(针对不同的数据实例)进行比较,因为每个实例可能从不同的预测值开始。中心冰图,简称 c-ICE,是解决这个问题的冰图的变体。Christoph Molnar 的可解释机器学习 boo 建议将曲线固定在特征的低端是一个不错的选择。[2]

来自可解释机器学习的典型 ICE 图示例,与下面的 c-ICE 图相当

从可解释的机器学习书中的两个冰图示例(典型的冰图与 c-ICE 图)中可以看出,c-ICE 图在 14 岁时将各个冰线固定为 0。与第一个典型冰图相反,这使得用户更容易在不同的冰线上进行比较。我们可以看到,在 c-ICE 图中,对大多数女性的预测保持不变,直到 45 岁,与 14 岁相比,预测的概率增加。[2]
衍生冰剧情
冰图的这个变体的名字使它不言自明。导数冰图,简称 d-ICE 图,是显示原始冰图的导数值的冰图。这有助于查看是否存在任何交互,因为对于所有数据实例,各自的导数应该是相同的。否则,这将意味着某种相互作用的存在。然而,Christoph Molnar 的可解释机器学习书籍认为,d-ICE 图需要很长时间来计算,因此相当不实用。[2]
优势
与 PDP 类似,ICE 曲线为用户提供了直观的见解。每条线代表单独的实例,因此允许我们观察特性的边际效应如何改变每个单独实例的特性的不同值。
ICE 图的另一个主要优势是,它使我们能够捕捉到 异构关系 ,这在仅查看 PDP 时是不可能做到的。异质关系意味着一个特征对目标变量的影响具有不同的方向性,这取决于特征值的不同区间。
例如,如果我们正在建立一个预测贷款申请批准的模型,部分依赖图可能会告诉我们,年龄的边际平均效应对贷款申请批准是积极的(即,如果你越老,你的贷款申请越有可能被批准)。然而,冰图可能表明存在贷款申请批准概率低的个人,无论其年龄如何(即冰线是平的,斜率接近或等于 0)。从贷款机构的角度来看,这些个人可能是其他条件(即收入)不足以获得贷款批准的申请人。如本例所示,PDP 中的“平均”效应可能会掩盖更多因人而异的局部效应。
不足之处
记得我之前的帖子中提到,PDP 的主要假设是感兴趣的特性不应该与其他特性相关联。否则,图中的一些值将没有意义或变得无效。冰原也有同样的问题。
冰情节的另一个问题是,如果太多的冰线在一张画布上,情节可能难以消化。解决这个问题的方法是省略一些冰线,只画其中的一部分。你可以使用不同的标准或条件来决定保留哪些冰线,放弃哪些冰线。例如,在来自 H2O.ai 团队的本教程笔记本中,它为特征值的每个十分位数绘制了冰线,以防止线的混乱。我强烈推荐查看那本笔记本,因为它也指导你如何在不使用任何开源库的情况下从头实现 PDP 和 ICE 图。这将帮助你更好地掌握 PDP 和 ICE 情节背后的理论以及它们实际上是如何工作的!
履行
我们可以使用多个包和库来绘制 ICE 图。如果你用的是 R,有包包括 iml 、 ICEbox 、 pdp 、 condvis 。对于 Python 来说, sklearn.inspection 模块中的partialdependenciedisplay函数、 PyCEBox 包和 H2O 包的 ice_plot 函数可用。
让我们来看看 Sklearn 文档中的一个例子。[4]
您首先阅读所有必要的库和包。
**import** numpy as np
**import** pandas as pd
**from** **sklearn.datasets** **import** make_hastie_10_2
**from** **sklearn.ensemble** **import** GradientBoostingClassifier
**from** **sklearn.inspection** **import** PartialDependenceDisplay
接下来,我们读入作为 Sklearn 数据集一部分的可用数据。
# Read in data as part of the Sklearn datasets offered
X, y = make_hastie_10_2(random_state=0) # set a seed with random_stateclf = GradientBoostingClassifier(n_estimators=100, learning_rate=1.0, max_depth=1, random_state=0).fit(X, y)features = [0, 1]PartialDependenceDisplay.from_estimator(clf, X, features, kind='**individual**')
与 PDP 类似,您只能在模型经过训练后绘制 ICE 曲线。
还有,注意参数“种类”。你会记得 PartialDependenceDisplay 模块是我们在我之前的帖子中用来计算部分依赖(PD)的同一个模块。这里唯一的区别是我们在计算 PD 时没有指定的新参数“kind”。我们还可以将参数指定为等于“both ”,这将让我们同时在一个画布上绘制 PDP 和 ICE 曲线。这将是有意义的,因为我们可以同时观察边际平均效应和边际个体效应!
PartialDependenceDisplay.from_estimator(clf, X, features, kind='**both**')
其他一些可以用来在 Python 中绘制 ICE 曲线的包有 PyCEBox 和著名的 AutoML 和大数据包 H2O (由 H2O.ai 团队制作)。[6, 7]
感谢您阅读我的帖子!下一个 XAI 方法是 ALE 图。敬请期待!
如果你感兴趣,请在各个页面上关注我!所有相关链接都汇集在这个页面!
参考
[1] 可解释的人工智能(XAI) (2019),Technopedia
[2] C .莫尔纳尔,,可解释机器学习 (2020)
[3] S. Kim,《可解释的人工智能(XAI)方法第一部分——部分相关图(PDP) (2021),走向数据科学
[4] 部分依赖,Sklearn 文档
[5] P. Hall,H2O.ai 团队,可解释 _ 机器 _ 学习 _ 用 _ python>xgboost _ PDP _ ice . ipynb,Jupyter 笔记本
[6] PyCEBox 教程,PyCEBox
[7] H2O 机器学习可解释性,H2O.ai
用决策树解释人工智能(XAI)
决策树可视化 XAI 分析实用指南

人工智能,学习模型随着时间的推移变得越来越复杂,很难直观地分析它们。人们经常听说机器学习模型是“黑匣子”,从某种意义上说,它们可以做出很好的预测,但我们无法理解这些预测背后的逻辑。这种说法是正确的,因为大多数数据科学家发现很难从模型中提取洞察力。然而,我们可以使用一些工具从复杂的机器学习模型中提取洞察力。
本指南是关于如何使用和解释 sklearn.tree.plot_tree 模型可解释性的实践指导。决策树本身是一种可解释的机器学习算法,广泛用于线性和非线性模型的特征重要性(在本文的全局解释部分中解释)。这是一个相对简单的模型,很容易通过可视化树来解释。
import numpy as np
from sklearn import tree
from sklearn.tree import DecisionTreeClassifier
import matplotlib.pylab as plt
from sklearn import datasets, ensemble, model_selection
from sklearn.ensemble import RandomForestClassifier
在这个例子中,我们将使用来自 sklearn 数据集的乳腺癌例子。这是一个简单的二元(恶性、良性)分类问题【1】。从乳腺肿块的细针抽吸(FNA)的数字化图像中计算特征。它们描述了图像中出现的细胞核的特征。
# import data and split
cancer = datasets.load_breast_cancer()
X_train, X_test, y_train, y_test = model_selection.train_test_split(cancer.data, cancer.target, random_state=0)
将数据集拆分为定型和测试数据集后,使用 tree。DecisionTreeClassifier()来构建分类模型。
# model and fit
cls_t = tree.DecisionTreeClassifier()
cls_t.fit(X_train, y_train);
现在,为了对模型有一个基本的印象,我推荐可视化特性的重要性。特征重要性被计算为通过到达该节点【2】的概率加权的节点杂质的减少。节点概率可以通过到达节点的样本数除以样本总数来计算。值越高,特性越重要。最重要的功能在树中的位置会更高。单个特征可以用于树的不同分支,那么特征重要性就是它在减少杂质方面的总贡献【3】。
importances = cls_t.feature_importances_
indices = np.argsort(importances)
features = cancer.feature_names
plt.title('Feature Importances')
j = 11# top j importance
plt.barh(range(j), importances[indices][len(indices)-j:], color='g', align='center')
plt.yticks(range(j), [features[i] for i in indices[len(indices)-j:]])
plt.xlabel('Relative Importance')
plt.show()

作者图片
cls_t.feature_importances_
在这种情况下,只使用了前 13 个特性。其他功能未被使用。他们的重要性为零。

让我们设想决策树的前三层,max_depth=3。
# visualization
fig = plt.figure(figsize=(16, 8))
vis = tree.plot_tree(cls_t, feature_names = cancer.feature_names, class_names = ['Benign', 'Malignant'], max_depth=3, fontsize=9, proportion=True, filled=True, rounded=True)

作者使用 max_depth=3 图像的决策树可视化
关于这个模型,我们能了解到什么?
首先,我们可以看到每个决策层所使用的特性的名称和条件的拆分值。如果一个样本满足条件,那么它去左边的分支,否则,它去右边。
每个节点中的 samples 行显示了我们在当前节点中检查的样本数。如果 proportion=True,则样本行中的数字以总数据集的%为单位。
每个节点中的值线告诉我们该节点有多少样本属于每个类,当比例=假时,按顺序排列,当比例=真时,按比例排列。这就是为什么在每个节点中,value 中的数字加起来等于 value 中显示的数字(比例=假,比例=真)。

作者使用 max_depth=8 图像的决策树可视化
在类行中,我们可以看到节点的分类结果。
基尼系数是一个量化节点纯度的指标,类似于熵。大于零的基尼系数意味着包含在该节点内的样本属于不同的类别。在上图中,树叶的基尼系数为零,这意味着每片树叶中的样本属于一个类别。注意,当纯度高时,节点/叶具有较深的颜色。
决策树代理模型
解释“黑盒”模型的全局行为的一种流行方法是应用全局代理模型。全局代理模型是一种可解释的模型,它被训练成近似黑盒模型的预测。我们可以通过解释代理模型得出关于黑箱模型的结论。用更多的机器学习来解决机器学习的可解释性!【4】
训练代理模型是一种模型不可知的方法,因为它不需要关于黑盒模型内部工作的任何信息,只需要访问数据和预测函数。这个想法是,我们采取我们的“黑箱”模型,并使用它创建预测。然后,我们根据“黑盒”模型和原始特征产生的预测训练一个透明模型。请注意,我们需要跟踪代理模型在多大程度上接近“黑盒”模型,但这通常不容易确定。
随机森林分类器是一种常用的模型,它解决了决策树模型容易出现的过拟合问题。因此,在测试集上具有更好的准确性,但它是
clf = RandomForestClassifier(random_state=42, n_estimators=50, n_jobs=-1)
clf.fit(X_train, y_train);
使用您的模型(在本例中为 RandomForestClassifier)创建预测
predictions = clf.predict(X_train)
然后使用预测来使数据适合决策树分类器。
cls_t = tree.DecisionTreeClassifier()
cls_t.fit(X_train, predictions);
想象一下。
# visualization
fig = plt.figure(figsize=(16, 8))
vis = tree.plot_tree(cls_t, feature_names = cancer.feature_names, class_names = ['Benign', 'Malignant'], max_depth=3, fontsize=9, proportion=True, filled=True, rounded=True)

作者图片
就是这样!即使我们不容易理解森林中数百棵树的样子,我们也可以建立一个浅显的决策树来代替,并有希望了解森林是如何运作的。
最后,测量代理模型复制黑盒模型预测的能力。衡量代理复制黑盒模型的好坏的一种方法是 R 平方度量。
cls_t.score(X_train, predictions)
提示
如果您正在使用 pycharm 创建模型,您可以使用 pickle 将其导出到 jupyter notebook。在出口方面:
import pickle
# dump information to that file
with open('model','wb') as outfile:
pickle.dump(cls_t, outfile)
在进口方面:
import pickle
# load information from that file
with open('model','rb') as inputfile:
modell = pickle.load(inputfile)
摘要
解释“黑盒”机器学习模型对于它们成功应用于许多现实世界问题非常重要。sklearn.tree.plot_tree 是一个可视化工具,可以帮助我们理解模型。或者换句话说,机器(模型)从特征中学到了什么?它符合我们的期望吗?我们能通过使用关于问题的领域知识添加更复杂的功能来帮助机器学习吗?使用决策树可视化可以帮助我们直观地评估模型的正确性,甚至可能改进它。本帖中的代码可以在这里找到。
可解释的人工智能(XAI)与 SHAP-多类分类问题
多类分类问题的 SHAP XAI 分析实用指南

模型可解释性成为机器学习管道的基本部分。将机器学习模型作为“黑盒”不再是一个选项。幸运的是,有一些分析工具,如 lime、ExplainerDashboard、Shapash、Dalex 等等,正在迅速发展,变得越来越流行。在之前的帖子中,我们解释了如何使用 SHAP 解决回归问题。本指南提供了一个实际示例,说明如何使用和解释开源 python 包 SHAP 来进行多类分类问题中的 XAI 分析,并使用它来改进模型。
Lundberg 和 Lee (2016)的 SHAP(Shapley Additive explaints)是一种解释个体预测的方法,基于博弈理论上的最优 Shapley 值【1】。计算 Shapley 值以获得特征贡献在计算上是昂贵的。有两种方法可以近似 SHAP 值以提高计算效率:KernelSHAP、TreeSHAP(仅适用于基于树的模型)。
SHAP 提供基于 Shapley 值集合的全球和局部解释方法。在本指南中,我们将使用 Kaggle 数据集【2】中的互联网防火墙数据集示例,展示多类分类问题的一些 SHAP 输出图。
# load the csv file as a data frame
df = pd.read_csv('log2.csv')
y = df.Action.copy()
X = df.drop('Action',axis=1)
像往常一样创建模型并进行拟合。
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y, random_state=0)cls = RandomForestClassifier(max_depth=2, random_state=0)
cls.fit(X_train, y_train)
现在,为了对模型有一个基本的印象,我推荐查看特性重要性和混淆矩阵。为了理解我们在特征重要性方面所处的位置,我使用了 scikit-learn 来计算每棵树内的杂质减少量【3】。
֫importances = cls.feature_importances_
indices = np.argsort(importances)
features = df.columns
plt.title('Feature Importances')
plt.barh(range(len(indices)), importances[indices], color='g', align='center')
plt.yticks(range(len(indices)), [features[i] for i in indices])
plt.xlabel('Relative Importance')
plt.show()

作者图片
稍后,我们可以将这些结果与通过 Shapley 值计算的特征重要性进行比较。
混淆矩阵
混淆矩阵是一种可视化模型性能的方法。更重要的是,我们可以很容易地看到模型失败的确切位置。
class_names = ['drop', 'allow', 'deny', 'reset-both']
disp = plot_confusion_matrix(cls, X_test, y_test, display_labels=class_names, cmap=plt.cm.Blues, xticks_rotation='vertical')

作者图片
模型无法从类 reset-both 中检测到任何实例。原因是一个不平衡的数据集,提供了很少数量的重置示例,这两个类都可以从中学习。
y.value_counts()
allow 37640
deny 14987
drop 12851
reset-both 54
Name: Action, dtype: int64
SHAP 汇总图
模型输出的 SHAP 值解释了要素如何影响模型的输出。
# compute SHAP values
explainer = shap.TreeExplainer(cls)
shap_values = explainer.shap_values(X)
现在我们可以绘制相关的图来帮助我们分析模型。
shap.summary_plot(shap_values, X.values, plot_type="bar", class_names= class_names, feature_names = X.columns)
在此图中,叠加了要素对类的影响,以创建要素重要性图。因此,如果您创建要素是为了将某个特定的类与其他类区分开来,那么这就是您可以看到它的图。换句话说,多类分类的摘要图可以向您显示机器从特征中学习到了什么。
在下面的例子中,我们可以看到类 drop 很少使用 pkts_sent、Source Port 和 Bytes Sent 特性。我们还可以看到,类 allow 和 deny 同等地使用相同的特性。这就是它们之间的混淆程度相对较高的原因。为了更好地区分允许类和拒绝类,需要生成专门用于这些类的新功能。

还可以看到特定类的 summary_plot。
shap.summary_plot(shap_values[1], X.values, feature_names = X.columns)

作者图片
摘要图结合了特征重要性和特征效果。摘要图上的每个点都是一个特征和一个实例的 Shapley 值。y 轴上的位置由特征决定,x 轴上的位置由 Shapley 值决定。可以看到,最不重要的特性 pkts_sent 具有较低的 Shapley 值。颜色代表从低到高的特性值。重叠点在 y 轴方向上抖动,因此我们可以了解每个要素的 Shapley 值的分布情况。这些功能根据其重要性进行排序。
在汇总图中,我们看到了特性值和对预测的影响之间关系的初步迹象。但是为了看到这种关系的确切形式,我们必须看看 SHAP 依赖图。
SHAP 依赖图
部分依赖图(短 PDP 或 PD 图)显示了一个或两个特征对机器学习模型的预测结果的边际效应(j . h . Friedman 2001【3】)。部分相关性图可以显示目标和特征之间的关系是线性的、单调的还是更复杂的。
部分相关图是一种全局方法:该方法考虑所有实例,并给出一个关于特征与预测结果的全局关系的陈述。PDP 假设第一特征与第二特征不相关。如果违反了这一假设,为部分相关图计算的平均值将包括非常不可能甚至不可能的数据点。
依赖图是一种散点图,显示单个功能对模型所做预测的影响。在这个例子中,当每个住所的平均房间数高于 6 时,房产价值显著增加。
- 每个点都是数据集中的一个预测(行)。
- x 轴是数据集中的实际值。
- y 轴是该要素的 SHAP 值,表示知道该要素的值会在多大程度上改变该样本预测的模型输出。
该颜色对应于可能与我们正在绘制的特征有交互作用的第二个特征(默认情况下,该第二个特征被自动选择)。如果另一个特征和我们正在绘制的特征之间存在交互作用,它将显示为明显的垂直着色图案。
֫# If we pass a numpy array instead of a data frame then we
# need pass the feature names in separately
shap.dependence_plot(0, shap_values[0], X.values, feature_names=X.columns)

作者图片
在上面的示例中,我们可以看到功能(源端口和 NAT 源端口)之间交互的清晰垂直彩色图案。
SHAP 力图
力图给了我们单一模型预测的可解释性。在该图中,我们可以看到特征如何影响特定观察的模型预测。用于错误分析或对特定案例的深入理解非常方便。
i=8
shap.force_plot(explainer.expected_value[0], shap_values[0][i], X.values[i], feature_names = X.columns)

从这个情节我们可以看出:
- 模型预测 _proba 值:0.79
- 基本值:这是在我们不知道当前实例的任何特性的情况下预测的值。基本值是模型输出在训练数据集上的平均值(代码中的 explainer.expected_value)。在本例中,基值= 0.5749
- 绘图箭头上的数字是该实例的特征值。运行时间(秒)=5,数据包= 1
- 红色表示将模型得分推高的要素,蓝色表示将得分推低的要素。
- 箭头越大,要素对输出的影响越大。影响的减少或增加量可以在 x 轴上看到。
- 5 秒的运行时间增加了该类的属性 allow。包 1,减少属性值。
SHAP 瀑布图
瀑布图是单实例预测的另一个局部分析图。让我们以 8 号实例为例:
row = 8
shap.waterfall_plot(shap.Explanation(values=shap_values[0][row],
base_values=explainer.expected_value[0], data=X_test.iloc[row],
feature_names=X_test.columns.tolist()))

作者图片
- f(x) 为模型 predict_proba 值:0.79。
- E[f(x)]是基值= 0.5749。
- 左侧是特征值,箭头表示特征对预测的贡献。
- 每一行显示了每个特征的正(红色)或负(蓝色)贡献如何将值从背景数据集上的预期模型输出移动到本次预测的模型输出【2】。
摘要
SHAP 框架已被证明是机器学习模型解释领域的一个重要进步。SHAP 结合了几种现有的方法,创造了一种直观的,理论上合理的方法来解释任何模型的预测。SHAP 值量化了特征对预测【6】的影响的大小和方向(正或负)。我相信 XAI 分析与 SHAP 和其他工具应该是机器学习管道的一个组成部分。本帖的代码可以在这里找到。
可解释人工智能(XAI)与 SHAP 回归问题
回归问题的 SHAP XAI 分析实用指南

模型可解释性成为机器学习管道的基本部分。将机器学习模型作为“黑盒”不再是一个选项。幸运的是,有一些工具正在快速发展,变得越来越流行。本指南是 XAI 分析 SHAP 开源 Python 包回归问题的实用指南。
Lundberg 和 Lee ( 2016 )的 SHAP(Shapley Additive explaints)是一种解释个体预测的方法,基于博弈理论上的最优 Shapley 值。Shapley 值是合作博弈理论中广泛使用的一种方法,它具有令人满意的性质。数据实例的特征值充当联盟中的参与者。Shapley 值是一个特性值在所有可能的联合[ 1 ]中的平均边际贡献。
在本指南中,我们将使用来自 sklearn 数据集的波士顿房价数据集示例。是简单的回归问题【2】。
boston = datasets.load_boston()
X_train, X_test, y_train, y_test = model_selection.train_test_split(boston.data, boston.target, random_state=0)
将数据集拆分为定型和测试数据集后,创建模型并进行拟合。
regressor = ensemble.RandomForestRegressor()
regressor.fit(X_train, y_train);
计算 Shapley 值
使用 SHAP 软件包,计算非常简单明了。我们只需要模型(回归量)和数据集(X_train)。
# Create object that can calculate shap values
explainer = shap.TreeExplainer(regressor)
# Calculate Shap values
shap_values = explainer.shap_values(X_train)
计算完 SHAP 值后,我们可以绘制一些分析图,帮助我们理解这个模型。
SHAP 特征重要性
作为第一步,我建议查看特性的重要性。这是对模型的基本理解。在下图中,您可以看到通过 SHAP 值计算的特征重要性(具有大绝对沙普利值的特征是重要的)和通过每棵树内杂质减少累积的平均值和标准偏差计算的特征重要性之间的比较(使用 scikit-learn【3】)。正如你所看到的,它们看起来非常相似,但它们并不相同。
shap.summary_plot(shap_values, X_train, feature_names=features, plot_type="bar")

在左侧要素中,重要性由 SHAP 值计算得出。在右侧特征上,重要性通过使用 scikit-learn 模型来计算。作者图片
SHAP 汇总图
摘要图结合了特征重要性和特征效果。摘要图上的每个点都是每个特性的一个实例的 Shapley 值。y 轴上的位置由特征决定,x 轴上的位置由每个实例的 Shapley 值决定。可以看到,LSTAT 是最重要的特征,具有较高的 Shapley 值范围。颜色代表从低到高的特性值。重叠点在 y 轴方向上抖动,因此我们可以了解每个要素的 Shapley 值的分布情况。这些功能根据其重要性进行排序。
shap.summary_plot(shap_values, X_train, feature_names=features)

作者图片
在汇总图中,我们看到了特性值和对预测的影响之间关系的初步迹象。但是为了看到这种关系的确切形式,我们必须看看 SHAP 依赖图。
SHAP 依赖图
部分依赖图(PDP 或 PD 图)显示了一个或两个特征对机器学习模型的预测结果的边际影响(j . h . Friedman 2001【4】)。部分相关性图可以显示目标和特征之间的关系是线性的、单调的还是更复杂的。部分相关图是一种全局方法:该方法考虑所有实例,并给出一个关于特征与预测结果的全局关系的陈述。PDP 假设第一特征与第二特征不相关。如果违反了这一假设,为部分相关图计算的平均值将包括非常不可能甚至不可能的数据点。
相关性图是一种散点图,显示单个功能对模型所做预测的影响。在这个例子中,当每个住所的平均房间数高于 6 时,房产价值显著增加。
- 每个点都是数据集中的一个预测(行)。
- x 轴是数据集中的实际值。
- y 轴是该要素的 SHAP 值,表示知道该要素值后该预测的模型输出会发生多大的变化。
该颜色对应于可能与我们正在绘制的特征有交互作用的第二个特征(默认情况下,该第二个特征被自动选择)。如果另一个特征和我们正在绘制的特征之间存在交互作用,它将显示为明显的垂直着色图案。
shap.dependence_plot(5, shap_values, X_train, feature_names=features)

作者图片
在上面的例子中,我们可以看到每个住宅的平均房间数高于 7.5,CRIM 总是很低。这些病例的 Shapley 值很高,大大提高了结果。这种洞察力是 RM、卷曲特征之间的依赖性相互作用的函数。
SHAP 力图
这个图给了我们一个单一模型预测的解释能力。力图可用于误差分析,找到对具体实例预测的解释。
i = 18
shap.force_plot(explainer.expected_value, shap_values[i], X_test[i], feature_names = features)

作者图片
从这个情节我们可以看出:
- 模型输出值:16.83
- 基本值:这是在我们不知道当前实例的任何特性的情况下预测的值。基本值是模型输出在训练数据集上的平均值(代码中的 explainer.expected_value)。
- 绘图箭头上的数字是该实例的特征值。CRIM,城镇人均犯罪率= 0.06664,RM,平均房间数= 6.546
- 红色表示将模型得分推高的要素,蓝色表示将得分推低的要素。
- 箭头越大,要素对输出的影响越大。影响的减少或增加量可以在 x 轴上看到。
- 0.066 的卷曲增加属性值,6.546 的 RM 减少属性值。
如果我们想更全面地展示先前的预测,我们可以使用力图的变体。在这里,我们可以看到一组垂直(旋转 90°)并排放置的行的预测。在下图中,我们可以看到数据集中的前 5 行。
# visualize the first 5 predictions explanations with a dark red dark blue color map.
shap.force_plot(explainer.expected_value, shap_values[0:5,:], X_test[0:5,:], plot_cmap="DrDb", feature_names=features)

作者图片
SHAP 决策图
决策图显示了与力图基本相同的信息。灰色垂直线是基值,红线表示每个特征是否将输出值移动到比平均预测值更高或更低的值。
这个图可以比前一个图更清晰和直观一点,尤其是当有许多特征要分析的时候。在力图中,当预测因子的数量很高时,信息可能看起来非常浓缩。
shap.decision_plot(explainer.expected_value[0], shap_values[0], feature_names = list(features))

作者图片
决策图的垂直直线标记了模型的基础值。这条彩色的线是预测。特征值打印在预测线旁边,以供参考。从图的底部开始,预测线显示了 SHAP 值如何从基础值累积到图顶部的模型最终得分。决策图是 SHAP 值的文字表示,使它们易于解释。
力图和决策图都有效地解释了前述模型的预测。主要影响的大小和方向很容易识别。

基于 SHAP 值的异常值检测。来源
将决策图堆叠在一起有助于根据 SHAP 值定位异常值。在上图中,您可以看到一个不同数据集的示例,使用 SHAP 决策图进行异常值检测。
摘要
SHAP 框架已被证明是机器学习模型解释领域的一个重要进步。SHAP 结合了几种现有的方法,创造了一种直观的,理论上合理的方法来解释任何模型的预测。SHAP 值量化了特征对预测【6】的影响的大小和方向(正或负)。我相信 XAI 分析与 SHAP 和其他工具应该是机器学习管道的一个组成部分。更多关于 XAI 与 SHAP 的多类分类问题,请参见链接。本帖中的代码可以在这里找到。
可解释的人工智能(XAI)。但是,为谁?
模型可解释性

西班牙加的斯,作者卡洛斯·穆根
这篇博客是 ECMLPKDD 偏见研讨会上“https://arxiv.org/abs/2107.08045对欧洲央行可解释人工智能的迫切需求”的科学披露(见下面的引文)
试图解释预测或建立可解释的机器学习模型是一个迅速发展的热门话题。当在医学、核能、恐怖主义、医疗保健或金融等许多领域的决策过程中应用机器学习时,一个人不能盲目相信预测,让算法“不受约束”,因为这可能会产生灾难性的后果。
模型的预测性能通过广泛的指标组合进行评估,如准确性、召回率、F1、AUC、RMSE、MAPE…但高预测性能可能不是唯一的要求。
在可解释的人工智能和可解释的人工智能领域有一个巨大的趋势,那就是建立对模型预测的信任。许多论文、博客和软件工具以非常明确的方式呈现了可解释性和可解释性,但是...
对谁负责?对谁负责?
我想我们都同意这样一个事实:我们不需要对一个模型的行为做出相同类型的解释。与普通的数据科学家相比,人工智能专家如 Andrew NG、Geoffrey Hinton、Ian good fellow……不需要同样的解释来解释为什么一个模型要做什么以及为什么要做。此外,外行用户可能需要一个完全不同的解释,因为他们可能没有人工智能领域的知识(他们也可能不想)。
所以,人工智能中的解释有不同的受众。
Andrew NG 和网飞推荐系统的一个随机选择的用户有不同的理解需求"这部电视剧为什么被推荐?“一个随机的用户可能会发现得到某部动作电影的建议就足够了,因为他们在过去两周的每晚都在看动作电影,但是然后 Andrew NG(用任何其他著名的人工智能人物替换名字)可能需要不同类型的解释。
机器学习的讲解是针对特定受众做的。当前的 xAI 趋势似乎是为拥有良好机器学习知识的数据科学家建立解释… 但是 xAI 应该专注于向数据科学家或普通用户解释 AI 吗?
软件开发中的一个典型错误是,开发人员最终为自己开发软件,结果却是为他们的目标受众设计的很差。
可解释的人工智能可能会面临与糟糕的软件开发类似的命运。人工智能的解释可能是为数据科学家和机器学习研究人员自己开发的,而不是为日常用户开发的。
使用案例:石灰
一种被证明有用的技术是使用局部代理模型。可能最著名的是局部可解释的模型不可知解释(LIME)。想法相当简单:目标是理解机器学习模型为什么做出个体预测;这叫个别解释。LIME 测试当你将数据的变化输入机器学习模型时,预测会发生什么[5]。
在下图中,我们可以看到著名的葡萄酒质量数据集的某个实例的局部近似值。无论原始模型是什么(模型不可知),我们都可以做一个局部近似,看看哪些特性有贡献,贡献多少。

葡萄酒质量数据集实例的本地解释
有了这个局部近似,我们就有了一个线性模型,它只适用于这个数据实例。我们扰动了输入数据空间,定义了一个局部近似,并在那里拟合了一个模型。
我们能向谁解释这个?
有人会说这种解释对他们来说很直观,因为它是作为一个线性模型估计器构建的,几乎没有什么特性。每个特征的贡献是线性的。有人甚至会说,这种解释对一个在这个领域有一点知识,但没有 ML* 经验的人来说是有好处的。此外,这对于没有时间并且由于某种原因快速浏览相同解释样本的数据科学家来说也很有用,因此对于没有时间的数据科学家来说。*
但是,我们到底在做什么呢?我们是在使用黑盒解释器来预测黑盒算法吗?只有一小部分数据科学家能够真正理解随机森林或梯度增强决策树的行为,但真正知道 LIME 在随机森林中具体做什么的数据科学家就更少了。石灰局部解释图中表示的是什么?这个解释有多稳定?它的可争议性呢?
尽管对可解释人工智能的兴趣正在快速增长,但一些问题仍然存在。我们在为谁构建解释?我们的目标是研究人员/数据科学家吗?还是我们针对的是普通观众?
引用
这篇博客是 ECMLPKDD 关于偏见的研讨会上发表的论文(链接)的科学披露。请引用为:
*@inproceedings{MouganDesi,
author = {Carlos Mougan Navarro and
Georgios Kanellos and
Thomas Gottron},
title = {Desiderata for Explainable {AI} in Statistical Production Systems
of the European Central Bank},
booktitle = {Machine Learning and Principles and Practice of Knowledge Discovery
in Databases - International Workshops of {ECML} {PKDD} 2021, Virtual
Event, September 13-17, 2021, Proceedings, Part {I}},
series = {Communications in Computer and Information Science},
volume = {1524},
pages = {575--590},
publisher = {Springer},
year = {2021},
url = {https://doi.org/10.1007/978-3-030-93736-2\_42},
doi = {10.1007/978-3-030-93736-2\_42},
}*
参考文献和文献
3-可解释的人工智能:当心管理精神病院的囚犯,或者:我如何学会停止担忧并热爱社会和行为科学
4-停止解释高风险决策的黑盒机器学习模型,转而使用可解释的模型
“我为什么要相信你?”:解释任何分类器的预测
感谢
欧洲委员会,no bias-h 2020-MSCA-ITN-2019 项目编号 860630。
可解释的深度神经网络
从隐藏层获得定性见解

图片由诺尔·奥托(Pexels)提供
自然是一个无限的球体,它的中心无处不在,它的圆周无处不在。
B.帕
F 还是有些年头, 黑盒机器学习 一直被批评在从数据中提取知识方面的局限性。深度神经网络(DNNs)是最著名的“黑盒”算法之一。深度神经网络(DNNs)是当今使用最广泛和最成功的图像分类和处理技术。但是当涉及到结构化数据(最常见的数据问题)时,关于它的优势仍然存在争议。近年来,人们一直在努力开发 可解释的机器学习 技术,目标是为算法提供更强有力的描述方法以及向用户提供附加信息,从而增强数据洞察力。在这项工作中,我们提出了一种新的方法,使深度神经网络在将编码数据转换为实际知识时更容易理解。在每个隐藏层表示中,将检查数据结构,以清楚地了解深度神经网络如何为分类目的转换数据。
深度学习的困惑
深度学习数学分析领域(Berner,J. et al. 2021)正在尝试使用数学方法来理解神经网络的神秘内部工作方式。这项研究的关键目标之一是理解神经网络在处理数据时实际上做了什么。深度神经网络(DNNs)在每一层转换数据,创建新的表示作为输出。在分类问题中,DNNs 寻求将数据分成不同的类别,逐层改进这一过程,直到产生最终输出。根据流形假设(manifold hypothesis),该假设提出自然数据在其嵌入空间中创建更低维的流形,该任务可以理解为在数据空间中分离更低维的流形(Fefferman C .,2016;奥拉赫 c .,2014)。

图一。从瑞士卷上随机生成的点,到使用 k-最近邻和 MDS 算法的低维嵌入。虽然初始数据分布是三维的,但最终的表示非常接近于一条连续的线(一维流形)。数据来自https://chart-studio.plotly.com/~empet#/。图片作者。
DNN 层由一个实现函数、φ(一个仿射变换)和一个组件式的激活函数、 ρ 链接。考虑图 2 所示的全连接前馈神经网络。网络架构可以通过定义层数 N,L ,神经元数量,激活函数来描述。网络参数是权重矩阵 W 和偏置向量 b。每一层的输出都是描述输入的新方式。这就是为什么它们被称为 r 表示的原因,因为它们本质上是输入数据的抽象。对于每层,φ(x,θ)= Wx+b ,参数 θ = (W,b) 。权重矩阵是 W ,而偏置向量是 b 。

图二。具有架构的全连接前馈神经网络,a = ((3,4,3,1), ρ)。图片作者。
激活函数在决定神经网络如何连接以及哪些信息从一层传递到下一层方面至关重要。最后,它控制信息交换,允许神经网络从数据中“学习”。他们学什么和为什么学仍然是一个困难的话题。一些研究人员甚至认为,DNN 可以从数据中学习一些特别的东西,而且这项研究是由几个层次共享的。因此,一个层学习了一些东西,然后将一个表示传递给下一个层,下一个层学习其他东西的说法是部分正确的。
数据拓扑
通过降维可视化高维数据表示是一种众所周知的用于检查深度学习模型的技术。在图 3 中,我们可以看到一个具有 a= ((33,500,250,50,1), ρ)架构的网络的层表示。

图 3 。使用 DNN 可视化二元分类问题中的高维表示。具有 33 列和 91 行的原始数据集。网络架构,a = ((33,500,250,50,1), ρ)。用 UMAP 算法(均匀流形近似和投影)完成的表示的维数减少。不同的层试图在聚类中分离低维流形,以便将标签(黄色点)从其余数据点推开。深度网络的任务是尽可能地分离流形,以便隔离标签点。图片作者。
除了降维之外,还有其他可视化高维模型的技术。拓扑学分析空间中元素的连接信息,处理定性的几何信息。拓扑数据分析(TDA)使用范畴理论、代数拓扑和其他纯数学方法来实现对数据形式的实际调查[4]。高维数据集极大地限制了我们可视化它们的能力。这就是为什么 TDA 可以帮助我们提高可视化和分析信息的能力。最常观察到的数据拓扑包括连接的零部件、回路、空隙等。
在深度神经网络中分析高维数据表示时,维数灾难是一个主要挑战。在高维空间中,点非常分散,当 DNN 将数据从一个层传输到另一个具有不同维数的层时,点之间的欧氏距离以及点到子集的距离往往会增加。拓扑学是数学的一个分支,研究独立于所选坐标的几何对象的属性,这意味着即使对象被拉伸、弯曲或以其他方式变形,这些属性也不会改变。这使得拓扑对于分析深度神经网络表示特别有用,因为它允许分析数据的底层结构,而不受数据点的规模或方向的影响。

图四。定量拓扑数据分析。图片作者。
拓扑学避免了距离函数的量化值,而是用一个点到底层空间子集的【无限接近】的概念来代替它们【4】。
拓扑数据分析方法遵循一个基本程序:我们遇到一个拓扑空间(一个高维数据集的表示),我们需要找到它的基本组(数据关系为链接、循环或空洞)。但是我们的数据集是一个陌生的空间,很难看到显式的循环和关系。然后,我们寻找另一个空间,它是与我们的[4]同伦等价的 T12,并且它的基本群更容易计算。由于两个空间都是同伦等价的,我们知道我们空间中的基本群与新空间中的基本群是同构的。这是一个无坐标的过程。一级连接信息与数据链路相关,二级连接信息与数据环路相关,三级连接信息与空隙相关(图 4 显示一级为绿色,二级为蓝色。

图 5 。定性拓扑数据分析。映射器算法。图片作者。
拓扑数据分析通过直接可视化为高维数据的定性理解提供了定量方法和工具(Carlsson G .,2009)。一个例子是映射算法(图 5)。定量数据分析将找到表示的基本组,定性数据分析将带来关于数据的其他定性见解。
空间的拓扑特征是它的基本群。在 TDA,这些签名是用来表征数据空间的定量元素。
为了直观地理解什么是数据拓扑,我们邀请您阅读这篇精彩的帖子:即将到来的事物的数学形状来自 Tang Yau Hoong,发表在 Quanta 杂志上。
作为推论,我们可以说拓扑数据分析是关于寻找连接。
理解深网表示的拓扑:一个真实的例子
在这篇文章中,我们提出对深度神经网络(DNNs)的表示使用拓扑数据分析可以帮助我们更好地理解这些网络如何运行以及可以从每一层收集到什么信息。为了证明这一点,我们将使用印度政府提供的数据集作为药物发现黑客马拉松的一部分,该数据集也可以在 Kaggle 上获得。该数据集包括一系列已经过抗 Sars-CoV-2 有效性测试的药物,以及从 PubChem 库中获得的每个分子的其他化学细节。最终数据集包括 100 个测试分子,每个分子有 40 个特征,包括 pIC50 值,pic 50 值是半最大抑制浓度的负对数,用于衡量物质作为抑制剂的功效。这种分析的目的是根据这些分子对 Sars-CoV-2 的有效性开发一种分类方法。该数据集还包括 6 个盲分子,它们没有 pIC50 值,可用于预测任务。
我们使用 Giotto-tda 库来执行 tda 计算。库gtda是用 Python 编写的高性能拓扑机器学习工具箱。在我看来,使用gtda的真正优势在于它与scikit-learn的直接集成以及对表格数据、时间序列、图表或图片的支持。
这两种资产都使得构建机器学习管道变得简单。对于深度神经网络,我们使用 Keras 库。
深度神经网络架构
在我们的实验中,我们使用了具有架构的全连接神经网络, a = ((33,500,250,50,1),ρ) 。这是一个具有三个隐藏层的基本图形。我们用 Keras functional API 建立了网络,以使不同的实验更具可重复性。功能 API 可以处理具有非线性拓扑的模型。
**# clean is the pandas data frame with the data,
# and 'pIC50' is the label feature.**input_dim = len(clean.drop(columns='pIC50').columns)
model = Sequential()**#The Dense function in Keras constructs a fully connected neural network layer, automatically initializing the weights as biases.****#First hidden layer**model.add(Dense(50, activation='relu',
kernel_initializer='random_normal',
kernel_regularizer=regularizers.l2(0.05),
input_dim=input_dim)
)**#Second hidden layer**model.add(Dense(40, activation='relu',
kernel_initializer='random_normal',
kernel_regularizer=regularizers.l2(0.05) ))**#Third hidden layer**model.add(Dense(20, activation='relu',
kernel_initializer='random_normal',
kernel_regularizer=regularizers.l2(0.05) ))**#Output layer** model.add(Dense(1, activation='sigmoid',
kernel_initializer='random_normal',
kernel_regularizer=regularizers.l2(0.05) ))model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])
如前所述,激活函数在将数据从一层映射到另一层时至关重要。我们感兴趣的激活函数有两种:可逆的(具有连续逆的连续函数)和不可逆的。tanh、sigmoid或softplus是一级函数的例子,ReLU是不可逆函数的例子。激活函数可以是可逆的,但是神经网络作为一个整体,即使具有可逆的激活函数,通常也是不可逆的。
在这一点上,它是值得重述的。我们想在深层神经网络中研究表象的拓扑结构。因此,关键问题将是拓扑签名是否从一种表示保留到下一种表示。答案可以在范畴理论中找到:将一个范畴的对象(例如,德 DNN 表示)转换为另一个范畴的对象的构造是函子如果:
- 它可以扩展到态射上的映射
- 同时保留复合和同一性态射(Riehl E .,2017)。
这种结构定义了类别之间的态射,称为函子。此类函子或构造将类别之间的态射定义为类别 C 和 D 之间的 F : C → D 。因此,该任务仅限于确定从一层映射到另一层的数据是否是函子。函子描述了类别的等价性,因此一个类别中的对象可以被翻译成另一个类别中的对象并从另一个类别中重构。
为了检查 DNN 中的功能,首先,我们分析我们的 DNN 架构的准确性和损失(图 6)。

图 6。我们 DNN 建筑的准确性和损失。图片作者。
训练精度为 0.916,训练精度为 0.709。请记住,我们的数据集很小,因此准确性会受到影响。现在,我们计算每个表示的拓扑签名(也就是说,为每个图层表示找到基本组)。我们使用 Vietori-Rips 过滤[4]对gtda库进行了这项工作。结果如图 7 所示。

图 7 。图层表示的过滤。图片作者。
为了理解该图,我们可以考虑以下情况:橙色点表示拓扑空间中“靠近”在一起的数据点,而绿色点表示“环”或点簇。靠近对角线的绿点可能被认为是噪声或不完全形成的环。橙色点还指示集合的大小,并可用作表征空间的定量方法,称为拓扑签名。在图 7 中,我们可以看到从输入层到第三个隐藏层的拓扑签名被保留,有四个不同的环和几个连接点。垂直线上还有一个橘色的点,和其他的分开。在二元分类任务中,我们期望在代表两个类别的垂直线上看到两个不同的点簇,否则该集合可能是不可分类的。
这在输出图层中很明显,我们可以看到一个点明显远离其余的点,这意味着有两个类别,其中一个比另一个小得多(一个只有一个点,另一个有超过 10 个点)。然后,DNN 将这些相连的点分开,直到她得到一个清晰的分离,即两点之间的最大“距离”或最小“接近度”。它对数据集进行了二进制分类,因此拓扑签名似乎是相同的,但在输出函数中,我们要求 DNN 对此进行操作以进行分类。所以表示之间的转换是函子:复合和同一性态射被保留。
我们重复了这个实验,改变了几个超参数。最有趣的结论是,函子性似乎更依赖于网络宽度,而不是激活函数的可逆性。

图 8 。精度和损耗同 a = ((33,50,25,10,1),ρ)
我们实验了一个网络架构 a = ((33,50,25,10,1),ρ)。精度和损耗与更广泛的架构非常相似(图 8)。然后,我们执行了相同的 Vietory-Rips 过滤(图 9)。我们看到拓扑签名与原始数据集大相径庭。在第一个隐藏层中,我们看到较少的循环在第二个隐藏层中完全消失(我们发现基本上是噪声)。从第二层开始,不映射拓扑签名。

图九。层表示的 Vietory-Rips 过滤,a = ((33,50,25,10,1),ρ) 。图片作者。
改写:神经网络即使在更宽的架构下也能够分离连接点,这似乎是违反直觉的,因为它表明 DNN 不需要考虑拓扑签名来进行准确的分离。然而,通过相对简单的操作来实现这一点是可能的。DNN 通过改变点之间的“接近度”来“操纵”数据的拓扑结构,这样就不会保留同态变换。
另一个话题是,在“学习”过程中,可能有必要考虑范畴的等价性。当我们考虑小数据集时,尽管我们获得了相对较高的准确性水平,但我们应该努力做得更好。拓扑数据分析为我们提供了在学习过程中双重测试网络性能的工具。
定性信息
我们已经看到了定量拓扑方法。但是拓扑学提供了非常有趣的定性理解工具。图 5 中介绍的 Mapper 算法是一种非常有趣的“可视化”算法,可以增强降维算法的性能。映射器算法可以自然地被视为一种聚类算法,它转换成具有有向边的图。在图 10 中,我们可以看到将 mapper 算法应用于具有架构 a = ((33,500,250,50,1),ρ) - 宽层 -的网络中的层表示的结果。节点用“pIC50”值的标度来着色(我们的标签特性)。尽管我们已经进行了二元分类(0 表示负 pIC50 值,1 表示正 pIC50 值),我们现在对回归的观点感兴趣,以获得啤酒的洞察力。我们用gtda库实现了 mapper 算法。制图者在选择超参数时的困难过滤、覆盖和聚类数据。
**""" 1\. Define filter function – can be any scikit-learn transformer. It is returning a selection of columns of the data """**filter_func = Eccentricity(metric= 'euclidean')**""" 2\. Define cover """**cover = CubicalCover(n_intervals=20, overlap_frac=0.5)**""" 3\. Choose clustering algorithm – default is DBSCAN """**clusterer = DBSCAN(eps=8,
min_samples=2,
metric='euclidean')**""" 4\. Initialise pipeline """**pipe_mapper = make_mapper_pipeline(filter_func=filter_func,
cover=cover,
clusterer=clusterer,
verbose=False,
n_jobs=-1)**""" 5\. Plot mapper """**plotly_params = {"node_trace": {"marker_colorscale": "RdBu"}}fig = plot_static_mapper_graph(pipe_mapper,
X,
layout='fruchterman_reingold',
color_variable =clean['pIC50'],
node_scale = 20,
plotly_params=plotly_params
)
fig.show(config={'scrollZoom': True})

图 10 。一个 = ((33,500,250,50,1),ρ) 的图层表示映射算法。图片作者。
我们在图 10 中看到了应用于输入、隐藏和输出层的映射器算法。输入层中的映射器显示了一些“循环”、几个不同长度的连通分量和一些非连通分量(孤立节点)。我们还可以在隐藏层 1 中看到循环,但在下面的层中看不到。在输入层,我们看到蓝点(pIC50 的正值)和红点(pIC50 的负值)完全混合在一起。有了这个图表,我们无法想象一个简单的方法来区分蓝点和红点。但输出层显示了深度神经网络是如何实现这种分离的:蓝色和红色的节点非常容易区分,因为蓝色位于连接组件的末端。我们可以很容易地切断联系,我们将有一个二元分类。我们可以看到,我们需要三个隐藏层来执行这种分离。如果我们看第三层,这种分离仍然是不可能的,所以另一个转换是必要的(输出层)。
DNN 通过尝试连接尽可能多的数据点来解决分类问题。输出层通常包括一个主连接分支,以及一个隔离节点和一个双节点连接组件。与网络中的前几层相比,这一层中的点往往更具连通性。
mapper 算法的另一个有趣的特性是,您可以可视化每个节点,提取数据点,并直接从原始数据集转换见解(图 11)。我们看到 mapper 算法将节点转换为数据点,特别是与分子绘图相关的特征。最后但同样重要的是,我们可以将输入数据集中的每个特征以及隐藏层中的每个神经元的图形可视化。

图 11 。Mapper 算法将节点转换为数据,在这种情况下转换为分子图。图片作者。
mapper 的这一功能非常有用,例如,可以监控每个分子在每个层的图形中的位置。
结论
在这项研究中,我们检查了使用神经网络进行图像分类和结构化数据分类。虽然已经有一些关于使用神经网络进行图像分类的研究,但是它们用于结构化数据分类的例子较少。我们已经展示了使用拓扑数据分析作为工具的潜力,以了解 dnn 在完成任务时使用的流程,并提高其性能。此外,我们发现,无论是否保留数据身份,DNNs 都可以达到类似的准确性水平,尽管这只是在小数据集上进行的测试。当从数据中学习时,使用保留拓扑签名作为评估 DNN 准确性的度量是值得探索的。
你可以在这里找到这篇文章中使用的代码。
Berner,j .,Grohs,p .,Kutyniok,g .,& Petersen,P. (2021 年)。深度学习的现代数学。 ArXiv,abs/2105.04026 。
C.奥拉(2014)。神经网络、流形和拓扑。 陈少华博客 。
C.Fefferman、S. Mitter 和 H. Narayanan (2016 年)。检验多重假设。美国杂志。数学。社会主义者 29, 4, 983–1049.
G.卡尔松。(2009)拓扑和数据。公牛。阿米尔。数学。社会主义者 46 , 255–308.
E.瑞尔。(2017)语境中的范畴论。信使多佛出版物。
使用卷积神经网络的可解释缺陷检测:案例研究
在没有任何包围盒标签的情况下训练对象检测模型。这篇文章展示了可解释人工智能的力量。

作者图片
尽管神经网络非常精确,但在需要预测解释能力的领域,如医学、银行、教育等,它并没有得到广泛应用。
在本教程中,我将向您展示如何克服卷积神经网络的这种可解释性限制。的确如此——通过探索、检查、处理和可视化由深度神经网络层产生的特征图。我们将详细介绍这种方法,并讨论如何将它应用到现实世界的任务中——缺陷检测。
我为这个项目创建了一个 Github 库,在那里你可以找到所有的数据准备、模型、训练和评估脚本。
内容 —任务
—训练管道
—推理管道
—评估
—结论
工作
给你一个 400 个图像的数据集,其中包含好的项目(标记为“好”类)和有缺陷的项目(标记为“异常”类)的图像。数据集不平衡,好的图像样本比有缺陷的图像样本多。图片中的物品可以是任何类型和复杂程度的——瓶子、电缆、药丸、瓷砖、皮革、拉链等。下面是数据集的一个示例。

图片 1。子集“药丸”来自 MVTEC 异常检测数据集 。作者图片
你的任务是建立一个模型,将图像分类为“良好”/“异常”类,如果图像被分类为“异常”,则返回缺陷的边界框。尽管这个任务看起来很简单,就像一个典型的对象检测任务,但有一个问题——我们没有边界框的标签。
幸运的是,这个任务是可以解决的。

图片 2。期望模型预测类别“良好”/“异常”并定位“异常”类别的缺陷区域。训练期间不提供边界框,只提供类别标签。作者图片
培训渠道
披露:我不是在分享我真实的商业项目,而是展示如何解释分类模型预测,因此这可能会用于许多领域和任务——不仅是制造业,还有医学。我还应该说,在这里不要期望高精度,因为这是我最喜欢的项目。但是你可以自由地使用我的结果作为你项目的起点,投入更多的时间并达到你需要的精确度:)
数据准备
对于我的所有实验,我都使用了 MVTEC 异常检测数据集(注意,它是在知识共享署名-非商业性-共享 4.0 国际许可下分发的,这意味着它不能用于商业目的)。
该数据集包括不同项目类型的 15 个子集,如瓶子、电缆、药丸、皮革、瓷砖等;每个子集总共有 300-400 幅图像,每幅图像都被标记为“良好”/“异常”。

图片 3。来自 MVTEC 异常检测数据集 :
的样本上排—良好图像,下排—有缺陷项目的图像。 图像来源
作为数据预处理步骤,将图像调整为 224×224 像素,以加快训练速度。大多数子集中的图像尺寸为 1024×1024,但由于缺陷尺寸也很大,我们可以在不牺牲模型精度的情况下,将图像尺寸调整到较低的分辨率。
考虑使用数据增强。一般来说,适当的数据扩充对你的模型总是有益的(顺便说一句,查看我关于数据扩充的帖子了解更多)。
但是让我们假设,当部署到生产中时,我们的模型将“看到”与我们现在拥有的数据集中格式完全相同的数据。因此,如果图像被居中、缩放和旋转(如在胶囊和电缆子集中),我们可能根本不使用任何数据增强,因为测试图像也被期望居中、缩放和旋转。但是,如果不旋转(而只是居中和缩放)mages,如在螺钉和金属螺母子集中,将旋转作为预处理步骤添加到训练管道将有助于模型更好地学习。

图片 4。包含预处理(居中、缩放和旋转)和未预处理(未旋转)图像的子集示例。可视化平均图像有助于理解子集中的图像是否经过预处理。作者图片
将数据分成训练/测试部分。理想情况下,我们希望拥有训练、验证和测试部分——分别用于训练模型、调整超参数和评估模型准确性。但是我们只有 300–400 张图像,所以让我们将 80%的图像放入训练集,20%放入测试集。对于小数据集,我们可以执行 5 重交叉验证,以确保评估结果是稳健的。
当处理不平衡数据集时,训练/测试分割应该以分层的方式执行,因此训练和测试部分将包含两个类别的相同份额——“良好”/“异常”。此外,如果您有缺陷类型的信息(如划痕、裂纹等),最好也根据缺陷类型进行分层分割,这样训练和测试零件将包含相同份额的有划痕/裂纹的项目。
模型
就拿在 ImageNet 上预训练的 VGG16 来说吧,改变它的分类头——用全局平均池和单个密集层代替扁平化和密集层。我将在“推理管道”一节中解释为什么我们需要这些特定的层。
(这种方法我在论文中找到了,学习深度特征进行鉴别定位。在这篇文章中,我将介绍文中描述的所有重要步骤。)
我们将该模型训练为典型的 2 类分类模型。该模型输出一个二维向量,其中包含分类“好”和“异常”的概率(对于一维输出,该方法也应该可行,请随意尝试)。

图片 5。原始 VGG-16 架构与定制架构。作者图片
在训练期间,前 10 个卷积层被冻结,我们只训练分类头和后 3 个卷积层。这是因为我们的数据集太小,无法微调整个模型。损失是交叉熵;优化器是 Adam,学习率为 0.0001。
我试验了不同的 MVTEC 异常检测数据集子集。我用 batch_size=10 对模型进行了最多 10 个时期的训练,并在训练集准确率达到 98%时提前停止。为了处理不平衡数据集,我们可以应用损失加权:对“异常”类图像使用较高的权重,对“良好”类图像使用较低的权重。
推理管道
在推断过程中,我们不仅要将图像分类为“好的”/“异常的”类别,而且要在图像被分类为“异常”的情况下获得缺陷的边界框。
由于这个原因,我们使模型处于推理模式,以输出类别概率以及热图,这些稍后将被处理到边界框中。热图是根据深层要素图创建的。
第一步。【ReLU 激活后,从 con V5–3 层获取所有特征地图。对于单个输入,将有 512 个大小为 14×14 的特征地图(大小为 224×224 的输入图像通过 4 个池层每次缩减采样两次)。

图片 6。con V5–3 层的特征映射(ReLU 激活后)。
共 512 幅特征地图,每幅尺寸为 14×14;只看到其中的一部分。作者图片
第二步。对 con V5–3 层的所有 512 个要素图求和,每个要素图乘以影响“异常”类得分计算的密集层中的权重。仔细看图 7 和图 8,理解这个步骤。

图 7。最后模型层的详细架构(分类标题)。作者图片

图 8。最终的热图计算为 con V5–3 层热图的总和乘以影响“异常”类得分的密集层中的权重。作者图片
为什么会这样?现在你会明白为什么分类头应该有一个全局平均池层和一个密集层。这种架构使得有可能跟踪什么特征图(以及多少)影响最终预测并使其成为“异常”类。
每个特征图(层 con V5–3 的输出;参见图 6)突出显示了输入图像中的一些区域。全局平均池图层将每个要素地图表示为一个数字(我们可以将其视为一维嵌入)。密集层通过将每个嵌入乘以相应的权重来计算分类“好”和“异常”的分数(和概率)。该流程如图 7 所示。
因此,密集图层权重表示每个要素地图对“良好”和“异常”类分数的影响程度(我们只对“异常”类分数感兴趣)。将 con V5–3 图层的特征地图相加,每个地图乘以密集图层的相应权重,这样做很有意义。
有趣的是,使用全局平均池而不是全局最大池对于使模型找到整个对象是至关重要的。下面是原始论文学习深度特征进行鉴别定位所说的:
“我们相信,与鼓励网络仅识别一个区别部分的全局最大汇集相比,全局平均汇集损失鼓励网络识别对象的范围。这是因为,当进行贴图的平均时,可以通过找到对象的所有有区别的部分来最大化值,因为所有低激活会减少特定贴图的输出。另一方面,对于全局最大值池,除了最有区别的图像区域之外,所有图像区域的低分数不会影响分数,因为您只是执行最大值。”

图 9。最终热图的计算方法是将要素图相加,再乘以影响“异常”类得分的密集图层中的权重。我们可以猜测,诸如 139 和 181 的特征图在求和期间具有较大的正权重,特征图 172 具有较大的负权重,而特征图 127 可能具有接近 0 的权重,因此它不会影响最终热图的外观。作者图片
第三步。下一步是对热图进行上采样,以匹配输入图像的大小——224×224。双线性上采样是可以的,就像任何其他上采样方法一样。
回到模型输出。该模型返回“良好”和“异常”类别的概率,以及显示在计算“异常”分数时哪些像素是重要的热图。模型总是返回热图,不管它将图像分类为“好”还是“异常”;当课程“好”的时候,我们只是忽略了热图。

图片 10。推理模式下的模型应输出“异常”类热图。作者图片
热图看起来很好(见图 11),并解释了是哪个区域使模型决定该图像属于“异常”类。我们可以就此打住,或者(如我所承诺的)将热图处理成一个边界框。

图片 11。一些“异常”类图像的热图。作者图片
从热图到包围盒。这里你可能会想到几种方法。我给你看一个最简单的。在大多数情况下,它工作得很好。
1.首先,标准化热图,使所有值都在范围[0,1]内。
2.选择一个阈值。将其应用于热图,以便所有大于阈值的值都转换为 1,小于阈值的值都转换为 0。阈值越大,边界框就越小。我喜欢阈值在[0.7,0.9]范围内时的结果。
3.我们假设,1s 的区域是一个单一的致密区域。然后,通过在高度和宽度维度中查找 argmin 和 argmax,围绕该区域绘制一个边界框。
但是,注意这种方法只能返回一个边界框(根据定义),所以如果图像有多个缺陷区域,它就会失败。

图片 12。如何将热图处理成包围盒?作者图片
估价
让我们在 MVTEC 异常检测数据集的 5 个子集上评估该方法——榛子、皮革、电缆、牙刷和药丸。
对于每个子集,我都训练了一个独立的模型;选择 20%的图像作为测试集——以随机和分层的方式。没有使用数据增强。我在损失函数中应用了类别权重——1 代表“良好”类别,3 代表“异常”,因为在大多数子集中,良好图像比异常图像多 3 倍。该模型最多被训练 10 个时期,如果训练集精度达到 98%,则提前停止。这是我的笔记本,上面有培训脚本。
以下是评测结果。子集的训练集大小为 80–400 幅图像。平衡精度在 81.7%到 95.5%之间。有些子集,比如榛子和皮革,对模特来说比较容易学,而药丸是相对比较难的子集。

图像 13。来自 MVTEC 异常检测数据集 的 5 个子集的评估结果。作者图片
数字就是这样,现在让我们看看预测是什么样的。在大多数情况下,如果类别是“异常”,模型产生正确的类别预测和精确的边界框。然而,存在一些错误:它们或者是不正确的类预测,或者是当类被正确预测为“异常”时错误的边界框位置。

图片 14。榛子子集:测试集上的预测。作者图片

图 15。皮革子集:对测试集的预测。作者图片

图片 16。电缆子集:测试集上的预测。作者图片

图 17。牙刷子集:对测试集的预测。作者图片

图片 18。药丸子集:对测试集的预测。
结论
在这篇文章中,我想告诉你,神经网络并不像有些人认为的那样是黑盒算法,但是当你知道去哪里找时,它是很容易解释的:)这里描述的方法是解释你的模型预测的许多方法之一。
当然,模型不是那么准确,主要是因为它是我的快速宠物项目。但是如果你要做类似的工作,请随意以我的结果为起点,投入更多的时间,获得你需要的精确度。
我正在开源这个项目的代码到这个 Github 库。请随意使用我的结果作为您项目的起点:)
下一步是什么?
如果您想提高这种异常检测模型的准确性,添加数据扩充—是开始的地方。我推荐你阅读我的帖子— 计算机视觉数据增强完全指南。在那里,您将发现如何使用数据扩充来使您的模型受益,而不是有害:)
如果您对案例研究感兴趣,请查看我的教程—2D 手部姿态估计简介:方法讲解。
参考
[1]周、、Aditya Khosla、Agata Lapedriza、Aude Oliva 和 Antonio Torralba:学习用于区别性定位的深度特征;载于:2016 年 IEEE 计算机视觉和模式识别会议论文集。 pdf
[2]保罗·博格曼、基利安·巴茨纳、迈克尔·福瑟、大卫·萨特勒格、卡斯滕·斯特格:MVTec 异常检测数据集:用于无监督异常检测的综合真实世界数据集;发表于:国际计算机视觉杂志,2021 年 1 月。 pdf
[3]保罗·博格曼、迈克尔·福瑟、大卫·萨特勒格、卡斯滕·斯特格:mv tec AD——用于无监督异常检测的综合真实数据集;参加:IEEE 计算机视觉和模式识别大会(CVPR),2019 年 6 月。 pdf
可解释图形神经网络
可解释人工智能的一个进步,以及为什么现有的解释方法很难适应 GNNs
TL;速度三角形定位法(dead reckoning)
- 可解释性是深度学习中的一个大话题,因为它能够实现更可靠和可信的预测。
- 由于图形结构的不规则性,现有的解释方法不容易适用于图形神经网络。
- 快速窥视 5 组 GNN 解释方法。
可解释性增加了可靠性
最近,人工智能中的可解释性备受关注,其主要动机在于深度神经网络等【黑箱】模型产生的更可靠、更可信的预测。在石灰论文【2】中给出的一个很好的例子是,你会信任你的医生,因为他/她有能力根据你的症状解释诊断。类似地,如果预测可以以人类可解释的方式解释或证明,则深度模型生成的预测更可靠和可信。
相关职位
在过去的一个月里,TDS 上有几篇关于可解释人工智能的文章。他们中的一些人提供了动手的例子来帮助开始使用可解释的人工智能。如果你有兴趣学习更多关于可解释人工智能的知识,这里列出了几个帖子。
- 如何使用形状来解释神经网络【3】作者 Gianluca Malato 提供了一个使用形状来解释神经网络的示例,该神经网络被训练来预测给定 33 个特征的糖尿病的概率。然后将解释可视化,以了解每个特征如何影响预测的结果。
- 可解释的人工智能(XAI)——由 Prateek Bhatnagar 撰写的解释模型的 7 个 Python 包的指南 [4]提供了一个概述,并提供了几个解释深度模型的优秀工具包的实践示例,以帮助您入门。
- 可解释的深度神经网络【5】作者 Javier Marin 提出了一种新的方法来可视化深度神经网络中的隐藏层,以使用拓扑数据分析来深入了解数据如何在整个网络中转换。
为什么很难用现有的方法来解释 GNN?
传统的解释方法在卷积神经网络(CNN)上工作得相当好。以下示例显示了输入图像(a)的三个最高预测类别标签的时间解释。我们可以清楚地看到导致相应预测的部分与类别标签相匹配。例如,吉他琴颈对预测“电吉他”(b)的贡献最大。

使用石灰法的示例说明[2]
然而,当涉及到图形神经网络(GNN),事情变得有点棘手。与 CNN 运行的高度规则的网格相反,图形结构的不规则性带来了许多挑战。例如,我们可以很容易地解释 CNN 模型的上述解释,但对于一个图形来说,类似的节点级别的解释不容易可视化和解释。
在接下来的部分中,我们将回顾最近关于 GNNs 中可解释性的综述[1]中提出的每组方法的主要思想。
解释 GNN 的方法概述
基于梯度/特征的方法:使用梯度或隐藏特征作为输入重要性的近似值,通过反向传播解释预测。
基于扰动的方法 : 相对于输入的扰动的输出变化反映了该输入区域的重要性。或者换句话说,需要保留哪些节点/边/特征,以便最终预测不会与原始 GNN 模型偏离太多。

基于扰动的方法的一般管道[1]
代理方法:使用输入节点的邻近区域训练一个更易解释的代理模型。

代理方法的通用管道[1]
分解方法:将预测分解成若干项,每项作为对应输入特征的重要性分数。

分解方法的通用管道[1]
生成方法:根据待解释的 GNN 模型,学习生成达到最优预测得分的图形。
什么是好的 GNN 解释方法?
当谈到方法评估时,有许多事情需要考虑。[1]中的作者建议了几个度量标准来确保以下特性。
- 保真度:可解释的模型应该和被解释的原始 GNN 有一致的预测。
- 稀疏度:只有一小部分节点/边/特征被用来解释。
- 稳定性:输入的微小变化不应过多影响解释。
- 准确性:解释应该准确地恢复地面实况解释(这只对地面实况已知的合成数据集有效)
结论
可解释性是人工智能的一个关键部分,因为它能够实现可靠和可信的预测。然而,将现有的解释方法应用于 GNN 并非易事。我们已经快速浏览了现有 GNN 解释方法中的几个通用方法,以及定义一个好的 GNN 解释方法的一些期望的属性。
参考
[1]袁,于,桂,纪,图神经网络的可解释性:一个分类综述(2020)arXiv
[2] M. T. Ribeiro,S. Singh,C. Guestrin,“我为什么要相信你?”:解释任何分类器的预测(2016), ACM SIGKDD 知识发现和数据挖掘国际会议(KDD)
[3] 如何用形状解释神经网络
[4] 可解释的人工智能(XAI)——用 Python 写的 7 个包的指南来解释你的模型
【5】可解释的深度神经网络
可解释的神经网络:最新进展,第 2 部分
回顾十年(2010–2020),四集系列
我们在哪里?
这个博客聚焦于神经网络可解释性的发展。我们将我们的演讲分为四个博客系列:
- 第一部分讲述了图像像素的可视化梯度对于解释 CNN 的 pre-softmax 类得分的有效性。
- 第 2 部分讲述了一些更先进的/改进的基于梯度的方法,如去卷积、导向反向传播来解释 CNN。
- 第 3 部分讲述了基于梯度的方法的一些缺点,并讨论了替代的公理化方法,如逐层相关性传播、泰勒分解、深度提升。
- 第 4 部分讲述了一些最近的发展,如集成渐变(上接第 3 部分)以及 CNN 架构中最近的创新,如类激活地图,开发这些地图是为了使特征地图更易理解。
基于梯度的方法
从第 1 部分继续,我们讨论为解释神经网络开发的更复杂的基于梯度的技术。
去卷积网络(2014 年)
解释 CNN 学习的特征图的最早努力之一可以追溯到马修·泽勒关于去卷积网络的工作。在他们关于“ 可视化和理解卷积网络的工作中,(ECCV 2014) ”作者描述了一种将中间隐藏层的激活近似投射回输入层的方法。这种投影可以提供对隐藏层从输入图像中捕捉到的细节的洞察。通过投影连续的层(回到输入层),作者表明 CNN 连续学习图像中更复杂的图案,如边缘、简单形状、更复杂的形状、纹理等。作者通过可视化著名的 AlexNet 的隐藏层来展示他们的结果,并根据他们的见解,也能够调整层以实现更好的性能!
下面简单介绍一下投影方法。从期望的层开始,激活信号通过层向下传递(类似于反向传播),通过最大池层、ReLU、权重倍增:
初始化:从您想要向下投影的层开始,初始化与该层激活相同的重建信号。向下反向传播重构的信号。
- MaxPool :当你遇到一个 MaxPooling 层时,从输入被汇集并在向前传递中被传递的地方寻找索引。在反向传递中,将重构的信号值传递给这些索引,将其他位置置零。
- ReLU: 当遇到 ReLU 层时,只有当重构信号为正时才通过,否则归零。
- 权值:当 u 遇到 CNN 层或者任何权值相乘时,将权值的转置乘以重构的信号,并向下传递。

反卷积方程
有几点需要注意:
- 这种方法并不完全反转 CNN。它只投影有利于激活隐藏层的像素。
- 虽然它被称为去卷积,但这种方法实际上并没有去卷积 CNN。去卷积是一个误称,由于历史原因一直存在。它可以/可能更好地称为转置卷积。
- 反卷积中的大多数操作接近于梯度反向传播。我们将在接下来的章节中对此进行更多的讨论。

向前传球 Vs 向后传球,来源:https://arxiv.org/pdf/1311.2901.pdf

从第 4 层投影到输入图像层的各种狗图像的去卷积,来源:https://arxiv.org/pdf/1311.2901.pdf
引导反向传播(2015 年)
鉴于梯度可以用作理解神经网络决策的显著图,反卷积方法与梯度可视化有多大不同?在他们关于显著图的原始论文(来自上一节)中,作者提到了反卷积和梯度反向传播惊人的相似性。Jost Tobias Springenberg、Alexey Dosovitskiy、Thomas Brox、Martin Riedmiller 在他们的工作“ 努力实现简单性:全卷积网络,(ICLR 2015) ”中进一步研究并提出了这一想法。值得一提的是,这篇论文对 CNN 架构进行了一项消融研究,并做出了除可解释性之外的许多其他重要贡献,这超出了本博客的范围。
当我们比较反卷积和梯度反向传播的方程时,我们注意到除了在 ReLU 阶段,所有的方程都是完全相同的。在去卷积中,(类似梯度的)重建信号只有在它为正时才被传递,即我们只传递有助于促进激活的信号。而在常规梯度传播中,只要 ReLU 在正向传递中向上传递输入,梯度就通过 ReLU 向下传递。这两者之间有细微的区别。


反卷积与梯度反向传播方程
作者将这两种冲突的方法结合成一种方法,称为导向反向传播。在这种方法中,作者建议使用梯度反向传播,除了在 ReLU 阶段。在 ReLU 阶段,只有当梯度为正时,我们才反向传播梯度。以下是方程式:

导向反向传播
导向反向传播仅在 ReLU 阶段不同于“香草”梯度反向传播。
以下是作者在 ImageNet 数据集上展示的一些结果。作者注意到,引导反向传播提供的可视化比“普通”梯度反向传播和反卷积更清晰。


样本图像的引导反投影结果,来源:【https://arxiv.org/pdf/1412.6806.pdf

反投影 Vs 反投影 Vs 制导反投影,来源:【https://arxiv.org/pdf/1412.6806.pdf
接下来是什么?
基于梯度的方法虽然容易理解,但有一些重要的缺点。我们将在下一部分讨论这些缺点。我们还讨论了一些公理化的方法来解决这些缺点,如分层相关性传播、深度提升等。我们将在下一部分讨论这些很酷的技术。
要阅读更多关于神经网络可解释性的激动人心的作品,你可以点击这里阅读下一部分: 链接到第 3 部分
可解释的神经网络:最新进展,第 3 部分
回顾十年(2010–2020),四集系列
我们在哪里?
这个博客聚焦于神经网络可解释性的发展。我们将我们的演讲分为四个博客系列:
- 第一部分讲述了图像像素的可视化梯度对于解释 CNN 的 pre-softmax 类得分的有效性。
- 第 2 部分讲述了一些更先进的/改进的基于梯度的方法,如去卷积、导向反向传播来解释 CNN。
- 第 3 部分讲述了基于梯度的方法的一些缺点,并讨论了替代的公理化方法,如逐层相关性传播、泰勒分解、深度提升。
- 第 4 部分讲述了一些最近的发展,如集成渐变(上接第 3 部分)以及 CNN 架构中最近的创新,如类激活地图,开发这些地图是为了使特征地图更易理解。
公理方法
到目前为止,我们讨论了用于理解由神经网络做出的决策的基于梯度的方法。但是,这种方法有一个严重的缺点。由于像 ReLU 和 MaxPooling 这样的单元的存在,通常得分函数对于一些输入像素可以是局部“平坦的”,或者换句话说具有 0°梯度。基于梯度的方法通常对使 ReLU 或 MaxPool 饱和的像素贡献 0。这是反直觉的。为了解决这个问题,我们需要:
- 关于我们所指的可解释性或相关性的一些正式概念(超越普通梯度)。我们希望“相关性”遵循的属性是什么。希望相关性在线性层表现得像普通梯度一样,因为梯度擅长解释线性函数。
- 有哪些候选满足我们的“相关性”公理,这也很容易计算,理想情况下,我们希望在一次回传中计算它们。
泰勒分解和逐层相关性传播(2015)
Sebastian Bach、Alexander Binder、Grégoire Montavon、Frederick Klauschen、Klaus-Robert Müller 和 Wojciech Samek 首先探讨了公理相关性。他们在他们的工作“ 中引入了逐层相关性传播的概念,通过逐层相关性传播对非线性分类器决策进行逐像素解释(PLOS 2015) ”。
作者提出了关联性必须遵循的以下公理:
- 所有像素的相关性总和必须等于模型的类别分数。我们从现在开始称这个公理为“总关联守恒”。这是一个流行的公理,其他作者也遵循。

总关联守恒,来源:https://journals.plos.org/plosone/article?id = 10.1371/journal . pone . 0130140
作者提出了两种不同的方法来将总相关性分配给各个像素。
1。泰勒分解
在该方法中,作者建议选择参考图像 X₀,该参考图像将被解释为“基线图像”,相对于该基线图像来解释图像 x 的像素。希望该基线图像的类别分数尽可能小。
使用基线图像来比较输入图像以突出重要像素是许多公理相关性工作中反复出现的主题。基线图像的一些好例子是:
- 模糊输入图像:适用于彩色图像
- 空白(暗)图像:适用于灰度/黑白图像
给定基线图像 X₀,我们执行类得分函数的泰勒分解以获得各个像素的相关性。

泰勒分解,来源:https://journals.plos.org/plosone/article?id = 10.1371/journal . pone . 0130140
神经网络的泰勒分解的扩展版本是由作者在他们的另一项工作中提出的,称为 深度泰勒分解 。深度泰勒分解形成了下面描述的逐层相关性传播的理论基础。
2。逐层相关性传播
泰勒分解是一种通用方法,适用于任何类得分函数。对于神经网络,我们可以设计一种更简单的方法,称为分层相关性传播。
对于神经网络,作者建议将相关性从输出层向下传递到起作用的神经元。
- 每当相关性从一个神经元向下传递到下一层中的起作用的神经元时,我们遵循起作用的神经元与从中传递相关性的神经元的总相关性守恒。因此,在 LRP,总的相关性保存在每一层。

分层相关性传播中的相关性守恒,来源:https://journals.plos.org/plosone/article?id = 10.1371/journal . pone . 0130140
- 从上一层传入神经元的所有相关都被收集起来并相加,然后再向下传递。当我们从一层递归到下一层时,我们最终丰富了输入图像,给出了每个像素的相关性。

对神经元的输入相关性求和,来源:【https://journals.plos.org/plosone/article? id = 10.1371/journal . pone . 0130140
还有待于定义我们如何分配神经元与其贡献输入或输入神经元的相关性。这可以通过多种方案来实现。作者给出了这样一个简单的方案:

单个神经元的层相关性传播,来源:【https://journals.plos.org/plosone/article? id = 10.1371/journal . pone . 0130140
我们注意到,上述方案只是近似总相关性公理的守恒。为了精确地保持和,我们必须以某种方式将偏置项重新分配回输入/输入神经元。
以下是 LRP 在 ImageNet 数据集上的一些结果:

来源:https://arxiv.org/pdf/1604.00825.pdf
深度提升(2017 年)
继塞巴斯蒂安·巴赫等人关于 LRP/泰勒分解的工作之后,两代情·什里库马尔、佩顿·格林塞德、安舒尔·昆达杰在他们的工作 中提出了通过传播激活差异学习重要特征的深度提升方法(ICML 2017) 。DeepLiFT(深度学习重要功能)使用参考图像和输入图像来解释输入像素(类似于 LRP)。虽然 LRP 遵循守恒定律,但没有明确的方法来分配像素间的净相关性。DeepLiFT 通过强制执行一个关于如何向下传播相关性的附加公理来解决这个问题。
DeepLiFT 遵循的两个公理是:
****公理 1。总相关性守恒:在每个神经元上,所有输入的相关性之和必须等于输入图像和基线图像的得分之差。这个公理和 LRP 的一样。

**总关联守恒,来源:【https://arxiv.org/pdf/1704.02685.pdf **
****公理 2。反向传播/链式法则:每个输入的相关性遵循类似梯度的链式法则。这足以帮助我们反向传播每个输入的类似梯度的相关性。这个公理使得 DeepLiFT 更接近“香草”梯度反向传播。

反向传播/链式法则,来源:https://arxiv.org/pdf/1704.02685.pdf
作者证明了上述两个公理是一致的。
给定这些公理,DeepLiFT 有哪些好的候选解决方案?作者建议将相关性分为积极和消极两部分:

正面和负面贡献,资料来源:https://arxiv.org/pdf/1704.02685.pdf
根据手头的函数,作者为 C() 和 m() 建议了以下候选解:
- ****线性规则对于线性函数:这与对 m()使用渐变完全相同。LRP 也会这么做的。

线性法则,来源:https://arxiv.org/pdf/1704.02685.pdf
- 像 ReLU,Sigmoid 这样的非线性函数的重新标度规则:这和 LRP 完全一样。

重新标度法则,来源:https://arxiv.org/pdf/1704.02685.pdf
线性和重新标度规则非常接近 LRP。
- reveal cancel(Shapley)规则对于像 MaxPool 这样的非线性函数:对 MaxPool 使用重新调整规则(参考输入为 0)将最终把所有相关性贡献归于最大输入。其他输入的变化对输出没有影响。RevealCancel 法则修正了这个反直觉的结论,利用了 Shapley 值 的思想。

沙普利法则,来源:https://arxiv.org/pdf/1704.02685.pdf
Shapley 值已经在博弈论中用于计算输入变量的属性。最近一些关于可解释人工智能的作品(如 、SHAP )使用了从 Shapley Values 得到的灵感。
作者展示了在 MNIST 数据集上训练的 CNN 上使用 DeepLiFT 的结果。

**深度提升 vs 梯度方法,来源:【https://arxiv.org/pdf/1704.02685.pdf **
接下来是什么?
继续研究相关性的公理方法,研究人员开发了一种完全基于梯度的方法,称为集成梯度,它满足许多理想的公理。最近,研究人员还探索修改 CNN 架构,使其更容易窥视。围绕这一点的一些新颖之处包括使用全局平均池和类激活映射。我们将在下一部分讨论这些很酷的技术。
要阅读更多关于神经网络可解释性的激动人心的作品,你可以点击这里阅读下一部分: 链接到第 4 部分
可解释的神经网络:最新进展,第 4 部分
回顾十年(2010–2020),四集系列
我们在哪里?
这个博客聚焦于神经网络可解释性的发展。我们将我们的演讲分为四个博客系列:
- 第一部分讲述了图像像素的可视化梯度对于解释 CNN 的 pre-softmax 类得分的有效性。
- 第 2 部分讲述了一些更先进的/改进的基于梯度的方法,如去卷积、导向反向传播来解释 CNN。
- 第 3 部分讲述了基于梯度的方法的一些缺点,并讨论了替代的公理化方法,如逐层相关性传播、泰勒分解、深度提升。
- 第 4 部分讲述了一些最近的发展,如集成渐变(上接第 3 部分)以及 CNN 架构中最近的创新,如类激活地图,开发这些地图是为了使特征地图更易理解。
公理方法
从上一节继续,我们将公理化方法和基于梯度的方法联系在一起。我们讨论一种基于梯度的方法,它遵循所有期望的公理。
集成渐变(2017)
在上一节中,我们看到了泰勒分解如何将像素值(和基线图像的像素)的梯度和差的乘积指定为单个像素的相关性。DeepLiFT 指定粗略梯度和输入图像与基线图像之间的像素值差的类似乘积。根据 RevealCancel 规则,我们可以观察到,单个像素的相关性是(粗略)梯度沿着δx 的正部分,后跟δx 的负部分的离散路径积分。这就引出了一个问题:
- 分数函数梯度上的路径积分对输入图像像素的特征属性有多有效。
Mukund Sundararajan、Ankur Taly 和 Qiqi Yan 在他们的工作“ 深度网络的公理化归属(2017) ”中研究了这种使用集成梯度的思想。作者沿着他们希望所有特征归因方案都满足的两个理想的公理批判了当时流行的归因方案:
公理 1。灵敏度:每当输入和基线在一个特征上完全不同时,不同的特征应该被赋予非零的属性。
可以看出,由于总相关性的守恒,LRP 和深度提升遵循灵敏度。但是基于梯度的方法不能保证灵敏度公理。发生这种情况是因为当分数函数相对于一些输入特征局部“平坦”时,在 ReLU 或 MaxPool 阶段饱和。通过饱和激活以适当的方式传递关联或归因是所有特征归因研究工作中反复出现的主题。
公理二。实现不变性:当两个模型在功能上等价时,它们必须对输入特性有相同的属性
“普通的”梯度在数学上保证了实现的不变性。但是像 LRP 和深度提升这样对梯度的粗略近似可能会打破这个假设。作者展示了 LRP 和 DeepLiFT 违反实现不变性公理的例子。
作者建议使用综合梯度进行特征归属:

综合渐变,来源:https://arxiv.org/pdf/1703.01365.pdf
作者进一步表明,上述定义遵循两个理想的假设:
- 灵敏度:根据微积分的基本定理,积分梯度归结为特征分数的差异,就像 LRP 和 DeepLiFT 一样。因此,他们就像 LRP 和 DeepLiFT 一样追随敏感性。
- 实现不变性:由于是完全按照梯度定义的,所以遵循实现不变性。
积分使用的默认路径是从基线到输入的特征的直线路径。对于上述公理,路径的选择并不重要。直线路径具有相对于基线和输入图像对称的附加属性。
以下是作者在 ImageNet 数据集上训练的 GoogleNet 模型上提供的一些结果:

综合渐变 Vs 渐变,来源:https://arxiv.org/pdf/1703.01365.pdf
新颖的建筑
随着神经网络在图像识别和定位方面的更好性能的发展,人们也对修改网络架构以使其更具可解释性感兴趣。在前面的章节中,我们已经讨论了一些可视化细胞神经网络特征图的方法。但是除了特征地图之外,CNN 还有一堆完全连接的层(在最后一个 CNN 层的顶部),这些层将过滤后的特征地图转换为 softmax 之前的分数,这不是很好解释。这一部分讲述了一些试图使 CNN 架构更具可解释性的作品。
类别激活图(2016)
由于完全连接的层不太容易解释,一些研究人员建议在(最后)CNN 层的每个特征图上用全局平均池(GAP) 替换完全连接的层,以在馈送到 softmax 之前将其减少为一维张量和其上的单个线性层。雷勃·周、阿迪蒂亚·科斯拉、阿加塔·拉普德里扎、奥德·奥利瓦、安东尼奥·托拉尔巴在他们的工作 中指出,这样的特征图(来自上一期 CNN)更具可解释性,学习深度特征用于鉴别性定位(CVPR 2016) 。


CNN 架构与全球平均池,来源:https://arxiv.org/pdf/1512.04150.pdf
GAP 如何让 CNN 更具可解读性?我们能从这样的架构中得到什么样的解释?
作者注意到,间隙层将每个特征图减少为单个标量,并且其后的权重可以被解释为每个特征图与特定类别的相关性。为了说明这一点,作者将每个类别的类别激活图(CAM) 定义为对应于该类别的各个特征图的加权和。

班级激活图,来源:https://arxiv.org/pdf/1512.04150.pdf
作者指出,尽管 CAMs 仅被训练用于图像识别,但它们在图像定位方面表现良好。这里是作者展示的单个图像的各种类的一些例子。

不同类别的 CAM 可视化,来源:https://arxiv.org/pdf/1512.04150.pdf
GradCAM 和制导 GradCAM (2019)
CAM 可视化的一个限制是,它只能应用于具有全局平均池(GAP)的架构。Ramprasaath Selvaraju、Michael Cogswell、Abhishek Das、Ramakrishna Vedantam、Devi Parikh、Dhruv Batra 将 CAM 方法扩展到其他架构,并在他们的工作“ Grad-CAM:通过基于梯度的定位从深度网络进行可视化解释(IJCV 2019、ICCV 2017) 中提出了 GradCAM 的想法并指导了 GradCAM。
作者注意到,在基于间隙的 CNN 中,在反向传播期间,最后一个 CNN 层中的特征图中的所有像素从上面的层接收相同的梯度。围绕这一思想,作者提出,在一般的 CNN 中,由最后一层 CNN 中的特征图接收的平均梯度可以用作定义类激活图的相应权重。作者将这种基于梯度的凸轮或梯度凸轮简称为。


完全连接层的局部线性近似以提取凸轮权重,来源:https://arxiv.org/pdf/1610.02391.pdf
可视化产生的 GradCAM 图像(向上采样到输入图像大小)提供了热图,描述了图像的哪些部分强烈影响输出。

GradCAM 狗 Vs 猫可视化,来源:https://arxiv.org/pdf/1610.02391.pdf
GradCAM 仅突出显示输入图像中负责特定类别激活的部分。对于更细粒度的细节,作者建议运行引导反向投影,并用 GradCAM 将结果信号逐元素相乘。作者称之为制导 GradCAM 。

制导 GradCAM,来源:https://arxiv.org/pdf/1610.02391.pdf
作者展示了制导 GradCAM 的许多有趣的应用,包括可视化由神经网络执行的各种各样的任务,如图像识别、视觉问题回答、检测性别偏见、在人工智能系统中建立更好的人类信任等。
前方的路
这篇博客重点介绍了过去十年(2010 年至 2020 年)在图像识别/定位领域对神经网络所做决策进行可视化的一些卓越工作。虽然本讨论仅限于计算机视觉主题,但这里讨论的大多数方法已经成功应用于自然语言处理、基因组学等许多领域。可解释的神经网络仍在发展,每天都有新的研究出现,以更好地解释神经网络做出的决定。
在过去的十年里,人们对人工智能的透明度、公平性、隐私性和信任度等问题越来越感兴趣。在这个可解释人工智能的更一般的领域中有许多有趣的作品,如石灰SHAP等。一些研究人员一直对探索新的机器学习模型感兴趣,如 软决策树 , 神经支持的决策树 ,这些模型可以隐式解释,并且足够强大,可以提取复杂的特征/表示。本博客中列出的大多数方法都不涉及重新训练网络,而只是窥视网络以提供可视化。最近一些名为 PatternNet 的工作挑战了这一假设,并探索了在生成解释方面更有效的神经网络架构。大量激动人心的工作正在进行中!
可解释人工智能的圣杯是当人工智能可以帮助人类从数据中做出新的发现,并指导我们的决策,而不仅仅是为我们提供“正确”的答案。
解释 BigQuery ML 模型
如何获得和解释对预测的解释
BigQuery ML 是一种简单易用的方法,只使用 SQL 就可以在结构化数据上调用机器学习模型。虽然它只是从线性回归开始,但通过将 BigQuery ML 与 TensorFlow 和 Vertex AI 连接起来作为后端,已经添加了更复杂的模型,如深度神经网络和 AutoML 表。换句话说,虽然我们写 SQL,但是执行的是 TensorFlow。

图片由 StartupStockPhotos 来自 Pixabay
BigQuery ML 正在从 Vertex AI 后端*获取更多功能。在早先的一篇文章中,我向你展示了超参数调谐。在这篇文章中,我将向你展示可解释性。
什么是可解释性?
可解释性是理解机器学习模型正在做什么的一种方式。有两种类型的解释。在局部可解释性中,我们问 ML 模型它是如何得出个体预测的结果的。为什么上面说这个租赁会持续 2000 秒?为什么会暗示这个交易是欺诈性的?
在全局可解释性中,我们询问特性的重要性。在预测租赁期限时,时间有多重要?交易金额对预测交易是否欺诈有多重要?
BigQuery ML 支持这两种类型的解释,但是根据我的经验,本地可解释性是最终用户想要的,所以我将重点讨论这一点。
回归模型
我将采用一个简单的模型来预测我在整本书中使用的自行车租赁期限(BigQuery:权威指南):
CREATE OR REPLACE MODEL ch09eu.bicycle_linear
TRANSFORM(
* EXCEPT(start_date),
CAST(EXTRACT(dayofweek from start_date) AS STRING)
as dayofweek,
CAST(EXTRACT(hour from start_date) AS STRING)
as hourofday
)
OPTIONS(model_type='linear_reg', input_label_cols=['duration'])
AS
SELECT
duration
, start_station_name
, start_date
FROM
`bigquery-public-data`.london_bicycles.cycle_hire
解释预测
而不是打电话给 ML。预测,我们称之为 ML。EXPLAIN_PREDICT 在预测的同时获得解释:
SELECT *
FROM **ML.EXPLAIN_PREDICT**(
MODEL `ai-analytics-solutions.ch09eu.bicycle_linear`,
(
SELECT 'Park Street, Bankside' AS start_station_name,
CURRENT_TIMESTAMP() AS start_date
)
)
结果是:

预计持续时间为 1929 秒。
该模型以 4598 秒的基线预测值开始,这是整个训练数据集的平均预测值。从第二个输入(时间戳)开始,模型提取出了一天中的小时和一周中的天。
星期几是星期三,模型将基线值增加了 247 秒。换句话说,人们在周三租自行车的时间比平均时间稍长。
但是,我们看到的是凌晨 4 点。这将导致租赁期限大幅下调 2800 秒!
最后,Parkside 是一个租金通常较低的车站。因此,模型应用了另一个修正,这次是 116 秒。
4598 + 247–2800–116 = 1929
分类模型
作为我们的第二个示例,我们来看一个欺诈预测模型:
CREATE OR REPLACE MODEL advdata.ulb_fraud_detection
TRANSFORM(
* EXCEPT(Amount),
SAFE.LOG(Amount) AS log_amount
)
OPTIONS(
INPUT_LABEL_COLS=['class'],
AUTO_CLASS_WEIGHTS = TRUE,
DATA_SPLIT_METHOD='seq',
DATA_SPLIT_COL='Time',
MODEL_TYPE='logistic_reg'
) AS
SELECT
*
FROM `bigquery-public-data.ml_datasets.ulb_fraud_detection`
同样,我们使用 ML。解释 _ 预测:
SELECT *
FROM ML.EXPLAIN_PREDICT(
MODEL `ai-analytics-solutions.advdata.ulb_fraud_detection`,
(
SELECT *
FROM `bigquery-public-data.ml_datasets.ulb_fraud_detection`
WHERE Amount BETWEEN 300 AND 305
LIMIT 1
)
)
结果的欺诈预测类为 0(无欺诈),概率为 0.946。这对应于-2.87 的 logits 值(概率是 logits 的 sigmoid)。
不幸的是,这个数据集没有告诉我们列是什么,但是基线是-2.70,表明数据值没有太多异常。
让我们尝试一个不同的预测,这次选择一个非常高价值的交易:
SELECT *
FROM ML.EXPLAIN_PREDICT(
MODEL `ai-analytics-solutions.advdata.ulb_fraud_detection`,
(
SELECT *
FROM `bigquery-public-data.ml_datasets.ulb_fraud_detection`
**WHERE Amount > 10000**
LIMIT 1
)
)
现在,该模型预测该交易很可能是欺诈性的。逻辑值是 12.665,而基线值是-2.7。这笔交易的概率大幅增加背后的关键因素是 V4(增加了 10.67)和 V14(减少了 4)。当然,如果我们知道 V4 和 V14 是……这将意味着更多,但公开发布的财务数据集没有这些信息。
如您所见,您可以使用实例解释来解释不同的输入是如何导致结果值发生变化的。这有助于建立模型正常工作的信心。
尽情享受吧!
*可解释性以最有效的方式为每个模型类型实现。在某些情况下,Vertex AI 被调用,而在其他情况下,它是在 BigQuery 内部完成的。但是这是一个实现细节——不管它是如何实现的,你都可以用同样的方式调用它。
向外行人解释人工智能
在聚会上教授人工智能概念的技巧和例子

图片来自 Unsplash 上的 Yutacar
作为人工智能爱好者,当朋友或熟人问我们整天都在做什么时,我们会回答一个笼统的说法,如“我从事人工智能应用/研究/等。”由于人工智能如今已成为一个家喻户晓的术语,他们可能会跟进并询问更多细节,这使得环境有些糟糕。这让你处境艰难。显然,人工智能非常复杂,如果你在一个聚会或晚宴上,在短时间内恰当地解释它是很困难的。你想用你的知识给人们留下深刻印象,并进行顺畅的交谈,但是他们的知识匮乏可能是一个重大障碍。
在一个月又一个月的家庭朋友、老师和同学问我人工智能是如何工作的以及我作为一名人工智能开发者做了什么之后,我想出了一些好的策略和类比来向完全的外行人解释人工智能。这样,每当你和问你 AI 的人说话时,你就不会慌张,也不会让人觉得你不知道自己在做什么。
一般提示
先处理任何误解。
如果你在聚会上遇到的人对人工智能到底是什么知之甚少,第一步是澄清他们以前的任何错误想法。在我教年轻人或不涉足科技的人的经验中,他们通常认为人工智能系统是一个可以自行操作的机器人意识。如果这些误解没有被消除,当你试图传达人工智能的基础知识时,他们会把他们不正确的想法和你告诉他们的混淆起来。这可能会导致你说话时被打断,让你的思路被转移。最简单的开始方式是让他们“用一句话给我解释一下 AI。”我总是从这个问题开始,因为它让我对他们的知识和他们可能有的任何错误想法有了一个基线。在此基础上,您就有了接下来对话的路线图。
还记得你是怎么学会 AI 的吗。
解释人工智能的最好方式是用你最初被教导的方式来教导它。对我来说,我第一次接触 AI 和 ML 是由我爸爸介绍的,他描述了机器使用数据进行学习。如果你像我一样,在第一次学习时没有背景知识,使用你所学的策略会非常有效。我爸爸解释说,机器可以获得大量数据来学习,当你进行逻辑思考时,这很有意义。询问人工智能的人很可能能够理解逻辑解释,所以只要想想是谁教你的,并遵循那些相同的步骤。
不要进入数学。
既然你很聪明,你的朋友可能也很聪明,但这并不意味着你应该开始向他们描述反向传播和梯度下降。奇怪的是,我看到很多人直接进入了如何优化模型参数的描述,这似乎是一种过于复杂的方式来教授一个完全外行的人工智能。在聚会场合,你只有很短的时间来表达你的观点。你可能认为证明你懂高级数学会让你看起来很聪明,但事实上,人们会更看重沟通技巧。如果你能用简单的方式将一个复杂的话题传达给门外汉,他们会认为你比你给他们讲微积分要聪明得多。
言简意赅,善于演讲。
这是一个向任何人解释任何事情的通用提示。但是,在解释 AI 这样的高级领域时,这一点尤为重要。你展示自己作品的大部分经历都是在小组会议或讨论会上,在这些会议上,每个人都很好地理解了你所解释的内容。因此,你可能更习惯于使用聚会上你的朋友不会理解的行话和术语。混淆视听是最糟糕的情况——它让你看起来好像不知道自己在做什么。确保在向外行观众演示时,使用外行术语和普通语言。做一个自信的演讲者,让他们经常提问。记住你只有很短的时间,因为你是在一个聚会上,所以你必须快速工作。他们问的问题越多,他们自己就越能把想法整合在一起,这样你的工作就容易多了。
简单的类比
AI/机器学习的基本前提是,计算机使用数据来学习底层模式,并根据这些数据进行预测。对于没有计算机科学背景的人来说,这种说法有点复杂。所以,让我们使用人工智能建立的原理——人类学习。几乎任何人都应该能够理解人类是如何学习执行新任务的,就像你的朋友们自己试图学习新事物一样。
数学考试
让我们举一个大学和高中的例子——准备数学考试。这是我过去几个月的类比。
当我准备数学考试时,我总是做练习题。为了在数学考试中取得成功,我需要理解或预先了解我正在练习的概念,我们可以忽略这一部分。试图解释参数如何以预训练的直觉的某些方式初始化不是一个好的第一步。我的目标是尽可能多地答对数学问题,以便在考试中获得 A。
打个比方,学生将是 AI 系统,教科书问题将是训练数据,答案关键将是标签,实际考试是测试案例。这是解释的设置。你必须确保他们明白从例子中练习是一种很好的学习方式。

安妮·斯普拉特来自 Unsplash 的照片
现在我们可以解释人工智能的训练是什么样的。训练的第一步是把所有的练习题做一遍。接下来,你要给你的答案打分,看看你错了多少。当你知道你错了什么,你会重新做同样的问题。然后再给他们打分,看看你错在哪里,再重新做一遍。坚持做下去,直到你觉得准备好了。正如我们所看到的,每次重复这些问题就像是在为另一个时代进行训练。与答案匹配并确定你做错了什么就是计算损失并更新你的参数。
这个例子是有效的,因为大多数人都做过类似的准备考试的程序。使用相关的类比可以减少你解释自己的次数,因为不管是谁在听你说话,他都可以自己把事情联系起来。
一旦你解释了每一步,就用更专业的人工智能术语来解释。所以如果你刚刚解释完如何纠正你的答案,快速描述一下什么是损失函数。确保遵循一般提示,并在更高的层次上解释它们,例如,仅仅将损失计算称为“惩罚”就足以让某人明白你在说什么。
儿童玩具
另一个很好的例子是为初学走路的孩子设计的玩具形状解算器,比如 this 或者下图中的那个。使用这种玩具,幼儿的任务是将特定的形状插入与形状匹配的孔中。对于没有什么先验知识的孩子来说,这个任务几乎是不可能的。

图片来自 Unsplash
我们可以用这个例子来说明,AI 模型在学习的时候往往没有先验知识,这也是为什么要花这么多数据和时间来训练的原因。蹒跚学步的孩子必须预测积木将适合哪种形状,一旦尝试,他可以从父母那里得到反馈,或者只是知道它不适合。这是损失计算过程,因为每次幼儿看到形状是否适合各自的洞,它将增加知识并再次尝试。下一次,他们更有可能答对。这个启发式的过程就是 AI 系统的学习方式,在给外行人解释的时候,应该能很快理解。每个人都明白试错的过程,他们只是不知道启发式是什么意思。这个例子更适合快速聊天,因为你不能深入聊天。
高级话题
这些例子甚至可以扩展到更高级的 AI 概念,如半监督学习或自监督学习。如果你想解释半监督学习,谈谈数据收集是一个大问题,为了类比,解释你有一些没有答案的数学问题。直觉上,不管怎样,做没有答案的问题是一个好主意,但是为了从中学习,你必须从有答案的问题中利用你的知识。为了解释自我监督学习,使用学生以前在课堂上做的热身问题的想法。你会把问题拿出来,自己修改,创造一个不同的解决方案,然后解决问题。即使没有解决方案,这也会让你理解问题的基本结构。
然后,您可以转而谈论现实世界的数据收集和标注是如何具有挑战性的,这促使使用高级策略来最大限度地利用可用数据。对于教科书,请注意,有些教科书的问题有限,只有奇数问题有答案,但做偶数问题仍然是有帮助的,不管你是否知道自己是否答对了。正如你所知道的,有许多很好的类比可以非常有效地表现人工智能的基本概念。
最后的想法
作为人工智能开发人员,我们有一项更复杂的工作要向外行人解释。但幸运的是,解释它真的很容易,因为人工智能是建立在人类学习的基础上的。你可能有聪明的朋友,所以当有人提问时,描述人工智能如何工作应该是一种令人兴奋的体验,而不是尴尬的体验,并且注意到他们何时开始明白你在说什么是非常有益的。如果顺利的话,对话会非常有趣。通过在新的技术革命领域工作,确保你可以向想学习的人恰当地传达人工智能,不仅给他们留下深刻印象,而且让其他人对人工智能有正确的看法。
使用电影宗旨解释开放人工智能环境的双 Q 学习
带着钦佩而不是剧透。如果你没看过这部电影,这不是问题。看看基于 CNN 的双 DQNs 如何解决你的OpenAI健身房 问题!

红色在左边前进,蓝色在右边后退图片作者
请阅读:
这个帖子绝不是为了吸引点击的人。我向你保证,在实际的主题上不会有任何妥协,我们会在任何需要的时候使用电影的类比来增加阅读的乐趣。
不是一种感觉,而是一种波动。
没有剧透
作为一个粉丝,我理解我们中的一些人因为疫情还没有看这部电影,因此在这篇文章中,绝对不会有剧透。我们只讨论一些让电影惊艳的概念,而不是电影,你不需要看电影来阅读这篇文章。
Tl;博士:【https://github.com/perseus784/Vehicle_Overtake_Double_DQN】https://github.com/perseus784/Vehicle_Overtake_Double_DQN
- 与解释机器学习所有概念的帖子不同,在这里,我们专注于项目的学习。每个元素如何影响我们的项目,以及如何设计这些将是主要的焦点。
- 这一点很重要,因为你可以通过原始论文以及互联网上不同帖子的其他精彩文章更好地了解概念和理解,但如果你打算自己实施一个,这种方法将有助于你了解实际障碍。
这个计划
- 基础知识(只是为了让它不碍事)
- 要求、环境和系统配置
- 深度强化学习
- 模型架构
- 钳形运动
- 该算法
- 后代
- 外卖食品
- 结果
- 原则
基础
强化学习 (RL)是机器学习的一个领域*关注的是智能代理应该如何在环境中采取行动,以最大化累积回报的概念。*
为什么?
在人工智能的其他方法中,我们的模型有一个特定的目标来学习并相应地调整参数。但是在这里,我们让模型选择它想学的东西,只是基于它从做一个特定的动作中得到的回报。
这很重要,因为我们人类不会为每项任务单独训练。我们只考虑回报,用解决任务的技能武装自己。我认为 RL 是迈向更通用的人工智能的第一步,而不是一个任务定制的人工智能。如果你想学习一个新的环境/任务,只要改变你得到的奖励。模特也应该学习你的新环境。
怎么会?
我们可以使用 RL 来训练一个代理来完成任何任务。这与我们人类的工作方式非常相似。
做点工作,吃点糖果。
做更多的工作,得到更多的糖果。
做不好/无效的工作得不到糖果。
够简单吧?在这里阅读更多信息:https://medium.com/@ipaar3/saturnmind-94586f0d0158
它像一个婴儿。把婴儿放在任何地方,它都会根据每一次行动所获得的回报来适应所处的环境。
这有用吗/我应该继续吗?
这篇文章的目标读者是想学习深度强化学习的人,这是一种基于深度学习的强化学习方法。如果你对 Deep RL 的任何方面感兴趣,如何创建一个可以自己学习玩游戏或处理任务的代理,你可以继续这篇文章。
希望获得一些关于深度 Q 学习、双 Q 学习及其实际实现的好见解的人是这篇帖子的目标受众。
也就是说,在互联网上学习一个新概念没有错,我会尽可能让你参与进来。
要求、环境和系统配置
- Python 3.7
- 张量流 2.3.0
- OpenCV 的 Numpy
- 你的开放式健身环境。
我们的目标是帮助用户汽车在道路环境中自动超越机器人汽车。我们在深度强化学习的帮助下训练用户汽车,奖励功能将在用户汽车每次减速时,每次撞上机器人汽车时以及如果它前面有任何机器人汽车时惩罚用户汽车。由于我们使用来自环境的原始像素作为我们的状态空间,我们可以容易地改变环境和奖励函数以适应新的问题。
- 这里使用的 OpenAI-Gym 环境是一个高速公路环境,它为我们的 RL 实验提供了一个干净的管道。如果你还没有注意到,由于在实现基于 RL 的环境中可能有很多版本和变化,OpenAI 将这个过程标准化了。
- 所以,任何你用 OpenAI 得到的环境,比如环境,将会有相同的获得奖励,变量和游戏统计的格式。对于我们的任务,我们将从到这里获取公路环境,如下所示。

作者拍摄的公路环境图片
主角:
在这个环境中,我们要训练我们的智能体(主角)通过直接摄取一个图像,输出一个动作,来学习超车我们环境中的车辆。但这并不意味着相同的管道对任何其他情况都没有用。正如我前面提到的,同样的代码可以应用于一个非常不同的问题,因为我们遵循的是 体育馆格式 。
请查看动物园的例子,通过修改几行代码就可以构建出什么。下面的格式对于所有的环境都是一样的,环境可以通过修改第 3 行的代码来替换。
***import** gym
**import** highway_envenv = gym.make("highway-v0")
observation = env.reset()
done =Falsewhilenot **done:**
env.render()
action = agent(observation) *# your agent*
observation, reward, done, info = env.step(action)
if done:
observation = env.reset()
env.close()*
系统配置:由于网络架构将涉及到几个 CNN,因此最好有一台配有像样的 GPU、超过 4GB 虚拟 RAM 和 4GB 系统 RAM 的机器。
代号:https://github.com/perseus784/Vehicle_Overtake_Double_DQN
深度强化学习
简单来说,深度强化学习只是强化学习,但我们将使用神经网络来学习我们环境的策略,而不是 Q 表。
为什么?
- 我们都知道神经网络在学习模式方面很有效。为了投入使用,我们用神经网络代替了 RL 算法的 Q 表。
- 这样做的好处是多方面的。首先,我们不需要为环境中的每个状态记忆单独的策略。这意味着,我们现在可以在更大的环境中进行 RL,而没有内存限制。为大型环境的每个状态制定一个策略将导致 Q 表的规模呈指数级增长。
- 其次,我们现在可以学习更复杂的政策,这些政策需要时间上的理解。旧的 RL 不能考虑环境以前的状态,而现在,dqn 可以学习采取一系列步骤,并在此基础上进行推广。
- 最后,我们可以有不同类型的网络输入,如图像、图像序列甚至数字序列。这可以是 CNN 和 rnn 的输入,它们分别在理解这些数据类型方面非常强大。
模型架构
该项目的最令人兴奋的部分是为给定的问题选择正确的神经网络。虽然我们可以使用一个普通的神经网络来学习状态并给出政策,但最好是建立一个更擅长处理和处理图像的 CNN。做出这个决定是为了让我们可以使用相同的网络来解决没有定义状态的问题,并给出我们看到的原始输入,从而节省一些数据工程工作。
最初,我用一个像模型一样的 MobileNet V2,但后来测试时意识到它对我没有帮助,最好建立我们自己的网络来学习任务。

作者的建筑形象模型
- 拍摄四幅连续的环境图像并作为输入。这包括我们的特工在环境中移动。
- 我们的网络具有如上图所示的架构,层块基本上由多个卷积层和一个 MaxPool 层组成。

作者图片
- 在架构中引入 Dropout 是为了避免学习更多的 about 图像,让网络学习我们的任务。这样,我们迫使网络了解任务和策略。
- 该网络从 200×100×4 尺寸(4 个堆叠灰度帧对应 4 个尺寸)到 9x3x512 特征地图。下图显示了使用的卷积块数、每个卷积块数的功能图以及池层。
- 然后,这些特征地图被展平并馈入两个密集层,每个层有 512 个隐藏单元。
- 这种架构有助于网络学习代理的复杂目标。我们的网络包含大约 9M 个参数。
代号:【https://github.com/perseus784/Vehicle_Overtake_Double_DQN】
钳形运动
到目前为止,我们已经看到了一个简单的神经网络如何取代 q 表方法,但经过实验,这种方法有很多问题,可以通过使用两个不同的网络来克服,这两个网络同时运行,使我们的代理更好。
钳形运动或双重包围是一种战术,在这种战术中,部队同时攻击敌人阵型的两侧。
怎么了?
深度 Q 学习在理论上很有意义,对吗?但是当尝试实现时,一旦复杂性增加一点,它的表现就不太好了。

图片来自【arxiv 的双 DQN 纸
上面的等式是通过取可用策略的最大值来计算州的新策略的贝尔曼等式。然而,这是因为它采用下一个状态的最大 Q 值的估计,并执行动作,如上面的等式所示。在网络学习期间进行的这种高估会引入最大化偏差。
- 简而言之,网络倾向于高估每个状态的具有较高值(最大值)的策略,这可能导致策略收敛波动,使得收敛非常慢。
- 这是有意义的,因为我们同时在评估新策略和更新它。这肯定会导致训练过程的中断。
- 更简单地说,我们不想同时学习和玩这个游戏(但它是理想的)。实际上更好的做法是玩游戏,从你的错误中学习,提高水平,犯新的错误并从中学习。
- 所以,这里我们不是单一的网络,我们要引入另一个网络。估计器中的一个将集中于最大化策略的 Q 值,另一个用于更新该值。
红蓝队:

作者图片
在电影《特尼特》中,他们做了一种叫做 太阳穴钳形运动 穿越时间来获取关于环境的知识,同时进行攻击。也就是说,一个小组将专注于收集事件发生时的环境知识,而另一个小组将利用这些知识攻击敌人。

作者的双 Q 学习图像
- 同样,我们将有两个网络在运行。一个将是我们的训练网络(红队),它用从游戏中获得的数据训练我们的代理,另一个将是预测网络(蓝队),它播放环境并为训练网络收集新的经验以保存在存储器中。
- 我们可以看到,随着时间的推移,代理获得的奖励会越来越高,因此这种操作会继续下去。您还可以看到使用我们的两个网络计算保单的最新数学公式。

arxiv 双 DQN 论文双 Q 学习图像的修正贝尔曼方程
- 这是他们介绍这项技术的原始论文,它是深度 RL 世界的主要助推器之一。我认为现在双 Q 学习这个名字以后用起来很好。
该算法
我们已经准备好了主角和环境,可以开始训练了。由于这是一个在线培训程序,数据是实时收集、处理和反馈的。
工作流程:

作者提供的培训管道图像
- 你可以在图片中看到我们训练程序的伪代码。我们将在下一节讨论ε和内存变量。
- 一旦环境启动,我们的预测网络将预测给定状态的行动,并保存从采取行动中获得的奖励。
- 这被重复几次,以收集更多的数据保存在存储器中,并避免过于频繁的训练。
- 每四次迭代,训练网络将对存储器中收集的数据进行训练。一旦网络被训练,该过程继续获得更多的数据。
**memory = [ [current_state, action, next_state, reward, done], ...]**
- 上面显示的存储器 数据结构存储当前状态、代理采取的行动、采取行动后到达的下一个状态、采取该行动获得的奖励以及情节是否结束。
- 对于每 10 集,我们用本地保存的目录中训练好的模型更新我们预测的网络。我认为这是一个拉平的过程。

作者的培训流程图
- 左图显示了我们的培训流程。训练网络基本上是在本地训练和存储模型。另一方面,每 10 集的预测网络通过加载本地存储的模型来更新自身。
- 这种拉平过程使得训练和收敛非常顺利,并且从直观的角度来看,随着时间的推移,代理学会犯比愚蠢的错误更好的错误。
培训:
培训持续数小时,最好使用 Tensorboard 记录所有日志,以便实时跟踪和可视化。我们在这里跟踪的两个主要指标是亏损和平均阶段性回报图。
损失:
- 损失图将显示网络在每个时期后学习得更好,因为它显示网络总体上做出更好的决策。

作者的损失图
- 我们在该系统中优化的损失是分别通过训练网络和预测网络的预测和地面真实值之间的均方误差。
- 请看一下这段代码,看看我们是如何分别从训练网络和预测网络得到预测和地面实况的。
**loss = tf.keras.losses.mean_squared_error(ground_truth, prediction)**
- 我们使用新的贝尔曼方程,我们必须找到预测的政策 Q 值和正确的政策之间的损失。
- 在我们将学习率降低到最小值后,来自训练网络的损失图更加平滑,并且我们还使用 Tensorboard 平滑了损失值。这个我们简单讨论一下。
平均情景奖励:
- 奖励图将显示随着纪元的增加,奖励也将增加,因为随着网络学习更多,它将执行给予它最大奖励的动作。

作者的奖励图
- 在他们开发双 DQN 技术的原始论文中,作者运行了 2.5 亿个时代的环境,与我们相比,我们只运行了 3K 时代的训练。
- 完美的政策不会因此而实现,但关键是要学习过程,而不是实现理想的结果。
- 特别是,在我们的实验中,我们给了更大的权重,让汽车保持在道路的右侧,以获得更好的回报。作为一个副作用,汽车在道路左侧行驶时会引发更多的事故。这有助于我们理解为强化学习任务设计奖励机制的复杂性。
- 一般来说,随着时间的推移获得越来越多的奖励是了解我们的代理正在学习我们打算学习的任务的一个好方法,甚至从视觉上来说,随着情节的增加,代理表现得很好,犯的错误也越来越少。
- 精确度指标并不能很好地衡量我们的网络在强化学习中的表现。因为可能是 Q 值死记硬背而不是学习任务的结果。
代号:T3【https://github.com/perseus784/Vehicle_Overtake_Double_DQN】T5
后代
老实说,这是这篇文章最重要的部分。我认为这些实验和决定更重要,因为这些是你在试图建立一个类似的项目时将面临的障碍或障碍。
后代——一个人的后代。
在我们构建管道时,添加/调整系统的一些关键元素以获得最大收益是非常重要的。这是通过创建多个版本的网络,并试验不同的超参数,甚至一些增加我们训练价值的机制来实现的。最后,选择合适的。
你看…特尼特不是在过去创立的,它将在未来创立。
体验回放
- 当代理学习和遍历情节时,它会忘记以前学习过的经验。因此,当我们训练时,递归地提醒网络它学到了什么是很重要的。
- 做到这一点的方法是使用体验重放机制。这种机制有助于网络在训练时通过随机选择来记住它的经历。

按作者划分的内存使用图像
- 该程序基本上存储了一个数据结构,其中包含当前观察结果、采取的行动、下一个状态、对该行动的奖励以及该集是否结束。
- 另外,我们不想存储整个历史,所以我们有一个 15000 个这样的数据结构的缓冲存储器。
- 缓冲存储器基本上是一个固定大小为 15000 的队列,当它积累更多时,就会弹出旧的存储器。网络使用该内存池来选择其批次并在其上进行训练。
- 假设我们的代理玩这个游戏,它学习通过一个特定的障碍。然后,当它继续前进到下一个障碍时,我们不希望它忘记从上一节中学到的东西。因此,该机制有助于深度 Q 网络根据经验进行训练,并在每个训练期间刷新其学习。
探索与开发
- 当代理从第一次训练开始就过度适应它在一种情况下的经验,并且不探索获得更多奖励的新选项(注意:不要将这与神经网络过度适应混淆)。
- 也就是说,如果代理发现了一些次优路径或处理这种情况的方法,因为它没有任何负面影响,所以它会选择这个值。
- 这最终导致代理人满足于次优的解决方案,而报酬从未达到理想状态。

作者的勘探与开发图表
- 为了克服这个陷入刚性状态的问题,我们可以使用一个叫做ε的值。
- Epsilon 是一个情节衰减变量,从 1 开始衰减到 0,直到总情节的一半在该会话上运行。
- 对于环境的每个渲染,我们的代理决定随机探索动作空间或者从预测的网络动作中选择一个。这可以通过如下代码实现:
**if np.random.random() > epsilon:
action = np.argmax(get_prediction(state)) **#Exploitation**
else:
action = np.random.randint(0, no_of_actions) **#Exploration****
- ε值在代理的探索或开发模式之间决定,并且它随着时间而减小。这是在培训的前半部分完成的,也就是说,在我们的案例中,我们在 3000 集的 1500 集内将ε值从 1 降低到 0.1,如上图所示。
超参数调谐
- 通常的神经网络训练涉及大量的超参数调整,但由于深度学习社区的人们多年来对他们的发现所做的努力,这些年来变得更加精简和容易。
- 但是对于深度 RL,尤其是ε、学习速率和批量调整,该过程变得更加复杂。这是非常复杂的,因为每件事都很重要,即使这些参数的微小变化也会随着时间的推移造成巨大的差异。
- 但是对我们有用的是在训练的前半段慢慢降低ε。
- 学习率 ,另一方面,我们使用默认的 TensorFlow Adam 学习率,但它在训练期间给我们带来了更大的损失波动,因此我们的训练网络使用了降低的 1e-4 学习率。这有助于减少波动损失。
- 批量 为最终培训的 32 个。最初,我们使用较高的批量大小,如 256,512,但这些并没有显示出改进,而且偶尔会导致内存耗尽错误。
- 训练频率 也是这其中的一个主要因素。训练频率基本上是目标网络在一个时间间隔内应该被训练的次数。代理在环境中每走四步,我们就训练一次网络。这为网络提供了足够的训练间隔以及收集新数据。
- 拉平 是我们多长时间更新一次网上或预测网络来传递经验。这是每 10 集做一次,以便随着时间的推移网络预测变得更好,并且不会太多地更新在线网络而导致预测太不稳定。
代号:https://github.com/perseus784/Vehicle_Overtake_Double_DQN
外卖食品
- 先选择你想解决的问题,再选择解决方案。有一个解决方案,或者在这种情况下,一个特定的人工智能技术,并试图使它适合你的解决空间,只是糟糕的工程。
- OpenAI gym 是我们基于 RL 的实验的惊人资源。一旦了解了格式,我们就可以用同样的方式处理所有的环境。
- 要知道,无论我们的假设听起来多么正确,它都会出错。比如,我认为通过使用 MobileNet v2 作为特征提取器,我们永远不会出错,但它对我不起作用。
- 我还尝试了一个想法,比如结合我们 CNN 中的一些盗梦空间模块,以获得更好的效果,这些模块来自我之前的项目 。这个想法在那个项目中发挥了惊人的作用,但在这个项目中却没有任何改进。
- 在处理一个神经网络架构的时候,从小的开始,随着你的进展增加复杂性,而不是从一个巨大的模型开始,在那里我们将不能识别出哪里出错了。
- 设计奖励函数是 RL 最重要的方面。因为这将决定我们的代理必须学习的任务。例如,在我们的例子中,我们对停留在最喜欢的车道上的代理人给予了奖励,但这引起了不希望的效果,如在其他车道上制造更多的碰撞,因为代理人总是试图停留在最右边的车道上。
- 总是考虑我们设计的奖励功能的分支,这些分支大部分隐藏在明显的地方,但是直观上有意义。
- 我们可以制作一个复杂的奖励函数来覆盖所有的边缘情况,并运行它一百万次迭代,最终会使它变得完美。但重点是创建一个通用的方法,你可以只输入原始图像(而不是每个任务需要设计的状态)和训练代理的奖励。

来自way mo 网站的鸟瞰图
- 不是噱头。这个项目不仅仅是展示 RL 的力量。许多自动驾驶研究使用这种俯视方法来学习车辆代理的控制机制。
- 这包括 Waymo、特斯拉、优步等。该图显示了用于培训 SDCs 控制代理的输入内容,这有助于导航和巡航控制。
- 在今天,人工智能领域的教师比学生多。我只是作为一个学习者而不是一个大师来提出这一点。
结果
请查看下面的每张图片,了解我们的代理如何通过训练超越该领域的其他代理。这里的是一分多钟的连续录像。你可以看到它在左侧比右侧犯更多的错误,因为在我们的奖励函数中最喜欢的车道偏向。




****
由作者训练的代理演示图像
原则
我今天的学习,明天属于你。使用它,不要重复发明轮子。
宗旨:被一个团体或运动坚持的原则。

红色在左边前进,蓝色在右边后退图片作者
总的来说,该项目为我们提供了关于深度 RL 的原始像素输入、网络架构选择、超参数调整、奖励函数设计、体验回放、探索与开发机制以及双重深度 Q 学习的良好知识。有了这个,我们可以进一步将这个项目扩展到更先进的技术,如 A2C 或 DDPG 的连续空间学习。
这里看一下代码https://github.com/perseus784/Vehicle_Overtake_Double_DQN。****
完整演示:
其他职位:
!eraC ekaT
解释 vim-sensible 的每一行
学习机
因为并非所有明智的 Vim 对你来说都是明智的

以下是你点击这个故事的三个可能原因。首先,它作为一个推荐故事出现在你的屏幕上。你不是在用 Vim,而是对 Vim 和懂事的 Vim 很好奇。
第二个原因,你已经痴迷于 Vim 很多年了,你想,“我已经知道里面所有的行,但是这是一篇关于 Vim 的文章,所以我还是要读它。”
最后,你和我处于学习 Vim 的相同阶段,你已经搜索了 Sensible 的注释版本,但是还没有找到。
首先,我们需要理解 Vimrc 文件。Vimrc 文件是 Vim 的配置文件,允许您定制 Vim、安装插件等等。它通常在~/.vimrc中找到,但是如果它不在那里,你可以创建它。对于 Windows,您可以在这里找到关于查找 vimrc 文件的更多信息。
https://vim.fandom.com/wiki/Open_vimrc_file
我在寻找一个 vimrc 文件,其中充满了有用的缺省值,作为我未来的 vimrc 的基础。首先,我想分享我在多个帖子和讨论中发现的一个反复出现的答案。引用道格·布莱克的文章,
不要在你的 vimrc 中放任何你不理解的行。
我认为这是一个非常合理的建议。程序员不会在他们的代码中添加任何额外或不必要的行。vimrc 文件也应该如此。
在我的搜索过程中,我偶然发现了 Reddit 上的这篇文章,它指出 Sensible 是 vimrc 文件的一个很好的起点。
https://github.com/tpope/vim-sensible
与我得到的其他选择(范围从完全相同的 Sensible 到超过 300 行)相比,我决定更深入地研究这一点作为起点。
这是 Sensible Vim 的完整代码。
tpope 对 vim 敏感的代码
有些最难理解的台词是有注释的,但是大部分根本没有任何解释。尽管这是可以理解的,但对于那些更熟悉 Vim 的人来说,在这些行上添加注释是没有意义的。
也许对他们来说会是这个样子。
if speed > 80.0: # checks if speed is more than 80.0
speeding = True # set speeding flag to True
n_violation += 1 # add 1 to variable n_violation
无论如何,我不是 Vim 专家,所以我想用过度注释的版本来更好地理解这些行实际上做了什么。
这是我发现的,随意跳过一些琐碎的解释。
逐行解释
if exists(‘g:loaded_sensible’) || &compatible
finish
else
let g:loaded_sensible = ‘yes’
endif
第一点是将 vim 变量loaded_sensible设置为‘yes ’,大概是为了与其他插件兼容,这些插件检查是否使用了 vim-sensible。
if has(‘autocmd’)
filetype plugin indent on
endif
该行将检查autocmd是否启用。大多数 Vim 都会附带,但是一些发行版比如vim-small和vim-tiny会默认不带autocmd。
在这种情况下,需要检查autocmd,因为filetype检测系统需要它才能正常工作。
filetype plugin indent on是这些命令的简称。
filetype on
filetype plugin on
filetype indent on
第一个命令打开 Vim 的文件类型检测,以帮助设置语法高亮显示和其他选项。plugin部件将加载特定文件类型的插件,如果它们存在的话。最后一位将加载特定文件类型的缩进文件,如果它们也存在的话。
例如,如果你想只为 Python 语言激活某些插件,那么你可以创建一个文件~/.vim/ftplugin/python.vim。将所有专门针对 Python 的插件和命令放在该文件中。
一个好的做法是在另一个文件中分离缩进配置(~/.vim/indent/python.vim)。然而,我通常只是把缩进放在插件文件里。
if has(‘syntax’) && !exists(‘g:syntax_on’)
syntax enable
endif
与前面的if命令相似,这个命令检查 Vim 是否用syntax编译,以及全局变量syntax_on是否已经存在。目标是避免多次打开syntax(如果它已经启用)。
set autoindent
autoindent选项将在插入模式下创建新行时使用当前缩进,通过正常返回或o / O。
set backspace=indent,eol,start
如果您使用 Vim 已经有一段时间了,您会注意到有时退格键在插入模式下不能正常工作。当您尝试在自动插入的缩进、换行符和插入开始处退格时,会发生这种情况。
设置这些退格选项将使您能够以特定的顺序正常地对它们执行退格操作。
set complete-=i
Vim 中的-=操作符意味着如果已经设置了选项,它将删除这些选项。在这种情况下,它将从 autocomplete 中删除i选项。
i选项声明它将“扫描当前和包含的文件”,如果当前文件包含许多其他文件,这可能会影响自动完成的结果。因此,禁用该选项是有意义的。
set smarttab
smarttab将根据shiftwidth、tabstop或softtabstop插入n空白(如果它们被配置的话),并且还将删除行首的一个shiftwidth空白,而不是默认的一个空白。
set nrformats-=octal
nrformats是数字格式的缩写,有助于定义哪种格式将被视为 Vim 的数字。如果您键入13,然后在正常模式下悬停在它上面并按 Ctrl+A,那么它会将它递增到14。或者可以用 Ctrl+X 递减到12。
由于使用基数 8,octal选项将导致007增加到010。在正常使用中,这不是预期的行为,因为没有多少人在日常工作中使用 base 8。禁用后,007将增加到008。
if !has(‘nvim’) && &ttimeoutlen == -1
set ttimeout
set ttimeoutlen=100
endif
nvim变量用于检查您运行的是 NeoVim 还是普通 Vim。ttimeoutlen设置 Escape、Ctrl、Shift 等组合键的超时时间。默认情况下,ttimeoutlen的值是-1,它将被改为 100,但这一行将其显式设置。
ttimeoutlen有什么用?假设您为 Cmd+Shift+Up 设置了一个映射。该命令的关键代码是^[[1;6A。另一方面,Esc 的关键代码是^[。如果将ttimeoutlen设置为 5000,则每次按下 Esc 键,都会等待 5 秒钟,然后才会注册 Esc 命令。这是因为 Vim 认为您有可能在 5 秒钟窗口内按下其余的按键代码。
可能有一种情况是将ttimeoutlen设置得更长是有用的,但是就我个人而言,我没有发现它们对我的用例有用。
set incsearch
增量搜索或incsearch允许 Vim 在您键入搜索关键字时直接进入下一个匹配结果。如果没有这个设置,您需要按 Enter 键让 Vim 转到搜索结果。
“ Use <C-L> to clear the highlighting of :set hlsearch.if maparg(‘<C-L>’, ’n’) ==# ‘’
nnoremap <silent> <C-L>
:nohlsearch<C-R>=has(‘diff’)?’<Bar>diffupdate’:’’<CR><CR><C-L>
endif
谢天谢地,这段代码附有解释,因为我肯定要花相当长时间才能弄明白它的意思。如果您启用了hlsearch,那么按 Ctrl+L 将会清除高亮显示,因为它不会自己消失。
set laststatus=2
laststatus的默认值是 1,这意味着只有当有 2 个或更多的 Vim 窗口打开时,Vim 的状态栏才会显示。通过将值设置为 2,现在状态栏将一直存在,即使你只打开 1 个 Vim 窗口。
set ruler
显示光标位置的行号和列号。有时候这有点多余,因为大多数状态栏插件都是内置的。
set wildmenu
当在命令行中调用自动完成时,打开wildmenu会在命令行上方显示可能的完成。查看效果的最简单方法是在正常模式下键入:!,然后按 Tab 键触发自动完成。
if !&scrolloff
set scrolloff=1
endif
scrolloff值确保你的光标上方和/或下方始终至少有 n 行。这一行检查您是否设置了scrolloff值,如果没有,则将其设置为 1。
scrolloff的默认值是 5,所以这个设置实际上将它减少到 1。
有些人将该值设置为一个非常大的数字(999 ),以使光标保持在屏幕中间,但我发现每当我创建一个新行时,屏幕就会刷新并移动一行,这有点令人迷惑。也就是说,我也将我的设置为 1。
if !&sidescrolloff
set sidescrolloff=5
endif
如果scrolloff是上下两行,sidescrolloff是光标左右两边的字符数。默认值为 0。您也可以将此值设置为 999,使光标保持在线的中间。
set display+=lastline
如果你打开一个很长的文件,它不适合屏幕,Vim 通常会用一串“@”来替换它。通过将其设置为lastline,Vim 将显示尽可能多的字符,然后在最后一列加上“@@@”。
该行使用+=来避免覆盖已设置为truncate的设置,这将在第一列显示“@@@”。
if &encoding ==# ‘latin1’ && has(‘gui_running’)
set encoding=utf-8
endif
==#操作符表示区分大小写的比较,与==不同,因为它依赖于:set ignorecase。如果 Vim 运行 GUI,该行将把编码从latin1改为utf-8。
if &listchars ==# ‘eol:$’
set listchars=tab:>\ ,trail:-,extends:>,precedes:<,nbsp:+
endif
listchars用于指定使用:list命令时,特定字符的显示方式。默认值是'eol:$',所以如果您已经配置了自己的listchars,这一行不会覆盖您的设置。
制表符将被替换为>,后跟数字\,直到制表符结束。尾随空格将被替换为-。如果当前行不适合显示在屏幕上,扩展字符>将显示在最后一列,前面的字符<将显示在第一列。最后,nbsp代表不可破坏的空格字符,将被替换为+。
if v:version > 703 || v:version == 703 && has(“patch541”)
set formatoptions+=j “ Delete comment character when joining commented lines
endif
还好这也是评论的台词。如果 Vim 版本比 7.3 或带有补丁 541 的 7.3 版本新,则为formatoptions添加j选项。
当您连接注释行时,该行将删除注释字符(例如“#”、“//”)。Vim 中formatoptions的默认值是tcq,但是这有点误导,因为它可能会根据打开的文件类型而改变。
if has(‘path_extra’)
setglobal tags-=./tags tags-=./tags; tags^=./tags;
endif
setglobal命令与通常的set命令有点不同。通过使用set,我们设置了一个选项的局部和全局值,而setglobal只设置了全局值。
全局值是应用于所有 Vim 的默认值。局部值仅适用于当前窗口或缓冲区。
tags命令定义了 tag 命令的文件名。该行所做的是从当前的tags中删除./tags和./tags;。^=命令将值./tags;添加到当前的tags中。
例如,如果tags的当前值是./a, ./b并且我们执行了setglobal tags ^= ./tags;,那么它将变成./tags;, ./a, ./b。;表示如果已经在./tags中找到标记文件,Vim 将停止查找。
if &shell =~# ‘fish$’ && (v:version < 704 || v:version == 704 && !has(‘patch276’))
set shell=/usr/bin/env\ bash
endif
=~#操作符意味着与正则表达式匹配,所以在这种情况下,它检查您是否正在使用fish shell。Vim 在fish上运行时有一些已知的问题,主要是因为fish不完全兼容 POSIX。
https://stackoverflow.com/questions/48732986/why-how-fish-does-not-support-posix
如果您正在使用 Vim 的旧版本(比 7.4 版本更早)或者使用没有补丁 276 的 7.4 版本,这一行将把您的 shell 设置为bash,以避免在运行一些不能用fish shell 执行的命令时出错。
set autoread
这将使 Vim 在检测到文件在 Vim 之外被更改时自动读取文件。但是,如果文件被删除,它将不会被重新读取,以保留删除前的内容。
if &history < 1000
set history=1000
endif
将保留的历史数量设置为最小 1000。默认情况下,Vim 对于每种类型的历史只记住 50。在 Vim 中有五种类型的历史。
:命令- 搜索字符串
- 公式
- 输入行(
input()功能) - 调试模式命令
if &tabpagemax < 50
set tabpagemax=50
endif
使用-p命令行参数或:tab all命令将打开的最大选项卡页数从默认值 10 设置为至少 50。
if !empty(&viminfo)
set viminfo^=!
endif
Viminfo 是一个由 Vim 自动编写的文件,作为一种“缓存”来存储信息,比如命令行历史、搜索字符串历史、最后一次搜索模式、全局变量等。
这一行将在前面加上!值,该值将保存和恢复以大写字母开头且没有任何小写字母的全局变量。
或者,如果您想在某种私有模式下运行 Vim,您可以使用命令set viminfo=禁用 viminfo 功能。
set sessionoptions-=options
当您使用:mksession进行会话时,从sessionoptions中删除options值将禁止保存选项、映射和全局值。
Tim Pope 自己解释说,他禁用它是因为记住全局选项会覆盖对 vimrc 所做的更改,这是他不想要的。
https://github.com/tpope/vim-sensible/issues/117
set viewoptions-=options
与上一行类似,这一行在使用:mkview制作视图时会禁用相同的东西。
我对视图不太熟悉,但是这一页解释了为什么不使用这一行会导致 Vim 在加载保存的视图时跳转到不同的工作目录。
https://vim.fandom.com/wiki/Make_views_automatic
“ Allow color schemes to do bright colors without forcing bold.if &t_Co == 8 && $TERM !~# ‘^Eterm’
set t_Co=16
endif
t_Co是终端使用的颜色数量。当当前颜色数量设置为 8 且当前终端不固定时,这将使颜色增加到 16 种。
但是大多数现代终端不需要这个,因为通常默认设置为 256。您可以通过键入:echo &t_Co来检查这一点。
“ Load matchit.vim, but only if the user hasn’t installed a newer version.if !exists(‘g:loaded_matchit’) && findfile(‘plugin/matchit.vim’, &rtp) ==# ‘’
runtime! macros/matchit.vim
endif
matchit是一个 Vim 特性,它使我们能够在左括号和右括号、HTML 标签等之间跳转。您可以尝试在正常模式下按下左括号顶部的%,它会将您的光标移动到匹配的右括号。
如果matchit尚未启用或者现有的仍然是旧版本,该行将启用。
if empty(mapcheck(‘<C-U>’, ‘i’))
inoremap <C-U> <C-G>u<C-U>
endifif empty(mapcheck(‘<C-W>’, ‘i’))
inoremap <C-W> <C-G>u<C-W>
endif
mapcheck命令检查您是否为特定模式下的特定键设置了自定义映射。在这种情况下,它会在为 Ctrl+U 和 Ctrl+W 创建新映射之前,检查您是否已在插入模式下映射了它们。
这两行将防止意外删除,而不可能同时使用 Ctrl+U 和 Ctrl+W 进行撤销。通过在实际的 Ctrl+U 或 Ctrl+W 之前执行Ctrl+G u,我们可以使用撤销操作(正常模式下的u)恢复我们删除的文本,如果没有这些重新映射,这是不可能的。
https://vim.fandom.com/wiki/Recover_from_accidental_Ctrl-U
最后的想法
在理解,或者至少试图理解所有这些行之后,我决定不把 Sensible 作为插件安装,而是偷一些有用的行。
我最喜欢的一个是与set hlsearch配对的set incsearch,它支持渐进式搜索,也可以渐进式地突出显示搜索结果。
有一些我省略了,比如如果使用 fish shell,将 shell 设置为 bash,因为我自己也在使用 zsh shell。
首先,我开始寻找构建 vimrc 的最佳文件。在搜索过程中,我发现很多其他人也在搜索同样的东西。然而,在对 Vim 配置有了基本的了解之后,我决定从头开始构建一个,因为我使用 Vim 不仅是为了编码,也是为了写作。
如果任何解释是错误的或不够清楚,请随时发表评论给予纠正或要求澄清任何行。
最后一个提示,如果您不确定一行代码的作用,您可以通过键入:h 'commandname'来搜索 Vim 的手册,以显示特定命令的帮助。了解这些命令在 vimrc 文件中的作用和效果是一个很好的实践。
学习机是一系列关于我所学到的,并且认为足够有趣可以分享的事情的故事。有时也是关于机器学习的基础。 获得定期更新 上新故事和 成为中等会员 阅读无限故事。
https://chandraseta.medium.com/membership
解释我如何在以数据为中心的新竞争中名列前茅
实践教程
继吴君如本人代言(!)关于我的上一篇文章,很自然地分享了所有的技巧(带代码!)关于我如何应对 DeepLearning.ai 的新挑战。
10 月 21 日编辑:我以优秀奖的身份被选为 8 名获奖者之一(见下文)。

比赛解释…又来了!
如果你还不熟悉 DeepLearning.ai 几周前推出的以数据为中心的新挑战,你可以看看我几周前写的描述这一挑战的文章。
不确定这是否值得?只是听从吴恩达的建议😉:

截图自 Andrew NG 对我上一篇文章的背书—2021 年 8 月
https://www . LinkedIn . com/feed/update/urn:李:活动:6830591493435224064/
https://medium.com/geekculture/a-deep-dive-into-andrew-ng-data-centric-competition-eb2bc0886005
如果你赶时间,长话短说:比赛的目标是产生尽可能好的一组图片来训练预定义的模型(ResNet50)识别罗马数字。该竞赛提供了一个大约。3000 张图片,包括嘈杂和贴错标签的数字,如下图所示:

从训练数据集中提取-由作者在 DeepLearning.ai 的授权下编译
让游戏开始吧!
我将介绍不同的步骤,以平稳的方式达到良好的性能,但这显然是我为了找到最佳组合而进行的多次测试和试验的结果!
如果您想进一步探索我的解决方案,我还在 GitHub 上创建了一个专门的资源库(本文末尾有链接)。
1.图片评论
第一项任务可能是要求最高的:查看每张照片,检查一些标准。以下是我用过的一些:
- 这看起来像罗马数字吗?(如果没有,我们就应该去掉!)
- 图片标注正确吗?(例如。“II”在“III”文件夹中,反之亦然)
- 什么是数质?(评分从 1:好到 4:差)
- 背景质量如何?(等级同上)
- 什么是字体风格?(“Arial”或“Roman”)
- 数字的确切格式是什么?(“viii”或“VIII”)
- 我们能应用对称吗?(水平或垂直对称通常适用于“I、II、III 或 X”数字,但不适用于“I”、“II”或“VII”)
以下是我评估的三个例子(以表格形式存储):

图片分类示例—按作者分类的图片
在查看 3000 张照片时(这花了我大约 2 到 3 个小时😅),我有时会有“似曾相识”的感觉,我开始怀疑数据集中是否隐藏了一些重复项?这并不奇怪,所以我也必须考虑到这一点。

我还设计了一个简单的函数来自动检查文件夹的内容,以评估我将执行的不同操作的结果。像我后来在笔记本中使用的所有其他函数一样,我将它存储在一个专用的“dcc_functions.py”中(可以在 GitHub 存储库中找到)。
以下是初始数据集的输出:

folders_sumary 由 Pierre-Louis Bescond 输出
2.数据集清理
2.1 噪声消除
我首先删除所有我认为纯粹是噪音或者至少是噪音太大而无法正确训练模型的图片。这显然是个人的选择,每个参与者可能都有不同的选择。我确定大约。要删除的 260 张图片(相应的列表存储在 GitHub repo 上的 Excel 文件中)。
2.2 去重
如前所述,我感觉有些照片完全一样,但手动识别它们是不可能的。我知道有一些技术可以解决这个问题:
- 将大小相同的文件配对 …但是会出现很多误报
- 配对大小相同的文件&配置(如两个“II”或“viii”)
- 按文件统计配对文件(使用,例如。, PIL 的 ImageStat
- 利用结构相似度指标对文件进行配对(此处有说明)
- 根据文件的“哈希”号将文件配对
由于这不是一个“生死攸关”的问题,我决定使用第二种解决方案,这种方案既简单又容易实现。剧本(此处)确定了大约 200 对双胞胎照片,其中 53 张实际上是真正的复制品(下面是一些例子):

由DCC _ find _ duplicates . ipynb通过 Pierre-Louis Bescond 识别的双胞胎对的例子
2.3 移动右侧文件夹中的一些图片
没必要在这上面花太多时间:当一张照片被贴错标签时,我只是把它移回到它所属的文件夹。
2.4 前卫还是不前卫?
在我们继续之前,我想分享一个有趣的发现:我看了两次照片:一次是在我参加比赛的时候,另一次是在我对照片有了更好的想法的时候。
当我第一次查看原始图片时,我已经排除了许多看起来太模糊而无法训练模型的“边缘案例”。
但比赛开始几周后,我开始习惯这些尖锐的案例,并以不同的方式考虑它们,比如:“嗯,包括这个案例可能是好的,以教导模型这个案例可能会发生。”我最后添加了大约。80 张图片到数据集。
与直觉相反的是,这一新选择的性能正在下降,包括更尖锐的图片。怎么会这样
参与者之一 Mohamed Mohey 在专门的对话主题中强调,32x32 变换(在训练前应用于数据集)有时会完全改变图片的本质,如下例所示:

32x32 转换前后的相同图片-图片由作者提供-归功于 Mohamed Mohey
我们可以观察到,由于这种 32x32 的转换,一个明显的“III”正在变成一个似是而非的“II”,解释了为什么一些尖锐的案例不一定会给模型带来有价值的信息。
在 32x32 的转换后再看一遍照片可能是件好事,但我没有!
2.5 使用“标签簿”图片训练模型
DeepLearning.ai 的组织者提供了一组 52 张图片,这些图片不在“train”或“validation”文件夹中,以评估我们的模型在 ResNet50 训练结束时的表现。
这是一个很好的方式来了解模型在最终和隐藏数据集上将如何表现,但由于排行榜上显示的分数,我也“猜测”到对隐藏数据集的最终评估包括 2420 张图片 ( 请参见这里的相应笔记本)。所以 52 张图片无论如何都不是很有代表性!
所以我干脆把这些图片放在了我的训练文件夹里!越开心,越有趣😁
2.6 评估增强技术的影响
正如你可能知道的,在由图片组成的数据集上使用增强技术来帮助深度学习模型识别允许正确推断类别的特征是非常常见的。
我决定考虑其中的几个:
- 水平和垂直对称
- 顺时针和逆时针旋转(10°和 20°)
- 水平和垂直平移
- 裁剪图片中的白色区域
- 添加合成“盐和胡椒”噪声
- 将一些图片的噪声转移到另一些图片上
2.7 实现海关功能
第一个功能非常简单,很容易用 PIL、OpenCV,甚至像 ImgAug 这样的“打包解决方案”来实现。我认为分享一些关于我设计的自定义函数的技巧会更有趣😀
2.7.1 平方裁剪功能
裁剪操作是一个有趣的操作!由于图片最终将被转换为 32x32 的图片,放大数字所在的区域可能会更好。
但是,如果数字不是“平方”形状,则在转换为 32x32 时,结果可能会失真(如下所示)。我重新设计了该函数,以便裁剪后的输出将始终为方形,并避免这种失真效果:

原始和裁剪的可能输出—作者提供的图像
2.7.2【盐和胡椒】功能
由于最终评估数据集上的背景可能不总是纯白的,我试图通过添加合成背景来增加图片。
我使用了“salt & pepper”函数,它基本上是将“0”和“1”随机添加到描述图片的 NumPy 数组中:

原创和“经验丰富”的图片——作者提供的图片
2.7.3 背景噪声传递函数
我对“盐和胡椒”函数的结果不太满意,因为噪声总是均匀的,所以我想了另一种方法给图片添加噪声。
我回收了一些我原本认为不可读的图片,让它们成为一些“嘈杂的背景”的基础。还有一些“背景很重”的图片,我去掉了编号(如下所示)以获得更多的样本。
它为我提供了一个“嘈杂背景库”,里面有 10 张图片,我在应用水平或垂直对称后随机添加到一些图片中:

嘈杂的背景银行-作者图片

背景噪声转移示例—作者提供的图片
2.8 选择最佳增量
由于允许的图片数量不能超过 10.000 个元素,我必须知道哪些转换提供了最高的效果。
我决定通过比较基线(没有转换的干净数据集)和每种增强技术的单独性能来对它们进行基准测试(总结如下):

增强的影响,基于损失或准确性-图片由作者提供
我们可以观察到旋转、平移和裁剪带来了比其他方式更大的影响,所以我决定把重点放在这些方面。
和“瞧”!
由于该过程是随机的(以 50%的概率和一些随机参数应用变换),脚本的每次迭代将产生图片的唯一组合。
我的许多测试产生了大约 84%的性能,而最高的竞争对手达到了 86%(基线为 64%)。我猜是光荣的😅
将有一些额外的调整要考虑(如创建我自己的图片,并将其添加到数据集,但我选择只依赖于提供的初始图片)。其他人可能也尝试过!
竞争对手表现的全球概览
可能还值得一提的是,我分析了竞争对手在挑战的前几周(直到 2008 年 6 月)的表现,我们可以看到大多数参与者很快达到了可接受的表现,很快就达到了 75%及以上:

参赛作品的性能分析—作者提供的图片
最后的话
如前所述,在对数据进行适当的审查/清理,并在不到 30 秒的时间内执行一个脚本之后,您可以轻松地胜过最先进的模型在有噪声的数据下所能产生的结果!
我真的很喜欢参加这个挑战,根据我的说法,比“GPU 能力”更需要“新鲜的想法”,我真的很期待下一次挑战!
我们与其他参赛者和 Lynn(来自 DeepLearning.ai)进行了许多有趣而丰富的互动,分享我们对这场“同类第一”竞赛的看法。许多参与者更希望分享他们的观点和发现,而不是在排行榜上名列前茅。
我也知道生成“平均和嘈杂的数据”有多困难…所以祝贺组织者提供了这么好的材料来研究!
当然,我希望你喜欢 DeepLearning.ai 关于以数据为中心的挑战的第二部分!
等级
我的解决方案被 Andrew 和他的团队从 2000 多份提交的作品中选中,并获得了荣誉奖(共有 8 名获奖者/团队):

正如我所承诺的,这里是 GitHub 知识库的链接,欢迎在评论中分享你的经验和/或发现:
https://github.com/pierrelouisbescond/data-centric-challenge-public https://pl-bescond.medium.com/pierre-louis-besconds-articles-on-medium-f6632a6895ad
用简单的方式解释 Python 类
使用示例理解类的基础

作者插图
当我第一次学习 Python 类时,我发现它真的很复杂,我不明白为什么需要知道它们。在一堂大学课上,教授开始直接解释构建类的语法,而没有解释做这个特定主题的真正意义,并使用非常无聊的例子,使我迷失了方向。
在这篇文章中,我想用不同的方式解释 Python 类。我将开始解释为什么需要知道这些类,并展示一些已经构建好的类的例子。一旦明确了外部环境,下一步就是使用一些例子和插图一步步地展示如何定义类。
目录:
- 课程介绍
- 创建一个类
- 构造函数方法
- 神奇的方法
- 实例方法
1.课程简介
当你开始学习 Python 的时候,你肯定遇到过这样一句话:
Python 是一种面向对象的编程语言
意思是用 Python 编程导致处处被对象包围。在瞬间
我们给一个变量赋值,我们就创建了一个对象。这个对象属于一个已经预先构建好的特定类,比如数字、字符串、列表、字典等等。根据类的不同,对象会有不同的属性和方法
让我们来看一些对象和类的经典例子:
a = 28
print(type(a))
#<class 'int'>
我们定义了一个对象 a,它属于 Integer 类。
l = [1,2,3]
print(type(l))
#<class 'list'>
这一次,新对象属于 List 类。从对象中,我们可以调用已经在 Python 中预先构建的方法。要查看对象中允许的所有方法,我们使用 dir 函数:
dir(l)

从输出中,我们可以注意到该类可以提供三种类型的方法:
- 初始化器方法
__init__,这样调用是因为它是初始化对象属性的方法。此外,一旦创建了对象,就会自动调用它。 - 神奇的方法是两边有双下划线的特殊方法。例如,
__add__和__mul__分别用于对同一类的对象求和、相乘,而__repr__则以字符串的形式返回对象的表示。 - 实例方法是属于被创建对象的方法。例如,
l.append(4)在列表末尾添加一个元素。
2.创建一个类
现在,我们将创建一个空类,并在教程中逐步添加部分代码。
class Dog:
pass
我们创建了一个名为 Dog 的类,其中pass用来表示没有定义任何东西。
jack = Dog()
print(type(jack))
#<class '__main__.Dog'>
一旦我们定义了 Dog 类,我们就可以创建类的对象,它被分配给变量 jack。它是使用类似于我们调用函数的符号构建的:Dog()。
该对象也可以被称为实例。如果你发现“实例”或“对象”这两个词写在某个地方,不要感到困惑,它们总是指同一个东西。
像以前一样,我们检查对象的类型。输出清楚地指出该对象属于 Dog 类。
3.初始化器方法
除了前面显示的代码,初始化器方法__init__用于初始化 Dog 类的属性。
class Dog:
**def __init__(self,name,breed,age)**:
self.**Name** = name
self.**Breed** = breed
self.**Age** = age
print("Name: {}, Breed: {}, Age: {}".format(self.Name,
self.Breed,self.Age))
我们可以观察到该方法有不同的参数:
self是用作第一个参数的标准符号,指的是稍后将创建的对象。访问属于该类的属性也很有用。name、breed和age是剩余的自变量。每个参数用于存储对象的特定属性值。Name、Breed和Age是定义的属性。注意这些属性通常不大写。在本文中,它们是大写的,以突出属性和它们相应的值之间的区别。
jack = Dog('Jack','Husky',5)
#Name: Jack, Breed: Husky, Age: 5
print(jack)
#<__main__.Dog object at 0x000002551DCEFFD0>
print(jack.Age)
#5
我们再次创建了对象,但是我们也指定了对应于属性的值。如果您尝试运行代码,您将自动获得灰色窗口第二行中显示的文本行。这是检查所定义的类是否运行良好的好方法。
还值得注意的是,初始化器方法是在创建对象后自动调用的。这一方面可以通过打印属性“Age”的值来演示。您可以对其余的属性执行相同的操作。
4.神奇的方法
也有可能以更复杂的方式打印相同的信息。为此,我们使用神奇的方法__repr__:
class Dog:
def __init__(self,name,breed,age):
self.Name = name
self.Breed = breed
self.Age = age
**def __repr__(self):**
return "Name: {}, Breed: {}, Age: {}".format(self.Name,
self.Breed,self.Age)
方法__repr__采用一个唯一的参数self,它可以从这个参数访问对象的属性。
jack = Dog('Jack','Husky',5)
print(jack)
#Name: Jack, Breed: Husky, Age: 5
如果我们显示创建的新实例,我们可以查看属性及其各自的值。
5.实例方法


睡觉还是醒着?保罗·特里内肯斯在 Unsplash 上的照片。卡尔·安德森在 Unsplash 上拍摄的照片。
实例方法是属于类的方法。作为神奇的方法,它们接受一个输入参数self来访问类的属性。让我们看一个例子:
class Dog:
def __init__(self,name,breed,age,**tired**):
self.Name = name
self.Breed = breed
self.Age = age
self.**Tired** = tired
def __repr__(self):
return "Name: {}, Breed: {}, Age: {}".format(self.Name,
self.Breed,self.Age)
**def Sleep(self):**
if self.Tired==True:
return 'I will sleep'
else:
return "I don't want to sleep"
在初始化器方法中,我们添加了一个新的参数tired,并因此添加了一个新的属性Tired。之后,我们定义了一个叫做 Sleep 的新方法:如果属性的值等于 True,狗就要睡觉,否则,它就不睡觉。
jack = Dog('Jack','Husky',5,tired=False)
print(jack.Sleep())
#I don't want to sleep
狗不累,所以不睡觉。
最终想法:
在这篇文章中,我提供了 Python 类的快速总结。我希望你发现这有助于巩固你的课程基础。我没有解释其他类型的方法,静态方法和类方法,因为我想把重点放在最常见的方法上。而且,另一个有趣的话题是类继承,我写的另一篇帖子里有涉及。感谢阅读。祝您愉快!
你喜欢我的文章吗? 成为会员 每天无限获取数据科学新帖!这是一种间接的支持我的方式,不会给你带来任何额外的费用。如果您已经是会员, 订阅 每当我发布新的数据科学和 python 指南时,您都可以收到电子邮件!
解释 sci kit-与 SHAP 一起学习模型

照片由 Marek Piwn icki 在 Unsp lash 上拍摄
数据科学基础
走向可解释的人工智能
可解释的人工智能(XAI) 通过使机器学习模型更加透明,帮助建立对它们的信任和信心。 XAI 是一套工具和框架,可用于理解和解释机器学习模型如何做出决策。一个有用的 XAI 工具是 Python 中的 SHAP 库。此工具允许我们量化特征对单个预测以及整体预测的贡献。该库还带有美观易用的可视化功能。在这篇文章中,我们将学习 SHAP 库的基础知识,以理解来自 Scikit-learn 中内置的回归和分类模型的预测。

1.夏普价值观✨
形状值帮助我们量化特征对预测的贡献。Shap 值越接近零表示该要素对预测的贡献越小,而 shap 值远离零表示该要素的贡献越大。
让我们来学习如何为回归问题提取要素的 shap 值。我们将从加载库和样本数据开始,然后构建一个快速模型来预测糖尿病进展:
import numpy as np
np.set_printoptions(formatter={'float':lambda x:"{:.4f}".format(x)})
import pandas as pd
pd.options.display.float_format = "{:.3f}".formatimport seaborn as sns
import matplotlib.pyplot as plt
sns.set(style='darkgrid', context='talk', palette='rainbow')from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.ensemble import (RandomForestRegressor,
RandomForestClassifier)import shap # v0.39.0
shap.initjs()# Import sample data
diabetes = load_diabetes(as_frame=True)
X = diabetes['data'].iloc[:, :4] # Select first 4 columns
y = diabetes['target']# Partition data
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size=0.2,
random_state=1)
print(f"Training features shape: {X_train.shape}")
print(f"Training target shape: {y_train.shape}\n")
print(f"Test features shape: {X_test.shape}")
print(f"Test target shape: {y_test.shape}")
display(X_train.head())# Train a simple model
model = RandomForestRegressor(random_state=42)
model.fit(X_train, y_train)

获取 shap 值的一个常见方法是使用解释器对象。让我们创建一个Explainer对象,并为测试数据提取shap_test:
explainer = shap.Explainer(model)
shap_test = explainer(X_test)
print(f"Shap values length: {len(shap_test)}\n")
print(f"Sample shap value:\n{shap_test[0]}")

shap_test的长度是 89,因为它包含了每个测试实例的记录。从第一个测试记录中,我们可以看到有三个属性:
◼ shap_test[0].base_values:目标的基值
◼ shap_test[0].data:每个特性的值
◼ shap_test[0].values:每个特性的形状值
让我们了解一下这些属性中的每一个向我们展示了什么。
📍1.1 基础值
基值( shap_test.base_values ) ,也称为期望值( explainer.expected_value),是训练数据中的平均目标值。我们可以用下面的代码来检查这一点:
print(f"Expected value: {explainer.expected_value[0]:.1f}")
print(f"Average target value (training data): {y_train.mean():.1f}")
print(f"Base value: {np.unique(shap_test.base_values)[0]:.1f}")

因此,我们将互换使用词语期望值和基础值。
📦 1.2.数据
接下来,shap_test.data包含与X_test相同的值:
(shap_test.data == X_test).describe()

让我们把它转换成数据帧来看看:
pd.DataFrame(shap_test.data, columns=shap_test.feature_names,
index=X_test.index)

这只是我们传递给它的数据集的副本。
✨ 1.3.价值观念
来自shap_test的最重要的属性是values属性。这是因为我们可以从中访问 shap 值。让我们将 shap 值转换成数据帧,以便于操作:
shap_df = pd.DataFrame(shap_test.values,
columns=shap_test.feature_names,
index=X_test.index)
shap_df

我们可以看到每个记录的 shap 值。如果我们把这些 shap 值加到期望值上,我们将得到预测:

我们来看看是不是这样:
np.isclose(model.predict(X_test),
explainer.expected_value[0] + shap_df.sum(axis=1))

厉害!这里我们用np.isclose()忽略浮点不准确。现在,我们有了 shap 值,我们可以像这样进行自定义可视化,以了解特性贡献:
columns = shap_df.apply(np.abs).mean()\
.sort_values(ascending=False).indexfig, ax = plt.subplots(1, 2, figsize=(11,4))
sns.barplot(data=shap_df[columns].apply(np.abs), orient='h',
ax=ax[0])
ax[0].set_title("Mean absolute shap value")
sns.boxplot(data=shap_df[columns], orient='h', ax=ax[1])
ax[1].set_title("Distribution of shap values");

左侧子图显示了每个特征的平均绝对形状值,而右侧子图显示了形状值按特征的分布。从这些图表中可以看出,bmi在使用的 4 个特性中贡献最大。
2.形状内置图📊
虽然我们可以使用 shap 值构建我们自己的可视化,但是shap 包带有内置的奇特的可视化。在这一节,我们将熟悉这些观想中的一些精选。我们将会看到两种主要的情节🌳全局:显示特征总体贡献的图形。这种图显示了一个特征对整个数据的总体贡献。
🍀局部:显示特定实例中特征贡献的图。这有助于我们深入了解单个预测。
🌳 2.1.全局|条形图
对于前面显示的左侧子图,有一个等效的内置函数,只需几次击键:
shap.plots.bar(shap_test)

这个简单但有用的图显示了特性贡献的强度。该图基于特征的平均绝对形状值:shap_df.apply(np.abs).mean()。要素从上到下排列,具有最高平均绝对形状值的要素显示在顶部。
🌳 2.2.全局|汇总图
另一个有用的情节是摘要情节:
shap.summary_plot(shap_test)

这里有一个替代语法:shap.plots.beeswarm(shap_test)用于这个具体的情节。与之前一样,要素按其平均绝对形状值排序。与之前的条形图相比,此图表更加复杂,包含更多信息。下面是解释该图的指南:
◼️ 图的横轴显示了特征的 shap 值分布。每个点代表数据集中的一条记录。例如,我们可以看到对于bmi,点非常分散,在 0 附近几乎没有任何点,而对于age,点更靠近 0 聚集。
◼️ 点的颜色显示特征值。这个额外的维度允许我们看到 shap 值如何随着特征值的改变而改变。换句话说,我们可以看到关系的方向。例如,我们可以看到,当bmi为高时,shap 值往往较高(由粉红色圆点表示),当bmi为低时,shap 值往往较低(由蓝色圆点表示)。还有一些紫色的点分散在光谱中。
如果我们发现默认颜色图不直观或不合适,我们可以将其更改为我们首选的 matplotlib 颜色图,如下所示:
shap.summary_plot(shap_test, cmap=plt.get_cmap("winter_r"))

此外,我们还可以使用不同的绘图类型。这里有一个例子:
shap.summary_plot(shap_test, plot_type='violin')

🌳 2.3.全球|热图
热图是可视化 shap 值的另一种方式。我们看到的是彩色编码的单个值,而不是将形状值聚合成一个平均值。特征标绘在 y 轴上,记录标绘在 x 轴上:
shap.plots.heatmap(shap_test)

该热图由顶部每个记录的预测值(即f(x))的线图补充。
我们可以使用cmap参数将颜色图更改为我们想要的颜色图:
shap.plots.heatmap(shap_test, cmap=plt.get_cmap("winter_r"))

🌳 2.4.全局|力图
这个交互图让我们可以看到由记录构成的 shap 值:
shap.force_plot(explainer.expected_value, shap_test.values,
X_test)

就像热图一样,x 轴显示每条记录。正的形状值显示为红色,负的形状值显示为蓝色。例如,由于第一条记录的红色成分比蓝色成分多,因此这条记录的预测值将高于预期值。
交互性允许我们改变两个轴。例如,y 轴显示预测,f(x),x 轴按上面快照中的输出(预测)值排序。
🍀 2.5.局部|条形图
现在,我们将通过图表来理解个别情况下的预测。让我们从柱状图开始:
shap.plots.bar(shap_test[0])

语法与第 2.1 节中的完全相同。Global | Bar plot' 除了这次我们对单个记录的数据进行切片。
🍀 2.6.局部|瀑布图
这是条形图的另一种替代形式:
class WaterfallData():
def __init__ (self, shap_test, index):
self.values = shap_test[index].values
self.base_values = shap_test[index].base_values[0]
self.data = shap_test[index].data
self.feature_names = shap_test.feature_namesshap.plots.waterfall(WaterfallData(shap_test, 0))

瀑布图是信息密集的,有四位信息:
◼️在 y 轴上,我们看到记录的实际特征值。如果您不确定我的意思,请将X_test.head(1)与 y 轴上的值进行比较。
◼️在图表的右下角,我们看到E[f(X)],预期值。
◼️在左上角,我们看到f(x),预测值。
◼️ ️Just 像前面的柱状图一样,横条代表颜色编码的特征贡献。从底部的期望值开始,我们可以看到每个贡献是如何上下移动预测值以最终达到预测值的。
🍀 2.7.局部|力图
最后一个要熟悉的图是单个记录的力图。如果我们将该图旋转 90 度并绘制多个记录,我们将看到全局力图。
shap.plots.force(shap_test[0])

我们既可以看到基础值:153.4,也可以看到预测值:103.48。我们还可以看到特性贡献的分解。
3.调整分类🌓
到目前为止,我们已经关注了一个回归示例。在这一节中,我们将学习一种方法来调整我们所学的内容以适应二元分类。让我们导入 titanic 数据的子集并训练一个简单的模型:
# Import sample data
df = sns.load_dataset('titanic')
df['is_male'] = df['sex'].map({'male': 1, 'female': 0}) # Encode
# Keep numerical complete columns
df = df.select_dtypes('number').dropna()
X = df.drop(columns=['survived'])
y = df['survived']# Partition data
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size=0.2,
random_state=1)print(f"Training features shape: {X_train.shape}")
print(f"Training target shape: {y_train.shape}\n")
print(f"Test features shape: {X_test.shape}")
print(f"Test target shape: {y_test.shape}")
display(X_train.head())# Train a simple model
model = RandomForestClassifier(random_state=42)
model.fit(X_train, y_train)

✨ 3.1.形状值
我们将创建一个解释器对象,并像前面一样提取 shap 值:
explainer = shap.Explainer(model)
shap_test = explainer(X_test)
print(f"Length of shap_test: {len(shap_test)}\n")
print(f"Sample shap_test:\n{shap_test[0]}")

就像回归shap_test.data将包含与X_test相同数量的记录。但是,从这个示例记录中,我们可以看到values 和base_values的尺寸不同。
我们先来看看基值。基值现在告诉我们每个类的概率。我们将关注积极类(即y==1,生存):
print(f"Expected value: {explainer.expected_value[1]:.2f}")
print(f"Average target value (training data): {y_train.mean():.2f}")
print(f"Base value: {np.unique(shap_test.base_values)[0]:.2f}")

现在,让我们看看 shap 值。还为这两个类提供了 Shap 值。你也会注意到它们是彼此的反面。我们可以这样提取正类的 shap 值:
shap_df = pd.DataFrame(shap_test.values[:,:,1],
columns=shap_test.feature_names,
index=X_test.index)
shap_df

让我们仔细检查一下将 shap 值的总和加到预期概率上是否会得到预测概率:
np.isclose(model.predict_proba(X_test)[:,1],
explainer.expected_value[1] + shap_df.sum(axis=1))

太棒了,现在你知道如何得到概率单位的 shap 值了。
📊 3.2.形状内置图
是时候调整我们之前看到的情节了。为了避免重复,我将展示一个全局图的示例和一个局部图的示例,因为其他图可以使用相同的逻辑来复制。
全局|条形图:让我们检查特征对预测正类的总体贡献:
shap.plots.bar(shap_test[:,:,1])

局部|瀑布图:我们来看看第一个测试记录的瀑布图:
shap.plots.waterfall(shap_test[:,:,1][0])

从这个图中,我们可以看到每个特征是如何对属于正类的预测概率做出贡献的:对于这个记录是 0.98。
📍 3.3.使用案例
在我们结束之前,让我们看一个示例用例。我们将为幸存者找到最不正确的例子,并试图理解为什么模型做出不正确的预测:
test = pd.concat([X_test, y_test], axis=1)
test['probability'] = model.predict_proba(X_test)[:,1]
test['order'] = np.arange(len(test))
test.query('survived==1').nsmallest(5, 'probability')

第一次记录的存活概率是 0.03。让我们看看特性是如何促成这一预测的:
ind1 = test.query('survived==1')\
.nsmallest(1, 'probability')['order'].values[0]
shap.plots.waterfall(shap_test[:,:,1][ind1])

年轻的三等舱男性乘客……我们可以看到,性别、乘客级别和年龄已经大大降低了预测。让我们在训练数据中找到类似的例子:
pd.concat([X_train, y_train],
axis=1)[(X_train['is_male']==1) &
(X_train['pclass']==3) &
(X_train['age']==22) &
(X_train['fare'].between(7,8))]

所有类似的训练例子其实都没有存活下来。这就说得通了。这是一个小用例,说明 shap 如何有助于解释为什么模型会得出不正确的预测。

希望您喜欢学习如何使用 SHAP 库来理解 Scikit-learn 中内置的回归和分类模型。因为 SHAP 是模型不可知的,所以你也可以把它用于其他模型。我希望你能很快使用这个可解释的人工智能工具来理解为什么模型会做出预测,以便改进模型并更好地向他人解释它。
您想访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果你使用 我的推荐链接 ,成为会员,你的一部分会费会直接去支持我。
感谢您阅读这篇文章。如果你感兴趣,这里有我其他一些帖子的链接:
◼️️ K 近邻解释
◼️️ 逻辑回归解释
◼️️ 比较随机森林和梯度推进
◼️️ 决策树是如何构建的?
◼️️ 管道、ColumnTransformer 和 FeatureUnion 说明
◼️️ FeatureUnion、ColumnTransformer &管道用于预处理文本数据
再见🏃 💨
用反事实的例子解释 XGBoost 模型的决策
模型可解释性——故障检测、识别和诊断
反事实推理是可解释性的一般范式。它是关于确定我们需要对输入数据应用什么样的最小变化,以便通过分类模型将它分类到另一个类中。
典型的应用场景是故障检测和诊断。让我们想象一下,我们可以使用传感器精确地监控生产过程,这些传感器遍布整个生产链(通常在每个工作站)。利用这些数据,我们可以在制造过程的每个阶段跟踪产品。因此,可以记录与最终出现故障的产品相关联的数据,用于进一步分析,以试图追溯故障是从哪里引入的。特别是,我们有兴趣找到引入故障的工作站,并且如果可能的话,有兴趣诊断那里可能出现了什么问题(如果数据允许的话)。在这一点上,您可能已经猜到,主要目标是避免长时间停止生产链,同时试图发现哪里出了问题。拥有一个能够在眨眼之间提供这种答案的人工智能可能会证明非常有用,最重要的是,对商业有好处,这是一个足够好的借口,可以解决你在阅读本文其余部分时遇到的所有理论困难。

如果你不希望这种事发生在你身上,请进一步阅读:)。你不必再品尝生产线上生产的所有饼干,以确保它们符合祖母的标准。你将有一个预测它的模型,每次饼干不符合标准时,你将能够准确地说出生产线的哪个地方出了问题,以及为什么,甚至有什么最小的行动来使事情恢复正常。以及 100%人工智能驱动的解决方案。[图片由作者提供]
好消息是我们能够设计非常有效的故障检测模型(FDM)。后者能够实时分析来自不同性质(数值、分类……)的大量数据,并在生产的不同步骤进行测量,以预测给定的制造元件是否有缺陷。不太好的消息是,解释这种模型提供故障诊断的决定要困难得多。通常使用的 FDM 通常是非常复杂的黑盒模型,并且除了透明之外什么都不是。
在这篇文章中,我们展示了一类称为树集合模型的模型,属于流行的高性能模型,如 XGBoost ,LightGBM,random forests …,我们可以使用一种称为“反事实解释”的方法来解释这类模型的决定。为了简单起见,我们在此仅考虑将数据分为两类的二元分类模型:正常/故障。
对于被模型分类为故障的给定查询点,我们计算称为反事实示例的虚拟点(以下称为 CF 示例)。后者是输入空间中欧几里得距离最近的点,被模型分类为正态。这个点是虚拟的,因为它不一定存在于训练集中。大多数情况下不会,我们是基于 FDM 模型参数建立的。CF 示例背后的几何直觉如下图所示。

两类分类器的决策区域。模型预测类别 1 的区域标记为“C1”,模型预测类别 2 的区域标记为“C2”。点 P#i 的最接近的反事实示例由 CF#i 表示。它是最接近 P#i 的点,被模型分类为类别 2。在上图中,我们将所有点 P#i 放在类 1 中,所以我们在类 2 中寻找它们各自的反事实例子。[图片由作者提供]
对于一个错误的数据,我们可以使用其相关联的 CF 示例来说明需要在最小程度上改变什么,以便它返回到正常类。你看到它在几英里外,这是我们用来执行故障诊断。这种方法非常有效,因为它可以发现细微的变化,从而区分错误数据和正常数据。这些变化可以是多变量的,也就是说,与正常状态相比,几个输入特征可能已经发生了变化。(CF vs 特性重要性?)最重要的是,我们有与物理设备(工作站)和制造过程相关的输入特性。因此,如果故障输入数据与 CF 示例的比较指出,为了恢复正常,我需要将站 25 中的温度降低 0.1°C,并将站 31 中的压力增加 0.05 巴,我可以迅速在我的生产链上安排这种干预,以避免新的故障发生和浪费更多的时间/材料。这也是反事实解释特别有趣的地方,它们给你一个精确的想法,告诉你应该采取什么样的最小行动来纠正问题。好吧,我希望我说服了你,手头有一个与你的错误数据相关的 CF 示例是快速解决你的问题的关键,并且,潜在地,是一笔巨大的金钱节省(认为一切都是关于金钱和时间节省的陈词滥调……)。
在这一部分中,我们展开了一个有效的算法方法来计算在树集成模型的情况下与错误数据相关的最近的 CF 例子。我会尽量保持整体的直观性,但有时我不得不挖掘数学。敏感的读者可以跳过这些部分,这并不妨碍对整体的理解,而且你仍然可以把它留到以后,当你解决了生产链上的问题,有了足够的自由支配时间的时候。
首先,我们需要输入树集合模型的特殊性。CF 示例是基于模型的特殊性(即模型参数)计算的,因此理解它们如何工作似乎是相当重要的一步。
让我们从决策树开始,它是这种模型的基础组件。更准确地说是二叉决策树:在这种树的每个节点中,我们通过将输入特性的值与单个阈值进行比较来分析它。如果特征值高于阈值,我们转到右边的分支,否则,我们转到左边的分支。我们重复这个逻辑,直到我们在与分数(对一个类的投票)相关联的树的叶子中结束。现在让我们从几何学角度来分析这意味着什么。我试着在下图中表现出来。如果你认为这个数字没用,我会在另一个帖子里把它作为数字艺术回收,但是请相信我把我所有的才华都放在里面了。

输入特征空间中的决策区域对应于决策树的叶子。这些区域是盒子/多维间隔,在某些边上可能是开放的。输入空间是二维的。在节点 Ni 中,我们指示输入数据的哪个维度 dj 被分析,以及与其比较的阈值 ci 。节点 Ni 因此与对 (dj,ci) 相关联。可以检查到,即使在树根和树叶之间的路径上对一个特征测试了两次以上,只有两次测试对于表征与树叶相关联的判定区域是有效的,其他测试是多余的。无论输入维度的数量是多少,该属性都有效。 N1 和 F1 之间的路径包含一个对特征 d2 进行冗余测试的例子:在节点 N2 中,我们测试“D22.5”,在节点 N4 中,我们测试“D21.4”,相当于只测试“D21.4”。[图片由作者提供]
从几何学上讲,决策树的叶子似乎是多维的盒子(在某些面上可以是开放的)。为了在数学上更加正确,我们会说叶子是多维区间。那么,树的组合怎么样?你自己可能很容易得出结论,它只不过是多维区间/盒子的集合,以及它们相关的类投票/分数。要预测一个输入元素,我们只需要搞清楚它属于哪些盒子(盒子之间可能会有交集),把关联的分数加起来就可以了。在数学上,如果我们将一个树集成模型 F 表示为一对( B ,S )其中是一组盒子/叶子和 S 的关联分数的集合,那么与 F 关联的预测函数就是这样简单:

树集合模型预测函数。n 是模型的叶子数。Bn 表示第 n 片叶子,Sn 表示与该叶子相关联的分数。Sn 是 K 维向量,其中 K 是与分类问题相关联的类的数量。它通常是一个稀疏向量,只为单个类投票(即只有一个非空系数)。为了知道与输入点 X 相关联的模型的决策,我们计算“arg max(F(X))”。如果 X 属于叶 Bn,则δBn(X)=1,否则δBn(X)=0。[图片由作者提供]
您可能认为像 XGBoost 这样的模型可能会产生更复杂的决策函数,但事实上不是这样……对于二进制分类,它会产生一个分数(在 0 到 1 之间),一旦与阈值(默认为 0.5)进行比较,就会告诉您数据属于哪个类别。我将发表另一篇文章,介绍如何将这篇文章中描述的可解释性方法应用于多类分类和回归的树集合模型,因此,请遵循我的渠道(为了你好,这没有任何商业成分,科学是我唯一的燃料)。
在这一点上,我们将尝试基于模型的决策区域的几何分解来确定我们的 CF 示例。这就是事情变得棘手的地方,因为我们不能直接使用我上面提到的分解。下面的例子应该足以让你相信我们做不到,这个世界比它看起来更糟糕。

几个叶子/盒子的交叉使得出现新的区域,其分数是通过将交叉形成这些区域的各个盒子的分数相加来确定的。例如,绿色区域的分数通过计算 S1 + S2 + S3 来确定,红色区域的分数通过计算 S1+S3 来确定。[图片由作者提供]
在上图中,三个方框 B1、B2 和 B3 的交叉点出现了两个新的决策区域(以红色和绿色突出显示)。我们无法确定这些区域属于哪一类,除非我们将相交形成这些区域的盒子的分数相加。你猜怎么着?这就是我们的工作。我们对原始分解进行“超级分解”,确定所有相交区域以及其他区域。出于算法的原因,我们计算一个“超级分解”,它也是盒子的集合。所以,它看起来会像这样:

相互交叉的三个盒子的初始集合的超级分解。超级分解也采用盒子集合的形式。后者不一定是能找到的最简单的盒状分解(就盒数而言)。[图片由作者提供]
这个问题的所有困难是设计一种算法方法来计算一个盒状的超分解,它避开了问题的潜在组合。实际上,以强力方式表述,该问题相当于确定,对于任何一组“k”个盒子,这些“k”个盒子是否形成最大相交区域,即它们的相交区域是否不为空,以及在“k”个盒子之外是否没有其它盒子被认为与该区域相交。这个问题是组合性的,即使对于中等数量的盒子也是不容易解决的。我们提出了一种逐维构建相交盒的算法。我们使用边垂直于坐标轴的盒子的一个便利的特性:如果两个盒子没有根据一个特定的维度相交,它们根本没有相交。下图说明了这个想法。

如果两个盒子没有根据一个特定的维度相交,则它们根本没有相交。在左图中,两个框根据尺寸 d1 相交,但不根据尺寸 d2 相交。在右图中,两个框根据尺寸 d2 相交,但不根据尺寸 d1 相交。[图片由作者提供]
这个属性产生了一个树状的层次结构。每个级别对应于输入空间的一个维度。在每个节点中,我们根据对应于树状结构的当前级别的维度来计算存储在该节点中的盒子的最大交集。如果两个框没有根据该维度相交,则它们将不再一起出现在树的下一层的相同节点中,因为根据上述属性,它们没有机会一起形成相交区域。
将我作为数字艺术创作者的惊人天赋付诸行动,我在下面尝试以图形方式“运行”该算法,已经达到了文字所能提供的极限,并且不想用一个核心的算法证明来窒息你。该算法主要是关于前面提到的树状结构的构造。该结构在其叶子中包含由树集合模型的叶子形成的交叉区域的盒状超分解。

由盒分解算法构建的树状探索结构。我们在此考虑二维特征空间中具有两片叶子(F1,F2)的模型。该算法逐维进行。第一级对应于维度 d1,并且对应于根据维度 d1 分解成模型叶的最大相交区间。在这一级之后,我们以三个独立的叶子子集结束,我们对其应用相同的 1D 分解过程,但是,这一次,根据维度 d2。阴影区域表示由算法产生的结果最大交集框。[图片由作者提供]
在树状结构的每个节点中,我们总是解决相同的交集问题:给定一列盒子和与该节点相关联的输入空间的维度,我们寻找该维度中盒子的最大交集,即我们解决前面提到的组合问题,但是是在一个维度中。因此,我们不再处理盒子,而是处理 1D 区间,它是盒子在所考虑的维度中的投影。幸运的是,在 1D,问题不再是组合的,甚至在考虑区间的数量上变成线性的。这个想法是将所有的区间放在一个 1D 轴上,如下图所示。

从 1D 区间的集合中寻找 1D 最大交集的区域(区间)。如果一个区域对应于 k 个区间的交集的区域,则称该区域为最大交集,其中 k 是在该区域中相交的区间的最大数量。为了计算这些区域,我们将所有区间放置在 1D 轴上,并且每当区间开始或结束时,我们创建一个新的最大相交区域。在上面的例子中,从区间{I1,I2,I3}的集合中,我们提取了五个最大相交区域 Z1,Z2,Z3,Z4 和 Z5。[图片由作者提供]
然后,我们注意到,除了终止最后一个最大交集区域的最后一个区间结束之外,每次区间开始或结束时,都会开始一个新的最大交集 1D 区域(这是一个 1D 区间)。因此,如果我们考虑 N 个区间的初始集合,我们将最多有 2 个。N-1 个最大相交区域。从算法上来说,我们必须对所有区间的开始和结束进行排序,这相当于排序 2。n 分。实际上,在超分解算法中,通过在每个维度上分别预分类构成树集成模型的盒子,并且通过观察使用屏蔽操作从有序集合中提取子集产生有序子集,可以跳过该操作。因此,我们可以考虑节点中盒子的子集,而不必根据与节点相关联的维度再次对它们进行排序。因此,与问题的初始复杂性相比,我们最终在树状结构的每个节点中进行的处理惊人地便宜。
在这一点上,你可能已经预料到超级分解算法可能会产生如此多的决策区域,以至于它甚至不适合你的计算机内存。这就是残酷的现实。因此,我们现在将寻找优化,仍然允许找到确切的 CF 示例,但限制实际建立的区域的数量。主要思想是,我们不需要构建模型的整个决策区域,只需要围绕查询点构建一些东西,这样就可以从其他类中找到一个例子。因此,我们想要限制精确 CF 示例所在的搜索区域的大小。作为第一次尝试,我们可以使用训练数据来这样做:给定一个查询点,我们在训练集中寻找被模型分类为正常的最接近的数据(注意,我说的是“分类的”,而不是“标记的”)。这为搜索区域的大小提供了第一个相当可靠的上限。使用这个,我们将只建立位于这个上限内的搜索区域。这很方便,因为用于建立决策区域的算法是逐维进行的。因此,如果对于给定的维度,部分构建的决策区域已经比上限更远,我们可以停止在搜索树的相应节点中的探索。这种方法的通用名是“分支-绑定”,在需要沿着树状结构的分支进行探索的所有场景中特别有用(这显然是我们的情况)。
我希望你还在跟随我进行这一危险的分支定界的探索。还有许多其他实用的优化方法,其中最有效的是以深度优先的方式探索搜索结构。这很快为我们提供了一个比单独使用训练集计算的上界更好的上界。在实践中,我们使用多线程并行维护多个深度优先探索,以保持其有效性。每当我们发现一个比最后一个紧的上界更紧的上界时,我们就更新最紧的上界的值,并将新的上界传递给所有线程以供立即使用。这最终导致搜索树的快速修剪,并允许在眨眼之间彻底探索模型的决策区域(有时更多,取决于你眨眼的速度)。
最后一点,维度在某些时候对你有利(能够写这个很少见,所以我几乎可以称这个时刻为历史性的,如果我不打字,你会看到我的笔迹颤抖)。简单地说,添加的维度越多,部分构建的框到查询点的距离超过上限的几率就越高。这个简单的效果会使您通过搜索树创建的区域数量保持稳定,甚至有时会在达到一定数量的维度后(略微)减少。好吧,让我们不要对此过于狂热,最好添加好的优化,祈祷快速的结果。
所以,现在我展开了资金原则,让我们把我们的手放在代码中。出于我极大的善意,也为了你美丽的眼睛,我用一段很好的 C++程序编写了上面所有的优化(甚至更多),我用 R 封装了它(也许在不久的将来,我还会写一个 python 包装器,这样它就可以和你喜欢的高级编程语言一起使用了)。我将在另一篇博文中向您展示如何做好简单的 Rcpp 包装,以至于您可能会考虑放弃 python。
R 包可以在我的 github 上找到。它需要“ Boost 和“TBB”c++库。您必须检查这些库的路径是否在位于。/src "文件夹,或者它们可以在标准系统路径中找到。要将软件包安装到 INSTALL_PATH 文件夹中,请从终端使用以下命令(仅限 linux 用户):
*cd PACKAGE_SOURCE_FOLDER
R CMD INSTALL -l /INSTALL_PATH ./ --preclean*
一旦你(成功地)安装了 R 包,让我们从用例开始。我选择保持相对简短的描述,以避免 github 的可怕冗余。如果您感兴趣,请注意代码仍在开发中,等待外部贡献者将它制作成一个大尺寸树集合模型的 CF 可解释性的伟大包,有一天它可能会在 XGBoost 的代码中占有一席之地(是的,作者是一个梦想家……)。
作为第一个例子,让我们考虑一个用于消费信贷批准/拒绝的数据集。与我在博客文章开头宣传的工业故障检测场景完全不同,但是 CF 可解释性方法可以扩展到任何分类场景,包括正常类(信用批准)和“异常”类(信用拒绝)。此外,小数据集很好地展示了一些东西,学术界已经使用它们几十年了(这是一个可靠的论点吗?).该数据集允许学习 20 个输入特征之间的映射,例如正在进行的信贷数量、收入、年龄、信贷目的…以及授予或不授予信贷的决定。
我省略了 XGBoost 模型的所有特性格式和训练细节,您可以很容易地从演示脚本中对它们进行逆向工程。让我们直接跳到 CF 示例计算。我们首先需要选择测试数据中与信用拒绝相对应的一点。我们检查它是否被归类并标记为“信用拒绝”。为了保证以后,我们简单地看看地面真相。就两类准确率而言,经过训练的模型确实不会高于 75%,因此会有许多误报(意味着被归类为“信用拒绝”的点,而这在现实中是不存在的)。给定一个我们称之为“查询点”的选定点,我们计算其相关的最近 CF 示例。下面,我解释用于确定 CF 示例的函数中每个参数的含义。我觉得有必要在这里做,因为它在代码中缺乏解释。
负责计算 CF 示例的主方法的签名。
下表显示了三个人的一些结果。每次提到查询点(客户端状态),以及关联的 CF 例子(推荐+限制推荐)。当查询点和 CF 示例之间没有变化时,该单元格留空。对于每个人,我们计算两个 CF 示例。第一个(行“建议”)对应于“完全行动能力”情景,即个人可以控制/改变与自己有关的所有特征。第二种情况对应于“部分可行动性”情景,即个人只能控制/改变少数变量。例如,认为个人无法控制自己的年龄是更现实的,尽管对再生乳液的研究已经取得了很大进展。

对于三个不同的个人,我们诊断了银行拒绝他们所要求的信贷的决定,并且我们展示了 CF 方法所提供的建议,即他们需要改变一个最小值以使信贷被授予他们。对于每个个体,我们发出两个建议:第一个认为个体有控制/改变所有特征的可能性(“建议”线)。第二种认为个人只能控制有限数量的特征(“限制推荐”线),这通常更现实(例如,个人不能对自己的年龄或婚姻状况做出任何改变)。无论如何,我真诚地希望这将帮助你获得 100 美元的信贷,你需要购买布加迪赛车你的梦想。[图片由作者提供]
第二个用例涉及 MNIST 数据集的误分类数字。我们从十个类别中提取两个类别,它们本质上是不明确的(例如 1 和 7,或者 5 和 6)。然后,我们训练一个两类分类 XGBoost 模型,该模型学习区分这两个类。作为查询点,我们选择属于一个类的一个点,该点被模型误分类到另一个类中。我们还确保输入的数字在视觉上是模糊的(意味着人眼不能真正判断它属于哪个类别)。这最后一点特别重要,因为我们的目标不是检测分类模型的潜在弱点,而是检测输入数据中异常值的存在。因此,我们需要能够认为输入数据在某些方面是错误的,并且分类器只是做它的工作。只有在这些假设下,我们才能在误分类和错误数据之间画出一条平行线,并像对待错误数据一样对误分类的例子应用我们的 CF 方法。
在这个用例中,我们引入了另一个方面:合理性的概念。为了使 CF 示例更可信(即,视觉上更有说服力),我们可以在与模型对 CF 示例的预测相对应的决策得分上添加约束。通过迫使模型以更大的置信度预测正确的类,我们获得看起来越来越接近正确的类的元素的 CF 示例(“正确的类”,我是指与基本事实中的查询点相关联的类)。强制似然性以失真为代价,这意味着较高的似然性导致 CF 示例在欧几里德距离方面离查询点更远。下图说明了这个想法。

考虑两个二进制分类问题(5 对 6 和 4 对 9)。第一行:一个“5”失误的例子——被模型归类为“6”。初始查询图像显示在左侧。我们使判定阈值在 0.5 和 0.2 之间变化。获得被模型更有把握地分类到正确类别的 CF 示例是以引入到初始查询数据的失真为代价的,即,判决阈值“eps”越低,到初始查询“dCF”的距离越高。从视觉上看,我们看到 CF 方法提供了对初始查询数据的合理更改,使其看起来更像“5”。第二行:一个“4”失误的例子——被模型归类为“9”。[图片由作者提供]
也可以实施其他似真性标准:例如,我们可以检查找到的 CF 例子位于包含来自训练数据集的至少一个元素的最大交集区域中。这将避免挑选出在现实生活中不现实/不可及的非分布 CF 示例。
最后一点,在我让你吃你的没有缺陷的晶片之前,并不强制考虑一个查询点和它对应的 CF 例子之间的欧几里德距离。也可以考虑其他距离,而不需要改变分解算法,只要这些距离可以被公式化为坐标相加,即被计算为:“ d (X,Y) = g ( ∑ di (X[i],Y[i])”,其中“ di ”是应用于第 I 个坐标的特定运算,“ g ”是递增的单调函数。比如: di (X[i],Y[i]) = (Y[i]- X[i])对于平方的欧氏距离。
好吧,这篇很长的博客文章要记住什么(抱歉,我可能在第一次尝试与世界交流时过于啰嗦)。首先,树集成模型允许精确 CF 示例的计算,同时是用于故障检测的更好的模型(尤其是梯度增强树)。第二,除了定位故障/异常之外,CF 示例给出了纠正故障/异常的最小行动的精确概念。此外,这里提供的 CF 方法具有产生稀疏解释的巨大优势,即,在数量减少的输入特征上建议改变,这使得解释对于人类用户来说更容易理解。
如果你对这个话题感兴趣,并且想知道更多关于这个方法的可能性,你可以在这里阅读全文,我恰好是这篇文章的作者(但这纯粹是随机巧合)。如果你想在你的应用程序中使用这个包,请引用:一个精确的基于反事实例子的树集合模型可解释性方法
在下一篇文章中,我将向您展示 CF 解释对回归问题的扩展,并教您(如果您允许的话)如何部署 CF 推理以实现利润最大化,或者至少,如何仅仅通过改变厨房地毯的颜色就使您的房屋销售价格攀升 10 000 美元。好吧,我说得太多了(或者说还不够),但是如果这第一篇帖子成功了,我会把完整的配方交付给你:)。
为非数学家解释量子电路的数学
你是否因为所有的数学知识而难以理解量子电路?
量子机器学习要不要入门?看看 动手量子机器学习用 Python 。
在以前的帖子中,我声称你不需要成为数学家或物理学家才能理解量子计算。
然而,如果你既不是数学家也不是物理学家,学习量子算法有时会很难。它具有挑战性,因为它广泛使用符号。符号的问题在于,你不能轻易地谷歌它们。例如,考虑下面的电路。

作者图片
它代表了一种你在学习量子算法时会经常遇到的结构。但是如果你不知道这些术语的意思,你怎么知道呢?
以|0⟩和|1⟩为例。只是为了好玩:谷歌一下。
𝐻⊗𝑛怎么样?你知道如何在谷歌中输入这个词吗?(即使是中等文本编辑器也不支持正确书写此术语。)我的意思是,如果你得到的只是一张图片,那么从图片上复制文字都不容易。
如果你不是数学家,这些符号和象形文字一样好。

作者图片
当然,你不需要成为天才才能理解量子电路。只要你比克里特斯聪明一点,你就会没事的。

作者图片
那么,让我们来解释一下量子电路。量子计算不是偶然用数学符号来描述的。量子电路是一种数学构造。这是一个等式。而且,毫无疑问,数学是一种简洁而精确的语言。有了数学,你可以非常简洁地描述算法。
不幸的是,对我们大多数人来说,数学充其量是第二语言。
让我们从最左边开始。在那里,我们找到了电路的输入。|0⟩和|1⟩表示量子位元最初的状态。当我们在|0⟩态测量一个量子位时,它是 0。当我们在|1⟩态测量一个量子比特时,它是 1。这类似于传统的钻头。
那么,我们为什么不直接写 0 和 1 呢?为什么我们要把它们放在这么奇怪的框架里?
原因是一个量子位不仅仅是 0 或 1。它是 0 和 1 的线性组合,除非你测量它。一旦你测量它,它立刻变成 0 或 1。我们用向量来描述这种线性关系。所以|0⟩和|1⟩表示两个向量。它们是我们在量子计算中使用的标准基向量,用狄拉克符号书写。向量是有长度和方向的几何对象。狄拉克符号(也称为 bra-ket)在量子力学领域很流行。但这并不新奇。
我们可以简单地说

我不得不承认。这两个方程只是将向量从一种符号转换成另一种符号。所以,它仍然使用数学和符号。但这是大多数人在高中学习的符号。如果你对向量一点都不熟悉,我给你谷歌了足够多的关键词。
但是在你去 Google now 之前,我们为什么不图形化地看一下这两个向量呢?

作者图片
向量中的每个数字代表一个维度。所以,我们的向量是二维的。
在此表示中,两个维度都位于垂直轴上,但方向相反。因此,系统的顶部和底部分别对应于标准基矢量|0⟩和|1⟩。
在图中,有另一个称为“psi”的向量——|𝜓⟩.
|𝜓⟩代表任意量子位状态。我们上面提到过,一个量子位是 0 和 1 的线性关系。因此,

𝛼和𝛽是该矢量到标准基矢量|0⟩和|1⟩.的距离如果我们将它们平方,我们得到测量量子位为 0 (=𝛼)或为 1 (=𝛽)的概率。
举个例子,如果我们说𝛼=1 和𝛽=0,那么|𝜓⟩=1⋅|0⟩+0⋅|1⟩=|0⟩.对于𝛼=1 来说,测量量子位为 0 的概率是 1 =1=100%。
仅仅说我们测量一个量子位为 0,另一个量子位为 1,这就需要很多数学,不是吗?
但是,我们还没有完成初始化。你可能已经注意到在|0⟩^𝑛.旁边有一个小小的上标𝑛通常,这表示一个指数。这里,它说我们在|0⟩态没有一个量子比特,但是在这个态有𝑛量子比特。我们也可以把它写成|00⋯0⟩和𝑛0。
所以,我们的电路包含了 n 个量子位,它们被初始化为一个我们测量为 0 的状态,还有一个量子位被测量为 1。
在量子位的初始化旁边,我们看到了术语𝐻⊗𝑛和𝐻.先说后者。在量子计算中,大写字母通常代表矩阵。矩阵𝐻代表哈达玛矩阵,其定义为

重要的是要知道,如果你用一个向量乘一个矩阵会发生什么(假设矩阵的列数等于向量的行数)。然后,结果是另一个向量。

例如,如果我们将哈达玛矩阵乘以状态向量|0⟩或|1⟩,那么我们得到另一个向量。

和

两个矢量的𝛼和𝛽值相等,如果你平方它们。唯一的区别是𝐻乘以|1⟩.时𝛽的符号通常,标志也是这种状态的简称。
在量子计算中,|+⟩是

|−⟩是

当你计算这些值的平方来分别计算量子位的概率为 0 或 1 时,你会得到 1/2。
因此,𝐻矩阵有效地转换了量子位的状态。我们不再确定地测量量子位为 0 或 1。但是当我们测量的时候,可能是 0,也可能是 1——各有 50%的概率。
也许,你已经开始怀疑𝐻⊗𝑛的意思了。|0⟩^𝑛代表|0⟩.的𝑛量子位如果我们把这些量子位看作一个单一的向量,那么这个向量就不仅仅只有二维,而是 2⋅𝑛维。每个量子位有两个维度。
只有当一个矩阵和一个向量有相同的维数时,我们才能把它们相乘。这就是𝐻⊗𝑛所关心的。
让我们看看𝐻⊗2=𝐻⊗𝐻.这种两个矩阵相乘的方式叫做张量积。我们可以把它写成

它创造了一个更大的矩阵。更重要的是,它是递归工作的。

我饶你在这里扩展𝐻⊗2。总的来说,是的

这个公式被称为克罗内克积。这是数学上的说法:
对每个 𝑛 量子位应用相同的变换矩阵(这里是 𝐻 )。
电路的下一个术语叫做𝑈𝑓.它代表一个酉矩阵。酉矩阵是它自己的共轭转置矩阵。这听起来很复杂。但简单来说,矩阵翻转了对角线。自身是共轭转置的矩阵具有有趣的特征。例如,当你把这样一个矩阵本身相乘时,结果就是单位矩阵。𝑈是一个矩阵,𝑈†是它的共轭转置。然后,𝑈†𝑈=𝑈𝑈†=𝐼.
我们知道矩阵可以改变量子位的状态。但是,单位矩阵不会改变它。所以,我们可以说𝐼|𝜓⟩=|𝜓⟩.
所以,当你把你的量子位状态乘以同一个幺正态两次,它会再次得到原来的量子位状态。或者,换句话说,转换会自行恢复。
酉矩阵的另一个重要特征是它保留了测量概率。这是当前电路中的重要一点。
记住,所有的量子位在进入𝑈𝑓变换之前都处于 0 和 1 概率相等的状态。所以,如果一个酉矩阵不改变概率,它们仍然是相等的。
如果这个矩阵不会改变概率,我们为什么还要应用它呢?
答案就在|+⟩和|−⟩之间。如上所述,两种状态下的量子位具有相同的测量概率。
但是我们已经知道的哈达玛门有一个有趣的效应。它不仅把一个量子位从|0⟩变成了|+⟩,从|1⟩变成了|−⟩,还把|+⟩变回了|0⟩,把|−⟩变回了|1⟩.它会恢复原状。
这就是最终的𝐻⊗𝑛所做的。关键是𝑈𝑓是我们要解决的某个问题的占位符。
它只是将一些量子位从|+⟩翻转到|−⟩.它翻转了问题解中必须为 1 的那些。而且,它没有触及|+⟩那些需要在解决方案中为 0 的部分。
结论
在这篇文章中,我们揭示了量子电路背后的数学原理。我们学习了量子电路的数学以及它的含义。
原来小小的数学符号里有很多含义。如果你都知道,数学是精确描述电路的简洁方法。但是如果这些你都不知道,单单数学作为解释是不够的。它没有强调重要的事情,也没有提供你可以用来做自己研究的线索(比如关键词)。
手边的电路代表了著名量子算法的结构,例如 Deutsch-Jozsa 算法和 Bernstein-Vazirani 算法。
两种算法的区别在于𝑈𝑓.的实现
𝑈𝑓矩阵将这些量子位从|+⟩翻转到|−⟩,我们想测量为 1。并且这些形成了算法旨在解决的问题的解决方案。
在这篇帖子中,你可以了解更多关于用友和 Deutsch-Jozsa 算法的细节。
你对量子计算了解得越多,你对数学的理解也就越多。但是你也会看到量子计算不全是数学。然后,你会奇怪为什么没人费心用非数学的方式解释事情。
数学应该是解释的补充。这样,读者就更容易理解这些公式。如果她在看到一个公式时已经理解了这个概念,那么所有这些符号更有可能是有意义的。
量子机器学习要不要入门?看看 动手量子机器学习用 Python 。

免费获取前三章这里。
自我学习、进化的神经网络的解释
观察并理解算法是如何学习、变异和进化的

就像在塞伦盖蒂草原上,我们的算法是适者生存(图片由陈虎在 unsplash 上提供)
在这篇文章中,我们将通过一个自我学习,基于进化的遗传算法的应用来增强它自己的拓扑结构。迷茫?我可以想象;这是一些大词。不过,请继续关注我,我保证在本文的结尾,您会看到这个算法的美妙之处。
为了尽可能清楚地理解神经进化算法,我编写了一个名为 Dots 的小游戏,它将展示算法的工作方式。我试图用外行的术语描述发生的事情,而不是专注于编程或数学。目标是让你了解神经网络的一般工作原理,并看到它们的可能性和应用。
我们正在使用的算法被 Ken Stanley 称为 NEAT(扩充拓扑的神经进化)。读完这篇文章后,你会对它的工作原理和好处有一个大致的了解。我们将通过以下步骤来实现这一点:
- 游戏的描述和目标
- 圆点;输入和输出
- 演变的点
- 将所有这些放在一起—快速总结
- 其他应用
1.设置和游戏目标
前往mike-huls.github.io/neat_dots并按下右上角的绿色大播放按钮。

1.显示单个点的游戏概述(出于说明目的画出的传感器)
你首先会看到的是右下角的一堆圆圈,然后它们就像喝醉的飞机驾驶员一样向前移动。这些圆圈被称为点,它们产生了数百个。在游戏开始的时候,几百个小点产生并开始移动。如果他们撞上了黑色的边界,他们就死了。边缘的黑边是一个障碍。如果一个点碰到其中的一个,它就死了。您可以点击“添加障碍”按钮来绘制新的障碍。
一旦所有的点都死了,或者过了一段时间后,一个全新的新一代的点就会诞生。每个点的目标是达到左上角的目标。游戏的目标是进化这些点,使它们以最快、最优化的方式达到目标。让我们来看看这是如何工作的!
2.介绍我们的选手
玩几次这个游戏,尝试一些不同的障碍,看看这些点是如何反应的。现在是时候更好地了解他们了。首先我们将分析点本身;它如何解释环境,它能做什么,它如何决定这样做?

介绍我们的点(和一个漂亮的大脑;稍后将详细介绍)
行动、传感器和决策
圆点可以以两种方式移动:它们可以加速、减速和左右转向。dots 的目标是加速和转向,以达到目标;左上角的白色圆圈。
但是一个圆点如何决定何时加速或转向呢?这完全取决于点的周围环境。每个有 9 个传感器;它可以朝 8 个方向看(由图 1 中的线条表示)。此外,它可以检测是否看到目标。偏置传感器总是设置为 1;以防所有其他节点“关闭”。
所有传感器的值都在 0 和 1 之间。越靠近障碍物,该值越大,为 1。如果没有检测到障碍物,传感器为 0。seesGoal 传感器的工作方式略有不同;不是 0 就是 1。
动作的值介于-1 和 1 之间。负值将减速或转向左侧,正值将加速或转向右侧。
连接这些点;创造一个大脑
我们的目标是让 dot 作用于它的传感器;它为了生存和触及目标而解读它的环境。圆点必须知道,如果它在那里探测到一个障碍物,它不应该转向左边。每个点都有一个大脑,使得一个点完全有可能做到这一点。把大脑想象成传感器连接到行动的方式。这也被称为神经网络的拓扑结构(还记得这篇论文的名字吗?).您将在第一列中看到传感器节点,在第二列中看到操作节点。

2.一个点的大脑
在游戏的右上角,你可以看到最成功的圆点的大脑。你可以在图 2 中看到一个特写。由于游戏刚刚开始,这个大脑相当简单,但它可以复杂得多:

3.更复杂的大脑
dot 不仅创建了更多的连接,还创建了一些额外的节点。这样它可以一次解释多个传感器。例如,它可以检测传感器 1 和 2 中的障碍物,然后将该观察结果与传感器 0 中的障碍物相结合,并让该结果影响转向。
创建额外的节点和边是非常特殊的;在大多数神经算法中,你必须事先定义你的拓扑(大脑)。它假设您知道需要连接哪些节点。NEAT 的美妙之处在于它从最简单的拓扑开始;只有输入(传感器)和输出(动作),然后建立自己的拓扑结构。这保证了一个最小的网络结构,通常真的很酷。稍后我们将讨论算法如何演化拓扑。
在图 2 中,我们看到传感器 6 通过一条红色粗线连接到方向盘。红色表示连接是负的。厚度表示连接的重量高;传感器 6 对转向有很大的负面影响。换句话说:当这个点探测到它左边的东西时,它会转向左边,直接撞上障碍物。这有点傻,但没关系,我们的小点还在学习。
3.演变的点
我们使用的神经网络的美妙之处在于,我们没有预先定义任何东西。这个点并没有被设定远离墙壁,它甚至一点也不知道墙壁是如此致命。然而,这个点被编程为学习。在这一节中,我们将深入探讨 dot 是如何学习的,它是如何分享其知识的,以及这些知识是如何传递给下一代的。一般顺序如下:
- 计算点适合度
- 形成物种
- 交叉
- 使突变

我们正在进化我们的点(图片由尤金·日夫奇克在 Unsplash 上拍摄)
适者生存
当一个点产生时,它的传感器和动作之间没有多少联系。将创建一个随机连接,并赋予一个随机权重,由其厚度表示。在上面的图像中,这个点会自己撞到墙上。交通部不知道这对他来说是否是个好决定;它不知道自己是否在做正确的事情。为了学习,必须有某种对他有益的衡量标准。这就是健身的用武之地。
你可能认识达尔文的这个术语。健康反映了你对周围环境的适应程度。把它想象成一只狗,如果它做了好事,它会得到一份奖励;我们在强化“好”的行为。在游戏中,我们可以在模型信息部分看到最佳表现点的适合度。

点模型信息
我们在代码中定义一个点什么时候做得好。在这个特殊的例子中,我将适应性定义如下:
- 如果 dot 还没有看到目标,它需要进入探索模式:健身是 0 开始,并增加它覆盖的距离。
- 如果这个点已经看到了目标,这意味着这个点已经到达了它可以到达的位置。首先,dot 会因此获得奖金;那么它的适应度就由它死于目标的距离来定义;越短越好
- 如果小点达到了目标,它将再次获得奖励。健康的定义是达到目标所需的最短步数;确保我们尽快到达那里。
太好了!当一个点表现良好时,我们已经进行了编码。有了这些信息,我们就可以执行这个算法中最重要的部分:物种形成。
物种形成
现在我们有了比较点的方法,我们可以揭示为什么我们同时产生数百个点:它们将进行实验,分享知识和进化。它是这样工作的:在每个点完成它的工作后;四处狂奔,随意撞墙,是时候看看谁最厉害了。首先我们要根据他们的大脑来比较所有的点。非常相似的点将被归为一个物种。
你可以在游戏中识别物种;有时你会清楚地看到一组点在做着与其他点完全不同的事情。也许他们走向北的路线,而另一组绕过障碍物向南走。这是因为他们的大脑明显不同。
我们将根据每个物种的适合度排列它们内部的点,然后杀死每个物种的下半部分。这听起来可能有点残酷,但你是完全正确的。但是就像在自然界一样;只有适者生存。还要注意的是,圆点只需要在它们的物种内部竞争;这是为了保护创新。新发展的 dots 可能在开始时表现不佳,需要几代人才能真正发挥作用。如果它们在发挥出真正的潜力之前就灭绝了,那就太可悲了。
最后,是时候消灭过时的物种了。我们可以通过检查物种中表现最好的点的适合度分数来检测这一点。如果几代人都没有改进,它就变得陈旧了,是时候淘汰了。

交叉
在最后几段中,我们已经删除了许多坏点。让我们用我们最好的点来繁殖后代,把那些聪明的头脑传递下去。在每个物种中,随机选择成对的亲本。具有较高适合度分数的点具有较高的变化以被选择。每一对将结合他们最好的特点创造一个孩子。他们后代的大脑将是他们父母大脑的结合体。在这一步之后,我们的人口将与这一代人开始时一样多。
使突变
新一代开始前的最后一步是突变。为了保持创新,大脑中的每个点都会随机变异。这意味着每个点要么得到一个新的节点,要么得到一个新的连接。
这一步在所有的点上引入了一些随机性。在某些情况下,这将是有益的,其他点将遭受他们的新大脑。谢天谢地,多亏了物种形成,这个点将有一些时间来“调整”它的大脑。
再做一次
我们的新一代已经准备好迎接下一轮挑战。所有的点将再次产生,希望导航与多一点成功,并最终达到目标或死亡。然后他们的适应度被再次计算,所有的点都被特化、剔除、繁殖和变异,我们为下一个周期做好了准备。每一代都会比上一代好一点,最终目标会实现。
4.把所有的放在一起
我认为 NEAT 非常特别,因为它要求你事先定义很少的东西。在许多统计技术中,有许多要求。在许多遗传算法中,我们需要预先定义我们的拓扑结构(点脑)。NEAT 从一个最小的拓扑开始,只是连接输入和输出,然后慢慢增加复杂性以尽可能保持简单。此外,它通过使用物种来保护创新;圆点只需要在它们的物种内部竞争。此外,它培育出表现最好的点,结合了父母双方的最佳特征。
与其他统计技术相比,NEAT 的缺点是它生成的模型非常复杂。当我们使用线性回归时,我们可以提取一个简单的公式,一个整洁的大脑可能会非常复杂,很难用一个简单的公式来把握。当我们可以使用线性回归来确定变量之间是否存在线性关系时,这种关系可能很难看出。
5.其他应用
在点的情况下,我们将点传感器连接到它可以执行的一些动作。这在现实世界中没有太大的价值,但谢天谢地,NEAT 并不局限于通过障碍迷宫导航笨拙的圆圈。在这一节中,我们将研究一个假设的例子,如何在现实世界中应用 NEAT。

经纪人机器人将决定你的房价(图片由亚历山大·奈特在 Unsplash 上提供)
决定房价
您有一个包含房屋特征和销售价格的数据集。我们将应用 NEAT 来创建一个大脑,它可以根据特征最准确地确定销售价格。
一个点有 9 个传感器:8 个方向的视线和是否能看到目标。例如,我们的房价大脑将拥有房屋表面、土地表面、卧室数量、建筑年份、拥有车道和停车位数量。例如,我们必须在 0 到 1 之间调整所有这些值,其中 0 是最小建筑年份,1 是最大建筑年份。我们的输出将是房价。房价也会是 0 到 1 之间的数字;在我们的训练数据中,它映射在最低和最高房价之间。
就像我们产生了几百个点一样,现在我们将产生几百个“宅脑”,并从我们的训练集中给他们所有人一些数据记录。数据流经大脑计算房价。我们将把这个与实际房价进行比较。区别在于适合度;越小越好。然后,简洁的算法再次发挥作用。大脑被评分、分类、筛选、特化、繁殖和变异,之后我们会再次向它们输入数据。
大脑会改变它们的拓扑结构并调整它们的权重。经过多次迭代后,我们可以将表现最好的大脑输入一些新数据,以便预测房价。
结论
当我第一次读这篇整洁的论文时,我爱上了这个算法,因为它非常像自然。我们谈论的是物种、适应度、基因组、杂交、父母和后代。对我来说,我们能够对这种自然行为进行编程是非常令人着迷的。我真的很喜欢这个算法的灵活性,优雅和效率。
我希望这篇文章是对这个伟大算法的介绍,并且足够清晰地向您展示它的美妙之处。如果你有建议/澄清,请评论,以便我可以改进这篇文章。同时,查看我的其他关于各种编程相关主题的文章。编码快乐!
—迈克
页(page 的缩写)学生:比如我正在做的事情?跟我来!
通过稳健性分析说明和特性的重要性
一种更精确、更可靠的方式来解释你的预测
本文基于 Hsieh 等人在 ICLR 2021 上发表的评估和通过稳健性分析进行解释的方法。
一组重要的特性意味着什么?
目前有许多不同的算法来为神经网络分类器预测提供解释。我们希望这些算法能够区分重要特征和不重要特征。如果小扰动可以改变预测,则一组特征是重要的,而如果需要大扰动来改变预测,则一组特征是不重要的。
例如,考虑一个极其简单的分类问题,它有两个预测类(下面是红色和蓝色)和两个特征( x 和 y ):

特性 y 更重要,因为一个小的扰动就能轻易改变等级:

虽然 x 不那么重要,因为需要更大的扰动来改变类别:

在 MNIST 数据集上,人们可以看到重要的要素是那些可以通过添加或删除数字线来更容易地更改预测的要素。

对于给定的分类器 f ,输入 x 和特征集 S ,我们定义了“鲁棒性-S”(表示为 g(f,x,S)) ,其量化了对 S 上 x 的最小扰动δ(δ在 S 外等于零),该扰动改变了预测:

如果我们把 S_r 表示为重要特征的集合,把 \not{S_r} 表示为它的补集,那么必然是这样的:g(f,x,S_r) 小而 g(f,x,\not{S_r}) 大。评估和通过鲁棒性分析进行解释的方法的作者设计了一种算法“Greedy-AS ”,可以为 S_r 找到一个很好的候选。他们的实现可在 Github 上获得。我们首先显示结果(下一节),然后更详细地介绍算法。
结果
对于给特征赋予重要性值的任何算法 f 以及对于任何数量的特征 k ,可以计算由 f 标识的前 k 特征定义的 S 的 g 函数。通过改变 k ,可以为 f 计算 g 函数的 AUC(下表中称为鲁棒性)。
这样,人们可以通过算法的鲁棒性来比较不同的算法:

可以看出,Greedy-AS 能够比当前的 SOTA 算法更精确地识别特征,如 IG(集成梯度)或 EG(预期梯度),这两种算法都在 Captum(py torch 的可解释库)中实现。
让我们想象一下所提供的不同解释(点击放大):


贪婪算法
为了计算 S_r ,第一个简单的方法是以贪婪的方式来做:从空集 S=∅ 开始,然后迭代地选择最大化 g(f,x,s∩{ I })的最佳特征 i ,当你选择 k 个特征时停止。
然而,这种方法不能很好地处理功能的交互,因为功能的子集在同时添加时可能是重要的,而在逐个添加时可能是不重要的。为了处理这个问题,我们最大化期望 g(f,x,S∩{ I }∪R)其中 R 是期望所基于的一组随机特征。如果 i 与其他特征相关,当 R 包含时 g 的值将会很大,因此期望值会更大。
这篇中型文章旨在快速简单地介绍这种算法。请随意阅读评估和通过鲁棒性分析进行解释的方法,以获得更多详细信息。
非线性核函数的显式特征映射
思想和理论
这一切都是为了找到合适的空间
在高层次上,许多机器学习算法可以被认为由以下步骤组成。
- 给我们一个输入数据集,其中的例子由 n 维特征向量描述。在有监督的问题中,我们也有一个目标标签。
- 使用一些函数将特征向量映射到新的 m 维空间。(通常情况下, m 大于 n 。)

3.在新的特征空间中训练基本的机器学习模型。
比如你可以在上面的框架里想到很多深度学习的模型。通过将原始输入传递给创建新要素制图表达的多个图层来转换原始输入。然后你在新的特征空间里训练一个基本的机器学习模型。用于回归任务的多层感知器可以被视为应用于由隐藏层生成的特征的线性回归模型。类似地,通过添加 sigmoid 作为输出层的激活函数,我们在新的特征空间中训练逻辑回归分类器。
事实上,神经网络的巨大成功可以归因于这样一个事实,即映射函数是复杂的和可学习的,因此能够表示问题特定的模式。
但是在许多其他情况下,我们使用一个固定函数将原始数据特征映射到一个新域。一个标准的例子是通过考虑现有特征对的乘积来创建新特征。看看下面的例子。通过简单地将两个维度的乘积相加作为第三维度,我们获得了一个线性可分的数据集。


左边的数据集不是线性可分的。通过添加 x*y 作为第三个特征,数据明显地变成线性可分的。
核函数和支持向量机
在高层次上,核函数测量由数字向量表示的对象之间的相似性。核函数是支持向量机的基础,其目标是学习对象之间的分离超平面。核函数应该满足一定的数学要求,这些要求经过一定的简化,可以表述如下。

上面说了我们必须能够把两个向量之间的核函数改写成一个新的(高维)空间中向量之间的内积。
让我们考虑几个例子,使上面的正式定义更清楚:
- 线性内核。这里 m = n 和 f 是身份映射。

线性核的一个变体是余弦核,其中向量被归一化为具有单位范数。
- 多项式内核。

注意,对于 p=2,我们可以将向量 x 重写为一个 n 维向量。例如,设 x = (x1,x2,x3) 和 y=(y1,y2,y3) 为两个三维向量,p=2。那么对于 2 次多项式核,它成立


即,对于显式特征图,我们有一个 9 维向量。
更一般地,多项式核的显式特征映射是 n^p-dimensional.
- RBF 内核。径向基函数用于定义 RBF 核:

指数函数的使用使得显式特征图的维度是无限的。
内核技巧
评估显式特征映射的明显问题是大多数内核的映射的高维度。这导致了臭名昭著的内核技巧的发现。我们让读者参考这个关于内核技巧的精彩解释,但是在高层次上,它实现了以下内容。通过重写我们想要优化的目标函数,我们可以在高维空间中学习一个隐式定义的分离超平面,而不需要生成高维向量。分离超平面由一组向量定义,即所谓的支持向量,而不是由精确的坐标定义。当我们需要决定一个点 x 位于超平面的哪一侧时,我们针对所有支持向量y来评估核函数 k(x,y) 这要高效得多。考虑 100 维输入向量和次数 p=4 的多项式核。显式计算要素制图表达需要我们使用 10⁸矢量,即 1 亿个条目。但是用支持向量评估乘积需要两个 100 维向量的内积,然后将结果提升到 4 次方。
为了更好地了解我们可以实现的计算节省,假设我们已经检测到超平面由 1,000 个支持向量隐式定义。评估所有支持向量的核将需要总共少于 1,0001004 次乘法(最后一项 4 来自评估内积幂的乘法次数)。这是使用评估单个示例所需乘法次数的 0.004 分之一。显式特征映射。
大数据改变了这一局面
然而,在大数据时代,支持向量机并不是真正可扩展的。首先,为了计算一组支持向量,我们需要计算由输入实例的所有对 (x,y) 的核值 k(x,y) 组成的 Gram 矩阵。因此,对于我们训练数据集中的 t 个例子,我们需要一个具有 t 个条目的矩阵。即使我们决定通过仅保留最大的条目来存储 Gram 矩阵的稀疏化版本,对于大规模数据集,简单地计算所有对的核函数也是不可行的。
另一个问题是,对于大规模复杂数据集,支持向量的数量随着数据集大小线性增长[1]。因此,即使我们有能力训练一个 SVM 模型,预测时间也会非常慢,因为对于单个预测,我们需要多次评估核函数。
明确但简洁
支持向量机的上述限制已经促使研究人员考虑获得显式特征图的紧凑表示的问题。这意味着原始的 n 维向量 x,y 被新的 m 维向量 f(x),f(y) 替换,对于相当大的 m ,使得

这里的 m 是用户定义的参数。m 越大,近似值越好。因此, m 是准确性和可伸缩性之间的权衡。
请随意跳过下面对构建显式特征图的底层机制的描述。这对理解接下来的部分并不重要。
它是如何工作的?
接下来,我们考虑为两种最广泛使用的核函数(RBF 核函数和多项式核函数)构建显式特征映射的两种方法,并直观地说明它们是如何工作的:
RBF 采样器 。
使用概率论中的一些高级数学概念,我们可以获得内核的以下表示

其中 p(w) 是一个概率分布[2]。
上面的公式允许我们设计一个采样程序,根据 p(w)对向量 w 进行采样。注意括号里的表达式可以写成两个坐标的内积。大量的样本将产生一个近似的显式特征图。对于向量的集合,我们使用相同的随机种子对近似显式特征图中每个坐标的值 w 进行采样。以这种方式,我们保证样本是协调的,并且彼此相似的向量,即具有小的欧几里德距离的向量,很可能在各自的坐标中以相似的样本结束。
多项式计数草图 。
多项式 CountSketch 或 TensorSketch [3]建立在 CountSketch 算法的基础上,用于检测大规模数据流中的重要内容。我们将直观地解释这种方法是如何工作的。想象一下,给我们一个向量 x 构造 n 维显式特征图,称之为 z 。然后,每个索引 i 由散列函数随机放置在一个 m 桶中,用 B 表示,并用零初始化。此外,值 z[i] 以相等的概率乘以随机选择的-1 或 1。准确地说,我们将一个存储桶更新为

假设某些 z[i] 值比其余值大得多,即所谓的重量级人物。让z【j】这样的重量级人物。包含它的桶, B[h(j)] 还包含其他条目 z[k] 。桶中的内容是

然而,符号是随机的,如果求和包含许多小项,它们很可能会相互抵消。
现在假设我们有两个向量的显式特征图 z 和 w,使得 z[j] 和 w[j] 都很大。(最终,这就是我们在内核中寻找的东西,检测在两个输入向量中都有意义的特征子集。)我们将 z[j]w[j]* 的乘法替换为

其中 R z 和 R w 是散列到同一桶的其他条目的上述总和。因为z【j】和w【j】前面的符号相同,R z 和 R w 较小,所以我们得到了z【I】 z【j】*的一个很好的近似。
上面假设我们可以直接访问显式特征图中的条目 z[i] 。在[3]中,作者设计了一种有效的算法,该算法可以通过避免生成显式特征图来执行上述操作。它在输入向量的长度上几乎以线性时间运行。
如何使用显式特征地图
好消息是,显式特征地图已经在 scikit-learn 中实现,并且易于使用。(以下示例的代码可在 https://github.com/konstantinkutzkov/explicit_feature_maps 的找到)
首先,导入必要的包:
from sklearn import svm, datasets
from sklearn.kernel_approximation import RBFSampler
from sklearn.kernel_approximation import PolynomialCountSketch
然后,我们可以简单地将原始向量转换为新的特征表示。
poly_cs = PolynomialCountSketch(
degree=3,
gamma=1.0,
random_state=1,
n_components=1000
)X_train, y_train, X_test, y_test =
get_train_test_split(X, y, train_index)
X_train_poly, X_test_poly =
poly_cs.fit_transform(X_train), poly_ts.fit_transform(X_test)
最后,使用线性 SVM 训练模型:
model_cs =
svm.LinearSVC(C=1.0, max_iter=10000).fit(X_train_poly, y_train)
我们在由 64 维向量描述的手写数字组成的数字数据集上训练上述模型。因此,3 次多项式核的显式特征映射将导致维数为 262,144 的向量。相反,我们使用只有 1000 个条目的特征地图,少了 260 倍,并获得了基本相同的结果。以下结果是 10 次交叉验证的平均值:
Linear SVM: 0.985
RBF SVM: 0.817
Polynomial SVM: 0.998
Explicit RBF: 0.818
Explicit Polynomial: 0.997
在上面的例子中,RBF SVM 和多项式 SVM 由使用核技巧的对偶 SVM 模型训练。多项式核优于其他方法,而 RBF 核似乎不适合这个问题。
实用提示
- 如果要将相同的映射应用到不同的集合,无论是为了模型部署还是结果的可再现性,都要确保使用相同的随机种子。
- 缩放要素。如果特征没有被缩放,那么在多项式计数草图中,特征图将偏向最重的条目(参见关于该方法如何工作的讨论)。
- 考虑到显式映射的近似的较小维度用作正则化参数。
应用程序
明显的应用是使用显式特征映射来训练线性支持向量机。但是,假设他们不知道手头的问题,显式特征地图可以与任何其他机器学习模型结合使用,无论是有监督的还是无监督的。就把它想象成在一个新的子空间中训练你的模型,这个子空间是由原始空间的坐标组合而成的。
讨论
优势
- 可达性。sci kit-learn 上易于使用的公开实施。
- 可扩展性。生成显式特征地图是一种高效的数学操作,不需要训练模型。
- 很好理解。我们正在用严格理解的性质逼近数学函数。这些内核函数已经被 ML 社区使用了几十年。
缺点
如果您决定在项目中包含显式要素地图,则应记住两个重要的缺点。
- 我们还有另一个需要仔细调整的超参数。与标准版本的内核相比,我们有一个额外的超参数,它是显式特征映射的维度。如前所述,这个参数是准确性和可伸缩性之间的折衷。
- 显式特征地图不再是可解释的。原始输入中的各个维度对应于在许多情况下人类专家可以访问的特征。这使得训练诸如线性或逻辑回归、决策树等可解释的 ML 模型成为可能。特别是,使用核技巧的支持向量机或多或少是可以解释的,因为我们可以查看两个向量之间的核函数得分。然而,显式特征图是原始向量的复杂数学修改的结果,并且个体坐标对人类用户不再有意义。
密码
【https://github.com/konstantinkutzkov/explicit_feature_maps 号
[1] Thorsten Joachims:在线性时间内训练线性 SVM。KDD 2006。https://www . cs . Cornell . edu/people/TJ/publications/joachims _ 06a . pdf
[2] Ali Rahimi,Benjamin Recht:随机厨房水槽的加权和:在学习中用随机化代替最小化。NIPS 2008https://people . eecs . Berkeley . edu/~ Brecht/papers/08 . rah . rec . NIPS . pdf
[3] Ninh Pham,Rasmus Pagh。通过显式特征映射的快速和可扩展的多项式核。https://dl.acm.org/doi/10.1145/2487575.2487591 KDD 2013
利用你的超参数:批量大小和学习率作为正则化
重新考虑这些超参数将提高您的模型的真实性能
梯度下降是许多人在学习机器或深度学习时首先学习的概念之一。这种优化算法是大多数机器学习的基础,包括神经网络中的反向传播。当学习梯度下降时,我们知道学习速率和批量大小很重要。
具体来说,增加学习速率会加快模型的学习,但有超过最小损失的风险。减少批量意味着你的模型在每次迭代学习中使用更少的样本来计算损失。
除此之外,这些珍贵的超参数很少受到关注。我们调整它们以最小化我们的训练损失。然后使用“更高级”的正则化方法来改进我们的模型,减少过度拟合。 这是正确的做法吗?
重新想象梯度下降
让我们后退一步。假设我们想要绝对最小的损失。如果我们有无限的计算能力,我们会用我们的学习速度做什么?我们可能会把它降低到绝对最小值。然而,与线性回归等模型不同,神经网络不是凸的。它们看起来不像碗,而是像山脉:

谢尔盖·佩斯特耶夫在 Unsplash 上的照片
降低你的学习速度肯定会让你陷入更深的低谷,但不会阻止你掉进一个随机的次优洞。这是一个局部最小值 或者一个看起来像最低点的点,其实不是。它很可能会超出你的训练数据,这意味着它不会推广到现实世界。
我们想要的是进入一个更平坦的高原。具有更平坦最小值的神经网络具有更大的泛化能力(加上像防止对你的网络的攻击这样的好处)。
一种直观的思考方式是,一个窄洞很可能是针对你的训练数据的。您的数据的微小变化(即在该空间中向任何方向移动)将导致损失的重大变化,而平坦的地形则不太敏感。这就是正则化的用武之地:防止我们的模型陷入这些狭窄的深度最小值的技术。
有许多正则化技术,如删除、数据集扩充和提取。然而,为什么要在充分利用我们现有的工具之前增加我们的工具箱呢?我们的工具总是存在,学习率和批量大小,可以为我们执行一定程度的正规化。
找到广泛的最小值

朱莉·莫里弗在 Unsplash 上的照片
正如前面的例子所显示的,将学习率降低到一个非常低的数值,很容易陷入局部极小值,而这个极小值可能会过拟合。
通过增加学习速率,我们实现了很少讨论的好处,即允许我们的模型摆脱过度拟合的最小值。该模型将错过局部最小值,并找到更宽、更平坦的最小值。
我们仍然需要确保学习率不要太大,潜在地利用像自适应学习率这样的技术。然而,最终,我们希望确保小的学习率不会使我们的模型在培训中看起来很好,而在其他地方很差。
类似地,减少批量会增加收敛的噪声。样本越小,彼此之间的差异就越大,因此上述地形上的收敛速度和方向就越易变。因此,该模型更有可能找到更广泛的局部最小值。这与采用大批量甚至所有样本数据形成对比,后者导致平滑收敛到深度局部最小值。因此,较小的批量可以为模型提供隐式正则化。
摘要
对于神经网络的正则化技术已经有了大量的研究。研究人员甚至质疑这种技术是否有必要,因为神经网络似乎显示出隐含的正则化。然而,在应用其他正则化步骤之前,我们可以重新想象学习率和批量大小的作用。这样做可以减少过度拟合,以创建更好、更简单的模型。
更高的学习率和更小的批量可以防止我们的模型陷入深度狭窄的极小值。因此,它们将对我们的训练数据和真实世界数据之间的变化更加稳健,在我们需要它们的地方表现得更好。
深度学习管道的探索变得容易
关于如何使用正确的包在 Python 中执行快速 DL 实验的简单指南

艾莉娜·格鲁布尼亚克在 Unsplash 上的照片
介绍
在项目的探索阶段,数据科学家试图为他的特定用例找到最佳的管道。在这个故事中,我将解释如何使用 ATOM 包来快速帮助你在任何给定的数据集上训练和评估深度学习模型。ATOM 是一个开源的 Python 包,旨在帮助数据科学家加快机器学习管道的探索。如果你想对图书馆有一个温和的介绍,请阅读这个故事。
例子
展示 ATOM 如何帮助您的最简单方法是通过一个示例。这个故事带你浏览一个笔记本,它训练并验证了一个用 Keras 实现的卷积神经网络。该模型使用 MNIST 进行训练,这是一个众所周知的图像数据集,其目标是对手写数字进行分类。
我们从必要的导入和定义模型开始:一个简单的神经网络,具有两个卷积层和一个输出层,由 10 个神经元组成,每个神经元对应一个数字。数据集包含 28x28 灰度图像,因此每个图像的数组都有形状(28,28,1)。之后,我们加载数据集。请注意,我们需要对数据进行整形,以与模型的输入保持一致。
from atom import ATOMClassifier, ATOMModel
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Flatten, Conv2D
from keras.wrappers.scikit_learn import KerasClassifier *# Create the convolutional neural network*
def neural_network():
model = Sequential()
model.add(
Conv2D(
filters=64,
kernel_size=3,
activation="relu",
input_shape=(28, 28, 1),
)
)
model.add(Conv2D(filters=64, kernel_size=3, activation="relu"))
model.add(Flatten())
model.add(Dense(units=10, activation="softmax"))
model.compile(
optimizer="adam",
loss="categorical_crossentropy",
metrics=["accuracy"],
)
return model*# Download the MNIST dataset*
(X_train, y_train), (X_test, y_test) **=** mnist**.**load_data()*# Reshape data to fit model*
X_train **=** X_train**.**reshape(60000,28,28,1)
X_test **=** X_test**.**reshape(10000,28,28,1)
data **=** (X_train, y_train), (X_test, y_test)
因为 ATOM 使用 sklearn 的 API,所以我们为我们的模型使用了 KerasClassifier 包装器。然后,我们将模型传递给 ATOMModel 函数,该函数向类添加一个名称和一个缩写词(用于从 atom 中访问模型)。
# Wrap the model to use sklearn’s API
model **=** KerasClassifier(neural_network, verbose=0)
*# Add useful attributes to the model*
model **=** ATOMModel(model, acronym**=**"NN", fullname**=**"Neural network")
现在我们已经准备好了模型,让我们从 atom 的管道开始。我们初始化 ATOM,以(X_train,y_train),(X_test,y_test)的形式提供我们想要训练的数据。注意,我们只使用了 10%的数据来加速计算。
atom = ATOMClassifier(*data, n_rows=0.1, warnings=False, verbose=2)
输出显示了数据集及其类别分布的概述。
<< ================== ATOM ================== >>
Algorithm task: multiclass classification.
Parallel processing with 10 cores.
Dataset stats ==================== >>
Shape: (7000, (28, 28, 1), 2)
-------------------------------------
Train set size: 6000
Test set size: 1000
-------------------------------------
| | dataset | train | test |
| -- | ----------- | ----------- | ----------- |
| 0 | 745 (1.2) | 614 (1.1) | 131 (1.5) |
| 1 | 764 (1.2) | 668 (1.2) | 96 (1.1) |
| 2 | 694 (1.1) | 584 (1.1) | 110 (1.2) |
| 3 | 679 (1.1) | 589 (1.1) | 90 (1.0) |
| 4 | 701 (1.1) | 612 (1.1) | 89 (1.0) |
| 5 | 631 (1.0) | 542 (1.0) | 89 (1.0) |
| 6 | 714 (1.1) | 604 (1.1) | 110 (1.2) |
| 7 | 714 (1.1) | 616 (1.1) | 98 (1.1) |
| 8 | 641 (1.0) | 548 (1.0) | 93 (1.0) |
| 9 | 717 (1.1) | 623 (1.1) | 94 (1.1) |
当输入数据超过 2 维时,ATOM 会将数据集转换为只有一个形状特征的数据帧(n_samples,shape_sample)。该特征被自动称为多维特征。这就是为什么形状被表示为(7000,(28,28,1),2)。请注意,末尾的 2 代表数据集包含的两列:功能列和目标列。让我们快速检查一下这个看起来怎么样。
atom.dataset.head()
Multidimensional feature Target
0 [[[0], [0], [0], [0], [0], [0], [0], [0], [0],... 5
1 [[[0], [0], [0], [0], [0], [0], [0], [0], [0],... 8
2 [[[0], [0], [0], [0], [0], [0], [0], [0], [0],... 8
3 [[[0], [0], [0], [0], [0], [0], [0], [0], [0],... 3
4 [[[0], [0], [0], [0], [0], [0], [0], [0], [0],... 9
列中的每一行都包含一个 shape=(28,28,1)的数组,该数组中的数据对应于一幅图像。
现在我们准备开始训练。为了自动执行超参数调整,我们首先定义我们想要在其中尝试参数的自定义空间。ATOM 使用贝叶斯优化算法进行超参数调整。确保超参数空间与 skopt 的 API 一致。
from skopt.space.space import Integer, Categoricalhyperparameter_space **=** [
Integer(1, 10, name**=**"epochs"),
Categorical([32, 64, 128, 256], name**=**"batch_size"),
]
下一步是训练模型。
atom**.**run(
models=model, # Our neural network
metric**=**"f1_weighted",
n_calls**=**25, # Number of trials of the optimizer
n_initial_points=10, # Number of initial random trials
bo_params**=**{"dimensions": hyperparameter_space},
)
输出如下所示:

就这样,使用优化器找到的最佳超参数来训练模型。
atom.evaluate()

可以通过我们定义的缩写词 nn 从 atom 访问该模型。
atom.nn.plot_confusion_matrix()

结论
我们已经学习了如何使用 ATOM 包来快速探索神经网络在给定数据集上的表现。然而,这个例子非常简单。ATOM 的能力远不止这些!其他有用的功能有:
- 多种数据清理和特征工程方法
- 30 多个分类和回归模型(逻辑回归、随机森林、xgboost、lightgbm 等)
- 轻松比较模型和管线
- 35 个以上的图来分析数据和模型性能
要了解更多信息,请看软件包的文档。对于 bug 或功能请求,请不要犹豫,在 GitHub 上发表问题或给我发电子邮件。
相关故事:
- https://towards data science . com/atom-a-python-package-for-fast-exploration-of-machine-learning-pipelines-653956 a16 e7b
- https://towards data science . com/how-to-test-multiple-machine-learning-pipelines-with-just-the-less-lines-of-python-1 a16 CB 4686d
- https://towards data science . com/from-raw-data-to-we B- app-deployment-with-atom-and-streamlit-d 8df 381 aa 19 f
引用:
- 邓,2012 年。用于机器学习研究的手写数字图像 mnist 数据库。 IEEE 信号处理杂志,29(6),第 141–142 页。(麻省理工学院许可证)
探索性数据分析

有很多工程师从未涉足统计学或数据科学领域。但是,为了构建数据科学管道或者将数据科学家产生的代码重写为适当的、易于维护的代码,工程方面会出现许多细微差别和误解。对于那些数据/ML 工程师和数据科学家新手,我做这个系列的帖子。我将尝试用简单的英语解释一些基本方法,并在此基础上解释一些数据科学的基本概念。
整个系列:
- 数据科学。概率
- 数据科学。贝叶斯定理
- 数据科学。概率分布
- 数据科学。措施
- 数据科学。相关性
- 数据科学。中心极限定理与抽样
- 揭开假设检验的神秘面纱
- DS 中的数据类型
- 描述性和推断性统计
- 探索性数据分析
急于迅速打动那些对商业感兴趣的人,数据科学家往往会完全错过了解数据的过程。这是一个非常严重且不幸的常见错误,可能会导致以下问题:
- 失去了洞察力,因此导致了项目的不幸结果。
- 由于不清楚的数据结构、数据中的异常值、数据中的偏差而产生不准确的模型;
- 基于不正确的数据创建准确的模型;
- 为模型选择了错误的属性;
- 资源利用效率低下。
探索性数据分析的目的是熟悉数据:了解数据结构,检查缺失值,检查数据中的异常,形成关于总体的假设,定义和阐明将用于机器学习的变量特征的选择,等等。
你会感到惊讶(讽刺),但有时你根本不需要机器学习,一个简单的启发式方法就可以轻松击败所有模型。为了找到启发,你还需要知道数据。
数据分析是有价值的,因为它让你更有信心,未来的结果将是可靠的,正确的解释和应用在理想的业务环境。
大多数 EDA 方法本质上都是图形化的。强烈依赖图形的原因是,就其本质而言,EDA 的主要角色是在开放研究中,图形与我们都拥有的自然模式识别功能相结合,为分析师提供了理解数据的前所未有的能力。
有无限多种可能的图表、图形和图形,但您只需要其中的少数几种就足以很好地了解数据并进行处理。
谁是亿万富翁?
让我们试着在亿万富翁的数据集上说明 EDA 方法。数别人的钱总是很有趣,不是吗?
工具
EDA 的工具可能会有所不同。我们将结合 Python 使用以下工具:
- Jupyter 笔记本,用于实时编码和可视化
- 任何从事数据工作的人的标准工具——pandas、numpy
- 可视化工具— matplotlib、seaborn、plotly、
从基础开始
从回答简单的问题开始:
- 数据集上有多少条目?
- 我们有多少列/功能?
- 我们的特征中有哪些类型的数据?
- 数据集中的所有列都有意义吗?
- 我们有目标变量吗?
df = pd.read_csv('billionaires.csv')
df.shape
df.columns
df.head()
显示数据集中的示例的目的不是为了进行彻底的分析。它是为了获得我们所拥有的数据的定性“感觉”。
描述统计学
描述性统计的目标是对您的数据有一个概括的看法,以便您可以开始以不同的方式查询和可视化您的数据。
pandas 中的函数describe()对于获取各种汇总统计数据非常方便,它返回数量、平均值、标准偏差、最小值和最大值,以及数据分位数
df.describe().T
- 值得注意的是,财富的 75%和最大值之间有很大的差异。
- 我们马上就能明白,我们有 1996 年到 2014 年的数据,2014 年也是中位数,也就是我们有很多专门针对 2014 年的数据。
- 百万富翁出生的年份显然有一些奇怪的值,如-47 年。
在这个阶段,你应该开始记录你想做的潜在的修正。如果有些东西看起来不合适,比如你的某个特性可能有偏差,现在是询问客户/关键利益相关者的好时机,或者再深入一点。
我们第一次看到了数据。现在让我们通过一些绘图来研究数据。
绘制定量数据
通常一个快速的直方图就足以理解数据。
先说有趣的事——大家都有多少钱?
plt.figure(figsize=(15,10))
sns.distplot(df['wealth.worth in billions'])
plt.xscale('log')

我用对数标度至少显示了一些分布。显然,有更多的人没有巨额资金,但也有一个长尾巴,表明有很多人非常有钱。
我们继续吧。我们的亿万富翁年龄有多大?
我们记得这一栏有离群值,我们把它们清理一下,看右图。
df = df[df['demographics.age'] > 0] # drop the records with incorrect age
plt.figure(figsize=(15,10))
sns.distplot(df['demographics.age'], bins=15)
plt.show()

分布与正态相似,左边的尾部略大。
让我们以同样的方式按行业进行划分。
plt.figure(figsize=(15,10))
g = sns.FacetGrid(data=df, hue='wealth.how.industry', aspect=3, height=4)
g.map(sns.kdeplot, 'demographics.age', shade=True)
g.add_legend(title='wealth.how.industry')

industries = ['Hedge funds', 'Consumer', 'Technology-Computer']
plt.figure(figsize=(15,10))
g = sns.FacetGrid(
data=df[(df['wealth.how.industry'] != '0') & (df['wealth.how.industry'].isin(industries))],
hue='wealth.how.industry',
aspect=3,
height=4)
g.map(sns.kdeplot, 'demographics.age', shade=True)
g.add_legend(title='wealth.how.industry')

你可以看到钱流向了数据集中较老的部分。此外,可以看出科技公司更偏向年轻人,而消费行业则偏向老年人。还有一个行业,出于某种原因,一个人可以在 20 岁之前变得富有。
绘制定性数据
分类特征不能通过直方图可视化。相反,您可以使用条形图。
让我们来回答这个问题— 比较有钱的亿万富翁都是在什么行业?
city = df['wealth.how.industry'].value_counts(ascending=False)
df_city = df.filter(['wealth.how.industry'], axis=1)
df_city['count'] = 1
grouped_city = df_city.groupby('wealth.how.industry', as_index=False,sort=False).sum()
grouped_city.sort_index(ascending=False)
grouped_city = grouped_city.sort_values('count', ascending=False)
plt.figure(figsize=(15,8))
sns.barplot(data=grouped_city, x='count', y='wealth.how.industry')
plt.title('Industries of billioners', fontsize=17)

从顶部的情节来看,是以消费者为目标的行业。我很难得出任何结论,但正是这种洞察力让我可以告诉企业。此外,还有一些行业“0”——我们可以假设这些人根本没有行业或者行业混杂。
亿万富翁中男性多还是女性多?
plt.figure(figsize=(7,5))
sns.countplot(data=df, x='demographics.gender')
plt.title('Gender', fontsize=17)

刚好大部分都是男的。
让我们来看看亿万富翁国家。
column = 'location.citizenship'
fig = go.Figure(data = [
go.Pie(
values = df[column].value_counts().values.tolist(),
labels = df[column].value_counts().keys().tolist(),
name = column,
marker = dict(line = dict(width = 2, color = 'rgb(243,243,243)')),
hole = .3
)],
layout=dict(title = dict(text="Billionaire countries"))
)
fig.update_traces(textposition='inside', textinfo='percent+label')
fig.show()

超过三分之一的亿万富翁来自美国。
可以看到,有些行业,还有性别,数据很少。这些是罕见的课程。在构建模型时,它们通常会有问题。它们要么导致阶级失衡(取决于什么将是目标),从而导致过度适应模型。
箱线图
盒状图(也称为盒须图)是一种基于五个数字汇总显示数据分布的标准化方法:
如果以非图形格式呈现,上述关于箱线图的信息几乎同样容易评估。箱线图是有用的,因为在实践中,所有上述内容以及更多内容都可以一目了然。

人们可以注意到分布的对称性和“肥尾”的可能迹象。你可以通过观察中线是否在盒子的中心以及胡须的长度是否相等来估计对称性。在偏斜分布中,中间值将被推向短须的方向。此外,箱线图有助于发现数据中的异常值——从其他数据中脱颖而出的数据。
让我们浏览所有的定量数据,并建立它们的箱线图。

它似乎显示了人类在整个样本中的等级。
year。我们可以看到收集亿万富翁的时间段。你可以看到他非常倾向于最近几年,这似乎是合乎逻辑的-如果你很久以前就可以赚到第一个十亿,那么随着时间的推移,你可能会积累更多,你不太可能离开这个名单。
company.founded。类似的结论,也可以看出很可能存在一些缺失值。我们以后还得对付他们。
demographics.age。很多离群值,你可以看到有零年龄或者负年龄的人,这是不对的。如果你扔掉这样的异常值,你可能会怀疑这个变量分布中有接近正态的东西。我们应该为这个变量绘制一个距离图。
location.gdp。很难在这个图表上说些什么——似乎大多数亿万富翁国家都不是很富裕,但很难判断这个栏目到底是什么意思。
wealth.worth in billions。大量的异常值,尽管按季度来看,我们可以说大多数都有接近于零的数十亿,这是我们在前面的图中已经看到的。
在最简单的箱线图中,中间的矩形跨越第一个四分位数到第三个四分位数(四分位数间距或 IQR)。通常,异常值要么是第三个四分位数以下的 3 倍 IQR,要么是第一个四分位数以上的 3 倍 IQR。但是对于每个数据集,离群值的定义是不同的。
箱线图非常擅长呈现关于中心趋势、对称性和方差的信息,尽管它们会误导多模态等方面。箱线图的最佳应用之一是并列箱线图(见下面的多变量图形分析)。

相关分析
我们已经讨论过什么是相关性以及为什么需要它。简而言之,相关性允许您查看数字特征和其他数字特征之间的关系。概括地说,相关性取值范围为-1 到 1。负相关意味着当一个特征增加时,另一个特征减少,正相关意味着当一个特征增加时,另一个特征增加。0 没有关系。
在查看数据时,相关性研究非常有用,因为它给出了列之间如何相关的概念。
关联热图有助于您可视化这些信息。
cols = ['rank', 'year', 'company.founded', 'demographics.age', 'location.gdp']
sns.set(font_scale = 1.25)
sns.heatmap( df[cols].corr(), annot = True, fmt = '.1f' )
plt.show()

一般来说,您应该注意:
- 哪些特征与目标变量密切相关?
- 其他特征之间是否存在有趣或意想不到的强相关性?
我从我的数据中看不到任何有趣的见解。
不要害怕尝试更多
很明显,这里描述的那些简单的可视化并不能总是描述和显示数据的所有方面并回答所有问题。所以不要害怕实验和尝试其他概念。

完整的笔记本可以在这里找到
结论
在探索性数据分析步骤结束时,您将对数据集有一个相当好的理解,对数据清理有一些注意,并且可能对要素工程有一些想法。
推荐书籍
感谢您的阅读!
有什么问题吗?请在下面留下您的评论,开始精彩的讨论!
查看我的博客或者来打个招呼👋在 Twitter 或订阅我的电报频道。
规划好自己!
探索性数据分析和数据清理实践练习
为 ML 算法准备数据的逐步指南

探索性数据分析(EDA)是数据科学的重要组成部分,因为它有助于发现我们正在处理的数据实体之间的关系。当我们第一次处理数据时,使用 EDA 很有帮助。它还有助于处理大型数据集,因为实际上不可能确定与大量未知数据的关系。随着我们在 EDA 领域的深入,我们发现了各种模式,这些模式被证明对进一步的数据处理和建模是卓有成效的。
在本文中,我们将尝试揭示影响印度工程专业毕业生薪酬的因素,并随后仅选择对薪酬有较大影响的相关类别。清理后的数据将为任何可以预测工资的回归算法做好准备。
资料组
对于这个 EDA,我们将使用 Kaggle 上的“工程毕业生工资预测”数据集。如果您想了解相同的内容,它包含了关于数据中可用类别的详细信息。
https://www.kaggle.com/manishkc06/engineering-graduate-salary-prediction
导入库
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
加载数据
df = pd.read_csv('../input/engineering-graduate-salary-prediction/Engineering_graduate_salary.csv')
df.head()

现在,让我们分析数据,并对我们正在处理的问题有一个基本的了解。
df.columns

现在,如果我们仔细观察,有一些列显然不会影响工资,如“ID”、“DOB”、“第 10 和第 12 董事会”等。我们将去掉这些多余的列。
df = df.drop(['ID', 'DOB', 'CollegeID', '12graduation' ,'GraduationYear','10board', '12board' , 'CollegeState','CollegeCityID', 'CollegeCityTier',],axis = 1)
df.head()
现在,我们应该还有 24 列。
让我们检查一下我们的数据中是否有重复的。
df.duplicated().sum()
在运行这段代码时,我们看到没有重复的代码需要处理。
现在,让我们检查专门化列。
df.Specialization.value_counts()
输出:
electronics and communication engineering 670
computer science & engineering 557
information technology 506
computer engineering 415
computer application 201
mechanical engineering 155
electronics and electrical engineering 148
electronics & telecommunications 89
electrical engineering 63
electronics & instrumentation eng 24
information science engineering 18
electronics and instrumentation engineering 18
instrumentation and control engineering 18
civil engineering 15
electronics engineering 13
biotechnology 12
other 10
industrial & production engineering 8
chemical engineering 7
mechanical and automation 5
applied electronics and instrumentation 5
telecommunication engineering 4
computer science and technology 4
automobile/automotive engineering 4
aeronautical engineering 3
electronics and computer engineering 3
mechatronics 3
instrumentation engineering 3
industrial engineering 2
biomedical engineering 2
metallurgical engineering 2
information & communication technology 1
computer and communication engineering 1
information science 1
mechanical & production engineering 1
ceramic engineering 1
computer networking 1
control and instrumentation engineering 1
electrical and power engineering 1
embedded systems technology 1
electronics 1
industrial & management engineering 1
Name: Specialization, dtype: int64
如果我们观察到这一点,我们可以理解,如果我们试图使用这些数据来训练模型,有许多单个值会不必要地影响模型。让我们将这些数据组合在一起,因为它们并没有给整个“信息”增加多少价值。
specialization = df.Specialization.value_counts() #Store the values # in specialization
lessthan10 = specialization[specialization<=10]
lessthan10def correctSpecialization(val):
if val in lessthan10:
return 'Others'
else:
return valdf['Specialization'] = df['Specialization'].apply(correctSpecialization)df['Specialization'].value_counts()

酷毙了。这些数据似乎是我们可以利用的。
让我们现在检查统计数据。
df.describe() #Understand the statistics of given data.

如果我们观察,我们可以看到 GPA 的最大值为 99.93,最小值为 6.63,这是一个很大的范围,6.63 可能是一些错误的值。我们还不知道呢!但是,有些事情似乎肯定是错的!让我们对此进行分析,看看我们是否可以去除异常值(如果有的话)。
plt.scatter(df.index,df['collegeGPA'])

当涉及到 EDA 时,图表和绘图是非常可靠的。这些简单的步骤有助于直观地识别是否有异常值。该图清楚地显示,这些值大部分位于 50–100 范围内,我们可以安全地删除小于 20 的值,这可能会引入不必要的偏差。
df = df[(df['collegeGPA'] > 40)]
您可以再次绘制相同的图表,并观察数据现在是一致的。
我对其他色谱柱进行了类似的操作,为了节省时间,我只提到了哪些色谱柱需要进一步分析和清洗。其他列都很统一,但是您也可以分析它们进行练习,并使用任何方法来处理相同的问题。
plt.figure(figsize = (15,15))plt.subplot(3,3,1)
plt.scatter(df.index, df.ElectronicsAndSemicon)
plt.title('ElectronicsAndSemicon')plt.subplot(3,3,2)
plt.scatter(df.index, df.Domain)
plt.title('Domain')plt.subplot(3,3,3)
plt.scatter(df.index, df.ComputerProgramming)
plt.title('ComputerProgramming')plt.show()

毫无疑问,在所有 3 个图中,我们可以看到异常值的存在。我们来处理一下。
df = df.replace(-1,np.nan)cols_with_nan = [col for col in df.columns if df.isna().sum()[col]>0]for col in cols_with_nan:
df[col] = df[col].fillna(df[col].mean())
我首先用 nan 替换了-1 值,然后用这些列的平均值替换了所有这些值。
现在,如果我们将它与原始数据进行比较,我们可以明确地观察到,我们的新数据更加清晰,更易于使用。它包含统一的价值观,并以某种方式影响工资。
让我们检查一下雄性和雌性的数量。

sns.countplot(df.Gender,palette = 'inferno')

让我们看看第 10 和第 12 个百分比之间是否有任何关系。
sns.scatterplot(df[‘10percentage’],df[‘12percentage’])
显然,在第 10 和第 12 个百分比之间可以看到线性。
可以使用所有类别对这些数据进行进一步分析,并与工资进行比较。例如:
plt.figure(figsize = (10,5))
sns.scatterplot(df.collegeGPA,df.Salary, hue = df.Degree,palette = 'inferno')

在这里,我们可以看到,大多数 B.Tech/B.E.学生都找到了薪水更高的工作。类似地,其他列可以与薪金进行比较。
现在让我们看看“开放体验”和薪水之间是否有什么联系。可以观察到,分数越高的毕业生薪水越高。
plt.figure(figsize = (10,5))
sns.scatterplot(df.openess_to_experience,df.Salary)

数据预处理
这最后一部分已经完成,所以最终的数据已经准备好提供给 ML 模型。
让我们检查数据类型,看看是否需要任何处理。
df.info()

性别、学位、专业需要处理,因为它们属于不适合 ML 模型输入的“对象”类型。
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()df.Gender = le.fit_transform(df.Gender)
df.Degree = le.fit_transform(df.Degree)
df.Specialization = le.fit_transform(df.Specialization)
现在,让我们检查最终数据。
df.head()

耶!数据终于准备好了。你现在可以在这个数据上尝试各种算法进行薪资预测!这整个过程绝对是重要的,以避免坏的模型预测,只是增加所有的工作全部重来!
我这边就是这样!建议总是受欢迎的。😃
探索性数据分析
我如何在建模前调查数据

在 Unsplash 上由 Adeolu Eletu 拍摄的照片
人们总说“做建模之前要先了解数据”。“了解数据到底是什么意思,有什么具体的步骤可以遵循吗?
理解数据的过程称为探索性数据分析(EDA)。它是指在应用机器学习模型之前,对数据集进行初步调查和分析,以了解分布、异常、相关性和其他数据特征的过程。
假设您已经有了包含所有必需数据变量的数据集,并定义了要优化的因变量。本文将介绍如何使用数据集来更好地理解它。
对于如何进行 EDA,没有固定的过程。我觉得以下步骤可能有助于您更好地理解数据,但您可以根据您的数据集对其进行自定义。
资料组
我们使用 Kaggle 的房价数据集。数据的想法很简单——根据房屋特征预测房价。但是,我们从这个数据例子中了解 EDA 过程是非常理想的,因为它有 80 个自变量!
H 我们如何更好地理解数据以选择有用的预测指标来预测房价将是这项工作的关键。
EDA 的步骤
第一步。查看整个数据概览
train.head()

查看前 5 行数据,了解每一行的情况。您可以使用 tail 函数查看最后 5 行数据
train.shape
#(1460,81)
查看数据集的行数和列数
train.info()

查看每列信息(数据类型,空行)
我们可以看出目标变量(Saleprice)是连续的整数数据类型。
结果的最后一部分(在下面的屏幕截图中用红色突出显示)显示了所有列的数据类型分布。例如,在这种情况下,我们知道我们有 38 列(包括因变量)数字类型(float 或 int),43 列字符串类型。
第二步。查看缺失的数据
在前面的屏幕截图中,我们可以看到某些列有很多空值,这在预测中是不可取的。在这里,我们首先希望看到数据集中顶部缺失的列。
#count rows of missing data for each column
missing_number=train.isnull().sum().sort_values(ascending=False)#Calculate percentage of null value
missing_percentage=missing_number/len(train)
missing_info=pd.concat([missing_number,missing_percentage],axis=1,keys=['missing number','missing percentage'])
missing_info.head(20)

缺失值计数
我们可以看到 19 列缺少记录,6 列缺少超过 15%的数据。
就个人而言,我会删除掉超过 15%缺失记录的列。然而,这也取决于有多少数据字段可用和字段在预测最终中有多重要。
drop_columns=missing_info.head(6).index
train=train.drop(columns=drop_columns)
删除前 6 个缺失的列(缺失超过 15%的 6 列)
第三步。将数据集分为数字和分类(字符串)
numerics = ['int64', 'float64']
df_num = train.select_dtypes(include=numerics)
从数据集中提取数字数据。这里你应该参考 data.info()结果来看看你的数据类型是什么。
df_num[['YearBuilt','GarageYrBlt','YrSold','YearRemodAdd']]=2010-df_num[['YearBuilt','GarageYrBlt','YrSold','YearRemodAdd']]
对“错误数字”列的特殊处理:这里我指的是以数字形式存在但实际上是分类的数据字段。例如,年份或城市等级以数字形式存在,但它们实际上是分类数据。您可以将它们视为数值型(如果 value 有意义),将它们放入分类桶中,或者将数据转换为数值型。
在我的例子中,我使用的是最后一种方法。例如,在“建造房屋的年份”列中,我使用当前年份减去“建造房屋的年份”来获得房屋建造的年份。这样,它就是一个有效的数值变量。
for num_var in df_num.columns:
df_num[num_var].fillna(df_num[num_var].mean(),inplace=True)
替换数字数据中的空值
这里我使用列的平均值来填充空值。您可以选择对其他一些变量使用中值甚至简单的回归函数(如果在最终模型中也包含这些变量,可能会导致多重共线性)
df_cat= train.select_dtypes(include='object')
从数据集中提取字符串值列
for cat_var in df_cat.columns:
df_cat[cat_var].fillna(df_cat[cat_var].mode()[0],inplace=True)
在这里,我用列中最常出现的值或模式值来填充分类空值。
第四步。查看数字列的数据分布
df_num.describe()

Describe 命令为您提供了一个关于所有数字列的汇总统计信息的概览表
for i, col in enumerate(df_num.columns):
plt.figure(i)
ax = sns.distplot(train[col])


或者你可以画出所有数字变量(这里包括目标变量)的分布图,看看它们是右偏还是左偏。如果分布过于倾斜,您可能需要进行变换。上面的两个示例图显示,目标变量和地面居住面积都是右偏的,有一个长的右尾。
df_num.corr()
Corr()将以表格的形式给出所有数值变量的相关矩阵。这对于查看自变量之间或自变量与因变量之间的相关性非常有用。然而,普通表格看起来并不那么用户友好,尤其是有大量预测值的时候。
import seaborn as sns
import matplotlib.pyplot as plt#find correlation matrix
corr_mat=df_num.corr()#Select variables have more than 0.4 or less than -0.4 correlation with the dependent variable(SalePrice)
chosen_variable=corr_mat[abs(corr_mat["SalePrice"])>0.4].index#plot the heatmap
plt.figure(figsize=(12,12))
ax = sns.heatmap(df_num[chosen_variable].corr(),annot=True,cmap='RdBu')

热图
热图是关联矩阵更直观的表示。在这里,我只选择和想象与销售价格相关度大于 0.4 或小于-0.4 的预测值。这是因为与因变量相关性的绝对值越高,预测值就越重要。
这也有助于你了解预测因子之间的相互关系。例如,地下室总表面和一楼表面具有 0.81 的强相关性。即使它们在预测销售价格方面都非常重要,您也应该只选择一个包含在模型中。
这是选择重要数值特征的好方法,您可以根据预测值的数量来选择边界。如果您不喜欢当前的颜色组合,您也可以通过更改热图功能中的“cmap”参数,在此处参考选择其他颜色。
步骤 5:查看文本变量的数据特征
for cat_var in df_cat.columns:
print(df_cat[cat_var].value_counts())

结果的前 4 个变量
这将打印出所有字符串变量的分布。如果一个文本变量非常集中在某个值上,如住房数据集中的“街道”,该数据集中有 1454 个 Pave,而 Grvl 上只有 6 个,那么它对预测不会有很大的贡献。
for i, col in enumerate(df_cat.columns):
plt.figure(i)
ax = sns.barplot(x=col, y="SalePrice", data=train)


分类变量和因变量之间的条形图(销售价格)
这部分绘制了每个分类变量类别之间销售价格分布的柱状图。理想情况下,您希望看到不同类别之间销售价格的巨大差异,因为这样分类变量将是预测最终价格的一个很好的预测器。
您还应该查看前面的 value_count 表的结果,以确保每个类别都有足够的数据大小来支持它。例如,即使铺路街道类型的房屋比 Grvl 街道类型的房屋具有明显更高的平均销售价格,也只有 6 所房屋具有 Grvl 街道类型,因此数据大小不具有支持结论的统计意义。
最后
还有几点需要注意:
- 这里我假设我们只有一个训练数据集。如果您既有训练数据集又有测试数据集,您应该将它们结合起来用于 EDA,因为它可能涉及一些数据转换
- 房价的例子就是回归的例子。这里的因变量是连续的和数值的。如果你有一个分类问题,你应该选择绘制条形图分布,而不是相关矩阵来选择数值预测。
- 如果你有时间序列变量,你也可以用因变量绘制时间趋势图来观察特征的显著性
绘制了变量的相关性和分布之后,如何知道应该处理多重共线性还是正态性问题?你可以从下面我以前的文章中找到更多信息:
使用优步游乐设施数据集的实用方法
Python 中的探索性数据分析

本杰明·沃罗斯拍摄的照片
探索数据无疑是数据科学过程中最重要的阶段之一。尽管它很简单,但它可以成为一个强大的工具,让你在数据和业务背景方面领先,并在创建机器学习模型之前确定关键的处理方法。
为了让事情变得更有趣一点,我决定在我的个人优步骑行数据上玩玩 Python,看看我能提取出哪些见解。
在这篇文章中,我将指导你完成以下步骤:
1.问题定义
2.数据发现
3.数据准备
4.数据分析和讲故事
注意:数据准备通常是一个需要围绕数据格式化、清理和操作进行大量工作的阶段,但是使数据一致是您的分析和未来建模的一个成功因素。
请求和下载您的个人数据集
优步的数据下载功能为您提供关于您的骑行的深入信息。您可以通过以下链接请求访问您的数据:【https://myprivacy.uber.com/privacy/exploreyourdata/download
在您的请求完成后,一封带有下载链接的电子邮件将发送给您(通常在同一天)。
为了安全起见,您的数据只有7 天 。
1.问题定义:第一件事,第一!
在开始处理和分析数据之前,你应该做的第一件事是思考目的。我的意思是,你应该想想你为什么要进行这样的分析的原因。如果你不确定这一点,简单地开始制定关于你的主题的问题,如什么?什么时候?在哪里?谁啊。哪个?怎么会?多少?多少钱?****
根据您拥有的数据和要素的数量,分析可能会变得无穷无尽。所以这就是为什么(经过思考过程)我决定关注以下问题:
**a. How many trips have I done over the years?
b. How many trips were Completed and Canceled?
c. Where did most of the dropoffs occur?
d. What product type was usually chosen?
e. What was the avg. fare, distance, amount and time spent on rides?
f. Which weekdays had the highest average fares?
g. Which was the longest/shortest and more expensive/cheaper ride?
h. What was the average lead time before begining a trip?**
很明显,这些只是引导你分析的问题。但是,让我们假设我是一名经理,我的问题定义是: 我对业务主要指标(问题)没有可见性,无法开始推动对业务(目的)的见解和决策。
为了更好地指导您,请始终尝试思考 3 个主要问题:
什么?(问题:需求是什么,意图是什么,痛苦是什么?)
为什么?(目的:为什么要这样?你的目标是什么?)
怎么会?(产品:如何解决你的问题?哪个工具?)
注意:在你清楚问题和目的之前,不要进入下一阶段。
2.数据发现
导入库和数据集。
检查基本数据集信息(数据类型和维度)
**<class 'pandas.core.frame.DataFrame'>
RangeIndex: 554 entries, 0 to 553
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 City 554 non-null int64
1 Product Type 551 non-null object
2 Trip or Order Status 554 non-null object
3 Request Time 554 non-null object
4 Begin Trip Time 554 non-null object
5 Begin Trip Lat 525 non-null float64
6 Begin Trip Lng 525 non-null float64
7 Dropoff Time 554 non-null object
8 Dropoff Lat 525 non-null float64
9 Dropoff Lng 525 non-null float64
10 Distance (miles) 554 non-null float64
11 Fare Amount 554 non-null float64
12 Fare Currency 551 non-null object
dtypes: float64(6), int64(1), object(6)
memory usage: 56.4+ KB**
T6。rename( ) 方法允许你重命名轴标签(索引和列)。在这种情况下,我决定规范化列名以清理编码,因为我以后可以通过键入**<data_frame>.<column>**轻松地调用列
使用 。head( ) 方法获得更多关于数据格式的敏感性,并了解数据集值的整体结构。
看一看连续变量,我们注意到一些异常值的存在。然而,这些异常值似乎并没有反映出任何异常值(例如,fare_amount = 1000 BRL),这可能会让我们稍微舒服一点。
附注:如果发现异常值,可能需要考虑一些处理措施(例如异常值替换/移除)。

下面的图表从不同的角度展示了变量的分布。在这种情况下,我们看到两个变量都呈现出不对称分布(正)。对于距离,我们注意到频率值越高,距离越短,对于费用金额,我们有相同的行为。
此外,我们还注意到标准偏差较高,以“平均值”作为我们的参考。这意味着两个变量中的值非常分散。

毫不奇怪,我们在“票价 _ 金额”和“距离 _ 英里数”之间有很强的相关性,推断出你在旅途中停留的时间越长,票价就越高。
**sns.scatterplot(x='distance_miles',y='fare_amount',data=df1);**

3.数据准备
我决定删除列 fare_currency ,因为我所有的旅行都发生在同一个国家(巴西)。
现在让我们检查 缺失值 的存在。

尽管 Lng 和 Lat 值为空(总共 29 个),但发现 3 个记录没有product_type。如下所示,这些记录对我的数据集来说无关紧要,因为实际上没有满足任何列。

现在,让我们在继续之前去掉这 3 条记录。
3.1 数据清理:分类特征
在分析第一个分类列**<product_type>**时,我可以清楚地看到一些工作是必要的,因为我可以找到引用同一类别的不同值。然后,我总结了 5 个中的 15 个原创类别。

由于该分析的范围仅围绕优步的游乐设施,所以我从数据集中删除了 UberEATS 记录。
我们的第二个分类特征<status>似乎可以分为三种状态,不需要任何治疗。

3.2 数据转换:处理日期
日期通常会大大增强你的分析能力,因为你可以把它分解成不同的部分,从不同的角度获得洞察力。如前所示,我们的日期特性实际上是对象数据类型,所以我们需要将它们转换成日期时间格式。
现在,让我们将<request_time>特性分解成不同的日期部分。我只是为<request_time>这样做,因为我假设所有的游乐设施都在同一天 (相信我,我已经检查过了!😄)。
3.3 特征工程:创建新特征
基于<fare_amount>和<distance_miles>功能,我创建了一个名为<amount_km>的新功能,它将帮助我们了解按公里支付多少费用。
<request_time>和<begin_time>之间的时间差将让我们知道我通常等待优步汽车到达我的目的地有多长时间(以分钟计)。在这种情况下,它是按分钟计算的。
类似地,<dropoff_time>和<begin_time>之间的时间差将让我们知道每次行程花费了多少时间(以分钟为单位)。
由于具有已取消和 Driver_Cancelled status 的记录中的特征对我的分析没有用,我将它们设置为空值以便在我的数据集中进行更多的清理。****
4.数据分析和讲故事:表演时间到了!
****建议:不要在没有完成业务问题定义的情况下开始你的分析,因为它决定了你分析的重点和质量。除此之外,这个过程将帮助你思考新的可能性/问题,同时尝试回答之前的问题。
注意:为了更好地组织我的分析,我将创建一个额外的数据框,删除状态为 **CANCELED** 和 **DRIVER_CANCELED**的所有行程,因为它们应该从一些问题中忽略。
a.这些年来我去过多少次?
从 2016 年 4 月到 2016 年 1 月 21 日,共完成 444 趟。如果不考虑 2016 年和 2021 年(未满年),我们可以清楚地看到,从 2017 年到 2019 年每年的平均乘坐次数为 124 次,从 2019 年到 2020 年有一个巨大的下降(-51%)** 。这很容易用 COVID 的爆发来解释。**
现在,想象一下,如果我们将这个结果推广到所有优步用户…

b.完成或取消了多少次旅行?
查看下面的堆叠条形图,我们可以看到不包括 2015 年和 2021 年(由于出行量低),2020 年的取消率最高。考虑到 Covid 爆发后对企业造成的巨大影响,这可能是一个令人担忧的指标。总体而言,取消率为 17.9%** (考虑到骑手和驾驶员取消)。**

c.大多数辍学发生在哪里?
下面的热图以不同的色调和强度动态显示了最常去的区域。这可能是优步调整价格和优化某些地区需求的宝贵信息,同时结合时空数据来跟踪用户的行为。

d.通常选择什么类型的产品?
UberX 是目前最受欢迎的产品类型,出现频率为 90.3% 。所以我大概可以推断,我是那种通常会寻找实惠价格的用户。

e.乘坐的平均费用、距离、数量和时间是多少?
考虑到所有的旅行,每次旅行的平均花费是 19.2 BRL ,除去大约。 8.1 公里。因此,如果我们在上做一个快速模拟,我一年要花多少钱来做每天的往返旅行,我们会:365 days * 2 trips * 19.2 BRL/fare = 14,016 BRL/year
同样,平均来说,大约花费了。行程**2.4 BRL/公里和 21.4 分钟。**

f.哪一个工作日的每公里平均票价最高?
根据下面的图表,我们可以看到,平均而言,周一至周三的、、、周五的、、周日的是最贵的、。因此,它允许我们更好地理解周季节性,并找出优步及其驱动因素盈利能力较高的天。****
****
g.最长/最短和最贵/最便宜的旅程是哪一次?
下表显示了最长(31.77 公里)和最短(0.24 公里)的记录。

分析里程支付的金额,我们有:贵(46.96 BRL/公里)和便宜(0 BRL/公里)。由于总距离仅为 0.24 公里,这种影响基本上是由高需求时期固定最低票价驱动的。

h.开始旅行前的平均准备时间是多少?
在发出请求后,大约花了5 分钟启动行程。
**4.9 minutes**
结论
探索性数据分析不是一件小事!它需要大量的工作和耐心,然而,如果正确地应用到你的商业环境中,它肯定是一个强大的工具。
这篇文章简要地展示了一些使分析更容易的技巧和步骤,并且毫无疑问地强调了一个定义良好的业务问题的至关重要性,指导所有的编码工作达到一个特定的目标,并且还强调了重要的见解。这个商业案例还试图反映 python 在日常商业活动中的实际应用,展示它可以变得多么有趣、有价值和有趣。
非常感谢你来到这里!我希望你喜欢它!😃
使用 SQL 和 Seaborn (SNS)在 Python 中进行探索性数据分析(EDA)
探索性数据分析(EDA)是一种分析数据集以总结其主要特征的方法,通常使用统计图形和其他数据可视化方法。可以使用或不使用各种统计模型,但 EDA 主要用于查看数据可以告诉我们什么,而不仅仅是正式的建模或假设检验任务。
你猜怎么着…总是…

图片来自 unsplash.com
为什么我首先要做 EDA?
我认为更合适的问题是:
哪种情况下不应该用 EDA?
EDA 是数据科学中至关重要的一步,它允许我们获得某些洞察力,并对我们正在处理的数据进行统计测量。这对无穷无尽的用户来说是必不可少的,包括业务经理、利益相关者、数据科学家等等。
对于数据科学家来说,EDA 有助于定义和完善我们的重要特征变量选择,这将用于尚未训练的机器学习模型。
在这个故事中,出于演示目的,我们将使用一些 FitBit 数据。
健身追踪器数据是数据科学家、统计学家、医学专家、生理学家和心理学家的热门研究领域,仅举几个学术研究领域为例。检测复杂时间序列数据(如 FitBit Fitness Tracker 数据)中的关系可以是建立日常生活模式的一种方式,也是检测这些模式偏差的一种方式。
一个好的 EDA 可以帮助发现这些…
分析
对 Fitbit 数据进行彻底的分析。重点介绍并讨论了关键发现。本文提供的分析使用从 33 个不同用户收集的 940 个数据点来执行。
在阅读这个故事的同时,我希望向你传达驱动代码编写的推理和逻辑。
首先,为了了解这些用户的生活方式,我们根据用户的活动水平绘制了分钟数和距离。

正如预期的那样,非常活跃的距离用更短的时间行驶(也就是说,它们用更陡的回归线表示更大的速度)。这里一个有点出乎意料的结果是轻活动分钟比公平活动分钟导致更高的速度。了解这种分类是如何进行的,以便真正理解“轻度”活动和“中度”活动之间的区别,这将是很有趣的。
让我们对总步数与卡路里… 做一些简单的线性回归

再一次,正如预期的那样,随着用户走的步数增加,一天燃烧的卡路里量也增加了。一个有趣的事实是,回归线的截距代表了不采取措施的一天燃烧的卡路里量。这是使用者在久坐的一天中燃烧的卡路里量。根据健康线部位,这个数字对应的是基础代谢率。
如果我们知道用户的性别、体重、身高和年龄,就可以计算出这个值。例如,他们报告称,体重 175 磅、身高 5 英尺 11 英寸的 35 岁男性的基础代谢率为 1816 卡路里,而体重 135 磅、身高 5 英尺 5 英寸的 35 岁女性的基础代谢率为 1383 卡路里。为了将这些估计值与我们的数据进行比较,我们可以使用线性回归得到截距值。预测的基础代谢率约为 1665.74(介于 35 岁女性和男性的预测值之间)。
如果我们只过滤零步骤的数据点,并获得卡路里分布的统计数据,我们可以进一步获得用户的基础代谢率信息。

让我们看看非常活跃分钟、公平活跃分钟和轻微活跃分钟……的数据分布

这里有一个问题:不清楚是否所有用户在分析期间的一整天都在使用健身追踪器。如果用户记录了一整天,那么VeryActiveMinutes+FairlyActiveMinutes+LightlyActiveMinutes+sedentaryments的总和应该等于 1440 分钟(一天的总分钟数)。

从上面的代码片段中,我们得出
There are 474 (out of 936) rows where users logged the whole day.There are 462 rows where users logged parts of the day.
LightlyActiveMinutes 分布非常对称,在活动的几分钟内没有峰值。记录一整天的用户可能最终会注册大量的 LightlyActiveMinutes ,而那些只记录一天中一部分时间的用户可能只注册需求较高的活动。

现在,让我们来看看睡眠习惯…

星期几有什么不同吗?现在我们已经看了一下我们的数据及其分布,一周中的哪一天会对用户行为产生相当大的影响吗?


周末久坐分钟有什么变化?

这种分布如何依赖于周末?

我们现在已经根据日均分钟数的分布区分了两组用户

似乎我们已经发现了一个趋势,这两个组有一个明显的偏移,似乎在边界线附近。让我们验证一下…

在这里,我们发现了一个明显的趋势,即睡眠时间越长的用户越少久坐。这表明睡得最多的用户往往在白天也更活跃。
数据洞察
仅使用我们 33 个用户的日常活动,我们得出了一些有趣的结论!
在这里,我包括了从上面的 EDA 中得到的一些高层次的见解:
- 在一周的不同日子里,用户活动没有明显的区别;平均每天走的步数在 7670 左右。
- 根据来自 CDC : 的一些研究,“……较高的每日步数与较低的各种原因的死亡风险相关。”疾病预防控制中心还告诉我们:“…与成人每天走 4000 步相比,这个数字被认为是低的,每天走 8000 步与全因死亡率(或全因死亡)降低 51%相关。每天走 12000 步比走 4000 步的风险低 65%。”
如果目标是燃烧一些卡路里,我们发现所采取的步骤和燃烧的卡路里之间存在线性关系。因此,我们可以使用用户数据来拟合模型,并预测用户应该走多少步才能达到一定的卡路里燃烧量。
- 关于睡眠习惯,随着睡眠时间的增加,久坐时间明显减少。
接下来呢?
EDA 通常是为了获得数据洞察力,这可以帮助我们完成机器学习任务。在接下来的故事中,我们使用相同的数据集和衍生的见解来训练几个机器学习模型,以解决回归问题。
如果你喜欢我的故事,并想跳到有代码和完整数据集的笔记本,我已经在我个人的 git 上的 repo 中发布了它。
给回购打个星:)
如果你在数据科学和/或人工智能项目上需要任何帮助,请随时通过 Linkedin 或 midasanalytics.ai 联系我
Seaborn 数字营销的探索性数据分析
数据可视化
比点击率还高。

来自 Unsplash 的照片 Carlos Muza
什么是 EDA,为什么它很重要?
探索性数据分析(EDA)通常是您手头有数据并想要分析它的第一步。在 EDA 中,没有假设,没有模型。你正在从数据中寻找模式和真相。
EDA 对于数据科学项目至关重要,因为它可以:1 .帮助你获得对数据的直觉;2.在分布之间进行比较;3.检查数据是否在您期望的范围内;4.找出数据丢失的地方或是否有异常值;5.汇总数据,计算平均值、最小值、最大值和方差。
EDA 的基本工具是绘图、图表和汇总统计。
为什么是 Seaborn?
Seaborn 是一个用 Python 制作统计图形的库。它建立在 matplotlib 之上,并与 pandas 数据结构紧密集成。它相当于 Python 中 R 的 ggplot2。
关于数据
在文章中,我们将使用 Seaborn 对模拟的数字营销数据进行 EDA。这是 Rachel Schutt 博士和 Cathy O'Neil 博士的书做数据科学中的第一个练习。
我已经将 zip 文件下载到我的本地机器上,并将使用 Google Colab 进行分析,但是也可以随意使用其他 ide。你可以在这里找到笔记本。
是时候开始探索了!
让我们首先探索第一个 CSV 文件:五月第一天的信息。
# Upload data from local machine to Google Colab
from google.colab import files
uploaded = files.upload()# Convert dataset to pandas dataframes
import pandas as pd
df = pd.read_csv("nyt1.csv")
df.shape

数据集的形状(图片由作者提供)
df.head()

数据集的前五行(图片由作者提供)
df.describe()

数字特征统计(图片由作者提供)
从上面的统计数据中,我们知道特性没有丢失值。年龄组在 0 到 108 岁之间。让我们把年龄分成七个组。
# Create a new variable 'age_group' to categorize users by age
bins = [0, 18, 25, 35, 45, 55, 65, 110]
labels = ['<18', '18-24', '25-34', '35-44', '45-54', '55-64', '65+']
df['age_group'] = pd.cut(df.Age, bins, labels = labels,include_lowest = True)
df.head()

创建了一个新的功能“年龄组”(图片由作者提供)
按性别分每个年龄段有多少人?
我们可以使用 countplot 用条形显示每个分类箱中的观察计数。
import warnings
import seaborn as sns
warnings.filterwarnings('ignore')
# Set default theme and figure size
sns.set_theme()
sns.set(rc={'figure.figsize':(20,10)})sns.countplot(x='age_group', hue='Gender', data=df)

按性别分列的各年龄组人数(图片由作者提供)
我们可以发现大多数用户是 18 岁以下的女性。在 18 至 64 岁年龄组中,男性使用者多于女性。但是对于 65 岁以上和 18 岁以下的用户,女性用户多于男性用户。
这里,用户指的是阅读《纽约时报》文章的人。我们更感兴趣的是他们是否会点击广告。
从统计图像中,我们知道用户没有点击或者点击了 4 次。
我们来看看在所有年龄段中,哪个群体的点击量更多。

点击量与年龄组(图片由作者提供)
所以大多数人没有点击广告,18 岁以下的年轻人比其他群体点击得更多,也许这个群体是广告公司的目标群体?
接下来的问题是:会有人点击一个没有印象的广告吗?我认为不是,我们可以通过以下方式来验证:
df[(df['Clicks']>0) & (df['Impressions']==0)].shape[0]
输出为 0。酷,这正是我们所期待的。只有当有印象(显示的广告)时,用户才会点击它。
让我们看看广告是如何推广的,也就是说,印象在不同年龄段的分布情况如何。
我们可以使用 直方图 来显示数据集的分布。
sns.histplot(df, x='Impressions', hue='age_group', stat='count', binwidth=1, multiple='stack')

按年龄组的印象分布(图片由作者提供)
大部分用户都被展示过 0 到 13 次广告,最有可能的频率是 5 次。不同年龄段之间没有太大区别。
我们还可以使用核密度估计来查看分布。
sns.displot(df, x='Impressions', hue='age_group', kind='kde', bw_adjust=2, multiple='stack')

按年龄组的印象分布(图片由作者提供)
让我们用一个箱线图来看看是否有异常值。
sns.boxplot(y=df['Impressions'], x=df['age_group'])

我们可以确认,对于每个年龄组,印象分布是相似的,而偶尔,广告会向 35 至 44 岁的年龄组显示 20 次。
现在,我们探讨了印象和点击。让我们把它们结合起来,看看点击率如何。
让我们创建一个新变量“CTR”
df['CTR'] = round(df['Clicks']/df['Impressions'], 5)
由于数据集非常大,使用箱线图无法告诉我们太多信息。我们将使用一个箱线图来代替。 Boxenplots 特别擅长总结大型数据集的分布情况。
# Plot the distribution of Click-through rate by age group
sns.boxenplot(x=df['age_group'], y=df['CTR'])

各年龄段点击率分布(图片由作者提供)
我们可以发现,50%的 18 岁以下用户的点击率低于 10%,而对于 18 至 65 岁的年龄组,点击率低于约 18%。75%的用户点击率低于 20%。
有多少人的点击率是 0?
让我们根据用户的点击行为对他们进行分组。
bins = [-1, 0, 0.1, 0.2, 0.4, 1]
labels = ['0', '<10%', '10%-20%', '20%-40%', '>40%']
df['CTR_group'] = pd.cut(df.CTR, bins, labels = labels,include_lowest = True)
df.CTR_group.value_counts(normalize=True, sort=True)

因此,91.2%的用户从未点击过任何广告,5.4%的用户点击率为 10%-20%,2.3%的用户点击率为 20%-40%。
我们可能希望更多地挖掘点击率在 10%到 20%之间的 7.7%的用户,并推广更适合他们的广告。
让我们进一步了解点击广告的用户。我们将为它们创建一个子集。
df_Clicked = df[df['CTR'] > 0]
df_Clicked.head()
df_Clicked.shape

人们点击广告时数据集的形状(图片由作者提供)
df_Clicked.age_group.value_counts(normalize=True)

按年龄组划分的点击人数比例(图片由作者提供)
在点击广告的所有用户中,50%的人年龄在 18 岁以下。
sns.boxenplot(x=df_Clicked['age_group'], y=df_Clicked['CTR'])

所有点击用户中按年龄组划分的点击率分布(图片由作者提供)
在点击广告的用户中,18 岁以下的用户有一半点击率超过 20%。
sns.countplot(x='age_group', hue='Gender', data=df_Clicked)

按性别分列的点击用户中各年龄组的人数(图片由作者提供)
sns.countplot(x='age_group', hue='Signed_In', data=df_Clicked)

按登录状态划分的点击用户中各年龄组的人数(图片由作者提供)
18 岁以下的女性用户在点击广告的用户中所占比例最大。年轻人也不喜欢登录,而其他年龄段的用户倾向于在点击广告前登录。
sns.countplot(x='CTR_group', hue='age_group', data=df_Clicked)

CTR 组的年龄组分布(图片由作者提供)
从上图我们可以得出结论,在所有点击广告的用户中,18 岁以下的用户往往点击率更高。点击率最高的是 10%-20%的群体。
让我们为这些人创建另一个子集。
df_Clicked1020 = df_Clicked[df_Clicked['CTR_group'] == '10%-20%']# Plot the distribution of impressions by age group
sns.histplot(df_Clicked1020, x='Impressions', hue='age_group', binwidth=1, stat='count', multiple='stack')

我们可以看到,大多数人都有 5 倍以上的印象;因此,更多的广告出现对获得更好的点击率至关重要。
我们从数据中解读出了什么?
- 91.3%阅读《纽约时报》的用户根本不会点击广告。
- 18 岁以下的用户是在《纽约时报》杂志上推广广告的广告公司的目标受众。他们占所有用户的 34%,占所有点击用户的 50%。他们也是对广告反应最大的群体。然而,广告对不同的年龄组没有不同的显示。一个建议是在《纽约时报》上推广更多适合年轻女性口味的广告。
- 点击率在 10%-20%之间的那 7.7 %的用户,才是广告公司更需要关注的。其中 49%不满 18 岁,70%为女性,56.8%倾向于登录《纽约时报》。
- 为了获得更高的点击率,推荐的印象频率在 5 到 10 之间。
我希望你喜欢这次旅行。欢迎在 Linkedin 上留言和我联系。
https://www.linkedin.com/in/fangyiyu/
参考
R 中的探索性数据分析:数据汇总、可视化和预测模型

考特尼·库克在 Unsplash 上的照片
提取数据集的含义
探索性数据分析对于理解任何数据集都是不可避免的。它包括数据汇总、可视化、一些统计分析和预测分析。本文将关注使用 R 和 R 的不同包的数据故事或探索性数据分析。
本文将涵盖:
- 一些关键点的总结和可视化
2.一些基本统计数据
3.预测模型
如果您经常关注我的文章,您可能已经在 Python 中看到过另一个使用相同数据集的探索性数据分析项目。以下是链接:
我在这里使用相同的数据集来执行 r 中的探索性数据分析。
开始吧!请随意从此链接下载数据集。
总结和可视化
我将数据集与 RStudio 文件放在同一个文件夹中。因此,我可以在 RStudio 中读取 CSV 文件:
df = read.csv("heart_failure_clinical_records_dataset.csv")
我没有展示截图,因为数据集相当大。这些是数据集中的列:
colnames(df)
输出:
[1] "age" "anaemia" "creatinine_phosphokinase" "diabetes" "ejection_fraction"
[6] "high_blood_pressure" "platelets" "serum_creatinine" "serum_sodium" "sex"
[11] "smoking" "time" "DEATH_EVENT"
数据集有不同的健康参数、年龄、性别和死亡事件。有许多方法可以处理数据集。我想先看一个相关图。最好先看看哪些变量是相关的。它需要“corrplot”库。
library(corrplot)
corrplot(cor(df), type = "upper")

我将用这个情节作为决定下一步行动的指南。
这个数据集中有六个连续变量。让我们检查它们的分布。首先,这六个变量将被分离为不同的数据集,然后使用基数 R 制作直方图:
df1 = df[, c('age', 'creatinine_phosphokinase', 'ejection_fraction', 'platelets', 'serum_creatinine', 'serum_sodium')]hist.data.frame(df1, title = "Histograms of all Numerical Variables")

该分布提供了关于这六个变量的概念。有一些数据会更有帮助。我将使用给出一些基本统计参数的汇总函数。
summary(df1)
输出:
age creatinine_phosphokinase ejection_fraction platelets
Min. :40.00 Min. : 23.0 Min. :14.00 Min. : 25100
1st Qu.:51.00 1st Qu.: 116.5 1st Qu.:30.00 1st Qu.:212500
Median :60.00 Median : 250.0 Median :38.00 Median :262000
Mean :60.83 Mean : 581.8 Mean :38.08 Mean :263358
3rd Qu.:70.00 3rd Qu.: 582.0 3rd Qu.:45.00 3rd Qu.:303500
Max. :95.00 Max. :7861.0 Max. :80.00 Max. :850000
serum_creatinine serum_sodium
Min. :0.500 Min. :113.0
1st Qu.:0.900 1st Qu.:134.0
Median :1.100 Median :137.0
Mean :1.394 Mean :136.6
3rd Qu.:1.400 3rd Qu.:140.0
Max. :9.400 Max. :148.0
同样,为了方便起见,将分类变量作为单独的数据集:
df2 = df[, c('anaemia', 'diabetes', 'high_blood_pressure', 'sex', 'smoking', 'DEATH_EVENT')]head(df2)

尽管相关图显示死亡事件和“性别”变量之间没有相关性。还是想看看男女死亡人数。在此之前,最好将这些列的 0 和 1 改为一些有意义的字符串。
df$death = ifelse(df$DEATH_EVENT == 1, "Yes", "No")
df$sex = ifelse(df$sex == 1, "Male", "Female")table(df$sex, df$death)

总体而言,数据集中男性多于女性。男女比例是多少?
data = table(df$sex)
data1 = round(data/sum(data)*100)
data1 = paste(names(data1), data1)
paste(data1, "%", sep = "")
输出:
[1] "Female 35%" "Male 65%"
为了进一步了解数据集中的人口,让我们分别检查男性和女性人口的年龄分布:
ggplot(df, aes(x = age)) +
geom_histogram(fill = "white", colour = "black") +
facet_grid(sex ~ .)

上面的直方图表明,男性人口总体上比女性人口年龄大。
血清肌酐和血清钠有一定的相关性。散点图将清楚地显示:
ggplot(df) + geom_point(aes(x = serum_creatinine, y = serum_sodium, colour = death, shape = death)) + ggtitle("Serum Creatinine vs Serum Sodium")

由于一些异常值,大部分数据都聚集在一个地方。在这种情况下,一个好的做法是检查没有这些异常值的同一个图。
df_scr = df[df$serum_creatinine < 4.0 & df$serum_sodium > 120,]ggplot(df_scr) + geom_point(aes(x = serum_creatinine, y = serum_sodium, colour = death, shape = death)) + ggtitle("Serum Creatinine vs Serum Sodium")

该图清楚地显示了血清钠和血清肌酸酐之间的关系。这些点被死亡事件用不同的颜色和形状分开。
下一个图是射血分数对死亡事件。它将显示抖动图,实际上是散点图的修改版本。当散点图中的一个变量是分类变量时,点保持在一条直线上,它们重叠。变得难以理解。这就是为什么在抖动图中,他们会稍微偏移点,以便更好地理解数据。此外,我们将添加一个红点来显示数据的平均值。

如果你注意到我稍微整理了一下数据。我剔除了两个异常值。
时间变量与死亡事件有很强的相关性。我希望看到一个显示死亡事件和无死亡事件的“时间”差异的箱线图:
ggplot(df, aes(death, time))+geom_point() + labs(title="Death Event with Time Variable Segregated by Gender",
x = "Death",
y = "Time") +
geom_boxplot(fill='steelblue', col="black", notch=TRUE) + facet_wrap(~ sex)

肌酐磷酸激酶与死亡事件有一定的相关性。下一个图用直方图探索了这个问题:
ggplot(df, aes(x=creatinine_phosphokinase, fill=death)) + geom_histogram(bins=20) + labs(title = "Distribution of Creatinine Phosphokinase", x = "Creatinine Phosphokinase", y = "Count")

贫血也与肌酐磷酸激酶有关。这种分布可以按贫血症来区分,以便按贫血症分别观察分布情况:
ggplot(df, aes(x=creatinine_phosphokinase, fill=death)) + geom_histogram(bins=20)+facet_wrap(~anaemia) + labs(title = "Distribution of Creatinine Phosphokinase", x = "Creatinine Phosphokinase", y = "Count")

贫血症和非贫血症人群的分布差异很大。
“时间”变量与死亡事件有很强的相关性。这个年龄与时间散点图用不同的颜色显示死亡事件。
ggplot(df, aes(x = age, y = time, col = death))+geom_point() + labs(title = "Age vs Time", x = "Age", y = "Time")

下图显示了死亡事件中不同颜色的血清肌酐分布:
ggplot(df, aes(x = serum_creatinine, fill = death))+geom_histogram() + labs(title = "Distribution of Serum Creatinine different colors for death event", x = "Serum Creatinine", y = "Count")

极少数异常值在最右边。让我们检查没有这些异常值的相同分布:
df_sc = df[df$serum_creatinine < 4.0,]ggplot(df_sc, aes(x = serum_creatinine, fill = death))+geom_histogram() + labs(title = "Distribution of Serum Creatinine different colors for death event", x = "Serum Creatinine", y = "Count")

现在分布更加清晰了。
死亡事件的射血分数可能不同。观察射血分数是否也因性别而异也将是有趣的。
ggplot(df, aes(death, ejection_fraction, fill = as.factor(sex))) +
geom_bar(stat = "summary", fun = "median", col = "black",
position = "dodge") + geom_point(position = position_dodge(0.9)) + labs(title = "Ejection Fraction per Death Event", x = "Death", y = "Ejection Fraction")

那些黑点显示了数据中的差异。
下一个图分别探索了死亡事件和非死亡事件中贫血和肌酸酐磷酸激酶之间的关系。
ggplot(df, aes(x = as.factor(anaemia), y = creatinine_phosphokinase, fill = death)) + geom_violin() +
stat_summary(aes(x= as.factor(anaemia), y = creatinine_phosphokinase), fun = median, geom='point', colour = "red", size = 3)+facet_wrap(~death)+
geom_jitter(position = position_jitter(0.1), alpha = 0.2) + labs(title = "Creatinine Phosphokinase for Anaemia State Segregated by Death Event", x = "Anaemia", y ="Creatinine Phosphokinase")

在这个情节中,你可以同时看到几个不同的角度。violin 图显示了分布和异常值。同时,我将抖动图放入其中,红点指向相应数据的中值。
预测模型
在本节中,我们将尝试使用数据集中的其他特征来预测死亡事件。首先,我们应该检查死亡事件的比例:
data = table(df$death)
round(data/sum(data), 2)
输出:
No Yes
0.68 0.32
对于这个预测模型,我将再次导入数据集,因为我对之前导入的数据集做了一些更改。
df1 = read.csv("heart_failure_clinical_records_dataset.csv")
现在,我将数据集分为训练数据和测试数据。
library(caTools)
set.seed(1243)
split = sample.split(df1$DEATH_EVENT, SplitRatio = 0.75)
这种分割应该为每个 DEATH_EVENT 提供布尔值。其中 75%是真的,25%是假的。基于该分割值,训练数据和测试数据将被分离:
training_data = subset(d, split == TRUE)
test_data = subset(d, split == FALSE)
数据集已准备好用于分类器。我为这个项目选择一个支持向量机分类器。
library(e1071)svm_fit = svm(formula = DEATH_EVENT ~ .,
data = training_data,
type = 'C-classification',
kernel = 'linear'
)
训练数据被拟合到分类器中,分类器现在被训练。现在可以用测试数据测试分类器了。使用测试数据,我将预测死亡事件。所以,我们需要从测试数据中排除死亡事件。
y_pred = predict(svm_fit, newdata = test_data[-13])
预测完成了。现在让我们检查一下预测的准确性。此处的“caret”包中使用了 ConfusionMatrix 函数。因为这个 ConfusionMatrix 函数只用一行代码就提供了如此多的信息:
library(caret)confusionMatrix(y_pred, as.factor(test_data$DEATH_EVENT))
输出:
Confusion Matrix and StatisticsReference
Prediction 0 1
0 47 5
1 4 19
Accuracy : 0.88
95% CI : (0.7844, 0.9436)
No Information Rate : 0.68
P-Value [Acc > NIR] : 5.362e-05
Kappa : 0.7212
Mcnemar's Test P-Value : 1
Sensitivity : 0.9216
Specificity : 0.7917
Pos Pred Value : 0.9038
Neg Pred Value : 0.8261
Prevalence : 0.6800
Detection Rate : 0.6267
Detection Prevalence : 0.6933
Balanced Accuracy : 0.8566
'Positive' Class : 0
总体准确率为 88%。
看上面的混淆矩阵!它准确地预测了 47 个无死亡事件,4 个无死亡事件被错误地预测为死亡事件。另一方面,19 个死亡事件被准确预测,5 个死亡事件被错误预测为无死亡事件。
这里真阳性是 19,假阳性是 4,假阴性是 5。使用此信息,F1 分数计算如下:
true _ positive/(true _ positive+0.5 *(false _ positive+false _ negative))= 0.81
我不是在评论预测模型的效果是好是坏。这取决于期望值,期望值因项目而异。
结论
探索性数据分析是非常动态的。你可以用很多不同的方法来处理数据集。这是我的方法之一。如果你以前在 Python 中检查过我在相同数据集上的工作,你会看到一种不同的方法。请随意尝试一些其他类型的可视化和分类器。感谢阅读!
欢迎在推特上关注我,喜欢我的 T2 脸书页面。
更多阅读
[## Python 还是 R?数据科学家和数据分析师哪个好?
towardsdatascience.com](/python-or-r-which-one-is-better-for-data-scientists-or-data-analysts-3be038f6c74c)
圣保罗政策谋杀报告的探索性数据分析
详细看看巴西人口最多的州的凶杀案数字

玛丽亚·卡斯泰利在 Unsplash 上的照片
根据世卫组织、世界卫生组织(查看日期:2021 年 18 月 3 日),巴西的凶杀率为每 10 万居民 32.59 起,比全球平均水平(每 10 万居民 6 起)高出五倍,在这一可悲的统计数据中,巴西排名第九。
巴西是一个幅员辽阔的国家,有 2 亿多居民,被 27 个联邦单位分割,存在极端的不平等。这些不平等的一个很好的例子是暴力地图集(第 18 页)报告的这些州的杀人率。这份报告向我们显示,2018 年,罗赖马州的凶杀率是全国最高的,每 10 万居民中有 71.8 起凶杀案。相比之下,圣保罗州的凶杀率在全国最低,每 10 万居民中只有 8.2 起凶杀案。
在这项工作中,将在 2018 年至 2020 年期间对圣保罗州涉及凶杀案的警方报告进行探索性分析。
工具和代码
Python 是用于开发这一分析的编程语言,如果你想检查代码,你可以点击这个链接上的。
获取数据
本项目中使用的所有数据都在圣保罗州公共安全部长的网站上。
对于此探索性数据分析,下载了以下文件并将其合并到一个文件中:
下载的文件
- 类别——homícidio doloso(谋杀、故意杀人)
- 期间-2018 年 1 月至 2020 年 12 月之间的每个月
储存库文件
- 路径—https://github . com/ghermsen/Sao _ Paulo _ murder _ EDA/tree/main/db
- file—https://raw . githubusercontent . com/ghermsen/Sao _ Paulo _ murder _ EDA/main/db/dados bo . CSV
注:为了保留受害者的身份,我在处理完数据后已经删除了受害者的个人信息。出于这个原因,下面的分析只显示了在受害者匿名化后剩下的数据集的数据处理。

了解数据集
理解数据是任何数据科学项目的基本步骤。这个过程将向我们展示数据集中可用的变量以及这些变量是如何构造的。这一步有助于我们了解问题的类型以及我们将如何解决它。
变量字典:
- “日期” —谋杀发生的日期。
- 【时间】 —命案发生的时间。
- 【时段】 —谋杀发生的当天时段。
- 【城市】 —命案发生的城市。
- 【地区】 —城市所属的行政区域。
- “派出所” —登记命案的派出所。
- 【性别】 —受害者的性别。
- 【年龄】 —受害者的年龄。
- “种族” —受害者的种族(巴西地理统计局使用的分类)。
数据集大小和变量类型
下面,您可以看到数据集的大小及其变量的类型。
# Dataset Size
Number of Entries: 11793
Number of Variables: 9
# Types of Variables
date object
time object
period object
city object
region object
police_station object
gender object
age int64
ethnicity object
dtype: object
可以看出,【日期】**【时间】变量是【对象】。由于这些变量代表时间,这些变量经过一个合并的过程,然后转换成 "datetime64[ns]" 。
清理数据集
查看变量字典和数据集中呈现的变量类型,可以看到一个与分析无关的变量,即“police _ station】。为了更清晰更客观的分析,我删除了这个变量。
特征工程
在这个项目中,我将创建从现有变量派生的变量,以协助未来的分析。变量如下:
- “年” —命案发生的年份。
- “月” —命案发生的月份。
- 【日】 —命案发生的那一天。
- “小时” —谋杀发生的小时。
- “星期几” —谋杀发生的那一天。
- “周末” —如果一周中的某一天是周末(周六或周日),则该值等于 1。如果不是,则该值等于 0。
- “年龄组” —受害者所属的年龄组。
完成此过程后,数据框就可以进行分析了。

数据分析
数据分析将涉及地域、社会和时间方面:
领地
- 圣保罗州
- 圣保罗州在 2018 年、2019 年和 2020 年期间的谋杀受害者总数是多少?
- 在所分析的几年间,谋杀受害者的总体变化是多少?
- 行政区域
- 圣保罗州不同地区谋杀受害者人数的分布情况如何?
- 在所分析的几年间,圣保罗州各地区谋杀受害者的总体变化是多少?
社交
- 性别
- 受害者的主角性别是什么?
- 不同性别的谋杀受害者之间的百分比差异是多少?
- 种族划分
- 遇难者的主导民族是什么?
- 不同种族的谋杀受害者之间的百分比差异是多少?
- 年龄层
- 受害者的主要年龄组是什么?
- 按年龄组划分,谋杀受害者的百分比差异是多少?
- 受害者简介
- 男性和女性受害者的主要特征是什么?
太阳穴
- 谋杀受害者最多的月份是哪个月?
- 谋杀受害者最多的一天是哪一天?
- 一天中的哪个时间段有更多的谋杀受害者?
- 一天中哪一小时谋杀案受害者最多?工作日和周末的时间有区别吗?
地域分析

圣保罗州的总面积为 248,209 平方公里,如果我们将圣保罗州的面积与一个国家相比,它将略大于英国的 242,495 平方公里。据 SEADE 统计,2020 年,圣保罗州 645 个市有 4463 万居民。
数据显示,在分析期间,该州有 505 个城市登记了谋杀受害者,只有 140 个城市在此期间没有登记任何谋杀受害者。
圣保罗州

如上所述,在圣保罗,505 个城市在分析期间登记了谋杀受害者。当我们检查受害者人数时,我们得到总共 11,793 名谋杀受害者。
逐年分析显示,最近三年,这一数字一直在下降,2019 年下降了 3.65%,2020 年下降了 4.03%,导致谋杀受害者人数下降了 7.52%。

圣保罗 行政区域
上面,我对整个圣保罗州的数字进行了分析。然而,根据圣保罗州公共安全秘书的警方报告,圣保罗州分为 12 个地区。
# regions of são paulo state
df.region.sort_values(ascending = True).unique()
array(['Araçatuba', 'Bauru', 'Campinas', 'Capital', 'Grande São Paulo ', 'Piracicaba', 'Presidente Prudente', 'Ribeirão Preto', 'Santos', 'Sorocaba', 'São José do Rio Preto', 'São José dos Campos'],
dtype=object)
既然我们已经知道了圣保罗州的所有行政区,就有可能看到这些行政区的受害者总数有很大差异。出现这种差异是因为一些地区的人口比其他地区多得多。

左边的图像显示了首都地区和总统普鲁登特地区的总人数之间的差异。然而,首都区大约有 1200 万居民,而普鲁登特总统区大约有 86 万居民。
因此,在按区域分析时,百分比值的分析将比按区域总值的分析显示更多的相关信息。
在百分比结果中,我们注意到三个地区(Piracicaba、Bauru 和 Araç atuba)的谋杀受害者人数近年来有所增加。相比之下,在其他地区,受害者人数有所下降。降幅最大的地区是普鲁登特总统、圣若泽多里奥普雷托和桑多斯。

社会分析
在进行基于社会方面的分析之前,有必要了解圣保罗州的人口构成。通常,巴西每十年进行一次人口普查,由于新冠肺炎疫情,上一次人口普查仅在 2010 年进行。因此,涉及人口性别、种族和年龄组的数字是非官方的预测。
性别

根据 SEADE 的数据,2020 年,圣保罗的人口为 44,639,899 名居民,其中男性占 48.67%,女性占 51.33%。
我们可以看到,在分析期间,10,388 名谋杀受害者是男性,圣保罗州有 1,405 名受害者是女性。
通过按性别分列的受害者总数,可以看出,尽管圣保罗女性人口的比例高于男性人口,但该州谋杀受害者的人数差异很大,男性受害者占 88%,女性受害者占 11%。


然而,为了更好地理解不同性别受害者数量的演变,我分析了每年的受害者数量。分析显示,男女受害者人数逐年下降。在这一时期结束时,女性受害者的人数大幅下降了 15.98%,而男性受害者的人数下降了 6.92%。
种族
巴西是一个多种族国家,在其人口普查中,人口分为五个种族群体:亚洲人、黑人、土著人、Parda(多种族)和白人。
据该协会称,2010 年,圣保罗州的人口分布如下:

注:一些谋杀案的受害者在警方报告中没有透露他们的种族。对于这些事件,警察局的系统自动为这些受害者标注“无信息”。

在按种族分析受害者总数后,我们看到 87%以上的案件由 parda(多种族)和白人受害者组成,分别有 5216 名受害者和 5096 名受害者。紧接着,我们有 991 名黑人受害者,463 名没有种族信息的受害者,23 名亚裔受害者,和 4 名土著受害者。
分析圣保罗的人口,我们可以看到多种族人口大约是白人人口的一半。考虑到按种族划分的谋杀受害者人数,就有可能看到不平等。在圣保罗州,多种族受害者占大多数,白人受害者占 44.23%,黑人受害者占 8.4%。经过对每年数字差异的研究,我发现白人受害者下降了 9.31%,其次是多种族受害者,8.24%,黑人受害者,6.73%。

年龄

在警方的报告中,许多受害者的年龄没有被指出。在这些受害者的案例中,增加了 200 岁,这样他们就被列入了【未知】年龄组。生成汇总统计数据时,不会选择这些受害者。
汇总统计显示,受害者的平均年龄和中位数分别为 33.77 岁和 32 岁。25%的受害者年龄在 23 岁以下,75%的受害者年龄在 41 岁以下。这一信息已经表明,大多数受害者集中在 20-29 岁年龄组,集中在 30-39 岁年龄组,如下所示。


得出总结果后,我进行了一项研究来验证每年的差异,所有年龄组的值都有所下降。然而,10-19 岁年龄组的受害者人数下降了 34.23%,0-9 岁年龄组的受害者人数下降了 25.00%,70 岁以上年龄组的受害者人数下降了 20.00%。
受害者简介
在分析了变量【性别】【种族】【年龄】**【年龄组】之后,我们可以描绘出最常见的谋杀受害者的轮廓。男性受害者的资料显示,大多数男性受害者是多种族的,年龄在 20-29 岁之间。该档案显示在分析期内共有 1285 名受害者。
另一方面,女性侧写有不同的特点。女性受害者的资料显示,大多数女性受害者是白人,年龄在 30-39 岁之间。该档案显示在分析期间共有 174 名受害者。

时间分析
时间分析将试图获得每个时期受害者总数的简单信息。该分析将回答以下问题:
- 谋杀受害者最多的月份是哪个月?
- 谋杀受害者最多的一天是哪一天?
- 一天中的哪个时间段有更多的谋杀受害者?
- 一天中哪一小时谋杀案受害者最多?工作日和周末的时间有区别吗?
1 —凶杀案受害者最多的月份是哪个月?

2 —哪一天被害人数最多?

3 —一天中哪个时段的谋杀受害者多?

4 —一天中哪一个小时谋杀案受害者最多?工作日和周末的时间有区别吗?

当我们分析工作日和周末的受害者总数时,可能会发现时间上有微小的差异。


结论
该分析提供了关于圣保罗州谋杀数据的激动人心的信息。首先,可以看到,在过去几年里,圣保罗州的谋杀案数量一直在下降。一些州的受害者人数大幅下降,而 12 个州中有 3 个州的受害者人数有所增加。
还可以观察到男性和女性受害者之间的巨大差异,并了解女性受害者人数的下降幅度如何明显大于男性受害者。通过性别和其他特征,有可能根据简单的受害者概况确定比其他人更有可能成为受害者的群体。
在研究结束时,有可能得出结论,12 月是谋杀受害者人数最多的月份,周六是大多数人被谋杀的日子。大多数谋杀发生在晚上,当比较工作日和周末时,可以在谋杀数据中发现小时的细微差异。
由于我之前处理了该项目中发布的数据集以保护受害者的身份,因此完整的数据集提供了更多信息,例如凶杀案发生在城市中的位置。继续这项研究的下一步是
- 根据这项研究,利用这些犯罪发生的地区进行新的分析,目的是绘制特定城市内的凶杀地图;
- 将这些数据与城市的社会指标进行比较,如人类发展指数等。
欢迎提问、改进建议、指正,也感谢你到目前为止和我一起分析数据!
这个项目的全部代码可以在我的 Github 上找到
文本数据的探索性数据分析,包括可视化和情感分析

柴坦尼亚·皮拉拉在 Unsplash 上的照片
文本数据的预处理、分析、可视化和情感分析
文本数据分析一天比一天简单。Python 和 R 等著名的编程语言都有很好的文本数据分析库。曾经有一段时间,人们认为你需要成为编码专家来完成这些复杂的任务。但是随着库版本的不断开发和改进,只需要简单的初级编码知识就可以更容易地执行文本数据分析。
在本文中,我将使用主要是文本数据集的数据集。该数据集包含亚马逊婴儿产品和评级的客户评论。请随意从下载数据集和此链接并跟随。这是一个用于机器学习的很好的数据集,因为它有评论评级。但是本文将只关注探索性的数据分析。我们仍然会讨论情感,但这不是情感分析机器学习。
让我们跳进来吧!
资料组
首先,导入数据集,然后我们将进一步讨论它。
import pandas as pd
import numpy as npdf = pd.read_csv("amazon_baby.csv")
df.head()

作者图片
如您所见,我们在数据集中有产品名称、客户评论和评级。我喜欢从检查数据集中有多少行数据开始:
len(df)
输出:
183531
这是大量的数据。我决定缩小数据集。因为当我试图处理这个大数据集时,需要花费太多时间来完成一些可视化、计算和数据操作。为了使运行代码更容易、更省时,我决定缩小数据集。一种方法是简单地从数据集中取出 5000 到 10000 或任何你想要的数字。
但在此之前,让我们检查每个产品的评论的价值计数。
df['name'].value_counts()
输出:
Vulli Sophie the Giraffe Teether 785
Simple Wishes Hands-Free Breastpump Bra, Pink, XS-L 562
Infant Optics DXR-5 2.4 GHz Digital Video Baby Monitor with Night Vision 561
Baby Einstein Take Along Tunes 547
Cloud b Twilight Constellation Night Light, Turtle 520
...
Mud Pie Baby Stroller Bear Buddy (Set of 4) 1
Baby Mod Modena 3 in 1 Convertible Crib, White 1
Britax Kick Mats 1
Camouflage Camo print Cloth Diaper 1
Baby Gear Blue Bear with Dots and Circles Security Blanket Lovey 1
Name: name, Length: 32415, dtype: int64
您可以从输出中看到,有些产品只有一个评论。仅仅通过一次回顾是不可能得出任何结论的。在这里,我选择了至少有 20 条评论的产品。
df = df[df.groupby("name")["name"].transform('size') > 20]
我再次检查数据集的长度,发现我们现在有 89691 个数据。那也很多。这次演示我只需要 10000 个数据。如果你不担心运行时间,或者如果你有一个更高容量的计算机,请随意使用所有的数据。
df= df.head(10000)
现在,我有一个包含 10000 个数据的数据集。
预处理
在进入分析之前,一些文本清理和处理是必要的。预处理和数据清理是数据分析的一大部分。首先,我将把“review”列设为字符串格式。它看起来像字符串。但是万一有一些数据不是字符串格式的,我会简单地将整列转换为字符串。
df['review'] = df['review'].astype(str)
评论是这个数据集中的主要信息。如果任何一行遗漏了评审,我们就不需要那一行。
df = df[~df["review"].isnull()]
我还有 10000 个数据。这意味着 review 列中没有空值。
删除特殊字符
评论可能包含许多对任何分析都没有帮助的特殊字符。一开始就清洗它们是很好的。
def clean(txt):
txt = txt.str.replace("(<br/>)", "")
txt = txt.str.replace('(<a).*(>).*(</a>)', '')
txt = txt.str.replace('(&)', '')
txt = txt.str.replace('(>)', '')
txt = txt.str.replace('(<)', '')
txt = txt.str.replace('(\xa0)', ' ')
return txtdf['review'] = clean(df['review'])
转换为小写
转换成小写是必要的。否则,它会将带有大写字母的同一个单词视为不同的单词。像‘我’和‘我’会被认为是不同的词。我们不想那样。
df['review1'] = df['review'].apply(lambda x: " ".join(x.lower() for x in x.split()))
df['review1'].head()
输出:
153 we bought these for our son when he turned two...
154 my son loves stacking cups, so a friend recomm...
155 my son cameron just loves these great little s...
156 my one year old son received these as a birthd...
157 i purchased this toy for my great grandson\'s ...
Name: review1, dtype: object
删除标点符号
这一步是删除标点符号。由于标点符号的原因,一个单词可能会被以不同于原来的方式处理。例如,“use”和“use:”会因为标点而变成不同的单词。
df['review1'] = df['review1'].str.replace('[^\w\s]', '')
df['review1'].head()
输出:
153 we bought these for our son when he turned two...
154 my son loves stacking cups so a friend recomme...
155 my son cameron just loves these great little s...
156 my one year old son received these as a birthd...
157 i purchased this toy for my great grandsons fi...
Name: review1, dtype: object
如你所见,标点符号不见了!在第 157 行,“孙子”一词中有标点符号。我们最终会在词汇化或词干部分去掉“孙子”中的“s”。
删除停用词
停用词是一些语法或约束词,如“是”、“the”、“and”、“so”、“my”等。这些是出现频率很高的词。但是可能不会给分析增加任何价值。虽然有争议。有些人认为他们有时很重要。在一些人工智能项目中,停用词可能很重要,但在这个例子中,停用词是不必要的。
import nltk
nltk.download('stopwords')
from nltk.corpus import stopwordsstop = stopwords.words('english')
df['review1'] = df['review1'].apply(lambda x: " ".join(x for x in x.split() if x not in stop))
df['review1'].head()
输出:
153 bought son turned two seen playmates home love...
154 son loves stacking cups friend recommended toy...
155 son cameron loves great little stacking cars e...
156 one year old son received birthday gift loves ...
157 purchased toy great grandsons first christmas ...
Name: review1, dtype: object
看,所有的停用词都没了!
去掉生僻字
有些词只出现过一次。那些生僻字并没有增加任何东西。所以我们可以安全地丢弃它们。首先,找出每个单词的频率,然后找出只出现过一次的单词。
freq = pd.Series(' '.join(df['review1']).split()).value_counts()
less_freq = list(freq[freq ==1].index)
less_freq
输出:
['djgs',
'now7monthold',
'joseph',
'area8',
'activing',
'tea',
'productdespite',
'worth3the',
'aroundand',
'80lb',
'combinedit',
'hikesnow',
'bubblesbeing',
'cheast',
'inexcusable',
'heavyeven',
这是输出的一部分。这个列表总共有 14352 个单词。如果你注意到这些单词中的大部分甚至看起来是有线的。大多是错别字或者拼写错误。让我们把它们从评论中去掉:
df['review1'] = df['review1'].apply(lambda x: " ".join(x for x in x.split() if x not in less_freq))
拼写纠正
简单的拼写错误可以使用 correct()函数来纠正。
from textblob import TextBlob, Word, Blobberdf['review1'].apply(lambda x: str(TextBlob(x).correct()))
df['review1'].head()
输出:
153 He bought these for our son when he turn two H...
154 By son love stick cups so a friend recommend t...
155 By son cameron just love these great little st...
156 By one year old son receive these a a birthday...
157 I purchase the toy for my great grandson first...
只是一个警告!运行这段代码需要几个小时。你必须有耐心。如果你愿意,你可以避免这样做以节省时间。
词干化和词汇化
词干将从单词中删去像“ly”、“ing”、“ed”这样的部分。我们之前谈过一点。
from nltk.stem import PorterStemmer
st = PorterStemmer()
df['review1'] = df['review'].apply(lambda x: " ".join([st.stem(word) for word in x.split()]))
输出:
153 We bought these for our son when he turn two. ...
154 My son love stack cups, so a friend recommend ...
155 My son cameron just love these great littl sta...
156 My one year old son receiv these as a birthday...
157 I purchas thi toy for my great grandson\' firs...
查看输出中的第 157 行。在‘孙子’这个词后面,标点符号又回来了。别担心,我们以后会处理的。
下一步是将单词词根化。您可以选择词干化或词汇化。词干化之后,您可能看不到词汇化的很多变化。我仍然展示它是为了演示的目的。
df['review1'] = df['review1'].apply(lambda x: " ".join([Word(word).lemmatize() for word in x.split()]))
df['review1'].head()
输出:
153 We bought these for our son when he turn two. ...
154 My son love stack cups, so a friend recommend ...
155 My son cameron just love these great littl sta...
156 My one year old son receiv these a a birthday ...
157 I purchas thi toy for my great grandson\' firs...
现在我们应该再次删除标点符号:
df['review1'] = df['review1'].str.replace('[^\w\s]', '')
df['review1'].head()
输出:
153 We bought these for our son when he turn two H...
154 My son love stack cups so a friend recommend t...
155 My son cameron just love these great littl sta...
156 My one year old son receiv these a a birthday ...
157 I purchas thi toy for my great grandson first ...
Name: review1, dtype: object
实际上,我多做了一步。你可以避免删除开头的标点符号,在这一点上做。那会省去你不必要的一步。
数据分析
让我们从向数据集中添加更多要素开始分析。在这里,我加上评论的长度和每篇评论的字数。
df['review_len'] = df['review'].astype(str).apply(len)
df['word_count'] = df['review'].apply(lambda x: len(str(x).split()))
我想再增加一个特性,叫做极性。极性表现了一段文字的情绪。它计算消极和积极的话,并确定极性。该值的范围从-1 到 1,其中-1 代表负面情绪,0 代表中性情绪,1 代表正面情绪。
from textblob import TextBlob, Word, Blobberdf['polarity'] = df['review1'].map(lambda text: TextBlob(text).sentiment.polarity)df.head()
输出:

分布
我想先看一下 word_count、review_len 和 polarity 的分布。
df[["review_len", "word_count", "polarity"]].hist(bins=20, figsize=(15, 10))

作者图片
评论长度和字数的分布与预期相似。大多数人口处于较低的范围。严重向右倾斜的分布。极性几乎正常。多数极性大于零。这意味着大多数评论都是正面的。
极性与额定值
因为有一个评级栏可用,我们应该检查极性是否与评级相符。以下是各等级极性的箱线图:
plt.figure(figsize = (10, 8))
sns.set_style('whitegrid')
sns.set(font_scale = 1.5)
sns.boxplot(x = 'rating', y = 'polarity', data = df)
plt.xlabel("Rating")
plt.ylabel("Polatiry")
plt.title("Product Ratings vs Polarity")
plt.show()

作者图片
等级越高,平均极性越大。尽管在等级 1 和 5 中有很多异常值。也许看看这些数字会更有帮助。
mean_pol = df.groupby('rating')['polarity'].agg([np.mean])
mean_pol.columns = ['mean_polarity']fig, ax = plt.subplots(figsize=(8, 6))
plt.bar(mean_pol.index, mean_pol.mean_polarity, width=0.3)#plt.gca().set_xticklabels(mean_pol.index, fontdict={'size': 14})
for i in ax.patches:
ax.text(i.get_x(), i.get_height()+0.01, str("{:.2f}".format(i.get_height())))plt.title("Polarity of Ratings", fontsize=22)
plt.ylabel("Polarity", fontsize=16)
plt.xlabel("Rating", fontsize=16)
plt.ylim(0, 0.35)
plt.show()

作者图片
我原以为评分 1 和 2 的极性接近-1。但是看起来它们更接近于 0。这意味着评论中可能没有那么多负面词汇。我只是通过观察极性来猜测。请阅读几篇评级为 1 的评论以进行复查。
每个评分的评论数
下面是一个计数图,显示了数据集中每个评分的评论数。
plt.figure(figsize=(8, 6))
sns.countplot(x='rating', data=df)
plt.xlabel("Rating")
plt.title("Number of data of each rating")
plt.show()

作者图片
该数据集的大多数评论的评级为 5。
评论的长度与评分
看看评论的长度是否会随着评分的变化而变化将会很有趣。
plt.figure(figsize=(10, 6))
sns.pointplot(x = "rating", y = "review_len", data = df)
plt.xlabel("Rating")
plt.ylabel("Review Length")
plt.title("Product Rating vs Review Length")
plt.show()

作者图片
当评分为 5 时,评论长度显著下降。所以,客户高兴的时候,也没写太多!
基于极性的 20 大产品
这些是基于极性的前 20 个产品
product_pol = df.groupby('name')['polarity'].agg([np.mean])
product_pol.columns = ['polarity']
product_pol = product_pol.sort_values('polarity', ascending=False)
product_pol = product_pol.head(20)
product_pol

作者图片
WordCloud
Wordcloud 是一种常见且漂亮的文本数据可视化工具,用于绘制单词的频率。如果您还没有 wordcloud,您可能需要使用以下命令安装它:
conda install -c conda-forge wordcloud
我使用 Anaconda 包。这就是为什么我给出了 anaconda 包的安装命令。
为了创建单词 cloud,我将 review1 列中的所有文本组合在一起,形成了一个 bid 文本块。
text = " ".join(review for review in df.review1)
使用这个文本块,我创建了单词 cloud。在造云这个词之前,我去掉了一些我认为没有必要的词。如果你愿意,你可以进一步清理。
from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator
stopwords = set(STOPWORDS)
stopwords = stopwords.union(["ha", "thi", "now", "onli", "im", "becaus", "wa", "will", "even", "go", "realli", "didnt", "abl"]) wordcl = WordCloud(stopwords = stopwords, background_color='white', max_font_size = 50, max_words = 5000).generate(text)
plt.figure(figsize=(14, 12))
plt.imshow(wordcl, interpolation='bilinear')
plt.axis('off')
plt.show()

作者图片
文本中出现频率越高的单词越大。
频率图表
这是文本数据分析中制作词频图表的常见做法。这很好地说明了人们在这篇文章中谈论最多的是什么。首先,找出每个单词在数据集的 review 列中的出现频率。然后根据频率画出前 20 个单词。
def get_top_n_words(corpus, n=None):
vec=CountVectorizer().fit(corpus)
bag_of_words = vec.transform(corpus)
sum_words = bag_of_words.sum(axis=0)
words_freq = [(word, sum_words[0, idx]) for word, idx in vec.vocabulary_.items()]
words_freq =sorted(words_freq, key = lambda x: x[1], reverse=True)
return words_freq[:n]common_words = get_top_n_words(df['review1'], 20)df1 = pd.DataFrame(common_words, columns = ['Review', 'count'])
df1.head()

作者图片
下面是前 20 个单词出现频率的柱状图:
df1.groupby('Review').sum()['count'].sort_values(ascending=False).plot(
kind='bar',
figsize=(10, 6),
xlabel = "Top Words",
ylabel = "Count",
title = "Bar Chart of Top Words Frequency"
)

作者图片
这些是评论中出现频率最高的词。但比起只看到一个单词,两个或三个连续的单词更有帮助。它们提供了一些意义。下图显示了最常见的二元模型:
def get_top_n_bigram(corpus, n=None):
vec = CountVectorizer(ngram_range=(2,2)).fit(corpus)
bag_of_words = vec.transform(corpus)
sum_words = bag_of_words.sum(axis=0)
words_freq = [(word, sum_words[0, idx]) for word, idx in vec.vocabulary_.items()]
words_freq =sorted(words_freq, key = lambda x: x[1], reverse=True)
return words_freq[:n]common_words2 = get_top_n_bigram(df['review1'], 30)
df2 = pd.DataFrame(common_words2, columns=['Review', "Count"])
df2.head()

作者图片
这是最高发生二元模型的条形图:
df2.groupby('Review').sum()['Count'].sort_values(ascending=False).plot(
kind='bar',
figsize=(12,6),
xlabel = "Bigram Words",
ylabel = "Count",
title = "Bar chart of Bigrams Frequency"
)

作者图片
看看这些二元模型。它们是一些更有意义的短语。下一个图是三元模型。也许这将为我们提供更多关于人们在评论中说什么的想法。
def get_top_n_trigram(corpus, n=None):
vec = CountVectorizer(ngram_range=(3, 3), stop_words='english').fit(corpus)
bag_of_words = vec.transform(corpus)
sum_words = bag_of_words.sum(axis=0)
words_freq = [(word, sum_words[0, idx]) for word, idx in vec.vocabulary_.items()]
words_freq =sorted(words_freq, key = lambda x: x[1], reverse=True)
return words_freq[:n]common_words3 = get_top_n_trigram(df['review1'], 30)df3 = pd.DataFrame(common_words3, columns = ['Review' , 'Count'])
df3.groupby('Review').sum()['Count'].sort_values(ascending=False).plot(
kind='bar',
figsize=(12,6),
xlabel = "Trigram Words",
ylabel = "Count",
title = "Bar chart of Trigrams Frequency"
)

作者图片
词性标注
这是一个用词性标注单词的过程,如名词、代词、动词、形容词等。使用 TextBlob API 很容易做到这一点。
blob = TextBlob(str(df['review1']))
pos_df = pd.DataFrame(blob.tags, columns = ['word', 'pos'])pos_df = pos_df.pos.value_counts()[:30]
pos_df.plot(kind='bar',
xlabel = "Part Of Speech",
ylabel = "Frequency",
title = "Bar Chart of the Frequency of the Parts of Speech",
figsize=(10, 6))

作者图片
情感分析
探索性数据分析也包括机器学习。在这里,您将发现使用相同数据集对产品评论进行的情感分析。
结论
我试图提出一些很好的方法来理解或从一篇文章中提取信息。在预处理部分,我介绍了几种在机器学习中也很有用的预处理技术。在所有的分析或机器学习中,您可能不会使用我在这里介绍的所有预处理技术。你可以选择任何你觉得合适的。然后,介绍了一些通用的探索性分析技术。还有很多技术可以利用。希望你能在自己的数据集中尝试这些,并做一些很酷的分析。
欢迎在推特上关注我,喜欢我的 T2 脸书页面。
更多阅读
DeFi 贷款用户健康的探索性数据分析
使用 Graph 协议中的数据分析 Aave 和 Compound 用户的健康状况

杰瑞米·多洛在 Unsplash 上的照片
免责声明:本帖内容纯属信息性,不能视为投资建议。分析的数据可能包含错误
介绍
在这篇文章中,我将围绕两个最著名的 DeFi 借贷协议的用户健康进行探索性的数据分析。如果你不熟悉用户的健康状况以及这个指标对避免清算的重要性,我写了这篇文章用一个在 Aave 上清算的例子来解释它。
获取数据
为了获取数据,我将使用我最喜欢的协议之一,Graph。在上一篇文章中,我解释了如何使用它们的子图获取数据。对于复合协议,所有数据直接来自子图,对于 Aave,需要使用 aavejs 库来转换一些数据。
将成为数据集一部分的字段如下
- 健康度:用户健康度,以总 ETH 抵押存款与总 ETH 借款之差计算,健康度值小于 1 时允许清算用户抵押。
- 协议:用户已投资的协议。
- totalBorrowETH: 用户借入的 ETH 总额,以 ETH 价格借入的所有资产的总和。
- Total deposites:用户存入的 ETH 总额,以 ETH 价格借入的所有资产的总和。
- assetsBorrowedCount: 用户借入的资产数量。
- assets depositedcount:用户存放的资产数量。
- stableBorrowedCount: 本金为稳定硬币的借款总额。
- stableDepositedCount: 抵押品为稳定币的存款总额。
- count liked:用户被清算的次数。
选择的用户应该是那些当前有多个有效借出的用户。
创建数据集后,我们可以通过运行。
df_users.head()

清理数据
第一部分是清理数据和检测异常值,以查看健康数据是如何分布的。首先,我们可以运行
df_users.health.describe()
这将为健康变量返回以下值

正如我们所看到的,有一个最小值-1,这个值对于 health 变量是不可能的,所以我们要删除它,为此,我们可以运行
df_users.drop(df_users[df_users['health']<0].index, inplace=True)
再次运行相同的命令,我们可以看到这个值不再是数据的一部分

现在,我们将检查数据集的大小,以及有多少用户从 Aave 借钱,有多少用户从 compound 借钱,我们可以用下面的代码来查看
aave_borrowers = df_users[df_users.protocol=='AAVE'].shape[0]
compound_borrowers = df_users[df_users.protocol=='COMPOUND'].shape[0]
user_size = df_users.shape[0]print("Total users in the dataset {}".format(user_size))
print("Number of Aave borrowers {}".format(aave_borrowers))
print("Number of Compound borrowers {}".format(compound_borrowers))
结果是
Total users in the dataset 8615
Number of Aave borrowers 1145
Number of Compound borrowers 7470
检测异常值
要查看用户的健康状况是分布的,我们可以使用箱线图来检测数据中的异常值,运行
sns.boxplot(x=df_users['health'])

我们可以看到,有一些非常厌恶风险的用户可以被视为异常值,因此我们可以使用 IQR 方法来删除这些异常值,为此,我们可以执行
Q1 = df_users.health.quantile(0.25)
Q3 = df_users.health.quantile(0.75)
IQR = Q3 — Q1
print(IQR)
3.902646865965031
并为数据集移除它们
df_users = df_users[~((df_users.health < (Q1–1.5 * IQR)) |(df_users.health > (Q3 + 1.5 * IQR)))]
print(“Total users in the dataset after remove outliers {}”.format(df_users.shape[0]))Total users in the dataset after remove outliers 7297
之后,我们可以看到已从数据集中删除,再次查看箱线图,我们可以看到大多数用户在 1 到 3 岁之间

直方图
在清理数据并删除异常值后,我们有兴趣了解用户风险是如何分布的,了解用户是如何管理风险的,我们可以看到每个协议中的健康直方图
df_users['health'].hist(by=df_users['protocol'])

他们中的大多数都接近清算限制,其中少数人的风险较低,为了更仔细地了解大多数用户的位置,可以运行相同的直方图,但通过运行以下内容按大多数用户的位置进行筛选
df_users[df_users['health']<4].health.hist(by=df_users['protocol'])

我们可以看到,它们大多数在 1 和 2 之间,为了证实这一点,可以在每个方案中使用该字段的箱线图
df_users.boxplot(column=’health’, by=’protocol’)

相关
数据集的变量不是随机选择的,其想法是试图找到用户健康和他们在协议中的行为之间的某种相关性。一种策略可以是,使用稳定的硬币来降低波动风险,其他策略可以使用多种资产来分散抵押品和本金,并且还可以有趣地了解拥有高或低抵押品的用户是否更有风险,或者之前的清算是否会使用户更加厌恶风险。
一旦所有这些变量都在数据集中,我们就可以使用热图来绘制变量之间的相关性,方法是运行
corr = df_users.corr()
sns.heatmap(corr)

这些变量与用户健康之间没有很高的相关性,countLiquidated 和 stableDepositCount 的值较高,这表明负相关,表明当用户存放不太稳定的硬币或被清算次数较多时,健康往往较高。
为了检验这些变量,我们可以通过运行
fig, ax = plt.subplots(figsize=(10,6))
ax.scatter(df_users['health'], df_users['stableDepositedCount'])
ax.set_xlabel('health')
ax.set_ylabel('stableDepositedCount')
plt.show()

我们可以看到,当用户存入更多稳定硬币时,健康通常更集中在 1 附近,借款稳定变量也是如此。

在清算计数的情况下,不存在明确的相关性,存在多次清算且同样承担高风险的用户,以及没有清算且风险较低的用户。我尊重那些有超过 25 次清算,但仍然接近一次健康清算的人。

结论
通过这种简单的分析,我们可以了解用户在租借协议中的行为,同样,我们可以看到从 DeFi 获取数据并对其进行分析是多么容易,而不仅仅是实际数据,在区块链中,我们在每个块中都保存了所有历史记录。
使用米托的探索性数据分析
使用 Mitosheet 实现 EDA 过程自动化

来源:作者
探索性数据分析是数据科学中最重要和最关键的步骤。它不仅为我们提供了有关数据点和特征的信息,而且有助于找出不同的数据模式、关联、数据相关性等。
这是一个耗时的过程,因为我们需要使用不同的统计方法和可视化来分析数据。有大量的 python 库可以在 EDA 过程中提供帮助,但它们要么不用户友好,要么我们需要使用不同库的组合来完成所有 EDA 步骤。
MitoSheet 是一个开源 python 库,用于创建高度交互式的仪表板,我们可以在其中分析数据,可视化不同的功能并相应地操作数据。在本文中,我们将探讨如何使用 MitoSheet 的不同功能。
让我们开始吧…
安装所需的库
我们将从使用 pip 安装 MitoSheet 开始。下面给出的命令可以做到这一点。对于这篇文章,我们将使用 Jupyter 实验室。
!pip install mitoinstaller
!python -m mitoinstaller install
接下来,我们将通过在命令提示符下运行下面给出的命令来启动 Jupyter 实验室。
!python -m jupyter lab
导入所需的库
在这一步中,我们将导入创建 MitoSheet 所需的库。
import mitosheet
运行有丝分裂表
接下来,我们将从运行 MitoSheet 仪表板开始,并对我们选择的数据执行探索性数据分析。
mitosheet.sheet()

来源:作者
这里你可以看到 MitoSheet 的主页。在这张图片中,您可以看到突出显示的导入按钮,我们将使用它来导入我们正在处理的数据集,对于本文,我将使用著名的糖尿病数据集。但是你可以选择任何你想要的数据集。

数据集(来源:作者)
创建可视化效果
我们将使用导航面板上的图形按钮来创建可视化效果。所有的可视化效果都是使用 Plotly 创建的,这使得它们具有高度的交互性,并且易于下载。

可视化(来源:作者)
您还可以使用导航面板中的按钮来添加和删除列。类似地,我们可以使用选项创建数据透视表并合并数据。

数据透视表(来源:作者)
继续使用不同的数据集进行尝试,并执行探索性数据分析。如果您发现任何困难,请在回复部分告诉我。
本文是与皮尤什·英加尔合作完成的。
在你走之前
感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。可以查看我的Github*简介针对不同的数据科学项目和包教程。还有,随意探索* 我的简介 ,阅读我写过的与数据科学相关的不同文章。
Python 中的探索性数据分析、可视化和预测模型

卡伦·艾姆斯利在 Unsplash 上的照片
在 Python 中使用 Pandas、Matplotlib、Seaborn 和 Scikit_learn 库
本文主要关注一个数据故事项目。换句话说,探索性数据分析。在看了一个大数据集甚至一个小数据集之后,很难马上理解它。从数据集中提取一些有意义的信息需要努力、更多的工作和分析。
在本文中,我们将采用一个数据集,并使用一些流行的 python 库,如 Numpy、Pandas、Matplotlib、Seaborn,从中找到一些有意义的信息。最后,我们将运行 scikit-learn 库中的预测模型。
作为一名数据科学家或数据分析师,您可能不得不处理您不太熟悉的主题数据。这个数据集可能就是其中之一。很多专栏都是医学术语。但这应该不是什么大问题。借助伟大的工具和技术,探索数据集仍然是可能的。
本文将涵盖:
如何处理数据集以从中提取有意义的信息或理解数据
使用机器学习模型进行预测
从数据集中提取信息
这里使用的数据集称为“心力衰竭临床记录”数据集。请随意从 Kaggle 下载数据集并跟随。
让我们在 jupyter 笔记本环境中导入必要的包和数据集:
import pandas as pd
import numpy as np
import matplotlib.pyplot as pltdf = pd.read_csv("heart_failure_clinical_records_dataset.csv")
数据集对于在这里显示截图来说有点大。数据集有 299 行数据,以下是数据集的列:
df.columns
输出:
Index(['age', 'anaemia', 'creatinine_phosphokinase', 'diabetes',
'ejection_fraction', 'high_blood_pressure', 'platelets',
'serum_creatinine', 'serum_sodium', 'sex', 'smoking', 'time',
'DEATH_EVENT', 'sex1', 'death'],
dtype='object')
这里,“年龄”、“肌酸酐磷酸激酶”、“T8”、“射血分数”、“血小板”、“T9”、“血清肌酸酐”、“血清钠”、“时间”是连续变量。
“贫血”、“糖尿病”、“T10”、“高血压”、“性别”、“吸烟”、“T11”、“死亡事件”是分类变量。所有分类变量只有 0 和 1 值。因此,“性别”只说是男性还是女性,“高血压”说的是这个人是否有高血压,“贫血”说的是这个人是否患有贫血。
我喜欢通过观察连续变量的分布来开始大部分 EDA 项目。
df[['age', 'creatinine_phosphokinase',
'ejection_fraction', 'platelets', 'serum_creatinine', 'serum_sodium']].hist(bins=20,
figsize=(15, 15))
plt.show()

它一目了然地显示了大多数人口所在的位置和分布的性质。
为了理解数据,找到一些常用的描述性变量数据是有帮助的,例如平均值、中值、最大值、最小值、标准差和四分位数。
continous_var = ['age', 'creatinine_phosphokinase',
'ejection_fraction', 'platelets', 'serum_creatinine', 'serum_sodium']df[continous_var].describe()

现在,我们已经分别看到了分布和统计参数,tt 将很高兴看到这些变量如何与“死亡事件”相关联。此列有 0 和 1 值。我会把它改成‘是’和‘不是’。我还想更改“性别”列,将 0 和 1 替换为“男性”和“女性”。
df['sex1'] = df['sex'].replace({1: "Male", 0: "Female"})
df['death'] = df['DEATH_EVENT'].replace({1: "yes", 0: "no"})
如果您现在检查这些列,您将看到数据集中的两个附加列:“sex1”和“death”。在下一个图中,我们将看到一个 pairplot,它将显示每个连续变量与其余变量之间的关系。我们也将为死亡事件使用不同的颜色。
sns.pairplot(df[["creatinine_phosphokinase", "ejection_fraction",
"platelets", "serum_creatinine",
"serum_sodium", "time", "death"]], hue = "death",
diag_kind='kde', kind='scatter', palette='husl')
plt.show()

这里,红色表示死亡事件,绿色表示没有死亡。该图显示了这些变量在死亡事件之间是如何分离的。此外,散点图和中间的密度图显示了死亡事件和非死亡事件数据的明显区别。箱线图将更清晰地说明这一点:
continous_var = ['age', 'creatinine_phosphokinase',
'ejection_fraction', 'platelets', 'serum_creatinine', 'serum_sodium']plt.figure(figsize=(16, 25))for i, col in enumerate(continous_var):
plt.subplot(6, 4, i*2+1)
plt.subplots_adjust(hspace =.25, wspace=.3)
plt.grid(True)
plt.title(col)
sns.kdeplot(df.loc[df["death"]=='no', col], label="alive", color = "green", shade=True, kernel='gau', cut=0)
sns.kdeplot(df.loc[df["death"]=='yes', col], label="dead", color = "red", shade=True, kernel='gau', cut=0)
plt.subplot(6, 4, i*2+2)
sns.boxplot(y = col, data = df, x="death", palette = ["green", "red"])

死亡事件和无死亡事件的每个连续变量的平均值的一些数字数据对于一个好的报告是必要的。一些可靠的数字数据有助于讨论。在这里,我们将找到死亡事件和无死亡事件的每个连续变量的平均值和中值。正如我们之前在分布和密度图中看到的,并非所有变量都是正态分布的。有些是倾斜的。所以,平均并不能代表每一个。
y = df.groupby("death")["creatinine_phosphokinase", "ejection_fraction", "platelets", "serum_creatinine", "serum_sodium", "time"].agg([np.mean, np.median])
y

看起来死亡事件之间的“时间”变量非常不同。
我们稍后可能会对“时间”变量进行更多的探索。
让我们看看高血压是否在性别死亡中起作用。
df.groupby(['sex1', 'high_blood_pressure', 'death']).size().unstack().fillna(0).apply(lambda x: x/x.sum(), axis=1)
输出:

现在,你可以看到一些比例上的差异。但是这种差异在高血压的情况下比在性别上更明显。但是即使在高血压的情况下,这种差异也不会太大。在女性人口中,28%的死亡发生在没有高血压的女性身上,39%的死亡发生在有高血压的女性身上。这看起来显然有很大的不同。但也不要太激烈。它需要一些统计推断来做出更好的结论,这不是本文的范围。
除了“死亡”变量,我们在这个数据集中还有五个其他分类变量。值得研究它们与“死亡”变量的关系。我将使用 barplot 或 seaborn 库中的 countplot 来完成这项工作。
binary_var = ['anaemia', 'diabetes', 'high_blood_pressure',
'sex1', 'smoking']plt.figure(figsize=(13, 9))for i, var in enumerate(binary_var):
plt.subplot(2, 3, i+1)
plt.title(var, fontsize=14)
plt.xlabel(var, fontsize=12)
plt.ylabel("Count", fontsize=12)
plt.subplots_adjust(hspace = 0.4, wspace = 0.3)
sns.countplot(data= df, x = var, hue="death", palette = ['gray', "coral"]
输出:

在上面的图中,它清楚地显示了不同性别(在该数据集中只有男性和女性)、高血压、吸烟和糖尿病状态之间的死亡事件数量的差异。但与此同时,它也表明了数据集在吸烟和不吸烟的人数或患糖尿病或不患糖尿病的人数,或男性和女性的人数上是不平衡的。所以,看看这个比例会让我们有一个清晰的概念。
此时,我可以考虑在这些变量和死亡变量之间做交叉表。让我们从 sex1 变量开始:
x = pd.crosstab(df["sex1"], df['death'])
x
输出:

你可以看到这些数字。男性人口中有多少人死亡,女性人口中有多少人死亡。但是在这种情况下,比例会提供更多的信息。因为显然男性和女性的数量是不一样的。
x.apply(lambda z: z/z.sum(), axis=1)
输出:

看啊!男性和女性人口的死亡率均约为 32%。所以他们是一样的。我对其他四个分类变量也做了同样的处理。
贫血 vs 死亡

贫血患者的死亡比例更高一些。
糖尿病 vs 死亡

这里,糖尿病患者和非糖尿病患者的死亡比例完全相同。
吸烟 vs 死亡

吸烟者和不吸烟者的死亡比例几乎相同。
高血压 vs 死亡

在这里,高血压患者的死亡比例更高。
在所有上述分析中,我们只试图确定所有其他变量与‘死亡’变量的关系。我们能做的远不止这些。我们试试看能不能提取一些有趣的信息。
接下来,元素是小提琴图,显示了吸烟和不吸烟男性和女性的“时间”分布。
plt.figure(figsize=(8, 6))
a = sns.violinplot(df.smoking, df.time, hue=df.sex1, split=True)
plt.title("Smoking vs Time Segregated by Gender", fontsize=14)
plt.xlabel("Smoking", fontsize=12)
plt.ylabel("Time", fontsize=12)
plt.show()

在不吸烟的人群中,男性和女性的分布是一样的。另一方面,就吸烟者而言,男性和女性的分布非常不同。大多数女性处于大约 0 到 140 的狭窄范围内。而男性人口的范围从-50 到 350。
现在我想看看‘射血分数’和‘死亡’所分隔的‘时间’之间的关系。
sns.lmplot(x="ejection_fraction", y="time",
hue="death", data=df, scatter_kws=dict(s=40, linewidths=0.7,
edgecolors='black'))
plt.xlabel("Ejection Fraction", fontsize=12)
plt.ylabel("Time", fontsize=12)
plt.title("Ejection fraction vs time segregated by death", fontsize=14)
plt.show()

这个图暂时没有提供太多的信息。但是你可以看到回归线和置信带。正如所料,置信带在数据密度较高的中间较窄,在数据密度较低的两侧较宽。
接下来的剧情,我们再来看一个男女人口的对比。“时间”如何随“年龄”而变化:
fig = plt.figure(figsize=(20, 8), dpi=80)
g = sns.lmplot(x='age', y='time',
data = df,
robust = True,
palette="Set1", col="sex1",
scatter_kws=dict(s=60, linewidths=0.7, edgecolors="black"))
for ax in g.axes.flat:
ax.set_title(ax.get_title(), fontsize='x-large')
ax.set_ylabel(ax.get_ylabel(), fontsize='x-large')
ax.set_xlabel(ax.get_xlabel(), fontsize='x-large')

注意这里的回归线,对于男性人口,回归线要陡得多。随着“年龄”的增长,“时间”也在减少。
在上面的大多数图中,我们试图找出变量之间的关系。显示所有变量之间相关性的热图非常有用。热图用于机器学习的特征选择,也用于数据分析以了解变量之间的相关性。
plt.figure(figsize=(10, 10))
sns.heatmap(df.corr(), annot=True, linewidths=0.5, cmap="crest")
plt.show()

您将在下一节看到这些相关性的使用。
死亡预测
利用数据集中的所有变量,我们可以训练一个机器学习模型来预测死亡。对于这个项目,我将简单地从 scikit_learn 库中导入一个机器学习模型并使用它。
数据准备
在这个数据集中,所有的列最初都是数字,这是机器学习模型所需要的。我们在开始创建了两个带有字符串的列。这些列需要删除。
df = df.drop(columns=['sex1', 'death'])
另外,我将在数据集上使用 dropna()函数。数据集非常干净。但是万一有空值,它会删除那些行。
df = df.dropna()
正如我之前提到的,上面的相关变量可以用于机器学习的特征选择。请注意热图中 DEATH_EVENT 和热图最后一列中的其他变量之间的相关性。死亡事件与“贫血”、“糖尿病”、“性别”、“吸烟”、“肌酸酐磷酸激酶”之间的相关性非常低。
我将简单地从数据集中删除这些列。
df = df.drop(columns=['anaemia', 'diabetes', 'sex', 'smoking',
'creatinine_phosphokinase'])
将数据集中的所有变量放在同一个尺度上是个好主意。看,不同的变量有不同的范围。像“血小板”有一个非常高的范围。另一方面,血清肌酸酐具有非常低的范围。为了使所有的连续变量范围相似,我将每个变量除以它们的最大值。
在此之前,我将把数据集 df 复制到变量 df2 中,以保持原始值不变。
df2 = dfcontinuous_var = ['age', 'ejection_fraction', 'platelets', 'serum_creatinine', 'serum_sodium']for i in continous_var:
df2[i] = df2[i]/max(df2[i])
分类变量不需要改变。它们已经是 0 或 1 值。
这个机器学习模型将用于预测死亡事件。所以,DEATH_EVENT 是输出变量。
y = df2['DEATH_EVENT']
我将使用其余的变量作为输入变量。DEATH_EVENT 从将成为输入变量列表的 df2 中删除。
X = df2.drop(columns=['DEATH_EVENT'])
训练和测试数据的分离
训练模型前的最后一步。通常,在机器学习中,一部分数据与训练数据是分开的。以便在训练模型之后,您可以使用模型看不到的一些数据来检查模型。但是标签或者输出是你知道的。
scikit_learn 库为此提供了一个 train_test_split 函数。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=22)
使用决策树分类器
我在这个例子中使用了决策树分类器。下面是分类器和结果:
clf_tree = DecisionTreeClassifier(random_state=21, max_depth = 7, max_leaf_nodes=6).fit(X_train,y_train)
y_pred = clf_tree.predict(X_test)
print("Accuracy:",metrics.accuracy_score(y_test,y_pred))
print("Precision:",metrics.precision_score(y_test,y_pred,pos_label=0))
print("Recall:",metrics.recall_score(y_test,y_pred,pos_label=0))
print("F Score:",metrics.f1_score(y_test,y_pred,pos_label=0))
print("Confusion Matrix:\n",metrics.confusion_matrix(y_test,y_pred))
输出:
Accuracy: 0.84
Precision: 0.9215686274509803
Recall: 0.8545454545454545
F Score: 0.8867924528301887
Confusion Matrix:
[[47 8]
[ 4 16]]
准确率 0.84 或者 84%。还有,F 分 0.89。F 值越接近 1,模型越好。
现在,如果你知道一个人的“年龄”、“射血分数”、“高血压”、“血小板”、“血清肌酸酐”、“血清钠”、“时间”,你就可以预测这个人是死是活。这个预测几乎有 84%是正确的。
结论
在本次演示中,我试图向您展示一些技术来理解该数据集并运行预测模型。有许多不同的方法来完成探索性数据分析任务。这是我对这次演示的选择。
欢迎在推特上关注我,喜欢我的 T2 脸书页面。
更多阅读:
Python 中的探索性数据分析和可视化

Firmbee.com在 Unsplash 上拍照
在本文中,我将使用 Python 来探索和可视化经典的泰坦尼克号数据。
这个分析的重点是回答这个问题——“泰坦尼克号幸存者的特征是什么?”。
泰坦尼克号的数据可以从的 Kaggle 网站下载。我将使用 train.csv 进行数据探索,其中包含了解泰坦尼克号幸存者特征所需的生存数据。
我们先来看看数据是什么样子的。
- 快速浏览数据


在列车数据中,有 891 名乘客,平均存活率为 38%。年龄范围从 0.42 岁到 80 岁,平均年龄为 30 岁。至少 50%的乘客在泰坦尼克号上没有兄弟姐妹/配偶,至少 75%的乘客在泰坦尼克号上没有父母/孩子。费用变化很大。
让我们将乘客 Id、Survived 和 Pclass 的类型改为 string。

对于 fare 需要注意的一点是,最小值是 0。会不会是数据错误?

以上是票价为 0 美元的乘客名单。我抽查了一些乘客,看看 0 美元的票价是否是有意的。
根据 www.encyclopedia-titanica.org 的说法,托马斯·安德鲁斯是皮尔里勋爵的侄子,皮尔里勋爵是哈兰德·沃尔夫(泰坦尼克号的建造者)的主要所有者。他持 112050 号赠票登机。
威廉·亨利·马什·帕尔先生是一名电气工程师,被选中监督泰坦尼克号首航的顺利进行。
约翰·维尔赫姆·亨里克·特伦奎斯特先生和他的美国航运公司同事(小威廉·卡胡恩·约翰逊、阿尔弗雷德·约翰逊、莱昂内尔·伦纳德)在他们的泰坦尼克号上被给予三等舱,以便返回纽约(机票号码 370160),在那里他们可以继续工作。
根据这些信息,0 美元的票价似乎不是数据错误。
拥有相同机票号码的乘客似乎属于同一个旅行团。我可以为团队旅行创建一个布尔变量,看看团队旅行的人是否更有可能幸存。这将在特性工程文章中讨论。

~20%的年龄数据缺失,77%的客舱数据缺失,0.2%的上船数据缺失。我们需要在建模前处理缺失的数据。这也将在特性工程文章中涉及。
2。数值变量

至于箱线图,幸存者和受害者在年龄和 SibSp 上有相似的四分位数。与受害者相比,幸存者更有可能有父母/孩子在泰坦尼克号上,并且有相对更贵的船票。
箱线图通过四分位数提供数字数据的快速视图。让我们使用直方图来检查数据分布,以发现更多的模式。

注意,当比较两组数据的分布时,最好使用相对频率而不是绝对频率。以年龄为例,绝对频率直方图表明,20-30 岁年龄组的受害者比幸存者多得多。这是真的吗?

在年龄相对频率直方图中,真正突出的是年龄组< 10. Children were more likely to survive compared to victims among all age groups.
In the earlier step to check missing values, we found ~20% of Age data is missing. The histograms for Age above are based on non na values. Does the missing data in Age contain any useful information?

From the pie plots, we can tell that passengers with missing age were more likely to be victims. If this view of the pie plots isn’t very clear, let’s try a different way to slice the data (see below).

Regarding feature engineering for Age, I’ll probably create a categorical variable including categories for Children, Adult, Senior and Missing Values respectively.
Let’s take a look at the histograms for the four numerical variables based on the relative frequency.

3。分类变量
让我们快速浏览一下分类变量。



在前面的步骤中,我们发现 77%的客舱数据丢失,0.2%的装船数据丢失。我将用占位符' _n/a '替换丢失的值,并且只保留 Cabin 中的第一个字母。


也许更好的清理客舱数据的方法是用共享相同机票号码的乘客的客舱号码替换丢失的值。调查那些在一个区域中有多个机舱号码的人,看看是否有任何有意义的模式,这也是很有趣的。我将把这个留到特性工程文章中。
在对数据进行一些简单的清理后,我们可以使用柱状图检查分类变量中的存活率。

至于 https://www.kaggle.com/c/titanic/data 的的可变音符,pclass 是社会经济地位(SES)的代理。从柱状图中,我们可以看出,SES 较高的乘客更有可能幸存。
此外,女性比男性更有可能存活下来。是因为整体乘客的分布偏向女性吗?其实恰恰相反。在快速浏览分类变量时,我们看到 65%的乘客是男性,35%是女性。
舱室有很多缺失值,所以不同舱室之间存活率的比较可能意义不大。在特性工程之后,我将再次讨论这个问题。
在瑟堡上船的乘客比在皇后镇和南安普顿上船的乘客有更大的生还机会。没有客舱号码的乘客 100%生还,但乘客数量只有两个。
总结一下,以下是幸存者与受害者相比的特征。
- 幸存者更可能有父母/孩子在泰坦尼克号上,并且有相对更贵的船票。
- 与所有年龄组的受害者相比,儿童更有可能幸存。
- 失踪年龄的乘客不太可能是幸存者。
- 具有较高 pclass (SES)的乘客更有可能存活。
- 女性比男性更有可能存活下来。
- 在瑟堡上船的乘客比在皇后镇和南安普顿上船的乘客有更大的生还机会。
我还发现一些变量需要进一步清理/转换。
- 年龄:给定年龄组< 10 岁和缺少年龄的乘客的有用信息,我将为年龄创建一个分类变量来表示这些类别。
- 客舱:77%的客舱数据丢失。一些没有客舱数据的乘客可能与有客舱号码的乘客共享同一张机票。我可以用这个来恢复一些机舱数据。
- 客舱计数(新功能):一些乘客有多个客舱号码。可能还有其他信息需要调查。
- 票价:票价与存活率正相关。正如我们之前看到的,一些乘客持赠票登机。我应该对车费做些什么吗?我需要对此多加考虑。
- 旅游团队(新特性):我可以使用门票号码为旅游团队创建一个布尔变量,看看人们在团队中旅行是否更有可能幸存。
- 名字:这是我唯一没有分析的变量。名字里有什么信息吗?我需要对此多加考虑。
感谢阅读。我希望这篇文章对你有所帮助。如果您有任何问题或建议,请留下评论。
在下一篇文章中,我将使用 Python 对大量数据进行特征工程,为构建机器学习模型做准备。请继续关注。😃
使用 Python 进行探索性数据分析的 My Goto 过程
探索新数据集的结构化方法

EDA 是什么?
EDA 或探索性数据分析是检查和理解数据集结构的过程。这是任何机器学习项目的关键部分,它是你工具箱中的工具,允许你接近你从未见过的数据,并适应各种特征。
我发现许多人在没有正确执行 EDA 的情况下就直接进入他们的数据。尤其是在他们做了几个项目并认为他们知道自己在做什么之后。对我来说,这是关键的第一步,它揭示了数据中如此多的隐藏宝石,是不可或缺的。
在这个过程中,我将使用 Kaggle 上的银行数据集。
我的 EDA 方法
本文描述了我用来执行 EDA 的一般过程。根据数据的类型、复杂性和混乱程度,它会因数据集而异。然而,这些步骤对于所有数据集通常是相同的。
- 基础探索
- 检查空值和重复值
- 处理异常值
- 将数据可视化
基础探索
从导入所需的包开始。这些是我所有新探索的默认设置。我倾向于发现seaborn是在matplotlib之上的一个方便的包装器,有助于更快地构建可视化。
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
查看大小、列和示例行
在我导入数据后,有几个来自pandas的函数我会先运行。让我们过一遍。
df.shape
(4521, 17)
我们可以从输出中看到,有带有17列的4,521观察值。
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4521 entries, 0 to 4520
Data columns (total 17 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 age 4521 non-null int64
1 job 4521 non-null object
2 marital 4521 non-null object
3 education 4521 non-null object
4 default 4521 non-null object
5 balance 4521 non-null int64
6 housing 4521 non-null object
7 loan 4521 non-null object
8 contact 4521 non-null object
9 day 4521 non-null int64
10 month 4521 non-null object
11 duration 4521 non-null int64
12 campaign 4521 non-null int64
13 pdays 4521 non-null int64
14 previous 4521 non-null int64
15 poutcome 4521 non-null object
16 y 4521 non-null object
dtypes: int64(7), object(10)
memory usage: 600.6+ KB
pandas导入数据的方式是给出数据类型的最佳猜测。如果它们没有正确导入,您可以在以后更改它们。在我上面的例子中,看起来大多数数据类型是integers和objects或者字符串。
接下来,显示第一行和最后几行数据。
df.head()
和
df.tail()
查看统计摘要
有一个超级强大的命令,在一个函数中,df.describe()可以给你一个数据的鸟瞰图。它将显示计数,平均值,标准差,最小值,最大值,等等。有了这些数据,你就能很好地意识到你面临的是什么;然而,以后将它可视化会增加你的分析。
# Without any parameters passed into the describe function, it will return numerical only.
df.describe().round(2)
age balance day duration campaign pdays previous
count 4521.00 4521.00 4521.00 4521.00 4521.00 4521.00 4521.00
mean 41.17 1422.66 15.92 263.96 2.79 39.77 0.54
std 10.58 3009.64 8.25 259.86 3.11 100.12 1.69
min 19.00 -3313.00 1.00 4.00 1.00 -1.00 0.00
25% 33.00 69.00 9.00 104.00 1.00 -1.00 0.00
50% 39.00 444.00 16.00 185.00 2.00 -1.00 0.00
75% 49.00 1480.00 21.00 329.00 3.00 -1.00 0.00
max 87.00 71188.00 31.00 3025.00 50.00 871.00 25.00
检查分类值
以下函数在处理分类数据时非常方便。它首先只选择类型为object的列;在我们的例子中,所有的文本字段都是绝对的。然后,它遍历这些列中的每一列,并打印每一列的value_counts。
# get categorical data
cat_data = df.select_dtypes(include=['object'])
# show counts values of each categorical variable
for colname in cat_data.columns:
print (colname)
print (cat_data[colname].value_counts(), '\n')
下面是单个列的示例输出。注意总结分类数据有多棒。
marital
married 2797
single 1196
divorced 528
Name: marital, dtype: int64
检查空值和重复值
空值和重复值最终都是机器学习模型中的一个问题,所以让我们检查并处理它们。
空值
首先,我从测试空值开始。要知道你想如何处理他们,需要一点探索和解释。它可以是当数量很少时简单地删除它们,用默认值如0填充它们,或者基于相邻值填充它们。基于相邻值填充它们对于时间序列数据来说是很常见的,在这种情况下,您可以使用前一个和后一个值的平均值来填充缺失值。熊猫的fillna()功能得到了广泛的支持,你可以在这里阅读。
在这个例子中,我将探索是否有,并把它们从数据集中删除。
# check for nan/null
df.isnull().values.any()
# count of nulls per column
df.isnull().sum()
放下它们
# Drop NULL values
df.dropna(inplace=True)
重复值
接下来,是寻找重复的。重复值类似于空值,因为它们需要被解释为真正干净、有用的数据或错误的数据。重复也可能是机器学习中的一个问题,因为它们可能会导致您的模型过度偏向不属于真实数据集的观察值。您可以使用下面的代码来检查重复项或删除重复项,如下所示:
len_before = df.shape[0]
df.drop_duplicates(inplace=True)
len_after = df.shape[0]
print(f"Before = {len_before}")
print(f"After = {len_after}")
print("")
print(f"Total Removed = {len_before - len_after}")
Before = 4521
After = 4521
Total Removed = 0
在这种情况下,没有重复的行或空值,但是对于您的用例,检查并适当地处理它们是非常重要的。
处理异常值
离群值是数据中另一个极其常见的问题。如果异常值是数据集中的良好观察值,或者是错误,则需要对其进行评估。首先,你可以测试有多少。对于正态分布的数据,最常用的方法是寻找任何位于+/- 3 标准偏差之外的观察值。如果你还记得你的统计课,68–95–99.7 规则。该规则规定,正态分布中 99.7%的数据位于平均值的三个标准偏差内。当您的数据高度向左或向右倾斜时,这将不是真的。你可以利用箱线图或直方图来检验正态性。我将在下面讨论这个问题。
def get_outliers(df):
'''Identify the number of outliers +/- 3 standard deviations.
Pass this function a data frame and it returns a dictionary'''
outs = {}
df = df.select_dtypes(include=['int64'])
for col in df.columns:
# calculate summary statistics
data_mean, data_std = np.mean(df[col]), np.std(df[col])
# identify outliers
cut_off = data_std * 3
lower, upper = data_mean - cut_off, data_mean + cut_off
# identify outliers
outliers = [x for x in df[col] if x < lower or x > upper]
outs[col] = len(outliers)
return outs
然后将dataframe传递给函数,返回异常值的数量。
get_outliers(df)
{'age': 44,
'balance': 88,
'day': 0,
'duration': 88,
'campaign': 87,
'pdays': 171,
'previous': 99}
移除异常值
如果您选择从数据集中删除异常值,这里有一个简单的方法。从scipy.stats开始,你可以使用zscore函数来识别异常值,类似于上面的方法很容易:
from scipy import stats
# build a list of columns that you wish to remove ouliers from
out_list = ['balance', 'pdays', 'duration']
# overwrite the dataframe with outlier rows removed.
df = df[((np.abs(stats.zscore(df[out_list])) < 3)).all(axis=1)]
将数据可视化
我喜欢把我的想象分成两个不同的部分。明确地首先仅利用单变量图或图,例如关于单个变量的直方图和箱线图。然后,我倾向于将不同变量的二元(或多元)图分层。这个过程帮助我将分析分解成对数据理解的层次。
单变量图
单变量图就像它听起来一样——一个针对单个变量的图。让我们从查看数据集中所有数值变量的箱线图开始。让我们从两个最常见的开始,一个是箱线图,一个是直方图。
箱线图
箱线图采用单个变量,并显示关于数据如何在其四分位数中分布的信息,这实质上意味着将数据分成四分之一。这个简单的图形包包含了很多信息,可以作为一个方便的工具来比较两个或更多不同的分类子段。下例显示了方框图中的duration变量,然后用分类模型中的y' variable, in this case they’变量作为目标对其进行切片(是/否)。
plt.figure(figsize=(7,6))
sns.boxplot(x="y", y="duration", data=df, showfliers=True)

作者图片
利用该图,我们可以看到中位数(方框中心的线)以及四分位数范围(IQR),或者方框的上部和下部作为一个整体,对于yes值来说大于no值。
一篇关于如何解读箱线图的关于数据科学的精彩文章:理解箱线图
直方图
直方图根据值出现的频率显示单个变量在“箱”或数据组中的分布。那些不熟悉它们的人一开始可能会觉得阅读困难,因为它们可能会与条形图混淆。然而,一个条形图在 X 轴和 Y 轴上绘制了两个变量,而直方图中的 X 轴和 Y 轴是容器,Y 轴是容器中的计数。**
sns.histplot(x='age', data=df, bins=20, kde=True)

作者图片
当您查看用直方图绘制的age变量时,您可以看到数据集中年龄在 30 到 40 岁之间的人数出现频率最高。因为直方图不是居中的,所以我们称之为偏斜度。在这种情况下,歪斜被称为 右歪斜 ,因为右尾较长。
二元图
相关矩阵
您需要运行的绝对二元(或多元)图是相关矩阵。这是一个矩阵,显示了数据集中所有变量之间的相关性。它将帮助您识别哪些变量是强相关的,例如简单的线性回归或不相关的变量,以及当您尝试使用多个特征来增加模型性能时的情况。
corr = df.corr()
f, ax = plt.subplots(figsize=(12, 8))
sns.heatmap(corr, annot=True, square=False, ax=ax, linewidth = 1)
plt.title('Pearson Correlation of Features')

作者图片
在大多数情况下,除了pdays和previous之间有一个0.62正相关值之外,没有任何强相关变量。了解为什么会这样,以及它们是否与类似的行为有关,将会很有价值。
配对图
接下来是一个奇妙的工具,叫做配对图。类似于相关矩阵,但是给出了每个 X 和 Y 对的散点图。对角线下方是变量的 KDE 或直方图。
g.fig.set_size_inches(12,12)
g=sns.pairplot(df, diag_kind = 'auto', hue="y")

作者图片
只需一行代码,您就可以快速、直观地评估关联矩阵中显示的关系。我们现在可以直观地看到,变量pdays和previous之间似乎没有太大的相关性。
附加图
从这里,您可以创建任意数量的视觉效果来帮助您更好地理解数据。由你来决定数据中有什么故事。我在 Github 上的笔记本里还有很多其他的例子。
结论
EDA 是一种强大的技术,每次开始处理新的数据集时,您都应该能够熟练掌握它。除了这些基础知识,还有很多其他的技术可以添加进来。在投入机器学习之前,走出去,探索并了解你的数据。
如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。一个月 5 美元,让你可以无限制地访问成千上万篇文章。如果你使用我的链接注册,我会赚一小笔佣金,不需要你额外付费。
参考
使用 Python 进行探索性数据分析—第 1 部分
让您开始使用 Python 和 Pandas 分析数据的模板

数据科学没有秘方。不要认为每个数据集都有一个模板可以遵循。没有。
我将在这一系列文章中提出的只是一个建议,一个开始的地方。显然,从那里开始,您将被您的数据所牵引,以执行其他符合您的项目需求的检查。它不应该被理解为一个要遵循的模型或一套规则,而只是让你行动起来并帮助你从数据中提取第一个见解的东西。
摘要
探索性数据分析(EDA)是理解数据集并从中提取初步见解的艺术,因此您可以为数据科学流程的下一阶段做准备,例如数据清理、数据格式化、要素工程和建模。
本系列包括以下主题:
- 库导入和加载数据
- 检查数据类型
- 查找空值或缺失值
- 描述性统计剖析
- 单变量分析
- 相关
- 多变量分析
数据集和库
当我创建一些例子时,我喜欢使用 seaborn 包中的数据集提示,因为我发现它真的很有教育意义。我们有不同的数据类型——浮点数、整数、字符串、类别——这有助于我们展示概念。所以我们来装吧。
我通常还导入其他基本模块来帮助我处理数据。
# Basics
import pandas as pd
import numpy as np# Visualizations
import seaborn as sns
import matplotlib.pyplot as plt
import plotly.express as px# Loading the dataset
df = sns.load_dataset('tips')
现在,加载数据集本身就是分析的一部分。在这篇文章中,我有一组很好的代码片段,可以帮助你在将数据加载到 Python 会话时更上一层楼:
https://medium.com/gustavorsantos/go-next-level-with-pandas-read-csv-29e3c207a66d
首先要做的是查看数据集。重要的是实际查看它,如果导入顺利,查看数据集是大还是长,了解每个变量中的内容。
这是用head完成的,带来数据集的前 5 行。
df.head()

数据集提示的 df.head()。图片由作者提供。
因此,我们看到数据显然是好的,它被正确地导入,至少在前几行没有奇怪的值。如果你想查看最后 5 行,你也可以使用df.tail()。
检查数据类型
了解数据类型至关重要。为什么?
软件中的数据类型对软件如何处理数据起着信号的作用。(Bruce,Peter 和 Bruce,Andrew——数据科学家实用统计,2017 年)
当你查看数据集头部时,你的大脑会自动发现 total_bill、tip 和 size 是数字,其他变量是文本,其中一些可能是二进制的(只有两个可能的值)。因此,您知道数字列可以进行计算。但是如果我们不检查计算机是否正确理解了它们,例如,当你试图为这些数字创建统计数据时,你肯定会看到错误。
有一些方法可以完成这项任务。
# Info will show the shape and data types.
df.info()

df.info()信息。图片由作者提供。
请注意,它带来了一些有用的信息,如行数、列数、数据类型,以及是否有空值(我们在这里没有看到空值!).
# Another way is to look at it separately#Shape
df.shape
(244, 7) #(number of rows, number of columns)#Types
df.dtypestotal_bill float64
tip float64
sex category
smoker category
day category
time category
size int64
dtype: object
查找空值或缺失值
我们不是刚看过吗?
是的,我们看到现在有了。但也有可能。因此,我将在这里展示一些检测这些值的好方法。
第一种方法是最基本的。我们运行一个返回布尔值(True 或 False)的命令,知道 Python 中 True = 1 和 False = 0,我们可以对这些值求和。当我们看到一个大于零的值时,这意味着数据丢失。
# Checking for missing values
df.isnull().sum()total_bill 0
tip 0
sex 0
smoker 0
day 0
time 0
size 0
dtype: int64
视觉方式也很有趣。我们可以使用 seaborn 的热图来查看数据丢失的地方。我在那里插入了一行 NAs,以便您可以看到它在视觉效果中的样子。
# Heatmap of null values
sns.heatmap(df.isnull(), cbar=False)

为示例目的插入的 NAs:请参见“smoker”变量。作者图片
整洁!我个人喜欢热图的视觉效果。这使得查看整体并找出空值或缺失值变得非常容易。
现在,我还加了一些“?”故意的。而那些在你运行df.isnull()的时候是检测不到的。空值并不总是像NaN一样。有时候他们会扮成。或者别的什么。

'?'为此示例添加了空值。图片由作者提供。
检查的一个好方法是使用正则表达式和 Pandas 的字符串包含方法。
# Checking possible null values - [?.,:*] only on text columnsfor col in df2.select_dtypes(exclude='number').columns:
print(f"{col}: {df2[col].str.contains('[?.,:*]').sum()} possible null(?.,:*) values")**[OUT]:** sex: 0 possible null(?.,:*) values
smoker: 0 possible null(?.,:*) values
**day: 5 possible null(?.,:*) values**
time: 0 possible null(?.,:*) values
您可以替换“?”用NaN使用replace的方法。
# If you want to replace the '?' values
df2.replace({'?':np.nan})
一旦您知道丢失的值在哪里,您就可以使用许多技术来替换它们。这将是你必须根据你的项目做出的决定。一个很好的经验法则是,如果它少于数据集的 5%,就应该放弃 NAs。但是你也可以使用平均值,中间值,最频繁值,使用 missingpy 来预测值。对于那个任务,你可以使用df.fillna(value)
最简单的方法就是扔掉它们。
# Find the most common value for variables
most_common_smoker = df.smoker.value_counts().index[0]
most_common_day = df.day.value_counts().index[0]# Fill NA Values with the most common value
df2.fillna(value={'smoker':most_common_smoker, 'day':most_common_day})# Drop the NA values
df2.dropna(inplace=True)
描述性统计剖析
按照流程,一旦您的数据清除了缺失值,就该开始检查描述性统计数据了。有些人喜欢在清除丢失的值之前检查统计数据,因为如果您丢失一些值,您的分布将会改变。但是,如果您只是删除几行,我不认为会有太大的变化。此外,使用干净的数据,您已经可以获得良好的统计数据,显示数据在更改后是如何分布的。
这一部分是非常必要的,这样你就会知道数据是如何分布的,你可以开始得到第一手的见解。只需一个简单的命令,您就可以在屏幕上打印统计数据。如果需要,使用参数include='all'查看分类数据。
# Since the fake NAs were all deleted, I will come back to using df.
df.describe(include='all')

df.describe(include='all ')结果。图片由作者提供。
从这里我们可以读到很多东西。例如:
- 大部分的观察都是在周六和晚餐时间。
- 数据集有更多的男性和不吸烟者。
- 标准差相当高,表明数据是分散的,可能有异常值。
- 平均小费是 2.99 美元。
- 每桌平均人数在 2 到 3 人之间。
我相信你能够看到这些统计数据对理解你的数据有多么强大。
在你走之前
好了,第一部分到此结束。我们已经介绍了一半的内容,现在您已经了解了如何加载数据集、检查数据类型、理解这一点的重要性、找到缺失值以及从数据集中清除缺失值的几种方法。
这篇文章的完整代码。
下一篇文章我们将继续这个过程。
如果这是你感兴趣的内容,请关注我的博客。




浙公网安备 33010602011771号