TowardsDataScience 博客中文翻译 2020(八百二十五)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

基于张量流概率的结构时间序列预测:铁矿产量

原文:https://towardsdatascience.com/structural-time-series-forecasting-with-tensorflow-probability-iron-ore-mine-production-897d2334c72b?source=collection_archive---------9-----------------------

张量流概率结构时间序列的贝叶斯结构时间序列预测

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

图片:https://unsplash.com/photos/-subrrYxv8A

介绍

IRon 矿石 是世界上交易量最大的商品之一。作为钢铁生产的主要投入,它为全球最大的金属市场的交易提供了基础,并在全球干散货贸易中占据最大份额。

不出所料,铁矿石生产始于矿山。作为实物或金融交易商,了解铁矿石市场的基本供需性质至关重要。铁矿石等级(质量)差异不仅对现货和远期合约定价有显著影响,而且对工厂的杂质罚款也有显著影响。基本供求关系的失衡可能导致铁矿石价格大幅上涨。预测最大铁矿石出口国的铁矿石产量以便预测全球铁矿石供应,这在投机现货、期货和污染物罚款价格变动时非常有用。

问题

在本文中,我们将使用 TensorFlow Probability 的结构时间序列 (STS)框架开发一个预测模型,以预测巴西主要铁矿的总产量。

巴西是全球第二大铁矿石出口国。如上所述,巴西供应的重大变化会对铁矿石价格产生影响。此外,巴西铁矿石通常品位很高,杂质含量很低。因此,巴西铁矿石的相对供应会对钢厂收取的杂质罚款价格产生影响。如果由于巴西矿山供应短缺,高污染铁矿石主导了全球供应,污染物的价格惩罚可能会急剧上升。因此,预测巴西的产量有助于理解上述动态。

本文中使用的代码遵循类似于 tensor flow Probability tutorial(tensor flow 作者版权所有 2019)中的结构时间序列建模中概述的逻辑。

为什么要张量流概率 STS?

当处理一个时间序列预测问题时,投入时间去理解你想要预测的变量的复杂性是至关重要的。在设计任何模型的架构之前,平稳性、季节性、分布和外生特征关系只是要记住的许多考虑因素中的一小部分。

结构时间序列模型(有时称为贝叶斯结构时间序列)表示为趋势、季节模式、周期和残差等组件的总和:

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

这些单独的组成部分本身是由结构假设定义的时间序列。在时间序列中配置每个组件的能力使得 TFP 的 STS 库在我们的时间序列预测问题的上下文中特别相关,因为它使我们能够将特定领域的知识编码到我们的模型中,例如交易者和采矿操作员的专业知识以及已知事件。

这对我们的问题有什么帮助

矿山产量通常表现为系统行为,可以建模为结构时间序列。就我们的问题而言,我们知道许多矿井在 12 月会关闭两周进行定期维护。这种可重复的模式可以作为季节性组件添加到结构化时间序列模型中。我们还知道,铁矿石和其他露天采矿呈现出明显的季节性模式,与降雨量密切相关,在暴雨期间,排水泵不堪重负,导致产量减少。

在我们的问题的背景下,结构时间序列模型特别有用的是,它们采用概率方法来模拟时间序列问题,即,它们返回后验预测分布,我们可以对其进行采样,不仅提供预测,还提供量化模型不确定性的方法。

STS 模型的另一个令人兴奋且非常有用的特性是,最终的模型可以被分解为多个独立组件的集合。然后可以绘制这些组件,让我们深入了解它们各自对因变量(Y-hat)的影响,并更深入地了解全局时间序列问题:

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

时间序列分解示例摘自https://blog . tensor flow . org/2019/03/structural-time-series-modeling-in . html

先决条件

在我们开始之前,值得注意的是张量流概率有一组特定的依赖关系。此外,张量流作为张量流概率库的依赖项,需要单独安装:

[## 释放张量流/概率

今天就创建您的免费 GitHub 帐户,订阅这个新版本库,并与 50…

github.com](https://github.com/tensorflow/probability/releases)

pip install --upgrade tensorflow-probability

或者,你可以使用谷歌的合作实验室 (Colab),他们友好地在 Colab 中完全免费地提供托管运行时(CPU、GPU 和甚至 TPU!)受制于内存限制。

结构时间序列模型

让我们开始吧。

数据

我们首先检查我们的矿山负荷(产量)数据,其观察值是每个矿山的每周总产量(百万公吨),汇总了所有主要的巴西铁矿石生产商:

# Imports
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import seaborn as sns
import tensorflow_probability as tfp
import tensorflow as tffrom statsmodels.tsa.seasonal import seasonal_decomposedf = pd.read_excel(
    '/content/bloomberg_weekly_io_output_brazil.xlsx',
    header = 1, 
    index_col = 0,
    parse_dates = True)# Loadings
df.plot(figsize=(16, 8))
plt.title(‘Weekly Iron Ore Output, Metric Tonnes’)
plt.ylabel(‘Loadings, Mt’)

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

每周矿山装载量(百万吨)

如果我们仔细检查观察到的输出时间序列,我们可以隐约看到本文前面提到的一些结构组件,我们可以尝试在 STS 模型中进行编码:

  • 明显的季节性模式。从每个周期的振幅和频率来判断,有理由认为这是加性季节性(定义如下)。
  • 2018 年末/2019 年初除外,呈线性趋势。
  • 在圣诞节期间,当矿井通常关闭进行定期维护时,产量明显下降。
  • 噪音程度,可能与天气、罢工、设备故障等相关。

时间序列分解

我们可以通过尝试将时间序列分解成其组成部分来验证时间序列中的某些上述成分。我们将使用 statsmodels 时间序列分析库来执行分解,并根据我们的时间序列图中观察到的行为选择一个“附加”模型作为季节成分。作为参考,附加季节性估计为:

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

将“iron _ ore _ Brazil”Pandas 系列传递给我们从 statsmodels 导入的 seasonal_decompose 方法会生成以下图形:

tsa = seasonal_decompose(
    df[‘iron_ore_brazil’],model=’additive’
).plot()

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

铁矿石产量吨—季节性分解

通过检查,我们可以立即识别(大部分)线性趋势和季节性成分。如果我们仔细观察频率和幅度,你可以看到在冬季期间每年的矿井产量减少。

通过对残差的检验,很明显,在这个时间序列中存在其他关系,这些关系不能仅由季节和趋势元素来解释。然而,残差中的方差保持相当恒定,并且在定义的界限内。在定义我们的结构组件时,这是需要牢记的有用信息。

“啊哈!”我听到你在这一刻哭泣。“如果这是 STS 模型的,为什么要分解时间序列?”。有两个原因:

  1. 它有助于识别和理解时间序列中的各种基本成分:趋势、季节性、周期和无法解释的方差,这反过来可以告诉我们如何配置 STS 模型先验的输入参数。
  2. TFP 的 STS 模型通过变分推理 (VI)或哈密顿蒙特卡罗(HMC) 方法对数据进行训练;
# Fit model to observed data with HMC
tfp.sts.fit_with_hmc(
    model, observed_time_series, num_results=100, num_warmup_steps=50,
    num_leapfrog_steps=15, initial_state=None, initial_step_size=None,
    chain_batch_shape=(), num_variational_steps=150, variational_optimizer=None,
    variational_sample_size=5, seed=None, name=None
)# Or, fit model to data with VI
tfp.sts.build_factored_surrogate_posterior(
    model, batch_shape=(), seed=None, name=None
)

一般来说,这两种方法在高维问题上都是计算密集型的(特别是在 HMC 的情况下),并且对调整非常敏感。在配置 STS 组件时,明智地选择输入参数有助于节省时间和资源,并提高后验分布的准确性。

解释 HMC 和 VI 背后的数学原理超出了本文的范围,但是你可以在这里找到更多关于 HMC 和 VI 的信息。

定义我们的结构组件

我们现在可以定义 STS 模型的各种组件,并根据我们对数据生成过程的了解来配置它们。让我们从季节性因素开始:

季节性:

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

铁矿石产量(吨):季节性因素

我们将结构时间序列模型的季节性输入定义如下:

# Create train dataset
_train = df[‘iron_ore_brazil’][df.index < ‘2018–01–01’]
_dates = train.index# Test data
test = df[‘iron_ore_brazil’][df.index >= ‘2018–01–01’]# TensorFlow requires an an (N, 1) float tensor
train = _train.to_numpy().reshape(-1, 1))# Seasonal effect 1: weekly cycle as identified in decomp.
weekly_cycle = tfp.sts.Seasonal(
    num_seasons=52, # 52 weeks in year
    observed_time_series=train,
    allow_drift=True,
    name=’weekly_effect’)# Seasonal effect 2: month of year to capture winter drop in output.
monthly_affect = tfp.sts.Seasonal(
    num_seasons=12, # 12 months in year
    num_steps_per_season=4, # assumed 4 weeks in every month
    observed_time_series=train,
    name=’month_of_year_effect’)

tfp.sts.Seasonal 模型提供的一个有价值的特性是能够将*【漂移】*添加到季节性影响中。此参数允许每个季节的影响按照高斯随机游走从一个事件演变或“漂移”到下一个事件(具体来说,样本取自由平均值和方差加上一些漂移项定义的正态分布)。如果我们对季节成分的先验分布的均值和方差有信心,我们可以在模型中自己配置它:

monthly_effect = tfp.sts.Seasonal(
    num_seasons=12, # 12 months in year
    num_steps_per_season=4, # assumed 4 weeks in month
    observed_time_series=train,
    drift_scale_prior=tfd.Normal(loc=1., scale=0.1), # define priors
    initial_effect_prior=tfd.Normal(loc=0., scale=5.),
    name=’month_of_year_effect’)

现在,通过设置参数“allow_drift=True ”,我们可以让模型为我们处理这个问题。

趋势分量

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

铁矿石产量(吨)趋势成分

对我们铁矿产量的目测显示(2018 年末/2019 年初除外)一致的线性趋势。我们可以用一个localineartrend模型来模拟这种行为。

局部线性趋势模型将时间序列趋势表示为某个量级(level)和slope.的组合,这些元素中的每一个都通过高斯随机游走随时间演变:

level[t] = level[t-1] + slope[t-1] + Normal(0., level_scale)
slope[t] = slope[t-1] + Normal(0., slope_scale) 

在我们的问题中,局部线性趋势组件的实现非常简单:

# Add trend
trend = tfp.sts.LocalLinearTrend(
    observed_time_series=train,
    name='trend')

在选择如何对趋势组件建模时,需要牢记的一个重要考虑因素是模型的选择,这取决于问题的性质。在我们的时间序列问题的情况下,观察到的趋势随着时间的推移相对稳定,并逐渐演变,即它没有显示任何强烈的非线性行为。因此,我们选择代表这种趋势的模型是合理的,但是这种模型可以在更长的预测期内产生高度不确定性的预测。

替代方案是什么?

众所周知,大多数时间序列都具有固有的时间结构,其中后续观测依赖于时间上的前 n 个观测,即自相关。因此,明智的选择可能是使用 TFP STS 的SemiLocalLinearTrend模型来模拟趋势。在半局部线性趋势模型中,slope分量按照一阶自回归过程演化。因此,AR 过程可以解释时间序列中的自相关(数量级为 n )效应,并且通常会导致更长时间内更确定的预测。

残差:

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

铁矿石产量(吨)剩余部分

如前所述,在我们检查季节分解图的过程中,我们的时间序列中的残差看起来相对一致,表明它们可能是稳定的,即它们在一段时间内保持恒定的方差,不表现出偏差或异方差等。因此,我们可以在 sts 中表示剩余行为。 自回归 模型:

*# Residuals
residuals = tfp.sts.Autoregressive(
    order=1,
    observed_time_series=train,
    coefficients_prior=None,
    level_scale_prior=None,
    initial_state_prior=None,
    name='residuals_autoregressive')*

与其他组件一样,对于完全贝叶斯方法,应该指定先验coefficients_priorlevel_scale_priorinitial_state_prior。由于我们没有指定先验,tensor flow Distributions(tfd)MultivariateNormalDiag 实例用作系数的默认先验,并基于输入时间序列为级别和初始状态构建启发式先验。

定义模型

我们现在可以使用 tfp.sts.Sum 类来定义我们的结构化时间序列模型。这个类使我们能够从上面定义的组件中定义结构化时间序列模型的组合规范:

*model = tfp.sts.Sum(
    components=[
        trend,
        weekly_cycle,
        monthly_effect,
        residuals],
    observed_time_series=train)* 

拟合模型

**我们现在将我们的模型拟合到观察到的时间序列,即我们的铁矿石产量。不同于传统的时间序列预测架构,例如线性回归模型,其通过最大似然估计来估计系数,或者,在规模的更强大的一端,LSTM 学习将一系列过去的观察值作为输入映射到输出观察值的函数,STS 模型学习 分布 即后验分布。

我们将使模型与数据相匹配,并使用变分推理建立后验预测分布。简而言之,VI 拟合了我们定义的模型参数的一组近似后验分布(针对每种成分),并通过最小化称为负证据下限(ELBO)的变分损失函数来优化这些分布:

*# VI posterior 
variational_posteriors = tfp.sts.build_factored_surrogate_posterior(
    model=loadings_model)# Build and optimize the variational loss function (ELBO).
@tf.function()
def train_sts_model():
  elbo_loss_curve = tfp.vi.fit_surrogate_posterior(
      target_log_prob_fn=loadings_model.joint_log_prob(
      observed_time_series=training_data),
  surrogate_posterior=variational_posteriors,
  ptimizer=tf.optimizers.Adam(learning_rate=.1),
  num_steps=200)
  return elbo_loss_curve# Plot KL divergence
elbo = train_sts_model()()
plt.plot(elbo_loss_curve)
plt.show()*

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

证据图下限优化(ELBO 最小化 Kullbeck-Leibler 分歧)

我们当中精明的人可能已经注意到了装修工。它接受一个函数,在这个例子中是我们的 STS 模型,作为一个参数,并将它编译成一个可调用的 TensorFlow 图。关于 TensorFlow 如何工作和通过使用图形处理操作的有趣介绍可以在这里找到。

预测

