【问题标题】:What is the proper way to compute 95% confidence intervals with PyTorch for classification and regression?使用 PyTorch 计算 95% 置信区间以进行分类和回归的正确方法是什么?
【发布时间】:2022-01-18 05:45:10
【问题描述】:

我想使用 PyTorch 报告我的数据的 90、95、99 等置信区间。但是置信区间似乎太重要了,不能让我的实现未经测试或受到批评,所以我想要反馈——至少应该由一些专家检查。此外,我已经注意到当我的值为负时我得到了 NaN 值,这让我认为我的代码只适用于分类(至少),但我也做回归。我也很惊讶直接使用 numpy 代码实际上给了我可微的张量......不是我所期待的。

那么这样对吗?:

import numpy as np
import scipy
import torch
from torch import Tensor

P_CI = {0.90: 1.64,
        0.95: 1.96,
        0.98: 2.33,
        0.99: 2.58,
        }


def mean_confidence_interval_rfs(data, confidence=0.95):
    """
    https://stackoverflow.com/a/15034143/1601580
    """
    a = 1.0 * np.array(data)
    n = len(a)
    m, se = np.mean(a), scipy.stats.sem(a)
    h = se * scipy.stats.t.ppf((1 + confidence) / 2., n - 1)
    return m, h


def mean_confidence_interval(data, confidence=0.95):
    a = 1.0 * np.array(data)
    n = len(a)
    m, se = np.mean(a), scipy.stats.sem(a)
    h = se * scipy.stats.t.ppf((1 + confidence) / 2., n - 1)
    return m, m - h, m + h


def ci(a, p=0.95):
    import numpy as np, scipy.stats as st
    st.t.interval(p, len(a) - 1, loc=np.mean(a), scale=st.sem(a))


# def ci(a, p=0.95):
#     import statsmodels.stats.api as sms
#
#     sms.DescrStatsW(a).tconfint_mean()

def compute_confidence_interval_classification(data: Tensor,
                                               by_pass_30_data_points: bool = False,
                                               p_confidence: float = 0.95
                                               ) -> Tensor:
    """
    Computes CI interval
        [B] -> [1]
    According to [1] CI the confidence interval for classification error can be calculated as follows:
        error +/- const * sqrt( (error * (1 - error)) / n)

    The values for const are provided from statistics, and common values used are:
        1.64 (90%)
        1.96 (95%)
        2.33 (98%)
        2.58 (99%)
    Assumptions:
    Use of these confidence intervals makes some assumptions that you need to ensure you can meet. They are:

    Observations in the validation data set were drawn from the domain independently (e.g. they are independent and
    identically distributed).
    At least 30 observations were used to evaluate the model.
    This is based on some statistics of sampling theory that takes calculating the error of a classifier as a binomial
    distribution, that we have sufficient observations to approximate a normal distribution for the binomial
    distribution, and that via the central limit theorem that the more observations we classify, the closer we will get
    to the true, but unknown, model skill.

    Ref:
        - computed according to: https://machinelearningmastery.com/report-classifier-performance-confidence-intervals/

    todo:
        - how does it change for other types of losses
    """
    B: int = data.size(0)
    # assert data >= 0
    assert B >= 30 and (not by_pass_30_data_points), f' Not enough data for CI calc to be valid and approximate a' \
                                                     f'normal, you have: {B=} but needed 30.'
    const: float = P_CI[p_confidence]
    error: Tensor = data.mean()
    val = torch.sqrt((error * (1 - error)) / B)
    print(val)
    ci_interval: float = const * val
    return ci_interval


def compute_confidence_interval_regression():
    """
    todo
    :return:
    """
    raise NotImplementedError


# - tests

def ci_test():
    x: Tensor = abs(torch.randn(35))
    ci_pytorch = compute_confidence_interval_classification(x)
    ci_rfs = mean_confidence_interval(x)
    print(f'{x.var()=}')
    print(f'{ci_pytorch=}')
    print(f'{ci_rfs=}')

    x: Tensor = abs(torch.randn(35, requires_grad=True))
    ci_pytorch = compute_confidence_interval_classification(x)
    ci_rfs = mean_confidence_interval(x)
    print(f'{x.var()=}')
    print(f'{ci_pytorch=}')
    print(f'{ci_rfs=}')

    x: Tensor = torch.randn(35) - 10
    ci_pytorch = compute_confidence_interval_classification(x)
    ci_rfs = mean_confidence_interval(x)
    print(f'{x.var()=}')
    print(f'{ci_pytorch=}')
    print(f'{ci_rfs=}')


if __name__ == '__main__':
    ci_test()
    print('Done, success! \a')

输出:

tensor(0.0758)
x.var()=tensor(0.3983)
ci_pytorch=tensor(0.1486)
ci_rfs=(tensor(0.8259), tensor(0.5654), tensor(1.0864))
tensor(0.0796, grad_fn=<SqrtBackward>)
x.var()=tensor(0.4391, grad_fn=<VarBackward>)
ci_pytorch=tensor(0.1559, grad_fn=<MulBackward0>)
Traceback (most recent call last):
  File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/pydevd.py", line 1483, in _exec
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/Users/brandomiranda/ultimate-utils/ultimate-utils-proj-src/uutils/torch_uu/metrics/metrics.py", line 154, in <module>
    ci_test()
  File "/Users/brandomiranda/ultimate-utils/ultimate-utils-proj-src/uutils/torch_uu/metrics/metrics.py", line 144, in ci_test
    ci_pytorch = compute_confidence_interval_classification(x, by_pass_30_data_points)

