你可能不知道的五个小技巧
这些例子说明了为什么 R 现在是直观数据操作的首选语言
最近通过与 tidyverse 的许多其他用户合作,我突然意识到,有许多人并不知道这个软件包集合为他们提供的帮助他们处理日常数据争论的所有东西。特别是,两个关键的软件包在过去的一年中进行了重大更新,并引入了我认为具有变革性的新功能——允许用户在控制数据和提高代码效率方面加快步伐。
2019 年末,tidyr 1.0.0
发布。在众多更新中,最关键的是引入了函数pivot_longer()
和pivot_wider()
来更好地管理和控制数据帧从宽格式到长格式的转换——这是最常见的数据争论任务之一。取代了gather()
和spread()
,这些新功能引入了更多管理转换细节的能力,在用户如何定制其输出方面为他们节省了时间。
2020 年初,dplyr 1.0.0
上映。这个版本中的新功能范围很广,但特别是引入了作为副词与summarise()
和mutate()
一起使用的across()
和c_across()
,简化了用户需要处理的范围变量的数量,并且像tidyr
变化一样,允许更好地控制输出的外观。
这两个更新都利用了 R 生态系统中的重大创新,包括rlang
、vctrs
和glue
等的更新。
因此,如果您还没有查看这些更新,现在是查看 tidyverse 包的好时机。在本文中,我想向您展示它们如何让您的生活变得更加轻松,以及如何使用它们用更少的代码来处理数据。为此,我将展示五个你可能不知道的简单例子。
1.在tidyr::pivot_wider()
中任意组合列名
pivot_wider()
的整体思想是,你想要获取长格式的数据,并将其转换为宽格式。例如,假设您的数据如下所示:
现在,假设您有兴趣查看每种风暴状态每年的平均压力和中值压力。您可以使用pivot_wider()
,它很聪明地知道您正在尝试做什么,并在默认情况下将列名粘贴在一起:
您还受益于names_glue
参数,它允许您使用简单而直观的glue
语法按照自己的意愿构造组合的列名:
2.使用tidyr::pivot_longer()
任意分解列维度
如果您有表示多个维度的宽数据,您可以将其转换为长数据,并在pivot_longer()
的names_pattern
参数中使用 regex 从列名中分离出任意数量的维度。例如,要将之前的表格移回长表格,我们可以这样做:
3.使用dplyr::across()
对任意多的列进行汇总或变异
across()
是 dplyr 中的一个新副词,它允许您灵活地处理任意数量的列,并更好地控制输出。以前,如果您想在许多列上进行许多操作,您需要使用summarise()
和mutate()
的作用域变体来实现这一点,并且默认输出不容易控制。例如:
现在副词across()
可以用于所有情况,以达到相同的目的,所以你不再需要使用_if
、_at
和_all
的变体。它充当在summarise()
或mutate()
中使用的选择函数,并提供对.names
参数的简单使用,以使用glue
语法控制输出的列名:
4.使用dplyr::nest_by()
对嵌套数据运行模型
需要对数据帧的子集执行操作是很常见的,这需要您根据某些变量来嵌套数据。以前,这只能通过组合像tidyr::nest()
、dplyr::group_by()
和dplyr::rowwise()
这样的函数来实现,但是新的dplyr::nest_by()
函数现在可以处理所有这些,并减少你需要键入的代码量。
假设您想在mtcars
上运行一个线性模型,但是您想对不同的圆柱体模型分别执行。对于nest_by()
来说,这是一份完美的工作。当您使用nest_by()
时,嵌套数据帧的“列表列”在名为data
的列中生成,如下所示:
然后,您可以在进一步的命令中引用该列,允许您执行非常复杂的操作,如运行模型,或者执行您自己的函数:
现在,为了使我们的输出更加漂亮和用户友好,我们可以使用我们之前学到的一些技巧:
5.使用dplyr::summarise()
和dplyr::mutate()
生成对象——不仅仅是值
列表列的美妙之处在于,您可以在其中存储许多不同的内容,而不仅仅是值。现在,您可以使用我们的老朋友summarise()
和mutate()
来生成不仅包含值,还包含数据框架、模型甚至图表的列。
在这个例子中,我使用ggplot2
创建了一些简单的函数来生成散点图和箱线图。然后,我嵌套我的数据并变异生成这些图的新列。
如您所见,所有的图现在都存储在我们创建的两个列表列中。我们现在可以方便地把它们拿出来使用。例如,使用patchwork
:
最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在LinkedIn或Twitter上找我。也可以看看我在drkeithmcnulty.com上的博客。
维基共享照片
通过 AWS 机器学习专业考试的五个技巧
数据科学家的视角
照片由 Unsplash 上的 Rishabh Agarwal 拍摄
我最近通过了 AWS 机器学习专业考试,收到了不少关于如何准备的问题。所以我想从数据科学家的角度写一些小技巧。这绝不是全面的 AWS ML 考试指南;相反,我把重点放在了我认为不应该丢分的几个方面,否则我经常在练习考试中出错。
AWS 的考试指南说,你将在四个领域接受测试:数据工程、探索性数据分析、建模以及机器学习实现和操作。建模部分占整个考试的 36%;这是测试你的实用机器学习知识的部分,测试你如何将正确的 ML 解决方案应用于现实世界的商业问题。此外,作为 AWS 考试,这一部分有许多关于 SageMaker 和其他 AWS ML 服务的问题。
我购买了弗兰克·坎和夏羽·马雷克的 Udemy 课程,发现它非常有用。实际上,我发现这次考试比我去年春天通过的 AWS 解决方案架构师助理考试需要的准备要少得多,这主要是因为作为一名数据科学家,我每天都在处理 ML 问题,而且我通常不必太担心设置网络和安全性。所以,如果你和我有类似的背景,AWS ML 考试是值得参加的。准备工作将有助于您更好地了解 AWS 提供的服务。在这里,我总结了五个可能帮助你通过 ML 考试的技巧:
1.了解您的发行版
这真的是统计学/机器学习的基础知识。你会在上面做测试,你应该能很快回答这些问题。该问题将解释一个场景,并询问您哪一个发行版最能描述该场景。作为一名统计学专业的学生,这些是我发现最简单的问题,但这也意味着你不应该在这些问题上丢分——因为其他考生可能也在这些问题上得了满分。
2.亚马逊 Kinesis 家族
如果你像我一样,更习惯于处理“离线数据集,你可能没有处理实时数据的经验,这就是 AWS Kinesis 系列工具的意义。kinesis 系列服务,连同 AWS Glue,将构成 ML 测试的大部分数据工程领域(20%)。我总结了一开始让我困惑的几个要点:
- 只有 Kinesis Data Firehose 可以将流数据加载到 S3;它还可以提供数据压缩(针对 S3),以及到 Parquet/ORC 的数据转换。
- 虽然 Kinesis Data Firehose 不能提供数据转换(如 CSV->JSON),但它可以通过 AWS Lambda 来实现。
- Kinesis Analytics 主要用于通过 SQL 进行实时分析;有两个自定义 AWS ML SQL 函数:RANDOM_CUT_FOREST(用于检测异常)和 HOTSPOTS(用于识别密集区域)。
- Kinesis Analytics 将使用 IAM 权限来访问流媒体源和目的地。
- 还有 S3 分析,这是不要与 Kinesis 分析混淆。S3 分析用于存储类别分析。
3.AWS 胶水
AWS Glue 是另一个我喜欢的 AWS 工具——我只是觉得它太酷了,它可以自动抓取我在 S3 上的拼花文件,并自动生成一个模式。当我们在 S3 托管的 Parquet 中有大量数据时,查询 Glue 生成的 Athena 表进行数据探索是非常方便的。在准备考试的时候,我又发现了胶水可以做的一些小把戏:
- 除了 DropFields、filter 和 join 等标准数据转换之外,它还附带了一个 AWS 自定义算法 FindMatches ML,该算法可以识别潜在的重复记录,即使这两个记录可能不完全匹配。
- Glue 将建立弹性网络接口,使作业能够安全地连接到其他资源
- Glue 可以运行 Spark 作业,同时支持 Python 2.7 和 Python 3.6。
4.安全性
同样,您的里程数可能会有所不同,但对我来说,我更习惯于 IT 部门告诉我,由于安全问题,我不能这样或那样做,而不是自己必须过多地担心它。虽然 ML 考试没有解决方案架构师考试那么多关于安全性的问题,但您不想在这方面丢分。至少,你需要知道安全与 AWS S3 一起工作;此外,围绕使用 SageMaker 的安全性,如何保护您的数据进出 SageMaker。
5.亚马逊 SageMaker
我有一些使用 Amazon SageMaker 的经验,我喜欢它让一些事情变得非常方便,比如提供预装在容器中的最流行的 ML 框架。我只是不喜欢它的成本,但是,我跑题了😔。Amazon SageMaker 是 AWS 全面管理的 ML 服务的旗舰产品,将在 ML 考试中受到严峻考验。除了我上面提到的安全性,您需要知道的一些事情包括:
- 了解 SageMaker 所有内置算法;你将在这些上被测试!还有,了解哪个算法可以通过多核,多实例,或者 GPU 来加速。
- SageMaker 只能从 S3 获取数据,有管道模式和文件模式;通常,当数据非常大时,管道模式可以加快速度。
- SageMaker 中的超参数调优,了解有哪些选项,了解自动模型调优如何与 SageMaker 一起工作。
- 众所周知,使用 Amazon 弹性推理(EI)和 Amazon SageMaker 托管端点可以加快推理时间,但是您只能在实例启动期间而不是在部署之后附加 EI。
额外提示:以一次考试的价格参加两次考试
从成本角度来看,我也建议在参加机器学习专业考试之前先参加 AWS 解决方案架构师助理认证考试(我就是这么做的)。AWS 解决方案架构师助理考试费用为 150 美元,通过考试后,您将收到下次考试的半价优惠券。AWS 机器学习专业考试是 300 美元,在应用折扣后将是 150 美元。所以花 300 美元你就可以获得两个 AWS 证书!祝你好运!
数据科学技术教我们成为更好的人的五种方式
自学的数据科学方法
当涉及到在世界上传播善的时候,数据科学有一个极其混杂的名声。
Marinus Analytics 一直在使用面部和纹身识别算法来识别和营救人口贩运的受害者。然而,执法中使用的一些相同的图像识别技术在妇女和少数民族群体中具有极高的错误识别率[1]。
如果即使是计算能力远远超过人脑的专家开发的模型也包含偏见和误解,那么作为人类,我们怎么能希望在伦理上完美无瑕呢?
我们处理数据和从数据中学习的方式是通过多种经验方法发展起来的。这些方法试图将我们引向最好的量化结果。那么,为什么不将您的数据科学实践应用到自己身上,看看您能学到什么呢?
电子设计自动化(Electronic Design Automation)
先说一个缩写吧!EDA 指的是探索性的数据分析,或者换句话说,在深入研究之前仔细看看你有什么。EDA 的目的是揭示各种有趣的发现,这些发现可能会影响你努力工作的结果,比如你的人口中存在很大的偏差。BMC 公共健康发表的研究[2]发现,招募男性参与健康行为研究在历史上比招募女性更困难。EDA 让我们能够尽早发现这些偏差,以确保我们不会制定出让女性成为最大受益者的公共卫生战略。
这种谨慎的方法鼓励数据科学家从后退一步开始,并问“我被告知什么”,他们将及时得到更准确、校准更好、理解更好的解决方案。或者,正如我更喜欢的,“草率的结论导致蹩脚的解决方案”,这同样适用于复杂的人类情况。
克服偏见
将您自己的经验带到您的数据挑战中,并希望首先测试这些挑战,这是很自然的。对我们的数据提出问题的过程通常在特征工程期间进行,是形成数据科学解决方案的基础。虽然我们可能希望从我们的直觉开始,但考虑不寻常或不太可能的事情是否重要?这确保了解决方案反映了现实,而不仅仅是你对现实的感知。
就像机器学习算法完全通过过去的数据获得信息一样,我们的世界观无疑是由我们自己的生活经历塑造的。心理学家告诉我们,不仅有可能克服我们的认知偏见,而且当我们这样做时,我们能够做出更好的决定。
协作
编码被认为是孤独的追求。“橡皮鸭法甚至建议你用无生命的物体解决问题!然而,征求他人的意见至少可以用来确定你的旅行方向。在职业生涯早期能够与有经验的同事合作的学者被证明更成功[4]。被告知新的想法或信息可能会让人不知所措,与他人一起面对这些会促进更好的相互理解。或者至少,你的困惑有人陪伴。
交叉验证
通过使用交叉验证,我们希望了解我们的解决方案将如何在独立数据集上执行。这可能会突出不吸引人的属性,如过度拟合或选择偏差,我们将明智地纠正这些属性,因此人们普遍认为,在不同的数据折叠上交叉验证您的解决方案会带来更好的结果。同样,麦肯锡的一份报告显示,管理董事会更加多元化的公司表现更好。
我们都有一个聪明的朋友,无论发生什么,他都会站在你这边;然而,当你真的需要被告知放弃不合适的合作伙伴/装备/算法选择时,你真的会更好地利用各种观点,而不是不断地在同质样本上交叉验证。
超参数调谐
调整算法的超参数可以把一个好的解变成一个优秀的解。或者,在著名的数据科学竞赛网站 Kaggle 上,可以挤出一点点额外的模型准确性分数,以便您现在可以向竞争对手“山姆卫斯·詹吉”展示谁真正统治了他们。虽然有许多技术可以帮助这一点,但本质上可以归结为转动一些旋钮,并使用您精心构建的交叉验证样本来告诉您它是否有效。
作为人类,完美是不可能的。我们有权犯错误并试图改正错误。用一个耐心的、深思熟虑的和多样的交叉验证样本包围你自己,你会发现自己能够不断迭代和改进。
在生活中,就像在数据科学中一样,从一个善意的天真方法开始,然后通过应用额外的信息来迭代它,以便朝着更好的方向努力,这并不可耻。我也毫不怀疑,当你认为自己或你的数据科学项目“完成”时,情况将会改变,需要新的校准。
[E] A. Pesah,A. Wehenkel 和 G. Louppe,用于无似然推理的递归机器 (2018),NeurIPS 2018 元学习研讨会
[1] Buolamwini,j .,Gebru,t .,“性别差异:商业性别分类的交叉准确性差异”机器学习研究论文集 81:1–15,2018 年关于公平性、问责制和透明度的会议
[2] Ryan,j .、Lopian,l .、Le,B. 等天没有下雨男人:一项调查改善男性健康行为研究方法的混合方法研究。 BMC 公共卫生 19、 814 (2019)。https://doi.org/10.1186/s12889-019-7087-4
[3] Morewedge C. K .,Sellier A-L .,Scopelliti I .“去偏置培训改善现场决策”(2019),APS。https://doi.org/10.1177/0956797619861429
[4] Li,w .,Aste,t .,Caccioli,F. 等早期与顶尖科学家合作预示着学术生涯的成功。Nat Commun10、 5170 (2019)。https://doi.org/10.1038/s41467-019-13130-4
加快数据分析的五种方法
让它在竞争中脱颖而出
图片由皮克斯拜的 Gerd Altmann 提供
Python 是数据分析的基本和流行工具之一。但是,如果比赛中的每个人都在使用 Python 呢?如何加快分析速度?如何让你的数据分析脱颖而出🏁达到积分表的顶端?在这里,我要告诉你 5 个简单易用的方法来加速你的数据分析。
由于 Jupyter-Notebook 在大多数数据科学初学者中很受欢迎,我将使用 Jupyter-Notebook 向您介绍这些工具。在开始这个故事之前,我假设您已经了解了 Python 和 Jupyter-Notebook 的基础知识。
有了一些例子,一切都好了🏆所以,我选择了数据集早餐麦片,这是它的样子。
作者图片:早餐谷物数据集一瞥
没有花更多的时间介绍,我直接进入 5 个工具池,以加快您的数据分析
让我们导入所有需要的包,并用下面的代码块将数据读入 Jupyter-Notebook
import pandas as pd
import df= pd.read_excel("cereal.xlsx")
没有熊猫概况的统计数据
数据分析过程从理解可用数据开始。这使您能够了解变量的数据类型、缺失值、每个变量或列中的唯一值,以及数字列的平均值、标准差、最小-最大值等基本统计数据。对于这些操作,熊猫概况可能是最好的解决方案。然而,根据 stackoverflow.com 的评论,它的安装并不简单。
所以,让我们坚持熊猫的基本功能🎯
用户定义的函数⚡️将在这里帮助你,你可以在你的项目的任何后期阶段修改这些函数。
这就是你如何把所有用户定义的函数组合起来。
def fast_intro(df):
print("------Summary-----\n")
print(df.info())
print("\n------Descriptive and Quartile Statistics------\n")
print(df.describe())
print("\n------1st 10 rows------\n")
print(df.head(10))
print("\n------last 10 rows------\n")
print(df.tail(10))
print("\n------Random 10 rows------\n")
print(df.sample(10))fast_intro(df)
输出看起来会像这样—
作者图片:上述代码的统计输出示例
🏆将所有基本功能组合在一个功能中的优势:
- 它让你的代码保持干净
- 它提高了代码的可读性
- 它将所有的统计数据保存在一个地方,可以通过一条线访问
📌您可以创建多个这样的函数,并将它们全部放在同一个 python 文件中。这个文件可以是你的自定义包,它可以被导入到任何项目和功能可以直接使用。
可视化缺失值和缺失号
原始数据经常遇到的一个问题是*缺少值。*如果数据集在特定的行-列对中没有任何值,则它有一个缺失值。当这样的数据集被读入 pandas 数据帧时,缺失的值用NaN
表示
作者图片:缺失值就是这样读入熊猫数据帧的
对于这个特殊的例子,我创建了另一个数据集——missing _ gratey . xlsx
熊猫提供功能如熊猫。DataFrame.isnull(),熊猫。DataFrame.isna()来标识空值。然而,软件包**missingno**
将整个功能提升到了一个新的水平。它可视化了数据帧中缺失值的分布。
同样,第一次使用时,你需要安装missingno
包
pip install missingno
安装完成后,只需将其导入到您当前的笔记本中,您就可以开始探索了
import missingno as msno
%matplotlib inline
让我们来看看这两个missingno
可视化
msno.matrix(df)
作者图片:数据框中缺少值
在上图中,数据集中的每一列都有一个绿色列,白色水平线表示缺失的值。
软件包missingno
的另一个可视化工具是热图——它显示了两列之间的缺失关联。相关值可以解释为,
value = 1 :如果一个变量出现,那么另一个变量很可能会丢失
value = -1 :如果一个变量出现,那么另一个变量最有可能出现
value = 0 :缺失值的出现之间没有依赖关系
包missingno
中的热图就是这样实现的—
msno.heatmap(df)
作者图片:缺失关联热图
🏆仅仅一行代码就能给你如此有趣的可视化效果,并使你的数据分析更快。只需一句话就能提供大量信息,帮助您快速⏰决定处理缺失值的策略。
对如何直接从网页中收集数据感到好奇??这是一篇关于网络抓取的有趣的 4 分钟阅读材料。
从网页中提取数据并将其存储到 excel 中—只需 4 个简单的步骤
towardsdatascience.com](/web-scraping-make-your-own-dataset-cc973a9f0ee5)
与 Plotly 和袖扣的互动情节
除了简单的静态图表和熊猫数据框的功能.plot()
,你还可以使用袖扣包包含互动和吸引人的图表。
Plotly 是袖扣的先决条件,需要在安装袖扣之前安装。同样,这些安装只是首次使用的一部分。
pip install plotly
pip install cufflinks
一旦这些包进入你的系统,你需要做的就是用下面的语句导入它们
import cufflinks as cf
import plotly.offline
cf.go_offline()
一旦包被导入,就该开始了-
df.iloc[:,0:4].iplot(kind ='scatter', x ='name', mode ='markers')
这是你的输出结果
作者图片:带袖扣的散点图
📌只需将光标移动到不同的散乱点上,就会弹出上图红框所示的数据标签。类似地,所有其他的绘图、图表都可以实现。
🏆您不需要在代码中添加任何额外的语句来获得这样的交互式绘图,它使您的数据可视化从其他 Matplotlib 图表中脱颖而出。
带有 Seaborn 的两两二元分布
一旦处理了丢失的值和其他与数据质量相关的问题,下一步就是探索性数据分析(EDA)。它揭示了隐藏的模式,单个变量的分布(单变量分布,以及两个变量之间的关系(双变量分布)。
seaborn 软件包中的 Pair Plot 是一个方便易用的工具,可以用来理解单变量和双变量分布。
让我们看一看——
import seaborn as sns
sns.pairplot(df)
在谷物数据集中,配对图看起来像这样—
作者图片:Seaborn Pairplot
在上面的配对图矩阵的对角线上,可以看到单变量分布,所有非对角线条目显示不同列对之间的双变量分布。
📌只需选择您想要分析的变量,将其传递给sns.seaborn()
,并在几分之一秒内获得如此有趣的可视化效果。
🏆快速配对包seaborn
中的图⏰创建强大的数据可视化,以识别数据集中的趋势。
创造性地使用降价——使用警告框
Markdown 是一种类似于 HTML 的标记语言。在任何数据分析项目中,markdown 用于讨论分析流程,在 Jupyter-Notebook 中生成报告,以及在 markdown 文件(如 README.md)中创建项目报告📋
❓:但是如果每个人都使用相同的减价格式,那么你的数据分析怎么会比别人看起来更好呢?
🏆这里有三种创造性的方法来使用减价文本,让你的分析看起来更有创意。在创建这样的警告框之前,将一个单元格转换为 markdown 并执行下面提到的代码。
- 红色警报框
此框用于表示危险或重要性,如代码中的重要声明。
<div class="alert alert-block alert-danger">
<b>Note:</b> This block is used to denote the Danger
</div>
作者图片:红色警戒框
2.绿色警告框
此框用于表示成功,如成功执行一段代码。
<div class="alert alert-block alert-success">
<b>Note:</b> This block is used to denote the Success
</div>
作者图片:绿色警示框
3.黄色警告框
此框用于表示警告,如需要安装额外的软件包或在应用功能前处理缺失值。
<div class="alert alert-block alert-warning">
<b>Note:</b> This block is used to denote the Warning
</div>
作者图片:黄色警告框
4.蓝色警报框
这个框用来表示提示,注释等关于分析的附加信息。
<div class="alert alert-block alert-info">
<b>Note:</b> This block is used to denote the Information
</div>
作者图片:蓝色警告框
总结一下,
为了满足对数据驱动的洞察力的持续需求,需要尽可能快地分析原始数据。更快、更有洞察力的数据分析意味着有更多时间专注于其他任务。考虑到这一点,我向您展示了 5 个现成的代码片段,以即兴发挥您的数据分析。
感谢您的时间和阅读!
我希望这些数据分析技术对你有用。请随时添加您的反馈。
新冠肺炎数据平滑的 5 种错误方法
新冠肺炎的大部分数据分析是基于有缺陷的平滑技术
您可能认为原始数据比平滑数据更准确。但在新冠肺炎疫情的情况下,平滑数据减少了报告异常,比原始数据更准确地表示了时间。但前提是平滑处理正确。
错误方法 1:不使用平滑数据来揭示趋势
原始的州级数据是嘈杂的,很难看出原始数据的趋势。以下示例显示了来自夏威夷的当前原始数据报告。浅蓝色的线代表阳性测试,红色的线代表死亡。
测试是上升还是下降?从这种数据描述中几乎无法判断。
相比之下,下图告诉你阳性测试目前是上升还是下降了?从视觉上看,很明显,大约一周以来,阳性测试一直持平或略有增加。
错误方式 2:不使用平滑数据来减少州数据校正的影响
大多数州已经在疫情过程中对其数据进行了校正,并且在许多情况下,州在一天之内将数周或数月的校正值全部转储到数据库中。
纽约的数据(下图)包括了他们对 5 月初死亡数据的一个修正(高红线)。如果你从字面上理解这个数据,一天内有 1000 人死亡。但是 1000 个人并没有真的在一天内死去;纽约刚刚报道了一天内如此大规模的调整。这种峰值极大地破坏了对包含峰值的时间段的分析。
平滑后的数据(如下)仍然会受到这种校正的影响-您可以从 1000 死亡校正中看到 5 月份的驼峰-但是平滑后的数据受峰值的影响较小。
错误方式#3:使用 7 天以外的平滑周期
许多州并不每天报告结果。许多国家显示出在某些日子少报而在其他日子多报的周循环。例如,我所在的州(华盛顿)在周日少报,然后在接下来的几天多报。你可以在下图中看到华盛顿每周少报和多报的节奏。
短于 7 天或长于 7 天的平滑期会对少报或多报结果的天数造成不成比例的影响。以下是全国范围内工作日与趋势的典型偏差:
作为为什么需要 7 天周期的一个例子,假设对死亡数据的平滑是在周六到周一的 3 天基础上完成的。在这种情况下,5 天期间将是趋势的 71%,因为周日和周一的典型漏报。
类似地,如果对从星期二到星期四的 3 天进行死亡平滑,那么这 3 天将是趋势的 124%。需要包括整整 7 天,以获得该周数据的准确情况。
从视觉上看,这表现为平滑后的数据看起来不太平滑。以下是最近美国 7 天的平滑数据,看起来相当平滑:
这是在 5 天基础上平滑的相同数据,看起来不是很平滑:
您可能会认为 7 天平滑比 5 天平滑更平滑的原因是因为它的周期更长。这是不正确的。长于 7 天的周期具有与短于 7 天的周期相同的问题:它们重复计算低于平均值或高于平均值的天数,因此降低了准确性。以下是 9 天平滑后的相同数据:
9 天平滑比 5 天平滑,但比 7 天粗糙。平滑问题是每周周期,而不是纯粹的天数本身。
7 天的倍数的平滑周期没有这个问题。7、14 和 21 天的平滑周期都可以是精确的。
错误方法#4:使用滞后平滑周期
一些平滑技术使用 7 天平滑期,但是它们在滞后的基础上计算 7 天平均值。换句话说,他们通过平均天数 n 、 n-1 、 n-2 、 n-3 、 n-4 、 n-5 和 n-6 来计算天数 n 的值。
在回顾的基础上进行平滑意味着数据的平均值基于 3.5 天前的中点。声称显示第 n 天的数据实际上显示的是第 n-3.5 天的数据。
在显示原始数据和平滑数据的图表上很容易发现这种现象,如下图所示:
如果你研究一下图表,你会发现平滑线滞后于原始数据线。波峰和波谷有 3-4 天的时差。如果平滑处理得当,平滑线将位于原始数据行的正上方,如下所示:
7 天平滑需要基于记录日期前 3 天、记录日期、记录日期后 3 天,例如基于天数 n-3 、 n-2 、 n-1 、 n 、 n+1 、 n+2 、 n+3 。
错误方式 5:没有有意识地处理最近 3 天的事情
如果记录的日期是今天,这意味着第 n+1 、 n+2 和 n+3 天还没有发生。我们丢失了 3 天的前瞻性数据。
同样的问题也适用于昨天,它丢失了 2 天的前瞻数据,以及前天,它丢失了 1 天的数据。
因此,我们需要一个平滑最近 3 天的计划,因为只有部分平滑数据可用。有几个选项可用:
- 将今天的数据预测到未来 3 天,并根据预测进行平滑处理。
- 当你没有展望未来的日子时,换成回顾过去。今天使用最近 7 天。昨天使用今天加上最近 6 天。前天使用今天、昨天和最近 5 天。之前的天数使用正常的 7 天平滑。
- 最近几天基于部分时段而非 7 天时段的平滑。今天是根据今天加上之前的 3 天进行平滑的,总共 4 天。昨天是基于 5 天平滑的。前天是按 6 天平滑的。之前的所有日子都可以用正常的 7 天来平滑。
- 不要提供最近 3 天的平滑数据。
最后一种方法是最正确的,但是它限制了利用最近几天的能力。
前三种方法有可能在最近几天的平滑中引入误差。但是,这些错误是暂时的,随着完整数据的出现,它们将在接下来的 3 天内得到纠正。
这方面的失败模式不是有意识地选择最适合这种情况的方法。制定一个计划,并仔细考虑当你的前瞻时间用完时,转向向前预测、向后看或使用不完整数据的影响。
摘要
平滑处理得好可以提高新冠肺炎数据的准确性和可用性。平滑处理得不好会给数据带来误差。
不正确的平滑技术可能是新冠肺炎数据分析的盲点。幸运的是,这个特殊的盲点很容易纠正。
更多详情请访问新冠肺炎信息网站
我领导的团队将 CovidComplete 的预测数据输入疾病预防控制中心的集合模型。对于这些图表的更新,更多的图表,美国和州级的预测,以及预测评估,请查看我的新冠肺炎信息网站。
我的背景
在过去的 20 年里,我专注于理解软件开发的数据分析,包括质量、生产率和评估。我从处理噪音数据、坏数据、不确定性和预测中学到的技术都适用于新冠肺炎。
使用 Alpha-Beta Estimator- Python 修正您的测量值
用 python 实现的数学模型,用于追踪位置和减少数据中的噪声
什么是阿尔法-贝塔滤镜?
就其核心而言,alpha-beta 滤波器是一个简单而强大的估计器,它使我们能够修改样本数据以减少噪声的影响,并准确确定样本的真实值,同时还使我们能够预测其下一个值。
它主要用于跟踪飞机、温度、位置等。它与卡尔曼滤波器密切相关,但它们有其核心区别,我们不会在本文中深入探讨,如果您对这个问题感兴趣,请发表评论。
演示问题
假设我们想要准确预测飞机的位置,但我们的输入中有采样噪声,必须提出两个问题:
- 我们怎样才能消除噪音?
- 平面的速度在样本之间是半常数,我不能只用一个线性函数吗?
让我们用一些图来表示这个问题,我们正在追踪一架在完美的 sin(x)波中飞行的飞机,这是原始数据,以及他的运动在一个线性函数中会是什么样子
蓝色—原始数据,绿色—线性函数
现在这是错误的,你可能会说,你可以做得更好,我可以从每两个蓝点创建一个线性函数,更准确地表示平面运动函数。虽然这是真的:
蓝色为平面数据,红色为点与点之间的线性函数
这没有考虑到两个问题
- 将使我们的衍生产品变得无关紧要的少量数据
- 噪音数据
左边—少量数据,右边是有测量误差的数据
这是非常明显的,即使 sin(x)导数在低数值时接近于线性线,但由于测量误差,一些红线完全倾斜。
该算法
这个算法本身非常简单,它被称为 alpha-beta 是有原因的,它有两个参数,alpha 和 beta,每个参数控制其他东西。
这个想法是预测平面的下一个点在哪里,与平面的样本点进行比较。
- α将是量化测量误差的因子
- β将是量化速度调整的因素。
β和α应该在 0 和 1 之间,否则,它会放大误差效应,α和β没有正确或错误的数字,只有符合你的数据的数字。
选择α和β
更快变化的模型将需要更高的β,以适应其新的速度,而具有高β的高噪声数据会损害估计器的准确性。
有噪声的数据也需要减少α,以减轻噪声的影响。
在实践中
我们用(δ时间*速度)+上一个位置来预测下一个位置。
然后我们假设我们的上一个速度,也是我们的当前速度。
然后我们计算样本位置和预期位置之间的误差。
- 最后,我们使用 Alpha 来减少误差
- 修正速度的β+误差
实践中的代码— Python
我已经写了一个类来演示这个算法的实现,注意,由于图形的原因,我已经记录了一些不需要的数据,你可以减去这些数据。
结果
首先,让我们创建一个示例数据列表:
然后让我们启动一个跟踪器,并将所有样本添加到跟踪器中。
最后,让我们看一些图表,看看它看起来像什么:
红色-实线,蓝色-估计值,绿色-下一个数据点之前的预测
蓝线是我们的估计,几乎完全精确到我们的数据,没有任何干扰!
我们可以看到一些东西:
- 如果你有大量的数据,开始的速度没那么重要,否则,你可能不得不用一个激进的β值
- 估计器和真实位置之间的距离相对较小。
- 除了主要的速度变化,该模型的预测能力非常好,有了大量的数据,或者优化 beta 和 alpha,你可以获得更好的结果。
最后,我还想说明估计量也更接近真实值,即测量误差
蓝色-我们的估计,绿色-样本,红色-纯数据
我们可以看到,我们已经改进了样本数据,并且估计目标的真实位置更加接近其真实值。 简单一句话——蓝色比绿色更接近红色
在所有这些之后,我们有:
- 比样本数据更好地估计目标的位置
- 每个给定时间的目标当前状态、速度和位置
- 在给定时间准确预测下一个样本位置的能力。
最后的想法
阿尔法贝塔过滤器,是一个简单而有效的估计器,跟踪随时间变化,对一个不固定的数学模型,它让你消除误差,预测估计器状态的未来结果,在一个非常简单的方式。
我希望你喜欢它!
修复损坏的 Ubuntu GUI
从不良驱动程序安装中吸取的教训
亚历克斯·库利科夫在 Unsplash 上的照片
我经常在 Windows 和 Ubuntu 之间切换。我最近注意到,我有一段时间没有使用我的 Kubuntu PC 了,在这段时间里它积满了灰尘。我决定加快速度。受够了 Kubuntu,我换到了 Ubuntu 20.04 ,安装起来并不那么痛苦,你知道 GUI 支持,等等。
后来,当安装速度太慢时,麻烦就来了。在修复这些错误的过程中,我经历了太熟悉的 NVIDIA,light-dm 问题,这是我作为 Ubuntu 新手必须经历的几次。
我决定把它一劳永逸地记录下来,给每个像我一样饱受 Ubuntu 新安装之苦的人。
SLOO…ooo www Ubuntu?
这可能是因为 Ubuntu 在新安装时默认使用 XOrg 驱动程序。您可以在“附加驱动程序”下的软件更新应用程序中看到这一点。如果选择 Xorg(截图第三个选项),很可能不是最好的。(在我的例子中,使用了大部分 CPU)。
作者截图:请原谅我的德语,但你仍然可以从他们的地理位置中识别选项
为了解决这个问题,您现在必须:
- 安装显卡的专有驱动程序(我的是 NVIDIA GeForce GT)
- 为您的显卡找到最新稳定版本的驱动程序并安装它们
(你一试 1 就知道要做 2 了,失败了(!)
如果我为你工作——太棒了,做一个
sudo reboot
你可以走了!
如果您的驱动程序安装失败并且 GUI 崩溃
如果您的 GUI 由于某种原因挂起,或者进程没有成功完成,切换回 Xorg 并重新安装稳定的驱动程序。如果你不这样做,你的 GUI 在重启时会变得一团糟。
在我的例子中,重启失败了。它把我带到一个空白屏幕上闪烁的光标,就像这样:
:(图片作者
为了走出这个深渊,按 ctrl + F1(或者 F2,3,4,5,6,7)直到其中一个起作用。
ctrl + F1
您将从那里进入一个常规的命令行界面。使用您的用户名和密码登录。然后,执行以下命令:
sudo apt-get purge lightdm
sudo apt-get update
sudo apt-get install lightdm
dpkg-reconfigure lightdm
sudo reboot
这是因为 Ubuntu 的显示是由 lightdm 包管理的,失败了。您正在清除并重新安装它以修复这些问题。
当您重新配置 lightdm 时,系统会询问您是想继续使用 lightdm 还是换成 GDM3。我留在了 lightdm,因为我更熟悉(网上很多人抱怨 gdm 是一个管理 NVIDIA 驱动程序的经理)
作者截图:lightdm 重新配置
此时,我欣喜若狂,因为一切似乎都正常,我来到了我的登录屏幕。
但是当我输入我的证书时,我的电脑决定一次又一次地询问我的证书。我陷入了一个登录循环!!
注意:如果您的 dpkg 重新配置在这一行失败,
dpkg-reconfigure lightdm
运行这个,你应该没问题:
sudo dpkg --configure -a
登录循环和出路
如果你不能登录到你的帐户,但图形用户界面是回来了,你必须修复你的驱动程序从命令行(你打破了他们在第 1 步!!!)
所以现在,按 Ctrl+F3 重新控制你的命令行。(使用 Alt+箭头键,可以在屏幕之间切换:登录、命令行等。).使用您的凭据登录。
Ctrl+F3
这里有两个可能的故障源:
- 。x 权限混乱
- NVIDIA 驱动程序被搞乱了
在我们的案例中,我们知道是司机。如果您想检查您的权限,请执行以下操作:
ls -lA
现在寻找这条线:
-rw — — — — 1 user user 79 Sep 3 19:56 .Xauthority
如果不是您的用户名必须在哪里,您会看到:
-rw — — — — 1 root root 79 Sep 3 19:56 .Xauthority
你必须:
chown username:username .Xauthority
你有你的权限,你可以登录!
如果这不是问题所在,请重新安装驱动程序:
sudo add-apt-repository ppa:graphics-drivers/ppa
sudo apt update
现在寻找可用的驱动程序:
作者截图
我用的是第三方免费推荐的驱动。安装它(替换最后的你的-Nvidia-driver-这里的与它的实际名称从上面的结果):
sudo apt install your-nvidia-driver-here
如果这成功了,不仅你的 GUI 回来了,而且你的显卡硬件也有了更好的驱动程序!
做一个:
sudo reboot
这个应该行!
我的电脑现在工作起来就像一台普通的电脑,并且不需要太长时间就能启动。如果你有类似的问题,希望这有所帮助!
修正新冠肺炎病例数和死亡人数漏报
现实世界中的数据科学
或者,为什么有些国家的数字可能是报告数字的 10 倍
由 Shutterstock 上的 Corona Borealis 工作室制作
在这个充满挑战的时代,我们中的许多人不断被新冠肺炎新闻中相互矛盾的更新和统计数据所淹没。你可能已经看到了最近的头条新闻,如*、【武汉再访其电晕死亡人数高达 50%】、为什么、流行病学家仍然不知道新冠肺炎的死亡率。*
这种含糊不清和相互矛盾的信息会导致沮丧、焦虑、困惑和日益增长的恐惧。作为一名科学家,我发现获得更清晰的理解有助于我们从恐惧转向有意识的行动。为了帮助解决这个问题,我创建了一个简单直观的应用程序(可在https://pharmhax.shinyapps.io/covid-corrector-shiny获得)和一个演练来帮助你理解这些数据:
- 首先,我将为你描绘一幅图画来解释我为什么发展这个方法。也许你会认同这个思考过程,也许你不会,但围绕它的潜在现实我们都能认同,因为,从今天起,它已经变得不可避免。
- 第二,我会告诉你为什么不幸的是,这些数字可能比报道的要高得多。
- 第三,我会告诉你如何确定这些数字的精确度。
- 第四,我将带你浏览正确的数字,并为你指出我为你创建的应用程序,以便你能够自己检查这些数字——这里需要注意的是你不需要任何专业知识来使用应用程序,你需要的只是一个浏览器(或者你的拇指,如果你坚持在你的手机上使用仪表板风格的应用程序)。我还分享了 GitHub 库,以防您想使用代码或扩展它。
- 第五,我会请你将这篇文章分享给你的网络,如果你知情的话,请在data @ neuro synergy . ai分享你可能拥有的关于漏报的任何数据。这将提高本文中提出的纠正方法的准确性,以便我们作为一个群体能够更好地准备和应对。
低细节渲染
商店里的长队、戴口罩的指南、呆在公寓里的要求以及社交距离——你已经看到了这些,但当你看到报告的冠状病毒感染数量(取决于你所在的位置)时,你可能会得出结论,你周围应该只有极少数人可能患有冠状病毒,所以为什么要大惊小怪呢?
重要的是要明白,忽视这些指导方针确实是危险的,在我看来,问题的关键是看冠状病毒病例(以及死亡人数)是否被低估了。如果确实有更多的人被感染,你可能会开始明白为什么出现在公共场合的风险比看起来要高。
剧透:这些数字确实比报道的要高得多,所以如果你只读到这一点,请呆在家里……休息的时候一定要读完剩下的部分!
好的。这幅画已经被勾勒出来了,至少是大致的轮廓。让我们进入更深层次的细节。
新冠肺炎——2020 年(或许也是 2021 年)的病态表演秀
你可能最近看到了很多关于冠状病毒的文章,特别是关于它可能被严重低估的文章。我在上面给你留了一些链接,但如果你想看到更多,只需在谷歌中键入“冠状病毒漏报”。你会惊讶于这种漏报的范围有多广。那么,如果漏报不是秘密,为什么对此无所作为,为什么我们看不到 T2 真实的 T3 数字?
一个原因是真实数字根本无法测量。
罗纳具有很强的传染性,R0(将从受感染个体身上感染病毒的人数)估计为 2-3,随着激烈的讨论,这个数字甚至会更高——根据洛斯阿拉莫斯国家实验室的一个小组最近的一篇文章,它接近于 5。无论如何,它比 R0 约为 1.3 的普通流感要高得多。
但是为什么我们甚至没有新冠肺炎 R0 的精确估计呢?其背后最可能的原因与导致冠状病毒病例和死亡病例系统性漏报的原因相同。让我们来看看其中的一些。
遗漏报告的一个主要原因是新冠肺炎普遍无症状。疾控中心主任提供的无症状携带者(仍具传染性)比例的最近估计为 25%。无症状携带者不会注意到他们的感染,因此不会对报告的病例数有所贡献,所以这些病例数被不适当地降低了这个分数。然而,根据发表在《科学》上的一项研究,数量惊人的冠状病毒病例被认为是轻微的,以至于大约 85%的病例轻微到不值得医生就诊,导致这些病例没有记录。85%!从表面上看,这确实是一个令人毛骨悚然的数字。这第一个(巨大的)漏报来源将主要影响病例的报告,以及冠状病毒动力学(如 R0)的估计,但它不会影响报告的死亡人数,因为这些将来自最严重的病例,最有可能在急诊室结束。
我们现在转换话题,关注死亡报道。冠状病毒导致的死亡确实可能被低报,这是因为次要的死亡原因、当地的报告做法以及更多的各种情况。
让我们从上面最后一个也是最深奥的类别开始,死亡漏报杂记。《华尔街日报》的一篇文章提供了一个例子,它讲述了意大利养老院发生的几起大规模死亡事件,多达三分之一的住院医生在 3 月份死亡,而这些死亡事件中没有一例是由冠状病毒引起的。其他各种各样的例子将源于发生在家中的死亡——这些似乎也普遍报道不足——以及由于测试短缺而没有进行冠状病毒测试的患者的死亡,因此没有被报道为 Covid 受害者。
次要死因是冠状病毒相关死亡漏报的另一个原因,其中死亡可归因于下游原因,如呼吸衰竭,而不是新冠肺炎作为呼吸衰竭背后的原始原因。
当地报告惯例可能是未报告 Covid 死亡的另一个来源。一个例子来自波兰,在波兰,用于报告冠状病毒死亡的两种医学代码中只有一种被指定用于政府发布的官方指南——U07**. 1**,这是 ICD-10 代码,表示通过实验室检测确诊的新冠肺炎病例,而 U07 .2 ,这是用于因临床表现而怀疑感染冠状病毒的患者的代码,不在官方指南范围内。在缺乏临床测试的情况下,大胆猜测一下这两种情况中哪一种更常见。这样,所有本应通过 U07 .2 ICD 代码确诊死于冠状病毒的患者都不会出现在官方报告的数字中。
好的,冠状病毒病例报告不足,冠状病毒死亡也是如此——我们能计算出真实的数字吗?
怎么做
本节将详细介绍方法,即支持我们应用程序的代码如何纠正报告的数字。如果您现在想跳过“如何做”并看看这些方法的实际应用,请随意进入下一节,在这里将纠正方法应用到报告中。
几周前,我在 LinkedIn 上偶然发现了 Lachmann 等人的一篇非常有趣的 medRxiv 预印本,它专注于本文概述的任务的一部分:纠正报道不足的新冠肺炎案件数量(正如预印本标题本身所揭示的)。
预印本是奇怪的东西。我当时下载的文章版本已经被取代,3 月 31 日上传的文章的当前版本具有非常不同的重心,作者专注于插值和住院建模,而 3 月 18 日的文章版本(仍可在此处访问)专注于开发一种简单的方法,通过将该国的人口统计数据和死亡率与参考国家的人口统计数据和死亡率进行比较,来校正任何国家的报告数字。“死亡率”中的单词 rate 在这里是有效的。潜在的想法是,在任何两个拥有相似医疗保健和相似人口分布的国家,真实的死亡率应该是相同的。
由于死亡率是死亡人数除以病例数,如果我们对病例的了解不全面,那么我们对死亡率的计算就会出错。如果我们对死亡有不完全的了解,这同样适用,但是理论上这部分比案例更难错过。正如我们在上一节中了解到的那样,我们之所以走到这一步,首先是因为全球的病例和死亡报告并不完善,但事实证明,韩国非常善于进行冠状病毒测试,提供良好的患者护理,并记录与 Covid 相关的病例和死亡,从而提供了尽可能好的基准。
Lachmann 等人提出的校正系数是基于参考国家的平均死亡率乘以校正国家和参考国家的脆弱性比率,其中每个国家的脆弱性是按年龄分层的公民总数乘以该年龄人群中观察到的死亡率。如果你感兴趣的话,手稿第一版的 C 部分给出了描述这些量及其关系的方程。
太好了,现在我们有一种方法来纠正未报告的病例数——这种调整使我们的数字相对接近于如果某个国家的检测实践(以及对冠状病毒的整体反应)像韩国一样好,那么在该国可以检测到多少例*。然而,这还没有考虑到其他非常难以解释的因素,例如无症状(或非常轻微)的病例。*
下一步,为了考虑其他因素并进一步调整病例数以及死亡报告数,我们将使用一个简单的乘数。这个乘数将不是一个单点,而是一个完整的高斯分布,这意味着我们对校正后的报告和死亡人数的估计也不再由每个日期的一个点来表示,而是所有可能值的集合。
这个乘数的平均值及其标准偏差目前是相当随意的。但今后我们将把它们视为贝叶斯先验,一旦我们有了某个国家未报告的病例和死亡比例的数据,我将使用贝叶斯定理来组合这些数据,并得出每个国家倍增校正因子的更加现实的后验估计。
应该指出的是,还有其他非常有希望的方法来纠正死亡报告数字,包括使用在特定时间特定地区的预期死亡人数的偏差,这些数字目前都可以归因于新冠肺炎死亡。经济学家已经使用了这种方法,我将仔细跟踪它,因为它最终可能成为一种数据来源,可以合并到这里讨论和显示的方法中。
更正报告
上述方法 Lachmann 等人的病例数校正和高斯乘数校正(一旦漏报数据可用,这将在未来被视为更准确的贝叶斯校正之前使用)—在我看来,如果有一个应用程序来查看每个国家的数字,应用校正,并立即可视化估计值,那就太好了。所以我做了一个闪亮的应用程序来做这件事!就是这里:
*【https://pharmhax.shinyapps.io/covid-corrector-shiny/ *
这里是 GitHub 库:
https://github.com/pharmhax/covid19-corrector
应该指出的是,代码,参考修正方法,和可视化是基于拉赫曼等人的预印本。
让我们直接进入可视化,从快速浏览美国和韩国未经修正的原始病例和死亡数字开始:
请注意,左边的 y 轴表示病例和恢复情况,而右边的 y 轴表示死亡情况
我们可以看到,美国的死亡率没有韩国稳定:
参考国家(韩国)用灰色表示;被更正的国家/地区(美国)为红色
这种不稳定性或许是美国修正病例数图的一个充分预兆:
~ 550 万箱,550 万!不是 100 万——我希望现在更清楚为什么你应该保持社交距离并戴上口罩。
现在,让我们首先假设只有大约三分之一的死亡被报告,并对死亡和病例进行统计上的 3 倍乘性校正:
也就是大约 1640 万例(95%置信区间:1100 万—2200 万)和 16.98 万例死亡(95%置信区间:116.2 万—223.4 万)。需要指出的是,乘性修正是在没有任何数据的情况下应用的,所以在我们有数据让统计修正更真实之前,这些估计值还是要带着一桶盐的!
现在让我们看看波兰作为一个代表性国家,其指南中不包括(主要的)U07 .2 病例和死亡:
约 11.9k 的报告病例已被更正为约 51.3k,这是一个显著的增长,但与我们在其他国家看到的情况相比,这个数字似乎相对较低(按人口比例)。同样,我们将首先假设只有大约三分之一的死亡病例被报告,并对波兰的死亡和病例进行统计上的 3 倍乘性校正(这对于病例来说实际上可能是保守的,因为我们还遗漏了无症状的患者!)并查看调整后的数字:
我们的病例经历了从大约 51.3k 预测病例到大约 155.3k (95%置信区间:102.5k-204.2k)的急剧增加,从大约 560 例报告死亡到大约 1,680 例(95%置信区间:1,080-2,220),因此在每种情况下增加了大约 3 倍,正如我们在校正中使用的设置所预期的那样。
让我们来看看意大利:
**
这些数字令人震惊——使用韩国的数据将目前估计的略低于 20 万的病例数调整为几乎 10 倍,即 176 万。使用乘法进行的进一步调整(其中我们乘以 5,因为意大利的医疗保健系统在这场危机中尤其不堪重负)显示,累计病例数可能高达 880 万(95% CI:560 万-1240 万),死亡人数为 13.49 万(95% CI:80.9 万-188.7 万)。
最后,让我们看看中国:
**
中国报告的病例徘徊在 8.4 万左右,最近更新了死亡人数,从< 3.5k to ~4.6k (this sudden uptick can be seen across the plots above). After adjustment the number of cases raises almost 10-fold, to ~790k. Due to the recent articles, we can assume that the underreporting of both cases and deaths in China is widespread, so we apply a multiplicative correction of 5. This shows us that there may be easily have been as many as 4M cases (95% CI: 2.5M — 5.5M), and 23k deaths (95% CI: 14k — 32.5k) — a number that, according to recent headlines might be still understating the true magnitude of the crisis in China. This example provides a good illustration to the fact that data is badly needed to make this corrective measure more accurate and enable it to capture the nuances of reporting specific to each country on a case-by-case basis.
From Priors to Posteriors — you can help make the estimates more realistic!
As mentioned in passing earlier, the multiplicative Gaussian correction presented here lends itself well to the Bayesian paradigm. Without going into excessive mathematical detail, we can use the Bayes’ theorem roughly as follows:
P(m|D)=P(m)P(D|m)/P(D)
其中 P ( m | D )是我们观察到真实生活数据中未报告的病例和/或死亡比例后乘数的后验估计, P ( m )是我们的预先数据先验估计(目前在应用程序中使用),P(D|m)是被观察的可能性
我敢打赌你已经看过无数次了——如果你没有看过,你可能会知道这其中哪个部分是至关重要的,但却是缺失的。是数据, D !
如果您有任何数据,即关于冠状病毒导致的病例比例或死亡比例未报告的信息,请通过 data@neurosynergy.ai 与我们联系。此外,请与您的网络分享这篇文章,尤其是与医疗保健专业人员,因为他们可能对未报告的部分有宝贵的见解。这将有助于将本文中介绍的简单乘法校正器变成现实的统计校正器。
目前就这些。保持健康,记住——保持社交距离和戴口罩是有意义的(这些数字,尤其是在适当纠正的情况下,就是 显示的)。
本文表达的观点和内容仅属于我个人,不代表我的雇主的观点或意见。
最初发表于神经协同。
编者注: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
修复数据文件-添加缺失的日期列
如何用 python 在 4 个快速步骤中为多个文件添加日期?
你有一整个文件夹的文件准备分析。仔细看,你会发现每个文件名都有日期,但记录上没有相应的日期。要将数据加载到数据框或表中,您需要一些日期或时间戳来维护记录的顺序。内心深处,你在呻吟。怎么办?
您可以使用 python 读入文件,并根据文件名日期添加一个新列。
方案
您需要创建 2020 年 10 月第一周糖果销售的可视化。
这些文件是每天创建的,包含不同的糖果和销售量。
这些文件位于一个文件夹中。文件名因销售日期而异。
解决办法
如果缺少日期列,将日期列添加到所有文件的 4 个步骤
- 使用前面我为文本抓取任务使用的 python glob,我创建了一个文件夹中所有文件的列表。
- 遍历文件列表。从文件名中获取日期。
- 接下来,打开文件并将其读入数据框。添加(或更新,如果已经存在)日期列。
- 将更新的数据写回文件。
运行这个简单的脚本后,文件现已更新:
现在,将这些文件放入一个文件中变得容易多了。我也在剧本中加入了这一点。
# import required packages
import pandas as pd
import glob
import os
import re
import sys# where is your folder?folderPath = '<path to your top folder>'# Find the candy sales files
files = glob.glob(folderPath + '/**/*candy_sales*',recursive=True)viz_df = pd.DataFrame()"""Loop through all the files , remove punctuationn, split each line into elements and match the elements to the list of words"""
for file in files:
basic_file_name = file.replace(folderPath,'')
basic_file_date = basic_file_name[14:22]
# yyyy-mm-dd
#file_date = str(basic_file_name[14:18]) + '-' + str(basic_file_name[18:20]) + '-' + str(basic_file_name[20:22])
# mm/dd/yyyy
file_date = str(basic_file_name[18:20]) + '-' + str(basic_file_name[20:22]) + '-' + str(basic_file_name[14:18])df = pd.DataFrame()
df = pd.read_excel(file, header=0)
df['date'] = file_datedf.to_excel(file, header=True, index=False)
print('Done updating files')# Create a viz file
for file in files:
df = pd.DataFrame()
df = pd.read_excel(file, header=0)
viz_df = viz_df.append(df)
viz_df.to_excel(folderPath + '/viz_file.xlsx', header=True, index=False)
print('Done printing viz file')
一旦你有了最终的文件,把它放到一个 Viz 工具中——比如 Tableau Public。
结论
在短短几分钟内,您已经从一堆无用的无日期记录变成了一个带有时间戳的行文件,以备分析。
修复 Keras 2.3.x 或更新版本中的 key error:“ACC”和 key error:“val _ ACC”错误
如果您的 Keras 代码中出现诸如 KeyError: 'acc '或 KeyError: 'val_acc '之类的错误,这可能是由于 Keras 2.3.x 中最近的一个更改。
在 Keras 2.3.0 中,矩阵的报告方式被更改为与指定的名称完全一致。如果您使用的是旧代码或旧代码示例,那么您可能会遇到错误。以下是修复它们的方法。
您是否使用过由 Keras 的 fit()函数返回的“历史”对象来绘制或可视化您的模型的训练历史?自从最近的 Keras 升级以来,您是否遇到过类似下面这样的“KeyError”类型的错误,并想知道为什么?
Traceback (most recent call last):
File "lenet_mnist_keras.py", line 163, in <module>
graph_training_history(history)
File "lenet_mnist_keras.py", line 87, in graph_training_history
plt.plot(history.history['acc'])
KeyError: 'acc'
尝试读取历史记录对象时出现密钥错误:“acc”
Traceback (most recent call last):
File "lenet_mnist_keras.py", line 163, in <module>
graph_training_history(history)
File "lenet_mnist_keras.py", line 88, in graph_training_history
plt.plot(history.history['val_acc'])
KeyError: 'val_acc'
尝试读取历史对象时出现密钥错误:“val_acc”
这要归功于 Keras 2 . 3 . 0 版本中引入的一个突破性的变化。
根据 2.3.0 发行说明:
"指标和损失现在以用户指定的确切名称报告(例如,如果您通过指标=[‘acc’],您的指标将在字符串“acc”下报告,而不是“accuracy”,反之,指标=[‘accuracy’]将在字符串“accuracy”下报告。
你可以在这里阅读官方发布说明:【https://github.com/keras-team/keras/releases/tag/2.3.0
这意味着,如果您在 model.compile() 中指定 metrics=[“accuracy”] ,那么历史对象将拥有关键字 ‘accuracy’ 和 ‘val_accuracy’ 。而如果您将其指定为 metrics=[“acc”] ,那么它们将使用关键字 ‘acc’ 和 ‘val_acc’ 进行报告。
因此,要修复这个错误,你应该在你的代码中使用一个标准。
您可以使用“acc”,
...
model.compile(loss="categorical_crossentropy",
optimizer=opt, metrics=["acc"])
...
plt.figure(**1**)
# summarize history for accuracy
plt.subplot(**211**)
plt.plot(history.history['acc'])
plt.plot(history.history['val_acc'])
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Training', 'Validation'], loc='lower right')
# summarize history for loss
plt.subplot(**212**)
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model Loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Training', 'Validation'], loc='upper right')
plt.tight_layout()
plt.show()
或者,使用“准确性”。
...
model.compile(loss="categorical_crossentropy",
optimizer=opt, metrics=["accuracy"])
...
plt.figure(**1**)
# summarize history for accuracy
plt.subplot(**211**)
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Training', 'Validation'], loc='lower right')
# summarize history for loss
plt.subplot(**212**)
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model Loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Training', 'Validation'], loc='upper right')
plt.tight_layout()
plt.show()
可视化“修复”后的模型训练历史
只要记住在 metrics=[…]和从 history 对象访问键的地方使用相同的键。
相关链接:
如何在 Keras 中绘制模型训练历史
Keras 2.3.0 发行说明
**注:**早在 2020 年 1 月,我第一次在我的博客上发表了这篇文章。但是我看到你们中的许多人仍然面临这个问题。所以我在这里重新发布它,希望能帮助许多人修复他们的代码。
【https://www.codesofinterest.com】最初发表于。
PyTorch 数据加载器需要翻新
入侵数据科学工作流程
向 PyTorch 数据加载器类添加高度可定制性
我最近遇到了一个有趣的问题。我和一个队友正在进行一系列深度学习实验,这些实验涉及一个跨越数百千兆字节的图像数据集。作为一个优柔寡断的傻瓜,我想知道这些数据是否适合过多的分类任务,所有这些任务都跨越数据集的不同配置。
这让我们在一个 PyTorch DataLoader 形状的兔子洞里走了几个小时,之后我们几乎因为沮丧而放弃。谢天谢地,唯一比编写脚手架代码更令人沮丧的事情是等待虚拟机完成跨任意目录复制大量文件。
您是否曾经为了在不同的标签集上训练 PyTorch 模型而不得不复制/symlink 文件?(图片由 Unsplash 上的jeshouts拍摄)
幸运的是,我们很快发现了一个解决方案,并决定是时候对 DataLoader 类进行一次翻新了。我们将杂乱的脚手架代码清理干净,不仅添加了动态标记训练数据的功能,还添加了指定子集、执行自定义预处理操作以输入数据加载器等功能。几个小时的咖啡因诱导代码后, BetterLoader 诞生了。
如果我说我对自己设计的商标一点都不自豪,那我是在撒谎
BetterLoader 完全去掉了默认的 PyTorch DataLoader 结构。现在,您可以将文件存储在一个单一的平面目录中,并使用 JSON 配置文件的强大功能以不同的方式加载数据。例如,下面几行代码允许您加载带有动态标签的数据集子集:
from betterloader import BetterLoaderindex_json = './examples/index.json'
basepath = "./examples/sample_dataset/"loader = BetterLoader(basepath=basepath, index_json_path=index_json)
loaders, sizes = loader.fetch_segmented_dataloaders(
batch_size=2, transform=None)
酷的部分?我们的index.json
只包含一个键-值对的列表,其中键是类标签,值是相关文件名的列表。然而,读取索引文件的 BetterLoader 函数可以被定制,我已经能够通过正则表达式、布尔条件甚至 MongoDB 调用来使用这个库。
我最新的 BetterLoader 工作流包括检查是否需要加载图像,从 MongoDB 实例获取裁剪中心,创建一堆裁剪,然后将这些裁剪馈送给加载器。我只是使用了 BetterLoader ,而不是每次都创建不同的搭建代码。
所以,是的。那是更好的加载器 — 类固醇 PyTorch 数据加载器。现在还为时尚早,但我们对让我们的生活,希望也是其他数据科学家的生活变得更容易的前景感到非常兴奋。如果你认为 BetterLoader 听起来有用,并且你已经避开了我在本文中散布的所有文档的链接,你可以在 Github 这里找到源代码,在 PyPi 页面这里找到源代码。
我们还将开放大量票证,以增加对无监督深度学习方法的支持,并修复最终会出现的过多问题,因为咖啡因引发的狂欢很少会产生完美稳定的库。我们希望能为我们带来一位明星、参与其中,或者敬请关注!
BigQuery 中的 FizzBuzz
有时你需要一个巨大的托管数据库服务来传递 FizzBuzz
如果你过去做过编程,我肯定你听说过 FizzBuzz 挑战。这是一个简单的编程任务,应该会让应届毕业生措手不及。有很多关于如何用 Java、Python、C 等等语言来做这件事的帖子。但我认为在 BigQuery 中进行会很有趣。
图片来源: pxfuel
挑战如下:
写一个函数或者程序,打印从 1 到 100 的数字,但是如果这个数字能被 3 整除,打印Fizz
,如果这个数字能被 5 整除,打印Buzz
,如果能被 3 和 5 整除,打印FizzBuzz
。
多刺激啊,对吧?
这里有一个朱莉娅版本让我们思考(因为我喜欢朱莉娅:
现在,这个问题已经解决了,让我们从创建数字开始。你可以使用union all
语句,但是那会很快变得很无聊,所以我将使用数组来代替。请注意,这些查询在 BigQuery 中都是免费的,因为我们不查询任何实际数据,我们只是自己动态生成数据。
这使我们得到以下结果:
还有,生活中免费的东西总是更好:
这里发生的事情是,GenerateArray
创建了一个从 1 到 15 的数组,然后我们使用Unnest
到展平数组,并使用数组的每个元素作为列num
的行值。(上面链接了函数的文档。)
现在是嘶嘶作响的部分。我将使用上面创建的表,只是为了使代码更容易阅读,并对其应用一个CASE
语句:
需要注意的事项:
- 我们必须首先检查一个数是否能被 3 和 5 整除(即 15)
- 不可分的数字必须转换为字符串,以确保该列的所有元素都具有相同的类型
这应该可以了。下次他们让你在采访中写 FizzBuzz 的时候,登录 GCP,在 BigQuery 中写!
在 BigQuery 中使用用户定义的 JavaScript 函数来计算 Fibonacci
towardsdatascience.com](/fibonacci-series-with-user-defined-functions-in-bigquery-f72e3e360ce6) [## 将文件更快地加载到 BigQuery 中
针对摄取的 CSV、GZIP、AVRO 和拼花文件类型进行基准测试
towardsdatascience.com](/load-files-faster-into-bigquery-94355c4c086a)
仪表盘教程(一):Flask 和 Chart.js
关于 Flask 和 Chart.JS 的教程。通过代码片段和示例浏览 web 和图表设计。
照片由freepik.com拍摄
当使用 Python 创建 web 时,Flask 通常是开发人员常用的后端。Flask 支持 HTTP 请求的管理和模板呈现。在这篇博客中,我们将介绍 flask 的基本结构,以及 flask 如何从其他网页中渲染模板。此外,您将学习加载一个逗号分隔值(CSV)文件,并使用 Chart.js 将其可视化。
在本文中,您将了解到:
(1)创建一个基本的烧瓶应用程序
(2)图表。JS 图
创建基本的烧瓶应用程序
首先,我们创建一个. py 文件来导入 flask 包并设置 flask 配置。Flask 会从 templates 文件夹中调用 HTML 文件,而不需要任何指定的路径。为了将 flask 指向正确的文件夹,我们指定了模板 _ 文件夹和静态 _ 文件夹路径。在静态文件夹中,它通常存储 javascript、CSS 文件和图像。在模板文件夹中,它通常存储 HTML 文件。此外,我们为终端设置 app.config 等于 True 来显示错误消息。此外,错误消息会在控制台的 inspect 元素下弹出。
**import** flask
app **=** flask.Flask(__name__, static_url_path='',
static_folder='static',
template_folder='template')
app.config["DEBUG"] **=** True
然后,我们继续定义主页的路径。我们能够给出一个可以传入 HTML 的网页名称。 Render_template 函数会调用 HTML 文件并显示在指定的网页路径上。对于 user 的第二个功能,可以将嵌入在 route 中的 name 标签传递到 HTML 中。例如,当给出(http://127 . 0 . 0 . 1:5000/home)时,内容将是“Hello home”。此外,用户可以通过使用重定向函数来分配 HTML 网页的路径,并将网页名称放入 url_for 函数中。
from flask import render_template, redirect, url_for
[@app](http://twitter.com/app).route("/home")
def home():
return render_template("home.html")[@app](http://twitter.com/app).route("/<name>")
def user(name):
return f"Hello-- {name}!"[@app](http://twitter.com/app).route("/admin")
def admin():
return redirect(url_for("home"))
完成上述设置后,您就可以通过启动主机和端口来运行 flask 应用程序了。
if __name__ == '__main__':
app.run(host="localhost", port=8000, debug=True)
图表。JS 图
图表。JS 成为一个流行且强大的数据可视化库。该库增加了用户通过输入某些参数来定制图的灵活性,这满足了用户的期望。从 Chart.js 创建绘图的主要优点是基本的代码结构,它允许用户显示绘图而无需创建许多行代码。
在开始之前,请确保通过调用网页或指定下载文件的本地目录来调用 Chart.js 文件。
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
我们还需要创建一个画布节点来呈现图表,并用其宽度和高度指定绘图的大小。
<canvas id="myChart" width="400" height="400"></canvas>
对于以下使用 Chart.js 的 4 个绘图示例,我主要使用来自加拿大新冠肺炎疫情数据源的流行病学数据。可以通过其链接访问数据。
条形图
首先,我们将通过调用 d3 库来加载 CSV 文件,该文件将被呈现到 makeChart 函数中。接下来,我们用数据参数定义函数,并将数据列映射到某些变量。
d3
.csv(“/file/Bar_cases.csv”)
.then(makeChart);function makeChart(data) {
var country = data.map(function(d) {return d.province;});
var value = data.map(function(d) {return d.cumulative_cases;});
然后,我们通过 **getElementById 调用画布 id 来创建图表。**通过将类型指定为条形(要翻转条形的方向,请将类型设置为 horizontalBar)即可生成条形图。条形图的标签设置为省份名称,数据为 5 月份的累计病例数。您可以通过参数创建图例和标题。
// Bar chart
new Chart(document.getElementById("myCanvas"), {
type: 'bar',
data: {
labels: country,
datasets: [
{
label: "Population (millions)",
backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
data: value
}
]
},
options: {
legend: { display: false },
title: {
display: true,
text: 'Total Confirmed Cases of COVID in May'
}
}
});
通过下面的柱状图,我们可以通过悬停的信息看到信息。在五个省份中,魁北克省感染 COVID 的人数最多,而安大略省是第二高的省份。
图表 1:条形图
折线图
首先,我们需要为线图准备数据集。为了创建时间序列线图,我们需要筛选每个省的每月 COVID19 确诊病例。 OnlyUnique 函数是从一个数组中生成唯一值。 forEach 函数能够通过一个数组上的值和索引的迭代,在多个数组上找到相同的索引。
// get unique value of array
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
var mon_unique = month.filter( onlyUnique );province.forEach((pro, index) => {
if (pro =="Alberta") {
ab_val.push(value[index]);
} else if (pro =="BC") {
BC_val.push(value[index]);
} else if (pro =="Manitoba") {
mb_val.push(value[index]);
} else if (pro =="New Brunswick") {
nb_val.push(value[index]);
}
else {
nl_val.push(value[index]);
}
});
通过将类型指定为线条,可以简单地生成折线图。对于每一行,我们可以输入包装在数据集列表中的标签和数据。默认情况下,每条线下的区域用颜色填充,覆盖该线和 x 轴之间的区域。可以通过指定(fill: false
)来删除颜色。此外,可以用 borderColor 参数指定颜色。
// Line chart
new Chart(document.getElementById("myCanvas"), {
type: 'line',
data: {
labels: mon_unique,
datasets: [{
data: ab_val,
label: "Alberta",
borderColor: "#3e95cd",
fill: false
}, {
data: BC_val,
label: "BC",
borderColor: "#8e5ea2",
fill: false
}, {
data: mb_val,
label: "Manitoba",
borderColor: "#3cba9f",
fill: false
}, {
data: nb_val,
label: "New Brunswick",
borderColor: "#e8c3b9",
fill: false
}, {
data: nl_val,
label: "NL",
borderColor: "#c45850",
fill: false
}
]
},
options: {
title: {
display: true,
text: 'Positive Cases of COVID in provinces of Canada'
},
hover: {
mode: 'index',
intersect: true
},
}
});
从下面的线图来看,除 BC 省外,4 个省在 1 月和 2 月报告了 0 例确诊病例。从 2 月中旬开始,艾伯塔省和不列颠哥伦比亚省的确诊病例逐渐增加。从 3 月开始,艾伯塔省的阳性病例大幅增加,因为 AB 省在 5 个省中名列前茅。另一方面,MB、NB 和 NL 等省报告的确诊 COVID 病例较少。5 月,曲线变平,COVID 案例减少。
图表 2:折线图
圆环图
首先,我们需要为甜甜圈图准备数据集。每个省数组被处理并有五月份累积案例的最终输入。
//donut chart, get the end of MAy & display the cumulative cases of 5 provinces// get unique value of array
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
var pro_unique = province.filter( onlyUnique );
var ab_val = []
var BC_val = []
var mb_val = []
var nb_val = []
var nl_val = []date.forEach((i_dt, index) => {
if (i_dt == '2020-05-31' && province[index] =="Alberta") {
ab_val.push(value[index]);
} else if (i_dt == '2020-05-31' && province[index] =="BC") {
BC_val.push(value[index]);
} else if (i_dt == '2020-05-31' && province[index] =="Manitoba") {
mb_val.push(value[index]);
} else if (i_dt == '2020-05-31' && province[index] =="New Brunswick") {
nb_val.push(value[index]);
}
else if (i_dt == '2020-05-31' && province[index] =="NL") {
nl_val.push(value[index]);
}
});
总共有 6 个按钮。按钮 randomizeData 使数据随机化,同时每个标签的区域可以按比例显示。对于其他 4 个按钮,它们支持添加或删除数据的功能。此外,像半圆/全圆按钮,允许圆扩大和缩小到秋天和一半的大小。一旦每个按钮都被单击,它将调用 Doughnut.update()函数来更新绘图。
<button id="randomizeData">Randomize Data</button>
<button id="addDataset">Add Dataset</button>
<button id="removeDataset">Remove Dataset</button>
<button id="addData">Add Data</button>
<button id="removeData">Remove Data</button>
<button id="changeCircleSize">Semi/Full Circle</button>window.chartColors = {
red: 'rgb(255, 99, 132)',
orange: 'rgb(255, 159, 64)',
yellow: 'rgb(255, 205, 86)',
green: 'rgb(75, 192, 192)',
blue: 'rgb(54, 162, 235)',
purple: 'rgb(153, 102, 255)',
grey: 'rgb(201, 203, 207)'
};
var config = {
type: 'doughnut',
data: {
datasets: [{
data: [
ab_val,
BC_val,
mb_val,
nb_val,
nl_val,
],
backgroundColor: [
window.chartColors.red,
window.chartColors.orange,
window.chartColors.yellow,
window.chartColors.green,
window.chartColors.blue,
],
label: 'Dataset 1'
}],
labels: pro_unique
},
options: {
responsive: true,
legend: {
position: 'top',
},
title: {
display: true,
text: 'Total counts of confirmed cases in Canada'
},
animation: {
animateScale: true,
animateRotate: true
}
}
};window.onload = function() {
var ctx = document.getElementById('chart-area').getContext('2d');
window.myDoughnut = new Chart(ctx, config);
};document.getElementById('randomizeData').addEventListener('click', function() {
config.data.datasets.forEach(function(dataset) {
dataset.data = dataset.data.map(function() {
return randomScalingFactor();
});
});window.myDoughnut.update();
});var colorNames = Object.keys(window.chartColors);
document.getElementById('addDataset').addEventListener('click', function() {
var newDataset = {
backgroundColor: [],
data: [],
label: 'New dataset ' + config.data.datasets.length,
};for (var index = 0; index < config.data.labels.length; ++index) {
newDataset.data.push(randomScalingFactor());var colorName = colorNames[index % colorNames.length];
var newColor = window.chartColors[colorName];
newDataset.backgroundColor.push(newColor);
}config.data.datasets.push(newDataset);
window.myDoughnut.update();
});document.getElementById('addData').addEventListener('click', function() {
if (config.data.datasets.length > 0) {
config.data.labels.push('data #' + config.data.labels.length);var colorName = colorNames[config.data.datasets[0].data.length % colorNames.length];
var newColor = window.chartColors[colorName];config.data.datasets.forEach(function(dataset) {
dataset.data.push(randomScalingFactor());
dataset.backgroundColor.push(newColor);
});window.myDoughnut.update();
}
});document.getElementById('removeDataset').addEventListener('click', function() {
config.data.datasets.splice(0, 1);
window.myDoughnut.update();
});document.getElementById('removeData').addEventListener('click', function() {
config.data.labels.splice(-1, 1); // remove the label firstconfig.data.datasets.forEach(function(dataset) {
dataset.data.pop();
dataset.backgroundColor.pop();
});window.myDoughnut.update();
});document.getElementById('changeCircleSize').addEventListener('click', function() {
if (window.myDoughnut.options.circumference === Math.PI) {
window.myDoughnut.options.circumference = 2 * Math.PI;
window.myDoughnut.options.rotation = -Math.PI / 2;
} else {
window.myDoughnut.options.circumference = Math.PI;
window.myDoughnut.options.rotation = -Math.PI;
}window.myDoughnut.update();
});
从下图中,我们可以看到新不伦瑞克省确诊的 COVID 病例最多,而马尼托巴省也有类似比例的确诊病例。对于下面的图 4,它显示了圆环图在半圆布局中的不同显示。
图表 3:圆环图(全圆)
图表 4:圆环图(半圆)
条形图
首先,我们需要为条形图准备数据集。本图表主要关注 1-6 月艾伯塔省COVID 的累计 _ 病例、累计 _ 痊愈、累计 _ 死亡、和活跃 _ 病例 _ 变化**。为了使这些值公平地分布在同一范围内,我处理了 cumulative_cases 和 cumulative_recovered 列,并在数组上记录日志。**
// get unique value of array
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
var date_unique = date.filter( onlyUnique );
var ab_case = []
var ab_recover = []
var ab_deaths = []
var ab_case_mean = []
var ab_case_log = []
var ab_recover_log = []province.forEach((i_pro, index) => {
if (i_pro =="Alberta") {
ab_case.push(case1[index]);
ab_recover.push(recover[index]);
ab_deaths.push(deaths[index]);
ab_case_mean.push(case_mean[index]);
}
});
// take log on ab_case, ab_recover
ab_case.forEach((i_ab, index) => {
ab_recover_log.push(Math.log(ab_recover[index]));
ab_case_log.push(Math.log(i_ab));
});
在 Chat.js 中,可以灵活地创建由两种或多种不同图表类型组合而成的混合图表。在下面的例子中,我创建了一个条形折线图,它具有独立的类型参数规范。
new Chart(document.getElementById("canvas"), {
type: 'bar',
data: {
datasets: [{
label: 'log on cumulative_cases in Alberta',
backgroundColor: window.chartColors.red,
data: ab_case_log,
borderColor: 'white',
borderWidth: 2
}, {
label: 'log on cumulative_recovered in Alberta',
backgroundColor: window.chartColors.green,
data: ab_recover_log,
borderColor: 'white',
borderWidth: 2
},{
label: 'cumulative_deaths in Alberta',
backgroundColor: window.chartColors.purple,
data: ab_deaths,
borderColor: 'white',
borderWidth: 2
},
{
label: 'Month average of active cases in Alberta',
data: ab_case_mean,
fill: false,
borderColor:window.chartColors.blue,
borderWidth: 2,
// Changes this dataset to become a line
type: 'line'
}],
labels: date_unique
},
options: {
title: {
display: true,
text: 'Positive Cases of COVID in provinces of Canada'
},
hover: {
mode: 'index',
intersect: true
},
}});
从下面的条形图中,我们可以看到四月份的平均活动案例急剧增加,而五月份的平均活动案例急剧减少。从 5 月到 6 月,尽管活跃病例的数量仍然为负,但有一条向上的曲线显示,感染 COVID 的人越来越少。
绘图 4:条形折线图(半圆)
总之:
- Flask 是一个强大的后端,可以很好地处理 HTML 和 javascript 文件。一旦用确切的文件夹指定了目录,应用程序将通过指定的路径呈现页面。
- 图表。JS 是一个可视化库,支持许多图表,如条形图、折线图、面积图、圆环图等。最棒的是,您可以自定义布局,如悬停信息、刻度范围、x 轴和 y 轴标签等。
参考:
图表。JS 文档网站:https://www.chartjs.org/
使用 Keras 进行多类分类的 Flask API
如何使用 Flask 为深度学习模型创建生产就绪的 API
在本文中,您将了解如何使用通过 Keras 和 Flask 构建的时尚 MNIST 数据集,为深度学习模型创建模块化的生产就绪库。
深度学习模型生产就绪代码的关键特征是什么?
面向生产的深度学习代码的特性
- 异常处理监控错误并在出现错误时理解代码流。
- 日志可以设置为不同的级别,如调试、信息、警告、错误或关键。在生产中,我们应该将日志记录级别设置为仅记录警告、错误和关键信息。
- 使用 GitLab 对代码进行版本控制
- 代码注释对于理解代码非常重要
- 代码优化实现高效的内存使用和计算
- 容器化深度学习模型代码及其所有依赖库。
我们将在另一篇文章中了解 Docker 以及如何将深度学习模型容器化。
创建配置类
我们需要参数化一些用于微调深度学习模型的常见属性,可以使用 Config 类来完成。我们还添加了根据输入数据而变化的参数,以保持模型的通用性。
- 输入数据集相关参数:图像尺寸——高度和宽度、数据集大小以及待识别类别的标签
- 模型微调参数:优化器、优化器的学习率、时期数和 batch_size。我们还可以包括退出率、输入节点的数量、隐藏层的数量等。
- 存储信息的文件:保存训练重量的重量文件和记录日志的日志文件名
您可以根据需要添加/删除配置参数。这些配置参数用于深度学习模型中的所有不同操作。
**class Config(object):**
**IMAGE_HEIGHT=28
IMAGE_WIDTH=28
DATA_SIZE=1000
CLASS_NAME=*'T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat','Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot'***
**WEIGHT_FILENAME='FashionMNIST.h5'
LOG_FILENAME='LOG_FASHION.TXT'
EPOCHS=100
OPTIMIZER='RMSProp'
LEARNING_RATE=0.001
BATCH_SIZE=64**
**def __init__(self):
self.IMAGE_DIM = (self.IMAGE_HEIGHT, self.IMAGE_WIDTH)**
建立分类模型
创建一个类classFashionMNIST用于处理深度学习模型的不同方面。该类将有方法来
- 归一化数据
- 构建深度学习模型
- 训练模型
- 使用模型进行预测
- 显示来自数据集的图像
- 从数据集中找到图像的实际类别
日志记录和异常处理是代码不可或缺的一部分,使其更加健壮。
init()用于设置我们对类进行不同操作所需的参数,比如图像的高度、宽度、数据集大小以及我们想要预测的不同类。init()还用于读取时尚 MNIST 数据集以进行训练和验证,
**def __init__(self, height, width, data_size, class_name):
try:
self.height= height
self.width=width
self.data_size=data_size
self.class_names =list(class_name)
(self.train_images, self.train_labels), (self.test_images, self.test_labels) = tf.keras.datasets.fashion_mnist.load_data()
self.test_data= self.test_images
except:
logging.error("Error in init %s", sys.exc_info())**
归一化数据集
由于图像中的像素强度介于 1-255 之间,因此通过在 0 和 1 之间缩放值来标准化图像
**def normalize_data(self):**
***try:*
*logging.info("Normalizing data")*** *# load train and test images and labels based on data size* **self.train_labels = self.train_labels[:self.data_size]
self.test_labels = self.test_labels[:self.data_size]** *#Normalize the data* **self.train_images = self.train_images[:self.data_size].astype('float32') / 255
self.test_images = self.test_images[:self.data_size].astype('float32') / 255
* logging.info("Rshaping data")*** *# Reshape the data* **self.train_images = self.train_images.reshape((self.train_images.shape[0], self.width, self.height,1))
self.test_images = self.test_images.reshape((self.test_images.shape[0], self.width, self.height,1))
*except:*
* logging.error("Error", sys.exc_info())***
创建深度学习分类模型
我们传递优化器和配置文件中设置的学习率来编译模型。由于深度学习模型是多类分类,使用的损失函数是sparse _ categorial _ cross entropy。 如果做的是二元分类模型,那么用binary _ cross entropy作为损失函数。
**def create_model(self, optimizer, learning_rate):**
***try:***
***logging.info("Creatig model")***
**model = tf.keras.Sequential()**
* # Must define the input shape in the first layer of the neural network*
** model.add(tf.keras.layers.Conv2D(filters=64, kernel_size=2, padding='same', activation='relu', input_shape=(28,28,1)))
model.add(tf.keras.layers.MaxPooling2D(pool_size=2))
model.add(tf.keras.layers.Dropout(0.3))
model.add(tf.keras.layers.Conv2D(filters=32, kernel_size=2, padding='same', activation='relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size=2))
model.add(tf.keras.layers.Dropout(0.3))**
**model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(256, activation='relu'))
model.add(tf.keras.layers.Dropout(0.5))
model.add(tf.keras.layers.Dense(10, activation='softmax'))**
***logging.info("Model Created")***
*# creating optimizer based on the config*
**opt= self.get_optimizer(optimizer, learning_rate)**
*#Compiling the model*
**model.compile(loss='sparse_categorical_crossentropy',
optimizer=opt,
metrics=['accuracy'])**
***logging.info(" Model Compiled")***
** *except:
logging.error(" Error during Model Creation - %s", sys.exc_info())***
** *finally:***
** return model**
设置优化器
处理了三个流行的优化器: Adam,SGD,RMSProp 。RMSProp 是默认的优化器,默认的学习率设置为 0.001。我们可以其他优化者像 动量、内斯特罗夫、阿达格勒、阿达德尔塔。
**def get_optimizer(self,optimizer_name='RMSProp', learning_rate=0.001):
*try:*
if optimizer_name=='Adam':
optimizer = optimizers.Adam(learning_rate=learning_rate, beta_1=0.9, beta_2=0.999, amsgrad=False)
elif optimizer_name=='SGD':
optimizer = optimizers.SGD(lr=learning_rate, momentum=0.9)
elif optimizer_name=='RMSProp':
optimizer = optimizers.RMSprop()
* logging.info("Optimizer created %s", optimizer_name)*
return optimizer
*except:
logging.error(" Error during visualization - %s", sys.exc_info())***
训练模型
创建模型,归一化数据,最后在列车图像和列车标签上训练数据。
如果准确度小于 0.8 或者验证准确度小于 0.7,那么我们将在日志文件中发出警告,通知团队该模型可能需要重新训练。
**def train_model(self,filename, epochs, optimizer, learning_rate, batch_size):
* try:*
model = self.create_model(optimizer, learning_rate)
*logging.info("Model created ")
logging.info("Normalizing the data")*
self.normalize_data()
*logging.info(self.train_images.shape)
logging.info("Training started")*
history=model.fit(self.train_images,
self.train_labels,
batch_size=batch_size,
epochs=epochs,
validation_data=(self.test_images,self.test_labels))
*logging.info(" Training finished")*
acc= np.average(history.history['acc'])
val_acc=np.average(history.history['val_acc'])
* logging.info(" Model accurcay on train images : {:5.2f}".format(acc))
logging.info("Accurcay too low for val {:5.2f}".format(val_acc))*
model.save(filename)
*logging.info("Model saved %s", filename)*
if acc <.8 or val_acc<0.7:
*logging.warn("Accurcay too low {:5.2f}".format(acc) )
logging.warn("Accurcay too low for val {:5.2f}".format(val_acc))*
return history, model
* except:
logging.error(" Error during Model Creation - %s", sys.exc_info())***
预测数据
为了预测数据,我们传递测试图像的索引和包含训练权重的文件。
**def predict_data(self, test_image_num, filename):
*try:
logging.info("Predciting the data for %d", test_image_num)*
test_img = self.test_images[test_image_num].reshape((1, self.width, self.height,1))
test_img=test_img.astype('float32') / 255
model = tf.keras.models.load_model(filename)
*logging.info("Loaded the trained weights from %s", filename)*
pred= model.predict(test_img)
pred= np.argmax(pred)
* logging.info("Predicted class %s",self.class_names[pred] )*
return self.class_names[pred]
* except:
logging.error(" Error during Model predcition - %s", sys.exc_info())***
测试图像的实际类别
因为我们使用了 FashoinMNIST,所以我们知道可以用来比较输出的测试图像的类别
**def actual_data(self,test_image_num):
return self.class_names[self.test_labels[test_image_num]]**
classFashionMNIST 类的完整代码
*#Importing required libraries*
**import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import sys
import logging
from tensorflow.keras import optimizers***# setting the random seed*
**np.random.seed(1)
tf.compat.v1.set_random_seed(1)****class classFashionMNIST:**
*'''
Method Name: init
Functionality: initializes the class
Parameters: sets the height, width of the image, data size and class labels
'''*
**def __init__(self, height, width, data_size, class_name):
*try:*
self.height= height
self.width=width
self.data_size=data_size
self.class_names =list(class_name)
(self.train_images, self.train_labels), (self.test_images, self.test_labels) = tf.keras.datasets.fashion_mnist.load_data()
self.test_data= self.test_images
*except:*
logging.error("Error in init %s", sys.exc_info())**
*'''
Method Name: normalize data
Functionality: Normalizes the images pixel intensity values by
scaling pixel values to the range 0-1 to centering and
even standardizing the values.
Parameters: None
'''*
**def normalize_data(self):**
***try:*
logging.info("Normalizing data")
# load train and test images and labels based on data size
self.train_labels = self.train_labels[:self.data_size]
self.test_labels = self.test_labels[:self.data_size]
#Normalize the data
self.train_images = self.train_images[:self.data_size].astype('float32') / 255
self.test_images = self.test_images[:self.data_size].astype('float32') / 255
logging.info("Rshaping data")
# Reshape the data
self.train_images = self.train_images.reshape((self.train_images.shape[0], self.width, self.height,1))
self.test_images = self.test_images.reshape((self.test_images.shape[0], self.width, self.height,1))
*except:*
logging.error("Error", sys.exc_info())***'''
Method Name: create_mode
Functionality: Creates the deep learning model for multiclass classification
Parameters: optimizer - optimizers can be Adam, SGD or RMSProp
Learning_rate- learning rate of the optimizer
'''***def create_model(self, optimizer, learning_rate):
*try:*
*logging.info("Creatig model")*
model = tf.keras.Sequential()
*** # Must define the input shape in the first layer of the neural network* **model.add(tf.keras.layers.Conv2D(filters=64, kernel_size=2, padding='same', activation='relu', input_shape=(28,28,1)))
model.add(tf.keras.layers.MaxPooling2D(pool_size=2))
model.add(tf.keras.layers.Dropout(0.3))
model.add(tf.keras.layers.Conv2D(filters=32, kernel_size=2, padding='same', activation='relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size=2))
model.add(tf.keras.layers.Dropout(0.3))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(256, activation='relu'))
model.add(tf.keras.layers.Dropout(0.5))
model.add(tf.keras.layers.Dense(10, activation='softmax'))
*logging.info("Model Created")**** # creating optimizer based on the config* **opt= self.get_optimizer(optimizer, learning_rate)*** #Compiling the model* **model.compile(loss='sparse_categorical_crossentropy',
optimizer=opt,
metrics=['accuracy'])
* logging.info(" Model Compiled")*
* except*:
*logging.error(" Error during Model Creation - %s", sys.exc_info())
finally:*
return model***'''
Method Name: train_model
Functionality: Trains the deep learning multiclass classification model
Parameters: filename : File o save the trained weights
epochs : No. of epcohs to train the model
optimizer - optimizers can be Adam, SGD or RMSProp
Learning_rate- learning rate of the optimizer
Batch_size - batch_size of the dataset to train the model
'''*
**def train_model(self,filename, epochs, optimizer, learning_rate, batch_size):
*try:*
model = self.create_model(optimizer, learning_rate)
*logging.info("Model created ")
logging.info("Normalizing the data")*
self.normalize_data()
*logging.info(self.train_images.shape)
logging.info("Training started")*
history=model.fit(self.train_images,
self.train_labels,
batch_size=batch_size,
epochs=epochs,
validation_data=(self.test_images,self.test_labels))
*logging.info(" Training finished")*
acc= np.average(history.history['acc'])
val_acc=np.average(history.history['val_acc'])
*logging.info(" Model accurcay on train images : {:5.2f}".format(acc))*
* logging.info("Accurcay too low for val {:5.2f}".format(val_acc))*
model.save(filename)
*logging.info("Model saved %s", filename)*
if acc <.8 or val_acc<0.7:
*logging.warn("Accurcay too low {:5.2f}".format(acc) )
logging.warn("Accurcay too low for val {:5.2f}".format(val_acc))*
return history, model
*except:
logging.error(" Error during Model Creation - %s", sys.exc_info())****'''
Method Name: predict_data
Functionality: predicts the data for multiclass classification model
Parameters: test_image_num - index of the test image that we want to predcit
filename : File containing the trained weights
'''*
**def predict_data(self, test_image_num, filename):
*try:
logging.info("Predciting the data for %d", test_image_num)*
test_img = self.test_images[test_image_num].reshape((1, self.width, self.height,1))
test_img=test_img.astype('float32') / 255
model = tf.keras.models.load_model(filename)
*logging.info("Loaded the trained weights from %s", filename)*
pred= model.predict(test_img)
pred= np.argmax(pred)
* logging.info("Predicted class %s",self.class_names[pred] )*
return self.class_names[pred]
* except:
logging.error(" Error during Model predcition - %s", sys.exc_info())****'''
Method Name: actual_data
Functionality: Retrives the actual class for the test image based on the index passed
Parameters: test_image_num - index of the test image that we want to predcit
'''
* **def actual_data(self,test_image_num):
return self.class_names[self.test_labels[test_image_num]]
*'''
Method Name: get_optimizer
Functionality: Creates the optimizers based on passed parameter and learning rate
Parameters: Optimizer_name - optimizers can be Adam, SGD or RMSProp
Learning_rate- learning rate of the optimizer
'''***
**def get_optimizer(self,optimizer_name='RMSProp', learning_rate=0.001):
*try*:
if optimizer_name=='Adam':
optimizer = optimizers.Adam(learning_rate=learning_rate, beta_1=0.9, beta_2=0.999, amsgrad=False)
elif optimizer_name=='SGD':
optimizer = optimizers.SGD(lr=learning_rate, momentum=0.9)
elif optimizer_name=='RMSProp':
optimizer = optimizers.RMSprop()
*logging.info("Optimizer created %s", optimizer_name)*
return optimizer
* except:
logging.error(" Error during visualization - %s", sys.exc_info())***
使用 Flask 创建 API
阅读这里的,对如何使用 Flask 创建一个基本 API 有一个基本的了解
导入使用 Flask API、classFashionMNIST、FashionMNIST 配置类和日志记录类所需的库。
通过创建 Flask 的一个实例来创建一个应用程序对象,我们将一个预定义的变量“name”传递给它,这是我们的模块的名称。
from flask import Flask, jsonify, request
from flask_restful import Api, Resource
import numpy as np
from Fashion_MNIST import classFashionMNIST
from FashionConfig import Config as cfg
import logging
import absl.loggingapp=Flask(__name__)
我们编写了两个 GET 方法,一个用于进行预测,一个用于检索实际的类
[**@app**](http://twitter.com/app)**.route("/predict", methods=["GET"])
def predict():
pred =""
posted_data = request.get_json()
test_image_num=posted_data['test_image_num']
logging.info("In Predict")
model_filename=cfg.WEIGHT_FILENAME
pred= fashionMNISTclass.predict_data(test_image_num, model_filename)
return jsonify(pred)**[**@app**](http://twitter.com/app)**.route("/real", methods=["GET"])
def real():
data =""
posted_data = request.get_json()
test_image_num=posted_data['test_image_num']
data = fashionMNISTclass.actual_data(test_image_num)
return jsonify(data)**
我使用的是 TF 1.14,因此我们需要禁用 abseil-py,这样日志就不会被重定向到 stderr。我们根据配置参数中指定的日志文件名来设置日志文件
加载 API 时训练数据集,因此等待预测,直到模型完全加载和编译
**if __name__ == '__main__':**
***logging.root.removeHandler(absl.logging._absl_handler)***
**absl.logging._warn_preinit_stderr = False**
**logging.basicConfig(filename=cfg.LOG_FILENAME, filemode='a', format='%(filename)s-%(asctime)s %(msecs)d- %(process)d-%(levelname)s - %(message)s',
datefmt='%d-%b-%y %H:%M:%S %p' ,
level=logging.DEBUG)**
** fashionMNISTclass= classFashionMNIST(cfg.IMAGE_HEIGHT, cfg.IMAGE_WIDTH, cfg.DATA_SIZE, cfg.CLASS_NAME)**
* # noramlize the data*
**fashionMNISTclass.normalize_data()**
*# train the model*
**history, model = history, model = fashionMNISTclass.train_model(cfg.WEIGHT_FILENAME,
cfg.EPOCHS,
cfg.OPTIMIZER,
cfg.LEARNING_RATE,
cfg.BATCH_SIZE)
app.run(debug=True)**
使用 postman 测试预测方法
检查实际的类名
完整的代码可在这里
结论:
现在,您可以使用 Keras 为二进制或多类分类模型编写一个生产就绪的代码了。可以对代码进行许多调整,本文中提到了其中一些。
拉平许多曲线
缅因州新港——迈克·卢基安诺夫摄影
曲线是局部的——即使很小的爆发也能摧毁较小的社区
当我与人们谈论社交距离和阅读反对庇护指令的观点时,我震惊于人们对社交距离的目的和价值以及“拉平曲线”的意义的困惑。非常简单地说,大规模隔离/社会距离服务于一个主要目的——阻止每个人同时生病 。当每个人同时生病时,医疗响应能力就会崩溃,死亡人数也会增加。这并不是为了永久地拯救人们免于被感染——这实际上只是为了推迟不可避免的事情,以便我们中的更多人能够幸免于难。包括一些美国参议员在内的许多人继续指出支持这一立场的顶级统计数据,即社交距离的成本比病毒本身更糟糕。如果不解决选择牺牲生命换取 GDP 这一严肃的道德和伦理问题,似乎我们至少应该向我们牺牲的羔羊解释清楚,社会距离做/不做什么。
缺乏检测使得大范围封锁成为必要:人们谈论/写道要采取更外科手术式的方法来控制病毒。将韩国作为一个案例研究——早期的大规模检测和追踪感染者的社会接触,以及在全国范围内的严厉执法帮助他们遏制了病毒。事实上,没有症状的人可以携带和传播这种病毒,这使得检测对于遏制病毒比以往任何病毒都更加重要。至少从一月份开始,这一点已经广为人知(尽管一位美国州长声称在 4 月 2 日发现了这一点)。如果没有疾控中心跟踪的广泛检测,就没有办法在不引发另一场疫情的情况下控制解除当地隔离。大规模测试要把我们从大规模隔离的需要中拯救出来已经太晚了,但是大规模测试需要到位才能让我们摆脱隔离。
**拉平曲线是关于管理医疗保健能力:**不,在开发出疫苗或更好的疗法之前,我们都不会被隔离(尽管这应该同时发生)。隔离的主要目的是试图将医院中的人数控制在医院能够照顾他们的最大能力之下。由于不同的地理区域在不同的时间被袭击,并且有不同的医疗能力,每个州和当地的自治市看起来会非常不同。要了解不同州的曲线,请访问 https://covid19.healthdata.org/projections。虽然很难知道在如此低的测试水平下,对大多数州病例数量的预测是否准确,但能力限制的整合应使其成为了解局部风险的非常有用的工具。
**如果医疗系统超出能力,病毒的“自然”死亡率就无关紧要:**由于测试不佳,病毒的自然死亡率估计值很难确定。似乎越来越确定的一件事是,那些认为它“就像流感”的人可能会错 7-10 倍。然而,即使新冠肺炎的自然死亡率与季节性流感一样低——它在人口中的传播速度也会增加需要医疗护理的总人数,以至于原本可以得救的人会死亡。以此作为一个过度简化的例子,说明超出容量的系统是如何放大结果的:
X 病毒已经感染了 1000 人,它的正常死亡率为 1%,10%达到需要呼吸机才能生存的地步。如果当地医院只有 100 台呼吸机(假设硬约束,没有即兴发明)。如果同样的病毒传播到 2000 人,容量限制会将前 100 名以上需要呼吸机的人拒之门外。假设没有一个需要呼吸机的人能在没有呼吸机的情况下存活,正常的死亡率适用于有呼吸机的人。在这个简化的例子中,死亡率从 1%飙升至 6%。
显然,在现实世界中,容量过剩/容量不足之间的界限并不十分清晰,但当你达到临界点时,其后果可能会实际上使死亡率增加许多倍——特别是在当地水平**。在能力方面有更多的限制,包括人员配备、重症监护室床位、药物等。试图通过简单地采用全国总感染人口估计数并乘以静态死亡率来预测 COVID 疫情总死亡人数的模型没有考虑到当地的能力限制,因此严重低估了无所作为的人类成本。**
****展望未来:尽管强烈的声音敦促我们在病毒达到顶点之前回去工作,过早的“恢复正常”将对人类生命造成又一次可怕的损失,并对经济造成第二次灾难性的打击。为了让我们“重新开始工作”,需要一个大规模的检测机制和一个协调良好的、有针对性的隔离系统,以确保健康的人受到保护,病毒携带者被隔离。这不仅需要测试,还需要警惕的集中跟踪结果和快速行动计划,以迅速遏制疫情的再次出现。这就是我们回去工作的方式——但是没有迹象表明联邦政府的领导层有能力取得如此大的成就。如果“恢复工作”的命令最终没有在流通中进行数十亿次快速 COVID 测试,以及在我们“重新开业”之前进行隔离和遏制的国家计划,我们将发现自己又回到了起点——在试图“管理曲线”的本地化封锁中。
编者注: 走向数据科学 是一份以研究数据科学和机器学习为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
缓解交通拥堵
新冠肺炎如何改变我们的驾驶模式的分析
Brian Wangenheim 在 Unsplash 上拍摄的照片
免责声明 — 健康或 COVID:来自我们编辑的注释
我花了很多时间在不列颠哥伦比亚的海天高速公路上,往返于我在斯夸米什的家和我在温哥华的工作单位之间。结果,我想到了交通…很多。我知道,周一到周五,如果我在早上 6:45 而不是 7:00 离开我在斯夸米什的家,我可以避免拥堵,并节省 10 分钟的上班时间。我知道,晚于下午 4:00 下班会让我在钢铁工人桥上遇到大塞车,周五下午温哥华到斯夸米什的“周末战士”交通会让我在北温哥华陷入令人窒息的停滞状态。我也知道周末海空高速公路上的交通堵塞,特别是在惠斯勒的长周末或大雪天,即使不比平日更糟糕,也一样糟糕。
你可能会认为,有了这些信息,我就可以避免交通堵塞,并最大限度地减少我的驾驶时间。不对。我仍然被困在城市走走停停,高速公路合并备份,以及与天气有关的减速。我可以接受这一现实,但作为一个喜欢硬数据而不是观察的人,我决定用一些数字来说明温哥华和海天高速公路的交通拥堵情况。
那么新冠肺炎和这些有什么关系呢?
事实证明,在建立我的数据集的过程中,我最终捕捉到了社会距离和非必要服务关闭对温哥华和海天交通拥堵的影响。这些结果不仅显示了道路上交通流量减少的明显结论,还表明了人们驾驶习惯和旅行倾向的转变,这意味着通勤者、“周末战士”、长周末家庭野营旅行和观光者的减少。
目标
我关注的是新冠肺炎对温哥华交通拥堵和海天高速公路的影响。具体来说,我想回答几个关键问题:
- 对日常交通拥堵的总体影响是什么?
- 工作日和高峰时间的交通拥堵是如何受到影响的?
- 周末的交通拥堵情况有所改变吗(即人们是否因为个人原因减少了出行)?
收集数据
我从谷歌地图距离矩阵 API 收集了驾驶时间数据,并将其存储在亚马逊网络服务(AWS) DynamoDB 数据库中。这些数据是使用在 AWS Lambda 中按计划运行的 Python 代码收集的。距离矩阵 API 允许我请求两点之间的估计行驶时间,就好像我现在就要出发一样。这相当于在我的谷歌地图手机应用程序中输入目的地,然后得到一个估计的驾驶时间。
我在 2019 年 10 月 17 日至 2020 年 4 月 13 日之间每 30 分钟收集 4 条路线的数据,分别是往返温哥华市区和斯夸米什和往返东温哥华和斯夸米什。本文仅提供温哥华至斯夸米什和东温哥华至斯夸米什的数据。这两条路线都是主要的通勤通道,但有一个主要因素不同,东温哥华到斯夸米什路线绕过温哥华市中心。
通过将我的 Google Maps API 请求限制在 4 条路线,并对 Lambda 和 DynamoDB 服务使用 AWS 免费层,我最终什么也没欠。一旦我收集了有用的数据集,我就编写一些 Python 代码,并将数据从 DynamoDB 导出到本地机器上的一个 CSV 文件中,在那里我清理并可视化了 Jupyter 笔记本中的数据。
从斯夸米什到温哥华市区(左)和东温哥华(右)的路线
探索数据
行驶时间是一个容易收集的交通指标,可以揭示很多交通趋势。请看下图,该图显示了 2020 年温哥华和斯夸米什之间以及东温哥华和斯夸米什之间的行驶时间。这两条路线在早上和晚上的高峰时间都有高峰(即高峰)和晚上的平静(即低谷)。这在东温哥华到斯夸米什的记录中尤其明显,其中驾驶时间的峰值以 5 为一组,对应于周一到周五(例如,2020 年 1 月 20 日这一周)。另外,请注意两个记录中的异常值(最大的峰值)。这些事件代表的是增加驾驶时间的事件,超出了平均高峰时间交通可能造成的影响,例如一月中旬的暴风雪。
2020 年温哥华到斯夸米什(上图)和东温哥华到斯夸米什(下图)的行驶时间
新冠肺炎对驾驶时间的影响跃然纸上!3 月 16 日,贾斯廷·特鲁多总理宣布了抗击新冠肺炎的重大行动。我将这一天视为不列颠哥伦比亚省新冠肺炎的“开始”(至少在对驾驶时间的影响方面)。该日期后记录的驾驶时间显著减少。事实上,午夜和高峰时间驾驶时间之间的差异几乎消失了(即高峰和低谷更接近)!
关于这个数据集,需要注意一些事情:
- 谷歌地图使用位置数据来帮助估计驾驶时间。这意味着路上的谷歌地图用户越少,驾驶时间估计的可变性就越大(即数据越少=不确定性越大)。例如,一辆汽车在半夜以 140 公里/小时的速度行驶在高速公路上,这将导致谷歌估计更短的行驶时间。相反,一辆汽车半夜停在高速公路上会导致谷歌估计更长的行驶时间。我见过谷歌地图在半夜显示减速(即红色地图颜色),因为有一辆车靠边了。
- 行驶时间对应于既有城市路段又有公路路段的路径。这意味着沿某条路线行驶时间的增加可能是由于城市中的交通流量增加,而高速公路是畅通的,反之亦然。因此,请务必记住,该数据集只能概括路线的总体趋势,而不能概括单个道路的趋势。
拥挤程度指数
虽然行驶时间对于可视化交通趋势(如高峰时间和天气延误)非常有用,但它并不容易解释。例如,如果我告诉你,昨天我花了 60 分钟从 A 开车到 B ,今天花了 70 分钟,你会认为今天堵车了。然而,如果我告诉你从 A 开车到 B 的平均时间是 55 分钟,你会认为这两天都堵车。但是如果我说平均时间是 75 分钟,而不是 55 分钟,你会认为这两天的交通都很少。看到问题了吗?行驶时间并不能直接告诉你交通拥堵的程度(即交通拥堵程度是高于还是低于平均水平)。
为了应对这一点,我创建了一个交通拥堵水平指数,定义为驾驶时间与周一至周五凌晨 2:00 的平均驾驶时间(即基线)之间的百分比差异。我假设周一到周五凌晨 2:00 的平均驾驶时间代表了没有交通堵塞的驾驶时间,或者交通专家所说的自由流动。
查看数据时,请记住以下几点:
- 正拥堵级别意味着路线上的交通流量大于平均凌晨 2:00 观察到的流量。
- 负拥堵级别表示路线上的交通流量低于平均凌晨 2:00 观察到的流量。
- 拥堵级别是针对整条路线的,并没有说明拥堵发生在路线的哪个位置(即高峰时段拥堵可能发生在城市,而不是远离城市的高速公路)。
请看下图,该图显示了 2020 年温哥华和斯夸米什之间以及东温哥华和斯夸米什之间的交通拥堵情况。两条路线在高峰时段的交通拥堵率都增加了 10%到 20%。超出这一范围的增长对应于“不可预见的”事件,如交通事故和恶劣天气(如 1 月中旬的暴风雪)。请注意,在暴风雪期间,即使是在午夜,拥挤程度也在上升。但是为什么暴风雪中的半夜会有更多的车辆呢?请记住上一节,谷歌地图使用位置数据来估计驾驶时间,因此,如果有几个人在暴风雪中驾驶,并且不可避免地驾驶缓慢,那么谷歌将估计较长的驾驶时间,这转化为增加的拥堵。
总的来说,拥堵级别指数比单独的驾驶时间提供了更大的洞察力,但也不是没有缺点(即,暴风雪中拥堵的增加或拥堵位置的不清晰)。
2020 年温哥华至斯夸米什(上图)和东温哥华至斯夸米什(下图)的拥堵水平
日常拥堵
问:对日常交通拥堵的总体影响是什么?
接下来的两个数字显示了在 2020 年 3 月 16 日之前和之后的某一天,上午 7:00 到下午 7:00 之间,您在温哥华到斯夸米什和东温哥华到斯夸米什路线上会遇到的平均交通拥堵情况。
这个数字不需要很多解释。新冠肺炎的影响是减少了这两条路线全天的交通拥堵。温哥华至斯夸米什航线的交通拥堵在工作日从 8%-10%减少到不到 4%,在周末从 5%-6%减少到不到 2%。东温哥华至斯夸米什航线的交通拥堵在一周内从 6%-9%减少到不到 1%,在周末从 2%-4%减少到不到 1%。东温哥华到斯夸米什航线的负拥堵级别表明,现在上午 7:00 到下午 7:00 之间的平均交通流量比新冠肺炎之前的凌晨 2:00 要少。哇!
新冠肺炎对温哥华至斯夸米什每日交通拥堵的影响
新冠肺炎对东温哥华至斯夸米什每日交通拥堵的影响
小时拥堵—工作日
问:高峰时段的交通受到了怎样的影响?
每日交通拥堵揭示了高水平的趋势,但每小时的交通拥堵真正告诉我们人们生活的哪一部分受到了新冠肺炎的影响。接下来的两个数字显示了在 2020 年 3 月 16 日之前和之后的工作周期间,上午 7:00 到下午 7:00 之间,每小时在温哥华到斯夸米什和东温哥华到斯夸米什路线上遇到的平均交通拥堵情况。
不足为奇的是,所有时段的交通拥堵都有所减少,高峰时段的交通拥堵减少最多。此外,两条路线都有负面或接近负面的交通拥堵在晚上后新冠肺炎。这表明,人们不仅不去工作,而且在工作日呆在家里,这无疑是企业关门的一个原因。这是惊人的,一个主要的大都市在下午 6 点几乎没有交通拥堵!
新冠肺炎对温哥华至斯夸米什工作日每小时交通拥堵的影响
新冠肺炎对东温哥华至斯夸米什工作周每小时交通拥挤的影响
每小时拥堵—周末
问:周末交通受到了怎样的影响?
接下来的两个数字显示了在 2020 年 3 月 16 日之前和之后的周末上午 7:00 到下午 7:00 之间,您在温哥华到斯夸米什和东温哥华到斯夸米什路线上每小时会遇到的平均交通拥堵情况。
在新冠肺炎之前,如果你从温哥华或东温哥华出发去斯夸米什骑自行车、滑雪或喝贡多拉啤酒一天,然后在上午 10 点后离开,你会觉得是在开慢车。现在,你最好还是早点离开,但是即使你中午离开,交通堵塞也会少很多。这表明,人们不仅在工作日不通勤上班,晚上呆在家里,而且由于个人原因,他们在周末开车的次数也减少了。
新冠肺炎对温哥华至斯夸米什周末每小时交通拥堵的影响
新冠肺炎对东温哥华到斯夸米什周末每小时交通拥堵的影响
每小时拥堵—晴朗/温暖的周末
问:在阳光明媚的周末,人们还会开车从海上去天空高速公路吗?
接下来的两张图显示了 2020 年 3 月 16 日之前的周末以及 2020 年 3 月 16 日之前和之后的周末,上午 9:00 至下午 14:00 之间,您在温哥华至斯夸米什和东温哥华至斯夸米什路线上每小时可能遇到的平均交通拥堵情况。
海空高速公路带司机穿过斯夸米什和惠斯勒,是一条很受欢迎的户外休闲公路。我的思考过程是,当户外活动更受欢迎时,人们更倾向于在天气好的时候开车去海上高速公路,所以在温暖晴朗的周末观察拥堵趋势会让我们更深入地了解有多少“游客”去斯夸米什。
结果相当令人信服。最高拥堵级别对应的是 2019 年 11 月温暖晴朗的周末。在这个周末,拥堵程度甚至大大超过了新冠肺炎之前周末的平均拥堵水平。更令人惊讶的是新冠肺炎飓风后温暖晴朗的周末拥堵情况有所减少。事实上,新冠肺炎事件后最不拥堵的周末是一个美丽的复活节周末,通常会有成千上万的司机在海上高速公路上行驶。
在更个人的层面上,最有趣的数据是 3 月 21/22 日的周末。本周末有经证实的新闻报道称数百人徒步 Stawamus Chief(斯夸米什的一条受欢迎的小道),在这些报道之后,有说法称大部分是来自温哥华的人没有遵循新冠肺炎的社交距离惯例,开车去斯夸米什。如果这是真的,我本以为拥堵水平会更接近新冠肺炎之前的平均水平。特别是因为社交距离意味着司机不再拼车,这意味着路上有更多的车来让同样数量的人到达一个地点。
相反,我认为一个更合理的答案是,游客的增加可能比预期的占当地居民的比例更大。考虑到这一点,工作的人越来越少,而那些工作的人都是在家工作,实行半灵活的时间表。此外,在新冠肺炎期间,人们一直在推动限制他们接触极限运动,如野外滑雪和爬山。所有这些都表明,可供娱乐的地方越来越少,而更多的当地人有空闲时间去娱乐。
新冠肺炎对温哥华至斯夸米什选定周末每小时交通拥堵的影响
新冠肺炎对东温哥华至斯夸米什选定周末每小时交通拥堵的影响
结论
我很幸运,因为我的数据集是在新冠肺炎措施开始前后收集的,这给我留下了一个有趣的故事,但我的发现并不孤单。全世界,包括其他加拿大城市,交通拥堵都在减少。
主要外卖如下:
- 路上的司机越来越少。工作日高峰时间和周末交通拥堵都有所下降。
- 人们不仅不去工作,而且他们似乎听从了呆在家里的指导方针,因为个人原因不开车。这表现在晚上和周末的交通拥堵明显减少。
- 开车从大海到天空高速公路,参观斯夸米什和惠斯勒的人数似乎有所下降。人们住在离家更近的地方。不过,我承认用这个数据集来做这个断言有点牵强。相反,仅沿着海空高速公路(即没有城市交通)收集斯夸米什和温哥华之间的数据将会揭示更多**。**
本分析中使用的所有数据都可以在 GitHub 上免费获得。
交叉验证的味道
使用交叉验证背后的动机
内森·杜姆劳在 Unsplash 上的照片
交叉验证(也称为旋转估计或样本外测试)是一种重采样方法,用于
模型评估(评估模型的性能)
型号选择(选择型号的适当灵活度)
交叉验证评估预测模型的性能,并评估它们在独立数据集上的样本外的表现。简单地说,它检查一个模型是否是可推广的。
传统模型评估技术:-
验证集方法(数据分割)
在这种方法中,观察集被随机分为训练集和验证集。70/30 或 80/20 的比例更常用,尽管确切的比例取决于数据的大小。
在训练集上拟合该模型,然后使用拟合的模型来预测对验证集中的观察的响应。得到的验证集错误率提供了对测试错误率的估计。根据响应的类型,可以使用适当的误差度量来测量误差率,例如均方误差(MSE)、均方根误差(RMSE)或平均绝对百分比误差(MAPE)。
优势:-
简单且易于实施
计算上便宜
缺点:-
错误率可能具有高方差,这取决于哪些数据点最终出现在训练集和验证集中
高估的测试误差。记住,统计方法在训练较少的观察值时表现会更差。在这种方法中,很大一部分观察值在验证集中,其余的在训练集中。
留一交叉验证(LOOCV)
在 LOOCV,数据以这样的方式分割:除了一个数据点包含在验证集中之外,所有的数据点 (n-1)都包含在训练集中。重复该方法,直到每个数据点都被用作验证集。计算平均误差以评估模型。
优点:-
偏差较小。由于训练集大小包括几乎所有的观察值(n-1),与验证集方法相比,高估误差的趋势几乎可以忽略。
劣势:-
- 计算上昂贵(模型需要拟合 n 次)。
对于线性回归,有一个降低 LOOCV 成本的捷径:
k 倍交叉验证
在这种方法中,数据被随机分成大小近似相等的 k 个子集**。一次,一个折叠被视为验证集,其余折叠(k-1)被视为训练集。重复该过程,直到每个折叠被用作验证集,即 k 次。通过取测试误差的 k 个估计值的平均值来计算 k 倍 CV 估计值。**
优势:-
这种方法减少了数据分割方式的影响。每个数据点在测试集中出现一次,在训练集中出现 k-1 次。随着 k 的增加,估计值的方差减小。
缺点:-
由于与 LOOCV 相比,该模型的数据较少,因此会将偏差引入测试误差的估计中。
k 倍 CV 的偏倚和方差权衡
偏倚是样本统计数据系统地高估或低估总体参数的趋势。就偏倚缩减而言, LOOCV 优于 K-fold CV,,因为它使用 n-1 个样本来训练模型,这与全数据集一样好。
方差衡量一组数据点偏离平均值的程度。高相关值的平均值比低相关值的平均值具有更高的方差。由于在 LOOCV 中,每个拟合模型中的训练数据集几乎相似,每个模型的输出高度相关,导致的方差高于 K 倍 CV
k 值越大,方差越小,偏差越大,而降低 k 会增加方差,降低偏差。考虑到这些因素, k = 5 或 k = 10 ,给出了偏差和方差平衡的最佳点。
选择最佳模型
在一个回归问题中
对于导致最低测试误差的方法,我们在估计测试 MSE 曲线中寻找最小点的位置。
尽管 CV 误差估计与实际测试误差不同,但交叉验证误差最小的模型通常具有相对较小的测试误差。
- 在分类问题中
通过选择交叉验证误差估计值最小的模型,可以使用交叉验证来决定最佳模型。在下图中,10 倍 CV 误差估计提供了测试误差率的一个很好的近似值。
带回家消息 : —交叉验证是评估模型有效性的有用工具,尤其是处理过拟合和欠拟合。
简而言之,学习方法中涉及数据的每个方面都必须进行交叉验证。
参考资料:
- G.放大图片作者:James d . Witten t .统计学习导论:在 R 中的应用。(2013).
PyTorch 中灵活的声明性数据集采样
一个可配置的树形结构 Pytorch 采样器,可以利用任何有用的示例元数据
克里斯蒂娜·温特在 Unsplash 拍摄的照片
当你正在用 PyTorch 构建你令人敬畏的深度学习应用时, torchvision 包提供了许多现有数据集的便捷接口,如【MNIST】和 Imagenet 。随机梯度下降通过将实例连续采样成小批量来进行。在许多情况下,您不必为此失眠,可以坚持默认行为:一个接一个地检查图像列表,并在每个时期后重新排列列表。如果您在建模过程中没有理由修改它,那么您可以停止阅读这里的内容。
然而,当你在你自己聚集和管理的自定义数据集上工作时,你可能最终会编写你自己的数据集、数据加载器、或采样器的子类。这就是我们在 Atomwise 的情况,我们试图根据结构模型预测可能的医学药物的生物活性。在不涉及太多细节的情况下,为了下面的展示,让我简要描述一下我们的数据模式的简化版本。每个例子由一对具有空间坐标的文件组成,用于蛋白质目标和与之结合的配体分子。根据它们的分析(为确定活性而进行的化学或生物实验的类型),存在对应于不同终点和置信度的多个标记。一些反面的例子可以通过多种不同的方式综合产生。我们不断地问自己这个问题,“使用这些数据的最佳方式是什么?”而且你猜对了,答案从“看情况”到“很复杂”不等。在药物发现领域,为基准构建提供规范、通用的方法可能很棘手。因此,随着时间的推移,我们一直在尝试各种变化,例如:
- 均匀地对目标进行采样,与已知活性分子的数量成比例,或者与其他导出的加权公式成比例
- 蛋白质属于高级家族;我们可以根据层次分组来指定数据集的组成
- 使用实例类型的相对频率(“实验实例 70%的时间,合成实例 30%的时间”)
- 对于匹配目标,在每个正样本之后立即采样负样本
- 替换或不替换的采样
我想你明白了:在构建我们的训练和测试分布时,有一个很大的假设空间。
与其他数据集类似,如 ImageNet ,原始数据量太大,无法完全加载到主内存中。通常,它驻留在磁盘上,并使用巧妙的缓存和压缩方案进行访问。我们也可以通过 Redis 等分布式内存服务器来提供。然而,我们可以在内存中保存的是一个目录表,其中包含对原始数据、目标标签和任何其他有用的元数据的引用,以支持上面概述的采样方案。以下是一个假设的元数据文件:
改变采样的一个简单方法是获取一个torch.utils.data.WeightedRandomSampler,
并应用一个脚本来计算元数据表中的权重列。或者,为了只包含一些实例而不包含其他实例,我们可以动态地确定所需的行索引,并应用一个torch.utils.data.SubsetRandomSampler.
。这些都是非常好的选项,这正是我们开始时所做的。但是我们的实验规范的复杂性随着时间的推移而增长;数据准备错误悄然而至,我们希望无需编写代码就能进行实验。因此,我们开始考虑更通用、更灵活的采样机制。我们提出的是一个由 YAML 规范配置的采样器的子类。由于我们的整个机器学习框架由 YAML 文件管理,采样现在构成了它的一部分。经过几轮的改进,我们最终得到了一个TreeSampler
,我将在这篇文章的剩余部分描述它的设计。
为了深入研究这个问题,想象一下正面的例子通常是很少的,但是为了平衡这些类,我们想对它们进行 30%的额外采样。所以我们的初始树只包含一个根节点和两个叶节点:
注意到一个节点自然地包含了WeightedRandomSampler
和SubsetRandomSampler.
的两个方面*,我们可以将满足选择条件的行子集与子节点相关联。每个节点的任务是一次枚举一个子节点。有不同的方法可以做到这一点。当然,熟悉的随机抽样概念“有替换”或“没有替换”浮现在脑海中。严格地说,只有前者才构成真正意义上的抽样;后者可以通过一个简单的列表迭代器来实现。列表通常会在枚举完所有元素后重新排列,但在某些情况下,不这样做是有用的。因此,总而言之,定义三种可能的采样模式是明智的:replacement,
sequential,
或shuffle.
*
在下文中,我将进一步形式化树及其节点的规范,并基于我们的玩具数据模式演示几个用例。
案例 1:加权抽样
采样权重通过对所有兄弟节点进行归一化来归纳概率。在树的图片中,我们可以标记父节点和子节点之间的分支。但是为了保持整洁,相当于给每个子节点赋予一个权重。注意,这些重量属于父母的取样程序;这意味着如果父代应用顺序或无序采样,它们就没有意义。所以让我们把上面的树写成如下:
节点名称出现在括号中,仅供参考。column
元数据文件中的表头行;value
特定内容。如上所述,mode
可以是replacement,
sequential,
或shuffle.
中的一个,这里权重作为常数给出。为了获得标准意义上的实例权重,我们还允许一个包含任何列名的表达式,比如用weight: proportional(conf)
或weight: proportional(count)
表示行数。
隐式地,子集选择使用等式操作符__eq__();
将列与指定的值进行比较,这是一种简单但可能不太常见的选择机制,它允许使用任何其他比较操作符。
案例二:多条件
表达多层次采样条件的能力是建立树的首要原因。在我们的玩具数据集中,我们有两种类型的负面例子,measured
和synthetic;
这里是如何以 60%:40%的比例平衡它们,此外还有与之前相同的活跃/不活跃类平衡:
敏锐的读者当然会认识到,对于主动、测得的阴性和合成的阴性示例,等效于权重分别设置为 .3、. 42 和. 28 的标准WeightedRandomSampler,
情况 3:欠采样多数类
先前的树正在生成无限序列的替换采样。以这种方式训练是有效的,尽管传统上,在 epochs 中的训练更常见——通过所有可用的数据完成扫描,然后重新洗牌。比方说,我们想对正面的例子这样做,但是我们有大量的合成的反面例子,并且不关心在每个时期列举它们中的每一个。进一步假设我们也想平衡正面和负面。然后,我们可以通过设置如下模式对多数类进行欠采样:
案例 4:均匀采样
我们如何描述以相同的频率对每种蛋白质进行采样,而不管我们的元数据包含每种蛋白质的多少数据?简单的解决方案是为每种蛋白质构建一个包含一个节点的树,并分配相等的权重:
然而,这个列表可能会很长,手动编写会非常繁琐!所以让我们发明以下捷径:
在构建树时,“magic”for_each
值就像正则表达式中的通配符:对于文件中遇到的每个惟一值,它克隆一个兄弟节点。
情况 5:重复
测试时间增加是一种常见的做法,可以稍微提高预测的准确性。我们对相同实例的多个副本的预测分数进行平均,但是应用了不同的增强。节点属性repeat
就是为了做这件事而设计的。比方说,我们希望聚合每个示例的 4 个以上的扩展:
这将生成四个相同活动实例的序列,后面跟着四个相同的非活动实例。
案例 6:约束
现在让我来看最后一个也是最复杂的例子。假设我们正在训练一个连体网络:每个实例由 2 个连续的蛋白质/分子对组成,具有相同的蛋白质但不同的分子;这对中的一个是活动的,另一个不是。
正如我们在上面看到的,我们可以使用sequential
模式在活动和不活动之间切换。但是我们需要在这方面做更多的工作;也就是说,将蛋白质限制在两者之间。这里repeat
功能再次派上了用场:
为了便于说明,让我们按顺序完成第一个请求的步骤:
root
有多少种蛋白质,就有多少个孩子。不失一般性,假设它选择node_prot_1.
2.node_prot_1
是一个顺序节点,所以它选择它的第一个子节点node_active.
3.node_active
是一个叶节点,所以它用protein=prot1
和active=1
随机选择元表中的某一行。这是TreeSampler
返回的例子。
在第二个请求中,
root
是一个重复的节点,所以它必然会选择和以前一样的节点,node_prot_1.
node_prot_1
是一个有两个子节点的顺序节点,由于它先前列举了第一个子节点,所以现在它前进到node_inactive.
node_active
是一个叶节点,所以它随机选取一个带有protein=prot_1
和active=0
的行。这是第二个请求中返回的示例。
第三个请求将再次跟随第一个请求的脚步。
算法
我们目前正致力于共享我们的代码;然而,它应该(但愿如此!)不难想象到现在TreeSampler
的实现。初始化以类似于上面所示的格式读取元数据文件和配置。树是从根到叶递归构造的;子节点被创建并与根据给定条件过滤的示例子集相关联。
我想指出一个乍一看可能并不明显的怪癖:配置和数据集的某些组合可能会导致空的(不可满足的)节点。例如,针对特定于目标的分类度量的训练要求每个蛋白质至少有一个活动的和一个非活动的例子;然而,对于某些蛋白质,元数据可能只包含活性物质或不含活性物质。这就是我们的修剪算法的用武之地。使用节点属性prune_method: individual,
,我们可以忽略(删除)这个单独的空节点。但是在这种情况下,通过指定prune_method: parent.
更有意义的是完全不考虑蛋白质,注意修剪可以沿着树向上传播多个步骤,直到到达prune_method: individual
(或者根,此时整个树是空的)。还要注意,修剪会改变聚集的节点权重,因此最终的采样概率是在最终的自底向上过程中计算的。
在我们的第一个实现版本中,我们曾经使用pandas.DataFrame,
存储示例子集,但是后来的优化导致了相当大的加速:实际的示例可以在构造后被丢弃。我们需要维护的只是索引数组,在内部节点的情况下指向子节点,在叶节点的情况下指向元数据文件中的行号。这些可以使用 NumPy 函数直接处理,而不必求助于 pandas 。
和其他采样器类一样,在运行时,每次调用TreeSampler:__next__()
都会返回一个元数据行的索引。它是沿着从根到叶子的路径来完成的。每个节点维护一个本地枚举状态,并根据其指定的属性彼此独立地进行采样。与其为每个实例请求生成一个随机数,不如让所有类型的节点在用完时创建一个完整的子索引缓冲区,这样效率会更高。三种采样模式的区别仅在于补充方式:替换模式的自举采样,洗牌模式的重新洗牌,或者顺序模式的除了倒带什么都不做。根据我们的经验,采样器是非常有效的,每次迭代只需要我们总时间的一小部分,即使对于有数百万行的文件也是如此。
辅助样本值
为了使模型训练运行可重复,通常建议将随机种子初始化为已知值(这意味着可以使用的所有随机值 python 库、NumPy、PyTorch 和 cudNN 的随机数生成器)。但是容易被忽略的是 PyTorch DataLoader
使用多个工作进程(基于multiprocessing
包)来预取 IO 密集型的例子,同时应用转换和扩充。所有这些过程都依赖于它们自己独立的随机数发生器。即使将它们都初始化为相同的值,也不能保证确定性,因为随着时间的推移,小批量到进程的分配顺序会因竞争条件而变化。
我们发现,在不引入太多代码复杂性的情况下,使数据集转换真正具有确定性的唯一方法是,生成与采样行索引序列并行的所有所需随机种子和参数的序列。例如,对于随机(2D)旋转,我们可以与每个采样的行索引一起生成一个角度。回想一下,采样器是驻留在主进程中的单例对象——因此,所使用的随机数生成器没有歧义。我们允许用户配置一个可变大小的命名迭代器列表。它们的结果被打包成一个数据类结构,传递给转换。我们的转换管道在整个 YAML 配置中也有专门的部分,我们的转换类知道如何从生成的数据记录中挑选出它们需要的值。这样,工作进程的所有局部随机性都被绕过了。
当然,这种机制可以控制任何迭代器序列,而不仅仅是随机值。假设对于测试时间增加(案例 5),我们希望对 4 种不同的作物进行平均,并且CropTransform
类接受一个字符串loc
参数。然后,我们可以通过指定参数loc: itertools.cycle([‘lower_right’, ‘lower_left’, ‘upper_right’, and ‘upper_left’]).
从采样器配置中集中控制这一点。这些值不依赖于所选示例的细节,并将连续出现在一行中。
总结
自定义数据集通常带有相关的元数据字段;探索使用它们来确定训练和评估机器学习模型的采样方案的最佳方式是值得的。在采样过程中,对于加权、子集化和约束示例,可能有各种合理的选择。为了避免重复硬编码或者为每个这样的实验改变元数据文件,我们提出并实现了一个 PyTorch TreeSampler
类。在初始化时,它是根据配置和元数据文件动态构造的。每个节点基于逻辑条件选择一个示例子集;它以顺序、无序或替换模式对其子节点进行采样,彼此独立。在后一种情况下,可以根据元数据列或行计数将频率权重作为常数分配给子代。为了控制跨多个工作线程的数据扩充和转换,有一种生成辅助参数和随机数序列的方法很有帮助,这种方法与采样器生成的行索引并行。
致谢
这项工作是我和来自 Atomwise 的令人敬畏的(前)同事 Misko Dzamba 和 Bastiaan Bergman 合作完成的——谢谢!