揭秘印度尼西亚顶级电子商务——来自 Twitter 的观点
印度尼西亚顶级电子商务的 Twitter 分析(Tweets,Followers,和 Following)
封面由作者创作,标识取自 Tokopedia.com、Shopee.co.id、Bukalapak.com、Lazada.co.id、Blibli.com 和 Twitter.com
印度尼西亚
印度尼西亚,一个截至 2020 年 5 月 9 日(星期六)总人口为 273,097,566 的国家来源是一个电子商务初创公司成长的充满希望的地方。根据统计,2020 年印尼电子商务市场的收入约为 269.22 亿美元,预计年增长率(2020–2014)为 16.9%,到 2024 年市场规模将达到 503.61 亿美元。市场最大的部分是时装(2020 年为 67.71 亿美元)。由 Cuponation 进行的一项研究报告了 2019 年印尼排名前 5 的电子商务网站,分别是 Tokopedia、Shopee、Bukalapak、Lazada、 和bli bli(Shopee 和 Lazada 分别指 Shopee Indonesia 和 Lazada Indonesia)。
数据
使用的数据是从 5 家电子商务公司的 Twitter 账户收集的推文,从他们的第一条推文开始,直到 2020 年 4 月 30 日以及截至 2020 年 4 月 30 日的关注者和追随者列表。一系列预处理技术已经应用于收集的推文。在本文的其余部分,电子商务的名称指的是它的 Twitter 账户。
免责声明: 每个电子商务数据都是从每个电子商务 Twitter 账户中收集的,并以相同格式的代码平等对待。本文中的所有数字都是基于收集的数据。与 Twitter 上实际数字的差异是由于 Twitter 在数据采集过程中的一些政策限制。
概观
就推文数量而言,Shopee 以 266,233 条推文领先,是 Tokopedia 的 9 倍,尽管 Shopee 是该国的最新玩家。排名前 5 的电商,关注的账号都不到 10 个。与此同时,Blibli 拥有最多的追随者。下面将提供更详细的分析。
*表 1 各电商概况统计。包括转发、提及和回复
第一条推特
千里之行始于足下——老子
让我们回到他们都开始的时候。
图 1 各电商的第一条推文;tokopedia(2010 年 3 月 25 日)、Blibli(2011 年 4 月 30 日)、Bukalapak(2012 年 2 月 11 日)、laza da(2012 年 3 月 15 日)和 Shopee Indonesia(2015 年 5 月 12 日)
这条时间线不仅显示了他们发布第一条推文的时间,还显示了他们的内容。Tokopedia 正在宣布来自 East Ventures 的投资,Bukalapak 正在他们的市场上推广一种产品,Shopee 正在发送介绍性的推文,通知他们在该国的存在。
值得注意的追随者
如表 1 所示,所有电子商务只有一位数的追随者。一些电子商务跟随其创始人/联合创始人/首席执行官,而一些电子商务跟随知名公众人物或团体,如印度尼西亚共和国总统和克里斯蒂亚诺罗纳尔多。表 2 列出了以下著名账户的详细情况
表 2 各电商列表如下
我们来谈谈这个数字
收集的推文包括一般推文、回复、提及和转发。为了简化分析,我将它们分为 3 类,即一般推文、提及(回复+提及)和转发。关于他们差异的全面解释可以在推特网站上看到。
趋势
这一小节将详细阐述一般推文、提及和转发的趋势。图 2 显示,在 2010 年至 2012 年期间,就一般推文而言,Bukalapak 是 Twitter 中最活跃的。自 2013 年以来,Blibli 一直保持着最活跃的地位,尽管自 2015 年以来所有电商(Shopee 除外)都呈下降趋势。很有可能在 2020 年底,Shopee 将拥有最多的通用推文。
图 22010 年至 2020 年各电子商务的一般推文趋势
图 3 显示了每个帐户与其追随者之间的活跃程度。2015 年,Lazada 在提及其他 Twitter 账户/其关注者方面最为活跃。这包括回答其追随者的问题或与追随者的简单互动。然而,在接下来的一年中,这种参与水平急剧下降。与此同时,自进入中国以来,Shopee 与粉丝的互动逐年增加,并在 2019 年达到峰值,约有 12.7 万次提及。
图 32010 年至 2020 年各电商的提及趋势
就转发而言,Bukalapak 在最初几年非常活跃,但此后数量呈下降趋势。Tokopedia 在 2015 年也曾一度领先,但之后数量也大幅下降。虽然其他人在转发中变得不那么活跃,但 contrasty Shopee 的转发数量在过去三年中呈上升趋势。
图 42010 年至 2020 年各电商的转发趋势
比例
为了简化分析并确保数据的相关性,从这一小节开始,我将只使用 2015 年至 2020 年的数据。
图 52005 年至 2020 年间来自各电子商务的推文类型比例
圆圈的大小代表推文总数。Tokopedia 和 Blibli 显示了类似的趋势,因为自 2015 年以来,它们的总推文数量逐年下降。有趣的是,Shopee 和 Lazada 有相反的趋势,Shopee 显示推文增加,Lazada 减少。与此同时,总体而言,Bukalapak 的趋势正在下降,尽管与前几年相比,2019 年他们更加活跃。
圆圈还显示了每个账户的推文类型的比例。自 2016 年以来,超过 90%的 Shopee 推文内容被提及,这一比例每年都在增加,2019 年达到 97.66%。与此类似的是 Lazada,他也花费了大约 80-87%的推文进行提及。这一发现表明 Shopee 和 Lazada 分配了相当多的时间与他们的追随者交流。相比之下,Tokopedia 和 Blibli 的推文大约有 75%是普通推文。从 2015 年到 2018 年,Bukalapak 显示一般推文的比例越来越高。然而,自 2019 年以来,他们转向更高比例的提及,而不是一般的推文。
点赞、回复和转发
从这一小节开始,我将只使用一般推文的数据,以便简化和确保分析的相关性。
由于有许多推文的粉丝 0 赞和/或 0 回复和/或 0 转发,我决定排除符合这些标准的推文。图 6 显示了 2015 年至 2020 年间每个电子商务推文的点赞、回复和转发的中位数。
很明显,在过去的 3 年中,Shopee 在所有变量中一直领先,其次是 Tokopedia,而 Blibli 和 Tokopedia 一直是垫底的两个。前 2 名和后 2 名之间的差距非常大。
图 62015-2020 年各电商一般推文的点赞、回复、转发的中位数
内容
理解推文的内容很重要。就内容而言,有 3 种不同类型的推文,即纯文本推文、带文本和照片的推文以及带文本和视频的推文。
图 7 显示 Tokopedia 和 Lazada 有几乎相似的偏好。他们更喜欢发布带照片的推文,除了 2020 年 Tokopedia 在只有文字的推文中占了更大的比例。与此同时,布卡拉帕克在过去四年里一直专注于带照片的推特。另一方面,Shopee 从 2019 年开始从带照片的推文转向文字。自 2015 年以来,Blibli 更喜欢只有文本的推文,并继续每年增加其比例,并在 2020 年 4 月达到 86.10%。同样清楚的是,他们都在试图增加带有视频的推文的比例。
图 72015 年至 2020 年间,基于各电子商务内容的推文比例
内容与印象的关系
内容会影响推文的印象吗?
图 8 各电商基于内容的点赞分布
根据 likes_count 的中位数,带有照片的推文获得了更多的喜欢。然而,这并不适用于 Shopee,它对所有不同类型的内容都获得了几乎相似的印象。
图 9 每个电子商务基于内容的回复分布
从图 9 来看,根据 replies_count 的中位数,如果推文包含视频,Bukalapak、Lazada 和 Blibli 会获得更多回复。另一方面,Tokopedia 和 Shopee 在只有文字的推文中获得了更好的参与度。
图 10 基于每个电子商务内容的转发分布
就转发而言,如果推文中包含视频,所有电子商务都会获得更多的转发。与 replies_count 类似,如果推文仅由文本组成,Bukalapak、Lazada 和 Blibli 获得的转发量最少。
从图 8、9 和 10 中可以发现一个有趣的现象,Shopee 倾向于在只有文本的 tweet 上有更好的参与度。他们的推文可能包含吸引粉丝注意的有趣内容。然而,还需要进一步的分析来证实这一点。
日期和时间
一个有趣的发现,每个电子商务都有自己最喜欢的日子。与此同时,周日是他们所有人发微博数量最少的一天。这可能是因为周末。
图 11 每个电子商务工作日的推文数量
上午 10 点(GMT +7)是 Shopee 和 Lazada 最喜欢的时间,而晚上 7 点(GMT +7)是 Bukalapak 和 Lazada 最喜欢的时间。然而,Tokopedia 和 Bukalapak 的分布模式相似,Shopee 和 Lazada 的分布模式相似。
图 12 每个电子商务每小时的推文数量
可以对此做进一步的分析,例如,哪一天/时间会获得更多的关注,但现在,我将限制在这里
让我们来谈谈推文
长度
为了分析推文的长度,我删除了停用词。图 13 显示了大多数 tweets 的长度在 12-14 个单词之间。然而,Shopee 和 Bukalapak 似乎具有双峰分布。
图 13 各电商的推文长度分布
标签
图 14 显示从 2015 年到 2020 年,Shopee 与他们的 hashtag ( #shopeeid )一致。对于 Tokopedia 本身来说,在 2018 年和 2019 年之间,他们在 #mulaiajadulu 上开展了活动,但在 2020 年,他们一直在推广 #tokopediasaja 。在过去的两年里,Bukalapak 一直专注于他们的标签。在过去的 5 年里,Lazada 几乎在所有的推文中都加入了#lazadaid。最后,对于 Blibli 来说,在 2020 年,他们将推出 #karenakamuno1 ,这与他们的客户满意度品牌有关。
图 142015-2020 年各电商年度热门标签
表情符号
Shopee 倾向于在几乎一半的推文中包含表情符号,而其他的仍然低于 5%。一个有趣的未来分析是看到表情符号的存在与印象之间的关系,但不是在这个写作中😀。
图 15 各电子商务中使用和不使用表情符号的推文比例
我们来谈谈追随者
在概述部分,我已经提到了每个电子商务的总关注者。在这里,我合并了所有关注者,唯一关注者总数为 1,317,933 。然后,我创建了一个维恩图来查看每个电子商务追随者之间的交集,如图 16 所示。所有电子商务共有 63 080 名关注者。从数量变量来看,Blibli 的忠诚者最多,而 Bukalapak 的忠诚者最少。Lazada 和 Blibli 共有 94060 名共同关注者,是其他两对中最多的。另一个发现是,Tokopedia、Bukalapak、Lazada 和 Blibli 共有 71,568 名共同追随者。这些追随者似乎是电子商务的早期追随者,这可能是他们不跟随 Shopee 的原因,Shopee 是印度尼西亚最新的电子商务玩家。更多信息可以在图 16 中看到
图 16 所有电商的追随者分布维恩图。
有很多更深入的分析可以在追随者身上进行,例如,通过执行图形分析来找出一个电子商务的忠诚者到其他电子商务的距离,然而,对于这篇文章,我将把它限制在这里。也许在未来,我可以更深入地了解这一点:)
评论
本文无意偏袒任何一方,不做任何判断,也不提供任何指导或建议。这篇文章的目的仅仅是分析和描述印度尼西亚五大电子商务的 Twitter 账户。我希望你喜欢并发现这篇文章有用!😀欢迎反馈。 Linkedin
引擎盖下—决策树
通过观察选择正确分支的数学过程,理解决策树的工作原理,从而得到最好的树
弗拉季斯拉夫·巴比延科在 Unsplash 上的照片
在后台
这是一系列文章中的第三篇,在这一系列文章中,我们将理解各种 ML 算法的“幕后”工作,使用它们的基本数学方程。
有这么多优化的实现,我们有时太关注库和它提供的抽象,而太少关注进入模型的底层计算。理解这些计算往往是一个好模型和一个伟大模型的区别。
在本系列中,我将重点放在手工实现算法上,以理解其背后的数学原理,这将有望帮助我们训练和部署更好的模型。
决策图表
决策树是最受欢迎的机器学习算法之一,因为它们简单直观。它形成了许多其他算法的基础,如随机森林和梯度提升机器,这些算法是大多数数据科学竞赛的主要内容,直到几年前,仍然是最通用、最容易理解的 ML 算法之一。
决策树的工作方式非常类似于我们人类通过问一系列问题来做决定的方式。例如,我们经常预测(或者曾经预测,直到我们可以在手机上查看当天的天气预报)外出时是否要带伞,基于各种决策点,例如—
- 天气多云吗?
- 现在是几月?
- 过去几天的这个时候下雨了吗?
- 我要走多远?
- 我有雨伞吗?(这应该是第一个问题)
基于对其中一个或多个问题的回答,我们出门时可能带伞,也可能不带伞。
决策树(用于分类)以类似的方式工作,一次询问一个变量,以找到数据中不同类别之间的最佳分割。
考虑以下数据,具有两个输入特征— X1、X2 和一个二元(0/1)目标特征— Y
有 10 行的示例数据
该算法遵循以下步骤来找到这种数据的最佳分割。
0.两个类的样本数据
- 对于每个输入变量,计算不同阈值下的数据分割。
1.计算不同阈值下的分割
2.选择给出最佳分割的阈值。
2.选择能产生最佳分割效果的分割
3.重复步骤 1 和 2,直到收敛。
3.计算剩余数据的拆分
4.组合所有选定的阈值,形成一个用于数据分类的规则链(树)。
4.组合选择的阈值以形成决策树
让我们使用示例数据更深入地研究这些步骤。
1.对于每个输入变量,计算不同阈值下的数据分割
这是决策树算法的第一步,有两个主要的决策点
- 我们如何选择阈值变量?
- 我们如何选择阈值?
对于第一种,最常见的策略是考虑所有变量,并检查哪个变量+阈值组合给出了数据的最佳分割。
第二点是事情变得更有趣的地方。可以有各种方法来选择每个变量的阈值—
- 在每个变量的范围内选择随机值。例如,对于变量 X2 ,值的范围在 3 和 9 之间(假设最后两行被保留用于验证我们的“模型”)。我们可以选择 3、4、5、6、7、8 和 9 的整数阈值。
很明显,当变量的范围增加到数百个时,或者在处理浮点特性时,这将不能很好地伸缩。在 3.6 和 3.7 之间,我们可以适应多少个浮点阈值? - 另一种方法是对变量中的唯一值进行排序,并在每个连续值之间选择一个阈值—使用最大值/最小值/平均值。这将把我们的搜索空间缩小到数据中存在的值的子集,因为阈值是基于数据中存在的值而不是整个范围的值来选择的。
对于变量 X2 ,阈值看起来像这样
可变 X2 的阈值策略
类似地,对于 X1 —
有一种更好的方法来选择进一步降低搜索空间的阈值。我们将在理解“最佳分割”以及如何使用这些阈值形成决策节点之后再来看它。
让我们继续选择*阈值(平均值)*值作为我们的阈值。
2。选择给出最佳分割的阈值
为了选择给出最佳分割的阈值,我们首先需要定义什么是“最佳分割”。
为了直观地理解它,我们可以看到在下面的图像中, X1=t2 看起来像是最佳分割,因为分割的左半部分只包含蓝色点(纯节点)。换句话说,可以说如果随机选择的点有 X1 < t2 ,那么这个点就是蓝色的。我们不能在 t1 或 t3 做出同样的推断。
因此,在每个阈值处,我们检查阈值两侧的点集的分布是否比分割前更好。我们选择能够提供最佳数据分布的阈值—理想情况下,该阈值将单个类(在我们的示例中为蓝色)的大多数点放在一边,同时在分割的那一边保留其他类(在我们的示例中为红色)的少数(最好是 0 个)实例。
从数学上来说,我们在拆分之前检查数据的杂质测量值(每类点的密度)与拆分的平均杂质测量值,并选择产生最大差异的阈值。对于我们的情况,让我们考虑“基尼系数”,定义为
其中 k 为类数,pᵢ*t5】为类概率 i.*
对于我们的数据,
为了计算我们在第一步中选择的基尼系数,我们首先将数据按照 X2 变量排序
按要素 X2 排序的数据
我们的数据在 0 类和 1 类各有四个点。因此,分割前的基尼系数可以计算为
如果我们考虑阈值为 *X2=5 的分割,*数据分布看起来像—
我们最终在拆分的每个部分都有 4 行。我们可以计算两种分裂的基尼系数——
因此,我们可以将杂质的总体变化(减少)计算为—
这种增益变化(δG)也称为“信息增益”。
我们对 X2 的所有阈值进行相同的计算
上表显示,使用特征 X2 进行决策时,最佳可能增益为阈值 7。
我们需要为另一个特征重复这个过程— X1 。按照我们为 X2 所做的一系列步骤,我们得到—
我们注意到,如果我们在 X1=5.5 处分割数据,我们会得到相同的最高增益值(0.340)。
在这种情况下,我们为多个特征获得相同的最大增益,选择左边的第一个特征(如在原始数据中)。根据我们案例的规则,“最佳分割”发生在 X1=5.5 。
因此,当我们的决策节点在 X1=5.5 时,我们可以开始创建一个“决策树”,就像—
3.重复步骤 1 和 2,直到收敛
我们对每个拆分数据重复步骤 1 和 2,直到我们的所有拆分都包含纯样品(杂质为零)。
根据上一步中在 X1=5.5 处的分割生成的数据集之一(分割 1)已经是纯的——它只有来自类 0 的样本。因此,我们可以说,这个分支已经达到了最佳点,我们不需要再分裂它了。
我们使用来自 Split 2 的数据来选择进一步的阈值—
来自分割 2 的数据
如果我们遵循对特征的唯一值进行排序并在连续值的平均值处选择阈值的策略,我们将最终得到 X1 的阈值为 6.5、7.5 和 8.5,而 X2 的阈值为 3.5、5 和 7.5。
快速目测显示,对于这两个变量,前三次分割都是次优的。最佳分割在 X1=8.5。
这使我们回到了选择最佳策略来选择阈值的问题上。
现在我们已经看到了最佳分割是如何计算的,并且考虑到我们一次只查看一个特征,我们可以说理想的阈值候选位于类别之间的过渡点即当特征被排序时,只有那些目标从 0 变化到 1 或相反的点才是好的决策点,因为任何其他位置都意味着从相同类别中分割点,这将导致我们两个分割中杂质的增加。
因此,我们可以看到,当选择 X1 的阈值时,我们的理想候选值是 8.5,因为当从 X1=6 移动到 X1=7 或从 7 移动到 8 时,目标值(1)没有变化。
因此,我们在 X1=8.5 — 的增益
同样,对于 X2 ,我们唯一的阈值候选是 X2=6 ,给我们一个 0.32 的等价增益值。
这给了我们在 X1=8.5 的第二个决策节点,使我们的决策树呈现为—
因为我们所有的节点都是纯的,所以不需要进一步的分裂。
因此,我们的终端节点是具有类 0 的
Split-1、具有类 1 的
Split-3 和具有类 0 的
Split-4。
我们可以通过遍历树来预测新变量的类别,并评估它属于哪个节点。
让我们通过测试我们在训练中没有使用的两行数据来验证我们的决策树的准确性
对于第一个数据点(3,5),由于 X1 小于 5.5,该点以 Split-1 结束,预测类别为 0;与我们实际的目标类相同。
类似地,对于另一个端点,我们看到样本以 Split 3 结束,预测值为 1,这与实际的类相匹配。
仅此而已。在其核心,这就是决策树算法所做的一切。
这真的是决策树所做的全部吗?
“在引擎盖下”是本系列的焦点,我们一次一个节点地查看决策树构建的基础,以适应我们的数据。
虽然这确实是决策树的核心功能,但是有一些复杂的地方我已经跳过了,比如—
- 为什么基尼不纯?
- 还有哪些其他杂质指标可用于选择最佳分离度?
- 决策树过度拟合数据的趋势。
- 特征重要性——我们可以看到,我们的两个决策点都只依赖于变量 X1 而不是 X2。这是否意味着我们可以抛弃 X2?
- 修剪和聚集节点。
- 预测概率是如何计算的?
我将在一个平行系列中介绍这些概念,重点是我们在这个系列中介绍的不同 ML 算法的各种复杂性。
下一步是什么?
在本系列的下一篇文章中,我们将继续讨论决策树,并在决策树的引擎盖下寻找回归,并了解如何使用决策树进行拟合,并输出连续的目标值。
幕后——逻辑回归
理解逻辑回归背后的数学原理,手动创建分类模型
乔尔·罗兰在 Unsplash 上拍摄的照片
这是一系列文章中的第二篇,在这一系列文章中,我们将使用各种 ML 算法的基本数学方程来理解它们的“幕后”工作。
有这么多优化的实现,我们有时太关注库和它提供的抽象,而太少关注进入模型的底层计算。理解这些计算往往是一个好模型和一个伟大模型的区别。
在本系列中,我将重点放在手工实现算法上,以理解其背后的数学原理,这将有望帮助我们训练和部署更好的模型。
注意——本系列假设您了解机器学习的基础知识以及我们为什么需要它。如果没有,请阅读本文来了解我们为什么以及如何利用 ML。
逻辑回归
逻辑回归建立在线性回归的概念上,其中模型产生一个将输入特征(X)与目标变量(Y)相关联的线性方程。
逻辑回归算法的两个主要区别特征是—
- 目标变量是离散值(0 或 1 ),与连续值不同,线性回归在计算线性方程的输出后增加了一个额外的步骤来获得离散值。
- 该模型建立的方程侧重于分离 target 的各种离散值,试图确定一条线,使所有 1 都落在该线的一侧,所有 0 都落在另一侧。
考虑以下数据,具有两个输入特征— X1,X2 ,和一个二进制(0/1)目标特征— Y
逻辑回归将试图找到参数 w1 、 w2 和 b 的最佳值,因此—
这里,函数 H ,也称为激活函数,将 y 的连续输出值转换为离散值。这将确保等式能够输出类似于输入数据的 1 或 0。
该算法通过以下步骤找到这些最佳值—
0.包含二进制(0,1)目标的样本数据
- 将随机值分配给 w1 、 w2 和 b.
将随机值分配给 w1、w2 和 b
2.选取数据的一个实例,并计算连续输出(z)。
选择一个实例并计算 z
3.使用激活功能 H()计算离散输出(ŷ)。
使用 H() 计算ŷ
4.计算损失——当实际目标是 1 时,我们的假设是否让我们接近 1?
计算损失
5.计算 w1 、 w2 和 b 的斜率——我们应该如何改变参数以更接近实际输出?
6.更新 w1 、 w2 和 b 。
计算梯度并更新参数
7.重复步骤 2–6,直到收敛。
用另一个实例重复
让我们详细看一下这些步骤。
1。给 w1 、 w2 和 b 分配随机值
我们从给模型参数分配随机值开始—
2.选取一个数据实例,计算连续输出(z)
让我们从数据的第一行开始——
输入我们假设的参数值,我们得到—
3.使用激活函数 H() — 计算离散输出( ŷ )
如果您一直在关注“幕后”系列,您会注意到步骤 1 和 2 与线性回归中的步骤完全相同。这一点会经常出现,因为线性回归形成了大多数算法的基础——从简单的 ML 算法到神经网络。
都是 Y = MX + c 供电
有了我们的基础,我们现在关注的是什么使逻辑回归不同于线性回归,这就是“激活函数”的作用。
激活函数在具有连续目标值的线性世界和“逻辑”世界之间架起了一座桥梁(就像我们现在工作的这个世界!)具有离散的目标值。
激活函数的简单形式是阈值函数(也称为“阶跃函数”)—
但是,和所有 ML 的事情一样,“不可能那么简单”。
像这样的简单阈值函数的一个主要缺陷是,每次我们建立分类模型时,我们必须手动选择正确的阈值(基于输出的范围)。根据输入变量和权重,我们的值可以有任意范围。
另一个不利于这样一个简单函数的事实是,它在 z=threshold 处不可微。我们需要遵循损失的管道- > 梯度 - >权重更新,因此我们需要将这里的事情变得复杂一点,这样当我们计算损失的导数(梯度)时,我们的生活就简单了。
这就是稍微修改过的阈值版本 Sigmoid 函数——
就像阈值处理一样,sigmoid 函数将实数转换为 0 和 1 之间的值,中点在 z=0 处(这样就解决了我们的第一个问题)。从图中可以明显看出,该函数在所有点上都是平滑的,这在计算梯度时将是有益的(从而解决了我们的第二个问题)。
sigmoid 函数的另一个优点是,它告诉我们估计值有多接近 0 或 1。这有助于我们更好地理解模型损失(错误)—对于实际值为 1 的行,预测值为 0.9 比预测值为 0.7 要好。当使用阶跃函数时,这种复杂性就消失了。
让我们使用 sigmoid 函数来计算我们的估计输出—
4.计算损耗
基于线性回归,我们可以选择平方误差来表示我们的损失,但这总是会使误差变小,因为我们的值在 0-1 范围内。我们需要一个函数,当我们的假设提供一个接近于 0 的值,而实际是 1 时,输出一个大的损失,反之亦然。
一个这样的函数是 log-loss,它使用我们的预测输出(z)的对数变换来计算损失。
我们将损失函数定义为—
当输入接近 0 时,对数函数的输出接近负无穷大,当输入为 1 时,输出为 0。负号反转对数值,以确保我们的损失位于 0 到无穷大之间。
我们可以把这个函数写成一个方程—
我们现在可以计算我们的假设给我们带来的误差—
5.计算 w1 、 w2 和b的斜率
现在,我们通过计算每个参数对损耗的梯度来计算每个参数对预测输出(和损耗)的影响。
这就是管道中的微分函数使我们的生活变得简单的地方,为我们提供了每个参数的导数
6.更新 w1 、 w2 和b
梯度告诉我们,我们应该改变多少参数假设,以减少损失,并使我们的预测输出更接近实际输出。
因为我们一次“训练”一个实例的模型,所以我们希望限制这个单独实例的损失对我们的参数的影响。因此,在更新参数之前,我们使用“学习率(η)”—
这完成了训练的一个迭代。
为了获得最佳参数值,我们重复上述步骤固定次数,或者直到我们的损失停止减少*,即*收敛。
让我们通过另一个迭代,使用更新的参数—
看来这次我们的损失增加了。这显示了二进制实例如何将权重拉向相反的方向,以在两个类之间生成决策边界。
重复这些步骤,再进行几次迭代*,从前 4 行中随机采样数据,我们得到最佳权重如下—
对我们最后一行数据(我们在训练时没有使用)使用这些值,我们得到—
这非常接近我们的实际类“1”。
选择截止值为 0.5(我们的 sigmoid 曲线的中点),对于这种情况,我们的预测值为 1。
仅此而已。在其核心,这是所有的逻辑回归算法。
这真的是逻辑回归所做的“全部”吗?
“引擎盖下”是本系列的焦点,我们看了看逻辑回归的基础,一次取一个样本,并更新我们的参数以适应数据。
虽然这确实是逻辑回归的核心,但是一个好的逻辑回归模型还有很多其他的东西,比如
1.正规化——L1 和 L2
2.学习率调度
3.为什么选择乙状结肠活化?
4.缩放和标准化变量
5.多类分类
我将在一个平行系列中介绍这些概念,重点是我们在这个系列中介绍的不同 ML 算法的各种复杂性。
下一步是什么?
在本系列的下一篇文章中,我们将继续分类任务,并了解另一类算法— 决策树 ,,它们的工作方式非常类似于您和我如何使用推理来得出关于数据的结论。
*经过 5 次迭代,这是我们的损失、梯度和参数值的变化方式—
** 在本文中,我还没有介绍我们如何获得损失函数相对于参数的导数,因为推导过程非常广泛,需要单独撰写一篇文章(将在另一篇文章中介绍:)
在深度学习的掩护下
理解神经网络的循序渐进教程
我用 Alexlenail 网站创建了这张图片
上图是深度神经网络的简单架构。这篇文章的目标是了解深度学习的细节并建立自己的网络,而不是将现有的模型用作黑箱!
准备输入数据
在这篇文章中,我们将介绍一个简单的神经网络,它可以学习识别手写数字(MNIST 数据集)。目前,有各种类型的神经网络,但为了简单起见,我们将从香草形式开始(又名*“多层感知器”*)。
请注意,之前图表中的圆圈称为神经元。每个神经元包含一个范围在 0 和 1 之间的数字,这就是所谓的“激活”。另外,请注意这些神经元之间的连线称为权重。每个权重是一个随机生成的数字,其值介于-1 和 1 之间。
MNIST 数据集中的每幅图像仅由黑白颜色组成,尺寸为(28 x 28 = 784 像素)。如果你仍然想知道为什么神经元激活的范围在 0 和 1 之间,那是因为它保持了图像的灰度,其中 0 是完全黑色的,1 是完全白色的。最后,要将图像输入到我们的神经网络中,我们需要通过获取每行像素并将其附加到上一行像素来将其转换为像素向量,请看以下示例:
图层说明
如上所述,网络的输入将是被转换成像素矢量(确切地说是 784)的图像,这是我们的神经网络的第一层。
输出层神经元的数量应该与我们试图预测的类的数量相关联。在我们的例子中,类的数量是 10 (0,1,2,…,9)。因此,最后一层(输出层)应该由 10 个神经元组成。
隐藏层是中间的层。现在,我们姑且说我们随机选择了隐藏层的数量和神经元的数量。
目标是什么?
我们希望建立一个模型,能够预测输入图像的数字(图像分类)。但是,很难展示整个网络的逐步示例。因此,我们将从一个更简单的神经网络开始,它由三个输入节点、一个具有单个神经元的单个隐藏层和一个单个输出组成。我们的网络将会是这样的
正向传播
前向传播是从输入开始,经过神经网络及其计算,并以做出单个预测结束的过程(用 y^ 表示,读作 y-hat)。让我们从下面的向量[0.6,0.4,0.2]开始,实际输出(y)是 1。
这些数字都是从哪里来的?好了,输入节点已经给定, Z 是输入激活与权重相乘并相加的结果, y^ 是 Z 与其权重相乘的结果。最后,也给出了 y,这是期望的输出。通常,为了计算任何输出,我们将激活与权重相乘,并将它们相加。这是第一层的公式。
很明显,我们在所有的输入和权重上重复相同的乘法。因此,我们只计算总和,因为我们可能有 3 个以上的输入。
偏见
每一个神经元都会在某个时候发光。当一个神经元触发时,意味着这个神经元检测到图像中的特定特征。例如,每当一个数字为 7 的图像进入网络,几乎相同的神经元就会激活并放电(这意味着它们触发了类似的事件、类似的角度等。).然而,当激活大于 0 时,你不希望每个神经元都触发(否则所有的正激活将保持触发)。你想让它们在某个阈值后启动,比如 10,这就是所谓的偏差( b )。我们将 b 加到求和中,以控制神经元何时触发。
激活功能
到目前为止,我们的方程将产生良好的结果。但是,结果可能小于 0 或大于 1。如前所述,每次激活应该在 0 到 1 的范围内,因为这是每个图像的灰度。因此,我们需要一个函数 (f) 将 y ^的结果挤压在 0 和 1 之间。这个函数被称为激活函数,特别是 Sigmoid 函数。在 y^上调用 sigmoid将会以如下结果结束
或者你可以这样写
注:我将左侧尺寸命名为 (Z) ,这是应用激活函数后隐藏层输出的约定名称。另外,请注意,我调用了 f 而不是 Sigmoid,因为我们有许多不同的激活函数可以应用。以下是常用激活功能的列表:
- Sigmoid: 是一个在 0 和 1 之间传递输出的函数,它大量用于概率,因为这是概率的范围。
图片来自 WolframMathWorld
- **双曲正切:**在某种程度上类似于 Sigmoid,范围从-1 到 1。
图片由 WolframMathWorld 提供
- 整流线性单元(RELU): RELU 是最常用的激活功能。它的范围是 0 到无穷大。如果输入为负,RELU 返回 0,否则返回实际输入: max (0,x)
图片由刘丹青 —中:拍摄
- Softmax: 是一个独特的激活函数,它采用一个由 k 个数字组成的向量,并将其归一化为 k 个概率。换句话说,它不是选择单个输出类,而是列出每个类的概率。
还有许多其他的激活函数,如漏 RELU,参数 RELU,SQNL,ArcTan 等。
计算损失
损失是什么?简单来说,损失就是模型离预测的正确答案有多远。如前所述,输出为 1,而预测输出为 0.3,因此本例中的损耗为 0.7。如果模型预测完美,那么损失为 0。因此,可以使用以下公式计算损耗( L 表示损耗)
就这样吗?基本上,是的,但有几件事有助于改善损失,对我们未来更有利:
- 绝对值:损失是模型预测与输出之间的差距。考虑有两个错误。第一个错误是 100,第二个错误是-100。将这些误差相加并求平均值得到 0,这意味着你的预测是 100%正确的,而不是。因此,我们只对正误差感兴趣。
- 平方值:考虑有两个误差(一个小误差和一个大误差)。你会更关注哪个错误?当然是更大的误差!因为更影响结果。因此,计算损失的平方值有助于我们摆脱符号,让大误差变大,小误差变小。考虑误差为 0.01 和 100。将这些误差平方,我们得到 0.0001 和 10000。区分这些错误的优先级对于了解是什么导致了错误的预测非常重要。
- 总结:在我们之前的例子中,我们计算了预测和输出之间的损失,但是如果我们有几个输出神经元呢?因此,我们计算神经网络中所有损失值之间的总和。(D 表示包含许多示例的数据集)。
- 平均值:数据集中样本的平均值。在我们的例子中,我们只有一个例子。但是现在,我们需要用它除以 d 中的例数(N)。
这个损失函数被称为均方差(MSE) ,它是最常用的损失函数之一。还有许多其他损失函数,如交叉熵、铰链、MAE 等。但是你想过成本函数是什么吗?损失函数和成本函数的区别是什么?区别在于,损失函数用于单个训练示例,而成本函数是整个训练数据集的平均损失。
恭喜你!我们完成了正向传播。但是,我们刚刚做出的预测可能不是很准确(考虑输出 1,但模型预测 0.7)。怎样才能做出更好的预测?我们不能改变输入值,但是我们可以改变权重!!维奥拉。现在你知道深度学习的秘密了。
反向传播
我们不能改变输入。然而,我们可以增加权重,然后将其乘以输入将得到更大的预测输出,比如 0.8。不断重复这个过程(调整权重),会得到更好的结果。向相反的方向改变权重称为反向传播!但是具体怎么做呢?这可以通过优化算法来实现。有梯度下降、随机梯度下降、Adam 优化器等不同的优化器。
梯度下降
梯度下降是一种优化算法,旨在通过调整权重来减少损失。当然,手动改变权重是不可能的(我们在单个神经网络中有几十甚至几百个权重)。那么,我们怎样才能使这个过程自动化呢?以及如何告诉函数调整哪个权重,何时停止?
让我们开始调整权重,检查它如何影响损失,并绘制所有结果(检查下图)。正如你所看到的,在一个特定的点(红线)是最小的损失。在红线的左边,我们必须增加重量来减少损失,而在红线的右边,我们显然需要减少重量来减少损失。主要的问题仍然是:我们如何知道一个给定点是在红线的左边还是右边(为了知道我们应该增加还是减少重量)?以及我们应该增加或减少多少重量才能更接近最小损失?一旦我们回答了这个问题,那么我们就可以减少损失,获得更好的准确性。
请注意,在简单的 2D 维度中,很容易快速到达最小点。然而,大多数深度学习模型处理的是高维度。
幸运的是,数学上有一种方法可以回答这些问题,导数(你在高中忽略的东西:)。利用导数,我们可以用损失对重量的导数来计算图上切线的瞬时变化率。
如果你不熟悉导数,前面的句子听起来像是胡言乱语,那么就把它想象成一种测量在特定点接触图形的直线的斜率和方向的方法。根据给定点的倾斜方向,我们可以知道该点是在红线的左边还是右边。
在上图中,您可以看到 1 号切线的右侧向上,这意味着它是一个正斜率。而其余的线指向下方,负斜率。此外,请注意,斜率编号 2 的梯度大于斜率编号 6 的梯度。这就是我们如何知道我们需要多少来更新权重。梯度越大,该点离极小点越远。
学习率
将点定位在红线的左侧或右侧后,我们需要增加/减少权重,以减少损失。然而,让我们说对于红线左边的一个给定点,我们应该增加多少重量?请注意,如果您大幅增加重量,那么该点可能会将最小损失传递给另一方。然后你要减轻体重,等等。随机调整权重是没有效率的。相反,我们添加了一个称为“学习率”的变量,用“η”表示,以控制如何调整权重。一般你都是从小的学习率开始,避免过了最小损失。为什么叫学习率?那么,减少损失和做出更好预测的过程基本上就是模型学习的时候。在那时,学习率是控制模型学习速度的因素。
调整重量
最后,我们将斜率乘以学习率,并从旧的权重中减去该值,以获得新的权重。看看下面的等式,你会更好地理解它
随机梯度下降
梯度下降使用整个数据集来计算梯度,而 SGD 在每次迭代中使用训练数据集的单个示例。SGD 通常比批量或标准梯度下降更快地达到收敛。批量梯度下降每次迭代使用一批训练样本。
过度拟合
当一个神经网络很深的时候,它有太多的权重和偏差。当这种情况发生时,神经网络往往会过度拟合它们的训练数据。换句话说,模型对于特定的分类任务将是如此精确,而不是一般化。该模型在训练数据中得分较高,而在测试数据中得分较低。退学是解决办法之一。
拒绝传统社会的人
避免过度拟合的一个简单而有效的方法是使用 dropout。对于每一层,都有一个脱失率,这意味着与该脱失率相关的神经元数量被停用。这些神经元将被随机选择,并在特定的迭代过程中被关闭。下一次迭代,另一组随机选取的神经元将被停用,以此类推。这有助于概括模型,而不是记住具体的特征。
我希望这篇文章是有帮助的。如果您有任何问题,请告诉我!
资源
线性回归,岭回归,主成分分析有什么联系?
引擎盖下
从保存病态回归问题到启用正则化路径的快速计算,这是引擎盖下的链接。
我正在写一个新的系列(相对较短)帖子,围绕统计学习中的基础话题。特别是,这一系列将会有意想不到的发现,很少被谈论的联系,和统计学习的幕后概念。
我的第一篇文章从岭正则化开始,数据科学中的一个基本概念。**普通最小二乘(OLS)估计、岭估计和 PCA 之间简单而优雅的关系可以通过光谱分解的透镜找到。**我们通过多元分析的练习 8.8.1 看到了这些关系。
这篇文章改编自我的一篇博文,省略了所有的证明。如果你喜欢 LaTex 格式的数学和 HTML 风格的页面,你可以在我的博客上阅读这篇文章。
设置
给定以下回归模型:
考虑一下 **XX** (在本文中,
和上标 T 都表示转置)的列已经标准化为均值为 0,方差为 1。那么的岭估计为
其中对于给定的 X ,λ≥0 为小的固定脊正则化参数。注意,当λ=0 时,它只是 OLS 公式。另外,考虑 var-cov 矩阵的谱分解 XX** = **GLG
。设 W = XG 为原始数据矩阵的主成分变换。
结果 1.1
如果α*=g`β表示主分量的参数向量,那么我们可以表明,通过简单地用岭正则化参数对它们进行缩放,岭估计 α* 可以从 OLS 估计 hat( α )中获得:*
这个结果向我们展示了两个重要的见解:
- 对于 PC 转换的数据,我们可以通过 OLS 估计值的简单元素式缩放来获得岭估计值。
- 脊正则化的收缩效果取决于λ和相应 PC 的特征值:
-较大的λ对应于每个参数的较大收缩。
——然而,给定相同的λ,对应于较大特征值的主成分得到最小的收缩。
形象化
为了演示这两种收缩效应,我绘制了收缩百分比(1-hat(α)与有序主分量以及脊正则化参数值的函数关系。从该图中可以清楚地看到两种收缩效应。
结果 1.2
从结果 1.1 可以得出,通过 var-cov 矩阵的谱分解,我们可以在 OLS 估计 hat(【β】)和岭估计**【β***之间建立直接联系(因此有了本文的标题)。具体来说,我们有
结果 1.3
估计量**【β*】**质量的一个度量是跟踪均方误差(MSE):
现在,从前面的两个结果可以看出,岭估计的迹 MSE 可以分解为两部分:方差和偏差,并得到它们的显式公式。MSE 的精确公式的可用性允许诸如正则化路径之类的东西被容易地计算。具体来说,我们有
其中第一个分量是方差之和:
第二部分是偏差平方和:
结果 1.4
这是从结果 1.3 得出的一个快速但有启发性的结果。对轨迹 MSE 函数对λ取偏导数,并取λ=0,我们得到
请注意,当λ为 0 时,跟踪 MSE 函数的梯度为负。这告诉我们两件事:
- 我们可以通过采用非零λ值来降低轨迹 MSE。特别是,我们用一点偏差换取方差的减少,因为方差函数在λ中单调递减。然而,我们需要在方差和偏差之间找到正确的平衡,以便使总的跟踪 MSE 最小。
- 当一些特征值(l_i)较小时,通过脊正则化的道 MSE 的减少较高。也就是说,当预测量之间存在相当大的共线性时,岭正则化可以实现比 OLS 小得多的迹 MSE。
形象化
使用sklearn.metrics.make_regression
函数,我生成了一个包含 50 个样本和 10 个特征的噪声回归数据集。特别是,我要求大多数方差(在 PCA 意义上)只能由这 10 个特征中的 5 个来解释,即最后 5 个特征值相对较小。这是正则化路径和系数误差图。
从图中,我们可以清楚地看到:
- 增加λ会使每个系数向 0 收缩。
- OLS 过程(两个图的左侧)产生错误的(并且具有大的方差)估计。估计量 MSE 明显大于岭回归的 MSE。
- 在岭估计系数的均方误差最小的 1 附近发现一个最佳λ。
- 另一方面,大于和小于 1 的λ 值是次优的,因为在这种情况下它们会导致过度正则化和欠正则化。
绘制该图的脚本附后。
摘要
通过光谱分解的镜头,我们看到线性回归估计、岭回归估计和 PCA 之间有一个简单而优雅的联系。特别是:
- 结果 1.2 表明,当你有数据 var-cov 矩阵的谱分解和通常的线性回归估计时,你可以通过简单的矩阵计算获得每个岭正则化参数值的岭估计。我们刚刚避免了很多矩阵求逆!
- 我们对岭估计的迹 MSE 进行了偏差-方差分解。很明显,我们总是可以通过增加正则化强度来减小估计量的方差。
- 虽然轨迹 MSE 函数在λ=0 处的梯度是负的,但是我们上面的可视化表明λ的值需要仔细选择,因为它可能导致过度正则化或欠正则化。
感谢您阅读本文!如果你有兴趣了解更多关于统计学习或数据科学的知识,你可以看看我下面的其他文章。尽情享受吧!
直觉、插图和数学:它如何不仅仅是一个降维工具,为什么它在现实世界中如此强大…
towardsdatascience.com](/linear-discriminant-analysis-explained-f88be6c1e00b)*
低估了 Tableau 的组合功能—点、线串和多边形映射
Tableau 中的地理空间分析-无需地理编码/底图服务即可工作+在单一设置中渲染混合几何类型
完成了我的一个空间分析项目后,我已经准备好在 Tableau 中展示我的发现,但是面临着一个非常规的问题,那就是完全没有互联网接入。由于我们大多数人通常认为 Tableau 中内置的地理编码和底图服务是理所当然的,这比我最初预期的要复杂得多。
作者提供的图片|由空间数据文件以 Tableau 形式呈现的地图|按规划区域展示新加坡公民(65 岁及以上)的分布情况
基本上,当互联网不可用时,上面的地图会变成这样
按作者分类的图像|在没有互联网连接的情况下由空间数据文件以 Tableau 形式呈现的地图-所有地理编码和底图服务均不可用。
哎呀…显然,这有一些毁灭性的影响。更糟糕的是,在没有内置地图功能的情况下,渲染空间数据的深入教程少得可怜。所以我开始自己做一些探索,主要是,我必须解决一些问题来完成我的项目:
- 确保新加坡的地图多边形按比例渲染和缩放。
- 显示缺失的底图详细信息-高速公路、街道名称等。因为它们不再自动显示在背景中。
解决问题 1 —绘制地图多边形并渲染其实际比例
基于上述背景,我必须处理的只是 GeoJSON 格式的空间数据文件(出于本教程的考虑,我已将一个示例上传到 GitHub 的SG _ planning _ areas _ 2020 _ eldery _ distribution . geo JSON)并继续研究它。
从根本上来说, GeoJSON FeatureCollection 对象是一个 JSON 对象,它包含所有必要的坐标(纬度和经度)以将其几何形状渲染到任何地图上。因此,我决定利用这些信息,用“多边形”代替“地图”来制作几何图形。这需要一种 Tableau 容易理解的数据格式,在这种情况下,我选定的格式是 CSV 文件:
Image by Author |要在 Tableau 中使用“多边形”而不是“地图”渲染几何图形,必须首先将 GeoJSON 数据转换为 CSV 格式。
幸运的是,我在https://tableau-data-utility.onrender.com/创建了一个工具来加速这个过程。
作者图片| GeoJSON 文件上传到 https://tableau-data-utility.onrender.com/生成 CSV 输出文件|“导出 CSV 输出”然后选择保存 CSV 文件
基本上,这是一个基于浏览器的应用工具,我创建了所有的指令和实施所需的信息。我现在需要做的就是上传上面的 GeoJSON 文件,通过选择“生成 CSV 输出”导出转换后的输出,并使用“多边形”在 Tableau 中渲染它:
按作者分类的图像 CSV 文件中生成的字段用于在 Tableau 中渲染地图多边形-不依赖于任何地理编码服务
然后奇迹发生了!问题 1 已解决-地图多边形现在以正确的比例渲染,无需地理编码服务。
图片作者| ( 左)用“地图”渲染的地图多边形| ( 右)用“多边形”渲染的地图多边形
然而,随着问题 1 的解决,第二个问题很快取代了它——这就是缺少底图的**,将我们直接引向问题 2。**
解决问题 2-绘制在 Tableau 中渲染的空间数据的底图图层
乍看之下,这似乎不是什么大事,但当我受命调查和优化岛上的各种交通路线时,这很快就成了问题:
作者提供的图片|展示了从出发地到目的地的各种交通路线|以上示例展示了在新加坡驾车从中巴鲁市场到 Vivo City 的两条可能路线
显然,虽然国家/州/省的轮廓可以很容易地从其地图多边形中推断出来,但由(多)点和(多)线串表示的交通路线在没有如上图所示的详细地图图层的情况下并不明显。用于渲染上述的样本数据可以在我的 GitHub 上找到:SG _ tiong _ Bahru _ mkt _ to _ vivo _ city . geo JSON。使用相同的工具,GeoJSON 文件现已转换为 CSV 格式(SG _ tiong _ Bahru _ mkt _ to _ vivo _ city . CSV)。
作者图片|我在https://tableau-data-utility.onrender.com/部署的工具的截图,该工具用于从用户上传的 JSON 文件中为 Tableau 输出 CSV 文件
因此,这为我们带来了一个简单但非常有用和方便的应用程序功能——导出 Tableau 的背景地图图像的能力:
作者图片|展示了网络应用将地图导出为背景图片的功能
这里需要注意几个特殊的子功能:
- 可更改底图选择-默认情况下,使用 OpenStreetMap (OSM)的底图。但是,用户可以通过输入有效的底图 URL 来更改底图,如下所示:
作者提供的图片| ( 左)使用默认底图| ( 右)使用备选地图服务,即 OneMap —请注意,用户的输入必须与小册子兼容— XYZ 底图切片服务又称滑动地图
2.地图边界的自动显示和更新 —在地图的正下方,有一个坐标表,当地图被缩放或移动时,它会自动更新。
按作者分类的图像|将背景图像绘制为底图时,这些坐标对应于 Tableau 中的精确坐标
因此,要为运输路线导出适当的地图图像,必须确保在地图查看端口中捕获的地图的缩放级别** l 和边界都包括您的几何形状的所需边界。这最终将我们引向:**
作者图片|导出的地图图像和渲染的坐标直接输入到 Tableau 中
图片由作者提供| ( 左侧)不带底图的路线| ( 右侧)带有导入的地图图像的路线|清楚地显示了地图图层的细节,上面的起点-目的地路线向查看者显示了哪些道路、街道、建筑物、设施等。就在附近,为进一步的空间分析提供了更多的背景
因此,问题 2(缺少底图详细信息)现已解决。
最后,虽然我可以在这里结束这篇文章,但我也想分享我从这里得到的另一个启示— 通过 Tableau 中标准功能的组合绘制混合几何类型(“多边形”和“直线”)。
额外的发现—从单个 CSV 文件在 Tableau 中绘制混合几何图形类型
尽管 Tableau 声称有局限性
我对 Tableau 已经存在的功能的探索表明情况并非如此。Tableau 已经具有渲染混合几何类型的固有功能,我将使用这个空间文件来证明这一点,该文件是通过合并之前的两个 GeoJSON 文件创建的(实际的文件在我的 GitHub 中)。重复相同的步骤— GeoJSON 被转换为 CSV 输出,底图图像通过 https://tableau-data-utility.onrender.com/的工具导出,这为我们提供了:
作者提供的图片|展示 Tableau 中具有“多边形”和“线”功能的混合几何类型的绘图|多边形和交通路线都在同一视图中呈现
需要注意的是,要仔细查看“标记”面板,因为每个必填字段都有专门的后缀**,带有“(点+线)”或“(多边形)”。这是为了区分哪些字段应该由**“线”——即(点+线)渲染,哪些字段应该由“多边形”——即(多边形)** (是的,我部署的工具确实基于几何类型生成字段)。**
简而言之,我试图通过其他方式在 Tableau 中呈现空间数据集,这向我展示了使用现有的多种功能进一步可视化的可能性。希望你们觉得这很有用,可以在 https://tableau-data-utility.onrender.com/使用这个工具来生成你们需要的数据集!
我之前发表的关于渲染网络图数据集的教程使用了部署在https://tableau-data-utility.onrender.com/的工具。如果你想在 Tableau 中用最少的努力来绘制网络图,请查看它。感谢阅读!
获得李思欣·崔和其他作家在媒体上的所有帖子!😃您的会员费直接…
geek-cc.medium.com](https://geek-cc.medium.com/membership)**
被低估的机器学习算法——APRIORI
使用 Python 从头开始构建分步模块
先验导论
Apriori 是一种用于关联规则挖掘的算法。它在数据集中搜索一系列频繁出现的项目集。它建立在项目集之间的关联和相关性上。这是你在推荐平台上常见的“你可能也会喜欢”背后的算法。
什么是关联规则挖掘?
关联规则挖掘是数据科学中的重要技术之一。在 ARM 中,在项目集中识别数据集中模式和关联的频率,然后用于预测集合中的下一个相关项目。这种 ARM 技术主要用于根据客户购买情况做出商业决策。
例如:在沃尔玛,如果 Ashok 购买牛奶和面包,他购买黄油的机会是通过关联规则挖掘技术预测的。
在本模块中,我们将在给定的数据集上从头开始构建一个 apriori 算法,并将其用作客户购买的推荐系统。
有些定义需要记住
在我们开始之前,先看一下下面解释的一些术语。
SUPPORT _ COUNT 项目集出现的事务数。
MINIMUM _ SUPPORT _ COUNT 数据集中项目集的最小频率。
CANDIDATE_SET —数据集中每个项目的 C(k) support_count。
ITEM _ SET-L(k)将候选集支持计数中的每个项目与 minimum_support_count 进行比较,并过滤出不频繁项目集。
支持-数据库中的事务百分比遵循规则。
Support(A->B) = Support_count(A U B)
信心——购买了 A 的顾客也购买了 b 的顾客的百分比
Confidence(A->B) = [Support_count(AUB)/Support_count(A)]*100
获取数据
在这个实验中,我们考虑了来自 Kaggle 的名为杂货店数据集的数据集。它由一家超市的 20 笔一般商品交易组成。这个数据集更容易理解模式和关联。
在这里,我们可以看到 20 组食品杂货交易,即牛奶、面包、饼干、玉米片、茶、BOURNVITA、果酱、MAGGI、咖啡、公鸡和糖。
尽管这个数据集很小,但我们不需要生成参数数据集。这足以开发 Apriori 算法。
准备数据
需要处理这些数据以生成记录和项目列表。将 minimum_support_count 视为 2。Pandas 库用于导入 CSV 文件。
先决条件:PYTHON 中级
Geeksforgeeks: Apriori 算法(基于理论)
import pandas as pd
import itertoolsdata = pd.read_csv('GroceryStoreDataSet.csv')minimum_support_count = 2
records = []
for i in range(0, 20):
records.append([str(data.values[i,j]) for j in range(0, 4)])items = sorted([item for sublist in records for item in sublist if item != 'nan'])
构建算法
让我们在构建算法之前修改 Apriori 的关键属性,
频繁项目集的所有子集必须是频繁的。如果一个项目集是不频繁的,那么它的所有超集都是不频繁的。
第一步
在阶段 1 中,通过测量数据集中每个项目的 support_count 来生成候选集 C1。通过比较 C1 支持计数和最小支持计数,生成项目集 L1。这里 k=1。
def stage_1(items, minimum_support_count):
c1 = {i:items.count(i) for i in items}
l1 = {}
for key, value in c1.items():
if value >= minimum_support_count:
l1[key] = value
return c1, l1c1, l1 = stage_1(items, minimum_support_count)
步骤 1 的结果是项目集 L1,
项目集 L1
在这种情况下,candidate_set 中不存在最小支持计数的低频率项目。因此,候选人集 C1 =项目集 L1。
第二步
在此阶段,使用上一步中的项目集 L1 生成候选集 C2。检查项目集中的所有子集是否是频繁的,如果不是,从列表中删除相应的项目集。项目集 L2 是通过将候选集 C2 与最小值支持计数进行比较而生成的。这里 k=2。
def stage_2(l1, records, minimum_support_count):
l1 = sorted(list(l1.keys()))
L1 = list(itertools.combinations(l1, 2))
c2 = {}
l2 = {}
for iter1 in L1:
count = 0
for iter2 in records:
if sublist(iter1, iter2):
count+=1
c2[iter1] = count
for key, value in c2.items():
if value >= minimum_support_count:
if check_subset_frequency(key, l1, 1):
l2[key] = value
return c2, l2c2, l2 = stage_2(l1, records, minimum_support_count)
为了检查一个项集的子集是否频繁,我们应该传递当前阶段的项集、前一阶段的项集(在本例中是 L1)和 k-1。
def check_subset_frequency(itemset, l, n):
if n>1:
subsets = list(itertools.combinations(itemset, n))
else:
subsets = itemset
for iter1 in subsets:
if not iter1 in l:
return False
return True
步骤 2 的结果是项目集 L2,
项目集 L2
在这种情况下,由于低频率类别,即低于 minimum_support_count,需要消除项目集中的 31 个项目。
第三步
在此阶段,使用上一步中的项目集 L2 生成候选集 C3。检查项目集中的所有子集是否是频繁的,如果不是,从列表中删除相应的项目集。通过将候选集 C3 与最小支持计数进行比较,生成项目集 L3。这里 k=3。
def stage_3(l2, records, minimum_support_count):
l2 = list(l2.keys())
L2 = sorted(list(set([item for t in l2 for item in t])))
L2 = list(itertools.combinations(L2, 3))
c3 = {}
l3 = {}
for iter1 in L2:
count = 0
for iter2 in records:
if sublist(iter1, iter2):
count+=1
c3[iter1] = count
for key, value in c3.items():
if value >= minimum_support_count:
if check_subset_frequency(key, l2, 2):
l3[key] = value
return c3, l3c3, l3 = stage_3(l2, records, minimum_support_count)
步骤 3 的结果是项目集 L3,
项目集 L3
步骤 4
在此阶段,使用上一步中的项目集 L3 生成候选集 C4。检查项目集中的所有子集是否是频繁的,如果不是,从列表中删除相应的项目集。项目集 L4 是通过将候选集 C4 与最小支持计数进行比较而生成的。这里 k=4。
def stage_4(l3, records, minimum_support_count):
l3 = list(l3.keys())
L3 = sorted(list(set([item for t in l3 for item in t])))
L3 = list(itertools.combinations(L3, 4))
c4 = {}
l4 = {}
for iter1 in L3:
count = 0
for iter2 in records:
if sublist(iter1, iter2):
count+=1
c4[iter1] = count
for key, value in c4.items():
if value >= minimum_support_count:
if check_subset_frequency(key, l3, 3):
l4[key] = value
return c4, l4c4, l4 = stage_4(l3, records, minimum_support_count)
步骤 4 的结果是项目集 L4,
项目集 L4
我们可以在这里停下来,因为没有进一步发现频繁子集。
生成关联规则
为了生成数据集的关联规则,我们需要计算每个规则的置信度。
''' Rule generation of itemset ''''''
Confidence:Confidence(A->B)=Support_count(A∪B)/Support_count(A)Confidence((COCK, COFFEE)->CORNFLAKES) = Support_count(‘COCK’, ‘COFFEE’, ‘CORNFLAKES’)/Support_count(‘COCK’, ‘COFFEE’)'''
让我们考虑关联规则的项目集 L3。
sets = []
for iter1 in list(l3.keys()):
subsets = list(itertools.combinations(iter1, 2))
sets.append(subsets)
在 L3 生成的集合上实现关联规则
def support_count(itemset, itemlist):
return itemlist[itemset]list_l3 = list(l3.keys())
for i in range(0, len(list_l3)):
for iter1 in sets[i]:
a = iter1
b = set(list_l3[i]) - set(iter1)
confidence = (support_count(list_l3[i], itemlist)/support_count(iter1, itemlist))*100
print("Confidence{}->{} = ".format(a,b), confidence)
假设最小置信度为 50%
结果
信心(‘饼干’,‘公鸡’)–> { ‘玉米片’ } = 100.0
信心(‘饼干’,‘玉米片’)- > { ‘公鸡’ } = 66.666666666666666
信心(‘饼干’,‘玉米片’)- > { ‘饼干’ } = 100.0
信心(‘饼干’,‘马奇’)- > { ‘茶’ } = 100.0
信心(‘饼干’,‘茶’)-【信心 奶’)- > { ‘饼干’ } = 50.0
信心(‘面包’,‘咖啡’)- > { ‘糖’ } = 66.66666666666666666
信心(‘面包’,‘糖’)- > { ‘咖啡’ } = 50.0
信心(‘咖啡’,‘糖’)- > { ‘面包’ } = 50.0
信心(‘面包’,‘糖’)-【MAGGI)-> 茶’)- > { ‘面包’ } = 100.0
信心(‘面包’,‘茶’)- > {‘BOURNVITA’} = 50.0
信心(‘饼干’,‘公鸡’)- > { ‘咖啡’ } = 100.0
信心(‘饼干’,‘咖啡’)- > { ‘公鸡’ } = 100.0
信心(‘公鸡’,‘咖啡’)- > { ‘饼干’ } = 66.66
结论
这种关联规则挖掘技术是由亚马逊、网飞、谷歌、Flipkart 和 Spotify 等巨头在其推荐平台中实现的。
这种算法也用作大多数销售产品组合的折扣营销技术。
今天,我们已经学习了如何构建 Apriori 算法,并在超市的一般杂货数据集上实现关联规则挖掘。
数据集和全部代码可以在我的 Git 仓库中找到。
理解并在 Python 中构建 FP-Growth 算法
Python 中使用 FP-树和条件 FP-树的频率模式挖掘
卢克·理查森在 Unsplash 上的照片
什么是 FP 增长
FP-growth 是 Apriori 算法的改进版本,广泛用于频繁模式挖掘(也称为关联规则挖掘)。它被用作从数据集中发现频繁模式或关联的分析过程。例如,杂货店交易数据可能有一个频繁模式,即人们通常一起购买薯条和啤酒。Apriori 算法通过生成项目集并发现超过阈值“最小支持计数”的最频繁项目集来产生频繁模式。它通过一个简单的原则极大地减小了数据库中项目集的大小:
如果一个项集是频繁的,那么它的所有子集也一定是频繁的。
然而,Apriori 算法有一个主要的不足。使用 Apriori 需要多次扫描数据库,以检查每个项目和项目集的支持计数。当数据库很大时,这将消耗大量的磁盘 I/O 和计算能力。因此,FP-Growth 算法被创建来克服这个不足。它只扫描数据库两次,并使用一个树结构(FP-tree)来存储所有的信息。根代表空,每个节点代表一个项,而节点的关联是在形成树时保持顺序的项集。FP-tree 简洁,用于直接生成大项目集。一旦 FP-tree 被构建,它就使用递归的分治方法来挖掘频繁项目集。
FP-tree 伪代码及解释
- 第一步:推导出有序的频繁项。对于频率相同的项目,按字母顺序排列。
- 步骤 2:从上面的数据构建 FP 树
- 步骤 3:从上面的 FP-tree 中,为每个项目(或项目集)构建 FP-conditional 树。
- 第四步:确定频繁模式。
让我们看一个如何生成 FP 树的例子。查找支持度≥2 的所有频繁项集。首先,找到支持计数≥ 2 的所有项目。
查找项目频率
在构建 FP 树之前,根据项目频率对事务进行重新排序。当开始构建 FP-tree 时,我们需要创建一个标题表,用一个链表记录所有项目节点的位置。每次向树中添加新节点时,都需要将它链接到具有相同项目的最后一个节点。标题表对于在后面的步骤中构建条件 FP-tree 是必不可少的。
构建 FP 树
现在我们已经扫描了数据库两次,并建立了 FP 树。所有交易信息都包含在树中。剩下要做的唯一事情是递归地构造条件 FP-tree,以找到支持计数大于 2 的所有频繁项目集。使用我们在上一步中创建的标题表,从最少次数到最多次数(E,D,C,B,A)的项目开始。
构建一棵条件 FP 树
最后,我们可以很容易地从那些条件 FP 树{E}、{A,D,E}、{A,E}、{D,E}、{D}、{A,D}、{C,D}、{C}、{A,C}、{B}、{A,B}、{A}中生成所有频繁项集。
生成频繁项目集
Python 实现
下面是一些示例代码,用于从头开始构建 FP-tree,并在 Python 3 中找到所有的频率项集。
总之,FP-tree 仍然是挖掘数据集中完整的频繁模式集的最有效和可伸缩的方法。大多数编程语言包括 Python、R 甚至 Pyspark 都有很好支持的库来生成 FP-tree。
感谢您的阅读,我期待听到您的问题和想法。如果你想了解更多关于数据科学和云计算的知识,可以在 Linkedin 上找我。
杰克·洛里菲茨在 Unsplash 上拍摄的照片
参考
https://en.wikipedia.org/wiki/Apriori_algorithm
https://www . software testing help . com/FP-growth-algorithm-data-mining/
从零开始的逻辑回归
在 NumPy 中从零开始建立逻辑回归。这比你想象的要容易得多!
在许多方面,逻辑回归模型是数据科学家工具箱中最简单的机器学习分类工具之一。理解这些如何直接工作有助于我们理解用于分类任务的深度神经网络。
在这里,我们将讨论模型的数学,如何训练它,以及如何使用 numpy 在 Python 中构建它。
模型背后的数学
让我们首先想象一下,我们有一个像上面这样的数据集,其中黄色点对应于类 0,紫色点对应于类 1。为了预测类标签,我们需要一个模型,它将两个数据维度作为输入,并预测一个介于 0 和 1 之间的数字。考虑这是给定数据和模型参数的标签的概率:ŷ = p( y = 1| X , w )。
逻辑回归模型旨在拟合两个类别之间的直线(或平面/超平面),以便线上的点的概率为 0.5,而远离线的点的概率为 0 或 1,具体取决于它们位于线的哪一侧。
现在我们理解了这个概念,让我们试着稍微形式化一下。
我们的直线/平面/超平面可以表示为:x̂ = w ᵀ x + b 其中 w 是权重的向量,b 是偏差。
“这很好,但我如何获得分配给标签的概率的封闭形式?”,我听到你问。
很棒的问题!
好了,我们现在需要的是一种方法来映射我们的线性模型到一个有效的概率。这意味着该函数必须具有以下属性:
- 在给定连续输入的情况下,它只能输出[0,1]范围内的值
- 它必须是光滑的和可微的(其原因将在后面变得更加清楚)
这就是乙状结肠或逻辑功能发挥作用的地方。sigmoid 函数的图形及其函数形式如下所示。这意味着我们有了一个表示某个点属于某个特定类的概率的表达式。
Sigmoid 激活函数
所以我们整个模型可以表述为:ŷ = σ( w ᵀ x + b)。
训练我们的模型
我们知道模型有能力为两类中的每一类输出一个概率,但是我们如何选择模型的权重( w ,b)?
为此,我们需要为模型定义一个成本函数,并根据模型参数最小化该函数。
为我们的模型选择成本函数是为了最大化指定类别的可能性:
从对数似然到二元交叉熵。x 是数据,w 是可训练的权重,ŷ是模型输出,y 是标签。
这种损失也被称为二进制交叉熵。
好消息是,我们就快成功了!我们现在需要使用梯度下降来优化基于成本函数的参数。这种优化过程是训练网络式机器学习模型的最常见方式。它包括计算成本函数相对于每个模型参数的导数——因此,sigmoid 函数的平滑和可微性非常重要。
对于那些懂一点微积分的人来说,你可能知道这是链式法则的一个应用,但多亏了 Geoff Hinton [1],在机器学习领域,我们称之为反向传播。
成本函数相对于模型参数的梯度。如果你想自己证明一个 sigmoid 函数的导数是σ(1-σ),我推荐这篇文章。
既然我们有了关于模型权重的梯度,我们可以采取小的步骤来降低成本函数。μ是指算法的学习速率。如果学习率太小,模型的训练时间就会太长,并且可能会陷入局部极小值。如果它太大,那么训练过程可能根本不收敛或者它可能收敛到一个差的最小值。
更新模型的权重。I 表示反向传播算法的第 I 次迭代。
如果我们有很多数据,我们可以创建小批量,并为训练数据中的每个批量采取一个步骤。或者,我们可以选择更简单的方法,计算整个数据集的成本函数,并在每个时期采取一个梯度步骤。我们重复计算成本、梯度,然后更新权重的过程,直到成本函数收敛。
从逻辑回归到深度神经网络
正如我们刚刚看到的,逻辑回归将线性模型拟合到数据,并将输出传递给 sigmoid 激活,以预测每个类别的概率。
如果我们想要从输入维度到输出维度进行更复杂的映射,那么我们可以在逻辑回归层之前添加额外的转换。为了训练这些层的权重,我们可以使用相同的反向传播原理来找到这些权重的梯度,并以与我们之前完全相同的方式更新它们。
代码
这是理论,但我们如何把它付诸实践呢?
让我们实现向前传递,用模型进行预测。
import numpy as npdef _sigmoid(x):
return 1/(1 + np.exp(-x))def predict(X,W):
X = np.asarray(X)
if len(X.shape) != 2:
raise ValueError("Shape must be (dims,n_data_points)")
X = np.concatenate([X,np.ones([1,X.shape[-1]])],axis=0) X_hat = np.matmul(W,X)
y_hat = _sigmoid(X_hat)
return y_hat
在实施过程中,有两件事需要特别注意:
首先,我们没有包括单独的偏差,而是做了一个小小的简化,增加了一个额外的权重,并将 1 附加到特征维度的数据上。这使得我们有一个单一的权重向量,而不是两个。对于读者来说,这是一个练习(因为你已经读得太久了),以检查简化后的梯度是否仍然正确。
其次,请注意,在数据点上没有循环,相反,计算是矢量化的,以提高性能。这通常在机器学习中很重要,以便在训练期间可以利用 GPU 的能力,但对于这个更简单的模型来说不是必要的。
现在让我们定义成本函数…
cost = -np.sum(y*np.log(y_hat) + (1-y)*np.log(1-y_hat))
…梯度下降步骤:
dc_dw = -np.sum((y-y_hat)*X,axis=-1)[np.newaxis,:]
self.W = self.W - dc_dw * lr
我们现在可以用一个类中的fit
和predict
方法来模仿 sk-learn 类型的 API。
class LogisticRegressor():
def __init__(self):
self.loss = []
self.W = None
def _sigmoid(self,x):
return 1/(1 + np.exp(-x))def fit(self,X:np.ndarray,y:np.ndarray,epochs: int=100,lr: float=0.01):
self.epochs = epochs
self.lr = lr
X = np.asarray(X)
y = np.asarray(y)
if len(X.shape) != 2:
raise ValueError("Shape must be (dims,n_data_points)")
X = np.concatenate([X,np.ones([1,X.shape[-1]])],axis=0)
dims, n_data_points = X.shape
if self.W is None:
self.W = np.random.randn(1,dims)
for i in range(epochs):
X_hat = np.matmul(self.W,X)
y_hat = self._sigmoid(X_hat)
cost = -np.sum(y*np.log(y_hat) + (1-y)*np.log(1-y_hat))
self.loss.append(cost)
dc_dw = -np.sum((y-y_hat)*X,axis=-1)[np.newaxis,:]
self.W = self.W - dc_dw * self.lr
def plot_loss(self):
plt.scatter(list(range(len(self.loss))),self.loss)
def predict(self,X:np.ndarray)-> np.ndarray:
X = np.asarray(X)
if len(X.shape) != 2:
raise ValueError("Shape must be (dims,n_data_points)")
X = np.concatenate([X,np.ones([1,X.shape[-1]])],axis=0) X_hat = np.matmul(self.W,X)
y_hat = self._sigmoid(X_hat)
return y_hat
让我们来测试一下!以本文开头所示的模拟数据集为例,我们已经准备好测试我们的模型…
在这种情况下,我们已经可视化了两个类之间的决策边界。这可以在分类器预测 0.5 概率的地方找到。换句话说,就是 w ᵀ x + b = 0 的那条线。所以我们的努力看起来是有回报的,我们的模型可以正确识别 0.89%的数据点,并在对接近决策边界的点进行分类时具有表达不确定性的能力!
那么,我们学到了什么?
如果你已经做到了这一步,那么希望你能更好地理解逻辑回归是如何工作的,以及为什么二元交叉熵只是表达模型预测的对数似然性的一种不同方式。
训练逻辑回归模型允许我们尝试反向传播,这为理解如何训练更复杂的深度神经网络提供了基础。
[1]:鲁梅尔哈特博士、辛顿博士和威廉斯博士,1986 年出版。通过反向传播误差学习表征。自然,323(6088),第 533–536 页。
理解并使用 TensorFlow 2.0 实现 ResNet-50
基于深度神经网络的图像分类
京都的清晨:来源(作者)
我们的直觉可能表明,较深的神经网络应该能够捕捉更复杂的特征,因此与较浅的神经网络相比,它们可以用于表示更复杂的功能。应该出现的问题是——如果学习一个更好的网络就等同于越堆越多的层?这种方法有什么问题和好处?这些问题和一些非常重要的其他概念在 2017 年 K. He 等人的用于图像识别的深度残差学习论文中讨论过。这种架构被称为 ResNet,本文介绍了许多与深度神经网络(DNN)相关的重要概念,这些都将在本文中讨论,包括在 TensorFlow 2.0 中实现 50 层 ResNet。你可以从这篇文章中学到什么—
- 深度神经网络的问题。
- ResNet 背后的数学直觉。
- 剩余块和跳过连接。
- 构造 ResNet 和 1×1 卷积的重要性。
- 用 TensorFlow 实现 ResNet。
我们开始吧!
**使用tf.data
的图像分类管道的更快版本在这里讨论。
退化问题:
ResNet 最初工作的主要动机是解决深层网络中的退化问题。向足够深的神经网络添加更多层将首先看到精度饱和,然后精度下降。何等人展示了以下使用普通网络的 Cifar-10 数据集的训练和测试误差图
图 1:在平原 DNN,Cifar-10 数据的分类误差随着训练(左)和测试数据(右)的层数的增加而增加。参考:[1]
正如我们可以看到的,较深网络(56 层)的训练(左)和测试误差(右)高于 20 层网络。深度越大,历元越多,误差越大。首先,似乎随着层数的增加,参数的数量也增加,因此这是一个过拟合的问题。但其实不是,我们来理解一下。
思考这个问题的一种方式是考虑深度 DNN,它可以计算手头任务所需的足够强的特征集(例如:图像分类)。如果我们在已经非常 DNN 的基础上再增加一层网络,这一层会有什么作用呢?如果网络已经可以计算强特征,则该附加层不需要计算任何额外的特征,而是,仅复制已经计算的特征,即执行身份映射(添加层中的内核产生与先前内核完全相同的特征)。这似乎是一个非常简单的操作,但在一个深层的神经网络中,这与我们的预期相差甚远。
ResNet 背后的数学直觉:
让我们考虑一个包括学习率和其他超参数的 DNN 架构,它可以达到一类函数 F 。因此,对于所有的 *f∈ F,存在参数 W ,我们可以在针对特定数据集训练网络之后获得这些参数。如果 f 表示我们真正想要找到的函数(最佳可能优化的结果),但是如果它不在 F 内,那么我们试图找到在 F 内的最佳情况 f1,,如果我们设计一个更强大的架构 G,我们应该会得到更好的结果 g1 ,这比 f1 更好。但是如果 f·⊈g,那么就不能保证上述假设会成立。事实上 g1 可能比 f1 更差,这是退化问题。所以要点是——*如果更深的神经网络函数类包含更简单和更浅的网络函数类,那么我们可以保证更深的网络将增加原始浅网络的特征发现能力。*一旦我们在下一节介绍残差块,这将变得更加清楚。
剩余块:
剩余块的想法完全基于之前解释的直觉。较简单的函数(较浅的网络)应该是复杂函数(较深的网络)的子集,以便可以解决退化问题。让我们考虑输入 x ,并且从输入到输出的期望映射由 g(x) 表示。我们将处理一个更简单的函数 f(x) = g(x)-x ,而不是处理这个函数。然后,原始映射被重新转换为 f(x)+x 。在 ResNet 论文中 He et al. 假设优化残差 f(x)比优化原 g 本身更容易。 优化残差还考虑到了这样一个事实,即我们不需要担心在非常深的网络中可怕的身份映射 f(y)→ y 。让我们看看下面剩余部分的示意图—
图 2:用于身份映射的剩余块和跳过连接。已重新创建以下引用:[3]
残差学习公式确保当恒等式映射是最优的(即 g(x) = x )时,该优化将使残差函数的权重趋向于零。ResNet 由许多残差块组成,其中残差学习被采用到每几个(通常 2 或 3 层)堆叠层。构建模块如图 2 所示,最终输出可以认为是 y = f(x,W) + x 。这里的 W 的是重量,这些是在训练中学会的。运算 f + x 通过快捷方式(“跳过”2/3 层)连接和元素相加来执行。这是一个最简单的块,跳过连接中不涉及任何附加参数。只有当 f 和 x 的尺寸相同时,元素相加才是可能的,如果不是这种情况,那么我们将输入 x 乘以投影矩阵 *Ws,*使得 f 和 x 的尺寸匹配。在这种情况下,输出将从之前的等式变为 y = f(x,W) + Ws * x 。投影矩阵中的元素也是可训练的。
构建 ResNet 和 1× 1 卷积:
我们将按照何在原论文中所采用的方法建立一个 50 层的 ResNet。ResNet-50 采用的体系结构不同于 34 层体系结构。快捷连接跳过了 3 个街区而不是 2 个,下面的示意图将帮助我们澄清一些问题-
图 3:左:跳过 2 层,ResNet-34。右图:跳过 3 层,包括 ResNet-50 中的 1× 1 卷积。参考:[1]
在 ResNet-50 中,残差块中的堆叠层将始终具有 1×1、3×3 和 1×1 卷积层。1×1 卷积首先降低维度,然后在瓶颈 3×3 层中计算特征,然后在下一个 1×1 层中再次增加维度。使用 1×1 过滤器来减少和增加瓶颈层前后的特征图的维度,如 Szegedy 等人在其初始论文中的 GoogLeNet 模型所述。由于残差块内没有汇聚层, 用步长 2 进行 1×1 卷积降维。记住这几点,让我们使用 TensorFlow 2.0 构建 ResNet-50。
建筑 ResNet-50:
在编码之前,让我们看看原始论文中呈现的 ResNet-34 架构—
ResNet-34(摘自 K. He 等人的《深度剩余学习》)
*仅池化层放置在架构末端的最开始和密集连接之前。*如前所述,使用 1×1 卷积来改变其他地方的尺寸。对于过滤器的数量和其他参数,我遵循了 Keras 的例子。现在是编码的时候了。首先,我们定义一个最简单的单位块,其中输入的维度不变,只有深度,下面是代码块-
最简单的残余块,尺寸没有任何变化,只有深度。
通过使用步长为 2 的 1×1 卷积,另一个残差块将包括输入维度的变化。因此,跳过连接也将经历尺寸变化——
与步幅 2 卷积。输入变化的维度
结合这两个剩余模块,我们现在可以构建完整的 50 层 ResNet,如下所示
使用 64,160 个时期的批量大小和数据扩充,在训练数据和测试数据上实现了 85%和 82%的准确度。下面是训练和验证曲线—
示出了 Cifar-10 数据 50 层 ResNet 的训练和验证精度/损失。(来源:作者)
此外,可以绘制 Cifar-10 数据中所有 10 个类别的混淆矩阵
使用 ResNet-50 训练的用于 Cifar-10 数据的 CM。(来源:作者)
讨论:
在这里,我们看到了一个使用 TensorFlow 实现 ResNet-50 并使用 Cifar-10 数据训练模型的示例。一个重要的讨论点是卷积-批处理-激活的顺序,这仍然是一个争论点。许多人认为最初的批次标准文件中使用的顺序不是最好的。看 GitHub 的一个问题这里。我建议您尝试不同于笔记本中使用的参数,以了解它们的效果。
你能从中得到的几个要点是—
- 退化和过度拟合之间的区别以及为什么退化发生在非常深的网络中。
- 使用 1×1 卷积来增加和减少特征图的维数。
- 残余块如何有助于防止退化问题?
暂时就这样吧!希望这能帮到你一点,保持坚强!!
页(page 的缩写)如果你想使用tf.data
建立一个更快的图像分类管道,那么查看这篇文章。
参考:
[1] ResNet 原创论文:何等。
[2] Keras 示例实现。
[3] Alex Smola: ResNet 直觉讲座
[4]笔记本所用代码: GitHub Link 。
如果你对更深入的基础机器学习概念感兴趣,可以考虑加盟 Medium 使用 我的链接 。你不用额外付钱,但我会得到一点佣金。感谢大家!!
更多来自 Saptashwa(以及 Medium 上的许多其他作者)。你的会员费直接支持 Saptashwa 和其他…
medium.com](https://medium.com/@saptashwa/membership?source=publishing_settings-------------------------------------)
了解并可视化色彩空间,以改进您的机器学习和深度学习模型
解释、分析和实验 14 种流行的颜色空间以及它们对我们模型的准确性的影响。
不同色彩空间的模型训练(自制测试)
介绍
“为什么我们在训练模型中使用 RGB 颜色空间作为标准?当然,这是最简单的颜色空间,因为它是默认的颜色空间。但是有没有其他可能更合适的色彩空间呢?它能改善我们的模型吗?”
这些问题浮现在我的脑海中,我必须找到答案。于是我调查了,做了一些实验。我愿意与你分享我的成果。💭
起初,我从探索不同的色彩空间开始,我发现启发了。所以在这篇文章的第一部分,我将向你简要介绍这些颜色空间以及它们在机器学习和深度学习中的可能的应用。
有大量(无限)的颜色空间,所以我为你选择了最有趣的颜色空间。😇
- RGB - CMYK
- 国际照明委员会 XYZ -国际照明委员会 Lab -国际照明委员会 Luv
- HSV- HSL- HSI
- YCbCr - YDbDr
- C1C2C3 - I1I2I3
- 皮肤单位剂量
在这篇文章的第二部分,我用相同的型号和相同的配置体验了这些色彩空间。我们会看到,从一个颜色空间到另一个颜色空间,我们的模型的精度可以从简单到两倍。
RGB — BGR —CMYK
那么一张 RGB 的图像是如何构造的呢?基本上是通过加上不同“比例”的红绿蓝。但是我想我不会告诉你比你已经知道的更多的东西。你添加的颜色越多,你得到的颜色就越浅。这是因为它们发出光(这是同样的原理,我们可以通过仔细观察屏幕来观察)。
RGB 和 CMYK —转换
这与原色反光不同。它是反向机制,即减法。你把颜色加在一起越多,你得到的颜色就越暗。这是用于打印、 CMYK (青色、洋红色、黄色和黑色)的系统。
那么为什么是 RGB 呢?事实是,你想要多少颜色空间就有多少颜色空间。我们将看到我们如何建造它们。但是 RGB 是关于简单的**。我们的电脑硬件就是这样组成的。**
RGB 分解(来源: Pixabay
RGB 是默认的颜色空间**,即使在机器学习和深度学习中也是如此。但是看一看的替代品。**
XYZ 国际照明委员会—国际照明委员会 Lab —国际照明委员会 Luv
我们看到 RGB 是面向设备的**。国际照明委员会 CIE 因其法语名称“Commission International e de l ’ eclairage”而设立了色度学标准**。它设计了更抽象的色彩空间来打破 RGB 标准的界限。****
CIE XYZ 分解(来源: Pixabay
编码在 3 个字节上的 RGB 空间允许表示人眼能够感知的 40% 的颜色。这就是为什么 CIE 建议用颜色空间来扩展人类实际感知的可能性领域。因此有了颜色空间 CIE XYZ 。它将颜色空间的边界扩展到包含所有可见的**。如果我们简化一下:**
- x 大致对应于红色刺激
- y 或多或少对应于亮度****
- z 大致对应于蓝色刺激
RGB 和 CIE XYZ——转换和模式(来源:维基百科
看一看原理图和我们从一个色彩空间切换到另一个色彩空间的方式,你就会理解两个关键要素**😗*
- 三种“原色”的任何选择只能产生一个可用颜色的子集。****
- 有一个矩阵通道有无限个不同的颜色空间
CIE XYZ 空间是一个工具空间 e,作为其他空间的支持:CIE Lab 和 CIE L * u * v将会很有趣,因为它引入了亮度的概念。**
RGB 和 CIE Lab—转换和模式(来源:维基百科)*
眼睛有三个不同的视锥细胞来感知颜色。一个红色,一个绿色,一个蓝色。但是这些视锥细胞没有同样的反应**。所以对颜色的感知不同于真实的颜色(用波长来说)。CIE Lab*颜色空间试图扭曲 CIE XYZ 空间以更好地代表人眼的颜色感知😗*
- L 为亮度黑色→白色*
- a 表示轴上的值绿色→红色*;**
- b 表示轴上的数值蓝色→黄色*。**
CIE Lab*分解(来源: Pixabay
为了训练学习模型,CIE Lab 可能是合适的。这一点可以从威尔逊·卡斯特罗的论文中看出,他们试图根据成熟程度对好望角醋栗进行分类。他和他的团队尝试了 SVM,安,DT 和 KNN。在这些模型中的每一个上,CIE Lab*色彩空间被证明比 RGB 色彩空间更有效。****
RGB 和 CIE Luv —转换和模式(来源:维基百科*
最后,CIE Luv*空间是接近人眼感知的另一种尝试。它的优点是善于表现自然场景的图像。颜色距离更容易估计,尤其是绿色之间的距离。在 M.P. Rico-Fernándeza 的论文中,他们使用 SVM 方法对栽培物种进行分类,CIE Luv*颜色空间使得精确度更高。
CIE Luv*分解(来源: Pixabay )
HSV- HSL- HSI
其他颜色空间基于心理学方法**。这是 HSV 、 HSL 和 HSI 空间的情况。所有这些都基于色彩心理学的概念,这是解释你所看到的的最佳方式:**
- 色调:主色调
- 饱和度:颜色的纯度
- 亮度:颜色的亮度
模式 HSV-HSL-HSI(来源: mathworks )
这些颜色空间被称为圆柱形**,因为它们由围绕色调的圆柱形或圆锥形形状**表示。所有这些空间都有相同的基础:代表主要波长的色调。****
RGB —色调转换
但是这些空间偏离了亮度的定义。
- 值对应最强震级
- 亮度对应于星等的范围
- ****强度对应于平衡的星等
RGB 值亮度强度转换
****饱和度因此也根据亮度的定义而有不同的定义。
RGB —饱和度转换
这三个色彩空间都有这个基础:色彩心理学。这有助于轻松设定亮度阈值**。在机器人学中,可以使用这个颜色空间。作为例子,我们可以看看 L. Nalpantidis 写的论文。他们使用 HSV 颜色空间来设计立体摄像机系统,并考虑到了非理想照明条件。**
HSV 分解(来源: Pixabay )
HSL 分解(来源: Pixabay )
HSI 分解(来源: Pixabay )
Y’UV — Y’IQ — Y’CbCr — Y’DbDr
这些色彩空间生来就有野心压缩视频传输所涉及的带宽**。我们以 Y’CbCr 为例。该色彩空间是一种标准,旨在确保与黑色&白色和彩色电视兼容。**
寻找色度的比例校正思想
所以你有一个信号Y’,它代表黑白的亮度**。然后使用两个 chromas 传输颜色信息的另外两个组件:蓝色 Cb 和红色 Cr 。它允许从 B & W 彩色图像中恢复丢失的信息。但实际上,磷光体(为电视屏幕着色)的效率不是线性的,因此我们应用校正系数。**
RGB 和 YCbCr——转换和模式(来源:维基百科
几个标准都有相同的亮度 Y’和校正基础,有两种色度:
- Y’UV (模拟格式 PAL)
- Y’IQ (模拟格式 NTSC)
- Y’CbCr (数字格式,JPEG……)
- Y’DbDr (模拟格式 SECAM)
RGB — Y’DbDr - Y’UV - Y’IQ 转换
因此,所有这些空间都或多或少有些相似,但也能在我们的模型中扮演重要角色。A. M. Aibinu 的论文表明,与 RGB 空间相比,基于 YCbCr 色彩空间的人工神经网络对于皮肤检测具有最佳性能。
Y’IQ 分解(来源: Pixabay )
Y’CbCr 分解(来源: Pixabay )
Y’DbDr 分解(来源: Pixabay )
Y’UV 分解(来源: Pixabay )
C1C2C3 — I1I2I3
I1I2I3 由 Y-I. Ohta 推出用于图像分割。媒体整合与传播中心(MICC)也提出了另一种形式,它建议不变,以突出效果。
RGB — I1I2I3 Otha 和 MICC 转换
MICC 还建议 C1C2C3 颜色空间对于阴影效果是不变的。****
RGB — C1C2C3 转换
I1I2I3 和 C1C2C3 有时被用作与其他颜色空间的补充或混合组合,如你在 A. Rasouli 的论文中所见。他和他的团队评估了大量的颜色空间,以测量它们检测和区分不同颜色物体的适用性。在所研究的组合中, C1C2C3 和 XYZ 的组合效果最好。
I1I2I3 分解(来源: Pixabay )
C1C2C3 分解(来源: Pixabay )
然后我决定测试 MICC 提出的色彩空间。
皮肤单位剂量
最后一个。 HED 用于苏木精-伊红-DAB** 是由 A.C.C. Ruifrok 和 D. A. Johnston 设计的颜色空间,用于分析医学领域的特定**组织。****
RGB-HED 转换
这种颜色空间可以通过图像用于血液分析,例如 K. Ben-Suliman 和 A. Krzyżak 的论文处理在显微镜下看到的图像。
HED 分解(来源: Pixabay )
方法
现在小旅行结束了,建议你在相同条件下和尝试这些色空间评估对同型号的影响。
模型
我决定训练一个 CNN ,更准确的说是受到 Yann Le Cun 的 LeNet5 的启发。有趣的是,这种模型多年来一直被用来分拣邮件。它被用来识别美国邮政服务手写的邮政编码。
模型摘要
将使用 CIFAR-10 数据库建立分类模型。
培训将在相同的条件下进行**😗*
- 预处理中输入图像的通道归一化****
- 批量:128 个
- 损失:分类交叉熵
- 优化器:SGD
- 学习率:1e-2
- 动量:0.9
- 混洗数据集:假
- 相同的训练和测试集
- 度量:准确性
- 在测试集上每个时期的度量计算
- 每次度量增加时,模型被保存
- 只有当在 5 个时期内没有增加,并且如果度量已经达到 0.11 阈值时,训练循环才停止,以避免在启动太慢的情况下停止训练 (0.11,因为有 10 个等级)
结果和讨论
根据颜色空间训练模型的进化
所以我在完全相同的条件下为这个模型训练了不同的颜色空间。我不想对你撒谎。我暗暗希望 RGB 色彩空间会比其他几个色彩空间更远。😐
完全没有。在我的例子中,RGB 空间仍然是满足色彩空间的。然而,我们可以注意到Y ’ uv 颜色空间对于我在 CIFAR-10 数据集上的模型是最合适的。👼****
不同颜色空间下的全局和类精度
在分析了我们的颜色空间的平均精度之后,我想知道我们的模型在依赖于类别的颜色空间上的精度是否有变化。
我们的两个最佳颜色空间(RGB 和 Y’UV)非常擅长对车辆进行分类,但在对动物的分类方面相对较弱。
基于 RGB 颜色空间的模型精度混淆矩阵
让我们仔细看看 RGB 空间:
- 船和车很少有误报或漏报**…**
- 假阳性和假阴性对鸟、猫、狗、鹿和青蛙来说是很重要的。
好了,现在是时候看看另一个颜色空间对分类动物是否有意思了。
在我们的例子中,颜色空间 CIE XYZ、CIE Lab、CIE Luv*非常有趣:*
- CIE Luv:根据不同等级,其精度分布非常均匀(即使它仍然不是最佳的)*
- **CIE Lab*和 CIE XYZ: **鸟是永远预测不到的!它们在这个颜色空间里大概很难分辨。
我不知道你怎么想,但在这种情况下,我会尝试为我的模型提供 Y’UV 图像输入,以获得其整体精度和 CIE Luv 颜色空间,以获得其稳定性的精度。*
你也可以看看其他的混淆矩阵👊
最后,我想以最后一个小图来结束,它总结了我们的模型在不同颜色空间和这些点下的训练:
- 可以用几个色彩空间来馈给你的模型。这就是 S.N .高达在他的论文中所做的,其中他提出了具有 RGB+HSV+YUV+LAB+HED 的分类模型。注:多不代表好。通过增加空间 CIE XYZ,他获得了较低的精度。
- 在深度学习的情况下,网络越深,就越会扭曲空间。因此,色彩空间的影响将最小化**。但是研究颜色仍然非常有趣,主要是在机器学习或者用定义的规则中。**
- 从一个型号到另一个型号,色彩空间的对准确度的影响不一定相同**。换句话说,对于同一数据集,RGB 空间可能更适合 SVM,而 HSL 空间更适合 KNN。**
****
知识就是分享。
支持我,获得 中我所有文章的访问一键点击此处 。
来源和参考
- 国际照明委员会
颜色通道表征对可转移性的影响 - 卷积神经网络,J. Diaz-Cely,C. Arce-Lopera,J.C. Mena,L. Quintero
- 使用机器学习技术和不同的颜色空间根据成熟程度对鹅莓果进行分类,W. Castro,J. Oblitas,M. De-la-Torre,C. Cotrina,k .巴赞,H. Avila-George
- 不同作物品种叶片分割的情境化方法,M.P. Rico-Fernándeza,R. Rios-Cabreraa,M. Castelána,H.-I. Guerrero-Reyesa,A. Juarez-Maldonado
- 非理想光照条件下机器人应用的立体视觉,L. Nalpantidis,A. Gasteratos
- 给动画世界上色:通过动画电影探索人类对颜色的感知和偏好。
- 基于人工神经网络的 YCbCr 皮肤检测
算法的性能分析,A. M. Aibinu,A. A. Shafie 和 M. J. E. Salami - 用于区域分割的颜色信息,Y-I. Ohta,T. Kanade T. Sakai
- 颜色空间选择对有色物体的可检测性和可辨别性的影响,A. Rasouli 和 J.K. Tsotsos
- 色彩,媒体整合与传播中心(MICC)
- ColorNet:探讨颜色空间对图像分类的重要性,S.N .高达,袁春华
- 通过颜色去卷积对组织化学染色进行定量,
A.C.C. Ruifrok,D. A. Johnston - 用于显微血液图像中急性淋巴细胞白血病检测的基于计算机计数的系统,K. Ben-Suliman,A. Krzyżak
- 机器学习:人工神经元的进化和研究进展
- 所有的色彩空间图片都是从维基百科和 Mathworks 图片中创建的
- 方程式是自制的:用乳胶写的
- 所有其他没有来源的内容都是自制的,可以免费使用
为什么凸性是最优化的关键
机器学习
使用凸成本函数是很容易的
凸函数。来源维基百科。
当开始机器学习时,你首先会遇到的最有趣的事情是优化算法,具体来说,就是梯度下降,这是一种一阶迭代优化算法,用于最小化成本函数。
梯度下降背后的直觉是收敛到一个解,这可能是邻域中的局部最小值,或者在最好的情况下,是全局最小值。
一切似乎都很好,直到你开始质疑自己的收敛问题。对凸性有很好的理解有助于你证明梯度下降思想背后的直觉。所以让我们讨论同样的问题。
希望大家对梯度下降有很好的理解。查看这篇文章的摘要。
“让我们达到全局最小值。”
nvsyashwanth.github.io](https://nvsyashwanth.github.io/machinelearningmaster/understanding-gradient-descent/)
凸集
简单来说,把凸集想象成这样的形状,其中连接该集合中两点的任何线都不会在该集合之外。这叫做凸集。
看看下面的例子。
理解凸集。图片由作者提供(使用 Adobe Xd 制作)。
很明显,任何连接圆或正方形(最左边和中间的形状)上的两点的线,将具有该形状内的所有线段。这些是凸集的例子。
另一方面,上图中最右边的形状有一部分线在形状外面。由此可见,这不是凸集。
凸集 C 可以表示如下。
凸集条件。作者图片
碑文
考虑一个函数的图形 f.
题图是位于函数的图形之上或上方的一组点。
一个函数的词表。图片由作者提供(使用 Adobe Xd 制作)。
凸函数
好了,现在你明白了什么是凸集和上图,我们可以谈谈凸函数了。
凸函数及其题图。来源维基百科。
一个函数 f 称之为凸函数 如果它的词牌是一个凸集(如下图左边绿色图所示) 。
这意味着在这个图 上画的每一条线段总是 等于或高于函数图。暂停一分钟,自己检查一下。
理解凸函数。图片由作者提供(使用 Adobe Xd 制作)。
这意味着函数 f 不是凸的,如果存在两个点 x,y 使得连接 f(x)和 f(y)的线段在函数 f 的曲线下面 。 这导致词牌的凸性丧失(如上图右侧红色部分所示)。 这意味着在这个图 上画的每一条线段都是 不总是等于或高于函数图 。通过在弯曲处取点也可以证明这一点。
凸性测试
在神经网络的情况下,大多数成本函数是非凸的。因此,你必须测试函数的凸性。
若函数 f 的二阶导数大于或等于 0,则称该函数为凸函数。
凸函数的条件。
凸函数例子: y=eˣ,y=x .这两个函数都是两次可微的。
如果-f(x)(减去 f(x))是凸函数,那么这个函数叫做凹函数。
凹函数的条件。
凹函数的例子: y=-eˣ.这个函数可微分两次。
让我们通过绘制指数函数 eˣ.来检查凸性
用于绘制凸凹函数的代码。作者写的。
代码输出:
凸凹函数。图片由作者提供(代码输出)。
梯度下降优化中的凸性
如前所述,梯度下降是一种一阶迭代优化算法,用于最小化成本函数。
为了理解凸性如何在梯度下降中起关键作用,让我们以凸和非凸成本函数为例。
对于线性回归模型,我们定义了成本函数均方误差(MSE ),它衡量实际值和预测值之间的平均平方差。我们的目标是最小化这个成本函数,以便提高模型的准确性。 MSE 是一个 **凸函数(两次可微)。**这意味着没有局部最小值,只有全局最小值。因此梯度下降将收敛到全局最小值。
MSE 方程。图片由作者提供。
现在让我们考虑一个非凸的成本函数。在这种情况下,取一个任意的非凸函数,如下图所示。
非凸函数上的梯度下降。图片由作者提供(使用 Adobe Xd 制作)。
您可以看到,梯度下降不会收敛到全局最小值,而是会停止在局部最小值,因为该点的梯度为零(斜率为 0)并且在邻域中最小。解决这个问题的一个方法是使用动量的概念。
结论
凸函数在优化中起着巨大的作用。优化是机器学习模型的核心。理解凸性对于 same 来说非常重要,我相信你从这篇文章中已经了解到了。
谢谢你。下一场见。
了解如何在 Python 中使用 NamedTuple 和 Dataclass
创建一个超出 init 的 Python 对象
如果我要求你在 10 秒内为交易记录创建一个 Python 类,你会怎么做?可能大多数人都会用__init__
创建一个类。在本文中,我想分享 Python 中构造一个类的两个替代方案:名为 Tuple 和 Dataclass 。最后,我将比较这三个选项的性能,并就何时使用哪一个给出一些建议。
我将在本文中把带有 *__init__*
的类称为“常规”Python 类。请让我知道它是否有正式名称。如果您想深入了解命名元组和数据类,请查看参考链接。
每笔支付交易都有发送者、接收者、日期和金额。如果我使用常规的 Python 类,它可能看起来像这样:
很直白。但是说实话,代码很多,至少很多行。在一个用例中,我从一个源(例如 Kafka)消费实时交易记录,我不希望有修改记录的灵活性。我如何以更干净的方式实现这一点?
命名元组
命名元组是构造类的一个很好的选择。命名元组基本上是 Python 内置元组数据类型的扩展。Python 的 tuple 是一种简单的数据结构,用于对不同类型的对象进行分组。它的定义特征是是不可变的。
不可变对象是在创建后其状态不能被修改的对象。
在 Python 中,不可变类型有 int , float , bool , str , tuple 和 unicode 。
然而,内置元组类型的一个缺点是它给程序员增加了很多责任。当访问内置元组的一个属性时,需要知道它的索引。如果你正在开发一个将被成千上万的用户使用的库,这会引起一些混乱。
另一种可能的方法是使用内置字典,像{"sender":"","receiver":"","date":"","amount":""}
。但是这里的问题是你需要正确的拼写键名。而且根本不是一个档次。
我们可以使用命名元组来避免这些问题。命名元组允许我们给元素命名,所以我们可以通过属性名和它的索引来访问属性。
Python3.8 中的命名元组实际上有两种类型,一种来自已经存在很久的collections
包,另一种来自从 Python3.6 开始引入的typing
包。
collections . named tuple
我先从[*collections*](https://docs.python.org/3.8/library/collections.html)
包 。这个包提供了 Python 通用内置类型的替代方案,如字典、列表、集合和元组。
这就是如何创建一个namedtuple
类。哇,就一行代码!
我们刚刚创建的Transaction
本质上是一个类。它有属性发送者、接收者、日期、金额和_fields
,允许我们通过名称和索引来访问属性。
创建一个对象
创建一个 namedtuple 对象和创建类一样简单。我还喜欢的是 namedtuple 对象的表示。您也可以通过覆盖__repr__
方法在常规 Python 类中定制它。
为属性分配默认值也是可能的。
named tuple-create-default . py
⚠️ ️ 注意了!
named tuple-create-default-career . py
在这个例子中,我只将None
作为默认值分配给一个属性。分配了哪个属性?想想吧。
None
被分配给属性amount
,它是元组的最后一个属性。
这里发生了什么事?我鼓励你在进一步阅读前思考一分钟。
好吧。本质上,命名元组的创建遵循与常规 Python 类相同的语法。
神奇的是,当我们创建一个类时,非默认参数不能跟在默认参数后面。在常规的 Python 类中,我们会立即注意到这个错误。这个语法规则也适用于命名元组,,所以默认值将从最后一个元素开始分配。
named tuple-create-default-regular-class . py
回到子主题:创建对象。也可以使用_make()
方法创建一个namedtuple
对象。但是你需要记住属性的顺序。
访问属性
访问namedtuple
中的属性也非常简单。事实上,它是一个元组,我们可以像访问基本元组一样访问属性。一个namedtuple
可以用_asdict()
转换成一本字典。
分配属性
等等?你刚才说 tuple 是一个不可变的数据类型,你怎么能在一个不可变的对象中分配属性呢?
这取决于元组属性的数据类型。如果属性像 str,float,int 一样是不可变的,那么我们就不能修改状态,比如("1.0","xiaoxu","2019-01-01")
。
**但是如果属性本身像 list 一样是可变的,你可以改变列表中的元素,而不让元组知道这种改变。
具有可变属性的 namedtuple
在这个例子中,发送者是一个可变对象的列表。在修改属性之前,我使用 Python 内置函数id
来检查链表的内存地址。
然后,我将列表的第二个元素更改为“gaga ”,并打印出同一列表的 id。原来 id 是一样的!因此,对于*命名的元组,*状态没有改变。
具有可变属性 2 的 namedtuple
但是如果我们用相同的元素创建一个新的列表:“jojo”和“gaga”,并试图将它分配给 namedtuple ,这是行不通的,因为它们有不同的 id。
继承类
有时我们想要继承一个类并扩展其属性。在namedtuple
中,您可以使用@property
添加一个新属性。就像你看到的一样简单。
分配字段名导致 csv 或 db 查询返回元组
我喜欢namedtuple
的另一个很酷的特性是,它可以将 csv 记录或 db 查询结果映射到一个 namedtuple 对象。它可以减少样板代码的数量。
named duple-read-CSV-database . py
打字。名为的一对
创建一个名为的元组的第二个选项是使用typing.NamedTuple
。它来自typing
包,所以每个属性都绑定到一个类型。在我看来,这就是collections.namedtuple
和typing.NamedTuple
的唯一区别。
这种格式看起来有点像常规的 Python 类,但是带有类型。确保你继承了typing.NamedTuple
。
从typing.NamedTuple
创建的类有一个额外的字段_field_types
,显示每个属性的定义类型。
有时候,如果你真的不想为某些属性定义类型,你可以使用 type Any
。静态类型检查器会将每个类型视为与任何类型兼容。
创建一个对象,访问属性&分配属性
在创建对象、访问属性和分配属性方面,typing.NamedTuple
与collections.tuple
相同。
数据类
Dataclass 是 Python 3.7 中引入的新特性。它被用作装饰器。它为我们实现了__init__
、__repr__
等。
命名元组的行为类似于元组,而数据类的行为更像常规的 Python 类。我为什么这么说?因为默认情况下,属性都是可变的,只能通过名称访问,而不能通过索引访问。
继承类
当继承一个数据类时,子类的属性跟随父类,所以要小心默认值。如果父类中的一个字段被赋予了默认值,那么子类中的所有属性都应该有默认值。
不可变数据类
命名元组的关键特性之一是不可变的。您也可以通过frozen=True
在 dataclass 中设置不可变的属性。
在冻结的数据类中,如果试图修改状态,将引发FrozenInstanceError
异常。
性能比较
最后,我想比较一下常规 Python 类、collections.namedtuple
、typing.NamedTuple
和dataclass
的性能。比较内容包括:
- 对象的大小
- 创建对象的时间
- 检索属性的时间到了
我已经创建了 5 个不同的类。最后一个是带有字段__slot__
的优化的数据类。槽用于使类更快,使用更少的内存。
named tuple-data class-comparison-init . py
比较物体的大小
第一步是使用sys.getsizeof
比较每个创建对象的大小。
named tuple-data class-size-comparison . py
比较时间创建一个对象
然后我用 Python 内置函数[timeit](https://docs.python.org/3.8/library/timeit.html)
对比对象创建时间。
named tuple-data class-creation-comparison . py
比较时间访问属性
named duple-data class-access-comparison . py
结果
named tuple-data class-comparsion-result . CSV
基于元组的对象通常较大,因为它们包含属性名和索引。具有槽的数据类具有最少的对象创建时间。常规 Python 类似乎擅长访问属性。
结论
在做决定之前,你必须了解你的要求。
- 您希望对象是不可变的吗?
- 您希望在 Python 代码中显式输入吗?
- 你在乎记忆吗?
- 你喜欢简洁的风格还是易读的风格?
……
选择常规 Python 类,如果你……
- 用于创建任何带有
__init__
的类。拥有__init__
让我感觉很安全。 - 每个人都能理解代码。
- 正在使用 3.6 以下的 Python 版本。
- 不需要不可变的对象。
- 不喜欢显式打字。
选择 **collections.namedtuple**
类如果你……
- 想要不可变的对象。
- 不喜欢显式打字。
- 想要更少的代码。少即是多!
选择 **typing.NamedTuple**
类如果你……
- 想要不可变的对象。
- 确实需要显式输入。
- 想要更少的代码。少即是多!
选择 **dataclass**
如果你……
- 希望灵活地使它既可变又不可变。
- 确实需要显式输入。
- 想要更少的代码。少即是多!
- 想要好的速度表现。
希望你喜欢这篇文章!如果你有任何想法,请在下面留下你的评论。
资源
[## 用命名元组编写干净的 Python-dbader.org
命名元组是手动定义类的一个很好的选择,它们还有一些其他有趣的特性…
dbader.org](https://dbader.org/blog/writing-clean-python-with-namedtuples) [## Python 3.7 中数据类的终极指南——真正的 Python
Python 3.7 中一个令人兴奋的新特性是数据类。数据类通常包含…
realpython.com](https://realpython.com/python-data-classes/) [## 数据类-数据类- Python 3.8.3 文档
这个模块提供了一个装饰器和函数,用于自动添加生成的特殊方法,比如和…
docs.python.org](https://docs.python.org/3/library/dataclasses.html) [## 类型-支持类型提示- Python 3.8.3 文档
源代码:Lib/typing.py 注意 Python 运行时不强制函数和变量类型注释。他们可以是…
docs.python.org](https://docs.python.org/3/library/typing.html) [## 集合-容器数据类型- Python 3.8.3 文档
源代码:Lib/collections/init。py 3.3 版本新增。该类可以用来模拟嵌套的作用域,并且是…
docs.python.org](https://docs.python.org/3/library/collections.html)
理解 Python 中的继承
一个简单但重要的设计模式
In 继承是任何面向对象编程语言的关键特性,包括 Python。当您在 Python 3 中编写代码时,您可能已经在不知不觉中使用了继承特性。与 Java 等其他面向对象语言不同,Python 支持单继承和多继承。
在本文中,我将讨论 Python 中不同类型的继承,并解释这个概念是如何集成到 Python 3 中的,以及我们如何在日常编程中使用它。我们开始吧!
什么是继承?
继承是基于现有的类创建一个类,新的类继承现有类的属性和方法。新的类通常称为“子类”,现有的类称为“父类”。这一概念与现实世界的运作方式相同,在现实世界中,孩子从父母那里继承了一些特征,但同时,他或她也可以拥有独特的特征。
任何面向对象编程语言中的继承都应该遵循 Liskov 替换原则 其中说:
如果 S 是 T 的子类型,那么 T 类型的对象可以用 S 类型的对象替换
这意味着子类将从父类继承属性、方法和实现。允许修改和添加新功能,但不能从父功能中删除功能。
例如,我有一个父类Student
和子类HighSchoolStudent
和PrimarySchoolStudent
。两个子类都继承了来自Student
的所有属性,但是它们中的每一个对于grade
都有不同的值。例如,如果你想创建一个不需要school
属性的对象,那么你可能不应该在这里继承。
每个类的祖先:Object
如果你在 Python 中创建了一个新类,那么它已经从某个地方继承了。我们从一个虚拟类BaseClass
开始,使用__bases__
方法检查它的父类。我们得到了<class 'object'>
,它有一些带有双前导和尾随下划线的方法,所以从object
继承的类也实现了这些方法。它们在 Python 中被称为魔法方法。
类 **object**
**是 Python 中每个类的祖先。**如果做object.__bases__
,会得到一个空值。
在 Python 2.x 中,仍然像 *class BaseClass(object)*
一样强制显式继承 *object*
,但是从 Python 3.x 开始,已经没有必要了。
对于 Python 中定制的异常,它们应该从类BaseException
或其子类Exception
扩展而来,尽管BaseException
的父类也是object
。不能提升不是从 **BaseException**
类继承的类。
raise BaseClass
# TypeError: exceptions must derive from BaseException
单一遗传
单一继承是指类只从一个类继承。根据在子类中做什么,子类可能有不同的结构。这里我们使用 UML 来显示类之间的关系。在每个例子中,我将首先说明用例,然后画出 UML 图。最后,我将展示代码。请在检查答案之前考虑解决方案。
例 1 :我有一个父类Job
,它有一个属性person_name
和一个方法task
。我想创建一个继承自Job
的子类Teacher
,并用*“教学生”*覆盖task
。
UML 图看起来像这样。这是一个非常简单的例子,我们唯一需要做的就是覆盖方法task()
。
UML 图
让我们检查一下代码。
例 2: 我们仍然使用Job
作为父类,但是这次除了重写task()
之外,我还想创建一个带有额外属性school_name
的子类Teacher
。
在 UML 图中,我们会在类Teacher
中找到一个新的属性。
UML 图—新属性
school
是一个新属性,所以这意味着我们需要覆盖__init__
方法来添加属性。在这个例子中,我们将使用一个内置函数[super()](https://docs.python.org/3.8/library/functions.html#super)
。
简而言之,super()
返回一个将方法调用委托给父类的对象。它允许您重用父类的属性和行为。在下面的代码中,super().__init__
将执行Job.__init__
中的所有内容,以避免重复代码。
super()
可以在其他方法中使用,所以也可以在子类中调用super().task()
。
例 3: 除了例 2,我想防止类Job
被实例化,因为没有名为Job
的作业。
UML 图将与示例 2 相同。
UML 图—新属性
为了实现这一点,我们将讨论 Python 中抽象类的概念。抽象类是打算被继承但从不实例化的类。 Python 提供了abc
模块定义抽象基类,提供了@abstractmethod
decorator 定义抽象方法。
所以新的Job
类是从ABC
类扩展而来的,方法task()
有装饰器@abstractmethod
。通过这样做,它告诉程序员这个类不允许被实例化。如果您试图创建一个对象,它将引发一个异常。此外,将任何有意义的逻辑放入抽象方法是没有意义的,尽管你仍然可以这样做。
继承-抽象. py
job = Job("xiaoxu")
# TypeError: Can't instantiate abstract class Job with abstract methods task
子类Teacher
将与前面的例子相同。
多重遗传
多重继承是指类从多个父类继承。在某种程度上,这可以减少冗余,但也会增加代码的复杂性。你应该清楚自己在做什么。
例子:我有一个父类Dad
和另一个父类Mum
。子类Kid
扩展了两个父类。父类如下所示。有些属性具有相同的值(例如城市),但有些属性不具有相同的值(例如眼睛颜色)。
好吧,那么问题来了。一个kid
对象的默认eye_color
是什么?当涉及到多重继承时,子类将首先在它自己的类中搜索属性,如果没有,那么在它的父类中以深度优先、从左到右的顺序搜索。这在 Python 中叫做 方法解析顺序 (MRO)。MRO 定义了 Python 如何搜索继承的方法。
幸运的是,我们可以通过执行Kid.__mro__
来检查这个订单。由于Kid
类第一次访问Dad
,那么它默认会有蓝眼睛。此外,kid
将会兼具游泳和舞蹈“技能”。
如果你只想从类Mum
继承属性,那么你可以在Kid
的__init__
中明确提到。
结论
继承是任何面向对象编程语言中极其常见的设计模式。它有助于程序员重用代码,节省大量的时间和精力。这种模式在 Python 中得到了很好的实现,既有单一继承的可能性,也有多重继承的可能性。
我希望你喜欢这篇文章!如果你有任何想法,请在下面留下你的评论。
参考
[## 用 Python super() -真正的 Python 来增强你的类
免费奖励:关于 Python 精通的 5 个想法,这是一个面向 Python 开发者的免费课程,向你展示了路线图和…
realpython.com](https://realpython.com/python-super/)