如何与您的数据库对话
传统的方式
介绍
毫无疑问,最近自然语言处理(NLP)中使用转换器——神经网络的进步确实令人瞩目。这些模型可以很好地执行不同类型的任务,如翻译、文本摘要和自动完成。作为变形金刚的一个不同变体,视觉变形金刚是图像分类和物体检测中许多计算机视觉任务的当前技术水平。
照片由 Alina Grubnyak 在 Unsplash 上拍摄
为了训练这些模型,我们需要大量的数据、专业知识和计算能力。不幸的是,对于中小型企业和个人来说,这可能相当昂贵。
本文的目的是从人工智能的炒作中退一步,并提供一种替代方法来完成这个 NLP 任务。在接下来的章节中,我们将看到如何使用自然语言——简单的英语——与数据库“对话”,而无需训练任何机器学习算法。
程序
第一步是结合若干基于规则的自然语言处理技术对用户输入进行预处理,例如标记化、词条化和词性标注。
Tokenizer API 方法代码(图片由作者提供)
例如,提供输入 “显示 2017 年 1 月拉纳卡的 agros 销售额” 将返回如下所示的响应。也可以玩玩 API ,如果发现什么 bug 或者有什么建议就告诉我。
Tokenizer API 示例响应(图片由作者提供)
接下来,我们创建一个平面表来表示我们的数据库模式。原因是查询单个表比连接多个表更简单。
平面表模式(作者图片)
在上面的例子中,仅仅通过列名,我们就可以识别出至少 7 个不同的表合并成 1 个。
发生在数据库级别的另一部分是映射视图。这个视图帮助我们找出每个标记/单词的含义,还可以在 SQL 中得到它的等价列名。比如给定“Agros”这个词,我们想得出的结论其实是一个品牌。
表格映射视图的一部分(作者图片)
上图显示了该视图的一部分。尽管一开始看起来有些混乱和不直观,但是一旦我们理解了它在生成 SQL 中的作用,它就变得有意义了。对于尽可能多的令牌,我们查询视图并根据令牌(或二元模型)的值进行过滤。
映射视图的使用示例(图片由作者提供)
下图显示了高级别的标记化和映射过程。我们首先提供用户查询,然后生成二元模型和令牌。为了提高效率,我们进行二元模型查找,并从我们的令牌中删除这些单词。
标记化和 SQL 映射过程(图片由作者提供)
一旦我们完成了映射,我们就可以通过编程方式将所有内容组合在一起来构造 SQL。我们用一个“AND”语句连接每个查找,最后我们执行查询来获得结果。
生成的 SQL(图片由作者提供)
结论
即使这个系统不像基于机器学习的系统那样智能和不能一般化,它也提供了为这个特定任务定制的更便宜和更快的解决方案。
下次见,感谢阅读!
这篇文章解释了我本科论文项目的一部分,你可以在这个 GitHub repo 中找到一个演示和源代码。
资源
如何驯服你的强盗
揭开纯粹探索的神秘面纱(第一部分)
一个独臂强盗。(照片由马库斯·斯皮斯克在 Unsplash 上拍摄)
TLDR
多臂强盗很棒,超越了简单的 A/B 测试,走上了之路。对于有强盗的假设检验,有效的算法和非常紧的样本大小界限是可用的。让我们检查一下!😃
在前一集里…
P 我的关于的系列文章第 0 篇解释了为什么使用顺序统计可以显著地提高 A/B 测试的效率,以达到期望的性能水平(第 0 部分点击此处)。在其中,我还解释了一个叫做“多臂 bandit”的统计结构如何概括顺序 A/B 测试,并提供了一个使用 Bandit 方法的优势列表。
在过去的二十年里,班迪特背景下的序贯假设检验受到了研究者的广泛关注。计算机科学家和统计学家一样,已经开发了一个丰富的算法和证明技术的工具包来处理建立在异常严格的数学基础上的各种各样的此类问题。
在本系列的这一部分和随后的部分中,我希望呈现关于这个主题的大量文献中的一小部分,以帮助读者更好地欣赏顺序统计分析的强大功能。让我们首先了解我们正在处理的问题,特别是它不是什么。
纯探索≠后悔最小化!😮
多臂强盗(MAB)的设定包括 K 个动作,一些特别虐待狂的统计学家决定称之为’武器’,以唤起拉斯维加斯吃角子老虎机的可悲形象,又名独臂强盗,偷走你所有的钱。在继续下一步之前,我将快速总结一下 MAB 模型(新手可以参考这个网站上任何数量的关于后悔最小化的基础知识的文章)。
每个臂 K 代表一个分布 P(k),1≤ k≤ K,拉臂代表从分布 P(k)中取样。在时间 t = 1,2,3,mab 算法拉臂 Aₜ.在时间 s 拉动臂 k 产生从分布 P(k)中抽取的样本 X(s,k)。这个样本被称为“奖励”。
分布 Pₖ的均值用θₖ表示,均值最大的臂称为最佳臂。不用说,除了它们所属的概率分布的类之外,我们并不先验地知道任何报酬分布。贯穿本文,我们将假设所有的奖励分布都在 亚高斯 类**中。**称随机变量 z 是σ-次高斯的,如果对于每个λ ∈ ℝ
亚高斯的定义(作者图片)
上面的参数σ本质上表现为 z 的方差的代理。在本文的其余部分,我们将假设我们的问题有一个唯一的最佳臂,并且该臂是臂 1 。此外,让δₖ= θ₁-θₖ表示臂 1 和 k 之间的平均回报差距。为了便于表示,让我们设置δ₁= δ₂.
**后悔最小化:**在上面的框架中,给定固定的时间范围 T,MAB 算法的后悔本质上测量了由于它没有拉最佳臂的所有时间而导致的回报损失。
后悔最小化不是这个系列的重点。我感兴趣的是激起普通读者对 A/B 测试的激进概括的兴趣,这种测试被不同地称为“纯探索”和“最佳手臂识别”
纯探索
给定置信参数δ∈(0,1),找到概率至少为 1-δ的最佳臂,使用尽可能少的臂拉动。因此,我们的算法需要使用尽可能少的样本来找到 Arm 1。
杰瑞能通过你的测试吗?(照片由 Ricky Kharawala 在 Unsplash 上拍摄)
**例子:**一个医学研究者有一种药物的 K 种构型,她希望找出其中最有效的一种。
测试这一点的自然方法是通过注射不同配置的实验室小鼠,并观察每种配置的成功率。这样的实验并不关心老鼠本身如何受到药物的影响。事实上,所采用的协议必须相当积极地快速找出次优配置(或武器)(在她耗尽鼠标并不得不四处寻求更多资金之前)。
听起来可能很可怕,但这个例子有助于说明这两个问题之间的区别。后悔最小化算法,根据它们处理的问题的本质,必须“小心行事”,尽可能少地采样次优臂。纯粹的探索算法没有这种强迫性,只关心尽可能快地聚焦于最佳行动,而不关心在此期间他们会产生什么样的遗憾。
💡考虑到我们只有来自分布的样本,臂 k 的平均θₖ的一个很好的代理是从采样臂 k 到时间 t 所获得的平均回报。因此,我们应该研究如何控制θₖ和这个平均值之间的距离。幸运的是,两位著名的数学家发明了一种技术来做到这一点。
切尔诺夫和赫夫丁前来救援
在文章的剩余部分,给定随机变量 X₁,X₂,…由术语’ 样本意味着 ’ 我们将指随机变量
“样本均值”的定义(图片由作者提供)
当 X₁,X₂,…也是独立同分布(IID)且均值为μ时,我们知道𝔼Xᵐ(t)=𝔼X₁=μ.为了突出μ在某种程度上代表了 IID 序列的基本事实,我们将称其为’'(与样本均值相对)。
问题 1
Xᵐ(t 离μ有多远)?假设我们的 IID 序列 X₁,X₂,…由σ-亚高斯随机变量组成,标准切尔诺夫-赫夫丁分析告诉我们
单面装订(图片由作者提供)
Xᵐ(t)-μ的情况也是如此
(Image by author)
💡 This means that O(1/ϵ² log(1/δ)) samples are needed to bring the sample mean to within ϵ of the true mean with high probability. This brings us to the next question.
The sample mean is close to the true mean for large enough t. (Image by author)
O(1/ϵ² log(1/δ)) samples are needed to bring the sample mean to within ϵ of the true mean with high probability.
问题 2(回到单克隆抗体)
给定一个双臂土匪,假设已知δ=θ₁-θ₂,需要多少样本才能高概率找到更好的手臂?(回想一下,我们假设臂 1 是最佳臂。)
💡回答这个问题的一个方法是对两个臂采样足够的次数,以确保它们的样本均值 Xᵐ₁(t 和 Xᵐ₂(t 分别足够接近真实均值θ₁和θ₂,并且简单地选择具有较大样本均值的臂。
事实证明这非常有效!
臂 1 的样本均值比臂 2 的样本均值大得多(图片由作者提供)
根据上面的分析,假设我们现在处理的是 4 个区间,而不是 2 个,我们需要用δ/4 代替δ/2。还有,如图所示,在这种情况下,ϵ=δ/2。由此,我们看到
作者图片
样本确保在概率大于 1 - δ的情况下,臂 1 的样本均值 xᵐ₁(t)>θ₁-δ/2≥θ₂+δ/2 > xᵐ₂(t),臂 2 的样本均值。所以,选择样本均值较大的臂给出正确答案的概率较大。
*一个名为**动作消除或‘AE’*的非常简单的算法正好利用了这一原理,以高概率在多臂强盗中找到最佳臂。然而,AE 还必须应对增加的复杂性
- 事先不知道δᵢ=θ₁-θᵢ的差距,以及
- 不得不(潜在地)处理两个以上的臂。
让我们看看算法如何设法解决这个问题。
动作消除算法
AE,顾名思义,是一种“消除”类型的算法,它分阶段进行,丢弃或…消除(啊哈!)在阶段结束时看起来不太理想的武器。
该算法巧妙地回避了需要知道的差距
- 选择每个阶段结束时样本均值最大的臂作为当前*(或经验)最佳臂,*
- 丢弃其样本均值在经验最佳值一定距离之外的所有臂,该距离在阶段开始时选择,以及
- 通过选择一个新的、更小的距离来开始下一阶段,以使武器在即将到来的阶段中存活。这意味着,随着阶段的进展,样本均值将不得不越来越接近经验最佳值,对应的臂才能存活。
下面的流程图说明了算法的细节。AE 从所有臂开始,并在一个阶段结束时恰好剩下一个臂时结束。请注意,由于ϵₗ随着阶段的进展呈指数下降,样本均值需要非常接近经验最佳值才能生存。换句话说,该算法非常积极地丢弃 arms。
给定置信度δ∈(0,1)的动作消除算法。(图片由作者提供)
分析 AE 在停止前消耗的样本数包括显示(很有可能)
- 臂 1 永远不会被消除
- 武器的样本手段永远不会比ϵₗ在每个阶段都远离它们真正的对手 l 。
- 臂 k 最多存在 o(log(8/δₖ)阶段。
这些观察意味着动作消除返回概率至少为 1-δ的最佳臂,消耗了最多以下数量的样本
活动消除消耗的样本数。(图片由作者提供)
好消息和坏消息
我们将在后面看到,AE 样本量中的许多项实际上从根本上是不可避免的。然而,这种算法存在许多问题。
- 首先,不能保证动作消除会停止。样本均值估计可能会波动,Arm 1 可能会在早期被踢出局,一系列问题可能会破坏算法。
- 即使在 AE 停止的轨迹上,在概率 1-δ的集合之外,其性能也可能是任意差的。
- 在开始下一阶段之前,该算法实际上丢弃了在给定阶段收集的所有样本。
基于这些原因,我决定称动作消除法为‘无齿’算法(明白吗?😉无牙…被驯服的龙…)。无论如何,回到样本大小,我们现在会看到,尽管它没有什么作用,但 AE 样本复杂性的表达式是一个有价值的信息宝库。
无齿算法的教训
让我们更仔细地看看样本量表达式。可以说,我只强调了“顺序”项,而隐藏了不相关的常量。
红色圆圈中的术语不能删除,但绿色圆圈中的术语可以删除。(图片由作者提供)
用红色圈出的术语是统计分析和随机过程理论中非常基本的问题的结果。在本系列接下来的部分中,我们将看到它们是如何出现的。简而言之,
- 事实上,第 1 项(σₖ1/δₖ)是任何好的纯探索算法所消耗的样本数的一个著名下限的最重要部分。
- 第 2 项(log(K))是弱并集的结果。我们将看到这个术语最终是如何被删除的。
- 第 3 项(log(1/δ))是统计分析中的一个基本极限。事实上,这篇文章暗示了为什么这个术语不可避免。你能找到它吗?提示:如果我给你δₖ的差距值,你能避免吗?
- 第 4 项(log(log(1/Delta)))来自一个有趣的现象,称为重对数的定律,它控制着独立随机变量之和的增长率。
结束语
- 在纯探索的背景下,AE 是最早提出的算法之一(并经过严格分析)。
- AE 不是真的“无牙!”我只是用这个形容词来强调这样一个事实,即更先进的技术显示了接近最优的样本复杂性。事实上,AE 已经被许多研究者成功地用于解决 MABs 和强化学习中的几个问题(包括你的)😅),通常作为更复杂算法中的构建块。
- 多年来,AE 的许多问题都已经解决了。在接下来的部分中,我将描述一些成功解决 AE 问题的尝试。
因此,在纯探索传奇的这一部分,我们正式定义了这个问题,并看到了如何构造一个算法来解决它。算法的性能分析给了我们一些提示,告诉我们在这种情况下什么可以做,什么不可以做。
在本系列的剩余部分,我将解释是什么使得这些术语不可避免,以及 log(K)术语是如何被消除的。
***注:*参考资料将在第 2 部分末尾提供。
如何教计算机识别图像中的狗和蛋糕
从数据中提取隐藏的知识
做松饼还是不做松饼——这是个问题!图片作者。
人工智能是一个非常有趣的话题,引发了许多情感。这是因为新技术的发展涉及许多机会和威胁。
一些人工智能技术已经存在了很长时间,但计算能力和数值优化例程的进步,海量数据的可用性,导致了该领域的巨大突破。
人工智能被广泛用于在购物或简单地在网上搜索信息时提供个性化推荐。更先进的发明包括自动驾驶汽车——以一种简化的方式,根据从安装在车上的各种传感器收集的数据,对车辆的下一步行动做出决定。
在自动化车辆中使用人工智能。图片由来自 macaubusiness.com的 Nelson Moura 拍摄。
谈到威胁,你可能知道一些科幻电影,其中反叛机器获得了自我意识,并试图接管世界。正是因为这样的形象,围绕人工智能产生了许多神话和恐惧。让我们试着回答一个基本问题——人工智能是发展的威胁还是机遇?
有可能理解 AI 吗?
首先,我们应该说明这个神秘的人工智能(AI)实际上是什么。
正如形容词 artificial 通常不会给我们带来太多的问题,因为我们会将它与人类创造的一些非自然物体联系起来——一台机器,一个纯粹的工程生物——智能这个词会带来很多问题。为什么?因为它是一个抽象的概念,我们可以理解为解决问题或者处理各种情况的能力。为了更好地理解什么是智力,我将在这里提到它的测量方法。
最经典的智力测试(智商测试)是瑞文进步矩阵测试。我相信你们很多人都接触过它们,因为它们是用各种颜色的几何形状的矩阵进行的测试,缺少了最后一个元素。接受检查的人必须抓住(矩阵的)模式元素之间的关系,并从下面给出的例子中指出缺少的元素。这就是测试一般智力——识别模式的能力——的方法。人工智能是任何形式的非自然智能,由机器实现,或者更近一点来说,由人造程序实现——算法。同样重要的是,任务由设备本身执行,而不需要来自用户端的持续监督。
瑞文渐进矩阵测试中的智商测试项目。图片由维基共享资源提供。
几门学科的结合
我们可以将机器学习视为从数据中提取隐藏知识的技术。根据可用的数据类型和手头的研究问题,科学家将选择使用特定的学习模型来训练算法:
- 监督学习(与老师):当所有呈现给机器的数据都被注释,即被标记,以与我们期望机器给出的答案完全相同的方式出现;
- 无监督学习(无老师):发生在我们拥有大量无任何标签的数据,主机的任务是确定数据的结构;
- 强化学习(与评论家一起):通过试错法学习,机器寻求制定任务的解决方案,它的行动会得到奖励(当它做正确的事情时)或惩罚(当它做错时)——机器不会得到任何其他提示或建议。
人工智能领域。图片作者。
除了人工智能,还有几个更密切相关的领域值得了解,至少知道名字。在这里,我们可以详细列出机器学习、数据科学和深度学习等。可以说,机器学习是人工智能的一个子领域,而人工智能又是计算机科学的一个子领域。
简单来说,机器学习使人工智能领域的自适应解决方案得以使用。
我们可以将机器学习视为从数据中提取隐藏知识的技术。根据可用的数据类型和手头的研究问题,科学家将选择使用特定的学习模型来训练算法:
- 首先,这种机器的眼睛是如何构造的,
- 其次,数据,比如图像,是如何被机器处理的,以及它们的算法。
计算机视觉过程
现在,我们继续介绍介绍的要点——解释这种基于神经网络的计算机视觉实际上是如何工作的。这里我必须说明两件事:
我们来思考一下机器学习的过程。第一件事是构造算法本身,即定义一定的规则或模型,第二件事是定义参数,参数在过程中迭代改变其值。
在计算机这个现代计算机器中,我们想要重现某个过程,例如检测图像中的某些东西。当我们谈论一台计算机器时,我们实际上要解一个数学方程,这并不奇怪。对于简化的任务,想象我们有一系列数学运算,其中一些参数具有未知的真实值——这些是自适应参数(模型权重),它们将在机器学习过程中改变它们的值。权重乘以输入数据的总和称为输入的线性组合。这有点像收据——我们将商品数量乘以其单价,然后将结果相加,得到最终付款。这里,物品的未知价格是重量,购买物品的数量是输入数据,网络的答案应该是总价。
神经网络模型
这种单一的数学方程以及对其结果的适当反应(激活函数)可以被称为人工神经元。就像大脑中的神经元形成某些结构一样,人工神经元被组合成称为神经网络的复杂结构,我们可以在其中区分层。在下图中,它象征性地显示出来——每个神经元是一个圆形。开始时,在左侧,有一层神经元,称为输入层。在我们的计算机视觉任务中——对带有狗或松饼的图像进行分类——输入数据是组成分析图像的所有像素。在右侧,有一个给出预测的输出层—在我们的例子中是两个类。中间可能还有几个隐藏层。下一层中的神经元连接到前一层中的所有神经元。
简单的神经网络模型。图片由维基共享提供。
开始上课吧
有了这些知识,我们就可以进入实践部分了。在仓库中,你会找到 jupyter 笔记本,你可以用它来进一步学习。实用部分是用波兰语写的,但它是基于来自 SJSU ML 俱乐部英语初学者研讨会的材料。
作为 NAVOICA 项目的一部分,与弗罗茨瓦夫理工大学和 OPI 合作,在 2020/21 年冬季(波兰)为波兰学生进行了深度学习练习。查看我的视频教程(PL)。
原载于 2021 年 1 月 18 日https://majsylw . netlify . app。
https://github.com/majsylw/chihuahua-vs-muffin
如何用数据讲述一个令人信服的故事
数据新闻
将任何数据分析转化为难忘故事的 3 个步骤
T 这里有很多关于讲故事的炒作。就像数据一样。对我来说,讲故事只是“合理组织你的想法”的另一种说法。在任何数据分析的背景下,当涉及到通过数据传达信息时,给你的想法带来一个适当的结构似乎更加合理。
然而,如何最好地呈现您所进行的数据分析的输出并不总是不言而喻的。尤其是当涉及到技术数据操作时,分析可以进行几天、几周甚至几个月。如何最终将您的所有产出整合在一起,讲述一个引人注目的数据故事?
照片由 Rain Bennett 在 Unsplash 上拍摄
让我们来设定场景
你发现了有趣的数据来处理。你独自或与同事进行了出色的数据分析。也许你花了几天几夜“让数据说话”,找到了有见地的结果。但是你做到了:你最终发现了有趣的结果,并且希望你能找到你开始分析的问题的答案。等等,这个分析是关于什么的?
在这篇文章中,我想带您踏上我最近在公司进行的一次数据分析之旅。从我的老板最初提出要求,到我展示我的成果,我经历了混合数据分析和讲故事的几个步骤。这就是为什么我想与你分享一些关于如何从任何数据分析中讲述令人信服的数据故事的技巧。
简单地说,我的老板让我建立同质的、相同规模的员工团队,这样就可以在具有相似背景的群体中规划 Excel 培训。如果你对这个分析的细节更感兴趣,可以看看本文中的。在找到了一种基于可用数据来回答他的请求的方法之后(这将是“数据挑战”),我必须向他和其他利益相关者展示我的方法的结果:这就是我所说的“讲故事的挑战”。
玛丽·勒费夫尔
第一步:收集你的想法
如果你进行过一定的数据分析,你应该知道你最初为什么开始它。然而,这说起来容易做起来难:当你在数据处理和解释中前进时,有时你可能会忘记分析的主要目标。别担心,我以前去过那里。
因此在进行“你是谁”数据分析之前回到“为什么”是从开始的关键一步。问自己以下问题:
- 你想回答什么问题?
- 你想达到什么目标?
- 在这种情况下,您的数据有什么关系?
在我的例子中,我进行这个数据分析的原因是由上下文本身给出的。我的团队需要组成四个相同级别的员工小组,以便让每个人都能与具有相同技术背景的同事一起接受优秀的培训。我必须回答的问题是:如何最好地为这次培训组成四个小组?我必须达到的目标是形成具有相同数量个体的同质群体。为了做到这一点,我会使用我所掌握的数据:关于员工资历、Excel 熟练程度以及平均每天花在电脑上的时间的数据。
玛丽·勒费夫尔
第二步:确定你的受众
在讲述您的故事时,需要考虑的一个关键因素是您的数据故事面向的个人。《剑桥词典》对观众的定义之一是:“观看或收听演出、电影、公共活动等的人群。,要么一起在一个地方,要么分开”。我想在这里强调一下集团的概念。特别是对于数据故事,你的听众可能没有相同的背景来理解你的数据分析的技术元素。
既然你已经清楚地知道了为什么,让我们把注意力放在世卫组织上。为了确定你的故事的目标受众,你应该问自己以下问题:
- 什么定义了你的观众群体?
- 您的受众预先拥有什么类型的知识(如技术或行业相关知识)?
- 你的故事为他们提供了什么额外的价值?
回到我之前的例子,我面临的挑战是说服我团队的老板,我的方法是将员工分成四个相似组的最佳方式。这是在更大范围内应用这种方法的第一次测试,所以我的故事将首先提交给我的老板,但最终它可能会被提交给其他部门并应用。因此,我的听众由团队领导组成,他们对数据科学有着模糊的认识(而我在分析中使用了 k-means 聚类)和非常以业务为导向的思维。我的故事带来的额外价值是在未来建立个人团体时节省时间。
你的观众是谁?戴维·拉古萨在 Unsplash 上的照片
第三步:建立你的叙述
对于这一最终阶段,请确保您拥有数据故事内容所需的所有元素:
- 为什么:这个数据故事相关的根本原因及其回答的问题
- 世卫组织:这个数据故事面向的受众以及他们应该面对的方式
- 内容:数据分析的有形元素以数据可视化的形式呈现(表格、图表、图片……)
如果你接受的话,你的最终任务是将所有这些元素整合成一个一致且令人信服的数据故事。在这里,你想抓住你的观众的注意力,不仅仅是给他们一个有趣的故事,而是告诉他们一个真正让他们着迷的故事。为此,让我们转向文学或电影中的故事世界。一种常用的戏剧结构叫做弗雷塔格金字塔。在这种结构下,任何故事情节都由五部分组成:
- 展览会
- 上升动作
- 高潮
- 下落动作
- 灾难还是解决
你应该关注的是这个金字塔的第一、第三和最后几个元素,因为它们构成了你故事的核心结构。不要犹豫让你的听众参与到整个故事中来,比如问他们在故事的这个或那个时刻会做什么。
玛丽·勒费夫尔
尽管我的例子是一个非常短的数据故事(例如,与一个一小时的大规模数据分析项目的演示相比),我也应用了这种类型的结构。我是这样建造的:
- 在计划的 Excel 培训开始之前,必须建立参与者小组。挑战在于建立由相似个体组成的同等规模的团队。
- 我们第一次尝试将如此多样化的个体分成群体失败了:要么群体是相似的,但不是相同的大小,要么他们是相同的大小,但相对异质。当将这个问题扩展到整个公司时,这种随机分组方法将会失败。
- 我发现使用机器学习算法可以帮助我们建立个体集群。因此,我将 k-means 聚类算法应用于我们的问题,并将其参数化以适应我们的数据集。
- 我采取了以下步骤——在这里,我将更详细地介绍我的方法和假设。
- 这一旅程将我们带到了预期的结果:我们现在能够建立具有相似特征的相同规模的个体群体。Tadaaa,这是与他们对应的训练组的人员列表。
总结思想:获得信任
为了总结您正在尝试构建的这个“数据故事中的故事”,我想提高对您将集成到数据故事中的数据可视化片段的认识。讲述一个故事,尤其是当它依赖于技术分析时,不应该误导读者,让他相信你的故事,而不管你的论点是否正确。从这个意义上来说,特别注意你的故事元素的质量和真实性对于获得你的观众的信任是至关重要的:
- 与他们分享你的分析中可能包含的潜在偏见
- 用数字系统地支持你的陈述
- 问问自己关于数据可视化的三个“黄金问题”
要进一步了解数据可视化方面的内容,您可以查看这篇文章:
💔-questions-you-should-ask-yourself-when-creating-data-visualizations-fa255652508a>
快乐讲故事!
你喜欢读这篇文章吗? 成为 的一员,加入一个不断成长的充满好奇心的社区吧!
如何用情感分析讲故事
一名记者在分析 QAnon 时试图将数学引入新闻编辑室
上周,我发表了“《卡侬时间线:四年,5000 滴和无数失败的预言》。这项调查是与 Bellingcat 合作进行的,belling cat 是一家新闻编辑室,通过创新的开源和数据驱动的报道重塑了调查性新闻报道,最近在《NYT》、《华尔街日报》、《金融时报》、《华盛顿邮报》等上进行了专题报道。
我从开源社区的同事、数据科学家和记者那里收到了数量惊人的反馈。
到目前为止,最受欢迎的问题和兴趣点是方法论——我使用情绪分析来获得对 Qanon 增长故事的定量和定性见解。
今天关于数据科学,我将揭示我的方法。我还将深入解释如何在讲故事中应用这种创新的新方法,这有望成为任何对从数据中提炼有意义的故事感兴趣的人的宝贵财富。
开始使用数据🍕
QAnon 调查的核心是一个包含 4952 个所谓“Q 滴”的数据集,这些神秘的信息是阴谋论的核心。这些帖子是由一个被简称为“Q”的匿名人士发布的,他的追随者认为这个人是美国政治内幕消息的来源。每当一个 Q drop 出现,全世界的信徒都急切地试图解读它隐藏的含义,将他们与现实世界的事件联系起来。
Q 滴数据集是在图片板 8kun 上找到的,被 Q 关注者用作评论 Q 滴的位置。它包含的帖子可以追溯到 2017 年 10 月,当时 QAnon 理论是一种边缘在线爱好,并持续到 2020 年 10 月——到那时它们已经被太认真了。
方法学
本次调查的目标是阐明 QAnon 阴谋论随时间推移的主要发展和讨论。为此,我们将数据分成多个子集,每个子集有一到三个月的时间间隔。
对于每个子集,我们运行了一个 聚类算法 ,将具有相似情感的句子分组在一起。利用聚类的结果,我们总结了每个时间段的主要主题和值得注意的发展。
“情绪”是使用 通用句子编码器 进行评估的,这是一种学术上认可的文本分类模型,根据其含义将每个 Q 下降转换为一组数字——一个向量。
意义相近的 Q 滴,向量相似。两个向量的接近程度可以通过计算它们的点积来计算。因此,我们能够评估句子之间情感的“接近程度”,以便对每个 Q drop 的文本进行分类。
综上所述,这里有三个主要步骤。我们将逐个介绍它们,而上面的部分可以作为这些步骤如何组合在一起的高层次概述。
- 将数据分割成多个部分
- 情感分析
- 算法聚类
1)拆分数据🐼🐍
首先,我们希望在更短的时间间隔内将数据分割成多个子集,并执行任何必要的数据清理。这只是数据分析 101,所以我不打算进入太多的细节,但会推荐一些额外的资源,如果你有兴趣阅读更多!
我最喜欢的数据分析工具是 Python + Pandas dynamic duo。这里欢迎您使用任何编程语言,但是如果您第一次尝试数据分析,我强烈建议您使用这种技术。
🐍对于运行 Python 编程语言来说,py charm是我首选的开发环境,但是很多数据科学家也更喜欢使用 Jupyter 笔记本 。
🐼 Pandas 是一个广泛流行且超级强大的 Python 数据分析库。
如果你对介绍导入数据集和清理数据的熊猫教程感兴趣,这里有一个很好的资源,来自数据科学。另外,我推荐 Pandas 用于任何数据分析任务的另一个原因是因为它的强大的“分组”功能。
Pandas Groupby 函数允许我们获取一个数据帧(Pandas 中的一个数据集),并基于一个属性轻松地将其分割成子集。
在这个用例中,我们可以按月“分组”,按时间间隔划分数据集。按月分组的具体代码片段可以在这篇 stack overflow 文章中找到,在 Pandas 中遍历“分组”数据的惊人指南可以在这里找到。
2)情感分析
文字很难处理——我们更喜欢数字!(从来没有其他记者这么说过,但对于情感分析来说,这是非常正确的。)
理想的目标是将每个 Q 液滴转换成代表其含义的数字数组,这样我们的液滴数据集看起来更像这样:
那么…我们该怎么做呢?
答案是单词嵌入,这是一种对文本的学习表征,意思相似的单词有相似的表征。由于单词嵌入是自然语言处理中的一种技术,也是机器学习的一个子集,因此基本上有两种方法可以做到这一点:
a)从 Qanon 相关数据中训练我们自己的单词嵌入模型。
b)借用别人的文字嵌入模型,将文字转换成数字。
由于前者需要许多许多个月的工作,我们将采用后者。在这个例子中,我使用了谷歌发布的一个学术上备受好评的单词嵌入模型,该模型用包括政治文本在内的各种数据进行了训练,并针对句子和短段落进行了优化。这个模型的教程“通用句子编码器”可以在这里找到。
from absl import logging
import tensorflow as tf
import tensorflow_hub as hub
import numpy as nptf.compat.v1.disable_eager_execution()
embed = hub.Module("https://tfhub.dev/google/universal-sentence-encoder/1")
paragraph1 = (
"I am a sentence for which I would like to get its embedding."
"Universal Sentence Encoder embeddings also support short paragraphs. "
"There is no hard limit on how long the paragraph is. Roughly, the longer "
"the more 'diluted' the embedding will be.")
paragraph2 = "There are Puppets. There are Puppet Masters. Which is MUELLER?"
messages = [paragraph1, paragraph2]
with tf.Session() as session:
session.run([tf.global_variables_initializer(), tf.tables_initializer()])
message_embeddings = session.run(embed(messages))
for i, message_embedding in enumerate(np.array(message_embeddings).tolist()):
print("Message: {}".format(messages[i]))
print("Embedding size: {}".format(len(message_embedding)))
message_embedding_snippet = ", ".join(
(str(x) for x in message_embedding[:3]))
print("Embedding: [{},...]\n".format(message_embedding_snippet))
在上面的片段中,我们首先导入 TensorFlow ,这是一个由 Google 开发的流行的 Python 机器学习库。
接下来的代码部分全部来自通用语句编码器指南,在第 8 行,我们从互联网上下载了“嵌入”模块,它获取我们的输入文本,并将文本转换为一个向量,一个 512 个数字的列表。输出数据包含在“message_embeddings”变量中,我们可以用它来分析数据并将数据导出到 excel 表中。
3)将事物聚集在一起
在我们将相似的液滴聚集在一起之前,我们需要知道如何评估两个液滴是相似的。幸运的是,我们已经将 Q 值转换成了向量(基本上是数字数组)。回想一下高中数学,两个向量的“相似性”与其点积成正比,平行向量的点积等于 1。
在 Python 中,求两个向量的点积非常简单:
import numpy as npprint(np.dot(a, b))
升温[地图]
让我们来看一个实际例子吧!下面我们有来自 Q 滴的 10 句话。其中五项与罗伯特·穆勒有关,五项与脸书有关。
10 句话列表:
米勒:“有木偶。有傀儡师。哪个是[穆勒]?”,
米勒 2:“试图取代[JC]成为联邦调查局局长失败[试图重新获得联邦调查局的控制权]。”,
米勒 3:“【米勒】【爱泼斯坦埋葬&掩盖真相】”MUELLER 4:"[MUELLER][阴谋推翻正式当选的总统]。"米勒 5:“美国历史上最大的丑闻。叛国。”,
face book 1:“FB 是什么?”,
Facebook2:“间谍工具?”,
Facebook3:“谁创造的?”,
Facebook4:“到底是谁创造的?”,
Facebook5:“没有什么是看起来那样的。”
使用 Python 中的 Seaborn 库制作的热图,我们可以在从 0 到 1 的范围内可视化每对句子的点积。对角线都是暗红色的,因为每一滴都是相同的。请注意左上角大部分是橙色的,因为穆勒的句子彼此更相关,而脸书的句子除了 FB3(“谁创造了它”)和 FB4(“谁真正创造了它”)之外几乎没有关系,这两个句子非常相似,并且都是红色的。
最后一步,我们可以对 Q 个液滴运行聚类算法,将相似的液滴分类在一起。在这个例子中,我们使用了聚集聚类,尽管有许多其他的聚类算法。
这里有一个在 Python 中应用聚集聚类的教程。它能够根据相似性对向量/数字列表进行分组,同时考虑到我们快速查看向量时得到的数学知识。
from sklearn.cluster import AgglomerativeClustering
import pandas as pd
import os
CSV_Path = os.path.join('data.csv')
df = pd.read_csv(CSV_Path)
arr = df.iloc[:,1].to_numpy()
vector = df.iloc[:,3].to_numpy()
dates = df.iloc[:,0].to_numpy()
dates = dates.tolist()[0:200]
sentences = arr.tolist()[0:200]
vector = vector.tolist()[0:200]
for i in range (len(vector)):
vector[i] = vector[i][1:-1].split()
clustering = AgglomerativeClustering(n_clusters=3).fit(vector)
print(clustering.labels_)
我们简化并指定我们想要的聚类数,并在 Q 个点的子集上运行该算法。我通常喜欢根据子集中的数量下降来修改聚类的数量。这是该算法的一个局限性,另一个聚类算法如 KD 意味着可能能够更好地预测数据集的最佳聚类数。
最后,在数据集上执行算法后,我们可以通读每个聚类中最常见的趋势,然后写下它们,这是计算机永远无法自动化的调查的定性/新闻部分。
这是最终产品的链接,这是调查 Q 滴的方法论的结果:🍕 《卡农时间线》:四年,五千滴,无数次失败的预言 。
感谢阅读!我目前是 BBC 非洲之眼的开源调查员和自由数据记者。你也可以订阅我的简讯 括号 ,每周一次深入科技、数据和新闻的新兴交叉领域。如果您有任何问题或意见,请直接在 Twitter @上联系我@Edward _ the 6。
如何用几行 Python 测试多个机器学习管道
昆腾·德格拉夫在 Unsplash 上的照片
介绍
在项目的探索阶段,数据科学家试图为他的特定用例找到最佳的管道。因为几乎不可能预先知道哪种转换将对模型的结果最有益,所以这个过程通常包括尝试不同的方法。例如,如果我们正在处理一个不平衡的数据集,我们应该对少数类进行过采样还是对多数类进行欠采样?在这个故事中,我将解释如何使用 ATOM 包来快速帮助您评估在不同管道上训练的模型的性能。ATOM 是一个开源的 Python 包,旨在帮助数据科学家加快机器学习管道的探索。如果你想对图书馆有一个温和的介绍,请阅读这个故事。
管理管道
解释如何管理多个管道的最简单的方法是通过一个例子。在本例中,我们将:
- 创建一个不平衡的数据集,并将其提供给 atom
2.使用递归特征消除减少特征数量(RFE)
3.训练三个随机森林(射频)模型:
4.比较结果
我们将用不到 20 行代码完成所有这些工作!让我们开始吧。
创建数据集
我们开始创建一个模拟二进制分类数据集,将 0.95–0.05 比例的样本分配给每个类。然后,数据被输入到 atom。
from atom import ATOMClassifier
from sklearn.datasets import make_classification# Create an imbalanced dataset
X, y = make_classification(
n_samples=5000,
n_features=30,
n_informative=20,
weights=(0.95,),
)# Load the dataset into atom
atom = ATOMClassifier(X, y, test_size=0.2, verbose=2)
数据集被自动分为训练集和测试集。输出如下所示。
<< ================== ATOM ================== >>
Algorithm task: binary classification.
Dataset stats ====================== >>
Shape: (5000, 31)
Scaled: False
Outlier values: 582 (0.5%)
---------------------------------------
Train set size: 4000
Test set size: 1000
---------------------------------------
| | dataset | train | test |
|---:|:------------|:------------|:-----------|
| 0 | 4731 (17.6) | 3777 (16.9) | 954 (20.7) |
| 1 | 269 (1.0) | 223 (1.0) | 46 (1.0) |
我们可以立即看到数据集是不平衡的,因为它包含的 0 比 1 多 18 倍。我们来看一下数据。注意,由于输入不是 dataframe,atom 为列指定了默认名称。
atom.dataset.head()
执行特征选择
出于解释的目的,我们将从一个数据转换步骤开始,我们希望在我们将要测试的所有管道中共享这个步骤。通常,这类似于特征缩放或缺失值的插补。在这种情况下,我们将数据的维度从 30 个特征减少到 12 个特征。有了 atom,就像这样简单。
atom.feature_selection("RFE", solver="RF", n_features=12)
此命令使用随机森林作为估计器来运行 RFE。剩余的数据集包含最有希望的要素。
Fitting FeatureSelector...
Performing feature selection...
--> The RFE selected 12 features from the dataset.
>>> Dropping feature Feature 2 (rank 3).
>>> Dropping feature Feature 3 (rank 8).
>>> Dropping feature Feature 5 (rank 10).
>>> Dropping feature Feature 7 (rank 17).
>>> Dropping feature Feature 8 (rank 12).
>>> Dropping feature Feature 11 (rank 19).
>>> Dropping feature Feature 13 (rank 13).
>>> Dropping feature Feature 14 (rank 11).
>>> Dropping feature Feature 15 (rank 15).
>>> Dropping feature Feature 17 (rank 4).
>>> Dropping feature Feature 19 (rank 16).
>>> Dropping feature Feature 20 (rank 2).
>>> Dropping feature Feature 21 (rank 6).
>>> Dropping feature Feature 23 (rank 5).
>>> Dropping feature Feature 24 (rank 9).
>>> Dropping feature Feature 25 (rank 18).
>>> Dropping feature Feature 26 (rank 7).
>>> Dropping feature Feature 27 (rank 14).
现在,我们直接在不平衡数据集上训练我们的第一个模型。使用run
方法,我们在训练集上拟合一个随机森林,并在测试集上对其进行评估。
atom.run(models="RF", metric="balanced_accuracy")
Training ===================================== >>
Models: RF
Metric: balanced_accuracy
Results for Random Forest:
Fit ---------------------------------------------
Train evaluation --> balanced_accuracy: 1.0
Test evaluation --> balanced_accuracy: 0.5326
Time elapsed: 0.733s
-------------------------------------------------
Total time: 0.733s
Final results ========================= >>
Duration: 0.733s
------------------------------------------
Random Forest --> balanced_accuracy: 0.5326
分支系统
在继续之前,是时候解释一下 ATOM 的分支系统了。分支系统允许您在同一个 atom 实例中管理多个管道。每个管道都存储在一个单独的分支中,可以通过branch
属性进行访问。分支包含数据集的副本,以及适合该特定数据集的所有转换器和模型。从 atom 调用的方法总是使用当前分支中的数据集,以及atom.dataset
等数据属性。默认情况下,atom 从一个名为master
的分支开始。致电分支机构,了解其包含的变压器和型号的概况。
atom.branch
Branch: master
--> Pipeline:
>>> FeatureSelector
--> strategy: RFE
--> solver: RandomForestClassifier(n_jobs=1, random_state=1)
--> n_features: 12
--> max_frac_repeated: 1.0
--> max_correlation: 1.0
--> kwargs: {}
--> Models: RF
当前分支包含我们之前调用的用于特征选择的类,以及我们刚刚训练的模型。
过采样
现在是时候测试模型在对数据集进行过采样后的表现了。在这里,我们创建一个名为oversample
的新分支。
atom.branch = "oversample"
New branch oversample successfully created!
**注意:**创建新分支会自动将当前分支更改为新分支。要在现有分支之间切换,只需输入所需分支的名称,例如atom.branch = "master"
即可返回主分支。
过采样分支从当前分支(主分支)中分离出来,采用其数据集和转换器。这意味着特性选择转换器现在也是过采样流水线中的一个步骤。像这样分割分支可以避免重新计算之前的转换。
使用 SMOTE 调用balance
方法对数据集进行过采样。
atom.balance(strategy="smote")
Oversampling with SMOTE...
--> Adding 7102 samples to class: 1.
请记住,该方法仅转换当前分支中的数据集。主分支中的数据集保持不变。快速检查转换是否有效。
atom.classes
请注意,只有训练集是平衡的,因为我们希望在测试集中保留原始的类分布。
现在,我们可以在过采样数据集上训练一个随机森林模型。为了将这个模型与我们训练的第一个模型区分开来,我们在模型的缩写后面添加了一个标记(os 表示过采样)。
atom.run(models="RF_os", metric="balanced_accuracy")
Training ===================================== >>
Models: RF_os
Metric: balanced_accuracy
Results for Random Forest:
Fit ---------------------------------------------
Train evaluation --> balanced_accuracy: 1.0
Test evaluation --> balanced_accuracy: 0.7737
Time elapsed: 1.325s
-------------------------------------------------
Total time: 1.325s
Final results ========================= >>
Duration: 1.341s
------------------------------------------
Random Forest --> balanced_accuracy: 0.7737
欠采样
需要一个新的分支来对数据进行欠采样。由于当前分支包含过采样数据集,我们必须将新分支从仅包含 RFE 变换器的主分支中分离出来。
atom.branch = "undersample_from_master"
New branch undersample successfully created!
在新分支和现有分支之间添加_from_
,将其从现有分支而不是当前分支中分离出来。检查欠采样分支中的数据集是否仍然不平衡。
atom.classes
再次调用balance
方法,使用 NearMiss 对数据进行欠采样。
atom.balance(strategy="NearMiss")
Undersampling with NearMiss...
--> Removing 7102 samples from class: 0.
并使用新标签(us 表示欠采样)拟合随机森林。
atom.run(models="RF_us", metric="balanced_accuracy")
Training ===================================== >>
Models: RF_us
Metric: balanced_accuracy
Results for Random Forest:
Fit ---------------------------------------------
Train evaluation --> balanced_accuracy: 1.0
Test evaluation --> balanced_accuracy: 0.6888
Time elapsed: 0.189s
-------------------------------------------------
Total time: 0.189s
Final results ========================= >>
Duration: 0.189s
------------------------------------------
Random Forest --> balanced_accuracy: 0.6888
如果我们现在查看我们的分支,我们会看到管道只包含我们想要的两个转换。
atom.branch
Branch: undersample
--> Pipeline:
>>> FeatureSelector
--> strategy: RFE
--> solver: RandomForestClassifier(n_jobs=1, random_state=1)
--> n_features: 12
--> max_frac_repeated: 1.0
--> max_correlation: 1.0
--> kwargs: {}
>>> Balancer
--> strategy: NearMiss
--> kwargs: {}
--> Models: RF_us
分析结果
我们最终在 atom 实例中获得了我们想要的三个模型。分支系统现在看起来如下。
RFE 变换在三个分支之间共享,但是之后,每个分支遵循不同的路径。主分支没有其他变压器,而其他两个分支各自应用不同的平衡算法。所有三个分支都包含一个随机森林模型,每个都在不同的数据集上进行训练。剩下的就是比较结果了。
atom.evaluate()
atom.plot_prc()
结论
我们已经了解了如何使用 ATOM 包轻松比较多个机器学习管道。将所有管道(以及模型)放在同一个 atom 实例中有几个优点:
- 代码更短,这使得笔记本不那么杂乱,并且更容易维护概览
- 跨管道共享的转换不需要重新计算
- 使用 atom 的绘图方法可以更容易地比较结果
我们所经历的例子非常简单,但是 ATOM 可以做得更多!要了解更多信息,请查看这个相关的故事或者看看软件包的文档。对于 bug 或功能需求,请不要犹豫,在 GitHub 上发帖或给我发邮件。
如何用 Julia 测试你的软件
确保你的 Julia 软件实际上与测试包一起工作!
(src =https://pixabay.com/images/id-731198/
介绍
测试是迭代软件开发最重要的方面之一。这是因为测试可以自动揭示软件的问题,而不需要实际使用软件。不用说,将甚至不工作的软件推送到存储库可能是个坏主意,测试可以避免碰到这个问题。在 Julia 编程语言中实现测试,我认为这是一个非常棒和独特的实现。我认为它很棒,因为它提供了一种使用条件和宏测试软件的非常结构化和简单的方法。今天我想为 Julia 介绍一下 Testing.jl 包的基础知识,这样你就可以在犯下大错之前确保你的软件正常工作。
设置包
为了演示 Julia 的测试包,我们首先需要建立一个新项目。为了建立一个新的项目,我们可以使用 Pkg.generate(),或者 Pkg REPL 中的 generate 命令。一旦进入朱莉娅 press,我将按下]并生成我们命名为“Sample”的新项目
julia> ]
([@v1](http://twitter.com/v1).6) pkg> generate sample
接下来,我将退格退出 REPL,然后按;参加 REPL 的狂欢。然后,我们将 cd 放入新的项目文件夹。
julia> ;
shell> cd sample
现在,我将 cd 放入 source 文件夹,并对 sample.jl 文件进行 nano,添加一个我们可以测试的新函数。
shell> cd src
/home/emmett/dev/julia/sample/srcshell> nano sample.jl
在这个文件中,我放了一些用于模型验证的函数,以及用于支持它们的函数:
mean(x) = sum(x) / length(x)
function mae(actual,pred)
l = length(actual)
lp = length(pred)
if l != lp
throw(ArgumentError("The array shape does not match!"))
end
result = actual-pred
maeunf = mean(result)
if maeunf < 0
maeunf = (maeunf - maeunf) - maeunf
end
return(maeunf)
endfunction mse(y,ŷ)
diff = y .- ŷ
diff = diff .^ 2
Σdiff = sum(diff)
return(Σdiff)
end
function correlationcoeff(x,y)
n = length(x)
yl = length(y)
[@assert](http://twitter.com/assert) n == yl DimensionMismatch("These Arrays are not the same size.")
xy = x .* y
sx = sum(x)
sy = sum(y)
sxy = sum(xy)
x2 = x .^ 2
y2 = y .^ 2
sx2 = sum(x2)
sy2 = sum(y2)
((n*sxy) - (sx * sy)) / (sqrt((((n*sx2)-(sx^2)) * ((n*sy2)-(sy^2)))))
endfunction r2(actual,pred)
l = length(actual)
lp = length(pred)
if l != lp
throw(ArgumentError("The array shape does not match!"))
end
r = correlationcoeff(actual,pred)
rsq = r^2
rsq = rsq * 100
return(rsq)
end
export r2, mae, mse
然后我使用 ctrl+O 来删除新的缓冲区,现在我们可以开始实际测试这些函数了!
测试
现在我们实际上已经有了一个要测试的包,我们可以着手设置我们的测试环境了。对于 Julia 包,测试和文档都被认为是独立于原始包的包。记住,我们将再次使用 generate 命令来创建这两个项目目录。我们将激活这个新的测试环境,然后将测试依赖项添加到它的虚拟环境中。
shell> cd ..
/home/emmett/dev/julia/sample([@v1](http://twitter.com/v1).6) pkg> activate test
Activating new environment at `~/dev/julia/sample/test/Project.toml`(test) pkg> add Test
Updating registry at `~/.julia/registries/General`
Resolving package versions...
Updating `~/dev/julia/sample/test/Project.toml`
[8dfed614] + Test
Updating `~/dev/julia/sample/test/Manifest.toml`
[2a0f44e3] + Base64
[b77e0a4c] + InteractiveUtils
[56ddb016] + Logging
[d6f4376e] + Markdown
[9a3f8284] + Random
[9e88b42a] + Serialization
[8dfed614] + Test(test) pkg>
现在我们可以编写测试算法了。包内的第一个函数是 MAE。为了执行测试,我们需要启动一个测试集。这是通过测试集宏完成的。在宏之后,我们提供一个字符串作为第一个参数,然后我们提供一个表达式作为第二个参数。字符串是测试集的标题,表达式是执行测试的代码。我们可以使用 begin 和 end 块创建这样的表达式。所有这些看起来就像这样:
using Testinclude("../src/sample.jl")using Main.sample[@testset](http://twitter.com/testset) "MAE tests" beginend
我们将从两者相等的测试开始这些测试,对于这个场景,MAE 应该是 1。测试宏接受一个参数,这个参数是一个布尔值。让我们在两个相等数组的 MAE 上使用一个 bool 类型的按位运算符,它应该等于零。
x = [5, 10, 15]
y = [5, 10, 15]
[@test](http://twitter.com/test) mae(x, y) == 0
我们的下一个测试将会是,我们是否真的得到了 dims 差异的平均值。
x, y = [4, 10, 15], [5, 10, 15]
[@test](http://twitter.com/test) mae(x, y) == mean([1, 0, 0])
对于最终的测试集,如下所示:
using Main.sample: r2, mae, mse, mean
[@testset](http://twitter.com/testset) "MAE Tests" begin
x = [5, 10, 15]
y = [5, 10, 15]
[@test](http://twitter.com/test) mae(x, y) == 0
x, y = [4, 10, 15], [5, 10, 15]
[@test](http://twitter.com/test) mae(x, y) == mean([1, 0, 0])
end
接下来,我们将为 MSE 函数开始一个新的测试集。我们将做一个断言测试,和一个零测试,就像我们之前做的一样。
[@testset](http://twitter.com/testset) "MSE Tests" begin
x = [5, 10, 15]
y = [5, 10, 15]
[@test](http://twitter.com/test) mse(x, y) == 0
y = [4, 10, 15]
y2 = [6, 10, 15]
[@test](http://twitter.com/test) mse(x, y) == mse(x, y2)
end
接下来,我们将测试我们的 r 函数。因为我们的 r 函数要返回一个百分比,我们将检查返回的类型是否为 Float64,然后检查它是否小于 1。
[@testset](http://twitter.com/testset) "R2 Tests" begin
x = randn(50)
y = randn(50)
[@test](http://twitter.com/test) r2(x, y) <= 1
[@test](http://twitter.com/test) typeof(r2(x, y)) == Float64
现在,我们可以通过返回到 REPL 并包含这些代码来运行我们的测试。
include("test/test.jl")
(图片由作者提供)
结论
无论你创建什么样的软件,测试它总是很重要的。Julia 有一个非常酷的测试软件的方法,它利用宏并使测试非常有条理。与许多其他测试风格相比,我更喜欢这种方法,我认为 Julia 使用这种方法使测试变得轻而易举。非常感谢您阅读我的文章,我希望所提供的信息对使您的软件更好地工作是有价值的!感谢您的阅读!
如何用 Python 测试你的软件
Python 中测试模块的概述
(src =https://pixabay.com/images/id-2521144/
介绍
不管你写的是什么样的软件,在开发过程中总会遇到一些问题和错误。不管你的软件写得有多好,或者你碰巧是个多么优秀的程序员,这都很重要。幸运的是,有一些方法可以让我们通过测试来发现软件中的错误和问题。
测试是解决软件缺陷和问题的行业标准技术。测试不仅对于修复错误很重要,对于监控软件中隐含的变化也很重要。有时,依赖的方法或类型可能会被意外地完全改变,测试可以让你在最终将你的软件推向精通之前意识到这一点。今天,我想回顾一下 Python 编程语言的测试基础,这样你就可以放心地编写代码了!另外,对于那些也喜欢用 Julia 编程或者只是好奇的人,我也有另一篇用 Julia 做同样事情的文章!:
[## 如何用 Julia 测试你的软件
towardsdatascience.com](/how-to-test-your-software-with-julia-4050379a9f3)
单元测试与集成测试
在开始实际测试我们的 Python 服务器之前,让我们快速回顾一下 Python 中常见的两种测试技术之间的区别。单元测试用于测试软件内部的小组件。另一方面,集成测试用于测试相互依赖的组件。
为了阐明这个观点,让我们考虑一个类比。我们有一栋房子,灯泡坏了。我们可以通过扳动电灯开关来测试灯是否熄灭。这将被认为是一个单元测试。然而,如果我们要测试这盏灯所依赖的电气系统,它将被视为一个集成测试。集成测试同时测试多个组件。
测试
在 Python 中,相当流行的方法是断言测试。断言测试使用条件来提供我们遇到问题时的抛出。我们可以使用许多方法来断言,考虑下面的例子:
def test_sum():
assert sum([1, 2, 3]) == 6, "Should be 6"
if __name__ == "__main__":
test_sum()
print("Everything passed")
这将抛出一个错误。对于后续测试,当在执行过程中抛出错误时,后续测试将不会运行。记住,我们可以通过使用 Python 中的 unittest 模块来解决这样的问题。这通常是通过构建单元测试的测试子类来完成的。测试用例类。我们可以这样写这个类:
**import** **unittest**
**class** **TestStringMethods**(unittest.TestCase):
现在我们将我们的每个测试方法添加到这个类中:
**def** test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')
**def** test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
**def** test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
*# check that s.split fails when the separator is not a string*
**with** self.assertRaises(TypeError):
s.split(2)
最后,我们需要调用 unittest.main()。这个函数在我们的模块内部运行 TestCase 类型的所有子类,并调用它们的所有方法。这将执行测试,并给我们一些可爱的输出,显示我们的测试是如何执行的,同时也运行所有的测试,不管失败与否。
结论
测试是软件工程过程中的一个重要步骤。尽管有时这看起来是额外的工作,但它对于确保您的软件按照预期的方式工作并在未来工作是至关重要的。它还可以用来监控我们软件的隐含变化。我还想说,我真的很喜欢 Python 的测试实现。我认为 unittesting 模块在测试集上加入面向对象的元素是很酷的。这是一个很酷的方法,尽管我仍然会说我更喜欢 Julia 的测试,原因纯粹是主观的——这是一个很好的实现!感谢您的阅读,祝您编码愉快!
如何测试你的 Spark Scala 代码
让我们使用 Mockito 和 scalatest 为 Spark Scala 数据帧转换编写一些测试
克里斯托夫·高尔在 Unsplash 上拍摄的照片
Spark 转换的单元测试可能很棘手,可能你甚至不能编写 try 单元测试(我喜欢 stackoverflow 的这个回答)。然而,您需要以某种方式测试您的转换。我写了这段代码来分享我最近在这个问题上的经验以及我是如何解决这个问题的。
问题陈述:
假设您的数据管道中有一个如下结构的对象:
它读取数据->转换数据->加载到某个存储。简单的 ETL,很容易。现在让我们使用 Mockito 和 scalatest 为该对象编写一个测试。
写作测试
对于测试,我将使用 mockito-scala 和 scalatest 库来使用它,您需要将它添加到您的 build.sbt:
libraryDependencies ++= Seq("org.scalatest" %% "scalatest" % "3.2.2" % Test,"org.mockito" %% "mockito-scala" % "1.16.23" % Test)
现在实际的测试类:
万岁,我们刚刚为我们的火花代码做了一个测试🎉🎉 🎉
让我们重复一下我们在这里所做的事情:
- 我们用 mockito 模拟 IO 方法,所以我们没有加载实际的文件,而是不调用loaddatafrombaute方法,而是替换 DataFrame,它通常会返回我们为测试准备的测试数据
- 类似的事情发生在 writeResultsToBucket 方法上,它从未被调用过
- 我们用 ArgumentCaptor 捕获 spark 转换的结果。
- 我们使用简单的断言比较了转换的实际结果和预期结果
您可能会考虑的一些其他事项:
测试专用火花会议:
你可以从 scalatest*(org . scalatest . BeforeAndAfter)*中使用 before and after,请看这里的例子:https://www.scalatest.org/getting_started_with_fun_suite
或者只为测试创建一个专用的 spark 会话。
如何比较数据帧:
比较数据帧的更好方法可能是借助 spark-fast-tests 库:https://github.com/MrPowers/spark-fast-tests/
特别是如果出于某种原因你需要比较大的数据帧,那么这个库将会非常方便
测试数据:
上面描述的方法依赖于您需要为您的测试准备的数据样本,因为您可能不希望查询与您的生产代码稍后要做的一样多的数据。所以你可以做以下列表中的事情:
- 如果您的样本非常小,比如只有 1-2 列,请在存储库中保存一个小的数据样本
- 作为测试的一部分,随时生成数据,基本上将测试数据硬编码在 scala 代码中
- 将样本数据保存在某个远程存储桶中,并在测试期间加载它
- 最后,您可以从数据库中查询示例数据
每种选择都有其利弊,我个人更喜欢将测试数据以某种人类可读的格式存储在存储库中,比如 JSON。但是这完全取决于你项目的具体情况。
感谢阅读!让我知道你的想法,以及你如何为 spark 编写测试?欢迎随时在 LinkedIn ,GitHub或我的网站piece-data.com
参考资料:
- 莫奇托-斯卡拉:https://github.com/mockito/mockito-scala
- https://www.scalatest.org/
- 火花快速测试:https://github.com/MrPowers/spark-fast-tests/
如何看待数据、数据科学和时间序列
什么是数据、数据科学和时间序列?
数据是对世界的测量。我们可以通过多种方式获取数据;我们可以在一个时间点捕捉不同人的特征数据,从人群中收集样本横截面数据,并找到例如身高分布,即所收集样本中个体的平均身高。我们还可以在多个时间点收集这样的样本,也许可以看到平均高度如何随时间变化——这是面板数据。最后,我们可以专注于一个个体而不是一个群体,多年来每天收集他们的身高测量值,并跟踪他们的身高演变。这个最后的数据将是一个时间序列类型的数据样本。为理解时间序列样本而开发的思想可以从一个人的高度转化为海事部门中一艘船的各种测量值的变化的演化。但是我们所说的“理解”时间序列是什么意思呢?它意味着创建一个函数 f(x) 来捕捉数据点如何随时间变化;在存在某种模式的情况下,该函数将捕获该模式,这样我们将能够描述/总结/抽象/概括时间序列过程。例如,如果我们在一个孩子生命的前 10 年里每年测量他的身高,我们发现他的身高从 70 厘米开始,每年增长 7 厘米,到 10 岁时达到 140 厘米,那么这个函数看起来就像:
**f(x) = b*x(t-1) + 7 (1)**
f(x)是下一年的观测值,x 是当年的观测值;因此,从一年到下一年的过渡由上面的函数定义(在前一个值的基础上增加 7 厘米)。换句话说,该函数捕获了数据生成过程,它被正式称为模型。在数据科学领域,我们使用数据来告诉我们模型看起来像什么。这意味着我们事先不知道孩子身高的进化是他们每年长高 7 厘米。我们“训练”一种算法,该算法找到这种模式,并发出通知我们这种模式的模型。因此,这个模型对于信息/研究原因是有用的,但是对于预测原因也是重要的。如果你知道这是孩子身高演变的方式,你可以用今天的身高,加上 7,得到明年身高的预测!如你所见,在时间序列的例子中,这个个体的观察是相关的。如果我们收集横截面数据(在一个时间点上许多不同个体的身高),就没有理由假设他们的身高以任何方式相关——假设从人群中随机抽取样本。如果我们要从人群中挑选一个新的个体,知道最后被选中的个体的身高并不重要。这与时间序列形成对比,在时间序列中,如果我们明年对同一个人进行采样,知道最后一次观察(他们今年的身高)确实会告诉我们他们可能的身高。
自回归模型
转到稍微更技术性的材料,人们可以很快发现并非所有的时间序列都是相同的。个人的身高可以被视为每年大致呈线性增长,但比雷埃夫斯港的室外温度显然不是线性的!我们看不到每年的温度比去年高 7 度!这里我们看到一个每年重复的季节性模式。分析这些温度数据的数据科学算法/函数将与我们的第一个函数有很大不同。有人可能会说,假设我们有每月观测数据,它会是这样的:
**f(x) = b*x(t-12) (2)**
这意味着本月的观察与 12 个月前相同,有道理;通过对比去年 7 月和今年 4 月的气温,可以最好地预测 7 月的气温。从技术上讲,这被称为自回归模型。有见识的读者可能知道什么是回归;我们只是针对横截面数据的情况讨论了这个函数/模型,例如,在横截面数据中,我们发现一个人的身高如何根据父母的身高而变化。但是在这里,因为我们在时间序列空间内,高度在先前的时间点上回归到自身;因此出现了自动回归。在正常回归的情况下,我们可能有一个我们试图预测的因变量和许多自变量/特征,其中一些将能够解释因变量的变化。真正显著的变量被保留在最终的模型/函数中,,因为数据告诉我们它们是有用的预测器。在时间序列自回归模型的情况下,要包括在自回归中的候选变量是人的身高观察的所有以前的滞后,模型告诉我们哪些过去的时期/滞后对于解释当前的观察是重要的。在温度的例子中,模型告诉我们,无论我们现在观察的是什么时间戳,重要的自回归变量都有 12 个滞后;当前值始终取决于之前的 12 个周期。寻找显著自回归变量的方法与横截面案例非常相似,在横截面案例中,我们可以从探索不同变量的相关性开始,保留一些变量,然后对最相关的变量进行回归,以得出最能代表数据的模型的最终特征选择。在时间序列的情况下,通过使用 Box-Jenkins 方法,我们运行自相关和偏自相关图,以查看时间序列的观测值与过去不同滞后时间的观测值之间的相关性。
作者图片
在上面的部分自相关图中,我们看到这个时间序列具有这样的特征,即当前值依赖于 1 到 9 个滞后之前的值(高于蓝色阈值)。因此,如果我们想要自回归该变量(称其为高度只是为了连续性),我们将选择 9 个特征,即 9 个高度滞后,以最好地表示最终模型中的数据。一个更有经验的建模者会知道一个简约/简单的模型总是更好的,所以他们会只选择第一个滞后作为一个特征,因为它是高度相关的,并且比其他滞后携带更多关于下一个值的信息。因此,他们只会在模型中选择一个如下所示的特征:
**f(x) = b*x(t-1) (3)**
自回归将给出 b 的值,即告诉我们控制所有其他因素的系数,最后一个滞后值对当前值的贡献如此之大。现在,在这一点上,探索这个 b 值是很有趣的,因为它背后隐藏着整个世界的特性。如果这个 b 值小于 1,那么它意味着这个过程是静止的!这意味着它会回复到一个恒定的平均值,不会向一个随机的方向爆炸。在第一个模型(1)中,该模型不是平稳的(它是非平稳的— 单位根),因为 b 是 1;如果这个人的身高等于去年的身高加 7,那么去年的身高应该乘以 b=1。如果我们有一个均值回归过程,b 会更小,例如 0.3,这表明如果在 1 个周期中我们看到一个高值,下一个周期我们应该预期不会看到这样高的值。一般来说,平稳过程更容易预测,因为它们始终有一个恒定的分布,并回复到一个恒定的均值。
作者图片
这是一个均值在 12 左右的平稳过程的例子。如果值为 13,请确保以下值之一将回到或低于 12。
移动平均模型
我们已经说过,建模这一过程的一种方法是使用自回归,其中我们发现在解释当前观察值时哪个滞后是重要的,但也有其他建模方法。第二种方式是移动平均线过程。这里,将每个时间段的预测值与实际值进行比较,定义“误差”值。现在,我们可以不在滞后观测值中(例如在自回归-AR 中)而是在滞后误差中回归当前观测值!这直观地告诉我们,如果一个时期发生**“错误”/冲击**,其影响将持续到未来多少个时期;这就相当于说当前值是由许多年前的误差解释的。同样,我们遵循相同的回归方法,在所有滞后误差中,我们找出哪些误差是显著的,并将其纳入模型。例如,在对石油价格建模时,如果出现冠状病毒导致的石油价格下跌(就像 2020 年 3 月发生的那样),价格需要多长时间才能恢复正常?这是 MA lags 的数量。现在我们知道了 AR 模型(自回归)和 MA 模型(移动平均),如果我们把它们结合起来,我们就得到 ARIMA 模型!在这种模型中,平稳时间序列的下一个值取决于某些滞后的值以及某些滞后的误差。
ARCH / GARCH
继续讨论时间序列建模的其他方法,还有 ARCH 和 GARCH 模型。这代表自回归条件异方差,是关于时间序列的波动性/方差/垂直分布的建模。因此,现在我们不是像在 AR 中那样将当前值回归到滞后值,而是将当前的方差回归到某个滞后之前的观测值的方差。我们的目标是检测波动性激增持续多长时间,或者它会以何种方式重演。这是有用的,因为如果我们发现当发动机的温度变化增加时,它会在未来更多的时期内增加更多,我们会比我们知道变化中的 1 个峰值通常伴随着收缩和向恒定平均值的强烈返回更担心。
**σ = b*σ(t-1) (4)**
这些模型看起来是这样的:一个时期的标准偏差或方差基于前一时期的标准偏差或方差回归。
政权
接下来,我们必须理解时间序列的结构或基本模型会随着时间而变化。例如,在儿童身高的例子中,从 0 岁到 18 岁的演变不同于 18-50 岁。换句话说,在高度的时间序列中有两种不同的“状态”,应该用两种不同的模型来描述。第一个是每年增加 7 厘米,第二个是恒定的;每年的值与前一年相同。在其他时间序列中,我们可能会发现更多的制度,以及在未来重复出现的制度:
作者图片
在船速的时间序列中,我们清楚地看到两种不同的状态,一种是当船移动时平均 12 节,另一种是当船停在港口时平均 0 节。显然,有经验的建模者需要为每个状态(旅行/不旅行)准备 2 个不同的模型。更有经验的建模者可以有一个模型来预测每个状态/状态持续多长时间,这样他们对未来的预测就可以及时地在两种状态之间摆动。这类序列的著名模型是马尔可夫切换自回归,其中模型从一个自回归模型切换到另一个模型,遵循马尔可夫过程;即无记忆过程,其中转移概率矩阵定义了这样的概率,即给定当前状态/制度,下一个状态是旅行的概率是 P(旅行),非旅行的概率是 P(非旅行)。因此,这是一个 2x2 转换矩阵,捕捉状态如何根据我们所处的状态进行转换。问题是,有 0.9 的概率停留在同一个州/政权将永远是 0.9,无论你已经在该政权停留了多少时期。因此,在 1 个方案中停留的小时数的模型更有用。
结构模型/向量自回归
到目前为止,我们已经研究了仅使用时间序列本身获得时间序列的下一个值的预测的情况。当然,一个序列的值不仅取决于过去,也取决于不同的序列。例如,发动机的温度不仅可以通过前一时期的温度来预测,还可以通过当前时期的船速、上一时期的船速、上一时期的 RPM、当前时期或上一时期的风速来预测。这在多元自回归或向量自回归(VAR)等更高级的模型下,开辟了一个全新的可能相关性世界。在多元时间序列的情况下,模型看起来像这样:
**f(x,y) = b*x(t-1) + c*y(t-1) (5)**
这表明一个变量在某一点上的回归是基于它自身的滞后和另一个时间序列的滞后值。在多变量时间序列的世界中,这两个变量都被认为是内生的,即变量 y 不像正常回归情况那样是外生的。这意味着我们需要 2 个方程来模拟它们,1 个方程用来显示 x 如何影响 y,1 个方程用来显示 y 如何影响 x。这些模型通常被称为结构模型或时间联立方程模型。
**X(t) = b*X(t-1) + c*Y(t-1) (6)
Y(t) = b1*Y(t-1) + c1*X(t-1) (7)**
这里有两个变量和两个方程。直觉是,如果我们要在一个多变量环境中模拟和预测一艘船上发动机的温度,我们可以用船的速度作为解释变量;我们将根据当前速度确定下一个温度,因为船只速度越快,RPM 越高,导致温度升高。另一方面, 2 变量可能以相反的方式相关,这需要在系统的第二方程中捕捉:温度越高,船长越有可能降低速度,以使发动机不会过热。
为了求解这个方程组,大致有两种方法:一种是基于领域专业知识来"识别系统并找到系数,另一种是理论方法,即向量自回归方法。在第一种方法中,传统的“ Cowles Commission ”方法用于识别联立方程/结构模型,为了识别 2 个变量(2 个方程)之间的 2 种不同关系,我们需要 2 个外生变量的帮助。如果等式(6)中的 X 是容器的温度,Y 是容器的速度,我们可以将系统增强为如下所示:
**X(t) = b*X(t-1) + c*Y(t-1) + d*F(t-1) (8)
Y(t) = b1*Y(t-1) + c1*X(t-1) + d1*G(t-1) (9)**
我们在这里添加的是每个方程中的一个外生因子,它将帮助我们求解这些方程,并找到系数 b、b1、c、c1、d 和 d1(我们总共在搜索 6 个未知数)。
为了更好地理解这里发生的事情,让我们引入一个供应和需求概念,在这里我们对船舶发动机的需求和船舶发动机的供应进行建模。供给曲线向上倾斜,2 个轴是温度和速度。因此,运动的提供者(发动机)随着速度的增加要求更多的温度——向上倾斜的供给曲线。另一方面,随着温度的升高,运动的消费者(船长)要求更低的速度——向下倾斜的需求曲线。等式 8 是供给曲线,其中一个周期中速度(Y)的增加导致下一个周期中温度(X)的增加,等式 9 是需求曲线,其中当前周期中温度的增加导致速度下降(由于机长避免过热)。
作者图片
假设我们有很多速度和温度的数据,并且我们从领域专家那里了解到这两个变量之间的两种对立关系,那么我们如何将一种关系产生的数据点与另一种关系产生的数据点分开呢?换句话说,我们如何找到需求和供给曲线的形状,以及如何找到等式 8 和 9 中的系数?利用上述的外生因素:为了找到供给曲线,我们在需求方程(9)上引入因子 G,其向上移动需求曲线,暴露供给曲线的斜率,并在供给方程上引入因子 F,其向外移动供给曲线,暴露需求曲线的斜率(即需求方程的系数)。F 的一个例子可能是突然的零风速条件,这将供应曲线向外移动,这是有意义的,因为现在对于相同的发动机温度/发动机功率,你可以获得比恶劣天气条件下更高的速度。G 的一个例子是,船长采取了不那么保守的态度,即使在高温下也允许更高的速度。
在使用向量自回归求解结构模型的第二种方法中,我们不需要使用任何外生变量或限制来识别系统,我们只需将所有变量接受为内生,对它们的滞后进行回归,并继续使用脉冲响应函数、格兰杰因果关系和预测来获得感兴趣变量的准确预测。如果在基准数据上运行,这种向量自回归可以给我们在正常条件下每个变量及其滞后对其他时间序列贡献多少的系数,然后如果在“异常”时期运行,它可以突出系数已经改变,因此表明变量已经改变,不再影响因变量。这是检测系列是否出现异常的可靠方法。
向量误差修正模型
作为 VAR 模型的扩展,人们可以利用协整的概念,建立误差修正或向量误差修正模型(VECM)。当两个或两个以上的非平稳变量在相互回归时,有一个平稳的剩余时间序列时,就会发生协整。趋势稳定的两个时间序列,这意味着它们有一个趋势,如果它们之间的“差异”是稳定的,即具有恒定的平均值,那么这两个序列是协整的。这意味着从长远来看,这两个系列是相关的;协整关系反映了长期关系。VECM 将短期偏差与协整关系结合在一个模型中。因此,人们可以想象,我们可以在 VECM 的协整部分获得变量的长期(基准)关系,在模型的其余部分获得短期偏差(异常)。这可能是使用 VECM 的一种新方法。
主成分分析
此外,我们可以使用主成分分析或动态因子模型,找出哪些时间序列是同步的,可以只用一个潜在变量来表示。因子加载,即选择包含在每个潜在因子下的变量,将会建议哪些变量在正常条件下是“相关的”。
相关
否则,更简单的方法是找出时间序列之间的相关性(忽略时间作为解释维度)。
如何思考概率
数据科学导论
数据科学的离散概率
克利姆·穆萨利莫夫在 Unsplash 上的照片
如果我必须用一句话来解释机器学习模型是如何工作的,我的答案会是*“通过计算(条件)概率。”*概率论是最复杂的数据科学任务的基础。这篇文章简单易懂地介绍了概率论。
在这里,我们将涵盖:
- 离散概率
- 独立事件和条件事件
- 概率的简单数学符号
离散概率
抛硬币,你将只有两种可能的结果(正面或反面)。从一副标准牌中抽出一张牌,你会有五十二种可能结果中的一种。当结果是固定数量的可能结果之一时,我们称之为“分类结果。离散概率处理我们的结果是明确的事件。
在本文中,我们将使用从标准的 52 副牌中抽取的例子。但是你可以把我们在这里学到的概念应用到任何离散概率的问题上。
通常,我们使用概率的目的是为了进行预测。我们试图找出哪种可能的结果是最有可能发生的。在计算这些概率时,我们需要考虑我们试图预测的事件是独立的还是有条件的。
独立性ˌ自立性
独立事件
假设我让你从一副标准洗牌牌中抽一张牌。你的牌是红色的概率正好是 50%,因为你是从一副有 26 张红色和 26 张黑色的牌中随机抽取的。
现在,我们把你的牌放回牌堆,重新洗牌。你下一次抽牌也是红牌的概率有多大?50%,因为我们有与第一次抽签相同的初始条件——随机洗牌中的 26 张红牌和 26 张黑牌。
因为第一次抽奖的结果对任何后续抽奖的概率没有影响,所以我们称这些事件为独立事件。
条件事件
现在,假设我们再次开始同样的实验。但这一次,在你抽完第一张牌后,我们不会把它放回牌堆里。如果第一次抽牌是一张红牌,那么第二张牌也是红牌的概率是多少?
第一次抽牌后,牌组不再均衡。如果你抽了一张红卡,那副牌现在还剩下 25 张红卡和 26 张黑卡。这改变了下一次抽签的概率。因为剩下的红色牌比黑色牌少,所以再抽一张红色牌的可能性降低了。抽到红牌的概率是:
- 第一次抽牌时的 26/52 (0.50)
- 25/51 (0.49)第二次抽签
这向我们展示了一个事件的结果如何影响下一个事件的概率。如果我们不把牌放回牌堆,每一次抽牌都会影响下一次的概率。我们称这些为条件概率。
计算概率(数学符号)
乘法法则
让我们继续我们的纸牌游戏的例子。我们要计算抽任何花色的红脸牌(国王、王后或杰克)的概率。我们可以认为这是两个事件。抽红牌和抽面牌:
- 事件 A =抽红牌
- 事件 B =抽一张脸牌
我们知道一副牌中有 26/52 张红色牌和 12/52 张正面牌。因此,我们可以计算这些事件的概率如下:
- 事件 A = 26/52 = 0.5
- 事件 B = 12/52 = 0.23
我们可以通过考虑每个事件发生的“概率空间”来形象化这个问题——我们用事件发生的概率来表示每个圆的大小:
可视化事件 A 和事件 b 的概率。
要得到红脸牌,我们感兴趣的区域是抽到了红牌和抽到了脸牌的概率的交集(重叠)。
可视化事件 A 和 B 同时发生的概率(两个概率空间的交集)。图片作者。
为了得到这个空间,我们用拿到红卡的概率乘以拿到脸卡的概率:
Pr(A 和 B) = Pr(A) * Pr(B)。图片作者。
所以我们可以把抽到红脸牌的概率计算为:
- Pr(A 和 B) = Pr(A) * Pr(B)
- Pr(A 和 B) = 0.5 * 0.23
- Pr(A 和 B) = 0.115
在这些简单的情况下,我们可以直接验证我们的结果。我们知道一副 52 张牌中只有 6 张红脸牌,这样我们就有 6/52 的机会(或 0.115)。
加法法则
让我们继续我们的纸牌游戏的例子。这一次,我们要计算任意花色抽到红牌或 a 面牌(国王、王后或杰克)的概率。我们可以采用同样的方法,把这看作两个事件:
- 事件 A =抽红牌
- 事件 B =抽一张脸牌
事件和每个事件的概率与我们之前的示例相同:
- 事件 A = 26/52 = 0.5
- 事件 B = 12/52 = 0.23
这个图像帮助我们想象抽一张红卡、一张脸卡或一张红脸卡的概率。
可视化事件 A 和 B 同时发生的概率(两个概率空间的交集)。图片作者。
然而,当试图计算抽红牌或抽脸牌的概率时,这些事件中的任何三个都会满足我们的标准。我们要寻找的是重叠概率的总面积。
想象事件 A 或 B 发生的概率。作者图片。
因此,总结加法法则,我们可以说
Pr(A 或 B) = Pr(A) + Pr(B) - Pr(A 和 B)。图片作者。
换个角度看概率空间图,让我们知道为什么需要减去 A 和 b 的概率。
为什么我们在计算作者的或 B. 图像的概率时要减去 A 和 B 的概率。
如果我们将事件 A 和事件 B 的概率分别相加,我们将两次考虑两个空间之间的重叠,并过度报告 A 或 B 的概率。
- Pr(A 或 B) = Pr(A) + Pr(B) - Pr(A 和 B)
- Pr(A 或 B)= 0.5+0.23–0.115
- Pr(A 或 B) = 0.615
我们可以直接验证我们的结果。我们知道,一副牌中有 32 张牌满足或事件的概率(26 张红牌+ 6 张黑脸牌)。这给了我们 32/52(或 0.615)。
条件事件
你第一次听牌时拿到红脸牌,第二次又拿到 a 的概率有多大?我们可以将此分解为两个独立的事件:
- 事件 A =在第一次抽牌时抽一张红脸牌
- 事件 B =在第二次抽牌时抽出一张 a
在这个例子中,我们引入了条件性。事件 B 发生在事件 A 之后,事件 A 的结果影响事件 B 的概率。
我们可以认为这是试图计算事件 A 和 B 发生的概率。对于类似的情况,我们再次使用乘法规则,并稍微调整一下条件性因素:
Pr(A 和 B) = Pr(A) * Pr(B|A) —竖线表示“鉴于”。它用来表示条件性。图片作者。
如果我们的第一次听牌是红脸牌,那么我们的牌组中还剩下 51 张牌。其中 4 张会是 a。因此,在第二次抽牌时,抽到 a 的概率是 4/51。我们计算任一事件的概率如下:
- Pr(A) = 6/52 = 0.115
- Pr(B | A) = 4/51 = 0.078
所以,抽一张红脸牌后跟一张 a 的概率是:
- Pr(A 和 B) = Pr(A) * Pr(B|A)
- Pr(A 和 B) = 0.115 * 0.078
- Pr(A 和 B) = 0.009
我们可以将此规则扩展到多个条件事件:
多重条件事件的乘法法则。图片作者。
在现实世界中,我们的算法基于概率的平衡做出预测。我们现在可以看到这是如何根据我们的预测任务而变化的。我们是否在考虑:
- 单个事件还是多个事件?
- 特定的事件顺序?
- 从属事件还是独立事件?
虽然离散概率构成了我们在数据科学统计学中所学内容的基础,但它很少直接应用于现实世界的问题。我们的下一篇文章将考虑一种更实用的方法来思考概率。
如何像数据科学家一样思考
距离数据科学更近 6 步
在 Unsplash 上拍摄的 ThisisEngineering RAEng
数据科学家是…
在我们考虑数据科学家的罕见情况下(哈,是的,没错,那很可能永远不会),我们可能会开始考虑某个真正擅长统计、概率、编程和神经网络的人。
约翰·莫塞斯·鲍恩在 Unsplash 上的照片
虽然知识库有助于工作,但我认为数据科学家确实是解决问题的专家。我们可以查看数据,我们可以将领域知识与真实数据结合起来,做出战略性的智能决策。
我还认为,虽然我认识的每个数据科学家都有不同的过程,但基本的方法可以系统化为一系列步骤,就像任何其他算法一样。肯定还有其他方法,比如路孚特实验室的数据科学家和数据质量解决方案。然而,这些观点强调将领域知识应用于问题,而不是为初学者解释行动过程。
由 Jens Lelie 在 Unsplash 上拍摄的照片
在这篇文章中,我想与你分享我认为更实用的思维过程。换句话说,我认为成为一名数据科学家的想法不仅仅是一种心态,更是一种日常实践。
通过用这种方法解决问题,我已经帮助几个客户解决了他们的业务问题,并作为一名计算生物学家和数据科学家为癌症代谢做出了一些贡献。而现在,我希望和大家分享一下我的大致过程。希望对你有帮助。
如何像数据科学家一样思考
作为一名数据科学家,我处理问题的方法有 6 个一般的迭代步骤:
- 定义问题和假设,我正在做关于这个问题
- 概述我将如何解决问题以及我衡量成功的标准是什么
- 收集和组织数据
- 可视化和分析数据
- 建模
- 数据和模型解释
1.定义问题和我对问题的假设
乔治·帕甘三世在 Unsplash 上的照片
“你成为世界级的方法是问好问题”——蒂姆·费里斯
数据科学家的主要目标是能够分析数据、衡量结果、设计实验并做出推动个人或组织前进的决策。
然而,在我们做所有这些之前,我认为最重要的一步是定义问题,我认为与问题相关的假设是正确的。
虽然这看起来是如此简单的一步,但我们通常不会太深入地思考我们试图解决的问题。更糟糕的是,我们很少考虑我们强加给模型和数据的假设和偏见。
这两个错误会让你和你的团队付出巨大的代价。如果我们问了错误的问题或做了错误的假设,我们就衡量了错误的变量。这既浪费了时间和金钱,也带来了来自竞争对手的风险,这些竞争对手对问题的想法可能与你不同,甚至比你更好。
我建议在这一阶段花很多时间,概述我们试图解决的问题、我们目前拥有的证据以及我们明确做出的假设。与雷伊·达里奥的彻底透明的原则一样,我们的目标是尽可能理性地看待问题,消除未知的未知和偏见。
一旦我们清楚地看到了问题,那么,也只有到那时,我们才应该进入下一步。
2.概述我将如何解决问题以及我衡量成功的标准是什么
斯蒂芬·道森在 Unsplash 上拍摄的照片
一旦我们知道了问题,我们需要选择解决问题的工具和评估成功的变量。在这一点上,我们可能对如何执行我们的总体规划没有一个高分辨率的视图。这完全没问题。
但是我们需要概述一个总的行动方案。我们应该考虑的一些事情是:
- 我们认为这是一个有可测量的响应变量的监督学习问题,还是我们需要更多地将它视为一个无监督或半监督学习问题?
- 我们的模型需要多少数据才能工作?
- 获取这些数据要花多少钱?
- 最后,对于我的建模方法来说,成功是什么样的?我们会最大化准确性吗?还是尽量让模型尽可能有可解释性?
虽然概述我们的问题看起来像是从解决问题的步骤中吸取创造力,但它给我们的方法增加了约束,让我们更深入地思考问题。更重要的是,它迫使我们通过一系列可行的步骤来解决问题,并制定策略来解决手头的问题。
3.收集和组织数据
希尔·格雷森在 Unsplash 上拍摄的照片
不幸的是,我们感兴趣的大多数问题在数据库中没有现成的数据。是时候开始收集和生成数据了,或者希望让别人为你收集和生成数据。想想亚马逊的机械土耳其人服务(这个宣传片没有报酬,但叫我亚马逊)。
作为一名数据科学家,最耗时的部分是收集和生成数据。如果你试图解决机器学习问题,将非结构化数据转换成结构化数据集是一个额外的挑战。
4.可视化和分析数据
一旦有了数据集,就该检查数据并真正了解它了。在这里,我们可能会想到探索性数据分析、数据总结和无监督学习。最后,我们开始思考我们在步骤 2 中制定的策略,并开始生成假设进行测试。否则,在极少数情况下,您的数据显示出清晰的模式,并且您有一个简单的问题要解决,您可以直接进入建模。
5.建模
亨特·哈里特在 Unsplash 上拍摄的照片
当然,这是我们都希望能做得更多的数据科学的性感部分。当选择模型子集进行训练时,我们应该考虑我们的目标和我们试图测量的关键性能度量。
在大多数情况下,我们希望我们的模型尽可能准确。但是我们可能也希望模型是可解释的。这就排除了深度学习和其他黑盒算法。
也许我们想知道哪些特性是重要的。那么套索或随机森林是我们最有可能采用的算法。
这一节的要点是**没有一种放之四海而皆准的模式可以解决我们所有的问题。**我们在选择模型的标准上应该具有战略性和选择性。通过确切地知道我们对从模型中测量什么感兴趣,我们可以更快地排除潜在的方法,并决定更快地测试模型。
6.模型解释和采取行动
你可能已经训练了你的模型几个小时,几天,或者几周,你终于得到了结果。现在是时候决定如何利用这些知识了!
你有没有从你的数据中发现一些很酷很有见地的东西?你从你的模型中发现了什么意想不到的结果?你需要调整你所做的一些假设吗?
这些问题将帮助您向前推进下一步,通过足够的迭代,它将为您和您的团队带来更好的决策。
摘要
成为一名数据科学家不仅仅是要知道很多关于机器学习、Python 和数据库的知识。它是能够更快更好地解决问题,使用数据来指导您的决策框架。希望这 6 个步骤能帮助你的数据科学项目!
我们谈谈吧!
如果您想分享您是如何解决一个具有挑战性的数据科学问题的,或者有改进我的方法的想法,请在下面的评论中留下您的回复。
此外,请随时通过 LinkedIn 联系我——我很乐意与你联系并讨论所有书呆子的事情!
如何看待纠缠量子比特
抱歉,会有点疼。
量子机器学习要不要入门?看看 动手量子机器学习用 Python 。
在之前的帖子中,我们了解了量子计算的两种不同观点。首先,我们将量子比特视为概率系统。第二,我们应用线性代数来可视化布洛赫球中的量子位状态。
不幸的是,当我们在最感兴趣的时候看量子位时,这两种观点都是不充分的。那是他们纠缠在一起的时候。
我们最终得到了不亚于一场大爆炸的结果。
作者图片
那么,我们为什么不再问问欧比旺的意见呢?
作者图片
单个量子位的图形表示是没有用的,因为两个纠缠的量子位共享一个叠加态。它们不再相互独立。他们的价值观相互关联。一旦你测量了一个量子位,另一个量子位的状态就会立即改变。
但是每个球体只覆盖一个量子位。
当我们处理纠缠量子比特时,我们需要看整个量子系统。单个量子位是 0 和 1 的组合。所以当你测量它的时候,你可以在这两种状态中找到它。
如果你有一个由两个量子位组成的量子系统,你可以发现它处于四种状态之一:00、01、10 或 11。如果你有三个量子比特,有八个状态,如果你有𝑛量子比特,有 2^𝑛状态。
是时候看看你在前两篇文章中可能忽略的一件事了。这是数学。
作者图片
向量是量子位状态的数学表示。对于量子位,这些是
量子叠加是这两种基态的组合。
𝛼和𝛽的值是我们已经知道的概率幅度。它们的平方表示测量量子位为 0 (𝛼)或 1 (𝛽)的概率。𝛼越大,量子位被测量为 0 的概率就越大。𝛽越大,量子位被测量为 1 的概率就越大。
由于概率的总和必须是 1,我们可以说它们的总和必须是1
。
现在,假设我们有两个量子位。让我们称他们为|𝑎⟩和|𝑏⟩.两个量子位中的每一个都有自己的概率振幅:
当我们同时观察这两个量子位时,有四种不同的基态组合。这些组合中的每一种都有其概率幅度。这些是两个相应状态的概率振幅的乘积。
这四种状态各自形成一个量子系统。因此,我们可以用一个方程来表示它们。虽然我们可以自由地为这个态选择一个任意的名字,但是我们使用|𝑎𝑏⟩,因为这个态是|𝑎⟩和|𝑏⟩.的集体量子态
量子位状态的这种表示类似于单量子位状态|𝜓⟩.的表示唯一的区别是双量子位系统有更多的维度。它有四个基本状态向量,而不是两个。
所有管理单一量子位元的规则,都适用于由两个量子位元组成的系统。它的工作原理相似。因此,所有概率的总和(记住状态的概率是振幅的平方)必须是 1:
不出所料,用两个量子位的系统工作与用一个量子位的系统工作是相似的。唯一的区别是,向量的维数更大。
一个向量代表一个量子位状态,一个矩阵代表一个量子位变换。
在我们的例子中,我们从为每个向量添加 CNOT 矩阵和另一个矩阵开始,比如𝐻|𝑎⟩⊗𝑋|𝑏⟩.此外,张量积是结合的。这意味着我们可以将术语重组如下:
手工处理这种大小的矩阵很麻烦。幸运的是,我们有计算机来计算矩阵和张量积。
那么,让我们来计算由𝐶𝑁𝑂𝑇(𝐻⊗𝑋).表示的矩阵
我们可以看到一个两量子位变换门的矩阵有 4 乘 4 维。它对应于两个量子位的状态向量所具有的四维空间。
除了更多的维度,这里没有什么特别的。我们可以将这个矩阵预先放置在两个量子位的系统中。
当我们将这个矩阵乘以初始状态向量时
然后,我们有效地选择矩阵的第一列,并将其作为新的状态向量。
数学揭示了四种状态的振幅。让我们更仔细地看看结果。
我们通过方程引入了两个量子比特的状态
从这个等式中,我们可以推导出外部值的乘积等于内部值的乘积。
每一边都是每个量子位元振幅值的产物:𝑎0a0、𝑎1a1、𝑏0b0 和𝑏1
但是当我们检查 CNOT 门的结果时,我们得到如下结果:
显然,这是一个矛盾。这个矛盾证明了我们不能再用两个独立的量子比特来描述这个量子态了。它已经成为一种共享状态。
结论
数学并不是量子计算的唯一视角。然而,这是最精确的一个。而且,当布洛赫球不确定时,数学提供了一个清晰的解释。
不幸的是,数学是量子计算最不直观的视角。然而,即使数学有时看起来像象形文字,但它不是。
作者图片
数学是许多教科书、博客文章中教授的一门语言,是的,学校也教授数学。
尽管如此,我仍然相信我们不应该仅仅用数学来解释量子计算。我们应该利用我们所有的不同视角。我们应该尽可能让读者容易理解。但是,我们不应该对读者隐藏所有的数学知识。
量子机器学习要不要入门?看看 动手量子机器学习用 Python 。
在这里免费获得前三章。
如何用 Python 中迭代器的组合追踪危险的小行星
去锈蟒
应用函数组合创建迭代器管道
功能性和面向对象的编程经常被认为是相反的。将 Python 的迭代器对象组合成一个管道就是它们完美互补的例子。我将使用 NASA 的一个开放 API 来展示这种模式。
显示优点的东西
API 小行星——NeoWs 是一个 RESTful 的近地小行星信息网络服务。它允许搜索离地球最近的小行星。
你可以在 NASA API 门户上找到。
它包含一些奇怪的数据——最大物体直径、最近接近距离、接近日期等等。在这些数据中,每个物体都有一个字段显示该物体对地球有潜在的危险。
让我们考虑这样一种情况,当我们想要获取小行星数据,然后按日期将其写入文件,并找到一个潜在危险的小行星。
通常,我从编写代码开始,我希望如何与不存在的高级类或函数进行交互。
这段代码混合了两种范式功能性和面向对象编程。
函数式编程擅长数据处理任务,例如对数据进行顺序操作和改变。对象允许将相关的行为耦合到一个实体中,并存储一个状态。结合两种范例使代码更具声明性。
配置
我们将从隔离每个步骤开始,以分离对象。我们将创建存储每个步骤配置的基本类,稍后将添加所需的实现。
我们需要一个从 URL 加载数据的步骤。
接下来,我们添加一个步骤,它将按日期将加载的小行星数据写入文件。
最后一步将负责寻找危险的小行星。我们将存储一个负责该属性的字段。
功能组成
函数组合是函数式编程的主要模式之一。
直观地说,组合函数是一个链接过程,函数*f*
的输出馈入函数*g*
的输入。
在 Python 中,我们可以如下实现它。
def compose2(f, g):
return lambda *args: g(f(*args))
注意,我们希望在从左到右读取函数时执行它们,我们使用了g(f(x))
。
例如,考虑两个函数。
def square(x):
return x*xdef format(x):
return f"x*x = {x}"
我们可以应用compose2()
函数制作一个新的。
>>> square_and_format = compose2(square, format)
>>> print(square_and_format(2))
x*x = 4
减少
另一种常用的功能模式是折叠或减少功能。reduce()
方法将数组累加成一个值。reduce()
方法为数组的每个值执行一个提供的函数(从左到右)。
顺便说一句,吉多·范·罗苏姆不喜欢它。
所以现在减少()。这实际上是我最讨厌的一个,因为除了几个涉及+或*的例子之外,几乎每次我看到带有重要函数参数的 reduce()调用时,我都需要拿起笔和纸,在我理解 reduce()应该做什么之前,画出实际输入到该函数中的内容。因此,在我看来,reduce()的适用性仅限于关联操作符,在所有其他情况下,最好显式写出累加循环。
——吉多·范·罗苏姆, 命运的减少(Python 3000 中的
幸运的是,它被藏在了functools
模块里。所以我们可以将我们的compose2
函数应用到函数列表中。
from functools import reducedef compose(*functions):
return reduce(compose2, functions)
现在,我们能够为函数列表应用组合。
请求即付的
为了能够对我们的对象应用函数组合和归约,我们需要使我们的类可调用。
在 Python 中,如果我们想让一个对象可调用,我们需要实现神奇的方法__call__
。
对于Load
类,这将是请求数据的简单返回。
class Load:
... def __call__(self):
return requests.get(self.url)
对于Write
对象来说,就有点棘手了。我们将迭代器作为参数,保存它,并返回对象本身。稍后我们将实现迭代接口,它将允许我们组合这样的对象。
class Write:
... def __call__(self, iterator):
self.iterator = iterator
return self
对于Find
,它看起来与Write
一样。
class Find:
... def __call__(self, iterator):
self.iterator = iterator
return self
迭代程序
最后,我们必须为对象实现一个迭代器接口。从技术上讲,一个 Python 对象必须实现__iter__
方法来提供迭代支持。
在我们的例子中,迭代也将保持数据处理的主要逻辑。
我们不需要为Load
类实现迭代。它以列表的形式返回数据,默认情况下可以迭代。
对于类Write
,处理和保存数据到文件的逻辑将在这个方法中实现。
对于Find
类,在迭代过程中,我们将过滤满足我们条件的对象。
决赛成绩
这就是了。我们实现了几个类,这些类可以通过配置启动,并组成一个管道,如下所示。
pipe = pipeline.compose(
pipeline.Load(
url=API_URL, api_key="DEMO_KEY", start_date="2021-05-09"
),
pipeline.Write(to_path="/tmp"),
pipeline.Find(field="is_potentially_hazardous_asteroid"),
)for each in pipe():
print(each)
完整的示例源代码请看我的 GitHub repo。
https://github.com/pavel-fokin/iterators-composition
看起来不错!你怎么想呢?
结论
这种模式有几个优点,我们举几个例子。
优点
- 关注点分离。每一步都有其独立的作用。
- 算法的更多声明性符号。
- 每一步都可以单独测试。
但是和其他模式一样,它不应该被过度使用。如果逻辑变得太复杂或者步骤太多,最好重新考虑这个方法。
更多阅读
如果你喜欢这篇文章,你可以对以下内容感兴趣。
不用 OOP 用 Python 解释依赖倒置原理。
https://levelup.gitconnected.com/tenet-of-inversion-with-python-9759ef73dbcf
仔细观察 Python 中多态性的类型。
https://levelup.gitconnected.com/hidden-power-of-polymorphism-in-python-c9e2539c1633
感谢阅读!与我分享你对LinkedIn和Twitter的想法。如果你喜欢这个故事,请关注me获取更多关于编程的精彩文章。
如何跟踪 Postgres 数据库中所有查询的统计数据,以防止缓慢的查询或瓶颈
用这个扩展提供的重要统计数据来优化数据库性能
这种延伸就像一件救生背心;你永远不会希望你需要它,但它在困难的情况下会非常有用(图片由 Unsplash 上的卡斯滕·温内格特提供)
你有没有想过为什么你的应用程序的某些部分突然变得非常慢?可以是你的数据库吗?你会怎么发现?如果有一个扩展来跟踪它执行的所有查询的统计数据,以便您可以分析数据库性能并清除瓶颈,这不是很好吗?
在本文中,我们将看到一个名为 pg_stat_statements 的 Postgres 扩展。它对数据库执行的所有查询进行统计,从而提供关于数据库性能的重要信息。有了它,您可以轻松地跟踪数据库的性能,检测缓慢的查询,消除瓶颈并防止问题发生。
我们将首先进行非常简单的安装。然后我们演示如何使用它。我们来编码吧!
这个扩展是做什么的?
Pg_stat_statements 是 Postgres 的扩展,它跟踪服务器执行的所有查询的执行统计信息。这个扩展跟踪静态数据,比如
- 该语句被调用的次数
- 语句的最快和最慢执行
- 语句执行时间的平均值和标准差
- 已检索或受影响的总行数
- 关于块的信息(命中、读取、写入、变脏等。)
这对于分析查询的质量非常有用。使用统计数据,很容易调试数据库中的问题,跟踪缓慢的查询和表,并防止与数据库相关的问题。
这一扩展将帮助我们保持数据在数据库中顺畅流动(图片由丹尼斯·内沃扎伊在 Unsplash 上拍摄)
装置
安装 pg_stat_statements 非常简单,只需要三个步骤。在这一部分中,我们将介绍每个步骤,然后向您展示如何在本地安装的 Postgres 实例和 Postgres 的 Dockerized 版本中执行这些步骤。如果你对 Docker 不熟悉,可以看看这篇文章,这篇文章由 Docker【和【撰写。
步骤如下:
- 给
postgresql.conf
添加几行 - 在数据库中创建扩展
- 重启 Postgres
1.修改 postgresql.conf
这个文件存储了 Postgres 的一些配置。我们需要修改它,以便告诉 Postgres 在我们的语句中存储一些统计数据是可以的。
本地安装 首先我们来处理postgresql.conf
文件。找到文件(通常位于 Postgres 安装位置的data
文件夹中(windows 示例:C:\Program Files\PostgreSQL\14\data
)并打开它。找到写着# — Shared Library Preloading
的行,并添加以下三行:
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.max = 10000
pg_stat_statements.track = all
我们将在docker-compose.yml
文件中定义一些额外的命令,以便将额外的行添加到postgres.conf
:
在第 13 行中,我们将 pg _ stat _ 语句包含到 shared_preload_libraries 中。搞定了。
步骤 2:创建扩展
这是一个非常简单的步骤。我们将在 PgAdmin(或另一个数据库 GUI 工具)中执行下面的语句
CREATE EXTENSION pg_stat_statements;
第三步:重启 Postgres
也没那么难;在 windows 中,按下control-r
并运行services.msc
。找到 Postgres,右键,重启。使用 Docker 时,只需重启容器。
安装完成!让我们为一些问题计时(图片由 Veri Ivanova 在 Unsplash 上提供)
演示
现在所有无聊的事情都已经过去了,让我们看看这个扩展能给我们带来的巨大优势吧!我们将假装是一个图书馆,创建一个出租书籍的数据库。
设置-创建表
我们将创建三个表:
我们简单的数据库结构(图片由作者提供)
在执行了创建这些表的语句之后,当我们调用SELECT [some columns] FROM pg_stat_statements
时,我们已经看到了一些统计数据:
我们的首次统计(图片由作者提供)
如您所见,这些统计数据主要涉及创建我们的表的语句。
重置我们的统计数据
下一步是插入一些数据,以便我们可以运行一些查询并检查结果。因为我们对以前操作的统计数据不感兴趣,所以我们调用SELECT pg_stat_statements_reset();
来清除所有当前的统计数据。
插入一些数据并运行一些查询
下一步;插入一些数据!
并运行一些查询:
如您所见,我们执行了一些不同的查询。
分析我们的查询
现在我们可以调用SELECT * FROM pg_stat_statements
并分析执行统计数据。我选择了一些列,并根据平均执行时间对行进行了排序。这是返回的内容:
来自我们新扩展的一批新的执行统计数据(图片由作者提供)
这些结果不言自明,但让我们快速浏览一遍。第一条记录包含了我们执行两个连接的最后一个最大的查询,难怪它是最慢的!
请注意,我们在lib.books
中选择了三个不同的标题。这由记录 5 表示;显示我们对过滤标题的books
表进行了三次调用。
使用统计数据,我们可以调整我们的数据库,以优化性能!(图片由妮娜·梅尔卡多在 Unsplash 上拍摄)
结论
通过这篇短文,我们完成了一个非常有用的扩展。我希望已经展示了简单的安装以及它可以为您提供的监视和改进数据库的价值。如果你有建议/澄清,请评论,以便我可以改进这篇文章。同时,看看我的其他关于各种编程相关主题的文章:
编码快乐!
—迈克
页(page 的缩写)学生:比如我正在做的事情?跟我来!
如何用 TQDM 跟踪 Python 中并行任务的进度
将 TQDM 与多处理相结合
马克-奥利维耶·乔多因在 Unsplash 拍摄的照片
Python 是一种很棒的编程语言。这是一把用于多个领域的瑞士刀:分析和可视化数据,训练机器学习模型,构建 API,抓取网站,DevOps,MLOps,显然还有更多事情。
这种语言的多功能性,除了其他的东西之外,也是有代价的:与其他编程语言相比,Python 是缓慢的。
那么,我应该换一种语言吗?
不急。有很多方法可以提高 Python 的速度。
在本帖中,我们将把多重处理视为其中一种方式。我们将看到如何在用例中利用它,以及如何使用稍加修改的 TQDM 版本来跟踪并行任务的进度。
你知道, TQDM ,这个很棒的进度条,你可能以前用过,用来跟踪你的 for 循环的进度。
今天,我们将看到它是如何被用来跟踪并行任务的。
好的,我们来看看。🔍
什么是多重处理?
多重处理允许您创建可以并发运行的程序,并利用工作站的多个内核。
当进程启动时,每个进程都有自己的内存。这使得在使用多重处理时很难共享多个对象。
作者图片
当您有 CPU 密集型操作,如数据处理(标记化、图像增强、过滤等)时,多处理非常有用。).
作为一名数据科学家,您可以使用多处理来加速您的工作负载。
p_tqdm:并行任务的进度条
正如你可能猜到的,“p”代表平行。
**p_tqdm**
是对 pathos.multiprocessing 和 tqdm 的包装。与 Python 的默认多处理库不同,pathos 提供了一个更加灵活的并行映射,可以应用几乎任何类型的函数——包括 lambda 函数、嵌套函数和类方法——并且可以轻松处理带有多个参数的函数。tqdm 应用于 pathos 的平行图之上,并显示一个进度条,包括预计完成时间。— Github 。
这里没有什么要补充的,让我们看看这个包装器的运行情况。
假设您有一大串数字要处理。
为了简单起见,我们假设这种“繁重的处理”将包括等待 5 毫秒并向一个数字添加一个常数(当然,您可以在这里想象任何更复杂的方式)。
让我们依次运行这个函数,
根据 TQDM,这些任务需要 8 分 42 秒(~ 10000x0.05s 秒)
作者 GIF
现在,如果我们尝试同时运行这些任务,
我们看到完成它们所需的时间是大约 42 秒,大约等于 500 / 12,12 是我的笔记本电脑的内核数。
GIF 格式
👉如果您想将内核数量设置为较低的值,您可以添加num_cpus
kwarg。这就像打字一样简单:
👉除了使用 p_map,您还可以使用:
p_imap
:做同样的事情,但不是返回一个列表,而是返回一个迭代器- `p_umap``:返回一个无序列表(处理速度不是特别快)
p_uimap
:返回无序元素的迭代器
资源
- https://github.com/swansonk14/p_tqdm
- https://github.com/tqdm/tqdm
- https://medium . com/@ rvi swa 00/multi-processing-in-python-to-speed-your-data-science-8a 810267 b07
- https://timber . io/blog/multi processing-vs-multi threading-in-python-what-you-need-to-know/
- https://hackernoon.com/why-is-python-so-slow-e5074b6fe55b
感谢阅读🙏
谢谢你坚持到最后。我希望您发现在您的项目中使用p_tqdm
来并行化工作负载并轻松直观地跟踪它们的进度是有用的。
我们演示的例子非常简单,但是您可以想象在许多场景中p_tqdm
会派上用场。作为一名数据科学家,每当我抓取数据或处理数据时,我的脚本通常会花时间运行。能够在并行化这些操作的同时跟踪它们是非常有趣的。
下面是我如何使用p_tqdm
来加速刮擦:
作者提供的图片——用于抓取的样板代码
如果你想玩这个库,不要犹豫在 Github 上查看它的代码。
我就这些了。下次见!👋
新到中?你可以订阅每月 5 美元,并解锁无限的文章——点击这里。
如何使用专家知识训练贝叶斯网络?
马修·费尼在 Unsplash 上的照片
实践教程
基于专家知识的机器学习
贝叶斯网络是一个强大的 IA 工具,可以用于一些需要混合数据和专家知识的问题。与机器学习(仅基于数据)不同,BN 带来了向人类询问因果定律(单向)的可能性,因果定律存在于我们想要解决的问题的上下文中。
bn 是表示变量之间概率关系的直接非循环图,其中节点表示变量,弧线表示依赖关系。
创建 BN 有三个主要步骤:
1.首先,确定要解决的问题中的主要变量。每个变量对应于网络的一个节点。为每个变量选择数字状态很重要,例如,通常有两种状态(真或假)。
2.其次,定义网络结构,即所有变量(节点)之间的因果关系。
3.第三,定义控制变量之间关系的概率规则。
(作者创作)
每个步骤都可以使用数据、专家知识或两者的结合来完成。在这个例子中,我将展示如何只用专业知识创建一个 BN。
贝叶斯网络的一个基本(教科书类型)例子如下:我们有四个变量,它们都有两种状态(真/假)。我们想知道在天空多云的情况下,草被弄湿的概率;因此,可能会下雨。此外,如果天空不是多云(是晴天),一个洒水器将被激活(使用太阳能),它将灌溉草地。
前两步如下图所示:
贝叶斯网络(作者使用 Genie 软件创建)
如果是阴天,可能会下雨= >阴天节点和雨天节点之间的正因果关系。
如果不是多云天气(是晴天),那么洒水喷头将被激活= >多云节点和洒水喷头节点之间的负因果关系。
最后,草地可能是湿的,原因有两个:正在下雨,或者洒水器被激活= >所以洒水器和雨节点与 wet_grass 节点有正因果关系。
因果关系通过条件概率表(CPT)来表达。
第三步在于创建那些 CPT。在这种情况下基于先前的专家知识。
多云节点:
多云节点 CPT
这是“输入节点”,我们从另一个来源假设多云(多云=真)的概率是 50%。所以,是晴天(多云=假)的概率也是 50%。在贝叶斯逻辑中,这是先验知识(来自于“是否”方面的专家)。
雨节点:
雨节 CPT
雨节点 CPT 应该代表多云节点和雨节点之间的正因果关系。因此,专家(或注册经验)说,鉴于天空多云,下雨的概率为 80%。也就是说,当天空多云(多云=真)时,下雨(下雨=真)的概率为 80%。同理,当天空不多云(多云=假)时,有 80%的概率不会下雨(下雨=假)。
其他情况是上述概率的补充。当天气多云时,不下雨的可能性是 20%。不多云的时候,下雨的概率是 20%。
需要注意的是,所有这些概率都来自于“专家”,或者如我之前所说的“人类知识”。
洒水节点:
喷头节点 CPT
洒水喷头 CPT 节点应代表多云节点和洒水喷头节点之间的负因果关系
CPT 配置来自 Spinkler 功能逻辑:当天气晴朗时(多云=假),洒水器将以 90%的概率被激活(洒水器=真)。因此,补充说明,当天气晴朗时,喷头有 10%的概率不会启动。
当天空多云(多云=真)时,Spinkler 没有基线配置。因此,概率为 50%(真),50%(假)。
同样,这些数字来自“专家”或人类知识,在这种情况下,来自喷灌机的基线配置。
湿 _ 草节点:
湿 _ 草节点 CPT
最后,Wet_Grass CPT 要表示洒水节点和 Wet_Grass 节点之间的正因果关系,以及雨水节点和 wet_grass 节点之间的正因果关系。
这个 CPT 比较复杂。专家可以完成它,或者贝叶斯定理可以帮助我们计算每个值。我已经在前一篇文章中解释了如何使用这个定理来计算这些值。所以我就不在这里做了,我让下面的链接:
我们可以使用与其他节点相同的逻辑来检查这个 CPT 告诉我们的内容:
如果是阴天,下雨的可能性很大(80%);【mag R1】另外,我们还可以加上 10%的概率,即使是阴天,洒水器也会被激活。求和是通过贝叶斯逻辑完成的,(即“加上“条件概率”)。结果是草地有 99%的可能性是湿的:
作者使用 Genie 软件创作
另一种情况是,当洒水装置关闭并且没有下雨时,我们不会期望看到草是湿的:
作者使用 Genie 软件创作
另外两种可能是洒水器开着的时候,不是下雨的时候。或者,当洒水装置关闭并且下雨时。在这两种情况下,在湿草 CPT 之后,我们发现看到湿草的概率是 90%:
作者使用 Genie 软件创作
我们看到贝叶斯网络如何尊重 CPT 的逻辑,这是可预测的,因为 CPT 是以这种方式“人工构建的”。
然而,这个小例子可以向我们展示贝叶斯网络的范围,也就是说,基于我们用来创建 CPT 的信息,我们可以实验更多在构建它时没有包括的案例(我认为这就是人工智能的意义)。
测试一个 BN 的学习
每个节点的基线概率分布在下一个网络中表示:
作者使用 Genie 软件创作
这个网络将条件概率显示在一起。在我们不知道天气是否多云的一天(真= 50%的概率),我们可以预期有 85%的概率会有湿草。这是可以解释的,因为我们有 70%的机会打开洒水器,有 50%的机会下雨。
然而,我们可以开始玩网络,看看它告诉我们什么:
如果是阴天(100%真实),喷头有 50%的概率开着,下雨有 80%的概率。因此,草地将有 85%的概率是湿的
作者使用 Genie 软件创作
反之,如果不是阴天,90%的概率会开着洒水车,20%的概率会下雨。因此,草地潮湿的概率为 84%。
作者使用 Genie 软件创作
新增病例:
这是我们可以开发 BN 潜力的时候(当它被正确建造的时候)。我们可以探索更接近真实世界的不同案例(巨大的组合):
例如,如果多云的概率是 20%、40%、60%或 80%,会发生什么?
这些网络显示了在这些情况下发生了什么:
作者使用 Genie 软件创作
我们看到在所有情况下,当天空因下雨而多云时,以及当天空因洒水器而不多云时,草将是湿的。所以,如果我们这种配置的目标是让草地尽可能的湿润,那么洒水器就是最好的选择!国阵从数量上解释了原因。
我用了四个例子,但是如果我们测试所有的值(多云=真从 0%到 100%),Wet_grass =真的状态,会一直在 84%到 85%之间。
逆向传播:从 BN 中获得更多价值。
我们还可以为目标节点(Wet_grass)设置“证据”,以便测试网络中的不同场景。这使我们能够作出决定。
例如,假设我们不知道天气是否多云,(50%对,50%错),如果我们希望草地是湿的,我们需要洒水器以 78%的概率打开,雨将以 56%的概率到来:
作者使用 Genie 软件创作
再举一个例子:我希望我的草永远是湿的。
案例 1:天气频道宣布有超过 95%的可能性是阴天。有必要用我的洒水器吗?答案是否定的。
作者使用 Genie 软件创作
案例 2:天气频道宣布阴天的概率小于 12%。为了 100%确定我的草地会是湿的,我必须使用我的洒水器。
作者使用 Genie 软件创作
影响分析
最后可以做一个网络的影响力分析。它向我们展示了哪些是最有影响力的节点,以及最有影响力的链接:
作者使用 Genie 软件创作
这个例子尽可能简单,但它告诉我们,我们需要洒水器或雨水来湿润草地(红色)。它还向我们表明,雨和多云天空之间的关系比多云天空和洒水器之间的关系更强一点(这可以从连接箭头的粗细看出)。
结论
这篇文章用一个非常简单的例子详尽地展示了如何使用专家知识来构建和训练一个 BN。
我展示了 BN 建模现实生活问题的范围和能力,包括概率知识和概率组合。
然而,现实生活中的问题有更高(真的更高)的复杂程度。
为此,有必要使用数据和人类知识。我将在下一篇文章中解释这一点。
如果你想继续阅读这样的故事,你可以在这里订阅!
如何从头开始训练 BERT 模型
见见伯特的意大利表弟,菲利波托
伯特,但在意大利——作者图片
我的所有文章都聚焦于 BERT——这个模型主宰了自然语言处理(NLP)的世界,标志着语言模型的新时代。
对于那些以前没有使用过变形金刚模型的人来说,这个过程看起来有点像这样:
pip install transformers
- 初始化预训练的变压器模型—
from_pretrained
。 - 用一些数据测试一下。
- 也许对模型进行微调(再多训练一些)。
现在,这是一个伟大的方法,但如果我们只是这样做,我们缺乏对创建我们自己的变形金刚模型背后的理解。
此外,如果我们无法创建自己的变压器模型,我们必须依赖适合我们问题的预训练模型,但情况并非总是如此:
一些关于非英语 BERT 模型的评论
因此,在本文中,我们将探索构建我们自己的 transformer 模型所必须采取的步骤——特别是 BERT 的进一步开发版本,称为 RoBERTa。
概述
这个过程有几个步骤,所以在我们开始之前,让我们先总结一下我们需要做什么。总的来说,有四个关键部分:
- 获取数据
- 构建标记器
- 创建输入管道
- 训练模型
一旦我们完成了每一个部分,我们将使用我们构建的记号赋予器和模型——并保存它们,这样我们就可以像使用from_pretrained
一样使用它们。
获取数据
和任何机器学习项目一样,我们需要数据。在用于训练 transformer 模型的数据方面,我们确实有太多的选择了——我们可以使用几乎任何文本数据。
使用 HuggingFace 的数据集库下载 OSCAR 数据集的视频演练
而且,如果说我们在互联网上有很多东西的话,那就是非结构化文本数据。
从互联网上搜集的文本领域中最大的数据集之一是 OSCAR 数据集。
OSCAR 数据集拥有大量不同的语言,其中一个最明显的从头训练用例是,我们可以将 BERT 应用于一些不太常用的语言,如泰卢固语或纳瓦霍语。
不幸的是,我唯一能说的语言是英语,但我的女朋友是意大利人,所以她——劳拉,将评估我们说意大利语的伯特模型的结果——菲利贝托。
因此,为了下载奥斯卡数据集的意大利部分,我们将使用 HuggingFace 的datasets
库——我们可以用pip install datasets
安装它。然后我们下载 OSCAR_IT:
我们来看看dataset
这个物体。
很好,现在让我们以一种在构建我们的标记器时可以使用的格式存储我们的数据。我们需要从我们的数据集中创建一组仅包含text
特性的明文文件,并且我们将使用换行符\n
分割每个样本。
在我们的data/text/oscar_it
目录中,我们会找到:
包含我们的明文 OSCAR 文件的目录
构建标记器
下一个是记号赋予者!当使用转换器时,我们通常加载一个记号赋予器,以及它各自的转换器模型——记号赋予器是这个过程中的一个关键组件。
构建自定义令牌化器的视频演练
在构建我们的标记器时,我们将向它提供我们所有的 OSCAR 数据,指定我们的词汇大小(标记器中的标记数量),以及任何特殊的标记。
现在,罗伯塔特殊令牌看起来像这样:
因此,我们确保将它们包含在我们的标记器的train
方法调用的special_tokens
参数中。
我们的标记器现在已经准备好了,我们可以将它保存为文件供以后使用:
现在我们有两个文件定义了新的 FiliBERTo 记号赋予器:
- merges.txt —执行文本到标记的初始映射
- vocab.json —将令牌映射到令牌 id
有了这些,我们可以继续初始化我们的记号赋予器,这样我们就可以像使用任何其他from_pretrained
记号赋予器一样使用它。
初始化标记器
我们首先使用之前构建的两个文件初始化记号赋予器——使用一个简单的from_pretrained
:
现在我们的记号赋予器已经准备好了,我们可以试着用它来编码一些文本。当编码时,我们使用通常使用的两种方法,encode
和encode_batch
。
从编码对象tokens
中,我们将提取input_ids
和attention_mask
张量用于 FiliBERTo。
创建输入管道
我们培训流程的输入管道是整个流程中更复杂的部分。它包括我们获取原始的 OSCAR 训练数据,对其进行转换,并将其加载到准备好进行训练的DataLoader
中。
MLM 输入管道的视频演练
准备数据
我们将从一个样本开始,逐步完成准备逻辑。
首先,我们需要打开我们的文件——与我们保存为的文件相同。txt 文件在先。我们基于换行符\n
分割每个样本,因为这表示单个样本。
然后,我们使用tokenizer
对我们的数据进行编码——确保包括关键参数,如max_length
、padding
和truncation
。
现在我们可以继续创建我们的张量——我们将通过掩蔽语言建模(MLM)来训练我们的模型。所以,我们需要三个张量:
- input_ids —我们的 token_ids 有大约 15%的令牌使用屏蔽令牌
<mask>
屏蔽。 - attention _ mask—1s 和 0 s 的张量,标记“真实”记号/填充记号的位置——用于注意力计算。
- 标签 —我们的 token_ids 带有号屏蔽。
如果你不熟悉 MLM,我在这里已经解释过了。
我们的attention_mask
和labels
张量就是从我们的batch
中简单提取出来的。然而input_ids
张量需要更多的关注,对于这个张量,我们屏蔽了大约 15%的记号——给它们分配记号 ID 3
。
在最终输出中,我们可以看到一个编码的input_ids
张量的一部分。第一个令牌 ID 是1
—[CLS]
令牌。围绕张量我们有几个3
记号 id——这些是我们新添加的[MASK]
记号。
构建数据加载器
接下来,我们定义我们的Dataset
类——我们用它来初始化我们的三个编码张量作为 PyTorch torch.utils.data.Dataset
对象。
最后,我们的dataset
被加载到 PyTorch DataLoader
对象中——在训练期间,我们用它将数据加载到我们的模型中。
训练模型
我们训练需要两样东西,我们的DataLoader
和一个模型。我们有——但没有模型。
初始化模型
为了训练,我们需要一个未经训练的BERTLMHeadModel
。为此,我们首先需要创建一个 RoBERTa 配置对象来描述我们希望用来初始化 FiliBERTo 的参数。
然后,我们用语言建模(LM)头导入并初始化我们的 RoBERTa 模型。
培训准备
在进入我们的训练循环之前,我们需要设置一些东西。首先,我们设置 GPU/CPU 使用率。然后我们激活模型的训练模式——最后,初始化我们的优化器。
培养
终于——训练时间到了!我们就像平时通过 PyTorch 训练一样训练。
如果我们去 Tensorboard,我们会发现随着时间的推移我们的损失——它看起来很有希望。
损失/时间—在此图表中,多个培训课程串联在一起
真正的考验
现在是真正考验的时候了。我们建立了一个 MLM 管道,并请劳拉评估结果。可以在这里看 22:44 的视频回顾:
我们首先使用'fill-mask'
参数初始化一个pipeline
对象。然后像这样开始测试我们的模型:
“ciao来了 *va?”*正确答案!这是我的意大利语达到的最高水平——所以,让我们把它交给劳拉。
我们从开始,“早上好,来弗吉尼亚吗?” —或者*“日安,你好吗?”*:
第一个回答,“buongiorno,chi va?”意思是“日安,谁在那里?”—如无意义。但是,我们的第二个答案是正确的!
接下来,是一个稍微难一点的短语,“你好,我的鸽子?” —或者*“嗨,今天下午我们在哪里见面?”*:
我们返回一些更积极的结果:
✅ "hi, where do we see each other this afternoon?"
✅ "hi, where do we meet this afternoon?"
❌ "hi, where here we are this afternoon?"
✅ "hi, where are we meeting this afternoon?"
✅ "hi, where do we meet this afternoon?"
最后,还有一个更难的句子,“你成功了吗?”或者“如果我们选择了另一天,会发生什么?”:
我们在这里也给出了一些更好的答案:
✅ "what would have happened if we had chosen another day?"
✅ "what would have happened if I had chosen another day?"
✅ "what would have happened if they had chosen another day?"
✅ "what would have happened if you had chosen another day?"
❌ "what would have happened if another day was chosen?"
总的来说,看起来我们的模型通过了 Laura 的测试——我们现在有了一个名为 FiliBERTo 的合格的意大利语言模型!
这就是从头开始训练 BERT 模型的演练!
我们已经覆盖了很多领域,从获取和格式化我们的数据,一直到使用语言建模来训练我们的原始 BERT 模型。
我希望你喜欢这篇文章!如果你有任何问题,请通过推特或者在下面的评论中告诉我。如果你想要更多这样的内容,我也会在 YouTube 上发布。
感谢阅读!
*所有图片均由作者提供,除非另有说明