TowardsDataScience 博客中文翻译 2022(三百二十三)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

随机森林回归

原文:https://towardsdatascience.com/random-forest-regression-5f605132d19d

7 分钟的基本解释和使用案例

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

塞斯·芬克在 Unsplash 上的照片

几周前,我写了一篇文章演示了随机森林分类模型。在本文中,我们将使用 sklearn 的 RandomForrestRegressor()模型演示随机森林的回归情况。

与我的上一篇文章类似,我将通过强调一些与随机森林机器学习相关的定义和术语来开始这篇文章。本文的目标是描述随机森林模型,并演示如何使用 sklearn 包来应用它。我们的目标不是解决最佳解决方案,因为这只是一个基本指南。

定义:
决策树
用于回归和分类问题。它们在视觉上像树一样流动,因此而得名,在回归的情况下,它们从树根开始,并根据可变结果进行分割,直到到达一个叶节点并给出结果。下面是决策树的一个示例:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

这里我们可以看到一个基本的决策树图,它从 Var_1 开始,根据特定的标准进行拆分。当“是”时,决策树沿着表示的路径前进,当“否”时,决策树沿着另一条路径前进。这个过程重复进行,直到决策树到达叶节点,并决定结果。对于上面的例子,a、b、c 或 d 的值可以代表任何数值或分类值。

集成学习是使用多个模型的过程,对相同的数据进行训练,平均每个模型的结果,最终找到更强大的预测/分类结果。我们对集成学习的希望和要求是,每个模型(在这种情况下是决策树)的误差是独立的,并且在不同的树之间是不同的。

Bootstrapping 是在给定迭代次数和给定变量数的情况下随机采样数据集子集的过程。然后将这些结果平均在一起,以获得更有效的结果。自举是应用集合模型的一个例子。

bootstrappingRandom Forest算法将集成学习方法与决策树框架相结合,从数据中创建多个随机绘制的决策树,对结果进行平均以输出新的结果,这通常会导致强预测/分类。

在这篇文章中,我将展示一个随机的森林模型,这个模型是由 Austin Reese 发布到 Kaggle 上的美国住房数据创建的,这个数据被授权为 CC0——公共领域。该数据集提供了有关待售房屋的信息和详细信息。该数据集由大约 380,000 个观察值和 20 多个变量组成。我进行了大量的 ed a,但为了让本文更多地介绍实际的随机森林模型,我不会包括所有的步骤。

随机森林回归模型: 我们将使用 sklearn 模块来训练我们的随机森林回归模型,特别是 RandomForestRegressor 函数。RandomForestRegressor 文档显示了我们可以为模型选择的许多不同的参数。下面重点介绍了一些重要参数:

  • n_estimators —您将在模型中运行的决策树的数量
  • 标准 —该变量允许您选择用于确定模型结果的标准(损失函数)。我们可以从均方误差(MSE)和平均绝对误差(MAE)等损失函数中进行选择。默认值为 MSE。
  • max_depth —设置每棵树的最大可能深度
  • max _ features-确定分割时模型将考虑的最大特征数
  • bootstrap —默认值为 True,这意味着模型遵循 bootstrap 原则(前面已定义)
  • max_samples —该参数假设 bootstrapping 设置为 True,否则该参数不适用。在 True 的情况下,该值设置每棵树的每个样本的最大大小。
  • 其他重要参数有 min_samples_split、min_samples_leaf、n_jobs 以及其他可以在 sklearn 的 RandomForestRegressor 文档中读取的参数,此处

出于本文的目的,我们将首先展示输入到随机森林回归模型中的一些基本值,然后我们将使用网格搜索和交叉验证来找到一组更优的参数。

rf = RandomForestRegressor(n_estimators = 300, max_features = 'sqrt', max_depth = 5, random_state = 18).fit(x_train, y_train)

看看上面的基本模型,我们使用了 300 棵树;max_features per tree 等于训练数据集中参数数量的平方根。每棵树的最大深度设置为 5。最后,random_state 被设置为 18 只是为了保持一切标准。

正如我在以前的随机森林分类文章中所讨论的,当我们解决分类问题时,我们可以使用准确性、精确度、召回率等指标来查看我们的性能。当查看回归模型的性能指标时,我们可以使用诸如均方误差、均方根误差、R、调整后的 R 等因素。在这篇文章中,我将重点介绍均方误差和均方根误差。

简单来说,均方误差(MSE)是实际输出值和预测输出值之间的平方差总和的平均值。我们的目标是尽可能降低 MSE。例如,如果我们有一个(3,5,7,9)的实际输出数组和一个(4,5,7,7)的预测输出数组,那么我们可以将均方误差计算为:
((3-4)+(5–5)+(7–7)+(9–7))/4 =(1+0+0+4)/4 = 5/4 = 1.25

均方根误差(RMSE)就是 MSE 的平方根,因此本例中 RMSE = 1.25^.5 = 1.12。

使用这些性能指标,我们可以运行以下代码来计算模型的 MSE 和 RMSE:

prediction = rf.predict(x_test)mse = mean_squared_error(y_test, prediction)
rmse = mse**.5print(mse)
print(rmse)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

我们从这个基本的随机森林模型中得到的结果总体上并不是很好。鉴于我们数据集的大多数值都在 1000–2000 之间,RMSE 值 515 相当高。展望未来,我们将看到调整是否有助于创建一个性能更好的模型。

在大型数据集上运行随机森林模型时,需要考虑的一个问题是潜在的长训练时间。例如,运行第一个基本模型所需的时间大约是 30 秒,这还不算太糟,但是正如我稍后将演示的,这个时间需求可能会迅速增加。

既然我们已经完成了基本的随机森林回归,我们将寻找一个性能更好的参数选择,并将利用 GridSearchCV sklearn 方法来实现这一点。

## Define Grid 
grid = { 
    'n_estimators': [200,300,400,500],
    'max_features': ['sqrt','log2'],
    'max_depth' : [3,4,5,6,7],
    'random_state' : [18]
}## show start time
print(datetime.now())## Grid Search function
CV_rfr = GridSearchCV(estimator=RandomForestRegressor(), param_grid=grid1, cv= 5)
CV_frf.fit(x_train, y_train)## show end time
print(datetime.now())

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

正如您可能已经注意到的,在上面的代码中,我包含了两个显示当前日期时间的打印语句,这样我们就可以跟踪函数的开始和结束时间来测量运行时间。正如我们在上面的图像中看到的,这个函数花了 2 个多小时来训练/调整,这是一个不小的时间量,也是一个明显更大规模的版本,我们在早期的基本模型中看到了 30 秒。

为了进一步扩展,我们的数据集有大约 380,000 个观察值,这仍然相对较小,特别是与那些用于专业应用或学术研究的观察值相比,这些观察值可能有数百万或数十亿个。在考虑使用哪种模型以及权衡性能与时间时,需要考虑这些时间限制。

通过网格搜索找到的最佳参数在下面的代码部分。使用这些参数,并对相同的数据进行测试,我们发现了以下结果。

{'max_depth': 7,
 'max_features': 'sqrt',
 'n_estimators': 300,
 'random_state': 18}# Create and train model
rf = RandomForestRegressor(n_estimators = 300, max_features = 'sqrt', max_depth = 7, random_state = 18)
rf.fit(x_train, y_train)# Predict on test data
prediction = rf.predict(x_test)# Compute mean squared error
mse = mean_squared_error(y_test, prediction)# Print results
print(mse)
print(mse^.5)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

这个均方误差结果低于我们的基本模型,这很好,但总的来说,我仍然认为这种性能是不够的。均方根误差为 504 意味着每次估算的平均误差比实际租赁价格低 504 美元。这种糟糕的性能可能有几个原因:

  • 不使用某些变量和/或使用不必要的变量
  • 糟糕的 EDA 和数据争论
  • 未能正确说明分类变量或文本变量

在我看来,结果不佳的主要原因可以归结为上面提到的第一点。也就是说,本文的目标不是产生最佳结果,而是演示如何使用 sklearn 将随机森林回归模型和一些背景信息应用到随机森林模型的操作中。就本文的目的而言,我认为我们能够实现我们的目标。

结论:
在本文中,我们展示了随机森林模型背后的一些基础知识,更具体地说,是如何应用 sklearn 的随机森林回归算法。我们指出了随机森林模型的一些好处,以及一些潜在的缺点。

感谢您花时间阅读这篇文章!我希望您喜欢阅读,并了解了更多关于随机森林回归的知识。我将继续撰写文章,更新这里部署的方法,以及其他方法和数据科学相关的主题。

连续测井预测的随机森林回归

原文:https://towardsdatascience.com/random-forest-regression-for-continuous-well-log-prediction-61d3ec1c683a

机器学习|岩石物理学

应用于岩石物理学的流行机器学习算法综述

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

HoliHo 的照片:https://www . pexels . com/photo/pathway-in-trees-between-daytime-1112186/

随机森林是一种非常流行,也相对容易理解的机器学习算法。在我之前关于随机森林的文章中,我们试图从一系列测井测量中预测分类数据(岩性)。在本文中,我们将重点关注连续测量的预测。

回归随机森林简介

在很高的层次上,随机森林本质上是决策树的集合(系综)。决策树理解起来非常简单和直观。我们经常在日常生活中使用它们来做决定,尽管我们可能没有意识到。

如果你想知道随机森林是如何应用于分类问题的,可以看看下面的文章。

在分类的情况下,我们试图使用决策将我们的数据分开,并将它们放入类中。当它们应用于回归时,我们试图做类似的事情,但我们预测的不是一个类,而是一个值。

在树的顶端(根节点),我们首先做出一个决定,拆分我们的数据。这一过程沿树向下继续,直到不能再进行分割,这通常定义为每个末端节点(叶节点)的最小样本数或树的最大深度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

随机森林中的单个决策树示例。图片由作者提供。

随机森林算法由以随机方式构造的多个决策树形成。这包括随机抽样我们的数据,并从我们的数据集中为每棵树随机选择变量/特征。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用测井/岩石物理数据的随机森林回归示例。图片由作者提供。

一旦森林建成,我们将所有预测的平均值作为最终的输出值。与单个决策树相比,这种平均允许我们提高预测的准确性,并且还减少了过度拟合的机会。

现在,我们已经对随机森林回归有了很高的理解,我们可以继续我们的 Python 示例,看看如何实现从测井测量预测声波压缩慢度。

即使您不熟悉测井测量或岩石物理学,所使用的大部分代码也可以应用于任何数据集。

本教程中使用的数据

本教程中使用的数据是 Equinor 在 2018 年发布的 Volve 数据集的子集。数据集的全部细节,包括许可证可以在下面的链接中找到。

https://www.equinor.com/energy/volve-data-sharing

Volve 数据许可证基于 CC BY 4.0 许可证。许可协议的全部细节可以在这里找到:

https://cdn . sanity . io/files/h 61 q 9 gi 9/global/de 6532 f 6134 b 9 a 953 f 6 c 41 BAC 47 a 0 c 055 a 3712d 3 . pdf?equinor-hrs-条款和条件-许可-数据-volve.pdf

导入库和数据

本教程的第一步是导入我们将要使用的库。在这个例子中,我们将结合使用 pandas 来加载和存储我们的数据,以及 matplotlib 来可视化我们的数据。

import pandas as pd
import matplotlib.pyplot as plt

接下来,我们将使用pd.read_csv()从 csv 文件导入数据。在这个函数中,我们将传入我们的文件路径以及我们将使用的列。只要 csv 文件包含每一列的标题,我们就可以使用usecols参数。

df = pd.read_csv('Data/Volve/volve_wells.csv', usecols=['WELL', 'DEPTH', 'RHOB', 'GR', 'NPHI', 'PEF', 'DT'])

检查我们有哪些井

数据成功加载后,我们可以检查一下,看看我们有哪些井。最简单的方法是在包含井名(数据源)的井列上调用.unique()

df['WELL'].unique()

它返回:

array(['15/9-F-11 B', '15/9-F-11 A', '15/9-F-1 B', '15/9-F-1 A'],
      dtype=object)

我们可以看到 Volve 油田有四口井。

创建培训、测试和验证数据集

由于我们使用多口井的测量数据,将我们的数据分成有效子集进行训练、验证和测试的一种方法是留出一口井(盲测试井),用于观察我们的模型在未知数据上的表现,然后使用剩余的井进行训练。

需要注意的一点是,测试和验证数据集的术语可能因文章、网站和视频而异。这里使用的定义说明如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

将数据分为训练、验证和测试的示例。图片由作者提供,来自麦当劳,2021。

**训练数据集:**用于训练模型的数据

**验证数据集:**用于验证模型和调整参数的数据。

**测试数据集:**留出的数据,用于在看不见的数据上测试最终模型。

# Training Wells
training_wells = ['15/9-F-11 B', '15/9-F-11 A', '15/9-F-1 A']# Test Well
test_well = ['15/9-F-1 B']train_val_df = df[df['WELL'].isin(training_wells)].copy()
test_df = df[df['WELL'].isin(test_well)].copy()train_val_df.describe()

这将返回我们的训练数据集:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们的训练数据集的数据帧统计。图片由作者提供。

我们还可以对测试数据集进行同样的操作,以确认数据帧已经创建。

test_df.describe()

这将返回下面的数据帧,其中包含我们的测试数据集所需的曲线。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们的测试数据集的数据帧统计。图片由作者提供。

从数据帧中删除缺失值

我们可以从前面的图像中看到,每列的计数行中有不同的数字。这表明我们缺少值。

从数据帧中移除缺失值是处理它们的一种方法,但是,这样做会减少可用的训练数据量。

从数据集中移除值时应小心谨慎,应进行完整的 EDA 以了解值缺失的原因。可以使用其他更复杂的方法来填充具有更可能和更合理的值的 nan。

train_val_df.dropna(inplace=True)
test_df.dropna(inplace=True)
train_val_df.describe()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实现随机森林回归模型

既然我们已经准备好了数据并移除了缺失值,现在我们可以继续构建随机森林回归模型了。为此,我们需要从 Scikit-learn 库中导入一些模块。

from sklearn.model_selection import train_test_split
from sklearn import metrics
from sklearn.ensemble import RandomForestRegressor

接下来,我们需要将我们的数据分成哪些变量将用于训练模型(X)和我们的目标变量,DT (y)。

X = train_val_df[['RHOB', 'GR', 'NPHI', 'PEF']]
y = train_val_df['DT']

我们接下来调用train_test_split()函数将我们的数据分成训练和验证数据集。

我们传入 X 和 y 变量,以及表示我们想要多大的测试数据集的参数。这是作为十进制值输入的,范围在 0 和 1 之间。在这种情况下,我们使用 0.2,这意味着我们的测试数据集将是原始数据的 20%,我们的训练数据集将是原始数据的 80%。

X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2)

构建随机森林回归模型

为了建立我们的回归模型,我们首先需要创建一个名为regr的变量。这可以是你想要的任何东西,只要有意义。

一旦我们初始化了模型,我们就可以根据数据进行拟合。这本质上是训练过程。如果需要,我们可以更改许多参数,但在本例中,我们将保留默认值。

regr = RandomForestRegressor()
regr.fit(X_train, y_train)

在模型被训练之后,我们可以通过调用.predict()对我们的验证数据进行预测。

y_pred = regr.predict(X_val)

检查预测结果

现在,我们的模型已经被训练并应用于验证数据,我们需要检查我们的模型表现如何。

我们将用到的第一个指标是平均绝对误差,计算方法如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

平均绝对误差公式。图片由作者提供。

metrics.mean_absolute_error(y_val, y_pred)

这将返回值 1.6841。这将告知我们实际测量值(y_val)和预测测量值(y_pred)之间的平均绝对误差,其测量单位与目标特征相同。在这种情况下,美国/英尺。

我们可以利用的另一个指标是均方根误差(RMSE)。这可以通过以下方式计算:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

均方根误差(RMSE)公式。图片由作者提供。

要使用 scikit-learn 计算 RMSE,我们首先需要计算均方误差,然后求它的平方根,这可以通过将 MSE 提高到 0.5 的幂来实现。

