原文:TowardsDataScience Blog
协议:CC BY-NC-SA 4.0
原文:https://towardsdatascience.com/improve-your-mlflow-experiment-keeping-track-of-historical-metrics-6e70a6c7b201?source=collection_archive---------21-----------------------

图片由萨汉德·巴巴里在 Unsplash 上拍摄
https://medium.com/@stefanobosisio1/membership
欢迎回到我们 MLflow 旅程的第二部分。今天,我们将扩展当前的 SDK 实现,增加两个功能来报告历史指标和自定义指标。然后,我们将最终看到 SDK 与一个简单的例子一起工作。下一次,我们将深入探讨 MLflow 插件,我们将为 GCP 人工智能平台创建一个“部署”插件
这是我关于 MLflow SDK 创建的第一篇文章:
— 我们今天需要什么—将实验的运行指标报告给最近一次运行
—将自定义指标报告给一次运行
—更新实验跟踪界面
— 创建您最终的 MLflow SDK 并安装它
— SDK 运行!
首先,让我们考虑主 SDK 协议的设计。今天的目标是让数据科学家能够:
因此,我们可以考虑实现以下两个功能:
report_custom_metrics:该函数返回数据科学家的度量注释,向给定的实验发布一个字典。如果数据科学家希望坚持使用一些关于看不见的数据的指标进行特定的实验,这可能是有用的。该功能利用 MLflow Tracking 中的MLflowClient客户端管理实验及其运行。从MLflowClient中,我们可以检索给定实验的所有运行。从那里,我们可以提取每次运行的指标。一旦我们收集了所有的指标,我们就可以进行第二步,我们将使用plotly来制作一个交互式html图。这样,用户可以在 MLflow 服务器人工制品框中分析所有运行的每个数据点。
图 1 显示了report_metrics_to_experiment功能的第一部分。首先,用给定输入tracking_uri初始化MlflowClient。然后,用client.get_experiment_by_name检索实验的信息,并将其转换成一个字典。这里列出了每个实验的运行情况,runs_list。每次运行都有自己的run_id,它可以将度量信息存储在字典models_metrics中。此外,可以通过run.to_dictionary()['data']['metrics']访问指标。该值返回指标的名称。
图 1:函数 report_metrics_to_experiment 的第一部分。在这个片段中,我展示了如何从实验运行中检索指标的数据点。最终的指标值与运行 id、指标的步骤和值一起存储在一个字典中
根据指标的名称,可以通过client.get_metric_history()记录指标的数据点。该属性返回指标的步骤和值,因此我们可以添加到列表中并保存在models_metrics[single_run_id][metric] = [x_axis, y_axis]中
图 2 显示了report_metrics_to_experiment的第二部分。首先,初始化一个新的plotly数字fig = go.Figure().,然后从models_metrics中读取指标并添加为散点图。最终的图形以html格式保存,以便进行交互式可视化。
图 2:功能报告的第二部分-度量-实验。这里,所有检索到的指标都显示在 plotly 图上,然后以 html 格式保存
我们今天要实现的最后一个功能是报告特定运行的自定义输入。在这种情况下,数据科学家可能从运行的模型中获得一些带有看不见的数据的指标。该函数如图 3 所示。给定一个输入字典custom_metrics(例如),该函数使用MlflowClient到log_metric来表示一个特定的run_id
图 3: report_custom_metrics 获取一个输入字典,其中包含看不见的度量或数据,它使用 log_metric 向 MLflow 运行报告键和 val。
现在,两个新闻函数已经添加到主 MLflow 协议中,让我们将它们封装到我们的 [experiment_tracking_training.py](/scale-up-your-models-development-with-mlflow-4b78a5f22cb7#b9d2)中,特别是,end_training_job可以调用report_metrics_to_experiment,因此,在任何训练结束时,我们都可以跟踪给定实验的所有历史指标,如图 4 所示
图 4:可以在任何培训作业结束时调用 report_metrics_to_experiment,并将其添加到 end_training_job 功能中。
此外,为了允许用户向特定的运行添加他们自己的度量,我们可以考虑一个add_metrics_to_run函数,它接收实验跟踪参数、我们想要处理的run_id和自定义字典custom_metrics(图 5)作为输入:
图 5:可以通过 experiment _ tracking _ training . add _ metrics _ to _ run 模块报告自定义指标。
将所有部分修补在一起,SDK 包应该以类似的方式构建:
mlflow_sdk/
mlflow_sdk/
__init__.py
ExperimentTrackingInterface.py
experiment_tracking_training.py
requirements.txt
setup.py
README.md
requirements.txt包含了我们安装 SDK 所需的所有包,特别是默认情况下需要numpy, mlflow, pandas, matplotlib, scikit_learn, seaborn, plotly。
setup.py允许在给定的 Python 环境中安装您自己的 MLflow SDK,脚本的结构应该是这样的:
图 6:在给定的 Python 环境中安装 mlflow_sdk 包的 Setup.py
要安装 SDK,只需使用 Python 或 virtualenv Python:python setup.py install
是时候将我们的 MLflow SDK 付诸行动了。我们将用一个sklearn.ensemble.RandomForestClassifier和虹膜数据集 ⁴ ⁵ ( 来源和许可,开放数据共享公共领域专用和许可)来测试它。图 7 显示了我们将要使用的完整示例脚本(我的脚本名是1_iris_random_forest.py)
tracking_params包含设置 MLflow 服务器的所有相关信息,以及运行和实验名称。加载数据集后,我们将使用sklearn.model_selection.train_test_split创建一个列车测试分割。为了在 MLflow 工件中显示不同的度量和图表,我运行了1_iris_random_forest.py 5 次,用以下值改变test_size:0.3, 0.2, 0.1, 0.05, 0.01
图 7:在这个例子中,我们将对 iris 数据集运行一个随机森林。MLflow SDK 将在训练期间跟踪模型信息,并使用 add_metrics_to_run 报告所有附加指标。
一旦数据已经设置好,clf=RandomForestClassifier(n_estimators=2)我们就可以调用experiment_tracking_training.start_training_job。该模块将与 MLflow 上下文管理器交互,并向 MLflow 服务器报告运行模型的脚本以及模型的信息和工件。
在培训结束时,我们希望在一个图中报告所有实验运行的指标,并且,仅仅为了测试,我们还将保存一些“假”指标,如false_metrics = {"test_metric1":0.98, ... }
在新的终端选项卡中运行1_iris_random_forest.py之前,打开与 MLflow 服务器的连接,如mlflow ui所示,并导航至http://localhost:5000或http://127.0.0.1:5000。然后,按照python 1_iris_random_forest.py运行上述示例,并针对test_size的不同值重复运行 5 次

图 8:运行示例脚本后的 MLflow UI
图 8 应该类似于运行示例脚本后的结果。在Experiments下面列出了实验的名称。每个实验都有一系列的跑步,特别是在random_forest下,你会找到来自1_iris_random_forest.py的随机森林跑步
对于每次运行,我们可以立即看到一些参数,这些参数由mlflow.sklearn.autolog()自动记录,以及我们的假指标(例如test_metric1)自动记录功能也保存Tags,报告估计器类(例如sklearn.ensemble._forest.RandomForestClassifier)和方法(RandomForestClassifier)。
单击单次运行,将显示更多详细信息。首先,您将看到所有的模型参数,这些参数也是由 autolog 功能自动报告的。向下滚动页面,我们可以访问指标图。在这种情况下,我们只有一个数据点,但对于更复杂的模型,您可以有一个完整的图,作为步骤数的函数。

图 MLflow SDK 保存的工件。在 artifacts 框中,您可以找到用于运行模型的代码(在我的例子中是 1_iris_random_forest.py ),以及 model 文件夹下的模型 pickle 文件和所有交互式度量图以及混淆矩阵。
然后,最重要的信息将存储在工件箱下(图 9)。在这里,您可以找到由我们的mlflow_sdk:创建的不同文件夹
code是一个文件夹,它存储了用于运行我们的模型的脚本——这是在第 24 行的experiment_tracking_training中用traceback、这里是链接完成的,并被推送到第 31 行的run_training函数的 MLflow artefacts、这里是链接。model存储二进制 pickle 文件。MLflow 自动保存模型文件及其要求,以允许结果的再现性。这在部署时非常有用。*.html),在培训结束时生成,以及我们在培训期间计算的其他指标,如training_confusion_matrix.png如您所见,我们以最少的干预为 ML 模型添加了完整的跟踪路线。实验在开发阶段至关重要,通过这种方式,数据科学家可以轻松使用 MLflow 跟踪功能,而无需过度修改他们现有的代码。从这里,您可以探索报告的不同“色调”,为每次运行添加进一步的信息,以及在专用服务器上运行 MLflow 以允许跨团队协作。
罗纳德·费希尔《分类问题中多重测量的使用》优生学年鉴7.2(1936):179–188。
爱德华·戴明。“对数理统计的贡献。拉。纽约:威利;伦敦:查普曼&霍尔出版社,1950 年。655 页“科学113.2930(1951):216–217。
R. O .杜达和 P. E .哈特。“模式分类和场景分析。(Q327。D83)约翰·威利父子公司。”(1973): 218.
⁴·达萨拉西,贝鲁尔 v 在邻居周围搜寻:在部分暴露环境中识别的新系统结构和分类规则〉 IEEE 模式分析和机器智能汇刊1(1980):67–71。
⁵·盖茨,杰佛瑞。“简化的最近邻规则(corresp。)."IEEE 信息论汇刊 18.3(1972):431–433。
今天就到这里吧!希望您喜欢这两篇关于 MLflow 及其 SDK 开发的文章。下一次,我们将深入 MLflow 插件世界,从理论上讲,这也可以引导您的团队进入部署阶段。
如果你有任何问题或好奇,请给我发电子邮件到 stefanobosisio1@gmail.com
原文:https://towardsdatascience.com/improve-your-model-performance-with-auto-encoders-d4ee543b4154?source=collection_archive---------6-----------------------

图片由 ar130405 来自 Pixabay
除非您评估模型的性能,否则您永远不知道模型的性能如何。数据科学家的目标是开发一个健壮的数据科学模型。模型的健壮性是通过计算它在验证和测试指标上的表现来决定的。如果模型在验证和测试数据上表现良好,则它在生产推断期间往往表现良好。
有各种各样的技术或技巧来提高模型的性能。正确的特征工程策略有助于提高模型的性能。在本文中,我们将讨论并实现使用自动编码器的特征提取。
自动编码器是一种无监督的神经网络,它从输入的未标记数据中学习有效的编码。自动编码器试图通过最小化重建损失来重建输入数据。
标准自动编码器架构具有编码器和解码器层:

(来源),自动编码器架构
跟随我以前的文章来了解自动编码器的应用或用法
</6-applications-of-auto-encoders-every-data-scientist-should-know-dc703cbc892b>
自动编码器将输入数据(X)编码成另一维度(Z),然后使用解码器网络重构输出数据(X’)。与输入层相比,编码嵌入优选地在维度上较低,并且包含输入层的所有有效编码。
想法是通过在训练样本上训练自动编码器来学习编码器层权重。通过在训练样本上训练自动编码器,它将试图最小化重构误差并生成编码器和解码器网络权重。
稍后,解码器网络可以被裁剪掉,并且可以使用编码器网络来生成特征提取嵌入。这种嵌入可用于监督任务。
现在,让我们深入研究上面讨论的思想的一步一步的实现。
我使用的样本数据集是使用 make_classification 函数生成的,包含 10k 个实例和 500 个特征。将数据集分成训练、验证和测试样本,以避免数据泄漏。此外,标准化数据样本。
我已经在自动编码器架构中定义了两层编码器和两层解码器网络。每个编码器和解码器层都有一个批量标准化层。
编码层的尺寸需要通过实验来决定。
使用 Keras 和 adam optimizer 编译并运行自动编码器架构,并使用均方误差损失来重构输入数据。
我将运行流水线 50 个时期,批处理大小为 64。
将训练和测试重建损失的变化可视化。

(作者提供图片),用纪元训练和验证 MSE
一旦自动编码器、权重被优化,我们可以裁剪解码器网络,并且仅使用编码器网络来计算输入数据的嵌入。这些嵌入可以进一步用作监督机器学习任务的特征。
来自解码器网络的嵌入可以用作分类或回归任务的特征。
现在,让我们通过将原始输入要素更改为编码要素来比较模型的性能。我们将用两个模型的默认超参数训练一个逻辑回归估计量。

(图片由作者提供),性能指标评测
第一列提到了具有 500 个特征的原始输入数据的度量性能。对于使用自动编码器的编码器网络的编码特征,我们观察到性能指标的改进。
使用自动编码器的特征提取捕获输入数据的有效编码,并将其投影到相同或更低的维度。对于具有大量输入要素的输入数据,最好将数据投影到较低的维度中,并将这些要素用于监督学习任务。
另外,请阅读我以前的一些文章来提高模型性能:
https://medium.com/geekculture/essential-guide-to-improve-imbalanced-data-classification-model-performance-c8cf0125b731
[1]https://machine learning mastery . com/auto encoder-for-class ification/
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一小部分会员费,不需要你额外付费。
https://satyam-kumar.medium.com/membership
感谢您的阅读
原文:https://towardsdatascience.com/improve-your-model-performance-with-bayesian-optimization-hyperparameter-tuning-4dbd7fe25b62?source=collection_archive---------19-----------------------

照片由丹尼斯·莱昂在 Unsplash 上拍摄
“我为什么要看这篇文章?”
如果你已经开始在你的项目中使用 ML,或者只是为了好玩,你可能已经意识到调整你的模型的任务是多么的具有挑战性,尤其是它是非常耗时的。如果你不是一个专业的 ML 从业者,对应该使用哪些超参数做出有根据的猜测将是一个挑战。此外,您正在使用的算法可能对超参数选择非常敏感,因此测试更多的实现是有用的。在本文中,我将根据经验展示贝叶斯优化对于超参数调优的强大功能,并将其与更常见的技术进行比较。
“超参数调优快速回顾”
在 ML 领域,评估几组超参数的最已知技术是网格搜索和**随机搜索。**下面是它们工作原理的形象化展示:

作者图片,灵感来自随机搜索超参数优化 (James Bergstra,Yoshua Bengio)
x 轴和 y 轴代表两个假设超参数的取值范围,在左图中,您可以看到算法尝试预定义的参数组合(GridSearch),而在右图中,超参数是从一个范围内随机选择的(RandomSearch)。
在图中,您还可以看到,对于每个超参数,都有一个函数来定义超参数本身与其对模型性能的贡献之间的关系。你的目标是优化函数以找到最大值。然而,超参数越多,就越难找到最佳点。
然而,这个例子不是很现实,因为 ML 算法可能有一个长的可以调整的超参数列表,并且可能组合的数量可能变成无法估计。我们的目标确实是试图找到性能最好的型号,同时尝试最少的组合。
“让超参数搜索更加数据驱动”
网格搜索和随机搜索并不是仅有的两种进行超参数调整的技术。我确实想展示另一个可以用来实现这个目标的方法,那就是贝叶斯优化。
我不会深究算法是如何工作的,我将只限于给出一个大概的概述。由此,您可以找到算法所采取的步骤:
通过阅读以上几点,你应该已经明白为什么这个方法在理论上比我在上一段提到的方法更好。该方法使用来自上一次运行到的信息通知下一次运行。这是一个非常强大的工具,因为它允许我们在整个过程中缩小超参数搜索的范围,以主要关注超参数范围中非常有利可图的领域。
更一般地说,每当你需要优化一个黑盒函数时,可以使用贝叶斯优化。这意味着您可以使用这种方法来搜索任何函数的全局最优解,而您只能观察该函数的输入和输出。
为了使用贝叶斯优化进行超参数调整,我使用了模块’ scikit-optimize ,我发现它非常直观和用户友好。在他们的文档中没有很多例子,但是你需要做的就是实现它。
“不错的理论,但它在实践中意味着什么?”
在这个实验中,我们将比较 GridSearch、RandomSearch 和 Bayesian 优化超参数调优的结果。我使用的数据是加州住房数据集,其目标是预测中值房价。为了达到这个结果,我使用了 GBM 算法。
值得一提的是,这个练习旨在到比较超参数算法和而非 以获得最佳模型,因此我不对数据进行任何转换。我正在做一个被蒙住眼睛的插件和游戏,这当然是不建议在现实世界中做的(总是吃你的胡萝卜和看数据)。
为了解决模型性能问题,我使用了 MAE 指标和 K 倍交叉验证。
在每个模型之后,我绘制每次迭代的超参数以及跨越 K 倍的平均负 MAE 度量(我所绘制的实际上是跨越迭代的负 MAE 和最高负 MAE 之间的差异)。我这样做是为了更清楚地了解超参数对模型性能的影响。
我选择优化的超参数是:
“我们来看一些剧情”
在下面的代码块中,我将导入必要的模块,并生成一些函数来可视化结果。
import pandas as pd
import numpy as np
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV, train_test_split
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_absolute_error
from scipy.stats import randint, uniform
import seaborn as sns
import matplotlib.pyplot as pltdef parameter_over_iterations(model_result):
'''
This function is generating a subplots with the hyperparameter values for each iteration and the overall performance score.
The performance score is the difference between the best performing model and the worst performing model
model_result: CV object
'''
param_list = list(model_result.cv_results_['params'][0].keys())
max_col_plot = 2
row_plot =int(np.ceil((len(param_list) + 1)/max_col_plot))
fig, axs = plt.subplots(nrows=row_plot, ncols=np.min((max_col_plot, (len(param_list) + 1))), figsize=(30,12))
for i, ax in enumerate(axs.flatten()):
if i == len(param_list):
break
par = param_list[i]
param_val = list()
for par_dict in model_result.cv_results_['params']:
param_val.append(par_dict[par])
sns.barplot(y=param_val, x=np.arange(len(param_val)), ax=ax)
ax.set_title(par)
dt = pd.DataFrame()
mean_metric = dt.mean(axis=1)
sns.barplot(y=(mean_metric.values + abs(np.min(mean_metric.values))), x=np.arange(len(mean_metric) ), ax=axs.flatten()[i])
axs.flatten()[i].set_title('overall metric')
在本节中,我们将看到使用 GridSearch 选择最佳超参数的结果。
param_test = {'max_depth':range(5,15,5), 'min_samples_split':range(200,800,300), 'learning_rate': np.arange(0.05,0.55,0.25), 'subsample': np.arange(0.4,1,0.4),
'max_features': np.arange(0.4,1,0.3), 'n_estimators': np.arange(40,160,60)}gsearch = GridSearchCV(estimator = GradientBoostingRegressor(random_state=10),
param_grid = param_test, scoring='neg_mean_absolute_error',n_jobs=4,iid=False, cv=5)
gsearch.fit(X_train,y_train)parameter_over_iterations(gsearch)