现在是有趣的部分。在检查损失函数收敛后,我们可以进行预测。我们从变分后验数据中提取轨迹(样本),并通过将这些作为参数传递给[tfp.sts.forecast(](https://www.tensorflow.org/api_docs/python/tf/function))来构建预测。给定我们的模型、观察到的时间序列和我们的采样参数, forecast() 会针对所需数量的预测步骤返回未来观察值的预测分布:

*# Draw traces from posterior
traces__ = variational_posteriors.sample(50)# No timesteps to forecast
n_forecast_steps = len(test)# Build forecast distribution over future timesteps
forecast_distribution = tfp.sts.forecast(
    loadings_model,
    observed_time_series=train,
    parameter_samples=traces__
    num_steps_forecast=n_forecast_steps)# Draw fcast samples
num_samples=50# Assign vars corresponding to variational posterior
fcst_mu, fcast_scale, fcast_samples=(
    forecast_distribution.mean().numpy()[..., 0],
    forecast_distribution.stddev().numpy()[..., 0],
    forecast_distribution.sample(num_samples).numpy()[..., 0])*

然后我们可以想象我们的预测:

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

铁矿石产量预测值与观察值(吨)

验证模型性能

乍一看,我们可以观察到该模型(除了 2019 年的不可抗力)在预测我们的矿山产量方面表现相当合理。通过计算平均绝对误差,我们可以根据观察到的数据点验证我们预测的准确性。然而,从商业角度来看,考虑到标签的规模,即以百万吨为单位,计算和呈现平均百分比误差(MAPE)更有意义,因为这对非数据科学人员来说更具可解释性和意义:

*# Remember to check for div by zero errors first
print(np.mean(np.abs((test- fcast_mu) / test)) * 100)>>> 13.92*

在 13.92% MAPE 的情况下,很明显,我们的铁矿石产量模型在模拟数据生成分布方面做得很好,尤其是在我们没有完全调整模型参数的情况下。

验证结构组件贡献

我们可以进一步了解我们的模型的拟合,并通过将其分解为各自的时间序列(再次)来验证我们的单个结构组件的贡献。我们使用[tfp.sts.decompose_by_component](https://www.tensorflow.org/probability/api_docs/python/tfp/sts/decompose_by_component)来执行该操作,该操作返回单个分量后验分布的collections.OrderedDict,映射回其各自的观察模型:

*# Dist. over individual component outputs from posterior (train)
component_dists = tfp.sts.decompose_by_component(
    loadings_model,
    observed_time_series=train,
    parameter_samples=traces__)# Same for fcast.
forecast_component_dists = tfp.sts.decompose_forecast_by_component(
    loadings_model,
    forecast_dist=loadings_model_distribution,
    parameter_samples=traces__)*

经过一点处理后,我们可以绘制分解时间序列的输出,得到下面的图表:

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

按成分、训练和预测分布的铁矿石结构时间序列分解

观察、批评&进一步分析

对我们的分解模型的检查为进一步分析提出了一些要点:

  1. 我们预测后验分布的均值和标准差为我们提供了每个时间步的边际不确定性。考虑到这一点,我们可以观察到,我们的模型对我们的趋势成分对时间序列的贡献充满信心,展示了直到 2018 年模型不确定性复合的稳定轮廓。为了应对这一点,我们可以通过将其建模为 半对数趋势 模型来提高趋势分量的准确性,这允许slope分量作为自回归过程发展。**
  2. 我们的模型对每周每月的季节性成分贡献有信心,在每个时间步显示一致的输出,表明我们的配置是正确的。虽然一致,但我们的月度部分的不确定性明显更高。我们有可能通过增加知情的先验知识(即initial_state_prior等)来改进这些组件。**
  3. 有趣的是,我们的自回归成分贡献很小。我们的模型对此也相当有信心,表明残差不能用 AR 过程来解释。我们可以探索使用 sts 添加外部特征**,如当地天气/降水模式和铁矿石现货价格作为线性协变量。线性回归** **
  4. 我们可以通过使用马尔可夫链蒙特卡罗(MCMC)方法(即[tfp.sts.fit_with_hmc()](https://www.tensorflow.org/probability/api_docs/python/tfp/sts/fit_with_hmc),而不是使用 VI)将模型拟合到数据,从而获得更准确的后验概率。

结论

在本文中,我们看到了如何使用 TensorFlow Probability 的结构化时间序列库来开发贝叶斯结构化时间序列模型。此外,我们还探索了:

  • 如何定义和配置结构化时间序列模型组件?
  • 如何训练 STS 模型建立后验预测概率分布?
  • 如何对后验分布进行采样以进行时间序列预测?
  • 如何分解结构时间序列模型,以检查其各个组成部分的个别贡献。
  • 通过 TensorFlow Probability STS 提供的其他一些令人惊叹的模型,简要了解一些改进我们的 STS 模型的替代方法。

感谢您的阅读!

参考

在数据科学面试中组织您对案例研究问题的回答

原文:https://towardsdatascience.com/structure-your-answers-for-case-study-questions-during-data-science-interviews-a14a02d21e6c?source=collection_archive---------10-----------------------

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

Clem Onojeghuo 在 Unsplash 拍摄的照片

办公时间

自信地完成数据科学面试,第 4 部分

在我之前的文章中,我已经谈到了机器学习、统计和概率中需要准备的面试问题:

[## 准备面试的 20 个机器学习相关问题

自信地完成面试

towardsdatascience.com](/20-machine-learning-related-questions-to-prepare-for-interviews-93bcba72f911) [## 为数据科学面试做准备的 22 个统计问题

自信的钉钉数据科学访谈,第 2 部分

towardsdatascience.com](/22-statistics-questions-to-prepare-for-data-science-interviews-d5651a8b3c56) [## 数据科学面试的 12 个概率练习题

自信地完成数据科学面试,第 3 部分

towardsdatascience.com](/12-probability-practice-questions-for-data-science-interviews-2ec5230304d9)

我将在下一篇文章中讨论行为问题:

[## 为数据科学面试准备行为问题

自信地完成数据科学面试,第 5 部分

towardsdatascience.com](/prepare-behavioral-questions-for-data-science-interviews-96e97f13be15)

在本文中,我将重点介绍案例研究问题的准备工作。

在数据科学面试中,有时面试官会提出一系列业务问题,并讨论使用数据科学技术的潜在解决方案。这是数据科学面试中案例研究问题的一个典型例子。面试官可以根据候选人的表现,透彻了解候选人在批判性思维、商业智能、用模糊的商业问题解决问题的能力,以及数据科学模型和基本面的实际运用等方面的能力。相比之下,这里问的大多数问题都是开放式问题,没有一个正确的答案。了解回答这类问题的模式并组织你的答案是很有用的。本文将讨论帮助您在数据科学面试之前和面试期间更好地准备回答案例研究问题的策略。

面试前的方式

1、培养商业意识

如果你在面试前还有很多时间,或者如果你想提高你对商业智能的总体认识以及数据科学在商业环境中的应用,请关注科技公司的博客帖子,并开始定期阅读这些帖子。例如,我在微软、沃尔玛全球技术insta cart等公司学习数据科学。,中等。除了 Medium,一些公司喜欢拥有自己的博客网站或 YouTube 频道,如谷歌数据科学博客优步工程等。作为一名没有全职数据科学行业经验的应届毕业生,我发现自己从阅读这些博客帖子中受益匪浅。它指导我理解某些方法的应用,以解决现实世界的业务问题。它开阔了我的眼界,让我了解数据科学中最新的前沿研究技术。

除了特定公司的博客,你还可以研究特定的主题和阅读相关的文章。例如,如果您对计算客户终身价值感兴趣,Medium 上有许多很棒的文章,在不同的商业环境中讨论这个问题。此外, StellarPeers 是一个很棒的出版物,它发布文章讨论不同产品和业务问题的解决方案。

阅读博文时,不要在技术细节上浪费太多时间,试着通过回答以下问题总结你所学到的东西:

  • 这篇文章解决的商业问题是什么?
  • 有哪些方法?这些方法对我来说是新的吗?
  • 他们如何评价他们方法的性能?
  • 你怎么能把它推广到其他类似的商业问题呢?

2、知道正确的术语

熟悉业务指标、关键绩效指标(KPI):

轻松讨论常用解决方案:

  • 分类变量的正确处理:一键编码
  • 维度缩减:PCA
  • 处理缺失数据
  • 处理异常值
  • 随意推断技巧
  • 机器学习模型
  • 评估模型性能
  • 试验设计

了解常见问题:

  • 样本偏差:选择偏差,反应偏差
  • 内生性
  • 相关性和因果关系的区别
  • [统]多重共线性
  • 适配过度,适配不足

面试前

当你准备案例研究问题的时间有限时,做以角色为导向的研究来练习面试中可能被问到的潜在问题是很有用的。

1。对公司做调查:

面试官通常会问一些他们每天都会遇到的问题。在进行研究时,重点关注以下几个方面:

  • 关于公司的一般信息:浏览公司网站、Linkedin 页面等。,对公司有一个透彻的了解:比如,他们在什么行业?他们的产品有哪些?他们的产品可能存在哪些业务问题?
  • 浏览该公司的博客帖子,了解他们的数据科学家每天都在解决的问题以及他们的方法。总结一些他们经常用于解决特定业务问题的技巧。

2。对公司的技术团队进行研究:

  • 他们的数据科学家正在解决的问题:关注公司的技术博客文章。许多公司让他们的数据科学家、工程师分享他们的项目,讨论他们用技术细节解决的问题。阅读这些博客帖子将有助于您了解这些公司的业务挑战以及数据科学技能的现实应用。
  • 他们希望你加入团队后解决的问题:通读你所申请职位的职位描述。具体来说,通过分析工作资格,试着列出他们需要和渴望的所有技能。
  • 即使在同一家公司,不同团队的数据科学家的日常任务也可能大相径庭。如果你知道谁将是你的面试官,试着通过在 LinkedIn 上搜索来找出你的面试官现在在哪个团队工作,或者之前在哪个团队工作。然后问自己他们解决了什么业务问题,你的解决方案是什么。

面试的时候

在面试中,组织你的回答以最好地展示你解决问题的技巧和批判性思维能力是很重要的。

1、首先,在开始任何解决方案之前,先问清楚问题!

用数据科学技术解决业务问题通常是模糊的问题,如如何预测需求?如何通过市场活动增加收入等。首先问清楚问题是很重要的。根据您被问到的确切问题,您可以澄清:

  • 假设是什么?例如,如果你被问及如何降低客户流失率,从询问你正在评估的流失率的时间段开始。
  • 对可用的数据做出合理的假设:为了预测过去的订单,问面试官我们是否有过去一个月订单的历史数据。
  • 定义问题中的模糊词语:这非常重要,因为它将设定你答案的方向。比如问你 app 的这个新功能好不好,你首先要定义什么是“好”。“好”可以定义为吸引更多的客户、更高的用户参与度、更高的收入等。,视上下文而定。“好”在短期或长期可以是不同的。在开始解决这个问题之前,我们需要确保设定合理的目标。在其他情况下,我们可以在几个场景中阐述潜在的解决方案,而不是只关注一种情况。
  • 定义问题中的指标:如果问题中没有定义业务指标,您需要定义对目标具体而敏感的自己。例如,当我们在用户应用程序中评估新功能的性能时,我们可以将指标定义为用户转化率、用户参与时间等。
  • 重复你对问题的理解,问面试官你对问题的理解是否正确。你确保你走在正确的方向上,你将为自己赢得一些额外的时间来思考解决方案。

2、提出你的解决方案,从简单开始

明确问题后,你就可以开始提出你的解决方案了。展示您对数据科学工具包的了解:什么、如何以及为什么。总是从最简单的解决方案开始,并在更多的条件下改进它。例如,您可以从线性回归模型开始了解数据,对您正在解决的问题有一个模糊的印象,然后转向更复杂的模型,讨论功能的选择等。

3、总是解释你的解决方案背后的原因

答案本身并不是你答案中最重要的部分,你选择它的原因才是。总是陈述你为什么选择你的方法,为什么不选择其他可能的方法。比如在回答价格暴涨时你如何选择暴涨乘数的时候,你可以说我想用合成控制是因为网络效应,像 AB 测试这样的实验设计在这里不适用。陈述你选择的原因,验证你的答案,向面试官展示你对这个话题有透彻的理解。

4、评估你的解决方案:技术性和非技术性

如何评价你的提案?从技术指标开始,如模型准确度、精确度、分类模型的召回率、R 分数、回归模型的 MSE。将技术指标与旧模型或基线模型进行比较,看看这是否是一个改进。比较非技术指标,如上面讨论的业务指标,您的模型是否增加了收入。此外,讨论其他指标,如我们解释模型的难易程度,这一新功能如何影响用户体验,以及将模型部署到生产中的难度。

5、讨论权衡

  • 永远要意识到你的方法的潜在缺点。尝试讨论某些方法的利弊,并在彻底调查的基础上做出最终决定。
  • 讨论短期和长期利益之间的权衡。例如,一个市场活动是否会在短期内增加订阅量,并在长期内增加收入。设定一个短期或长期偏好不同的目标,可能会产生不同的解决方案。
  • 讨论不同小组之间的权衡。例如,三方使用优步饮食,即餐馆、承运人和食客。试着讨论你的提议对三方的影响。
  • 讨论在计算复杂性和可解释性、更快的结果、更精确的模型、进行实验的更高成本、更可靠的结果等方面的权衡。

6、知道你在说什么

面试官喜欢根据你的回答提出后续问题。例如,如果你提到我想在我的线性回归模型中加入正则化,他们可能会问你为什么?L1 正规化和 L2 正规化有什么区别,你会选择哪个?这是面试官了解你对模型或技术的基本理解的一种方式。

7、接受暗示

如果你偏离了他们想要的答案,好的面试官会给你提示。例如,如果你在谈论解决问题的方法 A,面试官可能会说,使用方法 A 可能会带来问题 B,你认为方法 C 怎么样?当你得到提示时,接受它!!!他们不是想骗你。

8、树立正确的心态

面试不是考试,尤其是当你面对没有一个正确答案的开放式问题时。向面试官展示你将广泛的商业问题转化为可操作的分析的思维过程是很重要的。如果您向他们展示您如何提炼业务问题、提出数据科学技能,并展示这些项目将如何影响您期望的目标,将会有所帮助。你可以想想你自己已经在公司工作了,你正在和你的同事讨论你所面临的问题的潜在解决方案。设置正确的心态有助于你减轻压力,展现你的真实表现。

9、面试时不要默默思考

这条规则适用于面试中提出的任何问题。永远在你的面试官面前展示你的想法和你的思考过程,即使一开始这并不是一个完美的解决方案。你可以根据提示或你的进一步思考来修改你的答案。面试时沉默从来都不是好事。如果你完全不知道如何回答一个问题,就从问一些澄清性的问题开始。

以上是我在准备数据科学面试案例研究问题时的所有提示。如果你有其他建议,请在下面留下评论。感谢您的阅读!

这是我所有博客帖子的列表。如果你感兴趣的话,可以去看看!

* [## 我的博客文章库

我快乐的地方

zzhu17.medium.com](https://zzhu17.medium.com/my-blog-posts-gallery-ac6e01fe5cc3) [## 阅读朱(以及媒体上成千上万的其他作家)的每一个故事

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

zzhu17.medium.com](https://zzhu17.medium.com/membership)*

构建您的数据科学项目

原文:https://towardsdatascience.com/structure-your-data-science-projects-6c6c8653c16a?source=collection_archive---------6-----------------------

数据科学项目结构

开发协作和可复制的数据科学项目

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

费利佩·费塔多在 Unsplash 上的照片

与软件开发相比,数据科学有一些关键的区别。例如,数据科学项目侧重于探索和发现,而软件开发通常侧重于实现一个明确定义的问题的解决方案。在定义软件产品的需求、理解客户需求的过程中,很少会出现不明确的地方,尽管对于数据驱动的解决方案来说,范围可能会发生变化。

您可以发现数据科学和软件开发之间的许多其他差异,但是这两个领域的工程师需要致力于一致且结构良好的项目布局。毕竟,数据科学项目像任何其他软件系统一样包含源代码,以构建模型本身的软件产品。结构化源代码和与项目相关的数据有很多好处。构建数据科学工作的主要优势包括:

  1. 跨数据科学团队的协作变得更加容易。当团队中的每个人都遵循相同的项目结构时,发现其他人所做的变更并促成这些变更就变得非常容易。
  2. 再现性。 模型应该是可再生的,不仅是为了跟踪模型版本,也是为了在模型失败的情况下容易地恢复到旧版本。以可重复的方式记录您的工作,可以确定新模型是否比以前的模型执行得更好。
  3. 效率。 我已经多次检查过旧的 jupyter 笔记本,以便为新项目重用一些功能。我可以凭经验告诉你,假设你有很好的记忆力,平均迭代十个笔记本,找到一段 20 行的代码可能会令人沮丧。以一致的结构提交我写的代码避免了自我重复和复制。
  4. **数据管理。**原始数据应与中间数据和处理过的数据分开。这将确保从事该项目的任何团队成员都可以轻松地重现所构建的模型。找到在模型建立阶段之一中使用的各个数据集所花费的时间大大减少。

虽然要成功实现数据科学项目的可再现性,还需要许多其他的依赖因素,例如,如果您没有覆盖用于模型构建的原始数据,在下一节中,我将分享一些工具,它们可以帮助您开发一致的项目结构,从而促进数据科学项目的可再现性。

烹饪刀

Cookiecutter 是一个命令行工具,它从项目模板中创建项目。您可以创建自己的项目模板,也可以使用现有的模板。让这个工具如此强大的是,您可以轻松地导入模板,并且只使用最适合您的部分。

在这篇文章中,我将更多地谈论 cookiecutter 数据科学模板。安装简单明了。要安装,请运行以下命令:

pip install cookiecutter

要处理模板,只需使用命令行获取它:

cookiecutter https://github.com/drivendata/cookiecutter-data-science

该工具会询问一些配置选项,然后您就可以开始了。

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

项目结构如下所示:

├── LICENSE
├── Makefile           <- Makefile with commands like `make data` or    `make train`
├── README.md          <- The top-level README for developers using this project.
├── data
│   ├── external       <- Data from third party sources.
│   ├── interim        <- Intermediate data that has been transformed.
│   ├── processed      <- The final, canonical data sets for modeling.
│   └── raw            <- The original, immutable data dump.
│
├── docs               <- A default Sphinx project; see sphinx-doc.org for details
│
├── models             <- Trained and serialized models, model predictions, or model summaries
│
├── notebooks          <- Jupyter notebooks. Naming convention is a number (for ordering),the creator's initials, and a short `-` delimited description, e.g.
│                         `1.0-jqp-initial-data-exploration`.
│
├── references         <- Data dictionaries, manuals, and all other explanatory materials.
│
├── reports            <- Generated analysis as HTML, PDF, LaTeX, etc.
│   └── figures        <- Generated graphics and figures to be used in reporting
│
├── requirements.txt   <- The requirements file for reproducing the analysis environment
│
├── setup.py           <- makes project pip installable (pip install -e .) so src can be imported
├── src                <- Source code for use in this project.
│   ├── __init__.py    <- Makes src a Python module
│   │
│   ├── data           <- Scripts to download or generate data
│   │   └── make_dataset.py
│   │
│   ├── features       <- Scripts to turn raw data into features for modeling
│   │   └── build_features.py
│   │
│   ├── models         <- Scripts to train models and then use trained models to make
│   │   │                 predictions
│   │   ├── predict_model.py
│   │   └── train_model.py
│   │
│   └── visualization  <- Scripts to create exploratory and results oriented visualizations
│       └── visualize.py
│
└── tox.ini            <- tox file with settings for running tox; see tox.testrun.org

文件夹

生成的项目模板结构允许您为数据科学流程组织源代码、数据、文件和报告。这种结构简化了跟踪项目变更的过程。我将详细介绍五个文件夹:

  • **数据。**数据应分段,以便将来重现相同的结果。你今天用来建立机器学习模型的数据可能与你将来拥有的数据不一样,也就是说。数据可能被覆盖,或者在最坏的情况下丢失。为了拥有可再生的机器学习管道,保持原始数据不变非常重要。原始数据的任何进展都应该被适当地记录下来,这就是数据文件夹发挥作用的地方。您不再需要将文件命名为 final_17_02_2020.csv、final2_17_02_2020.csv 来跟踪更改。
  • **车型。**模型是机器学习管道的最终产品。它们应该存储在一致的文件夹结构下,以确保将来可以复制模型的精确副本。
  • **笔记本。**许多数据科学项目都是在 Jupyter 笔记本上完成的,这让读者可以了解项目的流程。这些笔记本很可能充满了大量的功能和代码块,这使得创作者甚至忘记了代码块的功能。将您的功能、代码块和结果存储在单独的文件夹中,可以让您进一步细分项目,并便于在笔记本中遵循项目逻辑。
  • **报道。**作为数据分析流程的一部分,数据科学项目不仅会生成模型,还会生成数字和图表。这些可以是平行线、条形图、散点图等。您应该存储生成的图形和数字,以便在需要时能够轻松地报告它们。
  • Src。 Src 文件夹是放置管道中使用的函数的地方。这些功能可以像软件产品一样根据它们在功能上的相似性来存储。您可以轻松测试和调试您的功能,而使用它们就像将它们导入笔记本一样简单。

为了演示如何使用这个工具,我修改了我之前参与的一个项目。存储库并不是为机器学习流程而优化的,尽管你可以很容易地理解按照链接组织你的数据科学项目的想法。

Makefile

GNU make 是一个控制程序的可执行文件和非源文件生成的工具。它利用 makefiles 列出所有要构建的非源文件,以便产生程序的预期结果。Makefiles 极大地帮助数据科学家建立他们的工作流程。在数据科学项目交付后的大部分时间里,开发人员很难记住构建最终产品所采取的步骤。Makefiles 帮助数据科学家记录管道,以重现构建的模型。因此,这个工具应该放在数据科学家的工具箱里。

Makefile 不仅提供了可再现性,还简化了数据科学团队的协作。一个团队成员,他将使用多个命令设置环境和安装需求,现在可以在一行中完成:

#old
virtualenv ex
source ex/bin/activate
pip install -r requirements.txt
#new
make requirements

水印

Watermark 是一个 IPython 扩展,可以在任何 IPython shell 或 Jupyter 笔记本会话中打印日期和时间戳、版本号和硬件信息。它提供了一种简单的方法来跟踪项目中涉及的工具、库和作者。对于大型项目,使用像 watermark 这样的工具来跟踪所做的更改是一种非常简单且低效的方法。

要安装和使用 watermark,请运行以下命令:

pip install watermark
%load_ext watermark

这里演示了如何使用它来打印库版本。您可以在他们的文档中找到更多信息:

%watermark -d -m -v -p numpy,matplotlib,sklearn,pandas

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

最终想法

凭经验我可以说,数据科学项目一般没有标准化的结构。然而,我在这篇文章中描述的工具可以帮助您创建可重复的数据科学项目,这将提高您的数据团队的协作、效率和项目管理。

如果你对这个帖子有任何疑问,或者对数据科学有任何疑问,你可以在Linkedin上找到我。

感谢阅读!

来自非结构化语料库的结构化食谱

原文:https://towardsdatascience.com/structured-recipe-from-an-unstructured-corpus-5710ee869505?source=collection_archive---------53-----------------------

信息检索

基于食物的知识图驱动的信息检索和自然语言处理项目概述

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

Unsplash 上由S O C I A l C U T拍摄

本文简要介绍了一个信息检索(和自然语言处理混合)项目的素质和标志,并强调了所获得的结果和推论。

该项目的目标是:

  • 从食谱数据的非结构化语料库中导出结构。
  • 利用这种结构来获得知识图。
  • 从获得的知识图中提取和检查推论。
  • 建立信息检索和可视化系统。

接下来的工作可以作为烹饪分类、推荐系统、基于知识图的语义推导等的基础。

什么是非结构化数据?

  • 在数据收集中缺乏定义良好的结构使其非结构化
  • 图像、文本、歌曲、视频是这种数据的一些例子。
  • 大多数生成的数据是非结构化的。

虽然事实上没有数据是完全非结构化的,但我们的机器发现这种形式的数据平淡无奇;除非我们无能为力。

无法对非结构化数据执行信息提取和分析。由于大多数数据是非结构化的,这是一个广泛面临的问题,因此我们的问题定义。

问题定义

许多烹饪网站提供各种烹饪和方法的食谱。尽管存在大量的食谱数据,但几乎没有结构。任何结构,如果存在的话,都不能从中导出有用的模式。

因此,我们的任务是从基于非结构化配方的数据中导出一些结构和有意义的关系,并能够在其上运行信息检索系统。

数据集

我们使用的数据集具有以下结构。

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

单一配方 JSON 分解。(图片作者)

这是一个食谱的 JSON 分解。食谱的标题、配料列表和说明列表是我们在这个项目中唯一关心的条目。配料表描述如下。即我们的非结构化数据:

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

一个配方的成分列表。(作者图片)

相邻的图像显示了配方配料数据是如何存储在配料列表中的。可以看出,这些数据是非结构化的。

这个列表是我们要构建的输入。

结构化数据

非结构化成分数据可以使用 NYTimes 的 CRF 成分短语标签进行结构化。

这个问题是一个结构化的预测问题,其中我们需要识别一系列标签,而不是识别单个标签。为此,CRF 成分短语标记器使用线性链条件随机场,这在诸如词性标记和命名实体识别(NER)的任务中经常是成功的。

成分的各种标签包括数量、单位、名称和注释。该模型通过在一种成分的所有可能标签序列中找到最高概率来找到最佳的可能标签序列。这描述如下:

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

如果我们的配料短语是“2 茶匙盐”然后我们需要给所有可能的标签序列打分。(图片作者)

生成的结构由附在配料上的标签组成。

该模型对 20000 种成分进行训练,并生成中间模型文件,该文件进一步用于测试目的。

评估标准是模型预测的准确性:

  • 句子级:模型预测成分句中每个单词的实际值。如果句子中的一个单词被错误地识别,则该句子被添加到错误指定的句子中。
  • 单词级:在单词级的情况下,检查每个单词,而不管句子是什么。

对 2000 种成分的测试在句子级别产生了大约 74%的准确度,而在单词级别产生了 90%的准确度。句子级别的较低准确度可归因于句子中存在多个单词,并且并非所有单词都被正确识别,因此降低了句子级别的整体准确度。

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

在构造阶段获得的单词级图表和句子级图表的准确度。(图片作者)

将模型应用于对应于食谱数据集的配料,我们可以构造其数据,从而暗示模型的稳健性和准确性。

现在我们已经从语料库中准备好了一个结构,我们可以从这里开始构建知识图了。

知识图构建

下一步是构建三元组来制作知识图。

如果你是知识图表的新手,看看叶戈尔·德芝克的快速阅读:

[## 理解知识图表

首先,人工智能系统严重依赖数据库中的手工知识。典型的专家系统使用这个…

medium.com](https://medium.com/@Dezhic/understanding-knowledge-graphs-5cb05593eb84)

在创建三元组之前,我们需要做一些快速的预处理步骤:

  1. 词汇化
  2. 删除标点符号
  3. 停用词删除
  4. 移除前导和尾随空格
  5. 小写转换

这清理了我们的数据,并为以后在本文的查询部分使用提供了一致性。

要构建的三元组的形式为:( ) — — ( 目标)。每个源值和目标值成为一个节点,第三个三元组将它们连接成一个

为每个(食谱,结构化烹饪步骤)对创建一个三元组集合。对于每一对,'’节点是结构化 JSON 的’标题’列,‘目标’节点是从获取的结构化数据中的’名称’和’注释字段中的’边缘’。

简而言之,每个菜谱标题都是一个源节点,通过在菜谱中应用的处理连接到它的配料。

我们的收集产生了这样一个知识图表:

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

10 种食谱的知识图表。(图片作者)

如果你认为这 10 个食谱的知识图表是密集的,那么看看下面 100 个食谱的图表。

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

100 种食谱的知识图表。(图片作者)

为了清楚起见,让我们来看一个来自单个配方的知识图:

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

单一配方的知识图表。(图片作者)

所以我们有一个图表。但是从这里去哪里呢?

查询图表

让我们查询这个图,这样我们就可以从中获得任何想要的信息。

为在三元词库中找到的每个关键词建立一个倒排索引。每个关键字指向该关键字所在的节点或边。倒排索引看起来像:

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

倒排索引的一个例子。这里,花括号内的元素是知识图中的节点或边。(图片作者)

对于每个查询词,候选集包含该词在倒排索引中指向的节点或边的集合。这个候选集用于从我们的原始知识图中绘制输出子图。

因此,包含查询词及其紧邻的任何节点都将显示在图中。任何包含术语“胡萝卜”的边都会出现它所连接的两个节点。互连得到了处理。

考虑查询— 胡萝卜。该查询的输出是:

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

查询的子图输出:胡萝卜。(图片作者)

这个图稀疏得多,可以清楚地观察到节点和边。

让我们考虑另一个查询— 菠菜丁。对于多个查询术语,输出知识图可以是连接的或断开的,这取决于查询术语从图中带来的启示。该查询的结果是:

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

查询的子图输出:菠菜丁。(图片作者)

我们得到了一个稍微多一点的连通图。甚至一些连接到它们各自成分的配方节点也出现在图中。

还有几个问题:

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

罐头豆蔻作为查询。(图片作者)

查询部分到此结束。让我们谈谈从我们的全知识图中可以得出的推论。

结论

构建的知识图用于进行推论以找出重要信息。

评估的措施:

程度中心性:

程度中心性是受欢迎程度的度量。它确定连接到最多节点的节点。该度量确定了图中可以在局部区域中快速传播信息的节点。

对于食谱数据的上下文,具有高度中心性的配料节点是受欢迎的配料,并且在许多食谱中使用或者与许多其他配料一起使用。

中间中心性:

介数中心性是一种度量,它提供了关于哪些节点重要的感觉,不是因为它们具有大量的连接,而是因为它们提供了网络的连通性和内聚性。

这些节点是食谱的重要组成部分,它们具有较高的介数中心性和较低的度中心性。

特征向量中心性:

特征向量中心性有助于理解哪些节点可以快速获得大量节点的信息。

也可以看做是相关的影响力或者说是在幕后发挥权力作用的节点。本质上,是知识图中重要节点的邻居的节点。

这些成分可以看做是最本质的成分。

连接组件:

可以看出,大多数配方构成了图中最大的连通部分,其余的包含一个或两个配方。这暗示了一些食谱是非常独特和不同的,因为它们的成分或制备方法不同。

这些食谱就像样本空间中的离群值,因为它们的制备方法和/或配料设置与大多数食谱非常不同。

社区检测:

社区检测是一种以最佳方式划分知识图的方法。可以看到,这些配方组合在一起。这些分组被称为社区

社区暗示了基于成分或准备方法的食谱的分离。

可以看出,在从 100 个食谱的语料库构建的知识图中,具有 2 个连接的组件,其中具有 9 个社区。

社区暗示了可以执行的食谱的可能分类。这些集群有同类的配方。

汇总推论:

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

从所描述的测量中得到的推论。(图片作者)

如果您想了解更多关于用于推断的测量方法,以下是一些有用的链接:

[## 图形分析——中心性的介绍和概念

社交网络、大数据和电子商务的出现再次强调了分析一种独特类型的…

towardsdatascience.com](/graph-analytics-introduction-and-concepts-of-centrality-8f5543b55de3) [## 图形和网络中的社区检测入门

今年,“社区”一词进入了世界各地的主流对话,这在很大程度上要归功于…

www.analyticsvidhya.com](https://www.analyticsvidhya.com/blog/2020/04/community-detection-graphs-networks/)

未来工作:

未来的工作可能包括包含具有不同规则的三重映射的架构上不同的知识图的几个副本。不同结构的组合可能能够更好地表示复杂的关系。可以进一步查询该图以得出更多的推论。

贡献:

这个项目的开发是一个共同的努力。这个团队在这个项目的大部分时间里密切合作。我们面临的困难是,我们改进业绩的想法效果不佳,这是一次高度团结的合作。虽然项目的内聚结构是众所周知的,但工作分配细分如下。

(坦莫·查克拉博蒂)坦莫·查克拉博蒂博士——全程指导。

知识图和子图查询系统的实现和设计。结构到三层结构的设计和转换。

罗斯·维尔马( 罗斯·维尔马 ) —从知识图中提取推理。结构到三层结构的设计和转换。

普拉蒂克·阿加瓦尔( 普拉蒂克·阿加瓦尔 ) —从非结构化数据和数据预处理技术中创建结构化数据。

这个项目是为 IIITD 的课程信息检索 2020 做的。

MATLAB/Octave 中的结构

原文:https://towardsdatascience.com/structures-in-matlab-octave-96145b0b3f9f?source=collection_archive---------67-----------------------

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

图片由作者提供。图片来源于https://pixabay.com/

帮助您的数据保持完整!

结构,或结构,是 MATLAB/Octave 中的一种基本数据类型,可用于将多个属性组织和组合成一个公共数据结构。结构上的属性也称为字段,可以是不同的类型和不同的大小。然后,可以像平常一样访问和操作结构上的每个属性。结构在 MATLAB/Octave 中有多种用途,对于那些更熟悉 Python 的人来说,它非常类似于字典。

在本文中,我将介绍结构的基础知识,然后分享一些我认为最有用的使用它们的“技巧”。特别是,我涵盖了以下主题:

  • 动态属性访问和 filednames() 函数
  • 使用结构数组
  • 使用结构编写高效代码的注意事项

所有例子的源代码都可以在 GitHub 库中找到。需要注意的是,MATLAB 和 Octave 显示结构的方式非常不同,因此根据运行代码的应用程序,您可能会看到一些差异。在本教程中,我用 Octave 运行了所有的例子,以尽可能地使事情变得容易理解。

基础知识

MATLAB/Octave 中的结构是动态管理的,所以我们可以随时添加和删除新的属性。创建结构最简单的方法是使用 struct() 命令创建一个空结构,并使用点操作符填充它。这里有一个简单的例子。

s = struct(); % create an empty struct
s.val1 = 100; % add a scalar
s.val2 = 0 : 25 : 100; % add a vectorize
s.val3 = randn( 2, 3, 5 ); % add an ND array
s.char1 = 'This is a character string'; % add a character string
s.cell1 = { 100, 300, '5', 'abc' };  % add a cell array
disp( s );scalar structure containing the fields:val1 =  100
    val2 =0    25    50    75   100val3 =ans(:,:,1) =-0.49715   0.32621  -0.81624
      -0.34628   1.85946   0.29556ans(:,:,2) =-0.32521   0.44471   2.40743
      -0.97223  -0.24240   0.39241ans(:,:,3) =-1.63085  -0.34078   0.46239
       0.48026  -0.45361   1.62214ans(:,:,4) =

       0.523431  -0.576770  -0.028377
      -1.035780   0.093593  -2.001485ans(:,:,5) =0.30492  -2.05388   0.13755
      -0.98662  -1.36354  -0.43432char1 = This is a character string
    cell1 =
    {
      [1,1] =  100
      [1,2] =  300
      [1,3] = 5
      [1,4] = abc
    }

我们现在可以使用相应的字段名称来访问任何新添加的属性。例如,让我们访问“val2”的第 2 个元素。

>> disp( s.val2(2) );25

或者我们可以访问“单元格 1”的最后一个元素。

>> disp( s.cell1{end} );abc

事实上,我们现在可以访问和修改所有的属性,就像它们不在结构上一样。让我们给“单元格 1”添加一个新值。

>> s.cell1{ end+1 } = 'I am new!';
>> disp( s.cell1 );{
  [1,1] =  100
  [1,2] =  300
  [1,3] = 5
  [1,4] = abc
  [1,5] = I am new!
}

或者让我们从“val2”中删除一些值。

>> disp( s.val2 );0    25    50    75   100>> s.val2( 2:3 ) = [];>> disp( s.val2 );0    75   100

最后,使用 rmfield() 函数,我们可以从一个结构中完全删除字段。例如,让我们创建一个名为“s2”的新结构,并删除除“val1”之外的所有字段。其语法是: sOut = rmfield( s,{ 'name1 ‘,’ name2 ',etc… } ) ,其中如果我们想要覆盖原始结构,则“sOut”可以是“s”,并且单元格数组包含我们想要移除的所有字段的名称。

>> s2 = rmfield( s, { 'val2', 'val3', 'char1', 'cell1' } );
>> disp( s2 );scalar structure containing the fields:val1 =  100

这是对结构的最基本用法的快速介绍。如果你是一个结构新手,花点时间浏览一下 MATLAB 文档可能是值得的。本文的其余部分将涉及一些更高级的,或者可能不太明显的使用结构的方法。

动态访问属性和 fieldnames()函数

结构的另一个非常有用的特性是你可以构建它们的数组。这些数组的行为与任何其他数组非常相似,但是,在填充它们时还需要额外的注意。特别是,结构数组中的每个元素都必须具有相同的属性。它们可以是空的,但是在将多个结构连接成一个数组之前,所有的属性都必须存在。

从一个简单的例子开始,让我们将“s3”的第二个元素设置为其自身的另一个副本。

>> s3(2) = s3;
>> disp( s3 );1x2 struct array containing the fields:noodles
    dexter
    ron
    greg

现在我们可以看到有一个 1x2 的结构数组。请注意,这是可行的,因为两个结构具有完全相同的属性。

结构数组的行为就像任何其他数组一样,可以使用 () 来访问。例如,让我们将第一个结构上的“面条”字段更新为等于当前值的两倍。

>> s3(1).noodles = s3(1).noodles * 2;
>> disp( s3(1).noodles );62
>> disp( s3(2).noodles );31

我们还可以通过将元素设置为空值来从结构中移除元素。

>> s3(1) = [];
>> disp( s3 );scalar structure containing the fields:noodles =  31
    dexter =  11
    ron =  46
    greg =  28

请注意,上面的输出显示我们再次拥有了一个标量结构。因为我们已经移除了结构数组的第一个元素。

最后,跨结构数组收集值也很方便。例如,假设我们有一个包含带有地理标签的图像的结构数组,我们对查看所有图像的位置感兴趣。我们可以通过捕获方括号 [] 之间的结构输出将这些值收集到一个数组中。

让我们用另一个例子来证明这一点。首先,我们将构建 10 个结构,每个结构都有“img”、“lat”和“lon”属性。请注意,在循环中,临时变量用于在将每个结构添加到数组之前完全填充该结构。在这种情况下,这实际上是不必要的,因为我是通过向后循环来预分配结构内存的;但是,一般来说,这是一个很好的做法,因为这将确保在您尝试连接结构之前所有的字段都存在。

% - Build a fake array of structs.
for iS=10 : -1 : 1
    tmp = struct();
    tmp.lat = randi( [-89, 90], 1 );
    tmp.lon = randi( [-179, 180], 1 );
    tmp.img = randn( 16, 16, 3 );
    img( iS ) = tmp;
end

结构的结果数组将如下所示。

>> disp( img );1x10 struct array containing the fields:lat
    lon
    img

接下来,我们可以将结构数组中的所有纬度和经度值收集到单独的数组中。

% - Collect all of the lat and lon values into separate arrays
lat = [ img(:).lat ];
lon = [ img(:).lon ];>> disp( lat );5   48  -36  -29   45   49  -68  -62   34  -31
>> disp( lon );28   -62    60    80  -130   125   -68     2    33   177

方便的是,我们现在可以访问所有的 lat/lon 值,而不必遍历结构。当你想绘制图表或计算统计数据时,这是非常方便的。它还可以节省大量的运行时间,我将在下一节演示这一点。

令人惊讶的是,当您收集具有不止一个非单例维度的属性时,甚至可以做更奇妙的事情。在这种情况下,技巧是使用 cat() 函数来明确说明数据应该如何分组。例如,我们可以将所有的像素数据从结构中收集到一个大小为 16 x 16 x 3 x 10 的新 ND 数组中,其中第四维对应于每个结构。

>> pixelData = cat( 4, img(:).img );
>> disp( size( pixelData ) );16   16    3   10

现在你可以看到我们有一个 4 维数组,它是高乘宽乘通道乘图像。我发现这是一种非常优雅的方法,可以同时访问一个结构中多个元素的数据。这个“技巧”起初对我来说也不是很明显,但现在我经常使用。

性能呢?

尽管结构很棒也很灵活,但当然也有一些代价。在这种情况下,代价是运行时间的增加,这主要是由于数据在内存中的存储方式。好消息是,使用我们刚刚介绍的技巧,您可以解决大多数问题。如果您有兴趣了解更多关于数组顺序和在 MATLAB 中编写高效循环的知识,请务必查看详细讨论该主题的以前的文章

让我们以前面的图像为例,但是现在我们将创建 50,000 个伪图像结构。

for iS=50000 : -1 : 1
    tmp = struct();
    tmp.lat = randi( [-89, 90], 1 );
    tmp.lon = randi( [-179, 180], 1 );
    tmp.img = randn( 16, 16, 3 );
    img( iS ) = tmp;
end

接下来,我们可以编写一些代码来对每个图像执行一个简单的任务。在这种情况下,我们将计算每个图像的平均像素强度,并将其保存在一个 1x10,000 元素的数组中。如果我们直接从结构中访问数据,代码看起来就是这样。

a = tic();% Loop over each struct
avgPixelInt = nan( 1, numel( img ) );
for iS=1 : numel( img )
  avgPixelInt( iS ) = mean( img( iS ).img(:) );
endt1 = toc( a )

或者,如果我们使用上一节中的 cat() 技巧,我们实际上可以编写相同的代码,而不必遍历数据。

a = tic();% Capture all of the images into an ND array
pixelData = cat( 4, img(:).img );% Reshape so we can take the mean over the entire image
sz = size( pixelData );
pixelData = reshape( pixelData, [ prod( sz(1:3) ), sz(4) ] );% Calculate the mean for all images at once
avgPixelInt = mean( pixelData, 1 );t2 = toc( a );

现在,如果我们运行两组代码,并使用 tic()toc() 对执行进行计时,这就是我们得到的结果。

>> t1 = toc( a )t1 =  9.8469>> t2 = toc( a )t2 =  1.9880>> disp( t1/t2 );
 4.9531

在这种情况下,使用 cat() 技巧使代码运行速度提高了近 5 倍!现在,在这个例子中,节省实际上来自于消除环路;但是,通常情况下,最好不要在循环中访问或设置结构的属性。几乎在所有情况下,由于结构中的数据存储在内存中的方式,这将导致代码变慢。所以,当有疑问时, cat() 它出来了。

摘要

这篇文章介绍了 MATLAB/Octave 中的结构,并介绍了一些基本的用法。一些更高级的用法基于使用变量来访问结构上的特定属性。这些技术确实给了程序员很大的能力来以编程方式解析和生成结构。最后,讨论了结构数组的使用,以及一些有助于编写高效代码的技巧。

以下是一些需要记住的关键事项:

  • 结构可以包含不同类型的数据作为属性
  • 可以随时添加或删除结构属性(或字段)
  • 结构可以连接成数组;但是,结构数组的每个元素必须包含完全相同的属性
  • 函数可以用来将所有结构中的特定属性收集到一个数组中
  • 虽然本文没有讨论,但是结构也可以包含其他结构作为属性
  • 尽管结构很棒,但是在使用它们的时候你应该小心,以避免编写低效的代码

编码快乐!

为快速迭代机器学习实验构建 Jupyter 笔记本

原文:https://towardsdatascience.com/structuring-jupyter-notebooks-for-fast-and-iterative-machine-learning-experiments-e09b56fa26bb?source=collection_archive---------13-----------------------

这是为那些需要在整洁的 Jupyter 工作空间中快速运行大量建模实验的忙碌的 ML 实践者准备的备忘单。

“模块化”你的代码在机器学习项目中很难

与软件世界不同,术语“可重用组件”可能很难应用于建模世界。实验通常是一次性的,没有多少代码被重用。如果你是一个干净的代码倡导者,喜欢花时间重构每一行代码来遵循“不要重复自己”(DRY)的原则,你很容易花太多时间这样做。

然而,我并不是建议去“不要重复自己”原则的对立面。我见过非常杂乱无章的 Jupyter 笔记本目录。然而,我们应该努力理解我们应该重用哪些组件。在本文中,我将根据我两年多来使用 Jupyter 笔记本对数据进行预处理和建模的经验,重点介绍机器学习项目中倾向于重用的组件。

首先,我们为什么要用 Jupyter?

我们在建模项目中使用 Jupyter 的主要原因是我们希望速度更快。我们想快速实验,快速失败,快速学习。数据处理需要时间,机器学习训练更需要时间。不像软件世界里“热重装”是一个东西,我们在建模世界里通常没有它。准备一个实验和实验本身都要花很多时间。为了更快,我们需要使用 Jupyter,它让我们能够只在一小部分代码上测试运行,而不是整个脚本。

这是一个迭代的过程。你越快完成这个循环,你的进步就越快。

—吴恩达,机器学习向往

现在,知道我们不应该在一开始就写脚本,而是应该使用 Jupyter 笔记本,让我们看看我们应该如何组织我们的项目。

概观

以下是我们将在这篇文章中涵盖的内容的概述:

  1. 拥有“小数据”有助于 —为什么以及如何在编写代码时拥有小数据集。
  2. 使用 git —如何使用 git 对你的笔记本进行版本控制。
  3. 关注点分离——如何构建你的 Jupyter 文件目录。
  4. 预处理、建模笔记本&报告笔记本 —这里我们讨论如何构建 3 个笔记本以及笔记本中包含的内容
  5. 主笔记本 —如何从一个笔记本调用其他笔记本以及如何记录输出

拥有“小数据”有助于

首先,在我们开始为我们的数据处理和数据模型编写代码之前,我们应该有一组“小数据”作为我们的数据。主要的直觉是有一个非常小的数据集,可以快速处理。通过这样做,当我们第一次运行我们的代码时,我们不必等几个小时才知道我们的代码中有一个简单的 bug。

例如,如果我们希望在 1000 万张图片上训练一个模型,那么试着每类只采样 50 张图片来编写代码。或者,如果我们正在训练 1 亿行销售数据,我们可以尝试采样 2000 行销售数据作为我们的“小数据”。

“小数据”应该有多大取决于样本的代表性和处理它的时间。就样本而言,每节课至少要有 5 个样本。就时间而言,经验法则是“小数据”从数据处理到完成模型训练所需的时间应该在 10 分钟以内。

你可以在笔记本的开头使用下面的代码来打开和关闭SMALL_DATA_MODE

SMALL_DATA_MODE = Trueif SMALL_DATA_MODE:
    DATA_FILE = "path/to/smallData.csv"
else:
    DATA_FILE = "path/to/originalData.csv"

使用 git

随着您运行越来越多的实验,很可能会删除旧代码并用新代码替换它们。仅仅为了代码上的一个小变化而创建一个新的笔记本是不好的,因为我们将来可能甚至不需要它们,它会占用我们的工作空间。

使用 git 有助于我们在保持工作场所整洁的同时对笔记本进行版本控制。如果需要,您可以通过返回到之前的 git 提交来恢复到旧版本。此外,如果我们定期将代码推送到远程存储库,我们就不必担心代码丢失。如果你独自做这个项目,你可以把所有的事情都推到主分支,如果你在一个团队中,你可以推到不同的分支。

安装 git,

在 Windows 上,转到https://git-scm.com/download/win

在 macOS 上,在终端中运行git --version。如果您还没有安装 git,它会提示您进行安装。

在 Linux Ubuntu 上,运行sudo apt install git-all

安装后,在项目目录中运行以下命令

git init

同样,让我们指定不想用 git 跟踪的文件。创建一个名为.gitignore的新文件,并将以下文本放入该文件。我们将忽略 Jupyter 检查点、python 缓存和数据目录。

.ipynb_checkpoints/
data/
__pycache__/

要提交,请使用以下内容(如果您以前加入过软件项目,您应该对这些内容很熟悉)。如果没有,我建议查看 git 教程。

git add .
git commit -m "Give a clear message here on what's changing compared to last time you commit"# remember to set a remote named `origin` for this:
git push origin master

关注点分离

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

图中显示了机器学习项目的推荐结构

一个机器学习项目通常会有多个使用相同数据和相同模型的实验。与其把所有东西都封在一个目录里,一个好办法是把数据预处理建模实验输出(exp) 分开。

这是我最喜欢的文件结构:

  • data/ —各种数据(原始数据、预处理数据等)的存储桶
  • exp/ —这里是实验的输出(保存的模型+实际和预测的标签)
  • logs/ —只是放置我们预处理数据和建模的日志文件
  • a_MASTER0.ipynb —“主”Jupyter 笔记本,可以调用其他“从”笔记本(预处理、建模、报告)。我们将在下一节展示如何从一个笔记本中调用另一个笔记本。
  • a_MASTER1.ipynb —只是另一个并行运行另一个实验的“主”笔记本。您可以根据需要添加任意数量的主笔记本。
  • b_preprocess.ipynb —预处理笔记本,接收来自data/raw的原始数据,并将数据输出到data/{dir}
  • c_model_svm.ipynb —该笔记本接收预处理的输出,稍加修改以适应 SVM 模型,然后将建模结果(如学习的模型参数、预测等)输出到exp/
  • c_model_randomForest.ipynb —如果你有另一个型号,就这样命名。
  • d_reporting.ipynb —这将从exp/中读取并为您的报告绘制表格或图像。

预处理笔记本

在这里,我将展示当我们第一次在笔记本中运行代码时应该做什么,我们应该用笔记本的参数启动笔记本。

# PARAMETER
#-------------# check if IS_MASTER exists, this variable will only exist if it's being called by MASTER notebook.
# if it does not exist, set it to False
try: IS_MASTER
except: IS_MASTER = False# The code below will only run if it's NOT being called from MASTER notebook
if IS_MASTER:
    DATA_DIR = './data/temp/' # 
    RAW_FILE = f'/path/to/smallData.csv' # use "small data" here
    PROCESSED_FILE = f'{DATA_DIR}processed.pkl' # always use pickle for fast I/O!
    OTHER_PREPROCESS_PARAMETER = ... # e.g. batch size, sliding window size, etc

上面的代码为我们的笔记本设置了默认参数。我们将只使用一个临时目录TMP_DIR(在data/目录下)来存储我们的输出。这是为了确保快速迭代。当我们写代码时,我们应该总是使用“小数据”。

您可以继续编写预处理部分。既然您在这个“开发”阶段使用“小数据”,那么您应该很快!当你完成预处理后,记得使用pickle库输出一个 Pickle 文件:

import pickle
with open(PROCESSED_FILE, 'wb') as f:
    pickle.dump(python_object, f)

或者,使用pandas快捷键:

df.to_pickle(PROCESSED_FILE)

我们使用 Pickle 而不是 CSV 格式来实现持久性和快速读写。这个PROCESSED_FILE将在下一部分的建模笔记本中读到。

建模笔记本

从这个开始我们的模型笔记本:

# PARAMETER
#-------------# check if IS_MASTER exists, this variable will only exist if it's being called by MASTER notebook.
# if it does not exist, set it to False
try: IS_MASTER
except: IS_MASTER = False# The code below will only run if it's NOT being called from MASTER notebook
if IS_MASTER:
    DATA_DIR = './data/temp/'
    EXP_DIR = './exp/temp/'
    PROCESSED_FILE = f'{DATA_DIR}processed.pkl'
    MODEL_FILE = f'{EXP_DIR}model.pkl'
    PREDICTION_FILE = f'{EXP_DIR}ypred.pkl'
    OTHER_MODEL_PARAMETERS = ... # like N_ESTIMATOR, GAMMA, etc

请注意,DATA_DIRPROCESSED_FILE与之前预处理笔记本的输出相同,并与之相连。

在本建模笔记本中,您应该做 3 件事(此处未显示,因为每个模型都不一样):

  1. 读取处理过的数据,并做一点小小的修改,使数据适合模型。
  2. 训练和评估模型
  3. 将模型的学习参数MODEL_FILE和预测PREDICTION_FILE输出到EXP_DIR目录。对于预测输出,将实际标注和预测标注放在同一个数据框中(便于报告)。

报告笔记本

报告笔记本是一个快捷的笔记本,它只需要从exp/目录中读取。其输入通过EXP_DIRMODEL_FILEPREDICTION_FILE连接到造型笔记本的输出。

# PARAMETER
#-------------# check if IS_MASTER exists, this variable will only exist if it's being called by MASTER notebook.
# if it does not exist, set it to False
try: IS_MASTER
except: IS_MASTER = False# The code below will only run if it's NOT being called from MASTER notebook
if IS_MASTER:
    EXP_DIR = './exp/temp/'
    MODEL_FILE = f'{EXP_DIR}model.pkl'
    PREDICTION_FILE = f'{EXP_DIR}ypred.pkl'

在这里,您可以将预测的标签与实际标签进行比较。在这里,您可以使用精度、召回率或 ROC AUC 等指标。您还可以在这里运行图表和绘图的代码。

主笔记本

最后,大师笔记本!

主笔记本是调用所有其他笔记本的笔记本。在这个笔记本中,您将监督整个数据管道(从原始数据预处理到建模和报告)。

主笔记本也是您调用其他(经过良好测试的)预处理和建模笔记本来运行实际“大数据”的地方。我还将介绍一个日志技巧(因为如果“大数据”出错,我们想知道原因)。

我们也将在这里使用 Jupyter 魔法命令%run

首先,创建一个名为print_n_log.py的文件,并将下面的代码粘贴到其中:

"""
Returns a modified print() method that returns TEE to both stdout and a file
"""
import loggingdef run(logger_name, log_file, stream_level='ERROR'):
    stream_level = {
        'DEBUG': logging.DEBUG,
        'INFO': logging.INFO,
        'WARNING': logging.WARNING,
        'ERROR': logging.ERROR,
        'CRITICAL': logging.CRITICAL,
    }[stream_level]

    # create logger with 'logger_name'
    logger = logging.getLogger(logger_name)
    logger.setLevel(logging.DEBUG)
    # create file handler which logs even debug messages
    fh = logging.FileHandler(log_file)
    fh.setLevel(logging.DEBUG)
    # create console handler with a higher log level
    ch = logging.StreamHandler()
    ch.setLevel(stream_level)
    # create formatter and add it to the handlers
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    fh.setFormatter(formatter)
    ch.setFormatter(formatter)
    # add the handlers to the logger
    logger.addHandler(fh)
    logger.addHandler(ch)
    def modified_print(*args):
        s = ' '.join([str(a) for a in args])
        logger.info(s)
    return modified_print

上面的代码将创建一个修改过的print()方法,该方法将输出(stdout)和错误(stderr)重定向到主笔记本单元的输出和日志文件。

接下来,将此模块导入您的主笔记本:

import print_n_log

在您的下一个单元格中,让我们尝试调用预处理笔记本:

# Parameters for Preprocessing Notebook
#---------------------------------------------
IS_MASTER = True # Remember this? We need to set this to True in MASTER Notebook so that it does not use the default parameters in processing notebook.
RAW_FILE = f'/path/to/smallData.csv' # use "small data" here
PROCESSED_FILE = f'{DATA_DIR}processed.pkl' # always use pickle for fast I/O!
OTHER_PREPROCESS_PARAMETER = ... # like batch size, sliding# Let's save the original print method in ori_print
#---------------------------------------------------
ori_print = print# Now we set the print method to be modified print
#--------------------------------------------------
print = print_n_log.run('preproc', './logs/preprocess.log', 'DEBUG')# Now, we run the Preprocessing Notebook using the %run magic
#-------------------------------------------------------------
%run 'c_preprocess.ipynb'# Finally, after running notebook, we set the print method back to the original print method.
#-----------------------------------------------------
print = ori_print

注意,我们使用%run魔法来运行预处理笔记本。这是一个 IPython 魔术,让我们从当前的笔记本运行其他 Python 文件和 Jupyter 笔记本。我们使用这个命令来运行预处理笔记本中的所有代码。

通过调用print_n_log.run('preproc', './logs/preprocess.log', 'DEBUG'),我们修改了原始的 Python 内置print()方法,将输出重定向到屏幕和日志文件。'preproc'只是我们记录器的一个名字,你可以使用任何其他名字。运行后,您可以转到'./logs/preproc.log'来查看运行预处理笔记本记录的输出。最后一个参数'DEBUG'只是说“打印每个输出到屏幕上”。如果您只想在屏幕上看到错误(无输出),也可以使用'ERROR'

是的,就是这样!您可以使用相同的模板来调用建模笔记本和报告笔记本。但是,提示一下,您可能不希望记录报告笔记本的输出,所以您可以对它使用原始的 print()方法。

结论

这里的关键思想是将 Jupyter 笔记本中与其他部分没有太多纠缠的部分“模块化”,同时对整个数据管道有一个概览。

与封装的软件哲学不同,我们实际上并不想将代码封装在 python 文件中,然后再也看不到它们(尽管我们可以对一些实用程序方法这样做)。我们希望将大部分代码保存在笔记本中,因为在设计实验时,每个部分都应该是可变的。当一个部分被改变时,我们也想让它们可测试。通过留在 Jupyter notebook,我们知道我们的代码在用“小数据”运行时是可测试的。通过遵循这些指导方针,我们将拥有一个组织化和模块化的 Jupyter 项目,然而每个部分都是易于编辑和测试的。

要获得我帖子的通知,请在 Medium、 Twitter脸书上关注我。

构建机器学习项目

原文:https://towardsdatascience.com/structuring-machine-learning-projects-be473775a1b6?source=collection_archive---------7-----------------------

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

图 1:自由女神像周围的脚手架——照片由纽约公共图书馆Unsplash 拍摄

构建 ML 项目的模板指南

“分层方法被认为是比作为一个整体块实现协议更好的实践,因为分别实现概念上不同的问题有几个好处”Buschmann 等人(1996)[1]。在发表的时候,Buschmann 等人被认为是软件开发的一种新方法。自从 POSA 第一卷发布以来已经过去了 20 多年,有趣的是,设计的思想仍然非常适用于现代软件开发。

那么,软件开发与构建机器学习项目到底有什么关系呢?实际编写的机器学习代码只是机器学习系统的一小部分。基于前述的基础,我相信将大规模的机器学习项目视为一个软件项目是非常公平的——当然,不要忽视实践者的能力。在本文中,我将详细说明拥有良好结构布局的好处,然后我将提供一个模板结构布局,详细描述每个目录中可能包含的内容。

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

资料来源:霍尔特,加里&戈洛文,丹尼尔&达维多夫,尤金&菲利普斯,托德&埃布纳,迪特马尔&乔德里,维奈&杨,迈克尔&丹尼森,丹。(2015).机器学习系统中隐藏的技术债务。乳头。2494–2502.

注:拟议的结构仅作为一个框架,可能会发生变化。没有一种方法可以设计一个项目,所以最好的方法是选择并采用符合您的偏好和项目需求的实践。

“风格指南是关于一致性的。与本风格指南保持一致非常重要。项目内部的一致性更重要。一个模块或功能内的一致性是最重要的。

然而,要知道什么时候不一致——有时候风格指南的建议并不适用。当有疑问时,使用你最好的判断。看看其他例子,决定什么是最好看的。不要犹豫地问!"

——吉多·范·罗森[2]。

W hy 在意结构?

你未来的自己会感激:

在某些时候,我们会想要复制我们的工作。就我接触过的许多人而言,在我们工作的时候,很少考虑未来会发生什么对我们最有利。当构建预测模型时,我们更关心的是获得洞察力,从而构建一个强大的工作预测模型——我们希望把事情做好!快速测试、可视化和分析并不罕见,但这种环境并不适合考虑结构,因此很难返回代码并理解您当时得出的某些结论或您当时的思维过程。

回到过去的项目,如果你不得不考虑是否应该执行make_column.pynew_make_column.pyfixed_make_column.py来完成工作,那么这就是我称之为 HDW(高度无组织工作)的症状。无法重现结果不仅令人尴尬,对灵魂来说也是相当痛苦的,因此,从长远来看,通过预先制定项目结构,我们为自己做了一件好事。好的项目结构鼓励那些让回到过去的工作充满快乐的实践。

别人会感激:

当一个项目组织良好时,它往往是自文档化的。出于各种原因,有人可能想要参观你的作品来扩展它、展示它或者仅仅是从中学习。可能想要访问您的工作的人是不熟悉您的项目的人,所以通过维护良好的结构,您为他们节省了大量的时间,因为他们可以通过简单地查看结构来对您所做的事情有一个大致的了解,而不必解析大量的文档或代码来找到他们正在寻找的特定功能——也就是说,这也意味着协作过程将更加容易,这在任何团队活动中总是高效团队的一个有用特征。

再现性:

我们简要地谈到了这个话题,但是由于它是 ML 中的一个至关重要的因素——在数据科学、深度学习、计算机视觉和自然语言处理等领域也是如此——所以必须明确地提到可再现性。在 Sugimura,P. Hartl,F. 2018[3]中,提供了阻碍模型再现能力的各种无意方式以及修复这些问题的解决方案。这类问题的例子有数据起源、特征起源、模型起源等等。此外,Sugimura,P. Hartl,F. 2018[3]指出,用于构建具有系统架构的可再生管道的方法从一开始就将该概念视为一等公民[4],为了使该架构实现通用性和可扩展性,该项目分为多个部分。拥有良好的项目结构有助于实现这个目标!

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

图二。整体系统架构示例。资料来源:杉村,P. Hartl,F. 2018。建立一个可复制的机器学习管道。arXiv。[3].

为了进一步扩展再现性的话题,值得注意的是,你做的每件事,你可能都要再做一遍——这就像墨菲定律的版本*。出于各种原因,你的工作可能需要复制,无论是你意识到有一个缺陷,还是你离开公司,有人想延长你的工作。因此,无论你做了什么都可能需要重做,如果有一个组织良好的结构,那么知道从哪里开始就会简单得多。*

注:请参阅 Ericson 等人 2020[6]关于标准化项目结构的更多信息。

拟建结构布局

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

图 3:目录布局

数据

*外部:*这是从第三方来源提取的数据(不可变数据)。如果没有提取第三方数据,则该文件夹过时。

*过渡:*在事件外部数据可用时,该数据将是我们通过使用src/data目录中的脚本为特征工程加载的数据。该数据集是通过执行各种连接和/或合并来组合外部和原始数据而生成的。

*已处理:*这是使用各种机器学习技术转换的数据。我们将在src文件夹中进入的 features 文件夹对数据执行各种转换,以使其为建模做好准备。为了缩短模型的训练时间,保存处理过的数据不失为一个好主意。

Raw: 拥有数据的本地子集副本可以确保您有一个静态数据集来执行任务。此外,这克服了由于网络延迟问题导致的任何工作流故障。这个数据应该被认为是不可变的。如果没有外部数据,那么这是由src\data中的脚本下载的数据。

车型

我们使用src\models中的脚本来训练我们的机器学习模型。我们可能需要将该模型与其他模型一起恢复或重用,以构建一个集合或进行比较,并且我们可以决定要部署的模型。为此,我们将训练好的模型保存到一个文件中(通常是 pickle 格式),该文件将保存在这个目录中。

笔记本

Jupyter 笔记本电脑非常适合制作原型、探索和交流发现,但是它们不太适合长期发展,并且在可重复性方面效率较低。笔记本可以进一步分为Notebooks\explorationsNotebooks\PoC等子文件夹。使用良好的命名约定有助于区分每个笔记本中填充了什么——一个有用的模板是<步骤> - <用户> - <描述>。ipynb(即 01-kpy-eda.ipynb ),其中步骤用作排序机制,创建者的名字首字母、姓氏的前两个字母以及笔记本内容的描述。

Src

*数据:*在这个目录中,我们有脚本,这些脚本从数据生成的任何地方接收数据,并转换这些数据,使其处于可以进行进一步特性工程的状态。

*特性:*在这个目录中,我们有一个脚本来处理数据,并将其转换成一种可以被我们的机器学习模型使用的格式。

*模型:*包含用于构建和训练我们的模型的脚本。

结论

拥有一个结构化的目录布局对于组织 ML 项目中数据科学团队的思维非常有用。在项目开始时花一些时间思考你将如何规划未来的工作并将其记录为你的标准化项目结构是很有用的(Ericson et al 2020。)[6].如果在项目过程中需要重命名、添加或删除某个目录,这是没有问题的,因为结构不是严格的,但是应该有一种方法向团队的其他成员提出这一点,以便您可以批准这一更改。在你独自进行一个项目或者作为一个单独的实践者的情况下,记录变化仍然是一个好主意,这样你就可以在你进行项目的过程中跟踪你的思维过程。

在这篇文章中,我没有提到其他重要的东西,如 README.md、environment.yml/requirements.txt 和测试。在以后的文章中,我会深入探讨这些问题。我还将在以后的文章中构建一个自定义管道。在 Medium 或 Twitter 上关注我吧 @kurtispykes 关注我的下一篇文章。

其他有用资源:

马志威 - 前期管理你的数据结构

Mateusz Bednarski - 机器学习项目的结构化和自动化工作流第一部分

Mateusz Bednarski - 机器学习项目的结构化和自动化工作流第二部分

Semi Koen - 成为数据科学家并不能让你成为软件工程师

Semi Koen - 构建机器学习管道

FloydHub - 如何计划和执行你的 ML 和 DL 项目

参考文献

[1] Buschmann 等人(1996 年)。建筑模式,在布希曼,F(编辑。)面向模式的软件架构。约翰·威利的儿子们。第 32 页

[2]范·罗森,G .华沙,B .科格兰,N. 2001 年。PEP 8——Python 代码风格指南。查看 2020 年 3 月 25 日,<https://www.python.org/dev/peps/pep-0008/>

[3]杉村,p .哈特尔,F. 2018。建立一个可复制的机器学习管道。arXiv。

[4]《一等公民》(2020)维基百科。可在:https://en.wikipedia.org/wiki/First-class_citizen(访问日期:2020 年 3 月 26 日)

[5]《墨菲定律》(2020)维基百科。可在:https://en.wikipedia.org/w/index.php?title=Murphy%27s_law&行动=历史(访问时间:2020 年 3 月 25 日)

[6]埃里克森等人 2020。*团队数据科学流程是什么?。*微软。查看 2020 年 3 月 25 日,<https://docs . Microsoft . com/en-us/azure/machine-learning/team-data-science-process/overview #标准化-项目-结构 >

构建 ML 管道项目

原文:https://towardsdatascience.com/structuring-ml-pipeline-projects-97c16348be4a?source=collection_archive---------19-----------------------

一个有组织的代码库使你能够更快地实现变化,犯更少的错误,最终导致更高的代码和模型质量。阅读更多内容,了解如何使用 Tensorflow Extended (TFX)构建您的 ML 项目,这是一种简单明了的方法。

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

图片由来自 Pixabay 的 Francis Ray 拍摄

项目结构:需求

  • 启用multiple管道实验
  • 支持local执行模式和deployment执行模式。这确保了创建两个独立的运行配置,第一个用于本地开发和端到端测试,第二个用于在云中运行。
  • Reuse code如果这样做有意义的话,跨管道变量
  • 提供一个易于使用的CLI interface来执行具有不同configurations和数据的流水线

一个正确的实现还可以确保测试很容易被整合到您的工作流程中。

项目结构:设计决策

  • 用 Python。
  • 使用 Tensorflow Extended (TFX)作为管道框架。

在本文中,我们将演示如何以最少的麻烦在本地和 Kubeflow 管道系统上运行 TFX 管道。

设计决策引起的副作用

  • 通过使用 TFX,我们将使用tensorflow。请记住,tensorflow 支持更多类型的模型,如增强树
  • Apache Beam 可以在本地、kubernetes 运行的任何地方以及所有公共云提供商上执行。例子包括但不限于:GCP 数据流,Azure 数据砖。
  • 由于 Apache Beam,我们需要确保项目代码很容易被 python 的sdist打包,以获得最大的可移植性。这体现在项目的顶层模块结构上。(如果您使用外部库,请确保通过向 apache beam 提供一个参数来包含它们。阅读更多关于 Apache Beam:管理 Python 管道依赖关系 )。

**【可选】**在继续之前,花点时间阅读一下提供的 TFX CLI 。目前,运行起来非常慢,而且目录结构比它需要的要冗长得多。它也不包括任何关于可再现性和代码重用的注释。

目录结构及其背后的直觉

  • $project-name是项目的根目录
  • $project-name/ml包括机器学习相关的东西。
  • $project-name/ml/pipelines包括实际的 ML 管道代码
  • 通常,您可能会发现自己需要管理多个 ML 管道,例如$project-name/ml/pipelines/predict-sales$project-name/ml/pipelines/classify-fraud或类似的管道。
  • 下面是一个简单的tree视图:

$project-name/ml/pipelines包括以下内容:

  • data →少量代表性培训数据在本地运行,用于测试和 CI。如果您的系统没有专门的组件来从某个地方提取数据,那就是真的。如果是这样的话,请确保包含一个带有少量有限项目的抽样查询。
  • util →跨$pipeline-name重复使用和共享的代码。不需要包含input_fn_utils.pymodel_utils.py。在这里使用任何有意义的东西。以下是一些例子:

在我自己的项目中,在实用模块上抽象一些部分是有意义的,比如为 keras 模型构建命名的输入和输出层。

使用张量流变换输出构建服务签名元图。

使用关键字将特征预处理成组。

以及其他常见的重复性任务,如使用 Tensorflow 数据集 api 构建输入管道。

  • cli.py →管道的入口点和命令行界面。以下是使用 TFX 时需要考虑的一些常见问题。

通过使用[abseil](https://github.com/abseil/abseil-py),你可以声明和访问全局标志。每个模块都定义了特定的标志。这是一个分布式系统。这意味着常见的标志,如--data_dir=...--hparam_tuning--pipeline_root--ml_metadata_url--use_cache--train_epochs可以在实际的cli.py文件中定义。另外,可以在子模块上为每个管道定义更具体的参数。

这个文件充当系统的入口点。它使用pipeline.py中的内容来设置管道的组件,并根据某些标志(如--pipeline_name=$pipeline-name或其他配置)提供用户提供的模块文件(在树形示例中为constants.pymodel.pytraining.py)。

最后,对于组装好的管道,它通过使用一个--runner=标志来调用某个_runner.py文件。

  • pipeline.py →参数化管道组件声明和接线。这通常只是一个声明一堆 TFX 组件并返回一个tfx.orchestration.Pipeline对象的函数。

  • local_beam_dag_runner.py →使用便携式光束运行器进行本地运行的配置。这通常几乎不需要配置,只需使用BeamDagRunner

  • kfp_runner.py →在 Kubeflow 管道上运行的配置。这通常包括不同的数据路径和管道输出前缀,并自动绑定 ml-metadata 实例。

注意:你可以有更多的运行者,像在 GCP 上运行的东西,只是配置更多的供应资源,如 TPU 实例,并行人工智能平台超参数搜索等。

$管道名称

这是用户提供的代码,用于制作不同的模型,安排不同的实验,等等。

由于有了util子模块,每个管道下的代码应该更精简。不需要把它分成 3 个以上的文件。但是这并不禁止将你的代码分割成更多的文件。

从实验中,我收敛到一个constantsmodeltraining分裂。

  • constants.py →申报。要跟踪的训练参数、超参数键和声明、特性键、特性组、评估配置和指标的合理默认值。这里有一个小例子:

  • model.py →模型定义。通常包含一个build_keras_model函数,并使用来自util$pipeline-name.constants的导入。这是我最近一个项目中的一个例子:

  • 最后,training.py包括训练模型所需的所有细节。这通常包括:预处理定义、超参数搜索、设置训练数据或模型——并行策略和张量板日志,以及保存用于生产的模块。

就是这样。谢谢你看完!

我希望你喜欢读这篇文章,就像我喜欢写它一样。

纠结于数据不平衡?半监督和自我监督学习帮助!

原文:https://towardsdatascience.com/struggling-with-data-imbalance-semi-supervised-self-supervised-learning-help-4de8b8f23490?source=collection_archive---------6-----------------------

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

(图片由作者提供)

入门

反思标签对改善班级不平衡学习的价值

给大家介绍一下我们最新的作品,已经被 NeurIPS 2020 接受: 反思标签对于改善班级不平衡学习的价值 。这项工作主要研究一个经典但非常实用和常见的问题:数据类别不平衡下的分类问题(也称为数据的长尾分布)。通过理论建模和大量实验,我们发现半监督自监督学习都能显著提高不平衡数据下的学习性能。

源代码(和相关数据,> 30 个预训练模型)可以通过这个 GitHub 链接找到:https://github.com/YyzHarry/imbalanced-semi-self

首先,我想用一句话来总结本文的主要贡献:我们已经从理论和经验上验证了,对于具有不平衡数据(类别)的学习问题,使用

  • 半监督学习 —即使用更多未标记数据;或者,
  • 自监督学习——即不使用任何额外的数据,只需先做一步自监督预训练对已有不平衡数据没有标签信息

都可以大大提高模型性能。它们的简单性和通用性也使得它们很容易与不同的经典方法相结合,以进一步增强学习效果。

接下来,我们将进入正文。我将首先介绍数据不平衡问题的背景和目前的一些研究现状。那我就介绍一下我们的思路和方法,省略不必要的细节。

背景

数据不平衡的问题在现实世界中非常普遍。对于真实数据,不同类别的数据量一般不会是理想的均匀分布,而往往会不平衡;如果按照样本数量从高到低对类进行排序,会发现数据分布有一个*【长尾】*,也就是我们所说的长尾效应。大规模数据集通常呈现这样的长尾标签分布:

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

大规模数据集通常呈现长尾标签分布(图片由作者提供)。

当然,不仅对于分类任务,对于物体检测或实例分割等其他任务,在很多常用的数据集中也存在类别不平衡的情况。除了视觉领域的数据,对于涉及安全或健康的关键应用,如自动驾驶和医疗/疾病诊断,数据本身就严重失衡。

**为什么会出现不平衡?**一般的解释是,特定类型的数据难以收集。以物种分类为例(例如,大规模数据集 iNaturalist),某些物种(如猫、狗等。)都很常见,但有些品种(如胡兀鹫)非常罕见。对于自动驾驶,正常行驶的数据会占大部分,而实际发生异常情况/车祸的数据很少。就医疗诊断而言,与正常人口相比,患有某些疾病的人数也极其不平衡。

**那么,不平衡或长尾数据有什么问题呢?**简单地说,如果你直接将不平衡样本扔给模型用 ERM 学习,很明显,模型在主要类别的样本上会学习得更好,但在次要类别上概括得很差,因为它看到的主要类别的样本远远多于次要类别。

**那么,目前有哪些解决学习不平衡问题的方法呢?**我总结的目前主流的方法大致分为以下几类:

  1. 重采样:具体来说,可以分为对少数样本过采样,或者对多数样本欠采样。然而,过采样容易过度适应小类,并且不能学习更鲁棒和可概括的特征,并且它通常在非常不平衡的数据上表现不佳;另一方面,欠采样会导致主类中严重的信息丢失,从而导致欠拟合。
  2. 合成样本:即生成与少数样本相似的“新”数据。经典方法 SMOTE 对随机选取的少数样本使用 K 近邻选取相似样本,通过线性插值得到新样本。
  3. 重新加权:给不同的类别(甚至不同的样本)分配不同的权重。注意,这里的权重可以是自适应的。这种方法有许多变种。最简单的就是按照类别数量的倒数来加权。
  4. 迁移学习:这类方法的基本思想是将多数类和少数类分别建模,将多数类的学习信息/表征/知识迁移给少数类。
  5. 度量学习:本质上是希望学习更好的嵌入,更好的建模少数类附近的边界/边距。
  6. 元学习/领域适应:可以使用头部和尾部数据的不同处理来自适应地学习如何重新加权,或者将问题公式化为领域适应问题。

至此,大致总结了背景和常用方法;然而,即使有数据重采样或类平衡损失等专门算法,在极端数据不平衡的情况下,深度模型性能的退化仍然普遍存在。因此,了解不平衡数据标签分布的影响是非常重要的。

我们的动机和想法

与以前的方法不同,我们考虑如何平衡这些不平衡数据标签的“价值”。然而,与平衡数据不同,不平衡学习环境中的标签扮演着令人惊讶的争议角色,这导致了对其价值的持续困境:(1)一方面,有标签监督的学习算法通常会比无监督的学习算法产生更准确的分类器,证明了标签的值;(2)然而,另一方面,不平衡的标签自然会在学习过程中强加“标签偏差”,其中决策边界可以由多数类显著驱动,这表明了标签的负面影响。结果,不平衡的标签就像一把双刃剑;一个很重要的问题是如何最大限度地挖掘标签的价值来改善类不平衡学习

因此,我们试图对上述两种不同的观点分别进行系统的分解和分析。我们的结论表明,对于视角,不平衡标签的值可以被充分利用,从而大大提高最终分类器的准确性:

  • 正面我们发现,当有更多的未标注数据时,这些不平衡的标注提供了稀缺的监管信息。通过使用这种监督,我们可以使用半监督学习来显著改善最终的分类结果,即使未标记数据也具有长尾分布。
  • 然而,我们认为不平衡的标签并不总是有用的。标签失衡几乎肯定会导致标签偏差。所以在训练时,我们首先想到的是“抛弃”标签信息,通过自监督学习学习一个好的初始表征。我们的结果表明,通过这种自我监督的预训练方法获得的模型也可以有效地提高分类的准确性。

具有未标记数据的不平衡学习

我们首先研究了一个简单的理论模型,并对原始不平衡数据和额外数据的不同成分如何影响整个学习过程建立了直觉。我们考虑这样的场景,其中我们有一个在不平衡训练集和一定量的未标记数据上获得的基本分类器,并且我们可以使用这个基本分类器来伪标记这些数据。这里,未标记的数据也可能是(高度)不平衡的。我在这里省略了细节,有兴趣的读者可以参考我们的论文。简而言之,我们展示了几个有趣的观察结果:

  • 训练数据的不平衡影响了我们估计的准确性;
  • 未标记的数据不平衡影响获得如此好的估计的概率。

**半监督不平衡学习框架:**我们的理论发现表明使用伪标签(因此训练数据中的标签信息)可以帮助不平衡学习;这种方法的有用程度受到数据不平衡的影响。受此启发,我们系统地探索了未标记数据的有效性。我们采用最简单的自训练半监督学习方法,在无标签数据上生成伪标签,然后一起训练。准确地说,我们首先在原始不平衡数据集上进行正常训练,以获得中间分类器,并应用它来生成未标记数据的伪标记。通过组合两部分数据,我们最小化联合损失函数来学习最终模型。

值得注意的是,除了自训练,其他半监督算法也可以很容易地纳入我们的框架,只需修改损失函数;同时,由于我们没有指定最终模型的学习策略,因此,半监督框架也可以很容易地与现有的不平衡算法相结合。

**实验:**现在进入激动人心的部分——实验:!让我们先谈谈实验的设置——我们选择了人工生成的长尾版本的 CIFS-10 和 SVHN 数据集,因为它们都有自然的对应的无标记部分,具有相似的数据分布:CIFS-10 属于 mini-Images 数据集,而 SVHN 本身有一个额外的数据集,可以用来模拟无标记数据。本部分设置详见本公司论文;我们也开源了相应的数据供大家使用和测试。对于未标记数据,我们还考虑了其可能的不平衡/长尾分布,并明确比较了来自不同分布的未标记数据的影响。

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

典型的原始不平衡数据分布和可能的未标记数据分布(作者提供的图像)。

实验结果见下表。我们可以清楚地看到,使用非标记数据时,半监督学习能够显著提高最终的分类结果,并且在不同的(1)数据集、(2)基本学习方法、(3)标记数据的不平衡比率、(4)非标记数据的不平衡比率之间,能够带来一致的改进。此外,我们还在附录(5)中提供了不同半监督学习方法的比较,以及不同数据量的消融研究。

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

(作者图像)

最后给出了定性实验结果。我们在训练集和有/无未标记数据的测试集上绘制 t-SNE 可视化。从图中可以直观地看出,使用未标记数据有助于建立更清晰的类边界模型,并促进类之间更好的分离,尤其是对于尾部类样本。这个结果也符合我们的直觉理解。对于尾部样本,这些区域的数据密度较低,模型在学习过程中无法很好地模拟这些低密度区域的边界,导致模糊性和泛化能力较差。相比之下,未标记数据可以有效增加低密度区域的样本量,而更强正则化的加入使得模型更好地对边界进行再建模。

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

(作者图像)

关于半监督不平衡学习的进一步思考

虽然半监督学习可以显著提高不平衡数据的性能,但半监督学习本身存在一些实际问题,这些问题在不平衡的情况下会进一步放大。接下来,我们将通过设计相应的实验系统地阐述和分析这些情况,并激发下一步对不平衡标签的负值的思考和研究。

第一,未标记数据与原始数据的相关性对半监督学习的结果影响很大。比如对于 CIFAR-10 (10 类分类),得到的未标注数据可能不属于原来的 10 类中的任何一类(比如胡兀鹫……)。在这种情况下,未标记的信息可能是不正确的,并且对训练和结果有很大的影响。为了验证这一观点,我们将未标记数据和原始训练数据固定为具有相同的不平衡比例,但改变未标记数据和原始训练数据之间的相关性,以构建不同的未标记数据集。从图 2 可以看出,未标记数据的相关性需要达到 60%以上,才能对不平衡学习有积极的帮助。

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

(图片由作者提供)

由于原始训练数据是不平衡的,未标记的数据也可能是高度不平衡的。例如,在医疗数据中,您构建了一个自动诊断某种疾病的数据集。其中,阳性病例极少,仅占总数的 1%;但是,由于现实中的发病率在 1%左右,即使已经收集了大量未标记的数据,其中真正患病的数据数量仍然很少。然后,在考虑相关性的同时,如图 3 所示,我们首先使未标记集合具有足够的相关性(60%),但是改变未标记数据的不平衡比例。在这个实验中,我们将原始训练数据的不平衡比例固定为 50。可以看出,对于未标记数据,当未标记数据过于不平衡时(在这种情况下,不平衡比例高于 50),使用未标记数据实际上可能会使结果变得更糟。

上述问题在某些实际的不平衡学习任务中可能非常普遍。例如,在医疗/疾病诊断应用中,可以获得的未标记数据大多是从正常样本中采集的,这首先造成了数据的不平衡;其次,即使是有疾病的样本,也很可能是由许多其他混杂因素引起的,而这将降低疾病本身的相关性。因此,在一些难以使用半监督学习的极端情况下,我们需要一种完全不同但也有效的方法。自然,我们就从 的角度出发,解释另一个思路——自我监督学习。

来自自我监督的不平衡学习

同样,我们从另一个理论模型开始研究不平衡的学习如何受益于自我监督。我们得到的结果也是鼓舞人心和有趣的:

  • 以高概率,我们使用通过自监督任务学习的表示获得满意的分类器*,错误概率在特征维度上指数衰减;*
  • 训练数据不平衡影响我们获得这样一个令人满意的分类器的概率。

自监督不平衡学习框架:为了利用自监督克服固有的“标签偏向”,我们提出在第一阶段抛弃标签信息,进行自监督预训练 (SSP)。该过程旨在从不平衡数据中学习独立于标签的更好的初始化/特征信息。过了这个阶段,我们可以用任何标准的训练方法来学习最终的模型。由于预训练与正常训练阶段使用的学习方法无关,因此该策略与任何现有的不平衡学习算法兼容。一旦自我监督产生良好的初始化,网络可以从预训练任务中受益,并最终学习更一般的表示。

**实验:**激动人心的实验部分又来了;)这次我们不需要额外的数据。除了在长尾 CIFAR-10/100 上验证算法之外,我们还在大规模数据集 ImageNet 的长尾版本上进行验证,以及在 Naturalist 中的一个真实基准上进行验证。对于自监督算法,我们采用经典的旋转预测和最新的对比学习方法 MoCo 。在附录中,我们还提供了更多的消融研究,比较了 4 种不同的自我监督方法的效果。

实验结果显示在下面的两个表中。简而言之,使用 SSP 可以在不同的(1)数据集,(2)不平衡率,和(3)不同的基本训练算法之间带来一致的和大的改进。

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

(图片由作者提供)

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

(图片由作者提供)

最后,我们还展示了自我监督的定性结果。和以前一样,我们分别绘制训练集和测试集的 t-SNE 投影。从图中我们不难发现,正常 CE 训练的决策边界会被头类样本大大改变,导致测试时尾类样本大量“泄漏”,不能很好的泛化。相比之下,使用 SSP 可以保持清晰的分离效果,并减少尾部样品的泄漏,尤其是相邻头尾类之间的泄漏。这个结果也可以直观的理解:自监督学习使用额外的任务来约束学习过程,更好的学习数据空间的结构,提取更全面的信息。因此可以有效缓解网络对高层语义特征的依赖和对尾部数据的过拟合。学习到的特征表示将更加健壮且易于概括,从而在下游任务中表现得更好。

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

(图片由作者提供)

结束语

总结这篇文章,我们首先尝试通过两种不同的观点来理解和利用不平衡数据(标签),即半监督自监督学习,并验证了这两种框架都可以改善不平衡学习问题。它有非常直观的理论分析和解释,用非常简洁通用的框架来改进长尾数据分布下的学习任务。这些结果可能会引起更广泛的不同应用领域的兴趣。最后,我附上了几个与本文相关的链接;感谢阅读!

【https://github.com/YyzHarry/imbalanced-semi-self】代号 : 代号

网站 / 视频:https://www.mit.edu/~yuzhe/imbalanced-semi-self.html

呆在家里?为什么不造一个 GPU 盒子!

原文:https://towardsdatascience.com/stuck-at-home-why-not-build-a-gpu-box-ac93bee21786?source=collection_archive---------31-----------------------

构建定制计算机以适应深度学习模型(或玩 GTA V:)

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

作者图片

在使用机器学习应用程序工作了一段时间后,我很兴奋地尝试了一些深度学习模型。可能我在开始时面临的最大障碍是工作流,特别是找到一个可访问的、流动的 GPU 驱动的工作环境。我的 Mac Pro(被亲切地称为垃圾桶型号)没什么用。我开始使用云服务,但怀念在没有仪表运行的情况下拥有自己的存储数据和运行代码的地方。所以我决定自己造一个 GPU 盒子。起初,这是相当令人生畏的(大写 I),但事情进行得相当顺利,最终结果是非常令人满意的。万一我的冒险对你有意思或有帮助,就在这里!

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

作者图片

母板

华硕 ROG Strix B360-I

我从移动电话开始。让我先说一下,如果你想使用多个 GPU,这个版本不会在没有修改的情况下适用于你的情况。我决定用单个 GPU 构建一个迷你 ITX 外形(即小型)的机器,因为这是我的第一次构建,我认为这将具有足够的挑战性。此外,我喜欢我的房子有一种装饰和组装的感觉,包括我的办公室。我并不是想创造一个拥有巨大霓虹灯塔的人类洞穴游戏天堂。但是我可以说,我爱这块板!对于游戏玩家来说,这是我为该系统购买的唯一一个 RGB 项目,它非常完美。但是在实际的构建中,真正的好处是内置的 I/O 屏蔽。正如一个有用的 youtube 游戏电脑组装先生指出的,安装防护罩可能是最具挑战性的部分。你已经是建造自己钻机的英雄了;你可以抓住机会而不会感到内疚。

情况

Thermaltake Core V1 雪景版

我在过程的早期就决定了这个案例,因为它决定了其他一些组件可能的大小。这也很有趣,因为有很多选择。我选择核心 V1 是因为,虽然它肯定是一个迷你,但它不是超级小,所以仍然有足够的空间来放置电缆等。一些需要注意的事情是:CPU 冷却器的高度,GPU 的长度,以及 3)电源的长度(PSU)。这个箱子设计得非常好。我喜欢这一事实,即董事会坐在垂直而不是侧面,所以你的组件不是“挂在亲爱的生活”作为一个评论家指出。

中央处理器

英特尔酷睿 i7–8700k

我想我可能在处理器上买多了。我必须有第 8 代才能适应主板,但型号中的“K”意味着处理器没有锁定,因此可以“超频”这是推动处理器比正常速度更快的游戏语言;然而,我的主板无论如何都不支持这个功能(它是 B 系列而不是 Z 系列)。

CPU 冷却器/散热器

配备 NF-A9 92 毫米风扇的 NOC tua NH-U9S

冷却器可能是一台小机器上的一个地方,在那里你会多付一点钱,但这是我列表中评价最高的项目(评论者星级)。它仍然比许多液体冷却器便宜(尽管没有那么可爱)。另外,谁会因为东西太便宜而不愿意多花 30 美元就把它们烧掉呢?你会学会爱这个孩子的。

随机存取存储

海盗船复仇 LPX 32GB(2x16GB)DDR 4 DRAM 2666 MHz

对 RAM 没有太多评论。我决定最大限度地发挥董事会的作用。这些棍子位置很低,因此将风扇安装在散热器上清理它们并不困难。

储存;储备

三星 860 EVO 1TB 2.5 英寸 SATA III SSD

目前我有一个固态硬盘。箱子里有专门的空间,如果需要的话可以再加一秒钟。

到目前为止建筑

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

作者图片

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

作者图片

供电设备

EVGA Supernova 650 G3,80 Plus Gold 650W,完全模块化

对于 PSU,我使用了 GPU 指定的推荐功率(“完全模块化”意味着没有预先连接电缆,所以您只使用您需要的)。PSU 是我最担心能不能装进箱子里的部分,但它还是进去了。

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

作者图片

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

作者图片

国家政治保卫局。参见 OGPU

微星 GeForce RTX 2070

最后,也是最重要的部分。我选择 RTX 2070 是因为它的性价比。定制机器的另一个好处是,如果需要,你可以在未来相对容易地更新你的 GPU。

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

作者图片

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

作者图片

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

作者图片

成品

只要有一点计划和运气,这一切都非常适合!现在你已经完成了硬件,你可以开始安装软件,将你的机器变成一台深度学习机器。

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

作者图片

学生在 R 和手工中的 t 检验:如何在不同场景下比较两组

原文:https://towardsdatascience.com/students-t-test-in-r-and-by-hand-how-to-compare-two-groups-under-different-scenarios-5ad79fce2130?source=collection_archive---------32-----------------------

学生对两个样本的 t 检验用于检验两组是否不同,基于从这两组中抽取的两个样本的比较。我们展示了如何在 r 中手工计算它。

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

照片由杰森·登特拍摄

介绍

推断统计学分支中最重要的测试之一是学生的 t-测试。 1 学生对两个样本的 t 检验用于检验两组(两个群体)在一个定量变量方面是否不同,基于从这两组中抽取的两个样本的比较。换句话说,一个学生对两个样本的 t 检验可以确定你的两个样本所来自的两个群体是否不同(这两个样本是在一个定量连续变量上测量的)。 2

这种统计检验背后的推理是,如果你的两个样本明显不同,可以假设从中抽取样本的两个总体是不同的。相反,如果两个样本相当相似,我们就不能拒绝两个群体相似的假设,所以在手头的数据中没有足够的证据可以得出样本从中抽取的两个群体是不同的结论。请注意,这种统计工具属于推断统计学的一个分支,因为从样本研究中得出的结论可以推广到总体,即使我们没有整个总体的数据。

为了比较两个样本,通常要比较每个样本的集中趋势。在学生 t 检验的情况下,均值用于比较两个样本。然而,在某些情况下,平均值不适于比较两个样本,因此通过威尔科克森检验使用中值来比较它们。这篇文章已经很长很完整了,Wilcoxon 测试在另一篇文章中有所涉及,同时还有一些关于何时使用其中一种测试的说明。

这两种检验(Student’s t 检验和 Wilcoxon 检验)具有相同的最终目标,即比较两个样本,以确定从中抽取样本的两个群体是否不同。请注意,学生的 t 检验比 Wilcoxon 检验更有效(即,如果存在真正的差异,它更经常检测到显著差异,因此学生的 t 检验可以检测到较小的差异),但学生的 t 检验对异常值和数据不对称很敏感。此外,在这两种测试的每一种中,都存在几个版本,每个版本使用不同的公式来得出最终结果。因此,有必要了解两种测试之间的差异以及使用哪种版本,以便根据问题和手头的数据进行适当的分析。

在本文中,我将首先一步一步地详细说明如何手工执行独立样本和配对样本的所有版本的学生 t 检验。为了说明和方便起见,将对一小组观察值进行分析。然后,我将展示如何用完全相同的数据在 R 中执行这个测试,以便验证手工发现的结果。还将介绍假设检验背后的推理、p 值和结果的解释以及该检验的假设。

请注意,本文的目的是展示如何用 R 手工计算学生的 t 检验,因此我们避免测试假设,并假设在本练习中所有假设都满足。为了完整性,我们仍然提到假设,如何测试它们,以及如果有一个不满足,还有什么其他测试。感兴趣的读者可以看看文章的结尾,了解更多关于这些假设的信息。

无效假设和替代假设

在开始手动计算学生的 t 检验之前,让我们回顾一下该检验的无效假设和替代假设:

  • H0: μ1=μ2
  • H1: μ1≠μ2

其中μ1 和μ2 是从中抽取样本的两个总体的平均值。

如简介中所述,虽然从技术上讲学生的 t 检验是基于两个样本的均值比较,但这种检验的最终目标实际上是检验以下假设:

  • H0:这两个群体很相似
  • H1:这两种人群是不同的

这是在一般情况下,我们只想确定两个群体是否不同(就因变量而言)。从这个意义上来说,我们事先并不认为某个特定群体比另一个群体大或小。这种测试被称为双边或双边测试。

如果我们对一个群体比另一个群体大或小有一些先验信念,学生的 t 检验也允许检验以下假设:

  • H0: μ1=μ2
  • H1: μ1>μ2

或者

  • H0: μ1=μ2
  • H1: μ1

In the first case, we want to test if the first population is significantly larger than the second, while in the latter case, we want to test if the first population is significantly smaller than the second. This type of test is referred as a 单边或单边测试。

一些作者认为,在实践中不应该使用单边检验,原因很简单,如果一个研究人员如此确信一个群体比另一个群体大(小),并且永远不会比另一个群体小(大),为什么她还需要检验显著性呢?这是一个相当哲学的问题,超出了本文的范围。感兴趣的读者被邀请去看 Rowntree (2000)中的部分讨论。

假设检验

在统计学中,许多统计检验是以假设检验的形式进行的。假设检验用于根据手头的数据(即样本)确定某个信念是否为真(似是而非)。大多数假设检验可以归结为以下 4 个步骤: 3

  1. 陈述无效假设和替代假设。
  2. 计算测试统计量,表示为 t-stat。计算检验统计量的公式在不同版本的学生 t 检验中有所不同,但它们具有相同的结构。请参见下面的场景 1 至 5,了解不同的公式。
  3. 给定测试的理论统计分布、分布参数和显著性水平α,找出临界值。对于一个学生的 t 检验及其扩展版本,要么是正态分布,要么是学生的 t 分布( t 表示学生分布, z 表示正态分布)。
  4. 通过比较 t-stat(在步骤 2 中找到)得出结论。)与临界值(在步骤中找到。3).如果 t-stat 位于拒绝区域(由临界值和测试方向决定),我们拒绝零假设,否则我们不拒绝零假设。这两个选项(拒绝或不拒绝零假设)是仅有的两个可能的解决方案,我们从不“接受”一个假设。总是根据最初的问题来解释决定也是一个好的做法。

学生 t 检验的不同版本

根据样本是独立的还是成对的,以及根据总体的方差,两个样本的学生 t 检验有几种版本:

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

一方面,独立样本是指在不同的实验单位或不同的个体上采集的两个样本,例如当我们分别对女性和男性进行研究,或者对被随机分配到对照组和治疗组的患者进行研究时(一个患者只属于一个组)。另一方面,当测量是在相同的实验单元、相同的个体上收集时,我们面对成对的样本。例如在医学研究中,当在两个不同的时间测试治疗的效率时,经常会出现这种情况。在治疗前和治疗后,对相同的患者进行两次测量,在计算检验统计数据时,必须考虑两个样本之间的相关性,对每个受试者的测量值进行差异处理。成对样本通常是两个不同时间的测量结果,但不排除其他情况。假设我们要测试 50 名运动员左右眼的视力差异。虽然测量不是在两个不同的时间(之前-之后)进行的,但是很明显,每一个对象的双眼都是相互依赖的。因此,应该使用学生对成对样本的 t 检验来说明两个样本之间的相关性,而不是标准的学生对独立样本的 t 检验。

选择合适的学生 t 检验版本的另一个标准是总体的方差(不是样本的方差!)是已知的或未知的,是相等的或不相等的。这个标准相当简单,我们要么知道总体的方差,要么不知道。无法计算总体的方差,因为如果您可以计算总体的方差,这意味着您有整个总体的数据,那么就没有必要再进行假设检验了…所以总体的方差要么在语句中给出(在这种情况下使用它们),要么没有关于这些方差的信息,在这种情况下,假设方差是未知的。在实践中,总体的方差在大多数情况下是未知的,为了选择合适的测试版本,唯一要做的就是检查方差是否相等。然而,在假设检验的 4 个步骤之后的下一节中,我们仍然说明了如何手工和在 R 中做这个检验的所有版本。

如何手工计算学生的 t-test?

请注意,这些数据是人为的,并不代表任何真实的变量。此外,提醒可能满足也可能不满足假设。本文的重点是详细说明如何手工和在 R 中计算不同版本的测试,所以所有的假设都被假定为满足。此外,假设所有测试的显著性水平α=5%。

如果你对手工应用这些测试感兴趣,而不需要自己做计算,这里有一个闪亮的应用程序可以帮你做。你只需要输入数据,并通过侧边栏菜单选择合适的测试版本。还有一个图形表示,帮助您可视化测试统计和拒绝区域。希望你会觉得有用!

场景 1:具有 2 个已知方差的独立样本

对于第一个场景,假设下面的数据。此外,假设两个样本是独立的,两个总体的方差都等于 1,并且我们想检验两个总体是否不同。

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

所以我们有:

  • 每个样本中的 5 个观察值:n1=n2=5
  • 样本 1 的平均值= 0.02
  • 样本 2 的平均值= 0.06
  • 两个总体的方差= 1

按照假设检验的 4 个步骤,我们有:

  1. H0: μ1=μ2,H1:μ1μ2≠0。(≠因为要测试两种手段是否不同,所以在测试中不强加方向。)
  2. 测试统计:

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

3.临界值:z(α/2)= z(0.025)= 1.96(如果你很难找到临界值,请参见如何阅读统计表的指南)

4.结论:因此,拒绝区域从∞到-1.96 和从 1.96 到+∞。检验统计量在拒绝区域之外,所以我们不拒绝零假设 H0。就最初的问题而言:在 5%的显著性水平上,我们不排斥两个总体相同的假设,或者数据中没有足够的证据可以得出所考虑的两个总体不同的结论。

场景 2:具有 2 个相等但未知方差的独立样本

对于第二种情况,假设下面的数据。此外,假设两个样本是独立的,两个总体中的方差未知但相等,并且我们想测试总体 1 是否大于总体 2。

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

所以我们有:

  • 样本 1 中的 6 个观察值:n1=6
  • 样本 2 中的 5 个观察值:n2=5
  • 样本 1 的平均值= 1.247
  • 样本 2 的平均值= 0.1
  • 样本 1 的方差= 0.303
  • 样本 2 的方差= 0.315

按照假设检验的 4 个步骤,我们有:

  1. H0: μ1=μ2,H1:μ1-μ2 > 0。(>因为我们要检验第一个总体的均值是否大于第二个总体的均值。)
  2. 测试统计:

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

在哪里

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

因此

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

(请注意,由于假设两个总体的方差相等,因此会计算一个混合(公共)方差,表示为 s_p。)

3.临界值:t(α,n1+N2 2)= t(0.05,9)=1.833

4.结论:拒绝区域因此从 1.833 到+∞(只有一个拒绝区域,因为它是单侧测试)。检验统计量位于拒绝区域内,因此我们拒绝零假设 H0。就最初的问题而言:在 5%的显著性水平上,我们得出的结论是,总体 1 大于总体 2。

场景 3:具有 2 个不相等且未知方差的独立样本

对于第三种情况,假设下面的数据。此外,假设两个样本是独立的,两个总体中的方差都是未知且不相等的,并且我们想要检验总体 1 是否小于总体 2。

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

所以我们有:

  • 样本 1 中的 5 个观察值:n1=5
  • 样本 2 中的 6 个观察值:n2=6
  • 样本 1 的平均值= 0.42
  • 样本 2 的平均值= 1.247
  • 样本 1 的方差= 0.107
  • 样本 2 的方差= 0.303

按照假设检验的 4 个步骤,我们有:

  1. H0: μ1=μ2,H1:μ1-μ2 < 0。(
  2. 测试统计:

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

3.临界值:t(α,υ)其中

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

因此

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

自由度 8.28 在标准学生分布表中是不存在的,那就简单的取 8,或者用qt(p = 0.05, df = 8.28)在 R 中计算。

4.结论:因此,拒绝区域从∞到-1.851。检验统计量位于拒绝区域内,因此我们拒绝零假设 H0。就最初的问题而言:在 5%的显著性水平上,我们的结论是总体 1 小于总体 2。

场景 4:差异方差已知的配对样本

成对样本的学生 t 检验与独立样本的稍有不同,它们实际上更类似于单个样本的学生 t 检验。这是它的工作原理。我们实际上计算每对观察值的两个样本之间的差异,然后我们通过计算这些差异的检验统计量来处理这些差异,就像我们在做单样本学生 t 检验一样。

如果不清楚的话,这里有第四个场景作为说明。假设下面的数据。此外,假设两个样本是相关的(匹配的),总体差异的方差已知且等于 1,并且我们想测试总体差异是否不等于 0。

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

首先要做的是计算所有观测值对的差异:

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

所以我们有:

  • 对数:n=5
  • 差异的平均值= 0.04
  • 总体差异的方差= 1
  • 总体差异的标准偏差= 1

按照假设检验的 4 个步骤,我们有:

  1. H0: μD=0,H1: μD≠0
  2. 测试统计:

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

(这个公式与一个样本学生的方差已知的 t 检验公式完全相同,只是我们处理的是方差的平均值。)

3.临界值:z(α/2)= z(0.025)= 1.96

4.结论:因此,拒绝区域从∞到-1.96 和从 1.96 到+∞。检验统计量在拒绝区域之外,所以我们不拒绝零假设 H0。就最初的问题而言:在 5%的显著性水平上,我们不拒绝两个总体的差异等于 0 的假设。

场景 5:差异方差未知的配对样本

对于第五个也是最后一个场景,假设下面的数据。此外,假设两个样本是相关的(匹配的),总体差异的方差是未知的,并且我们想测试一种治疗在增加跑步能力方面是否有效(数值越高,跑步能力越好)。

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

首先要做的是计算所有观测值对的差异:

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

所以我们有:

  • 对数:n=5
  • 差异的平均值= 8
  • 样本差异的方差= 16
  • 样本差异的标准偏差= 4

按照假设检验的 4 个步骤,我们有:

  1. H0: μD=0 和 H1: μD>0(>因为我们想测试治疗是否有效,所以治疗是否对跑步能力有积极影响。)
  2. 测试统计:

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

(这个公式与一个样本学生的方差未知的 t 检验公式完全相同,只是我们处理的是方差的平均值。)

3.临界值:t(α,n1)= t(0.05,4)=2.132 ( n 是对数,不是观测数!)

4.结论:拒绝域从 2.132 到+∞。检验统计量位于拒绝区域内,因此我们拒绝零假设 H0。根据最初的问题:在 5%的显著性水平,我们得出结论,治疗对跑步能力有积极的影响。

这里总结了如何手工对两个样本执行不同版本的学生 t 检验。在接下来的小节中,我们将详细介绍如何在 r 中执行完全相同的测试。

如何计算学生在 R 中的 t 检验?

在 R 中进行 t-tests 之前,一个好的实践是借助箱线图(或者密度图,或者最终两者都有)按组可视化数据。两个方框相互重叠的箱线图给出了两个样本相似的第一个指示,因此,可以不拒绝相等平均值的零假设。相反,如果两个框不重叠,则表明两个样本不相似,因此,群体可能不同。然而,即使箱线图或密度图能很好地显示两组之间的比较,只有可靠的统计测试才能证实我们的第一印象。

在按组可视化数据后,我们在 R 中复制手工发现的结果。我们将看到,对于 t-test 的某些版本,R 中没有内置默认函数(至少就我所知,如果我弄错了,请不要犹豫让我知道)。在这些情况下,编写一个函数来手动复制结果。

请注意,我们对所有 5 个场景使用了相同的数据、相同的假设和相同的问题,以便于比较手工测试和 r。

场景 1:具有 2 个已知方差的独立样本

对于第一个场景,假设下面的数据。此外,假设两个样本是独立的,两个总体的方差都等于 1,并且我们想检验两个总体是否不同。

dat1 <- data.frame(
  sample1 = c(0.9, -0.8, 0.1, -0.3, 0.2),
  sample2 = c(0.8, -0.9, -0.1, 0.4, 0.1)
)
dat1##   sample1 sample2
## 1     0.9     0.8
## 2    -0.8    -0.9
## 3     0.1    -0.1
## 4    -0.3     0.4
## 5     0.2     0.1dat_ggplot <- data.frame(
  value = c(0.9, -0.8, 0.1, -0.3, 0.2, 0.8, -0.9, -0.1, 0.4, 0.1),
  sample = c(rep("1", 5), rep("2", 5))
)library(ggplot2)ggplot(dat_ggplot) +
  aes(x = sample, y = value) +
  geom_boxplot() +
  theme_minimal()

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

注意,如果你想用 [{ggplot2}](https://www.statsandr.com/blog/graphics-in-r-with-ggplot2/)画一个方框图而不自己写代码,你可以使用[{esquisse}](https://www.statsandr.com/blog/rstudio-addins-or-how-to-make-your-coding-life-easier/#esquisse) RStudio addin 。如果你喜欢默认图形,使用boxplot()功能:

boxplot(value ~ sample,
  data = dat_ggplot
)

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

这两个方框似乎重叠,说明这两个样本非常相似,因此我们倾向于认为我们无法拒绝两个总体相似的零假设。然而,只有正式的统计测试才能证实这一观点。

由于 R 中没有使用已知方差来执行 t 检验的函数,因此这里有一个函数,其参数接受两个样本(xy)、总体的两个方差(V1V2)、零假设下均值的差异(m0,默认为0)、显著性水平(alpha,默认为0.05)以及备选项(alternative"two.sided"(默认)、"less""greater"之一):

t.test_knownvar <- function(x, y, V1, V2, m0 = 0, alpha = 0.05, alternative = "two.sided") {
  M1 <- mean(x)
  M2 <- mean(y)
  n1 <- length(x)
  n2 <- length(y)
  sigma1 <- sqrt(V1)
  sigma2 <- sqrt(V2)
  S <- sqrt((V1 / n1) + (V2 / n2))
  statistic <- (M1 - M2 - m0) / S
  p <- if (alternative == "two.sided") {
    2 * pnorm(abs(statistic), lower.tail = FALSE)
  } else if (alternative == "less") {
    pnorm(statistic, lower.tail = TRUE)
  } else {
    pnorm(statistic, lower.tail = FALSE)
  }
  LCL <- (M1 - M2 - S * qnorm(1 - alpha / 2))
  UCL <- (M1 - M2 + S * qnorm(1 - alpha / 2))
  value <- list(mean1 = M1, mean2 = M2, m0 = m0, sigma1 = sigma1, sigma2 = sigma2, S = S, statistic = statistic, p.value = p, LCL = LCL, UCL = UCL, alternative = alternative)
  # print(sprintf("P-value = %g",p))
  # print(sprintf("Lower %.2f%% Confidence Limit = %g",
  #               alpha, LCL))
  # print(sprintf("Upper %.2f%% Confidence Limit = %g",
  #               alpha, UCL))
  return(value)
}test <- t.test_knownvar(dat1$sample1, dat1$sample2,
  V1 = 1, V2 = 1
)
test## $mean1
## [1] 0.02
## 
## $mean2
## [1] 0.06
## 
## $m0
## [1] 0
## 
## $sigma1
## [1] 1
## 
## $sigma2
## [1] 1
## 
## $S
## [1] 0.6324555
## 
## $statistic
## [1] -0.06324555
## 
## $p.value
## [1] 0.949571
## 
## $LCL
## [1] -1.27959
## 
## $UCL
## [1] 1.19959
## 
## $alternative
## [1] "two.sided"

以上输出概括了执行测试所需的所有信息:测试统计量、p-值、所用替代方案、两个样本平均值和总体的两个方差(将 R 中的结果与手工找到的结果进行比较)。

p-值可以照常提取:

test$p.value## [1] 0.949571

p-值为 0.95,因此在 5%显著性水平下,我们不拒绝相等平均值的零假设。数据中没有足够的证据拒绝这两种方法在人群中相似的假设。这个结果证实了我们手工发现的东西。

关于 p 值和显著性水平α的注记

对于那些不熟悉p-值概念的人来说,p-值是一个概率,和任何概率一样,它是从 0 到 1。p-值是在零假设为真的情况下,观察结果与我们测量的结果(通过样本)一样极端的概率。换句话说,假设零假设是真的,它是测试统计量和我们计算的一样极端的概率。如果观察结果不那么极端——如果零假设是真的,也不太可能发生——我们不会拒绝这个零假设,因为它被认为是可信的。如果这些观察结果被认为过于极端——在零假设的情况下不太可能发生——我们会拒绝零假设,因为它被认为太不可信而不真实。请注意,这并不意味着我们 100%确定这种可能性太大,有时会出现零假设被拒绝的情况,尽管它是真的(参见后面的显著性水平α)。

在上面的例子中,观察结果并不极端,两个平均值之间的差异也不极端,因此检验统计量也不极端(因为检验统计量部分基于两个样本平均值的差异)。拥有一个非极端的检验统计量并非不可能,这就是为什么 p 值很高的原因。p-0.95 的值实际上告诉我们,假设群体中的平均值差异为 0(零假设),则两个样本的平均值差异为-0.04(= 0.02–0.06)的概率等于 95%。95%的概率被明确认为是合理的,因此我们并不拒绝总体均值相等的零假设。

人们可能会想,“对于一个测试统计来说,什么太极端了?”大多数时候,我们认为,假设零假设为真,当出现这种极端检验统计量的概率低于 5%时,检验统计量过于极端,不可能偶然发生。你在统计学课程或教科书中非常经常看到的 5% (α=0.05)的阈值是许多领域使用的阈值。当一个p-值低于 5%的阈值时,我们认为如果零假设为真,观察值(以及测试统计量)是不太可能偶然发生,因此零假设被拒绝。当一个p-值高于 5%的阈值时,我们认为,如果零假设是真的,面对我们所得到的观察结果并不是不合理的,因此我们不拒绝零假设。

注意,我写的是“我们不拒绝零假设”,而不是“我们接受零假设”。这是因为有可能零假设事实上是错误的,但我们无法用样本证明这一点。假设一个嫌疑犯被指控谋杀,而我们不知道真相。一方面,如果我们收集了足够的证据证明嫌疑犯犯了谋杀罪,他就被认为有罪:我们拒绝他是无辜的无效假设。另一方面,如果我们没有收集到足够的对嫌疑犯不利的证据,尽管他可能实际上犯了罪,他还是被假定是无辜的:我们不能拒绝他是无辜的无效假设。即使他被释放了,我们也不能确定他没有犯罪,我们只是没有找到足够的证据来反对嫌疑人无罪的假设。这就是为什么我们不拒绝零假设,而是接受它的原因,也是为什么你会经常读到“数据中没有足够的证据来拒绝零假设”或“基于样本,我们无法拒绝零假设”之类的话。

显著性水平 α,来源于前面提到的阈值 5%,是当零假设事实上为真时拒绝零假设的概率。在这个意义上,为了能够得出结论,我们接受处理的是一个误差(5%)。如果我们不接受任何误差(0%的误差),我们将无法得出关于总体的任何结论,因为我们只能通过样本访问有限部分的总体。因此,在解释假设检验的结果时,我们永远不会有 100%的把握,除非我们可以获得整个群体的数据,但这样就没有理由再做假设检验了,因为我们可以简单地比较两个群体。我们通常允许这种误差(称为 I 型误差)为 5%,但为了在得出我们拒绝零假设的结论时更加确定,alpha 水平也可以设置为 1%(在一些罕见的情况下甚至可以设置为 0.1%)。

总结一下关于 p 你需要记住的——值和显著性水平α:

  • 如果 p 值小于预定的显著性水平α(通常为 5%),那么如果 p 值<为 0.05,我们拒绝零假设
  • 如果 p 值大于或等于预定的显著性水平α(通常为 5%),那么如果 p 值≥ 0.05,我们不拒绝零假设

这毫无例外地适用于所有的统计测试。当然,无效假设和替代假设会随着测试的不同而变化。

经验法则是,对于大多数假设检验来说,替代假设是你想要检验的,而无效假设是现状。带着这个极度谨慎(!)因为,即使它适用于所有版本的学生 t 检验,它也不适用于所有的统计检验。例如,当测试正态性时,您通常希望测试您的分布是否遵循正态分布。根据这条建议,你可以写出另一个假设 H1:分布遵循正态分布。然而,对于正态性检验,如夏皮罗-维尔克或科尔莫戈罗夫-斯米尔诺夫检验,情况正好相反;另一个假设是 H1:分布不遵循正态分布。所以对于每个测试,确保使用正确的假设,否则你的测试的结论和解释将是错误的。

场景 2:具有 2 个相等但未知方差的独立样本

对于第二种情况,假设下面的数据。此外,假设两个样本是独立的,两个总体中的方差未知但相等,并且我们想测试总体 1 是否大于总体 2。

dat2 <- data.frame(
  sample1 = c(1.78, 1.5, 0.9, 0.6, 0.8, 1.9),
  sample2 = c(0.8, -0.7, -0.1, 0.4, 0.1, NA)
)
dat2##   sample1 sample2
## 1    1.78     0.8
## 2    1.50    -0.7
## 3    0.90    -0.1
## 4    0.60     0.4
## 5    0.80     0.1
## 6    1.90      NAdat_ggplot <- data.frame(
  value = c(1.78, 1.5, 0.9, 0.6, 0.8, 1.9, 0.8, -0.7, -0.1, 0.4, 0.1),
  sample = c(rep("1", 6), rep("2", 5))
)ggplot(dat_ggplot) +
  aes(x = sample, y = value) +
  geom_boxplot() +
  theme_minimal()

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

与前面的场景不同,这两个框没有重叠,这说明这两个样本彼此不同。从这个箱线图中,我们可以预期该检验会拒绝总体均值相等的零假设。尽管如此,只有正式的统计测试才能证实这一预期。

R 里面有一个函数,简单来说就是t.test()函数。这个版本的测试实际上是“标准”学生对两个样本的 t 检验。请注意,假设两个总体的方差相等,因此我们需要在函数中用参数var.equal = TRUE(默认为FALSE)指定它,另一个假设是 H1:μ1-μ2>0,因此我们还需要添加参数alternative = "greater":

test <- t.test(dat2$sample1, dat2$sample2,
  var.equal = TRUE, alternative = "greater"
)
test## 
##  Two Sample t-test
## 
## data:  dat2$sample1 and dat2$sample2
## t = 3.4113, df = 9, p-value = 0.003867
## alternative hypothesis: true difference in means is greater than 0
## 95 percent confidence interval:
##  0.5304908       Inf
## sample estimates:
## mean of x mean of y 
##  1.246667  0.100000

上面的输出概括了执行测试所需的所有信息:测试的名称、测试统计、自由度、p-值、使用的备选项和两个样本均值(将 R 中找到的这些结果与手动找到的结果进行比较)。

可以照常提取 p 值:

test$p.value## [1] 0.003866756

p-值为 0.004,因此在 5%的显著性水平上,我们拒绝相等平均值的零假设。这个结果证实了我们手工发现的东西。

与第一个场景不同,这个场景中的p-值低于 5%,因此我们拒绝零假设。在 5%的显著性水平上,我们可以得出人口 1 大于人口 2 的结论。

在 R 中报告学生 t-test 结果的一种简单而又好的方法是使用{report}包中的report()函数:

# install.packages("remotes")
# remotes::install_github("easystats/report") # You only need to do that once
library("report") # Load the package every time you start Rreport(test)## Effect sizes were labelled following Cohen's (1988) recommendations.
## 
## The Two Sample t-test testing the difference between dat2$sample1 and dat2$sample2 (mean of x = 1.25, mean of y = 0.10) suggests that the effect is positive, significant and large (difference = 1.15, 95% CI [0.53, Inf], t(9) = 3.41, p < .01; Cohen's d = 2.07, 95% CI [0.52, 3.55])

如您所见,该函数为您解释了测试(以及p-值)。

注意report()功能可用于其他分析。如果你觉得这个有用,请查看 R 中的更多提示和技巧。

如果您的数据是长格式(长格式更好),只需使用~。例如,想象完全相同的数据如下所示:

dat2bis <- data.frame(
  value = c(1.78, 1.5, 0.9, 0.6, 0.8, 1.9, 0.8, -0.7, -0.1, 0.4, 0.1),
  sample = c(rep("1", 6), rep("2", 5))
)
dat2bis##    value sample
## 1   1.78      1
## 2   1.50      1
## 3   0.90      1
## 4   0.60      1
## 5   0.80      1
## 6   1.90      1
## 7   0.80      2
## 8  -0.70      2
## 9  -0.10      2
## 10  0.40      2
## 11  0.10      2

下面是如何用长数据在 R 中执行学生的 t 检验:

test <- t.test(value ~ sample,
  data = dat2bis,
  var.equal = TRUE,
  alternative = "greater"
)
test## 
##  Two Sample t-test
## 
## data:  value by sample
## t = 3.4113, df = 9, p-value = 0.003867
## alternative hypothesis: true difference in means is greater than 0
## 95 percent confidence interval:
##  0.5304908       Inf
## sample estimates:
## mean in group 1 mean in group 2 
##        1.246667        0.100000test$p.value## [1] 0.003866756

结果完全一样。

场景 3:具有 2 个不相等且未知方差的独立样本

对于第三种情况,假设下面的数据。此外,假设两个样本是独立的,两个总体中的方差都是未知且不相等的,并且我们想要检验总体 1 是否小于总体 2。

dat3 <- data.frame(
  value = c(0.8, 0.7, 0.1, 0.4, 0.1, 1.78, 1.5, 0.9, 0.6, 0.8, 1.9),
  sample = c(rep("1", 5), rep("2", 6))
)
dat3##    value sample
## 1   0.80      1
## 2   0.70      1
## 3   0.10      1
## 4   0.40      1
## 5   0.10      1
## 6   1.78      2
## 7   1.50      2
## 8   0.90      2
## 9   0.60      2
## 10  0.80      2
## 11  1.90      2ggplot(dat3) +
  aes(x = sample, y = value) +
  geom_boxplot() +
  theme_minimal()

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

对于这个版本的测试,R 中也有一个函数,它就是带有var.equal = FALSE参数的t.test()函数。FALSEvar.equal参数的默认选项,因此您实际上不需要指定它。这个版本的检验实际上是韦尔奇检验,当总体的方差未知且不相等时使用。要测试两个方差是否相等,可以使用 Levene 的测试({car}包中的leveneTest(dat3$value, dat3$sample))。请注意,另一个假设是 H1:μ1-μ2<0,因此我们还需要添加参数alternative = "less":

test <- t.test(value ~ sample,
  data = dat3,
  var.equal = FALSE,
  alternative = "less"
)
test## 
##  Welch Two Sample t-test
## 
## data:  value by sample
## t = -3.0841, df = 8.2796, p-value = 0.007206
## alternative hypothesis: true difference in means is less than 0
## 95 percent confidence interval:
##        -Inf -0.3304098
## sample estimates:
## mean in group 1 mean in group 2 
##        0.420000        1.246667

上面的输出概括了执行测试所需的所有信息(将 R 中找到的结果与手工找到的结果进行比较)。

可以照常提取 p 值:

test$p.value## [1] 0.00720603

p-值为 0.007,因此在 5%的显著性水平上,我们拒绝了均值相等的零假设,这意味着我们可以得出人口 1 小于人口 2 的结论。这个结果证实了我们手工发现的东西。

场景 4:差异方差已知的配对样本

对于第四种情况,假设下面的数据。此外,假设两个样本是相关的(匹配的),总体差异的方差已知且等于 1,并且我们想测试总体差异是否不等于 0。

dat4 <- data.frame(
  before = c(0.9, -0.8, 0.1, -0.3, 0.2),
  after = c(0.8, -0.9, -0.1, 0.4, 0.1)
)
dat4##   before after
## 1    0.9   0.8
## 2   -0.8  -0.9
## 3    0.1  -0.1
## 4   -0.3   0.4
## 5    0.2   0.1dat4$difference <- dat4$after - dat4$beforeggplot(dat4) +
  aes(y = difference) +
  geom_boxplot() +
  theme_minimal()

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

由于 R 中没有函数在已知差异方差的情况下对成对样本执行 t 检验,因此这里有一个函数的参数接受两个样本之间的差异(x)、总体差异方差(V)、零假设下的差异均值(m0,默认为0)、显著性水平(alpha,默认为0.05)以及备选项(alternative"two.sided"(默认)、"less""greater"):

t.test_pairedknownvar <- function(x, V, m0 = 0, alpha = 0.05, alternative = "two.sided") {
  M <- mean(x)
  n <- length(x)
  sigma <- sqrt(V)
  S <- sqrt(V / n)
  statistic <- (M - m0) / S
  p <- if (alternative == "two.sided") {
    2 * pnorm(abs(statistic), lower.tail = FALSE)
  } else if (alternative == "less") {
    pnorm(statistic, lower.tail = TRUE)
  } else {
    pnorm(statistic, lower.tail = FALSE)
  }
  LCL <- (M - S * qnorm(1 - alpha / 2))
  UCL <- (M + S * qnorm(1 - alpha / 2))
  value <- list(mean = M, m0 = m0, sigma = sigma, statistic = statistic, p.value = p, LCL = LCL, UCL = UCL, alternative = alternative)
  # print(sprintf("P-value = %g",p))
  # print(sprintf("Lower %.2f%% Confidence Limit = %g",
  #               alpha, LCL))
  # print(sprintf("Upper %.2f%% Confidence Limit = %g",
  #               alpha, UCL))
  return(value)
}test <- t.test_pairedknownvar(dat4$after - dat4$before,
  V = 1
)
test## $mean
## [1] 0.04
## 
## $m0
## [1] 0
## 
## $sigma
## [1] 1
## 
## $statistic
## [1] 0.08944272
## 
## $p.value
## [1] 0.9287301
## 
## $LCL
## [1] -0.8365225
## 
## $UCL
## [1] 0.9165225
## 
## $alternative
## [1] "two.sided"

上面的输出概括了执行测试所需的所有信息(将 R 中找到的结果与手工找到的结果进行比较)。

可以照常提取p-值:

test$p.value## [1] 0.9287301

p-值为 0.929,因此在 5%的显著性水平上,我们不拒绝差异均值等于 0 的零假设。数据中没有足够的证据否定两个人群的差异等于 0 的假设。这个结果证实了我们手工发现的东西。

场景 5:差异方差未知的配对样本

对于第五个也是最后一个场景,假设下面的数据。此外,假设两个样本是相关的(匹配的),总体差异的方差是未知的,并且我们想测试一种治疗在增加跑步能力方面是否有效(数值越高,跑步能力越好)。

dat5 <- data.frame(
  before = c(9, 8, 1, 3, 2),
  after = c(16, 11, 15, 12, 9)
)
dat5##   before after
## 1      9    16
## 2      8    11
## 3      1    15
## 4      3    12
## 5      2     9dat5$difference <- dat5$after - dat5$beforeggplot(dat5) +
  aes(y = difference) +
  geom_boxplot() +
  theme_minimal()

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

对于这个版本的测试,R 中有一个函数,它就是带有paired = TRUE参数的t.test()函数。这个版本的测试实际上是学生配对样本 t 检验的标准版本。注意,另一个假设是 H1: μD > 0,所以我们也需要添加参数alternative = "greater":

test <- t.test(dat5$after, dat5$before,
  alternative = "greater",
  paired = TRUE
)
test## 
##  Paired t-test
## 
## data:  dat5$after and dat5$before
## t = 4.4721, df = 4, p-value = 0.005528
## alternative hypothesis: true difference in means is greater than 0
## 95 percent confidence interval:
##  4.186437      Inf
## sample estimates:
## mean of the differences 
##                       8

注意,我们按照这个顺序写了after,然后是before。如果先写before,再写after,一定要把备选改为alternative = "less"

如果您的数据是长格式,使用~:

dat5 <- data.frame(
  value = c(9, 8, 1, 3, 2, 16, 11, 15, 12, 9),
  time = c(rep("before", 5), rep("after", 5))
)
dat5##    value   time
## 1      9 before
## 2      8 before
## 3      1 before
## 4      3 before
## 5      2 before
## 6     16  after
## 7     11  after
## 8     15  after
## 9     12  after
## 10     9  aftertest <- t.test(value ~ time,
  data = dat5,
  alternative = "greater",
  paired = TRUE
)
test## 
##  Paired t-test
## 
## data:  value by time
## t = 4.4721, df = 4, p-value = 0.005528
## alternative hypothesis: true difference in means is greater than 0
## 95 percent confidence interval:
##  4.186437      Inf
## sample estimates:
## mean of the differences 
##                       8

上面的输出概括了执行测试所需的所有信息(将 R 中找到的结果与手工找到的结果进行比较)。

可以照常提取 p 值:

test$p.value## [1] 0.005528247

p-值为 0.006,因此在 5%的显著性水平上,我们拒绝了差异均值等于 0 的零假设,这意味着我们可以得出结论,该治疗在提高跑步能力方面是有效的。这个结果证实了我们手工发现的东西。

绘图和统计检验相结合

写完这篇文章后,我发现了{ggstatsplot}包,我认为这里值得一提,特别是分别用于独立样本和配对样本的ggbetweenstats()ggwithinstats()函数。

这两个函数组合了一个图(代表每个组的分布)和显示在图的副标题中的统计测试结果。

请参见以下场景 2、3 和 5 的示例。不幸的是,这个包不允许运行场景 1 和场景 4 的测试。

场景 2

ggbetweenstats()功能用于独立样本:

# load package
library(ggstatsplot)
library(ggplot2)# plot with statistical results
ggbetweenstats(
  data = dat2bis,
  x = sample,
  y = value,
  plot.type = "box", # for boxplot
  type = "parametric", # for student's t-test
  var.equal = TRUE, # equal variances
  centrality.plotting = FALSE # remove mean
) +
  labs(caption = NULL) # remove caption

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

p-值(显示在p =之后)是使用t.test()函数获得的值的两倍,因为当我们运行t.test()时,我们指定了alternative = "greater"(即单边测试)。在我们使用ggbetweenstats()函数的图中,这是一个默认执行的双边测试,即alternative = "two.sided"

场景 3

我们也有独立的样本,所以我们再次使用ggbetweenstats()函数,但是这一次两个总体的方差并不相等,所以我们指定参数var.equal = FALSE:

# plot with statistical results
ggbetweenstats(
  data = dat3,
  x = sample,
  y = value,
  plot.type = "box", # for boxplot
  type = "parametric", # for student's t-test
  var.equal = FALSE, # unequal variances
  centrality.plotting = FALSE # remove mean
) +
  labs(caption = NULL) # remove caption

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

注意,出于与上述相同的原因,图的副标题中显示的p-值也比使用t.test()功能时大两倍。

场景 5

在这种情况下,样本是成对的,因此我们使用ggwithinstats()函数:

ggwithinstats(
  data = dat5,
  x = time,
  y = value,
  type = "parametric", # for student's t-test
  centrality.plotting = FALSE # remove mean
) +
  labs(caption = NULL) # remove caption

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

同样,图的副标题中的p-值是使用t.test()函数获得的值的两倍,原因同上。

然而,本节的重点是说明如何轻松绘制带有统计结果的图表,这正是{ggstatsplot}包的目的。在这篇文章中可以看到更多的细节和例子。

假设

对于许多统计测试,为了能够解释结果,需要满足一些假设。当其中一个或几个不满足时,尽管技术上可以进行这些测试,但解释结果或相信结论是不正确的。

以下是学生对两个样本进行 t 检验的假设,如何检验它们,以及如果假设不成立,还有哪些其他检验:

  • 变量类型:学生的 t 检验需要混合一个定量因变量(对应于与问题相关的测量)和一个定性自变量(精确的 2 个水平将决定要比较的组)。
  • 独立性:从总人口中随机选择的有代表性的部分收集的数据,在组间和组内应该是独立的。独立性的假设通常基于实验的设计和对实验条件的良好控制来验证,而不是通过正式的测试。如果你仍然不确定基于实验设计的独立性,问问自己一个观察是否与每个组内或组间的另一个观察相关(如果一个观察对另一个有影响)。如果没有,很可能你有独立的样本。如果样本之间的观察值(形成要比较的不同组)是相关的(例如,如果两个测量值是在相同的个体上收集的,这是医学研究中测量指标(I)治疗前和(ii)治疗后时的常见情况),则应首选学生 t 检验的配对版本,称为配对样本的学生 t 检验,以便考虑要比较的两组之间的相关性。
  • 常态:
  • 对于小样本(通常 n 个样本< 30), when the two samples are independent, observations in 和一个样本 都应遵循正态分布 )。当对成对样本使用学生的 t 检验时,两个样本的观察值之差应该遵循正态分布。正态性假设可以通过直方图QQ 图进行直观测试,和/或通过正态性测试进行正式测试,如夏皮罗-维尔克或科尔莫戈罗夫-斯米尔诺夫测试。即使在变换(例如,对数变换、平方根等)之后。),您的数据仍然不符合正态分布,可以应用 Wilcoxon 检验(R 中的wilcox.test(variable1 ~ variable2, data = dat)。这种非参数检验对非正态分布稳健,它比较中位数而不是平均值,以便比较两个总体。
  • 对于大样本(n ≥ 30),不需要数据的正态性**(这是一个常见的误解!).根据中心极限定理,即使数据不是正态分布,大样本的样本均值通常也是正态分布的近似值。因此,当每组/样本中的观察值数量很大时,不需要检验正态性假设。**
  • 方差相等:当两个样本独立时,两组的方差在总体中应该相等(这种假设被称为方差齐性,或者有时甚至被称为同方差,与方差在组间不同时的异方差相对)。这种假设可以通过图形进行测试(例如,通过比较箱线图点线图中的离散度),或者更正式地通过 Levene 测试({car}包中的leveneTest(variable ~ group))或 f 检验(var.test(variable ~ group))。如果等方差假设被拒绝,可以使用另一种版本的学生 t 检验:韦尔奇检验(t.test(variable ~ group, var.equal = FALSE))。请注意,韦尔奇检验不要求方差齐性,但在小样本情况下,分布仍应遵循正态分布。如果你的分布不是正态分布或者方差不相等,应该使用 Wilcoxon 检验。这种检验不需要假设正态性或方差的同方差性。

这就结束了一篇比较长的文章。谢谢你阅读它。我希望这篇文章能帮助你理解学生对两个样本的不同版本的 t-test 是如何工作的,以及如何手动和在 r 中执行它们。如果你感兴趣,这里有一个闪亮的应用程序可以轻松地手动执行这些测试(你只需要输入你的数据,并通过侧边栏菜单选择合适的测试版本)。

此外,我邀请您阅读:

  • 这篇文章如果你想知道如何计算学生的 t 检验但是这次,对于一个样本,
  • 如果您想在非正态假设下比较两组,或
  • 如果您想使用方差分析来比较 3 个或更多组,请点击此

和往常一样,如果您有与本文主题相关的问题或建议,请将其添加为评论,以便其他读者可以从讨论中受益。如果您发现了错误或 bug,可以通过在 GitHub 上提出问题来通知我。对于所有其他要求,你可以与我联系。

相关文章:

参考

朗特里德里克。2000.统计无泪

  1. 请注意,与描述统计学相反,推断统计学是统计学的一个分支,它被定义为从对人口的代表性样本的观察中得出关于人口的结论的科学。参见人群和样本的差异
  2. 在本文的其余部分,当我们写学生的 t 检验时,我们指的是两个样本的情况。如果您只想比较一个样本,请参见一个样本 t 检验↩︎
  3. 这至少是参数假设检验的情况。参数测试意味着它是基于理论统计分布,这取决于一些定义的参数。在学生对两个样本进行 t 检验的情况下,它基于具有单个参数的学生 t 分布,即自由度(df = n1+n2 2,其中 n1 和 N2 是两个样本大小)或正态分布。

原载于 2020 年 2 月 28 日 https://statsandr.com**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值