如何修复上面的代码以进行回归,例如任意大小的负值?

考虑到 CI 应该是多么重要……也许是深度学习的坏习惯,目前还没有实现,尤其是官方 PyTorch 还没有实现,这有点令人惊讶?不幸的是,很少在论文中看到它。


参考资料:

【问题讨论】:

  • pytorch 论坛中的相同问题:discuss.pytorch.org/t/…
  • 您可以考虑在datascience.stackexchange.com 上提问。
  • @TimRoberts 可能不允许交叉发布...那么您有什么建议?我觉得 SO 总的来说也很强大 - 特别是对于实现/编码 - 这就是我在这里选择它的原因,但它有点武断......必须在某个地方发布!。
  • 无论您是否将其留在这里,我都认为您会在 Data Science Stack Exchange 上得到更集中的响应。

标签: python machine-learning pytorch statistics pytorch-lightning


【解决方案1】:

tldr;

置信区间 (ci) 计算:

  • 真实均值在给定区间内的概率(通常写成mu_n +- ci

假设:

  • 传统的置信区间陈述仅适用于关于我们想要估计为均值的值(参数、随机数量等)的陈述
  • 您有足够的样本以使分析成立(例如,平均值 $mu_n = 1/n sum_i x_i$,建议使用n&gt;=30

如果这些假设成立(**即您通过具有 +- 值的样本均值估算真实均值**),则使用我提供的名为 torch_compute_confidence_interval 的下面代码进行回归、分类以及您想要的任何操作。


首先,asfaik 置信区间 (ci) 是深度学习 (DL) 中的一个开放研究问题 - 因此可能存在更复杂的答案。但我将提供一个我计划使用的实用答案(并在 DL 中报告结果时看到其他人使用)。

要计算置信区间,我们必须先了解一点 ci。它们是对随机调查/数据集样本的概率陈述,表明您尝试报告的平均值在报告的区间内。所以当人们说:

mean_error +- CI for p=95%

这意味着,如果您对 95 个数据集进行采样,您会期望真正的平均值在 95 次的时间间隔内(但您不知道是哪一个,因此您无法说出您计算出的任何特定时间间隔平均值会在那里)。

这意味着您只能将其用于报告手段。这是因为它背后的数学(这不是很难)通过利用我们可以分析地计算样本均值的概率来近似计算边界成立(或置信区间成立)的概率,因为近似 a根据中心极限定理 CLT 正常。因此,计算的特定 CI 假定您要计算的数量是样本均值,并使用此正态近似值计算您的 +- 数。因此,通常建议为您正在使用的特定数据集使用 n&gt;=30 数据点,但事情仍然可以很好地解决,因为 ci 可以使用分布而不是正态分布(在统计软件中表示为 z)。

鉴于这些假设,您可以简单地执行以下操作:

def torch_compute_confidence_interval(data: Tensor,
                                           confidence: float = 0.95
                                           ) -> Tensor:
    """
    Computes the confidence interval for a given survey of a data set.
    """
    n = len(data)
    mean: Tensor = data.mean()
    # se: Tensor = scipy.stats.sem(data)  # compute standard error
    # se, mean: Tensor = torch.std_mean(data, unbiased=True)  # compute standard error
    se: Tensor = data.std(unbiased=True) / (n**0.5)
    t_p: float = float(scipy.stats.t.ppf((1 + confidence) / 2., n - 1))
    ci = t_p * se
    return mean, ci

我已经对其进行了测试,并将其与专门用于分类的事物进行了比较,它们的值一致,最高可达 1e-2,因此代码可以正常工作。输出:

Connected to pydev debugger (build 213.5744.248)
x_bernoulli.std()=tensor(0.5040)
ci_95=0.1881992999915952
ci_95_cls=tensor(0.1850)
ci_95_anything=tensor(0.1882)
x_bernoulli.std()=tensor(0.5085, grad_fn=<StdBackward>)
ci_95_torch=tensor(0.1867, grad_fn=<MulBackward0>)
x.std()=tensor(0.9263)
ci_95=0.3458867459004733
ci_95_torch=tensor(0.3459)
x.std()=tensor(1.0181, grad_fn=<StdBackward>)
ci_95_torch=tensor(0.3802, grad_fn=<MulBackward0>)

有关更多详细信息,请参阅我的 Ultimate-utils 库,我在其中评论了文档中的数学:https://github.com/brando90/ultimate-utils/blob/e81a8c3c4425b33e00b3ade172705f20b626b2b1/ultimate-utils-proj-src/uutils/torch_uu/metrics/confidence_intervals.py#L1


对深度学习的评论

如果您报告特定型号的错误,例如神经网络,像这样,您或多或少地报告说,该非常特定的神经网络和权重的真正平均误差位于这些范围内。但正如我所说,这是一个开放的研究领域,所以必须有更好的东西可用,例如考虑一些层实际上是随机的,等等。

【讨论】:

    猜你喜欢
    • 2016-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-23
    • 1970-01-01
    • 1970-01-01
    • 2016-10-30
    • 1970-01-01
    相关资源
    最近更新 更多