在上图中,我们可以看到每个超参数选择的模型性能。在前 6 个子图中,我绘制了在每个特定迭代中使用的超参数值(迭代总数为 64)。
最后一个图(在左下方)是性能图,对该图的解释是,越高,和越好,模型的性能就越好。
从上面的图中,我们可以清楚地看到两件事:学习率为 0.3 会对模型性能产生积极影响(您可以看到图右侧的值更高),估计器的数量越多,性能也越好(在图的开始,您可以看到当估计器的数量较高时,峰值最高)
总之,通过这次搜索,我发现最佳超参数组合是
这导致了一场 35212.30 的 MAE。
RandomSearch 应该比 GridSearch 产生更好的结果,即使它通常不能达到未知函数的全局最优。
param_distrib = rsearch = RandomizedSearchCV(estimator = GradientBoostingRegressor(random_state=10),
param_distributions = param_distrib, scoring='neg_mean_absolute_error',n_jobs=4, n_iter=64,iid=False, cv=5)
rsearch.fit(X_train,y_train)parameter_over_iterations(rsearch)

事实上,随机搜索方法导致了更好的超参数选择,导致 MAE 为 33942 。
选定的超参数有:
现在是测试贝叶斯优化算法来调整模型的时候了。
正如您在下面的脚本中看到的,除了我为每个超参数指定值范围的字典之外,我还指定了一些将影响贝叶斯算法行为的值。我为获取函数指定了一些参数(xi 和卡帕)。这两个参数控制了采集函数在多大程度上倾向于探索到开发。较高的 xi 和卡帕值意味着更多的探索,而较低的值意味着更多的开发。
关于这个主题的更多信息可以在这里找到:https://sci kit-optimize . github . io/stable/auto _ examples/exploration-vs-exploitation . html?highlight=kappa
from skopt import BayesSearchCV
from skopt.space import Real, Integeroptimizer_kwargs = {'acq_func_kwargs':{"xi": 10, "kappa": 10}}space = bsearch = BayesSearchCV(estimator = GradientBoostingRegressor(random_state=10),
search_spaces = space, scoring='neg_mean_absolute_error',n_jobs=4, n_iter=64,iid=False, cv=5, optimizer_kwargs=optimizer_kwargs)
bsearch.fit(X_train,y_train)parameter_over_iterations(bsearch)

如果我们在迭代中观察超参数,我们确实看到,对于某些参数,随着时间的推移,这些值趋向于变得更加稳定。这表明算法正在向最优值收敛。然而,对于相当多的迭代来说,该算法似乎在某些局部最优上停滞不前(这表明更多的探索将对该算法有益)。事实上,如果我们查看学习率,我们会发现在迭代 10 和迭代 53 之间,该值几乎没有变化,但学习率为 0.19 时会获得最佳结果。
看起来这种方法将受益于更多的迭代,然而,这种方法能够找到一组可能导致 32902 的 MAE 的超参数。
“贝叶斯优化值得吗?”
在这个简短的(肯定不是详尽的)例子中,我们可以清楚地看到 GridSearch 肯定是探索超参数空间最差的方法,而 RandomSearch 和贝叶斯优化都表现良好。然而,特别是当超参数的数量很高时,随机搜索可能不会提供最好的结果,而潜在的贝叶斯优化会提供最好的结果,因为它基于观察到的性能通知未来的超参数选择。此外,贝叶斯优化应该花费较少的迭代来达到全局最优,而随机搜索可能需要大量的迭代才能达到。
原文:https://towardsdatascience.com/improving-application-availability-with-pod-readiness-gates-4ebebc3fb28a?source=collection_archive---------39-----------------------

