回归分析导论
本文介绍了回归分析的基础,使用加州住房数据集作为一个说明性的例子
机器学习任务可以分为以下四类:
本文着重于回归分析。具体来说,本文描述了这项任务的基础,并在加州住房数据集上阐释了其主要概念。
这篇文章的结构如下:
- 什么是回归?
- 回归和分类的区别
- 回归的类型:由于回归模型数量众多,我们介绍最常见的几种。
- 如何选择正确的型号
- 线性模型:在所有可用的回归模型中,本文重点介绍线性模型的理论和假设。
- 线性模型示例:用线性回归模型分析加州住房数据集。
- 其他回归分析示例
1.什么是回归?
回归分析在维基百科中的定义是:
在统计建模中,回归分析是一组统计过程,用于估计一个因变量(通常称为“结果变量”)与一个或多个自变量(通常称为“预测值”、“协变量”或“特征”)之间的关系。
您经常听到的与回归分析相关的术语是:
- 因变量或**目标变量:**要预测的变量。
- 自变量或**预测变量:**估计因变量的变量。
- **异常值:**与其他观察值显著不同的观察值。应该避免,因为它可能会妨碍结果。
- 多重共线性 : 两个或两个以上自变量高度线性相关的情况。
- 同质性 或**方差同质性:**误差项在自变量的所有值上都相同的情况。
回归分析主要用于两个不同的目的。一是广泛用于预测和预测,与机器学习领域重叠。其次,它还用于推断自变量和因变量之间的因果关系。
2.回归和分类的区别
回归和分类都是有监督的学习方法,也就是说它们使用带标签的训练数据来训练自己的模型,进行预测。因此,在机器学习中,这两项任务通常被归为同一组。
它们之间的主要区别是输出变量。在回归中,输出是数字或连续的,而在分类中,输出是分类的或离散的。这定义了分类和回归评估预测的方式:
- 分类预测可以使用准确性进行评估,而回归预测则不能。
- 回归预测可以使用均方根误差进行评估,而分类预测则不能。
下面的环节在训练分类和回归模型的时候都收集了一些损失函数。
分类和回归算法之间有一些重叠;例如:
- 分类算法可以预测连续值,但是该连续值是类别标签的概率形式。
- 回归算法可以预测离散值,但是该离散值是整数形式的。
有些算法只需稍加修改即可用于分类和回归,如决策树和人工神经网络。对于这两种问题类型,其他一些算法更难以实现,例如用于回归预测建模的线性回归和用于分类预测建模的逻辑回归[ 1 ]。
3.回归的类型
数据科学和机器学习中使用了各种类型的回归。每种类型在不同的情况下都有自己的重要性,但核心是,所有的回归方法都分析自变量对因变量的影响。这里我们提到一些重要的回归类型:
详细解释每一个都需要几篇文章,因此,如果读者对回归变量的进一步信息感兴趣,我推荐阅读[ 2 、 3 、 4 ]。
4.如何选择正确的回归模型?
在我看来,这是最困难的任务,不仅在回归方面,而且在一般的机器学习方面。
尽管我认为经验可能是这个问题的正确答案,一些建议是:
- 线性模型是最常见和最简单的使用方法。如果你有一个连续的因变量,线性回归可能是你应该考虑的第一种类型。
- 如果因变量是连续的,并且您的模型具有共线性或许多自变量,您可以尝试,例如,岭或套索模型。您可以根据 R 方或 RMSE 选择最终型号。
- 如果您正在处理分类数据,可以尝试泊松、拟泊松和负二项式回归。
- 为了避免过度拟合,我们可以使用交叉验证方法来评估模型。脊、套索和弹性网回归技术可用于纠正过度拟合问题。
- 当你有一个非线性模型时,尝试支持向量回归。
5.线性模型
回归分析中最常见的模型是线性回归。这个模型通过拟合一个线性方程找到自变量和因变量之间的关系。拟合该回归线最常用的方法是使用最小二乘法,它计算出最佳拟合线,使每个数据点到该线的垂直偏差平方和最小。
建立线性回归模型只是工作的一半。为了在实践中实际可用,模型应符合线性回归的假设[ 7 , 8 ]:
- 参数呈线性。
- 样本在总体上具有代表性。
- 独立变量的测量没有误差。
- 观察值的数量必须大于自变量的数量。
- 独立变量中没有多重共线性
- 残差的平均值为零。
- 残差正态性
- 独立变量和残差是不相关的
- 残差的同方差性(残差的方差在整个观测中是常数)
- 残差无自相关(特别适用于时间序列数据)。数学上,误差的方差-协方差矩阵是对角线。
很难满足所有这些假设,因此从业者开发了各种各样的方法来在现实世界中保持一些或所有这些理想的属性。下面的文章[ 9 , 10 解释一些例子。
6.线性模型示例
为了展示之前介绍的一些概念,我们在加州住房数据集上实现了一个线性回归模型。下面是代码以及对每个块的简要说明。
首先,我们导入所需的库。
接下来,我们从scikit-learn
库中加载住房数据:
dict_keys(['data', 'target', 'feature_names', 'DESCR'])
为了了解更多的特性,我们打印了california_housing_dataset.DESCR
:
- MedInc median income in block
- HouseAge median house age in block
- AveRooms average number of rooms
- AveBedrms average number of bedrooms
- Population block population
- AveOccup average house occupancy
- Latitude house block latitude
- Longitude house block longitude
这是八个独立变量,基于它们我们可以预测房子的价值。房子的价格由变量AveHouseVal
表示,它定义了我们的因变量。
我们现在使用pd.DataFrame
将数据加载到 pandas 数据帧中。
数据预处理
加载数据后,最好查看数据中是否有任何缺失值。我们使用isnull()
计算每个要素缺失值的数量(本数据集没有缺失值)。
探索性数据分析
让我们首先根据Latitude
和Longitude
绘制目标变量AveHouseVal
的分布。该图像应该是美国加利福尼亚州的绘图。据观察,靠近海边的房子比其他地方要贵。
接下来,我们创建一个相关矩阵来度量变量之间的线性关系。
观察结果:
- 为了拟合线性回归模型,我们选择那些与我们的因变量
AveHouseVal
高度相关的特征。通过查看相关矩阵,我们可以看到MediaInc
与AverageHouseVal
(0.69)有很强的正相关性。另外两个相关性最高的变量是HouseAve
和AveRooms
。 - 为线性回归模型选择要素时,重要的一点是检查多重共线性。例如,特征
Latitude
和Longitude
具有 0.92 的相关性,所以我们不应该在我们的回归模型中同时包括它们。由于变量MediaInc
、HouseAve
和AveRooms
之间的相关性不高,我们考虑将这三个变量用于我们的回归模型。
训练和测试模型
我们使用 scikit-learn 的LinearRegression
在训练集和测试集上训练我们的模型。
残差的分布:
让我们打印残差的分布,以验证线性模型的假设。
假设的验证:
- 线性输入参数(正常)
- 样本代表总体人口(假设)
- 自变量被无误差地测量(假设)
- 观察值的数量必须大于独立变量的数量(好的)
- 独立变量内无多重共线性(适用于研究变量)
- 残差的平均值为零(好的)
- 残差的正态性(否
- 独立变量和残差不相关(未检查)
- 残差的同方差性(残差的方差在整个观测中是常数)(否)
- 残差无自相关(特别适用于时间序列数据)。数学上,误差的方差-协方差矩阵是对角线 ( 未检查)
在研究这个数据集时,线性回归模型不是最好的模型,所以我们将在以后的文章中探讨其他模型。
作为提示,这里有一个关于如何继续的好的链接:
残差和异方差的非正态性的一个大问题是,模型中的误差量在观测数据的整个范围内并不一致。这意味着他们的预测能力在整个因变量范围内是不一样的。转换因变量有助于纠正这一点,但会给解释带来困难。如果平方根变换没有完全归一化您的数据,您还可以尝试逆变换。转换的强度趋向于从 1。对数,2。平方根,3。反向(1/x)。
7.其他回归分析示例
如果读者感兴趣,我建议尝试以下数据集:
关于波士顿住房数据集,我推荐阅读这篇其他文章。
此外,在这个网络链接中,有一些主题相关的数据集的集合,这些数据集适用于不同类型的回归分析。
最后,许多数据集可以在以下位置找到:
- https://www.kaggle.com/datasets
- https://toolbox.google.com/datasetsearch
- https://archive.ics.uci.edu/ml/datasets.html
感谢您的阅读!!
如果你喜欢这个帖子,请考虑 订阅 。你将获得我所有的内容+所有其他来自牛逼创作者的文章!
参考
[1]机器学习精通,机器学习中分类和回归的区别
[2] Analytics Vidhya,你应该知道的 7 个回归技巧!
[3] ListenData,数据科学中的 15 种回归类型
[4]Java point,机器学习中的回归分析
[5] ListenData,如何选择正确的回归模型?
[6]吉姆统计,选择回归分析的正确类型
[7] R 统计,线性回归的假设
[8]科罗拉多大学,线性模型假设和诊断
[9]中等,线性回归及其假设
[10]中等,如何解决你的下一个回归问题
回归分析简介[使用 Excel]
简单和多元线性回归介绍。使用 Excel 的数据分析工具库实现和构建预测模型。
不熟悉 Python 或者 R 之类的工具?
完全没问题!您仍然可以使用 Excel 执行回归分析。而且你不需要有任何编程知识来做到这一点。
在数据分析方面,Excel 无疑是一个非常强大的工具。您可以进行数据清理,使用数据透视表进行分析,设计视觉效果等等。
如果你想了解更多关于使用 Excel 进行数据分析的知识,这里有一些很好的资源可以参考。
数据清理:清理 Excel 电子表格数据的 10 种超级简洁的方法
数据分析:【Excel 数据分析介绍
相当惊人吧!
但是等等。还有呢!
Excel 有一个名为“数据分析工具库”的加载项,允许您执行各种统计操作,包括只需单击一个按钮的回归。
既然我们已经谈了足够多的 Excel,让我们继续回归。
什么是回归?
我是简单的术语;回归是确定因变量和一组自变量之间关系的过程。
考虑下面的例子:
下表显示了电视广告费用与相应销售收入之间的关系。
如果你需要找到两个变量之间的关系,最简单的方法就是画一个散点图,就像这样。
观察这个情节,我们可以说花在电视上的广告和销售收入之间存在正相关关系。
这是正相关的迹象。
现在,假设我们需要量化这种关系。你会怎么做?
同样,这很简单。只要画一条最符合散点图中显示的所有点的线,线方程就会给你变量之间的关系。
Sales = 0.0555*(TV) + 6.9748
这是回归的基本思想。它是通过用一个函数拟合所有的点来量化变量之间的关系。
用于确定最佳拟合线的方法超出了本文的范围。但是如果你感兴趣,这是通过一种叫做普通最小二乘法的技术完成的。
我们刚刚看到的例子叫做简单线性回归,它只涉及一个独立变量或特征(电视广告成本)。
但同样的回归概念可以扩展到多个自变量或特征,称为多元线性回归。
例如,看看这张表。
这里,我们有花费在电视、广播和报纸上的广告费用以及相应的销售收入。
我们不能在 2D 平面上显示这种关系,但是线性回归的概念仍然可以用来确定所有这些点的最佳拟合函数。
并且该函数将具有以下形式:
Sales = 0.0544*(TV) + 0.1070*(Radio) + 0.0003*(Newspaper) + 4.6251
这就是回归背后的基本思想。
现在让我们看看如何使用 Excel 实现回归。
第一步是添加数据分析工具库
添加数据分析工具库
默认情况下,数据分析工具库不可用,您需要单独激活外接程序。
要激活,进入文件- >选项- >插件,然后激活分析工具箱。一旦你激活了插件,它应该出现在工具栏的数据标签下。
一旦您添加了数据分析工具库并下载了 Excel 文件中的数据,请进入下一步。
执行回归
要执行回归,请转到数据分析工具,并从可用选项中选择回归。选择回归后,应该会出现以下窗口。
在输入 Y 范围中输入因变量范围,在输入 X 范围中输入自变量范围。然后按,确定。
您已经成功地完成了使用 Excel 执行回归!
现在让我们看看如何解释结果。
解释结果
一旦您执行了回归,您的结果页面应该看起来像这样。
如果你看一下第三个表,有第一列名为系数。该列给出多元线性回归方程中每个变量的系数值。
Sales = 0.0544*(TV) + 0.1070*(Radio) + 0.0003*(Newspaper) + 4.6251
表中计算的其他度量用于比较性能、统计显著性和其他因素。这些度量需要对统计学有很好的理解,因此我在这篇介绍性文章中没有讨论所有这些。
一旦你执行了回归,你可以做两件事。
- 分析变量之间的关系
- 建立预测模型
既然我们已经讨论了分析变量之间的关系,那么让我们来看看如何建立一个预测模型。
构建预测模型
预测模型只不过是通过提供自变量的值来预测因变量的值的过程。
同样,我们的多元线性回归方程是预测模型函数,如果我们输入自变量的值,我们就可以获得销售额的预测值。
例如,如果您想预测以下广告支出组合的销售收入,
电视= 100
无线电= 200
报纸= 500
将这些值输入多元线性回归方程。这将给你 31.6377 的销售收入,这是预测的收入。
结论
我希望你已经很好地理解了什么是回归,使用 Excel 实现,分析关系和建立预测模型。
当然,这只是回归的介绍,一旦你熟悉了本文中的基础知识,你还可以探索许多其他的概念。
你可以在这里下载包含本文中的数据和步骤的 excel 文件。
初学者机器学习中的回归技术介绍。
学习和实现机器学习的基础知识
由 Unsplash 上的 Roozbeh Eslami 拍摄的照片
当谈到机器学习时,应用程序是巨大的,但允许开发人员在一系列模型之间进行选择的各种方法和技术也是巨大的。然而,如果没有对各种可用的回归模型的全面理解,机器学习的基础是不完整的。本文的目标是为初学者和有抱负的爱好者提供在该领域探索和进步所需的技能和框架。欢迎来到未来的开端。
线性回归:cout < <《你好世界》;的数据
那么回归到底是什么?回归是一种统计工具,有助于识别和实现给定数据集的因变量和自变量之间的关系。然而,当谈到具体的线性回归时,我们考虑一个自变量和一个因变量之间的线性关系。我们将使用相对简单的数据集来理解这些概念。
y:因变量
x:独立变量
答:拦截
b:X 的常数
现在,上述方程是线性回归的数学表示,对于给定的一组数据,可以形成各种这样的回归线。然而,最佳拟合回归是最适合给定数据集的回归,并产生最佳的可能预测。
我们的数据集是什么?
我们的数据集构成了任何给定模型的预测基础,并且必须为我们的预测提供准确的反映。对于任何可用于实现机器学习模型的数据集,必须对其进行清理和排序。本文将描述各种这样的数据处理方法。
上面提供的数据集显示了提供个人 SAT 分数和 GPA 的数据,从而为我们提供了线性回归应用的理想场景。快照仅包含 10 个数据点,然而,实际数据集包含 84 个数据点。
让我们开始编码
导入库
我们将使用 python 作为我们的主要语言进行编码,要实现这些模型,我们的第一步应该是导入所有必需的库,在这种情况下。
import numpy as npimport pandas as pdimport matplotlib.pyplot as plt
NumPy :增加了对大型多维数组和矩阵的支持。
pandas :它提供数据结构和操作,用于操纵数值表和时间序列。
matplotlib :是一个用于图形化表示的库。
导入数据集
dataset = pd.read_csv('data.csv')x = dataset.iloc[:,:-1].valuesy = dataset.iloc[:,-1].values
接下来,我们使用“pd.read_csv”导入给定的数据集,并将其存储在一个变量中。使变量尽可能不言自明是有益的。
最后,我们必须将自变量分配给变量 x,将因变量分配给变量 y,我们通过使用。iloc”方法为实现选择相应的列。
分割数据集
将数据集分成两部分——训练集和测试集。训练集用于训练您的模型,并使其从数据集“学习”,而测试数据集将允许我们测试我们训练的模型并检查其准确性。
sklearn 库用于各种数据处理功能。我们用这个库把我们的因变量(x)和自变量(y)分成各自的测试集和训练集。
from sklearn.model_selection import train_test_splitx_train, x_test, y_train, y_test = train_test_split(x,y , test_size = 0.2, random_state=0)
神奇发生在哪里:训练你的第一个机器学习模型
接下来,我们再次使用我们的 sklearn 库,通过数据集的训练集分区来训练我们的模型。我们以回归变量的名称创建一个对象,并用它来调用线性回归。
from sklearn.linear_model import LinearRegressionregressor = LinearRegression()regressor.fit(x_train,y_train)
我们的预测是什么?
y_pred = regressor.predict(x_test)
print(y_pred)
我们简单地使用上面的命令来为我们训练的“x_train”模型预测“x_test”。
长什么样?
现在,我们必须可视化我们的结果,以了解我们的回归线如何匹配我们的数据点。我们使用 Matplotlib 来做这件事。
plt.scatter(x_train,y_train, color = 'red')plt.plot(x_train,regressor.predict(x_train) , color='blue')plt.title('salary vs experience(Training set')plt.xlabel('years of experience')plt.ylabel('saslary')plt.show()
多变量回归
我们随后的回归模型将处理多个变量,这是机器学习中的一个重要里程碑,因为各种结果取决于不止一个独立变量。多变量回归是通向大量此类应用的大门。
让我们考虑一个将用于所有后续模型的数据集。
与之前的数据集相比,我们当前的数据集看起来要复杂得多。这是因为有大量的列作为影响我们结果的多变量因素。
from sklearn.linear_model import LinearRegressionregressor = LinearRegression()regressor.fit(X_train, y_train)
我们将使用 sklearn 导入我们的模型,并在我们的 X_train 和 y_train 数据分区上实现它。
预测我们的结果
这一次,我们将预测模型的实际结果,我们将通过实现以下代码行来做到这一点。
y_pred = regressor.predict(X_test)np.set_printoptions(precision=2)print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
我们使用 regressor 对象调用 X_test 分区上的 predict 方法,然后使用后续代码行同时打印 y_pred 和 y_test。这有助于我们比较预测值和期望值。
最后的话
上面的文章是对数据世界的简要介绍,它基于使用数据作为一个强大的工具来进行准确预测的想法。我将写各种其他的回归和分类模型,以及它们在这个基础上的应用。敬请关注。
};
强化学习中的后悔介绍
有时候后悔是改善的好方法
更新:学习和练习强化学习的最好方式是去http://rl-lab.com
“最终,我们只后悔没有抓住机会”
刘易斯·卡罗尔
介绍
几乎可以肯定的是,每个人一生中都有后悔的事情(实际上是很多事情)。后悔没有在价格还能承受的时候买票,后悔没有做出职业决定,后悔个人或社会的举动,等等……当然,后悔是有苦味的,尽管它可能具有教育意义,但现实是机会往往会失去,并且没有回头路。
但在训练机器或算法时,情况可能就不一样了。
遗憾
你最后悔的行动是本应该(更有可能)使用或采取的行动。所以采取这个行动的概率和你后悔没采取行动的程度成正比。
从数学上来说,后悔被表达为一个可能的行动的回报(奖励或回报)和已经实际采取的行动的回报之间的差异。如果我们将收益函数表示为 u ,则公式变为:
后悔= u(可能的行动)- u(采取的行动)
显然,我们感兴趣的是’‘可能行动的收益胜过’ 采取行动 '的收益的情况,所以我们考虑积极的遗憾,忽略零和消极的遗憾。
如前所述,使用一个行为而不是实际使用的行为的概率与它产生的遗憾成正比。
例如,如果我们采取行动 a1,得到 u(a1) = 1,那么我们计算出 u(a2)= 2,u(a3) = 4,u(a4) = 7。相应的后悔将是后悔(a2) = u(a2) - u(a1) = 1,后悔(a3) = 3,后悔(a4) = 6。
总遗憾是遗憾(a1) +遗憾(a2) +遗憾(a3) +遗憾(a4) = 0 + 1 + 3 + 6 = 10。
很容易看出最后悔的动作是 a4。为了在数字上反映这一点,我们更新了我们的策略,表示为σ,例如σ(a2) = 1/10 = .1,σ(a3) = 3/10 = .3,σ(a4) = 6/10 = .6。
显然,你可能会问,为什么不显式地给动作 a4 一个概率 1 (σ(a4) = 1)?仅仅是因为当面对另一个演员时,比如在游戏中,后悔的概念被使用。在游戏中以确定性的方式进行游戏会给你的对手一个反击你的策略并赢得胜利的机会。
石头剪刀布示例
考虑一个石头剪刀布 (RPS)的游戏,其点数系统如下:
- 损失-1 分
- 抽签(两个相同类型的项目)的结果是 0 分
- 赢给获胜者 1 分
石头剪刀布游戏的支付格
下表给出了玩这个游戏的不同组合,以及结果和如何改进策略。
表格的第一部分(实际游戏)显示了你与对手的比赛,以及每集的“Y 我们的结果”。“迭代”列是针对 ex: R 对 R,或 S 对 P 等的相同组合发生的发作次数
第二部分(你的其他游戏场景)包含假设对手以同样的方式玩游戏,你为了提高(或不提高)你的结果可以玩的场景。它也显示了没有玩一个给定动作的遗憾。
积云遗憾列包含累计遗憾,累计遗憾是遗憾的总和。
对累积遗憾的需求源于这样一个事实,即独立计算遗憾,并不能捕捉到在其他游戏或剧集中发生的事情。这意味着算法没有从它的经验中学习。
作为一个人,你在记忆中保留你以前玩过的游戏,以及如何利用这些过去的经验。但是为了让一个算法做同样的事情,应该有一个考虑到以前发生过的事情的计算。
第三部分(策略调整),计算每个动作(石头、布、剪刀)的概率,以最大化你的结果,总是假设对手以同样的方式玩。
这些概率被计算为 (对行动的累积后悔)/总后悔 。其中总遗憾是同一行的正累积遗憾的总和。如果总遗憾为零,我们为每个行动分配相等的概率(检查第二行)。
上表第一行,你打了 R,对手打了 R,所以结果是平局(0)。如果你出了 P 而对手出了 R 那就更好了,所以你的后悔是 1 没出 P
策略调整说明你不后悔石头,也不后悔剪刀但是你后悔没用纸。
第二排,你用 R 对 S 获胜,策略调整部分显示 2 局后,你没有遗憾。
随着情节的继续,我们看到策略的变化达到了平衡,每个动作应该用 1/3。
PS。这是 RPS 中的最佳策略,因为它使所有 3 个动作的概率相等,因此移动是对手不可预测的。
极端情况
现在,如果一个场景比其他场景播放得多会发生什么?
例如,在下表中,S 对 S 这一集发生了 1000 次,结果是 1000 次平局。这导致在这些情况下后悔 1000,并且策略转向 100%的时间使用石头。
在下面的例子中,P 对 S 发生了 1000 次,导致 1000 次损失,2000 次后悔没有用石头,1000 次后悔没有用剪刀。
因此,该策略被调整为 67%的时间使用石头,33%的时间使用剪刀。
但是,下面的例子中有一个陷阱,R vs S 出现 1000 次,结果是千胜万败,无怨无悔。因为没有遗憾,所以算法不更新策略。
密码
下面是 Google Colab 书籍的链接,其中包含一个简单的后悔算法的代码。
重要提示:为了运行或编辑代码,你需要复制一本书。
colab.research.google.com](https://colab.research.google.com/drive/1FB57Jfi1llITSL6DUyUr_9P7lsXmOP-8#scrollTo=_9p-AnRTy8rI)
自娱自乐后悔
到目前为止,我们假设对手一直以同样的方式玩,使用同样的策略。然而,这不可能是真的!任何对手最终都会发现你策略中的任何偏差,并加以利用。
所以训练不能针对固定的策略。为了补救这种情况,我们使用自我游戏。Self Play 不是训练一个演员对抗一个固定的策略,而是训练所有演员互相对抗。这是通过“复制”第一个演员所做的序列,并将其应用于其他演员来完成的。因此,每个演员现在都维护自己的数据结构,其中包含自己的策略、遗憾等。在每一集之后,每个演员都从自己的角度计算结果,以及可以(可能)做些什么来改善结果。
密码
下面的链接指向一个 Google Colab 书籍,它实现了一个简单的后悔自我游戏。
重要提示:为了运行或编辑代码,你需要复制一本书。
colab.research.google.com](https://colab.research.google.com/drive/1FB57Jfi1llITSL6DUyUr_9P7lsXmOP-8#scrollTo=-ej-kJVb8j0y)
有趣的是,我们注意到,无论两个参与者从什么策略开始,他们都收敛到 RPS 游戏的最佳策略,即每个动作的概率都是 1/3。这确保了所有行动的可能性相等,并防止任何可能被对手利用的偏差。
结论
本文介绍了简单直观的后悔技巧。它允许玩家通过跟踪过去游戏的遗憾来达到平衡游戏,使未来游戏与积极的遗憾成比例。这项技术是更精细的技术的基础,比如反事实后悔最小化(CFR)和深度 CFR。*
强化学习简介
经典强化学习算法的高级结构概述
图片来自https://unsplash.com/photos/iar-afB0QQw
强化学习(RL)是机器学习的一个不断发展的子集,也是人工智能的一个最重要的前沿领域,因为它在过去几年里随着机器人、游戏和其他许多领域的大量成功应用而获得了极大的普及。它表示一组处理顺序决策的算法,并且能够根据本地环境做出智能决策。
RL 算法可以被描述为一种模型,该模型向代理指示它应该在封闭环境中采取哪组动作,以便最大化预定义的整体回报。一般来说,代理人尝试不同的行动,评估获得的总回报。经过多次尝试后,该算法会学习哪些行为会带来更大的回报,并建立一种行为模式。由于这一点,它能够告诉代理在每种情况下采取哪些行动。
RL 的目标是捕捉更复杂的结构,并使用比经典机器学习更具适应性的算法,事实上,与经典机器学习算法相比,RL 算法在行为上更加动态。
应用程序
让我们看看一些基于 RL 的应用示例:
- 机器人 - RL 可用于高维控制问题,也可用于各种工业应用。
- 文本挖掘 - RL 和一个文本生成模型,可以用来开发一个系统,这个系统能够生成长文本的高度可读的摘要。
- 交易执行 -金融行业的主要公司使用 RL 算法来改进他们的交易策略。
- 医疗保健 - RL 可用于药物剂量,以及优化慢性临床试验患者的治疗等。
- 游戏——RL 因作为用于解决不同游戏和实现超人性能的主要算法而闻名。
演员
RL 算法基于马尔可夫决策过程(MDP)。马尔可夫决策过程是一种特殊的随机时间控制决策过程。RL 算法的主要参与者是:
- 代理:在环境中执行动作以优化长期回报的实体;
- 环境:代理人做决策的场景;
- 状态集 ( S ):环境所有可能状态的集合,状态描述环境的当前状况;
- 动作集合 ( A ):代理可以执行的所有可能动作 A 的集合;
- 状态转移模型 P(s0 | s,a) :描述对于每个状态 s 、s0和动作 a ,当 agent 在状态 s 执行动作 a 时,环境状态在s0发生变化的概率;
- 奖励(r = R(s,a)) :指示在状态 s 采取行动 a 的即时实际价值奖励的函数;
- 插曲(rollout) :从 0 到最终值 L 变化的 t 的一系列状态 st 和动作 at(称为视界,最终可以是无限的);代理在其环境的给定状态下启动;在每个时间步 t 处,代理观察当前状态 s_t ∈ S 并因此采取行动A _ t∈A;根据状态转换模型,状态演变成新的状态 s_(t+1) ,其仅取决于状态 s_t 和动作a _ t;代理获得奖励r _ t;然后代理观察到新的状态S(t+1)∈S,循环重新开始;
作者图片
- 策略功能:策略可以是确定性的( π (s) )或随机的( (a|s) ):确定性策略π (s)表示当环境处于状态 s ( a = π (s) )时代理执行的动作 a;随机策略 π (a|s) 是描述当环境处于状态 s 时代理执行动作 a 的概率的函数。一旦指定了策略,新的状态只依赖于策略和状态事务模型;
- Return G_t :根据当前时间步和后续每个时间步的即时奖励,以及折扣因子γ < 1,每集结束时获得的带折扣的总长期奖励:
- 价值函数 V(s) :从状态 s 开始,在当前时间步长 t 的一集结束时的预期长期回报:
- Q-Value 或 Action-Value function Q(s,a) :从当前时间步的状态 s 开始,执行动作 a 的一集结束时的预期长期收益;
- 贝尔曼方程:大部分 RL 算法中的理论核心;根据它,当前价值函数等于当前报酬加上在下一步评估的自身,并折现γ(我们回忆一下,在方程中 P 是模型转换模型):
最优策略
随着策略的改变,动作值函数的最大值被称为最优动作值函数 Q(s,a)* ,并且根据贝尔曼方程由下式给出
那么最优策略 π(s)* 由最大化动作值函数的动作给出:
问题是,在大多数实际情况下,状态转移模型和报酬函数是未知的,因此有必要从采样中学习它们,以便估计最佳行动值函数和最佳策略。由于这些原因,使用 RL 算法,以便在环境中采取行动,观察和学习模型的动态,估计最优值函数和最优策略,并提高回报。
勘探开发困境
探索是对新数据点的训练,而开发是对以前获取的数据的使用。如果我们在每次迭代中不断寻找最佳行动,我们可能会停留在有限的一组状态中,而无法探索整个环境。为了摆脱这个次优集合,通常使用一种叫做ϵ-greedy:的策略,当我们选择最佳行动时,选择随机行动的ϵ概率很小。
方法
当我们实现一个 RL 算法时,我们可以使用 3 种主要的方法:
- 基于价值的方法- 基于价值的算法通过不断改进其估计值来逼近最优价值函数或最优行动价值函数。通常值函数或动作值函数是随机初始化的,然后不断更新直到收敛。基于值的算法保证收敛到最优值。
- 基于策略的方法- 基于策略的算法寻找一个策略,使得在每个状态下执行的动作是最优的,以在未来获得最大的回报。它在每一步重新定义策略,并根据这个新策略计算值函数,直到策略收敛。基于策略的方法也保证收敛到最优策略,并且通常比基于值的算法花费更少的迭代来收敛。
- 基于模型的方法- 基于模型的算法从原始环境开始学习虚拟模型,代理学习如何在虚拟模型中执行。它在学习阶段使用减少数量的与真实环境的交互,然后基于这些交互建立新的模型,使用该模型模拟进一步的情节,并获得虚拟模型返回的结果。
基于价值的方法
价值函数逼近
价值函数逼近是最经典的价值方法之一。其目标是通过迭代逼近最优行动值函数 Q(s,a)* 来估计最优策略 π(s)* 。我们开始考虑一个参数动作值函数 Q^(s,a,w) ,其中 w 是一个参数向量。我们随机初始化向量 w 并迭代每集的每一步。对于每次迭代,给定状态 s 和动作 a ,我们观察奖励 R(s,a) 和新状态s’。根据获得的回报,我们使用梯度下降更新参数:
等式中,α是学习率。可以证明这个过程是收敛的,得到的作用值函数就是我们对最优作用值函数的逼近。在大多数实际情况下,参数动作值函数 Q^(s,a,w) 的更好选择是神经网络,然后参数矢量 w 由神经网络的权重矢量给出。
值函数逼近算法:
深度 Q-网络
深度 q 网络是深度学习和 RL 的组合,因为它是值函数近似算法,其中参数动作值函数 Q^(s,a,w) 是深度神经网络,特别是卷积神经网络。此外,深度 Q 网络主要使用两种技术来克服不稳定学习
- 目标网络- 模型更新可能非常不稳定,因为每次模型更新自身时,真实的目标都会改变。解决方案是创建一个目标网络 Q^(s’,a’,w’) ,它是训练模型的副本,更新频率较低,例如每千步更新一次(我们将目标网络的权重表示为w’)。在使用梯度下降的每个模型更新中,目标网络代替模型本身用作目标:
- 体验回放- 在所描述的算法中,使用来自同一集的数据执行若干连续更新,这可能导致过拟合。为了解决这个问题,创建了一个经验重放缓冲区,存储所有不同剧集的四元组( s 、 a 、 r 、s’),并在每次模型更新时随机选择一批元组。这种解决方案有 3 个优点:减少过度拟合,用小批量提高学习速度,重用过去的元组以避免遗忘。
拟合 Q 迭代
另一种流行的基于值的算法是拟合 Q 迭代。考虑确定性的情况,在这种情况下,根据某个函数 f,,新状态s’由状态 s 和动作 a 唯一确定,那么我们可以写出s’= f(s,a) 。设 L 为地平线,可能是无限的,我们回忆地平线是所有剧集的长度。该算法的目标是估计最佳动作值函数。根据贝尔曼方程,最佳动作值函数 Q(s,a)* 可以看作是一个算子 H 对动作值函数 Q(s,a) 的应用:
现在考虑时间范围 N 小于或等于时间范围 L ,并且由 Q_N (s,a) 表示通过将刚刚定义的算子 H 应用于动作值函数Q _(n1)(s,a) 而定义的在 N 步上的动作值函数,其中
可以表明,这个序列的N-阶跃动作值函数 Q_N (s,a) 收敛到最优动作值函数 Q(s,a)* 为 N → L 。由于这一点,有可能建立一种算法来逼近最优动作值函数 Q(s,a)* ,在 N 上迭代。
拟合 Q 迭代算法:
Fitted Q 迭代的完整实现可以在 GitHub
(https://github.com/teopir/ifqi)上找到。
拟合 Q 迭代应用示例:山上的汽车
考虑一辆由点质量建模的汽车,它以如下形式在山上行驶:
作者图片
控制问题的目标是在最短的时间内将汽车带到山顶,同时防止汽车的位置 p 变得小于 -1 并且其速度 v 超出区间 [-3,3】。在位置 p = 1 到达山顶。
状态空间- 这个问题有一个二维(汽车的位置 p 和速度 v )的(连续)状态空间,我们希望位置的绝对值小于等于 1 ,速度的绝对值小于等于 3 :
每隔一个位置和速度的组合被认为是一个终端状态。
动作空间- 动作 a 直接作用于汽车的加速度,并且
只能假设两个极值(全加速( a = 4)或全减速( a -4 ))。因此,动作空间是由集合给出的
系统动力学- 时间离散化为 0.1 秒的时间步长。给定时间步长 t 处的状态( p , v )和动作 a ,我们能够计算时间步长 t + 1 处的状态( p , v ),用数值方法求解与描述系统动态的位置和速度相关的两个微分方程:
当然,为了我们的目的,理解这些方程的含义并不重要,重要的是理解在时间步长 t 的给定状态和动作,时间步长 t + 1 的状态是唯一确定的。
奖励函数- 奖励函数 r(s,a) 通过以下表达式定义:
如果位置小于 -1 或者速度的绝对值大于 3 因为我们到达了终止状态但是没有到达山顶,奖励是*-1*;如果位置大于 1 并且速度的绝对值小于 3,奖励是 1 ,因为我们到达了遵守速度限制的山顶;否则奖励为 0 。
折扣因子- 衰减因子γ被选择为等于 0.95。
初始点 -开始时,汽车停在山脚下( p 、 v ) = ( 0.5 、 0 )。
回归器- 使用的回归器是一个额外的树回归器。
对 N = 1 到 50 执行拟合的 q 迭代,结果是对于 N > 20 动作值函数 Q^_N (s,a)和 Q^_(N+1)(s,a) (对( p , v )的所有组合计算)之间的均方误差迅速减小到 0 为此,使用动作状态函数 Q^_20(s,a) 研究结果。
左图中我们可以看到根据动作值函数 Q^_20(s,a) 为
( p , v )的每种组合选择的动作(红色区域代表减速,绿色区域代表加速,蓝色区域代表减速和加速的动作值相等)。
根据动作值函数 Q^_20(s,a) 的最佳轨迹如右图所示。
作者图片
策略值方法
政策梯度
政策梯度是最经典的基于政策的方法。策略梯度法的目标是在参数化策略 π (a|s,θ) 下,找到使价值函数 V (s,θ) 最大化的参数矢量 θ 。
我们开始考虑一个参数策略 π (a|s,θ) 关于参数向量 θ 可微;特别地,在这种情况下,我们选择随机策略(在这种情况下,该方法被称为随机策略梯度,然而,确定性策略的情况非常类似)。
我们随机初始化向量 w 并在每一集迭代。对于每个时间步长 t ,我们生成一个三元组序列( s , a , r ),根据参数策略 π (a|s,θ) 选择动作。对于结果序列中的每个时间步,我们计算总的长期奖励,并根据所获得的奖励计算折扣 G_t :
然后,使用梯度更新过程来修改参数向量 θ_t
在等式中,α > 0 是学习率。
可以证明这个过程是收敛的,得到的过程就是我们的近似最优策略。
策略梯度算法:
参数策略的示例
最常用的参数策略是 Softmax 策略和高斯策略*。*
Softmax 策略
Softmax 策略由一个 soft max 函数组成,该函数将输出转换为概率的
分布,主要用于离散动作的情况:
在这种情况下,梯度更新的显式公式由下式给出
其中 φ(s,a) 是与状态和动作相关的特征向量。
高斯策略
高斯策略用于连续动作空间的情况,由高斯函数给出
其中 (s) 由φ(s) T θ给出, φ(s,a)为特征向量, σ 可以是固定的,也可以是参数化的。在这种情况下,我们还有梯度更新的显式公式:
政策梯度的利与弊
优点
- 与基于价值的
方法相比,政策梯度方法是一个更简单的流程。 - 它允许动作相对于状态是连续的。
- 相对于其他方法,它通常具有更好的收敛特性。
- 当动作和状态集很大时,它避免了存储器使用和计算时间的增长,因为目标是学习一组参数,其大小远小于状态集和动作集的大小。
- 它可以学习随机政策。
- 它允许使用ϵ-greedy 方法,这样代理人可以有一个采取随机行动的概率ϵ。
缺点
- 策略梯度方法通常收敛于局部最优,而不是全局最优。
- 它通常具有很高的方差(但是可以通过一些技术来降低)。
政策梯度应用示例:CartPole
CartPole 是一种游戏,其中一根杆子通过一个未驱动的关节连接到一辆沿着无摩擦轨道移动的车上。杆子开始直立。
作者图片
目标是通过增加和减少小车的速度来防止杆子倒下。
状态空间- 单个状态由 4 个元素组成:
- 推车位置
- 推车速度
- 磁极角度
- 极点角速度
当杆子落下时游戏结束,也就是杆子角度超过 12 ,或者推车位置到达显示器边缘。
行动空间- 代理只能采取两种行动:
- 向左移动杆子
- 向右移动杆子
奖励- 每采取一个步骤(包括终止步骤),奖励增加 1 。这显然是因为我们希望实现尽可能多的步骤。
使用 Softmax 策略的梯度策略方法解决问题,折扣因子 γ = 0.95 ,学习率 α = 0.1 。对于每一集,最大迭代次数为 1000 次。
在大约 60 个时期(其中 1 个时期等于 20 个连续的情节)之后,代理学习一个策略,由于这个策略,我们获得了等于 1000 的奖励,这意味着在该情节的所有 1000 个步骤中杆子都没有落下。
在这些图中,我们可以看到动作的选择是如何随杆角度和小车速度(左图)以及杆角速度和小车速度(右图)而变化的。红色区域是选择向左移动动作的地方,绿色区域是选择向右移动动作的地方,黄色区域是选择一个动作或另一个动作的概率相似的地方。
作者图片
一个很有意思的结果是,如果 γ 大于 0.9,单集奖励随着历元数增长,达到最大值 1000,而如果 γ 小于 0.9,经过一些历元后,单集奖励停止增长。这意味着,在这个问题中,下一步的奖励对于找到最佳策略非常重要,这实际上是合理的,因为学习如何防止杆子落下的基本信息是知道在每一集里它落下了多少步。
在 GitHub 上,可以找到这个例子的许多不同的实现。
演员-评论家方法
另一种流行的基于政策的方法是演员批评法。它不同于策略梯度方法,因为它同时估计策略和值函数,并更新两者。
在政策梯度中,参数向量 θ 使用长期报酬 G_t 进行更新,但这种估计通常具有较高的方差。为了解决这个问题并减少结果的巨大变化,演员-评论家方法的思想是从总报酬中减去折扣 G_t 基线 b(s) 。
所获得的值 δ = Gt - b(s) ,即所谓的时间差分误差,用于更新参数矢量 θ 来代替长期报酬 G_t 。基线可以有几种形式,但最常用的是价值函数 V(s) 的估计。
如同在基于值的方法中,值函数 V(s) 可以用神经网络学习,其输出是近似值函数 V^(s,w) ,其中 w 是权重向量。然后,在每次迭代中,时间差误差 δ 不仅用于调整参数矢量 θ ,还用于更新权重矢量 w 。
这种方法被称为演员-评论家方法,因为:
- 评论家估计价值函数 V(s) 。
- 参与者按照评论家建议的方向更新策略分布(如在策略梯度方法中)。
演员-评论家算法:
基于模型的方法
如已经强调的,基于模型的方法从原始环境开始创建虚拟模型,并且代理学习如何在虚拟模型中执行。基于模型的方法开始考虑基本参数模型,然后运行以下 3 个步骤:
- Acting :基础策略 π_0(a_t|s_t) 用于选择在真实环境中执行的动作,以收集三元组(state、action、new state)给出的一组观察值;
- 模型学习:从收集的经验中,推导出新的模型 m(s,a) ,以使模型新状态与真实新状态之间的最小二乘误差最小;可以使用监督学习算法来训练模型,以最小化来自采样轨迹的最小平方误差;
- 规划:根据新的模型更新价值函数和策略,以便在下一次迭代中用于选择要在真实环境中执行的动作。
最常用来表示系统动态的模型之一是高斯过程,其中预测使用高斯分布对观测值进行插值。另一种可能性是使用高斯混合模型,这是一种概率模型,它假设所有数据点都是从具有未知参数的有限数量的高斯分布的混合中生成的。这是对 k 的一种概括,意味着聚类包含了关于数据的协方差结构以及潜在高斯中心的信息。
基于模型的方法示例算法:
模型预测控制
模型预测控制是刚刚描述的方法的发展。所描述的基于模型的算法易受漂移的影响:小误差沿着轨迹快速积累,并且搜索空间太大,任何基本策略都无法覆盖全部。由于这个原因,轨迹可能到达模型还没有被学习的区域。如果没有围绕这些区域的适当模型,就不可能规划最佳控制。
为了解决这个问题,不是在开始时学习模型,而是在轨迹期间连续地执行模型的采样和拟合。然而,前面的方法在再次拟合模型之前执行所有计划的动作。
在模型预测控制中,整个轨迹被优化,但是仅执行第一个动作,然后新的三元组( s 、 a 、s’)被添加到观测值中,并且再次进行规划。这允许在再次观察到当前状态时采取纠正措施。对于随机模型来说,这尤其有用。
通过不断改变计划,MPC 不容易受到模型中问题的影响。新算法运行 5 个步骤,其中前 3 个与以前的算法相同(动作、模型学习、规划)。然后我们有:
- 演技
- 模型学习
- 策划
- 执行:执行第一个计划动作,观察结果状态s’;
- 数据集更新:新的三元组( s , a ,s’)被追加到数据集;转到第 3 步,每隔 N 次转到第 2 步(正如已经看到的,这意味着规划在每一步都执行,并且模型在轨迹的每 N 步都被拟合)。
基于模型的方法的优点和缺点
基于模型的 RL 具有很强的优势,即在样本很少的情况下非常有效,因为许多模型至少在局部附近表现为线性。
一旦模型和回报函数已知,最优控制的规划就不需要额外的采样。通常,学习阶段是快速的,因为不需要等待环境响应,也不需要为了恢复学习而将环境重置到某个状态。
不利的一面是,如果模型不准确,我们可能会学到与现实完全不同的东西。另一点没有价值是,基于模型的算法仍然使用无模型的方法来构建模型或在计划和模拟阶段。
结论
本文是许多经典 RL 算法的高级结构概述。然而,令人遗憾的是,每个型号系列中都有很多我们没有涉及到的变体。例如,在深 Q 网络家族中,双深 Q 网络给出了非常有趣的结果。
RL 的主要挑战在于准备仿真环境和选择最合适的方法。这些方面高度依赖于要执行的任务,并且非常重要,因为许多现实世界的问题具有巨大的状态或动作空间,必须被有效和全面地表示。
其他主要任务是优化奖励以获得期望的结果,建立系统以使学习过程在合理的时间内收敛到最优,并避免过度适应和遗忘。
作者
我关于走向数据科学的文章:https://medium.com/@marcodelpra
我的 LinkedIn 个人资料:【https://www.linkedin.com/in/marco-del-pra-7179516/
领英集团 AI 学习:https://www.linkedin.com/groups/8974511/
参考
- 理查德·萨顿和安德鲁·巴尔托。强化学习:安
简介。 - 文森特·弗朗索瓦·拉韦特、彼得·亨德森、里亚沙特·伊斯兰、马克·g·贝勒马尔、乔埃勒·皮诺。深度强化学习简介。
- 达米恩·恩斯特,皮埃尔·格茨,路易斯·韦汉高。基于树的批处理模式
强化学习。机器学习研究杂志 6(2005)
503–556。 - https://github.com/openai/gym
- https://github.com/teopir/ifqi
不确定度校准和可靠性图表简介
挑战不确定度校准的常见误解
不确定性校准是机器学习中最容易被误解的概念之一。这可以归结为一个简单的问题:“考虑到上述下雨的可能性,你带雨伞了吗?”
我们在日常生活中使用主观概率和不确定度校准的概念,却没有意识到它们。对于一个校准良好的不确定性天气预报模型来说,如果下雨的概率只有 5%,那么带伞大概是不划算的。从频率主义者的角度来看,如果可以在大量随机试验中反复观察到上图中早上 7 点的天气状况,那么只有 5%的天气会下雨。然而,另一方面,如果不确定性校准不当,很可能早上 7 点的随机试验中有 40%会以下雨告终——这是一个大惊喜。
什么是不确定度校准?
DeGroot 等人[1]以预测降雨为例来说明校准的概念。
校准的概念与预报员的预测和实际 观测到的降雨相对频率 之间的一致有关。笼统地说,如果一个预测者的预测是 x,那么他的长期相对频率也是 x,那么这个预测者就被称为校准良好的。
换句话说,对于一个校准良好的模型,如果它预测一组图像有 40%的概率是猫,那么该组中包含的猫的频率应该等于 40%。“相对频率”有时被称为“条件概率”,可以理解为 cat 的概率,即,以预测概率为 40%为条件的阳性结果。
Dawid [1]提出了类似的程序,如下所示。
将预测与现实进行比较的一种方法是挑选一些相当任意的测试日,并在其中比较(a)相关事件实际发生的日的比例 p 与(b)这些日的平均预测概率π。
如果我们遵循一些划分方案来选择测试集,并在其中比较(a)和(b ),我们就得到可靠性图。
可靠性图表
在一个二元分类问题中,我们训练一个模型来估计一个例子被分类为正类的概率,即 f(x_i)=p(y_i=1|x_i),如下图 1 所示。
图一。估计一组例子的概率
一旦我们获得了一个测试集的概率,我们将概率划分为 K 个子集,其中每个子集代表 0 和 1 之间的一个不相交的概率区间。这如图 2 所示。
图二。这些示例基于区间[0,0.33]、[0.33,0.66]和[0.66,1]被分成三组。
对于不同颜色的每个子集,我们计算两个估计值:(a)平均预测概率,(b)正例的相对频率。
我们首先计算(a)每个子集的平均预测概率,如图 3 所示。
图 3。每个子集的平均预测概率。
我们接下来计算(b)正例的相对频率,这需要基础事实标签的知识。在图 4 中,我们用灰色圆圈表示负类,其余的颜色表示正类。作为例子,在集合 1 中,只有一个例子是正的;所以正例的相对频率是 1/3。
图 4。正面例子的相对频率
可靠性图是绘制(b)与(a)的关系,如图 5 所示。
图 5。可靠性图表
直观地,校准图表明:(I)当平均预测概率为 0.17 时,大约 33%的预测是肯定的;(II)当平均预测概率为 0.45 时,约 50%的预测是肯定的;(III)当平均预测概率为 0.82 时,约 80%的预测是肯定的。这个人为的模型虽然不完美,但相对来说还是比较精确的。
误解:相对频率与准确度
关于可靠性图表的一个普遍误解是用“准确性”代替“相对频率”有时从业者——包括我非常尊敬的杰出研究人员——用“每个子集的准确度”来表示相对频率,这并不是校准的目的。我们需要从相对频率的角度来理解校准,其中阳性类别的预测置信度应该反映所有预测中阳性样本的频率(有时称为子集的流行度),而不是准确性。
我将使用来自 scikit-learn [3]的一个例子来展示它们之间的区别。图 6 显示了一个逻辑回归模型的可靠性图,这是一个相对校准良好的模型。
图 6。可靠性图表
然而,如果我使用精度来绘制 y 轴,它看起来像一条 V 形曲线,如图 7 所示。
图 7。准确度-平均预测值图
这是因为当正面预测概率较低而负面概率较高时,会提升图的左半部分。这可以通过平均预测值的直方图来验证。大量实例的平均预测值在 0 和 0.1 之间,因为它们是负类,并且它们中的大多数被模型正确分类。
结论
我希望你永远不要担心天气预报的不确定性校准。如果当下雨的概率低于 20%时,你总是淋湿,你就知道预测模型没有校准好。
从机器学习从业者的角度来看,不确定性校准与模型的概率结果的解释高度相关,特别是在安全关键应用中,如医疗领域。
参考
[1] DeGroot、Morris H .和 Stephen E. Fienberg。"预测者的比较和评估."皇家统计学会杂志:D 辑(统计学家)32.1–2(1983):12–22。美国心理学协会(American Psychological Association)
[2]达维德,菲利普。"精确的贝叶斯理论。"美国统计协会杂志77.379(1982):605–610。美国心理学协会(American Psychological Association)
机器人控制系统介绍
PC:pick pik(https://www . pick pik . com/wall-e-robot-toy-cute-wallpaper-romantic-2954)
机器人学是工程学和计算机科学之间的交叉学科。机器人技术的主要目标是生产计算机可编程的机器,能够以更高的速度和精度完成任务。在当前时代,机器人的应用数不胜数,例如运输重物(在物流管理中)、自动化制造、自动驾驶汽车和无人驾驶飞行器等等。
了解控制系统的概念对每个初学者来说都是入门机器人的必要条件。控制系统有助于控制机器人的运动和功能。为了理解控制系统,我们首先需要理解机器人学中使用的一些术语。
反馈控制系统(作者供图)
- 状态——机器人系统产生输出被称为状态。通常我们用 x 来表示,状态取决于其先前的状态、施加到致动器的激励(信号)以及环境的物理特性。状态可以是姿态、速度、速率、角速度、力等等。
- 估计 -机器人无法确定准确的状态 x ,但它们可以使用附着在它们身上的传感器来估计x。这些估计用 y 表示。机器人工程师的责任是选择足够好的传感器或很好地校准传感器,以便它们能产生 y ~ x.
- 参考 -我们希望达到的目标状态,用 r. 表示
- 误差—参考值和估计值之间的差异称为误差。
- 控制信号 -控制器产生/输出的刺激称为控制信号,用 u. 表示
- 动力学 -也称为系统设备/系统模型,它表示系统在非静态条件下的行为。动态受环境影响,环境可能变化或不总是线性的。例如,地板类型(混凝土/木材)、空气阻力、坡度等。
工程师的主要职责始终是构建一个控制器,该控制器反应并产生控制信号 u,,使得 e~0 & x~r.
让我给你举一个上面讨论的关键术语的例子。假设你正在建造一个自动驾驶机器人。你应该为你的机器人配备一个巡航控制器。这里,
- 你的机器人的速度(x)就是状态
- 来自首选传感器(例如车轮编码器)的估计当前速度(y)
- 你希望达到的速度是参考值®
- 误差仅仅是 r 和 y 之间的差异
- 控制器产生的增加或降低速度的电压就是你的控制信号(u)
- 摩擦和空气阻力等环境特征可能会影响系统的动态特性。你应该在设计控制系统方程之前考虑它们
注意:一个机器人可以有一个或多个用于不同目的的控制器。例如,一个控制器用于巡航控制,另一个控制器用于控制机器人手的线性运动,一个控制器用于手的旋转运动等。
我们需要控制器,因为动态(系统设备)随时间变化。例如当机器人在斜坡上向上移动,然后在斜坡上向下移动,或者首先在光滑的混凝土上移动,然后在铺有地毯的地板上移动。因此,设计控制器的最佳方式是充分了解环境的物理特性。这将有助于列出控制器设计中需要考虑的要素。
在我结束之前,让我们看看控制器的理想特性。我们已经看到了第一个要求,
- 控制器应该将误差减小到接近零, **e~0,**它应该将估计值带到参考值, y~r
- 他们需要**健壮,**不应该依赖于我们不知道的东西。如果机器人的环境有任何变化,它们应该能够适应。
- 它们必须稳定,也就是说它们不应该失控。
- 他们需要动作流畅。
- 控制器需要响应。它应该足够快,以便在令人满意的时间内使输出达到基准电平
总之,我们给控制器一个参考状态。控制器还具有传感器反馈,利用参考状态和传感器反馈控制器产生达到参考状态所需的控制信号。这个控制信号被馈送到“系统”。系统动态特性决定了系统对该控制输入的反应。如果控制器是好的,希望“系统”将达到我们期望的参考状态。
希望这篇关于机器人控制系统的简短介绍对初学者有所帮助。
模式介绍:验证数据的 Python 库
验证您的数据变得更加复杂!
动机
您的脚本可以处理训练数据,但是当您将该脚本用于一个新的但应该相似的数据时,就会遇到错误。这是怎么回事?这可能是因为您的数据结构不像您预期的那样。
但是您可能很难查看新数据的每一行来找出问题所在。每次使用新数据时,手动分析您的数据也可能耗时。
如果您的代码没有抛出任何错误,但是数据发生了变化,那就更糟了。因此,您的型号的性能可能会变得更差**,因为数据与您的预期不同。**
如果我们可以用 Pytest 之类的工具编写函数测试,那么有没有一种方法也可以编写数据测试呢?
我们可以用图式做到这一点。本文将向您展示如何在各种场景中使用模式。
什么是图式?
Schema 是用于验证 Python 数据结构的库。
安装架构时使用
pip install schema
我们将使用 faker 来创建字典列表数据。Faker 是一个 Python 库,使我们能够轻松地创建假数据。我在这里写了如何使用 faker。
想象一下,这些数据展示了你朋友的信息。
[{'name': 'Norma Fisher',
'city': 'South Richard',
'closeness (1-5)': 4,
'extrovert': True,
'favorite_temperature': -45.74},
{'name': 'Colleen Taylor',
'city': 'North Laurenshire',
'closeness (1-5)': 4,
'extrovert': False,
'favorite_temperature': 93.9},
{'name': 'Melinda Kennedy',
'city': 'South Cherylside',
'closeness (1-5)': 1,
'extrovert': True,
'favorite_temperature': 66.33}]
模式入门
验证数据类型
我们可以使用模式来验证数据类型,如下所示。
[{'name': 'Norma Fisher',
'city': 'South Richard',
'closeness (1-5)': 4,
'extrovert': True,
'favorite_temperature': -45.74},
{'name': 'Colleen Taylor',
'city': 'North Laurenshire',
'closeness (1-5)': 4,
'extrovert': False,
'favorite_temperature': 93.9},
{'name': 'Melinda Kennedy',
'city': 'South Cherylside',
'closeness (1-5)': 1,
'extrovert': True,
'favorite_temperature': 66.33}]We want to make sure that ‘name’, ‘city’ columns are string type, ‘closeness (1–5)’ are
因为 schema 返回输出时没有抛出任何错误,所以我们知道我们的数据是有效的。
让我们看看如果数据类型不像我们期望的那样会发生什么
SchemaError: Or({'name': <class 'int'>, 'city': <class 'str'>, 'closeness (1-5)': <class 'int'>, 'extrovert': <class 'bool'>, 'favorite_temperature': <class 'float'>}) did not validate {'name': 'Norma Fisher', 'city': 'South Richard', 'closeness (1-5)': 3, 'extrovert': True, 'favorite_temperature': -45.74}Key 'name' error:
'Norma Fisher' should be instance of 'int'
从错误中,我们确切地知道数据的哪一列和值与我们期望的不同。因此,我们可以返回数据来修复或删除该值。
如果您只关心数据是否有效,请使用
schema.is_valid(data)
如果数据符合预期,这将返回True
,否则返回False
。
验证某些列的数据类型,而忽略其余列
但是,如果我们不关心所有列的数据类型,而只关心某些列的值,那该怎么办呢?我们可以用str: object
来说明
Output: True
如您所见,我们尝试验证“名称”、“城市”和“favorite_temperature”的数据类型,而忽略数据中其余要素的数据类型。
数据是有效的,因为指定的 3 个要素的数据类型是正确的。
用函数验证
如果我们想确定列中的数据是否满足与数据类型(如列中值的范围)无关的特定条件,该怎么办?
Schema 允许您使用函数来指定数据的条件。
如果我们想检查“亲密度”列中的值是否在 1 到 5 之间,我们可以使用如下的lambda
Output: True
正如你所看到的,我们指定n,
列中每一行的值为‘亲密度’,在 1 到 5 之间,用lambda n: 1 <= n <=5.
表示整洁!
验证几个模式
和
如果你想确保你的‘接近度’列在 1 和 5 之间并且数据类型是一个整数呢?
这时候And
就派上用场了
Output: False
虽然所有值都在 1 和 5 之间,但数据类型不是浮点型。因为不满足其中一个条件,所以数据无效
或
如果我们想在满足任一条件的情况下使列的数据有效,我们可以使用Or
例如,如果我们希望城市名称包含 1 个或 2 个单词,我们可以使用
与和或的组合
如果我们希望“城市”的数据类型是字符串,但长度可以是 1 或 2,该怎么办?幸运的是,这可以通过组合And
和Or
来轻松处理
Output: True
可选择的
如果我们没有你朋友的详细信息怎么办?
[{'name': 'Norma Fisher',
'city': 'South Richard',
'closeness (1-5)': 4,
'detailed_info': {'favorite_color': 'Pink',
'phone number': '7593824219489'}},
{'name': 'Emily Blair',
'city': 'Suttonview',
'closeness (1-5)': 4,
'detailed_info': {'favorite_color': 'Chartreuse',
'phone number': '9387784080160'}},
{'name': 'Samantha Cook', 'city': 'Janeton', 'closeness (1-5)': 3}]
因为 Samantha Cook 的“detailed_info”并不是对您所有的朋友都可用,所以我们想将此列设为可选。模式允许我们用Optional
设置条件
Output: True
被禁止的
有时,我们可能还想确保某种类型的数据不在我们的数据中,比如私人信息。我们可以用Forbidden
指定禁止哪个列
Forbidden key encountered: 'detailed_info' in {'name': 'Norma Fisher', 'city': 'South Richard', 'closeness (1-5)': 4, 'detailed_info': {'favorite_color': 'Pink', 'phone number': '7593824219489'}}
现在,每当 schema 抛出一个错误时,我们都知道了被禁止的列的存在!
嵌套词典
到目前为止,schema 已经使我们能够在几行代码中执行许多复杂的验证。但是在现实生活中,我们可能会处理比上面的例子更复杂的数据结构。
我们能把它用于更复杂结构的数据吗?比如字典中的字典?是的,我们可以
我们创建另一个包含嵌套字典的数据
>>> data[{'name': 'Norma Fisher',
'city': 'South Richard',
'closeness (1-5)': 4,
'detailed_info': {'favorite_color': 'Pink',
'phone number': '7593824219489'}},
{'name': 'Emily Blair',
'city': 'Suttonview',
'closeness (1-5)': 4,
'detailed_info': {'favorite_color': 'Chartreuse',
'phone number': '9387784080160'}}]
现在我们用嵌套字典进行验证
语法非常简单!我们只需要在字典中编写另一个字典,并为每个键指定数据类型。
转换数据类型
schema 不仅可以用来验证数据,还可以用来转换数据类型,如果数据类型与我们预期的不一样的话!
例如,我们可以用Use(int)
将字符串‘123’转换成整数 123
>>> Schema(Use(int)).validate('123')
123
结论
恭喜你!您刚刚学习了如何使用模式来验证和转换数据结构。如果你希望你的代码是可复制的,不仅需要测试代码,还需要测试你的数据。如果你正在寻找一种方法来验证你的数据,试试这个工具。它不仅有用,而且易于使用。
更多模式示例的源代码可以在这里找到。
如果您想找到更多验证数据的方法,请查看 schema 的文档。
我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以在 LinkedIn 和 Twitter 上与我联系。
如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:
[## 如何在 VSCode 上使用定制的代码片段来提高效率
与其为同一段代码复制,为什么不把它保存起来以备将来使用呢?
towardsdatascience.com](/how-to-boost-your-efficiency-with-customized-code-snippets-on-vscode-8127781788d7) [## 如何用 Faker 创建假数据
您可以收集数据或创建自己的数据
towardsdatascience.com](/how-to-create-fake-data-with-faker-a835e5b7a9d9) [## 如何在命令行上创建和查看交互式备忘单
停止搜索命令行。用作弊来节省时间
towardsdatascience.com](/how-to-create-and-view-interactive-cheatsheets-on-the-command-line-6578641039ff) [## Hydra.cc 简介:配置数据科学项目的强大框架
尝试不同的参数和模型,而无需花费数小时来修改代码!
towardsdatascience.com](/introduction-to-hydra-cc-a-powerful-framework-to-configure-your-data-science-projects-ed65713a53c6) [## IBM 联邦学习简介:一种在私有数据上训练 ML 模型的协作方法
在训练来自不同来源的数据时,如何保证数据的安全?
towardsdatascience.com](/introduction-to-ibm-federated-learning-a-collaborative-approach-to-train-ml-models-on-private-data-2b4221c3839)
Biopython 序列比对简介
在 Biopython 中使用序列比对软件包装器
上周,我开始用 Biopython 库摆弄 Python 中的一些生物信息学工具。在我之前的帖子中,我介绍了生物信息学领域,并提供了一个用 Biopython 的 API 接口从 GenBank 下载数据的例子。今天,我想转到分析 DNA 序列数据的下一个典型步骤——比对过程。我将对序列比对进行介绍,然后给出一个使用 Biopython 过滤一些数据并运行比对软件的简单示例。
序列比对介绍
当处理生物序列数据(DNA、RNA 或蛋白质)时,生物学家通常希望能够将一个序列与另一个序列进行比较,以便对序列的功能或进化做出一些推断。就像您不希望使用数据表中数据在错误的列进行分析一样,为了从序列数据中做出可靠的推断,我们需要确保我们的序列数据组织良好或“对齐”不幸的是,序列数据没有漂亮的标签,比如日期、每加仑英里数或马力。相反,我们所拥有的只是序列中的位置编号,而且只与该序列相关。幸运的是,许多序列在相关生物之间高度保守或相似(并且所有生物在某种程度上都是相关的!).如果我们相当确定我们已经从来自多个生物体的相同序列中获得了数据,我们可以将这些数据放入一个我们称之为比对的矩阵中。如果你只是比较两个序列,这叫做成对比对。如果您正在比较三个或更多序列,这被称为多序列比对 (MSA)。
利用序列中每个分子的位置和身份,我们可以推断出每个分子在基质中的相对位置。有时序列中会有差异,比如在一个大多数序列都是 C 的位置,我们发现一个序列带有 g,这被称为单核苷酸多态性(SNP)。在其他时候,我们发现一个序列缺少一个存在于其他序列中的分子,或者一个序列有一个额外的分子。前者是删除,而后者是插入,统称为“插入”当用 indels 比对序列时,我们必须通过在剩余的序列上添加缺口来解释这些额外的或缺失的分子。这些微小的差异通常是序列数据中有趣的部分,因为变异是我们如何对序列的功能或进化做出推断的。以下是 SNPS 和因德尔斯签订的管理服务协议的几个例子。
Example 1\. Multiple Sequence Alignment with a Single Nucleotide Polymorphism (SNP) in Sequence 3 at position 3\. 1 2 3 4 5 6 7 8 <- Nucleotide Positions
Seq1 G T **C** G C A A A
Seq2 G T **C** G C A A A
Seq3 G T **G** G C A A AExample 2\. Multiple Sequence Alignment with an insertion in Sequence 1 at position 6. 1 2 3 4 5 6 7 8
Seq1 G T C G C **A** A A
Seq2 G T C G C **-** A A
Seq3 G T C G C **-** A A
上面两个例子中,序列长度短,序列个数少,序列间相似度高。因此,直观地排列序列非常容易。然而,如果您有数百个序列,有数千或数万个位置要对齐,您可能不想全部手动对齐!相反,有一些程序使用算法来排列序列。
对于算法来说,比对是非常有趣的问题。首先,您希望通过对齐序列来最小化它们之间的差异。对于不同的序列区域,该算法有两种选择:(1)在比对中添加一个缺口,说明“该区域对于该序列是唯一的,并且在其他序列中没有类似的东西,”或者(2)将不相似的序列比对在一起,实质上说明“这些在这里是不同的,但是周围的序列足够相似以保持位置结构相同。”如果算法的唯一目标是最小化差异,它只会不断增加差距!相反,大多数比对算法的第二个目标也是最小化缺口的数量,并且当缺口被引入时,它们被给予惩罚分数。由于要跨许多序列和序列中的许多位置执行两种优化,这些程序的计算时间和内存需求可能非常大。我已经等了几个小时甚至几天来完成跑步!
尽管排列序列很费时间,但结果在遗传和进化研究中至关重要。序列之间的相似性和差异可以告诉你一个生物体与另一个生物体的进化关系,或者一个基因与另一个基因相比的功能。不建立序列之间的关系,你就不能做出这种推论。我已经掩饰了进入这个过程的一些更好的细节和假设,但是,在本质上,比对是分子生物学世界的数据框架。
使用 Biopython 进行序列比对
在我之前的文章中,我从 GenBank 下载了新型冠状病毒病毒(又名新冠肺炎病毒)样本的序列数据。我想将这些序列相互比较,以进行一些进化分析,但这首先需要比对!因此,继续我离开的地方,我将使用 Biopython 使用多序列比对软件 MUSCLE 来创建我的 MSA。
生物信息学 python 库 Biopython 有几个用于操作和构建序列比对的工具。Bio.AlignIO
和Bio.Align
模块包含这些工具。您可以读取和写入路线文件,转换其类型,并使用路线软件界面来创建路线。因为对齐软件相当慢,所以创建该软件的 Python 实现效率不是很高。相反,Biopython 只是使用包装器来执行用其他更快的语言编写的软件。它基本上与从命令行运行这些软件相同,但是 Biopython 让您有机会完全在 python 中构建工作流。Biopython 为九个常用的比对软件提供了包装器(在本文撰写之时),您可以通过运行下面一行来查看这些包装器:import Bio.Align.Applications; dir(Bio.Align.Applications)
。
使用包装器非常类似于在命令行中使用它们。使用对齐的所有参数设置实例化一个对象,然后执行该对象。下面,我展示了如何将我的新型冠状病毒数据过滤成完整的序列,然后使用 MUSCLE 来排列这些序列。(**注意:**你需要先装上 MUSCLE 才可以。你可以在这里下载肌肉。将可执行文件重命名为“muscle”会很有帮助然后,您应该确保muscle
在您的 PATH 中,即在usr/local/bin
目录中。只需将下载并重命名的副本复制并粘贴到该目录中。)
from Bio import SeqIO
from Bio.Align.Applications import MuscleCommandline#Read in unfiltered data
unfiltered = SeqIO.parse("../../data/raw/SARS-CoV-2.gbk", "genbank")#Drop data without (close to) full length sequences
full_length_records = []
for record in unfiltered:
if len(record.seq) > 29000:
full_length_records.append(record)#Write filtered data to file
SeqIO.write(full_length_records, "../../data/raw/SARS-CoV-2.fasta", "fasta")#Align sequences with MUSCLE (using parameters to make the alignment
#process as fast as possible)
muscle_cline = MuscleCommandline(input="SARS-CoV-2.fasta",
out="SARS-CoV-2_aligned.fasta",
diags = True,
maxiters = 1,
log="../../data/raw/align_log.txt")
muscle_cline()
正如我前面提到的,运行校准需要相当长的时间。有一些在线服务可以比你在本地机器上更快地运行你的比对。如果你时间不够,这些可能会有用。此外,看起来有一些新兴的技术可以利用 Hadoop 和 Spark 等系统来并行化这一过程,但在推荐之前,我想进一步了解这些技术。
对齐完成后,您可以使用多种对齐查看器之一对其进行目视检查。不幸的是,这不是一个容易从命令行完成的过程,所以 Biopython 没有试图给你这种能力。是的,您可以读取与Bio.AlignIO
模块的对齐,并通过切片来迭代滚动,但这不是很有效。这是我用 NCBI 的多序列比对查看器打开的一个比对实例,这是一个基于浏览器的选项。
多序列比对示例,用 NCBI 多序列比对查看器查看。
这就是我本周关于对齐的文章的结尾。下周,我将利用这里创建的比对继续分析新型冠状病毒数据。像往常一样,如果你有问题或意见,请告诉我!感谢阅读!
熊猫体育分析简介
德国德甲足球比赛分析。
Bermix 工作室在 Unsplash 拍摄的照片
体育分析是数据科学的一个主要分支。数据收集技术和数据分析的进步使得团队根据数据分析调整策略变得更有吸引力。
数据分析为团队表现和球员表现提供了有价值的见解。如果明智而系统地使用,数据分析最有可能让团队领先于竞争对手。
一些俱乐部有一整个团队致力于数据分析。利物浦是使用数据分析的先锋,我认为这是他们成功的重要部分。他们是上届英超冠军,也是 2019 年欧冠冠军。
在本帖中,我们将使用熊猫从 2017-18 赛季的德国德甲比赛中得出有意义的结果。这些数据集可以从的链接下载。我们将使用论文足球比赛时空比赛事件公共数据集中介绍的部分数据集。
数据集以 JSON 格式保存,可以很容易地读入 pandas 数据帧。
import numpy as np
import pandas as pdevents = pd.read_json("/content/events_Germany.json")
matches = pd.read_json("/content/matches_Germany.json")
teams = pd.read_json("/content/teams.json")
players = pd.read_json("/content/players.json")events.head()
(图片由作者提供)
事件数据帧包含比赛中发生的事件的细节。例如,第一行告诉我们,球员 15231 在比赛 2516739 的第三秒从位置(50,50)到(50,48)做了一次“简单的传球”。
事件数据帧包括球员和球队 id,但不包括球员和球队名称。我们将使用合并功能从球队和球员数据帧中添加他们。
(图片由作者提供)
Id 存储在球队和球员数据帧的“wyId”列中。
#merge with teams
events = pd.merge(
events, teams[['name','wyId']],left_on='teamId',right_on='wyId'
)
events.rename(columns={'name':'teamName'}, inplace=True)
events.drop('wyId', axis=1, inplace=True)#merge with players
events = pd.merge(
events, players[['wyId','shortName','firstName']],
left_on ='playerId',right_on='wyId'
)
events.rename(columns={'shortName':'playerName', 'firstName':'playerFName'}, inplace=True)
events.drop('wyId', axis=1, inplace=True)
我们根据包含 id 的列合并数据帧,然后重命名新列。最后,删除“wyId”列,因为 Id 已经存储在 events 数据帧中。
(图片由作者提供)
每场比赛的平均传球次数
主导比赛的球队通常传球次数更多。一般来说,他们更有可能赢得这场比赛。当然,也有一些例外。
让我们检查一下每支球队每场比赛的平均传球次数。我们将首先创建一个数据帧,其中包含球队名称、比赛 ID 和比赛中完成的传球次数。
pass_per_match = events[events.eventName == 'Pass']\[['teamName','matchId','eventName']]\
.groupby(['teamName','matchId']).count()\
.reset_index().rename(columns={'eventName':'numberofPasses'})
(图片由作者提供)
奥格斯堡在比赛 2516745 中进行了 471 次传球。这是每场比赛传球数量排名前五的球队名单。
pass_per_match[['teamName','numberofPasses']]\
.groupby('teamName').mean()\
.sort_values(by='numberofPasses', ascending=False).round(1)[:5]
(图片由作者提供)
拜仁慕尼黑传球次数最多并不奇怪。近年来他们一直统治着德甲联赛。
球员平均传球长度
可以基于许多事情来评估一次通过。有些传球非常成功,使得得分变得非常容易。
我们将关注传球的量化评估,即长度。有些球员非常擅长长传。
位置列包含球在 x 和 y 坐标上的初始和最终位置。我们可以根据这些坐标计算长度。让我们首先创建一个只包含通道的数据帧。
passes = events[events.eventName=='Pass'].reset_index(drop=True)
我们现在可以计算长度。
pass_length = []
for i in range(len(passes)):
length = np.sqrt(((passes.positions[i][0]['x'] -
passes.positions[i][1]['x'])**2)\ +
((passes.positions[i][0]['y'] -
passes.positions[i][1]['y'])**2))pass_length.append(length)passes['pass_length'] = pass_length
groupby 函数可以用来计算每个球员的平均传球长度。
passes[['playerName','pass_length']].groupby('playerName')\
.agg(['mean','count']).\
sort_values(by=('pass_length','mean'), ascending=False).round(1)[:5]
(图片由作者提供)
我们列出了平均传球长度和传球次数排名前五的球员。传球次数很重要,因为只传球 3 次对平均值来说意义不大。因此,我们可以过滤那些少于一定数量的通道。
胜利和失败的平均传球次数
让我们比较一下胜利和失败比赛的平均传球次数。我将用勒沃库森的比赛作为例子。
我们首先需要从“matches”数据框架中添加比赛的获胜者。
events = pd.merge(events, matches[['wyId','winner']], left_on='matchId', right_on='wyId')events.drop('wyId', axis=1, inplace=True)
我们现在可以创建一个 dataframe,它只包含团队 ID 为 2446(b . Leverkusen 的 Id)的事件。
leverkusen = events[events.teamId == 2446]
如果“获胜者”列中的值等于 2446,则获胜者是 b .勒沃库森。为了计算 B. Leverkusen 赢得的比赛的平均传球次数,我们需要根据 winner 和 eventName 列过滤数据帧。然后我们将应用 groupby 和 count 来查看每场比赛的传球次数。
passes_in_win = leverkusen[(leverkusen.winner == 2446) & (leverkusen.eventName == 'Pass')][['matchId','eventName']].groupby('matchId').count()passes_in_notwin = leverkusen[(leverkusen.winner != 2446) & (leverkusen.eventName == 'Pass')][['matchId','eventName']].groupby('matchId').count()
(图片由作者提供)
通过应用均值函数,我们可以很容易地得到平均通过次数。
(图片由作者提供)
虽然多传球并不意味着一定会赢,但它会帮助你控制比赛,增加你得分的机会。
体育分析的范围远远超出了我们在这篇文章中所做的。然而,在不熟悉基础知识的情况下,掌握更高级技术的知识将更加困难。
数据可视化也是体育分析的基础。球队和球员如何管理球场,射门和传球的位置,以及球场上覆盖最广的区域提供了宝贵的见解。
我也会写一些关于某些事件如何在球场上可视化的帖子。感谢您的阅读。如果您有任何反馈,请告诉我。
参考文献
[1] Pappalardo 等,(2019)足球比赛时空匹配事件公共数据集,自然科学数据 6:236,https://www.nature.com/articles/s41597-019-0247-7
[2]https://figshare.com/articles/Events/7770599
压缩激励网络简介
基于注意力的机制来提高你的深度 CNN
挤压和激励网络( SENet )是 2017 年 Imagenet 分类挑战赛的获胜者,比 2016 年的获胜者相对提高了约 25%。SENets 引入了一个关键的架构单元——挤压和激励模块(SE 模块),这对性能的提升至关重要。SE 模块还可以轻松添加到其他架构中,额外开销很低。
SE 模块介绍
典型地,CNN 通过从空间维度提取信息并将它们存储在信道维度中来工作。这就是为什么当我们在 CNN 中深入时,特征地图的空间维度会缩小,而频道会增加。当考虑一个特定 CNN 层的输出特征图时,所有通道被同等加权。
我们知道,CNN 中的早期层负责捕捉基本特征,如边缘、拐角和线条,而后期层则捕捉更高级别的特征,如面部和文本。因此,这是有道理的
SE 块的主要思想是:基于每个通道的重要程度(挤压),为特征图的每个通道分配不同的权重(激励)。
SE 块可视化。作者创建的图像。
技术解释
SE 模块可以分为 3 个主要部分——挤压、计算和激励。在这里,我将更详细地介绍它们。
- **【挤压】**操作
在 CNN 图层的输出要素地图上执行全局平均池。这实质上是在空间维度(H×W)上取所有激活的平均值,给出每个通道一个激活。这样做的结果是一个形状矢量(1 x 1 x C)。
2.计算
来自前一个操作的向量通过两个连续的完全连接的层。这用于完全捕获从空间地图聚集的通道相关。ReLU 激活在第一 FC 层之后执行,而 sigmoid 激活在第二 FC 层之后使用。在该论文中,还有一个缩减率,使得第一 FC 层的中间输出具有较小的尺寸。这一步的最终输出也有一个形状(1 x 1 x C)。
3.**【励磁】**运行
最后,计算步骤的输出被用作每声道权重调制向量。它只是简单地与大小为(H x W x C)的原始输入特征图相乘。这根据它们的“重要性”缩放每个通道的空间图。
SE 模块可以很容易地与许多现有的 CNN 集成。在这篇论文中,ResNets、VGG 和 Inception 等架构的精度显著提高,但额外的计算成本很低。
代码示例
这里有一个示例代码片段,您可以尝试一下。写于 PyTorch 。如您所见,添加挤压和激励模块的功能非常简单!在大约 10 行代码中,我们有一个来自 moskomule 的模块化实现,可以很容易地实现到大多数深度 CNN。
class SELayer(nn.Module):
def __init__(self, channel, reduction=16):
super(SELayer, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Sequential(
nn.Linear(channel, channel // reduction, bias=False),
nn.ReLU(inplace=True),
nn.Linear(channel // reduction, channel, bias=False),
nn.Sigmoid()
)def forward(self, x):
b, c, _, _ = x.size()
y = self.avg_pool(x).view(b, c)
y = self.fc(y).view(b, c, 1, 1)
return x * y.expand_as(x)
结论
SE 模块的主要吸引力在于其简单性。仅从图中,我们可以理解挤压-激发过程中涉及的功能和步骤。此外,它们可以添加到模型中,而不会增加太多的计算成本,所以每个人都应该尝试将其集成到他们的深度学习架构中!
参考
[## 压缩和激励网络
卷积神经网络(CNN)的核心构件是卷积算子,它使网络能够…
arxiv.org](https://arxiv.org/abs/1709.01507) [## PyTorch
开源深度学习平台,提供从研究原型到生产部署的无缝路径。
pytorch.org](https://pytorch.org/)
Python 中的统计学简介
数据分析
统计学在数据分析中至关重要。我们将回顾一些关于统计学的基础知识,以及如何在 Python 编程语言中应用它
克里斯·利维拉尼在 Unsplash 上的照片
什么是统计
统计学是一门关于数据收集、组织、分析、解释和展示的学科。在将统计学应用于科学、工业或社会问题时,习惯上是从要研究的统计总体或统计模型开始。
中心趋势:
是概率分布的中心值或典型值。它也可以被称为分布的中心或位置。通俗地说,集中趋势的度量通常被称为平均值。
分散:
是分布被拉伸或压缩的程度。统计离差度量的常见例子有方差、标准差和四分位距。
相关性:
或相关性是两个随机变量或二元数据之间的任何统计关系,无论是否是因果关系。在最广泛的意义上,相关性是任何统计上的关联,尽管它通常指的是一对变量线性相关的程度。
辛普森悖论:
这是概率和统计中的一种现象,在这种现象中,一种趋势出现在几组不同的数据中,但当这些数据组合在一起时就会消失或逆转。
什么是高级数据分析
数据分析解决方案提供了一种利用业务数据的便捷方式。但是,市场上的解决方案数量可能会令人望而生畏,许多解决方案似乎涵盖了不同类别的分析。组织如何理解这一切?首先了解不同类型的分析,包括描述性、诊断性、预测性和规范性分析。
- 描述性分析告诉你过去发生了什么。
- 诊断分析帮助你理解过去为什么会发生一些事情。
- 预测分析预测未来最有可能发生的事情。
- 规范性分析推荐您可以采取的行动来影响这些结果。
Python 中的应用统计方法
想象一下,我们必须对工作中每个成员的朋友数量进行一些数据分析。朋友的数量将在 Python 列表中描述,如下所示:
num_friends = [100, 49, 41, 40, 25, 100, 100, 100, 41, 41, 49, 59, 25, 25, 4, 4, 4, 4, 4, 4, 10, 10, 10, 10,
]
我们将使用 matplotlib 在直方图中显示朋友数量:
看到直方图会是
直方图朋友计数器
中心倾向
- 意思是
我们想知道朋友数量的平均值
def mean(x):
return sum(x) / len(x)
应用此方法将获得的价值为多少朋友喜欢
35.791666666666664
- 中位数
中位数是对集中趋势的简单衡量。为了找到中值,我们按照从最小值到最大值的顺序排列观察值。如果有奇数个观察值,中值是中间值。如果有偶数个观察值,中值是两个中间值的平均值。
运用这种方法会给我们带来结果
25.0
- 分位点
中位数的一个概括是分位数,它表示小于某个数据百分位的值。(中值表示小于 50%数据的值。)
def quantile(x, p):
*"""returns the pth-percentile value in x"""* p_index = int(p * len(x))
return sorted(x)[p_index]
对 num_friends 应用分位数方法,因为百分位是 0.8,将会有结果
59
- 模式(或最常见的值)
将返回 num_friends 的应用模式方法
[4]
结论
学习统计学有助于我们更多地了解数据分析或数据科学的基本概念。还有很多关于统计学的东西,比如假设检验、相关性或估计,我还没有讲过。因此,请随意了解更多关于它们的信息。
参考
《从零开始的数据科学》Joel Grus 著
如今,大多数组织都强调数据驱动业务决策,这是理所当然的。但是数据本身并不是…
www.logianalytics.com](https://www.logianalytics.com/predictive-analytics/comparing-descriptive-predictive-prescriptive-and-diagnostic-analytics/) [## 统计数字
统计学是一门关于收集、组织、分析、解释和展示…
Dispersionen.wikipedia.org](https://en.wikipedia.org/wiki/Statistics)
流式算法简介
用 Python 解释和实现的各种算法
乔恩·弗洛布兰特在 Unsplash 上的照片
在过去,编程意味着永远记住内存限制。虽然在 1990 年拥有 32mb 内存是一笔财富,但现在有时甚至在一台家用电脑上拥有 32gb内存都不够。随着硬盘、RAM 和 GPU 内存的增长,可用的数据量也在增长。因此,拥有一套内存高效算法仍然是很重要的。
两个小例子
一个典型的例子是互联网交换机,它监控不同的 IP 互相发送数据包。交换机的一个常见任务是找出最重要的,即一对两个 IP 地址,与其他对相比,IP₁在这些地址上与 IP₂的通信非常频繁。这很有趣,因为这可能是一个拒绝服务攻击的指示器。
照片由Webaroo.com.au在 Unsplash 上拍摄
这听起来是个简单的问题,对吗?只需实现从 IP 对(IP₁、IP₂)到从 IP₁到 IP₂.的通信数量的映射在 Python 中,这可以是字典、计数器类的实例或邻接矩阵。然后就可以在数据结构中搜索最高的 k 计数,输出对应的 IP 地址。
42.42.42.42 经常满腹狐疑地和 182.162.77.12 交谈,154.78.122.56 也经常满腹狐疑地和 43.111.23.122 交谈。这也许值得研究一下。
但是现在想想这些数据结构的大小。一个更大的交换机可以接收来自数百万个 IP(T21)的请求,并将它们路由到尽可能多的其他 IP。
这意味着我们最终可能会拥有数百万乘以数百万的 IP 对。
这一点,再加上交换机通常较低的存储容量,引发了许多问题。这种方法是不可能的:我们需要一种比存储所有内容占用更少内存的算法。一种方法是使用计数分钟草图。你也可以在 YouTube 上搜索“重量级人物”,如果你感兴趣,可以找到一些很好的解释和例子。
现在我听到你们中的一些人说:
我为什么要关心网络的东西?我是一个机器学习的家伙,咄!—你
嗯,还有一个来自机器学习世界的著名例子:梯度下降!
如果我们处理足够小的数据集,它可以完全适合(GPU) RAM,我们可以使用批量梯度下降,即一次将完整的数据放入内存并进行处理。然而,大多数时候我们的工作记忆太小,使得有必要使用随机梯度下降或小批量梯度下降,它们是所谓的流算法的例子。另一个例子是 Hoeffding 树算法,我在这里描述了。
在本文中,我想向您展示几个流算法的例子,包括您可以使用的 Python 实现!除了让你意识到这个问题,我已经做到了。;)
直觉
对于流算法,我指的是能够处理非常大的,甚至可能是无界的数据集,并且仅使用恒定数量的 RAM 来计算一些期望的输出的算法。
如果数据集是无界的,我们称之为*数据流。*在这种情况下,如果我们在某个位置 n 停止处理数据流,我们期望我们的算法有一个解,对应于到这一点看到的数据。
在下文中,假设我们的硬盘上有一个巨大的数据集,我们想要处理它而不需要立刻将它加载到我们的 RAM 中(因为我们不能),或者有一个输出数据流的源,例如 Twitter 上的传入 tweets。两种情况都以同样的方式处理。
我将用大型数据集的语言来表述即将出现的例子,因为我们知道它们是有限的,我不必一直提到我们停止读取数据流。
我们进一步假设我们可以准确地传递数据一次。
一个平缓的开始
让我们通过两个简单的例子来熟悉如何设计流算法。
寻找最小值
假设有一个非常大的数字列表,对你的内存来说太大了。你想找出这个列表的最小值。在 Python 中,经典的解决方法是这样的:
print(min(my_list))
但这是假设my_list
已经在 RAM 中。那么,我们如何用另一种方式来解决这个问题呢?也许你已经找到了解决方案:
只要一个数字接一个数字地读数据集,只要发现一个更小的数字,就更新最小值。
更准确地说:读取第一个元素,并声明它是最小值。然后读取第二个元素,如果它小于当前最小值(第一个元素),就声明它是最小值。否则,什么都不做。
然后读取第三个元素,如果它小于当前最小值,就声明它是最小值。否则,什么都不做。
然后读取第四个元素,如果它小于当前最小值,则声明它为最小值。否则,什么都不做。
好吧,我停下来,你知道这是怎么回事。这基本上行得通,因为
使用这个公式,你可以很容易地通过归纳证明算法是正确的。读取第一个元素后,结果是正确的,因为 a ₁ < ∞,因此 min(a₁)=a₁.归纳步骤正是公式(想想吧!).但是说够了,让我们回到正轨。
在 Python 中,我们可以使用下面这个非常简单的类来解决它:
from math import inf
class StreamingMinimum:
def __init__(self):
self.result = inf # Immediately replaced by the 1st element
def update(self, element):
self.result = min(self.result, element)
您可以通过以下方式使用它
import numpy as np
stream = iter(np.random.randn(10000)) # Simulate a stream
s = StreamingMinimum()
for element in stream:
s.update(element)
print(s.result)
很简单,对吧?让我们增加一点难度。
寻找平均值
同样的设置:大数据集,但是现在我们要找的是均值而不是最小值。
当然,你知道公式!
所以,我们现在的问题如下:
当我们已经有了前 n 个元素的均值时,如何计算 n+1 个元素的均值?
一个简单的解决方案是使用下面的标识,您可能在稍加思考后就会想到:
我们可以看到,我们不仅必须存储旧的平均值,而且还必须跟踪元素的数量 n,,因为这在公式*中是需要的。*在计算最小值的情况下,这是没有必要的。
这个类是这样的:
class StreamingMean:
def __init__(self):
self.result = 0
self.n = 0
def update(self, element):
self.result = (self.result * self.n + element) / (self.n+1)
self.n += 1
您可以像以前一样再次测试这段代码。用StreamingMean
类代替StreamingMinimum
就行了。
健全性检查:结果大约为 0,这也是我们对标准正态分布随机变量的预期。同样:检查这个算法的正确性是一个简单的归纳练习。
既然你现在知道它是如何工作的,让我们来看一个更有趣的算法。
储层取样
假设您有一个大型数据集,并且想要对一个对象进行统一采样。你怎么能这样做?好吧,如果你知道数据集的大小 n ,你可以在 1 和 n 之间均匀地抽取一个随机数 k ,扫描数据集,取第 k 第个元素。
但是现在想象一下,你有一个数据流,你事先不知道有多少元素进来,也不知道什么时候想停止读取数据流。现在变得越来越困难,因为你不知道从哪个范围抽取随机指数。但是这个问题也有一个简单的解决方法,叫做油藏取样。
这个想法是这样的:你有一个单独的盒子(容器)来存放元素。扫描数据流时,以一定概率用当前元素替换框内内容。
典型的水库。来源:超级马里奥世界
如果我把这个想法留给你一个人,也许过一段时间你就能算出概率。跳过 n 个元素后的目标是能够以 1/ n 个的概率将每个元素放入盒子中。
在最简单的情况下,从某个恒定概率 p 开始。但是,举例来说,第一个元素出现在盒子里的概率是(1- p ) *ⁿ,*对于任何一个 p < 1 来说都是指数级的小,而不是我们所寻找的。
解决这个问题的一个办法是:我们扫描序列的时间越长,就必须降低交换的概率。什么是简单衰变率?1/ n 怎么样?让我们试试。
起初,盒子是空的。我们扫描第一个元素,用第一个元素填充盒子(概率 1/1=1)。到目前为止一切顺利。
现在我们来看第二个元素。我们用 1/2 的概率替换盒子里的内容。这一步之后,第一个元素在盒子里的概率是 1/1 * 1/2 = 1/2,第二个元素在盒子里面的概率是 1/2。完美!我们再做一个。
到达第三个元素,它以 1/3 的概率替换盒子内的元素。第一个元素还在盒子里的概率是多少?就是它熬过了第一个**(好吧,这个很容易)** 第二个第三个互换机会的概率:1/1 * 1/2 * 2/3 = 1/3。看起来不错!第二个元素呢?它必须在第二次和第三次互换机会中存活下来,这种机会发生的概率为 1/2 * 2/3 = 1/3。好像管用!事实上,它确实如此,正如一个人可以用… 归纳看到的那样!
代码如下:
from random import random
class ReservoirSampler:
def __init__(self):
self.result = None
self.n = 0
def update(self, element):
self.n += 1
if random() < 1 / self.n: # Satisfied with prob. 1/n.
self.result = element
我们可以快速检查一下它是否有效。让我们从大小为 20 的数据集重复采样。我们预计在大约 5%的情况下抽取每个元素。
results = []
for _ in range(1000000):
r = ReservoirSampler()
for s in range(20):
r.update(s)
results.append(r.result)
可视化results
给出了以下内容:
我们可以看到,在所有试验中,每种元素都被取样了大约 5%。完美!
结论
我们已经看到,即使在今天,内存高效的算法也是必要的。处理非常大的数据集的聪明技巧仍然是相关的,幸运的是,聪明人已经在这个领域投入了很多努力。
在本文中,我向您展示了三个非常简单的算法示例,它们应该可以教您如何解决内存极度受限的问题。
下一次,如果你的数据再一次不适合你的内存,考虑一下是否有办法以流的方式处理它!
我希望你今天学到了新的、有趣的、有用的东西。感谢阅读!
作为最后一点,如果你
- 想支持我多写点机器学习和
- 无论如何都计划获得一个中等订阅,
为什么不做 通过这个环节 ?这将对我帮助很大!😊
说白了,给你的价格不变,但大约一半的订阅费直接归我。
非常感谢,如果你考虑支持我的话!
如有问题,在LinkedIn上写我!
PyTorch 风格转换简介
神经网络的应用
卷积神经网络的新用途
我电脑旁的艺术。
你知道神经网络不相信左右脑分离吗?
典型的例子:左边的图像不是由人手的笔触创建的,而是由卷积神经网络创建的!
这表明神经网络并不局限于复杂的数学和统计学。稍加帮助,他们甚至可以创造艺术!这篇文章旨在解释风格转移的概念,正如加蒂丝等人在这篇有趣的研究论文中所定义的那样。
什么是风格转移?
风格转移是由 Leon A. Gatys 等人开发的卷积神经网络的一种新颖应用。它允许对图像的“内容”和“风格”进行精确的数学定义。有了内容和风格,我们可以定义一种新的损失函数来描述两个图像之间的风格和内容的差异。然后,通过反向传播,我们可以更新一个图像的像素,以更紧密地匹配另一个图像的样式或内容。
左:梵高的星夜。右图:安德烈亚斯·普雷夫克拍摄
这些图像分别包含文章简介中图像的样式和内容。
对 ConvNets 的一点回顾
卷积神经网络是以这样一种方式独特设计的,即它们擅长识别和分离视觉输入中的模式。
下面是它们工作原理的大概描述:
- 图像被传递到 ConvNet 的第一个卷积层。
- 第一个卷积层让图像通过一组过滤器,这些过滤器检测像垂直线和水平线这样的简单图案。这些图案被提取并作为新的图像通道输出(每个滤镜一个)。
- 然后,这些新的镜像通道被馈送到下一个卷积层,并重复该过程。本质上,网络是在检测模式中的模式!
- 有时,卷积层的输出可能会经过一个池层。池层本质上抛弃了“细节”,但保留了全局模式。这允许一个 ConvNet 将它所知道的推广到以前从未见过的图像。
定义网络
图像风格传输最初在卷积网络上执行,该网络包含由池层分隔的 5 个组中的 16 个卷积层。这个网络叫做 VGG19。每个组在输入中找到模式,然后将这些模式传递到池层,丢弃一些细节,但保留大图。
最左边的黑色方块是原始图像。被网络过滤还原。
我们将使用 PyTorch 预先培训的 VGG 网络开始:
定义图像内容
根据我们对 ConvNets 的了解,它们保持一般模式,但开始丢弃网络深层的细节。记住这一点,就很容易理解为什么图像的“内容”被定义为网络深层的输出。
我们将使用第 10 个卷积层的输出来定义图像的“内容”。我们将这一层命名为 conv4_2 ,因为它是第四叠卷积层中的第二层。
下面是一个从网络中提取所选图层内容的函数。这些层是根据它们在链接的研究论文中的用途选择的。请注意, conv4_2 也在其中。其余的将用于确定风格:
使用 conv4_2 来描述“内容”给了我们想要模拟的一般结构。使用较浅的图层保留更多细节,使用较深的图层丢弃更多细节。您可以根据自己的艺术喜好选择使用不同的图层。
定义风格
如果您知道如何计算样本空间的相关矩阵(通常被视为相关值的热图),那么这将看起来很熟悉,而且相当容易。
回想一下,任何给定卷积层的输出都是一组新的镜像通道,每个通道描述输入中的一些模式或特征。这些通道中的每一个都包含输入图像的过滤版本,该版本突出显示某些特征或图案。然后,我们将风格定义为这些不同特征之间的相关性,并使用 Gramian 矩阵计算相关性。
还记得图像通道只是像素值的 2D 网格。然后,为了确定来自单个卷积层的不同模式信道之间的相关性,我们执行以下操作:
- 对给定图层输出的每个通道进行矢量化。换句话说,分解像素值网格,从每个输出通道创建一个行向量。
- 将所有这些行向量一个接一个地堆叠起来,创建一个二维矩阵。姑且称这个矩阵为 S 。
- 将这个新的 S 矩阵乘以它的转置。这个结果就是格拉米矩阵。
下面是一个小函数,它用两行代码为我们完成了这项工作:
为了更好地感受图像的“风格”,我们为 5 个不同的卷积层分别创建了 5 个独立的格拉米矩阵。
conv1_1
conv2_1
conv3_1
conv4_1
conv5_1
你可能想知道这 5 层是如何被选择来代表输入图像的“风格”的。简而言之,我选择使用与作者相同的层。请随意尝试不同的层!
定义损失
因为我们希望创建一个包含一个父图像的样式和另一个父图像的内容的新图像,所以我们必须定义一个考虑样式和内容的损失函数。
内容损失
内容流失很容易!内容损失被正式定义为两幅图像的内容之间的均方误差。图像的内容被定义为层 conv4_2 的输出。
如果上面的等式令人困惑,那也没关系。使用线性代数可以大大简化这个方程:
回头看看get_features()
的代码片段,您会看到该函数返回了一个字典,其中包含以下各层的所有特征通道:
conv1_1
conv2_1
conv3_1
conv4_1
**conv4_2**
conv5_1
c_features
和t_features
仅仅是get_features()
应用于内容模板图像和目标图像时的输出。因此,将代码与等式匹配,我们看到张量 T 和 C 在代码中定义如下:
T = t_features['conv4_2']
C = c_features['conv4_2']
风格丧失
风格损失在数学上稍微复杂一点,但实现起来很简单。通过首先计算目标图像的格拉米矩阵和样式模板图像的格拉米矩阵的均方误差来找到样式损失:
这里, P 和 G 是目标图像和样式模板图像的格拉米矩阵。上标 l 表示计算 Gramians 的图层输出:
**conv1_1 --> E_1
conv2_1 --> E_2
conv3_1 --> E_3
conv4_1 --> E_4**
conv4_2 --> N/A (used for content loss)
**conv5_1 --> E_5**
同样,这些层输出存储在由get_features()
返回的字典中。
接下来,我们求出每个误差项的加权和:
权重 w 只是用户出于艺术偏好而选择的。每一层的错误都会对最终艺术表现的结果产生不同的影响。
下面是计算风格损失的代码:
全损
总损失是风格和内容损失的线性组合:
其中,α和β是比例因子。α/β的比率将决定新目标图像中的样式/内容比率。在实践中,β会大得多,因为风格误差的尺度小得多。
把所有的放在一起
最后要做的事情是把前馈和反向传播结合起来。这一次,我们不更新网络参数!相反,我们正在更新我们的目标图像的像素值,以便它迭代地处理我们的样式图像的样式和我们的内容图像的内容:
不要害怕使用代码中的参数来实现您想要的艺术风格。这需要一些练习,并不是每一组图像都像你期望的那样。好好享受吧!
如果你想看我的关于风格转移的完整 Jupyter 笔记本(包括我遗漏的一些助手功能),你可以在这里找到它。
现在,我将留给你这张结合了抽象艺术和积云的航拍照片:)
生存分析导论
来源: pixabay
了解生存分析的基本概念,以及可以用来做什么任务!
在这个竞争异常激烈的时代,所有企业都面临着客户流失/保留的问题。快速给出一些背景,当客户停止使用公司的服务(停止购买、取消订阅等)时,就会发生流失。).保留是指保持企业的客户活跃(活跃的定义在很大程度上取决于商业模式)。
直觉上,公司希望通过防止流失来增加保留率。通过这种方式,他们与客户的关系更长久,因此潜在的利润也更高。此外,在大多数情况下,公司留住一个客户的成本远低于获得一个新客户的成本,例如,通过绩效营销。对于企业来说,保留的概念与客户终身价值 (CLV)密切相关,企业希望最大化客户终身价值。但是这是另一篇文章的主题。
通过这篇文章,我想开始一个关注生存分析的简短系列,生存分析通常是统计学习中一个被低估但非常有趣的分支。在这篇文章中,我提供了生存分析及其构建模块的一般介绍。首先,我解释了所需的概念,然后描述了分析事件时间数据的不同方法。开始吧!
生存分析简介
生存分析是一个统计学领域,专注于分析某一事件发生前的预期时间。最初,这一统计学分支是围绕在临床试验中测量药物治疗对患者存活率的影响而发展起来的。例如,想象一组癌症患者接受某种新的治疗。生存分析可用于根据患者的预期寿命分析该治疗的结果。
然而,生存分析并不局限于调查死亡,也可以用于确定机器故障前的时间,或者——乍听起来可能有点违反直觉——某个平台的用户转向高级服务。这是可能的,因为生存分析关注的是事件发生前的时间,而不是将事件定义为负面事件。适用于最流行的生存分析方法的条件是:
- 感兴趣的事件被清楚地定义和很好地指定,因此对于它是否发生没有模糊性,
- 对于每个受试者,该事件只能发生一次——这在死亡的情况下很明显,但如果我们将分析应用于流失,这可能是一个更复杂的情况,因为流失的用户可能会被重新激活并再次流失。
我们已经确定,生存分析用于建模时间到事件序列,换句话说,就是生存期(因此也是 Python 库的名称,它是这种分析的首选工具)。一般来说,我们可以用生存分析来尝试回答这样的问题:
- 有百分之几的人会活过某个特定的时间?
- 幸存者的死亡率/失败率是多少?
- 特定特征(例如,年龄、性别、地理位置等特征)是如何实现的。)影响生存概率?
简要描述了生存分析的一般思想后,是时候介绍一些对彻底理解这个主题至关重要的概念了。
审查
删失可以描述为生存分析领域中的缺失数据问题。当关于存活时间的信息不完整时,观察结果被删截。有不同种类的审查,例如:
- 右审查,
- 区间审查,
- 左侧审查。
为了使本节简短,我们只讨论最常遇到的一个— 右删截。让我们回到癌症治疗的例子。想象一下,对新药效果的研究持续了 5 年(这是一个任意的数字,实际上没有任何依据)。可能发生的情况是,5 年后,一些患者存活下来,因此没有经历死亡事件。与此同时,该研究的作者与一些患者失去了联系——他们可能已经搬到了另一个国家,他们可能真的已经死亡,但从未得到证实。这些病例受到右删截的影响,即他们的真实存活时间等于或大于观察到的存活时间(在这种情况下,研究的 5 年)。下图说明了右删截。
删失的存在也是我们在生存分析中不能用简单 OLS 解决问题的原因。这是因为 OLS 有效地绘制了一条最小化误差平方和的回归线。但是对于删失数据,误差项是未知的,因此我们不能最小化 MSE。应用一些简单的解决方案,如使用审查日期作为死亡事件的日期,或删除审查的观察结果,可能会严重影响结果。
有关不同类型审查的信息,请点击此处。
生存函数
生存函数是时间的函数( t ,可以表示为
其中 Pr() 代表概率,而 T 代表从样本中随机观察到的感兴趣事件的时间。我们可以将生存函数解释为感兴趣的事件(例如,死亡事件)在时间 t. 之前不发生的概率
生存函数的取值范围在 0 和 1(包括 0 和 1)之间,并且是 t. 的非递增函数
危险函数
我们可以将风险函数(或风险率)视为受试者在一小段(或更准确地说,无限短)时间间隔内经历感兴趣事件的概率,假设受试者一直存活到所述间隔开始。危险函数可以表示为:
其中,分子中的表达式是在给定时间间隔内感兴趣的事件发生的条件概率,前提是该事件以前没有发生过。 dt 中的分母是所考虑的时间间隔的宽度。当我们将前者除以后者时,我们有效地获得了事件在单位时间内的发生率。最后,当区间宽度变为零时,取极限值,我们最终得到瞬时发生率,即某一事件在特定时间点发生的风险。
你可能想知道为什么用这么短的时间间隔来定义风险率。其原因在于,连续随机变量等于特定值的概率为零。这就是为什么我们需要考虑事件在很短的时间间隔内发生的概率。
技术说明:为了在理论上正确,重要的是要提到危险函数实际上不是一个概率,名称危险率是更合适的名称。这是因为尽管分子中的表达式是概率,但分母中的 dt 实际上可以导致大于 1 的危险率值(在较低的区间中仍然限制为 0)。
最后,生存函数和危险函数相互关联,如下式所示:
给这个等式一点上下文,括号中的积分称为累积风险,可以解释为从时间点 0 到 t 受试者面临的风险的总和。
不同的生存分析方法
由于生存分析是一个不同的统计方法的整个领域,用于处理时间到事件序列,自然有许多不同的方法可以遵循。在高层次上,我们可以将他们分为三大类:
- 非参数化 —通过这些方法,我们对数据的基本分布不做任何假设。也许这一组中最受欢迎的例子是卡普兰-迈耶曲线,简而言之,这是一种估计和绘制作为时间函数的生存概率的方法。
- 半参数——正如你可能已经猜到的,这个群体介于两个极端之间,很少做出假设。最重要的是,没有关于风险函数/风险率形状的假设。这一组中最受欢迎的方法是 Cox 回归,我们可以用它来确定风险函数和一组解释变量(预测值)之间的关系。
- 参数化 —你可能在做研究时遇到过这种方法。这个想法是使用一些统计分布(一些流行的包括指数分布、对数分布、威布尔分布或洛马克斯分布)来估计一个受试者将存活多久。通常,我们使用最大似然估计(MLE)来拟合数据的分布(或者实际上是分布的参数),以获得最佳性能。
这个简短列表中提到的方法绝不是详尽的,还有许多更有趣的方法来使用基于机器或深度学习的技术分析时间到事件的数据。我会尽量在下面的帖子中涵盖最有趣的内容,敬请关注:)
结论
在这篇文章中,我试图对生存分析领域提供一个简短而全面的介绍。我认为,在谈论不同的数据科学解决方案时,这一领域经常被忽略。然而,通过使用一些简单的(或者根本不那么简单!)解决方案我们可以为公司或利益相关者提供有价值的见解,并产生实际的增值。
这篇文章只是一个简短系列的开始,我将在下面继续添加以下部分。如果你有问题或建议,请在评论中告诉我,或者在 Twitter 上联系我。
同时,你可能会喜欢我的其他一些文章:
了解用于生存分析的最流行的技术之一,以及如何用 Python 实现它!
towardsdatascience.com](/introduction-to-survival-analysis-the-kaplan-meier-estimator-94ec5812a97a) [## 用 Tableau 提升你的 Kaplan-Meier 曲线
方便访问整个公司的生存分析!
towardsdatascience.com](/level-up-your-kaplan-meier-curves-with-tableau-bc4a10ec6a15) [## 生存分析导论:尼尔森-艾伦估计量
了解如何使用非参数方法来估计累积风险函数!
towardsdatascience.com](/introduction-to-survival-analysis-the-nelson-aalen-estimator-9780c63d549d)
生存分析导论:卡普兰-迈耶估计量
托拜厄斯·图利乌斯在 Unsplash 拍摄的照片
了解用于生存分析的最流行的技术之一,以及如何用 Python 实现它!
在我的上一篇文章中,我描述了生存分析的潜在用例,并介绍了理解用于分析事件时间数据的技术所需的所有构件。
我继续这个系列,解释可能是最简单,但非常有见地的生存分析方法——卡普兰-迈耶估计量。在理论介绍之后,我将向您展示如何使用流行的lifelines
库在 Python 中进行分析。
1.卡普兰-迈耶估计量
Kaplan-Meier 估计量(也称为乘积极限估计量,稍后您会看到原因 ) 是一种将生存概率作为时间函数进行估计和绘图的非参数技术。这通常是进行生存分析的第一步,因为这是最简单的方法,需要的假设最少。为了使用卡普兰-迈耶方法进行分析,我们假设如下:
- 感兴趣的事件是明确的,并且在明确指定的时间发生。
- 所有观察的存活概率是相同的,它们何时进入研究并不重要。
- 删截的观测值与继续被跟踪的观测值具有相同的生存前景。
在现实生活中,我们永远不知道真实的生存函数。这就是为什么使用 Kaplan-Meier 估计量,我们从收集的数据中逼近真实的生存函数。估计值被定义为在相同情况下存活了一定时间的观察值的分数,由以下公式给出:
其中:
- t_i 是至少一个事件发生的时间,
- d_i 是在时间 t_i 发生的事件数,
- n_i 代表已知存活到时间 t_i 的个体数量(他们尚未发生死亡事件或已被审查)。或者换句话说,在 t_i 时刻有风险的观测值的数量。
从公式中的乘积符号,我们可以看到与该方法的另一个名称,乘积极限估计量的联系。在时间 t 的生存概率等于在时间 t 的生存机会百分比与之前各时间的乘积。
我们最常与这种生存分析方法联系在一起的,以及我们通常在实践中看到的是卡普兰-迈耶曲线——卡普兰-迈耶估计量随时间变化的曲线。我们可以使用这些曲线作为探索工具——比较队列、接受或不接受某种治疗的群体、行为集群等之间的生存函数。
生存线实际上是一系列递减的水平台阶,在给定足够大的样本量的情况下,这些台阶接近人口真实生存函数的形状。在实践中,该图通常伴随着置信区间,以显示我们对点估计的不确定性——宽置信区间表明高度不确定性,可能是因为研究只包含少数参与者——这是由观测值死亡和被删截引起的。有关使用格林伍德方法计算置信区间的更多详细信息,请参见[2]。
图片由作者提供
存活曲线的解释非常简单,y 轴代表受试者存活到时间 t 后仍未经历感兴趣事件的概率,x 轴代表该概率。生存函数(由 Kaplan-Meier 估计器近似)的每次下降都是由至少一次观测中发生的感兴趣的事件引起的。
垂直线的实际长度代表在时间 t 经历该事件的处于风险中的观察的部分。这意味着,在两个不同的时间经历该事件的单个观察(实际上不是同一个,而是简单的单个)可以导致差异大小的下降,这取决于处于风险中的观察的数量。这样,下落的高度也可以告诉我们有风险的观察次数(即使没有报告和/或没有置信区间)。
当没有观察经历感兴趣的事件或一些观察被删截时,存活曲线没有下降。
2.对数秩检验
我们已经学会了如何使用卡普兰-迈耶估计量来逼近一个群体的真实生存函数。我们知道我们可以绘制多条曲线来比较它们的形状,例如,通过我们的移动应用程序的用户使用的操作系统。然而,我们仍然没有一个工具,将真正允许比较。嗯,至少比目测曲线更严谨。
这时对数秩检验开始发挥作用。这是一个统计测试,比较两组(或更多,请参见 Python 实现)之间的存活概率。检验的零假设表明所考虑的组之间的生存功能没有差异。
对数秩检验使用与 Kaplan-Meier 估计量相同的假设。此外,还有比例风险假设——风险比(请参见上一篇文章中关于风险率的提示)在整个研究期间应保持不变。在实践中,这意味着如果生存曲线交叉,对数秩检验可能不是一个合适的检验。但是,这仍然是一个争论激烈的话题,请看[4]和[5]。
为了简洁起见,我们不涉及测试背后的数学。如果你有兴趣,请看这篇文章或【3】。
3.Kaplan-Meier 的常见错误
在这一部分,我想提一下在使用 Kaplan-Meier 估计器时可能会出现的一些常见错误。
删除审查的数据
删除删失数据可能很诱人,因为它会显著改变卡普兰-迈耶曲线的形状,但是,这可能会导致严重的偏差,因此我们在拟合模型时应该始终包括它。
解释曲线的端点
在解释存活曲线的终点时要特别注意,因为接近研究终点的任何大的下降都只能通过到达该时间点的少数观察来解释(这也应该通过更宽的置信区间来指示)
二分法连续变量
通过二分法,我的意思是使用中间值或“最佳”分界点来创建任何连续指标的“低”和“高”分组。这种方法会产生多种问题:
- 寻找“最佳”分界点可能非常依赖于数据集,并且不可能在不同的研究中重复。此外,通过进行多重比较,我们冒着增加假阳性几率的风险(在生存函数中发现差异,而实际上没有差异)。
- 二分法通过强制所有测量值为二进制值来降低统计检验的能力,这反过来会导致需要更大的样本量来检测效果。还值得一提的是,在生存分析中,所需的样本量是指对感兴趣事件的观察次数。
- 当进行二分法时,我们对观察值之间的风险分布做出了错误的假设。让我们假设我们用 50 岁作为年轻患者和老年患者的分界线。如果我们这样做,我们假设一个 18 岁的人和一个 49 岁的人在同一个风险组,但在大多数情况下这是不正确的。
仅考虑一个预测因素
Kaplan-Meier 估计量是一种单变量方法,因为它最多使用一个变量/预测值来逼近生存函数。因此,结果很容易出现偏差,要么夸大了信号,要么遗漏了信号。这是由所谓的遗漏变量偏差引起的,这导致分析假设多个预测因子的潜在影响应仅归因于我们考虑的单个预测因子。因此,应该使用多变量方法,如 Cox 回归。
4.Python 中的示例
是时候把我们在实践中学到的东西付诸实施了。我们从导入所有需要的库开始。
然后,我们加载数据集,并做一些小的调整,使其与lifelines
库配合良好。为了进行分析,我们使用流行的电信客户流失数据集(此处或我的 GitHub 上的可用)。该数据集包含电话/互联网提供商的客户信息,包括他们的任期、他们使用哪种服务、一些人口统计数据以及最终指示流失的标志。
对于此分析,我们使用以下各列:
tenure
—客户在公司工作的月数,churn
—客户是否翻炒的信息(二进制编码:如果事件发生则为 1,否则为 0)。PaymentMethod
—客户使用了哪种付款方式。
对于最基本的场景,我们实际上只需要事件发生时间和指示感兴趣的事件是否发生的标志。
KaplanMeierFitter
的工作方式类似于scikit-learn
中已知的类:我们首先实例化类的对象,然后使用fit
方法使模型适合我们的数据。在绘图时,我们指定at_risk_counts=True
来额外显示关于在特定时间点有风险的观察数量的信息。
通常,我们会对中位存活时间感兴趣,即平均 50%的人口已经死亡的时间点,或者在这种情况下,被搅动。我们可以使用下面一行来访问它:
kmf.median_survival_time_
然而,在这种情况下,该命令返回inf
,因为我们可以从生存曲线中看到,我们实际上没有观察到数据中的那个点。
我们已经看到了基本的用例,现在让我们把分析复杂化,并为支付方式的每个变体绘制生存曲线。我们可以通过运行以下代码来做到这一点:
运行代码块会生成以下图形:
我们可以看到,电子支票的生存概率肯定是最低的,而自动银行转账/信用卡的曲线非常相似。这是使用对数秩检验来检验它们是否真的不同的绝佳时机。
下表列出了结果。
通过查看 p 值 0.35,我们可以看到没有理由拒绝零假设,即生存函数是相同的。在这个例子中,我们只比较了两种付款方式。然而,我们肯定可以测试更多的组合。有一个很好用的函数叫做pairwise_logrank_test
,可以让对比变得非常容易。
在表格中,我们可以看到之前的对比,以及所有其他组合。银行转账和信用卡是我们不应该拒绝零假设的唯一案例。此外,我们应该谨慎解释对数秩检验的结果,因为我们可以在上面的图中看到,银行转账和信用卡支付的曲线实际上是交叉的,因此违反了比例风险的假设。
使用生命线库,我们还可以很容易地测试另外两个东西。第一个是多变量对数秩检验,其中零假设表明所有组具有相同的“死亡”产生过程,因此它们的生存曲线是相同的。
测试的结果表明,我们应该拒绝零假设,所以生存曲线是不相同的,这一点我们已经在图中看到了。
最后,我们可以测试特定时间点的存活率差异。回到示例,在图中,我们可以看到曲线在 t = 60 左右相距最远。让我们看看这种差异是否有统计学意义。
通过查看测试的 p 值,没有理由拒绝零假设,即在该时间点的存活率没有差异。
5.结论
在这篇文章中,我描述了一个非常流行的进行生存分析的工具——Kaplan-Meier 估计量。我们还介绍了比较两个/多个生存函数的对数秩检验。所描述的方法是一个非常受欢迎的方法,但是,也不是没有缺陷。在结束之前,让我们看看 Kaplan-Meier 估计量/曲线的优缺点。
优势:
- 给出总体的平均视图,也是按组。
- 不需要很多功能—只需要关于事件发生时间和事件是否实际发生的信息。此外,我们可以使用任何描述组的分类特征。
- 自动处理阶级不平衡,因为几乎任何比例的死亡审查事件是可以接受的。
- 由于它是一种非参数方法,所以很少对数据的基本分布进行假设。
劣势:
- 我们无法评估预测因子对生存概率的影响程度。
- 我们无法同时考虑多个观察因素,例如,原产国和手机操作系统。
- 删截和存活之间的独立性假设(在时间 *t,*删截的观察结果应该与未删截的观察结果具有相同的预后)可能不适用/不现实。
- 当底层数据分布(在某种程度上)已知时,该方法不如一些竞争技术准确。
总之,即使有一些缺点,Kaplan-Meier 生存曲线也是进行生存分析的一个很好的起点。在这样做的同时,我们可以获得关于生存的潜在预测因素的有价值的见解,并通过一些更先进的技术加速我们的进展(我将在未来的文章中描述)。
你可以在我的 GitHub 上找到本文使用的代码。一如既往,我们欢迎任何建设性的反馈。你可以在推特或评论中联系我。
如果您对这篇文章感兴趣,您可能也会喜欢本系列中的其他文章:
了解生存分析的基本概念,以及可以用来做什么任务!
towardsdatascience.com](/introduction-to-survival-analysis-6f7e19c31d96) [## 用 Tableau 提升你的 Kaplan-Meier 曲线
方便访问整个公司的生存分析!
towardsdatascience.com](/level-up-your-kaplan-meier-curves-with-tableau-bc4a10ec6a15) [## 生存分析导论:尼尔森-艾伦估计量
了解如何使用非参数方法来估计累积风险函数!
towardsdatascience.com](/introduction-to-survival-analysis-the-nelson-aalen-estimator-9780c63d549d)
6.参考
[1]卡普兰,E. L .,&迈耶,P. (1958)。不完全观测值的非参数估计。美国统计协会杂志, 53 (282),457–481。—此处可用
[2] S .索耶(2003 年)。生存分析中的格林伍德和指数格林伍德置信区间—此处可用
[3] Kaplan-Meier 生存曲线和对数秩检验—此处可用
[4]非比例风险——那又怎样?—此处可用
[5]g .布略蒂斯和 l .比林汉姆(2011 年)。穿越生存曲线:对数秩检验的替代方法。选拔赛, 12 (S1),A137。