mse = metrics.mean_squared_error(y_val, y_pred)
rmse = mse**0.5

这将返回值 3.0664。RMSE 方程为我们提供了预测误差大小的概念。

超越度量标准

像上面这样的简单指标是查看模型表现的好方法,但是您应该总是检查实际数据。

一种方法是使用散点图,x 轴表示验证数据,y 轴表示预测数据。为了帮助观想,我们可以添加一条一对一的关系线。

完成这项工作的代码如下。

plt.scatter(y_val, y_pred)
plt.xlim(40, 140)
plt.ylim(40, 140)
plt.ylabel('Predicted DT')
plt.xlabel('Actual DT')
plt.plot([40,140], [40,140], 'black') #1 to 1 line

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实际测量值与预测测量值的散点图。图片由作者提供。

我们得到的是上面的图,这表明我们有一个相当好的预测。有几个点在 60 到 80 us/ft 之间,预测值高于实际值。

理想情况下,我们应该回到我们的数据或模型,看看我们是否可以改善这种预测。这可能涉及改变输入、检查异常值、处理异常值以及收集更多数据。

测试数据预测(盲测试井)

一旦我们最终确定了我们的模型,我们就可以用我们为盲测留出的数据来测试它了。

首先,我们将创建用于应用模型的特征。

test_well_x = test_df[['RHOB', 'GR', 'NPHI', 'PEF']]

接下来,我们将为预测数据的数据框架分配一个新列。

test_df['TEST_DT'] = regr.predict(test_well_x)

预测完成后,我们可以查看与上面相同的散点图。

plt.scatter(test_df['DT'], test_df['TEST_DT'])
plt.xlim(40, 140)
plt.ylim(40, 140)
plt.ylabel('Predicted DT')
plt.xlabel('Actual DT')
plt.plot([40,140], [40,140], 'black') #1 to 1 line

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实际测量值与预测测量值的散点图。图片由作者提供。

为了更好地了解预测相对于实际测量的效果,我们可以查看一个简单的数据对深度的线图。

plt.figure(figsize=(15, 5))
plt.plot(test_df['DEPTH'], test_df['DT'], label='Actual DT')
plt.plot(test_df['DEPTH'], test_df['TEST_DT'], label='Predicted DT')
plt.xlabel('Depth (m)')
plt.ylabel('DT')
plt.ylim(40, 140)
plt.legend()
plt.grid()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

预测测量值与实际测量值的线图(对数图)。图片由作者提供。

如上所述,这个简单的模型表现得相当好。然而,有几个地区的结果预测不足(在 3170 米和 3200 米之间),也有一些地区我们预测过高(在 3370 米和 3450 米之间)。

摘要

随机森林是一种强大的机器学习算法,可以同样应用于基于分类和回归的问题。不需要对模型进行重大调整,我们就可以得到一个好的结果,尤其是在盲测数据上。随着模型的进一步调整和新数据训练的引入,我们可能能够提高模型的泛化能力。但是,请记住,随着数据集变得越来越大,训练时间也会增加。

在达到最终模型之前,比较几个机器学习模型的结果也是值得的。

感谢阅读。在你走之前,你一定要订阅我的内容,把我的文章放到你的收件箱里。 你可以在这里做!或者,您也可以 注册我的简讯 免费将更多内容直接发送到您的收件箱。

其次,通过注册会员,你可以获得完整的媒介体验,并支持我和其他成千上万的作家。它每个月只花你 5 美元,你可以完全接触到所有令人惊叹的媒体文章,也有机会用你的写作赚钱。如果你用 我的链接报名,你直接用你的一部分费用支持我,不会多花你多少钱。如果你这样做了,非常感谢你的支持!

随机森林漫游——群体的智慧以及为什么它们比决策树更好

原文:https://towardsdatascience.com/random-forests-walkthrough-why-are-they-better-than-decision-trees-22e02a28c6bd

随机森林总是被称为“基于树的”模型的更强大和更稳定的版本。在本帖中,我们将证明为什么将群体的智慧应用到决策树是一个好主意。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由@ skamenar-Unsplash拍摄。com

决策树是非常强大的算法。当你进入数据科学和机器学习领域时,它们可能是你可能学习的第一批非线性算法之一。

决策树可以处理非线性模式,并理解目标和特征之间的一些最疯狂的关系。从这个意义上说,它们比线性模型如线性或逻辑回归有巨大的优势。

虽然它们具有捕捉更复杂的特征和目标关系的巨大能力,但它们也很容易过度拟合。它们是倾向于搜索完美叶节点的贪婪算法的子集。特别是当我们处理高维度(行或特征)时,这可能导致树最终用小样本进行概括,从而使您的算法对您的训练数据来说是完美的。

他们的内部工作方式很难找到纯节点,并且很难建立一个在偏差和方差之间达到最佳平衡的决策树。如何避免这种情况?

大多数情况下,研究人员已经找到了两种解决这个问题的方法:

  • boosting——将多个弱学习者组合并迭代到一个算法中——XGBoost 是最著名的实现之一。
  • 打包——将多个模型聚合成一个整体——例如随机森林(RF)。

RFs 使用多个决策树的输出,将它们合并到一个投票系统中,这有助于缓解他们的一些紧迫问题。打个比方,如果决策树是一个独裁政府,只使用一个“头脑”的意见来做决定,随机森林就是一个民主政府,依靠群众的智慧来执行预测。

这篇文章会让你很好地理解随机森林背后的直觉。通过一些实验,我们将比较它们与单一决策树的性能,并理解为什么它们通常比单一决策树模型有更好的结果。

拟合多个决策树

为了真正理解 RF 的工作原理,让我们做一个实验,在一个数据集上拟合几个决策树。为了便于每个人复制,我们将使用 Kaggle 中可用的 泰坦尼克号 数据框,这是大多数数据科学家都知道的数据集。

对于这个特定的问题,我们希望根据一组特征(如乘客年龄、机票等级、性别等)来预测哪些乘客在泰坦尼克号失事中幸存。— 这是一个二元分类问题,有两个互斥的结果:

  • 乘客生还;
  • 乘客没有生还;

让我们首先拟合 3 个不同的决策树。我们控制和创建完全不同的树的最常见的方法之一是给它们自己的一组超参数。这意味着产生的不同决策树会因用于预测结果的不同分割和变量而有所不同。

除了不同的超参数,我们还将为每棵树选择 80%的原始训练样本,从而在构成的每棵树中引入更多的随机性来源。

为了评估我们不同的决策树,我们将使用 30%的维持率作为纯测试集:

# Reading the titanic train dataset
titanic <- read.csv('./train.csv')# Obtaining the number of rows for training (70%)
size <- ceiling(0.7*nrow(titanic))# Use an indexer to perform train and test split
set.seed(999)
train_index <- sample(
  seq_len(nrow(titanic)), size = size
  )train_df <- titanic[train_index, ]
test_df <- titanic[-train_index, ]

train_df 数据帧包含 624 名乘客,而 test_df 包含 267 名乘客。

我们可以构建三个不同的决策树——由于它们对超参数和训练数据中的微小变化非常敏感,我们可以轻松地构建三个彼此完全不同的决策树,只需对代码进行非常小的更改。

举个例子,让我来拟合 1 号决策树,我将它命名为 oak_tree (用不同的名字来称呼不同的树将有助于我们更好地形象化我们的练习)。下面是我训练它的代码:

library(rpart)set.seed(9990)
oak_tree <- rpart(Survived ~ Fare + Age + Sex + Pclass,
                       data = sample_n(train_df, 600), 
                       method = 'class',
                       control = list(maxdepth = 2,
                                      minsplit=30))

(不要忘记在运行 set.seed 的同时运行训练代码,这样您就可以复制这些结果)

请注意,我们只是使用 4 个特征来预测是否有人在泰坦尼克号失事中幸存——乘客票价、年龄、性别和机票等级。是什么让我的橡树与众不同?我的 oak_tree 是一棵深度只有 2 的超级小树,允许在每个节点上进行至少 30 个例子的分割。如果这个超参数术语让你感到困惑,看看这个

正如我以前说过的,我也给我的橡树增加了更多的辣度— 我只在大约 600 名随机乘客的数据上训练算法,这将模拟不同树之间更多的随机性。

所以我们的橡树看起来像下面的 :

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

决策树 1 —作者图片

在这个树中,用来决定结果的第一个变量(也是唯一的一个)是性别。这一变量似乎具有很高的判别能力,因为如果乘客是男性,他不幸存的可能性更高——约为 82%,而如果乘客不是男性,则为 26%。关于此图如何工作的一个小插图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

决策树图表说明—图片由作者提供

请记住,这种特殊的树是以这种方式构建的,因为我们定义了 600 个客户的特定子样本和一组超参数。

现在让我们训练一个完全不同的树,这次我叫它 pine_tree。

set.seed(9991)
pine_tree <- rpart(Survived ~ Fare + Age + Sex + Pclass,
                      data = sample_n(train_df, 600), 
                      method = ‘class’,
                      control = list(maxdepth = 3, 
                                     minsplit=3, 
                                     minbucket=4, 
                                     cp=0.01))

我们的松树可以比我们的*橡树更深一点。*此外,我们允许我们的树用更少的例子构建节点——无论是在进行分割时还是在构建末端叶节点时。

让我们检查一下结果——注意这个树形图和橡树图的区别:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

决策树 2 —作者图片

注意一些很酷的事情——新的变量开始发挥作用。虽然性别仍然被用作第一分割点,但从那时起,票价、阶级和年龄就占据了舞台,并决定了我们的大部分概率。

有趣的流程如下所示:

  • 支付超过 16 美元且不到 2 岁的儿童有 86%的存活概率。
  • 上流社会的女性生存的可能性最高(95%)。

我们现在已经建立了两个不同的树!请注意,它们显示了完全不同的结果,尽管我们的松树似乎是我们的橡树的自然延伸。也预期松树橡树有更多的过度拟合,仅仅是因为超参数的设置。

最后再来拟合另一棵树, elm_tree。

Elm_treepine_tree 相似,但有一点不同——我们只允许树的最大深度为 2。

set.seed(9992)
elm_tree <- rpart(Survived ~ Fare + Age + Sex + Pclass,
                data = sample_n(train_df, 600), 
                method = 'class',
                control = list(maxdepth = 2, 
                               minsplit=2, 
                               minbucket=4, 
                               cp=0.01))

榆树 _ 树看起来如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

决策树 3 —作者图片

请注意,性别仍然是定义第一次拆分的变量。接下来就是上课了。

这三棵树看起来很相似,只是深度不同。当然,我们现在需要评估它们!它们在性能上有多大的不同?

评估每棵树的性能

为了快速进行性能评估,并且不受限于任何阈值,让我们使用 AUC 作为评估模型的指标。

更高的 AUC 意味着我们的模型更善于区分 0 类和 1 类,使我们的模型在预测中更有效和正确。

我正在使用 ROCR 库来更快地获得 AUC:

library(ROCR)obtainauc <- function(model) {
  predictions <- predict(model, test_df)[,2]
  pred <- prediction(predictions, test_df$Survived)
  perf <- performance(pred, measure = 'auc')
  return (perf@y.values[[1]])
}

我们的采油树表现如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

树与树之间 AUC 的比较—图片由作者提供

从上面的图来看:

  • 橡树的 AUC 为 0.778
  • 松树的 AUC 为 0.832
  • 榆树的 AUC 为 0.807

松树是表现最好的树。这是意料之中的,因为这也是包含最佳细节的树。每当我们提高最大深度时,我们可能会在训练集上有更好的性能——当我们在测试集上评估时,可能会有所不同。

因此,随机森林集中在一个核心问题中:“即使我的松树是最好的,我是否应该完全放弃橡树榆树的观点?”

让我们建立另一个实验——我们将做一个多数投票选项——平均所有树的结果,并将这个意见“集合”与每棵树单独进行比较。由于我们正在处理概率结果,我们将只是平均 3 个不同树之间的概率,并认为这是我们的组合投票。

平均结果

为了更容易理解“平均结果”的真正含义,我们来做一点角色扮演。

假设我询问橡树、松树和榆树对下面这个人的看法:

  • 一名男性花了不到 16 美元登上泰坦尼克号,他会幸免于难吗?

橡树说这位乘客有 18%的可能性会活下来。松树说只有 10%的机会活下来。榆树也说这个人有 18%的几率活下来。

如果我们对每棵树的推荐进行平均,我们将得到 (18%+18%+10%)/3 ,这将产生 15.3%使用投票平均值,这应该是我们最终的乘客存活概率。

让我们看看另一位乘客:

  • 乘坐头等舱或二等舱的女乘客。

橡树说这位乘客有 74%的可能性会活下来。松树和榆树都显示有 95%的可能性。

如果我们平均这些建议,我们得到 88%的生存概率。

主要问题是——你认为对测试集中的所有乘客使用这种基本原理会提高我们的性能吗?还是由于他们更糟糕的表现,我们的橡树榆树的意见只会拖累我们的成绩?

让我们看看!

群众的智慧

这里有一小段代码,我基于橡树、松树榆树构建了这个集合模型。

ensemble <- (
  predict(oak_tree, test_df)[,2]
  +
  predict(pine_tree, test_df)[,2]
  +
  predict(elm_tree, test_df)[,2]
)/3

这个简单的系综模型表现如何?让我们再次检查使用 ROCR 图书馆:

# Ensemble Performance
prediction <- prediction(ensemble, test_df$Survived)
perf <- performance(prediction, measure = 'auc')
performance_ensemble <- perf@y.values[[1]]

我们有以下 AUC 结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

树木和系综的 AUC 比较——作者提供的图像

哇!尽管我们的橡树榆树比我们的松树更糟糕,但在做预测时考虑他们的意见是有价值的。**请注意,我们的集合模型的 AUC 略好于最佳个体树。**结论是,即使通过将该树与“较弱”的树相结合,我们也能够提升性能。

这正是随机森林背后的基本原理!每次你适应一个随机的森林,你就适应了无数的树,当与一个单独的决策树比较时,即使是较弱的树也会有利于你的整体性能。

当你增加被训练的树的数量时——有时增加到几千棵树——这种行为就更加强大了。当然,当你增加训练的树的数量时,你提高了你的“集合”比组成相同“集合”的单个决策树更好的可能性。

就是这样!感谢你花时间阅读这篇文章,希望你喜欢。我的目标是解释为什么随机森林通常比单一决策树更好,以及为什么它们是您的机器学习项目中值得考虑的优秀算法。这个例子应该能让你很好地理解 RF 背后的“群体智慧”假设。

我在 Udemy 上开设了一门关于学习数据科学的课程——这门课程是为初学者设计的,包含 50 多个练习,我希望你能在我身边!您将学习基于树的模型、回归以及如何从头到尾构建数据科学项目。**

这里有一个小的要点和贯穿这篇文章的代码:

数据集许可:本文中使用的数据集可以在https://www.openml.org/d/40945公开使用

*https://ivopbernardo.medium.com/membership *

Python 中的随机投影

原文:https://towardsdatascience.com/random-projection-in-python-705883a19e48

降维

Python 中的随机投影

在这篇文章中,我简要介绍了降维工具——随机投影的概念,以及它在 Python 中的实现。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Gabriella Clare Marino 在 Unsplash 上拍摄的照片

D 维度缩减通常是处理大数据时必须做的预处理。最广泛使用的方法之一是主成分分析 ( PCA ),但是 PCA 的主要缺点是对于高维数据来说计算量非常大。一种替代方法是随机投影,这是一种计算成本较低的降维工具。

在这篇文章中,我将简要描述一下随机投影的思想及其在 Python 中的实现。

TLDR

1.随机投影适用于高维数据处理。

2.随机投影的核心思想在 Johnson-Lindenstrauss 引理中给出。它基本上说明了高维空间中的数据可以被投影到低得多的维空间中,而几乎没有距离失真。

3.随机投影中的投影矩阵可以使用高斯分布生成,这被称为高斯随机投影;或者稀疏矩阵,称为稀疏随机投影。

4.随机预测可用作管道中的早期步骤之一,以更好地理解数据。