由 Kelly Sikkema 在 Unsplash 上拍摄的照片
使用 Pod 活跃度和就绪度探测器,确保您在 Kubernetes 上运行的应用程序可用并准备好服务流量是非常容易的。但是,并不是所有应用程序都能够使用探测器,或者在某些情况下需要更复杂的就绪检查,而这些探测器根本无法执行这些检查。然而,如果 Pod 探针不够好,还有其他解决方案吗?
答案很明显,是。借助就绪门,可以为 Kubernetes Pods 执行复杂的定制就绪检查。
就绪门允许我们创建类似于PodScheduled或Initialized的自定义状态条件类型。然后,这些条件可用于评估 Pod 准备情况。
通常情况下,箱准备状态仅由箱中所有容器的准备状态决定,这意味着如果所有容器都是Ready,那么整体也是Ready。如果准备状态门被添加到一个箱,那么一个箱的准备状态由所有集装箱的准备状态和所有准备状态门条件的状态决定。
让我们看一个例子,以便更好地理解这是如何工作的:
上面的清单显示了一个名为www.example.com/some-gate-1的有单一准备门的分离舱。查看 status 节中的条件,我们可以看到ContainersReady条件是True,这意味着所有容器都准备好了,但是定制就绪门条件是False,因此 Pod 的Ready条件也必须是False。
如果您在这样的 pod 上使用kubectl describe pod ...,您还会在条件部分看到以下内容:
我们现在知道实现这些额外的就绪条件是可能的,但是它们真的有必要吗?难道仅仅利用探针进行健康检查还不够吗?
在大多数情况下,探测应该足够了,但是也有需要进行更复杂的准备状态检查的情况。准备就绪关口最常见的用例可能是与外部系统同步,例如云提供商的负载平衡器。例如 GKE 的 AWS 负载平衡器或容器本地负载平衡。在这些情况下,就绪门允许我们让工作负载网络感知。
使用准备状态门的另一个原因是,如果您有外部系统,可以使用应用程序指标对您的工作负载执行更彻底的健康检查。这有助于将您的系统集成到 Kubernetes 的工作负载生命周期中,而不需要对 kubelet 进行更改。它还允许外部系统订阅 Pod 条件变化并对其采取行动,可能应用变化来补救任何可用性问题。
最后,如果您有部署到 Kubernetes 的遗留应用程序,而该应用程序与活性或就绪性探测不兼容,那么就绪性检查可以是一个救命稻草,但是它的就绪性可以通过不同的方式来检查。
要了解该特性的完整原理,请查看 GitHub 中的原始 KEP。
说够了,让我们创建我们的第一个就绪门。我们所需要做的就是在 Pod spec中添加readinessGates节,其中包含我们想要的条件的名称:
添加门很容易,但是更新稍微复杂一点。kubectl子命令不支持对象状态的修补,因此我们无法使用kubectl patch将条件设置为True / False。相反,我们必须使用直接发送到 API 服务器的PATCH HTTP 请求。
访问集群 API 服务器最简单的方法是使用kubectl proxy,它允许我们在localhost上访问服务器:
除了在后台启动代理之外,我们还使用curl来检查服务器是否可达,并向服务器查询我们将要更新的 pod 的清单/状态。
既然我们有了到达 API 服务器的方法,让我们尝试更新 Pod 状态。每个就绪门状态条件默认为False,但让我们从显式设置它开始:
在这个代码片段中,我们首先使用针对 API 代理服务器的PATCH请求,将 JSON 补丁应用到 Pod 的status.condition字段。在这种情况下,我们使用了add操作,因为状态尚未设置。此外,您还可以看到,当我们列出带有-o wide的 pod 时,READINESS GATES列显示0/1,表明闸门设置为False。在kubectl describe的输出中也可以看到同样的情况。
接下来,让我们看看如何将值切换到True:
与前面的代码片段类似,我们再次使用PATCH请求来更新条件,但是这一次我们使用了replace操作,特别是在由/status/conditions/0指定的列表中的第一个条件上。但是请注意,自定义条件不一定要在列表中排在第一位,所以如果您将使用一些脚本来更新条件,那么您应该首先检查您应该更新哪个条件。
像我们上面看到的用curl更新条件适用于简单的脚本或快速手动更新,但是通常你可能需要更健壮的解决方案。考虑到kubectl在这里不是一个选项,您的最佳选择将是 Kubernetes 客户端库之一。出于演示目的,让我们看看如何在 Python 中实现:
我们需要做的第一件事是向群集进行身份认证并创建 Pod。在这种情况下,使用从~/.kube/config加载您的凭证的config.load_kube_config()来完成身份验证部分,一般来说,虽然使用服务帐户和令牌来对集群进行身份验证更好,但是可以在文档中找到相关示例。
至于第二部分——Pod 创建——这相当简单,我们只需应用 Pod 清单,然后等待,直到它的状态阶段从Pending开始发生变化。
在 Pod 运行的情况下,我们可以通过将其状态设置为初始值False来继续:
除了设置状态,我们还在更新后查询集群的当前 Pod 状态。我们查找对应于Ready条件的部分并打印其状态。
最后,我们可以用下面的代码将值翻转到True:
这里的代码与前面的例子非常相似,但是这一次我们寻找类型为www.example.com/gate-1的条件,我们用print验证它的当前状态,然后使用replace操作将更改应用到在index列出的条件。
上面的 shell 脚本和 Python 代码都演示了如何实现就绪门及其更新。但是在实际应用中,您可能需要一个更健壮的解决方案。
这种情况的理想解决方案是定制控制器,它可以监视 pod,将readinessGate节设置为相关的conditionType节。然后,控制器将能够根据观察到的 pod 状态更新条件,无论是基于定制 pod 指标、外部网络状态还是其他。
如果您正在考虑实现这样的控制器,那么您可以从现有的解决方案中获得一些灵感,比如前面提到的 AWS 和 GKE 负载平衡器或者(现在已存档的)[kube-conditioner](https://github.com/itaysk/kube-conditioner)。
本文最初发布于martinheinz . dev
https://itnext.io/hardening-docker-and-kubernetes-with-seccomp-a88b1b4e2111
原文:https://towardsdatascience.com/improving-chores-using-data-d972e5f24475?source=collection_archive---------24-----------------------
我们先说,我不记得有什么事情做得这么好。我记得我感兴趣的事情,但我患有多动症,这意味着我在追逐多巴胺。这些年来,我已经能够欺骗我的大脑来提高我做不有趣的任务或杂务的能力,但它一直具有挑战性。
在压力很大的情况下,我会用音乐来保持精力充沛的状态,但是我必须持续保持活跃才能很好地工作。在某些时候也有一个崩溃期。对于搬到一个新的地方,这是一个有用的工具。
通常,列表是最有帮助的。大约六个月前,我创建了一个包含三列的表格:{任务、询问日期、完成日期}。目的是帮助了解哪些事情是重要的,以及我完成工作的效率如何。我可以看看这个清单,找到我能完成的事情。

所有图片由作者提供
几个月后,我决定做一些分析,看看我在完成任务方面是否有所进步。数据中唯一的问题是,通过观察和记录这类数据,影响了我的行为。
我开始按月查看一些指标:

最大的改进是当天完成。我最终完成了 90%我想完成的事情。完成任务的平均时间普遍下降,这可能与一些长期任务有关。
我也按周查看了这些数据

利用这些数据,我做了一个散点图来寻找任何趋势。但是,似乎并没有大的趋势。

我们可以从整体上看这些数据,以了解任务完成的速度。似乎我能够在一周内完成 70%的任务,在两周内完成 80%多一点的任务。然后是长尾,这主要是由于新任务的优先级推低了旧任务。

星期几呢?对我来说,周二似乎是最不适合完成任务的一天,但是周六承担了很多完成任务的负担。

完成时间可能看起来有点长,但是如果有一个稳定的任务完成流,那么单个任务的持续时间是由优先级的重新调整驱动的。看看每周的要求和完成的任务,每周都有稳定的任务流被完成。

每次我觉得数据没什么用的时候,它似乎成了过更好生活的绝佳解决方案。有时候,没有目标是很难完成任务的,但这个列表绝对给了我一个目标和一个指定的目标。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以在 Medium 或者 Patreon 上关注我。
浓缩咖啡系列文章
工作和学校故事集
个人故事和关注点
乐高故事启动页面
摄影飞溅页
数据科学:基础知识
数据展示要领
生活色彩数据
如何轻松进入数据科学领域
数据科学家的一天
机器学习中的隐私:PII
收集爱好数据集:咖啡
使用自适应滤波器的车道识别
原文:https://towardsdatascience.com/improving-classical-ai-planning-complexity-with-planning-graph-c63d47f87018?source=collection_archive---------23-----------------------
使用一个新的搜索空间,规划图,以提高表现力和复杂性的问题,发现在经典的规划方法。

规划图(图片由作者提供)
人工智能规划的经典方法使用状态空间和计划空间来搜索解决方案以解决规划问题。在状态空间搜索中,通过应用适当的动作,初始世界状态经历几次变换,直到找到解决方案以达到目标,或者搜索算法终止并返回失败。我们可以使用 BFS、DFS、Dijkstra、A*等搜索算法。

状态空间(作者图片)
由此产生的解决方案是一系列动作,当应用时,通过一个或多个步骤将初始世界状态转换为目标状态。

国家空间计划(图片由作者提供)
另一个搜索空间是计划空间,我们将计划作为我们的节点,并尝试以 开放(未解决)目标 和 威胁 的形式解决 缺陷 。

平面空间规划(作者图片)
这种方法本身相当复杂,我们不打算在这里讨论细节,如果你有兴趣了解更多,你可以阅读这篇文章:
https://medium.com/swlh/plan-space-search-2d877b4ab231
在《自动化规划:理论与实践》一书中,Malik、Dana 和 Paolo 提到传统方法的研究由于表达性和复杂性的原因而停滞不前,规划图——一种新的搜索空间,允许搜索空间大小的改进,从而为解决更复杂的规划问题开辟了一条道路。
我们将在下面的章节中探讨细节,以理解它如何改进传统规划方法中发现的问题。
对于我们的例子,我们将使用简化的码头工人机器人(DWR)领域和问题,经常在人工智能规划教程中使用。

简单码头工人机器人领域(图片由作者提供)
在这个领域中,我们有两个机器人 robr 和 robq ,两个容器 conta 和 contb ,以及两个位置 loc1 和 loc2 。
有三种可能的操作:
动作的 前置条件 和 效果 的细节可以在下面的 pddl 文件中看到。
我们还有五个谓词来表示状态:
我们用 PDDL (规划领域定义语言)来表示我们的规划领域和规划问题。下面是我们感兴趣的领域的 pddl 文件。
现在让我们开始深入研究规划图及其规划器。
规划图基于可达性分析的思想,可达性分析是一种计算一组状态是否可以从一组初始状态到达的过程。
让我们通过一个例子来逐步理解这个概念。首先,这是我们的初始状态:

初始状态(图片由作者提供)
我们使用谓词来表示我们的世界状态(为了简单起见,我们省略了相邻的谓词):
我们想知道这个状态是否可以达到:

目标状态(图片由作者提供)
我们没有在图片中展示机器人,因为我们不关心它们的位置,我们只对集装箱的位置感兴趣。我们用这种方式表示它:
现在,最简单的方法是使用可达性树。我们从初始状态(根节点)开始,搜索状态(边)的适用动作,然后我们生成预测状态(子节点),我们对所有子节点重复该过程,直到达到指定的 深度 。
这是深度=1 的可达性树。

可达性树-深度=1(作者图片)
并且,这是深度=2 的可达性树。

可达性树-深度=2(作者图片)
我们可以看到,这并没有很好地扩展,当我们增加深度时,节点的数量会激增。此外,我们还没有在这个深度找到目标,我们需要进一步扩展它。
我相信你会这样想,我们可以用图来改善它,因为有些节点确实是重复的。这是深度=2 的图形版本。

可达图—深度=2(作者图片)
它略有改进,但要达到我们的目标(红色节点),我们需要深度=6,如下所示:

可达图—深度=6(作者图片)
很吓人吧?规模确实很大,因为当我们增加深度时,复杂程度会增加,如果我们想解决更复杂的问题,这在某些时候变得不切实际。
规划图的概念是带有松弛的的可达性图。在每个深度(也称为 级 )它不使用单个状态,而是使用状态的联合,因此我们可以认为每个级别只有一个节点。
与我们通过应用可应用的动作来生成节点的可达图不同,所有的动作都用来生成状态的联合。

规划图(图片由作者提供)
另一个区别是,在可达图中,一个状态是一组一致命题,但在规划图中,它不是。例如在图中的前提条件 1 中, robr 的位置可以同时在 loc1 和 loc2 中。
类似地,这些动作并不总是兼容,它们可能会抵消彼此的影响。
在规划图中,为了跟踪这些不一致的命题和不兼容的动作,我们使用了所谓的互斥(mutex)。在每个级别,我们都有:
现在,让我们看看如何一步一步地构建规划图。最初,我们有 0 级,其中只有和前提条件 0,等于初始状态。

0 级(图片由作者提供)
接下来,我们建立下一个级别,级别 1。从一系列行动开始:

A1 号楼(图片由作者提供)
这个等式的意思是 A1 是一组动作,它们:
下一步是建立一套主张:

建设 P1(图片由作者提供)
我们采用先前构建的 A1,并创建所有行动的积极效果的联合。
接下来的两步是构建互斥体,从 A1 的互斥体开始:

为 A1 构建互斥体(图片由作者提供)
A1 的两个动作是互斥的,如果它们是依赖的(它们互相抵消对方的效果)或者它们的前提条件是 P0 的互斥。如果有负面影响会抵消行动正面影响的前提条件,则两个行动是相关的:

依赖动作(作者图片)
最后一步是为 P1 构建互斥体:

为 P1 构建互斥体
这基本上意味着 P1 的一对命题是互斥的,如果:
现在,这很复杂,但这一步可以简单地重复到下一个级别。这是深度=3 的规划图的结果。

规划图—深度=3
在这一点上,我们可以看到,与可达性树和可达性图相比,规划图的构建要复杂得多,但正如您所看到的,它的搜索时间更快,大小更小,更重要的是,它更易于我们进行分析或调试。
构建规划图时的另一个要点是,在一定深度后,它将是固定的,这意味着动作集和命题集不会再发生任何变化。
我们的目标在第三级达到了,我们可以看到这两个命题:
在 P3(见上图)。
现在我们有了规划图——数据结构,我们可以使用搜索算法来为我们的规划问题找到解决方案。
这种方法中的计划不是一系列的行动,而是一系列行动的集合。一个级别集合中的所有操作都可以独立执行,这意味着它们之间没有顺序约束。
在我们的例子中,从 P3 到 P0 向后执行搜索,递归地求解目标中的所有命题。在我们解决它们之后,我们递归地使用行动的前提条件作为子目标,直到我们达到 P0。

向后搜索(作者图片)
我们跳过的一件重要的事情是,在每一关我们都有 虚拟动作 ,它们什么也不做——它们的前提条件和效果是一样的。这允许算法对于那些在较低级别中已经被满足的命题平稳地工作。
对于我们的示例,我们的解决方案是:
我们现在有希望理解如何通过使用规划图方法来改进传统规划方法的复杂性。与可达性树和可达性图相比,规划图的构建更为复杂,但它节省了我们搜索解决方案的时间和空间,如分步示例所示。
我希望你理解这个概念,在下一篇文章中,我们将用 Python 实现这个方法来证明它是可行的,并帮助我们进一步理解它。
原文:https://towardsdatascience.com/improving-coffee-grind-distribution-using-adaptive-thresholds-194e001f301?source=collection_archive---------45-----------------------
之前,我已经使用了筛子和不同的成像技术来改进咖啡磨粒分布测量,现在我将应用我的一项旧技术来更好地收集成像数据。通常,图像很容易找到依据,但有些却不是。由于光线与咖啡的相互作用,简单的阈值并不总是有效,所以我仔细观察了一下。
由此产生的变化使我能够测量的咖啡颗粒的数量增加了两倍,这是一个直接的自适应滤波器,有助于解决照明问题。
我拍了一张照片,用一张纸确定了测量地面的真实情况(像素到毫米),然后用一个阈值来寻找粒子。

所有图片由作者提供
我从看一些图片开始。我觉得这些分布没有完全捕捉到微粒的数量。所以我看着我的灰度图像,它是红色、绿色和蓝色平面的乘积。然后我取逆得到黑点为 1 为最高值。
我以两种方式想象这一点:

它揭示了有许多较亮的斑点可能没有达到阈值。在这种情况下,阈值为 0.9,这有两种影响:
在我攻读硕士学位期间,我在计算机视觉的青铜时代研究过一辆自动驾驶汽车。使用低级别图像处理,我开发了一种称为低滤波器的方法,用于在尝试进行车道线识别(自主移动机器人的车道识别和路径规划)时改善滤除噪声。
“低”过滤器基于这样的观察结果:从图像底部到顶部,平均行值会增加。这可以用来帮助滤除全局阈值不能滤除的不太理想的像素。

左上:原图。右上:桶被移除。左下角:应用了低通滤波器。右下:门槛。
该过滤器的工作原理是基于该阈值丢弃每个颜色平面中的像素:
阈值=平均值(行)+ A *标准值(行),其中 A 是用户定义的常数。
所以我把这个想法应用到咖啡渣上!
我按行和列的方式应用了这个过滤器,我发现 A=2.5 对我的应用程序来说相当不错。我似乎比以前获得了更多的粒子和粒子边缘。

我们可以查看三幅图像的总计数,几乎是三倍。

我展示了前后粒子分布的对比。我在顶部有三个设置,然后我在下面拆分每个设置。

对于最小的设置有轻微的偏移,这证实了该技术能够挑选出更小的颗粒,或者至少它挑选出更多的小颗粒。

这看起来像是 15 号场景怪异分布的转变。

我越看这些分布,它们似乎越不符合我的想法,因为它们很不平坦。除了轻微的移动,这个基本上不受影响。

这些分布很接近,所以我观察了原始粒子的分布。

正如你在这张设置分类图中看到的,这两者非常接近。曲线的形状有些变化。

主要的问题仍然是假设咖啡是圆的,但它不是。然而,这种技术仍然可以比较好地比较不同的研磨设置和研磨机。我更有信心有一个自适应过滤器来帮助表征粒子。这种技术也使整个过程对光照变化更加鲁棒。
下一步是比较多个研磨设置和研磨机。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以关注我中。
浓缩咖啡系列文章
工作和学校故事集
个人故事和关注点
乐高故事启动页面
摄影启动页面
使用图像处理测量咖啡研磨颗粒分布
浓缩咖啡过滤器:分析
浓缩咖啡过滤篮:可视化
建模咖啡研磨机
浓缩咖啡过滤器对比:佩萨多 vs VST
浓缩咖啡篮(VST):有脊与无脊
IMS superficial vs VST:小样浓缩咖啡过滤器对比
浓缩咖啡模拟:计算机模型的第一步
克鲁夫:对技术状态的进一步分析
克鲁夫咖啡筛:一项分析
浓缩咖啡篮隆隆作响:机器人 vs 铂尔曼 vs 正派
原文:https://towardsdatascience.com/improving-coffee-grind-measurement-using-a-sifter-7b358f9c8331?source=collection_archive---------43-----------------------
在之前的中,我研究了使用图像处理来测量研磨颗粒分布,我发现这很有挑战性。拍摄图像需要细心,但这还不是全部。咖啡似乎发生了一些事情,因为根据图像处理技术,在壁龛上设置 15 和 30 产生了类似的结果。我怀疑这是由于地面粘在一起,为了测试这一点,我将使用筛子。
我有一个 Kruve 筛,我有几个筛(250,400,500,800),但我主要用 400um 和 500um。考虑到我最关心的是使用一些利基研磨水平来制作一个断奏镜头,我将注意力集中在这两个屏幕上,这两个屏幕给出了三个筛选水平。


所有图片由作者提供
我用了 4 或 5 颗豆子,所以测试很快。然后我筛出每一层的粉末,测量每一层的重量,然后把每一层的粉末放在纸上成像。

这里是使用研磨设置 15 在纸上的三个筛水平。



设置 15 幅图像:左边是<400um, Middle is between 400um and 500um, and the right is > 500um
我使用了从每个筛子测得的重量,并将其与成像测量结合起来,给出了下面的这些分布。这假设粒子是球形的(它们不完全是但是任何 3D 形状都与三次方成比例)。

然而,这些图表并不正确。设置 15 和 0 看起来太接近了,它们看起来都有很多较粗的颗粒,这是不可能的,因为使用了筛子将它们过滤掉。让我们看看设置 0 和每个筛子中颗粒的分布:

大于 500 微米的粉末数量是有问题的。对于< 400um 或中间值,任何高于 500um 的值都是错误的。很可能是由于多个粒子聚集在一起导致算法出现问题。
此外,如果我们合计三个筛子的体积,我们不会得到相对于地面事实的正确分布。

如果我们忽略所有期望筛之外的颗粒,我们得到的分布看起来好得多。这些曲线是在合并筛上数据时忽略筛范围之外的颗粒的结果。

将成像和筛子结合起来似乎是一种可以廉价确定咖啡研磨颗粒分布的技术,但这仍然没有达到更理想的情况,即任何人都可以将咖啡研磨颗粒放在一张纸上,并拍摄图像以获得颗粒分布。
在第 3 部分中,我将研究这些数据,看看使用图像处理来自动清理图像可以做出哪些改进。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以在中关注我。
浓缩咖啡系列文章
工作和学校故事集
个人故事和关注点
乐高故事启动页面
摄影飞溅页
使用图像处理测量咖啡研磨颗粒分布
浓缩咖啡过滤器:分析
浓缩咖啡过滤篮:可视化
造型咖啡研磨机
浓缩咖啡过滤器对比:佩萨多 vs VST
浓缩咖啡篮(VST):有脊与无脊
IMS 超细 vs VST:小样浓缩咖啡过滤器对比
浓缩咖啡模拟:计算机模型的第一步
克鲁夫:对技术状态的进一步分析
克鲁夫咖啡筛:一项分析
咖啡篮隆隆声:机器人 vs 铂尔曼 vs 体面
原文:https://towardsdatascience.com/improving-machine-learning-outcomes-by-focusing-on-framing-timing-and-targets-28d5d112b697?source=collection_archive---------36-----------------------

阿尔瓦罗·雷耶斯在 Unsplash 上拍摄的照片
为了构建成功的机器学习解决方案,每个相关人员都需要理解一些基本的想法。在这篇博文中,我们来看看设计过程的三个关键早期阶段,经理们可以关注这些阶段,以确保项目朝着成功的方向发展。
这篇文章假设读者已经了解机器学习的区别,如监督和非监督模型,训练和测试阶段,以及整个机器学习生命周期。回到定义业务问题的最早阶段,我们将注意力集中在三个关键目标上。
数据科学培训在一定程度上介绍了这些目标。然而,他们往往没有得到应有的重视。部分原因是,从表面上看,它们似乎是显而易见的:框定问题,决定何时进行预测,并确定模型学习的目标变量。
这是项目涉众可以用来影响项目成功可能性的三个主要杠杆。出于这个原因,深入研究每一个问题是很重要的,以便发现一些微妙之处,这些细微之处可以区分是否获得了解决您的业务问题的模型。
在应用机器学习的世界中,需要决定机器学习模型将如何与业务进行交互。这包括确定模型做出的预测将如何影响或决定决策。具体一点,通过模型的输出被业务使用的确切的机械方式来思考。企业是否只在模型产生特定价值时才采取行动?或者企业总是采取行动,但是模型输出修改了行动?预测的具体真实值会影响行动吗?还是仅仅决定团队是否采取行动?
人们很容易认为这种关系会非常简单,或者对于一个给定的问题只有一个选项,然而通常情况并非如此。让我们来看一个例子,它说明了如何针对一个常见的问题生成可能产生更好结果的替代框架。
例子:重构预测问题
许多组织遇到了与预测未来给定时间点将订购或需求的货物量相关的挑战。想要理解那些数字的欲望通常与 一个关键的商业决策 有关。它可以确定:手头是否有足够的库存?或者也许:会有足够的仓库空间吗?
在框定机器学习项目时,这意味着什么?
有时,人们非常重视一个模型,该模型试图预测将被销售或交付的小部件的确切数量,然而在现实中,支持特定业务所需的只是一个传达该数量是否会超过给定阈值的模型。在许多情况下,构建后一种模型会更容易、更有效。
这就是关注问题框架的含义:仔细思考为了达到为目标业务问题提供解决方案的结果,有必要知道什么。
这里的一个很好的经验法则是,通过 使机器学习模型的输出更接近企业期望做出的具体决策 ,可以实现更好的结果。这可能有所帮助的原因是因为 模型将集中在业务部门需要了解的决策 上,而不是试图对每一个细节进行建模。
如果你对重构预测问题的其他例子感兴趣,那么我推荐你在 AWS Summit Online 网站上观看 Charles Prosper 的精彩演讲。
所有的机器学习模型都需要产生一个输出,并且在特定的时间点产生这个输出是物理上的必然。例如,这个时间是星期四下午 3 点可能并不重要,但重要的是相对于被预测事件的时间。
例如,在客户关闭账户前一个小时预测客户是否会流失,与在客户流失发生前一个月预测客户流失有很大的不同。另一个重要因素是需要采取的行动。不同的行动需要不同的时间来实施和生效。向不满意的客户发送即时报价短信可以快速实施并取得成效。相反,如果需要的行动是派遣员工去拜访客户以解决他们的问题,那就需要更多的时间。
这看起来像是一个独立于构建模型以预测客户流失的任务的实现细节,但在许多情况下并非如此。为什么?模型依赖于数据,而数据只有在关键时间点才变得可用。如果客户流失模型依赖于以简化的方式使用企业服务的客户的信息,那么,尽管它是准确的,但它可能无法产生足够早的预测以采取行动。
出于所有这些原因,仔细考虑模型的预期动作是很重要的。考虑执行这些操作所需的时间,然后确保设计的计时组件足以让这些操作生效。与数据科学团队清楚地沟通 以确保他们仅使用在事件被预测之前足够远的时间内可用的数据,以允许预期的行动 。数据科学培训和测试程序应反映这一时间安排,以便用于评估模型的任何性能指标都能准确反映模型在现场如何执行*。*
在机器学习的大多数实际应用中,需要确定一个历史信号来通知正在建立的模型。在监督机器学习中,该信号通常被称为目标变量。
在某些情况下,定义目标变量可能很简单,而在其他情况下,定义目标变量似乎很困难。为了说明困难的常见来源,让我们再看一个例子。
示例:定义一个不会强化现状的目标变量
一家大型连锁超市要求数据科学团队开发一个应用程序,向忠诚会员推荐要购买的产品。最初的诱惑可能是把这个项目定位为产品推荐者。可能有一个团队成员对此有直接的经验,这意味着团队可以立即开始工作。然而,进一步挖掘可能会发现,推动该项目的主管实际上希望这些建议推动鼓励健康饮食的目标。现在,问题不再是简单的推荐,而是决定如何为忠诚会员确定产品,这些产品既是他们可能购买的东西又是健康饮食的选择*。*
在这种情况下,客户的历史购买模式可以用来确定推荐者的信号,但这不会产生正确的结果。这只会鼓励他们继续购买他们以前购买的东西,或者有类似经历的人购买的产品。简而言之,这将巩固现状。
定义这个特定目标最困难的部分是如何确定一个健康的饮食选择,这也是一些人可能会购买的东西。是否需要手动将一些购物篮分类为健康与不健康?有可能从产品描述或成分中提取信息吗?是否值得查看顾客的历史购物篮,并对哪些内容是健康的进行分类,以便从他们自己以前的健康行为中获得建议?
有许多方法可以解决这个问题,但关键是不能盲目地将所有历史数据用作训练集,因为企业希望推动的结果不同于历史行为。
虽然这是一个微不足道的例子,但它表明了一个普遍存在的问题: 依赖历史数据并不总是会导致企业正在努力实现的结果。 机器学习项目管理的最佳实践是严格的目标变量评审会议。在该会议中,所有利益相关者可以坐在一起,确定提议的目标变量和封装它的数据是否会准确地产生一个产生他们期望的结果的模型。
有了框架、时机和目标这三个核心目标,企业可以确保他们的机器学习项目与他们的目标保持一致。这些主题可以在评审项目和向业务的其他部分展示项目时用作基线。虽然它们不能保证成功,但是定义这些目标通常意味着即使一个简单的模型也能交付业务成果。
原文:https://towardsdatascience.com/improving-model-management-practices-for-machine-learning-teams-3bec1dc40929?source=collection_archive---------24-----------------------
在本文中,我们阐述了如何通过模型管理功能来识别使机器学习(ML)活动更有效的问题。然而,我们并没有提出任何具体的解决方案,而是提供了一个系统地思考模型管理问题的框架。这样一个框架对于那些处于产业化或扩展他们的 ML 系统的边缘的团队来说是很有用的,他们可以在处理 ML 模型时解决技术债务或缺口。如果你对此感兴趣,请继续阅读。

图 1:只有一小部分真实世界的机器学习系统由机器学习代码组成,如中间的小黑框所示。所需的周边基础设施庞大而复杂。最初发表在论文机器学习系统中隐藏的技术债务。
ML 系统是复杂的,因为它们将所有的技术问题与维护代码库结合在一起,而代码库又是由数据和建模问题产生的。如图 1 所示,生成 ML 模型的代码只是整个系统中用于端到端模型处理的一小部分。模型管理的目标是使所有建模活动中的 ML 模型的处理尽可能简单和高效。在接下来的小节中,我们将简要描述建模活动以及管理功能。如果您已经熟悉这些概念,请跳到下两个部分。

图 2:ML 中的三个主要建模活动:开发、选择和应用。这些活动具有线性相关性。这些活动主要由数据科学专家监督和执行,而活动的结果可由利益相关方(如业务方的代表)访问和批准。这些活动是在团队可访问的基础设施上的一系列平台上进行的。图片由作者提供。
图 2 描述了 ML 建模中的三个主要活动:模型开发、选择和应用。模型开发是使用训练数据训练新模型的过程。模型选择是从一组训练好的模型中选出一个获胜模型的过程。模型应用是使用选定的模型进行预测工作以促进业务流程的过程。这些活动具有线性依赖关系,这意味着它们必须按照一定的顺序执行。例如,在生产中应用一个模型一段时间后,它的性能可能会因为不同的原因而下降。在这种情况下,需要重新开发和选择新的模型。
建模活动的结果最终会进入业务流程,并且很可能需要得到业务代表的理解和批准。这些活动由数据科学专家执行,即在这些活动中具有知识和/或经验的分析师、科学家、工程师。这些活动在众多平台上开展。为了更好地理解,我们在这里列出了几个这样的平台:
现代组织中的团队访问云计算服务提供的基础设施中的大多数平台,如 Azure 。你会发现来自其他提供商的类似平台,比如谷歌云、亚马逊网络服务。

图 3:模型管理过程中的关键能力:跟踪、注册、服务、分析、监控和文档化。ML 建模活动使用这些能力来更有效和透明地管理这些活动的输入和输出。数据科学专家是这些能力的主要利益相关者。这些功能主要是由团队中的工程专家通过团队可以访问的一系列平台提供的。图片由作者提供。
模型管理功能使得使用模型变得更加简单、透明和高效。如图 3 所示,主要功能是跟踪、注册、服务、分析和监控。模型跟踪是保持关于建模活动的关键信息记录的能力。模型注册是使用一系列打包格式将模型注册为版本化模型的能力。此外,它让模型处于众所周知的状态,例如,测试、生产、存档等。并且能够改变模型的状态。模型服务是使模型可用于预测过程的能力。模型分析是通过输入和输出的质量来评估模型质量的能力。模型监控是跟踪模型和数据质量并对不可接受的变化做出反应的能力。模型文档是在执行活动的团队之内和之外获取和传递关于建模活动的部落知识的能力。
模型管理功能由图 1 中描述的不同系统来表示。我们在此列举几个众所周知的公共平台作为例子:

图 4:模型管理能力与建模活动之间的关系。颜色编码的模型管理功能作为较小的表示放在不同的建模活动中。图片由作者提供。
图 4 展示了模型管理功能和建模活动监控之间的关系。我们建议,改进模型训练需要关注模型跟踪、注册、分析和记录。改进模型选择需要关注模型注册、服务、分析和记录。改进模型应用需要采用所有的模型管理功能。
为了更好地阐明这一点,我们提出了下面的框架,重点关注七个改进挑战。
为了更好地理解这些挑战,我们使用了一些改进因素:
挑战 1: 观察并展示跑步过程中发生的事情
相关性:所有三个建模活动
关键改善因素:
挑战 2: 决定一个模型的质量是否适合生产使用
相关性:模型开发和选择
关键改进因素:
挑战 3: 在生产中高效使用模型
相关性:模型应用
关键改进因素:
挑战 4: 监控生产中模型的质量
相关性:模型应用
关键改进因素:
挑战 5: 使用开发团队范围之外的模型
相关性:模型应用
关键改善因素:
**挑战六:**重现一次奔跑
相关性:所有三个建模活动
关键改善因素:
挑战 7 :执行整个流程,包括但不限于安排活动、处理对资源和工件的依赖、准备、风险监控等。建模活动
相关性:所有三个建模活动
关键改善因素:
**整体挑战:**传授建模活动的知识
相关性:所有三个建模活动和以上所有七个挑战
关键改善因素:
一次性解决所有这些挑战是不可行的。当一个团队没有解决一些或所有的挑战时,采取一种务实的方法是很重要的。我们在这里提供一种方法。挑战 1-4 比其余的更重要。因此,我们作为一个团队可以对属于该组的挑战进行优先排序。在每个挑战中,准确性和功能性比持续时间和文档化更重要。
显然,细节决定成败。你面临这些挑战吗?这些澄清有用吗?你不同意这样的观点吗?让我们知道。
我要感谢我在 H&M 的同事,他们为这篇文章提供了意见和反馈。
原文:https://towardsdatascience.com/improving-models-with-missing-values-52fb19559948?source=collection_archive---------37-----------------------

图片由作者提供。
当我加载一个新的数据集时,我首先检查的事情之一是我们有多少丢失的值。看到所有这些南价值观令人失望和沮丧。超过 50%的值缺失的列并不罕见。我们都对新的数据集感到兴奋,主要是因为新的数据集意味着更多的数据和更多的改进。但是像任何其他以前的数据集一样,新的数据集充满了无用的缺失值!!!!
但是等等!缺失的价值观真的无价吗?我能从缺失的值中获得一些额外的信息或见解吗?
在这篇短文中,我将改变你对缺失数据的看法。我将向您展示如何以不同的方式看待缺失值,并利用缺失数据提高模型性能。让我说清楚,专业数据科学家和普通数据科学家之间的主要区别之一是他们如何处理缺失值并从中获得有价值的信息。
我先问个问题。当您看到缺少值时,您会怎么做?我们中的许多人最初(或永久)移除它们。我们中的一些人折磨自己几个星期来归咎于他们。
在调查其背后的原因之前,删除或输入缺失的值有三个负面后果。
让我说清楚。我不反对删除或添加丢失的值,但是你可以问自己几个问题。这里有三个重要的问题,在遗漏数据删除或插补之前,你必须问自己。

马库斯·温克勒在 Unsplash 上的照片
想象一下,我们有一个医学研究小组收集的患有和未患有某种癌症的人的数据集。你有两组的所有信息。缺失的一列可能是活检测试结果。没有癌症的人的价值观缺失是有道理的,但为什么我们对最终确诊为癌症的人有很多价值观缺失呢?一个简单的解释是,收集数据的医学研究小组无法找到一些患者的活检结果,因此跳过了这些结果。但一个更重要的原因可能是医生因为其他因素而没有安排活检。例如,可能其他测试结果使医生确信应该诊断癌症(即使没有活检)。或者另一种可能性是,医生无法诊断癌症,并拒绝安排活检测试。如果您删除或忽略丢失的值,您将丢失大量信息。在本例中,缺失值是这种特定类型癌症的症状非常明确和非常不明确的一类患者。活检结果中没有缺失值的患者(样本)是具有足够症状的患者,医生为他们安排了活检。
如您所见,缺失值为我们提供了至关重要的信息。获得这些有价值信息的唯一方法是思考为什么我们会看到一个缺失的值并理解它的意义。
请记住,缺失值不仅仅是由数据收集者的错误造成的。也许,其他因素(本例中的医生)出于某些原因决定将其作为缺失值。

迈克·阿隆佐在 Unsplash 上的照片
有时,一个或几个特征与缺失值之间有很好的相关性。换句话说,缺失值不是随机缺失的。
一个非常简单的例子是从患者信息表中收集的数据。这些数据中的许多(尤其是原始数据集)都缺少关于怀孕问题的值。男人不检查这些问题(当然),这些表格的原始数据集将在那些列中充满缺失值。请注意,一些女性可能没有检查这些问题,但是男性患者的缺失值与女性患者的缺失值的含义不同。
有时,您可能会看到丢失的值与一些奇怪的特征(如 id 或代码号)之间的关联。例如,在医院中,患者 ID 号或订单编号可能有特定的含义。有时,缺失值和 ID 号之间的关联可以帮助我们理解缺失值以及这些代码和数字背后的结构。

戴维·特拉维斯在 Unsplash 上拍摄的照片
(在调查缺失值背后的原因之前)移除缺失值样本的另一个危险是给我们的模型或研究带来偏差。假设我们有一个在线约会应用程序的用户资料数据库。该数据库可以基于年龄和种族为可选问题的问卷来制作。我们应该期待女性或少数民族群体对这些与年龄和种族相关的问题不予回答吗?我认为这是一种可能性。如果我们删除基于缺失值的行,我们会在模型中引入偏差。我的假设(应该被检验)是,在去除了提到的缺失值的样本后,数据库应该更偏向于白人男性。通过这个假设的例子,我想向您展示,在不考虑固有偏差的情况下删除缺失值的样本会如何导致有偏差的模型。
缺失值可能包含重要的信息,这些信息不仅可以改善我们模型的性能,还可以改善我们正在解决的业务问题。
在删除或添加丢失的值之前,问自己三个问题。
1)是什么产生了这些缺失值?
2)缺失数据和其他数据之间有什么关联吗?
3)有缺失值的样本是否存在偏倚?
原文:https://towardsdatascience.com/improving-nlp-with-causality-2dec1fa90b74?source=collection_archive---------10-----------------------

