使用 Numpy 的深度学习优化算法的性能比较
在本文中,我们将通过计算二次凸函数的最优点,从数值上比较主要深度学习优化算法的性能。
简介:
深度学习被称为人工智能的未来。如今,神经网络被称为通用函数逼近器,即它们有能力表示这个宇宙中的任何复杂函数。计算这个具有数百万个参数的通用函数背后的想法来自优化的基础数学。优化可以通过多种方式完成,但对于本文,我们将重点关注基于梯度下降的优化技术。
非凸函数的优化是主要的研究领域。多年来,不同的科学家提出了不同的优化算法来优化神经网络的成本函数。这些算法中的大多数都是基于基于梯度的方法,只是稍加修改。在这篇文章中,我们将讨论五个主要的基于下降的算法-梯度下降,动量,Adagrad,RMSprop,Adam。
进场-
为了了解每个算法在实际中是如何工作的,我们将使用一个凸二次函数。我们将每种算法运行固定的迭代次数(20 次),以比较它们在达到最佳点时的收敛速度和轨迹。下面给出了该任务所选函数的方程,以及使用 Matplotlib 绘制的该函数的三维图形和水平集。
函数方程
功能的 3D 图
3D 绘图的数字实现
二次函数的水平集
最初,我们将从最基本的算法梯度下降开始,然后我们将跟随他们发展的趋势,以支持每个算法发展背后的思想。所以趋势会是这样的-
1-梯度下降
2-动量
3-阿达格勒
4-RMSprop
5-亚当
因为所有这些算法在更新规则的每次迭代中都需要梯度。因此,下面给出了函数的梯度,它将用于在每次迭代中更新两个变量。对于所有算法,我们将使用学习率的固定值=0.4。
渐变矢量
1-梯度下降:
梯度下降是寻找最优解的最传统的技术。在该算法中,使用当前梯度(gt)乘以某个称为学习率的因子来更新当前权重, *α。*更新规则的等式如下所示。
更新梯度下降规则
梯度下降的数值实现
轨迹使用梯度下降
正如我们在上面的图中看到的,梯度下降经历了许多振荡,收敛非常缓慢。因此,在后面的部分,我们将研究梯度下降的修改,帮助我们实现稳定和更快的收敛。
2-势头:
带动量的梯度下降是一种非常常用的优化器,它消除了由标准梯度下降引起的振荡,并加速了收敛到最佳点。当它加速水平方向时,它减慢垂直方向。在这种杰出行为的帮助下,它使我们在学习率方向上迈出了一大步。除此之外,动量比标准梯度下降稳定得多。
它不是在当前时间步长计算梯度,而是通过参数动量更新权重,动量是当前和过去梯度的指数移动平均值的集合。下面给出的等式解释了更新规则
动量更新规则
Numpy 实现的动量
梯度下降 vs 动量
3-阿达格拉德
自适应梯度下降算法是一种学习梯度下降算法。它的主要区别是 Adagrad 根据网络中参数的重要性对每个权重使用不同的学习率。换句话说,当不必要的参数以较高的学习率训练时,重要的参数以较小的学习率训练以更稳定地收敛。这导致在不允许失真的情况下加速算法。更新公式类似于动量公式,这里每一步的动量都是用先前的动量和梯度的平方来计算的。下面的等式显示了 Adagrad 中的更新规则。
这里,Gt 是由过去梯度的平方和组成的对角矩阵, ϵ是平滑项。此外,⊙表示矩阵-矢量积运算。
Adagrad 的 Numpy 实现
动量 vs 阿达格拉德
从上图可以看出,虽然阿达格拉德呈现出平滑的运动,没有振荡,但是收敛能力不足。为了解决这个问题,Geoffrey Hinton 引入了一个更有效的优化器 RMSprop。
4-RMSprop
Rmsprop 是由著名计算机科学家 Geoffrey Hinton ( Hinton et al .,2012 )提出的另一种高效优化算法。这种算法的工作原理与 Adagrad 相似,只是稍加修改。我们采用这些梯度的指数移动平均值,而不是像 AdaGrad 那样采用梯度平方的累积和。使用指数平均的原因是,较新的梯度更新比不太新的梯度更新更重要。下面的等式显示了 Rmsprop 的更新规则。
RMSprop 的 Numpy 实现
动量 vs 阿达格拉德 vs RMSprop
可以明显看出,当向最优点收敛时,梯度下降算法在垂直方向上产生巨大的振荡,RMSprop 限制垂直方向的运动并加速水平方向的运动。此外,尽管动量减少了振荡,RMSprop 提供了更可靠和更快的收敛。
5-亚当
自适应矩估计是为每个参数计算自适应学习率的另一种类型的优化器。与其他优化器相比,它表现得更加鲁棒和可靠,因为它基本上结合了动量和 RMSprop(即,类似梯度的动量的移动平均和使用平方梯度来调整类似 RMSprop 的学习速率)。更准确地说,Adam 算法执行如下
Adam 的 Numpy 实现
动量 vs Adagrad vs RMSprop 和 Adam 收敛期间的轨迹
如最后一幅图所示,Adam 优化器在直线路径上实现了最快的收敛。对于 Adam,它通过累加之前梯度的总和来抑制振荡,并且由于与 RMSprop 类似的平方梯度项,它遵循一条直线。这导致了一个明显的结论,即 Adam 是 momentum 和 RMSprop 的组合版本。从上图我们不难看出,为什么 Adam 是深度学习中最受欢迎的优化器。即使从远离最优(-6,-6)的点开始,Adam 也能以最少的迭代收敛得最快。
如需了解详情,请访问—https://github.com/Saket-Uoft/Deep-Learning-Optimizers
感谢阅读!
如果你喜欢我的工作,想支持我。支持我的最好方式是在媒体上关注我。
参考资料:
https://medium . com/@ sdoshi 579/optimizer-for-training-neural-network-59450d 71 caf 6
比较 Amazon Textract 和 tesserract OCR—OCR 和 NLP 用例
用于光学字符识别(OCR)和自然语言处理的两种已知引擎的比较
OCR 到底是什么,为什么这么热门?人工智能(AI)使具有人类智能(us)的实体能够大规模处理数据——更快、更便宜。毫无疑问,很大一部分数据是以数字形式保存的,易于阅读和分析。然而,有很大一部分数据存储在物理文档中,既有打字的也有手写的。如何分析这类数据?这就是引人入胜的光学字符识别(OCR)技术的用武之地。使用 OCR,您可以将文档转换为适合编辑和搜索的文本格式的数据。这就是 OCR 能够做到的。
图片由 Gerd Altmann 从 Pixabay 拍摄
在本文中,我们将关注两个众所周知的 OCR 框架:
- Tesseract OCR —在 Apache 许可下发布的自由软件,版本 2.0——自 2006 年以来一直由谷歌赞助开发。
- Amazon Textract OCR — 来自 Amazon 的完全托管服务,使用机器学习来自动提取文本和数据
我们将比较这两个框架的 OCR 能力。让我们从一个简单的图像开始,如下图所示:
作者图片—typewritten.jpg
$ git clone https://github.com/mkukreja1/blogs.git
下载并安装笔记本 blogs/ocr/OCR.ipynb
!pip install opencv-python
!pip install pytesseract
!pip install pyenchantimport cv2
import pytesseract
import re
from pytesseract import Outputimg_typewritten = cv2.imread('typewritten.jpg')
custom_config = r'--oem 3 --psm 6'
txt_typewritten=pytesseract.image_to_string(img_typewritten, config=custom_config)
print(txt_typewritten)
使用镶嵌 OCR 的 OCR 输出:
最佳影片福特 V 法拉利
爱尔兰人
乔乔兔子
小丑
小女人
婚姻故事
1917
从前……在好莱坞
寄生虫
使用 Amazon Textract OCR 的 OCR 输出:
两个框架的表现完全相同。让我们看看手写文本是如何比较的。
作者图片—handwritten.jpg
img_handwritten = cv2.imread('handwritten.jpg')
txt_handwritten=pytesseract.image_to_string(img_handwritten)
print(txt_handwritten)
使用镶嵌 OCR 的 OCR 输出:
<>
疮
&&.一)
哎哟!NS ané
Lp 房地产集团
大使 ATH。远离|房地产
洛美服务
我很高兴今天能和我见面,我也很高兴听到我的声音。再见
AA aiv Go|干杯|
梅根·欧文斯,房产经纪人 402–689–4984www。ForSalebyMegan 。
多媒体短信服务
com
使用 Amazon Textract OCR 的 OCR 输出:
作者图片
对于手写文本,亚马逊 Textract OCR 的表现略好于 Tesseract OCR 。
现在我们将尝试一个繁忙的图像。
作者图片—invoice-sample.jpg
img_invoice = cv2.imread('invoice-sample.jpg')
custom_config = r'--oem 3 --psm 6'
txt_invoice=pytesseract.image_to_string(img_invoice, config=custom_config)
print(txt_invoice)
使用镶嵌 OCR 的 OCR 输出:
http://mrsinvoice.comi7
贵公司 LLC 地址 123,州,我的国家 P 111–222–333,F 111–222–334
收单人:
P:111–222–333,F:111–222–334 a . z
cient @ eromplent
联系电话 101–102–103
f:122–222–334 应付金额:4170 美元
office@example.net
无产品/服务数量/费率/单位金额
小时:价格
1 赞成 2 20 美元 40 美元
2 |方向盘 5 10 美元 50 美元
3 |机油 10 美元 15 美元 150 美元
4 |刹车片 24 美元 1000 美元 2400 美元
小计 20 美元
使用 Amazon Textract OCR 的 OCR 输出:
作者图片
亚马逊 Textract 识别文档中的表格和表单。这是整齐的。
作者图片
高级功能—拼写检查
OCR 扫描的结果通常被输入到 NLP 模型中。因此,结果文本的高度准确性非常重要。我们可以用两种方式处理:
通过拼写检查模块像附魔传递每一个作品
选项 1 —如果拼写检查失败—从结果文本中屏蔽/删除该单词
选项 2——如果拼写检查失败——使用拼写检查建议并编辑结果文本
Python 中经常使用 Enchant 模块来根据字典检查单词的拼写。除了拼写检查之外,enchant 还可以给出纠正单词的建议。
img = cv2.imread('invoice-sample.jpg')
text = pytesseract.image_to_data(img, output_type='data.frame')
text = text[text.conf != -1]
lines = text.groupby('block_num')['text'].apply(list)
print(lines[25])
[‘‘感谢’,‘你’,‘为’,‘你的’,‘生意。]
请注意,第一个单词似乎有拼写问题。
import enchant
dict_check = enchant.Dict("en_US")for word in lines[25]:
if (dict_check.check(word)):
print(word+ ' - Dictionary Check Valid')
else:
print(word+ ' - Dictionary Check Valid Invalid')
print('Valid Suggestions')
print(dict_check.suggest(word))
【感谢—字典检查有效无效
有效建议
【感谢】
你—字典检查有效
为—字典检查有效
你—字典检查有效
业务。—字典检查有效
请注意,enchant 发现第一个单词无效,并能够提供替代建议。
为 NLP 使用正则表达式
我在 IT 行业的早期(大约 25 年前)充满了起起落落。有一天,我会学到一些新的东西,感觉自己站在世界之巅。其他日子就没那么多了。我记得有一天,我的经理让我解决一个模式匹配问题。我必须在 Oracle 中对数据进行模式匹配。因为我以前从来没有这样做过,所以我请求他指点一下。我得到的答案是*“使用 REGEX 可以非常容易地做到这一点”。*我是一个很好的追随者,除了使用正则表达式模式匹配并不容易。我过了一会儿才意识到我的经理是在开玩笑。
我仍然害怕使用正则表达式,但无法逃避它……它仍然广泛应用于数据科学,尤其是 NLP。学习正则表达式需要时间。我经常使用像 https://regex101.com/这样的网站进行练习。
例如,如果您想从文档中提取所有日期字段。
d = pytesseract.image_to_data(img, output_type=Output.DICT)
keys = list(d.keys())date_pattern = '^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[012])/(19|20)\d\d$'n_boxes = len(d['text'])
for i in range(n_boxes):
if int(d['conf'][i]) > 60:
if re.match(date_pattern, d['text'][i]):
(x, y, w, h) = (d['left'][i], d['top'][i], d['width'][i], d['height'][i])
img_date = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
print(d['text'][i])
产出:2001 年 12 月 12 日
您甚至可以突出显示给定文档中的日期字段
cv2.imwrite('aimg_date.png', img_date)
作者图片
我希望这篇文章有助于启动您的 OCR 和 NLP 知识。像这样的主题是由 Datafence 云学院提供的 AWS 大数据分析课程的一部分。课程是周末自己在网上教的。
比较哪种机器学习模型性能更好
自信地比较机器学习模型
一旦您理解了要解决的业务问题并确定了可用的数据集,您是否曾经对使用哪种机器学习技术感到困惑?
说真的,有这么多机器学习技术可用:线性模型,如逻辑回归,非线性模型,如基于树的,神经网络,支持向量机,在线和强化学习,贝叶斯模型,图形模型等。即使我们能够决定一个,也有大量的特征工程是可能的,这使得一个模型与其他模型非常不同。有一些可用的集成技术试图结合所有世界(模型)的精华,但它们是有成本的。这里的成本可能是时间、精力和把事情复杂化。一个好的机器学习建模者总是喜欢保持事情简单。
这个帖子是关于如何在高置信度的机器学习模型之间进行比较
方案
假设我们建立了两个机器学习模型,模型‘A’和模型‘B’。这两个模型都在验证集上进行了适当的训练和测试。现在,企业想知道哪种模式性能更好,因为在计算和时间方面资源有限。
经理给你分配了这个任务,挑选置信度较高的更好模型,假设置信度为 90%或 显著性水平 (α=0.10) 。或者报告,如果很难宣布一个明确的赢家,集合似乎是唯一的办法。你的策略是什么?
战略
模型每周都会生成值。我们有 10 周的时间来得出结论。我们开始注意模型预测的准确性和未来 10 周的实际情况。下表总结了这两种模型的准确性得分。
模型在 10 周时间内的准确性
μ(A) = 73.70,σ(A)= 6.90[RVA】的均值和方差]]
μ(B) = 75.80,σ(B)= 5.51[RVB 的均值和方差]]
从两个随机变量的平均值和标准偏差来看,似乎模型‘B’表现更好。 但是我们有多大的信心或把握呢?
创建假设检验框架
让我们首先定义无效假设和替代假设。
- H₀:“两款车型表现同样出色。”
- h₁:“b 型表现更好。”
继续之前快速复习一下概念(可选)
- 中心极限定理:抽样分布的样本均值,给定我们取足够的样本(n ≥ 30),无论总体遵循什么分布,都将具有正态分布。
此外,对于采样分布
μ(X̄) = μ(X)和σ(X̄) = σ(X)/√n
这里,x 是表示总体的随机变量,X̄表示抽样分布,μ表示平均值和σ标准差(√方差)
- Z-Score/Z-statistics:
“Z-Score 是一种数值度量,用于统计一个值与一组值的平均值(平均值)之间的关系,根据平均值的标准偏差进行测量。如果 Z 分数为 0,则表明数据点的分数等于平均分数。Z 值为 1.0 表示该值是平均值的一个标准差。z 分数可以是正值,也可以是负值,正值表示分数高于平均值,负值表示分数低于平均值。(此处抄袭此处)
一旦我们有了 Z 分数,我们就可以查看 Z 表并对事件的发生赋予正确的置信度。
z 分数计算公式
- T-score/T-statistics/T-distribution
“T 分布,又称学生 T 分布,是一种概率分布类型,类似于正态分布,具有钟形,但尾部较重。t 分布比正态分布出现极值的机会更大,因此尾部更厚。”(从这里复制)
t-score 的公式与 z-score 类似,不同之处在于,它在样本量< 30.
- 增加随机变量时使用
对于任意两个随机变量 X 和 Y,这些变量之和的期望值将等于它们的期望值之和。
E(X+Y)=E(X)+E(Y)对于任意两个随机变量 X 和 Y,这些变量之和的方差等于方差之和加上协方差的两倍。
Var(X+Y)= Var(X)+Var(Y)+2Cov(X,Y)如果 X 和 Y 是独立的,那么
Var(X+Y)= Var(X)+Var(Y)
回到真正的问题
我们创建了假设框架
H₀ =这两种型号的表现一样好。
H₁ =模型的表现不一样(模型 b 表现更好)。
E(B-A)= E(B)-E(A)=μ(B)-μ(A)= 2.09
Var(B-A)= Var(A)+Var(B)=σ(A)+σ(B)= 12.41
对于,H₀为真,E(B-A)应该为 0
使用 CLT,我们可以计算抽样分布的样本均值的 t 得分,即它为 0 的可能性有多大。
t 得分= 0-E(B-A)/√(Var(B-A)/n)
这里 n = 10,E(B-A) = 2.09,Var(B-A)= 12.41
t-score =-2.09/√( 12.41/10)=-1.87,自由度= 10-1 = 9
- 自由度总是 n-1。(为什么?参考此
接下来,我们可以看看 t 表。为了达到 90%的置信度或显著性水平 (α=0.10),即两个模型在自由度= 9 的情况下表现相同,我们的 t 得分应≥ -1.833。但是,事实并非如此,t 值为-1.87 ( < -1.833)
t 表(单侧)和 90%置信度 t 分数值
t 分布,90%的置信度得分和我们的 t 分布得分
我们有 90%以上的把握 0 不在这个区间内下跌。换句话说,在 90%的置信度下,模型 B 和模型 A 的平均准确度之差大于 0。因此,我们拒绝零假设,并赞成替代假设。
结论
我们可以通知经理和企业,我们已经从统计上发现 B 型比 A 型表现得更好,因此我们可以继续生产 B 型。
在这里,我们证明了模型 B 优于 A,但如果让我们以 90%的置信度证明模型 B 至少比 A 好 2%,你的策略会是什么?你会在假设框架上做哪些改变?请评论您的想法:)
我的 Youtube 频道获取更多内容:
嗨,伙计们,欢迎来到频道。该频道旨在涵盖各种主题,从机器学习,数据科学…
www.youtube.com](https://www.youtube.com/channel/UCg0PxC9ThQrbD9nM_FU1vWA)
关于作者-:
Abhishek Mungoli 是一位经验丰富的数据科学家,拥有 ML 领域的经验和计算机科学背景,跨越多个领域并具有解决问题的思维方式。擅长各种机器学习和零售业特有的优化问题。热衷于大规模实现机器学习模型,并通过博客、讲座、聚会和论文等方式分享知识。
我的动机总是把最困难的事情简化成最简单的版本。我喜欢解决问题、数据科学、产品开发和扩展解决方案。我喜欢在闲暇时间探索新的地方和健身。在 中 、Linkedin或insta gram关注我,查看我以前的帖子。我欢迎反馈和建设性的批评。我的一些博客-****
比较各种朴素贝叶斯分类算法
数据科学模型
文本分类公式的综合列表
在 Unsplash 上 Leone Venter 拍摄的照片
N aive Bayes 算法是著名的监督分类算法之一。它基于贝叶斯定理,速度非常快,对于文本分类来说足够好。我认为没有必要描述背后的理论,尽管如此,我们将介绍一些概念,然后将重点放在不同实现的比较上。
1.概念
首先,我们采用贝叶斯公式本身:
这是什么意思?既然可能会混淆,我们就换个眼神。我们将从垃圾邮件的角度讨论事件的类别。所以现在我们有两类事件:垃圾邮件和非垃圾邮件。
让我们将 A 改为收到垃圾邮件的事件( y ),将 B 改为由一组单词(x1,x2,…)组成的消息。所以:
现在更明显了:包含单词(x1,x2,…)的消息是垃圾邮件的概率等于收到垃圾邮件的一般概率,乘以垃圾邮件包含上述单词的概率,再除以任何传入消息包含给定单词的概率。
朴素贝叶斯定理说,集合(x1,x2,…)中所有事件的概率可以被视为独立的,所以:
这仍然是一个有点复杂的公式,但我们可以简化它。显然,我们需要计算邮件是垃圾邮件的概率和邮件不是垃圾邮件的概率。因为我们需要比较这两个值,而且它们在同一个字段中,并且具有相同的除法器( P(x1,x2,…) ),所以我们只能使用分子:
我们可以从分布中找到从数据集获取垃圾邮件的一般概率。所以,主要的问题是找到每个单词出现在垃圾消息中的条件概率( P(xi | y) )。这几乎是朴素贝叶斯算法实现之间的唯一区别。
2.履行
我已经从头开始构建了一个算法的实现:
有效模型的简单实现
towardsdatascience.com](/how-to-build-and-apply-naive-bayes-classification-for-spam-filtering-2b8d3308501)
不过,现在让我们研究一下 sklearn 库的能力。出于我们的目的,我们将使用 Tiago A. Almeida 和 José María Gómez Hidalgo 收集的短信。它是免费的,可以从UCI 机器学习库下载。它包含 5572 条不同消息的记录以及 747 条垃圾消息。
我们将每条信息拆分成一组单词,并对标签进行编码。之后,我们将数据集分为训练和测试部分。
下一步是最重要的:我们将训练部分转向词汇,并计算每个消息中每个词汇的重复次数(在训练和测试部分):
现在数据集已经为模型创建做好了准备。
2.1.高斯朴素贝叶斯
这种方法建立在概率正态分布的假设上。也就是说,垃圾邮件和非垃圾邮件类别的消息中,词汇的出现频率符合高斯定律:
该公式基于消息类中每个词的频率的平均值( μ 和贝塞尔校正方差( σ )。
cl_gauss = **sklearn**.naive_bayes.**GaussianNB**()
res_gauss = cl_gauss.fit(X_train_voc, y_train).predict(X_test_voc)
metrics.accuracy_score(y_test, res_gauss) * 100
结果是模型精度 91.03% 。这已经足够好了,但是说明了单词并不完全是高斯分布。
2.2.多项式朴素贝叶斯
多项式分类最适合离散值,如字数。所以我们希望它能表现出最好的准确性。在这种情况下,每个事件的概率分布基于以下公式:
Ny 是事件的特征总数 y (所有垃圾短信中的总字数) Nyi —每个特征的计数(一个词在所有垃圾短信中重复的汇总数) n —特征数(词汇中的字数) α 是一个平滑拉普拉斯参数,用于丢弃词汇中不存在的词的影响。同样的公式也适用于非垃圾邮件消息集。
cl_multi = **sklearn**.naive_bayes.**MultinomialNB**()
res_multi = cl_multi.fit(X_train_voc, y_train).predict(X_test_voc)
metrics.accuracy_score(y_test, res_multi) * 100
模型精度 99.19% ,是(剧透)包括定制款在内的测试模型中最高的。
2.3.互补朴素贝叶斯
这种方法几乎与多项式相同,尽管现在我们计算一个单词在该类补集中的出现次数。例如,对于垃圾邮件,我们将计算每个单词在所有非垃圾邮件中的重复次数:
Nc —相反类别中的总字数(对于垃圾邮件参数—非垃圾邮件字数),Nci —相反类别中某个单词的重复次数(对于垃圾邮件中的某个单词—在所有非垃圾邮件中的重复次数)。我们也使用相同的平滑参数。计算基本值后,我们开始处理实际参数:
它是 k 个单词的消息中每个单词的权重。最终决策由以下公式计算得出:
因此,分类结果是邮件中每个单词的权重之和的最小值。
cl_compl = **sklearn**.naive_bayes.**ComplementNB**()
res_compl = cl_compl.fit(X_train_voc, y_train).predict(X_test_voc)
metrics.accuracy_score(y_test, res_compl) * 100
我们得到了 98.12%的正确预测,这是一个非常高的结果。
2.4.伯努利朴素贝叶斯
伯努利公式接近于多项式公式,尽管输入是一组布尔值(该词是否出现在消息中)而不是一组频率。
因此,该算法明确地惩罚特征的不出现(消息中的单词在词汇表中不存在),而多项式方法对不存在的值使用平滑参数。sklearn
伯努利算法将输入值二进制化,因此无需额外操作。
cl_bern = **sklearn**.naive_bayes.**BernoulliNB**()
res_bern = cl_bern.fit(X_train_voc, y_train).predict(X_test_voc)
metrics.accuracy_score(y_test, res_bern) * 100
我们已经得到了 98.30% 的正确预测
2.5.分类朴素贝叶斯
分类朴素贝叶斯适用于分类值-无论示例是否具有特征集。在我们的例子中,这意味着词汇表被视为一组特征,而一个单词在消息中的出现被视为与该特征的匹配。所有公式都与多项式方法相同,只是出现次数不同于重复次数。
由于算法需要分类值,我们将单词的频率转换为单词的存在:1-消息包含该单词,0-该单词不在消息中。
X_train_voc_cat= X_train_voc.applymap(**lambda** el: 1 **if** el > 0 **else** 0)
X_test_voc_cat = X_test_voc.applymap(**lambda** el: 1 **if** el > 0 **else** 0)
现在,我们将模型应用于更新的数据集:
cl_cat = **sklearn**.naive_bayes.**CategoricalNB**()
res_cat=cl_cat.fit(X_train_voc_cat,y_train).predict(X_test_voc_cat)
metrics.accuracy_score(y_test, res_cat) * 100
准确率相当高: 98.30% 。
在本文中,我们熟悉了几种朴素贝叶斯算法,理解了它们背后的数学原理,并测试了公式。结果自说自话。您可以在我的 Github 上找到包含所有工作模式的 Jupyter 笔记本:
permalink dissolve GitHub 是 4000 多万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/Midvel/medium_jupyter_notes/blob/master/scikit_bayes/scikit-bayes.ipynb)
此外,看看一个自定义算法的实现,从头开始,没有任何带模型的库:
有效模型的简单实现
towardsdatascience.com](/how-to-build-and-apply-naive-bayes-classification-for-spam-filtering-2b8d3308501)
机器学习模型的 AUC 与德隆检验的比较
根据卡洛斯·德尔加多的照片修改。此处有原始照片。
你有没有想过如何证明一个机器学习模型的测试集性能与另一个模型的测试集性能有显著差异?本文将描述如何使用 DeLong 检验来获得一个模型的 AUC 是否与另一个模型有显著差异的 p 值,其中 AUC 是指受试者操作特征下的面积。这篇文章包括一个手工计算的例子来说明德隆对一个小数据集测试的所有步骤。它还包括 DeLong 测试的一个示例 R 实现,以支持大型数据集上的高效计算。
德隆测试的一个用例:模型 A 预测心脏病风险,AUC 为 0.92,模型 B 预测心脏病风险,AUC 为 0.87,我们使用德隆测试证明模型 A 的 AUC 与模型 B 有显著差异,p < 0.05.
参考
伊丽莎白·雷·德隆是杜克大学的统计学家和教授。1988 年,她发表了一项测试,以确定两个模型的 AUC 在统计学上是否有显著差异。这个测试被称为“德隆测试”
这篇文章的主要参考资料是:
- Elizabeth DeLong 等人,“比较两条或多条相关接收机工作特性曲线下的面积:一种非参数方法。”生物统计学 1988 年。
- 孙旭等人,“德龙算法的快速实现,用于比较相关接收机工作特性曲线下的面积。”IEEE 信号处理快报 2014。
方便的是,两篇论文都使用了相似的符号,这也是我们将在本文中使用的符号。
灵敏度和特异性的定义
为了理解德隆的测试是如何工作的,我们需要从一些符号开始。我们将通过用它来定义敏感性、特异性和 AUC 来习惯这种符号。
考虑这样一种情况,我们建立了一个模型来预测一个人是否患有疾病。我们对总共有 N 名患者的测试数据集进行预测,其中 m 名患者真正患病,而 n 名患者真正健康。我们的模型为第位患病患者产生预测概率 X ,为第位健康患者产生预测概率 Y :
使用这种符号,我们可以写出灵敏度和特异性的定义:
敏感度也称为回忆率或真实阳性率。它是被模型正确识别为阳性(患病)的真阳性(患病人)的比例。我们将特定决策阈值 z 下的灵敏度定义如下:
特异性也被称为真阴性率。是真阴性(健康人)被模型正确识别为阴性(健康)的比例。我们将特定决策阈值 z 下的特异性定义如下:
这意味着当我们改变决策阈值 z 时,我们将改变灵敏度和特异性。
AUC 的定义和经验 AUC 的计算
受试者工作特性(ROC)提供了不同阈值 z 的灵敏度和特异性汇总。我们通过改变阈值 z 并在每个 z 值处绘制灵敏度对(1 —特异性)的曲线来构建 ROC 曲线。
ROC 曲线下的面积(或 AUC)提供了一个数字来总结模型在所有不同决策阈值下的表现。
在的论文中,DeLong 等人描述了一种方法,其中经验 AUC“通过对构成 ROC 曲线的连接点下方形成的梯形面积求和来计算”(参考)。
就上下文而言,DeLong 的经验 AUC 方法不同于二项式 AUC 方法。二项式 ROC 曲线是“基于对应于阳性条件的诊断测试分数和对应于阴性条件的分数均可由正态分布表示的假设”而构建的(“T18”参考)。
经验 AUC 方法比二项式 AUC 方法更受欢迎,因为经验 AUC 不依赖于二项式 AUC 所要求的强正态假设。
我们可以使用梯形规则计算经验 AUC(表示为“theta hat”),如下所示:
AUC 的这一定义具有直观意义:
- 当 Y < X, this means that the predicted disease probability of a healthy individual is less than the predicted disease probability of a sick individual, which is good: we want actually healthy people to have lower predicted disease risk than actually sick people. So, we reward the model for its good prediction and make a contribution to the model’s AUC of +1/mn.
- When Y = X, this means that the predicted disease probability of a healthy individual is equal to the predicted disease probability of a sick individual, which isn’t awesome but isn’t horrible, so we make a contribution to the model’s AUC of (+1/2)/mn.
- When Y > X 时,这意味着健康个体的预测疾病概率大于患病个体的预测疾病概率,这是不好的:该模型认为实际健康的人比实际患病的人具有更高的疾病风险。在这里,我们不对模型的 AUC 添加任何东西(+0)。
函数 psi(X,Y)也称为heav iside 函数(具有半最大值约定)。
关于敏感性、特异性以及如何构建 ROC 曲线的更多背景信息,请参见这篇文章。
AUC 和 Mann-Whitney 统计之间的联系
在之前的一篇文章中,我描述了 AUC 如下:
AUROC 告诉您您的模型是否能够正确地对示例进行排序:对于临床风险预测模型,AUROC 告诉您随机选择的经历了某个事件的患者比随机选择的未经历该事件的患者具有更高的预测风险评分的概率。
事实证明,这个定义是正确的,因为经验 AUC 等于 Mann-Whitney U 统计量。引用德隆等人的话,
当使用梯形法则计算时,包含经验 ROC 曲线的点下的面积等于比较两个样本值分布的 Mann-Whitney U 统计量。[……]Mann-Whitney 统计估计从 C2[健康人]代表的人群中随机选择的观察值小于或等于从 C1[病人]代表的人群中随机选择的观察值的概率θ。
用另一种方式表述同一观点,维基百科将曼-惠特尼 U 检验描述为:
零假设的非参数检验,即从一个总体中随机选择的值小于或大于从第二个总体中随机选择的值的可能性相等。
因此,总结一下,
请注意:
比较两种模型 AUC 的德隆试验
示例设置
既然我们已经完成了经验 AUC 和所有符号的定义,我们可以继续描述德隆检验如何确定一个模型是否具有与另一个模型在统计上显著不同的 AUC。
这是一个玩具测试集,由五名患者组成,预测来自两个模型。我们将在工作示例中使用这个玩具测试集:
该数据集包括两名真正健康的患者和三名真正患病的患者,他们的健康状况是以某种明确的方式确定的,因此我们可以将其视为“基本事实”模型 A 的列显示了根据模型 A 预测的所有患者的疾病概率。按照前面介绍的符号,这些预测概率由健康患者的 a Y 和患病患者的 X 表示。模型 B 的列显示了根据模型 B 预测的所有患者的疾病概率
请注意,模型 A 是一个具有完美 AUC 的分类器(稍后将明确演示),因为所有健康患者的患病概率都低于所有患病患者。模型 B 没有完美的 AUC。
根据论文的注释,正在考虑的模型总数是 1 <= r <= k where k = 2 (because we are only considering 2 models here.) n = 2 (the number of healthy patients) and m = 3 (the number of sick patients.)
德隆测试的目标
我们想知道就 AUC 而言,模型 A 还是模型 B 更好,其中θ-hat(A)是模型 A 的 AUC,θ-hat(B)是模型 B 的 AUC。为了回答这个问题,我们将计算一个 z 分数:
引用孙等人的话,
在零假设下, z 可以很好的近似为标准正态分布。因此,如果 z 的值偏离零太多,例如z1.96,则认为显著性水平 p <为 0.05 的[theta(A) > theta(B)]是合理的。
换句话说,如果 z 偏离零太多,那么我们可以得出结论,模型 A 与 p <为 0.05 的模型 B 具有统计上不同的 AUC。
为了找到 z 分数,我们需要计算经验 AUC、方差 V 和协方差 c。以下部分将展示如何计算这些量。
(旁注:此处“z 得分”中“z”的使用与前面讨论“z 作为决策阈值”中“z”的使用完全无关。)
计算模型 A 和模型 B 的经验 AUC
根据前面提供的经验 AUC 的定义,我们计算模型 A 的经验 AUC:
类似地,我们计算模型 B 的经验 AUC:
注意,模型 A 的 AUC 为 1.0,因为它完美地将所有患病患者归类为比所有健康患者具有更高的患病概率。模型 B 的 AUC 为 2/3,因为它不能完美地对所有患者进行排序。
结构部件 V10 和 V01
德龙测试需要的下一步是计算 V10 和 V01,它们被孙等人称为“结构组件”。
V10 和 V01 将帮助我们找到计算 z 分数所需的方差和协方差。V10 和 V01 定义如下:
回想一下,“r”代表我们正在考虑的型号,因此我们对 r = A(型号 A)和 r = B(型号 B)有不同的结构组件计算。
对于我们的小示例数据集,模型 A 和 B 的结构组件计算如下:
矩阵 S10 和 S01
接下来,我们将使用结构成分 V10 和 V01,结合我们的经验 AUC,来计算矩阵 S10 和 S01,其定义如下:
矩阵 S10 和 S01 是 k×k 矩阵,其中 k 是我们正在考虑的模型总数。因为我们只考虑两个模型(模型 A 和模型 B ),所以每个矩阵都是 2 x 2。
以下是矩阵 S10 中条目的计算:
结果,对于矩阵 S10,除了(B,B)项之外,所有的项都是零。
以下是矩阵 S01 中条目的计算:
矩阵 S01 以该特定示例的所有零条目结束。
计算方差和协方差
现在,我们将使用 S10 和 S01 来获得矩阵 S,其中矩阵 S 包含我们需要的方差和协方差,以便获得德隆测试的 z 分数。我们所要做的就是将 S10 和 S01 相加,分别基于 1/ m=3(患病人数)和 1/ n=2(健康人数)进行加权:
从上面的计算中可以看出,我们最终的 S 矩阵包含模型 A 的经验 AUC 方差、模型 B 的经验 AUC 方差以及模型 A 和 B 的 AUC 协方差。这些是我们获得 z 分数所需的关键量。
Z 分数的计算
为了计算 z 得分,我们插入刚刚计算的经验 AUC、方差和协方差的值:
使用 Z 分数获得 P 值
我们算出来的 z 分数是 1。我们如何由此获得 p 值?
我们正在进行“双尾检验”,因为我们试图宣称模型 A 的 AUC 不同于(“不等于”)模型 b 的 AUC。
我们可以使用一个 z 统计量的双尾 P 值的查找表。在表中,在“十分之一”(垂直)下我们定位为 1.0,在“百分之一”(水平)下我们定位为 0.00,因为我们的 Z 值是 1.00。表中这个位置的条目是 0.31731,这是我们的 p 值。
或者,为了避免在一大堆数字中搜索,我们可以使用像这样的在线计算器。我们需要选择“双尾假设”并输入我们的 z 得分 1,这将产生 p 值 0.317311(与我们从查找表中得到的结果一致。)
计算 R 中的德龙测试
手工计算对于微小的玩具数据集来说很好,但对于任何真实的数据集,我们都希望使用软件来计算德龙的测试。一种选择是使用 r 中的 pROC 包。
使用 pROC 包,我们首先使用 roc 函数创建两条 ROC 曲线进行比较。roc 函数可以作为 roc(响应,预测值)在“响应”(基本事实)和“预测值”(预测概率)上调用。
对于我们的模型 A 和模型 B 示例,我们有:
- 响应
- modela
- modelb
- roca
- rocb
Now that we have built our ROC curves, we can apply the pROC roc.test 函数比较两条 ROC 曲线的 AUC。
输出:
两条相关 ROC 曲线的德隆检验
数据:roca 和 rocb
Z = 1,p 值= 0.3173
替代假设:AUC 的真实差异不等于 0
样本估计:
roc 1 的 AUC roc 2 的 AUC
1.0000000 0.666667
这与我们上面手动获得的结果相匹配。
R 中德隆测试的另一个例子
下面是德隆在 R 中的测试的另一个例子,显示了当使用不同的基本事实和预测概率时,结果是如何不同的:
响应 modela < -c(0.1,0.2,0.05,0.3,0.1,0.6,0.6,0.7,0.8,0.99,0.8,0.67,0.5)
modelb < -c(0.3,0.6,0.2,0.1,0.1,0.9,0.23,0.7,0.9,0.4,0.77,
roc.test(roca,rocb,method=c("delong "))
两条相关 ROC 曲线的德隆检验
数据:roca 和 rocb
Z = 1.672,p 值= 0.09453
替代假设:AUC 的真实差异不等于 0
样本估计:
roc 1 的 AUC roc 2 的 AUC
0.9642857 0.7380952
总结
- 德隆检验可用于显示两个模型的 AUC 在统计上显著不同。
- ROC 曲线总结了不同决策阈值下的敏感性和(1-特异性)。AUC 是 ROC 曲线下的面积。
- 使用 ROC 曲线上的梯形规则计算经验 AUC。
- 德隆检验需要计算经验 AUC、AUC 方差和 AUC 协方差。
- pROC 包中的 R 实现可以快速计算 DeLong 的测试。
关于特色图片
特色图片是一只埃及秃鹰。最初的秃鹫照片是由 Carlos Delgado CC-BY-SA 拍摄的,可以在维基百科这里找到。这篇文章中显示的照片已经被修改,以包括 AUC 图和与德隆测试相关的方程。修改后的照片在与原始照片相同的许可下分发。
原载于 2020 年 2 月 4 日http://glassboxmedicine.com。
葡萄酒质量预测分类模型的比较
选择监督机器学习模型预测葡萄酒质量的项目演练
Matthieu Joannon 在 Unsplash 上拍摄的照片
目录
简介
这是我的第一篇博文!由于我决定将职业从化学工程转向数据科学,所以我将自己置于一个持续的、永无止境的学习过程中,我决定以博客帖子的形式分享我在项目中所学到和应用的东西。
在这个项目中,我想比较几个分类算法来预测葡萄酒质量,得分在 0 到 10 之间。由于我更喜欢白葡萄酒而不是红葡萄酒,我决定通过使用来自 UCI 机器学习库的 winequality-white.csv 数据来比较和选择一种算法,以找出是什么造就了一种好酒。该数据集中的属性包括:
- 固定酸度
- 挥发性酸度
- 柠檬酸
- 残糖
- 氯化物
- 游离二氧化硫
- 二氧化硫总量
- 密度
- pH 值
- 硫酸盐化
- 酒精
- 质量
探索性分析
探索性数据分析是一个非常重要的步骤,有助于了解数据集是什么样的,以及我们需要做什么样的修改。我开始导入库和模块,并将我将使用的数据读入 pandas dataframe。
导入库和模块
import matplotlib.pyplot as plt
from scipy import stats
import seaborn as sns
import pandas as pd
import numpy as np
from sklearn import metrics
读取数据
white_wines = pd.read_csv('winequality/winequality-white.csv')
理解数据
为了理解这些数据,我查看了前几行。它看起来很干净,但是它有 4898 行数据,仅仅通过查看来检查所有的行以确定数据是否有问题是不明智的。所以我将在下一步中立刻检查所有丢失的值。
print(white_wines.shape)
white_wines.head()
检查缺失值
数据中没有缺失值,因此可以进行分析,但在建模步骤之前,必须首先检测并消除异常值。
white_wines.isnull().any() fixed acidity False
volatile acidity False
citric acid False
residual sugar False
chlorides False
free sulfur dioxide False
total sulfur dioxide False
density False
pH False
sulphates False
alcohol False
quality False
dtype: bool
检测异常值
我应用了 df.describe()方法来了解数据集的描述性统计信息。对数据集中的问题有一个直观的了解是一个非常好的方法。快速浏览一下,我可以看到有一些异常值。
让我们看看‘残糖’一栏。 count 表示这一列有 4898 行。表示,STD分别表示该列的平均值和标准偏差值,其中 25% 小于 1.70, 75% 小于 9.90。有趣的是,平均值为 6.39,最小值为 0.60,最大值为 65.80。它看起来像一个离群值。
white_wines.describe()
我使用箱线图来可视化“残余糖”列中的值的分布,以获得更好的洞察力。实际最大值约为 20,大于该值的值为异常值,因为它们不包括在观察范围内。
sns.boxplot(white_wines[‘residual sugar’])
使用 Z 值消除异常值
大多数时候,删除离群值很重要,因为它们很可能会影响机器学习模型的性能。但是假设你的数据集有 30%是异常值。那么将它们全部移除可能是不明智的,因为可能有更多的问题需要进一步检查。为了找到并去除异常值,我使用了 z-score。
z 分数的数学公式
它的解释是取数据点或观察值,减去总体的平均值,然后除以标准差。它表示一个数据点距离平均值有多少标准差。离平均值太远的数据点被认为是异常值。在大多数情况下,异常值检测的阈值是 z 得分> 3 或 z 得分
z = np.abs(stats.zscore(white_wines))
white_wines = white_wines[(z < 3).all(axis=1)]
white_wines.shape(4487, 12)
检查属性之间的相关性
当我检查列之间的相关性时,我可以看到一些特性与质量有很强的相关性,而另一些则没有。
plt.subplots(figsize=(15, 10))
sns.heatmap(white_wines.corr(), annot = True, cmap = ‘coolwarm’)
检查班级不平衡
作为探索性数据分析的最后一步,我想检查班级的不平衡。似乎存在着严重的阶级不平衡,少数阶级的代表性低于多数阶级。这将是后面步骤中建模的关键部分。
white_wines[‘quality’].value_counts()6 2038
5 1309
7 855
8 161
4 124
Name: quality, dtype: int64
数据预处理
在这一步的分析中,我定义了训练和测试机器学习模型的特征以及预测“质量”的目标。然后我对这些特征进行了标准化(也称为 z 分数标准化),因为不同规模的特征可能会影响机器学习模型的性能。为此,我使用了 Scikit-learn 中定义的 StandardScaler()函数。最后,我将数据集分成 80%的训练集和 20%的测试集。
定义特征和目标
# Define features X
X = np.asarray(white_wines.iloc[:,:-1])# Define target y
y = np.asarray(white_wines[‘quality’])
标准化数据集
from sklearn import preprocessingX = preprocessing.StandardScaler().fit(X).transform(X)
训练和测试集分离
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)
print (‘Train set:’, X_train.shape, y_train.shape)
print (‘Test set:’, X_test.shape, y_test.shape)Train set: (3589, 11) (3589,)
Test set: (898, 11) (898,)
验证和模型选择
在这一部分中,我训练了几种分类算法,以找到最适合我所使用的数据集的算法。
k-最近邻
我从 K-最近邻分类算法开始。该算法所做的是获取一个数据点,并在训练数据中选择 K 个最接近该数据点的观察值,然后根据来自 K 个最近邻居的最受欢迎的响应值来预测该数据点的响应。
KNN精准剧情
我画了一个图,看看精度是如何随着 k 的数量而变化的。
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import cross_val_score# Number of k from 1 to 26
k_range = range(1, 26)k_scores = []# Calculate cross validation score for every k number from 1 to 26
for k in k_range:
knn = KNeighborsClassifier(n_neighbors=k)# It’s 10 fold cross validation with ‘accuracy’ scoring
scores = cross_val_score(knn, X, y, cv=10, scoring=’accuracy’)
k_scores.append(scores.mean())%matplotlib inline# Plot accuracy for every k number between 1 and 26
plt.plot(k_range, k_scores)
plt.xlabel('Value of K for KNN')
plt.ylabel('Cross-validated accuracy')
交叉验证为 KNN
我决定用 k=19,因为用它可以获得最高的精度。并用不同的验证方法对模型进行训练并计算精度。
# Train the model and predict for k=19knn = KNeighborsClassifier(n_neighbors=19)
knn.fit(X_train, y_train)
y_pred = knn.predict(X_test)from sklearn.metrics import accuracy_score
from sklearn.metrics import roc_auc_score# classification report for test set
print(metrics.classification_report(y_test, y_pred, digits=3, zero_division = 1))# Calculate cv score with 'accuracy' scoring and 10 folds
accuracy = cross_val_score(knn, X, y, scoring = 'accuracy',cv=10)
print('cross validation score',accuracy.mean())# Calculate cv score with 'roc_auc_ovr' scoring and 10 folds
accuracy = cross_val_score(knn, X, y, scoring = 'roc_auc_ovr',cv=10)
print('cross validation score with roc_auc',accuracy.mean())# Calculate roc_auc score with multiclass parameter
print('roc_auc_score',roc_auc_score(y_test,knn.predict_proba(X_test), multi_class='ovr'))
KNN 的分类表和验证分数
当我查看分类报告时,我立即发现训练时没有考虑类别 4 和类别 8,因为它们的回忆结果为零。这意味着,在所有第 4 类和第 8 类成员中,它没有正确预测任何一个。所以,对于我们的数据集来说,这不是一个好的模型。
逻辑回归
逻辑回归实际上是一种二元分类算法,可用于是/否、真/假等问题。
在这种情况下,它允许我们将它用于多类分类问题,如我们的问题。因为在我们的数据集中,有 5 类质量被预测为。为了将其用作多类分类算法,我使用了 multi_class= ‘多项式’,solver = '牛顿-cg '参数。
考虑到这是一个多类分类问题,我在计算交叉验证分数时使用了“roc_auc_ovr”评分参数,而不是“准确性”。我还用 multi_class='ovr '参数计算了 roc_auc_score。我将在后面的结论中解释这些。
# import module
from sklearn.linear_model import LogisticRegression# Train and fit model
logreg = LogisticRegression(multi_class=’multinomial’,solver =’newton-cg’)
logreg.fit(X_train, y_train)# Predict out-of-sample test set
y_pred = logreg.predict(X_test)# classification report
print(metrics.classification_report(y_test, y_pred, digits=3, zero_division = 1))print(‘accuracy’,accuracy_score(y_test, y_pred))# Calculate cv score with ‘roc_auc_ovr’ scoring and 10 folds
accuracy = cross_val_score(logreg, X, y, scoring = ‘roc_auc_ovr’,cv=10)
print(‘cross validation score with roc_auc’,accuracy.mean())# Calculate roc_auc score with multiclass parameter
print(‘roc_auc_score’,roc_auc_score(y_test,logreg.predict_proba(X_test), multi_class=’ovr’))
逻辑回归的分类表和验证分数
即使交叉验证分数稍微高一点,一些召回结果仍然是零。让我们看看如果我们添加一些多项式特征会发生什么。
向逻辑回归添加多项式特征
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline# Add polynomial features to the logistic regression modeldef PolynomialRegression(degree=2, **kwargs):
return make_pipeline(PolynomialFeatures(degree),
LogisticRegression(multi_class=’multinomial’,solver =’newton-cg’, **kwargs))
现在,我尝试向逻辑回归模型添加三次多项式特征。
# Train and fit the 3rd degree polynomial regression model
poly = PolynomialRegression(3)
poly.fit(X_train,y_train)# Test out-of-sample test set
y_pred = poly.predict(X_test)# Classification report
print(metrics.classification_report(y_test, y_pred, digits=3))# Calculate cv score with 'roc_auc_ovr' scoring and 10 folds
accuracy = cross_val_score(poly, X, y, scoring = 'roc_auc_ovr',cv=10)
print('cross validation score with roc_auc_ovr scoring',accuracy.mean())# Calculate roc_auc score with multiclass parameter
print('roc_auc_score',roc_auc_score(y_test,poly.predict_proba(X_test), multi_class='ovr'))
三次多项式回归的分类表和验证分数
最后,在预测时,我有一些少数类的代表,但它们非常低,交叉验证分数也比以前低。
决策图表
决策树是数据科学领域中最常用的分类算法之一。当我将其应用到我的数据集时,召回结果有所增加,但交叉验证分数下降了。
from sklearn.tree import DecisionTreeClassifier# Train and fit the Decision Tree Classification model
tree = DecisionTreeClassifier(random_state=0)
tree.fit(X_train, y_train)# Evaluate the model with out-of-sample test set
y_pred = tree.predict(X_test)# Classification report
print(metrics.classification_report(y_test, y_pred.round(), digits=3))# Calculate cv score with ‘roc_auc_ovr’ scoring and 10 folds
accuracy = cross_val_score(tree, X, y,scoring = ‘roc_auc_ovr’,cv=10)
print(‘cross validation score with roc_auc_ovr scoring’,accuracy.mean())# Calculate roc_auc score with multiclass parameter
print(‘roc_auc_score’,roc_auc_score(y_test,tree.predict_proba(X_test), multi_class=’ovr’))
决策树的分类表和验证分数
随机森林
随机森林是一种集成学习方法,它构建多个决策树,然后根据大多数决策树的预测结果进行预测。我喜欢把它想象成为了一个健康问题去看多个医生,然后根据大多数医生的意见决定你是否应该做手术。
所以让我们看看随机森林模型的结果。
from sklearn.ensemble import RandomForestClassifier# Train and fit the Random Forest Classification model
forest = RandomForestClassifier(n_estimators=100,random_state = 0)
forest.fit(X_train, y_train)# Test out-of-sample test set
y_pred = forest.predict(X_test)# Classification report
print(metrics.classification_report(y_test, y_pred.round(), digits=3))# Calculate cv score with 'roc_auc_ovr' scoring and 10 folds
accuracy = cross_val_score(forest, X, y,scoring = 'roc_auc_ovr',cv=10)
print('cross validation score with roc_auc_ovr scoring',accuracy.mean())# Calculate roc_auc score with multiclass parameter
print('roc_auc_score',roc_auc_score(y_test,forest.predict_proba(X_test), multi_class='ovr'))
决策树的分类表和验证分数
是目前为止最好的!roc_auc_score 非常好,交叉验证分数是目前为止最好的,甚至对于少数类也有一些召回结果。但这还不够。因此,可以提高召回率的一个方法是过度采样少数类。为此,我使用了随机森林算法和 SMOTE 算法实现。
添加 SMOTE 算法
SMOTE(合成少数过采样)算法创建合成少数类样本,以增加少数类的代表性。
# Import SMOTE module
from imblearn.over_sampling import SMOTE# Create model and fit the training set to create a new training set
sm = SMOTE(random_state = 2)
X_train_res, y_train_res = sm.fit_sample(X_train, y_train.ravel())# Create random forest model
forest = RandomForestClassifier(n_estimators=100,random_state = 0)# Fit the model to the new train set
forest.fit(X_train_res, y_train_res.ravel())# # Test out-of-sample test set
y_pred = forest.predict(X_test)# Classification report
print(metrics.classification_report(y_test, y_pred.round(), digits=3))# Calculate cv score with 'roc_auc_ovr' scoring and 10 folds
accuracy = cross_val_score(forest, X, y,scoring = 'roc_auc_ovr',cv=10)print('cross validation score with roc_auc_ovr scoring',accuracy.mean())# Calculate roc_auc score with multiclass parameter
print('roc_auc_score',roc_auc_score(y_test,forest.predict_proba(X_test), multi_class='ovr'))
SMOTE 随机森林的分类表和验证分数
即使准确率几乎与前一个相同,少数民族班级的召回结果也显著增加。
结论
在这个项目中,我使用了 K 近邻、多项式特征的逻辑回归、决策树和随机森林。利用 Scikit-learn 中的roc_auc_score
,我计算了每个模型的 AUC 分数。同样使用cross_val_score
方法,我通过传递roc_auc_ovr
参数使用交叉验证方法找到 AUC 分数。尽管它通常用于二进制分类中的性能评估,但通过一对多的方法,我将其应用于多类分类问题。当存在高等级不平衡时,用这种方法评估模型是有利的。也不需要设置分类阈值。
如果我们比较所有模型的交叉验证分数和召回结果,我们可以看到使用随机森林分类器和 SMOTE 方法得到的结果最好,交叉验证分数为 0.7565。由于我们的班级高度不平衡,SMOTE 创建了合成少数抽样来平衡样本。并训练模型,就好像我们的数据集中有平衡的类一样。
尽管我们得到了最高的交叉验证分数,大约为 0.75,但它仍然不是一个理想的模型,因为召回结果对于任何类别都没有足够的代表性。在这种情况下,为了改进模型,我们需要更多的数据来训练它。
感谢您的阅读!
如果您有任何反馈或更正,请随时通过我的 LinkedIn 联系我!
比较冠状病毒对各国经济和健康的影响
Covid 疫情的简单多元分析
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
几周前,国际货币基金组织发布了 2020 年 6 月的《世界经济展望》( T23 ),这份报告令人沮丧。该机构预测,今年全球国内生产总值(GDP)将下降 4.9%。我们应该关心吗?毕竟,人们一直批评 GDP 是衡量一个国家健康状况的糟糕指标。当人们生病或濒临死亡时,GDP 的下降真的重要吗?
幸运的是,我们并不局限于根据单一变量来分析全球健康状况,甚至经济学家也意识到了这一事实。他们现在有另一个度量到轨道。像我们一样,国际货币基金组织的工作人员可能每天早上都会打开仪表板,记录冠状病毒感染和死亡人数。正如 2007 年金融危机让每个人都成了业余经济学家一样,这场危机让我们都成了业余流行病学家。而且,就像一个国家的股市或 GDP 一样,冠状病毒感染率可以代表一个国家的实力和健康。
我们今天跟踪哪个指标?—由奥斯汀·迪斯特尔在 Unsplash 上拍摄
为了更好地了解世界是如何面对这种病毒的,我收集了各国的经济和卫生数据。一个主要的输入将是以每 100 万人感染人数衡量的病毒传播。另一个是一个国家的经济增长率,以 GDP 增长的百分比来衡量。很简单,让我们开始吧。
1 —收集数据
2020 年 4 月全球 GDP 增长预测—https://www . IMF . org/external/data mapper/NGDP _ RPCH @ WEO/OEMDC/ADVEC/WEO world/WEQ
年的经济数据来自国际货币基金组织。该组织于 4 月在 T2 发布了每个国家的增长预测,并于 6 月再次发布了大多数国家的增长预测。当然,这些只是投射,在平时很难做出。不能保证它们会实现,但这是我能找到的最好的了。
作为参考,我还抓取了 2019 年 10 月的经济预测,在 Covid 袭击之前。在那些平静的日子里,美国国内生产总值预计增长 2%,失业率为 3.7%,创历史新低。我将所有这些预测放入一个简单的表格中,如下图所示。
对于冠状病毒病例,数据更容易获得,有几十个好的来源。我选择了来自 worldometers 的漂亮的表格记录,因为它们是最新的,并且格式良好,如下所示。
2016 年 7 月的数据
2——增加一些有趣的变量
我知道我说过我只看 GDP,但是我忍不住为这个分析添加了一些其他的数据。最好留着它们,以防我们需要。
失业
IMF 也在今年 4 月公布了预期就业数据。它们甚至比 GDP 数据更加黯淡。利用国际货币基金组织的网站,我们可以导出 2020 年每个国家的失业率的表。
国内生产总值总额
每个国家的 GDP 总量也是由国际货币基金组织发布的,它有助于了解 GDP 增长/损失的大小。
全球区域数据
最后,我想我可能想要比较世界上不同的地区,所以我也从 statisticstimes.com 获取了地区数据
3-合并数据源
用多个数据源做任何有趣的事情都意味着将它们放入一个单一的数据框架。幸运的是,python Pandas 库允许轻松合并不同的数据集。在这种情况下,我只用 4 行代码就可以合并不同国家的表!
import pandas as pdimf_data = pd.read_csv(‘imf_merged.csv’, sep=’,’)
corona_data = pd.read_csv(‘corona.csv’, sep=’,’)
inner_merged_total = pd.merge(imf_data, corona_data, on=[“Country”])
inner_merged_total.to_csv( “merged_data.csv”)
就是这样。在清理了国家名称(例如“大韩民国”与“韩国”)后,我们可以合并任意多的数据源。
合并后,一切都在一个整洁的数据框架中,包含了分析所需的所有关键信息。数据帧的示例如下所示。
最终数据帧的一部分
4 —分析结果
我喜欢用 R 进行分析和探索,因为它是交互式的,并且允许数据操作而不需要太多代码。下面是一个简单的代码片段。我们用 python 生成的 csv 文件。很容易用任何变量来排序,比如国家人口
dt = read.csv(“all_data2.csv”, strip.white=TRUE)dt = dt[order(dt$Population, decreasing=TRUE),]
有了干净的数据,就很容易调查病毒的经济影响问题。下面是其中的一些问题。
富裕国家能更好地应对病毒吗?
看待国家财富和新冠肺炎之间关系的一种东方方式是将这两个变量对立起来。使用 R ggplot 包完成这项工作的代码如下所示。
dt$gdp_per_capita = as.numeric(as.character(dt$X2020.01.01.Gdp.US.billions))/dt$Population * 1000000000p = ggplot(dt, aes(y = Tot.Cases.per.1M.pop, x = gdp_per_capita)) + geom_point(aes(color = region, size=Total.Cases))
p = p + theme_classic()
p = p + theme(legend.position = "none")
p = p + labs(title="Infections vs GDP per Capita")
p = p + geom_text(aes(label=CountryChart),hjust=0, vjust=0,size=4.5)
p = p + theme(axis.text=element_text(size=14,face="bold"), axis.title=element_text(size=18))
p = p + xlab("GDP per Capita") + guides(fill=FALSE)
p = p + ylab("Total Cases per 1M People") + guides(fill=FALSE)
当绘制 Covid 病例(每 100 万人)与国家财富(人均 GDP)的对比图时,一个观察结果引人注目。看起来富裕国家并不比贫穷国家更好地应对危机。事实上,一些最富裕的国家,如卡塔尔、美国、瑞典和新加坡的感染率非常高。事实上,国家人均国内生产总值和人均感染之间的关系是正相关的。
correlation = cor(dt$Tot.Cases.per.1M.pop, dt$gdp_per_capita)
每百万人口超过 5000 例的国家人均 GDP 高达 36233.19 美元,远远高于贫困国家。计算代码如下。
high_cases = dt[dt$Tot.Cases.per.1M.pop > 5000,]
mean(high_cases$gdp_per_capita)
富裕国家测试的多吗?
富裕国家结果不佳的一个可能解释是更好的 Covid 测试。事实上,川普总统已经声称美国的大量病例完全是因为优越的检测制度。如果我们将总测试数(每 100 万人)与总案例数(每 100 万人)进行对比,我们会发现相关性并不完美。事实上,像美国这样的国家并没有比许多欧洲国家测试更多的人。
蓝线是一个简单的线性回归,它给了我们一个很好的指标,表明哪些国家测试得很好。贫困线以下的国家应该进行更多的检测,以跟上具有相似财富/感染率的国家。在 R 中,线性回归线很容易用单个语句添加。请参见下面的粗体行。
p = ggplot(dt, aes(x = Tot.Cases.per.1M.pop, y = Tests.per.1M.pop)) + geom_point(aes(color = region, size=gdp_per_capita))
p = p + theme_classic()
p = p + labs(title="Tests vs Cases")
p = p + theme(axis.text.x = element_text(angle = 90))
***p = p + geom_smooth(method='lm', formula= y~x, se = FALSE)*** p = p + geom_text(aes(label=CountryChart),hjust=0, vjust=0,size=2.5)
ggsave(filename = "tests_vs_cases.png" , plot=p, width=12, height=9)
哪些地区受灾最严重?
在绘制冠状病毒病例与 GDP 的对比图时,有一点很突出,那就是东亚国家的表现。在第一张图中,这些国家用挂在 y 轴下方的大绿点表示。也许更容易的方法是按地区做一个柱状图,以确切了解病毒是如何影响世界各地的。下面是条形图的 r 代码。
# Aggregate by region
regions = aggregate( Tot.Cases.per.1M.pop ~ region, dt, mean )# Create Factor for plotting
regions$region = factor(regions$region, levels = regions$region[order(regions$Tot.Cases.per.1M.pop, decreasing=FALSE)])p = ggplot(regions, aes(x=region, y=Tot.Cases.per.1M.pop, fill="#1dcaff")) + geom_bar(stat="identity")
p = p + ggtitle("Average Number of Coronavirus Cases per 1M Population")
p = p + theme(plot.title = element_text(hjust = 0.5))
p = p + theme(axis.text=element_text(size=13,face="bold"), axis.title=element_text(size=13), axis.title.y=element_blank())
p = p + theme(plot.title = element_text(size=14,face="bold"))
p = p + ylab("Cases per 1M Population") + guides(fill=FALSE)
p = p + coord_flip()
按地区绘制平均感染率图,我们确认东亚国家(中国、日本、韩国、台湾等)的平均感染率非常低。相比之下,西亚(或中东)国家受到的冲击最大。最后,南欧(西班牙、意大利等)、南美(巴西、智利等)和北美(美国)的人均 Covid 病例数最多。
Covid 病例与经济衰退相关吗?
好的,所以富裕国家通常比贫穷国家(东亚除外)有更多的感染者。一个简单的问题是,这场健康危机是否会在遭受重创的国家转化为经济危机。或者,全球经济是如此相互关联,以至于每个国家都面临衰退?将 2020 年各国的预计 GDP 与其冠状病毒病例(每百万人)进行对比,没有明显的相关性。许多病例较少的国家今年仍有望大幅萎缩。
上面的图使用 GDP 来确定点的大小。因此,我们可以看到 2020 年中国的病例很少,增长率为 1%。我们也看到美国有许多病例,预计经济将收缩 8%。
p = ggplot(dt, aes(x = Tot.Cases.per.1M.pop, y = X2020.GDP.Growth.Projected.06_2020)) + geom_point(aes(color = Country, size=X2020.01.01.Gdp.US.billions))
当然,以 2019 年 10 月份所做的经济预测作为参考,再检查一遍可能会更好。如果我们从冠状病毒袭击前的预测中减去 2020 年 6 月的预测,我们可以得到一个更好的指标,我称之为“GDP 损失”。
dt$june_gdp_loss = dt$X2020.GDP.Growth.Projected.06_2020 - dt$X2020.GDP.Growth.Projected.10_2019
这个图也好不到哪里去,但我们确实看到,像英国、美国和西班牙这样大规模爆发的国家现在受到了更大的打击。总的来说,不管目前的感染率如何,东亚以外的每个地区在 2020 年都将经历重大的经济影响。这可能是由于对未来疫情的预期,或者是因为今年全球需求(尤其是来自美国的需求)将非常低迷。
Covid 病例与失业相关吗?
从失业数据中可以更清楚地看出冠状病毒造成的经济损失。下面,我从 2020 年 4 月的测量数据中减去了 2020 年的预期失业数据(2019 年 10 月的数据)。在这方面,更明显的是,受冲击最严重的国家失业率上升幅度更大。
dt$uneployment_loss = dt$X2020.Unemployment.Projected.04_2020 - dt$X2020.Unemployment.Projected.10_2019
最后的想法
在我生活的美国,新冠肺炎危机令人遗憾地将许多人分成对立和激进的阵营。一些人认为必须不惜一切代价控制病毒,还有一些人认为我们应该考虑 T2 因长时间关闭造成的人员和经济损失。不幸的是,这些意见已经沿着政党路线分裂,并导致了阻碍美国政治的同样愚蠢的二分法和基本思想。
就我个人而言,我一直渴望一份从医学和经济两方面考虑一个国家健康状况的分析报告。尽管国际货币基金组织的经济预测并不完美,但它们可以开始表明受 Covid 影响的国家遭受了重大的经济损失。为了全世界的利益,抗击和控制这种病毒符合所有国家的利益。
比较数据版本控制工具— 2020 年
Alexander Schimmeck 在 Unsplash 上的照片
无论您使用逻辑回归还是神经网络,所有模型都需要数据来进行训练、测试和部署。管理和创建用于这些模型的数据集需要大量的时间和空间,并且由于多个用户更改和更新数据,可能会很快变得混乱。
这可能导致意想不到的结果,因为数据科学家继续发布新版本的模型,但针对不同的数据集进行测试。许多数据科学家可能在相同的几组训练数据上训练和开发模型。这可能会导致对数据集进行许多微妙的更改,一旦部署了模型,这可能会导致意想不到的结果。
这篇博客文章讨论了管理数据带来的许多挑战,并概述了用于机器学习和数据版本控制的顶级工具。
汇总表—请进一步阅读,了解更详细的比较。作者图片
数据管理的挑战
管理数据科学和机器学习模型的数据集和表格需要数据科学家和工程师投入大量时间。从管理存储、数据版本到访问,一切都需要大量的手动干预。
储存空间
训练数据会占用 Git 存储库的大量空间。这是因为开发 Git 是为了跟踪文本文件中的变化,而不是大型二进制文件。因此,如果一个团队的训练数据集涉及大型音频或视频文件,这可能会给下游带来很多问题。对训练数据集的每次更改通常会在存储库的历史中产生一个重复的数据集。这不仅创建了一个大的存储库,而且使得克隆和重置非常慢。
数据版本管理
当试图管理版本时,无论是代码还是 ui,都有一种普遍的趋势——甚至在技术人员中——通过在文件名的末尾添加版本号或单词来“管理版本”。在数据的上下文中,这意味着项目可能包括 data.csv、data_v1.csv、data_v2.csv、data_v3_finalversion.csv 等。这个坏习惯已经不是老生常谈了,事实上大多数开发人员、数据科学家和 UI 专家都是从坏的版本控制习惯开始的。
多用户
在生产环境中工作时,最大的挑战之一是与其他数据科学家打交道。如果您在协作环境中没有使用某种形式的版本控制,文件将被删除、更改和移动;你永远不会知道谁做了什么。此外,很难将您的数据恢复到原始状态。这是管理模型和数据集的最大障碍之一。
数据版本化是团队机器学习模型开发自动化的关键之一。虽然如果你的团队试图开发自己的系统来管理过程会非常复杂,但情况并非如此。
让我们探索六个伟大的开源工具,您的团队可以使用它们来简化数据管理和版本控制。
DVC
DVC ,或数据版本控制,是许多可用的开源工具之一,可以帮助简化您的数据科学和机器学习项目。该工具采用 Git 方法,因为它提供了一个简单的命令行,只需几个简单的步骤就可以设置好。顾名思义,DVC 不仅仅关注数据版本。它还帮助团队管理他们的管道和机器学习模型。最后,DVC 将帮助提高你的团队的一致性和你的模型的再现性。
赞成的意见
- 轻量级、开源,可用于所有主要的云平台和存储类型。
- 灵活,格式和框架无关,易于实现。
骗局
- DVC 版本控制与管道管理紧密结合。这意味着如果你的团队已经在使用另一个数据管道工具,就会有冗余。
- DVC 是轻量级的,这意味着您的团队可能需要手动开发额外的功能,使其易于使用。
三角洲湖
Delta Lake 是一个帮助改进数据湖的开源存储层。它通过提供 ACID 事务、数据版本化、元数据管理和管理数据版本来实现这一点。
该工具更接近于数据湖抽象层,填补了大多数数据湖有限的空白。
赞成的意见
- 提供了许多您当前的数据存储系统中可能没有的功能,例如 ACID 事务或有效的元数据管理。
- 减少了手动数据版本管理和处理其他数据问题的需要,使开发人员能够专注于在其数据湖之上构建产品。
骗局
- 对于大多数项目来说,Delta Lake 往往是多余的,因为它是基于 Spark 和大数据开发的。
- 需要使用专用的数据格式,这意味着它不太灵活,并且与您当前的格式无关。
- 工具的主要目的是更像一个数据抽象层,这可能不是你的团队所需要的,可能会让开发人员需要一个更轻便的解决方案。
吉特 LFS
Git LFS 是由许多开源贡献者开发的 Git 扩展。该软件旨在通过使用指针来消除可能添加到您的存储库中的大文件(例如,照片和数据集)。
指针较轻,指向 LFS 商店。因此,当您将回购放入主存储库时,更新时间不会太长,也不会占用太多空间。
对于管理数据来说,这是一个非常轻量级的选项。
赞成的意见
- 轻松集成到大多数公司的开发工作流程中。
- 利用与 Git 存储库相同的权限,因此不需要额外的权限管理。
骗局
- Git LFS 需要专门的服务器来存储你的数据。反过来,这最终会导致您的数据科学团队陷入困境,并增加工程工作量。
- Git LFS 服务器不像 DVC 那样可以扩展,它将数据存储到更通用的易于扩展的对象存储器中,比如 S3。
- 非常具体,可能需要在数据科学工作流的其他步骤中使用许多其他工具。
迟钝的人
Pachyderm 是该榜单上为数不多的数据科学平台之一。Pachyderm 的目标是创建一个平台,通过管理整个数据工作流,轻松再现机器学习模型的结果。在这方面,厚皮动物是“数据的码头工人”。
Pachyderm 利用 Docker 容器来打包您的执行环境。这使得重现相同的输出变得容易。版本化数据和 Docker 的结合使得数据科学家和 DevOps 团队可以轻松部署模型并确保它们的一致性。
Pachyderm 致力于其数据科学权利法案,该法案概述了该产品的主要目标:可再现性、数据来源、协作、增量、自治和基础设施抽象。
这些支柱驱动了它的许多特性,并允许团队充分利用这个工具。
赞成的意见
- 基于容器,这使得您的数据环境可移植,并易于迁移到不同的云提供商。
- 健壮,可以从相对较小的系统扩展到非常大的系统。
骗局
- 由于有如此多的移动部件,比如管理 Pachyderm 的免费版本所需的 Kubernetes 服务器,所以需要更多的学习曲线。
- 由于各种各样的技术组件,很难将 Pachyderm 集成到公司现有的基础架构中。
傻瓜
就数据版本化而言,Dolt 是一个独特的解决方案。与其他一些简单的版本数据不同,Dolt 是一个数据库。
Dolt 是一个具有 Git 风格版本的 SQL 数据库。与 Git 不同,在 Git 中,您可以对文件进行版本控制,而 Dolt 则是版本表。这意味着您可以更新和更改数据,而不必担心丢失更改。
虽然这款应用还是新的,但有计划在不久的将来让它 100%兼容 Git 和 MySQL。
赞成的意见
- 轻量级和部分开源。
- SQL 接口,与更模糊的选项相比,使数据分析师更容易访问它。
骗局
- 与其他数据库版本选项相比,Dolt 仍然是一个成熟的产品。
- Dolt 是一个数据库,这意味着您必须将您的数据迁移到 Dolt 中才能获得好处。
- 为版本控制表而构建。这意味着它不包括其他类型的数据(例如图像、自由格式的文本)。
莱克夫斯
LakeFS 让团队构建可重复的、原子的和版本化的数据湖操作。这是一个新来的人,但它有很强的冲击力。它提供了一个类似 Git 的分支和版本控制模型,旨在处理您的数据湖,扩展到数 Pb 的数据。
与 Delta Lake 类似,它为您的数据湖提供 ACID 合规性。然而,LakeFS 支持 AWS S3 和谷歌云存储作为后端,这意味着它不需要使用 Spark 来享受所有的好处。
赞成的意见
- 为易于使用的云存储(如 S3 和 GCS)提供高级功能,如 ACID 事务,同时与格式无关。
- 易于扩展,支持非常大的数据湖。能够为开发和生产环境提供版本控制。
骗局
- LakeFS 是一个相对较新的产品,因此与其他解决方案相比,其特性和文档可能会变化得更快。
- 侧重于数据版本化,这意味着您将需要为数据科学工作流的其他步骤使用许多其他工具。
你真的需要数据版本控制吗?
尽管数据版本化有诸多好处,但您并不总是需要在管理数据方面投入大量精力。例如,大部分数据版本控制都是为了帮助跟踪随时间变化很大的数据集。
有些数据,如网络流量,只是附加到。这意味着添加了数据,但很少更改。这意味着创建可重现结果所需的数据版本是开始和结束日期。注意这一点很重要,因为在这种情况下,您可能能够避免上面提到的所有工具的设置。您仍然需要管理开始和结束日期,以确保您每次都测试相同的数据,以及您正在创建的模型。然而,在这些情况下,您不一定需要将所有数据提交到您的版本控制系统。
摘要
管理数据版本是数据科学团队避免输出不一致的必要步骤。
无论您使用 Git-LFS、DVC,还是其他讨论过的工具,都需要某种数据版本控制。这些数据版本化工具有助于减少管理数据集所需的存储空间,同时还有助于跟踪不同团队成员所做的更改。如果没有数据版本控制工具,随叫随到的数据科学家可能会在凌晨 3 点起床调试由不一致的模型输出导致的模型问题。
然而,所有这些都可以通过确保您的数据科学团队实施数据版本管理流程来避免。
原载于 2020 年 10 月 31 日【https://dagshub.com】。
R 中留一交叉验证法比较因变量变换
通过转换因变量和利用交叉验证优化线性回归模型的预测能力。
在决定因变量变换以实现最佳预测能力时,一个关键问题是 R 平方值(通常用于测量不同模型的预测拟合度)不再具有可比性。由于 R 平方是因变量中方差的比例,该比例由自变量的变化来解释,通过对因变量进行变换,我们也变换了它们的方差结构。因此,我们不再能够通过比较它们的 R 平方值来洞察不同模型的相对预测能力。
Box-Cox 幂变换
虽然可以使用 Box-Cox 幂变换来指示哪种变换将有助于我们的模型拟合数据,但这种方法不会帮助我们衡量变换后的模型在预测方面的表现,也不会指示在倒带变换时使用平均值或中值预测值是否会产生最佳预测。以下示例通过采用留一交叉验证来计算可比较的预测能力度量,突出了过于依赖 Box-Cox 变换本身的问题。
留一交叉验证
交叉验证可作为一种手段,用于比较不同的因变量转换。交叉验证的留一法使用样本数据集中的一个观察值作为验证数据,使用其余的观察值作为训练数据。每个模型用于通过拟合训练数据来预测验证数据点的值,然后通过从验证数据观测值中减去预测值来计算预测误差。
为了确保我们获得可比较的测量值,必须将转换模型产生的拟合值转换回“水平”形式,以获得与原始观察数据单位相同的预测值。使用不同的方法进行这种转换能够比较平均值和中位数预测值。重复该过程,直到来自原始样本的每个观察值都被用作验证数据点,然后通过对预测误差的绝对值*求和,我们获得所讨论的转换模型进行样本外预测的程度的度量。
一旦为每个变换(和适用的预测器类型)计算了交叉验证度量,则产生最低度量的模型被认为具有最大的预测能力。
*人们可能希望使用预测误差的平方和来计算比较。
工作示例
借助所附的 mazda.csv 数据集,我们希望开发一个模型,在给定车龄的情况下,预测汽车的价格。根据初步观察,确定将年龄平方项作为模型中的第二个变量将有助于减轻非线性问题。然后产生以下诊断图。
型号: 价格=α+β年龄+δ年龄**
# LOAD & ORGANISE DATA #####################################
original_data <- rio::import("mazda.csv")
data <- original_data
names(data) <- c("Year", "y", "x")
# Inclusion of Age^2 variable
x_sq <- data$x^2
data <- tibble::add_column(data, x_sq, .after = 100)
# MODEL DEVELOPMENT ########################################
reg1 <- glm(y ~ x + x_sq,
data = data)
fitted.1 <- stats::fitted(reg1)
resid.1 <- stats::resid(reg1)
stu.resid.1 <- MASS::studres(reg1)
leverage.1 <- sur::leverage(reg1)
norm_lev.1 <- (nrow(data)*leverage.1)/(length(coef(reg1)))
# Diagnostic Plots
par(mfrow = c(2, 2))
plot(fitted.1, data$y, main = "Actual vs Fitted", ylab = "y", xlab = "Fitted")
plot(fitted.1, stu.resid.1, main = "Studentised Residuals vs Fitted", ylab = "Student Residuals", xlab = "Fitted")
plot(norm_lev.1, stu.resid.1, main = "Studentised Residuals vs Normalised Leverage", ylab = "Student Residuals", xlab = "Normalised Leverage")
abline(v=2, col="blue")
abline(h=3, col="red")
abline(h=-3, col="red")
qqnorm(stu.resid.1, , main = "Normal quantile plot of Studentised Residuals",
xlab = "Inverse Normal", ylab = "Studentised Residuals",
plot.it = TRUE, datax = FALSE,)
qqline(stu.resid.1 , col="red")
par(mfrow=c(1, 1))
尽管包含此项有助于将诊断图线性化,但我们仍有两个问题,即较大拟合值的方差增加(异方差)和非正态残差。我们知道,这两个问题都可以通过转换因变量来解决,并执行 Box-Cox 幂转换来查看哪种类型的因变量转换可能是合适的。
Box-Cox 幂变换
上面的 Box-Cox 幂变换图表明,通过对数变换来变换我们的价格变量是合适的(因为 0 位于λ, λ 的 95%置信区间内)。然而,我们知道平方根变换也有助于减轻异方差,并获得更接近正态分布的残差。因此,我们决定使用留一交叉验证来测试价格的对数转换和平方根转换(以及作为控制的初始线性模型),以确定哪个模型可能产生最佳的样本外拟合。
# BOX-COX TRANSFORMATION ###################################
box_cox <- MASS::boxcox(reg1, optimise = T, lambda = seq(-1,1, by=.1), main = "Box-Cox Power Transformation")
lamda_bc <- box_cox$x[which.max(box_cox$y)]
roundlamda_bc <- plyr::round_any(lamda_bc, .5) # We see the Box-Cox transformation suggests a Log-transformation is most appropriate
# However lamda is close enough to 0.5 to also consider a square-root transformation.
使用附带的 R 脚本,我们为三个模型执行 LOO 交叉验证。为了确保我们的交叉验证方法的可比性,我们必须将预测值转换回它们的“水平”值(即 ln (价格)对价格和 sqrt (价格)对价格),然后将它们与用作验证数据的原始观察值进行比较。这里我们可以选择均值或中值预测值。对于没有进行变换的模型,均值和中值预测值将是相同的,但是对于对数和平方根模型,它们是不同的。因此,当我们将拟合值转换回“水平”预测值时,我们也可以通过交叉验证来测试这些,以查看哪个预测值通过使用以下公式获得了最低的交叉验证测量值。随附的 R 脚本在计算可比预测功效指标时,在相应的交叉验证循环中执行这些转换。
日志转换:
- 平均预测值(对数)=预期值(拟合值)*预期值(1/2 *均方误差)
- 中位数预测值(对数)=预期值(拟合)
平方根变换:
- 平均预测值(sqrt) =(拟合)+ MSE
- 中位数预测值(sqrt) =(拟合)
*拟合的是我们的预测值,MSE 是模型的均方误差。
然后,我们可以比较计算出的测量值,看哪个模型和预测值在交叉验证方面表现最好。从下表中,我们可以看到,实际上平方根转换模型在交叉验证方面表现最佳,中值预测值获得了最低的 LOOCV 度量。
# LEAVE-ONE-OUT CV #########################################
dep.cv.glm <- function(x, data, mean.pred = TRUE, trans = "no"){
if(trans == "no"){
devn.1 <- list()
for(i in 1:nrow(data)) {
reg1.cv <- glm(formula(x),
data = dplyr::slice(data,-i))
fitted.cv1 <- predict(reg1.cv, newdata = data)
devn.1[i] <- ((data$y[i])-(fitted.cv1[i]))
}
devn.1 <- as.numeric(devn.1)
devn.1 <- abs(devn.1)
cv.lin <- mean(devn.1)
cv.lin
}
else if((mean.pred == TRUE) & (trans == "log")){
devn.1 <- list()
for(i in 1:nrow(data)) {
reg1l.cv <- glm(formula(x),
data = dplyr::slice(data,-i))
logfitted.cv1l <- predict(reg1l.cv, newdata = data)
mse <- summary(reg1l.cv)$dispersion
fitted.cv1l <- exp(logfitted.cv1l)*exp(0.5*mse)
devn.1[i] <- ((data$y[i])-(fitted.cv1l[i]))
}
devn.1 <- as.numeric(devn.1)
devn.1 <- abs(devn.1)
cv.log.mean <- mean(devn.1)
cv.log.mean
}
else if((mean.pred == FALSE) & (trans == "log")){
devn.1 <- list()
for(i in 1:nrow(data)) {
reg1l.cv <- glm(formula(x),
data = dplyr::slice(data,-i))
logfitted.cv1l <- predict(reg1l.cv, newdata = data)
mse <- summary(reg1l.cv)$dispersion
fitted.cv1l <- exp(logfitted.cv1l)
devn.1[i] <- ((data$y[i])-(fitted.cv1l[i]))
}
devn.1 <- as.numeric(devn.1)
devn.1 <- abs(devn.1)
cv.log.mean <- mean(devn.1)
cv.log.mean
}
else if((mean.pred == TRUE) & (trans == "sqrt")){
devn.1 <- list()
for(i in 1:nrow(data)) {
reg1s.cv <- glm(sqrt(y) ~ x + x_sq,
data = dplyr::slice(data,-i))
sqrtfitted.cv1s <- predict(reg1s.cv, newdata = data)
mse <- summary(reg1s.cv)$dispersion
fitted.cv1s <- ((sqrtfitted.cv1s)^2)+mse
devn.1[i] <- ((data$y[i])-(fitted.cv1s[i]))
}
devn.1 <- as.numeric(devn.1)
devn.1 <- abs(devn.1)
cv.sqrt.mean <- mean(devn.1)
cv.sqrt.mean
}
else if((mean.pred == FALSE) & (trans == "sqrt")){
devn.1 <- list()
for(i in 1:nrow(data)) {
reg1s.cv <- glm(sqrt(y) ~ x + x_sq,
data = dplyr::slice(data,-i))
sqrtfitted.cv1s <- predict(reg1s.cv, newdata = data)
mse <- summary(reg1s.cv)$dispersion
fitted.cv1s <- ((sqrtfitted.cv1s)^2)
devn.1[i] <- ((data$y[i])-(fitted.cv1s[i]))
}
devn.1 <- as.numeric(devn.1)
devn.1 <- abs(devn.1)
cv.sqrt.median <- mean(devn.1)
cv.sqrt.median
}
}# RESULTS ##################################################
MSE.LOO.cv <- list()
MSE.LOO.cv[1] <- dep.cv.glm(y ~ x + x_sq, data, mean.pred = TRUE, trans = "no")
MSE.LOO.cv[2] <- dep.cv.glm(log(y) ~ x + x_sq, data, mean.pred = FALSE, trans = "log")
MSE.LOO.cv[3] <- dep.cv.glm(log(y) ~ x + x_sq, data, mean.pred = TRUE, trans = "log")
MSE.LOO.cv[4] <- dep.cv.glm(sqrt(y) ~ x + x_sq, data, mean.pred = FALSE, trans = "sqrt")
MSE.LOO.cv[5] <- dep.cv.glm(sqrt(y) ~ x + x_sq, data, mean.pred = TRUE, trans = "sqrt")
names(MSE.LOO.cv) <- c("Linear", "Log-Median", "Log-Mean", "Sqrt-Median", "Sqrt-Mean")
MSE.LOO.cv
最后看一下首选模型的诊断图,我们可以看到平方根变换大大减轻了我们对异方差和非正态残差的担忧。因此,我们可以使用平方根转换模型和中值预测值来相对轻松地生成预测和预测区间。
型号: sqrt(价格)=α+β年龄+δ年龄**
# PREFERRED MODEL ##################################################
reg1 <- glm(sqrt(y) ~ x + x_sq,
data = data)
fitted.1 <- stats::fitted(reg1)
resid.1 <- stats::resid(reg1)
stu.resid.1 <- MASS::studres(reg1)
leverage.1 <- sur::leverage(reg1)
norm_lev.1 <- (nrow(data)*leverage.1)/(length(coef(reg1)))
# Diagnostic Plots (Preferred Model)
par(mfrow = c(2, 2))
plot(fitted.1, sqrt(data$y), main = "Actual vs Fitted", ylab = "sqrt(y)", xlab = "Fitted")
plot(fitted.1, stu.resid.1, main = "Studentised Residuals vs Fitted", ylab = "Student Residuals", xlab = "Fitted")
plot(norm_lev.1, stu.resid.1, main = "Studentised Residuals vs Normalised Leverage", ylab = "Student Residuals", xlab = "Normalised Leverage")
abline(v=2, col="blue")
abline(h=3, col="red")
abline(h=-3, col="red")
qqnorm(stu.resid.1, , main = "Normal quantile plot of Studentised Residuals",
xlab = "Inverse Normal", ylab = "Studentised Residuals",
plot.it = TRUE, datax = FALSE,)
qqline(stu.resid.1 , col="red")
par(mfrow=c(1, 1))
结论
本指南概述了在比较因变量的不同变换的模型时,比较 R 平方值无效的原因。该指南建议使用 Box-Cox 幂变换可以帮助确定因变量的合适变换,但是,Box-Cox 变换本身并不能确保我们的模型在进行样本外预测时表现最佳。本指南和随附的 R 脚本将使用户能够执行留一法交叉验证,以测试和比较因变量不同转换的模型的预测能力,并指出平均值或中位数预测值是否最合适。
感谢一路阅读到文章结尾!很想听听大家对以上的评论。欢迎随时留言,或者通过LinkedIn联系我。
附件 A —马自达. csv 数据文件
[## 马自达. csv
用于示例分析的数据文件](https://drive.google.com/file/d/1REMXRT8wvyR3pPgpbIjdSRr2GtwjGgsh/view?usp=drive_open&usp=embed_facebook)
附件 B — R 包装清单
# Packages #############################pacman::p_load(pacman, dplyr, MASS, plyr, rio, tidyverse)
比较 Grakn 和语义 Web 技术—第 1/3 部分
探索共同的概念和差异
观看本次网络研讨会,了解更多信息
本文探讨了 Grakn 与语义 Web 标准的比较,特别关注 RDF、XML、RDFS、OWL、SPARQL 和 SHACL。这两套技术之间有一些关键的相似之处——主要是因为它们都植根于符号人工智能、知识表示和自动推理领域。这些相似之处包括:
- 两者都允许开发人员表示和查询复杂和异构的数据集。
- 两者都提供了向复杂数据集添加语义的能力。
- 两者都使用户能够对大量数据进行自动演绎推理。
然而,这些技术之间存在核心差异,因为它们是为不同类型的应用程序设计的。具体来说,语义网是为 Web 而构建的,不完整的数据来自许多来源,在这里任何人都可以对信息源之间的定义和映射做出贡献。相比之下,Grakn 不是为了在网络上共享数据而构建的,而是作为封闭世界组织的交易数据库。正因为如此,比较这两种技术有时感觉就像比较苹果和橘子。
这些差异可以总结如下:
- 与语义网相比, Grakn 降低了复杂性,同时保持了高度的表达能力。有了 Grakn,我们不必学习不同的语义 Web 标准,每个标准都有很高的复杂性。这降低了进入的门槛。
- Grakn 为处理复杂数据提供了比语义网标准更高层次的抽象。使用 RDF,我们用三元组来建模世界,这是一个比 Grakn 的实体-关系概念级模式更低级的数据模型。对高阶关系和复杂数据的建模和查询是 Grakn 的原生功能。
- 语义网标准是为网络而建立的,Grakn 适用于封闭世界系统。前者旨在处理开放网络上不完整数据的链接数据,而 Grakn 的工作方式类似于封闭环境中的传统数据库管理系统。
该文档显示,这两种技术在如何提供知识表示和自动推理工具方面有很大的重叠,并且在高层次上涵盖了最重要的概念,而没有涉及太多的细节。目标是帮助具有 RDF/OWL 背景的用户熟悉 Grakn。
语义网栈
语义网开始于 20 世纪 90 年代末,用一层形式语义来扩展 Web 的现有架构。它由许多标准组成,这些标准共同构成了语义 Web 栈。本文涉及的技术包括:XML、RDF、RDFS、OWL、SPARQL 和 SHACL。
- RDF 是一种在 web 和 XML 上交换数据的标准。
- RDFS 提供了一个模式和一些基本的本体论结构。
- OWL 用描述逻辑的构造进一步增强了这一点。SPARQL 是查询和插入 RDF 数据的语言。
- SHACL 提供了一组验证约束来逻辑验证数据。
除了这些标准,还有不同的库和实现可供用户选择,以便在实践中真正使用这些标准。例如,有几个库允许用户使用 RDF 或 SHACL(仅对于 Java,您可以在这两个库中选择: TopBraid 和 Apache Jena ),但是它们都与标准略有不同,并且有各自的细微差别。
然而,尽管有很多可用的教育材料,语义网还没有在学术界之外被广泛采用。由于用户需要学习大量的技术,再加上它们固有的复杂性,用户在开始学习之前要花很长时间在语义网上自学。进入的门槛很高。这让大部分开发者很难入门。
相反,Grakn 只为用户提供了一种可以替代语义网中许多标准的技术(在本文中,我们将介绍 RDF、RDFS、OWL、SPARQL 和 SHACL)。这意味着,例如,构建应用程序的用户不需要学习使用什么类型的推理机、什么验证系统或什么查询语言。有了 Graql,所有这些都发生在同一技术中,用户只需要学习一次。
Grakn 工作在更高的层次,更容易学习,降低了进入门槛,使数百万开发者能够接触到以前无法接触到的语义技术。在 Grakn 中,易用性是首要原则。
我们用 Grakn 和 Graql 代替 RDF、SPARQL、RDFS、OWL 和 SHACL。
简而言之,Grakn 是一个知识图形式的分布式逻辑数据库,它实现了概念级的模式。该知识表示系统然后由自动推理引擎解释,该自动推理引擎在查询运行时执行自动演绎推理。查询、模式和推理都是通过 Grakn 的查询语言 Graql 进行的。
Grakn 的概念级模式的正式基础由超图提供,它与关系数据库的关系模型、语义网中的有向图以及图形数据库的属性图起着相同的作用。超图概括了边是什么的一般概念。在 RDF 和属性图中,边只是一对顶点。相反,超图是顶点的集合,它可以被进一步结构化。有向图模型的优势包括:
- 以关系方式将相关信息分组的自然机制,这在很大程度上迷失在有向图中。
- 统一处理所有 n 元关系,与有向图相反,在有向图中,与两个以上角色的关系需要对建模方法(n 元)进行彻底的改变。
- 一种表达高阶信息的自然方式**(关系之间的关系,信息的嵌套),在有向图中需要专门的建模技术(即具体化)。**
无线电测向
RDF 三元组
RDF 是一个对数据建模并在网络上分发数据的系统。它是由带标签的有向多重图构成的,有顶点和带标签的边。这些由 URIs(事物)、文字(数据值)和空白节点(虚拟节点)组成。
RDF 以主-谓-宾的形式存储三元组,即一个接一个声明的原子语句。下面是一个人“Peter Parker”的例子,他知道另一个人“Aunt May”的 XML 符号。
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
<foaf:Person>
<foaf:name>Peter Parker</foaf:name>
<foaf:knows>
<foaf:Person>
<foaf:name>Aunt May</foaf:name>
</foaf:Person>
</foaf:knows>
</foaf:Person>
</rdf:RDF>
正如我们所看到的,RDF 放弃了它在灵活性上获得的紧凑性。这意味着它可以是富于表现力的,同时又是极其精细的。数据点之间的每个关系都是显式声明的——关系要么存在,要么不存在。与传统的关系数据库相比,这使得合并不同来源的数据变得更加容易。上面的三元组构成了这个图:
RDF 三元组的图形表示。
Grakn 不支持三元组。相反,它公开了一个概念级的实体关系模型。因此,Grakn 不是以主-谓-对象的形式建模,而是在更高的层次上用实体、关系、角色和属性来表示我们的数据。对于上面的例子,我们会说有两个person
实体,它们具有name
属性,并且通过knows
关系相关联。
$p isa person, has name "Peter Parker";
$p2 isa person, has name "Aunt May";
($p, $p2) isa knows;
具有两个实体(“Peter Parker”和“May 阿姨”)和一个关系(“knows”)的 Grakn 模型。
超关系
如前所述,Grakn 的数据模型是基于超图的。在 RDF 中,边只是一对顶点,而超边是一组顶点。Grakn 的数据模型的正式基础基于三个前提:
- 超图由一组非空的顶点和一组超边组成
- 超边是顶点的有限集合(通过它们在超边中扮演的特定角色来区分)
- 超边本身也是一个顶点,并且可以由其他超边连接
注意:虽然 Grakn 利用了超边缘,但是 Grakn 实际上并没有暴露边缘或者超边缘。相反,它与关系或超关系一起工作。下图描述了这是如何工作的。该示例显示了两个超级关系:
- 婚姻,描述了分别扮演丈夫和妻子的鲍勃和爱丽丝之间的二元婚姻关系
- 离婚申请描述了一个三元离婚申请关系,涉及三个角色的角色扮演者认证婚姻,申请人和答辩人
Grakn 中的超关系示例。
超关系可以简单地看作任意基数的角色和角色扮演者对的集合。由于超关系不能在带标签的有向图中自然地表示,RDF 中的上述例子可能看起来像这样:
RDF 中超关系的表示。
如图所示,Grakn 中的每个超关系都可以映射到 RDF 模型中相应的有向图。例如,在这个模型中,实体和关系类型也被显式编码为 RDF 风格的 RDF 资源。因此,超关系可以在 RDF 三元组存储上实现。因此,就建模而言,超关系提供了一种非常自然和直接的数据表示形式,允许使用实体关系图在概念层次上建模。
然而,不同之处在于,在 Grakn 中,超关系成为一流的建模构造。这一点很重要,因为在现实生活中,当完整的概念模型在开始时不能完全预见时,实际的建模结果可能会产生许多不必要的复杂性。此外,与二进制有向边相比,本地模拟超关系导致查询规划和查询优化的改进,因为在相同结构“容器”中分组在一起的数据也经常由用户和应用程序在类似分组中检索。并且通过在查询之前确认它们的结构,可以更好地计划和执行检索过程。
名称空间
由于 Web 的公共性质,RDF 使用名称空间来解释和识别不同的本体,通常在文档的开头给出名称空间,以使其更具可读性。RDF 中的每个资源都有一个唯一的标识符。标签告诉它这是一个 RDF 文档:
rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#
由于 Grakn 不在网络上运行,因此不需要 URIs。一个相关的概念是keyspaces
,它们是 Grakn 实例中逻辑上独立的数据库。与 RDF namespace
不同的是,它们不能互相交谈。
连载
用文本形式表达 RDF 有很多种方法。一种常见的方法是用 XML 格式表示三元组(W3C 推荐):
<http://example.org/#spiderman>
<http://www.perceive.net/schemas/relationship/enemyOf>
<http://example.org/#green-goblin> .
<http://example.org/#green-goblin>
<http://www.perceive.net/schemas/relationship/enemyOf>
<http://example.org/#spiderman> .
然而,由于 XML 可能变得难以阅读, Turtle 也可以用作更紧凑的序列化(其他流行的序列化格式包括 JSON-LD 和 N-triples)。有了海龟,我们用 qnames 代替本地 URIs。下面的例子代表了来自foaf
名称空间的两个Person
:“绿魔”和“蜘蛛侠”。它们通过来自rel
名称空间的一个叫做enemyOf
的关系连接在一起。
@base <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rel: <http://www.perceive.net/schemas/relationship/> .<#green-goblin>
rel:enemyOf <#spiderman> ;
a foaf:Person ;
foaf:name "Green Goblin" .<#spiderman>
rel:enemyOf <#green-goblin> ;
a foaf:Person ;
foaf:name "Spiderman" .
在 Grakn 中,我们不需要在多个序列化之间进行选择,而是使用 Graql。这个例子可以这样表示:
$g isa person, has name "Green Goblin";
$s isa person, has name "Spiderman";
(enemy: $g, enemy: $s) isa enemyship;
这里,我们有两个person
实体,属性为name
“绿魔”和“蜘蛛侠”。它们通过类型enemyship
的关系连接在一起,两者都扮演enemy
的角色。
高阶关系
给定三元组的主语/谓语/宾语形式,RDF 中的建模在表示更高阶的关系时会变得有限。举个例子,让我们看看这个三元组:
lit:HarryPotter bio:author lit:JKRowling .
这表明 JK 罗琳写了《哈利·波特》。然而,我们可能想通过说 JK 罗琳在 2000 年写了《哈利波特》来修饰这个说法。在 RDF 中,要做到这一点,我们需要经历一个称为具体化的过程,为每个语句创建一个三元组,其中三元组的主题是同一个节点:
bio:n1 bio:author lit:JKRowling .
bio:n1 bio:title "Harry Potter" .
bio:n1 bio:publicationDate 2000 .
在 Grakn 中,考虑到它的概念级模式,具体化的需要并不存在,我们可以自然地表示更高阶的关系。 *JK 罗琳写的《哈利·波特》*会这样表述:
$a isa person, has name "JK Rowling";
$b isa book, has name "Harry Potter";
(author: $a, publication: $b) isa authorship;
然后,如果我们想限定这一点,说 JK 罗琳在 2000 年写了《哈利·波特》,我们可以简单地给关系添加一个属性:
$a isa person, has name "JK Rowling";
$b isa book, has name "Harry Potter";
(author: $a, publication: $b) isa authorship, has date 2000;
空白节点
有时在 RDF 中,我们不想给出 URI 或字面量。在这种情况下,我们处理的是空白节点,即没有 Web 标识的匿名资源。一个例子是这样一种说法:哈利·波特的灵感来自于一个住在英国的人
lit: HarryPotter bio:name lit:"Harry Potter" .
lit:HarryPotter lit:hasInspiration [a :Man;
bio:livesIn geo:England] .
由于 Grakn 并不存在于 web 上,所以空白节点的概念并不能直接转化为 Grakn。虽然在 RDF 中我们使用空白节点来表示没有 URI 的事物的存在,但是在 Grakn 中有多种方法可以做到这一点。如果像上面的例子一样,我们使用一个空白节点来表示我们除了知道这个人住在英国之外,对他的其他情况一无所知,我们就这样表示:
$b isa book, has name "Harry Potter";
$m isa man; ($b, $m) isa inspiration;
$l isa location, has name "England";
($m, $l) isa lives-in;
我们可以看到的是,变量$m
被分配给实体类型man
,除了他通过一个lives-in
关系类型连接到实体类型location
和name
“英国】之外,没有给出进一步的信息。
在第 2 部分( 链接此处 ),我们看看 Grakn 是如何比较 SPARQL 和 RDFS 的。要了解更多信息,请务必通过 此链接 参加我们即将举办的网络研讨会。
比较 Grakn 和语义 Web 技术—第 2/3 部分
探索共同的概念和差异
这是比较语义网技术和 Grakn 的第二部分。在 第一部分中, 我们看了 RDF 与 Grakn 的比较。在这一部分中,我们将特别关注 SPARQL 和 RDFS。如果你还没看完第 1 部分,跟着 这个链接 。
要了解更多信息,请务必通过 此链接 参加我们即将举办的网络研讨会。
SPARQL
什么是 SPARQL
SPARQL 是一种 W3C 标准化语言,用于从数据库中查询可以映射到 RDF 的信息。与 SQL 类似,SPARQL 允许插入和查询数据。与 SQL 不同,查询不仅限于一个数据库,还可以跨多个 HTTP 端点进行联合。
作为 Grakn 的查询语言,Graql 是等价的查询语言。和 SPARQL 一样,Graql 允许插入和查询数据。然而,鉴于 Graql 不是作为开放的 Web 语言构建的,它不允许跨多个端点进行本地查询(这可以通过 Grakn 的一个客户端驱动程序来实现)。因此,Graql 更类似于 SQL 和其他传统的数据库管理系统。
使用 SPARQL 插入数据
为了将数据添加到默认的图形存储中,这个代码片段描述了如何使用 SPARQL 插入两个 RDF 三元组:
PREFIX dc: <http://purl.org/dc/elements/1.1/>
INSERT DATA
{
<http://example/book1> dc:title "A new book" ;
dc:creator "A.N.Other" .
}
在 Graql 中,我们以insert
语句开始,声明要插入数据。变量$b
被分配给实体类型book
,它有一个值为“一本新书”的title
和一个值为“A.N.Other”的creator
。
insert
$b isa book, has title "A new book", has creator "A.N.Other";
使用 SPARQL 查询
在 SPARQL 中,我们首先声明我们想要从中检索数据的端点,我们可以将这些端点附加到某个前缀上。在陈述我们想要返回的数据之前,实际的查询从SELECT
开始。然后,在WHERE
子句中,我们说明了 SPARQL 将找到匹配数据的图形模式。在这个查询中,我们使用名称空间foaf
和vCard
查找“Adam Smith”认识的所有人:
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX vCard: <http://www.w3.org/2001/vcard-rdf/3.0#>SELECT ?whom
WHERE {
?person rdf:type foaf:Person .
?person vcard:family-name "Smith" .
?person vcard:given-name "Adam" .
?person foaf:knows ?whom .
}
在 Graql 中,我们以match
语句开始,声明我们想要检索数据。我们匹配类型为person
的实体,它有一个family-name
“史密斯”和一个given-name
“亚当”。然后,我们通过一个knows
关系类型将它连接到$p2
。因为我们想知道“亚当·斯密”认识谁,所以我们希望返回到$p2
,这在get
语句中声明:
match $p isa person, has family-name "Smith", has given-name "Adam";
($p, $p2) isa knows;
get $p2;
让我们来看一个不同的查询:给我詹姆斯·迪恩演过的导演和电影,其中也有一个女人演过角色,并且那个女人在约翰·福特导演的电影中演过角色。下面是 SPARQL 代码和这个遍历类型查询的可视化表示。
PREFIX movie: <http://example.com/moviedb/0.1/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?director ?movie
WHERE{
?actor rdf:type foaf:Man ;
movie:name "James Dean" ;
movie:playedIn ?movie .
?actress movie:playedIn ?movie ;
rdf:type foaf:Woman ;
movie:playedIn ?anotherMovie .
?JohnFord rdf:type foaf:Man ;
movie:name "John Ford" .
?anotherMovie movie:directedBy ?JohnFord .
}
Sparql 遍历查询的可视化表示。
在 Grakn,我们可以这样问同样的问题:
match
$p isa man, has name "James Dean";
$w isa woman;
(actor: $p, actress: $w, casted-movie: $m) isa casting;
(actress: $w, casted-movie: $m2) isa casting;
$d isa man, has name "John Ford";
($m2, $d) isa directorship; get $d, $m;
这里,我们将具有属性name
和值“James Dean”的实体类型man
分配给变量$p
。然后我们说$w
是实体类型woman
。这两个与movie
以称为casting
的三向关系连接。woman
也在另一个casting
关系中起作用,其中movie
实体通过directorship
关系连接到与此movie
相关的“John Ford”。
在上面的例子中,Grakn 中的超级关系casting
表示 SPARQL 中的两个playedIn
属性。然而,在 SPARQL 中我们只能有两条边将woman
和“詹姆斯·迪恩”与movie
连接起来,而不能在它们之间。这显示了 Grakn 中的建模与 RDF 中的建模有多么根本的不同,因为 RDF 具有超图建模的能力。Grakn 允许在一个关系中自然地表示 N 个角色扮演者,而不必具体化模型。
示意性地,这是上面查询的可视化表示方式(注意三元关系casting
):
Grakn 中查询的可视化表示。
否认
在 SPARQL 中,我们还可以使用关键字NOT EXISTS
在查询中指定某些数据不存在。这将找到一个只有在子图不匹配时才匹配的图模式。在下面的例子中,我们寻找曾出演电影《巨人》但尚未去世的演员:
PREFIX movie: <http://example.com/moviedb/0.1/>SELECT ?actor
WHERE {
?actor movie:playedIn movie:Giant .
NOT EXISTS {?actor movie:diedOn ?deathdate .
}
使用封闭世界假设,Grakn 支持否定。这是通过使用关键字not
后跟要求反的模式来完成的。上面的例子表示如下:
match
$m isa movie, has name "Giant"; ($a, $m) isa played-in;
not {$a has death-date $dd;}; get $a;
在这里,我们寻找一个名为“Giant”的实体类型movie
,它通过类型played-in
的关系连接到$a
和actor
。在not
子查询中,我们指定$a
不能有带有任何值的death-date
类型的属性。我们接着get
演员$a
。
RDF 模式
由于 RDF 只是一种数据交换模型,它本身是“无模式的”。这就是为什么引入 RDF Schema (RDFS)来用基本的本体论语义扩展 RDF。例如,这些允许 RDF 数据上的简单类型层次。在 Grakn 中,Graql 被用作它的模式语言。
RDFS 班级
RDFS 扩展了 RDF 词汇表,允许描述类和属性的分类法。RDFS 类将 RDFS 资源声明为其他资源的类。我们可以用rdfs:Class
来简化它。使用 XML,创建一个带有子类horse
的类animal
,如下所示:
<?xml version="1.0"?><rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xml:base="http://www.animals.fake/animals#"><rdfs:Class rdf:ID="animal" />
<rdfs:Class rdf:ID="horse">
<rdfs:subClassOf rdf:resource="#animal"/>
</rdfs:Class>
</rdf:RDF>
要在 Grakn 中做同样的事情,我们应该这样写:
define
animal sub entity;
horse sub animal;
RDFS 还允许Properties
的子类型:
<?xml version="1.0"?><rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xml:base="http://www.animals.fake/animals#"><rdfs:Class rdf:ID="mammal" /><rdfs:Class rdf:ID="human">
<rdfs:subClassOf rdf:resource="#mammal"/>
</rdfs:Class><rdfs:Property rdf:ID="employment" /><rdfs:Property rdf:ID="part-time-employment">
<rdfs:subPropertyOf rdf:resource="#employment"/>
</rdfs:Property></rdf:RDF>
在 Grakn 中是这样的:
mammal sub entity;
human sub mammal;
employment sub relation;
part-time-employment sub employment;
如示例所示,RDFS 主要描述了对象类型的构造(Classes
)、相互继承(subClasses
)、描述对象的属性(Properties
)以及相互继承(subProperty
)。这种子类型行为可以通过 Graql 的sub
关键字获得,该关键字可用于创建 Grakn 中任何thing
( entities
、relations
和attributes
)的类型层次结构。
然而,在 Grakn 中的class
到entity
或者在 Grakn 中的property
到relation
之间创建一对一的映射,尽管它们看起来很相似,但不应该总是这样做。这是因为 RDF 中的模型是使用较低级别的数据模型构建的,以三元组工作,而 Grakn 能够在较高级别建模。
多重遗传
Grakn 和语义网之间一个重要的建模差异是关于多重继承。在 RDFS,一个类可以有许多命名的或逻辑推断的超类。让我们举这个例子:
company rdf:type rdfs:Class
government rdf:type rdfs:Classemployer rdf:type rdfs:Class
employer rdfs:subClassOf company
employer rdfs:subClassOf government
这将一个employer
建模为类company
和government
。然而,尽管这看起来可能是正确的,问题是多重继承,作为一个建模概念,经常没有以正确的方式使用。多重继承应该对事物进行分组,而不是对“类型”进行子类化,其中每个类型都是其他事物的定义。换句话说,我们不想表示数据的实例。这是一个常见的错误。
Grakn 支持单一类型继承,而不是多重继承,在这种情况下,我们应该分配role
而不是多重类。一个role
在一个relation
的上下文中定义了一个thing
的行为和方面,我们可以将多个role
分配给一个thing
(注意,当类型继承另一个类型时,角色被继承)。
比如一个government
可以雇佣一个person
,一个company
可以雇佣一个person
。有人可能会建议创建一个继承了可以使用person
的government
和company
的类,并以子类化这两者的employer
类结束(如上例所示)。
然而,这是对继承的滥用。在这种情况下,我们应该创建一个角色employer
,它与一个employment
关系相关联,并将一个company
或government
如何参与到该关系中联系起来(通过扮演employer
的角色)。
company sub entity,
plays employer;government sub entity,
plays employer;employment sub relation,
relates employer;
rdfs:域和 rdfs:范围
rdf:property
的两个常用实例包括domain
和range
。这些用于分别声明属性的成员或值是一个或多个类的实例。下面是一个rdfs:domain
的例子:
:publishedOn rdfs:domain :PublishedBook
这里,rdfs:domain
将类Person
分配给hasBrother
属性的主题。
这是一个rdfs:range
的例子:
:hasBrother rdfs:range :Male
这里,rdfs:range
将类Male
分配给hasBrother
属性的对象。
在 Grakn 中,没有直接实现range
和domain
。从它们中得出的基本推断要么已经通过使用role
在 Grakn 数据模型中被本地表示,要么我们可以创建rule
来表示我们想要推断的逻辑。
但是,请记住,在 Grakn 中使用规则可以提供更多的表达能力,允许我们表示我们想要进行的推理类型。简而言之,将range
和domain
翻译成 Grakn 应该根据具体情况进行。
在上面的例子中,rdfs:domain
可以翻译成 Grakn,即当一个实体有一个属性类型published-date
时,它在一个publishing
关系类型中扮演published-book
的角色。这体现在 Grakn 规则中:
when {
$b has published-date $pd;
}, then {
(published-book: $b) is publishing;
};
可以使用以下 Grakn 规则创建rdfs:range
的示例,该规则添加值为“男性”的属性类型gender
,前提是在任何siblingship
关系中,a person
扮演角色brother
,其中其他兄弟的数量为 n
when {
$r (brother: $p) isa siblingship;
}, then {
$p has gender "male";
};
我们再来看另一个例子。在海事环境中,如果我们有一艘DepartingVessel
级的船只,它具有指定的属性nextDeparture
,我们可以声明:
ship:Vessel rdf:type rdfs:Class .
ship:DepartingVessel rdf:type rdfs:Class .
ship:nextDeparture rdf:type rdf:Property .
ship:QEII a ship:Vessel .
ship:QEII ship:nextDeparture "Mar 4, 2010" .
通过下面的rdfs:Domain
,任何指定了nextDeparture
的船只将被推断为DepartingVessel
类的成员。在这个例子中,这意味着 QEII 被分配了DepartingVessel
类。
ship:nextDeparture rdfs:domain ship:DepartingVessel .
为了在 Grakn 中做同样的事情,我们可以编写一个规则,找到所有具有属性next-departure
的实体,并将它们分配给一个扮演departing-vessel
角色的关系departure
。
when {
$s has next-departure $nd;
}, then {
(departing-vessel: $s) isa departure;
};
然后,如果该数据被摄取:
$s isa vessel, has name "QEII", has next-departure "Mar 4, 2010";
Grakn 推断 QEII 号船在departure
关系中扮演departing-vessel
的角色,相当于本案例中的nextDeparture
级。
在 web 环境中使用rdfs:domain
和rdfs:range
非常有用,在 web 环境中,联邦数据经常会被发现是不完整的。由于 Grakn 不存在于 web 上,所以对这些概念的需求减少了。此外,这些推断数据的大部分已经在 Grakn 的概念模型中进行了本地表示。这很大程度上是由于它的高层模型和规则的使用。因此,在 Grakn 中将rdfs:range
和rdfs:domain
直接映射到一个概念通常是幼稚的,会导致冗余。相反,将这些概念转换成 Grakn 应该使用规则和角色根据具体情况来完成。
在最后的第 3 部分( 链接此处 ),我们看看 Grakn 是如何比较 OWL 和 SHACL 的。要了解更多信息,请务必通过 此链接 参加我们即将举办的网络研讨会。
比较 Grakn 和语义 Web 技术—第 3/3 部分
探索共同的概念和差异
这是比较语义网技术和 Grakn 的第三部分。在前两部分中,我们研究了 RDF、RDFS 和 SPARQL 与 Grakn 的比较。在这一部分,我们特别关注 OWL 和 SHACL。如果你还没看完第 1 部分,跟着 这个链接 ,或者第 2 部分上 这个链接 。
要了解更多信息,请务必通过 此链接 参加我们即将举办的网络研讨会。
猫头鹰
猫头鹰和 Grakn
OWL 是一种基于描述逻辑(DL)的本体语言,它在 RDFS 的基础上增加了本体结构来表达条件和推导新的事实。为了有意义地使用它们,OWL 提供了不同的风格:OWL QL、OWL RL、OWL DL,用户需要决定哪一个最适合他们的用例。
另一方面,Grakn 自带内置的本地推理功能。这是一个重要的区别,因为 OWL 假设用户对逻辑领域有很好的理解,而使用 Grakn 不需要用户对这个领域有广泛的研究。
结果是 OWL 在表达性和复杂性之间努力保持令人满意的平衡。仅仅为了推理两个简单的约束:每个父母都有一个孩子和每个孩子都是一个人需要使用成熟的 OWL DL 推理器。此外,OWL 不适合复杂关系的推理。它的正式基础基于树模型属性,这使它更适合树形数据,但对于更复杂的数据伸缩性较差。
OWL 采用开放世界假设,而不是 Grakn 的封闭世界假设。这意味着在 OWL 有约束的例子中:每个父母必须至少有一个孩子,如果我们有一个人没有孩子,这仍然符合约束,因为我们可能还不知道 John 的孩子。然而,根据 Grakn 的封闭世界假设,如果没有实际提到约翰的孩子,这意味着他真的没有任何孩子,也不是父母。
开放世界的假设非常适合于开放式网络,它包含来自多个来源的不完全信息,这就是为什么 OWL 提供了许多概念来管理和处理这种不完全性。然而,由于这种开放世界的假设,OWL 很难验证数据的一致性。这就是关系数据库维护模式约束以保证数据质量的原因。Grakn 结合了两种类型的推理:本体类型的开放世界推理和模式类型的封闭世界约束检查。
所有的东西放在一起,OWL 对于非逻辑学家来说有着非常高的入门门槛。由于它基于描述逻辑,开发人员避免使用 OWL,因为理解这种语言及其预期行为非常重要。正因为如此,Grakn 的知识表示形式仍然是轻量级的,为比 OWL 更多的受众提供了语义能力。换句话说,Grakn 比 OWL 更容易使用。
在这里,我们看看 OWL 中的一些常见公理,并将它们与 Grakn 进行比较。这不是一个详尽的列表,提供它是为了帮助用户理解如何考虑迁移到 Grakn。
限制
OWL 的一个关键功能是定义限制类(owl:Restriction
)。这些未命名的类是基于该类的某些属性值的限制而定义的。OWL 允许对某个类的某些成员需要具有某些属性的情况进行建模。限制允许区分适用于一个类的所有成员的东西。通过给出一个描述来定义一个限制,该描述限制了关于该类成员的事情的种类。
一个例子是限制AllValuesFrom
,它声明在特定类的上下文中,属性的范围应该总是特定的类。例如,如果AllValuesFrom
已经被申请为Person
类,并且这被应用到具有Person
的hasParent
的范围,那么它们只能有Person
父类,而Animal
不能有Person
父类。下面的例子也表明只有Animal
s 可以有Animal
-parents。
:Person
a owl:Class ;
rdfs:subClassOf
[ a owl:Restriction ;
owl:onProperty :hasParent ;
owl:allValuesFrom :Person
] .:Animal
a owl:Class ;
rdfs:subClassOf
[ a owl:Restriction ;
owl:onProperty :hasParent ;
owl:allValuesFrom :Animal
] .
为了在 Grakn 中实现这一点,在模式定义中表示了限制。创建person
和animal
实体类型和两个带约束的关系:person-parentship
和animal-parenthship
。前者仅与person
相关,后者与animal
相关。
person sub entity,
plays person-child,
plays person-parent;animal sub entity,
plays animal-child,
plays animal-parent;parentship sub relation, abstract;person-parentship sub parentship,
relates person-child,
relates person-parent;animal-parentship sub parentship;
relates animal-child,
relates animal-parent;
传递属性
OWL 中一个常见的推论是传递性。这说明关系 R 被说成是传递的,如果 A 用 R(a,B)连接到 B,B 用 R(b,C)连接到 C,这暗示 A 用 R(a,C)连接到 C。构造owl:TransitiveProperty
用于此目的。下面的例子可以推断国王十字车站位于欧洲。
:isLocated rdf:type owl:TransitiveProperty.:KingsCross isLocated :London.
:London isLocated :UK.
:UK isLocated :Europe.
在 Grakn 中,将创建一个规则来表示传递性:
when {
$r1 (located: $a, locating: $b);
$r2 (located: $b, locating: $c);
}, then {
(located: $a, locating: $c);
};
一旦定义了规则,如果加载了此数据:
insert
$a isa city, has name "London";
$b isa country, has name "Uk";
$c isa continent, has name "Europe";
(located: $a, locating: $b); (located: $b, locating: $c);
如果我们接着查询:
match
$b isa continent, has name "Europe";
(located: $a, locating: $b); get $a;
这不仅会产生与continent
“欧洲”有直接关系的country
“英国”,而且还会产生与continent
“欧洲”有过渡关系的city
“伦敦”。
等效属性
OWL 还提供了一个构造来模拟等价的属性。这表明两个属性具有相同的属性扩展。一个例子:
:borrows owl:equivalentProperty :checkedOut .
这可以用 Grakn 中的规则来表示,其中可以推断出如下新关系checked-out
:
when {
(borrower: $x, borrowing: $y) isa borrowing;
},
then {
(checking-out: $x, checked-out: $y) isa checked-out;
};
对称属性
对称关系表示一种具有自身反向属性的关系。例如,如果 Susan 通过一个hasSibling
属性与 Bob 相关,那么可以推断 Bob 也通过一个hasSibling
属性与 Susan 相关。
:hasSibling rdf:type owl:SymmetricProperty .
在 Grakn 中,对称性可以通过简单地在一个或多个关系中重复角色来建模:
(sibling: $p, sibling: $p2) isa siblingship;
换句话说,在 OWL 中需要显式构造的东西是 Grakn 模型固有的,也就是说,对称性规则不需要显式。然而,这也是 OWL 和 Grakn 比较困难的地方,因为两者服务于不同的用例。特别是,鉴于 Grakn 的封闭世界假设,OWL 中的许多问题根本不存在。
功能属性
这就是我们如何建模更多 OWL 结构的方法。owl:FunctionalProperty
表示如下:
hasFather rdf:type owl:FunctionalProperty .
在 Grakn 中,可以使用一个规则:
when {
(father: $x, child: $ y) isa fatherhood;
(father: $d, child: $ y) isa fatherhood;
},
then {
(father: $x, father: $y) isa same-father;
};
的交叉
如果我们在这个例子中使用owl:intersectionOf
:
:Mother rdfs:subClassOf [ owl:interesctionOf ( :Female :Parent ) ]
如果资源既是:Female
又是Parent
,那么这将分配类:Mother
。在 Grakn 中,我们可以选择使用包含连接条件的规则来表示这一点:
when {
$p isa person, has gender "female";
(mother: $p) isa motherhood;
},
then {
(parent: $p) isa parenthood;
};
工会联合会
如果我们有这个owl:unionOf
的例子:
:Person owl:equivalentClass [ owl:unionOf (: Woman :Man ) ]
如果资源是类:Woman
或:Man
,则分配类:Person
。在 Grakn 中,如果我们想达到同样的目的,一种方法是使用类型继承:
person sub entity;
man sub person;
woman sub person;
散列值
owl:hasValue
限制可以规定红葡萄酒应该具有颜色“红色”作为其color
属性的值:
:RedWine
a owl:Class ;
rdfs:subClassOf
[ a owl:Restriction ;
owl:onProperty :color ;
owl:hasValue "red"
] .
在 Grakn 中,可以使用一个规则来表示这一点:
when {
$w isa red-wine;
},
then {
$w has color "red";
};
哈斯尔夫
限制可以说明自恋者爱自己。
:Narcissist rdfs:subClassOf
[ owl:hasSelf true ; owl:onProperty :loves ]
这可以用 Grakn 表示:
when {
$n isa narcissist;
},
then {
(loving: $n) isa loves;
};
如前所述,Grakn 不是为 OWL 的相同用例而构建的。因此,无法进行一对一的直接映射。例如,当处理多个类时,需要考虑建模决策:在 Grakn 中,哪些成为实体,哪些成为角色。
向 SHACL 核实
传统上,RDF 不能确保接收的数据符合一组条件——即模式。这就是 SHACL 标准的作用,它在提交之前检查 RDF 是否与模式逻辑一致。还有其他检查逻辑验证的方法,只是实现略有不同。
像 SHACL 一样,Grakn 也检查有效性,并强调与数据质量有关的问题。但是尽管 SHACL 只是一个验证模式,Grakn 却实现了一个语义模式。
使用验证模式时,如果被摄取的数据源包含模式冲突,事务将失败。然后,我们会查看来源,对其进行筛选,并再次摄取。使用语义模式,我们可以加载数据,验证数据,并标记数据是否违规。然后我们可以在它们被载入的时候在线处理它们。
从这个意义上说,语义模式为您提供了额外的保证,即所有获取的数据都符合您的模式。这意味着数据库中的所有数据都与已定义的模式一致。
SHACL 中的代码片段显示了数据需要如何遵守某些限制:
:Person a sh:NodeShape, rdfs:Class ;
sh:property [
sh:path schema:worksFor ;
sh:node :Company ;
] .:Company a sh:Shape ;
sh:property [
sh:path schema:name ;
sh:datatype xsd:string;
] .
在 Grakn 中,这种验证发生在 Graql 的模式语言中。Person
实体定义为在employment
关系中只扮演employee
的角色,通过employer
的角色与company
实体相关联,包括类型name
和值string
的属性。
define
person sub entity,
plays employee;
company sub entity,
has name,
plays employer;
employment sub relation,
relates employer,
relates employee;
name sub attribute, value string;
结论
总之,我们已经看到了:
- 与语义网相比,Grakn 降低了复杂性,同时保持了高度的表达能力。有了 Grakn,我们不必学习不同的语义 Web 标准,每个标准都有很高的复杂性。这降低了进入的门槛。
- 与语义网标准相比,Grakn 为处理复杂数据提供了更高层次的抽象。使用 RDF,我们用三元组来建模世界,这是一个比 Grakn 的实体-关系概念级模式更低级的数据模型。对高阶关系和复杂数据的建模和查询是 Grakn 的原生功能。
- 语义网标准是为网络构建的,Grakn 适用于封闭世界系统。前者旨在处理开放网络上不完整数据的链接数据,而 Grakn 的工作方式类似于封闭环境中的传统数据库管理系统。
Grakn 为我们提供了一种语言,这种语言为我们提供了概念级模型、类型系统、查询语言、推理引擎和模式验证。用语义 Web 标准做同样的事情需要多种标准和它们各自的实现,每种标准都有自己固有的复杂性。特别是,OWL 的特性非常丰富,这导致了高度的复杂性,使它不适合大多数软件应用程序。相反,在处理知识表示和自动推理时,Grakn 提供了复杂性和表达性之间的适当平衡。
这种比较旨在提供两种技术之间的高层次的相似性和差异,但是,当然,Grakn 和语义 Web 还有比我们在这里试图展示的更多的东西。
要了解更多信息,请务必通过 此链接 参加我们即将举办的网络研讨会。