TowardsDataScience-博客中文翻译-2021-八十-

news/2024/10/23 5:43:27

TowardsDataScience 博客中文翻译 2021(八十)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

AWS 胶水上的火花 MLlib

原文:https://towardsdatascience.com/spark-mllib-in-aws-glue-1416b3b5ffe6?source=collection_archive---------23-----------------------

机器学习

准备就绪的 AWS 上的分布式 ML

AWS 力推 Sagemaker 作为其机器学习平台。然而,Spark 的 MLlib 是一个全面的库,它在 AWS Glue 上本地运行分布式 ML——并为他们的主要 ML 平台提供了一个可行的替代方案。

Sagemaker 的一大优势是,它可以通过 Jupyter 笔记本轻松支持实验。但是操作您的 Sagemaker ML 可能很困难,特别是如果您需要在管道的开始包含 ETL 处理。在这种情况下,运行在 AWS Glue 上的 Apache Spark 的 MLlib 可能是一个很好的选择——就其本质而言,它可以立即操作化,与 ETL 预处理集成,并准备好在生产中用于端到端的机器学习管道。

就其本质而言,[AWS Glue]是立即可操作的,与 ETL 预处理集成,并随时可用于端到端机器学习管道的生产中

AWS Glue 是一个托管 Spark ETL 平台,用于通过分布式机器处理大量数据。MLlib 是 Spark 2.4 的一部分,Spark 2.4 是 AWS Glue 的默认版本。在 AWS Glue 中使用 MLlib 不需要添加库。

AWS Glue Studio 图表显示了通过 ETL 的数据流(图片由作者提供)

ETL 预处理到训练和推理一气呵成

作为一个分布式 ETL 平台,AWS Glue(通过 Spark)允许您轻松地执行大规模的数据预处理。Glue Studio 提供了一个很好的 UI 来构建有向无环图,这些图表示每个预处理步骤中的数据流(参见上图中的示例)。从图形用户界面,您可以重命名字段,转换类型,过滤记录和删除列。然而,一些更复杂的数据处理活动需要添加“自定义转换”。需要自定义转换的操作包括:

  1. 运行 MLlib 函数,如 TF-IDF 或模型,如随机森林或梯度提升树算法
  2. 聚合值,例如生成计数、平均值和总和值
  3. 复杂的列转换,如一键编码或连接值

在使用 ETL 转换执行预处理和特征化之后,您需要训练您的数据。要在 AWS Glue 中访问 MLlib,您只需要添加导入语句,就可以开始了。下面是如何使用 PySpark 在 AWS Glue Studio 自定义转换中训练回归决策树模型的示例:

# Get the dataframe. Ensure there's a 'features' column.
df = dfc.select(list(dfc.keys())[0]).toDF()# Get the logger for Cloudwatch Logs
logger = glueContext.get_logger()from pyspark.ml import Pipeline
from pyspark.ml.regression import DecisionTreeRegressor
from pyspark.ml.evaluation import RegressionEvaluator
*# Split data into training and test sets* (trainingData, testData) = df.randomSplit([0.7, 0.3])*# Create a DecisionTree model.* dt = DecisionTreeRegressor(featuresCol="features")*# Create a pipeline to wrap the DecisionTree* pipeline = Pipeline(stages=[dt])*# Train model.* model = pipeline.fit(trainingData)*# Make predictions.* predictions = model.transform(testData)*# Select (prediction, true label) and compute test error* evaluator = RegressionEvaluator(labelCol="label", predictionCol="prediction", metricName="rmse")
rmse = evaluator.evaluate(predictions)
logger.info("Root Mean Squared Error (RMSE) on test data = %g" % rmse)

在上面的代码中,均方根误差(RMSE)被记录到 Cloudwatch 日志中。要使用模型进行预测,最简单的选择是使用相同的自定义转换,并在看不见的推理数据的数据帧上运行 model.transform 。为了使用这些预测,您需要在一个 DynamicFrameCollection 中从自定义转换中返回它们(参见本文底部的代码片段)。

在 AWS Glue 中运行 MLlib 的技巧

在 AWS Glue 中运行 Spark 的 MLlib 时,需要注意一些问题:

  1. 实验可能是缓慢而困难的。迭代和调试可能会令人沮丧,因为每个作业至少需要一分钟来运行。实验时,在缩减的数据集上运行以加快过程(仍然需要 1 分钟以上)。把工人减少到 2 个,用 G.1X 省钱。禁用书签和重试。
  2. 为了帮助调试,您可以从您的定制转换中注销信息到 Cloudwatch。你必须进入 Cloudwatch 上的 /aws-glue/jobs/logs-v2 日志组,然后打开以'-driver '结尾的日志流来查看注销的值。下面是 Glue Studio 定制转换的 PySpark 示例,设置了 Cloudwatch 日志记录。
  3. AWS Glue 开发人员端点可能有助于实验和调试。这些允许您在 Spark 集群上运行开发笔记本,并在编写代码时加速开发迭代。主要缺点是价格昂贵(可能需要 5-15 分钟来启动集群)。请注意,它们是按秒计费的,因此可能比直接执行作业要贵得多。一种选择是在学习 Spark 时使用开发人员端点,然后在熟悉编写 Spark 代码后直接在 AWS Glue 中执行作业。
  4. 虽然您可以在与特性化 ETL 相同的作业中运行 ML 训练和预测,但是您可能希望将它们分成两个作业。通过将它们分成两个作业,您可以分离它们的配置。AWS Glue 提供了两种工作器类型:G.1X 和 G.2X。您可能会发现 G.1X 对于 ETL 来说已经足够了,但是 G.2X 可能更适合 ML 训练和推理,因为每个工作器都有两倍的可用内存。
  5. 只有批量推理可以在 AWS Glue 上工作——实时推理在这里不是一个选项。将 Sagemaker 端点用于该用例。
  6. AWS Glue 上的 MLlib 允许您轻松地重新训练整个数据集,然后在单个作业中执行推理。训练可能是最昂贵的步骤,但是,在批量推断之前,通过再训练,您可以从更好的预测中受益
  7. 尝试使用本地 Spark 数据帧和库,而不是例如 Pandas 数据帧。这将允许您更好地利用 Spark 的分布式功能。
  8. MLlib 中有两种可用的 API——基于数据帧的 API 和基于 RDD 的 API。您应该尽可能使用基于数据框架的方法。同样,尽量避免使用用户定义的函数(UDF)。UDF 和基于 RDD 的功能都比本地数据帧方法慢。
  9. SparseVector 是一种表示稀疏数据集(即具有大量列且大多数值为零的数据源)的有效方式。一个常见的例子是执行自然语言处理时 TF-IDF 的输出。使用 SparseVector 列可以避免向 dataframe 添加成百上千的列(这很可能会导致您的作业因内存不足或超时而失败)。
  10. 由 MLlib 创建的某些 dataframe 列类型如果不先转换为整数或字符串,就无法写出自定义转换。当这种情况发生时,您可能会得到一个错误,如“调用 o341 . todf . org . Apache . spark . ml . Lina LG . vector udt @ 3 bfc 3 ba 7(org . Apache . spark . ml . Lina LG . vector udt 的类)时出错”。
  11. 在 AWS 中,从 S3 读取数据比从其他数据源/目的地读取数据要容易得多。直接连接到数据库可能比它的价值更麻烦。
  12. 您很可能需要使用 Glue 爬虫来学习您在 S3 上的模式。每次数据发生变化时,您都需要重新运行 crawler。使用 S3 路径/键中的分区键和 AWS Glue“S3 数据源”输入节点上的分区谓词来过滤特定日期的数据。
  13. JSON 和 CSV 在开发时可能是有用的数据类型,因为您可以看到输入和输出。然而,它们又慢又大。对于生产,考虑切换到拼花格式的输入和输出。
  14. SparseVector 列(已经转换为字符串)导出到 CSV 会导致无效的 CSV 结构。拼花格式可以替代。
  15. 注意区分新的基于数据框架的 API 和旧的基于 RDD 的 API。确保导入正确的类。在 PySpark 中,你应该导入' pyspark.ml.linalg '而不是' pyspark.mllib.linalg '来访问更新的 API。如果您混淆了类型,您可能会得到奇怪的错误,如" IllegalArgumentException:'要求失败:列特征必须是 struct 类型< type:tinyint,size:int,indexes:array,values:array>,但实际上是 struct < type:tinyint,size:int,indexes:array,values:array>。这两个值看起来是一样的,但是区别在于它们使用了来自错误 API 的对象的不兼容版本。

能否将 AWS 胶水与 Sagemaker 整合?

是的,通过 AWS Glue 的 Spark ETL 可以与 Amazon Sagemaker 集成。典型的工作流可能是:

  1. 使用 Jupyter 笔记本在 Sagemaker 中试验和训练模型
  2. 通过从 Sagemaker 笔记本中部署一个批量推理模型来生产模型
  3. 在 AWS Glue 中使用 ETL 进行生产化预处理和特征化
  4. 计划/触发批处理推理模型在 AWS Glue ETL 完成后运行

上述方法似乎是 AWS 服务的最佳用途。然而,在走这条路之前,你确实需要注意一些事情。例如,有些列类型在 Spark MLlib 和 Sagemaker 模型之间不兼容。例如,Sagemaker XGBoost 要求其输入为特定格式,并且不能从 Spark 读取 SparseVector 列。

注销到 Cloudwatch 的示例

下面是 AWS Glue Studio 上的 PySpark 自定义转换示例,用于注销到 /aws-glue/jobs/logs-v2 日志流下的“ -driver ”日志流。

def MyTransform (glueContext, dfc) -> DynamicFrameCollection:**logger = glueContext.get_logger()**df = dfc.select(list(dfc.keys())[0]).toDF()**logger.info("Number of df rows:" + str(df.count()))**dyf = DynamicFrame.fromDF(df, glueContext, "df")  return DynamicFrameCollection({"CustomTransform": dyf}, glueContext)

在传递自定义转换之前转换 dataframe 列的示例

如果不先将一些 MLlib 列类型转换为字符串或整数,则在尝试从自定义转换返回 dataframe 时会出现错误。VectorUDT 是一种“用户定义类型”的列,通常由 MLlib 转换生成。下面是一个在返回之前将列转换为字符串的示例。

# Cast VectorUDT column to String
rescaledData=rescaledData.withColumn("features",rescaledData["features"].cast("String"))# Reduce the number of columns being returned
rescaledData = rescaledData.selectExpr("labels", "features")# Convert from Spark dataframe to AWS Glue DynamicFrame
dyf_out = DynamicFrame.fromDF(rescaledData, glueContext, "rescaledData") # Wrap the DynamicFrame in a DynamicFrameCollection and return
return DynamicFrameCollection({"CustomTransform": dyf_out}, glueContext)

用于兴趣点分类的火花多层感知器分类器

原文:https://towardsdatascience.com/spark-multilayer-perceptron-classifier-for-poi-classification-99e5c68b4a77?source=collection_archive---------15-----------------------

使用 Spark 深度学习模型将兴趣点分类为机场、公交车站和火车站

尼克·奥瓦尔在 Unsplash 上拍摄的照片

:本文是关于交通 POI 数据分类系列文章的第三篇。 第一篇文章 试图使用各种机器学习模型将记录分类为机场、公交车站和火车站。 第二篇文章 的中心思想是使用特征约简算法来调整第一篇文章中的模型,以提供更高的精度。查看这些文章,了解该项目在寻找 POI 数据分类的最佳方法方面是如何发展的。

本文将使用 Spark MLlib 包——特别是多层感知器分类器,使用步行交通模式对来自 SafeGraph 的 POI 记录进行正确分类。SafeGraph 是一家数据提供商,为数百家企业和类别提供 POI 数据。它向学术界免费提供数据。对于这个项目,我选择使用 SafeGraph 模式数据将记录分类为不同的兴趣点。模式数据的模式可以在这里找到:模式信息

Spark 简介

Apache Spark 是一个开源框架,旨在处理大数据。该框架之所以出名,是因为它能够通过使用并行(或分布式)计算来大幅提高性能并减少运行时间。

为什么要火花 MLlib?

  1. 支持 Statsmodels 和 Scikit-Learn 等软件包支持的许多流行的机器学习算法
  2. 允许构建复杂的机器学习管道
  3. 允许保存和重用算法和 ML 管道
  4. 支持分布式计算技术,通过将任务分成块并将其分配给多个节点并行执行,极大地减少了运行时间并提高了所有算法的性能

Spark MLlib 确实是一个非常有趣和简单易懂的包,可以在很短的时间内提供一些非常有趣的见解,在运行时和性能方面提供了最好的东西。

火花深度学习

MLlib 包很少容纳深度学习。从 Spark 3.0 开始,该包不支持许多深度学习模型,而是更专注于回归、分类和聚类的概念。这个缺点的一个例外是多层感知器分类器。

为什么是多层感知器分类器?

MLlib 包中的每个分类模型都有其优点和缺点,不同的数据可能需要使用不同的模型来获得最大的效率。MLPC 优于 Spark MLlib 包中提供的其他监督分类算法,因为它能够自行查找要素之间的相关性。此外,分类器适用于多类分类,并且不需要像传统算法(如 Spark 接口中的 SVM 算法)那样多的预处理和管道创建。最后,对于足够大和足够多样化的数据集,MLPC 算法可以比大多数分类器表现得更好。

有了这个介绍,让我们看看火花 MLPC 分类器是否能比本系列第 1 部分中的分类器表现得更好

由胡安乔·科雷拉在 Unsplash 上拍摄的照片

在我们开始深度学习之前,我们必须首先加载数据。这个特殊的步骤在本系列的第一篇文章和第二篇文章中都有涉及。满足本文需求的数据加载和预处理的基本步骤是:

在我们进入特征缩减概念的第一步之前,我们必须首先加载我们将用于这个项目的数据:加载数据的过程可以在笔记本中找到,并且已经在系列的第一部分中详细解释。就我们的目的而言,所需要的只是对所采取的步骤和结果数据框架的简要概述:

  1. 删除不必要的列- ['parent_safegraph_place_id ',' placekey ',' safegraph_place_id ',' parent_placekey ',' parent_placekey ',' safegraph_brand_ids ',' brands ',' poi_cbg']
  2. 创建地面实况列,将每个记录建立为机场、汽车站、机场或未知
  3. 删除未知记录以清除无法识别的记录
  4. 使用 pyspark 水平分解 JSON 字符串的列
  5. 阵列的水平分解列
  6. 使用 Sklearn LabelEncoder 包转换类列

作为这些转换的结果,输出的数据如下所示,并具有以下各列:

Raw_visit_counts: 在日期范围内,我们的小组中对此兴趣点的访问次数。

Raw_visitor_counts: 在日期范围内从我们的面板访问该兴趣点的独立访问者的数量。

Distance_from_home: 访客(我们已确定其住所的访客)离家的中间距离,以米为单位。

中值 _ 停留:中值最小停留时间,以分钟为单位。

分时段停留(分解为< 5,5–10,11–20,21–60,61–120,121–240):键是分钟的范围,值是在该持续时间内的访问次数

Popularity_by_day(分解到周一至周日):一周中的某一天到日期范围内每天(当地时间)的访问次数的映射

Popularity_by_hour(分解为 Popularity _ 1-Popularity _ 24):一天中的某个小时到当地时间日期范围内每小时的访问次数的映射。数组中的第一个元素对应于从午夜到凌晨 1 点的时间

Device_type(分解为 ios 和 Android): 使用 Android 和 IOS 的 POI 的访客数量。仅显示至少包含 2 个设备的设备类型,包含少于 5 个设备的任何类别都报告为

照片由阿鲁迪巴 S 在 Unsplash

POI 记录的深度学习分类

现在数据已经准备好了,我们可以开始深度学习方面了。

Spark MLlib 包执行 ML 函数的方式与用 Sci-kit Learn 等包编写的传统代码片段略有不同。该软件包要求使用矢量汇编器转换器将所有用于预测的特征编译成一列单独的矢量。这要求事先将标注列与要素列分开,这一步预处理的代码如下所示:

步骤 1:将标签列与特征列分离,并将熊猫 DF 转换为火花 DF

from pyspark.ml.classification import MultilayerPerceptronClassifierfrom pyspark.ml.evaluation import MulticlassClassificationEvaluatorlabel = transportation_df[‘Class’]transportation_df = spark.createDataFrame(transportation_df)transportation_df.show(5)

第二步:使用矢量汇编程序创建特征列

from pyspark.ml.feature import VectorAssemblertransportation_df = transportation_df.drop(‘Class’)assembler = VectorAssembler().setInputCols(transportation_df.columns[1:]).setOutputCol(“features”)transportation_df = assembler.transform(transportation_df)transportation_df.show(10)

这样做的最终结果应该是将所有特性都编译到一个列中,如下所示:

从这里开始,下一步是将这些数据与标签列结合起来。因为不再需要其他列(它们已经被编译到 features 列中),所以运行我们的模型只需要 features 和 label 列

transportation_df = transportation_df.toPandas()features = transportation_df[‘features’]features_df = pd.DataFrame(data = {‘features’: features})features_df = features_df.loc[~features_df.index.duplicated(), :]label_df = pd.DataFrame(data = {‘label’: label})label_df = label_df.loc[~label_df.index.duplicated(), :]transportation_df = pd.concat([features_df, label_df], axis= 1).dropna()transportation_df.head()

下一步是将数据分成训练和测试数据:

transportation_df = spark.createDataFrame(transportation_df)trainSet, testSet = transportation_df.randomSplit([0.8, 0.2])

现在是时候创建分类器模型了。该模型有许多我们需要了解和调整的超参数,以便最大限度地提高模型性能:

Tol: 该参数指的是收敛容差或权重开始向预测答案收敛的值。该参数的默认值是. 000001。该参数的较小值可能会导致过度拟合,而较大值可能会导致更精确的结果

种子:模型随机生成值的随机种子

Blocksize: 该参数是指模型每次迭代将包含的记录数。该参数的默认值是 128。较大的块大小将导致过度拟合,而较小的块大小将以运行时间为代价提供更精确的结果

步长:该参数是指模型的学习速率。该参数的默认值是. 03。较小的步长将以较大的运行时间为代价得到更精确的模型,而较大的步长将导致过拟合

Layers: 也许是所有超参数中最重要的,这个参数指的是这个模型中将要出现的层数和每层的节点数。第一个图层必须始终是数据中存在的要素的数量,最后一个图层必须始终是可用的输出标注的数量。在这个图层阵列中必须至少有一个隐藏图层。

考虑到这些需求,让我们看看 MLPC 模型的代码:

layers = [44,50,50,3]mlpc = MultilayerPerceptronClassifier(layers = layers, solver=’gd’, tol=.0000001, stepSize=.00001, blockSize= 30).setLabelCol(“label”).setFeaturesCol(“features”).setSeed(20).setMaxIter(500)model = mlpc.fit(trainSet)

这里,我们制作了一个具有两个隐藏层的神经网络,每个隐藏层有 50 个节点。该模型使用梯度下降来训练,并且具有非常高的收敛容限和步长。让我们看看结果:

result = model.transform(testSet)result.show(10)

from pyspark.ml.evaluation import MulticlassClassificationEvaluatorevaluator = MulticlassClassificationEvaluator(labelCol = ‘label’, predictionCol = ‘prediction’, metricName = ‘accuracy’)mlpacc = evaluator.evaluate(result)mlpacc

整体精度…不令人满意,但这是我们必须用这些数据运行的许多实验中的第一个,以便找到最有效的层数和节点数来提供更好的结果。下表列出了尝试的图层和精度。如果您想尝试这段代码,请不要犹豫,查看笔记本并亲自运行它。

这项实验的直接收获是:

  1. 更多的隐藏层不一定意味着更好的准确性
  2. 更深的隐藏层不一定意味着更好的准确性

从上表中我们可以看出,当模型保持简单而不是复杂时,它的性能最好。尽管如此,我们看到这个特定数据集的准确性仅仅超过了来自系列第一部分的高斯朴素贝叶斯模型。为什么会这样呢?好吧,简单的答案就是数据的不平衡。从 SafeGraph 中提取该数据时,使用了三个 NAICS 码来检索数据,输出的数据严重不平衡,机场记录的数量几乎是公交车站记录数量的 4 倍。这种不平衡已经成为贯穿本系列的一个问题,并对我们根据数据训练的许多模型的结果产生了不利影响。为了纠正这种不平衡,对不同类别的数据进行了随机抽样,但这样做会导致机场和火车站的数据代表性不足,从而仍然损害了模型的准确性。

结论

不管结果如何,这篇文章通过 MLPC 模型介绍了 Spark 深度学习的概念。本文展示了 Spark 为创建深度学习模型带来的便利,并进一步展示了在真实世界数据集(如 SafeGraph 模式数据)上使用这样一个复杂模型的优点和缺点。

在本系列的下一篇也是最后一篇文章中,我们将研究集成分类在这种模式数据上的应用,并看到使用多层分类器有助于提供最佳结果。

提问?

我邀请你在 SafeGraph 社区的 #safegraphdata 频道问他们,这是一个面向数据爱好者的免费 Slack 社区。获得支持、共享您的工作或与 GIS 社区中的其他人联系。通过 SafeGraph 社区,学者们可以免费访问美国、英国和加拿大 700 多万家企业的数据。

Spark SQL 102 —聚合和窗口函数

原文:https://towardsdatascience.com/spark-sql-102-aggregations-and-window-functions-9f829eaa7549?source=collection_archive---------5-----------------------

Spark 中的分析函数,适合初学者。

波格丹一世·卡伦科在 Unsplash 上的照片

数据聚合是许多数据分析中的重要步骤。这是一种如何减少数据集并计算各种指标、统计数据和其他特征的方法。一个相关但稍微高级一些的主题是窗口函数,它允许基于具有所谓框架的窗口对数据计算其他分析和排序函数。

这是最近的文章的延续,在文章中我们描述了什么是数据帧以及 Spark SQL 中的转换一般是如何工作的。在这里,我们将深入研究聚合和窗口函数,它们是两组特定的转换,它们密切相关,但正如我们将看到的,它们之间有一个重要的区别,这是很好理解的。

对于代码,我们将使用 PySpark API,这是撰写本文时的最新版本 3 . 1 . 2(2021 年 6 月)。

整个数据帧的聚合

让我们从最简单的聚合开始,这是一种将整个数据集简化为一个数字的计算。这可能类似于数据帧中的总行数或某个特定列中值的总和/平均值。为此,我们可以直接在数据帧上使用 agg() 函数,并以逗号分隔的方式将聚合函数作为参数传递:

from pyspark.sql.functions import count, sumdf.agg(count('*'))df.agg(count('*'), sum('price'))df.agg(count('*').alias('number_of_rows'),sum('price').alias('total_price')
)

请注意,第一个示例的输出是一个具有单行和单列的数据帧,它只是一个由数据帧表示的数字。在第二个示例中,输出是一个具有单行和两列的数据帧,每个聚合函数对应一列。在最后一个示例中,我们可以看到每个聚合也可以使用 alias() 函数进行重命名。

基于组的聚合

通常,我们需要计算聚合,不是针对整个数据帧,而是针对每组行单独计算,其中组被定义为在特定列中具有相同值的行。例如,假设一个信用卡交易数据集,其中每一行都是唯一的交易,但不同的行可能属于同一个用户(持卡人)。这里,为每个用户单独计算聚合可能是有用的,对于这种聚合,我们可以使用 groupBy 转换:

(df.groupBy('user_id').agg(count('*').alias('number_of_transactions'))
)

同样,我们在这里使用的是 agg 函数,我们可以传入任何聚合函数,例如 countcountDistinctsumavg / meanminmaxfirstlastcollect_list注意,如果我们不重命名聚合的结果,它将有一个默认名称,在 count 函数的情况下是 count(1)

或者,我们可以直接在 groupBy 之后调用聚合函数,如下所示

(df.groupBy('user_id').count()
)

注意,使用这种语法的缺点是不能使用别名()直接重命名聚合的结果,因为这里 count 函数返回一个数据帧,所以别名将应用于整个数据帧。因此重命名必须由另一个转换来处理,比如带有 ColumnRenamed('count ',' new_name') 。同样,这里你一次只能调用一个聚集函数,而带有 agg 的语法允许你同时调用任意多个函数。其他在分组后被调用的功能参见文档。

这些 groupBy 转换的一个重要属性是,输出数据帧将只包含在 groupBy() 中指定为参数的列和聚合结果。所以如果我们调用 df.groupBy('user_id ')。count() ,无论 df 有多少字段,输出都只有两列,分别是 user_idcount 。此外,输出数据帧所代表的行数将更少,或者在边际情况下,与原始 df 中相同。边际情况对应于分组列的所有值都不同的情况,因此每个组只有一行。正如我们将看到的,这将与窗口函数不同。

基于带框架的窗口的聚合

窗口函数是一组函数,也可以在一组行上调用,就像我们在前面的例子中看到的那样。然而,还是有一些不同之处。首先,在调用窗口函数之后,数据集不会被缩减-所有行和所有列都将在输出数据帧中,并且计算将被添加到新列中。将应用该函数的行组再次由特定列(或列的列表)给出,对于该特定列,行具有相同的值,并且该组被称为窗口。此外,窗口函数更加灵活,因为有时您不想将函数应用于整个窗口,而是只应用于窗口中行的子集,即所谓的帧。最后,窗口也可以排序,因为一些功能(所谓的排序功能)需要它。让我们看看窗口函数的语法:

from pyspark.sql import Windoww = Window().partitionBy('user_id')df.withColumn('number_of_transactions', count('*').over(w))

正如你所看到的,我们首先使用函数partition by()定义窗口——这类似于 groupBy() ,所有在指定列中具有相同值的行(这里是 user_id )将形成一个窗口。然后我们向数据帧添加一个新列,在这里我们调用指定窗口的窗口函数。

排名功能

这是一组特定的窗口函数,需要对窗口进行排序。作为一个具体的例子,考虑函数 row_number() ,它告诉您窗口中的行数:

from pyspark.sql.functions import row_numberw = Window.partitionBy('user_id').orderBy('transaction_date')df.withColumn('r', row_number().over(w))

其他排名功能例如有 排名 ()密集 _ 排名 ()

指定框架

如上所述,一些功能可以应用于窗口中的行的子集。一个典型的用例是计算值的累积和,其中帧将指定我们希望从窗口的开始直到当前行应用该函数。此外,很明显,帧中各行的顺序很重要,因为如果各行的顺序不同,累积和将具有不同的形状。可以使用以下两个函数之一来指定帧:

  • https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.Window.rowsBetween.html#pyspark.sql.Window.rowsBetween
  • rangeBetween()

这两个函数都有两个参数:帧的开始和结束,它们可以指定如下:

  • window . unbounded 前进,window . unbounded 跟随-从头到尾的整个窗口
  • Window.unboundedPreceding,Window.currentRow 从窗口的开头到当前行,这用于累计和
  • 使用数值,例如,0 表示当前值,但是其他值的含义可以根据帧功能行之间* / 范围之间而不同。*

为了理解和之间的行和之间的范围之间的区别,让我们看看下面的例子,其中我们有三列, user_idactivityday ,我们想对每个用户的活动求和:**

df.withColumn('activity_sum', sum('activity').over(w))

我们将按对窗口进行排序,并使用间隔(-1,0)来指定帧。我们将会看到,这两个函数的区间有不同的含义。在图像的右侧,您可以看到每种情况下的求和结果(为了简单起见,我们在图像中只为 id 为 100 的用户显示一个窗口):

作者图片

rowsBetween 的情况下,在每一行上,我们对当前行和前一行(如果存在)的活动求和,这就是 interval (-1,0)的含义。另一方面,在 rangeBetween 的情况下,在每一行上,我们首先需要通过从 day 列中的值减去值 1 来计算将要求和的行的范围。例如,在第 3 行上,我们有 7–1 = 6 导致间隔(6,7),我们应该合计所有符合此间隔的列中的值的行,在我们的例子中,它只是当前行,因为没有 =6 的行。

如您所见,在 rangeBetween 的情况下,我们排序所依据的列需要是某种数字类型,因为 Spark 需要对该列中的值进行一些运算。在之间的行中不存在这种限制。

要提及其他一些窗口功能,请参见例如:

  • 引()
  • 【滞后()】
  • ntile()
  • 【第 n _ value()】
  • 【cume _ dist()

值得知道的重要事情

  • 对窗口进行排序会改变帧— 这可能不直观 —对经过排序的窗口计算 sum 会导致与未经排序的窗口相比不同的结果。更多细节,请查看我的另一篇文章,我在那里展示了一个例子。
  • 这两种转换, groupBywindow 都需要特定的分区,如果分区不存在,它们将导致洗牌——数据需要以这样的方式重新组织,即一个组/窗口中的所有行都需要放在一个分区中。然而,不同之处在于,使用 groupBy ,Spark 将首先部分聚合数据,然后混洗缩减的数据集,而窗口将混洗整个数据集。
  • 如果我们不向 partitionBy 函数传递任何参数,并将窗口指定为 w = Window()。partitionBy() ,整个数据集将变成一个大窗口,所有数据将被混洗到单个分区,这可能会导致性能问题,因为所有数据都将放在集群中的单个节点上。

结论

在本文中,我们讨论了聚合和窗口函数,它们是非常常用的转换,尤其是在数据分析师中。我们讨论了调用 groupByWindow.partitionBy 的区别,我们已经看到了如何在窗口上指定框架的不同选项。

使用 Python 的 Spark 流

原文:https://towardsdatascience.com/spark-streaming-with-python-5144cfc8b883?source=collection_archive---------23-----------------------

Pyspark 流管道入门指南

照片由 JJ 英在 Unsplash

Apache Spark 流相当受欢迎。由于其集成技术,Spark Streaming 在数据流质量和综合方法方面优于以前的系统。

Python 和 Spark 流在一起使用时会给行业巨头带来奇迹。网飞是一个优秀的 Python/Spark 流媒体代表:这个流行的流媒体平台背后的人们已经发表了多篇文章,讲述他们如何使用该技术来帮助我们更好地享受网飞。让我们从基础开始。

什么是 spark 流,它是如何工作的?

Spark 平台包含各种模块,包括 Spark 流。Spark 流是一种分析“无界”信息的方法,有时也称为“流”信息。这是通过将其划分为微批处理并允许在多个批处理上执行窗口来实现的。

Spark 流接口是一个 Spark API 应用程序模块。Python,Scala,Java 都支持。它允许您以容错和灵活的方式处理真实的数据流。Spark 引擎获取数据批次,并批量生成最终结果流。

什么是流式数据管道?

这是一种允许数据从一个位置平滑且自动地移动到另一个位置的技术。这项技术消除了该公司的许多典型问题,如信息泄漏、瓶颈、多个数据冲突和重复创建条目。

流数据管道是可伸缩地实时处理数千个输入的数据管道架构。结果是,您将能够收集、分析和保留大量数据。该功能支持实时应用、监控和报告。

图片由agency followebonUnsplash

Spark 的流架构。

作者提供的 Spark 流架构图

Spark 流的主要结构是逐批离散时间流。微批处理不断地被分配和分析,而不是一次一个项目地通过流处理管道。因此,数据根据可访问的资源和位置分发给员工。

当数据被接收时,它被接收机分成 RDD 部分。因为 rdd 确实是 Spark 数据集的一个关键抽象,所以转换成 rdd 可以使用 Spark 脚本和工具进行分组分析。

现实生活中的火花流示例(Twitter Pyspark 流)

在这个解决方案中,我将构建一个流管道,从互联网上获取特定关键字(Ether)的推文,并对这些实时推文执行转换,以获取与其相关的其他热门关键字。

来自作者的真实生活火花流示例架构异想天开的图

视频教程

从 Anuj Syal 开始,不到 12 分钟就有 Python 的火花流

第一步:使用 tweepy 传输推文

import tweepy
from tweepy import OAuthHandler
from tweepy import Stream
from tweepy.streaming import StreamListener
import socket
import json# Set up your credentials
consumer_key=''
consumer_secret=''
access_token =''
access_secret=''class TweetsListener(StreamListener):def __init__(self, csocket):self.client_socket = csocketdef on_data(self, data):try:msg = json.loads( data )print( msg['text'].encode('utf-8') )self.client_socket.send( msg['text'].encode('utf-8') )return Trueexcept BaseException as e:print("Error on_data: %s" % str(e))return Truedef on_error(self, status):print(status)return Truedef sendData(c_socket):auth = OAuthHandler(consumer_key, consumer_secret)auth.set_access_token(access_token, access_secret)twitter_stream = Stream(auth, TweetsListener(c_socket))twitter_stream.filter(track=['ether'])if __name__ == "__main__":s = socket.socket()         # Create a socket objecthost = "127.0.0.1"     # Get local machine nameport = 5554                 # Reserve a port for your service.s.bind((host, port))        # Bind to the portprint("Listening on port: %s" % str(port))s.listen(5)                 # Now wait for client connection.c, addr = s.accept()        # Establish connection with client.print( "Received request from: " + str( addr ) )sendData( c )

步骤 2:编码 PySpark 流管道

# May cause deprecation warnings, safe to ignore, they aren't errors
from pyspark import SparkContext
from pyspark.streaming import StreamingContext
from pyspark.sql import SQLContext
from pyspark.sql.functions import desc
# Can only run this once. restart your kernel for any errors.
sc = SparkContext()ssc = StreamingContext(sc, 10 )
sqlContext = SQLContext(sc)
socket_stream = ssc.socketTextStream("127.0.0.1", 5554)
lines = socket_stream.window( 20 )
from collections import namedtuple
fields = ("tag", "count" )
Tweet = namedtuple( 'Tweet', fields )
# Use Parenthesis for multiple lines or use \.
( lines.flatMap( lambda text: text.split( " " ) ) #Splits to a list.filter( lambda word: word.lower().startswith("#") ) # Checks for hashtag calls.map( lambda word: ( word.lower(), 1 ) ) # Lower cases the word.reduceByKey( lambda a, b: a + b ) # Reduces.map( lambda rec: Tweet( rec[0], rec[1] ) ) # Stores in a Tweet Object.foreachRDD( lambda rdd: rdd.toDF().sort( desc("count") ) # Sorts Them in a DF.limit(10).registerTempTable("tweets") ) ) # Registers to a table.

步骤 3:运行 Spark 流管道

  • 打开“终端”并运行 TweetsListener,开始播放推文

python TweetsListener.py

  • 在 Jupyter notebook 启动 spark 流上下文中,这将让传入的 tweets 流进入 spark 流管道,并执行步骤 2 中所述的转换

ssc.start()

步骤 4:查看实时输出

从 spark tweets中注册的临时表在图表/仪表板上绘制实时信息。该表将每 3 秒更新一次推文分析

import time
from IPython import display
import matplotlib.pyplot as plt
import seaborn as sns
# Only works for Jupyter Notebooks!
%matplotlib inline count = 0
while count < 10:time.sleep( 3 )top_10_tweets = sqlContext.sql( 'Select tag, count from tweets' )top_10_df = top_10_tweets.toPandas()display.clear_output(wait=True)plt.figure( figsize = ( 10, 8 ) )
#     sns.barplot(x='count',y='land_cover_specific', data=df, palette='Spectral')sns.barplot( x="count", y="tag", data=top_10_df)plt.show()count = count + 1

出局:

作者输出的屏幕截图

火花流的一些优点和缺点

既然我们已经完成了构建 spark 流管道的实际解决方案,让我们列出使用这种方法的一些优点和缺点。

优点

  • 对于困难的工作,它提供了非凡的速度。
  • 对故障的敏感性。
  • 在云平台上,执行起来很简单。
  • 支持多种语言。
  • 与主要框架的集成。
  • 连接各种类型数据库的能力。

缺点

  • 需要大量的存储空间。
  • 很难使用、调试和掌握。
  • 缺少文档和指导资源。
  • 数据的可视化并不令人满意。
  • 处理少量数据时反应迟钝
  • 只有少数机器学习技术。

结论

Spark Streaming 确实是一种收集和分析大量数据的技术。在不久的将来,流数据可能会变得更加流行,所以您应该现在就开始了解它。请记住,数据科学不仅仅是构建模型;它还需要管理一个完整的管道。

本文讨论了 Spark 流的基础知识,以及如何在真实数据集上使用它。我们建议您使用另一个样本或实时数据,将我们学到的一切付诸实践。

最初发表于【https://anujsyal.com】

借助 SAP HANA 云实现空间匿名化

原文:https://towardsdatascience.com/spatial-anonymization-with-sap-hana-cloud-1f3582ec0d1e?source=collection_archive---------32-----------------------

使用 k-匿名和地理散列来匿名化个人位置数据

如何隐藏个人数据,同时保留有价值的见解。【图片由 盖拉格斯奇

在处理个人位置数据时,您很快会遇到关于数据隐私和匿名化的问题。由于全球疫情和许多不同的方法进行接触追踪,这个话题在去年得到了相当多的媒体报道。幸运的是,对于新冠肺炎接触者追踪,已经有了不使用任何位置数据的工作机制。例如,德国的 Corona-Warn-App 利用了蓝牙令牌的交换。

但是,在许多情况下,您可能希望利用公司中现有的位置数据。由于隐私问题、用户选择退出或法规,例如GDPR,您通常无法处理这些数据、利用其价值并获得进一步的见解。一个例子可能是有针对性的营销活动,在这种活动中,确定个人的确切位置并不太重要,但了解活动的大致“区域”却很有帮助。现在的技术问题是,如何恰当地将一个确切的位置推广到一个区域,并在匿名化方面保持一定的保证?

您可能已经知道,SAP HANA 支持空间数据类型以及数据匿名化算法。在这篇博客中,我不会深入讨论这两者的细节——至少不会超过必要的程度。已经有很多博客描述了背后的概念(点击链接获取更多信息)。相反,我将重点介绍如何将空间处理与匿名化概念相结合,并向您展示如何在 SAP HANA 中实现空间匿名化。

我将通过一个例子来解释这种方法。这里的问题是,任何例子都涉及数据集。这个 per 定义是一个隐私敏感的数据集,否则匿名化没有多大意义。这是因为这些数据集很难在网上找到(…至少是那些我会用在博客条目上的数据集)。因此,我给你们举的例子,可能看起来有点牵强。

那是因为它确实是。

数据集

我将使用来自科学出版物【1】的数据集。数据集包括纽约和东京大约 10 个月的 Foursquare 签到记录。我现在将使用纽约部分。除了签入数据之外,还有一个数据集,其中包含进行签入的一些用户的一些详细信息。

我们将使用的数据字段如下:

  • RECORD _ ID【INTEGER】
    一个 ID,我在导入时创建的
  • USER_ID 【整数】
    链接到用户资料
  • loc 4326【ST _ GEOMETRY(4326)】
    检入位置
  • UTC_TIME 【时间戳】
    签到时间

然后是第二个数据集,包含一些用户简档的一些数据:

  • 用户标识【整数】
    用户的唯一标识
  • 性别【NVARCHAR(6)】
    男/女
  • TWITTER_FRIENDS 【整数】
    TWITTER 好友数量
  • TWITTER_FOLLOWER 【整数】
    TWITTER 关注者的数量

如你所见,这个数据集已经看起来相当匿名。所以我们来构造一个狂野的故事。

虚构的故事

假设我可以访问这个数据集,也许我是一名在公司工作的数据科学家。我们还假设用户档案数据更丰富一些,实际上至少包含了个人的 Twitter 账号。当然,我是邪恶的——这是每个好故事需要的盐。

在餐厅里,我旁边的桌子上,有人在谈论一些非常好吃的饼干,那人在家里。我可以看到这个人正在 Foursquare 上签到。

描绘这一幕。

我真的需要得到那个人的 Twitter 账号来发一条推文,我知道有人会喜欢尝试这些饼干。我知道 时间报到地点* 和那个人。因为我可以访问签到数据库,所以我可以很容易地过滤出相应的签到并检索那个人的个人资料!邪恶的笑声*

解决方案

声音构造?也许吧。然而,重新识别是一个问题,并已在现实生活中表现出来——如已经在 2006 年与搜索记录:https://en.wikipedia.org/wiki/AOL_search_data_leak

那么,数据集的所有者可以做些什么来防止这种攻击,同时仍然保持数据本身的价值呢?在现实世界中,您可以认为我是数据科学家,所有者是数据库管理员。管理员喜欢限制所有的数据访问,而数据科学家只想访问几乎所有的数据。提供一个既能安全使用又能保持洞察力和相关性的数据集不是很好吗?

实现这一点的一个方法是概括数据。因此,我们不显示具体的位置,而是提供一个粗略的区域,不显示具体的时间戳,而是提供一个时间跨度。SAP HANA 有一个内置的匿名化算法,称为k-anonymous,它可以自动概括数据,同时在匿名化方面提供一定的保证。

最简单的方法是用我们的例子来解释这一点:当我使用参数 k=3 应用 k-anonymous,并对结果集应用相同的时间、位置和性别查询时,我保证检索到至少 3 个签到记录。

虽然有大量的材料,解释这如何在 SAP HANA 上使用数字或分类数据,但我今天将向您展示,如何将位置和时间维度添加到算法中。

空间等级

这才是问题的关键!为了使用数据库字段进行匿名化,您需要定义数据的层次结构。对于空间数据,当然有一些明显的层次结构。对于 SAP 总部的位置,可能的层级是:

确切地点>沃尔多夫(城市)>巴登-符腾堡州>德国(国家)

这种等级制度(至少)有两个缺点:

  • 它不是数据驱动的,您必须手动将所有层次结构建模为数据的一部分
  • 它是静态的,不是精细的颗粒。概括的每一步都会导致大量信息的丢失。

一种数据驱动的定义位置数据层次的方法是使用所谓的离散全球网格系统(DGGS) 或更简单的地理哈希。

幸运而非巧合的是,SAP HANA 为 HANA2 SPS05 和 HANA 云提供了地理哈希的数据库内处理。您可以从几何图形生成几何图形,并将它们转换回点或几何图形。

让我们为 SAP 总部生成一个地理哈希:

得到的字符串是:u 0 y0 ktsgn 98 z 2 pkqr5tt**

这就是地理哈希。一个长度为 20 的字符串,可以还原为一个点,该点与原点的位置大致相同。例如,这种格式可用于以字符串表示形式交换位置信息,并且有助于 API 编程。

但是我们如何使用它来定义我们位置的层次结构呢?Geohashes 拥有令人难以置信的价值。如果您截断它们并在末尾留下一些字符,位置只会变得更加不精确。

我们以前 5 个字符为例来看: u0y0k 定义了一个矩形。是另一个矩形,它保证包含第一个矩形。这样,地理哈希为我们提供了一个纯粹的数据驱动的位置层次结构。

以我们的例子为例:

u 0 y0 ktsgn 98 z 2 PK qr5 TT>…>u 0 y0 ktsgn 98 z>…>u 0 y0 kts>…>u 0>u

地理哈希定义的 SAP 总部的空间层级

实际实施

那好吧。现在我们只需要把我们的拼图拼在一起,创建层次结构,并把它们交给我们的 k-匿名算法。

首先,让我们通过将签到与用户配置文件连接起来来构建我们的基本数据集。我们创建了一个视图,可以用来匿名。

现在,我们知道了地理哈希的魔力,空间层次变得非常简单。我们需要创建一个存储函数,它接收一个值(即 Geohash)和一个级别参数,并根据概化级别返回概化的 Geohash。

接下来,我们需要注意的是,概括时间维度。我的天真方法是建立以下层次结构:

yyyy-mm-DD hh:mm:ss>yyyy-mm-DD hh:mm:00>……>yyyy-mm-DD 00:00:00>..>yyyy-01–01 00:00:00

所以,每一步,我们都在切割最精细的时间单位。从秒到分钟,到小时,等等。由于 SAP HANA 中实现了 TO_TIMESTAMP 函数,该函数也接受部分时间字符串,我们可以通过以下方式实现。

最后,我们可以创建视图,也就是进行实际的匿名化。对于视图中的每个字段,我们需要确定层次结构,作为参数用于匿名化。对于时间和位置,我们使用上面定义的函数,对于性别,我们只是硬编码嵌入的层次结构(本质上这意味着要么显示性别,要么不显示性别)。

为了使用这个视图,我们需要用一个所谓的 REFRESH 语句来初始化它。

事情就是这样。我们已经为进行空间匿名化准备了一个视图!

我们可以检查视图的输出,并观察匿名数据:

该算法保留了性别信息,但决定只提供月份信息,而不是确切的时间戳(注意,日期设置为 01)和 6 字符的 Geohash。右侧的地图预览可让您了解空间概化的程度。

匿名签到数据

使用匿名数据

让我们回到我们虚构的故事:我在 UTC 时间 2012-04-14 06:35:00 左右在那家日本餐馆 ( lat: 40.7294418,lon: -73.989344 )见过一名男性。

有了原始数据,很容易查找。我的 GPS 和手表都不精确——但我是一名数据科学家,可以给我的查询增加一些不确定性。在本例中,我正在查询一个 10 分钟的时间窗口和我所在位置周围 100 米的区域。

这就是了。我确实只有一条结果记录。USER_ID = 54 是配置文件,我需要检查进一步的个人信息。

查询非匿名数据得到 1 个精确匹配

现在,我的管理员意识到了误用,并把原始数据集与我们用 k-anonymous 匿名的数据集进行了交换。由于我们设置了参数 k=3 ,我们希望通过类似的查询至少获得 3 条记录。因为我们现在处理的是(每月)的范围和地区,所以查询必须稍作调整。

这个查询的结果看起来非常不同。这次,我收到了 62 张唱片!

使用相同参数查询匿名数据

您还可以看到,该区域现在覆盖了东村,而不仅仅是某个位置。请注意, user_id = 54 的用户的签入仍然是数据的一部分。它还没有被删除。然而,那个邪恶的我一直在寻找的登记,现在藏在东村的 62 个男性登记中。

然而,数据科学家仍然可以使用这些匿名数据来生成有价值的见解,例如网站访问者的时间和位置的概况。数据的价值没有丢失。

摘要

数据管理层的匿名化和数据隐私是真实的,尊重隐私并不一定会导致信息的重大损失。通过明智的方法,可以保持数据的价值,同时将数据与个人信息分离。

SAP HANA 的独特价值在于其多型号引擎的组合。无论是图形或空间处理、数据匿名化还是机器学习,借助可互操作的引擎,您可以在安全可靠的环境中从数据中获得最大价值。

您在上面看到的不是一步一步的描述,我希望您在使用您的 SAP HANA 云试用实例复制它时会遇到这样或那样的小问题,其中空间处理和数据匿名化都在中。大多数问题都是可以解决的——如果不是这样,请在下面的评论区提问。无论如何,我强烈建议你用自己的数据进行一次试驾!

综上所述,我们只创建了 2 个视图和 2 个函数:匿名化不必很复杂!今天就开始用吧。

【丁琪】 、张大庆、郑文卿、。LBSNs 中利用用户时空特征的用户活动偏好建模。IEEE Trans。论系统、人和控制论:系统,(TSMC),45(1),129–142,2015。【PDF】

原载于 2021 年 3 月 5 日【https://blogs.sap.com】**

使用 Google BigQuery 的空间宁滨

原文:https://towardsdatascience.com/spatial-binning-with-google-bigquery-d118afba6273?source=collection_archive---------23-----------------------

用 Google BigQuery 将宁滨地理坐标转换成方块

数据宁滨在数据科学和数据分析中是一种有用的常见实践,从几个方面来看:机器学习中连续变量的离散化或简单地制作直方图以便于可视化是两个常见的例子。通常,值被分组到相同大小的箱中,例如,分别用于一维和二维变量的恒定长度的间隔或恒定面积的矩形,但是只要每个值落入一个且仅一个箱中,我们就可以完全自由地定义它们。宁滨的一个小例子可以通过截断一个十进制数来实现,只保留它的整数部分或任意数量的十进制数字,这取决于所需间隔的大小。

盖尔·加博雷尔在 Unsplash 上的照片

我们希望在这里关注连续变量的特殊情况,即与地理参考数据相关的经度和纬度。在一般情况下,我们几乎有无限种方法来定义覆盖地球表面的二维非重叠面元:一个直接的选择可以是使用任何现有的行政区划,例如州、省、市和区。这里唯一的限制是访问这样的地理数据,幸运的是,这些数据通常是公开的:BigQuery 允许我们访问几个地理公共数据集,如 geo us boundaries 数据集,其中包含代表美国边界的多边形表。

几何定义的箱

这种解决方案并不总是令人满意的,因为我们也希望像我们通常对其他连续变量所做的那样,根据数学函数在几何上定义面元。将一个给定的地理区域分割成几个在某种意义上规则的更小的多边形将会很棒。棘手的部分来了:地球表面是(据称😃)不是平面的,所以它的平面表示必然会造成一些形状或长度上的扭曲。人们很容易忘记我们通常用来定位一个点的坐标的角度性质,即经度和纬度,并将它们视为笛卡尔坐标。这将使我们能够很容易地定义地理箱,就像以前为二维变量的一般情况所建议的那样:我们可以截断经度和纬度,只保留给定数量的十进制数字。让我们展示一下我们会得到什么,使用 Google BigQuery 来制作多边形,使用 Google Geo Viz 来可视化它们(图 1):

with **starting_point** as(
select0 **sp_lat**,18 **sp_long**,1 **digits**
)
selectst_makepolygon(st_makeline(array[st_geogpoint(**pol_long**,**pol_lat**), st_geogpoint(**pol_long**,**pol_lat** + pow(10,-**digits**)),st_geogpoint(**pol_long** + pow(10,-**digits**),**pol_lat** + pow(10,-**digits**)),st_geogpoint(**pol_long** + pow(10,-**digits**),**pol_lat**) ])) **polygon**
from(select**digits**,trunc(**sp_long**,**digits**) + **x***pow(10,-**digits**) **pol_long**,trunc(**sp_lat**,**digits**) + **y***pow(10,-**digits**) **pol_lat**,from**starting_point**,unnest(generate_array(-10,10,1)) **x**,unnest(generate_array(-10,10,1)) **y**)

让我们简短地讨论一下这里提出的代码:在 starting_point 中,我们定义了围绕其构建网格的起点的坐标,以及截断后我们希望保留的十进制位数(这将清楚地确定每个多边形的大小)。然后,我们生成两个跨越区间[-10,10]的整数数组,以便在截断两个坐标后迭代地改变最后一个十进制数字。这将为我们提供多边形左下角的坐标,从这里可以直接得到其他三个。地理函数 st_makeline()ST _ make polygon()让我们最终创建包含多边形的地理。我们最终得到一个 21x21 的网格,如下所示(图 1):

图 1 —通过地理坐标的数值截断在赤道附近生成的网格。

看起来不错!
让我们换个起点:

*with **starting_point** as(
select59 **sp_lat**,18 **sp_long**,1 **digits**
)*

因此我们得到(图 2):

2 —通过地理坐标的数值截断在赤道上方生成的网格。

这个网格看起来和上一个有点不同。为什么?请记住,经度和纬度是角坐标,当在极点附近沿经线(恒定经度)移动 1°时,会导致在赤道附近测得的完全相同的位移,但这不适用于沿纬线(恒定纬度)的移动:当我们接近极点时,经度上 1°的变化逐渐变小,在极点处正好为 0°。当我们改变起点时,我们可以很容易地使自己相信这样一个测量多边形任意两个顶点之间距离的特征:

*st_length(st_makeline(st_geogpoint(**pol_long**,**pol_lat**),st_geogpoint(**pol_long**,**pol_lat** + pow(10,-**digits**)))) **length_constant_longitude**,st_length(st_makeline(st_geogpoint(**pol_long**,**pol_lat**) ,st_geogpoint(**pol_long** + pow(10,-**digits**),**pol_lat**))) **length_constant_latitude***

你应该注意到这两个长度之间的比率大约等于 1/cos(pol_lat),,这就是为什么在赤道附近多边形看起来像正方形!此外,考虑到这种影响,我们应该期待两个网格具有相同的高度,但不同的宽度,即在最后一个网格显示较短的基础。令人惊讶的是,如果我们直接比较地图上的两个形状,它们看起来完全相反!(图 3)

图 3 —前两个网格的直接比较。

它们宽度相同,但高度不同。这完全是由投影不可避免的扭曲造成的,该投影用于在平面上绘制地球的曲面。这就是为什么格陵兰看起来几乎和非洲一样大!

创建方形垃圾箱

刚刚提出的宁滨技术非常好,因为任何地理点都落在这个网格(一旦扩展到整个表面)的一个且仅一个多边形(从现在起我们将称之为 tile )中,但是它缺乏由完美排列的正方形组成的网格的令人满意的视觉规律性(至少远离赤道!).机器学习算法可能不会在意,在这种情况下,可能有更好的解决方案,其中长度或面积在整个网格中保持不变,但如果我们对整洁的视觉输出更感兴趣,那么这种解决方案更适合。按照上一个示例的结构,我们有:

 *with **starting_point** as( -- step 1
select0 **sp_lat**,18 **sp_long**,12 **zoom**
)
select**zoom**,**tile_x**,**tile_y**,st_makepolygon(st_makeline([st_geogpoint(**ne_long**,**ne_lat**),st_geogpoint(**se_long**,**se_lat**),st_geogpoint(**sw_long**,**sw_lat**),st_geogpoint(**nw_long**,**nw_lat**)])) **polygon**,
from( --step 3select**zoom**,**tile_x**,**tile_y**,180/acos(-1)*(2*acos(-1)***tile_x**/pow(2,**zoom**)-acos(-1)) **nw_long**,360/acos(-1)*(atan(exp(acos(-1)-2*acos(-1)/pow(2,**zoom**)***tile_y**))-acos(-1)/4) **nw_lat**,180/acos(-1)*(2*acos(-1)*(**tile_x**+1)/pow(2,**zoom**)-acos(-1)) **ne_long**,360/acos(-1)*(atan(exp(acos(-1)-2*acos(-1)/pow(2,**zoom**)***tile_y**))-acos(-1)/4) **ne_lat**,180/acos(-1)*(2*acos(-1)*(**tile_x**+1)/pow(2,**zoom**)-acos(-1)) **se_long**,360/acos(-1)*(atan(exp(acos(-1)-2*acos(-1)/pow(2,**zoom**)*(**tile_y**+1)))-acos(-1)/4) **se_lat**,180/acos(-1)*(2*acos(-1)*(**tile_x**)/pow(2,**zoom**)-acos(-1)) **sw_long**,360/acos(-1)*(atan(exp(acos(-1)-2*acos(-1)/pow(2,**zoom**)*(**tile_y**+1)))-acos(-1)/4) **sw_lat**        from( --step 2select**zoom**,trunc((**sp_long***acos(-1)/180+acos(-1))/(2*acos(-1))*pow(2,**zoom**)) + **x** **tile_x**,trunc(pow(2,**zoom**)/(2*acos(-1))*(acos(-1)-safe.ln(tan(acos(-1)/4 + (**sp_lat**/180*acos(-1))/2)))) + **y** **tile_y**,fromstarting_point,unnest(generate_array(-10,10,1)) **x**,unnest(generate_array(-10,10,1)) **y**))*

这看起来有点复杂和冗长,让我们讨论每一步:

  1. 和前面的例子一样,我们首先定义了生成网格中每个图块的起始点,然后通过 zoom 参数选择它们所需的大小。重要的是,缩放的最低级别是 0(即,只有一个图块覆盖整个地图),每次我们提升一个级别,图块的数量就会翻两番。这里我们任意选择 12 级。
  2. 我们从这两个整数数组中确定网格中每个方块的“ 平铺坐标 ”。这些坐标和缩放级别将一起形成每个区块的明确标识符。注意,我们使用 acos(-1) 来获得π的值。
  3. 接下来,我们应用反函数来获得每个正方形的四个顶点的坐标。
  4. 最后,我们使用常用的地理函数将切片生成为地理数据类型。请注意,只有当我们希望在地图上可视化网格时,这最后两个步骤才是必需的。

因此我们得到了这个漂亮的网格(图 4):

图 4 —由赤道附近生成的方形瓦片组成的网格。

如果我们改变起点呢?让我们看一看(图 5):

图 5 —由赤道上方很远的地方生成的方形瓦片组成的网格。

很明显,我们现在有两个相同的网格,由相同外观尺寸的规则正方形组成,无论它们在地图上的什么位置。我们想再次强调,它们只是看起来平等,而实际上并不平等。对他们面积的评估揭示了这个特征:赤道附近的瓦片平均面积约为 95.5 km,而更靠近北极的瓦片平均面积约为 25.3 km!

我们终于准备好使用这种技术在 Geo Viz 和 Data Studio 上创建许多很酷的可视化效果了!

尽情享受吧!😃

使用 scikit-learn 进行空间交叉验证

原文:https://towardsdatascience.com/spatial-cross-validation-using-scikit-learn-74cb8ffe0ab9?source=collection_archive---------6-----------------------

图片由 Mohit (@98mohitkumar)在 Unsplash 上提供

使用 scikit-learn 对具有空间自相关的数据集实施交叉验证

如果您想直接跳到代码,请跳到 scikit-learn 上的 空间交叉验证实现部分。

统计推断的一个典型而有用的假设是数据是独立同分布的(IID)。我们可以随机抽取患者子集,用正常的训练-测试分割预测他们患糖尿病的可能性,没问题。然而,在实践中,有一些类型的数据集这种假设不成立,一个典型的训练测试分割可能会引入数据泄漏。当感兴趣的变量的分布是而不是随机时,数据被认为是自相关的——这对机器学习模型有影响。

我们可以在许多带有地理空间成分的数据集上找到空间自相关。考虑下面的地图:

具有真实人口的大马尼拉地图(左)与人口随机分布的地图(右)。数据在人道主义数据交换公开。图片作者。

如果数据是 IID,它看起来就像右边的地图。但在现实生活中,我们有像左边这样的地图,可以很容易地观察到模式。地理第一定律指出,距离较近的事物比距离较远的事物更加相关。属性通常不会随机分布在某个位置,更有可能的情况是某个区域与其相邻区域非常相似。在上面的例子中,一个地区的人口水平很可能与邻近地区相似,而与远处的地区相反。

我们什么时候需要空间交叉验证?

当数据自相关时,我们可能要格外小心过度拟合。在这种情况下,如果我们使用随机样本进行训练测试分割或交叉验证,我们违反了 IID 假设,因为样本在统计上不是独立的。区域 A 可能在训练集中,但是验证集中的区域 Z 恰好离区域 A 只有一公里远,同时也共享非常相似的特征。该模型对于区域 Z 将具有更准确的预测,因为它在训练集中看到了非常相似的示例。要解决这个问题,按区域对数据进行分组可以防止模型偷窥它不应该看到的数据。以下是空间交叉验证的情况:

默认交叉验证与空间交叉验证的图示。图片来自 Lovelace 等人[1]

这里要问的一个好问题是:我们是否总是想要防止过度拟合?直觉上,是的。但与大多数机器学习技术一样,这取决于具体情况。如果它符合您的用例,过度拟合甚至可能是有益的!

假设我们有一个随机抽样的全国财富调查。我们有分布在全国各地的一组家庭的财富值,我们希望推断未调查地区的财富水平,以获得整个国家的完整财富数据。这里,目标仅仅是填充空间间隙。使用最近区域的数据进行训练肯定有助于更准确地填补空白!

如果我们试图建立一个可推广的模型,也就是说,一个我们可以完全应用到另一个国家的模型,那就是另一回事了。【2】在这种情况下,在训练期间利用空间自相关属性将很可能提高潜在的差模型的准确性。如果我们把这个看似不错的模型用在一个没有地面事实可以验证的领域,这尤其令人担忧。

在 scikit-learn 上实现空间交叉验证

为了解决这个问题,我们必须在培训和测试之间划分区域。如果这是一个正常的训练测试分割,我们可以很容易地为我们的测试数据过滤出一些区域。然而,在其他情况下,我们希望通过使用交叉验证来利用所有可用的数据。不幸的是,scikit-learn 的内置 CV 函数随机或按目标变量分割数据,而不是按选择的列。考虑到我们的数据集包含地理编码的元素,可以实施一种变通方法。

下面的代码假设我们有一个带有行政边界的列(例如,城市、州、省)。如果你只有坐标,你可以先对数据进行技术上的聚类(比如用 KMeans ,但是你最好先用一个像 GeoPandas 这样的库把它们映射到一个带有行政边界的数据集上。(给它们分配一个像“城市”这样有意义的值,使结果更容易解释。)

from sklearn.model_selection import GroupKFold, cross_val_predictcities = df['city'].valuesgroup_kfold = GroupKFold(n_splits=5) # Generator for the train/test indices
city_kfold = group_kfold.split(X, y, cities) # Create a nested list of train and test indices for each fold
train_indices, test_indices = [list(traintest) for traintest in zip(*city_kfold)]
city_cv = [*zip(train_indices,test_indices)]predictions = cross_val_predict(model, X, y, cv=city_cv)

现在我们有了一个 CV 或 KFold 对象,我们可以将它插入到 Scikit-learn 的许多交叉验证拟合函数中,如上面所示的cross_val_predict,甚至可以使用参数网格搜索进行嵌套交叉验证,如RandomizedSearchCV。虽然只有几行代码,但希望它能帮助第一次谷歌空间自相关的人。快乐造型!

我非常感谢对实施、写作或直觉的反馈。有什么建议可以让这变得更好吗?请随时联系 chiaraledesma@gmail.com 或我的LinkedIn

参考

[1] R .洛夫莱斯、j .诺沃萨德和 j .明肖。统计学习:空间 CV (2021),用 r 进行地理计算。

[2] P. Deville 等利用手机数据进行动态人口制图 (2014),美国国家科学院院刊。

空间数据科学基础-绝对位置与相对位置

原文:https://towardsdatascience.com/spatial-data-science-basics-absolute-vs-relative-location-b7f24470f0b2?source=collection_archive---------25-----------------------

“定位科学”的简单入门,附实例

托拜厄斯在 Unsplash 上的照片。

每一天的每一分钟,您的连接设备都在挖掘您的位置数据。

你在哪里吃饭,你在哪里睡觉,甚至你下班后在哪里聚会都被外部实体一天 24 小时,一周 7 天记录下来。公司使用你的位置数据来创造令人惊讶的产品和应用的多样性。

你有没有想过谷歌地图怎么会有世界大片地区的交通报告?或者,你是否觉得奇怪,即使没有你的直接输入,你最喜欢的送货应用程序似乎总是向你推荐附近的餐馆?在谷歌的案例中,他们正在汇总数十万条记录,以估计全球道路上的车速。在 DoorDash 或 UberEats 等应用程序中,您的个人位置数据被用来鼓励您从他们的服务中进行购买。

虽然所有这些听起来有些令人不安和害怕,但对于一个有抱负的数据科学家来说,还是有一线希望的——公司需要能够处理位置数据的分析师。在你的简历中加入地理位置方面的资质可以帮助你在职业生涯中成长,在专业上成功。在本文中,我将介绍位置的概念,并详述它在数据科学中的应用。这个核心概念可能看起来很简单,但它比你想象的更微妙。

空间数据科学导论

空间数据科学是一个新兴领域,它将“传统”空间数据分析的关键方面(如查询、ETL、数据挖掘和可视化)与定位科学结合起来。“区位科学”的同义词是地理学。

地理学有许多重要的原则和概念,但如果你想更全面地了解这门学科,有三个关键概念你应该知道:空间地点位置

位置是本文的主要主题,但我想简单介绍一下空间和地点,因为这三者是相互关联的。

空间是一个抽象的概念,用来描述人、景观物体和地点之间的关系距离。空间在数学上可以是绝对的。我和邻居之间的物理距离可以用精确的单位来衡量,假设我们每个人都停留在一个位置。测量员也可以在预期合同报价的情况下精确测量一块房地产的边界。在这两种情况下,空间(线性、矩形或其他)可以使用可测量的单位绝对量化。然而,在许多情况下,空间是相对的,不容易测量。我们所有人都有个人空间或泡泡,根据我们的情况而变化。在新冠肺炎期间,当我们试图在社交上与陌生人保持距离时,我们的大部分个人空间都扩大了,但如果你和亲人一起躲在家里,这种个人空间反而会减少。这些类型的可变空间无法量化测量。许多空间也存在于我们的头脑和想象中。你遇见你的生活伴侣的空间可能对你有很深的意义;同样,你曾经和童年伙伴一起玩耍的地方对你来说也可能是很伤感的。当一个空间具有情感价值时,这就是所谓的场所

现在,你可能正在阅读这篇文章,想知道这些概念与数据有什么关系。毕竟,在一个要求苛刻的商业世界里,公司想要可实现的洞察力,像空间和地点这样的抽象概念有什么好处呢?

跟着我。我向你保证,知道这些概念会派上用场!

位置:空间数据的关键组成部分

使空间/地理数据不同于空间数据的关键要素是位置。打开关系数据库或 Microsoft Excel 电子表格,快速浏览一些数据表。您的表中有没有将街道地址作为属性或列的?GPS 坐标呢?甚至一个州/省的名字也可以。如果您的表中有任何包含位置成分的列,那么恭喜您,您正在处理空间数据!

位置只是地球表面上的一个地方,用某种形式的坐标系来表示。这些坐标可以是绝对的,也可以是相对的。

绝对位置通常是大多数数据科学家习惯使用的。顾名思义,绝对位置是精确和可测量的。GPS 坐标虽然容易出现小的误差,但通常是相当精确的。街道地址也是如此:如果你把街道地址输入你最喜欢的地图应用程序,它几乎总能把你带到离你想去的地方只有几英尺远的地方。

相对位置,另一方面,容易不准确。这种类型的位置是近似的,但不是精确的。当你向陌生人问路时,这通常是你会得到的位置类型。如果有人告诉你,“银行就在几条街外,过了火车站就到了,”这不是一套精确的方向,但它会带你接近你要去的地方。银行的位置与火车站等其他位置相关。使用注释/评论字段来表示位置的数据集并不少见。如果现场技术人员没有 GPS 单元,那么他们可以求助于使用手写注释来表示他们服务过的位置。这些位置可能相对于其他地标和位置。

了解相对位置和绝对位置之间的差异将有助于您改进数据分析和可视化工作流,并有助于提高您的整体工作效率。

场景 1:使用具有绝对位置的空间数据

作为一名空间数据科学家,您的大部分工作将会看到您处理带有绝对位置的数据。你的位置数据的精度将会深刻影响你可以对其进行的分析类型。在这种情况下,精度是指您的位置数据的准确性。如果您有某个位置的精确地理坐标,那么您可以使用这些数据做更多的事情,而不是使用城市或州名。

考虑下面的场景。你是一名数据科学家,在一家私人安全公司工作。您的公司为企业主提供商业警报服务,通常非常可靠,但由于某种原因,在过去的一个月中,新墨西哥的虚拟城市斯普林菲尔德发生了异常多的假警报事件。您的团队领导希望您调查此问题,尝试找出任何可能有助于解释您的警报产品发生问题的模式。

当您使用上面的表格视图时,您能发现位置数据吗?

地理空间数据分析师的与众不同之处在于,他们知道如何利用位置科学为雇主提供有价值的见解。

在这种情况下,不习惯使用地理空间数据的分析师可能会倾向于关注“日期”或“事件类型”属性。虽然这些属性很重要,但是如果没有位置信息,它们的重要性就会降低。在我们的表视图中有三个位置属性。这三个位置都可以归类为“绝对位置”,但它们的精度各不相同。

“州”列是我们的位置数据中最不精确的。

“城市”列更精确。使用该属性,可以在城市之间进行描述性比较。我们也可以做一些简单的可视化来帮助这些比较。当然,在这种情况下最好的图表之一是条形图(如下图所示)。

使用城市属性,可以清楚地看到,在 6 月份,斯普林菲尔德及其郊区“郊区城市”的虚警事件数量相等。这是一个独特的位置洞察力。我们现在可以排除该地区存在公用设施故障问题的可能性,因为每个城市都使用自己的电网。引入时间因素,我们也许能收集到更多的洞见。

虽然我们从使用城市属性中获得的见解很有帮助,但这几乎不是我们最精确的位置数据。“街道地址”一栏甚至更精确。有了这个属性,我们现在可以精确到建筑物级别的警报事件。我们还可以通过称为地理编码的过程将每个事件映射为地图上的一个点。

地理编码是指使用地址、名称或坐标将数据点的位置编码到地球表面的某个位置。地理编码只能使用绝对位置和专业软件进行,如 ESRI 的 ArcGIS 或 Tableau Desktop。地理编码也可以使用 Python 来完成,正如 Abdishakur 在迈向数据科学中所展示的。

如果我们对我们的街道地址进行地理编码,我们就可以创建一个地图,让我们能够根据警报事件发生的地点来可视化这些事件。这将使我们能够确定是否有任何空间或时空模式可以帮助解释已经发生的事件。下面的地图(图 2)详细描述了在我们假设的区域中每个事件的位置和时间。

新墨西哥州虚拟城市斯普林菲尔德的假警报事件地图(实际地图描绘了亚利桑那州凤凰城)OpenStreetMap 贡献者

使用我们最精确的位置数据,我们现在可以看到按日期划分的假警报事件的清晰空间集群。这告诉我们,在导致这些假警报事件发生的空间中,可能正在发生一些事情。如果没有准确的街道地址,就不可能确定这一点。

有了这些信息,我们可以搜索这些地点附近的事件,试图找出发生了什么。在这种情况下,我们发现由于夜间道路重铺,这些地点出现了巡回停电。因为你的警报器需要一个持续的电源,当道路施工暂时切断警报器时,一个错误的警报器触发了。

这种情况的教训是绝对位置在数据分析工作中非常有价值。这些位置越精确越好!

使用具有相对位置的空间数据

作为数据分析师,处理附有相对位置的数据要难得多。90%的情况下,数据库中的位置信息是绝对的,可以根据地形进行地理编码。然而,情况并非总是如此。

让我们用另一个场景来打破这个概念。

这一次,你工作的保安公司想要一张在我们虚构的新墨西哥州斯普林菲尔德市的大学校园内自行车失窃的地图。你得到的是模糊的位置信息,这些信息是由校园里的安全官员输入的。

查看表视图,可以清楚地看到,这里没有可以使用的绝对位置。缺少绝对位置数据意味着您无法再对地图上的点进行地理编码。

使用“Officer_Comments”属性,您意识到您拥有这些盗窃事件发生的相对位置。这些位置是相对的,因为它们只是相对于大学校园中的其他位置来描述的。既然知道了这些相关建筑的位置,就可以对盗窃事件发生的地点做出一些推断。

没错,使用相对位置的关键是使用上下文线索进行推理。如果评论说“250 英尺。从娱乐中心“并且你知道在那个大概的位置有一个自行车架,你可以推断盗窃发生在自行车架上。同样,如果某个位置被指定为位于建筑物的“西北侧”,您可以根据自己的最佳判断在该位置放置一个点。地理信息系统(GIS)特别有助于通过地理编码和推理放置向地图添加数据点(点要素)。

使用相对位置时,您会遇到两个主要问题。首先,当您使用上下文线索“推断”数据点的位置时,必然会有一些数据准确性问题。让你的雇主提前了解这些问题。如果 250 英尺内有两个自行车架。对于一栋建筑,你可能会推断在一个机架上发生了盗窃,而实际上在另一个机架上发生了盗窃。这就产生了一个精度问题,如果您计划对数据应用地统计分析技术,这可能会成为一个问题。第二个问题是推断大型数据集的位置可能很麻烦,如果不是完全不可能的话。

结论

对于数据分析师和科学家来说,能够处理位置数据正成为一项越来越重要的技能。了解位置的基本情况有助于提高你的分析能力,改善你的工作表现。

用于碰撞预测的时空变换

原文:https://towardsdatascience.com/spatial-temporal-convlstm-for-crash-prediction-411909ed2cfa?source=collection_archive---------12-----------------------

2015-2019 年亚特兰大市的 3D 场景交通事故。数据源 GADOT。作者图片

一种独特的深度学习方法用于事故预测

这篇文章讲述了一项独特的实证研究,该研究使用 ConvLSTM 深度学习模型和 ArcPy 利用碰撞特征数据的时间序列来预测第二天的碰撞风险位置。交通事故被公式化为时空序列预测问题,其中输入和预测数据都是时空序列。卷积 LSTM ( ConvLSTM )方法是为崩溃预测建立一个端到端的可训练模型。结果表明 ConvLSTM 网络能够捕捉交通事故发生的时间和地点的时空相关性。

问题:交通事故导致严重的人员伤亡和巨大的经济损失。预测时空环境中交通事故风险的能力不仅对于公众而且对于政府官员来说对于预防事故的发生都是重要的。然而,预测交通事故是一项非常具有挑战性的任务,不仅要预测多种因素(如人、时间、几何和环境)的原因,还要预测稀有因素和稀疏数据集。传统的事故预测一般采用统计回归,如泊松负二项、【NB】、多元回归。但是,当处理复杂和高度非线性的数据时,如事故的时空相关性,如事故发生的时间、地点和原因时,它们往往会失败。有经典的机器学习方法,例如 XGBoostSVMRandomForest 分类器,它们将特征设计到模型 中,以寻求特征对于概率 的重要性。我们正在讨论机器学习中的特征,即多维空间中的数字阵列。要了解更多机器学习中的特征工程,请阅读来自 丹尼尔·克拉尔 的一个最好的故事。

此外,时间和季节性在决定何时何地发生事故的概率方面起着重要作用。使用 ArcGIS Pro 中的 ArcPy,我们可以聚合特定时间窗口内的崩溃位置。 点击查看 3D 场景中 坠机地点示例。

解决方案:那么,我们是否可以应用经典的机器学习模型来搜索重要的因果特征,然后提出一个实用的深度学习网络来预测事故发生的时间和地点?答案是肯定的。 ConvLSTM 模型是最有趣的深度学习模型之一,用于预测下一帧视频或图像。最初的研究是针对 降水临近预报 进行的。为了更好地理解 ConvLSTM 模型,我们先来看看普通的 LSTM 网络模型。(LSTM)**网络是一种具有反馈连接的人工递归神经网络()。它不仅可以处理单个数据点(如图像),还可以处理整个数据序列(如语音或视频)。以下是迈克尔·菲尔为《LSTM 解说指南》制作的最佳解说视频之一。也可以看 他的博客 **

有用吗?现在,让我们看看什么是 ConvLSTM 。简单的说, ConvLSTM 就是 CNN 卷积神经网络结合 LSTM 网络。不是只有数据序列的输入,它的输入是最适合图像和视频的 CNN 卷积神经网络的数据序列。这种组合确保 ConvLSTM 捕捉潜在的局部时空相关性。 ConvLSTM 的关键方程和内部结构图见下图 1。

图一。图片来自http://home page . div ms . uio wa . edu/~宁拙/papers/p984-yuan.pdf

工作原理:这项实证研究的目标是利用 ConvLSTM 网络,设计先前的事故数据序列,以预测科布县固定时间段内的车祸地点。每个事故都有记录的时间和位置坐标。想象每个时间段,即每两个小时,是时空空间中的一个画面。当堆叠这些碰撞图像帧并馈入 ConvLSTM 网络时,我们可以预测固定长度,即接下来几个小时的交通事故图像帧。为了将数据拟合到模型中,数据将被公式化为 3D 张量,例如图像,并作为具有上述特征的 4D 张量序列进行叠加,作为第四维。(序列、图像、图像、特征)。使用 ArcPy 将整个科布县地区划分为 45hx41w(总共 1845 个,每个 0.25 英里)的方形箱。在这个初始项目中,我使用七天的碰撞序列数据作为模型的输入,并预测第二天的碰撞图像地图,因为县一级的碰撞数据非常稀少,并且具有很强的每周模式。将有另一个项目来预测未来两个小时的崩溃使用相同的模型和算法。当应用**XGBoost 和 RandomForest 模型*时,总结出了导致科布交通事故的 11 个重要特征。这些特征属性包括不随时间变化的变量,如道路长度、道路曲率、平均坡度、人口密度等。以及时变变量,例如天气、一天中的时间、事故和位置等。这些特征被格式化为 4D 张量的第四维(7,45,41,11)。三年的科布县事故数据被处理并批量输入 ConvLSTM 模型,其中 90%为训练序列,10%为测试序列。每个序列一天移位一次,总共 1096 个序列用于训练和测试。输出预测是第二天在 1845 个箱中发生的碰撞(参见图 2 显示堆叠的县碰撞箱地图的解释性图片。蓝色的箱子是在那个时间段发生事故的箱子)*****

图二。作者图片

建立模型,训练数据,进行风险预测

****幕后:典型的深度学习模型是工程化数据集,建立模型,训练数据,进行预测,部署模型。用 二元交叉熵亚当 优化器对模型进行 50 个历元的训练(实际上可以训练几百个历元)。结果证明很有希望。上面的图库显示了除数据工程部分之外的一些步骤。随机选择 2020 年 12 月 12 日的地面实况和预测样本,这在训练中没有使用。通过使用一定的概率阈值直观地比较实际情况和预测,该模型可以预测当天最匹配的碰撞位置和模式。参见图 3。

图 3。作者图片

作者图片

结果:通过几行 ArcPy 代码,我们可以将模型输出记录添加到 ArcGIS Pro 中的格网要素类和地图中。使用符号化的道路和其他要素,在 ArcGIS Pro 中更易于可视化。

作者图片

作者图片

为什么重要:这个实证研究项目展示了我们如何使用 ArcPy 和深度学习技术,如 ConvLSTM 来找到一个有希望的崩溃预测解决方案。整个工作流和数据流程可通过更多工程要素和数据得到进一步增强,在 ESRI ArcGIS Pro desktop 中实现自动化、部署和制图,或成为 ArcGIS 企业服务。因此,同样的方法可以应用于交通流量、降雨量、犯罪预测等你能想到的造福人类的时空解决方案。

当今世界向我们提出了许多挑战。然而,借助人工智能和深度学习的力量,我们可以共同努力寻求解决方案,让世界变得更加美好。感谢您阅读这个故事地图。

参考文献

[1]邢建石,,陈,,杨迪燕,黄伟建,王春宇, 卷积 LSTM 网络:一种用于降水临近预报的机器学习方法。 计算机视觉与模式识别(cs。CV)引用为:arXiv:1506.04214【cs。简历]

[2]袁,,杨天宝, Hetero-ConvLSTM:一种基于异构时空数据的交通事故预测深度学习方法 2018 年 KDD 奥运会,2018 年 8 月 19 日至 23 日,英国伦敦。

[3] Sobhan Moosavi,Mohammad Hossein Samavatian,Srinivasan Parthasarathy,Radu Teodorescu,Rajiv Ramnath, 基于异质稀疏数据的事故风险预测:新数据集和见解 机器学习(cs。LG);数据库(cs。DB);as:arXiv:1909.09638【cs。LG】。

*[4] Jeremiah Roland ,Peter D. Way,Connor Firat,Thanh-Nam Doan,Mina Sartipi,田纳西州查塔努加市车辆事故发生的建模与预测,事故分析与预防 149 (2021) 105860

[5]ESRI·阿皮

[6]丹尼尔·克拉尔, 汽车碰撞预测特征工程 故事地图 2020 年 5 月 19 日

空间变压器网络

原文:https://towardsdatascience.com/spatial-transformer-networks-b743c0d112be?source=collection_archive---------7-----------------------

思想和理论

独立的介绍

由 Max Jaderberg 等人引入的空间转换器模块是一种流行的方法,用于增加模型对空间转换的空间不变性,如平移、缩放、旋转、裁剪以及非刚性变形。它们可以被插入到现有的卷积架构中:要么紧跟在输入之后,要么在更深的层中。它们通过自适应地将其输入变换为规范的、预期的姿态来实现空间不变性,从而导致更好的分类性能。单词 adaptive 表示根据输入本身的条件,为每个样本产生适当的变换。可以使用标准的反向传播来端到端地训练空间转换器网络。

空间转换器模块将输入转换成一个规范的姿态,
,从而简化后续层的识别(图片由作者提供)

在这个由四部分组成的教程中,我们涵盖了深入理解空间转换器所需的所有先决条件。在前两篇文章中,我们已经介绍了正向映射和反向映射的概念,并深入研究了双线性插值的细节。在本帖中,我们将介绍构成空间转换器模块的所有构件。最后,在下一篇也是最后一篇文章中,我们将从头开始推导所有必要的反向传播方程。

职责分离

为了理解空间转换器模块的一些构建块背后的动机,我们必须快速重复第一篇文章中介绍的反向映射的原理。

反向映射(图片由作者提供)

在反向映射中,我们遍历输出图像,一次一个像素,对于每个位置,我们执行两个操作:

  1. 使用逆变换𝑇-1{…}来计算输入图像中的相应位置
  2. 使用双线性插值对像素值进行采样

我们在上面的动画中一个接一个地直接执行这两个操作的原因主要是为了说明反向映射的概念。然而,当实现反向映射时,首先计算所有输出像素的相应位置(并且可能存储它们),然后才应用双线性插值是有益的。很明显,这对最终结果没有影响。

这种方法的主要好处是,我们现在得到了两个具有独立职责的组件:网格生成器采样器网格生成器具有执行逆变换的专有任务,并且采样器具有执行双线性插值的专有任务。此外,正如我们将在下一篇文章中看到的,分离极大地促进了反向传播。

电网发电机

网格生成器在输出/目标图像的规则网格上迭代,并使用逆变换𝑇-1{…}来计算输入/源图像中的相应(通常是非整数)样本位置:

网格生成器(图片由作者提供)

上标𝑡和𝑠取自原纸,分别表示“目标图像”和“源图像”。采样网格的行和列索引分别表示为𝑖和𝑗。还请注意,在原始论文中,常规输出网格上的逆变换𝑇-1{…}表示为𝒯𝜃(𝐺).

尽管为了清楚起见,上图中的坐标是按顺序计算的,但出于计算效率的原因,网格生成器的实际应用将尝试并行转换尽可能多的点。

网格生成器的输出是所谓的采样网格,它是一组将对输入地图进行采样以产生空间变换输出的点:

在哪里

请注意,采样网格的大小,决定了目标图像的大小。

采样网格包含将对输入/源地图进行采样的点(图片由作者提供)

关于采样网格最后要提到的重要一点是,它的高度和宽度不一定需要与输入图像的高度和宽度相同。

取样器

采样器迭代采样网格的条目,并使用双线性插值从输入图中提取相应的像素值:

采样器(图片由作者提供)

像素值的提取包括三个操作:

  1. 找到四个相邻点(左上、右上、左下和右下)
  2. 对于每个相邻点,计算其相应的权重
  3. 取加权平均值以产生输出

所有操作都总结在下面的等式中,该等式已在上一篇文章中导出:

记住,𝑑𝑥表示从样本点到单元格右边界的水平距离,𝑑𝑦表示到单元格上边界的垂直距离。

网格生成器中,每个输出像素的计算完全独立于任何其他输出像素。因此,现实世界中的采样器将通过并行提取尽可能多的点来加速这个过程。

本地化网络

定位网络的任务是找到逆变换𝑇-1{…}的参数𝜃,该逆变换将输入特征图置于标准姿态,从而简化后续层中的识别。定位网络可以采取任何形式,例如全连接网络或卷积网络,但是应该包括最终回归层以产生变换参数𝜃:

本地化网络(图片由作者提供)

𝜃的大小可以根据被参数化的变换而变化,例如对于仿射变换,𝜃是 6 维的:

仿射变换非常强大,包含平移、缩放、旋转和剪切作为特例。然而,对于许多任务,更简单的转换可能就足够了,例如,仅使用 2 个参数来实现纯转换:

网格生成器采样器都是无参数操作,即它们没有任何可训练的参数。在这方面,它们相当于最大池层。因此,空间转换器模块的智能来自定位网络,它必须学会检测输入特征地图的姿态(如其方向、比例等)。)以便产生适当的变换。

完整模块

最后,让我们看看空间转换器模块的单个构建块是如何相互作用的。输入特征图𝑈首先被传递到定位网络,其回归适当的变换参数𝜃.网格生成器然后使用变换参数𝜃产生采样网格,这是一组输入特征地图将被采样的点。最后,采样器获取输入特征图和采样网格,并使用例如双线性插值输出变换后的特征图。

空间转换器模块(图片由作者提供)

在这一点上,我们想再次提请注意这样一个事实,即定位网络为每个输入单独预测变换参数。以这种方式,空间转换器模块成为自适应组件,其行为取决于输入。

多通道

到目前为止,我们已经在具有单通道𝐶=1 的输入上展示了空间变换器模块的原理,如在例如灰度图像中遇到的。然而,空间转换器模块通常用于更深的层,并在特征地图上操作,这些特征地图通常具有多个通道
𝐶 > 1。即使在输入之后立即使用,空间变换器模块也可能面对具有不止一个通道的输入,例如具有 3 个通道的 RGB 图像。

扩展很简单:对于多通道输入,对输入的每个通道进行相同的映射,因此每个通道都以相同的方式进行转换。这样,我们可以保持通道之间的空间一致性。请注意,空间转换器模块不会改变通道𝐶的数量,该数量在输入和输出特征图中保持不变。

我们到了第三个帖子的末尾。到目前为止,您已经熟悉了空间转换器模块的两个基石:反向映射和双线性插值。您知道空间转换器模块的所有构造块以及它们如何相互作用。大量的动画应该有助于你形成一个强有力的心理概念。现在,您可以在自动微分框架(如 TensorFlow 或 PyTorch)中使用空间转换器模块了。

下一个也是最后一个帖子主要是为那些想知道双线性插值怎么可能是微分运算的高级读者准备的。我们将详细了解梯度如何通过采样器不仅流回输入特征地图,还流回采样网格坐标。

参考

原文
关注点分离
py torch 中的空间变形金刚
点评:STN —空间变形金刚网络

空间变压器网络—反向传播

原文:https://towardsdatascience.com/spatial-transformer-networks-backpropagation-15023fe41c88?source=collection_archive---------11-----------------------

独立的介绍

由 Max Jaderberg 等人引入的空间变换器模块是一种流行的方法,用于增加模型对空间变换(如平移、缩放、旋转、裁剪以及非刚性变形)的空间不变性。它们通过自适应地将其输入变换为规范的、预期的姿态来实现空间不变性,从而导致更好的分类性能。

在这个由四部分组成的教程中,我们涵盖了深入理解空间转换器所需的所有先决条件。在前两篇文章中,我们已经介绍了正向和反向映射的概念,并深入研究了双线性插值的细节。在上一篇文章中,我们已经介绍了构成空间转换器模块的所有构件。最后,在这篇文章中,我们将从头开始推导所有必要的反向传播方程。

梯度流动

在我们开始推导公式之前,让我们快速了解一下梯度是如何通过空间转换器模块流回的:

梯度流,𝓛表示损失函数(图片由作者提供)

上面的动画清楚地说明了为什么空间转换器网络可以使用标准反向传播进行端到端训练。我们从模块输出端的梯度开始,该梯度已经在较高层计算过了。我们要做的第一件事,是导出显式公式,通过采样器将该梯度传播(或流动)回输入特征图和采样网格。然后,我们必须通过网格生成器推导出控制反向传播的公式。记住采样器网格生成器都是无参数操作,即没有任何可训练参数。最后,我们必须通过定位网络反向传播梯度,这是一个标准的神经网络,因此这里不需要推导新的公式。参数更新发生在本地化网络中。

如果你从未遇到过反向传播,对梯度流概念的理解有问题,请看看我的介绍帖。

梯度 w.r.t 采样网格坐标

在所有以前的帖子中,我们将假设采样器使用双线性插值来转换输入特征图。让我们快速回忆一下在第二篇文章中推导出的相应公式。对于采样网格的每个条目:

采样器首先通过取下限和上限运算找到四个相邻值:

为了清楚起见,我们去掉了上标“𝑠”。接下来,采样器计算从采样点到其单元格右边界的水平距离和到单元格上边界的垂直距离:

最后,需要一个加权平均值来产生输出:

为了得到想要的导数的直觉,让我们摆动单个(!)进入采样网格:

单个条目对输出地图的影响(图片由作者提供)

我们看到,摆动仅影响输出特征地图中的单个像素。这是意料之中的,因为采样器采样网格的每个入口上独立运行(这是采样器非常适合并行化的原因)。为了将损失误差从输出特征图反向传播到采样网格,我们所要做的就是应用链式法则:

其中𝓛是损失函数。接下来,我们必须把𝑉·w·r·t 的导数带到𝑥:

这需要我们对水平距离求导:

为了更进一步,我们必须看看天花板运算的导数:

天花板操作及其衍生工具(图片由作者提供)

我们可以看到,上限运算是分段常数,常数的导数为零。上限运算在𝑥的整数值处是不连续的,并且在那里是不可微的。

从技术上讲,我们不能对一个不可微的函数应用梯度下降。我们的补救办法是所谓的次导数,它是导数的延伸,见参考文献。实际上,这可以归结为在𝑥:的整数值处将导数设置为零

类似地:

形式上,我们现在计算子梯度而不是梯度。我们最后的公式是:

重新排列后:

对于𝑦分量,我们相应地得到:

梯度 w.r.t 输入要素地图

在我们深入研究数学公式之前,让我们再次首先发展一种直觉。这一次,我们必须摆动输入要素地图中的像素值,比如坐标(2,1):

单个输入像素对输出地图的影响(图片由作者提供)

我们看到,摆动输入特征图中的单个像素会导致输出特征图中的多个像素发生变化。为了理解原因,让我们仔细看看受影响的输出像素的采样点:

受影响的样本点共享一个公共输入像素(图片由作者提供)

我们注意到,所有提到的样本点都有一些共同点:坐标(2,1)处的输入像素总是属于双线性插值中使用的四个相邻点之一。还请注意输入像素(2,1)有时是右上邻居,有时是左下邻居,依此类推。

用于反向传播误差的链式法则现在变成:

其中两个和考虑了输入特征图中的每个像素可能(潜在地)影响输出特征图中的多个像素的事实。在下一步中,我们必须评估表达式∂𝑉/ ∂𝑈,它强烈依赖于𝑈相对于𝑉's 样本点的相对位置(左上邻居、右上邻居等)。).为此,我们以如下方式重写双线性插值公式:

相应的衍生工具有:

我们现在有了计算梯度的所有必要公式。为了对整个过程有一个更好的直觉,让我们把它应用到上面动画的例子中。这里,输入像素(2,1)影响以下五个输出像素(0,0)、(0,1)、(1,0)、(1,1)和(2,1):

该过程的主要挑战似乎在于找到所有受影响的输出像素。幸运的是,在实际实现中,我们可以通过利用线性来完全省略显式搜索:

高效实施(图片由作者提供)

为此,我们首先为梯度∂𝓛 / ∂𝑈初始化一个空数组,然后迭代采样网格的条目。对于每个条目,我们使用倒数第二个公式来计算∂𝑉/ ∂𝑈的所有四个导数,随后乘以梯度
∂𝓛 / ∂𝑉.的相应条目剩下的最后一步是将四个计算值添加到梯度数组中。请注意,每个值都添加在不同的位置,由四个相邻点的位置定义。在整个过程结束时,梯度阵列的每个条目将包含所有受影响的输出像素的完整总和。

通过网格生成器反向传播

我们已经看到损失函数如何依赖于采样网格的所有坐标:

此外,每个样本坐标是由定位网络提供的参数的函数:

因此,将链式法则应用于多元函数,我们得到:

在下文中,我们将假设网格生成器正在使用仿射变换:

由于目标坐标位于规则的采样网格上,因此我们有:

使得上述等式简化为:

相应的衍生工具有:

其余情况为零。为了获得最终的公式,我们将这些导数代入上述链式法则:

类似地:

如第一节所述,网格生成器通常被实现为标准的神经网络,例如全连接网络或卷积网络。为此,我们不需要推导任何新的反向传播公式。

我们已经到了第四个也是最后一个帖子的末尾。在这篇文章中,我们讨论了空间转换模块中很少被提及的反向传播的话题。虽然很复杂,但它将极大地帮助您调试在使用空间转换器模块时可能会遇到的问题。如果你想进一步磨练你的理解,请看看通过采样器反向传播的实际 Tensorflow C++实现。

参考

原文
超越导数—次导数
ReLU:不是可微函数
次导数

空间变压器网络教程,第 2 部分—双线性插值

原文:https://towardsdatascience.com/spatial-transformer-networks-tutorial-part-2-bilinear-interpolation-371e1d5f164f?source=collection_archive---------12-----------------------

独立的介绍

空间转换器模块是一种流行的方式,用于增加模型的空间不变性,以抵抗空间转换,如平移、缩放、旋转、裁剪以及非刚性变形。它们可以被插入到现有的卷积架构中:要么紧跟在输入之后,要么在更深的层中。它们通过自适应地将其输入变换为规范的、预期的姿态来实现空间不变性,从而导致更好的分类性能。单词 adaptive 表示根据输入本身的条件,为每个样本产生适当的变换。可以使用标准的反向传播来端到端地训练空间转换器网络。

空间转换器模块将输入转换为标准姿态,从而简化后续图层中的识别(图片由作者提供)

在本教程中,我们将涵盖深入理解空间转换器所需的所有先决条件。在的上一篇文章中,我们已经介绍了正向映射和反向映射的概念。在这篇文章中,我们将深入探讨双线性插值的细节。在下一篇文章中,我们将介绍构成空间转换器模块的所有构件。最后,在第四篇也是最后一篇中,我们将从头推导所有的反向传播方程。

线性内插法

作为热身,我们将从简单的一维情况开始。这里我们处理的是位于等距网格上的一系列数据点:

数据点序列(图片由作者提供)

由于在本教程中,我们将主要处理图像数据,我们可以安全地假设两个连续数据点之间的空间是一个而不失一般性。

请注意,上图中的离散序列仅针对整数位置定义,其他位置未定义。然而,我们经常需要非整数位置的值,比如上面例子中的 2.7。这是通过插值技术实现的,插值技术从已知数据值中估计未知数据值。在下文中,我们将未定义的点称为样本点,我们希望使用插值技术来估计这些点,并用字母𝑥.来表示它们

在线性插值中,我们只是简单地将一条直线拟合到。)𝑥的两个相邻点,然后查找所需的值:

线性插值(图片由作者提供)

我们通过地板和天花板操作找到𝑥的邻近点。请记住:floor()将𝑥舍入到低于其值的最接近的整数,而 ceil()将它舍入到高于其值的最接近的整数。

由于两个相邻的点都位于网格上,我们知道它们的𝑈值(如上图箭头右侧所示)。此外,对于网格上的两个连续点,我们有:

接下来,我们必须将以下直线拟合到两个相邻的点上:

其中𝑚是斜坡,𝑏是交叉点。我们通过将左邻点插入方程来获得𝑏:

斜率𝑚是通过将右边的邻近点插入方程中获得的:

因此,我们的初步公式是:

为了获得更多的直觉,让我们扩展上述乘积并重新排列:

让我们将从采样点𝑥到其右侧相邻点的距离表示为:

我们也可以改写为:

因此,我们得出了最终的公式:

了解如何根据按相反距离加权的相邻点的值来解释线性插值:

线性插值直觉(作者图像)

双线性插值

接下来,我们将把我们的尝试扩展到二维情况。这里我们处理的是一组位于等间距二维网格上的数据点:

等距二维网格上的数据点(作者图像)

与一维情况一样,非整数位置的值未定义,必须使用插值技术进行估计。

在双线性插值中,我们将平面拟合到最接近的四个(!)采样点周围的相邻点(𝑦、𝑥),然后查找所需值:

双线性插值(作者图像)

幸运的是,要得到平面方程,我们不需要像上一节那样求解复杂的线性方程组。取而代之的是,我们将对每个点重复应用线性插值(1-D)。我们从上面两点开始,在水平方向应用线性插值。接下来,我们对下面两点做同样的操作。最后,为了获得所需的值,我们对刚刚获得的中间点应用垂直方向的线性插值,参见下面的动画:

将线性插值扩展为双线性插值(作者图像)

𝑥-dimension:线性插值

𝑦-dimension:线性插值

综合起来,我们得到:

由于对称性,上述两个步骤的顺序是不相关的,即首先在垂直方向上执行线性插值,然后在水平方向上执行,得到完全相同的公式。

双线性插值还有一个很好的几何可视化:要获得所需点的值(蓝色),我们必须对每个角上的值和对角相对的部分区域的乘积求和:

双线性插值直觉(作者图像)

还有另外两种重要的插值技术:最近邻插值和双三次插值。最近邻插值法只是将距离采样点最近的像素复制到输出位置。双三次插值考虑样本点的 16 个邻居,并拟合高阶多项式来获得估计值。

在教程的下一部分中,我们将解释使用双线性插值的空间变换网络的所有概念。这些概念可以很容易地扩展到更先进的插值技术。

参考

原创论文
线性插值
双线性插值

空间变压器网络教程,第 1 部分—正向和反向映射

原文:https://towardsdatascience.com/spatial-transformer-tutorial-part-1-forward-and-reverse-mapping-8d3f66375bf5?source=collection_archive---------17-----------------------

思想和理论

独立的介绍

卷积神经网络具有内在的平移不变性。这使他们能够在测试时正确地对图像进行分类,即使当其组成部分位于训练期间看不到的位置时。然而,细胞神经网络缺乏内在的尺度和旋转不变性:自然图像中最常见的两种变换。由于这种属性不是内建的,所以必须通过一种费力的方式来学习:在训练过程中,所有相关的对象必须以不同的比例和旋转来呈现。以这种方式,网络学习每个尺度和每个方向的冗余特征集,从而实现期望的不变性。因此,CNN 通常非常深,需要大量的训练数据来获得高精度。

空间转换器模块将输入转换为标准姿态,从而简化后续图层中的识别(图片由作者提供)

空间转换器模块是一种增加模型空间不变性的流行方法,可以抵抗空间转换,如平移、缩放、旋转、裁剪以及非刚性变形。它们可以被插入到现有的卷积架构中:要么紧跟在输入之后,要么在更深的层中。它们通过自适应地将其输入变换为规范的、预期的姿态来实现空间不变性,从而导致更好的分类性能。单词 adaptive 表示根据输入本身的条件,为每个样本产生适当的变换。可以使用标准的反向传播来端到端地训练空间转换器网络。

在本教程中,我们将涵盖深入理解空间转换器所需的所有先决条件。在这第一篇文章中,我们将介绍正向和反向映射的概念。在的下一篇文章中,我们将深入探讨双线性插值的细节。在第三篇文章中,我们将介绍构成空间转换器模块的所有构件。最后,在第四篇也是最后一篇中,我们将从头推导所有的反向传播方程。

输入数据

空间转换器最常用于图像数据。数字图像由有限数量的称为像素(像素是图片元素的缩写)的小方块组成,这些小方块被组织成行和列。每个像素值代表信息,如强度或颜色。

字母“T”的二进制图像(作者提供的图像)

我们使用𝑦-axis 朝下的坐标系,这是计算机视觉中的常见惯例。

图像数据的主要特征是像素之间的空间关系。像素的空间排列携带了图像内容的关键信息。如果没有其余的像素,单个像素就没有什么意义。

为了清晰起见,在本教程中,我们将经常使用下面的“线图”来可视化图像数据。在该图中,显示了𝑥-axis 和𝑦-axis 的空间位置,以及𝑧-axis.沿线的强度值

相同图像的线图(作者提供的图像)

线图清楚地说明了数字图像的离散性质,像素值仅在等距网格上定义,外部未定义。

每当只有空间信息是重要的时,例如当我们导出空间转换器的梯度时,我们将使用以下自上而下的视图:

线形图的俯视图(图片由作者提供)

有一点要记住:像素值可以是离散的,也可以是连续的。例如,在 8 位灰度图像中,每个像素具有范围在 0 和 255 之间的离散值,其中 0 代表黑色,255 代表白色。另一方面,由卷积层生成的特征图具有连续的像素值。

空间变换

空间变换将输入图像中的每个点(𝑥𝑦)移动到输出图像中的新位置(𝑢𝑣),同时在一定程度上保留邻域中像素的空间关系:

转换概念(图片由作者提供)

基本的空间变换是缩放、旋转、平移和剪切。其他重要的转换类型是投影和映射。

正向变换𝑇{…}将输入空间中的位置映射到输出空间中的位置:

逆变换𝑇-1{…}将输出空间中的位置映射回输入空间中的位置:

正向映射

实现空间变换最直接的方法是迭代输入图像的每个像素,使用𝑇{…}计算其在输出图像中的新位置,并将像素值复制到新位置:

正向映射(图片由作者提供)

大多数情况下,新位置(𝑣、𝑢)不会落在输出图像的网格点上(不是整数值)。我们通过给𝑣和𝑢分配最近的整数并使用它们作为输出坐标来解决这个问题。

正向映射有两个主要缺点:重叠和空洞。正如我们在上面的动画中看到的,一些输出像素接收多个输入图像像素,而其他输出像素根本不接收任何输入图像像素。

由于正向映射方法的缺点,实际上使用了一种称为反向映射的不同技术。

反向映射

反向映射对输出图像网格上的每个像素进行迭代,使用反向变换𝑇-1{…}来确定其在输入图像中的对应位置,对该位置的像素值进行采样,并将该值用作输出像素:

反向映射(图片由作者提供)

这种方法完全避免了重叠和孔洞的问题。反向映射也是空间转换器中使用的方法。

正如我们在上面的动画中看到的,大多数时候,输入图像中确定的位置(𝑦,𝑥)并不在输入图像的网格上。为了在这些未定义的非整数位置获得输入图像的近似值,我们必须对像素值进行插值。一种称为双线性插值的插值技术将在下一篇文章中介绍。

多通道

到目前为止,我们已经展示了使用单通道𝐶=1 对输入进行正向映射和反向映射的原理,例如在灰度图像中遇到的情况。然而,大多数时间输入将具有不止一个通道𝐶 > 1,例如具有三个通道的 RGB 图像或深度学习架构中的特征图,其可以具有任意数量的通道。

扩展很简单:对于多通道输入,对输入的每个通道进行相同的映射,因此每个通道都以相同的方式进行转换。这样,我们可以保持通道之间的空间一致性。请注意,空间变换不会改变通道𝐶的数量,这在输入和输出地图中是相同的。

参考

原创论文
CNN 的平移不变性
数据可视化
空间变换

季节性干旱的时空制图

原文:https://towardsdatascience.com/spatio-temporal-mapping-of-seasonal-droughts-e471f8cf9dad?source=collection_archive---------32-----------------------

如何创建动画 gif 来显示你感兴趣的地区在一段时间内的干旱情况

干旱的时空制图:作者提供的图像

介绍

干旱规模和频率的增加经常被认为是气候变化的不利影响之一。干旱是一种气候现象,其特点是由于降水不足而导致水分不足。干旱有三种类型:

  1. 气象——降水不足导致。印度气象局(IMD)根据降雨量不足分别超过 25%和 50%,将气象干旱分为“中度”和“严重”。
  2. 水文——长时间的干旱导致地表水和地下水枯竭,造成牲畜和人类用水短缺。
  3. 农业——作物季节降雨量和土壤湿度不足的情况。

监测干旱很重要,因为它直接关系到粮食安全。有许多指数可以用来量化干旱情况。在这篇博客中,我将演示如何使用 Python 中的开源工具以动画 GIF 的形式呈现干旱的时空变化。

2.工作流程

工作流程由以下主要步骤组成:

  • 导入库并验证谷歌地球引擎(GEE)。
  • 定义感兴趣区域(aoi)。
  • 根据观测期间的日降水量栅格创建月复合数据。
  • 创建长期的月度复合基线——比如说 10 年。
  • 计算每个月的降雨量不足。
  • 通过创建镶嵌图,使用矢量边界覆盖的计算赤字图层的 RGB 可视化创建镶嵌图。
  • 将每月的马赛克绘制成动画 GIF 的帧。
  • 用相应月份标注每一帧并显示 GIF。

我们将从通常的 python 设置和 GEE 初始化开始。

import geemap
import eetry:ee.Initialize()
except:ee.Authenticate()ee.Initialize()

3.数据处理

我们将使用气候灾害小组的红外降水和台站(CHIRPS)降水数据,这些数据在 GEE(https://developers . Google . com/earth-engine/datasets/catalog/UCSB-CHG _ CHIRPS _ DAILY)中被同化。这是一个开放的数据集,所有版权都被放弃。它包含从 1981 年开始的 0.05 分辨率卫星图像的每日降雨量数据,这有助于为趋势分析和季节性干旱监测创建网格降雨量时间序列。

正如工作流中所讨论的,我们定义了用于分析和可视化的区域。我们将使用印度的矢量 shapefile,其中包含各邦边界的 GIS 信息。这个 shapefile 在互联网(IGISmap.com、gadm.org、arcgis.com 等)上很容易找到。)并可以通过代码编辑器作为资产上传到 GEE 上。一种更简单的替代方法是使用 FAO-GAUL 数据集,该数据集已经在 GEE 中获取,但需要做出一些努力,以将有争议的地理区域纳入印度领土,从而实现精确制图。

# We use the country boundaries from Global Administrative Unit Layers (2015) provided by FAO(UN)
india = ee.FeatureCollection("users/skantbksc/states");# Define the regional bounds for animation frames.
region = india.geometry().bounds();

3.1 为观察期创建每月合成图像

我们将分析 2020 年的干旱情况。由于干旱主要发生在印度大部分降水发生的季风季节,我们将选择 6 月至 10 月进行分析。

# set start date for analysis
startDate = '2020-06-01';# set end date for analysis
endDate = '2020-10-31';#filter the image collection using dates
pptCol = ee.ImageCollection("UCSB-CHG/CHIRPS/DAILY")\.filterDate(startDate, endDate)\.select('precipitation');# Setting year and month as a property of every image which will later be used to form monthly composites
def set_yymm(img):return img.set('yymm', img.id().slice(0,6))pptCol = pptCol.map(set_yymm)# Define a list of unique observation months from the image collection.
yymmList = ee.List(pptCol.aggregate_array('yymm')).distinct().sort();# Function to sum up the daily precipitation data of each month 
def groupByMonth(yymm):pptMonth = pptCol.filterMetadata('yymm', 'equals', yymm)\.sum()\.clip(india)\.set('yymm', yymm);return pptMonth# Map the function over the list of months to build a collection of monthly image composites.
pptMonthList = yymmList.map(groupByMonth);pptByYYMM = ee.ImageCollection.fromImages(pptMonthList);

使用pptCol.size().getInfo()pptByYYMM.size().getInfo()可以检查到,原始每日采集和计算每月采集中的图像数量分别为 152 和 5。

3.2 基线月度综合数据的计算

# We use 10 year data to construct a baseline figure 
baseCol = ee.ImageCollection("UCSB-CHG/CHIRPS/DAILY")\.filterDate('2000-01-01','2010-01-01')\.select('precipitation');# Create a list of months
months = ee.List.sequence(1, 12);# Function to find the average monthly precipitation
def avgByMonth(m):img = baseCol.filter(ee.Filter.calendarRange(m,m, 'month')).sum().divide(10).clip(india);return img.set('mm',ee.Number(m).format('%02d'));# Map over the list of months to build a collection of long term average monthly precipitation composites
baseMonthCol = ee.ImageCollection.fromImages(months.map(avgByMonth))

3.3 降雨量不足的计算

# Compare the monthly composites against the corresponding baseline to arrive at the deficit figuresdef calc_deficit(img):mm = ee.String(img.get('yymm')).slice(4,6); base = baseMonthCol.filterMetadata('mm','equals',mm).first();deficit = base.subtract(img).divide(base).set('yymm', img.get('yymm'));return deficit;deficitByMonth = pptByYYMM.map(calc_deficit);

3.4 移除干旱区域进行分析

由于许多干旱地区降水很少,由于分母效应低,它们的赤字百分比可能很高。我们将把这些像素的不足百分比设置为零。如果该地区的年降雨量少于 500 毫米,我们将把该地区划分为干旱地区。

# Remove deficit of arid areas from visualisation. We define an area as arid if its annual rainfall is less than 500mm.baseAnnualCol = baseMonthCol.sum();notArid = baseAnnualCol.gt(500);def arid_rem(img):return img.multiply(notArid);deficitByMonth = deficitByMonth.map(arid_rem);

4.测绘

下一步是可视化这 5 张图片,它们是deficitByMonth图片集的一部分。作为参数提供的调色板在最小和最大不足值之间线性插值颜色。我们设置可视化图像的参数,使黄色和红色像素分别代表中度和严重干旱条件。

# Define RGB visualization parameters.
visParams = {'min' : 0,'max' : 1,'palette': ['e6ffe6','ffffff', 'fff633', 'fc0703']
};

创建印度各州的轮廓很重要,这样干旱像素就可以通过州边界来描绘。我们将创建干旱层的 RGB 可视化,并在每个层上叠加州边界,从而创建一个马赛克。首先,我们创建地图轮廓。

# Create an empty image into which to paint the features.
transparent = ee.Image();
empty = ee.Image.cat([transparent, transparent,transparent]);# Select and (optionally) rename bands.
empty = empty.select(['constant', 'constant_1', 'constant_2'], #old names['vis-red', 'vis-green', 'vis-blue']  #new names
);# Paint all the polygon edges with the same number and width, display.
outline = empty.paint(featureCollection = india,color = 1,width = 1);

接下来,我们创建一个干旱的 RGB 图像表示的镶嵌图,然后是地图轮廓,按此顺序。这个顺序很重要,因为否则地图轮廓就看不见了。我们创建一个函数rgb_fc_mosaic来做这件事,并把它映射到defictByMonth中的图像集合上。

def rgb_fc_mosaic(img):#Create RGB visualization images for use as animation frames.imgRGB = img.visualize(**visParams).clip(india)
#   Mosaic the visualization layers and display (or export).mosaic = ee.ImageCollection([imgRGB, outline]).mosaic()return mosaicrgbVis = deficitByMonth.map(rgb_fc_mosaic);

生成动画的最后一步是设置动画帧的参数和它显示图像的速度。我们将为getVideoThumbURL函数提供必要的参数,并将其映射到rgbVis图像集合上。

# Define GIF visualization parameters.
gifParams = {'region': region,'dimensions': 600,'crs': 'EPSG:3857','framesPerSecond': 1
};# Print the GIF URL to the console.
print(rgbVis.getVideoThumbURL(gifParams));

一旦动画 GIF(drugal _ in . GIF)准备就绪,我们将在 GIF 的每一帧上添加月份信息。这可以使用geemap包的add_text_to_gif功能轻松完成。

import pandas as pd
text = pd.date_range(startDate, endDate, freq='MS').strftime("%b-%Y").tolist()# Add the months information on each frame of GIFin_gif = 'C:\\Users\\ADMIN\\Desktop\\satellite\\geemap\\images\\drought_in.gif'out_gif = 'C:\\Users\\ADMIN\\Desktop\\satellite\\geemap\\images\\drought_out.gif'geemap.add_text_to_gif(in_gif, out_gif, xy = ('40%', '0%'), text_sequence = text, font_size=30, font_color = '#ffffff',duration = 1000)

您可以使用show_image功能在 Jupyter Notebook 中可视化带注释的 GIF。

geemap.show_image(out_gif)

5.结论

动画 gif 是表现天气时空变化的非常有用的工具。这些视觉工具以一种吸引读者并易于理解的方式表达了价值千言万语的信息。在这篇博客中,我演示了如何使用 GEE 中的开放数据和 python 中的开源工具创建动画 gif 来捕捉干旱条件随时间的变化。

免责声明:本博客表达的观点是个人观点。

参考

  • 吴(2020)。geemap:一个 Python 包,用于与 Google Earth 引擎进行交互式地图绘制。《开放源码软件杂志》,5(51),2305 页。https://doi.org/10.21105/joss.02305
  • https://developers . Google . com/earth-engine/guides/feature _ collections _ visualizing
  • https://developers . Google . com/earth-engine/guides/image _ visualization
  • https://www . nrsc . gov . in/sites/default/files/pdf/ebooks/Chap _ 13 _ agriculturedraughtmonitoring . pdf

Spearman 系数:广义相关分析的工具

原文:https://towardsdatascience.com/spearman-coefficient-tool-for-a-generalized-correlation-analysis-d15b70d4ff1e?source=collection_archive---------32-----------------------

线性关系并不是相关性分析所能揭示的全部。我们讨论基于等级的相关性,这种相关性更加普遍和有效。

超越线性关系

是常见的数据科学面试问题。

比方说, y = x +噪声,你画出 yx 并试着确定相关系数。会是什么呢?

如果你说的不是零分,大多数面试官不会给你分数。在大多数情况下,相关性这个词会引起变量之间的线性或线性关系的概念。

这里有一个直观的例子说明我的意思。

图片来源:作者创作

这种线性概念来自统计分析或数据科学中最广泛使用的特定相关系数的定义,即皮尔逊系数。

它以著名的英国统计学家卡尔·皮尔逊的名字命名,

其中 cov( XY )是两个随机变量 XY 的协方差,分母由它们的标准差的乘积组成。

卡尔·皮尔逊,图片来源: 维基媒体 ,免费媒体使用

皮尔逊系数足以进行一阶分析,但会受到异常值的影响,无法处理定义的非线性。因此,有必要超越线性相关分析。

对于这样的广义相关性分析,还有一些工具。其中,斯皮尔曼系数是最容易理解和计算的。在本文中,我们将讨论这一点。

斯皮尔曼系数

Spearman 系数是一个等级相关性(两个随机变量的等级之间的统计相关性的度量)。它是以英国心理学家查尔斯·斯皮尔曼的名字命名的,他以因素分析和其他贡献而闻名。

那么,这和我们熟悉的正则相关系数有什么不同呢?斯皮尔曼系数是不同的,因为它不仅仅是关于原始数值,而是关于排名。

什么是等级?

那么,什么是等级呢?这里是维基百科的定义,

一个 排名 是一组条目之间的关系,使得对于任何两个条目,第一个要么“排名高于”、“排名低于”,要么“排名等于”第二个。

取两组数字——1,2,3 和 10,24,102。这些集合之间的精确数字关系可能看起来很奇怪,但如果我们只考虑它们的排名,它们在“一对一”的意义上非常相似。每组中的第二个数大于第一个数,小于第三个数。从这个角度来看,两组是相同的。相对顺序很重要,而不是实际的数值。

这两个集合的皮尔逊(线性)相关系数可以完全不同于 1。但是斯皮尔曼系数将是一个完美的 1。

遵循类似皮尔逊系数的公式,它由下式给出:

唯一的区别是,所有原始数值都被它们的等级值(即,第 1、第 2、第 3、第 50 百分位等的顺序值)所取代。).

Python 的一个例子

使用 Scipy 库,计算两组一维或二维数组的 Spearman 系数非常简单。

让我们用一个典型的例子来展示这两种类型的相关性之间的明显区别,

import numpy as np, matplotlib.pyplot as plt
import scipy.stats as statx = np.linspace(0,10,20)
y = np.log(1+1/(x+0.1))pearson = stat.pearsonr(x,y)
spearman = **stat.spearmanr(x,y)**

如果我们检查它们的值,它们将分别是-0.65 和-1.0。这是视觉表现,

图片来源:作者创作

为什么斯皮尔曼系数是完美的-1.0?因为两个数组的排列顺序完全相同。只要基础函数遵循单调(递增或递减)顺序,这将是正确的。显然,生成函数是高度非线性的(对数的倒数),但这不会影响 Spearman 系数。

Spearman 系数对异常值更稳健

如果我们将一些随机异常值引入到如上所述的相同阵列中,我们将会看到 Spearman 系数对于这些异常值更加稳健,因为等级顺序受到的干扰比实际数值小得多。这是一个带有异常值的样本结果。

图片来源:作者创作

皮尔逊系数受到异常值的干扰(从-0.65 到-0.79),但是斯皮尔曼系数几乎没有变化(从-1.0 到-0.99)。

摘要

在本文中,我们讨论了 Spearman 系数,它相当于更广泛使用的 Pearson 系数,但用于相关数组的排序。这是更广义的相关性分析的一个特例,它着眼于与两个随机变量可以假定的数字数组或数值集相关联的多级属性。

在许多情况下,可以使用超出线性皮尔逊系数的方法,例如在存在异常值的情况下,或者当排序比原始数值更值得分析时。在这种情况下,像斯皮尔曼系数这样的概念对数据科学家来说将会派上用场。

喜欢这篇文章吗?成为 中等会员 继续 无限制学习 。如果您使用下面的链接, ,我将收取您的一部分会员费,而不会对您产生额外费用

https://medium.com/@tirthajyoti/membership

Spec2Vec:质谱相似性度量的下一步

原文:https://towardsdatascience.com/spec2vec-the-next-step-in-mass-spectral-similarity-metrics-b79c3db7511a?source=collection_archive---------37-----------------------

Spec2Vec 使用无监督的机器学习模型从 MS/MS 质谱预测结构相似性

来源:作者

计算两个 MS/MS 质谱之间的相似性是大多数无目标代谢组学工作流程中的重要步骤。

然而,人们早就知道余弦相似性——最流行的相似性度量——有明显的弱点。

Justin van der Hooft 和他的团队开发了一种计算光谱相似性的新方法:Spec2Vec。这对于分析生物标志物发现或天然产物研究中的非靶向代谢组学结果非常有帮助。

让我们看看 Spec2Vec 是如何工作的,以及为什么它是余弦相似性的一个很好的替代品。

为什么质谱相似性度量很重要

最初,非目标代谢组学数据集中的数据点只是一个被测量的实体。我们还不知道它可能是什么分子。因此,我们不能解释它的意义:它是新的东西吗?它的代谢功能是什么?

但是如果你能把这个实体与一个数据库相匹配,比如人类代谢组数据库,那么你就可以给这个实体添加(注释)重要的信息。为了匹配你的测量和数据库中的条目,你需要一个相似性度量

这就是质谱相似性度量的主要用途:

  • 光谱库匹配:将化合物与光谱库(如 HMDB)进行匹配。
  • 模拟搜索:在一个大的光谱库中寻找最佳匹配,寻找结构相关的分子。
  • 质谱网络:在质谱网络中聚类相关化合物。

任何错误都可能代价高昂:如果你找不到匹配,那么你的化合物可能看起来是一个令人兴奋的新药候选物,而实际上,它是一个众所周知的分子;如果你只是在研究的后期才发现这一点,那么你就浪费了很多努力。

你的相似性度量越好,这样的错误就越少发生。

余弦相似性的缺点

余弦相似性测量一个质谱与另一个质谱重叠的程度。如果两个质谱有近乎完美的重叠,那么余弦相似性就很高——它们的分子结构很可能非常相似。

然而,有时你会有质谱不重叠的结构相似的分子——在这种情况下,余弦相似性可能会失败。这是如何发生的:

  1. 多处修饰的大分子:大分子——常见于植物和微生物中——可以很容易地对其分子结构进行几次小的修饰。
  2. 几个片段被移位:如果你将一个有几个修饰的分子片段化,那么不是一个,而是它的几个片段也保留一个修饰。结果,质谱上的几个碎片峰移动。
  3. 很少有峰完全重叠:如果大量片段发生位移,整个光谱就不再与未修饰的分子整齐重叠。
  4. 低余弦相似度:就余弦相似度(重叠)而言,两个分子因此看起来不相关,你会看到低余弦值。
  5. 错误结论:结果,你可能认为一个库没有与你的化合物相似的分子,而事实上它有。这可能会引发你发现了一种新分子的结论,尽管你所发现的只是一种描述良好的分子的轻微修改。

这是一个众所周知的问题,但在 Spec2Vec 之前,还没有提出根本上替代的质谱相似性度量作为解决方案。

Spec2Vec:光谱相似性的无监督学习

Spec2Vec 的基础是一个强大的新假设:如果两个碎片峰经常在数千个质谱中同时出现,那么这可能是因为它们来自同一个分子亚结构。

所以这两个峰值在某种程度上是相关的。现在,如果你在一个分子中只看到这些峰中的一个,而在另一个分子中看到另一个,你可以猜测,同样的亚结构,可能有小的改变,在两个分子中都存在。

学习峰之间的关系允许 Spec2Vec 识别两个光谱是相似的——即使它们的光谱在几个地方没有重叠。来源:作者

Spec2Vec 恰恰实现了这一思想。但是在大规模上,Spec2Vec 获取了成千上万质谱的数据集,并且从该数据集学习哪些峰是相关的。然后就可以用 Spec2Vec 学到的东西来计算任意两个质谱之间的光谱相似度。

如果这行得通,那么一个有一些小修饰的分子——就像前面的例子一样——应该不会骗过 Spec2Vec。现在,这有用吗?

比较 Spec2Vec 和余弦相似度

我们来看两个测试的结果。为了测试,我们使用了来自 GNPS 的大型分子库,其中包含分子结构和它们的光谱。

测试 1:spec 2 vec 分数与结构相似性的相关性如何?

首先,您需要一个基本事实来比较 Spec2Vec 和余弦相似性。贾斯汀和他的团队选取了 12797 种独特的化合物,利用它们已知的分子结构,直接计算出每对分子的结构相似性得分(Tanimoto score)。

对于每个相同的对(总共 81,875,206 对),他们还计算了余弦、修正余弦和 Spec2Vec 得分,但这一次,只使用了库谱,而不是已知的结构。这是结果:

  • Spec2Vec 与结构相似性的相关性更好
  • 只有当光谱几乎完全重叠时,所有指标的表现才会相似。

Spec2Vec 在其得分最高的光谱对中发现了更多高度相似的结构(通过 Tanimoto 评分衡量)。来源:作者

Spec2Vec 与结构相似性(Tanimoto 得分)的高度相关性非常有希望,但这是否转化为更好的文库匹配?

测试 2:spec 2 vec 能在库搜索中找到正确的匹配吗?

在第二次测试中,贾斯汀的团队从光谱库中选择了 1000 个独特的分子,这些分子在库中也有至少四个平面结构等价物(这些在 2D 看起来相同)。这些近乎等价的分子留在了文库中。

接下来的任务是弄清楚具有相同 2D 结构的分子是否会得到最高的相似性分数。

结果与第一个测试一致:同样, Spec2Vec 在所有分数范围内发现了比余弦相似度更可靠的匹配

在所有得分范围内,Spec2Vec 的命中率比余弦相似性的命中率高得多。来源:作者

但是 Spec2Vec 更有优势。

Spec2Vec 要快得多

余弦相似性计算在质谱上非常昂贵,因为它们需要预处理。它们非常昂贵,以至于通常无法对整个大型数据库进行比较。

Spec2Vec 模型一旦经过训练,速度极快:大约比余弦相似性评分快 100 倍。即使将训练 Spec2Vec 模型的时间包括在内,总的来说仍然快 10 倍左右(不考虑余弦相似性方面的任何可能的优化)。

这意味着使用 Spec2Vec,您可以轻松地对大型库运行所有对所有的搜索,而没有任何限制。

Spec2Vec 并不难实现,尽管它是一种更高级的相似性评分方法。

如何将 Spec2Vec 集成到您的工作流程中

你可以用一个易于使用的 Python 开源包来实现 Spec2Vec,这个包可以在 Github:【https://github.com/iomega/spec2vec上免费获得。您可以用 Anaconda(推荐)或 pip 安装它。

您甚至可以从 Zenodo 下载用于上述测试的预训练 Spec2Vec 模型。而且不用训练自己的模型也可以计算质谱相似度。

> >重要注意事项 : Spec2Vec 是一种无监督算法。这意味着你可以在任何一组 MS/MS 谱图上训练它,而不需要任何额外的信息。

这仅仅是开始

Spec2Vec 是一种很好的方法,可能很容易扩展:

  • GC-MS :到目前为止,Spec2Vec 只在 LC-MS 数据上进行了测试。这对于 GC-MS 研究可能更有价值,因为在 GC-MS 测量中,您不能通过根据前体离子的相似性进行预过滤来限制库搜索。
  • 有监督:spec 2 vec 没有监督真是太好了。但是你也可以建立一个监督模型:例如,你可以训练一个模型,直接从光谱嵌入中预测 Tanimoto 分数。这可能会给你更精确的模型。
  • 改进的 Mass2Motifs : Spec2Vec 矢量嵌入有可能被用来训练更强大的 Mass2Motifs。
  • 改进的网络注释传播(NAP) :相似性得分在构建质谱网络中至关重要,因此如果 Spec2Vec 产生更多有用的网络,它也可以改进您的注释并加快结构解析。

总的来说,目前使用余弦结构相似性的任何工具都可能受益于集成 Spec2Vec 作为替代的相似性度量。

进一步阅读

如果您想了解更多关于 Spec2Vec 的信息:

  • 试试 Spec2Vec python 包
  • 在 GNPS 内试用 Spec2Vec
  • 阅读 Spec2Vec 论文;和
  • 查看弗洛里安·胡伯的 Spec2Vec 教程。

将机器学习应用于组学研究需要帮助吗?

我们可以帮助您的研究团队为可重复且易于部署的机器学习模型建立合适的基础设施。刚刚取得联系。

使用 Python 实现实时语音识别

原文:https://towardsdatascience.com/speech-recognition-in-real-time-using-python-fbbd62e6ff9d?source=collection_archive---------4-----------------------

现场转录分步指南

真诚媒体在 Unsplash 上拍摄的照片

在这篇文章中,我将向你展示如何使用 Python 将你的演讲实时转换成文本。我们将编写一个程序,理解我们所说的话,并将其翻译成书面文字。这种翻译被称为语音识别。对于机器学习和人工智能来说,语音识别是一个非常令人兴奋和有趣的领域。

在我之前的帖子中,我已经讨论过类似的主题,比如使用谷歌的语音识别 API 将语音转换为文本,以及使用 Python 构建语音情感识别器。在这些项目中,识别和训练是通过静态音频文件完成的。今天,我们将在这个项目中做一些更先进、更非凡的事情。我们将学习如何实时地将我们的语音转换成文本。

如果你准备好了,让我们开始工作吧!

目录

  • 入门
  • 第一步——库
  • 第二步——直播
  • 第三步—异步功能
  • 最后一步——测试实时识别器

入门指南

在今天的教程中,我们将使用 AssemblyAI 的实时转录 API 。作为一名软件工程师和技术爱好者,很高兴看到语音识别领域正在飞速发展。我相信这些发展正使我们的生活变得更容易。

例如,实时转录可以有所帮助的一个领域是在线讲座。在听导师讲课的同时,我们也可以把导师的讲话当字幕来读。这一功能将使听力障碍者的学习过程更加容易。这只是一个基本的例子;我确信有许多领域可以实现实时转录。

顺便说一下, AssemblyAI 的 API 是免费访问的。创建帐户后,您将获得一个唯一的 API 密钥。我们需要这个 API 密匙来使用服务和特性。

步骤 1 —库

我们将在这个项目中使用三个 Python 库。它们可以列举如下:PortAudio、PyAudio 和 Websockets。以下是它们的简短定义。

PortAudio 是一个免费、跨平台、开源的音频输入/输出库。这也是让后续库 PyAudio 工作的先决条件库。

公文。

PyAudio 是一个免费的开源库,可以在各种平台上播放和录制音频文件。这个库是 PortAudio 的扩展。

官方文件。

Websockets 是建立在 asyncio 之上的依赖项,asyncio 是 Python 的标准异步 I/O 框架。它提供了一个优雅的基于协程的 API。

官方文件。

让我们开始安装它们。

我们将在终端窗口中运行以下代码行。要安装 PortAudio,我们必须使用名为 brew 的 python 库管理器。

brew install portaudio

之后,是时候安装另外两个库了。这次我们使用另一个 python 库管理器 pip。您知道我们可以在一行代码中安装一个或多个库吗?

pip install pyaudio websockets

步骤 2 —实时流

我们的图书馆已经安装好了。是时候准备好流媒体系统了!

是时候打开我们的编码平台,开始编码了。我们的代码是静态的,所以我将使用 Atom 文本编辑器。整个代码将在一个 python 文件中。易于运行。

import pyaudioFRAMES_PER_BUFFER = 3200
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 16000
p = pyaudio.PyAudio()stream = p.open(format=FORMAT,channels=CHANNELS,rate=RATE,input=True,frames_per_buffer=FRAMES_PER_BUFFER
)

在上面的代码中,我们定义了流系统的参数。说到细节,音频世界真的很复杂。我建议在网上搜索每一个变量,以便更好地理解它们。

步骤 3 —异步功能

在这一步,我们将编写一个异步函数,用 API 发送和接收音频数据。异步函数在主题协程和任务下。

用异步语法声明的协同程序是编写 asyncio 应用程序的首选方式。因为我们希望实时地传输语音,所以这是一个很好的方法,可以在终端窗口中显示识别出的单词,但需要等待一段时间。

发送和接收音频数据

首先,让我们导入 python 库。

import websockets
import asyncio
import base64
import json
from configure import auth_key

现在,我们将编写一个函数,我将其命名为: send_receive_data( )。在这个函数中发生了许多事情:

  • 将程序连接到 AssemblyAI 的端点 url。
  • 音频数据被传输到 API 进行识别。
  • API 返回一个 json 数据和识别的语音。

这基本上是一条双向高速公路,我们通过笔记本电脑的麦克风发送语音数据。然后,从 API 接收识别的语音。一切都发生得太快了。

是不是很酷?你猜怎么着,没有高峰时间:)

# the AssemblyAI endpoint we're going to hit
URL = "wss://api.assemblyai.com/v2/realtime/ws?sample_rate=16000"async def send_receive_data():print(f'Connecting websocket to url ${URL}')async with websockets.connect(URL,extra_headers=(("Authorization", "our API key"),),ping_interval=5,ping_timeout=20) as _ws:await asyncio.sleep(0.1)print("Receiving SessionBegins ...")session_begins = await _ws.recv()print(session_begins)print("Sending messages ...")async def send():while True:try:data = stream.read(FRAMES_PER_BUFFER)data = base64.b64encode(data).decode("utf-8")json_data = json.dumps({"audio_data":str(data)})await _ws.send(json_data)except websockets.exceptions.ConnectionClosedError as e:print(e)assert e.code == 4008breakexcept Exception as e:assert False, "Not a websocket 4008 error"await asyncio.sleep(0.01)return Trueasync def receive():while True:try:result_str = await _ws.recv()print(json.loads(result_str)['text'])except websockets.exceptions.ConnectionClosedError as e:print(e)assert e.code == 4008breakexcept Exception as e:assert False, "Not a websocket 4008 error"send_result, receive_result = await asyncio.gather(send(), receive())

send_receive_data() 函数全部设置完毕!

现在,让我们把它放在一个循环中,这样我们的程序就能连续不断地听到我们的声音。由于我们不想让它永远运行,所以有一个超时,也就是 1 分钟。阿辛西奥会为我们做这件事。

asyncio.run(send_receive_data())

最后一步—测试实时识别器

在终端中运行 main.py 后,程序开始监听。并在那一刻显示识别的语音。我看了这篇文章的引言部分,下面是结果。

作者截图。

作者截图。

恭喜你。在本实践教程中,我们学习了如何使用 Python 构建一个程序来检测我们的语音并将其实时转换为文本。我喜欢创建和从事这些项目,因为它们是机器学习和人工智能在我们日常生活中的自然应用。希望你喜欢阅读本指南,并在今天学到一些新东西。

我是贝希克·居文,我喜欢分享关于编程、教育和生活的故事。订阅我的内容,保持灵感。泰,

如果你想知道我写的是什么样的文章,这里有一些:

  • 使用 Python 构建人脸识别器
  • 分步指南—用 Python 构建预测模型
  • 使用 Python 构建语音情感识别器

如何使用 Python 执行语音识别

原文:https://towardsdatascience.com/speech-recognition-python-assemblyai-bb5024d322d8?source=collection_archive---------7-----------------------

用 Python 中的 AssemblyAI API 执行语音转文本

马特·博茨福德在 Unsplash 上的照片

什么是语音识别

语音识别,也被称为自动语音识别语音到文本,是位于中的的一个领域,是计算机科学计算语言学的交叉,开发某些技术使计算机系统能够处理人类语音并将其转换为文本格式。换句话说,语音识别方法和工具用于将语音从口头格式翻译成文本

在语音识别设置中使用的最佳执行算法利用了来自人工智能和机器学习领域的技术和概念。这些算法中的大多数都随着时间的推移而改进,因为它们能够通过交互来增强它们的能力和性能。

语音识别的应用可以在许多行业中找到,这些行业使用这种技术来帮助用户、消费者和企业提高效率。苹果 Siri、亚马逊 Alexa、T21 和谷歌助手等虚拟代理利用语音识别技术,通过语音命令访问某些功能。语音识别的其他应用包括汽车中的声控导航系统,医疗保健中的文档口述,甚至安全设置中的语音认证

由于对语音识别技术的需求不断增加,该领域已经取得了巨大的发展,这使得开发人员和组织可以很容易地将它们分别集成到他们的代码库和产品中。在接下来的章节中,我们将探索如何用 PythonAssemblyAI API只用几行代码就能让执行语音识别

使用 AssemblyAI API 进行语音到文本转换

AssemblyAI 提供强大的语音转文本 API ,由先进的 AI 提供动力,使用户能够准确转录音频和视频文件。在今天的指南中,我们将使用这个 API 来对 mp3 音频文件执行语音识别。

如果你想跟随这个教程,你所需要的就是一个 API 密匙,如果你注册了一个免费的 AssemblyAI 账户,你就可以得到它。一旦您这样做了,您的密钥应该会出现在您的帐户部分。

出于本教程的目的,我准备了一个简短的音频文件,您可以在下面找到。请随意创建您自己的或使用我已经为您创建的。

测试我们将在示例语音到文本教程中使用的音频文件——来源:作者

现在,我们将使用requests库来调用 AssemblyAI API,它需要您获得的 API 键以及下面定义的头。

导入请求库并定义请求头—来源:作者

下一步,是读入文件并上传到 AssemblyAI 主机服务上,以获得一个链接,然后我们将使用它来转录实际的音频。

上传音频文件到 AssemblyAI 主机服务—来源:作者

现在,我们已经成功地将音频文件上传到 AssemblyAI 的托管服务,我们可以继续将上一步中授予的上传 url 发送到 AssemblyAI 的转录端点。来自端点的示例响应显示在要点末尾的注释中。

向抄本端点发送请求—来源:作者

最后,我们可以通过提供从上一步的转录端点的响应中收到的转录 ID 来访问转录结果。请注意,我们将不得不重复 **GET** 请求,直到响应中的状态为completederror,以防音频文件处理失败。

在成功完成时接收到的来自抄本端点的示例响应作为评论被共享在下面的要点的末尾。

发送重复的 GET 请求,直到失败或成功完成—来源:作者

最后,假设文件处理成功结束,我们可以将最终响应写入文本文件。

将输出脚本写入文件—来源:作者

对于我们的示例音频文件,我们从 AssemblyAI 语音到文本 API 获得的输出将被写入输出文本文件

你知道,电视上的恶魔就是这样。让人们暴露自己在电视上被拒绝或被恐惧因素羞辱。

这是相当准确的!

完整代码

我们使用 AssemblyAI 语音转文本 API 的完整代码可以在下面的 GitHub Gist 中找到。概括地说,代码将从您的本地系统上传一个音频文件到 AssemblyAI 主机服务,然后将它提交给执行语音到文本任务的抄本服务。最后,我们处理输出响应,并将其存储到本地文件系统的一个文本文件中。

使用 AssemblyAI API 实现语音转文本的完整代码来源:作者

请注意,在本教程中,我已经上传了一个本地文件到 AssemblyAI 托管服务,但你甚至可以从任何云服务(如 AWS)提交音频 URL。更多细节和例子,可以参考 AssemblyAI 官方文档中的这一节。

最后的想法

语音识别是一个快速发展的领域,它极大地受益于机器学习、人工智能和自然语言处理的巨大进步。由于对语音到文本应用程序的需求不断增长,出现了大量能够快速访问这些技术的工具。

在今天的文章中,我们探索了如何用 Python 快速执行语音识别,只需几行代码,使用 AssemblyAI,这是一个强大的 API,被全球成千上万的组织使用。该 API 提供了一系列我们在本文中没有涉及的特性,但是您可以在这里探索。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

使用 IBM 的语音到文本 API 的语音识别

原文:https://towardsdatascience.com/speech-recognition-using-ibms-speech-to-text-api-9956254c46d4?source=collection_archive---------39-----------------------

使用 Python 从音频记录中轻松提取对话

Avel Chuklanov 在 Unsplash 上拍摄的照片

在本文中,我们将学习如何使用 IBM 的语音转文本 API 从音频记录文件中识别语音。我们将使用 API 的免费版本,它可能有一些限制,例如音频长度限制。我将在本文后面分享更多关于 API 的细节。让我先给你一些关于语音识别在我们日常生活中使用的背景信息。

背景

如果你正在阅读这篇文章,我肯定你听说过“人工智能”这个术语及其重要性。我可以很容易地看出,人工智能在现实生活中的良好应用是语音识别。

从音频中识别语音基本上允许我们通过说话而不是打字来节省时间。这使得使用我们的科技产品变得更加有趣和容易。这项技术还帮助我们无需编写一行代码就能与这些设备进行交互。想象一下,人们必须知道编程才能向 Alexa 或 Siri 发出命令。那太疯狂了。😊

作者设计

我迫不及待地想向您展示实际运行的语音识别器。我们开始工作吧。下面是我们在这个语音识别项目中将遵循的步骤。

目录:

  • 语音识别云服务
  • 步骤 1 —图书馆
  • 步骤 2-导入音频剪辑
  • 步骤 3 —定义识别器
  • 步骤 4 —语音识别器的作用
  • 最后一步—导出结果

语音识别云服务

许多大型科技公司都有自己的认可模式。我将在这里分享其中的一些给你看大图。这些 API 通过云工作,只要有互联网连接,就可以从全球任何地方访问。此外,它们大多数是付费服务,但可以免费测试。例如,微软通过 Azure 云账户提供为期一年的免费访问。

以下是一些最受欢迎的语音转文本云服务:

  • 谷歌云
  • IBM 沃森
  • 微软 Azure
  • 亚马逊转录

步骤 1 —图书馆

对于这个项目,我们将只需要一个库。这就是演讲认知。SpeechRecognition 是免费和开源的。它支持多种语音识别引擎和 API。如;微软 Azure 语音、谷歌云语音、IBM Watson 语音转文本 API 等等。对于这个项目,我们将测试 IBM Watson 语音转文本 API。您可以从这里随意查看 SpeechRecognition 软件包的源代码和文档。

让我们从安装包开始。我们将使用 pip,它是一个 Python 库管理器。

pip install SpeechRecognition

安装过程完成后,我们可以打开代码编辑器。也可以用 Jupyter 笔记本。

import speech_recognition as s_r

步骤 2-导入音频剪辑

我用电脑录了一份语音备忘录。它是 m4a 格式的,但是识别器不能识别 m4a 格式的。这就是为什么我必须把它转换成 wav 格式。

audio_file = s_r.AudioFile('my_clip.wav')

作者图片

步骤 3 —定义识别器

在这一步,我们要做的就是定义语音识别器。前面,我们已经导入了库。现在,我们将创建一个新变量,并将识别属性赋给它。

rcgnzr = s_r.Recognizer()

步骤 4 —语音识别器的作用

表演时间到了!我们将在音频文件中运行 IBM 的语音转文本。在运行识别器之前,我将继续运行名为“ adjust_for_ambient_noise ”和“ record ”的其他函数,这将清除一些噪声并优化音频。这样,我们的识别器将能够返回更准确的结果。

with audio_file as source: rcgnzr.adjust_for_ambient_noise(source) clean_audio = rcgnzr.record(source)

太好了,现在我们有更清晰的录音了。现在,让我们继续运行 IBM 的语音识别器。(我花了几个小时才弄明白 IBM 语音转文本 API 是如何与 SpeechRecogniton python 库集成的)。下面是从 API 调用识别器的最佳方式:

recognized_speech_ibm = rcgnzr.recognize_ibm(clean_audio, username="apkikey", password= "your API Key")

注意:没有 API 键,IBM 的 API 就无法工作。这就是为什么我们需要从 IBM Watson 页面获取一个。我已经创建了一个帐户来测试这个语音到文本的模型。我喜欢 IBM 模型的一点是,我仍然可以使用 lite 帐户每月录制 500 分钟,这对学习来说绰绰有余。

最后一步—导出结果

我们差不多完成了。是时候检查结果了。我们的识别器在上一步中检测到了音频文件中的语音。我们将继续检查它是如何工作的。如果我们对结果满意,我们将把结果导出到一个文本文档中。

为了检查识别的语音,让我们打印出识别的变量:

print(recognized_speech_ibm)

作者图片

看起来不错。它对我的录音做了很大的认可。我在读这篇文章中的一段。如果您对结果不满意,有许多方法可以预处理音频文件以获得更好的结果。这是一篇很好的文章,向展示了关于语音识别以及如何提高识别器预测能力的更详细的信息。

现在,我将把识别的语音导出到一个文本文档中。我们将看到消息“准备好了!“出口完成后,在我们的码头。

with open('recognized_speech.txt',mode ='w') as file:    file.write("Recognized Speech:") file.write("\n") file.write(recognized) print("ready!")

恭喜你。!如果您正在阅读这一段,那么您已经完成了语音识别器的构建。希望你喜欢这个实践教程,并在今天学到一些新东西。练习编程技能的最好方法是做有趣的项目。我已经分享了很多像这样的实践项目。如果您在实施该计划时有任何问题,请随时联系我。

我们来连线。查看我的博客和 youtube 以获得灵感。谢谢你,

为您提供更多动手项目:

带有时间戳的语音识别

原文:https://towardsdatascience.com/speech-recognition-with-timestamps-934ede4234b2?source=collection_archive---------8-----------------------

使用 Python 和 vosk API 进行离线和免费语音识别

带时间戳的语音识别。作者图片

在这篇文章中,我将告诉你如何使用 Python 实现带有时间戳的离线语音识别。以下示例中的代码将允许您识别音频文件并获得置信度、每个单词的开始和结束时间、超过 15 种语言、离线和免费。****

我将使用 vosk API 。参见下面的文章,了解如何设置 vosk 库。在本文中,您还可以找到无需时间戳即可进行离线语音识别的代码。

**https://medium.com/@andimid/offline-foreign-speech-recognition-32d8d63de2dc

首先,安装库并下载模型。

重要提示 —音频必须为 wav 单声道格式。如果你的音频文件以不同的格式编码,用一些免费的在线工具如 this 将它转换成 wav 单声道。

Vosk 的输出数据格式

Vosk 模型以 JSON 格式输出结果。语音识别的最终结果是results变量,它是 JSON 字典的列表。该列表的每个元素具有以下结构:

第一个元素result —也是一个 JSON 字典列表。这个变量实际上代表一个句子,并包含一个句子中每个单词的四个变量:置信度、开始和结束时间以及识别的单词。

第二个元素,text组合了程序确定为句子的所有已识别单词。它可以用于简单的语音识别,如上文所述。

为了方便起见,我创建了一个简单的Word类,它以那种格式表示一个单词。除了用于实例化类的__init__方法之外,它只有一个打印对象的方法。我们以后会用到它。

程序的本质

一旦我们得到了results变量,我们只需要遍历它包含的列表来获得我们需要的信息。

该程序输出以下内容:

程序的结果。作者图片

我使用了与上一篇文章相同的音频文件。在这里,我从维基百科上的一篇语音识别文章中读到了一段文字。具体来说,这里识别的短语是:“某些语音识别系统需要训练(也称为“注册”),其中…”。

在上面的代码示例中,我保持了大纲的简单。对于可靠的、防御性的编程,我建议增加对文件存在和 vosk 模型、文件格式等的检查。在git lab repo中有更详细评论的完整程序。

https://gitlab.com/Winston-90/foreign_speech_recognition/-/tree/main/timestamps

感谢您的阅读!

  • 我希望这些材料对你有用。在 Medium 上关注我,获取更多类似的文章。
  • 如果您有任何问题或意见,我将很高兴得到任何反馈。在评论里问我,或者通过 LinkedIn 或者 Twitter 联系。
  • 为了支持我作为一个作家,并获得数以千计的其他媒体文章,使用我的推荐链接获得媒体会员资格(不收取额外费用)。**

使用 AWS 转录、S3 和 Lambda 将语音转换为文本

原文:https://towardsdatascience.com/speech-to-text-using-aws-transcribe-s3-and-lambda-a6e88fb3a48e?source=collection_archive---------5-----------------------

使用 AWS 转录、S3、Lambda 进行语音到文本转换,并使用 SNS 和 CloudWatch 事件输出通知。

Miguel Henriques 在 Unsplash 上拍摄的照片

peech to text 是将音频转换成文本的过程。对于计算机软件和程序,音频文件几乎不可能用于可视化/分析或以有意义的方式从中获取数据。因此,需要将这些音频文件转换为文本,然后才能用于分析和收集数据。虽然在当前的技术世界中,语音转文本似乎非常简单,但它涉及许多语言模型和算法,以提供接近 100%的准确性。

目前,有许多由软件提供商创建的工具,他们创建了自己的模型和算法来提供这种语音到文本的服务。在本文中,我将介绍 AWS 提供的一种名为 AWS 转录的语音转文本服务。

自动气象站转录

AWS Transcribe 是由 Amazon Web Services 提供的语音到文本解决方案,它以非常快速和高准确性而闻名。AWS Transcribe under the hood 使用了一个名为 ASR(自动语音识别)的深度学习过程,来快速、更准确地将音频转换为文本。它内部也有一个名为 Amazon Transcribe Medical 的独立服务,也用于医疗文档应用程序。

AWS 转录有一个创建良好的 API,程序可以自动将音频文件转换为文本文件的转录工作。但是,一旦您开始一个转录工作,因为它可能需要时间取决于文件,AWS 转录不会发送输出在同一个请求作为响应。因为我们需要连续轮询来检查转录作业是否完成,或者我们需要某种事件触发器来识别作业的状态。在这篇文章中,我们将探讨两种这样的事件触发器,它们将使得从头到尾自动转录成为可能。下面是我们将在本文中完成的内容的简要概述。

  • 创建两个 S3 存储桶,作为 AWS 转录的输入存储桶和输出存储桶。
  • 使用 python 创建一个 Lambda 函数,以在新文件上传到输入 S3 存储桶时触发 AWS 转录。
  • 使用 S3 事件完成转录后,发送一封包含转录作业详细信息的电子邮件。
  • 使用 CloudWatch 事件完成转录后,发送一封包含转录作业详细信息的电子邮件。

整体架构

  • 场景一—使用 S3 事件发送电子邮件
  • 场景二—使用云监控事件发送电子邮件

以上是我们在本文中试图实现的总体架构。我们可以选择场景一或场景二来接收电子邮件通知。现在没有更多的解释,让我们深入实现。将按以下顺序在 AWS 中创建以下服务。

  • Lambda 触发转录的 IAM 角色
  • λ函数
  • 输入和输出 S3 存储桶
  • 社交网络话题

为 Lambda 函数创建具有转录权限的 IAM 角色

因为我们的 Lambda 函数将代表我们自己触发 AWS 转录,所以我们需要允许 Lambda 函数调用转录服务。为此,让我们创建一个新的 IAM 角色,它将被我们的 Lambda 函数使用。

转到 IAM 仪表板,选择创建一个角色,并作为 AWS 服务,选择 Lambda。

接下来,从策略中为我们的 IAM 角色选择以下两个策略。

  • CloudWatchLogsFullAccess
  • amazontranscribefullacess

接下来,提供一个角色名称并创建新角色。

为触发转录作业创建 Lambda 函数

接下来,让我们创建 Lambda 函数,当我们上传一个新文件到我们的输入 S3 桶(我们将在下一步创建)时,它将触发 AWS 转录。

转到 Lambda 仪表板并创建函数。选择 Python 作为运行时,并在执行角色上选择我们在上面创建的角色。

将下面给出的代码复制到 lambda 函数。代码解释如下。

当 Lambda 功能被触发时,将遵循以下步骤。

  • 该功能将由 S3 事件触发。因此,首先我们将提取 S3 存储桶和触发事件的文件的详细信息。
  • 接下来,我们将创建一个转录 API 所需的作业名称。为了保持唯一性,我们将 UUID 附加到文件名的末尾。
  • 接下来,我们用所需的参数调用 start_transcription_job 。这里对于 OutputBucketName,我们需要指定我们接下来要创建的输出 S3 存储桶。因此,现在您可以完全删除该字段。

创建输入 S3 桶和 S3 事件以触发 Lambda 函数

接下来,让我们开始创建我们的 S3 桶。我们将创建两个存储桶,首先,让我们创建我们的输入存储桶,我们的音频文件将被上传到这里。

创建 S3 存储桶后,转到属性和事件通知。这里是我们配置 S3 事件的地方,每当一个新的对象被添加到这个桶中时,它就触发我们创建的 Lambda 函数。

单击创建事件通知,然后提供一个名称。在事件类型上,选择所有对象创建事件。这将确保在上传新对象、对现有对象进行重命名或者将新对象直接复制到存储桶时创建一个事件。

对于目的地,选择我们创建的 Lambda 函数。现在,这将确保无论何时在这个桶中创建新文件,我们的 Lambda 函数都将被触发,从而触发 AWS 转录。

创建输出 S3 存储桶并授予写权限

现在让我们开始创建我们的输出 S3 桶,从我们的自动气象站转录的结果将是可用的。这里我们需要提供对 lambda 函数的写访问,这样它就可以在这个桶上写输出结果。首先,创建一个新的 S3 存储桶。

然后,我们首先需要创建一个新策略,指定对此 S3 存储桶的写访问权限。转到 IAM 控制面板并导航到策略。然后点击 Create Policy,选择 JSON 并添加下面的策略。

{"Version": "2012-10-17","Statement": [{"Action": ["s3:PutObject"],"Resource": ["arn:aws:s3:::transcribe-output-tutorial/*"],"Effect": "Allow"},{"Action": ["s3:ListBucket"],"Resource": ["arn:aws:s3:::transcribe-output-tutorial"],"Effect": "Allow"}]
}

接下来,为了授予我们为 Lambda 创建的写入 S3 存储桶的角色。为此,再次返回到我们之前创建的 Lambda 角色,并将这个新创建的策略附加到它。

最后一步,确保编辑我们创建的 Lambda 函数,将 OutputBucketName 作为我们现在创建的新 S3 存储桶名称。该名称应该只是没有任何前缀的存储桶名称。(例如:-转录-输出)

现在,我们配置了所有需要的组件,而没有输出通知电子邮件。让我们首先试着测试配置,以检查我们的系统目前是否工作正常。转到输入 S3 桶,上传一个 mp4 文件到我们的输入桶。如果一切顺利,如果你去转录仪表板和转录作业下,你应该看到一个由我们的 Lambda 函数触发的新作业。

几分钟后,状态将变为 completed,我们将看到输出。也检入输出文本对象的输出 S3 存储桶。如果一切正常,让我们进入下一步。

让我们继续,一旦转录作业完成,就触发电子邮件通知。我们将介绍两种实现这一点的方法,使用 S3 事件和 CloudWatch 事件。但是对于这两者,我们需要先创建一个 SNS 主题。

创建社交网络话题

转到 SNS 仪表板并创建一个主题。作为类型选择标准。

接下来,如果我们要使用 S3 事件,我们需要授予 S3 存储桶访问该主题的权限。为了做到这一点,在 AccessPolicy 下,在策略的末尾添加以下行。

{"Sid": "s3","Effect": "Allow","Principal": {"Service": "s3.amazonaws.com"},"Action": "SNS:Publish","Resource": "{YOUR_SNS_TOPIC_ARN}"}

现在我们可以完成主题的创建了。接下来,我们需要为这个主题创建一个订阅,它将使用电子邮件协议。

点击创建订阅并选择协议为电子邮件。

请确保在输入您的电子邮件后通过您的电子邮件确认订阅。

既然我们的 SNS 主题已经创建,让我们继续尝试实现场景一,即使用 S3 事件触发发送电子邮件。

AWS S3 事件触发器

主要思想是每当 AWS 转录服务上传一个新文件到我们的输出 S3 桶时,触发我们的 SNS 主题。要做到这一点,首先去我们的输出 S3 桶。转到 properties 并创建 even notifications,就像我们对输入桶所做的那样。

事件类型将与输入桶相同,而这次的目的地将是 SNS 主题。

现在,当 AWS 转录输出输出结果到我们的桶,我们将自动收到一封电子邮件。您可以通过重命名当前在我们的输入桶中的对象来尝试这一点。现在转录完成后,它被上传到我们的输出 S3 桶你会收到一封电子邮件。

使用 CloudWatch 事件

场景二是使用 CloudWatch 事件触发电子邮件。当转录作业启动时,AWS transcriptor 开始向 CloudWatch 发送事件。因此,这里的概念是每当转录完成时,使用这些 CloudWatch 事件创建一个触发器。首先让我们来看看 AWS CloudWatch、事件和规则。

点击创建规则。这里我们需要定义一个规则,当我们的转录作业进入完成状态或失败状态时,我们需要一个触发器。

{"source": ["aws.transcribe"],"detail-type": ["Transcribe Job State Change"],"detail": {"TranscriptionJobStatus": ["COMPLETED","FAILED"]}
}

作为目标添加我们的 SNS 话题。接下来,通过命名来创建规则。

现在尝试重命名或添加一个新对象到我们的输入 S3 桶,最后,我们会收到一封电子邮件通知转录作业的状态。(如果您已经在同一主题中添加了 S3 事件,请确保将其删除,否则会触发两封电子邮件。)

这就是本文的全部内容。在 AWS 转录完成一项工作后,我们可以通过许多其他方式来触发事件。但我会让你自己找到解决方法。如果你想了解更多关于 AWS 转录以下是文件。感谢您阅读这篇文章和快乐编码😁😁😁

https://docs.aws.amazon.com/transcribe/index.html

机器学习的速度立方

原文:https://towardsdatascience.com/speed-cubing-for-machine-learning-4bf90170e2ee?source=collection_archive---------44-----------------------

第 3 集:使用多 GPU、Dask 和 CuPy

布莱斯·巴克在 Unsplash 拍摄的照片

介绍

《机器学习的速度立方》前情提要……

在第 1 集【1】中,我们描述了如何使用 CPU、多线程和云资源,尽可能快地生成 3D 数据,以最终养活一些生成性对抗网络。我们达到了每秒 20 亿个数据点的速度。

在第 2 集[2]中,我们使用了一个本地 GPU,一个名为 RAPIDS 的框架,以及 CuPyVisPy 等库。我们的速度甚至更快,几乎达到了每秒 50 亿个数据点!

在这最后一集,我们仍将关注速度,但这一次,我们将在几个 GPU的帮助下执行实际计算

技术设置

所有实验都将通过 JupyterLab 笔记本电脑在云中实例化的虚拟机(VM)上进行。该虚拟机具有 16 个英特尔至强 vCPUs @ 2.30GHz、60 GB 内存和 8 个 NVIDIA Tesla K80 GPUs(图 1)。虚拟机预先配置了所有必要的 NVIDIA 驱动程序和 CUDA 11.0 工具包。操作系统是 Linux (Debian 4.19 64 位),安装了 Python 3.7。

图一 : NVIDIA Tesla K80,24GB GDDR5,4992 个 CUDA 核心(来源:nvidia.com)。

我们还需要以下额外的图书馆/包:

  • ****Dask:Python 中并行计算的开源库[3]。使用命令python -m pip install "dask[complete]" 进行安装。
  • CuPy :用 NVIDIA CUDA 加速的开源阵列库[4]。它相当于 GPU 的 NumPy。使用命令pip install cupy-cuda110进行安装(以匹配我们的 CUDA 版本)。
  • Dask CUDA :由rapidsai【5】托管的实验性开源库,帮助多 GPU 系统中 Dask workers 的部署和管理。使用命令pip install dask-cuda进行安装。**

安装完成后,我们可以像这样导入库:

**import** dask.array **as** da
**import** cupy
**from** dask_cuda **import** LocalCUDACluster
**from** dask.distributed **import** Client

我们开始吧!

我必须承认,成功运行多 GPU 数值模拟是一段不平凡的旅程。事实上,大多数在线材料都专注于专门用于加速深度学习模型训练的内置方法。然而,当计算在这个框架之外执行时,我缺乏完整的文档。此外,多 GPU 计算比多 CPU 计算更棘手:内存分配和数据管理可能难以处理。这就是达斯克的用武之地。

存储数据:Dask 数组

Dask 数组协调许多数组(在 API 中足够“像 NumPy ”,比如 CuPy 数组),在一个网格中排列成块(图 2)。将大数组分割成许多小数组,可以让我们使用所有内核在比可用内存更大的数组上执行计算。Dask 数组受益于多维阻塞 算法任务 调度

****图 2:Dask 阵列的结构。

第一个实验:单 CPU

我们首先生成一个二维 Dask 数组,其中的数据是从高斯分布中抽取的随机样本。我们将平均值设为 10,标准差设为 1。分布大小是 sz=100000,块大小是 ch=10000。这导致一个 80 GB 的 Dask 数组包含 100 个块,每个块是一个 800 MB 的 numpy 数组。当您在笔记本中打印 Dask 数组时,Dask 会自动显示数据的图形表示以及有关其体积、形状和类型的信息(图 3)。这个过程完全是“懒惰”的,这意味着还没有数据被加载到内存中。请注意,dask 需要执行 100 个任务来创建 Dask 数组。

图 3 :分块随机 numpy 数组的 Dask 数组。

对于我们的基准测试,我们希望执行下面的简单计算:获取数据的一个子集,将所有值递增 1,然后计算平均值

图 4 显示了我们将要计算平均值的子集的信息。我们可以看到,Dask 现在需要执行 300 个任务来获得该子集,这是一个 20GB 的 Dask 数组,有 100 个块,每个块是一个 200 MB 的 numpy 数组。

图 4 :原始数据子集(类型为 numpy.ndarray )。

现在,实际的计算可以使用.compute()方法来完成。我们还将调度器参数指定为'single-threaded',因此 Dask 将在一个单 CPU** 上执行任务。整个过程用时 11 分 41 秒。最终值与预期值接近(1.0e-5)移动平均值(图 5)。**

图 5 :单个 CPU 上的计算:耗时 701 秒。

第二个实验:多 CPU

在 Dask 中,从单个 CPU 到多个 CPU 非常简单:您所要做的就是将调度器的参数改为'threads'。这样,Dask 将使用所有可用的内核来执行并行计算(图 6)。这一次,使用我们 VM 的 16 核,只需要 55 秒就可以完成任务。计算出的平均值仍然接近(1.7e-5)预期值 11(图 7)。

图 6 :运行中的 16 个内核(使用‘htop’命令)。**

****图 7:16 核计算:耗时 55 秒。

第三个实验:单个 GPU

从 CPU 切换到 GPU 非常容易。只需要知道一个小技巧,这就是 CuPy 库的用武之地。在第 2 集[2]中,我们将我们的 numpy 数组移动到我们的 GPU 设备中(我们将这个操作称为数据 传输)。这里我们用参数cupy.random.RandomState直接在随机状态下指定数组的类型。因此,我们告诉 Dask,我们使用 cupy 数组而不是 numpy 数组,以确保计算将在 GPU 上完成(图 8)。同样,这是一个“懒惰”的操作,还没有数据被加载到视频存储器中。

图 8 :分块随机 cupy 数组的 Dask 数组。

子集完全相同,只是块的数据类型发生了变化,现在是 cupy.ndarray 而不是之前的 numpy.ndarray (图 9)。

图 9 :数据子集(类型为 cupy.ndarray )。

对于实际计算,使用第一个实验中的相同调度程序参数(单个 CPU)。在一个单 GPU** 上,它需要将近 12 秒,因此几乎比 16 个内核快 4.6 倍。计算出的平均值仍然很好(1.8e-5)(图 10)。**

图 10 :单个 GPU (1x Tesla K80)上的计算:几乎需要 12 秒。

第四个实验:多 GPU

对于最后这个实验,我们首先必须定义一个支持 GPU 的集群,这就是 Dask CUDA 库的用武之地。我们从 Dask CUDA 库中导入LocalCUDACluster,并设置参数CUDA_VISIBLE_DEVICES,这样我们就可以选择特定的 GPU。然后,我们为该集群构建一个分布式客户端(图 11)。总而言之,Dask 创建了两个对象:

  • ****集群:由 8 个 workers 和 8 个 cores 组成(都是我们的 GPU)。
  • ****客户端:通过本地 url 访问(此处http://127 . 0 . 0 . 1:8787/status))。您可以使用一个非常丰富的仪表板来监控整个过程。

图 11 :本地 CUDA 集群及其 DASK 分布式客户端。

从那里,我们可以像以前一样执行相同的表达式(不需要在这里指定 scheduler 参数)。最后, 8 个 GPU上的计算非常快,花费不到 2,因此与单个 GPU 相比加速了 6.26 倍。计算的平均值也非常接近(2.2e-6)预期值(图 12)。

图 12 :使用全套 GPU(8xTesla K80)的计算:仅需 2 秒。

图 13 描述了我们全套 GPU 的 GPU 活动。实际上,谷歌云实际上将特斯拉 K80 的显存分成了两半。这就是为什么我们只有 12 GB 的可用内存,而不是 24 GB。这也意味着我们“只”拥有每个 GPU 2496 个 CUDA 核心。

****图十三:特斯拉 k80 正在摇滚。

结论

通过 Dask 阵列上的简单数值模拟,我们从使用单个 CPU 的几乎 12 分钟的计算时间,到在 8 特斯拉 K80 GPUs 上的几乎 2的闪电般速度,因此有了360 倍的加速比!计算时间的巨大增长并不是以准确性为代价的。这些实验在很大程度上受到了来自 Matthew Rocklin 的帖子的启发,他是 Dask 的最初作者,现任 Coiled computing 的首席执行官。我们希望能够在使用多 GPU 系统执行更复杂的任务之前,重现简单方法的速度结果,例如我们正在处理的生成模型的快速训练。多 GPU 计算乍一看似乎令人生畏,但使用正确的工具时,它会变得非常容易。

我们的三部曲到此结束,希望你喜欢,我会很快回来的。

感谢

我要感谢克里斯托夫、马特奥、菲利普和佩瑟瓦尔在本研究实现过程中给予的支持,以及他们对本文草稿的反馈。

参考

[1] N. Morizet,“机器学习的速度立方—第 1 集”,走向数据科学,2020 年 9 月 15 日。
【2】
n . Morizet,《机器学习的速度立方——第二集》,迈向数据科学,2020 年 11 月 20 日。
【3】Dask:Python 中并行计算的开源库。
【4】CuPy:用 NVIDIA CUDA 加速的开源阵列库。
【5】Dask CUDA:一个实验性的开源库,帮助多 GPU 系统中 Dask workers 的部署和管理。
【6】m . Rocklin,“GPU Dask 数组,第一步把 Dask 和 CuPy 扔在一起”,matthewrocklin.com,2019。

关于我们

Advestis 是一家欧洲合同研究组织(CRO),对统计学和可解释的机器学习技术有着深刻的理解和实践。Advestis 的专长包括复杂系统的建模和时间现象的预测分析。
领英:https://www.linkedin.com/company/advestis/

速度转移矩阵:一种新的道路交通数据建模技术

原文:https://towardsdatascience.com/speed-transition-matrix-novel-road-traffic-data-modeling-technique-d37bd82398d1?source=collection_archive---------30-----------------------

探索道路交通数据可视化和分析的新途径

亚历山大·波波夫在 Unsplash 上拍照

对道路交通数据建模的方法有很多种。研究人员大多使用某种时间序列来表示一些交通参数在时间上的变化,如速度、延误或流量。大多数情况下,需要聚合大量数据来创建这种时间序列。在这一步,出现了不同的问题,大多与大偏差有关。速度转移矩阵(STM)就是为了解决这个问题而提出的,它提供了一种从交通数据中提取更多信息的方法。通过这个故事,我将解释 STM 的一些基本概念和用例。更多细节,你可以跳转到发表在《可持续发展杂志》上的论文“使用速度转换矩阵进行城市范围内的交通状态估计和分类”。

基本概念

让我们首先解释一下 STM 的基本部分:

1。过渡

在 STM 概念中,过渡被定义为单个车辆在两个连续路段(链路)之间的运动。在一个转换中,我们有两个链接,起点和终点。值得一提的是,链接长度取决于您使用的地图。例如 OpenStreetMap 使用非常短的链接(几米),而其他地图提供商将一个链接定义为两个路口之间的所有路段。

道路网络上的两个过渡示例(图片来源:作者)

2.速度

第二步是速度计算。为了构建一个 STM,我们计算每一辆穿越这个过渡的车辆的速度。计算起点和终点链路的速度非常重要。我们使用谐波速度,因为它倾向于较低的值,以提取更好的拥堵表示,但人们可以试验其他速度计算方法,如平均速度或中值速度。以下是 GNSS 数据的示例,但也可以使用其他数据源。

过渡时的速度计算(图片来源:作者)

3.[数]矩阵

矩阵的第一次迭代用于表示一次转换中的速度计数。下图举例说明了五条经过绿色过渡区的车辆路线。可以看出,在计算 STM 时,观察时间是一个非常重要的参数。如果经过的时间越长,计数会越大,因为会有更多的汽车通过观察到的转换。

STM 计算(第一部分——计算速度变化)

以下是在两个时间间隔内观察到的一个转变的 STM 的两个最常见的例子:1)从 08:30 h 到 15:30 h,和 2)从 15:30 h 到 17:30 h:

STMs 的例子:1)非高峰时间,2)晚上高峰时间(图片来源:作者)

现在我们可以用这个工作了!在第一幅图像中,起点和目的地速度位于中间,起点和目的地速度较高,这表示交通正常,没有拥堵。在第二张图中,我们可以观察到大多数计数位于左上角,起点和终点速度较低,这可能表示拥堵。由此,我们提取了一些有用的流量模式。

您还可以观察到,起点和目的地速度用以 km/h 表示的绝对值表示。为了使 STM 在城市范围内更具可比性,以 0–100%范围内的相对形式表示速度非常重要。我们选择相对于限速来表示速度,但是你可以相对于最高速度或者平均夜间速度来表示。此外,我们通过将矩阵中的每个值除以矩阵值的总和,将计数转换为概率。有了这个,结果更好理解,也更容易解释。

STM 的最终形式:相对值,概率(图片来源:作者)

利弊

这里有一些使用 STMs 时要考虑的最重要的优点和缺点。
优点:
-易于解释
-信息量更大
-不存在聚集问题
缺点:
-比时间序列数据更复杂
-计算时间更长
-不能用于非常稀疏的数据

STM 用例

结论

STM 是一种新颖的交通数据建模技术,在交通数据表示和分析方面显示出巨大的潜力。在最近发表的论文中,我们展示了将 STM 应用于交通状态估计的可能性,但它也可以用于异常检测、可视化等。

除了它的优点之外,它也有一些缺点,在实施之前必须加以考虑。目前,这种方法处于“进展中”状态,将是我博士论文的主要部分。

如果您对类似的、与交通相关的话题感兴趣,请关注我的媒体简介,或者在研究门户:)查看发表的研究

如有任何问题或建议,欢迎评论或联系我!https://www.linkedin.com/in/leo-tisljaric-28a56b123/

发表的论文

这里有一个发表论文的列表,显示了扫描隧道显微镜的用法。

  1. l . tiljari、T. Cari、B. Abramovi 和 T. Fratrovi,“使用速度转换矩阵进行全市范围的交通状态估计和分类”,可持续性,第 12 卷,第 18 期,第 7278 页,2020 年。
  2. l . tiljari、S. Fernandes、T. Cari 和 J. Gama,“使用张量分解方法对城市道路网进行时空交通异常检测”,载于 Discovery Science。计算机科学讲义,第 12323 卷,A. Appice、G. Tsoumakas、Y. Manolopoulos 和 S. Matwin 编辑。Cham: Springer,2020 年,第 674-688 页。
  3. l . tiljari,Majstorovi、T. Erdeli 和 T. Cari,“使用速度转换矩阵进行城市道路交通异常检测的方法”,载于《国际信息和通信技术、电子和微电子会议会议记录》,2020 年,第 268-275 页。

使用 SageMaker Training 编译器,将 AWS 上的拥抱面部训练工作的速度提高 50%

原文:https://towardsdatascience.com/speed-up-hugging-face-training-jobs-on-aws-by-up-to-50-with-sagemaker-training-compiler-9ad2ac5b0eb?source=collection_archive---------21-----------------------

SageMaker 培训编译器概述和入门指南

简介

深度神经网络每年都在稳步变大,因为硬件和算法的进步已经允许神经网络由数千亿个参数组成。尤其是通常用于自然语言处理(NLP)任务的 Transformer 模型,近年来使用的参数数量激增。例如,2018 年提出的变压器双向编码器表示(BERT) large 有超过 3.4 亿个参数,2021 年提出的开关变压器有 1.6 万亿个参数。

尽管最近的技术进步,训练这些模型的成本和时间变得令人望而却步。在 AWS,我们有代表客户进行发明的记录,以减少为最常见的深度学习框架(TensorFlow 和 PyTorch)运行培训工作的成本和时间。其中两项创新包括用于数据并行和模型并行的 SageMaker 库,它通过分布式培训技术的进步减少了特定任务的培训时间。如果你想更深入地了解我们的分布式培训技术,我强烈推荐阅读我们关于 SageMaker 数据并行的出版物。

2021 年,我们推出了深度学习模型训练的最新创新产品: SageMaker 训练编译器。SageMaker Training 编译器作为 SageMaker SDK 的一部分提供,只需对利用 TensorFlow 或 PyTorch 的现有代码进行最少的更改,就可以优化您的训练代码,以提高运行效率,消耗更少的计算和内存,将 GPU 驱动的训练作业速度提高 50%。这一进步将使最先进的深度学习模型向更广泛的受众大众化,并为我们在 AWS 上使用这些框架的客户提供性能改进。

在这篇文章中,我将带您了解 SageMaker 训练编译器的高级设计,并向您展示如何将 SageMaker 训练编译器与拥抱脸变形金刚库集成在一起。拥抱脸是一家提供易于使用的工具和库的公司,用于开发和部署最先进的开源 NLP 模型。

SageMaker 培训编译器

编译器负责将您在高级编程语言(如 Python 或 Java)中指定的代码转换成在硬件上执行的机器代码。在将高级编程语言翻译成机器代码的过程中,编译器设计者必须做出与这种翻译相关的决定,这种决定对所执行的编译代码的性能有影响。在 AWS,我们的编译器设计人员致力于创建一个专门针对大规模深度学习训练优化的编译器,结果是 SageMaker Training 编译器。

用 TensorFlow 和 PyTorch 构建的神经网络,以图形的形式直观、逻辑地表示出来。SageMaker Training 编译器使用图形级优化,如运算符融合、内存规划和代数简化,来优化图形,以便在底层硬件上执行。此外,SageMaker Training 编译器提供后端优化(内存延迟隐藏、面向循环的优化)和数据流优化(布局转换、公共子表达式消除)来有效地协调训练执行。

实时运算符融合是 SageMaker Training 编译器提供的节省时间的主要技术之一,因此值得深入研究。训练深度学习模型的主要瓶颈之一是往返内存以检索和执行操作符的延迟成本。通过将多个运算符融合在一起,SageMaker 训练编译器要求训练作业更少地访问内存,并且可以直接在后端设备的硬件寄存器中执行更多的运算符。与传统的操作员执行方法相比,这可以提高性能。

高层建筑

高级架构图显示了 SageMaker Training 编译器如何利用您提供的深度学习训练脚本中的深度学习模型图来编译和训练您的模型(图片由作者提供)

SageMaker 培训编译器可在 select AWS 深度学习容器(DLCs) 中直接获得。指定适当的框架和估计器参数将启用 SageMaker 训练编译器,如果您是带来您自己的模型,可能需要对您的训练脚本进行额外的修改。SageMaker 培训编译器将分析您的培训脚本,并在编译期间自动进行优化,从而使您的模型培训作业执行得更快。由训练作业产生的模型工件然后被输出到亚马逊简单存储服务(S3)。

使用拥抱脸变形器开始使用 SageMaker 训练编译器

SageMaker 训练编译器目前支持 PyTorch 和 TensorFlow 。为了快速上手 SageMaker Training 编译器,我将向您展示一种将 SageMaker Training 编译器与 Hugging Face Trainer API 和 PyTorch 集成的简单方法,如果您需要重构现有工作,这种方法只需要最少的代码更改。

先决条件

  • 如果这是你第一次使用亚马逊 SageMaker,你需要设置 SageMaker
  • 配置 SageMaker Studio 或随意修改用于 SageMaker 笔记本实例或其他开发环境的指令
  • 确保查看 SageMaker Training 编译器测试的实例类型,如果必要,如果您的帐户级服务限制没有您想要的实例类型和可用的所需实例数量,请请求增加配额

演练

关于使用BERT-base-cased为情感分类任务执行模型调整的技术的完整示例,您可以访问我们的 AWS GitHub 示例页面。此示例假设使用单个 p3.2xlarge 实例。此外,该示例假设 PyTorch v1.9.0 和 Transformers v.4.11.0。

重要提示:sage maker 培训编译器已经在精选型号上进行了测试。虽然您可以在 AWS 文档页面上列出的型号之外的型号上试验 SageMaker Training 编译器,但是 SageMaker Training 编译器在其他型号上的性能尚未经过测试。在支持的模型表中也有推荐的批量大小设置。如果您偏离了测试的模型和推荐的批量设置,您需要在解锁任何训练加速之前重新调整参数。

在下面的演练中,我将向您展示如何使用拥抱脸代码生成功能来快速生成用于 Amazon SageMaker 的训练代码。然后,我将向您介绍构建由 Hugging Face 生成的代码以与 SageMaker Training 编译器集成所需的步骤。在本演练中,我选择“文本分类”任务作为模型任务,我希望对我的预训练模型进行微调。

  1. 选择我们测试过的型号 中的一个,它也可以作为抱脸预训练型号。
  2. 现在,让我们生成我们的启动代码。导航至所选模特的拥抱脸模特页面(例如,这是 roberta-large 的页面)。
  3. 在页面顶部附近,点击“火车”>“亚马逊 SageMaker”。

使用拥抱脸模型页面,你可以在 Amazon SageMaker 上轻松生成代码来训练你的模型

4.选择要微调模型的任务,并为配置选择 AWS。

在本例中,我们选择“文本分类”测试,并计划在 AWS 上启动或作业

5.将显示的代码复制到剪贴板,创建一个在 Amazon SageMaker 开发环境中运行的 Jupyter 笔记本,并将代码粘贴到笔记本中。

6.准备您的训练数据,根据需要分成训练、验证和测试数据集,并将数据上传到 S3。将 S3 路径存储在 Jupyter 笔记本中定义的变量中。在准备数据时,你可能会发现拥抱面部数据集库很有帮助。

training_input_path=‘s3://example_bucket/s3_prefix_example/train’
test_input_path=‘s3://example_bucket/s3_prefix_example/test’

7.编写训练脚本,并将训练脚本存储在拥抱面部估计器的 source_dir 参数中指定的位置。例如,训练脚本见拥抱脸 GitHub 库或参考上面的 BERT 基本案例。对于这个例子,你的训练脚本必须使用变形金刚库的训练器类。

from transformers import Trainer, TrainingArgumentstraining_args=TrainingArguments(**kwargs) 
trainer=Trainer(args=training_args, **kwargs)

8.为了确保您充分利用 SageMaker Training 编译器在内存中适应更大批量的能力,请参考本机的批量大小和训练编译器编号的批量大小。使用获得的批量数量,更新批量数量,并根据 Jupyter 笔记本顶部的新批量调整您的学习速度。

# the original max batch size that can fit into GPU memory without compilerbatch_size_native=12
learning_rate_native=float(‘5e-5’)# an updated max batch size that can fit into GPU memory with compilerbatch_size=64# update learning ratelearning_rate=learning_rate_native/batch_size_native*batch_size

9.更新您的 HuggingFace estimator 以指定启用 SageMaker Training 编译器的正确参数,目前建议禁用调试器以确保对性能没有影响,但请参考 SageMaker Training 编译器文档以获取关于调试器和 SageMaker Training 编译器的最新指南。

在这一步中,注意我们正在实例化一个TrainingCompilerConfig类的实例,并将其传递给compiler_config参数。这个类使您能够为您的培训工作配置 SageMaker 培训编译器。TrainingCompilerConfig 类接受两个布尔参数作为enableddebug参数。有关这些参数的更多信息,请参见文档。通过在估算器中不指定compiler_config参数,您明确地禁用了 SageMaker 训练编译器。

pytorch_huggingface_estimator=HuggingFace(source_dir=’./scripts’,entry_point=’train.py’,instance_count=1,instance_type=’ml.p3.2xlarge’,transformers_version=’4.11.0',pytorch_version=’1.9.0',hyperparameters=hyperparameters,compiler_config=TrainingCompilerConfig(),disable_profiler=True,debugger_hook_config=False
)

10.要启动您的训练作业,调用 Estimator 对象上的 fit 方法,传入您在步骤 6 中指定的数据位置。

huggingface_estimator.fit({‘train’: training_input_path, ‘test’: test_input_path}, wait=False)

恭喜你!现在,您已经使用新的 SageMaker 培训编译器启动了您的第一个培训工作。您可以从 SageMaker 服务控制台的“培训>培训作业”下跟踪您的培训作业的进度,当培训完成时,您的模型将输出到 S3,准备部署。要查看使用上述技术微调BERT-base-cased的完整示例,您可以访问我们的 AWS GitHub 示例页面。

结论

在这篇博客文章中,你了解了新的 SageMaker 训练编译器,以及它如何帮助你减少训练深度学习模型所需的时间。然后我们介绍了如何使用 PyTorch 和 Hugging Face 开始使用新的 SageMaker 培训编译器。

我鼓励你把你在这里学到的东西应用到你自己的用例中。很可能你已经在通过使用多 GPU 甚至多节点的训练方法来优化性能增益了。您会很高兴地知道 SageMaker 培训编译器也可以适应这些用例(参见文档中的分布式培训指南,并在我们的 AWS GitHub 示例库中找到多节点多 GPU 示例)。您还可能有需要使用不同于拥抱脸训练器 API 的方法的用例,对于这些用例,请检查您如何能够轻松地调整您的训练脚本以继续利用 SageMaker 训练编译器的性能优势。

用矩阵数学加速线性回归

原文:https://towardsdatascience.com/speed-up-linear-regression-with-matrix-math-fe5ff7f2b53b?source=collection_archive---------29-----------------------

使用 Numpy 和线性代数拟合多元回归模型

简·侯伯在 Unsplash 上拍照

线性回归是一个非常流行和有用的模型。它被 Excel 专家和数据科学家等使用——但是我们如何快速拟合大量的回归模型呢?本文介绍了拟合线性回归模型的各种方法,以及如何使用线性代数来加快速度。

在本文中,我将介绍普通最小二乘线性回归的几种不同方法。

  1. 使用stats models拟合一个模型
  2. 仅使用 Numpy 拟合简单的线性回归
  3. 使用矩阵公式
  4. 如何一次通过分组数据拟合多个模型
  5. 比较循环与矩阵方法的性能

数据概述

本文中使用的数据将是生成的样本数据。使用 numpy +引入一些随机噪声,创建了以下数据集,用于演示 OLS 拟合技术。在接下来的几个示例中,这些数据将存储在和变量中。

数据概述,按作者分类的图像

OLS 统计模型公司

拟合线性模型的一个很好的方法是利用 statsmodels 库。我们只需传入两个变量,调用 fit 方法,并通过调用 summary() 来检索结果。注意——这些参数也可以使用 fit.params 直接访问。

Statsmodels OLS 线性回归结果,图片由作者提供

上述结果显示了许多细节,但对于本演练,我们将重点关注系数。在列的 ceof 下,你会看到 y 截距(标为 const)约为 211.62,我们的 x 回归系数为 101.65。考虑到初始数据集引入的参数和随机性,这非常符合预期。

简单线性回归的 Numpy 算法解

如果我们只处理一个回归变量/独立变量(类似于本例),那么直接求解斜率和截距就相当简单了。

简单线性回归-斜率(系数)公式,图片由作者提供

看上面的公式,它是 x 减去它的平均值乘以 y 减去它的平均值,除以 x 计算的平方。第二步,也可以用这个斜率和 x、y 的平均值来求出截距。

这个系数公式很简单,只需几行代码就可以在 numpy 中直接实现。numpy 中的点函数将负责我们的 sumproducts,我们可以调用。mean() 直接在向量上寻找各自的平均值。

最后,我们使用 allclose 函数检查 numpy 解决方案的结果是否与 statsmodels 版本完全匹配。

Output:
Simple linear regression arithmetic slope of 101.65 vs. statsmodels ols fit slope of 101.65\. Results equal: True

矩阵公式

矩阵公式进一步扩展了 OLS 线性回归——允许我们直接从 X 和 y 得出截距和斜率,即使是多个回归变量。这个公式如下,详细的推导过程请看经济理论博客的这篇文章。

OLS 矩阵公式,图片作者

下面的 numpy 代码非常直接地反映了这个公式。这里也可以使用点积,但是使用 matmul 是因为当扩展到三维时,它更适合我们的下一个场景(一次拟合多个模型)。

在使用新数据集进行下一步之前,快速检查每种方法会发现相同的截距和斜率——对于简单线性回归用例,这是实现相同目标的三种不同方式。

OLS 结果三种方式,由作者形象

一次多重回归

用例示例

继续我们的下一个用例,如果你想一次运行多个回归怎么办?假设您有一个包含各种商店的零售数据集,并且您希望对每个商店独立运行 ols 回归,这可能是为了比较系数或获得更细粒度的线性模型拟合。

另一个使用案例示例—一家电子商务商店可能希望按产品对每月销售数据进行多次 ols 线性回归。他们希望独立地拟合线性模型,以便通过观察各种模型的系数来获得方向性和趋势的粗略感觉。

数据形状和多重矩阵公式

为了进行演示,创建了一个包含 2 组 50 个数据点的数据集。两者都有 2 个回归变量/独立变量,但我们想分别拟合两个分组的线性模型。

数据的形状为—代表 2 个分组,每个分组有 50 个数据点,每个分组有 2 个自变量(+截距)。我们的 Y 变量现在是——2 个分组,50 个数据点。

如果我们试图将它插入到我们的 statsmodels ols 函数中,我们将得到一个错误。然而,我们可以扩展我们的矩阵公式,在一次通过中找到每个模型的截距/斜率。让我们从数学上定义我们试图实现的目标:

多组矩阵公式,作者图片

对于由 k 表示的每个数据回归分组的 n 个回归(本例中为 2),我们想要运行我们在上一节中执行的 ols 线性回归的矩阵版本。你会看到这是与前一节相同的公式,我们只是介绍执行多次迭代( k = 1 到 n ),每个分组一组矩阵乘法。

Numpy 移调调整

在 numpy 中实现这一点需要一些简单的修改。如果我们只是将我们的 Xy 变量直接插入到之前的公式中,维度不会按照我们想要的方式排列。发生这种情况的原因是使用了相同的转置函数()。T in numpy)会把我们的形状从变成。不完全是我们想要的…

numpy matmul 文档规定,如果有两个以上的维度通过任一参数传入,则运算将被视为驻留在最后两个索引中的矩阵堆栈。这是我们正在寻找的行为,所以第一维应该保持为 2(堆叠我们的分组),我们只是想转置 50 和 3。

我们可以使用转置功能并指定如何准确转置我们的轴以获得正确的形状。传入轴将得到想要的形状——将最后一个交换到轴,但保持我们的 n 回归维度不变,直到堆栈结束。

对于下面的代码示例,X 现在将被称为 X_many,类似地,y 也被称为 X _ many,以便与前面的示例相区别。

Output: (2, 3, 50)

修订的数字操作

记住这个转置知识,是时候做出改变来解决这个分组版本了。为了可读性,numpy 代码被分成两行。Transpose 被修改为沿正确的尺寸工作,一个额外的空轴被添加到 y_many 只是为了排列 matmul 操作。

Output:
array([[209.9240413375,  95.3254823487,  61.4157238175],[514.164936664 ,   8.1355322839,   4.3353162671]])

我们得到一个数组,其中包含每个独立变量的截距和系数。使用 statsmodels 执行类似的(但循环的)操作,并使用 numpy.allclose 进行检查,发现系数是相同的。

我们能够一次拟合许多回归。在我们希望拟合一组线性模型的情况下,这种方法会很有帮助,这些模型是按一个特定的维度分组和划分的,并且是独立求解的。

Einsum 符号

我们也可以用 numpy 中的 einsum 函数来解决这个问题。这使用爱因斯坦求和约定来指定如何执行操作。在高层次上,我们将每个矩阵的下标定义为一个字符串——如果指定转置的顺序被交换,并且乘法发生在共同的字母上。要了解更多信息,请查看 numpy 文档,因为它们提供了一些示例。

为了可读性,einsum 版本被分成了几个步骤。在符号串中,我们指定了转置以及要执行的矩阵乘法。这也允许我们通过指定输出维度来处理 np.newaxis 段代码。

Output:
[[209.9240413375  95.3254823487  61.4157238175][514.164936664    8.1355322839   4.3353162671]]

这给了我们与前一版本相同的结果,虽然性能可能稍慢。如果您熟悉 einsum 符号,这可能是一个有用的实现。

速度比较

分组依据/应用模式

我见过的一种一次性实现多重回归的方法是使用 pandas 数据框架和 groupby/apply 方法。在这种方法中,我们定义一个函数,pandas 会将数据集分成几个组,并应用该函数—根据需要返回和减少结果。

这是我们“分组回归”中的用例。将我们的 numpy 数组切换到一个 dataframe 中,通过一个 regression_number 列进行分组,并应用一个定制的 ols 函数来拟合和返回结果,这将提供相同的系数(尽管 pandas 显示了一些小数点)。

按作者分组/应用回归结果、图像

比较性能

现在,我们有两种不同的方法在多个分组中执行 OLS,它们的表现如何呢?

对于这个测试,我们将扩展到 100 个分组和 3 个回归变量——( 100,50,4)的形状,而不是 50 个点和 2 个回归变量的 2 个分组,作为一个更现实的场景。

使用 %%timeit 魔术命令在我们的笔记本中运行 timeit 测试显示,pandas groupby/apply 方法需要 120 毫秒,而 numpy matrix 版本只需要 0.151 毫秒。 矩阵乘法几乎快了 800 倍!

当然,我确信有方法可以优化 pandas 的实现,但是 matrix 版本的好处是直接在 numpy 中的几行代码已经很快了。

摘要

理解机器学习的某些基本方法背后的数学可以让你在需要的地方定制和扩展方法。许多开源库速度很快,经过良好的测试,并且经常做您需要它们做的事情。

然而,可能存在这样的一次性情况,即使用 numpy 实现简单但易于理解的方法(如线性回归)可以在不增加太多复杂性的情况下产生更好的性能。

所有的例子和文件都可以在 Github 上找到。

原发布于https://data stud . dev

通过批处理加速多重处理

原文:https://towardsdatascience.com/speed-up-multiprocessing-with-batching-72f6ac6594e0?source=collection_archive---------12-----------------------

批还是不批?

作者:爱德华·克鲁格和道格拉斯·富兰克林。

Jonathan Farber 在 Unsplash 上拍摄的照片

介绍

我们将评估三种并发模式在具有不同输入长度和复杂性的函数上的性能。

我们的函数 f(x)返回一个整数的因子。

f(x)

这不是 f(x)的最优实现;相反,我们设计了两个“杠杆”来改变问题,从而改变执行时间。

  1. 我们将修改范围来产生一个更大的尺寸问题。想想平行部分。
  2. 我们将修改一个 randint range 来产生一个更复杂的问题。思考顺序部分。

你可以在 这个库 里找到下面的代码和图形。

https://github.com/edkrueger/concurrency_with_joblib

什么是批处理?

批处理是在并行化之前将我们的输入分组。批处理可以通过将计算资源集中在一个批处理上,直到处理完所有输入,从而提高效率。

如果您有许多简单的任务,批处理往往会提高执行时间。我们可以想象,如果我们需要折叠 100 条毛巾并将它们放在架子上,将它们分配到许多毛巾文件夹中会大大减少完成任务所需的时间。这里,许多文件夹代表了多进程解决方案中的许多进程。

让我们将这个比喻扩展到利用批处理。如果我们在 10 个文件夹完成任务时一条一条地分发 100 条毛巾,我们会花很多时间来分发毛巾。相反,想象我们在任务开始时给每个文件夹一批10 条毛巾。

pablo ramos 在 Unsplash 上拍摄的照片

如果你有复杂的任务,批处理就没什么帮助了。想象一下,我们有 4 个画家,需要 100 幅杰作。如果我们一开始给每个画家 1 或 25 张画布,这并不重要。这是因为画这幅杰作所花的时间相对来说比分发一幅画布所花的时间要多得多。然而,如果一个画家碰巧在其他人之前完成了他的 25 幅画,那么当其他人完成工作时,他将被迫闲置。也就是说批次不占某人提前完成的便宜。

下面我们的第三个并发模式展示了如何在 joblib 中修改多处理模式以使用批处理。当执行这种模式时,每个流程将接收一批预先确定的输入来执行,而不需要从队列中提取或查看其他流程的结果。

重要的是要认识到,与在单线程上执行计算相比,无论有无批处理,多处理都会通过委托输入带来一定的计算开销。然而,由于批处理只需要传递一次输入,所以它比其他形式的多处理要快。

多处理可以通过利用更多的硬件或为手头的问题创建更好的并发模式来加快执行时间。

如何让我们的机器使用更多的进程?

Joblib

Joblib 是一组工具,用于在 Python 中提供并行处理解决方案。Joblib 经过优化,速度更快,鲁棒性更强,尤其是在大型计算任务上。

joblib。并行文档

Joblib 提供了一个简单的助手类来使用多重处理编写并行化的进程。核心思想是把要执行的代码写成生成器表达式,转换成并行计算。

例如,可以使用n_jobs=2将 f(x)分布在两个 CPU 上:

我们的函数 f(x)返回一个整数的因子。delayed函数是joblib.Parallel的一部分,它延迟函数的执行,直到它被适当地并行化。

请记住,joblib 可以做得更多!查看 Pratik Gandhi 的这篇文章,看看如何在管道中使用它。

我们的并发模式

单线程的

这是解决这个问题的最简单的模式。在单个线程或进程上运行计算。

朴素多进程

这里我们使用joblib.Paralleln_jobs=-1来最大化这个问题的并行化。在我的例子中,这意味着 i7 处理器需要 12 个进程。

对于简单的多处理,每个输入都给第一个可用的进程。

分批多重处理

在第 16 行,我们再次使用 joblib。与n_jobs=-1并行,最大化这个问题的并行化。然而,我们对我们的输入委托更加慎重。每个流程都有自己的输入要执行。每个批次大小相等,每个流程接收一个批次。

测试我们的并发模式

我们来比较一下三种模式;单线程朴素多进程批量多进程、同时改变输入长度和复杂度。

结果

我们的第一个图表显示了输入数量从 0 增加到 10,000 时的执行时间,其中最大的随机整数值为 1000。还有。请注意,多重处理是最慢的模式。对于这个问题,跨多个进程委托输入的开销太大,无法保证多重处理。但是,当我们使用批处理时,我们可以将性能提高到比单线程执行更快!

图表 1 —按时间缩放至 10,000 图表 2 —按时间缩放至 100,000

在第二张图中,我们将最大的随机整数值设置为 1000,并绘制输入数量从 0 增加到 100,000 时的执行时间。

我们看到,当输入数量超过 50,000 时,多处理开始胜过单线程。我们再次看到,批处理多进程优于其他模式,这一次规模更大。

现在让我们随着问题复杂性的增加来看性能。

图表 1 —时间复杂度高达 1000 图表 2 —时间复杂度高达 10,000

我们的第一张图显示,随着复杂度从 1 增加到 1000,多处理是最慢的,而单线程和批处理之间实际上没有什么区别。

在第二张图中,我们将复杂度提高了两个数量级。现在我们看到,随着复杂性增加到 10,000,多处理优于单线程。批处理多处理优于其他处理,直到大约复杂度 70,000,在这里多处理和批处理多处理汇合。最终,在最复杂的情况下,我们看到多重处理变得比批处理更快。

结论

什么时候用批处理?如果你对一个简单的问题有许多输入。假设你没有很多输入,使用单线程。在一个非常简单的问题上使用多重处理是不值得的。最后,如果你的问题有很多输入并且很复杂,使用简单的多重处理。

当许多人可以从批处理中获益时,他们天真地进行多重处理。请注意,在下面的热图中,多进程在低复杂度时会产生开销,这使得它比单线程和批处理多进程要慢。

跨多种规模和复杂性的 3 种并发模式的运行时热图

随着规模和复杂性的增加,我们发现使用更多的流程会带来显著的好处。所以对于你的复杂问题,用 joblib 来并行执行吧!

加速数据科学的自然语言处理

原文:https://towardsdatascience.com/speed-up-nlp-for-data-science-79eb819326f7?source=collection_archive---------20-----------------------

为自然语言处理任务编写更快代码的 5 种简单方法

由 CHUTTERSNAP 在 Unsplash 上拍摄的照片

不久前,我写了一篇关于我创建的过滤相似文本的程序的文章。但是当我把它投入生产时,我的程序的 NLP 部分造成了巨大的瓶颈。他们花了大约 1000 万来运行一个程序,而我希望它的总运行时间是 10 分钟!

但是在一些简单的性能改进之后,它们迅速成为程序中最快的过去,并且在不到 1 秒的时间内执行。

外卖?为 NLP 任务写快速代码真的很有用!它防止了产品瓶颈,并使您的代码更容易调试,因为您可以更快地接收程序输出。

在本文的其余部分,我将介绍五个步骤,您可以将它们应用到许多 NLP 风格的程序中,以提高它们的速度。我将使用 spacy 作为自然语言处理库来说明这些例子,因为它是我在我的相似文本程序中使用的,还因为它非常快——如果你正确使用它的话!

超级列表理解

所以,你可能已经知道列表理解比循环更快。如果没有,这里有一个关于如何写清单理解的简要回顾:

*# Traditional for loop* one_to_a_hundred = []
**for** x **in** range(1, 101):one_to_a_hundred.append(x)*# List comprehension equivalent* one_to_a_hundred = [x **for** x **in** range(1, 101)]

但是实际上在这里你可以做更多的事情。列表理解语法非常强大,如果您充分利用它,您可以最大限度地提高代码的性能。

举以下例子:

*# Traditional for loop* evens_times_two_and_zeros_from_one_to_four = []
**for** x **in** range(1, 101):**if** x **in** [1, 2, 3, 4]:**if** x % 2 == 0:evens_times_two_and_zeros_from_one_to_four.append(x * 2)**else**:evens_times_two_and_zeros_from_one_to_four.append(0)*# List comprehension equivalent* evens_times_two_and_zeros_from_one_to_four = [(x * 2) **if** x % 2 == 0 **else** 0 **for** x **in** range(1, 101) **if** x **in** [1, 2, 3, 4]]

明白我的意思吗?你可以在函数中工作,比如 times two,if else 子句比如测试 x 是否是偶数,另一个 if 子句看看 x 是否在列表[1, 2, 3, 4]中。

这里的语法非常强大,打开了许多大门,所以值得看看如何充分利用它。

尽可能使用发电机!

这是你会经常看到的事情,不是没有原因的。生成器极大地提高了代码性能,并大大加快了运行时间。

加快代码速度的一个非常直接的方法是使用生成器理解,而不是 for 循环。

这使用了与上面几乎相同的语法:

*# Traditional for loop* one_to_a_hundred = []
**for** x **in** range(1, 101):one_to_a_hundred.append(x)*# Generator comprehension equivalent* one_to_a_hundred = (x **for** x **in** range(1, 101))**for** number **in** one_to_a_hundred:print(number)

你实际上只是用普通的方括号代替了方括号。generator comprehension 返回的输出是一个 generator 对象,您可以一次迭代一个项目。

因为它们与列表具有不同的基础数据类型,所以您不能基于索引访问生成器对象中的项目,也不能对列表执行您可能喜欢的许多其他功能。

但是如果你有一个简单的用例,需要迭代输出——比如在预处理文本之后做一些事情——那么生成器可能是一个很好的选择!

例如:

*# Possible preprocessing pipeline* preprocessed_tests = (preprocess_function(x) **for** x **in** texts)**for** text **in** preprocessed_tests:some_nlp_function(text)

使用缓存装饰器!

这听起来可能比实际复杂得多…

您可能已经通过浏览互联网了解了什么是缓存。当你打开一个网页时,你的浏览器会缓存有用的信息,这样当你返回到那个网页时,浏览器就不需要重新加载所有的内容,只需要加载新的内容。

因此,如果你在脸书上访问一个你经常访问的页面,你的浏览器可能已经缓存了该页面的一些基本布局。在这种情况下,它不必重新加载数据,而只需重新加载页面上的新帖子,这意味着它的工作速度会快得多。

如果在 python 函数中添加一个缓存装饰器,基本上就会发生这种情况。我不打算深究装饰器是什么——如果你感兴趣,这里有一篇很棒的文章——所以只要把它想成是在不改变函数本身的情况下向函数添加额外内容的一种很酷的方式。

实际上是什么样的呢?

**import** functools@functools.lru_cache(maxsize=128)
**def** preprocess_texts(input_texts):*"""Generic preprocessing function.* **:param** *input_texts: tuple of input documents"""# do some pre-processing*

很简单,对吧?您可以根据需要调整缓存大小。

注意,如果你向这个函数输入一个列表——也就是说,如果你输入了preprocess_texts(input_texts=['a', 'b']),那么你会得到一个错误,因为列表不是一个可散列的类型,所以它必须是一个元组。

简而言之,这意味着由于列表是一种我们可以改变的数据类型——例如,通过添加或删除值——列表不能被转换成特定的字母数字标识符,也称为哈希。

functools decorator 要求函数的输入是可散列的,所以这里最好使用元组,因为一旦创建了元组,就不能添加或删除元素或以任何方式真正改变它。元组可能是 python 最顽固的数据类型,这也使得它们——你猜对了——是可散列的。

禁用不必要的 NLP 开销

这个例子是专门关于空间的,但也适用于其他库。

默认情况下,spacy 包含了很多很酷的东西,比如命名实体识别和依赖解析器。

但是这需要花费大量的时间,你可能不需要它来完成一个简单的预处理任务,甚至是你可能正在做的许多其他任务。

您可以在 spacy 中很容易地改变这一点,但这里的主要教训是— 阅读文档

在你从某个很酷的新库中调用一个方法之前,确保你理解这个库在做什么,这样你就可以根据你的用例来调整它。

撇开恐吓不谈,你可以这样做:

***import** spacytext = **"Net income was $9.4 million compared to the prior year of $2.7 million."** nlp = spacy.load(**"en_core_web_sm"**)*# Default* doc = nlp(text)
*# Modified* doc = nlp(text, disable=[**"tagger"**, **"parser"**, **"ner"**]*

加载预训练模型

这个问题很容易解决,但是我发现它是我运行时瓶颈的主要原因。

看到上面那行代码了吗— nlp = spacy.load("en_core_web_sm")?你做这个的时候要小心。

如果你把它放在你的函数中,你调用这个函数 100 次,那么你将会加载这个令人印象深刻但通常很重要的预训练模型,这个模型是这个库附带的 100 次。

然而,如果你把它放在你的脚本的顶部,并使 nlp 成为程序的一个全局变量,你只是做了一次…

因此,开始时的一些繁重工作会导致后来运行时间的大幅减少。

结论!

这些只是一些简单的步骤,我采取这些步骤来改进我的程序的运行时间,使其长度满足生产需求。

概括来说,它们是:

  1. 增强你的列表理解能力
  2. 更好的是,使用生成器理解
  3. 向函数中添加缓存装饰器
  4. 根据您选择的库,禁用不必要的 NLP 开销
  5. 考虑在程序中的什么地方加载预先训练好的模型

这里您可以做更多的事情,比如为运行时分析您的代码或者运行并行作业,但是希望这提供了一个简单且容易操作的起点。

张量是你所需要的

原文:https://towardsdatascience.com/speed-up-the-inference-in-traditional-machine-learning-models-by-converting-them-into-tensor-based-efe6bbe5c92d?source=collection_archive---------24-----------------------

加快您的 scikit-learn 模型的推理速度

弗拉多·帕诺维奇在 Unsplash 上拍摄的照片

深度学习框架由张量组成,作为它们的基本计算单元。结果,它们可以利用硬件加速器(例如,GPU),从而加速模型训练和推断。然而,像 scikit-learn 这样的传统机器学习库是为了在 CPU 上运行而开发的,没有张量的概念。因此,他们无法利用 GPU,因此错过了深度学习库享有的潜在加速。

在本文中,我们将了解一个名为 Hummingbird 的库,它就是为了弥合这一差距而创建的。Hummingbird 通过将传统机器学习模型转换为基于张量的模型,加快了它们的推理速度。这使我们能够在 GPU 上使用 scikit-learn 的决策树和随机森林等模型,并利用硬件功能。

蜂鸟是什么?

如上所述,蜂鸟是传统机器学习模型中用于加速推理的库。Hummingbird 通过将这些传统的机器学习管道编译成张量计算来实现这一点。这意味着你可以利用 GPU 和 TPU 等硬件加速,甚至是传统的机器学习模型,而无需重新设计模型。

这在几个方面是有益的。在蜂鸟的帮助下,用户可以受益于:

  • 在神经网络框架中实现的优化;
  • 原生硬件加速;
  • 拥有支持传统和神经网络模型的单一平台;

蜂鸟库高层架构|来源:官方论文

除了上述优点,蜂鸟还提供了许多方便的功能,其中一些列举如下。

1️⃣ .方便的统一“推理”API

Hummingbird 提供了一个方便的统一“推理”API,它非常类似于 sklearn API。这允许用 Hummingbird 生成的模型交换 sklearn 模型,而不必更改推理代码。

2️⃣.支持主要模型和特征。

当前版本的 Hummingbird 目前支持以下操作符:

运营商支持 b 蜂鸟。来源:官方 Github 库

3️⃣.转换能力

目前,您可以使用蜂鸟将您训练的传统 ML 模型转换为 PyTorch 、 TorchScript 、 ONNX 和 TVM

蜂鸟可以按作者转换你训练过的传统 ML |图像

工作

蜂鸟库的主要重点是加快传统机器学习模型的推理速度。已经开发了很多专门的系统,比如 ONNX Runtime、TensorRT 和 TVM。然而,许多这些系统都专注于深度学习。传统模型的问题在于,它们是用命令式代码以特定的方式表达的。让我们通过一些视觉表现来理解它。

传统机器学习模型如何工作|作者图片|转载自蜂鸟会议。

传统模型的一个缺点是,它们是使用命令式代码以特定的方式表达的

让我们考虑一个包含四列的数据框架,其中两列是分类的,其余两列是数字的。这些被输入一个机器学习模型,比如逻辑回归,以识别它们是属于class 0还是class 1。这是二元分类问题的经典案例。如果我们深入观察,我们有一个 DAG 或有向无环操作图,称为管道。管道由特征器组成,这些特征器对数据进行预处理,然后将其提供给预测器,预测器将输出预测。这只是传统模型的一个简单表示。在所有传统的 ML 框架中,有成百上千的这些特征和预测器。因此,很难用一种对所有不同框架都有意义的方式来表示它们。

深度学习模型被表示为张量运算的 DAG

另一方面,我们在深度学习中主要依赖张量的抽象,它只是一个多维矩阵。深度学习模型也表示为 DAG,但明确专注于张量算子。在下图中,我们有非常通用的矩阵运算,可以很容易地在各种系统中表示出来。

深度学习模型如何工作|作者图片|转载自蜂鸟会议。

深度学习预测服务系统可以利用这些张量运算,并利用这种抽象在许多不同的目标环境中工作。

Hummingbird 通过重新配置算法运算符,将传统的流水线转换为张量运算。下面这个来自他们官方博客的例子解释了蜂鸟将决策树翻译成张量的策略之一,其中涉及到 GEMM(通用矩阵乘法)。

将简单的决策树转化为神经网络|转载自蜂鸟的 官方博客

演示

蜂鸟的语法非常直观和简洁。要在 DNN 框架上运行您的传统 ML 模型,您只需要import hummingbird.ml并将convert(model, 'dnn_framework')添加到您的代码中。下面是一个使用 scikit-learn 随机森林模型和 PyTorch 作为目标框架的例子。

🔗 链接访问整个代码和数据集

使用一个 scikit-learn 随机森林模型和 PyTorch 作为目标框架使用 Hummingbird |图片由作者提供

CPU 上的随机森林回归器与 GPU 上的随机森林回归器的时间比较|由作者绘制的图像| 链接到代码

结论

Hummingbird 是一个很有前途的库,正在研究机器学习领域的一个核心问题。让用户能够从 CPU 无缝过渡到 GPU,并利用硬件加速器来加速推理,这将有助于直接关注问题而不是代码。如果您想更进一步,请确保查看下面的参考资料。本文以这些参考资料为基础,如果您决定更深入地研究该库的底层解释,您也会发现它们很有帮助。

参考

  • https://github.com/microsoft/hummingbird
  • 张量是你所需要的:用蜂鸟更快的推理
  • 为统一机器学习预测服务的张量编译器。
  • 将经典的 ML 管道编译成张量计算,以实现“一刀切”的预测服务。

用这三个工具加速你的命令行导航

原文:https://towardsdatascience.com/speed-up-your-command-line-navigation-with-these-3-tools-f90105c9aa2b?source=collection_archive---------25-----------------------

用一个词修复您的打字错误,保存您的命令行片段,等等!

动机

命令行是数据科学家的常用工具。因此,知道如何有效地使用命令行将会提高您的生产率。

比如在命令行打错字的时候,如果能像下面这样只打一个字就能修复错误不是很好吗?

作者 GIF

在本文中,您将学习 3 个工具,使您在命令行上工作时更有效率。它们是:

  • 用一个词纠正你之前的命令
  • pet —保存并执行您的命令行片段
  • 猛拉—将终端输出猛拉到剪贴板

用一个词纠正你之前的命令

使用命令行时,输入错误是很常见的。例如,您可以键入git comit而不是git commit。除了重新键入整个句子之外,有没有一种方法可以只用一个单词来修复之前的命令?

这时候吸盘就派上用场了。这个按钮允许你用一个单词来纠正前面的命令,如下所示。

作者图片

如果你忘记添加sudo,使用这个工具将修复它。

作者图片

有时,您可能会不小心将一个命令键入两次。该工具将识别并删除重复项。

作者图片

如果您在跟随教程时不小心复制了$,这个工具也可以检测到。

作者图片

您可以使用向上或向下箭头从该工具中获得其他建议:

作者图片

点击了解如何安装推杆。

Pet —保存并执行您的命令行片段

有没有一些有用的命令,在需要的时候你总是忘记?与其在 Google 上搜索或在 Word 文档中保存这些命令,不如直接在命令行中保存这些命令,这样不是很好吗?

作者图片

这时候宠物就派上用场了。与其他命令行片段管理器相比,我喜欢 pet 的一点是它易于使用。

要保存新命令,请键入pet new。您只需要插入两条信息,如下图所示的CommandDescription,就可以创建一个新的命令片段。

作者图片

要执行该命令,运行pet exec:

作者图片

注意,由于我们把file1放在了<>中,所以 pet 知道它是一个变量。Pet 允许我们使用 tab 键轻松地输入变量的名称!

要查看你所有的代码片段,输入pet list:

作者图片

要编辑命令,请键入pet edit:

作者图片

默认编辑器是 Vim。如果你想改变编辑器,键入pet configure或转到文件~/.config/pet/snippet.toml。接下来,将editor的值更改为您最喜欢的编辑器:

我把编辑器换成了 VSCode。

要查看所有可用的命令,请键入pet:

作者图片

在这里找到如何安装宠物。

猛拉—将终端输出猛拉到剪贴板

你有没有想过不用鼠标就能复制终端输出?

作者图片

这时猛拉就派上用场了。Yank 允许您仅使用键盘选择和复制终端输出。

要使用 yank,请在要复制输出的命令末尾添加| yank。例如,键入docker image ls | yank来拉动docker image ls的输出。

使用箭头在不同的字段之间移动,并键入 Enter 来复制字段。

作者 GIF

如果要复制整行,使用yank -l

作者 GIF

多方便啊?

如果你用的是 Linux,用command | yank -- xsel -b代替command | yank。由于yank -- xsel -b输入起来很长,您可能想要创建一个别名,比如grab来缩短命令:

$ alias grab="yank -- xsel -b"$ docker image ls | grab

点击了解如何安装拉环。

结论

恭喜你!您刚刚学习了 3 个工具来提高您在命令行上的工作效率。从长远来看,在这里或那里节省一点时间将会为你节省大量的时间。

我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以在 LinkedIn 和 T2 Twitter 上与我联系。

星这个回购如果你想检查我写的所有文章的代码。在 Medium 上关注我,了解我的最新数据科学文章,例如:

</2-tools-to-automatically-reload-when-python-files-change-90bb28139087> [## Ptpython:更好的 Python REPL

towardsdatascience.com](/ptpython-a-better-python-repl-6e21df1eb648)

帮助您更好地学习数据科学的技术

原文:https://towardsdatascience.com/speed-up-your-data-science-learning-process-a49ae080a46d?source=collection_archive---------16-----------------------

发现你的学习方式,更聪明地学习

乔纳森·博尔巴在 Unsplash 上的照片

自从我开始自学数据科学以来,我投入了大量的时间来找出最好的学习方法。

我想以更快的速度学习,尽可能快地更好地理解概念。有一段时间我是边工作边学习,时间真的很宝贵。

对我来说,尽可能多、尽可能快地学习是很重要的。

当我在寻找让自学变得更容易的技巧时,我不可避免地遇到了不同的学习风格

什么是学习风格?

我们都有不同的学习方式。

不仅速度不同,而且方式也不同。这可能是人们倾向于不同事物的原因——我们中的一些人更有创造力,而另一些人更有逻辑性。

我们中的一些人喜欢公开演讲和辩论,而另一些人喜欢绘画和素描。

这种对不同道路的倾向源于我们的思维方式,并影响我们的学习方式。

我们都非常不同,有独特的思想、观念和学习模式。我们以不同的方式体验周围的世界。

这些不同的模式可以分为四种学习风格。

这意味着有四类学习者,我们都属于每一类。

有些人不止属于这一类。

当我读到更多关于学习风格的内容时,我有所领悟。我明白让我的学习材料与我的学习风格相一致的重要性。

我也意识到了为什么我在一些问题上苦苦挣扎,而在其他问题上却表现出色。

我在科学考试中总是得 A,而且计算起来轻而易举。然而,我可以对实验课说相反的话。

照片由this engineering RAEng在 Unsplash 上拍摄

把我放在一个化学实验室里,你会看到我是如何努力去遵循讲师给出的看似简单的指令的。

为了克服这个困难,我会请我的朋友告诉我完成实验任务的所有步骤。然后我会写下这个,对自己重复一遍,然后清楚地知道该做什么。

这是因为我的学习方式。我不是一个视觉学习者,这让我很难观察和模仿其他人做的事情。我也不是动觉型学习者,这让我更难跟上老师的指示。

了解你的学习风格很重要,因为它可以帮助你找到学习一门学科的最佳方法。如果你正在纠结于一个看似简单的概念,而你周围的人似乎都能理解,也许你只是学错了。

我现在将分解不同的学习风格。

根据您的学习风格,我还将解释您可以采取的学习数据科学的不同方法。

视觉学习者

由乌列尔·索伯兰斯在 Unsplash 上拍摄的照片

视觉学习者是通过视觉手段学习得最好的人。这是一个人通过观察最能理解一个概念。

当一个概念以图片思维导图流程图的形式出现时,这个学习者理解得最好。如果你是一个视觉学习者,你会通过观察某人执行任务而不是听他们解释过程来学得更好。

作为一名可视化学习者,观看在线课程将极大地有利于您的数据科学学习过程。有很多 MOOCs 迎合视觉学习者,因为导师会告诉你如何执行任务。

当导师在你面前运行一段代码并演示程序如何工作时,你会学得最好。

记笔记时,你应该用不同颜色的笔,做思维导图,或创建流程图。这将有助于加快你的学习进程。

作为视觉学习者,你能够通过观察在脑海中描绘出一个概念。我建议在数据科学学习中利用这一点。

制作一张你想要实现的短期学习目标和长期学习目标的思维导图,并朝着它们努力。你还应该创建一个愿景板展示你希望在未来几年看到自己的

每天早上醒来,看着你的愿景板,准确地看到你未来想要达到的目标会激励你,并且每天推动你更接近你的目标。

听觉学习者

在 Unsplash 上 NeONBRAND 拍摄的照片

听觉学习者更喜欢通过听觉而不是视觉来获取信息。

如果你是一个听觉学习者,当你大声听到一个概念时,你理解得最好。你倾向于自己重复笔记,更喜欢听讲座。

作为一名听觉学习者,我喜欢向他人解释概念,因为大声说出来有助于强化我的记忆。

当我的朋友在考试前大声修改笔记时,我总是专注地听,因为听觉概念帮助我将它们内化。

作为一个听觉学习者,我经常听 MITx 或者哈佛的数据科学和深度学习讲座。虽然大多数人觉得传统的听课方式很无聊,但这是我学得最好的方式。

讲课结束后,我喜欢用自己的话再次解释概念,将学到的东西内化。这种先听一个概念,然后大声重复的方法对我理解新话题有很大帮助。

如果你是一个听觉学习者,我建议这样做是为了更好地理解新概念。(从经验上讲,我建议在这样做的时候避开图书馆或者安静的学习空间)。

动觉学习者

由凯利·西克玛在 Unsplash 上拍摄的照片

动觉学习者通过实践学习效果最好。当他们真正参与其中,并进行一些实践时,他们最能理解一个概念。

这些学习者在学校环境中最挣扎,因为他们发现很难安静地坐着并接受大量的信息。

然而,这些学习者在校外有很大的潜力。他们对与周围环境互动的兴趣使他们很容易在工作场所和任何需要动手实践的环境中茁壮成长。

如果你是动觉学习者,我建议采取自上而下的方式学习数据科学。

这意味着您可以先学习 Kaggle 教程,然后通过编写代码来解决端到端的问题。

一旦你开始构建并熟悉所使用的工具,你就可以开始研究底层算法了。

阅读/写作学习者

照片由 Aaron Burden 在 Unsplash

顾名思义,阅读和写作学习者从阅读和写作中学习效果最好。他们通过阅读课本和记笔记来掌握概念。

传统的学校系统迎合阅读和写作学习者,因为所做的大部分工作涉及写文章和从教科书中学习。

如果你是一个阅读/写作学习者,我建议采取自下而上的方式学习数据科学。

可以买一本机器学习的书,熟悉一下概念。在对每个算法的用例有了直觉之后,你可以继续深入研究实现,并实际开始编码。

除了是一个听觉学习者,我也是一个阅读/写作学习者。由于这个原因,我喜欢大声读出概念,写下来,然后再读出来。

我的学习方法

如上所述,我的学习方式是听觉和阅读/写作。然而,我想强调的是,学习风格只是信息最自然地出现在你面前的方式。

有可能适应并学习我上面列出的所有方法,这取决于你所从事的领域。

以我的经验来看,以一种符合我学习风格的方式来学习一个概念会更容易。

例如,大约六个月前,我正在尝试吴恩达的深度学习课程。这是一门被强烈推荐的课程,有着令人难以置信的评价。

然而,我发现自己在听课时很容易感到无聊和走神,并且没有取得太大的进步。

这是因为我不是一个视觉学习者,试图通过视频讲座来保留大量信息对我来说并不理想。

我开始寻找学习相同概念的新方法,并偶然发现了一本由 Francois Chollet 写的名为《用 Python 进行深度学习》的书。

这本书有很多与吴恩达的课程相同的材料,这本书的免费 PDF 版本可以在网上找到。

与在线课程相比,我发现自己从书中学到的东西要快得多。我可以写下我正在阅读的概念,并通过对自己重复来内化它。我可以很容易地回头多次阅读这些单词,以帮助在我的脑海中描绘出这个概念。

这让我意识到,我可以通过简单地找到与我的思维过程一致的资源来加快我的数据科学学习过程。

我们都以不同的方式思考。因此,我们消费信息的方式各不相同。

如果你想以更快的速度学习,并真正享受学习的过程,那么你需要以一种与你的学习过程相一致的方式来创建你的学习计划。

我希望这篇文章能对如何更有效地学习提供一些见解。

祝您的数据科学之旅好运,并继续学习。感谢阅读!

尽可能以最不受约束、最不敬和最原始的方式,努力学习你最感兴趣的东西——理查德·费曼

使用 BigQuery Cloud SQL Federation 加速您的 ELT

原文:https://towardsdatascience.com/speed-up-your-elt-using-bigquery-cloud-sql-federation-90b3ede99472?source=collection_archive---------26-----------------------

作者图片

实时查询云 SQL 实例中的数据,减少 ELT 开发时间,避免复制和移动数据,这要归功于 BigQuery 云 SQL 联盟。

众所周知, Cloud SQL 是一个完全托管的关系数据库服务,适用于 MySQL、PostgreSQL 和 SQL Server。这些类型的数据库面向宿主应用程序或事务性数据。另一方面,BigQuery 支持分析工作负载。

在传统世界中,您必须开发一个数据管道来从应用程序的数据库中提取数据。让我们看看如何在 PostgreSQL 数据库中利用表联合。

循序渐进的案例

在这个场景中,我们作为数据工程师负责在分析区域(BigQuery)中启用来自事务数据库(云 SQL)的数据。这些数据可以被其他流程或其他角色使用,如数据科学家或机器学习工程师。

像往常一样,公司希望我们的团队可以立即使用最新生成的交易数据。

由于 BigQuery Cloud SQL Federation,我们能够实时查询 Cloud SQL 中的数据。这一特性为传统的批量摄取提供了新的替代方案。现在,我们可以使用创建当天或同一小时的数据。例如,Data Studio 上的报告可以显示过去一个小时的销售信息,最棒的是,如果您有使用 BigQuery 的经验,开始使用它并不困难。

在你继续之前

为了执行接下来的步骤,您需要一个谷歌云帐户。要开户,只需在 GCP 网站注册,自动获得 300 美元的信用。

在云 SQL 中

为了首先重现这个案例,我们创建了事务性数据库。在这个数据库中,我们将生成一个表来存放一个虚构商店的销售额。

让我们创建云 SQL 实例,在本例中是 PostgreSQL

作者图片

如果是第一次创建云 SQL,您需要启用计算引擎 API,这允许 GCP 代表您创建和管理计算引擎。只要看看窗口,点击蓝色按钮。

作者图片

在这种情况下,为新实例设置最小值。有了这些价值,我们保证了最便宜的价格。如果你想详细了解价格明细,请点击这里。

  • 区域可用性:单一区域
  • 机器类型:共享核心
  • 存储:硬盘

作者图片

创建该实例大约需要 10 分钟。

然后让我们使用云外壳连接到实例。这是最容易连接的形式。记住你之前设置的密码就行了。存在其他选择,比如从本地机器使用数据库客户机的连接。

作者图片

连接后,创建事务表。带有主键和其他常规字段的简单表。

此外,我们向表中插入一些数据。

作者图片

因此,我们有了 PostgreSQL 数据库和事务表,保留插入脚本,并在下一步模拟新事务后随意插入数据。

我们准备转移到我们的分析区域。

在 BigQuery 中

BigQuery 是我们的数据工程和数据科学团队花费大量时间来转换公司数据并从中获得洞察力的地方。

为了继续这个案例,BigQuery 需要与我们的云 SQL 实例建立连接。这个连接将允许我们查询刚刚插入的数据。

在浏览器中,打开另一个选项卡,进入 BigQuery 界面。

我们转到“添加数据”,然后单击“外部数据源”

作者图片

如果是第一次启用大查询连接 API。

在窗口的右边,会出现一个询问外部数据源的表单。添加您的连接 id(您需要从云 SQL 页面复制的字符串)、数据库名称和用户密码。

重要——联邦表只在同一个项目中起作用。这意味着您的云 SQL 实例和大查询联邦表需要在同一个项目中。
-仅适用于具有公共 IP 连接的云 SQL 实例。

作者图片

就这么简单。最好的情况是,CloudSQL tabla 中的每个更改都将反映在联邦表上,因此不需要运行提取管道。

最后,只需运行一个简单的查询!就像您观察到的查询表一样,您可以使用相同的 BigQuery UI,除了一个特殊语法,因此所有的特性,如调度查询或保存都是允许的。

作者图片

另一个特性是,您不仅限于使用来自云 SQL 的数据,您可以将一个联邦查询结果与一个 BigQuery 表连接起来。这意味着您可以构建一个丰富的流程,从 PostgreSQL 中获取交易数据,并添加其他变量,如客户信息,以根据他们的购买情况发送特殊促销信息。

SELECT a.customer_id, a.name, b.first_order_date
FROM bqdataset.customers AS a
LEFT OUTER JOIN EXTERNAL_QUERY('projects/datapth/databaseconnection','''SELECT * FROM public.transactions''') AS b
ON b.customer_id = a.customer_id
GROUP BY c.customer_id, c.name, rq.first_order_date;

注意事项

  • 定价 : 根据外部查询返回的字节数向您收费(每 TB 5 美元)。
  • 性能 : BigQuery 需要等待源数据库执行外部查询,并将数据从外部数据源临时移动到 BigQuery。
  • 限制 : PostgreSQL 支持很多 BigQuery 不支持的非标准数据类型,比如moneypathuuid or boxer.

结论

BigQuery Cloud SQL federation 是加速 ELT 开发的一个极好的特性。如果你想更进一步,我推荐你查看这篇关于增量数据接收管道的文章,然后回来重新思考这个小管道,为每日报告仪表板提供数据。

PS 如果你有任何问题,或者想要澄清什么,请给我发电报、脸书或 LinkedIn 或者我喜欢讨论数据😊

参考

  • https://cloud . Google . com/big query/docs/cloud-SQL-federated-queries
  • 【https://cloud.google.com/bigquery/docs/batch-loading-data】
  • https://cloud . Google . com/blog/products/data-analytics/optimizing-your-big query-incremental-data-ingestion-pipelines
  • https://cloud . Google . com/big query/docs/federated-queries-intro # federated _ query _ syntax
  • https://cloud.google.com/sql/docs/postgres/quotas

用 NumExpr 包加速你的 Numpy 操作

原文:https://towardsdatascience.com/speed-up-your-numpy-operations-with-numexpr-package-1bead7f5b520?source=collection_archive---------23-----------------------

利用多线程功能加速您的数字运算

图片来自 Pixabay 的 Ashim Shres

Numpy 是数据科学社区中一个流行的 Python 包,它提供了大量高级数学函数来操作这些多维数组。在数据科学案例研究中,经常会遇到对大型向量执行数学运算的情况。

Numpy 提供了快速和优化的矢量化函数来加速数学运算,但不涉及并行性。在本文中,我们将介绍 NumExpr 包,它是 NumPy 的一个快速数值表达式计算器。

NumExpr 入门:

Numexpr 是一个完全基于 NumPy 1.6 中引入的新数组迭代器的开源 Python 包。Numexpr 计算作为参数传递给**evaluate**函数的字符串表达式。使用 Python 编译函数计算字符串函数,以找到变量和表达式。

例如,a 和 b 是两个 NumPy 数组。要添加这两个数组,需要传递**numexpr.evaluate(“a+b”)** ,其中**a+b**是字符串表达式。然后通过构建表达式的解析树来计算字符串表达式。这个解析树结构然后被编译成字节码程序,它描述了如何执行基于元素的操作。

NumExpr 如何加速计算?

NumExpr 通过将符号赋值器数值 Python 表达式转换为高性能的矢量化代码,并在元素块中进行矢量化,而不是一次性编译所有内容,从而实现了并行性。

NumExpr 比 NumPy 实现了更好的性能,因为它避免了为中间结果分配内存,从而提高了缓存利用率并减少了一般的内存访问。还有,NumExpr 虚拟机完全是用 C 语言写的,这使得它比 Python 快。因此,NumExpr 最适合大型数组。

预期绩效:

根据 NumExpr 文档,NumPy 运算的计算速度有望提高 2 到 4 倍。这完全取决于表达式的复杂性和操作符的内部优化。使用大型阵列或非对齐阵列时,性能可能会提高 20 倍。

实施:

NumExpr 包可以使用**pip install numexpr** 从 PyPI 安装。

简单的数学运算:

让我们从两个 NumPy 数组(a 和 b)的简单数学运算开始,每个数组都有 10⁷元素。与 Python 操作相比,我们使用 NumExpr 获得了几乎 4.2 倍的加速——从 337 毫秒到 79 毫秒。

(图片由作者提供),与 NumExpr 的简单操作对比

逻辑运算:

在执行一些逻辑表达式和布尔过滤时,我们通过 NumExpr 获得了 13 倍的加速——从 644 毫秒到 52 毫秒。

(图片由作者提供),与 NumExpr 的复杂逻辑运算比较

未对齐的数组操作:

对于未对齐的数组,与 Python NumPy 操作相比,加速要高得多。使用 NumExpr,我们可以实现大约 32 倍的增长

(图片由作者提供),非对齐数组操作与 NumExpr 的比较

配置:

使用 NumExpr,您还可以使用**numexpr.set_num_threads()**函数指定内核的数量。

结论:

在本文中,我们讨论了一个开源 Python 包 NumExpr,它通过向量化元素块而不是一次性编译所有元素来实现并行性,从而加快数学运算的速度。与 NumPy 操作相比,速度可以提高 2 到 20 倍,具体取决于各种条件。

NumExpr 是一个很好的工具,可以加速大型数组的数学运算。

参考资料:

[1] NumExpr 文档:https://nume xpr . readthedocs . io/projects/nume xpr 3/en/latest/intro . html

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一小部分会员费,不需要你额外付费。

感谢您的阅读

加快你基于熊猫的分析

原文:https://towardsdatascience.com/speed-up-your-pandas-based-analysis-4836049d186e?source=collection_archive---------34-----------------------

通过亲身体验了解核心原则

作为一名 python-data scientist,您无疑对使用pandas进行数据分析很熟悉,这是一个用于处理类似表格的数据类型的 python 包,类似于R中的ExcelSASSQLdata.tabledplyr等规范工具。

要在pandas中进行分析,可走的路不止一条。即使对于有经验的数据科学家和 python 开发人员来说,基于传统方法选择的解决方案有时也可能不是最佳的,甚至在计算方面效率低下。

本文旨在向您介绍一个简单的用例,并定量地比较不同的编程方法。希望所讨论的比较可以为你在未来基于pandas的分析中挑选最优解提供一些定量的基础。

背景

给定一组随机点,我们的用例是将它们分类到不同的同心环中。

就算法而言,这个问题可以通过计算从每个点到区域中心的欧几里德距离,根据给定的容差对它们进行宁滨,并对它们进行标注来轻松解决。为了方便起见,我们将把标签作为颜色索引,以便在最后进行简单的可视化。基本上,初始结果()和最终结果()将是:

使用案例的目标—作者提供的图像

请记住,这种走查的主要目的不是用例本身,而是用于到达那里的方法,以及它们性能的相对比较

分析

数据

我们将使用坐标xy模拟一个包含 100k 个点的简单数据帧。

n = 100000
x = np.random.rand(n)
y = np.random.rand(n)
df = pd.DataFrame({'x': x, 'y': y})
df.head(4)

简单的应用方法

作为第一种方法,我们将使用pandas的内置apply函数将自定义函数encode_color_v1映射到每个数据帧的行。encode_color_v1的定义如下。

def **encode_color_v1**(ser: pd.Series, step: float = 0.1, ncolors: int = 7) -> int:r = np.sqrt((ser['x']-0.5)** 2 + (ser['y']-0.5)**2)return int(round(r/step)%ncolors)

基本上,我们计算到中心点的距离(0.5,0.5),将距离绑定到每个step中,并根据颜色数量ncolors进行调制,稍后我们将使用这些颜色来可视化结果。

在每行上使用内置的“应用”和自定义函数

如图所示,我们用apply完成了工作。正如支持者指出的,代码语法相当清晰,实现映射的自定义函数并不需要花费太多时间。易用性是apply经常用于pandas分析管道的原因之一。

100k 行的“应用”方法的运行时

现在仔细一看,运行时间好像不是很快。对于 100k 行,在 MacOS、英特尔酷睿 i7、16GB DDR3 上用了 4 s。当规模增大或计算需要重复多次时,这将成为一个实际问题。对于 1M 的行,它花费了 35.5 秒的 737 毫秒,而对于 10M 的行,它变得长得可笑,超过了 6.5 分钟。

让我们用ipython魔法命令%prun检查一下主要耗时步骤。

%prun -l 5 df.apply(encode_color_v1, axis=1)

应用方法的时间配置文件

正如我们所看到的,主要花费时间在我们的自定义函数encode_color_v1 (100k ncalls ,6.4s cumtime )上,所以任何重大的加速都必须处理这个问题。

有两个显著的观察结果:

  • 内置的 python 函数get_value花费了大量的时间
  • 使用__getitem__get_value对与数据帧长度(如 10k)成比例的不同函数进行多次重复调用(ncalls)

这是因为在 sence 后面,apply函数会将沿着所提供的轴的每一行转换成一个pandas.Series,并在 dataframe 中的所有行上循环。

因此,我们可以在两个主轴上对此进行优化:

  • 避免转换到Series的开销
  • 避免循环

让我们从第一次优化开始。

内部循环

这里的想法是避免将每一行数据转换到pandas.Series,其时间足迹可以在上面看到,例如__getitem__get_value。为此,我们将重写自定义函数,将输入作为 numpy 向量,并在每个索引上循环。

def **encode_color_v2**(x: np.ndarray, y: np.ndarray,step: float = 0.1, ncolors: int = 7) -> int:n = len(x)res = np.empty(n, dtype=int)for i in range(n):r = np.sqrt((x[i]-0.5)** 2 + (y[i]-0.5)**2)res[i] = int(round(r/step)%ncolors)return res

跳过转换为熊猫时运行时间更短。系列

如图所示,我们轻松获得了 8 倍的加速。这清楚地表明了熊猫的转换和使用所带来的开销。使用apply功能时的系列。在这种情况下,这似乎是显而易见的,但在实践中,尤其是当处理包含str记录的数据帧时,许多从业者没有意识到或注意到这种开销。

现在,让我们再次检查时间配置文件。

时间曲线显示了循环的影响,例如 100k 次调用。

如你所见,我们找不到__getitem__get_value的开销。然而,我们仍然可以看到循环的影响,例如,在上面的第二行,100k 倍的ncalls

让我们向量化我们的自定义函数。

矢量化

基本上,我们将利用输入的向量性质,并使用向量计算来代替循环。

def **encode_color_v3**(x: np.ndarray, y: np.ndarray,step: float = 0.1, ncolors: int = 7) -> int:r = np.sqrt((x-0.5)** 2 + (y-0.5)**2)return ((r/step)%ncolors).astype(np.int32)

如前所述,我们可以通过 numpy 矢量化显著减少运行时间。

通过矢量化进一步加速计算

就时间剖面而言,我们在我们执行计划中看不到任何明显的缺陷。现在唯一真正需要时间是自定义函数的执行。

时间配置文件显示主要时间消耗在自定义功能的执行中

用 Numba 加速

让我们继续用编译函数来挤压性能。有人可能会想到通过Cython使用 C 预编译功能,然而,更简单的方法是通过Numba使用基于低级虚拟机(LLVM)的JIT-compilation。更详细的解释可以在我的另一个帖子这里找到。

from numba import njit[@njit](http://twitter.com/njit)(cache=True)
def **encode_color_v4**(x: np.ndarray, y: np.ndarray,step: float = 0.1, ncolors: int = 7) -> int:r = np.sqrt((x-0.5)** 2 + (y-0.5)**2)return ((r/step)%ncolors).astype(np.int32)

与使用numpy矢量化的解决方案的主要不同之处在于,我们只是从导入的numba中添加了一个装饰器'@jit'

使用简单的 njit 装饰器,速度提高 25%

我们可以很容易地看到,自定义功能的执行时间在numba版本中减少了(与numpy版本的时间配置文件中的 3ms 相比,为 2ms)。

我们可以从这里开始,为我们的用例找到一个相当好的计时解决方案。然而,为了增加额外的好奇,让我们走得更远一点!

优化数字

这里优化的想法是相当反直觉的。我们从减少循环的逻辑开始我们的旅程,并试图从矢量化中获益。然而,并不是每个人都知道内存分配的成本。

在 numpy 矢量化计算中,经常会将中间结果保存到新的时态变量中,从而需要分配新的内存块。最终的问题是函数调用时间和内存分配时间之间的权衡。这并不总是显而易见。

numba的情况下,jit-compiled函数的加载时间很快(例如,与 python bytecode函数的加载时间相比),并且它们的时间成本不如分配大数据矢量化计算所需的大内存块重要。

让我们把它具体化。我们将重新编码我们的自定义函数,但是通过每个元素使用一个循环,而不是整个向量。

from numba import njit[@njit](http://twitter.com/njit)(cache=True)
def **encode_color_v5**(x: np.ndarray, y: np.ndarray,step: float = 0.1, ncolors: int = 7) -> int:n = len(x)res = np.empty(n, dtype=np.int32)for i in range(n):r = np.sqrt((x[i]-0.5)** 2 + (y[i]-0.5)**2)res[i] = int(round(r/step)%ncolors)return res

令人惊讶的是,我们的执行时间更短了。

自定义函数的优化 numba 版本可实现 2 倍加速

优化 numba 版本的时间配置文件

关于这一时间收益的深入讨论值得另发一篇专稿。现在,请保持函数调用和内存分配之间的平衡。

最终注释

作为那些经常使用pandas进行日常分析的人之一,我承认直到最近我才意识到这里讨论的几个方面。我希望这篇文章对其他人有所帮助,或者至少为进一步优化打下一些基础。

实际上,我们并不总是需要从代码中挤出每一秒钟。一个清晰的语法,易于书写、阅读和被他人理解,比优化后的代码更重要。然而,对于具有大数据集的工业化版本,对一些定制函数的多次调用,那么高效的代码可能是一个重要因素。

不同方法的运行时间和增益总结

在整个使用案例中,我们要传达的信息是:

  • 循环和转换为熊猫。使用内置apply功能时,系列是速度缓慢的主要原因。
  • 尽可能避免使用apply
  • 尽可能向量化你的函数。
  • numba支持自定义功能时使用numba(基本上大部分 numpy 功能)。
  • 对于大型数据集,内存分配成本很高,因此最好使用带有编译函数的循环。

非常感谢你的阅读。像往常一样,如果你有任何意见和建议,请告诉我。

参考

【1】深度潜入[numba](/why-numba-sometime-way-slower-than-numpy-15d077390287)vs[numpy](/why-numba-sometime-way-slower-than-numpy-15d077390287) 【2】熊猫官方纪录片

通过改变一行代码来加速你的熊猫工作流程

原文:https://towardsdatascience.com/speed-up-your-pandas-workflow-by-changing-a-single-line-of-code-11dfd85efcfb?source=collection_archive---------5-----------------------

使用 Modin 扩展您的数据探索和可视化

蒂姆·高在 Unsplash 上拍摄的照片

Pandas 是用于数据探索和可视化的最流行的 Python 库之一。Pandas 提供了大量的内置功能,使得执行数据探索变得更加容易。但是当处理大型数据集时,它失败了,因为它使用单核 CPU 执行所有操作。熊猫没有利用所有可用的 CPU 核心来扩大计算规模。

所有可用的 CPU 内核都可以用来提升大型复杂计算的性能和时间复杂度。有各种开源库,如 Vaex、Dask、Modin 等,可以使用 CPU 的所有内核来提升性能。CPU 内核的充分利用节省了数据科学家的大量时间,因为它减少了计算时间。

阅读文章下面的,了解如何使用 Vaex 处理有 2 亿条记录的数据集。

在本文中,您可以了解如何使用 Modin 提升 Pandas 库计算的性能,只需更改一行代码。

摩丁是什么?

Modin 是一个开源的 Python 库,它通过将操作分布在 CPU 的多个核心上来加速 Pandas 的工作流程。与其他分布式库不同,Modin 可以很容易地与 Pandas 库集成和兼容,并具有类似的 API。

开发人员不需要担心指定 CPU 内核的可用性,也不需要提供如何分发数据。摩丁通过将计算分配到 CPU 的多个内核来处理所有这些。

安装和使用:

可以使用 PyPl 安装 Modin:

**pip install modin**

安装库后,导入库以进一步使用它:

**import** **modin.pandas** **as** **md**

这只是为了提升熊猫的性能而对代码进行的一行修改。摩丁的 API 和熊猫的很像,开发者不用担心学习或者搜索。

摩丁是如何在引擎盖下工作的?

使用 Pandas 对数据帧的操作很慢,因为它使用单核 CPU 来执行计算,而没有利用多核 CPU。现代笔记本电脑有许多内核,可以并行使用来提升性能。摩丁做了同样的事情,它利用了 CPU 的所有核心,并将其计算分配到 CPU 的多个核心中,这有助于加快性能。

(来源),熊猫和摩丁的 CPU 核心利用率

对于拥有大量 CPU 内核的大型数据科学工作站或集群,Modin 性能呈指数级增长,如下所示充分利用 CPU 内核。

API 覆盖范围:

摩丁使用 Ray 或 Dask 引擎来加速计算。在 API 覆盖上,大部分常用的 API 都被摩丁覆盖了,巨大的开发量要覆盖剩下的功能。

**About,
91% of API’s are covered for Pandas Dataframe.
88% of API’s are covered for Pandas Series.**

(来源),摩丁 API 覆盖率

基准时间限制:

(图片由作者提供),摩丁和熊猫之间的基准时间数

结论:

在观察了基准时间数字后,我们可以得出结论,与熊猫相比,摩丁的操作更快。摩丁分配计算并最大限度地利用 CPU 内核。还有各种其他的分布式库也有类似于 Pandas 的 API,包括 Dask、Vaex、Pandarallel 等等。

参考资料:

[1]摩丁文献:【https://modin.readthedocs.io/en/latest/】T4

[2]熊猫文献:https://pandas.pydata.org/docs/

感谢您的阅读

加速伯特推理:量化与稀疏

原文:https://towardsdatascience.com/speeding-up-bert-inference-different-approaches-40863b2954c4?source=collection_archive---------16-----------------------

来自 Unsplash 的 Joshua Hoehne

简介

最近,变压器和类似变压器的架构已经成为 NLP 的事实上的最新技术。伯特就是一个很好的例子。BERT 和它的各种表亲如 RoBERTa 和 AlBERT 从文本序列中产生嵌入。然后,嵌入可以用于各种下游任务,如分类、语义相似性或问答,在其中一些任务中实现接近人类水平的性能。

BERT(以及最先进的 NLP)的一个大问题是,这种伟大的人类水平不是免费的。这通常表现为客户的长时间延迟和每月的巨额 AWS 账单。

许多努力试图解决这一挑战。批处理查询,允许灵活的序列长度和智能的客户机/服务器工作划分可以走很长的路。但是有没有方法可以加速实际的伯特推理本身呢?在这篇文章中,我假设我们正在处理一个 CPU 后端,这是最常见的情况。

使用正确的库

第一步可能是从 Tensorflow 或 Pytorch 切换到 Onnx 或 OpenVINO 的更好的免费库。根据您的 Tensorflow/Pytorch 版本和特定的硬件,这一步可能是我在这里讨论的所有内容中最大的节省。流行的 Huggingface 库正在不断地与 Onnx 集成,所以请查看那里的最佳实践。

这意味着你应该注意商业工具他们声称可以提高 Tensorflow/Pytorch 的推理速度,但没有提到 Onnx 或 OpenVINO 基准!理想情况下,您还需要查看 Onnx/OpenVINO 版本,因为只有较新的版本包含针对 Transformer 的优化。

量化

假设你现在运行 Onnx 或者 OpenVINO,你如何进一步推动性能?首先要尝试的可能是量子化。这仅仅意味着用 int8 权重替换模型中的浮点权重。这通常可以节省大量的内存空间,但是这可能不会节省太多的执行时间!

这一不幸的事实是因为在 AVX512-VNNI 推出之前,英特尔(和 AMD) CPU 的矢量单元不能对 int8 数据进行本机操作,至少不能以对深度学习推理有用的方式进行操作。AWS 上的绝大多数云 CPU 目前不支持 AVX512-VNNI。仅有的起价为 c5.12xlarge 的产品,在成本规划方面可能无法为您提供很大的灵活性。

例如,在具有 c5.2xlarge 的单核上执行 BERT-base,使用 Onnx,量化仅导致 25%的加速。相比之下,c5.12xlarge 上的 AVX512-VNNI 内核的加速约为 250%。

量化的一个好处是你通常只损失不到 1%的精度。它也很好地集成到大多数深度学习框架中,所以很容易尝试:https://colab . research . Google . com/github/py torch/tutorials/blob/GH-pages/_ downloads/dynamic _ quantization _ Bert _ tutorial . ipynb。

修剪

量化的替代方法是修剪。修剪在权重矩阵中引入了零(也称为稀疏度),保证了内存和计算的节省。例如,Huggingface 最近的一项工作 pruneBERT 能够在 BERT 上实现 95%的稀疏性,同时为下游任务进行微调。麻省理工学院彩票假说团队的另一项有前途的工作表明,人们可以获得 70%稀疏预训练的 BERTs,在下游任务的微调方面实现类似于密集 BERTs 的性能。Tensorflow 和 Pytorch 都支持进行修剪。

然而,通过修剪获得加速比量化更具挑战性,因为 CPU 不太喜欢稀疏计算。事实上,据我所知,Pytorch 的稀疏矩阵密集矩阵乘法只有在稀疏矩阵包含超过 98%的零的情况下才比密集-密集版本更快!通常,在不损失太多准确性的情况下,人们最多可以承受 90%或 95%的稀疏度。

最近的解决方案,如 OctoML 的 TVM,已经开始解决稀疏推理问题:https://medium . com/OctoML/using-sparsity-in-Apache-TVM-to-halve-your-cloud-bill-for-NLP-4964 EB 1c E4 f 2。虽然只给出了与 Tensorflow 的比较,但 pruneBERT 上近 2 倍的加速似乎相当有希望。遗憾的是,它似乎只适用于 AMD CPUs,可能是因为它没有针对特定于英特尔 CPU 的 AVX512 进行优化。

Neuralmagic 是一家麻省理工学院的初创公司,专门加速稀疏神经网络。虽然他们报告的性能很好,但不幸的是,他们目前只支持计算机视觉模型。

我将在这里为我的库 SparseDNN 添加一个广告,它(我相信)为类似 BERT 的模型提供了目前市场上最好的稀疏推理性能:https://arxiv.org/abs/2101.07948。SparseDNN 为 pruneBERT 提供了5 倍的加速,适用于英特尔和 AMD 的 CPU。SparseDNN 还为 ResNet 和 MobileNet 等流行的计算机视觉网络提供加速。

值得注意的是,目前没有库可以同时利用量化和修剪。(知道的请评论。)SparseDNN 提供了实验支持,但是它的稀疏 INT8 内核只比浮点内核快一点点。

底线

在本文中,我以 BERT 为例,按照难度增加的顺序介绍了几种提高神经网络性能的方法。在实践中应该如何决定采用哪种方法?这完全取决于特定应用的精度-加速比权衡。直觉上,如果你愿意牺牲更多的准确性,你可以加快你的神经网络。

本文中提到的几种应用于 BERT 的方法的精度-加速权衡如上图所示。该设置假设我们使用一个不带 AVX512-VNNI 的 CPU 内核。理想情况下,你想坐在右下角,低精度损失和高加速。绿线是优化选项的帕累托最优边界。

这篇文章绝不是神经网络优化的详尽指南。比如量化不限于 int8,我甚至没有涉及结构化剪枝。新的硬件选项,如 AWS Graviton 和推理,也提供了有趣的依赖于架构的权衡。但是希望它能给你一些初步的想法和一个心理框架来比较不同的优化方法。

Github 针对稀疏 NN 的回购:【https://github.com/marsupialtail/sparsednn】T4

加速弹性研究中的 BERT 搜索

原文:https://towardsdatascience.com/speeding-up-bert-search-in-elasticsearch-750f1f34f455?source=collection_archive---------6-----------------------

理解大数据

弹性搜索中的神经搜索:从香草到 KNN 到硬件加速

伯特(图片来自 Flickr ,经 CC BY-SA 2.0 许可/背景被作者弄模糊)

在我之前的两篇关于 BERT 之旅的博客文章中:用 BERT 和 Solr 进行神经搜索和用 Apache Lucene 和 BERT 进行娱乐我已经向您介绍了如何在 Solr 中实现由 BERT 支持的语义搜索(事实上,您可以插入除 BERT 之外的任何其他密集嵌入方法,只要它输出浮点向量;二进制向量也可以工作)。虽然用像 BERT 这样的技术来增强你的搜索体验感觉很酷也很现代,但是让它具有高性能对于产品化来说仍然很重要。你希望你的搜索引擎运营团队在真实的行业环境中快乐。你希望你的用户喜欢你的搜索解决方案。

Devops 非常关心磁盘大小、RAM 和 CPU 消耗。在一些公司,他们也关心用电量。数百万或数十亿用户和数十亿文档的规模并不便宜。

在使用 BERT 和 Solr 的神经搜索中,我确实在处理 BERT 时测量了时间和内存消耗,其中时间用于索引和搜索。随着时间的推移,出现了一些令人不快的意外。

搜索时间实际上是文档数量的函数,因为从算法复杂性的角度来看,它需要 O( n ),其中 n 是索引中文档的总数。如果您正在索引数百万个文档,这很快就会变得难以处理,而且更重要的是:您并不真的想要向您的用户交付 n 个文档:没有人会有时间去查看数百万个文档来响应他们的搜索。那么,为什么要费心去给所有的 n 打分呢?我们之所以需要访问所有的 n 个文档,是因为我们事先不知道这些文档中的哪些文档将根据文档和查询密集向量之间的点积或余弦距离与查询相关。

在这篇博文中,我将把 BERT 密集嵌入技术应用于 elastic search——许多公司选择的流行搜索引擎。我们将着眼于实现普通的矢量搜索,然后在矢量搜索方面向 KNN 迈出一大步——测量我们前进的每一步。

由于有大量博客文章讨论了使用弹性搜索的矢量搜索的复杂性,我想:这篇博客文章能给你什么独特的视角,我不知疲倦的读者?这就是你今天将得到的:我将与你分享一个在 Elasticsearch 中处理向量搜索的鲜为人知的解决方案:使用由 GSI 实现的关联处理单元(APU)。我得到了这个独特的系统,它不仅关心大规模查询向量的速度,还关心消耗的瓦特数(我们确实希望对我们的星球生态友好!).听起来很刺激?让我们投入进去吧!

Elasticsearch 自己实现的向量搜索

Elasticsearch 在内部使用 Apache Lucene 作为搜索引擎,因此许多底层概念、数据结构和算法(如果不是全部)同样适用于 Solr 和 Elasticsearch。记录在https://www . elastic . co/blog/text-similarity-search-with-vectors-in-elastic search中的向量搜索方法与我们在 Solr 中观察到的限制完全相同:它将检索所有匹配搜索标准的文档(关键字查询以及文档属性过滤器),并根据选择的向量相似性(余弦距离、点积或 L1/L2 范数)对所有文档进行评分。也就是说,向量相似性将不会在检索过程中使用(第一步,也是最昂贵的一步):而是在文档评分过程中使用(第二步)。因此,由于您无法预先知道要获取多少个语义最相关的文档,因此向量搜索的数学思想并没有真正应用。

等等,它与基于 TF-IDF 或 BM25 的搜索有何不同——为什么我们不能对矢量搜索使用相同的技巧?对于 BM25/TF-IDF 算法,您可以在索引阶段预先计算一组信息以帮助检索:术语频率、文档频率、文档长度,甚至术语在给定文档中的位置。使用这些值,可以在检索步骤中非常有效地应用评分过程。但是您不能在索引阶段应用余弦或点积相似性:您不知道您的用户将向您发送什么查询,因此无法预计算查询嵌入(除了电子商务中的一些情况,在这些情况下您可以知道这一点,因此预计算一切)。

但是回到实践。

要运行 vanilla Elasticsearch index 的索引器,触发以下命令:

time python src/index_dbpedia_abstracts_elastic.py

如果您想重现实验,请记住更改 MAX_DOCS 变量,并将其设置为需要索引的文档数。

就像每一项新技术一样,我设法运行了我的弹性搜索索引器代码,遇到了一个问题:索引在索引过程中变成了只读,并且无法前进!原因很好解释这里,一言以蔽之:你需要确保至少 5%的空闲磁盘空间(如果你有一个 1 TB 的磁盘,51.5!)以避免这个讨厌的问题,或者需要关闭这个保护功能(不推荐用于生产部署)。

错误如下所示:

{‘index’: {‘_index’: ‘vector’, ‘_type’: ‘_doc’, ‘_id’: ‘100’, ‘status’: 429, ‘error’: {‘type’: ‘cluster_block_exception’, ‘reason’: ‘index [vector] blocked by: [TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark, index has read-only-allow-delete block];’}

在这种情况下,您可以求助于 ki Bana——UI 工具,它从单纯的数据可视化发展到安全性和索引管理、警报和可观察性功能。在这篇博文中,我一直在收集索引大小信息,并通过索引管理仪表板检查索引设置和映射:

用于检查弹性搜索索引的索引管理仪表板:健康状况、文档数量、存储大小以及索引是否开放更新(图片由作者提供)

如果你仍然想摆脱这个限制,你可以在 Kibana 开发工具中尝试这样的东西(为你的用例选择合适的值——但是要小心“cluster . routing . allocation . disk . watermark . flood _ stage”值,因为如果它太低,你的操作系统可能会遇到稳定性问题——咨询官方文档):

PUT _cluster/settings
{"transient": {"cluster.routing.allocation.disk.watermark.low": "100gb","cluster.routing.allocation.disk.watermark.high": "50gb","cluster.routing.allocation.disk.watermark.flood_stage": "10gb","cluster.info.update.interval": "1m"}
}

索引后,我运行了 10 个查询来测量平均速度。我还记录了索引所用的时间(包括从文本计算向量的时间)以及每个 N=100、200、1000、10000 和 20000 的索引的大小。我没有记录瓦特消耗,这可能是下一个实验的一个有趣的想法。

索引时间与搜索时间作为文档数量的函数(普通弹性搜索,按作者分类的图片)

下面是上图背后的原始表格:

由于在 bert-as-service 中索引是由单个工作人员完成的,因此索引时间呈指数增长,而搜索速度与文档数量呈次线性增长。但是这有多实际呢?对于 20k 的短摘要,搜索 40ms 似乎太高了。索引大小线性增长,这也是一个令人担忧的因素(记住,您的 devops 团队可能会担心,您需要证明您的算法的有效性)。

因为索引这么慢变得不切实际,所以我必须找到另一种方法来计算向量(大部分时间都花在计算向量上,而不是索引它们:我很快就会用实验证明这一点)。所以我看了看拥抱人脸库,它允许使用暹罗伯特网络索引句子嵌入,这里描述了。在拥抱脸的情况下,我们也不需要使用 http 服务器,不像在 bert-as-service 中。下面是一个示例代码:

from sentence_transformers import SentenceTransformersbert_model = SentenceTransformer('bert-base-nli-mean-tokens')def compute_sbert_vectors(text):*"""Compute Sentence Embeddings using Siamese BERT-Networks: https://arxiv.org/abs/1908.10084* ***:param*** *text: single string with input text to compute embedding for* ***:return****: dense embeddingsnumpy.ndarray or list[list[float]]"""* return sbert_model.encode([text])

SBERT 方法计算嵌入的速度比 bert-as-service 快 6 倍。实际上,1M 向量使用 bert-as-service 需要 18 天,使用 SBERT 和拥抱脸需要 3 天。

在向量搜索方面,我们可以比 O( n )做得更好

并且该方法是使用 KNN 算法在最近的向量子空间中有效地寻找文档候选。

我采用了开源软件中的两种方法:Alex Klibisz 的 elastiknn 和 AWS 支持的 Elasticsearch 开放发行版的 k-NN:

  1. https://github.com/alexklibisz/elastiknn
  2. https://github.com/opendistro-for-elasticsearch/k-NN

让我们在所有三个维度上将我们的标准矢量搜索与这些 KNN 方法进行比较:

  • 索引速度
  • 最终索引大小
  • 搜索速度

KNN:弹性蛋白方法

我们首先需要安装 elastiknn 插件,你可以从项目页面下载:

bin/elasticsearch-plugin install file:////Users/dmitrykan/Desktop/Medium/Speeding_up_with_Elasticsearch/elastiknn/elastiknn-7.10.1.0.zip

要在索引上启用 elastiknn,您需要配置以下属性:

PUT /my-index
{"settings": {"index": {"number_of_shards": 1,          # 1  "elastiknn": true               # 2}}
}

# 1 指的是索引中的分片数量,分片是加速查询的方法,因为它将在每个分片中并行执行。

****# 2elastic KNN 使用二进制 doc 值来存储向量。将此变量设置为 true 可以显著提高 Elasticsearch 版的速度,因为它更喜欢不压缩文档值的 Lucene70DocValuesFormat ,而不是使用压缩的 Lucene80DocValuesFormat ,这样可以节省磁盘空间,但会增加读取文档值的时间。这里值得一提的是,从 Lucene 8.8 开始,Lucene80DocValuesFormat 提供以压缩比换取更好的速度,反之亦然(相关 jira:https://issues.apache.org/jira/browse/LUCENE-9378,这个版本的 Lucene 用于elastic search 8 . 8 . 0-alpha 1)。所以最终这些好东西会同时登陆 Solr 和 Elasticsearch。

有很多相似性不同的索引和搜索选项——我推荐学习写得很好的文档。

要运行 elastiknn 索引的索引器,触发以下命令:

time python src/index_dbpedia_abstracts_elastiknn.py

下表总结了 elastiknn 的索引和搜索性能:

elastiknn 的性能,模型:LSH,相似度:角度(L=99,k=1),前 10 名候选人

KNN: Opendistro 方法

我在 Open Distro 上遇到了很多问题——我真的希望这篇博文能引起 OD 开发者的注意,尤其是如果你能找到我的配置中可以改进的地方。

毫不夸张地说,我花了几天时间搞清楚不同设置的迷宫,让 OD 索引 1M 向量。我的设置是:

  1. 在 docker 下运行的 OD elastic search:Amazon/open distro-for-elastic search:1 . 13 . 1
  2. 单节点设置:仅保留 odfe-node1,没有 kibana
  3. open distro _ security . disabled = true
  4. -" ES _ JAVA _ OPTS =-xms 4096m-xmx 4096m-XX:+use G1 GC-XX:G1 reserve percent = 25-XX:initiationheapoccupancypercent = 30 " #最小和最大 JAVA 堆大小,建议将两者都设置为系统 RAM 的 50%
  5. 为 docker 分配足够的磁盘空间

我也进行了索引特定的设置,这是展示如何配置 KNN 插件的合适时机:

"settings"**:** {"index"**:** {"knn"**:** true**,** "knn.space_type"**:** "cosinesimil"**,** "refresh_interval"**:** "-1"**,** "merge.scheduler.max_thread_count"**: 1** }
}

设置 index.refresh_interval = 1 可以避免频繁的索引刷新,从而最大化索引吞吐量。并且merge . scheduler . max _ thread _ count = 1将合并限制到单个线程,以在索引本身上花费更多资源。

通过这些设置,我成功地将 20 万个文档编入了 Open Distro Elasticsearch index。重要的一点是像这样定义向量场:

"vector"**:** {"type"**:** "knn_vector"**,** "dimension"**: 768** }

该插件在索引期间构建分层可导航小世界(HNSW)图,用于加速 KNN 矢量搜索。为每个 KNN 场/ Lucene 段对构建图,使得有可能为给定的查询向量有效地找到 K 个最近的邻居。

要运行开放发行版的索引器,触发以下命令:

time python src/index_dbpedia_abstracts_opendistro.py

要进行搜索,您需要将查询向量包装到以下对象中:

"size": docs_count**,** "query": {"knn": {"vector": {"vector": query_vector**,** "k": **10** }}
}

插件支持的最大 k 为 10000。每个片段/碎片将返回最接近查询向量的 k 个向量。然后,候选文件将“累积”到大小数量的结果文件。请记住,如果您对候选项使用后期过滤,很可能每个片段/分片会得到< k 个候选项,因此它也会影响最终的大小

在索引设置中选择的 KNN 空间类型定义了用于查找 K 个最近邻的度量。“cosinesimil”设置对应于以下距离公式:

“余弦米尔”空间类型的距离函数(截图来自开放区

来自插件文档:“余弦相似性公式不包括1 - 前缀。然而,因为 nmslib 将更小的分数等同于更接近的结果,所以它们返回1 - cosineSimilarity用于余弦相似性空间——这就是为什么1 - 被包括在距离函数中。”

在 KNN 空间中,较小的距离对应于更近的矢量。这与弹性搜索评分的工作方式相反:更高的分数代表更相关的结果。为了解决这个问题,KNN 插件将把距离颠倒成一个1 / (1 + distance)值。

我对索引时间、大小和搜索速度进行了测量,平均 10 次查询(所有方法都使用了完全相同的查询)。结果:

KNN 性能开放区,空间类型:cosinesimil

用 GSI APU 和拥抱脸让它更快

在 Solr 和 Lucene 上发表了两篇关于 BERT vector search 的文章后,GSI 科技联系了我,并邀请我测试他们的 Elasticsearch 插件,该插件实现了由 GSI 的关联处理单元(APU)硬件技术支持的分布式搜索。APU 加速向量运算(像计算向量距离)并直接在内存数组中就地搜索,而不是在内存和 CPU 之间来回移动数据。

架构看起来是这样的:

GSI APU 供电的 Elasticsearch 架构的架构(截图由 GSI 科技提供)

# The query flow:
GSI query → Elasticsearch -> GSI plugin -> GSI server (APU) → top k of most relevant vectors → Elasticsearch → filter out → < ktopk=10 by default in single query and batch search

为了使用这个解决方案,用户需要生成两个文件:

  • 带有所需维数向量的 numpy 2D 数组(在我的例子中是 768)
  • pickle 文件,其文档 id 与 Elasticsearch 中所述向量的文档 id 相匹配。

这些数据文件上传到 GSI 服务器后,同样的数据会在 Elasticsearch 中被编入索引。APU 供电搜索在多达 3 块 Leda-G PCIe APU 板上进行。

由于我已经遇到了 bert-as-service 解决方案的索引性能问题,我决定采用上述 SBERT 方法来准备 numpy 和 pickle 数组文件。这让我可以随时自由地进入 Elasticsearch,而无需等待数天。您可以使用这个脚本在 DBPedia 数据上这样做,它允许在嵌入模型之间进行选择。拥抱 _ 脸 _ 句子(SBERT)和嵌入模型。BERT _ un cased _ 768(BERT-as-service)。

接下来,我预计算了 1M SBERT 矢量嵌入,并为矢量嵌入预计算快进了 3 天,我可以将它们索引到我的 Elasticsearch 实例中。我不得不更改索引脚本来解析带有 1M 向量的 numpy&pickle 文件,同时从 DBPedia 归档文件中读取数据,以合并成一个文档接一个文档的索引过程。然后,我将 100k 和 1M 向量索引到单独的索引中,以运行我的搜索(每个索引 10 个查询)。

对于 vanilla Elasticsearch 7.10.1,结果如下:

Elasticsearch 7.10.1 普通矢量搜索性能

如果我们成比例地将使用 bert 即服务方法计算 1M 矢量所需的时间增加到 25920 分钟,那么 SBERT 方法将快 17 倍!索引大小不会改变,因为它取决于 Lucene 编码,而不是选择 BERT 还是 SBERT。通过降低向量精度来压缩索引大小是另一个有趣的研究课题。

对于 elastiknn,我得到了以下数字:

结合 vanilla 和 elastiknn 的平均速度指标,我们得到:

elastic search vanilla vs elastic KNN 矢量搜索性能(图片由作者提供)

在我的硬件上,elastiknn 平均比 Elasticsearch 原生矢量搜索算法快 2.29 倍。

GSI APU 解决方案怎么样?首先,索引速度与上述方法不直接兼容,因为我必须单独制作 numpy+pickle 文件并上传到 GSI 服务器。对 Elasticsearch 进行索引与我上面所做的相当,结果是 1M SBERT 向量的索引为 15G。接下来,我运行同样的 10 个查询来测量平均速度。为了用 GSI Elasticsearch 插件运行查询,我需要将查询向量打包成以下查询格式:

GSI 的矢量查询格式

由于我只上传了 1M 的向量,并在 Elasticsearch 索引中试验了 1M 的条目,所以我得到了 10 次查询的平均速度:92.6 毫秒。这给出了我们的速度图上 X 轴上 1M 标记的上限:

Elasticsearch native、elastiknn、knn 开放发行版和 GSI APU 矢量搜索方法的平均速度比较(图片由作者提供)

图中所有时间均来自 Elasticsearch 报告的“are”值。所以这些数字都不包括将结果传输回客户端的网络速度。然而,GSI 的“take”包括 Elasticsearch 和 APU 服务器之间的通信。

有趣的是,GSI Elasticsearch 插件支持批量查询——这可以用于各种用例,例如当你想运行几个预先设计好的查询来获得数据更新时。一个具体的用例可能是针对用户感兴趣的查询创建用户警报——对于许多商业搜索引擎来说,这是一个非常常见的用例。

要运行批处理查询,您需要像这样更改查询:

作为响应,GSI 插件将并行执行查询,并返回每个查询的前 N 个(由 topK 参数控制)文档 id,余弦相似度为:

GSI 的搜索架构可以扩展到数十亿个文档。在这篇博文中,我用 100 万条 DBPedia 记录测试了它,看到了令人印象深刻的性能结果。

摘要

在这篇博文中,我研究了 4 种方法,可以这样分类:

  • Elasticsearch: vanilla(原生)和 elastiknn(外部插件)
  • 打开 KNN 发行版插件
  • APU 上的 GSI 弹性搜索插件

如果你喜欢留在原来的 Elasticsearch by Elastic,最好的选择是 elastiknn。

如果你愿意开放发行版作为 Elasticsearch by Elastic 的开源替代品,你将赢得 50-150 ms。然而,你可能需要额外的帮助来设置。

如果你喜欢尝试硬件加速的商业解决方案,那么 GSI 插件可能是你的选择,给你相对最快的矢量查询速度。

如果你想进一步探索这篇博文中讨论的主题,请阅读下一部分,如果你有什么要问或要补充的,请留下评论。

关于该主题的进一步阅读

  1. 在 Elasticsearch(矢量搜索的高等数学及更多)中编写您的分数:https://www . elastic . co/guide/en/elastic search/reference/7.6/query-DSL-script-score-query . html # vector-functions
  2. 第三章“寻找相似的物品”影响了这篇博文中使用的 elastiknn 中的随机投影算法:【http://www.mmds.org/
  3. 使用 Vespa、Elasticsearch 和 Open Distro 对 Elasticsearch K-NN 进行最近邻搜索的性能评估:https://github . com/jobergum/dense-vector-ranking-performance/
  4. GSI Elasticsearch 插件:https://medium . com/GSI-technology/scalable-semantic-vector-search-with-elastic search-e79f 9145 ba8e
  5. 开放发行版何去何从:https://www.youtube.com/watch?v=J_6U1luNScg
  6. 非度量空间库(NMSLIB):一个高效的相似性搜索库和工具包,用于评估通用非度量空间的 k-NN 方法:https://github.com/nmslib/nmslib/

使用远程缓存加速容器映像构建

原文:https://towardsdatascience.com/speeding-up-container-image-builds-with-remote-cache-c72577317886?source=collection_archive---------13-----------------------

使用这些缓存技术可以轻松地优化 CI/CD 管道中的容器映像构建

照片由罗宾·皮尔在 Unsplash 上拍摄

在 CI/CD 管道中构建映像可能与在本地机器上构建有很大不同。一个主要的区别是缓存的可用性。在本地环境中,您很可能缓存了以前构建的所有资源、依赖项和图像层,因此您的构建可能只需要几秒钟。另一方面,在 CI 管道中,没有本地缓存,这会导致构建需要几分钟时间。这是有解决办法的,在这篇文章中,我们将看看如何在使用和不使用 Docker 的情况下,为您可能使用的任何 CI/CD 平台解决这个问题。

通用解决方案

适用于任何环境的通用解决方案的想法非常简单——我们需要以某种方式创建缓存或将缓存引入管道。这里我们有两种选择——要么我们将构建器工具(例如 Docker)指向我们的映像存储库,它可以从该存储库中检索映像层并将它们用作缓存,要么我们将这些层存储在文件系统上,我们使其可供管道使用,并从那里获取这些层。无论哪种方式,我们都需要通过将映像推送到存储库或文件系统来创建缓存,然后,在后续构建中,我们会尝试使用它,如果因为缓存未命中而无法使用,我们会用新的层来更新它。

现在,让我们看看如何使用各种工具在实践中做到这一点…

码头工人

这个问题最简单的解决方案是使用 Docker 和 BuildKit 。BuildKit 是对docker build的一组增强,它改进了性能、存储管理并增加了一些额外的特性,包括更好的缓存功能。要用 BuildKit 构建容器映像,我们需要做的就是在每个命令前面加上DOCKER_BUILDKIT=1:

对于曾经使用 Docker 构建过图像的人来说,这个例子应该是不言自明的。这与基本 Docker 用法之间唯一真正的区别是添加了BUILDKIT_INLINE_CACHE=1,它告诉 BuildKit 启用内联缓存导出器。这确保 Docker 将缓存所需的元数据写入映像。这些元数据将在后续构建中使用,以找出可以缓存的图层。上述代码片段中唯一的不同之处是命令输出——在第一次构建期间,我们可以看到 Docker 将缓存导出到存储库,而在第二次构建期间,它导入缓存清单并使用一个缓存层。

使用 BuildKit 作为 Docker 的一部分很方便,但是它隐藏了一些特性和选项。因此,如果您想要对构建和缓存进行更多的控制,那么您可以直接使用上游 BuildKit 项目。为此,您需要从 GitHub 发布页面下载二进制文件,将其解压缩并移动到您的路径中(例如/usr/local/bin/)。最后,您需要启动 BuildKit 守护进程,然后就可以开始构建了:

如果我们想用 upstream BuildKit 执行与 Docker 集成相同的缓存构建,我们需要编写一个稍微复杂一点的命令:

正如您在这里看到的,我们必须指定许多标志和参数,这可能很烦人,但允许很好的可定制性。这种方法的一个优点是我们不需要运行docker push,取而代之的是我们将push=true包含在一个参数中,而buildctl负责推送图像。

以这种方式使用 BuildKit 的另一个优点是能够将图像和缓存层放入单独的存储库或标记中。在本例中,我们将图像本身存储在docker-cached:latest中,而缓存将位于docker-cached:buildcache中:

为了完整起见,我还将提到,不单独安装 BuildKit 也可以利用上面提到的高级特性。为此,你将需要buildx,它是一个扩展构建功能的 Docker CLI 插件。然而,buildxbuildctl有不同的参数,所以您需要根据这里的文档来调整您的构建命令。

也就是说,我们正在做所有这些恶作剧来提高 CI/CD 构建性能,所以在本地运行这些命令对于测试来说是很好的,但是我们需要以某种方式在一些 CI/CD 平台的环境中执行,我选择的环境是 Kubernetes。

为了在 Kubernetes 中实现这一点,我们需要带一些额外的东西,即作为工作空间的图像和卷的凭证:

上面是一个单独的作业,它首先使用 init 容器在 PersistentVolumeClaim 提供的工作空间内创建一个Dockerfile。然后,实际的作业执行构建,如前面所示。它还从名为buildkit-docker-configSecret 中挂载存储库凭证,这是 BuildKit 将缓存层和图像本身推送到存储库所必需的。

为了清楚起见,我省略了上面使用的 PersistentVolumeClaim 和 Secret 的清单,但是如果您想自己测试一下,那么您可以在这里找到那些。

无码头的

然而,Docker 并不是构建映像的唯一工具,它可以帮助我们在 CI/CD 构建期间利用缓存。Docker 的替代品之一是谷歌的 Kaniko。它的优点是它应该作为容器映像运行,这使得它适合 Kubernetes 这样的环境。

考虑到这个工具是为 CI/CD 管道设计的,我们需要在本地模拟相同的条件来测试它。为此,我们需要几个目录和文件用作卷:

上面我们创建了 3 样东西——一个由单层组成的样品Dockerfile,我们将用它来测试。接下来,我们创建了一个cache目录,它将被挂载到容器中,用于存储缓存的图像层。最后,我们创建了包含注册表凭证的config目录,该目录将以只读方式挂载。

在上一节中,我们只查看了使用图像注册/存储库的缓存图像层,但是使用 Kaniko,我们还可以使用本地目录/卷作为缓存源。为此,我们首先需要“预热”用图像层填充缓存:

注意:这一节是关于在没有 docker 的情况下构建图像和缓存图像,然而在 Kubernetes 之外的测试期间,我们仍然需要以某种方式运行 Kaniko 图像,这就是使用 *docker*

Kaniko 项目提供了两个图像— warmerexecutor,上面我们使用了前者,它获取可变数量的图像,并使用它们来填充指定的缓存目录。

缓存就绪后,我们可以开始构建映像了。这次我们使用executor映像,传入 2 个卷——一个用于注册表凭证(以只读方式挂载),另一个用于 workspace,我们用示例Dockerfile预先填充了 workspace。此外,我们指定标志来启用缓存以及最终图像将被推送到的目的地:

这些例子向我们展示了它在理论上是如何工作的,但是在实践中,我们希望在 Kubernetes 上运行它。为此,我们将需要与 BuildKit 示例中相似的一组对象,即——缓存目录的卷声明、工作区(Dockerfile)的卷声明、带有注册表凭证的秘密以及将执行kaniko的作业或 Pod:

这里,假设我们已经使用warmer图像填充了缓存,我们运行kaniko executor,它从/workspace目录中检索Dockerfile,从/cache中检索缓存层,从/kaniko/.docker/config.json中检索凭证。如果一切顺利,我们应该在日志中看到 Kaniko executor找到了缓存层。

从本地卷缓存层可能是有用的,但大多数时候你可能会想使用远程注册表。Kaniko 也可以做到这一点,我们需要做的只是改变几个论点:

我们在这里做的重要改变是我们用--cache-repo替换了--cache-dir标志。此外,我们还能够省略用于缓存目录的卷声明。

除了 Kaniko,还有很多其他工具可以构建容器映像。最值得注意的是podman,它利用buildah来构建图像。然而,使用这两个用于缓存,现在不是一个选项。--cache-from选项在buildah中可用,但是它是 NOOP,所以即使你指定了它,也不会发生什么。因此,如果您想将您的配置项从 Docker 迁移到 Buildah,并且需要缓存,那么您需要等待这个问题得到实现/解决。

结束语

本文描述了我们如何利用层缓存来提高构建性能。如果您在映像构建中遇到糟糕的性能,问题可能不在于丢失缓存,而在于您的Dockerfile中的命令。因此,在你开始实现层缓存之前,我建议你先优化一下Dockerfiles的结构。此外,只有当您拥有结构良好的Dockerfiles时,缓存才会起作用,因为在第一次缓存未命中之后,就不能使用更多的缓存层了。

除了缓存层之外,您可能还想缓存依赖项,这样可以节省从 NPM、PyPI、Maven 或其他工件库下载库所需的时间。一种方法是使用 BuildKit 和它的--mount=type=cache标志,这里的描述为。

本文最初发布于martinheinz . dev

加速数据分析

原文:https://towardsdatascience.com/speeding-up-data-analysis-d2aa65ff0347?source=collection_archive---------41-----------------------

使用最新版本的 Mitosheets 加速数据分析

来源:作者

数据分析是数据科学的一个组成部分;它有助于解开数据,找出所有隐藏的模式,以便我们可以使用它们来产生有意义的见解。数据分析也有助于找出不同数据点之间的关系。

数据分析是建立模型的第一步,因为我们应该能够知道数据是关于什么的,它包含什么。它还可以用于操作数据并为建模做准备。做所有这些需要花费大量的时间和精力,因为我们需要编写大量的代码并创建不同的可视化和表格来更好地理解数据。

使用最新版本的 Mitosheets 我们可以轻松地执行所有这些操作,甚至不需要编码。

Mitosheet 是一个开源的 python 库,用于在一行代码中执行数据分析。它作为一个界面,用户可以通过单击不同的选项来执行与数据分析相关的不同操作。

在本文中,我们将探索在数据集上执行数据分析的 Mitosheet 。

让我们开始吧…

安装所需的库

为了安装 mitosheets ,我们需要运行下面给出的 pip 安装命令。Mitosheets 在 Jupyter Lab 上运行,因此我们应该在系统中安装它。

!pip install mitoinstaller
!python -m mitoinstaller install

安装后,我们需要启动 Jupyter Lab,这样我们就可以使用 Mitosheets 。我们可以使用下面给出的命令从 Anaconda 接口或 Anaconda 提示符启动它。

start jupyter lab

导入所需的库

我们需要导入的唯一库是 mitosheets ,我们将使用下面给出的命令导入它。我们还将使用下面给出的命令启动 mitosheet ,这是我们执行分析所需的唯一代码。

import mitosheet
mitosheet.sheet()

主屏幕(来源:作者)

正如您在这里看到的,这是我们将执行数据分析的 Mitosheets 的主屏幕,它包含不同的选项,如导入和导出数据、添加或删除列、创建不同的可视化和数据透视表。

正在加载数据集

接下来,我们将加载我们将要处理的数据集,您可以选择任何数据集。对于本文,我们使用的是汽车设计数据集。

导入数据集(来源:作者)

这里我们可以看到导入数据集是多么容易,现在我们将开始我们的分析。

我们将从查看不同列的统计属性开始。为此,我们需要单击列名前面的“漏斗”,然后单击右侧面板中的“汇总统计”。

统计摘要(来源:作者)

在右边的面板中,您还可以看到另外两个不同的选项卡,显示值(百分比)和排序数据。现在让我们创建一个可视化。

方框图(来源:作者)

在这里你可以看到我们如何使用 Mitosheets 中的图形选项创建了一个方框图。我们只需要选择轴和我们想要绘制的图表类型。它包含各种各样的图表,我们可以很容易地创建。

创建数据透视表是 Mitosheets 提供的功能之一。在下图中,您可以看到我们使用 pivot 选项并选择所需的行、列和值创建了一个数据透视表。

数据透视表(来源:作者)

类似地,我们可以探索多种功能,如添加或删除列、合并数据等。此外,我们可以在预处理后保存数据集。

继续尝试使用不同的数据集并执行数据分析。如果您发现任何困难,请在回复部分告诉我。

本文是与 Piyush Ingale 合作的。

在你走之前

感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。可以查看我的Github*简介针对不同的数据科学项目和包教程。还有,随意探索* 我的简介 ,阅读我写过的与数据科学相关的不同文章。

使用 PostgreSQL 和 TimescaleDB 加速数据分析

原文:https://towardsdatascience.com/speeding-up-data-analysis-with-timescaledb-and-postgresql-e3bdfee41108?source=collection_archive---------38-----------------------

理解大数据

评估、清理和转换数据时的常见问题 PostgreSQL 和 TimescaleDB 如何帮助我解决这些问题。

由马库斯·温克勒通过 Unsplash 创作

  1. 常用数据分析工具和“问题”
  2. 数据分析问题#1:存储和访问数据
  3. 数据分析问题#2:最大化分析速度和计算效率(数据集越大,问题越大)
  4. 数据分析问题#3:存储和维护数据分析脚本
  5. 数据分析问题#4:轻松利用新技术或附加技术
  6. 包扎

时序数据无处不在,它驱动着每个行业的决策。时间序列数据共同代表了一个系统、过程或行为如何随时间变化。了解这些变化有助于我们解决众多行业的复杂问题,包括可观察性、金融服务、物联网,甚至职业足球。

根据他们构建的应用程序的类型,开发人员最终会收集数百万行时间序列数据(有时每天甚至每小时会收集数百万行数据!).理解这种高容量、高保真度的数据需要一组特殊的数据分析技能,而这些技能在传统的开发人员技能中并不常见。为了执行超越基本问题的时间序列分析,开发人员和数据分析师需要专门的工具,随着时间序列数据日益突出,这些工具的效率变得更加重要。

通常,数据分析师的工作可以归结为评估清理转换,以及建模数据。根据我的经验,我发现这些行动对我从数据中获得理解是必要的,我将在这篇文章中称之为“数据分析生命周期”。

数据分析生命周期。图片作者。

Excel、R 和 Python 可以说是一些最常用的数据分析工具,虽然它们都是很棒的工具,但它们可能并不适合每项工作。从经验上讲,这些工具在生命周期的早期阶段对于“数据管理”尤其低效;具体来说,预建模工作中涉及的评估数据清理数据转换数据步骤。

随着我处理更大和更复杂的数据集,我开始相信为特定类型的数据(如时间序列数据)构建的数据库对于数据分析更有效。

在这个博客系列中,我将深入讨论分析生命周期中的三个数据管理步骤,并演示如何使用 TimescaleDB 和 PostgreSQL 作为强大的数据分析工具。

在这篇介绍性的文章中,我将探讨一些我在使用流行的数据分析工具时遇到的常见挫折,并从这里开始,深入探讨我是如何使用 TimescaleDB 和 PostgreSQL 来帮助缓解这些棘手问题的。

在以后的帖子中,我们将关注:

  • TimescaleDB 和 PostgreSQL 数据分析功能如何取代通常在 Python 和 pandas 中执行的数据管理工作
  • TimescaleDB 和 PostgreSQL 与 Python 和 pandas 在数据管理任务中的比较(标准数据分析工作流的基准测试)
  • 如何使用 TimescaleDB、PostgreSQL 和 Python 进行端到端的深度数据分析,使用来自纽约市出租车和豪华轿车委员会 (NYC TLC)的真实黄色出租车数据。

常用数据分析工具和“问题”

正如我们已经讨论过的,用于数据分析的三个最流行的工具是 Excel、R 和 Python。虽然它们本身是很好的工具,但它们并没有优化到能有效地执行分析过程中的每一步。

尤其是大部分数据科学家(包括我自己!)随着数据量的增长,或者需要月复一月地重复相同的分析,为类似的问题而苦恼。

这些困难包括:

  • 数据存储和访问:存储和维护用于分析的数据的最佳位置是哪里?
  • 数据大小及其对分析的影响:如何提高数据管理任务的效率,尤其是在数据扩展时?
  • 脚本存储和可访问性:我能做些什么来改进数据管理脚本存储和维护?
  • 轻松利用新技术:我如何设置我的数据分析工具链,以便轻松过渡到新技术?

所以系好安全带,让你的胳膊和腿一直呆在车里,让我们开始研究这些问题吧!

数据分析问题#1:存储和访问数据

为了进行数据分析,你需要访问…数据。

管理数据的存储位置,以及访问数据的难易程度是分析过程中的第一步(通常也是最重要的一步)。每当我开始一个新的数据分析项目时,这通常是我第一次陷入困境的地方。不管原始数据源是什么,我总是问“当我开始处理数据管理过程时,存储和维护数据的最佳位置在哪里?”

尽管数据分析师使用数据库存储和查询数据变得越来越普遍,但它仍然不是普遍存在的。很多时候,原始数据是在生成 JSON 的 CSV 文件或 API 流中提供的。虽然这对于较小的项目来说可能是可管理的,但是它可能很快变得难以维护,并且难以在项目之间进行管理。

例如,让我们考虑如何使用 Python 作为我们选择的数据分析工具。

在使用 Python 进行数据分析时,我可以选择通过文件/API 或数据库连接来获取数据。

如果我在分析过程中使用文件或 API 来查询数据,我经常会遇到这样的问题:

  • 文件在哪里?如果某个 API 的 URL 或参数发生变化,会发生什么情况?
  • 如果制作了重复的文件会怎么样?如果只更新了一个文件,而没有更新另一个文件呢?
  • 我如何最好地与同事共享这些文件?
  • 如果多个文件相互依赖,会发生什么?
  • 如何防止不正确的数据被添加到 CSV 的错误列中?(即。字符串应该在的地方是十进制数)
  • 非常大的文件怎么办?10MB、100MB、1GB、1TB 大小的文件的接收率是多少?

在经历了一个又一个项目的初始问题后,我知道一定会有更好的解决方案。我知道我的数据需要一个真实的来源——很明显,一个专门的 SQL 数据库可能就是我的答案!

现在,让我们考虑一下,如果我连接到 TimescaleDB。

通过将我的时间序列数据导入 TimescaleDB,我可以为我的所有数据创建一个真实的来源。因此,与他人协作变得像共享数据库访问权限一样简单。对数据库中数据管理过程的任何修改都意味着所有用户可以同时访问相同的更改,而不是通过解析 CSV 文件来验证我的版本是否正确。

此外,数据库通常可以处理比用 Python 或 r 编写的脚本大得多的数据负载。TimescaleDB 旨在高效、经济地容纳、维护和查询数 TB 的数据(从计算角度和您的钱包角度而言)。有了像连续聚合和本机列压缩这样的功能,存储和分析多年的时间序列数据变得高效,同时仍然易于访问。

简而言之,随着时间的推移管理数据,尤其是当数据来自不同来源时,高效地维护和访问数据可能是一场噩梦。但是,不一定要这样。

数据分析问题#2:最大化分析速度和计算效率(数据集越大,问题越大)

Excel、R 和 Python 都能够执行数据分析“生命周期”的前三个步骤:评估、清理和转换数据。然而,这些技术在处理过程中通常没有针对速度或计算效率进行优化。

在过去几年的许多项目中,我发现随着数据集大小的增加,导入、清理和转换数据集的过程变得更加困难、耗时,在某些情况下甚至是不可能的。对于 Python 和 R 来说,解析大量数据似乎要花很长时间,而 Excel 一旦遇到数百万行就会崩溃。

当我需要为聚合或数据转换之类的事情创建额外的表时,事情变得尤其困难:根据数据的大小、我使用的计算机或分析的复杂性,一些代码行可能需要几秒钟,或者在极端情况下,几分钟才能运行。

虽然几秒钟或几分钟可能看起来不算多,但当你在执行一个月需要运行数百或数千次的分析时,它们累积起来会导致数小时或数天的生产力损失!

为了说明这一点,我们再来看一个 Python 例子。

假设我正在处理来自 Kaggle 的物联网数据集。该套件包含两个表格,一个指定德克萨斯州休斯顿一个家庭的能源消耗,另一个记录天气情况。

要使用 Python 进行分析,我的分析的第一步是获取数据并观察它。

当使用 Python 来做这件事时,我会像这样运行代码👇

import psycopg2import pandas as pdimport configparser## use config file for database connection informationconfig = configparser.ConfigParser()config.read('env.ini')## establish connectionconn = psycopg2.connect(database=config.get('USERINFO', 'DB_NAME'),host=config.get('USERINFO', 'HOST'),user=config.get('USERINFO', 'USER'),password=config.get('USERINFO', 'PASS'),port=config.get('USERINFO', 'PORT'))## define the queries for selecting data out of our databasequery_weather = 'select * from weather'query_power = 'select * from power_usage'## create cursor to extract data and place it into a DataFramecursor = conn.cursor()cursor.execute(query_weather)weather_data = cursor.fetchall()cursor.execute(query_power)power_data = cursor.fetchall()## you will have to manually set the column names for the data frameweather_df = pd.DataFrame(weather_data, columns=['date','day','temp_max','temp_avg','temp_min','dew_max','dew_avg','dew_min','hum_max','hum_avg','hum_min','wind_max','wind_avg','wind_min','press_max','press_avg','press_min','precipit','day_of_week'])power_df = pd.DataFrame(power_data, columns=['startdate', 'value_kwh', 'day_of_week', 'notes'])cursor.close()print(weather_df.head(20))print(power_df.head(20))

总之,使用我的 2019 款 32GB 内存的 MacBook Pro 笔记本电脑,这段代码运行了 2.718 秒。

但是,如果我用数据库中的 SQL 运行这个等价的脚本呢?

select * from weatherselect * from power_usage

作者图片

这个查询只花了 0.342 秒,比 Python 脚本快了近 8 倍。

当我们考虑到 Python 必须连接到数据库,然后运行 SQL 查询,然后解析检索到的数据,然后将其导入到 DataFrame 中时,这种时间差是很有意义的。虽然几乎三秒钟的时间很快,但是随着脚本变得更加复杂以及更多数据管理任务的增加,额外的处理时间也会增加。

拉进数据观察只是我分析的开始!当我需要执行转换任务时,比如聚合数据,会发生什么情况?

对于这个数据集,当我们查看power_usage表时——如上所示——每小时记录千瓦时读数。如果我想进行每日分析,我必须将每小时的数据汇总到“天时段”中。

如果我使用 Python 进行这种聚合,我可以使用类似👇

# sum power usage by day, bucket by day## create column for the dayday_col = pd.to_datetime(power_df['startdate']).dt.strftime('%Y-%m-%d')power_df.insert(0, 'date_day', day_col)agg_power = power_df.groupby('date_day').agg({'value_kwh' : 'sum', 'day_of_week' : 'unique', 'notes' : 'unique' })print(agg_power)

…运行时间为 0.49 秒(这不包括导入数据的时间)。

或者,使用 TimescaleDB [time_bucket()](https://docs.timescale.com/api/latest/hyperfunctions/time_bucket/?utm_source=tds&utm_medium=blog&utm_id=tsdb-for-data-analysis&utm_content=time-bucket-docs)函数,我可以使用下面的查询直接在数据库中进行聚合👇

selecttime_bucket(interval '1 day', startdate ) as day,sum(value_kwh),day_of_week,notesfrom power_usage pugroup by day, day_of_week, notesorder by day

作者图片

…这只需要 0.087 秒,比 Python 脚本快 5 倍以上。

你可以在这里看到一个模式。

创建 TimescaleDB 是为了高效地查询和存储时间序列数据。但是简单地查询数据只是触及了 TimescaleDB 和 PostgreSQL 功能所提供的可能性的表面。

TimescaleDB 和 PostgreSQL 提供了广泛的工具和功能,可以取代使用其他工具来评估、清理和转换数据的需求。一些 TimescaleDB 功能包括连续聚合、压缩和超功能;所有这些都允许您直接在数据库中完成几乎所有的数据管理任务。

当我直接在 TimescaleDB 中执行分析的评估、清理和转换步骤时,我不再需要使用额外的工具(如 Excel、R 或 Python)来执行数据管理任务。然后,我可以将经过清理和转换的数据直接导入 Excel、R 或 Python,为建模做好准备。

数据分析问题#3:存储和维护数据分析脚本

将 Excel、R 或 Python 专门用于整个数据分析工作流的另一个潜在缺点是,分析数据的所有逻辑都包含在一个脚本文件中。与拥有许多不同数据源的问题类似,维护脚本文件可能会很不方便和混乱。

我和许多数据分析师遇到的一些常见问题包括:

  • 丢失文件
  • 无意中创建重复文件
  • 更改或更新一些文件,但不更改或更新其他文件
  • 需要编写和运行脚本来访问转换后的数据(参见下面的示例)
  • 每当添加新的原始数据时,花费时间重新运行脚本(参见下面的示例)

虽然您可以使用代码库来克服其中的一些问题,但它不能解决最后两个问题。

让我们再次考虑我们的 Python 场景。

假设我使用了一个专门用于我所有数据分析任务的 Python 脚本。如果我需要导出转换后的数据以用于德克萨斯州的能源消耗报告,会发生什么情况?

很可能,我必须在脚本中添加一些代码来导出数据,然后再次运行脚本来实际导出数据。根据脚本的内容和转换数据所需的时间,这可能会非常不方便和低效。

如果我也得到一堆新能源使用和天气数据呢?为了将新的原始数据合并到现有的可视化或报告中,我需要再次运行脚本,并确保我的所有数据管理任务都按预期运行。

数据库函数,如连续聚合和物化视图,可以创建转换后的数据,这些数据可以直接从数据库中存储和查询,而无需运行脚本。此外,我可以为连续聚合创建策略,以便在任何时候修改原始数据时,定期更新转换后的数据。由于这些策略,我不必担心运行脚本来重新转换数据以供使用,从而高效地访问更新的数据。

数据分析问题#4:轻松利用新技术或附加技术

最后,数据分析生命周期的最后一步:建模。如果我想使用新的工具或技术来创建可视化,很难轻松地将转换后的数据用于其他地方的建模或可视化。

Python、R 和 Excel 的可视化和建模能力都非常棒。然而,当你的公司或团队想要采用一种新工具时会发生什么呢?

根据我的经验,这通常意味着要么在分析过程中增加另一个步骤,要么重新发现如何在新技术中执行评估、清理和转换步骤。

例如,在我以前的一份工作中,我被要求将我的一部分分析转换成 Power BI,用于业务分析。我的利益相关者想要的一些可视化要求我从 Python 脚本中访问转换后的数据。当时,我可以选择从 Python 脚本中导出数据,或者想出如何在 Power BI 中直接转换数据。这两种选择都不理想,而且肯定要花费额外的时间。

当谈到采用新的可视化或建模工具时,使用数据库来评估、清理和转换数据可以再次对您有利。大多数可视化工具——如 Grafana 、 Metabase 或Power BI——允许用户直接从数据库导入数据。

由于我可以在 TimescaleDB 中完成大部分数据管理任务,因此添加或切换工具——比如使用 Power BI 实现仪表板功能——变得非常简单,只需连接到我的数据库,提取管理的数据,并使用新工具进行可视化和建模。

包扎

总之,Excel、R 和 Python 都是很好的分析工具,但可能不是每项工作的最佳工具。例证:我在时间序列数据分析上的挣扎,尤其是在大数据集上。

借助 TimescaleDB 和 PostgreSQL 功能,您可以直接在数据库中存放数据并执行数据分析的评估、清理和转换,并在此过程中解决许多常见的数据分析难题(我希望如此!—在本文中演示)

在接下来的博客文章中,我将探索 TimescaleDB 和 PostgreSQL 功能与 Python 的比较,比较 TimescaleDB 和 PostgreSQL 性能与 Python 和 pandas 的数据管理任务,并深入研究 TimescaleDB(用于数据管理)和 Python(用于建模和可视化)的数据分析。

如果你对 TimescaleDB、时间序列数据或上述任何功能有疑问,欢迎加入 Timescale 的社区 Slack ,在那里你会发现一个由时间序列爱好者和各种 Timescale 团队成员(包括我!).

如果你对使用 TimescaleDB 和 PostgreSQL 感兴趣,你可以注册30 天免费试用或安装 TimescaleDB 并在你当前的 PostgreSQL 实例上管理它。我们还有一堆很棒的教程来帮助你入门。

下次见!

本帖由米兰达·奥尔原创,发表于 2021 年 9 月 9 日https://blog.timescale.com

加速数据可视化

原文:https://towardsdatascience.com/speeding-up-data-visualization-e370f4470b86?source=collection_archive---------23-----------------------

在一行代码中使用 Klib 进行数据可视化

来源:作者

数据可视化帮助我们理解数据,找出不同的模式、关联和视觉洞察。根据我们试图解决的问题,可以使用各种各样的可视化方法。Python 提供了 N 个可以帮助数据可视化的库,其中一些是 Seaborn、Matplotllib 等。

可视化包含不同数据类型的列的数据可能有点困难,因为我们需要识别这些列并相应地创建一个图。Klib 可以解决这个问题,只需一行代码就可以创建不同类型的可视化效果。

Klib 是一个开源的 python 库,可以轻松地对数据进行可视化、分析和预处理。在本文中,我们将探索 Klib 提供的不同类型的可视化。

让我们开始吧…

安装所需的库

我们将从使用 pip 安装 Klib 开始。下面给出的命令可以做到这一点。

!pip install klib

导入所需的库

在这一步中,我们将导入加载数据集和可视化数据集所需的库。

import klib
import pandas as pd

正在加载数据集

对于这篇文章,我使用的是可以从 Kaggle 下载的汽车数据集。该数据集包含分类列和数字列。

df = pd.read_csv("/content/Automobile_data.csv")
df

数据集(来源:作者)

创建可视化

在这一步中,我们将为分类和数字数据类型创建不同类型的可视化。

  1. 相关矩阵
klib.corr_mat(df)

相关性(来源:作者)

2。相关图

相关图可以根据正相关和负相关来划分。

klib.corr_plot(df, split="pos")
klib.corr_plot(df, split="neg")

积极的(来源:作者)

否定的(来源:作者)

3。分类图

klib.cat_plot(df)

分类(来源:作者)

4。分布图

klib.dist_plot(df)

分发(来源:作者)

5。缺失值图

klib.missingval_plot(df)

缺失数据(来源:作者)

继续尝试不同的数据集,并使用 Klib 创建不同的可视化效果。如果您发现任何困难,请在回复部分告诉我。

本文是与 Piyush Ingale 合作完成的。

在你走之前

感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。可以查看我的Github*简介针对不同的数据科学项目和包教程。还有,随意探索* 我的简介 ,阅读我写过的与数据科学相关的不同文章。

辣咖啡研磨配送

原文:https://towardsdatascience.com/spicy-coffee-grind-distributions-e4d73c6e3e1f?source=collection_archive---------27-----------------------

咖啡数据科学

检查热咖啡渣

去年,我一头扎进了一个彻底的调查中,调查咖啡豆和研磨粉的温度是如何影响萃取和味道的。这是一个到处探索的领域,许多人发现当加热咖啡豆时,他们的喷射流变慢了。结果是,尽管热的豆子会影响研磨的方式,但是研磨出来的东西是热的。所以我想更好地了解热磨豆的研磨分布。

我从几个方面看了这些理由。我在设置 13(浓缩咖啡的典型设置)和设置 50 下研磨。对于设置 50,我使用 800 微米的筛子过滤掉较大的块。这里的目的是了解更高温度的咖啡豆如何影响咖啡豆的更软的部分,当设置为 50 时,通常会产生更细的颗粒。

然后我拍摄了一些图像,并使用的一些图像处理来确定研磨分布。

研磨发行版!

他们看起来都很相似。很难看出有什么不同。

所以我把我的范围缩小到小于 500 微米,这是浓缩咖啡中大多数粒子的范围。

更清楚的是,74C 时的 S50 向更细的颗粒转变。S50 在 20℃和 51℃之间略有变化,但最大的变化是在 74℃。很有可能,将咖啡豆加热到那个温度,尤其是在微波炉中,会导致它们稍微变熟。

所以我们只看 S13:

在这种情况下,分布在 51C 而不是 20C 处具有更少的低于 200um 的颗粒,表明向右移动而不是像 S50 那样向左移动。

我们可以回到同样的范围,300 微米以下,差别非常明显。然而,在这种情况下,随着温度的升高,会向左移动。

虽然这只是在一些样品上,但它似乎表明热量对咖啡的不同部分有不同的影响。对于软的内部,颗粒尺寸变得更小,如 S50 数据所示。对于其余部分,颗粒尺寸稍微变大。

这让我想知道我有多了解研磨,以及研磨设置如何影响咖啡豆的不同部分。

如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以在中和 Patreon 上关注我。

我的进一步阅读:

浓缩咖啡系列文章

工作和学校故事集

个人故事和关注点

乐高故事启动页面

摄影飞溅页

使用图像处理测量咖啡研磨颗粒分布

改善浓缩咖啡

断奏生活方式概述

测量咖啡磨粒分布

咖啡萃取

咖啡烘焙

咖啡豆

浓缩咖啡用纸质过滤器

浓缩咖啡篮及相关主题

意式咖啡观点

透明 Portafilter 实验

杠杆机维护

咖啡评论与思考

咖啡实验

5 分钟后启动新的 MirrorMaker

原文:https://towardsdatascience.com/spin-up-new-mirrormaker-in-5-minutes-a28c14bcde9f?source=collection_archive---------23-----------------------

使用 Kubernetes 部署脚本的逐步演练

图片由Pixabay.com的树 23 提供

正如最近包含在 Apache Kafka 中以及在我的前一篇博客中介绍的那样, new MirrorMaker 成为了官方认证的开源工具,可以在数据中心的两个 Kafka 实例之间复制数据。

为了获得新 MirrorMaker 的第一手经验,在本文中,我们将在本地 Kubernetes 上进行端到端部署。

作为先决条件,在执行以下步骤之前,需要在本地安装 Minikube 和一个虚拟机监视器实例(例如 VirtualBox 、VMWare Fusion……)。

注意:以下使用的脚本可以在 Kubernetes 集群中使用,但不保证生产质量部署

第一步:开始本地 Kubernetes

minikube start --driver=<driver_name> --kubernetes-version=v1.15.12 --cpus 4 --memory 8192

如果使用 VirtualBox ,< driver_name >将会是“VirtualBox”

步骤 2:克隆 Kubernetes 部署脚本并启动 Kafka

克隆 repo(https://github.com/ning2008wisc/minikube-mm2-demo)并运行以下命令来创建名称空间,2 个 kafka 实例

kubectl apply -f 00-namespace
kubectl apply -f 01-zookeeper
kubectl apply -f 02-kafka
kubectl apply -f 03-zookeeper
kubectl apply -f 04-kafka

然后验证 2 个 kafka 集群正在运行,每个集群有 3 个节点

kubectl config set-context --current --namespace=kafka
kubectl get podsNAME                               READY   STATUS    RESTARTS   AGE
kafka-0                            1/1     Running   0          2m5s
kafka-1                            1/1     Running   0          86s
kafka-2                            1/1     Running   0          84s
kafka2-0                           1/1     Running   0          119s
kafka2-1                           1/1     Running   0          84s
kafka2-2                           1/1     Running   0          82s
zookeeper-<hash>                   1/1     Running   0          2m8s
zookeeper-backup-<hash>            1/1     Running   0          2m2s

步骤 3:部署新的 MirrorMaker

镜子制造者将由头盔部署。在本地安装 Helm,然后如下初始化:

helm init --tiller-namespace kafkakubectl create serviceaccount --namespace kafka tillerkubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kafka:tillerkubectl patch deploy --namespace kafka tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'

为了最大限度地减少占用空间,MirrorMaker 被部署为分布式和独立的 kubernetes 服务,而不是设置 Kafka Connect 集群并通过 Kafka Connect REST 接口部署 MirrorMaker。

cd kafka-mm
helm --tiller-namespace kafka install ./ --name kafka-mm

检查 MM 2 的日志,确保它运行正常

kubectl logs -f kafka-mm-<hash> -c kafka-mm-server

步骤 4:用 Kafka 实例测试 MirrorMaker

现在,让我们在源 kafka 集群(kafka-{0,1,2})上生产一些东西,并从目标集群(kafka2-{0,1,2})上消费,以验证数据同时被镜像。

打开一个新的终端,切换到kafka名称空间,登录到source Kafka 集群的代理节点,然后启动控制台生成器

kubectl exec -i -t kafka-0 -- /bin/bash
bash-4.4# unset JMX_PORT
bash-4.4# /opt/kafka/bin/kafka-console-producer.sh  --broker-list localhost:9092 --topic test

打开另一个新终端,切换到kafka名称空间,登录到target Kafka 集群的代理节点,然后启动控制台消费者

kubectl exec -i -t kafka2-0 -- /bin/bash
bash-4.4# unset JMX_PORT
bash-4.4# /opt/kafka/bin/kafka-console-consumer.sh  --bootstrap-server localhost:9092 --topic primary.test

现在在控制台生成器中键入一些随机字符。预计在控制台消费者处会同时看到相同的字符。

步骤 5:监控 MirrorMaker

为了跟踪性能和健康状况,MirrorMaker 通过 JMX bean 公开了许多指标。下面是如何通过端口转发快速验证它们的原始格式。

kubectl port-forward kafka-mm-<hash> 8081:8081

打开本地 web 浏览器,输入 http://localhost:8081/ 以原始和纯文本格式查看相关指标。

结论

在接下来的几篇博客中,我计划围绕新的 MirrorMaker 介绍更多有趣的话题,包括:

  • 跨数据中心的一次性消息传递保证
  • 从现有镜像解决方案迁移到 MM2 的工具

更多文章敬请关注!

使用 MLflow 部署插件在 GCP 人工智能平台上旋转您的模型

原文:https://towardsdatascience.com/spin-up-your-models-in-gcp-ai-platform-with-mlflow-deployment-plugin-c0198077dca1?source=collection_archive---------20-----------------------

MLflow 插件允许集成到任何定制平台。这是一个从实验跟踪到模型生产的绝佳机会。今天让我们看看如何在 MLflow 中实现一个部署插件

Vishnu Mohanan 在 Unsplash 上的图片

https://medium.com/@stefanobosisio1/membership

之前关于 MLflow 的文章:

  • 使用 MLflow 扩展您的模型开发
  • 改进您的 MLflow 实验,跟踪历史指标

目录

— 什么是 MLflow 插件?
——MLflow AI-platform 插件
— — 我们需要什么来创建一个新的 ml flow 插件?
—插件文件夹和模型模板文件的结构
—部署跟踪界面
—创建捆绑方法
—上传捆绑方法
—创建模型方法
—更新部署方法
—删除部署方法

什么是 MLflow 插件?

MLflow 是一个与框架无关的机器学习工具,可以覆盖整个 ML 过程,从数据探索到模型开发、调整和部署。MLflow Python API 非常通用,允许开发人员为不同 ML 框架和后端完全集成 MLflow。插件是 MLflow 提供的集成之一。插件允许用户拥有额外的 MLflow 兼容组件,例如,可以保存特定服务的人工制品(例如,在专用数据库上)。此外,可以开发插件来接受第三方认证,或者将任何模型部署到定制平台。

在这里你可以找到 MLflow 插件优秀文档:https://www.mlflow.org/docs/latest/plugins.html而在这里:https://www . mlflow . org/docs/latest/plugins . html # deployment-plugins你可以找到 ml flow 部署插件的列表。

在这篇文章中,我们将看到如何实现一个插件,这个插件可以将一个模型从开发带到 GCP 人工智能平台上。特别是,我们将理解创建一个新插件需要什么元素,以及我们需要什么功能来部署一个定制模型到 AI 平台。这将是一个可以进一步开发的框架,以在我们的 ML Ops 框架中获得一个更加自动化和自治的组件。

MLflow 人工智能平台插件

我们需要什么来创建一个新的 MLflow 插件?

最终,MLflow 插件是一个定制的 Python 包,它必须满足主部署对象的以下约束:

  • 必须是mlflow.deployments.BaseDeploymentClient的子类;
  • 它必须具有run_localtarget_help功能。前者允许本地测试——我们不会在本教程中实现这个功能——而后者可以返回有用的信息;
  • 它必须具有以下方法(由BaseDeploymentClient继承):1) create_deployment,用于定义部署过程的主要方法,2) update_deployment,用于更新部署信息,3) delete_deployment,用于删除已部署的模型,4) list_deployments,用于列出所有已部署的模型,5) get_deplyoment,用于检索特定已部署模型的信息,6) predict,用于直接从 MLflow 返回预测的主要方法。

特别是,我们的目标是 GCP 人工智能平台。在 AI 平台上,将模型部署为端点有一条路径可循:https://cloud . Google . com/AI-platform/prediction/docs/deploying-models。因此,我们可以构建这个路径,以便在我们的部署方法create_deployment中轻松实现,如图 1 所示:

  • 首先,代码检索所有需要的配置设置(例如production_bucketrun_id、工件等等)
  • 然后创建一个“部署包”。代码寻找模型工件的位置、生产 uri,它为我们的模型创建安装包以及模型的端点 API Python 代码— create_bundle()
  • 此时,可以将捆绑包推到特定的生产桶— upload_bundle()
  • AI 平台 Python API 开始在平台上创建模型容器— create_model()
  • 一个 json 请求被发送到 AI 平台以继续模型部署— update_deployment()

图 1:主部署类方法 create_deployment()遵循 AI 平台路径将新模型部署到端点

此外,我们必须创建delete_deploymentget_deploymentlist_deployments方法,始终使用 AI 平台 API,以满足 MLflow 部署插件的需求。

插件文件夹和模型模板文件的结构

首先,插件包文件和文件夹的结构可以定义如下:

mlflow_ai_plugin/ mlflow_ai_plugin/__init__.pyDeploymentTrackingInterface.pymodel_setup.pypredictor_template.pyrequirements.txtsetup.py

主部署类包含在入口点DeploymentTrackingInterface.py中。predictor_template.py是模型端点 API 的模板版本:

2:给定模型的模板化 API。在这个例子中,我们正在处理 load_iris,注释指出可以使用一个预处理器

API 满足了 AI 平台的需求。首先,类构造函数__init__读取输入模型和预处理器——为了简单起见,我们在这里注释掉了预处理器的位。方法predict返回模型的预测。一个输入数据集被转换成一个 NumPy 数组作为np.array(instances),并通过self._model.predict(inputs).计算概率。最后,方法from_path允许使用 MLflow 在 AI 平台中旋转模型。任何模型的 artefact 文件(如pickle, joblib, h5)都可以作为mlflow.pyfun.load_model()通过 MLflow 读取。这里的MODEL_ARTIFACT_URI是一个模板化的关键字,当部署脚本运行时,可以通过 main 方法create_bundle()用模型工件 uri 替换它,我们将在后面看到。

为了让模型的 API predictor_template.py工作,需要一个setup.py文件,这样 AI-platform 就知道需要哪些包来安装模型。model_setup.py是模型设置文件的模板版本。在本例中,设置文件相当简单,但是可以进行调整,例如,由数据科学家在部署时进行调整:

图 3:模型的 setup.py 文件的模板版本

同样,在方法create_bundle中,模板关键字MODEL_VERSION被当前模型版本所替代。该文件将创建一个名为deploy_from_script的包,它的脚本是predictor.py——它是从predictor_template.py创建的——它的依赖项是通过install_requires安装的。

部署跟踪界面

是我们插件的核心,所有人工智能平台的步骤都在这里被触发和控制。

配置和构造器

第一步是定义作业常量变量、配置和对象构造函数。

图 4:定义插件常量变量,如 AI_PLATFORM、GS_BUCKET、TODAY 和 MLFLOW_AIPLATFORM_PATH。

图 4 显示了导入语句和常量变量。在这些导入中,值得记住的是我们需要导入from mlflow.deployments import BaseDeploymentClientimport mlflow_ai_plugin,这是包本身,因此将有可能检索模板化文件predictor_template.pymodel_setup.py的路径。最后,我们可以在这里定义 MLflow 插件需要的两个功能,分别是run_localtarget_help——这里就不实现了。

图 Config 类定义了我们使用 AI 平台所需的所有配置设置

对于人工智能平台和流程配置,我们可以定义一个类Config,它读取包含以下所有变量的字典的输入:

  • 这是我们正在进行的 GCP 项目
  • production_bucket生产用桶的名称
  • production_route我们希望在production_bucket中存储所有模型工件的路径
  • run_idml flow 模型的运行 id,因此我们可以选择特定的模型
  • tracking_uri是 MLflow UI uri,例如http://localhost:5000
  • prediction_script定义了predictor_template.py的路径,该路径将被create_bundle()读取以容纳模型的端点信息
  • tmp_dir定义一个临时路径,插件可以在这里创建文件
  • setup_filemodel_setup.py文件路径,将由create_bundle()读取
  • model_name生产中的型号名称
  • version_name型号的版本

部署协议

图 6 显示了部署协议,它定义了在 MLflow 中实现插件所需的所有方法,以及将模型部署到 AI 平台的所有方法。构造函数__init__定义了来自Config类的配置设置——如上所述。

图 6:部署协议定义。这个类是 AI 平台 MLflow 插件的核心,定义 MLflow 所需的方法和 AI 平台方法来部署模型

创建捆绑方法

图 7 显示了创建模型“包”,即模型文件的代码步骤。

图 7:该方法检索模型信息,创建 setup.py 和模型的 API 脚本,并将所有内容打包到一个 tar 文件中

首先调用retrieve_model_info()(图 8)。在这里,通过MlflowClient从其run_id中检索模型文件,并将其转换为字典run_info['info']['artifact_uri']

随后,模型的端点 Python predictor.py被相应调整,这样MODEL_ARTIFACT_URI将是self.model_uri_production路径(例如gs://bucket/model/model.joblib)template_predictor.py文件将被保存为self.predictor_path(例如predictor.py)。这个替换是使用 Ubuntu 的sed命令完成的

图 8:检索模型信息并创建设置和端点文件

最后,创建模型的setup.py,再次通过sed替换MODEL_VERSION,并将文件保存在本地,这样 AI 平台可以读取它。一旦模型文件可用,create_bundle通过命令python self.setup_path sdist --formats=gztar将安装sdist打包到gztar 中,并将gztar文件路径返回给create_deployment

上传捆绑方法

此时,安装gztar文件,以及模型工件(如模型二进制文件model.joblib)被上传到生产路径。如图 9 所示

图 9:上传包方法负责将包文件和模型的工件上传到生产桶。在模型工件中,我们将有模型的二进制文件(例如 joblib 扩展),它允许预测 API 读取模型

创建模型方法

接下来,一旦所有文件对 AI 平台可用,我们需要通过create_model创建一个 AI 平台模型。最初,一个 json 请求被发送到 AI 平台,指定模型名称self.settings['model_name'],我们是否想要一个终点预测onlinePredictionLogging和工作区域(例如europe-west1)。然后,列出 AI 平台中的所有模型AI_PLATFORM.projects().models().list,一个 for 循环检查给定的模型名称是否已经存在。如果模型不存在,AI_PLATFORM创建这个新模型作为AI_PLATFORM.projects().models().create().execute()

图 10:创建模型方法向 AI 平台发送请求,以检查模型是否存在,否则 AI 平台创建新模型

更新部署方法

update_deployment方法是部署的真正触发器,也是 MLflow 插件的要求。该方法调用update_source方法,如图 11 所示

向人工智能平台发送 json 请求。json 请求包含模型的名称,deploymentUri是模型工件的位置,createTimemachineTypepackageUrisgztar模型安装文件的位置,pythonVersionruntimeVersion是 AI 平台的位置,predictorClass是模型端点文件predictor.py的预测类

通过AI_PLATFORM.projects().models().versions().create创建新的端点

图 11:上传部署和更新源是 MLflow 部署插件的关键元素。update_source 向 AI 平台发送一个 json 请求,带有 model_uri_production,即模型的 artefacts 路径,以及 package uri,即模型的 gztar 路径和 endpoint 类,以便创建模型的端点

删除部署方法

现在,让我们看看附件插件方法。delete_deployment删除一个现有的模型,给出它的 GCP uri(图 12)。

模型的版本被重构,以与 AI 平台一致,并且模型的 AI 平台路径被创建parent = self.settings['project_id'] + f"/models/{self.settings['model_name']}"。然后通过AI_PLATFORM.projects().models().versions().delete(name=body)删除该模型

图 12:删除部署方法。json 请求被发送到 AI 平台,以便从 AI 平台中删除给定的模型

获取和列出部署方法

图 13:获取并列出部署方法。从 get 可以从模型的名称中检索到关于特定模型的信息,而 list deployments 返回所有已部署的模型

图 13 显示了get_deploymentlist_deployment方法。前者通过AI_PLATFORM.projects().models().version().get(name=body)从一个模特的名字中返回模特的信息。后者,通过AI_PLATFORM.projects().models().version().list(parent=parent)登记所有部署的模型,其中parent是模型在 AI-platform 中的 uri。

安装 MLflow 部署插件

图 14:安装 MLflow 部署插件 g mlflow_ai_plugin 的 setup.py 文件

现在让我们将注意力转向 MLflow 部署插件setup.py文件。此安装程序将在您的 MLflow 安装中安装插件。值得注意的是,我们需要指定插件入口点:entry_points={"mlflow.deployments":"aiplatform=mlflow_ai_plugin.DeploymentTrackingInterface"},它调用DeploymentTrackingInterface代码。aiplatform是从 MLflow 中读取的目标,因此我们可以通过类似mlflow deployments create -t aiplatform ...的命令调用我们的 MLflow AI-platform 插件

例子

最后,让我们看一些如何使用部署插件的例子。首先,我们将通过 Python 运行部署插件。首先,正如我们在 MLflow 系列的第 2 部分中看到的,让我们运行一个 MLflow 实验,在这里我们训练一个简单的神经网络模型,如图 15 所示

图 15:要在 MLflow 上训练的玩具模型

一旦模型的训练被保存在 MLflow 中,我们就可以通过 Python 脚本部署该模型,如下所示:

from mlflow.deployments import get_deploy_clienttarget_uri = 'aiplatform'
aiplatform = get_deploy_client(target_uri)
aiplatform.create_deployment()

target_uri='aiplatform'将目标传达给 MLflow。从那里我们可以使用mlflow.deployments方法来获得我们的 AI 平台插件并调用部署核心aiplatform.create_deploment()

因此,我们可以通过 bash 脚本完成部署,指定输入 env 变量:

#!/bin/bash 
export project_id='YOUR GCP PROJECT'
export production_bucket='YOUR PRODUCTION BUCKET'
export production_route='YOUR PATH WITHIN PROD BUCKET'
export run_id='2c6d5f4bc3bc4bf3b6df3ca80212a28d'
export tracking_uri='http://localhost:5000'
export model_name='YOUR MODEL NAME'
export version_name='VERSION NUMBER'
python deploy.py

通过命令行界面可以获得相同的结果。在这种情况下,我们不会调用 python 脚本,而是:

# export statements as above 
export model_uri="YOUR MODEL PRODUCTION URI"mlflow deployments create -t aiplatform --name tester -m $model-uri

最后,我们可以发送请求来获得对数据的预测,如图 16 所示。这里我们将使用googleapiclient.discovery API,用输入数据创建一个 json 请求来返回模型的预测。

图 16:通过 googleapiclient.discovery 发送预测数据

MLflow 这边到此为止!我希望你喜欢这些关于 MLflow 及其 SDK 开发和插件的文章。

如果你有任何问题或好奇,请给我发电子邮件到 stefanobosisio1@gmail.com

Python 中用于要素选择和数据平滑的样条

原文:https://towardsdatascience.com/splines-for-feature-selection-and-data-smoothing-python-ed5ad31436d5?source=collection_archive---------9-----------------------

(图片由作者提供)

描述和展示如何使用样条曲线进行降维和去除数据集中的噪声

因此,本周我结束了用 Python 编写样条函数的工作,并对信息的状态和缺乏对 Python 样条函数初学者的支持文章感到震惊。数学方面的信息已经很多了。本文的目标是分解 B 样条和光滑样条理论的应用。

如果有帮助的话,请考虑给我一个关注来支持未来的文章。

概述

  • 什么是样条?
  • 样条是如何使用的?
  • 演练 1:从零开始的 B 样条曲线,这是最灵活但数学上最实用的方法
  • 演练 2:来自 SciPy 的 splrep,用于轻松平滑 B 样条线
  • 总结用于特征提取的样条
  • 结论

什么是样条?

样条本质上是分段回归线。试图在一组非常动态的数据上拟合一条回归线会导致很多妥协。你可以裁剪你的线条,使其很好地适应某个区域,但结果往往是在其他区域过度适应。相反,我们将观察结果分解成不同的“结”,并在由这些结或分界点分割的每个线段上拟合不同的回归线。

样条是如何使用的?

通常,当我们查看样条的输入时,我们使用 1D 列表或数组。这可能是我们记录了一个病人的心率,每秒记录一次,持续 30 秒。这创建了一个具有 30 个特征的观察,或者在这 30 秒中的每一秒记录的心率。然后,我们将对这一观察值进行样条拟合,详细说明我们的结(t)和顺序(k),这将返回一条最佳拟合线,它有自己的系数,我们可以按原样利用或进行预测。

如果我们的目标只是平滑数据和去除噪声,我们可以:

  1. 在观察值上拟合/调整样条
  2. 通过输入我们希望预测心率的准确时间来提取心率(在本例中,是一个范围为 0:30 的列表/数组)
  3. 将此输出保存为我们 30 个功能中每个功能的新数据

相反,如果我们的目标是减少数据的维数并进行特征选择,我们可以:

  1. 在观察值上拟合/调整样条
  2. 提取我们的样条曲线的系数(每个结的 Beta 值),并将它们保存为我们的新特征数据(系数的数量取决于你的样条曲线的复杂性,但是举例来说,你可以将你的特征从 30 个减少到 8 个)
  3. 重复步骤 1 和 2 进行多次观察(我们已经对多名患者的心脏进行了超过 30 秒的跟踪)
  4. 在 n 个患者作为行,m 个提取的系数作为列的新数据帧上运行新的模型,例如回归或随机森林

废话说够了,让我们看看它的实际效果吧…

演练 1: B 样条从零开始,最灵活的,但数学上动手的方法

首先,这是我们的功能,在我们设置样条线的基础时,均匀分布我们的结的位置(并根据选择的度数考虑缓冲结)。

def knot_points(nKnots, x, degree):
#create the knot locations    
knots = np.linspace(x[0], x[-1], nKnots) lo = min(x[0], knots[0]) #we have to add these min and values to   conform by adding preceding and proceeding valueshi = max(x[-1], knots[-1])augmented_knots = np.append(np.append([lo]*degree, knots), [hi]*degree)return augmented_knotsloo = LeaveOneOut()

我们正在使用的数据是一系列无线电信号。每次观测都是不同的无线电信号,每个特征都是给定时间点的信号幅度。

x1 = pd.read_csv("data.csv", header=None)y = np.array(x1.values[0,:])
x = np.array(range(0,len(x1[0]))

y 和 x 都应该具有(51)的形状,等于特征/列的数量。数组 x 表示特征的数量,这将有助于指定我们的样条曲线和图形的输出。数组 y 代表一个观察值及其各自的振幅测量值。

所以让我们开始吧!

nknots = 8
degree = 3
k=degree
DOF = nknots + degree +1
augmented_t = knot_points(nknots, x, degree)

我们设置了几个将要使用的变量(DOF =自由度)。下面我们开始手动拟合样条曲线。

bs2 = BSpline(augmented_t, np.eye(DOF), degree, extrapolate=False) #setting up the basis
B = bs2(x)[:,:-2] #Creating the basis for x & getting rid of extra column of zeroes from padding for order
# Least square estimation
x_proj_matrix = B@np.linalg.inv(B.T@B)[@B](http://twitter.com/B).T
coeff = np.linalg.lstsq(B, y.T,rcond=-1)[0].T
yhat = B@coeffn = x.shape[0]
K = np.trace(x_proj_matrix)
sigma2 = (1/(n-K))*(y-yhat).T@(y-yhat) #SSE/(n-p)
width = np.diag(sigma2*x_proj_matrix)**0.5
y_neg_ci = yhat-4*width
y_pos_ci = yhat+4*width

我们将基函数与 x 和 y 拟合,以计算系数,然后我们可以用这些系数进行预测。我们还可以使用 X 投影矩阵直接计算协方差,并给出我们在下图中看到的置信区间。

(图片由作者提供)

所以我们看到一些用来创建这些样条的数学。虽然手动计算似乎不是最有趣的,但您可以开始轻松地提取其他强大的信息,如协方差,以围绕我们的趋势线创建这些置信区间。

演练 2:来自 SCIPY 的 SPLREP,用于轻松平滑 B 样条线

现在让我们来看看示例 2:使用平滑样条线遍历一个示例。这些稍微复杂一些,因为它们包含一个平衡方差和偏差的平滑超参数。我们可以使用留一个的交叉验证来找到 MSE,并在此选择最佳平滑参数。好处是这个函数会自动为我们设置结数组并返回它。

lambs = np.array([0.001,0.005, 0.1,0.25,0.5])
for i in lambs:error = []for trg, tst in loo.split(x):spl = splrep(x[trg], y[trg],s=i)pred = splev(x[tst],spl)[0]true = y[tst][0]error.append((pred - true)**2)mse = mean(error)

我们的最佳λ(平滑参数)结果是 0.005。

spl = splrep(x, y, s=0.005)
y_hat = splev(x, spl)

(图片由作者提供)

还有一个有用的方法是调用一个拟合的 splrep 模型,如下所示,这给了我们一个自生成节点数组、系数和函数的幂/阶的清晰输出。

spl

(图片由作者提供)

现在,如果我们要应用以上所有这些来平滑我们的数据,我们可以循环通过每个观察/行,在循环中的迭代的结果 x 和 y 数组上拟合样条,然后预测新的特征。

用于特征提取的概括样条

平滑的例子到此结束。我将在这里简要总结特征提取,并在下一篇文章中进行更深入的探讨。本质上,我们不是预测新的特征值,而是输出样条产生的β/系数。例如,使用上面的 B 样条曲线,我们可以得到以下输出:

bs = make_lsq_spline(x, y, augmented_t, k=degree)
bs.c

(图片由作者提供)

现在,我们可以遍历我们的数据框,在每一行上训练一个样条,并返回由上述样条中的 Betas 组成的 9 个要素,而不是 51 个要素。这可以帮助我们克服高维度和对较小数据集的过度拟合,同时仍然保持较高的解释方差。总的来说,这种样条特征选择方法甚至可以击败 PCA 主成分分析,以更少的所需特征获得更好的模型性能分数。

感谢阅读,如果你喜欢这篇文章,请给我一个关注!

幽灵——用机器学习渲染恐怖面孔

原文:https://towardsdatascience.com/spookygan-rendering-scary-faces-with-machine-learning-c390000206c6?source=collection_archive---------27-----------------------

如何使用 StyleGAN 2、VQGAN 和 CLIP 为万圣节创建幽灵般的图像和视频

来自斯布克根的样本图像,作者图像

使用文本短语引导生成性对抗网络(gan)生成新图像的趋势日益增长。当 OpenAI 推出他们的 CLIP [1]系统来比较文本和图片时,它是用于文本到图像生成的自然选择。两种流行的 GAN 是来自 Nvidia 的 style gan 2[2]和 Esser 等人的 VQGAN[3]。有开源项目来指导这两个 gan 生成图像。例如,O. Patashnik 等人的 Style CLIP[4]和凯瑟琳·克劳森的 VQGAN+CLIP 分别与 Style GAN 2 和 VQGAN 一起工作。在对这两种系统进行试验后,我发现它们各有利弊。但是同时使用这两种 GANs 会提供最好的结果。

本文将展示 CLIP 如何指导 StyleGAN 2 和 VQGAN 创建恐怖面孔,即吸血鬼和怪物。然后我将展示如何使用这些图像来制作“噩梦”视频。我称系统为间谍。

间谍概述

这里有一个图表,显示了间谍网的主要组成部分。您可以在以下章节中阅读组件的详细内容。

斯布克干成分,作者图

创建过程从使用 StyleCLIP 指导的 StyleGAN 2 生成图像开始。你可以输入一个像“老可怕的女巫长尖鼻子绿色屏幕”的文本提示,系统会在一些迭代后生成一个图像,像 40。StyleGAN 2 系统使用的模型是用 Flickr [5]中真人的脸训练出来的,所以输出看起来并不可怕。

下一步是获取输出图像,并按照 Adam 优化器使用 CLIP 的指示,使用 VQGAN 对其进行修改。您可以使用与生成初始图像相同的提示,也可以根据需要对其进行修改。只需要大约 10 次迭代就能“惊吓”图像。

最后一步是再次使用 VQGAN 和 CLIP 生成视频。系统将使用简单的文本提示“噩梦”修改可怕的图像 300 次,并将图像写入一系列文件。 FFmpeg 编解码器将图像压缩成 mp4 电影供观看。

系统详细信息

以下部分将更详细地描述每个组件。

StyleGAN 2

NVidia 对 StyleGAN 的第二次迭代写在他们的论文《分析和改善 StyleGAN 的图像质量》[2]中。他们说…

…我们改进的模型重新定义了无条件图像建模的技术水平,既包括现有的分布质量度量,也包括感知的图像质量。— T. Karras,et al .

一般来说,GANs 由发电机网络和鉴别器网络组成。在训练过程中,生成器试图创建逼真的图像,鉴别器试图辨别哪些图像是真实的,哪些图像是假的。我用于初始幽灵图像的 GAN 是使用来自 NVidia 的 Flickr-Faces-HQ 数据集的图像训练的。

生成性对抗网络中的组件,作者图片

夹子

对比语言-图像预训练(CLIP)是 OpenAI 的一对编码器,在他们的论文“从自然语言监督中学习可转移的视觉模型”[1]中进行了描述。在论文中,作者…

...证明预测哪个标题与哪个图像搭配的简单预训练任务是一种高效且可扩展的方法,可以在从互联网收集的 4 亿(图像、文本)对的数据集上从头开始学习[最先进的]图像表示。亚历克·拉德福德等人。

经过训练后,剪辑图像编码器可以将图像转换为 512 个数字的列表,称为图像嵌入,它捕获图像的“特征”。剪辑文本编码器可以将文本短语转换为类似的数字列表,该列表可以捕获文本的特征。

剪辑组件图,图片作者

如果一幅图像和一个短语描述了大致相同的东西,那么在数学上比较时,嵌入将是相似的。这两个编码器模型可用于使用 GANs 增量地从文本生成图像。

样式夹

正如您可能从名称中猜到的那样,StyleCLIP 是一个使用剪辑编码器来控制 StyleGAN 2 从文本短语生成图像的系统。在他们的论文“StyleCLIP:文本驱动的 StyleGAN 图像处理”中,作者…

…引入优化方案,利用基于剪辑的损失来修改输入潜在向量,以响应用户提供的文本提示。—或者帕塔什尼克等人。

StyleCLIP 通过迭代地将输入更改为样式 GAN 2 来控制生成的图像,以匹配 CLIP 测量的文本提示。

style clip 细节,作者配图

系统从发送到剪辑图像编码器的 StyleGAN 2 模型定义的平均图像开始。文本提示被发送到剪辑文本编码器,并且两个嵌入被比较。然后,StyleCLIP 优化器控制生成器创建图像,这些图像逐渐接近提示中描述的功能。StyleCLIP 通过在每次迭代中最小化图像嵌入和文本嵌入之间的差异来实现这一点。在这个阶段,学习率被设置为 10%,因此每次迭代中的变化可能很大。大约经过 40 次后,图像将开始看起来像提示所描述的那样。

正如在概述中提到的,StyleGAN 2 模型是使用 Flickr 上发布的真实人脸进行训练的。输出通常看起来像真人,而不是万圣节的坏人,像女巫或怪物。但是 VQGAN 可以带我们去那里。

VQGAN

大多数 gan 使用卷积神经网络(CNN)作为发生器,使用反向 CNN 作为鉴别器。然而,在论文“驯服用于高分辨率图像合成的变压器”[3]中描述了一种新的混合变压器-GAN,称为矢量量化生成对抗网络(VQGAN)。作者…

…演示如何将 CNN 的感应偏置效率与变压器的表现力相结合,使其能够建模并合成高分辨率图像。帕特里克·埃塞尔等人。

我使用的 VQGAN 模型是在来自 WikiArt.org 的数万张图片上训练出来的。下图显示了 VQGAN 的主要组件。

VQGAN 组件,作者提供的图表

VQGAN 在概念上像编解码器一样工作,因为它被训练成将图像编码到嵌入式空间中,其中变换器用于定义子图像部分的“码本”。然后,解码器创建一个与原始图像非常相似的输出图像。

在训练期间,VQGAN 鉴别器查看网格中的子图像,并评估每个部分作为改进的反馈。这使得模型非常灵活,可以创建多种类型的图像。然而,它的缺点之一是 VQGAN 需要帮助从头生成新图像。这就是 CLIP 的用武之地。

VQGAN+CLIP

类似于 StyleGAN 2 可以被引导从文本提示创建图像的方式,VQGAN 可以由 CLIP 控制来修改输入图像,以更接近短语所描述的内容。凯瑟琳·克劳森,一个在 GitHub 上被称为克劳森 kb 的开发者,为此创造了一些 Python 代码。下图显示了 VQGAN 和 CLIP 如何协同工作来迭代修改图像。

VQGAN 和剪辑,作者配图

系统从 StyleGAN 2 创建的输出图像开始,并被发送到剪辑图像编码器。类似于 StyleCLIP 的过程运行大约十次,图像看起来甚至更像提示所描述的。

创作电影

我发现我可以通过使用 VQGAN 和 CLIP 来创建图像的视频,只需使用一个通用的文本提示,如“噩梦”,并降低优化器的学习速度。我把学习率调低到 1%来创建视频,所以帧会慢慢变化。这是一张展示电影制作要素的图表。

用 VQGAN 和剪辑制作电影,图片作者

这一次,系统从 VQGAN 创建的修改图像开始,并被发送到剪辑图像编码器。提示简直是“噩梦。”该系统运行 300 帧,以每秒 30 帧的速度生成 10 秒的视频。

ffmpeg 编解码器用于生成 mp4 电影文件。下面是创建电影的命令。

ffmpeg -r 30 -i /content/steps/%4d.png -c:v libx264 witch.mp4

-r 选项指定帧速率为 30 fps。-i 选项指示图像的位置,而-c-v 选项指定 H.264 视频编解码器。

这是视频。

斯布克根女巫,作者视频

你可以看到女巫的脸是如何变成看起来像一个眼睛里有牙齿的头骨的。对我来说这看起来就像一场噩梦!😲

可疑结果

以下是来自斯布克根的更多结果。图像来自左边的 StyleGAN 2 和右边的 VQGAN。文字提示显示在标题中。请务必查看附录以查看更多视频。

幽灵女巫

古灵精怪的女巫长着尖尖的鼻子绿色的皮肤,图片作者

怪异的吸血鬼

德拉库拉肌肉发达的老年男性脸色苍白皮肤尖牙向后梳头发,图片作者

怪异的弗兰肯斯坦怪物

阴森森的弗兰肯斯坦怪物大眉毛高额头绿色皮肤,图片由作者提供

幽灵狼人

阴森森的狼人男性浓密的胡须鬓角獠牙发光的眼睛,图片由作者提供

源代码

使用 SpookyGAN 的 Google Colabs 在这里是。

感谢

我要感谢詹尼弗·林和奥利弗·斯特瑞普对这个项目的帮助。

参考

[1]a .拉德福德等人的剪辑,从自然语言监督中学习可转移的视觉模型 (2021)

[2]t . Karras、S. Laine、M. Aittala、J. Hellsten、J. Lehtinen 和 T. Aila 的 StyleGAN 2分析和改善 StyleGAN 的图像质量 (2020)

[3]p . Esser、R. Rombach 和 B. Ommer 的 VQGAN,驯服高分辨率图像合成的变压器 (2020)

[4]o . Patashnik,Z. Wu,e .,D. Cohen-Or 和 D. Lischinski 的 StyleCLIP, StyleCLIP:文本驱动的 StyleGAN 图像处理 (2021)

[5]t . Karras,J. Hellsten 编写的 FFHQ, Flickr-Faces-HQ (2018 年)

附录

下面是三个视频,来自提示“噩梦”,学习率设置为 1%的 VQGAN+剪辑优化器。

幽灵吸血鬼,作者视频

斯布克根·弗兰肯斯坦的怪物,视频作者

幽灵狼人,视频作者

为了无限制地访问 Medium 上的所有文章,成为会员,每月支付 5 美元。非会员每月只能看三个锁定的故事。

体育分析— NBA 2K 收视率预测

原文:https://towardsdatascience.com/sport-analytics-nba-2k-ratings-prediction-b7b72e2e72eb?source=collection_archive---------63-----------------------

下一个 2K22 评级是什么?是不是有些队伍被编辑高估了?哪些统计数据对评分影响最大?让我们找出这些答案。

在 Unsplash 上 NeONBRAND 拍摄的照片

作为一名对体育,尤其是篮球充满热情的数据科学家,我正在撰写一系列与体育分析相关的文章。它将从经典的回归或分类问题发展到更高级的计算机视觉应用。

NBA 2K 是自 1999 年以来开发的一系列篮球运动模拟视频游戏,每年发行一次。最新版是 2K21,2020 年 9 月 4 日发行。视频游戏现在由 2K Sports 发布。

在每一个版本中,所有 NBA 现役球员和一些传奇人物都被单独评分为 99 分。那些评级总是会引起讨论、辩论、反应…甚至是来自玩家自己。

这篇文章的目的是建立一个模型来预测每个玩家的 99 分等级,使用与玩家本身相关的特征和上一年的游戏统计数据。当我们试图预测一个数字时,这是一个回归问题。

数据源

当谈到 NBA 的数据时,大量各种各样的公开数据存在,这些统计数据被联盟本身广泛使用。数据是 NBA 文化的一部分。

从 2K14 到 2K21,NBA 2K 收视率在 https://hoopshype.com/nba2k/提供。这 8 年将是我们的项目范围。

我们观察到,自 2015 年以来,评级的分布非常相似,中位数在[74.5;76.9]和相当恒定的四分位数。

在 2K14 中,分数处于更广阔的空间,中位数更低(71.4)。这种演变可能是由于 2K 体育改变了收视率策略。这是一个需要注意的重要信息,因为它可能会引入偏差并降低模型性能。

关于 NBA 球员的统计数据,可以在https://www.basketball-reference.com/leagues上检索到。我们将只废弃 2013–2014 至 2020–2021 赛季,以便与 2K 数据周期保持一致。

网站上有许多视图,这里我们只关注总计高级选项卡。

数据集中的要素类型:

  • 年龄、职位和团队
  • 已玩和已开始的游戏数,获胜百分比,已玩时间
  • 投篮得分(3 分,2 分,罚球…)
  • 篮板,助攻,犯规,盖帽,抢断…

为了丰富我们的数据集,我们将通过组合变量来执行特征工程,特别是通过按分钟和所玩游戏以及位置和团队 one-hot 编码进行标准化。

建模

数据集在与时间相关的训练/测试/验证中被分割,以避免模型中的泄漏。请注意,我们删除了 2014 年,因为评级分布与随后几年差异太大。

许多算法可以用来执行回归:从基本的线性回归到高级的树方法。

比较不同的算法性能,我们将选择 XGBoost 并用网格搜索进行超参数调优。

验证集(对应于 2K21 评级)上,我们获得了 1.75 的 MSE 和 1.33 的 MAE,残差如下:

圆点的颜色代表玩游戏的次数。我们观察到,主要的异常值是在上一个赛季没有打足够比赛的球员(例如 2019/20 赛季的斯蒂芬·库里)。在这种情况下,统计数据可能很难代表真实的玩家价值,这种偏差被校正了 2K。

模型解释

现在我们有了一个性能合理的模型,我们可以打开黑盒并转向可解释的机器学习。

目标是分析模型背后的东西,以及哪些特征对评级预测有更大的影响。换句话说,我们可以突出显示哪些玩家属性将对他们的 2K 评级产生最大影响(无论是积极的还是消极的)。

SHAP (SHapley 附加解释)的目标是通过计算每个特征对预测的贡献来解释实例 x 的预测。

可解释的机器学习

使用 SHAP 库,我们可以绘制出前 10 个最重要的特性(左)和特性重要性与特性效果的组合(右)。

这两大特点反映了球员在球队中的重要性,他的平均上场时间和在前五场比赛中的百分比。我们还注意到,整体团队表现(赢得份额的百分比)对个人玩家评级有积极影响。

任何位置(后卫、中锋……)都有利于获得更高的收视率,并且该模型似乎也不倾向于特定的球队(出现在特征重要性图中的第一个球队是排名第 27 位的金州勇士队)。

最后,我们可以探索两个特定玩家的 SHAP 解释力图,以直观显示哪些功能产生了积极影响(=红色评分增加)和消极影响(=蓝色评分减少)。

鲁迪·戈贝尔,犹他爵士队的中锋,两届年度最佳防守球员,对他的球队有很大的影响(个人得分率,%GS,%MPPG,DWS),但是他的低得分降低了他 0.44 的评分

蒙特雷兹·哈雷尔赢得 2020 年 NBA 年度第六人。我们观察到%GS 降低了他的得分,这被他在球场上花费的时间和他的高进攻影响力所抵消(FGPM,PTSPM,PER)

结论

使用玩家统计数据,我构建了一个 XGBoost 模型,该模型可以很好地预测 2K 收视率。这说明 2K 记谱法是全局公正诚实地代表了球场上的现实。可以发现一些例外,例如当一名球员在赛季中受伤时。

操纵篮球数据非常有趣,模型解释非常有趣,可以正确理解每个预测并深入了解球员的属性。

2K 评分预测是一个非常具体的用例,但体育分析是一个无限的游乐场,有许多未充分利用的数据我将在我的数据科学之旅中继续探索。

所有代码都可以在 Github 中找到。

参考

[1] NBA 2K,维基百科 (2020)

[2] Christoph Molnar,可解释的机器学习 (2021)

“发现”ML 成本的差异

原文:https://towardsdatascience.com/spot-the-difference-in-ml-costs-358202e60266?source=collection_archive---------39-----------------------

使用 spot 实例优化云上的 ML 培训成本

使用云上的 spot 实例节省 70%以上的成本。【来源:Unsplash

任何规模的每个组织都明白,利用公共云是便利性和成本之间的权衡。虽然谷歌、亚马逊和微软等云提供商极大地降低了机器学习的门槛,但 GPU 成本仍然很高。

在机器学习社区中有一种越来越强烈的恐惧,即机器学习的真正力量仍然掌握在少数人手中。这方面的旗舰例子是 OpenAI 的大型 GPT-3 模型,包含 1750 亿个参数,350GB 的内存,据报道至少花费了 460 万美元进行训练。这种趋势看起来还将继续:关于下一代 GPT-4 大小的谣言不断流传,一些估计的参数范围在 万亿左右。即使有更有效的训练技术,这些模型仍然需要花费数百万美元来训练。

对于 ML 社区的其他人来说,现在越来越依赖他们的秘密武器:迁移学习。就在最近,优秀的 HuggingFace 库公布了一个简单的方法在单个云 GPU 上微调大规模参数模型。这给 ML 从业者带来了希望,即使他们无法从头开始训练模型,利用现代机器学习模型的巨大能力仍然是可以实现的。

对云的需求

无论是从零开始的培训还是微调,公共云提供商为大多数 ML 从业者提供了最便捷的配置和利用计算资源的途径,这一点仍然很清楚。然而,即使对于微调任务或较小的模型,这些成本也可能会快速增长并变得难以控制。例如,以下是在适用于许多机器学习任务的相对中间层配置上训练机器学习模型的大量成本的简单分类:

美国地区的所有成本,截至 1 月 28 日 14:00 CET 的 AWS 实例定价。

请记住,上述费用只是一次训练的费用。大多数机器学习项目都经历了更多的实验阶段,这些数字很快就会增加。因此,大多数手头没有巨额预算的 ML 团队通常会对他们的数据集进行采样,并在确定的时候进行大规模训练。这可能既缓慢又乏味,更不用说难以协调了。这也可能导致团队在错误的结果上趋同。如果较小的采样数据集不能代表较大的数据集,那么随着模型的发展,可能会导致令人沮丧和不同的结果。

Spot 实例:ML 实验的完美契合

如果 ML 从业者可以奢侈地开展实验,而不必如此担心成本随着时间的推移而激增,这难道不容易吗?可能会有一个解决方案,由所有主要的云提供商提供,但被机器学习社区严重利用不足:preemptable/Spot 实例。

preemptible instance 这个词很大程度上是 Google 云平台术语,而 spot instance 是 AWS/Azure 使用的。无论您如何称呼它,概念都是一样的:这些实例的成本只是普通实例的一小部分,唯一的问题是不能保证实例一直保持运行。通常,这意味着在 24 小时内实例将被提供者关闭。

这类实例是云提供商在任何给定时间最大化其所有资源利用率的一种机制。它们适用于批处理的非关键工作负载。大多数用例的大多数培训工作需要不到 24 小时来完成。即使作业在此之前被中断,它们也几乎总是可以从检查点重新启动。

成本比较:成本降低 80%

因此,机器学习训练完全符合 spot 实例的预期用途。通过使用这些实例,从业者可以大幅降低成本。我们对三大云提供商进行了粗略的分析,以展示成本优势。原始数据可以在这里找到。如果你发现有什么要补充的,请随意分享这个文档并发表评论。这是一个快照,配置与之前相同:

美国地区的所有成本,AWS 实例定价截止到 1 月 28 日 14:00 CET。

可以看出,根据配置的不同,通过使用 spot 实例,成本最多可降低 82% ,跨多个云和配置的平均成本节约约为 74% 。这相当于节省了数百美元。特别是对于爱好者、小公司或小部门尝试机器学习来说,这可能意味着部署模型与起飞前崩溃和烧毁之间的差异。

使用这种技术并不新鲜:早在 2018 年,FastAI 团队用 16 个 AWS spot 实例在 18 分钟内训练了 ImageNet。这在当时花费了 40 美元,并且是在社区中最公开地展示了 spot 实例的疯狂成本效益。

然而,考虑到越来越大的模型的趋势,以及人工智能在世界范围内越来越多的采用,我只能看到随着时间的推移,现场实例培训的需求会增加。鉴于成本的巨大差异,使用现场实例培训作为培训的主要机制几乎是显而易见的,至少在实验阶段是如此。

ZenML:编排 spot 实例训练的简单工具

如果你正在寻找 spot 实例训练的开端,请查看 ZenML ,这是一个用于可重复机器学习的开源 MLOps 框架。在 ZenML 中运行 spot pipeline 非常简单:

ZenML 不仅将你的代码压缩到实例中,还确保正确的 CUDA 驱动程序能够利用你选择的加速器。它提供实例,并在管道完成时关闭实例。更不用说框架提供的实验跟踪、版本控制和元数据管理的其他好处了。自己尝试一下:完整的代码示例可以在这里找到。

AWS 和 Azure 支持即将推出,我们希望您能对当前的设置提供反馈。如果你喜欢你所看到的,在 GitHub repo 给我们留下一颗星吧!

Spotify API 和音频功能

原文:https://towardsdatascience.com/spotify-api-audio-features-5d8bcbd780b2?source=collection_archive---------5-----------------------

实践教程

一个女孩的旅程,使她的妈妈可以跳舞的播放列表

Tableau 公共仪表盘 (只在桌面上好看,说实话) Jupyter 笔记本

这是我上一篇文章 用 Python 和 Tableau 可视化 Spotify 数据 的后续。

通过“跳舞能力”过滤的仪表板——右下方图表中不可见的是 Fergalicious,排名第九。这绝对是犯罪,不是第一名。

我在晚上出去跳舞的最开心的一次是在 2015 年的一个星期二,我去了伦敦的一个独立之夜。我经常一边听菲比·布里杰斯的音乐一边锻炼。今天早上我看着 Spotify 上的“ 悲伤女孩首发包 ”播放列表,心想“哇,这些歌都是波普音乐!”不用说,我不是邀请去做派对 DJ 的最佳人选。

今年夏天,我的任务是为我 66 岁的父亲、62 岁的母亲、13 岁的狗和 4 岁的侄子的 12 小时汽车旅行挑选音乐。标准?“我可以跟着跳舞的东西,”我妈妈说。我知道这意味着没有拉娜·德尔·雷,但除此之外我不确定。我登陆了这个 银河护卫队播放列表 ,这绝对是一个大众喜闻乐见的。但我很好奇我自己的图书馆里有哪些歌曲被认为是“适合跳舞的”

幸运的是,在从 Spotify 下载了我的库之后,我能够使用 Spotify 的 API 来提取每首歌曲的一些音频特征:舞蹈性能量乐器性流行度语速速度。我现在可以看到我的曲库中哪些歌曲在这些指标上得分最高/最低,以及我最常播放的歌曲&的艺术家在谱上的排名。

看看下面你能做什么!

这里选择的音频特性是“可跳舞性”——你是说你不能在看台上跳舞?????(图片由作者提供)。

步骤 1:请求数据

在此 向 Spotify 索取您的数据副本。耐心等几天。

步骤 2:准备流/库数据

请参考我之前的文章,用 Python 和 Tableau 可视化 Spotify 数据。请注意,唯一的变化是,当我们合并两个数据帧时,我们将通过过滤掉空值来丢弃所有不在库中的流式歌曲(见下文)。

步骤 3:创建新的 Spotify 项目

在这里 登录你的开发者账号 。在仪表板中,创建一个新项目。创建后,您可以检索您的“客户 ID”和“客户机密”我们将在步骤 4 中使用这些。

步骤 4:使用 Spotify 的 API 创建音频特征数据帧

首先,我们将使用我们的客户端 ID 和客户端密码来生成一个访问令牌,这样我们就可以从 Spotify 的 API 中提取数据。注意:此令牌必须在一小时后重新生成。我利用 这篇文章 的帮助,想出了如何做到这一点。

jovian embed 不能在这个单元上工作——很抱歉!

现在,我们将提取与库中每个 track_uri 相关联的指标,并将它们添加到字典中。我选择了上面列出的六个指标,但请查看 Spotify 的 控制台 以了解如何提取您感兴趣的指标。

我们将把这个字典转换成一个数据帧( df_features )并将 df_tableaudf_features 保存为 csv 文件,我们可以将它们加载到 tableau 中。

步骤 5:将数据加载到 Tableau 中

连接到您的 Excel 文件(MySpotifyLibraryStreams.csv)作为数据源。这应该会在左侧弹出您的 AudioFeaturesTable.csv 文件。将后一个文件拖到右边,并在两个表之间添加一个关系。确保基于 track_uri 创建关系。

(图片由作者提供)

步骤 6:编辑 Tableau 中的字段

我们将在数据表中添加一些计算字段和一个参数。

#1: 创建计算字段上场时间:

新的计算字段:“上场时间”(图片由作者提供)。

#2: 为每个功能创建聚合计算字段(即dance ability(by min)】,这样当我们可视化数据时,我们可以通过播放分钟数而不是流数来计算加权平均值(因为每次启动汽车时,您可能会听到 5 毫秒的 、A-Team )。对您感兴趣的每个功能重复:

新的计算字段:“舞蹈能力(按分)”(图片按作者)。

#3: 创建参数和计算字段音频特征 详细说明,我参考了 Tableau 中的 这篇帖子 。结果应该如下所示:

新参数:“音频特征”(图片由作者提供)。

新计算字段:“音频特征”(图片由作者提供)。

然后,您需要单击新创建的参数并选择“Show Parameter ”,这样您就可以在变量之间切换,并在同一个图表中显示结果。

步骤 7:创建可视化效果

下载仪表盘 看看我是怎么创建下面的可视化效果的。它们都是相互关联的,因此可以相互过滤,以便更好地浏览数据!

  1. 音频特征与最小值。已播放:每首歌曲的播放时间与所选音频特征的分数的散点图
  2. 平均。音频特性 : 显示每个音频特性的平均值(按播放分钟数加权);请注意,筛选仪表板时,将重新计算此平均值
  3. 播放次数最多的艺术家:气泡图,圆圈的大小表示播放的分钟数,而颜色表示我听的该艺术家的歌曲在所选音频功能上的得分(蓝色=低,红色=高)
  4. 艺术家:根据我听的该艺术家的歌曲在所选音频功能上的评分从高到低排序;颜色代表播放的分钟数(蓝色=低,红色=高)
  5. 歌曲:根据歌曲在所选音频功能上的评分高低排序;颜色代表播放的分钟数(蓝色=低,红色=高)

请注意,对于每个标签,我都按照 sum(播放分钟数)≥ 2 进行了过滤,以剔除今年我从未完整听过的歌曲(也就是说,当它们出现时,我只是跳过了它们)。

按“受欢迎程度”过滤的仪表板—贾斯汀比伯、坎耶等。是我听的(目前)最受欢迎的艺术家。

最后

原来我不是特别喜欢很适合跳舞或者很不适合跳舞的歌。下面图表上的 R 低得可怕,所以我想我只是#不可预测!也许这就是为什么我很难理解当我妈妈问她要一首可以跳舞的歌时,她是什么意思…因为我可以跳任何歌(见:贝尼托·斯金纳的不可思议系列 “你不能随着民间传说跳舞”“你不能永远地跳舞……”)。

散点图:舞蹈能力与上场时间(图片由作者提供)。

相反,我想我只能依靠 Spotify 给我的排名了。我要让我妈妈进监狱!另一方面,我了解到,显然 Bleachers 不是一个很适合跳舞的乐队,尽管我全心全意地不同意,尽管我承认 Phoebe Bridgers 很难跳舞。

根据可跳舞性从高到低排序的歌曲;瑞金娜·斯派克特的《忠诚》有点令人惊讶,但我是为它而来的(图片由作者提供)。

同样的分析也可以在仪表盘中包含的所有其他音频功能上进行,如能量乐器流行度语速速度。这就是我发现这个项目最有用的地方:学习如何在 Tableau 中创建一个参数,允许我在相同的图表中切换不同的变量。例如,下面的散点图与上面的跳舞度散点图相同,只是针对语速进行了过滤。你可以看到,我并不真的听那些在语音上得分很高的歌曲,我认为这与某些说唱风格有关。如果是这样的话,散点图是相当准确的,因为我不太听说唱。将这些散点图与我的不同偏好流派的朋友的散点图进行比较会很有趣!

散点图:语速与播放时间(图片由作者提供)。

这是一种探索我音乐品味的有趣方式。我希望你也能使用这个指南来了解更多关于你的东西!

Spotify 艺术家推荐

原文:https://towardsdatascience.com/spotify-artist-recommender-7950af1fe20a?source=collection_archive---------27-----------------------

使用余弦相似度轻松构建智能推荐系统

图片来自 Pixabay

应用程序和服务中的推荐系统不再是一个“好东西”。用户现在已经习惯于将此功能作为标准。音频流、视频流、广告、搜索引擎、博客等。仅举几个例子。

说到推荐系统,有一些常见的技术。我最喜欢的技巧是“余弦相似度”。很容易理解,速度也很快。即使没有数据科学背景,这个概念也不应该太难理解。

基本概念如下。

作者图片

画出你想要比较的项目的数字表示(上面的 A 和 B ),然后计算点之间的余弦距离。背后的理论是距离越小,它们越相似。

让我们看一个例子…

步骤 1:确定项目范围

创建一个推荐系统,在 Spotify 上找到相似的艺术家。

第二步:收集数据

我在 Kaggle 上下载了数据集“Spotify Dataset 1922–2021,~600k 曲目”。我将使用的文件名为“ data_by_artist_o.csv ”。

import pandas as pd
import numpy as npfrom sklearn.metrics.pairwise import cosine_similarity
from sklearn.preprocessing import StandardScalerdf_artists = pd.read_csv('./data_by_artist_o.csv', encoding='utf-8')df_artists.shape

这个数据集有 28680 首歌曲,有 11 个特征/列。

步骤 3:清理数据

数据实际上已经处于很好的状态,没有任何空值。

df_artists.isnull().sum()

df_artists.info()

df_artists.describe()

有几个特征是非数字的或者没有真正增加多少价值,所以我们将删除它们。

df_artists.drop(columns={'genres', 'key', 'mode', 'count'}, inplace=True)df_artists.head()

我们现在面临的问题是,所有这些描述性特征的比例都不同。我们需要对数据进行缩放,以便它们都具有可比性。

ss = StandardScaler()df_artists_scaled = ss.fit_transform(df_artists)
df_artists_scaled = pd.DataFrame(data=df_artists_scaled, index=df_artists.index, columns=df_artists.columns)df_artists_scaled.head()

正如您现在看到的,这些特性都在-1 到 1 的范围内。

步骤 4:探索性数据分析(EDA)

让我们看看“艾德·希兰”作为艺术家是如何工作的。

artist_array = np.array(df_artists_scaled.T['Ed Sheeran']).reshape(1,-1)
artist_dataset_array = df_artists_scaled.drop(index='Ed Sheeran').values
cosine_similarity(artist_array, artist_dataset_array)

df_Ed_Sheeran = df_artists.drop(index='Ed Sheeran').copy()
df_Ed_Sheeran['cosim_score'] = cosine_similarity(artist_array, artist_dataset_array).reshape(-1,1)df_Ed_Sheeran

看看最后新创建的“ cosim_score ”。这向我们展示了该艺术家与其他艺术家的相似性,使用了“余弦相似度”。

我们现在有了为自己创建一个方便的函数的构件。

def get_cosim_artist_df(artist_name, df, n):ss = StandardScaler() df_scaled = ss.fit_transform(df)df = pd.DataFrame(data=df_scaled, index=df.index)artist_array = np.array(df.T[artist_name]).reshape(1,-1)dataset_array = df.drop(index=artist_name).valuescosim_scores = cosine_similarity(artist_array, dataset_array).flatten()artist_names_array = df.drop(index=artist_name).index.valuesdf_result = pd.DataFrame(data = {'artist'               : artist_names_array,'cosim_' + artist_name : cosim_scores,})df_result = df_result.sort_values(by='cosim_' + artist_name, ascending=False).head(n)    return df_result.reset_index(drop=True)

让我们看看它的实际效果…

get_cosim_artist_df('Ed Sheeran', df_artists, 20)

根据我们新创建的函数,与“艾德·希兰”相似的前 20 位艺人…

我不得不承认我不认识名单上的大多数艺术家。我打开 Spotify,听了余弦相似度超过 93%的前三名,我认为它做得相当好。想知道你在评论中的想法会很有趣。

结论

这种技术不仅限于音频和视频流,这可能是第一个想到的用例。只要你能把你的数据组织成一个数据透视表,这就可以了。我再给你举一个例子供你思考。假设您有一个网站,您想向您的用户推荐页面或文档。您可以创建一个数据透视表,将页面名称或文档标题作为索引(行),还可以创建一个“标签”列表作为列。如果标签描述了页面或文档,你可以将它标记为 0 或 1,或者如果一些标签比其他标签更重要,甚至可以标记为权重。然后你运行它通过完全相同的过程,因为它会像魅力一样工作。

如果你喜欢这篇文章,我写了一些其他的文章,我想你可能会感兴趣。我正在通过例子解释各种机器学习概念。

https://levelup.gitconnected.com/predicting-titanic-survivors-using-ml-8570ef4e89e8 https://levelup.gitconnected.com/predicting-house-sale-prices-using-ml-30b829fd9556 https://levelup.gitconnected.com/categorising-using-machine-learning-5601baf9a05e

我希望你觉得这篇文章有趣并且有用。如果您想随时了解情况,请不要忘记关注我,注册我的电子邮件通知。

迈克尔·惠特尔

  • 如果你喜欢这个,请 跟我上媒
  • 更多有趣的文章,请 关注我的刊物
  • 有兴趣合作吗? 我们上领英 连线吧
  • 支持我和其他媒体作者 在此报名
  • 请别忘了为文章鼓掌:)←谢谢!

Spotify 案例研究:制作热门歌曲有秘诀吗?

原文:https://towardsdatascience.com/spotify-case-study-is-there-a-secret-to-producing-hit-songs-aab8c2dc64c1?source=collection_archive---------15-----------------------

照片由 Yomex Owo 在 Unsplash 上拍摄

我可以用我的车库之歌让自己成为明星吗?

几个月后,我参加了一个数据科学项目,并为
的职业转型做好了准备,我发现要想在这个领域取得真正的成功
,应该将获得的知识应用到项目中。这些
知识可以回答任何关于分析数据的问题。

然而,人们可能会遇到几个新问题:这个问题是否符合我目前的技能组合,或者它是否过于雄心勃勃?一个人应该从哪里开始?这个问题有意义吗?

在这里,我提出了一个对初学者更友好的方法:回答一个已经回答过的问题,但加入自己的个人风格。

我将带你了解我在 Spotify 音乐数据集上的第一个探索性数据分析(EDA)项目。这将有助于提供更多的背景。

你可以在这里找到这个数据集(tracks . CSV)。

第 1 部分:数据操作和清理

首先,我们必须导入这个项目所需的库:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import datetime
import calendar

接下来,让我们阅读我们的数据框架,看看第一个样本:

df = pd.read_csv('../Spotify Tracks.csv')df.sample(10)

现在,我们已经看到了数据框架中不同类型变量的列,让我们看看它的数据类型,看看是否需要一些争论:

df.info()

有趣的是,似乎发布日期列被设置为一个字符串。我们最好把它定为一个日期,以确保更顺利的分析。让我们也为音轨的发布年份和月份创建一个新的列。

为此,我们将使用PD . to _ datetime()函数将该列设置为日期,并使用dt提取其年月。我们还将使用一个 lambda 函数来为日历库中的每个月指定相应的名称:

*df['release_date'] = pd.to_datetime(df['release_date'])
df['release_year'] = df['release_date'].dt.year
df['release_month'] = df['release_date'].dt.month
df['release_month'] = df['release_month'].apply(lambda x : calendar.month_name[x])*

让我们再看一看:

*df.info()*

这个看起来没错!但是看起来 id 和 id_artists 对我们的分析没什么用。所以我们要用。下降方法:

***columns_to_drop = ['id','id_artists']
df.drop(columns = columns_to_drop, inplace = True)df.sample(5)***

好了,现在我们有了正确的列,在开始有趣的东西之前,让我们做最后一件事:检查重复项。

***df.duplicated().value_counts()***

消除重复:

***df.drop_duplicates(inplace = True)df.duplicated().value_counts()***

非常有效。现在让我们进入正题。

第二部分:数据探索和分析

好了,现在我们应该阐明驱动我们分析的主要问题:“有人能根据音轨属性数据发布一首热门歌曲吗?”****

我们将为我们的图表设置样式和调色板(如果我们正在处理 Spotify 数据,它必须是绿色的,对吗?)

***sns.set_palette('BuGn_r')
sns.set_style('darkgrid')***

让我们来看看流行歌曲的分布情况:

***plt.figure(figsize = (14,8))sns.histplot(df['popularity'], kde = True)
plt.title('Distribution of Tracks by Popularity')
plt.show()***

有趣的是,超过 45,000 首歌曲在流行的墓地里。并且大多数歌曲分布在大约 1 到 40 点的流行度之间。

看起来音乐市场现在竞争很激烈,是吧?但是一直都是这样吗?让我们来看看:

***plt.figure(figsize = (20, 8))
sns.countplot(data = df, x = 'release_year', color = '#1DB954')
plt.title('Distribution of Tracks Produced Over Time')
plt.xticks(rotation = 90)
plt.show()***

看起来它的竞争一年比一年激烈,2020 年生产了近 14 万首歌曲,这一年许多人在家里有很多空闲时间。该数据集包含截至 2021 年 4 月的歌曲数据,因此我们必须等待,看看今年会产生多少首歌曲。

现在,让我们看看数据集中的一些总体指标。我们将使用。 describe() 方法,但是添加了一些附加参数来沿着几个百分点分布数据:

**df.describe([.01,.1,.2,.3,.4,.5,.6,.7,.8,.9,.99])**

这张表中有几个发现,但真正引起我注意的是,与之前的(19 点)相比,受欢迎程度变量中的 90%和 99%之间有一个很大的飞跃。因此,似乎有几首热门歌曲的人气接近 100 分。

这意味着 Spotify 中有一组精选的歌曲非常受欢迎。通过在我们的歌曲中加入正确的和弦和节奏,有可能达到这个目标吗?让我们绘制一个相关图表来找出答案:

**#First we make a list with the track attributes we want to comparetrack_attributes = ["popularity","acousticness","danceability", "energy", "duration_ms", "instrumentalness", "valence", "tempo", "liveness", "loudness", "speechiness"] #Then we plot
plt.figure(figsize = (10,8), dpi = 80)sns.heatmap(df[track_attributes].corr(),vmin=-1, vmax=1, annot=True, cmap = 'BuGn_r' )
plt.xticks(rotation = 45)
plt.show()**

我们可以在这张热图中看到,流行度和赛道属性之间没有明显的相关性。尽管如此,深入研究显示出正相关的三个属性仍然是值得的:可跳舞性能量响度。****

让我们试一试:

**corr_vars = ['danceability', 'energy', 'loudness']list(enumerate(corr_vars))**

因此,在列出了我们想要检查的变量并将其转换成一个生成器对象之后,我们从每个图表中随机抽取 500 个出现次数的样本来绘制这三个属性:

**plt.figure(figsize = (20, 15))for i **in** enumerate(corr_vars):plt.subplot(1,3,i[0]+1)sns.regplot(data = df.sample(500), y = 'popularity', x = i[1])
plt.show()**

看起来这证实了我们最初在相关性热图中看到的情况,但揭示了一些对我们的分析非常有趣的事情:大多数高受欢迎度异常值都在三个属性的最高范围内,特别是响度。这可能是解决我们主要问题的一块巨大的垫脚石。

最好将我们的数据分成子集,以获得最流行的歌曲,这样我们就可以看到这些属性的存在程度:

**popular_songs = df[df['popularity'] >= 80]print(popular_songs.shape)popular_songs.sample(10)**

这里我们有 954 首歌曲,让我们通过 mean 绘制它们的属性来看看它们有什么共同点:

**#First we list the attributes we want to see reflected in the plot
labels = [ "valence", "danceability", "energy", "acousticness","instrumentalness", "liveness","speechiness"]#Then we plot those attributes by meanfig = px.line_polar(popular_songs, theta = labels, r = popular_songs[labels].mean(), line_close = True)fig.show()**

****

这是个好消息,因为我们又发现了一个可能有助于我们创作热门歌曲的属性:化合价。

另一方面,响度是一个非常强的竞争者,因为它的值范围从-60 到 0,平均值为-6 表明最流行的歌曲往往很大声。

现在,让我们来看看随着时间的推移,这些是如何成为最受欢迎的歌曲的一部分的:

**#First we make a list of our attributes of interest
audio_attributes = ["danceability","energy","valence"]#Now we plot our charts
plt.figure(figsize = (20, 8))
sns.set_palette('Accent')
plt.subplot(2,1,1)
for attribute in audio_attributes:x = popular_songs.groupby("release_year")[attribute].mean()sns.lineplot(x=x.index,y=x,label=attribute)plt.title('Audio Attributes Over Time for Popular Songs')plt.subplot(2,1,2)
sns.lineplot(x = popular_songs.groupby('release_year')['loudness'].mean().index, y = popular_songs.groupby('release_year')['loudness'].mean())plt.ylabel('Range')
plt.xlabel('Year')**

看起来垃圾音乐真的让价在 90 年代下降了,是吗?

似乎在过去的 50 年里,流行歌曲总是充满活力,声音洪亮,也很欢快。这看起来像是人们音乐品味的一个明确指标。

所以,任何创作出具有这些特质的歌曲的人肯定会成为明星,对吗?

让我们来看看目前最受欢迎的 25 位歌手的歌曲,并与随机样本进行比较:

**popular_songs['artists'].value_counts().nlargest(25)popular_songs['artists'].value_counts().sample(30)**

看起来贾斯汀比伯和比莉·艾莉丝现在很受欢迎,有 11 首热门歌曲!但这并不令人惊讶,因为前 25 名名单中的所有艺术家都非常知名,样本名单中的许多艺术家也是如此。

第三部分:结论

制作一首热门歌曲不一定取决于你的歌曲有多快乐、有活力或响亮,但更有可能的是,它与你目前作为一名艺术家的受欢迎程度有关。

尽管如此,为了增加推出流行歌曲的几率,在其中添加流行属性可能是好的,就像我们在分析中看到的那样。这对新音乐人来说也是一个很好的建议,让他们真正投入时间和精力来营销他们的内容。

感谢阅读!

在 LinkedIn 上找到我

Spotify 流派分类算法

原文:https://towardsdatascience.com/spotify-genre-classification-algorithm-88051db23d42?source=collection_archive---------10-----------------------

监督机器学习— SVM、随机森林、逻辑回归

本文假设对机器学习算法和数据科学技术有基本的了解。

文章大纲:

  • 监督机器学习
  • 分类—多类
  • 数据集-初步分析和特征选择
  • 算法选择— SVM、逻辑回归、随机森林
  • 模型性能—准确性得分
  • 改进—超参数调整和集成学习
  • 结论—更多数据!

亚历山大·沙托夫在 Unsplash 上拍摄的照片

什么是有监督的机器学习?

正如所有技术都有术语一样,监督学习是一个总括术语,用来描述机器学习的一个领域(在实践中使用最频繁),其中使用的数据是标记为的。监督学习算法的目标是利用数据集来产生一个模型,该模型以特征向量(x)作为输入,以变量(Y)作为输出。使用一种算法来学习从输入到输出的映射函数,然后使用新的未知输入数据来预测该数据的输出变量。

什么是分类?

分类算法将已标记示例的数据集作为输入来产生模型,该模型可以获取未标记的新数据并自动将标签分配给未标记的示例。

如果分类问题有一组两个标签(例如“垃圾邮件”或“非垃圾邮件”),那么它就是一个二元分类问题。当集合中的标签数量为三个或更多时,多类分类是一个问题。我们正在寻找的问题是一个多类,因为在这个集合中有许多类型。

给我看看数据

被检查的数据集是歌曲信息的集合。它可以在我的 python 代码旁边的 Kaggle 和 Github 上获得。

数据已经被分成标记的训练数据和未标记的测试数据。

按作者分类的图像(标记为训练数据)

作者提供的图片(未标记的测试数据)

  • Id —任意唯一的轨道标识符
  • 标题—音轨标题
  • 艺术家——歌手或乐队
  • 年份—发布(或重新发布)的年份
  • bpm —每分钟节拍数(节奏)
  • nrgy —能量:数值越高,能量越大
  • dnce — danceability:值越高,越容易随着这首歌跳舞
  • dB —响度(dB):值越高,歌曲的音量越大
  • live-liveness:该值越高,歌曲越有可能是现场录制的
  • val-valence:值越高,歌曲的情绪越积极
  • dur —持续时间:歌曲的长度
  • acous-acoustic:值越高,歌曲的声音越大
  • spch-speechiness:值越高,歌曲包含的口语单词越多
  • 流行度:值越高,歌曲越流行
  • 热门流派—曲目的流派(以及此问题的目标变量)

在 top_genre 列的训练集中识别出 15 个空值。missingo 库为缺失值提供了很好的可视化效果,使得识别具有空值的列变得很容易。15 个空值被删除。

import missingno as msno
msno.bar(class_train, color="dodgerblue", sort="ascending", figsize=(10,5), fontsize=12)
class_train["top genre"].isnull().value_counts()
# dropping NULL values
class_train = class_train.dropna(axis=0)

作者图片

在创建任何模型之前,最好检查多重共线性,多重共线性是数据集中独立要素之间的相关性。检查这一点最简单的方法是使用关联热图。显然不存在多重共线性。

# Plot linear correlation matrix
fig, ax = plt.subplots(figsize=(15,10))
sns.heatmap(class_train.corr(), annot=True, cmap='YlGnBu', vmin=-1, vmax=1, center=0, ax=ax)
plt.title('LINEAR CORRELATION MATRIX - CLASS_TRAIN')
plt.show()

作者图片

pop(流行度)列可用于创建另一个列,表示一首歌曲是否受欢迎(1)或不受欢迎(2)。直方图可用于显示数据集中每个要素的“相似”分布。这是特征工程的一个例子。

conditions = [(class_train['pop'] >= 55),(class_train['pop'] < 55) ]
values = [1, 2]
class_train['like'] = np.select(conditions, values)# for all features
pos_bpm = class_train[class_train['like'] == 1]['bpm']
neg_bpm = class_train[class_train['like'] == 2]['bpm']
pos_nrgy = class_train[class_train['like'] == 1]['nrgy']
neg_nrgy = class_train[class_train['like'] == 2]['nrgy']
pos_db = class_train[class_train['like'] == 1]['dB']
neg_db = class_train[class_train['like'] == 2]['dB']
pos_live = class_train[class_train['like'] == 1]['live']
neg_live = class_train[class_train['like'] == 2]['live']
pos_dur = class_train[class_train['like'] == 1]['dur']
neg_dur = class_train[class_train['like'] == 2]['dur']
pos_acous = class_train[class_train['like'] == 1]['acous']
neg_acous = class_train[class_train['like'] == 2]['acous']
pos_spch = class_train[class_train['like'] == 1]['spch']
neg_spch = class_train[class_train['like'] == 2]['spch']
pos_val = class_train[class_train['like'] == 1]['val']
neg_val = class_train[class_train['like'] == 2]['val']
pos_dnce = class_train[class_train['like'] == 1]['dnce']
neg_dnce = class_train[class_train['like'] == 2]['dnce']fig2 = plt.figure(figsize=(20,20))
#dnce
ax3 = fig2.add_subplot(331)
ax3.set_xlabel('Danceability')
ax3.set_ylabel('Count')
ax3.set_title('Song Danceability Like Distribution')pos_dnce.hist(alpha=0.5, bins=30)
ax4 = fig2.add_subplot(331)neg_dnce.hist(alpha=0.5, bins=30)
plt.legend(['Like', 'Dislike'])#duration
ax5 = fig2.add_subplot(332)
ax5.set_xlabel('Duration')
ax5.set_ylabel('Count')
ax5.set_title('Song Duration Like Distribution')pos_dur.hist(alpha=0.5, bins=30)
ax6 = fig2.add_subplot(332)neg_dur.hist(alpha=0.5, bins=30)
plt.legend(['Like', 'Dislike'])# loudness (dB)
ax7 = fig2.add_subplot(333)
ax7.set_xlabel('Loudness -dB')
ax7.set_ylabel('Count')
ax7.set_title('Song Loudness Like Distribution')
plt.legend(['Like', 'Dislike'])pos_db.hist(alpha=0.5, bins=30)
ax8 = fig2.add_subplot(333)neg_db.hist(alpha=0.5, bins=30)
plt.legend(['Like', 'Dislike'])# energy
ax9 = fig2.add_subplot(334)
ax9.set_xlabel('Energy')
ax9.set_ylabel('Count')
ax9.set_title('Song Energy Like Distribution')pos_nrgy.hist(alpha=0.5, bins=30)
ax9 = fig2.add_subplot(334)neg_nrgy.hist(alpha=0.5, bins=30)
plt.legend(['Like', 'Dislike'])# live
ax10 = fig2.add_subplot(335)
ax10.set_xlabel('Liveness')
ax10.set_ylabel('Count')
ax10.set_title('Liveness - Like Distribution')pos_live.hist(alpha=0.5, bins=30)
ax11 = fig2.add_subplot(335)neg_live.hist(alpha=0.5, bins=30)
plt.legend(['Like', 'Dislike'])# val
ax12 = fig2.add_subplot(336)
ax12.set_xlabel('Valence')
ax12.set_ylabel('Count')
ax12.set_title('Valence (Mood?) - Like Distribution')pos_val.hist(alpha=0.5, bins=30)
ax13 = fig2.add_subplot(336)neg_val.hist(alpha=0.5, bins=30)
plt.legend(['Like', 'Dislike'])# acous
ax14 = fig2.add_subplot(337)
ax14.set_xlabel('Acousticness')
ax14.set_ylabel('Count')
ax14.set_title('Acousticness - Like Distribution')pos_acous.hist(alpha=0.5, bins=30)
ax15 = fig2.add_subplot(337)neg_acous.hist(alpha=0.5, bins=30)
plt.legend(['Like', 'Dislike'])# speech
ax16 = fig2.add_subplot(338)
ax16.set_xlabel('Speech')
ax16.set_ylabel('Count')
ax16.set_title('Speech - Like Distribution')pos_spch.hist(alpha=0.5, bins=30)
ax17 = fig2.add_subplot(338)neg_spch.hist(alpha=0.5, bins=30)
plt.legend(['Like', 'Dislike'])# bpm
ax18 = fig2.add_subplot(339)
ax18.set_xlabel('Beats Per Minute')
ax18.set_ylabel('Count')
ax18.set_title('Song BPM - Like Distribution')pos_bpm.hist(alpha=0.5, bins=30)
ax19 = fig2.add_subplot(339)neg_bpm.hist(alpha=0.5, bins=30)
plt.legend(['Like', 'Dislike'])

作者图片

然后调查歌曲在训练集中的流派分布。在训练集中,有 86 个独特的流派名称,这实在是太多了。经过进一步调查,这些流派名称中有许多只是地理上的差异,例如,“英国摇滚”和“专辑摇滚”——它们可以归入“摇滚”类别。因此,流派被进一步概括,将流派的数量从 86 个减少到 20 个。

class_train = class_train.replace({'top genre': {"album rock": "rock", "glam rock": "rock", "dance rock":"rock", "art rock":"rock","soft rock":"rock", "country rock": "rock", "classic rock":"rock", "blues rock":"rock", "celtic rock":"rock","australian rock":"rock", "german alternative rock":"rock", "alternative rock":"rock", "dance pop":"pop","brill building pop": "pop", "europop": "pop", "barbadian pop": "pop", "classic uk pop":"pop", "new wave pop":"pop", "canadian pop":"pop", "art pop":"pop", "belgian pop": "pop", "britpop": "pop", "italian pop":"pop", "classic danish pop": "pop", "bow pop": "pop", "baroque pop": "pop", "bubblegum pop": "pop","afropop":"pop", "hip pop":"pop", "atl hip hop": "hip hop", "east coast hip hop": "hip hop", "detroit hip hop":"hip hop", "bronx hip hop": "hip hop", "bubblegum dance": "dance", "eurodance":"dance", "belgian dance":"dance", "german dance": "dance","classic soul": "soul", "british soul": "soul", "chicago soul": "soul", "british folk": "folk", "american folk revival":"folk","drone folk":"folk","canadian folk":"folk", "deep adult standards":"adult standards", "glam metal": "metal", "alternative metal": "metal","acoustic blues":"blues", "british blues":"blues", "louisiana blues":"blues", "g funk":"funk", "brit funk":"funk","afrobeat":"dance", "british invasion":"rock", "doo-wop":"blues", "boy band":"pop", "merseybeat":"rock-and-roll", "blue":"blues","bebop":"jazz", "avant-garde jazz":"jazz", "boogaloo": "latin", "big room": "trance", "bubble trance":"trance", "glam punk":"rock","australian talent show":"pop", "mellow gold":"rock", "hi-nrg": "dance", "neo mellow": "pop", "yodeling":"folk", "classic girl group":"pop","british dance band":"jazz", "deep house":"dance", "uk garage": "dance", "chicago rap":"hip hop"}})

下面的饼状图显示了前 10 个流派,只是为了保持可读性。很明显,训练数据集中的大多数歌曲属于摇滚和流行音乐类别——这种情况的影响将在结论中讨论。

# Find percent of each genre
df_genre = class_train['top genre'].value_counts()[:10].sort_values(ascending=False) / len(class_train)
sizes = df_genre.values.tolist()
labels = df_genre.index.values.tolist()# Pie chart for genre
fig1, ax1 = plt.subplots(figsize=(10,10))
ax1.pie(sizes, labels=labels, autopct='%1.1f%%', shadow=False, textprops={'fontsize': 14})
ax1.axis('equal')
plt.title("Most Popular Genres\n" + "(limited to top 10 genres)", bbox={'facecolor':'0.8', 'pad':5})
plt.show()

作者图片

然后观察特征的分布。这是因为我们希望每个特征的直方图显示正态分布。

import matplotlib.pyplot as plt
%matplotlib inline
class_train.hist(bins=20, figsize=(15,15))
plt.show()

作者图片

上面的直方图显示一些变量是偏斜的(acous,live,spch),这种偏斜可能是异常值的原因。因此,可以通过移除潜在的异常值来纠正这种偏差。可以使用箱线图和 z 值来识别异常值。z 值大于 3 的数据点将被移除。移除异常值以减少噪声可以提高模型的性能并防止过度拟合。

import seaborn as sns
# acoustic
sns.boxplot(x=class_train['acous']) # no visible outliers
# dur
sns.boxplot(x=class_train['dur']) # OUTLIERS !!
# live
sns.boxplot(x=class_train['live']) #OUTLIERS !!
# spch
sns.boxplot(x=class_train['spch']) #OUTLIERS !!class_train = class_train[np.abs(class_train.dur-class_train.dur.mean()) <= (3*class_train.dur.std())]
# keep only the ones that are within +3 to -3 standard deviations in the column 'live'.class_train = class_train[np.abs(class_train.live-class_train.live.mean()) <= (3*class_train.live.std())]
# keep only the ones that are within +3 to -3 standard deviations in the column 'live'.class_train= df[np.abs(class_train.spch-class_train.spch.mean()) <= (3*class_train.spch.std())]
# keep only the ones that are within +3 to -3 standard deviations in the column 'spch'.

将在模型创建过程中使用的特征是第 3 列到第 13 列。被删除的功能有标题、艺术家和 Id。为模型训练选择的特征是:“年份”、“bpm”、“nrgy”、“dnce”、“dB”、“spch”、“pop”、“live”、“acous”。

然后使用 train_test_split 函数对训练集进行分离,以进一步将数据划分为 75% / 25%的分割。通过进一步分割训练数据集,我们创建了一个验证集。

y = class_train.values[:,14]
X = class_train.values[:,3:13]from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)

算法选择+模型性能+改进

这三个部分紧密交织在一起。因为所选择的算法在很大程度上取决于它与其他算法相比的表现,反过来,改进在很大程度上是一个迭代过程。

支持向量机(SVM)算法是分析的好选择,因为训练集具有少量实例和大量特征。这是合适的,因为 SVM 算法可以处理高偏差/低方差。 [OneVersusRest](https://machinelearningmastery.com/one-vs-rest-and-one-vs-one-for-multi-class-classification/#:~:text=One-vs-rest (OvR for short%2C also referred,into multiple binary classification problems.) 是一种启发式方法,涉及将训练数据拆分为多个二元分类问题。如前所述,这个问题被认为是多重标签。然而,SVM 算法要求正标签的数值为+1,负标签的数值为-1。因此,使用 OneVersusRest 技术,它对每个二元分类问题训练二元分类器,并根据最有把握的模型进行预测。

from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
from sklearn.multiclass import OneVsRestClassifierstd_scaler = StandardScaler()
X_scaled_train = std_scaler.fit_transform(X_train)
X_scaled_train **=** std_scaler.fit_transform(X_train)X_scaled_train **=** std_scaler.fit_transform(X_train)svm_clf **=** OneVsRestClassifier(LinearSVC(C**=**1, loss **=** "hinge", random_state **=** 1))svm_clf.fit(X_scaled_train, y_train)

在构建我们的 SVM 之前进行缩放,因为它们对要素比例很敏感。这是为了防止模型中最宽的可能街道过于靠近决策边界。

超参数不是由学习算法优化的,必须由我们这些数据分析师来完成。网格搜索是最简单的超参数调整方法,可以在 scikit-learn Python 机器学习库中找到。在使用 C=1 的超参数的模型的第一次运行中,精确度非常低,为 26%。使用网格搜索将 C(算法的超参数之一)减少到 0.01。这将防止过度拟合。

SVCpipe = Pipeline([('scale', StandardScaler()),('SVC',LinearSVC())])# Gridsearch to determine the value of C
param_grid = {'SVC__C':np.arange(0.01,100,10)}
linearSVC = GridSearchCV(SVCpipe,param_grid,cv=5,return_train_score=True)
linearSVC.fit(X_train,y_train)
print(linearSVC.best_params_)svm_clf = OneVsRestClassifier(LinearSVC(C=0.01, loss = "hinge", random_state=1))preds = svm_clf.predict(X_scaled_train)
from sklearn.metrics import classification_report
print(classification_report(y_train,preds))

作者图片

最终达到的模型精度为 46%。其中准确度是正确分类的例子的数量除以分类的例子的总数。

逻辑回归算法是一种分类算法,而不是回归算法,可用于二元和多类问题。获得的准确度分数为 50%。

from sklearn.linear_model import LogisticRegression
ovr_clf = OneVsRestClassifier(LogisticRegression(max_iter=1000, random_state=1))
ovr_clf.fit(X_train, y_train)
y_test_pred = ovr_clf.predict(X_test)from sklearn.metrics import accuracy_score
confusion_matrix(y_test, y_test_pred)
print(accuracy_score(y_test, y_test_pred))

随机森林算法使用一种改进的树学习算法来检查“学习”过程中每个分裂处的随机特征子集。这样就避免了树的相关性。随机森林减少了最终模型的方差,从而减少了过度拟合。Random Forest 取得了 46%的准确率。

from sklearn.ensemble import RandomForestClassifier
rnd_clf = RandomForestClassifier(n_estimators=25, max_leaf_nodes=16, n_jobs=-1, random_state=1)
rnd_clf.fit(X_train, y_train)
ypred = rnd_clf.predict(X_test)
print(accuracy_score(y_test, ypred))

选择最好的 3 个模型用于集成学习:SVM、逻辑回归和随机森林。使用硬投票分类器,因为它实现了高精度。

from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVClog_clf = OneVsRestClassifier(LogisticRegression(max_iter=1000, penalty = "l2", C=1, random_state=1))
rnd_clf = RandomForestClassifier(random_state=1)
svm_clf = OneVsRestClassifier(LinearSVC(C=0.01, loss = "hinge", random_state = 1))
voting_clf = VotingClassifier(estimators=[('lr', log_clf), ('rf', rnd_clf), ('svc', svm_clf)],voting='hard')
voting_clf.fit(X_train, y_train)from sklearn.metrics import accuracy_score
for clf in (log_clf, rnd_clf, svm_clf, voting_clf):clf.fit(X_train, y_train)ypred = clf.predict(X_test)print(clf.__class__.__name__, accuracy_score(y_test, ypred))

逻辑回归和投票分类器得分相同。选择投票分类器是因为它更稳健,因为它减少了预测和模型性能的传播。

结论

当提交给 Kaggle 竞赛时…一个可接受的第 17 名(50 支队伍中)。

当选定的模型应用于测试数据时(一旦提交给 Kaggle 竞赛),准确性得分明显较低。这被称为过度拟合,即模型可以很好地预测训练数据的标签,但在应用于新数据时会经常出错。这意味着模型有很高的方差。

尝试过的过度拟合的可能解决方案:

  • 添加更多数据
  • 移除异常值
  • 降维(通过主成分分析)

通过识别 15 个空值并在训练集中手动添加流派标签,试图增加总体样本大小。然而,使用 z 分数移除异常值会产生很大的反作用。总体而言,可用于训练模型的数据量非常有限,这可能是导致过度拟合的原因。

# reload the data
class_train = pd.read_csv("CS98XClassificationTrain.csv")
class_test = pd.read_csv("CS98XClassificationTest.csv")# update the genres for the missing values
class_train.loc[class_train['title'] == 'Unchained Melody', 'top genre'] = 'pop'
class_train.loc[class_train['title'] == 'Someone Elses Roses', 'top genre'] = 'adult standards'
class_train.loc[class_train['title'] == 'Drinks On The House', 'top genre'] = 'pop'
class_train.loc[class_train['title'] == 'Pachuko Hop', 'top genre'] = 'blues'
class_train.loc[class_train['title'] == 'Little Things Means A Lot', 'top genre'] = 'blues'
class_train.loc[class_train['title'] == 'The Lady Is A Tramp', 'top genre'] = 'pop'
class_train.loc[class_train['title'] == 'If I Give My Heart To You', 'top genre'] = 'pop'
class_train.loc[class_train['title'] == 'Happy Days And Lonely Nights','top genre'] = 'rock'
class_train.loc[class_train['title'] == 'Stairway Of Love','top genre'] = 'rock'
class_train.loc[class_train['title'] == 'You', 'top genre'] = 'pop'
class_train.loc[class_train['title'] == 'No Other Love' , 'top genre'] = 'adult standards'  
class_train.loc[class_train['title'] == "Hot Diggity" , 'top genre'] = 'folk'
class_train.loc[class_train['title'] == "Ain't That Just the Way" , 'top genre'] = 'r&b'
class_train.loc[class_train['title'] == "I Promised Myself" , 'top genre'] = 'pop'# dropping NULL values  (I've Waited So Long Anthony Newley = ? (Dance/ Electronic))
class_train = class_train.dropna(axis=0)# check again for null values
class_train.info()

PCA(主成分分析)是一种降维方法。降维移除数据集中冗余和高度相关的特征。它还有助于减少数据中的总体噪声。

from sklearn.decomposition import PCApca = PCA()
X_train = pca.fit_transform(X_train)
X_test = pca.transform(X_test)explained_variance = pca.explained_variance_ratio_
pca = PCA(n_components=0.95)
X_reduced = pca.fit_transform(X_train)
print(pca.explained_variance_ratio_)pca = PCA(n_components=6)
X_train = pca.fit_transform(X_train)
X_test = pca.transform(X_test)

使用主成分分析来降低维数没有显示出对 Kaggle 分数的改善。当特征之间有很强的相关性时,PCA 是最有效的。正如之前在关联热图中所强调的,训练数据集中的要素之间没有强关联。

虽然该模型在测试数据上表现不佳,但希望有一些有用的经验教训和可视化,可以为您未来的项目重铸!

安德烈·布尔科夫的百页机器学习书是一个很好的免费资源来学习更多关于 ML 的知识。

Spotiscience:数据科学家和音乐爱好者的工具

原文:https://towardsdatascience.com/spotiscience-a-tool-for-data-scientists-and-music-lovers-a3e32bd82ed1?source=collection_archive---------25-----------------------

Spotiscience 致力于简化下载和预测 Spotify 音乐数据的工作

马修·费雷罗在 Unsplash 上的照片

谁不喜欢和音乐一起工作?我们中的许多人整天都在电脑的后台进程中运行 Spotify,而播放列表中的随机音乐和我们喜欢的艺术家正在用他们的旋律营造一种神奇的氛围。它们提供了充分发挥生产力所必需能量。

为了整合我作为数据科学家的工作和我对音乐的热情,我决定创建一个名为“ Spotiscience 的工具,它允许使用官方 Spotify API 下载歌曲、艺术家、专辑和播放列表的数据。此外,这些数据被建模以生成新的数据,例如知道一首歌的情绪或歌词的主题,并找到类似的歌曲,这一切听起来很有趣,对吗?如果你想了解更多,请继续阅读这篇文章!

在本文中,您将学习:

  • 从 Spotify API 下载歌曲、专辑、播放列表和艺术家的数据
  • 从 API Genius 下载歌曲歌词
  • 预测歌曲的音乐情绪
  • 查找歌词的最相关主题
  • 从 Spotify 的专辑、艺术家目录和播放列表中搜索相似的歌曲

索引

1.1 SpotiscienceDownloader

  • 初始设置
  • 1.1.2 歌曲特征的提取
  • 1.1.3 影集提取
  • 1.1.4 提取播放列表
  • 1.1.5 播放列表和艺术家信息的提取

1.2 SpotisciencePredicter

  • 初始设置
  • 1.2.2 歌曲情绪的预测
  • 1.2.3 歌词主题预测
  • 1.2.4 相似歌曲预测

1.斑点科学

Spotiscience 是我在 GitHub 上用 Python 编程创建的一个项目,在这个项目中,你可以与 Spotify API 和 Genius API 进行交互,以提取歌曲、专辑、艺术家和播放列表的数据和特征。您还可以分析这些数据来生成新的信息,如情绪预测、主题建模和数学距离,以找到相似的歌曲。要下载 Spotiscience,您可以访问 Github 资源库。

https://github.com/cristobalvch/spotiscience

为了理解 Spotiscience 的应用和配置,我将详细介绍该工具的两个主要类别:

1.1 SpotiscienceDownloader

这个类从 Spotify API 和 Genius API 中提取数据。

初始设置

要使用它,必须进行如下设置:

import spotiscience#create a dictionary with authorization keys
CREDENTIALS = {}
CREDENTIALS['client_id'] = "your_spotify_client_id"
CREDENTIALS['client_secret'] = "your_spotify_client_secret"
CREDENTIALS['redirect_url'] = "your_redirect_url"
CREDENTIALS['user_id'] = "your_spotify_user_id"
CREDENTIALS['genius_access_token'] = "your_genius_access_token""""You also can set your credentials id on credentials.py"""# returns 'downloader class'
sd = spotiscience.SpotiScienceDownloader(credentials=CREDENTIALS)

要获得 Spotify API 和 Genius API 的授权凭证,您可以观看以下教程:

认证 Spotify API 教程

认证天才 API 教程

要获取您的 Spotify 帐户的“ user_id ,您需要打开桌面 Spotify 应用程序,转到“ profile ”,然后复制指向 profile 的链接,如下所示:

作者照片

您将获得这个结果,您的 user_id 是粗体部分,链接的所有其他部分都可以删除。

" https://open . Spotify . com/USER/{USER _ ID}?si=9f52cafadbf148b2 "

1.1.2 歌曲特征的提取

要提取歌曲的特征,您应该在 Spotify 上搜索歌曲,然后复制歌曲的链接,如下所示:

在这种情况下,我复制了威肯的歌曲“眩目的灯光”的链接:

song_copy_link = "https://open.spotify.com/track/0VjIjW4GlUZAMYd2vXMi3b?si=369f90167c9d48fb"song = sd.get_song_features(song_id=song_copy_link)

结果将是具有以下歌曲特征的字典。要获得关于这些功能的更多信息,您可以在 Web API Spotify 上阅读关于音频功能的官方文档

{'id': '0VjIjW4GlUZAMYd2vXMi3b','name': 'Blinding Lights','artist': 'The Weeknd','album': 'After Hours','release_date': '2020-03-20','popularity': 94,'length': 200040,'acousticness': 0.00146,'danceability': 0.514,'energy': 0.73,'instrumentalness': 9.54e-05,'liveness': 0.0897,'valence': 0.334,'loudness': -5.934,'speechiness': 0.0598,'tempo': 171.005,'key': 1,'time_signature': 4}

您还可以提取歌曲的音乐类型和歌词,如下所示:

# Returns song lyric
sd.get_song_lyrics(songname=song['name'],artistname=song['artist'])#Returns song Genre
sd.get_song_music_genre(song_id=song['id'])

1.1.3 影集提取

要从专辑中提取歌曲的特征,您必须在 Spotify 上搜索专辑并复制专辑的链接。相册提取方法有一个 id 参数,接收相册链接的字符串或字符串列表,需要在“False”中指定参数 is_artist :

#Returns songs features of album or albumsalbums =[
‘https://open.spotify.com/album/4yP0hdKOZPNshxUOjY0cZj?si=p5ItRNgXRlarmq4cihAVmA&dl_branch=1',
‘https://open.spotify.com/album/6Yf9kML4wT3opmXhTUIfk7?si=clKN-hzuTB236hINPATp-Q&dl_branch=1'
]sd.get_albums_song_features(id=albums,is_artist=False)

结果将是一个字典,其中的关键字是专辑的名称,内容对应于专辑歌曲的所有特征的列表。

也可以下载艺术家的唱片目录,在这种情况下,参数 id 只接收一个字符串,需要在“True”中指定 is_artist ,如下所示:

#Returns songs features of artist's discographyartist = 'https://open.spotify.com/artist/4fvA5FEHcdz97gscK90xZa?si=HNLNN7-dS5OR2W9TIUqQug&dl_branch=1'sd.get_albums_song_features(id=artist,is_artist=True)

1.1.4 提取播放列表

也可以从播放列表中提取歌曲特征。在这种情况下,playlist_id 参数只接收一个字符串,要提取的歌曲总数必须指定如下:

#Return song features of playlistplaylist = ‘https://open.spotify.com/playlist/37i9dQZF1DXcfZ6moR6J0G?si=da1435f1a0804933'sd.get_playlist_song_features(playlist_id=playlist,n_songs=50)

结果将是一个字典,其中的关键字是播放列表的名称,内容对应于一个包含播放列表歌曲所有特性的列表。

1.1.5 播放列表和艺术家信息的提取

最后,可以如下提取播放列表和艺术家的主要信息:

playlist = ‘https://open.spotify.com/playlist/37i9dQZF1DXcfZ6moR6J0G?si=da1435f1a0804933'artist = 'metallica'#Returns playlist information
sd.get_playlist_information(playlist_id=playlist)#Returns song information
sd.get_artist_information(artist=artist)

结果将是包含播放列表和艺术家信息的 2 个字典。

为了更好地理解所有的 SpotiscienceDownloader 方法,您可以通过点击这里来查看 GitHub repo 中模块 downloader.py 的源代码。

1.2 SpotiSciencePredicter

本课程使用监督学习的分类技术对歌曲数据建模,使用自然语言处理对主题建模,使用数学距离对歌曲相似性建模。

初始设置

要设置这个类,您只需要如下调用它:

import spotiscience
# returns 'predicter class'
sp = spotiscience.SpotiSciencePredicter()

1.2.2 歌曲情绪的预测

为了进行歌曲情绪预测,我使用了一种机器学习方法,从 Spotify 创建的情绪播放列表中标记一组歌曲,然后我用随机森林分类器算法训练一个模型,根据歌曲的特征标记歌曲。

关于这个话题的更多信息,你可以点击这里阅读我的文章《音乐情绪预测》

要预测情绪,只需传递用 SpotiscienceDownloader 提取的歌曲数据,如下所示:

#returns the tag of mood 
sp.predict_song_mood(song=song)

结果将是一个带有相应情绪类别的字符串,这些类别是;“悲伤、平静、活力和快乐”

1.2.3 歌词主题预测

歌词的主题预测使用潜在狄利克雷分配模型(LDA)、非负矩阵分解模型(NMF)或潜在语义索引模型(LSI)中的任何算法。为此,我将我的代码基于下面这篇文章,你可以在这里阅读。

要预测歌词的主题,您必须配置以下参数:

歌词=歌曲的歌词

model =要使用的模型[选项有“lsi”、“lda”或“NMF”]

lang =歌词语言[选项为“英语”或“西班牙语”]

n_grams =要分组的单词的子集数

n_topics =返回主题的数量

top_n =每个返回主题的字数

关于参数 n_grams 的更多信息,你可以点击这里阅读关于 sklearn 矢量化的官方文档

lyric = song_lyrics
model = 'lda' (available type 'lda', 'lsi', 'nmf')
lang = 'english' (available type 'english','spanish')
n_grams = (1,1)
n_topics = 1
top_n = 5#predict the topics of the song lyric
sp.predict_topic_lyric(lyric,model,lang,n_grams,n_topics,top_n)

1.2.4 相似歌曲预测

为了预测歌曲的相似性,我使用曼哈顿距离(l1)和欧几里德距离(l2)来计算歌曲特征之间的距离,并按升序对结果进行排序。

要预测歌曲相似性,您必须配置以下参数:

  • object =要比较的参考歌曲
  • target =要在专辑、播放列表或艺术家中评估的歌曲组
  • 距离=使用距离[选项为“l1”和“L2”]
  • n_features =用于计算距离的歌曲特征的数量
  • top_n =在元组结果中返回的歌曲数

关于参数 n_features 的更多信息,可以点击这里阅读方法的源代码。

例 1 :通过亚声调预测“ Nu Metal Generation ”播放列表中哪些歌曲与歌曲Change(In the House of Flies)】最相似。

playlist_link = "[https://open.spotify.com/playlist/37i9dQZF1DXcfZ6moR6J0G?si=452e104160384c8e](https://open.spotify.com/playlist/37i9dQZF1DXcfZ6moR6J0G?si=452e104160384c8e)"song_link = "[https://open.spotify.com/track/51c94ac31swyDQj9B3Lzs3?si=5aca903582464acd](https://open.spotify.com/track/51c94ac31swyDQj9B3Lzs3?si=5aca903582464acd)"target = sd.get_playlist_song_features(playlist_link,n_songs=70)
object  = sd.get_song_features(song_link)
distance = 'l2'
n_features = 6
top_n = 10#returns most similar songs from playlistsp.predict_similar_songs(object,target,distance,n_features,top_n)

2 :预测“杜阿·利帕唱片公司的哪些歌曲与“威肯的歌曲“眩目的灯光”最相似

artist_link = "https://open.spotify.com/artist/6M2wZ9GZgrQXHCFfjv46we?si=VJ3J-isZRbSM5x2pNUnrhw&dl_branch=1"song_link = "https://open.spotify.com/track/0VjIjW4GlUZAMYd2vXMi3b?si=369f90167c9d48fb"target = sd.get_albums_song_features(id=artist_link,is_artist=True)
object  = sd.get_song_features(song_link)
distance = 'l2'
n_features = 6
top_n = 10#returns most similar songs from playlistsp.predict_similar_songs(object,target,distance,n_features,top_n)

这两个例子的结果都是一个字典,其中的关键字是引用歌曲的名称(对象),内容是一个元组列表。每个元组都是歌曲名称及其与参考歌曲(对象)的距离的一对值。

注意:也可以预测专辑中的类似歌曲,而不必下载艺术家的整个唱片目录。为此,您可以在目标参数上使用相册功能。

2.结论

将数据科学和音乐这两个不同的领域结合起来,可以产生很好的选择,有助于理解音乐在不断变化的文化中是如何发展的,在这种文化中,乐器的声音、歌词和声乐技巧可以有许多不同的解释。在技术的帮助下,我们试图获得这些解释和意义的近似值,以研究是什么无形的能量使音乐伴随我们一生。我希望 Spotiscience 可以成为帮助像我一样的数据科学家、开发人员和音乐爱好者的技术之一。

3。参考文献

  • https://www . ka ggle . com/the brownviking 20/topic-modeling-with-spacy-and-sci kit-learn
  • https://towards data science . com/predicting-the-music-mood of-a song-with-deep-learning-c3ac2b 45229 e

我的其他文章

https://medium.com/datos-y-ciencia/data-science-para-todos-4cb84264bb5f https://cristobal-veas-ch.medium.com/artificial-intelligence-extracting-qualifying-adjectives-from-comments-on-donald-trump-facebook-84bf60be5c8e https://medium.datadriveninvestor.com/automate-the-process-of-adding-your-users-saved-tracks-by-genre-to-playlists-on-spotify-d6402da60285 https://medium.datadriveninvestor.com/automating-playlists-on-spotify-from-the-music-folders-of-your-computer-c79060eadab2 https://medium.com/datos-y-ciencia/una-forma-interactiva-para-buscar-y-analizar-ofertas-de-trabajo-en-la-web-ef9327b0a8d3 https://medium.com/datos-y-ciencia/una-forma-genial-para-buscar-y-analizar-arriendos-de-departamentos-en-la-web-e20727390a8c https://medium.com/datos-y-ciencia/cómo-hacer-reflexionar-a-tu-computador-con-las-noticias-del-país-dc14d2fc0a67

发现天才的机器学习工程师

原文:https://towardsdatascience.com/spotting-talented-machine-learning-engineers-5298a76427a0?source=collection_archive---------14-----------------------

了解关键技能领域,以识别有才华的机器学习工程师。这种理解将有助于招聘、分配和提升工程师。

图 1:与机器学习工程师相关的学科。作者图。

机器学习工程师(MLE)是目前最热门的角色之一。虽然许多人会将这样的角色与 Python 、 R 、随机森林、卷积神经网络、 PyTorch 、 scikit-learn 、偏差-方差权衡等联系起来。在这些工程师前进的道路上,会有更多的事情发生。MLE 需要处理的事情不仅来自机器学习领域,还来自其他技术和软学科。如图 1 所示,除了拥有 ML 技能,MLE 还需要了解编程、(大)数据管理、云解决方案和系统工程。此外,此人需要有相当多的项目管理技能,以及成为一个坚实的团队成员,而不牺牲个人的好奇心和野心。

在这篇文章中,我们分享了如何根据这些学科的能力来发现有才华的 mle。我们将学科分为四类,其中只有一类是纯技术性的。其余的更软,组织和项目管理技能,这可能与其他专业人士有关。随着工程师成熟度的发展,我们也分享了期望的演变。你应该考虑到,除了在早期阶段获得的技能之外,处于某一成熟水平的工程师还会获得新的技能。在文章中,我们只展示了三个级别:初级、高级和领导。如果您不同意这些级别,而更喜欢更细粒度的级别,那么就按照您认为合适的方式进行调整。

可扩展的机器学习系统

如图 2 所示,支持基于 ML 的系统需要的不仅仅是 ML 代码。因此,MLEs 需要了解来自数据科学、计算机科学、软件工程、云工程和系统工程的概念。此外,MLEs 需要精通编程语言、数据科学库、大数据工程库、云和系统管理平台。在这种设置中,MLEs 感知笔记本或脚本之外的机器学习代码。相反,他们将它们视为可以模块化的应用程序,以便在生产基础设施中轻松工作。他们也知道 ML 代码需要使用一组协同工作的系统来托管、管理、执行、调试、记录和监控。我们在这些维度上捕捉 mle 的能力。

图 2:只有一小部分真实世界的机器学习系统由机器学习代码组成,如中间的小黑框所示。所需的周边基础设施庞大而复杂。MLE 需要深入研究所有这些系统。最初发表在论文机器学习系统中隐藏的技术债务。

核心技术学科的基础知识

  • 初级工程师拥有这些学科的功能知识。
  • 高级工程师在某些领域拥有广泛而深入的知识。这些工程师也可以指导低年级学生提高他们的知识。
  • 领导级别的工程师可以重新定义某些领域的概念和知识,以更好地适应问题领域。这些工程师还可以指导高年级学生如何成为这些学科的专家或多面手。

核心学科的库和框架知识

  • 初级工程师熟悉知名的库和框架。
  • 高级工程师与许多备选方案合作过,了解其中一些方案的设计原则和底层实现。这些工程师还可以帮助团队采用和解密这些库。
  • 领先水平的工程师自举或贡献了一些众所周知的被许多人使用。工程师还可以识别出团队应该关注的库和框架。

端到端机器学习工作流的实现

  • 初级工程师知道如何实现典型的端到端 ML 流程。
  • 高级工程师知道如何为大规模或复杂的部署改进/重新设计/重新实施工作流中的组件。工程师也知道如何测试和验证流程。
  • 领导级别的工程师可以提出参考架构,他们的概念证明,并使其他团队能够采用这些架构。

系统设计与实现

  • 初级工程师知道如何与操作和开发系统互动。
  • 高级工程师知道如何使用常见的虚拟化、DevOps 和云技术来实现其中的一些系统。他们还可以指导初级工程师更熟练地使用和掌握构建模块。
  • 首席工程师可以设计参考体系结构及其概念验证。他们还可以帮助团队制定采用这些架构的策略。他们还知道容错、可测试性、可调试性等的实现方法。

最佳实践

像任何其他工程一样,机器学习工程是一项团队活动。这基本上意味着某些实践需要微调,以便团队可以有效地相互作用,从而更容易交付。MLE 还需要能够原型很多。此外,MLEs 需要开发易于共享的东西,并且必须愿意建立在其他人的工作之上,这来自于内部源文化(想象一下,只在组织的范围内采用开源文化)。

工程最佳实践

  • 初级工程师擅长执行坚持原则的任务
  • 高级工程师帮助团队采用某些最佳实践,同时继续价值交付。
  • 一流的工程师可以将这些原则具体化为可行的实践。他们还可以找出团队的(长期)策略,通过采用关键的最佳实践,在不牺牲当前价值交付太多的情况下,爬上技术成熟度的阶梯。

原型制作

  • 初级工程师偶尔可以通过查看已知的例子来开始一个新的组件。
  • 高级工程师可以在没有已知示例的情况下实现/验证新组件。
  • 首席工程师可以提出设计原则和流程来实施/验证新组件,并推动核心组件的开发,从而实现快速原型开发。

内部来源

  • 初级工程师知道如何对内在源泉做出贡献。
  • 高级工程师可以推动一些关键的最佳实践和部落知识文档。工程师可以帮助团队在不同的环境中翻译这些内容。
  • 领导级别的工程师可以确定内部源文化的框架,这将使高级工程师能够起草最佳实践和部落知识文档的内容。

项目管理

像任何工程师一样,MLEs 需要在日常工作中保持高效。这意味着掌握项目管理技能,包括设计思维、计划、理解交付和敏捷原则。对于 MLE 来说尤其如此,因为 MLE 很可能在跨职能团队中处于核心地位,需要发挥很大的作用。

设计思维

  • 初级工程师知道如何进行简单的需求分析。
  • 高级工程师知道如何执行全面的需求分析,验证需求,并帮助团队充实细节。
  • 领导级工程师与利益相关者建立牢固的关系。他们知道如何参与描绘涉众旅程的对话,并将它们转化为一组系统需求。工程师还知道如何定义度量,以便通过分析理解需求及其解决方案。

通信

  • 只要有需要,初级工程师就能与团队很好地沟通。
  • 只要有需要,高级工程师也会与利益相关者进行良好的沟通。
  • 领导级别的工程师努力建立便于沟通的环境。

策划

  • 初级工程师知道如何针对工程问题实施计划。
  • 高级工程师知道如何充实此类计划的细节,并细化实施细节。
  • 领导级别的工程师知道如何为工程问题定义里程碑,并帮助团队区分活动的优先级,以确保达到里程碑。他们知道如何做出正确的取舍,不会过度设计事情。

交付心态

  • 初级工程师知道如何在合理的时间内按计划交付。
  • 高级工程师知道如何将交付分解成可管理的集合,并能够支持扩展目标,例如降低成本、提高效率等。,用于专注的工作。
  • 领导级别的工程师知道如何通过复杂的调度来推动并行交付议程。他们还充分预测即将到来的情况和跑道上的进展,以使其易于交付。

敏捷原则

  • 初级工程师通常能熟练掌握敏捷方法的仪式(看板、Scrum 等)。).
  • 高级工程师知道如何坚持原则,并帮助团队坚持原则。
  • 领导级别的工程师知道如何为团队翻译敏捷仪式的原则,并帮助他们选择正确的变体

文化契合度

MLEs 的存在是为了支持团队,而不是相反。因此,很多重点应该放在 MLEs 如何融入团队文化上。这并不意味着 MLEs 应该同意团队其他成员决定的所有事情。更确切地说,MLEs 和其他专业人士一样,应该做一些让团队变得更好的事情,即使这意味着不同意团队的其他成员,但无论如何都要遵守工作计划。此外,无论工程师开的是什么,他们都应该尽可能拥有全部所有权。争取多元化是建立一个健康团队的方法,这个团队知道如何处理赞美的意见。机器学习旨在解决复杂的全球问题,这不可能由一个狭隘的背景团队来完成。最重要的是,工程师需要有一种成长的心态,这种心态显然来自于主动和被动的学习。

团队第一

  • 只要团队需要,初级工程师随时准备介入。
  • 高级工程师知道如何介入,并可以随着情况的变化调整参与程度。
  • 一流的工程师积极致力于创造一个培养健康团队的环境。

所有权

  • 初级工程师知道如何掌控他们觉得舒服的领域。
  • 当被要求时,高级工程师知道如何掌控所有领域,即使他们对其中一些领域感到不舒服。
  • 领导级别的工程师知道如何掌控一切,无论舒适程度如何,并建立一个鼓励他人这样做的环境。

包容性

  • 初级工程师知道如何在多样化的工作环境中工作。
  • 高级工程师知道如何支持团队保持包容性。
  • 领导级别的工程师知道如何努力创造一个更包容的工作环境。

学习心态

  • 初级工程师有很大的动力提升自己的技能。
  • 高级工程师知道如何在满足他们自己的学习目标的同时促进一群人的学习活动。
  • 领导级别的工程师知道如何通过流程和策略实现团队学习。

评论

我确信我没有充实许多其他重要的特征。然而,至少上面提到的那些会让你开始。如果您不同意以上某些观点,请分享您的观点。如果你觉得其他一些观点需要提及,请分享你的观点。如果您同意这些观点并认为它们很有帮助,请分享您的观点。

电子表格到 Python:是时候进行转换了

原文:https://towardsdatascience.com/spreadsheets-to-python-its-time-to-make-the-switch-ef49cf9463d5?source=collection_archive---------5-----------------------

电子表格到 Python

使用代码更可靠,更可复制,也更有趣

照片由米卡·鲍梅斯特在 Unsplash 上拍摄

大多数人对数据分析的入门是使用电子表格,比如 Microsoft Excel。

电子表格非常强大和受欢迎,但也有明显的局限性,特别是在可重复性、可共享性和处理大型数据集方面。

当到了认真对待数据分析的时候,有经验的从业者会告诉你使用代码:它更可靠,更容易复制,而且更令人愉快。

但是,如果您是编码新手,从基于电子表格的数据分析过渡到基于代码的数据分析可能会令人望而生畏。

为了让您更容易开始基于代码的分析,我创建了一系列博客文章,使用日常示例展示代码如何取代电子表格来可视化、分析和建模数据。

我们将使用流行的编程语言 Python,它已经成为基于代码的数据分析的事实上的标准。代码都是通过在线协作工作空间运行的。

让我们开始吧。

你所在的地方天气怎么样?

为了向您展示为什么 Python 中的分析更优越,我将稍微聊一下天气。

具体来说,牛津的天气,但你也可以把它应用到你的城镇。这篇文章将向你展示如何从服务器自动下载天气数据,生成包含温度信息的数据帧,绘制不同的平均值,并拟合趋势线。

所有这些活动都可以在电子表格中完成,但使用代码可以更容易地更改分析,随着新数据的出现更新分析,分析更大的数据集,并以可重复的方式分享我们的分析。也更容易产生漂亮的图形!

下载数据

牛津的拉德克利夫气象站有可下载的天气数据。对于这个例子,我将探索从 1815 年开始的每日温度测量!

我们需要做的第一件事是导入 Pandas,这是一个用于数据分析的强大的 Python 包。然后,我们可以使用 Pandas 函数 read_csv 直接从牛津大学网站读取天气数据。这给了我一个数据框架,我们可以将它可视化如下:

作者图片

数据帧由数据列组成,标题指示每列的内容。

这看起来非常像你在电子表格中看到的。在没有数据的地方,我们看到 NaN。然而,在某些单元格中,csv 文件包含文本解释,稍后我们可以很容易地用 NaN 替换它。

通过查找包含我们需要的年、月和日的行,我们可以找到特定日期的数据。在 Excel 中,我们可以通过在列上放置过滤器来实现这一点。

作者图片

如果我们只想知道当天的温度,我们可以使用:

这给出了 4.7℃的输出

这比在电子表格中滚动大量数据要容易得多。这里唯一棘手的是记住如何写学位标志!

我们现在将做一些数据处理,以便我们可以比较不同年份的温度。

我们想要的是一个数据帧,其中列代表年份,行代表一年中的每一天。

我们要做的第一件事是删除 2 月 29 日的所有数据。我们通过查找所有行的索引(最左边的一列)来实现,其中 month 是 2,day 是 29。

然后我们使用。拖放以删除这些行。同样,这可以在电子表格中完成,但是使用 Python 和 Pandas 要简单得多。

完成后,保存新的数据帧是很有用的,这样我们就不必再次下载和操作它。

绘制温度图

现在让我们制作一些漂亮的图表来显示每天的温度。

我们将使用三个最常见的 Python 库中的函数:Pandas、Numpy 和 Matplotlib。首先我们需要导入这些库。

现在读入我们之前保存的数据。

利用这些数据回答一个有趣的问题可能是,“今天异常热吗?”,所以我们来看看 Tmax。为此,我们创建了一个包含每天最高温度的新数据帧。

作者图片

虽然这段代码最初看起来很复杂,但实际上比在 Excel 中执行同样的任务要简单得多。

现在我们有了每天的最大温度,很容易画出每年每天的最高温度,并突出显示我们想要的任何一年。然而,这并没有太大的意义,正如我们从下图中看到的。

作者图片

在这个图中,很难看出 2020 年是否是一个异常值。为此,我们需要计算 Tmax 的均值和标准差,并重新绘制。

作者图片

这里灰色的线代表标准差。看起来 2020 年有些热天!

当然,如果我们选取任何一年,都可能有异常值:我们预计大约有三分之一的日子在标准偏差之外,大约六分之一高于标准偏差,六分之一低于标准偏差。

如果我们想知道温度总体上是否在上升,也许最好取两个时间段的平均值,如下所示:

我们现在可以将最近 20 年的平均值与 1990 年以前的平均值和标准误差进行对比。

作者图片

所以,看起来过去 20 年比长期平均气温要高;尤其是冬天的气温明显更高。

计算趋势

让我们通过仔细观察 Tmax 这些年来的变化来进一步探讨这一点。我们感兴趣的是每年最热和最冷的一天,以及全年 Tmax 的平均值。

这显示了使用 Pandas 数据框架的一些威力;上面我们计算了行的平均值,现在我们找到了列的平均值,我们甚至可以在绘制图表的同时进行计算。

作者图片

从这张图表中可以看出,在过去的几年中,气温有所上升;让我们通过拟合一些趋势线来量化这一点。在这里,我们将从 1980 年开始重新定义“最近”的年份。

作者图片

该代码告诉我们,1980 年之前的梯度为每世纪 0.44 摄氏度,但自 1980 年以来,它已相当于每世纪 4.9 摄氏度。

在代码中这样做的一个巨大优势是,很容易改变用于比较的年份范围。

显然,近年来的梯度比过去 165 年要大。为了确定这是否有统计学意义,我们需要测量梯度计算中的误差。这就是所谓的标准误差,可以用不同的函数很容易地计算出来,相当于 Excel 中的 LINEST(我个人觉得用起来很痛苦)。

作者图片

该函数在那里输出相当多的信息;让我们只看我们想要的价值观

作者图片

+/-值代表 95%的置信区间。这意味着在过去的 40 年里,平均气温以每世纪 4 到 6 摄氏度的速度上升,这在统计学和这个词的日常意义上都是很重要的。

摘要

我已经展示了一个如何使用 Python 来执行简单的可视化和数据分析的例子。

有些步骤会比使用电子表格花费更长的时间。然而,对于大型数据集或重复操作,使用代码减少了错误的范围,同时使重复或修改计算变得容易。这绝对值得额外的时间投入。

最后,在这里的几乎所有例子中,我都使用了默认的 matplotlib 格式的图形,它们已经非常好地呈现了。这绝对是电子表格无法比拟的优势。

在这个博客系列中,我将进一步探索 Python 的功能,并希望能启发您迈出使用 Python 的第一步。

这篇文章的文本、代码和图像是使用 灯丝 创建的,这是一个用于数据、分析和报告的一体化工作空间。如需了解更多信息,请访问我们的网站http://www.filament.so*。Filament 目前正在运行一个封闭的测试程序;前 100 人到* 报名 使用推荐代码 TDSFILAMENT 可以跳过等候名单,提前报名。

参考

[1] Stephen Burt 和 Tim Burt,1767 年以来的牛津天气与气候 (2019)牛津大学出版社。

在生存森林中被抛弃:Python 中的客户流失预测

原文:https://towardsdatascience.com/spurned-in-a-survival-forest-customer-churn-prediction-in-python-2083551f3cc5?source=collection_archive---------6-----------------------

具有 PySurvival 的条件生存森林模型

年轻的爱被拒绝的股票照片——由伊斯托克/范德韦尔登(istockphoto.com)在标准许可下拍摄

一家公司的客户流失率 比率是在给定的时间段内停止与该公司做生意的客户的百分比。他们中的大多数人肯定已经开始喜欢竞争对手的产品了。留住客户的努力旨在最大限度地降低流失率。

一般来说,试图吸引新客户——通过把他们从竞争对手那里引走——比留住现有客户的成本更高。贝恩公司(Bain & Company)的一篇论文曾指出,在金融服务行业,客户保持率每增加 5%,利润就会增加 25%以上(bain.com)。

我们的目标是构建一个预测每个客户流失风险的模型。为了向销售和营销团队提供早期预警信号,该模型应识别那些与客户可能很快会拒绝我们的业务的风险增加相关的客户资料。

1.属国

除了我们的核心包如 pandas 和 numpy,我们将安装并导入 PySurvival 库。

和往常一样,在安装一个复杂的包之前,您应该创建一个新的虚拟环境,尤其是具有非 Python 依赖性的包。PySurvival 需要 C++可再发行版本。

要在 MacOS 或 Linux 上 pip-install PySurvival,您应该检查 gcc 是否已经安装在您的机器上:

  • 安装—py survival(square . github . io)

Windows 机器上,安装需要一些额外的步骤。PySurvival 开发人员似乎忘记了一个事实,即 Python 用户社区存在于操作系统划分的 Windows 端;或者他们只是没那么喜欢 Windows 用户。但是我们可以在 Windows 机器上运行 PySurvival,即使 pip 安装无法运行。请遵循以下步骤:

  • 使用不高于 3.8 的 Python 版本创建新的虚拟环境。例如在 conda 中:$ conda create-n your chosen name python = 3.8。原因是当前的 PySurvival 版本使用了一种 3.8 以后已经不推荐使用的方法 tp_print。
  • 你将需要微软的 C++编译器。如果您的计算机上还没有安装它,那么一种方法是从以下网址下载免费的 Visual Studio 构建工具:下载 Visual Studio 工具—免费安装 Windows、Mac、Linux(microsoft.com)。在构建工具选择窗口的左上角,选择“用 C++进行桌面开发”选项。

截图:作者

  • 从这个 GitHub 资源库下载分叉的 PySurvival 包,在 Windows 下工作:【github.com】bacalfa/py Survival:生存分析建模开源包【对其创建者 B. A. Calfa 的赞赏】。将下载的软件包保存在要用作其安装目录的文件夹中。
  • 激活您的新虚拟环境,例如:$ conda activate yourchosenname
  • 在命令提示符下,导航到保存并解压缩 PySurvival 包的目录。
  • 在命令提示符下,首先重新打包下载的文件,然后安装软件包:

作者图片

作者图片

2.数据争论

PySurvival 附带了一个内置的数据集来分析客户流失。我们从数据集模块加载它。

原始数据代表 SaaS 提供商(软件即服务)的客户数据库,该提供商为中小型企业提供一系列服务:数据存储、工资和费用核算、在线营销和客户响应跟踪。它的商业模式是基于每月订阅。

这家 SaaS 公司要求我们实施一个预测客户流失风险的模型,以便销售和营销团队能够采取行动,增加留住这些高风险客户的机会。

作者图片

源数据由 13 列组成,其中 5 列是分类变量。Pandas 的 info()函数显示 dataframe 不包含空值。

当我们试图确定流失风险背后的模式时,有两列将作为我们的目标变量:

  • “搅动的”,标识过去没有续订的客户
  • “months_active”,它添加了一个时间维度,我们希望沿着这个维度来跟踪客户流失风险

其他列表示影响客户流失风险的特征。我们想确定哪种特征值组合——哪种客户特征——将流失概率提高了多少。

作者图片

作者图片

当我们查看分类变量时,我们看到每一列中唯一值的数量很少。它们的低计数将使它们更容易转换成数值。

作者图片

为了评估分类值对流失风险的影响,我们需要导出数值。

下面的列表 catcols 标识分类列的索引。然后, ne-hot 编码,通过使用熊猫的 get_dummies() 函数,为每个唯一分类值创建一个新列,并用二进制值 1 和 0 填充它:值 1 出现在找到匹配分类值的行中。

info()函数确认没有留下分类列。它还显示 dataframe 现在包含 26 列,而原来只有 13 列。例如,前一列“公司规模”中的四个唯一值被拆分为四列。

作者图片

作者图片

这个扩展的数据框架将使我们能够运行数据科学模型,量化特征变量对流失风险的影响。

作者图片

我们通过分离目标变量来完成我们的数据争论工作:

  • “事件”——在我们的例子中是“搅动”= 1 或 0
  • 时间维度:“months_active”

然后,第 6 行中的 numpy 函数 setdiff1d() 从所有列名的列表中删除“churned”和“months_active ”,留下一个特性列的列表。

作者图片

3.识别流失风险

3.1 特征的相关性

在 PySurvival 的效用函数中,我们找到了相关矩阵。它显示了特征对齐的紧密程度。如果任何一对显示出惊人的高相关性,接近 1.0,我们应该删除其中一个来处理它们的多重共线性。

在我们的例子中,大中型客户公司之间的最高相关性不超过 0.52。这不是一个令人担忧的水平,所以我们继续我们的分析。

作者图片

3.2 训练与测试数据集

在第 3 行,Scikit 的 train_test_split 方法保留了 35%的数据集用于测试。

第 9 行到第 11 行对特性列 X、事件列 E ("churned ")和时间列 T ("months_active ")应用相同的分割。

作者图片

3.3 条件生存森林模型—概念

我们从生存分析之外的分类问题中知道决策树和随机森林。你可以在 Carolina Bento 的文章中找到关于决策树的优秀初级读本:现实生活中解释的决策树分类器:挑选度假目的地| Carolina Bento |迈向数据科学;在饶彤彤关于随机森林的文章中:了解随机森林。算法如何工作|饶彤彤|走向数据科学

生存问题增加了一个困难:模型必须处理被审查的数据。当数据集达到观察期的末尾时,许多(希望是大多数)客户将不会有动摇。他们的流失事件仍未被观察到,并将在未知的未来日期发生。因此,数据集是右删截的。

为了处理审查问题,生存模型使用一个修改的配置:

  • 传统的回归模型对两个数据组 X 和 y 进行操作:回归变量 X 和目标向量 y。
  • 而生存模型对 X、E 和 T 这三个变量起作用:
  • —特征数组 X(由代表客户属性或概况的列组成)
  • —二进制事件指示符(1 或 0)的向量 E,其表示是否已经为客户记录了流失事件;
  • —时间向量 T = min(t,c),其中 T 表示事件时间,c 表示审查时间

生存模型预测感兴趣的事件在时间 t 发生的概率。

我们将实现由赖特/丹科夫斯基/齐格勒在 2017 年(【arxiv.org】)设计的条件生存森林模型。CSF 改进了旧的随机生存森林方法使用的训练算法。

3.4 拟合模型

PySurvival 的 CSF 模型采用以下参数:

  • 每次分割的最大要素数
  • — int =绝对数字
  • —浮点=分数
  • —“sqrt”=特征数量的平方根
  • —“log2”=特征数量的二进制对数
  • —“全部”
  • 最小节点大小=每个节点的最小样本数,默认为 10
  • alpha =允许拆分的显著性水平,默认为 0.05
  • 样本大小百分比= %每个树构建中使用的原始样本,默认值为 0.63
  • num_threads =并行线程中可用内核的数量

拟合生存森林后,我们检查它的准确性。

3.5 准确性指标

一致性指数衡量模型的辨别能力。如果有四个客户在 2 年、3 年、4 年和 5 年后流失,而模型准确预测他们将按此顺序流失,则和谐指数达到其最佳值 1.0。如果模型预测了不同的等级顺序,则指数将低于 1。该指数并不评估预测的存活时间,而是评估排名顺序。如果模型为四个客户分配了 3、4、6 和 7 的流失周期,则指数仍将等于 1.0,因为它报告了客户的正确排序顺序( C-index) 。虽然这看起来像是一个弱点,但与 MAE 等其他准确性指标相比,我们需要一个像 concordance index 这样的指标来处理右删截数据:在观察数据集的结束日期,所有客户的流失事件将只记录一小部分。

就我们的目的而言,我们认为 0.87 的一致性指数是令人满意的。

Brier 评分衡量实际流失状态和估计概率之间的平均差异:观察到的生存状态(0 或 1)和给定时间 t 的生存概率之间的平均平方距离。它可以采用 0 到 1 之间的值,0 是最佳值:生存函数将完美地预测生存状态( Brier 评分)。通常,最大值 0.25 被视为接受阈值。

在我们的例子中,Bier 分数不超过 0.14:低到可以接受。

作者图片

PySurvival 的compare _ to _ actual方法沿时间轴绘制了预测的和实际的风险客户数量。它还计算三个精度指标,RMSE、平均误差和中位数绝对误差。在内部,它计算 Kaplan-Meier 估计量,以确定源数据的实际生存函数。

作者图片

3.6 模型结果:特征重要性

拟合模型报告了数据帧中每个特征的重要性。重要性衡量特性对流失风险的影响。积极的重要性分数会增加风险,消极的分数会减轻风险。

列“pct_importance”在从 0 到 1 的范围内校准相对重要性。相对重要性合计为 1.0。

  • 不出所料,“csat _ score”——通过市场调查衡量的客户满意度得分——与客户流失风险密切相关。
  • 没有预订工资单或产品会计服务的客户更容易拒绝。要么其他顾客已经学会欣赏整个产品系列;或者,将这些更复杂的任务转移给竞争对手会花费他们更多的精力,因此,只要竞争对手不能展示出明显更好的竞争优势,他们就会继续留在 SaaS 公司。**
  • “minutes_customer_support”将那些在取消订阅之前经常向支持热线投诉的客户视为高流失风险。

作者图片

3.7 流失预测

让我们从源数据中随机抽取一个客户:index 1989。

请记住,我们创建了 dataframe X 来包含所有的特性列(不包括目标变量 churnedmonths _ active】)。我们将随机选择的索引行 1989 的特征输入三个函数:生存、危害和风险评分函数。

如果我们将时间变量 t 设置为 None,那么 PySurvival 将计算数据帧中所有时间段的函数值,并显示它们的趋势——在我们的示例中是 12 个月。

作者图片

生存函数是累积分布函数的补码:

  • svf(t > T) = 1 — cdf(t ≤ T)

变量 T 表示直到感兴趣的事件发生的时间。

累积分布函数沿着时间轴追踪事件的增长概率。流失风险会随着时间的推移而增加。最终,许多客户将会破产或尝试竞争对手的产品。

相反,生存函数 svf(t)报告事件在时间 t 之前没有发生的概率。

危险函数 hf(t)代表时间 t 的瞬时故障率:事件在下一个时刻 t内发生的概率,假设事件在时间 t 之前没有发生。通常,危险函数不能单独解释。*

风险分值汇总了一段时间内的累积危险值。PySurvival 将其报告为我们选择的整个时间范围的标量。

下面的笔记本单元格计算每个客户在四个时间点的风险分数和生存概率:订阅期的 1、3、6 和 12 个月。它将生存概率作为四个新列插入数据帧:svf1、svf3、svf6 和 svf12。

作者图片

PySurvival 的方法 create_risk_groups 绘制了数据集中客户的得分。这有助于想象,例如,客户数量是否在接近标尺的高端时开始上升。

该方法采用可选数量的分组标准。在第 2 行到第 5 行,我选择用四分位数来划分风险分值。然后,图表通过许多不同的颜色代码来区分四个风险分值组。

作者图片

结论

我们将生存分析技术——条件生存森林——应用于我们的客户数据库。

该模型确定了客户档案中的哪些特征决定了流失风险:每个特征的重要性权重。然后我们将生存概率输入客户数据库。这些指标将为营销团队提供他们所寻求的预先信号:什么时候接触客户是明智的?

Jupyter 笔记本可在 GitHub 下载:h3ik0th/py survival _ CSF:py survival 的有条件生存森林(github.com)的客户流失

从 A 到 Z 的 SQL:第 1 部分

原文:https://towardsdatascience.com/sql-a-to-z-part-1-79ba695a563d?source=collection_archive---------35-----------------------

学习最重要和最常用的 SQL 语句

为什么要学习 SQL?

作为数据科学家,我们利用数据提供见解和建议,为产品战略、增长和营销、运营以及许多其他业务领域提供信息。在许多情况下,数据以关系数据库格式存储,使用允许访问彼此相关的数据点的结构(来源: AWS )。SQL(结构化查询语言)是一种用于访问关系数据库的编程语言。作为数据科学家,我们使用 SQL 对数据库执行查询,以检索数据、插入、更新和删除数据库中的记录,并创建新的数据库和表。语法可能会因关系数据库管理系统的不同而略有不同(例如,在查询末尾使用分号),但一般逻辑应该适用。

数据作为称为表的对象存储在数据库中,表是列和行形式的数据条目的集合。列(也称为字段)包含列名及其属性。行也称为记录,包含每列的数据。

照片由奥斯丁·迪斯特尔在 Unsplash 上拍摄

例子

让我们用一个简单的例子来完成最常用的 SQL 查询。假设有一家公司通过类似于脸书、Reddit、Pinterest 等的应用程序向用户提供内容。

当用户登录该应用程序时,该公司会向用户提供内容。这记录在内容插入表中。该表存储了向谁提供了什么内容以及时间戳。

我们还有一个名为 user_content_activity 的表,当用户对内容进行操作时,它会存储所有数据。它包括用户 id、内容 id、操作类型和用户执行操作的时间戳。

最后,我们有两个表给出了用户和内容的属性,分别命名为用户内容users 表存储用户居住的国家和用户加入(或注册)应用程序的日期。内容表存储内容类型。

表名:用户 _ 内容 _ 活动

+--------+-----------+--------+---------------------+
| userid | contentid | action |      timestamp      |
+--------+-----------+--------+---------------------+
|1       | 5         | view   | 2021-01-05 10:30:20 |
|1       | 5         | click  | 2021-01-05 10:30:55 |
|2       | 21        | view   | 2021-01-06 03:12:25 |
|3       | 100       | view   | 2021–02–04 06:25:12 |
+--------+-----------+--------+---------------------+

表名:用户

+--------+---------------+------------+
| userid |    country    |  join_date |
+--------+---------------+------------+
|1       | United States | 2018-05-06 |
|2       | Italy         | 2019-12-31 |
|3       | Japan         | 2020-03-05 |
|4       | United States | 2021–01-26 |
+--------+---------------+------------+

表格名称:内容

+-----------+-----------+
| contentid |   type    |
+-----------+-----------+
|1          | video     |
|2          | photo     |
|3          | text      |
|4          | photo     |
+-----------+-----------+

基本形式

***SELECT {column1, column2, …} 
FROM {tablename}***

这是从数据库中检索信息的最基本形式。编写查询时,要问的第一个问题是“哪个(哪些)表有我需要的信息?”然后,“哪一列有适当的信息?”举个例子,

  • 问:给我用户来自的国家列表 >从用户中选择国家
  • 问:给我所有的用户、内容和用户采取的行动

    从用户内容活动中选择用户标识、内容标识、行动

  • 问:给我 content_insertion 表中的所有数据

    从 content_insertion 中选择 userid、contentid、时间戳
    从 content_insertion 中选择*

符号 ***** 是从表中检索所有列而不是列出所有列的简单方法。当您想要查看表中的一些数据时,这特别有用。为此,我们建议使用SELECT * FROM content _ insertion LIMIT 100,这将只给出表格的前 100 行。“LIMIT”总是出现在查询的最后。****

添加条件

****SELECT {column1, column2, …} 
FROM {tablename} 
WHERE {conditional statements}****

当我们想要给我们正在检索的数据添加一些条件时,我们添加“WHERE”子句。在许多情况下,我们将需要使用众所周知的数学符号=,!=,>, =,<= or logics such as AND, OR, NOT, as well as many others. We’ll go through the most common ones in the following examples.

  • ****问:给我美国的用户列表

    从国家=‘美国’的用户中选择 userid****

请注意,我们在问题中使用了“美国”而不是“我们”。这是因为当我们查看该表时,US 被存储为 United States。一个很好的澄清问题应该是“美国是存储为‘US’,‘美国’,还是两者都是?”

我们使用符号“=”来表示与右边的符号完全匹配的国家。如果数据存储为“美国”,则不会检索到该行,因为字符串不完全匹配。如果我们想同时检索两者,那么使用子句就可以解决问题(阅读更多关于通配符)。**

  • ****问:给我 2018 年加入的用户的唯一国家列表

    从 join_date 在‘2018–01–01’和‘2018–12–31’之间的用户中选择不同的 userid
    从 join _ date>= ' 2018–01–01 '和 join _ date<= ' 2018–12–31 '的用户中选择不同的 userid****

DISTINCT 是检索列的唯一值的有用语法。

****日期函数有很多,但要知道最重要的是 CURRENT_DATE、DATE_ADD、DATE_SUB、DATE_DIFF、DATE_FORMAT。例如,

SELECT userid FROM users,其中 join _ DATE BETWEEN CURRENT_DATE AND DATE _ SUB(' day ',-6,CURRENT _ DATE)给出一周前到今天之间加入的用户。****

  • ****问:给我照片和视频形式的内容列表

    从输入的内容中选择 contentid 照片’,‘视频’)****

****当我们希望用逻辑语句“或”匹配多个字符串时,使用中的中的【非 。以上查询同

从内容中选择内容 id WHERE(type = ' photo '或 type = 'video)
从内容中选择内容 WHERE type!= 'text'
从输入不在(' text ')的内容中选择 contentid********

聚合函数

通常我们希望找到一个变量的集合,比如 sum、average、counts、min、max。使用它的一般语法是

**SELECT column1, SUM(column2), COUNT(column3) 
FROM {tablename} WHERE {conditional statements} 
GROUP BY column1**

请注意,我们可以有一个或多个聚合函数,如果我们有任何条件语句,它们总是在 FROM 之后和 GROUP BY 之前。

  • **问:每个国家有多少用户?

    选择国家,COUNT(userid)从用户组按国家**

重命名聚合结果通常是一个好习惯。别名用于为表或表中的列提供临时名称。例如,使用COUNT(userid)作为 number_users

如果我们认为有重复的行,我们可以通过
选择国家,将(不同的用户标识)作为国家用户组中的 number_users 进行计数

  • **问:2020–01–03

    点击次数最多的 contentid 是什么?SELECT contentid,COUNT()AS view FROM user _ content _ activity WHERE action = ' click ' AND CAST(timestamp AS DATE)= ' 2020–01–03 ' GROUP BY contentid
    ORDER BY COUNT(
    )desc 限制 1**

ORDER BY 用于按升序(默认)或降序(通过指定 DESC)对结果集进行排序

  • **问:超过 100 个视图的内容有哪些

    选择 contentid,COUNT()作为来自 user_content_activity 的视图,其中 action =‘view’GROUP BY contentid HAVING COUNT()>100**

****当您根据聚合结果提取信息时,使用是一个很好的方法。它总是在 GROUP BY 语句之后。

到目前为止,我们已经学习了使用 SQL 检索数据的基本格式。在第 2 部分中,我们将讨论更多利用关系数据库的有趣内容。

SQL 作为数据分析工具

原文:https://towardsdatascience.com/sql-as-a-data-analysis-tool-a18bf698a9cd?source=collection_archive---------29-----------------------

如何使用 SQL 来执行高效的数据过滤和转换

艾萨克·史密斯在 Unsplash 上拍摄的照片

SQL 是一种用于管理关系数据库中的数据的语言。关系数据库的核心组件是表,它以带有标签的行和列的表格形式存储数据。

我们使用 SQL 的 select 语句从关系数据库中查询数据。就数据转换和过滤操作而言,select 语句具有高度的通用性和灵活性。

从这个意义上说,SQL 可以被认为是一种数据分析工具。使用 SQL 进行数据转换和过滤的好处是,我们只检索我们需要的数据。这比检索所有数据然后应用这些操作更加实用和高效。

在本文中,我们将通过 7 个例子来演示 SQL 如何被用作数据分析工具。例子的复杂性稳步增加,所以如果你到达结尾会更好。

我之前将 Kaggle 上的医疗费用数据集存储在一个名为 insurance 的 SQL 表中。让我们首先通过显示前 5 行来看看这个表。

mysql> select * from insurance-> limit 5;

保险表(图片由作者提供)

“*”表示我们想要显示所有列,limit 关键字指定要显示的行数。

该数据集包含一些个人信息和保险费用。

示例 1

我们可以计算吸烟者和不吸烟者的平均收费金额。

mysql> select smoker, avg(charges)-> from insurance-> group by smoker;+--------+-----------------+
| smoker | avg(charges)    |
+--------+-----------------+
| no     |  8434.268297857 |
| yes    | 32050.231831532 |
+--------+-----------------+

它类似于大熊猫的功能群。我们选择“吸烟者”和“费用”列,并对费用应用平均值函数。group by 语句允许根据 smoker 列中的不同类别来分隔行。因此,我们在结果中得到两个平均值。

如果我们想查看总体平均值,我们只需选择“费用”列。

mysql> select avg(charges) from insurance;+-----------------+
| avg(charges)    |
+-----------------+
| 13270.422265142 |
+-----------------+

示例 2

我们可能还希望看到吸烟者和非吸烟者的数量以及平均费用值。

mysql> select smoker, avg(charges), count(charges)-> from insurance-> group by smoker;+--------+-----------------+----------------+
| smoker | avg(charges)    | count(charges) |
+--------+-----------------+----------------+
| no     |  8434.268297857 |           1064 |
| yes    | 32050.231831532 |            274 |
+--------+-----------------+----------------+

除了前面的示例,我们还选择了“费用”列的计数。

示例 3

考虑一个案例,我们需要根据性别进一步区分吸烟者和不吸烟者。为了完成这个任务,我们需要将性别列到 group by 语句中。

mysql> select smoker, sex, avg(charges), count(charges)-> from insurance-> group by smoker, sex;+--------+--------+-----------------+----------------+
| smoker | sex    | avg(charges)    | count(charges) |
+--------+--------+-----------------+----------------+
| no     | female |  8762.297299542 |            547 |
| no     | male   |  8087.204731276 |            517 |
| yes    | female | 30678.996276260 |            115 |
| yes    | male   | 33042.005975283 |            159 |
+--------+--------+-----------------+----------------+

实例 4

select 语句还允许过滤。例如,我们可以针对居住在东南部地区的人运行前面语句中的查询。

mysql> select smoker, sex, avg(charges), count(charges)-> from insurance-> where region = 'southeast'-> group by smoker, sex;+--------+--------+-----------------+----------------+
| smoker | sex    | avg(charges)    | count(charges) |
+--------+--------+-----------------+----------------+
| no     | female |  8440.205551942 |            139 |
| no     | male   |  7609.003586716 |            134 |
| yes    | female | 33034.820716388 |             36 |
| yes    | male   | 36029.839366545 |             55 |
+--------+--------+-----------------+----------------+

我们使用 where 语句来指定过滤条件。请务必注意,在选择查询中,where 语句必须写在 group by 语句之前。

实例 5

我们希望根据吸烟者和儿童列找到不同类别的平均 bmi 值,但只显示平均 bmi 值最高的 3 个类别。

这个示例包括对聚合值进行排序,这可以通过 order by 语句来完成。

mysql> select smoker, children, avg(bmi) -> from insurance-> group by smoker, children-> order by avg(bmi) desc-> limit 3;+--------+----------+------------+
| smoker | children | avg(bmi)   |
+--------+----------+------------+
| no     |        4 | 31.6754545 |
| yes    |        2 | 31.3041818 |
| no     |        2 | 30.8811622 |
+--------+----------+------------+

order by 语句根据给定的列按升序对行进行排序。我们可以通过在列名后使用 desc 关键字将其改为降序。

实例 6

让我们详细说明前面的例子。考虑这样一种情况,我们需要平均 bmi 值高于整体平均值的组。

一种方法是单独计算整体平均值,并将其作为过滤的条件。

mysql> select avg(bmi) from insurance;+------------+
| avg(bmi)   |
+------------+
| 30.6633969 |
+------------+ mysql> select smoker, children, avg(bmi)-> from insurance-> group by smoker, children-> having avg(bmi) > 30.6633969;+--------+----------+------------+
| smoker | children | avg(bmi)   |
+--------+----------+------------+
| no     |        2 | 30.8811622 |
| no     |        3 | 30.7384322 |
| no     |        4 | 31.6754545 |
| yes    |        1 | 30.8743443 |
| yes    |        2 | 31.3041818 |
+--------+----------+------------+

值得注意的是,当我们基于聚合值进行过滤时,我们使用 having 语句而不是 where 语句。

第二种选择是将这两个查询组合成一个嵌套查询。

mysql> select smoker, children, avg(bmi)-> from insurance-> group by smoker, children-> having avg(bmi) > (-> select avg(bmi) from insurance-> );+--------+----------+------------+
| smoker | children | avg(bmi)   |
+--------+----------+------------+
| no     |        2 | 30.8811622 |
| no     |        3 | 30.7384322 |
| no     |        4 | 31.6754545 |
| yes    |        1 | 30.8743443 |
| yes    |        2 | 31.3041818 |
+--------+----------+------------+

我更倾向于第二种选择,因为第一种更容易出错。

例 7

这个例子比上一个稍微复杂一点。我们不是只显示 bmi 值高于平均值的行,而是希望创建一个新列来指示一行是否高于平均值。

我们将在嵌套查询中使用 case-when 语句。

mysql> select smoke, children, avg(bmi),-> (case when avg(bmi) > (-> select avg(bmi) from insurance) then "more than avg"-> else "less than avg" end) as compared_to_avg-> from insurance-> group by smoker, children;

在显示结果之前,让我们试着理解查询中的每一步是做什么的。

我们选择了与上一个示例相似的三列。第四列是用 case-when 语句创建的。新列根据行的平均 bmi 值与总体平均 bmi 值的比较,取值“高于平均值”或“低于平均值”。

总体平均 bmi 值是通过使用 case-when 语句中的嵌套 select 语句来计算的。下面是这个查询的结果。

+--------+----------+------------+-----------------+
| smoker | children | avg(bmi)   | compared_to_avg |
+--------+----------+------------+-----------------+
| no     |        0 | 30.5519499 | less than avg   |
| no     |        1 | 30.5648859 | less than avg   |
| no     |        2 | 30.8811622 | more than avg   |
| no     |        3 | 30.7384322 | more than avg   |
| no     |        4 | 31.6754545 | more than avg   |
| no     |        5 | 30.2700000 | less than avg   |
| yes    |        0 | 30.5436957 | less than avg   |
| yes    |        1 | 30.8743443 | more than avg   |
| yes    |        2 | 31.3041818 | more than avg   |
| yes    |        3 | 30.5206410 | less than avg   |
| yes    |        4 | 29.3066667 | less than avg   |
| yes    |        5 | 18.3000000 | less than avg   |
+--------+----------+------------+-----------------+

结论

我们已经介绍了一些查询示例来展示 SQL 的数据分析能力。

我认为 SQL 是数据科学家或分析师的必备技能。我们至少应该知道如何查询关系数据库。在检索数据时执行数据转换和操作操作的复杂查询有可能节省内存。它们也减轻了以后需要完成的任务。

感谢您的阅读。如果您有任何反馈,请告诉我。

SQL 挑战:案例研究

原文:https://towardsdatascience.com/sql-challenge-case-study-7bd75eb864ac?source=collection_archive---------1-----------------------

顶级技术公司提出的 SQL 挑战的逐步演练

艾米丽·莫特在 Unsplash 上的照片

在的上一篇文章中,我谈到了如何使用框架来解决 SQL 挑战。今天我就来做一个这个挑战的演练,2021 年 2 月微软问的。在你继续阅读这篇文章之前,我强烈建议你尝试自己解决这个问题。你练习解决 SQL 问题越多,你就能越好地解决它们。好吧,让我们开始吧!

阅读提示

这个问题要求我们:

根据至少有 50%的事件发生在以下列表中的用户数,选择最受欢迎的 client_id:“收到视频通话”、“发送视频通话”、“收到语音通话”、“发送语音通话”。

在我开始研究这个问题之前,我想想象一下数据集中的信息是如何出现的。让我们运行一个简单的 SELECT 语句来检查存储在表 fact_events 中的数据。

SELECT *
FROM fact_events
LIMIT 10

该查询返回以下输出:

作者截图( StrataScratch 网站)

出于好奇,我想知道所提供的列表中的所有值是否都出现在数据集中:

SELECT event_type
FROM fact_events
WHERE event_type IN ('video call received','video call sent', 'voice call received','voice call sent')
GROUP BY event_type

作者图片

看起来只包括了 4 个值中的 2 个。尽管其他两个值没有出现在当前数据集中,但它们可能会在数据追加到表中时出现。你不能假设数据会保持不变。因此,让我们在过滤器中保留所有 4 个值。

太好了!我现在对我正在做的事情有了更好的了解。下一步是分析提示,以便我们可以提出一个策略来应对这一挑战。

笔和纸

对于这一步,我喜欢把我的想法写在一张纸上📝然而,请随意使用你的电脑、iPad 或其他最适合你的设备。这里,我们需要分析提示,以准确理解要求我们做什么。

作者图片

在这种情况下,提示似乎分为三个部分:

  1. 从上面的列表中找到发生了 50%事件的用户
  2. 计算步骤(1)中找到的用户数量
  3. 根据步骤(2)选择用户数量最多的客户端 id

我还会做如下假设:

  • 我不需要使用 customer_id 和 event_id 列
  • 时机并不重要
  • 我用给定列表中事件的数量除以所有事件的数量来计算 50%。
  • 我在统计用户总数,即使他们在列中出现多次。提示没有指定通过识别用户在数据集中出现的不同次数来选择最流行的 client_id。

在面试的这一点上,你应该花时间去理解挑战,并向面试官传达最初的想法。此外,如果你有任何问题,请随时向面试官寻求澄清。没有面试官在场的情况下,让我们进入下一步吧!

第一步

首先,让我们在下面的列表中获取具有 50%或更多事件的用户:“收到视频呼叫”、“发送视频呼叫”、“收到语音呼叫”和“发送语音呼叫”为了得到百分比,我们将在一个 CASE 语句中使用一键编码,将列表中的值返回为 1,其他值返回为 0。CASE 语句总和将是分子。分母是 event_type 列中所有元素的计数。

CAST(SUM(CASEWHEN event_type IN ('video call received','video call sent', 'voice call received','voice call sent')THEN 1ELSE 0END) AS FLOAT)  / COUNT(event_type)

接下来,我们将在 GROUP BY/HAVING 子句中包装条件。记住,我们需要用列表中至少 50%的事件对 user_id 进行过滤。为此,我们需要执行聚合,根据条件进行过滤,最后按 user_id 进行分组。

SELECT  user_id
FROM fact_events
GROUP BY user_id
HAVING CAST(SUM(CASEWHEN event_type IN ('video call received','video call sent', 'voice call received','voice call sent')THEN 1ELSE 0END) AS FLOAT)  / COUNT(event_type) >= 0.5

完成第一步后,我们得到以下输出:

作者图片

第二步

现在我们知道只有 2 个用户拥有列表中至少 50%的事件,让我们通过 client_id 和 user_id 找出用户的数量。

SELECT client_id, user_id, COUNT(user_id)
FROM fact_events
WHERE user_id in (SELECT  user_idFROM fact_eventsGROUP BY user_idHAVING CAST(SUM(CASEWHEN event_type IN ('video call received','video call sent', 'voice call received','voice call sent')THEN 1ELSE 0END) AS FLOAT)  / COUNT(event_type) >= 0.5)
GROUP BY client_id, user_id
ORDER BY COUNT (user_id) DESC

输出已经指向了我们的答案。请注意,client_id“桌面”有 7 + 4 = 11 个用户,而“移动”只有 3 个。

作者图片

第三步

在最后一步,我们将清理上面的输出,只返回拥有最多用户的 client_id。这里不需要对 SQL 脚本进行太多的修改。我没有按 client_id 和 user_id 分组,而是只按 client_id 分组。我还确保限制返回最多用户数的 client_id 的行数。如果不限制选择,在最终输出中还会返回“mobile”。

--final querySELECT client_id
FROM fact_events
WHERE user_id in (SELECT  user_idFROM fact_eventsGROUP BY user_idHAVING CAST(SUM(CASEWHEN event_type IN ('video call received','video call sent', 'voice call received','voice call sent')THEN 1ELSE 0END) AS FLOAT)  / COUNT(event_type) >= 0.5)
GROUP BY client_id
ORDER BY COUNT(user_id) DESC
LIMIT 1

最终输出:

作者图片

结论

我希望你喜欢这个演练!有大量的资源供你练习面试问题。一些最受欢迎的网站是 Leetcode、StrataScratch 和 InterviewQuery,但我相信你可以找到许多其他网站,为你赢得 DA 面试做好准备。别忘了在下面的评论中分享反馈或提出问题!

本帖最后编辑于 2021 年 10 月 30 日。这里表达的观点仅属于我自己,并不代表我的雇主的观点。

SQL —删除到另一个表中

原文:https://towardsdatascience.com/sql-delete-into-another-table-b5b946a42299?source=collection_archive---------15-----------------------

在一条语句中删除一个表中的记录,并将它们插入到另一个表中

把这个虎尾兰从一个锅里删除到另一个锅里!(图片由像素上的 Cottonbro 拍摄)

“我为什么需要这样的查询?”一个删除成语句是有利的,主要有两个原因:

  • 语句是原子;要么两者都发生,要么什么都不发生,即如果删除或插入失败,它将回滚更改。
  • 它更便宜:你的数据库只需要查找一次记录。或者,执行单独的插入和删除需要两次查找
  • 吹牛的权利:给你的老板和同事留下深刻印象

深信不疑?“给我看一些代码!”。好的,但是首先我们必须设置一些表来演示查询。让我们编码:

设置:创建一些包含数据的表

作为一个例子,我们假设我们有一个有很多流程的公司。每个进程都将其错误记录到一个名为 ErrorMessages 的表中,以便进行跟踪。一旦一个错误被解决,过期或者不再相关,我们可以从错误消息中删除它。不过,我们希望保存错误信息,这样我们以后就可以分析哪个进程失败得最多。为此,我们将创建 ErrorMessageBackup。我们的目标是将 ErrorMessages 删除到 ErrorMessageBackup 表中。让我们首先创建我们的表,并将一些数据插入到我们的错误消息中。

用一些记录创建我们的表

通过执行这些查询,我们看到 ErrorMessages 表的内容如下所示:

我们的错误消息表

执行我们的查询

这是魔法开始的地方。我们想从数据库中删除 404 错误。让我们检查一下我们的查询,然后再深入一点。

我们的删除查询

我们的目标是 Id 为 1 的错误消息。我们将它保存在@targetId 变量中。然后我们从 ErrorMessages 表中删除,过滤这个 Id。使用输出,我们可以访问已经删除的列。我们将这些列以及一个额外的列输出到 ErrorMessageBackup 表中。

就是这样!一个简单、安全的查询,允许您一次执行两个查询。

结论

通过这个查询,我们可以执行原子操作,从一个表中删除记录,并允许我们将它们插入到另一个表中。另请查看:

  • 更新到另一个标签页 le
  • 使用交易撤销查询
  • 在一条语句中插入、删除和更新
  • 更新选择一批记录
  • 版本控制你的数据库

这篇 文章向您展示了如何更新到表格中。编码快乐!

—迈克

页(page 的缩写)学生:比如我正在做的事情?跟我来!

SQL 数字营销分析

原文:https://towardsdatascience.com/sql-digital-marketing-analysis-be52c14e39aa?source=collection_archive---------8-----------------------

MySQL Workbench 中的一种方法,修改了应用于营销运营和常见分析请求的一些主要 SQL 查询

图片作者:gon alo GUI mares Gomes

介绍

SQL marketing analytics 设置的主要目标是帮助 mediumcompany 【虚构】的营销团队根据他们的表现和收入,计算出不同付费流量细分市场的合理报价。

在 MySQL Workbench 环境中,我们将使用 SQL 数据库 mediumcompany ,具体来说,两个表格“网站 _ 会话”和“订单”,帮助我们了解流量来自哪里,以及流量和转化率方面的表现如何。我们还将调整出价,以优化营销预算。

连同以下选定的 5 个常见任务,我们将编写简单的 SQL 查询,但功能强大到足以回答几个运营营销问题。我们将处理关于连接表、计算转换率、设备性能和数量趋势的例子。

From: CEO
主题:网站流量细分
日期:2012 年 4 月 12 日

我们已经上线快一个月了,我们开始创造销售。
能不能帮我了解一下我们
网站会话 的大头是从哪里来的,通过
我想看看
按来源、 和所指领域细分。

—思考 我们要统计按 utm_source、utm_campaign、utm_referer 分组的 website_session_id(会话数)。
这是我们的预期结果表:

图片作者。

要解决这个问题,我们只需要 website_sessions 的表。

图片作者。

*SELECT * FROM mediumcompany.website_sessions;*

图片作者。

*USE mediumcompany; -- set global use of the db.SELECT utm_source, utm_campaign, http_referer,
COUNT(DISTINCT website_session_id) AS nb_sessionsFROM website_sessionsWHERE created_at < ‘2012–04–12’GROUP BY utm_source, utm_campaign, http_refererORDER BY nb_sessions DESC;*

—提示 根据 SELECT 语句中的每个位置更快地分组和排序:

*SELECT 
(...)
GROUP BY 1, 2, 3
ORDER BY 4 DESC;*

—输出

图片作者。

—了解 从从网站 _ 会话表中选择来源、活动和推荐人开始,直到 2012 年 4 月 12 日。

*SELECT utm_source, utm_campaign, http_refererFROM website_sessionsWHERE created_at < “2012–04–12”*

图片作者。

然后计算使用 GROUP BY 子句的会话数,合计每个组合的值。按降序排列会话(最高在顶部)。

*SELECT (...)
COUNT(DISTINCT website_session_id) AS nb_sessions
(...)
GROUP BY 1, 2, 3ORDER BY 4 DESC;*

图片作者。

我们可以得出结论, gsearchnonbrand 在截至 2012 年 4 月 12 日的这段时间内,比任何其他活动或来源带来的流量(会话)都多。

发件人:市场总监
主题:g 搜索转换
日期:2012 年 4 月 14 日

看起来g search non brand是我们的主要流量来源,但我们需要了解这些会议是否在推动销售。
能否请您
计算一下从会话到订单的转化率(CVR) ?基于我们为点击支付的费用,我们需要至少 4%的 CVR 来让这些数字起作用。

—思考 我们想统计截止到 2012 年 4 月 14 日的 gsearch 和 nonbrand 的会话和订单总数。
关于转化率,我们简单的按时段划分订单。
这是我们的预期结果表:

图片作者。

为了解决这个问题,我们需要 website_sessions(我们已经从前面的作业中知道了)和 orders 表(见下文)。

图片作者。

*SELECT * FROM mediumcompany.orders;*

图片作者。

我们将执行“website_sessions”左连接“orders ”,因为我们希望看到 website_sessions 表中的所有 website_session_id,并查看 orders 表中何时有匹配的订单。我们将在 website_session_id 上同时加入。

*USE mediumcompany;SELECT
COUNT(DISTINCT ws.website_session_id) AS sessions,
COUNT(DISTINCT o.order_id) AS orders,
(COUNT(DISTINCT o.order_id) / COUNT(DISTINCT ws.website_session_id)) *100 AS session_to_order_conv_rateFROM website_sessions ws
LEFT JOIN orders o 
ON o.website_session_id = ws.website_session_idWHERE ws.created_at < ‘2012–04–14’ 
AND ws.utm_source = ‘gsearch’ 
AND ws.utm_campaign = ‘nonbrand’;*

—提示
2小数对‘session _ to _ order _ conv _ rate’进行舍入:

*SELECT
(...)
ROUND((COUNT(DISTINCT o.order_id) / COUNT(DISTINCT ws.website_session_id)) * 100 **,2**) AS session_to_order_conv_rate*

—输出

图片作者。

—了解 首先,通过在 website_session_id 上使用左连接来连接两个表(website_sessions 和 order_id),选择 website_session_id 和 order _ id。

*SELECT 
ws.website_session_id AS sessions,
o.order_id AS ordersFROM website_sessions ws
LEFT JOIN orders o 
ON o.website_session_id = ws.website_session_idWHERE ws.created_at < “2012–04–14”
AND ws.utm_source = ‘gsearch’ 
AND ws.utm_campaign = ‘nonbrand’;*

图片作者。

统计会话和订单的总数。

*SELECT 
COUNT(DISTINCT ws.website_session_id) AS sessions,
COUNT(DISTINCT o.order_id) AS orders
(...)*

图片作者。

通过将订单除以会话来计算转化率(比率为100)。*

*SELECT (...)
(COUNT(DISTINCT o.order_id) / COUNT(DISTINCT ws.website_session_id)) *100 AS session_to_order_conv_rate 
(...)-- Round **2** decimals:
ROUND((COUNT(DISTINCT o.order_id) / COUNT(DISTINCT ws.website_session_id)) *100 **,2**) AS session_to_order_conv_rate*

图片作者。

2.9%的转换率意味着 gsearch 非品牌出价没有像预期的那样推动销售,投资没有以最佳方式发挥作用。

发件人:市场总监
主题:g 搜索量趋势
日期:2012 年 5 月 10 日

基于您的转化率分析,我们于 2012 年 4 月 15 日投标g search non brand
能不能调出
gsearch 非品牌趋势交易量,按周细分, 看看竞价变化有没有导致交易量下降?

—思考
我们想按时间顺序按周统计 gsearch 非品牌会话,直到 2012 年 5 月 10 日。要做到这一点,按年和周将它们分组,找出每周的第一天或最小的一天。最后,看看交易量是否从 4 月 15 日开始下降。
这是我们的预期结果表:

图片作者。

*USE mediumcompany;SELECT 
MIN(DATE(created_at)) as week_started_at,
COUNT(DISTINCT website_session_id) AS sessionsFROM website_sessionsWHERE created_at < ‘2012–05–10’ 
AND utm_source = ‘gsearch’ 
AND utm_campaign = ‘nonbrand’GROUP BY YEAR(created_at), 
WEEK(created_at);*

—提示
我们实际上可以按 SELECT 语句中没有包含的列(年、周)进行分组。

—输出

图片作者。

—了解
先按年、周、日选择分组,统计场次。

*SELECT 
YEAR(created_at) AS yr,
WEEK(created_at) AS wk,
DATE(created_at) AS dt,
COUNT(DISTINCT website_session_id) AS sessions
(...)
GROUP BY 1, 2, 3*

图片作者。

找到一周中的第一天或最小的一天。因为我们继续按年和周对它们进行分组,但没有在 SELECT 子句中添加,所以日期的粒度将变得更粗。

*SELECT 
MIN(DATE(created_at)) as week_started_at,
COUNT(DISTINCT website_session_id) AS sessions
(...)
GROUP BY YEAR(created_at), WEEK(created_at);*

图片作者。

在 4 月 15 日之后,gsearch nonbrand 的流量明显下降。

*发自:市场总监
主题:*g 搜索设备级性能 日期:2012 年 5 月 11 日

前几天,我试图在手机上使用我们的网站,但体验并不好。
您能否按设备类型从会话到订单获取
转换率? 如果台式机的性能优于移动设备,我们或许可以提高台式机的价格,以获得更大的销量?

—思考 我们想统计截至 2012 年 5 月 11 日期间 gsearch 和 nonbrand 的会话和订单总数。
关于转化率,我们简单的按时段划分订单。然后,我们按设备类型分组。这是我们的预期结果表:

图片作者。

*USE mediumcompany;SELECT 
ws.device_type,
COUNT(DISTINCT ws.website_session_id) AS sessions,
COUNT(DISTINCT o.order_id) AS orders,
ROUND((COUNT(DISTINCT o.order_id) / COUNT(DISTINCT ws.website_session_id)) * 100, 2) AS session_to_order_conv_rtFROM website_sessions ws
LEFT JOIN orders o 
ON o.website_session_id = ws.website_session_idWHERE
ws.created_at < ‘2012–05–11’
AND ws.utm_source = ‘gsearch’
AND utm_campaign = ‘nonbrand’GROUP BY 1;*

—输出

图片作者。

—理解
从统计会话数、订单数,以及转化率开始。

*SELECT
COUNT(DISTINCT ws.website_session_id) AS sessions,
COUNT(DISTINCT o.order_id) AS orders,
(COUNT(DISTINCT o.order_id) / COUNT(DISTINCT ws.website_session_id)) *100 AS session_to_order_conv_rate
(...)*

图片作者。

呼叫和分组依据按设备细分。

*SELECT 
**ws.device_type,**
COUNT(DISTINCT ws.website_session_id) AS sessions,
COUNT(DISTINCT o.order_id) AS orders,
ROUND((COUNT(DISTINCT o.order_id) / COUNT(DISTINCT ws.website_session_id)) * 100, 2) AS session_to_order_conv_rt
(...)
**GROUP BY 1**;*

图片作者。

台式机的表现要好得多,因此我们应该提高这类设备的报价,以增加销量。

来自:市场总监
主题:g 搜索设备级趋势
日期:2012 年 6 月 9 日

在您对转化率进行设备层面的分析后,我们意识到台式机表现不错,因此我们在 2012 年 5 月 19 日对我们的 gsearch 非品牌台式机活动进行了竞价。
你能调出
桌面和移动的每周趋势 这样我们就能对销量产生影响了?
你可以用
2012–04–15*直到投标变更为基线。
*

—思考
我们希望选择并过滤设备,以统计设备类型为“桌面”或“移动”的次数,然后按年和周对时间序列进行转换和分组,以查看周开始日期。
最后,检查桌面会话的数量是否从 4 月 15 日到 6 月 9 日有所下降。这是我们的预期结果表:

图片作者。

*USE mediumcompany;SELECT 
MIN(DATE(created_at)) AS week_start_date,
COUNT(DISTINCT CASE WHEN device_type = ‘desktop’ THEN website_session_id ELSE NULL END) AS desktop_sessions,
COUNT(DISTINCT CASE WHEN device_type = ‘mobile’ THEN website_session_id ELSE NULL END) AS mobile_sessionsFROM website_sessionsWHERE created_at BETWEEN ‘2012–04–15’ AND ‘2012–06–09’ 
AND utm_source = ‘gsearch’ AND utm_campaign = ‘nonbrand’GROUP BY YEAR(created_at), WEEK(created_at)*

—输出

图片作者。

—了解 我们将从选择 device_type 开始,并转换年、周和日期。

*USE mediumcompany;SELECT
**device_type**,
**YEAR(created_at)** AS yr,
**WEEK(created_at)** AS wk,
**DATE(created_at)** AS dtFROM website_sessionsWHERE created_at BETWEEN ‘2012–04–15’ AND ‘2012–06–09’ AND utm_source = ‘gsearch’ AND utm_campaign = ‘nonbrand’;*

图片作者。

接下来,根据设备类型(桌面或移动)过滤网站会话 id。

*USE mediumcompany;SELECT
device_type,
YEAR(created_at) AS yr,
WEEK(created_at) AS wk,
DATE(created_at) AS dt,**CASE WHEN device_type = ‘desktop’ THEN website_session_id ELSE NULL END AS desktop_session_id,
CASE WHEN device_type = ‘mobile’ THEN website_session_id ELSE NULL END AS mobile_session_id**FROM website_sessionsWHERE created_at BETWEEN ‘2012–04–15’ AND ‘2012–06–09’ AND utm_source = ‘gsearch’ AND utm_campaign = ‘nonbrand’*

图片作者。

接下来,我们将删除“device_type”列,因为我们希望汇总并计算 device _ type 为“desktop”或“mobile”的次数。

不要忘记按年、周和日期分组,这样计数才有意义。

*USE mediumcompany;SELECT
YEAR(created_at) AS yr,
WEEK(created_at) AS wk,
DATE(created_at) AS dt,**COUNT(**CASE WHEN device_type = ‘desktop’ THEN website_session_id ELSE NULL END**)** AS desktop_sessions,
**COUNT(**CASE WHEN device_type = ‘mobile’ THEN website_session_id ELSE NULL END**)** AS mobile_sessionsFROM website_sessionsWHERE created_at BETWEEN ‘2012–04–15’ AND ‘2012–06–09’ AND utm_source = ‘gsearch’ AND utm_campaign = ‘nonbrand’**GROUP BY 1,2,3***

图片作者。

最后,从 SELECT 语句中删除“yr”和“wk ”(但仍按它们分组),以获得更大的粒度。

对于日期列“dt ”,将日期转换并设置为第一(最小)天,按年“yr”和周“wk”分组,以便最小天等于每个分组周的第一天。

不要忘记按年和周分组,这样计数才有意义。

*USE mediumcompany;SELECT
**MIN(**DATE(created_at)**)** AS week_start_date,
COUNT(CASE WHEN device_type = ‘desktop’ THEN website_session_id ELSE NULL END) AS desktop_session_id,
COUNT(CASE WHEN device_type = ‘mobile’ THEN website_session_id ELSE NULL END) AS mobile_session_idFROM website_sessionsWHERE created_at BETWEEN ‘2012–04–15’ AND ‘2012–06–09’ AND utm_source = ‘gsearch’ AND utm_campaign = ‘nonbrand’**GROUP BY YEAR(created_at), WEEK(created_at)***

图片作者。

回答问题“了解桌面和移动设备的每周趋势,这样我们就可以看到对销量的影响?你可以将 2012 年 4 月 15 日之前的出价作为基准。”,gsearch 非品牌桌面的流量从 4 月 15 日至今有所增加。

结论

流量来源分析旨在了解客户来自哪里,以及哪些渠道带来了最高质量的流量。

竞价优化分析是关于了解付费流量各个细分市场的价值,以便我们优化营销预算。

从营销人员的角度来看,目标是产生流量,为网站带来更多的流量,为企业赚更多的钱。

在分析方面,任务是分析流量来源和竞价优化,以了解付费流量各部分的价值,改善营销预算。

*</15-business-questions-about-mobile-marketing-campaigns-roas-return-on-ad-spend-ff636a8095b6>

感谢阅读。*

QuestDB 中时序数据的 SQL 扩展

原文:https://towardsdatascience.com/sql-extensions-for-time-series-data-in-questdb-f6b53acf3213?source=collection_archive---------43-----------------------

杰森·布里斯科在 Unsplash 上的照片

入门,数据工程

一个简短的实践教程,介绍如何在 QuestDB 中使用为时序数据构建的 SQL 扩展

在本教程中,您将学习 QuestDB SQL 扩展,它被证明对时间序列数据非常有用。通过使用一些示例数据集,您将了解指定的时间戳如何工作,以及如何使用扩展的 SQL 语法来编写对时间序列数据的查询。

介绍

传统上,SQL 被用于关系数据库和数据仓库。近年来,互联系统产生的数据量呈指数级增长,因此需要新的方法来存储和分析这些信息。因此,时间序列分析对于理解金融服务中的实时市场数据、来自物联网设备的传感器数据和应用指标至关重要。

时间序列数据量的爆炸式增长导致了专门数据库的开发,这些数据库旨在尽可能高效地接收和处理时间序列数据。QuestDB 实现了这一点,同时支持标准的 ANSI SQL 和用于时间序列分析的本地扩展。

除此之外,QuestDB 还通过实现隐式子句简化了语法。它还包括一个随机数据生成特性,这对于探索数据库的功能以及数据库测试非常有用。尽管有很多关于 QuestDB 的 SQL 方言的内容,但在本教程中,您将了解 SQL 扩展。

在整个教程中,我们将使用两个数据集。第一个是 2018 年 2 月纽约市的出租车出行数据。它包含关于乘客数量、旅程费用、小费金额和旅程开始日期的信息。你可以找到每位乘客的平均收入、纽约出租车司机的小费行为、一天中最繁忙的时间等等。

第二个数据集包含从 2010 年 1 月 1 日到 2020 年 1 月 1 日的 10 年的天气信息。该数据集包含温度、风速、降雨量、积雪深度、能见度等信息。你可以用这些数据来分析天气模式在很长一段时间内是如何出现的。你也可以比较不同年份同一时间的天气。首先,您可以使用以下 shell 脚本安装上述数据集:

安装示例数据集的脚本。

SQL 扩展

在最大程度地实现 ANSI SQL 的同时,QuestDB 引入了一些特定于时间序列的 SQL 扩展,以增强数据库用户和开发人员的性能和查询读写体验。让我们逐一研究所有的 SQL 扩展。

时间戳搜索

如果时序数据库没有提供跨时间搜索的方法,那么它就是不完整的。在 QuestDB 中,您可以按照时间间隔对表进行分区。每个分区将保存在磁盘上一组单独的文件中。为了提供类似关系数据库的优化,QuestDB 提供了时间戳搜索特性。

为了利用这个特性,一个表应该有一个指定的时间戳列。在创建表或在查询中创建临时子表时,可以将任何时间戳列标记为指定的时间戳列。指定的时间戳列强制表中的记录按时间顺序递增。因此,它隐式地实施了一个约束,该约束拒绝任何无序的插入。QuestDB 没有拒绝无序插入,而是已经开始接受无序的延迟记录。也可以使用普通的≥、≤、<、>操作符来执行时间戳搜索,但是它不如使用指定时间戳有效。

指定时间戳列的另一个好处是,它能够有效地使用 ***ASOF*** 连接,这些连接是在时间戳不完全匹配的情况下基于时间戳连接表的专用连接。使用从 ***ASOF*** 连接获取确定性结果的先决条件是表中的数据应该按时间排序。指定的时间戳列强制表中的时间顺序。

这两个样本数据集直接从一个 CSV 文件导入,并动态创建一个表。虽然您可以在导入数据时创建一个指定的时间戳,但是理解如何处理没有指定时间戳的表是很重要的。因此,现在让我们创建指定的时间戳,并按月份对这两个表进行分区。

CTAS 导入数据集的 SQL 脚本创建指定的时间戳列。

使用指定的时间戳搜索符号,可以简化基于时间戳的表上搜索。以下示例查询**weather** 数据集。在这个例子中,您可以看到同一个操作符可以用于查询许多不同的时间范围。**UNION**的第一部分将为您提供 2019 年全年的记录数,而**UNION**的第二部分将为您提供 2019 年 12 月的记录数,以此类推。

用于演示时间戳搜索符号的 SQL 查询。

[LATEST BY](https://questdb.io/docs/reference/sql/latest-by/)

这个 SQL 扩展通过时间戳查找给定键或键组合的最新条目。**LATEST BY**的功能类似于**FIRST****FIRST_VALUE**等功能。这在传统的关系数据库和数据仓库中是可用的。

在关系数据库中,您要么必须首先找出最近的时间戳,并使用子查询找到**passengerCount****farePlusTip** 金额,要么必须使用前面提到的分析函数之一,如**FIRST_VALUE**。QuestDB 通过创建一个新的子句来查找每组的最新记录,使数据库用户和开发人员的工作变得更加轻松。

在下面的示例中,您将看到,通过使用**LATEST BY** 子句,基于**passengerCount**,我们可以找出最近完成的行程的**farePlusTrip** 金额。

最新通过条款的例子。

样品通过

这是另一个对时间序列数据最理想的扩展,因为它允许基于时间戳对数据进行分组,而无需在 where 子句中显式提供时间戳范围。使用这个扩展,您可以将数据分成时间块。

在常规的 SQL 中,您需要结合使用**CASE WHEN**语句、**GROUP BY**子句和**WHERE**子句来获得类似的结果。在 QuestDB 中,**SAMPLE BY**完成了这个任务。要使用这个 SQL 扩展,您需要确保该表有一个指定的时间戳列。

在下面的示例中,您将看到使用**24h** 作为**SAMPLE BY**子句中的**SAMPLE_SIZE**,按天对数据进行采样或分组。根据接收到表中的数据的频率,您可能需要通过调整**SAMPLE_SIZE**来调整存储桶的大小。

在时序数据库中,粒度非常低是很常见的。因此,通常将数据按时间间隔分组,时间间隔从几秒到几年不等。以下是更多的例子,展示了如何针对不同的样本量使用**SAMPLE BY**子句:

使用不同时段的 SAMPLE BY 子句示例。

对常用 SQL 语法的更改

除了 SQL 扩展之外,还对常用的 SQL 语法进行了一些更改,以增强数据库用户体验。变更与GROUP BYHAVING条款相关。这样做的目的是简化查询的编写,提高可读性和 SQL 方言的易用性,同时减少 SQL 的冗长。

可选的分组依据子句

由于聚合函数在时序数据库中的广泛使用,QuestDB 隐式地对聚合结果进行分组,以使查询编写体验更好。虽然 QuestDB 支持**GROUP BY**关键字,但是无论您是否将它包含在查询中,都不会对结果集产生任何影响。让我们看一个例子:

在 QuestDB 中,使用 GROUP BY 子句是完全可选的。

隐含有条款

由于**HAVING**总是只与**GROUP BY**子句一起使用,因此**HAVING**子句自动隐含在上面提到的可选的**GROUP BY** 子句中。让我们来看一个例子:

不需要在 QuestDB 中使用 HAVING 子句。

可选的 SELECT * FROM 短语

QuestDB 更进一步,将**SELECT * FROM** 短语设为可选。当涉及到嵌套子查询时,这种方法确实有助于减少冗长性。在 QuestDB 中,只需写入表名并执行语句,就会充当一个**SELECT * FROM TABLE_NAME** 语句。请看下面的例子:

所有这些改进都有助于减少在像 QuestDB 这样的时序数据库中编写和维护查询所需的工作量。

结论

在本教程中,您了解了 QuestDB 如何通过编写专门为时序数据库设计的定制 SQL 扩展来支持 SQL 并增强性能和开发人员体验。您还了解了 QuestDB 的 SQL 方言中的一些语法变化。如果你有兴趣了解更多关于 QuestDB 的信息,请访问 QuestDB 的官方文档。

破解 SQL 面试问题:子查询 vs. CTE

原文:https://towardsdatascience.com/sql-for-data-analysis-subquery-vs-cte-699ef629d9eb?source=collection_archive---------1-----------------------

CTE 和子查询的区别

迈克·本纳在 Unsplash 上的照片

背景

SQL 是数据科学专业人员的必备技能。许多公司将其数据存储在关系数据库系统中,如 MySQL、PostgreSQL、MS SQL Server、SQLite。SQL 是我们与他们互动所需的编程语言。我们可以编写一个 SQL 查询来选择、过滤、转换、插入、更新和删除数据库中的底层数据。

SQL 不仅可以用来查询数据库,还可以用来进行数据分析。子查询和 CTE(公共表表达式)都是有用的工具,我们可以使用它们编写复杂的 SQL 查询来实现数据分析,就像其他数据科学工具一样,比如 Python 中的 Pandas 和 r 中的 dplyr

在本文中,我将解释子查询和 CTE 之间的异同。

子查询和 CTE 之间的相似性

我将使用一个常见的 SQL 访问问题来演示子查询和 CTE 之间的相似性。

SQL 问题:

Distance Per DollarYou’re given a dataset of Uber rides with the traveling distance ("distance_to_travel") and cost ("monetary_cost") for each ride. For each date, find the difference between the distance-per-dollar for that date and the average distance-per-dollar for that year-month. Distance-per-dollar is defined as the distance traveled divided by the cost of the ride.  
The output should include the year-month (YYYY-MM) and the average difference in distance-per-dollar for said year-month as an absolute value rounded to the 2nd decimal. You should also count both success and failed request_status as the distance and cost values are populated for all ride requests. Also, assume that all dates are unique in the dataset. Order your results by the earliest request date first.Source: [stratascratch.com](https://platform.stratascratch.com/coding-question?id=10302)

来源:stratascratch.com

以下是我们需要采取的步骤来解决这个问题:

使用子查询

顾名思义,子查询是中的查询。要使用子查询,我们只需添加括号并将查询放入其中。子查询创建的输出将充当临时表。在我们执行完整个语句后,临时子查询将自动消失。

对于这个 SQL 问题,如果我们使用子查询,我们将最终拥有多个嵌套子查询

  • 步骤 1:我们创建一个内部查询 (a)来计算每成本的每日距离,并使用TO_CHAR创建一个新列 year-month。
SELECT *,TO_CHAR(request_date::DATE, 'YYYY-MM') AS month(distance_to_travel/monetary_cost) AS daily_dis_to_cost
FROM uber_request_logs
  • 步骤 2:我们创建另一个嵌套子查询 (b)来使用窗口函数AVG(a.daily_dis_to_cost) OVER (PARTITION BY a.month)计算每月平均每成本距离。使用窗口函数,输出将具有与原始数据集相同的行数。
SELECT a.request_date,a.month,a.daily_dis_to_cost,AVG(a.daily_dis_to_cost) OVER(PARTITION BY a.month) AS monthly_dist_to_costFROM (SELECT *,TO_CHAR(request_date::DATE, 'YYYY-MM') AS month,(distance_to_travel/monetary_cost) AS daily_dis_to_costFROM uber_request_logs) aORDER BY request_date

中间 输出:

(作者创作)

  • 第 3 步和第 4 步,我们运行外部查询来实现聚合函数AVG(ABS(b.daily_dis_to_cost-b.monthly_dis_to_cost))GROUP BY b.month来计算年月级别上每日每成本距离和每月每成本距离之间的绝对差值的平均值。最后,我们按月排序输出。
SELECT b.month,ROUND(AVG(ABS(b.daily_dis_to_cost-b.monthly_dist_to_cost))::DECIMAL, 2) AS avg_diff
FROM(SELECT a.request_date,a.month,a.daily_dis_to_cost,AVG(a.daily_dis_to_cost) OVER(PARTITION BY a.month) AS monthly_dist_to_costFROM (SELECT *,TO_CHAR(request_date::DATE, 'YYYY-MM') AS month,(distance_to_travel/monetary_cost) AS daily_dis_to_costFROM uber_request_logs) aORDER BY request_date) b
GROUP BY b.month
ORDER BY b.month

期望输出:

(作者创作)

使用 CTE

一个 CTE ( 又名公共表表达式)是我们在编写主查询之前使用WITH子句创建的结果集。我们可以简单地将其输出用作临时表,就像子查询一样。类似于子查询,我们也可以创建多个 cte。

为了使用 CTE 解决上述 SQL 问题,我们将创建多个 cte(cte 1 和 cte2),它们相当于上面提到的子查询(a 和 b)。

WITH cte1 AS(SELECT *,TO_CHAR(request_date::DATE, 'YYYY-MM') AS month, (distance_to_travel/monetary_cost) AS daily_dis_to_costFROM uber_request_logs
),
cte2 AS(SELECT request_date,month,daily_dis_to_cost,AVG(daily_dis_to_cost) OVER(PARTITION BY month) AS monthly_dis_to_costFROM cte1ORDER BY request_date
)
SELECTmonth,ROUND(AVG(ABS(daily_dis_to_cost-monthly_dis_to_cost))::DECIMAL, 2) AS avg_diff
FROM cte2
GROUP BY month
ORDER BY month

子查询和 CTE 产生的结果完全相同。起初,我们可能认为这两个工具除了语法之外没有太多区别。在下一节中,我们将讨论这两种方法之间的区别。

子查询和 CTE 的区别

使用 CTE 的优势

CTE 可以重用:使用 CTE 的一个优点是 CTE 在设计上是可重用的。不必在需要使用子查询的每个地方都声明相同的子查询,可以使用 CTE 定义一次临时表,然后在需要时引用它。

CTE 更具可读性:CTE 的另一个优势是 CTE 比子查询更具可读性。因为 CTE 可以重用,所以使用 CTE 比使用子查询可以编写更少的代码。此外,人们倾向于按照顺序而不是嵌套的方式来遵循逻辑和想法。当你写一个查询时,使用 CTE 将一个复杂的查询分解成更小的部分会更容易。

cte 可以递归:CTE 可以递归运行,而子查询不能。这使得它特别适合于树形结构,在这种结构中,给定行中的信息是基于前面行中的信息的。递归特性可以用RECURSIVEUNION ALL实现。

WITH RECURSIVE [cte_name] (column, ...) AS ([non-recursive_term]
UNION ALL[recursive_term])
SELECT ... FROM [cte_name];

让我们创建斐波纳契数列(数列中的每个数字都是它前面两个数字的和。)使用 CTE 的递归特性。

WITH RECURSIVE fib(f1, f2) AS ( SELECT 0, 1 UNION ALLSELECT f2, (f1+f2) FROM fib ) 
SELECT f1 FROM fib LIMIT 10;

输出:

到目前为止,我们已经讨论了 CTE 和子查询之间的一些差异。看起来 CTE 比子查询有更多的优势。但是在下一节中,我们将讨论子查询的特性,这是使用 CTE 无法实现的。

使用子查询的优势

在 WHERE 子句中可以使用子查询:我们可以使用子查询返回值,然后在 WHERE 子句中使用它。在下面的示例中,我们希望返回工资高于平均工资的雇员。用一个计算平均工资的子查询很容易实现。

SELECTemployee_name, salary 
FROM sample
WHEREsalary > (SELECT AVG(salary) FROM sample)

子查询可以充当具有单个值的列:您也可以将子查询用作新列。唯一的约束是子查询必须只返回一个值。在下面的示例中,我们希望添加一个新列,其中包含平均工资。我们可以使用子查询来计算平均工资,然后将其包含在 SELECT 语句中。

SELECTemployee_name,salary,(SELECT AVG(salary) FROM sample) AS average_salary
FROM sample

子查询可以与相关子查询一起使用:与 CTE 不同,我们可以将内部子查询用作相关子查询。这意味着对于外部查询处理的每个记录,将执行一个内部查询。

在下面的例子中,我们想返回工资第二高的雇员。下面是我们将使用相关子查询来解决这个问题的方法。对于每个雇员(在外部查询(a)中),我们计算工资高于给定雇员的雇员数量(在内部查询(b)中)。如果只有一个其他雇员的工资比这个给定的雇员高,我们就保留这个雇员。

SELECT employee_name, salary
FROM sample a
WHERE 1 = (SELECT COUNT(DISTINCT(salary)) FROM sample b WHERE a.salary < b.salary)

但是请记住,因为外部查询每次处理每一行时都会计算内部子查询,所以它可能会很慢。

如果你想探索更多的 SQL 面试问题,请查看我的文章:

  • 综合 SQL 备忘单
  • 破解 SQL 面试问题的有用程序
  • 破解 SQL 面试问题:子查询 vs CTE
  • 破解 SQL 面试题:Join vs Case-When 语句
  • 破解 SQL 面试题:带分区的窗口函数-By
  • 破解 SQL 面试问题:Date_Part 函数
  • 破解 SQL 面试题:ROW_NUMBER、RANK 和 DENSE_RANK
  • 破解 SQL 面试问题:UNNEST,STRING_TO_ARRAY
  • 破解 SQL 面试问题:GENERATE_SERIES,STRING_AGG,SPLIT_PART
  • 破解 SQL 面试问题:自连接和非等同连接
  • 破解 SQL 面试问题:任意运算符
  • 破解 SQL 面试问题:子查询

感谢您的阅读!!!

如果你喜欢这篇文章,并且想请我喝杯咖啡,请点击这里。

您可以注册一个 会员 来解锁我的文章的全部访问权限,并且可以无限制访问介质上的所有内容。如果你想在我发表新文章时收到电子邮件通知,请订阅。

SQL —在一条语句中插入、删除和更新:用 MERGE 同步表

原文:https://towardsdatascience.com/sql-insert-delete-and-update-in-one-statement-sync-your-tables-with-merge-14814215d32c?source=collection_archive---------4-----------------------

用于合并两个表的灵活、安全和高效的解决方案

该进程一次只能处理一个表;我们必须合并(图片由罗杰·布拉德肖在像素上拍摄)

通过合并,你可以通过在一个语句中执行插入删除更新来“同步”两个表。合并远不止这些。它在比较和同步表格方面为您提供了广泛的选项。您甚至可以跟踪合并的输出。我们将在本文中深入探讨所有这些问题。合并的主要原因是:

  • 它是原子的;它或者执行所有命令(例如更新插入删除)或者不执行。如果其中一个命令失败,它将回滚所有内容。
  • 高效快速:SQL Server 需要比较记录一次
  • 非常灵活
  • 给你的经理和同事留下深刻印象

1 设置:创建一些包含数据的表

想象一下,我们有一家新成立的披萨公司。我们在一个名为 Pizzamenu 的表中记录披萨。每周我们都会收到一个新的数据集,其中包含最新的菜单。我们的目标是更新现有比萨饼的信息,添加新的和删除旧的。让我们首先创建表格并插入一些数据。

在这些查询中,您可以看到 PizzaMenu 和 NewPizzaMenu 几乎是相同的。唯一的区别是 PizzaMenu 包含一个额外的列,指示比萨饼是否在菜单上(IsDeleted)。

PizzaMenu 和 NewPizzaMenu

如你所见,新菜单不再包含夏威夷披萨。第一个比萨饼的名字和托诺的价格也是固定的。此外,还添加了两个比萨饼。让我们来看看如何做到这一点。

2 合并查询

合并查询比较目标表和源表。目标表是你真理的来源,这个来源将用来充实目标。看看下面的图表。

匹配源表和目标表(作者照片)

比较基于多个列进行,并提醒其中一个连接。把合并看作是一种连接;它比较两个表的记录。代替左、内和右连接,我们可以认为合并能够检测和处理匹配“左”、“内”和“右”的记录。查看我们的查询:

在合并中,我们将目标表与源表合并。目标标签是真相的来源。在查询的第一个块中,我们定义了我们的源(newPizzaMenu)和目标(PizzaMenu)将会是什么。就像在连接中一样,我们定义了哪些列值必须匹配才能匹配记录。在这种情况下是“PizzaId”和“Size”。

—新记录('右匹配')
第二个块处理当源中有新记录而目标中还没有时会发生什么。在我们的例子中,这是两种新披萨的情况:Calzone L 和 XL。在这个块中,我们决定将新记录插入到目标(PizzaMenu)中。

—匹配记录('内部匹配')
这部分代表文氏图的中心;源表和目标表之间的一种内部连接。在我们的例子中,匹配的记录是意大利香肠和 Tonno 比萨饼。在这一部分,我们更新了名称、价格和修改。

—已弃用的记录('左匹配')
处理目标中存在但源中不存在的记录。这表示存在于当前菜单中但不在新菜单中的记录,在我们的示例中,这由 pizza Hawaii 表示。我们决定不删除这条记录(它被注释掉了),而是更新目标(我们当前的菜单)中的 IsDeleted 列。

当我们执行查询时,我们看到:

我们合并的结果

正是我们想要的!请注意:

  • 红色:从“正确搭配”→披萨 tonno 从菜单中删除
  • 橙色:更新:修正了前两个比萨饼的价格和名称
  • 绿色:添加了底部的两条记录

3 灵活性

您不必更新出现在“匹配时”部分的记录;你想做什么都可以。合并只是检测记录是否匹配“左”、“右”或“内”。一些例子:

  • 仅更新匹配记录并插入新记录;不要删除任何东西
  • 如果记录出现在左匹配或右匹配中,则在错误表中插入记录
  • 从目标中删除所有匹配的记录,只插入正确匹配的(新)记录

例如,在选择匹配记录时也有一些灵活性。尝试下面的代码,该代码使用特殊限制更新匹配记录的名称。

  • 当匹配和目标匹配时。然后瞄准目标。Name = '被禁者'
  • 当与 target.stock 匹配时!= '夏威夷'
    然后瞄准。名称=来源。Name
    只有当新股票是正数时,才会更新股票

现在让我们真正地用一些额外的用途来弄脏我们的手(图片由 Cottonbro 在 Pexels 上提供)

额外收获:查看合并的输出

在执行合并时,我们还可以输出它所做的所有更改。为了演示这一点,我们稍微调整了一下查询。唯一的变化是,我们没有在正确的匹配更新,我们现在完全删除它。查看下面的查询。

对于最后 5 条记录,我们将输出我们执行了什么操作以及我们具体更改了什么:

我们合并的输出

在第一列中,您会看到我们执行了什么操作,然后,在第 2 到第 9 列中,您会看到关于我们插入的记录的信息。最后 8 列您将看到关于已删除记录的信息。显然,插入的记录没有“插入的”信息,删除的记录没有“插入的”信息。被更新的记录两者都有。

Bonus2:将合并的输出插入到日志记录表中

您可以使用合并的输出,并跟踪您的清单在所有新数据传递中的变化。让我们创建一个名为 PizzaMenuhistory 的表。

现在,我们可以在一个事务中合并两个表(更新、插入和删除),并将合并的输出删除到历史表中:

现在来看看这个小桌子披萨的历史:

我们 PizzaMenu 的历史变迁

结论

总之:merge 为合并两个表提供了灵活、安全和高效的解决方案。使用它的输出,你也可以很容易地跟踪所有的变化。另请查看:

  • 删除到另一个表中
  • 更新进入另一个标签页 le
  • 使用交易撤销查询
  • 更新选择一批记录
  • 版本控制你的数据库

现在前进,优化!!编码快乐!

—迈克

页(page 的缩写)学生:比如我正在做的事情?跟我来!

SQL 从另一个表中插入具有关联 id 的值

原文:https://towardsdatascience.com/sql-insert-values-with-joined-ids-from-another-table-83ff7f149296?source=collection_archive---------0-----------------------

在表中有 id 的值。一次性插入 id!

搜索一些身份证(图片由像素上的卢卡斯·佩泽塔提供)

当我们的表需要 id,但我们只有字符串时,该怎么办?假设我们是一家餐馆。我们将每一个菜单项及其成分存储到我们的数据库中,以便我们可以跟踪各种统计数据;多久点一道菜,哪些配料最受欢迎,例如

我们想在 recipes 表中存储一个新的食谱,但是有一个问题:这个表需要一个 ingredient_id,而不是我们当前拥有的 ingredient_name。当我们只有名字时,如何将这些 id 插入到食谱表中呢?

本文重点介绍如何通过使用独特的配料表,以最明智的方式存储这些食谱和配料。阅读本文后,您将:

  • 了解独特的表
  • 了解如何使用直通表
  • 能够在唯一表中只插入唯一值
  • 能够在从唯一表中检索 id 时插入到表中

首先,我们将设置一些表,然后进入查询。

设置

在这一节中,我们将定义数据库结构并创建所有的表。然后我们将在其中插入一些测试数据。在这之后,我们将进入如何插入连接的 id。注意,本文使用 Postgres,但是同样的技术适用于所有关系数据库

数据库结构

我们将定义 3 个表:一个保存配料,一个保存食谱,第三个表将这两个表连接在一起:

我们餐厅的数据库结构(图片由作者提供)

配料表

首先,我们将创建配料表并插入一些数据。查看 这篇文章 中的查询只能插入表中不存在的配料,确保我们永远不会出错。在本例中,我们将使用下面的“普通”插页:

所以我们的桌子看起来像这样:

我们新加入的配料

食谱表

该表将存储具有唯一 id 和名称的配方:

我们的食谱表

食谱配料表

该表将一个或多个配方与一种或多种配料联系起来。这种类型的表称为“直通表”;它充当具有多对多关系的两个表之间的中介。一个配方可以有多种配料,一种配料可以是多个配方的一部分。

我们将用 recipe_id 和 ingredient_id 将它们连接在一起:

T

现在我们所有的表都创建好了,让我们开始插入吧!

我们的桌子准备好了,让我们开始做饭吧!(图片由马腾·范·登·霍维尔在派克斯拍摄)

插入到我们的食谱配料表中

让我们着手处理手头的问题。我们希望创建一些记录,将 recipe-table 中的记录与配料表中的一些记录连接起来。我们需要配方标识和一些配料标识。问题是我们没有配料 id,只有配料名称。我们如何将数据插入配方配料表?现在我们假设我们知道 recipe _ id 值 1。下面的查询解决了这个问题:

让我们看一看并浏览一下查询。

  • 1 号线到 8 号线;这里我们定义了我们想要插入的数据集。我们知道成分的名称和数量。
  • 在第 9 行,我们说我们想要将 recipe_id、ingredient_id 和数量插入 recipe_ingredients 表中。这是我们的目标
  • 从我们定义为d的输入值(第 1 行到第 8 行)中选择数据。我们在配料表上连接这个数据集,匹配配料名称。
  • 这就是神奇之处:正如您所看到的,我们为 recipe_id 选择值 1,id 来自连接的配料表,数量来自输入数据集。

考虑配方 id

诀窍是将我们的输入数据定义为一个数据集,将它与所需的表连接起来,然后只插入 id。很简单,对吧?让我们用一个更难的例子来尝试一下,在这个例子中,我们不仅要连接配料 id,还要连接配方 id。

我们将前一个查询中的技巧执行了两次:我们将 recipes 表中 recipe 列上的 inputvalues 中的 recipe 连接起来。然后,我们从该表中提取配方 id,并将其插入到 recipe_ingredients 中。查看以下结果:

我们已经插入了与正确配方标识相对应的配料标识。完美!

结论

在本文中,我们已经学习了一些非常巧妙的技术:一个唯一的表、一个直通表和插入连接值。我希望能给他们一些启发。如果你有建议/澄清,请评论,以便我可以改进这篇文章。同时,看看我的其他关于各种编程相关主题的文章,比如:

  • 删除到另一个表中
  • 更新到另一个标签页 le
  • 在一条语句中插入、删除和更新
  • 更新选择一批记录
  • 保存上插
  • 插入唯一表格

编码快乐!

—迈克

页(page 的缩写)学生:比如我正在做的事情?跟我来!

SQL 仅在唯一表中插入唯一值

原文:https://towardsdatascience.com/sql-inserting-only-unique-values-in-a-unique-table-af2eb3b9890a?source=collection_archive---------15-----------------------

如果表格中不存在值,则将其插入表格中

我们只想要我们的表中的唯一值(图片由 Darius Cotoi 在 Unsplash 上提供)

想象我们是一家卖各种菜的餐厅。因为我们很聪明,我们在数据库中记录所有的成分。ingredients表将只包含唯一的成分,每个成分都有自己的 id。

本文的目标是创建一个查询,将表中没有的独特成分插入到表中。其他表可以引用 ingredient_id,这样我们就可以过滤和连接整数,这在大型数据集中要快得多。

1.创建表

首先,我们将创建包含配料的表。本文使用 Postgres,但同样的技术适用于所有关系数据库。成分表:

创建配料表

如您所见,我们有两列:

  • 身份证。因为这个 id 是一个串行主键,所以它会在插入时自动递增。我们不必将这个 id 传递给表,表会为我们创建它
  • 成分。成分名称。此列有一个unique约束;这意味着它只能包含唯一的值。

2 插入配料

下一步是编写一些代码,只将新的配料插入配料表:

让我们把意大利面的所有配料都放进桌子里

让我们浏览一下这段代码:

  1. 首先我们定义想要输入的数据,这发生在WITH 子句中。可以把它想象成一个名为inputvalues的表,列为ingredient。我们将 6 种成分放入输入值中。
  2. 我们将把输入值放到public.ingredients表中INSERT
  3. 我们将过滤插入WHERE输入值中的成分还不存在于public.ingredients中。

这些是一些新鲜的原料(图片由 Syd Wachs 在 unsplash 上提供)

执行此查询将输出下表:

我们新加入的配料

让我们用咖喱来试试这个查询:

执行后,Postgres 说INSERT 0 2这是个好消息!这意味着我们只向表中插入了两条记录,尽管我们传递了六条记录。我们只插入了表中没有的两种配料:paksoy 和大米。让我们检查一下表格的内容,看看它是否有效:

我们的配料表

3.保存插入

使用上面的查询,我们只能插入独特的成分。如果我们试图插入 Postgres 中已经存在的成分,就会返回一个错误。

INSERT INTO public.ingredients (ingredient) VALUES (‘paprika’);

会产生以下错误:

ERROR: duplicate key value violates unique constraint “ingredients_ingredient_key” DETAIL: Key (ingredient)=(paprika) already exists. SQL state: 23505

因此,即使我们忘记了使用这个令人惊叹的新查询,表中的惟一约束也可以防止重复值。厉害!

结论

通过这篇文章,我希望对如何处理惟一值和使用惟一约束有所启发。如果你有建议/澄清,请评论,以便我可以改进这篇文章。与此同时,请查看我的其他文章关于各种与编程相关的主题,例如:

  • 删除到另一个表格
  • 更新到另一个标签页 le
  • 在一条语句中插入、删除和更新
  • 更新选择一批记录
  • 保存上插

编码快乐!

—迈克

页(page 的缩写)学生:比如我正在做的事情?跟我来!

SQL 面试准备:下一个级别

原文:https://towardsdatascience.com/sql-interview-prep-the-next-level-e329a67086d?source=collection_archive---------25-----------------------

仅仅知道内连接和左连接之间的区别是不够的

照片由西格蒙德在 Unsplash 拍摄

作为数据科学和数据工程团队的成员,我也参加了很多面试。每个面试官都有自己的破局者;我的是 SQL。如果您对 SQL 有概念上的问题,那么这意味着您可能会无意中错误地提取数据。数据科学家需要正确提取数据,以便为他们的机器学习(ML)模型构建训练/测试集。数据工程师需要正确地提取数据,以便为利益相关者提供可靠的数据源。如果一个数据工程师犯了一个错误,这个错误可能会影响不止一个 ML 模型——它可能会传播到订阅该数据集的所有数据科学、分析、营销和财务团队。SQL 技能很重要。大多数初级受访者知道 SELECT *语句、GROUP BY 和所有不同类型的连接的基本原理,但是他们很难真正将数据概念化。让我们来看几个真实世界的例子,它们有望将你的 SQL 面试准备提高到一个新的水平。

挑战 1:占总数的百分比

假设这是你作为在线零售商 Pets R Us 的数据科学家的第一天。您有一个表 customers ,它跟踪所有的客户记录。每个客户记录包括一个客户 IDcust _ ID,以及该记录的创建日期 cre8_dt 。您能否编写一个 SQL 查询来计算在“2007-12-22”之前创建的客户记录的百分比?

作为一名数据科学家,我在第一天的时候会用这种类似黑客的方式来做这件事。首先,我会计算表中记录的总数:

-- calculate total number of records
SELECT COUNT(cust_id)
FROM customers

给了我们:

+----------------+
| COUNT(cust_id) |
+----------------+
|      1,210,000 |
+----------------+

然后,我会计算在“2007 年 12 月 22 日”之前创建的记录的数量:

-- calculate number of records created before 2007-12-22
SELECT COUNT(cust_id)
FROM customers
WHERE cre8_dt < '2007-12-22'

给了我们:

+----------------+
| COUNT(cust_id) |
+----------------+
|          3,400 |
+----------------+

然后我会用手机上的计算器将 3400 除以 1210000,再乘以 100 得到 0.28%。哈!明白了!

但是…不要在面试中那样做!这里有一个更简洁的方法:

SELECT cre8_flag,COUNT(*)*100.0/SUM(COUNT(*)) OVER() AS perc_of_tot
FROM (SELECT cust_id,CASE WHEN cre8_dt < '2007-12-22' THEN 'before'ELSE 'after'END AS cre8_flagFROM customers)
GROUP BY 1

给了我们:

+-----------+-------------+
| cre8_flag | perc_of_tot |
+-----------+-------------+
| before    |        0.28 |
| after     |       99.72 |
+-----------+-------------+

这里的技巧是首先使用一个子查询来创建一个标志,表示帐户是在我们的日期阈值之前还是之后创建的。然后,我们可以根据主查询中标志进行分组。当我们构建 perc_of_tot 列时,COUNT()计算每组中的帐户总数(一组用于cre 8 _ flag=‘之前’,另一组用于cre 8 _ flag=‘之后’)。因为我们没有在 OVER()之后包含 PARTITION BY,SUM(COUNT()对整个结果集的计数求和,在本例中,结果集由两个值组成:COUNT()表示“before”组,COUNT()表示“after”组。

挑战#2:获取蛋糕客户

你加入 Pets R Us 的数据科学团队才一周,你的经理希望你参与新的 Pupcake 收购活动。她要求您建立一个 ML 模型,预测从未购买过蛋糕的现有客户在未来四周内购买蛋糕的可能性。

让我们考虑一下,为了建立这个模型的训练/测试集,您需要提取哪些数据。首先,您需要汇总两个时间段的客户销售数据:(1)结果期(假设今天之前的四周)和(2)基线期(假设结果期之前的十二周)。

假设到目前为止,您已经成功提取了两个数据集。您将每个数据集写入一个表:

  1. cust_basel :在基线期内至少从 Pets R Us 购买了一件商品的客户列表。
  2. cust_pupcakes_basel :基线期内至少从 Pets R Us 购买过一个 pupcakes 的客户列表。

你真正需要的是在基线期内至少从 Pets R Us 购买了一件商品,但在此期间没有购买任何蛋糕的顾客名单。您能使用上面的两个表编写一个 SQL 查询来生成这样一个客户列表吗?

解决方案如下:

SELECT *
FROM cust_basel all
LEFT JOIN cust_pupcakes_basel cakes ON cakes.cust_id = all.cust_id
WHERE cakes.cust_id IS NULL

这是一个有用的技巧,我以前在一次面试中被要求这样做!在 Scala 和 PySpark 中,它实际上被称为左反连接。上面的左反联接将只返回左侧表( cust_basel )中不存在于右侧表( cust_pupcakes_basel )中的客户。

挑战#3:按客户对信用卡进行分组

让我们回到第一个例子中的 Pets R Us customers 表。我们已经检查了 cust_idcre8_dt 列。每条记录还有一个 full_nm 列和一个 email_adr 列。假设这个表在 cust_id 上是唯一的(每个 cust_id 只出现一次)。以下是表格中的五行示例:

+---------+------------+----------------+--------------------+
| cust_id |  cre8_dt   |    full_nm     |     email_adr      |
+---------+------------+----------------+--------------------+
|       1 | 2003-06-14 | Karen Gillan   | [kgil@mail.com](mailto:kgil@mail.com)      |
|       2 | 2020-12-19 | Dwayne Johnson | [therock@mail.com](mailto:therock@mail.com)   |
|       3 | 2007-10-28 | Kevin Hart     | [kevin@hart.com](mailto:kevin@hart.com)     |
|       4 | 2008-01-01 | Awkwafina      | [awkwa@fina.com](mailto:awkwa@fina.com)     |
|       5 | 2015-05-17 | Nick Jonas     | [jonasbro3@mail.com](mailto:jonasbro3@mail.com) |
+---------+------------+----------------+--------------------+

除了 customers 表,您还可以访问存储信用卡信息的 cards 表。每条记录都有一个 card_id (由 Pets R Us 内部生成,以确保每张卡都有一个唯一的 id)、持卡人的全名( full_nm )、卡的到期日期( exp_dt )、与卡相关联的 cust_id ,以及使用卡进行 Pets R Us 购买的最近日期( last_purch_dt )。

第一个问题(没有检查数据):你认为卡片表在 cust_id 上是唯一的吗?

回答:不能,因为每个客户可以有多张信用卡。

以下是纸牌牌桌的小样本:

+---------+------------+---------------+---------+-----------------+
| card_id |   exp_dt   | last_purch_dt | cust_id |    card_adr     |
+---------+------------+---------------+---------+-----------------+
|    1234 | 2099-08-31 | 2020-12-24    |       2 | 123 Rock St     |
|    2345 | 2019-01-31 | 2018-11-15    |       3 | 5 Main St       |
|    3456 | 2023-03-21 | 2020-07-13    |       2 | 123 Rock Street |
|    4567 | 2020-01-19 | 2019-12-31    |       2 | 21 Rock Ln      |
|    5678 | 2022-10-31 | 2020-12-05    |       4 | 345 Awka Blvd   |
+---------+------------+---------------+---------+-----------------+

第二个问题:您能否编写一个 SQL 查询来显示与每个 cust_id 相关联的活动信用卡的总数,以及该客户的全名和电子邮件地址?

以下是大多数入门级求职者的做法:

SELECT c.cust_id,c.full_nm,c.email_adr,nbr.nbr_cards
FROM customers c
INNER JOIN (-- nbr of cards per customerSELECT cust_id,COUNT(card_id) AS nbr_cardsFROM cardsWHERE exp_dt > Current_DateGROUP BY 1) nbr ON nbr.cust_id = c.cust_id

大多数人首先使用卡片表来计算每个 cust_id 的卡片数量。然后,他们将其放入一个子查询中,并将其连接回 customers 表,以获得 full_nmemail_adr

下面是一种更简洁的方法:

SELECT c.cust_id,c.full_nm,c.email_adr,COUNT(cards.card_id) AS nbr_cards
FROM customers c
INNER JOIN cards ON cards.cust_id = c.cust_id
WHERE card.exp_dt > Current_Date
GROUP BY 1,2,3

请注意,这里我们实际上不需要子查询!因为我们知道每个 cust_id 只有一个唯一的 full_nmemail_adr 与之相关联,所以我们可以通过 cust_idfull_nmemail_adr卡片表和组进行联接。然而,如果 customers 表在 cust_id 上不是唯一的,那么上面的查询可能会产生重复的 cust_id

跟进问题:如何检查客户表中是否有重复的客户标识

回答:

-- check for duplicates
SELECT COUNT(cust_id),COUNT(DISTINCT cust_id)
FROM customers

这里的要点是,您可以向 GROUP BY 子句添加额外的列,即使您不一定要根据它们进行分组。在上面的例子中,从技术上讲,我们只是按照 cust_id 进行分组,而 full_nmemail_adr 列只是客户属性。但是,我们可以将其他列放入组中,只要它们在 cust_id 上是唯一的。我以前在一次 SQL 访谈中看到过这方面的讨论!

挑战#4:为每个客户选择一个地址

这里还有另一个挑战:使用客户表,编写一个 SQL 查询,显示每个客户标识,他们的完整客户标识,和卡地址标识

首先,在深入 SQL 之前,让我们检查一下现有的数据,并从概念上考虑一下。向上滚动到上一部分,再看一下表中的几行。看起来道恩·强森( cust_id = 2)至少有三张信用卡,每张信用卡都有一个稍微不同的地址。因此,如果一些客户有多张信用卡,并且每张卡理论上可以有不同的地址与之相关联(或者甚至相同的地址格式不同),我们如何为每个客户选择一个确切的地址呢?

让我们仔细看看德韦恩的三张牌。一个已经过期,而另外两个仍然有效。活动卡具有相同的地址,但格式不同。过期的卡有完全不同的地址。看起来德韦恩最近可能搬家了,过期的卡反映了他的旧地址。

解决方案:

-- select one address per customer
SELECT cust_id,full_nm,card_adr
FROM (-- rank each customer's addressesSELECT c.cust_id,c.full_nm,cards.card_adr,row_number() OVER (PARTITION BY cust_id ORDER BY last_purch_dt DESC) AS rnk_nbrFROM customers cINNER JOIN cards ON cards.cust_id = c.cust_idWHERE card.exp_dt > Current_Date)
WHERE rnk_nbr = 1

我们首先需要做的是按照 cust_idcards 表中的所有记录进行分区。这意味着每个客户都有自己的分区,其中只包含他们自己的信用卡。我们告诉 SQL 如何在每个分区内对卡进行排序:通过 last_purch_dt 。最近的 last_purch_dt 的卡会排在最前面。一旦我们对所有的卡进行了排名,我们就只为每个客户选择排名第一的卡。这样,我们将只剩下每个客户一张卡(和一个地址)。

还有其他方法为每位顾客选择一张卡。例如,您可以将 exp_dt 考虑在内,或者使用某种交易表来确定每张卡在过去一个月中的使用频率。这里的要点是要知道你应该使用某种 PARTITION BY 语句。

此外,一定不要在面试中过多考虑任务;尽量在不增加不必要的复杂性的情况下解决问题。如果面试官想跟进的话,你可以在你的解决方案上再加一层。如果一个候选人是 SQL 高手,但却把简单的事情变得不必要的复杂,这仍然会给他们留下不好的印象。

挑战 5:黑色星期五销售——第一部分

您在 Pets R Us 的业务利益相关者希望分析过去 10 年中每个客户的黑色星期五销售额分布情况。首先,在我们一头扎进 SQL 之前,让我们想一想为了满足这个请求我们需要什么。

我们需要一个包含所有 Pets R Us 事务的表。每笔交易都将与一个客户和一个日期相关联。首先,我们应该只过滤黑色星期五的交易。然后我们可以按客户汇总销售额。

您可能只需要从过去 10 年中查找黑色星期五的日期,并将它们硬编码到您的 SQL 查询中。然而,在大多数情况下,通常最好避免硬编码。另外,这是一次采访,那有什么意思呢??

数据工程团队为数据科学团队提供了一个维度日期表 dim_date ,以帮助完成类似这样的日期筛选任务。以下是来自 dim_date 的一些示例行:

+------------+-------+--------+--------+
|  id_date   | id_yr | id_mth | id_dow |
+------------+-------+--------+--------+
| 2020-12-01 |  2020 |     12 |      2 |
| 2020-12-02 |  2020 |     12 |      3 |
| 2020-12-03 |  2020 |     12 |      4 |
| 2020-12-04 |  2020 |     12 |      5 |
| 2020-12-05 |  2020 |     12 |      6 |
| 2020-12-06 |  2020 |     12 |      7 |
| 2020-12-07 |  2020 |     12 |      1 |
| 2020-12-08 |  2020 |     12 |      2 |
| 2020-12-09 |  2020 |     12 |      3 |
+------------+-------+--------+--------+

问题:使用 dim_date ,您能编写一个 SQL 查询来生成 2010 年到 2020 年间所有黑色星期五的日期列表吗?

提示:使用感恩节发生在每年 11 月的第四个星期四的规则。

解决方案:

%sql
-- black friday
SELECT d.id_date AS thanksgiving,date_add(d.id_date, 1) AS black_friday
FROM (SELECT id_yr, id_mth, id_dow, id_date, row_number() OVER (PARTITION BY id_yr, id_mth, id_dow ORDER BY id_date) AS numocc FROM dim_dateWHERE id_yr BETWEEN 2010 AND 2020) d
WHERE (d.id_mth=11 AND d.id_dow=4 AND d.numocc=4)

输出应该如下所示:

+--------------+--------------+
| thanksgiving | black_friday |
+--------------+--------------+
| 2010-11-25   | 2010-11-26   |
| 2011-11-24   | 2011-11-25   |
| 2012-11-22   | 2012-11-23   |
| 2013-11-28   | 2013-11-29   |
| 2014-11-27   | 2014-11-28   |
| 2015-11-26   | 2015-11-27   |
| 2016-11-24   | 2016-11-25   |
| 2017-11-23   | 2017-11-24   |
| 2018-11-22   | 2018-11-23   |
| 2019-11-28   | 2019-11-29   |
| 2020-11-26   | 2020-11-27   |
+--------------+--------------+

后续问题:如果我们改变 PARTITION BY 子句中列名的顺序,会发生什么情况?

答:这不会改变输出,因为 dim_date 中的相同记录会被分组在一起,不管您是先按年、先按月还是先按星期几排序。但是,如果 ORDER BY 子句中有多个列,并且更改了它们的顺序,则输出可能会发生变化。

假设我们将黑色星期五查询的输出写到一个表中: blk_fri_dates 。现在我们已经有了一个表,其中包含了我们想要用来过滤事务的所有日期,让我们来看看来自 transactions 表的几条记录,这样我们就可以对事务数据有一个大致的了解:

+---------+------------+---------+--------+
| tran_id |  id_date   | card_id |  amt   |
+---------+------------+---------+--------+
|    1111 | 2020-11-10 |    1234 |  50.99 |
|    1122 | 2020-11-12 |    5678 |  32.34 |
|    1133 | 2020-11-13 |    0123 |  24.78 |
|    1144 | 2020-11-15 |    1234 |  19.85 |
|    1155 | 2010-11-15 |    9012 | 112.00 |
+---------+------------+---------+--------+

问题:使用 transactions 表、 blk_fri_dates 表以及我们上面讨论过的任何其他 Pets R Us 表,对 2010 年到 2020 年之间每个黑色星期五每个客户的总销售额进行求和。

解决方案:

-- customer sales for Black Friday 2010-2020
SELECT c.cust_id,t.id_date,SUM(t.amt) AS tot_sales
FROM transactions t
INNER JOIN blk_fri_dates bf ON bf.black_friday = t.id_date
INNER JOIN cards c ON c.card_id = t.card_id 
GROUP BY 1,2

首先,我们从事务表开始。我们将事务表内部连接到 blk_fri_dates 表,因为我们只想获取发生在黑色星期五的事务。我们还必须将交易表内部连接到表,因为我们需要为每个交易查找哪个 cust_id 对应哪个 card_id

以下是几行示例输出:

+---------+------------+-----------+
| cust_id |  id_date   | tot_sales |
+---------+------------+-----------+
|       1 | 2010-11-26 |     87.54 |
|       1 | 2013-11-29 |     45.78 |
|       5 | 2015-11-27 |     32.18 |
|       5 | 2016-11-25 |     19.98 |
|       3 | 2020-11-27 |     40.35 |
+---------+------------+-----------+

后续问题:如果 blk_fri_dates 表中有重复的行,那么上面的输出会怎么样?

回答:想象一下 blk_fri_dates 表错误地包含了 2020 年的两行:

+--------------+--------------+
| thanksgiving | black_friday |
+--------------+--------------+
| 2010-11-25   | 2010-11-26   |
| 2011-11-24   | 2011-11-25   |
| 2012-11-22   | 2012-11-23   |
| 2013-11-28   | 2013-11-29   |
| 2014-11-27   | 2014-11-28   |
| 2015-11-26   | 2015-11-27   |
| 2016-11-24   | 2016-11-25   |
| 2017-11-23   | 2017-11-24   |
| 2018-11-22   | 2018-11-23   |
| 2019-11-28   | 2019-11-29   |
| 2020-11-26   | 2020-11-27   |
| 2020-11-26   | 2020-11-27   |
+--------------+--------------+

在这种情况下,如果我们将该表内连接到交易,那么从 2020 年开始的每个黑色星期五交易都将被重复计算。

后续问题:如果 blk_fri_dates 有重复的行,如何调整上面的 SQL 查询,以便仍然获得正确的每个客户的黑色星期五销售额?

回答:

-- customer sales for Black Friday 2010-2020
SELECT c.cust_id,t.id_date,SUM(t.amt) AS tot_sales
FROM transactions t
INNER JOIN (-- use a sub-query!SELECT DISTINCT black_fridayFROM blk_fri_dates) bf ON bf.black_friday = t.id_date
INNER JOIN cards c ON c.card_id = t.card_id 
GROUP BY 1,2

额外收获:我们可以通过在 blk_fri_dates 表上使用左半连接而不是内连接来进一步优化这个查询:

-- customer sales for Black Friday 2010-2020
SELECT c.cust_id,t.id_date,SUM(t.amt) AS tot_sales
FROM transactions t
LEFT SEMI JOIN blk_fri_dates bf ON bf.black_friday = t.id_date
INNER JOIN cards c ON c.card_id = t.card_id 
GROUP BY 1,2

上面的左半连接将只选择来自事务(左侧表格)的事务,其中在 blk_fri_dates (右侧表格)中的 id_date 列上有一个或多个匹配。左半连接的优点是,如果在 blk_fri_dates 中有重复的行,左半连接不会像内连接那样重复事务。

左半连接的执行速度通常比内连接快,因为它只能返回左侧表中的列。因为在这个查询中我们不需要从 blk_fri_dates 中选择任何列,所以左半连接是一个很好的选择。下次你需要做一些过滤的时候,考虑一个左半连接!

挑战#6:黑色星期五销售——第二部分

让我们重温一下上面的查询,它汇总了 2010 年到 2020 年间每个黑色星期五每个客户的总销售额。想想你的业务利益相关者最初的要求:“过去 10 年每个客户的黑色星期五销售额的分布。”上述解决方案是否完全满足此目的?

提示:如果客户在交易表中有 2010-2014 黑色星期五和 2016-2020 黑色星期五的关联交易,但没有 2015 黑色星期五的关联交易,会发生什么情况?

回答:如果一个客户(假设 cust_id =20)在 2015 年黑色星期五没有进行任何交易,那么该客户黑色星期五总销售额的输出行将如下所示:

+---------+------------+-----------+
| cust_id |  id_date   | tot_sales |
+---------+------------+-----------+
|      20 | 2010-11-26 |     87.54 |
|      20 | 2011-11-25 |     15.14 |
|      20 | 2012-11-23 |     42.71 |
|      20 | 2013-11-29 |     45.78 |
|      20 | 2014-11-28 |      8.07 |
|      20 | 2016-11-25 |     32.18 |
|      20 | 2017-11-24 |     19.98 |
|      20 | 2018-11-23 |     38.16 |
|      20 | 2019-11-29 |     19.57 |
|      20 | 2020-11-27 |     40.35 |
+---------+------------+-----------+

注意到 2015 年黑色星期五根本没有记录吗?我们真正想要的是这样的东西:

+---------+------------+-----------+
| cust_id |  id_date   | tot_sales |
+---------+------------+-----------+
|      20 | 2010-11-26 |     87.54 |
|      20 | 2011-11-25 |     15.14 |
|      20 | 2012-11-23 |     42.71 |
|      20 | 2013-11-29 |     45.78 |
|      20 | 2014-11-28 |      8.07 |
|      20 | 2015-11-27 |      0.00 |
|      20 | 2016-11-25 |     32.18 |
|      20 | 2017-11-24 |     19.98 |
|      20 | 2018-11-23 |     38.16 |
|      20 | 2019-11-29 |     19.57 |
|      20 | 2020-11-27 |     40.35 |
+---------+------------+-----------+

当你在面试中遇到这样的问题时,几乎可以肯定这是一个左连接问题。黑色星期五的总销售额将出现在右侧。但是左手边应该放什么呢?

在左边,我们需要顾客和黑色星期五的所有可能的组合。换句话说,我们需要编写一个 SQL 查询来生成每一个可能的 cust_idblack_friday 对。

首先,为每个独特的客户创造产出:

-- every unique customer
SELECT cust_id
FROM customers

其次,利用 2010 年至 2020 年间每个独特的黑色星期五创造产出:

-- every unique Black Friday
SELECT black_friday
FROM blk_fri_dates

第三,用交叉连接将它们相乘:

-- every possible unique customer, Black Friday pair
SELECT c.cust_id,bf.black_friday
FROM customers c
CROSS JOIN blk_fri_dates bf

注意:记住交叉连接的计算开销非常大,所以要小心使用。

问题:使用我们刚刚编写的查询来生成每个可能的唯一客户,黑色星期五对,以及我们在上一节中编写的查询来合计每个客户的黑色星期五总销售额,编写一个 SQL 查询来输出每个客户每年的黑色星期五总销售额。如果客户在任何一年的黑色星期五都没有交易,那么该年的交易应该显示为 0.00 美元。

WITH bf_sales AS (
-- customer sales for Black Friday 2010-2020
SELECT c.cust_id,t.id_date,SUM(t.amt) AS tot_sales
FROM transactions t
LEFT SEMI JOIN blk_fri_dates bf ON bf.black_friday = t.id_date
INNER JOIN cards c ON c.card_id = t.card_id 
GROUP BY 1,2
)
SELECT c_bf.cust_id,c_bf.black_friday,COALESCE(bf_sales.tot_sales, 0.0) AS tot_sales
FROM (-- every possible customer, BF pairSELECT c.cust_id,bf.black_fridayFROM customersCROSS JOIN blk_fri_dates) c_bf
LEFT JOIN bf_sales ON bf_sales.cust_id = c_bf.cust_idAND bf_sales.id_date = c_bf.black_friday

我使用了一个 WITH 子句来构建一个临时表,因为我不想让我的 SQL 查询因为太多的子查询而变得混乱。注意,我从左边的表中选择了 cust_idblack_friday 列。这一点很重要:如果您意外地从右边的表中选择了 cust_idblack_friday ,那么您可能会得到 nulls,这就否定了左连接的目的。我使用了一个 COALESCE 来用 0.0 替换左连接中的任何 nulls 值。

还要注意,我喜欢在每个子查询的顶部添加定性注释,以跟踪我的代码在做什么。我见过有人使用两层、三层或更多层的子查询,而不包含任何注释。我觉得这很难读懂。我建议加入注释,以促进协作,并使团队成员更容易审查您的代码。

做完这些工作后,让我们后退一步。实际上,在我们将一堆直方图放在一起并发送给我们的利益相关者之前,我们还需要考虑一件事。

提示:仔细查看一些客户记录:

+---------+------------+----------------+--------------------+
| cust_id |  cre8_dt   |    full_nm     |     email_adr      |
+---------+------------+----------------+--------------------+
|       1 | 2003-06-14 | Karen Gillan   | [kgil@mail.com](mailto:kgil@mail.com)      |
|       2 | 2020-12-19 | Dwayne Johnson | [therock@mail.com](mailto:therock@mail.com)   |
|       3 | 2007-10-28 | Kevin Hart     | [kevin@hart.com](mailto:kevin@hart.com)     |
|       4 | 2008-01-01 | Awkwafina      | [awkwa@fina.com](mailto:awkwa@fina.com)     |
|       5 | 2015-05-17 | Nick Jonas     | [jonasbro3@mail.com](mailto:jonasbro3@mail.com) |
+---------+------------+----------------+--------------------+

在 2010 年至 2020 年间的每个黑色星期五,只有凯伦·吉兰的宠物反斗城账户存在。道恩·强森的账户是在 2020 年黑色星期五之后才创建的。道恩·强森的黑色星期五零销售额应该被列入 2010 年的柱状图吗?对于 2015 年?2020 年?不,不,不。奥卡菲娜的黑色星期五零销售额应该被列入 2007 年的柱状图吗?不是。2008 年的?是的。

在本文中,我们不会专门讨论如何解决上述问题,而是考虑如何解决这个问题,无论是通过编程还是使用 SQL。对此有多种正确的方法!如果考虑多种方法,哪种方法的性能最好?如果你只想到一种方法,你如何优化它?在面试的最后,这可能是一个很好的定性讨论。

如您所见,提取数据会变得非常棘手。对手头的任务进行定性思考,并预测如何规避数据中的任何古怪之处或已知缺陷,这一点极其重要。

最后一条建议:

了解你的数据。

—我见过的所有数据工程师

如果你在面试,不要害怕问关于手头数据集的问题。这向面试官展示了你注重细节,考虑周到,在工作中不太可能出错。

勇往直前去面试吧!

上面的例子是实际面试问题的变体,我见过很多面试者纠结于这些问题。有时候我的团队真的很喜欢一个候选人,但是他们的 SQL 太落后了,我们担心他们会花太多时间来适应。

还有,一定要毫不留情地表现出积极的态度。这意味着,即使事情变得艰难,你认为你已经表现得不能再差了,或者你认为面试问题完全不公平,保持积极的态度!作为一名面试官,我不只是在寻找正确的答案。我在寻找一个专业、合作、坦率、友善的人。你想雇佣谁加入你的团队:布雷弗斯通还是范佩尔??

我希望这篇文章有助于提升你的 SQL 面试准备水平。走上前,向你的下一位面试官展示你已经准备好加入他们的团队。

祝你好运!

你必须准备的 SQL 面试问题:终极指南

原文:https://towardsdatascience.com/sql-interview-questions-you-must-prepare-the-ultimate-guide-12f0546bfb8f?source=collection_archive---------5-----------------------

您是否想知道会被问到哪些 SQL 面试问题?这本终极指南将带你了解各种数据职位的 SQL 面试问题,以及迎接下一次 SQL 面试的技巧。

作者创造的形象

SQL 是任何有抱负的数据科学家的必备工具。在本文中,我们提供了一个大纲,帮助您学习、准备和赢得下一次数据科学职位的 SQL 面试。我们将探讨为什么 SQL 被如此广泛地使用,然后为您提供每个角色所需的 SQL 技能的细分,即数据分析师、数据科学家、机器学习工程师等。此外,我们还为您提供了来自 StrataScratch 平台的真实面试示例,展示了其中的一些技能,并为您提供了一个循序渐进的学习指南,让您即使不太熟悉 SQL 概念也能熟练掌握 SQL,并获得理想的工作。

因此,让我们从为什么 SQL 在数据科学领域如此广泛地使用开始。

SQL 的流行

SQL 流行的最大原因之一是数据存储的表格格式。很容易将数据库想象成具有数百万行和数百万列的大型电子表格。SQL 允许用户快速操作这些表格来访问信息,并以最常见的格式、表格和相关的图形和可视化方式呈现结果。

作者创造的形象

虽然 MongoDB、Cassandra 等非 SQL 数据库随着大数据和实时应用程序的需求以及非结构化数据的日益流行而受到青睐,但 SQL 数据库仍然占据着十大最受欢迎的数据库引擎的前七名。事实上,No-SQL 数据库已经将自己定位为 No just SQL,以突出它们对 SQL 的支持,从而有助于增加它们在已经使用传统的基于 SQL 的数据库的组织中的接受度。

SQL 流行的另一个原因是易于使用。语法非常容易理解,任何人都能很快掌握。很容易理解下面的语句是做什么的。

SELECT name FROM EMPLOYEES;SELECT name FROM EMPLOYEESWHERE age >= 35;SELECT state, AVG(age) as average_age FROM EMPLOYEESWHERE age >= 35GROUP BY state;

学习 SQL 非常容易,对于招聘经理来说,这是一个方便而强大的工具,可以用来评估潜在员工的推理、编码和数据科学能力。我们对 903 个数据科学面试问题的分析显示,最突出的面试问题类别是编码。它几乎占了所有面试问题的三分之一,因为大多数情况下,在编码部分测试的最突出的概念是编写 SQL 查询。

作者创造的形象

在这些顶级公司,基于 SQL 的面试问题比基于 Python 或 R 的面试问题要多。随着对数据管理技能的日益关注,学习 SQL 是不会错的。那么,在培养数据科学和相关职位的 SQL 技能时,应该专注于什么呢?

按角色划分的数据科学中的 SQL 用法

并非数据科学中的每个职位都使用相同的 SQL 概念。虽然一些角色将主要关注查询和查询优化,但其他角色将倾向于数据架构和 ETL 过程。我们可以将数据科学访谈中问到的 SQL 命令分为以下几类。

  • 数据定义语言(DDL)
    -创建
    -更改
    -删除
    -重命名
    -截断
    -注释
  • 数据查询语言(DQL)
    -选择
  • 数据操作语言(DML)
    -插入
    -更新
    -删除
    -合并
    -调用
    -解释计划
    -锁表
  • 数据控制语言
    -授权
    -撤销

数据分析师或数据科学家通常会使用 SELECT 语句和相关的高级概念,如子查询、分组/汇总、窗口函数和 cte。如果您是一名分析师,您可能已经在使用 SQL 了。不管你是数据分析师、报告分析师、产品分析师,甚至是财务分析师。你的职位通常需要处理原始数据,并利用你的技能为管理层和其他利益相关者提供决策洞察力。

数据工程师与数据科学家和数据分析师密切合作。他们的主要任务是构建和维护数据架构,并创建算法以使数据科学家和分析师更容易地访问数据。通过做他们的工作,他们也在帮助数据科学家做他们的工作。作为一名数据工程师,你不能回避的是了解 SQL,与数据分析师和科学家相比,SQL 通常处于高级水平。要成为一名优秀的数据工程师,你需要成为一名 SQL 高手。这就是为什么你会被问到一些与数据分析师和科学家相同的问题。除了 DQL 命令之外,您还应该精通数据库建模,因此您还应该详细了解 DDL、DML 和 DCL 命令。

机器学习工程师是混合专家,他们正在弥合数据科学家和软件工程师之间的差距。由于他们是这两个职位之间的桥梁,他们需要拥有来自两个世界的特定技能。他们使用这些技能来设计、构建和维护机器学习系统。为了实现这一点,他们通常使用几种技能:

  • 统计数字
  • 数学
  • 数据挖掘技术
  • 数据分析
  • 预测分析

机器学习工程师通常需要了解 Python 和/或 r。然而,由于机器学习工程师和数据科学家有一些共同的技能(数据挖掘、数据分析),因此机器学习工程师通常还需要了解 SQL。这样,他们就能够根据自己的需要进行分析和使用数据。他们不需要某个中间人来提取数据并为他们进行分析。例如,这里有一个来自优步的关于创建一个简单预测模型的问题。​

截图来自 StrataScratch

为一个新的指标开发一个天真的预测:“每美元的距离”。在我们的数据集中,每美元距离被定义为(旅行距离/货币成本)。计算指标并测量其准确性。

算法概要:

每月对“旅行距离”和“货币成本”值求和,并计算“每美元的距离”。这是当月的实际值。
接下来,填充每个月的预测值。要实现这一点,取上个月的值。
现在,我们有了实际值和预测值。这是我们天真的预测。现在通过计算均方根误差(RMSE)来评估我们的模型。RMSE 是实际值和预测值之间的均方差的平方根。报告 RMSE,四舍五入到小数点后第二位。

软件工程师的 SQL 面试通常也是各种技能的交汇点,如计算机科学、工程和数学。他们使用这些不同的规程来设计、编写、测试和维护软件。像机器学习工程师一样,他们也需要与各个部门和客户合作。这就是为什么他们也需要高水平的业务和技术技能,尽管他们的主要任务不是数据分析。这是为什么呢?当他们构建界面时,他们必须依赖后台运行的数据库。在实施新软件的过程中,他们需要使用这些数据库并分析数据。

您可以参考这篇关于数据科学中各种角色和职责的精彩文章,了解更多信息。本文分解了您每天可能会遇到的每个角色的最常见需求。

让我们看看在数据科学访谈中测试最频繁的 SQL 领域。

SQL 面试问题中测试的技术概念

由于 SQL 角色差别很大,测试领域也有很大不同。根据您申请的职位类型和组织,您可能会遇到一种或多种 SQL 数据科学面试问题类型

  • 基本 SQL 概念
  • SQL 基础面试问题
  • SQL 聚合面试问题
  • 开放式 SQL 面试问题
  • 数据转换面试问题
  • 数据库建模面试问题
  • 软件工程 SQL 面试问题

让我们分别看一下这些 SQL 面试问题类型,以了解它们之间的相似性和区别。

基本 SQL 概念

这些问题的重点在于您对基本数据库和 SQL 术语的理解和知识。不需要编写任何代码。这些是人们应该熟悉的宽泛概念和一些例题。

  • 关于 SQL
    的一般信息——什么是 SQL?
    -SQL 有哪些不同的风格?
    ——什么是主键?
  • 关系数据库及其工作原理?
    -有哪些顶级的 RDBMS 引擎?
    -RDBMS 与非 SQL 数据库有何不同?
  • SQL 子语言及其主要关键字
    ——DDL、DCL、DML 分别代表什么?
    -给出每个命令的例子。
  • 数据类型及 SQL 如何处理(包括空格和空值)
    -SQL 中常见的数据类型有哪些?
    -SQLite 数据库支持日期时间对象吗?
  • 属性约束
    -什么是属性约束,并解释它们?
  • 连接类型
    -内连接和左外连接有什么区别?
    -UNION 和 UNION ALL 有什么区别?
  • 聚合和汇总函数
    -什么时候应该在子查询上使用 CTE?
    -什么是窗口功能?
  • 各种 SQL 函数的知识
    -WHERE 和 HAVING 有什么区别?一个应该使用另一个的例子
    -COALESCE 函数做什么?

虽然在最初的访谈中可能找不到这些问题,但这些问题可能会作为您提交的编码解决方案的后续问题。例如,如果您在解决方案中使用了内部联接,您可能会被问到为什么不使用左联接,或者如果使用了会发生什么情况?

SQL 基础面试问题

SQL 基础面试问题要求你将上述一些理论概念付诸实践。这并不意味着这些问题一定是编码问题。它们也可以是描述性的。但是如果你想写一个代码,它们通常涵盖了你需要知道的概念。这些概念是:

  • 使用 SUM()、COUNT()、AVG()、MIN()、MAX()和其他聚合函数
  • 分组依据
  • CASE WHEN 语句
  • 哪里有
  • 连接
  • 联合和联合所有

问题示例

这里有一个来自邮政 SQL 数据科学采访的例子

“有多少客户下了订单,平均订单金额是多少?”

解决方案:要回答这个 SQL 面试问题,您必须使用 postmates_orders 表。

SELECT count(DISTINCT customer_id), avg(amount) FROM postmates_orders

如你所见,这真的很简单。它测试聚合函数 COUNT()和 AVG(),还需要了解如何使用 DISTINCT 子句。

这是另一个,这次来自《信贷业》:

“编写一个查询,返回创建了至少一个‘再融资’提交和至少一个‘在校’提交的所有用户的用户 ID。”

解决方案:要回答这个 SQL 面试问题,您必须使用表 loans。

SELECT user_id
FROM loans
WHERE TYPE in ('Refinance', 'InSchool')
GROUP BY user_id
HAVING count(DISTINCT TYPE) =2

该代码从表 loans 中选择列 user_id,其中的值等于两个值之一:“Refinance”和“InSchool”。因为我们需要确保每种类型都有提交,所以我们需要使用 DISTINCT 子句。人们还需要理解在哪里和拥有之间的区别。因为我们在聚合函数中使用 DISTINCT 子句,所以我们在这里使用 HAVING 而不是 WHERE。

SQL 聚合面试问题

聚合函数广泛用于报告指标和评估汇总结果。数据分析师和数据科学家必须做很多事情。聚合的目的是将数据转化为信息。这些信息以报告、仪表板和图表的形式呈现。您报告的是需要监控的不同指标。因此,基本上,您的两个主要任务是聚合和过滤数据,并对这些数据执行各种计算。

虽然严格来说,数据分析师和数据科学家不是报告而是分析数据,但有很多情况下需要定期监控和报告指标。如果您正在面试 SQL 数据分析师或数据科学家职位,您应该熟悉这些概念:

  • 子查询
  • 连接和自连接
  • 窗口功能
  • CTEs
  • 按扩展分组(汇总、多维数据集、分组集)

问题示例

这是一个来自 Zillow 的中等 SQL 面试问题,测试你对聚合函数的理解

“编写一个查询,找出与全国平均水平相比房价高于平均水平的城市。输出城市名称。

解决方案:要回答这个问题,您必须使用表 zillow_transactions。

SELECT city
FROM zillow_transactions a
GROUP BY city
HAVING avg(a.mkt_price) >(SELECT avg(mkt_price)FROM zillow_transactions)
ORDER BY city ASC

为了回答这个 SQL 面试问题,我们在 HAVING 子句中编写一个子查询。内部查询计算所有交易的总体平均市场价格。然后,主查询只筛选那些平均市场价格高于整体平均市场价格的城市。因为我们只需要在最终输出中报告城市,所以只报告那些指标。

这是另一个。这个来自 Lyft 的稍微复杂一点。

“查找旅行距离最远的前 10 名用户。输出他们的名字和旅行的总距离。”

解决方案:要回答这个问题,您必须使用 lyft_rides_log 和 lyft_users 表。

SELECT name, traveled_distance
FROM(SELECT lu.name,SUM(lr.distance) AS traveled_distance,rank () over (order by SUM(lr.distance) desc) as rankFROM lyft_users AS luINNER JOIN lyft_rides_log AS lr ON lu.id = lr.user_idGROUP BY lu.nameORDER BY traveled_distance DESC) sq
WHERE rank <= 10

这个 SQL 面试问题测试子查询、窗口函数和连接。我们首先用一个内部连接来连接两个表,因为我们需要那些使用过服务的用户,并删除带有不完整用户信息的乘车信息。然后,我们对这些用户的总行驶距离进行排名。我们最终根据排名选择前 10 名用户,并输出他们的姓名和总的旅行距离。

为了提高 SQL 数据科学面试的编码技能,您可以参考此视频,其中我们讨论了一些顶级数据科学面试问题,以及如何解决这些问题和避免常见错误。

作者通过 YouTube 发布的视频

开放式 SQL 面试问题

在需要一些工作经验的数据分析师或数据科学家岗位上,你会遇到这类问题。这些问题中最大的挑战是缺少任何需要计算的特定指标,就像 SQL 聚合面试问题一样。您仍然需要编写一个 SQL 查询,返回一些指标作为结果。然而,有一个很大的不同。在开放式 SQL 面试问题中,您将被要求找到洞察力。

理解你的数据,什么样的计算回答了你被问到的问题,这完全取决于你。例如,你必须找出某个产品发布活动是否成功,或者新的呼叫程序是否节省了成本,或者新的车辆是否提高了用户的满意度。你必须拿出衡量标准来定义“成功”、“节约”或“改进”。

与 SQL 聚合问题相比,这些问题具有额外的维度,旨在测试您解决问题的思路。关于开放式 SQL 面试问题的编码部分,它们测试您将在基本级别和 SQL 聚合类型问题中使用的所有概念。

问题示例

看看脸书问的一个问题:

“脸书开发了一种搜索算法,它可以解析用户评论,并将搜索结果呈现给用户。为了评估算法的性能,我们给出了一个表,其中包含用户点击的搜索结果(“notes”列)、用户的搜索查询以及针对特定评论返回的结果搜索位置。
位置越高越好,因为这些评论正是用户正在搜索的。编写一个查询,针对每个用户查询评估搜索算法的性能。

解决方案:要回答这个问题,我们必须使用表 fb_search_results。

SELECT t.result_id,t.query,CASEWHEN t.check = FALSE THEN 1WHEN t.check = TRUEAND t.position >= 11 THEN 2WHEN t.check = TRUEAND (t.position BETWEEN 6 AND 10) THEN 3WHEN t.check = TRUEAND (t.position BETWEEN 4 AND 5) THEN 4WHEN t.check = TRUEAND t.position <=3 THEN 5END AS rating
FROM(
SELECT query,result_id,position,notes,(regexp_replace(notes, '[^\w]+',' ','g') ilike concat
('% ', query,' %')) AS checkFROM fb_search_results) t

为了解决这个问题,我们首先需要定义评估的标准。我们将用户的搜索查询和算法返回的搜索结果之间的匹配评级定义如下(5 为最高,1 为最低)。

  1. 如果没有匹配,则给出 1 的评级。
  2. 如果匹配发生在查询的前 10 个字符之后,则评级为 2
  3. 如果匹配发生在 6 到 10 个字符之间,则评级为 3。
  4. 如果匹配在前五个字符内,而不是在前三个字符内,则给出 4 的评级
  5. 如果匹配在前三个字符中,则给出最高可能评分 5。

基于这些评级,我们评估每个搜索字符串。为了匹配搜索查询和结果,我们使用 SQL 中的正则表达式匹配函数。主查询在 CASE WHEN 语句中使用该检查列。这是一个开放式的 SQL 面试问题,因为您没有获得区分优秀和不优秀绩效的指标。因此,你必须决定你的规模,将显示如何表现。您的比例可能与上面解决方案中的比例不同。然而,重要的是你要解释你的假设,以及为什么你决定采用某种评估标准。

数据转换面试问题

对于数据工程师或机器学习工程师职位来说,这些问题可能会经常被问到。尽管它在需要一些经验的职位的数据科学家面试中可能并不合适。数据转换或更一般的 ETL(提取、转换和加载)是用于从各种来源收集数据(提取)、根据业务规则改变数据(转换)、然后将这样提取和转换的数据加载到数据库中的过程。

当提取数据时,是从各种数据源中完成的,这些数据源通常以完全不同的格式存储数据。

通过转换,数据采用适合报告和分析的格式。数据通过数据聚合、过滤、排序、连接、基于业务需求规则集的计算等进行转换。

这种数据被加载到分析师或任何其他用户可能使用的另一个数据库或表中。

ETL 大量用于数据仓库,数据仓库是集成数据的中心来源,数据从一个或多个独立的来源流入数据仓库。如果你想在 SQL 工作面试中表现出色,这些是你需要知道的概念:

  • 数据定义语言(DDL)关键字
  • 数据操作语言(DML)关键字
  • 数据控制语言关键字
  • 事务控制语言(TCL)关键字
  • SQL 约束
  • 连接
  • 指数
  • 处理
  • 视图
  • 用户定义的函数
  • 存储过程
  • 扳机
  • 变量
  • 查询优化

问题举例

这是来自西南航空数据科学 SQL 面试的最简单也是最常见的问题之一:

“删除和截断有什么区别?”

回答:

DELETE 是一个 DML 语句。TRUNCATE 是一个 DDL 语句。DELETE 语句可用于删除所有行或仅删除某些行。要删除一些行,您必须使用 WHERE 子句。执行此操作时,数据库会将删除的每一行记录为一个活动。另一方面,TRUNCATE 仅用于删除整个表,这将被记录为仅一个操作。这就是 TRUNCATE 比 DELETE 更快的原因,这在删除包含大量数据的表时表现出来。另外,如果表中有外键,就不能使用 TRUNCATE。

另一个常见的问题是

“如何通过用 SQL 编写查询来更改列名?”

回答:假设你用的是 PostgreSQL。对于一个假设的表,比如说 product,其中一列名为 year,但是我想将其重命名为 description。将实现这一点的查询是:

ALTER TABLE product
RENAME year TO description;

数据转换 SQL 面试问题的另一个例子是:

“如何创建存储过程?”

答:我们将为 Microsoft SQL Server 解决这个问题。例如,如果使用名为 employee 的表。您的过程应该有助于您获得在某个部门工作的员工。代码应该是

CREATE PROCEDURE employee_deparment @deparment nvarchar(50)
AS
SELECT * FROM employees WHERE department = @department
GO;

一旦创建了过程,我可以用以下方式调用它:

EXEC employee_deparment @department = 'Accounting';

数据库建模面试问题

这些问题旨在测试您在数据库设计或数据库建模方面的水平。那是什么意思?您需要展示根据业务流程和业务需求从头开始设计和构建数据库的能力。这需要高水平的技术和业务知识。您将与技术和非技术同事一起工作。因此,您需要了解他们需求的业务方面,以及如何以最合理的方式,在技术上满足他们对数据的业务需求。一般来说,这是一个经过这些步骤的过程(至少在理想世界中是这样的):

  1. 定义数据库的用途
  2. 收集和定义用户需求
  3. 创建概念模型
  4. 创建逻辑模型
  5. 创建物理模型

问题举例

SQL 面试中出现的一个典型问题是Audible 的这个问题:

“你能告诉我们你将如何建立一个推荐系统吗?”

回答:由于有各种各样的方法来回答这个问题,我们将让您自己想出一个构建方法。

数据库设计问题也可以包括 SQL 编码,比如这个来自脸书:

“写一个 SQL 查询,计算涉及两个连接的某个属性的频率表。如果您想按某种属性进行分组或排序,该怎么办?你需要做出什么改变?您如何解释空值?”

回答:由于问题的性质,我们将让您自己回答这个问题。

软件工程 SQL 面试问题

这些问题需要 SQL 知识,但通常情况下,所问的问题在实践中可能并不广泛适用。这些问题会在面试中出现,因为即使作为一名软件工程师,您可能不会每天都编写 SQL 代码,但您仍然需要与每天都使用 SQL 的同事进行交流,了解他们想要实现的目标,并在软件开发中实现他们的需求和 SQL 逻辑。这些问题更考验你的逻辑能力,而不是编码能力。在你面试时可能会被问到的一个问题是

假设您正在使用两张桌子。一个是产品表,它包含以下数据:

  • 身份证明(identification)
  • 产品名称
  • 制造商 id

第二个表是制造商,包含以下数据:

  • 身份证明(identification)
  • 制造商

第一个表中有 8 条记录,第二个表中有 4 条记录。

以下 SQL 代码将返回多少行:

SELECT *
FROM product, manufacturer

答:查询将返回 32 行。每当省略 WHERE 子句时,默认结果是交叉连接或笛卡尔积。这意味着查询将返回第一个表中的所有行组合和第二个表中的所有行组合。

FAANG 公司询问的 SQL 面试问题

FAANG 是五大最著名科技公司的首字母缩写:脸书、亚马逊、苹果、网飞和谷歌。除了对为他们工作的可能性心存敬畏之外,你为什么会特别准备那些公司问的问题?他们可能看起来甚至很有吸引力,但这不是你想在这些公司工作就要特别注意的主要原因。

主要原因是他们的 SQL 面试问题有点不一样。作为科技公司,他们的业务严重依赖数据。哪里有数据,哪里就有 SQL, FAANG 公司经常使用。因此,他们希望绝对确定他们的员工深入了解 SQL。你总是会得到 SQL 面试问题,有点曲折。不同的是,他们的问题更实际,涉及某家公司在日常业务中面临的真实问题和数据的案例研究。这些可以说是我们前面看到的开放式 SQL 面试问题的下一个层次。

看看谷歌的这个例子:

“找到每个用户的邮件活动排名。电子邮件活动等级由发送的电子邮件总数定义。发送电子邮件数量最多的用户的排名为 1,依此类推。输出用户,电子邮件总数,以及他们的活动排名。按邮件总数降序排列记录。按字母顺序对具有相同排名分数的用户进行排序。
在您的排名中,即使多个用户的电子邮件数量相同,也要返回唯一的值(即唯一的百分比)。”

解决方案:要回答这个问题,你需要使用 google_gmail_emails 表。

SELECT  from_user, count(*) as total_emails, row_number() OVER ( order by count(*) desc)FROM google_gmail_emails GROUP BY from_userorder by 3, 1

如您所见,这个问题测试您的聚合函数和窗口函数知识,以及 GROUP BY 和 ORDER BY 子句。但是如果你找到工作,他们也会解决你可能需要解决的现实生活中的问题。

这是另一个类似问题的例子,这次是来自网飞的:

“找出获得奥斯卡奖最多的提名者。
在结果旁边输出被提名人的姓名。
根据获胜次数降序排列结果。"

解决方案:要回答这个问题,您需要使用 Oscar _ incentimites 表。

SELECTnominee,count(winner) AS n_times_won
FROM oscar_nominees
WHERE winner = true
GROUP BY nominee
ORDER BYn_times_won DESC

同样,这个问题测试一些常见的概念。但是问题集是你每天都要做的事情。如果你在网飞从事 SQL 工作,你肯定会分析一些包含奥斯卡提名和获奖者的数据。

SQL 数据科学面试需要学习什么?

您可能已经注意到技术性 SQL 面试问题与其他 SQL 问题重叠。这是因为一个没有另一个就无法工作。知道理论却不能付诸实践,也就是 SQL 代码,是没有意义的。相反,您需要描述您编写的代码背后的技术概念。虽然您应该了解的 SQL 概念取决于您的职位、多年的工作经验以及您希望工作的公司,但我们已经了解了一些跨职位有用的概念。虽然这不是一个详尽的列表,但如果你参加 SQL 数据科学面试,这肯定是你应该知道的。

SQL 和数据库概述

SQL 定义

SQL 代表“结构化查询语言”。它是一种编程语言,用于创建数据库结构,检索和操作其中的数据。

SQL 命令关系数据库的类型

  • 数据定义语言(DDL)
    -创建
    -更改
    -删除
    -重命名
    -截断
    -注释
  • 数据查询语言(DQL)
    -选择
  • 数据操作语言(DML)
    -插入
    -更新
    -删除
    -合并
    -调用
    -解释计划
    -锁表
  • 数据控制语言
    -授权
    -撤销

关系数据库

关系数据库是基于关系数据模型的数据库。这意味着数据库是关系的集合。这些关系显示为表格,由列、行和值组成。关系数据库旨在最小化或完全避免数据冗余,从而实现数据完整性并加快检索速度。

数据库中的关系

关系定义了数据库中表之间的连接类型。有三种主要类型的关系:

  • 一对一的关系(一对一)
  • 一对多关系(1:N)或多对一关系(N:1)
  • 多对多关系(多对多)

数据库规范化

数据库规范化是组织数据库中的数据以实现其目的的过程:数据完整性、无冗余性和检索速度。

约束条件

约束是定义什么类型的数据可以和不可以作为值输入到数据库中的规则。最常见的属性是:

  • 不为空
  • 支票
  • 独一无二的
  • 主关键字
  • 外键

指标

索引是数据库中为优化数据库性能而创建的结构。它们用于加速数据检索。索引的类型有:

  • 聚集索引
  • 非聚集索引
  • 唯一索引
  • 过滤指数
  • 列存储索引
  • 散列索引

视图

视图是一个虚拟表,包含 SQL 语句产生的一个或多个表中的数据。

存储过程

存储过程是由一个或多个 SQL 语句组成的 SQL 代码,这些 SQL 语句被保存起来,并可以在需要时被调用和执行。

触发

触发器是一种特殊类型的存储过程。每当数据库中发生一些特殊事件时,就会自动执行(触发)它。

连接表和查询

内部连接

内部联接只返回一个表中的数据与第二个表中的数据相匹配的那些行。

左外连接

左连接是一个表连接,它将从左表中检索所有的行,并且只从右表中检索匹配的行。

右外连接

这个连接返回右表中的所有行,只返回左表中匹配的行。

全外连接

完全外部联接将联接数据,这样结果将包括一个表中的所有行和第二个表中的所有行。

交叉连接

这就产生了笛卡尔积。这意味着它将返回一个表中所有行的组合以及另一个表中所有行的组合。

工会

这是一个将一个查询的结果与另一个查询的结果相结合的 SQL 命令。因此,它将只显示唯一的记录。

联盟所有

这一个也结合了来自两个或更多查询的结果。UNION 和 UNION ALL 的区别在于它还包括重复项。

聚合和分组数据

聚合函数

聚合函数对数据集执行计算,并返回单个值作为结果。聚合函数的示例有:

  • 计数()
  • 总和()
  • 最小值()
  • 最大()
  • AVG()
  • STDEV()
  • 风险值()

GROUP BY 子句

GROUP BY 子句允许您根据定义的(一个或多个)标准对数据进行分组。

过滤和排序数据

独特条款

DISTINCT 子句是一个只返回不同或唯一值的子句,即结果中没有重复值。

WHERE 子句

WHERE 子句用于根据指定的条件筛选数据。

HAVING 子句

HAVING 子句还根据指定的标准过滤数据。与 WHERE 子句的区别在于 HAVING 子句使用聚合函数。因此,如果使用它,它总是位于 GROUP BY 子句之后,ORDER BY 子句之前。

按条款排序

ORDER BY 子句用于根据某个数据列对查询结果进行排序。

案例陈述

CASE 语句返回基于特定标准的定义值。正是 SQL 语句允许您应用 IF-THEN 逻辑。你不用 IF,而是用 WHEN。对于那时,你用那时。

子查询、公共表表达式(cte)和窗口函数

子查询

子查询是在查询中找到的查询。它可以出现在 SELECT 子句、FROM 子句或 WHERE 子句中。

CTE

CTE 或公用表表达式是由一个查询返回并由另一个查询使用的临时结果集。在这方面,它类似于子查询。但是主要的区别是 CTE 可以被命名并且可以引用它自己。

窗口功能

窗口函数是在定义的行集合(一个窗口)上执行计算的 SQL 函数。与返回单个值的聚合函数相比,窗口函数允许您将聚合值添加到单独列中的每一行。这意味着不对行进行分组,所有行都作为查询结果保存。窗口功能包括:

  • row_number()
  • 排名()
  • 密集等级()
  • 百分比排名()
  • 累计距离()
  • 铅()
  • 滞后()
  • ntile()
  • 第一个值()
  • 最后一个值()
  • 第 n 个值()
  • 平均值()
  • 计数()
  • 最小值()
  • 最大()
  • 总和()

如何组织你的 SQL 数据科学面试方案?

擅长 SQL 是在求职面试中表现出色的先决条件。然而,它不是唯一的技能。问题可能很棘手,因为看起来太复杂或太简单而让你分心或怀疑你的知识。这就是为什么在特定情况下有一个清晰的策略是很重要的。

1.确保你明白需要什么

如果你不明白这个问题是什么,以及对你的期望是什么,你很可能会得到错误的答案。为了避免这种情况,请确保您了解要求您做什么。大声重复这些要求,让面试官确认你是否正确理解了这个问题。不要害怕做那件事。面试官也是人。他们也可能无意中不清楚,犯了一个错误,或忘记给你足够的细节,让你正确回答。

2.花费你的方法

在你开始回答问题之前,特别是如果你正在写 SQL 代码,先考虑一下你的方法。这样,您将能够更快地找到解决方案,或者找到您打算编写的代码中的漏洞。你应该这样做,让面试官引导你,以防你错过了问题的要点。在提出最终解决方案之前,最好先进行修正。

3.尝试将输出可视化

这可以帮助你编写解决问题的代码。有时,当您阐明输出应该是什么样子,并将其与初始数据进行比较时,方法和解决方案就会显现出来。

4.编写 SQL 代码

在某些时候,你需要开始写代码。正如我们讨论过的,你不应该一头扎进去。但是你不能一直拖延最佳的写作方法。在你完成了前面所有的步骤之后,你仍然不确定你是否有正确的解决方案,那么就开始写代码吧。

原因之一是,有时候根本无解。意思是这个问题太复杂了,在给你的时间内无法解决。在这种情况下,面试官对你的解决方案不感兴趣。相反,他或她对你的思维方式和你处理问题的方式感兴趣。通常有多种使用 SQL 解决问题的方法,这也是一些面试官感兴趣的:过程,而不是目标。

5.逻辑部分中的代码

当你写代码的时候,注意它的结构。将代码分成逻辑部分。这样,你会让你的代码更容易阅读,这也是获得这份工作的要求之一。写一个乱七八糟的正确代码是没有意义的,而且在你写完之后没人能读懂它。你也不行!!如果你的代码被分成逻辑部分,你就更容易向面试官解释你做了什么。

6.优化代码

记住代码优化也很重要。当然,如果你的代码很复杂,你就不能在写作的时候优化它。但是你要注意一些通用的优化准则,这样你的代码才是合理优化的。你也可以和面试官讨论你还需要采取什么步骤来优化你的代码。这也是工作要求,类似于上一点。没有必要编写能让你得到想要的结果的代码,但是要花很长时间去执行。

7.解释你的答案和假设

即使你没有得到要求的答案,也不意味着你没有通过面试。这就是为什么你应该总是知道你为什么做某事,并解释你为什么这样做。也许你没有得到他们所提问题的答案,但是你得到了一些问题的答案。所以确保你陈述了你的假设,并解释了你为什么这么做。同样,他们可能正是在寻找这一点:符合假设的正确推理,即使假设是错误的。这也表明你知道自己在做什么,即使这不是他们要求的。

此外,解释假设的原因之一是问题中可能存在漏洞。所以想象一下,当你认为自己都错了的时候,在面试中指向它,简单地解释你为什么要做某事。

在这段视频中,StrataScratch 的 Nate 分享了一些关于如何组织 SQL 面试解决方案的技巧:

作者通过 YouTube 的视频

额外的 SQL 面试技巧

这里有一些额外的提示,可能会帮助你在即将到来的 SQL 面试中取得成功。

了解你潜在的雇主

这在总体上很重要,不仅仅是面试的 SQL 部分。了解你未来的雇主、他们的产品和行业是很重要的。当考虑到 SQL 问题时,这一点尤其重要。这是为什么呢?

正如我们之前讨论的,FAANG 公司通常会问你一些非常实用的 SQL 编码问题,这些问题会让你使用相同的数据,解决相同的问题,就像你被雇佣时必须做的那样。FAANG 公司并不是唯一这样做的公司。所以当你准备面试时,试着想想哪些数据对这家公司来说是重要的,他们的数据库会是什么样的,等等。当你练习 SQL 问题时,试着从你感兴趣的公司或者至少从他们的竞争对手那里找到真正的问题。如果这些公司在同一个行业,他们使用的数据很可能会或多或少相同。

为白板做准备

被要求在白板上写 SQL 代码是很常见的。对某些人来说可能是震撼,这是可以理解的。您可能习惯于在真实的 RDBMS 中,在真实的数据上编写代码,这允许您定期检查查询是否有效。即使是最伟大的 SQL 大师,也无法在不运行代码以查看它是否工作或是否返回预期结果的情况下编写代码。但是,在 SQL 面试中,标准要高一点。

虽然这可能很可怕,但也是可以理解的。在白板上写代码表明你知道如何写代码。阅读你的(或别人的)代码也很重要。这也是在白板上测试的技能。如果您可以阅读代码并判断它是否会给出您想要的结果,而不依赖于数据库来告诉您,那么使用真正的数据库和 SQL 环境将会更容易。

编写干净的代码

我们不是在谈论你的笔迹。如果你的字迹潦草,你就无能为力了。但这并不意味着你的代码不可读。当你写代码的时候,试着把它格式化,以便你和面试官更容易阅读和检查你的解决方案。

代码被阅读的次数比它被编写的次数多。代码应该总是以提高可读性的方式编写。

  • Guido Van Rossum,Python 的创造者

经常使用空格和换行符使你的代码更容易阅读。如果需要(重新)命名表和列,请与您选择的命名约定保持一致。需要时添加注释。尽可能使用别名,但是当你使用别名时,要让它们听起来有逻辑,而不是一些随机的决定。

下面是 Nate 提供的一些关于如何组织冗长的 SQL 代码的有用提示。

作者通过 YouTube 发布的视频

只有在你适应的情况下,才使用公司的 SQL 方言

如果你有使用多种 SQL 数据库(Oracle、PostgreSQL、Microsoft SQL Server、MySQL)的经验,试着适应并使用你未来雇主使用的数据库方言。那会很好,可以显示你的多才多艺,尤其是如果你知道他们喜欢什么方言。然而,如果你只熟悉一种方言,不要认为这是面试的结束。例如,如果您只使用 PostgreSQL,而公司使用 Microsoft SQL Server,那么在这两个数据库中,同一命令可能会有不同的关键字。也有可能 PostgreSQL 有一些 Microsoft SQL Server 不允许的功能,反之亦然。

问面试官是否有可能用 PostgreSQL 而不是 Microsoft SQL Server 编写代码,因为你对它更熟悉。如果你懂几种方言,那总是更好。但是如果你用一种熟悉的方言来写,即使是“错误的方言”,也比仅仅因为你太害怕去问你是否可以用你熟悉的方言来写而弄乱代码要好。方言之间的差异并不是很大。因此,如果您了解 SQL,您将轻松快速地适应新的数据库。

交流。自信。协作。

当面试评估你的时候,除了编码能力,雇主还会在雇员身上寻找其他东西。你将作为一个团队的一部分工作,因此你自信地表达你的想法的能力,对关于你的代码的积极反馈持开放态度的能力和作为一个团队工作的能力同样重要。雇主们试图衡量这些,即使是在编码这样的技术性工作中。

万一你遇到困难,寻求帮助是至关重要的。寻求帮助是自信的表现,而不是软弱的表现。让面试官了解你的思维过程,这样万一你陷入困境或者无意中遗漏了一些信息,她可能会帮助你。倾听所提供的任何解释,以确保您已经考虑了所有可能出现的边缘情况。

这些是每个数据科学家都必须具备的技能,不幸的是,没有多少候选人关注这些技能。面试官更有可能聘用一位对 SQL 有良好基础理解的数据科学家,他愿意适应变化,并在一个庞大但僵化的过程中获得额外的技能。雇主正在寻找长期关系的潜力。如果你给出正确的信号,你可能会得到你梦想中的工作。

查看这些关于如何准备数据科学面试的 5 条建议。

我们还收集了 2021 年真实公司提出的一些高级 SQL 问题,你可以在我们的高级 SQL 面试问题你必须知道如何回答文章中找到。

结论

在本文中,我们考察了 SQL 数据科学面试的各个方面。我们首先了解了为什么 SQL 在数据科学领域如此受欢迎,以及该行业中的不同角色。然后,我们详细概述了每个职位可能会遇到的问题类型,以及为了在数据科学面试中熟练使用 SQL,需要学习的内容。

即使你刚刚开始使用 SQL,要做好面试准备所需要的只是坚持、耐心和大量的练习。如果您没有太多使用 SQL 的实际经验,练习编写 SQL 代码是非常重要的。多做,有规律地做。连续性非常重要。试着回答尽可能多的 SQL 面试问题,无论是假设性的,还是更好的,来自你想去工作的公司的真实问题。只有通过编写大量代码,你才会获得经验,掌握一些需要用 SQL 解决的典型问题,语法也会变得像第二天性一样。

即使你在 SQL 方面有丰富的经验,并在商业环境中使用它,为面试做准备和提高你的技能总是好的。没有人了解 SQL 的一切。一般来说,人们知道他们需要什么,他们每天经常使用什么。所以有可能在工作几年后,你成为了 SQL 某一方面的大师。不要让它使你认为你知道一切。可能你的新工作会要求你了解一些不同的 SQL 函数和可能性,而这些你并不完全精通。

最初发表于【https://www.stratascratch.com】

以太坊上的 SQL:如何处理来自事务的所有数据

原文:https://towardsdatascience.com/sql-on-ethereum-how-to-work-with-all-the-data-from-a-transaction-103f94f902e5?source=collection_archive---------3-----------------------

作者图片

关于如何在以太坊上查询交易、内部交易、事件日志和主题等最常见问题的所有答案。

本文所有图片均由作者创作。

如果你在寻找更多的 web3 数据内容,请查看我的 30 天免费课程(带视频) !请务必 订阅我的简讯 了解更多课程和教育技巧。

> >我在这里写了一个初学者指南 < <

你钱包里有什么?

如果你曾经在以太坊(或任何支持区块链的智能合约)上进行过交易,那么你可能已经在 block explorer 上查找过,并且看到了这一堆信息:

这只是第一个选项卡上的信息

学习阅读交易的细节将是你所有以太坊数据分析和知识的基础,所以让我们涵盖所有的部分以及如何在 SQL 中使用它们。我将使用 Dune Analytics 来运行我的查询,但还有许多其他工具可以用来查询链,如 Big Query 和 Flipside Crypto 。

如果你对 SQL 和以太坊完全陌生,我建议你先从 我的完全初学者概述 开始。

指南概述

我们将从四个层面讨论交易:

  1. 交易基础
  2. 函数调用和状态
  3. 内部交易(跟踪)
  4. 日志(发出的事件)

作为我们交易示例的基础,我们将使用镜像众筹契约。简而言之,这是一个智能合同,允许您通过向合同捐赠 ETH 来换取 ERC20(可替换)或 ERC721 (NFTs)令牌。合同的创建者可以通过关闭众筹基金来收回这些资金。这绝不是一个简单的契约,但我在这里想指出的一点是,你不需要理解所有的 solidity 代码来开始你的分析——你只需要知道如何导航上面的四层。

我们将学习的三个事务是:

  1. 众筹合同的创建/部署
  2. ETH对合同的贡献
  3. 关闭并从合同中提取资金

这边注意,我们还刚刚开放了众筹供任何人使用,所以如果你很好奇或者想创建一个众筹头像的话,可以到mirror.xyz/dashboard开始使用。趁现在,跳进 我们的不和谐

众筹合同的创建/部署

第一笔交易:0x 5 e 5 ef 5 DD 9d 147028 F9 BC 21127 E3 de 774 a 80 c 56 a2 e 510d 95 f 41984 e 6 b 7 af 1 b 8 db

让我们从交易基础开始。

  1. 每笔交易都有一个唯一的几个不同变量的keccak 256transaction hash
  2. 基于挖掘事务的时间,有一个关联的blocknumber,通常每 15 秒创建一个新块。
  3. From是签署交易的一方,To是被交互的合同地址
  4. Value是从签名者的钱包中转移的 ETH 值。即使该值为 0,也不意味着在交易期间没有以太网被传输。
  5. Gas 有点复杂(尤其是 EIP-1559),但只要记住这个公式:Gas Price * Gas Used by Transaction = Transaction Fee

现在是肉和骨头,交易的input data:

这只是任何函数调用和传入参数的字节码。前 8 个字符(4 个字节)是函数签名0x849a3aa3,本质上是函数名和参数类型的散列。不,这些并不总是唯一的,可能会导致黑客攻击/安全问题。在这种情况下,该函数调用工厂契约来创建众筹契约(这是一个代理,但我们不会深入讨论)。

**createCrowdfund**((uint256,uint256,bytes32)[], (address,uint256), string, string, address, address, uint256, uint256, uint256)

如果你点击“解码输入数据”,就会显示出来,你也可以看到各种变量值的设置。每个后续的 64 个字符(32 字节)是一个不同的输入变量。众筹基金有三层版本。在这次 BLVKHVND 的众筹中,他们使用了 1000、250 和 50 的数量,价格分别为 0.1、0.3 和 1 ETH。

注意,价格实际上显示为100000000000000000,这是因为前 18 个零代表小数。我们必须通过除以数据中的10^18来进行转换。

这是很多,让我们开始查询。Dune 有一个名为ethereum.transactions的表,其中包含了我们在上面讨论过的从第一个块开始的每个事务的所有变量。我们可以在这个表中查询0x849a3aa3在过去几个月中的出现情况:

SELECT * FROM ethereum.transactions 
WHERE "block_time" > **now**() - interval '3 months'
AND "data" is not null
AND SUBSTRING ( encode("data", 'hex'), 1, 8 ) = '849a3aa3'

ethereum.transactions是一个非常大的表,所以如果您在没有过滤器的情况下查询,查询将会超时(超过 30 分钟)。按block_time过滤通常是最有用的,在这种情况下,我们取 3 个月内发生的所有行。此外,许多交易只是 ETH 传输,没有任何附加的data,因此我们将通过仅保留data is not null来过滤掉它。现在为了检查函数签名,我们需要将数据从十六进制转换成字符串,然后使用SUBSTRING只取从位置 1 到位置 8 的字符。

https://dune.xyz/queries/192453

现在把复杂的部分,内部交易和事件发射出来。为此,查看代码会更容易。如果你转到 etherscan 上的contract选项卡,在 10 个文件中的第 1 个文件上按 ctrl+f,你会发现下面的代码(我编辑掉了一些位,使其更具可读性)。

function createCrowdfund(...variables...) external returns (address crowdfundProxy) {...some variable prep code... crowdfundProxy = address(new CrowdfundWithPodiumEditionsProxy{salt: keccak256(abi.encode(symbol_, operator_))}(treasuryConfig, operator_)); emit CrowdfundDeployed(crowdfundProxy, name_, symbol_, operator_); ...register to treasury code...}

这里的第一个关键行是 crowdfundProxy = address(contract_to_be_created),它部署新合同并创建一个CREATE 0类型的内部交易。转移 ETH 还会创建一个类型为CALL的内部事务,我们将在下一个学习的事务中看到。

我们可以查询通过以下方式创建的所有众筹合同:

SELECT tx."block_time", tx."from", tr."type", tr."code"
FROM ethereum.transactions tx 
LEFT JOIN ethereum.traces tr ON tx."hash"=tr."tx_hash" *--tracks internal transactions*
WHERE tx."to" = '\x15312b97389a1dc3bcaba7ae58ebbd552e606ed2' *-- crowdfund podiums edition*
AND tr."type" = 'create'

【https://dune.xyz/queries/192466】

我们需要ethereum.transactions,因为我们想要过滤仅与工厂合同上的事务相关的跟踪(内部事务)。我们需要这样做,因为一个内部交易的to并不总是与整个交易的to相同。我们可以根据事务散列连接这些表,然后只过滤掉create类型的内部事务。

这里的第二个关键行是 emit CrowdfundDeployed,它创建了一个存储在节点中而不是块中的日志。如果您查看日志,您会注意到也发出了EditionCreated事件,但是这是来自另一个契约的,即实际上创建了 ERC721 令牌(因此地址不同)。

类似于函数签名,事件在Topic 0中也有唯一的散列。所以在上面的事件中,0x5133bb164b64ffa4461bc0c782a5c0e71cdc9d6c6ef5aa9af84f7fd2cd966d8eCrowdfundDeployed的散列,0xbaf1f6ab5aa5406df2735e70c52585e630f9744f4ecdedd8b619e983e927f0b6EditionCreated的散列。

我们可以查询 dune 中的ethereum.logs表,查看所有创建的众筹资金:

SELECT * FROM ethereum.logs
WHERE "topic1"='\x5133bb164b64ffa4461bc0c782a5c0e71cdc9d6c6ef5aa9af84f7fd2cd966d8e'::bytea

https://dune.xyz/queries/192553

topic2topic3通常保存 ETH 传输的数据,否则事件数据将显示在data栏中。稍后我们将深入探讨如何使用它。

日志非常有用,因为它们可以用来发出状态变量,而不仅仅是函数调用值(Graph 使用日志为 GraphQL 查询的子图建模)。接下来,我们将利用我们所学的一切来研究 ETH 对我们新创建的众筹合同的贡献(位于地址0x320d83769eb64096ea74b686eb586e197997f930)。

如果你已经做到了这一步,那么你已经理解了所有艰难的概念。给自己一个鼓励!我们将在接下来的两个部分中详细讨论,所以如果需要的话,可以休息一下。

ETH 对合同的贡献

第二笔交易:0x D4 ce 80 a5 ee 62190 C5 F5 D5 a5a 7 e 95 ba 7751 c8 F3 ef 63 ea 0 E4 b 65 a1 abfdbbb 9 D1 ef

这本书读起来相当简单。杰西从 BLVKHVND 众筹基金支付 1 ETH 来铸造一版 tokenId 167。他还得到了 1000 HVND,这是众筹基金根据捐款规模发放的 20 元人民币。

但是,如果我们想知道一段时间以来 ETH 贡献了多少,或者卖出了多少个版本呢?有时合同在 etherscan 的Read Contract中会有一个查看功能,你可以在那里得到总余额。但在这种情况下,合同没有这一点。

请记住,函数调用会改变状态数据,我们需要通过聚合事务历史来拼凑整体状态数据。有时,契约的整体状态可以在事件中发出,比如复合 V2 的AccrueInterest事件。

在我们的例子中,我们需要在一个查询中做两件事来获得 ETH 贡献的总数:

  1. 获取调用了“contribute”方法的事务
  2. 通过对类型为CALL的内部交易进行过滤,对转移的总 ETH 进行求和

记住,我可以通过解码 etherscan 上的 input data得到方法函数签名。

SELECT SUM(tr."value"/1e18) as contribution FROM ethereum.transactions tx 
LEFT JOIN ethereum.traces tr ON tx."hash" = tr."tx_hash"
--transactions filtering 
WHERE tx."to" = '\x320d83769eb64096ea74b686eb586e197997f930'::bytea
AND tx."data" is not null
AND SUBSTRING ( encode(tx."data", 'hex'), 1, 8 ) IN ('a08f793c', 'ce4661bb')
--traces filtering 
AND tr."success"
AND tr."value" > 0
AND tr."call_type" = 'call'

技术上有另一个方法叫做contributeForPodium,这就是为什么我们检查上面的两个函数签名。CALL类型实际上在操作码级别也有子类型,所以我们需要call的特定基call_type(如果你熟悉 delegatecall,那么你会知道这会给我们一个双重计数)。我们对事务散列进行连接,然后除以 10^18,得到 ETH 值的正确小数。

https://dune.xyz/queries/192577

让我们继续最后一笔交易,数据开始变得非常棘手。

结束合同并从合同中提取资金

第三笔交易:0x e 9 D5 Fe FDE 77d 4086d 0 f 64 DD 1403 f 9 b 6 E8 e 12 AAC 74 db 238 ebf 11252740 C3 f65 a 8

在这里,我们可以看到 337 个 ETH 被转移,1,012,965 个 HVND 令牌(后者在第一次交易中由operatorPercent_决定)。在调用这个函数之后,契约就像任何普通的 ERC20 一样运行。

在众筹已经结束的情况下,我们可以从该交易的数据中获得筹集的总额——例如在CALL类型的内部交易中转移的价值。不过,最好将这与一个事件联系起来,以防有一些我们不知道的转移行为。但是等等,为什么日志不可读?

好吧,这就是我们开始进入一些非常混乱的模式的地方。早些时候我提到过,这个众筹基金是作为一个代理来部署的——这意味着它就像一个插入计算机的空 USB,实际上包含了逻辑。创建 USB 比创建计算机要便宜得多——这一逻辑也适用于链上(除了成本是汽油)。如果你想了解代理模式,我可以看看 OpenZeppelin 团队的这篇文章。

在这种情况下,计算机被称为逻辑,并且只部署一次。代理被多次部署,并且它不具有契约代码中的逻辑功能或事件。因此,etherscan 不能显示日志中的解码数据。那我们怎么把这些拼凑起来?我们可以获取事件的 keccak256 散列,就像我们对函数签名所做的那样。但是在这里阅读代码将帮助您节省一些时间。如果你去工厂合同上的Read Contract ,你会看到逻辑合同的地址:

从那里,我们可以在代码中寻找closeFunding()函数:

function closeFunding() external onlyOperator nonReentrant {...code... _mint(operator, operatorTokens);*// Announce that funding has been closed.*emit FundingClosed(address(this).balance, operatorTokens); ...ETH value transfers...}

ETH 值传输不会发出事件,因为它们只是内部事务。如果你熟悉 ERC20 标准是如何工作的,你会知道_mint实际上创建了一个Transfer事件(这意味着覆盖了我们的第一个事件)。这意味着FundingClosed一定是第二个日志,主题是0x352ce94da8e3109dc06c05ed84e8a0aaf9ce2c4329dfd10ad1190cf620048972。你能想出为什么不能是第三个日志吗(提示:前两个日志和第三个日志有什么关键区别吗?

有了这些知识,我们可以像查询任何其他事件一样查询这个事件,并进行一些复杂的数据解码(记住参数是每 64 个字符(32 个字节)。我们必须把它变成一个字符串来切片,然后我们把它变成一个数字,除以 10 ⁸来去掉小数。

SELECT "contract_address", bytea2numeric( decode ( SUBSTRING ( encode("data", 'hex') , 1, 64 ), 'hex'))/1e18 as eth_raised, bytea2numeric ( decode ( SUBSTRING ( encode("data", 'hex') , 65 , 64 ), 'hex'))/1e18 as tokens_allocated_owned
FROM ethereum.logs
WHERE "topic1"='\x352ce94da8e3109dc06c05ed84e8a0aaf9ce2c4329dfd10ad1190cf620048972'::bytea
AND "contract_address"='\x320d83769eb64096ea74b686eb586e197997f930'::bytea

【https://dune.xyz/queries/192560】

恭喜你,你现在已经知道如何使用ethereum.transactionsethereum.tracesethereum.logs。它们总是可以通过事务散列来连接,然后剩下的就是知道如何用encode/decodesubstring和一些bytea操作符来操作数据。呜哇!

我们也可以在上一个交易中为 contribute方法做这个练习。因为这都发生在代理合同上。

把所有的放在一起

现在,如果我们必须跟踪函数签名和事件主题,以及解码每个查询中的所有变量,我想我们现在都应该退出数据分析了。幸运的是,大多数数据服务都有一些不同的 合同解码 的意思是我可以给一个合同地址,ABI和 Dune 会帮我处理解码。这样,事件/函数就变成了它们自己的表,我可以很容易地使用下面的语句进行与前面相同的“总贡献”查询:

WITH union_sum as (SELECT **SUM**("amount")/1e18 as raised FROM mirror."CrowdfundWithPodiumEditionsLogic_evt_Contribution"WHERE "contract_address"='\x320d83769eb64096ea74b686eb586e197997f930'UNION ALL SELECT **SUM**("amount")/1e18 as raised FROM mirror."CrowdfundWithPodiumEditionsLogic_evt_ContributionForEdition"WHERE "contract_address"='\x320d83769eb64096ea74b686eb586e197997f930')SELECT **SUM**("raised") FROM union_sum

链接到查询

幸运的是,这个查询可读性更好,也更容易编写。他们甚至负责代理/工厂逻辑模式——感谢团队!如果没有这种抽象,我保证数据分析写起来会混乱十倍,调试起来会糟糕一百倍。Dune 还有很多其他有用的表格,比如用于每日代币价格的prices.usd和用于所有主要交易所的代币交易的dex.trades(以及用于 OpenSea NFT 行动的事件nft.trades)。

虽然大部分时间你都在玩解码的数据,但是了解数据背后真正隐藏的东西会帮助你在 Web3 中更快地升级。另外,你现在已经精通以太扫描——我保证这将成为未来所有加密工作描述的一部分。我希望这对您有所帮助,如果您需要一些帮助,请随时联系我们。

SQL 性能提示#2

原文:https://towardsdatascience.com/sql-performance-tips-2-128a31e8ecb?source=collection_archive---------37-----------------------

避免在堆和 cte 与临时表上运行

克里斯·奥瓦尔在 Unsplash 上拍摄的照片

除了现代 SQL 世界中通常被忽略的有用概念之外,本文是关于提高 SQL 查询性能的两个小技巧系列中的第二篇:

  • SQL 性能提示# 1:避免自连接和用于连接的操作/函数

与之前的迭代类似,需要一个介绍性的注释:我绝不是 SQL 专家,非常欢迎反馈和建议。

用索引临时表替换 cte

cte 在大多数查询中是一个很大的帮助,简化了开发过程及其维护,提供了单一的事实来源。但是,它们的影响很大程度上取决于 SQL 引擎。例如,PostgreSQL 12+会自动具体化(将输出分配到内存中)被多次调用的 cte。众所周知,Microsoft SQL Server 不会具体化 cte,而是在每次调用时将其作为视图运行,这甚至会导致每次调用的输出不同,具体取决于序列化级别。但是,即使引擎像 PostgreSQL 一样实现了 CTE,它的索引呢?通常,引擎会利用底层源表的索引。

cte 在许多情况下是完美的,但是有时,您查看查询优化器并意识到正在进行全表扫描,这仅仅是因为查询不依赖于底层的现有索引。更糟糕的是,例如,当 CTE 自身被调用时,Microsoft SQL Server 往往会发生并发竞争,这通常会导致死锁。

认识到临时表的潜在好处的另一个有用的方法是使用 SQL 引擎的统计信息,通过逻辑读取的数量和类似的统计信息来发现过多的 I/O 操作。提示:阅读次数越少越好。

考虑堆栈溢出数据库创建一个查询来:

  1. 找到用户最多的 5 个位置
  2. 检索居住在此位置的用户,按其显示名称排序

使用 CTE(上图)与临时表(下图)查询

执行计划— CTE

CTE 执行计划的亮点是:

  • 好:CTE 将顶级位置和查找所述位置的顶级用户合并到一个语句中,这有一个好处,就是不必物化数据
  • 好:CTE 正确估计了 5 个结果
  • 坏:另一方面,它无法识别前 5 个结果的内容,估计有 50.073 个用户匹配所述结果,而不是实际的 66 个(75 868%的高估)
  • 错误:SQL Server 依赖于索引查找和键查找

执行计划—临时表

临时表的执行计划的亮点是:

  • 好:SQL Server 正确估计了位置的数量(5)
  • 好:它正确识别了这 5 个位置
  • 好:因此,临时表版本更好地接近了实际用户数,估计为 17 184 行,而实际为 50 073 行(低估了 291%)
  • 好:它依赖于表扫描

那么什么时候选择 CTEs vs 临时表呢?

在以下情况下,cte 往往是合适的选择:

  • CTE 的内容对查询结果的其余部分没有影响
  • 当 CTE 可能包括额外的不必要的信息时

另一方面,在下列情况下,临时表可能是一个不错的选择:

  • 输出将被多次使用
  • 数据需要传递给其他对象
  • 出于性能方面的考虑,您需要将流程分成几个子流程

注意:这一部分是从 Brent Ozar 的博客中大量获取的。确保遵循它,因为他比任何人都了解 SQL Server。下面的链接。

https://www.brentozar.com/blog/

避免在堆上运行查询

这是 SQL 101 的一个教训:在任何情况下,都不要在(集群)无索引表上运行查询,除非你被强迫这样做。即使它们是临时表,也要确保至少添加一个主键,最好是包含可查询列的组合键。如果不能使用主键,可以通过在可筛选列上指定非聚集键来进一步提高性能(尽管这是有代价的)。

描述表创建的典型索引决策工作流的图表。(信用 toptal.com

在未编制索引的表上运行大型操作,无论是筛选还是连接,都将反映在全表扫描中,就像它听起来的那样,为每一行和每一个操作扫描整个表。听起来效率不高?那是因为它是。

但是等等,我永远不会有一个没有索引的表,当然也不会对它运行查询!

其实没那么简单。正如我在第一段中提到的,如果您的查询没有利用现有的索引(例如,在 where 谓词中使用一个没有索引的列),那么将执行全表扫描。这就是执行计划可以成为您最好的朋友的地方,它暗示了正在执行全表扫描的情况,这表明可能需要额外的或经过调整的索引。

综上

  • 如果您正在使用 Microsoft SQL server,并且不止一次地调用 CTE,请探索使用临时表或使用中间具体化的可能性(来自性能提示# 3);
  • 如果您不确定语句的哪些部分将被进一步使用,CTE 可能是一个不错的选择,因为 SQL Server 能够检测实际使用了哪一部分
  • 如果在 CTE 上执行的操作不使用底层表的索引,则考虑将数据具体化到具有专用索引的临时表上;
  • 尽可能依赖于诊断,使用执行计划来检测全表扫描,使用统计信息来检测可能的过量 I/O 源,这可以从实体化中受益;
  • 看在基督的份上,给任何表添加索引;如果您事先知道将严重依赖其他列,而不是索引中的列,请考虑将它们包含在索引中,或者创建一个新的(非聚集)索引;

SQL 难题优化:衰减函数的 UDTF 方法

原文:https://towardsdatascience.com/sql-puzzle-optimization-the-udtf-approach-for-a-decay-function-4b4b3cdc8596?source=collection_archive---------16-----------------------

在 SQL 中构建一个衰减函数并不简单,但是很有趣。到目前为止,最好的答案是使用窗口函数,但我们能在雪花中使用 JS UDTF 做得更好吗?在此查找结果

一个 UDTF 解决了衰减分数 SQL 难题(图片由作者提供)

在 YouTube 上观看

布列塔尼·贝内特和克莱尔·卡罗尔 用一个有趣的 SQL 谜题剪贴数据-推特。几个小时后,Benn Eifert 用 SQL 窗口函数提出了一个很棒的解决方案。然后 TJ Murphy 在雪花中测试了一下,解释了为什么窗口函数比 joins 好。现在,轮到我上场了。用 JavaScript UDTF 我能做得更好吗?找到下面的结果。

为什么要用 UDTF?

让我们首先来看看窗口纯 SQL 解决方案:

with t as (select *, row_number() over (partition by name order by week desc) as week_countfrom decay_puzzle_10m
),select *, points_this_week * power(0.9, week_count - 1) as decayed_points, sum(decayed_points) over (partition by name order by week rows between unbounded preceding and current row) as decayed_points_cumulative, decayed_points_cumulative / power(0.9, week_count - 1) as score_this_week_calc
from t
;

优点:它很聪明,效率也很高。

缺点:难以理解、解析、修改和复用。

与此同时一个 JS 表格 UDF 正好可以做这个问题所需要的:

  • 作为一个表格 UDF,它将接收整个表格并逐行查看
  • 由于它可以对在之前看到的行进行存储,因此它可以为所需的衰减数学积累数据。
  • 由于它允许分区,先前行的内存可以为每个段拆分。
  • 随着每一行被处理,它可以输出每一行的正确结果

天真的 UDTF

这是 Claire & Brittany 针对该问题发布的原始 Python 逻辑:

for n in range(number_of_weeks):sum = 0for i in range(n+1):sum += points[i] * pow(0.9, n-i)print(f"Score: {sum}")

让我们把它翻译成一个 JS UDTF:

create or replace function decay_udtf_v1(points float)
returns table (output_col float)
language javascript
as $$
{processRow: function f(row, rowWriter, context){this.pointarr.unshift(row.POINTS); // store historysum = 0;this.pointarr.forEach(function (item, index) {sum += item * Math.pow(0.9, index);})rowWriter.writeRow({OUTPUT_COL: sum});}        , initialize: function(argumentInfo, context) {this.pointarr = [];this.counter = 0;}
}
$$;

这基本上是用processRow处理每一行,将前面的行值存储到数组pointarr中。它对衰减值进行forEachsum,并输出结果。

使用它既简单又干净:

select output_col, a.*
from decay_puzzle_10m a, table(decay_udtf_v1(points_this_week::float) over(partition by name order by week));

但是这比窗口函数快吗?我们来测试一下。

基准测试天真的 JS UDTF 与窗口函数

首先,让我们从 Claire & Brittany 给我们的 4 个样本行中创建一个 1000 万行的表:

create table decay_puzzle asselect 'claire' as name, '2021-10-04'::date as week, 3 as points_this_week, 3.00 as score_this_week
union all select 'claire', '2021-10-11'::date, 1, 3.70
union all select 'claire', '2021-10-18'::date, 2, 5.33
union all select 'claire', '2021-10-25'::date, 0, 4.797
;create or replace table decay_puzzle_10m asselect name ||  seq8() % 25000 name, week+seq8()%100 week, points_this_week, score_this_week
from decay_puzzle, table(generator(rowcount => 100000)) g1, table(generator(rowcount => 25)) g2
;

这创建了 1000 万行,有 25,000 个不同的名称,每个名称有 400 周。日期需要清理,但这与我们的目的无关。

现在是时候对我们的 1000 万行样本进行基准测试了,将窗口函数解决方案与 XL 仓库中的 JS UDTF 朴素解决方案进行对比。获胜者是…

  • 车窗功能:46s
  • JS 天真的 UDTF: 36s

窗口函数 vs JS UDTF 超过 10M 行——天真的 JS UDTF 赢了(图片由作者提供)

我们有赢家了!但这还不是全部。你可能已经注意到了,我一直称 UDTF 为“天真”。事实证明,如果我们在其中优化 JS,结果会更好:

  • 车窗功能:46s
  • JS 天真的 UDTF: 36s
  • JS 优化的 UDTF: 9s

窗口函数与超过 10M 行的 JS UDTF 的对比——优化的 JS UDTF 胜出(图片由作者提供)

优化 JS 代码是一个巨大的胜利——原来我们的原始代码相当低效。稍后我会在这里分享它的代码,但首先我想看一些更有趣的东西:当我们从 10M 行增加到 100M 行时会发生什么?

用 100M 行对简单的 JS UDTF 和窗口函数进行基准测试

要生成 1 亿行,我们可以使用类似的 SQL:

create or replace table decay_puzzle_100m
as
select name ||  seq8() % 25000 name, week+seq8()%100 week, points_this_week, score_this_week
from decay_puzzle, table(generator(rowcount => 1000000)) g1, table(generator(rowcount => 25)) g2
;

仍然有 25,000 个不同的名字,但是现在每个名字有 4,000 周(而不是 400 周)。

如果我们再次尝试我们的 3 个解决方案,结果令人惊讶:

  • 车窗功能:16s
  • JS naive: 378s
  • JS 优化:95s

真有意思!由于每个名字的观察次数增加了 10 倍,JS 解决方案的时间也增加了 10 倍。但是 SQL 窗口函数变得更快了!这是 1/3 时间内的 10x 行。

我们在这里看到的是并行化的力量,以及 SQL 优化器在根据输入和可用资源的大小决定路径时所做的假设。

10 倍的数据,1/3 的时间—使用 SQL 中的窗口函数(图片由作者提供)

优化的 JS UDTF 代码

让我们回到 JS 代码,以及为什么它可以被优化以 1/4 的时间运行:

  • Array.push()要求增加分配给数组的内存。如果我们知道输入的大小,我们可以预先分配一块连续的内存。
  • reduce()似乎比foreach()跑得快
  • power(0.9, week_count — 1)运行太多次,我们可以预先计算它——特别是如果我们知道输入的大小。如果我们不这样做,我们可以做一些懒惰的评估。

优化的 JS UDTF:

create or replace function decay_udtf_optimized(points float)
returns table (output_col float)
language javascript
as $$
{processRow: function f(row, rowWriter, context){this.pointarr[this.counter]=row.POINTS;reduce_func = function(prev, curr, idx, arr) {return prev + curr * this.precalc[this.counter - idx];};sum = this.pointarr.reduce(reduce_func.bind({precalc: this.precalc, counter: this.counter}), 0);rowWriter.writeRow({OUTPUT_COL: sum});this.counter++;}        , initialize: function(argumentInfo, context) {var preoptimized = 4000 // testing for 400|4000this.pointarr = Array(preoptimized);this.precalc = Array(preoptimized);this.precalc[0]=1;for (let i = 1; i < preoptimized; i++) {this.precalc[i] = this.precalc[i-1]*0.9;}this.counter = 0;}
}
$$;

附加说明:如果我们去掉乘法,用一个prev + curr代替prev + curr * this.precalc[this.counter — idx],那么处理 1 亿行的时间从 95 秒减少到 75 秒。这不是我们想要计算的,但是它显示了通过优化 JS 中的数学可以节省多少时间。

我们学到了什么

  • UDTFs 是实现逻辑的一种自然方式,就像 Claire & Brittany 提出的难题中所要求的那样
  • 这个 UDTF 不仅使用起来更优雅,而且在对 10M 行进行基准测试时,它比纯 SQL 更快。
  • 在 JS UDF 中优化数学可以带来巨大的性能提升。
  • SQL 窗口函数很酷,并由 SQL 优化器进行了优化。
  • 可伸缩性以奇怪的方式工作:有时增加 10 倍的数据会使整个事情花费的时间减少 3 倍。

对于 10 倍的数据,JS UDTFs 花费了 10 倍的时间,但是 SQL 窗口函数花费了 1/3 的时间(图片由作者提供)

后续步骤

  • 爪哇 UDTF 的表现会更好吗?雪花发生了很多事情,包括 UDF。敬请关注。
  • 检查衰减评分函数的真实用例,如轨道模型中的所示。

轨道模型中的衰变计分。

  • 布列塔尼深入轨道模型:

https://www.brittanybennett.com/post/replicating-the-orbit-model-an-exercise-in-measuring-movement-power

  • TJ 墨菲第一反应:

想要更多吗?

  • 用一个雪花免费试用账户试试吧——你只需要一个电子邮件地址就可以开始了。

我是 Felipe Hoffa,雪花的数据云倡导者。谢谢你和我一起冒险。你可以在推特和 LinkedIn 上关注我。查看 reddit.com/r/snowflake的最有趣的雪花新闻。

SQL 与 SQLAlchemy 的关系

原文:https://towardsdatascience.com/sql-relationships-with-sqlalchemy-ee619e6f2e8f?source=collection_archive---------15-----------------------

手动配置如何简化查询

由 Unsplash 上的 Shubham Dhage 拍摄的照片

几周前,我写了一篇关于利用实体框架的力量来最小化复杂的 Linq 查询的文章。本质上,解决方案是为数据库中的每个表创建配置,然后定义配置中表之间的关系。这并不是一件容易的事情。事实上,这有点令人困惑。但是这让我想到,是否可以使用 SQLAlchemy for Python 来完成类似的事情。

形势

在我们的场景中,我们有一个包含三个表的数据库,即 Application、ApplicationRunner 和 ApplicationLog。应用程序表包含一个名为 ApplicationRunnerId 的外键。该键在 Application 和 ApplicationRunner 表之间创建一对一的关系。至于 ApplicationLog 表,它保存了应用程序表的外键,指定了一对多关系。

看看我们当前的代码,所有确定表属性和关系的繁重工作都是由 SQLAlchemy 自动处理的。

至于获取数据,我们要么手动查询每个表,要么使用连接方法。为了简单起见,我们手动查询每个表。

有哪些不同的做法

与其偷懒让 SQLAlchemy 做所有艰苦的工作,我们可以采取代码优先的方法,为我们的表创建手动配置。

在我们开始修改表类之前,我们需要导入一些额外的东西。第一个将允许我们为表属性定义不同的类型,而第二个为我们提供了在表之间创建关系的功能。

from sqlalchemy import Column, ForeignKey, Integer, String, Numeric, DateTime, ForeignKey, CHAR, Table
from sqlalchemy.orm import relationship

导入后,我们可以通过定义所有属性和设置主键来开始处理应用程序表。属性类型将与数据库中的字段类型相匹配(INT 将是整数,VARCHAR 将是字符串,等等。).

class Application(Base):__tablename__ = "Application"ApplicationId = Column(Integer, primary_key=True)ApplicationName = Column(String(100), nullable=False)

接下来是 ApplicationRunner 表。

class ApplicationRunner(Base):__tablename__ = "ApplicationRunner"ApplicationRunnerId = Column(Integer, primary_key=True)ApplicationRunnerName = Column(String(50), nullable=False)

在继续之前,我们需要在这两个表之间创建一对一的关系。首先,我们需要向应用程序表添加一个外键。

ApplicationRunnerId = Column(Integer, ForeignKey("ApplicationRunner.ApplicationRunnerId"))

然后,我们将添加一个新的 class 属性来创建与 ApplicationRunner 表的关系。这种关系很特殊,因为我们将利用惰性联合加载。从本质上讲,延迟加载是指查询返回的对象没有加载相关的对象。一旦引用了相关对象,将运行另一个 SELECT 语句来加载所请求的对象。至于“joined ”,它向初始选择添加了一个连接,以保持所有内容都在同一个结果集中。

Runner = relationship("ApplicationRunner", lazy="joined")

我们需要做的下一件事是向 ApplicationRunner 表添加一个关系。这样做时,我们需要确保这个关系将反向引用 ApplicationRunner 表,并且它不需要对象列表,因为它是一对一的关系。

ApplicationRelationship = relationship("Application", backref="ApplicationRunner", uselist=False)

现在我们已经定义和配置了这些表,我们可以继续到 ApplicationLog 表。就像我们对前两个表所做的那样,首先需要定义属性和主键。

class ApplicationLog(Base):ApplicationLogId - Column(Integer, primary_key=True)ApplicationLogMessage - Column(String(250), nullable=False)

同样,为了创建关系,这个新表中需要一个外键。

ApplicationId = Column(Integer, ForeignKey("Application.ApplicationId"))

最后,ApplicationLog 表的一对多关系如下所示。

ApplicationRelationship = relationship("Application", backref="ApplicationLog", uselist=True)

但是对于应用程序表,它会是这样的。

Log = relationship(“ApplicationLog”, lazy=”joined”)

最终设置好一切后,我们可以运行一个查询来获取数据。

application = session.query(Application).filter(Application.ApplicationId == command.ApplicationId).one()

从应用程序表中获取数据很容易。

print(application.the_attribute)

但是,如果您希望从 ApplicationRunner 表中访问数据:

print(application.ApplicationRunner.the_attribute)

最后,要查看 ApplicationLog 表中的记录,需要一个 FOR 循环。

for item in application.ApplicationLog:print(item.the_attribute)

赞成还是反对

很快,您会注意到设置配置比让 SQLAlchemy 自动为您处理要花费更多的时间。就像在实体框架中一样,启动和运行东西也可能有点混乱/复杂。然而,这个过程并不全是坏事。通过使用代码优先的视图来配置表,它消除了 SQLAlchemy 的一些黑箱,将更多的权力交还给开发人员。尽管如此,它确实在查询端制造了更多的神秘,因为您看不到额外的连接或选择。

结束语

总而言之,我认为这是一次非常有启发性和有趣的经历。它明确回答了我关于 SQLAlchemy 能力的问题。然而,与此同时,它也带来了一些关于性能的新问题。作为编写大量需要快速请求的 API 的人,这种查询和配置是明智的吗?让我知道您对 SQLAlchemy 和在其中配置表关系的想法。在以后的文章中,我计划探索这些性能问题。直到下一次,编码快乐,干杯!

用我的 每周简讯 免费阅读我的所有文章,谢谢!

想看完介质上的所有文章?成为中等 成员 今天!

看看我最近的一些文章:

https://python.plainenglish.io/searching-for-text-in-those-annoying-pdfs-d95b6dc7a055 https://miketechgame.medium.com/one-year-of-writing-on-medium-d4d73366e297

参考资料:

https://stackoverflow.com/questions/16433338/inserting-new-records-with-one-to-many-relationship-in-sqlalchemy https://blog.theodo.com/2020/03/sqlalchemy-relationship-performance/

SQL —了解如何回滚事务中的查询

原文:https://towardsdatascience.com/sql-rolling-back-statements-with-transactions-81937811e7a7?source=collection_archive---------16-----------------------

要么执行所有查询,要么一个都不执行

这台机器是为处理交易而建造的,就像你这篇文章之后的代码一样(图片由大卫·卡尔波尼在 Unsplash 上提供)

尽管有错误,事务负责保证数据的有效性,它们是 SQL 工具箱中的重要工具。事务中的所有查询要么成功,要么全部失败;如果最后一个失败,那么之前的查询将回滚(撤消)。在本文中,你将学习如何使用事务,但首先我们将讨论何时使用事务:

0.何时使用事务

在某些情况下,您需要在数据库中执行两个不能同时完成的操作(比如将合并到、更新到,或者删除到),但是它们仍然相互依赖。如果他们中的任何一个失败了,他们都不会成功。在 SQL 中使用事务就可以做到这一点;如果一个事务失败,则回滚以前的事务。

1.设置

让我们用一个例子来说明。在我们的披萨公司,我们有披萨菜单桌和披萨饼桌。每周我们都会收到这两个表格的文件。我们的目标很简单;我们必须将每周文件插入表格中。面临的挑战是,如果数据库中有价格,我们只能在菜单上有比萨饼。让我们创建表格:

2.用错误的方法解决它

错误的做法是将数据插入到两个表中:

你会注意到最后一条记录中的错误;我们在只允许浮点数的地方插入一个字符串。如果我们执行上面的代码,我们会注意到第一次插入成功了,第二次插入失败了。这不是我们想要的,因为我们现在菜单上有比萨饼,但没有价格。

❌ 我们的披萨已经满了,但是我们的披萨不见了!

3.拯救交易!

当我们使用事务和 try-catch-block 时,我们将得到我们想要的。让我们检查代码,然后解释。

上面的代码从 TRAN 开始。这启动了一个事务事务有两种可能的执行方式:提交回滚。从开始 TRAN 到提交 TRAN 或回滚 TRAN 之间执行的每个命令都被“记住”。一个提交确认所有已被记忆的命令。一个回滚撤销所有命令。

在上面的代码中,我们将 insert 语句包装在一个 try 中。如果他们中的任何一个失败了,我们就会在 CATCH 块中结束。如果我们在那里结束,我们回滚所有事务。 ✅ 如果我们执行上面的代码,两个表都是空的;两次插入均未成功

回去走另一条路!(图片由罗杰·布拉德肖在 Unsplash 上拍摄)

4.抛出一个交易

您的代码不一定要在事务中利用回滚;你可以自己决定:

在上面的代码中,我们将数据插入到 PizzaMenu 和 PizzaPrices 中。然后我们检查是否有菜单上所有比萨饼的价格。如果不是这样,我们抛出一个错误,触发回滚。否则我们提交我们的事务。执行上面的代码不会插入任何数据,因为我们已经注释掉了第 18 行。

5.保持你的桌子快速

一旦开始一个事务,执行操作的表就会被锁定。锁定的表不可编辑,因此为了保持表的速度,非常重要的一点是,一旦事务开始,它应该尽快提交回滚

结论

事务是您工具箱中的一个很棒的新工具,就像下面这些一样:

  • 删除到另一个表格
  • 更新到另一个标签页 le
  • 在一条语句中插入、删除和更新
  • 更新选择一批记录
  • 版本控制你的数据库

编码快乐!

—迈克

页(page 的缩写)学生:比如我正在做的事情?跟着我!

SQL 技能(如果掌握的话)会让你获得更好的数据科学机会

原文:https://towardsdatascience.com/sql-skills-if-mastered-will-get-you-better-data-science-opportunities-9f912d4f88d3?source=collection_archive---------9-----------------------

如何使用 SQL 让您的职业生涯更上一层楼

在 Unsplash 上由 Austin Distel 拍摄的照片

无论您处理多大规模的数据,分析数据通常都很复杂。大多数数据科学家和程序员一直在寻找方法和工具来最小化庞大的长日志文本,以使理解更容易和准确。许多人认为,当他们达到国家级别时,他们只需要升级他们的数据技能——银行,也许是好莱坞,希望是联邦调查局。但事实是所有与数据库一起工作的东西,在那里存放着大量的文件和信息。

我是这么说的:你在麦当劳买一个奶酪汉堡,扫描你的信用卡,勉强提交你的身份证,然后幸运的是,你赢得了一张免费优惠券,上面有兑换地点和截止日期。从一次购买中得到的所有交易和信息都被存入一个数据库。麦当劳每天为 6800 万人提供服务,所以你可以想象存储、处理和检索的数据量。

我认为 SQL 是让你生活更美好的语言。SQL 代表结构化查询语言。它是一种特定于领域的语言,用于数据科学和编程,以导入、管理、处理和修改存储在数据库中的数据安排。使用 SQL,您可以轻松地管理大量原始数据,并无缝地对其进行分析。

SQL 帮助您以最简单快捷的方式排列数据,并通过 SQL 模块、库和预编译器将其嵌入到其他语言中。我们将探究 SQL 对于所有数据和计算机科学家来说必不可少的关键原因。

SQL 技能是你提升职业素养所需要的

你拥有的技能足以保证你有更好的机会吗?你认为你能跑赢同行业的其他个人吗?从我进行的采访来看,这些问题的答案通常是“不”——这是大多数程序员和数据科学家不断失去机会的主要原因——缺乏熟练程度。

根据 Payscale 的研究,数据分析师的平均年薪超过 55,000 美元。

很简单,更好的专业性=更多的钱。

技术在不断发展,跟上新技能的压力令人沮丧。超过 50%的开发人员使用 SQL,所以在这个行业获得并保持职业生涯是至关重要的。对于数字化世界中的非技术专业人员来说,获得 SQL 及其应用程序的标准知识从来都不是坏事。

SQL 和数据分析技能需求旺盛

我喜欢 SQL,因为不是每个开发人员都知道这个技能,不像 C++或 python。因此,对于掌握 SQL 的其他开发人员来说,这是一个优势。随着雇主寻找具有新思想、新观点和现代数据分析方法的专业人士,具有 SQL 知识的专业人士将会很有优势。事实是他们已经绝望了,所以展示额外的技术技能会让你比其他开发者更有优势。

随着大多数行业现在采用更好的技术视角来使工作更好、更准确,产生的数据量持续快速增长。2018 年,人类每天创造了 2.5 艾字节的数据,预计到 2025 年将达到惊人的 463 艾字节。从营销到教育,到医疗保健,再到时尚,数据科学家通常需求量很大,而拥有 SQL 技能的专业人士需求量更大。

SQL 是最容易使用的语言之一

有了这么多可供选择的编程语言,我为什么还要花时间学习 SQL 呢?嗯,它简单明了,界面友好。我们都熟悉安装 Oracle 12c(一种流行的数据库服务器)所需的长时间过程和大量命令行配置。微软 SQL 是必不可少的安装到您的电脑,并附带一个快速点击安装,提高生产力。

像 Java 和 Ruby 这样的语言需要记忆一系列程序来完成一项任务。然而,SQL 利用声明性语句来提取数据——这取决于查询是否有效。对于对编程和数据管理感兴趣的非技术人员来说,SQL 是一个很好的起点。最酷的事情是 SQL 处理简单的英语单词来检索信息,而其他编程语言处理字符串和二进制。

MS SQL Server 全文搜索的体系结构。知识共享许可。https://commons.wikimedia.org/w/index.php?curid=3177963

快速轻松地访问信息

随着数以百万计的企业利用数据库存储数据,他们也需要瞬间访问他们的信息。例如,一家鞋类公司需要显示在一段时间内购买的商品类型、性别、在线客户数量以及哪些商品还有库存。企业可以利用这些信息做出有利可图的决策和战术性的商业行动。

SQL 的 EMS 管理器、磁盘分区和数据挖掘工具提高了数据存储效率,促进了快速的信息检索。

有了 SQL,数据科学家和业务经理的工作都得到了简化,因为数据分析师不会面临以简单格式呈现信息的压力,业务经理也不必等待几个小时或几天来执行业务计划。

最终视角

有大量的学习平台提供关于 SQL 及其应用的深入教程——有些平台会授予你结业证书。你可以从这些平台开始,开始你的职业生涯。

  • SQL 简介:通过可汗学院查询和管理数据
  • 【Sqlcourse.com
  • 通过 edX 开发 SQL 数据库

无论您是刚刚开始编程,还是正在寻找一种新的语言来学习,或者向您的投资组合中添加更多技能,无论您拥有多少编程和数据科学经验,SQL 都是简单明了的。

资源:

https://blog.sqlizer.io/posts/sql-43/

SQL 跟踪创业公司,首次公开募股,等等:Crunchbase 企业与雪花

原文:https://towardsdatascience.com/sql-to-track-startups-ipos-and-more-crunchbase-pro-with-snowflake-6c24175a0d50?source=collection_archive---------29-----------------------

借助雪花数据市场中的 Crunchbase,您可以使用 SQL 来跟踪并购、收购、首次公开募股以及这些故事背后的关键人物。

2020 年,全球超过 500 家数据和分析领域的公司被收购。你能猜出这些公司所在的前三个城市吗?

第三名是伦敦,第二名是纽约。排名第一的是旧金山,2020 年数据分析领域有 33 家公司被收购。

现在,你如何获得分析背后的原始数据呢?嗯,有一家公司在追踪并购和首次公开募股的疯狂世界。这是一个令人难以置信的数据集,包括公司、投资者、融资数据以及这一切背后的关键人物。我喜欢 Crunchbase 的一点是,他们希望让所有人都能获得这些数据。

他们在自己的网站上提供这个数据集,你可以免费获取其中的大部分。如果你想更深入,你可以获得 pro 订阅,但如果你想完全访问数据云中每天更新的这些数据呢?好吧,Crunchbase 已经可以在雪花数据市场买到了。你所需要做的就是进入你的雪花账户,找到 Crunchbase 列表并请求访问 premium 数据集。一旦 Crunchbase 批准了您的请求,您会发现数据就在那里,每天更新,没有任何麻烦。这就是数据云的力量。

这就是我如何运行我的查询,并发现 Crunchbase 跟踪了 2020 年期间收购的大约 8000 家公司。这些公司中有 7%属于数据和分析领域:

2018 年与 2020 年的趋势

对比 2018 年和 2020 年的趋势:

  • 与两年前相比,今年跟踪的收购数量大大减少。
  • 医疗保健、金融服务和科学与工程收购在排名中上升。
  • 制造业、媒体和娱乐业下滑。

董事会的多元化

我们可以继续深入。例如,这些公司中有多少有女性董事会成员?只有 16%,比 2010 年的 9%要好。

2010 年与 2020 年相比:至少有一名女性董事会成员的数据和分析公司的百分比

但是让我们来谈谈可能性。一旦您在 Snowflake 中有了这些数据,您就可以使用 SQL 对其进行探索,应用您最喜欢的可视化工具对其进行可视化,或者将其与您的 Salesforce 数据进行链接以增强您的销售流程。

可能性是无穷无尽的,数据云可以助您一臂之力。

问题

每个类别在 2020 年收购的公司

select sum(price_usd), count(*) c, cat.value category
from acquisitions a
join organizations b
on a.acquiree_uuid=b.uuid, table(split_to_table(category_groups_list, ',')) catwhere acquired_on between '2020-01-01' and '2021-01-01'
group by category
order by 2 desc nulls last;

数据和分析公司在 2020 年收购了每个城市

-- Data & Analytics companies aquired 2020 per city
select sum(price_usd), city, country_code, count(*) c --, acquiree_name, acquirer_name, city
from public.acquisitions a
join organizations b
on a.acquiree_uuid=b.uuid
where acquired_on between '2020-01-01' and '2020-12-31'
and category_groups_list like '%Data and Analytics%' 
group by 2, 3
order by c desc

董事会成员为女性的被收购公司的百分比

with acquired_da as (select price_usd, acquiree_name, acquirer_name, city, acquiree_uuidfrom public.acquisitions ajoin organizations bon a.acquiree_uuid=b.uuidwhere extract(year from acquired_on) = 2010and category_groups_list like '%Data and Analytics%' 
)select female_board_member, count(*) c, c/sum(c) over() percent
from (select org_name, boolor_agg(gender='female')::varchar female_board_memberfrom (select person_name, org_uuid, gender, org_namefrom jobs ajoin people bon a.person_uuid=b.uuidjoin acquired_da con a.org_uuid=c.acquiree_uuidwhere job_type = 'board_member')group by 1order by 2
)
group by 1
;

结束语

通过这些查询,您可以看到将行业内发生的事情和您关心的事情联系起来是多么容易。在这篇文章中,我关注的是数据和分析领域,但是还有很多内容有待发现。

我会继续在 Twitter 帖子中添加更多关于 Crunchbase 数据集的有趣发现,请分享您的想法:

想要更多吗?

我是 Felipe Hoffa,雪花的数据云倡导者。谢谢你和我一起冒险。你可以在 Twitter 上关注我,并查看 reddit.com/r/snowflake上最有趣的雪花新闻。

https://github.com/Snowflake-Labs/awesome-snowflake

SQL——了解索引如何在幕后加速您的查询

原文:https://towardsdatascience.com/sql-understand-how-indices-work-under-the-hood-to-speed-up-your-queries-a7f07eef4080?source=collection_archive---------18-----------------------

不再需要等待缓慢的查询完成

如何在这些记录行中快速找到我们的数据?(图片由杨奇煜·巴拉尔在 Unsplash 上拍摄)

有没有想过数据库引擎如何快速返回你的查询数据?它如何能在瞬间搜索许多表格和数百万条记录?本文探讨了数据库引擎是如何工作的,并揭示了如何以最佳方式设计表和索引。不再需要等待查询完成!

像往常一样,我们将首先建立一个示例,创建一些样本数据,这样我们就有东西可以使用了。然后,我们将了解数据库引擎在没有索引的情况下如何运行。然后我们将添加索引来加速我们的查询,演示您也可以这样做。在本文结束时,您将:

  • 了解什么是指数
  • 理解指数的类型及其区别
  • 理解指数是如何工作的
  • 知道在什么情况下使用什么类型的索引

请注意,在本文中,我们使用的是 SQL Server,但该原则适用于许多其他关系数据库,如 Postgres 和 MySQL,例如语法可能会有所不同。让代码!

设置

为了说明本文中的代码,我创建了一个很多应用程序都使用的表格。它拥有 2000 万条用户信息记录,可用于注册新用户、登录时检查密码以及更改用户信息。我已经用 Python 生成了这个表,使用了本文 中描述的超快速插入方法。创建表的代码如下所示:

CREATE TABLE [dbo].[Users] ([Created] DATETIME NOT NULL DEFAULT GETUTCDATE(), [Modified] DATETIME NOT NULL DEFAULT GETUTCDATE(), [FirstName] NVARCHAR(100) NOT NULL, [LastName] NVARCHAR(100) NOT NULL, [UserName] NVARCHAR(100) NOT NULL, [Password] NVARCHAR(100) NOT NULL, [Age] INT NULL
);

我们的用户表(图片由作者提供)

如您所见,它包含一些与用户相关的列(最后 5 列)和两个跟踪记录创建和更新时间的列。这张桌子有一些问题,我们将在这篇文章中解决。

假设我们的 Users 表用于像 Reddit 这样的网站。每次登录时,我们都需要检查用户名和密码。此外,有时用户改变他们的电子邮件,用户名或密码,所以我们也需要更新。此外,我们需要能够通过插入新记录来添加新用户。如何在这么大的表中进行快速查询?

垃圾堆

我们目前已经用最笨最慢的方式设计了我们的桌子。当我们寻找一个特定的用户时,我们的表必须查看表中的每一条记录。这种表称为堆表,检查表中的每条记录称为表扫描。想象一下,去一家酒店,检查每一个房间,然后确定哪一个是你的!非常低效。

堆的精确表示(图片由 Neonbrand 在 Unsplash 上拍摄)

询问

让我们尝试按姓氏列查找记录:

SELECT *
FROM Med.dbo.UsersHeap WITH (NOLOCK)
WHERE LastName = 'LastName123123'

执行上述查询将花费相当长的时间;数据库引擎必须扫描表中的每条记录。它不能在第一个找到的记录处停止,因为可能有更多的用户具有相同的姓氏。此外,数据是无序的,所以记录可以在任何位置(任何行号)。

让我们分析一下执行计划:

从堆表中选择记录的执行计划(图片由作者提供)

您将看到数据库引擎执行了一次表扫描,这几乎花了一秒钟的时间!当您将鼠标悬停在最右侧的块上时,它会显示下图:

执行计划详情(图片由作者提供)

您将看到它实际上读取了所有 2000 万条记录,但只返回了 2 条。这些类型的堆表和表扫描效率非常低,不应该使用。我们可以通过增加一个索引来提高很多。

聚集索引

让我们向表中添加一个聚集索引:我们将向表中添加一个存储主键的新列;每行具有唯一值的整数。然后,这些值被分类并物理存储在一个树状结构中。

从根部开始,沿着这个树状结构向上(图片由 Max Nayman 在 Unsplash 上提供)

它是如何工作的

但是数据库引擎如何使用这个索引来更快地检索行呢?让我们再次使用酒店的例子:你需要找到房间 E512。

  • 字母 E 表示你必须去酒店的东翼
  • 第一个数字(5)表示我们必须去 5 楼
  • 一旦我们走出电梯,我们看到一个标志,上面写着 1-20 号房间在左边,21-40 号房间在右边。因为房间已经订好了,所以我们找房间时不需要找太远!

数据库引擎的工作方式很像这样。我们不需要访问酒店的每一个房间并检查是否是我们的,而是使用树状结构的索引来更快地引导我们到达目的地。唯一的区别是,数据库引擎使用更多的分支,而不是我们使用的三个分支(东翼,5 楼,大厅右侧)。

创建索引

让我们来收拾桌子:

CREATE TABLE [dbo].[Users] ([Id] INTEGER IDENTITY PRIMARY KEY NOT NULL, [Created] DATETIME NOT NULL DEFAULT GETUTCDATE(), [Modified] DATETIME NOT NULL DEFAULT GETUTCDATE(), [FirstName] NVARCHAR(100) NOT NULL, [LastName] NVARCHAR(100) NOT NULL, [UserName] NVARCHAR(100) NOT NULL, [Password] NVARCHAR(100) NOT NULL, [Age] INT NULL
);

神奇在二线;当您提供主键列时,数据库引擎会自动创建聚集索引。IDENTITY 部分将为每个记录生成一个新的整数。我们的 Id 栏是新的。新表如下所示:

带有聚集索引的用户表(作者图片)

询问

现在我们可以通过 Id 列检索记录。假设我们想要检索 Id = 42 的记录:

SELECT *
FROM Med.dbo.Users WITH (NOLOCK)
WHERE Id = 42

当我们执行这个时,数据库引擎使用我们新创建的索引;这是非常非常快,因为它使用了类似酒店的方法。我们还可以在执行计划中看到这一点:

我们的查询的聚集索引查询计划(图片由作者提供)

看到我们正在使用一个聚集索引查找了吗?这表明我们正在使用聚集索引。也要注意时间:0.000 秒;真快!我们还可以在该图块的执行细节中查看这一点:

聚集索引查找的执行详细信息(作者图片)

看到我们只读了一行吗?这与我们的表格扫描相比有很大的不同!但是如果我们想使用另一个列来过滤呢?

非聚集索引

好的,主键在 Id 列上,我们可以使用聚集索引很快找到我们的记录。我们如何优化在其他列上过滤的查询?对于这些列,我们可以创建一个非聚集索引。

蜘蛛侠使用书中的索引查找 SQL 术语的图片(图片由 Raj 在 Unsplash 上提供)

它是如何工作的

非聚集索引的工作方式很像书中的索引。这些包含某些单词以及这些单词的使用位置。在一本经济学书中,你可能会在第 42、119 和 246 页看到“通货膨胀”一词。非聚集索引的工作方式非常类似:它将列中的所有值作为键,并注册所有相应的 id(来自聚集索引)。非聚集索引需要聚集索引才能运行。这些键-值-对被排序并存储在它们自己的树状结构中,因此我们可以快速定位它们。这个操作被称为步进扫描

首先,我们将创建索引:

CREATE NONCLUSTERED INDEX ix_users_lastname ON Med.dbo.Users(LastName)

您在 LastName = 'Cartman '上过滤的图像:

  1. 然后,非聚集索引将执行键查找:它将遍历树,查找名为‘Cartman’的键。它将在我们的表中找到 3 个具有这个姓氏的记录;他们有编号 4,16 和 333。
  2. 在步骤 2 中,我们将使用聚集索引,执行索引查找,返回实际记录。让我们看看这是怎么回事!

询问

现在,我们可以通过名称列检索记录。假设我们想要检索 LastName = 'LastName456456 '的记录:

SELECT *
FROM Med.dbo.Users WITH (NOLOCK)
WHERE LastName = 'LastName456456'

这个查询也非常快;正如您在下面的执行计划中看到的,所有操作都在 0.000 秒内完成:

我们的非聚集索引查询的执行计划(图片由作者提供)

您将看到它首先执行键查找;它查找我们的列的值并返回 id。然后这些 id 会在索引搜索中使用。这两个索引一起工作保证了用户友好的、超快的查询!

结论

在这篇文章中,我希望通过添加索引来加速你的表。本文主要关注创建索引和从表中检索数据。在以后的文章中,我们将讨论什么时候创建索引实际上会损害性能,以及如何最好地插入数据以防止频繁更新索引。请关注我,保持联系!

同时,看看我的关于各种编程相关话题的其他文章,比如:

  • 删除进入另一个表
  • 更新到另一个标签页 le
  • 在一条语句中插入、删除和更新
  • 更新选择一批记录
  • 保存上插
  • 插入唯一表格
  • 从另一个表中插入具有连接 id 的值

编码快乐!

—迈克

页(page 的缩写)学生:比如我正在做的事情?跟我来!

SQL —更新到另一个表中

原文:https://towardsdatascience.com/sql-update-into-another-table-bfc3dff79a66?source=collection_archive---------20-----------------------

在一条语句中更新一个表中的记录,并将更新后的记录插入到另一个表中

我们的桌子上有些工作要做(图片由梅布尔·安伯在像素上拍摄)

“我为什么需要这样的查询?”将更新为语句是有利的,主要有两个原因:

  • 语句是原子;要么两者都发生,要么什么都不发生,即如果更新或插入失败,它将回滚更改。
  • 它更便宜:你的数据库只需要查找一次记录。或者,执行单独的插入和删除需要两次查找
  • 吹牛的权利:给你的老板和同事留下深刻印象

深信不疑?“给我看一些代码!”。好的,但是首先我们必须设置一些表来演示查询。让我们编码:

设置:创建一些包含数据的表

想象一下,我们有一个有很多文章的新闻网站。如果用户注意到某篇文章中有拼写错误,他/她可以通过网站提交错误。这些错误存储在一个名为 SpellingErrors 的表中。我们将存储包含错误的文章的 Id,以及消息和报告者的电子邮件地址。我们存储了电子邮件地址,以便联系记者提问。

当我们的作者之一修复拼写错误,我们更新记录设置电子邮件地址为空;我们不再需要它,我们想保证记者的隐私。此外,我们想保留一些关于错误的信息,这样我们就可以可怕地惩罚犯了很多错误的作者。为此,我们将一些数据存储到一个名为 SpellingErrorsHistory 的表中。让我们首先创建表并插入一些错误。

用一些记录创建我们的表

执行这些查询。如果您从拼写错误表中选择,您将看到以下内容:

我们的拼写错误表

执行我们的查询

让我们开始创建查询吧!让我们来看看这个查询,然后解释它在做什么。

我们的一个作者已经修复了 Id 为 1 的错误。我们将把它存储在一个名为@targetId 的变量中。然后,我们更新 SpellingErrors 表并将 ReporterEmail 设置为 NULL,其中 Id 等于@targetId (1)。使用输出,我们可以访问已经从 SpellingsErrors 表中更新的列。我们将一些列以及一个额外的列(SolvedMessage)输出到 SpellingErrorsHistory 表中。还要注意,我们没有在这个表中保存用户的电子邮件数据。

恭喜你,你成功了!这是一个简单、安全的查询,允许您同时执行两个查询。

结论

使用这个查询,我们可以执行原子操作,更新一个表中的记录,同时将这些更新的记录插入到另一个表中。另请查看:

  • 删除到另一个表中
  • 使用交易撤销查询
  • 在一条语句中插入、删除和更新
  • 更新选择一批记录
  • 版本控制你的数据库

编码快乐!

—迈克

页(page 的缩写)学生:比如我正在做的事情?跟我来!

SQL —在一个查询中更新选择

原文:https://towardsdatascience.com/sql-update-select-in-one-query-b067a7e60136?source=collection_archive---------12-----------------------

选择一批记录,同时更新它们

如何记录你选择了哪些记录?(图片由像素上的凯拉什·库马尔拍摄)

本文将向您展示如何在一个查询中同时更新和选择记录。这种技术对于处理具有状态的成批记录特别实用,因为它确保您只选择每条记录一次。更新选择的优势包括:

  • Atomic:该语句要么成功执行 select 和 update,要么不执行任何操作。更新未处理的记录或选择具有错误状态的记录没有变化。
  • 性能:您的数据库只需要执行一个操作,而不是多个独立的操作
  • 冷静:用这个令人敬畏的查询给别人留下深刻印象

我们将通过一个实际例子来展示这个查询的强大功能。首先,我们将设置一些表,并用数据填充它们。

0.目标

你经营一家公司,允许你的用户写漫画并提交给出版物(例如杂志或报纸)。在漫画出版之前,出版商必须同意该漫画。因为他们是非常忙碌的人,一部漫画可能需要一段时间才能被批准。

我们决定在一个名为 ComicStatus 的表格中记录漫画的进度。该表包含漫画名称、出版物和漫画的状态。状态反映了漫画在处理、提交和被出版接受或拒绝的过程中的状态。让我们摆好桌子。

创建包含所有数据的 ComicStatus 表

每隔几分钟,我们就会运行一个执行以下操作的流程:

  • 获取所有新漫画并提交
  • 拿着提交的漫画,检查出版社是否已经处理了它们
  • 如果漫画已处理:将状态更新为“已接受”或“已拒绝”

现在让我们看看 select-update 如何帮助我们改进流程。

2.更新状态时批量选择记录

出于开发目的,我们已经决定将这个过程限制在一次最多 5 幅漫画。我们将从处理这个问题的最明显的方法开始,然后慢慢地改进它。

我们将所有记录集中在一批中(图片由 Steven Lasry 在 Unsplash 上提供)

1 .愚蠢的方式

假设我们以如下方式每分钟运行我们的流程:

  1. 选择前 5 条记录新记录(状态为 0)
  2. 遍历所有记录,并将每一条记录提交给他们的发布
  3. 等待出版 API 批准漫画
  4. 将漫画的状态更新为已接受或已拒绝

由于我们的流程每分钟都在运行,而批准漫画可能需要一分钟以上的时间,因此我们选择同一记录两次。这可以很容易地通过快速更新状态来解决。

2.更好的方法

在提交记录之前,我们会更新所选记录的状态

  1. 选择前 5 条记录新记录(状态为 0)
  2. 将所选记录的前 5 名更新为状态 1(处理中)
  3. 遍历所有记录,并将每一条记录提交给他们的发布
  4. 等待发布 API 返回结果
  5. 将漫画的状态更新为已接受或已拒绝

这种方式仍然会产生错误。如果在第 1 步和第 2 步之间有新的记录插入到我们的表中会怎样?如果是这种情况,我们更新未处理的记录并错过更新已处理的记录。

3.更新-选择方式

我们选择我们想要的记录,并在同一个查询中更新它们的状态。

  1. 更新前 5 条记录新记录(状态为 0)选择它们
  2. 遍历所有记录,并将每一条记录提交给他们的发布
  3. 等待发布 API 返回结果
  4. 将漫画的状态更新为已接受或已拒绝

这样可以确保我们不会多次处理记录。我们的流程运行多长时间以及何时插入新记录都无关紧要。如何执行步骤 1?比你想象的容易:

就是这样!很短吧?我们首先更新状态为 0 的前 2 条记录,然后输出它们。该输出像常规选择一样工作。尝试连续多次执行此查询;您会注意到,您永远不会选择同一个记录两次,因为它首先更新状态。它将对我们的记录进行批处理,直到不再有状态为 0 的记录。

结论

使用这个 Update-Select 确保我们只更新我们选择处理的记录,并保证要么两者都成功,要么整个操作失败。它是你工具箱中的一个很棒的新工具,就像这些一样:

  • 删除到另一个表中
  • 更新到另一个标签页 le
  • 在一条语句中插入、删除和更新
  • 使用事务回滚查询
  • 版本控制你的数据库
  • Docker 适合绝对初学者

编码快乐!

—迈克

页(page 的缩写)学生:比如我正在做的事情?跟我来!

使用 PostgreSQL 的 SQL 视图

原文:https://towardsdatascience.com/sql-views-with-postgresql-493ba7aa0934?source=collection_archive---------14-----------------------

简化任务的聪明方法

在 Unsplash 上由 Arvind meena mina 拍摄的照片

SQL 是数据科学家的必备技能。由于许多公司将数据存储在关系数据库中,我们需要使用 SQL 来访问、查询和分析这些数据。

SQL 能够执行比基本查询更高级的任务。它提供了几个使 SQL 成为高效数据分析工具的函数。

下面可能是最简单的 SQL 查询。它选择表格中的所有行和列。

SELECT * FROM table_name;

随着任务复杂性的增加,查询的语法也变得更加复杂。此外,您可能需要查询多个表来获得所需的数据。因此,您的查询可能有几行代码。

您肯定不希望一遍又一遍地输入如此复杂的查询。一种选择是保存它并从那里复制。然而,SQL 提供了一种更好的方法,即 SQL 视图。

视图是数据库中存储的查询。我们可以像查询表一样查询视图。但是,除了 PostgreSQL 中的物化视图之外,视图不存储数据。

类比将帮助我们更好地理解视图的概念。假设您编写了一个 Python 脚本,该脚本读取一个 csv 文件,计算一些统计数据,然后返回结果。你需要不时地执行这项任务。如果您将脚本保存在您的工作环境中,您可以在需要执行任务时执行它。您不必每次都编写相同的脚本。

让我们做一些例子来演示如何使用视图。我用模拟数据创建了两个名为“订单”和“产品”的表。

select * from orders; order_id | product_id | order_qty |    date
----------+------------+-----------+------------101 |       1001 |         2 | 2021-01-04102 |       1423 |         1 | 2021-01-04103 |       1260 |         5 | 2021-01-13104 |       1590 |         5 | 2021-05-13105 |       1002 |         3 | 2021-01-13106 |       1600 |         2 | 2021-05-13 select * from product; product_id | price | description
------------+-------+-------------1001 |  2.50 | bread1002 |  1.90 | water1423 | 10.90 | icecream1260 |  5.90 | tomato1590 |  4.90 | egg1600 |  2.90 | milk

订单表包含产品 id、订单数量和日期信息。产品表包含产品的价格和描述。

以下查询计算每份订单的总金额。

SELECT O.order_id, (O.order_qty * P.price) AS order_amount                         FROM orders O 
LEFT JOIN product P on O.product_id = P.product_id 
ORDER BY order_amount desc; order_id | order_amount
----------+--------------103 |        29.50104 |        24.50102 |        10.90106 |         5.80105 |         5.70101 |         5.00

由于产品价格和订单数量在不同的表中,我们需要在这个查询中使用一个连接。

假设这是一个“高度复杂”的查询,我们不想每次都键入它。我们的解决方案是将其存储在视图中。

CREATE VIEW order_amounts AS
SELECT O.order_id, (O.order_qty * P.price) AS order_amount                         FROM orders O 
LEFT JOIN product P on O.product_id = P.product_id 
ORDER BY order_amount desc;

使用 create view 语句和查询创建视图。我们还需要为视图指定一个名称。

创建视图不会返回任何内容。我们可以像查询表一样查询这个视图。

SELECT * FROM order_amounts; order_id | order_amount
----------+--------------103 |        29.50104 |        24.50102 |        10.90106 |         5.80105 |         5.70101 |         5.00

我们接受金额高于 20 英镑的订单吧。

SELECT * FROM order_amounts
WHERE order_amount > 20; order_id | order_amount
----------+--------------103 |        29.50104 |        24.50

视图对于简化任务非常有用。我们用作示例的查询并不复杂。当我们执行涉及多重过滤、聚合和嵌套 select 语句的高度复杂的任务时,这些视图就派上了用场。

使用视图的另一个优点是可以更灵活地控制数据库访问。例如,表中的某些列可能包含只有特定员工才能访问的敏感信息。

当我们查询视图时,我们只能检索视图中存在的列,而不是视图中使用的表中的每一列。因此,视图允许我们授予对表的部分访问权。

我们可以使用 alter view 语句来更改视图的各种辅助属性。例如,列的名称可以更改如下:

ALTER VIEW order_amounts RENAME order_amount TO total_amount;SELECT * FROM order_amounts; order_id | total_amount
----------+--------------103 |        29.50104 |        24.50102 |        10.90106 |         5.80105 |         5.70101 |         5.00

为了删除视图,使用了 drop view 语句。它类似于用于删除表的 drop table 语句。

DROP VIEW order_amounts;SELECT * FROM order_amounts;
ERROR:  relation "order_amounts" does not exist

结论

SQL 视图是存储查询。它们允许我们通过保存复杂的查询以备后用来简化某些任务。这些查询可以看作是一个表,所以我们可以像查询表一样查询它们。

感谢您的阅读。如果您有任何反馈,请告诉我。

SQL 与 NoSQL 在 8 个例子中的对比

原文:https://towardsdatascience.com/sql-vs-nosql-in-8-examples-25aebcf49922?source=collection_archive---------3-----------------------

比较两者基本操作的实用指南

诺德伍德主题公司在 Unsplash 上拍摄的照片

关系数据库以带有标签的行和列的表格形式存储数据。尽管关系数据库通常为存储数据提供了一个不错的解决方案,但在某些情况下,速度和可伸缩性可能是一个问题。

大多数关系数据库管理系统使用 SQL(结构化查询语言)来管理以表格形式存储数据的数据库。NoSQL 指的是非 SQL 或非关系数据库设计。它仍然提供了一种有组织的方式来存储数据,但不是以表格的形式。

抛开对速度和可伸缩性的考虑,SQL 和 NoSQL 数据库都提供了通用而高效的数据查询方式。这对于数据库来说是必须的,因为可访问性也是至关重要的。

在本文中,我们将涵盖 8 个示例,演示如何查询 SQL 和 NoSQL 数据库。这些示例将涵盖如何:

  • 基于条件选择数据
  • 插入新项目
  • 更新现有项目
  • 应用聚合函数

我将在两个数据库中完成相同的任务,以便我们可以看到不同之处和相似之处。

我将使用 SQL 的 MySQL 和 NoSQL 的 MongoDB。在开始举例之前,让我们简要解释一下数据是如何存储在 SQL 和 NoSQL 中的。

SQL 以表格形式存储数据,表格中有带标签的行和列。NoSQL 数据库存储数据的常用结构是键值对、宽列、图形或文档。MongoDB 将数据存储为文档。MongoDB 中的文档由字段-值对组成。文档被组织在一个称为“集合”的结构中。打个比方,我们可以把文档想象成表格中的行,把集合想象成表格。

我在 MySQL 中创建了一个简单的表,并在 MongoDB 中创建了一个集合,其中包含一些汽车的特性及其价格。

这里有一个文件,确定了汽车系列中的一个项目:

{"_id" : ObjectId("600c626932e0e6419cee81a7"),"year" : "2017","make" : "hyundai","color" : "white","km" : 22000,"price" : 32000
}

在 SQL 中,一个数据点(在我们的例子中是一辆汽车)由一行标识。

+------+---------+-------+-------+-------+
| year | make    | color | km    | price |
+------+---------+-------+-------+-------+
| 2017 | hyundai | white | 22000 | 32000 |
+------+---------+-------+-------+-------+

示例 1

找到福特制造的汽车。

NoSQL(蒙古数据库):

我们将条件传递给 find 函数。“db”是指当前数据库,“car”是我们正在查询的集合。

> db.car.find( {make: "ford"} ).limit(1).pretty(){"_id" : ObjectId("600c63cf32e0e6419cee81ab"),"year" : "2017","make" : "ford","color" : "black","km" : 34000,"price" : 28000
}

福特生产的汽车不止一辆,但我使用 limit 函数只显示了一辆。

pretty 函数使输出更具可读性和吸引力。这是没有漂亮功能时的样子。

> db.car.find( {make: "ford"} ).limit(1){ "_id" : ObjectId("600c63cf32e0e6419cee81ab"), "year" : "2017", "make" : "ford", "color" : "black", "km" : 34000, "price" : 28000 }

SQL (MySQL):

我们选择所有列(*),并在 where 子句中指定条件。

mysql> select * from car-> where make = "ford"-> limit 1;+------+------+-------+-------+-------+
| year | make | color | km    | price |
+------+------+-------+-------+-------+
| 2017 | ford | black | 34000 | 28000 |
+------+------+-------+-------+-------+

示例 2

找到 2019 年福特生产的汽车。

NoSQL(蒙古数据库):

我们可以传递由逗号分隔的多个条件,以表示条件上的“与”逻辑。

> db.car.find( {make: "ford", year: "2019"} ).pretty(){"_id" : ObjectId("600c63cf32e0e6419cee81af"),"year" : "2019","make" : "ford","color" : "white","km" : 8000,"price" : 42000
}

SQL (MySQL):

它类似于前面的例子。我们可以使用 and 运算符在 where 子句中组合多个条件。

mysql> select * from car-> where make = "ford" and year = "2019";+------+------+-------+------+-------+
| year | make | color | km   | price |
+------+------+-------+------+-------+
| 2019 | ford | white | 8000 | 42000 |
+------+------+-------+------+-------+

示例 3

找出 2017 年福特或现代制造的汽车。

NoSQL(蒙古数据库):

我们首先将品牌的条件与“或”逻辑相结合,然后使用“与”逻辑与年份相结合。“$in”运算符可用于“或”逻辑。

> db.car.find( {make: {$in: ["ford","hyundai"] } , year: "2017"} ).pretty(){"_id" : ObjectId("600c626932e0e6419cee81a7"),"year" : "2017","make" : "hyundai","color" : "white","km" : 22000,"price" : 32000
}{"_id" : ObjectId("600c63cf32e0e6419cee81ab"),"year" : "2017","make" : "ford","color" : "black","km" : 34000,"price" : 28000
}

SQL (MySQL):

where 子句接受“in”操作符,因此我们可以指定类似于 NoSQL 的条件。

mysql> select * from car-> where make in ("ford","hyundai") and year = "2017";+------+---------+-------+-------+-------+
| year | make    | color | km    | price |
+------+---------+-------+-------+-------+
| 2017 | hyundai | white | 22000 | 32000 |
| 2017 | ford    | black | 34000 | 28000 |
+------+---------+-------+-------+-------+

实例 4

插入新项目。

NoSQL(蒙古数据库):

“insertOne”函数用于将单个文档插入到集合中。我们需要编写新文档的字段-值对。

> db.car.insertOne(
... {year: "2017", make: "bmw", color: "silver", 
...  km: 28000, price: 39000}
... ){"acknowledged" : true,"insertedId" : ObjectId("600c6bc79445b834692e3b91")
}

SQL (MySQL):

“插入”功能用于向表中添加新行。不像 NoSQL,我们不需要写列名。但是,值的顺序必须与表中列的顺序相匹配。

mysql> insert into car values -> ("2017", "bmw", "silver", 28000, 39000);Query OK, 1 row affected (0.03 sec)

实例 5

将品牌“宝马”更新为“宝马”。

NoSQL(蒙古数据库):

使用更新功能。我们首先传递指示要更新的文档的条件,然后传递更新后的值和 set 关键字。

> db.car.update(
... { make: "bmw" },
... { $set: { make: "BMW" }},
... { multi: true }
... )WriteResult({ "nMatched" : 5, "nUpserted" : 0, "nModified" : 5 })

我们需要使用 multi 参数来更新满足给定条件的所有文档。否则,只有一个文档得到更新。

SQL (MySQL):

我们使用如下的更新语句:

mysql> update car-> set make = "BMW"-> where make = "bmw";Query OK, 5 rows affected (0.05 sec)
Rows matched: 5  Changed: 5  Warnings: 0

实例 6

在查询数据库时,SQL 和 NoSQL 在数据聚合方面都非常通用。例如,我们可以很容易地计算出每个品牌的平均价格。

NoSQL(蒙古数据库):

我们使用聚合函数。

> db.car.aggregate([
... { $group: { _id: "$make", avg_price: { $avg: "$price" }}}
... ]){ "_id" : "hyundai", "avg_price" : 36333.333333333336 }
{ "_id" : "BMW", "avg_price" : 47400 }
{ "_id" : "ford", "avg_price" : 35333.333333333336 }

我们首先通过选择“\(make”作为 id,根据品牌对文档进行分组。下一部分指定了聚合函数(在我们的例子中是“\)avg ”)和要聚合的字段。

如果您熟悉 Pandas,语法与 groupby 函数非常相似。

SQL (MySQL):

group by 子句用于根据给定列中的类别对行进行分组。选择列时应用聚合函数。

mysql> select make, avg(price)-> from car-> group by make;+---------+------------+
| make    | avg(price) |
+---------+------------+
| BMW     | 47400.0000 |
| ford    | 35333.3333 |
| hyundai | 36333.3333 |
+---------+------------+

实施例 8

我们可以在聚合函数中实现条件。对于每个品牌,我们来计算一下 2019 年制造的汽车的平均价格。

NoSQL(蒙古数据库):

我们只需要添加匹配关键字来指定条件。

> db.car.aggregate([
... { $match: { year: "2019" }},
... { $group: { _id: "$make", avg_price: { $avg: "$price" }}}
... ]){ "_id" : "BMW", "avg_price" : 53000 }
{ "_id" : "ford", "avg_price" : 42000 }
{ "_id" : "hyundai", "avg_price" : 41000 }

SQL (MySQL):

我们使用 where 和 group by 子句,如下所示:

mysql> select make, avg(price)-> from car-> where year = "2019"-> group by make;+---------+------------+
| make    | avg(price) |
+---------+------------+
| BMW     | 53000.0000 |
| ford    | 42000.0000 |
| hyundai | 41000.0000 |
+---------+------------+

结论

我们已经讨论了 8 个例子,展示了处理 SQL 和 NoSQL 数据库的基本操作。

它们都提供了更多的函数和方法来创建更高级的查询。因此,它们也可以用作数据分析和操作工具。

感谢您的阅读。如果您有任何反馈,请告诉我。

SQL 与 NoSQL —连接操作

原文:https://towardsdatascience.com/sql-vs-nosql-join-operations-401f18a8a53b?source=collection_archive---------17-----------------------

比较两者中连接操作的实用指南

在 Unsplash 上拍摄的 Mineragua 苏打水

SQL(结构化查询语言)用于管理以表格形式存储数据的数据库,这些表格具有带标签的行和列。NoSQL 指的是非 SQL 或非关系数据库设计。它仍然提供了一种有组织的方式来存储数据,但不是以表格的形式。

NoSQL 数据库存储数据的常用结构是键值对、宽列、图形或文档。一个流行的 NoSQL 数据库是 MongoDB,它将数据存储为文档。

MongoDB 中的文档由字段-值对组成。文档被组织在一个称为集合的结构中。打个比方,文档可以看作是表中的一行,集合可以看作是整个表。

在本文中,我们将从连接操作的角度比较 SQL 数据库(MySQL)和 NoSQL 数据库(MongoDB)。我还写了一篇文章,演示如何执行查询 SQL 和 NoSQL 数据库的基本操作。

当我们从关系数据库中检索数据时,所需的数据通常分布在多个表中。在这种情况下,我们使用 SQL 连接来处理包括从两个或更多相关表中选择行的任务。

在 NoSQL 的情况下,一个项目(或数据点)的数据主要存储在一个集合中。然而,在某些情况下,我们可能需要跨越多个集合来获取我们需要的所有数据。

因此,连接查询对于这两种类型数据库都至关重要。

我准备了两个具有相同虚构数据的表和集合。第一个包含零售企业的客户信息。第二个包含关于这些客户所下订单的信息。

下面是客户和订单表中的一个条目(即行):

+---------+------+----------+--------+
| cust_id | age  | location | gender |
+---------+------+----------+--------+
|    1000 |   42 | Austin   | female |
+---------+------+----------+--------++----------+------------+--------+---------+
| order_id | date       | amount | cust_id |
+----------+------------+--------+---------+
|        1 | 2020-10-01 |  27.40 |    1001 |
+----------+------------+--------+---------+

下面是客户和订单集合中的一个文档:

{"_id" : ObjectId("600e120b44284c416405dd7e"),"cust_id" : "1000","age" : 42,"location" : "Austin","gender" : "Female"
}{"_id" : ObjectId("600e141d44e046eb7c92c4fe"),"order_id" : "1","date" : "2020-10-01","amount" : 27.4,"cust_id" : "1001"
}

这两个表通过 cust_id 列相互关联。以下示例包括需要使用联接操作的查询。我将在两个数据库中完成相同的任务,以便我们可以看到不同之处和相似之处。

示例 1

我们希望看到 40 岁以上的客户所下的订单。

NoSQL(蒙古数据库):

我们可以在聚合管道中使用“$lookup”关键字执行连接操作。聚合管道在 MongoDB 中非常有用,因为它们允许在一个管道中执行许多不同种类的操作,比如过滤、排序、分组、应用数据聚合等等。

在本例中,我们首先使用“$match”关键字根据客户年龄过滤文档,然后从 orders 表中选择符合过滤条件的文档。

> db.customer.aggregate([
... { $match: { age: {$gt:40} }},
... { $lookup: { from: "orders",
...              localField: "cust_id",
...              foreignField: "cust_id",
...              as: "orders_docs" }}
... ]).pretty(){"_id" : ObjectId("600e120b44284c416405dd7e"),"cust_id" : "1000","age" : 42,"location" : "Austin","gender" : "Female","orders_docs" : [{"_id" : ObjectId("600e141d44e046eb7c92c4ff"),"order_id" : "2","date" : "2020-10-01","amount" : 36.2,"cust_id" : "1000"},{"_id" : ObjectId("600e157c44e046eb7c92c50a"),"order_id" : "13","date" : "2020-10-03","amount" : 46.1,"cust_id" : "1000"}]
}

本地和外部字段指示用于连接值的字段名称。输出包含客户集合中符合指定条件的文档以及这些客户的订单。碰巧只有一位顾客年龄超过 40 岁,她有两份订单。

SQL (MySQL)

我们可以在一个选择查询中连接两个表,如下所示。

mysql> select orders.* -> from customer-> join orders-> on customer.cust_id = orders.cust_id-> where customer.age > 40;+----------+------------+--------+---------+
| order_id | date       | amount | cust_id |
+----------+------------+--------+---------+
|        2 | 2020-10-01 |  36.20 |    1000 |
|       13 | 2020-10-03 |  46.10 |    1000 |
+----------+------------+--------+---------+

在普通的 select 语句中,我们只写入要选择的列的名称。当我们连接表时,用表的名称指定列,以便 SQL 知道列来自哪里。

然后我们写下带有连接关键字的表名(例如,客户连接订单)。“on”关键字用于指示这些表是如何相关的。where 语句根据给定的条件筛选行。

示例 2

我们希望看到每个位置的客户的平均订单量。

NoSQL(蒙古数据库):

此任务要求联接两个集合,然后应用数据聚合。这两者都可以在聚合管道中使用“\(lookup”和“\)group”阶段来实现。

> db.customer.aggregate([
... { $lookup: { from: "orders",
...              localField: "cust_id",
...              foreignField: "cust_id",
...              as: "orders_docs" }},
... { $group: { _id: "$location", 
...             avg_amount: { $avg: "$amount" }}}
... ]){ "_id" : "Houston", "avg_amount" : 44.450000 }
{ "_id" : "Dallas", "avg_amount" : 34.591667 }
{ "_id" : "Austin", "avg_amount" : 33.333333 }

在“\(lookup”阶段的连接操作之后,我们通过选择“\)location”作为 id,基于位置对文档进行分组。下一部分指定了聚合函数(在我们的例子中是“$avg ”)和要聚合的字段。

SQL (MySQL)

我们在选择列时应用聚合函数。使用 group by 子句根据位置对结果进行分组。

mysql> select customer.location, avg(orders.amount) as avg_amount-> from customer-> join orders-> on customer.cust_id = orders.cust_id-> group by customer.location;+----------+------------+
| location | avg_amount |
+----------+------------+
| Austin   |  33.333333 |
| Dallas   |  34.591667 |
| Houston  |  44.450000 |
+----------+------------+

示例 3

在本例中,我们将在前面的示例中添加一个过滤标准。对于每个地点,我们来计算 30 岁以下客户的平均订单额。

NoSQL(蒙古数据库):

我们只需要在管道的开头添加一个“$match”阶段来应用过滤标准。

> db.customer.aggregate([
... { $match: { age: {$lt: 30} }},
... { $lookup: { from: "orders",
...              localField: "cust_id",
...              foreignField: "cust_id",
...              as: "orders_docs" }},
... { $group: { _id: "$location",
...             avg_amount: { $avg: "$amount" }}}
... ]){ "_id" : "Houston", "avg_amount" : 35.625000 }
{ "_id" : "Dallas", "avg_amount" : 34.591667 }
{ "_id" : "Austin", "avg_amount" : 36.000000 }

在“$match”阶段,我们指定条件以及要过滤的字段名。

SQL (MySQL)

筛选条件是通过在 select 语句中使用 where 子句添加的。

mysql> select customer.location, avg(orders.amount) as avg_amount-> from customer-> join orders-> on customer.cust_id = orders.cust_id-> where customer.age < 30-> group by customer.location;+----------+------------+
| location | avg_amount |
+----------+------------+
| Austin   |  36.000000 |
| Dallas   |  34.591667 |
| Houston  |  35.625000 |
+----------+------------+

由于行是在 group by 子句之前过滤的,所以我们可以使用 where 子句。如果我们需要应用基于聚合值的过滤器(例如 avg_amount > 35),应该使用 having 子句。

结论

我们需要从关系(SQL)或非关系(NoSQL)数据库中检索的数据可能分散到多个表或集合中。因此,全面理解连接操作非常重要。

我们已经做了三个基本的例子来演示 SQL 和 NoSQL 数据库中连接操作的思想和实现。

感谢您的阅读。如果您有任何反馈,请告诉我。

SQL 嵌套窗口函数

原文:https://towardsdatascience.com/sql-window-functions-64c26bd643fd?source=collection_archive---------16-----------------------

辅导的

嵌套的 Case 表达式:无限可能

卡尔·弗雷德里克森

不要用你自己的极限来定义我的宇宙的参数。—匿名

一个日落美得令人望而生畏。每当我看到它,我意识到我只受到视角和视野的限制。超越这些限制的是一个无限可能性的世界。在那个世界里,我是自由的。我可以自由想象,希望和梦想。我可以自由地绘制一条通向想象力创造力未知目的地的路线。我不受年龄、背景、性别或种族的限制。我的思想是画布,想法是原材料。所以,是用 SQL 窗口功能实现的。只有有限的视角和视野阻碍了他们创造性的用法。

为了突破自我强加的限制,我鼓励想象和实验。在这篇高级教程中,我通过将CASE Expression添加到 SQL 窗口函数中,打开了深入学习的大门。作为一个高级主题,我将介绍与学习相关的基础知识。如果你需要一本关于 SQL 窗口函数的初级读本,我推荐我以前写的一篇文章, SQL 窗口函数,一段爱恨交加的关系

基础知识

我们可以在任何允许有效表达式的语句或子句中使用CASE Expression。比如可以在SELECTUPDATEDELETESET等语句中,在PARTITION BYORDER BYWHEREHAVING等从句中使用CASE Expression利用语句和 SQL 窗口函数的能力始于语法。我们可以将CASE Expression包含在Window_Function()中,如下图所示。

语法、窗口函数和 CASE 表达式

使用窗口功能时,记住处理顺序事项很重要。首先执行OVER()子句,然后执行PARTITION BYORDER BYWindow_Function()ORDER BY子句确定Window_Function如何将计算、AVG(), SUM(), MAX()CASE Expression逻辑应用于PARTITION BY子句中的行。CASE Expression遍历这些条件,并在第一个条件评估为真时返回一个单值。所以,一旦条件为真,它将停止读取并返回结果。如果没有条件为真,它将返回 ELSE 子句中的值。

入门指南

重要的是要知道,在 Oracle、SQL Lite、SQL Server、MySQL、Postgres 和 Redshift 等数据库平台上有不同的 SQL 窗口函数实现。Window_Function()功能中CASE Expression的数量和支持可能因平台而异。下面的显示了Window_Function(), PARTITION BYORDER BY子句是否支持CASE Expression语法。表格不包含 不包含所有的 SQL 窗口函数。然而,我列出了一些你可能会遇到的更常见的问题。

SQL 窗口函数

虽然该表是一个很好的参考,但确定您的数据库平台是否支持 case 语句的一个更快、更简单的方法是在Window_Function中使用case when 1 = 1 then 1 else 0 end,如下图所示。

窗口函数,案例表达式测试

测试时,我建议分别测试语法的各个部分,以确定 SQL 平台是否支持该函数。如果查询执行,窗口函数支持CASE Expression

虽然有可能在PARTITION BY子句中使用CASE Expression,但我很少在项目中使用。所以,我不在本教程中涉及。话虽如此,我还是很快补充一句,不要让那限制你从探索它。您可能会发现它有用的多个实例。所以,继续被创意推动可能的极限。在本教程的剩余部分,我们将看看如何在Window_Function()ORDER BY子句中使用Case Expression

我们的数据

我们虚构示例中的数据包括每个州的商店收入数据。目的是使用SUM()窗口函数和CASE Expression添加三个额外的指标,即调整后的收入、新店收入和州收入。我们想衡量无销售税新店开业对一个州的公司收入的影响。我们可以编写单独的查询来获得答案,但是嵌套的CASE Expression是最高效的。在本教程结束时,您将看到如何添加指标,并对CASE Expression进行微小的修改。

也就是说,让我们深入研究 SQL Server 文本编辑器中显示在下面的代码。对于调整后的收入,CASE Expression执行且记录没有销售税时,Sales_Tax = NO,收入为预计收入的 95%。****

使用新店收入,CASE Expression执行时,记录是新店,New_Store = 'YES',我们计算收入。否则,case 语句返回值零。对于州收入,CASE Expression执行且记录没有销售税时,Sales_Tax = NO,收入为预计收入的 95%。

我可以通过修改CASE Expression来继续创建额外的指标。最后,Case Support列使用case when 1 = 1 then 1 else 0 end来测试Max()窗口函数是否支持 SQL Server 中的CASE Expression。成功的查询执行意味着CASE Expression被支持。不要担心填充Case Support列的值。目标只是确保查询语句运行。

排序依据:我们的方案

ORDER BY条款是Window_Function()中隐藏的超能力。她平常的用法掩盖了她真正的美丽和优雅。理解并掌握她的优点将会打开解决问题的新境界。让我们看一个例子来帮助解释我的意思。

我们场景中的数据包括一个计算机制造商的计算机销售交易的估计值。每年 12 月,部门销售主管都试图通过与OPEN_DATE达成交易来增加收入。根据经验,她知道在6 月OPEN_DATE的生意比其他月份更有价值。因此,她向她的数据团队发送了一个请求,要求提供一个列表,让将 6 月的交易排在每个州的最前面。当她收到清单时,她会将它分发给每个州的区域销售经理。数据分析师使用如下所示的 SQL 逻辑完成请求。

****说明:数据分析师使用**DENSE_RANK()**函数创建每个区域的优先级列表。OVER()子句创建执行空间。PARTITION BY子句将State数据分成不同的组。如果记录的日期在6/1/20176/30/2017之间,则CASE Expression返回一个1,而ELSE返回一个超出这些日期的0DESC按照从 1 到 0 的降序对CASE表达式结果集进行排序。DESCEstimated_Value从最高到最低排序。结果是一个priority列,根据ORDER BY排序,六月份的OPEN_DATE交易在估计价值中排名最高。

一个关键:慢慢来

有效使用Window_Function()CASE Expression的一个关键是花时间去了解你的数据集。这意味着将样本数据导出到电子表格中。当Window_Function()执行CASE Expression时,您可以做笔记或模拟预期的结果。一个Window_Function()CASE Expression的组合提供了大量的动力,所以如果处理不当很容易死机和烧毁。你可以花上几个小时试图弄懂那些在电子表格中发现的结果。大多数情况下,当我犯了错误时,那是因为我跳过了这一步。

我用Case ExpressionsWindow_Functions()做过一些很神奇的项目。本教程只是触及了无限创造性编码可能性的表面。我希望这些信息能帮助你探索和创造性地解决复杂的问题。继续成长,用CASE ExpressionsWindow_Functions()推动可能的极限。

分享灵感 :分享每一个按键和每一课,都让我想起我在 Vanguard Jr .高中八年级时的英语和打字老师 Reeves 女士。她帮助一个笨拙的小男孩相信,只要有足够的纪律、善良和爱,他可以做得更好。不断分享并激励他人变得比他们想象的更伟大。

SQL 窗口函数

原文:https://towardsdatascience.com/sql-window-functions-78593bcabf4?source=collection_archive---------8-----------------------

辅导的

爱恨交加的关系

我爱你,即使我恨你— 亨利·利奥·苏贤

我一直对 SQL 窗口函数又爱又恨。当他们工作时,我爱他们,当他们不工作时,我恨他们。我会花上几个小时与他们争论和斗争,直到我沮丧地走开,或者通过蛮力让他们工作。这些努力前后矛盾,令人困惑。如此强大的力量,却又如此难以驾驭。喝了几个小时的咖啡,或许还说了一些不友好的话,我意识到我误解了驱动 SQL 窗口函数算法的核心元素。

基础知识

如果你处理过大数据,你很可能在 Spark、Pandas、Tableau、MySQL、Postgres 和 Snowflake 中遇到过窗口函数。如下表所示,窗口函数可以分为三种类型。

在本教程中,我将重点介绍窗口函数的核心语法,即OVER()PARTITION BYORDER BY子句。这些元素是大多数窗口函数背后的驱动因素,所以如果你正在纠结如何应用一个窗口函数,这很可能与不理解窗口函数算法如何执行这些元素有关。理解这些工作方式将为实现所有窗口函数类型打开大门。在本教程中,我不涉及frame条款。这是一个很大的话题,值得自己的教程。此外,它的实现因 SQL 平台而异。所以,我建议看平台的文档。

开始理解任何窗口功能都是语法。下图显示了窗口函数的基本语法,以及每个重要元素的描述。

语法,窗口函数

在下面的教程中你会学到更多。同时,我建议复制一份语法以供将来参考。当您使用越来越多的窗口函数时,它可以作为快速复习工具。

窗函数算法

用于处理窗口函数的算法步骤因窗口函数、分区和子句排序而异。虽然对算法处理的深入探究超出了本教程的范围,但我发现将算法想象成从OVER()子句开始,然后是PARTITION BY和最后的ORDER BY会很有帮助。

我们的 SQL 窗口函数

本教程探索了RANK(sales_price) OVER(PARITITION BY city ORDERY BY sold_date [frame clause])函数。RANK()窗口函数确定一个值在一组值中的排名。该表显示了数据集。

使用sold_datecitystreet_name作为RANK()窗口功能的输入来填充rank_sales_price栏。您可能已经注意到rank_sales_price列是空白的。别担心。我们将介绍对销售价格进行排序所需的步骤。

我们的数据

我们示例中的数据来自我创建并填充到 SQL Server 表中的数据。在学习 SQL 窗口函数时,我鼓励您在电子表格中构建自己的示例,然后将它们导入到自己的 SQL 表中。另一种方法是使用现有的数据集,并将其导入 SQL 表。下面的 SQL 查询显示了我的数据表中的所有列函数以及我创建的窗口函数。

除了RANK()窗口功能外,SELECT语句还可以支持额外的窗口功能。这是测试不同的PARTITION BYORDER BY子句的强大功能。OVER()条款启用该功能。

OVER()子句:总建筑师

OVER()条款就像建筑师设计住宅一样。家居设计通常包括卧室、浴室、厨房和几个其他房间;请参见下面的平面图。类似地,对于每个窗口,OVER()子句为PARTITION BYORDER BY子句构建了一个单独的执行T21 空间。

房子里的每个房间都有不同的功能。卧室是用来睡觉的,而厨房是用来做饭的。对于窗口类型,每个功能都有不同的用途。AVG窗口函数返回平均值(算术平均值),而SUM窗口函数返回输入列的总和。

一个SELECT语句中的窗口函数越多,创建执行空间的架构师、OVER子句就越多。这就是OVER()子句的强大之处,它消除了创建多个查询的需要。一条SELECT语句可以返回多个结果。

设计考虑

在查看窗口函数时,将PARTITION BY子句视为列中数据的分组器并将ORDER BY子句视为列中数据的组织器会很有帮助。作为分组器,PARTITION BY对数据列中的相似值进行分组。作为组织者,ORDER BY子句决定聚合排序或分析窗口函数如何应用于行数据。由于可选的默认设置,很容易忽略ORDER BY子句,但这将是一个错误。条款很重要,理解它会让你省去几个小时的挫败感。我们稍后会深入探讨,但首先让我们更详细地看看条款。

分区依据子句

使用PARTITION BY子句时,记住几条规则很有帮助:

  • PARTITION BY子句从左到右处理列。
  • PARTITION BY子句对相似值进行分组,并根据差异进行拆分。

我发现将PARTITION BY子句想象成在移动到PARTITION中的下一列之前扫描该列并对相同的值进行分组是很有用的。让我们看看下面的图片,了解它是如何工作的。

分组和拆分,分区依据

步骤 1 中,处理从最左边的列城市开始。在城市列中有三个不同的值:埃塞克斯绿色英亩蓝色海洋算法将具有相同值的行分组在一起,而将差异分割在上。结果是步骤 2 中的三个结果集。处理在下一列街道名称中继续。窗口算法继续对差异进行分组和分割。在步骤 2 中,以埃塞克斯为城市的数据不拆分。行值不存在差异。主街在两排。然而,街道名称中的一个差异导致数据分裂为绿色英亩和蓝色海洋的城市。图像显示了分组和分割步骤 3 的结果。

ORDER BY 子句

我认为ORDER BY子句在编写SELECT语句查询时存在镜像问题。您可能几个月都不需要使用 SQL 窗口函数,因此在使用 SQL 窗口函数时很容易被忽略——这是一个高级主题。然而,在编写窗口函数时,ORDER BY子句是应用窗口函数结果的有力工具。它的排序属性(升序-降序)控制窗口函数如何应用于一行数据。

使用ORDER BY子句时,记住几条规则很有帮助:

  • ORDER BY子句从左到右处理列。
  • ORDER BY子句中,列处理PARTITION BY子句中列的独立。在PARTITION BY子句中使用的列不必在ORDER BY子句中使用。
  • ORDER BY子句的 sort 属性决定了窗口函数如何应用于行数据。
  • ORDER BY子句中发现整数和日期数据类型列是很常见的。日期列可以是开始日期、结束日期等。而整数列可能是价格、大小或距离。窗口函数算法不会将ORDER BY列限制为这些值或数据类型。这些示例旨在帮助理解用法。

我已经将Date SoldRank列添加到下面的数据中。Date Sold列包含在ORDER BY子句中使用的日期数据。Rank列的值为空。ORDER BY子句排序属性设置列中的值。

有两个路径,升序或降序,用于设置Rank列中的值。

  • 路径 1:如果我将ORDER BY子句中的Date Sold列设置为升序 (asc),它会将1RANK值赋给第一条记录1/18/2017
  • 路径 2:如果我将ORDER BY子句中的Date Sold列设置为降序 **(desc)**,它会将1RANK值赋给最后一条记录1/20/2019

因此,窗口函数通过ORDER BY子句的ascdesc排序属性应用于记录。

结论

了解窗口算法如何处理OVER()PARTITION BYORDER BY子句是实现 SQL 窗口函数的关键。OVER子句为PARTITION BYORDER BY子句创建执行空间。子句对值进行分组,并对差异进行拆分。ORDER BY子句的 sort 属性决定了窗口函数如何应用于分区中的记录。

灵感: 不要轻视微小的开端。随着每一课的分享,我记得和我妈妈在一个小房间里,一台唱机和字母表。她用简单的工具教两个小黑人读书、写字和思考。开始可能很卑微,但梦想可以很大。不断分享并激励他人变得比他们想象的更伟大。

SQL fluff——现代 SQL 的缩写

原文:https://towardsdatascience.com/sqlfluff-the-linter-for-modern-sql-8f89bd2e9117?source=collection_archive---------2-----------------------

SQL 也值得一提

为什么我相信 SQLFluff 有潜力为好🧹 ✨解决 SQL 林挺

布雷特·乔丹在 Unsplash 上的照片

几个月前,我的团队开始大量使用 dbt ,因此我发现自己在编写了很长时间的 Spark Scala 之后,大部分时间都在编写 SQL。每次当我从通用编程语言转换到 SQL 时,我都会怀念使用成熟的 linter。我害怕再次花时间在“风格”上,害怕在拉取请求上吹毛求疵。

当我偶然发现这个整洁的小(当时)工具叫做 SQLFluff 。我认为这个设计很有前途,(稍后我会谈到为什么),所以我开始在这个项目上投入一些时间。其他人也开始定期投稿,SQLFluff 的势头越来越大。2020 年 10 月, Alan Cruickshank (该项目的创建者),成立了 GitHub 组织,并邀请我们中的一些人成为维护者。当时,这个项目有大约 180 颗星,现在已经超过三倍,成为 GitHub 上最受欢迎的 SQL linter🌟。

GitHub 星随时间推移,生成于https://star-history.t9t.io/#sqlfluff/sqlfluff

在这篇文章中,我将带你了解:

  • 棉绒的不同角色以及我们(维护者)如何看待它们。
  • 林挺现代 SQL 面临的挑战以及 SQLFluff 如何应对这些挑战。
  • SQLFluff 的未来是什么样子。
  • 如何让为 SQLFluff 贡献

在此阅读关于标志的信息

林挺工具的作用

💅风格

linter 的作用之一是在你的代码上执行修饰性的规则。这些规则不会改变代码的行为方式,只会改变它的外观。

没有一致风格的代码库很难阅读和使用,因为它们的结构不可预测。读者没有机会习惯能让他们快速浏览逻辑的模式。

强制执行一个没有短打的一致的风格是很难的,因为“风格”规则可能非常主观,喜欢“前导”逗号而不是“尾随”逗号。没有短评,这种主观性导致了许多充满吹毛求疵的谈话和公关评论。

没有人喜欢在公关评论中挑剔风格,也没有人喜欢得到这些评论。这不仅对参与其中的每个人来说都是糟糕的体验,而且还会妨碍围绕业务逻辑、架构和测试的重要对话。

如果你习惯了像 black (Python)或者 scalafmt (Scala)这样的 linters,你就习惯了不用担心风格的编写代码,因为它们都可以自动重新格式化你的代码。我们的目标是用 SQLFluff 提供相同的体验——我们认为提出样式问题是一个好的开始,但是自动执行它们是让开发人员真正忘记格式化的原因。

SQL 改造示例:

通过 VSCode SQLFluff 插件链接的样式错误的有效查询

“sqlfluff fix”之后的相同查询,使用默认配置

👃代码气味

除了“丑陋的”代码,还有另一类“有问题的”代码。据说不是的代码是无效的,但可能表明逻辑中有问题。例如,在通用语言中:我们可能会以定义了但从未使用过的变量、冗余导入和隐藏变量而告终。

在 SQLFluff 中,我最喜欢的例子是规则 L037 ,当“ORDER BY”显式地定义了一列的升序或降序,但却隐含了其他列的顺序时,它会发出警告。默认情况下,该行为被隐式设置为升序,但通常会对这种隐式行为感到惊讶。

通过升序进行隐式排序,通过 VSCode SQLFluff 插件进行链接

您可能会错误地认为所有三列的结果都是按降序排列的,而实际上 column_a 和 column_b 是按升序排列的,column_c 只有是按降序排列的

在这种情况下,fix 例程通过重写查询在 column_a 和 column_b 上添加 ASC 关键字,使隐式行为显式化。

asc/desc 订购的明确订单

在 Scala 中,编译器将这些问题作为警告提出,而在 Python 中,通常使用 flake8 。在 SQLFluff 中,这些问题与样式规则没有什么不同,它们也经常实现修复。

📖最佳实践库

对于试图学习一种新的编码语言及其最佳实践的人来说,记录良好的规则可以成为一个很好的伙伴。

在 SQLFluff 中,我们用一个“反模式”、“最佳实践”和一个简短的描述来记录我们的规则,该描述解释了为什么一个规则存在——灵感来自 flake8rules 。

随着时间的推移,该项目成为 SQL 中最佳实践和反模式的知识库。它聚集并提炼了互联网上存在的所有信息:SQL 参考文档、StackOverflow、沮丧的 Tweets 以及经验丰富的 SQL 程序员的集体想法。

看一下 Rule_L015 :它强调“DISTINCT”不是一个函数,使用带有“function”语法的关键字可能会产生误导。您可以在这个 StackOverflow 答案中找到相同信息,但是这个规则引起了您的注意。“不知道自己不知道的东西”是学习一门新语言的挑战之一,linter 可以帮助你注意到这些怪癖,这样你就可以很快发现它们。

https://docs . SQL fluff . com/en/stable/rules . html # SQL fluff . core . rules . rule _ L015

林挺现代 SQL 的挑战

许多流行的通用语言中都有现成的 linters,但 SQL 没有。在我看来,这是由于许多方言的存在,以及模板化或动态 SQL 的使用。

🗣方言

SQL 有许多方言通常与特定的 SQL 引擎相关联:比如 Redshift、BigQuery 和 Snowflake。由于方言之间的差异,构建特定于一种方言的 linter 似乎是正确的事情。然而,在我看来,这些项目将很难建立一个足够大的社区来维持创建一个强大的 linter 所需要的工作。当用户或维护人员切换到不同的引擎时,他们会对项目失去兴趣,造成用户和贡献者的流失。

然而,我理解为什么迄今为止构建的大多数 linters 都是特定于方言的:SQL 引擎很少共享它们的解析逻辑,所以需要构建特定的解析器来理解每种方言。Spark SQL 有一个ANTLR规范,可以用来生成一个解析器,但是这是规则的例外!

幸运的是,大多数方言都有一些相似之处,例如 Redshift / Postgres、Presto/Spark SQL——我们在 SQLFluff 中利用了这一点。

在 SQLFluff 中,我们定义了一个基础方言,它不依赖于任何特定的引擎,而是松散地基于 ANSI,我们从这个基础中派生出特定于数据库的方言。解析器的结构尽可能地被重用,但是可以添加或替换父方言的元素来处理方言之间的差异。

例如,MySQL 接受文字的双引号,所以我们将它添加到解析器的“LiteralGrammar”部分。

另一个例子:BigQuery 方言接受通配符表达式中的 EXCEPT 和 REPLACE 子句,因此我们将这些情况添加到“WildcardExpressionSegment”。

将方言抽象成可组合的解析器是 SQLFluff 最强的特性之一——它允许我们从相似的父方言中派生出一种方言。得到的解析结构不依赖于任何特定的数据库引擎,使我们能够拥有与方言无关的规则

下面是sqlfluff parse my_file.sql --code-only --format yaml输出的部分结构:

SQLFluff 能够以 json、yaml 或“人类”格式输出它解析的结构

在撰写本文时,SQLFluff 实现了以下方言:BigQuery、Exasol、MySQL、Postgres、Snowflake、Teradata。

✨模板

像 Apache Airflow 和 dbt 这样的大规模流行工具已经使模板化 SQL 成为标准。这两个工具都使用 Python 库 Jinja 作为它们的模板引擎,这使得:

  • 变量注入 SQL 代码。
  • 定义生成 SQL(宏)的函数
  • 对于循环和宏,通过使用变量避免重复

这些特性允许 SQL 开发人员编写更多可维护的代码,并且更有生产力,所以我们认为这是 linter 支持的一个重要特性。

用 Jinja 模板化的查询示例

SQLFluff 以模块化的方式支持模板化。今天,我们支持关闭模板,使用 Jinja,或者使用在内部重用 dbt 编译器的 dbt 模板。

支持模板也带来了挑战!例如,大多数用户不关心模板化部分中的样式问题,所以我们必须找到一种方法来忽略它们(错误,而不是用户)😅).Alan 实现了“源代码映射”,报告用户看到的行号(模板化前),而不是模板化后。我们有时也会看到修复规则会将泄漏到模板化部分的问题。

我认为该项目正在快速解决所有这些特定于模板的困难,我非常兴奋地看到 SQLFluff 中模板支持的发展,如预模板代码上规则的实现,例如应用于 Jinja 标签的 Rule_L046 。

SQLFluff 的未来

🤔超越风格和代码气味的规则

我们最近在 SQLFluff 中添加了插件,允许用户在自己的公司中开发自定义规则,而不必与社区共享。

我希望它将允许用户试验非常特定于他们用例的规则,这些规则不容易被共享。

例如,我们可能开始看到强制执行与 GDPR 相关的限制的规则,比如“不要使用这个列,使用这个新的匿名列”。我很高兴看到社区对这一功能所做的工作,以及我们将来会看到的规则。

sqlfluff 的不同接口

如果你在 GitHub 上查看 SQLFluff 组织,你会看到几个项目。其中包括:

  • sqlfluff-online 允许用户在网站上获得他们的 SQL 链接。
  • vscode-sqlfluff 实现一个 vscode 插件。
  • sqlfluff-github-actions 有 github 动作。

还有文档介绍如何在 CI 中使用 SQLFluff 和 diff-quality 或预提交。

理想情况下,我们会看到更多的 ide 与 SQLFluff 集成,有更多的方式在 CI 上运行它,甚至有供应商在他们的 SQL 编辑器中使用 SQLFluff。

🤖类型检查

目前,SQLFluff 不使用来自 SQL 引擎的元数据。

如果是这样的话,我们可以用每一列的类型和表的模式来丰富解析后的 SQL。它将开启一整类的规则!例如,当对错误类型的列调用函数时,或者当选择了源表中不存在的列时,规则可能会失败。

在 SQL 中实现类型安全将是一个伟大的里程碑。

贡献给 SQLFluff

为项目做贡献的方式有很多,没有必要写代码做有意义的贡献!

  • 使用项目和报告你遇到的问题。
  • 帮助回答社区关于我们的 Slack 的问题,在 GitHub issues 或者 dbt Slack 的#tools-sqlfluff 频道。
  • 编写文档。
  • 尝试在您的团队中推出 SQLFluff。
  • 谈论/写 SQLFluff。

如果您决定编写代码,您可以参与项目的不同领域:

  • 方言:多为解析逻辑。
  • 规则:文档,添加新规则,添加规则的自动修复。
  • 模板:支持新的模板引擎,改进现有的模板以及它们如何与代码的其余部分交互,比如当一个规则被应用了一个补丁时。
  • 性能:速度,cpu,内存使用。

当然,对开发工作流程的改进,以及重现 GitHub 问题中出现的 bug 的测试总是受欢迎的!

结论

感谢您阅读并对该项目感兴趣!我希望你能很快成为这个社区的一员😀。

我要感谢 Alan Cruickshank 创建了这个项目,投入了这么多时间,并邀请我们(维护者)更直接地为 SQLFluff 做出贡献。

当然,通过这个项目,我认识了很多不可思议的人,我从他们身上学到了东西,并喜欢和他们一起工作。

当你和人们的大部分互动都是通过拉式请求评审,而你最终还是喜欢他们的时候,你知道他们是好人😁。

社区非常友好和支持,Slack / GitHub 上的每一次互动都非常积极。🙇‍♂️

最后,下面是 Alan 在 Coalesce 2020 上关于 SQLFluff 的演讲:

Niall Woodward (维护者)写了一篇博客,讲述了他一年来为开源做贡献的经历:

https://www.niallrees.com/posts/lessons-from-a-year-of-open-source

SQLite vs TinyDB

原文:https://towardsdatascience.com/sqlite-vs-tinydb-7d6a6a42cb97?source=collection_archive---------13-----------------------

轻量级数据库之战

罗曼·辛克维奇在 Unsplash 上拍摄的照片

就在最近我写了一篇关于 TinyDB 是什么以及如何使用它的文章。在解释 TinyDB 时,我注意到了与 SQLite 的一些相似之处。这让我想知道这两个数据库有多相似。理论上,这两个数据库有相对相同的目标。这两个数据库都是轻量级的、基于文档的数据库,易于设置和维护。虽然它们似乎有着相同的目标,但我们可以看看 TinyDB 与 SQLite 有多大的不同。这也将帮助我们确定哪个数据库更适合我们的项目,这取决于您的目标。注意,每个 SQL 变体的语法都是不同的,但是我们仍然会讨论一些主要的函数与子句的差异。因此,让我们大致了解一下它们的区别,看看我们能从 TinyDB 和 SQLite 中学到什么。

它们有多“轻”?

SQLite 被认为是一个小型快速的 SQL 数据库引擎。截至 2018 年,合并文件超过 220,000 行。这些文件包含在 100 多个文件中,其中有 102 个主 C 文件。SQLite 包含在文件格式中,这意味着数据库在系统中是通用的。这意味着 SQLite 可以在电脑和手机上运行。如前所述,SQLite 是用 C 库编写的。SQLite 的最初版本是在 2000 年。

TinyDB 是另一个小型 SQL 数据库。它当前的源代码大约有 1800 行。这些行有大约 40%的文档。还有大约 1600 行测试。TinyDB 是用纯 Python 编写的,因此没有外部依赖性。TinyDB 最初发布是在 2013 年。

这两个数据库都声称运行速度很快。两者都足够快,可以在手机或电脑上运行。然而,与 TinyDB 相比,SQLite 有更多的文件和代码行。此外,SQLite 更像是一个数据库引擎,而 TinyDB 主要只是数据库存储。然而,两者都是包含在文件中的自包含数据库。因为它们包含在文件中,所以不需要服务器来保存数据。这也意味着数据库是特定于项目的,因为文件是在项目的目录中访问的。

语法差异

SQLite 是用 C 语言编写的,但是用 SQL 语法执行。相比之下,TinyDB 是用纯 Python 编写的,在更 Python 化的 SQL 版本中执行。这是一个既定的,因为它已经提到。然而,我们仍然可以看到一些查询类型的差异。SELECT 语句就是一个例子。在 SQLite 中,您使用典型的 SQL SELECT(语法因 SQL 和 SQLite 而异)。与 SELECT 相反,TinyDB 使用一些不同的变体。

首先,如果试图从数据库中选择所有内容,TinyDB 只需使用“all”函数。这个函数类似于没有“WHERE”子句的 SELECT 语句,SQLite 将遵循该语句。收集文件中包含的所有记录。

当需要一个特定的记录时,您可以在典型的 SQL 中制定一个 WHERE 子句。对于 SQLite 也是如此。但是,TinyDB 有点不一样。对于如何找到所需的记录,有几个选项。我在教程中使用的第一个选项是“搜索”功能。使用该功能,您可以搜索找到任何指定键的所需值。类似地,您可以使用“get”函数。就像“搜索”功能一样,您可以指定键和所需的值。

注意,我在这方面没有做太多的研究,我确信再多一点努力就会有结果,但是我们至少可以讨论一下难度。因此,在 SQLite 中,如果您想要排序,只需使用 ORDER BY 子句。这是一个高于入门水平的头发,对于已经学会了基本选择的人来说。然而,在 TinyDB 中没有简单的 ORDER BY 或等价函数。这也是我做的一点点研究,所以如果你找到了解决方案,请随意分享。我在搜索中发现,没有简单的排序函数。相反,因为它是用纯 Python 编写的,所以你可以做的是将结果排序,就像它们是一本字典一样。对初学者来说,整理字典可能需要更多的研究和努力,在我看来,这比使用 ORDER BY 要复杂得多。

TinyDB 过去使用“清除”功能而不是“截断”来清空文件中的记录。然而,在一些更新中,“清除”被改为“截断”。SQLite 使用 TRUNCATE TABLE 命令来清空一个表,因此在这种情况下,它们可以被视为语法相同。

可扩展性

SQLite 被认为是可靠的 ACID(原子的、一致的、隔离的和持久的),即使在电源故障或崩溃期间。SQLite 甚至可以支持一个完整的万亿字节大小的数据库。尽管 SQLite 在设计时更多地考虑了单个用户,但它也可以支持多个用户。然而,用户越多,性能就越成问题。在写入数据库时,多个用户可能是一个问题。然而,SQLite 确实允许单个用户写入数据库。对于各种用途,SQLite 也有超过 225 个 API,使用范围和难度各不相同。

TinyDB 被认为是快速且容易学习的。只需很少的设置就可以让数据库工作。然而,代价是性能和一些可靠性。例如,这个数据库不保证有 ACID。尽管 TinyDB 为易用性进行了优化,但它不能轻松处理多个进程或线程。此外,不能保证管理表之间的关系。TinyDB 也没有索引。更重要的是,TinyDB 不提供 HTTP 服务器。TinyDB 确实有几个 API 可供使用。它还允许在存储和中间件方面进行扩展。

相比之下,这两个数据库都做得又小又快。然而,SQLite 具有一定的可伸缩性。例如,SQLite 可以保存 1tb 的数据。对于 TinyDB,大小主要由设备决定。如果该设备是移动的,不仅安装时间长得多,而且会因快速消耗内存而降低设备本身的速度。TinyDB 也不使用 HTTP 服务器,所以 SQLite 是更合适的选择。同样,TinyDB 不处理索引或维护表之间的关系。这些都是 SQLite 所具备的功能,因为它是一个更加成熟的数据库引擎。然而,SQLite 确实有它的问题。例如,需要对 SQLite 数据库进行写访问的多个用户会导致问题。然而,它支持 ACID,而 TinyDB 不保证 ACID。

结论

作为描述的一般概述,听起来 SQLite 和 TinyDB 非常相似。理论上,它们有相同的基本目标:拥有一个小型、轻量级、易于建立的数据库。然而,一旦我们深入到更多的规范,我们可以看到 TinyDB 和 SQLite 是不同的。除了语言和实际大小(我们预计会有所不同)之外,还有一些不同的语法。更重要的是,在可伸缩性方面,SQLite 有一些 TinyDB 没有的选项。一个这样的选项是 HTTP 服务器。另一个选择是多用户读取,如果用户没有写入数据库,SQLite 可以处理。相比之下,TinyDB 不支持多进程。另一个关键特征是维持和保证酸性的能力。对于 SQLite,ACID 很重要,因此有保证。然而,TinyDB 更侧重于设置的简单和快速,没有外部依赖性,因此特别是随着它的发展和使用,ACID 不能得到保证。

最后,对于不需要太多高级功能的小型项目来说,这两个数据库都是不错的选择。当决定使用哪一个时,它将取决于你的项目。如果您的项目需要一个前端,您可能希望选择 SQLite。同理,如果需要酸有保障,就选 SQLite。但是如果您正在为个人项目寻找一种快速保存数据的方法,并且您更担心快速查看数据,TinyDB 将是您的选择。需要注意的是,如果您需要更高级的功能,比如对数据库的并发写请求,您可能需要选择一个比 SQLite 更强大的数据库,因为它对于这些类型的应用程序来说可能太轻量级了。然而,对于像移动项目这样的东西,SQLite 和 TinyDB 都是轻量级的数据库,足以在移动设备上工作。下次见,干杯!

用我的 每周简讯 免费阅读我的所有文章,谢谢!

想阅读介质上的所有文章?成为中等 成员 今天!

查看我最近的文章:

https://python.plainenglish.io/python-virtual-environments-what-you-need-to-know-95487982c586 https://python.plainenglish.io/tinydb-b646e3270fd7 https://python.plainenglish.io/python-database-dumping-9a8658994e5a

参考资料:

<https://www.sqlite.org/features.html>  <https://www.sqlite.org/whentouse.html>  <https://www.sqlite.org/cintro.html>    <https://stackoverflow.com/questions/43168631/app-inventor-tinydb-record-limit-or-size-limit>  <https://www.python-engineer.com/posts/tinydb/> 

在你的熊猫节目中加入新的技巧

原文:https://towardsdatascience.com/squeezing-a-new-trick-into-your-pandas-repertoire-d8ae3f338246?source=collection_archive---------52-----------------------

大蟒

看看如何将 Pandas 对象转换成标量来进行更多的计算

照片由桑迪·克拉克在 Unsplash 拍摄

Pandas 库允许您轻松地将数据切片和切块。通常,您将处理各种形式的数据帧和系列。您可以使用 groupby 聚合数据,甚至可以创建一个 MultiIndex 来增加表的层次结构。

然而,有时您会希望能够处理非熊猫对象形式的数据。在这些情况下,能够将您的熊猫对象转换成标量值就太好了。

为此,您可以只使用一行代码,其中包含一个内置的 Pandas 方法。让我们来看几个例子,看看如何做到这一点!

如果您想继续这篇文章,请运行下面的代码来导入我将在示例中使用的数据:

import pandas as pddf = pd.read_html("[https://finance.yahoo.com/quote/TSLA/history?period1=1546300800&period2=1550275200&interval=1d&filter=history&frequency=1d](https://finance.yahoo.com/quote/TSLA/history?period1=1546300800&period2=1550275200&interval=1d&filter=history&frequency=1d)")[0]
df = df.head(30)
df = df.astype({"Open":'float',"High":'float',"Low":'float',"Close*":'float',"Adj Close**":'float',"Volume":'float'})
df.head()

现在让我们开始吧!

使用挤压将熊猫对象转换为标量

假设我们想要获得特定日期的“开放”列值。为此,我们可以利用loc和一些条件选择从现有数据帧中获取一行:

feb_15_data = df.loc[df['Date']=='Feb 15, 2019']

如果您不熟悉 *loc* 和条件选择,请查看链接在该块底部的第一块。

现在我们已经返回了只包含 Febary 15 的值的所需行,让我们看看当我们试图只为这一行选择“Open”列值时会发生什么:

feb_15_open = feb_15_data['Open']

正如您所看到的,我们得到了我们需要的值“60.9”,但是我们也得到“0”左边的索引值以及下面 Panads 对象的描述(Name 和 dtype)。对于我们来说,要获得标量值,我们需要做的就是在当前的熊猫对象上使用squeeze方法:

feb_15_open_scalar = feb_15_open.squeeze()

现在我们已经将标量值赋给了一个新变量!现在,您可以像对待任何其他浮点值一样对待该变量(因为这是在读入数据时分配给它的 dtype)。

我们可以再次经历相同的过程,但是这次从一系列开始。首先,我们来看看“体积”系列。然后,我们将应用条件掩码并寻找一个真正高的值:

volume = df['Volume']
volume_highest = volume[volume > 100000000]

和前面的例子一样,我们确实得到了系列中最高“Volume”的期望值,但是我们也看到了相关的索引和 Pandas 对象描述符。如果我们再次对它使用squeeze方法,并将它赋给一个新变量,我们将再次得到一个标量值:

volume_highest_scalar = volume_highest.squeeze()

现在,您可以自由地使用这个变量在数据帧上或在未来的数据分析步骤中执行更多的操作。

仅此而已!

我希望你发现这个快速提示对你将来的熊猫工作有帮助。更多熊猫和我的数据分析相关的作品,请随时查看下面的链接!

**More by me:** - C[onditional Selection and Assignment With .loc in Pandas](/conditional-selection-and-assignment-with-loc-in-pandas-2a5d17c7765b?sk=e5672d859a3964c1453a1c09edca22cf)
- [2 Easy Ways to Get Tables From a Website With Pandas](/2-easy-ways-to-get-tables-from-a-website-with-pandas-b92fc835e741?sk=9981ddaf0785a79be893b5a1dd3e03dd)
- [5 (and a half) Lines of Code for Understanding Your Data with Pandas](/5-and-a-half-lines-of-code-for-understanding-your-data-with-pandas-aedd3bec4c89?sk=7007a1ae248cf7ea4ef5fcd4af7ae72b)
- [Top 4 Repositories on GitHub to Learn Pandas](/top-4-repositories-on-github-to-learn-pandas-1008cb769f77?source=friends_link&sk=d3acc38062490a86ecb46875342224e6)
- [Learning to Forecast With Tableau in 5 Minutes Or Less](/learning-to-forecast-effectively-with-tableau-in-6-minutes-or-less-3d77a55930a0?source=friends_link&sk=9abdfd7533ee9a31ab8a036413450059)

在定制网络中压榨石灰

原文:https://towardsdatascience.com/squeezing-lime-in-a-custom-network-7aa30386d342?source=collection_archive---------27-----------------------

模型可解释性

怎么解读口译员?

Rbm ,本杰明拉鲁斯,托马斯 g

我们定制的 RNN-LSTM 网络架构。(图片由作者提供)

机器和深度学习模型应用于广泛的领域,从基础研究到工业和服务业。它们对各种各样问题的成功应用进一步促进了该领域的扩展:它正在经历一个“黄金时代”,这个时代的尽头仍然看不到。

虽然预测和分类算法的发展充满了成功的故事,但仍有几个必须妥善解决的重大挑战,这也是目前几项研究的主题。

模型可解释性(通常被称为“可解释的人工智能 (XAI)”)就是这些问题之一。问题的核心是提供一种方式来理解和解释预测算法背后的基本原理,否则这些算法将像黑盒一样运行。

要求更好地理解机器学习模型的结果有各种各样的原因,但列出一些我们可以想到的原因:

  • 更深入地了解模型再现的机制。我们可以想到机器学习应用于基础研究的情况,在这种情况下,仅仅有一个模拟观察到的行为的算法是不够的,但理解它背后的机制很重要;
  • 增加最终用户对模型结果的信任。由于人工智能应用于不同的领域,算法的最终用户可能是应用领域的专家;然而,他或她不一定有机器学习的背景。通过使用可解释性算法来理解模型结果,他或她可以有意识地应用或拒绝从预测算法中获得的信息;
  • 法律问题。随着机器学习应用的兴起,为保证正确使用算法运行所需的数据而发布的法规也在增加。尊重法律约束(例如 GDPR 中包含的解释权)需要正确理解算法的基本原理。这种理解可以通过可解释性的算法来获得。

此外,模型结果的解释可以作为在算法开发期间评估算法有效性的附加工具。

随着这种(相对)新趋势的出现,我们决定将一种不同的可解释性算法应用于预测客户购买概率的模型。选择的算法是 LIME(T2 本地可解释模型不可知解释的缩写)。

接下来的部分描述了预测算法的功能,并简要介绍了时间解释器。最后给出了它在我们的用例中的应用结果。

本文省略了可解释性理论和可用模型的细节,因为文献在这个主题上提供了足够的材料。那些有兴趣进一步探索这一主题的人可以从 Christoph Molnar 的著名著作开始。

https://christophm.github.io/interpretable-ml-book/

我们感兴趣的算法

我们将尝试解释的模型是一种深度学习算法,它根据客户的导航历史预测客户在未来 X 天(其中 X 通常是 7 或 28)购买一件商品的概率:页面浏览量、购物篮、在线购买和实体店购买。它为我们提供了表征用户行为的事件序列。因此,可以训练一种算法,用于基于该数据来估计购买概率。

输入长度(即客户端历史中的事件数量)不是固定的,可以任意大。此外,每个事件都有许多特征,这使得数据集相当大。出于这些原因,我们选择使用深度学习算法,特别是长短期记忆(LSTM),这是一种递归神经网络(RNN)。LSTM 网络非常适合任意大长度的输入,也可以将相对旧事件的信息保存到内存中。

为了有效地使用提出的 LSTM,我们从数据预处理开始,以便对原始数据进行整形..并非所有特征都是数字的;因此,必须考虑它们的特殊性(例如,值的类型和范围)。这一步非常重要,可以减少内存使用,提高预测质量。

LSTM 网络在 Tensorflow 1.15 中设计。它根本不是一个普通的网络:考虑到我们数据输入的特殊性,我们构建了一个自定义架构,使用独立的 LSTM 单元来表示数字特征和分类特征。下图显示了该架构的示意图。

购买概率模型示意图。(图片由作者提供)

架构的定制性质以及每个事件的大量功能(约 50 个)使得解释在许多方面变得复杂:必须考虑许多维度,并且由于我们的架构不是标准的,现成的可解释算法不能直接使用。尽管如此,这些算法是我们在这项研究中想要实现的目标的起点。

(又一个)石灰底漆

LIME 是一个本地代理模型;因此,可解释的模型,通常是一个线性模型或一棵树,适合在实例的邻域中进行解释。由于是模型不可知的,LIME 可以应用于任何类型的期望算法,包括深度神经网络。

拟合局部解释器所需的训练数据集是由 LIME 通过扰动要解释的实例来生成的。通过将扰动的数据传递给模型进行解释,得到相应的标签。

为石灰生成输入数据和标签的模式。(图片由作者提供)

通过最小化损失函数(其是加权 RMSE)来训练可解释的模型。权重是根据扰动数据与要解释的实例的距离来定义的;数据越接近,权重越高。

使用合成数据对权重的影响的例子。红点是要解释的实例,蓝点代表受干扰的数据。在右图中,点的大小根据权重进行缩放。(图片由作者提供)

为了加强模型的可解释性,损失函数的最小化受到复杂性度量的约束,即在可解释模型中应用的特征的数量。该过程旨在确保局部保真度(即,可解释模型必须是原始模型的足够好的局部近似)和可解释性(即,可解释模型必须具有低复杂度)。

尽管 LIME 算法很简单,但它依赖于用户可以调整的几个参数和定义,这些参数和定义会影响最终结果。最重要的一个是损失函数中基于距离的权重的核心的核函数的定义。默认情况下,LIME 使用欧氏距离的指数内核,默认内核宽度为 0.75 √(nf ),其中 nf 是要素的数量。

在 LIME 中,每个特征对预测的影响由其在局部解释器中的权重来定义。通过这种方式,可以获得不同要素的相对重要性和影响“方向”的估计值:权重为正的要素将预测推向所选标注,权重为负的要素将预测推离所选类。

当模型和石灰结合时

尽管 LIME 是模型不可知的,但是将算法直接应用于我们的模型就像试图将两个不合适的拼图块匹配起来一样。如果不首先找到两者之间的缺失部分,这是不可能的,在我们的例子中,这对应于预处理和后处理步骤..为了使预测模型的约束与解释者的约束相匹配,这是必需的。

石灰要求:

  • 二维数组形式的一组训练数据;这些数据应该与用于预测模型的训练数据相同。LIME 评估每个连续特征的平均值和方差,以及每个分类特征的对(值,频率)。作为替代,可以通过向 LIME 提供包含该信息的字典来避免这一步骤;当训练数据集非常大时,这是一个有用的解决方案;
  • 要解释的实例必须是与定型数据具有相同数量特征的二维数组;
  • 原始模型必须具有预测功能。

这最后两个要求不会对类似于 scikit-learn 包中可用的模型造成任何特殊的约束。在我们的情况下,这些要求可能很严格。

我们估计购买概率的模型不满足这些要求。模型的输入由每个 LSTM 单元的三维阵列和致密层所需的附加二维阵列组成。扰动数据的产生在我们的应用中提出了另一个问题。当扰动实例来解释时,LIME 没有约束,但具有从训练数据获得的特征的统计属性。此外,一些特征是相互关联的;当产生扰动数据时,必须处理这些关系。例如,在我们的模型中,一周中的第变量就是这种情况,由一个热编码向量表示。LIME 中的默认扰动不考虑这种约束,从而最终生成包含不止一个不为零的元素的向量。

为了解决所有这些问题,我们设计了一系列预处理步骤,以便以适合于时间的形式映射原始模型的输入,从而确保扰动输入的一致性。显然,当由 LIME 产生的扰动数据作为输入输入到原始模型中时,变换必须是反向的。这种方法不仅与 LIME 相关,而且在应用模型不可知的解释器时,已经被视为一种通用方案。

预处理部分包括以下转换:

  • 将一个热编码的数字特征映射到标签编码的分类特征上;
  • 将 LSTM 细胞的三维输入整形为二维阵列;
  • 将不同的输入连接成一个数组。

后处理部分包括以下变换:

  • 对相互依赖的分类特征进行重采样(如有必要);
  • 将采样数据转换(当特征需要时)为正值;
  • 在预处理阶段反转转换。

使用前检查您的仪器:石灰的参数分析

购买预测和 LIME 的算法应用于一组真实世界的数据,这些数据属于一家专门从事消费电子产品和家用电器的法国零售商。

举个例子,我们将 LIME 应用于一个实例,这个实例在我们的购买预测算法的上下文中由一个人表示,这个人有一个特定的导航历史,可以最终导致一次购买。

下图显示了 LIME 使用参数默认值提供的解释。

用石灰解释单个实例的例子。(图片由作者提供)

左侧面板显示了每个特征的权重,这些权重是通过拟合解释器来评估的。每个特征的重要性基于其权重的绝对值。由于问题的顺序性质,权重指的是一对(特征、步骤)。因此,相同的特征在序列中的不同位置可能具有不同的效果;例如,案例“产品 _ lvl _ 2 _ 名称=电视”在 t=2 时有正面影响,在 t=3 时有负面影响,在 t=4 时没有影响。

右侧面板列出了关于实例的序列长度和组成所解释实例的序列总数的信息;此外,还提供了一些关于解释者的信息。这些数字表明,尽管 LIME 提供的本地预测确实接近原始模型获得的真实值(使用 LIME 和原始算法的购买概率分别为 15%和 19%),但决定系数(R 平方值)极低,这表明本地模型相当不可靠。

在把石灰扔进垃圾桶之前,我们可以花一分钟思考一下这种低保真度的原因…

第一个猜测是本地模型的复杂度太低。原始模型需要大约 50 个变量作为输入。当应用前一节中提到的预处理转换时,这个实例的特征数量增加到大约 400 个,这比用于训练局部解释器的 10 个特征要多得多。

为了验证这个假设,我们基于完全相同的解释器,但不同的复杂性,实现了对同一实例的几种解释。下图显示了要素数量对生成的 R 平方值的影响。

作为解释器中特征数量的函数的 R 平方系数和调整的 R 平方的演变。垂直虚线表示在解释的实例的原始模型中应用的真实特征的数量。(图片由作者提供)

变量数量的增加确实会导致 R 平方值的小幅增加;然而,对于大约 600 个特征,调整后的 R 平方值达到其最大值。这表明 R 平方的增加是由简单增加新特征引起的虚假增加。调整后的 R 平方值的最大值约为 0.16;这个相当小的值不能确保局部解释器的保真度。

有趣的是,对于大约 600 个特征获得了最大值,这高于对应于该实例的真实变量的预期的大约 400 个特征,并且由图中的垂直虚线表示。这个高数字可能是由 LIME 引起的,它为整个(特征、步骤)对集生成扰动数据,因此也包括大于实例的最大序列长度的序列步骤,并且应该被认为是伪值。(我们需要一些背景知识。最初的 LSTM 模型涉及具有可变序列长度的输入,这种特殊性通过组合使用分桶和填充来解决。另一方面,当训练 LIME 解释器时,我们需要考虑所有训练数据中的最大序列长度,以便能够将解释器应用于任何期望的实例,而不管它们的序列长度;另一种方法是为数据集中每个可能的序列长度提供一个解释器。因此,当生成扰动时,解释器可以包含序列长度大于要解释的实例的序列长度的变量。)

这个简单的分析表明所获得的解释对石灰参数是多么敏感。解释器的复杂度只是可以调整的参数之一。其他相关的是样本数量(代表解释器的训练数据数量)和核宽度(为了简单起见,我们没有考虑核函数本身和核函数内距离的定义,同时保持默认定义不变)。

为了恰当地应用 LIME,我们必须确定最佳的参数来提供一个可理解的解释(用低复杂度来表示),而不会严重影响解释者的忠实度。考虑到这一目标,我们对调整后的 R 平方得分进行了参数分析,作为模型复杂性、生成样本数和内核宽度的函数。目标是找到提供最佳分数的三个参数的组合。全尺寸参数分析在计算上是昂贵的。因此,我们选择了一小组可能接近最优值的值。

下图清楚地显示了核宽度是影响调整后的 R 平方系数的主要参数;内核宽度越短,得分越高。

调整后的 R 平方系数对解释器参数的依赖性。(图片由作者提供)

这种行为是合理的。当最小化损失函数时,减小核宽度强调更接近实例的扰动数据的重要性。与解释相关的实例的邻域越小,原始模型越有可能用线性函数来近似,因为非线性效应不太明显。通过只关注内核宽度为 0.25 的情况(即,具有最佳结果的情况),可以更好地理解剩余两个参数、复杂度和样本数量的作用。

下图显示了分数作为两个变量的函数的近似演变。调整后的 R 平方值随着样本数量和复杂性的减少而增加(在本分析的测试范围内)。用 5000 个样本和 300 到 500 之间的复杂度获得最佳分数。

调整后的 R 平方系数对解释器中特征数量和生成样本数量的依赖性。(图片由作者提供)

这个小而重要的参数分析强调了调整 LIME 解释器以便正确使用的重要性;否则,会得出误导性的结论。

根据这些结果,我们模型的最佳解释器具有 0.25 的核宽度、500 的复杂度和 5000 个样本。

在下一节中,将展示解释器的应用程序,以提供对原始模型结果的深入了解。

🥁…的解释是送达

单解释

找到了一组保证足够好的保真度的参数后,我们现在可以彻底看看对感兴趣的实例所得到的解释。

下图给出了与上一节类似的解释;这个是用新的解释器得到的。对于 500 个特征的复杂性,所呈现的 10 个特征是关于重要性(定义为权重的绝对值)的前 10 个特征。

用优化的 LIME 解释器解释单个实例。(图片由作者提供)

显然,新的解释与以前的解释大相径庭。最重要的特征(具有负面影响)是不活动时间;它被定义为从预测日到用户最后一次交互日之间的时间段。该特征在之前的解释中不存在。所有其他功能对购买都有积极的影响。相对于前一种情况的另一个不同是特征的小权重。这可能是因为解释中使用了大量的特征。

目前的解释确实可以提供一些原始模型的基本原理的见解。当考虑模型的范围时,不活动时间的重要性显得合理。长的不活动时间可以暗示该人对所观看的产品不感兴趣,这转化为微弱的购买可能性。相比之下,短的不活动时间可以指示仍然活跃的购买兴趣。因此,在对购买意向进行建模时,该变量可能是一个重要的驱动因素;解释证实了这个假设。

基于给出的解释,其他特征的影响更难提取和理解。包括变量和序列步骤的混合信息不能提供每个特征的总体影响的清晰图像。此外,从每一步更重要的变量中得出结论是不可能的。

为了获得对这些问题更透彻的了解,通过特别关注这些方面,产生了相同解释的表示。

按照顺序步骤解释

**实例历史中两个不同步骤的解释示例。(图片由作者提供)

上图提出了解释每个步骤中每个特性的影响的问题。该表示类似于上一节中的表示;但是,特征权重指的是特定的步长(在这种情况下,左边的 t = 7,右边的 t = 19)。在本说明中,不存在与步数无关的特征(如不活动时间)。

该描述提供了从前一个描述中难以获得的附加信息。更具体地说,可以更容易地确定哪些特征在特定步骤具有正面或负面影响。此外,当在不同的会话中移动时,可以观察到特征排序的演变。例如,在 t = 7 时,变量 month 排名第二,具有正面影响,而在 t = 19 时,它不在前十名之列。类似地, product_lvl_1_name 在 t = 7 时不存在,而它在 t = 19 时排名第一。当然,这些信息可以根据用户的目标进行不同的解释。应用该模型来增加销售的最终用户可以看到变量在 t = 7 时的重要性,作为与观察到的产品的链接,如果该产品通常在夏季销售的话。模型的开发者可以假设变量在历史的最初步骤中是重要的,以建立模型的“环境”(例如,一年的时间或位置)。

因此,变量在整个序列中的相对位置的变化可能是重要的信息,应该提取这些信息,而这些信息是先前的表示所不能清楚提供的。

**作为用户历史中的步骤的函数的特征重要性的演变。(图片作者)

为了解决这个问题,我们准备了一个图表(见上图),展示了特征等级相对于序列步骤的演变。显然,等级信息比权重与这种类型的图更相关,因为后者是标准化的。然而,该图不包含特征影响预测的方式的信息。等级的演变为模型提供了额外的洞察力。例如,功能 visit_nb_pages 几乎总是在前五名,这强调了它的重要性。功能 product_lvl_1_name 仅在特定步骤中获得重要性,这可能表示新会话的开始或新产品的可视化。同样,可以从图中提取的洞察信息的类型也取决于最终用户。

根据功能的解释

尽管先前的图集中于从解释中提取与历史的单个步骤相关的信息,但是仍然缺乏对变量对于整个历史的全局重要性的整体理解。

下图解决了这个问题。两个结果都是通过合计整个序列的特征的权重而获得的。在左侧,变量根据绝对重要性进行排序,绝对重要性由路径上每个要素的权重绝对值的平均值定义。在右侧,通过评估路径上的实际平均权重来维护要素的影响方向。显然,在该图中,权重不再与拟合的线性模型相关(除了变量不活动时间);相反,它们是对特性重要性的简单度量。

特征重要性(左)和特征影响(右)在所解释的实例的整个历史上平均。(图片由作者提供)

第一个解释已经证明了变量不活动时间是最重要的一个,并且它对购买概率有负面影响。此外,这些图提供了关于特征的全球重要性的额外信息。平均而言,似乎所有功能(除了inactive _ time)都通过将预测推向购买产生了积极的影响。通过查看图的下部,可以注意到特性 product_ratingproduct _ number _ of _ ratings的无用性。如果这种行为在不同的实例中重复出现,那么说明可以简单地从模型中省略这些特征,而不会影响最终的结果。(我们认为这些特征应该很重要——宁愿购买五星商品,而不是零星商品——暗示背后的数据可能是坏的或缺失的……)。对于其他变量,例如经度或时间可以得出类似的结论。**

因此,试图改进模型性能的开发者可以为变量的权重设置阈值,并检查仅基于超过阈值的特征的更简单的模型是否提供具有更少变量的类似预测。最终用户可以通过改进由变量product lvl 2–3 name定义的产品的已访问页面的内容来增加访问持续时间并调整访问 nb 页面访问唯一页面来从该信息中获益。

对模型结果的解释不再是产品的附加特征;这对于确保透明度、提高对产品的信任度以及满足有关数据利用的最新法规至关重要。

LIME 是一个相当灵活的工具,可以提供这些可能性。通过使用 LIME,我们可以深入了解预测背后的基本原理,并得出结论,这些结论可以反馈到模型中以进一步改进模型,或者由最终用户用来制定销售策略。

解释器的灵活性有其第一次使用的复杂性的缺点。如果预测模型相当复杂(如我们的情况),必须进行密集的预处理和后处理,以使模型与解释者相匹配,而不会得到误导性的解释。此外,对参数的敏感性要求进行精确的分析,以确保对问题采用最佳的解释;这项任务计算量很大。

预测模型中递归网络的使用使得解释器的直接使用变得困难;需要更多的努力来获得对解释结果的更透彻的了解。

总的来说,石灰是一种有用的工具,但在使用前需要仔细准备。

在未来的帖子中,我们将测试 SHAP 对同一模型的能力,以检查我们是否遇到了与 LIME 相同的问题,以及它是否提供了更好的适合循环网络的解释。

这项工作已经在 easyence 数据实验室内完成。

充分利用细分市场

原文:https://towardsdatascience.com/squeezing-the-most-out-of-segmentation-466db9d08573?source=collection_archive---------42-----------------------

这种分析有意义吗——还是我们将继续分裂?

杰西卡·路易斯在 Unsplash 上的照片

每当有人要求对我们的客户进行细分,然后提供预先确定的属性来对他们进行分组时,我会立即想到——你只是想让我过滤数据吗?这种细分是最好的,还是简单的受众创造?

如果我们参考分段的定义——也就是说,根据组中的相似性组之间的不同性来整理个体——那么上述要求看起来完全合理。那么,为什么它们会留下如此令人失望的余味呢?

事实上,我对这种类型的分割的不满源于分割所提供的丰富性。要提取的实在太多了(双浆!)将客户从他们的人口统计属性或描述性特征之外进行细分。以我自己为例——作为一名 30 多岁的女性,我对护发产品毫不关心(我认为一个好发型的一天就是我洗头的时候)。如果你把我分到一个完全基于人口统计的细分市场,你将很快告别你的空气动力学广告支出。

细分的美妙和丰富之处在于两个观点,这两个观点不仅证明了细分本身,还证明了细分分析。首先,我们如何看待漏斗——考虑个人在转化前后的行为倾向,以加强我们对他们的分组。第二,我们如何看待可扩展性——将细分分析作为营销和产品计划的持续措施进行整合。正是这种将个人分组的思维模式,提供了丰富的(有点讽刺意味的)个性化用户体验。

漏斗图:远离浅层……

虽然你可以收集的用于细分分析的第一方行为数据的数量通常取决于个人的转化漏斗有多低,但肯定不仅限于他们成为客户之后。事实上,有行为数据可以在漏斗的两个方向上增强细分,转换前和转换后。

行为数据不仅限于转化的收入阶段(AARRR)漏斗[图片由作者提供]

如果你观察漏斗的上部,你可以从意识阶段开始使用行为属性。潜在客户的行为可以通过网站用户搜索信息、消费内容和进行比较的方式的差异来挖掘。这些行为表明用户倾向于通过漏斗进一步移动,从而采取比仅使用人口统计特征的细分市场更有效的策略。

通过漏斗进一步遍历,你会发现超越收入(或交易)阶段的有价值的行为属性。利用行为深入研究了过去顾客在购买点的行为——揭示了突出产品更符合市场、更能体现其价值的持续趋势。举个简单的例子,顾客 A 购买了价值 100 美元的产品,但只使用了其中的 50%,他可能不如顾客 B 有价值,顾客 B 购买了价值 60 美元的产品,但使用了 100%。这些行为,以及推介行为,如推广人评论,透露了优化客户终身价值的机会;不仅仅是他们的人口统计学甚至交易属性传播了顾客对产品的适应性。

可扩展视图:运行精益 v 扩展

到目前为止,我已经通过采用全漏斗视图向您展示了细分的丰富性。那么,你如何利用这种方法呢?好消息是,好处既有眼前的,也有长远的——从获得快速洞察,到使用您的客户数据库将细分构建为可扩展的数据产品。此外,通过聚类分析对个体进行细分(不仅仅是任意决定如何对个体数据点进行分组)应用了既精简又可扩展的统计严谨性。

让我们从快速洞察开始——我最近已经能够修补 Tableau 相对精简但有洞察力的集群解决方案。它的功能非常适合您既想要简单又想要强大的方法,因为它支持:

  • 自动缩放——简单明了,但对各种规模的输入变量进行标准化非常必要;
  • 自动优化聚类数—使用 k-means 为给定的数据点描绘组。Tableau 采用了卡林斯基-哈拉巴斯指数来自动输出 k 个簇,而不是用肘法或类似的方法手动绘制出最优的簇数。或者,您可以定制您想要的集群数量(如果您由于预算限制而无法提供许多不同的细分市场,则不太可取,但也是可行的);
  • 探索性分析-能够快速可视化跨多个轴的基于统计的聚类(甚至是地理上的,如果在数据集中可用的话)。

当谈到作为数据产品的缩放分段时,这就是 Tableau 作为主要用于可视化的工具的局限性所在。尽管有更多的前期工作,但最好使用一种组合,在 R 或 Python 中实现统计分析(有各种各样的包可用于集群),并在您的客户群中自动运行它。与 Tableau 等开箱即用的解决方案相比,这种方法通过定制和自动化来实现更高的可扩展性:

  • 数据清理和转换——将代码分类变量编码为虚拟变量,并将空值重新编码为平均值或中值。Tableau 从其聚类中排除具有空值的数据点(简单地分配‘未聚类’)可能会扭曲您的分段,因此重新编码具有空值的数据点更理想;
  • 变量选择—确定用于聚类分析的变量。尽管 Tableau 提供了一个直观的拖放功能来在用于聚类的变量之间进行交换,但这并没有告诉您应该使用哪些变量。您可以在分割分析之前合并特征重要性分析来确定输入变量,而不是任意确定用于聚类的变量;
  • 聚类分配-根据离每个点最近的聚类中心将聚类分配给新的数据点。在 Tableau 中,对新数据的聚类分配是手动的——通过在 Dimensions 窗格中物理单击聚类(保存为组)上的“Refit”选项。如果您用您的集群分析构建了一个仪表板(像我一样),您将不能自动进行这种改装——即使数据源计划进行刷新。聚类自动化还可以包括基于新的数据点和对其行为的更新来安排新的聚类分析。这可能是有用的,例如,当你向市场推出新产品时;但是要谨慎行事——因为在实施新计划时,您还希望能够对集群的趋势变化进行基准测试和比较。每天运行新的聚类分析无法让您进行有根据的历史比较(不要忘记 set.seed!).

让我们打消细分仅仅是根据人口统计学或描述性变量对客户进行分组的想法。事实上,如果与全漏斗和规模化思维一起使用,那么细分可以更深入地了解我们的客户,最终为可持续的个性化体验提供动力。

SSWL-IDN:自监督 CT 去噪

原文:https://towardsdatascience.com/sswl-idn-self-supervised-ct-denoising-208fde94583e?source=collection_archive---------28-----------------------

思想和理论

回顾我们最近的 CT 去噪论文“窗位是强去噪代理”

作者图片

在这篇文章中,我将讨论我们最近的工作,一种新的自我监督的 CT 去噪方法: SSWL-IDN ,由来自萨拉托加高中和斯坦福大学的 RSL 的 Ayaan Haque(我)、王一行和 Abdullah-Al-Zubaer Imran 完成。在本文中,我们介绍了 SSWL-IDN,一种新的自监督 CT 去噪窗口级预测代理任务。我们的方法是任务相关的,并且与下游任务相关,与最近的方法相比产生了改进的性能。我们的论文最近被 2021 年的 MLMI MICCAI 接受,并将在九月发表。这篇文章将涵盖我们解决的问题,我们的方法,以及(简要地)我们的结果。我们的论文在 ArXiv 上可用,代码在 Github 上可用,我们的项目页面在这里可用。

概观

什么是 CT 去噪,为什么重要?

对于那些没有强大的医学成像背景,CT 成像是一个突出的成像方式。CT 成像依赖于辐射剂量,因此,在图像质量和辐射剂量之间存在权衡。辐射剂量越高,图像包含的噪声就越少。然而,高辐射剂量对患者有害,这意味着最好以较低的剂量扫描患者。然而,随着图像中噪声的增加,CT 图像的诊断性能降低,因为噪声可能阻碍某些结构的可见性。因此,对 CT 图像进行去噪处理以达到两全其美是一个非常关键的医学成像问题。

为了执行基于深度学习的 CT 图像去噪,模型将输入低剂量 CT 扫描(LDCT)并预测全剂量 CT 扫描(FDCT)。全剂量扫描按常规剂量采集,低剂量扫描一般按四分之一剂量采集。然而,这构成了一个明显的挑战。医疗数据通常很难获得,尤其是 CT 扫描,因为很难同时获得干净的参考和同一扫描的低剂量版本。但是,在标签数据有限的情况下,深度学习性能会下降。这意味着利用未标记数据的学习框架的使用至关重要。

什么是自我监督学习?

由于辐射的有害性质以及在不同辐射剂量下进行两次相同扫描的困难,获取参考图像是具有挑战性的。因此,期望用有限的参考数据来训练去噪模型。自我监督学习(SSL)已经成为完全监督学习的一种有前途的替代方法,以便利用大量的未标记训练样本。在 SSL 方案中,可以从数据本身为标记和未标记的数据生成合成标签。与迁移学习类似,SSL 在代理任务上预先训练模型,但在相同的数据集上而不是来自外部域的数据集上,然后在下游或主要评估任务上微调预先训练的模型。

自我监督学习是无监督学习的一种形式,因为它使用完全免费的标签对单独的任务进行训练。一些常见的例子包括随机旋转图像和让模型预测图像旋转的角度。

我们的自我监督学习形式使用两个独立的任务:一个代理任务和一个下游任务。这不要与完全无监督的方法相混淆,例如 Noise2Noise ( Lehtinen 等人 2018 )和 Noise2Void ( Krull 等人 2018 )。这些方法根本不使用任何参考扫描。这不是计算机视觉领域的传统 SSL 定义。由于这些方法不使用任何参考扫描,正如这些论文的作者所争论的那样,以任何方式与使用参考扫描的方法进行比较都是不公平的。为了证明这一说法,我们将我们的 SSL 算法与 Noise2Void 进行了比较。

在本文中,我们使用 SSL 来提高具有有限参考 FDCT 的深度去噪模型的性能。我们提出了一种新的去噪替代预测窗口水平的 ct 图像从非窗口水平的图像作为借口。与许多其他现有的自监督学习方法不同,我们提出的自监督窗口水平(SSWL)是一个任务相关的代理,因为它直接与下游任务相关,优先考虑相似的特征学习。此外,我们将所有实验限制在 5%的剂量水平,这可能是一种积极的剂量减少机制,并证明即使在如此低的剂量设置下也有效。我们的主要贡献如下:

  • 一种新的与任务相关的自监督窗口级预测代理,它与下游任务相关
  • 一种创新的基于残差的 VAE 架构,与混合损失函数相结合,同时对模型进行逐像素和感知惩罚
  • 对域内和跨域数据上不同建议成分的不同数量的标记数据进行的大量实验证明,即使是极低剂量(5%)的 CT 图像,也能实现改进和有效的去噪

SSWL-IDN

窗口调平及 CT 去噪

去噪和窗位的关系(图片由作者提供)

在 CT 去噪中,输入图像是 LDCT,参考图像是 FDCT。两者的关系表示为 LD = FD + Noise。去噪模型旨在从 LD 中去除噪声并重新获得 FD。

在 CT 成像中,窗位调整是使用 CT 数修改图像灰度的过程,以突出、增亮和对比重要的结构。窗位化(WL)和非窗位化(NWL)图像之间的关系表示为:WL = a * NWL + b,其中 a 和 b 是窗位化参数。如图所示,从图像变换的角度来看,窗位扫描和非窗位扫描之间的关系可以比作 FDCT 和 LDCT 之间的关系。因此,训练模型以从非窗位图像预测窗位图像是与去噪相关的任务。

提议的 SSWL-IDN 模型的示意图。对于 SSL 代理任务,该模型从非窗口级别图像预测窗口级别图像。对于下游任务,模型将输入 LDCT 去噪以匹配 FDCT。(图片由作者提供)

预测分级图像是一项自我监督的任务,因为窗口分级图像可以自由创建,因为这样做的信息在 DICOM 元数据中是自由可用的。具体而言,当获得全剂量参考图像极其困难时,将其作为下游去噪任务的借口更合适。特别是因为任务是特定领域的,它允许比外来或任意代理更重要和相关的特征学习。

因此,我们提出的自监督学习方法包括两个步骤:在窗口水平任务上的全监督预训练,然后在小标签去噪任务上的微调。对于预训练,我们为标记和未标记的数据准备了每次 LDCT 扫描的 NWL 和 WL 版本。Loss 针对从输入 NWL LDCT 预测 WL LDCT 进行了优化。我们的代理是端到端的,与许多其他不使用相关任务的方法相反,因为在任务之间不需要架构或损失的改变。

模型架构和损失函数

对于模型结构,我们提出了残差变分自动编码器(RVAE)。我们的模型使用 RED-CNN ( Chen et al. 2017 )作为主干架构,并加入了一个瓶颈。虽然已经提出了基于残差的 vae,但是它们在编码器和解码器中分别使用残差(ResNet 作为编码器,转置 ResNet 作为解码器),而不是像以前的方法那样使用编码器和解码器之间的残差连接。在瓶颈中使用参数化技巧改善了 FD 预测,因为通过添加可调噪声,我们可以减少过拟合,改善泛化,并有助于正则化模型。

对于我们的损失,我们使用 MSE 和感知损失之间的混合损失。这鼓励了逐像素和感知学习,这将提高定量和定性去噪性能。我们的感知损失使用从预训练的 VGG-19 网络中提取的特征。我们的总损失函数表示为:

我们的总损失函数(图片作者)

其中,LMSE 是标准 MSE 损失,l 感知是感知损失,β是 l 感知权重。对于 VAE,LKL 代表 KL 发散损失,α是 LKL 重量。是均值项,σ是标准差项,都来自于潜在空间。KL 发散试图减少两个参数与目标分布参数的发散。

结果

我们使用了梅奥低剂量 CT 数据集。具体来说,我们的目标是在超低剂量下降噪,因此我们将四分之一剂量扫描缩小到 5%剂量。这允许进行彻底的去噪评估。虽然从临床角度来看,与质量较低的去噪 5%剂量相比,去噪良好的四分之一剂量更理想,但从计算角度来看,显示去除大量噪声的能力可以更恰当地评估模型准确去除噪声的全部潜力。

我们将我们的 RVAE 架构与各种基线和最先进的架构进行了比较,并将我们的 SSWL 方法与各种基线和最先进的 SSL 训练算法进行了比较。

我们的 RVAE 与各种架构进行了比较,并优于所有架构(图片由作者提供)

如上表所示,我们的 RVAE 优于所有具有统计学意义的模型。重要的是,我们优于两个架构,RED-CNN 和 DnCNN(【张等人 2016 ),证明了 RVAE 方法的有效性。此外,我们的跨域结果显示了我们的架构的改进的通用性。

我们的 SSWL-IDN 模型优于所有的基线 SSL 方法(图片由作者提供)

更重要的是,我们的自监督窗口水平代理任务优于基线和两种最先进的方法,Noise2Void (N2V)和 noise-As-Clean(NAC)(Xu et al . 2019)。这显示了任务相关性对于 CT 去噪的重要性,并且证实了我们的方法的强性能。混合损失也是有益的,与 RVAE + SSWL 相比,我们的最终模型具有更好的性能。

SSWL 和我们的 RVAE 优于其他架构和 SSL 方法

上图展示了我们的网络对 5%剂量 CT 扫描进行精确降噪的能力。虽然似乎有过度光滑和丢失的结构细节,这可以归因于低剂量的 CT 扫描。此外,我们的 SSWL 算法优于没有 SSL 和我们的 RVAE 优于红色 CNN 本身。

在基于 ROI 的定性去噪方面,SSWL 优于我们的 SSL 方法

最后,我们在上图中证实了我们的算法相对于其他 SSL 方法的定性优势。如图所示,当检查特定的 ROI 时,我们比其他方法获得了更清晰的去噪图像。

最后的想法

在这篇文章中,我们提出了 SSWL-IDN,一个自我监督的去噪模型,一个新颖的,任务相关的,有效的窗口级预测的替代任务。我们还提出了专门用于去噪的残差 VAE,以及利用感知和像素级学习优点的混合损失。我们证实,我们的方法的每个组成部分都优于域内和跨域评估的困难的 5%剂量去噪基线,并且当组合时,该模型显著优于最先进的方法。

这个框架的最终目的是帮助减少在临床环境中使用高 CT 辐射剂量。如果我们的方法能够在有限的标记数据上进行训练,并准确地对 CT 扫描进行去噪,那么可以以较低的剂量对患者进行扫描,并且可以在专家诊断或筛查之前对扫描进行去噪。

如果你觉得这篇文章或论文有趣,请告诉我!如果你觉得这项工作有用,下面是引文:

@article{haque2021window,title={Window-Level is a Strong Denoising Surrogate},author={Haque, Ayaan and Wang, Adam and Imran, Abdullah-Al-Zubaer},journal={arXiv preprint arXiv:2105.07153},year={2021}
}

断续浓缩咖啡比普通浓缩咖啡更好

原文:https://towardsdatascience.com/staccato-espresso-is-fundamentally-better-than-regular-espresso-e73e88337127?source=collection_archive---------5-----------------------

咖啡数据科学

水接触时间

断奏法的历史很有趣,因为证明它有效的证据最初只是在味觉上。后来在萃取过程中证明了这一点,但仍然有人持怀疑态度,部分原因是筛选咖啡需要大量的工作。然而,我相信我已经明白了为什么断奏击球在提取方面应该从根本上优于常规击球,答案是水接触时间。

断奏镜头是在使用至少两层筛选研磨后的分层镜头。我喜欢的变体在底部有一个精细层(<400 um), a coarse layer in the middle (>500 微米),在顶部有一个中间层(在 400 微米和 500 微米之间)。

所有图片由作者提供

研磨尺寸和提取

一般来说,众所周知,当温度、压力和水流等所有参数保持不变时,较细的咖啡比较粗的咖啡在水中提取出更快的 T4。所以当你提取一杯浓缩咖啡时,到最后,较细的颗粒倾向于被过度提取,而较粗的颗粒倾向于被更少提取。我们的目标是拉一杆足够长的时间,使大部分咖啡被适当提取。

我做了一项研究,使用受控场景提取单个研磨粒度,这样就不会出现沟道效应。对于所有研磨尺寸,该实验也具有相同的水接触时间、水温和压力。在这种情况下,我将镜头分成三杯:第一杯、第二杯和第三杯。该图显示了每种情况的累积 EY,并且显示了更高提取的趋势,尤其是在较小研磨的第一部分。

常规射击接触时间

对于在整个击球过程中均匀分布的常规击球,在冰球的顶部会有细粒,杯子中的所有水都从这些细粒中穿过。

假设我们用 20 克咖啡渣和 40 克液体咖啡。为了简单起见,假设冰球中的水量是 20g,因为通常冰球会保留那么多。因此,底部的颗粒在第一次滴入杯中时几乎没有接触水的时间,而圆盘顶部的颗粒已经暴露在 20g 的水流中。

这意味着关于提取,圆盘顶部的细粒将比圆盘底部的细粒具有更高的提取,并且它们将是第一个过度提取的。

此外,对于粗颗粒,位于圆盘底部的粗颗粒将更有可能提取不足,而位于圆盘顶部的粗颗粒将更有可能被正确提取。

当观察温度时,让我们假设你进行低压预输注。在预注入过程中,圆盘底部的水的温度将低于圆盘顶部的温度。这一温度最终会达到平衡,但当存在温差时,在所有其他参数相同的情况下,较高的温度将导致顶部比底部提取得更快。如果你击球的时间足够长,这一点可能会静音,但话说回来,你击球的时间越长,你就越有可能从冰球顶部开始过度提取咖啡。

断续接触时间

对于断奏击球,罚金在底部。粉末是过度提取味道的最大原因,因为它们提取得最快。

当镜头结束时,精细层将比其他层具有更少的接触时间,因为它位于底部。细粒的过量或不足提取量将更加均匀。其他层也是如此。

此外,其他两层比细粒层更粗糙,与水的接触时间更长,导致它们提取更多,提取率更接近细粒层。每一层在整个层中具有相似的接触时间,因此这些颗粒尺寸的提取应该比常规拍摄更均匀。

按层数据提取

我收集了一些以前的分层提取数据。我拥有的主要数据是使用干燥的用过的冰球重量筛选断奏和断奏夯实的击球。对于筛选过的断续镜头,精细层提取的内容比中/粗层多得多。而对于断续夯实,中/粗层是顶层,其提取率高于底层。就粒子分布而言,断续夯实的镜头是规则的镜头。

此外,我已经收集了一些其他数据,使用地面总溶解固体(gTDS) 。我有多个断奏夯实的镜头,我测量了镜头上半部分和下半部分的 GTD。较高的数值意味着仍有更多的可提取固体。

在大部分数据中,顶部的 gTDS 比底部的 gTDS 低得多,这意味着顶部的一半比底部提取得多。这为圆盘的上半部分比下半部分提取得更多或者提取得比底部更快的想法提供了一些证据。

其他胜利

断奏击球不容易出现沟流,因为通过筛分,水可以更均匀地流过每一层。每个颗粒层在该尺寸下得到更均匀的提取。

典型的断奏镜头有较少的克莉玛。克莉玛是由提取过程中释放的二氧化碳引起的,我怀疑这与提取速率有关。因此,如果提取率较高的颗粒位于底部,释放气体在发射过程中对较大颗粒尺寸的干扰较小。

高端研磨机

最初反对断奏的一个论点是,我的研磨机并不好。我用的是韩国磨床,但结果是,韩国比利基表现得更好。尽管如此,人们认为更高端的研磨机应该表现更好,但从根本上说,他们不能。

不管是什么研磨机,一个普通的球在冰球中都有接触水的时间问题。因此,如果你从任何研磨机中取出一杯咖啡的咖啡渣并过筛,过筛后的咖啡渣应该总是具有更大或相等的提取率,并且提取应该在颗粒研磨中更加均匀,因此,味道应该相同或更好。

查看以前的数据

我提取了一些数据,我必须把提取率(EY)的成对拍摄放在一起。对于这些镜头,我有 1:1 的输出与输入比率以及 3:1 的数据,因为我使用了杠杆机器,并在拍摄后在下面放了另一个杯子。在所有其他参数都相同的情况下,在 5 次烘烤和 2 台机器(Kim 和 Flair)上有 9 对镜头。

在大多数双人舞中,EY 的断奏得分胜出。这是我们用来比较咖啡萃取技术的最客观的标准。如果一个断奏的镜头使用拨入镜头中的理由进行适当筛选,它应该比使用未经筛选的相同理由具有更高的 EY。

此外,当查看数百个镜头的总溶解固体和 EY 的控制图时,TDS 与 EY 的断续镜头趋势非常高。

所有这些对断奏的理解都是基于正确的筛选。如果根据颗粒分布,预计 40%的粉末小于 400 微米,但只筛出 14%,那么这意味着筛出的颗粒味道可能完全不同。这需要对筛选过程进行改进,比如在筛子中放置一个搅拌器,这样几乎可以减少一半的筛选时间和精力。加重的纸张搅拌器甚至可以把时间缩短到 100 米

我坚信我们正处于第四波咖啡的边缘。虽然筛选需要时间,但我相信新机器会造得更快、更自动化。

断续击球有望获得更高的提取率。

如果你愿意,可以在推特、 YouTube 和 Instagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在中和订阅。

我的进一步阅读:

我未来的书

浓缩咖啡系列文章

工作和学校故事集

个人故事和关注点

乐高故事启动页面

摄影飞溅页面

使用图像处理测量咖啡研磨颗粒分布

改进浓缩咖啡

断奏生活方式概述

测量咖啡磨粒分布

咖啡萃取

咖啡烘焙

咖啡豆

浓缩咖啡用纸质过滤器

浓缩咖啡篮及相关主题

意式咖啡观点

透明 Portafilter 实验

杠杆机维护

咖啡评论和想法

咖啡实验

堆叠式自动编码器。

原文:https://towardsdatascience.com/stacked-autoencoders-f0a4391ae282?source=collection_archive---------4-----------------------

使用深度学习从数据中提取重要特征。

米卡·鲍梅斯特在 Unsplash 上的照片

降维

在解决数据科学问题时,您是否遇到过包含数百个要素的数据集?或者一千个特征?如果没有,那么你就不知道开发一个高效的模型会有多大的挑战性。对于那些不知道的人来说,降维是一种从数据中过滤出本质特征的方法。

数据中的输入要素越多,预测从属要素的任务就越困难。大量的元素有时会导致模型性能不佳。这背后的原因可能是模型可能试图找到特征向量和输出向量之间的关系,这种关系非常弱或不存在。有各种方法可以用来降低数据的维度,在下面的链接中可以找到关于这方面的综合指南。

https://www . analyticsvidhya . com/blog/2018/08/dimensionally-reduction-techniques-python/

主成分分析

主成分分析是一种常用的降维方法。主成分分析可以帮助你找到最相关的特征向量。这组新的特征称为主成分。提取第一个主成分,以便它解释数据集中的最大变化。第二个中心成分与第一个无关,试图解释数据集中的其余变化。第三个主成分试图解释前两个主成分解释不了的解释,以此类推。尽管这种方法有助于我们降低维数,但是 PCA 仅在从属特征和独立特征之间的关系是线性时才有效。要更深入地了解 PCA,请访问下面的链接。

https://towards data science . com/a-一站式主成分分析-5582fb7e0a9c

自动编码器

当非线性函数描述从属和独立特征之间的关系时,自动编码器用于减少数据的维数。自动编码器是一种无监督的人工神经网络。自动编码器用于从数据中自动提取特征。它是最有前途的特征提取工具之一,用于各种应用,如语音识别、自动驾驶汽车、人脸对齐/人体手势检测。下图显示了自动编码器的架构

自动编码器来源:自动编码器介绍。

如上图所示,自动编码器架构分为三个部分:编码器、瓶颈和解码器。编码器从数据中挑选关键特征,而解码器试图使用关键成分来重建原始数据。通过仅保留重建数据所需的特征,自动编码器降低了数据维度。自动编码器是一种前馈网络,可以使用与前馈网络相同的过程来训练。自动编码器的输出与输入相同,但有一些损耗。因此,自动编码器也被称为有损压缩技术。此外,如果我们在每个编码器和解码器中有一个具有线性激活函数的密集层,则自动编码器可以像 PCA 一样执行。

堆叠自动编码器

一些数据集的要素之间存在复杂的关系。因此,仅使用一个自动编码器是不够的。单个自动编码器可能无法降低输入特征的维数。因此,对于这样的用例,我们使用堆栈式自动编码器。顾名思义,堆叠式自动编码器是多个编码器堆叠在一起。下图显示了一个堆叠式自动编码器,其中三个编码器堆叠在一起。

作者图片

根据上图所示的架构,输入数据首先提供给自动编码器 1。然后,自动编码器 1 的输出和自动编码器 1 的输入作为自动编码器 2 的输入。类似地,自动编码器 2 的输出和自动编码器 2 的输入作为自动编码器 3 的输入给出。因此,自动编码器 3 的输入向量的长度是自动编码器 2 的输入的两倍。这种技术在一定程度上也有助于解决数据不足的问题。

使用 python 实现堆栈式自动编码器

为了演示堆叠式自动编码器,我们使用振动信号的快速傅立叶变换(FFT)。FFT 振动信号用于故障诊断和许多其他应用。数据具有非常复杂的模式,因此单个自动编码器无法降低数据的维数。下图是 FFT 波形图。FFT 的幅度被变换到 0 和 1 之间。

作者图片

为了更好的直观理解,我们将信号重塑成 63*63 的矩阵,并绘制出来(由于是振动信号转换成图像,所以要半信半疑)。下图是振动信号的图像表示。

作者图片

我知道在这幅图像中很难看到很多东西。不过,我们还是能在图中看到几个特征。大约在(0,15)处拍摄的亮白色是振动信号 FFT 中的峰值。

现在我们开始创建我们的自动编码器。

batch_size = 32
input_dim = x_train[0].shape[0] #num of predictor variables learning_rate = 1e-5
input_layer = Input(shape=(input_dim, ), name=”input”)#Input Layer
encoder = Dense (2000, activation=”relu”, activity_regularizer=regularizers.l1(learning_rate))(input_layer)#Encoder’s first dense layer
encoder = Dense (1000, activation=”relu”,
activity_regularizer=regularizers.l1(learning_rate))(encoder)#Encoder’s second dense layer
encoder = Dense (500, activation=”relu”, activity_regularizer=regularizers.l1(learning_rate))(encoder)# Code layer
encoder = Dense (200, activation=”relu”, activity_regularizer=regularizers.l1(learning_rate))(encoder)# Decoder’s first dense layer
decoder = Dense(500, activation=”relu”, activity_regularizer=regularizers.l1(learning_rate))(encoder)# Decoder’s second dense layer
decoder = Dense(1000, activation=”relu”, activity_regularizer=regularizers.l1(learning_rate))(decoder)# Decoder’s Third dense layer
decoder = Dense(2000, activation=”relu”, activity_regularizer=regularizers.l1(learning_rate))(decoder)# Output Layer
decoder = Dense(input_dim, activation=”sigmoid”, activity_regularizer=regularizers.l1(learning_rate))(decoder)

上面设计的自动编码器在两侧有两个密集层:编码器和解码器。请注意,每个解码器和编码器中的神经元数量是相同的。此外,解码器是编码器的镜像。

正如我们所见,FFT 信号有 4000 个数据点;因此,我们的输入和输出层有 4000 个神经元。当我们深入网络时,神经元的数量随之减少。最后,在代码层,我们只有 200 个神经元。因此,这个自动编码器试图将特征的数量从 4000 减少到 200。

现在,我们构建模型,编译它,并根据我们的训练数据进行拟合。由于自动编码器的目标输出与输入相同,我们将 x_train 作为输入和输出传递。

autoencoder_1 = Model(inputs=input_layer, outputs=decoder)autoencoder_1.compile(metrics=[‘accuracy’],loss=’mean_squared_error’,optimizer=’adam’)satck_1 = autoencoder_1.fit(x_train, x_train,epochs=200,batch_size=batch_size)

一旦我们训练了我们的第一个自动编码器,我们连接第一个自动编码器的输出和输入。

autoencoder_2_input = autoencoder_1.predict(x_train)autoencoder_2_input = np.concatenate((autoencoder_2_input , x_train))

现在,自动编码器 2 的输入准备就绪。因此,我们在新的数据集上构建、编译和训练 autoencoder 2

autoencoder_2 = Model(inputs=input_layer, outputs=decoder)autoencoder_2.compile(metrics=[‘accuracy’],loss=’mean_squared_error’,optimizer=’adam’)satck_2 = autoencoder_2.fit(autoencoder_2_input, autoencoder_2_input,epochs=100,batch_size=batch_size)

一旦我们训练了我们的自动编码器 2,我们就开始训练我们的第三个自动编码器。正如我们对第二个自动编码器所做的那样,第三个自动编码器的输入是第二个自动编码器的输出和输入的串联。

autoencoder_3_input = autoencoder_2.predict(autoencoder_2_input)autoencoder_3_input = np.concatenate((autoencoder_3_input, autoencoder_2_input))

现在,最后,我们训练第三个自动编码器。正如我们对上两个编码器所做的那样,我们对新数据进行构建、编译和训练。

autoencoder_2 = Model(inputs=input_layer, outputs=decoder)autoencoder_3.compile(metrics=[‘accuracy’], loss=’mean_squared_error’, optimizer=’adam’)satck_3 = autoencoder_3.fit(autoencoder_3_input, autoencoder_3_input, epochs=50, batch_size=16)

在训练我们的堆叠式自动编码器之后,我们达到了大约 90%的准确率。这意味着我们的堆叠式自动编码器可以以大约 90%的准确度重建我们的原始输入信号。

原始信号和重建信号的图像如下所示。

作者图片

Plotly Express 的堆积条形图

原文:https://towardsdatascience.com/stacked-bar-charts-with-plotly-express-85885e91874f?source=collection_archive---------6-----------------------

长格式与宽格式数据

图片来自 Unsplash 的 Bekir Donmez

阴谋地表达

总部位于加拿大蒙特利尔的计算公司 Plotly 开发了 plotly.py,这是一个用于 Python 的交互式开源可视化工具。2019 年,该公司发布了 Plotly 4.0,其中包括 Plotly Express,这是一个高级包装器,与 Plotly 生态系统的其余部分完全兼容。

Plotly Express (PE)是免费的,它提供了一个面向对象的图形创建接口。该工具不仅可以生成标准的 2D 图(条形图、折线图、散点图、饼图等)。),还包括复杂的 3D 散点图和曲面图。PE 可以将数据帧、列表和字典作为输入数据,用于快速绘图生成。特别是,“大多数情节都是由一个函数调用完成的,该函数调用接受一个 整齐的熊猫数据帧”(1)。

长格式数据,宽格式数据

数据有许多不同的格式。关于表格数据(以具有行和列的表格的形式呈现的信息),数据可以是长格式(整齐、窄或堆叠形式)或者可以是宽格式 ( 未堆叠或杂乱形式)。

宽格式数据的每个变量都有一个单独的列,而在长格式中,每一行都是一个单独的变量标识组合。长格式对于筛选和执行某些类型的聚合最方便,而宽格式通常用于随时间收集的数据。

来源:https://lost-stats . github . io/Data _ Manipulation/shape/shape _ panel _ Data _ from _ long _ to _ wide . html

Python 中的 Pandas 库有几种将长格式数据转换成宽格式的方法:df.pivot().reset_index();df.pivot_table();df.groupby();pd.crosstab.

从长型转换到宽型的过程通常被描述为枢转

要从宽格式转换为长格式,您可以使用: df.melt()or df.wide_to_long().

我们可能更熟悉宽格式,因为这是我们习惯于在 Excel 电子表格中工作的格式。所以,这种格式很直观,也更容易理解。宽格式的表格适用于汇总信息。尽管长格式数据不太常见,但它易于存储,允许快速转换为其他类型,并且对于某些可视化工具(如 Seaborn)来说很方便。长格式的表格适用于连续的数据记录。

该公司于 2020 年 5 月 26 日发布了 Plotly.py 版本。以前的版本只支持长格式熊猫数据帧。从现在开始,PE 也用宽格式的表格数据进行操作。他们还声称致力于混合格式数据,即长格式和宽格式数据的混合体。以下 2D-笛卡尔函数可以对宽格式和混合格式的数据进行操作:px.scatter, px.line, px.area, px.bar, px.histogram, px.violin, px.box, px.strip, px.funnel, px.density_heatmap and px.density_contour.

堆积条形图

堆积条形图(SBC)显示了主要分类变量及其子类之间的数量关系。每个条形代表一个主要类别,它被分成代表第二分类变量的子类别的段。该图表不仅显示了不同子类别之间的数量关系,而且还显示了与整个主要类别之间的数量关系。它们还用于显示子类别的组成如何随时间变化。

堆积条形图应用于比较和比例,但重点是成分。这种成分分析可以是静态的-对于某一时刻-或者是动态的-对于确定的时间段。

SBC 通过矩形条表示,可以像标准条形图一样水平或垂直定向。它们是二维的,有两个轴:一个轴显示类别,另一个轴显示数值。每个主要类别被分成代表第二分类变量的子类别的段。每个子类别的数量由水平或垂直首尾相连堆叠的矩形段的长度或高度表示。每个条形的最终高度或长度代表每个主要类别的总量(100%堆积条形图除外)。

同等子类别在每个条中必须有相同的颜色,以免混淆观众。主线条之间通常留有一些空间,以清楚地表明它们指的是离散的组。

有两种不同类型的 SBC:

1.- 简单堆叠条形图将每个子类别的绝对值放置在前一个子类别之后或之上。数轴具有数值的刻度。该图显示了每个子类别的绝对值,这些值的总和表示该类别的总数。通常,主杆具有不同的最终高度或长度。

2.- 100%堆积条形图将每个子类别的百分比放置在前一个子类别之后或之上。数轴具有百分比数字的刻度。该图显示了每个细分市场占整个类别的百分比。所有的主杆都有相同的高度。

带 Plotly Express 的堆积条形图

我们使用了从 Kaggle [2]下载的数据集。该数据集包括 10,000 名银行客户,其中提到了他们的年龄、工资、教育水平、婚姻状况、信用卡限额、信用卡类别和附加功能。银行经理对客户离开他们的信用卡服务感到不安(流失客户与现有客户)。因此,我们将确定一些分类变量(教育水平、婚姻状况)与流失情况之间是否存在任何关系。

首先,我们导入 Plotly Express 为 px,Pandas 库为 pd ,并将我们的 csv 文件转换成 dataframe:

import pandas as pdimport plotly.express as pxpath = 'your path'df = pd.read_csv(path + 'CreditCardCustomersCols.csv', index_col = False, header = 0, sep = ';', engine='python')

然后,我们选择客户条件作为我们的主要分类变量,选择教育水平作为第二分类变量。在 Kaggle 数据集中,客户条件由attachment _ Flag列[2]描述。由于数据集中的记录是长格式,我们使用 df.groupby() 将它们转换成宽格式。我们使用函数 size( )以绝对值(计数)或百分比值(百分比)来计算要绘制的元素的数量。

df_stack=df.groupby(['Attrition_Flag','Education_Level']).size().reset_index()df_stack['Percentage']=df.groupby(['Attrition_Flag','Education_Level']).size().groupby(level=0).apply(lambda x:100 * x/float(x.sum())).valuesdf_stack.columns= ['Attrition_Flag', 'Education_Level', 'Counts', 'Percentage']df_stack['Percentage'] =  df_stack['Percentage'].map('{:,.2f}%'.format) 

对于本文中的堆叠条形图,Plotly Express 函数为 px.bar ,对应的参数为:data _ framex =表示主分类变量的数据帧中的列名; y =表示每个子类别的绝对值或百分比值的 data_frame 中的列名;颜色数据帧中的列名代表第二分类变量的子类; barmode 决定相同位置坐标的条如何在图形上显示。通过堆叠将棒材堆叠在另一个的顶部。我们可以选择 barmode = 'overlay' 来绘制重叠条形图,或者选择 barmode = 'group' 来将条形图并排放置在簇状条形图中(https://towards data science . com/clustered-overlapped-bar-charts-94 f1 db 93778 e)。

我们用 update.layout 更新了图表:设置标题、x 轴名称、y 轴名称,用宽度高度设置图形尺寸。最后,我们使用默认的模板( plotly 、【带 Plotly Express 的直方图、主题&模板】、https://towardsdatascience . com/Histograms-with-Plotly-Express-e 9e 134 AE 37 ad)绘制图表。

fig = px.bar(df_stack, x = 'Attrition_Flag', y = 'Counts', color = 'Education_Level', barmode = 'stack')fig.update_layout(title = "Education Level Customers' Composition",xaxis_title = 'Customer Condition', yaxis_title = 'Counts', width = 1600, height = 1400)fig.show()

图 1:简单的堆叠条形图。作者用 Plotly Express 制作的图表。

图 1 显示了银行客户教育水平构成的简单堆积条形图。这种图形表示不允许我们进行很好的比较,所以我们决定用 100%堆积条形图( y='Percentage ')绘制相同的数据:

fig2=px.bar(df_stack, x='Attrition_Flag', y='Percentage',color='Education_Level', barmode   ='stack')fig2.update_layout(title = "Education Level Customers' Composition", xaxis_title = 'Customer Condition', yaxis_title =  'Percentage', width = 1600, height = 1400)fig2.show()

2:百分比堆积条形图。作者用 Plotly Express 制作的图表。

现在我们可以做一个直观的比较,但如果能包括数值就更好了:

fig3=px.bar(df_stack,x='Attrition_Flag',y='Percentage',color=  'Education_Level', barmode = 'stack',  text=df_stack['Percentage'])fig3.update_layout(title = "Education Level Customers' Composition", template = 'simple_white', xaxis_title = 'Customer Condition', yaxis_title = 'Percentage', width = 1600, height = 1400)fig3.show()

图 3:带批注的百分比堆积条形图。作者用 Plotly Express 制作的图表。

我们使用text = df _ stack[' Percentage ']作为注释。我们还把模板改成了‘simple _ white’,一个清晰图表的极简模板。现在,我们可以做一个适当的比较,结果我们可以说流失客户和现有客户的教育水平没有显著差异。

最后,我们想知道婚姻状况是否与减员情况有任何关系(color = ' marriage _ Status'):

fig4= px.bar(df_stack2, x = 'Attrition_Flag', y = 'Percentage', color = 'Marital_Status', barmode = 'stack', text=df_stack2['Percentage'])fig4.update_layout(title = "Marital Status Customers' Composition ",template = 'simple_white', xaxis_title = 'Customer Condition', yaxis_title = 'Percentage', width = 1600, height = 1400)fig4.show()

图 4:婚姻状况为第二分类变量的百分比堆积条形图。作者用 Plotly Express 制作的图表。

同样,我们可以肯定流失客户和现有客户的婚姻状况没有显著差异。

总结一下:

您可以用几行代码绘制简单的堆积条形图或百分比堆积条形图;

通常会先将数据集记录从长格式转换为宽格式;

请注意,虽然长格式也称为堆叠式,但使用宽数据或未堆叠数据的堆叠条形图可以更好地讲述故事。

如果你对这篇文章感兴趣,请阅读我以前的(【https://medium.com/@dar.wtz】T2):

带有 Plotly Express、趋势线和分面的散点图

带有 Plotly Express、主题和模板的直方图

参考文献

[1]:https://medium . com/plotly/introducing-plotly-express-808 df 010143d

【2】:https://www.kaggle.com/sakshigoyal7/credit-card-customers

多元时间序列的堆积机器学习模型

原文:https://towardsdatascience.com/stacking-machine-learning-models-for-multivariate-time-series-28a082f881?source=collection_archive---------1-----------------------

实践教程,通过 Dataland 进行讨论

使用堆栈集合预报 PM 2.5 空气污染

北京雾霾来自北京

ime 序列分析经常被视为数据科学的一个深奥的子领域。它不是。其他数据科学子领域有它们的特质(例如 NLP、推荐系统、图论等。),时间序列也是如此。时间序列是有特质的,不是截然不同的

如果你的目标是预测,你可能不需要经典的计量经济学模型——ARIMA、ARDL、VAR——以及它们的假设,包括平稳性,这取决于你的需求、方法和数据。事实上,一些著名的计量经济学家长期以来一直认为,平稳生成的多元时间序列数据剥夺了有用的动态趋势和关系,因此你这样做可能会丢弃有价值的信息。

存在 VAR(向量自回归)中的变量是否需要是静态的问题。Sims (1980 年)和 Sims,Stock 和 Watson (1990 年)建议反对差分,即使变量包含单位根。他们认为,风险值分析的目标是确定变量之间的相互关系,而不是确定参数估计值。反对差异的主要论点是,它“丢弃”了关于数据中共同运动的信息(例如协整关系的可能性)。同样,有人认为数据不需要去趋势化。— 沃尔特·恩德斯,应用计量经济学时间序列,第三版

让我来演示一下机器学习模型是如何很好地适用于时间序列预测的,我将通过堆叠一个机器学习模型来让它变得更有趣。您确实需要调整交叉验证过程,以尊重时间序列的时间顺序,但是一般的方法是相同的。同样,这是如果你的目标是预测,而你对假设检验和统计推断毫无兴趣的话。

从截面数据分析类比是显而易见的。如果你希望进行统计推断,那么你可能需要一个线性回归模型,并且(很大程度上)遵守高斯-马尔可夫假设。但是,如果你不需要假设检验,那么你可以使用随机森林或支持向量机或神经网络,完全不理会残差图和 p 值

正在讨论的数据集是与污染和天气相关的,目标是预测空气中每小时 2.5 微米颗粒物(“PM 2.5”)的浓度。它是一个连续变量。“PM 2.5”是微粒物质中最细的一类空气污染,并对健康构成重大威胁,因为这些微粒如此之细,以至于它们可以绕过人体的大部分自然防御,当被吸入时会进入肺部深处。这些数据可以在 UCI 资料库这里免费获取。[1]

[1]宋·(2016)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml];加州欧文:加州大学信息与计算机科学学院。

栈集合建模

我将用一些最成功的机器学习算法填充栈集合,但不会包括任何计量经济学时间序列模型。堆栈的第一阶段将包括以下基本模型:

  • 套索回归(套索)
  • 多层感知器 (MLP),一种人工神经网络
  • 线性支持向量回归 (SVR)
  • 支持向量机 (SVM) —限于 rbfsigmoidpoly 内核
  • 随机森林回归器 (RF)
  • XG 升压回归器 (XGB)

堆栈的第二个(也是最后一个)阶段是一个单一的元模型,由最受欢迎的线性回归(“OLS”)模型表示。下面是代表我的方法的简单图表。

根据时间序列的时间流程的三层堆栈模型(图片由作者提供)

没有“正确的”方法来做系综叠加建模。这主要是实践经验加上大量的尝试和测试。一种相当典型的方法是在第一阶段中使用每种机器学习算法中的几个设置为不同的超参数,然后将它们的预测馈送给元模型,该元模型根据这些预测和目标变量进行训练。

更复杂的设置可能涉及元模型之前的模型的附加层。数据分析公司 SAS 的一篇博客文章在这里解释了堆叠的总体思路。

如果堆栈整体建模有什么原则的话,我会想到三个原则:

  1. 组合各种算法,可以对感兴趣的特定数据提供像样的预测,但使用不同的方法。例如,我在这个集合中混合使用了线性模型、基于树的模型、支持向量模型和神经网络。
  2. 过度拟合通常是一个问题,因此在评估堆栈模型时严格的交叉验证非常重要。
  3. 小心地分离各种培训、验证和测试数据层以防止它们“渗透”到下一层。

目标变量

该数据集包含从 2010 年 1 月 1 日到 2014 年 12 月 31 日的五年内每小时的数据,包括北京的 PM 2.5 颗粒读数以及选定的天气变量,如温度、风向和降雨量等。原始 csv 文件中有43824 行。这项工作的目标变量将是提前一个周期(即提前一小时)的 PM 2.5 读数

目标变量的折线图表明数据中有很强的季节性,但没有明显的多年趋势。PM 2.5 读数按年分布的箱线图也表明缺乏趋势,尽管数据被限制在零,并且在较高值时似乎受到大量“异常值”的困扰。我想空气中的微粒含量不可能是负数。

然而,这些“异常值”中的一些可能根本不是异常值,因为空气污染的某些高发生率可能有某种模式。这就是领域知识和/或普通的求知欲变得重要的地方。让我们先仔细看看这些数据。

前 15 名 PM 2.5 读数

左边的表格列出了数据集中前十五个小时 PM 2.5 读数。这可能不会立即显而易见,但前三名的读数来自 2010 年和 2012 年农历新年 (LNY)的第一个小时。阴历不同于阳历,因为 LNY 可能落在一年中不同的公历日。传统上,新年是通过燃放鞭炮来迎接的。鞭炮产生大量烟雾和空气中的碎片,通常会造成空气污染。因此,我们需要在模型中考虑 LNY 午夜前后的时间。我想知道汽车是否能够识别这个?

早春时节,北京也很容易出现偶尔的沙尘暴,2010 年 3 月 22 日就出现了一次大沙尘暴,这也导致了上述 15 个榜单中的两个榜单。因此,该模型还需要考虑重大沙尘暴的发生率。沙尘暴可能会提前预报,尽管可能不会提前太久,因此肯定可以作为提前一小时 PM 2.5 预报的解释变量

在解释性分析的过程中,我还发现,作为一个整体,周末时段的 PM 2.5 读数高于工作日时段的 PM 2.5 读数。均值差异的 t 检验返回一个 4.6551t 统计量,意味着差异在 99%水平上显著。

复杂的季节性

此外,数据中还有复杂的季节性。每年 PM 2.5 读数的图表显示,在一年的开始和接近结束时出现明显的峰值,基本上是较冷的月份。这是因为北京的大部分污染是由室内供暖产生的,而且天冷时显然会有更多的供暖。这也可以解释为什么 PM 2.5 读数在周末往往更高。

2010-2014 年 PM 2.5 小时读数

当我们查看每月平均 PM 2.5 读数及其置信区间时,季节性变得更加明显。月度数据证实了上述假设的季节性,在温暖的月份记录的水平较低,在寒冷的月份记录的水平较高,在 2 月和 10 月达到高峰。顺便说一下,农历新年也往往是在 1 月下旬到 2 月下旬之间。

深入研究数据,还会发现明显的日内季节性。事实上,在这种情况下,每日的季节性几乎与每月的季节性一样重要。****PM 2.5 平均每小时读数在午夜左右开始达到一天中的最高水平,然后在下午 15:00 逐渐下降到底部,然后迅速上升到夜晚。这可能是加热效应,因为白天太阳升起时对加热的需求减少了。

所有这些数据分析表明了什么?我需要虚拟变量用于以下内容:

  • 每年 LNY 从晚上 9 点到凌晨 3 点的 7 个小时;
  • 大沙尘暴的日子;
  • 周末;
  • 月;和
  • 几个小时。

我对“大沙尘暴”的判断非常简单。只需在数据集中搜索每年的*“北京沙尘暴 201x”,无论搜索结果的首页出现的房源日期如何,都将被记录为“重大沙尘暴”。*

但是在进行必要的数据工程之前,我首先需要处理缺失值的紧迫问题。**

缺少目标值

数据的一个主要问题是缺失值完全在目标变量中。几乎 5%的目标观测值是 NaNs ,而三分之二的缺失数据是在前两年,即 2010 年和 2011 年。这是一个问题,因为训练数据将来自早期,而训练集中扭曲的目标变量值可能会导致模型在后期测试期间表现不佳。**

上面描述的季节性表明,简单地用估算总体平均值、中位数或众数来代替 NaN 值不是一个好主意。现有数据中的随机赋值结转上一次观测值也不会。一些(每小时)丢失的数据也连续几天流动,因此从一天流动到第二天的线性插值也会引入失真(记住当天的季节性)。面对目标变量复杂的季节性,这些典型的易缺失数据插补方法肯定会导致数据失真。**

最后,缺失值完全在目标变量中的事实表明,任何从其他变量(即后续解释变量)中估算这些值的尝试都意味着,人们正在设置制造的目标值,这些目标值将很容易通过那些相同的解释变量预测,无论人们选择使用哪种算法。**

这个练习不应该是对目标变量的插补方法的全面覆盖,所以我决定采用一个不太复杂的解决方案来解决这个问题,并考虑到每小时和每月的季节性。执行了一个三阶段流程:

  1. 删除遗漏的第一天的观测值(2010 年 1 月 1 日每小时 PM 2.5 读数全部为秒)。
  2. 在当天 0:00-14:00(含)之间的观测值内插“内”缺失值,然后在 15:00-23:00(含)之间的观测值内插。这一两步程序考虑了上述数据中的两部分日内季节性**
  3. 通过可用值中值估算剩余缺失值,可用值按各年的月份和小时分组。如前所述,鉴于 PM 2.5 读数中极值的显著出现,我决定使用中值而不是平均值。****

各日每小时 PM 2.5 的两步插值代码

相关矩阵

在缺失数据插补之后,我接着研究了目标变量相对于数据集中其他连续变量的相关矩阵。许多与天气相关的变量似乎与目标变量的相关性很弱。

同时,几个天气变量之间也有很高的相关性,例如“dewp”、“temp”和“pres”。这些是露点、温度和压力读数。如果要丢弃这三个变量中的一个,“pres”似乎是最明显的,因为它与其他两个变量(“temp”和“dewp”)成对相关,而这三个变量与目标变量的相关性最低。所以我从分析中去掉了的“pres”、“cr”和“cs”变量。

之前关于世界各地 PM 2.5 污染的研究风力*确定为主要解释因素。基本上,持续的大风条件能有效地驱散空气传播的污染。数据集中的“cws”变量是一个累积风力强度变量,从上面我们可以看出,它与目标变量的相关性为+0.25。*

在这个特例中,风速和风向之间有一个有趣的相互作用 偏北风使“cws”与 PM 2.5 之间产生更强的相关性,同时也降低了温度与 PM 2.5 之间的相关性。您可以在下面的两个相关矩阵中观察到这种现象,第一个是当风向混合/不确定时,第二个是当有东北风吹时。

**

研究结果表明,我应该在北风方向和“cws”之间设计一个相互作用变量。

自相关和平稳性

由于这是一个每小时的时间序列,并且是关于空气污染的,因此因变量中的自相关将会很高是合乎逻辑的。空气中的 PM 2.5 颗粒物不太可能从一个小时到下一个小时突然出现或消失。相反,它们会随着时间的推移逐渐积累或消散。因此,在目标变量的滞后中有显著的自相关,正如我们在下面的左图中看到的。

然而,部分自相关在两个滞后之后迅速消失。时间序列爱好者会立即意识到这是至少一个 AR(2) 序列的迹象。例如,如果我要运行一个 ARIMA 模型,我会在模型中包含目标变量的至少两个滞后。虽然我在分析中没有使用任何计量经济学时间序列模型,但它确实表明 PM 2.5 读数的滞后应该作为特征纳入模型中。

此外,PM 2.5 颗粒读数缺乏趋势表明目标变量自然是稳定的。事实上,数据集中所有的连续变量都是平稳的。增强的 Dickey-Fuller* 测试证实非平稳零假设对于所有变量都被拒绝。*

Statsmodels 的 BIC 结果自动 arma* 工具仅推荐目标变量的两个滞后,而 AIC 结果建议十九个滞后。在这种情况下,我宁愿更节俭。有趣的是,AIC 得分在三次滞后时达到了最初的低点,随后随着更大的滞后进一步下降。最终,我决定采用目标变量的三个滞后值*

解释变量

这需要经历很多,但却是完全必要的。特征工程往往比运行花哨的算法或超参数调优更重要,我也会这么做!最终,我得到了下面的变量列表:

目标:

  • PM 2.5 读数提前一个周期

特点:

  • “PM 2.5”的当前和两个滞后
  • “温度”-温度(电流)
  • “露点”-露点(电流)
  • “CWS”-累积风速(海流)
  • 三个“cbwd”模型——风向(海流)
  • 北风方向的交互变量&“CWS”(海流)
  • 农历新年假人
  • 大沙暴假人
  • 周末假人
  • 小时假人
  • 月假人

最后五个虚拟变量都与目标变量提前一个周期或同时设置。这是因为 LNY 日期、主要沙尘暴、周末、时间和月份都可以至少提前一小时正确预测。我们可能不知道未来一两个小时的风向,但我们肯定会同意晚上 8 点之后是晚上 9 点,或者 9 月之后是 10 月,或者下一个农历新年将在 2022 年 2 月 1 日。

作为一个题外话,我意识到通过时间和日历变量的三角变换伪造的循环时间变量通常可以成为一个有用的替代品。但是循环时间变量不能在基于树的模型中使用,所以我决定在这里不使用它们,因为我对在栈集合中使用这样的模型感兴趣。

或者另一方面,在一个栈集合中,将不同定制的特性提供给不同的算法是完全可能的。我设计了这样的堆栈。然而,让我们在这个练习中让事情变得更简单。

堆叠过程和网格搜索

现在我们终于到达建模。我首先用三种方式对数据进行子集化:gridsearch 交叉验证训练数据、元模型训练数据和维持测试数据。在这种情况下,当我处理时间序列时,有必要一丝不苟地尊重分割中向前的时间流。

数据按照时间的向前流动分成三种方式(图片由作者提供)

数据的最新 10% (根据时间流程)被用作保持测试集,包括 4380 个观察值。其余 90%的数据中,最早的三分之二观测值被分配给 gridsearch 训练数据(第一批训练数据),而后来的三分之一被分配给元模型训练数据(第二批)。

对于使用 StandardScaler()数据缩放程序,只有 gridsearch(第一批)训练数据被**拟合转换。另外两个子样本是仅经过变换的**。我复制了下面的建模方法的早期图表。

根据时间序列的时间流程的三层堆栈模型(图片由作者提供)

通过 GridsearchCV (使用 TimeSeriesSplit==3 )调整每个基础模型,以在 gridsearch(第一批)训练数据上找到它们的最佳超参数设置。每个后网格搜索模型然后在完整的第一批训练数据上进行训练*。为了避免任何混淆,在这种情况下我说“完整”,因为 GridsearchCV 过程仅使模型适合每次迭代的一部分 gridsearch 训练数据。*

对于我上面的陈述,以及 Sklearn 的 GridsearchCV 函数是如何操作的,这里可能会有一些读者的进一步误解。在实际实现中,只要将 GridsearchCV 中的改装选项留在默认 设置,就不需要手动重新训练 gridsearch-tuned 模型。 GridsearchCV 随后将根据 gridsearch 中使用的全部训练数据自动训练最佳估计器(链接)。所以请把我的说法作为一个概念点。

元模型训练集(第二批数据)然后被馈送到每个训练的基础模型,以产生目标变量的预测。这些预测随后被用作元模型中的解释变量。在本练习中,元模型仅使用基础模型的预测作为其特征。换句话说,元 OLS 模型是通过基于基础模型的预测回归元训练集中的目标值来训练的。请注意,一些堆栈模型在训练元模型时包含了原始的解释变量,这很好,但是我在这里没有使用它们。

最后一步是让六个基本模型(仍然只对第一批数据进行训练)生成它们各自对维持测试集中的目标变量的预测。这些预测然后被馈送到元 OLS 模型(如上训练的),以产生测试集中目标值的堆栈模型的预测

该过程的摘要如下:

  1. 子集数据三路,持有最新的 10%的数据作为维持测试集,将剩余的 90%拆分为较早的 gridsearch 训练集(2/3)和较晚的元模型训练集(1/3);
  2. ***GridsearchCV***六个基础模型找到各自的最优超参数,然后在全 gridsearch 训练数据上训练调好的模型;
  3. 基模型对元训练集的预测形成解释变量对目标变量训练元模型*;和*
  4. 基础模型最终对维持测试集做出预测,这些预测再次被反馈到元模型,并且元模型对维持测试集中的目标变量的预测最终被评分(与基础模型的预测一起用于比较)。

所有型号仅安装/训练一次。基础模型只在 gridsearch(第一批)训练数据上训练,元模型在元训练集上的基础模型预测上训练。无论如何,完整的代码可以在我的 GitHub 页面上找到,文章底部的链接是给那些对细节感兴趣的读者的。

下面列出了 GridsearchCV 之后的调谐基础型号。没有对 OLS 元模型进行网格研究,也没有太大的必要,因为单个超参数只是一个常数项(在某种程度上,是否标准化解释变量的数据),我总是在这个练习中保留它。

基础模型的网格搜索后列表

正向链交叉验证

在对维持测试集进行评分之前,我使用基础模型的网格搜索后超参数设置对堆栈集合进行了一次 5 重交叉验证练习。对于交叉验证练习,我将训练集的两个批次(base+meta)组合起来,以将完整的 90%训练数据重新组成为 CV 集,并为迭代设置 CV==5

交叉验证通过正向链接或扩展窗口方法执行。在下面的 StackExchange 讨论中可以很好地解释这种时间序列 CV 方法。我复制了讨论中发布的图表来说明下面的方法,但应该强调的是,图表中的“数据”不包括维持测试集

https://stats . stack exchange . com/questions/14099/using-k-fold-cross-validation-for-time-series-model-selection

遗憾的是,Sklearn 的 TimeSeriesSplit 函数不能用于交叉验证过程。这是因为基础模型的预测形成了每个 CV 折叠中元模型的输入。我们需要在每个折叠中的交叉验证过程中提取每个基础模型的预测,然后将这些预测提供给元模型进行交叉验证。所以需要一个定制的代码。

为了确保在稍后对维持测试集进行评分之前,堆栈层之间没有任何数据污染的可能性,这里描述的交叉验证程序是在一个单独的 Jupyter 笔记本 上实现的,感兴趣的读者可以在我的 GitHub 页面上查看。

来自 5 重交叉验证程序的 MAE 和 RMSE 评分如下所示。我们看到“堆栈模型”,即 OLS 元模型,具有最低的平均值和第二低的中值 MAE 得分。然而,在 RMSE 得分中,“堆栈模式”的中值得分最低,但平均得分第二低。XGBoost 的平均 MAE 得分最低,而 Lasso 的平均 RMSE 得分最低。

交叉验证 MAE 分数&统计

交叉验证 RMSE 分数&统计

堆栈集成方法似乎在很大程度上如人们所愿地工作,即产生比集成中任何基础模型更精确的结果。它并不完全在那里,但是当我们走向坚持测试集时,它看起来很有希望。

保持测试结果

最好有一个基线模型进行比较,典型的时间序列基线是“持续性模型】。这只是通过目标变量的滞后值预测的目标变量的模型。在这种情况下,它将是由当前读数预测的提前一小时的 PM 2.5 读数。

如前所述,基本模型只在 gridsearch 训练集上训练一次,然后它们在维持测试集中对目标变量的值进行预测。这些预测形成了在元训练集上训练的 OLS 元模型的特征,然后在测试集上做出堆栈的最终预测。

下面列出了测试集中烟囱模型预测值与实际目标值的一些图表。我将图表的覆盖范围缩短到 400 次(每小时)观察,以使它们更容易理解。下面我们看到的是前 400 次观察

在测试集的前 400 次观测中,预测与实际 PM 2.5 读数的对比

然后中间 400 观察值如下所示。

在测试集的中间 400 次观测中,预测与实际 PM 2.5 读数的对比

我们看到栈模型在维持测试集上获得了最低的 MAE 和 RMSE 分数。此外,与基线持久性模型相比,stack ensemble 在 RMSE (18.66 比 19.76)和 MAE (10.53 比 11.12)得分方面分别实现了 5.5%的改进。

维持测试集的测试分数

**

还可以观察到,测试集上的堆栈的 RMSE 分数在上述 5 重交叉验证过程中观察到的范围内,但是 MAE 分数低于该范围。一般来说,维持测试集的错误分数往往比所有模型的交叉验证过程中观察到的错误分数低,这意味着测试集通常更容易预测(因此错误更低)。这突出了在以更全面的方式评估模型性能时进行多重交叉验证的重要性。

结论

机器学习算法非常适合时间序列预测。我利用一个机器学习堆栈来预测未来一个时期的 PM 2.5 空气污染水平。stack ensemble 包括线性模型、基于树的模型、支持向量模型和神经网络的多样化组合,作为基础模型。最终的超级模特是一直以来最受欢迎的 OLS。

这些数据表现出显著的异常值和复杂的季节性,并受到缺少目标值的困扰。花哨的算法和方法永远无法替代精心的预建模数据分析和工程。在此之后,根据时间顺序,数据被分成三部分,最新的 10%的数据作为维持测试集。剩下的 90%的数据又被分成用于基础模型的早期 gridsearch 训练集 (2/3),以及用于元模型的后期元训练集* (1/3)。***

训练数据(90%以上)还用于运行 5 重正向链交叉验证程序,以评估所有使用模型的模型性能。交叉验证发现堆栈模型在 MAE 和 RMSE 分数上大多优于单个基础模型**

维持测试集的后续结果显示了具有最佳 MAE 和 RMSE 分数的堆栈模型。栈的分数也证明了比基线持久性模型有5–6%的改进。总之,该练习展示了机器学习集成堆栈方法对多变量时间序列分析的有效性**

(本练习的完整 Python 代码和数据可在我的 GitHub 资源库中获得。如果直接渲染 GitHub 笔记本文件有问题,使用 nbviewer 。)

如果你看到了阅读这样的文章的价值,你可以在这里订阅 Mediumhttps://at-tan.medium.com/membership来阅读我和无数其他作家的其他文章。谢谢你。

**** https://medium.datadriveninvestor.com/bitcoins-shifting-relationship-to-macro-factors-5465d542078f ****

用于语音情感分析的堆叠机器学习模型

原文:https://towardsdatascience.com/stacking-machine-learning-models-for-speech-sentiment-analysis-adf433488845?source=collection_archive---------15-----------------------

如何建立一个从音频和文本记录中识别人类情感的模型?合著者:关,亚历山大洛朗。

照片由你好我是 Nik 上 Unsplash

在 Le Wagon 的 bootcamp 的最终项目的背景下,我和我的团队决定承担一项令人着迷的任务:语音情感识别。

这是一个巨大的挑战,因为情感在文化、性别、语言甚至个人层面上都是主观的,因此很难对人类情感进行普遍分类。

我们的数据

我们从卡耐基甜瓜大学找到了一个名为CMU-莫塞 的数据集,这是在线视频中句子级情感分析和情感识别的最大数据集。它包含超过 65 小时的注释视频,来自 1000 多名演讲者和 250 个主题。

数据被分成不同长度的片段,每个片段代表一个完整的口语句子(特征)和情感,我们的目标,它在值-3 到 3 之间变化(从负到正,0 是中性的)。

我们决定分析录音和文字记录来预测一个人的句子背后的情绪。我们的直觉是,结合两个不同来源的两个模型,使用多模态学习,可以提高我们的性能。

数据预处理

我们工作的第一步是清理文本和音频数据。

虽然已经从视频中提取了文本,但编辑主要是对文本文件进行基本格式化(删除标点、数字和大写字母)。然而,在自然语言处理(NLP)中,很难选择去除文本的哪些部分以及保留哪些部分(单个单词、句子、整个会话)。我试图对单词进行词汇化和词干化,但是没有发现性能上的改进。

音频格式稍微复杂一些,我们尝试了两种方法:

1.首先是使用 Python 的库 librosa 进行音频特征提取。它能够从每个记录中提取 5 个主要特征(平均值):MFCC、色度、梅尔谱图、光谱质心& Tonnetz。从中,我获得了大约 190 个特征,这些特征可以作为表格数据用于建模。

2.第二个是音频到 Mel 声谱图,它允许我们将音频解释为图像,并从视觉角度对其进行建模。请看下图,x 轴代表时间(s),y 轴代表频率(Hz),颜色强度代表信号幅度(dB)。在这种情况下,图像允许在深度学习设置(卷积网络)中进行特征提取。

作者图片

堆叠机器学习模型

ML 文本模型

我首先尝试看看我们可以用一个简单的单词袋 NLP 模型实现什么结果。

单词袋表示包括对文本中每个单词的出现次数进行计数。每个单词的计数变成一列。我决定使用 scikit-learn 的 CountVectorizer,并执行网格搜索来寻找最佳的超参数。

这些超参数包括:

  • 忽略数据集中出现频率高于指定阈值(**max_df**)的单词
  • 指定矢量化时要保留的顶部特征的数量(**max_features**)
  • 指定要考虑的序列长度(**ngram_range**)。

令人惊讶的是,给出最好结果的**ngram_range**是在训练中只保持一个单词长度的那个(**(1,1)**)。因此,我们的模型可能无法检测出“不开心”是一种负面情绪。我们的解释是,大多数时候,我们的模型会关注关键词(“好”、“灾难”)来检测正确的情绪。

矢量化之后,我使用了一个具有岭正则化的回归模型:其想法是通过在基于回归系数(betas)的损失函数中添加一个惩罚项来避免我们的模型过度拟合。我们决定使用 L2 罚函数,因为我们假设所有系数对预测都有相似的影响。

为了评估模型的性能,我使用了平均绝对误差(MAE ),一种预测值和“真实”观察值之间的误差度量。在这里,这个基本的 NLP 模型给了我们 0.87 MAE,这意味着情绪的预测值和真实值之间的差异平均为 0.87,其中情绪等级为[-3,3]。

作为比较,我使用来自均匀分布的随机样本创建了一个基线模型,它给出了 1.77 MAE。

ML 音频模型

如上所述,音频的输入变量(X)是从音频文件中提取的声学特征。

为了预测情绪,我们使用 scikit-learn 构建了一个随机森林(RF)模型。RF 是一种集成方法,它在数据集的子样本上打包一组决策树。这种方法的优点是 RF 使用平均来提高预测精度和控制过拟合。

然后,我们进行了网格搜索,以优化 RF 的超参数,包括:

  • 森林中的树木数量(**n_estimators**)
  • 分割内部节点所需的最小样本数(**min_samples_split**
  • 树的最大深度(**max_depth**)。

在这里,这个安装在音频功能上的 RF 模型给了我们 0.91 MAE。

堆叠最大似然模型以改进我们的预测

一旦这两个模型建立起来,我们打算把它们的预测结合起来,看看是否能改进我们的结果。

首先,我创建了一个定制的特性选择器,使我们的管道能够在堆叠它们之前为每个模型选择正确的特性:

然后,我使用 scikit-learn 的堆叠回归和 MLP 回归来创建我们的堆叠模型的结构。想法是增加神经元层,在管道执行中结合两种模型。迭代之后,我们选择了一个有五个神经元的单层。

该模型作为一个深度学习神经网络工作:我实现了 500 个纪元(**max_iter**),这是一个在每个神经元中校正的线性激活函数(**activation='relu'** ) &,一个限制过度拟合的早期停止工具。

堆叠 ML 模型的过程能够显著改善我们的预测:我们的最终模型达到了 0.78 的 MAE。

堆叠深度学习模型

NLP CNN 模型

为了用神经网络分析文本,我选择了一个带有自定义嵌入的卷积网络模型。

嵌入包括将我们的训练集的每个单词放置在我们创建的多维空间中。我们决定创造自己的词汇,潜在地更加强调每个单词的情感‘价值&因此比例如 Word2Vec 更加精确。为此,我们创建了一个“词汇”类来训练&保存这个词汇以备将来预测。

至于模型,我们实现了一个卷积神经网络(CNN):这些类型的深度学习模型广泛用于图像,也执行某些 NLP 任务,这是情感预测的情况。

下面的代码展示了我们用 Tensorflowkeras库构建的神经网络。在集成嵌入之后,训练数据通过一个卷积层。然后将它压平,得到一个由 32 个神经元组成的致密层。所有神经元都具有校正的线性激活函数(**activation='relu'**)。

这个 CNN 模型在我们的测试数据中给了我们 0.75 MAE,成为我们最好的模型。

音频 CNN 模型

我们的下一个方法是使用音频 Mel 声谱图,这在深度学习中被广泛采用。我们将频率转换为 Mel 标度,结果成为 Mel 频谱图,它将成为 CNN 模型的输入(作为图像)。

因为人类不能在线性标度上感知频率,Mel 标度接近人类对音高的感知。因此,在我们的研究中,所有的频率都被映射到 128 个 Mel 波段。

由于 CNN 的所有输入应该具有相同的输入形状,因此我们为较短的音频填充静音,为较长的音频进行剪辑,以便获得唯一的输入形状(128,850,1),其中 128 表示 128 个 Mel 带,850 表示长度,1 表示 1 通道(灰度图像)。

这是我们使用 Mel 声谱图为情绪预测建立的最终 CNN 模型。

这种对 Mel 光谱图的图像分析给出了 0.89 MAE。

堆叠 DL 模型

两个原始模型后具有两层神经元的 DL 堆叠的视觉表示—图片由作者提供

从 ML 结果中,我们了解到堆叠 NLP 和音频模型可以改进我们的预测。因此,由于使用了keras‘Concatenate’方法,我们在输出层之前堆叠了两个 DL 模型输出和一个密集层。

使用与 ML 桩中相同的方法,模型本质上基于两个模型输出进行回归。不幸的是,它并没有改善我们对测试数据的预测,因为这个叠加模型也给出了 0.75 MAE。

结果和展望

作者图片

总的来说,我们任务的最佳模型是我们的 NLP 深度学习模型。我们发现最终的 0.75 MAE 是可以接受的,反映了项目所花费的时间、数据集的大小及其质量:

首先,它是由人类注释的:由于情绪和情感是高度主观的(文化、意义的不同解释、讽刺等等),模型的质量受到了损害。其次,大多数时候情绪是中性的,这意味着数据可能会被扭曲。这导致我们的模型不成比例地预测情绪为中性(尽管它是积极的或消极的)。在不牺牲数据集大小(数据平衡)的情况下,解决这个问题的方法是收集更多的负面和正面数据,以便提取预测更广泛情感的特征。

此外,我们认为我们的结果可以在几个方面得到改进:

  • 在模型调整上花费更多时间,关注超参数、文本清理步骤或内核大小。
  • 尝试堆叠其他深度学习模型,看看它们是否可以改善我们的预测。Mel spectrogram 似乎没有找到可以与我们的 NLP CNN 上分析的文本互补的模式。我们的结论是,这个问题值得深入挖掘,我们绝对欢迎来自社区的评论和建议!
  • 用不同的方法建立一个情绪分类器,能够预测快乐、愤怒、惊喜等,而不是情绪评级。

源代码可以在 GitHub 上找到。我们期待听到任何反馈或问题。

作为结论,我们相信从音频和文本中进行情感识别有一个非常令人兴奋的未来,因为它允许从人们那里收集很好的见解。如果进一步推进,并与情感分类相结合,这类项目的一些用例可以为社会增加巨大的价值。例如:

  • 改善电话客户服务。根据客户的情绪/观点重新引导客户。满意的客户可以被引导到销售部门,不满意的客户可以被引导到保留部门,困惑的客户可以被引导到技术支持部门,等等。
  • 这也是一种评估服务质量和品牌监控的好方法。我们绝对欢迎来自社区的评论和建议,并期待看到这一领域的改进。

[1]A,梁 PP,茯苓 S,Vij P,Cambria E,Morency L-P (2018),面向人类交流理解的多注意递归网络,第三十二届 AAAI 人工智能大会。**

在数据洪流中坚守你的主张

原文:https://towardsdatascience.com/staking-your-claim-in-the-data-rush-97c3dd5e351f?source=collection_archive---------60-----------------------

亨妮·斯坦德在 Unsplash 上的照片

“数据是新的石油。”

除非你在过去的 15 年里一直生活在岩石下,否则你可能听说过“数据是新的石油”这句话。2006 年,数学家兼企业家克莱夫·亨比(Clive Humby)首次创造了这个短语——从那以后,成千上万的人都在重复这个短语。

克莱夫·亨比可能从未想到这句话会给人留下如此深刻的印象,但它确实是的完美比喻。石油和数据有很多共同点。

看看你的周围,以这样或那样的方式,石油对你周围的一切都起着至关重要的作用。许多日常用品都是石油的产品,它们在一个由石油驱动的工厂里生产,然后由船只运输,这些船只也是由石油驱动的,你猜对了。

今天,数据正在产生同样大的影响。数据的使用方式多种多样,影响着政府、公共政策、医疗保健、科学,当然还有商业决策。

社会依靠数据和石油让事情运转。由于这个原因,它们被认为是有价值的商品——许多政府、决策者和企业都拼命想得到它们。数据和油就是力量。没有他们,让事情发生会很有挑战性。

最重要的是,当我们钻探石油或开采数据时,要成功做到这一点需要一些知识和正确的工具。否则,这可能是对时间和资源的巨大浪费。钻探石油需要了解石油可能在哪里以及如何开采。它需要机械和人力。一旦石油被提取出来,它需要被精炼,这样它才能被充分利用。

数据也是如此。必须知道在哪里收集数据以及如何提取数据。它需要特定的技术和人力。然后,一旦收集了数据,就需要对其进行提炼,以便充分发挥其潜力。

与钻探石油不同,你不会为了收集数据而累死累活。但这确实需要一些努力。收集数据是回答那些有价值的问题的第一步,然后我们才能提炼它,得到它的真正价值。

以下是收集数据的一些最佳实践:

拥有强大的内部系统:

在解决内部业务挑战或发现机会时,在内部收集数据是最佳实践。在内部收集数据意味着信息被一致地记录和存储在内部。通常,数据被收集并存储在客户关系管理器(CRM)中。如果你的团队正在使用你的 CRM,你可能有一套相当不错的数据来获得洞察力。如果他们没有充分发挥 CRM 的潜力,你真的应该考虑一下。有一些数据,即使不完整,也比没有数据好。

如果您不确定要收集什么,尽可能多地收集数据仍然是一个很好的做法。永远不可能确切地知道未来什么是有价值的。因此,以结构化的格式收集数据,可以在未来转化为信息和见解,并在未来创造新的机会。好消息是,如今数据存储很便宜,所以保留这些数据不会对你的预算造成太大影响。

从第三方购买数据:

有时,一家公司无法收集自己的数据,因为他们可能是一家初创公司,一家希望进入新市场的公司,或者在当前的数据集中存在重大差距。与其从头开始,不如从第三方购买数据是个不错的选择。那里有大量的数据集,第三方数据经纪人已经仔细地花时间把它们放在一起。他们有各种各样的数据集,这些数据集通常由难以收集的数据、难以访问的数据或非常大的数据集组成,这些数据集可能需要几十年的时间来收集。

如果你不确定从哪里开始,Quandl 和 T2 Explorium 是著名的数据经纪人,他们有大量的数据。

使用第三方购买培训数据:。

如果你希望参与人工智能和机器学习的热潮,并希望承担一个项目,算法将需要一些训练数据来学习。如果缺少可用的培训数据,第三方也是一个很好的解决方案。第三方可以更快、更高效地提供数据集,而不是试图拼凑一组训练数据

训练数据有很多选项,查看蜂巢、秤、 NTT 数据,或者权重和偏差。

使用第三方收集培训数据:

有些时候,第三方数据经纪人没有准确的数据集来回答这个百万美元的问题。当需要特定数据,但您的团队可能没有人力、工具或时间来收集数据时,有第三方可以为您收集这些数据。龙再一次注意到了第三方数据收集者。最受欢迎的例子之一是亚马逊土耳其机器人。来自世界各地的个人根据提供给他们的指令收集数据的平台。

从互联网上抓取数据:

众所周知——万维网上有大量数据,随手可得。这种实践确实需要一些专业知识来创建特定的工具。但是一旦处理好了,非常具体的数据就可以很容易地从互联网上刮下来,进行相应的解析和使用。请尊重你从哪里收集数据的条款和条件!只有在你允许的情况下才这样做。

例如,我们的团队已经成功地从互联网上收集数据来进行销售线索挖掘。我们的团队没有让销售代表花费数小时寻找合格的潜在客户,而是开发了一个工具,从律师和会计师等服务提供商的网站上收集数据来挖掘潜在客户。信息被解析,然后直接放入 CRM 供销售团队访问。有了正确的专业知识,可以创建一个工具来收集任何数据,并用于回答这个百万美元的问题。

准备,开始,我的。

好了,我们最后的话。企业不收集数据的前两个原因是因为它看起来太吓人或者太耗时。这实际上是看不到价值和潜在的繁荣。数据推动决策并发现新的机会。如果数据收集不是重中之重,机会和见解就会被忽略,而挑战也会被忽略——无论是现在还是未来。这相当于几十万美元从你手中溜走。

收集数据是这个过程中最简单的部分,尤其是在一点点指导下。提炼它是事情开始变得有点棘手的地方。幸运的是,有数据专家可以帮助您发现数据的真正价值。

作者:

莎拉·韦斯特伯里 是 integrityCo 的一名人类学地下转需求生成专家。凭借以人为本的学术和工作背景,Sarah 热衷于与她的网络建立有意义和信任的关系。尽管数据科学、AI & ML 是一个高度技术性的领域,但 Sarah 努力使这些服务简化和可访问,以帮助公司实现他们的目标。

Hamid Omid 是 integrityCo 的创始人,在数据科学、机器学习、产品管理领域拥有多年的经验,并且极具幽默感,Hamid 喜欢帮助公司识别有价值的变革机会,专注于数字产品和大数据。随着行业开始了解人工智能等新技术的未开发资源,高管们正在寻找创新战略,并从他们的数据中创造价值。哈米德分享了他的专业知识,以帮助公司规划成功的人工智能产品,并实现其成功的最大潜力。

用这个有趣的 Python 项目脱颖而出

原文:https://towardsdatascience.com/stand-out-from-the-crowd-with-this-interesting-python-project-6f24c7ad0777?source=collection_archive---------23-----------------------

自动混合歌曲创作

由 StockSnap 在 Pixabay 上拍摄的照片

典型的数据科学家的目标是使用分析和机器学习为现实世界的问题提供解决方案,但不一定总是这样。你也可以是艺术家。

如果你把数据科学生命中的每一口气都花在解决业务问题上,那么到了某一点后,你就会筋疲力尽,有失去对数据的热爱的危险。如果你厌倦了详尽的数据科学项目,是时候做些有趣的事情来填充你头脑中的燃料,让火花继续燃烧。

除了成为一名数据科学家,我还一直希望成为一名音乐艺术家。这就是为什么作为一个有趣的项目,我决定做一些歌曲/音乐。

这是数据科学家成为艺术家的方式。

我这么做的灵感主要来自于洋红色。如果你在数据科学领域工作,处理音频文件,几乎不可能不碰到 Librosa 和 Magenta 库。这是我在这个项目中使用的两个主要的库。

我打算怎么做?

有了洋红色,你可以做很多艺术作品。不要相信我的话,看看 Magenta 博客就可以自己决定了。你可以找到各种聪明人的艺术作品,他们使用了品红色,创造了新的音乐,创造了钢琴曲,音调转换等等。

我将使用三首歌曲来创建一个自动使用 Python 的混合。使用的古典歌曲是从 cal500 数据集[1]下载的。

资料组

我从 Cal500 数据集【1】中选择了 3 首歌曲,这些歌曲被下载并上传到 Google Colab 运行时文件夹。

请随意尝试你喜欢的歌曲。我选择的歌曲如下(选择纯粹基于个人兴趣)

  • 披头士草莓地
  • 约翰·列侬的《想象》
  • 鲍勃·迪伦《今夜我将是你的宝贝》

如果你听过任何一首混合歌曲,你会发现一首好的歌曲会有一段共同的背景音乐,而且通常所有的歌曲都是由同一个人演唱的。

这意味着尽管有多首歌曲,但混合曲将有相同的声音和共同的音乐。

现在,你如何用 python 来做呢?我在这个项目中实现的解决方案是首先识别背景音乐,并将其与语音分开。

这意味着我们将有一些只有原声的文件,然后我们可以把所有的声音片段组合在一起,做成一个混音。你可以看下面的视频来清楚地了解这个计划。

Medley 创建计划[图片由作者创建]

从技术上讲,我们在这里要做的是

  • 移除背景音乐
  • 从裸音剪辑中找到最佳片段
  • 把最好的剪辑分开,然后组合成一首混合泳

你可以用 colab 笔记本或者 Github 直接跳转看代码自己跑。我在下面概述了我实施这个项目的思考过程。

从想法到行动——用 Python 实现

对于这个项目,您可以使用任何支持 python 的平台。我的建议是使用 Google Colab Notebook 或者你可以检查一下 block Deepnote 中的新成员。

开始之前,请确保您已经安装了必要的库。这个项目需要的主要库如下。

  • 分离人声和歌曲
  • librosa——加载和拆分歌曲
  • pydub —加入所有的声音

你可以使用pip install xxxx来安装上面的库

pip install spleeter
pip install librosa
pip install pydub

步骤 1 —分离声音

现在,作为第一步,我们需要从歌曲中分离出声音。这意味着程序应该能够识别背景音乐,并删除它。

为此,最简单有效的方法就是使用 spleeter 库。Spleeter 库是基于 Tensorflow 的。如果感兴趣的话,你也可以尝试使用 librosa 的声音分离。

当我尝试用 spleeter 和 librosa 搜索相同的歌曲时,与 librosa 相比,spleeter 给出了更有效的结果。Librosa 的输出不清晰,也更嘈杂。

要分离歌曲的声音,使用如下的 spleeter 功能。

!spleeter separate song1.mp3 -d 60 -o output/

这里-d 指的是要处理的歌曲的持续时间。我处理了前 60 秒,-o 表示输出路径。

要了解更多关于 spleeter 用法的信息,您可以运行!spleeter separate --help

对另外两首歌重复同样的操作,并将声音路径保存在songs_path

songs_path = ["/content/output/song1/vocals.wav",
"/content/output/song2/vocals.wav",
"/content/output/song3/vocals.wav"]

点击下面的播放按钮可以听到声音输出。

今夜我将是你的宝贝之歌

想象之声

草莓之声之歌

如果你观察到这些音频文件中有很多静音,因为背景音乐被移除了,只有音乐的部分会是静音的。

识别沉默

为了识别声音之间的寂静,我使用了 librosa 的特效。分割,为此我们需要将声音音频文件转换成波形 y ,采样率为 sr.

可以使用librosa.load()来完成。你可以在所有的语音音频文件中看到 for 循环的实现。

for song in songs_path:y, sr = librosa.load(song, duration=60)y_all.append(y)sr_all.append(sr)

现在,要知道文件中的静音并只分离有声音的音频,使用librosa.effects.split()并将 top_db 设置为 40 ,这样它将只考虑 40db 以上的声音。

for x in y_all:tr_all.append(librosa.effects.split(y=x, frame_length=8000,top_db=40))

选择最佳分割

在前面的步骤中,所有 3 首歌曲的声音文件被分割成更小的部分。因为想法是找到歌曲的最佳可能片段,所以我所做的是获得歌曲每个片段的开始时间、结束时间和持续时间,以获得最大持续时间片段的细节。

获得最大持续时间的想法背后的思想是选择最佳片段,这样任何人听到片段都应该能够识别歌曲。

您可以在下面看到安装代码

下一步是计算每首歌曲的选定片段的开始和结束时间的准确时间。它可以按如下方式实现。

for d in duration:song_snip_duration_index.append(d.index(max(d)))song_snip_duration.append(max(d))for i,s in enumerate(start_time):
song_offset.append(s[song_snip_duration_index[i]])

下一步是使用开始和结束时间,生成只有声音的原始歌曲的最终片段。

for n,new_song in enumerate(songs_path):new_y, new_sr = librosa.load( new_song,offset=float(song_offset[n]),duration=song_snip_duration[n])sf.write("new_song" + str(n) + ".wav", new_y, new_sr)

最后,所有的片段可以按如下方式连接在一起

from pydub import AudioSegmentsound1 = AudioSegment.from_wav("/content/new_song0.wav")sound2 = AudioSegment.from_wav("/content/new_song1.wav")sound3 = AudioSegment.from_wav("/content/new_song2.wav")combined_sounds = sound1 + sound2 + sound3combined_sounds.export("all_voices.wav", format="wav")

通过点击下面的播放按钮,可以听到带有裸露声音的混合泳的最终神奇输出

3 首歌曲的混合曲—只有声音

你现在已经做了什么,你还能做什么?

最终结果出来后,我非常兴奋,因为我可以用 python 做一些有趣而非正统的事情。我希望你也有同样的感觉。没有什么比学习新东西更甜蜜的了。拿起一个你自己的想法,自己去做。相信我,你不仅让自己感到自豪,还学到了很多东西。

你可以在 Colab 笔记本上看到完整的代码—

包含整个实现的笔记本

参考

[1]s-y Wang,j-c Wang,y-h Yang,h-m Wang,“基于 CAL500 扩展的时变音乐自动标记方法”,IEEE 国际多媒体会议论文集和博览会,西班牙巴塞罗那,2014。

从云中脱颖而出:如何塑造和格式化单词云

原文:https://towardsdatascience.com/standing-out-from-the-cloud-how-to-shape-and-format-a-word-cloud-bf54beab3389?source=collection_archive---------27-----------------------

在 Python 中使用和设计单词云的技巧

这个博客将涵盖单词云,它们的局限性,然后是一些格式化的例子。

好人

词云是一种简单的方法来总结文本,并使最流行的词引起你的注意。它们引人注目,易于理解,不需要任何额外的解释。

坏了

但是,词云确实有局限性。它们对深入分析没有帮助。它们可能比简单的条形图更吸引人——但是它们提供的信息要少得多。以下是词云的一些问题。

  • 字体大小并不是显示差异的有效方式。我们可以区分大、中、小字体;除此之外,事情就变得棘手了。
  • 我们不知道单词是如何加权的——大字体实际上意味着什么?大字体的单词是出现 10 次还是 1000 次?
  • 可视化中的定位通常非常重要。然而,在词云中,词序是随机的。
  • 较长的单词比较短的单词受到更多的强调,因为它们的长度占据了更多的空间。
  • 我们对单词的上下文一无所知。在某些情况下,相反的意思会被突出显示,例如,在“not”的停用词被删除后,短语“not good”会在词云上表示为“good”。

尽管有这些限制,单词云已经变得非常流行,主要是因为它们很容易创建。词云的流行和缺点意味着它们有许多诽谤者——我最喜欢的词云(或“标签云”)侮辱是它们是“新的鲻鱼”……哎哟。

何时使用文字云

你看这个博客不是为了了解字云有多可怕。有很多其他的博客都是关于这个的。文字云是吸引注意力的绝佳工具。它们作为介绍和打破沉重的文本段落是很方便的。那么,如何让你的字云从云端跳出来呢?

入门

对于这些例子,我将使用来自 OpenMoji 的表情符号和来自 Wikipedia 的关于表情符号的文本来形成云。

微微笑着的脸

一张黄色的脸,睁着简单的眼睛,带着淡淡的微笑。传达广泛的积极、快乐和友好的情感。它的语气也可以是居高临下、被动攻击或讽刺,就好像在说这很好,但实际上并不好

https://emojipedia.org/slightly-smiling-face/

来源埃米莉·耶格尔的 OpenMoji 。Unicode 1F642。牌照: CC BY-SA 4.0

以下是可以使用的库

from PIL import Image
from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np

默认字云

# Default WordCloud
wordcloud = WordCloud().generate(emoji_text)  
plt.figure(figsize=(10,6))
plt.imshow(wordcloud)
plt.axis(“off”)
plt.show()

默认单词云—按作者分类的图像

格式化

wordcloud = WordCloud(max_font_size=80, max_words=1000, background_color=”black”, colormap=’Paired’).generate(emoji_text)
# Use the lasst four lines from above (plt) to plot the Word Cloud
plt...

这里我们改变了一些默认参数——使用 background_color、max_font_size 和 colormap——任何 Matplotlib 调色板都可以工作并设置字体。

在这个例子中,我还使用 font_path 来选择自定义字体。您将此设置为所选字体的文件位置。例如:

font_path = “/Users/You/Fonts/Comfortaa-Bold.ttf”

WordCloud 还有一个内置的停用词列表,您可以忽略、调整或使用,如下所示:

stopwords = set(STOPWORDS)

格式化单词云—按作者分类的图片

形状

您可以使用遮罩为您的单词云创建一个形状。首先,上传一个 png 图像,并将其转换为数组。Wordcloud 会在图片非白色的位置画出文字。在数组中,255 是白色的,1 是黑色的。

mask2 = np.array(Image.open("paint1a.png"))
emo2 = WordCloud(font_path = font_path, background_color="white", max_words=100, mask=mask2, stopwords=stopwords,min_font_size=10, colormap='Dark2')# Generate a wordcloud
emo2.generate(emoji_text)# plt ...

单词云形状—作者图片

不良透明遮罩错误

如果你的图像有一个透明的背景,它将抛出一个错误。

ValueError: bad transparency mask

遮罩需要白色值为 255,而不是 0。您可以通过查看阵列来检查您的图像。

emoji_mask = np.array(Image.open(“1F642_color.png”))
emoji_maskarray([[0, 0, 0, …, 0, 0, 0],
…,
[0, 0, 0, …, 0, 0, 0]], dtype=uint8)# replace 0 with 255
emoji_mask[emoji_mask == 0] = 255
emoji_maskarray([[255, 255, 255, …, 255, 255, 255],
…,
[255, 255, 255, …, 255, 255, 255]], dtype=uint8)

颜色

您还可以使用 ImageColorGenerator 函数来设置色彩映射表以匹配图像。(深色斑块是我们表情符号的眼睛和嘴巴。您可能需要减小 max_font_size 以改善图像细节的外观)

# note — need to set max font size, or font extends over the colors
mask = np.array(Image.open(“paint1.png”))
emo_clr = WordCloud(background_color=”white”, mode=”RGBA”, max_words=3000, max_font_size=18, mask=mask).generate(emoji_text)# create coloring from image
image_colors = ImageColorGenerator(mask)plt.figure(figsize=[7,7])
plt.imshow(emo_clr.recolor(color_func=image_colors), interpolation=”bilinear”)
plt.axis(“off”)
plt.show()

文字云颜色示例—作者图片

表情符号确实展示了颜色效果是如何工作的,但这并不是一个很好的例子。黄色勉强显示在白色背景上,无法看清上面的字。为了显示眼睛和嘴巴的清晰度,这个词云使用了海量的最大字数和小的最大字体。我建议您尝试使用颜色,在保持每种颜色的边框的同时,尽可能将字体变大。有些图像比其他图像效果更好,直线颜色比曲线颜色效果更好。

灰度图像 TODO 错误

使用颜色时,有些图像会产生错误。

NotImplementedError: Gray-scale images TODO

color 函数寻找一个三通道数组,代表红色、绿色和蓝色(RGB)的值。有些图像只有一个通道。你可以通过在数组中增加两个额外的通道来解决这个问题——参见马修·亚瑟的博客中关于如何做的一个很好的例子。

或者,我发现通过在画板或画笔中打开图像并重新保存图像,它会转换成预期的格式。

通过检查形状来检查图像,如下所示:

test = np.array(Image.open(“1F642_color.png”))
test.shape(618, 618) # -> error example(618, 618, 3) # -> RGB example

这里有一个类似的例子,我调整了 NumPy 数组,使表情符号的眼睛和嘴巴保持空白。

mask2 = np.array(Image.open(“paint1.png”))
mask3 = np.where(mask2==0, 255, mask2)emo = WordCloud(background_color=”white”, max_words=500, mask=mask3, stopwords=stopwords, colormap=’Dark2')emo.generate(emoji_text)

字云反形—图片作者

反转

最后,你也可以用反转图像来做实验。这里,我们将单词 cloud 绘制在阴影周围,而不是形状内部。我在 MS Paint 中调整了这张图片,反转了颜色,这样我就有了一个围绕着白色圆圈的黑色轮廓。最后,我用 Python 打开它,把它作为一个词云加载——代码是一样的。

一旦你有了文字云轮廓,你可以把原始图像放在上面,就像这样。

左侧为反向文字云|右侧为反向文字云,图片由作者提供

摘要

文字云是吸引人们关注你作品的绝佳方式。它们可以成为更深入分析的良好开端。有许多不同的方法可以调整你的词云,使之适合你的项目,而不是局限于默认设置。我希望这篇博客的例子能给你一个创建你自己的单词云的起点。

快乐云!

附加资源

表情符号

https://open moji . org/library/# group = smileys-emotion % 2f face-smiling&e moji = 1f 642

Word 云文档

https://amueller.github.io/word_cloud/index.html

数据营 Word 云教程

https://www . data camp . com/community/tutorials/word cloud-python

将图像转换为 RGB

https://towards data science . com/transform-gray-images-to-RGB-using-python-matplotlib-6a 0625d 992 DD

斯坦福大学的 AIMI 通过向研究人员提供免费的大数据,正在彻底改变医疗保健人工智能

原文:https://towardsdatascience.com/stanfords-aimi-is-revolutionizing-healthcare-ai-by-providing-free-big-data-to-researchers-836b6284419f?source=collection_archive---------27-----------------------

斯坦福大学的 AIMI 图书馆是人工智能模型训练中使用的数据的一个令人难以置信的强大资源

来源:埃尔努尔 _/【depositphotos.com

人工智能系统的好坏取决于它的数据

人工智能正在医疗保健领域开辟新天地,创造一个一切都将自动化和一体化的可能性世界。事实上,人工智能、物联网和机器人正在联合起来,实现我们世界中几乎所有东西的自动化。查看我们关于物联网和机器人技术的结合如何改善世界医疗保健系统的许多方面的文章,了解关于这一现象的更多信息。

正如旅顺江和他的研究团队所指出的,影响人工智能在几乎所有行业的实施的最大挑战是获得良好的培训数据。

来源:video doctor/Depositphotos.com

任何给定的人工智能系统都只和它的训练数据一样好,这就是为什么人工智能和大数据是不可分割的一对。斯坦福大学医学和成像人工智能中心(AIMI)正在努力解决人工智能在医疗保健中的这一障碍。

人工智能的早期受到人工智能偏见带来的几个陷阱的困扰。在其他问题中,算法偏见造成了不公平的结果,使一组任意的用户优先于其他用户。这种不足导致许多人工智能算法被贴上性别歧视和/或种族歧视的标签。今天,随着像斯坦福大学的 AIMI 这样的程序为机器学习提供了几个数据集,这个古老的挑战得到了适当的关注。

关于斯坦福的 AIMI

斯坦福大学和微软创建了 AIMI 作为带注释数据集的一站式目的地,这些数据集可用作医疗保健模型的训练数据。它已经获得了超过 100 万张图像,并在不断增加,使其成为世界上最大的带注释的去识别医学图像数据库。这些图像免费提供给研究人员。

通过与微软的 AI for Health 项目合作,新的平台将具有高度的注释性、可访问性和可见性。它托管和组织来自全球医疗保健机构的大量额外图像,以创建一个开放的全球存储库。该平台将成为共享研究、完善模型和识别人群共同差异的中心。

来源:Depositphotos.com一切皆有可能/https://depositphotos.com/

这对人工智能和医疗保健意味着什么?

随着人工智能作为我们社会众多领域的解决方案不断扩大其作用,对更多高质量训练数据的需求也在增长。学习算法和模型只受到它们被馈送的数据的限制;这就是斯坦福大学的 AIMI 发挥作用的地方。

大人工智能、物联网、AIoT、机器人和支持 5G 的智能系统需要尽可能多的信息,以便创建实时解决方案。AIMI 图书馆预计将在 2022 年达到 200 万张图片。它是最强大的精选、患者身份识别和人工智能就绪数据资源,其对人工智能在医疗保健中的影响是不可想象的。

AIMI 的数字财富金矿将是开源的,这是人工智能世界一直在等待的,以便开辟更多新的领域。有了开源数据库,来自世界任何地方的研究人员都可以为他们的下一个人工智能项目访问数据。

所有的人工智能医学研究都将受到积极影响,而不仅仅是医学成像。它将允许人们探索人工智能的重要临床用途,而不仅仅是像素数据,包括伴随的多模态数据。

来源:光源/Depositphotos.com

由于大多数数据将免费提供给研究人员,他们将有机会探索几个利基领域,如大公司可能忽视的社区特有的医疗问题。

斯坦福大学的人工智能医疗数据集是去识别的,这一事实使它们变得更好,更有用。该平台还旨在创建一系列标准化的机器学习工具和预训练模型,这些工具和模型将利用开源数据及其通用架构。

关键的三点

1)更多的开源数据意味着更好的训练模型,这意味着更好的基于人工智能的模型。

2)斯坦福大学的 AIMI 通过开放世界上最大的训练数据库,解决了人工智能的长期挑战。它还在增长——到 2022 年底,它将拥有超过 200 万张图片。

3)所有这些有价值的数据都将免费提供,这意味着其影响潜力是无限的。

AIM 博客 阅读更多健康、科学和领导力相关文章,别忘了 订阅我们的简讯 !!

星球大战数据科学

原文:https://towardsdatascience.com/star-wars-data-science-d32acde3432d?source=collection_archive---------13-----------------------

网络分析、主题建模和一个词云

星球大战🌌是有史以来最史诗的奇幻太空冒险(强烈偏颇)。当你可以拯救整个星系的时候,为什么只拯救一个世界!每年,数百万影迷会在 5 月 4 日庆祝星球大战日。去年,我找了点乐子,创建了一个博客帖子,可以使用神经网络(从头开始构建)破译来自穆斯塔法的秘密信息。

图 1:本文中创建的词云和网络图。

为了开创一个传统,今年,我再次将《星球大战》与数据科学结合起来。一个臭名昭著的《星球大战》信息来源收集于wookiepedia,一个拥有数千页的粉丝网站。使用主题建模和网络分析等数据科学工具进行调查的惊人来源。

由于这是一篇相当长的文章,我把它分成了几个主题。这样你就可以很容易地浏览到你最感兴趣的话题。

主题概述:

  1. 抓取和构建数据集
  2. 伍基人媒体数据探索
  3. 我们需要一个词云!
  4. 主题建模
  5. Wookieepedia 网络分析

包含所有笔记本和数据集的 github 存储库可以在这里找到。

👉阿纳金天行者的互动网络图!

其他有用的链接是我的每天 10 分钟的 Python 课程和一个使用 Python 环境的教程。

1.抓取和构建数据集

《星球大战》在线上有大量的信息。我最喜欢的来源之一是所谓的wookiepedia,这是一个由粉丝们建立的拥有大量星球大战知识的维基。

所有的数据都可以在 Github 和 Kaggle 中找到,所以没有必要自己去收集,为 Wookieepedia 创造大量的流量。

本节描述了收集所有使用的数据的过程。wiki 是页面的集合,每个主题都有自己的页面。为了收集这些信息,我们需要访问每个页面并下载其内容。有一个聪明的方法来抓取这样的网站,那就是使用网站地图。这是一个特殊的文件,网站管理员可以提供这将有助于网络爬虫索引网站。我们可以利用站点地图来获取所有可用页面的列表。

目前(2021 年 4 月)有 219,900 页可以被抓取。然而,这有点过了。因此,我决定只刮那些被认为是经典的页面。幸运的是,wookiepedia给了佳能文章自己的类别。当我们点击类别时,我们会得到所有被认为是经典的页面的分页索引。这需要一些额外的工作来收集所有的话题。

好了,我们现在有一个 29k 页的清单,被认为是佳能仍然需要刮。这些页面具有典型的格式,由标题、通常包含子部分的描述和包含属性的侧边栏组成。为了减少信息量,我将只抓取第一段,完整的侧边栏,以及所有指向其他佳能页面的链接。典型的 Wookieepedia 页面如图 2 所示:

图 2:从每个页面中抓取描述、侧边栏信息和所有链接(页面)。

接下来,我们抓取每个页面并将分区保存到磁盘:

收集所有信息花了一个多小时,所有信息都存储在 pickle 文件中,这些文件被分割成最多 5000 页的部分。这是原始数据集,我们可以随时使用它。

接下来,我们将把原始数据分成两部分:字符和原始文本句子。这些角色由边栏中的一个名为“物种”的属性来标识。我们将在一个强结构的熊猫数据框架中收集所有的角色,这意味着我们需要事先选择属性。我们总共有 5334 个字符被标记为佳能。关于所选属性的更多细节可以在 Github 库的 scrape 笔记本中找到。

原始文本句子是从描述中提取的。每个描述都被拆分成句子,并收集在一个列表中。这种分裂的细节也可以在 Github 上的 scrape notebook 中找到。

2.伍基人媒体数据探索

使用新数据集的第一步总是数据探索。这是熟悉数据集的一种方式,也是了解其中包含哪些信息的第一步。分析的细节可以在 Github 上的数据探索笔记本中找到。

种类

如果你问一个不喜欢《星球大战》的人,你经常会听到的回答是,这部电影充满了怪异的生物。尤其是著名的“T2”酒吧场景,充满了银河系中存在的不同物种。因此,先看看物种的数量以及它们在佳能数据集中出现的频率是很有趣的。

总共有 530 个物种被提及,太多了以至于不能很好的可视化,因此,我们只选择在数据集中至少有 40 个被提及的物种。所有其他被归类为其他。

图 3:人类是目前占优势的物种。

到目前为止,人类是占主导地位的物种,拥有超过一半的字符或 2770 次提及。下一个物种是提列克人,只占数据集的 2.5%。其他字符只有十分之几,因为数量正在迅速下降。我们将 519 个其他物种归为“其他”类,每个物种的平均特征不到 4 个。如果我们随机遇到一个星球大战人物,他/她有 50%的几率是人类。如果它不是人类,它可能是许多不同物种中的一个。

家园世界

由于物种非常多样,看看每个角色的家园也可能会很有趣。查看唯一计数,数据集中提到了 463 个世界。同样,由于高度的多样性,我们将这些限制在至少 8 个必须提到地球是家园的宪章中。

图 4:卡米诺是被提及最多的 planed。

有趣的是,卡米诺是被提及最多的以克隆技术闻名的世界。它在第二集《克隆人的进攻》中首次被提及,主角们在那里发现了一支隐藏的克隆人军队。但这个星球如此突出的主要原因是因为名为《克隆人战争》的星球大战动画系列。它有七季,总共 133 集,以卡米诺星球上创造的许多角色为中心。第二颗行星是纳布,由阿米达拉女王统治,在整个系列中非常有名。也许令人惊讶的是曼达洛的高提及率,但这可能是因为曼达洛,这是流媒体服务迪士尼+上的一个伟大的衍生系列。

性别

虽然有许多物种,但它们中的大多数似乎都被划分为我们在地球上所知道的相同性别。让我们快速浏览一下分布情况:

图 5:自 2016 年以来,《星球大战》有了非二进制或性别酷儿角色(插图:wookiepedia)。

因为《星球大战》开始于 70 年代末,我预计会有很多男性角色。事实上,三分之二的角色是男性,但不知何故我期望更多。也许迪斯尼开始增加女性的数量来最终平衡原力。

另一件很酷的事情是,在官方的星球大战传说中有一些非二进制字符。作者查克·温迪格证实,他书中的一个名为 Eleodie Maracavanya 的人形角色确实不是二进制的,因此是第一个正式的非二进制佳能角色。现在一共四个,包括 Keo Venzee。

对于一些物种来说,不太清楚它们是雄性还是雌性,大约 8%的角色没有性别。

最高最小的一束

正如魁刚·金曾经评论的那样:“总有更大的鱼”,因此,观察数据集中记录的鱼的大小是很有趣的。不幸的是,只有大约 12%的人物知道身高。让我们看看分布情况:

图 6:《星球大战》中的人物身高达到 1.83 米,接近荷兰的平均水平。(插图:伍基人百科)

大多数角色的身高都是人形种族的典型身高,有一个 1.83 米的大高峰,大约是荷兰男性的平均身高。看看这两个极端,我们发现巴布·弗里克(出现在第九集)是迄今为止最小的智慧生物,只有 22 厘米。同样令人高兴的是,格罗古还得再长 20 厘米才能和尤达一样大。

最大的生物是 Omi,一只生活在死星垃圾压缩机里的独眼野兽,显然有 10 米高。赫特人贾巴也很大,有 3.90 米,但我猜他们是从鼻子到尾巴的末端测量他的。

眼睛颜色

我们还记录了他们眼睛的颜色,数据集中有 97 种颜色。我猜有一些错别字,也有一些描述性的颜色,如“蓝绿色”。仍然有将近一半的眼睛颜色被记录。

图 7:第三种最常见的眼睛颜色是黑色。

棕色眼睛在《星球大战》宇宙中是最常见的,这与现实世界中世界上绝大多数人也有棕色眼睛非常相似。更令人不安的是,黑色显然是第三种最常见的眼睛颜色。

肤色、发色等其他属性也有。请随意在笔记本上查看它们。

3.我们需要一个词云!

在对一个 fandom 话题做数据分析的时候,我们要做一个 wordcloud。为此,我们将使用来自 Andreas Mueller 的描述数据集和 Wordcloud Python 包。更酷的是,文档中甚至有一个使用冲锋队的例子!

Wordcloud 包让它变得再简单不过了。所有的频率计算都是在引擎盖下完成的,并且生成了一幅美丽的图像:

图 8:佳能文章所有描述的文字云(使用所有数据)。

单词云是使用所有佳能文章的文本生成的,所以不仅仅是字符。与所有的词云一样,首先所有的停用词都被移除,经过频率分析后,我们看到最常见的词在《星球大战》中肯定有意义。

4.主题建模

到目前为止,我们已经介绍了一些数据探索的基础知识,并且创建了强制性的 wordcloud。因此,我们现在准备进入更高级的分析,这也是我想尝试的第一个主题建模。这里描述的方法是基于 BerTopic 的创作者马尔滕·格罗滕赫斯特的作品。

主题建模是一种无监督的学习技术,可以回答以下问题:我有一堆文本,这些文本谈论的最常见的主题是什么。

对于主题建模,我们将使用一个名为 Sentence-Transformers 的 Python 包,顾名思义,它基于 Transformers 架构,能够将完整的句子转换为向量。为了找到相似的主题,我们需要找到分组在一起的向量。让我们首先创建句子嵌入:

句子向量的长度为 768,这对于聚类分析来说是非常大的,因此,我们将应用降维。有许多选择,如 LDA 或 NMF,但这里我们将使用一种叫做 Umap 的方法,它的好处是保持局部结构完整。

降维后,我们可以尝试使用 Hdbscan 来识别集群。这将找到在缩减的参数空间中被分组在一起的聚类。

我们已经将 768 个特征减少到只有 6 个特征,但是如果我们想将它绘制到屏幕上,这种减少是不够的。为此,我们需要将它减少到至少 3 维,为了更好的可视性,我们甚至会减少到 2 维。为此,我们再次使用 Umap,将原始数据集从 768 一直减少到 2,并将之前定义的标签与坐标相结合。

现在我们可以画出结果:

图 9:所有主题的彩色概览。

结果是非常漂亮的,但如果这些集群有任何意义,我们需要将集群与它们的原始句子结合起来。此外,我们希望找到每个聚类最常见的关键字,因此,我们需要分别分析每个聚类。为了解决这个问题,Maarten Grootendorst 想出了一个基于类的 TF_IDF ,它工作起来非常简洁!

我们需要为每个集群应用 c_tf_idf,并将文档集合视为单个文档。结果是最常见术语的列表,希望它们在我们的 Wookieepedia 数据集上有意义。

当查看结果时,实际上有一些是有意义的,但不是很酷。有一个完整的集群有许多颜色,这仍然很酷,知道它是从一个无人监管的方式获得的。然而,有几个集群确实有基于故事的主题。例如,包含帕尔帕庭皇帝的集群包含许多政治术语,如参议院、最高议长和宪法。帕拉丁也与克隆人有联系,克隆人是他创造的消灭绝地的总计划。

然而,我最喜欢的集群结合了几乎所有的明星人物以及任务和雅文战役这两个术语。当然,雅文战役是《星球大战》中最重要的事件之一,经常被提及是有道理的。尽管如此,使用主题建模发现这一点还是很棒的。

图 10:我最喜欢的集群,其中包含了在集群中出现频率最高的术语。

5.Wookieepedia 网络分析

我们总共收集了 5334 个角色,分析每个角色之间的关系不是很好吗?作为使用图形网络的第一次尝试,我想将角色周围的网络可视化。为此,我们需要节点,也就是字符,以及它们之间的关系,在图中称为边。例如,阿纳金·天行者与卢克·天行者有一个“父亲”的关系。提取各种关系将意味着广泛的自然语言处理,以将语料库缩减为节点-边缘-节点的形式,这远非微不足道。

为了简单一点,我们将所有关系归结为一种我们称之为“连接到”的关系。为了找出一个角色是否与另一个角色有关联,我们将查看页面上是否有链接。我们预计在阿纳金·天行者的页面上会有一个到卢克·天行者页面的链接。所有这些链接在抓取过程中被收集成一个列表,我们称之为交叉链接。

在 Github 库中找到完整的笔记本。让我们从准备原始数据开始:

现在我们可以建立一个图形网络,我们将使用优秀的网络 x 包。网络是将数据组织成节点和边的几何结构。在我们的例子中,节点是字符,节点之间的关系是边。通常你也给一个边赋予一个权重。这可以看做是纽带有多强,也可以看做是距离。这些权重支持多种分析方法,例如最短路径。因为我们不能给我们的关系添加一个度量(至少从这个数据中我不知道),所有的权重将被设置为 1。

在构建网络时,我们将扫描每个字符的交联。这些可以指向其他字符,也可以指向任何其他页面。因此,我们需要检查交叉链接是否是字符,并忽略非字符:

创建完成后,我们得到了 4794 个节点。显然,大约 500 个角色与另一个角色没有任何关系。从有关系(边)的字符来看,平均总共有将近 20k 或 4 个关系。

让我们首先看一下度分布,它显示了到其他节点的连接数量。度为 1 的节点只有一个连接,而度为 10 的节点与其他节点有 10 个连接。代码在笔记本里。

图 Wookieepedia 数据集的度分布。

大多数节点只有几个连接,对于包含 5334 个字符的数据集来说,这也不足为奇。很多角色只有单一的外貌。像阿纳金·天行者这样的角色与 100 多个其他角色有联系,这可能也是《星球大战》中其他明星的情况。

为了缩小网络的规模并删除不太重要的字符,我们将过滤掉所有度为 2 或更小的节点。我们必须迭代地这样做,因为当我们删除节点时,也会删除边,这可能会将节点设置为较低的程度。

这使得字符数下降到略低于 3000。边的数量几乎没有减少,我们仍然有 17k。这当然是有意义的,因为我们只移除了低度节点。

图表的强大之处在于,您可以使用一套工具来调查节点的重要性。其中一个工具叫做中间中心性。它是一种寻找充当其他节点之间的桥梁的节点的方法,即作为一种信息中继站。在我们的例子中,我们使用介数中心性来找到哪些节点是与各种其他人的链接,并有希望找到一些关键人物。结果如图 12 所示。

图 12:根据中间中心性,图中最重要的节点。

这里没有惊喜。我不得不寻找埃兹拉·布里杰,但他在《克隆人战争》系列中非常突出。还有许多其他算法,如接近中心或网页排名,但这些导致了类似的结果。

为了更好地玩图形网络,我们将使用 PyVis 来可视化它。PyVis 是围绕 VisJS 构建的,它使得绘制这些图形和处理结果变得非常容易。然而,有一件事我们需要注意:我们有太多的节点和边。虽然 PyVis 似乎对大数量没有问题,但由于所有边缘的重叠,它并不那么有趣。因此,我们需要制定一个算法,从我们的网络中选择一个子群。

下一段代码需要三个参数:起始节点、最大水平渗透和最大交叉链接。顾名思义,该算法将从起始节点开始,选择最大数量的相邻节点。如果有比最大集合交叉链接更多的可用节点,它将选择具有最大度的节点。接下来,算法将跳转到选定的节点(这是一个级别增加),并重复该过程,直到达到最大渗透率。所选节点的数量将随着最大渗透率的增加而呈指数级增加,所以要小心这个参数。

结果是一个交互式网站,直接显示在 Jupyter 笔记本或任何其他网络浏览器中。

👉阿纳金·天行者的互动网络图!

图 13:交互图网络截图。

可以用鼠标拖动每个节点,并随之拖动其连接。当鼠标悬停在一个节点上时,Wookieepedia 页面的第一段就会显示出来,这样就很容易看出这个角色是谁。节点的颜色表示级别。边缘的颜色是其来源的颜色。也可以随意调查其他角色!

围捕

我在这个数据集上玩得很开心。仍然有很多可能性,因为我几乎没有触及表面。但是即使用一些简单的方法,我们还是发现了一些很酷的东西。

‘就是这条路’—丁·贾林

如果您有任何意见,请告诉我!在 LinkedIn 上随意联系。

开始更好地管理模型的生命周期

原文:https://towardsdatascience.com/start-managing-your-models-lifecycles-better-307729fe6fe5?source=collection_archive---------14-----------------------

与 MLflow 和 Delta Lake 一起对 Databricks 进行的 MLOps 审查

美丽的人们,你们好!在过去的几个月里,我和我的同事一起为一个出版物进行数据科学项目,在这个项目中,我们必须不断更新我们的训练数据集、功能和机器学习模型。但是,在写这篇文章的时候,没有关于模型生命周期的内部政策,这导致了我们的集体失败。不,我太激动了,但是时间和任务管理得不够理想,这自然让我感到有点沮丧。

这个行业的一大优点是总有一些工具可以帮助我们。所以我不得不介入,希望为未来的项目解决这些问题。今天,我向大家介绍我使用 MLflow 和 Delta lake 处理 MLOps 的经验。为什么是这些框架?这是因为它们是我日常使用的数据块的一部分。

我们走吧!

穆罕默德·阿里扎德在 Unsplash 上的照片

本次审查概述

我没有计划它,它只是押韵。在本条目中,我们将了解:

  1. 数据存储和版本控制用 增量表
  2. 使用 MLflow Tracker 在每次训练迭代后执行和注册实验并保存模型的结构化标准方式。
  3. 使用 MLflow 模型注册中心 注册模型并管理生命周期阶段。
  4. 使用 MLflow 模型服务 将模型部署到生产中。

请记住,本文不会涵盖这些工具必须提供的所有特性。我只是给出一个介绍,这样你可以在这里涉及的每个工具的文档中探索更多。我会在每个部分的末尾和上面列表中的每个项目中留下一个链接。

0.设置数据块环境

这一步是可选的。我们将创建一个 Databricks community edition 工作区,因为默认情况下,它包含了将要涉及的所有工具。

首先,前往这个链接。您需要填写必要的信息来创建 Databricks 帐户并使用 community edition 工作区。之后,您将登录到 Databricks,这将引导您进入以下屏幕:

数据砖之家。捕捉来自我自己的作者。

接下来,您将创建您计算实例。为此,前往左侧栏,选择计算T4。点击创建集群,然后选择规格,如下图所示。

集群规格。被我自己的作者捕获。

点击创建集群 a 增益,并等待集群启动。就这样,我们都为有趣的部分做好了准备。

顺便提一下,您也可以在自己的环境中安装它们,但是需要更多的工作。阅读关于安装的信息

  • MLflow
  • 三角洲湖泊

1.使用增量表进行数据存储和版本控制。停止使用 CSV 文件。

我不能笼统地说,因为我还是一名大三学生,但据我所见,人们对使用 CSV 文件非常满意,包括我自己。它们便于携带,易于使用,并且易于创建。然而,作为在云中工作的数据科学家,由于受到空间和计算的影响,这不是您想要使用的文件格式。

由于 CSV 是面向行的,所以使用起来计算量很大,这意味着即使您不想使用所有的列,也需要加载整个文件。它们不像拼花地板那样是高度压缩的格式,因此存储起来很贵。它们很容易被腐蚀,还有许多其他原因。

也就是说,最好实现一个数据湖,在我们的例子中是 Delta。增量表建立在拼花文件格式之上。它们经过全面指定、高度压缩,并针对大量数据进行了优化。此外,我们有现成的表版本控制、事务日志和时间旅行,从而使工作空间更有条理。

增量表历史。被我自己的作者捕获

你可以在下面的文档中了解更多关于三角洲湖的信息。

2.使用 MLFlow Tracker 执行并记录实验。

MLFlow 是 ML 实验的一个很好的工具。它允许我们注册超参数、性能度量、特定 ML 模型的输入模式,以及 ROC 曲线等工件,只需最少的代码更改。这为我们设计和实现自己的 ML 实验日志策略节省了大量时间和精力。此外,模型被保存为 pickle 文件并可用于加载。

首先你要去侧边栏,选择 W 工作空间,右击,选择 C 创建,,然后选择 MLflow 实验

实验创作。被我自己的作者捕获。

每个实验都将有一个 id,您可以在执行培训和测试之前将它传递给笔记本中的 MLflow 上下文。如果您使用的是 scikit-learn 模型,代码将与此非常相似:

您创建的所有实验都显示在以下界面中:

ML 实验。再次被我的作者捕获。

在每个实验中,你会发现所有被执行的运行,并且每个运行会显示所有注册的度量、参数、工件和模型。如果您访问某个跑步记录,它会显示如下内容:

实验中的一次特殊运行。我做的捕捉。

嗯,不完全是这样,参数会显示在工件上方,但捕获不适合我的屏幕。

此外,MLflow tracker 还有许多其他好处,不仅仅是记录信息,您可以比较实验的多次运行,在相同条件下重现特定的运行,等等。阅读这篇文章了解更多信息。

3.使用 MLflow 模型注册表注册模型和管理阶段。

现在我们可以更好地管理我们的数据和实验,但是我们的模型的各个开发阶段呢?MLflow 模型注册是一个很好的工具。根据文档,它是一个集中式的模型存储库、一个 UI 和一组 API,使您能够管理 MLflow 模型的整个生命周期。它提供了按时间顺序排列的沿袭、版本控制和阶段转换:无、转移、生产或存档。

注册模型有两个选项。首先是使用用户界面。选择一个实验。然后,在所需的运行下,在工件部分,点击按钮注册模型。

注册模型。被我自己的作者捕获。

第二种选择是使用 API。首先,您需要声明运行 id 和保存模型的路径。然后,您键入以下内容:

在您注册您的模型后,它们将出现在 UI 中,在左侧边栏的模型部分。

模型阶段。我做的捕捉。

正如我前面所说的,您还可以从 UI 转换您的模型阶段:

模型细节和阶段转换。被我的作者捕获。

和 API:

使用 API 应该允许您自动执行更复杂的验证,比如确保您的模型在测试集上满足特定的性能标准。你可以用 model registry 做更多的事情,查看文档。

4.使用 MLflow 模型服务将模型部署到生产中。

因此,有了模型注册,我们现在确切地知道我们的模型处于什么阶段,它们何时被训练,何时使用超参数,何时报告指标,而不仅仅是在一个目录中有一堆 pickle 文件。这太棒了!但是,当一个模型最终到达生产阶段时,我们希望有一个工具来进行推理服务。然后再次,MLflow 来救援。我们可以基于特定模型的版本和阶段,使用 MLflow 模型作为 REST APIs 来托管我们注册的模型。根据文档,Databricks 自动为模型创建一个唯一的集群,并在该集群上部署模型的所有非归档版本。

现在,要准备我们的模型进行服务,我们只需在 UI 上的 model registry 中,单击所需的模型,然后选择 Serving 选项卡,最后单击 Enable Serving。

模特服务。我做的捕捉。

现在我们可以使用 python 发出请求,进行推理:

您可以在文档中了解更多关于此功能的信息。

这就是本文的全部内容。回顾所有这些便利的特性对我来说非常有用,我希望对你也是如此。注意这只是一个介绍。要掌握所有这些工具,您需要查看本文每一节末尾提供的文档。感谢您的阅读,感谢您的关注和支持。

再见!

https://hihello.me/p/951c78ee-719f-4c8b-98b5-d47af565d6b0

从使用 DGL 的图形卷积神经网络开始

原文:https://towardsdatascience.com/start-with-graph-convolutional-neural-networks-using-dgl-cf9becc570e1?source=collection_archive---------15-----------------------

轻松的介绍

我查阅了一些现有的库来做图卷积神经网络(GCNN 的),虽然总的来说它们很好,但我总是回到 DGL,因为它有很好的文档和许多例子,以及其他东西[1]。在这里,我想分享我对 GCNN 氏症研究中一个经典例子的回顾,当然是使用 DGL 的 CORA 数据集。CORA 数据集是一个引用网络,其中节点是文章,边是它们之间的引用。下面的 gif 有助于直观地了解这些联系。

用 Pyvis 可视化 CORA 图。作者 GIF。

有 7 类 2708 个节点,每个节点与 1433 个特征相关联[2]。在这里,我们将使用该数据集来执行半监督分类任务,以预测已知少量节点的节点类(七个之一)。在这种情况下,已知的节点的数量是 140,如在 DGL 实现的那样,但是当全部信息可用时,可以使用不同的数量。在开始之前,我们必须安装 DGL 库,目前是 V0.7。然后我们继续以通常的方式导入一些模块,

接下来,我们必须以下面的形式加载 CORA 数据集,

在第 4 行中,我们将 g 设置为图形对象,然后我们检索一些张量。特征张量具有 2708 个节点的 1433 个特征,标签张量具有为每个节点分配从 0 到 6 的数字作为标签的条目。另外两个张量, train_masktest_mask 只是得到如果节点分别为训练或测试。在下表中,我们可以看到该图在 DGL 的值:

现在我们必须定义图的卷积,但在此之前,简单回顾一下公式是很重要的。我们回想一下,原则上需要图的邻接矩阵 A ,但它是根据这些方程进行变换的,其中 I 是单位矩阵[3]:

我们将根据以下等式在 python 类中定义图形卷积:

这里 x1x2 分别是第一和第二卷积。在 DGL,这可以通过调用 GraphConv 模块来轻松完成,在这种情况下,该模块执行 parentesis ( AXW +b )之间的操作,因为默认激活函数被设置为 None:

注意,在正向方法中,我们按照上面的等式定义了 x1x2 。下图显示了矩阵的大小是如何影响的。首先,在 Conv1 中, AX 是邻接矩阵( A )与特征矩阵( X )的矩阵乘法,给出 2708x1433 的矩阵。权重矩阵 W 因此具有 1433 行和 8*16=128 列(这个数字是任意的,但是工作良好)。我们以 2708x128 的矩阵 x1 结束。其次,我们按照相同的过程对 x1 进行第二次卷积,但这一次我们只以 7 个特征(与总类别数相同)结束,从而得到 2708x7 的矩阵 x2 :

两个回旋的示意图。图片作者。

最后,我们必须使火车的一部分。在这里,我们使用 Adam optimizer 和 log_softmax 进行了多达 200 个历元,这对于分类任务来说是很好的。为了检索损失、准确性和特征预测的值,我们在第 4 到第 6 行中引入了损失列表acc 列表所有日志

运行这段代码,经过 200 个周期后,我们得到大约 0.78 的精度,如下图所示:

200 个历元后的精确度。图片作者。

最后,如果希望查看预测的要素和标注,我们可以执行以下代码,其中最后一个时期(此处为 199)的结果存储在数据帧中,并对要素应用 argmax 运算,我们获得给出类的较高值的索引(0 到 6 之间的数字):

下面是数据帧前 5 行的输出,其中 7 列是学习的特征值,最后一列是该类的结果:

仅此而已。现在你可以尝试一些其他的例子或者其他的 GCN 变体,比如 SAGEConv,门控图卷积层或者图注意力层,这些都包含在大 DGL 库中。

试试这个 COLAB 笔记本中的代码:

https://github.com/napoles-uach/MediumPost/blob/main/CoraDGL.ipynb

我希望你喜欢这个讲座,如果是的话,请鼓掌 50 次!!

在推特上关注我→ @napoles3d

参考资料:

[1]https://www.dgl.ai/

[2]https://ojs . aaai . org//index . PHP/aimagazine/article/view/2157

https://arxiv.org/abs/1609.02907

开始一个新的机器学习模型

原文:https://towardsdatascience.com/starting-a-new-machine-learning-model-beeb81a361b8?source=collection_archive---------28-----------------------

布莱恩·马坦格罗在 Unsplash 上的照片

正如我的许多读者所知,我最近加入了一个新组织!project 44正在为将货物从一个地方运送到另一个地方的公司——也就是大多数公司——构建非常有价值的物流可见性和透明度工具!如果你生产、运输或销售任何有形的产品,你想知道它在哪里,它要去哪里,它应该什么时候到达,project44 在这里帮助所有这些事情,甚至更多。(我们正在招聘数据科学和许多其他职位!如果你想了解更多,请告诉我。)

感谢已经到位的优秀数据科学团队,我能够在到达时立即开始高价值的建模项目,这是每个数据科学家都喜欢的职位。经过深思熟虑,我意识到,我能够立即投入工作,并在潜水中感到舒适,是因为我从过去的经验中吸取了一些不成文的教训,这就是我要与你分享的。我没有权利深入了解这个模型的细节,但是我仍然可以给你一些好的建议。

特色是最重要的

你可能想在第一天抽取一个样本并运行一个模型——这是一个错误。您的数据以一种有益于模型的方式被清理、格式化和组织的可能性几乎为零。垃圾进,垃圾出!你的模型只和你的特征工程一样好,在大多数情况下,我会花至少3 倍于构建模型的时间进行特征工程。您的特征是构建模型最重要的部分,一个巧妙设计的特征比任何超参数调整都更能改善模型。

这可能意味着扩展你对特征的看法——一种我们在社会科学中称之为测量有效性的形式。简而言之,你想在你的模型中加入什么样的机制或概念?这就是接下来需要转换成数字特征的东西,它可能比你想象的更复杂。经常需要多个特性来完全覆盖您打算使用的概念,并且/或者可能仍然有一些方面您没有一个合理的方法来测量或量化。不要只是接受表或数据集中的列,而是要真正思考它们代表什么以及它们如何关联。

我们到底在做什么?

我想得太多了!让我们回到开始,在那里你被分配了一个建模任务,并且可以自由支配。我们将在项目的前几周花在特征工程上,采取我上面描述的扩展观点。我们如何在实践中实现它呢?根据我的经验,你必须从研究问题开始。除非您对结果、可用数据、数据的局限性等有所了解,否则您甚至不知道应该尝试构建什么功能(或重要的底层概念)。理解背景和情况不是可有可无的——把事情做好是必要的。

当你研究这个问题时,构建一个你认为可能有前途的领域的计划。如果您的团队使用看板或敏捷风格的方法,这非常适合构建标签或故事。如果你发现某个领域的背景或理论可能会对你的结果产生重大影响,把它放在一边进行深入研究,但不要现在就做。坚持下去,直到你有一系列强有力的深度潜水要做,记录下每一次潜水的大致想法,然后系统地完成它们。给自己设定时间限制,这样你就不会陷入一个把你带进兔子洞的困境——这些领域需要好好研究,但是你还有其他事情要做!

画,画,画

这可能只是“我”的事情,但当我学习一个新的主题领域为建模做准备时,我会做无数的可视化来帮助我理解这个东西。图表、绘图、地图、图解等——我发现这些对发展我的理解能力非常有用,这样我以后就能理解我的特征与结果以及彼此之间的关系。检查你对你正在建造的东西的直觉,并测试你的假设!可视化是做到这一点的好方法。对于我在新角色中构建的模型,到目前为止,我已经使用了笔和纸、散景、Altair 和 geopandas 来制作视觉效果,我仍然只处理前几个功能。

八二十分法则

正如我从许多明智的老板那里听到的,包括我现在的老板,在这个问题上追求完美和着手下一个需要优先做的事情之间有一个权衡。80/20 背后的想法是,你用 20%的努力将获得 80%的结果,然后你必须完成 80%的工作才能获得最后 20%的边际改善。(当我们意识到完美的模型性能不会发生时,这个比喻就有点站不住脚了,但你明白我的意思。)

如果你像我们一样幸运,有大量的问题要解决,有大量的数据要解决,唯一的障碍就是你的时间和员工能力。对我们来说,这意味着我们需要一个满足客户需求的好模型,但我们不需要一个“完美”的模型。MVP(最小可行产品)开发后的增量改进对于软件开发来说是很自然的,这也是数据科学经常使用的框架。

你的模型总是会有一些失误和一些盲点或边缘情况,这没关系!其实这是必然的。作为一名数据科学家,你需要理解并凭直觉判断什么时候你已经找到了一个可以完成工作的模型,并学会在那个时候放手。你以后会回来改进它,没有人期待完美。(如果是的话,那是他们的问题,也许是你所在组织对数据科学的态度的问题,但这不是你的模型的问题。)对于您的业务来说,回去继续那个 MVP 的工作几乎肯定没有让另一个 MVP 出去解决下一个主要问题来得紧迫。

一旦您所有的高优先级任务或问题都有了模型,那么您就可以自由地回去重新培训或对现有模型进行增量改进——这就是当您是一个维护稳定模型的成熟数据科学团队时的情况,这与我们在这里讨论的问题不同。

结论

当然,这只是开始建模过程的一组小提示-一旦开始,您会发现许多有趣的事情,例如数据中的特质、生成要素的新的创新方法,以及对先入为主的假设的完全意想不到的挑战。这就是工作的乐趣所在!拥抱机器学习项目的问题解决和探索性质,你将最终拥有你可以自豪的模型。

开始自然语言处理

原文:https://towardsdatascience.com/starting-natural-language-processing-8b37d189b406?source=collection_archive---------51-----------------------

探索自然语言处理的基础:清洗,NER,词性,模糊字符串匹配

由作者使用流程图(http://flowchart.js.org/)拍摄

每当人们谈论自然语言处理(NLP)时,花哨的机器学习模型和强大人工智能的前景的图像就会弹出。但是没有什么是从无到有的,在最基本的层面上,NLP 只是一个处理文本数据的工具的集合,以使它们更加干净和统一。我们开始吧!

文本清理

清理句子

首先,让我们从 a 句开始:

test001 = "01 Google Gains In Popularity, Soon Surpassing Apple"

这是一个标题行,所以所有的单词都是大写的,并且有标点符号,我们需要清理它们:

# remove punctuations
import string
translator = str.maketrans('', '', string.punctuation)
test002 = test001.translate(translator)
# Output:
# 01 Google Gains In Popularity Soon Surpassing Apple

我们可以将它们转换为全部大写或全部小写,让我们使用大写:

# convert to upper case
test003 = test002.upper()
# Output:
# 01 GOOGLE GAINS IN POPULARITY SOON SURPASSING APPLE

看起来不错!现在我们可以把它们分解成单独的单词:

# convert to list of words
from nltk.tokenize import word_tokenize
test004 = word_tokenize(test003)
# Output:
# ['01', 'GOOGLE', 'GAINS', 'IN', 'POPULARITY','SOON', 'SURPASSING', 'APPLE']

令牌清理

清理单词

有了单词列表,我们可以用它们做很多事情,首先,让我们去掉数字:

def clean_digits(words):return [w for w in words if not w.isdigit()]
test005 = clean_digits(test004)
# Output:
# ['GOOGLE', 'GAINS', 'IN', 'POPULARITY','SOON', 'SURPASSING', 'APPLE']

然后,我们可以对单词进行词干处理或将其词条化:

# stem words
from nltk.stem import PorterStemmer
PorterStemmer()
def stem(words):stemmer = PorterStemmer()res = []for w in words:rw = stemmer.stem(w)res.append(rw)return res
test006 = stem(test005)
# Output:
# ['googl', 'gain', 'IN', 'popular','soon', 'surpass', 'appl']

词干化将单词简化为它们的根,所以“GAINS”变成了“gain”,但是正如你所看到的,它非常粗糙,“GOOGLE”,实际上是一个名字,变成了“googl”…然而,词干化非常快,所以如果你有太多的数据要处理,并且准确性不是太大的问题,就继续使用词干化。

查找词根的另一种选择是 lemmatize:

# lemmatize words
from nltk.stem import WordNetLemmatizer
def lemmatize(words):lemmatizer = WordNetLemmatizer()res = []for w in words:word = lemmatizer.lemmatize(w)res.append(word)return res
test006 = lemmatize(test005)# Output:
# ['GOOGLE', 'GAINS', 'IN', 'POPULARITY','SOON', 'SURPASSING', 'APPLE']

等等,发生什么事了?似乎什么都没有改变。

这是因为 lemmatizer 搜索语料库(用于训练 lemmatizer 的文本正文)来确定词根,所以大小写很重要:

# lemmatize words, version 2
from nltk.stem import WordNetLemmatizer
def lemmatize(words):lemmatizer = WordNetLemmatizer()res = []for w in words:word = lemmatizer.lemmatize(w.lower())res.append(word)return res
test006 = lemmatize(test005)
# Output:
# ['google', 'gain', 'in', 'popularity','soon', 'surpassing', 'apple']

看起来不错!但是我们必须重新大写一遍:

# convert words to upper case
test007 = [w.upper() for w in test006]
# Output:
# ['GOOGLE', 'GAIN', 'IN', 'POPULARITY','SOON', 'SURPASSING', 'APPLE']

命名实体识别

NER:找出每个单词的意思

随着文本的清理,我们可以开始分析这句话的实际意思,即什么是“谷歌”,什么是“苹果”。

在此之前,我们或许可以去掉像“IN”这样的词,因为它们不会给句子增加太多意义(反正对机器来说不会),所以让我们去掉停用词:

# remove stop words
from nltk.corpus import stopwords
def clean_stop_words(words):stop_words = set(stopwords.words('english'))res = []for w in words:w_test = w[0] if isinstance(w, tuple) else wif w_test.lower() not in stop_words:res.append(w)return res
test008 = clean_stop_words(test007)
# Output:
# ['GOOGLE', 'GAIN', 'POPULARITY','SOON', 'SURPASSING', 'APPLE']

现在我们准备给这些词贴上标签:

# NER tagging
from nltk.tag.stanford import StanfordNERTagger
jar = 'stanford-ner.jar'
model = 'english.all.3class.distsim.crf.ser.gz'
ner_tagger = StanfordNERTagger(model, jar, encoding='utf8')
test009 = ner_tagger.tag(test008)
# Output:
# [('GOOGLE', 'ORGANIZATION'),
#  ('GAIN', 'O'),
#  ('POPULARITY', 'O'),
#  ('SOON', 'O'),
#  ('SURPASSING', 'O'),
#  ('APPLE', 'O')]

让我们来看看。因此,NER 过程试图用一个类别来标记每个单词,在这种情况下,我们使用斯坦福的 3 类 NER 标记器[1],它可以识别位置,人和组织。还有其他 NER 标记者,如 spaCy,一个标记者更适合某些项目,而另一个可能更适合其他项目。

请注意,“苹果”没有被标记为“组织”,这凸显了这种方法的一些缺陷。

词性标注者

词性标注

如果我没有提到 POS tagger,那将是我的疏忽,因为在许多应用中它是非常重要的(只是在这个故事中对我来说不是)。

POS tagger 正是你所猜测的,它根据词类标记每个单词:名词、动词、代词等:

# POS tagging
from nltk.tag.stanford import StanfordPOSTagger
jar = 'stanford-postagger.jar'
model = 'english-caseless-left3words-distsim.tagger'
pos_tagger = StanfordPOSTagger(model, path_to_jar=jar)
test010 = pos_tagger.tag(test008)
# Output:
# [('GOOGLE', 'NNP'),
#  ('GAIN', 'NN'),
#  ('POPULARITY', 'NN'),
#  ('SOON', 'RB'),
#  ('SURPASSING', 'VBG'),
#  ('APPLE', 'NN')]

在这里,我们再次使用斯坦福的 POS 标记器[2],在这种情况下,标记器能够识别“谷歌”是一个专有名词(NNP),而“超越”是一个动词(VBG),但“增益”标记错误。

串匹配

将单词与单词配对

嗯,所有的文本处理和标记都很好,但是你能用它们做什么呢?

从模型训练、预测到数据分析,都有无限可能。天空是极限。

让我们把我们所做的应用到一个不起眼的例子中。

假设我想为一家公司收集新闻标题,比如说谷歌。我已经收集了我们一直在谈论的一个标题,我想要一种方法来映射这个标题到谷歌的股票。我如何开发一个自动化的过程来做这件事?

首先,我将使用我们开发的技术从标题中提取第一个组织,假设是主题:

# get subject organization
def first_org(words):for w in words:if w[1] == "ORGANIZATION":return w[0]return None
org = first_org(test009)
# Output:
# GOOGLE

一旦我们有了组织,我们将需要公司的股票代码和与之匹配的描述。为此,我从维基百科[3]中检索信息:

ticker = "GOOGL GOOGLE ALPHABET INC CLASS A"

由于股票代码描述通常与从文本中提取的公司名称不完全匹配,我们需要应用模糊匹配:

# try match organization
from fuzzywuzzy import fuzz
ticker = "GOOGL GOOGLE ALPHABET INC CLASS A"
fuzz.ratio(ticker, org)
# 31
fuzz.partial_ratio(ticker, org)
# 100

因此,这里的“ratio”返回 31%的匹配,因为它是匹配与总字数的比率:

l1 = len(ticker)
# 33
l2 = len(org)
# 6
res = 2*6/(l1+l2) # 6matches
# 0.31

而“部分比率”实际上是在较长的字符串中循环,对较短的字符串进行每个子字符串的比率匹配,并返回最高分。由于“GOOGLE”实际上是 ticker 描述的一部分,所以最高分是 100%。

结论

在所有的文本清理之后,我们才刚刚开始探索 NLP。有了可用的输入数据,我们可以应用单词矢量化、单词聚类和许多技术来建立复杂的预测模型。更多即将推出!

这个故事的灵感来自于:明周、萨希尔普里、阿尔什苏德和亚当韦恩【4】发表的论文

参考:

[1]https://nlp.stanford.edu/software/CRF-NER.shtml

https://nlp.stanford.edu/software/tagger.shtml

[3]https://en.wikipedia.org/wiki/List_of_S%26P_500_companies

https://papers.ssrn.com/sol3/papers.cfm?abstract_id=2940564

从数据分析开始,推动您的业务发展

原文:https://towardsdatascience.com/starting-out-with-data-analytics-to-boost-your-business-ae0da038b3de?source=collection_archive---------38-----------------------

如何进入晦涩复杂的数据分析世界?提示:这比你想象的要简单。

卢卡斯·布拉塞克在 Unsplash 上的照片

数据分析可以成为你武器库中非常强大的工具。无论你是拥有一家小企业还是在一家大公司。使用数据分析可以帮助您提高盈利能力,更加精简,甚至改进您的产品。如果你有兴趣了解更多关于分析如何帮助你的工作流程,这是我写的另一篇关于这个主题的文章。但是现在,让我们假设你已经做了决定,为了开始,你应该采取哪些步骤呢?

我真的需要学习如何编码吗?

这个问题的答案显然是否定的。为了能够分析数据,你需要学习一些东西,但编码不是其中之一。当然,学习一些有用的语言,比如 R 和 Python,会对你有很大的帮助,但它们不是先决条件。

数据分析师为什么要编码?

如果编码不是必需的,那么为什么那么多数据分析师学习它?毕竟,如果你去上大学,不管是本科还是研究生,你都会接触到大量的编程课程。在这些课程中,你主要学习 R 和 Python。许多数据科学家更喜欢编码原因是它允许更大的灵活性和速度。

编程语言是有等级的,简单来说,一种语言的可读性越强,计算机运行它的难度就越大。这也适用于将 Python 与 Excel 之类的东西进行比较。您可以使用这两种工具完成相同的工作,但是 Excel 在处理大型数据时会比较慢。如果你习惯使用 Python,你可能会更喜欢它而不是 Excel,因为它速度快,而且你不会被 Excel 函数所限制。Python 有大量的软件包可供选择,可以做任何事情,从数据分析(Numpy,Pandas),到创建网站(Flask,Django),到创建 2D 游戏(Pygame)。编程语言也更具可扩展性。当您知道将来会有更多的数据到来时,可伸缩性和面向未来是非常重要的。这允许您一次性完成工作,然后在以后进行编辑以适应数据的涌入。

你如何学习如何编码

尽管我刚刚指出了编码的一些好处,但这并不是必需的。如果你的最终目标是成为一名成熟的数据分析师,那么是的,你需要学习这些语言。但是,如果您希望在较小的规模上执行一些分析,还有其他选择。

Excel 和 Tableau 就是两个很好的例子。使用这两个程序有一个学习曲线,但它们在处理中等规模的数据集方面很棒。Excel 非常适合运行计算、收集见解。如果你希望可视化你的数据并展示给其他人看,Tableau 将会很好地为你服务。各地的分析师仍在使用这两个程序,许多大学都在教授它们。

从哪里开始?

数据分析是用数字讲故事。你的主要目标是能够从你的企业产生的数字中获得有用的信息。

选择你的武器

你需要做的第一件事是挑选你的工具。你需要选择一个程序,(Excel,R,Python,Tableau…)开始学习基础。这里需要记住一件重要的事情。归根结底,这些都是做同样基本事情的工具。除非你需要分析一个非常具体的数据集,否则你真的不能选择其中任何一个。你应该记住的两个主要考虑因素是学习曲线和个人偏好。你愿意花多少时间来掌握这项技能?你觉得用哪个最舒服?永远不要开始的最好方法是认为你必须使用完美的工具,而实际上它们都有相同的基本功能。做一些快速的调查,然后做出决定。

弄清楚你的目标

当你心中有一个目标时,致力于一个项目更有可能帮助你长期坚持下去。所以在你开始这段旅程之前,试着想想你想从中学到什么。是对你的产品有更多的见解吗?关于供应商的更多信息?你的目标市场?这也将帮助你制定一个更稳健的计划,并产生更好的结果。

寻找灵感

为你的故事寻找灵感是非常重要的。当你开始时,你会问自己,这些数据中有什么信息可以解开?这是一个很难回答的问题,事实是,它因业务、行业、公司规模和许多其他因素而异。一个开始的方法是看看其他人在网上做了什么。做一些研究,找出类似公司的分析师在做什么,可以给你指明正确的方向。但是在一天结束的时候,你处于确定你需要分析什么的最佳位置。你的业务之外没有人能告诉你你到底需要什么,所以相信你的直觉。

收集数据

这也可能是旅程中令人生畏的一部分。我的建议,由内而外开始。查看您公司不同部门的所有记录,找到这些数字在哪里,并开始查看它们。如果这不起作用,那就自己想办法,看看你是否能找到与你想要实现的目标相关的市场数据,如果找不到,那就估算一下。你可以查看是否有其他城市、州、甚至国家的顾客信息,并开始思考如何应用到你的市场。再次强调,相信你的直觉,如果对近似值有什么感觉,那么就解释一下。例如,如果你所关注的市场的家庭收入中值比你高得多,那么在解读数据时要记住这一点。

实践

作为一名数据分析师,入门或提高的最佳方式是进行一些实践。这并不需要很长时间,几个小时的工作时间就足够了。获得一些实践的一个好方法是在像 Kaggle 这样的网站上查看更著名的数据集,下载它们,并开始处理它们。在这些数据集上进行一些实践的好处是,所有级别的数据科学家以前都曾在这些数据集上工作过。这给你提供了一个很好的参考,一旦你完成了,就可以回去比较你自己的工作。你会知道你做对了什么,你可能错过了什么。

结论

数据分析是无限的。你可以想走多远就走多远,但重要的是开始行动。一旦你开始并更适应这个领域,你可以进一步推进,但是,你必须迈出第一步。

2021 年人工智能状况报告摘要

原文:https://towardsdatascience.com/state-of-ai-report-2021-summary-6c16f4eb72a6?source=collection_archive---------11-----------------------

人工智能的世界里发生了很多事情。这篇文章在 17 分钟内包含了 183 张幻灯片。

在过去的几年里,由内森·本耐奇和伊恩·霍加斯撰写的《人工智能现状报告》报道了人工智能领域的最新和最伟大的成就。

虽然数量庞大,但《人工智能状况报告》每年都会收集人工智能领域发生的事情的快照。

我说快照是因为发生了很多事情。尽管官方报告中有 180 多张幻灯片,但仍有很多遗漏。

也就是说,这个总结是一个总结的总结。

因此,从《2021 年人工智能报告的状态来看,我错过了很多东西,以下几点因我最感兴趣而有所偏差。

如果你想阅读完整的报告和过去的报告,请访问 AI 网站。

该报告将覆盖范围分为五类:

  1. 研究——技术突破。
  2. 人才—AI 人才的供给、需求和集聚。
  3. 行业 —人工智能的商业应用领域及其商业影响。
  4. 政治 —人工智能的监管,其经济影响和人工智能的新兴地缘政治。
  5. 预测 —作者认为未来会发生什么,以及对过去预测的绩效评估。

我保持这些部分不变。

研究(幻灯片 10–74)

研究部分涵盖了最近的技术突破及其能力。

我在这一部分的重点是自我监督学习,transformer 架构的使用和语言模型的接管,更多的研究论文使他们的代码可用,以及一项研究显示推文学术工作导致多达 3 倍以上的引用。

监督较少的模型学习得更好(好得多)

自我监督学习是模型遍历未标记的数据集并自行识别潜在模式的过程。

例如,自我监督学习用于支持大型语言模型,这种模型可以理解不同的单词在语言中如何交互。

语言模型可能会阅读整个维基百科(以及更多),并了解到单词“dog”比单词“car”更可能出现在句子“The ___ jumped over the fence”中。

与这种技术类似的过程现在正被用于计算机视觉,只是用学习图像中像素之间的关系来代替学习单词。

  • 幻灯片 12: 脸书 AI 创造了 SEER (自我监督),这是一个在 10 亿张随机的未标记和未分类的公共 Instagram 图像上训练的十亿参数自我监督模型,在 ImageNet 上达到了 84.2%的 top-1 准确率。
  • 幻灯片 13: 自我监督的视觉变形金刚 (SSViT)学习监督模型没有的功能。发现这些特征也是强有力的预测器,使用 k-NN 分类器达到 78.3%的前 1 名准确度。从自我监督中学习到的特征包含关于语义分割(图像中不同项目的分离)的更好信息。这些发现被应用到一种叫做的方法中,这种方法被称为【无标签自交法】(DINO) 。

迪诺学习图像中主题的语义分离。来源:脸书 AI 博客。

很明显,有了这些发现,从几乎没有标签的数据中学习变得更加现实。

变压器接管

我想知道是什么推动了 transformer 架构的发展,因为关注是你所需要的全部纸,HuggingFace 在几天内发布每一个新的变体或者架构的性能。产品、营销和渠道的完美风暴。

  • 幻灯片 11: 视觉变形金刚(ViT) 在 ImageNet 上以 90.45%的顶级准确率实现计算机视觉的最先进水平(SOTA)。然而,结合卷积(更好的电感偏置)和变压器(高性能上限)会产生略好的结果,其中卷积和自关注网络 (CoAtNets)在数据量减少 23 倍的情况下获得类似的结果,或者在数据量增加的情况下获得略好的结果(90.88%的前 1 名准确度)。
  • 幻灯片 13: 基于 Transformer 的 Conformer 结合了自我关注和卷积,实现了语音识别的最先进水平(LibriSpeech 基准测试中最低的单词错误率)。点变形器也实现了 3D 点云分类的最新技术。
  • 幻灯片 15: DeepMind 的感知者和感知者 IO 架构展示了变压器架构的构建模块(注意力)的通用程度。感知者模型可以处理各种不同的数据类型,如语言、音频、视频、点云和游戏。一个亮点是,感知者 IO 在 GLUE 语言基准上匹配基于 Transformer 的 BERT 基线,而不需要标记化(直接从 UTF-8 字节学习)。

没那么快变压器、卷积神经网络(CNN)和多层感知器(MLPs)仍然很酷

事实证明,有了现代训练技术和数据准备程序,CNN 和 MLPs 可以表现得非常好。

  • 幻灯片 17: 谷歌研究人员发现当像变形金刚一样进行预训练时,CNN 在自然语言任务上的表现类似或更好,这提出了一个问题,是训练还是架构得到了结果?他们还发现,在 MLP 混合器中,一个仅由 MLP 组成的架构,当应用于以与视觉变形器相同的方式准备的数据(图像被切割成小块)时,可以与其他更成熟的视觉架构竞争。
  • 奖励: ResNet 反击是一篇表明现代训练技术(MixUp、CutMix、LAMB optimizer 等)可以将普通 ResNet 体系结构的结果在 ImageNet 上提高大约 4%的 top-1 准确率的论文。而补丁就是你所需要的一切?论文显示,使用与视觉转换器相同的数据准备技术(将图像转换为补丁),简单的 ConvMixer 架构(可以适合一条推文)优于 ViT 和 MLP 混频器。

事实证明,如果你以一种好的方式准备你的数据,一个史诗模型可以在一条推特的空间内建立起来。来源:安德烈·卡帕西推特。

同样,是培训、数据准备还是架构导致了结果?

深度思维深度思维深度思维

DeepMind 在生物学和强化学习方面取得了重大研究突破。

DeepMind 的 XLand 模拟器示例,用于帮助 RL 代理学习通用技能。来源: DeepMind 博客。

无处不在的语言模型提出了即时工程的新挑战

查看语言模型的一种方式就像一个工具。这种乐器可以演奏几乎无限多种声音,但只有其中一些听起来不错。而获得更好声音的方式取决于你如何使用(提示)乐器。

如果你敲击一件乐器,你会得到剧烈的声音,但如果你以某种方式演奏,你会得到交响乐。

如果你给一个语言模型一个好的提示,你通常会得到好的结果(幻灯片 46),然而不好的提示可能会有同样差的结果(幻灯片 47)。

  • 幻灯片 41: Codex 是语言模型 GPT-3 的一种形式,专注于将自然语言转化为可工作的计算机代码。Codex 被用来驱动诸如 GitHub Copilot 这样的应用。
  • 研究人员发现,对于自然语言处理(NLP)中的大型语言模型,提示可能比微调更好。在预训练、提示和预测中,他们发现基于提示的学习允许语言模型(LM)在大量原始文本上进行预训练,并且通过定义新的提示函数,LM 能够使用很少或标记的数据执行少量甚至零次学习。例如,在社交媒体文本上训练,当识别“我今天错过了公共汽车”的情绪时,提示“我感觉如此 ____”可以导致 LM 填充该情绪的空白。

医学模型可以比医生学到更多,但额外的知识可能会有偏差

人们发现,在医学扫描上训练的视觉模型可以比临床医生更好地识别病人的种族。这是有益的还是有害的?

  • 幻灯片 55: 如果人工智能系统可以从医学扫描中识别出患者自我认同的种族,这是否可能最终放大或制造种族差异?种族的已知特征似乎来自图像的所有区域,这使得缓解变得复杂。这篇论文强调的主要问题是,该模型碰巧秘密地使用其自我报告的种族知识对某一种族的患者进行了错误分类,放射科医生将无法使用该模型可以访问的相同数据进行辨别。

机器学习研究的代码共享有所改善,但还可以更好

  • 幻灯片 56:有代码的论文网站显示,在 arXiv 上发表的人工智能研究论文中,26%有可用的代码库,高于去年的 15%。然而,检查过去 30 天的“最热门的论文”(截至 2021 年 9 月 8 日在 Twitter 上共享的顶级论文)只有 17%共享代码库。60%的可用代码仓库使用 PyTorch 。

分享的机器学习论文代码仓库中,他们用的是什么深度学习框架?来源:https://paperswithcode.com/trends

如果机器学习论文没有附加代码,并且结果不可复制,研究是否有效?

  • 幻灯片 60: 没有更多的数据。更好的数据。选择模型训练的数据会对模型的性能和训练产生巨大影响。一种被称为“通过代理选择”(SVP)的方法允许 CIFAR-10(一种流行的图像数据集基准)的 50%可以被删除,而不影响准确性,并将训练时间加快 40%。SEALS ( 用于高效主动学习的相似性搜索和稀有概念搜索)允许网络规模的主动学习(例如,约 100 亿张图像)。它的工作原理是将已标记数据的已学习表示聚类在一起,然后在每一轮选择中只考虑这些已学习表示的未标记最近邻,而不是扫描所有未标记样本。
  • 幻灯片 62: 学术机构提交其研究代码多于行业。
  • 幻灯片 63: 在 Twitter 上分享您的作品→获得多达 3 倍的引用。一项由一项为期一年的随机试验组成的研究显示,随着时间的推移,在推特上发表或不发表论文的学术工作会导致更多的文章被引用。如果没有乔布斯,沃兹尼亚克的工作会成功吗?或者没有圣保罗,谁会听说过耶稣?
  • 幻灯片 74: 有一个新的深度学习框架正在酝酿中: JAX 。JAX 的设计类似 NumPy,只是它可以在 GPU 上运行。就个人而言,我发现它类似于 PyTorch,尽管可能更基于函数而不是基于类(OOP)。目前,它更侧重于研究,而不是生产(像 PyTorch 不是一段时间),但它似乎很可能会结束。

人才(幻灯片 75–91)

人才部分报告了人工智能人才(人工智能技能需求和可用性)在世界各地的传播。

人才大量集中在美国、中国和英国(这与全球许多地方类似)。

我的国家,澳大利亚,不在任何供应或需求的名单上。但人工智能和人工智能的美妙之处在于,许多最好的作品都可以在网上找到。因此,尽管我是在澳大利亚的布里斯班写下这些文字的,我还是非常享受其中的乐趣。

美国和英国以外的国家在人工智能能力和人才方面都在增长

  • 幻灯片 76: 中国和印度在人工智能研究方面不断发展。巴西和印度招聘的人工智能人才是 2017 年的 3 倍以上,赶上或超过了加拿大和美国的增长。就多样性而言,印度 30%的论文包含女性作者,相比之下,美国和英国为 15%,中国为 4%。
  • 幻灯片 77: 从 1980 年没有出版物开始,中国科学院现在出版了当今数量最大的高质量人工智能研究。

机构及其历年 AI 出版物。资料来源: Oecd.ai

  • 幻灯片 78: 中国在 STEM 博士增长方面正在超越美国。预计到 2025 年,中国的 STEM 博士生数量将达到美国的近两倍。博士项目的质量似乎仍然与数量相匹配。

再见大学任期,你好股票期权

  • 幻灯片 81: 精英和精英一起工作。大型科技公司通常会与大型精英大学合作。这意味着一小部分公司和大学完成了大部分研究。没有计算能力加入进来?你出局了。
  • 幻灯片 83: 北美大学的教授们看到了美元和流向大型科技公司。深度学习革命的最后 10 年是成为数学或统计学教授的好时机,这些教授希望研究他们的领域,同时变得富有。CMU 大学、佐治亚理工学院、华盛顿大学和伯克利大学在 2004 年至 2019 年间失去了最多的教师。
  • 幻灯片 88: 大型科技公司喜欢资助人工智能研究, 88%的顶级人工智能教师都得到了大型科技公司的资助。
  • 第 91 张幻灯片:许多科技工作者正在让在家工作成为常态(就目前而言)。机会总是普遍存在,但现在更是如此。像 Twitter 这样的一些公司已经将远程工作永久化。从 2021 年开始,拥有技术技能似乎是获得远程工作的绝佳机会。

行业(幻灯片 92–152)

把研究和才能放在一起,你会得到什么?最好是有用的东西。这就是工业部门处理的事情。

人工智能研究都去哪了?

今年的报告在药物发现领域有很多特色,尽管新冠肺炎的 ML 社区做出了努力,许多申请还是失败了。

人工智能正在推动药物研发

  • 幻灯片 93: Exscientia,一家人工智能优先的药物公司,它首创了世界上第一批 3 种人工智能设计的药物进入第一阶段人体测试,在纳斯达克以>美元的 3B 估值 IPO。该公司还有 4 种候选药物正在提交。人工智能有助于合成少 10 倍的化合物来找到一个候选物,并导致 12 个月的目标命中对 54 个月的行业平均水平。
  • 幻灯片 94: Allcyte 的计算机视觉 AI 有助于为每个癌症患者确定最有效的药物,以提高存活率。它在细胞水平上测量活癌细胞对 140 种不同的第三方抗癌药物的反应,并帮助确定特定患者的最佳选择。

从电力需求预测到人工授精奶牛的最佳时间,人工智能正在广泛应用于各个行业

  • 幻灯片 98: 英特斯耶的计算机视觉模型有助于保护员工免受工伤。该系统经过培训,可检测超过 35 种员工健康和安全事故。与人类安全检查员相比,该系统全天候实时工作,导致其在 18 个月内检测到 180 万起不安全行为。

Intenseye 的计算机视觉捕捉工作场所不安全行为的示例。资料来源:Intenseye.com。

  • 幻灯片 100: 变压器闪闪发光,它们被用于工业的一个地方是改善电网需求。英国国家电网 ESO(电力系统运营商)与开放气候修复公司联合使用时间融合变压器,该变压器自 2021 年 5 月以来一直在提供预测。该系统将 1 小时交付周期的平均绝对误差(MAE)减少了一半以上,将 24 小时交付周期的平均绝对误差减少了 14%。更好地预测电力需求可以降低排放,提高电网稳定性。
  • 幻灯片 101: Connectera 开发了一个人工智能软件程序和硬件设备,名为 Ida ,它可以帮助跟踪奶牛,并能够预测理想的繁殖窗口、健康下降、听觉运动等情况。数据是通过戴在脖子上的传感器收集的。
  • 人工智能对你的直觉有什么看法?佐伊可以从 1100 人的肠道细菌宏基因组测序中把好细菌和坏细菌与不同的食物来源联系起来。该模型可以根据微生物群中的细菌,以 0.72 AUC 预测某人是否饮用大量咖啡。这些模型是在英国数据上训练的,但在英国和美国的测试集上进行了测试。

生产中的机器学习是可行的,但要让它发挥作用仍有困难

  • 幻灯片 106: 生产学习中的机器学习推动研究人员更多地从以模型为中心的人工智能转向以数据为中心的人工智能。这是因为 ML 社区越来越意识到更好的数据实践和更标准化的 MLOps(机器学习操作)的重要性。有一些资源可以帮忙:deep learning . AI 举办的以数据为中心的比赛,以数据为中心的 GitHub repo 收集资源和datacentricai.org,一个致力于详细介绍以数据为中心的人工智能挑战的网站。
  • 幻灯片 110: 尽管付出了所有努力,也有许多参与者,但许多新冠肺炎的 ML 文献并没有达到临床实践所需的稳健性或可重复性的阈值。25%使用计算机视觉来检测新冠肺炎和肺炎的论文使用相同的控制数据集来比较成年患者,而没有提到它包括 1-5 岁的儿童。
  • 幻灯片 111: 随着数据质量工具的发展,ML 团队正在启动更多的项目,并意识到训练数据集不再是一个固定的对象,而是一个不断增长的知识语料库。自动化标签和可访问的最先进的架构可用性意味着数据数量和质量成为人工智能优先创业公司的竞争指标。

COVID 强调了我们有多爱电脑

但是电脑需要芯片,芯片需要被制造出来,而事实证明只有少数公司能做到。

  • 幻灯片 114: 欧洲终于意识到他们最大的公司 ASML 是全球半导体的基础。ASML 为芯片制造商提供硬件来制造他们生产的芯片。随着市场意识到芯片制造商的重要性,以及随后帮助芯片制造商制造芯片的公司的重要性,他们的市值在疫情期间翻了 3 倍。

ASML 的芯片制造机每台成本 1.5 亿美元,包含 10 万个零件,用四个集装箱装运。来源:连线杂志。

  • 幻灯片 117: 随着全球芯片市场的持续增长,全球主要半导体制造商承诺投入约 4000 亿美元用于新功能。英特尔投入总计超过 1000 亿美元,TSMC 超过 1000 亿美元,三星超过 2000 亿美元。
  • 幻灯片 129: 谷歌一直在慢慢地将人工智能注入其更多的商业和消费者应用程序,如基于人工智能的语法检查器 Gmail smart reply、 Google Sheets 公式预测、使用 ar 的地图和针对更低的燃料使用和二氧化碳排放而优化的新路线,以及开源 MediaPipe ,这是一个用于将视觉技术集成到不同设备的跨平台工具包。

使用上下文感知的谷歌表单公式预测。来源:谷歌 AI 博客。

  • Slide 133:clip drop是一款应用程序,它使用人工智能将图像的主题从背景中分离出来,然后允许你将分离的图像放到你的电脑上,以便在网站上使用。这有助于张贴背景干净的产品图片。他们的开源工具 cleanup.pictures 允许你通过在图片上乱涂乱画来移除不想要的东西。

运行中的 ClipDrop 应用程序。来源:https://clipdrop.co/。

  • 幻灯片 141 至 152: 大量投资进入人工智能领域,大量人工智能公司也退出了(出售给更大的公司)。

我觉得奇怪的一点是,在自动驾驶部分或任何行业部分都没有提到特斯拉。考虑到特斯拉拥有世界上最大的公共自动驾驶车队,并且是人工智能领域的领导者。

我也希望看到更小的公司加入进来。

就像描述使用人工智能提供的转录功能通过文本编辑视频和播客,描述使用计算机视觉识别不同种类的植物,以及 comma.ai 创建开源的自动驾驶汽车软件和运输设备,以允许人们使用它。

尽管这些观点因我个人的兴趣而有所偏颇。虽然我喜欢大公司做酷的事情,但我更喜欢小公司做酷的事情。

政治(幻灯片 153–181)

政治部分着眼于人工智能的管理以及如何在经济和伦理意义上使用它。

这是一个艰难的话题,因为在人工智能领域发生的许多事情是:1 .快速移动和 2。未知,比如,人们试图规范的一些事情,创造它们的工程师甚至不确定他们是如何做到的。

  • 幻灯片 154: 谷歌解雇了员工 Timnit Gebru 博士,因为她试图发表一篇关于人工智能伦理的论文,但没有通过。这里有些双重标准。促进道德的人工智能,但解雇那些表明立场的人。
  • Slide 155: 工业革命规模的变革性人工智能或人工智能预计将在 2052 年左右到来。一个核心假设是,如果研究人员能够训练一个神经网络或 ML 模型,使用与人脑类似的计算水平,这将有可能导致变革性的人工智能。
  • Slide 156: 68%接受调查的机器学习研究人员认为人工智能安全应该比现在更优先考虑。但这是一个反问题吗?就像,你不会说相反的话,“人工智能安全不应该被优先考虑”。在商业玩家中,OpenAI、DeepMind、谷歌和微软被认为最有可能塑造人工智能在公共利益方面的发展。不过既然 DeepMind 是 Google,OpenAI 是微软,玩家应该更多(后面进入 Anthropic)。
  • 幻灯片 160: DeepMind 试图从谷歌独立出来,变得更加非营利,然而谷歌阻止了这一举动。DeepMind 想要创造 AGI,他们担心这不应该掌握在一个单一的实体手中(理应如此)。
  • 幻灯片 161: Anthropic 是一家新的人工智能安全公司,致力于构建可靠、可解释和可操纵的人工智能系统。这个新实体已经筹集了 1.24 亿美元,由 OpenAI 等公司的研究人员组成。正因为如此,它们将成为继 DeepMind 和 OpenAI 之后 AGI 研究的第三大支柱。他们在招人。

Anthropic 有望成为专注于 AGI 的公司的第三大支柱。

  • 幻灯片 163 + 164: EleutherAI 接受通过开源分散权力的挑战。他们创造了一个与较小的 GPT-3 一样大小的模型,性能相当或更好,并且向所有提供。我爱爱爱这个。阅读他们的博客,了解 2020 年 7 月一群不和谐的流氓黑客的故事。

EleutherAI 在 2020 年 7 月通过一个 Discord 服务器开始。从那以后,他们做了一大堆。阅读他们博客上的故事。

  • 幻灯片 168: 一些实体希望更多地监管人工智能。然而,人工智能的监管是在科学界尚未完全理解的许多事情上提出的,例如,人工智能算法的公平性、可解释性和鲁棒性,这些都是仍然开放的研究问题。在经济方面,预计到 2025 年,每年的合规成本将在€16 亿欧元和€33 亿欧元之间。
  • 幻灯片 170: 美国的大部分数据仍然不受监管。一些州有不同的法规。但是,不管一些大型科技公司怎么想,美国宪法并没有规定隐私权。但是,有多少人在乎这个呢?我不确定。放弃你的数据意味着什么?我是伴随着互联网长大的,我明确知道网上或电脑上的一切都是在线的,并且可能是可访问的。
  • 幻灯片 175–180:军事人工智能正在加速发展。以色列声称在加沙袭击中使用了人工智能引导的无人机群。美国空军基于 DeepMind 在游戏上的工作制造了一个名为 Zero 的人工智能副驾驶。微软签署了一份价值 220 亿美元的合同,将 Hololens 用于军事用途,并订购了 12 万头戴式耳机,作为集成视觉增强系统的一部分。

预测(幻灯片 183)

预测部分包括报告作者对未来一年人工智能世界的想法和预测。

  1. 变形金刚取代递归网络用 RL 代理学习世界模型在大而丰富的环境中超越人类的表现。
  2. ASML 的市值达到 5000 亿美元。
  3. Anthropic 在 GPT、Dota、AlphaGo 的水平上发表文章。
  4. 人工智能半导体的整合浪潮,至少有一家芯片公司被大型科技公司收购。
  5. 小型变压器+ CNN 混合模型匹配 ImageNet top-1 精度上的当前 SOTA,参数少 10 倍。
  6. DeepMind 发布物理科学重大研究突破。
  7. 根据 PapersWithCode 的测量,JAX 框架每月创建的代码回购从 1%增长到 5%。
  8. 一家新成立的以 AGI 为重点的公司拥有强大的支持和专注于某个垂直行业(如开发工具、生命科学)的路线图。

我的预测

我来补充几个我自己的。

  1. HuggingFace 成为托管公共模型和数据集的事实标准,社区将能够将这些构建到自己的应用程序中。
  2. 芯片公司开始意识到单一供应链的脆弱性,一家大公司决定在当地投入超过 1000 亿美元。
  3. EleutherAI 复制了 GPT-3 比例模型,但更加高效和开源。
  4. 随着深度学习框架和模型架构的成熟,一个新的数据创建框架出现了,适用于所有数据:标记、监管、验证、提取。尽管你可能会说像 scale.com 这样的公司已经这样做了。正因为如此,第一次 Kaggle 式的纯数据竞赛开始了。
  5. 一项大型研究发表在改进模型的不同方面:是数据准备吗?是训练套路吗?是模型架构吗?

资源

以上所有观点要么直接来自于以下内容,要么受到以下内容的启发。

  • 艾网站状态。
  • 人工智能 2021 年的状态幻灯片(参见幻灯片的脚注,链接到作者的观点)。

非常感谢内森·本耐奇和伊恩·霍加斯今年和每隔一年整理出这份报告。

这最初是作为机器学习月刊 2021 年 10 月刊发表的,这是我写的每月一次的时事通讯,涵盖了机器学习领域的最新和最伟大的(但不总是最新的)。 注册 将下一期内容发送到您的收件箱。

2021 年 5 月底语音合成的技术水平

原文:https://towardsdatascience.com/state-of-the-art-of-speech-synthesis-at-the-end-of-may-2021-6ace4fd512f2?source=collection_archive---------4-----------------------

思想和理论

在 2021 年 5 月底展示语音合成研究(也称为文本到语音)的最新发展水平,重点是深度学习技术。我提供了 71 个出版物的综合,并给你理解基本概念的钥匙。

瓦伦丁·米勒在 Unsplash 上的照片

介绍

声音是我们最自然的交流方式。因此,会话助手的发展自然会朝着这种通信方式发展。这些虚拟语音助手可以部署到呼叫中心,通过预先确定呼叫者的请求,考虑最简单的请求(例如预约)。对于视障者,提供屏幕上文本的描述或描述他们面前的场景。操作员可以在语音的帮助下对机器进行干预以对其进行修复,并且他可以不用手来与机器进行交互,即不必使用键盘或鼠标。几年来,GPS 导航一直通过语音为你导航。

在这篇论文中,我将讨论语音合成研究的现状。我将介绍用于从句子中自动生成信号的技术。在对该主题进行简短介绍后,我将介绍与自动合成相关的问题,然后我将介绍处理管道,我将快速解释什么是 mel-spectrogram,然后是深度生成模型、端到端系统、当今研究的参与者,我将继续讨论允许实现学习的可用数据集。我将解释质量是如何衡量的,以及哪些会议展示了这项工作。我会完成剩下的挑战。

TL;速度三角形定位法(dead reckoning)

在语音识别和“简单”(单调)语音生成方面,对话式语音助手已经达到了与人类几乎相同的水平。语音生成是一个复杂的过程,包括从一个简单的句子中生成几千个代表声音信号的值。神经网络通过提供更好的信号质量、更容易的训练数据准备以及生成时间已经减少到能够比人快几百倍地生成句子的程度,已经取代了传统的串联生成技术。

信号的生成通常在两个主要步骤中完成:第一步生成句子的频率表示(mel 频谱图),第二步从该表示生成波形。第一步,将文本转换成字符或音素。这些被矢量化,然后编码器-解码器型架构将这些输入元素转换成压缩的潜在表示(编码器),并通过解码器将该数据逆转换成频率表示。这一步最常用的技术是与注意机制相关的卷积网络,以改善输入和输出之间的一致性。这种对齐通常通过持续时间、电平和音调预测机制来加强。在第二阶段,由所谓的声码器处理,三维时间频率表示(时间、频率和功率)被转换成声音信号。最有效的架构是 GAN(生成对抗网络)架构,其中发生器将生成信号,该信号将被鉴别器挑战。

当这些架构由人评估时,质量水平几乎达到训练数据的质量。由于很难比输入数据做得更好,因此研究现在转向元素的贡献,通过添加韵律、节奏和个性元素,使生成的信号更接近现实,并能够更精细地参数化生成。

虚拟语音助手

三年前的今天(2018 年 5 月),谷歌首席执行官桑德尔·皮帅在谷歌 I/O 的主题演讲上展示了一段虚拟助理(谷歌双工)与一家发廊的员工交谈的电话录音。这位助理负责为第三方预约。当时最例外的元素是通话近乎完美,完美的模仿了一个真实的人进行预约,并在谈话过程中加上了“嗯嗯”的声音。对话的流程是如此完美,我仍然怀疑这是不是一个骗局。这个演示预示着一场即将到来的语音人际关系自动化领域的革命。

无论是梦想还是现实,这种将餐馆或理发店的预订委托给虚拟助理的功能,3 年后,只在美国可用。这项服务在其他国家也可以使用,但只是通过自动呼叫商家进行验证来提高谷歌搜索引擎和地图时间表的可靠性。这家创新的公司在这里向我们展示了它制造工具的能力,这些工具可以以优异的质量水平陪伴人类进行某些活动。至少有足够的质量让他们决定提供这种服务。

为了实现对话式语音助手,必须有一个处理链,其中第一个组件将用户的语音转换成文本(语音到文本)。第二个组件(机器人)分析呼叫者的文本并生成响应。第三个也是最后一个组件将机器人的回答转换成语音(文本到语音)。通过扬声器或电话线向用户播放结果。

语音助手的符号表示(作者的图表)

语音合成,也称为文本到语音或 TTS,在很长一段时间内是通过组合一系列或多或少由一组编程规则规定的转换和或多或少令人满意的输出结果来实现的。近年来,深度学习的贡献使得更多自主系统的出现,这些系统现在能够生成数千种不同的声音,其质量接近人类。今天的系统已经变得如此高效,以至于他们可以从几秒钟的声音样本中克隆出一个人的声音。

一对多问题

为了生成音频信号,合成系统将遵循一组或多或少复杂的步骤。语音合成必须解决的一个主要问题是一对多建模问题,这包括将一条信息(要发声的句子)转换成需要几千个值的数据(波形)的能力。此外,这种信号可以有许多不同的特征:音量、特定单词的重音、发音速度、句子结尾的管理、感情的添加、音调……因此,系统架构师的问题是通过将这一代分成可以全局或单独训练的步骤来解决这种复杂的处理。

信号产生的不同阶段(作者的图表)

加工管道

第一个语音生成系统直接使用空气来产生声音,然后计算机科学带来了可以通过参数使用生成规则的系统,以通过从或多或少的结果声音数据库(英语中有超过 1,700 个双音素,法语中有 1,200 个双音素,可以通过说话者、句子开始/结束、韵律等复制)连接双音素来快速生成句子

传统的语音合成系统通常分为两类:拼接系统和生成参数系统。双音素的组合属于拼接语音合成的范畴。级联合成有两种不同的方案:一种是基于线性预测系数(LPC),另一种是基于基音同步重叠相加(PSOLA)。结果通常是平的、单调的和机械的,即缺乏真正的韵律,尽管可以调整结果。对于韵律,我们指的是超音段特征,如语调、旋律、停顿、节奏、流、重音……随着基于隐马尔可夫模型(HMM)的生成声学模型的创建和上下文决策树的实现,该方法得到了改进。深度生成系统现在已经成为标准,淘汰了已经过时的旧系统。

遵循一对多原则,要构建的系统包括将文本转换成中间状态,然后将该中间状态转换成音频信号。大多数统计参数语音合成系统(SPSS)不直接生成信号,而是生成其频率表示。第二个组件称为声码器,它根据这种表示完成生成。近年来,随着卷积网络、递归网络、(2013)、注意机制(2014)、甘(2014)和其他网络的出现,生成网络的原理已经成为规范。

下图描述了用于生成语音的基于机器学习的流水线架构的不同组件。

语音合成系统标准操作图(作者图)

像任何基于学习的系统一样,生成主要由两个阶段组成:学习阶段和生成阶段(或推理阶段)。有时,插入一个“中间”阶段,用其他数据对声学模型进行微调。

管线的外观因阶段而异:

  • 在学习阶段,管道允许生成模型。句子是编码器/解码器的输入和与句子相关的语音文件。有时还会加上说话人的 ID。在许多系统中,产生 mel 频谱图,并且声码器将该表示转换成波形。声码器的输入是声学参数(通常是 mel 声谱图)和与参数相关的语音。从两个综合分析模块中提取的信息集被称为“语言特征”(声学特征)。
  • 在生成阶段,管道负责执行推理(或合成或生成)。输入是要转换的句子,有时是说话人 ID,用于选择与生成的语音相匹配的语音特征。输出是 mel 光谱图。声码器的作用是从要产生的音频的紧凑表示中产生最终波形。

具体来说,用于学习(培训)的渠道包括:

  • 一个文本分析模块,执行文本规范化操作,将数字转换为文本,将句子拆分为多个部分(词性),将字素(书面音节)转换为音素(G2P),添加韵律元素等。有些系统直接处理文本的字符,有些系统只使用音素。在培训和综合过程中,此模块通常“按原样”使用。
  • 声学分析模块接收与文本相关联的声学特征作为输入。该模块还可以在多说话者训练期间接收说话者 ID。本模块将分析理论特征和培训阶段产生的数据之间的差异。声学特征可以使用诸如快速傅立叶变换的“经典”信号处理算法从语音样本中生成。该模块还可以生成模型来预测信号的持续时间(音素和 mel 声谱图的样本数量之间的联系)及其与文本的对齐。最新的系统倾向于改进预测网络并增加音调预测。2020 年底,斯德哥尔摩 EECS 学校的 va Szé kely 在学习阶段增加了呼吸处理,这缩短了人与机器之间的距离。
  • 来自训练阶段的声学模型表示从句子嵌入向量、说话者向量和声学特征中提取的潜在状态。此外,还有对齐和其他功能的预测模型。
  • 语音分析模块用于从原始语音文件(地面实况)中提取各种参数。在某些系统中,尤其是端到端系统,前面和后面的静默被去除。提取因系统而异,可以包括提取音调、能量、重音、音素持续时间、基频(第一谐波频率或 F0)等。从输入语音信号中。这些输入语音文件可以是单个或多个扬声器。在多扬声器系统的情况下,扬声器矢量被添加到输入中。

用于合成(推理)的管道包括:

  • 基于文本分析模块的输出并通过声学模型,特征预测模块生成完成生成所必需的紧凑语音表示。这些输出可以是以下表示中的一个或多个:信号的梅尔频谱图(MelS)、巴克标度倒谱系数(Cep)、线性标度对数幅度频谱图(MagS)、基频(F0)、频谱包络、非周期性参数、音素的持续时间、音调高度…
  • 声码器的输入可以是一个或多个上述表示。这个模块有许多版本,它倾向于作为一个独立的单元使用,以牺牲端到端系统为代价。最受欢迎的声码器有 Griffin-Lim、WORLD、WaveNet、SampleRNN、GAN-TTS、MelGAN、WaveGlow 和 HiFi-GAN,它们提供的信号接近人类的信号(参见如何测量质量)。

早期基于神经网络的架构依赖于使用传统的参数 TTS 管道,例如:DeepVoice 1 和 DeepVoice 2。DeepVoice 3、Tacotron、Tacotron 2、Char2wav 和 ParaNet 使用基于注意力的 seq2seq 架构(Vaswani 等人,2017)。基于深度神经元网络(DNNs)的语音合成系统现在优于所谓的经典语音合成系统,例如(几乎)不再在研究中看到的串联单元选择合成和 hmm。

下图显示了按年份分类的研究论文出版的不同架构。它还显示了系统使用以前系统的功能时的链接。

按年份和关系划分的建筑之间的不同网络和链接(作者图)

梅尔谱图简介

声码器的输入通常由 mel 声谱图构成,Mel 声谱图是声音信号的特定表示。这个声谱图是通过对音频信号(时间/幅度)应用若干变换来实现的。

第一种变换包括使用短期快速傅立叶变换(STFFT)提取信号的频谱。STFFT 将通过捕捉组成音频信号的不同频率以及每个频率的幅度来分解音频信号。由于信号随时间的可变性,信号被分成部分重叠的窗口段(通常在 20 毫秒和 50 毫秒之间)。

由 aqu egg——自己的作品,公共领域,https://commons.wikimedia.org/w/index.php?curid=5544473

横轴对应时标,纵轴对应频率,像素颜色对应信号功率,单位为分贝(dB)。颜色越浅,频率越强。频率标度然后被转换成对数标度。

由于人耳感知频率差异的方式是不同的,无论频率是低还是高,Stevens、Volkmann 和 Newmann 在 1937 年提出了一种称为 mel 音阶的音阶,它给出了一个音高单位,使得距离听者相同距离的音高声音相同。因此,声谱图的频率通过该标度被转换成 mel 声谱图。

深度生成模型

语音生成系统面临的挑战是从少量信息中,甚至从无信息中产生大量数据。一个 30 个单词的句子,以 22KHz 在 10 秒内发音,需要产生 440.000 字节(16 位)的序列,也就是说,比率为 1 比 14.666。

自动生成建模领域是一个广阔的领域,几乎有无限的用途。直接的应用多种多样,如图像生成,以及由 GPT-2 和最近的 GPT-3 普及的文本生成。在我们的情况下,我们期望他们实现声音的产生。一开始的所谓“经典”网络(CNN 代表卷积神经网络)已经被更复杂的递归网络(RNN 代表递归神经网络)所取代,因为它们引入了先前上下文的概念,这在语音连续性的上下文中是重要的。今天,这一代主要是通过深度模型架构实现的,如 DCCN(扩张因果卷积网络)、师生、VAE(变分自动编码器)和 GAN(生成对抗网络)、精确似然模型(如 PixelRNN/CNN)、图像转换器、生成流等。

最常见的架构有:

自回归方程

  • 自回归模型是一种基于回归的时间序列模型,其中序列由其过去的值而不是其他变量来解释。在语音生成的情况下,大多数早期的基于神经网络的模型是自回归的,这意味着未来的语音样本取决于过去的样本,以便保持长期的依赖性。这些模型很容易定义和训练,但有传播和放大误差的缺点。并且生成时间与生成句子的长度成正比。首先,它们具有串行的缺点,因此无法受益于 GPU(图形处理器单元)和 TPU(张量处理单元)处理器的最新并行化能力。这使得创建需要在合理的时间内响应用户的实时系统变得困难。随着 WaveNet 和 ClariNet 的推出,非自回归系统允许在不依赖于前代产品的情况下生成语音样本,这允许仅受处理器存储器限制的强大并行化。这些系统的实现和训练更加复杂,精确度较低(内部依赖性被消除),但可以在数毫秒内生成所有样本。

扩张因果卷积网络(DCCN)

  • 由谷歌及其流行的 WaveNet 于 2016 年推出的扩张因果卷积网络(或 DCCN)是一种扩张因果卷积,通过跳过某个步长的输入值,将滤波器应用于大于其长度的区域。这种扩张使得网络只有几层就有非常大的感受野。随后,许多架构已经将这种模型集成到它们的生成链中。

流动

  • 流程架构由一系列可逆转换组成(Dinh 等人,2014 — Rezende 和 Mohammed,2015)。术语“流程”意味着简单的可逆转换可以相互组合,以创建更复杂的可逆转换。非线性独立分量估计(NICE)模型和实非保体积(RealNVP)模型构成了两种常见的可逆变换。2018 年,NVIDIA 通过将 Glow(用于生成流)技术集成到 WaveGlow 中来使用这项技术,以便从 mel 频谱图表示中生成语音文件。

师生模式

  • 师生模型涉及两个模型:一个预训练的自回归模型(老师),用于指导非自回归网络(学生)学习正确的注意力对齐。教师将记录学生的并行前馈模型的输出。这种机制也被称为知识蒸馏。该学生的学习标准与反向自回归流相关,其他基于流的模型已通过 WaveGlow 引入。这些并行合成模型的主要问题是它们对可逆变换的限制,这限制了模型的容量。

可变自动编码器

  • 变型自动编码器 (VAE)是自动编码器的一种改编。自动编码器由两个相互协作的神经网络组成。第一个网络负责将输入编码成连续的潜在简化表示,其可以被内插(压缩形式= z)。第二个网络负责通过减少输出损失来从编码中重建输入。为了限制过度学习的影响,学习在均值和协方差方面被正则化。在语音生成的情况下,编码器将文本转换成对应于声学特征的潜在状态,解码器将该状态转换成声音信号。编码器/解码器模型从图像生成中借鉴了很多,因为基本思想是生成图像,即频谱图。因此 PixelCNN、Glow 和 BigGAN 一直是 TTS 网络的灵感来源。

生成对抗网络

  • 生成对抗网络(GANs-good fellow 等人)于 2014 年出现,以协助图像生成。它们是基于反对生成器和鉴别器的原理。生成器被训练成从数据生成图像,鉴别器被训练成确定所生成的图像是真的还是假的。圣地亚哥的一个团队(Donahue et al .,2018)有使用这种技术产生音频信号的想法(WaveGAN 和 SpecGAN)。许多声码器使用这种技术作为产生原理。gan 是生成语音文件的最佳单元之一。

其他系统也存在,但不太常见,如扩散概率模型,包括通过马尔可夫转移链修改信号,如添加高斯噪声,IAF…

注意机制的贡献通过消除递归的需要极大地改善了 seq2seq 网络,但是仍然很难预测输入和输出之间的正确对齐。早期的网络使用基于内容的注意机制,但是导致了对齐错误。为了纠正这个问题,测试了其他几种注意机制:高斯混合模型(GMM)注意机制、混合位置敏感注意机制、动态卷积注意(DCA)和单调注意(MA)对齐方法。

下表显示了近年来建设的主要网络的不同架构。

不同网络体系结构的描述(作者列表)

端到端系统

2017 年 2 月,蒙特利尔大学提出了 Char2Wav,对应于 SampleRNN 的双向递归神经网络、注意力递归神经网络和神经声码器的组合。这种端到端网络允许直接从文本中学习波形样本,而无需经过中间步骤,如 mel 频谱图。它直接是网络的输出,用作输出网络的输入。

char 2 wav(【https://openreview.net/forum?id=B1VWyySKx】T2)的建筑

Char2Wav 由播放器和神经声码器组成。读者是一个专注的编码解码器。编码器接受文本或音素作为输入,而解码器从中间表示开始工作。如该大学所述:

"与传统的文本到语音模型不同,Char2Wav 学习直接从文本产生音频."

同月,百度还开发了名为 DeepVoice 的全自主端到端系统。它是从一个小音频剪辑及其转录的数据集来训练的。最近,微软在 2020 年推出了一个名为 FastSpeech 2s 的网络,该网络避免了 mel 频谱图的生成,但质量低于其姐妹网络生成的频谱。

这种端到端网络架构在语音生成领域并不占优势。大多数体系结构将字形-音素模型、持续时间预测模型和用于生成 Mel-频谱图的声学特征模型以及声码器分开。

最新的出版物和去年由中国科学技术大学(USTC)在爱丁堡大学的帮助下组织的暴雪 2020 挑战赛证实了这种情况,因为上次挑战赛见证了经典系统的消失和基于 2 代阶段的 SPSS(统计参数语音合成)系统的统治。超过一半的参赛团队使用神经序列对序列系统(例如 Tacotron ),并使用 WaveRNN 或 WaveNet 声码器。另一半研究基于 DNNs 和同样的声码器的方法。

谁是研究参与者?

在研究的 71 篇论文中,公司或大学家族的出版物分布显示,网络公司占主导地位,紧随其后的是技术公司。大学只是第三。

出版商家族发行的出版物

论文的来源国首先是美国,然后是中国。排在第三位的是韩国。出版商的国家与公司总部所在地相对应。

按出版商国家分列的出版物发行情况

就出版物数量而言,亚洲仅领先于北美(32 对 30)。

按洲分布

谷歌及其子公司 DeepMind(英国)是近年来发表论文最多的公司(13 篇)。我们欠他们关于 WaveNet,Tacotron,WaveRNN,GAN-TTS 和 EATS 的论文。其次是百度(7 篇出版物),有关于 DeepVoice 和单簧管的论文,微软有关于 TransformerTTS 和 FastSpeech 的论文。

按公司/大学分配

哪里可以找到数据集?

不再需要对语音序列的每个元素进行强制注释。今天,有一个包含相关句子和声音的语料库就足够了,这是生成模型所必需的唯一元素。因此,有可能比以前有更多的样本,特别是通过(按字母顺序):

  • 暴雪语音数据库——多语言——可变大小——暴雪挑战赛为每个参赛者提供了几个语音样本,供他们在挑战过程中进行训练。这些数据库在每次比赛中都可以免费下载。
  • CMU-北极-美国 1.08 GB 该数据库由大约 1150 个从古腾堡计划文本中精心选择的短语组成,没有版权。
  • 共同的声音 6.1-多语言-可变大小 Mozilla 已经启动了一项计划,建立一个数据库,使语音识别对每个人开放和可用。为此,他们发起了一个社区项目,允许任何人背诵句子并检查其他人的背诵情况。法语版 6.1 数据库代表 18 GB 或 682 个有效小时。
  • 欧洲语言资源协会 —多语言—可变大小—许多商业用途的付费语料库。
  • LDC 语料库数据库 —多语言—可变大小—语言数据联盟(LDC)是一个由大学、图书馆、企业和政府研究实验室组成的开放联盟,成立于 1992 年,旨在解决语言技术研发面临的严重数据短缺问题。LDC 由宾夕法尼亚大学主办。
  • LibriSpeech —美国— 57.14 GB — LibriSpeech 是由 Vassil Panayotov 在 Daniel Povey 的帮助下准备的,以 16 kHz 的采样率朗读的约 1000 小时的英语语音语料库。数据来源于阅读 LibriVox 项目的有声读物,并经过仔细的分割和排列。
  • LibriTTS —美国— 78.42 GB — LibriTTS 是一个 585 小时英语阅读的多说话人英语语料库,采样率为 24 kHz,由 Heiga Zen 在 Google Speech 和 Google Brain 团队成员的帮助下编写。
  • LibriVox —多语言—可变大小— LibriVox 是一群来自世界各地的志愿者,他们阅读并录制公共领域的文本,以创建免费的有声读物供下载。
  • LJ Speech —美国— 2.6 GB —无疑是模型评估中最知名和最常用的数据集之一。这是一个公共领域的语音数据集,由 13,100 个单个说话者阅读 7 本非小说书籍的简短音频剪辑组成。为每个剪辑提供一份抄本。这些剪辑的长度从 1 秒到 10 秒不等,总时长约为 24 小时。这些文本出版于 1884 年至 1964 年之间,属于公共领域。该音频由 LibriVox 项目在 2016-2017 年录制,也在公共领域。
  • VCTK —美国— 10.94 GB — CSTR VCTK(语音克隆工具包)包括 109 个操各种口音的英语者所说的语音数据。每个演讲者阅读大约 400 个句子,这些句子选自《先驱报》、《彩虹通道》和一段启发性段落。这些句子对所有参与者都一样。

存在许多数据集,给它们命名会很复杂: OpenSLR ,META-SHARE,audio books……尽管有大量的语音数据,但总是很难找到允许在韵律和情感方面训练模型的数据集。

如何衡量质量?

为了确定生成信号的质量,没有琐碎的或计算机化的测试可用于评估分类器的性能。质量是根据许多因素评估的,包括自然度、鲁棒性(系统不会忘记单词或重复的能力)和准确性。

因为必须检查质量以确定模型的性能,所以这必须由人类来完成。讲被评估语言的被选择的人被要求评价声音信号的音频质量。平均意见得分(MOS)是从通过对声音再现的质量判断(非标准化的,因此是主观的)进行投票而获得的得分中计算出来的。分数从 1 分差到 5 分优不等。

MOS 评定量表

被选中的听众被邀请收听生成的音频文件,有时与源文件进行比较(地面实况)。听完后,他们给出一个分数,分数的平均值就是 MOS 分数。自 2011 年以来,研究人员可以受益于一种描述良好的工作方式,这种方式基于众包方法。最著名的是一个被称为 CrowdMOS 框架,主要使用众包网站 Amazon Mechanical Turk(f . Ribeiro et al .—Microsoft—2011)。

大多数实验室使用这一原则对其算法进行评估,从而对算法之间的性能进行总体评价。应当注意,评估结果高度依赖于说话者和录音的声学特性。另一方面,这些值不是直接可比的,因为它们通常是用不同的数据集训练的。尽管如此,它允许我们了解彼此相关的架构的质量。当研究人员对他们的模型和他们同事的模型进行对比测试时,情况就更是如此了。

该图提供了所研究的每个模型的 MOS 值。只有用英语实现的 MOS 分数被保留。

生成模型和声码器的美-英 MOS(作者图)

MUSHRA 方法(针对具有隐藏参考和锚的多个刺激)也是一种常用的听力测试。听众被要求比较自然语音和生成信号之间的混合信号,他们在 0 到 100 的范围内指定一个分数。

2019 年,Binkowski 等人引入了一种称为弗雷歇深度语音距离(FDSD)的定量自动测试,这是对应用于计算 2 个语音文件之间距离的弗雷歇距离的改编。该测试允许产生的信号和原始文件之间的距离得分。这个分数特别用于生成性对抗系统。

还计算其他指标,如 RMSE(均方根误差)、NLL(负对数似然)、CER(字符错误率)、WER(单词错误率)、UER(发音错误率)、MCD(梅尔倒谱失真)…

语音会议

一些会议涉及语音合成,我们注意到,科学出版物的日期往往针对这些会议之一。特别是,我们将提到(按字母顺序):

  • 国际声学、语音和信号处理会议(ICASSP)是由 IEEE 组织的年度会议。它发生在六月。主题包括声学、语音和信号处理。
  • ICLR-成立于 2013 年的国际学习代表会议(ICLR)于 5 月举行,主要讨论机器学习。
  • ICML——国际机器学习大会(ICML)创立于 1980 年,每年 7 月举行。研究论文的提交时间为 12 月底至 2 月初。
  • inter seech——inter seech 会议成立于 1988 年,于 8 月底/9 月初举行。国际语音通信协会(ISCA)的目标是促进与语音通信科学技术相关的所有领域的活动和交流。
  • NeurIPS——创建于 1987 年,人工智能和计算神经科学的科学会议称为 neur IPS(用于神经信息处理系统),每年 12 月举行。它涉及机器学习网络和人工智能使用的所有方面。

接下来的挑战

在会话系统中,尽可能自然地进行会话是很重要的,即不要有过多的停顿。因此,声音的产生必须几乎是即时的。人类能够非常迅速地对一个问题做出反应,而生成系统通常必须等到生成结束时才能将信号返回给用户(特别是因为它接收到一个完整的句子进行转换)。随着并行性的使用,最近出现的非自回归系统明显超过了旧模型,因为它们能够将输出信号的产生分成几个并行的活动,从而能够在几毫秒内产生语音信号,而不管句子的长度(这被称为实时因子或 RTF)。在电话呼叫中心,对话式虚拟助理(也称为 callbot)必须能够在几秒钟内对请求做出反应,否则会被认为效率低下或功能失调。

语音生成系统仍然经常受到单个字母、拼写、重复数字、长句、数字到语音等的限制。,这导致取决于输入的质量不一致。此外,模型中仍会产生一些不寻常的峰值,并导致产生的信号不一致。由于人耳对这种变化极其敏感,自动生成的低质量会被立即检测到并降低欣赏分数。即使已经取得了巨大的进步,消除这些错误仍然是一个挑战。

模型的大小和它们在存储器中占用的空间也是未来网络的一个问题,未来网络将不得不匹配具有许多参数(数千万个参数)但资源少得多的网络。在低资源设备(如手机或机顶盒)上,CPU 和内存是有限的。DeviceTTS (Huand 等人-阿里巴巴集团语音实验室-2020 年 10 月)能够生成“仅”150 万个参数和 0.099 GFLOPS 的语音,质量接近 Tacotron 及其 1350 万个参数。基于 FastSpeech 2 的 LightSpeech (Luo 等人-中国科技大学和微软-2021 年 2 月)设法在不损失 MOS 的情况下,将其与 FastSpeech 2 的 1.8M 参数相匹配。

大多数生成的声音通常是单调的,平的,除非有包括各种情感表达和多个说话者的数据集。为了向系统指示要应用于输出信号(韵律)的持续时间和节奏的变化,有一种被称为语音合成标记语言(或 SSML)的标记语言。通过围绕要变化的单词的标签系统,可以应用一定数量的特征,例如音高及其范围(较低或较高)、轮廓、速率、持续时间和音量。它还允许你定义停顿,说一个词,等等。最新的出版物倾向于用其他机制取代这种标记,如预期韵律发音(使用风格标记的表达性 TTS-Kim 等人,2021)。研究人员正在修改现有的网络,增加额外的网络来调制产生的信号,从而模拟情感、强调等。(重点-李等 2018,CHiVE-Wan 等 2019,加味 Tacotron- Elyasi 等 2021)。还使用了声音转换技术:信号在生成后被修改,以便根据目的地对其进行调制。

大多数系统被设计成产生与用于训练的声音相对应的单一声音。人们对在训练期间不被看到的情况下生成新语音有一些兴趣(特别是根据最近的出版物:Arik 等人 2018 年、贾等人 2018 年、Cooper 等人、Attentron Choi 等人 2020 年和 SC-GlowTTS Casanova 等人 2021 年)。零触发 TTS (ZS-TTS)方法包括依靠几秒钟的语音来使网络适应新的声音。这种方法类似于语音克隆。名为多说话人多风格语音克隆挑战赛的比赛是一项挑战,参赛队必须提供一个解决方案,以相同或其他语言克隆目标说话人的语音。在大规模听力测试中也对结果进行了评估。另一个类似的比赛更普遍地关注声音转换:T2 声音转换挑战赛。

世界上有 7100 多种不同的语言。如果我们把所有使用的方言加起来,这个数字就上升到 41,000(例如,中国境内有 540 多种语言,印度有 860 多种语言)。工业语音合成系统通常只提供这个极其多样化的生态系统的一小部分。例如,谷歌云语音到文本提供 41 种语言,如果您添加国家变化(例如,法国加拿大和法国法国),则提供 49 种语言。

当前的网络能够通过从被克隆的人的声音的几秒钟的学习转移来再现声音。它让一个失去声音的人有可能产生声音,现在可以通过录制他的声音几分钟来模仿别人。因此,有可能让一个人说出你想要什么,或者试图模仿一家公司的经理的声音。在综合的同时,下一个挑战将是提供能够检测这些欺诈的系统。由于人类无法在不改变两种发音的情况下将同一个句子发音两次,因此有可能通过简单地要求他重复他的句子来检测我们是在与一个机器人还是一个人打交道。然而,你需要有一个好的耳朵和一个好的听觉记忆来做出改变!

结论

语音合成是一个令人兴奋的领域,因为它触及了我们人类地位的核心,并且它今天达到了非常接近自然声音的质量水平。

大学和公司研究实验室已经超越了简单的语音再现阶段,并且已经接受了其他挑战,例如在不降低质量的情况下提高生成速度、纠正上一代错误、从单个说话者生成几种不同的语音、向信号添加韵律等。

迄今为止所产生的语音的质量水平足够高,可以应用于所有可能的领域,尤其是基于语音的对话助手的环境中。

你会用语音合成做什么商业用途?

参考

CrowdMOS:一种众包平均意见得分研究方法

暴雪挑战 et 暴雪挑战 2020

基于双向 LSTM 递归神经网络的语音合成(2014),范等【pdf】

SampleRNN:无条件端到端神经音频生成模型(2016),Soroush Mehri 等人【pdf】

wave net:Raw Audio 的生成模型(2016),Aä ron van den Oord 等人【pdf】

Char2Wav:端到端语音合成(2017),J Sotelo 等人【pdf】深度语音:实时神经文本到语音(2017),塞尔詹 o .阿里克等人【pdf】

深度语音:实时神经语音转文本(2017),Arik 等人【pdf】

深度语音 2:多说话人神经文本到语音(2017),塞尔詹·阿里克等人【pdf】

深度语音 3:2000-说话人神经文本转语音(2017),魏平等[pdf]

通过调节 Mel 光谱图预测的 WaveNet 进行自然 TTS 合成(2017),沈健等人【pdf】

并行 WaveNet:快速高保真语音合成(2017),Aaron van den Oord 等人【pdf】

多任务学习框架下基于生成对抗网络的统计参数语音合成(2017),S . Yang 等【pdf】

Tacotron:走向端到端的语音合成(2017),王雨轩等[pdf]

VoiceLoop:通过音韵循环进行语音拟合和合成(2017),Yaniv Taigman 等人【pdf】

单簧管:端到端文语转换中的并行波生成(2018),魏平等【pdf】

LPCNet:通过线性预测改善神经语音合成(2018),让-马克·吕林燕等人【pdf】

FastSpeech:快速、鲁棒和可控的文本到语音转换(2019),任意等人【pdf】

梅尔内特:频域音频生成模型(2019),肖恩·瓦斯奎兹等人【pdf】

多说话人端到端语音合成(2019),Jihyun Park 等[pdf]

梅尔根:条件波形合成的生成对抗网络(2019),昆丹·库马尔等人【pdf】

【Transformer TTS】用 Transformer 网络进行神经语音合成(2019),李乃汉等【pdf】

【ParaNet】并行神经文本转语音(2019),彭等【pdf】

WaveFlow:一个基于压缩流的 Raw 音频模型(2019),魏平等人【pdf】

Waveglow:基于流的语音合成生成网络(2019),R Prenger 等人【pdf】

alignts:无需明确对齐的高效前馈文本到语音系统(2020),甄曾等[pdf]

EfficientTTS:一个高效高质量的文语转换架构(2020),苗晨峰等[pdf]

[EATS] 端到端对抗性文本转语音(2020),杰夫·多纳休等[pdf]

FastSpeech 2:快速高质量的端到端文本转语音(2020),任意等人【pdf】

Flowtron:一个用于文本到语音合成的基于自回归流的生成网络(2020),Rafael Valle 等人【pdf】

Flow-TTS:基于 Flow 的非自回归文语转换网络(2020),苗晨峰等【pdf】

Glow-TTS:通过单调对齐搜索的文本到语音的生成流程(2020),Jaehyeon Kim 等人【pdf】

HiFi-GAN:高效高保真语音合成的生成对抗网络(2020),Jungil Kong 等人【pdf】鲁棒长格式语音合成的位置相关注意机制(2020),Eric Battenberg 等人【pdf】

【Merlin:一个开源的神经网络语音合成系统(2016)吴等

【DC-TTS】基于深度卷积网络的高效可训练文本到语音转换系统,具有引导注意力(2017) Tachibana 等人【pdf】

WaveGAN / SpecGAN 对抗性音频合成(2018) Donahue 等人【pdf】

WaveRNN 高效神经音频合成(2018) Kalchbrenner 等人【pdf】

实时说话人相关的神经声码器(2018)金等【pdf】

【SEA】样本高效自适应文语转换(2018)陈等【pdf】

FloWaveNet:Raw Audio 的生成流程(2018) Kim 等人【pdf】

GELP:用于从 Mel-spectrogram 合成语音的 GAN 激励线性预测(2019)朱韦拉等人【pdf】

榴莲:用于多模态合成的持续时间通知注意网络(2019)于等【pdf】

具有对抗网络的 GAN-TTS 高保真语音合成(2019) Binkowski 等人【pdf】

梅尔根:条件波形合成的生成对抗网络(2019)库马尔等人【pdf】

并行 WaveGAN:基于生成式对抗网络的多分辨率声谱图快速波形生成模型(2019) Yamamoto 等人【pdf】

SqueezeWave:用于设备上语音合成的超轻型声码器(2020)翟等[pdf]

【RobuTrans:一种基于鲁棒变换的文本语音转换模型(2020)李等【pdf】

【多频带 MelGAN:高质量文本到语音的快速波形生成(2020)】杨等【pdf】

FeatherWave:一种高效的高保真多带线性预测神经声码器(2020)田等【pdf】

TalkNet:全卷积非自回归语音合成模型(2020)贝利耶夫等【pdf】

WG-WaveNet:无需 GPU 的实时高保真语音合成(2020) Hsu 等人【pdf】

JDI-T:用于无显式对齐的文本到语音的联合训练持续时间通知转换器(2020) Lim 等人【pdf】

WaveNODE:语音合成的连续归一化流程(2020) Kim 等人【pdf】

FastPitch:带基音预测的并行文本到语音(2020) Lancucki 等人【pdf】

VocGAN:一种具有分层嵌套对抗网络的高保真实时声码器(2020)杨等【pdf】

SpeedySpeech:高效的神经语音合成(2020) Vainer 等人【pdf】

WaveGrad:波形生成的梯度估计(2020)陈等【pdf】

DiffWave:音频合成的通用扩散模型(2020)孔等【pdf】

【BVAE-TTS】非自回归文语转换的双向变分推断(2020) Lee 等【pdf】

非注意 Tacotron:鲁棒可控的神经 TTS 合成包括无监督持续时间建模(2020)沈等【pdf】

平行 Tacotron:非自回归可控 TTS (2020) Elias 等【pdf】

【高效 WaveGlow:一种改进的 WaveGlow 声码器,速度更快 (2020)宋等【pdf】

Reformer-TTS:使用 Reformer 网络的神经语音合成(2020) Ihm 等人【pdf】

DeviceTTS:一个小尺寸、快速、稳定的设备上文本到语音转换网络(2020)黄等[pdf]

Wave-Tacotron:无声谱图的端到端文本语音合成(2020) Weiss 等人【pdf】

s-Transformer:鲁棒神经语音合成的段-Transformer(2020)王等【pdf】

通用 MelGAN:用于多域高保真波形生成的鲁棒神经声码器(2020) Jang 等人【pdf】

VARA-TTS:基于具有残留注意的深度 VAE 的非自回归文本到语音合成(2021) Elias 等人【pdf】

PeriodNet:具有分离周期和非周期分量的结构的非自回归波形生成模型(2021) Hono 等人【pdf】

AdaSpeech:自定义语音的自适应文语转换(2021)陈等【pdf】

并行 Tacotron 2:具有可微分持续时间建模的非自回归神经 TTS 模型(2021) Elias 等人【pdf】

用于快速流式文本到语音频谱建模的多速率注意力架构(2021)何等【pdf】

Diff-TTS:文本到语音的去噪扩散模型(2021) Jeong 等人【pdf】

TalkNet 2:具有显式基音和持续时间预测的语音合成的非自回归深度可分离卷积模型(2021) Beliaev 等人【pdf】

AdaSpeech 2:非转录数据的自适应文语转换(2021)严等【pdf】

Grad-TTS:文本到语音的扩散概率模型(2021) Popov 等人【pdf】

塞凯丽,é。,Henter,G . e .,Beskow,j .,Gustafson,J. (2020)
自发语音合成中的呼吸和语音规划

状态转换图和对象-角色建模

原文:https://towardsdatascience.com/state-transition-diagrams-object-role-modeling-36808e3fc0c2?source=collection_archive---------35-----------------------

四层元模型体系结构的实现

状态转换图。图片作者。

状态转换图捕获值类型的状态以及导致值类型的值从一种状态变为另一种状态的事件(如转换)。

如果你已经关注我的文章有一段时间了,现在你会看到对象-角色模型逐渐演变成实体关系图(erd)和属性图模式(pgs)(如下)。这非常有趣,再次证明了如果你有一个对象-角色模型(ORM ),你会自动有一个 ER 图或 PGS。

ORM 和 ER 图与属性图模式之间的态射。图片作者。

然而,在这个演示中没有明确的是,所有的图可能被存储在一个元模型中,或者视情况而定的元元模型中。例如,所有的图都可以存储在对象-角色建模本身的元模型中。

对于门外汉来说,这可能会令人困惑。这意味着,如果你有一个足够表达的模型来建模,那么你就有了元模型。如果你有一个元模型,你就有一个元模型。对象管理小组已经在其元对象设施(MOF)中定义了一个这样的模型排列,关于这个的更多信息可以在维基百科条目这里中找到。我们感兴趣的是捕捉对象、模型、元模型和元元模型的概念的架构;这就是所谓的四层架构。

状态转换图和四层架构

对象-角色模型、实体关系图和属性图模式都是彼此的变形,这意味着它们有共同的结构点。ORM 是所有三种语言中表达能力最强的一种,它可以在功能上被翻译成 ER 图和属性图模式,而不需要将 ER 图和 PGS 图与它们的通用 ORM 图分开存储……所以它们不需要存储在元模型/元元模型中。这意味着您可以动态地生成实体关系图和 PGS 图,可以这么说,如果您愿意的话。但是选择权在你……如果你愿意,你可以将它们存储在建模软件的元模型中。

但是,如果您想在与 ORM 图相同的元模型中创建和存储状态转换图,您需要一个四层架构。

你为什么会?为什么要使用四层架构?

本文顶部的状态转换图描述了一个人的生命阶段,以及一个人如何从一个阶段转换到另一个阶段。

但是理想情况下,你会把这个概念和它所涉及的价值类型联系起来,针对一个人。

值类型带有值类型约束的对象-角色模型。图片作者。

上面的对象-角色模型表明,在我们的话语世界中,我们储存了关于人及其生活阶段的信息。生命阶段,儿童成人老年,被捕获为生命阶段值类型的值约束。请注意,在 ORM 图中,没有指示生命阶段的开始和结束状态,也没有指示触发从一个值约束/状态到另一个值约束/状态的转换事件。

如果您想在您的对象-角色模型和相关的状态转换图之间转换,将模型存储在一个模型中(作为元模型/元元模型)有很大的好处…这样图就统一了…

状态转换图和对象-角色模型之间的变形。图片作者。

关于四层架构的更多信息

一个四层的元模型架构可能会让外行人感到困惑,最好从最终的对象向后看。

想象一下,你有一个人对象,我们在 M0 层有这个。如果我们有一个人对象的模型作为对象-角色模型…那么这是上一层(在这个例子中是我们的 M2 层),如果我们有一个对象-角色建模的元模型,那么这又是上一层(在我们的例子中是 M3)。

但是,如果我们考虑 Person 对象的状态转换图,该模型位于 M1 层,其元模型(作为 ORM 图)位于 M2 层,M3 层是对象-角色建模的元模型,现在作为元元模型,如下所示:

在元模型和元模型层使用对象角色建模的四层架构。作者图片

为什么在我们的例子中对象角色建模不需要四层?

在这种情况下,这是因为它不需要它,因为您可以直接从 ORM 的元模型中提取对象-角色模型。

但是如果您将状态转换图作为元模型存储在 ORM 模型中,那么您需要四个层来将该元模型作为元模型存储在 ORM 元模型中。

这是令人头晕的东西,但如果你慢慢地通过它,它是有意义的。如果你只有三层,你能从 ORM 的元模型中提取出来的只有 ORM 模型…你想要的是从 ORM 元模型中提取状态转换图、实体关系图、属性图模式或任何其他语言,就像从 ORM 的元模型中提取出来的模型一样,从元模型中提取出来…四层。

状态转换图很好地补充了对象-角色模型,因为 ORM 中值类型的值约束可能与状态转换中的一组互补状态有一种形态。

在以后的文章中,我将详述某些值类型约束之间的关系,以及它们如何在我们的模型中限制对象的子类型。

对象角色建模和值类型约束中的子类型。图片作者。

这里可以说,影响对象构成的状态转换也可能影响与该对象相关的子类型,如超类型。

感谢阅读;如果时间允许,我会写更多关于对象-角色建模、状态转换图、erd 和 pgs 的内容。

==============================

国家价值观和政策评估

原文:https://towardsdatascience.com/state-values-and-policy-evaluation-ceefdd8c2369?source=collection_archive---------18-----------------------

入门

强化学习简介:第 1 部分

所有图片由作者提供。

很久以前,有一个机器人宝宝在商场里迷路了。使用来自 的策略,多股武装的强盗 他能够在最短的时间内充电,并且现在准备开始寻找他的妈妈。

不幸的是,他不太记得回到她身边的路,所以需要我们的帮助来指引他。我们将使用强化学习来帮助他找到路,并确保他安全返回。

介绍

简单来说,强化学习可以被认为是从试错中学习。与它的 环境 交互的 代理,接收反映它完成某个预定目标的能力的 奖励 。通过评估智能体的行为对其性能的影响,然后修改这些行为以提高性能,强化学习可以逐步转向一个能给出最大回报并解决手头任务的智能体。

因此,强化学习可以被认为由两个不同的部分组成:

  • 预测题 ,其中对智能体的表现进行评估。
  • 控制问题 ,代理用来选择其动作的策略被修改以提高性能。

在这一部分,我们将主要考虑预测问题。然后,一旦我们能够衡量行动与获得的奖励金额之间的关系,我们就可以将注意力转向改进政策,以最大化这些奖励。

标准的强化学习书籍或教学大纲将从完整的强化学习系统的描述和用于描述该系统的方程的推导开始。只有这样,一旦他们掌握了所有的理论,他们才能展示如何将理论应用到实际应用中。

在本文中,我们将采取相反的方法。我们将从实际解决非常简单的问题的非常简单的方法开始,并逐步建立在这些基础上,根据需要添加一些理论,直到我们能够解决完整的强化学习问题。

因此,我们将在与传统课程相同的地方结束,但使用的是一种 自上而下 的方法,而不是标准的自下而上的方法。我们想让机器人宝宝尽快回到妈妈身边,所以没有时间提前学习所有的理论,我们会边学边补充。

内容

在本帖中,我们将讨论以下强化学习主题:

  • 强化学习(RL)的术语
  • 基础 RL 数学
  • 策略评估
    • 迭代策略评估
  • 政策完善
  • 折扣奖励

本文的 Jupyter 笔记本版本可以在 github 上找到。
这包含了用于创建本文中描述的网格级别和 RL 算法的所有代码。

https://github.com/WhatIThinkAbout/BabyRobot/blob/master/Reinforcement_Learning/Part 1 - State Values and Policy Evaluation.ipynb

强化学习的术语

考虑婴儿机器人最初发现自己的这个水平:

在进入关卡时,他有两个可能的选择:他可以向北或向南走。哪一个能让他最快到达出口?

奖励

强化学习( RL )的基本概念是 奖励 的概念:一个单一的数值,用来衡量手头的任务完成得有多好。正是奖励推动了学习,使得解决问题的策略得到优化,从而获得最大的奖励。

在像这样的问题中,机器人宝宝必须找到走出关卡的路,只要到达出口就可以得到奖励,然而这并不能完全描述我们想要达到的目标。我们实际上想尽快到达出口。仅仅因为到达出口而给予奖励不会鼓励这种行为。在找到出口之前,机器人宝宝可以花几天时间在关卡中走来走去,并且会得到和他直接到达那里一样多的奖励。

因此,一个更好的奖励制度应该是鼓励走最短的路线,不鼓励漫无目的地闲逛。实际上,我们想把奖励表达成惩罚,惩罚随着到达出口的步数而增加。

然而,我们仍然希望坚持 RL 的核心理念,即奖励最大化,因此我们引入了负奖励的理念。每走一步,我们给-1 的奖励。这样,花费很长时间找到出口的路线将积累大量的负奖励,而直接到达那里的路线将有少量的负奖励。就获得最大回报而言,直接路线会更好,因为负面影响会更少。

有了我们的奖励系统,每次机器人宝宝从一个方格移动到下一个方格,他将得到-1 的奖励。在 RL 术语中,这些方块中的每一个都代表一个 状态 ,其中状态被定义为我们正在工作的 环境 中的一个独特的、独立的阶段。

因此,在这种情况下,每个状态描述了构成该级别的网格中的一个位置。在一个游戏中,比如国际象棋,状态将描述当前的棋盘位置。在无人驾驶汽车的情况下,状态可以描述诸如道路上的位置、汽车的方向和速度以及其他交通细节等属性。在每一种情况下,国家定义当前的情况。

国家是自足的,因为它独立于任何先前的国家。例如,在国际象棋中,当前棋盘位置给出的状态独立于到该点为止已经进行的所有其他移动。要选择下一步,你不需要知道过去采取了哪些行动。类似地,对于机器人宝宝来说,从网格中的一个方格移动到下一个方格,他不需要知道他之前处于哪个状态。当一个状态独立于先前的状态时,据说它满足 马尔可夫性质

价值

因为我们知道,每次机器人宝宝从一个状态移动到下一个状态时,都会招致-1 的惩罚(奖励),并且我们还可以看到关卡的地图,所以我们可以通过计算每个状态的 来帮助机器人宝宝做出选择,其中该值定义了处于特定状态有多好。

显然,对于这个网格级别,处于靠近出口的状态比处于远离出口的状态要好。在 RL 中,一个状态的价值被定义为当从该状态开始,然后在所有未来状态中选择由计划或 政策 定义的行动时可以获得的预期回报。

让我们将这些信息添加到关卡中。从出口开始(在 R1 中被称为 终端状态 ,在此情节结束,根据定义,奖励值为 0),并绕过关卡,每次移动到一个新的方块都会招致-1 的惩罚,为每个状态给出以下值:

这里有几点需要注意:

  • 任何状态的值都等于从该状态开始时可以累积的预期奖励量。在 RL 语言中期望的报酬总额被称为
    因此,给定机器人宝宝的起始位置,如果他每次从一个状态移动到下一个状态时得到-1 的回报,那么他可以期望得到-4 的回报,如果他沿着最短的路径到达出口的话。
  • 此外,状态的值由在该状态中采取的动作和下一个状态的值的总和给出。例如,从初始状态向南移动,采取行动的奖励为-1,新状态的值为-3,因此初始状态的值等于-4。
  • 通过展望一个状态,我们可以选择朝着一个方向前进,这个方向会把我们带到一个负值较小的状态。所以,从初始位置开始,我们会选择向南到值为-3 的州,而不是向北到值为-5 的州。用于选择下一个动作的策略被称为 策略
    在这种情况下,如果我们选择朝着价值增加的方向前进,我们实际上是在遵循最佳或 最优 政策,正如我们在 多臂强盗 中看到的那样,当我们选择采取能给我们带来最高即时回报的行动时,我们被说成是贪婪地选择了
    。在这种情况下,贪婪策略会产生最优策略。**

我们可以在我们的级别图上显示策略,其中箭头现在指向每个状态中应该采取的操作的方向。请注意,对于这个贪婪的政策,箭头总是指向从当前状态获得最大回报的方向。

所以,从机器人宝宝的起始位置,如果他遵循这个最优策略,他将会以-4 的累计总回报到达出口。

由于我们在从一个状态转移到下一个状态时给予固定的回报,在这种最优策略下,预期回报,因此每个状态的价值,就是退出的步骤数,乘以每一步给予的回报-1。

基础数学

在对初级水平的描述中,婴儿机器人找到了自己,我们已经涵盖了强化学习的大部分基本概念。我们现在可以添加与这些概念相关的简单数学术语,然后在此基础上继续学习。

首先我们说过,当机器人宝宝在状态下采取动作时,他会得到奖励。不出所料,我们使用这些术语的第一个字母来指代其相应的值,小写字母用于指代这些量中每一个的特定值,这就给出了:

  • r =奖励
  • a =动作
  • s =状态

此外,当机器人宝宝采取行动时,他很可能会从当前状态移动到另一个状态。下一个或继承国表示(读作“s 质数”):

  • *****s′*=下一状态

当机器人宝宝对 a 动作时,他从当前状态 s 进入新状态s**并获得奖励 r。**

奖励、状态和行为实际上是随机变量:有获得某种奖励、采取某种特定行动或处于某种状态的概率,这些概率用大写字母表示。

将所有这些术语联系在一起,我们可以得到对状态-动作对的预期回报:

等式 1:状态-动作对的预期报酬

因此,在时间“T36”t-1 处,从某一特定状态“T38”s 开始并采取行动“T40”a 时,下一时间步收到的预期报酬是当前状态和行动的函数。之所以称之为预期报酬,是因为当在特定状态下重复采取特定行动时,所获得的报酬金额并不总是返回恒定值,因此这有效地定义了将获得的平均值。

使用这些术语,我们可以为我们定义的基本属性创建方程:

返回'Gₜ': 奖励累计超过一集的总金额,起始于时间‘t’。 在我们的情况下发作指进入和离开一个级别之间发生的所有时间步长。(在长时间运行或连续的任务中,事情显然会变得有点复杂,但我们稍后会回到这个问题上来)。

等式 2:以回报总和表示的回报。

所以,从时间' t '开始,回报只是未来回报的总和。

从状态 开始,'Sₜ'、 返回'gₜ'一世** 年代从那个状态得到的总报酬,直到插曲终止。**

价值: 一个状态的价值只是衡量那个状态有多好。这可以用未来回报的数量来表示,或者换句话说,如果你以那种状态开始,你可能得到的回报。显然,无论你从哪个状态开始,你将获得的奖励将取决于你选择的行动,而这些行动由 策略 决定,通常用符号“【π】”表示。

所以在策略π下状态' s '的值就是预期收益:

等式 3:策略“ ”下状态“s”的值π ”。

因为,在我们的简单例子中,我们总是因为采取行动而得到相同的-1 的回报,所以一个状态的值就是直接的回报加上下一个状态的值:

等式 4:在确定性策略'【π】'下的状态值。

例如,级别的开始状态的值是-4。如果从这种状态开始遵循最优策略,当机器人宝宝到达出口时,他将已经累积了-4 的总奖励。类似地,如果他选择从初始状态向南走一步,他会得到-1 的奖励,下一个状态的值是-3,所以初始状态的值也是-4。

注意状态值的计算是如何分成两部分的;采取行动所获得的直接回报,以及该行动带给你的状态的价值。这种将问题分解为子问题的技术被称为。利用这一点,已经计算出的状态值可以被重用来计算其动作导致的其他状态的值。

这大大简化了问题,因为在计算从一个状态开始到一集结束之间获得的总回报时,你不需要计算出在每个状态下将给出的奖励。相反,你只需要向前看一步。

政策评价

机器人宝宝的妈妈告诉他永远不要相信陌生人,所以他对遵守我们的政策有点紧张。如果我们对他撒谎并且这不是最优策略会怎么样?

所以,他没有使用我们的政策,而是决定抛硬币来决定走哪条路。每次他进入一个新的状态,他都会抛硬币。如果是正面,他会往前走,反面,他会往后走。因此,现在每个动作都有 50%的机会被选中。

在这种新政策下,每个州的价值会发生什么变化?我们该如何计算它?

  • 首先,一个国家的价值仍然是衡量这个国家有多好的标准。然而,由于机器人宝宝不再直接走向出口,他将积累的总奖励将减少。换句话说,因为他可能会访问更多的州,或者多次访问同一个州,并且从一个州移动到另一个州的奖励仍然是-1,总奖励将变得更负。
  • 此外,一个状态的值仍然表示从该状态的预期回报。但由于宝贝机器人现在遵循的是随机策略(RL 中称为 a 随机策略 ),基于抛硬币,从任意状态到退出的步数可能会有所不同。因此,一个州的价值现在代表了在该州开始时可以获得的平均回报,或预期**
  • 因为状态的值是从该状态可获得的预期回报,所以它的值可用于帮助计算前一状态的值。这避免了需要知道在该集期间将累积的所有奖励。
  • 因此,任何行为的价值仅仅是采取这一行为的回报加上下一个状态的价值。该动作对当前状态值的贡献是通过将该动作的值乘以采取该动作的概率来获得的。

这可以用下面的等式来概括:

等式 5:随机策略''下的状态值。

与等式 4 一样,对于确定性策略,任何动作的值由采取该动作' a '获得的奖励' r '加上该动作导致的下一状态's′'的值给出。然而,由于在随机策略下可以有一个以上的行动,行动的回报乘以采取行动的概率: π(a|s)表示从状态“在策略下”【π】采取行动“a”的概率。****

然后对状态的所有动作将这些值相加,这给出了状态' s '的 预期 奖励值。实际上,总和与采取行动的概率的组合给出了行动回报的平均值。

在婴儿机器人的新政策下,2 个动作(向前或向后)中的每一个都有 0.5 的概率被采取,采取任何动作的奖励仍然是-1。因此,任何状态的值都将是:

等式 6:有两个同样可能的行动,回报都是-1 的状态值。

其中 sᶠ 是选择向前动作时移动到的状态,而 sᵇ 是采取向后动作时的下一个状态。

迭代策略评估

对于这个简单的层次,计算最优策略是很容易的。我们简单地从出口处开始,然后往回走,每移动到下一个州就增加一个-1 的奖励。但是,当移动到下一个状态的机会是随机的时,我们如何着手计算状态值呢?
(在 RL 的语言中,移动到下一个状态的机会被称为 状态转移概率 )。

我们可以通过采用类似的方法来计算最优策略值来做到这一点,只是现在,我们不能通过一次扫描所有州来找到每个州的值,而是需要进行多次扫描。这些都会给我们一个国家真实价值的更好的估计。

最初,由于我们不知道任何状态的值,让我们假设它们都不返回奖励,所以我们将所有初始值设置为零。根据定义,退出的回报,即最终状态,也是零,因为这是一集结束的地方。

迭代政策评估:初始状态值。

为了开始迭代过程,我们可以从任何状态开始,但是,为了简单起见,让我们从机器人宝宝的当前位置开始,关卡的入口。

机器人宝宝扔硬币。正面他去北方,反面他去南方。所以每个行动的概率是 0.5,每个行动的回报是-1,两个行动的下一个状态的值都是 0。因此,使用上面的等式 6,当前状态的电流值是:

为了简单起见,我们将在扫描开始时取每个状态的初始值,而不是它的更新值,以避免一些状态已经更新而一些状态没有更新的情况(尽管这样做是完全合理的,并且通常可以导致状态值更快地收敛;使用更新的值被称为'就地更新)。**

因此,在这次扫描中,保持每个下一状态的值为零,我们可以对其余状态重复上述过程。这导致在第一遍结束时所有状态的值为-1。

在第一遍结束时,每个状态的值如下所示:

迭代策略评估:在第一次扫描结束时陈述值。

一旦计算了每个状态的值,就可以重复该过程,使用新计算的状态值来计算下一次迭代的状态值。在最初的 10 次迭代中,计算状态值的过程如下所示(暂时忽略蓝色箭头,我们很快就会看到)。

迭代策略评估:迭代 0 到 9。

如果这个过程重复足够长的时间,状态值最终停止增加,并且被认为已经达到收敛。理论上,收敛只有在极限时才真正达到,或者换句话说,当时间步数等于无穷大时。显然这是相当不切实际的,因此我们将收敛定义为在一次迭代和下一次迭代的状态值之间的最大差值小于某个阈值时发生。在我们的实验中,我们使用阈值 1e-3 (=0.001),因此,对于该策略,状态值收敛需要 206 次迭代。****

迭代策略评估:经过 206 次迭代后达到的收敛状态值。

现在可以看到,在这个通过抛硬币来选择下一个状态的策略下,每个状态的值都比直接退出的最优策略下的值负很多。然而,这些值仍然表示从任何状态到退出的预期步数,除了现在机器人宝宝遵循随机轨迹,这将导致更多的状态被访问。这是他从起点到终点的一次短途旅行,如下所示:

随机政策下的样本轨迹。

迭代策略评估代码

用于评估策略的代码如下所示。这重复计算每个状态的状态值,直到达到收敛。

(这段代码摘自本文的完整笔记本,可以在 github 上找到)

该代码由实现迭代策略评估例程的三个主要部分组成:

  • 在等式 6 中,我们通过将每个动作获得的值乘以 0.5 来对动作进行平均,因为对于这个级别,我们只有 2 个可能的动作。在代码中,这变得更加一般化,在可能的操作数上乘以 1。
  • 进行一次扫描,计算所有状态的值(standard _ sweep函数)
    对所有状态进行迭代,计算每个状态的值。
    **
  • 执行多次状态扫描直到收敛 最初每个状态的值都被设置为零。然后,在每次迭代中,使用当前值计算所有状态的新值。
    这一直持续到状态值的最大变化低于预定义的阈值。

政策改进

不出所料,根据抛硬币来决定下一步该怎么走并不是一个很好的策略。如上所示,在这种策略下,到达出口需要更长的时间。此外,每个状态的值比最优策略下的值低得多。然而,尽管状态值要差得多,但就可以获得的回报而言,它们仍然提供了一个重要的信息:每个状态的相对良好度。

回头看看最终的、收敛的状态值,可以看到从起始方块可以获得的期望回报是-32。所以,从这一点到出口平均要走 32 步。类似地,对于紧挨着起始位置北面的方块,预期回报是-35,南面是-27。因此,显而易见,要以最短的步数到达出口,最好从起点广场向南走。

通过重复这种一步前瞻,并贪婪地考虑下一个状态的值,我们可以修改随机抛硬币策略,以创建一个朝着最大回报方向移动的策略。通过这种方式,我们可以改进政策,产生一个给予更多奖励的政策。

事实上,在这一级别的政策评估的单次迭代之后,对状态值的贪婪行为给了我们最优的政策。这是由蓝色箭头显示的,可以看到它指向最大奖励的方向,并直接从入口到出口的水平。

另一个有趣的观察是,当贪婪地对计算的状态值采取行动时,在策略可以被改进之前,可能没有必要等待这些值收敛。再次查看这个级别的前几次迭代(方便地复制到这里,以避免您必须滚动!):

迭代策略评估:迭代 0 到 9。

尽管在策略评估期间,状态值完全收敛需要 206 次迭代,但是可以看出,到第 5 次迭代时,贪婪选择已经找到了最优策略。事实上,对于我们真正感兴趣的开始方块,最优策略已经在第四次迭代中找到了。因此,就改进策略而言,所有未来的迭代都是多余的。当我们寻找更有效的方法来寻找最佳策略时,我们将利用这一观察结果。

折扣奖励

到目前为止,我们已经评估了最优策略的状态值,我们能够很容易地确定这个非常简单的水平,以及随机策略,其中所有的行动都是以随机概率选择的。在这两种情况下,都存在一系列的行动,最终导致从开始的水平到退出。但是,如果情况不是这样,如果策略的任何操作都不会导致最终状态,会发生什么呢?

例如,考虑下面显示的确定性策略:

初始确定性策略

在该策略中,为每个状态定义一个动作,以指定应该从该状态移动的方向。关于这个策略需要注意的重要一点是,没有一个动作会导致退出,因此,这一集将永远不会结束。显然,这将在评估政策时造成问题。

状态值表示从一个状态可以获得的总奖励。正如我们已经看到的,这是作为将获得的所有奖励的总和来计算的,从该州开始,然后遵循该政策。因为我们的初始策略永远不会到达终止状态,所以在策略评估的每次迭代中,这个和只会继续增长。

为了防止这种情况发生,我们引入了 折扣奖励的概念。 现在,回报不再是从一个状态到一集结束累积的所有奖励的简单总和,我们逐渐减少奖励的贡献。一项奖励越往后,在计算国家的回报价值时,它所占的权重就越小。

计算回报的公式现在变为:

等式 7:贴现回报

在这个新的返回值贴现公式中,'γ'(γ)是贴现因子,其中 0 ≤ γ ≤ 1。因此,每个时间步长的回报都乘以一个递增的幂' γ '。当折扣因子的值小于 1 时,这将逐渐减少未来时间步长的奖励值,直到最终它们对整体回报的贡献实际上为零。**

例如,值 0.9 通常用作折扣系数。这样我们就可以计算初始状态的返回值,如下所示:

显然,应用贴现因子会逐渐降低未来的回报值,用不了多久,它们就会下降到接近于零。

然而,正如我们已经看到的,通过考虑所有未来状态的回报来计算一个状态的价值是不切实际的。相反,我们使用动态规划将问题简化为一个只使用下一个状态的直接回报和价值的问题。下一个状态的值表示将从下一个状态获得的回报,因此我们可以修改等式 7,将贴现因子应用于下一个状态的值:

等式 8:状态“在政策下”的值π‘具有贴现的未来奖励。

换句话说:当遵循“π”政策时,一个州的价值等于该州所有行动的总和,即采取每个行动的概率,乘以该行动的直接回报,再加上采取行动后下一个州的贴现值。**

使用贴现状态值函数,将贴现因子设置为 0.9,我们现在可以计算确定性策略的值:

政策迭代:贴现状态值。

有了折扣奖励,每个状态的值不再持续下降,而是在 67 次迭代中收敛到我们的阈值。在这种情况下,所有状态(除了退出)的值都是-10。没有一个状态比另一个状态更好的原因是,在这种政策下,永远不可能达到最终状态,因此所有的状态都一样糟糕。在未来的一部分,我们将看看如何使用这些状态值来改善政策,给一个确实允许婴儿机器人逃离这一关。

关于新的贴现状态值,需要注意的一个要点如下:

  • 状态值不再代表预期的退出步骤数。取而代之的是,它们现在显示了在这一政策下,每个州的预期贴现未来回报。

摘要

毕竟在迷宫里跑来跑去,机器人宝宝很累了(我相信你也是!),所以我们就在这里休息一下。我们已经成功地让机器人宝宝通过了非常简单的初始阶段。在这个过程中,我们几乎涵盖了强化学习的所有主要基础:

  • 强化学习使用奖励的概念来推动学习。对于构成问题环境的状态,可以从奖励中计算出一个值来表示每个状态有多好。然后,通过选择最大化回报的行动,就有可能找到可以用来解决手头问题的最佳策略。
  • 每个州的值代表如果你从该州开始,然后遵循当前政策直到该集结束时可以获得的回报。折扣奖励可以将状态值集中在不久的将来获得的奖励上。此外,它们可以防止如果不能保证情节结束时可能发生的问题。
  • 使用动态规划可以大大简化状态值的计算。该问题被分解为即时奖励和下一个状态的值,而不是必须知道来自特定状态的所有未来奖励。
  • 使用动态编程,我们能够执行策略评估,其中我们计算当前策略下所有状态的值。
  • 一旦策略评估完成,状态值就可以用于通过贪婪地选择这些值来改进策略。

下一步是什么?

尽管我们已经覆盖了很多领域,但我们仍然缺少强化学习的一些核心概念。特别是 马尔科夫决策过程贝尔曼方程 。简而言之,它们分别是用于模拟强化学习问题的数学框架和用于计算状态和动作值的方程组。如您所见,我们已经使用了一些等式来计算状态和动作值。这些实际上是完整的贝尔曼方程的部分形式。我们将在下一篇 的 中全面描述这两个主题。

此外,我们只真正研究了 预测问题 ,其中我们评估了给定策略的价值函数。虽然我们能够根据这些计算值贪婪地行动,从而帮助机器人宝宝在简单的网格级别中找到自己的路,但我们需要对更复杂的问题进行扩展。因此,我们需要检查 控制问题 ,其中我们基于策略评估找到最优策略。

此外,机器人宝宝还没有真正探索他发现自己的任何水平。所有关于等级的信息,比如奖励和状态转移概率,都已经给定了,这被用来推导每个等级的最优策略。当前面所有的信息都被给出时,它被称为基于 模型的 系统。更现实的情况是当这些值不可用时,需要进行一些探索来解决问题。不出所料,这种性质的问题被称为 无模型 问题,我们将在未来的帖子中探讨这些问题。

*** ***

脚注:

自顶向下学习 :这种学习方法可能是杰瑞米·霍华德在他出色的 Fast.ai 课程中最著名的一次使用(至少在机器学习领域)。在学术研究中,它被证明有助于更好地理解一门学科的整体概念。

参考

关于本文涵盖的所有内容的完整理论分析,请查看强化学习的圣经:“ 强化学习:简介 ”,萨顿&巴尔托(2018)

对于婴儿机器人的多臂土匪指南,从这里开始:

*** ***

应用程序安全测试中的静态与动态

原文:https://towardsdatascience.com/static-vs-dynamic-in-application-security-testing-36687a0c55c5?source=collection_archive---------39-----------------------

探索 SAST 和 DAST 的主要区别

Alex 在 Unsplash 上的原始照片。它后来被修改,以包括文本 SAST 和 DAST。

我以前写过一篇文章,讨论静态应用程序安全性测试的利弊。我之前的一篇文章根据 OWASP(一个旨在教育人们了解安全漏洞的非营利组织)的报告,介绍了全球开发者面临的十大最关键的 web 应用和 API 安全风险。

在本教程中,我们将探索和比较静态应用程序安全测试(SAST)和动态应用程序安全测试(DAST)之间的差异。

静态应用程序安全测试(SAST)是减少应用程序安全漏洞的方法之一。另一种方法是动态应用程序安全测试(DAST),它可以保护您的应用程序。让我们来看看这两种方法的区别。

静态应用程序安全性测试

白盒测试

SAST 是一种白盒安全测试,可以完全访问底层源代码和二进制代码。它将通过由内向外的方法测试你的程序。专门的 SAST 软件,如 GitLab 、 Klockwork 或 AppThreat 将在编码过程中或在您将代码提交到管道后自动扫描您的源代码。

例如,对于 Klockwork 的用户来说,一旦您在项目和 Klockwork 桌面之间建立了链接,它就允许您使用您选择的任何 IDE 正常编写程序,只要它在后台是打开的。每次保存文件时,Klockwork Destop 都会自动更新代码,并当场执行扫描。如果检测到任何安全问题,它会在用户界面上显示出来。

作者图片

早期发现

SAST 通常在系统开发生命周期的早期进行,通常是在开发阶段期间或之后。这允许您在进入测试或质量保证阶段之前识别任何形式的安全漏洞。

当问题在早期被发现时,解决问题要容易得多。此外,大多数 SAST 执行会标记有漏洞的代码行。这非常有用,可以作为开发人员修复漏洞时的参考。维护和开发项目的成本也更低。

假阳性

与 DAST 相比,SAST 方法容易出现大量的假阳性。因此,可能会出现这样一种情况,开发人员浪费了宝贵的时间和资源来修复他们系统中想象的问题。如果出现成百上千的误报,从长远来看,这样的失败代价会很高。

语言相关的

在分析底层源代码和二进制代码方面,SAST 是语言相关的。大多数 SAST 工具只专注于几种计算机语言。您将无法为项目中使用的每种编程语言找到一种通用的 SAST 工具。因此,用不同的计算机语言来扩展和维护一个项目将是一项巨大的任务。

不包括运行时漏洞

SAST 无法检测任何形式的运行时漏洞,因为它只扫描静态代码和二进制文件。如果您错误地配置了系统,SAST 工具将无法识别运行时问题,从而导致开发人员产生错误的安全感。

动态应用安全测试

黑盒测试

另一方面,DAST 被称为动态的,因为它不能访问底层的静态代码或二进制代码。测试由外向内进行。你可以把它想象成一个黑客试图测试你系统的安全漏洞。

与 SAST 不同,它分析正在运行的应用程序,而不需要任何用于开发系统的技术知识。一旦它检测到任何潜在的漏洞和风险,它就会为开发人员记录这些问题。

下面的例子说明了 Gitlab 如何对源分支和目标分支之间的漏洞进行比较。该信息将显示在每个合并请求上。

图片来自 Gitlab 关于 DAST 的文档

后期检测

DAST 通常在系统开发生命周期的末尾进行。它通常发生在测试阶段,就在用户验收测试之前。在将应用程序部署给用户之前,它是保护应用程序的最后一道关口。

大多数时候,在 DAST 下检测到的问题不会被立即修复,除非它们被认为是关键的。这主要是由于在 UAT 或部署阶段之前缺少时间。因此,大多数问题都被推到下一个开发周期。

独立于语言

由于测试是直接在运行的应用程序上进行的,DAST 不依赖于任何计算机语言。因为测试独立于系统开发中使用的编程语言,所以扩展和维护测试要容易得多。

然而,大多数 DAST 工具只支持特定类型的应用程序,如 web 应用程序或 web 服务。如果你的项目由 web 应用程序、桌面应用程序和移动应用程序组成,你可能需要一些不同的 DAST 工具。

例如,让我们看看 Gitlab 制作的 DAST 报告。它强调了在 web 应用程序上执行的反 CSRF 令牌分析。

图片来自 Gitlab 关于 DAST 的文档

在幕后,它使用一个名为 OWASP ZAP (Zap 攻击代理)的开源 web 应用程序扫描器来扫描您正在运行的应用程序。

覆盖运行时漏洞

DAST 相对于 SAST 的一个主要优势是它能够发现运行时漏洞。这包括配置问题、认证问题和系统内存问题。您将能够从用户的角度识别更多的问题。

结论

让我们回顾一下今天所学的内容。

我们首先简要解释了 OWASP 报告的 10 大安全漏洞。

然后,我们继续探索静态应用程序安全测试(SAST)和动态应用程序安全测试(DAST)之间的主要区别。我们了解到 SAST 是一种白盒测试,而 DAST 是一种黑盒测试方法。

虽然 SAST 通常在系统开发生命周期的早期完成,并且依赖于语言,但是 DAST 不依赖于任何计算机语言,并且通常在周期的末尾进行。

感谢你阅读这篇文章。希望在下一篇文章中再见到你!

参考

  1. 维基百科—静态应用安全测试
  2. 维基百科—动态应用安全测试
  3. 什么是 SAST?概述+ SAST 工具

功率 BI 中的静态与动态排序

原文:https://towardsdatascience.com/static-vs-dynamic-ranking-in-power-bi-ef318fbb61d7?source=collection_archive---------7-----------------------

了解如何以多种不同的方式处理最常见的业务请求之一!

图像来源

我最畅销的产品是什么?哪些客户在我的服务上花费最多?哪些商店销售了我们奢侈品牌的最多产品?回答这些问题以及许多类似的问题是创建 Power BI 报告时最常见的要求之一。

为了能够根据上述问题的答案做出正确的业务决策,我们需要确定各自的价值,并相应地对它们进行排序。

DAX 为处理排序提供了两个不同的函数——RANKXTOPN 。您可以直观地得出结论, TOPN 函数可以帮助您识别,比方说,去年最畅销的前 5 个产品,或者购买最多商品的前 10 个客户。然而, TOPN 不在本文讨论范围之内,因为我们将重点讨论 RANKX 函数…

了解静态排名

静态排名给一个特定的值分配一个排名,这个排名不会改变——不管你是否过滤结果!让我们看看这在一个真实的例子中是怎样的,使用(通常)一个样本 Contoso 数据库。

首先,让我们在 FactOnlineSales 表中创建一个显式度量值来计算销售额:

Sales Amt = SUM(FactOnlineSales[SalesAmount])

现在,为了查看哪些产品给我们带来了最多的利润,我们需要在 DimProduct 表中创建一个新列。该列将用于根据销售额为特定产品分配等级:

Rank Product Sales Amt = RANKX(DimProduct,[Sales Amt])

作者图片

正如您所注意到的,Power BI 根据销售额对我们的行进行了排名。

让我们利用这一计算方法,检查哪些产品的销售额最高:

作者图片

哦,我喜欢那些液晶显示器,它们真的带来了很多钱!这看起来真的很好,因为我可以很快发现哪些产品表现最好。

但是,如果我很好奇想知道具体品牌的表现呢?让我们添加一个切片器,只查看 Contoso 品牌的数据:

作者图片

哎呀,我的数字 1、2、4、5……在哪里?我觉得这有点奇怪,不是吗?如果你在问自己:到底发生了什么?回到文章开头,我明确地说过:排名不会改变——不管你是否过滤结果!所以,长焦转换镜头永远是 3 号(除非底层数据改变),滤镜应用与否!

因此,让我们调整我们的计算,在特定品牌中设置一个适当的计算:

Rank Product Brand = RANKX(ALLEXCEPT(DimProduct,DimProduct[BrandName]),[Sales Amt])

这里, ALLEXCEPT 函数将删除 DimProduct 表上的过滤器,只保留 BrandName 列的过滤器,现在我们将得到不同的结果:

作者图片

这很好,但是这个解决方案远非完美。想象一下,为每一个切片器创建一个单独的计算列——这将是一项非常麻烦和乏味的工作!更不用说它会扩大您的数据模型,因为每次数据刷新都会对所有计算列进行计算和具体化,并且它们会占用一定的内存!

了解动态排名

每当你在 Power BI 中听到术语 动态 时,你应该假设你将不得不处理措施,而不是列。也就是说,如果您希望在 Power BI 报告中应用动态排名,这意味着排名值将根据当前过滤器上下文动态计算和分配。

因此,让我们创建我们的度量,它将准确地执行我们动态计算排名所需的操作:

Rank Product Measure = IF(ISINSCOPE(DimProduct[ProductDescription]),IF(NOT ISBLANK([Sales Amt]),RANKX(ALLSELECTED(DimProduct),[Sales Amt])))

现在,如果我们看一下我们的表格,您会看到将根据用户与报告的交互来分配等级:

作者图片

您可以在这个短视频中看到,我的度量将根据用户的选择进行动态调整:如果在切片器中没有选择任何值,它将与我们的第一个计算列完全一样。另一方面,一旦我在切片器中选择了一个特定的品牌,该度量将显示与我们的第二个计算列相同的结果!

最后,让我们看看如果我们在切片器中选择多个品牌会发生什么:

作者图片

由于我已经选择了 Contoso 和 Litware,我们的度量将在当前的过滤器上下文中进行评估,并根据它分配等级——Contoso 和 Litware 现在代表我们要应用我们的度量的子集!

结论

处理与排名相关的请求在大多数业务场景中非常常见,为了应用正确的计算,理解静态和动态排名之间的差异是绝对必要的。

这个要求没有唯一正确的解决方案——你应该和你的用户谈谈,看看他们认为分配排名的标准是什么。在任何情况下,如果可能的话,您应该倾向于应用动态排名,因为这不仅会确保更大的灵活性,而且还会减少数据模型的大小和维护的麻烦。

感谢阅读!

成为会员,阅读 Medium 上的每一个故事!

订阅此处获取更多有见地的数据文章!

时间序列数据的平稳性假设

原文:https://towardsdatascience.com/stationarity-assumption-in-time-series-data-67ec93d0f2f?source=collection_archive---------10-----------------------

平稳性假设重要吗,我们该怎么做?

萨曼莎·加德斯在 Unsplash 上的照片

当我进行时间序列分析时,平稳性总是我遇到的术语。当我遇到这个学期时,我脑子里有许多问题。什么被认为是平稳数据?我们需要关心平稳性假设吗?如果我们需要,我们应该做什么?

什么是平稳数据?

平稳数据是指均值和方差不随时间变化的时间序列数据。如果从数据中观察到强趋势或季节性,则数据被认为是非平稳的。

图片来自预测:原理与实践

如上图所示从这里,只有(b)和(g)被认为是静止的。

观察到的明确趋势 : a、c、e、f、I

观察到明显的季节性 : d、h、I

(g)乍一看显示了一些周期性运动,但实际上它具有不同的循环时间周期,因此仍然被认为是静止的。如何检测时间序列数据中的平稳性的更详细的方法将在后面的文章中介绍。

平稳性对时间序列分析重要吗?

在大多数情况下,这很重要。这是因为许多统计分析或模型是建立在均值和方差随时间保持一致的假设上

当我们用平稳模型来拟合我们想要分析的时间序列数据时,我们应该检测数据的平稳性,并从数据中去除趋势/季节性效应。

许多当前的时间序列模型,如 ARIMA,可以选择包括将原始数据转换成平稳数据的步骤,或者使我们的生活更加方便。然而,如果我们能够理解数据的平稳性,以便我们能够为模型提供更好的输入,这仍然是有益的。

我们使用的数据:

正如我在我的另一篇文章这里中介绍的,我使用的是来自苹果健康的我自己的计步数据。可以参考这个链接下载自己的苹果健康数据,开始一些很酷的分析。

我们如何知道数据是否是平稳的?

方法一:看图表

最简单方便的方法就是直接看图表,看是否有明显的趋势或者观察到的季节性。

我自己的步数可视化

正如你从我自己的步数图表中看到的,从 2020 年开始有一个明显的向下移动的趋势,这使得数据不是那么稳定。有时很难区分,你可以选择使用季节性分解包将主图表分解成趋势和季节性

import statsmodels.api as sm
extract=train.set_index('date')
from pylab import rcParams
rcParams['figure.figsize'] = 18, 8
decomposition = sm.tsa.seasonal_decompose(extract, model='additive')
fig = decomposition.plot()
plt.show()

季节性分解图

这将原始时间序列图分解为趋势图、季节图和残差图。从这张图上你可以看到更清晰的趋势变化模式。

方法 2:统计方法——扩展的 Dickey-Fuller 检验

如果您仍然不能从直接图或分解图中确定平稳性。扩展的 Dickey-Fuller 检验或 ADF 检验是确定时间序列数据是否平稳的最流行的统计方法之一。

增强的迪基-富勒测试是迪基-富勒测试的增强版本。它测试预测 Y 值与时间滞后项和滞后差分项之间的相关性,如下所示。

AFD 测试公式

在 Python 中,我们可以直接使用 statsmodels 的 adfuller 包来查看数据在不同的显著性水平上是否平稳。让我们对步数数据进行 ADF 测试,看看它是否是稳定的。

from statsmodels.tsa.stattools import adfuller
result = adfuller(weekly.value)
print('ADF Statistic: %f' % result[0])
print('p-value: %f' % result[1])
print('Critical Values:')
for key, value in result[4].items():print('\t%s: %.3f' % (key, value))

ADF 结果

从上面的结果可以看出,p 值略高于 0.05,这意味着我的步数数据在 1%或 5%的显著性水平上是非平稳的。您还可以将 ADF 统计数据与不同显著性水平的临界值进行比较。与临界值相比,ADF 统计值越负,数据就越稳定

如果我的数据不稳定,我该怎么办?

最常见的解决方案是差分。这意味着您对后续时间序列值之间的差异建模,而不是对值本身建模。这是因为差异通常可以消除趋势或季节性的影响。另外,如果一阶差分(扣一次)不行,可以试试二阶差分(扣两次)。

原始数据: Y0,Y1,Y2

一阶差分: (Y1-Y0),(Y2-Y1)

二阶差分: ((Y2-Y1)-(Y1-Y0))

让我们将差分方法应用于我们的步数数据,看看会发生什么。

#calculate first order differencing value
first_order_diff=weekly.value.diff()#remove na value due to differencing
first_order_diff=first_order_diff.dropna()#ADF test
result = adfuller(first_order_diff)
print('ADF Statistic: %f' % result[0])
print('p-value: %f' % result[1])
print('Critical Values:')
for key, value in result[4].items():print('\t%s: %.3f' % (key, value))

差异后的 ADF 测试结果

正如我们从上面的结果中看到的,在一阶差分后,p 值已经下降到 0.01 以下,数据已经转换为严格平稳。

我们如何在 ARIMA 模型中应用平稳性?

“I”在 ARIMA 的意思是“集成的”,它指的是获得稳定数据所需的差分数。当我们在 Python 中使用 ARIMA 模型包时,它也与参数 d(差分顺序)相关。

在我的步数预测示例中,我们已经分析并知道原始数据不是严格静态的,一阶差分可以将数据转换为静态时间序列数据集。我们可以调整 auto_arima 包的启动参数如下:

最后

建议每次开始时间序列分析时检查平稳性假设。它可以帮助您更好地理解数据,并为模型选择更好的参数值范围。

如果您有兴趣了解使用时间序列方法进行端到端步骤计数预测的更多信息,请参考以下链接:

参考:

https://otexts.com/fpp2/stationarity.html(平稳性检查和差分)

https://www . stats models . org/stable/generated/stats models . TSA . stat tools . ad fuller . html。(Python 中的 ADF 测试包)

感谢阅读!如果您有任何反馈或任何其他您想了解的话题,请在评论框中填写!

矩阵形式的主成分分析和 OLS

原文:https://towardsdatascience.com/statistical-analysis-1a291f4bff3a?source=collection_archive---------19-----------------------

使用 R 的线性代数运算:主成分分析和普通最小二乘法

弗拉多·帕诺维奇在 Unsplash 上拍摄的照片

介绍

主成分分析(PCA)和普通最小二乘法(OLS)是两种重要的统计方法。他们在一起表演时甚至更好。我们将使用 R 中的矩阵运算来探索这些方法,并介绍一种基本的主成分回归(PCR)技术。

数据生成

我们将从高斯分布中生成一个简单的数据集,该数据集包含四个高度相关的探索性变量,以及一个响应变量,该响应变量将是它们与添加的随机噪声的线性组合。

> library(‘MASS’)> mu=rep(3,4)
> sigma=matrix(.9, nrow=4, ncol=4) + diag(4)*0.1> set.seed(2021)
> data <- as.data.frame(mvrnorm(20, mu = mu, Sigma = sigma), 
+ empirical = T)> y <- apply(data, 1, sum)+rnorm(20, 1, 1)

我们可以观察相关矩阵,并确认探索变量是高度相关的。在这种情况下,回归系数可能会有偏差。

> cor(data)

主成分分析

这种统计方法有助于我们处理多重共线性和高维度。使用 R,我们可能只需要 4 个步骤就可以获得 PC。

首先,我们需要对数据进行缩放。在我们的例子中,我们知道数据具有相同的数量级;因此,我们只将其居中。

data.scaled <-  scale(data, scale=F)

第二个,我们需要计算协方差矩阵:

data.cov <- cov(data.scaled)

第三个,我们需要计算特征值来获得特征向量。在 R 中,函数 eigen()返回两个结果。特征值是标度参数;特征向量是旋转参数。

data.eigen <- eigen(data.cov)

最后,我们需要执行数据和特征向量的矩阵乘法。结果将产生主成分:这些是新轴上原始数据的坐标。

> data.custom.pca <- data.scaled%*%(data.eigen$vectors)

这些分量是正交的,我们可以通过观察相关矩阵来证实这一点:

> round(cor(data.custom.pca),5)

我们还可以计算出每个成分所占的解释变量的比例。第一个成分获得了 93.88%的变异;第一+第二分量捕获 97.06%,以此类推。

> cumsum(data.eigen$values)/sum(data.eigen$values)

现在,让我们检查内置函数 prcomp()。

该功能自动化了我们之前执行的四步方法。从下面的结果中,我们可以看到,这些组件与我们刚刚获得的组件是等效的:

> data.pca <- prcomp(data)

一些组件具有相反的符号,但是如果我们检查相应的特征向量(在这个包中称为旋转),我们会注意到它们的符号也是相反的,当我们将它们相乘以获得原始数据时,一切都会很好。

> data.pca$rotation

特征值可以通过平方该包的标准偏差来获得。

> data.pca$sdev^2

最后,summary 函数将返回组件的重要性和每个组件捕获的方差的比例。我们可以观察到结果与我们以前的估计是一致的。

> summary(data.pca)

普通最小二乘法

在没有进一步细节的情况下,回归方程的系数可以使用以下公式获得:

https://en.wikipedia.org/wiki/Ordinary_least_squares

要计算它,我们需要知道 R 中的三个运算:如何求逆矩阵,如何转置矩阵,如何乘矩阵:

# Matrix multiplication:  %*% 
# Transpose matrix:       t(x)
# Invert matrix:          solve(x)

但在我们最终确定我们的方程之前,我们需要记住,这个公式假设 x0 = 1,因此我们要在数据中添加一个单位列。然后,我们以矩阵形式运行该方程,并获得系数:

> x <- as.matrix(cbind(1,data))> solve(t(x)%*%x) %*% t(x) %*% y

我们可以看到,R 中的这个方程直接重复了我们在题目开始时提到的方程。让我们将它与 R 中的内置函数进行比较:

> model <- lm(y~x[,2:5])

这两种方法的系数是相同的,因为它本质上是相同的方法。

现在我们知道了 PCA 和 OLS 方法是如何在简单数据集上执行的。这就完成了我们下一个主题的先决条件。

主成分回归

我们将使用我们之前获得的 PC,因为它们对于某些成分具有不同的符号,我们将显示,当我们将数据转换为原始形式时,系数将是相等的。

# Adding components from both methods to the data setdata.new <-  cbind(data, data.custom.pca, data.pca$x, y)
colnames(data.new)[5:8] <- c('cust1', 'cust2', 'cust3', 'cust4')

出于演示目的,将使用所有四种成分来估计模型,即使通常在 PCA 之后我们减少预测因子的数量:

model.cust <- lm(y~cust1+cust2+cust3+cust4, data.new)model.pca <- lm(y~PC1+PC2+PC3+PC4, data.new)

现在我们需要将分量旋转的系数转换到原始空间。这可以通过将它们乘以相应的特征向量来实现:

beta.pca <- as.matrix(model.pca$coefficients[2:5])
beta.original.pca <- as.matrix(data.pca$rotation) %*% beta.pcabeta.cust.pca <- as.matrix(model.cust$coefficients[2:5])
beta.original.cust <- as.matrix(data.custom.eigenvector) %*% beta.cust.pca

这证实了当我们将点变换到原始系统时,PCs 的相反符号将由特征向量的符号补偿。

现在,让我们检查来自库(pls)的内置函数 pcr()

函数 pcr()有几个参数需要调整,但是我们将构造最基本的版本,并获得系数。

library(pls)fit <- pcr(y ~.,  data = as.data.frame(data.scaled), center=F)

该函数计算所有 PC、特征值等,以及不同数量组件的系数。上面我们对所有四个分量进行了切片,我们可以看到,它们与我们先前得到的没有一点不同。

但是需要多少组件呢?我们可以绘制估计的 PCs 或 PCR 模型,以在图上搜索“肘”,或者尝试预测测试集/交叉验证,或者使用一些更正式的方法。

plot(fit, "validation", val.type = "MSEP")# orplot(data.pca, type='l')

从图中,我们可以看到,一台电脑足以捕捉数据的变化。从前面的观察中我们还知道,第一个分量捕获了约 93.88%的变化。因此,如果我们想对真实数据集进行回归并获得相似的结果,我们将只使用第一个分量来避免数据集中的多重共线性。

结论

在本文中,我们探索了一种计算 PCs 和 OLS 系数的矩阵形式方法,将这种自定义方法与 R 中的内置函数进行了比较,并通过执行 PCR 对其进行了总结。我们发现这两种技术没有区别。出于实用目的,使用 PLS 库很方便,但是出于理论目的,了解幕后的数学是很有用的。

完整代码可以在 GitHub 上找到。

感谢您的阅读!

如果你有任何问题或发现了错误或错别字,请在下面留下评论。

在 LinkedIn 上连接

生物医学数据的统计分析——概述

原文:https://towardsdatascience.com/statistical-analysis-of-biomedical-data-an-overview-7fe34eac790d?source=collection_archive---------14-----------------------

从 p 值和回归到聚类和分类

在过去的十年中,患者数据的产生出现了巨大的增长。从记录我们心率的智能手表等可穿戴设备到用于差异表达的 RNA 测序,我们监测和观察个体患者健康的能力从未像现在这样需要大量数据。尽管这使得能够对临床数据进行广泛的统计分析以用于诊断和研究,但至少可以说,统计解释可能相当棘手。能够正确理解和利用统计工具是你的武器库的强大补充。据说英国前首相本杰明·迪斯雷利曾经说过,“有三种谎言;谎言,该死的谎言,和统计”,尽管这句话的真正出处还不清楚。

基础知识

左图:单变量分布图示例。右图:多元分布图示例。作者创作的人物。

所有数据要么是数字要么是分类。数值数据要么是连续(可以分成无限小的单位,像药物浓度)要么是离散(不能分成无限小的单位,像整数)。另一方面,分类数据有三种类型:二元(是或否,存在或不存在)名义(命名,如 20 个氨基酸),以及序数(命名并具有先天顺序,如疾病严重程度)。

单变量统计处理一个感兴趣的变量,而多变量统计处理多个变量。例如,如果我们观察一组病人的心率分布,它是单变量的。但是如果我们在一个轴上观察心率,在另一个轴上观察患者的年龄,那么这就是多元的。有时,多元统计仅在因变量被测量为两个或更多自变量的函数时才被提及,而不是一个自变量。

为了描述分布的集中趋势,统计学家使用点估计,如均值、众数。为了描述分布的,常用标准差方差。特别是标准差是数据集在平均值附近的变化的度量。

高斯“正态”分布。平均值为 0。作者创作的人物。

左边的这个分布也被称为高斯分布,以发现它的德国数学家卡尔·弗里德里希·高斯的名字命名。大多数临床数据遵循这个正态分布。因为它在自然界的许多实例中被观察到,它已经成为大多数统计方法中的一个基本假设。为什么它经常出现?中心极限定理。样本数量越多,样本越接近目标人群。

低标准差表示接近平均值的数据点范围较小。高标准偏差表示数据点远离平均值的范围较大。68%的值落在偏离均值一个标准差的范围内,95%落在两个标准差范围内,99%落在三个标准差范围内。这些百分比是指特定值在该范围内的概率。

P 值难题

推断统计建立在描述统计的基础上,以确定两个平均值之间观察到的差异是纯粹由于随机因素还是由于潜在的假设因素(也称为实验假设)。这被称为单变量 t 检验。例如,为了确定吸烟者和非吸烟者的血氧饱和度是否不同,可以使用不成对 t 检验。当在两种不同条件下观察同一组时,使用成对 t 检验,例如比较相同参与者在运动前后的肌肉氧饱和度水平。

血氧饱和度。作者创作的人物。

t-test 提供了一个检验统计量,该统计量对应于两个平均值相同(由于潜在的假设因素而没有实际差异,也称为零假设)以及观察到的差异纯属随机的最小概率。这个概率被称为 p 值。还选择预定的阈值(在大多数医学研究中通常为 0.05),使得如果 p 值小于该阈值,则 p 值太小而不显著。例如,如果 p 值是 0.04,那么这两个平均值相同的概率太小,表明这两个平均值确实不同。这就是我们说差异在统计上****显著并拒绝零假设,从而接受实验假设的时候。

CI =置信区间。阿尔法= 0.025+0.025 = 0.05。注意区间是如何在距离 0(平均值)2 个标准差处结束的。回想一下,95%的数据可以在 2 个标准差内的正态曲线下找到,剩下的 5%在外面。作者创作的人物。

预定阈值被称为α值。它通常为 0.1、0.05 或 0.01,并创建一个值区间(范围),其中必须包含两组之间的真实差异。如果这个区间包含 0,那么我们接受零假设,因为它表明真正的差异可能是 0。如果间隔不包含 0,则两组之间一定有一些差异。较高的α值表示估计的较高精度(较小的区间),但也表示用于生成该估计的方法的置信度较低(又名。实验)。这里有一个权衡。这就是为什么 0.05 是最被广泛接受的值。因此,人们可能会想,如果一项研究的 p 值最终为 0.09,仅比α值 0.1 小 0.01,这种差异在实践中有多大意义?真的“显著”显著吗?这就是统计数据非常棘手和具有欺骗性的时候。这就是为什么无论显著性如何,报告 p 值总是最好的。

t 检验假设随机和独立抽样。因为 t 检验也假设高斯分布,所以我们不能将其用于其他类型的分布。然而,可以使用曼-惠特尼 U-检验(也称为 Wilcoxon 秩和检验)来比较两个队列的中值,并呈现可以与 alpha 值进行显著性比较的 p 值。这是一个比 t 检验更加稳健和强大的检验,但是分布必须而不是是正态的。

但是如果我们的数据点太少呢?测试差异显著性的另一种方法是排列测试。首先,我们计算两个群组的平均值之间的差异。然后,我们简单地混洗和混合来自两组的数据点,并找到新的平均值和它们的差异。我们重复这个过程几百次,并绘制出差值分布图,该分布图类似于泊松分布。

原始差异= 10。重复次数= 240。稀有度= 1/240 = 0.004

如果原始差异与随机改组组之间的差异相比足够罕见,那么这两组具有统计学意义。稀有度=(差异至少与原始差异一样大的次数/计算差异的次数)。稀有程度与α值进行比较,如果小于α值,则认为原始差异具有统计学意义。

到目前为止,我们已经讨论了比较两个队列的推断统计。但是,更复杂的研究可能有两个以上的队列进行比较,每个队列都是独立取样,且呈正态分布。这时 ANOVA (方差分析)测试就有用了。它比较组内和组间的方差。有单向、双向、三向和 n 向 ANOVA 测试,用于确定组的平均值是否受 1、2、3 或 n 个独立变量的影响。这些测试呈现遵循 f 分布的 F 值。f 分布基本上是右偏高斯分布。这个想法是比较这个分布上 F 值的 p 值。类似于 t 检验,如果 p 值小于α值,则各组的变异性显著。一旦方差分析拒绝了零假设,其他测试如 Tukey 的或 Bonferroni 的测试被用于确定哪些组在统计上是不同的。

随机生成 f 分布。f 值= 4。作者创作的人物。

通常,数据集中可能会有异常值。这些严重扭曲了平均值。有时,正态分布可能是偏态多峰态。其他时候,也可以观察到二项式或指数分布。序列比对研究保证只对正态分布的极端进行采样,最终结果类似于泊松分布。在这些情况下,像 t 检验这样的标准统计检验很难被证明是合理的,因为它们通常假设正态分布。

这是当你想改变曲线的形状,使它尽可能地类似于正常的曲线,又名。正常化。根据研究的范围和需求,可以决定完全排除异常值,或者将所有异常值限制在最大值。将 x 轴的比例更改为对数变换也使其更正常,因为它使异常值更接近平均值,使其分布更加对称。物质浓度通常在毫、微米和纳米尺度之间变化,导致多模态分布。使用合适且统一的标度可以将分布归一化为单峰。

回归分析的相关系数

另一个常见的研究目标是测量一个变量的变化在多大程度上解释了另一个变量的变化。通常,散点图用于说明这种关系。这方面有两个关键的统计:相关系数线性回归分析。将它们互换使用是一个常见的错误,但它们确实有一些区别特征。

左图:完美的正相关。中间:完全负相关。右:无相关性。作者创作的人物。

相关性系数** (R) 衡量关系的强度和方向。它的值范围从-1 到 1。相关性的强度由 R 值与 1 或-1 的接近程度来表示。因此,如果 R=0.9,则是非常强的正相关,即 X 的变化几乎与 y 的变化成比例。如果 R=-0.8,则 X 的变化几乎与 y 的变化成反比。但如果 R=0,则 X 的变化与 y 的变化不成比例。相关性对于评估预测、估计、模拟或相关性非常有用。样本太少会导致数据不可靠。它可能会被滥用,只选择极端的数据点,而忽略中间的数据点,以获得误导性的高 R 值。自然,数据点越多,我们对分析就越有信心。但是,这也可能被滥用,因为对于足够大的数据集,更容易获得具有统计显著性的 p 值,而在实践中,它可能不是“显著”显著的。**

R 常与 R(决定系数)混淆。r 其实和线性回归更有关系。它是对 X(自变量)变量的集体方差如何解释 Y(因变量)变量的方差的度量,范围从 0 到 1。线性回归呈现了一个完整的方程,用于预测 X 对 y 的影响。与相关性不同,变量是不可互换的,尽管两个值都可以用代数方法确定。X 变量被认为是解释变量,而 Y 变量被认为是响应变量。x 解释了 Y 的变化,Y 对 x 的变化做出反应,而不是相反。

左图:线性回归图。右:从左图得出的预测函数的评估。导出的预测值线性回归函数:y = 0.181x + 0.008。R = 0.973。R = 0.947。作者创作的人物。

在上面的例子中,我们比较了改变酶浓度对血红蛋白浓度的影响。有很强的正相关性(R=0.973),94.7%的血红蛋白浓度变化是由酶浓度解释的(R =0.947)。线性回归分析给我们的方程是:[血红蛋白]= 0.181[酶] + 0.008。使用这个方程,我们能够从任何浓度的酶预测血红蛋白浓度。在下一张图中,我们评估了该预测函数相对于观察到的血红蛋白浓度的性能。理想情况下,完美的预测函数将导致所有点位于从(0,0)到(max(x),max(y))的对角线上。由于图中的大多数点都落在这条对角线上,因此预测函数的性能非常高,如 R 值所示。线性回归是统计建模数据的最简单形式。其他回归模型包括逻辑、多项式、贝叶斯线性回归等等。然而,重要的是要警惕模型过度适应训练数据集,这是涉及机器学习和神经网络的应用程序中的常见问题。

聚类和分类的奇特案例

从找出解决方案中的不同代谢物到预测医院再入院、微阵列、基因芯片和蛋白质相互作用分析,聚类分类是生物医学研究中最常用的两种统计工具。

聚类是将具有一些共同特征的对象组合在一起。它有助于分类。后者不同于集群,因为共性是预先定义的。关于对象的一些先验知识被用作将它们分组在一起的基础,而在聚类中,从对象本身挖掘知识以发现共性。虽然聚类是一种无监督的机器学习方法,但分类是有监督的。

对于集群,我们需要:

  1. 对象之间相似性和不相似性的度量
  2. 根据测量值确定两个对象是否相似的阈值
  3. 一种测量物体间“距离”的方法
  4. 群集种子,群集从这里开始

为了测量相似性,我们可以使用皮尔逊相关系数或者欧几里德距离或者仅仅是差异。有两种主要的聚类方法,即 K-means层次化

k-表示将 n 个对象聚类成 m 个聚类,有或没有重叠。首先,选择一个对象作为第一个簇的质心。然后,对于第二个对象,计算它与所有预先存在的质心的相似性(最初只是第一个对象)。如果相似性满足预定阈值,则将对象添加到具有最高相似性的质心并重新计算质心,否则使其成为质心并开始新的聚类。重复,直到所有对象都用完。

首先,我们比较两点之间的距离,并计算新的质心(浅绿色的点)。前两个红点在它们的距离达到预定阈值时聚集在一起。然后我们计算新的质心和 x=6 的第三个点之间的距离。当它们足够接近时,它们被聚集在一起并计算新的质心(橙色点)。类似地,角上的两个点将形成第二个聚类,因为它们离第一个聚类太远。作者创作的人物。

分层聚类是一种自下而上的方法,将最相似的对象组合成一个单独的聚类。结果可以用一个叫做树状图的图来说明,它看起来像一个树形图或热图。这些对于系统发育序列分析非常有用。

基因组数据分析的层次聚类的热图形成了 4 个不同的聚类(C1-C4)。奥格登等人(2020 年)。

大多数生物医学数据往往是多维,即我们一起测量许多变量,例如,所有必需氨基酸的浓度,或 mRNA 和蛋白质微阵列。分析多元数据需要以某种方式将这些维度减少到一个、两个或三个,以便它们更容易理解。它允许在相同数据集上应用经典概念,如 t 检验、p 值或方差分析。

一个强大的降维工具是主成分分析(PCA)** 。本质上,它所做的是将一组变量组合在一起,这些变量之间具有高度相关性,并能最好地解释观察到的数据集中的变化。这导致数百个变量被减少到两三个关键特征,作为主要成分。分组成分本身通常是不相关的,因为它们描述了不同变化的观察数据集的不同方面。**

PCA 降维后的 3D 散点图示例,含 3 个聚类。PC =主成分(1,2,3)。作者创作的人物。

它还导致原始数据集被重新校准到主成分,相似变化的数据点聚集在一起。一般来说,如果主成分分析即使在很小的程度上也不能对数据集进行聚类,那么明智的做法是首先接受数据中没有多少区分因素的可能性。

虽然主成分分析是一个很好的降维工具,但它不应该用于分类变量的维度。与数字变量不同,它们在人工编码时不会有很大变化。这导致它们的效果被削弱,这意味着它们不能像数字那样解释观察数据集中的变化,因此主成分分析可能会产生误导。

图取自 Gromsky 等人(2015)。

然而,如果我们手头有一些关于数据的先验知识,并希望基于这些知识对它们进行分类,那么可以使用第二种工具,称为偏最小二乘判别分析(PLS-DA)* 。它需要对以前标记的数据进行训练。然后,它可以预测新获得的数据集的分类。事实上,它是现在广泛用于监督分类的机器学习算法的先驱。PLS-DA 是代谢组学中非常常用的分类技术。***

从 PLS-DA 外推,我们还可以使用一个称为变量重要性投影(VIP)* 的图来确定哪些变量是最有效的变量。它清楚地量化了每个变量的重要性。任何高于 1–1.5 的分数都表示一个重要变量。这些分数然后可以用于特征选择。***

基因组数据分析的 VIP 图。图取自奥格登等人(2020)。

评估技术

为了评估预测模型,如 PLS-DA 或任何其他预测类别和二元类的 ML 分类模型,统计学家使用混淆矩阵**

二元分类模型的混淆矩阵示例。作者创作的人物。

预测与观察类(地面真实)进行比较,然后分成四组;真阳性、真阴性、假阳性(I 型错误)和假阴性(II 型错误)。然后评估该模型,以计算灵敏度和特异性**

灵敏度(Sn) = TP 比率= TP/(TP+FN) = 在所有观察到的阳性结果中,被正确预测为阳性的观察结果的数量

特异性(Sp) = TN 比率= TN/(TN+FP) = 在所有观察到的阴性中,被正确预测为阴性的观察数量

这种评估也用于评估序列预测。例如,如果一个模型预测一段基因的编码区和非编码区,我们可以这样描述它们:

作者创作的人物。

通常一个模型需要优先考虑一个。自由主义模型(做出风险更大的预测的模型)往往更敏感,但不太具体,而保守主义模型(做出风险较小的预测的模型)往往更具体,但不太敏感。一个模型是自由的还是保守的,主要取决于它用于分类的预定义阈值。

如果模型预测患者有 75%的机会患有心脏病,自由模型更可能做出肯定的预测,因为其预定义的分类阈值可能低于保守模型的阈值。如果模型预测患者有 75%的机会需要外科手术,保守模型更有可能做出否定的预测,因为其预定义的分类阈值更有可能低于自由模型的阈值。用于筛选患者的模型往往更自由,因为所提出的建议比用于手术决策测试的模型具有更少的严重后果。一个模型在其敏感性和特异性方面的表现在受试者工作特征(ROC)曲线中得到很好的说明。

二元 ROC 曲线示例。作者创作的人物。

红线显示了随着预定义阈值的改变,真阳性率和假阳性率的不同组合。黑色圆圈是模型平衡的地方。如果我们随机猜测二进制类别,黑色对角线显示的是比率。

比较不同预测模型的 ROC 曲线的另一种方法是使用曲线下的面积(AUC)* 。随机猜测应该是最差的预测值,AUC 为 0.5。完美预测者的 AUC 为 1,中等预测者的 AUC 介于两者之间。***

完美的 AUC 为 1(红色)和 AUC 为 0.5(黑色)。作者创作的人物。

评估 PLS-DA 等分类模型性能的另一种方法是通过排列测试。与我们之前对单变量数据所做的类似,首先,我们对正确标记的数据应用 PLS-DA 模型进行训练,并确定分离系数,这基本上是基于一些主成分对数据分离程度的衡量。然后,我们打乱标签,重新运行 PLS-DA 模型,确定其分离系数,并重复几百次。分离系数应呈正态分布,如果原始系数基于某个潜在因素,则它应明显大于分布的其余部分,表明它不是一次性随机事件。

监督分类方法功能强大,当对象之间没有真正的联系时,通常可以将对象分组为簇。重要的是不要跳过 t 检验、回归、PCA 和非监督方法,因为这些方法不包含监督方法中存在的先验偏差。统计学是直觉的形式化。因此,在盲目遵循某种公式之前,相信自己的直觉是很重要的。

这是对生物医学研究中使用的日常统计的非常非数学化的概述。每一种背后的数学原理都有很大的不同;从寻找一组数据点的平均值到为描述性、预测性和区别性特征选择目的进行 PLS-DA。程序员在使用库之前必须知道它的数据结构。没有必要理解它的实现。医生在开处方前必须了解疫苗的药代动力学。不需要理解疫苗中使用的载体生物分子的特定分子机制。同样,理解数学并不重要,但它显然很有帮助。然而,重要的是理解他们潜在的假设、限制和行为。

如果你有兴趣了解更多关于这些带有数学解释的统计工具,那么 Josh Starmer 的这个 YouTube 播放列表是一个很好的资源。

参考文献:

  1. 奥格登、A. J .、威茨马、T. W .、温克勒、法里斯、y .、迈尔斯、G. L .、&阿赫卡米、A. H. (2020)。生长中短柄根系基因表达和调控元件的动态变化。科学报告,10(1)。https://doi.org/10.1038/s41598-020-63224-z
  2. Gromski,P. S .,Muhamadali,h .,Ellis,D. I .,Xu,y .,Correa,e .,Turner,M. L .,& Goodacre,R. (2015)。教程回顾:代谢组学和偏最小二乘法-判别分析-权宜婚姻或奉子成婚。化学分析学报,879,10-23。https://doi.org/10.1016/j.aca.2015.02.012
  3. David S. Wishart,代谢组学统计学导论,代谢组学创新中心(TMIC),https://www.youtube.com/channel/UC4CHrL7t9brRTIomi9rDLHA

*** 💔-reasons-why-your-machine-learning-model-is-garbage-d643e6f0661>

附言:如果你想要更多关于数据科学、编程以及生物学家如何在数据革命中导航的简明扼要的文章,可以考虑关注我的博客。

由于每分钟都有成千上万的视频被上传,所以过滤掉它们是很重要的,这样你就可以只使用高质量的数据。我亲自挑选的,我会把你感兴趣的主题的教育视频发邮件给你。报名这里。

谢谢大家!***

你可能面临的统计偏差(以及如何避免)

原文:https://towardsdatascience.com/statistical-biases-you-probably-face-and-how-to-avoid-them-f6b32bfba7eb?source=collection_archive---------25-----------------------

现实世界中很容易被统计数据忽悠。幸运的是,有办法识别这些陷阱并克服它们。

图片来自里欧·克罗尔

人类有理解世界的自然倾向。我们收集信息,将点点滴滴联系起来,得出结论。因为有了数据,我们对自己的结论越来越有信心。

当我们错误地解读数据时,这种信心就会变成一种诅咒。它让我们执着于对世界的错误理解。这种现象被称为统计偏差。对于统计偏差,您收集的数据或观察结果是合法的。但是你解读这些数据的方式是误导性的。

在下面的段落中,我用简单的英语介绍了我最喜欢的统计偏差,并解释了如何避免被它们误导。

  1. 赌徒的谬论🎰
  2. 生存偏差🔪
  3. 有影响的观察结果🦖
  4. 数据疏通 🗂️
  5. 辛普森悖论🍩

赌徒谬误🎰

假设你正在和一个朋友掷硬币。你们两个同意赌一美元。如果硬币正面朝上,你的朋友会付给你钱。如果它落在反面,你需要付钱给你的朋友。

在几次抛硬币之后,你注意到你已经连续输了五次了。沮丧之余,你决定用剩下的钱下注。你已经连续输了五次了。下一次抛硬币一定会有所不同,对吗?

当你羞愧地走回家,口袋里一点钱也没剩下时,你意识到自己成了赌徒谬误的受害者。受赌徒谬误影响的人认为,过去的事件总是影响未来的概率。

推断出某种模式很有诱惑力,但是要小心这种模式是否真的存在。(图片由作者提供)

概率的独立性

像多次投掷硬币这样的情况被称为独立事件。一次投掷的结果不会影响未来任何一次投掷的结果。其他常见的独立事件包括扑克手和骰子滚动。

当你第一次抛硬币时,它正面朝上的概率是 50%。你连败之后,概率还是 50%。这可能会让你觉得应该多赢一点,以便“平衡”输赢。但事实是,未来的事件并不关心过去的事件结果如何。

(请注意,如果你继续无限地抛硬币,你可能会期望正面和反面的比率收敛成 50-50 的比例。然而,这是由于高样本量和大数定律。过去的成绩不重要。)

修正赌徒的谬误

我会告诉你停止赌博,但那一点也不好玩。相反,一个简单的心态转变可以帮助你克服赌徒谬误:

当你基于独立事件做决定时,忘记过去发生的事情。

对待每一次抛硬币,就像这是你的第一次一样。如果你处于连败或连胜之中,假装什么都没发生过。这样,过去的结果就不会误导你。

生存偏差🔪

在第二次世界大战期间,美国希望减少在敌人炮火中损失的轰炸机数量。因此,研究人员调查了从任务中返回的飞机上弹孔的位置。

研究人员假设飞机上受到最大损伤的部分应该有额外的装甲。似乎很合理。当飞机完成任务返回时,研究人员记录了弹孔的位置。他们的总体数据如下图所示:

幸存的飞机引擎没有受到任何损坏!我想我们根本不需要保护这些部分……(图片由马丁·格兰德让提供)

从图中可以得出结论,翼尖应该有更多的装甲。毕竟,这些部门受到的损害最大。但是退一步讲。那些没有执行完任务回来的飞机怎么了?他们在哪里被枪杀的?

如果我们批判性地思考我们的数据集,很明显,一架飞机只有在没有被击落的情况下才成为数据点。数据没有显示任何飞机的引擎或驾驶舱有弹孔。是因为子弹神奇地避开了这些区域吗?也许那些飞机再也没有回家。

如果我们考虑到被成功击落的飞机,幸存飞机上的弹孔实际上表明了飞机上不需要保护的部分。我们的样本不能代表所有的飞机。

当数据点的某些属性可能阻止它成为数据集的一部分时,就会出现生存偏差。我们错误地关注“幸存”数据点的属性。这就是为什么我们在飞机的机翼上看到弹孔,而发动机中弹的飞机永远无法回家。这也是为什么似乎所有的对冲基金都赚了很多钱,而大学辍学生往往成为亿万富翁。不知道的人不在统计范围内。

存活偏差

你可以通过问自己一个简单的问题来发现生存偏差:

如果我的结论是错的,我能注意到吗?

如果你对这个问题的回答是否定的,那么你可能会经历生存偏差。如果一个彩票中奖者和你分享他的策略,想想如果策略不好会发生什么。对于每一个成功者来说,可能有数百万失败者做着完全相同的事情。

在抽象的层面上,注意你的观察是否会影响到你得到的数据。如果你想了解彩票,抽样调查每一个买彩票的人,不仅仅是中奖者。每个彩票中奖者都购买了一张彩票,但并不是所有的购票者都中奖了。

有影响的观察🦖

一般来说,大多数玩俄罗斯轮盘赌的人都能活下来并赚钱。然而,尽管平均回报率如此之高,我还是建议不要玩俄罗斯轮盘赌。

我们倾向于从综合指标(如平均值)中进行归纳。问题是总量并不能说明数据的全部。如果我们只知道俄罗斯轮盘赌的平均结果,这似乎是一个合理的游戏。然而,看到人们可以从游戏中死去是一个有影响力的观察。随着我们了解得越多,平均值就失去了价值,我们的结论也改变了。

观察数据集的分布填补了聚合度量的盲点。例如,平均值对于初创公司的估值并不重要。大多数初创公司一文不值,而另一些却价值数十亿美元。平均值什么也没告诉我们。如果我们观察书籍的受欢迎程度,我们会发现少数书籍占据了所有书籍销售的大部分。同理,平均也没多大帮助。除非数据的分布是简单的,否则平均数只能给我们有限的世界观。

Anscombe 的四重奏:每个数据集共享相同的均值、方差、相关性和线性回归。除了数据本身,一切都是一样的。(图片由舒尔茨提供)

检查有影响的观察

围绕有影响力的观察的故事的寓意是,你总是需要一个故事的两面。这就是为什么统计学家倾向于计算和可视化数据。都是为了了解数据而达到目的的手段。

小心真空中的计算。寻求收集更多的背景信息并理解你的数据。

我的意思不是说平均数和总数会误导人。相反,把它们想象成一个充满盲点的世界中的一块拼图。

Datasaurus 十二个:每个数据集共享相同的平均值、方差和相关性。最重要的是,它们都有好玩的造型!(图片由欧特克提供)

数据挖掘🗂️

数据挖掘包括寻找数据中具有统计意义的模式。(一些读者可能也知道这个术语叫做 p-hacking 。)pattern 是不是假阳性并不重要;目标是强迫出一种“洞察力”因此,那些实践数据挖掘的人可能会滥用以下战术:

  • 详尽地测量数据集中变量的随机组合
  • 丢弃和重新采集数据样本
  • 有选择地报告具有统计意义的结果

简而言之,数据挖掘是一种纯粹出于偶然呈现具有统计学意义的东西的行为。

你可能想知道数据挖掘是否只在模糊的情况下有效。可惜没有。如果一个数据集包含足够多的变量,那么不会出现任何假阳性模式的概率很低。

从什么时候开始吃鸡肉让我们进口原油了?(图片由泰勒·维根提供)

假设一家大型含糖零食公司资助研究,证明含糖零食是健康的。他们收集的数据表明这些零食(显然)是不健康的。作为回应,研究人员摆弄着这些数据,希望发现任何与此相反的模式。当发表这项研究时,他们只提供了“证明”零食是健康的证据。这就是现实世界中数据挖掘的方式。有些人对得出某个结论有既得利益,即使这意味着抓住救命稻草。

探测数据疏通

为了确定数据挖掘是否发生,质疑研究者的方法。

  • 数据收集前是否确定了测量值?
  • 结果可重复吗?
  • 结论有意义吗?

对这些问题中的任何一个回答“否”都可能意味着数据挖掘。如果研究人员在数据收集前没有计划好他们的测量,他们可能会寻找假阳性。如果结果不可重复,结论可能只是巧合。如果结论没有意义,也可能表明某种形式的篡改。

辛普森悖论🍩

1973 年,加州大学伯克利分校因性别歧视被起诉。似乎它的研究生院录取女性的比例较低。该研究生项目接受了 8442 名申请人中的 44%。同时,它只接受了 4321 名女性申请者中的 35%。多么性别歧视!

一项对入学数据的研究试图理解为什么会发生歧视。它将入学人数按院系分类,并重新评估数据。下面是按人口最多的六个省分列的图表。

总的来说,85 个部门中的大部分似乎还算公平。然而,研究确定 6 个部门对男性有偏见。同时,其他 4 个部门对女性有偏见。在统计院系时,整个学校更偏向于男性。多么性别歧视!

我们从论文中得出的结论是,具体的部门很重要。女性倾向于申请录取率较低、竞争更激烈的院系。相比之下,男性倾向于申请竞争不那么激烈的部门。

辛普森悖论:当一个群体分成几个组时,这个群体中的趋势就会逆转。(图片由舒尔茨提供)

整个招生混乱是辛普森悖论的经典例子。在一个群体中有一个明显的趋势,但是这个群体被分成不同的群体,这个趋势要么消失,要么逆转。

如果你直觉地思考这个问题,有几个因素导致了这个悖论。首先,不同群体的人口规模往往是不同的。这可能会影响平均值。此外,我们可能会错过真正决定我们结果的混淆变量。重要的变量被倾斜的样本量淹没了。

解决辛普森悖论

对辛普森悖论的反应是一个两步的过程。您需要采取以下措施:

  1. 检测辛普森悖论可能存在的情况
  2. 确定辛普森悖论的哪种解释是正确的

如果你注意到看起来像概括的不寻常的模式,辛普森悖论可能存在。寻找可以解释这种模式的隐藏变量。如果考虑这个变量真的很重要,这就是辛普森悖论的一个例子。

下一步是确定哪个趋势符合现实。请注意,将一个群体分成几个组并不总是正确的。在这种情况下,你必须运用批判性思维。在大学录取方面,院系影响录取率是有道理的。

假设你观察了申请人的头发颜色,发现了一个新趋势。你应该怀疑头发颜色是一个真正的混淆变量。发色影响录取率说不通。(追求头发颜色和录取之间的相关性实际上是数据挖掘!)

结束语

冒着被我更倾向于数学的朋友告诫的风险,我想分享我的“街头智慧”版本的统计学核心原理:

  1. 数字很难。
  2. 人们会被统计数据愚弄。
  3. 你不想成为一个笨蛋。

统计偏差囊括了所有三个原则。说到底,对现实有一个清晰的认识是很重要的。为此,你必须知道如何正确解释你观察到的东西。保持清醒的头脑,你就很难被愚弄。玩得开心!

感谢阅读!如果您喜欢阅读或有任何反馈,请考虑发消息并在 Twitter 上关注我。

统计机器学习:从零开始的梯度提升和 AdaBoost

原文:https://towardsdatascience.com/statistical-machine-learning-gradient-boosting-adaboost-from-scratch-8c4b5a9db9ed?source=collection_archive---------19-----------------------

全计算模拟助推程序的数学推导

奥斯卡·诺德在 Unsplash 上的照片

1:简介

Boosting 是一系列用于离散和连续随机变量目标的集成机器学习技术。助推模型采用非参数加性模型的形式,并且最典型地被指定为加性成分是“弱学习者”。从经验风险分解的角度来看,可以很容易地表明,任何任意统计估计量的均方误差(MSE)是所述抽样估计量的偏差和抽样方差的平方和…

作者图片

…“弱学习者”是具有高平方偏差但低抽样方差的统计估计量。从计算的角度来看,弱学习者具有模型拟合容易的优势。弱学习器的例子包括只限于几层深度的单个决策和回归树,甚至可能是一层(称为“决策/回归树桩”)。

提升的动机是恢复弱学习器的附加集合,它们一起可以指定任意复杂的模型。在本文中,我们将详细说明梯度增强和 AdaBoost 的理论框架和完整的数学推导。我们还将从头开始提供这些方法的完整计算模拟,而不使用增强计算包。

这篇文章的目录如下:

作者图片

2:梯度推进模型

利用梯度推进,模型估计过程可以被视为在提议弱学习者的空间上在函数空间中执行梯度下降(因此得名“梯度推进”)。让我们进入数学规范和拟合程序:

2.1:统计模型的规格

作者图片

2.2:安装程序

为了理解梯度增强模型的拟合过程,首先回忆一下泰勒展开是有帮助的。

作者图片

作者图片

作者图片

作者图片

作者图片

作者图片

3: AdaBoost 模型

接下来让我们讨论 AdaBoost,它实际上是更一般的梯度增强模型的一个特例

3.1:统计模型的规格

作者图片

3.2:安装程序

根据第 3.1 节中概述的信息和符号,让我们深入了解 AdaBoost 的安装程序

作者图片

作者图片

4:对于非参数可加模型,提升作为贪婪拟合过程的另一种观点/动机

从非参数加性模型的角度来看,有一个略微不同的观点。这是一个在数理统计和统计学习社区中被很好理解的观点,但是我很少在机器学习社区的计算机科学分支中看到这个观点。我认为这是一个值得理解的视角,并且提供了一个更健壮的关于 Boosting 作为一系列过程的观点。

有一类统计模型称为非参数可加模型。这类估计量非常庞大,而且非常灵活。从某种意义上说,这类模型太灵活了。作为一名利用该类统计模型的分析师,有如此多的超参数和设计选项可供选择,指定一个“正确的”模型可能会让人不知所措。

对于一个给定的预测问题,有一些潜在的非参数加性模型,可以说在全局意义上“工作得最好”。这种“最佳”模式我们将称为“真正的 NPA 模式”。给定我们的样本数据,我们将尽最大努力指定和恢复这个“真正的 NPA 模型”。但是,如何以一种有条理的、切实可行的方式着手这样做呢?我们需要搜索的参数空间是巨大的!

先打个比方吧。想象一下,我们有一个机器学习问题,我们正在利用线性回归来解决它。我们有 100 个特性可供选择,我们知道有一些真正的特性组合可以提供“最佳子集选择”线性回归模型。但是,我们将不得不测试 2 ⁰⁰不同的模型,以恢复“最好的一个”,这是一个数百万亿的搜索空间。我们可以利用向前选择等贪婪算法,而不是试图在整个空间中搜索,在向前选择中,我们将选择一个变量,并每次将一个变量添加到最终模型中,根据贪婪的短视启发式方法做出选择。通过这种方法,我们只需要测试几千个模型,而不是数百万亿个。我们知道这种向前选择的过程不可能恢复“最佳子集选择”问题的精确解;但它将恢复一个可能非常适合我们目的的模型。

上面的前向选择场景非常类似于利用 Boosting 作为机器学习上下文中非参数可加模型的拟合过程。我们希望恢复对“真实 NPA 模型”的估计,但是搜索空间的大小令人难以承受。在这种意义上,Boosting 可以被视为一系列贪婪算法过程,用于估计“真实 NPA 模型”问题的解决方案,类似于使用前向选择来估计最佳子集选择问题。

5:计算模拟

下面是一个完整的工作示例(从头开始构建),在连续目标上拟合梯度增强模型,在二元分类目标上拟合 AdaBoost 模型。

首先,让我们加载我们需要的库:

接下来,使用一个特意复杂的模型规范来模拟数据集的函数:

让我们恢复一个具有连续结果目标 Y 的模拟数据集,并拟合一个梯度推进模型。注意,我们将打印每个弱学习者添加到模型中的全局损失函数的经验实现。当经验损失< 1.

Let’s now recover a simulated dataset with a binary categorical outcome target Y, and fit an AdaBoost Model. Note, we will print the empirical realization of the Global Loss function with each weak learner added to the model. We will consider the model to have converged when all n=1000 training points are correctly classified by the ensemble model.

For the complete code/notebook for the above computational simulation, please see the following github 链接 时,我们将认为模型已经收敛。

6:总结和结论

希望以上有见地。正如我在以前的一些文章中提到的,我认为没有足够的人花时间去做这些类型的练习。对我来说,这种基于理论的洞察力让我在实践中更容易使用方法。我个人的目标是鼓励该领域的其他人采取类似的方法。以后我会继续写类似的作品。请 订阅并关注我在 Medium 和 LinkedInhttp://www.linkedin.com/in/andrew-rothman-49739630上的更新!

统计机器学习:内核化广义线性模型(GLMs)和内核化线性回归

原文:https://towardsdatascience.com/statistical-machine-learning-kernelized-generalized-linear-models-glms-kernelized-linear-876e72a17678?source=collection_archive---------12-----------------------

思想和理论

线性平滑器的内核化特征空间的数学推导,以及完整的计算模拟

照片由詹姆斯·哈里逊在 Unsplash 上拍摄

1:简介

对于线性平滑器和基于线性预测器的采样估计器,Mercer 核是在高维特征空间中拟合线性决策边界的非常方便的工具。事实上,这样的特征空间甚至可以是无限维的(正如我们将要展示的)。从机器学习的角度来看,Mercer 内核可以被视为对设计矩阵中的一组“基础”变量执行一种“半自动”特征工程。我们可以在不显式计算 X 的情况下,在特征矩阵 X 中拟合出高维特征空间对应的线性决策边界。

从教学背景来看,在机器学习社区引入与支持向量机(SVM)配对的 Mercer 内核是历史上常见的。我们在这里不打算这样做(参见第 3.1 节解释原因),而是在更友好的基于线性预测器的抽样估计器的环境中引入核:特别是普通最小二乘(OLS)线性回归和规范广义线性模型(GLMs)。

这篇文章的目录如下:

作者图片

说到这里,让我们开始吧。

2:激发内核化特征空间和“内核技巧”

让我们假设一个我们想要拟合的普通最小二乘(OLS)线性回归模型。我们有一个包含 n 个观察值的数据集,其中:

作者图片

使用我们的模型:

作者图片

尽管我们很快认识到设计矩阵 D 中的 v 变量过于有限,不足以创建一个稳健的预测模型。我们决定对设计矩阵 D 中的 v 变量手动执行特征工程。

这个特征工程步骤在特征矩阵 X 中产生 p 个特征,其中 p 远大于 vX 中的这些 p 工程特征可能包括 D 中的原始 v 变量的乘法交互和/或非线性变换。我们现在可以利用我们的特征矩阵 X 来拟合一个健壮的模型。我们以 X 中的 p 特征为自变量,拟合出预测 Y 的 OLS 回归模型。得到的 OLS 拟合将是对应于特征矩阵 X 中丰富的 p 特征的高维特征空间中的线性判定边界。

这些步骤也是:

作者图片

我们现在要重复同样的练习,但是增加了一个中间步骤。从我们的特征矩阵 X 中,我们构造了“核矩阵” Kₓ ,它仅仅是 X 与其自身转置的点积。注意 Kₓ 的尺寸为 n x n 。然后,我们采用我们的线性模型,将其完全重新参数化为 Kₓ 的函数,而不是 X 的函数。然而,我们的拟合模型恢复了与以前相同的线性决策边界。

这些步骤现在又是:

作者图片

我们现在已经拟合了同一个模型两次,一次没有,一次有了内核矩阵 Kₓ 的额外步骤。这一额外步骤的动机很快就会变得明显。

我们将最后一次重复这个练习,并做最后的修改。从 1)设计矩阵 D ,到 2)特征矩阵 X ,再到 3)内核矩阵 Kₓ ,如果我提供一个“神奇的”数学方程,允许直接根据原始设计矩阵 D 计算 Kₓ ,会怎么样?有了这个等式,人们可以跳过构建特征矩阵 X 的第二步。然而,我们仍然以同样的模型拟合结束;与 X 中的 p 特征对应的高维 p 特征空间中的同一线性判定边界。尽管我们不需要明确地计算特征矩阵 X ,也不需要在计算机上的任何地方存储 X 来拟合这个模型。我们完全跳过了这一步,而是直接根据原始设计矩阵 D 计算参照 XKₓ

作者图片

这通常被称为“内核技巧”。上述过程允许我们在高维特征空间中拟合线性决策边界,而无需将所述高维空间中的所有特征显式计算到显式特征矩阵 X 中。甚至在我们感兴趣的高维特征空间是无限维的情况下也是如此!有大量关于 Mercers 定理和再生核 Hilbert 空间(RKHS)的文献在数学上支持上述说法,但这超出了本文的范围。相反,我将基于简单的线性代数和内积提供一个直观的解释来支持这个观点:

假设我们有一个特征矩阵 Xn 个观察值以及 p 个特征(即大小nXp)p =∞。这对应于一个特征矩阵 X 我们从来没有希望在任何真实世界的计算机上显式计算或存储,因为这个 X 有无限多的列。但是注意,即使有了 p = ∞,核矩阵 Kₓ 根据定义是 X 与其自身转置的点积。这个点积的内部维度塌缩(即(nxp)(pxn)=nxn*),我们剩下大小为 n x n 的核矩阵 Kₓ ,它是有限大小的。假设内核化过程允许我们跳过显式计算和存储 X ,我们仍然可以用 p = ∞来拟合我们的模型。

3:将 GLMs 和线性回归重新参数化为核矩阵的函数

3.1:暂不考虑支持向量机(SVM)

在本节的后面,我们将推导出拟合带核线性回归和广义线性模型(GLMs)所需的抽样估计量。不过在继续之前,我想先提一下支持向量机(SVMs ),以及为什么我有意不在本文中讨论这类统计估计量。注意,我将在以后的文章中讨论内核化的支持向量机。

关于我离开支持向量机的动机,在机器学习社区中,线性平滑器和基于线性预测器的采样估计器的 Mercer 核通常与支持向量机有关。即使从教学的角度来看,绝大多数学生对内核化特征空间的第一次介绍都是关于支持向量机的。我个人认为这种教学方法并不理想,原因有三:

  • 由于凸优化空间中的历史原因,支持向量机的参数通常通过最大化其对偶拉格朗日形式(与直接最小化其原始形式相反,这是最有可能做到的)来进行经验估计。正因为如此,内核化的支持向量机通常也通过它们的对偶拉格朗日形式来拟合。对于入门的学生来说,凸函数的背景材料,确定原始函数,推导对偶拉格朗日函数,理解为什么需要强对偶,证明支持向量机具有强对偶,这些都是对自身的一种挑战。我认为,在介绍凸优化材料的同时介绍 Mercer 内核对于普通学生来说是势不可挡的,并且与其说是教育,不如说是迷惑。在我看来,更好的做法是首先介绍具有更熟悉用例的内核,然后在 Mercer 内核、线性支持向量机和凸优化材料都被分别介绍和消化之后,再回到内核化支持向量机。
  • 正如“线性”支持向量机一样,核化支持向量机的经验参数估计没有封闭形式的解析解。求解这些参数估计值需要迭代数值拟合过程(梯度下降、次梯度下降、坐标下降、牛顿-拉夫森等)。将迭代拟合过程扔进混合物中是另一层复杂性,它分散了对 Mercer 内核本身的基础知识的学习。或者,就像普通的最小二乘(OLS)线性回归一样,核化线性回归的经验参数估计的闭式解;不需要迭代拟合过程!鉴于这一特性,我完全支持将内核化线性回归的教学作为 Mercer 内核的介绍。有了内核化的 OLS,我们可以在纸上查看封闭形式的解决方案,并可以充分欣赏 Mercer 核及其解决的参数估计问题,而不会被迭代拟合过程分散注意力。
  • 最后,但最重要的是,我一直看到机器学习社区中的年轻从业者对将 Mercer 核应用于几乎任何线性平滑器或基于线性预测器的采样估计器感到困惑,而不仅仅是 SVM。普通学生被教导将 Mercer 核与支持向量机如此紧密地联系在一起,以至于他们冒着错误地将核视为支持向量机本身的特殊固有属性的风险(他们肯定不是)。人们会提到“嗯,支持向量机是通用逼近器!”。这种说法不正确。支持向量机本身是而不是通用逼近器;相反,它们只是线性平滑器和基于线性预测器的估计器家族中的许多抽样估计器之一。与线性回归或广义线性模型(GLMs)相比,支持向量机在这方面没有什么本质上的特殊之处。相反,任何明智选择 Mercer 核的基于线性预测的估计器都是通用的近似器,而不仅仅是支持向量机。例如,通过适当的核选择,有核逻辑回归是一个通用的近似器。首先在支持向量机之外引入 Mercer 内核对于打破这种通常被误解的联系很有帮助。

3.2: OLS 回归和岭回归复习

接下来让我们继续复习 OLS 回归和岭回归的封闭解。

从 OLS 回归开始:

作者图片

现在让我们稍微修改一下我们的 OLS 估计量,以恢复岭回归的封闭形式解:

作者图片

3.3:内核化的 OLS(有里奇罚分)

我们现在已经准备好导出具有核化特征空间的岭惩罚 OLS 线性回归的封闭形式的抽样估计量。让我们再次指定我们的线性模型:

作者图片

作者图片

3.4: GLM 复习

为了激励内核化的 GLMs,让我们做一个简短的 GLM 复习。

请注意,有关 GLMs 的深入介绍和动机,请参阅我的三篇系列文章:

  • GLMs 第一部分:严格的数学公式
  • GLMs 第二部分:牛顿-拉夫森,费希尔评分,&迭代加权最小二乘法(IRLS)——严格概述
  • GLMs 第三部分:作为递归广义线性模型的深度神经网络

作者图片

作者图片

作者图片

我们可以稍微修改上面的推导,以适应损失函数中的附加脊罚:

作者图片

3.5:内核化的 GLM(有里奇罚分)

随着我们的 GLM 复习,我们现在继续内核化的 GLMs。我们的内核化 GLMs 的方法与我们在第 3.3 节中用内核化 OLS 实现的参数化技巧非常相似:

作者图片

4:构造有效的核矩阵

概括一下:

  • 2中,我们提供了在高维特征空间中拟合线性决策边界的动机,而无需经验计算特征矩阵 X ,并展示了如何通过“核技巧”实现这一点
  • 在第 3 节的*中,我们展示了如何将 OLS 和 GLM 回归模型重新参数化为其内核矩阵 Kₓ 的函数,而不是其特征矩阵 X*

问题仍然是,我们如何直接根据设计矩阵 D 计算关于有效特征矩阵 X 的核矩阵 Kₓ (如2中提到的)?这正是第四章要解决的问题。

由于只能访问设计矩阵 D ,我们总是可以很容易地计算出一个内核矩阵,我们知道它 1)是一个有效的内核矩阵,2)不需要访问一些高维特征工程特征矩阵 X ?答案是参照设计矩阵 D 本身的内核矩阵!这相当于根本不执行任何特征工程:

作者图片

本节接下来是一组数学“运算”,用于从已知的其他有效核矩阵生成有效核矩阵。直观地说,我们的方法可以是参照设计矩阵 D ( 我们根据定义知道它必须是一个有效的核矩阵)获得我们的核矩阵的几个副本,并开始使用下面的一些数学运算将这个矩阵的副本组合在一起。结果将是有效的核矩阵 Kₓ ,其对应于存在的一些有效的(并且可能复杂的)特征矩阵 X 。然而,我们已经恢复了它的内核矩阵 Kₓ ,而不必显式地计算特征矩阵 X 本身。

4.1:内核的标量倍数

作者图片

4.2:向内核添加一个正常数

作者图片

4.3:核的元素式和

作者图片

4.4:内核的元素乘积

作者图片

4.5:核的多项式函数

作者图片

4.6:核的径向基函数

作者图片

注意,上面的径向基函数核是参考具有无限数量特征的特征矩阵 X (即 p = ∞)。利用这种类型的核是拟合对应于无限维特征空间的线性决策边界的一个例子(在2中提到)。

5.计算模拟

python 中提供了针对内核化线性回归和内核化逻辑回归的计算模拟。请注意,在这两种情况下:

  • 我们从只有三个变量( v =3)的设计矩阵 D 开始。
  • 我们利用具有正常数 c =3 的 2 次简单多项式核(参见第 4.5 节)。
  • 在这个特殊的玩具例子中,特征矩阵 X 对于手工特征工程师来说是故意简单的。因此,我们将首先执行手动特征工程并直接使用特征矩阵 X 来拟合我们的模型,然后使用内核化方法来拟合我们的模型。我们将展示这两种方法导致相同的线性决策边界拟合。
  • 在这个玩具示例中,已知特征矩阵 X 不包含任何彼此线性重新参数化的特征(我是这样设计的),我们不会在模型中包含脊正则化。请注意,在实际中,对于具有更复杂内核的现实世界问题,为了稳定起见,非常鼓励使用岭正则化。

使用具有正常数 c =3 的 2 次多项式核,我们得到:

作者图片

如果我们对设计矩阵 D 执行手动特征工程,以恢复对应于上述内核矩阵 Kₓ 的特征矩阵 X ,我们有:

作者图片

现在进行计算机模拟。让我们从加载我们需要的库开始:

接下来,sigmoid 激活函数和手动恢复特征矩阵的函数 X :

现在,让我们模拟 OLS 和逻辑回归的数据集:

最后,使用 1)特征矩阵 X 直接拟合和预测 OLS 和逻辑回归模型的函数,以及 2)设计矩阵 D 的内核化:

我们现在准备执行我们的分析。

首先,让我们直接利用特征矩阵 X 来拟合和产生我们的 OLS 线性回归模型的输出预测:

…现在我们执行相同的分析,但是对设计矩阵 D 进行了内核化:

请注意两种分析的输出预测图是如何相同的

接下来,让我们直接利用特征矩阵 X 来拟合和生成逻辑回归模型的预测:

…最后,我们执行相同的分析,但对设计矩阵 D 进行了内核化:

同样,请注意上述内核化分析的预测结果如何与直接利用特征矩阵 X 的逻辑回归模型的预测输出相同。

以上计算模拟的完整代码/笔记本,请看下面的 github 链接

6:总结和结论

希望以上有见地。正如我在以前的一些文章中提到的,我认为没有足够的人花时间去做这些类型的练习。对我来说,这种基于理论的洞察力让我在实践中更容易使用方法。我个人的目标是鼓励该领域的其他人采取类似的方法。以后我会继续写类似的作品。请 订阅并关注我在 Medium 和 LinkedInhttp://www.linkedin.com/in/andrew-rothman-49739630上的更新!

你的手机应用程序对隐私有多敏感?

原文:https://towardsdatascience.com/statistical-metrics-to-quantify-the-privacy-level-of-your-mobile-app-6bbc31848219?source=collection_archive---------21-----------------------

基于位置、联系人和内容量化移动应用隐私风险的统计指标

德马尔亚·比斯瓦斯、伊马德·阿德、吉安·保罗·佩鲁奇

摘要应用程序越来越受欢迎源于它们能够为用户提供高度定制的服务。另一方面,为了提供这样的服务,应用程序需要访问用户非常敏感的私人信息。这导致恶意应用程序在后台收集个人用户信息,并以各种方式利用这些信息。研究表明,目前的应用程序审查过程主要限于安装时间验证机制,无法检测和防止此类攻击。我们认为,这里缺少的基本方面是一个全面和可用的移动隐私解决方案,它不仅保护用户的位置信息,还保护其他同样敏感的用户数据,如用户的联系人和文档。对于不理解或不关心底层技术细节的普通用户来说,这是一个可用的解决方案。为了弥合这一差距,我们提出了隐私指标,根据隐私影响量化低级应用访问,并将其转换为高级用户可理解的评级。我们还提供隐私面板应用程序的设计和架构,该应用程序以图形用户友好的格式表示计算出的评级,并允许用户基于这些评级定义策略。

该论文的出版的版本可以在下面的 IEEE 可用性、可靠性和安全性国际会议记录( ARES ) ( pdf )中获得

一.导言

应用程序(智能手机的应用程序)是当今智能手机的生命线。从苹果的 AppStore、谷歌的 Android Marketplace、诺基亚的 Ovi Store、Windows Marketplace Hub 等网站上的数千款应用程序可以明显看出它们的受欢迎程度。应用程序如此受欢迎和有用的原因是因为它们为我们生活的不同方面提供高度定制的服务,从推荐感兴趣的基于位置的服务到监测我们的健康。缺点是,为了做到这一点,应用程序需要访问关于用户的非常实时和上下文相关的信息。这些信息是由许多输入设备(摄像机、麦克风等)提供的。)和传感器(GPS、加速度计等。)嵌入移动设备中,然后提供给应用程序。显然,这些信息非常敏感,如果落入他人之手,将会带来严重的隐私问题。

为了避免这种滥用,大多数移动平台,如 iOS、Android,都提供了一种基于“需求”的访问控制模型,在这种模型中,只有在用户明确授权后,应用程序才能访问设备资源。在高层次上,该模型的工作方式如下:

  • 应用程序在其基于 XML 的清单文件中声明它们需要访问以提供功能的资源列表。
  • 在安装过程中,会读取清单文件,并以用户友好的格式向用户显示所需资源的访问列表。
  • 只有当用户“接受”时,才允许安装该应用程序。安装后,移动操作系统会提供所需的访问控制,以确保应用程序只能访问清单文件中声明的资源。

虽然这是一种威慑,但研究[1]、[2]、[3]、[4]表明这种模型本身是不够的。许多应用程序,包括一些最流行的应用程序,都被发现在运行时滥用了安装时访问权限。

例如,虽然天气应用程序需要合法访问用户的位置,但如上所述的安装时验证无法阻止应用程序每隔几秒钟检索用户的位置并将其提供给外部服务器。

这方面的最新成果包括细粒度运行时访问控制模型[5]、表达性移动隐私策略[6]、[7]、智能匿名技术(尤其是在保护用户位置方面)[8]等。虽然它们很有帮助,并解决了部分问题,但缺少的基本方面是一个全面且可用的移动隐私解决方案。

  • 所谓全面,我们指的是这样一种解决方案,它不仅保护用户的位置数据(正如大多数文献目前关注的那样),还保护其他同样敏感的用户数据类型,如用户的联系人和内容(图片、音乐等)。).
  • 所谓可用,我们指的是“普通”用户可以使用和理解的解决方案。如果用户不明白什么是加速度计,或者从加速度计中可以很容易地推断出什么样的个人信息,那么定义复杂的政策来限制应用程序访问设备的加速度计是没有帮助的。

因此,我们的目标是提供可用的隐私控制,既提供隐私相关信息,又允许用户在用户可理解的级别设置策略。为此,我们建议隐私专家组(PP)做出以下主要贡献:

  • 识别和提取移动设备上要保护的用户数据类别:位置、联系人和内容。
  • 隐私指标,根据隐私影响量化低级应用访问,并将其转化为用户可理解的评级(第 II-B 节)。
  • 用户友好的应用程序设计,代表隐私评级,并允许根据它们定义策略(第二至第三节)。

二。隐私面板

A.体系结构

图 1:隐私面板应用架构(图片由作者提供)

图 1 所示的 PP 架构包括以下四个阶段:

  1. 拦截移动设备上安装的应用程序访问的数据。
  2. 量化他们的隐私影响。
  3. 以用户友好的格式显示评级。
  4. 为用户提供有效的访问控制机制。

设备上的应用的行为对应于它如何与设备资源交互,例如

  • 访问传感器以读取它们的数据值。现在常见的智能手机包括以下许多传感器:GPS,加速度计,环境光,指南针,磁力计,方位,接近度,旋转。
  • 访问网络、文件系统、联系人。
  • 截取触摸屏上的动作。
  • 打开摄像机和麦克风。
  • 拦截设备上更传统的操作,如打电话、发送短信等。

app 通常通过调用移动平台依赖中间件框架(如 Qt、Android SDK 等)的(开发者友好的)API 来执行上述动作。鉴于此,第一个 PP 阶段包括监控这些 API 调用,截取并记录它们的细节。

值得注意的是,在现实生活中,移动应用程序将有多个接口连接到底层传感器,例如在浏览器中运行的 HTML5 应用程序,或具有本机代码/JNI 的 Android 应用程序。因此,PP 应用程序需要确保覆盖所有可能的入口/出口点,以便拦截所有访问。

B.隐私指标

在本节中,我们将介绍三种用户数据类别的隐私指标:位置、联系人和内容。这些指标让我们能够量化访问特定用户数据的应用程序对隐私的影响。值得注意的是,提出的隐私指标绝不是万无一失的。这是第一次尝试从总体上量化移动应用对隐私的影响。然而,我们相信这仍然是正确的道路。

我们把它比作网络上广泛使用的密码强度检查器。基于试探法,它们是对密码强度的实用且普遍适用的估计,不一定与密码分析结果匹配。他们成功地得到了普通用户的信任,这些用户现在正额外努力设置“强”密码。同样,我们的 PP 旨在为普通用户提供他们对手机上运行的应用程序的隐私暴露的粗略估计,而不一定提供严格的安全保证。

位置 :位置数据用非常简单的术语来说就是对应于用户所在位置的(经度,纬度)地理坐标。我们的目标是量化应用程序访问的用户位置数据对隐私的影响。量化的目的是捕捉应用程序收集了多少位置数据,它有多精确,以及从用户的角度来看它有多敏感。“多少”的纯线性测量可以通过记录用户位置数据何时(多频繁)被相关应用访问来计算。关于位置数据的精度,在此重要的是要记住,用户位置信息可以通过不同的定位方法获得,例如基于卫星的(GPS)、基于非卫星的(WiFi、蜂窝塔)等。具有不同的能量和精度权衡。因此,我们计算在应用活动期间考虑的 N 个样本的位置隐私度量 L_P ,作为频率精度因子的函数如下:

其中 F 为频率因子, A 为精度因子,定义如下:

其中 T^ON 是应用活动时的周期总和, f_max 是位置传感器支持的最大采样频率(=默认为 1/秒)。精确系数 A 的公式为:

其中 P_max 为传感器提供的最大精度(即 2.2m)。 H_i 为第 i_th 个样品的水平精度。当应用程序请求具有高、低、任意精度的位置样本时,它被计算为平均返回精度。要考虑的默认值分别为 2.2m、30m 和 15m。

L_P(t) 表示直到时间 t 计算的隐私度量。然后,度量的增量版本只需要考虑可变参数:

其中 H_t+1 表示在时间 t + 1 读取的位置数据的水平精度,t^on_[t,t+1】表示 app 在时间 tt + 1 之间的运行时间。

值得一提的是,应用程序请求的数据类型和它实际收到的数据之间可能存在差异。前者暗示了应用程序的“好奇程度”(恶意),例如一个总是以最高精度请求用户位置的应用程序。

然而,由于各种技术和系统问题,由于 GPS 不可用(室内)、电池保存等,最终返回给它的位置数据可能具有比所请求的更低的精度。一个 app 获取的位置数据方面的实际隐私泄露,显然取决于后一个方面。下面我们简要讨论影响应用程序获取的位置数据的实际隐私影响的几个方面。这些方面中的一些已经在先前呈现的位置隐私度量 L_P 中被考虑。考虑以下所有方面的综合位置度量超出了本工作的范围。

联系人 :我们认为联系人主要是用户在移动设备上的通讯录。地址簿中的每个条目都有唯一的标识符(如 URI、姓名、出生日期)和相关字段,如地址、电子邮件、电话号码等。,也称为“细节”。应用程序的联系人隐私评级 CP 直观地基于两个因素:

  • 应用程序访问的唯一联系人的数量,以及
  • 访问的每个联系人的详细信息的数量。

更准确地说,

其中 N_t 为联系人总数, N_a 为应用访问的联系人数量, D_a(C) 为被访问的联系人 C 的明细数量, D_t(C) 为他的明细总数。

此处访问的联系人的唯一性非常重要,因为两次访问相同的联系人详细信息不会给攻击者提供任何附加信息。

该模型为扩展基于用户与所访问的联系人(例如,配偶、密友、办公室同事等)的关系分配的权重的度量留出了空间。类似地,也可以基于所访问的联系人详细信息字段(例如,地址、电子邮件、电话号码等)的敏感度来分配权重。联系人隐私等级 C_P 可以按如下方式递增计算。对于联系人条目 C 的每个新截取:

  1. 检查 C 是否唯一,即根据其 ID 是否被首次访问。如果是,继续;否则退出。
  2. 确定通讯录中联系人的数量 n
  3. C_P = 1/2 × (C_P + 1/n)

对于联系人 C 的联系人详细信息字段 D 的每个新截取:

  1. 根据对的唯一性( CIDD )检查字段 D 是否被首次访问。如果是,继续;否则退出。
  2. 确定地址簿中 C 的详细字段 m 的数量。
  3. C_P = 1/2 × (C_P + 1/m)

内容 :我们认为内容是以下文件类型之一:音频(音乐)、视频、图片(图像)和文档。

除了捕获应用程序访问了“多少”文件和“什么类型”的文件,我们还考虑了文件的属性和标签,以量化该文件对用户的隐私敏感性,例如,用户生成的内容被认为比非用户生成的内容更敏感。同样,从隐私影响的角度来看,包含地理标记信息的内容比未标记的内容评级更高。

考虑到以上方面,我们在下面给出了计算内容隐私度量 D_P 的增量算法。我们考虑三个可配置的比例因子 ypt ,定义如下:

  • y :文件类型因子
  • p :属性因子
  • t :标签因子

假设上述所有比例因子的默认值为 0.5。

对于 app 访问的每个文件 fD_P 更新如下:

  1. 检查 f 是否属于用户:只考虑驻留在用户文档目录中的文件。这是对应用程序文件访问的折扣,以检索应用程序拥有的媒体,如图标。这里假设设备上存在一个“文档”文件夹,默认情况下,所有用户生成的文件都存储在该文件夹中。如果是,继续;否则退出。
  2. 根据文件的修改时间戳,检查 f 是否是唯一的,即第一次被访问。这里,我们将具有不同修改时间戳的文件视为“唯一的”文件访问。这是基于这样的观察:修改过的文件有可能向攻击者提供额外的信息。如果唯一,继续;否则退出。
  3. 计算比例因子 s:最初让 s = 1。如果文件类型是“图像”或“视频”,那么 s = s×y 。文件类型由文件扩展名决定。如果 f 不是用户所有,那么 s = s × p 。文件的创建者/所有者属性用于确定这一方面。如果 f 没有地理标记,那么 s = s × t 。检查文件的 EXIF 标签以确定它是否被地理标记。
  4. 根据文件类型,扫描相应的用户目录“音乐(音频)”、“视频”、“图片”、“文档”目录,以计算该目录中文件的数量。
  5. 最后更新 D_P = 1/2 (D_P + s/n)

C.UI/UX

在本节中,我们将描述隐私面板用户界面流程。截图如图 2 所示。屏幕(1)是开始屏幕,在调用 PP 时向用户显示。它列出了安装在移动设备上的应用程序及其相应的隐私等级。隐私等级相对于三个类别显示:位置、联系人和内容。点击(轻触)其中一个应用程序行,进入相应应用程序的详细隐私报告屏幕(2)。我们以天气应用程序为例进行说明。除了隐私相关信息,屏幕(2)有两个文本字段:“来自开发者”和“关于隐私面板”。后者是不言自明的,给出了关于应用软件、底层逻辑、安装版本等的高级信息。“来自开发者”提供了一个 RSS feed 类型的滚动消息框。消息可能来自应用程序开发人员,向用户解释为什么需要关于数据类别的特定访问级别(导致更高的隐私侵犯评级)。

2:隐私面板用户界面(作者图片)

点击其中一个类别图标(位置、联系人、内容)会显示与该类别相对应的隐私详细信息。让我们首先考虑位置类别—屏幕(3)。该屏幕显示了根据应用程序到目前为止在设备上的位置访问计算的位置隐私评级。评级被抽象为三个级别,较高的隐私评级对应于较高的入侵级别,即具有低隐私评级的应用程序更安全,即入侵性更低。

屏幕(3)的下半部分显示了基于应用程序在过去一周访问的用户位置数据的频率和准确性访问模式。用户可以前后滚动来查看前几周各自的模式。每日频率模式是基于当天用户位置数据被请求的次数来计算的。可以以不同的精确度请求用户的位置,这对用户的隐私和设备电池有不同的影响。应用程序要求的每日平均定位精度水平绘制在屏幕(3)中。

关于应用程序位置隐私的最后一个方面是显示应用程序访问用户位置数据的实际位置。这有助于用户了解应用程序更活跃的位置,例如,恶意应用程序是否针对特定的敏感用户位置(如家庭、办公室等)执行选择性分析。为了显示这些信息,PP 应用程序还需要访问用户位置数据(以记录应用程序访问的实际位置),因此只有在用户明确“选择加入”后才能启用该功能。启用后,以热图屏幕(4)的形式向用户显示该信息。

在位置行上,点击屏幕(2)中的内容或联系人图标,分别进入屏幕(5)或(6)。屏幕(5)和(6)显示与内容和联系人类别相关的应用访问信息。内容隐私评级是根据应用程序对手机上物理文件、媒体和消息的访问来计算的。联系人评级是根据应用程序对地址簿条目的访问以及与联系人通信相关的元数据来计算的,例如,给哪个联系人打电话/发电子邮件的时间和频率。

一旦用户意识到自己的个人数据是如何被应用程序收集的,以及这些数据对隐私的影响,他很自然会想要制定政策来规范未来的应用程序行为。请注意,设置策略并不一定等同于阻止应用程序执行某些操作。保护措施可以是当应用执行某个动作或者达到某个预定义的隐私等级阈值时的简单通知消息。

图 3:隐私控制界面(作者图片)

图 3(a)给出了 PP 用户界面以生成控制应用对设备资源级别的访问的低级隐私策略。该界面主要面向了解设备资源/内部结构的高级技术用户。遵循这项工作的总体理念,为“普通”用户提供用户友好的隐私控制,我们允许这些用户通过为每个数据类别指定阈值隐私评级来监管应用程序。参考之前讨论的图 2 的屏幕(3 ),图 3(b)示出了天气应用的允许位置访问的用户设置为“低”。实施此类政策显然需要遵循“持续更新”执行模式,根据应用程序执行的每次拦截访问,重新计算相关隐私类别评级。

三。结论

在这项工作中,我们考虑了从隐私影响方面量化移动用户数据的应用访问的问题。我们提出了三个用户数据类别的隐私指标,即位置、联系人和内容。这些指标允许将与这些类别相关的应用访问映射到高级隐私评级。然后,我们概述了我们提出的 PP 的设计和架构,它既向用户显示隐私相关信息,又允许他们根据计算的评级来指定策略。最后,我们给出了性能结果,显示了我们的概念实现的可行性。

我们认为,所提出的方法为实现让普通用户更容易使用和访问移动隐私保护的长期目标奠定了基础。

参考

  1. W.Enck,P. Gilbert,B.-G. Chun,L. P. Cox,J. Jung,P. McDaniel 和 A. Sheth,“TaintDroid:智能手机实时隐私监控的信息流跟踪系统”,2010 年,OSDI。
  2. A.禅三钗,“iPhone 缺陷允许应用程序访问您的联系人。”
  3. J.-L. Boyles,A. Smith 和 M. Madden,“移动设备上的隐私和数据管理”
  4. 页(page 的缩写)I .项目,“调查:移动用户警惕应用程序侵犯隐私。”
  5. Y.周,张,江,V. Freeh,“驯服窃取信息的智能手机应用程序(Android),”于 2011 年。
  6. D.比斯瓦斯,“智能手机隐私政策改变管理”,2012 年 MUCS(链接)
  7. D.比斯瓦斯、内费多夫和涅米,“分布式和最小使用控制”,《面向服务的计算和应用》,第 6 卷,第 4 期,2012 年(链接)

用 Python 进行统计建模:三个必须知道的“S 模块”

原文:https://towardsdatascience.com/statistical-modelling-with-python-the-three-must-know-s-modules-79fa393e5640?source=collection_archive---------13-----------------------

图一。使用图形子模块的 regressionplots.plot_fit 子模块绘制的美国谋杀率(%)与贫困水平(%)的曲线图。作者出于教育目的创作的图形。

我简介

Python 是现存的最重要和最有效的多任务软件之一,它为用户提供了许多库和模块来执行不同的任务。在数据科学的背景下,统计建模是数据科学家日常工作的重要组成部分,Python 拥有不同的库和模块,可以执行高效的统计计算和可视化。

在本文中,我将讨论最重要的 Python 库/模块,当您需要执行统计建模和计算时,您必须了解这些库/模块并时刻准备着。我下面讨论的库,在我看来是最好的,我称它们为" S-modules ",因为它们都以字母" S "开始,以反映它们的统计性质。在本文中,我假设读者了解 Python 及其最重要的库。

1.统计模型

如果 Python 中有一个统计库/模块是一个人必须知道的,那么这个模块就是 Statsmodel s,当我需要执行统计计算时,它可能是我最喜欢的 Python 模块。这个库/模块基于 SciPy Python 库,它是一个完整的模块,允许用户执行许多统计分析操作。

通常使用以下 Python 语法调用该 Python 模块:

[In] **import** statsmodels.api **as** sm

您可以注意到,这个模块具有 api 扩展,与其他 Python 库和模块略有不同。在许多情况下,Statsmodels 具有与其他 Python 模块不同的命名法和语法。例如,如果想要出于统计目的分析数据,Statsmodels 使用 endogexog 术语来表示 xy 变量。endog 是内源性一词的缩写,本质上的意思是由系统内的因素引起的。另一方面,exog 是单词 exogeneous 的缩写,本质上的意思是由系统外的因素引起的。因此,在处理 Statsmodels 模块文档时,应该记住这个术语。

在统计建模中,用户可以用 Statsmodels 做什么?正如我上面已经提到的,有了这个模块,用户几乎可以在这个领域做任何事情。在多元线性回归的情况下, OLS 子模块允许用户使用普通最小二乘法(OLS)进行多元线性回归。如果用户想要执行加权最小二乘法(WLS),那么可以使用 WLS 子模块。如果用户想要执行,可以使用广义最小二乘()子模块。此外,在回归的背景下,Statsmodels 可以执行 ANOVA 测试、具有离散因变量的回归 y、线性混合效应模型等。

对于纯粹的统计计算和分析,Statsmodel 有一个名为stats的子模块,允许用户执行多个统计测试。通常使用以下 Python 语法调用该子模块:

[In] **from** statsmodels **import** stats

通过 stats 子模块,用户可以根据遇到的具体问题进行大量的统计测试。仅举几个例子,通过 stats 子模块,您可以执行不同的卡方拟合优度测试、Anderson-Darling 测试、Ramsey 重置测试、常态综合测试等。

像 Statsmodels 这样的完整统计模块不能没有图形子模块。实际上,该模块还包括用于统计结果绘图和可视化的 图形 子模块。在这一点上,我给出一个具体的例子来说明如何使用 Statsmodels 来可视化统计结果是非常有用的。

Statsmodels 还有一个名为数据集的子模块,用户可以选择默认的预加载数据集进行统计建模。在本文中,我选择了 statescrime dataset 作为示例,该数据集包括许多与美国犯罪相关的参数。我运行以下 Python 代码来显示 statecrime 数据集的几个条目,如下图 2 所示:

[In]: **import** statsmodels.api **as** sm[In]: **import** matplotlib.pyplot **as** plt[In]: data = sm.datasets.statecrime.load_pandas().data[In]: data.head(10)[Out]: 

图二。statecrime 熊猫数据集的前十行。

现在,假设我想使用图 2 中的数据集对贫困率与谋杀率和 hs_grad 进行多元线性回归,并绘制仅 T2 贫困率与谋杀率的结果。为了实现这一点,我使用带有以下 Python 代码的 graphics.plot_fit 子模块:

[In]: y = data['murder'] # Select the murder rate column from the data dataset as the exonog variable.[In]: X = data[['poverty', 'hs_grad']].copy() # Create the design matrix by using the poverty rate and hs_grad columns of data dataset.[In]: X['constant'] = 1 # Add a constant term of to the design matrix as the intercept.[In]: model = sm.OLS(y, X) # Perform ordinary OLS on the data[In]: results = model.fit()[In]: fig, ax = plt.subplots(figsize = (10, 6))
fig = plot_fit(results, 0, ax=ax, vlines = True)
ax.set_ylabel("Murder Rate (%)")
ax.set_xlabel("Poverty Level (%)")
ax.set_title("Murder Rate vs. Poverty Level in US")[Out]: plt.show()

如果您运行上面的代码,您将得到本文顶部所示的图 1。垂直线代表拟合数据对真实数据(谋杀数据)的线性回归的残差。如您所见,graphics . regression plots . plot _ fit子模块使用两个预测值执行多元线性回归(在本文考虑的情况下),并且只绘制一个预测值的结果。该示例显示了 Statsmodels 模块为用户提供的许多选项。还有许多其他的我在这里没有讨论。****

2.统计数据

SciPy 是用于科学计算的 Python 库。这个库有许多模块和子模块,这使它成为科学计算的最佳库之一。SciPy 有一个名为stats的模块,负责统计建模和计算。该模块是 Statsmodels 模块的前身,后者是前者的扩展。这两个模块互为补充,在许多情况下,它们也有重叠的功能,但调用语法不一定相同。

stats模块允许用户执行不同的统计计算,如汇总统计、频率统计、计算相关函数、计算单变量和多变量概率分布(连续和离散)、蒙特卡罗模拟、生成随机变量等。

甚至在这一节中,为了说明 SciPy 的性能,我给出了一个具体的例子。这里我用的是和我之前文章中一样的数据,其中 x 数组是不同国家的人均 GDP,y 数组是生活满意度值。对于这些数组,我使用以下 Python 代码,使用 stats 模块来计算皮尔逊相关系数(r) 及其 p 值:**

**[In]: **import** numpy **as** np[In]: **from** scipy **import** stats[In]: x = np.array([ 56755.72171242,  44178.04737774,  40991.80813814,   8814.00098681, 43585.51198178,  13574.17183072,   6175.8760297 ,  17829.69832237, 53254.85637009,  17522.23018625,  42784.69836164,  36638.18492916, 41086.72967373,  18167.77372717,  12706.89121489,  52564.42917946, 61995.42280258,  35776.79516181,  30230.22630213,  34524.46986093, 13774.60527391,  14258.22933451, 101376.49657434,   9616.64500569, 45175.23189338,  38615.99518491,  74355.51585756,  12578.49547344, 19242.3664711 ,  16310.988409  ,  20881.76692993,   5734.63362915, 25732.01836475,  51545.48360953,  82081.59716162,  11006.2497364 , 44974.83187718,  56839.38177423])[In]: y = np.array([7.3, 7.1, 6.9, 6.4, 7.4, 6.5, 6.3, 6.7, 7.6, 5.7, 7.6, 6.5, 7.0, 5.4, 5.6, 7.5, 7.0, 7.2, 6.0, 5.9, 5.9, 5.9, 6.9, 6.5, 7.4, 7.3, 7.6, 6.1, 5.4, 6.2, 5.9, 4.7, 6.3, 7.3, 7.5, 5.5, 6.8, 6.9])[In]: (r, p_value) = stats.pearsonr(x, y)[Out]: (0.7202871953226558, 3.426556470064707e-07)**

从上面的 Python 代码中可以看到,皮尔逊相关系数与我之前的文章中计算的相吻合。在假设 x 来自正态分布的情况下,使用 pearsonr 子模块计算的双边 p 值有效,在本例中,这一条件不一定适用于 x。**

人们可以更详细地计算临界 zt 值。例如,在线性回归的情况下,计算线性回归系数的临界 zt 值,即截距和斜率。假设有人有兴趣计算 95%置信水平下的临界值。在这种情况下,需要运行以下 Python 代码:

**[In]: n=38                            # Number of degrees of freedom[In]: alpha=0.05                      # Significance level[In]: t_critical = stats.t.ppf(1-alpha/2, n-2) # ppf is the inverse function of cdf[In]: print("Model t_critical: ", t_critical)[Out]: Model t_critical:  2.0280940009804502 # Critical value of T statistic at alpha=0.05**

您可以看到,为了计算临界 t 值,我使用了 stats 子模块 t.ppf()。该函数是累积分布函数的反函数,它计算以百分比值表示的不同置信水平值的 t 值。你可以在我的 GitHub 页面上看到,在对 x 和 y 数组进行线性回归的情况下,t-score 值为 t_s = 6.23 ,大于 t_critical = 2.02 ,从而拒绝了 xy 不相关的零假设。

3.海生的

Seaborn 是另一个优秀的统计数据可视化 Python 库。这个 Python 库大部分是建立在 Matplotlib 库的基础上的,它也包含了许多熊猫库的功能。Seaborn 专门用于可视化统计数据和图表。它可以很容易地用 PyPi 命令 pip install seaborn 或者通过命令 conda install seaborn 用 Anaconda 发行版安装。一般来说,seaborn 需要不同的库依赖,比如 NumPy、SciPy、Matplotlib 和 Pandas。如果您的 Python 发行版中没有这些库,那么它们将与 seaborn 一起安装。

使用 seaborn,您可以:

  1. 可视化统计关系
  2. 可视化数据分布
  3. 可视化回归模型
  4. 绘制分类数据
  5. 控制图形美感
  6. 构建多地块网格
  7. 选择调色板

Seaborn 有许多选项来绘制和可视化统计结果。作为示例,考虑第 2 节图 2 中的熊猫数据集。假设我想寻找贫困率和谋杀率之间任何可能的线性关系。为了看到这一点,我运行下面的 Python 代码,并在下面的图 3 中显示结果:

**[In]: **import** seaborn **as** sns[In]: sns.set(style = "whitegrid")[In]: fig = sns.lmplot(x="poverty", y="murder", data=data)[Out]:**

图 3。使用 seaborn 库的 sns.lmplot 子模块绘制美国谋杀率(%)与贫困率(%)的曲线图。作者出于教育目的创建的图像。

如您所见,通过 seaborn,您可以使用该库提供给用户的许多不同选项,并创建出色的最终结果。显然,在这里我没有展示 seaborn 的许多其他功能,我邀请读者去体验它们。

结论

在本文中,我讨论了 Python 中三个最重要的统计模块。如果你想进行统计建模和计算,这些是你应该一直带在身边的模块。它们中的每一个都有几乎独一无二的特征,并且在大多数情况下,它们也是互补的。Statsmoldels 应该是统计建模的第一模块,seaborn 应该是第二模块,因为它允许您创建统计建模的图形,而不需要用户进行任何计算或编写额外的 Python 代码来进行这些计算。

如果你喜欢我的文章,请与你可能对这个话题感兴趣的朋友分享,并在你的研究中引用/参考我的文章。不要忘记订阅将来会发布的其他相关主题。

数据科学访谈中的统计时刻

原文:https://towardsdatascience.com/statistical-moments-in-data-science-interviews-bfecd207843d?source=collection_archive---------6-----------------------

数据科学家的数学复习

数据科学家的基本数学从零开始解释

矩是用来描述分布的一组统计参数。计算很简单,因此经常被用作对数据的第一次定量分析。对数据的良好理解应该是训练任何高级 ML 模型之前的第一步。它可以最大限度地减少选择方法和解释结果所需的时间。

在物理学中,指的是质量,并告知我们物理量是如何定位或排列的。在数学中,指的是类似的东西——概率分布——一个解释实验不同可能结果的概率的函数。为了能够比较不同的数据集,我们可以使用前四个统计矩来描述它们:
1。期望值
2。差异
3。偏斜度
4。峭度

我们一起来过一遍细节吧!

文章分为两部分:
一、数学复习
二。数据科学访谈中与主题相关的问题

一.数学复习

1.期望值

第一个矩-期望值,也称为期望、数学期望、平均值或平均值,是变量可以取的所有值的总和乘以该值出现的概率。可以直观地理解为算术平均值:

当所有结果都有相同的发生概率时,这是正确的(例如,掷骰子——从 1 到 6 的所有数字都有相同的机会被掷出)。包含每个事件概率的更一般的等式是:

对于掷骰子,当每个值出现的概率为 1/6 时,期望值为:

或者:

对于概率相等的事件,期望值与算术平均值相同。这是最受欢迎的集中趋势的量度之一,通常被称为平均值。其他常见的措施有:

  • 中间值 —中间值
  • 模式— 最可能的值。

例如,取 7 个值的集合:2、4、4、5、8、12、14,我们得到:

  • 意思是:

  • 中值- 这是“中间”值,正好在数据集的中间。在我们的例子中,这是 5,因为它分隔了数据的大部分和小部分:我们有 3 个小于 5 的值和 3 个大于 5 的值。对于具有偶数个值的数据集(例如,向我们的数据集添加 15 个值),我们取中间的两个值,并计算它们的平均值:

  • 模式 -一组数据中出现频率最高的值。对于我们上面的例子,模式是 4,因为它出现了两次。

2.差异

第二个中心矩是方差。方差解释了一组值如何围绕它们的期望值分布。对于 n 个同样可能的值,方差为:

其中 μ 为平均值。所以方差很大程度上取决于期望值。

对于上面的样本数据序列,方差为:

其中 n 是 7,因为我们的数据集中有 7 个元素,并且 μ 是 7,如上所述。

当值的分布较低且均值相同时,方差也较低,例如:

标准偏差

标准偏差是方差的平方根,由于其单位与 X 的单位相同,因此通常被使用:

方差和标准差告诉我们数据围绕平均值分布的强度,如下图所示:

方差/标准差越大(如蓝线),平均值的分布越广。如果方差较低,累积的值越接近平均值(红线),峰值越高。
下图总结了前两个时刻的诠释:

图片由作者提供。

如果您想了解更多关于发行版以及如何解释它们的信息,请查看以下内容:

3.歪斜

偏斜度是第三个统计矩,用于度量数据关于其均值的不对称性。计算偏斜度的公式为:

根据偏度,我们可以区分三种类型的分布:

  • 对称分布:如上例。两个尾部都是对称的,偏斜度等于零。
  • 正偏斜(右偏斜,右尾,向右偏斜 ) : 右尾(数值较大)较长。这告诉我们“异常值”的值高于平均值。
  • 负斜 ( 左斜,左尾,向左斜 ) :左尾(数值小)较长。这让我们知道了值低于平均值的“异常值”。

一般来说,偏斜度会以下列方式影响均值、中值和众数的关系:

  • 对于对称分布:均值=中位数=众数
  • 对于正偏态分布:众数
  • 对于负偏态分布:均值

但这并不适用于所有可能的分布。例如,如果一条尾巴很长,但另一条很重,这可能行不通。调查数据的最佳方法是计算所有三个估计量,并根据结果得出结论,而不是一般规则。

4.峭度

第四个统计矩是峰度。它关注分布的尾部,并解释分布是平坦的还是具有高峰。峰度告诉我们,我们的分布在极值上是否比正态分布更丰富。

对于用于计算峰度的公式没有严格的共识,不同的程序/软件包使用三种主要的公式。一个好习惯是,在对数据下结论之前,先检查你的软件使用的是哪一种。包含负 3 修正项的公式指的是过剩峰度。所以,超额峰度等于峰度减 3。

一般来说,我们可以区分三种类型的分布:

  • 中等峰度——峰度为 3 或过度峰度为 0。这组包括正态分布和一些特定的二项分布。
  • 尖峰值 —峰度大于 3,或过度峰度大于 0。这是一种尾部较宽、峰值较窄的分布。
  • 扁峰度— 峰度小于 3 或为负值为过度峰度。与正态分布相比,这是一个尾部非常细的分布。

对于那些视觉记忆更好的人来说,看看我的草图:

我们经历了前四个统计时刻。现在是我们检查面试问题的时候了。

二。数据科学访谈中的问题

1。什么是正态分布的峰度?

这是个棘手的问题!正如《数学复习》中提到的,对于用于计算峰度的公式没有严格的共识,通常满足三个公式。最重要的区别,尤其是对于大样本而言,方程的选择并不那么重要,是了解您的公式是否包含-3 的校正项。如果是这样,该公式将计算超额峰度。这意味着正态分布可能有峰度为 3 或过度峰度为 0。但要小心,因为过度峰度有时也会缩短为更简单的峰度

一些语言允许你在计算中选择公式的类型(例如 R)或者定义你想要使用的正态峰度的定义(Python)。了解你计算的内容将允许你将结果与正态分布进行比较并得出结论。

2。你什么时候会考虑用中位数代替均值?

样本均值是对未知总体均值的一种众所周知的常用估计。然而,它容易受到异常值的影响,尤其是当样本量很小时。所以,如果数据集很小,有偏差,并且有异常值,那么检查中位数是值得的。

3。你想投资你的钱,并有两种可用的回报分布:正的和负的偏斜。你会选择哪一个,为什么?

这里没有好与坏的答案,只要你能给出你选择的理由。这取决于你的风险偏好。

就个人而言,在均值和方差保持不变的情况下,我会投资正偏斜。一般来说,获得高回报的机会越大,亏损的可能性就越大。所以,在选择之间:

  1. 85%的机会赢得至少 1000 美元,1%的机会输掉 99000 美元或更多
  2. 1%的机会赢得至少 99000 美元,85%的机会输掉 1000 美元或更多

我选择第一种选择——相对于彩票大奖的希望来说,赢的可能性更小,但可能性更大。但是选择取决于你!

4.在你看来,一个国家的平均工资能提供多少信息?

我认为它应该总是与中位数一起报告。这样,我们可以更多地了解社会中的工资分配。举个例子,如果有一小群人的薪水超级巨大,但是其余的人赚的很少,那么在比较中位数和均值的时候就可见一斑了。从这两个估计量中,我们可以理解体面的工资是可以被视为正常的,还是被视为异常值。当然,薪水也应该与一个特定国家的生活成本进行比较,以便更好地了解生活质量。

感谢阅读!

我们一起经历了前四个统计时刻:期望值、方差、偏度和峰度。我希望这对你来说是一次激动人心的旅行。

记住,学习(数学)技能最有效的方法是实践。所以,不要等到你觉得“准备好了”,就拿起笔和纸或者你最喜欢的软件,自己试几个例子。我为你祈祷。

您可能还喜欢:

https://kujaga.medium.com/math-refresher-for-data-scientist-part-1-matrices-88620a92d046

我很乐意在下面的评论区听到你的想法和问题,直接通过我的 LinkedIn 个人资料或akujawska@yahoo.com 联系我。回头见!

统计预言和预测的艺术

原文:https://towardsdatascience.com/statistical-prophecy-and-the-art-of-forecasting-3b80b5547ab7?source=collection_archive---------55-----------------------

利用脸书的先知进行平衡预测

作者:普拉森·比斯瓦斯和钱丹·杜吉亚

资料来源:Unsplash 的 Hannah Jacobson

前奏: 《追加保证金通知》被认为是 2008 年金融危机前后制作的最好的电影之一。如果你没看过,一定要看。有着令人震惊的资本主义故事和强大的演员阵容——这的确是一个成功。虽然电影中有许多精彩的对话,但有一段对话一直留在我的脑海中,其中银行的首席执行官/董事长对他的下属说:“你想知道为什么我会和你们一起坐在这张椅子上吗?我是说,为什么我能挣大钱。我来这里只有一个原因。我在这里猜测音乐从现在起一周、一个月、一年后会发生什么。就是这样。仅此而已”。

在这种情况下,音乐意味着“情况”。也就是说,一切都是为了预测未来“一周、一个月、一年以后”,你预测得越好,你就能赢得越大的比赛…

已经有很多统计工具,如 ARIMA(X)、指数平滑、LSTM、误差修正模型等被大量用于预测。然而,使用这些技术,模型的质量通常是不合格的,因为调整这些模型并不容易——除非您的团队中有一个博学的人。凭借极其直观的超参数,Prophet 成为了游戏规则的改变者。

正如他们所说,银行业是管理风险的行业,当你的部门被称为“银行中的银行”时,你就知道自己在主持大局。

简而言之,任何银行的资产负债管理(ALM)部门处理两个关键职能:管理货币(存款)的供应和满足贷款账簿的需求。这些功能反过来驱动银行的两个最重要的 KPI——流动性和盈利能力(与借贷利率差相关)。

从传统银行业的角度来看,ALM 一直是并且仍然是任何银行的核心职能。然而,随着竞争压力的增加,ALM 桌面的优化受到了极大的关注,这在商业上非常有意义。假设有一笔 5 亿美元的贷款,即使 ALM 设法在一年内为一家银行提高 100 个基点的回报率。这相当于节省了 500 万美元。

利率等市场变量。受全球经济的驱动,而且总是难以准确预测。但是,如果 ALM 部门能够准确预测贷款余额提取/预付和存款流入/提取,银行将能够以最佳方式管理资金,从而提高流动性和盈利能力。

预测贷款和存款余额具有挑战性,因为有太多因素在起作用:

1.趋势(例如,某一特定存款产品可能会在市场上大获成功,而且随着口碑的传播,存款可能会在其饱和之前显著增加)

2.期权性(例如贷款提前还款、基于行为的存款支取)

3.季节性(例如,存款余额在某些月份随着人们从税务部门获得退款而增加)

4.节假日(如节日假期存款余额减少)

5.银行策略(例如,为了提高某一特定产品的可销售性,银行可能会向客户提供更高的回报或开展强有力的营销活动)

6.其他行为维度

有鉴于此,传统技术无法产生强有力的模型。关键问题在于——这些技术可能很难调整,并且通常太过不灵活,无法结合某些假设或试探法。

这就是脸书的先知模式正在获得巨大的接受。Prophet 将预测问题归结为曲线拟合,而不是明确考察时间序列中每个观察值基于时间的相关性。此外,它在逻辑上将时间序列函数划分为:

最后,它提供了极为直观的超参数来管理这些组件中的每一个。下文将讨论一些关键参数。

A.“趋势”相关超参数:

a.变化点T16:这是一个超参数,用于捕捉时间序列中的任何突变。使用此参数可以轻松捕获未来的计划战略转移(#5,在上面的列表中)。请注意,如果用户没有提供任何变化点,Prophet 会尝试自己确定这些变化点。

b. 变化点 _ 优先 _ 规模:并非所有战略变化都有影响。其中一些比另一些更有影响力。此超参数调整趋势的强度。默认值为 0.05 时,可以减小该值以使趋势更灵活,反之亦然。

c.增长:时间序列的趋势可以在一定水平上继续增加/减少或饱和。再次举例来说,一个新的营销活动可以促进销售在短期内,但它会饱和一段时间后。此参数可帮助定义趋势的上限和下限。(以上列表中的第 1 位)

B.”季节性相关超参数:

这就是 Prophet 明显优于其他模型的地方。(以上列表中的#3)

a.季节性 _ 模式:可以设置为“加法”或“乘法”。如果预计季节模式在未来保持相似,则使用“加法”模式(2023 年的季节性将与 2015 年的季节性相似),否则使用“乘法”模式。

b.y 的 3 个超参数早期季节性,周季节性,日季节性。根据数据的不同,这些可以设置为真或假

c.季节性 _ 先验 _ 规模:类似 changepoint _ 先验 _ 规模。这反映了不同程度的季节性模式。

C.假期相关参数:

a.假日列表:我们可以向模型传递一个定制的假日列表来捕捉任何假日(上面列表中的#4)

b. holidays_prior_scale: 类似于 changepoint_prior_scale。这反映了各种假期的不同影响。

上面列表中的#2 和#6 是作为利用“附加模型”框架的“趋势”拟合的一部分捕获的。

因此,总的来说,除了 Prophet 提供了简单的超参数外,值得注意的是,即使使用默认参数,模型也会自动调整到很高的精度。因此,它不需要很强的时间序列建模能力。

下面是一个通用代码,它遍历各种参数,目标是获得最小的 MAPE。

*from fbprophet import Prophet**from sklearn.model_selection import ParameterGrid**params = {‘growth_mode’:(‘multiplicative’,’additive’),**‘seasonality_mode’:(‘multiplicative’,’additive’),**‘changepoint_prior_scale’:[0.1,0.2,0.3,0.4,0.5],**‘holidays_prior_scale’:[0.1,0.2,0.3,0.4,0.5],**‘n_changepoints’ : [50,100,150]}**grid = ParameterGrid(params)**count = 0**for p in grid:**count = count+1**start=start_date**end=end_date**model_parameters = pd.DataFrame(columns = [‘MAPE’,’Parameters’])**for p in grid:**Pred = pd.DataFrame()**random.seed(120)**train_model =Prophet(growth = p[‘growth_mode’],**changepoint_prior_scale = p[‘changepoint_prior_scale’],**holidays_prior_scale = p[‘holidays_prior_scale’],**n_changepoints = p[‘n_changepoints’],**seasonality_mode = p[‘seasonality_mode’],**weekly_seasonality=True,**daily_seasonality = True,**yearly_seasonality = True,**holidays=holiday,**interval_width=0.95)**train_model.add_country_holidays(country_name=’US’)**train_model.fit(X_train)**train_forecast = train_model.make_future_dataframe(periods=42, freq=’D’)**train_forecast = train_model.predict(train_forecast)**test=train_forecast[[‘ds’,’yhat’]]**Actual = df[(df[‘ds’]>start) & (df[‘ds’]<=end)]**Mape = mean_absolute_percentage_error(Actual[‘y’],abs(Pred[‘yhat’]))**print(‘MAPE is : ‘,MAPE)**model_parameters = model_parameters.append({‘MAPE’:Mape,’Parameters’:p},ignore_index=True)*

#一旦收到最终参数,您就可以构建最终模型(final_model)并可以预测 n 个周期。

*future = final_model.make_future_dataframe(periods=18, freq=’D’)**forecast = final_model.predict(future)*

顶端的樱桃:

Prophet 在某种程度上只是自回归(AR)模型的扩展,其中除了使用滞后变量之外,还使用输入变量的傅立叶变换来生成补充特征。这可以更好地调整模型,从而提高性能,并提供分解结果的能力,以获得更好的可解释性。

这里的关键问题是,时间序列数据很少在一段时间内遵循单一模式。为了解决这个问题,引入了 NeuralProphet 来帮助映射非线性函数来逼近任何连续函数,从而给出更好的拟合。这里不涉及太多细节,但要记住 NeuralProphet 的关键特性。

1.PyTorch 的梯度下降优化引擎使建模过程比 Prophet 快得多

2.自相关使用自回归网络建模

3.使用单独的前馈神经网络对滞后回归量进行建模

4.前馈神经网络的可配置非线性深层

5.您可以使用“neuralprophet”包在 python 中定制损耗和度量模型。

结论:

准确的余额预测是任何银行的关键需求之一。围绕余额预测的其他用例可以围绕财务规划和分析、PPNR (CCAR)建模、业务单位级别的余额预测等。

Prophet 是一个优秀的软件包,它为时间序列预测提供了很大的准确性。随着越来越受欢迎,这可能成为银行在未来进行时间序列预测时所依赖的关键工具之一。在那之前,这是一场竞赛,一些 ALM 部门肯定会比其他部门多创造额外的基点。

时间会证明一切!..在那之前,祝你学习愉快!!

免责声明:本文中表达的观点是作者以个人身份表达的观点,而非其各自雇主的观点。

使用 Python 进行过程改进的统计采样

原文:https://towardsdatascience.com/statistical-sampling-for-process-improvement-using-python-9decc7b8288d?source=collection_archive---------24-----------------------

使用样本数据来估计电梯零件供应商的客户服务中处理客户订单的平均提前期。

统计抽样以估计平均订单处理提前期—(图片由作者提供)

作为供应链管理的一个重要组成部分,客户服务是你的公司让你的客户感受到你正在销售的产品和业务的地方。

一个重要的绩效指标是从收到客户订单到将其发送到仓库进行准备的平均交付时间。

在本文中,我们将介绍一种使用统计抽样的方法,以使用200 个观察值的样本来估计总平均提前期

💌新文章直接免费放入您的收件箱:时事通讯

**SUMMARY**
**I. Scenario
Problem Statement** You are the **Customer Service Manager of an elevator parts supplier** that produce and deliver engine parts for elevators.
**Question** Can you estimate the average processing time with a confidence interval of 90% using your sample data?
**II. Statistical Sampling** 1\. Notations 
2\. Application of the Central Limit Theorem3\. Confidence Interval4\. Final estimation **III. Conclusion**

一.情景

问题陈述

你是电梯零件供应商的客户服务经理,该供应商生产并交付电梯的发动机零件。

您的团队负责订单处理:

  • 客户通过电话或电子邮件发送订单,要求交货时间
    (例如:客户订购 5 台 SKU X,希望当天 10:00 交货)
  • 您的团队确认订单,并将其分配到最近的仓库进行准备和装运。
  • 订单由快递公司准备并从仓库发货。

你最近收到了许多客户的投诉,因为交货晚了。据仓库经理说,主要是由于客服在订单处理过程中的延误。

在 3 个月的时间里,您测量了随机选择的操作员的订单处理时间,并收集了 200 个观察结果。

问题

您能否使用您的样本数据,以 90%的置信区间估计平均处理时间?

http://samirsaci.com

二。统计抽样

由于我们无法测量所有操作员对每个订单的平均处理时间,我们希望使用这些样本记录来估计总体平均处理时间。

1。符号

为了简化理解,让我们介绍一些符号:

符号—(作者提供的图片)

2.中心极限定理的应用

在之前的一篇文章(链接)中,我们一直在使用中心极限定理(CLT)来估计随机变量 P(X≥k)的概率,假设 X 遵循正态分布。

CLT 还告诉我们:

方程式—(图片由作者提供)

3.置信区间

我们的目标是知道总体均值范围[ -b,+b],置信度为 90%。

方程式—(图片由作者提供)

通过构造单位正态分布,我们知道对于 P(-z≤Z≤z) = 0.9,我们得到 z = 1.64

最后,我们可以得到我们的估计范围或总体均值

方程式—(图片由作者提供)

4.最终估计

count    200
mean      22.705
std        6.81
min        4.0
25%       18.0
50%       23.0
75%       27.0
max       41.0

方程式—(图片由作者提供)

我们有,

n = 200
x̄ = 22.705 (min)
s = 6.81 (min)The confidence interval is [21.96, 23.54]

超出

如果您对持续改进的统计数据感兴趣,可以看看这个系列的文章,它们涵盖了使用 Python 实现的精益六适马概念

https://www.samirsaci.com/lean-six-sigma-with-python-kruskal-wallis-test/ https://www.samirsaci.com/lean-six-sigma-with-python-logistic-regression/ https://www.samirsaci.com/lean-six-sigma-with-python-chi-squared-test/

三。结论

关注我,了解更多与供应链数据科学相关的见解。

对于 90%的置信水平和适度的实验努力,我们对订单处理的平均交付时间有一个非常好的估计。

这种方法可以用在过程性能测量成本高、费时费力的情况下。

但是,您需要在实验协议中投入精力,以确保您的样本数据是基于随机选择的操作符构建的。

关于我

让我们在 Linkedin 和 Twitter 上连线,我是一名供应链工程师,正在使用数据分析来改善物流运作和降低成本。

如果你对数据分析和供应链感兴趣,可以看看我的网站

https://samirsaci.com

参考

[1]用 Python 实现过程改进的中心极限定理,Samir Saci, Link

FAANG 在 2021 年问这 4 个 Python 模拟

原文:https://towardsdatascience.com/statistical-simulation-in-python-part-2-91f71f474f77?source=collection_archive---------21-----------------------

破解数据科学面试

数据科学和数据工程面试的必读材料,第 2 部分

eberhard grossgasteiger 在 Unsplash 上的照片

2021 年 1 月 10 日更新

介绍

统计模拟是数据科学/工程面试中测试最重的题目!如果我们看一下 Glassdoor 上发布的 DS 面试问题,统计模拟是所有大型科技公司都希望其申请人擅长的关键技能。

访谈场景可能会被要求对 A/B 实验进行功效分析,或者构建二项式分布来模拟编程语言 R 或 Python 中的用户行为。

这些问题并不难,但需要对基本统计学有深刻的理解,并有流利的编程技巧。如果没有经过深思熟虑的练习,这些问题可能会让你犯错。这篇文章介绍了数据科学访谈中最常测试的 4 种统计分布,以及 Python 中的实时代码解决方案。

在之前的帖子中,我已经介绍了统计思维和 R 代码的基础知识。如果你错过了,这里是入口:

免责声明:我假设我的读者朋友理解统计学基础知识(例如,什么是二项分布),并且熟悉 Python 编程环境(例如,如何编写简单的 for 循环)。下面是由朱撰写的关于常见统计分布的简要回顾。

问题 1:均匀分布

用 R 或者 Python,请回答以下问题。

对于一个数列,(a1,a2,a3,a4,…,an),请写一个随机返回每个元素 ai 的函数,概率为 ai/∑ai( )条件 1 )

例如,对于一个序列(1,2,3,4),函数以 1/10 的概率返回元素 1,以 4/10 的概率返回元素 4。 (条件二)

您可以使用任何库,但不能使用 random.choice()。 (条件 3)

走过我的思考过程

这是我在一家旅游公司问的一个真实的面试问题。

我们来分解一下。

问题要求函数返回与其权重成比例的元素, ai/∑ai。可以分两步完成:

*# Step 1: Calculate the probability for each element* ***ai*** *with respect to the total sum* ***∑ai****.**# Step 2: Simulate the process and return the element (it is more complicated than it sounds).*

对于第一步,我们做如下事情:

import numpy as npdef weight_func(sequence):# step 1prob = []total_sum = sum(sequence)for i in range(len(sequence)): prob.append(sequence[i]/total_sum)**# step 2: the following is pseudo-codereturn the element according to its probability**

这里有一个问题:我们不能使用内置方法, random.choice() ( 条件 3 )。假设,如果我们被允许进口 Numpy 包,这将是一个容易得多的问题。

或者,我们必须开发一些东西来执行与随机选择相同的功能。

那时候,我对现场一无所知。我的面试官友好地给出了他的第一个提示:你可以使用一个范围从 0 到 1 的均匀分布,将生成的值(名为)与每个位置 I 的累积概率和(名为 cum_prob[i] )进行比较,如果cum _ prob[I]>a,那么从序列中返回相应的值。

这个想法听起来很棒,让我们看看 Python 代码是什么样子的。

解决办法

人们犯了一个常见的错误,试图使用控制流(if-else 语句)来过滤场景。这对于小样本是可行的,但是如果序列中有成千上万的数字,这就不切实际了。我们没有使用 1000 多个“if,elif,else”语句来告诉 Python 如何处理这些数字,对吗?

几个月后回头看这个问题,最有挑战性的部分是想出用累积概率和来模拟过程的思路。在详细的一步一步的解释之后,现在更可行了。

问题 2:二项分布

在线购物网站(例如,亚马逊、阿里巴巴等。)希望测试将出现在网站顶部的两个版本的横幅。工程团队将访问版本 A 的概率指定为 0.6,将访问版本 B 的概率指定为 0.4。

在 10000 次访问后,有 6050 名访问者接触到版本 A,3950 人接触到版本 b。

随机化过程正确时有 6050 例的概率是多少?

换句话说,版本 A 的概率确实是 0.6。

走过我的思考过程

这是假设检验问题的一部分。我们来分解一下。

有 A 和 B 两个版本,实验把治疗分配给 10000 人。因此,这是采用二项分布的完美设置。

但是收到 A 版的概率略高于 b 版,最后的答案应该会返回 10000 次试验中超过 6050 人收到 A 版的概率。

这些信息提醒我们将二项分布与条件 for 循环结合起来,如下所示。

解决办法

*0.1498*

结果接近 15%,具有实用价值。这是一个假设检验问题。由于观察到 6000 名或以上访客的概率为 15%,我们无法拒绝零假设,并得出 10000 名访客中 6000 名和 6050 名访客之间没有统计差异的结论。换句话说,版本 A 的概率是 0.6。

我们已经学习了假设检验以及如何拒绝或未能拒绝零假设,但这样的问题让我对统计模拟三思。

新年快乐裘德·贝克在 Unsplash 上的照片

问题 3:泊松分布

我的中型博客每天有 500 次访问,访问次数遵循泊松分布。1000 次之外,每天 510 次以上的访问比例是多少?写一个函数来模拟这个过程。

走过我的思考过程

这是一个相当简单的问题。因为这个问题问的是一个事件在特定时间内发生了多少次,所以我们可以遵循泊松过程。

**# step 1: create a poisson distribution**# step 2: use an if clause to count the number**

解决办法

*0.318*

31.8%的模拟结果有 510 次以上的访问。

这篇文章发表后,媒体博客的数量飙升到另一个水平。

问题 4:正态分布

编写一个函数,从正态分布中生成 X 个样本,并绘制直方图。

走过我的思考过程

这是谷歌问的问题。这是一个相对简单的编码问题,有两个步骤:

**# step 1: generate a normal distribution**# step 2: take X samples and plot the sampling distribution**

开始了。

解决办法

我们用 100 个数字生成一个正态分布,设 X 等于 10。

*array([ 7.27305691,  6.98741057, 14.37357218, 14.17422672,  7.57495374, 10.39904815,  7.27305691,  7.41182935, 10.565957, 12.0081078 ]) # X_samples*

我的 Github 上有完整的 Python 代码。

外卖食品

  • 作为第一步,在进入编码部分之前,我们需要理解这个问题。求职者犯的一个最大的错误是没有问清楚问题就直接进入编程部分。他们在卡住后不得不多次重新访问该问题。
  • 这篇文章只关注最重要的模拟类型,不讨论其他类型。底层逻辑是相同的:将问题分解成不同的步骤,并用 Python/R 编写每一步的代码。

Medium 最近进化出了自己的 作家合伙人计划 ,支持像我这样的普通作家。如果你还不是订户,通过下面的链接注册,我会收到一部分会员费。

*https://leihua-ye.medium.com/membership *

我的数据科学面试序列

*</4-tricky-sql-questions-for-data-scientists-in-2021-88ff6e456c77> </5-python-coding-questions-asked-at-faang-59e6cf5ba2a0>

喜欢读这本书吗?

请在 LinkedIn 和 Youtube 上找到我。

还有,看看我其他关于人工智能和机器学习的帖子。*

用于比较分类算法的统计测试

原文:https://towardsdatascience.com/statistical-tests-for-comparing-classification-algorithms-ac1804e79bb7?source=collection_archive---------6-----------------------

回顾开创性的论文和实施,为您的数据找到最佳选择

照片由алексарцибашев在 Unsplash

对大多数数据科学家来说,比较预测方法以确定哪种方法应该用于手头的任务是一项日常活动。通常,人们会有一个分类模型池,并使用交叉验证来验证它们,以确定哪一个是最好的。

然而,另一个目标不是比较分类器,而是学习算法本身。这个想法是:给定这个任务(数据),哪种学习算法(KNN,SVM,随机森林等)将在大小为 D 的数据集上生成更准确的分类器?

正如我们将看到的,这里介绍的每种方法都有一些优点和缺点。然而,使用两个比例测试的第一直觉会导致一些非常糟糕的结果。

为了更多地了解我们如何比较这些算法,并提高我们的统计知识,今天我将解释和实现来自用于比较监督分类学习算法的近似统计测试 [1]的方法,这是一篇关于该领域的开创性论文。

在接下来的部分中,我将描述每个测试,讨论它的优点和缺点,实现它们,然后将结果与可用的实现进行比较。

这篇文章的笔记本可以在 Kaggle 和我的 Github 上找到。

初始代码设置

对于本文中的代码,我们将使用两种分类算法:KNN 和随机森林来预测葡萄酒数据集[2]上的葡萄酒质量,该数据集来自 UCI 机器学习库,可在 sklearn 包中免费获得。为此,我们将导入一些必需的库,并将实例化算法:

# Importing the required libs
import numpy as np
import pandas as pdfrom tqdm import tqdm
from scipy.stats import norm, chi2
from scipy.stats import t as t_dist
from sklearn.datasets import load_wine
from sklearn.metrics import accuracy_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split, KFold*# Libs implementations*
from mlxtend.evaluate import mcnemar
from mlxtend.evaluate import mcnemar_table
from mlxtend.evaluate import paired_ttest_5x2cv
from mlxtend.evaluate import proportion_difference
from mlxtend.evaluate import paired_ttest_kfold_cv
from mlxtend.evaluate import paired_ttest_resampled# Getting the wine data from sklearn
X, y = load_wine(return_X_y = True)# Instantiating the classification algorithms
rf = RandomForestClassifier(random_state=42)
knn = KNeighborsClassifier(n_neighbors=1)*#* For holdout cases
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=42)

两比例检验

比较两个比例是一个非常古老的问题,统计学有一个经典的假设检验来解决这个问题:给定两个人口的两个比例,零假设是两个比例之差的均值等于零。

我们可以用下面的统计数据来计算:

两比例检验统计量

看起来很简单,对吧?我们只是得到我们算法的命中比例(准确率)并进行比较。然而,这个测试有一个重要的假设:样本的独立性。

可以很快猜到,这里的样本不是独立的,因为两种算法的测试集和训练集是相同的。所以这个假设是错误的。

这种方法还有另外两个问题:

  • 它没有考虑测试集的方差。如果我们改变它,我们可能会有非常不同的结果
  • 它不考虑整个数据集,而是考虑被选择用于训练的一个较小的数据集

要使用该测试,可以使用以下代码:

# First we fit the classification algorithms
rf.fit(X_train, y_train)
knn.fit(X_train, y_train)# Generate the predictions
rf_y = rf.predict(X_test)
knn_y = knn.predict(X_test)# Calculate the accuracy
acc1 = accuracy_score(y_test, rf_y)
acc2 = accuracy_score(y_test, knn_y)# Run the test
print("Proportions Z-Test")
z, p = proportion_difference(acc1, acc2, n_1=len(y_test))
print(f"z statistic: **{**z**}**, p-value: **{**p**}\n**")

在这里,我们只是在拒不接受的测试集上拟合算法,并对结果精度进行测试。

重采样配对 t 检验

为了说明测试集的方差,可以使用重采样的配对 t 检验。在这个测试中,我们将设置一些试验(例如 30 次),并使用维持测试集在每次试验中测量每个算法的准确性。

然后,如果我们假设 p_i = pA_i - pB_i,对于每个试验 i 是正态分布的,我们可以应用配对学生的 t-检验:

配对 t 检验统计量

因为在每一次试验中,我们都要改变我们的测试集,它的方差被考虑在内,改进了前一次试验中的一个问题。然而,我们手中仍有一些问题:

  • p_i 的正态分布不成立,因为这些比例不是独立的,因为它们是在同一测试集上计算的
  • 每次试验的训练集和测试集之间都有重叠,所以 p_i 不是独立的
  • 它要求我们的算法被拟合多次,如果拟合时间太长,这可能是禁止的

对于这个实现,我们将定义创建一个函数,它将接收 p_is 作为参数:

def paired_t_test(p):p_hat = np.mean(p)n = len(p)den = np.sqrt(sum([(diff - p_hat)**2 for diff **in** p]) / (n - 1))t = (p_hat * (n**(1/2))) / denp_value = t_dist.sf(t, n-1)*2return t, p_value

在这个函数中,我们只是根据等式从测试中创建 t 统计量。然后,我们使用 scipy 中学生的 t 分布来找出测试的 p 值,然后返回统计数据和 p 值。

运行重采样 t-test 的代码如下:

n_tests = 30p_ = []
rng = np.random.RandomState(42)
for i **in** range(n_tests):randint = rng.randint(low=0, high=32767)X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=randint)rf.fit(X_train, y_train)knn.fit(X_train, y_train)acc1 = accuracy_score(y_test, rf.predict(X_test))acc2 = accuracy_score(y_test, knn.predict(X_test))p_.append(acc1 - acc2)print("Paired t-test Resampled")
t, p = paired_t_test(p_)
print(f"t statistic: **{**t**}**, p-value: **{**p**}\n**")

这里,我们将迭代次数定义为 30 次,对于每一次迭代,我们分割数据,拟合分类器,然后计算精确度之间的差异。我们将这个值保存在一个列表中,然后用这个列表调用上面定义的函数。

Mlxtend 库已经实现了这个测试,因此也可以使用:

print("Paired t-test Resampled")
t, p = paired_ttest_resampled(estimator1=rf, estimator2=knn, X=X,y=y, random_seed=42, num_rounds=30, test_size=0.2)
print(f"t statistic: **{**t**}**, p-value: **{**p**}\n**")

请注意,我们必须传递该函数的整个训练集,因为它将在自身内部创建拆分。您可以验证给定相同种子的结果是否相同。

交叉验证配对 t 检验

这个方法的结构与上面的方法相同。然而,我们将使用 K 折叠进行交叉验证,而不是在每次试验中使用坚持测试集。

这将消除重叠测试集的问题,因为现在每个样本都将针对不同的数据进行测试。

然而,我们仍然有重叠的训练数据问题。在 10 重交叉验证中,每轮训练与其他轮训练共享 80%的训练数据。

此外,我们仍然有一个时间问题,因为我们必须适应我们的分类器几次。然而,通常少于重采样配对 t 检验,因为通常运行 10 倍或 5 倍交叉验证,这比我们以前做的 30 次检验要小得多。

对于这个测试,我们将使用我们在之前的测试中定义的相同函数,因为它无论如何都将是一个 t 测试,我们只需要改变我们在循环的每次迭代中划分数据的方式(当然,还有循环的迭代次数)。使用 sklearn 库中的 KFold,我们可以:

p_ = []kf = KFold(n_splits=10, shuffle=True, random_state=42)
for train_index, test_index **in** kf.split(X):X_train, X_test, y_train, y_test = X[train_index], X[test_index], y[train_index], y[test_index]rf.fit(X_train, y_train)knn.fit(X_train, y_train)acc1 = accuracy_score(y_test, rf.predict(X_test))acc2 = accuracy_score(y_test, knn.predict(X_test))p_.append(acc1 - acc2)print("Cross Validated Paired t-test")
t, p = paired_t_test(p_)
print(f"t statistic: **{**t**}**, p-value: **{**p**}\n**")

Mlxtend 也有针对此测试的实现:

t, p = paired_ttest_kfold_cv(estimator1=rf, estimator2=knn, X=X, y=y, random_seed=42, shuffle=True, cv=10)
print(f"t statistic: **{**t**}**, p-value: **{**p**}\n**"

麦克内马试验

这种测试的优点是,我们的每种算法只需要一次拟合。我们使用一个维持集来拟合它们,然后创建一个列联表:

麦克纳玛的应急表。由作者开发。

然后,我们陈述两种算法应具有相同错误率的零假设,并用以下统计量进行卡方检验:

麦克内马检验统计量

在 paper benchmarks 测试中,与其他测试相比,该测试在错误率方面排名第二,仅次于我们将在下面看到的 5x2 交叉验证测试。正因为如此,作者说,如果你负担不起交叉验证,就应该使用这种方法。

然而,这仍然存在问题:

  • 该测试没有考虑训练集的变化,因为我们只适合一次
  • 因为我们只适合一次,所以我们不考虑内部算法的变化
  • 我们使用比原来更小的一套。但是,请注意,这里的每个测试都会受到影响

为了实现测试,我们将创建一个专用函数:

def mcnemar_test(y_true, y_1, y_2):b = sum(np.logical_and((knn_y != y_test),(rf_y == y_test)))c = sum(np.logical_and((knn_y == y_test),(rf_y != y_test)))c_ = (np.abs(b - c) - 1)**2 / (b + c)p_value = chi2.sf(c_, 1)return c_, p_value

在这里,我们只是计算列联表中的值,看看模型在哪里有正确或不正确的答案。然后,我们查看卡方分布,找出我们计算的统计数据的 p 值。

由于这使用了维持集,因此以下步骤很简单:

print("McNemar's test")
chi2_, p = mcnemar_test(y_test, rf_y, knn_y)
print(f"chi² statistic: **{**chi2_**}**, p-value: **{**p**}\n**")

此外,还可以使用 Mlxtend 库中的实现:

print("McNemar's test")
table = mcnemar_table(y_target=y_test, y_model1=rf_y, y_model2=knn_y)
chi2_, p = mcnemar(ary=table, corrected=True)
print(f"chi² statistic: **{**chi2_**}**, p-value: **{**p**}\n**")

5x2 交叉验证测试

根据作者的基准测试,这个测试被认为是这 5 个测试中最好的一个。

这个想法是运行 2 重交叉验证 5 次,产生 10 个不同的估计。然后,我们定义以下比例:

5x2 交叉验证测试参数

最后是统计数据:

5x2 交叉验证测试统计

这里最大的缺点是我们必须多次调整算法。

该论文对该方法有更全面的描述和推导,所以我建议阅读它以获得全面的理解。

最后,为了实现它,我们将创建一个函数:

def five_two_statistic(p1, p2):p1 = np.array(p1)p2 = np.array(p2)p_hat = (p1 + p2) / 2s = (p1 - p_hat)**2 + (p2 - p_hat)**2t = p1[0] / np.sqrt(1/5\. * sum(s))p_value = t_dist.sf(t, 5)*2return t, p_value

请注意,我们只是创建所需的值来计算统计数据,然后像往常一样,查看分布来查找 p 值。

然后,我们继续运行双重交叉验证五次:

p_1 = []
p_2 = []rng = np.random.RandomState(42)
for i **in** range(5):randint = rng.randint(low=0, high=32767)X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.50, random_state=randint)rf.fit(X_train, y_train)knn.fit(X_train, y_train)acc1 = accuracy_score(y_test, rf.predict(X_test))acc2 = accuracy_score(y_test, knn.predict(X_test))p_1.append(acc1 - acc2)rf.fit(X_test, y_test)knn.fit(X_test, y_test)acc1 = accuracy_score(y_train, rf.predict(X_train))acc2 = accuracy_score(y_train, knn.predict(X_train))p_2.append(acc1 - acc2)# Running the test
print("5x2 CV Paired t-test")     
t, p = five_two_statistic(p_1, p_2)
print(f"t statistic: **{**t**}**, p-value: **{**p**}\n**")

我们还有 Mlxtend 实现:

print("5x2 CV Paired t-test")
t, p = paired_ttest_5x2cv(estimator1=rf, estimator2=knn, X=X, y=y, random_seed=42)
print(f"t statistic: **{**t**}**, p-value: **{**p**}\n**")

我们在两个实现上都得到了与预期相同的结果。

结论

重要的是要注意,在这种情况下没有银弹。这里提出的每个测试都有一些优点和缺点,它们都是近似值。但是,请注意,在任何情况下都不应该使用两个比例检验。

给定所需的时间预算,可以应用所有这些测试并比较它们的结果,以尝试对是否应该使用一类算法或另一类算法做出更好的评估。

另一方面,如果感兴趣的算法可以计算它们的粉碎系数(如 SVM、MLP 或决策树),这些测试可以与统计学习理论的结果一起使用,以确定应该使用哪种算法。但这是一个单独的帖子的讨论。

我强烈建议阅读这篇论文,看看这些基准是如何构建的,它很容易阅读,而且信息量很大。

[1] Thomas G. Dietterich,比较监督分类学习算法的近似统计检验(1998),神经计算1998;10 (7): 1895–1923

[2]利奇曼(2013 年)。https://archive.ics.uci.edu/ml UCI 机器学习库。加州欧文:加州大学信息与计算机科学学院。

统计学#01:平均值、中位数和众数

原文:https://towardsdatascience.com/statistics-01-mean-median-and-mode-d6d8597ed9f1?source=collection_archive---------18-----------------------

理解集中趋势的三个最常见的度量

艾萨克·史密斯在 Unsplash 上拍摄的照片

当人们需要得到某事物的“平均值”时,我们通常将所有的数字/项目相加,然后除以有多少个数字/项目。这是对平均值的一个简单定义,但是还有其他类型的“平均值”或集中趋势的度量,每一种都有其用途,取决于你想要实现什么。

在本文中,我们将讨论三种最常见的“平均值”,即表示中值众数,以及如何使用它们。

平均

平均值,也由希腊字母“μ”表示,可能是最常用的集中趋势的量度。前面说过,要计算一组数的均值,需要把它们加在一起,然后除以有多少个数。

假设我们有下面的列表,包含 5 个数字,我们需要找到这些数字的平均值:

我们可以简单地计算:

我们发现平均值是 14 。请注意,平均值不必是列表中包含的值之一。

概括地说,我们可以说:

在上面的等式中,n 代表对象的数量或列表的长度,xₙ代表列表中的第 n 个数字。

写这个方程的另一种方法是使用数学求和符号,用 x 代替 x₁+x₂+x₃+…+xₙ:

考虑到值的频率,还有另一种方法来表示平均值。注意,在我们的五个数字 (5,10,15,15,25) 的列表中,数字 15 出现了两次。

在像我们这样的小列表中,这可能不会产生很大的影响,但当我们处理数百个循环出现的数字时,就值得考虑每个数字的频率。我们只需要将每个数字乘以它的频率,将结果相加,然后将结果除以频率之和。为了举例说明,让我们来看看下面的等式。

为了简单起见,我们可以将均值方程改写为:

其中,∑fx 是数值乘以频率之和,而∑f 是频率之和。

中位数

中位数是序列中间的值。这可能是最直接的描述了。但是让我们仔细看看,看看什么时候考虑中位数可能是个好主意。

假设一个房间里有 10 名刚毕业的大学生,我们正在调查刚找到第一份工作的毕业生的平均工资。下面的列表以升序显示了这些人的工资。

如果我们测量这些值的平均值,我们将得到 $40,500 作为毕业生的平均工资,正如我们在下面的直方图中看到的。

图一。显示异常值的直方图(图片由作者提供)

但请注意,它并不代表准确的现实,因为 40,500 美元比所有工资都高得多,只有一个例外。为什么会这样?好吧,一个幸运的人一年挣20 万美元,并且正在扭曲我们的评估。

看上面的直方图,很明显大部分值都集中在 20000 到 30000 左右,大致如此。但是,有一个值与其他值不兼容。与房间里其他人的工资相比,20 万美元的工资是极端的,可以被称为异数

离群值的一个问题是它扰乱了平均值。直方图中的红色虚线代表房间中的平均工资,40,500 美元,这个值高于我们列表中 10 个工资中的 9 个。异常值正在推高平均值。

在这种情况下,中位数可能会给我们一个更好的数据画面。请记住,中位数是一个序列中间的值。这里,因为我们有偶数个值,所以中值将是两个中间数的平均值。我们去看看。

请注意,中值不必是列表中包含的值之一,就像平均值一样。但是,如果我们有一组奇数值(假设我们去掉了异常值,剩下 9 个值),中值将是 20,000,这个数字正好在中间。只要记住数字必须按升序排列。

让我们画同样的直方图,但这次,红色虚线代表 10 份薪水的中位数。

图二。显示异常值的直方图(图片由作者提供)

请注意,在这种情况下,中位数比平均数更准确地描述了平均工资。

方式

模式可以简单地描述为在数据集中出现最频繁的值。让我们看看如何识别模式,以及它与均值和中值有何不同。

考虑下面的分布,包含 20 个数字。

让我们画一个直方图,来形象化数据集。

图三。显示两组数据的直方图(图片由作者提供)

请注意,我们有一个值大约为 5 和 8 的聚类,还有一个值大约为 17 和 20 的聚类。然而,平均值和中值都是 12.5,与这些值相差甚远。

与均值和中值不同,的模式是集合中包含的值之一。在上面的例子中,我们有两种模式, 5 和 20 ,因为它们是数据集中最常见的值。我们可以说我们的数据是双峰的。

异常值和偏差数据

最后,让我们简要地看一下一些图表,这些图表说明了异常值会对我们的数据集产生什么影响。

我们已经看到,异常值是数据样本中极端的、异常的观察结果,它们会扭曲数据。当这种情况发生时,我们通常会说我们的数据是偏斜的。它可以向右或向左倾斜,这取决于异常值是特别高还是特别低。

请看看下面的图表。

图 4。右偏分布(图片由作者提供)

这是一种右偏分布,当我们出现异常高值时,就会发生这种情况,使平均值向右扭曲。当数据向右倾斜时,平均值将大于中位数。上面的薪水调查是右偏数据的一个例子。

图五。左偏分布(图片由作者提供)

上面的图表代表了一个左偏分布。当我们出现异常低值时,就会发生这种情况,使平均值向左扭曲。当数据向左倾斜时,平均值将低于中位数。

图六。对称分布(图片由作者提供)

最后这张图表显示了一个对称分布。当数据对称时,没有异常值将平均值向左或向右拉。这是均值、中值和众数具有相同值的情况。

结论

当然,关于这些主题还可以说得更多,但我希望这篇文章可以帮助您理解均值、中值和众数之间的主要区别,以及异常值和偏差数据的概念。

总而言之,让我们回顾一下我们在这里看到的内容:

平均

  • 把所有的数字加起来,然后除以有多少个数字。

中位数

  • 在升序排列的集合中,它是位于中间的值。
  • 如果集合中有偶数个值,将中间的两个值相加,然后除以 2

方式

  • 列表中最频繁出现的值。
  • 一个数据集可以有几种模式。

参考

  1. Griffiths,d,《头部优先统计学:一个有益大脑的指南》。奥莱利,2008 年。
  2. 【https://www.itl.nist.gov/div898/handbook/】**/sema tech 统计方法电子手册,* ,2012 年。*

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/72129.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

牛逼!5K star! 推荐一款集监控和埋点于一体的前端性能监控工具!开源、简单易用、功能强大!

在互联网的快速发展下,网站已成为企业和个人展示信息、提供服务的重要平台。然而,随之而来的网站性能问题也日益凸显,如加载速度慢、频繁出错、服务器故障、数据异常、网络攻击等。如何确保用户能够快速稳定地访问网站成为了一个亟待解决的问题。 为了帮助大家解决这一问题,…

TowardsDataScience-博客中文翻译-2020-一百二十九-

TowardsDataScience 博客中文翻译 2020(一百二十九)原文:TowardsDataScience Blog 协议:CC BY-NC-SA 4.0什么是机器学习?—直观的解释。原文:https://towardsdatascience.com/what-is-machine-learning-a-visual-explanation-14642b90429f?source=collection_archive---…

什么是泰森多边形?

世界各地的雨量分布存在着巨大的差异,即使是方圆百米内也可能出现东边日出西边雨的情况。那么科学家们是如何根据气象站的降雨数据计算各个地区的平均降雨量的呢?让我们一起来了解在计算平均降雨量中运用到的泰森多边形吧! 1911年,荷兰气候学家Thiessen根据离散分布的气象…

忘记网站密码,该怎么办呢?一招教你解决

忘记网站密码时,通常可以按照以下步骤来尝试找回或重置密码:访问登录页面:前往您需要登录的网站,并找到登录界面。查找“忘记密码”链接:在登录界面通常会有一个“忘记密码”、“找回密码”或类似的链接。输入注册邮箱或手机号:点击“忘记密码”后,系统会要求您输入注册…

C#-新增一列删除按钮

实现: 给查询出来的数据添加一列"删除按钮(delete Button)" 查询接口新增deleteButton:// 创建一个按钮列DataGridViewButtonColumn btnColumn = new DataGridViewButtonColumn();btnColumn.Name = "deleteButton";btnColumn.Text = "删除";b…

P11080 [ROI 2019 Day 1] 拍照 题解

题意 给定 \(m\) 个位置和 \(n\) 个颜色,以及一个目标序列。找到一组合法的操作使得一个无色序列能变成目标序列。操作:选定一个颜色 \(c\) 和一个区间 \(l,r\),将 \(l,r\) 中的每个元素染色为 \(c\)。每个颜色只能用一次,且会覆盖原来的颜色。思路 首先我们肯定是对一组颜…