克里斯·劳顿在 Unsplash 上的照片
两年前,谷歌宣布正在使用一种人工智能语言模型,命名为 BERT,以改进谷歌搜索;当时大约有 10%的搜索使用了这个词。一年后,在一篇题为“人工智能如何为更有帮助的谷歌提供动力”的文章中,该公司报告称,100%的搜索查询都在使用 BERT。然而,这些公告并没有包括模型中编码的性别和种族偏见的信息。自从 BERT 公开发布以来,许多研究人员已经注意到模型中存在的偏差( Kurita 等人 2019 , Bhardwaj 等人 2020 , Bender 等人 2021 )。然而,谷歌尚未解决围绕这一如今无处不在的模式的许多紧迫的伦理问题。抛开令人失望的问责缺失,我们可以选择专注于解决方案;因此,本文主要讨论如何使用因果关系来改进像 BERT 这样的 NLP 模型。
在之前的一篇文章中,我提到了最近的一篇计算语言学调查论文(费德等人,2021a ),该论文着眼于因果关系和 NLP 模型之间的关系。我的前一篇文章关注的是如何将 NLP 方法用于文本变量进行因果推理,而本文关注的是反向连接。在这里,我探索因果推理如何帮助改进传统的 NLP 任务,如文本分类、情感分析或文本生成。我首先简要介绍像 BERT 这样的 NLP 模型,强调其固有的弱点,并提出因果关系可以解决现有缺点的广泛方法。接下来,我将讨论伪相关性,并介绍一个实际的案例研究,该研究将在本文的剩余部分中使用。接下来,我将重点关注健壮性和敏感性——特别是因果驱动的数据增强和分布标准可以克服挑战的方式。这导致了对 NLP 模型中的公平性和偏见的讨论,以及如何使用因果关系来解决提高公平性和减少偏见的目标。最后一节讨论了因果关系在提高 NLP 模型的可解释性和可解释性中的应用。最后,我对未来的工作提出了一些想法,这些工作将进一步探索因果关系和 NLP 之间的关系。
BERT 代表了 NLP 的一个突破时刻, Transformer 架构的使用和上下文嵌入的双向特性已经被许多研究人员采用,衍生出了诸如 RoBERTa 、 M-BERT 、 ALBERT 和 ERNIE 等模型。然而,这些架构不区分原因、结果和混杂因素。费德在阿尔。(2021a) 解释这些模型并不试图确定因果关系。因此,“一个特征可能是一个强大的预测器,即使它与期望的输出没有直接的因果关系”( Feder et al .,2021a )。为了更好地介绍这些以芝麻街角色命名的模特,我推荐这本由 Jay Alammar 撰写的视觉入门。
根据 Feder et al. (2021a) ,当前的 NLP 模型可以被描述为“相关性预测模型*”*,其中预测是由于相关性(可能是虚假相关性)而不是因果关系。因此,尽管取得了最先进的结果,这些相关性预测模型可能是不可信的,并可能导致分布外设置的错误(费德等人,2021a )。关于信任, Jacovi 等人(2021) 探索了人工智能模型的可信度——他们讨论了“有保证的”信任意味着什么,以及如何通过内在推理和外在行为来实现信任。他们提出了设计可信人工智能的方法,一个关键的发现是信任和可解释人工智能(XAI)之间关系的重要性——这是使用因果关系来提高人工智能可解释性的进一步理由。此外, McCoy et al. (2019) 展示了人工智能语言模型如何在错误的原因下往往是正确的,并且会由于依赖易错的语法试探法(虚假的“快捷方式”)而在非分布设置中出错。显而易见,缺乏因果关系会导致难以解释的模型,而这些模型的概括能力很差。
相关性预测模型还存在另外两个问题:“用户组之间不可接受的性能差异,以及他们的行为太难以理解而无法纳入高风险决策的问题”( Feder 等人,2021a )。例如,一项名为“男性也喜欢购物”的电子商务研究讨论了性别偏见放大,并展示了这些人工智能模型如何在不同用户群之间表现出不同的表现(赵等人,2017 )。此外,如上所述,像 BERT 这样的黑盒模型的一个常见问题是,它们缺乏用于高风险决策的透明度( Guidotti 等人,2018 )。在处理人的生命或生计受到威胁的场景时,依赖内部逻辑被隐藏的 AI 模型是不道德的。
解决相关预测模型的这四个已知缺点的解决方案是将因果观点应用于模型开发和验证。最近的研究表明,可以“利用观察值和标签之间的因果关系知识,将虚假相关性形式化,并减轻预测者对这些相关性的依赖”( Feder 等人,2021a )。换句话说,理解数据生成过程(DGP)的因果结构可以帮助识别潜在的混杂因素。例如, Veitch 等人(2021) 建议使用压力测试作为一种形式化要求的方式,即改变输入的不相关部分不应改变模型的预测(反事实不变性)。这项工作还表明,反事实不变性的意义和含义取决于数据的真正潜在因果结构。因此,不同的因果结构需要不同的正则化方案来诱导反事实不变性(维奇等人,2021) 。
因果关系的另一个优点是,它提供了一种语言来说明和推理公平条件。 Kilbertus 等人(2017) 探索了如何使用因果推理来避免歧视,他们通过用因果推理的语言来构建歧视问题来做到这一点。他们声称这是必要的,因为观察标准有内在的局限性,阻止他们决定性地解决公平问题。这意味着,与其关注什么是正确的公平标准,不如关注应该对因果 DGP 做出什么样的假设。
此外,当涉及到测试时,解释预测的任务可以通过用反事实的术语来表述预测来完成。Feder 等人(2021b) 在 BERT 上测试了这种方法,在最近的一篇论文中,他们概述了通过反事实语言模型进行因果模型解释的框架。他们的方法是微调像 BERT 这样的深度情境化嵌入模型,使用从预测问题的因果图中导出的辅助对抗性任务。他们表明,有可能选择辅助对抗性训练前任务,使伯特有效地学习一个感兴趣的概念的反事实表征。然后,这种反事实表示可以用来估计概念对模型性能的真正因果影响。本质上,“一个反事实的语言表示模型被创建,它不受测试概念的影响,这使得它有助于减轻训练数据中存在的偏见”(费德等人,2021b) 。
虽然最近的因果动机研究给了我们有希望的结果,但由于当前 NLP 模型带来的鲁棒性和可解释性挑战,“有必要开发新的标准来学习超越利用相关性的模型”(费德等人,2021a )。下一节通过一个实际例子强调了这些挑战,并更详细地讨论了伪相关性。
随后的章节讨论了关于稳健性的伪相关性问题,以及处理公平性和可解释性问题的因果方法。为了有助于下面几节中的解释,我们需要一个实际的例子来说明这些挑战。因此,我依赖一个取自之前提到的调查论文的例子( Feder 等人,2021a )。考虑这样一个场景,我们希望构建一个分类器来预测诊断/医疗状况,给定一个书面的临床叙述,并使用来自多家医院的带标签的训练集。下图显示了此示例,其中 Z 代表医院,Y 是医生指定的诊断标签,W 是书面的临床叙述,Y_hat 是来自分类器的预测。

分类问题,其中预测(Y_hat)取决于临床叙述(W),临床叙述本身受诊断标签(Y)和医院(Z)的影响,这两者恰好相关。来源:费德等人,2021a 。
分类器 Y_hat(W)将临床叙述(W)的文本作为输入,并输出诊断预测。然而,叙述是基于医生的诊断(Y ),也受医院 Z 使用的写作风格的影响。在这种情况下,根据费德等人的说法(2021a ),我们希望在保持标签(Y)不变的情况下对医院 Z 进行干预。反事实地,我们将医院设置为( z ),这给了我们反事实叙事 W( z ),而 Y 是固定的。给定反事实叙事 W( z )的输入,分类器 Y_hat(W)的输出是反事实预测 Y_hat(W( z ))。
因为训练数据具有来自多个医院的记录,所以可能存在这样的医院,在该医院中,患者更有可能被诊断为具有特定状况。也可能是由于特定医院的惯例,该医院的医生倾向于使用独特的文本特征。如果我们要使用 BERT 建立一个相关性预测模型,该预测器将使用携带医院信息(Z)的文本特征(X),这些特征对于预测任何特定医院内的诊断都是无用的( Feder 等人,2021a )。因此,挑战在于较差的分发外性能。由于写作风格和医疗诊断之间的虚假相关性,预测对于训练数据之外的医院将无效。
Feder 等人(2021a) 指出,出现伪相关性必须满足两个条件:首先,在训练数据中,需要有一个因子(Z)来提供关于特征(X)和标签(Y)的信息,其次,Y 和 Z 必须以某种方式依赖于训练数据,而这种方式一般不能保证成立。在这个医疗诊断分类器示例中,如下图所示,Z 与 y 相关。

