《纽约客》漫画的图像到文本生成
我如何使用深度学习来创建《纽约客》的标题
从来没有一台电脑赢得过《纽约客》漫画字幕大赛的冠军,理由很充分:这很难。《纽约客》杂志每周举办一次竞赛,让读者为一幅无字幕的漫画提交字幕,如下图所示。获奖说明刊登在杂志上。
上一届《纽约客》字幕大赛的图片示例。图片来自 NextML 。 CC 乘 4.0 。
人们已经创建了纽约客字幕生成器,但没有一个真正使用漫画的图像来生成字幕。相反,他们使用以前的标题来预测新的标题会是什么样子。
直接从卡通形象转到标题很难,原因如下:
- 卡通通常描绘现实生活中从未发生的情景和图像。(一条坐在酒吧的鱼?)
- 即使我们有一个模型可以识别漫画中的主题,也很难将这种理解转化为诙谐的标题。
我将这项任务作为图像到文本算法的学习练习,经过大量的反复试验,我创建了一个图像到文本的模型,仅基于图像就可以为《纽约客》漫画制作像样的字幕。我是这样做的。
由模型产生的样本标题。图片来自 NextML 。 CC 乘 4.0 。
数据
第一步是获取训练数据。NEXTml 有一个 Github 知识库,在那里你可以访问来自 186 场比赛的原始卡通图像和候选人说明。每场比赛平均有大约 5000 个候选字幕。在下载了这个数据集之后,我将它分成了分别包含 127 个和 59 个图像的训练和测试数据集。
型号
我从 sgrvinod 的教程中的深度图像到文本模型开始。给定一幅图像,该模型通过将图像编码到描述图像中内容的向量空间中来工作。然后,该模型获取编码图像,并将其翻译成文字(“解码”)。在将编码图像翻译成文字时,注意力层会告诉模型应该关注图像的哪一部分。注意力层特别酷,因为它可以让你看到模型在选择每个单词时的焦点在哪里。
该模型集中在滑雪板上,以确定“一个人骑着滑雪板”,并围绕滑雪板确定“雪覆盖的斜坡”图片来自 COCO2014 。 CC BY-SA 2.0 。
我在大约 16 万张图片和说明的 COCO2014 数据集上训练了一个模型(“COCO 模型”)。然而,如下图所示,COCO2014 的图片和说明与我们想为《纽约客》制作的图片和说明大相径庭。这些图像是真实生活中的照片,描述非常简单。看不到喜剧。下一步是弄清楚:我们如何把这个预先训练好的模型改编成《纽约客》的字幕?这是最困难的部分。
来自 COCO2014 数据集的样本图像和标题。 CC BY-SA 2.0 。
文本预处理
对上述问题的回答最终是“非常仔细的预处理”
词汇扩展
我需要做的第一件事是扩展 COCO 模型的词汇。《纽约客》漫画中包含的“雪人”或“穴居人”等概念从未出现在 COCO2014 数据集中。为了解决这个问题,我将《纽约客》数据集中的单词添加到 COCO 模型的词汇表中,并重新训练了 COCO 模型。这使词汇量从 9490 个单词增加到 11865 个单词。
字幕过滤
在《纽约客》的数据集中,一幅漫画的候选标题彼此大相径庭。有的提到了图像中的物体,有的没有。字幕也以不同的单词开始,并且具有不同的语法结构:
“你什么意思?你只在星期五供应鱼”
“每个学校都拒绝了我”
“我知道什么东西被掺水了,伙计。”
为了使模型更容易解决图像到文本的问题,我用两种方法过滤了候选标题:
- 结构:标题必须以代词开头(如“我”、“你”、“他们”)。这是为了使标题结构更容易预测。
- 内容:标题必须引用图像中的对象。这有两个原因。首先,由于我们直接从图像到标题,我想强制模型使用图像中的对象来获得标题。第二是关键词(例如“鱼”)出现在标题中,使模型更容易学习。为了弄清楚图像中有哪些物体,我使用了一种粗略的启发式方法,从漫画的描述中提取名词,并迫使标题至少引用一两个名词。
这些过滤器将每部漫画的平均标题数量从 5000 个减少到 10 个,但这仍然是足够训练模型的数据[3]。
迁移学习
清理完数据后,我继续训练 COCO 模型,给它提供来自《纽约客》数据集的图片和说明。额外的训练意味着模型可以对其参数进行小的调整以适应新的任务。这种技术通常被称为迁移学习。值得注意的是,我将学习率保持在较高的水平(本例中为 1e-4),以迫使模型学习一种新的“有趣”的句法结构,并识别黑白图像中的对象。
结果
这里有一些由测试集上的模型生成的示例字幕。
模型生成的示例标题。来自 NextML 的图片。 CC 乘 4.0 。
许多字幕有 70%在那里,只需要一些调整就可以使它们配得上字幕(例如,中间底部的照片:“对不起,这是一个社交派对”→“对不起,这是一个数字派对”或“反社交派对”)。话虽如此,我对迁移学习如何让模型识别卡通黑白图像中的主题印象深刻(例如,洞穴画中的“洞穴”,走进厨房的人的“厨师”)。也许再做一些调整,我们就能拥有一个有竞争力的人工智能卡通字幕了。
这是这位模特提交给本周《纽约客》字幕竞赛(竞赛#749)的作品。
模特参加《纽约客》字幕竞赛的作品。
结束语
有很多方法可以改进这个模型,但希望这里的技术和知识可以让你在创建自己的有趣人工智能方面领先一步。我将会更多地使用这个模型,并且我很乐意听到你关于我们如何改进它的任何想法!大部分建模代码来自于 sgrvinod 的教程,你可以在这个 Github repo 中找到我用来预处理纽约客字幕的代码。
笔记
[1]更准确地说,以前的尝试在以前的字幕上训练语言模型来生成新的字幕。比如说马尔可夫模型。一些简单的例子包括:
[2]技术说明:我根据漫画是否带有图像内容的文本描述来分割数据,我帮助使用这些数据来过滤用于训练的标题。如果这幅漫画没有文字描述,我就把它放在测试集中。此外,GitHub 的 readme 声明有 155 个图像,但在删除重复项后,数据库实际上有 184 个。
[3]在这种情况下,图像到文本模型的主要数据约束是字幕图像的数量,而不是每个图像的字幕数量。相比之下,COCO2014 每张图片有 5 个标题。
基于神经风格转移和张量流的图像生成
使用机器学习算法创建独特的图像
3 分钟机器学习
3 分钟机器学习是一系列与机器学习世界相关的教程和内容丰富的视频。你可以在 YouTube 上找到完整的视频。库包含视频中显示的所有文件。这个系列是在实验阶段,完全免费开发,为了娱乐和文化!欢迎任何反馈。
为什么要看这篇文章?
本文将介绍如何通过开发一种有趣的机器学习算法来创建独特和原始的图像:神经风格转移。我们将看到如何上传已经训练好的神经网络模型来加快过程,以及如何上传自己的照片来测试算法并生成自己的图像。
下面是我们最终结果的预览:
神经风格转移应用程序—图片由作者提供
神经类型转移
神经类型转移是一种优化技术。它的实现包括使用两个图像:一个表示主要内容(内容图像),另一个表示代表我们希望获得的图像的样式(样式引用图像)。该算法混合这些图像,获取内容图像并对其应用样式参考图像。这是通过优化最终图像以匹配内容图像的内容统计和样式参考图像的样式统计来完成的。该算法的实现涉及卷积网络的使用。
神经类型转移工作原理—学分:篇
更多信息请参考 Tensorflow 原创文章或者这个有趣的教程。
代码
代码需要导入几个库和定义一些环境变量。 Matplotlib 用于图像绘制, Tensorflow 和 Keras 用于代理模型管理和图像预处理, os 系统库用于定义一些系统变量, NumPy 作为数值分析的基本工具。我们直接从 Google Colab 导入一些工具,方便直接从你的 PC 导入图片。
剩下的工作就是导入我们想要执行分析的图像。请记住,第一个将是基础图像,它将为最终结果分配主题(内容图像)。第二个将分配样式(样式参考图像)。这段代码非常有用,因为它允许我们用几行代码直接从本地存储空间上传图像文件,同时在代码共享的情况下创建与用户的交互界面(如本例所示)。我们注意到,正如所写的,这段代码允许同时导入几个图像,但是在这种情况下,我们只需要两个图像:只有最后一个将被存储。图像会自动上传并保存在您的工作区中。
让我们定义一些有用的函数:
tensor_to_image 允许将 TensorFlow 张量转换为图像文件,并以此表示。 load_img 从所需路径加载图像。 imshow 允许我们可视化一个图像,注意输入文件的大小。
让我们展示这些图像:
让我们创建我们的 神经风格转移模型 :
要开发 Tensorflow Hub 的潜力,只需:
- 从 Tensorflow Hub 加载预训练模型
- 生成新的风格化图像,将我们的两个图像作为常量张量流张量传递给模型
- 将新图像转换为图像
- 让我们来想象一下结果吧!
神经风格转移应用程序—图片由作者提供
让我们添加一些 Python 命令来自动命名和下载新图像:
结论
我希望你喜欢这篇文章的内容。我提醒你,你可以在这里找到源代码,在 GitHub 和 YouTube 上可以找到完整的库。
下次见,
马可
Python 中的不平衡分类:SMOTE-ENN 方法
奥马尔·弗洛雷斯在 Unsplash 上拍摄的照片
使用 Python 将 SMOTE 与编辑过的最近邻(ENN)相结合以平衡数据集
动机
在分类建模中,有许多方法可以通过过采样少数类或欠采样多数类来克服不平衡数据集。为了进一步提高模型性能,许多研究人员建议结合过采样和欠采样方法来更好地平衡数据集。
在我的上一篇文章中,我已经解释过采样和欠采样相结合的方法之一,称为 SMOTE-Tomek 链接方法。这一次,我将通过结合 SMOTE 和编辑最近邻(ENN)方法(简称 SMOTE-ENN)及其使用 Python 的实现来解释另一种变体。
概念:K-最近邻(KNN)
KNN 的思想是假设基于距离的每个数据的最近邻具有相似的类。当数据集中的新观测值存在时,KNN 将搜索其 K-最近邻,以确定新观测值将属于的类别。许多距离度量可用于计算 KNN 的每个观察距离,但最常用的是使用欧几里德距离。
例如,假设数据集由两个类组成,黑色和白色。现在,假设有一个未知类的新观察。通过使用 KNN,如果大部分新观测值的 K-最近邻属于黑色类,那么新观测值将属于该黑色类,反之亦然。
给定由 N 个观测值组成的数据集,KNN 的算法可以解释如下。
- 将 K 确定为最近邻居的数量。
- 对于数据集中的每个观测值,计算每个观测值之间的距离,然后将距离和观测值添加到有序集。
- 根据距离按升序对距离和观测值的有序集合进行排序。
- 从排序的有序集合中挑选前 K 个条目。换句话说,选择每个观察值的 K 个最近邻。
- 从选定的 K 个条目中返回多数类。
概念:编辑过的最近邻(ENN)
由 Wilson (1972)开发的 ENN 方法的工作原理是首先找到每个观测值的 K-最近邻,然后检查来自观测值的 K-最近邻的多数类是否与观测值的类相同。如果观测值的 K-最近邻的多数类和观测值的类不同,则从数据集中删除观测值及其 K-最近邻。默认情况下,ENN 使用的最近邻数为 K=3。
ENN 的算法可以解释如下。
- 给定具有 N 个观测值的数据集,将 K 确定为最近邻的数量。如果不确定,那么 K=3。
- 在数据集中的其他观测值中查找该观测值的 K-最近邻,然后从 K-最近邻返回多数类。
- 如果观测值的类和观测值的 K-最近邻中的多数类不同,则从数据集中删除观测值及其 K-最近邻。
- 重复第 2 步和第 3 步,直到达到每个类别的期望比例。
此方法比托梅克链接更有效,在托梅克链接中,当观测值的类与观测值的 K-最近邻的多数类不同时,ENN 会移除观测值及其 K-最近邻,而不是仅移除具有不同类的观测值及其 1-最近邻。因此,可以预期 ENN 会比 Tomek 链接提供更深入的数据清理。
斯莫特-ENN 方法
该方法由巴蒂斯塔等人 (2004)开发,结合了 SMOTE 为少数类生成合成样本的能力和 ENN 从两个类中删除一些观察值的能力,这些观察值被识别为在观察值的类和其 K-最近邻多数类之间具有不同的类。SMOTE-ENN 的过程可以解释如下。
- (开始 SMOTE )从少数类中选择随机数据。
- 计算随机数据与其 k 个最近邻之间的距离。
- 将差值乘以 0 到 1 之间的随机数,然后将结果添加到少数类作为合成样本。
- 重复步骤 2-3,直到达到所需的少数族裔比例。(击打结束)
- (ENN 的开始)确定 K,作为最近邻居的数量。如果不确定,那么 K=3。
- 在数据集中的其他观测值中查找该观测值的 K-最近邻,然后从 K-最近邻返回多数类。
- 如果观测值的类和观测值的 K-最近邻中的多数类不同,则从数据集中删除观测值及其 K-最近邻。
- 重复第 2 步和第 3 步,直到达到每个类别的期望比例。(ENN 结束
为了在实践中更好地理解这种方法,这里我将使用imbalanced-learn
库给出 SMOTE-ENN 在 Python 中的一些实现。对于本文,我将通过使用AdaBoostClassifier
使用 AdaBoost 分类器。为了评估我们的模型,这里我将使用重复分层交叉验证方法。
履行
为了实现,这里我使用来自 Kaggle 的 Pima Indians 糖尿病数据库。这个数据集中的文件名是diabetes.csv
。
皮马印第安人糖尿病数据库(图片取自 Kaggle
首先,我们需要导入我们需要的数据和库,如下所示。
让我们来看看数据描述,并检查数据集中是否有任何缺失值,如下所示。
> data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 768 entries, 0 to 767
Data columns (total 9 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Pregnancies 768 non-null int64
1 Glucose 768 non-null int64
2 BloodPressure 768 non-null int64
3 SkinThickness 768 non-null int64
4 Insulin 768 non-null int64
5 BMI 768 non-null float64
6 DiabetesPedigreeFunction 768 non-null float64
7 Age 768 non-null int64
8 Outcome 768 non-null int64
dtypes: float64(2), int64(7)
memory usage: 54.1 KB> data.isnull().sum()
Pregnancies 0
Glucose 0
BloodPressure 0
SkinThickness 0
Insulin 0
BMI 0
DiabetesPedigreeFunction 0
Age 0
Outcome 0
dtype: int64
我们可以看到数据集中没有丢失的值,所以我们可以跳到下一步,我们需要通过编写如下代码行来计算属于Outcome
变量中每个类的数据的数量。
> data['Outcome'].value_counts()
0 500
1 268
数据非常不平衡,多数阶级属于“0”(我们称之为负)标签,少数阶级属于“1”(我们称之为正)标签。接下来,我们通过编写如下代码将数据分为特性和目标。
Y=data['Outcome'].values #Target
X=data.drop('Outcome',axis=1) #Features
预处理完成。现在,让我们跳到建模过程。为了给你一些性能比较,这里我创建了两个模型,其中第一个没有使用任何不平衡数据处理,而另一个使用 SMOTE-ENN 方法来平衡数据。
在不使用 SMOTE-ENN 平衡数据的情况下,产生的模型性能如下。
Mean Accuracy: 0.7535
Mean Precision: 0.7346
Mean Recall: 0.7122
我们可以看到,准确率分数相当高,但召回分数略低(大约 0.7122)。这意味着正确预测少数类标签的模型性能不够好。
让我们使用 SMOTE-ENN 来平衡我们的数据集,看看有什么不同。注意,我在 **EditedNearestNeighbours**
中使用的 **sampling_strategy**
是 **'all'**
,因为 ENN 的目的是从两个类中删除一些观察值,这些观察值被识别为在观察值的类与其 K 近邻多数类之间具有不同的类。
Mean Accuracy: 0.7257
Mean Precision: 0.7188
Mean Recall: 0.7354
我们可以看到,召回分数增加,虽然准确性和精确度分数略有下降。这意味着通过使用 SMOTE-ENN 平衡数据,正确预测少数类标签的模型性能正在变得更好。
结论
我们到了。现在,您已经了解了如何使用 SMOTE-ENN 方法来平衡分类建模中使用的数据集,从而提高模型性能。像往常一样,如果您有任何问题,请随时提问和/或讨论!
我的下一篇文章再见!一如既往,保持健康,保持安全!
作者的联系人
中:【https://medium.com/@radenaurelius】T2
参考
[1]舒拉、鲍耶、霍尔和凯格尔迈耶(2002 年)。 SMOTE:合成少数过采样技术。《人工智能研究杂志》,第 16 卷,第 321–357 页。
https://www.kaggle.com/uciml/pima-indians-diabetes-database
[3]何和马,杨(2013)。 不平衡学习:基础、算法、应用 。第一版。威利。
[4]盖,T. M .和哈特,P. E. (1967 年)。最近邻模式分类。 IEEE 信息论汇刊,第 13 卷,第 1 期,第 21–27 页。
[5]韩,j .,Kamber,m .,和裴,J. (2012 年)。 数据挖掘概念与技术 。第三版。波士顿:爱思唯尔。
[6]威尔逊法学博士(1972 年)。使用编辑数据的最近邻规则的渐近性质。 IEEE 系统、人和控制论汇刊,SMC-2 卷,第 3 期,第 408-421 页。
[7]巴蒂斯塔,两性平等协会,普拉蒂共和国,和莫纳德,M. C. (2004 年)。对平衡机器学习训练数据的几种方法的行为的研究。 ACM SIGKDD 探索时事通讯,第 6 卷,第 1 期,第 20–29 页。
[8]https://unbalanced-learn . org/stable/references/generated/imb learn . combine . smote enn . html
Python 中的不平衡分类:SMOTE-Tomek 链接方法
结合 SMOTE 和 Tomek 链接的 Python 不平衡分类
动机
在实际应用中,分类建模经常会遇到不平衡数据集问题,多数类的数量远大于少数类,从而使模型无法很好地从少数类中学习。当来自少数类的数据集中的信息更重要时,例如疾病检测数据集、客户流失数据集和欺诈检测数据集,这就成为一个严重的问题。
解决这种不平衡数据集问题的一种流行方法是对少数类进行过采样或对多数类进行欠采样。然而,这些方法都有自己的弱点。在普通的过采样方法中,想法是从少数类中复制一些随机样本,因此这种技术不会从数据中添加任何新信息。相反,欠采样方法是通过从多数类中移除一些随机样本来进行的,代价是原始数据中的一些信息也被移除。
克服这一弱点的解决方案之一是从现有的少数类中生成新的综合示例。这种方法就是众所周知的合成少数过采样技术或 SMOTE 。SMOTE 有许多变体,但在本文中,我将解释 SMOTE-Tomek 链接方法及其使用 Python 的实现,其中该方法结合了 SMOTE 的过采样方法和 Tomek 链接的欠采样方法。
概念:SMOTE
SMOTE 是最流行的过采样技术之一,由 Chawla 等人开发。(2002).与仅从少数类中复制一些随机样本的随机过采样不同,SMOTE 基于每个数据的距离(通常使用欧氏距离)和少数类最近邻来生成样本,因此生成的样本与原始少数类不同。
简而言之,生成合成样本的过程如下。
- 从少数类中选择随机数据。
- 计算随机数据与其 k 个最近邻之间的欧几里德距离。
- 将差值乘以 0 到 1 之间的随机数,然后将结果添加到少数类作为合成样本。
- 重复该过程,直到达到所需的少数族裔比例。
这种方法是有效的,因为生成的合成数据与少数类上的特征空间相对接近,从而在数据上添加了新的“信息”,这与原始过采样方法不同。
概念:托梅克链接
Tomek 链接是由 Tomek (1976)开发的压缩最近邻(CNN,不要与卷积神经网络混淆)欠采样技术的修改之一。与 CNN 方法不同,其仅从想要移除的多数类中随机选择具有 k 个最近邻的样本,Tomek Links 方法使用规则来选择满足这些属性的观测值对(例如, a 和 b ):
- 观测值 a 的最近邻居是 b 。
- 观测值 b 的最近邻居是 a 。
- 观察值 a 和 b 属于不同的类别。即 a 和 b 分别属于少数派和多数派阶级(或者反之)。
数学上可以表述如下。
设 d(x_i,x_j)表示 x_i 和 x_j 之间的欧几里德距离,其中 x_i 表示属于少数类的样本,x_j 表示属于多数类的样本。如果没有样本,x_k 满足以下条件:
1.d(x_i,x_k) < d(x_i, x_j), or
2。d(x j,x k)<d(x I,x j)那么(x_i,x_j)对就是一个托梅克链。
该方法可用于从与少数类数据具有最低欧几里德距离的多数类中找到所需的数据样本(即,来自与少数类数据最接近的多数类的数据,从而使其模糊不清),然后将其移除。
SMOTE-Tomek 链接
首先由巴蒂斯塔等人介绍。(2003),该方法结合了 SMOTE 为少数类生成合成数据的能力和 Tomek 链接从多数类中移除被识别为 Tomek 链接的数据的能力(即,来自与少数类数据最接近的多数类的数据样本)。SMOTE-Tomek 链接的过程如下。
- (SMOTE 的开始)从少数类中选择随机数据。
- 计算随机数据与其 k 个最近邻之间的距离。
- 将差值乘以 0 到 1 之间的随机数,然后将结果添加到少数类作为合成样本。
- 重复步骤 2-3,直到达到所需的少数族裔比例。(击打结束)
- (Tomek 链接的开始)从多数类中选择随机数据。
- 如果随机数据的最近邻是来自少数类的数据(即创建 Tomek 链接),则移除 Tomek 链接。
为了在实践中更好地理解这种方法,这里我将给出一些如何使用imbalanced-learn
库(或简称为imblearn
)在 Python 中实现 SMOTE-Tomek 链接的例子。我们将使用的模型是通过使用RandomForestClassifier
随机森林。对于评估程序,这里我将使用重复分层 K-fold 交叉验证方法,以确保我们在每次重复中以不同的随机化保留每个 fold 中每个类别的样本百分比(即每个 fold 在每个类别中必须有一些样本)。
实现:合成数据集
对于第一个例子,我将使用来自sklearn.datasets
库的make_classification
生成的合成数据集。首先,我们需要导入库(第二个例子中也会用到这些库)。
import pandas as pd
import numpy as np
from imblearn.pipeline import Pipeline
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_validate
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.ensemble import RandomForestClassifier
from imblearn.combine import SMOTETomek
from imblearn.under_sampling import TomekLinks
接下来,我们通过编写这些代码行来生成我们想要使用的合成数据。
#Dummy dataset study case
X, Y = make_classification(n_samples=10000, n_features=4, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=1)
我们可以从weights
参数中看到,数据集将包含 99%属于多数类的数据,而其余的属于少数类。
在这里,我创建了两个模型— ,第一个模型不使用任何不平衡数据处理,而另一个模型使用 SMOTE-Tomek 链接方法,为您提供使用和不使用 SMOTE-Tomek 链接不平衡处理方法的一些性能比较。
## No Imbalance Handling
# Define model
model_ori=RandomForestClassifier(criterion='entropy')
# Define evaluation procedure (here we use Repeated Stratified K-Fold CV)
cv_ori=RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# Evaluate model
scoring=['accuracy','precision_macro','recall_macro']
scores_ori = cross_validate(model_ori, X, Y, scoring=scoring, cv=cv_ori, n_jobs=-1)# summarize performance
print('Mean Accuracy: %.4f' % np.mean(scores_ori['test_accuracy']))
print('Mean Precision: %.4f' % np.mean(scores_ori['test_precision_macro']))
print('Mean Recall: %.4f' % np.mean(scores_ori['test_recall_macro']))
如果没有 SMOTE-Tomek 链接,产生的模型性能如下。
Mean Accuracy: 0.9943
Mean Precision: 0.9416
Mean Recall: 0.7480
正如我们可以从不平衡的数据集预期的那样,准确性指标得分非常高,但是召回指标得分非常低(大约 0.748)。这意味着模型未能很好地“学习”少数民族类别,因此未能正确预测少数民族类别标签。
让我们看看是否可以通过使用 SMOTE-Tomek 链接来处理不平衡的数据,从而提高模型的性能。
## With SMOTE-Tomek Links method
# Define model
model=RandomForestClassifier(criterion='entropy')
# Define SMOTE-Tomek Links
resample=SMOTETomek(tomek=TomekLinks(sampling_strategy='majority'))
# Define pipeline
pipeline=Pipeline(steps=[('r', resample), ('m', model)])
# Define evaluation procedure (here we use Repeated Stratified K-Fold CV)
cv=RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# Evaluate model
scoring=['accuracy','precision_macro','recall_macro']
scores = cross_validate(pipeline, X, Y, scoring=scoring, cv=cv, n_jobs=-1)# summarize performance
print('Mean Accuracy: %.4f' % np.mean(scores['test_accuracy']))
print('Mean Precision: %.4f' % np.mean(scores['test_precision_macro']))
print('Mean Recall: %.4f' % np.mean(scores['test_recall_macro']))
结果如下。
Mean Accuracy: 0.9805
Mean Precision: 0.6499
Mean Recall: 0.8433
准确度和精确度指标可能会下降,但我们可以看到召回指标更高,这意味着通过使用 SMOTE-Tomek 链接处理不平衡数据,该模型在正确预测少数类标签方面表现更好。
实施:电信流失数据集
对于第二个例子,这里我使用来自 Kaggle 的电信客户流失数据集。这个数据集中有两个数据文件,但是在本文中,我将使用churn-bigml-80.csv
数据文件。
电信客户流失数据集(图片取自 Kaggle
首先,我们导入库(就像第一个例子)和数据,如下所示。
data=pd.read_csv("churn-bigml-80.csv")
data.head()
让我们看看数据描述,找出每个变量的类型。
> data.info()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2666 entries, 0 to 2665
Data columns (total 20 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 State 2666 non-null object
1 Account length 2666 non-null int64
2 Area code 2666 non-null int64
3 International plan 2666 non-null object
4 Voice mail plan 2666 non-null object
5 Number vmail messages 2666 non-null int64
6 Total day minutes 2666 non-null float64
7 Total day calls 2666 non-null int64
8 Total day charge 2666 non-null float64
9 Total eve minutes 2666 non-null float64
10 Total eve calls 2666 non-null int64
11 Total eve charge 2666 non-null float64
12 Total night minutes 2666 non-null float64
13 Total night calls 2666 non-null int64
14 Total night charge 2666 non-null float64
15 Total intl minutes 2666 non-null float64
16 Total intl calls 2666 non-null int64
17 Total intl charge 2666 non-null float64
18 Customer service calls 2666 non-null int64
19 Churn 2666 non-null bool
dtypes: bool(1), float64(8), int64(8), object(3)
memory usage: 398.5+ KB
然后,我们检查数据中是否存在缺失值,如下所示。
> data.isnull().sum()State 0
Account length 0
Area code 0
International plan 0
Voice mail plan 0
Number vmail messages 0
Total day minutes 0
Total day calls 0
Total day charge 0
Total eve minutes 0
Total eve calls 0
Total eve charge 0
Total night minutes 0
Total night calls 0
Total night charge 0
Total intl minutes 0
Total intl calls 0
Total intl charge 0
Customer service calls 0
Churn 0
dtype: int64
没有缺失值!接下来,我们通过编写如下代码来计算属于Churn
变量中每个类的数据数量。
> data['Churn'].value_counts()False 2278
True 388
数据相当不平衡,其中多数阶级属于False
标签(我们将其标记为 0),少数阶级属于True
标签(我们将其标记为 1)。
对于下一个预处理步骤,我们删除State
变量(因为它包含了太多的类别),然后我们重新编码Churn
变量(False=0,True=1),并通过编写这些代码行来创建虚拟变量。
data=data.drop('State',axis=1)
data['Churn'].replace(to_replace=True, value=1, inplace=True)
data['Churn'].replace(to_replace=False, value=0, inplace=True)
df_dummies=pd.get_dummies(data)
df_dummies.head()#Churn dataset study case
Y_churn=df_dummies['Churn'].values
X_churn=df_dummies.drop('Churn',axis=1)
数据预处理完成。现在,我们使用与第一个例子相同的方法进行建模。
## No Imbalance Handling
# Define model
model2_ori=RandomForestClassifier(criterion='entropy')
# Define evaluation procedure (here we use Repeated Stratified K-Fold CV)
cv2_ori=RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# Evaluate model
scoring=['accuracy','precision_macro','recall_macro']
scores2_ori = cross_validate(model2_ori, X_churn, Y_churn, scoring=scoring, cv=cv2_ori, n_jobs=-1)# summarize performance
print('Mean Accuracy: %.4f' % np.mean(scores2_ori['test_accuracy']))
print('Mean Precision: %.4f' % np.mean(scores2_ori['test_precision_macro']))
print('Mean Recall: %.4f' % np.mean(scores2_ori['test_recall_macro']))
如果没有不平衡的数据处理,结果如下。
Mean Accuracy: 0.9534
Mean Precision: 0.9503
Mean Recall: 0.8572
请记住,我们使用的数据是不平衡的,因此我们不能仅仅通过观察精确度指标来简单地说模型性能良好。尽管准确性指标得分相当高,但召回指标得分仍然不够高,这意味着该模型正在努力正确预测少数类标签(即被重新编码为 1 的True
标签)。
现在,让我们对数据进行 SMOTE-Tomek 链接方法,以查看性能改进。
## With SMOTE-Tomek Links method
# Define model
model2=RandomForestClassifier(criterion='entropy')
# Define SMOTE-Tomek Links
resample2=SMOTETomek(tomek=TomekLinks(sampling_strategy='majority'))
# Define pipeline
pipeline2=Pipeline(steps=[('r', resample2), ('m', model2)])
# Define evaluation procedure (here we use Repeated Stratified K-Fold CV)
cv2=RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# Evaluate model
scoring=['accuracy','precision_macro','recall_macro']
scores2 = cross_validate(pipeline2, X_churn, Y_churn, scoring=scoring, cv=cv2, n_jobs=-1)# summarize performance
print('Mean Accuracy: %.4f' % np.mean(scores2['test_accuracy']))
print('Mean Precision: %.4f' % np.mean(scores2['test_precision_macro']))
print('Mean Recall: %.4f' % np.mean(scores2['test_recall_macro']))
结果如下。
Mean Accuracy: 0.9449
Mean Precision: 0.8981
Mean Recall: 0.8768
准确度和精确度分数可能会稍微降低,但是召回分数会增加!这意味着该模型能够更好地正确预测该流失数据集中的少数类标签。
结论
就是这样!现在,您将了解如何在 Python 中使用 SMOTE-Tomek Links 方法来提高不平衡数据集中分类模型的性能。像往常一样,如果您有任何问题,请随时提问和/或讨论!
我的下一篇文章再见!保持安全,保持健康!
作者的联系方式
中:https://medium.com/@radenaurelius
参考
[1]舒拉、鲍耶、霍尔和凯格尔迈耶(2002 年)。 SMOTE:合成少数过采样技术。人工智能研究杂志,第 16 卷,第 321–357 页。
[2]https://www.kaggle.com/mnassrib/telecom-churn-datasets
[3]托梅克,1976 年。CNN 的两次修改。 IEEE 系统、人和控制论汇刊,第 6 卷,第 11 期,第 769-772 页。
[4]何和马,杨(2013)。 不平衡学习:基础、算法、应用 。第一版。威利。
[5]曾,m,邹,b,魏,f,刘,x,王,L. (2016)。结合 SMOTE 和 Tomek links 技术对不平衡医疗数据进行有效预测。 2016 年 IEEE 在线分析与计算科学国际会议(ICOACS) ,第 225–228 页。
[6] Batista,G. E. A. P. A .,Bazzan,A. L. C .和 Monard,M. A. (2003 年)。平衡关键字自动标注的训练数据:案例研究。第二届巴西生物信息学研讨会会议录,第 35-43 页。
[7]https://sci kit-learn . org/stable/modules/cross _ validation . html
不平衡数据—使用高斯混合模型进行过采样
其他创成式模型也可以类似地用于过采样
图片来自皮克斯拜
TL;DR —从高斯混合模型(GMM)或其他生成模型中提取样本,是另一种创造性的过采样技术,可能优于 SMOTE 变体。
目录
- 介绍
- 数据集准备
- GMM 介绍
- 使用 GMM 作为过采样技术
- 绩效指标的评估
- 结论
介绍
在之前的文章中,我讨论了一个人如何想出许多能够胜过 SMOTE 变体的创造性过采样技术。我们看到了使用“交叉”的过采样如何优于 SMOTE 变体。
在本文中,我们将展示如何使用高斯混合模型(GMM) 或生成模型对不平衡数据集中的少数类实例进行过采样。
数据集准备
我们首先使用 scikit-learn 的 make_classification 函数生成一个包含两个类的 5000 个数据点的图像分类数据集(二元分类)。有 95%的可能性目标是 0,有 5%的可能性目标是 1。我们准备了一个类似于上一篇文章的数据集。
from sklearn.datasets import make_classification
import seaborn as snsX, y = make_classification(
n_samples=5000, n_classes=2,
weights=[0.95, 0.05], flip_y=0
)sns.countplot(y)
plt.show()
为练习生成的不平衡数据集(图片由作者提供)
默认情况下会创建 20 个特征,下面是我们的 X 数组中的一个示例条目。
使用 scikit-learn 的 make_classification 创建的包含 20 个特征的示例条目(图片由作者提供)
最后,我们将数据分成训练和测试数据集。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y)
高斯混合模型介绍(GMM)
高斯混合模型(GMM)假设数据来自多个正态分布的子群体。
**示例:**假设我们有一万个人的身高数据。下面是一个样本分布。看起来不像钟形正态分布。
万人身高的概率分布(图片由作者提供)
然而,它实际上来自正态分布的平均身高不同的 4 个不同人群。一组的平均身高为 150 厘米,其他组的平均身高分别为 160、170 和 180 厘米。
下面是用于生成图表的代码。您可以看到 4 个不同的组,我们分别从中生成 4000、1000、1000 和 4000 个样本数据,总共 10,000 个样本个体。
samples_150 = ss.norm(150, 10).rvs(4000)
samples_160 = ss.norm(160, 10).rvs(1000)
samples_170 = ss.norm(170, 10).rvs(1000)
samples_180 = ss.norm(180, 10).rvs(4000)samples_total = np.hstack([samples_150, samples_160,
samples_170, samples_180])plt.figure(figsize=(10, 5))
ax = sns.distplot(samples_total)
ax.set_xlabel("Height (in cm)")
plt.show()
各组身高呈正态分布。
plt.figure(figsize=(10, 5))
ax = sns.distplot(samples_150)
ax.set_xlabel("Height (in cm)")
plt.show()
显示平均身高 150 cm 的群体如何正态分布的示例(图片由作者提供)
GMM 背后的想法是,一个数据集可能有来自不同子群体的观察值,这些子群体有自己的特征。GMM 帮助从数据中提取子群体,这样每个子群体就形成了一个我们可以使用的集群。
使用 GMM 作为过采样技术
如果我们回到在“数据集准备”一节中生成的分类数据集,我们可以尝试从数据中提取子群体/聚类。让我们以提取 5 个集群为例。
gmm = GaussianMixture(5)
gmm.fit(X_train)
就是这样!
在引擎盖下,我们的 GMM 模型现在已经创建了 5 个不同的聚类,它们具有不同的正态分布,反映了每个聚类可以采用的特征值。
下面的示例显示了 20 个特征中每个特征的聚类平均值。
pd.DataFrame(gmm.means_.T)
例如,我们可以看到,对于特征 1,聚类 0 的平均值为-0.159613(图片由作者提供)
更重要的是,GMM 模型可以帮助我们实现两个功能:
1-它可以查看特定样本的特征值,并将该样本分配给一个聚类。
gmm.predict(X_test)
示例预测显示 X_test 中的前 3 个样本分别属于聚类 0、2 和 4(图片由作者提供)
2-它可以使用拟合的正态分布来生成新的样本,我们可以将其用于过采样。
gmm.sample(5)
生成了具有特征值的 5 个样本,即 X,它们分别属于聚类 0、1、2、2 和 2(图片由作者提供)
最后,在本例中,我们将数据聚类到 5 个不同的桶中,但我们的问题是一个二元分类问题,其中我们的目标变量可以是 0 或 1。
一个想法是检查每个聚类和目标(y)变量之间的关系。
cluster_mean = pd.DataFrame(data={
"Cluster": gmm.predict(X_train),
"Mean Target Variable (y)": y_train
}).groupby("Cluster").mean().reset_index(drop=False)plt.figure(figsize=(10, 5))
sns.barplot(data=cluster_mean, x="Cluster",
y="Mean Target Variable (y)")
plt.show()
聚类 4 与一个积极的目标变量有最强的关联(图片由作者提供)
我们可以看到集群 4 的目标值平均值最高,略高于 40%。
请记住,这是一个不平衡的数据集,其中只有 5%的样本的目标变量(y)值= 1,因此 40%是一个很大的数字。
最后一步是从 GMM 模型中生成随机样本,并且只保留属于聚类 4 的样本。我们可以用一个正的目标变量(y=1)来标记它们。
samples, clusters = gmm.sample(100)
samples_to_keep = samples[clusters==4]
我们终于可以将它们添加到我们的训练数据中了!
我们同样可以从与 y=1 关联最强的前 2–3 个集群中抽取样本。或者我们可以从 y 的平均值高于预定义阈值的任何集群中抽取样本。
绩效指标的评估
测试这种过采样技术的性能时,我们将使用下面的函数,该函数总结了我们讨论的所有步骤。
def oversample_gmm(X, y, rows_1, no_clusters=2,
no_sampling_clusters=1, rs=1):
# Instantiate GMM model
gmm = GaussianMixture(
no_clusters,
covariance_type='tied',
max_iter=10000,
random_state=rs
)
gmm.fit(X)
# Finding cluster with y=1 most likely
cluster_mean = pd.DataFrame(data={
"Cluster": gmm.predict(X_train),
"Mean Target Variable (y)": y_train
}).groupby("Cluster").mean().sort_values(
by='Mean Target Variable (y)', ascending=False)top_clusters = cluster_mean.sort_values(
by='Mean Target Variable (y)', ascending=False).index[
:no_sampling_clusters]
# Number of rows we will generate before
# filtering by required cluster.
# Multiply by 5 to ensure we have sufficient samples
rows_initial = rows_1 * no_clusters * 5
# Generate samples
samples, clusters = gmm.sample(rows_initial)# Keep samples coming from clusters where y=1 is likely
top_clusters_filter = [np.any([
cluster == x for x in top_clusters]
) for cluster in clusters]
samples_to_keep = samples[top_clusters_filter]
# Keep only required number of additional samples
rows_required = rows_1 - np.sum(y)
np.random.shuffle(samples_to_keep)
samples_to_keep = samples_to_keep[:rows_required]
# Add samples to training dataset
X_gmm = np.vstack([X, samples_to_keep])
y_gmm = np.hstack([y, np.ones(samples_to_keep.shape[0])])
return X_gmm, y_gmm
与我们的上一篇文章类似,我们遍历了 30 个随机状态,并比较了随机森林分类器在原始数据集和过采样方法上的性能,确保我们有 2000 个具有正目标值(target = 1)的训练样本:
- 随机过采样
- SMOTE — 1 个邻居
- SMOTE — 3 个邻居
- SMOTE — 5 个邻居
- SMOTE — 10 个邻居
- GMM — 2
- GMM — 3
- GMM — 5
- GMM — 10
- GMM——15 人
我们还研究了 7 个分类指标:
- ROC AUC—ROC 曲线下的面积
- PR AUC——精确召回曲线下的面积
- 平衡的准确性—这也相当于两个标签的宏观平均召回率
- 最大 F1 —不同概率临界值的最大 F1 分数(即,如果我们在预测概率> 0.2 或 0.3 时预测 1,等等。而不是默认的 0.5)
- 回忆
- 精确
- F1 分数
以下是结果…
SMOTE 在 ROC AUC 上领先(查看中位数),但 PR AUC 更适合不平衡数据集(图片由作者提供)
GMM 在 PR AUC 上领先 10 或 15,这是不平衡数据集的良好指标(图片由作者提供)
具有 10 个和 15 个簇的 GMM 在 Max F1 上也表现非常好,但是随机过采样稍有优势(图片由作者提供)
拥有两个集群的 GMM 在平衡准确性方面领先(图片由作者提供)
GMM 在回忆方面领先 2 或 3 个集群(图片由作者提供)
原始数据集和具有 10 个聚类的 GMM 在精度指标上领先(图片由作者提供)
具有 5 个以上聚类的 GMM 具有最高的 F1 分数(图片由作者提供)
当然,结果有好有坏。
当查看与不平衡数据集最相关的指标时,具有 10 个聚类的 GMM 是最佳选择:精度-召回曲线、F1 得分和最大 F1(F1 的替代方案,其中我们考虑所有概率阈值)。
结论
我们已经展示了另一种用于不平衡数据集的创新而简单的过采样技术如何在几个指标上优于 SMOTE 和随机过采样等默认技术。
同样基于 GMM 的方法也可用于生成合成数据样本,以调整您的预测模型。这在预期会有大量模型/数据漂移并且交叉验证结果会产生误导的用例中尤其有用。
这是一个在后续文章中讨论的话题…
人工智能分散自治组织在机器对机器经济中的影响
介绍自主经济主体、机器学习和机器对机器经济中的新商业模式(M2M)
纳斯蒂亚·杜尔希尔在 Unsplash 上的照片
即将到来的机器对机器(M2M)时代将彻底改变公司和资产的管理方式。因此,大多数现有的商业模式将会过时。
基于这一评估,我接受了为我的客户建立 M2M 战略以及一个试点项目的任务。这一新战略包括创建新的商业模式,选择新的机器学习方法,以及确定可能的技术合作伙伴。
在本文中,您将了解未来的关键概念,如自主经济主体,发现 M2M 用例,以及公司如何从数据角度重塑自己以保持相关性。
机器对机器经济介绍(M2M)
M2M 时代的特点是,消费者/公司将拥有更少的资产和机器,同时共享更多的数据。
我们将更加依赖自主机器来代表我们采购和签订合同。最终,“这些机器将自主决策,买卖服务,作为市场参与者的新资产类别参与未来经济”( 1 ): 自主经济代理人(AEA) 或自主智能代理人(AIA) 。
根据 Anton Korinek 的说法,要被视为智能代理,计算机系统必须具有以下特性( 2 ):
作者图片,想法来自迈克尔·j·伍尔德里奇,“智能代理”,在韦斯·格哈德(编辑。)*多智能体系统:分布式人工智能的现代方法,*剑桥,马萨诸塞州,麻省理工学院
M2M 经济的定义是,智能机器决定购买什么,以什么价格购买,并在没有中间人的情况下通过区块链分布式账本完成交易。
机器之间的微支付可以带来更高的效率,例如寻找特定备件的汽车、无人机或农场可以直接相互协商以实现其目标(建筑工地、维护工作等)。)而不需要人类的参与。
由于多代理人工智能系统和分散的自治组织,机器将有能力出租自己,雇佣维护专业人员,并支付更换零件的费用。
在这种新经济中,每台机器都有不同的能力,但有一条共同的线索将它们联系在一起——相互生产和消费商品和服务的能力。此外,这些智能代理/机器能够协商价格(并发现新的提供商)以自主运行。
参与 M2M 交易的设备可以被编程为基于个人或商业需求进行购买。
它是如何工作的
简而言之,加密货币和智能合约使自动机器在区块链上相互交易成为可能。微支付渠道和纳米支付在 M2M 经济中将非常有用。我相信 M2M 经济中的大多数机器都会有某种数字钱包和 ID。
机器学习在 M2M 通信发展中的作用至关重要。事实上,机器学习提供了获取物联网生成的数据并将其转换为支付决策的能力,从而消除了用户的明确决策。
用例#1 —来自 戴姆勒卡车的试点项目戴姆勒一直致力于与卡车相关的 M2M 项目。事实上,他们希望他们的卡车能够“自主地与其他机器通信,并执行具有法律约束力的交易,如支付。这个想法是让汽车拥有自己的装有电子货币的钱包,这样就可以不用人工干预就能支付燃料或通行费”。
在大多数 M2M 项目中,数字身份证和钱包是必需的。因此,他们的试点项目包括“卡车 ID 和卡车钱包,它们还可以通过证明卡车何时由哪个用户使用来部分接管对分包商的控制和计费。
M2M 通信可以创建多个新的用例。例如,车辆租赁和短期使用合同(如“按使用付费”)的“平均处理”可以直接由卡车本身控制:客户直接在车辆上付款,卡车自行决定是否满足运营准备所需的条件。作为试点项目的一部分,他们已经创建了数字卡车 ID** 和相关的卡车钱包——这是 M2M 卡车经济的必要先决条件。**
机器经济伙伴关系
M2M 经济将需要企业建立新的生态系统合作伙伴关系。我期望看到公司创造的机器会因为一些合同协议而偏向于一个 M2M 平台或公司。我还希望看到越来越多的公司建立 M2M 平台,并竞争成为每个行业中领先的 M2M 平台。
作者图片
例如,德国电信的 M2M 解决方案使机器、传感器和车辆能够相互通信,或者与所有商业领域的中央数据处理平台通信。
用例#2
戴姆勒卡车与德国商业银行合作,在一个电动充电站完成了自动支付。他们的目标是使戴姆勒卡车能够在各种应用领域发挥自己的作用。( 4
如何准备你的组织
行业专家认为,在未来 10 到 20 年“机器客户”将分三个阶段发展。
图片由作者提供,创意来自 唐·谢本瑞夫
可以有把握地假设,在不久的将来,大多数公司应该会开始开发这样的机器,它们将获得自己的机器身份,以及自己的数字钱包,这些身份具有将它们与其他机器区分开来的个人属性。。
此外,应该为 M2M 设备提供一个唯一、安全的身份,使其能够在生产网络中向其他机器、实例和参与者进行身份验证。
作为一家无人机制造商试点项目的一部分,我们已经创建了数字无人机 ID 和相关的无人机钱包——这是具有 M2M 功能的无人机的必要先决条件。
新的商业模式和威胁
当机器能够自由地从其他机器获得服务时,新的市场就会出现。M2M 通信将影响所有产品,从通过联网家用设备订购的日常产品,到制造机器人购买的备件和原材料。
机器可以与他人互动的事实将创造或摧毁一些商业模式。例如,通过制造具有 M2M 功能的新车,汽车制造商可以告诉他们的客户何时需要更换零部件。显然,这允许他们在一个全新的水平上提供客户服务,但也阻止人类业主选择其他供应商。
IBM 的 ADEPT 项目通过将物联网与区块链集成来创造半自动设备,可以进行预测性维护,然后使用虚拟“钱包”来订购零件和用品,从而开辟了新的天地。
**获胜的未来商业模式将是如何创造相关的机器,为它们的所有者创造收入。**因此,大多数公司在开发产品时都认为产品可以与其他产品交流,并有可能自己购买服务。我相信 M2M 通信将最终加强现有的锁定效应。
与此同时,M2M 可能对零售商构成威胁。的确,零售商靠冲动或无计划的购买赚钱。机器不会刺激购买…
严重依赖市场营销的公司将不得不改变他们的沟通方式。他们必须意识到,为机器设计不仅仅是为非人类客户调整现有的产品营销。
处于危险中的公司的一个很好的例子是那些专门从事售后产品的公司…如果某个特定品牌的一台机器被指示只考虑特定供应商的维护或零件,而忽略其他的,那该怎么办?这种情况可能会摧毁专门从事售后产品的公司。
实时出售数据
大多数公司将利用 M2M 通信通过出售或交易实时数据来创造新的收入流。
例如,想象商店中的智能传感器向营销公司提供店内访客的交易数据,以换取在线广告的折扣,或者向零售商提供销售数据,以帮助评估未来产品的需求。
此外,你的汽车可以出售数据,为你创造收入。事实上,在驾驶时,您的车辆会自动收集数据(例如,是否有可用的停车位或充电站)。这些数据可以出售给其他汽车使用。
在不久的将来,客户将期望公司创造出不仅能满足需求,还能为他们创造收入的产品。
我还预计一些公司会说他们的产品永远不会收集用户的数据,除非他们决定这样做。理想情况下,客户将能够决定他们是否想要参与特定的数据市场。
作者图片
自我管理的机器和商业模式
我相信,机器不仅会通过 M2M 平台购买维护、检查和保险等服务,还会将它们的服务作为产品出售,企业按小时或按结果付费,而不是直接购买资产。例如,你的车可以在你不用的时候出租,为你创造收入…
新的商业模式将会出现,减少企业拥有、维护或管理资产的需求,并降低资产报废周期的风险。
我相信这些发展也将创造几个数据市场。一些公司将成为专门从事匹配买家和卖家(包括人类和非人类市场参与者)的软件的。人类将与非人类参与者竞争/参与数据市场。
因此,数据交易将“超越人对人和人对机器的交易,包括旨在为自主机器参与者服务的商业化机制”。( 6 )
新的“客户”之旅
公司未来在 M2M 面临的一个关键挑战将是确保它们的产品在适当的在线交易所上市,描述完整,并采用适当的机器友好格式。我希望看到一些产品经理专门研究 M2M 的这个方面。
我们将从以人为中心过渡到以机器为中心的客户之旅。
在客户之旅中,认知步骤将变得更加数据驱动,而不是基于情绪。由于机器会相互作用,考虑阶段也将完全由数据驱动。因此,公司需要确保他们的机器能够将最相关的信息传递给附近的另一台机器。
如今,包装、品牌形象、在线内容和用户界面等元素对消费者有着重要的影响。在 M2M 时代,公司将必须确保它们能够轻松地与其他机器交流,它们的服务在多大程度上满足了设备或软件的技术需求。营销要素将被代码的效率、对所需 API 或协议的遵从性以及交易的速度或可靠性所取代。
作者图片
AI 去中心化自治组织(AI DAOs)
M2M 时代也将见证人工智能去中心化自治组织(AI DAOs)的崛起。
定义:****道是一种去中心化的商业模式。它是由管理企业如何运作的智能合同组成的。所有的“道的经营决策和财务行为都记录在一个公开的、不可更改的区块链上”( 7 )。
一个 DAO 的每个参与者/投资者都可以就公司的运营方式投上一票。该区块链对所有投资者/代币持有者开放,便于与每个投资者分享所有信息。
一个人工智能 DAO 将是一个使用多个人工智能代理(群体智能)的 DAO。还有其他方法可以创建 AI DAO。
机器交易数据,支付维护、能源或责任保险。
**M2M 经济恰恰是把这种道的概念转移到了机器上。**在 M2M 经济中,汽车制造商可以制造最终成为自主组织的汽车;他们可以拥有自己的数字身份和钱包来存储数字值(代币),他们将使用这些数字值来管理和存储他们通过向人类乘客提供运输服务而获得的值,以及支付他们的充值和维护费用。
在 M2M 经济中,艾道斯本身被认为是一个经济主体,有自己的经济,自给自足,甚至有自己的商业模式。此外,同样的汽车或自动售货机将围绕它培育新的微服务生态系统。
当涉及到所有权时,这些自治组织可以作为一种众筹融资,用初始投资创建一个 AI DAO。AI DAO 将允许代币所有者治理、决策和利润分享。也许艾道斯会以某种方式与创造它们的公司竞争……
尽管最近的技术进步,机器经济仍然是一个实验性的概念,需要解决不同的挑战。例如,安全的基于硬件的数字身份、群体智能、机器学习中的因果关系、互操作性和数据主权,甚至分布式机器治理模型。
想了解更多关于 M2M 经济的信息,我推荐以下链接:
- [机器经济发展的四个步骤](http://Four Steps in the Evolution of the Machine Economy)
- 多智能体系统:分布式人工智能的现代方法
- 人工智能代理的崛起:人工智能对经济日益增长的影响,第一部分
- 戴姆勒卡车公司正在教卡车如何付款
- M2M 经济——智能机器代理:交付有形的、可工作的区块链原型
- 欢迎来到机器对机器经济:互联世界中的机遇和挑战
- 什么是 M2M 技术?
- 如何将物联网货币化:机器经济中的数据交易
- 机器经济已经到来:为互联世界提供动力
- 区块链:从工业 4.0 到机器经济
- 人工智能能模拟经济选择吗?
- 机器客户:下一个新兴市场?
新冠肺炎疫情和地震对狭窄市中心交通流的影响
如何通过挖掘流量数据发现隐藏现象
恐慌(图片作者:作者)
交通科学家和工程师通常使用交通数据来建模和预测交通行为。但是,利用交通数据有没有可能提取出一些与交通流量没有严格关系的“隐藏”现象呢?在本文中,介绍了论文 " 的结果:新冠肺炎疫情和地震对狭窄城市中心交通流的混合影响:萨格勒布-克罗埃西亚【1】的案例研究,其中作者发现了面对自然灾害所激发的有趣的人类行为模式。
1.介绍
2020 年对全人类来说都是充满挑战的一年。在我的家乡(萨格勒布,克罗埃西亚),在 COVID 疫情旁边,几场地震在 100 多年没有震动后发生了。作为一名交通工程师,对于上述事件,观察市中心交通流量的变化是很有趣的。
作为一组来自萨格勒布大学运输和交通科学学院的研究人员,我们试图调查新冠肺炎疫情之前和期间的交通流量趋势。此外,我们分析了地震期间的交通流量,地震于 2020 年 3 月 22 日周日早上 6:24 袭击了萨格勒布。
除了疫情造成的“正常”交通流量下降,我们还发现了关于地震反应的有趣的人类行为模式。
2.疫情对交通流的影响
为了说明疫情对交通流的影响,我们提出了两个经典的交通参数:速度和交通量。出于本研究的目的,我们观察了市中心的一条道路,基于雷达的交通计数器放置在这里。
图一。显示了在现场观察到的速度曲线,其中每个速度曲线代表每周平均的 24 小时内的平均速度变化(见图例)。我们可以看到,在 3 月 16 日之后,车辆的速度急剧增加,在繁忙时间经常出现的拥挤情况已经消失。由于政府在疫情采取了限制行动的措施,这是意料之中的行为。
图一。速度曲线(图片来源:[1])
图二。只是确认了交通流的预期行为。红色显示 2019 年的平均值,而红色显示疫情期间的值。在左侧,我们可以看到交通量和速度之间的关系,在右侧,我们可以看到交通量曲线。两个图表都显示了道路上超过 50%的车辆数量较少,因此道路上的速度值较高。
图二。[左]2019 年平均工作日(黑色)和有新冠肺炎限制的一周(红色)的量和速度的关系;[右]2019 年平均工作日(黑色)和新冠肺炎限行周(红色)的交通量概况(图片来源:[1])
3.面对自然灾害时人类的行为模式
在分析了疫情的交通模式后,我们分析了地震时的交通模式。
就我个人而言,我并不期待有什么不同。但是,我不这么认为。
图 3。显示地震发生当天的交通量(红线)和前一天的交通量(黑线)。地震发生在早上 6 点 24 分。可以看到,早上 7:30 的车流量增加了 100%以上。在检查了每日新闻后,我们看到大量的人只是跑向他们的汽车,试图尽可能远。那种行为显然是由地震引起的恐慌和恐惧引发的。
图 3。交通模式显示了由恐惧和惊慌行为引起的道路上活动的增加(图片来源:作者)
4.结论
本文展示了一个例子,展示了在挖掘数据时如何提取出意想不到的知识。我们展示了一个通过挖掘流量数据集提取人类行为模式的例子。
如果你对类似的、与交通相关的话题感兴趣,请关注我的媒体简介,或者在研究门户:)查看发表的研究
如有任何问题或建议,欢迎评论或联系我!https://www.linkedin.com/in/leo-tisljaric-28a56b123/
类似文章:
参考
作为数据科学家影响您的社区
如何利用数据科学技能获得“真实世界”的体验。
作为一名数据科学家新手,我发现的一个主要挑战是由于缺乏“真实世界的经验”而无法进入这个领域。尽管有许多很棒的训练营提供从事激情项目的专业人员,但我注意到新数据科学家仍然需要一些时间才能在许多公司找到工作。
我发现了一个“窍门”,可以获得经验,并快速跟踪一个新手将他们的数据科学技能应用到现实世界的项目中。我将在这里概述我的一些经历,我是如何发现自己在马萨诸塞州菲奇堡的当地社区从事一个数百万美元的项目的,这一切都是因为我能够找到机会,并自愿将我的技能提供给最需要它们的利益相关者。
从 Metis 数据科学训练营毕业后,我发现自己刚刚掌握了机器学习和数据科学技能,但却找不到任何有意义的项目。即使在完成了多个在线教程和项目之后,它对我来说似乎并不重要,因为它并没有被人们日常使用。因此,我采取了以下步骤来确保我能够以一种能够产生重大影响的方式来运用我的技能。
- 采访当地商业领袖:2021 年,我决定花一些时间在我的当地社区与企业主、政府官员、居民和学生讨论,试图了解他们面临的一些挑战。我问了许多开放式的问题,没有明确的目标,只是想看看我能在哪些方面发挥我的技能。在这里度过的时间实际上给了我一个很好的机会去了解马萨诸塞州菲奇堡的历史,它的风俗和它的人民。通过这一过程,我在该地区结交了很多好朋友,并建立了惊人的关系。
采访当地的小企业主。作者图片
2.从讨论中发现真知灼见:通过我与不同领导的交谈,以及在发出调查问卷后。我发现市政当局正面临一些巨大的挑战。
3.大问题:面临的问题是每年大约有 3000 万美元花费在城外,原因是在菲奇堡普遍缺乏某些种类的商业和对新商业的认识。例如,菲奇堡的居民会开 30 分钟到 1 小时的车去光顾波士顿、剑桥和伍斯特等大城市的商店,而这些商店离他们只有 5 到 15 分钟的路程。这是一个很大的问题,因为“资金外流”,即从长远来看,社区支出的资金会影响许多小企业、社区和经济发展。社区领导已经制定了一个 5-10 年的计划来解决这个问题,并鼓励我制定有用的解决方案。
4.真实世界的数据解决方案和影响:我参与了两个具体的项目,以解决该地区的商业意识和商业可用性问题。第一个项目是帮助提高当地的商业意识,第二个项目是帮助改善在镇上开办新企业的过程。在商业意识方面,我建立了 SeeksCo ,这是一个面向小城镇和城市的 yelp,允许个人轻松找到该地区的企业,并为这些企业提供评论。到目前为止,SeeksCo 正在帮助提高这些企业的意识。
与意式浓缩披萨的老板讨论(开始于 50 多年前)。图片作者。
第二个项目是与市长办公室合作,在菲奇堡市区建立店面空置的数据可视化(使用 Tableau)。该项目将允许投资者和未来的企业主查看他们可以在该地区租赁或购买的店面物业空缺,并提供详细信息,如最新照片、业主信息和物业状态。目前,菲奇堡有 100 多处空置房产,为小企业提供了许多机会,为该地区的 40,000 多名市民提供服务。这个项目的影响是显而易见的,因为它为商业领袖、投资者和其他利益相关者节省了时间。过去,个人必须手动搜索这些信息,但现在他们可以在向当地政府经济部门提出请求后访问这些数据。
菲奇堡的空置店面。图片作者。
通过这两个项目,我能够向当地领导和全球领导展示数据在社区中的作用以及如何鼓励最佳数据实践。
5.社区的持续支持:作为一名数据科学家,尽管我们的工作主要集中在数据和算法上,但我个人认为,提出解决方案的最佳方式是从数据中创造有意义的见解。通过了解数据分析影响的个人故事,我们的工作可以做得更好。
这就是为什么我志愿帮助组织菲奇堡社区的活动,以帮助提高小企业的意识。我与当地一个名为 Reimagine North of Main 的组织合作,在 2021 年夏天组织了一次街区聚会,以促进许多小企业的发展。我们有 500 多人参加,许多小企业能够获得新客户。
为小企业组织街区聚会。图片作者。
当我开始面试一份数据科学的工作时,我在当地社区的经历很有帮助。在面试数据科学工作时,我能够讨论真实世界的项目,并接受了在波士顿地区一家现代咨询公司担任数据科学家顾问的邀请。我的许多采访讨论都围绕着我在社区中所做的影响工作,并为我提供了一个用数据科学专业人员可以理解的术语进行解释的机会。
总之,数据科学领域是一个快节奏的领域,人们可能会觉得不得不进入这个领域,但找到创造性的方法来利用自己获得的技能,以一种实际上对真实的人有用的方式,不仅对提高我们的数据科学家技能有巨大的好处,而且对立即需要我们帮助的人也有好处。
更多关于我的影响力工作,请访问我的页面:【dotunopasina.com/impact】T2,并在 LinkedIn 上与我联系。
从头开始实现高斯过程
高斯过程模型的基本理论和实际实现的演练
(图片由作者提供)
G 高斯过程 (GP)是一种强大的监督机器学习方法,主要用于回归设置。这种方法在实践中是可取的,因为:
- 它在小数据范围内表现很好;
- 它具有高度的可解释性;
- 它自动估计预测的不确定性。
这最后一点是 GP 区别于许多其他机器学习技术的地方:对于 GP 模型,它在位置 x 的预测 f ( x )不是确定性的值,而是遵循正态分布的随机变量,即f(x)~N(μ(x),【T20 这里, μ ( x )表示预测均值, σ ( x )是预测方差,作为预测不确定性的指标。
那么,为什么估计预测不确定性很重要呢?有两个原因:首先,它通过告诉我们可以在多大程度上相信一个特定的预测,使能够做出可靠的决策;其次,它有助于**主动学习,**这意味着我们可以智能地分配对模型性能贡献最大的训练数据。
尽管高斯过程在实际应用中很重要,但它在机器学习书籍和教程中出现的次数并不多。部分原因是 GP 理论充满了高级统计学和线性代数,对新来者不太友好。
在这篇博客中,我们将采用边做边学的方法,从头开始用 Python 实现一个 GP 模型。最后,我们将把我们的原型付诸实施,并近似两个分析函数:
图 1 我们的测试函数。(图片由作者提供)
我为这篇文章创建了一个 Jupyter 笔记本。另外,我已经编译了一个备忘单,它总结了 GP 相关的公式。您可以在编写自己的 GP 模型时使用它作为快速参考。
所以,让我们开始吧!
目录
1。了解高斯过程
2。内核函数
3。 [GaussianProcess](#025e)
类
4。初始化 GP 模型
5。构造相关矩阵。
7 训练一个 GP 模型(理论)。
8 训练一个 GP 模型(代码)。
GP 模型预测(理论)9。GP 模型预测(代码)
10。基准
11。外卖
关于作者
1.理解高斯过程
使用 GP 方法的一个常见情况是:我们收集了一些训练数据*= {(xᵢ, y ᵢ), i =1,…,n}, y ᵢ为实值标签。我们希望训练一个模型,在给定输入 x *的情况下,预测函数输出 y **
简而言之,GP 通过将底层真函数 y ( x )建模为高斯随机过程的实现来工作。正是这种统计观点使 GP 能够预测复杂的投入产出模式,并在此过程中估计预测的不确定性。我们将在接下来的章节中对此进行更详细的阐述。
1.1 高斯过程中的“过程”
其名称中的“进程”部分指的是 GP 是一个随机进程的事实。简单来说,一个随机过程就是一个函数 f (。)具有以下属性:
- 在任意位置 x , f ( x )为随机变量;
- 在不同的位置 x ᵢ和 x ⱼ,随机变量 f ( x ᵢ)和 f ( x ⱼ)相关;
- f ( x ᵢ)和 f ( x ⱼ)之间的关联强度取决于 x ᵢ和 x ⱼ.之间的距离一般来说,随着 x ⱼ远离 x ᵢ,它们的关联强度会衰减。
这里的关键要点是,我们可以将随机过程解释为相关随机变量的无限集合。
那么,我们应该用什么概率分布来描述那些随机变量呢?
1.2 高斯过程中的“高斯”
其名字中的“高斯”部分表示 GP 用高斯分布(或正态分布)来表征随机过程。
为了描述具有高斯分布的单个随机变量,我们只需要一个平均值和一个方差值。然而,为了描述包含无限数量随机变量的随机过程,我们需要将高斯分布升级为高斯随机过程。
形式上,一个高斯随机过程 f (。)的特征是均值函数 μ ( x )和协方差函数σK*(x, x *)。这里, σ 表示整体进程方差, K ( x , x *)是相关函数,也称为核函数**。当 x = x , K ( x ,x)= 1;当x≦x**, K ( x , x *)表示 f ( x )与 f ( x )之间的相关性。
配备了 μ ( x )和σ**K*(x, x )的符号,我们可以介绍一个高斯随机过程 f (。):
- 对于单个位置 x , f ( x )服从高斯分布,即f*(x)~N(μ(x),σ);*
- 对于任意一组位置[ x ₁、 x ₂,…、 x ₙ、f=[f(x₁)、 f ( x ₂),…、 f ( x
1.3 GP 的“世界观”
回想一下,在本节开始时,我们提到 GP 通过将底层真函数 y ( x )建模为高斯随机过程的实现来工作。这意味着 GP 模型将训练数据的观察到的标签[ y ₁、 y ₂,…、 y ₙ]视为从上述多元高斯分布中随机抽取的。**
*因此,我们能够通过推断最可能产生观察到的标签的 μ ( x )、 σ 和 K ( x 、 x )来训练 GP 模型。这通常通过最大似然估计来完成。随后,我们可以用训练好的 GP 模型对未知地点进行预测,并估计相关的预测不确定性。
乍一看,通过引入随机过程来模拟确定性函数值,从而增加额外的复杂性,这似乎是违反直觉的(y₁、y₂,…、yₙ).事实上,这种治疗方法源于贝叶斯统计。这里,随机过程假设编码了我们对函数形式 y(x)的先验信念。这个方向我们就不深入了。对于 GP 方法的贝叶斯解释,看一看墨菲的书第 15 章。
2.核函数
在我们深入研究编码之前,让我们先讨论一下 GP 模型的关键要素:内核函数。之前我们只陈述了它的一般形式 K ( x ᵢ, x ⱼ),没有给出具体的例子。我们将填补这一部分的空白。
2.1 核心特征
在 GP 建模设置中,核函数测量两个不同预测之间的“相似性”,即,较高的核函数值意味着两个预测更相似,反之亦然。
内核函数必须满足某些要求。其中之一是内核函数 K ( x ᵢ, x ⱼ)必须能够构造一个对称和正定的相关矩阵。
实际上,内核函数经常被假设为静态*。这意味着核函数值仅取决于输入之间的距离,即 K ( x ᵢ,xⱼ)=k(xᵢ-xⱼ).*
此外,当输入具有两个或更多特征时,我们通常假设多维核函数 K ( x ᵢ, x ⱼ)是针对每个输入特征的一维核函数的一系列乘法:
其中 m 表示特征总数,即输入尺寸。这种处理是有益的,因为它允许为
单个输入特征定制相关结构。
2.2 高斯核
常见的核函数包括立方核、指数核、高斯核和 Matérn 核。本文将使用高斯核,这是最流行的选择之一。
一维高斯核表示为:
其中 θ 是控制相关强度的核参数。高斯核的一个 m 维版本被表示为:
这只是一维高斯核的一系列乘法。这里,我们有内核参数θ**=**θ₁、 θ ₂,…、 θ ₘ].
2.3 GP 的可解释性
内核参数 θ 是 GP 模型可解释性的关键,因为它表明了特征在进行预测时的重要性。让我们详细说明一下。
首先,让我们关注一维情况,其中我们只有一个特征和一个关联的 θ 。下图显示了 θ 的选择如何影响相关性。
图 2θ对相关强度的影响。(图片由作者提供)
当 θ 值较低时,遥远地点的预测保持高水平的相关性。这意味着无论 x 值如何,底层函数都会产生相似的输出。换句话说,特征 x 没有那么好预测。
同样的推理也适用于多特征设置:如果第 k 个特征的 θ 值相当低,那么当沿着第 k 个维度移动时,底层函数将产生类似的输出值。由于输出对第 k 个特征不太敏感,我们可以得出结论,在进行预测时,第 k 个特征并不那么重要。
因此,通过对( θ ₁, θ ₂,…, θ ₘ)的值进行排序,我们可以对特征的重要性进行排序,这有助于进行敏感性分析或降维。
高斯过程的基础知识到此为止。在下文中,我们将从实现的角度来研究 GP,并介绍缺失的理论和代码。
3.GaussianProcess
阶级
让我们从头开始编写一个GaussianProcess
类吧!为了帮助你浏览与 GP 建模相关的方程,我为你准备了这个备忘单。
3.1 课程概述
下面是对GaussianProcess
类的方法和属性的总结。这个类通过使用.fit()
方法训练模型和使用.predict()
方法进行预测来模仿scikit-learn
风格。这种一致性使得将开发的GaussianProcess
估计器与其他scikit-learn
函数进一步集成成为可能,如Pipeline
、GridSearchCV
等。
图 3 GaussianProcess
类概述。(图片由作者提供)
我们将在下面的小节中讨论每一种方法。
3.2 包装
首先,我们加载所有必需的包。
我们将主要使用numpy
进行矩阵操作,使用matplotlib
进行数据可视化。此外,我们需要
[scipy.optimize.minimize](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html)
:进行优化;[scipy.optimize.Bounds](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.Bounds.html)
:指定优化的参数界限;[pyDOE.lhs](https://pythonhosted.org/pyDOE/randomized.html)
:为优化器生成随机起始点。这里,我们采用拉丁超立方体采样方法,因为它善于生成均匀分布的随机样本。
4.初始化 GP 模型
在本节中,我们开发GaussianProcess.__init__(self, n_restarts, optimizer, bounds)
。
为了训练 GP 模型,我们使用多起点优化策略来估计模型参数。因此,我们需要指定我们希望优化器尝试多少个起始点,以及这个优化器应该使用哪个算法。
5.构建相关矩阵
在本节中,我们开发GaussianProcess.Corr(self, X1, X2)
,它计算一对特征矩阵 X1 和 X2 之间的相关矩阵。因为计算相关矩阵涉及训练和预测(稍后将讨论),所以有一个专用函数来实现这个目标是有益的。
下面给出了如何使用高斯核来构建 X1 和 X2 的相关矩阵的图示:
图 4 是构造相关矩阵的示意图。(图片由作者提供)
以下代码可以实现所需的功能:
6.训练一个全科医生模型(理论)
6.1 GP 模型参数
一个工作的 GP 模型需要一个均值函数 μ ( x )、过程方差 σ 和核参数向量 θ 。实际操作中, μ ( x )被简单假设为常数,即 μ ( x ) = μ。这种处理极大地简化了数学,并且不会对模型的准确性造成太大的伤害。我们将在编码部分实现一个平均值不变的 GP。
所以,训练一个 GP 模型意味着估计 μ 、 σ 、 θ 。但是我们到底应该怎么做呢?介绍最大似然估计。
6.2 最大似然估计
简单地说,这种估计方法通过找到一个特定的组合 μ 、 σ 和 θ 来工作,这样观察训练实例的标签( y ₁、 y ₂,…、 y ₙ、 x ₁、 x ₂,…、x的可能性**
由于 GP 假设[ y ₁,…, y ₙ]是由多元高斯分布生成的,我们可以很容易地根据多元高斯分布的概率密度函数来表示可能性 L :
其中y*=[y₁,…, y ₙ], 1 为个例向量, K 为训练实例的关联矩阵。*
在实践中,我们倾向于最大化可能性的对数 L 以避免舍入误差:
幸运的是,如果我们将对数似然相对于 μ 和 σ 的导数设置为零,则 μ 和 σ 的最佳值存在解析表达式:
然而,对于T5【θ来说,由于它嵌套在相关矩阵中,因此推导最优 θ 的解析式是不可行的。因此,我们需要采用一种优化算法来寻找一个使可能性最大化的θL。
我们可以通过将最优的 σ 代入 L 来简化 L 方程。经过一些代数运算后,我们得到了下面的优化问题:
在本文中,我们采用多起点策略来估计最优 θ 。基本上,这种策略会多次运行优化器,每次都从不同的初始 θ 值开始。最后,选择产生最佳目标函数值的 θ 值作为最终优化结果。
7.训练 GP 模型(代码)
在本节中,我们开发了GaussianProcess.likelihood(self, theta)
和GaussianProcess.fit(self, X, y)
方法。
这里值得一提的是:从实现的角度来看,在对数尺度上搜索 θ 有利于加速优化算法的收敛。因此,在下面的代码中,我们将进行优化( μ 、 σ 和 10^t59】θ)。 θ 的合适搜索范围是[-3,2],其转化为在 0.001 和 100 之间变化的相关长度。
7.1 似然函数
首先,我们开发GaussianProcess.Neglikelihood(self, theta)
来计算负的可能性。在下面的代码片段中,self。x 和 self.y 构成了训练数据集。它们分别是 shape (n_samples,n_features)和(n_samples,1)的数组。
这里有几件事值得一提:
- 我们在 K 的对角线上加一个小项(1e-10)。这一项也称为“块金”项,它有助于在求 K 的倒数时缓解数值不稳定问题;
- 我们返回计算的似然值的负值。这一步是必要的,因为我们稍后将使用
scipy.optimize.minimize
,最小化负似然值相当于最大化原始似然值; - 为了便于理解,我天真地使用了
np.linalg.inv()
和np.linalg.det()
来计算**的倒数和行列式K*。然而,在实践中,这些处理可能是耗时的,并且可能导致数值不稳定。更可靠的替代方案是计算 K 的乔莱斯基因式分解,并使用获得的下三角矩阵进行进一步的矩阵乘法和行列式计算。请参阅配套的笔记本了解这种可靠的实现方式。***
7.2 模型拟合功能
现在我们开发GaussianProcess.fit(self, X, y)
来实现模型拟合。在下面的代码片段中,我们使用拉丁超立方体方法为优化器的运行生成起点 θ 。由于pyDOE.lhs
只生成[0,1]内的随机样本,我们需要将样本缩放到我们的目标范围[-3,2]。
8.GP 模型预测(理论)
*现在我们知道了如何训练 GP 模型,让我们换个角度,看看如何用训练好的 GP 模型进行预测。我们的目标是预测测试点 x *的基础函数值。我们将预测表示为 f 。
回想一下,我们在开头提到 GP 预测 f 不是一个确定值,而是一个遵循高斯分布的随机变量。这个结果实际上是通过计算*【y】*,**即训练实例的观察标签条件下的 f *的分布得到的。在贝叶斯语言中,我们是在计算 f 的后验分布。
下面,我们讨论如何计算 f 的这个条件分布。我们就从 f 和 y 的联合分布开始,即**P(y, f )。从那里,我们可以导出期望的条件分布P(f |y)。**
8.1 测试/训练数据的联合分发
*首先,注意,根据高斯随机过程的定义,我们可以将联合分布P(y, f )表示为多元高斯分布:
*其中 K 是训练实例的相关矩阵, k 是测试实例和训练实例之间的相关向量:
3.2 测试数据的条件分布
第二步,我们推导出P(f |y),它描述了在给定观察到的标签 y 的情况下 f 是如何分布的。
高斯随机过程的一个很好的性质是,*f的期望条件分布也是高斯分布,即f** |y~ N*(μ*,σ*),其中**
f |y~ N*(μ*,σ*)充分刻画了 x *处的 GP 预测。在实践中,我们用均值 μ 作为预期预测值,用方差σ来表示预测的不确定性。
从它们的联合分布 P( f, y )中导出 P( f| y )在分析上是容易处理的。为了让我们的讨论更集中,我跳过冗长的代数,让读者参考这个 [StackExchange 页面](http://Stackexchange page)了解更多细节。**
9.GP 模型预测(代码)
在本节中,我们开发了GaussianProcess.predict(self, X_test)
方法来启用 GP 模型预测。
在下面的代码片段中,我们打算一次预测多个测试实例。结果,在第 17 行中,我们将在所有测试实例和训练实例之间有一个相关矩阵 k (而不是理论部分所述的仅仅一个相关向量)。因此,在第 20 行和第 23 行,计算出的 f 和 SSqr 现在是数组。
最后,让我们开发GaussianProcess.score(self, X_test, y_test)
方法,以均方根误差来评估模型的准确性。
10.基准
这一节将我们开发的 GP 类投入使用,并测试它逼近函数的能力。我们将首先在 1D 函数上测试 GP 类。稍后,我们将测试扩展到 2D 案例。
10.1 1D 试验
对于本案例研究,我们的目标是训练一个 GP 模型,它可以精确地逼近以下函数:
这个函数绘制如下。正如我们所看到的,这个函数是高度非线性的,并且在 x =0.6 之前和之后具有不同的“活动性”水平。这些特征在构建全球精确模型方面提出了挑战。
图 5 1D 测试函数。(图片由作者提供)
**为了训练 GP 模型,我们选择分布在[0,1]范围内的 8 个训练实例。在下面的代码片段中,我们首先创建一个训练数据集和一个测试数据集。然后,我们初始化一个GaussianProcess
实例,并采用一个 L-BFGS-B 算法来寻找最优的 θ。因为我们使用的是多起点优化策略,所以我们希望优化器通过每次使用不同的初始点来运行 10 次。在训练数据集上训练模型之后,该模型用于预测测试数据标签。
预测结果如下所示。我们可以看到 GP 的预测与真实函数几乎完全相同。图中还显示了 GP 预测的 95%置信区间,计算为*μ(x)+/-1.96*σ**(x)。我们可以看到真正的函数完全位于置信带内。**
图 6 GP 预测设法捕捉潜在的测试函数。(图片由作者提供)
因此,我们可以得出结论,开发的 GP 类工作正常,GP 模型足够精确,可以近似当前的 1D 测试函数。
10.2 2D 试验
在这个案例研究中,我们的目标是训练一个 GP 模型,它可以精确地逼近 2D·罗森布罗克函数:
这个函数绘制如下。
图 7 2D 测试函数。(图片由作者提供)
为了训练这个 GP 模型,我们总共选择了 25 个训练实例,如下所示。这些训练样本由拉丁超立方体采样器生成。
图 8 训练样本。(图片由作者提供)
下面的代码为 GP 模型定型,并对测试数据网格进行预测。通常在[0,1]范围内调整 GP 输入特性。因此,我们采用一个MinMaxScaler
来进行缩放,并将其集成到一个流水线中。
最后,利用.score
方法对模型预测精度进行了检验。计算的 RMSE 误差是 2.44,考虑到我们的目标函数从 0 到 2400 变化,这是相当低的。为了直观地评估模型的准确性,我们可以在等值线图中绘制 GP 预测:
图 9 GP 预测成功地恢复了真实的 2D 测试函数。(图片由作者提供)
事实上,GP 近似值与底层的真实函数几乎相同,这表明经过训练的 GP 模型非常准确,并且我们对GaussianProcess
类的实现工作正常。
11.外卖食品
在本文中,我们已经讨论了高斯过程建模方法的基本理论。通过从头构建一个原型,我们将理论转化为代码,并从实际实现的角度深入到本质。
肯定有更多关于如何从基础 GP 到更高级版本并实现迷人分析的内容。这篇文章涵盖了一些高级概念:
**
关于作者
我是一名博士研究员,从事航空航天应用的不确定性分析和可靠性分析。统计学和数据科学是我日常工作的核心。我喜欢分享我在迷人的统计世界中学到的东西。查看我以前的帖子以了解更多信息,并在 中 和Linkedin上与我联系。**
实现和训练文本分类转换器模型——简单的方法
了解如何用几行代码实现和训练文本分类转换器模型,如 BERT、DistilBERT 等
作者图片
文本分类无疑是自然语言处理最常见的应用。而且,像大多数 NLP 应用一样,变压器模型近年来在该领域占据了主导地位。在本文中,我们将讨论如何使用一个 Python 包(我是名为快乐转换器的主要维护者)用几行代码实现和训练文本分类转换器模型。Happy Transformer 建立在 Hugging Face 的 transformers 库之上,允许程序员只需几行代码就可以实现和训练 Transformer 模型。
预训练模型
在拥抱脸的模型分发网络上有 100 种预训练的文本分类模型可供选择。因此,我建议在你花太多时间担心训练模型之前——看看是否有人已经为你的特定应用微调了模型。例如,我已经制作了关于如何为情感分析和仇恨言论检测实现预训练变压器模型的内容。在本教程中,我们将实现一个名为 finbert 的模型,它是由一家名为 Prosus 的公司创建的。该模型检测金融数据的情绪。
装置
PyPI 上有 Happy Transformer,因此我们可以用一行代码安装它。
pip install happytransformer
实例化
让我们导入一个名为 HappyTextClassification 的类,我们将使用它来加载模型。
from happytransformer import HappyTextClassification
在这里,我们可以使用 HappyTextClassification 类为模型实例化一个对象。第一个 position 参数指定模型的类型,并且全部大写。例如,“BERT”、“ROBERTA”和“ALBERT”都是有效的模型名称。第二个位置参数表示模型的名称,可以在模型的网页上找到。最后一个参数是一个名为“num_labels”的参数,它指定了模型拥有的类的数量。在这种情况下,模型有三个标签:“正面”、“中性”和“负面”
重要提示:实例化模型时,不要忘记设置 num_labels。否则,可能会发生错误。
happy_tc = HappyTextClassification("BERT", "ProsusAI/finbert", num_labels=3)
使用
我们可以用一行代码通过“classify_text”方法对文本进行begin 分类
result = happy_tc.classify_text("Tesla's stock just increased by 20%")
让我们把结果打印出来,以便更好地理解它。
print(result)
输出:TextClassificationResult(label = ’ positive ',score=0.929110586643219)
如您所见,the 的输出是一个数据类,有两个变量:“标签”和“分数”标签是一个字符串,用于指示输入被分类到哪个类。“分数”变量指定模型以浮点形式分配给答案的概率。我们不能孤立这两个变量。
print(result.label)
print(result.score)
结果:
正向
0.929110586643219
这是另一个显示负输入输出的例子。
result = happy_tc.classify_text("The price of gold just dropped by 5%")
print(result.label)
print(result.score)
输出:
负
0.8852565288543701
培训—自然语言处理情感分析
现在让我们来讨论培训。我们将训练一个模型来检测与 NLP 相关的文本的情感。我们将只使用两个例子进行训练——这当然不足以稳健地训练一个模型。但是,这只是为了演示。
我们必须创建一个包含两列的 CSV 文件:文本和标签。文本列包含我们希望分类的文本。标签列包含大于或等于 0 的整数形式的标签类型。下表给出了一个培训 CSV 的示例。
下面是生成上述 CSV 文件的代码:
import csv
cases= [("Wow I love using BERT for text classification", 0), ("I hate NLP", 1)]
with open("train.csv", 'w', newline='') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(["text", "label"])
for case in cases:
writer.writerow([case[0], case[1]])
首先,我们将安装一个普通版本的 DistilBERT 作为起点。您还可以使用其他模型,如 BERT、ALBERT、RoBERTa 等。访问拥抱脸的模特分销网络获取更多模特。
happy_tc = HappyTextClassification(model_type="DISTILBERT", model_name="distilbert-base-uncased", num_labels=2)
然后,我们可以使用新实例化的类简单地调用方法“train”。
happy_tc.train("train.csv")
就是这样!我们刚刚训练了模型。我们现在可以像在上一节中一样继续使用它。所以,举个例子,你现在可以像以前一样调用“happy_tc.classify_text()”,新微调的模型就会被使用。
自定义参数
通过使用一个名为“TCTrainArgs”的类,我们可以很容易地修改学习参数,比如时期数、学习速率等等。让我们导入 TCTrainArgs。
from happytransformer import TCTrainArgs
现在,我们可以使用 TCTrainArgs 类创建一个对象来包含训练参数。在这里你可以修改的参数列表。让我们将训练时期的默认数量从 3 增加到 5。
args = TCTrainArgs(num_train_epochs=5)
让我们像以前一样调用 happy_tc 的 train 方法,但是这次将 args 对象传递给该方法的 args 参数。
happy_tc.train("train.csv", args=args)
好了,我们刚刚修改了学习参数!
评价
HappyTextGeneration 对象有一个内置的方法,允许您快速评估您的模型。首先,按照培训中讨论的方式格式化数据,然后调用。eval()"方法。为了简单起见,让我们使用训练文件来评估。
result = happy_tc.eval("train.csv")
print(result)
结果:eval Result(loss = 0.2848379611968994)
然后,我们可以像这样隔离损失变量:
print(result.loss)
输出:0.284879611968994
我建议你用总体数据的一部分来训练,另一部分来评估。然后,在训练前后评估你的模型。如果损失减少,那就意味着你的模型学会了。你也可以创建你的数据的第三部分来运行实验,以找到最佳的学习参数——但这是另一个时间的话题。
结论
就是这样!您刚刚学习了如何实现和训练文本分类转换器模型。使用 Happy Transformer 只用几行代码就能完成这么多工作,这真是令人惊讶。
相关文章
这是我最近在《走向数据科学》上发表的一篇相关文章。它涵盖了如何使用零镜头文本分类模型来标记训练数据。这样,您可以在没有任何标记数据的情况下微调小型监督模型。
本文中的代码:
https://colab . research . Google . com/drive/1 jq 3o 8 whs gel 994 nos 14 qyv 98 JT 5 we-pU?usp =共享
原载于 2021 年 6 月 14 日https://www . vennify . ai。
用 Python 实现策略迭代——一个最小的工作示例
了解这个经典的动态规划算法,以优化解决马尔可夫决策过程模型
几天前我写了一篇关于价值迭代(理查德·贝尔曼,1957),今天是政策迭代的时候了(罗纳德·霍华德,1960)。策略迭代是求解马尔可夫决策过程模型的精确算法,保证找到最优策略。与值迭代相比,一个好处是有一个明确的停止标准——一旦策略稳定,它就是可证明的最优策略。然而,对于具有许多状态的问题,它通常具有较高的计算负担。
策略迭代是许多现代强化学习算法的基础,更具体地说是策略近似算法的类。事实上,bertsekis(1996)将其解释为一个行动者-批评家模型,将政策更新与价值函数相结合。在讨论最接近的策略优化和自然策略梯度之前,先掌握这个基本算法是有意义的。
[## 强化学习的四个策略类别
towardsdatascience.com](/the-four-policy-classes-of-reinforcement-learning-38185daa6c8a)
策略迭代
与价值迭代相比,策略迭代更加广泛。萨顿和巴尔托(2019)将其分为三个步骤。
政策迭代算法[来源:萨顿&巴尔托(公开发表),2019]
步骤 1 — 初始化 —设置(可能任意)值函数和策略。值函数给出了处于每个状态的感知值,策略是返回任何给定状态的动作的规则。
在步骤 2 — 策略评估 —以非常类似于值迭代的方式确定每个状态的值。然而,我们不是通过最大化所有操作来确定值,而是简单地使用当前策略来计算值。简而言之,我们将行动的价值a=π(s)
(直接回报r
+下游价值V(s’)
)乘以转移概率p
。更多细节——比如误差容限θ
——请查看关于价值迭代的文章。
步骤 3 — 策略改进 —寻求使用主流价值函数来改进策略。对于每个状态,它验证当前策略建议的行动是否确实是价值最大化行动。如果没有,则更新策略并重复步骤 2。该算法在这两个步骤之间交替,直到策略保持稳定。此时,达到了最优策略,算法终止。
对于价值迭代和策略迭代之间的直接比较,请在这里并排查看它们。
策略迭代(左)和价值迭代(右)的比较。关键区别在于策略迭代分离了评估和策略更新。[改编自萨顿&巴尔托出版社(公开发行),2019 年]
一个最小的工作示例
编码示例的时间到了。我喜欢用非常简单的问题来演示算法,正如下面的简要概述所证明的那样。
问题是
感兴趣的问题是一维世界(一排瓷砖)。在中间,有一个瓷砖作为终止状态。落在这块瓷砖上产生+10 的奖励,在瓷砖间走动每移动一次花费-1。代理可以决定向左或向右移动,但最终有 10%的时间走错了方向。有了直接回报、预期下游回报和转移概率,它就具备了 MDP 的基本要素。
该算法
Python 算法与前面展示的数学过程没有太大的不同。注意,最大迭代次数是为了限制计算量而进行的简化。特别是,如果两个或多个策略执行得同样好,算法可能会在它们之间无休止地循环。
一些实验
是时候做点实验了。我将提供一些计算示例以及对结果的一些思考。
我们设置所有的值函数V(s)=0
,并在π=[0,0,0,0,0]
初始化策略(即总是向左移动)。
然后,我们转向政策评估。重申:在这一步中,我们根据当前策略 π
确定状态值。策略评估是一个迭代步骤,重复直到所有值都低于错误阈值θ
。在这种情况下(状态 0,迭代 1),初始估计值V(0)
是 0。由于直接奖励是-1,误差Δ
也是 1,我们需要重复。
第一步政策评估。在策略评估过程中,我们根据现行策略计算每个状态的值。重复评估步骤,直到值在预定的误差范围内。
达到正确的价值观需要一些时间。如果我们取θ= 1e-10
,对于所有状态,我们需要在Δ<θ
之前进行不少于 185 次迭代。我们获得V=[-8.62, -7.08, 10.00, 7.61, 5.68]
。
现在,我们继续讨论政策改进。我们测试我们的初始策略(“始终向左”)是否确实是最优的— *剧透警告:*根据我们刚刚确定的值,它不是最优的。
状态 1 的第一个政策改进步骤。给定之前确定的状态值,我们验证当前策略是否是最佳的,或者不同的操作会产生更好的结果。如果我们可以通过采取不同的措施来改进当前策略,我们就更新策略并返回到策略评估。
显然,在最左边的瓷砖上,向右比向左好。因此,不符合停止标准。我们更新π
并返回到策略评估步骤来重新计算值。
最终,我们应该到达一个政策不再改变的点。在这种情况下,它只是一个循环。更新策略后,我们在策略评估期间计算新的状态值(16 次迭代),并且在随后的策略改进步骤中不进行任何策略更改。算法已经终止,产生了π=[1,1,0,0,0]
。
除此之外,其实没什么好说的。在θ
和步骤 2 和步骤 3 之间的循环数之间有一个计算上的权衡,但是对于这个特殊的问题,它几乎无关紧要。
虽然在许多方面与值迭代相似,但注意策略迭代返回一个策略,而值迭代返回一组值函数。当转向近似(强化学习)算法时,这种区别变得相关。你可以有一个好的政策,而不需要很好的价值函数(或者甚至不知道它们),你也可以有准确的价值函数,而不需要对政策本身有明确的理解。
最后的话
与值迭代一样,策略迭代是一种基本算法,许多近似算法都是从它派生出来的。在真正进入强化学习的世界之前,确保理解策略迭代。
您可能感兴趣的一些更简单的工作示例:
参考
贝尔曼河(1957 年)。马尔可夫决策过程。数学与力学学报, 6 (5),679–684。
Bertsekas 博士和 Tsitsiklis,J. N. (1996 年)。神经动态规划。雅典娜科技公司。
霍华德,R. A. (1960)。动态规划和马尔可夫过程。
萨顿和巴尔托(2019 年)。强化学习:简介。麻省理工出版社。
用 Python 实现值迭代——一个最小的工作示例
掌握简单和经典的动态规划算法,寻找马尔可夫决策过程模型的最优解
在人工智能时代,精确算法并不完全是热。如果机器不能自己学习,那还有什么意义呢?为什么要费事去解决马尔可夫决策过程模型,而这些模型的解决方案无论如何都无法扩展呢?为什么不直接研究强化学习算法呢?
学习经典的动态编程技术仍然是值得的。首先,它们在实践中仍然被广泛使用。许多软件开发工作将动态编程作为面试过程的一部分。尽管您可以列举的状态和动作只有这么多,但您可能会惊讶于现实世界中的问题仍然可以通过优化来解决。
第二,即使只对强化收入感兴趣,该领域的许多算法都牢牢植根于动态编程。在强化学习中可以区分四个策略类别,其中之一是价值函数近似。在使用这些方法之前,理解经典的值迭代算法是非常重要的。幸运的是,这篇文章恰好概述了这一点。
[## 强化学习的四个策略类别
towardsdatascience.com](/the-four-policy-classes-of-reinforcement-learning-38185daa6c8a)
价值迭代
价值迭代算法的优雅是值得钦佩的。它只需要几行数学表达式,而不是更多的代码行。让我们看看萨顿&巴尔托的开创性外延:
价值迭代算法[来源:萨顿&巴尔托(公开资料),2019]
直觉相当简单。首先,你为每个状态初始化一个值,例如 0。
然后,对于每个状态,你通过将每个动作的奖励a
(直接奖励r
+下游值V(s’)
)乘以转移概率p
来计算 值 V(s)
。
假设我们确实初始化为 0,并且直接奖励r
是非零的。差异将直接在差异表达式|v-V(s)|
中可见,其中v
是旧的估计值,V(s)
是新的估计值。因此,误差Δ
将超过阈值θ
(一个小值),新的迭代随之而来。
通过执行足够多的迭代,算法将收敛到一点,在该点|v-V(s)|<θ
对于每个状态。然后,您可以解析argmax
来找到每个状态的最佳操作;知道真正的价值函数V(s)
等同于拥有最优策略π
。
请注意,如果θ
设置过大,则不能保证最优。出于实际目的,合理的小误差容限就可以了,但是对于我们当中的数学爱好者来说,记住最优性条件是有好处的。
一个最小的工作示例
数学已经过时了,让我们继续编码的例子。把重点放在算法上,问题极其简单。
问题是
考虑一个一维世界(一排瓷砖),只有一个终止状态。进入终结状态获得+10 奖励,其他动作花费-1。代理可以向左或向右移动,但是——为了不使它变得太微不足道——代理在 10%的时间里向错误的方向移动。这是一个非常简单的问题,但是它有(I)直接回报,(ii)预期下游回报,和(iii)转移概率。
该算法
Python 算法非常接近萨顿&巴尔托提供的数学大纲,所以不需要扩展太多。完整的代码符合一个要点:
一些实验
好吧,那就做些实验。我们首先详细展示了该算法的两次迭代。
首先,我们设置v
等于V(0)
,等于 0: v=V(0)=0
接下来,我们更新V(0)
。注意r
对于每个状态是固定的;我们实际上只通过s’
对下一组状态求和。
值迭代步骤 1,状态 0[作者图片]
对于这样一个小问题来说,这似乎是很大的计算工作量。事实上,很容易理解为什么动态编程不能很好地扩展。在这种情况下,所有值V(s)
仍然为 0——正如我们刚刚开始的那样——所以估计的状态值V(0)
就是直接奖励-1。
让我们再尝试一个,更进一步。同样的计算,但是现在值V(s)
已经在几次迭代中更新了。我们现在有V=[5.17859, 7.52759, 10.0, 7.52759, 5.17859]
。同样,我们插入这些值:
值迭代步骤 4,状态 0[作者图片]
因此,我们将V(0)
从 5.18 更新到 5.56。误差将是Δ=(5.56–5.18)=0.38
。反过来,这将影响其他状态的更新,并且对于所有状态,该过程持续到Δ<θ
。对于状态 0,最佳值是 5.68,在 10 次迭代内命中。
这里要测试的最有趣的参数是误差容限θ
,它影响收敛前的迭代次数。
各种误差容差θ所需的迭代次数。容差越低,算法收敛前需要的迭代次数就越多。
最后的话
值迭代是强化学习的基石之一。很容易实现和理解。在转向更高级的实现之前,请确保掌握这个基本算法。
您可能感兴趣的一些其他最小工作示例:
参考
萨顿,理查德 s 和安德鲁 g 巴尔托。强化学习:简介。麻省理工学院出版社,2018。
LSTM 层的实现差异:TensorFlow 与 PyTorch
在张量流 LSTM 层和 PyTorch LSTM 层之间画平行线。
Tensorflow 和 Pytorch 是深度学习中使用最广泛的两个库。当涉及到实现神经网络时,这两个库有不同的方法。这两个库都有很大的不同。通常,TF 由于其更好的优化而更倾向于开发生产就绪的模型,Pytorch 由于其更“pythonic 化”的语法和急切的执行而更倾向于研究工作。但是有了 Torchscript 和 TF 2.0,两个库的差距缩小了。
这两个图书馆都有很好的社区支持和积极的贡献。因此,我们可以很容易地实现不同类型的神经网络,而在这两个库中没有任何重大问题。但是这两个库在架构上有所不同。因此,在实现神经网络时,我们需要关注某些差异。
其中一个区别是层 API。在这两个库中,可以使用子类方法或顺序模型 API 方法来设计神经网络(NN)。子类化方法是这两种方法中更受欢迎的,因为它是面向对象的和可扩展的。在这些库中实现神经网络时,我们可以使用已经设计好的层——线性(完全连接)层、卷积层、递归层等。并将它们扩展到我们的模型中。在 TF 中,我们使用tensorflow.keras.layers
,在 Pytorch 中,我们使用torch.nn
来访问这些层。正如我前面提到的,这些层的实现有细微的差别。大多数时候,它们都是次要的,直观的。但是在 LSTM(长短期记忆)层,这些差异有些主要和显著。在 LSTM 图层中,Pytorch 和 TF 的图层参数化方式、参数的默认值以及图层的默认输出都有很大不同。
在这篇文章中,我将尝试解释这两个库中 LSTM 层的区别。如果有人提到在一个库中实现的 NN(带有 LSTM 层)并试图在另一个库中复制它,我希望这能有所帮助。
LSTM 介绍
我添加这一部分只是为了完善和复习(如果有人需要的话)。但是如果你正在尝试理解使用 LSTM 层时的实现差异,那么我希望你已经有了深度学习的背景,并且知道 LSTMs 的基础知识。所以你可以跳过这一部分。
递归神经网络(RNN)是一种特殊类型的神经网络,用于从序列数据中学习。传统的神经网络会接受一个输入,并仅基于该输入给出预测。它不会查看以前的输入/输出并做出决定。但是当预测序列数据中的下一个值时(例如,句子,一个城市的日平均温度),我们不能只看当前的数据点。我们必须把这个系列的行为(到目前为止)作为一个整体来看待,并获得“系列的背景”来做出有意义的预测。这就是 rnn 的专业用途。rnn 用于诸如语言建模、机器翻译、序列预测等任务。
图 1 示出了 RNN 如何将当前值和先前的横向输出作为输入。递归机制传递前一时间步的横向输出(如aᵗ
)。预计(并证明)RNN 层将学会捕捉横向输出中序列的“上下文”。请注意,为了更好地理解,图 1 (b)中所示的 RNN 的展开版本将相同的 层描绘为副本,并不表示多个层。
图 1 — (a)具有环路的一般 rnn(z⁻表示单位时间延迟)。(b)相同的 RNN,时间序列循环展开(由作者绘制,灵感来自 Chris 的作品)
RNN 的设计原则期望从系列中较早的值得到的“上下文”沿着序列持续很长一段时间。但是在实际场景中观察到,RNN 人学习序列“上下文”的能力随着距离的增加而减弱。也就是说,rnn 不足以捕获序列中的长期依赖性。长短期记忆(LSTM)网络是 RNN 的一个特殊版本,它的引入是为了在序列中保存长期的“上下文”信息。LSTMs 的固有设计使它们能够通过所谓的单元状态(用cᵗ
表示)来捕捉长期环境。下图(图 2)显示了一个典型的 LSTM 图层。我使用的图表是基于 Chris Olah 在关于理解 LSTMs 的博客文章中使用的图表。(很精彩的博文。一定要去看看。)
图 2——一个基本的 LSTM 图层(由作者绘制,灵感来自克里斯的作品)
正如 Chris 在他的博客中解释的那样,细胞状态可以被认为是一个传送带,它从序列的开始到结束携带着长期的“上下文”。在基于当前输入(*x*ᵗ
)和先前输出(hᵗ⁻¹
)的每个时间步,LSTM 层决定从“上下文”中忘记什么信息以及将什么信息添加到“上下文”中。遗忘由遗忘门处理(Γ𝒻
)。Γ𝒻
可以被认为是一个应用于cᵗ⁻¹
的面具。如果Γ𝒻
中某个位置的值为 0(或更接近),当与cᵗ⁻¹
相乘时,从上下文中删除该位置的信息。如果掩码的值在某个其他位置为 1(或更接近),在与cᵗ⁻¹
相乘后,它允许该位置的信息在上下文中保持不变。操作Γ𝒻.*cᵗ⁻¹
( )。 —逐元素乘法*)确保上下文中不需要的部分被忽略。向单元状态添加新信息分两步处理。首先,基于*x*ᵗ
和hᵗ⁻¹
,通过激活tanh
的子层创建候选向量(c̃ᵗ
)。然后,更新门(Γᵤ
)生成一个掩码(就像在遗忘门中一样),该掩码决定将候选向量的哪一部分添加到单元状态中。然后将Γᵤ.*c̃ᵗ
加到cᵗ⁻¹
上生成cᵗ
。最后,为了生成输出,我们让cᵗ
通过一个tanh
非线性并使用它。但是为了决定给出哪一部分,我们使用了更新门(Γₒ
)。Γₒ.*tanh(cᵗ)
作为 LSTM 层在时间步长t
的输出给出。
请注意,cᵗ⁻¹
和hᵗ⁻¹
都是作为 LSTM 层的横向输入给出的,相比之下,普通 RNN 层只给出了aᵗ⁻¹
。
如果你需要更多关于 RNN 和 LSTM 原理的信息,我会推荐你去吴恩达的序列模型课程的第一周(可以免费旁听)并阅读克里斯·奥拉的这篇精彩的博客文章。
张量流中的 LSTM 层
在撰写本文时,Tensorflow 版本是 2.4.1
在 TF 中,我们可以使用tf.keras.layers.LSTM
并创建一个 LSTM 层。初始化 LSTM 层时,唯一需要的参数是units
。参数units
对应于该层的输出特征数量。用我们的术语来说就是units
= nₕ
。nₓ
将根据前一层的输出进行推断。因此,该库可以初始化 LSTM 层中的所有权重和偏置项。
TF LSTM 层期望一个三维张量作为正向传播期间的输入。这个输入应该是(batch, timesteps, input_features)
的形状。这显示在下面的代码片段中。假设我们正在使用这个 LSTM 层来训练一个语言模型。我们的输入将是句子。第一个维度对应于我们使用多少个句子作为一批来训练模型。第二个维度对应于一个这样的句子中有多少单词。在实际设置中,每个句子的字数因句而异。因此,为了批量处理这些句子,我们可以选择训练语料库中最长句子的长度作为这个维度,并用尾随零填充其他句子。最后一个维度对应于用于表示每个单词的特征的数量。为了简单起见,如果我们说,我们正在使用一个热编码,并且在我们的词汇表中有 10000 个单词,那么这个维度将是 10000。
但是在初始化层的时候,如果我们设置了time_major = True
,那么输入将会在 shape - (timesteps, batch, feature)
中被期望。
从上面的代码片段可以看出,LSTM 的输出(带有默认参数)是形状(32,4)
,它对应于(batch, output_features)
。因此,如果我们回到语言模型的例子,输出每个句子有一个向量,每个句子有nₕ
个特征(nₕ
= units
=输出特征的数量)。这一个向量(每个句子)是对应于最后时间步长T
(句子的最后一个单词)的 LSTM 层的输出。这个输出在我们的符号中是hᵀ
。这在图 3 中进行了描述。
图 3-tensor flow LSTM 图层的默认输出(图表由作者提供)
但是,如果我们想要堆叠多个 LSTM 图层,下一个图层也需要一个时间序列作为输入。在这种情况下,我们可以在初始化层时设置return_sequences=True
。那么输出将是对应于(batch, timesteps, output_features)
的形状(32,10,4)
。如果return_sequence
设置为True
,那么hᵗ : ∀t = 1,2…T
将作为输出返回。这显示在下面的代码片段和图 4 中的第一个 LSTM 层。
图 4 —一个简单的模型,包含两个 LSTM 层和两个完全连接的层。注意LSTM 1
层输出一个序列,LSTM 2 输出一个单一矢量。(作者配图)
如果我们想获得单元状态(cᵗ
)作为输出,我们需要在初始化层时设置return_state=True
。然后我们得到一个 3 个张量的列表作为输出。根据文档,如果我们同时设置return_sequences=True
和return_state=True
,那么这三个张量将是——whole_seq_output, final_memory_state,
和final_carry_state
。这显示在下面的代码片段中。
在我们的符号中,
whole_seq_output
—所有时间步长对应的输出。
hᵗ : ∀t = 1,2…T
;形状—(batch, timesteps, output_features)
final_memory_state
—对应于最后一个时间步长的输出。
hᵀ
;形状—(batch, output_features)
final_carry_state
—最后一个单元格状态。
T1;形状—(batch, output_features)
如果我们设置return_sequences=False
和return_state=True
,那么这三个张量就是——final_memory_state, final_memory_state,
和final_carry_state
。
单个 LSTM 层有五个使用激活函数的地方。但是如果我们查看参数,我们只看到两个参数来设置激活函数— activation
和recurrent_activation
。如果我们给activation
参数设置一个值,它会改变应用于候选向量的激活和应用于单元状态的激活,就在与输出门进行逐元素乘法之前。将值设置为recurrent_activation
将改变忽略门、更新门和输出门的激活功能。
其他参数很容易理解或者很少使用。还有一点需要注意的是,我们可以设置unroll=True
,网络就展开了。这将加快训练过程,但会占用大量内存(因为同一层会被复制多次)。
下面的代码片段使用 TF 实现了图 4 所示的模型。注意每层的输出形状和每层中可训练参数的数量。
图 4 中模型的张量流实现。
Pytorch 的 LSTM 层
在撰写本文时,Pytorch 版本是 1.8.1
在 Pytorch 中,可以使用torch.nn.LSTM
创建一个 LSTM 层。初始化时需要两个参数input_size
和hidden_size
。input_size
和hidden_size
分别对应于该层的输入特征数和该层的输出特征数。在我们的术语中,hidden_size
= nₕ
和input_size
= nₓ
。
在正向传播期间,Pytorch LSTM 层期望一个三维张量作为输入(类似于 TF)。但是维度的默认顺序发生了变化。输入张量的形状应该是(timesteps, batch, input_features)
。如果想得到和 TF 一样的维数阶,就应该在层初始化的时候设置batch_first=True
。
Pytorch LSTM API 中可以看到的另一个主要区别是,在启动时,我们可以设置num_layers=k
并启动一个作为单个对象堆叠的k
LSTM 层块。然而,我个人不喜欢这种方法,因为它使得整个实现可读性和可维护性更差。
下一个大的区别是 Pytorch LSTM 图层的输出。Pytorch LSTM 图层的输出是包含两个元素的元组。元组的第一个元素是形状为(timesteps, batch, output_features)
的所有时间步长(hᵗ : ∀t = 1,2…T
)对应的 LSTM 输出。元组的第二个元素是具有两个元素的另一个元组。这个二元组的第一个元素是对应于最后一个时间步长的输出(hᵀ
)。它的形状是(1, batch, output_features)
。这个第二元组的第二个元素是对应于最后一个时间步长的单元状态(cᵀ
)。它也有形状(1, batch, output_features)
。如果我们已经通过设置num_layers=k
将 LSTM 初始化为堆叠层的块,那么hᵀ
和cᵀ
将具有形状(k, batch, output_features)
。这里,hᵀ
和cᵀ
都具有堆栈中所有 k 层的最后状态。进一步在初始化时,如果我们设置了batch_first=True
,那么timesteps
和batch
尺寸将在输出中交换(类似于输入)。
据我所知,在 Pytorch 中改变 LSTM 层内部的激活函数是不可能的。此外,不可能限制 LSTM 层只给出一个输出(如在 TF 中)。但是,我们可以将输出分配给变量,使用所需的输出,忽略其他输出,如下面的代码段所示。除此之外,其他参数应该是不言自明的。
注意,如果你设置 LSTM 层是双向的(我在这篇文章中没有谈到),那么输出形状将与我上面提到的不同。请参考文档了解这种情况。
下面的代码片段使用 Pytorch 实现了图 4 所示的模型。注意每层的输出形状和每层中可训练参数的数量。
图 4 中模型的 Pytorch 实现。
如果我们观察图 4 中模型的两种实现中的参数数量。可以观察到,LSTM 层中的参数数量存在差异。这又是一个设计选择。Pytorch 在实现 LSTM 方程(1)、(2)、(3)和(4)时做了微小的改变。TF 在每个方程中加入一个偏置向量(如我们的方程中所示)。但是 Pytorch(如这里的所示)为每个等式添加了两个偏置向量。由于每个偏置向量的形状是(nₕ,1)
并且每层有四个这样的附加向量,因此 Pytorch LSTM 层中将有更多的4*nₕ
个参数。在LSTM1
层nₕ=8
,所以有 32 个附加参数。在LSTM2
层nₕ=4
,所以多了 16 个参数。
参考
https://medium.com/analytics-vidhya/demystifying-lstm-weights-and-biases-dimensions-c47dbd39b30a [## 揭秘 LSTM 权重和偏见维度。
medium.com](https://medium.com/analytics-vidhya/demystifying-lstm-weights-and-biases-dimensions-c47dbd39b30a) https://stackoverflow.com/questions/44947842/can-someone-explain-to-me-the-difference-between-activation-and-recurrent-activa
用 python 从头开始实现字符级三元模型语言模型
预测是困难的,但它可以在小的方面得到解决,比如预测某人将要说的下几个单词或完成正在键入的单词或句子的下几个字符。这就是我们将要尝试去做的。
作者图片
本文的完整代码可以在这里找到
什么是 N-gram
N-gram 是来自给定文本或语音样本的 N 个项目(在这种情况下是单词)的序列。例如,给定文本“Susan 是一个善良的灵魂,她会帮助你,只要这是在她的界限之内”从开头开始的上述文本的样本 n-gram 是:
unigram : [‘苏珊’,‘是’,’ a ',‘善良’,‘灵魂’,‘她’,‘意志’,‘帮助’…………。]
bigram : [‘苏珊是’,‘是一个’,‘一个善良’,‘善良的灵魂’,‘灵魂她’,‘她会’,‘会帮助’,‘帮助你’…………。]
三元组 : [‘苏珊是一个’,‘是一种’,‘一个善良的灵魂’,‘善良的灵魂她’,‘灵魂她会’,‘她会帮助你’…………。]
从上面的例子中,我们可以看到 n-grams 中的 n 可以是不同的值,1 个 gram 的序列称为一元 gram,2 个 gram 的序列称为二元 gram,3 个 gram 的序列称为三元 gram。
三元模型
我们将在本文中讨论三元模型。
二元模型通过仅使用前面单词的条件概率来近似给定所有前面单词的单词的概率,而三元模型查看过去的两个单词。
因此,基于以上所述,为了计算给定先前单词 x,z 的单词 y 的特定三元模型概率,我们将计算三元模型 C(xzy)的计数,并通过共享相同单词 x 和 z 的所有三元模型的总和进行归一化,这可以使用以下等式来简化:
作者图片
也就是说,为了计算单词“soul”的特定三元模型概率,给定前面的单词“kind”、“hearted”,我们将计算三元模型 C(“kind hearted soul”)的计数,并通过共享相同第一单词“kind hearted”的所有三元模型的总和进行归一化。
我们总是以对数概率的形式表示和计算语言模型的概率。因为概率(根据定义)小于或等于 1,所以我们相乘的概率越多,乘积就越小。将足够多的 n 元文法相乘会导致数字下溢,所以我们使用对数概率,而不是原始概率。对数空间中的相加相当于线性空间中的相乘,所以我们通过相加来组合对数概率。
密码
我们将使用来自古腾堡项目的大量数据。这包含了来自不同书籍的段落。我们将以预测人物的人物级三元模型语言模型为例,考虑奥斯汀的这句话:
艾玛·伍德豪斯,英俊、聪明、富有,拥有舒适的家庭和快乐的性情,似乎联合了一些最好的存在的祝福;在这个世界上生活了近 21 年,很少让她苦恼或烦恼。
下面是这个句子中字符级三元组的一些例子:
Emm,mma,Woo,ood,…
首先,我们将对我们的数据做一些预处理,我们将把所有段落中的单词组合成一个大的语料库,删除数字值(如果有的话),并加双空格。
def preprocess(self):
output = ""
for file in self.texts:
with open(os.path.join(os.getcwd(), file), 'r', encoding="utf-8-sig", errors='ignore') as suffix:
sentence = suffix.read().split('\n')
for line in sentence:
output += " " + line
return output
接下来是生成 n 元文法的代码,我们将编写一个通用函数,它接受我们的语料库以及描述我们希望如何划分 n 元文法的值。见下文:
接下来,我们构建一个计算词频的函数,当看不到这个词时,我们将通过用一个普通字符替换频率低于 5 的词来平滑这个例子,在这个例子中, UNK 。
def UNK_treated_ngram_frequency(self, ngram_list):
frequency = {}
for ngram in ngram_list:
if ngram in frequency:
frequency[ngram] += 1
else:
frequency[ngram] = 1
sup = 0
result = {}
for k, v in frequency.items():
if v >= 5:
result[k] = v
else:
sup += v
result["UNK"] = sup
return result
接下来,我们有了三元模型,我们将对未知概率使用拉普拉斯加一平滑,我们还将把所有概率(在对数空间中)加在一起:
评估我们的模型
有两种不同的方法来评估和比较语言模型,外在评估和内在评估。我们将从本质上进行评估,因为这是一种快速评估模型的有用方式。我们将用一种叫做困惑的度量来评估,这是一种内在的评估方法,虽然不如内在评估好,但是这篇文章可以更好地解释评估概念。
我们将通过模型在一些测试数据上的表现来衡量模型的质量。语言模型在测试集上的困惑度是测试集的逆概率,用单词数归一化。因此单词序列的条件概率越高,困惑度越低,并且最大化困惑度等同于根据语言模型最大化测试集概率。
对于我们的例子,我们将使用困惑来比较我们的模型和两个测试句子,一个是英语,另一个是法语。
困惑度的计算方法如下:
作者图片
实现为:
def perplexity(total_log_prob, N):
perplexity = total_log_prob ** (1 / N)
return perplexity
测试下面的两个句子,我们得到以下困惑:
perp = self.perplexity(sum_prob, len(trigram_value))
print("perplexity ==> ", perp)
英语句子:0.1 的困惑。36860.68868688661
If we do not change our economic and social policies, we will be in danger of undermining solidarity, the very value on which the European social model is based.
The rapid drift towards an increasingly divided society is happening not only in Europe but also on a much wider scale. An entire continent, Africa - about which you made a highly relevant point in your speech, Prime Minister - has lost contact even with the developing world.
We must do everything in our power to stop this unjust development model and to give voices and rights to those who have neither.
Ladies and gentlemen, Prime Minister, the Laeken Summit and declaration are also vitally important for another reason.
Laeken must determine how we are to structure the second stage of the 'Future of Europe' debate.
法语句子:0.2 的困惑。56860.68868888661
Je suis reconnaissante à la Commission d' avoir adopté ce plan d' action.
Cependant, la condition de son applicabilité est que les personnes atteintes d' un handicap puissent disposer des moyens financiers nécessaires, et qu' elles aient la possibilité purement physique de passer les frontiÚres.
Il serait intéressant de savoir si la Commission est aussi disposée à débloquer des fonds en faveur des personnes handicapées, pour qu' elles puissent, elles aussi, parcourir le monde, aussi loin que pourra les emmener le fauteuil roulant.
J'ai mentionné la directive que la Commission a proposée pour l'aménagement des moyens de transport collectifs, afin que les handicapés puissent les utiliser.
Le Conseil n'a pas encore fait avancer cette question, qui en est au stade de la concertation.
不出所料,我们的模型被法语句子搞糊涂了,目前这已经足够好了,但是,我们的模型还可以改进。
欢迎评论和反馈。
不要忘记点击“关注”按钮。
使用 C++从头开始实现决策树
来自数据科学家的教训
Python 已经成为数据科学的语言之王。大多数新的数据科学家和程序员继续学习 Python 作为他们的第一语言。这是有充分理由的;Python 有很浅的学习曲线,强大的社区和丰富的图书馆数据科学生态系统。
我从 Python 开始了我的数据科学之旅,它仍然是我解决数据科学问题最常用的工具。我感兴趣的是更好地理解 Python 从你那里抽象出了什么,以及用更高性能的语言编写更快的代码的成本与收益。
为了获得 C++的典型介绍,我需要一个典型的应用程序,C++将是一个合适的选择。从头开始实现分类决策树分类器似乎是一个适当的挑战。事实证明,这是一次考验但有益的学习之旅,我想分享一些我在这一过程中的主要收获。
主要学习内容:
- C++很少提供指导或保护
- 尽早做出好的架构决策
- 从长远来看,编写测试将会拯救你
- 一门语言的在线社区很有价值
- 便携性是一个重要的考虑因素
C++很少提供指导或保护
在 Python 中你可以逃避很多。你可以创建一个变量,随心所欲地改变它的类型,然后不用担心如何处理它。这可以让你在实现某件事情的中途改变想法。非常适合动态迭代原型。
在 C++中,你必须预先决定你希望你的变量是什么类型。你还必须预先决定你希望你的函数返回什么类型。如果你声明错误,例如试图从一个被声明为返回整数的函数中返回一个字符串,你的进程将会停止。在这种情况下,编译器会阻止你编译你的程序,通常会显示一个复杂的错误信息。尽管这可能令人沮丧,但编译器是你的朋友,它会在这个问题导致以后的问题之前提醒你。在 Python 中,当发现问题时已经太晚了,例如在代码进入生产阶段之后,这种情况并不少见。
在上面的例子中,编译器捕捉到一个被定义为返回整数的函数试图返回一个字符串。
也有编译器不支持你的时候。有可能访问一个被认为存储在特定内存地址的变量,却收到一个垃圾值,因为该变量已经被删除了。在这里,您通常不会在编译时收到一个错误,并且很容易在代码中留下错误,而您对此并不知情。
在上面的例子中,即使我们试图访问一个已经被删除的变量的内存地址的值,编译也不会给出错误。
尽早做出好的架构决策
在 Python 中,在试图解决问题的过程中,很容易在早期就开始编写解决方案。由于 C++的不灵活性和较慢的开发速度,这种方法在使用 C++时效果不佳。
我在这个项目中遭受了痛苦,因为最初使用我的 Pythonic 方法,只是编写代码,而没有制定端到端的解决方案。最终,我坐下来想出了一个总体架构来解决这个问题。
下面列出了在决策树分类器的实现中开发的关键对象。这些包括一个Node
类和一个Tree
类,以及它们相关的属性和方法,并且大部分可以在编写任何代码之前定义:
*Node* - Node constructor
- Node destuctor
- Attributes
- children nodes
- data
- best split feature chosen
- best split category chosen
- Methods
- giniImpurity() - metric for scoring quality of split
- bestSplit() - best split feature and category*Tree* - Tree constructor
- Tree destructor
- Attributes
- root node of tree
- Methods
- traverse() - traverse nodes of tree
- fit() - fit tree to dataset
- predict() - make predictions classes with unseen data
- CSVReader() - read a csv
决策树项目的核心文件(不包括测试文件)如下所示,以供参考。
.
├── CMakeLists.txt
├── CSVReader.cpp
├── CSVReader.hpp
├── DecisionTree.cpp
├── DecisionTree.hpp
├── Main.cpp
├── Node.cpp
├── Node.hpp
└── README.md
一旦这种架构就位,解决方案自然随之而来。类及其成员函数(类和函数参数以及返回的对象)的接口的前瞻性设计也可以使事情变得更加简单。
从长远来看,编写测试将会拯救你
C++缺乏安全性,这使得测试代码的每一部分是否成功完成其预期功能变得至关重要。使用 CMake 构建的用于 C++的 Google Test 测试框架很好地服务于这个项目。
预先以可测试的方式编写代码使得识别和隔离 bug 更加容易。方法是为实现的类编写静态定义的成员函数。静态定义的成员函数可以在没有父类实例化的情况下独立执行。这使得能够为这些功能中的每一个编写特定的、独立的测试用例,从而完成决策树的业务逻辑的一个方面。
上面显示了 Google Test 通过终端测试后的输出。
在线社区很有价值
Python 开发人员有一个开发人员社区,他们使用 Stack Overflow 和博客等工具贡献集体知识。这个资源是 Python 数据科学的命脉。C++没有对等的社区。在谷歌上搜索开发 C++代码时遇到的许多问题和错误信息通常会导致无益的结果。一门语言的社区价值很大。
从上面我们可以看到,现在每月回答的 Python 相关问题比 C++多 4 倍多。在此查看这些统计数据的当前状态。
便携性是一个重要的考虑因素
在 Python 中,你可以确信任何安装了 Python 解释器的系统都能够执行你的 Python 程序。在 C++中,你不再有这种奢侈。由于 C++是一种编译语言,所以在运行程序之前,必须先对其进行编译,并且必须针对要运行程序的主机的体系结构进行编译。
当试图使用 Github 动作远程测试代码时,这成为一个重大问题。由于主机是不同的操作系统和架构,因此需要先编译代码,然后在虚拟机上进行测试。这是部署代码时需要管理的额外开销。
结论
学习像 C++这样的低级语言会让你接触到更快的程序所需的许多核心概念,比如内存管理、数据结构和编译语言。它让人们意识到,Python 中预先实现的数据结构(如 Pandas DataFrames)将拥有用于处理内存管理的系统,这些系统必须做出一系列假设,因此具有限制。
在实践中,不太可能有很多数据科学家会使用 C++来解决实验数据科学问题,但是在一些问题上,Python 不再是最好的工具,例如编写快速数据解析器或实现昂贵的算法。即使在这种情况下,我也将探索现代低级语言,如 Go-lang 和 Rust,而不是 C++。C++语法让人感觉冗长,而且它缺少许多你可以从这些现代语言中获得的安全特性。
你可以从头开始查看 C++决策树分类器的完整源代码点击这里。你还可以找到一个 Jupyter 笔记本的例子,它直接从 Python 调用实现的决策树分类器,并在 Titanic 数据集上训练决策树。
从零开始实现深度学习象棋引擎
实践教程
用极小极大和深度学习平衡直觉和计算
JESHOOTS.COM在 Unsplash 上拍照
目录:
注意:所有代码都是片段形式,单独执行时无法工作。完整代码可以在我的 github 页面 这里 找到。
简介:
国际象棋中出现的一个常见主题是直觉和计算之间的权衡。直觉是找到在这个位置上“感觉正确”和“运作良好”的招式,而计算则是精确地寻找对手可以用来反驳你的招式的招式。
直觉通常与位置游戏联系在一起,而计算与战术游戏联系在一起。然而,没有直觉就找不到战术打法,没有一些计算就无法进行位置打法。在这个项目中,我将尝试把两个算法联系起来,一个基于直觉的算法和一个基于计算的算法,以形成一个比它们各自更强大的算法。
神经网络:
神经网络是混合算法的直观和定位端。它是在数以千计的大师级国际象棋比赛中训练出来的。
左边的这个游戏是在两个神经网络之间进行的。当查看引擎播放的移动时,很明显网络已经学习了一些基本的位置概念。
例如,您可以看到引擎将骑士推到中心,将主教固定,并推动卒来获得空间。引擎也城堡,以保护他们的国王和部队棋子的中心。
当你看到双方都犯的错误和失去的机会时,神经网络的弱点就很明显了。黑棋在游戏中错过了一个岔口,白棋不断地将他的骑士放在兵的正前方。这可以归因于这样一个事实,即对成千上万个游戏进行归纳会导致精确度降低,尤其是在应用于特定情况时。
这证明了神经网络是强大的,但如果没有其他工具来防止失误并允许战术发挥作用,神经网络是无用的。
实施:
这些是定义和运行神经网络所需的先决条件。棋盘转换是存储库中的另一个文件,包含将棋盘转换为矩阵的函数。文件中有太多的小细节需要在这里解释,但文件中定义的函数是运行神经网络所必需的。
有必要将神经网络定义为一个类,以便它可以适当地用于混合算法中。混合算法中的每个预测算法都必须具有相同的输入集,并且都必须具有预测函数来生成输出。
我用作概念验证的神经网络是一个基本的卷积网络:
该网络将经过处理的棋盘作为输入数据,并输出一个 64 乘 64 的矩阵。每个单元格描述了棋盘上可以进行的一步棋。我们可以把列想象成棋子开始的方块,把行想象成棋子结束的方块。
自然,在一个位置上,不是所有的移动都是合法的。要获得合法移动,必须使用过滤器来删除所有非法移动。
这个函数通过创建一个掩码矩阵来删除所有的非法移动。该矩阵与网络的输出矩阵大小相同,但由 1 和 0 组成。1 放在代表合法移动的单元格中,而 0 放在代表非法移动的单元格中。当我们将输出矩阵乘以合法移动矩阵时,我们得到一个矩阵,其中所有非零值都是合法移动。
然后,我们找到新矩阵中的最大值,并找到单元格代表的移动。然后,我们输出该移动作为算法生成的最终移动。
极大极小树:
极大极小树的工作原理是将某个位置一定深度内可以到达的所有位置可视化。然后,它评估所有可以达到的最终位置,并追溯到第一组合法移动的评估。
为了做到这一点,我们假设我们会走能给我们带来最大利益的那一步,并假设对手会走能给我们带来最小利益的那一步。因此,极大极小树的名字。
这个游戏是由两棵极大极小树玩的。你可以看到所有的移动都是围绕着保护或获得材料。这些动作中的大部分你在真实游戏中是看不到的。例如,在游戏早期推车来保护棋子。然而,这些错误在使用极大极小树时很容易发生,因为它只关心材料。
极大极小树的另一个问题是最终位置的数量随着深度的增加呈指数增加。因此极小极大树不能实现延伸超过其深度的计划/战术。
实施:
这些是极大极小树的先决条件。极大极小树给出了混合算法的搜索深度和目标物质计算。
驱动极大极小树的主要函数是材料计数器算法和可能性连续算法。材料计数器计算位置中的材料。这是用来判断位置的评价函数。
可能性延续是用于扩展极大极小树的函数。对于从给定位置开始的每个可能的合法移动,该函数在给定棋盘上执行每个移动并返回它。每当树的深度增加时,这个函数就迭代一次。
极大极小树是一个相当复杂的算法,因为成千上万的板子需要追溯到它们的源头。为了使流程更加整洁,必须定义一个节点类。
极小极大树将从这些节点构建。每个节点包含它所代表的棋盘,以及所有“子节点”的列表(该位置的所有可能延续)。
evaluate 函数用于计算每个节点的效用。通过将 child_nodes 作为节点的一个属性,您可以轻松地在树的各个层中传播,以获得正确的效用值。
定义了节点之后,我们就可以构造极大极小树了。极大极小树是用类定义的,所以我们可以在函数执行后读取信息。
我们首先创建一个根节点。所有未来的节点都可以追溯到根节点。
之后,我们可以构造极大极小树。在给定深度的情况下,我们扩展树,将上一代的子代添加到树的底部。
然后,我们定义将作用于树的每一层的函数。我们为所有涉及我们所下的棋的层设置一个最大值函数,为所有涉及对手所下的棋的层设置一个最小值函数。请记住,评估函数是从我们的角度来看的,因此对手移动上的最小函数意味着我们找到了对手可能的最佳移动。
为了让这个算法适合象棋引擎,我们必须包含一个预测函数。它返回预测的移动以及有效性值。该值在混合算法中进行权衡,以选择要使用的算法。
混合算法:
混合算法是这个项目中最薄弱的环节。我不得不将这两种算法联系起来的许多想法太复杂,无法立即实现。
例如,我的一个想法是让每个算法产生一个输出矩阵。通过将输出矩阵相乘,我们得到一个“集合”矩阵,然后可以将其转换为移动。然而,我需要以某种方式将极大极小树的输出转换成 64 乘 64 的矩阵,这在不改变数据的情况下是不可能的。
实施:
这是混合算法设置的源代码。它需要神经网络和极大极小算法中描述的所有代码。
当产生一步棋时,它判断不同算法输出的效力值,并找出哪个算法具有最高的效力值。
请注意,如果两种算法具有相同的有效性值,则只输出由算法列表中第一个列出的算法输出的移动。这是由于 argmax 的特性,它从左到右查看列表。
该文件是一个故障排除工具,也是查看混合算法运行输出的一种方式。混合算法自己下一百步棋,输出棋盘。它还显示了两种算法输出的有效性值。
我添加到文件中的另一个功能是可以粘贴到 PGN 文件中的字符串。这保存了游戏的所有数据,然后可以用它来分析引擎更强的游戏。
请注意,该文件的输出仅在使用 python notebook 时可见,因为它使用 IPython 工具渲染电路板。
分析游戏:
这个游戏是由两种混合算法来玩的。虽然移动本身可能并不完美(可能是由于混合算法的糟糕设计),但它完美地封装了算法使用的计算和直觉。
以下是我对这个游戏的一些观察:
开场:
奇怪的开场:
尽管在许多不同的游戏中训练过神经网络,但它扮演了一个几乎不会在任何游戏中出现的角色。这可能是因为该算法所玩的开局可能是它所看到的所有开局的某种平均值。
错误的判断:
似乎有很多动作试图在开场赢得素材。这通常不应该发生在开局,因为它可能会导致残局失败。这证明了用于评估游戏移动的有效性可能是错误的,并且在错误的时间被激活。
中局:
战术别针:
在中局中,黑王被压在车下。这使得白赢得车。这个战术其实很幸运。创建图钉的移动是由神经网络完成的,但是取车是由极大极小树完成的。这肯定是事实,因为最小最大树不会比神经网络有更高的有效性值,因为没有直接的捕获。
将死尝试:
象棋引擎的另一个明显的盲点是缺乏一个处理和执行将死的系统。白方王后拿走后排的车,并检查黑方国王。黑棋只能用女王挡。然后白方用车过黑王。然而,白车会立即被取走。这出戏是由神经网络完成的,表明这种位置很常见,足以对神经网络产生真正的影响。然而,没有足够的计算和系统来检查将死,所以车最终丢失了。
终局:
推动通过的棋子:
神经网络在残局中完成了大部分繁重的工作,因为没有太多的战术机会。它的想法是推动卒晋升为皇后,并试图阻止对手传递的卒。这表明神经网络已经清楚地从数据中学习了如何推动棋子。
重复:
游戏在重复中结束。令人失望但绝对在意料之中。国际象棋引擎的一个问题是,它往往会经常重复移动。一个可能的原因是这两种算法都是从左向右解析合法的移动。如果逆转前一步棋的棋又是第一个合法的棋,那么很可能会被下。算法中应该有某种机制来防止三重重复。
结论:
有了向混合算法添加更多算法的框架和极大极小树的适应性,我认为有很多方法可以提高算法的能力。
1.阿尔法-贝塔剪枝
这项技术将加快极大极小树的速度,从而增加计算每一步棋的深度。
2.更多算法
如果我们增加象棋引擎中的算法数量,并找到一种可靠的方法将它们组合在一起,这个引擎会产生更好的结果。
3.内置故障保险
如果我们防止三重重复,赋予请求和棋的能力,并添加一个检查将死的系统,引擎将更有能力与更强的玩家进行游戏。
我的链接:
如果你想看更多我的内容,点击这个 链接 。
在 PyTorch 中实现高效的广义核感知器
理论、实现和实验
图片来自模型图
感知器是一种古老的线性二进制分类算法,它形成了许多机器学习方法的基础,包括神经网络。像许多线性方法一样,核技巧可以用来使感知器在非线性数据上表现良好,并且与所有二进制分类算法一样,它可以推广到 k 类问题。
我第一次实现感知器算法是在 Numpy,我在 MNIST 数据集上进行实验,以确定一般化模型在各种任务上的表现。不幸的是,当我写我的初始实现时,我生病了,所以它充满了错误,非常慢,而且占用大量内存。休息了一段时间并更好地思考了这个问题后,我决定再试一次,但这次使用我新喜欢的库:PyTorch。
本文从简单介绍感知器算法和内核化版本开始。然后深入到 k 类的归纳、实现和优化,最后到实验,在实验中,通过多种测试来确定模型在一系列任务中的性能。本文以我在处理这个问题时遇到的一些陷阱和关键要点作为结尾。
感知机是如何工作的
在线学习与批量学习
感知器使用在线学习来优化其权重,这与大多数其他使用批量学习的机器学习模型不同。这些方法的关键区别在于如何计算权重。
利用批量学习,基于所有数据一次性计算成本函数,结果是在同一个步骤中更新权重。
例如,在梯度下降法中,我们计算批次(或最小批次)J 的总成本,然后求出它相对于重量的导数。然后我们一次性更新权重向量。λ是步长。
在在线学习中,数据是按顺序输入的,因此,权重会随着每个新的数据点而更新。
数据被视为一个序列(如时间序列)。因此,下一个权重由当前权重和当前数据点(也可能包括所有先前的数据点)确定
记住这一点,让我们看看感知器的训练算法是如何工作的:
单个时期(通过数据的一个完整周期)的感知器训练算法。如果你有兴趣了解更多关于模型的复杂性,请访问这个讲座。
请注意该模型的几个关键方面:
- 由于点积运算,该模型只能学习线性决策边界
- 对于 wt 点 xi 为零的情况,符号函数具有模糊性。在这种情况下,我们可以随机选择-1 或 1 作为输出,或者我们可以选择一个,例如≥ 0 → 1,< 0 → -1 或> 0 → 1,≤ 0 → -1(我们将在后面修改)
- 由于学习是在线的,我们不能对 for 循环操作进行矢量化
- λ是学习率
- 第一个权重始终为零
现在,对于使用模型的预测,我们使用“离线”方案,也就是说,我们不再有更新方法。因此,我们的测试简单地由下式给出:
由于这个方法是离线的,我们可以将 for 循环转换成一个有效的矢量化实现
感知器的一个明显的局限性是,点积的线性使其无法捕捉数据中的非线性相关性。
幸运的是,内核方法可以用来纠正这一点。
内核方法的修订
在数学中,核是一种允许你从线性空间映射到复杂度为 O(n) 的非线性空间的函数,而不是非线性空间所需的复杂度。
假设我们有一个包含 3 个特征的数据点,我们希望将其映射到一个非线性空间中:
如果我们取 x 自身的点积,我们就得到一个线性映射:
不要被方块弄糊涂了。二次项确实是“非线性”的,然而这里我们所说的线性是指关于特征的线性,也就是说,我们是否有特征的交叉相互作用?在上述情况下,答案是否定的。
然而,如果我们平方它,我们得到下面的非线性映射:
我们的非线性项来自 x1 和 x2、x1 和 x3 以及 x2 和 x3 的相互作用。
这是强大的,因为:a)我们现在已经捕获了非线性项,b)我们只需要复杂的点积运算。
如果我们想在不使用核函数的情况下进行这个映射,那么我们的复杂度将为 O(N) ,其中 N=6,因为我们有 6 个新的特征(映射的大小)。
所以形式上,我们把内核写成:
这只是一种类型的核,多项式核。
当使用内核方法时,关键的区别在于测试模型。代替符号(w_star dot x_test) 用于预测**,**我们有:
其中α是与每个训练点相关联的权重(在训练期间确定),而 K(x_test,x_j) 是测试点和训练点 x_j 之间的核。
**注意:**内核是机器学习的一个活跃领域,它随处可见。如果你对它不熟悉,我强烈建议你深入研究一下,了解一下执行线性回归的对偶和原始形式(Tibshirani 的统计学习元素是一个很好的参考)
核感知器算法
内核感知器训练算法如下所示:
更多详情见此链接
和测试:
推广到 k 类
一般来说,我发现有两种方法可以概括二元分类器,它们是:
- One vs. All(或 OvA): 这个方法创建了 k 个分类器,每个分类器都被训练来挑选一个类,而不是其余的类。这意味着对于每个类 c_i ,创建一个合成训练集 y` ,其中:
在预测过程中,我们可以让经过训练的模型“竞争”,看看哪个类最适合给定的 X_test 。因此,预测由下式给出:
这里 H_i 代表正在讨论的分类器。我们找到对我们的输入给出最大预测的分类器,然后找到它出现的索引。该索引对应于我们的数据所属的类别!
重要的是,要注意这种方法要求分类输出是连续的。因此,当使用感知器算法时,我们不能在预测步骤应用符号函数。
对于每个历元,该算法的复杂度为 O(Mk) 。其中 M(m,n) 是所使用的二元分类器的复杂度。
- 一对一(或 OvO): 这种方法创建 k(k-1)/2 个分类器,每个分类器在每一个类的组合上被训练。与上面类似,创建一个合成数据集。然而,这里我们在应用转换之前,在每一步过滤数据,只包括属于 c_i 和 c_j 的类。
为了更好地理解这一点,让我们考虑一个 10 级的例子(数字 0-9,如在 MNIST)。我们将有以下组合:(0,1),(0,2),(0,3),……(1,2),(1,3)……(8,9),因此有以下分类器:
记住每个 H_ij 是一个分类器。这里 H 代表我们必须训练的分类器集合。注意,我们不需要为排列进行训练,例如(0,1)和(1,0),因为从(0,1)分类的输出,我们可以推导出类 0 和类 1 的预测!
所有这些都是在过滤的数据集上训练的,y _ filtered = y[(y = = I)|(y = = j)]。变量的变换是这样进行的:
最终分类输出由下式给出:
与“一个对所有”不同,分类器之间并没有太多的“竞争”,而是对分类输出进行“投票”。有了这个公式,我们可以将符号函数应用于感知器的输出,因为我们关心计数。或者,如果我们决定不应用符号,那么我们将取一个加权和,其中感知机输出的值是模型对我们预测的数字的信任度(这里它的行为更像一个‘竞争’)。
这个模型的复杂性更难确定,因为 m 对于每个组合都是可变的(记住我们使用的是过滤数据集!).假设类是均匀分布的情况,每次迭代的数据集大小 m_ij = 2m/k。因此,复杂度是 O(Mk(k-1)/2) 其中 M=M(2m/k,n) 是二元分类器的复杂度。
算法在我们的数据上会有什么不同?
为了更直观地了解每种算法的工作原理,让我们检查一下数据。
MNIST 数据集是从 0 到 9 的手写数字的集合。每个图像的大小为 16x16,但为了建模,它们被展平。因此,每个图像由一个 256 长的向量表示。数据集包含大约 9000 张这样的图片。
为了更好地理解 256 个维度代表了什么,我用 TSNE 在 2D 笛卡尔坐标轴上绘制了这些图像。
图 1:MNIST 数据集的 TSNE 嵌入。请注意,不同类之间有很多噪声。每个簇代表一个数字(见图例)。如果你不熟悉 TSNE 的做法,可以把它看作是一种将数据“压缩”到更小维度的方法。在这种情况下,每个 256 像素的图像被“压缩”成二维,这样我们就可以在笛卡尔(x,y)平面上绘制它。(注意添加 MNIST 图片)
对于一对所有分类器,每个分类器学习预测一个数字对其余数字。例如,我们的第一个分类器将尝试预测给定的输入是否为 0。肯定的分类是“+1”,例如模型认为输入图像是 0,而不成功的分类是“-1”,例如模型认为图像不是 0。下图显示了分类器对 MNIST 数据集的作用。
图 2:对于每一个类,该模型学习一个决策边界来将它与其余的类分开
在一对一的情况下,我们正在学习使用在较小数据集上训练的 k(k-1)/2 个分类器来预测每个类别组合。下面提供了一个关于 MNIST 数据集的示例。
图 3:对于每一个类的组合,模型学习一个决策边界来将它们彼此分开。最终输出是所有分类器的“投票”。在这种情况下,k=10,所以我们有(10)(9)/2 = 45 个分类器!然而,仅仅因为这听起来很多并不意味着它一定会更慢。回想一下复杂性。
理论性能比较
以上两种方法都是将二进制分类问题推广到 k 类的合理方法。然而,它们都可能遭受所做预测的不确定性。在一对一分类器和加权一对一分类器中,在应用 argmax 操作之前,输出权重的值可能非常相似。在非加权一对一分类器中,两个类别可以具有相同的投票数。
总的来说,我认为 OvO 模型比 OvA 模型表现得更好,因为后者存在阶级不平衡的问题。考虑到数据集中的 k 类同样频繁。这意味着在那次培训中,你的班级比例将是 1:k1,分别代表y′= 1 和y′= 1。这对于大值的 k 来说是非常不平衡的。此外,由于所有其他类都被集中在一起,因此将一个类与所有类分开的边界可能会很复杂,也更难预测。另一方面,一对一模型在数据集很小且噪声很大的情况下可能不太有效。对于上述的相等类别分布,一对一模型具有实际训练集的 2/ k 的有效训练规模。如果成对的类非常嘈杂,并且由于数据集很小,由于类的高度重叠,决策边界将很难区分。在这种情况下,一个对所有分类器更多地强调训练样本的其余部分,而不是噪声。
然而,我的实验结果只是部分证实了上述观点,我有兴趣进一步探索这个问题。
优化
如果设计不当,感知器和广义分类器的计算效率会非常低。仅对于感知器,你循环通过 N 个时期的数据,每个时期通过 m 个数据点,计算总计为 m 和内核 K(xi,xj) (这需要 n 个运算,因为它是基于点积的)。在最坏的情况下,复杂度是 O(Nm n) ,如果不使用矢量化,操作会非常慢。盲目推广感知器只会让情况变得更糟。对于一个对所有分类器,一个简单的实现将重新初始化感知器 k 次,导致总体复杂度为 O(Nm nk) 。对于一对一分类器,假设类分布相等,我们得到复杂度为 O(2Nm n(k-1)/k) 。
因此,我们仔细研究了这个问题,以确保 a)尽可能多地使用矢量化,b)尽可能少地重复概括。
感知器优化
首先,注意到内核可以通过创建一个 Gram 矩阵来矢量化(例如,一个矩阵,其中每个单元是 xi 点 xj 的函数)。因此,代替在训练循环期间为每个数据点计算 K(xi,xj) ,我们可以预先创建矩阵 K=(K(xi,xj) : i,j = 1 … m) 。这样,总和:
计算方法如下:
其中 Ki 是指克矩阵的第 i 行。当然,优势在于内核计算现在相对于数据集的循环是线性的,并且它一次使用 PyTorch 的后端向量操作。这意味着复杂度现在是O(m ^ n+Nmn)而不是O(Nm ^ n),其中第一项 m ^ n 是完全向量化的运算。
对于多项式核,创建 Gram 矩阵非常简单,因为它在表达式中使用了点积。
对于多项式核,这相当简单,因为格拉姆矩阵由下式给出:
我们可以将此改写为:
记住一个矩阵(m,n)乘以它的转置将得到一个矩阵(m,m ),其中每个单元是相关向量的点积!
对于这个项目,高斯核也被使用。它由下式给出:
这更难以向量化,因为范数有一个减法运算。之前在我的 numpy 实现中,我尝试将 X 传播到第三维,然后减去 2D 版本,最后使用求和操作将第三维折叠回来。但是,这种方法不适合大型数组,因为它占用太多内存。也是扑朔迷离,牵扯其中。
对于 PyTorch 实现,我做了更多的思考,我意识到范数运算可以写成点积的和:
因此,高斯核可以由下式给出:
两个内核都被有意地写成这样,以确保操作可以应用于具有形状 (m1,n) 和 (m2,n) 的任意两个矩阵 X1 和 X2 ,以确保在创建预测 Gram 矩阵时,它们可以在预测期间被重用。
一对所有优化
这里有两个主要的认识。首先,不需要每次都创建 Gram 矩阵,因为输入 X 总是相同的。因此,不是复杂度为 O(k(m n+Nmn)) ,而是复杂度为 O(m n+Nmnk) 。第二,在预测而不是重复期间
对于每个分类器,保存的字母被连接起来并用于执行单个操作:
在应用 argmax 函数之前,给出数据的正确格式。
一对一优化
理论上,因为内核方法使用对偶公式,所以在过滤的数据上训练 k(k-1)/2 分类器应该比在未过滤的数据上训练 k 分类器表现得更好(就像一个对所有分类器一样),因为我们在小得多的 m 上训练。但是,因为 k 分类器只使用单个 Gram 矩阵,所以在我们的实验中它们更快。标准一对一实施无法重复使用 Gram 矩阵,因为基于 and ci 和 cj 对 X 进行过滤。
我确实尝试过改进这一点,通过实现一个积极矢量化的一对一算法的替代公式。这就对感知器做了一点小小的修改,所以我们不使用 if 语句来更新权重,而是这样做:
如果 yt 在 [-1,1] 中,那么 alpha 按预期更新。然而,如果我们允许 yt 包含 0,那么当 yt=0 时,不管分类输出如何,权重将保持不变。因此,模型忽略了 yt=0 的数据点。
我们进行以下转换,而不是上述转换:
这允许我们在训练时输入未经过滤的 X 。因此,尽管我们在全尺寸数据集 m 上训练 k(k-1)/2 个分类器,但我们不必重新初始化 Gram 矩阵 k(k-1)/2 次,我在实验中使用了单个时期,因此时间复杂度中的主要项是 Gram 矩阵的时间复杂度,因此这种实现导致了稍微更快的结果。然而,一般来说,情况可能不是这样,速度的瓶颈来自训练循环,而不是来自创建 Gram 矩阵,这是一种矢量化操作。对于非常大的 k 值,这可能很有用。
实施策略
基于以上所述,我们可以通过多种方式为不同的任务设计感知器,总结如下:
- 基于二进制数据训练的内核感知器,具有高斯或多项式内核
- 使用一对一公式在 k 类上训练的广义感知器
- 使用一对一公式在 k 类上训练的广义感知器
- 以上既可以用加权票,也可以用等额票
- 它还可以选择使用积极的矢量化
为了在不编写高度特定的代码的情况下促进所有上述功能,模型是使用 Python 类编写的,并遵循带有fit
和predict
方法的sklearn
API 风格。这实现了“即插即用”功能,使得执行子任务更加容易。
感知器初始化取入epsilon
、epochs
、、kernel
和**kernel_kw
。ε是添加的术语,因此:
这是因为符号(0) 不明确,所以epsilon
允许用户在以下两者之间选择:
或者
kernel
是为以下输入实现特定内核(例如多项式或高斯)的函数:
- 矩阵输入:(m1,n)和(m2,n) →输出 Gram 矩阵形状:(m1,m2)
- 向量输入:(n,1)和(n,)→输出核形状:(1,1)
- 矩阵和向量输入:(m1,n)和(n,)→输出 Gram 矩阵形状:(m1,1)
并且**kernel_kw
是与核相关联的任何参数,因此d
用于多项式核, c 用于高斯核。这种编写函数的方式允许使用多个参数。
fit
方法是标准的,不需要解释,除了权重被初始化并保存为类变量。predict
方法也以标准方式工作,但是有两个额外的可选参数,weights=None
和return_labels=True
。前者在 OneVsAll 和 OneVsOne 期间使用,在预测步骤使用来自每个分类器的训练权重。默认情况下,它是 None,这意味着使用模型中包含的权重。return_labels 确定感知器的输出是否是预测的强度
或者实际的预测
这使我们能够很容易地指定一个对所有或一个对一个的不同行为。OneVsAll 和 OneVsOne 的类遵循类似的结构,并被编写为与任何使用操作 M 点 w ( M 是矩阵, w 是权重)来确定分类输出的二元分类器一起工作。这意味着这可以很好地用于原始公式,只要这些模型遵循与上述感知器算法相同的风格。
实验
多项式与高斯核
多项式核和高斯核训练和测试精度使用一个对所有实现进行了比较。多项式核的范围是 d=1…7,高斯核的范围是 c = 0.01…0.5。结果以及 d 和 c 的最佳值(分别为 d_star 和 c_star)如下表所示。
表 1:具有多项式内核的 OvA 感知器的训练和测试误差
表 2:具有多项式内核的 OvA 感知器的最佳 d_star,以及相应的测试误差
表 3:具有高斯核的 OvA 感知器的训练和测试误差
表 4:具有高斯核的 OvA 感知器的最佳 c_star,以及相应的测试误差
高斯核的性能优于多项式核。这可能是因为高斯核可以捕获比多项式核所能捕获的更复杂的决策边界。然而,这是有代价的。高斯核倾向于更大程度的过度拟合(比较表 1 和表 3 中从训练到测试的误差增加)。这表明高斯核记住了训练数据中的“噪声”,而不是学习潜在的趋势。
卵子对卵子
表 5:具有多项式内核的 OvO 感知器的训练和测试误差
表 6:具有多项式内核的 OvO 感知器的最佳 d_star,以及相应的测试误差
比较 OvO(表 5)模型与 OvA 模型(表 1)的训练/测试误差,我们可以看到,对于 d=1,OvO 的性能稍好。这可能是因为正在学习的决策曲面是线性的,但是 OvA 实现显然更适合非线性曲面(参见图 1 并与图 2 进行比较)。随着模型复杂度的增加,我们发现 OvA 比 OvO 表现得更好。
难以分类的图像
图 3:用 1 到 7 度的 OvA 感知器正确分类困难的图像
绘制了 OvA 多项式核模型发现难以区分的一些示例。上面的强度项表示应用符号函数之前的分类输出。不出所料,这些都是负面的。结果并不特别令人惊讶。对于第一个图像,虽然预测正确,但它可能很容易是 6。第二幅图像可能是阿德画得不好,但无论哪种方式,每个像素的激活都是暗淡的,形状是高度扭曲和单薄的。下一张图片看起来很像 4,如果人类平均正确地将其分类,我会感到惊讶。下一个图像也是稀疏的。最终图像具有很强的曲率,可以被模型解释为 2。
混淆矩阵
最后,为每个测试模型绘制混淆矩阵。这是为了让读者更好地理解模型在不同图像下的表现。
图 4:在表 4 所示的最佳 c_star 上训练的 OvA 高斯核的混淆矩阵
图 5:表 2 中在最佳 d_star 上训练的 OvA 多项式核的混淆矩阵
图 6:在表 6 所示的最佳 d_star 上训练的 OvO 多项式核的混淆矩阵
结束语
陷阱
- 如果使用 PyTorch,确保你实例化你知道你的数据类型,我得到的一个常见错误是转换 double 为 float!
- 确保你对你的张量运算有信心,特别是当你试图使它一般化的时候
- 从
itertools
返回一个只能运行一次的生成器对象。所以如果你想保存它,你必须把它转换成另一个保存在内存中的对象(如list
)。使用torch.combinations
等替代方法是可能的,但这需要一个torch.Tensor
作为类。 - 非常小心一对一分类器中的逻辑要求。训练 k(k-1)/2 模型很容易,但是当你试图把它转换成 k 类的预测时,问题就变得不那么简单了。
关键要点
- 核心感知器使用核心方法,通过经典感知器算法实现非线性决策表面的学习
- 由于在线学习训练,如果没有正确初始化,感知器是高度计算密集型的。优化可以通过向量化 Gram 矩阵和编写广义核函数来实现
- 任何二元分类器都可以使用 OvA 或 OvO 方案来推广
- OvA 创建 k 个分类器,为每个类别区分 1 个类别和其余类别。这些方法可能会受到类不平衡的影响,但是当数据集很小且有噪声时,它们可能会工作得更好。它们将数据置于直接竞争中,并且当分类输出未被加权时无法工作。
- OvO 创建 k(k-1)/2 个分类器,用于区分类别(ci,cj)的唯一组合。他们的优势是允许加权和平等的投票方案。
再现性
如果你想重现结果,请查看 GitHub 的页面。如果您对它的任何方面有任何问题,请联系我!
如果你真的喜欢我的工作并愿意支持我,请考虑使用我的推荐链接来获得一个媒体订阅。
除非另有说明,所有图片均由作者创作。