使用无服务器和 SAM 部署预定的 Lambda
实践教程
使用 AWS SAM 和无服务器构建和部署计划的 AWS Lambda 以及外部平台相关包
图片来源:来自 unsplash 的古伊列梅·加西亚
Lambda 构建和部署
有几种方法可以部署 Lambda 函数,最常见的方法是在 IDE 中编写代码,测试,保存,然后部署。您可能还需要它在某个时间运行,同样,更多的点和 Cloudwatch 事件的点击。这不符合最佳实践、易于管理如撤销变更、版本控制和代码可重用性。
基础设施作为代码
然而,由于许多开源工具的可用性,管理和供应云资源已经变得容易得多,这些工具允许您通过使用人类可读的配置文件(如 YAML)来定义和配置您的资源。其中, AWS 无服务器应用程序模型(SAM) 和无服务器,是将 Lambda 部署为依赖项和 IaC 的最佳框架。
问题陈述:暂停笔记本资源
Cron 调度的 Lambda 的设计视图
与许多数据科学家一起工作,我们经常面临的问题之一是笔记本电脑在下班后不使用时仍在运行。在本文中,我将演示如何使用 SAM 和 Serverless 部署一个带有外部依赖的 cron 调度的 Lambda,以挂起 SageMaker 笔记本。
我们将部署的 Python 代码如下:
注意:任何带有 always_on: no 标签的笔记本都将被暂停
和需求文件:
requirements.txt 内容
我们用熊猫做什么……没什么,我只是想展示一个引入外部依赖的例子。
设置 AWS CLI
确保您的计算机上安装了 AWS CLI,并且您已经使用aws configure
设置了环境。
最好练习 使用临时凭证,或 CI/CD 环境进行部署。
AWS 无服务器应用程序模型(SAM)
图片来源: AWS
SAM 概述
AWS SAM 是一个开源框架,允许您将 Lambda 等无服务器应用程序部署到 AWS。它支持配置管理、本地测试和调试,以及与 DevOps 流程的集成。SAM 使用最佳实践方法将配置文件转换为 Cloudformation 脚本。
部署具有外部依赖性的 SAM 调度 Lambda
5 个简单步骤:
1。安装 SAM CLI
按照此链接安装 SAM CLI,我们将使用它将 SAM 包部署到 AWS 上。SAM 需要 Docker 和 amazon 映像来进行本地测试和构建部署,因为 Lambda 是基于 Linux 的环境。
> docker pull amazon/aws-sam-cli-build-image-python3.8
2。创建您的 SAM 项目
运行以下命令创建您的项目:
> sam init --runtime python3.8 *--app-template hello-world --name suspend_sm_notebook*
您应该看到创建了以下文件夹和文件:
注意:文件夹 hello_world 重命名为 source,并添加了 requirements.txt
将 Python 代码复制到handler.py
中,然后就可以构建 YAML 文件来部署 Lambda 和基于时间的 Cloudwatch 事件了。
3。构建 SAM 模板
完整模板
使用 SAM,您可以部署各种无服务器模型,但是因为我们想要部署 Lambda,所以资源类型将是Function
。还增加了一项政策,允许其与 SageMaker 笔记本电脑进行交互。SAM Event
将通过 Cron 表达式经由Schedule
调用 Lambda 函数。
4。建造你的萨姆
一旦您完成了测试,您就可以构建您的部署,该部署会将您的代码与来自需求的任何依赖项打包在一起。由于pandas
的性质,它必须在类似 lambda 的容器中编译。
**>** sam build --use-container --manifest source/requirements.txt
5。部署您的 SAM
最后,您可以部署到您的 AWS 环境中。SAM 将使用在您的设备上设置的 AWS 配置,只需确保它具有所需的访问权限和现有的 S3 存储桶。
**>** sam deploy --stack-name SuspendSMNotebookSAM --s3-bucket sam-lambda-deploy-apse2-jy --region ap-southeast-2 --capabilities CAPABILITY_IAM
无服务器应用框架
图片来源:无服务器
无服务器概述
Serverless 是一个开源框架,它允许您将无服务器应用程序部署到云中。与 SAM 不同,它还允许您部署到其他云,如 GCP 和 Azure,并有一个免费的托管仪表板来提供完整的应用程序生命周期管理视图。
部署具有外部依赖性的 SAM 调度 Lambda
4 个简单步骤:
1。安装无服务器 CLI
按照此链接进行无服务器设置。对于那些已经安装了节点的用户,可以使用 npm 进行安装。无服务器允许您安装可用于部署的插件。在这种情况下,python-requirements 插件用于允许无服务器将 python 与需求中的依赖项打包在一起。
**>** npm install -g serverless
**>** docker pull lambci/lambda:build-python3.8
**>** serverless plugin install -n serverless-python-requirements
2。创建您的无服务器项目
运行以下命令创建您的项目:
**>** serverless create --template aws-python3 --path suspend_sm_notebook_serverless
您应该会看到以下创建的文件:
注意:添加一个 requirements.txt
3。构建无服务器模板
完整模板
作为一个多云框架,必须将目标提供商定义为 AWS 和 ap-southeast-2 region。添加 Lambda 的策略和角色是为了授予 SageMaker 访问权限,以及授予 cron 调度调用schedule
访问权限。
4。部署您的无服务器
这一步很简单,只需运行下面的命令,它会在后台将您的无服务器模板转换为 Cloudformation,在需求中打包依赖项,并为您的部署创建一个 S3 存储桶。
> serverless deploy
回滚和删除部署(可选)
无服务器允许您回滚到以前的部署:
> serverless deploy list
> serverless rollback --timestamp <timestamp>
或者清理并从 AWS 中删除我们部署的堆栈。
> serverless remove
那么…我应该用哪一个呢?
图片来源:作者
正如您所看到的,AWS SAM 和 Serverless 都实现了允许您使用外部平台相关包将 cron 调度的 Lambda 部署到 AWS 的目标。这些框架在配置格式和简单性方面本质上也是相似的。然而,它们也有各自的优点和缺点:
无服务器和 SAM 的比较
在选择 Serverless 还是 SAM 时,一些因素应该基于您想要实现的目标、您的团队能力和专业知识以及强大的社区(开源)。
作为一个建议,如果您在一个多云环境中工作,无服务器可能是一个更好的选择,但是如果本地测试和评估更重要,那么 SAM 可能更适合您的团队。
关于我
我喜欢写媒体文章,并为知识共享社区做贡献。除了尝试新的食物配方,我还帮助企业构建云和数据解决方案。请随时与我联系并向我问好!
— 李帝努·雅玛
使用跳过连接来增强去噪自动编码器算法
在 Flickr 的一个 RGB 图像样本上,比较了具有跨越瓶颈的残差网络的自动编码器和没有残差网络的自动编码器的去噪性能。
自动编码器:
官方的 Keras 博客,称自动编码器为“自我监督算法的一个例子,因为它们的目标是从输入数据生成**。因此,它们被用于图像重建的任务。**
自动编码器的主要部件有: 编码器、瓶颈和解码器 。编码器在每一步提取图像特征,并在此过程中压缩输入数据。瓶颈将输入限制在其最低维度,即输入数据的压缩表示。解码器出现在这个瓶颈之后,用于重构输入数据。损失测量用于比较生成或重建的数据与输入数据的接近程度。**
图像去噪:
来源: GIPHY
数据去噪图像是自动编码器的一个常见应用。图像中的噪声可以理解为图像颜色或亮度的随机变化,降低图像质量。在图像数据的各种使用情况下,去除这种噪声通常是预处理步骤。卷积自动编码器可用于此目的。编码器学习提取将它们与图像中的噪声分开的特征。从而压缩图像。来自瓶颈的最终压缩表示被传递到解码器。解码器最终解压缩图像,使噪声最小化。
跳过从编码器到解码器的连接
我们知道深度神经网络遭受退化问题 。由于自动编码器具有多个卷积和去卷积层,因此当重建图像时,由于这种信息丢失,它们的性能也会受到影响。由跳跃连接组成的剩余网络是解决这个问题的已知方案。因此,为了提高自动编码器的性能,可以从编码器到解码器添加这样的“跳过连接”,即跨越瓶颈 的 。这些附加连接可以直接将特征映射从编码器的较早层发送到解码器的较晚层。这有助于解码器形成输入图像的更清晰定义的解压缩。****
跨越瓶颈的剩余网络,来源:作者
为了了解这些残差网络的优势,让我们来看看卷积自动编码器模型不同阶段的激活输出。首先,我们训练了一个没有跨越瓶颈的跳跃连接的自动编码器模型。然后,我们添加它们,并在相同的图像样本上再次训练模型。
数据集
10K RGB 图像的样本取自 Kaggle 上 Flickr 图像的数据集。所有的分析都是使用 Google Colab 的 GPU 完成的。
模型
我们使用 Keras 的功能 API 来构建自动编码器模型。由于计算限制,输入图像被调整到较小的尺寸(128,128,3)。图像像素也以 1/255 的因子标准化。此外,通过应用高斯噪声矩阵,图像被故意破坏。这些损坏的图像形成了自动编码器的输入,而原始图像在训练模型时被用作目标。构建卷积网络时,重要的是要记住,随着深入,通道或滤波器的数量会增加,而输入的大小(高度和宽度)会减小。
****#Input**
input_img = Input(shape=(128, 128, 3))**#Encoder**
y = Conv2D(32, (3, 3), padding='same',strides =(2,2))(input_img)
y = LeakyReLU()(y)
y = Conv2D(64, (3, 3), padding='same',strides =(2,2))(y)
y = LeakyReLU()(y)
y1 = Conv2D(128, (3, 3), padding='same',strides =(2,2))(y) **# skip-1**
y = LeakyReLU()(y1)
y = Conv2D(256, (3, 3), padding='same',strides =(2,2))(y)
y = LeakyReLU()(y)
y2 = Conv2D(256, (3, 3), padding='same',strides =(2,2))(y)**# skip-2**
y = LeakyReLU()(y2)
y = Conv2D(512, (3, 3), padding='same',strides =(2,2))(y)
y = LeakyReLU()(y)
y = Conv2D(1024, (3, 3), padding='same',strides =(2,2))(y)
y = LeakyReLU()(y)**#Flattening for the bottleneck**
vol = y.shape
x = Flatten()(y)
latent = Dense(128, activation='relu')(x)**
下面是解码器的代码。转置卷积或反卷积层用于构建该解码器。去卷积层的工作方式大致类似于卷积层和上采样层的组合。最初,我们在没有第一个和第二个跳过连接的情况下训练模型。然后,使用 Keras 中 layers API 的 Add()将这些连接从编码器的早期层添加到解码器的后期层。lrelu_bn()辅助函数用于将激活应用于这些添加,并将结果传递给批处理规范化层。
****# Helper function to apply activation and batch normalization to the # output added with output of residual connection from the encoder**def lrelu_bn(inputs):
lrelu = LeakyReLU()(inputs)
bn = BatchNormalization()(lrelu)
return bn**#Decoder**
y = Dense(np.prod(vol[1:]), activation='relu')(latent)
y = Reshape((vol[1], vol[2], vol[3]))(y)
y = Conv2DTranspose(1024, (3,3), padding='same')(y)
y = LeakyReLU()(y)
y = Conv2DTranspose(512, (3,3), padding='same',strides=(2,2))(y)
y = LeakyReLU()(y)
y = Conv2DTranspose(256, (3,3), padding='same',strides=(2,2))(y)
y= Add()([y2, y]) **# second skip connection added here**
y = lrelu_bn(y)
y = Conv2DTranspose(256, (3,3), padding='same',strides=(2,2))(y)
y = LeakyReLU()(y)
y = Conv2DTranspose(128, (3,3), padding='same',strides=(2,2))(y)
y= Add()([y1, y]) **# first skip connection added here**
y = lrelu_bn(y)
y = Conv2DTranspose(64, (3,3), padding='same',strides=(2,2))(y)
y = LeakyReLU()(y)
y = Conv2DTranspose(32, (3,3), padding='same',strides=(2,2))(y)
y = LeakyReLU()(y)
y = Conv2DTranspose(3, (3,3), activation='sigmoid', padding='same',strides=(2,2))(y)**
因为输入图像是标准化的,所以它们的结果像素值在 0 和 1 之间。为了获得可比较的重建图像,在最终层中使用了“sigmoid”激活。在训练集之外,1k 个图像被用作验证集。使用二元交叉熵作为损失函数。最后,两个模型都被训练了 200 个时期,最小批量为 32。学习率为 0.001 的 Adam 优化器在收敛时给出最小的训练和验证损失。
让我们来看看测试图像模型各层的激活输出。这将有助于我们清楚地看到编码器-解码器的作用!
激活输出是使用 keract 包创建的。
测试图像:
我们拍摄一张哥伦比亚大学校园的测试图片。测试图像根据模型调整了大小,并添加了随机噪声。“有噪声的”图像被用作模型的输入。
************
原始调整大小的图像(左),添加了随机噪声的图像用作模型的输入。(右)****
编码器的激活输出
第一卷积层的激活输出
由于两种型号的编码器相同,因此该部件的激活输出也相同。在检查前三个卷积层的激活输出时,可以看到大部分图像信息被保留。这也可能意味着图像中的噪声也保留在模型的这个阶段。然而,当我们深入模型时(参见第三个卷积层之后的层的激活输出),保留的信息相当抽象。该模型开始提取更高层次的特征,例如边界、拐角和角度。
第二和第三卷积层的激活输出
“摘要”第四、第五和第六卷积层的激活输出
第七卷积层的激活输出和输入图像的压缩表示
解码器的激活输出(没有跨越瓶颈的跳跃连接的模型)
有趣的是,解码器的激活输出与上述编码器的序列完全相反。
第一、第二和第三去卷积层的激活输出
在第三个去卷积层之后,我们仍然看不到图像的任何边缘被再次形成。
第四、第五和第六解卷积层的激活输出
如下图所示,在解码器的最后一层,没有明确定义图像的解压缩方式!
********
第七反卷积层和最后一层的激活输出
解码器的激活输出(添加了跳过瓶颈连接的模型)
正如在编码器的激活输出中所看到的,其早期层保留了噪声,但是后期层提取了图像的更高表示。因此,从编码器的第三和第五卷积层到解码器的第三和第五去卷积层进行跳跃连接。该解码器的第一、第二和第三解卷积层的激活输出与先前版本的解码器相同。然而,在第三层之后,我们看到激活输出与先前解码器的不同。下面我们可以清楚地看到,在第六个反卷积层的激活输出中,图像再次形成。
第四、第五和第六解卷积层的激活输出
该解码器的最后一层给出了测试输入图像的清晰定义的解压缩版本。
********
第七反卷积层和最后一层的激活输出
结果的比较
第一个模型的去噪图像输出:
具有跨越瓶颈的跳跃连接的第二模型的去噪图像输出:
我们可以清楚地看到,跨越瓶颈连接的 autoencoder 模型性能更好!
前方的路…
除了对图像去噪之外,这种方法可以用于自动编码器的任何图像重建应用。虽然跳过连接提高了自动编码器的性能,但是可以试验这些连接的位置和数量。该模型也可以用不同级别的噪声因子来训练,以便更好地概括结果。完整的代码可以访问这里!
参考资料:
J.董,人。毛,沈振华。杨,【2017】利用卷积自动编码器和对称跳跃连接学习深度表示
使用通气管进行多标签标注。
如何使用浮潜的多类实现来创建多标签
hazy research @Snorkel.org的浮潜
sculpt 是一个很好的小软件包,它允许我们使用标签函数(LFs ),简单如启发式或关键字,复杂如算法和人工注释器,以便创建一个标签数据集,用于训练分类器。
“通气管是一个系统,用于以编程方式建立和管理训练数据集**,而无需手动标记**。在 snuck 中,用户可以在几小时或几天内开发大型训练数据集,而不是在几周或几个月内手工标记它们。”—Snorkel.org
动机
使用通气管来创建多标签的动机很简单。通气管允许我们使用简单的试探法或关键字来创建一个监督数据集。当我们需要理解为什么一个样本被分配到一个特定的类别时,使用它作为一个标记算法允许一定程度的清晰度。你也可以想到这样一个场景,你创建了一个启发式的多标签算法,分配稀疏数量的多标签;当您的产品或功能需要大量的多标签,以便为您的客户提供更高的价值。
通气管
*值得注意的是,在整个过程中,我们实际上是在创建两个分类器,第一个使用的是 sauce 的 MajorityLabelVoter 或 *LabelModel,前者为我们提供了一个多数票作为基线,后者为我们提供了 secret-sauce 模型,后者是为了训练一个机器或深度学习(MLDL)算法,因为我们不想依赖我们创建的标签函数。我们希望训练一个不局限于我们的关键字、正则表达式等的分类器。我们想要一个比我们给它的更一般化的分类器。理想情况下,它会发现,例如,我们在标记过程中没有考虑到的标记与我们的最终标记之间的相关性。
使用多级通气管
首先,我们需要了解如何使用通气管。考虑一个情感分类任务和下面的句子:1。“蛋糕尝起来真难吃”,2。“奶油真好”& 3。“这食物是公平的”。这些句子分别是否定句、肯定句和中性句。因此,我们将创建几个相应地分配标签的逻辑框架。
*from snorkel.labeling import labeling_function
@labeling_function()
def lf_keyword_good(x):
return POSITIVE if "good" in x.text.lower() else ABSTAIN
@labeling_function()
def lf_keyword_bad(x):
return NEGATIVE if "bad" in x.text.lower() else ABSTAIN@labeling_function()
def lf_keyword_fair(x):
return NEUTRAL if "fair" in x.text.lower() else ABSTAIN*
剩下的过程很简单,一旦你有了很多 LFs,你就可以把它们用在你的熊猫身上。DataFrame* 训练其中一个模型,即 MajorityLabelVoter 或 LabelModel。*
*from snorkel.labeling import LabelModel, PandasLFApplier
# Define the set of labeling functions (LFs)
lfs = [lf_keyword_bad, lf_keyword_good, lf_keyword_fair]
# Apply the LFs to the unlabeled training data
applier = PandasLFApplier(lfs)
L_train = applier.apply(df_train)
# Train the label model and compute the training labels
label_model = LabelModel(cardinality=3, verbose=True)
label_model.fit(L_train, n_epochs=500, log_freq=50)
df_train["label"] = label_model.predict(L=L_train, tie_break_policy="abstain")*
df_train[“label”] 也将包含弃权标签,因此为了进一步训练我们的二级分类器,我们必须将它们过滤掉。
*df_train = df_train[df_train.label != ABSTAIN]*
同样,在过滤的数据集上训练二级分类器(在本例中为随机森林)的目的是“推广到标注函数和T0 的覆盖范围之外
***from** **sklearn.ensemble** **import** RandomForestClassifier
clf = RandomForestClassifier()
clf.fit(df_train.drop(['label'],axis=1), df_train["label"])*
将通气管用于多标签
通气管只能作为多类贴标机开箱使用。要将其用于多标签,您可以执行以下三种方法之一:
- 使用 MajorityLabelVoter 的 predict_proba()并分配所有“概率”≥ 0 的类。即第一个和最后一个[0.5,0,0.5]。我们可以把它想象成一个样本,它存在于两个聚类中,或者来自两个类别的两个关键词,这允许一个样本具有多个标签。例如,“汉堡包有好有坏”。
- 使用 LabelModel 的 predict_proba,分配所有概率高于“拐点”的类。可以用 Kneed 算出来。本质上,我们的概率是 softmax,只有少数人会得到高值。请注意,根据我的实证测试,MajorityLabelVoter 的概率值与 LabelModel 的概率值之间有很高的相关性,即前者是一个“硬”softmax,而后者是你对 softmax 的预期。即[0.5,0,0.5]对[0.45,0.06,0.49]。
- 使用 MajorityLabelVoter 或 LabelModel 训练“一对所有”模型,并根据预测分配多标签。请注意,在考虑放弃标签时,您需要考虑一个策略。
最高概率的图示。
实践中的多标签
因为“1”和“2”给出了相似的输出,并且从“1”获得多标签的计算更简单,所以我选择基于 1 分配多标签。
请注意,当一个模型分配一个弃权标签时,它不能决定谁是赢家。我们在 predict_proba()的输出中观察到了这种行为。比如考虑下面这个概率向量:[1.0,0,0],这里的赢家是第一类。现在,考虑下面的向量:[0.5,0,0.5],我们看到这不是一个明确的赢家,因此,浮潜将分配一个弃权标签。
当我们为每个具有非零概率的类分配一个标签时,我们实际上消除了所有的放弃标签,标记了所有的数据集,并为每个样本分配了许多多标签。
以下代码使用了一个 MajorityLabelVoter 分类器,并根据所有分数高于零的类分配标签。就这么简单:)。
*from snorkel.labeling import MajorityLabelVoter
from sklearn.preprocessing import MultiLabelBinarizerY = [['POSITIVE', 'NEGATIVE', 'NEUTRAL']]# fit a MultiLabelBinarizer
mlb = MultiLabelBinarizer()
mlb.fit_transform(Y)# create a majority vote model and predict
majority_model = MajorityLabelVoter(cardinality=3)
predictions = majority_model.predict_proba(L=L_test)df_multilabel = pd.DataFrame()
df_multilabel['predict_proba'] = predictions.tolist()# get all the non zero indices which are the multi labels
df_multilabel['multi_labels'] = df_multilabel['predict_proba'].apply(lambda x: np.nonzero(x)[0])
#transform to mlb for classification report
df_multilabel['mlb_pred'] = df_multilabel['multi_labels'].apply(lambda x: mlb.transform([x])[0])print(df_multilabel.head())#convert to str in order to see how many multi labels did we gain
multi_label_string = df_multilabel.multi_labels.apply(lambda x: ", ".join(le.inverse_transform(x)))
print(multi_label_string.value_counts()[:50])# print some metrics using classification report
y_pred = df_multilabel.mlb_pred.apply(lambda x: list(x)).to_numpy().tolist()
y_true = mlb.transform(Y.values).tolist()print(classification_report(y_true, y_pred, target_names = mlb.classes_))*
就这样,您已经为每个样本创建了多个标签。请记住,由于这种使用浮潜的方法论,在标记策略方面非常贪婪,您可能会得到一个非常面向回忆的模型。你的经历会有所不同。
Ori Cohen 博士拥有计算机科学博士学位,主要研究机器学习。他是 TLV 新遗迹公司的首席数据科学家,从事 AIOps 领域的机器和深度学习研究。
用 SQL 玩‘你有过吗?’
高级 SQL 技术第 2 部分:collect_list、group_concat、agg_array、string_agg 等等
学习使用高级 SQL 技术可以在两个方面帮助你。它使您能够更快地获得洞察力,如果您使用输出进行进一步分析,您可以以最有效的布局清理和格式化数据。如果您的数据已经到达一个可以直接在 python 中使用的格式良好的列表中,该怎么办?让我们回顾一下不同数据库中的一些技术,继续使用我在以前的文章中使用的恶劣天气细节数据。
虽然这很重要,但我不想只讨论代码的语法,而是想把重点放在业务用例上。学习如何找出哪种技术可以帮助你回答业务问题是至关重要的。为了让它变得有趣,我喜欢关注那些超出典型范围的问题。好吧, Sharknado 的文章有点出格。
这个问题
佛罗里达,你曾经…经历过导致生命损失的天气事件吗?
给我一份所有死亡事件的清单。
用 SQL 怎么回答这个问题?
答案取决于你的数据库。每个数据库可能需要稍微不同的 SQL。这也取决于你希望你的结果如何格式化。
需要考虑的事项:
请求者将如何使用该列表?如果他们要将列表传递给 python 程序,您需要提供不同的格式,以便他们将列表剪切并粘贴到 PowerPoint 演示文稿中。
你想要那个列表排序吗?您希望对列表进行重复数据删除吗?
数据
数据本质上是“垂直的”,由每个事件的时间戳列出。我们的目标是将这个事件数据展平成一列。
样本风暴数据—作者截图
样本风暴数据—作者截图
如果你想在家玩,这里有一个数据链接:
恶劣天气数据目录(SWDI)是美国恶劣天气记录的综合数据库。
www.ncdc.noaa.gov](https://www.ncdc.noaa.gov/ncei-severe-weather-data-inventory)
MySQL: GROUP_CONCAT()
MySQL Group_Concat 返回找到的元素的列表,以逗号分隔。它没有类似 python 的列表格式[“c “,” d “,” e “,” f”]。您可以使用一些额外的代码来创建这种格式,如下所示。
MySQL Group_Concat —作者截图
SELECT
STATE
, CASE WHEN DEATHS_DIRECT > 0 OR DEATHS_INDIRECT > 0
THEN 1 ELSE 0
END AS DEADLY_EVENT
, SUM(CASE WHEN DEATHS_DIRECT > 0 OR DEATHS_INDIRECT > 0
THEN 1 ELSE 0 END)
AS TOTAL_DEADLY_EVENTS
, COUNT(*) AS TOTAL_EVENT_COUNT
, GROUP_CONCAT(EVENT_TYPE) AS EVENT_LIST
, GROUP_CONCAT(DISTINCT EVENT_TYPE) AS UNIQUE_EVENT_LIST
, CONCAT('[' , GROUP_CONCAT( DISTINCT CONCAT( '\"', EVENT_TYPE ,'\"')), ']') AS UNIQUE_EVENT_LIST_BRACKETED
FROM severe_events_details
WHERE STATE = 'FLORIDA'
GROUP BY STATE, (CASE WHEN DEATHS_DIRECT > 0 OR DEATHS_INDIRECT > 0
THEN 1 ELSE 0 END)
ORDER BY COUNT(*) DESC
雪花:ARRAY_AGG()和 LISTAGG()
如果请求者想要一个重复数据删除的 python 列表,那么 ARRAY_AGG(DISTINCT)就是您所需要的。如果他们想要一个简单易读的列表,LISTAGG()就很好。
雪花数组 _Agg —作者截图
雪花数组 _Agg —作者截图
SELECT
STATE AS STATE
, CASE WHEN DEATHS_DIRECT > 0 OR DEATHS_INDIRECT > 0
THEN 1 ELSE 0 END AS DEADLY_EVENT
, SUM(CASE WHEN DEATHS_DIRECT > 0 OR DEATHS_INDIRECT > 0
THEN 1 ELSE 0 END) AS TOTAL_DEADLY_EVENTS
, COUNT(*) AS TOTAL_EVENT_COUNT
, ARRAY_AGG(EVENT_TYPE) AS EVENT_LIST
, ARRAY_AGG(DISTINCT EVENT_TYPE) AS UNIQUE_EVENT_LIST
, LISTAGG(DISTINCT EVENT_TYPE ,', ') AS UNIQUE_LIST_AGGFROM MEDIUM_DEMO.PUBLIC.WEATHER_EVENT_DETAILS
WHERE STATE = 'FLORIDA'
GROUP BY STATE, (CASE WHEN DEATHS_DIRECT > 0 OR DEATHS_INDIRECT > 0 THEN 1 ELSE 0 END)
ORDER BY COUNT(*) DESC
MS SQL-SERVER TSQL: STRING_AGG()
TSQL 有点混乱。创建完整列表和唯一列表需要分成两个不同的查询。请求时,Sting_Agg 不允许使用 Distinct。
你可以得到你需要的东西,但是要达到它需要更多的工作。
TSQL String_Agg —作者截图
SELECTSTATE AS STATE, CASE WHEN DEATHS_DIRECT > 0 OR DEATHS_INDIRECT > 0
THEN 1 ELSE 0 END AS DEADLY_EVENT, SUM(CASE WHEN DEATHS_DIRECT > 0 OR DEATHS_INDIRECT > 0
THEN 1 ELSE 0 END) AS TOTAL_DEADLY_EVENTS, COUNT(*) AS TOTAL_EVENT_COUNT, STRING_AGG(CAST(EVENT_TYPE AS NVARCHAR(MAX)) , ',') AS EVENT_LISTFROM demo.dbo.[StormEvents_details-ftp_v1.0_d2020_c20200922]WHERE STATE = 'FLORIDA'GROUP BY STATE, (CASE WHEN DEATHS_DIRECT > 0 OR DEATHS_INDIRECT > 0 THEN 1 ELSE 0 END)ORDER BY COUNT(*) DESC
TSQL String_Agg —作者截图
SELECTSTATE AS STATE
, CASE WHEN DEATHS_DIRECT > 0 OR DEATHS_INDIRECT > 0
THEN 1 ELSE 0 END AS DEADLY_EVENT, SUM(CASE WHEN DEATHS_DIRECT > 0 OR DEATHS_INDIRECT > 0
THEN 1 ELSE 0 END) AS TOTAL_DEADLY_EVENTS
, COUNT(*) AS TOTAL_EVENT_COUNT, STRING_AGG(CAST(EVENT_TYPE AS NVARCHAR(MAX)) , ',') AS UNIQUE_EVENT_LIST, CONCAT('[' , (STRING_AGG(CONCAT('"', CAST(EVENT_TYPE AS NVARCHAR(MAX)) , '"'), ',')) , ']') AS UNIQUE_EVENT_LIST_BRACKETEDFROM(SELECT DISTINCT STATE, EVENT_TYPE, DEATHS_DIRECT,DEATHS_INDIRECTFROM demo.dbo.[StormEvents_details-ftp_v1.0_d2020_c20200922]) XWHERE STATE = 'FLORIDA'GROUP BY STATE, (CASE WHEN DEATHS_DIRECT > 0 OR DEATHS_INDIRECT > 0 THEN 1 ELSE 0 END)ORDER BY COUNT(*) DESC
Hive 和 Spark SQL: COLLECT_SET 和 COLLECT_LIST
Collect_List 返回所有值。Collect_Set 返回唯一值。
COLLECT_LIST(EVENT_TYPE)返回[Rip 电流,Rip 电流,闪电,Rip 电流]
COLLECT_SET(EVENT_TYPE)返回[Rip 电流,闪电]
结论
根据您的需要和您的数据库,使用 SQL 有许多方法来回答一个业务问题。当您经常在平台之间切换时,所有的命令开始融合在一起。这就是为什么如果有人告诉我他们编码时很少使用谷歌,我会立刻不信任他们。除非他们连续几年只使用一种语言,否则我会把他们叫出来。
用状态机解决复杂编码面试问题。
图片来自 unsplash.com@ othentikisra
这篇博客是我的“大型科技公司黑客技术面试 15 天备忘单”的一部分。在这篇博客中,我们将使用状态机来解决两个编码面试问题。我希望这些例子能帮助你理解状态机解决问题的方法,以及它在解决包含许多复杂情况的问题时是多么的强大。
背景:
有限状态机(FSM)是一种用于设计算法的抽象。它通过转换将有限数量的状态映射到其他状态。状态机在任何给定时刻只能处于一种状态。
使用 FSM 处理编码面试问题的步骤:
- 识别所有类型的输入数据。
- 识别 FSM 的所有可能状态。
- 识别给定输入状态之间的有效转换。
- 画出有限状态机图。
- 按照 FSM 图实现算法。
面试问题示例:
问题 1: 实现将字符串转换为整数的
*atoi*
。 完整描述*:https://leetcode.com/problems/string-to-integer-atoi/举例:*" -42"*
=>*-42*
=>**4193*``*"words and 987"*
在这个问题中,我们需要处理三种类型的输入数据:空白字符(空格)、符号字符(+或-)和数字字符(0->9)。我们从初始状态 1 开始。对于空白字符,状态保持在 1,对于符号字符,我们移动到状态 2,对于数字字符,我们移动到状态 3。继续确定状态 2 和 3 中的有效转换,我们有下图。
有了这个图表,我们可以得出下面的算法:
问题 2 :验证给定的字符串是否可以解释为十进制数。 完整描述*:【https://leetcode.com/problems/valid-number/】举例:
*"0"*
=>*true*``*" 0.1 "*
=>*true*``*"abc"*
=>*false*``*false*``*" 6e-1"*
=>*true*
*" 99e2.5 "*
=>*false*``*"53.5e93"*
=>*true*``*" --6 "*
=>**false*
相关的输入类型有:空白字符(空格)、符号字符(+或-)、数字字符(0->9)、*e*
和*.*
我们从初始状态 1 开始,扩展到不同的状态,以构建下面的 FSM 图。
如果输入以状态 3、5、8 或 9 结束,则为有效数字。其余的是中间状态,它们会给出错误的输出。
下面是我对 FSM 图的实现。
结论
使用状态机有助于避免代码中出现太多嵌套的 if-else 语句,并使代码更加简单。
使用统计距离进行机器学习的可观察性
图片作者。统计检查:输入、输出和实际值
介绍
统计距离用于量化两个分布之间的距离,在 ML 可观测性中非常有用。这篇博文将深入研究统计距离度量,以及如何使用它们来检测常见的机器学习模型故障模式。
为什么要进行统计距离检查?
机器学习中的数据问题可能有很多种,从突然的数据管道故障到特征输入的长期漂移。统计距离度量为团队提供了影响模型的数据变化的指示和故障排除的洞察力。在模型部署后的真实世界中,这些数据分布的变化可能以各种不同的方式出现,并导致模型性能问题。
这里是我们在实践中看到的一些真实世界的数据问题。
- 不正确的数据索引错误—破坏数据的上游映射
- 错误的文本处理—导致新令牌模型从未见过
- 错误处理案例
- 新文本字符串的问题
- 具有不同坐标或索引的不同要素源
- 第三方数据源做出更改,删除要素、更改格式或移动数据
- 新部署的代码改变了特征向量中的项目
- 定期的每日数据收集失败,导致丢失值或缺少文件
- 软件工程改变了一个领域的意义
- 第三方库功能变更
- 假设有效格式发生变化,但突然无效
- 日期字符串改变格式
- 系统会自然发展,功能也会改变
- 外部世界急剧变化(如新冠肺炎疫情),每个功能都在变化
- 数量的急剧增加扭曲了统计数据
这些是使用统计距离检查可以发现的数据问题的示例。
在哪里使用统计距离检查
统计距离可用于分析
- 模型输入:模型输入的变化,特别是关键的最重要的特征或可能是上游模型输出的特征。
- 模型输出:模型输出的变化
- **实际值:**实际值的变化(地面接收到的真实值)。在某些情况下,预测后的短时间内可能无法获得真实情况。在这些情况下,团队经常使用代理指标/数据。
这些检查对于模型性能故障排除非常有洞察力,并且它们允许团队在主要的模型问题影响业务结果之前就发现这些问题。在下图中,可以对模型输入(要素)和模型输出(预测)进行统计检查。
图片作者。统计检查:输入、输出和实际值
如何使用统计距离检查
统计距离度量是在两个分布之间定义的——分布 A 和分布 b。其中一个分布通常被称为 参考分布 (我们将它称为分布 A)——这就是你要比较的。另一个分布通常是您正在与参考分布进行比较的系统的当前状态(我们称之为分布 B)。
什么是参考分布?
在 ML 可观测性的背景下,参考分布可以是许多不同的选项。首先要区分的是,参考分布可以是跨固定时间窗口的分布(分布不变)或移动时间窗口的分布(分布可能变化)。
在下图中,您可以看到固定参考分布的示例包括培训分布的快照、初始模型部署分布(或认为分布稳定的时间)以及验证/测试集分布。使用固定参考分布的统计距离的一个示例是将从训练环境(分布 A)得到的模型预测分布与从生产环境(分布 B)得到的模型预测分布进行比较。
参考分布也可以是移动窗口。在下图中,有两个参考分布是移动窗口的例子——上周和 A/B 测试用例。对于第一个例子,人们可能想要比较上周的预测和本周的预测的分布。在这种情况下,每周都会有一个新的参考分布。如果您正在生产中 A/B 测试两个不同的模型版本,您可以比较来自您的冠军模型的预测分布是否类似于来自挑战者模型的预测分布。
您设置为参考分布的选项将取决于您试图捕捉的内容,我们将深入探讨常见的统计距离检查以设置模型。
图片作者。参考分布
统计距离检查的常见用例
模型输入
模型输入可能突然漂移、逐渐漂移或周期性漂移,具体取决于参考分布的设置。俗话说,“垃圾进来,垃圾出去。”换句话说,模型的好坏取决于流入模型的数据。如果输入数据发生变化,并且与模型之前观察到的或训练的数据有很大不同,则可能表明模型性能可能有问题。
以下是对模型输入设置的一些统计距离检查:
- 培训中的特征分布vs生产中的特征分布
在生产过程中,特征的分布会随时间漂移。了解产品中的特征分布是否随着时间的推移而改变,以及这是否会影响模型,这一点非常重要。在该设置中,参考分布(分布 A)是训练中的特征分布。当前窗口(分布 B)可以设置为某个时间窗口(例如:一天、一周、一个月等)上的特征分布。如果特征分布变化很大,设置更长的回看窗口会很有帮助,这样统计距离检查的噪声会更小。
图片作者。功能(培训)与功能(生产)
2.生产时间窗 A 的特征分布 vs生产 时间窗 B 的特征分布
在生产中的两个不同时间间隔对要素设置分布检查也很有用。与培训和生产检查相比,这种分布检查可以关注更短期的分布变化。如果将训练分布设置为参考分布,如果有任何波动(例如:交通模式、季节变化等),设置较短的生产时间窗口可能会产生噪音。针对上周和本周设置统计距离检查可以指示特征值中的任何突然异常值或异常值。这些对于识别可能被更大的时间窗口掩盖的任何数据质量问题也非常有用。
识别特征中是否有分布变化可以给出模型性能退化的早期指示,或者如果该特征不影响模型性能,是否可以丢弃该特征。如果对模型性能有重大影响,则可能导致模型重新训练。虽然应该调查特性分布的变化,但这并不总是意味着会有相关的性能问题。如果该特征对模型不太重要,并且对模型预测没有太大影响,那么特征分布变化可能更多地表明它可以被丢弃。
现实世界中的团队使用模型输入检查来确定模型何时变得陈旧,何时重新训练模型,以及可能指示性能问题的特性片段。我与之交谈过的一个团队使用财务模型进行核保,通过将产品中的功能与培训中的功能进行比较来生成对功能稳定性的分析,以确保模型决策仍然有效。
模型输出
就像模型输入会随时间漂移一样,模型输出分布也会随时间变化。对模型预测设置统计距离检查可确保模型的输出与其参考分布不会有太大差异。
3.培训中的预测分布vs生产中的预测分布
输出漂移的目标是检测模型相对于训练的工作方式的巨大变化。虽然这些对于确保模型在先前测试和批准的范围内运行是极其重要的,但这并不保证存在性能问题。类似于特征分布变化不一定意味着存在性能问题,预测分布变化也不保证存在性能问题。一个常见的例子是,如果一个模型被部署到一个新的市场,在一些模型输入和模型输出中会有分布变化。
图片作者。距离检查的参考分布示例
4.生产时间窗 A 的预测分布对比生产 时间窗 B 的预测分布
与模型输入类似,预测分布也可以被监控到生产中的另一个时间窗口。我们与之交谈的一个团队正在评估一个垃圾邮件过滤器模型,他们使用模型输出的分布与一个固定的时间框架来显示可能通过模型的攻击模式的变化。这里的参考分布可以是移动时间窗口或固定时间帧。我们听到的一个常见的固定时间框架是使用初始模型启动窗口。
5.型号版本 A 的预测分布与型号版本 B 在同一时间窗口的预测分布
支持 canary 模型部署的团队可以对不同模型版本的预测分布设置统计距离检查。A/B 在生产中测试两个不同的模型,每个模型接收一定量的流量,或者根据历史数据回测一个模型,通过比较预测分布,可以深入了解一个模型相对于另一个模型的表现。
模型实际值
6.培训的实际分配与生产的实际分配
在做出模型推断后,实际数据可能不总是在短期范围内。但是,对实际分布的统计距离检查有助于确定从训练数据中学习的结构是否不再有效。这方面的一个主要例子是新冠肺炎·疫情,它导致交通、购物、需求等模式与疫情开始前的生产模式有很大不同。除了大规模的转移之外,了解特定群组的训练数据与生产之间的实际分布可以识别是否存在
模型预测与实际
7.产量的预测分布与产量的实际分布
这种统计距离检查是比较预测和实际的生产分布。这有助于通过精确定位与实际值差异最大的特定预测群组来发现性能问题。这些检查有时可以发现被平均数据掩盖的问题,如梅伊或 MAPE。
常见的统计距离度量
我们刚刚讨论了统计距离的常见用例。有许多不同的统计距离度量来量化分布之间的变化。不同类型的距离检查对于捕捉不同类型的问题很有价值。在这篇博文中,我们将介绍以下 4 种距离测量方法,以及每种方法最具洞察力的时间。
- 人口稳定指数
- Kullback Leibler 散度( KL 散度
- 詹森香农发散( JS 发散)
- 推土机距离(EMD)
- 科尔莫戈罗夫斯米尔诺夫试验 (KS 试验)
磅/平方英寸(pounds per square inch)
PSI 指标 在金融行业有很多现实应用。对于分布相当稳定的数值和分类特征来说,这是一个很好的度量。
等式:PSI =(Pa-Pb)ln(Pa/Pb)PSI 是一种理想的分布校验,用于检测分布中的变化,这些变化可能会使作为模型输入的要素的有效性降低。它经常在金融中用于监控模型的输入变量。它有一些众所周知的阈值和有用的属性。
图片作者。根据分布计算 PSI 的原始值
通过以上 PSI 值,我们可以看到:
- 分布的任何变化都将增加 PSI 总数,无论变化是正还是负。这意味着不管分布是增长还是收缩,任何变化都会增加 PSI。
- ln(Pa/Pb)项意味着,代表小百分比分布的仓中的大变化将比具有大百分比分布的仓中的大变化具有更大的影响(对 PSI)
- 与从 12%增加到 18%相比,分配箱从 4%增加到 9%对 PSI 的影响几乎是两倍
- 上面的示例分布包括许多小百分比变化(小于 6%),其中没有一个单独产生超过 0.1 的 PSI 项,这是模型调查的经验法则基准。关键是,相对于行业基准,微小的变化不会改变指针。
- 阈值的设置我们建议采用通用的金融行业基准,或者基于该特征/预测/实际的 PSI 的先前样本的天数/小时数
- 0.1-0.25 的金融行业基准通常可以捕捉仓位之间 10%左右的波动
图片作者。PSI 计算开关分布:对称
PSI 是对称的,也就是说,如果颠倒分布,PSI 值是相同的。在上面的示例中,我们将紫色图形与之前示例中的黄色图形进行了交换,值 0.98 与之前的反向分布相同。
下面的示例对一个重要要素使用了种群稳定性指数(PSI)。该检查定期运行,权衡您希望收到更改警报的速度与您试图检测的更改类型。
当检查低于一个明确定义的阈值时,就需要对变化进行调查,这可能表明存在模型性能问题。
图片作者。PSI —按日开启功能
下面显示了一个实时特征,其中稳定性指数远低于设置的 0.15 限制(0.1–0.25 金融行业标准范围)。在设置时,我们建议查看多天的统计窗口来设置检测范围。
图片作者。PSI(较大变化)-按日启用功能
上述每日 PSI 分布变化是引入新的分类特征时测量特征的真实变化。
KL 散度
如果一个分布相对于另一个或小样本量具有高方差,KL 散度统计是有用的。
等式:KLdiv = Ea[ln(Pa/Pb)]=(Pa)ln(Pa/Pb)
KL 散度是一个众所周知的度量,可以认为是样本分布和参考(先前)分布之间的相对熵。像 PSI 一样,KL 散度也有助于捕捉分布之间的变化。也和 PSI 类似,它有信息论的基础。
与 PSI 的一个重要区别是 KL 散度是不对称的。反向分布将具有不同的值-您将从 A -> B 然后 B -> A 获得不同的值。非对称指标对于分布监控来说并不理想,因为当您切换参考分布与比较分布时,您会获得不同的值。对于监控用户来说,这可能显得不直观。
图片作者。KL-散度示例计算
虽然 KL-Divergence 中的不对称使得它不适合于监视度量,但是它确实为 KL-Divergence 提供了一些其他有用的应用。例如,KL-散度不对称与贝叶斯定理中比较先验值和后验值时发现的不对称并没有什么不同。我们发现,当参考分布中有大量样本,而比较分布中有非常少的一组样本(导致更多的方差)时,这种不对称性特别有用。
图片作者。KL-散度:开关分布不对称
JS 分歧
方程:JS Div(P,Q) = KL-DIV(P,M) + KL-DIV(Q,M)
参考= M(混合分布)= (P + Q)
JS 散度有一些有用的属性。首先,它总是有限的,所以不存在被零除的问题。当一个分布在某些区域有值而另一个没有值时,就会出现被零除的问题。其次,与 KL-Divergence 不同,它是对称的。
JS 散度使用两种分布的混合作为参考。这种移动窗口检查的方法存在挑战;混合参考基于移动窗口分布的变化而变化。由于移动窗口在每个周期都在变化,所以混合参考也在变化,并且如果没有经过深思熟虑的处理,每个周期中的度量的绝对值不能直接与之前的周期进行比较。有一些变通方法,但对于移动窗口来说并不理想。
图片作者。JS 分歧问题(不稳定参考)
对于每个分配检查,移动窗口在每个期间都会发生变化。它代表当前期间分布的一个示例。JS 分布对于移动窗口有一个独特的问题,因为混合将随着您比较的每个时间窗口而变化。这会导致 JS Divergence 返回的值的含义定期发生变化,从而在不同的基础上比较不同的时间范围,这不是您想要的。
PSI 和 JS 都是对称的,并且具有用于度量监控的潜力。相对于 JS,我们建议对 PSI 进行一些调整,作为移动窗口的距离度量,用于警报。
推土机的距离
方程式:𝐸𝑀D0=0
𝐸𝑀𝐷𝑖+1=(𝐴𝑖+𝐸𝑀𝐷𝑖) − 𝐵𝑖
𝑇𝑜𝑡𝑎𝑙𝐷𝑖𝑠𝑡𝑎𝑛𝑐𝑒=∑|𝐸𝑀𝐷𝑖|
推土机的距离测量给定区域内两个概率分布之间的距离。推土机距离对于非重叠数值分布移动和高维空间(例如图像)的统计非常有用
作为分布检查的一部分,使用以上 PSI 和 KL 计算,将 Bin0 与 Bin0、Bin1 与 Bin1 等进行比较。Bin0 永远不会与 Bin1 进行比较,计算会修复 Bin 比较。
以下统计距离检查在计算中不包含锁定的条柱。箱号是不相关的;更重要的是分布之间的距离。
推土机的距离是一个相当古老的计算方法——它是在 1781 年制定的。在一维分布的情况下,它捕获在将一个分布移动到另一个分布时,分布的形状和到平均值的距离保留了多少。
图片作者。一维经验模态分解的可视化
推土机的距离可以简单地用一个一维的例子来说明,如上图所示(最初 EMD 算法被设计用来解决移动泥土的问题)。这里,推土机的距离可以被认为是将一堆泥土移动到另一堆泥土中所需的功。卡车沿着直路(X 轴)将泥土装入卡车,从而填满泥土。移动泥土所需的功由沿 X 轴的每个单位计算,以及卡车中有多少泥土,以及卡车可以运输多少单位的泥土。卡车将泥土倒入另一个分配区。分布的距离越远,推土机的距离就越大,因为卡车会将泥土从一个位置运输到另一个位置。分布越分散和重叠,数量就越小。
与 KL 散度相比,EMD 处理 KL/PSI 需要修改的自然非重叠分布。
使用统计距离和模型性能指标
统计距离检查对于模型的可观察性非常有用。有许多设置配置可以帮助识别不同的模型问题(漂移、数据分布变化、数据质量问题、模型性能退化等)。这些统计距离度量最好与模型性能度量一起分析。
使用分布变化的性能度量使团队能够识别可能降低模型整体性能的预测片段。在下面的示例中,整体模型精度为 71%。使用统计距离度量,我们可以看到在 2 个箱中有显著的移动。这些箱子的性能如何?这个运动在模型中引起任何全局性能问题了吗?在本例中,我们可以看到这些频段的性能低于整体精度。
图片作者。功能的性能分析
将统计距离检查与更广泛的模型故障排除工作流联系起来有助于识别不同的模型故障模式。我们的建议是:
- 对要素、预测和实际值设置统计距离检查
- 当统计距离度量超过合适的阈值时,确定这是否会影响模型性能
- 当查看模型性能时,与来自训练/验证集的性能进行比较
- 查看与更改相关的特定存储片的性能
图片作者。分布变化并不总是意味着性能问题
分布的变化可能会也可能不会引起大的下游问题。关键是,不应该在真空中看待任何变化,或者仅仅因为某些事情发生了变化就进行调查。应该根据其他系统性能指标过滤这些更改,以调查哪些是重要的。
结论
以下是我们将总结的最终想法/建议:
- 统计距离检查在跟踪模型的输入、输出和实际情况的变化方面非常有用,允许团队在业务影响之前发现问题。
- PSI 应用于监控顶级特性的特性漂移
- PSI 应用于监控概念漂移的预测输出和实际值
- 当一个分布的样本数小得多并且方差大时,应该使用 KL 散度
- JS 散度可用于特征漂移,但由于混合(参考)是变化的,你应该看绝对数字而不是每天
- 零值比较,当一个 bin 有值而另一个没有值时,需要以一种非常周到的特定方式来处理。需要对脱离分布的事件进行事先处理,以便脱离分布的小变动不会破坏 PSI calc。拉普拉斯平滑的标准方法,即给每个面元增加一个小值,效果并不好。请了解更多详情。
- 当发生重大分布变化时,我们建议查看绩效指标,并调查再培训是否是一种解决方案。
联系我们
如果这个博客引起了你的注意,并且你渴望了解更多关于机器学习可观察性和模型监控,请查看我们的其他博客和 ML 监控上的资源!如果您有兴趣加入一个有趣的 rockstar 工程团队,帮助模型成功生产,请随时联系我们,并在此处找到我们的空缺职位!
在 Pandas 中使用字符串方法
如何在 Python 中对 pandas 数据帧中的列应用字符串方法
通常在熊猫数据帧中,我们有包含字符串值的列。幸运的是,pandas 提供了一种将字符串方法应用于整个列的简单方法,这些列只是 pandas 系列对象。我们可以将这些 series 对象(或列)的值作为字符串访问,并通过使用 series 的 str 属性对它们应用字符串方法。让我们在这里看一些例子。
创建数据帧
我们将使用在这里发现的不明飞行物报告来创建我们的数据框架。然后,我们将使用 dataframe head 方法查看 dataframe 的前 5 行,并使用 dataframe info 方法检查我们的列值。
info 方法的输出告诉我们,列中的所有非空值很可能是字符串对象。在本教程中,我们将主要关注城市列。
将字符串方法应用于列
记住熊猫数据帧是由列组成的。这些列中的每一列都是熊猫系列对象。在 pandas 中,我们可以访问这些数据帧和系列对象的方法和属性。Pandas 为我们提供了一系列用于处理字符串的方法。这些字符串方法将应用于列中的每个条目。
为了将字符串方法应用于列,我们将使用 Series 对象的 str 属性。因此,一般来说,我们将使用以下格式:
Series.str.
假设我们想将 ufo 数据帧中的所有城市名称转换成大写。首先,我们需要访问该系列(或列),然后添加。str,最后加上我们要用的具体方法。要找到我们可用的所有字符串方法,请转到这里并找到字符串处理部分。
我们可以使用括号或点符号来访问系列或列,如下所示:
# bracket notation
ufo[‘City’] # dot notation
ufo.City
注意城市列的类型是一个序列对象。因此,我们可以对该对象应用任何级数方法。
因此,要将我们列中的所有城市名称转换为大写,我们可以添加。str 后跟 upper 方法,如下所示:
ufo.City.str.upper()
注意:这实际上并没有改变我们的原始数据帧。如果我们想改变我们的原始数据帧,我们可以用下面的代码将这个输出值赋给我们的原始城市列:
ufo.City = ufo.City.str.upper()
现在,我们原始 ufo 数据帧的城市列包含大写的城市名称。
如何在 Python 中使用 loc 和 iloc 方法
towardsdatascience.com](/how-to-index-data-in-pandas-with-python-4437c24ff332)
替换列中的字符
如果我们想用下划线替换 City 列中的所有空格,该怎么办?我们可以通过使用 replace 方法来实现这一点。我们只需指定要替换的内容,然后用什么来替换它。
ufo.City.str.replace(‘ ’, '_')
注意所有的空格都被替换成了下划线。
链接方法
我们也可以将字符串方法链接在一起。记住,这些系列方法返回一个熊猫系列对象。所以我们可以给它们添加另一个字符串方法。例如,假设我们想用下划线替换所有空格,并将所有城市名改为小写。我们可以通过下面的代码来实现这一点:
ufo.City.str.replace(‘ ‘, ‘_’).str.lower()
自从不明飞行物。City.str.replace(‘,’ _ ')返回一个 Series 对象,我们可以只使用或链接另一个 Series 方法到它。
本教程使用的所有代码:
如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名媒体成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你用我的 链接 注册,我会赚一小笔佣金。
阅读卢艾·马塔尔卡的每一个故事(以及媒体上成千上万的其他作家)。您的会员费直接支持…
lmatalka90.medium.com](https://lmatalka90.medium.com/membership)
结论
在本教程中,我们学习了如何访问 pandas 数据帧中的列并对其应用字符串方法。
使用 Stringr 和 Regex 从 R 中的文本和字母数字数据中提取特征
使用 Titanic 数据集的特征提取教程
大型数据集通常充斥着难以破译的数据。人们可能会想到文本数据、名称、唯一标识符和其他种类的代码。通常,分析这些数据集的人会很快丢弃这些变量。然而,有时这种类型的数据中可能有有价值的信息,这可能有助于您的分析。
很高兴地,R 提供了惊人的包“stringr”,它非常适合这些目的。本快速教程将向您展示如何从这些变量中提取微小但仍有洞察力的数据。在这种情况下,我们将从 Titanic 数据集提取这种数据。
美国国家海洋和大气管理局在 Unsplash 拍摄的照片
加载数据集
如果你想跟随教程,使用下面的命令加载 Titanic 数据集。泰坦尼克号数据集以泰坦尼克号乘客的各种信息为特色,以练习预测他们的生存。本教程旨在研究一些大多数人在开始分析时会立即丢弃的变量(如姓名和票证)。然而,我们希望与他们合作,看看我们是否能提取一些有用的信息。如果您想知道如何使用 Stringr 和 regex,请跳过这一部分。
### install package
install.packages("titanic")### load package
library(titanic)### load dataset
df = titanic_train #load dataset
安装和加载字符串
在开始之前,我们需要安装和加载 Stringr 包。
### install package
install.packages("stringr")### load package
library(stringr)
现在,我们都准备好了,可以开始了。
从名称中提取标题
作为第一个练习,我想从名字中提取标题。这是因为我相信,比如说,贵族比凡人更有可能生存下来。还有,我假设年轻的未婚女性(“小姐”)更有可能活下来。可能,标题中可能有很多信息。
titanic 数据集中的名称(df$Name)都是这样保存的:
Futrelle,夫人雅克希思(莉莉可能皮)
为了避免任何错误,我们首先要运行一个命令,将所有这些字符串转换成小写,并保存为新变量:
df$lcName = str_to_lower( df$Name )
标题(上例中的 Mrs)前面有一个空格,前面有一个点。我们可以使用 regex(正则表达式)作为一种语言,将这种模式传递给 Stringr,并要求它以如下方式查找标题:
(?<=\s)[[:alpha:]]+(?=\.)
This pattern consists of three parts:
- (?< =\s) 告诉 Stringr 我们要寻找的文本段在(?由空格(\s)分隔。
- [[:alpha:]]+ 告诉 Stringr 我们要寻找的文本由一个或多个(+)字母([[:alpha:]])组成。
- **(?=\.)**告诉 Stringr 我们要找的那段文本已经开始(?=)加一个点(\。).
可以看到 regex 使用了各种符号来交流模式。当你想开发自己的模式时, Stringr 备忘单是一个有用的指南。这个网站提供了一个测试正则表达式模式的简单方法。
我们提取标题,并通过让 Stringr 在小写的“Name”字符串中查找这个模式,将其保存为一个新变量。
df$title = str_extract( df$lcName, "(?<=\\s)[[:alpha:]]+(?=\\.)" )
现在让我们使用 Ggplot2 和下面的命令来绘制我们的新变量“title”。
### install package (ggplot2 is a must have)
install.packages("ggplot2")### load package
library(ggplot2)### plot titles
ggplot(data.frame(table(df$title)),aes(x=reorder(Var1, -Freq),y=Freq))+
geom_col()+
labs(title="Count of Title", x="Title",y="Count")
您可以看到,Stringr 很好地从名称中提取了所有的标题,现在我们有了一个新的分类变量,可以用作我们分析的输入。
从名称中提取族
作为第二个练习,我想从 names 字符串中提取姓氏。我相信家庭成员可能是生存的一个预测因素,也就是说,如果一个家庭成员存活,在控制了性别等其他因素后,其他成员也更有可能存活。
在上面的示例“name”字符串中,您可以看到这个人的姓,例如 Futrelle,是第一个单词。所以我们可以告诉 Stringr 获取第一个匹配模式([[:alpha:]]+)的单词(str_extract)并获取第一个单词,这就是 family。
但是,数据集中有姓氏,如“范德·普兰克”或“奥德怀尔”如果我们在这种情况下取第一个单词,我们只会得到“vander”或“o”,而不是整个家族的名字。因此,我们需要修改模式以包含任何类型的字符(。)长度为 1 个字符以上,后跟一个逗号(?=\,).
df$family=str_extract(df$lcName,".+(?=,)")
这一次,因为我们有很多姓,所以让我们画出 10 个最常用的姓。
t=data.frame(table(df$family))t=t[order(-t$Freq),]ggplot(head(t,10),aes(x=reorder(Var1,-Freq),y=Freq))+
geom_col()+
labs(title="Count of Family Name", x="Title",y="Family Name")
同样,我们现在有了新的变量“family name”,我们可以使用它进行进一步的分析。
从票中提取信息
在大多数对泰坦尼克号数据集的分析中,人们很快就丢弃了船票信息。然而,机票代码中的一些字母可能意味着什么。例如,它们可以表示票是最后一分钟购买的,并且乘客没有为航行做好充分准备。
但是,这些缩写和代码的写法不一致(例如,“SOC”和“S.o.C .”),这就是为什么这些数据需要一些准备工作。让我们打扫它。
### set ticket codes to lowercase
df$lcTicket=str_to_lower(df$Ticket)### remove punctuation from ticket codes
df$lcTicket=str_replace_all(df$lcTicket,"[:punct:]","")
现在我们已经清理了一些数据,让我们提取这些缩写。通常,机票代码看起来像这样:
索托诺 2 3101287
在我看来,“sotono 2”是一个首字母缩写词,可能意味着什么,“3101287”是一些机票号码(我稍后会详细介绍)。如果我们考虑缩写词的模式,它是字符(。)后面跟一个空格(\s),空格后面跟一个三位数或更长的数字([[:digit]])({ 3,})。有时,整个机票代码是一些首字母缩写词(例如,一个乘客将“line”作为机票),这意味着我们可以给 Stringer 另一个选项(“|”是“or”运算符),即整个代码是感兴趣的首字母缩写词([[:alpha:]]+)。我们使用下面的命令告诉 Stringr 查找这个模式,并将其保存在一个新变量“ticketinfo”中。然后,我们删除空白并替换 NA 值。
### identify acronym
df$Ticketinfo = str_extract( df$lcTicket, ".+(?=\\s(?=[[:digit:]]{3,}))|[[:alpha:]]+" )### remove whitespaces from ticketinfo
df$Ticketinfo = str_replace_all( df$Ticketinfo, "\\s", "" )### remove NA values
df$Ticketinfo = ifelse( is.na(df$Ticketinfo), "none", df$Ticketinfo )
让我们再次绘制 10 个最常见的 ticketinfo 值。
t=data.frame(table(df$Ticketinfo[df$Ticketinfo!="none"]))t=t[order(-t$Freq),]ggplot(head(t,10),aes(x=reorder(Var1,-Freq),y=Freq))+
geom_col()+
labs(title="Count of Ticketinfo Acronym", x="Ticketinfo Acronym",y="Count")
你可以看到这对许多乘客来说增加了相当多的信息。将近 60 名乘客的值为“PC”在检查数据后,我相信它可能意味着“私人客舱”,因为许多(但不是所有)头等舱乘客的机票上都印有这个值。
从机票号码中提取免费升级
关于泰坦尼克号命运多舛的处女航,有趣的是船只有一半的容量。这艘船本可以轻而易举地运送更多的乘客横渡大西洋。这让我想到一些乘客可能已经“免费”升级到了更高的舱位。对机票号码的初步检查显示,第一个数字似乎与乘客等级(Pclass)相关,但也有例外。我假设第一个数字可以携带一些关于阶级变化的信息,这将对乘客的生存概率产生深远的影响。让我们提取首字母缩写词后的数字的第一个数字,并将其保存为假定的乘客类别(PresumedPclass)。
我们先提取一下票号。同样,票号由数字([[:digit:]])组成,长度为 3 个或更多字符({3,})。让我们也将它存储为一个字符(as.character),而不是一个数字,以便仍然对它使用 Stringr。
df$Ticketno = as.character( str_extract( df$lcTicket, "[[:digit:]]{3,}" ))
那么我们就取这个票号的第一个数字([[:digit:]])存储为推定的旅客类别。
df$PresumedPclass = str_extract( as.character(df$Ticketno) ,"[[:digit:]]{1}")
现在,让我们再次绘制这个新变量,但是让我们在所提供的乘客类的上下文中绘制它,看看它是如何相关的。
ggplot(df, aes(x=Pclass, fill=PresumedPclass))+
geom_bar()+
labs(title="Count of Passenger Class", x="Passenger Class",y="Count")+
scale_fill_discrete(name="Presumed Passenger Class")
正如我们在下图中看到的,这两个值大部分是相关的。但有时他们不会。在这两个类别不匹配的情况下,这可能是令人兴奋的信息。
进一步阅读
您想了解更多信息吗?在 Medium 上关注我看我的其他故事!
使用生存回归获得关于用户的洞察并优化黑名单保留策略
使用生命线 Python 库和生存分析来估计用户的“寿命”
克里斯·利维拉尼在 Unsplash 上的照片
公共网站和 SAAS 应用程序的安全管理员面临的最大挑战之一是检测非人类或恶意用户的非法活动。幸运的是,当有足够的使用数据可用时,通常不难发现某个用户的“行为”有些奇怪或不寻常。然而,要决定 T4 如何对这样的发现做出反应,通常也是一个不小的挑战。
在大多数情况下,最好的方法是简单地将可疑用户列入黑名单或阻止(通过阻止其 IP 地址、会话、设备、帐户等)。然而,用户有时不能被列入黑名单,直到得到进一步通知,安全管理员必须回答的下一个问题是“我们应该将可疑用户列入黑名单多长时间?”,“我们如何确保选择正确的阻塞持续时间?”,以及“我们如何测试自己?”当攻击者滥用合法用户的设备或 IP 地址时尤其如此。通常,这种关于黑名单的维护和保留策略的问题是使用相当多的直觉和假设来回答的——当使用直觉和假设的人足够有经验时,这是非常好的。
在这篇文章中,我将提出一个更可靠的方法来解决这个问题,它需要更少的直觉和更多的数据。我的方法是基于一种被称为生存分析或“*事件发生时间”*分析的方法论或统计工具。虽然我将集中讨论生存分析如何帮助我们处理一个非常具体的问题,但我相信这种方法,更重要的是,这里介绍的工具在各种环境和类似问题中是有价值的。
生存分析主要被研究人员和科学家用来估计在给定的时间发生某个*【死亡】事件的几率*。传统上,它用于比较或确定给定特定情况下不同人群的存活率,或回答一些问题,如“给予某种治疗,患者多活 3 年的可能性有多大”或“有多少人在被诊断患有 X 后有可能活 10 年”。
然而,生存分析也被用来估计其他类似死亡事件的可能性。生存分析用于估计用户在离开网站前可能会在网站上花费多长时间,引擎在发现某个故障后会继续运行多长时间,或者政府在不同类型的政治制度下“存活”多长时间。
该图表摘自我将在这里使用的生命线 Python 库的文档(https://lifelines.readthedocs.io/)
例如,上图中的分析描述了一条生存曲线,显示与平均执政时间不超过 10 年的民主政权相比,非民主政权更有可能“存活”20 年以上。
生存分析是一个巨大的话题,但我更愿意在下面引用一些好的介绍资源,然后继续展示它如何帮助我们解决这里提到的问题。然而,在我们继续之前,对于生存分析,或者更准确地说,对于它通常基于的表单数据,只有一个概念需要解释。
驱使科学家使用生存分析的一个主要问题是他们的数据被 T4 审查。假设我们试图在某一种树长得太大并倒塌之前估计它的寿命。同样,假设我们有一个数据集,其中包含我们在全国范围内监控的 1000 棵树。现在,假设我们的数据集中只有 50%的树倒塌了,剩下的 500 棵树仍然挺立着。我们应该如何解释这些树?在我们知道它们的寿命结束之前,我们能报告它们的寿命吗?我们是否应该将这些树从我们的数据集中排除,因为我们对它们的预期寿命一无所知?换句话说,我们的数据是*右删截的——*我们知道它从哪里开始(在图表的右侧),但我们不一定知道它至少在哪里结束。我不打算在这里讨论这个理论(你可以使用下面引用的一个很好的来源),但是生存估计量的一个主要目的就是解释这种数据。否则,生存分析会是一个更简单的任务。
因此,任何生存分析估计器都需要至少 2 个变量或特征: (1) 持续时间(例如,树在倒塌之前站立了多长时间);以及 (2) 事件(是否已经发生“死亡”事件——树是否已经倒塌)。这一点很快就会清楚。
黑名单、保留策略和用户生活
因此,假设我们有一个 web 应用程序,它经常成为僵尸程序或自动化攻击的目标。幸运的是,我们知道如何识别由非人类和恶意用户生成的请求,并且我们可以轻松阻止生成这些请求的设备或地址。然而,我们也知道这些设备或地址也被真实用户使用,因此我们希望尽可能短时间地阻止它们。我们的安全顾问建议将我们黑名单的保留策略设置为 14 天,此后设备将不再被阻止,因为她的经验表明这是一种非常常见的模式。我们能测试这个吗?
我相信我们可以,生存分析是这项工作的一个非常好的工具。假设我们有了所需的数据,我们可以使用生存分析来估计这些设备或地址在停止生成非人类恶意请求之前将被邪恶用户接管多长时间。如果我们的安全专家是正确的,那么 14 天后,当我们检测到设备或地址产生非人类或恶意请求时,我们应该可以安全地重新激活我们已列入黑名单的设备或地址。
我们拥有的数据由大约 25 天前首次观察到的非人类行为的设备或地址的汇总统计数据组成。这里有两个我们特别感兴趣的特性: days_with_bad_req,记录设备或地址生成被我们识别为非人类的请求的天数,以及一个名为 event 的变量,告诉我们设备或地址是否已经停止生成非人类的请求,或者“攻击”是否仍在继续。当一个地址或设备发出任何请求已经超过 10 天时,我们将该地址或设备视为已经“死亡”或停止生成非人类请求。因此,如果有人接管了我的 IP 地址,并在 6 天内生成“坏”请求,然后消失了 14 天,我的地址将有个 days_with_bad_req = 6 和个 event = 1。如果他昨天接管了我的 IP,并仍然使用它来攻击网站,那么它将有个 days_with_bad_req = 2 和个 event = 0。
这里有两个更有趣的特性,我们稍后会用到: req_day 记录该设备或地址每天生成的请求数,这些请求应该会给我们一些关于其活动强度的测量值。 bad_req_days_rate 是告诉我们该设备或地址产生非人类流量的时间百分比(天数)的功能(即,它是否专门用于生成恶意请求)。
初步数据显示,恶意用户实际操作的时间比 14 天短得多,但请记住,这里报告的一些数字可能是仍在进行的攻击。
我们将首先使用 KaplanMeierFitter 来确认这一声明。KM 可能是最流行的单变量生存分析估计器,Lifeline 库版本非常容易使用,并且具有非常有用的绘图功能,可以立即给我们一些见解。请注意它只需要两个变量:变量持续时间——对我们来说是设备或地址创建非人类请求的天数,以及变量事件——在我们的情况下,它等于设备或地址的攻击是否正在进行,或者是否被认为已经完成(即,自其上次活动以来已经超过 10 天)。
KM fitter 绘制的图表显示,超过一半的恶意用户会在 2 天的活动后停止攻击,大约 90%的攻击者会在第 5 天结束时消失。
生命线库实际上有一个非常有用的函数,名为*【百分位()】*,它可以很容易地帮助我们进一步解释生存曲线,并确定所需的边界。 percentile() 函数可以只取一个参数,这是所需的百分位数,因此,例如, percentile(0.1) 将给出 90%的人口将“死亡”的时间点—回想一下,我们使用生存分析来估计“死亡时间”事件。
让我们用一些有用的百分位数来更好地了解我们的生存曲线。
50% of the population will not make bad req after 1.0 days
75% of the population will not make bad req after 2.0 days
99% of the population will not make bad req after 9.0 days
no one will make bad req after 14.0 days
嗯,这肯定证实了我们的安全专家的直觉,因为任何人都不可能到达的时间点是 14 天。然而,如果尽快从黑名单中删除地址或设备对我们来说真的很重要,那么我们的分析表明,我们也可以将保留时间设置为仅仅 10 天,以避免大约 99%的恶意非人类请求。
我们也可以使用 predict() 方法来确认这一点,该方法将我们想要估计存活几率的天数作为参数。
print(f’Probability that device/address takeover continues after 3 days is: {kmf_breq.predict(3)}’)**Probability that device/address takeover continues after 3 days is: 0.14585572331045113**print(f'Probability that device/address takeover continues after 10 days is: {kmf_breq.predict(10)}')**Probability that device/address takeover continues after 10 days is: 0.0033031481126646763**
生存回归越来越复杂
在大多数情况下,使用 Kaplan-Meier 估计量就足以获得所需的洞察力,但有时我们可能需要(或想要)做出更准确的估计。例如,假设我们有充分的理由相信,更具侵略性的非人类行动者也可能更快消失。如前所述,我们有另一个名为 bad_req_days_rate 的功能,它告诉我们设备或地址被用于生成恶意非人类活动的天数百分比。我们想检验这一特征(以及其他特征)的变化如何改变我们的生存功能。
Lifelines 库提供了另一个非常有用的估计器,名为 CoxPHFitter,这也是一个非常流行的估计器,用于 survival *回归。*与 KMF 等单变量估计不同,生存回归估计允许我们在分析中添加更多的协变量,并根据持续时间对它们进行回归。因此,让我们将特性 bad_req_days_rate 与特性 req_day 一起添加到我们的模型中,该特性通过计算每天生成多少请求(任何请求)来记录用户、地址或设备运行的一般速度。
这个总结告诉我们许多需要用教科书来解释的事情,但请注意几个要点:尽管这两个特征的 p 值在统计上是显著的(它小于 0.05),但我们的模型的一致性得分并不令人印象深刻,这意味着我们的模型(我们选择的协变量)不会很好地预测我们想要估计的最终寿命。这通常意味着我们需要重新思考我们的模型甚至数据的结构。然而,为了便于解释,我现在将忽略这一点。
要注意的第二件重要的事情是在 exp(coef) 列下的两个特性的值,这是我们用来解释模型的主要值。变量 req_day 的 exp(coef) = 0.89,这意味着 req_day 增加一个单位将降低死亡的“危险”,从而增加设备或地址随时间存活的机会约 11%。另一方面,我们的第二个参数, bad_req_days_rate 的为正(或> 1.0) exp(coef) — 1.04。这意味着 bad_req_days_rate 每增加一个单位(例如,设备在%51 而非 50%的活动天数内发出错误请求)将增加死亡风险,从而将用户继续发出错误请求的几率降低 4%。
换句话说,正如我们假设的那样,更具攻击性的攻击持续的时间更短。实际上,我们可以使用 CoxPHFitter 通过 plot_covariate_groups()方法来可视化这一点。
cp.plot_covariate_groups('bad_req_days_rate', [75, 100])
这向我们展示了 bad_req_days_rate 为 75%和 100%的用户的生存函数。正如预期的那样,发起更集中攻击的用户“存活”更少或更早死亡。
我们也可以通过使用 *predict_survival_function()来看到这一点。*下面的代码通过创建两个用户来说明这一点,这两个用户的 bad_req_days_rate 只有一个不同,即一个是 80%,另一个是 95%。
**#Create the first user with *bad_req_days_rate = 80***
user_a_df = pd.DataFrame(np.array([0,2,1,80]).reshape(1,-1),columns=cols)**#Create the second user with *bad_req_days_rate = 95*** user_b_df = pd.DataFrame(np.array([0,2,1,95]).reshape(1,-1),columns=cols)**#predict the survival function for each user and plot it**surv = cp.predict_survival_function(user_a_df)
plt.plot(surv)surv = cp.predict_survival_function(user_b_df)
plt.plot(surv)
结论:这是对生存分析的一个简短而深刻的介绍,我希望它能成为一个强大的工具,使我们能够对用户活动数据的重要方面获得有趣的见解。虽然这只是一个(有趣的)用例,但我试图使它足够简单,以便在其他环境中使用。
祝你好运!
我的笔记本在这里可以找到
[1]生命线图书馆
[2] SciKit-Survival 是一个与我使用的不同的生存分析 python 库,但是一个极好的资源。
【2】几个比较好的来源这里,这里这里。还有几个短的这里这里和这里。
在亚马逊 SageMaker PyTorch 培训工作中使用 TensorBoard:一步一步的教程
理解 Amazon SageMaker 培训工作的数据流,并通过示例学习如何在 SageMaker Pytorch 培训工作上设置 TensorBoard
TL;速度三角形定位法(dead reckoning)
在这篇文章中,我们在这个博客中向你展示如何在亚马逊 SageMaker PyTorch 培训工作中使用 TensorBoard。这些步骤是:
- 在 SageMaker 培训作业运行时安装 TensorBoard,此处为
- 将 PyTorch SageMaker 估计器初始化时的tensor board _ output _ config参数配置为这里
- 在 PyTorch 培训脚本中,记录您想要监控和可视化的数据,如这里的所示
- 启动 tensorbard 并将日志目录指向步骤 2 中配置的 s3 位置
写这篇博客的原因
在我最近的一个项目中,我需要使用 TensorBoard 来可视化来自亚马逊 SageMaker PyTorch 培训工作的指标。在网上搜索并查看了 AWS 官方文档、SageMaker SDK 示例和 AWS 博客后,我意识到没有针对该主题的现有分步教程。所以,我写这篇文章,希望给你一个现成的解决方案。这篇文章也可以在我的博客里找到。
SageMaker 培训作业如何在 S3 和培训实例之间交换数据
首先,让我们在执行 PyTorch SageMaker 培训工作时有一个总体的了解。SageMaker 简化了以下流程:
SageMaker 培训工作
- 启动并准备请求的 ML 实例
- 从 S3 下载输入数据
- 从 ECR 中提取训练图像
- 执行训练文件(上图中的 train.py)作为训练的入口点
- 把训练模型神器推回 S3
让我们通过下面的例子来放大 ML 实例和 S3 之间的数据交换。这里我们使用 SageMaker Python SDK 。
输入数据:
- 输入数据位置由估算器配置。拟合函数输入参数。在这个例子中,训练数据是具有 S3 前缀S3://input-data-bucket/train的所有对象,验证数据也是如此。
- 数据下载到 ML 实例中,位于*/opt/ML/input/data/train和/opt/ML/input/data/val文件夹中。“ train ”和“ val ”通道通过 estimator.fit.inputs 参数字典中的键进行配置。*
输出模型神器
- 您的训练脚本应该将您所有的最终模型工件写到目录 /opt/ml/model 中。
- SageMaker 将 /opt/ml/model 下的数据作为单个对象以压缩 tar.gz 格式复制到您在估计器对象 output_path 参数中指定的 S3 位置。在我们的示例中,在训练作业成功之后,模型工件被定位为S3://output-data-bucket/model/model . tar . gz*。*
在 SageMaker PyTorch 培训工作中逐步使用 tensor board
同步张量板日志
SageMaker 调试器是去年年底的一个特性。它提供了一种非常简单的方式从 SageMaker 培训工作中发出 TensorBoard 数据。要使调试钩子发出张量板数据,需要如下指定新选项TensorBoardOutputConfig
:
在训练作业期间,调试挂钩将生成的张量板数据近乎实时地上传到从配置中提供的s3_output_path
的值导出的 S3 路径。
在你的训练脚本中,你应该把你的 TensorBoard 日志写到 ML 实例中由s3_output_path
指示的本地文件夹中。如果跳过设置,默认目录是/opt/ml/output/tensor board/。以下是一些示例代码:
在 SageMaker PyTorch 容器中安装 TensorBoard
默认情况下,SageMaker PyTorch 容器中不包含 TensorBoard 包,SageMaker Python SDK 中的 PyTorch estimator 使用该包。你得先装 TensorBoard。最简单的方法是使用在 SDK 文档中描述的 requirements.txt 文件:
如果您想在您的脚本中使用其他包,您可以在与您的训练脚本相同的目录中包含一个
requirements.txt
文件,以便在运行时安装其他依赖项。requirements.txt
和你的训练脚本应该放在同一个文件夹里。创建 PyTorch 估计器时,必须在source_dir
参数中指定该文件夹。
启动 TensorBoard 服务器
现在,我们可以通过命令*F_CPP_MIN_LOG_LEVEL=3 AWS_REGION=YOUR-AWS-REGION tensorboard --logdir s3://tensorboard-output-location*
启动 TensorBoard 服务器
- TensorBoard 支持直接从 S3 加载日志文件。我们将日志目录指定为在
TensorBoardOutputConfig
中配置的目录。 - 您需要正确设置 AWS 环境变量,例如
AWS_ACCESS_KEY_ID and AWS_ACCESS_KEY_ID.
- F_CPP_MIN_LOG_LEVEL 可以抑制详细日志
- 如果您在 SageMaker 笔记本实例中运行 TensorBoard 服务器,您可以通过预先设计的笔记本实例 url 访问 TensorBoard 页面,例如https://YOUR-NOTEBOK-INSTANCE-name . notebook . YOUR-region . SageMaker . AWS/proxy/6006/。 注意最后一个反斜杠是 madantory,否则看不到 TensorBoard 页面。
参考
- SageMaker 容器提供的环境变量列表:https://github . com/AWS/SageMaker-Containers # list-of-provided-environment-variables-by-SageMaker-Containers
- SageMaker 培训工作如何运作https://docs . AWS . Amazon . com/sage maker/latest/DG/how-it-works-training . html
- SageMaker 调试器https://docs . AWS . Amazon . com/SageMaker/latest/DG/train-debugger . html
- Estimator:通过 SageMaker Python SDKhttps://sagemaker.readthedocs.io/en/stable/estimators.html为 SageMaker 培训提供的高级接口
使用 TensorFlow Go 通过 web 服务服务于对象检测模型
使用 TensorFlow Go 构建 web 服务来服务 MobileNet 对象检测模型。
张量流这个术语超出了 Python 和神经网络的范畴。在这个时髦的词背后,有一个由几个框架、项目甚至硬件组成的完整生态系统。其中一个项目是 TensorFlow Go 。这个 TensorFlow API for Go 擅长在 Go 程序中加载和部署模型,这些模型是用 Python 对应物创建的。在本教程中,我们将看到一个这样的例子。这里的目标是在 Go 中创建一个 web 服务器,为 TensorFlow 中训练的对象检测模型提供服务。
安装 TensorFlow Go
安装 TensorFlow Go 需要使用下载软件包
$ go get github.com/tensorflow/tensorflow/tensorflow/go
此外,您需要安装 TensorFlow C 库。安装起来没那么简单,为了避免侧钻,这里就不讨论了。你可以在 https://www.tensorflow.org/install/lang_c找到说明。
项目的设计
对于第一次迭代,我只向项目中添加一个对象检测模型。然而,我将这些功能实现为包,以防将来我决定添加额外的模型。如果发生这种情况,我将重用部分代码或将其他部分重构为接口。
模型
我们将服务的模型是在从http://download . tensor flow . org/models/object _ detection/SSD _ MobileNet _ v1 _ COCO _ 2018 _ 01 _ 28 . tar . gz获得的 COCO 数据集上训练的 SSD MobileNet 对象检测模型。“但是胡安,MobileNet,SSD,还有 COCO。这是什么意思?”很高兴你问了!我将从 MobileNet 开始简要解释这些术语。
MobileNet ( Howard et al. )是一个轻型机器学习模型家族,以精度换取速度和性能。顾名思义,MobileNet 设计用于移动或嵌入式设备。SSD,代表单次多盒检测器(刘等),是指使用单个神经网络来检测对象的方法。与这种“单镜头”方法相对应的是一种架构,它使用一个称为“提议生成器”的额外组件来查找图像的感兴趣区域。最后,我们有 COCO,它是“上下文中常见对象”的缩写(林等人),用于训练模型的数据集。COCO 数据集是一个超过 200,000 个标记图像的集合,这些图像分布在 90 个类别中,包括“鸟”、“猫”、“人”和“车”。"
服务
我们将构建的服务有一个端点——predict
——它将图像的路径作为参数。它的响应是一个 JSON,包含以下字段:
detections
:被检测对象的数组,包括以下对象:score
、box
和label
。score
:检测的置信度得分。box
:检测对象的边框([yMin, xMin, yMax, xMax]
)。这个数字不是检测的像素位置。相反,返回值是相对于图像的宽度和长度的位置。例如,在大小为[400,200]的图像中,位于其垂直中心的对象的yMin
值为 0.5(图 1)。label
:被检测对象的 COCO 类。numDetections
:检测总数。
图一。检测到的对象的示例。
图 2 给出了一个响应示例。
图二。服务的输出。
构建服务
下载模型
从这里下载模型开始教程http://download . tensor flow . org/models/object _ detection/SSD _ mobilenet _ v1 _ coco _ 2018 _ 01 _ 28 . tar . gz。然后解压缩文件。在该目录中,您填充了几个文件,其中包括关于模型的元数据、训练配置文件(来自 TensorFlow 的对象检测 API)、检查点等。从所有这些中,您只需要 saved_model/目录。所以复制它,并粘贴到您的工作目录。
加载模型
下载完模型后,下一步是将其加载到 TensorFlow Go 中。在工作目录中,创建一个新文件夹,并将其命名为 models。在里面,创建一个文件 coco_ssd_mobilenet.go 。然后添加以下代码:
在最上面,我们有包的名字,imports,和一个 struct Coco,我们将在其中添加模型。结构之后是文件的路径和创建结构的构造函数。很标准的做法。然后就变得有趣了。
该结构的第一个方法Load()
加载模型。它的第一行tf.LoadSavedModel()
,使用模型的路径、一系列标签和一个我们目前不需要的SessionOptions
对象作为参数:
等等,标签?那是什么?
SavedModel 标记和签名
像我们这样的 TensorFlow SavedModel 可以有多个图。因此,为了识别它们,这些图表与标签相关联。因此,当加载模型时,你需要指定你要使用的图。在这个例子中,我们需要标记“serve”,它预测。要查看 SavedModel 的标签,以及我们即将使用的模型的其他信息,您需要 TensorFlow 安装附带的工具 saved_model_cli 。要使用它,请执行以下命令:
$ saved_model_cli show --dir PATH/TO/MODEL/ssd_mobilenet_v1_coco_2018_01_28 --all
下面的图 3 显示了输出。
图 3。模型的“服务”标签。
上面是标签“serve”的签名。SavedModel 签名定义了模型的输入和输出。这个模型的输入是一个名为“image_tensor”的张量,其dtype
为UINT8
,输入形状(-1,-1,-1,3)(任意大小的一批彩色图像)。至于输出,它们与我们在 JSON 响应中返回的值相同(detection_boxes、detection_classes、detection_scores 和 num_detections)。
有关 SavedModel 的更多信息,请访问https://www.tensorflow.org/guide/saved_model
阅读标签
加载模型后,下一步是读取标签文件。Coco 模型有 91 个类,因此,模型的输出是 1 到 91 之间的一个数字。但是这个值对用户来说没有意义。如果你给我看,我会说,“这是什么数字?我需要实际的标签。”因此,我将数据集的标签收集在一个名为 labels.txt 的文件中,目标是将模型的输出映射到一个标签:
以上是功能readLabels()
。这个函数读取标签文件并将它们作为字符串片段返回。要使用该函数,从Load()
添加对它的调用:
预测
现在最酷的部分来了:预测。总而言之,预测包括获取输入图像,将其转换为张量,然后将其提供给模型。正如我们在模型的签名中看到的,在预测之后,它产生四个张量detection_boxes
、detection_classes
、detection_scores
和num_detections
。这些值是用户将收到的信息:
Predict()
有一个参数[]byte
,代表图像。在函数的第一行,我们将使用[]byte
并使用即将到来的函数makeTensorFromBytes()
将其转换为张量。然后,我们使用Run()
方法和刚刚转换的张量作为输入来执行图形。至于输出,使用一个[]tf.Output
切片,包含模型的所有可能输出。接下来是函数responses.NewObjectDetectionResponse()
将输出值封装在一个结构中。在进入细节之前,我们先来看看makeTensorFromBytes():
函数makeTensorFromBytes()
取自这里的,将[]byte
转换为张量。在它的内部有一个对makeBatch()
的调用,这个函数为一个张量增加了一个额外的维度,并将其转换为大小为 1 的一批(还记得输入形状第 4 维输入形状):
最后,我们需要一个方法CloseSession()
来结束会话:
准备响应
在预测并得到结果后,我们需要提取值,并使它们对用户来说是可显示的。为了实现这一点,我创建了一个名为responses
的新包,并在其中添加了一个由检测列表和检测数量组成的ObjectDetectionResponse
结构:
这里最重要的部分是NewObjectDetectionResponse
函数,负责创建响应。这个函数有两个参数:输出张量和标签列表。输出张量与几个类型断言语句一起使用,以获取它们的值。然后,在一个循环中,我们迭代numDetections
次,每次都检查检测的置信度是否低于给定的阈值。如果是真的,我们继续。否则,创建一个由乐谱、边界框和标签组成的detection
实例,并将其添加到列表detection
中。在函数结束时,返回ObjectDetectionResponse
对象。
网络服务
因此,我们到达了最后一部分:网络服务。如上面的例子所示,该服务有一个 POST 方法,它接收一个图像并以 JSON 的形式返回ObjectDetectionResponse
:
两个函数中的第一个,main()
加载模型并设置服务。而第二个函数,处理函数predict()
,接收图像并执行预测。
要运行服务器,请执行$ go run main.go
。要进行测试,执行 run a curl 命令,就像这样curl -F "data=@PATH/TO/IMAGE" [http://localhost:8080/predict](http://localhost:8080/predict)
。
尽情享受吧!
概述
TensorFlow 是一个由多个平台组成的庞大生态系统。其中之一 TensorFlow Go 能够执行由 TensorFlow (Python)和 TensorFlow.js 等同类产品生成的图形。在本教程中,我们构建了一个程序来加载对象检测 MobileNet 模型,并通过 web 服务为其提供服务。
该项目可以用作一个库。如果您想使用它,运行$ go get github.com/juandes/tensorflow-go-models/models
。为了进行测试,您可以使用上面给出的 web 服务器代码。将来,我想在项目中添加更多的模型,并将我们使用的一些方法转换成接口。这样,我们可以轻松地扩展项目以支持更多的模型。如果你想尝试并添加一个新的模型,请随时提交您的 PR。
完整的源代码可以在 https://github.com/juandes/tensorflow-go-models获得
使用 Tensorflow Lite 进行对象检测
了解如何使用在 Python 中的 COCO 数据集上预训练的 MobileNet SSD 模型,在具有完整代码和非最大抑制的边缘设备上运行。
伊利亚·赫特在 Unsplash 拍摄的照片
Tensorflow 最近发布了用于 Tensorflow 2 的对象检测 API,tensor flow 2 有一个非常大的模型动物园。然而,他们只提供了一款采用 Tensorflow lite 的 MobileNet v1 SSD 型号,此处描述的是。在那篇博文中,他们提供了在 Android 和 IOS 设备上运行的代码,但没有为 edge 设备提供代码。随着 edge 设备的普及和 OpenCV 发布空间 AI 套件,我想填补这一空白。这正是我们将在这里看到的。
要求
这不需要安装 Tensorflow 对象检测 API 即可运行。一个简单的 Tensorflow 安装,以及用于图像处理的 OpenCV 足以运行它。
pip install tensorflow
pip install opencv
我使用了对象检测 API 中提供的一些代码来使工作变得更容易,但没有必要担心它,因为不需要显式安装它,你可以在这里找到完整的代码。
预训练模型可以从 Tensorflow 的博客这里下载,或者也提供代码。
标签
我们的第一步是将标签制作成随后需要的格式,这是一个嵌套的字典,里面有 id 和标签。我们将使用随模型提供的标签地图文本文件,并将其转换为所需的格式。
注 :-标签用“???"需要被忽略,并且除了第一个索引之外,跳过那些索引。所以每个标签都向后移动了一个位置。例如,标签“person”位于第一行,但它将被分配一个标签 0,依此类推。
这可以在它的 Java 实现中找到注释。
// SSD Mobilenet V1 模型假设类 0 是后台类
//在标签文件和类标签中,从 1 开始到 number_of_classes+1,
//而 outputClasses 对应于从 0 到 number_of_classes 的类索引
这可以通过下面的代码来完成:
def create_category_index(label_path='path_to/labelmap.txt'):
f = open(label_path)
category_index = {}
for i, val in enumerate(f):
if i != 0:
val = val[:-1]
if val != '???':
category_index.update({(i-1): {'id': (i-1), 'name': val}})
f.close()
return category_index
这里,忽略第一行,我使用 if 语句并存储i-1
。这将创建一个字典,如下所示。
字典已创建
它将有 80 行,89 个键。
TfLite 解释程序
标签做好了,让我们来了解一下 TfLite 的解释器,以及如何得到结果。
初始化解释程序
import tensorflow as tfinterpreter = tf.lite.Interpreter(model_path="path/detect.tflite")
interpreter.allocate_tensors()
只需加载你的 tflite 模型的正确模型路径,分配张量即可。
输入和输出详细信息
要获得输入和输出的详细信息,请编写:
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
现在,让我们研究它们,看看给出什么类型的输入和我们将得到的输出。
输入细节是一个只有 1 个元素的列表,它是一个字典,如下所示。
输入详细信息
这里,我们可以看到输入形状是[1, 300, 300, 3]
。除此之外,它要求输入图像的数据类型为np.uint8
。
输出细节是一个包含 4 个元素的列表,每个元素包含一个类似输入细节的字典。每个调用返回 10 个结果,第一项存储矩形边界框,第二项存储检测类,第三项存储检测分数,最后一项存储返回的检测数。返回的边界框是标准化的,因此它们可以缩放到任何输入尺寸。
为了获得输出,我们需要读取图像,如果使用 OpenCV,将其转换为 RGB,适当地调整其大小,并在用输入帧设置张量后调用解释器。然后可以使用解释器的get_tensor
功能获得所需的值。
import cv2img = cv2.imread('image.jpg')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_rgb = cv2.resize(img_rgb, (300, 300), cv2.INTER_AREA)
img_rgb = img_rgb.reshape([1, 300, 300, 3])interpreter.set_tensor(input_details[0]['index'], img_rgb)
interpreter.invoke()de_boxes = interpreter.get_tensor(output_details[0]['index'])[0]
det_classes = interpreter.get_tensor(output_details[1]['index'])[0]
det_scores = interpreter.get_tensor(output_details[2]['index'])[0]
num_det = interpreter.get_tensor(output_details[3]['index'])[0]
使用对象检测代码进行绘制
为了可视化,我使用了可用的 python 代码这里是,它不仅可以用来绘制边界框,如果需要的话还可以用来绘制关键点和实例遮罩。我们需要传递要绘制的图像、边界框、检测到的类、检测分数和标签字典。除此之外,当我们从解释器接收到归一化的边界框坐标时,我们还将归一化的坐标设置为 true。
from object_detection.utils import visualization_utils as vis_utilvis_util.visualize_boxes_and_labels_on_image_array(
img,
output_dict['detection_boxes'],
output_dict['detection_classes'],
output_dict['detection_scores'],
category_index,
use_normalized_coordinates=True,
min_score_thresh=0.6,
line_thickness=3)
结果
它将给出如下所示的结果。
结果
但是,仍然有一个问题需要解决,如下所示。
问题结果
这可以通过非最大抑制来实现。
非最大抑制
我不打算解释它,因为它已经在互联网上的各种文章中深入讨论过了。一个这样的例子是这篇文章。为了实现它,我将使用combined_non_max_suppression
Tensorflow Image 来执行这个任务,因为它允许我们同时处理多个类。它获取输出并返回阈值后剩余的预测。
def apply_nms(output_dict, iou_thresh=0.5, score_thresh=0.6):q = 90 # no of classes
num = int(output_dict['num_detections'])
boxes = np.zeros([1, num, q, 4])
scores = np.zeros([1, num, q])
# val = [0]*q
for i in range(num):
# indices = np.where(classes == output_dict['detection_classes'][i])[0][0]
boxes[0, i, output_dict['detection_classes'][i], :] = output_dict['detection_boxes'][i]
scores[0, i, output_dict['detection_classes'][i]] = output_dict['detection_scores'][i]
nmsd = tf.image.combined_non_max_suppression(
boxes=boxes,
scores=scores,
max_output_size_per_class=num,
max_total_size=num,
iou_threshold=iou_thresh,
score_threshold=score_thresh,
pad_per_class=False,
clip_boxes=False)
valid = nmsd.valid_detections[0].numpy()
output_dict = {
'detection_boxes' : nmsd.nmsed_boxes[0].numpy()[:valid],
'detection_classes' : nmsd.nmsed_classes[0].numpy().astype(np.int64)[:valid],
'detection_scores' : nmsd.nmsed_scores[0].numpy()[:valid],
}
return output_dict
下面给出了完整的代码,或者你可以访问我的 Github repo ,它也包含了visualization_utils.py
和模型。
在结束之前,我想澄清一件事,如果你试图在采用英特尔处理器的 Windows 上运行它,你会得到一个可怕的 fps。我在 i5 上得到了大约 2,作为比较,没有 tflite 的相同 Tensorflow 模型给了我大约 8 fps。这里的就是解释这个。然而,在 edge 设备上,这不是问题,而且内存占用相当少,这将有利于他们的内存限制。
虽然这个模型不是很准确,但我希望我会提供一个样板文件,使您在使用 Tflite 的对象检测器时更容易完成任务。
使用 TFRecords 训练 CNN 了解 MNIST
编写、读取和使用 TFRecords 的简短演练
当我开始使用 TFRecords 时,我花了一段时间来理解它的概念。有那么多新事物。为了让其他人免于这种麻烦,我创建了一个基于 MNIST 数据集的实践演练。
注意:这篇博文现在在有一个更通用的版本,包含了更多最新的概念。此外,还要注意的是,一旦你开始使用,TFRecord 格式并不是那么难,这就是为什么我创建了一个的实用介绍。要了解更多,在本教程之后,我建议您参考这两个资源。
概观
MNIST 数据集由黑白的数字化手写数字组成。每张图片 28x28x1,非常小。整个数据集的内存占用只有 32 MB。
数据集概述。作者约瑟夫·斯泰潘。
导入和助手函数
让我们从必要的进口开始;两个库,os 和 Tensorflow。此外,我们还设置了一个全局变量 AUTOTUNE,我们稍后会用到它。
首先,我们将 MNIST 数据集下载到本地机器上。然后,我们将两个选项设置为 True, shuffle_files 和 as_supervised 。
当我们创建 TFRecord 数据集时,使用第一个选项;第二个选项允许稍微舒服一点的迭代。
我们还可以通过调用。基数()。
以下四个函数提高了可读性,并用于创建我们写入 TFrecord 文件的单个示例。
写入 TFRecords
接下来,我们检查所有拆分(这里,只有“训练”和“测试”)。
对于每个分割,我们创建一个 TFRecordWriter ,它将解析后的示例写入文件。请注意,我们将当前处理的分割添加到文件名中——这允许我们稍后通过字符串模式对文件进行分组。我们使用的额外索引用于计算我们写入磁盘的样本数量。这个小技巧对于自定义数据集很有帮助。cardinality()操作不会返回元素的数量。
因为我们之前设置了 as_supervised,所以现在我们可以迭代(例如,label)对。
主要的示例生成发生在临时字典数据的创建期间。首先,我们将稍后要使用的每个数据字段转换成一个 tf.train.Feature 。对于图像的高度和宽度,我们使用一个 _int64_feature (因为这些数字是整数);对于实际的图像数据,我们首先序列化数组,然后将其转换成一个 bytes_list 。存储非标量数据需要这种转换。
定义好所有特性后,我们现在可以创建一个单独的示例并将其写入 TFRecord 文件。
我们继续进行,直到处理完当前拆分的所有示例,然后对以下子集重复该过程。
在处理完两个子集之后,我们已经创建了前两个 TFRecord 文件(耶!),一个保存训练,一个保存测试数据。我们为每个子集的每个记录增加的索引在训练模型时很有用:
对于 tensorflow_datasets 附带的数据集,只需调用。基数()。这不会报告像我们这样的自定义数据集的实际大小,而是返回-1,这意味着示例的数量是未知的。
但是,当在这样的数据集上训练时,我们必须知道我们的数据集可以交付多少批。否则,我们可能会在一个无限循环中运行;详情见下文。
简单回顾一下:我们使用了 MNIST 数据集,并将所有示例写入 TFRecord 文件。
正在读取 TFRecords
在创建之后,我们希望将它们读回内存中。这个过程与上面类似,但方向相反:
我们创建了一个从 TFRecord 文件中读取示例的函数。在这里,我们创建了一个字典,其中包含了我们希望从示例中使用的所有字段;这本字典类似于我们用来写数据的那本。对于每个键,我们定义一个占位符。注意最后一个字段:它的类型是 tf.string (尽管我们将它存储为一个字节列表);所有其他字段都用与以前相同的类型初始化。
准备好字典后,我们可以从 TFRecord 文件中提取示例。最后,我们获得原始图像数据。注意,我们将 uint8 设置为数据类型。如果我们的图像包含浮点,我们将设置 float64 为数据类型。由于 MNIST 数据的范围在 0 到 255 之间,因此我们可以接受整数:
创建数据集
通过下面的函数,我们围绕 TFRecord 文件创建了一个数据集。以前,我们只定义了一个函数来得到一个例子;现在我们创建一个 TFRecordDataset 来将所有的例子映射到这个函数。
我们在中间语句中这样做。这里,我们使用上面创建的自动调优器。在训练过程中,稍后,它会自动确定我们可以并行处理多少个示例。这可以减少 GPU 的空闲时间。
之后,我们重组数据,设置批量大小,并设置 repeat,不带参数;这意味着无休止地重复。这需要我们记住我们的数据集有多少个例子(如上所述)。
[作为一种简单的替代方法,我们可以在这里将 repeat 设置为我们稍后想要训练的时期数,并将 fit()-函数中的时期数设置为 1。这使得数据集通过我们的网络解析“epoch”次(因为我们用 epoch 的数量设置了 repeat(),但是只解析一次(因为我们用 epochs = 1 设置了 fit())。]
最后,我们让自动调优器决定预取的最合适的例子数量。
到目前为止,我们只创建了一个数据集,并向它映射了一个数据生成函数。作为健全性检查,让我们看一下数据集给我们的一个样本。
它返回两个张量。第一个张量的形状为(32,28,28,1)(因为我们取了一个批次,批次大小为 32),第二个张量的形状为(32,)(因为我们有 32 个标签,在我们的批次中每个示例一个)。
到此为止,我们来回顾一下:
我们创建了两个 TFRecord 文件,一个用于训练数据,一个用于测试数据。我们通过迭代原始 MNIST 数据集中的所有(影像,标注)对,将它们转换为 tf.train.Feature。所有要素一起形成一个示例。然后,我们创建了一个函数来反转这一点;它从存储在 TFRecord 文件中的示例中提取特征。最后,我们将该函数映射到我们的数据集,并进行健全性检查,看看是否一切正常。
培养
我们的下一步是在 TFRecordDataset 上训练一个网络。
我们使用一个简单的卷积神经网络;请随意尝试不同的架构。
为了保持可读性,我们编写了一个函数来返回我们的网络,并检查我们的输出层是否符合标签形状,
然后在我们的训练数据集上拟合网络。我们将历元数(网络看到完整数据集的次数)设置为 2。
现在,我们需要知道我们的训练数据集有多少个例子。由于我们将数据集设置为无限重复,所以我们需要告诉网络要查询多少批,直到完成一个历元;这是参数步数每纪元:
训练完成后,在启用 GPU 的情况下,在 Colab 上大约需要 30 秒,我们将测试该模型。由于我们还将测试数据写入了 TFRecord 文件,我们可以使用我们的 get_dataset() 函数从这些文件中快速创建另一个 TFRecordDataset。
由于我们将函数调用中的第二个参数设置为“test”,所以我们的数据集不会重复;我们不需要确定步骤的数量。
然后我们调用 model.evaluate(),它返回两个值的数组。
第一个数字是损失;第二个数字是我们感兴趣的:精确度。仅仅过了两个时代,它就徘徊在 95%左右,这是一个好的开始。
摘要
我们把重点放在 MNIST 数据集上作为我们正在进行的例子。使用它,我们创建了两个 TFRecord 文件,一个用于训练数据,一个用于测试数据。接下来,我们讨论了将数据读回内存,以最终训练 CNN。
这篇博文现在在有了一个更通用的版本,包含了更多最新的概念。此外,还要注意的是,一旦你开始使用,TFRecord 格式并不是那么难,这就是为什么我创建了一个的实用介绍。要了解更多,在本教程之后,我建议您参考这两个资源。
涵盖整个过程的 Colab 笔记本在这里有售。
这篇文章讨论了现有数据集环境中的 TFRecords。如果你有兴趣看到这种用于自定义数据集的文件格式,请看一下这段代码
创建自定义 TFR 数据集
colab.research.google.com](https://colab.research.google.com/drive/1yQRDYzJsHX8w5g042w02y0lm1S3vMscL?usp=sharing)
对于这个帖子
包含代码的端到端示例项目。
towardsdatascience.com](/custom-audio-classification-with-tensorflow-af8c16c38689)
使用卡方检验进行特征选择和实现
特征越少,解释模型就越容易
https://unsplash.com/@purzlbaum
让我们使用卡方问答方式来解决这个特征选择问题。如果你是一个视频爱好者,你可以看看我们在同一个的 youtube 讲座。
问题 1:什么是特性?
对于任何 ML 或 DL 问题,数据都是按行和列排列的。让我们以泰坦尼克号沉船问题为例。
- 这些行由乘客的数据组成,实际上,每一行都由单个乘客的数据组成。这通常被称为实例、实体或观察。
- 这些栏目描述了乘客的性别、年龄等。这些被称为特征或属性。
泰坦尼克号的数据、观察、特征(Imag 来源作者)
问题 2:有哪些不同类型的功能?
不涉及太多细节,让我们以上面的例子为例:
- 年龄、费用、Pid 是数字属性。但是,Pid 不会泄露任何模式,因为它通常是连续或随机分配的。
- 姓名、性别等。是分类属性(它们可以进一步分为名词性的和序数性的‘大小’,值有小、中、大之类)
- 特殊类是具有两个可能值的分类变量或二元变量,如性别或幸存变量
- 在问题的上下文中,我们要预测的变量的值是因变量,而我们要确定的变量或特征称为自变量。
问题 3:什么是特征选择?
在泰坦尼克号沉船数据集中,要解决的问题是给定乘客的特征,要确定乘客是否幸存(即,他是否能够接近船只)。所以,这是一个分类问题。
图片来源:https://unsplash.com/photos/MeGmdPNe36w
如果原始数据集有关于乘客的 8 个特征,并且分类模型带来大约 90%的分类准确度,特征选择的目标是从 8 个特征中选择可能的 3 个或 4 个,并且仍然达到相似的准确度。
问题 4:为什么选择功能?
具有较少的功能:
- 这些模型更容易解释
- 模型训练更快,模型所需的空间也更大
问题 5:特征选择和卡方有什么联系?
实际上,这不是卡方检验,而是假设检验。同样的典型设置是:
- H0:属性性别在乘客的生存中不起作用(特征不重要)
- H1:属性性别在生存中扮演了一个角色(特征很重要)
值得注意的是,这些技术被称为单变量技术,因为我们分别检查每个特征。
C 当特征是分类的,目标变量是任何可以被认为是分类的时,使用卡方检验。它衡量两个分类变量之间的关联程度。如果两者都是数字,我们可以使用皮尔森的积差相关,如果属性是数字,并且有两个类别,我们可以使用 t 检验,如果超过两个类别,我们可以使用 ANOVA
当我们探讨性对生存状态的影响时,我们可以从一个简单的形象化开始。
泰坦尼克号生存状态对性别(图片来源:作者)
上面的图像清楚地表明,两个班级的男女比例并不相同。假设检验是一种更正式的方式。卡方统计使用以下公式计算,其中“O”代表观察值或实际值,“E”代表期望值(如果这两个类别是独立的)。如果它们是独立的,这些 O 和 E 值将是接近的,并且如果它们有一些关联,那么卡方值将是高的。
卡方公式(来源:作者)
乘客性别对抗生存(来源:作者)
上表显示了乘客的性别与生存状态。这也被称为列联表。
- 如果他们是独立的,那么幸存和未幸存的男女比例应该是相同的。
- 幸存的比率为 449/1313
- 如果没有依赖或关联,463 * 449/1313 的女性应该存活,即 158.13
- 这是独立状态下的预期计数
- 所有四种情况计算如下
卡方计算观察值与期望值(图片:作者)
这些卡方统计由自由度调整,自由度随变量的级别数和类变量的级别数而变化。
可以注意到,在适当离散化之后,卡方也可以用于数值变量。
问题 6:如何实现相同?
导入库
from sklearn import datasets
from sklearn.feature_selection import chi2
from sklearn.feature_selection import SelectKBest
我们将对葡萄酒数据集进行特征选择
来源:https://archive.ics.uci.edu/ml/datasets/wine
让我们看看卡方得分
# Loading wine data
X,y=datasets.load_wine(return_X_y=True)
# k = 4 tells four top features to be selected
# Score function Chi2 tells the feature to be selected using Chi Square
test = SelectKBest(score_func=chi2, k=4)
fit = test.fit(X, y)
fit.scores_
它将按如下方式打印这些值
array([5.44549882e+00, 2.80686046e+01, 7.43380598e-01, 2.93836955e+01,4.50263809e+01, 1.56230759e+01, 6.33343081e+01, 1.81548480e+00,9.36828307e+00, 1.09016647e+02, 5.18253981e+00, 2.33898834e+01,1.65400671e+04])
现在,如果我们想选择前四个特性,我们可以简单地做以下事情
X_new=test.fit_transform(X, y)
尾注:
卡方是用于分类的单变量特征选择的非常简单的工具。它没有考虑特征的相互作用。这最适合分类变量,因此在文本数据中应用广泛。
参考资料:
[1]https://youtu.be/hkDfi5j-6lM
[2]https://machine learning mastery . com/feature-selection-machine-learning-python/
[3]https://www . ka ggle . com/saptarsi/feature-selection-using-chi-square/
在 Python 中使用当前人口调查
克里斯里德在 Unsplash 上的照片
我花了长周末的大部分时间浏览所有公开的关于消费者金融和劳动力的数据。这是一个长期项目的一部分,但我现在必须分享我遇到的一些困难,这些困难无法通过堆栈溢出或其他资源来解决。
教训很明显:用 Python 处理 CPS 数据很难。我依靠了一些资源,比如 Brian Dew 的经济学博客和 Tom Augspurger 的工具,它们在缺少 API 的情况下帮助很大。但是因为我主要是想导入尽可能多的数据,并把它读入熊猫数据框,所以我不得不利用他们的技术来做一些符合我的目的的东西。我想出的一般方法如下。
该方法
我遇到的第一个障碍是,我找到的大多数资源都在 CPS 中寻找数据子集,无论是按时间框架还是按变量。你可能会认为,不用担心子集,我所要做的就是处理 CPS 数据本身的古怪,但是唉。
总的来说,CPS 数据的古怪相对简单地分解了方法。
对于一年中的任何给定月份,CPS 提供:
- 。存储在 zip 文件夹中的 dat 文件
- 答。当年使用的 txt 数据字典
虽然 CPS 没有以更容易阅读的方式提供。csv 或。dta 的步骤还是很容易理解的。一般的方法是(1)使用数据字典查找变量及其位置的解析参数,以及(2)从。dat 文件转换成数据帧。
同样,你会认为给定资源,我发现实现我的目标会很容易。对我发现的代码进行一些调整,应该可以让我轻松地获得给定月份的每个变量的数据,并在 CPS 中这样做很多年。然而,这第一步是最困难的,需要最多的变通办法。我希望提供一个更完整的解决方案,但我会在这里检查几个问题和解决方案。
问题 1:检索并解析出相关的数据字典
我不确定我是否有先见之明,或者只是固执地记录我从网上检索数据的确切位置,但第一个挑战涉及实际检索和解析数据字典。
首先,正如您在下面看到的,存储字典的地方没有一致的 URL 样式。第二,有时数据字典适用多年或仅适用几个月。最后,数据字典有多种组织相关数据的方式。
第一课:如果其他方法都失败了,那就放弃吧
如果你快速浏览一下自 1998 年以来所有可用的数据字典,你会发现它们所在的 URL 并不一致。并且没有明确的模式来决定一个字典何时退役或者更新到足够多的异常。
真正让我明白如何解析每个 URL 的是编写一长串 if-elif 语句。正如您在下面的片段中所看到的,2020 年大大改变了他们的 URL,数据字典在 2012 年进行了更新,在 2012 年之前,URL 格式似乎有所改变。使用下面的 if-elif 代码可以更容易地识别和归纳这些模式,但是显然需要归纳成可读性更好的东西。
URL 的变化是最不重要的问题。数据字典本身在 2002 年发生了巨大的变化,用一种完全不同的模式来描述 CPS 变量。下面你可以看到另一个 if-elif 链,显示了这些年来的变化。尽管它们已经相对一般化了,但是这个 if-elif 链还有一个额外的好处,那就是显示主要变化发生的时间。
问题 2:准备用于熊猫数据框的数据
如果你看一个. dat 文件,人眼完全看不懂。所以要把它变成类似桌子的东西需要一点准备。这里的主要问题是获得。dat 文件转换成稍微更有组织的形式,就像一个元组列表,其中每个元组都是数据帧中的一行。
之前的资源提供了一个很棒的例子【2017 年 4 月数据:
然而,当我查看前几年的数据时,你开始遇到问题。dat 文件,如包含杂散*字符或字母。你可以在代码中看到这一点。dat 数据将被转换成 int。
第二课:垂直阅读而不是水平阅读
为了解决这个问题,我必须创建一个助手函数来识别哪里的数据不能作为 int 读取,并提供一个估算值。在这种情况下,估算值将为“-1”,因为它经常在 CPS 数据中用作“不适用”值,但肯定需要对变量进行双重检查。
鉴于上面的代码缺乏可读性和代码嵌套,调整它来执行这样的操作有点困难。一般来说,水平阅读代码比垂直阅读代码更难,即使垂直阅读一开始看起来有点奇怪。
您可以看到下面的转换示例有一些好处。首先,更容易理解列表理解是如何工作的,以及每行代码是如何分组的。
其次,它读起来几乎完全像一步一步的指导,而不是文学的一句俏皮话。每行都有一个括号,读起来就像它自己的步骤,就像“开始组”或“结束列表”。
下面的代码仍然不漂亮,需要更多的 Python 禅宗,但它的工作。
问题 3:我想要更多的数据!!
这是更严重的问题之一。因为我的主要目标是尽可能多地收集数据,所以我倾向于让我的代码把所有东西放在一个整洁的包里。当然,在我的笔记本电脑上这样做肯定会导致一些内核重启。
第三课:概括,但不要太多
解决方法很简单:保持简单。在这种情况下,我不得不限制自己编写能够一次获得一个月 CPS 数据的代码。我还不得不阻止自己制作额外的助手功能,通过互联网存档对数据源进行存档,但我认为这将是我的下一个项目。
最后的想法
真的没有什么可以代替你自己去尝试。因为我真的对获取尽可能多的数据感兴趣,如果你真的只是对特定的数据集感兴趣,你可能会遇到较少的问题。但是你和我一样,希望这些课程能对你的 CPS 工作有所帮助。
通过 Python 使用 Fitbit Web API
上图是由 Fitbit 数据制作的,只是没有通过 API。我只是想分享我同事的工作。https://journals.plos.org/plosone/article?id = 10.1371/journal . pone . 0227709
Fitbit 提供了一个 Web API,用于从 Fitbit 活动跟踪器、Aria & Aria 2 秤和手动输入的日志中访问数据。所以如果你一直在使用 Fitbit,你可以使用【https://dev.fitbit.com/build/reference/web-api/basics/】的 Fitbit API ( 的)来获取自己的数据。除了使用 API 获取数据的便利之外,您还可以在在线应用程序上获取数据,您可以使用 API 获取在线应用程序上不可用的当天(每分钟)数据。虽然本教程最初与 Stephen Hsu 的惊人的教程相似,但我想我应该稍微更新一下这个过程,解决几个你可能会遇到的错误,并展示一点你如何绘制数据。和往常一样,本教程中使用的代码可以在我的 GitHub 上获得。就这样,让我们开始吧!
1.)创建一个 Fitbit 帐户
您可以点击这里创建一个 Fitbit 账户。它会带你到一个类似下面的页面。
你不需要像我一样勾选“随时更新”框。此外,fakeuser@gmail 不是我的帐户使用的电子邮件。
2.)设置您的帐户并创建应用程序
前往dev.fitbit.com。将鼠标悬停在“管理”上,然后点击“注册应用程序”。
应该会出现一个类似于下图的页面。
(答)你需要指定个人,以便能够更方便地要求下载日内数据(如果你没有或得到一个错误,你可以随时要求它这里)。( B )回调 URL 是 http://127.0.0.1:8080,因为我们将使用的 Python API 将它作为默认重定向 URL。
对于图像中的每个字段,这里有一些关于您可以在注册页面中放置什么的建议。
**应用名称:**可以是任何东西。
**描述:**可以是任何东西。
**申请网址:**可以是任何东西。
**组织:**可以是任何东西。
**组织网站:**由于我是个人使用(查看个人 fitbit 数据),这可能不适用。
**服务条款网址:**我放了 Fitbit 服务条款:https://dev.fitbit.com/legal/platform-terms-of-service/
**隐私政策网址:**我放了 Fitbit 隐私政策:https://www.fitbit.com/legal/privacy-policy
**OAuth 2.0 申请类型:**如果您想下载您的日内数据,OAuth 2.0 申请类型应为“个人”。顺便说一下,如果你不知道 OAuth 是什么,这里有一个解释。
回调 Url: 确保回调 Url 是 http://127.0.0.1:8080/这是因为我们将使用的库需要这样做,如下所示。
最后,单击协议框,然后单击注册。应该会出现一个类似于下图的页面。
记下您的 OAuth 2.0 客户端 ID 和客户端机密。
我们将从这个页面中需要的部分是 OAuth 2.0 客户端 ID 和客户端密码。稍后您将需要客户端 ID 和客户端密码,因此请记下它们。
# OAuth 2.0 Client ID
# You will have to use your own as the one below is fake
12A1BC# Client Secret
# You will have to use your own as the one below is fake
12345678901234567890123456789012
3.)安装 Python 库
下一步是使用一个 Fitbit 非官方 API 。点击链接后,点击绿色按钮。接下来,单击 Download Zip 并继续解压缩文件。
下载 zip 文件。
之后,打开终端/命令行,将目录切换到解压后的文件夹,并运行下面的命令。
#reasoning for it here:
#[https://stackoverflow.com/questions/1471994/what-is-setup-py](https://stackoverflow.com/questions/1471994/what-is-setup-py)
# If you want to install it a different way, feel free to do so. python setup.py install
进入文件夹目录,在终端/命令行中输入 python setup.py install 。
4.)API 授权
在开始本节之前,我应该注意两件事。首先,本教程中使用的代码可以在我的 GitHub 上找到。第二,如果你有错误,我在博文的后面有一个潜在错误部分。
下面的代码导入各种库,并将步骤 2 中的 CLIENT_ID 和 CLIENT_SECRET 赋给变量。
# This is a python file you need to have in the same directory as your code so you can import it
import gather_keys_oauth2 as Oauth2import fitbit
import pandas as pd
import datetime# You will need to put in your own CLIENT_ID and CLIENT_SECRET as the ones below are fake
CLIENT_ID='12A1BC'
CLIENT_SECRET='12345678901234567890123456789012'
下面的代码使授权过程能够发生
server=Oauth2.OAuth2Server(CLIENT_ID, CLIENT_SECRET)
server.browser_authorize()
ACCESS_TOKEN=str(server.fitbit.client.session.token['access_token'])
REFRESH_TOKEN=str(server.fitbit.client.session.token['refresh_token'])
auth2_client=fitbit.Fitbit(CLIENT_ID,CLIENT_SECRET,oauth2=True,access_token=ACCESS_TOKEN,refresh_token=REFRESH_TOKEN)
当您运行上面的代码(如 A 所示)时,单元格显示仍在运行,直到您登录您的 fitbit 帐户(B)并单击允许访问您的 Fitbit 帐户中的各种数据。
授权和登录后页面的外观。
这个窗口应该是你得到的。
5a。)获取一天的数据
您将使用 intraday_time_series 方法来获取数据。
我将首先从获得一天的数据开始,这样下一节将更容易理解。
# This is the date of data that I want.
# You will need to modify for the date you want
oneDate = pd.datetime(year = 2019, month = 10, day = 21)oneDayData = auth2_client.intraday_time_series('activities/heart', oneDate, detail_level='1sec')
如果你愿意,你可以把这些数据放入熊猫数据框。
当然,您总是可以将数据导出到 csv 或 excel 文件中。
# The first part gets a date in a string format of YYYY-MM-DD
filename = oneDayData['activities-heart'][0]['dateTime'] +'_intradata'# Export file to csv
df.to_csv(filename + '.csv', index = False)
df.to_excel(filename + '.xlsx', index = False)
5b。)获取多天的数据
下面的代码从startTime
变量(在下面的代码中称为oneDate
)开始获取所有日期的数据。
你当然可以导出final_df
到一个 csv 文件(我建议不要试图导出到 excel,因为可能有太多的行很容易导出到 excel)。
filename = 'all_intradata'
final_df.to_csv(filename + '.csv', index = False)
6.)试着用图表显示当天的数据
这是一个部分,我强烈建议你用你自己的数据看看 GitHub 代码。
这一部分的最终目标是获得特定日期的特定时间的适当图表(日期也包括当天的时间)。我应该注意到,部分由于我的懒惰,我没有使我的代码高效。如果你不明白是怎么回事,不要着急。您可以向下滚动并查看最终结果。
# I want to get the hour of the day and time. The end goal of this section is to get a particular time on a particular day.
hoursDelta = pd.to_datetime(final_df.loc[:, 'time']).dt.hour.apply(lambda x: datetime.timedelta(hours = x))minutesDelta = pd.to_datetime(final_df.loc[:, 'time']).dt.minutes.apply(lambda x: datetime.timedelta(minutes = x))secondsDelta = pd.to_datetime(final_df.loc[:, 'time']).dt.seconds.apply(lambda x: datetime.timedelta(seconds = x))# Getting the date to also have the time of the day
final_df['date'] = final_df['date'] + hoursDelta + minutesDelta + secondsDelta
我的下一步将是查看 3 天的数据,而不是几天。
startDate = pd.datetime(year = 2019, month = 12, day = 24)
lastDate = pd.datetime(year = 2019, month = 12, day = 27)coupledays_df = final_df.loc[final_df.loc[:, 'date'].between(startDate, lastDate), :]
请记住,您也可以尝试通过按日期和小时(如果您愿意,甚至可以按秒)分组并取心率的平均值来绘制图表。代码还试图使图形看起来更好一些。
fig, ax = plt.subplots(figsize=(10, 7))# Taken from: [https://stackoverflow.com/questions/16266019/python-pandas-group-datetime-column-into-hour-and-minute-aggregations](https://stackoverflow.com/questions/16266019/python-pandas-group-datetime-column-into-hour-and-minute-aggregations)
times = pd.to_datetime(coupledays_df['date'])
coupledays_df.groupby([times.dt.date,times.dt.hour]).value.mean().plot(ax = ax)ax.grid(True,
axis = 'both',
zorder = 0,
linestyle = ':',
color = 'k')
ax.tick_params(axis = 'both', rotation = 45, labelsize = 20)
ax.set_xlabel('Date, Hour', fontsize = 24)
fig.tight_layout()
fig.savefig('coupledaysavergedByMin.png', format = 'png', dpi = 300)
有了这些工作,也许这不是我下去的好方法,因为目前的数据没有给出足够的背景来知道它是在休息或四处走动时。
7.)静息心率
有研究称静息心率可以反映你现在和未来的健康状况。实际上有一篇研究论文显示了持续佩戴 Fitbit 的 92,447 名成年人的日常静息心率的个体间和个体内可变性及其与年龄、性别、睡眠、身体质量指数和一年中的时间的关联。下面是论文中的一张图片(为清晰起见,添加了文字)。
【https://journals.plos.org/plosone/article? id = 10.1371/journal . pone . 0227709
我们一直使用的 api 调用也返回静息心率,因此下面的代码与前面的步骤没有太大的不同。
# startTime is first date of data that I want.
# You will need to modify for the date you want your data to start
startTime = pd.datetime(year = 2019, month = 11, day = 21)
endTime = pd.datetime.today().date() - datetime.timedelta(days=1)date_list = []
resting_list = []allDates = pd.date_range(start=startTime, end = endTime)for oneDate in allDates:
oneDate = oneDate.date().strftime("%Y-%m-%d")
oneDayData = auth2_client.intraday_time_series('activities/heart', base_date=oneDate, detail_level='1sec')
date_list.append(oneDate)
resting_list.append(oneDayData['activities-heart'][0]['value']['restingHeartRate'])# there is more matplotlib code on GitHub
fig, ax = plt.subplots(figsize=(10, 7))ax.plot(date_list, resting_list)
试着用图表显示你的数据。
8.)获取睡眠数据
在不涉及太多细节的情况下,下面的代码获得了每分钟的睡眠数据(final_df
)和睡眠摘要信息final_stages_df
,即 Fitbit 用户在深度睡眠、浅度睡眠、快速眼动睡眠和觉醒阶段总共花费了多少分钟。
final_stages_df
是一个熊猫数据框架,显示了 Fitbit 用户在给定日期的给定夜晚每个阶段的睡眠时间(清醒、深度、浅睡、快速眼动)以及在床上的总时间。
final_df
是一个熊猫数据帧,给出日期时间、日期和值。值栏中的 1 是“睡着了”, 2 是醒着的, 3 是真的醒着。
潜在错误
没有名为 gather_keys_oauth2 的模块
这是针对你得到一个类似下面的错误的情况。
要解决这个问题,可以将 gather_keys_oauth2.py 文件放在 python 文件或 jupyter notebook 所在的同一个目录下。你可以在下面看到我的项目目录是如何排列的。
请注意,gather_keys_oauth2.py 与我的 jupyter 笔记本在同一个目录中。这是我们在步骤 3 中下载的 python-fitbit-master 中包含的一个文件。
无法识别 cherrypy,没有名为 CherryPy 的模块
如果你得到一个类似下面的错误,首先关闭你的 jupyter 笔记本或者 python 文件。
您可以使用下面的命令解决这个问题。
pip install CherryPy
Https vs Http 错误
以下错误来自第 2 步,当时我曾试图将回调 url 设置为 https 而不是 http。
HTTPTooManyRequests:请求太多
fitbit 社区论坛有这个问题的答案。使用该 API,您可以每小时为每个用户/fitbit 处理多达 150 个请求。这意味着,如果您有超过 150 天的数据,并且您使用了本教程中步骤 5b 的代码,那么您可能需要等待一段时间才能获得其余的数据。
结束语
这篇文章讲述了如何从单个 Fitbit 获取数据。虽然我已经讲了很多,但是还有一些事情我没有讲,比如说 Fitbit api 如何获得你的步数。Fitbit API 是我仍在学习的东西,所以如果你有贡献,请让我知道。如果你对教程有任何问题或想法,欢迎在下面的评论中或通过推特联系。
用基尼系数评价信用评分模型的性能
基尼系数背后的机制、计算方法、常见陷阱及其主要缺点。
当一个新的信用评分模型诞生时,通常出现的第一个问题是:“它的基尼系数是多少?”。对于一个局外人来说,这听起来一定像是对迪士尼电影《阿拉丁》的奇怪引用。但是,基尼系数或基尼系数是金融业用来评估信用评分模型性能的最受欢迎的指标之一。
照片由 Cesira Alvarado 在 Unsplash 上拍摄
基尼系数是一个指标,表明模型的区分能力,即模型在区分“坏”借款人(将来会违约)和“好”借款人(将来不会违约)方面的有效性。该指标通常用于比较不同模型的质量,并评估它们的预测能力。
FICO 使用基尼系数展示了其信用评分模型的质量。来源: FICO
尽管基尼系数具有共性,但一些从业者并不真正熟悉基尼系数之外的机制,并错误地将其与同名的不同指标相混淆。虽然许多从业者错误地将基尼系数与洛伦兹曲线的摘要、科拉多基尼的不平等度量[1]联系起来,但他们使用的基尼系数大多数时候是 Somers’ D ,即 CAP(累积准确度分布)曲线的摘要。
Somers’ D 是以 Robert H. Somers 的名字命名的,他在 1962 年提出了这个概念[2]。它是两个变量之间顺序关系的度量。在信用评分模型的情况下,它衡量模型预测(以违约概率或评分表示)与实际结果(违约或不违约)之间的顺序关系。如果模型是有用的,低分(高 PD)应该比高分(低 PD)更与违约相关。
Somers’ D 取值在(-1)和 1 之间。(-1)是完美的负序数关系,1 是完美的序数关系。在实践中,Somers’ D 为 0.4 的信用评分模型被认为是好的。(此后,我将把萨默斯称为基尼系数)
出于极简主义的考虑,我不会描述计算基尼系数所涉及的数学。相反,我将展示三种不同的方法来导出它。
为了演示每一种方法,我将使用一个样本信用评分模型,该模型是使用逻辑回归和来自 Lending-club 的 10,000 名借款人的数据开发的。
model <- glm(default ~ fico + loan_amnt + annual_inc + home_ownership, family = "binomial", data = data_set)
基于模型的预测——估计违约概率(PD ),我从 1 到 1000 给每笔借款打分;1 代表最低 PD,1000 代表最高 PD。
从 CAP 曲线中提取基尼系数
在我们的上下文中,CAP 曲线旨在捕捉得分(PD)和违约率之间的顺序关系。如果我们的模型在区分好的和坏的借款人方面做得很好,我们将会发现低评分借款人比高评分借款人有更多的违约。CAP 曲线通过从最低分到最高分抽样借款人时累计违约率来捕捉这一概念。
为了构建 CAP 曲线,所有模型的总体需要按照违约的预测可能性排序。也就是说,得分最低的观察值在前,得分最高的观察值在后。然后,我们从头到尾对总体进行抽样,每次抽样后,计算累计违约率。帽曲线的 x 轴代表抽样人口的部分,y 轴代表相应的累积违约率。
如果我们的模型具有完美的区分能力,我们将期望在对一部分观察值进行采样后达到 100%的累积违约率,这等于我们数据中的违约率(下图中的绿线)。例如,如果我们数据中的违约率是 16%,那么在对 16%的观察值进行抽样后,我们将捕获我们数据中的所有违约。相反,如果我们使用随机模型,即以相等分布随机分配分数的模型,累积违约率将始终等于抽样观察的部分(下图中的红线)。
基尼系数被定义为模型曲线和随机模型线(A)之间的面积与完美模型曲线和随机模型线(A+B)之间的面积之比。换句话说,基尼系数是一个比率,代表我们的模型离“完美模型”有多近,离“随机模型”有多远。因此,“完美模型”的基尼系数为 1,“随机模型”的基尼系数为 0。
我的模型获得了低基尼系数 0.26 :
构造洛伦茨曲线,提取科拉多基尼系数,然后得出基尼系数
洛伦兹曲线是 CAP 曲线的逆曲线;它是使用相同的抽样观察和累计违约率机制构建的,但抽样顺序相反(从最高分到最低分)。洛伦兹也有一条对角线,相当于 CAP 随机模型’线,被称为‘等比线’(下图中的红线)。
两条曲线的另一个区别是“完美模型”线。由于洛伦茨曲线是为了捕捉财富的分布而设计的,因此最具歧视性的结果是人口的所有财富都集中在一个观察值中的情况。也就是说,相当于洛伦兹曲线中的“CAP perfect model”线的线是由两条垂直线构成的,这两条垂直线是 x 轴和一条垂直线,垂直线是从 x 轴的末端以 100%的值(下图中的绿线)演变而来的。这条线表示所有累积结果都在最后一次采样观察中的情况。科拉多基尼系数的值被定义为模型曲线和“平等线”之间的面积与“平等线”和 x 轴之间的面积之比。
然而,当使用洛伦兹曲线来评估信用评分模型的区分能力并将其 y 轴指定为累积违约率时,问题出现了。由于 y 轴描述的是二元结果(1 或 0)的集合,因此不存在所有累积违约率都集中在一次观察中的情况。换句话说,在使用洛伦兹曲线评估一个信用评分模型时,不可能达到“完美模型”线。因此,这种评估的合适的“完美模型”线应该调整到人群中的违约率,就像在 CAP 曲线中一样。
因此,为了调整 Corrado Gini 的信用评分模型评估的措施,我们需要从其分母中扣除不可及区域。
最后,为了从科拉多基尼系数的测量中得出基尼系数,我们可以使用以下公式:
我的模型获得了科拉多基尼系数 0.22:
我的样本中的违约率是 16%,因此我的模型的基尼系数可以计算如下:
构建 ROC 曲线,提取 AUC,然后得出基尼系数
计算基尼系数的第三种方法是通过另一种流行的曲线:ROC 曲线。ROC 曲线下的面积,通常称为 AUC,也是评估和比较信用评分模型性能的常用指标。ROC 曲线总结了混淆矩阵中的两个比率:真阳性比率(TPR 或回忆)和假阳性比率(FPR)。
对于给定的阈值,混淆矩阵总结了以下情况的数量:
- 该模型预测会出现违约,而借款人违约了——T4——真的是正数。
- 该模型预测会出现违约,但借款人没有违约——假阳性。
- 该模型预测没有违约,借款人也没有违约——T2——真阴性。
- 模型预测没有违约,借款人违约——假阴性
例如,让我们用 850 分作为我们的阈值,即 850 分以下的借款人被预测为违约,850 分以上的借款人被预测为不违约。
850 阈值的混淆矩阵
真实正比率(TPR)定义为我们的模型捕捉到的违约借款人的数量超过我们数据中违约借款人的总数。假阳性率(FPR)的计算方法是,模型错误预测违约的案例数占非违约案例总数的比例。
ROC 曲线是通过使用源自 1 到 1000 之间阈值的混淆矩阵并驱动它们的 TPR 和 FPR 来构建的。ROC 曲线的 y 轴代表 TPR 值,x 轴代表 FPR 值。AUC 是曲线和 x 轴之间的面积。
令人惊讶的是,正如 Schechtman & Schechtman,2016[3]所示,AUC 和基尼系数之间存在线性关系。因此,要从 AUC 中得出基尼系数,只需使用以下公式:
从业者倾向于在他们的模型验证报告中披露 AUC 和基尼系数。然而,由于这些指标具有线性关系,这些指标的公开对模型质量的评估没有任何价值。
我的模型的 AUC 是 0.63 ,因此基尼系数是这样计算的:
基尼系数的弊端和陷阱
尽管它的共性,基尼系数有一些缺点和陷阱,你应该考虑当使用它来评估和比较信用评分模型。为了极简主义,在这一节中,我将描述一个在试图推导基尼系数时的常见陷阱及其主要缺点。
为了说明这些概念,我将使用一个玩具示例:15 个借款人,2 个“坏”和 13 个“好”,分数从 1(最高 PD)到 10(最低 PD)。
在试图得出基尼系数时,一个常见的陷阱是相同的分数。在大多数情况下(尤其是当使用大型数据集时),信用评分模型将为不同的观察估计相同的分数。当试图使用 CAP 曲线法得出基尼系数时,这种分数重复提出了一个问题。如上所述,导出 CAP 曲线的第一步是根据得分对观察值进行排序,如以下两个示例所示:
这两个例子使用相同的借款人,得分相同。这两个表之间的唯一区别是第二级排序。在示例 1 中,当存在相同分数的情况时(在分数 5 中),首先出现默认的观察值。例 2 正好相反。这种微小的变化会对基尼系数的值产生重大影响,例如,在这种情况下,实施例 1 的基尼系数为 0.67,实施例 2 的基尼系数为 0.38。
为了避免这个陷阱,我建议像例 1 一样进行二次排序,或者简单地使用上述 AUC 方法得出基尼系数。
基尼系数的主要缺点在于,它是一个有序的度量标准,也就是说,它记录了数值的顺序,而忽略了数值之间的距离。基尼系数的这一特征有时会掩盖模型的不良表现。
当根据两个模型预测的分数对借款人进行排序时,我们会得到相同的“违约”列。这表明这两个模型具有相同的基尼系数(0.85)。
当比较由这两个模型预测的分数分布时,我们得到以下图表:
具有相同基尼系数的两个模型的分数分布
两个模型的基尼系数相同。但是,模型 B 只能将借款人分为三种类型:1、2 和 3,而模型 A 能够捕捉所有 10 个级别的风险。这表明,模型 A 比模型 B 对借款人的不同特征更敏感,可以更好地区分不同的风险水平。
为了理解上述特性的重要性,假设模型 A 的所有者和模型 B 的所有者决定设置一个分数,该分数将是他们贷款批准的阈值。即分数低于或等于阈值的借款人的贷款请求将被拒绝,而分数高于阈值的借款人的贷款请求将被批准。两个模型所有者都检查了他们在测试样本上的模型结果,并决定使用获得 100%累积违约率的分数作为他们的阈值。因此,模型 A 的所有者将阈值设置为 6,而模型 B 的所有者将她的阈值设置为 2。通过选择这些分数,模型所有者得到了非常不同的结果:
模型 A 的所有者拒绝了 8 个“好”借款人,批准了 5 个,而模型 B 的所有者拒绝了 12 个“好”借款人,只批准了 1 个。模型 A 的所有者设置的阈值产生 62%的 FPR,而模型 B 的所有者设置的阈值产生 92%的 FPR。
因此,基尼系数无法捕捉模型区分不同风险水平的有效性是一个主要缺陷。
为了克服这一缺点,我建议对模型预测的分布进行眼球测试,如 Gambacorta,Huang,Qiu 和 Wang,2019[4]所述,并使用精确回忆曲线来评估模型在捕捉“坏”借款人和错误预测“好”借款人违约之间的权衡。
总结
- 金融行业中用于评估信用评分模型质量的基尼系数实际上是 Somers’ D,而不是 Corrado Gini 的不平等衡量标准。
- 计算基尼系数有三种常用方法:
- 从 CAP 曲线中提取基尼系数。
- 构造洛伦茨曲线,提取科拉多基尼系数,然后得出基尼系数。
- 构建 ROC 曲线,提取 AUC,然后得出基尼系数。
- 在计算基尼系数时,一个常见的陷阱是相同的分数。
- 基尼系数的主要缺点是,它没有捕捉到模型对不同风险水平的敏感性。
参考文献
[1]基尼,C. (1914 年)。再版:关于测量字符的集中性和可变性(2005)。密特隆,LXIII(1),338。
[2]萨默斯,R. (1962 年)。有序变量关联的一种新的非对称度量。《美国社会学评论》, 27 (6),799–811。从 www.jstor.org/stable/209040取回 8
[3]谢克特曼,e .,&谢克特曼,G. (2016)。基尼系数法与 ROC 曲线的关系 (SSRN 学术论文编号 ID 2739245)。纽约罗切斯特:社会科学研究网络。
[4]甘巴科尔塔,李,黄,杨,邱,黄,王(2019)。机器学习和非传统数据如何影响信用评分?来自一家中国金融科技公司的新证据。从 https://www.bis.org/publ/work834.htm取回