医疗诊断分类器示例,其中诊断标签(Y)与医院(Z)相关。资料来源:费德等人,2021a 。
医院 Z 和标签 Y 之间的这种虚假关联存在于训练数据中;因此,预测器将学习 X 中提供医院 z 信息的部分。然而,该预测器并不稳健,因为特征 X 和标签 Y 之间的学习关系在部署期间将不会成立( Feder 等人,2021a) 。下面的章节描述了如何利用因果关系来解决这类问题。
当处理像 BERT 这样依赖相关性进行预测的人工智能语言模型时,确保预测不会因为错误的原因而变得正确是至关重要的。有两种类型的评估可用于这项任务:不变性测试和敏感性测试。不变性测试“评估预测是否受到与标签没有因果关系的扰动的影响” (Feder 等人,2021a) 。另一方面,敏感性测试“应用的扰动在某种意义上应该是转换真实标签所需的最小变化”( Feder 等人,2021a )。因果动机不变性测试的目的是观察当给定反事实输入时,预测者是否表现不同。如果 Z 是文本中应该与标签 Y 因果无关的原因的标签,那么反事实输入将是 X(Z = ~z )。根据 Veitch 等人(2021) 的说法,在某些情况下,一个其预测在这些反事实中不变的模型在 Y 和 z 之间具有不同关系的测试分布中表现更好。
敏感性测试类似于不变性测试,因为它们也是对反事实的评估;但不同的是,标签 Y 被改变了,但所有其他对 X 的因果影响保持不变( Kaushik et al .,2020 )。敏感性测试的反事实可以表示为 X(Y = ~y)。敏感性和不变性测试是测试稳健性的一种方式,并且有几种学习预测器的方法可以通过这种测试。这些方法是将数据因果结构的领域知识结合到学习目标中的一种方式,这意味着它们是因果驱动的方法。在这篇文章中,我介绍了费德等人(2021a) 描述的两种方法:反事实数据扩充和因果驱动的分布标准。
数据扩充是指构建反事实实例并将其纳入训练数据。这种方法对不变性和敏感性都有效。例如,利用不变性,可以向学习目标添加一项,该项惩罚反事实对的预测中的不一致(例如,X(Z = z 和 X(Z = ~z ))。如果我们测试敏感性,干预将在标签 Y 上,我们将用标签反事实 X(Y = ~y)来增加训练数据。这将“降低对噪声的敏感性,提高域外泛化能力”( Feder 等人,2021a )。
有几种方法可以生成反事实的例子,如手动后期编辑、关键字的启发式替换或自动文本重写( Feder et al .,2021a )。手动编辑的第一种选择通常是准确的,但是昂贵的。使用关键字的第二种选择只在某些场景下有效;例如,当反事实可以通过像代词这样的词的局部替换来获得时。然而,这种关键字方法不能“保证流畅性或覆盖所有感兴趣的标签和协变量,并且难以跨语言推广”( Feder 等人,2021a )。最后一个选项是完全生成的方法,其目标是“将手动编辑的准确性和覆盖范围与词法试探法的便利性结合起来”( Feder 等人,2021a )。
对于所有这三种选择,都存在引入虚假相关性的风险。例如,如果关键字词典不完整,关键字替换方法可能会引入新的虚假相关性( Feder 等人,2021a )。手动编辑和自动文本生成的其他两个选项具有相同的问题,即反事实的生成也会引入新的虚假关联。反事实的例子可能是“强有力的,因为它们直接解决了因果推断固有的缺失数据问题”(费德等人,2021a ),但是反事实的数据增强容易受到虚假相关性的影响。因此,另一种选择是“直接对观察数据进行操作的因果动机分布标准”(费德等人,2021a )。
根据定义,反事实不变预测器将满足可从 DGP 的因果结构中导出的独立性标准( Veitch 等人,2021) 。通过推导不变预测器的分布特性,我们可以确保这些特性被训练的模型所满足。对于我们的医疗示例,我们希望确保改变医院书写风格 Z 不会改变诊断 Y,因为这是一个虚假的相关性。根据 Feder et al. (2021a) 这种对 Z 的反事实不变性,给定一个预测因子 f 和文本特征 X,可以形式化为f(X(Z)=f(X(Z’))对于所有Z;。这里,Z 和 Y 都是 X 的原因,因此反事实不变预测器将给出独立于协变量 Z 的预测 f (X ),条件是真实标签 Y。也就是说,“标签不是文本的影响”( Feder 等人,2021a) 。
除了为不应该影响预测的文本原因设置明确的标签 Z(这是我们的医学示例的情况)之外,还有两种其他的方法来获得分布标准。第一种选择是将训练数据视为“源自一组有限的环境,其中每个环境在原因上具有唯一的分布,但是 X 和 Y 之间的因果关系在环境之间是不变的”( Feder 等人,2021a) 。环境不变性标准的目的是确保我们有一个表示,其中相同的预测值在每个环境中都是最佳的。这可以被认为是领域泛化。另一个选择是控制混杂;如果 Z 是一个混杂因素,根据费德等人(2021a) 的说法,一个与将观察概率转换为干预概率的“后门调整”相关的因子分解可用于控制 Z 的混杂特征(珀尔,2000 )。
与数据扩充相比,分布式标准需要“比监督学习问题通常可用的数据更丰富的训练数据”( Feder 等人,2021a )。因此,方法的选择应取决于是否更容易获得分布标准所需的观察数据,或者是否更容易创建反事实实例来扩充训练集( Feder 等人,2021a )。下一节讨论公平和偏见,这是可以通过数据扩充或分配标准解决的问题。
当谈到确保模型公平和无偏见时,“因果关系提供了一种语言,用于指定跨种族和性别等人口统计属性的期望公平条件”( Feder 等人,2021a )。例如,哈特等人(2016) 表明,需要进行因果分析,以确定观察到的数据分布和预测是否会引起公平性问题。他们讨论了反歧视法如何要求人工智能模型不会延续现有的不平等,这项工作通常专注于“受保护”的属性,如性别、种族和宗教等。“通过无意识的公平”的想法表明,人工智能模型只需要忽略这些受保护的属性;然而, Hardt 等人(2016) 声称这是无效的,因为冗余编码使得从其他特征预测受保护的属性成为可能。他们测试各种“不经意”的方法,发现它们无效;因此,他们得出结论,因果关系是必要的,以确定公平的关注。
如前一节所述, Kilbertus 等人(2017) 表明,DGP 的因果解释可以激发公平性度量。与 Hardt 等人(2016) 类似,他们提出这种方法是因为观察数据在正确解决公平性问题方面存在固有的局限性。 Kusner 等人(2017) 定义了“反事实公平”的概念,其中,如果对于每个个体,预测对于通过改变受保护属性而创建的个体的反事实版本保持相同,则预测器是公平的。他们声称这是必要的,因为历史数据可能包含偏见,人工智能模型必须考虑这一点,以避免歧视。
使用因果推理 Kusner 等人(2017) 开发了一个模型公平的框架,该框架捕捉了一个直觉,即一个决定对一个人是公平的,如果它在真实世界中与在反事实世界中是相同的,其中该个人属于不同的人口统计组。也就是说,改变一个受保护的属性,比如种族,在预测上应该具有反事实不变性。费德等人(2021a )解释说,将受保护的属性视为服从反事实推理或干预的变量的合法性存在问题。作为一个解决方案, Kilbertus et al. (2017) 提出可以用名字等可观察的代理来测试不变性。
根据 Feder 等人(2021a) 的说法,有几种方法可以使用反事实数据增强来减少偏差。例如,可以通过交换“身份术语”列表来构建反事实,目的是减少文本分类中的偏差 (Garg 等人,2019) 。另一项研究将代词等性别标记物用于指代消解,指代消解是指文本中的同一实体(赵等,2018) 。反事实数据扩充也已经被用于减少像 BERT 这样的模型的预训练中的偏差;然而,这种预训练模型中的偏差会在多大程度上传播到下游应用,目前尚不清楚( Feder 等人,(2021a )。迄今为止,分配标准还没有经常被用来提高公平性。由 Feder 等人(2021a) 提到的一项值得注意的研究使用不变风险最小化来减少对毒性检测数据集的虚假种族相关性的使用 (Adragna 等人,2020) 。
回到我们的医疗诊断示例,假设训练数据包含患者和医生的受保护属性,也许这些方法中的一些可能有助于提高公平性。然而,一个值得关注的问题应该是书面叙述中潜在的偏见;例如,批判种族理论认为用来描述有色人种的语言可以不同于用来描述白人的语言。从历史上看,人口统计受贫困等社会问题的影响,这意味着某些医院服务区可能有更高比例的 BIPOC 患者,同时获得的资金和财政捐赠也较少。此外,还有大量关于有色人种和女性面临的医疗保健不平等的研究。例如,引用美国律师协会的话,“黑人根本得不到与白人同等质量的医疗保健,这种二流的医疗保健缩短了他们的寿命。”。
所有这些因素都可能对用于构建诊断预测因子的书面临床叙述产生影响,并且偏倚可能被硬编码到医生诊断标签本身中。因果关系可以帮助确定预测是否公平和公正,例如,因果数据扩充可以测试预测的不变性。然而,同样重要的是,这种决策的透明度,这需要可解释和可说明的人工智能模型。下一节将讨论这个问题。
虽然像 BERT 这样的 NLP 模型目前是“难以理解的黑匣子,难以解释,但有必要诊断错误并与决策者建立信任”( Feder 等人,(2021a )。目前,费德在艾尔。(2021a) 陈述了一种常见的方法是利用网络工件,例如注意力权重,来生成解释。理由是注意力权重是在生成预测的路径上计算的(王等,2016 )。另一种方法是试图通过使用测试实例或其隐藏表示的扰动来估计更简单和更可解释的模型( Kim 等人,2017 )。这两种基于注意力和干扰的方法都有重要的局限性,会使它们产生误导。例如,“基于注意力的解释通常只可能用于单个标记,因此不能用抽象的语言概念来解释预测”( Feder 等人,(2021a )。此外,基于扰动的方法“不允许估计句子级估计的影响,并且经常会产生不可信的反事实”( Feder 等人,(2021a )。
鉴于现有可解释性度量的问题,有应用因果观点的空间。根据费德在阿尔。(2021a) ,一种方法是使用数据扩充生成反事实示例,然后将每个示例的预测与来自生成的反事实的预测进行比较。反事实数据增强使得计算观察到的文本和如果文本中不存在特定概念时的文本之间的差异成为可能。在这种情况下,生成反事实文本似乎是合理的,这就有可能估计基于文本的模型的因果效应( Feder 等人,(2021a )。
例如, Ross 等人(2021) ,为了促进数据扩充,创建了一个任务不可知的生成系统,它以语义受控的方式干扰文本。这个系统被他们命名为 Tailor,它使用了不相似性训练和一个生成器,该生成器遵循一系列来自语义角色的控制代码,这些控制代码的修改允许细粒度的扰动。使用 Tailor 生成反事实,减少了通常与特定任务扰动生成器相关的开销,并具有提高模型可解释性的优势。 Gardner et al. (2020) 试图通过使用“对比集”来评估模型的决策边界,从而提高模型的可解释性。他们建议,在构建数据集后,作者应该以小而有意义的方式手动扰动测试实例,以创建对比集。这些对比集应该“提供模型决策边界的局部视图,然后可以用来更准确地评估模型的真实语言能力”( Gardner 等人,2020) 。
尽管在生成自然语言反事实方面取得了进展,但对于风格、主题或情感等抽象语言概念来说,通常不可能“自动生成有意义的反事实,手动生成成本太高”( Feder 等人,(2021a )。鉴于这一问题,提出了一种替代方法,其中文本本身被搁置,而文本的表示被操纵。这种方法的一个例子是前面提到的 Feder 等人(2021b) 关于反事实语言表征模型的工作。他们通过预先训练分类器使用的语言表示模型(BERT)的附加实例来计算反事实表示,并使用对抗性辅助任务来控制混淆概念。另一个例子是 Ravfogel 等人(2020) 的工作,其中提出了一种使用迭代零空间投影从模型中移除信息的方法,以保留受保护的属性。
到目前为止,所讨论的可解释性方法使用反事实来确定不变性,一种“补充方法是生成具有最小变化的反事实,以获得不同的模型预测”( Feder 等人,(2021a )。这种敏感性方法提高了可解释性,因为它强调了改变模型预测所需的变化。例如,莫西拉尔等人(2020) 开发了一个框架,用于生成和评估一组不同的反事实解释,作为提高模型可解释性的一种方式。本质上,将不变性和敏感性测试合并到模型开发过程中,是一种使用因果关系来提高可解释性和可解释性的方法。
因果关系可以通过增加鲁棒性、公平性和可解释性来改进 NLP 模型。做出这些改进应该有助于解决当前最先进的人工智能语言模型(如 BERT)中存在的一些问题。我相信因果推理在伦理人工智能的发展中有一定的作用。事实上,我最近描述了神经科学和批判理论如何能够启发基于突触可塑性的道德人工智能系统,这些系统使用因果推理。最近的研究加强了这一观点,即因果关系可以提供一种识别和减轻偏见的方法。然而,最终还是要靠公司和从业者来利用我在本文中描述的一些因果方法。我希望这些因果驱动的技术能够成为标准实践。
目前,我主要关注的是使用 NLP 进行因果推理的潜力。然而,在没有首先解决鲁棒性、公平性和可解释性问题的情况下,追求将 NLP 模型用于因果社会科学研究是不负责任的。完成我的尽职调查后,我打算回到我的第一篇文章中关于因果推理和 NLP 的观点。由于这一领域的研究刚刚开始获得牵引力,因此缺乏实用教程来帮助数据科学家将这些想法融入他们自己的工作中。因此,我的下一篇文章将是如何使用 NLP 估计因果关系的实际 Python 代码演练。
我欢迎反馈和问题,请随时通过 Linkedin 与我联系。
原文:https://towardsdatascience.com/improving-the-deutsch-and-jozsa-quantum-algorithm-760b2dab12b3?source=collection_archive---------30-----------------------
量子机器学习要不要入门?看看 动手用 Python 学习量子机器。
在之前的一篇文章中,我们了解了由 David Deutsch 和 Richard Jozsa 开发的算法。第一批证明量子算法如何比经典算法快一倍的算法之一。

作者图片
该算法在一次运行中评估我们提供的输入是恒定的还是平衡的。作为一个例子,我们看了一枚硬币。要说一枚硬币是否公平,还得多次折腾评估。另一方面,Deutsch-Jozsa 量子算法只计算一次。
然而,该算法有一些缺点。
首先,我们用完全不同的神谕来表示公平和欺骗的硬币。所以,在创造量子电路时,我们必须决定硬币是公平的还是被欺骗的。
第二,我们没有一个量子位来告诉我们硬币是否公平。相反,我们必须测量三个量子位。
第三,当神谕将量子位置于代表其输入值的状态时,该输入值并不用于决定硬币是公平的还是被欺骗的。我们甚至可以跳过将量子位放入正确状态的步骤。只要我们选择正确的先知,这个算法仍然可以工作。
总而言之,在运行量子电路之前,我们需要决定甲骨文(常数/狡猾或平衡/公平),量子电路应该会告诉我们我们拥有哪种硬币。
所以,让我们解决这些缺点。
我们从设置开始。在这里,我们从 Qiskit 导入所有需要的库。
接下来,我们创建我们的广义甲骨文。它将两个量子寄存器和配置(config)作为参数。量子寄存器包含量子位,允许我们使用它们。config包含一个由0和1组成的字符串。每个数字代表我们要分类的函数的一个输出——或者如果你愿意,也可以是投掷硬币的结果。
如果字符串包含三个0或三个1,则为常量函数(或骗币)。如果字符串至少包含一个0和一个1,则为平衡函数(或公平币)。
在oracle功能中,我们评估config字符串(第 6 行)的每个位置。注意这一点很重要,因为它解决了我们提到的第一个缺点。在量子电路的准备过程中,我们不会在任何时候使用整个配置字符串。相反,我们让量子算法来评估整个字符串是恒定的还是平衡的。
对于config串中的每个位置(即1),我们应用一个受控非门,其中代表掷的量子位作为控制量子位,辅助量子位作为目标量子位。
显而易见的问题是:“我们为什么要这样做?”
在我们回答这个问题之前,我们应该看看整个量子电路。但是,在应用 oracle 之前,只需关注量子位的初始化(第 8-13 行)。
下图描述了两个量子位系统(仅一个 toss)的初始化和 oracle。

作者形象
"那么,这样的电路是做什么的?"首先,我们把上面的量子比特放入|+⟩州,把下面的量子比特(辅助)放入|−⟩.州在这些状态下,我们以 50%的几率将每个量子位测量为0或1。唯一的区别是量子位相位。
当我们应用受控非门时,这种差异很重要。它翻转了|01⟩和|11⟩态的振幅(顶部的量子位在右边)。但是这两种状态的振幅绝对值相等。因此,该门不会影响最终的测量概率。
下图描述了测量概率的变化。

作者形象
我们看到顶部量子位从“关闭”位置开始(意味着 0%的几率被测量为1)。底部的量子位也是如此。但是𝑋门将其翻转到“开”(100%的几率被测量为1)。阿达玛门将两个量子位等同叠加,有 50%的几率被测量为1。受控非门不会改变这些概率。
那么,我们为什么要使用这个门呢?与最初的 Deutsch-Jozsa 算法一样,答案是相位反冲。底部的量子位在|−⟩.州虽然它具有与状态|+⟩相同的绝对振幅,从而具有相同的测量概率,但它的相位是偏移的。看看布洛赫球就知道了。

作者形象
顺便说一下,布洛赫球生动地说明了为什么 x 门没有改变|+⟩和|−⟩.X 门通过 X 轴镜像量子位状态向量。但这两种状态都存在于这个轴上。因此,在 X 轴上反射不会改变任何东西。
但是受控非门会影响控制量子位的相位。下图显示了应用受控非门后量子位的状态。

作者形象
由于甲骨文对config串中的每个1应用了受控非,它有效地翻转了这些量子位的相位,并将它们置于|1⟩.状态但是当我们在config串中有一个0时,神谕什么也不做,把这些量子位留在|+⟩.状态
现在,让我们继续电路的其余部分。下图描述了这部分电路。

作者图片
首先,一连串的哈达玛门将量子位元带回基本状态。如果量子位在|+⟩状态,哈达玛门会把它变成|0⟩.如果我们没有在甲骨文中的这个量子位上应用受控非门,情况就是这样。它代表config字符串中的一个0。如果量子位在|−⟩状态,哈达玛门会把它变成|1⟩.当我们应用受控非门时,情况就是这样,它在我们的字符串中代表一个1。
辅助量子位不变。所以,它仍然在|−⟩,哈达玛门把它变成了|1⟩.下面的非门将这个量子位翻转回|0⟩.状态
接下来,我们应用 MCX 门。这是一个多控非门。它需要任意数量的控制量子位和单个目标量子位。它仅对处于|1⟩.状态的所有控制量子位在目标量子位上应用非门这意味着,如果我们的config字符串意味着一个总是返回1的常数函数,我们就将辅助量子位翻转到|1⟩。
接下来,我们用非门翻转三个输入量子位,然后用另一个 MCX 门翻转。现在,如果我们的config字符串暗示一个总是返回0的常数函数,我们将辅助量子位翻转到|1⟩。
仅此而已。如果没有一个 MCX 门将我们的辅助量子位翻转到|1⟩态,我们的输入一定是一个平衡函数。
因此,我们修正了原始 Deutsch-Jozsa 算法的第二个缺点。也就是说,我们看不到单一量子位元的结果。在我们改进的电路中,我们只需要测量辅助量子位。
让我们看一看。下图显示了常量000配置的输出。我们也包括所有量子位元的测量,以说明输入量子位元也有正确的值。

作者图片
我们看到只有辅助量子位导致1。但是所有的输入量子位都是0,因此是不变的。接下来,我们看看常数1函数。

作者图片
我们看到所有量子位的结果都是1,包括辅助量子位。接下来,我们来看一个平衡函数。

作者图片
在这种情况下,一个平衡函数,我们测量辅助量子位为0。下图显示了完整的电路。

作者图片
Deutsch-Jozsa 算法是最早和最著名的量子算法之一。它生动地说明了我们如何使用量子相位反冲来创建一个比经典算法快得多的量子算法。
在这篇文章中,我们解决了原始算法的三个缺点。首先,我们对所有可能的输入使用相同的 oracle 函数。第二,我们可以使用单个(辅助量子位)来衡量输入函数是恒定的还是平衡的。第三,我们用来将输入标记为0或1的量子门对于它们的输出值以及我们是否将整体函数分类为常数或平衡是决定性的。
Deutsch-Jozsa 算法的这种扩展并不打算以任何方式降低原始性能。Deutsch 和 Jozsa 在 1992 年开发了他们的算法。他们没有学习量子计算的教科书。他们没有模拟器,也没有真正的设备。最重要的是,他们第一时间想出了相位反冲!这个扩展简单地使用它并简化算法的其余部分。
我们本可以进一步简化整个算法。我们可以不用一连串的哈达玛门和受控非门,只需用 x 门将输入量子位放入|1⟩状态。然而,我们不会使用相位反冲。这是算法教给我们的一课。
量子机器学习要不要入门?看看 动手用 Python 学习量子机器 。

在这里免费获得前三章。
原文:https://towardsdatascience.com/improving-the-inference-speed-of-tabnet-971397b74f63?source=collection_archive---------41-----------------------
TabNet[1]是一个基于深度神经网络(dnn)的表格数据集模型。TabNet 的作者声称,dnn 对于图像数据、序列数据(例如,文本)是成功的,但是当涉及到表格数据时,它的性能比 LGBM 或 XGBM 等梯度增强模型差。作者试图用 dnn 解决这个问题。他们可以为表格数据集开发一种新的基于 dnn 的模型,并表明 TabNet 的性能明显优于梯度推进模型。此外,TabNet 在一项 Kaggle 竞赛中提供了其卓越的性能——动作预测机制[2]。
尽管 TabNet 的性能很好,但它有一个弱点——推理速度慢。我先解释一下为什么推理速度慢(如果不感兴趣可以跳到下一段下一段)。由于其特征选择步骤,TabNet 很慢。TabNet 使用“sparsemax”[3]选择其特征,对于向量,sparse max 定义为:
sparsemax_i(z) = [z_i - au(z)]_+
Sparsemax 输出稀疏概率,这在进行特征选择时很有用。例如,假设我们有 3 个特征(A、B 和 C),只有 A 与预测相关。在这种情况下,sparsemax 在 A 上分配概率 1,在 B 和 C 上分配概率 0,以便只处理合适的特征。我们在 softmax 上没有看到这种趋势。
现在,我们来谈谈为什么 sparsemax 会让 TabNet 变慢。慢度来自上面函数中的值。为了获得,它涉及排序和搜索时间复杂度分别为 O(n*log(n))和 O(n)的最大值,其中是向量的长度。
那么,我们该如何解决这个问题呢?我建议使用 softmax 而不是 sparsemax,但要用 multiplier 。
softmax_i(z,m) = exp(m*z_i)/(exp(m*z_1)+...+exp(m*z_n))



Sparsemax V softmax:从左到右,乘数分别设置为 3、1 和 5。z 的长度是 2。
上图显示了 sparsemax 和 softmax 之间的比较。正如 sparsemax[3]的作者所声称的,sparsemax 在创建稀疏概率方面可以比乘数为 1 的 softmax 执行得更好。然而,当我们增加乘数幅度时,softmax 相当好地逼近 sparsemax,尤其是当乘数等于 3 时。此外,计算 softmax 比计算 sparsemax 要快得多。因此,我们可以期待快速的推理速度!
**性能对比:**我将这个修改版的 TabNet 应用于 Kaggle 竞赛,简街市场预测[4]。在那场比赛中,一个人需要在 16 毫秒内决定是否接受金融交易机会,给出的数据是表格。虽然这是一个金融交易决策问题,但我不认为它可以用时间序列来表述,因为大部分数据都是匿名的:匿名的特征、匿名的证券类型、匿名的市场等等。不管怎样,我能得到 7990.420 分,而其他人只能得到 5000 分。在比赛中,大多数人因为 TabNet 的推理时间慢而停止使用它,而我利用了 softmax 的快速推理。
综上所述,我建议使用带乘法器的 softmax 来提高 TabNet 的推理速度。虽然我没有将它应用于许多其他数据集,但它似乎不会因为使用 softmax 和 multiplier 而降低性能;它有时会提高性能。
请务必让我知道,如果你发现任何错误或如何在媒体中键入数学方程式。感谢您阅读我的文章。
参考
[1] Arik,S.O .和 Pfister,t .,2019 年。Tabnet:专注的可解释表格学习。 arXiv 预印本 arXiv:1908.07442 。
[2]卡格尔。2020.作用机制(MoA)预测。https://www.kaggle.com/c/lish-moa
[3] Martins,a .和 Astudillo,r .,2016 年 6 月。从 softmax 到 sparsemax:注意力和多标签分类的稀疏模型。在机器学习国际会议(第 1614–1623 页)。PMLR。
[4]卡格尔。2021.简街市场预测。https://www.kaggle.com/c/jane-street-market-prediction
原文:https://towardsdatascience.com/improving-the-world-for-birds-3ab0ecbdbe21?source=collection_archive---------28-----------------------

怀塔克里山脉的惠亚湾。得到凯莉·贝内特许可的照片
想象一下,就一会儿,你可以通过在你的社区做一些小事来改变现状。这就是成千上万的新西兰人在全国各地所做的,他们一起努力捕捉入侵的食肉动物。新西兰是一个鸟类之国,但负鼠、老鼠、刺猬和鼬(雪貂、黄鼠狼和白鼬)已经大量减少了鸟类的数量,并且在许多地区还破坏了原始森林。新西兰人正在夺回国家,改善鸟类的世界,一次一个捕食者。只需要一点时间,正确的诱捕方法和足够多的邻居。
新西兰“干净、绿色”的形象有点像神话。今天的国家是一个高度改良的环境,其中最深刻的改变是哺乳动物的引入。政府保护部门记录了澳大利亚的刷尾负鼠,不仅严重破坏了标志性的本土树木,如 Pohutukawa ,还吃掉鸟蛋甚至雏鸟。在这段视频中,kea 保护基金会记录了负鼠对活着的 Kea 雏鸟的捕食。
刺猬通常被认为是可爱的,虽然它们在英国受到保护,但在新西兰,刺猬攻击南岛辫状河上的涉禽巢穴,并吃掉大量的本地无脊椎动物。当亚南极山毛榉森林或罗汉松森林有所谓的“肥大年”时,船鼠、挪威鼠和老鼠繁殖迅速,它们的数量激增。这些啮齿动物是贪婪的掠食者,不仅捕食鸟蛋,还捕食当地特有的昆虫,如湿地鼠和本地石龙子。视频中甚至有老鼠啃食活信天翁幼鸟的记录。
白鼬和其他鼬类动物,如雪貂和黄鼠狼,是最凶残的外来食肉动物。它们不寻常的生殖系统允许它们优化利用现有的食物,它们在肥大的年份享用老鼠爆发的食物。众所周知,它们很难被捕获。
在新西兰捕捉捕食者有一个不寻常的方面,那就是将努力转化为一个公民科学项目。许多人统计他们的诱捕捕获量,并将它们输入数据库。有几个应用程序可用于记录和总结捕获量。在某些情况下,这些应用程序还提供有毒杂草和鸟类的数量。
这些数据本身对于追踪减少或甚至从景观中消除入侵捕食者的进展非常有用。在某些情况下,消灭是可能的,新西兰已经在世界上率先消灭了隐藏着珍贵本土动植物的近海岛屿上的食肉动物。捕食者自由 2050 是一个政府资助的项目,有着更广泛的抱负仍然有争议。无论最终是否有可能从新西兰消灭捕食者,无可争议的是,邻里诱捕具有减少当地捕食者数量和改善鸟类生活环境的潜力。
动物的行为很复杂,通过使用野生动物摄像机观察陷阱周围的行为,可以提高邻里诱捕的成功率。在 Waitakere 山脉的 Huia 进行诱捕时拍摄的镜头很有启发性。
在这个视频中,老鼠研究了一个自动化的 Goodnature A24 陷阱。然后,一只负鼠试图袭击陷阱,触发装置,产生一个错误的捕杀记录。第二天晚上,一只老鼠被 A24 捕鼠器杀死,第二天早上,尸体被一只猫吃掉了,没有尸体。如果没有录像,就不可能知道一个是假杀,一个是真杀。
下一个视频解决了一个谜,一个捕鼠笼陷阱被触发,诱饵被移走,但陷阱里什么也没有。为了确定谁该负责,我安装了一个摄像头。答案是老鼠触发陷阱并吃掉诱饵,而老鼠和一只乌鸫也从陷阱内外同时吃掉诱饵。这个问题是通过在鼠笼附近设置捕鼠夹来解决的。
另一个问题是诱饵是从捕鼠夹里偷出来的。这段视频展示了一只大的雄性老鼠在一只负鼠到来之前从捕鼠夹里偷取野生饲料诱饵。解决办法是在负鼠陷阱附近放置一些捕鼠器,以杀死诱饵袭击者。
在下一个视频例子中,诱饵从 Goodnature A24 自动捕鼠器中消失,一台摄像机让我确定罪犯是老鼠。一只刺猬的死亡证明了捕鼠器已经装好并且正在工作,这只刺猬尝试了和老鼠一样的把戏。
最后,运动感应相机可能会捕捉到尚未被捕捉到的重要掠食者。在这个例子中,相机捕捉到一只白鼬。这是非常重要的信息,因为白鼬很怕陷阱,通常只对用整只蛋做诱饵的陷阱感兴趣。它们是新西兰最具破坏性的哺乳动物掠食者。


摄像机截图显示一只尚未被陷阱捕获的白鼬。照片由惠亚陷阱组的戴夫·明蒂许可。
视频显示白鼬拿一个鸡蛋。视频许可戴夫明蒂,惠亚陷阱组。
经过三年的邻近诱捕,我们现在在 Huia 有超过 1000 次捕获,所以我们可以对数据提出一些探索性的问题。首先,我们在抓什么?答案主要是大鼠和小鼠(图 1B 和 A),刺猬(图 1C)和负鼠(图 1D)少得多。老鼠是最丰富的捕获物(图 1B)。第二个问题是捕获量是否有季节性?有了四年的数据,捕鼠的季节模式很明显(图 1A),但老鼠、刺猬或负鼠没有明显的季节模式。老鼠在冬天更容易被抓到(图 1A)。也有一些迹象表明,更多的老鼠是在秋季或冬季被捕获的,尽管与老鼠相比,这种模式在不同年份之间变化更大(图 1B)。

图 1:在过去的 4 年中,新西兰怀塔克里山脉的休伊阿地区的邻里捕获组捕获的捕食者。所有这些食肉动物都是已知会破坏鸟类生活的哺乳动物。这项诱捕行动是对新西兰政府保护部 2050 年无捕食者行动的自愿贡献。
使用 tidyverse 系统用 R 编写了这个初步分析的代码。的完整剧本可以在这里下载。代码的解释如下。
加载必要的库:
*# plot_catches
library*(tidyverse)
*library*(tibbletime)
*library*(lubridate)
*library*(gridExtra)
*library*(ggthemes)
*library*(ggpubr)
原始数据是从我们的 Huia 项目中导出的。新西兰网站。这段代码使用的数据可以从这里下载。
*# load raw data* series <- *read_csv*("./data/export.csv")
陷阱存储的数据。由于储存的方式,新西兰需要一些操作。日期以字符形式存储,渔获量作为一个称为“捕获的物种”的单一变量存储,而不是作为每个物种的单独变量存储,这很不方便。

以下代码将导出的数据转换为结构合理的时间表,每个物种有单独的变量:
*# keep the required variables* series2 <- *select*(series, Date, "Trap type", "Trap", "Recorded by", "Species caught")
*# create new variable with Date in the correct format,
# and new variables for catches by species* series2 <- *mutate* (series2,
date = *dmy_hm*(Date, tz = "Pacific/Auckland"),
rats = *ifelse* (series2$"Species caught" %in% *c* ("Rat", "Rat - Ship", "Rat - Norway"), 1, NA),
mice = *ifelse* (series2$"Species caught" == "Mouse", 1,NA),
possums = *ifelse* (series2$"Species caught" == "Possum", 1,NA),
hedgehogs = *ifelse* (series2$"Species caught" == "Hedgehog", 1,NA),
scavenged = *ifelse* (series2$"Species caught" == "Unspecified", 1, NA)
)
*# convert tibble to tibble time
# summarize catches by time interval* series2 <- *as_tbl_time*(series2, index = date)
series2 <- *arrange*(series2, date)
*# keep the required variables* series2 <- *select*(series2, date, rats, mice, possums, hedgehogs, scavenged)
现在,时间变量可用于方便地将数据分组为四分之一年的季节:
grouped_data <- *collapse_by*(series2, "quarterly") %>%
dplyr::*group_by*(date) %>%
dplyr::*summarise_all*(sum, na.rm=TRUE)
接下来,绘制数据:
*# Plots* mp <- *ggplot*(grouped_data, *aes*(x = date, y = mice)) +
*geom_bar*(stat = "identity") +
*geom_bar*(stat="identity", fill="darkblue") +
*annotate*("text", x = grouped_data$date[1:16], y = -12,
label = *rep*(*c*("Spring","Summer","Autumn","Winter"),4),
size=3.5, angle = 70) +
*ylim*(-20,80) +
*theme_hc*(base_size = 18)
rp <- *ggplot*(grouped_data, *aes*(x = date, y = rats)) +
*geom_bar*(stat = "identity") +
*geom_bar*(stat="identity", fill="dark gray") +
*annotate*("text", x = grouped_data$date[1:16], y = -12,
label = *rep*(*c*("Spring","Summer","Autumn","Winter"),4),
size=3.5, angle = 70) +
*ylim*(-20,80) +
*theme_hc*(base_size = 18)
hp <- *ggplot*(grouped_data, *aes*(x = date, y = hedgehogs)) +
*geom_bar*(stat = "identity") +
*geom_bar*(stat="identity", fill="dark gray") +
*annotate*("text", x = grouped_data$date[1:16], y = -12,
label = *rep*(*c*("Spring","Summer","Autumn","Winter"),4),
size=3.5, angle = 70) +
*ylim*(-20,80) +
*theme_hc*(base_size = 18)
pp<- *ggplot*(grouped_data, *aes*(x = date, y = possums)) +
*geom_bar*(stat = "identity") +
*geom_bar*(stat="identity", fill="dark gray") +
*annotate*("text", x = grouped_data$date[1:16], y = -12,
label = *rep*(*c*("Spring","Summer","Autumn","Winter"),4),
size=3.5, angle = 70) +
*ylim*(-20,80) +
*theme_hc*(base_size = 18)
sc<- *ggplot*(grouped_data, *aes*(x = date, y = scavenged)) +
*geom_bar*(stat = "identity") +
*geom_bar*(stat="identity", fill="dark gray") +
*annotate*("text", x = grouped_data$date[1:16], y = -12,
label = *rep*(*c*("Spring","Summer","Autumn","Winter"),4),
size=2.5, angle = 70) +
*ylim*(-20,80)
*# layout plots on the page
ggarrange*(mp, rp, hp, pp,
labels = *c*("A", "B", "C", "D"),
ncol = 2, nrow = 2)
本文描述了一个简单的探索性数据分析。从这些数据中可以得出更多信息。我们将在后续的文章中进一步探讨这个问题。
最后,我要强调的是,将相机与陷阱配对,为揭示动物行为增加了一个额外的维度。对陷阱周围动物行为的了解使得诱捕策略得以改进,使其更加有效。相机也可能揭示惊喜,如隐形,难以捕捉的捕食者。有了这些知识,就可以有针对性地把注意力放在那些害怕陷阱的动物身上。
数据的收集和管理对于保护工作非常重要。没有渔获量的数据,就不可能制定指标来衡量诱捕努力的成功。通过将诱捕数据与对鸟类鸣声的环境监测配对,有可能确定邻里诱捕努力是否达到了改善鸟类世界的预期效果,一次一只捕食者。
原文:https://towardsdatascience.com/improving-transformation-invariance-in-contrastive-representation-learning-63f881ea1ac2?source=collection_archive---------31-----------------------
在这篇博文中,我想快速回顾一下我最近与 Adam Foster 和 Tom Rainforth 合作的关于提高对比表征学习中的转换不变性的工作,我们试图回答“转换不变性在对比学习中的作用是什么?”我们的方法是加强更强的不变性,并表明这导致下游任务的性能提高。
我们的方法是加强更强的不变性,并表明这导致下游任务的性能提高。
对比学习是一种自我监督的方法,通过利用来自未标记数据的信号来学习有用的数据表示。为了评估这种方法,我们重新引入标签,并在冻结的表示上安装线性分类器。最近的对比学习方法已经在这个评估任务上实现了最先进的性能,并且几乎消除了与监督学习的差距,例如 SimCLR 、 MoCo 。这些方法的主要思想是学习对诸如随机裁剪或旋转之类的有害变换不变的表示。我们问了以下问题

SimCLR 体系结构学习用不同的变换来匹配相同输入的低维投影。—作者图片
这些方法的主要思想是学习对诸如随机裁剪或旋转之类的有害变换不变的表示。
我们提出了 2 个想法 1)一个新的梯度正则化 2)特征平均来鼓励更强的不变性。我们另外提出了一个新的数据集 Spirograph 来探索我们在完全可微分的生成过程中的想法。
变换不变性是什么样子的?这里我们看到了相同输入的不同转换,我们希望它们的表示接近。

作者图片
我们感兴趣的许多变换(例如颜色失真)是可微的,并且由连续参数α控制。如果我们用一个变换参数α,z_α来调用一个输入的表示,我们可以通过条件方差正式地测量该表示如何随着变换的变化而变化

我们想把这一项减到最小,这样当我们应用不同的变换时,表示就不会有太大的变化。
由于变换是可微的,我们可以取表示相对于α 的梯度。鼓励这个梯度变小将使表示随着变换的改变而缓慢改变。在本文中,我们在这个梯度和条件方差的近似值之间建立了联系。我们的最后一个正则项是不同输入的近似条件方差,以促进输入之间的不变性。

梯度相对于α的条件方差的近似值。

梯度正则化-利用相对于变换参数的梯度来近似条件方差。—作者图片
由于变换是可微的,我们可以取表示关于α的梯度。鼓励这个梯度变小将使表示随着变换的改变而缓慢改变。
对于测试时间,标准协议是使用来自未转换输入的表示。我们建议,在测试期间利用转换,对来自同一输入的不同转换的多个表示进行平均。该方法产生了一个强不变的表示
,包括不可微的变换(例如随机水平翻转),因此在第一个想法中不能被正则化。

要素平均-相同输入但使用不同变换的平均表示。—作者图片
我们建议,在测试期间利用转换,对来自同一输入的不同转换的多个表示进行平均。

肺活量描记器的产生。
受 spirograph 模式的启发,我们提出了一个新的数据集,该数据集允许从有害转换因子 (6 个因子)中分离出感兴趣的生成因子 (4 个因子),并由完全可微分的生成过程形成。
下游任务是使用冻结表示上的线性回归来恢复生成因子。下图显示了具有相同生成因子但具有不同变换因子的肺活量图。

肺活量图数据集的样本。两组四幅图像(左和右):每组
显示应用于感兴趣的相同生成因子的不同变换。—作者图片
这里讨厌的变换超出了改变纹理的传统变换,即相同输入的颜色。螺旋图变换也使用相同的生成因子改变图像的结构,如下所示。这允许我们在不变性确实存在的地方探索变换不变性的贡献。

仅改变变换参数 h 对相同生成因子的影响。—作者图片
我们将展示我们在 CIFAR-10、CIFAR-100 和 spirograph 数据集上的实验结果。让我们回到我们的问题
是的,有可能学到更强的不变性。如图所示,我们的梯度正则化有效地降低了 CIFAR-10 上的条件方差。

CIFAR-10 的条件方差
是的,梯度正则化和特征平均都能带来更好的下游性能。

不同数据集上半监督任务的梯度正则化性能

不同数据集的特征平均性能,其中虚线表示来自未转换输入的表示性能。
当结合我们的两种方法时,我们在 CIFAR-10、CIFAR-100 数据集上实现了一流的性能。

除了改进下游性能之外,我们发现我们的正则化器还导致对变换参数α的移动更鲁棒的表示,例如,移动α分布的均值,增加α分布的方差。

您可以在这里找到可复制的代码

原文:https://towardsdatascience.com/improving-ui-layout-understanding-with-hierarchical-positional-encodings-b19e1e9235e?source=collection_archive---------14-----------------------
【杰森·李】**【康纳约翰逊】 ,以及 瓦伦奈尔

用户界面包含丰富的元素层次结构,可以用基于树的表示进行编码。(图片由作者提供)
布局理解是人工智能的一个子领域,它使机器能够更好地处理布局中的语义和信息,如用户界面(ui)、文本文档、表格、演示幻灯片、图形设计组合等。许多公司已经在他们的网络/移动应用的用户界面和用户体验上投入了大量的资源(UX),据《快速公司》报道,在 UX 上投入的每 1 美元可以获得 2-100 倍的投资回报。因此,人工智能和深度学习工具有很大的潜力来帮助和加速迭代设计过程。
在这篇博客文章中,我们将分享我们的发现和教训,利用基于深度学习的布局理解模型来完成以用户界面为中心的任务。这项研究是与位于 Uizard 的机器智能团队合作进行的。
具体来说,我们重点研究如何改变变压器模型的位置编码( Vaswani,2017 ),以编码更好的布局表示。我们强调:
这是一个相对未探索的领域,仍有许多工作要做——我们希望使用这篇短文来指导未来对布局理解的研究,并释放深度学习在设计领域的更多潜力。
我们将首先完成一些与 UI 布局理解相关的任务,然后分享一些我们认为有用的论文背景,最后更详细地讨论上面列出的要点。
当试图在 transformer 模型中构建良好的 UI 布局表示时,考虑数据集和许多类型的任务来帮助构建这些表示是很重要的。我们使用 RICO 数据集,这是一个 9.3K Android 应用的数据集,具有超过 66k 个独特 UI 屏幕和 3M+ UI 元素的视觉、文本、结构和交互设计属性。我们还提出了构建布局表示的四个任务,到目前为止已经独立研究过这些任务:

对后续任务有用的布局理解预培训任务示例(左上和右上由 Javier Fuentes Alonso ,左下由 Gupta 等人,2020 ,右下由李等人,2020 )。
令牌分类
在令牌分类中,除了一个目标元素之外,所有 UI 元素的类名都会呈现给我们,并要求我们预测该目标元素的类。我们还知道所有元素的边界框位置,包括目标元素。
语义分组
在此任务中,我们将看到 UI 元素的类名和边界框位置,并被要求预测输入序列中元素集的分组。例如,给定一个图像、段落和图标,我们可能希望将这个组归类为卡片元素。
布局生成
布局生成遵循我们在 LayoutTransformer ( Gupta 等人,2020 )中研究的工作,通过生成与训练数据集中的示例相似的布局。我们大概可以使用 RICO 数据集训练一个布局生成模型来生成真实的 UI 元素。
层次树生成
该任务遵循李等人在2020中研究的工作,其中基于变换器的树解码器模型用于接受 UI 元素作为输入,并输出元素的层次结构。这个任务可以被认为是语义分组的一个更复杂的版本,其中可以找到其他分组的组并将其分配给一个树。
—
虽然我们在工作中没有尝试所有这些任务,但我们希望这能给出对理解布局有用的任务类型的一般概念。采用多任务学习框架,类似于文本到文本转换转换器 (T5)模型,这些任务甚至可以通过一个模型来组合和解决。我们下面的发现将讨论标记分类(即序列标记)任务。
除了上面的任务之外,这里有几篇论文为我们在布局理解方面的工作提供了信息。我们在下面提供了最有影响力的几部作品的摘要和其他作品的链接。
LayoutLM
LayoutLM 模型的架构深受 BERT 的启发,同时结合了来自更快的 R-CNN 模型的图像嵌入。LayoutLM 输入嵌入作为文本和位置嵌入的组合生成,然后与图像嵌入组合。屏蔽视觉语言模型(受原始 MLM 的启发)和多标签文档分类模型(模型如何很好地聚类相似的文档)主要用作 LayoutLM 的预训练任务。LayoutLM 模型对于版面理解是足够有用和动态的,表单理解、收据理解和文档图像分类作为下游任务包含在本文中。在我们的例子中,微调的主要下游任务是屏蔽(元素)语言建模,屏蔽表示 UI 的每个元素的标记。

LayoutLM 模型架构—(图片来自徐等,2020 )
原始 LayoutLM 模型在 IIT-CDIP 测试集 1.0 上进行预训练,该测试集包含超过 600 万个文档,以及超过 1100 万个扫描文档图像。正如在相关任务一节中介绍的,我们针对我们的目的对 RICO 数据集上的模型进行了微调。
我们考虑改进 LayoutLM 论文,因为它将现成的 OCR 视为基本事实,这对于从业者来说不太实际。对 OCR 有更大程度的控制可以提供更好的下游任务相关的结果。

CanvasEmb 模型的架构——(图片来自谢等,2020 )。
CanvasEMB 是一个大规模、自我监督、预训练的模型,用于学习上下文布局信息,如 LayoutLM,它将布局分解为类型、几何形状、颜色和内容相关属性。该模型可以应用于下游任务,如角色标签和图像字幕(具有 SOTA 性能),也可以用于布局自动完成和布局检索。
在该模型中,从 0 到 N 的每个视觉元素 X_i 由属性元素 0 到 M 组成,这些属性元素被投影并连接成元素嵌入。对于分类属性(类型、颜色),使用嵌入矩阵,对于数字属性,采用正弦位置编码。
该模型基于 BERT 或 LayoutLM 等屏蔽语言建模进行训练,其他微调任务添加了特定于任务的层,如:
该模型根据带标签的数据进行预训练,并根据演示幻灯片数据集进行微调,这意味着与其他模型预训练的文档不同,该模型包含不同类型的语义信息。
这篇论文( Shiv 等人,2020 )提出了一种生成新的位置编码来编码分层信息的技术。在像 RICO 这样的数据集里,用户界面有丰富的层次信息,普通变形金刚中的顺序位置嵌入是不够的。本文针对常规树提出了这种技术,其中形成了类似堆栈的数据结构,并且针对树中向下的每一步将一个向量添加到堆栈中,n 长向量中的一个向量(针对每个节点的 n 个节点)表示当前节点是上一级的下一个分支。****
因为我们不是在处理常规的树(其中每个节点都有相同数量的子节点),所以必须对本文中的技术进行修改,以使其可行。下面将更详细地讨论新颖的位置编码和这些变化的示意图。
—
—
用户界面通常在组成它的元素集合中包含丰富的层次结构。例如,一个列表可以包含几个列表项,每个列表项可以包含一个卡片对象,这个卡片对象可以包含一个图像、一个段落和一个图标。我们也不需要一个固定的事实来获得这个信息——如果除了元素类名之外还存在边界框信息,那么我们可以通过查看哪些边界框相互重叠来推断哪些元素是子元素。
我们试图解决的第一个任务是上面的标记分类(即序列标记)任务。我们修改了我们最初研究的模型 LayoutLM,使其能够输入边界框和类标签信息。与大多数传统的 transformer 模型一样,LayoutLM 接受由序列中的位置编码的固定大小的输入。然而,由于我们知道用户界面的层次信息是一个丰富的信息源,我们可以通过将 LayoutLM 方法与基于树的新颖位置嵌入相结合来利用这个信息。****

从 Shiv & Quirk,2020 到变压器的基于树的输入的新颖位置编码(图片来自 Shiv & Quirk,2020 )。
直观地说,通过注入关于输入令牌(UI 元素)如何相互关联的附加信息,我们应该能够更好地执行下游任务。否则,该信息可能已经由模型在多个训练步骤之后或者在看到许多示例之后学习到,但是通过在输入中显式地提供它,我们允许模型的参数编码其他(并且希望更有用)表示。
元素之间的关系由 JSON 文件中的 RICO 数据集提供,它告诉我们每个 UI 元素的祖先和子元素。我们找到了树的最大度数(n)和树的最大深度(k ),并为每个示例中的每个元素形成了 n×k 大小的向量,为每个单独节点未使用的每个值添加了填充。这个大小是一致的,因此解码器可以理解这些嵌入。
虽然我们相信有很好的理由通过这些位置嵌入来提高性能(如上所述),但实际上它们很难实现。我们面临的一些挑战包括:
当实现这些分层位置嵌入时,由于在所有示例中简单使用最大深度和度时位置编码向量的稀疏性,为维度选择适当的值是重要的。我们将在下一节进一步讨论这些挑战。
—
—
为了探索布局理解模型,我们使用了前面提到的 RICO 数据集,它由超过 66k 个独特的 UI 屏幕的视觉、文本、结构和交互设计属性组成。当探索这样的数据集时,最初的认识是 UI 屏幕并不总是像我们预期的那样出现,例如,它们可能具有非常高的信息密度。下面的示例显示了在 RICO 数据集的任何给定级别上具有最大节点数的 UI:421。

下面的示例显示了在 RICO 数据集的任何给定级别上具有最大节点数的 UI—总共 421 个。(图片由作者提供)。
当使基于树的位置嵌入适应于非规则树(其中每个节点可以有不同数量的孩子)时,一个考虑是存储器使用。为异常值示例添加的填充如果过多,在微调变压器时会给计算带来很大压力。回想一下,我们的基于树的位置嵌入向量的维数和填充是数据集中任何单个节点(包括根)的总最大深度和总最大分支的乘积。
在下面的方框图中,我们看到绝大多数 UI 屏幕的元素层次最多有 25 个分支或者更少。如果我们保留离群值,位置嵌入向量的维数将大几个数量级。在 80/20 训练测试分割且没有过滤的情况下,包含训练示例的位置嵌入向量的文本文件是 3.09 千兆字节,其在投影层中被投影为 631.40 千兆字节。通过过滤具有最多 80 个分支的示例,我们能够将每个向量的维数从 2947 (4217)减少到 560 (807),由于填充和削减存储嵌入的存储器,使得向量不那么稀疏,减少了 80%以上。虽然过滤减少了内存占用,但使用 560 的令牌嵌入维度仍然会导致向量投影到 79.11 千兆字节,远远超过(我们的)典型 GPU 上可用的 16 千兆字节内存。**

RICO 数据集中示例树层次结构中节点的最大分支数量的箱线图(图片由作者提供)。
有了这些信息,改进模型在 RICO 数据集上的微调方式的一个可能的解决方案是在进一步使用的示例中过滤最大数量的节点(从 421 个节点到 25 个节点),以便更好地反映数据集本身的分布。另一种方法是从数据集中移除被认为是“装饰性元素”的内容。对数据集的分析显示,所有叶子(没有子节点的节点)的 39%多一点存在于数据集中的第一级树上。
我们还可以通过限制层次结构的深度来过滤 RICO 数据集中的元素。虽然最大深度是 7,但超过 99%的叶子都集中在每个示例树的前三层,这意味着很少有分支到达第 7 层甚至第 4 层(0.6%的叶子位于该层)。
总之,过滤数据集中使用了哪些示例以及每个示例中包含了哪些节点的信息会导致有价值的语义信息的显著丢失,这些信息有助于识别复杂的 UI 元素分组。然而,保留所有信息会在训练时导致严重的内存问题,我们认为过滤是对 RICO 数据集上的 LayoutLM 等模型进行微调以执行相关下游任务的必要的第一步。
布局理解是一个子领域,在未来的几年里已经成熟,可以取得更大的进步,我们希望这项工作能够展示一些潜力。最近将 LayoutLM 添加到 HuggingFace transformers 库中也应该允许研究社区进行更快的迭代。总结一下:
如相关任务部分所述,布局理解任务可以在多任务训练中结合起来,以获得更好的表征学习(类似于 T5 模型, Raffel 等人,2020 )。其他未来的工作包括试验其他形式的位置嵌入,并将这项工作扩展到下一代版本的 LayoutLM (LayoutLMv2 — 徐等人,2020 )。
感谢 Javier Fuentes Alonso、Arturo Arranz 和 Tony Beltramelli 在过去几个月对这项工作的支持。要了解更多关于 Uizard 如何将机器学习应用到设计中,请查看 Uizard 的研究页面这里。
这篇文章的作者也是杜克应用机器学习(DAML)的成员,这是杜克大学的一个学生团体,致力于应用人工智能项目和研究——点击这里了解更多关于 DAML 的信息。
原文:https://towardsdatascience.com/improving-your-data-visualizations-with-stacked-bar-charts-in-python-f18e2b2b9b70?source=collection_archive---------23-----------------------

我才华横溢的姐姐的作品
仅仅看表中的数字可能会使识别数据集中的趋势变得困难。可视化数据使人们更容易快速理解关键思想。数据可视化的一个重要部分是根据正在分析的数据类型选择正确的图表类型。
标准散点图、折线图和条形图非常适合可视化各种数值和分类数据。您可以通过添加附加功能来改进这些基本图表,使最终用户更容易理解数据。
在这篇文章中,让我们来看看如何使用 Python 中的 Plotly Express 库来创建和定制堆积条形图以实现数据可视化。我们还将使用 Pandas 库进行一些数据预处理步骤,所以请确保您已经安装了这两个包。然后,导入以下内容,并准备好在您自己的 Jupyter 笔记本上继续学习!
import pandas as pd
import plotly.express as px
import random
我们开始吧!
首先,让我们生成一些样本数据,用于我们的数据分析和可视化。为此,请运行以下代码。
expense_data = {
"Person": random.choices(["A", "B"], k=30),
"Amount": random.sample(range(100, 200), 10) + random.sample(range(0, 99), 10) + random.sample(range(49, 499), 10),
"Category": ["Groceries"] * 10 + ["Restaurant"] * 10 + ["Appliances"] * 10,
"Date": pd.to_datetime(pd.date_range('2020-01-01','2020-10-01', freq='MS').tolist() * 3)
}
df = pd.DataFrame(data=expense_data)

作者图片
在这篇文章中,我们将分析和可视化一些随机生成的个人支出数据。该代码应该生成一个 Pandas 数据框架,其中包含一段时间内三个不同类别的 30 行费用。
**此数据分析的目标是检查每个月每个类别的支出,并潜在地确定一段时间内发生的趋势。**因此,让我们用一些熊猫来对每个类别每月的总支出进行分组。您可以使用下面的两行代码来做到这一点。
df_grouped = df.groupby(by=[pd.Grouper(key="Date", freq="1M"), "Category"])["Amount"]
df_grouped = df_grouped.sum().reset_index()

作者图片
现在我们的数据已经被处理以适应我们最初的问题,我们可以直接进入可视化数据。Plotly 库使看起来干净的可视化比你用 Pandas(或 matplotlib)创建的标准图形更有风格。Plotly Express 更进了一步,使创建可视化变得简单得可笑。我们将用下面的一行代码创建初始图表。
fig = px.bar(df_grouped, x="Date", y="Amount", color="Category")
fig.show()

作者图片
bar方法的第一个输入是我们刚刚创建的分组数据帧。然后,我们将数据帧中的列名传递给bar方法的x和y参数。最后,要实现堆叠条形图,我们需要做的就是将想要堆叠的列名传递到color参数中。
通过填写可选的barmode参数,您可以进一步定制堆积条形图。你可以在下面看到一个例子和结果图。
fig2 = px.bar(df_grouped, x="Date", y="Amount", color="Category", barmode="group")
fig2.show()

作者图片
将group作为参数传递给barmode给我们一个类似上面的图,其中每个月的每个类别的费用条并排分组,而不是垂直堆叠。
有四个可能的选项可以传入 **barmode** **:堆栈、组、覆盖和相对。**作为探索性数据分析阶段的一个示例,您可以运行以下代码来查看条形图的所有不同选项。
barmodes = ["stack", "group", "overlay", "relative"]
for barmode in barmodes:
fig_bar = px.bar(df_grouped, x="Date", y="Amount", color="Category", barmode=barmode)
fig_bar.show()

作者图片

作者图片

作者图片

作者图片
每个条形模式选项显示的图形略有不同。“相对”栏模式仅在您有负值时才相关,因为此模式将导致 Plotly 在轴下方显示负值的相关类别。
您可以将上面的代码块封装到一个函数中,然后作为探索性数据分析(EDA)的一部分在您的数据上运行它,在这里您还可以找出这些图形中的哪一个最适合您正在查看的数据。然后,一旦你决定了哪种条形模式最适合你,你可以选择只生成一个图形或者从你的笔记本中将图形导出为 png(这是 Plotly Express 的一个便利特性)。
我们来看看条形图的其他一些功能,让我们继续分析我们的样本数据并比较一段时间内的人均支出数据。为此,我们将使用与之前相同的groupby方法再次对数据进行分组。
df_people = df.groupby(by=[pd.Grouper(key="Date", freq="1M"), "Person"])["Amount"]
df_people = df_people.sum().reset_index()

作者图片
得到的数据框架将给出我们数据集中每人每月的总支出。我们现在可以像以前一样将这个数据帧插入到 Plotly bar方法中,但是有一个小的增加。
fig_people = px.bar(df_people, x="Date", y="Amount", color="Person", barmode="stack", text="Amount")
fig_people.show()

作者图片
通过这张图表,我们现在可以清楚地看到哪个人每月花费更多。因为我们处理的是财务数据,所以将每个类别的金额直接显示在条形图上可能会有用(而不必从轴上猜测大致的金额)。
为此,我们将bar方法中的text参数设置为我们希望显示为条形标签的列,在本例中是“Amount”。然后,您可能会从图表中删除 y 轴,因为它现在是多余的,因为文本标签直接在列上。
通过**分析每个类别的人均支出,我们可以探索 Plotly Express 中条形图的最后一个特性。**我们将通过运行下面的代码,首先将我们的数据按类别和人员分组到一个新的数据框架中。
df_category = df.groupby(["Category", "Person"]).sum().reset_index()

作者图片
现在,使用这个分组的数据帧,我们可以使用相同的 Plotly express bar方法,使用我们到目前为止探索过的特性,但是增加了一些内容。
fig_category = px.bar(df_category, x="Amount", y="Person", color="Category", text="Amount", orientation="h")
fig_category = fig_category.update_traces(insidetextanchor="middle", texttemplate="$%{text}")
fig_category = fig_category.update_xaxes(visible=False, showticklabels=False)
fig_category.show()

作者图片
首先,您将看到我们创建了一个水平条形图,而不是我们以前使用的垂直格式。我们所要做的就是将“h”传递给 bar 方法中的参数orientation。然后,我们还想再次拥有文本标签,但是为了确保文本标签出现在条形标签的中间,我们将使用update_traces方法,并将“中间”传递给insidetextanchor参数。
此外,我们可以通过在update_traces方法的texttemplate参数中设置文本标签的自定义格式。我们将简单地在标签前添加一个美元符号,所以在这种情况下,我们传入"$%{text}",因为在 Plotly Express 中,您可以通过使用百分号后跟括号中的列名来引用列。
最后,由于我们已经添加了一个美元符号来使条形值非常清楚,我们可以继续使用update_xaxes方法并设置visible和showticklabels参数为“False”来删除带有“Amount”列值的轴。
仅此而已!
我希望你会发现这个用 Python 编写的关于堆积条形图的快速介绍对你的数据分析很有用。使用 Plotly Express,您只需插入一个 Pandas 数据框架,只需几行代码就可以直接可视化您的数据。
再次感谢您的阅读!如果你正在考虑成为 Medium 的付费会员,如果你使用我下面的推荐链接注册,我会非常感激!这会让直接收到你的一部分会费,所以这将是一个很大的帮助。
https://byrondolon.medium.com/membership
**More by me:** - [Check for a Substring in a Pandas DataFrame](/check-for-a-substring-in-a-pandas-dataframe-column-4b949f64852?sk=bfb5bbab11ae45c47bfb316d931c3b56)
- 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)
- [Top 4 Repositories on GitHub to Learn Pandas](/top-4-repositories-on-github-to-learn-pandas-1008cb769f77?source=friends_link&sk=d3acc38062490a86ecb46875342224e6)
- [Level Up Your Data Visualizations with Trend Lines in Python](/level-up-your-data-visualizations-with-trend-lines-in-python-6ad4a8253d6?sk=d9174bf7fd3394000acd92289ac41623)
- [Better Data Visualization with Dual Axis Graphs in Python](/better-data-visualization-with-dual-axis-graphs-in-python-a7f35a493558?sk=4e080437f9d29818e25120e287fa550d)
原文:https://towardsdatascience.com/improving-your-image-matching-results-by-14-with-one-line-of-code-b72ae9ca2b73?source=collection_archive---------1-----------------------
OpenCV 4.5.1 中最令人兴奋的功能之一是 BEBLID(增强的高效二进制局部图像描述符),这是一种新的描述符,能够提高图像匹配的准确性,同时减少执行时间!这篇文章将向你展示这个魔法是如何实现的。所有源代码都存储在这个 GitHub 存储库中:
https://github.com/iago-suarez/beblid-opencv-demo/blob/main/demo.ipynb
在本例中,我们将匹配这两幅因视点变化而相关的图像:

首先,确保安装了正确版本的 OpenCV 是很重要的。在您喜欢的环境中,您可以使用以下命令安装并检查 OpenCV Contrib 版本:
**pip install "opencv-contrib-python>=4.5.1"**
python
>>> import cv2 as cv
>>> print(f"OpenCV Version: {cv.__version__}")
OpenCV Version: 4.5.1
用 Python 加载这两幅图像所需的代码是:
为了评估我们的图像匹配程序,我们需要两幅图像之间正确的(即地面真实)几何变换。这是一个称为单应性的 3×3 矩阵,当我们将第一幅图像中的一个点(在齐次坐标中)相乘时,它会返回第二幅图像中该点的坐标。让我们加载它:
下一步是检测图像中一些容易在其他图像中发现的部分:局部图像特征。在这个例子中,我们将使用快速可靠的检测器 ORB 来检测角点。ORB 检测强角点,在不同的尺度上进行比较,并使用其 FAST 或 Harris 响应来选择最佳角点。它还使用局部面片一阶矩找到每个角点方向。让我们在每幅图像中最多检测 10000 个角:
在下图中,您可以看到用绿点标记的 500 个检测响应最强的角点特征:

干得好!现在是时候用一种我们可以在其他图像中找到的方式来表示这些关键点了。该步骤被称为描述,因为每个角周围的局部小块中的纹理由来自图像上不同操作的数字矢量表示 (即被描述)。有许多描述符,但如果我们想要一些准确的东西,甚至在手机或低功耗设备上实时运行,OpenCV 有两种重要的方法:
是时候匹配两幅图像的描述符以建立对应关系了。让我们使用强力算法,它基本上将第一幅图像中的每个描述符与第二幅图像中的所有描述符进行比较。当我们处理二进制描述符时,使用汉明距离进行比较,即计算每对描述符之间不同的位数。
这里还使用了一个叫做比率测试的小技巧。它确保不仅描述符 1 和 2 彼此相似,而且没有其他描述符像 2 一样接近 1。
因为我们知道正确的几何变换,所以让我们检查有多少匹配是正确的(内嵌)。如果它在图像 2 中的点和它从图像 1 投影到图像 2 的点之间的距离小于 2.5 个像素,我们将认为马赫是有效的。
既然我们在 inliers1 和 inliers2 变量中有了正确的匹配,我们可以使用 cv.drawMatches 对结果进行定性评估。每一个对应点都可以帮助我们完成更高层次的任务,如单应性估计、透视-n 点、平面跟踪、实时姿态估计或图像拼接。

因为很难对这种结果进行定性比较,所以让我们绘制一些定量评估指标。最能反映我们的描述符可靠性的指标是内联体的百分比:

Matching Results (**BEBLID**)
*******************************
# Keypoints 1: 9105
# Keypoints 2: 9927
# Matches: 660
# Inliers: 512
# Percentage of Inliers: **77.57%**
使用 BEBLID 描述符获得一个 77.57% 的内联器。如果我们在 description 单元格中注释 BEBLID 并取消注释 ORB 描述符,结果将下降到 63.20% :
Matching Results (**ORB**)
*******************************
# Keypoints 1: 9105
# Keypoints 2: 9927
# Matches: 780
# Inliers: 493
# Percentage of Inliers: **63.20%**
总之,只需更改一行代码,用 BEBLID 替换 ORB 描述符,我们就可以将这两幅图像的匹配结果提高 14%。这对需要局部特征匹配的高级任务有很大的影响,所以不要犹豫,试试 BEBLID 吧!
原文:https://towardsdatascience.com/imputer-class-in-python-from-scratch-66df6ae067e1?source=collection_archive---------18-----------------------

类图
这份实验室工作报告的主要重点是展示对面向对象编程原则的理解以及如何应用它们。
实现的每一步都有说明;因此,假设额外的代码文档是多余的。然而,为了展示对 PEP 257 中描述的文档约定的理解,示例包含在类inputr中。
由于代码是为了演示的目的而不是为了重用,错误处理也简化为在类估算器中产生错误的两个例子。
估算器将执行其估算选择的策略模式,这使得所使用的算法能够在运行时独立变化。从而应用设计原则 1: 识别应用程序中变化的方面,并将它们与保持不变的方面分开。
导入一个用于定义抽象基类的元类,以及一个指示来自 abc 模块的抽象方法的装饰器。
from abc import ABCMeta, abstractmethod
导入 deepcopy 以制作复合对象的唯一副本。
from copy import deepcopy
首先定义了策略界面。
插补策略类是所有复合类继承的超类,它定义了一个接口,其默认行为集等于所有具体策略。从而将设计原则 2: 程序应用于一个接口,而不是一个实现,以及设计原则 3: 重组合轻继承。
class ImputationStrategy(metaclass=ABCMeta): @abstractmethod
def _imputation(self, my_list: list, missing_values: str) -> float:
"""Must be implemented in order to instanciate"""
第二,明确具体策略。一系列算法被封装在单独的类中,并通过拥有相同的接口而变得可互换。
每个家庭成员都可以定义新的行为;然而,它们继承了所有超类的行为,可以通过覆盖它们来替换。在超类中定义 abstractmethods 要求任何子类实现特定的行为,从而实现一个公共的默认接口。
由于这些类只有一个单一的责任设计原则 6: *一个类应该只有一个改变的理由。*已应用。
用平均数估算平均数是一组数字的平均总和。
class ImputeByMean(ImputationStrategy): def _imputation(self, my_list: list, missing_values: str) -> float:
temp_sum = 0
count = 0
for num in my_list:
if num != missing_values:
temp_sum += num
count += 1
mean = (temp_sum/count)
return round(mean, 2)
按中位数估算
中位数是一个排序的数字列表的中间值。
class ImputeByMedian(ImputationStrategy): def _imputation(self, my_list: list, missing_values: str) -> float:
temp_list = []
for num in my_list:
if num != missing_values:
temp_list.append(num) temp_list.sort()
temp_len = len(temp_list) if temp_len % 2 == 0:
median1 = temp_list[temp_len//2]
median2 = temp_list[temp_len//2 - 1]
median = (median1 + median2)/2
else:
median = temp_list[temp_len//2]
return round(median, 2)
通过模式估算
模式是最频繁的值。
**注意:**模式包含在上下文中,但出于演示目的在下面实施。
class ImputeByMode(ImputationStrategy): def _imputation(self, my_list: list, missing_values: str) -> float:
frequency = {}
for item in my_list:
if item != missing_values:
if (item in frequency):
frequency[item] += 1
else:
frequency[item] = 1
mode = max(frequency, key=frequency.get)
return round(mode, 2)
通过实例化类并引用对象实例,只需要创建一个对象。从而应用设计原则 5: 依赖抽象。不要依赖于具体的类。
如果直接调用这个类,每次都会定义新的对象。
mean = ImputeByMean()
median = ImputeByMedian()
# mode = ImputeByMode()
Imputer 是策略模式上下文,包含对具体策略对象的实例化的引用。
估算器使用策略接口调用由具体策略定义的算法;然后每个具体的策略实现一个算法。
因为这是估算器和策略接口之间的唯一连接,所以应用了设计原则 4: 争取交互的对象之间的松耦合设计。
当需要一个操作时,具体的策略对象运行算法,而输入者不知道策略的实现。
如果有必要,可以实例化附加的估算对象,将数据从估算对象传递到策略接口,从而排除它作为单一候选对象。
class Imputer:
"""
The base class for imputer objects.
Enables the user to specify which imputation method, and which "cells" to
perform imputation on in a specific 2-dimensional list.
A unique copy is made of the specified 2-dimensional list before
transforming and returning it to the user.
""" def __init__(self, strategy="mean", axis=0) -> None:
"""
Defining instanse attributes on instansiation. Args:
strategy (str, optional): A concrete strategy. Defaults to "mean".
axis (int, optional): Column=0 or Row=1. Defaults to 0.
""" # Reference to the concrete strategy object being used.
self._strategy = strategy
# Calling internal method.
self.__strategy_prosessor()
# Reference to the axis orientation being used.
self._axis = axis
# Reference to the keyword for missing values.
# Defined as public, as per convention.
self.missing_values = "nan"
# Defines which column or row to start.
self._from_item = None
# Defines which column or row to end.
self._to_item = None def __strategy_prosessor(self) -> None:
"""
Internal method validating that selected strategy is allowed.
If so, selecting its imputation method. Raises:
AssertionError: If the selected strategy is not allowed.
""" allowed_strategies = ["mean", "median", "mode"]
if self._strategy == allowed_strategies[0]:
self._strategy = mean._imputation
elif self._strategy == allowed_strategies[1]:
self._strategy = median._imputation
elif self._strategy == allowed_strategies[2]:
self._strategy = mode._imputation
else:
assert self._strategy in allowed_strategies, (
f"Can only use these strategies: {allowed_strategies}, "
f"got strategy = {self._strategy}") def __transpose(self, my_matrix: list) -> list:
"""
Transposes 2-dimensional list. Args:
my_matrix (list): 2-dimensional list. Returns:
list: 2-dimensional list transposed.
""" trans_matrix = []
temp_matrix = [[my_matrix[j][i] for j in range(len(my_matrix))]
for i in range(len(my_matrix[0]))]
for row in temp_matrix:
trans_matrix.append(row)
return trans_matrix def fit(self, my_matrix: list, from_item: int, to_item: int) -> object:
"""
Passes in the 2-dimensional list for imputation,
and sets from which column to start with, and end by. Args:
my_matrix (list): 2-dimensional list.
from_item (int): The column to start with.
to_item (int): The column to end by. Raises:
ValueError: If axis is not equal to the defined options. Returns:
object: The same imputer object that calls the method.
""" self._to_item = to_item if self._axis == 0:
self._array = my_matrix
self._from_item = from_item - 1
elif self._axis == 1:
self._array = self.__transpose(my_matrix)
self._from_item = from_item
else:
raise ValueError(
f"Can only use integer value 0 or 1: "
f"got axis = {self._axis}")
return self def __axis_lister(self, matrix: list, col: int) -> list:
"""
Generates a list for all values in a 2-dimensional list column. Args:
matrix (list): 2-dimensional list.
col (int): selected column to generat list from. Returns:
list: All values in a 2-dimensional list column.
""" temp_list = []
for row in range(len(matrix)):
temp_list.append((matrix[row][col]))
return temp_list def _imputation(self, my_list: list, missing_values: str) -> float:
"""
Passing a list to the concrete strategy object with the desired
imputation algorithm. For this reason, the method cannot be private,
but have to be public or protected. Args:
my_list (list): The list to be calculated by the algorithm.
missing_values (str): The keyword for the missing values. Returns:
float: The calculated value to swap with the missing value keyword
""" return_value = self._strategy(my_list, missing_values)
return return_value def transform(self) -> list:
"""
Inserts the imputed column value in each column-row ("cell") of the
2-dimensional list where the missing value keyword exists. Returns:
list: A unique copy of the selected 2-dimensional list after
it has been imputed.
""" return_matrix = deepcopy(self._array)
for col in range(self._from_item, self._to_item):
imputed_value = self._imputation(
self.__axis_lister(self._array, col), self.missing_values)
for row in range(len(return_matrix)):
if return_matrix[row][col] == self.missing_values:
return_matrix[row][col] = imputed_value
if self._axis == 0:
pass
elif self._axis == 1:
return_matrix = self.__transpose(return_matrix) return return_matrix def __str__(self) -> str:
"""
Provides users with an easy to read representation of the class. Returns:
str: The class name.
""" return f"{self.__class__.__name__}" def __repr__(self) -> str:
"""
Provides developers with unambigous information of the class. Returns:
str: The class name and the state of instance variables.
"""
return "{self.__class__.__name__}"
"(Strategy: {self._strategy}, "
"Axis:{self._axis}, "
"Missing value: {self.missing_values}, "
"From:{self._from_item}, "
"To:{self._to_item})".format(self=self)
具有用于转置二维列表的方法和基于单词长度用制表符打印该列表格式的方法的类被实例化。
class Matrix:
def transpose(self, my_matrix):
trans_matrix = []
temp_matrix = [[my_matrix[j][i] for j in range(len(my_matrix))]
for i in range(len(my_matrix[0]))]
for row in temp_matrix:
trans_matrix.append(row)
return trans_matrix def printer(self, my_matrix, title=None, axis=0):
my_matrix = deepcopy(my_matrix)
if title is not None:
if axis == 0:
my_matrix.insert(0, title)
elif axis == 1:
for i in range(len(my_matrix)):
my_matrix[i].insert(0, title[i])
str_matrix = [[str(entity) for entity in row] for row in my_matrix]
max_len_col_str = [max(map(len, col)) for col in zip(*str_matrix)]
form = " ".join("{{:{}}}".format(x) for x in max_len_col_str)
matrix_row = [form.format(*row) for row in str_matrix]
return_matrix = "
".join(matrix_row)
print(return_matrix)matrix = Matrix()
提供了一个用于演示的模拟数据集。
dataset = list([["Country", "Age", "Salary", "Children", "Cars"],
["Swe", 38.0, 47200.0, 1, 1],
["Den", 27.0, 48000.0, 0, 6],
["Nor", 30.0, 54000.0, 2, "nan"],
["Den", 38.0, 61000.0, "nan", 1],
["Nor", 40.0, "nan", 2, 1],
["Swe", 35.0, 58000.0, 1, 1],
["Den", "nan", 52000.0, 0, "nan"],
["Swe", 48.0, 67900.0, 2, 1],
["Nor", 50.0, 88300.0, 6, 2],
["Swe", 37.0, 67900.0, "nan", 2]])
模拟了一些数据预处理。首先,从列表中删除标题行,并将其插入到自己的列表中供以后使用。
dataset_title_row = dataset.pop(0)
display(dataset_title_row)
display(dataset)['Country', 'Age', 'Salary', 'Children', 'Cars'][['Swe', 38.0, 47200.0, 1, 1],
['Den', 27.0, 48000.0, 0, 6],
['Nor', 30.0, 54000.0, 2, 'nan'],
['Den', 38.0, 61000.0, 'nan', 1],
['Nor', 40.0, 'nan', 2, 1],
['Swe', 35.0, 58000.0, 1, 1],
['Den', 'nan', 52000.0, 0, 'nan'],
['Swe', 48.0, 67900.0, 2, 1],
['Nor', 50.0, 88300.0, 6, 2],
['Swe', 37.0, 67900.0, 'nan', 2]]
定义数据集的转置版本,以便稍后演示轴功能。
dataset_trans = matrix.transpose(dataset)
模拟预处理结束后,演示矩阵对象打印方法;传入所需的数据集以及可选的标题行。
matrix.printer(dataset, dataset_title_row)Country Age Salary Children Cars
Swe 38.0 47200.0 1 1
Den 27.0 48000.0 0 6
Nor 30.0 54000.0 2 nan
Den 38.0 61000.0 nan 1
Nor 40.0 nan 2 1
Swe 35.0 58000.0 1 1
Den nan 52000.0 0 nan
Swe 48.0 67900.0 2 1
Nor 50.0 88300.0 6 2
Swe 37.0 67900.0 nan 2
现在,对转置版本执行相同的过程。但是,有必要将 axis 属性设置为1来表示转置矩阵,以确保正确的输出格式。
**注意:**为了正确显示转置矩阵的预期输出,运行 Jupyter 的网络浏览器必须具有默认缩放级别。
matrix.printer(dataset_trans, dataset_title_row, 1)Country Swe Den Nor Den Nor Swe Den Swe Nor Swe
Age 38.0 27.0 30.0 38.0 40.0 35.0 nan 48.0 50.0 37.0
Salary 47200.0 48000.0 54000.0 61000.0 nan 58000.0 52000.0 67900.0 88300.0 67900.0
Children 1 0 2 nan 2 1 0 2 6 nan
Cars 1 6 nan 1 1 1 nan 1 2 2
首先,实例化默认插补器、平均值插补和列插补。
mean_imputer = Imputer()
演示了在估算类中定义的__str__和__repr__方法。
print(mean_imputer) # <- __str__
mean_imputer # <- __repr__ImputerImputer(Strategy: <bound method ImputeByMean._imputation of <__main__.ImputeByMean object at 0x10f3dbf50>>, Axis:0, Missing value: nan, From:None, To:None)
定义估算者应使用哪个数据集作为数据源,以及从哪个列开始和结束。
mean_imputer = mean_imputer.fit(dataset, 2, 5)
现在,估算器复制二维列表,使用所需的策略对象根据轴设置计算所有选定的列或行值,然后将结果返回给用户。
dataset_by_mean = mean_imputer.transform()
显示插补的结果。
matrix.printer(dataset_by_mean, dataset_title_row)Country Age Salary Children Cars
Swe 38.0 47200.0 1 1
Den 27.0 48000.0 0 6
Nor 30.0 54000.0 2 1.88
Den 38.0 61000.0 1.75 1
Nor 40.0 60477.78 2 1
Swe 35.0 58000.0 1 1
Den 38.11 52000.0 0 1.88
Swe 48.0 67900.0 2 1
Nor 50.0 88300.0 6 2
Swe 37.0 67900.0 1.75 2
现在演示由平均值估算的的转置版本,以及从和到设置。在这个例子中,最后一行被省略了。
首先,实例化均值插补器、均值插补器和 row。然后,执行与平均值输入的所示相同的步骤。
mean_imputer_trans = Imputer("mean", 1)
mean_imputer_trans = mean_imputer_trans.fit(dataset_trans, 1, 4)
dataset_by_mean_trans = mean_imputer_trans.transform()
matrix.printer(dataset_by_mean_trans, dataset_title_row, 1)Country Swe Den Nor Den Nor Swe Den Swe Nor Swe
Age 38.0 27.0 30.0 38.0 40.0 35.0 38.11 48.0 50.0 37.0
Salary 47200.0 48000.0 54000.0 61000.0 60477.78 58000.0 52000.0 67900.0 88300.0 67900.0
Children 1 0 2 1.75 2 1 0 2 6 1.75
Cars 1 6 nan 1 1 1 nan 1 2 2
首先,对中位数估算值进行实例化,按中位数和列进行估算。然后,执行与由平均值输入的所示相同的步骤。
median_imputer = Imputer("median")
median_imputer = median_imputer.fit(dataset, 2, 5)
dataset_by_median = median_imputer.transform()
matrix.printer(dataset_by_median, dataset_title_row)Country Age Salary Children Cars
Swe 38.0 47200.0 1 1
Den 27.0 48000.0 0 6
Nor 30.0 54000.0 2 1.0
Den 38.0 61000.0 1.5 1
Nor 40.0 58000.0 2 1
Swe 35.0 58000.0 1 1
Den 38.0 52000.0 0 1.0
Swe 48.0 67900.0 2 1
Nor 50.0 88300.0 6 2
Swe 37.0 67900.0 1.5 2
定义了一个新的具体策略类来证明估算器是可扩展的,而不影响任何现有的策略。启用新策略所需的唯一更新代码是在 Imputer _ _ strategy _ prosessor 方法的 allowed_strategies 列表中添加一个选项,以及在同一方法的 if 语句中添加一个选项。
class ImputeByMode(ImputationStrategy): def _imputation(self, my_list: list, missing_values: str) -> float:
frequency = {}
for item in my_list:
if item != missing_values:
if (item in frequency):
frequency[item] += 1
else:
frequency[item] = 1
mode = max(frequency, key=frequency.get)
return round(mode, 2)
首先,模式估算器被实例化,按模式和列进行估算。然后,执行与平均值输入的所示相同的步骤。
mode = ImputeByMode()
mode_imputer = Imputer("mode")
mode_imputer = mode_imputer.fit(dataset, 2, 5)
dataset_by_mode = mode_imputer.transform()
matrix.printer(dataset_by_mode, dataset_title_row)Country Age Salary Children Cars
Swe 38.0 47200.0 1 1
Den 27.0 48000.0 0 6
Nor 30.0 54000.0 2 1
Den 38.0 61000.0 2 1
Nor 40.0 67900.0 2 1
Swe 35.0 58000.0 1 1
Den 38.0 52000.0 0 1
Swe 48.0 67900.0 2 1
Nor 50.0 88300.0 6 2
Swe 37.0 67900.0 2 2
使用 PlantUML 生成 UML 类图的演示。
from zlib import compress
import base64
import string
import requests# This was taken from plantuml library
plantuml_alphabet = string.digits +
string.ascii_uppercase + string.ascii_lowercase + '-_'
base64_alphabet = string.ascii_uppercase +
string.ascii_lowercase + string.digits + '+/'
b64_to_plantuml = bytes.maketrans(base64_alphabet.encode(
'utf-8'), plantuml_alphabet.encode('utf-8')) def deflate_and_encode(plantuml_text):
"""
zlib compress the plantuml text and encode it for the plantuml server.
"""
zlibbed_str = compress(plantuml_text.encode('utf-8'))
compressed_string = zlibbed_str[2:-4]
return base64.b64encode(compressed_string).translate(b64_to_plantuml).
decode('utf-8') def render_uml(uml, fmt="svg"):
uri = "http://www.plantuml.com/plantuml/{}/{}".format(
fmt, deflate_and_encode(uml))
r = requests.get(uri)
if r.ok:
return r.contentdiagram = """
@startumlskinparam class {
BackgroundColor White
ArrowColor Gray
BorderColor Black
}
skinparam stereotypeCBackgroundColor Gray
hide circletitle Imputer Strategy Patternclass Client
hide Client methods
hide Client attributesclass Imputer
Imputer : +fit()
Imputer : +transform()
Imputer : #imputation()
Imputer : -strategy_prosessor()
Imputer : -transpose()
Imputer : +missing_values : str = "nan"
Imputer : #strategy : str = "mean"
Imputer : #axis = int = 0
Imputer : #from_item : int
Imputer : #to_item : int
note Top: Contextclass ImputerStrategy <<Interface>>
ImputerStrategy : #imputation()class ImputByMean
ImputByMean : #imputation()
note Bottom: Concrete Strategy 1class ImputByMedian
ImputByMedian : #imputation()
note Bottom: Concrete Strategy 2class ImputByMode
ImputByMode : #imputation()
note Bottom: Concrete Strategy 3Imputer <-- Client : Requests
Imputer *- ImputerStrategy : Strategy
ImputerStrategy <|-- ImputByMean : Extends
ImputerStrategy <|-- ImputByMedian : Extends
ImputerStrategy <|-- ImputByMode : Extends@enduml
"""from IPython.display import SVG
SVG(render_uml(diagram))

导出为 png 文件格式。
from IPython.display import Image
Image(render_uml(diagram, "png"))

虽然我更愿意使用 sklearn 进行插补,但从头开始制作插补器教会了我很多关于面向对象编程的知识。
Lewi Uberg 是挪威应用数据科学专业的一名四年级学生,拥有各行各业的极客、IT 经理和 CAD 工程师的背景。请随意在 中关注他的 或访问他的 网站 。