利用 AutoXGB 进行金融欺诈检测
AutoXGB 与标准 XGBoost 在检测欺诈性信用卡交易方面的对比
XGBoost 由于其通用性和令人印象深刻的性能,已经成为最重要的机器学习算法之一。
在以前的项目中使用过 XGBoost 之后,我一直乐于让它的**实现更快、更好、更便宜。**当我遇到 AutoXGB 时,我的好奇心被激起了,它声称是一个自动化工具,用于简化 XGBoost 模型的训练和部署。
鉴于我在金融服务行业的工作,欺诈是一个很大的问题,这将是一个利用信用卡欺诈数据来评估 AutoXGB 相对于通常使用的标准 XGBoost 设置的表现的绝佳机会。
内容
【1】数据采集与理解【2】处理类不平衡【3】选择性能指标 让 AutoXGB 经受考验【6】最终裁决
点击 此处 查看该项目的 GitHub 回购
(1)数据获取和理解
概观
这个项目使用了来自 Worldline 和机器学习小组(布鲁塞尔大学)在欺诈检测方面的研究合作的信用卡交易数据(在 GNU 公共许可证 下使用的)。**
该数据集是真实世界信用卡交易的现实模拟并且被设计为包括复杂的欺诈检测问题。****
这些问题包括类别不平衡**(只有< 1%的交易是欺诈性的)、数字和(高基数)分类变量的混合,以及与时间相关的欺诈事件。**
数据转换
我们将使用 转换后的数据集,而不是原始数据集,以符合项目目标。基线特征工程的细节不在讨论范围之内,因此这里有一个简短的总结:
- 注明交易是发生在日还是*夜*(I);在工作日或周末。 这样做是因为欺诈模式根据一天中的时间和一周中的日期而不同
- 使用 RFM (近期、频率、货币价值)指标描述客户的消费行为**(例如,平均消费、交易数量)**
- 通过计算一个时间窗口内欺诈案件的平均数量,对与每笔支付终端相关的风险进行分类
预测器和目标特征
在完成特征工程后,我们有一个具有以下特征的数据集:
- TX_AMOUNT :美元交易金额 float
- TX_DURING_WEEKEND :交易是否发生在周末 boolean
- TX_DURING_NIGHT :交易是否发生在夜间 boolean
- ***CUSTOMER _ ID _ NB _ TX _DAY _ WINDOW:每个客户最近 1 天、7 天、30 天的交易笔数[ 整数
- ***CUSTOMER _ ID _ AVG _ AMOUNT _DAY _ WINDOW:每位客户在过去 1 天、7 天、30 天的平均消费金额(美元) float
- TERMINAL _ ID _ NB _ TX _ * DAY _ WINDOW:终端最近 1、7、30 天的交易笔数整数**
- TERMINAL _ ID _ RISK _ * DAY _ WINDOW:过去 1 天、7 天、30 天终端上欺诈交易的平均数量[ 整数
- TX_FRAUD: 指示交易是合法( 0 )还是欺诈(1)【布尔】
转换后的信用卡欺诈数据集示例
从目标变量 TX_FRAUD ,,我们可以看出,我们正在处理一个二元分类任务**。**
具有延迟期的列车测试分离
欺诈检测中要考虑的一个重要方面是延迟期**(又名反馈延迟)。在现实世界中,欺诈交易只有在投诉或调查发生后一段时间**才会为人所知。****
因此,我们需要引入一个连续的延迟期**(例如,一周)来分离训练集和测试集,其中测试集应该在训练集的最后一个事务之后至少一周**发生。****
列车测试划分如下:
- 车组 : 8 周(2018 年 7 月 1 日至 2018 年 8 月 27 日)
- 延迟期 : 1 周(2018 年 8 月 28 日至 2018 年 9 月 3 日)
- 测试集 : 1 周(2018 年 9 月 4 日至 2018 年 9 月 10 日)
列车和测试设备之间的延迟时间图|图片由作者提供
(2)处理阶层失衡
欺诈交易不会经常发生,所以我们手上有一个严重不平衡的数据集也就不足为奇了。
从目标变量的值计数来看,在 550,000+交易中,只有 4,935 个欺诈案例 (0.9%) 。
原始类的值计数:1 =欺诈;0 =合法|作者提供的图片
我们可以使用合成少数过采样技术(SMOTE)** 来处理这种类别不平衡。原始 SMOTE 论文的作者在他们的实现中结合了 SMOTE 和随机欠采样,所以我在这个项目中使用了这种组合。**
具体采样策略如下:
- **进行过采样**以将少数类增加到总数据集的 5%(从最初的 0.9%增加 500%),然后
- ****随机欠采样使多数类的规模为少数类的两倍(即少数类为多数类规模的 50%)
尽管SMOTE作者表明不同的组合给出了可比较的结果,但我选择了 500%/50%组合,因为该论文表明它在石油数据集中的少数样本上给出了最高的准确性。
经过这次采样后,数据集更加均衡,占整个数据集的少数类从 0.9%增加到** 33.3%。**
样本类的值计数:1 =欺诈;0 =合法|作者提供的图片
(3)性能指标的选择
在我们开始建模之前,我们必须决定评估模型性能的理想指标是什么。
典型的是基于阈值的指标,如准确性和 F1 分数。虽然他们可以评估错误分类的程度,但他们依赖于特定决策阈值的定义,例如,概率>0.5 =欺诈。
这些指标对决策阈值的依赖使得对比较不同的模型提出了挑战。因此,更好的选择是无阈值指标**,如 AUC ROC 和平均精度。**
****平均精度将精度-召回曲线总结为每个阈值达到的精度的加权平均值,权重为召回从前一阈值的增加。
虽然 AUC-ROC 更常见,但平均精确度被选为该项目的主要报告指标,原因如下:
- 对于不平衡数据集,平均精度为 比 更具信息量。虽然我们已经应用了抽样来平衡我们的数据,但我觉得仍然存在一定程度的剩余不平衡(67:33 而不是 50:50)
- 过多的假阳性会使欺诈调查团队不堪重负,因此我们希望在低 假阳性率(FPR)值时评估召回(又名敏感度)。使用 PR 曲线(和 AP)相对于 ROC 的优势在于 PR 曲线可以有效地突出低 FPR 值的模型性能。****
照片由 Christiann Koepke 在 Unsplash 上拍摄
(4)基线— XGBoost 和随机搜索 CV
基线模型的设置(XGBoost withRandomizedSearchCV)是我倾向于使用的分类任务的第一线方法。
以下是基线模型的测试集预测结果,其中平均精度的关键度量是 0.776 。训练所用的时间是 13 分钟。
测试集基线 XGBoost 模型的性能指标|作者图片
(5)测试 AutoXGB
与最近兴起的 AutoML 解决方案一致,AutoXGB 是一个库,它从 CSV 格式的表格数据中自动训练、评估和部署 XGBoost 模型。
使用 Optuna 自动完成超参数调整,使用 FastAPI 进行部署。
AutoXGB 由拥有世界首位 4x Kaggle 特级大师头衔的 HuggingFace 研究员 Abhishek Thakur 开发。用他自己的话说,AutoXGB 是一个建立 XGBoost 模型的简单工具。
因此,我热衷于利用他的专业知识来探索和改进我的 XGBoost 模型通常的实现方式。
要安装 AutoXGB,请运行以下命令:
**pip install autoxgb**
AutoXGB 框架大大简化了设置 XGBoost 训练和预测所需的步骤。下面是用于为二进制分类任务设置 AutoXGB 的代码:
下面是 AutoXGB 的测试集结果,其中平均精度的关键指标是 0.782 。训练时间为 9 分钟。
测试集 AutoXGB | Image 按作者的性能指标
(6)最终裁决
结果比较
性能和速度比较|图片由作者提供
与基准分数 0.776 相比,AutoXGB 的平均精度分数略高,为 0.782** 。**
AutoXGB 培训所花费的时间比基准的 13 分钟大约缩短了30%9 分钟。****
AutoXGB 的另一个关键优势是,我们距离将模型作为 FastAPI 端点只差一个命令行**。这种设置减少了建模部署的时间,这是培训时间之外的一个关键因素。**
以下因素可能是推动 AutoXGB 获得更好性能的原因:
- 使用贝叶斯优化和 Optuna 进行超参数调整,这比随机搜索更快,因为它使用来自先前迭代的信息,在更少的迭代中找到最佳超参数
- 根据作者丰富的数据科学经验,仔细选择 XGBoost 超参数(类型和范围)的进行调优****
- 优化内存使用,通过特定的类型转换减少内存使用,例如,将数据类型为
int64
的值转换为int8
(消耗的内存减少了 8 倍)
重要警告
虽然性能指标给了 AutoXGB 一个优势,但它最重要的问题之一是参数设置中粒度控制的损失。
如果您一直密切关注,您可能会意识到,我们没有引入我们为交叉验证申请的训练/测试分割的顺序延迟期(我们应该已经完成)。****
在这种情况下,AutoXGB 不允许我们指定我们想要用作交叉验证(CV)一部分的验证折叠**,因为唯一与 CV 相关的参数是n_folds
(CV 折叠数)。**
这种缺乏控制的另一个问题是,我们不能为 XGBoost 分类器指定评估度量**。**
在基线模型中,我能够将eval_metric
设置为aucpr
(PR 曲线下的 AUC),这与我们的平均精度的主要指标一致。但是,对于二进制分类,AutoXGB 的 XGBoost 分类器中的硬编码评估度量是logloss
。
归根结底,虽然 AutoXGB 等解决方案在简化(并可能改进)XGBoost 实现方面做得很好,但数据科学家需要通过了解幕后发生的事情来了解其局限性。
随意查看 GitHub repo 中的代码,点击 。
在你走之前
欢迎您加入我的数据科学学习之旅!关注我的 Medium 页面和 GitHub ,了解更多精彩的数据科学内容。同时,享受在你的 ML 任务中使用 AutoXGB 的乐趣!
**https://medium.com/geekculture/using-ensemble-regressors-to-predict-condo-rental-prices-47eb7c3d5cd9
资源
AvatarGAN —使用 GAN 生成卡通图像
有没有想过如何生成不属于任何人脸的 bitmoji?看看 GAN 是如何创建这些图像的。
我们大多数人都创建了自己定制的 bitmoji,并在不同的社交媒体应用中使用。这些 bitmoji 是为特定用户定制的。但你有没有想过如何生成不属于任何人脸的 bitmoji?好吧,让我们探索一下甘斯是如何为我们完成这项工作的。
作者对 AvatarGAN 图像的预测
生成对抗网络是当今计算机科学中最有趣的想法之一。GANs 可以从垃圾数据集生成图像。甘是由伊恩·j·古德菲勒于 2014 年开发的。它由两个神经网络组成,这两个网络相互竞争,以使预测更加准确。
生成模型找出输入数据中的特征,能够分析、捕获和复制数据集中的变化,并以无监督的方式生成看起来与输入集相似的新图像。例如,GANs 可以创建类似人脸照片的图像。
来源:https://www.thispersondoesnotexist.com/
所有这些由甘生成的图像都有一个共同的模式。每张脸的眼睛都在同一个坐标上。背景只是一个模糊的随机纹理。如果有多个面,第二个面的形状会非常扭曲。
在我们开始训练之前,让我们先了解一下 GANs 是如何工作的。
生成顾名思义就是试图生成看起来像真图像的假图像。它学习特征的概率 X 。发生器将噪声(随机特征)作为输入噪声。
鉴别器是一个二元分类器,试图区分真实图像和生成器生成的图像。它学习类 Y (真或假)给定特征 X 的概率。概率是生成器的反馈。
发电机学会制造看起来像真的假货。鉴别者学会辨别真假。
训练 GANs 的步骤:
- 定义发生器和鉴别器网络架构
- 训练生成器模型以生成可以欺骗鉴别器的假数据
- 训练鉴别器模型以区分真实数据和虚假数据
- 继续训练几个时期,并保存发电机模型
本质上,我们把随机噪声通过发生器。生成器生成假图像。该输出图像连同来自真实图像数据集的图像流一起被传递到鉴别器。真实和伪造的图像都被提供给鉴别器,鉴别器返回图像真实性的概率。然后,我们从鉴别器输出计算成本函数,并更新两个模型的权重。
噪声→发生器→特性→鉴别器→输出→成本(输出)
甘建筑—作者图片
甘的训练
现在,我们已经完成了 GAN 的基础知识,是时候进行繁重的工作并训练模型了。
GIF via Giphy
1.资料组
我们将在卡通场景上训练我们的甘,这是一组随机的二维卡通化身图像。这些漫画有 10 个艺术类别、4 个颜色类别和 4 个比例类别,所以我们有很多可能的组合。我们将使用包含 100,000 个随机选择的卡通图像的数据集。
下一步是读取所有图像。由于我们有许多图像要读取和处理,这项任务可能需要一段时间。因此,我们将读取所有的图像,将其转换为 JPG 格式,调整其大小,将其规范化,并将预处理后的图像存储为二进制文件。只执行一次这一系列步骤会更有效。这样我们可以简单地读取处理过的图像数据并快速使用它。我们将为所有图像创建一个 Numpy 数组,并将其保存为一个. npy 文件。我们使用 Numpy 二进制而不是 Pickle,因为该文件非常大,可能会导致某些版本的 Pickle 出现问题。
现在,为了在内存中保存图像,我们将使用 tensor flowTF . data . dataset。Dataset 对象用于编写描述性的高效输入管道。迭代以流的方式发生,因此完整的数据集不需要适合内存。
2.建立模型
这两个模型都使用 Keras 顺序类。
发电机
生成器需要上采样层来从噪声(即种子)生成图像。我们可以使用 UpSampling2D() 和 Conv2DTranspose() 进行上采样。
UpSampling2D 只是利用一些上采样技术对图像矩阵进行简单的放大。我们通常使用最近邻法或双线性上采样法。所以没有机器在这里学习。向上采样 2D 的好处是它很便宜。而 Conv2DTranspose 层是一个卷积运算,学习几个类似于常规 Conv2D 层的滤波器。转置层简单地交换向后和向前传递,保持其余的操作相同。Conv2DTranspose 也将对其输入进行上采样,但关键的区别在于,该模型应该了解什么是该作业的最佳上采样功能。
第一层是密集层,其输入是种子噪声。然后我们对其进行多次上采样,直到尺寸为28x28x1。对于生成器,我们将使用 LeakyReLU 激活函数,对于最后一层,我们将使用 tanh。
来源: DCGAN
让我们尝试绘制生成器神经网络生成的图像。
训练前由生成器根据随机噪声生成的图像—作者提供的图像
鉴别器
鉴别器网络是一种简单的卷积神经网络图像分类器。
让我们检查一下我们的鉴别器模型的输出。
输出: tf。张量([0.50059265]],shape=(1,1),dtype=float32)
它返回概率得分。
3.损失函数
我们将使用二元交叉熵损失函数。BCE 成本函数有两个部分,一个与每个类相关。当标签和预测相似时,该值接近零,但当标签和预测不同时,该值接近无穷大。
二元交叉熵损失函数—作者图片
让我们分解方程,分析每一部分。
作者图片
作者图片
鉴别器损失量化了鉴别器模型能够区分真实和伪造图像的程度。它将鉴别器对真实图像的预测与 1 的数组进行比较,并将鉴别器对虚假图像的预测与 0 的数组进行比较。
发电机损耗量化了它欺骗鉴别器的能力。直观地说,如果生成器运行良好,鉴别器会将假图像分类为真实图像(或 1)。这里,我们将把鉴别器对生成图像的判定与 1 的数组进行比较。
生成器和鉴别器模型都使用 Adam 优化器和相同的学习速率和动量。它们的优化器是不同的,因为我们分别训练两个不同的网络。
4.培训渠道
现在,我们已经定义了培训渠道的主要组成部分,让我们转到培训部分。下面的函数就是神奇之处。
注意 tf.function 注释的使用。这可以完善功能并提高性能。
这两个神经网络必须在两个单独的过程中独立训练。因此我们为梯度定义了两个独立的损失函数和独立的更新。在鉴别器模型的反向传播期间,有必要仅应用鉴别器的梯度来减少鉴别器的损耗,并且仅更新该模型的权重。如果创成式模型的权重也同时更新,模型将不会学习。
5。训练模型
训练数据集应该被规范化。两个类别的样本数量必须相等。对于鉴别器训练集,输入图像将是 x 和 y,对于真实图像包含值 1,对于生成的图像包含值 0。而对于生成器训练集,x 包含随机噪声(种子), y 总是 1。这里,生成器的目的是生成如此好的图像,以至于鉴别器被愚弄,并给它们分配一个接近 1 的概率。
既然一切都准备好了,我们开始训练吧。
GIF via Giphy
train(train_dataset, EPOCHS)
查看正在被训练生成卡通图像的模型。
作者图片
拍拍背!我们的模型终于被训练好了,现在是时候保存它了,以便我们将来可以使用它。
generator.save(os.path.join(DATA_PATH, "face_generator.h5"))
氮化镓的应用
现在我们知道了 GAN 的功能,是时候检查它的迷人应用了。在研究中有大量关于 GAN 的用法。
图像到图像的翻译
在 GANs 的帮助下,我们可以进行照片的翻译。 Phillip Isola 在论文中展示了 pix2pix 方法用于许多图像到图像的翻译任务。例如,使用 GANs,我们可以将马的图像转换成斑马,从草图创建彩色照片,彩色黑白图像,等等。
来源: CycleGAN
甘斯安保公司
随着人工智能的兴起,欺诈和网络威胁的风险也在增加。网络威胁会泄露大量机密信息。GANs 可以用来防止*【对抗性攻击】*。这些对抗性攻击使用各种技术来欺骗深度学习架构。GAN 可以创建更多这样的假样本,我们可以通过在假生成的样本上训练模型来轻松地标记它们。
来源: SSGAN
SSGAN 用于执行隐写分析,并检测图像中不应该出现的隐藏编码。GANs 还可用于生成监督用的合成数据。
照片修复
GANs 可用于执行照片修补或斑点填充,即,填充由于某种原因被移除或被破坏的照片的缺失区域。论文上下文编码器:通过修补的特征学习已经描述了使用上下文编码器来执行照片修补。
用于 3D 对象生成的 GAN
吴家俊提出了一种 GAN,可以用来生成三维物体,如枪、椅子、汽车、沙发和桌子。
来源:通过 3D 生成-对抗建模学习物体形状的概率潜在空间
结论
生成器的目标是欺骗鉴别器,而鉴别器试图区分真假。两个模型都从彼此的竞争中学习。最后,假的看起来像真的。生成数据的想法开启了新的潜力,但不幸的是也带来了巨大的危险。
如果你坚持了这么久…谢谢!我希望这对你来说是一次学习经历。如果你喜欢这篇文章,请与你的朋友和同事分享。如果你觉得有用或有任何后续问题,请给我留言。
对于那些没有阅读或者没有亲自完成教程的懒人,这里有一个源代码的链接。
https://github.com/aakashjhawar/AvatarGAN
避免使用函数式编程的调试器
用纯函数和跟踪器调试你的代码
照片由 Fotis Fotopoulos 在 Unsplash 上拍摄
介绍
我们的目标是有一个纯函数的代码库,我们可以用一个 tracer 来修饰它。通过将这个装饰器应用到纯函数,我们可以在不使用笨重的调试器的情况下调试代码。这减少了开发人员的痛苦,因为调试器通常是乏味且难以使用的。
什么是纯函数?
严格地说,在一个函数式编程范例中,所有函数都是纯函数。它们是什么,我们如何编码它们,它们为什么有用?
在此之前,让我们回顾一下数学函数。
数学函数
数学函数,如cos(x),
返回单个值。给它一个x
,它返回x
的余弦值。相同的x
值将总是返回相同的结果。
Python 函数不同于数学函数。Python 函数不仅仅计算值;它可以改变程序的状态。
Python 函数可以设置影响不同函数结果的全局变量。例如,一个函数可以改变另一个函数使用的列表。这种突变是该功能的副作用。
副作用使得编程和调试更加困难。你不需要仅仅考虑一个特定的功能在做什么;您还必须考虑其他函数之前可能做了什么,这会影响程序的状态。
纯函数消除了一些意想不到的副作用。
纯函数
纯函数是数学函数的计算模拟。这意味着当你传递一个值给一个函数时,它将总是产生相同的结果,没有任何副作用。
让我们用一个非纯计数器和一个纯计数器来说明这个概念。这个计数器是迷信的,所以它将跳过 13。
带测试的不纯计数器
对于非纯函数——为了测试 13 的条件跳过,我们必须注意count .
的全局状态。为了检查这种行为,我们必须运行计数器 13 次,看看它是否工作。每次我们调用increment_counter()
都会有将count
增加 1 的副作用。因此,我们必须运行increment_counter()
12 次,让它进入一种状态,在这种状态下count = 13
触发我们正在测试的条件行为。
注意,我们将在本文后面的第 20 行讨论调试器代码breakpoint()
。
当我们将计数器实现为一个纯函数时,测试我们的计数器是否跳过 13 要简单得多。
带测试的纯计数器
对于纯计数器,我们需要做的就是检查我们的条件情况,看看计数器是否跳过 13。不存在需要留意的全球状态,也不存在需要担心的副作用。
如果您只使用纯函数编写项目代码,计算将通过嵌套或组合函数调用进行,而不会改变状态或可变数据。这将允许更容易的调试。此外,因为纯函数对于相同的参数有相同的返回值,所以它们非常适合单元测试。
什么是调试器?他们很差劲吗?
调试器的目标是在受控条件下运行目标程序,允许程序员跟踪正在进行的操作并监控变化。
下面我使用 Python 调试器 pdb 运行了非纯计数器代码。第 20 行上的breakpoint()
触发程序暂停,并向我们显示下一行的代码。我调用count
来查看这个全局变量的状态,并使用n
来运行下一行代码。在这种情况下,我们可以看到每次调用increment_counter
时count
都会增加。
使用调试器检查上述非纯计数器的状态
我们可以看到count
比我们的下一个断言落后了一个,这是意料之中的。此外,我们可以观察到计数从 12 跳到 14。注意计数状态是如何随着每次断言测试而增加的。这看起来像是好的软件设计吗?
像这样一行一行地遍历程序是很慢的。使用调试器重新创建一个状态来测试某个行为可能会很痛苦。
传统的调试器在几个方面都很糟糕。
- 你花了很多时间试图在本地重现错误,这样你就可以附加你的调试器。
- 当您反复缩小原因范围时,您必须多次重现问题。
- 人工调试环境将创建一个与错误最初出现时不同的状态。
- 调试器不跟踪数据或控制流,也不记得过去发生了什么。
开发人员有时会发现自己在为调试器编写代码来解决特定的问题。应该为手边的应用程序编写代码,而不是为正在其上使用的调试器编写代码!
各地的软件开发人员都应该对此感到不安。有更好的调试方法。如果您没有使用调试器,那么您可能通过添加日志记录代码并重新运行程序来进行调试。
这类似于我们的解决方案,使用带有跟踪器的纯函数,以获得更优雅和可重用的调试方法。
纯函数结合跟踪器对于生产中的日志记录非常有用,因为您可以确切地看到什么输入值产生了错误。对于纯函数,如果我们知道我们的输入和输出,我们就知道修复代码中的错误所需要的一切。
使用追踪器调试你的软件
应用跟踪装饰器,而不是使用调试器。
为什么要追踪?
跟踪是模拟程序的执行,一行一行地遍历程序,显示变量是如何变化的。有经验的程序员使用跟踪来调试程序,通常作为调试器的替代品。
“干杯,爱!骑兵来了!”—示踪器
当您知道一个纯函数的输入和输出时,您就知道了调试所需的一切。
跟踪装饰者:@tracefunc
这是作为装饰器编写的跟踪程序,我们将把它应用于我们感兴趣的函数。
tracer.py
这个装饰器打印出传递给tracefunc()
的函数的轨迹。在第 10 行,result
存储了func(*args, **kwargs).
的值,之后生成一个打印语句。此语句报告func.__name__
,args 和 kwargs。
这个装饰器将允许我们看到我们装饰的函数的名字和它的参数,kwargs 和 result。
通过用tracefunc
修饰我们的纯函数,我们生成了调试程序所需的所有日志信息。
对纯函数应用 tracefunc
让我们用我们的 tracer 来装饰这个纯计数器,看看输出。
用 tracefunc 装饰的纯计数器
我们在第 3 行使用@
语法将tracefunc
应用到increment_counter
。
增量 _ 计数器 _ 纯的跟踪
现在,当我们运行这个文件时,我们在第 11 行的测试生成了上面的输出。我们可以看到func.__name__
,12 作为自变量,14 作为结果。
通过对这个纯函数应用追踪器,我们生成了我们想要测试的行为的清晰日志。专柜确实很迷信。
结论
通过用追踪器装饰纯函数,您获得了一个优雅的、可重用的模式来调试您的程序。装饰是增强功能行为的有力工具。有了好的代码(纯函数)和跟踪器,您可以避免使用调试器和管理状态来调试程序的痛苦。
请随意将tracefunc
复制到您的代码库中,或者您可以尝试编写自己的跟踪装饰器!
用 Jupyter 内核避免 DLL 地狱
斯特凡诺·波利奥在 Unsplash 上拍摄的照片
类似于 Docker 容器,内核是确保你的 ML 应用程序可以在任何地方运行的关键。
介绍
以前在 Windows 上有一个叫做 DLL Hell 的东西。简而言之,过去你电脑上的所有应用程序都共享已安装库的相同版本——所以每次你安装一个新的应用程序,你都冒着更新底层库和破坏先前安装的应用程序的风险。
对于习惯于容器化他们的应用程序的 Docker 爱好者来说,这是很好理解的:运行时环境需要虚拟化以便应用程序可以移植,并且对特定版本库的依赖可以被隔离到容器本身。这确保了应用程序可以在任何人的计算机上运行,包括云。
蟒蛇
Anaconda 个人版让打包和分发 ML 应用变得轻而易举:
https://www.anaconda.com/products/individual
中的到底是什么由你决定——你可以使用 Python 或 R ,并包含你的项目所需的任何库/框架。当你完成后,你就有了一个可移植的内核,它可以跟随你的应用程序,并确保它总是看到相同的运行时环境。不管你的同事使用的是 Windows 、 MacOS 还是 Linux 都没关系——你可以轻松地与他们分享你的项目,并确保他们在他们的机器上看到的结果与你在自己的机器上看到的一样。
康达环境
这个食谱背后的秘方是环境的概念。安装完 Anaconda 之后,你将能够使用一个叫做 Anaconda Prompt 的漂亮的命令窗口。在管理环境时,它会很快成为你新的最好的朋友。
这是你可以用康达环境做的所有酷事情的官方清单,但在下一节,我将带你经历一个典型的场景:
GPU 上的张量流
斯蒂芬·科斯马在 Unsplash 上拍摄的照片
或者…曲速!在 CPU 上构建神经网络是所以昨天,所有酷孩子都在使用支持 CUDA 的 Nvidia GPU来运行他们的!比方说,我们有一些新的想法来提高我们一直在做的 CNN 的准确性。首先要做的是旋转一个 conda env 并加载我们需要的所有东西(从 Anaconda 提示符):
conda create -n tf-warp-speed tensorflow-gpu=2.1 python
我们将把我们的 conda 容器命名为 tf-warp-speed ,并用支持 GPU 的版本 TensorFlow 对其进行初始化。我们现在有了一个沙盒来玩——从现在开始我们在里面做的任何事情都不会影响我们所依赖的操作系统。
步入沙盒很容易,只需激活它:
conda activate tf-warp-speed
我们已经安装了 Python 和支持 GPU 的 TensorFlow 版本,但是您可以在这里为您的项目加载额外的库/框架。
对于这篇文章,我们将使用 rogue 和 version-up(通过pip)tensor flow:
pip install tensorflow-gpu==2.3.1
忽略你可能看到的任何依赖错误,让我们继续让我们的新 conda env 对 Jupyter 笔记本可见!
conda install ipykernel
我们上面安装的是一个交互式 Python 内核,它为 Jupyter 笔记本提供了一个执行后端。现在我们需要给它贴上消费标签:
python -m ipykernel install --user --name=tf-warp-speed
瞧!启动一个 Jupyter 笔记本,进入内核- >改变内核,你应该会看到一个 tf-warp-speed 的选项!选择那个,我们就可以开始了!在新笔记本的代码单元中输入以下内容:
import tensorflow as tf
from tensorflow.python.client import device_libprint("TensorFlow version: ", tf.**version**)
print("TensorFlow built with CUDA: ",tf.test.is_built_with_cuda())
print("GPUs Available: ", len(tf.config.list_physical_devices('GPU')))!python --version
当您执行它 (CTRL-ENTER)时,您应该看到类似这样的内容:
TensorFlow version: 2.3.1
TensorFlow built with CUDA: True
GPUs Available: 1
Python 3.7.9
让我们带它出去兜一圈,看看它能做什么!
import timeitdef cpu():
with tf.device('/cpu:0'):
random_image_cpu = tf.random.normal((100, 100, 100, 3))
net_cpu = tf.keras.layers.Conv2D(32, 7)(random_image_cpu)
return tf.math.reduce_sum(net_cpu)def gpu():
with tf.device('/device:GPU:0'):
random_image_gpu = tf.random.normal((100, 100, 100, 3))
net_gpu = tf.keras.layers.Conv2D(32, 7)(random_image_gpu)
return tf.math.reduce_sum(net_gpu)
# We run each op once to warm up
cpu()
gpu()# Run the op several times
print('Time (s) to convolve 32x7x7x3 filter over random 100x100x100x3 images '
'(batch x height x width x channel). Sum of 100 runs.')
print('CPU (s):')
cpu_time = timeit.timeit('cpu()', number=100, setup="from __main__ import cpu")
print(cpu_time)
print('GPU (s):')
gpu_time = timeit.timeit('gpu()', number=100, setup="from __main__ import gpu")
print(gpu_time)
print('GPU speedup over CPU: {}x'.format(int(cpu_time/gpu_time)))
如果此时一切正常,您应该会看到如下内容:
Time (s) to convolve 32x7x7x3 filter over random 100x100x100x3 images (batch x height x width x channel). Sum of 100 runs.
CPU (s):
6.590599500000053
GPU (s):
0.8478618999999981
GPU speedup over CPU: 7x
等等…什么?!?在 GPU 上运行我的 CNN比我的 CPU 快七倍 ?!?现在那就是的曲速!
共享
在这一点上,我对我的新内核非常兴奋,但是我有一个朋友在 Ebay 上花了太多的钱买了两张 3090 卡,我真的很想听听它是什么感觉。
轻松点。我可以简单地将我的 conda env 导出到一个 yaml 文件中,并通过电子邮件发送该文件(连同我的*。ipynb 文件)给我的朋友。他们可以重新创建我的内核,重新运行我的笔记本,让嘲讽开始(因为我还在运行一台 GTX 1080)!
conda env export > environment.yml
运行完以上程序后,我有了我的 yaml 文件,其中包含了我的朋友构建他们的新 conda env 所需的一切:
conda env create -f environment.yml
结论
使用 Anaconda 来管理您的虚拟环境是维护您的 ML 项目的完整性的关键。库和框架的版本来来去去就像日落一样快——明智的做法是为您的每个项目拍摄运行时环境的快照。
避免带有偏斜计数数据的机器学习模型中的错误
入门
一些常见警告和解决方案的介绍
1.背景
计数数据到处都是**。计数数据听起来很容易处理:它们只是无限的整数,没什么特别的。如果你这样认为,那么你很可能会把它们处理得* 错误* 。为什么本博客旨在为您提供一些在 机器学习(ML) 中使用**计数数据的技巧,帮助您避免一些您以前可能从未注意到的常见错误。****
先说一个简单的问题**。**
假设我们正在开发关于电影观看的 ML 模型,并且有一个名为“用户在最近 6 个月观看的卡通电影计数”的字段。由于每个人对电影的口味不同,所以我们看到的是 0,1,2,3…101 这样的值(没错,看了 101 电影的用户一定是个超级粉丝)。
现在我有个问题要问你:
这个计数数据可能遵循什么统计分布?
如果你的答案是“正态分布”或者“我不知道”,那么恭喜你!我相信这个博客会帮助你。
如果你没有太多的统计学知识,那也没关系。这个博客旨在提供实际操作的 ML 技术**,尽管我也为好奇的读者提供一些统计数据的细节。**
首先我们来看看这个计数数据(一个为了演示而创建的玩具数据)的剧情和数据汇总。正如我们所知,进入一个全新的数据集最快的方法是绘制一些图表。所以我们开始吧:
import numpy as np
import pandas as pd
import plotly.express as px
count = np.concatenate((np.zeros(202),np.repeat(1, 50),np.repeat(2, 40)))
np.random.seed(1)
a=np.random.choice(range(3, 60),90)
b=np.random.choice(range(61, 100),20)
df = pd.DataFrame(np.concatenate((count, a, b, [101])),columns=['val'])
fig = px.histogram(df, x='val',nbins=200)
fig.show()
作者图片
作者图片
现在我确信你不会再假设这是一个正态分布。你可能会注意到数据是…
…高度歪斜右边有一条长尾巴,带着一堆 0 的。****
事实上,如果我们看看中间值,他们中的一半是 0,这表明一半的用户在过去 6 个月中没有看过任何卡通电影。你可能开始同意我的观点(如果你以前不同意),这种数据可能需要在你的 ML 模型中得到适当的处理。是的,这种类型的数据在统计中有一个特定的类别:零膨胀,表示计数数据的分布是稀疏的,有许多 0(本例中为 50%)。
接下来,在第 2 节中,我们将讨论新手在处理作为建模特征(即自变量)的偏斜计数数据时的常见错误。然后在第 3 节中,我们将看到当预测偏斜计数数据是您的目标(即响应变量)时的注意事项。如果你准备好了,那我们就开始吧!
2.作为独立变量的偏斜计数数据
常见错误 1:根据数据的统计数据移除异常值
我们都知道在数据探索之后要做的第一件事是数据清理,其中离群点去除是一个重要的部分。但是,在应用一些常见的异常值剔除方法时,如标准差法和四分位差法,请务必小心。正如您从下面看到的,您可能最终会删除大部分不是异常值的值,因为在这个倾斜的数据中,中值是 0。
- ****错误:使用标准差法- >计算出的异常值百分比为:96.5%(该方法将大部分数据作为异常值)
**# Mistake 1: Standard Deviation Method
# calculate summary statistics
data_mean, data_std = np.mean(df.val), np.std(df.val)
# identify outliers
cut_off = data_std * 3
lower, upper = data_mean - cut_off, data_mean + cut_off# remove outliers
outliers_removed = [x for x in df.val if x > lower and x < upper]
print('the calculated percentage of outliers is:',100*len(outliers_removed)/df.shape[0])**
- ****错误:使用四分位差法- >计算出的异常值百分比为:19.7%(好于标准差法,但异常值仍然过多)
**# Mistake 2: Interquartile Range Method
# calculate interquartile range
q25, q75 = np.percentile(df.val, 25), np.percentile(df.val, 75)
iqr = q75 - q25
# calculate the outlier cutoff
cut_off = iqr * 1.5
lower, upper = q25 - cut_off, q75 + cut_off# identify outliers
outliers = [x for x in df.val if x < lower or x > upper]
print('the calculated percentage of outliers is:',100*len(outliers)/df.shape[0])**
剔除异常值的解决方案:
谨慎使用基于数据分布统计的方法,如均值或中值。我们可以考虑使用像百分点封顶这样的方法(删除大于某个阈值的点,比如 99%)。****
常见错误 2:不做任何事情就将有偏差的计数数据投入线性回归
在执行线性回归模型时,人们通常会忽略使用 OLS 模型的基本假设,比如所有自变量建模后的误差应该服从正态分布。因此,如果你对有偏差的计数数据什么都不做,OLS 模型不会给你可靠的结果。
如果你感兴趣,这背后的一些统计数据(或者你可以跳过这个):
你不需要让自变量遵循正态分布进行线性回归,但为什么你仍然关心你的自变量是否偏斜?原因是:
- 尽管在实际绘制残差图之前很难判断,但有偏差的数据很可能会违反我们上面提到的假设(残差/同方差的正态性),并牺牲模型的准确性。
- 重尾可能会增加高杠杆的概率,从而影响回归模型的性能。
- 运行线性回归后计算 t 统计量和 p 值是很常见的,线性回归旨在评估估计系数是否与 0 有显著差异。为了使推论有效,估计系数的分布必须遵循正态分布。
那该怎么处理呢?有些人可能会考虑使用一些数据转换方法。嗯,这是正确的方向,但请务必避免下一个错误:
常见错误 3:使用 Box-Cox 方法进行数据转换
Box-Cox 是转换数据的一种很好的方法,但它不应该在这里应用,因为它有一个假设:数据必须全是负数或正数。由于我们的数据中有大量的 0,Box-Cox 不能再用了。
数据转换的解决方案:
1)给这个字段的所有值加一个小数字(我建议 0.5),做对数转换。为什么要加 0.5?因为你不能对 0 做对数运算,这在数学上是没有定义的。
2)通过取值的平方根进行平方根变换。
这两种方法都有助于改善偏斜度,如下面的输出所示。
**print('skewness of raw data:', df['val'].skew())
# log transformation
df['log_trans'] = np.log(df.val+0.5)
print('skewness of log-tranformed data:', df['log_trans'].skew())
# square root transformation
df['sqr_trans'] = np.sqrt(df.val)
print('skewness of square root tranformed data:', df['sqr_trans'].skew())**
作者图片
附加提示:
1。如果您的数据非常稀疏,例如> 60%都是 0,我建议您将其更改为二进制变量:
**df['binary'] = np.where(df.val>0, 1, 0)**
2.如果您正在使用非线性 ML 方法,比如树模型,您可能不需要太担心有倾斜的数据。然而,由于多种原因,如可解释性、立法要求等,线性回归模型在工业中被广泛使用。
3.作为响应变量的偏斜计数数据
当偏斜计数(零膨胀)数据是响应变量时,我们可能会面临一些类似的问题,如异常值去除,您可以参考第 2 节中的解决方案。就不对称计数数据的建模而言,我们希望避免以下错误:
常见错误 4:将数据作为一般的连续响应变量来处理
许多人用线性回归(OLS)拟合数据,发现模型性能不好。这是可以理解的,因为数据不是来自正态分布(如第 1 节所述),所以 OLS 不会很好地估计它(尽管这样做是有效的)。
****为了获得更好的模型性能,我们需要理解数据分布的本质。回到我们一开始的问题:“这个计数数据可能遵循什么统计分布?答案是:
与其用单一分布来描述,不如用二项式(看不看)和泊松分布(如果看了也算)来描述。
理论表明,多余的零是通过与计数值(即正值)分开的过程产生的(参考文献 1)。1).因此,我们可以认为数据有两部分:第一部分是一个二进制变量,表示值是否为 0。在第二部分,我们看到数据来自计数数据的泊松分布。
有两种方法可以尝试符合模型:
3.1 机器学习方法
- 如果你有一些 0(<30%):**
您可以尝试使用 Python 库“scikit-lego”(参考文献 2)中的零膨胀回归器来拟合一个模型的数据
- 如果您有多余的 0:
虽然你仍然可以使用上面提到的 Python 库“scikit-lego ”,但我个人会对这么多零感到有点担心,因为这表明数据有明显的分段/分离。正如我们讨论的数据来自两个过程,我会考虑为这种类型的数据创建两个 ML 模型:
-第一个模型将预测值是否等于 0。在博客的例子中,我们使用第 2 节中创建的 df[‘binary’]作为第一步的响应变量。
-其次,我们过滤掉所有的 0,并为大于 0 的值开发一个模型。
****在我们的“观看卡通电影”的例子中,我们可以像“用户细分”一样考虑这个过程,通过预测:
- 如果用户观看了卡通电影,****
2)关注观看过卡通电影的用户,并预测他们观看了多少部。
3.2 统计建模
有许多研究讨论零膨胀数据。这里有几个你可以尝试的模型(参考。1):
- 零膨胀泊松回归
- 零膨胀负二项式回归 —负二项式回归更适用于过度分散的数据,即方差远大于平均值。
- 普通计数模型 —如果没有多余的零,泊松或负二项式模型可能更合适。
4.摘要
计数数据很常见,但如果有多余的零,就很难处理了。这篇博客为您提供了清理、操作和建模偏斜计数数据时的四个常见错误。我希望你能从这个博客中学习,这样你会对你的机器学习之旅更有信心!
注意:我在这个例子中创建的正值不一定遵循泊松分布,因为它只是用于演示目的的玩具数据。在现实中,这种正计数数据应该遵循泊松分布。
💖喜欢这个故事吗?请随意订阅 DS 和 ML 粉丝的邮件列表,并且成为会员!🤩
这篇博客最初发表在我的个人网站上。
****参考文献
1。https://stats.idre.ucla.edu/r/dae/zip/2。https://scikit-lego.netlify.app/meta.html****
避免用 R 平方来判断回归模型的性能
依靠 R 来判断一个回归模型的表现是被误导的
总结
r 可以在 之前计算 甚至拟合一个回归模型,这没有意义然后用它来判断预测能力。如果你翻转输入和输出,你会得到相同的 R 值。同样,这对于预测指标来说是没有意义的。
您的回归模型的意图是选择合适度量的重要因素,合适的度量可能是 而不是 R。
本文解释了存在哪些更好的替代方案:标准误差、置信区间和预测区间。
1.我们为什么要建立线性回归模型?
收集历史数据并计算输入𝑥和输出𝑦之间的关系。这种关系往往是线性回归模型,写成𝑦 =a+ 𝑏𝑥,其中截距为 a ,斜率为 b 。
最常见的目的是使用计算出的关系,并预测一些未来的输出,称为𝑦̂,给定一个新的输入:𝑦̂=和+𝑏𝑥.
以下是建立线性回归模型的两个最常见的原因:
- 向了解更多关于投入与产出的关系;
- 到目前为止,最常见的用法是根据输入获得输出的预测。
让我们依次来看一下每一项。
了解更多关于关系的信息
线性回归𝑦= a+bx 的系数𝑏显示了投入𝑥.每增加一个单位对产出𝑦的平均影响这叫做“了解我们的系统”。
回归模型的斜率和截距提供了对系统的学习或洞察。该图还显示了这里使用的术语。【来源:自己的作品
例如,如果您在系统(输入)的摄氏温度𝑥=temperature 和𝑦=pH(输出)之间建立了一个回归模型,您可能会得到一个回归模型: 𝑦=4.5+0.12𝑥 ,从中您可以了解到两点:
- 温度每升高 1 度,平均会导致 pH 值增加 0.12
- 当使用𝑥=0 摄氏度的温度时,预期或预测的 pH 是 4.5 单位的输出 pH。
但是考虑两种情况:如果我告诉你这个模型的 R 是 0.2,或者是 0.94 呢?这对你的学习有什么影响?我们将在下一节中讨论这个问题,在这里我们将对 R 测量的内容有更多的了解。
使用模型的预测
这个场景是大多数人都熟悉的。继续上述内容,它询问对于给定的新的温度输入值𝑥.,预测的 pH 值是多少例如,在我们从未使用过的 13°C 新温度下,我们预计输出 pH 值为 4.5+0.12×13=6.06 pH 单位。
同样,当模型的 R 值在 0.2 左右,或者模型的 R 值为 0.94 时,我们赋予这样的预测什么样的价值呢?
2.背景:R 实际衡量的是什么?
注意:搜索类似“http://www.google.com/search?q=R2+interpretation”或类似的短语,会返回许多推理错误的网站。
最简单的形式是,R 只不过是两个变量相关程度的度量。它是𝑥和𝑦.之间相关系数的平方
其中花式“E{…}”是“的期望值”运算,花式“V{…}”是“的方差”,Cov{ x , y }是“的协方差”运算。𝑥和𝑦上方的水平线告诉您使用这些数据列的平均值。
A 第二种解读是 R 是回归平方和(RegSS)与平方和总和(TSS)的比值。任何“平方和”值都只是一个重新调整的方差,因此这里的公式显示 R 只是两个方差的比值:
r 值为 1.0 意味着分子和分母完全相同,或者换句话说,预测值𝑦̂ ᵢ 与原始值𝑦 ᵢ 完全相同。相反,要使 r 的值为 0.0,分子必须等于零,这意味着预测只是等于一条平线,即𝑦的平均值,而不管𝑥.的输入值是多少这是你能得到的最差的模型:它说在 R =0 的情况下,你能对𝑦做出的最好预测只是返回训练𝑦值的平均值。
R 的一般解释来自于这个等式:这就是解释的百分比变化。分母与总变化量成比例,分子是解释的变化量,导致 0 和 1 之间的分数(百分比)。
开放圆用于计算(建立)模型;而单个闭合圆圈显示了该模型如何在稍后被用于进行预测。RegSS、RSS 和 TSS 是根据空心圆表示的数据点计算的。【来源:自己的作品】。
与之前的解释相关的是 R = 1.0 RSS/TSS,因为对于简单的回归模型,我们有 TSS = RegSS + RSS。
RSS 是残差平方和(RSS),或者从数学上讲
这清楚地表明,要得到 R =1,你必须没有残差(RSS = 0);R =0 意味着 TSS = RSS,换句话说,残差与原始数据的方差相同。
更多的细节和说明在我的免费书中:https://learn che . org/PID/least-squares-modeling/least-squares-model-analysis
到目前为止的总结
理解上面的公式并尝试用简单的语言解释它们是非常有益的。一开始这并不容易,但理解如何使方程的每一部分变大变小,以及如何获得 0.0 和 1.0 的极值是值得的。
3.为什么 R 不足以判断模型的回归性能
综上所述,这里有两个非常简单的原因,说明为什么 r 几乎不是判断根据新的输入𝑥:预测新的输出𝑦的正确度量
- 如果你把历史数据调换一下,把𝑦变成𝑥,让𝑥变成𝑦,那么你得到的 和 R 值完全一样。这有什么意义呢?模型预测能力的度量标准必须取决于什么是输入,什么是输出。
- 如果我告诉你在 计算模型的斜率和截距之前,我可以告诉你 R 值会是什么 会怎么样?同样,这没有意义。在拟合预测模型之前,如何计算预测性能的良好度量?
这相当于在拟合神经网络之前计算它的预测能力;或者翻转神经网络的输入和输出,得到相同的性能指标。
那么我们如何做出这两个关于 R 的强有力的陈述呢?回头看看第一个公式:
把𝑥和𝑦调换一下
分子是对称的。如果你交换𝑥和𝑦的角色,你会得到相同的分子值。这也适用于分母。请用 Python,Excel,R,MATLAB,或者任何你用来做线性回归的工具自己确认一下。下面是一些拟合线性模型的 R 代码(lm
):
*x = c(10, 8, 13, 9, 11, 14, 6, 4, 12, 7, 5)
y = c( 8, 7, 8, 9, 8, 10, 7, 4, 11, 5, 6)
summary(lm(y ~ x)) # R^2 = 0.657
summary(lm(x ~ y)) # R^2 = 0.657*
能够在拟合模型之前计算 R
再来看一下上面的公式。它是否取决于:
- 模型的残差? 否
- 模型的任何系数,如斜率或截距? 否
因此,即使不拟合回归模型,也可以计算 R。该公式仅依赖于原始数据,而不依赖于任何模型!这不是什么数学把戏,因为事情以某种特殊的方式抵消或简化了。这只是一个 r 被设计用来测量的事实:𝑥和𝑦.两个序列之间的相关程度
很简单,最后一句话指出了何时应该使用 R:仅作为两个序列之间相关性的度量。
4.好吧,我已经被说服了。但是应该使用哪些指标呢?
让我们回头看看这两个用例:
- 从模型中学习
- 根据模型进行预测
从模型中学习
如果你的目标是解释斜率或截距,那么使用一个与之相关的度量,而不是 R。在这种情况下,斜率和截距的置信区间是信息性的。置信区间是两个数字,一个是范围。在此范围内,您可以预期,在指定的置信度下,它包含参数的真实值(如斜率)。让我们回到温度和 pH 值的例子,只考虑斜率。同样的想法也适用于截击。
请记住,𝑦=4.5+0.12𝑥意味着温度每升高 1 度,平均会导致 pH 值增加 0.12 个单位。真实(但未知)斜率的 95%置信区间可能是[0.09;0.15],这意味着我们有 95%的可能性这个范围包含真实的斜率。注意, 不是 真实斜率在这个范围内的概率;一个微妙但重要的区别。有 没有概率 与真值相关联;概率与包含真实斜率的范围相关,真实斜率是一个固定但未知的值。
***为什么使用置信区间?*主要原因是数值都是你关心的单位,而不是两个方差的抽象比值,比如 R,其次,范围越宽,模型越差。假设斜率的置信区间是[0.09;0.15]或[0.03;0.21];你想要哪个?
在间隔的宽度和 R 的值之间存在直接的关系,但是这种关系是非线性的,并且对于不同的模型具有不同的形状。
这里有一些你可能会用到的 R 代码。类似的代码在 Python 中也是可行的:
*x = c(10, 8, 13, 9, 11, 14, 6, 4, 12, 7, 5)
y = c( 8, 7, 8, 9, 8, 10, 7, 4, 11, 5, 6)linmod = lm(y ~ x) # predict y from x
summary(linmod)
confint(linmod) # confidence interval*
根据模型进行预测
如果您想要判断模型的预测,请使用标准误差 (se)或预测区间,而不是 R。为什么使用 SE?
标准误差是残差的标准偏差。这听起来比 R 更复杂,但事实并非如此。首先,标准误差有输出变量的单位。换句话说,它有你真正关心的量的单位,预测的单位!如果您的残差呈正态分布(使用 q-q 图进行快速简单的检查),那么您知道大约 70%的残差位于范围[SE;+SE],95%的残差在 2SE+2SE]。这是我个人使用最多的方法:计算 se 并乘以 4,以了解我的典型残差的“带宽”。不确切,但足以判断“我的预测模型够好吗?”如果您认为预测值的范围在 2SE+2SE],那你就可以对你的模型满意了。
你会在无数的博客和教科书上读到 R 低不一定不好,R 高也不一定好。没错。这是因为我见过 R 超过 0.99 的情况,但标准误差仍然太大,对最终用户的要求没有用处。
比使用构建(训练)模型时计算的标准误差更好的方法是使用全新测试数据集上预测的残差的标准偏差。这是相同的指标,但只是从测试数据中计算出来的,或者如果您不能将测试数据放在一边,则通过交叉验证来计算。
与标准误差相关的另一个选项是使用预测区间。这就像是一个置信区间,但对于一个全新的预测。在上面的例子中,我们可能有:
- 一个新的温度测量,𝑥=10 摄氏度,导致预测的 pH 值为 5.7±0.4
- 换句话说,预测的 pH 值位于从[5.3 到 6.1]的范围内,置信度为 95%。
预测区间(为了说明而放大)是阴影区域。它在训练数据的中心最窄,并且随着距离的增加而变宽。【来源:自己的作品
这样的时间间隔非常有用;我们马上得到一个范围,用我们关心的单位,也就是输出变量𝑦.的单位预测区间是图中的阴影区域,并且是非线性的。离模型中心点越远,它就越宽。虽然在一些软件包(如 Excel)中并不总是容易获得这个区间,但作为一个近似规则,您可以使用阴影区域覆盖 2SE。
*x = c(10, 8, 13, 9, 11, 14, 6, 4, 12, 7, 5)
y = c( 8, 7, 8, 9, 8, 10, 7, 4, 11, 5, 6)linmod = lm(y ~ x) # predict y from x# Get the prediction interval for 3 new values of x:
newdata = data.frame(x=c(1, 8, 15))
predict(linmod, newdata, interval="predict")*
如前所述,R 值越高,预测区间宽度越小,这直观地证明了 R 越高越好,但您无法提前推导出什么是“好 R”的截止界限,因为它取决于测量的数据,当然也取决于您的目的可接受的范围。
收尾
问问自己为什么要建立线性回归模型:
- “我想更多地了解我的斜率或截距”。对它们使用置信区间,而不是 R。较高的 R 与较小的置信区间相关。
- “我希望能够从我的模型中判断预测”。使用预测间隔或模型的标准误差。较高的 R 与较小的预测区间和较低的标准误差相关。
所有描述的选择都比 R 更容易理解,并且是你关心的单位。如果你已经做到这一步,并想阅读更多,请查看我的免费书籍。
避免这些与 Python 中的参数和自变量相关的陷阱/错误
我们将讨论几个错误和一个与 Python 参数相关的问题。
麦克斯韦·尼尔森在 T2 的照片
在继续之前,让我们讨论几个术语。查看下面的代码片段。这是一个简单的函数,打印其参数的总和。
代码片段
- 参数 -它们是函数定义中列出的名称。例:在上面的代码片段中, num1 和 num2 是参数
- 参数——它们是我们在调用函数时传递的值。例如:在上面的代码片段中,在对函数 10 和 12 的两次调用中,参数都被传递给了函数
- 位置实参——当我们将实参传递给一个函数而没有显式地将它们赋给参数时。在上面的代码片段中,我们在第一次调用 func 时使用了位置参数
- 关键字参数 -当我们通过显式地将参数赋值给形参来传递参数给函数时。在上面的代码片段中,我们在第二次调用 func 时使用了关键字参数
- 默认值 -在功能定义期间分配给参数的值。例:在上面的代码片段中, 20 是赋给参数 num2 的默认值
默认参数的顺序
规则:如果在定义函数时有一个带默认值的参数,那么它后面的所有参数也必须有默认值
错误的
代码片段
在上面的 func 的函数定义中,一对没有默认值的参数( num3,num4 )跟在一个有默认值的参数( num2 )后面。这将导致语法错误。
正确的
代码片段
这不会导致任何错误,因为我们已经为变量 num3 和 num4 指定了默认值
关键字参数的顺序
ULE:当调用一个函数时,一个关键字参数必须跟在一个关键字参数之后。因此,位置参数不能跟在关键字参数后面。
错误的
代码片段
12 作为关键字参数传递。然而,它后面的自变量( 20 和 30 )作为位置自变量被传递。这将导致语法错误。
正确的
代码片段
这次,我们将参数( 20 和 30 )作为关键字参数进行传递。因此,不会引发任何错误。
默认参数值
误解:在每个函数调用期间,默认值被分配给参数
正确:默认值只分配一次。在将来的函数调用中,使用以前的值
当你遵循下面的例子时,这将更有意义
代码片段
该函数有两个参数 num 和 arr 。 arr 有一个空列表的默认值。该函数不做任何花哨的事情。它只是将参数 num 插入到数组 arr 中,然后打印该数组。
预期
'''OUTPUT[10][20][30]'''
所以基本上每次我们调用函数的时候, arr 都被设置为一个空列表的默认值,并且 num 被插入其中。
但这不是正确的输出
实际的
'''OUTPUT[10][10, 20][10, 20, 30]'''
嗯,很奇怪吧?🤯
基本上,默认值是在第一次函数调用时设置的。在下面的函数调用过程中,不会再次分配默认值。而是使用以前的值。
解决办法
代码片段
不将空列表指定为默认参数,而是将 None 指定为默认值,并在函数内指定空列表。
结论
我希望你今天学到了新东西。参数和实参看起来是简单的概念,但实际上,它们比你想象的要稍微复杂一些。现在你知道要避免的错误和误解。
原载于 2021 年 5 月 2 日 https://www.realpythonproject.comhttps://www.realpythonproject.com/avoid-these-gotchas-errors-related-to-parameters-and-arguments-in-python/。
用平均避免麻烦
平均值是最常见的显示值,但经常会产生误导
由 Charles Deluvio 在 Unsplash 上拍摄的图像
平均反应是积极的。平均值是 3。对于这个话题,普遍的看法是中立的。平均而言,女性比男性更赞同。你一直都在阅读和听到这样的陈述。是什么样的信息?
平均值可以是多个值。它是一组数据中的典型值(按照牛津语言的定义)。使用统计词汇,平均值可以是:
- Mode 是一个数据集中出现频率最高的数字,意思是被挑选最多的选项。
- Median 当你从最小到最大排序你的值时,把你的数据集从中间分开。
- Mean 是一种将所有数字相加并除以数值个数的计算方法。
都有一些优点。然而,平均值是最常用的值。但是我们用得好吗?
卑鄙可以是卑鄙
注意您的平均值,并根据您的数据对其进行评估,以防止误解。这里我们有一个问题和一个分析师的笔记:
Q1。您对以下陈述的同意程度如何:您的经理定期与您一起评估您的目标。
- 非常同意(5)
- 稍微同意(4)
- 既不同意也不反对(3)
- 略微不同意(2)
- 强烈反对(1)
分析师:对 Q1 的平均反应是 3.00。我们的受访者既不同意也不反对这一说法。
如果我们只看平均值,我们就看不到全貌。我们可以使用平均分数来比较其他语句和我们的 Q1,但是这样做值得吗?每当我们有一个平均值时,我们需要掩盖它背后的东西。我们来看看下面的数据:
Ivona Fikejzlova 提供
这里我们有选项 A 和 b,它们的平均值都是 3.00。然而分布是不同的。如果我们有选项 A,3 的平均值也是它的众数和中位数。在这种情况下,大多数受访者既不同意也不反对。
但是当我们有选项 B 时,反应是两极化的:30%的人同意,30%的人中立,30%的人不同意。
均值不适合捕捉两极分化的数据或极端情况。意思是可以隐藏信息。所以,不要让刻薄的人对你刻薄,要经常检查你的数据分布。平均值并不是任何时候都有用,所以在有意义的时候使用它。
小数字的平均值
许多学生确实打算从课堂上评估他们的最终评价。这样,他们知道会发生什么。
Ivona Fikejzlova 提供
在这里,我们的学生知道,如果他想得到 2 分,他需要在期末考试中表现出色,得到 1 分。否则,他会得 3 分。这是计算平均值的一种简单而有用的方法,
在其他情况下,当我们从一小部分人那里收集数据时意味着是多余的。假设我们有一份 10 人的问卷,最终呈现的数据如下所示:
Ivona Fikejzlova 提供
它让数据变得更加复杂。当我们有像这样的小数字时,它足以呈现计数。我们不必为平均值或百分比而烦恼。毕竟 10% = 1 人。
在这种情况下,这种方法不能提供有用信息。这会分散观众的注意力。在这种情况下,我们会做更多的定性分析。我们关注个体而不是平均水平。
干净数据的平均值
许多数据收集调查都有逃避性的回答,比如“我不知道”或“我不想回答”。
在准备平均值时是否应该考虑这些因素?而完全不填数据的人呢?他们应该参与计算吗?
只有相关的数据才能提供你的平均数据。你的平均分数)。所以,小心错误的选择。从数字数据中得出平均值是很容易的。当你必须将书面数据转换成数字时,你可能会面临一个潜在的问题。
所以,在计算平均值之前,你必须清理你的数据。清洁过程正在检查这些:
- 数据范围:如果我们有一个刻度,比如 1-5,就不应该有 6、7、8 等等。如果我们把错误留在那里,平均值将是错误的。
- 空数据:我们经常会遗漏一些数据。这就是为什么该项目不应该进入计算,因为平均将再次是错误的。
- 非数字数据:需要填写数字。但是他们写的不是 2,而是 2。您需要将所有文本值更改为数字,以便能够计算平均值。此外,任何其他不相关的文本应该相应地编码。
干净的数据易于处理和操作。您可以选择感兴趣的平均值——众数、中位数或平均值。这取决于你分析的范围。
最终平均音符
无论何时你要提出一个平均值,不要想当然。想想为什么要呈现,怎么呈现。
平均值适用于比较,但不适用于分析数据分布。您有三个选项——众数、中位数和平均数。
所有数据应该是干净的,没有错误的值,以达到正确的平均值。
注意,如果数据样本很小,平均值可能不是最合适的值。最后,一定要提到你是如何计算平均值的。每个人都应该清楚你在说什么,你是怎么做到的。
附注:你应该会收到我的邮件。 做到这里 ! 如果你自己喜欢体验媒介,可以考虑通过注册会员 来支持我和其他成千上万的作家 。每月只需 5 美元。
如何以及为什么停止使用熊猫。申请()(这么多)
理解为什么确定。apply()的工作很好,为什么有些人表现不佳。
如果您正在阅读这篇文章,我猜您已经在某个时候使用过流行的 python 数据处理库 pandas 中的.apply(…)
方法。如果你没有,那么你可能会迷失,你可能不会从这篇文章中得到很多(但无论如何,如果你读了它,还是值得称赞的)…
如果这篇文章写得好,并且你成功地阅读和理解了它,你可以希望;
- 更好地了解
.apply(…)
在不同场景中的作用(剧透一下,它可能不总是你想的那样); - 知道什么时候
.apply(…)
可能是一个好的选择,什么时候使用替代品可能更好;
“当你只有一把锤子时,一切看起来都像钉子”
我将在这篇文章中指出,最好少用.apply()
,当你把手放在.apply()
瑞士军刀上时,可能会有其他更合适的工具。
由我用 Microsoft Paint 专业绘制
有什么关系?apply() do?
在一个非常高的层次上,.apply()
允许我们以不同的方式调用 pandas 对象上的 python 函数。这使得第一次使用 pandas 库时变得非常容易,因为你可以用一种非常熟悉的方式编写 Python 代码,然后使用.apply()
将其转换到你的系列或数据帧上。
什么(在我看来)是有效的熊猫?
关于这方面的文章有很多很多,所以我就不赘述了,但这里有一些关于“有效的熊猫”(或一些人喜欢称之为“可漫游的熊猫”)的重要特征的观点:
- 尽可能有效利用矢量化运算的代码(这几乎总是可能的)
- 对于我们原始的大脑来说,代码是容易阅读和理解的(当然这不仅仅是熊猫的代码)
如果你想了解更多关于这些事情,请去做吧(但是之后一定要回来)。关于 pandas 性能的一个关键要点是,在 pandas 数据帧中对每一行进行操作通常很慢,但使用列作为序列对其进行矢量化操作(一次处理一整列)通常很快。
申请什么?
在熊猫中使用 **.apply(…)**
的时候,这大概是最需要问自己的问题。你可能会合理地认为,每当你在熊猫身上做 **.apply(…)**
动作时,它都会以大致相同的方式动作,不幸的是事实并非如此!
你有一个 python 函数和一个 pandas 对象,你想把你的 python 函数应用到你的 pandas 对象,但是这个函数实际上会收到什么参数(它会得到一个 int,一个 series,一个 dataframe)以及这对代码的行为有什么影响?
答案是,这取决于你正在应用的对象的类型(我们将查看pd.Series
、pd.DataFrame
和pd.GroupBy
),以及你如何调用**、**。
我们将创建一个简单的测试数据框架,并进行一些探索…
import pandas as pd
import numpy as np
df_len = 1000
test_df = pd.DataFrame(
{
"cat_column": np.random.choice(["a", "b", "c"], size=df_len),
"float_col1": np.random.random(size=df_len),
"float_col2": np.random.random(size=df_len),
}
)
这里我们有一个具有 3 列的数据帧,一个对象列包含“ a”、“b”或“c”,两个浮点列包含 0 和 1 之间的值。
警察。系列.应用
好了,这是更有趣的地方(因为我们已经完成了预华夫饼,我们正在运行一些代码)。现在,请耐心等待,我们将把内置的type
函数作为一个系列应用到我们的一个专栏中。
输入:
print(f"Applying on {type(test_df['float_col1'])}")
test_df["float_col1"].apply(type)
结果:
Applying on <class 'pandas.core.series.Series'>0 <class 'float'>
1 <class 'float'>
2 <class 'float'>
3 <class 'float'>
4 <class 'float'>
...
995 <class 'float'>
996 <class 'float'>
997 <class 'float'>
998 <class 'float'>
999 <class 'float'>
Name: float_col1, Length: 1000, dtype: object
酷,谁在乎?这告诉我们什么?嗯,第一行简单地告诉我们,在这种情况下,我们正在.apply
到一个pd.Series
。输出的其余部分告诉我们什么?它告诉我们一些事情:
- 当我们对
pd.Series
应用函数时,函数接收序列中的值作为参数,而不是一次性接收整个序列。我们正在使用的type
函数接收每个“单元格”中的浮动对象。 - 我们应用的函数在序列中每行被调用一次(在本例中被调用 1000 次)
如前所述,每行调用一个函数会有性能损失,所以当您在一个pd.Series
上调用.apply
时,您并没有向量化您的操作,如果可以的话,请避免使用这种.apply
。
系列应用的调用模式图,应用的函数 f 是用系列中的单个值调用的。
例子
例子的问题是它们总是做作的,但是相信我,在大多数情况下,这种pd.Series.apply
是可以避免的(请至少试一试)。因此,在这种情况下,我们将采用示例数据帧中的一个浮动列的log(cos(x) + 5)
,只是因为它看起来有点复杂,我们可能会在这种情况下使用 apply。
def trig_transform(x):
return np.log(np.cos(x) + 5)
使用 pd。
test_df["float_col1"].apply(trig_transform)
时机
3.25 ms ± 119 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
忌 pd。Series.apply:
因为大多数常见的数值运算在 numpy 中都有一个矢量化的实现,可以接受标量或数组,所以在某些情况下(比如这样),您可以简单地直接在pd.Series
上调用函数,而不是将它应用于每个元素。
trig_transform(test_df["float_col1"])
时机
264 µs ± 11.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
我不会喋喋不休地说它快了多少,但在这个基准测试中,它看起来快了大约 12 倍,但您应该亲自尝试一下,因为性能有许多因素。
非数字示例可能需要对我们在 pandas 对象上调用的函数进行一些更改,我建议您查看一下pd.Series.str
访问器和pd.Series.dt
以了解字符串和日期时间类型上的常见操作。
警察。数据框架.应用
警察。DataFrame.apply(轴=0)
好了,让我们对之前的代码做一个很小的改动,来观察 apply 在pd.DataFrame
而不是pd.Series
上的行为。我们使用相同的测试数据帧,但是这里选择相关的列作为列表["float_col1"]
而不是单个字符串"float_col1"
。
输入:
print(f"Applying on {type(test_df[['float_col1']])}")
test_df[["float_col1"]].apply(type)
输出:
Applying on <class 'pandas.core.frame.DataFrame'>float_col1 <class 'pandas.core.series.Series'>
dtype: object
我们可能期望在这里得到类似的结果,但事实并非如此,我们可以看到我们的函数只被调用了一次,而不是 1000 次,并且在我们应用的函数中收到了一个pd.Series
。
当您在pd.DataFrame
上调用 apply 时,(默认轴=0),应用的函数在每列被调用一次**。这对调用函数的行为有很大的影响。**
dataframe apply 的调用模式图示在 axis=0 的情况下,使用 dataframe 中的列系列调用应用的函数 f。
例子
这是另一个人为的例子。这一次,我们将对示例数据帧中的每个浮点列应用上一个示例中的相同函数。
def trig_transform(x):
return np.log(np.cos(x) + 5)
使用 pd。DataFrame.apply (axis=0)
在我们的示例数据框架中,我们只将这些应用于数字列,否则我们会遇到一些错误。
test_df[[“float_col1”, “float_col2”]].apply(trig_transform)
时机
2.74 ms ± 367 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
如果你回到前面的例子,你可以看到它在 2 列上比pd.Series.apply
在 1 列上运行得更快。
避免 pd。DataFrame.apply (axis=0)
等等,为什么我们要避免使用pd.DataFrame.apply
?看起来性能还不错。好吧,我要给出的原因与性能无关,你可以不同意;每次你使用.apply()
时,它都要求代码的读者/维护者知道它是如何被使用的,以及它将如何运行;因为.apply()
可能有许多行为方式,这增加了代码的复杂性,而(在大多数情况下)没有增加价值。
同样值得注意的是,这种.apply()
非常罕见,因为您很少想要在数据帧中的每一列上单独调用同一个函数。
*trig_transform(test_df[[“float_col1”, “float_col2”]])*
时机
*1.1 ms ± 45.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)*
在这种情况下,就像 Series 示例一样,我们可以直接在 DataFrame 上调用我们的函数,因为 numpy 函数适用于所有不同形状的数组(wow numpy 很好)。在这种情况下,速度快了两倍,而且我认为更容易理解。如果你想念.apply()
给你的链接风格,考虑在这里使用.pipe()
,这有一个更简单的使用模式,并且和直接函数调用一样快。
*test_df[["float_col1", "float_col2"]].pipe(trig_transform)*
警察。DataFrame.apply(轴=1)
在应用于pd.DataFrame
时,您可能会经常看到 axis=1 参数。这给出了另一种不同的行为;该函数每行调用一次,而不是每列调用一次。
输入:
*print(f"Applying on {type(test_df[['float_col1']])}")
test_df[["float_col1"]].apply(type, axis=1)*
输出:
*Applying on <class 'pandas.core.frame.DataFrame'>0 <class 'pandas.core.series.Series'>
1 <class 'pandas.core.series.Series'>
2 <class 'pandas.core.series.Series'>
3 <class 'pandas.core.series.Series'>
4 <class 'pandas.core.series.Series'>
...
995 <class 'pandas.core.series.Series'>
996 <class 'pandas.core.series.Series'>
997 <class 'pandas.core.series.Series'>
998 <class 'pandas.core.series.Series'>
999 <class 'pandas.core.series.Series'>
Length: 1000, dtype: object*
在本例中,与pd.Series
apply 不同,调用函数不接收单个单元格元素,而是接收包装在pd.Series
中的行,其中索引是列名,值是列值。
再次考虑性能,您可能希望避免使用 axis=1 applys,因为每行都有一个函数调用,并且我们通常在对列进行矢量化操作时获得最佳性能。
axis=1 应用程序可能很常见,通常很慢,并且通常易于矢量化。
dataframe apply 的调用模式图示在 axis=1 的情况下,对 dataframe 中的每一行调用一系列应用函数 f。
例子
axis=1 apply 是在 pandas 数据框架中的不同列之间进行比较或操作时可能会遇到的一种情况。这种模式(或者反模式)相当常见,因为您编写的应用于数据帧的函数看起来非常 pythonic 化(您得到一行,像字典一样对其进行索引,并对值进行处理)。
这一次,我们将对数据帧应用以下函数。
*def row_trig_transform(row):
return np.sin(row["float_col1"]) * np.sqrt(row["float_col2"])*
使用 pd。DataFrame.apply (axis=1):
*test_df.apply(row_trig_transform, axis=1)*
时机
*34.2 ms ± 1.39 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)*
避免 pd。DataFrame.apply (axis=1):
在这种情况下,我们实际上不需要改变调用函数,尽管我们打算这样做,因为这将提高代码的质量。
*def calculate_series_trig_transform(ser_1, ser_2):
return np.sin(ser_1) * np.sqrt(ser_2)*
为什么这样更好?现在,您可以轻松地在任何其他列或系列中重用该函数。
*calculate_series_trig_transform(
ser_1=test_df[“float_col1”],
ser_2=test_df[“float_col2”],
)*
时机
*290 µs ± 12 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)*
我们得到了相同的结果,我们的代码更具可重用性,并且由于我们使用了矢量化运算,最终速度提高了约 90 倍,这是一个很大的提高。
警察。GroupBy.apply
这是我们在这篇文章中要看的最后一个.apply()
(我们不会触及pd.Series.map
或pd.DataFrame.applymap
,它们也在这个空间中)。你可以说pd.GroupBy.apply
是一个特例,但我认为既然所有这些.apply()
的行为都不同,那么它们都是特例。
所以让我们来看看当我们执行pd.GroupBy.apply
时,我们的调用函数接收到了什么…
投入
*test_df.groupby("cat_column").apply(type)*
输出
*cat_column
a <class 'pandas.core.frame.DataFrame'>
b <class 'pandas.core.frame.DataFrame'>
c <class 'pandas.core.frame.DataFrame'>
dtype: object*
在这种情况下,我们没有得到标量,没有得到一系列的列,也没有得到一系列的行,我们实际上得到的是整个数据帧。这些数据帧包含特定组中的所有行(一个函数调用使用数据帧调用所有行,其中一个调用使用数据帧调用cat_column=="a"
,一个调用使用数据帧调用cat_column=="b"
,一个调用使用数据帧调用cat_column=="c"
)。我们可以推断,只要组的数量不太大,这里的性能就会相当不错(如果每一行都在一个唯一的组中,性能就会很差,而且分组也没有什么意义)。
groupby-apply 调用模式的图示,应用的函数 f 被称为每个组的数据帧(在这种情况下,将“cat_column”作为分组变量)。
例子
这里我们将假设我们想要得到数据帧中所有sin
的总和(我不知道为什么我们可能想要这样,但我们可能会这样)。
*def sum_all_sins(df):
return np.sin(df).sum().sum()*
该函数将每个数据帧中的所有值的sin
转换成一个序列,然后sum
转换该序列的值,得到一个标量。值得一提的是,你不一定要返回一个标量,你也可以返回一个序列或一个数据帧,但我们不打算深入讨论。
使用 pd。GroupBy.apply:
*test_df.groupby("cat_column").apply(sum_all_sins)*
时机
*6.08 ms ± 316 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)*
避免 pd。GroupBy.apply:
在我看来,使用pd.GroupBy.apply
很好。
为什么这个没问题?一个原因是在 groupby 对象中将函数应用于组的其他方法不多(有agg
和transform
,但是它们的用途更具体,或者在组上循环,这不太优雅并且有性能成本)。从这个意义上来说,这个组合就是习语熊猫自己的组合。这也很难出错,应用的函数接收一个数据帧,所以你可以让它快速处理数据帧。
摘要
最后,我将快速回顾一下这些应用程序是如何工作的,以及我是否认为这是一个好的实践(当然这些事情是主观的,这是一个自由的世界)。你可能已经知道了,我的观点是尽可能避免使用 apply。
pd。Series.apply
- 应用的函数每行调用一次,值在该行中
- 通常最好避免——你的列上可能有矢量化操作或内置熊猫操作,这样会更快一些
pd。DataFrame.apply (axis=0)
- 每个列调用一次应用函数,使用列系列的值
- 通常最好避免——如果应用的函数使用了系列运算,则速度会相对较快,但您很可能不需要这种应用,您可以在数据帧上直接调用函数(或通过管道传递它),这可能会使代码更具可读性。
pd。DataFrame.apply (axis=1)
- 每行调用一次应用函数,在每个函数调用中获取一系列行
- 通常最好避免——很可能有一种矢量化的方法可以实现更好的性能。
pd。GroupBy.apply
- 每组调用一次应用函数,每次函数调用获得一个数据帧(如果只选择一列,也可以是系列)
- 典型的好选择——只要你没有太多的组,因为你的函数每个组调用一次。
结论
在你从工具箱中拿出.apply()
之前,考虑一下你是否有合适的箱子。有时“通常很慢”的使用是难以避免的,也许如果你正在使用一些不可避免的棘手的object
类型(比如一列不同长度的列表),这些类型需要不容易矢量化的操作。尽管在大多数情况下有更好的选择。
如果实现很难解释,这是一个坏主意。
这是直接引用自 python 的禅宗(你可以通过在 python 解释器中运行import this
来阅读)。我认为.apply()
API 很难解释(因此有了这篇文章),所以我个人认为最好少用,特别是当经常有其他选项对我们的灵长类大脑来说更容易理解的时候。
希望你从这篇文章中学到了什么,或者如果没有,至少喜欢阅读它!快乐的熊猫!
避免时间序列中的数据泄漏 101
你已经做出了选择。你来这里是为了明白你为什么会成功。—神谕,矩阵
时间序列是几乎每天都变得难以建模的极少数数据学科之一。
例如,丰富的数据对于许多其他领域来说是一个好消息。我们可以训练更好的模型并期待更好的结果。但是在时间序列的背景下,大量的数据经常以不同的形式出现。这通常意味着数据到达的时间间隔越来越短。当这种情况发生时,与其更好地解释这种情况的潜在动力,通常更新的和以前未知的怪癖开始在时间序列中出现。例如,部分季节性或突然变化的趋势。
对于许多其他学科,建模和预测是齐头并进的。对于时间序列来说,它们通常是大敌。以有效市场假说(EMH)为例。它是金融学的一个理论基础,然而,它曾经(现在仍然)是金融时间序列预测的一个强有力的打击。因为它说,由于市场效率,股票价格只是随机游走。模拟随机漫步非常简单。然而,预测是一种徒劳的尝试(EMH 是否成立是另一回事,许多聪明人拒绝相信 EMH)。
在这篇文章中,我将讨论数据泄露,这在很多 ML 任务中很普遍。数据泄漏可能会在您的 ML 生命周期中蔓延,并一直保持沉默,直到您将模型推向生产,一切都将失控。但是什么是数据泄露呢?
维基百科为数据泄露提供了一个恰当的定义。我冒昧地以如下方式对其进行了一点修改:数据泄漏是在模型训练和验证期间使用的信息,这些信息在预测阶段是不可用的。就时间序列而言,这种可用性的缺乏可能以多种形式出现,尤其是当预测范围很大时。这可能是由于以下原因:
- 该功能在未来根本不可用。在交叉验证期间,这种不可用性可能会被忽略,因为我们在数据集上进行交叉验证,而数据集本身是由过去的观察值组成的。因此,在生产之前,我们总是可以访问这些特性。这些类型的数据泄露的主要影响是——他们认为花费在特征创建上的时间是没有意义的
- 为了便于讨论,假设特性可以在预测范围内通过特性本身的预测/预期而变得可用。在这种情况下,有两个警告:(a)可靠的预测是非常昂贵的(b)预测带有许多不确定性/误差,所以做好准备,看看模型在生产中的性能急剧下降。
数据泄露的一个常见例子可能是使用库存变动数据来预测某些生产的数量。在交叉验证阶段,股票运动数据可能显示出极好的匀称值,使其成为一个重要的建模特征。但是当涉及到实时预测时,它的效用变得非常小,如果不是绝对无用的话。
在接下来的内容中,我将尝试标准化一些可以用来避免这个陷阱的实践。这里详述的一些实践在 ML 从业者中非常普遍地被接受,为了完整起见,我将提到它们。
先分裂,后正常化。
这种做法是众所周知的洗钱从业者。这也与时间序列分析有些关系,特别是当涉及神经网络的技术(例如:AR-Net,NeuralProphet,RNN-LSTM)被应用时。这是因为神经网络很难(如果不是不可能的话)用未标准化的数据集进行训练。
这种做法包括以下步骤:
- 将数据集分为训练集、验证集和测试集
- 仅使用训练集计算归一化因子。规范化训练集
- 使用在前一阶段发现的标准化因子,标准化验证和测试集
如果我们使用整个数据集计算出归一化因子,我们就会在模型训练期间以归一化因子的形式引入数据泄漏。这仅仅是因为归一化因子现在包含了来自验证和测试集的统计数据。
有一些重要的警告值得注意。如果被建模的过程不是静态的,那么将归一化因子带到很远的将来(验证+测试)会对模型性能产生不利影响。然而,这是一个不同的讨论。
直接进入 MVP,稍后交叉验证!
听起来有点违背直觉,但让我解释一下。
MVP 代表最小可行产品。这基本上是我们正在设计的 ML 系统的精简版本(例如:推荐引擎或预测引擎),能够实时交付端到端的解决方案。它不一定需要被部署。
通常的 ML 实验生命周期有以下步骤:
- 准备数据集(分割+归一化+要素创建)
- 快速构建一系列模型
- 交叉验证,然后进行超参数调整
- 型号选择/组装
- 创建一个 MVP
请记住,如果模型中存在数据泄漏,那么您所监控的每个性能指标都会被严重夸大,并且在所有模型中不对称。因此,在模型选择过程中,我们可能会因为一个据说是错误的性能指标而放弃最健壮的模型。或者在组装时,我们可能会给每个模型分配不准确的重量,从而在生产中产生不良的预测。
为了避免所有这些陷阱,让我稍微重组和重新设计一下实验生命周期:
- 准备数据集(分割+归一化+要素创建)
- 训练一个简单的模型。为什么不是复杂精密的模型呢?下一步就清楚了。
- 创建一个 MVP。由于 MVP 是实时预测的(例如,未来 7 天),因此无法衡量模型的性能。因此,模型复杂性是不相关的。
- 假设引入数据泄漏的特征被识别,继续进一步迭代(更复杂的模型,模型选择/集合)
这个重新设计的生命周期不仅可以帮助您识别可能引入数据泄漏的特性,还可以让您了解实际上可以包含哪些类型的特性。
不要解耦模型训练和预测/推理
对于许多 ML 任务,模型训练和预测/推理可以完全解耦。以所谓的猫分类器为例。人们可以完全分离分类器训练和分类的过程。可以有把握地假设,进行预测所需的特征(像素值数组)的可用性不是时间的函数。
然而,当涉及到时间序列时,情况就完全不同了。在时间序列分析中,建模和预测通过时间关系交织在一起。只有当一个特征是时间的直接/间接函数时,它才能有助于推断。因此,在模型训练期间所做的每一个决定都需要得到这个想法的全面支持。这与其说是一种实践,不如说是思维过程和领域专业知识的结合。自然需要时间来完善。
就我而言,在我的时间序列分析之旅中,我发现上面的实践非常有帮助。我希望也有一些外卖给你。祝你愉快。干杯!
避免 ML 管道的技术债务
在快速结果和高质量编码之间取得平衡。
机器学习中的技术债务可能很难管理。来源: Unsplash
好吧,首先让我们弄清楚:这篇文章不是为那些做一次性的孤立项目的人准备的,比如参加 Kaggle 竞赛或者在 Jupyter 笔记本上做爱好项目来学习交易。抛弃、快速、肮脏的脚本代码的价值是显而易见的——并且有它的位置。相反,它是为在生产环境中工作的 ML 从业者设计的。因此,如果你在一个 ML 团队中工作,在生产 ML 模型的同时努力管理技术债务,这一个适合你。
典型的工作流程
图片来源: 迈克尔·迈耶上 Flickr |找到归属许可 这里
这里有个前提:你是一名在初创公司/中小企业/企业工作的 ML/DL/AI 工程师/分析师/科学家。你的工作是从所有地方获取一堆随机数据,并产生价值。你是做什么的?您坐下来,以某种方式将数据传输到您的本地机器上,并且不可避免地要做一些类似以下的事情:
jupyter notebook
或者你可以去一个实验室,如果你喜欢并且你的团队的隐私规则允许的话。
下面是一个我以前看过很多次的故事——当然是伪代码。
import pandas **as** pd
import xyzlibraryforml***# CELL 1: Read***
df = pd.read_*("/path/to/file.*")
df.describe()*# INSERT HERE: a 100 more cells deleted and updated to explore data.****# CELL 2: Split***
train, eval = split_the_data() *# basic**# INSERT HERE: trying to figure out if the split worked****# CELL 3: Preprocess***
*# nice, oh lets normalize*
preprocess(train)
preprocess(eval)*# exploring preprocessed data, same drill as before****# CELL 4: Train***
model = some_obfuscated_library.fit(train, eval) *# not being petty* *# if youre lucky here, just look at accuracy. otherwise:****# CELL 5: Evaluate***
complicated_evaluation_methods(model)*# INSERT HERE: do this a 1000 times****# CELL 6: Export (i.e. pickle it)***
export_model()
你说完了,对吧?就是这样——嘣。测试集结果非常好。让我们把它交给运营部的人在生产中部署。午休和 Reddit 一整天!
好吧,我是不是太夸张了?是。对于一些企业来说,这是否非常接近事实?也是的。
那么,有什么问题呢?
问题是,上面的笔记本电脑是一个技术债务球,如果不及早剔除,它将继续增长。让我们来分析一下它出了什么问题:
没有朝着更大的方向前进
当您将可概括的逻辑放入无版本的、一次性的笔记本块中时,您的团队就无法利用它了。例如,从静态数据源加载/提取数据的逻辑。当然,现在使用pd.read_json
很容易,但是如果格式改变了会发生什么呢?更糟糕的是,如果数据增长并被分割成多个文件,会发生什么?更糟糕的是,如果它不再适合内存会发生什么?如果你的同事遇到同样的问题,会发生什么——她可能会经历同样的循环,而你甚至不知道这是一个已经解决的问题。当然,所有这些问题都有解决方案,但是你会一直在你的本地笔记本上解决它们吗?
答案很可能是否定的(除非你是网飞或者其他什么)。通常,合乎逻辑的做法是将加载提取到一个逻辑上独立的服务中。通过这种方式,您可以将实际的数据提取抽象出来,放到一个对每个人都透明的可维护层中。例如,这可能是某种形式的feature store
,它将组织中所有不同的数据流收集到一个点,然后团队中的每个人都可以在定义的 API 中使用它。
这同样适用于上述脚本的预处理、训练和评估部分。
在本地构建逻辑(因此一次又一次)
就像上面的例子一样,当您编写代码来探索数据时,您会生成大量的好东西——可视化、统计数据框架、经过清理和预处理的数据等等。Jupyter 笔记本执行的随机、任意顺序确保了实现这些工件的路径永远消失在被覆盖的局部变量和自发的内核重启中。更糟糕的是,这种逻辑在代码本身的深处嵌入了复杂的、容易被覆盖的配置——这使得重新创建工件变得更加困难。
听着,我明白——我一直都这么做。数据争论是一个随机的、痛苦的过程,而且会很混乱。但是建立一些框架来跟踪您的数据探索管道将会带来巨大的回报。类似于评论你的代码,跟踪的最大受益者将是你自己。此外,如果这些工件和问题的迷你答案对每个人都是自动透明和清晰的,您的团队将会更快,并避免多余的工作。
不拥有部署
这本笔记本的最后一部分可能是最令人沮丧的。我真的不相信写那个笔记本的人的工作会以导出 ops 的模型而结束。这没有任何意义。
首先,preprocessing_fn()
函数必须与它一起使用,否则 training-serve skew 将从一开始就破坏你的模型。第二,运营人员如何知道您在构建模型时采用了哪些假设?您是否要编写大量的文档,说明输入什么数据、应该如何预处理数据,以及在部署到端点时数据应该是什么形状?我的意思是,现在已经有了自动化的方法来实现这一点,所以拥有部署吧!
上述脚本中缺少的一个方面是忽略了对模型性能的度量。我认识的大多数数据科学家并不关心模型有多大,它为预测消耗了多少内存,以及它在部署中有多快/多高效。如果不满足最终应用程序的性能标准,模型就不会产生价值。同样,开发模型的人应该拥有其最终部署的所有权。
建议
解决上述问题的最简单的方法是开发一个框架,在这个框架中,ML 团队可以用可维护的、易于共享的代码来平衡一次性的探索性代码开发。如果您要这样做,您可能需要记住以下几点:
创建定义良好的接口(即分解成管道)
您的工作流的split
、transform
、train
、evaluate
、deploy
组件是逻辑上独立的实体/服务。从具体的实现中抽象出 ML 工作流的单个组件。这可以通过定义面向对象风格的实际接口来实现,或者简单地确保您的 repo 具有某种形式的结构,便于每个人参与和扩展。这不一定一开始就是火箭科学,但它会有巨大的帮助。
这就是 ML 管道的概念发挥作用的地方:管道是定义一系列数据处理任务的抽象表示。在pipelines
中思考将帮助你的团队在他们的工作流程中分离出逻辑实体,并让数据独立地流过它。这将不可避免地产生更健壮、可维护的代码库。此外,像这样定义 ML 管道可以确保您可以在新数据到来时自动对旧模型进行连续训练。但是,您还需要为此跟踪您的数据元数据(见下文)。
为你的 ML 元数据制定一个计划
你运行的每一个实验都会产生 ML 元数据:谁运行的,什么时候运行的,输入了什么数据,结果存储在哪里等等。请务必标出这些内容,并提供一种方便的方式来添加到该商店。重要的是要注意:我也不只是在谈论实验跟踪。有许多很棒的库可以帮助跟踪以模型为中心的元数据,例如度量等。然而,经常被忽视的是以数据为中心的元数据——尤其是当数据不断变化时。像数据版本控制、统计、可视化、随机分割时使用的种子之类的东西。应该有一种简单的方法来跟踪数据在开发过程中的各种路径。
确保您的工作负载可以在任何环境中运行
在一台机器上运行一个工作负载总是比在任意环境下运行相同的代码要容易。我知道 Docker 对于很多 ML 的人来说是无敌的,也是辛苦的,但是至少做一个requirements.txt
加一个__init__.py
!理想情况下,将您的代码容器化,并在某种形式的编排框架上运行实验。现在做这一步,当您扩展和自动化整个事情来处理更大的数据时,将会省去很多麻烦。
不要将部署与培训分开
这可能是迄今为止最没脑子的建议了。20 年前,端到端所有权导致了整个 DevOps 革命,这在 ML 开发中也没有消失。提供一个平滑的机制来将一个训练好的模型转移到一个端点,并且确保操作人员和你的 ML 开发人员坐在同一个房间里(并不总是这样)。制定流程,让每个人都了解生产的最终目标。尽可能实现自动化。
不要在可重复性和可追溯性上妥协
你知道当人们开始用 Python 编码,然后转向 C++或 Java 时,他们不理解像指针和静态类型这样的概念吗?他们认为:“给变量一个类型有什么用,我知道它是什么,为什么我被迫这么做?”很抱歉打断你,但是指针和静态类型有一个目的——了解它们可以保护你的代码免受你自己的错误,并确保高质量的健壮输出。终极灵活性可能是一件坏事,尤其是对于那些容易犯懒惰错误的开发人员(像我一样)。
在 Jupyter 笔记本中也发生了非常类似的事情——以任何顺序运行任何任意代码的自由带来了自由,但也让你失去了非常重要的可重复性和可追溯性的概念,这是任何健壮的、生产就绪的工程学科的两个基石。请至少确保您的笔记本是自上而下可执行的,并且是可重复的。杂乱无序的 Jupyter 笔记本应该受到像代码审查会议上这样的长篇大论的惩罚。
确保这两种特性的一种方法是从实现中提取代码的settings
。这就引出了我的下一个观点…
将配置与实施分开
将配置与实际代码实现分开绝对是一件痛苦的事情。然而,这是另一种“长期回报”的事情。我们在之前已经写过了,但是总结一下:分离你的配置允许你自动化重复的任务,增加结果的可预测性,并且确保可再现性。理想情况下,配置应该被视为代码、版本和维护。管理这一点的一个好方法是将代码写成管道——我已经写了很多关于管道的内容,并且在这篇 Neptune.ai 博客文章中受到了极大的启发。
结论
许多组织中的 ML 从业者都被强烈地激励着快速取胜以产生早期的结果。然而,这导致了累积的技术债务,随着时间的推移,最终会减慢团队的速度。解决方案是从一开始就遵循适当的软件工程原则,并依靠指南在快速结果和高质量软件开发之间取得平衡。
以上想法是我在过去 4 年中在生产中部署模型时学到的非常个人的经验。它们绝不是一个详尽的列表,欢迎您将它用作基准测试您的产品架构的样板,或者作为设计您自己的产品架构的蓝图。如果你正在寻找一个实用的教程
我们将这些因素作为我们开源 MLOps 框架 ZenML 的指导原则。所以在你从头开始之前,请在 GitHub 上查看 ZenML:https://github.com/zenml-io/zenml,如果你喜欢你所看到的,别忘了给我们一颗星!
避免数据科学项目的 4 大陷阱
从事数据科学项目,尤其是与新的利益相关方合作,可能会很有挑战性。学习如何避免主要的陷阱。
图片由风险化(经许可使用)
随着数据科学、机器学习和人工智能在商业领域的不断普及,公司正在试图利用这些技术解决新问题。作为一名数据科学家,你知道你接下来的项目很有可能会失败。如果机器学习只是你公司运营的一个小方面——主要产品中的一个功能或产生有价值的商业洞察力的一种方法,那就更是如此。在 Riskified ,机器学习是我们业务的核心,但牢记这些陷阱总是很重要的。
由于数据科学家方面的不专业,项目可能失败的原因数不胜数:选择偏差、目标泄露、数据漂移、p-hacking、过度拟合等等。然而,在本文中,我们将关注与业务协作相关的一些基本问题。
纳入数据科学与任何变更管理流程都是一样的,需要仔细考虑和规划才能做好。任何重大的变革过程都需要强大的成员联盟,包括创造愿景、推动愿景和消除障碍的倡导者。约翰·科特的八步流程是最经典的变革管理模型之一,它强调提前规划变革流程的重要性。让我们看看当关键的准备工作没有完成时会发生什么。希望下面的提示可以帮助你挽救你的数据科学项目,或者避免开始一个注定失败的项目。
错误数据
如果您是您所在组织(或之前从未使用过数据科学的业务部门)的第一位数据科学家,那么您很有可能错过了关键数据,或者数据质量不够好,无法开始工作。与数据工程师或数据库管理员讨论现有数据、获得对该数据的许可和访问权、探索该数据以检查其特性、空率、一致性、验证其回溯多远、预测标签的存在、该标签的准确性以及其他质量问题可能会花费大量时间。如果你的公司到目前为止做得很好,那些数据提供商不一定理解为什么他们必须满足关于数据质量的新的严格要求(“到目前为止一切都很好,不是吗?”).
对于您工作线之外的业务人员来说,在这段漫长的时间里似乎没有取得任何进展,因为这似乎是一个永无止境的数据清理过程。如果高级管理层不了解将数据置于最低工作条件下所需的大量准备工作,他们就不会有必要的耐心,也不会期望很快得到结果。在那些背负大量技术债务以快速前进的公司中,数据质量可能会更差,他们的耐心会更低,因为他们习惯于快速交付。
如果你处于这种棘手的情况,在对你的项目进展速度做出任何假设(或者项目是否可行)之前,获取数据并开始一些最小的探索是至关重要的。如果你是第一次想加入数据科学的利益相关者,提前做好数据准备是很重要的,否则可能会有一个非常沮丧的雇员,他可能会很快离开。
缺乏认同和信任
你公司的高级管理层很可能会收到大量承诺通过实施“人工智能”创造奇迹的信息你可能会发现一位孤独的高管决定冒险将机器学习引入他们的组织。关于人工智能实际上如何被利用,它将解决的商业问题,以及它可以带来的价值的细节将作为细节留给你去解决。虽然这可能行得通,但组织必须了解利用机器学习所需的准备工作量以及它擅长解决的问题类型。
如果组织没有准备好,你通常会看到整个公司对机器学习的支持有限(即,它是由高管自上而下驱动的),这意味着你可能会遇到中层管理利益相关者的障碍。从数据科学家的角度来看,它可能看起来有点像这样:你刚刚为一个不习惯使用机器学习的新利益相关者完成了一个项目。在最后一次会议中,你不会得到很多关于工作的问题或反馈。任何销售人员都知道,拒绝比得不到任何反馈更容易。经验较少的数据科学家可能会尝试证明他们的解决方案的价值。然而,当你到达这一点的时候,可能已经太晚了。你的利益相关者没有参与进来,也不会最终使用你的项目。
此外,许多公司并不是真正由数据驱动的。虽然每个公司都说他们的决策是由数据驱动的(在今天的商业环境中,不这样说实际上是一种亵渎),但许多公司实际上并不是这样运作的。在许多情况下,这可能是因为没有足够的数据。结果,他们没有提高他们的分析能力,公司文化可能不重视对新流程的系统测试。如果一个数据科学家(或者具体来说是一个决策科学家)提供了独特而关键的洞察力来对抗公司的部落知识,那么直觉很有可能会占上风。员工们只是不会对这种类型的工作感到足够舒服,如果它与他们所知道的有足够的不同,他们就不会接受它。
为了避免这种情况,了解谁是你的利益相关者并让他们参与整个过程是至关重要的。不要在几周的工作后拿出一个解决方案,然后期望他们只是接受它。通过经常让利益相关者参与进来,利用他们的专业知识,并允许他们塑造你的项目解决方案,你可以利用宜家效应——一旦利益相关者亲自投入时间,他们会更加重视项目。
不知道的利益相关者
有时,利益相关者对利用机器学习感兴趣,但不知道如何使用它。你可能真的有足够的参与度和相关级别的兴趣(包括高管买入和中层管理),但他们不确定到底会发生什么,也缺乏对机器学习背后过程的清晰理解。当数据科学家不断提到数据质量问题、缺少必需的工具和基础设施,或者在现有生产系统中部署他们的第一个模型时遇到困难时,他们可能期望立即得到结果,并变得不耐烦。
从数据科学家的角度来看,事情可能是这样的:你和一个新的利益相关者开始了一个项目,这个利益相关者几乎没有数据科学方面的经验。他们想利用“ML 魔力”,但很难定义他们的具体问题。你可能会感到有压力,开始探索数据,“看看你能发现什么。”虽然这可能有效,但应尽可能避免。
首先,确定您想要尝试和预测的具体指标——花在详细说明指标计算和完成定义上的每一分钟都是值得的。这也可以帮助你从一开始就设定期望——一些利益相关者可能期望从 ML 那里得到不可能的东西,在开始项目之前,最好把这一点提前说出来。你可以先随意探索这些数据,但是不要只是开始,看看你能做得有多好。重申任何研究项目固有的不可预测性也很重要。你没有考虑到的问题会突然出现,你期望的绩效可能无法实现。在承诺具体的绩效目标或交付日期时要非常谨慎。
随着项目的进展,在向前推进太远之前,不断地运行假设分析。例如,如果您已经从一个简单的基准模型开始,与涉众讨论准确性度量。模型已经好到有价值了吗?如果不是,这是否在正确的范围内?这种复杂性在生产中实现可行吗?还需要考虑哪些其他限制因素?
经常讨论这些问题是有好处的,尤其是当项目已经取得进展并且需要考虑新的信息时。
当利益相关者改变主意时
在某些情况下,利益相关者可能非常清楚他们的要求,而你在继续之前已经同意了所有的期望。由于数据科学项目可能需要几周(甚至几个月),利益相关者可能会改变他们的请求。这可能是由于以下原因造成的:
- 最初的请求不再相关。只要有可能,使用更快的增量交付成果(瀑布式敏捷)可以帮助减少这类问题。
- 原始任务的成功将项目推向不同的方向。例如,涉众最初可能要求一个非常准确的销售预测。虽然这听起来很简单,但解决方案可能涉及多个模型的集合,每个模型都有不同的参数和季节性。在您最终改进了模型以满足最初的阈值,并且它被业务所利用之后,涉众现在要求能够解释每个预测背后的基本原理。这可能是一个比涉众想象的要大得多的请求,这种必要性最好在项目开始时讨论,因为它可能会影响设计的解决方案的复杂性。
- 利益相关者会改变——不要假设每个人都会以同样的方式对待你的工作。一个新的利益相关者不太认同,在机器学习方面的经验有限,并且普遍持较高的怀疑态度,这可能会严重影响你的项目进展。同样,争取增量速赢以展示对业务的价值也很重要。比起继续投资一个没有明确结束日期的黑箱,给一条有价值的赛道开绿灯要容易得多。
作为一名真正的专业人士,你需要考虑利益相关者如何看待你的工作,权衡影响你工作的约束和限制。无论您是与新的利益相关方合作一个项目,还是刚刚在您的公司开始数据科学,都要提前计划几个步骤-管理预期,关注增量价值,并获得项目成功所需的认可。时间太少,工作太多,不能让一个运行良好的数据科学项目白白浪费掉。祝你好运!
如果你已经读到这里,你可能会喜欢我以前的博客:
</7-must-haves-in-your-data-science-cv-9316841aeb78> https://medium.com/riskified-technology/3-ways-to-break-into-data-science-6a7a8fd679b3
每个数据分析师都应该知道的出色 AWS-Quicksight 视觉效果
使用这些视觉效果创建引人入胜的仪表板
答几乎每个以分析数据为生的人都会使用这样或那样的可视化工具。有数百种可视化数据的工具。无论是在带有 Matplotlib、Plotly 和 Bokeh 等包的 Python 编程环境中,还是在带有 SAP Lumira、Tableau 甚至 Excel 等内置软件的环境中,我们拥有的工具都比我们可能使用的工具多。
那你怎么选择最好的呢?事实上,几乎所有这些都是非常好的实现。您的选择取决于您的用例。
到目前为止,我在大多数用例中使用 Tableau,因为在服务器上共享仪表板便于终端用户使用。但是当我们需要创建某些类型的视觉效果时,它却缺乏能力。例如,您将不得不经历一个乏味的仪式来创建甚至简单的流程图。祝你好运,记住最后所有的聚合和数据乘法。
在这种情况下,Plotly 当然会有所帮助,但是你必须忍受为每一个需要的可视化对象从头创建一个好的数据框架的痛苦。
我最近遇到的一个工具消除了上述几个限制。
它将众多可视化软件的精华结合到一个基于云的商业智能服务中。它叫做 Quicksight ,作为亚马逊网络服务(AWS)的一项服务提供。您可以在以下文档中了解更多信息:
亚马逊 QuickSight —商业智能服务—亚马逊网络服务
在 Quicksight 上工作了几天后,我发现那里有几个作为标准提供的可视化,否则在其他工具中需要花费很长时间才能构建。
对于不熟悉 AWS 和 Quicksight 的人来说,有大量的文档可供使用。如果您愿意,在我们进入视觉效果之前,您可以快速浏览一下如何在 Quicksight 中连接数据的概述:
使用新数据源创建数据集— Amazon QuickSight
在这篇文章中,我将带你浏览我非常喜欢在 Quicksight 中使用的 7 个视觉效果,这是我最喜欢的倒计时。我为什么喜欢他们?因为:
- 数据准备的简单性
- 提供给最终用户的直观性
- 使用频率(基于我的使用案例)
我将带您了解所需的基本数据结构以及结果可视化。有了这些视觉效果的知识和组合,你也可以创建引人入胜的仪表板!
7.瀑布图
当我们需要显示一段时间内值的累积增加或减少时,可以使用瀑布图。虽然有许多工具可以创建这些图表,但 Quicksight 提供了最简单的一种。考虑公司收入和费用的下列累积数据:
作者截图
你需要做的就是在 Quicksight 中导入 Excel 数据。数据导入后,从左下角的视觉类型选择区域选择瀑布图。
你会看到一个图表的空白区域和一个叫做油田井的区域。
创建瀑布图所需的油田井有:
- 种类
- 价值
- 分解(可选)
将字段列表中的类型和值列分别拖动到和字段井的类别和值中。
好了,图表准备好了!
作者截图
6.漏斗图
漏斗图基本上用于显示任何业务流程的流程,最常用于销售。因为。例如,在这些图表的帮助下,我们可以看到在流程的每个步骤中用户数量的下降(这给出了特有的漏斗形状)。
考虑以下图书销售过程的累积数据,从估计的市场规模到售出的总册数:
作者截图
将数据导入 Quicksight 后,从视觉类型部分选择漏斗图。所需的油田井有:
- 分组依据
- 价值
将字段列表中的图书销售和计数列拖至字段井的类别和值中。
作者截图
5.树形地图
在我们到目前为止经历了 2 个简单的图表之后,让我们走向更复杂和有趣的图表。一种这样的视觉类型是树形图,这是我在几个用例中使用的图表之一。它用于以嵌套矩形的形式表示分层数据。这种图表中的节点以大小和颜色两个维度进行分类,为我们提供了一个非常紧凑和数据丰富的可视化。
考虑以下由产品项目、数量和总价组成的虚拟数据:
作者截图
数据导入后,从视觉类型部分选择树形图。所需的油田井有:
- 分组依据
- 大小
- 颜色
将字段列表中的项目、数量、和价格栏分别拖动到字段井的分组依据、大小和颜色中。
作者截图
正如您所看到的,图中的每个图块根据数量和价格来量化数据中的给定项目,数量和价格分别用图块的大小和颜色来表示。当分类涉及两个维度时,这使得它成为一种非常方便的视觉类型。
4.热图
这是一种数据可视化技术,以颜色显示事件的大小。通常,颜色强度的变化用于向最终用户提供关于事件如何聚集的明显视觉提示。因此,热图的一个常见用途是可视化自然环境现象。此外,它还用于可视化流体流动动力学、表面变形等。
考虑到这一点,有什么数据比城市的温度数据更适合可视化为热图呢?在本例中,我从这里下载了柏林的天气数据,并做了一些数据工程处理,以获得日期、一天中的时间和温度(以摄氏度为单位)。因此,在 Quicksight 中导入的最终数据如下所示:
作者截图
数据导入后,从视觉类型部分选择热图。所需的油田井有:
- 行
- 列
- 价值观念
将字段列表中的日期、一天中的小时、和温度列分别拖动到字段井的行、列和值中。
作者截图
瞧啊。几秒钟之内的惊人热图。这绝对是我最喜欢的可视化类型之一,但是还有更多的。
3.地图上的点
如今这是一个相当普遍的可视化。它被用来可视化国家的人口,疫情疫情,空中交通路线,供应链,等等。很有可能,无论你在哪个行业工作,你都很可能会在你的公司仪表盘中使用或看到这种视觉类型。因此,将它作为标准的可视类型才是可行的。
在 Quicksight 中,这种实现称为地图上的点,它可以理解基于纬度、经度、城市名称甚至邮政编码的数据!(尽管根据您所在的地理位置,您可能需要等待它的上市,但它刚刚在欧洲上市)。
为了演示它是如何工作的,我使用了来自德国城市数据库的人口数据,我在这里找到了。
作者截图
导入数据后,从视觉类型部分选择地图上的点。所需的油田井有:
- 地理空间(纬度、经度、城市名称等。)
- 大小
- 颜色
将字段列表中的 lat & lng 列拖到地理空间中,并将人口拖到 Size 字段中。如果需要指示另一个参数,例如人口密度,您也可以使用颜色。
作者截图
现在你知道了。有一个惊人的地图可视化是如此容易!
2.词云
词云被用作文本数据的可视表示,其可以或可以不按照任何特定的顺序或次序。也是一个随着社交媒体和文本分析领域的兴起而变得非常普遍的图表。它们特别受欢迎,可以直观地显示所使用的词频,例如标签、关键词,甚至可以分析网页或书籍中的文本。
有许多在线工具可以免费构建单词云。但是 AWS 环境中的这种实现使它变得强大。您可以使用 python 分析数据,并创建输出数据集以便在 Quicksight 中导入。这为高度灵活的文本可视化提供了广泛的可能性!
为了在这里进行演示,我创建了一个虚拟数据集,其中包含来自一项调查的成员及其母语列表:
导入数据后,从视觉类型部分选择单词 Cloud。所需的油田井有:
- 分组依据
- 大小
将字段列表中的本地语言列拖入分组依据。它会根据计数自动生成云!如果数据的结构不同,字数在不同的列中,也可以使用大小指示器。
我唯一怀念的是没有一个色域,用不同的颜色显示不同的单词。这将使可视化更加吸引人(我真的希望在未来的更新中实现这一点)。
作者截图
1.桑基图
最后,我最喜欢使用的视觉类型,因为它灵活、直观。桑基图本质上是流程图,其中流程表示与流量(或速率,或流量中的任何其他度量)成比例。
这使得 Sankey 图成为业务运营中的能量流、材料流、网络、流程链等用例中的绝对宝藏。
到目前为止,它们缺乏普遍性的唯一障碍是缺少一个简单的实现。我们可以在舞台上做到,但只能用一千种迂回的方法。或者我们可以自己编写这样的代码。所有这些都可以通过迄今为止我在商业智能软件中发现的最简单的实现来避免。
考虑下面的数据(来自德国的能源流程图— 2018,我在这里找到了):
作者截图
所有你需要的是一个来源,目的地,和两者之间的流量。让我们看看这是如何工作的。
导入数据后,从视觉类型部分选择 Sankey 图。所需的油田井有:
- 来源
- 目的地
- 重量
将字段列表中的**源、目的、和单位(千万亿焦耳)**列分别拖到字段井中的源、目的和重量中。
几秒钟内,你就拥有了魔力!基于数据的流的可视化表示。实现会根据给定的数量自动调整自己,这意味着值中的任何错误都会使图表看起来有点笨拙(例如,如果流出的比流入的多)。
作者截图
其实现的简单性使它成为这个列表中最好的可视化工具!
这是 AWS Quicksight 中 7 种视觉类型的列表!有了这些以及更多提供的和持续升级的组合,我相信你已经可以成为一个令人惊叹的视觉故事讲述者了!你喜欢哪一个?是只有我,还是你也有一些经常使用的可视化类型?我肯定想知道和学习更多。欢迎在评论中告诉我!
真棒 PyTorch 闪电模板
TLDR:py torch Lightning 模板,包含了很多特性。链接到谷歌实验室这里。
认知状态:这个模板是几周学习的结果,而不是多年的经验。把这当成你朋友的课堂笔记,而不是老师的讲义。为了让你知道我是多么的不合格和固执己见,只要看看我没能解决的问题列表就知道了。如果你知道如何解决它们,请告诉我。
可怕的闪电照片。Marc Szeglat 在 Unsplash 上拍摄的照片
你是否曾经困惑过 PyTorch 的最佳实践是什么?比如,什么时候把你的张量发给 GPU?或者什么时候打电话给 zero_grad?或者,您是否尝试过做一些新的事情,比如添加一个类似 SimCLR 的管道,并且因为写得太差而不得不重写大部分代码?又或许,你在想别人的管道是什么样的?他们的效率是 100%的吗?有哪些你一直缺失的简单小技巧和窍门?也许不是你,但我有。PyTorch 闪电 (PL)前来救援。它基本上是一个关于你的代码应该如何构造的模板。
PL 在他们的文档中有很多特性,比如:
他们还有很多模板,例如:
问题是所有这些例子都很简单。这对于演示或作为模板来说非常好。但是缺少一个更完整的例子来展示所有这些特性是如何集成的。尤其是对于深度学习领域的第一个编码项目的人来说,这就是:
模板
点击此处链接至 Google Colab。
特征
记录
张量板 HPARAMS 选项卡。出于某种原因, hp_metric 不工作,我无法修复它 T_T(黑暗模式 ftw!).(图片作者)。
- 记录到 TensorBoard 和 stdout 用于冗余(这是我自己做的,^^).我还是不能什么都抄。一些信息只放入标准输出。这是一个问题,因为如果您使用超级计算集群,输出文件有难以描述的名称,这使得查找实验细节更加困难,特别是在遥远的未来。(我也在考虑登录到 WandB )。
- 正确使用 hp_metric 以便我们可以在 TensorBoard 中选择最佳的超参数(还没有运行 T_T。作为一个临时解决方案,它保存到一个. json 中,该文件将被加载到 pandas 数据帧中)。
显示最低验证的损失曲线。该图是诊断培训中的问题的一种方式。(图片作者)。
我们可以看到参数分布的演变(文字权重和偏差,而不是品牌)。(图片作者)。
- 将参数以直方图的形式记录到 TensorBoard(这是我自己做的,^^).记录单个参数可能不现实,每个时期会有数百万个参数。
- 打印照明模块的摘要。这是一个打印输出的例子,我不能重定向到 TensorBoard 文本。
- 记录系统(硬件和软件)信息。
你能在这幅图中找到这只虫子吗?很好!现在发现我的模板中的错误,需要帮助!玄阮在 Unsplash 上拍照。
排除故障
PyTorch 号侧写员报告。(图片作者)。
- 剖析器 ( PyTorch )找出哪些层/操作是一直在窃取你的时间和内存的瓶颈。请注意,这会减慢速度。差很多!因此,在进行超参数调整之前,请务必关闭此功能。
- 健全性检查是默认开启的功能。很高兴知道这个存在。(我是吃了苦头才知道的)
GPU 内存日志。为什么会涨?(图片作者)。
- 有两种方法来监控 GPU。第一个只是监控内存,而第二个可以监控多个统计数据。我的模板就用第一个。
- 有两种方法可以缩短纪元。第一种是通过限制批次数量。而第二个是 fast_dev_run ,它限制了后台的批处理数量。我的模板直接调用限制参数。
- 对数据子集进行模型过拟合
(错误:探查器与缩短时期冲突。)
想象一下这崎岖不平的地形。格拉汉姆·斯潘塞在 Unsplash 拍摄的照片。
最佳化
- 提前停止因为当模型已经收敛时,我们不要浪费资源。
- 渐变裁剪
- 说到优化器,我过去只是简单地使用 Adam ,使用 ReduceLROnPlateau ,然后就到此为止(我甚至不为 betas 优化)。但这让我晚上睡不着觉,因为我总是怀疑自己,怀疑自己是否错过了巨大的进步。我知道这是一个非常活跃的研究领域。但是另一种选择是优化器超参数的维数灾难。这就是 PL 拯救我的地方。现在,我可以简单地将 PL 视为一个行业标准,使用所有提供的优化工具,睡得稍微轻松一些。这里有两个工具:学习率查找器,和随机加权平均。
激活分析:MNIST MLP 隐藏层激活的前 2 个主成分。(图片作者)。
保存和加载重量
- 保存最佳模型和测试它。
- 一旦你完成了你的超参数搜索,你可能想要在会话结束后加载一个检查点来对最佳模型做类似残差分析或激活分析(看上面漂亮的图)。
- (跟踪训练期间激活如何变化可能也有帮助,但我没有在这个模板中实现这一点。)
超参数调谐:
- 在我的 Google Colab 模板中有一个网格搜索的例子。是的,我知道随机搜索更好,但这只是为了演示。
- 出于某种原因,我无法让 TensorBoard 上的 hp_metric 工作,所以我做了一个. json 来解决这个问题。还包括一个代码片段来聚合。来自不同实验运行的 json 文件。
- 我还需要一个超参数优化器库,实现良好的算法,从离线文件读取和写入(因为我正在使用 HPC)。目前为止最好的解决方案是 Optuna ,因为离线很容易并行化 。
从这里去哪里?
显然,我在谷歌上的模板还没有检查过。但是你可能想在 Bolts 中检查现有的模型,并为你自己的数据集制作你自己的 LightningDataModule 。祝你好运!
参考
所有的图片,除了标题中注明的,都是我的。
Awesome R Markdown Word 报表,带有以编程方式插入的标题、输出和交叉引用
如何在 Word 中自动生成报告,以便专注于解决具有挑战性的问题
Thomas de March in(pharm Alex 统计和数据科学高级经理)和 Milana Filatenkova(pharm Alex 统计和数据科学经理)
什么是 R: R 语言被统计学家和数据挖掘者广泛用于开发数据分析软件。
什么是 R Markdown : R Markdown 是一个工具,旨在帮助使用 R 创建可再现的动态报告。R Markdown 文档是以 Markdown(一种易于编写的纯文本格式)编写的,包含大量嵌入式 R 代码。
为什么使用 R and R 降价:作为数据科学家,我们经常需要重复进行分析(例如:分析方法的验证)。自动化这样的例程可能是一种很好的方法,可以减少在执行大量重复任务时经常出现的人为错误的可能性,并将资源从枯燥的复制/粘贴类型的练习转移到具有挑战性的问题解决上。
**本帖面向谁:**本帖面向希望使用 R Markdown (Rmd)脚本创建 Word (docx)文档并希望以编程方式插入标题、输出和 Word 交叉引用的 R 用户。
**不包括的内容:**这篇文章不会介绍 R and R 降价。如果您刚刚开始使用这些工具,有许多优秀的教程可供参考。另外,安装 R 和 MS-Word 是继续本教程的先决条件。
当用 R 生成报告时,PDF 和 HTML 给出了更好的结果。但是,在职业语境中,Word 是无法避免的。
有几个包可以用来从 r 生成 Word 文档,最流行的是 rmarkdown 。Markdown 的美妙之处在于它使纯文本文档(代码)可读性更好,没有杂乱的标签。另一方面,缺点之一是 Word 的功能非常有限。例如,不能调整样式、添加表格和图表标题以及使用单词交叉引用。在其他旨在自动生成报告的软件包中,值得一提的是官员。这个软件包提供了许多在 rmarkdown 中没有的 Word 功能。但是,官码非常难读。在官里,单词交叉引用也不行。还值得一提的是,一些 R 包已经开发出来管理表格,数字编号和标题,如标题或*预订。*但是,这些软件包输出常规文本,无法生成动态标题和引用(即,您无法点击这些引用或生成图表目录)。后者带来了交叉引用的问题:当内容更新时,引用保持静态。
最后,让我们提一下最近令人敬畏的 officedown 包,它将一些 officer 特性引入 R Markdown 文档。 officedown 的一个关键特性是为引用和标题添加 Word 计算字段的选项。换句话说,这是第一个支持真正单词交叉引用的 R 包!
最后,关于 R 生成的输出(图、表……)在报告中的出现——使用 rmarkdown ,可以通过编程将它们插入 Word 报告中,这实际上是 rmarkdown 包的中心目的。但是,如果您事先不知道报告将包含多少表格、数字和章节,该怎么办呢?一个典型的例子是对多个属性执行重复分析的脚本或应用程序。让我们设想这样一种情况,当用户选择上传具有任意数量属性的数据,5、10、20、100……在这种情况下,为了以编程方式从 R 中插入标题和输出,解决方案是在 rmarkdown 中设置 chuck 选项‘results = " asis’”。该选项将使用循环的从 R 创建 R 降价代码。
那么,这篇文章的目的是什么呢?嗯,即使有可能以编程方式插入输出和标题,并通过 officedown 包获得正确的单词交叉引用,我也从未设法使这两者一起工作……当我试图在循环的*中使用交叉引用时,它从未产生想要的输出(引用不起作用,图形重复……)。我猜这个问题会在 *officedown、的未来版本中得到解决,但我就是等不及了。我需要为我的客户完成这项工作。然后,我花了很多时间在互联网上寻找解决办法,并提出了一些解决方案,最终完成了它!我并不认为我的方法是自动编译功能 Word 报告所独有的,当然还有其他方法可以达到同样的目的。这里的困难是文档的缺乏和许多与 Markdown 相关的 R 包的不成熟。这篇文章是我尝试与大家分享我的发现,这样你就不会迷失在无休止的试错过程中,试图从你的 Rmarkdown 中得到一个好的 Word 文档。
这是要点列表。
交叉引用由以下人员创建:
1.使用 run_autonum 函数创建书签
2.使用 block_caption 函数添加标题
3.文本中的引用是使用 run_reference 函数完成的
通过使用 result=‘asis’ chunk 选项,以编程方式创建分析部分中的子部分。这个选项告诉 knitr 不要把文本输出打包在逐字的代码块中(就像普通的代码块一样),而是“按原样”处理,输出原始的减价内容。在这些类型的块中:
1.使用*cat(’ \ n # # heading name \ n ‘)*创建标题。请注意’ \n '是使其工作所必需的。
2.绘图输出为:打印(绘图)
3.必须使用 knit_print 和 cat 功能输出弹性工作台
4.标题( block_caption )和引用( run_reference )需要封装到 knit_print_block 函数中
下面是一个例子。请注意,下面的代码以及它生成的 Word 报告和所有必要的文件都可以在我的 Github 上找到:https://github.com/tdemarchin/AwesomeRmarkdownWordReport
结论
虽然直到最近,从 R 生成定性的 Word 报告仍然具有挑战性,但是已经开发了新的包,使得在业务环境中生成报告成为可能。本文中描述的方法证明了手工编写 R 输出的报告已经没有用武之地了。
例子
输出
自动生成 Word 报告的示例
代号
---
title: "Awesome Rmarkdown Word report with programmatically inserted headings, outputs and cross-references"
author: "Thomas de Marchin"
date: "17MAR2021"
output:
word_document:
reference_docx: "template.docx"
---```{r setup, include=FALSE}
# Let's setup a few things.rm(list=ls())
library(knitr)
library(officedown)
library(officer)
library(ggplot2)
library(tidyverse)
library(broom)
library(flextable)# set chunks defaults
knitr::opts_chunk$set(
echo = FALSE,
message = FALSE,
warning = FALSE
)# set flextable defaults
knitr::opts_chunk$set(echo = TRUE, fig.cap = TRUE)
set_flextable_defaults(
font.family = "Arial", font.size = 9,
theme_fun = "theme_vanilla",
big.mark="", table.layout="autofit")
# formatting properties for specific paragraphs
centeredP <- fp_par(text.align = "center")```# IntroductionThe aim of this document is to introduce a way to generate word reports from R using Rmarkdown with programmatically inserted headings, outputs and Word cross-references. See [https://towardsdatascience.com/awesome-r-markdown-word-report-with-programmatically-inserted-headings-outputs-and-19ad0de29a22](/awesome-r-markdown-word-report-with-programmatically-inserted-headings-outputs-and-19ad0de29a22) to understand the context of this example.# DataWe will use the built-in iris dataset as an example.
This dataset consists in Petal and Sepal width and length measurements for three iris species.
Table `r run_reference("summaryTable")` shows summary statistics.```{r data, echo = FALSE}
# this chunk is a normal chunk compared to the next one# create the bookmark for the summary table
tab_num <- run_autonum(seq_id = "Table", pre_label = "Table ", bkm = "summaryTable")
# add the caption
block_caption(label= "Summary table for the iris dataset",
style = "caption", autonum = tab_num)# create the summary table and output it with flextable()
summaryData <- iris %>% gather(value="value", key="Attribute", -Species) %>% group_by(Attribute, Species) %>% summarise(n=n(), mean=mean(value), sd=sd(value), min=min(value), max=max(value))
summaryData %>% flextable() %>% merge_v(j = "Attribute") %>% colformat_double(j=c("mean", "sd"), digits = 2)
```# Analysis```{r analysis, echo = FALSE, results = 'asis'}
# this chunk will programmatically generates Markdown code (results='asis')# split the data by specie to simulate different attributes to analyze, it is here 3 here but it could be any number.
data <- split(iris, iris$Species)
uniqueSpecies <- levels(iris$Species)# for loop
for(i in 1:length(uniqueSpecies)){
dataSubset <- data[[uniqueSpecies[i]]]
# print the heading
cat("\n##", uniqueSpecies[i], "\n")
# print an empty line
cat("\n <br> \n")
# print some text
cat("Figure ")
# reference the figure below (note the use of knit_print_run function in 'asis' chunks)
knit_print_run(run_reference(paste0("pData", uniqueSpecies[i])))
cat(" shows the relation between Sepal width and length.")
# plot the data
pData <- ggplot(aes(x=Sepal.Length, y=Sepal.Width), data=dataSubset) +
geom_point() + geom_smooth(method='lm', se=F) + labs(title=uniqueSpecies[i])
# output the plot (note the use of the print function in 'asis' chunks)
print(pData)
cat("\n") # sometimes you need to add this to make things work
# Add the figure numbering and caption (note the use of the knit_print_block function in 'asis' chunks)
fig_num <- run_autonum(seq_id = "Figure", pre_label = "Figure ", bkm = paste0("pData", uniqueSpecies[i]))
knit_print_block(block_caption(paste0("Scatter plot of Sepal Width vs Sepal Length for ", uniqueSpecies[i], ". Blue line is a linear regression."),
style = "caption", autonum = fig_num))
# print some text
cat("A linear regression was performed using Sepal.Length as the response and Sepal.Width as the explanatory variable. Table ")
knit_print_run(run_reference(paste0("tabRegression", uniqueSpecies[i])))
cat(" shows the parameters estimates and 95% Confidence intervals.")
cat("\n") # sometimes you need to add this to make things work
# do a regression generate the fit summary table
regression <- lm(Sepal.Length ~ Sepal.Width, data=dataSubset)
tabRegression <- tidy(regression)
tabRegression <- cbind(tabRegression, confint(regression, level=0.95))
# Add the table numbering and caption (note the use of the knit_print_block function in 'asis' chunks)
tab_num <- run_autonum(seq_id = "Table", pre_label = "Table ", bkm = paste0("tabRegression", uniqueSpecies[i]))
knit_print_block(block_caption(paste0("Parameters estimates of the fit for ", uniqueSpecies[i]),
style = "caption", autonum = tab_num))
# output the summary table (note the use of knit_print and cat functions in 'asis' chunks)
tabRegression %>% flextable() %>% knit_print() %>% cat()
cat('\n')
}# Add a page break
run_pagebreak()
```# Conclusion`r fpar(ftext("Congratulations!", fp_text(font.size = 12, bold = TRUE, color = "#C32900")), fp_p = fp_par(text.align = "center"))`
参考
https://R Markdown . R studio . com/
https://bookdown.org/yihui/bookdown/
https://ardata-fr.github.io/officeverse/
https://science loft . com/technical/programmably-create-new-headings-and-outputs-in-R Markdown/
解释 AWS 应用程序迁移服务架构
了解流程
AWS 应用程序迁移服务(AWS MGM)是一项提升和转移 AWS 服务,任何人都可以通过 AWS 管理控制台进行访问。在这篇文章中,我将回顾 AWS MGN 服务和网络架构。AWS MGM 是一种灵活、可靠、高度自动化的提升和移位解决方案。
塞缪尔·马丁斯的图片
您可以使用它来帮助简化、加快和降低将应用程序迁移到 AWS 的成本。您还可以使用 AWS NGN 将物理、虚拟或云服务迁移到 AWS,而不会出现兼容性问题、性能问题、中断或长时间切换窗口。您可以从运行受支持操作系统的任何源基础架构中迁移应用程序和数据库。这包括 Oracle 和 SQL server 等常见数据库、SAP 等任务关键型应用程序以及自主开发的应用程序。AWS MGN 支持最常见的 windows 和 Linux 操作系统,并在数据块级别持续复制它们的数据。
塞缪尔·马丁斯的图片
左侧是源环境。它可以包括物理、虚拟或云服务器的组合。在此示例中,源环境有两台服务器,其中两个磁盘连接到顶层服务器,三个磁盘连接到底层服务器。在右侧,您可以看到服务器将被迁移到的 AWS 区域。在本例中,已经定义了子网。
首先,在源服务器上安装 AWS 复制代理。代理可以以无人值守的方式安装,不需要重新启动。
塞缪尔·马丁斯的图片
安装时,代理会对使用 TLS 1.3 加密的 AWS MGM API 端点执行身份验证握手。这将向服务注册代理,而服务又会自动提供临时区域子网资源。
塞缪尔·马丁斯的图片
塞缪尔·马丁斯的图片
临时区域子网使用低成本计算和存储来保持源环境中的数据在 AWS 上同步。临时区域子网由以下资源组成:
- 复制服务器。这些是轻量级、Amazon elastic、云计算或 Amazon EC2 实例。
- 暂存卷。这些是低成本的亚马逊弹性块商店,或亚马逊 EBS 卷。
- 亚马逊 EBS 快照。这些是增量 EBS 快照。
对于复制的每个源磁盘,AWS MDN 都会在临时区域子网中创建一个大小相同的 EBS 卷,以便进行数据同步。在此示例中,五个复制源磁盘导致五个等效的 EBS 卷连接到登台区复制服务器。
塞缪尔·马丁斯的图片
安装代理并创建临时区域子网资源后,复制开始。数据经过加密,并直接从源服务器传输到复制服务器卷。用户控制复制路径,在复制路径中,他们可以使用专用连接选项,如 AWS direct connect 或 VPN。
塞缪尔·马丁斯的图片
该服务自动管理临时区域子网资源,根据并发复制的源服务器和磁盘按需扩展或缩减资源。因此,用户无需维护操作即可管理中转区子网。AWS MGN 服务器是短暂的资源,由服务不时自动轮换。
需要注意的重要信息包括:
- 默认情况下,复制服务器使用 T3.small Linux 实例,启用 T3 无限制定价。
- 一般经验是,一台复制服务器最多可以处理 15 个并发复制的磁盘。
- 默认情况下,数据在传输过程中使用 AES 256 位加密密钥进行压缩和加密。也可以在您的 AWS 区域使用 Amazon EBS 加密对其进行静态加密。
复制从初始同步开始。在初始同步期间,代理将源磁盘上的所有内容复制到分段区域子网中的 EBS 卷。同时,代理会在数据权限发生时跟踪并持续复制数据权限,并将数据异步复制到临时区域子网中的相关资源。初始同步完成后,连续复制将无限期继续。
安装代理并开始复制后,检查启动设置。启动设置是服务、特定配置和易于启动的模板的组合。启动设置定义了迁移实例的启动位置和方式,包括子网安全组、实例类型、卷类型和标记。配置启动设置后,等待初始同步完成。当源服务器被标记为可以测试时,您可以选择 test and cutover 按钮并启动实例。然后,AWS MGN 将发出一系列 API 调用来启动启动过程,并根据您配置的启动设置在 AWS 上自动启动实例。
在启动过程中,该服务会自动启动转换服务器。转换过程包括对驱动程序、网络和操作系统许可证的更改,以便实例可以在 AWS 上本地引导。
塞缪尔·马丁斯的图片
塞缪尔·马丁斯的图片
塞缪尔·马丁斯的图片
与复制服务器(只要复制处于活动状态,复制服务器就处于活动状态)不同,转换服务器仅在启动过程中出于转换服务器的特定目的而启动,然后立即终止。转换完成后,新的实例在 AWS 上启动并准备好使用。请注意,连接到已启动实例的卷代表启动时源服务器的状态。
塞缪尔·马丁斯的图片
启动后,新创建的卷不再与源服务器保持同步。对源服务器所做的任何更改仍然会由 AWS 复制代理不断复制到临时区域卷,但是这些更改不会应用到已启动的实例。每次启动新的测试或转换实例时,它都会反映从源服务器复制到临时区域子网的数据的最新状态。使用这种设计,您可以快速测试启动的实例,而不会中断源服务器或复制过程。您还能够按需启动源服务器的最新拷贝。为同一个源服务器启动新实例将自动清理并覆盖该服务器以前的副本(如果以前启动过的话)。这有助于最小化成本,并避免未使用资源的积累。
要准备运行 AWS MGN 的网络,请设置以下连接:
- 源服务器和 AWS MGN API 端点之间的连接。允许源服务器上的 AWS 复制代理和 AWS MGN 端点之间的端口 443 上的连接。这种连接的目的是身份验证、配置和监控。对于 AWS 复制代理安装,并且只要源服务器正在复制,就必须有这种连接。因为这是一个 HTTPS 连接,所以可以使用源服务器上的标准设置通过 web 代理进行重定向。您还需要允许源服务器和 S3 之间的连接。设置此连接,以便 AWS 复制代理可以从 S3 存储桶中检索必要的软件组件。
塞缪尔·马丁斯的图片
塞缪尔·马丁斯的图片
- 源服务器和临时区域子网之间的连接。AWS 复制代理将数据从源服务器直接发送到端口 1500 上的复制服务器。因此,必须允许通过端口 1500 进行连接,以实现连续数据复制。
塞缪尔·马丁斯的图片
- 中转区之外的连接。在临时区域子网中启动的 AWS MGN 复制服务器和转换服务器必须在端口 443 上持续与 AWS MGM 端点通信,以便进行身份验证、配置和监控。
塞缪尔·马丁斯的图片
只要复制正在进行,这种连接就必须存在。此外,必须允许与 S3 的连接。当复制或转换服务器启动时,它连接到 S3 存储桶以下载软件和配置文件。
塞缪尔·马丁斯的图片
- 最后,复制服务器的角色之一是发出 API 调用,以便在复制期间拍摄分段 EBS 卷的快照。为此,还必须在端口 443 上建立到 EC2 API 端点的连接。
塞缪尔·马丁斯的图片
感谢您花时间来看这个架构。我希望这有助于你理解 AWS MGN,因为它帮助了我,因为我正在研究它。