5.X_new = sklearn.random_projection。GaussianRandomProjection(n _ components = ’ auto ',eps = 0.05)。拟合 _ 转换(X)

随机投影的概念

随机投影是一种降维工具。“投影表示该技术将数据从高维空间投影到低维空间,“随机表示投影矩阵是随机生成的。直截了当,对吧?

那么,投影是怎么做的?通过投影矩阵的线性变换。

具体来说,假设我们有一个带有 d 行(特征)和 N 列(样本)的原始数据集 X ,我们希望将特征维数从 d 减少到k(d>>k)。随机投影使用随机生成的具有 k 行和 d 列的投影矩阵 R 来获得变换后的新数据集 X_new

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从 k 维到 d 维的投影(图片由作者提供)

随机投影不会改变样本之间的成对相似性(或距离),这得到了约翰逊-林登斯特劳斯引理的支持。它基本上说明了高维空间中的数据可以被投影到低得多的维空间中,而几乎没有距离失真。

如果我们用数学语言来描述这个引理,它是这样的,

给定 0 < ε < 1 ,一个特征中 d 维的数据集 XN 个数据点k>8ln(N)/ε,从 d 有一个线性映射(投影) f

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

约翰逊-林登施特劳斯引理方程(图片由作者提供)

其中 uv 均来自原始特征空间, f(u)f(v) 来自变换后的特征空间。

基于上面的描述,我们可以看到两件重要的事情。首先,投影并没有完全保持从尺寸 d 到尺寸 k 的成对距离,而是带有一个误差参数 ε 。第二,对于任何固定的 ε 和样本大小 N ,对于成对距离失真的“可接受”水平,存在最小的最终变换尺寸 k 。如果我们仍然想更努力地减少尺寸 k ,我们可能需要通过接受更大的 ε 来失去失真的容差。

因此,在样本大小固定的情况下,在成对距离的失真 ε 和最终特征空间的最小维度 k 之间存在权衡。

生成投影矩阵的两种方法

生成投影矩阵 R 的一种方法是让 {r_ij} 遵循正态分布。而另一种方式是使用稀疏随机矩阵作为 R 。“稀疏”是指 {r_ij} 的大部分为零。典型使用的分布是

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

稀疏投影矩阵的分布。

稀疏随机投影比高斯随机投影的计算量小,主要是因为两个原因。第一,上面的公式只涉及整数运算;第二,稀疏投影矩阵几乎没有非零值(更稀疏)。

实现随机投影的 Python 代码

随机投影作为一种降维工具,可以作为数据分析的前期步骤之一。基于约翰逊-林登斯特劳斯引理,我们可以研究数据集的结构,并在一个低得多的维度上可视化数据。

下面是 Python 中实现高斯稀疏随机投影的代码,

# Gaussian Random Projection
**from** **sklearn.random_projection** **import** GaussianRandomProjection

projector = GaussianRandomProjection(n_components='auto',eps=0.05)
X_new = projector.fit_transform(X) 

其中 X 是我的原始数据, n_components 是目标空间的维数( auto 表示根据参数自动调整, ε ), epsJohnson-Lindenstrauss 引理中的 εX _ 1

稀疏随机投影也是如此,

# Sparse Random Projection
**from** **sklearn.random_projection** **import** SparseRandomProjection

projector = SparseRandomProjection(n_components='auto',density = 'auto', eps=0.05)
X_new = projector.fit_transform(X)

其中密度是随机投影矩阵中的非零分量密度。

就是这样!希望文章有帮助。

如果你喜欢读这篇文章,这里有一些其他的片段,

https://towards data science . com/Gaussian-mixture-models-with-python-36 dabed 6212 a

https://towards data science . com/fuzzy-c-means-clustering-with-python-f 4908 c 714081

https://towards data science . com/regression-splines-in-r-and-python-cfba 3 e 628 BCD

参考资料:

https://en.wikipedia.org/wiki/Random_projection https://scikit-learn.org/stable/modules/generated/sklearn.random_projection.GaussianRandomProjection.html https://scikit-learn.org/stable/modules/generated/sklearn.random_projection.SparseRandomProjection.html#sklearn.random_projection.SparseRandomProjection

用 SciPy 和 NumPy 进行随机抽样:第一部分

原文:https://towardsdatascience.com/random-sampling-using-scipy-and-numpy-part-i-f3ce8c78812e

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Unsplash 上的 Edge2Edge 媒体拍摄

介绍采样、编写我们自己的程序、速度测试

能够从你选择的分布中随机抽取样本是非常有用的。它是任何一种随机过程模拟的基础,无论是粒子扩散、股票价格运动,还是模拟任何显示某种时间随机性的现象。

因此,获得准确高效的采样过程非常重要。一旦我们接触到像正态分布这样到处都在使用的分布,这种重要性只会增加。

下面是对 SciPy 和 NumPy 如何为我们打包以使大规模采样非常快速和易于使用的“深入探究”。任何有一点使用 SciPy 历史的人都会告诉你原因如下:

  • 它是由一些非常聪明的人写的
  • 这是非常优化的代码
  • 它使用 C 语言编写的底层数值例程

这都是真的。这里的目的是更深入地了解这是如何发生的,以及为什么聪明人可以用一些聪明的算法让事情变得更快。

我为什么要关心“引擎盖下”发生了什么?

因为 SciPy 只能让我们到此为止,尽管它提供的发行范围相当令人难以置信。当我们想从“自定义分布”中取样时,问题就出现了。如果不是从给定的参数化正态或指数分布中采样,而是从我们自己的分布中开始采样,会怎么样?也许是因为这种分布更好地代表了我们试图拟合的数据,我们希望利用蒙特卡罗过程进行一些测试?

在这些情况下,正如我们将在下面看到的,理解它的工作原理是有好处的,因为:

  • 编写自己天真的采样机制可能会慢得令人难以置信
  • 理解它的工作原理可以让我们在 SciPy 框架中编写自己的定制发行版

采样是如何工作的?

换句话说:给定一个密度函数(pdf),我如何使用它来绘制随机样本,如果我绘制它们,它们将形成与 pdf 相同形状的直方图?为了给这个陈述增加一点视觉效果,让我们用一个正态分布的例子。使用 SciPy,让我们绘制 pdf,然后生成一系列随机样本,然后再深入了解:

  • 它是如何产生这些样本的
  • 它怎么做得这么快

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

因此,蓝色线显示我们绘制的 pdf,橙色直方图显示我们从同一分布中提取的1,000,000样本的直方图。这是采样——给定一条指定的蓝线(无论它可能采取什么形状),我们如何定义一个过程(最好是快速准确的),可以生成形成与蓝线一致的直方图的数字。

要回答最初的问题我们如何做到这一点,答案是:视情况而定。有许多方法可以做到这一点,每种方法都有优点和缺点。我们会发现其中一些方法比其他方法快得多。首先,我们将关注一种特定的方法,这种方法是通用的,我们将使用它作为比较 SciPy 速度的基线。如果我们没有可以与之比较的东西,我们就不能称之为 SciPy fast。

逆变换采样(ITS)

这里有一些想法,在编写一些基本代码来说明和形成我们的速度基准之前,我们将尝试将其浓缩成几个简短的段落。首先,我们将解决以下问题— 生成随机数需要某种随机数生成器

不管我们想从哪个分布中得到它们,我们都需要某种潜在的随机过程。在计算机的情况下,这个过程不可能是真正随机的,因为它需要能够被编程到机器中,但是它们可以是“伪”随机的。

换句话说,如果我们不知道生成这些数字的基本过程,那么它们对我们来说可能是随机的,即使它们对生成过程来说不是随机的。借用纳西姆·塔勒布的话,圣诞节那天对火鸡随机的东西对农民来说并不随机——这完全取决于你的信息集。这种过程被称为伪随机数发生器(PRNG ),有许多竞争对手提供。

让我们想当然地认为,我们有这样一个 PRNG,它产生这些随机数,这些随机数来自均匀分布。如果你对这些数字究竟是如何产生的感到好奇,那么我已经在这里写了一个解释器,但是对于这篇文章来说,说这样一个过程存在就足够了。

。事实证明,如果我们:

  • 从连续的概率分布中抽取一批数字
  • 获取所有这些样本的 cdf 值

**…这些 cdf 值的分布将是均匀分布的。**这里有一个很棒的 gif 展示了标准正态分布的过程。但是对我们来说更有用的是反过来——或者说。如果我们从一堆均匀分布的随机数开始(我们的 PRNG 会给我们),那么我们可以在“逆”cdf 上发射它们,并获得一堆遵循我们想要的分布的数。这就是逆变换采样。

来自https://gfycat.com/unfitflatflounder的 GIF

我们能把这个编码吗?

绝对的。写下来并创建我们自己的正态分布随机抽样器有两个目的:

  • 提供一个模拟上述理论解释的代码
  • 为 SciPy 实现创建一个纯 python 比较来检查速度

首先让我们定义我们的 pdf。我们不会给 SciPy 太多的优势,所以为了保持合理的速度,我们将:

  • 利用 NumPy 进行矢量化计算
  • sqrt(2 * pi)编码为浮点数,以保存乘法和 sqrt 操作
# define std normal pdf
def norm_p(x):
    return np.exp(-0.5 * x**2) / 2.5066282746310002

接下来,我们需要创建 cdf,这样我们就可以反转它。为此,我们将:

  • 定义一个数值范围,并计算每个数值的 pdf
  • 归一化 pdf 值,这样我们就有了一个密度函数,即 pdf 下的面积为1
  • 使用“累积分布函数”中的“累积”来创建 cdf:我们只需对这些 pdf 值应用累积和来创建我们的 cdf

下面的代码实现了这一点,为了更好地衡量,我们将绘制创建的 pdf 和 cdf 以供检查。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

所以看起来在意料之中。现在我们需要获取生成的 CDF——此时它只是一组 x 值的累积概率值,并将其转换为一个函数。特别是我们想把它变成反函数。幸运的是,我们可以依靠 SciPy 并使用插值函数interp1d:

# generate the inverse cdf
func_ppf = interp1d(my_cdf, xs, fill_value='extrapolate')

我们称之为“PPF”——百分点函数,因为这与 SciPy 术语一致,但这正是我们想要实现的——反向 cdf 函数。现在我们有了这个函数,我们可以用它来:

  • 首先:向它发射均匀分布的随机数,从标准正态分布中产生样本
  • 第二:比较它与内置的 SciPy 采样相比有多快

分配检查

首先,让我们仔细检查一下,以确保我们是根据正确的分布来生成数字的——换句话说,我没有将一个错误集中到上面几行代码中。如前所述,现在我们有了反向 cdf,我们只需要向它发射随机均匀分布的数。我们如何获得那些均匀分布的随机数?

让我们利用 NumPy 中的随机数生成器:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

因此,我们可以确信我们创建的函数确实从标准正态分布中抽取了随机样本——我们的橙色直方图与 SciPy 生成的代表 pdf 的蓝色线条对齐。

速度

现在进入主要问题——我们生成的函数与 SciPy 相比如何?

%timeit func_ppf(np.random.uniform(size=n))2.32 s ± 264 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)%timeit snorm.rvs(size=n)56.3 ms ± 1.08 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

因此,即使我们尽了最大努力来创建一个正态分布采样器的有效实现,我们仍然比做同样事情的 SciPy 慢。这就引出了下一个问题:为什么?

概述

在深入研究 SciPy 和 NumPy 代码库以弄清楚为什么我们在速度方面仍然落后之前,让我们简单回顾一下我们已经建立的东西:

  • 抽样是抽取随机数的过程,这些随机数作为一个集合遵守给定的 pdf
  • 有许多方法可以实现这种采样,其中一种方法称为逆变换采样
  • 它依赖于在插入均匀分布的随机数之前对给定分布的 cdf 求逆
  • 即使有相当有效的自我实现,我们也比 SciPy 慢大约40x

记住这一点,让我们继续第二部分,开始挖掘 SciPy 和 NumPy 代码库。

[## 用 SciPy 和 NumPy 随机抽样第二部分

towardsdatascience.com](/random-sampling-with-scipy-and-numpy-part-ii-234c2385828a)

使用 SciPy 和 NumPy 的随机抽样:第二部分

原文:https://towardsdatascience.com/random-sampling-with-scipy-and-numpy-part-ii-234c2385828a

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由андрейсизовUnsplash 上拍摄

奇特的算法,源代码演练和潜在的改进

在第一部分中,我们讲述了逆变换采样(ITS)的基础知识,并创建了我们自己的 ITS 纯 python 实现来从标准正态分布中采样数字。然后,我们比较了我们稍微优化的功能和内置的 SciPy 功能的速度,发现我们有些欠缺——慢了40x倍。

在这一部分中,我们的目的是通过挖掘 SciPy 和 NumPy 代码库的相关部分来解释为什么会出现这种情况,看看这些速度改进在哪里表现出来。总的来说,我们会发现它由以下几个部分组成:

  • 由于是用 Cython 或直接用 C 编写的,所以函数速度更快
  • 与我们屡试不爽的逆变换采样相比,更新的采样算法速度更快

我们如何在 SciPy 中生成正态分布的随机样本?

下面是从标准正态分布生成随机数1,000,000的代码。

43.5 ms ± 1.2 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

所以函数rvs在刚刚超过40ms的时间内生成1,000,000个样本。为了进行比较,我们使用基于逆变换采样原理的算法在2.3s中平均实现了这一点。为了理解速度差异,我们将不得不深入到那个rvs方法中。

值得注意的是,*(一般而言)*使用 SciPy,逻辑的核心包含在下划线方法中——所以当我们想要查看rvs时,我们真的想要查看_rvs的代码。非下划线方法通常在传递给下划线方法之前执行一些参数类型检查或默认设置。

在我们开始之前,让我们先简要概述一下 SciPy 在库中组织分发功能的方式。

RV _ 一般和 RV _ 连续

SciPy 发行版是从一个简洁的继承结构中创建的,它具有:

  • rv_generic作为顶层类,提供get_supportmean等方法
  • rv_continuousrv_discrete用更具体的方法继承它

所以在上面的例子中,我们将我们的正态分布类snorm初始化为stats.norm(),这实际上是创建了一个rv_continuous的实例,它继承了rv_generic的很多功能。更具体地说,我们实际上创建了一个rv_frozen实例,它是rv_continuous的一个版本,但是分布的参数是固定的(例如,平均值和方差)。记住这一点,现在让我们看看rvs方法的内部。

房车

当我们在snorm.dist._rvs上运行??魔术时,我们看到下面的代码片段:

# ?? snorm.dist._rvs
def _rvs(self, size=None, random_state=None):
    return random_state.standard_normal(size)

因此,似乎在我们创建的 distribution 类中的某个地方,我们已经在某个地方分配了一个random_state对象,该random_state对象包含一个方法,该方法可以返回根据标准正态分布分布的数字。

原来,吐出这些随机数的 **random_state** **物体其实来自 NumPy。**我们通过查看 rv_generic 的源代码来了解这一点,它在其__init__方法中包含对一个名为 check_random_state 的 SciPy util 方法的调用,如果还没有传递种子,该方法将把random_state设置为np.random.RandomState的实例。下面是这段代码:

交给 NumPy

因此,似乎提供如此高速采样的“魔法”实际上存在于 NumPy 而非 SciPy 中。这不应该太令人震惊,因为 SciPy 是故意构建在 NumPy 之上的,以防止两个库可能提供相同特性的重复和不一致。这在 SciPy 简介文档的第一行中有明确说明,这里是:

SciPy 是建立在 Python 的 NumPy 扩展之上的数学算法和便利函数的集合

要了解这是怎么回事,我们可以看看这里的np.random.RandomState类。从使用中我们可以看出:

  • cdef代替def进行函数声明
  • 一个.pyx文件扩展名代替。巴拉圭

这两者都表明该函数是使用 Cython 编写的——这是一种非常类似于 python 的语言,允许以几乎 Python 的语法编写函数,然后编译成优化的 C/C++代码以提高效率。正如他们自己在文档中所说的:

“源代码被翻译成优化的 C/C++代码,并被编译成 Python 扩展模块。这允许非常快速的程序执行和与外部 C 库的紧密集成,同时保持 Python 语言众所周知的高程序员生产率。”

在本课程中,我们需要了解两件事来理解采样过程:

  • 它是如何生成均匀分布的随机数(PRNG)的
  • 它使用什么算法将这些均匀分布的数字转换成正态分布的数字

PRNG

正如在第一部分中提到的,生成随机样本需要某种形式的随机性。几乎总是这不是真正的随机,而是由“伪随机数发生器”(PRNG)产生的一系列数字。正如采样算法一样,有多种 PRNGs 可用,这里使用的具体实现在np.random.RandomState[__init__](https://github.com/numpy/numpy/blob/b991d0992a56272531e18613cc26b0ba085459ef/numpy/random/mtrand.pyx#L180) 方法中详细介绍:

如上所示,当该类被初始化时,默认的 PRNG 被设置为梅森扭结算法的一个实现——如此命名是因为它的周期长度为梅森素数(它在开始重复自身之前可以生成的随机数的数量)。

取样过程

沿着类np.random.RandomState的代码往下,我们看到[standard_normal](https://github.com/numpy/numpy/blob/b991d0992a56272531e18613cc26b0ba085459ef/numpy/random/mtrand.pyx#L1344)的定义调用了一个叫做legacy_gauss的东西。legacy_gauss函数的 C 代码在这里是,为了便于查看,我们将在这里显示它:

正如在 Wiki 上的实现部分中所看到的,这正是 Marsaglia 极坐标方法的 C 实现,用于在给定一串均匀分布的输入数的情况下,从正态分布中生成随机样本。

概述

我们已经经历了很多,所以有必要回顾一下,确保一切都非常清楚。我们已经从:

  • 一个用 python 写的名为_rvs的函数启动
  • 一个 NumPy 类np.random.RandomState,用 Cython 写的,它
  • 使用 Mersenne Twister 算法生成均匀分布的数字,然后
  • 将这些数字输入用 C 编写的函数legacy_gauss,该函数使用 Marsaglia 极坐标法生成正态分布的样本

以上强调了构建 SciPy 和 NumPy 的聪明人为了生成高效代码所付出的努力。在基础设施的更深层尽可能接近 C 语言(为了速度)之前,我们有一个可以被用户(比如你和我)调用的顶层,它是用 python 编写的(为了 python 的“程序员生产力”)。

为什么 SciPy 调用 NumPy 函数被视为“遗留”?

因为采样是数学/计算机科学的一个分支,仍然在向前发展。与其他领域不同,在这些领域中,某些原则在几个世纪前就已达成一致,并且从那以后没有发生变化,有效地对各种分布进行采样仍然有新的发展。随着新的开发得到测试,我们希望更新我们的默认流程,以纳入这些进步。

这正是 2019 年 7 月 NumPy 1.17.0 所发生的事情,当时他们引入了 2 个影响采样的新功能:

然而,由于对 prng 向后兼容性的期望,他们没有创建突破性的改变,而是引入了一种新的方式来启动 prng,并将旧的方式切换到引用“遗留”代码。

这里提到的向后兼容性是指在给定相同种子的情况下,希望 PRNG 函数生成相同的随机数字符串。两种不同的算法不会产生相同的随机数,即使它们被给予相同的种子。这种再现性对于测试尤其重要。

看来 SciPy 还没有升级来利用这些新的发展。

我们能打败西皮吗?

鉴于我们现在所知道的关于在 SciPy 中如何实现正态分布抽样的知识,我们能战胜它吗?

答案是肯定的——通过利用 NumPy 为我们实现的最新采样技术。下面是采样的一个实现,其中我们:

  • 使用最新的 PRNG
  • 使用新的金字形神塔算法将这些数字转换成正态分布的样本
# test scipy speed
%timeit snorm.rvs(size=n)51 ms ± 5.08 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)# test numpy speed
%timeit nnorm.normal(size=n)24.3 ms ± 1.84 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

所以看起来我们现在已经和 SciPy 一样快了 NumPy 在他们的发布中强调了这是预计的 2-10 倍。

结论:这个有多大用处?

说到实现定制分布抽样:非常有用。我们现在完全理解了追求 SciPy 式采样速度的决定,并且可以适当地实现定制分布采样。我们可以:

  • 坚持使用第一部分中的纯 python 逆采样转换实现(毕竟,在大多数上下文中,2s对于1,000,000的示例来说并不坏)
  • 编写我们自己的采样程序——最好用 C 或 Cython 编写这个采样程序——这不是一个小问题

在下一部分中,我们将研究如何做到这一点——在 SciPy 基础结构中实现一个高效的定制分布采样函数。这给了我们两全其美的东西——既可以灵活地实现我们选择的精确分布,又可以利用我们从rv_genericrv_continuous SciPy 类继承的高效且编写良好的方法。

使用 SciPy 和 NumPy 的随机抽样:第三部分

原文:https://towardsdatascience.com/random-sampling-with-scipy-and-numpy-part-iii-8daa212ce554

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

米克·豪普特Unsplash 上拍摄

在 SciPy 中实现自定义分布采样

在前两部分(第一部分第二部分)中,我们快速介绍了采样需要什么,然后深入研究了 NumPy 和 SciPy 的源代码,以准确理解这在现代 python 库中是如何实现的。

当我们通常不关心底层过程时,所有这些看起来都是多余的——我们很高兴智能体操由巧妙构建的导入库来处理,并让我们专注于利用输出来完成更有洞察力的任务(如蒙特卡洛模拟)。

然而,有时我们会遇到标准库不太适合的用例。下面是一个这样的例子,提供了一个理解这两者的例子:

  • 采样(尤其是逆变换采样)背后的理论思想
  • 这种采样过程的程序实现

可以在一个不可行的缓慢实现和一个几乎可以与 NumPy 和 SciPy 背后的聪明人的优化工作相媲美的实现之间做出区别。

为什么我需要从自定义分布中取样?

在很多场合。而在正常情况下(请原谅这个双关语),默认的做法是:

  • 收集数据并绘制直方图
  • 得出数据大致正常的结论(即呈钟形)
  • 拟合一个合适的正态分布并完成它

在有些情况下,这并不能完全解决问题。下面是我的特例,但可以推广到任何一个预先打包的发行版,不管参数化得多好,都不够好。

“保方差尾肥”

这听起来比实际上要美好得多。几何布朗运动(GBM) 由以下方程定义:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

简而言之,在某个时间增量dt内,我们的过程值S的变化由两部分组成:

  • 一些常数确定性漂移项
  • 一些随机项

重要的是,GBM 的特点是随机元素呈正态分布——dW元素被称为维纳过程或更常见的“随机游走”。我的问题如下——我想模拟这些“随机路径”的负载,但我不想从正态分布中取样我的随机变化。

相反,我想创建自己的“定制”分布——特别是我想要一个方差保持变换(即方差仍然是标准正态的1),但该分布有着丰富的尾部(过度峰度)。那么问题就变成了:

我如何在 SciPy/NumPy 框架中做到这一点?

在开始之前,让我们先快速了解一下以下 pdf 之间的区别:

  • 标准正态分布
  • 我希望从中取样的分布

为了创建我想要的厚尾分布,我将利用詹森不等式。具体来说,我将利用这样一个事实,即如果我们取两个平均方差为1的正态分布的平均密度,那么我们将得到一个“身材更高”和“尾部更胖”的分布,即峰度更大,但仍保持像标准正态分布一样的方差1

更清楚地说,我们的厚尾分布将是通过平均得到的 pdf:

  • 有方差的正态分布1 + e
  • 方差为正态分布1 - e

其中e被限制在[0,1]之间。让我们画出这个图,这样我们就可以看到当我们改变e与标准正态分布相比时,这些分布是什么样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

如上所述,我们的平均分布显示了更高的身体和更胖的尾巴。特别是,我们看到所有的定制分布在几乎相同的点上与标准正态分布交叉——肩部大约为+/-0.66,尾部大约为+/-2.13。事实上,我们可以用这个特性来定义当我们谈论“尾部”时,我们实际上指的是概率分布的哪一部分,而不仅仅是一个抽象的概念。

更专业地说,尾部是由凸方差概率分布的密度定义的。简单来说,一旦我们改变方差(而不是一个常数1),在上面的分布中+/-2.13 之外的密度增加了。

这与通常流传的关于厚尾分布如何与峰度相关的观点非常吻合——因为峰度只是四阶矩,或者换句话说,是方差的方差。改变方差(通过我们上面的分布平均),我们开始得到厚尾的 pdf。

现在我们知道了我们要实现的发行版,让我们来实现它。

尝试 1:简单,幼稚但缓慢(真的很慢)

尽管这种方法太慢了,根本不能成为真正的解决方案,但 SciPy 内置了所有这些功能,这还是很了不起的。方法是这样的:

  • 创建一个从rv_continuous SciPy 类继承的定制分发类
  • 只需定义反映我们所追求的定制发行版 pdf 的_pdf方法
  • SciPy 做了所有其他事情来允许我们从这个定制发行版中进行采样

要快速回顾一下什么是rv_continuous类或者 SciPy 的分布结构,那么最好跳回第二部分快速回顾一下rv_continuous是如何继承rv_generic的。否则,让我们继续写我们天真的实现,naive_cust_dist:

现在我们已经定义了它,让我们做两件事:

  • 从中抽取样本,检查我们从这些样本中生成的直方图是否与我们定义的 pdf 一致
  • 快速测试我们抽取这些样本并与 SciPy 的嵌入式正态分布采样进行比较需要多长时间

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

上面显示了从我们的自定义类naive_cust_dist中采样1,000数字得到的直方图,该自定义类实现了我们想要从中采样的 pdf。正如我们所见,直方图很大程度上与理论 pdf(绿线)一致,这与标绘的标准正态 pdf 明显不同。

所以理论上就是这样。我们已经实现了自定义采样,只定义了_pdf方法,其余的由 SciPy 完成。

“其余的”是什么?

正如在第一部分中提到的,有许多不同的方法从一个分布中抽取样本。然而,它们都没有单独将 pdf 作为输入,并使用它来生成随机数,如果我们将它们绘制成直方图(如上),我们将恢复相同的 pdf。相反,我们需要:

  • 巧妙的算法(例如,像金字形神算法这样的拒绝采样算法)
  • 利用逆变换采样的逆累积分布函数(cdf)

算法可以是特定于发行版的,当实现定制发行版时,SciPy 使用逆变换采样,这需要后者:逆 cdf。因此,“其余”包括:

  • 根据给定的_pdf计算_cdf
  • 从 cdf 计算【SciPy 给逆向 cdf 起的名字——“百分点函数”)
  • 使用该_ppf函数通过使用 PRNG 生成均匀分布的随机数并使用逆变换采样来产生样本

问题如下:

做“其余的”是极其缓慢的

下面显示了速度有多慢——从我们的定制发行版中计算一个仅仅是1,000的样本就需要花费超过30s的时间。

%timeit naive_dist.rvs(size=n)32.6 s ± 4.57 s per loop (mean ± std. dev. of 7 runs, 1 loop each)

我们可以将其与使用 SciPy 从正态分布中采样的1,000数字进行比较。

%timeit stats.norm.rvs(size=n)168 µs ± 29.1 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

所以要明确的是,我们几乎比内置的 SciPy 采样方法慢200,000x。然而,有问题的不仅仅是速度有多慢的相对性质——从绝对意义上来说,仅用30s来采样1,000数字是不可行的,特别是如果我们想要做一些类似于采样10,000路径的事情,其中每条路径都由1,000步骤组成。我们不能每次想运行模拟都等3.5天。

为什么这么慢?

在使用我们从第一部分和第二部分中学到的知识来创建一个更好的解决方案之前,让我们快速找出为什么内置的 SciPy 功能如此之慢。要回答这个问题,我们需要深入研究 SciPy 源代码,找到_ppf方法——特别是它调用的_ppf_single方法(像 NumPy 这样命名 SciPy 是为了便于快速计算数字集合)。下面是代码片段,但是如果你喜欢自己阅读 Github 源代码,你可以点击这里:

换句话说,以上是逆变换采样的实现,其利用求根算法( Brent 的方法)来将给定随机数q从均匀分布转换成来自我们选择的定制分布的数。即使不深入算法的本质,这个过程也需要:

  • 两个while循环
  • 数值积分函数

应该表明这种功能的速度可能不足。相反,我们想避开这个过程,实现我们自己的_ppf。换句话说,让我们把在第一部分中创建的逆向 cdf 放到 SciPy 发行框架中。

尝试 2:可行的解决方案

所以我们将返回到创建我们自己的分发类,它继承了 SciPy 中的通用rv_continuous类。然而,除了定义一个_pdf方法,我们还要定义一个_ppf方法。

怎么会?我们将实现第一部分中的反变换采样方法。这意味着当我们实例化我们的类时,我们需要做更多的工作。更具体地说,我们需要通过以下方式创建ppf:

  • 首先通过对pdf进行累积求和来创建cdf
  • 使用 SciPy 的内置interp1d函数来反转它,最后得到我们的反转cdf

一旦我们有了它,我们就可以将它设置为我们的_ppf方法,以防止 SciPy 从方法 1 开始进行数学体操,这使得它慢得不可行。

现在我们已经定义了它,让我们试一试。就像之前一样,让我们检查它是否生成了与我们定义的pdf一致的样本,然后我们可以检查我们是否得到了速度方面的可行解决方案。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

因此,我们似乎又一次实现了期望的采样分布。现在让我们来看看额外的代码和复杂性在加速方面是否值得。

%timeit my_dist.rvs(size=1000)1.84 ms ± 512 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)%timeit stats.norm.rvs(size=1000)116 µs ± 5.21 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)%timeit my_dist.rvs(size=10000000)12.8 s ± 1.62 s per loop (mean ± std. dev. of 7 runs, 1 loop each)

因此,我们仍然比 SciPy 慢得多(大约在15x),但这比我们以前的200,00x方法有了巨大的改进。我们现在有了一个解决方案,可以在15s多一点的时间内产生10,000,000个随机数——与我们之前实现的3.5天相比,这个时间要合理得多。

结论

SciPy 提供了广泛的打包发行版,可以快速采样。这种采样是特定于发行版的,是为了利用 C 语言的速度、优化的 python 代码和最有效的采样过程而编写的。虽然这是一个无价的资源,在大多数情况下将提供一个充分的解决方案,但确实有需要定制分布采样的时候。

更深入地了解 SciPy 的分发体系结构,特别是它如何依赖于 NumPy,如上所示,可以决定一个精确但不可行的缓慢解决方案和一个与 SciPy 的超高速实现相差不远的解决方案。

随机种子和再现性

原文:https://towardsdatascience.com/random-seeds-and-reproducibility-933da79446e3

在 Python、Numpy 和 PyTorch 中设置您的实验

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

克里斯蒂安·朱德雷在 Unsplash 上拍摄的照片

动机

“程序员最可怕的噩梦是什么?”

就我而言,我可以有把握地说,作为一名程序员,我最糟糕的噩梦是一段代码,它的行为就好像是随机的,每次运行它都会产生不同的结果,即使我给它完全相同的输入**!**

实际上有一个著名的关于精神错乱的定义:

“疯狂就是一遍又一遍地做同样的事情,却期待不同的结果。”

尽管人们通常认为这是阿尔伯特·爱因斯坦的功劳,但研究表明事实并非如此。但是,撇开引用的作者不谈,事实仍然是:一遍又一遍地给一段代码输入相同的输入,每次都得到不同的结果,会让你发疯的:-)

本帖包含部分转载自我的书内容:**深度学习用 PyTorch 循序渐进:初学者指南 **

(伪)随机数

“一个人怎么可能调试和修复这样的东西?”

幸运的是,对于我们程序员来说,我们不必处理真正的随机性,而是处理伪随机性。

“什么意思?”

嗯,你知道,随机数并不完全是随机的……它们实际上是伪随机的,也就是说,一个数字生成器抛出一系列数字,看起来像是随机的。但是是不是**,真的。**

这种行为的好处是我们可以告诉生成器启动一个特定的伪随机数序列**。在某种程度上,它的工作原理就好像我们告诉生成器:“请生成序列#42,”,它就会溢出一个数字序列。**

这个数字 42 的作用类似于序列的索引,被称为种子。每次我们给它相同的种子,它都会生成相同的数字。****

“同样的老种子,同样的老号码。”

这意味着我们有两个世界最好的:一方面,我们做产生一个数字序列,对于所有意图和目的来说,被认为是随机的;另一方面,我们有能力复制任何给定的序列。我确信您能够理解这对于调试目的和避免精神错乱是多么方便:-)。****

此外,你可以保证其他人能够复制你的结果。想象一下,运行从博客帖子或书籍中获得的代码,每次都得到不同的输出,不得不怀疑它是否有问题,这是多么令人讨厌的事情。

在学习一个新主题时,您最不需要的就是失去平衡,因为每次运行一些代码时,您都会得到不同的结果(除了有一个种子集之外,代码很可能完全正确)。但是,通过正确设置随机种子,您和我,以及运行代码的每个人,都可以获得完全相同的输出,即使这涉及到生成随机数据!

生成随机数

虽然种子叫做随机,但是它的选择肯定不是!通常,你会看到选择的随机种子是 42 ,这是一个人可能选择的所有随机种子中(第二个)最不随机的。****

因此,我们也在本帖中向将种子设定为 42 的悠久传统致敬。在纯 Python 中,您使用[**random.seed()**](https://docs.python.org/3/library/random.html#random.seed)来设置种子,然后您可以使用[**random.randint()**](https://docs.python.org/3/library/random.html#random.randint)来绘制一个随机整数,例如:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

设定种子后抽取四个随机整数。图片作者。

看到了吗?完全决定论!一旦你将随机种子设置为 42(显然!),生成的前四个整数依次是 10、1、0 和 4,无论您是一个接一个地生成它们,还是在一个列表中理解它们。

如果你对生成本身感到好奇,Python 的 random 模块使用了 Mersenne Twister 随机数生成器,这是一个完全确定性算法。这意味着该算法对于解决再现性问题来说很棒,但是完全不适合用于加密目的。

数字发生器有一个内部状态,它跟踪从特定序列中提取的最后一个元素(每个序列由其对应的种子标识),因此它知道从哪里挑选下一个元素。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

种子#42 的状态序列和相应的生成号。图片作者。

如果您愿意,可以使用[**random.getstate()**](https://docs.python.org/3/library/random.html#random.getstate)[**random.setstate()**](https://docs.python.org/3/library/random.html#random.setstate)来检索(和设置)该状态:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

让生成器“忘记”抽取了一个数字!图片作者。

不出所料,第一个数字还是 10(因为我们使用了同一个种子)。此时,发生器的内部状态记录了从序列中只抽取了一个数字。我们将这个州命名为**first_state**

所以我们又画了一个,我们得到了,不出所料,数字 1。内部状态会相应地更新,但我们会将其设置回绘制第二个数字之前的状态。

现在,如果我们绘制另一个数字,我们将再次得到数字 1,因为我们通过更新它的内部状态,迫使生成器“忘记”最后绘制的数字。

这些数字看起来不再是随机的了,嗯?

“是的,但是我必须问…那个州有什么?”

很高兴你问了。它只是一个元组!第一个元素是 version (3),第二个元素是 625 个整数的长列表(内部状态),最后一个元素通常是**None**(你可以暂时放心地忽略它)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

生成器的内部状态(仅显示第一行和最后一行)。图片作者。

看到最后那个“1”了吗?那是列表的第625 个元素,它作为其他元素的索引实际内部状态由前 624 个元素表示。记住这一点,我们将很快回到它!****

“好吧,那么我们很好,现在一切都完全可以复制了?”

我们还没有到那一步…如果你查看 Python 的“ 关于再现性的注释 ”,你会看到:

“通过重用种子值,只要多个线程没有运行,相同的序列就应该可以在不同的运行之间重现。”

所以,如果你是多线程的,再现性就拜拜了!从好的一面来看,Python 的(伪)随机数生成器(从现在开始姑且称之为 RNG)有两个保证(从“注释”转录而来):

  • 如果添加了新的播种方法,那么将提供向后兼容的播种机。
  • 当兼容的播种机被给予相同的种子时,生成器的random()方法将继续产生相同的序列。

“好了,现在我们好了吗?”

抱歉,但是不行!Python 自己的 RNG 是而不是唯一一个你可能需要设置种子的。

Numpy

如果你也使用 Numpy,你需要为它自己的 RNG 设置一个种子。为此,您可以使用[**np.random.seed()**](https://numpy.org/doc/stable/reference/random/generated/numpy.random.seed.html):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Numpy 中的随机数(传统)。图片作者。

从上面的例子可以看出,Numpy 的 RNG 的行为方式与 Python 的 RNG 相同:一旦设置了种子,生成器就输出完全相同的数字序列,6、3、7 和 4。

虽然上面的代码是最常见的“T7”,而且许多人一直这样使用它(包括我自己,被指控有罪),但它已经被认为是遗留的代码。

从 1.17 开始,更近的 Numpy 版本使用不同的方式生成(伪)随机数:首先创建一个生成器,然后从中抽取数字。用户可以使用[**np.random.default_rng()**](https://numpy.org/doc/stable/reference/random/generator.html#numpy.random.default_rng)创建默认生成器:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Numpy 中的随机数(最新)。图片作者。

“等等,现在数字不一样了吗?”

是的,它们不同,尽管我们使用的是相同的种子,42。

“那是为什么?”

数字不同是因为发生器与不同,也就是说,它使用了不同的算法。Numpy 的遗留代码使用 Mersenne Twister (MT) 算法,就像 Python 的 random 模块一样,而 Numpy 的新默认生成器使用 Permute 同余生成器(PCG) 算法。

但是,事实证明,即使 Numpy 的遗留代码和 Python 的随机模块使用相同的算法,并且我们在它们两者中使用相同的种子,生成的数字仍然不同**

“你一定是在和我开玩笑!为什么?!"

我理解你可能会感到沮丧,这种差异归结为Python 的随机模块和 Numpy 处理生成器内部状态中讨厌的“索引”的方式。如果你对这方面的更多细节感兴趣,请查看下面的旁白——否则,请随意跳过它。**

匹配内部状态

如果我们使用相同的 624 个数字的列表来更新两个生成器的状态,同时将“索引”设置为 624(就像 Numpy 默认设置的那样),这就是我们得到的结果:匹配序列!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

匹配 Python 的 random 和 Numpy 的内部状态。图片作者。

正如您在上面的代码中看到的,还可以分别使用 [***set_state()***](https://numpy.org/doc/stable/reference/random/generated/numpy.random.set_state.html) [***get_state()***](https://numpy.org/doc/stable/reference/random/generated/numpy.random.get_state.html) 来检索或设置 Numpy 的生成器的内部状态,并且状态本身在其元组中有更多的元素(‘mt 19937’代表 Mersenne Twister (MT)及其范围(顺便说一下,2 ⁹⁹ ⁷-1)),但是我们不会对此进行任何深入的研究。毕竟,在 Numpy 中,您不太可能需要修改生成器的内部状态…

还有一点需要指出,转自 Numpy 的[**Generator**](https://numpy.org/doc/stable/reference/random/generator.html#numpy.random.Generator)文档,一个标题为“ 无兼容性保证 ”的章节:

Generator不提供版本兼容性保证。特别是,随着更好的算法的发展,比特流可能会改变。

谁说确保再现性很容易?不是我!

请记住:为了真正的再现性,您需要使用相同的随机种子、相同的模块/包和相同的版本!

是时候换一个不同的包了!

PyTorch

就像 Numpy 一样,PyTorch 也有自己设置种子的方法,[**torch.manual_seed()**](https://bit.ly/3hOwklL),为所有设备(CPU 和 GPU/CUDA)设置一个种子:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

PyTorch 中的随机数(CPU)。图片作者。

正如您可能已经预料到的,生成的序列又是不同的。新包装,新顺序

但还有更多!如果你在一个不同的设备中生成一个序列,比如你的 GPU ( **'cuda'**),你会得到又一个序列

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

PyTorch 中的随机数(CUDA/GPU)。图片作者。

在这一点上,这不应该是一个惊喜,对不对?此外,PyTorch 关于再现性的文档非常简单明了:

“不能保证 PyTorch 版本、单个提交或不同平台的结果完全可重复。此外,即使使用相同的种子,CPU 和 GPU 执行之间的结果也可能不可重复。”

因此,我相应地更新了上一节的建议:

请记住:为了真正的再现性,您需要使用相同的随机种子、相同的模块/包、相同的版本、相同的平台、相同的设备,甚至可能是相同的驱动程序(例如,您的 GPU 的 CUDA 版本)!

也许你注意到了上面输出中的一个**Generator** …不出所料,PyTorch 也使用生成器,就像 Numpy 一样,并且那个生成器是 PyTorch 的默认生成器。我们可以使用[**torch.default_generator**](https://pytorch.org/docs/stable/torch.html#torch.torch.default_generator)检索它,并使用[**manual_seed()**](https://pytorch.org/docs/stable/generated/torch.Generator.html#torch.Generator.manual_seed)方法设置它的种子:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

PyTorch 的默认生成器。图片作者。

您也可以创建另一个生成器,并将其用作其他函数或对象的参数:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 PyTorch 中创建和使用生成器。图片作者。

有一种情况下使用自己的生成器特别有用:数据加载器中的采样。

数据加载器

在为训练集创建数据加载器时,我们通常将其参数**shuffle**设置为**True**(因为在大多数情况下,混合数据点可以提高梯度下降的性能)。这是一种非常方便的混洗数据的方式,它是使用引擎盖下的[**RandomSampler**](https://pytorch.org/docs/stable/data.html#torch.utils.data.RandomSampler)实现的。每次请求一个新的小批量时,它会随机采样一些索引,并返回对应于这些索引的数据点。

即使不涉及混洗,也要使用[**SequentialSampler**](https://pytorch.org/docs/stable/data.html#torch.utils.data.SequentialSampler),这在用于验证集的数据加载器中是很典型的。在这种情况下,每当请求新的小批量时,该采样器简单地按顺序返回一系列索引,并且返回对应于那些索引的数据点。

从 PyTorch 1.7 开始,为了保证再现性,我们需要给 [**DataLoader**](https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader)分配一个生成器,所以在相应的采样器中使用它(当然前提是它使用生成器)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

将生成器分配给数据加载器。图片作者。

实际上,我们可以从加载器中检索采样器,检查其初始种子,并根据需要手动设置不同的种子:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

手动设置装载机发电机的种子。图片作者。

我们将在几个部分中这样做,同时编写一个函数来使用"一个种子来统治所有的 " 😃

为数据加载器分配一个生成器将会覆盖您,但是只有当您在主进程 ( **num_workers=0**,默认)中加载数据时。如果您想要使用多重处理来加载数据,也就是说,指定更大数量的工人,您还需要为您的数据加载器分配一个 **worker_init_fn()** ,以避免您的所有工人绘制完全相同的数字序列。让我们看看为什么会出现这种情况!****

PyTorch 实际上可以在上面的情况下照顾自己——它用不同的编号播种每个 worker,也就是**base_seed + worker_id**,但是它不能照顾其他的包(比如 Numpy 或者 Python 的 random 模块)。

我们可以使用作为参数传递给数据加载器的**seed_worker()**函数中的一些打印语句来看看发生了什么:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

看一眼每个工人使用的种子。图片作者。

有两个工人,(0)和(1),每次调用一个工人执行任务时,**seed_worker()**函数打印 PyTorch、Numpy 和 Python 的 random 模块使用的种子。

你可以看到 PyTorch 使用的种子刚刚好——第一个工人使用一个以 55 结尾的数字;第二个工人的,一个以 56 结尾的号码,不出所料。

但是 Numpy 和 Python 的 random 模块使用的种子是跨 worker 的相同,这也是我们要避免的。不过,不同模块之间的种子可以相同。****

幸运的是,有一个简单的解决方法:我们在** **seed_worker()** 函数中包含一些种子设置语句,使用 PyTorch 的初始种子(并将其调整为 32 位整数),而不是打印语句:**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

适当地播种工人。图片作者。

现在每个工人将为 PyTorch、Numpy 和 Python 的 random 模块使用不同的种子。

“好的,我明白,但是如果我只使用 PyTorch,为什么我需要播种其他的包呢?”

播种 PyTorch 是不够的!

您可能认为,如果您没有在代码中显式使用 Numpy 或 Python 的 random 模块,您就不需要关心为它们设置种子,对吗?

也许你没有,但是最好稳妥一点,为一切设置种子:PyTorch,Numpy,甚至 Python 的 random 模块**,这就是我们在上一节中所做的。**

“那是为什么?”

原来,PyTorch 可能用的是不属于自己的发电机!老实说,当我发现这件事的时候,我也很惊讶!听起来可能很奇怪,在 0.8 之前的 Torchvision 版本中,仍然有一些代码依赖于 Python 的随机模块,而不是 PyTorch 自己的随机生成器。当使用一些用于数据扩充的随机转换时,问题就出现了,比如**RandomRotation()****RandomAffine()**等等。****

库达

手动设置 PyTorch 的种子对 CPU 和 CUDA/GPU 都有效,正如我们在前面几节中看到的那样。但是由 CUDA 卷积运算使用的 cuDNN 库仍然可能是非确定性行为的来源。

事实证明,根据所提供的参数以及底层硬件和环境,库试图使用最快的算法。但是我们可以通过禁用这个所谓的基准特性,将**torch.backends.cudnn.benchmark** 设置为**False**.,来强制确定性地选择一个算法****

虽然使用上述配置可以使算法的 选择具有确定性,但是算法本身可能不是的**!**

“哦,来吧!”

我听到了。为了解决这个问题,我们还需要做另一个配置:将**torch.backends.cudnn.deterministic** 设置为**True**.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使 CUDA 卷积运算具有确定性。图片作者。

使用 CUDA 的可再现性还有其他含义:由于 CUDA 版本 10.2 中引入的变化,RNN 和 LSTM 层也可能表现出不确定性行为(详细信息参见文档)。

PyTorch 的文档建议将环境变量**CUBLAS_WORKSPACE_CONFIG** 设置为**:16:8****:4096:2**来实施确定性行为。

合唱:老麦克托奇有一个模型

“老麦克托奇有一个模型,咿呀咿呀

在它的模型上,有一些种子,咿呀咿呀

这里有一粒种子,那里有一粒种子

这里一粒种子,那里一粒种子,到处都是一粒种子

老麦克托奇有一个模型,咿呀咿呀哟"

你觉得上面这首歌怎么样,来自“程序员童谣”?对了,我开玩笑,那不是真书,我编的!也许我应该写这样一本书…但是我跑题了!

回到我们的主要话题,可能感觉和那首歌一模一样——种子和更多的种子——到处都是种子!

要是有就好了…

“一粒种子统治他们所有人!”

没有这种事,但是我们可以试试退而求其次:我们自己的函数设置尽可能多的种子!下面的代码为 PyTorch、Numpy、Python 的 random 模块、采样器的生成器设置种子;除了配置 PyTorch 的后端使 CUDA 卷积操作具有确定性之外。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一粒种子统治所有人。图片作者。

“这样够吗?”

不一定,不。一些操作可能仍然具有不确定性,从而使您的结果不完全可再现。虽然有可能强制 PyTorch 只使用确定性算法设置[**torch.use_deterministic_algorithms(True)**](https://pytorch.org/docs/stable/generated/torch.use_deterministic_algorithms.html#torch.use_deterministic_algorithms),但是有一个问题…

“我就知道!”

可能你正在执行的一些操作只有非确定性算法可用,然后你的代码在被调用时会抛出一个**RuntimeError**。出于这个原因,我没有将它包含在上面的 set_seed 函数中——我们没有破坏代码,以确保它的可再现性。

此外,如果您使用的是 CUDA(10.2 或更高版本),除了设置**torch.use_deterministic_algorithms(True)**,您还需要设置环境变量**CUBLAS_WORKSPACE_CONFIG**,如前一节所述。

那些非确定性算法可能来自最意想不到的地方**。例如,在 PyTorch 的文档中,有一条关于在图像中使用填充时可能出现再现性问题的警告:**

“使用 CUDA 后端时,此操作可能会在其反向传递中引发不确定性行为,这种行为不容易被关闭。请参阅关于再现性的注释以了解背景信息。”

让我觉得有点奇怪的是,这么简单的操作会危及可重复性。真不敢相信

随机种子调谐

“(正确的)随机种子是你所需要的!”

看起来像个笑话,但是随机种子的选择可能会对模型训练产生影响。一些种子比其他种子“更幸运”,因为它们允许模型训练得更快,或者实现更低的损失。当然,没有办法事先说清楚,也没有,42 不是“什么是正确的随机种子”问题的答案:-)

如果你对这个话题很好奇,可以查看大卫·皮卡德的论文:“ Torch.manual_seed(3407)是你所需要的全部:论深度学习架构中随机种子对计算机视觉 的影响”。摘要如下:

“在本文中,我研究了在将流行的深度学习架构用于计算机视觉时,随机种子选择对准确性的影响。我在 CIFAR 10 上扫描了大量种子(多达 104 个),在 Imagenet 上也扫描了较少的种子,使用预先训练的模型来调查大规模数据集。结论是,即使方差不是很大,也很容易找到表现比平均值好得多或差得多的异常值。”

最后的想法

再现性很难!

我们甚至没有谈论更基本的问题,例如确保你正确使用数据,以避免多年后当别人试图复制你发表的结果时的尴尬(见莱因哈特和罗格夫的 Excel 大错,也被称为“如何不擅长经济学”)!

我们只关注(伪)随机数生成器,即使如此,我们也需要考虑 许多不同来源的(伪)随机性 以确保可再现性。那是大量的工作,但是它值得麻烦。

确保总是在代码的最开始初始化你的随机种子,以确保(或者尝试!)您的结果的可重复性。

愿你未来的实验完全可复制!

如果您有任何想法、意见或问题,请在下方留下评论或通过我的 简历链接 页面联系。

如果你喜欢我的帖子,请考虑使用我的推荐页面通过 注册一个中级会员 来直接支持我的工作。对于每一个新用户,我从中获得一小笔佣金:-)

R 中的范围:如何使用 Range 函数找到最小值和最大值

原文:https://towardsdatascience.com/range-in-r-how-to-find-min-and-max-values-using-the-range-function-5c3a1f5dffad

通过这 6 个实际例子掌握 R 中的范围函数

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由 Vipul JhaUnsplash 上拍摄

R 中的 Range 返回一个向量,该向量包含给定参数的最小值和最大值—在统计学中称为 range 。您可以将范围视为向量中最低值和最高值之间的间隔。向量可以是任何东西,从数字列表到数据帧列——R 真的不在乎。

实际上,范围是向量中最大值和最小值之间的差,但是计算 R 中的范围只报告区间极值。你可以用一个减去另一个来得到实际的范围。

今天,您将通过大量实际例子了解 R 中的range()函数是如何工作的。您将使用常规和字符向量、数据帧和数据帧列,还将看到如何处理缺失值和无穷大值。

R 中的范围—定义和用法

你想学习如何在 R 中找到一个范围内的值?你来对地方了。在这一节中,我们将检查语法和返回值,它们将作为以后动手操作示例的基础。

句法

range(x, na.rm = FALSE)

其中:

  • x -一个字符或一个数字向量。
  • na.rm -可选参数,指定在计算范围前是否应移除 NA 值。

返回值

R 中的range()函数返回一个双精度值或一个字符,这取决于作为参数传递的是数字向量还是字符向量。

R 范围—示例

本节我们将讨论六个例子,涵盖从基本数字向量到整个数据帧的用法。

如何用 R 中的值域求最小值和最大值

下面的代码显示了如何计算数值向量的范围:

arr <- c(-10, -15, 5, 19, 27, 0)
range(arr)

输出:

-15 27

有意义,因为 27 是最大值,而-15 是最小值。

在字符向量上使用范围函数

你不会总是和数字数据打交道。您可以计算字符串的范围,该函数将返回按字母顺序排序的第一个和最后一个值:

arr_char <- c("Bob", "Mike", "Kelly", "Sue")
range(arr_char)

输出:

"Bob" "Sue"

求缺失值向量的值域

真实世界的数据通常是混乱的,充满了缺失值。如果处理不当,它们会打乱你的计算。这里有一个例子:

arr <- c(-10, -15, NA, 19, 27, 0)
range(arr)

输出:

NA NA

指定na.rm = TRUE以避免此问题:

range(arr, na.rm = TRUE)

输出:

-15  27

求一个无穷(Inf)值向量的范围

计算错误有时会产生正值或负值的Inf值。它们打乱了范围计算,因为它们要么是向量的最小值,要么是向量的最大值:

arr <- c(-10, -15, Inf, 19, 27, 0)
range(arr)

输出:

-15 Inf

指定finite = TRUE从范围计算中排除Inf:

range(arr, finite = TRUE)

输出:

-15  27

在 DataFrame 列上使用 Range 函数

让我们将虹膜数据集存储到变量df中:

df <- iris head(iris)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片 1 —虹膜数据集的头部(图片由作者提供)

您可以通过以下方式计算特定 dataframe 列的范围:

range(df$Sepal.Length)

输出:

4.3 7.9

在整个数据帧上使用范围函数

但是如果您需要整个数据集的范围呢?你只能对这个数据集中的数字列进行计算:

df_numeric <- df[c('Sepal.Length', 'Sepal.Width', 'Petal.Length', 'Petal.Width')]
range(df_numeric)

输出:

0.1 7.9

这并不能告诉您太多—您不知道最小值和最大值在哪些列中。使用sapply()函数获取各个列的范围:

sapply(df_numeric, function(df_numeric) range(df_numeric))

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片 2-计算整个数据集的范围(图片由作者提供)

结论

今天,您已经通过大量的实际例子了解了 R 中的值域是如何工作的。这是一个简单的功能理解,但绝对需要有你的工具带。您现在已经准备好在您的数据分析项目中使用它,因为没有您不能处理的数据类型或用例。

请继续关注后续文章,因为我们将探索其他有用的内置 R 函数。

保持联系

  • 雇用我作为一个技术作家
  • 订阅 YouTube
  • 在 LinkedIn 上连接

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

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

原载于 2022 年 2 月 9 日 https://betterdatascience.com**的

在 GCP 使用 Terraform、GitHub Action、Docker 和 Streamlit 进行快速原型制作

原文:https://towardsdatascience.com/rapid-prototyping-using-terraform-github-action-docker-and-streamlit-in-gcp-e623ae3fdd54

使用 CI/CD 工具加速见解共享

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

马修·布罗德在 Unsplash 上的照片

介绍

数据科学家和分析师是许多组织的宝贵资源。他们将大部分时间用于理解业务需求,并通过数据收集、争论、清理和建模来寻找见解。支持这些数据专家的工具非常丰富,例如,像 Jupyter 和 Colab 这样的笔记本、Python 或 R 库以及其他数据技术。

另一方面,当涉及到与利益相关者分享重要的发现和有价值的见解时,选择是有限的。对于非技术人员来说,笔记本可能不是最佳选择;在像 Word 和 PowerPoint 这样的办公文档中分享你的发现通常不能描述你的发现的动态和交互性质。对于数据专家来说,找到以合适的格式快速有效地向决策者展示调查结果的最佳方式通常是一件痛苦的事情。

在本文中,我们将展示一种使用 CI/CD(持续集成和持续交付)工具(如 Terraform、GitHub Actions 和 Docker with Streamlit applications)与利益相关方共享数据科学发现的方法,这些工具可以加速原型开发。我们将代码部署到谷歌云平台(GCP)的虚拟机上,但是你也可以将这些技术应用到其他云服务提供商。

工作流程概述

本文将指导您创建以下 CI/CD 管道。

1.在本地写 Terraform 文件,推送到 GitHub。
2。GitHub Action 使用 Terraform 实现 GCP 供应自动化。
3。在本地编写 Streamlit app 代码和 Dockerfile,并将代码推送到 GitHub。
4。通过 GitHub Action 将代码部署到 GCP 的虚拟机实例中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图一。工作流程概述(作者图片)

先决条件

实施这些解决方案有四个要求。

● GitHub 账号
● Terraform 安装
● GCP 服务账号
● Python 库

开源代码库

GitHub 是一个使用 Git 的基于 web 的版本控制服务。它还提供一项名为 GitHub Actions 的服务。您不需要安装第三方应用程序来自动化测试、发布和将代码部署到生产中的工作流。您可以在 GitHub 网站上创建一个帐户。

将(行星)地球化(以适合人类居住)

Terraform 是一个开源的 CI/CD 工具,它将基础设施作为代码(IaC)进行管理,由 HashiCorp 提供。IaC 使用代码来管理和配置基础设施,消除了通过云供应商提供的 UI 手动配置物理硬件的需要。

使用 Terraform 进行原型开发有一些优点。例如:

  1. 避免手动调配基础架构。
  2. 数据科学家和软件工程师可以自助。
  3. 对当前设置的改变变得简单明了。
  4. 易于传递知识和保持版本。

不需要在本地安装 Terraform,就可以通过 GitHub Action 实现流程自动化,因为 GitHub 在云中执行工作流。然而,在测试新资源或刚刚开始使用 Terraform 时,在本地安装软件可能是个好主意。在这种情况下,您需要向 Terraform 用户提供访问云资源的凭据,这在一些组织中可能是一个安全问题。你可以在 Terraform 网站上找到安装说明。

GCP

服务账户(SA)代表谷歌云服务身份,例如,在计算引擎上运行的代码、在云存储中创建桶等。可以从 IAM 和管理控制台创建 sa。出于原型制作目的,请为帐户选择编辑者角色,以查看、创建、更新和删除资源。你可以参考谷歌云的官方指南。本文使用了计算引擎、云存储和 VPC 网络。

计算机编程语言

我们只用了三个库:Pandas、Yfinance 和 Streamlit。Pandas 是一个开源的数据操作工具。yfinance 是一个 python API,用于从 Yahoo!金融。Streamlit 是一个基于 Python 的 web 应用框架;与 Flask 或 Django 等其他框架相比,您可以用更少的代码开发 web 应用程序。

1.地形文件

Terraform 是一个 IaC 工具。您可以通过编写代码来管理云基础架构。Terraform 可以通过各种云提供商提供服务和资源。HashiCorp 和 Terraform 社区编写和维护代码。你可以在 Terraform Registry 网站上找到你可以使用的提供商列表,包括 AWS、Azure 和 GCP。

Terraform 的工作流程由三个步骤组成:定义、计划和应用。在定义步骤中,您将编写配置文件来声明提供者、服务和资源。配置文件可以由 Terraform 使用 terraform fmtterraform validate 命令进行格式化和验证。在计划步骤中,Terraform 根据配置文件和现有基础设施的状态创建一个执行计划。它可以创建新的服务或更新/删除现有的服务。最后,应用步骤执行计划的操作,并记录 Terraform 提供的基础设施的状态。

我们将创建三个 Terraform 文件:main.tf、variables.tf 和 terraform.tfvars。

main.tf

main.tf 是一个包含主要配置集的文件。它定义了所需的提供者、提供者变量和资源。该文件的内容如下:

首先,定义提供者。在本文中,我们将使用 google。

定义了提供者的变量。var。*将从单独的文件 variables.tf 中导入值。

credentials_file 应该是从 GCP 下载的 json 中服务帐户密钥的路径。

接下来的脚本创建了一个 VPC 网络。

然后,我们配置一个虚拟机实例。

我们指定虚拟机实例名称、机器类型、磁盘映像和网络接口。我们还添加标签并定义启动脚本。标记允许您将防火墙规则应用于 VM 实例。我们创建了三个标记,分别对应于下面定义的三个防火墙规则。metadata_startup_script 在启动时运行 shell 脚本。在我们的例子中,我们将运行脚本在虚拟机中预安装 Docker 引擎。

下一个特定的 http 访问防火墙规则。

您可以通过 CIDAR 格式的 source_ranges 来限制访问。

我们可能希望通过 SSH 访问 VM 实例。

第三个防火墙规则是针对 Streamlit web 应用程序的。端口 8501 将被打开。

最终的资源定义是针对云存储的。它使用从 variables.tf 文件中读取的变量创建新的 bucket。

变量. tf

var 的值。*由 variables.tf 文件提供。

每个变量都可以有一个默认值。在我们的例子中,一些变量没有默认值。因为它们是必需的参数,所以 Terraform 需要提供它们。提供变量的方法之一是通过*。tfvar 文件,减少了一些敏感信息的暴露。

terraform.tfvars

我们在 terraform.tfvars 文件中有三行。请用您的值替换“***”。

GCP 机器类型和图像列表

在 main.tf 中,我们指定了机器类型和磁盘映像。接受的值可能不总是与您在 GCP 控制台上看到的描述相同。您可以使用以下 gcloud 命令检索机器类型列表和计算引擎映像。(必须安装谷歌云 SDK。如果没有,请参考官方指南

g 云计算映像列表

Docker 安装脚本

我们将 docket 安装到虚拟机实例中。在 main.tf 文件所在的目录中找到安装脚本 install_docker.sh。Ubuntu 的安装脚本复制自官方 Docker Doc 网站

2.GitHub 行动——GCP

GitHub 操作允许您构建自动化的 CI/CD 管道。公共存储库免费,私人存储库包含 2000 分钟【1】

GitHub 动作读取了下保存的 YAML 文件。github/workflows/repository 中的目录并执行文件中定义的工作流。一个工作流可以有多个作业,每个作业都有一个或多个步骤。一个步骤可以有动作,但不是所有的步骤都可以执行一个动作。

文件内容如下:

首先,我们定义工作流名称和触发器,即 push 到主分支。接下来,我们添加关于作业的信息

上面的作业在服务器端的 ubuntu 机器上运行。每个作业在一个新的实例上运行;因此,每个作业下定义的环境变量不能用于另一个作业。

接下来,我们指定步骤和动作。

第二步,我们将使用秘密。GCP 萨基。这些秘密存储在存储库的动作秘密中。GitHub 支持 base64 编码的秘密。您可以使用 Python 将 GCP 服务帐户密钥编码为 base64 格式的 JSON:

Terraform init 步骤通过下载提供者插件启动工作目录的准备。我们还在后端配置中指定了一个 GCS bucket 名称。

如果您将目前准备好的文件从本地驱动器提交到主驱动器,GitHub 工作流会自动运行并提供服务。你可以在 GitHub Action 页面查看执行日志。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图二。GitHub 动作工作流程日志(图片由作者提供)

3.Streamlit 和 Dockerfile

在前面的章节中,我们使用 Terraform 和 GitHub 操作准备了基础设施。我们现在用 Streamlit 和 Docker 准备运行在 GCP 虚拟机实例上的 web 应用程序代码。

作为一个简单的例子,我们将为 Yahoo Finance 的时间序列数据创建一个仪表板,显示每日回报率,以便用户可以比较不同的指数或外汇汇率。仪表板用户也可以修改日期范围。

我们将要创建的仪表板图像如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3。Streamlit Web 应用程序用户界面(图片由作者提供)

Python 脚本

上面显示的应用程序可以使用 Streamlit 创建。

首先,我们导入三个库。如果没有安装,请使用 pip(或 pip3) install 命令安装它们。

我们在 st.title('text ')中指定 web 应用程序的名称。

然后,我们准备选择题。st.multiselect()创建一个下拉选择。

Streamlit 提供了 date_input 选项,可以从日历中选择日期。这里我们创建两个变量:from_date 和 to_date。

最后,我们用计算出的返回值设计图表。

Streamlit 动态读取选择器变量中的值,然后从 yfinance API 中检索收盘价数据。增长率是使用 pandas pct_change()计算的。最后一步是使用 st.line_chart()函数在折线图中表示数据框。

我们将脚本保存在 app.py 文件中。

Dockerfile 文件

dockerfile 是一个文本文件,包含创建图像文件的所有命令。您可以使用 docker build 构建映像文件,并通过 docker run 命令将映像作为容器运行。

在上面的 docker 文件中,我们在最新的官方 Python docker 发行版上构建了自己的映像。因为我们需要三个库来运行 Streamlit 应用程序,所以我们在文本文件中指定依赖关系,然后运行 pip install 命令。Streamlit 的默认端口是 8501。然后,我们复制容器的/app 目录中的 app/py 文件,并运行 Streamlit web 应用程序。

4.GitHub Action — Web 应用程序

在前面的小节中,我们为 GitHub 存储库中的基础设施供应准备了一个 YAML 文件。我们还必须创建一个存储库并定义一个工作流来部署 docker 容器,该容器在 GCP 的已调配虚拟机实例上运行 Streamlit web 应用程序。

首先,我们为 web 应用程序代码创建一个新的私有 GitHub 存储库,然后重复相同的步骤,在 GitHub Action secrets 中添加 GCP 服务帐户凭证。

接下来,我们在 GitHub 中准备一个私有访问令牌。该令牌用于从 GCP 的 VM 实例克隆 GitHub 中的这个存储库。

在你的 GitHub 页面,进入设置->-开发者设置->-个人访问令牌,然后点击生成新令牌。在新增个人访问令牌页面:回购工作流必须打勾。工作流程选项允许您更新 GitHub 动作工作流程。您只会看到一次生成的令牌,所以请将您的令牌复制到编辑器中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4。新的个人访问令牌(图片由作者提供)

我们需要在存储库 URL 中插入您的用户名和个人访问令牌。比如【https://github.com/your-user-name/your-repository.git】会是*https://****your-user-name:your-access-token @***github.com/your-user-name/your-repository . git。将完整的 URL 保存在 GitHub Action secrets 中,这样我们就可以在工作流 YAML 文件中调用它。

在下面。github/workflows 目录,我们创建 deploy_dokcer.yaml 文件。该工作流从 Dockerfile 构建 docker 映像,并将其部署在 GCP 虚拟机实例中。

在 jobs 部分,我们设置了一些变量和权限。

在“步骤”部分,我们定义了操作。为了通过 ssh 在 VM 实例中运行 bash,我们设置了 gcloud CLI。在命令中,我们克隆 GitHub 存储库并创建 docker 映像,然后从映像运行容器。

当您将文件推送到 GitHub repo 时,工作流运行并将容器部署到 VM。您可以从 GitHub Actions UI 查看工作流日志。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5。GitHub 动作工作流程日志(图片由作者提供)

现在,您可以修改 app.py,将修改后的代码推送到存储库,并确认更改已应用到虚拟机。

结论

本文解决了数据专家在分享来自数据分析阶段的有价值的见解方面的难题,并提出了一种使用 Streamlit web 应用程序呈现发现的方法。为了部署应用程序,CI/CD 工具和服务(如 Terraform 和 GitHub Actions)通过自动化工作流来帮助数据专家加速原型开发。

我们使用的例子是一个简单的用例;然而,Streamlit 可以做得更多。我们建议您访问 Streamlit 网站了解它能提供什么。类似地, Terraform Registry 也有很多有用的资源,并且由 HashiCorp 和提供商积极更新。值得查看您感兴趣的提供商的文档,以找到工作流自动化的其他机会。最后,GitHub 动作允许你设计更复杂的工作流程。如果你想在原型之外使用 GitHub 动作,强烈推荐阅读官方文档。

参考

[1]“关于 GitHub 操作的计费”,GitHub 文档。https://docs . github . com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions【访问时间:2022 年 8 月 7 日】。

用 Python 快速构建和部署 Web 应用程序

原文:https://towardsdatascience.com/rapidly-building-and-deploying-a-web-app-in-python-10e0021596a5

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Unsplash 上拍摄的 ThisisEngineering RAEng

我最近用 Python 写了几个 web 开发框架,今天我将介绍使用 Streamlit 构建和部署一个非常简单的新冠肺炎仪表板。Streamlit 旨在通过围绕您的 Python 代码构建一个流畅的 UI 来轻松创建以数据为中心的应用程序,不需要任何 web 开发经验(尽管它确实有所帮助)。

在开始之前,我想解释一下为什么我要写这篇文章。我和一位技术高超的数据科学家同事通了电话。他在寻找利用他的 Python 技能来构建可共享的应用程序的方法。我向他介绍了 Streamlit,他很快就开发出了一个很棒的应用。然而,当要分享他的应用程序时,他完全不知道该怎么做。作为一名数据分析师,我也经历过他的情况,如果不能与他人分享自己辛苦编写的代码,会非常沮丧。我希望通过这篇文章来弥补这一差距,解释让你在适当的时候轻松分享应用程序所需的所有步骤。

我们将首先为我们的项目建立一个虚拟环境。然后,我们将编写一些 Python 代码,作为应用程序的引擎。第三,我们将使用 Streamlit 库来帮助为我们的 Python 代码创建 UI,最后我们将部署我们的应用程序。我希望你能更好地理解 Python 中 web 开发的端到端过程。

1.设置虚拟环境

当您开始一个项目时,设置虚拟环境是您应该采取的第一步。你可以把虚拟环境想象成你的应用程序所在的地方,它与外界完全隔离。它不知道您可能已经编写的任何其他程序或您可能已经安装的 Python 库。这一点很重要,因为已经安装在计算机上的 Python 库可能会给应用程序带来问题。例如,一个新版本的包可能会通过取消某些功能来破坏您的代码。虚拟环境通过允许您控制库的版本并将它们与已经安装在您机器上的库分开来缓解这个问题,有助于防止任何依赖关系被覆盖或彼此冲突。此外,它有助于保持整洁有序。您的应用程序可能不会使用您机器上安装的所有 Python 库。如果你要在虚拟环境之外构建一个应用程序,那么每个库都将被包含在所谓的需求文件中(稍后会详细介绍),这是部署你的应用程序的必要元素。这只会导致额外的膨胀。

要实际设置虚拟环境,请打开终端或命令提示符。如果你在 Mac 上,通过键入 cd desktop 导航到你的桌面位置——在 windows 上是 CD C:\ Users \ YOUR PC USERNAME \ Desktop。接下来键入 mkdir streamlit _ project & & CD streamlit _ project。在您的项目目录中,通过键入 python3 -m venv 创建一个虚拟环境,后跟您决定的虚拟环境名称。惯例通常称之为虚拟环境。但是我要把我的名字叫做 virt_env。他就是那个样子:

python3 -m venv virt_env # Mac
python -m venv virt_env # Windows

接下来,我们将激活虚拟环境,并开始安装项目所需的软件包。要激活虚拟环境,请根据您的操作系统键入以下内容之一:

source virt_env/bin/activate # Mac
virt_env\Scripts\activate # WindowsOnce activated you should see
(virt_env) ...

创建我们的应用程序

现在让我们安装 streamlit 和 Python 应用程序中需要的其他包:

python3 -m pip install streamlit # Mac
python -m pip install streamlit # Windows python3 -m pip install pandas # Mac
python -m pip install pandas # Windowspython3 -m pip install matplotlib # Mac
python -m pip install matplotlib # Windows

在我们安装完这些包之后,请从我的 Github repo 中复制并粘贴代码,并将其作为 covid_dashboard.py 保存在您的虚拟环境的目录中。这是我们的 python 文件,包含我们的 streamlit 应用程序,它主要做两件事:1)计算四个指标,2)按状态绘制新案例的时间序列。我将更详细地解释每一部分。

@st.cache(allow_output_mutation=True)def load_dataset(): try: df = pd.read_csv("https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-counties.csv", sep=",") states = list(df['state'].unique()) except: Exception return df, states

load_dataset()函数从 NYT Github 帐户中提取所有的 Covid 数据,并创建一个美国各州的列表进行过滤。在这个函数上面使用 st.cache 很重要,因为它允许将整个数据集存储在内存中,这样程序就不必在每次应用不同的过滤器时重新加载整个数据集。

def pct_change(data, state, today):
   ....

pct_change 函数有三个参数:covid 数据集、我们在下拉列表中选择的州(我们稍后会讲到)和今天的日期。该函数返回一个包含四个指标的字典(新病例、14 天病例变化、新死亡和 14 天死亡变化)。

def chart_data(data, state):
   ...

def chart_data 从下拉列表中获取我们的 covid 数据集和所选州,并返回我们所选州的新病例的时间序列数据帧。

移动到代码的最后一部分(这是我们的函数被调用和数据被返回并显示在我们的应用程序中的地方)。调用 load_dataset()并生成数据框和美国各州列表。然后,我们用 st.selectbox 实例化一个下拉列表,并向其中添加一个标签和我们的状态列表。然后,当下拉菜单被选中时,我们添加逻辑。最后,streamlit 提供了一种方便的方式来添加列和图形到您的网页,创建一个更干净的整体演示。所有这一切的美妙之处在于能够用最少的代码用纯 Python 创建流畅且响应迅速的 HTML 组件。

df, states = load_dataset() state_dropdown = st.selectbox('Select a State', states)if state_dropdown:data_dict = pct_change(df, state_dropdown, today)col1, col2, col3, col4 = st.columns(4)col1.markdown(f"New Cases: {data_dict['Cases']}")col2.markdown(f"""<p>14-Day Change: <span style="color:red">{data_dict['Case Change']}%</span></p>""", unsafe_allow_html=True)col3.markdown(f"New Deaths: {data_dict['Deaths']}")col4.markdown(f"""<p>14-Day Change: <span style="color:red">{data_dict['Death Change']}%</span></p>""", unsafe_allow_html=True)chart_data = chart_data(df, state_dropdown)st.line_chart(chart_data)

部署应用程序:初始步骤

我们已经创建了一个虚拟环境和我们的 streamlit 应用程序,但它只存在于我们的本地机器上。为了与全世界分享它,我们需要确保安装了 Git 和 Heroku。要安装 Git,打开一个新的命令行窗口,在 Mac 上键入

git --version

应该会出现一个弹出窗口,显示进一步的说明,允许您继续安装。对于 Windows 用户,使用以下链接下载 Git:https://git-scm.com/downloads

要在 Mac 上安装 Heroku,首先安装家酿,如果你还没有的话,然后在新的终端窗口中键入:

brew tap heroku/brew && brew install heroku

如果你有一个 M1 芯片,你可能会得到一个错误,但这将有助于解决问题:【https://support.apple.com/en-us/HT4

对于 Windows 用户,使用以下链接下载 Heroku CLI:https://dev center . Heroku . com/articles/Heroku-CLI #下载并安装

一旦安装了这些依赖项,就可以将代码推送到 Github 了。首先创建一个. gitignore 文件,将虚拟环境文件从 Github repo 中排除,然后通过键入 virt_env/(在做出更改后保存文件)来修改它:

(virt_env) > touch .gitignore # Mac
(virt_env) > echo.>.gitignore # Windows

现在,在命令行窗口中初始化 git,通过 Git 激活您的虚拟环境

git init
git add -A
git commit -m "initial commit"

登录您的 Github 帐户,创建一个新的存储库,然后选择“从命令行推送现有的存储库”选项将该选项中的代码片段复制并粘贴到运行虚拟环境的命令行中。

为了结束这个阶段,通过 pip freeze 创建一个需求文件。这告诉 web 服务器成功运行应用程序需要哪些依赖项

python -m pip freeze > requirements.txt

在名为 setup.sh 的虚拟环境目录中创建新文件,并将以下内容保存到其中:

mkdir -p ~/.streamlit/echo "\[server]\n\headless = true\n\port = $PORT\n\enableCORS = false\n\\n\" > ~/.streamlit/config.toml

然后在虚拟环境目录中创建一个 Procfile(就叫它 Procfile)并将以下内容保存到其中:

web: sh setup.sh && streamlit run covid_dashboard.py

现在是时候将 requirements.txt、Procfile 和 setup.sh 文件添加到 Github repo:

git add -A
git commit -m "files for Heroku deployment"
git push -u origin main

将应用程序部署到 Heroku:

要将应用程序部署到生产环境,请在运行虚拟环境的命令行中键入以下内容:

(virt_env) > heroku login
(virt_env) > heroku create

这将创建一个随机的 url 名称并部署您的应用程序。这将需要一两分钟的时间来完成,但是您应该会在命令行中看到一个类似于此处的 url。你可以把它复制到你的网络浏览器里。

总结:

读完这篇文章后,我希望代码困在您的计算机上的日子已经成为过去,您的组织中的人们开始从您的创新中受益。

快速浏览 Jupyter 笔记本电脑(就在您的终端中)

原文:https://towardsdatascience.com/rapidly-explore-jupyter-notebooks-right-in-your-terminal-67598d2265c2

优化笔记本搜索

作者:阿玛尔哈斯尼 & 迪亚赫米拉

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者截图

想象一下,你有一个代码样本,埋藏在一个旧的 Jupyter 代码中,却不记得是哪一个了。您可以使用两种简单的解决方案:

1.旋转 Jupyter 实例并手动搜索所有笔记本
2。在文本编辑器中打开笔记本,搜索特定文本

这两种方式都不理想,因为:

  1. 在 Jupyter 中加载每个笔记本并进行搜索需要时间

2.JSON 格式的大笔记本可读性不强

如果有第三种方法可以解决这两个问题,并且你可以在不离开终端的情况下使用它,那会怎么样?

认识一下 nbmanips,这是一个 python 包,我们专门为处理、探索和转换笔记本而创建。

💡声明:这是我和 Dhia Hmila 创建的一个包。

**Table Of Contents:** 
. [1 — How to Install](#667f)
. [2 — nbmanips in Python](#da52)
  ∘ [2.1 — Reading a notebook](#1fcb)
  ∘ [2.2 — Filtering/Selecting Notebook Cells](#2662)
  ∘ [2.3 — Handling Multiple Notebooks](#8dd6)
. [3 — nbmanips in the Terminal](#3660)

1 -如何安装

如果使用 pip,安装 nbmanips非常简单:

pip install nbmanips

通过该软件包,即使您的 Jupyter 笔记本内容包含图像,也可以在您的终端上显示。是的,你没看错!如果您安装以下可选要求,它将呈现包含的图像:

pip install nbmanips[images]

如果你愿意,你可以用你自己的笔记本文件测试下面的库。但是如果你需要测试笔记本,这里有一个很棒的 Git 资源库,里面有超过 30 个机器学习相关的笔记本。

Python 中的 2 - nbmanips

2.1 -阅读笔记本

阅读 Jupyter 笔记本非常简单:

一旦你读完了笔记本,你可以用 show方法把它打印到屏幕上:

nb.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

您可以选择不同的展示风格:

# Possible values: ['single', 'double', 'grid', 'separated', 'rounded', 'dots', 'simple', 'copy']
nb.show(style='double')

💡提示: copy风格非常适合从终端复制/粘贴代码😉

📓其他有趣的参数有:

  • width:单元格的宽度
  • exclude_output:如果想要隐藏单元格的输出,则为 True。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.2 -过滤/选择笔记本单元格

现在,我们有了一种不用离开终端就能以可读的方式可视化我们的笔记本的方法

然而,Jupyter 笔记本可以有大量的电池,我们不一定想在屏幕上看到所有的电池。

这就是 Selectors发挥作用的地方。它们用于指定我们将在哪些单元格上应用给定的操作(如显示或删除)。在我们的示例中,操作只是显示单元格。

要选择要应用前面操作的单元格,可以使用:

1。使用索引过滤

2。使用预定义选择器进行过滤

nbmanips中可用的预定义选择器如下:

  • code_cells / markdown_cells / raw_cells:选择给定类型的单元格
  • contains:选择包含某个文本/关键字的单元格
  • is_empty / empty:选择空单元格
  • has_output:检查电池是否有输出
  • has_output_type:选择具有给定输出类型的单元格
  • has_byte_size:选择字节大小在给定值范围内的单元格。这对于过滤掉包含大图像的单元格特别有用。
  • has_html_tag:选择具有特定 HTML 标签的减价单元格
  • has_tag:选择具有某个单元格标签的单元格。对于那些不知道它的人来说,可以给 Jupyter 笔记本单元格添加一个标记:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者截图

  • has_slide_type:选择具有给定幻灯片类型的单元格
  • is_new_slide:选择新幻灯片/子幻灯片开始的单元格:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者截图

3。使用用户自定义函数过滤

该函数采用一个 Cell对象,如果单元格应该被选中,则返回 True:

4。组合过滤器

  • 组合选择器的第一种方法是简单地使用一个列表。

  • 通过链接 select 语句来组合选择器:

nb.select('markdown_cells').select('is_empty').show()
  • 使用二元运算符组合选择器:

2.3 —处理多台笔记本电脑

现在我们知道了如何在每个笔记本中显示特定的单元格,让我们将它应用于给定文件夹中的所有笔记本。

终端中的 3 - nbmanips

nbmanips还附带了一个 CLI,因此我们之前看到的所有内容都可以在几行代码中完成。

例如,要预览一个笔记本,您可以运行 show子命令:

nb show nb.ipynb

如果你想看到可用的参数,你可以通过 --help选项:

您可以使用过滤器,通过以下方式进行管道连接:

您也可以使用 --help选项获取更多信息:

最后的想法

Jupyter 笔记本对于交互式探索数据或快速尝试新想法非常有帮助。然而,一旦你完成了它们,你经常会留下一个快速和肮脏的代码。

nbmanips试图提供一种快速探索代码的方法,同时也能轻松地重组单元格和改造笔记本。

请继续关注我们的下一篇文章,了解如何拆分和合并不同的笔记本。

你可以在这个 GitHub 库中找到所有的 Python 脚本。如果您有任何问题,请不要犹豫,在回复部分留言,我们将非常乐意回答。

感谢您坚持到现在,注意安全,我们将在下一篇文章中再见!😊

更多文章阅读

</8-tips-to-write-cleaner-code-376f7232652c> 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

人工智能生成的图像(稳定扩散)

自然语言处理任务中模糊匹配的原始文本校正

原文:https://towardsdatascience.com/raw-text-correction-with-fuzzy-matching-for-nlp-tasks-828547742ef7

了解如何修复拼写错误的单词,以便更好地识别重要的文本表达式

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Diomari Madulara 在 Unsplash 上拍摄的照片

今天,自然语言处理(NLP)被用于医疗保健、金融、营销等领域的许多 ML 任务和项目中。数据科学家经常努力清理和分析文本数据,以便获得洞察力。对于大多数自然语言处理任务,通常使用诸如标记化、词干化、词汇化等技术。

但是,在某些情况下,需要保持原始文本的完整性,而不是将其拆分成标记。例如,在作为命名实体识别(NER)的私人情况的数据去标识中,一种用于识别文档中不同实体的方法,该方法的输出显示原始文本,其中标签替换期望的实体。

在这些情况下,纠正拼写错误或错误的术语可能会很有挑战性。这篇文章将解释如何结合使用正则表达式和模糊字符串匹配来完成这项任务。

模糊字符串匹配

模糊字符串匹配是一种查找与给定字符串模式近似匹配的字符串的技术。模糊字符串匹配背后的算法使用距离度量(如 Levenshtein 距离)来计算两个字符串之间的差异,方法是确定将第一个字符串转换为第二个字符串所需的最少更改次数。我们将使用 python 库 Fuzzywuzzy 来执行这项任务。

安装和示例:

pip install fuzzywuzzyfrom fuzzywuzzy import fuzzfuzz.ratio("appls","apples")#91

在这个例子中,我们得到了 91 分的相似度,所以单词非常相似。现在,我们可以考虑使用什么阈值来决定是否“纠正”原始单词。

正则表达式

RegEx 是正则表达式的缩写,是一种特殊的文本字符串,用于指定文本中的搜索模式。这种模式基本上是一种语言,它确切地定义了在文本字符串中要寻找什么。例如,如果我们想提取除数字以外的所有字符,正则表达式模式将是:

[^0-9]+

如果我们想要提取所有的电子邮件地址,正则表达式模式将是:

[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}

我们将使用 python 库 re 来执行这项任务。

安装和示例:

pip install reimport restring = "my e-mail is example@email.com"pattern=r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}'print(re.search(pattern,string).group())# example@email.com

原始文本校正

回到最初的问题,当我们需要修复错误的单词或短语,但保持原始文本完整,而不是将其拆分为标记时,该怎么办。保持文本完整也意味着保持完全相同的间距、制表符、换行符、标点符号等。

假设在一个 NER 任务中,我们想要标记医院里给病人的药物。该信息可在电子健康记录(EMR)系统的医生笔记部分获得。例如,这里有一个条目:

病人 XXX 上周住院了。

他被赋予了勇气。

他是男性,65 岁,有心脏病史。

该药物的正确名称是“莫昔普利”。为了纠正这一点,我们需要:

  1. 准备一个我们想要搜索的关键字列表,在这种情况下只有一个关键字
  2. 决定相似性阈值(默认为 85)
  3. 将文本拆分成标记
  4. 在关键字和每个标记之间运行模糊匹配
  5. 如果相似性得分超过预定阈值,则用关键字替换标记
  6. 把它们放回原处

我们可以使用如下函数来实现这一点:

from fuzzywuzzy import fuzz
import redef fuzzy_replace(keyword_str, text_str, threshold=85):
    l = len(keyword_str.split())
    splitted = re.split(r'(\W+)',text_str) #split, keep linebreaks
    for i in range(len(splitted)-l+1):
        temp = "".join(splitted[i:i+l])
        if fuzz.ratio(keyword_str, temp) >= threshold:
            before = "".join(splitted[:i])
            after = "".join(splitted[i+l:])
            text_str= before + keyword_str + after
            splitted = re.split(r'(\W+)',text_str)    
    return text_str

运行此函数后,文本输出现已得到纠正,文本原始结构得以保留:

病人 XXX 上周住院了。

他被给予莫昔普利。

他是男性,65 岁,有心脏病史。

现在让我们看一个更复杂的例子。这一次,医疗记录中包含了一些我们想要纠正的不同的药物,而且有些在文本中出现了不止一次。为了解决这个问题,我们定义了一个包含所有正确药物名称的列表,并简单地遍历文本以找到需要纠正的内容。以下代码片段显示了如何实现这一点:

meds = ["moexipril", "vasotec", "candesartan"] text = """The patient XXX was hospitalized last week.He was given moxiperil and vasotek.He is male, 65 years old, with a history of heart disease.Patient has been taking vasotek for several years.In the past was given candasarta.""" for med in meds: text = fuzzy_replace(med, text)

结果是所有药物名称均已更正的相同文本。

病人 XXX 上周住院了。

他接受了莫昔普利和 vasotec 治疗。

他是男性,65 岁,有心脏病史。

患者服用 vasotec 已有数年。

曾被给予坎地沙坦。

我已经决定不包括强制转换成小写的文本,因为有些时候人们希望保持原来的大小写,例如识别缩写。然而,这可以通过将参数的小写形式输入到函数中很容易地完成,就像这样— fuzzy_replace(med.lower(),text.lower())

结论

我们可以使用模糊字符串匹配和正则表达式的组合来纠正错误的单词或短语,并保持原始文本不变。当处理不同的 NLP 任务(如 NER)时,这种操作是可取的。

rbokeh:如何在 R 中创建交互式情节

原文:https://towardsdatascience.com/rbokeh-how-to-create-interactive-plots-in-r-cf8fd528b3d5

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

布拉姆·瑙斯Unsplash 上拍摄的照片

简介

数据可视化是人工智能的一个重要方面。数据可视化使您能够从数据中获得洞察力,并使您能够与他人就数据进行交流。我们可以使用许多软件包来可视化数据并构建有意义的仪表板。在用 Python 做可视化的时候,我们有不同的库,比如 Matplotlib,Seaborn,Altair 等等。,和 ggplot2,使用 r 时晶格。

数据可视化中的交互性使它们更上一层楼。当用户可以使用不同的操作与图表进行交互时,如缩放、悬停和使用选择的变量过滤绘图,这为图表增加了很大的灵活性。这种交互性允许用户更深入地挖掘可视化以在数据集中找到附加信息。使用不同的工具在仪表板中提供交互式可视化是当今的常见做法。

Python 和 R 都有构建交互情节的包。Bokeh 是一个流行的交互式可视化 python 包。散景库允许我们用很少的代码行创建不同的图,为高级定制增加了灵活性。Bokeh 现在有一个 R 接口以及 Python、Scala 和 Julia 的现有接口。

rbo keh 是什么?

rbokeh 是一个开源的 R 包,它利用了 bokeh 可视化工具。它提供了一个声明性的界面,对于基于 web 的动态可视化是灵活的。Ryan Hafen 创建并维护 rbokeh 包。你可以在这里找到更多关于 rbokeh 包的细节。在安装 rbokeh 之前,你必须安装 R and R 工作室。还可以用 Kaggle 或 Google Colab 构建 rbokeh 可视化。

安装

首先,我们将使用 R 函数 install。packages()'从 CRAN 获取 rbokeh 包。

install.packages(“rbokeh”)

我们将使用以下命令来导入 rbokeh 包:

library(“rbokeh”)

对于 rbokeh 包,我们还将导入其他 R 库

library(“tidyverse”)
library(MASS)

使用 rbokeh 的可视化

在进行图形可视化之前,需要注意的是 rbokeh 图是通过调用figure()函数生成的。

这个函数相当于一个可以设置的空白画布,然后可以使用管道操作符添加层。这里 x、y 和 ly_geom()是指定所用 geom 类型的数据输入,如 ly_points、ly_lines、ly_hist、ly_boxplot 等。

对于本教程,我们将从 r 的质量包中加载内置的 cars93 数据集

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由克雷格·冯尼克Unsplash 上拍摄

Cars93 是一个 93 行 27 列的数据集。关于封装和文档的更多细节可以在这里找到。数据集中的汽车是从发表在《消费者报告》和《PACE 购买指南》上的 1993 款乘用车模型中随机选择的。

使用以下命令,我们可以打印“Cars93”数据集的前几行:

head(Cars93)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

散点图

我们将首先构建一个简单的散点图。在这里,我们将看到不同汽车的马力和价格之间的关系。散点图有助于可视化两个变量之间的关系,因此,是可视化所有数据点的好选择。首先,我们将创建一个“figure()”函数。然后,我们将创建一个名为“ly_points”的图层,并将以下参数传递给数据参数:-x 轴上的马力-y 轴上的价格-以及数据集,即 Cars93。请注意,我们可以将生成的图形分配给“散点图”,然后通过写“散点图”来显示它。我们需要使用hover命令来查看添加的工具提示。平移和缩放也可以作为交互式元素访问。

#Simple Scatter Plotscatter_plot <- figure(title ="Scatter Plot") %>%
  ly_points(x = Horsepower, y = Price, data = Cars93, hover = c(Horsepower, Price))
scatter_plot

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:作者

为了构建一个散点图,除了显示与马力和价格的关系之外,还显示每个原点的一个点,我们将按颜色对其进行分组,如下所示。

#Scatter Plot with groupingscatter_plot1 <- figure(title ="Scatter Plot") %>%
  ly_points(x = Horsepower, y = Price, color = Origin, data = Cars93, hover = c(Origin, Horsepower, Price))
scatter_plot1

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:作者

折线图

接下来,我们将使用 Cars93 数据集绘制一个基本的折线图。假设我们想要查看小型和紧凑型汽车价格的变化,我们将首先使用类型变量过滤数据。

#Filteringdf <- Cars93 %>%
filter(Type %in% c(“Small”, “Compact”))

然后对于线图,我们将使用价格变量,并用“ly_lines”层中的数据参数指定它,如下所示:

#Line Plotline_plot <- figure(title =”Line Plot”) %>%
ly_lines(Price,color=Type,data = df)
line_plot

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:作者

我们可以改变线条的粗细和颜色。根据默认主题为所有这些地块选择颜色。我们可以用颜色向量或预定义的调色板调用set_palette()函数来获得我们想要的颜色。这里我们将使用 discrete color 属性,因为我们正在处理分类值,为了获得更粗的线条,我们正在更改 width 参数的设置,如下面的代码所示。我们也可以使用十六进制代码的颜色或任何我们在这里选择的 CSS 颜色。

#Changing the color and width of linesline_plot2 <- figure(title =”Line Plot”) %>%
ly_lines(Price, color = Type, width = 2, data = df)%>%
set_palette(discrete_color = pal_color(c(“#6200b3”, “#ff0831”,”#226f54")))
line_plot2

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:作者

现在,我们将探讨如何通过在一个图形中组合点图层和线图层来可视化多层绘图。对于多层绘图,我们将使用ly_lines() 绘制线条,并使用ly_points()添加标记。注意,在这两层中,“类型”都被映射到颜色。可以为图形使用不同的字形。我们可以使用下面的命令来探索字形可用的可能值。

point_types()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:作者

当在代码中指定一个带编号的标志符号时,fill 和 line 属性将得到有效的管理,以获得所需的结果。

#Multi-layer plot with glyphmulti_layer <- figure(df, legend_location =”top_right”, title =”Multi-layer Plot”) %>%ly_points(Price, color = Type, hover = c(Price,Type),glyph=16) %>%ly_lines(Price, color = Type, width = 1.5, data = df) %>%set_palette(discrete_color = pal_color(c(“#6200b3”, “#ff0831”,”#226f54")))multi_layer

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:作者

直方图

接下来,我们将绘制一个直方图来查看 Cars93 数据集中的 RPM 频率。

#Simple Histogramhistogram <- figure(width = 700, height = 400, title =”Histogram”) %>%
ly_hist(RPM, data = Cars93, breaks = 5, freq = TRUE,color=”green”)
histogram

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:作者

方框图

现在,我们将使用以下代码创建一个方框图来显示马力和气缸之间的关系。我们还可以通过箱线图发现数据集中的一些异常值。

#Box-Plotbox_plot <- figure(width = 600, title =”Box Plot”) %>%
ly_boxplot(Cylinders, Horsepower, data = Cars93)
box_plot

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:作者

条形图

我们将在下一个柱状图中绘制各种类型的汽车,即分类数据。

#Bar Chartbar_chart <- figure(title =”Bar Chart”) %>%
ly_bar(Type, data = Cars93) %>% theme_axis(“x”, major_label_orientation = 90)
bar_chart

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:作者

网格图

我们可以使用网格图在一个布局中显示不同的图形。我们可以将不同类别的数字组合起来,如条形图、折线图、散点图等。在网格布局中。

#Grid Plottools <- c(“wheel_zoom”, “box_zoom”, “box_select”)
p1 <- figure(tools = tools, width = 500, height = 500) %>%ly_points(Horsepower, RPM, data = df, color = Type,hover=c(Horsepower, RPM,Type))p2 <- figure(tools = tools, width = 500, height = 500) %>%ly_points(Length, Wheelbase, data = df, color = Type,hover=c(Length, Wheelbase,Type))grid_plot(list(p1, p2), link_data = TRUE)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:作者

Hexbin 图

最后,我们绘制了一个赫克斯宾图,这是一个 2D 密度图,以可视化两个数字变量之间的关系,即价格和发动机大小。当数据包含大量点时,Hexbin 图通常用于可视化。绘图窗口用几个十六进制箱分割,以避免重叠。

hexbin <- figure() %>% ly_hexbin(x = EngineSize, y = Price, data = Cars93)
hexbin

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:作者

就是这样!我们从头开始在 rbokeh 中创建和定制了各种图表。

结论

在这个简短的教程中,我们探索了如何在 rbokeh 中绘制一些交互式的、令人愉快的图表。这个 rbokeh 教程的完整代码可以在我的 GitHub 资源库中找到。如果您喜欢使用 ggplot,那么您可能以前使用过 plotly 或 ggiraph 来使您的 ggplot 图表具有交互性。对于您的下一个可视化项目,rbokeh 包可能是一个不错的选择。

参考文献:

罗宾·h·洛克(1993) 1993 年新车数据,统计教育杂志,1:1,DOI:10.1080/10691898 . 1993 . 11910459

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值