[指南]使用 React、NodeJS 和 MySQL 构建数据科学 Web 应用程序
数据科学是任何企业的强大工具。然而,为了将数据科学应用到业务中,我们必须开发一种工具,普通用户可以轻松地使用它来从查看数据中获益。
Tableau 或 PowerBI 等数据可视化工具是数据探索工具如何为任何业务增加巨大价值的绝佳例子。
最近,我有机会为股票市场数据从头开始开发另一个数据探索工具。由于我必须处理从服务器到开发 API 和前端应用程序的所有事情,我觉得这些知识和经验可能对其他希望开发数据科学 web 应用程序的人有所帮助。
在这篇博文中,我将带您了解构建我几周前构建的 web 应用程序的过程和开发。如果你有兴趣看代码,请随时通过 Twitter ( @woraperth )告诉我。以后我可能会考虑在我的 Github 上开源代码。[由于许多请求,我已经清理了代码并将其发布在 Github 库上]
股票分析器 Web 应用程序
这是我在 1 周内构建的 web 应用程序。它不是最漂亮的,但我希望界面足够简单,让用户看得懂。
我们为什么需要这个应用程序?
分析师想要分析股票市场中可能成为他们潜在客户的公司。逐一查看每家公司会花很多时间。他们需要易于使用的工具来帮助他们加快这个过程。
**“股票分析器”**的目标是利用股票市场数据并构建简单的界面来帮助分析师找到合适的客户。
我们有哪些数据,在哪里可以找到这些数据?
数据由每个股票市场中每只股票每天的股价组成。
股票市场数据可以从财经网站获得,如 Yahoo!金融。它允许您指定日期范围,并下载每只股票历史数据的 CSV 文件。
项目架构:React & NodeJS & MySQL
(Image by author)
最重要的部分是计划我需要什么技术,以及我如何将它们联系在一起。我决定通过使用 NodeJS 构建 API 并将数据发送到 React 前端来挑战自己。这是我第一次编写 NodeJS 代码,也是我第一次以现代方式构建 web 应用程序(将后端与前端分离)。
我看到很多现代网站都是这样发展的,而且他们的网站都很快。我知道我可能很难开发它,因为我以前没有这样做过,但从长远来看,了解它是如何工作的会让我受益匪浅。
下面是这个应用程序如何在幕后工作:(请按照上面的架构图像,从左到右&从上到下)
- 用户访问一个网站,看到由 React 构建的前端
- 当用户请求数据时,前端会连接 NodeJS 构建的 API 端点请求数据
- NodeJS 将从 MySQL 数据库中查询数据,然后将结果发送回前端
- MySQL 数据库会将文本文件中的数据加载到数据库中,每隔一段固定的时间,例如每天。
项目准备和组织:数字海洋和 Git
(Image by author)
因为我计划将后端和前端分离,所以我可以在不同的服务器上设置后端和前端,没有任何问题。
对于后端,我选择在 DigitalOcean 上安装云服务器(类似于 AWS 的服务,但使用起来简单得多)。我喜欢 DigitalOcean,因为它易于安装,支持团队提供了许多关于如何在他们的云服务器上安装不同软件的有用文章。
下面是我用来安装 NodeJS、Nginx 和 MySQL 的文章列表:
- NodeJS 安装教程(这也将教你使用 PM2,它有助于在后台服务 NodeJS 应用程序。因此我们不必保持终端窗口打开):https://www . digital ocean . com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-Ubuntu-16-04
- Nginx 安装教程(为了暴露 NodeJS 端口,我们需要 Nginx 做反向代理。我们必须首先启用防火墙,然后允许 SSH。否则我们将被锁在服务器之外):https://www . digital ocean . com/community/tutorials/how-to-install-nginx-on-Ubuntu-16-04
- MySQL 安装教程:https://www . digital ocean . com/community/tutorials/how-to-install-MySQL-on-Ubuntu-16-04
对于上面的 Nginx 部分,确保运行这个命令来允许 SSH:
sudo ufw enable
sudo ufw allow ssh
对于前端,我可以使用任何可以提供 HTML 页面的 web 服务器。
开发流程和技术堆栈
(Image by author)
为了开发这个应用程序,我首先按照上面提到的教程设置了 git 存储库和服务器环境。然后我开始开发 API 端点。测试完 API 后,我开发了前端应用程序来连接 API。
我用的技术是数据库用的 MySQL ,后端 API 用的 NodeJS ,前端用的 React 。选择这些技术是有原因的:
为什么选择 MySQL
(Image by author)
- 因为股票数据存储在结构化数据文件(CSV)中,所以它可以轻松地存储在关系数据库(如 MySQL)中
- MySQL 是开源的,受欢迎的,并且支持标准的 SQL 命令。对我们来说,转换到任何支持 SQL 的数据库软件都很容易
- 通过在 NodeJS 中使用“mysql”模块,可以自动对输入进行转义以防止 sql 注入
为什么 NodeJS
(Image by author)
- 另一种流行的开发后端的编程语言。用于 LinkedIn 和易贝等大公司
- 快速运行并支持并行查询
- 使用 Express 构建 API 很简单
- 使用 JavaScript 语法,这使得代码和知识可以与前端共享
- 最重要的是,我以前没有用过它,想学习如何使用它
为什么要反应
(Image by author)
- 易于维护、扩展、重用网页上的组件
- 庞大的第三方组件库,可以即插即用
- 良好的用户体验,因为它非常快
- JSX 太棒了
数据管道:使用 NodeJS 调度数据加载
Screenshot from Node-schedule project at GitHub
我使用 NodeJS 包节点调度器来安排在每天凌晨 1 点将数据加载到 MySQL 数据库中。语法与设置 cronjob 相同。因此,我们可以使用在线工具来帮助编写 cron,如 crontab 。
注意,我是数据工程领域的新手。因此,这可能不是计划任务的最有效方式。我还发现,通过 NodeJS 加载数据比用 MySQL 命令行直接加载数据需要更多的 RAM。
用 NodeJS 记录日志
(Image by author)
日志记录非常有用,尤其是当我们在后台运行进程时,例如数据加载。我们可以随时打开日志文件,看看在我们睡觉的时候是否出了什么问题。
我使用 NodeJS 包 winston 来管理日志。winston 允许我将错误从警告信息中分离出来,这非常方便。
这个项目中有两个日志文件:
- logs/error.log — SQL 错误
- logs/combined.log —警告&信息
额外的功能:股票比较
股票分析器的第一个版本可以一次看到一只股票的表现:
(Image by author)
“数据点只有在与其他数据点进行比较并在它们之间建立联系时才能获得价值。我不相信一个组织可以在没有标杆的情况下存在。”
——Jan-Patrick Cap,引自《外部观察》(2017)
我觉得一次只显示一只股票很枯燥。它也没有给将要使用这个工具的分析师带来多少价值。
最近,我读了一本名为《外部洞察力》的好书,书中谈到了我们如何利用外部数据的力量。有一个关于标杆管理的话题让我很感兴趣。
我决定进一步开发这个应用程序,增加比较多只股票的功能。这样,分析师可以看到这些公司在市场上的实际表现。在某些情况下,我们可能会发现,与其他公司相比,业绩非常好的公司可能收益很小。
(Image by author)
我从这个项目中学到了什么
这是一个伟大的项目,我学会了使用新技术和新方法将不同的技术联系在一起。与 CMS 后端的传统网站相比,我发现 NodeJS API 的反应非常快,而且成本很低(我每月为云支付 5 美元)。然而,当交通拥挤时,速度可能会很慢。
将来,我可以改进数据工程过程,以达到行业标准。因为我以前没有这方面的经验,所以我有兴趣学习更多关于数据工程的知识。也很难找到实用指南。如果你知道研究这个的好地方,请随意分享:)
我还想探索其他数据库软件,看看哪一个性能最好。我在考虑使用 Postgres,或者像 MongoDB 这样越来越受欢迎的 NoSQL,或者像 BigQuery 这样的云数据存储也可能是一个不错的选择,因为这个应用程序更像 OLAP 而不是 OLTP。
我希望这个博客对正在寻找开发数据科学 web 应用程序的人有用。如有任何问题,请随时在 Twitter @woraperth 上联系我。
附录
这是我从这个项目中学到的有用的东西。如果您想使用相同的技术栈来开发项目,它会派上用场。
常见问题
- 运行“npm start”后出现错误“listen EADDRINUSE 8080”
运行“killall -9 node”通常可以修复该问题。阅读更多关于 StackOverflow 的信息。 - 要求等待许多任务完成后再做下一部分
我们可以用 Promise 来处理异步。很久以前,我用样本代码写了一个博客,但它仍然运行得很好(除了 2018 年我们不再需要 polyfill) - 试图通过 NodeJS API 端点
加载数据时没有足够的 RAM 如果浏览器标签失效,也没关系,因为这是由于浏览器超时造成的。如果节点失效,请将 droplet 的内存增加到 2GB 或 4GB,然后再试一次。 - 服务器时间不在墨尔本时间
运行此命令设置时区:sudo dpkg-reconfigure tzdata
检查当前服务器时间:timedatectl - 如何检查 mysql 是否正在运行
运行此命令检查 MySQL 状态:system CTL status MySQL . service
如果 MySQL 没有运行,运行此命令启动 MySQL: sudo systemctl start mysql
最初发表于 沃拉特纳珀斯 。
学术研究论文阅读指南
学会用系统的方法处理这个费力的过程!
My desk full of papers. Slightly shuffled around for dramatic purposes of course.
在数据科学和机器学习领域工作是一个令人兴奋和具有挑战性的领域。新的技术和工具在不断渗透,老实说,这种感觉让人无法抗拒。许多这些新发展都是在学术研究文章中发现并首次披露的。从这些文章中提取知识是困难的,因为这些论文的预期读者往往是其他研究人员。然而,为了跟上时代潮流,阅读论文是一项基本技能——幸运的是,这项技能可以通过勤奋和练习来提高。
在研究生院,你很擅长(应该会很擅长…)阅读论文和吸收研究成果。不是每个人都会接受这方面的培训,但这并不意味着你不能从这些文章中获益。公共税收是这项研究的大部分资金来源。这里的目标是使学术界民主化,只是一点点,并为你在浏览论文时提供一个应用的脚手架。
我阅读论文的方式并不特别独特,但它很有效,对我很有帮助。请记住,这不是唯一的方法,有很多技巧,随着你阅读的越来越多,我相信你会找到自己独特的风格。
本指南细分如下:
- 学习这个技能会帮到你!我保证
- 我听说读论文很难。为什么?
- 论文通常是如何组织的?
- 我阅读论文的“防弹法
- 帮助您完成工作的工具
为什么要学看论文?
阅读报纸当然可以培养性格,因为这通常要花很多时间,而且不能保证你能看完整个故事。这并不是贬低你,而仅仅是为了公开透明。阅读论文是困难的,这是毫无疑问的。机器学习、深度学习、数据科学、数据库和数据工程等领域的进步通常以学术研究的形式出现,其语言是学术论文。想想你可能会用到的一些技术:卷积神经网络、 PCA 、 AdaBoost (甚至深度增强!).这些都来自研究,是的,他们都有论文。此外,考虑到有许多关于这些技术的应用和使用的论文,当您试图解决一个特定的问题时,这些论文可能是至关重要的。除了跟上当前的研究之外,回到过去读一读旧论文也是值得的。你会学到很多东西。我保证。
纵观深度学习领域,似乎每隔几天或几周就会有一篇新的批判性论文问世。掌握它的唯一方法是拿起报纸读一读。
哪里出现困难…
这是哈伯德和邓巴 2017 年发表的一篇科学论文中的一个数字,关于阅读科学论文。科学论文盗梦空间!
Fig 2. Different sections of scientific papers are considered easy to read and important at different stages of academic careers.
答:参与者认为某一部分容易阅读(表现为“有些容易”、“容易”、“非常容易”的组合)的比例,作为职业阶段的函数。卡方检验的结果显示在左侧。 B: 作为职业阶段函数的部门平均重要性等级。为清楚起见,省略了单个点的误差线,唯一的灰色误差线表示任何数据点的最大 95%置信区间。数据点上方的星号表示与之前的职业阶段相比,响应有显著差异,这由 Mann-Whitney 事后测试确定。
一个不足为奇的结果表明,学术上的进展越深入,他们就越容易阅读论文的每一部分。有趣的一点是,各个职业阶段如何看待每个部分的重要性。方法、结果和数字似乎非常重要,表面上是因为作为的学者他们在自己的领域有更高的技能,这使得他们可以对论文的方法提出批评。这也意味着他们非常了解自己的领域,因此,引言和摘要就不那么重要了。早期阶段的博士生发现这些方法、结果和数字很难理解。这非常有意义,因为这些领域是论文中最需要某个领域知识的部分。你很可能有类似的经历。
到底是什么让这个过程如此困难和耗时?
- 作者倾向于假设读者的重要背景知识
- 学术语法非常密集,因此读者很难分析
- 数学表达式通常被压缩,为了简洁,方程被重新排序,经常跳过推导的步骤
- 如果读者阅读了被引用的论文,大量的知识空白就被填补了(有点像——你需要经验才能得到工作,但需要工作才能得到经验!)
- 不是所有的结论都是正确的。小样本量和功效、糟糕的研究设计、研究者偏见和选择性报告确保了你必须成为批判性读者!
显然,阅读论文时需要考虑很多问题。害怕吗?是时候放松一下了。这里有一篇有趣的文章,由科学的亚当·鲁本博士写的关于阅读论文的恐惧。这表明,即使是科学家也同意,论文很难阅读,而且考虑到它们的密度,会让你保持规律。
想想看,你读的论文越多,你学到的就越多,这个阅读的过程就变得越快。趋势开始浮现在眼前,你开始洞察科学方法,了解某些作者和团体正在做什么,并对你正在学习的领域形成一种欣赏。随着时间的推移,所有这些知识和技能都会让你更快、更有效、更成功地阅读论文。学习读报类似于学习吃饭。一开始很乱,你的调色盘也不是很发达。但是随着时间的推移,你的饮食经验会增加,你会更多地了解你喜欢什么和不喜欢什么,以及厨师的饭菜是好是差。
论文是如何组织的
这里有好消息。绝大多数论文或多或少遵循相同的组织惯例:
- 标题:希望朗朗上口,可能性感!包括关于作者和他们的机构的附加信息
- 摘要:高度概括
- 引言:该领域的背景信息和导致本文的相关研究
- 方法:非常详细的部分研究进行,它是如何设置的,使用的任何工具,最后,过程和工作流程
- 结果:作者谈论的是创造或收集的数据,它应该被理解为对所发生的事情的无偏见的描述
- 讨论:作者在这里解释结果,并说服读者他们的发现和假设
- 参考资料:在正文中引用的任何其他作品都会在这里显示
- 附录:更多的数字,相关数学的额外处理,或者额外感兴趣的项目可以在附录中找到它们的方式
开发系统方法
当你坐下来阅读时,制定一个计划是很重要的。仅仅从第一页开始阅读到最后一页可能对你没有好处。除了保留有限的信息之外,你会精疲力尽,而且付出巨大的努力却收获甚微。这是很多人止步的地方。
做好计划,从花 3-6 个小时真正消化一篇论文,记住它们非常密集!准备好并愿意让通过论文几次,每次都希望提取不同的信息和理解。请帮你自己一个忙,不要在你第一遍的时候从头到尾读完这篇论文。
下面是两个列表。我在阅读论文时或多或少采取的系统方法。)我在浏览论文时试图回答的一系列问题。我通常会根据论文增加更具体的问题。
我们开始吧!
- 试着找一个安静的地方呆几个小时,拿上你最喜欢的饮料(可能是咖啡、茶,或者任何其他东西)。现在我经常发现自己在华丽的咖啡店里工作。
- 从阅读标题和摘要开始。旨在获得论文的高水平概述。作者的主要目标和高水平的结果是什么。摘要通常为论文的目的提供一些线索。把摘要想象成广告。
- 花大约 15 分钟略读这篇论文。快速浏览图表,并注意阅读文本时要注意的关键词。试着对报纸的布局和东西的位置有个概念。稍后,你将在不同的部分和页面之间来回引用,这有助于了解资料的位置。试着不要花时间做笔记或强调/强调任何事情。
- 把注意力转向简介。我对论文/领域越不熟悉,我花在介绍上的时间就越长。作者倾向于做好巩固背景信息和提供大量参考资料的工作。这一部分通常是最容易阅读的,感觉就像你在读教科书一样。记下你不知道或不想进一步研究的其他参考资料和背景信息。
- 这部分极其关键。仔细浏览每一个图形,试着感受它们告诉你的东西。当我还是本科生的时候,我的神经科学导师给了我一些很好的建议。转述:“图包含了论文中一些最重要的信息。作者花了大量的时间创建它们,并认为它们包含的信息足够重要,可以通过视觉传达给读者。请特别注意它们。“第一次浏览时,你不会很好地理解所有的数字,但你会对作者认为最重要的东西有所了解,而且你还会透露出阅读其他部分时需要注意的有价值的信息。
- 到目前为止,你可能已经花了大约一个小时。休息一下。散散步,享受羊角面包!
- 现在你已经准备好第一次通过纸张了。这次你应该开始做一些高水平的笔记。你会遇到对你来说陌生的词汇和想法。不要觉得你需要在每一件没有意义的事情上停下来,你可以简单地标记它,然后继续前进。目标是花大约一个半小时。你不想陷入所有血淋淋的细节。第一遍的目标是熟悉论文。就像第一次约会。你要了解报纸,问一些好问题,也许会让它发笑。但你不会想深入每一个细节。那是粗鲁的。从摘要开始,快速浏览简介,给方法部分一个勤奋的通行证。请注意整体设置,方法部分包括大量的细节,此时您不需要仔细检查每个部分。最后,阅读结果和讨论部分,了解关键发现以及这些发现是如何确定的。请记住,作者试图说服你,读者,他们工作的价值和发现。
- 被铃声救了。休息一下,做一些跳跃运动,让血液流动起来。除非你在咖啡店里。那就别这么做。
- 现在你已经对论文有了一个很好的概述,你将进入数字的本质。在阅读了方法、结果和讨论部分之后,你应该能够从这些数字中提炼出更多的精华。找到那些宝石。目标是再花 30 分钟到 1 小时在这些数字上。
- 你应该对第二次全面通过试卷充满信心。这一次你将会以批判的眼光阅读。这个过程可能需要一两个小时,你也可以把它留到当天晚些时候,或者第二天。特别注意你标记为难以理解的地方。不要留下未定义的单词,并确保你理解每个句子。这一关你是在尝试真正的学习论文。浏览你有信心的领域(摘要、介绍、结果)。重点应该是巩固你以前不理解的内容,掌握方法部分,最后成为讨论部分的批判性读者。在讨论部分,你可以考虑作者的推理/理性,并把你从阅读论文中学到的东西与论文中提供的证据进行权衡。这一部分应该会引发一些有趣的问题,让你问你的朋友或同事。你甚至可以给论文作者发电子邮件,提出一个有见地的问题!他们可能需要一段时间才能给你回复,但是作者确实喜欢就他们的研究进行对话,并且通常非常乐意回答读者的问题。
- 在这一点上,你应该有信心与同事谈论这篇论文,批判性地思考结果,并能够将这项工作与该领域的其他研究进行比较(如果你读过其他论文)。为了保留和强化你所学到的东西,我建议你写关于论文的内容。它可以简单地是几段关于你学到了什么和结果的意义。当你通读这篇论文时,你可以参考你所回答的问题列表。
如上所述,这里有一个通用的问题列表来帮助指导你。如果你能回答这些问题,你就对这篇论文有了很好的理解,至少你能就此与他人进行明智的交流。
1\. What previous research and ideas were cited that this paper is building off of? (this info tends to live in the introduction)
2\. Was there reasoning for performing this research, if so what was it? (introduction section)
3\. Clearly list out the objectives of the study
4\. Was any equipment/software used? (methods section)
5\. What variables were measured during experimentation? (methods)
6\. Were any statistical tests used? What were their results? (methods/results section)
7\. What are the main findings? (results section)
8\. How do these results fit into the context of other research and their 'field'? (discussion section)
9\. Explain each figure and discuss their significance.
10\. Can the results be reproduced and is there any code available?
11\. Name the authors, year, and title of the paper!
12\. Are any of the authors familiar, do you know their previous work?
13\. What key terms and concepts do I not know and need to look up in a dictionary, textbook, or ask someone?
14\. What are your thoughts on the results? Do they seem valid?
我建议亲自或在网上找人来讨论这篇论文。建立一个日志俱乐部,目标是每月完成 1-2 篇论文。通过与朋友讨论论文,我获得了巨大的额外洞察力。纪念…唯一比独自忍受论文的痛苦更好的事情,就是和朋友一起忍受它!
另外,Keshav 写了一篇关于如何阅读论文的好文章。他介绍并探索了一种三阶段方法,您可能会感兴趣。也读一读吧!
帮助您完成工作的工具
您可以从以下几个来源找到论文:
- 一个 rXiv :是一个开放存取的知识库(维护在康乃尔),在这里你可以免费下载和阅读许多定量领域的预印研究论文。这里是更多关于 arXiv 的一般信息。你在网上找到的许多论文都会链接到 arXiv 论文。
- PubMed :他们说得最好:“ PubMed Central (PMC)是美国国立卫生研究院国家医学图书馆(NIH/NLM)的生物医学和生命科学期刊文献的免费全文档案。如果你正在寻找医学或生命科学相关的论文,PubMed 有一个强大的搜索功能。
- 谷歌学术:我会像使用谷歌一样使用谷歌学者。只需搜索一个主题、作者或论文,谷歌就会代表你开始工作。正如谷歌所说的“”谷歌学术提供了一种广泛搜索学术文献的简单方法。从一个地方,你可以搜索许多学科和来源:文章,论文,书籍,摘要和法院意见,从学术出版商,专业协会,在线仓库,大学和其他网站。谷歌学术帮助你在学术研究领域找到相关的工作。
- 社交媒体:我通过跟踪和保持与几个积极发表文章的人的联系,发现了很多新论文。附加奖金…他们通常会推送他们感兴趣的、你可能想了解或阅读的其他论文。
- 朋友和同事:找到和你兴趣相同的人,和他们一起读论文,互相学习。我从朋友那里得到好论文的推荐。它们是很好的过滤器。
- 大学:去你当地的学院或大学(如果附近有)可以让你接触图书馆,图书馆员(非常有用的搜索向导!)和许多期刊,在那里你可以找到并阅读在线付费墙后面的文章。
当你开始阅读更多的文件时,你会想把它们存放在某个地方。将 pdf 文件放入你硬盘上的一个文件夹中固然很好,但是缺少了一些物质享受。大多数研究人员和研究生使用一个咨询经理。 Zotero 和门德利很受欢迎,我喜欢 Zotero。最近一直在用纸堆。我喜欢 PaperPile,因为它是轻量级的,存在于我的浏览器中,并使用 Google Drive 来备份和存储我所有的 pdf。它有一个简单的,令人耳目一新的用户界面,它有一个非常好的标签和文件夹层次系统。我还可以在我的浏览器中注释 pdf,并在写作时建立引用列表。几乎任何参考资料管理器都有很多这样的功能,但我碰巧最喜欢 PaperPile。
随着你收集和阅读越来越多的论文,推荐人将很快成为你最好的朋友。
感谢您通读这篇文章。我希望它对你有所帮助,并且在你处理下一篇论文时给你一些好的想法。大多数人在看论文的时候都有自己独特的流程。我相信你会及时开发你自己的调整,希望这是一个很好的模板让你开始。
现在只需要相信过程。
我也希望我们能从读者那里得到一些好的反馈和评论,以及其他的提示和技巧。
干杯,
狭海峡
联系我:datasci@kmshannon.com
linkedin.com/in/kmshannon/
twitter.com/ButFirstData
[1]哈伯德,K. E .,&邓巴,S. D. (2017)。对科研文献的看法和阅读论文的策略取决于学术生涯阶段。 PloS one , 12 (12),e0189753。
[2]在 CoffeeCycle 向克里斯大喊!简直是圣地亚哥最好的咖啡。
[3]凯沙夫,S. (2007 年)。如何阅读一篇论文? ACM SIGCOMM 计算机通信评论, 37 (3),83–84。
NIPS 2018 竞赛赛道指南以及如何参与
今年是国家实施计划的第二个年度“竞赛轨道”。在竞赛跑道上,有许多有趣的比赛项目可供参加。所有的活动都很容易参加,并跨越 ML 的许多领域。竞赛适用于广泛的行业,包括汽车、安全、医疗保健和物理。
终身机器学习 AutoML(截止日期 10 月 26 日)
这场比赛的重点是建立一种算法,可以在没有任何人工干预的情况下,自动为后续任务创建预测模型。竞赛分为两个阶段:反馈阶段和测试阶段。在反馈阶段,组织者提供了五个与最终测试数据集性质相似的数据集。在第二阶段,上传的模型代码将在五个测试集上进行盲评估。这种竞争具有挑战性,因为分布将随着时间的推移以及各种特征类型(即分类、时间、二元等)的变化而缓慢变化。所有必要的数据和说明都可以在官方网站上找到。
对抗性视觉挑战(最终提交日期 11 月 1 日)
这项挑战的重点是制造鲁棒的视觉计算机视觉算法,对敌对的干扰有抵抗力。这对于保护计算机视觉模型免受恶意用户的攻击以及创建更加健壮的模型非常重要。首先,你可以转到 GitLab 库,然后按照指示进行操作。
对话智能挑战 2(convai 2)(9 月 30 日提交截止日期)
这是一个旨在开发非目标导向聊天机器人的竞赛。为了比赛,他们提供了一个有趣的新数据集,这个数据集是通过将众包工人配对在一起,给他们一个简单的角色并让他们聊天而制作的。评估将分三步完成:通过自动化评估指标(困惑、F1 和点击率),对机械土耳其人的评估,以及志愿者与机器人聊天的“野生”评估。
你可以在这里找到基线模型的源代码。首先,你需要创建一个私有的 Github 库,并与组织者共享。更多信息请访问官方网站。
这是一个与物理学相关的挑战,涉及使用机器学习来检测粒子在碰撞后的路径。由于需要非常快速地对粒子碰撞数据进行分类,这项竞赛非常重要。目前,预计算法不会随着来自 LHC 的数据量的增加而扩展。比赛正在进行,包括几个不同的部分。第一阶段始于 5 月,将于 8 月结束,重点是重建粒子轨迹的准确性。第二阶段(NIPs 竞赛的焦点)关注模型在测试时的速度。
Kaggle 比赛(已完成)
(截止日期 11 月 26 日)
这项比赛的重点是训练一队 AI 智能体,在 Bomberman 的策略游戏的变种 Pommerman 的游戏中与另一队 AI 智能体的玩家进行比赛。这个竞赛是一个有趣的多智能体学习实验,在互补和对抗两个层面上。更多信息请参见 Github 或上面的官方网站链接。
包容性图片(截止日期 11 月 9 日)
NIPs 2018 赛道的又一场比赛。该竞赛旨在解决数据失真的问题。“具体来说,在这场比赛中,研究人员将在开放图像【2】上进行训练,这是一个大型、多标签、公开可用的图像分类数据集,已被发现表现出地理倾斜。”
AI 驾驶奥林匹克(截止日期 11 月 30 日)
这个你可能已经很熟悉了,因为它已经在 Reddit 和其他网站上流传,但如果你不熟悉,我会描述一下。“人工智能驾驶奥运会”于 10 月正式开始,将由两部分组成,第一部分于 12 月在 NIPS 举行,第二部分于 5 月在 ICRA 举行。比赛由三个独立的任务组成:车道跟踪,动态车辆的车道跟踪,以及动态障碍物的导航。你可以去他们的官方网站了解更多信息。
最后但同样重要的是人工智能假肢挑战赛。这项挑战旨在使用 RL 来学习最佳策略,以帮助装有假肢的人跑步。这个挑战是 RL 在现实世界问题中的有趣应用。这是“学习跑步”在 NIPs 的第二年,有相当多的文章让你开始。最后,你可以在 GitHub 库上找到所有的额外信息和更多挑战信息。
正如你所看到的,今年在 NIPs 有一系列真正有趣的挑战,跨越了许多行业和机器学习领域。希望你能找到一个你感兴趣的并参与竞争。祝你好运!
用数据科学指导你在非洲的下一笔房地产投资
这里的目标是在选定的非洲国家( 【加纳】 , 肯尼亚 和 尼日利亚 )中确定利润最高和利润最低的国家/地区,以便用数据科学技术指导您的房地产投资决策。
投资房地产的理想社区应该有
- 高租金
- 低价出售。因此,租金将高于每月的按揭付款,以赚取利润。
为此,我们需要一种方法来比较租赁价格和销售价格。对于每个街区,我们计算了两件事:
- 租赁价格的每平方英尺单价和销售价格的每平方英尺单价
- 假设 20%的首付、19.67%的年利率(选定国家的平均值)和 30 年的抵押贷款期限,平均每月抵押贷款额。
在我们开始比较租赁价格和销售价格之前,先看一下数据集是很重要的。
我注意到一些社区很少有甚至没有挂牌。列表数量少可能会带来不稳定性并导致错误的结果。因此,我删除了等于或少于 5 个列表的邻域,以确保数据的质量。
Data showing distribution by number of property listings by region
以下是一些拥有 3 个以上房源的小区的例子。例如,在尼日利亚阿布贾的一平方英尺租金为 4.53 美元,购买价格为 344 美元,而在大阿克拉地区的一平方英尺租金为 1.68 美元,购买价格为 165 美元。肯尼亚内罗毕——租金 2.47 美元,房价 203 美元。
评估盈利能力
为了衡量一个地区的利润,我们计算了租金单价与销售单价的比率。这意味着:单位租售比越高,该地区的利润越低。见下图:
Profitability Ratio Distribution
根据我们的盈利能力评估,下面的图表显示了所有选定国家中房地产投资盈利最多和最少的地区。
Top Profitable Regions by Ratio
Least Profitable Regions by Ratio
最后
这种分析可能并不完美,因为这些国家之间没有标准的汇率。因此,价格可能会有变化(尼日利亚本身至少有四种不同的美元汇率)。此外,数据源(web scraped from House Jumia)可能不包含所有列出的属性,因此有点偏。
然而,这并没有放弃应用于该分析的技术。
我想听听你对分析的反馈。请留言或发送推文@ elesinOlalekan
我们希望这能对你的房地产投资决策有所帮助。
感谢 余晚晚 让我重现她的分析
使用 KNIME 分析平台的引导式分析
Guided Analytics KNIME workflow
什么是引导式分析?
虽然“引导式分析”这个词会让你的思维进入一种复杂的状态,但它仅仅意味着自动化数据科学。引导式分析允许数据科学专业知识较少的人浏览流程,并看到隐藏在现有数据中的新见解。例如,学校的 IT 管理员可以跟踪这一过程,并预测未来的结果,如特定班级的学生在特定科目上的表现。
什么是 KNIME?
KNIME Analytics platform 是数据科学中最受欢迎的开源平台之一,用于自动化数据科学流程。KNIME 的节点库中有数千个节点,这允许您将节点拖放到 KNIME 工作台中。相关节点的集合创建了一个工作流,在将工作流部署到 KNIME 服务器之后,该工作流既可以在本地执行,也可以在 KNIME web 门户中执行。KNIME 通过帮助自动化过程,帮助在 KNIME 平台中创建作为工作流的引导分析过程。
数据科学生命周期
Data science lifecycle
上图描绘了从数据清理到模型评估的递归数据科学生命周期。在向 Cinglevue 的 Virtuoso 平台引入引导式分析时,我们基本上专注于为 Virtuoso 最终用户提供灵活性和用户友好性。我们开发的引导式分析流程的范围从功能选择到模型评估,假设用户已经清理了数据。最上面的图说明了 KNIME 引导的分析工作流,该工作流用于实现前面提到的从特征选择到模型评估的过程。当您在 KNIME server 中部署这个工作流时,您可以通过 web 门户访问它,并获得用户友好的向导。基本上,它允许用户将数据集上传为 csv 文件,如下所示。
File upload wizard
然后将这些特征以表格的形式列出,其中包含使用正向特征选择方法获得的精度值。基本上,前向特征选择所做的是,它一次迭代地向模型添加一个特征,并在添加特定特征后找到模型的准确度分数。用户可以浏览列表,并通过考虑准确性来选择他认为将在模型中工作的一些特征。
Feature Selection wizard
下一个屏幕要求用户选择目标变量,如下图所示。
Target variable selection wizard
第四个屏幕显示了用户可以用来训练数据集的分类算法列表。
ML Algorithm selection wizard
在下一个屏幕中,用户可以通过 ROC 曲线和条形图查看所选算法中哪个算法执行得最好,以便于理解。下图描述了这种情况。
Graphical model comparison
最后一步允许用户下载 PMML 格式的最佳模型,它允许在任何企业应用程序中使用。
架构
High level architecture
上图描述了所采用的引导式分析流程的高级架构。分析服务包装在 KNIME 服务器上,它允许列出并执行已部署的 KNIME 工作流。Virtuoso 平台直接与分析服务对话,并执行与特征选择和建模相关的工作流。预测服务在开放评分引擎之上实现。Virtuoso 平台直接与预测服务对话,并利用 openscoring 的模型部署和模型评估功能。
cing levu 是如何在 Virtuoso 平台中引入导向分析的?
为了将引导式分析引入 Virtuoso 平台,我们必须将最上面的图中描述的 KNIME 工作流分解为针对分类和回归类型问题的特征选择和模型训练。
数据源访问本身是在前端完成的。重要的是,任何 csv 格式的数据集都可以利用这一工作流程获得新的见解。上传数据集后不久,用户需要选择目标变量。选择目标变量后,其余提取的特征列在可用特征列表框中,用户可以选择他认为会影响结果的任何特征。所选择的特征通过分析服务被发送到 KNIME 服务器,以运行工作流并发送回所获得的评估度量(准确性)。使用前向特征选择方法通过迭代添加特征来获得准确度值。如下图所示,所获得的精度值显示在所选特征下。
我们使用准确度作为分类类型问题的前向特征选择标准,而使用 R 作为回归类型问题的前向特征选择标准。如前所述,主引导分析工作流已被分解为几个子工作流,特征选择是一个子工作流。我们在后台使用的工作流程如下图所示。
Feature Selection KNIME workflow
对于分类类型问题,我们在前向特征选择元节点中使用朴素贝叶斯算法来预测添加特征的精度值,如下图所示。
Forward feature selection using Naive Bayes algorithm
对于回归类型的问题,我们在正向特征选择元节点中使用线性回归算法来预测添加特征的 R 值,如下图所示。
Forward feature selection using Linear Regression algorithm
然后下一步是根据在机器学习方法选择下选择的挖掘函数来选择列出的机器学习算法。如果选择了分类方法,则会列出分类算法;如果选择了回归方法,则会在算法选择下列出回归算法。所选算法以及数据集和所选要素被发送到 KNIME 服务器,以 PMML 格式返回所选算法的精度或性能 R 值以及最佳模型。结果显示为条形图,如下图所示。
Algorithm Selection for a classification type problem
Algorithm Selection for a regression type problem
下图显示了用于分类和回归类型问题的模型训练的后端 KNIME 工作流。模型训练元节点内部使用的 ML 算法根据挖掘功能的性质(分类或回归)而变化。
在这个阶段,我们已经完成了特征选择和模型训练这两个步骤。然后是模型部署和模型评估。Virtuoso 平台利用 Openscoring 引擎的功能来实现这两个目的。
我们获得的最佳模型的 PMML 文件使用用户给定的模型 id 部署到 Openscoring 引擎中。部署的模型如下图所示。
Deployed ML model list
一旦用户点击特定模型的评估按钮,与该模型相关的输入字段将与有效边界一起列出,如下图所示。
Input fields for model features
下一步是使用这个接口评估模型的不可见数据,如下图所示。
下一步将是验证获得的结果,并将新数据输入到重新训练模型中,以获得更好的预测。将会带来另一篇有趣的博文,讲述如何通过继续数据科学生命周期来重新训练模型。
如果您需要任何澄清,请通过udeshika.sewwandi@cinglevue.com联系我。非常感谢您对这种自动化数据科学流程的方法的宝贵意见。
延伸阅读
[1]https://www.knime.com/blog/principles-of-guided-analytics
[2]https://www . knime . com/knime-software/knime-analytics-platform
[3]https://en.wikipedia.org/wiki/Data_science
[4]https://en.wikipedia.org/wiki/Machine_learning
[5]https://en.wikipedia.org/wiki/Automated_machine_learning
用 Python 分析黑客新闻书籍建议
一个黑客新闻线程的分析,使用 Python,黑客新闻 API 和 Goodreads API,以及权威的前 20 名书籍建议列表!
几天前,传统的“你今年读了什么书”主题出现在黑客新闻上。线程充满了非常好的书籍建议。我试图为明年做一份阅读清单,我认为收集数据并分析它会很有趣。在接下来的文章中,我将展示我如何使用 Hacker News 的 API 来收集文章内容,我如何选择最常见的标题并对照 Goodreads API 进行检查,最后我如何得出最值得推荐的 20 本书。和往常一样,处理文本数据一点也不简单。不过最后的结果还是挺满意的!
抓取线程:黑客新闻 API
第一步是获取数据。幸运的是,黑客新闻提供了一个非常好的 API 来免费抓取它的所有内容。API 有帖子、用户、热门帖子和其他一些的端点。对于本文,我们将使用一个帖子。用起来很简单,下面是基本语法:v0/item/{id}/.json
其中 id 是我们感兴趣的项目。在这种情况下,线程的 id 是 18661546.json()) ,因此这里有一个关于如何获取主页面数据的示例:
import requests
main _page = requests.request(‘GET’, ‘[https://hackernews.firebaseio.com/v0/item/18661546.json').json()](https://hacker-news.firebaseio.com/v0/item/18661546.json').json()))
相同的 API 调用也用于线程或帖子的子帖子,其 id 可以在父帖子的kids
键中找到。在孩子们身上循环,我们可以得到线程中每个帖子的文本。
清理数据
现在我们有了文本数据,我们想从中提取书名。一种可能的方法是寻找文章中所有的 Amazon 或 Goodreads 链接,并按此分组。这是一种干净的方法,因为它不依赖于任何文本处理。然而,只要快速浏览一下这个帖子,就可以清楚地看到,绝大多数建议都没有任何关联。所以我决定走一条更难的路:将 n gram 分组,并将这些 n gram 与可能的书籍匹配。
因此,在从文本中剔除特殊字符后,我将二元模型、三元模型、四元模型和五元模型组合在一起,并计算出现的次数。这是一个计算二元模型的例子:
import re
from collections import Counter
import operator# clean special characters
text_clean = [re.sub(r"[^a-zA-Z0-9]+", ' ', k) for t in text for k in t.split("\n")]# count occurrences of bigrams in different posts
countsb = Counter()
words = re.compile(r'\w+')
for t in text_clean:
w = words.findall(t.lower())
countsb.update(zip(w,w[1:]))# sort results
bigrams = sorted(
countsb.items(),
key=operator.itemgetter(1),
reverse=True
)
通常在文本应用中,处理数据时首先要做的事情之一是消除停用词,即一种语言中最常见的词,如冠词和介词。在我们的例子中,我们还没有从我们的文本中删除停用词,因此这些 ngrams 中的大多数几乎都是由停用词组成的。事实上,下面是我们数据中最常见的 10 个二元模型的输出示例:
[((u'of', u'the'), 147),
((u'in', u'the'), 76),
((u'it', u's'), 67),
((u'this', u'book'), 52),
((u'this', u'year'), 49),
((u'if', u'you'), 45),
((u'and', u'the'), 44),
((u'i', u've'), 44),
((u'to', u'the'), 40),
((u'i', u'read'), 37)]
在我们的数据中有停用词是好的,大多数标题书中都会有停用词,所以我们希望保留这些停用词。然而,为了避免查找太多的组合,我们排除了仅由停用词组成的 ngrams,保留了所有其他的。
检查书名:Goodreads API
现在我们有了一个可能的 n gram 列表,我们将使用 Goodreads API 来检查这些 n gram 是否对应于书名。如果有多个匹配项可供搜索,我决定将最近的出版物作为搜索结果。这是假设最近出版的书最有可能与这种环境相匹配。这当然是一个可能导致错误的假设。
Goodreads API 使用起来没有 Hacker News 那么简单,因为它以 XML 格式返回结果,比 JSON 格式更不友好。在这个分析中,我使用了xmltodict
python 包将 XML 转换成 JSON。我们需要的 API 方法是[search.books](https://www.goodreads.com/api/index#search.books)
,它允许通过标题、作者或 ISBN 来搜索书籍。以下是获取最近发布的搜索结果的书名和作者的代码示例:
import xmltodictres = requests.get("[https://www.goodreads.com/search/index.xml](https://www.goodreads.com/search/index.xml)" , params={"key": grkey, "q":'some book title'})xpars = xmltodict.parse(res.text)
json1 = json.dumps(xpars)
d = json.loads(json1)lst = d['GoodreadsResponse']['search']['results']['work']
ys = [int(lst[j]['original_publication_year']['#text']) for j in range(len(lst))]title = lst[np.argmax(ys)]['best_book']['title']
author = lst[np.argmax(ys)]['best_book']['author']['name']
这个方法允许我们将 ngrams 与可能的书籍关联起来。我们用 Goodreads API 对照全文数据检查匹配所有 ngrams 的图书列表。在进行实际检查之前,我们删除了书名,去掉了标点符号(尤其是半栏)和副标题。我们只考虑主标题,假设大部分时间只使用标题的这一部分(列表中的一些完整标题实际上很长!).根据线程中出现的次数对我们得到的结果进行排序,我们得到以下列表:
Books with more than 3 counts in the thread
所以《坏血》看起来是帖子里最受推荐的书。检查其他结果大部分似乎是有意义的,并与线程匹配,包括计数。我能在列表中发现的唯一一个大错误是在第二位,这本书被列为魔法师而不是列夫·格罗斯曼的《魔术师 T2》。后者在文中确实被引用了 7 次。这个错误是由于我们假设 Goodreads API 的结果中考虑了最新的书。至于原始数据上的结果,没有出现在列表中,除了三体,我找不到任何明显的结果。这本书或同一三部曲中的其他书在文中被多次引用,但因为它们被不同的名称或不同的标点符号引用,所以没有被这种方法提取出来。解决这个问题的一个方法是在这个步骤中使用模糊匹配。
结论
在本文的结论中,我展示了如何从黑客新闻中提取数据,解析数据以提取书名,使用 Goodreads API 检查书名,并将最终列表与原文进行匹配。这个任务被证明是相当复杂的,因为它需要几个假设和处理两个不同的 API。而且最终的结果还是有一些不正确的结果。
尽管如此,我设法得到了一个好的最终结果。以下是《黑客新闻》推荐的前 20 本书的名单:
- 《恶感:硅谷的秘密和谎言》约翰·卡瑞鲁
- 我们为什么睡觉:释放睡眠的力量,马修·沃克著
- 列夫·格罗斯曼的《魔术师》
- 鞋狗:耐克创始人菲尔·奈特回忆录
- 迈克尔·波伦《如何改变你的想法》
- 真实:汉斯·罗斯林的《我们看错世界的十个理由》
- 维克多·e·弗兰克尔《人类对意义的探索》
- 卡尔·纽波特的深入研究
- 《德乌斯人:明日简史》尤瓦尔·诺亚·哈拉里著
- D.M .凯恩的凤凰计划
- 尤瓦尔·诺亚·哈拉里的《21 世纪的 21 课》
- 系统中的思考:多内拉·h·梅多斯的初级读本
- 沃尔特·伊萨克森的达芬奇
- 克里斯·沃斯的《永不分裂》
- Jocko Willink 的极端所有权
- 吉姆·赫夫龙的线性代数
- 生活的 12 条规则:混乱的解毒剂
- 蒂姆·马歇尔的《地理囚犯》
- 纳西姆·尼古拉斯·塔勒布的《游戏中的皮肤》
- 詹姆斯·克利尔的原子习惯
源码可以在Github上查看。任何形式的评论或批评都将不胜感激。
人工智能基因组黑客马拉松上的黑客数据艺术
不久前,我注意到一个有趣的黑客马拉松即将到来。人工智能基因组学黑客马拉松聚焦于一种罕见的疾病()。是的,我曾向自己保证不再参加任何黑客马拉松,但这太难以抗拒了。它结合了我最喜欢的两件事:编程和科学。此外,找到一种罕见疾病的治疗方法的可能性非常有吸引力。所以我填好申请表,点击“发送”,然后等着听我是否被录用。
我在去度假前不久发现我被录取了。我乘喷气式飞机离开,完全忘记了黑客马拉松。几个星期以来,我漫步于伦敦、威尼斯和佛罗伦萨,欣赏艺术、建筑、旧地图和科学仪器。
在黑客马拉松的前几天,我精神焕发地回到家,准备继续编码和解决问题。但是说实话,我也有点时差。
黑客马拉松于周五晚上开始, Onno Faber 与 NF2 分享了他的个人故事。他谈到了为什么他把自己的基因组数据给了这个项目,以及他希望我们可以用它来帮助找到治疗方法。晚上剩下的时间充满了关于 NF2 周围的科学和我们在项目中可以使用的各种工具的有趣的谈话。
这不是黑客马拉松吗?我为什么做艺术?
最初,我计划在黑客马拉松上做一些生物信息学。或者对医疗日志进行一些自然语言处理。但是在活动的前几天,一些组织者看了我的作品集,Onno 问我是否可以用基因组数据创作一些艺术作品。
一个周末的黑客马拉松时间不多,尤其是对于创建数据艺术来说。但它有足够的时间来开始和发挥一些想法。艺术可能需要很长时间,但我喜欢在黑客马拉松上开始一个项目的想法……熟悉一些数据,更好地理解科学。
什么是 NF2?
NF2 基因与肿瘤抑制有关,当突变破坏蛋白质时,疾病就会发展,肿瘤就会沿着听神经生长。除了寻找治疗 NF2 的方法,研究 NF2 基因的一个动机是该基因可能以某种方式与其他癌症有关。
Python (Biopython + PIL)
Python 是这个项目的合理选择,因为我要处理基因组数据。我很快选择了图书馆,因为我知道我可以以后再换。毕竟,这只是一个周末项目。
我知道有些数据是 FASTA 格式的,所以我查找了可以处理 FASTA 文件的库。Biopython 看起来记录良好且易于使用。
对于视觉草图,我保持简单并使用 Python 图像库(PIL/枕头),它可以写入各种图像文件格式。我以前用过它,所以我可以很快上手,而不需要学习新的库。
我的黑客马拉松
当大多数参与者组成小组并提出项目计划时,我在独自工作。我对我想要创造的东西有一些想法,但保持它们相当流畅,因为这将是一次创造性的探索。这与我以前在黑客马拉松中经历的任何事情都非常不同。在黑客马拉松期间,我发了几个帖子,上面有正在进行的工作的截图。
入门指南
对我来说,任何涉及数据的项目的第一步都是获取数据,并将数据打印到控制台或终端。
这是我的第一张草图。这是人类骨 gla 蛋白的 DNA 序列,我用 Biopython 从 FASTA 文件中提取的。这一步的全部目的是确保我从文件中获得了正确的数据段。在这种情况下,是第一串碱基(ATGC)编码了基因。FASTA 文件可以包含几个序列。
First Step: output data to terminal.
我还想更好地了解 Biopython 库,它有打开 FASTA 文件和从中提取数据的便利功能。
注意:出于隐私考虑,当我从代码和数据中创建图像时,我使用公开可用的基因组数据作为我的“快照”。
Creating an image file from the data.
一旦我将数据打印到终端,我就开始创建图像并将它们保存到我的本地文件系统。我的目标是解析数据,并根据数据中的值生成图像。在这种情况下,它是一串核苷酸(DNA)。
这个迭代的代码仍然有很多问题,但是它确实生成了一个新的图像,并且数据被用来创建这个图像。
Mosaic of images
我已经编写了 python 脚本来创建一个使用随机起始颜色的图像,所以每次运行脚本时都会创建一个新的调色板。当我看到我的图像渲染时,我惊讶地发现它们多么像彩色马赛克中的瓷砖。
一些形式的 NF2 不是种系,而是镶嵌或“自发”突变:这一事实激发了这幅早期草图。黑客马拉松第一天晚上的演讲开始激励我。
Getting transparency working with individual layers with PIL
下一个比我预期的要长一点。对我来说,棘手的部分是弄清楚如何让 python 图像库用一些透明度来绘制每个“点”,这样我就可以得到这种混合的视觉效果。
之前,我已经将序列字符串中的每个字母处理并呈现为一个中间图像,然后将其写入最终图像。但是为了获得透明度,每个点都必须单独写入最终图像。这需要大量的图像渲染。
第一稿代码很慢,效率很低…我需要优化,只将点的面积写入图像,而不是整个图像的面积…但在返回优化之前,我一直在玩视觉效果。让自己继续前进并不容易。我不得不不断提醒自己这是一次黑客马拉松,时间很短。
Starting to hint at the Codons in the sequence.
在这次迭代中,我想让这些点重叠,就一点点。我已经解决了透明度问题,结果不那么抽象,密码子作为三联体更加明显。在遗传学中,一个基因中的密码子就像一个句子中的单词;每个都有特定的含义。它们有三个核苷酸长,编码特定的氨基酸。例如,AUG 是一个“起始密码子”。它位于起始处,编码 Met 氨基酸。
Sleek
只是为了好玩,我尝试了不同的背景颜色。我很高兴黑色的背景让它看起来如此时尚。语境决定一切。小的改变可以带来大的不同。
Digging deeper into Biopython: translating Codons to Amino Acids
所以,我选择 Biopython 库不仅仅是因为它能够打开和读取 FASTA 文件。它也使得将 DNA 序列的密码子翻译成氨基酸序列变得容易。在这个阶段,我已经知道如何用 Biopython 翻译密码子,并将 DNA 和氨基酸序列输出到终端。
虽然我知道自己想要什么,但此时我还没有一个清晰的“愿景”。为了加快渲染速度,我限制了序列的绘制量,因为我尝试了视觉创意。
Amino Acid for each Codon
给 DNA 序列中的每个密码子加上氨基酸,就产生了一个令人愉快的模式。但在这一点上,我想打破网格布局。
Pretty Bug
编写代码来创建图像的一个好处是,有时你犯了一个错误(bug ),结果看起来还不错……或者至少很有趣。这里有一个完美的例子。虽然这不是我想要的视觉效果,但我真的很喜欢它的样子。所以我留着它。当我有更多的时间玩它的时候,我可能会回来。
What’s next?
黑客马拉松已经结束了。但这并不意味着我会停止处理这些数据。我仅仅触及了我能用 Biopython 库做的事情的表面,所以我当然想探索更多。我还想更多地玩视觉游戏,探索不同的想法和模式。
- 想了解更多关于黑客马拉松的信息,请查看官方的活动网站。
- 要查看我在黑客马拉松期间的原创帖子,请查看 my Patreon 。
黑客 Scikit-Learn 的矢量器
自然语言处理是一个令人着迷的领域。由于所有的预测因子都是从文本中提取的,因此数据清理、预处理和特征工程对模型的性能有着更加重要的影响。
在我自己的一个涉及 NLP 的机器学习项目中工作了几个月之后,我已经了解了关于 Scikit-Learn 的矢量器的一两件事,我想与大家分享一下。希望在这篇文章结束时,你会有一些新的想法用在你的下一个项目中。
关于 Scikit-Learn 的矢量器
如你所知,尽管机器可能很先进,但它们不能像人类一样理解单词和句子。为了使文档的语料库更适合计算机,它们必须首先被转换成某种数字结构。有一些技术可以实现这一点,但在这篇文章中,我将重点关注向量空间模型,也称为 词汇袋 (BoW)模型。
****词汇袋是解决该问题的一种非常直观的方法,该方法包括:
- 按照某种模式将文档分割成令牌。
- 给每个标记分配一个与它在文档和/或语料库中出现的频率成比例的权重。
- 创建文档术语矩阵,每行代表一个文档,每列寻址一个标记。
Scikit-Learn 提供的矢量器对象开箱即用,非常可靠,它们允许我们一次有效地执行上述所有步骤,甚至可以应用预处理和关于标记数量和频率的规则。最重要的是,它们有三种不同的风格(还有其他版本,如 DictVectorizers,但它们并不常见):
- 计数矢量器:最简单的一个,它计算一个标记在文档中出现的次数,并使用这个值作为它的权重。
- 哈希矢量器:这个设计是为了尽可能的提高内存效率。矢量器不是将记号存储为字符串,而是应用散列技巧将它们编码为数字索引。这种方法的缺点是,一旦矢量化,就无法再检索要素的名称。
- TF-IDF 矢量器 : TF-IDF 代表“术语频率-逆文档频率”,这意味着分配给每个标记的权重不仅取决于它在文档中的频率,还取决于该术语在整个语料库中的出现频率。更多关于这里。
下面是一个计数矢量器的例子。
出局:
要更深入地了解每个步骤,请查看我写的这段代码。它实现了 Sklearn 的 CountVectorizer 的一个简化版本,该版本被分解成一些小函数,使其更具可解释性。
定制矢量器
矢量器本身非常强大,可以轻松地执行一些令人惊讶的复杂操作。也就是说,通过修改这些结构,可以将 Scikit-Learn 本身不支持的转换合并到单词包模型中。
创建自定义矢量器时主要使用两种方法:使用 Sklearn 默认分析器的修改版本实例化矢量器,以及创建从 CountVectorizer 继承的类。
修改默认分析器
在幕后,Sklearn 的矢量器调用一系列函数将一组文档转换成文档术语矩阵。其中,三种方法最为突出:
- build_preprocessor :返回一个 callable,用于在标记化之前对输入文本进行预处理。
- build_tokenizer :创建一个函数,能够将文档的语料库拆分成标记。
- build_analyzer :构建一个应用预处理、标记化、移除 停用词 并创建 n-grams 的分析器函数。
简而言之,这些方法负责创建默认的分析器**、预处理器和标记器,然后用于转换文档。通过用正确的参数实例化矢量器,我们可以轻松地绕过这些方法,创建一个定制的矢量器。让我们看一个如何实现的例子。**
让我们模拟一个场景,我们从 web 上删除了一些文本。使用默认设置:
出局:
现在,让我们通过创建一个矢量器来稍微清理一下文档集,该矢量器在预处理步骤中删除了 HTML 实体 ,并在文档被标记化时删除了 词汇😗***
出局:
看看“x00021”和“x0002e”是如何被人类可读的“!”替换的和“.”而“跳跃”被简化为引理?另外,如果你以前从未使用过 spaCy ,我建议你看一下它的文档。SpaCy 是一个高度优化的模块,能够同时执行多个复杂的 NLP 任务,它可以是一个救命稻草。
如果由于某种原因,您不能将转换分成预处理和标记化阶段(这种方式可能效率不高),那么您可以选择创建一个定制的分析器来同时执行这两个步骤。
自定义矢量器类
尽管用户定义的分析器可能会派上用场,但它们会阻止矢量器执行一些操作,如提取 n 元语法和删除停用词。引用 Scikit-Learn 的文档:
“默认分析器都调用预处理器和标记器,但是定制分析器将跳过这一步。N-gram 提取和停用词过滤发生在分析器级别,因此自定义分析器可能需要重复这些步骤。
例如,当使用预处理器+记号赋予器组合时:
矢量器创建单字、双字并删除停用词如“The”:
另一方面,当尝试使用自定义分析器执行相同的操作时:
输出不显示二元模型,包含停止字:
发生这种情况是因为当使用用户定义的分析器时,build_analyzer
方法不调用_word_ngrams
,T1 负责移除停止字并提取 n 元语法。
避免这种情况的一种方法是创建定制的矢量器类。这个概念非常简单,只需创建一个从基础矢量器继承的新类,并根据需要覆盖build_preprocessor
、build_tokenizer
和/或build_analyzer
方法。
所以,让我们再试一次,但是这次创建一个自定义矢量器类:
出局:
非常管用!尽管很耗时,但这种方法让分析器知道在矢量器实例化期间使用的任何参数,从而允许自定义函数与矢量器的其余部分无缝集成。
最后的想法
考虑到创建定制矢量器的不同方法,我的建议是总是首先尝试使用最简单的方法。这样你就减少了错误发生的几率,避免了许多不必要的麻烦。
此外,值得指出的是,这篇文章中讨论的结构本身非常通用,但只有在与 管道 中的其他变压器结合使用时,它们的真正潜力才会显现出来。你可以在这里了解更多关于这个的信息。
我希望这篇文章给你一套新工具,用在你的下一个项目上!
破解你的图像识别模型
Staircase to 99% Classification Accuracy
一旦您找到/创建了一个数据集,构建了一个卷积网络,并训练了模型,您就有了您的性能指标。很有可能,一开始你可能对你的模型的性能不太满意。以下是提高图像识别模型性能指标的一些最常用策略:
根据实施的难易程度/耗时程度,它们被分为两组。
黑客集群#1:易于实现
**一、添加更多数据:**改善你的图像识别模型的一个最简单的办法就是给它添加更多数据!如果你已经建立了一个很深的网络,或者你没有太多的训练实例,这是非常有用的。如果你有一个小的数据集,比如每个类中有 50 张图片,这可能就是你需要的。如果你对为什么添加更多数据会产生更好的模型感到好奇,我推荐这篇著名的文章。
**二。添加更多的层:**如果你有一个复杂的数据集,你应该利用深度神经网络的能力,在你的架构上添加更多的层。这些附加图层将允许您的网络学习更复杂的分类函数,从而提高分类性能。
Add more layers!
**三。增大/减小图像尺寸:**当您为训练和评估对图像进行预处理时,需要对图像尺寸进行大量实验。如果您选择的图像尺寸太小,您的模型将无法识别有助于图像识别的显著特征。如果您的图像太大,您可能没有足够的数据,或者您的模型可能不够复杂,无法处理它们。
一**五、更多的训练时间:**喝杯咖啡,用更多的纪元增量训练模型。以+25、+50、+100,…看看额外的训练是否提高了你的分类器性能。但是,您的模型将达到一个点,在这个点上,额外的训练时间不会提高准确性。在实验的同时,你也可以了解更多关于你的学习率参数。记得在每次实验迭代中加载并保存你的模型。
**五、颜色通道:**这里有很多选项可以选择 RGB、YUV、灰度,甚至灰度图像中的进一步处理像素值,所以它们只能取 X 数量的值,例如,[0,64,128,255]。颜色通道越复杂,数据集就越复杂,训练模型所需的时间就越长,但是,如果您有一个深度模型,这可能是一个很好的解决方案。
黑客集群#2:更难实现
六。迁移学习:避免让你的卷积神经网络学习边缘和线条等特征,相信聪明人已经用他们的 ResNet 做了很好的工作。将模型下载并加载到您的工作流中,然后更改完全连接的图层以适应您的问题。这篇文章声称迁移学习帮助他从 71%提高到 96%的准确率。
**七世。数据扩充:**尝试通过翻转图像、添加噪声或任何其他您认为不会破坏数据集的失真,将合成数据添加到您的数据集。如果你是一个强大的机器学习工程师,你也可以尝试用 GANs 进行数据增强。
**VIII。改变内核大小,激活函数:**这种技术带有一个“我希望你知道你在做什么”的警告,因为有一些数学涉及到确保你在每个卷积层正确地转换数据。然而,如果你是一名机器学习研究人员,这可能是一个很好的解决方案。
这些是提高分类器性能、训练更长时间、添加更多数据、添加合成数据、改变图像大小、改变颜色通道、转移学习、添加层以及改变内核大小和激活函数的一些解决方案。这些解决方案中的任何一个都可以帮助您获得额外的 5%的图像识别准确性,这一点至关重要。感谢阅读,请留下一些掌声!
CShorten
Connor Shorten 是佛罗里达大西洋大学计算机科学专业的学生。对软件经济学、深度学习和软件工程感兴趣。
今天的头发,明天就没了
自从我上次剪头发以来已经有相当长的时间了。它现在已经过了我的肩膀,在过去的几年里它一直留在那里。
尽管它还在生长,但它不再长了,因为它也脱落了。它已经达到了一个动态平衡,头发生长的速度等于头发脱落的速度。
为了更好地理解这个过程,我查阅了一些关于头发生长的事实。头发以每月大约 0.5 英寸的速度生长。人的头上大约有 100,000 根头发,其中每天大约有 100 根脱落。这相当于每月 3%的头发脱落。头发还有其他细微差别,但我简化了模型,以假设生长是恒定的,所有头发都有相同的脱落机会。
我在 R 中模拟了这种动态,并绘制了一个图表来观察头发长度如何随时间变化。它开始没有头发(想象一个寸头),收敛到大约 16 英寸的稳定状态长度。将 y 轴反转,使图本身看起来像悬挂的几缕头发。
假设 T 是一个随机变量,代表一缕头发脱落前的时间,或者一根头发的年龄。在统计学中,风险率是某个事件(如失败或死亡)发生的瞬时率,假设它尚未发生。
hazard rate
通过应用条件概率的定义,
而取极限为 dt → 0,危险率就是概率密度函数(pdf)与 1 的比值——累积密度函数(cdf)。危险率唯一地表征了一个分布,反之亦然。常数风险率是指数分布的独特性质。
这里的危险率是. 03/月,所以 T ~ Exp (率= . 03/月)。头发年龄呈指数分布,平均年龄为 1/.03 = 33 个月。由于长度随年龄线性增长,头发长度~ Exp (比率= . 06/英寸),平均长度为 16.7 英寸。这与模拟中收敛的平均毛发长度一致。Q-Q 图将模拟的最后一个时间步长的头发长度与随机生成的指数进行比较,并确认它们具有相同的分布。
汉密尔顿:联邦党人文集的文本分析
用潜在语义分析探索汉密尔顿、麦迪逊和杰伊在联邦党人文集里的写作风格
Photo by Ryan Quintal on Unsplash
在这一点上,你可能已经听说过这部戏剧,汉密尔顿,这是席卷百老汇电路和复兴国家利益的“十美元无父开国元勋”。
这是一部极具娱乐性的引人入胜的故事,并且具有历史真实性(带有一些创作自由)。联邦制当然有了性感的改变。
在其中一首歌曲《马不停蹄》中,艾伦·伯尔的角色让我们来回顾一下这段关于《联邦党人文集》写作的令人难忘的台词:
“约翰·杰伊在写完第五部后生病了。麦迪逊写了二十九个。汉密尔顿写了另外 51 个。”
This line really resonated with people. [Source: altarandwitchinghour]
这是令人振奋的一句话,它激励我看一看《联邦党人文集》,看看在这个国家结束其 240 周年纪念日之际,这些政治巨人给美国人留下了什么样的遗产。这些文件打破了有利于批准宪法的政治平衡,为我们今天所珍视的民主奠定了基础。我想回答这样一个问题:现代文本分析是否能对这些有影响力的作品提供任何高层次的见解。
使用 Python 的 NLP
注意:这一部分技术含量很高,所以如果你不喜欢 NLP,可以直接跳到结果部分。
首先,下载 85 份联邦党人文集的文本。幸运的是,古腾堡计划是一个免费的在线电子书库,保存着联邦党人文集(以及大量其他书籍!)向公众提供。我下载了 1.2 MB 的 txt 文件,并将其加载到 Jupyter 笔记本中。
立刻,我可以看到文本必须被清理。古登堡计划许可证和 HTML 格式弄乱了文本,每篇论文都必须从单个文件中解析出来。我清理了格式,构建了一个文本刮刀,按照作者将每篇论文分离出来,并加载到一个包含作者、标题和语料库的 Pandas 数据框架中。到目前为止一切顺利!
接下来,我必须弄清楚如何对单词进行定量建模以便可视化。我最终使用了 sci-kit learn 的 Tf-idf 矢量化(术语频率-逆文档频率),这是自然语言处理的标准技术之一。归结到基本术语,这种技术跟踪一个单词在单个文档中出现的频率,如果它在所有其他文档中也频繁出现,则惩罚该分数。这是衡量一个词的重要性和独特性的标准。
对每个单词都这样做,就可以为每篇联邦党人论文创建一个定量向量。在过滤掉常见的英语单词如“of”和“they”后,我们最终得到了 558,669 个独特的单词 n-grams。我们对 Tf-idf 分数高于某个阈值的短语进行优先排序,以便在论文中找到可能的关键词。
尽管如此,我们仍然处于困境。这给了我们一个(85 x 558669)向量——在我们当前的现实中是不可能用图表示的。我们正在尝试做的是一种叫做潜在语义分析(LSA)的东西,它试图通过对文本内容中的潜在模式进行建模来定义文档之间的关系。
信息时代是仁慈的,幸运的是,LSA 算法有开源实现,比如奇异值分解(SVD)。该算法降低了文本向量的维数,同时使我们能够保留数据中的模式。完美地满足了我们将数据降低到二维的可视化需求!
结果:奇怪的预示
The Federalist Papers, visualized. [Image By Author]
整洁的图表。这意味着什么?
每个点代表一份联邦党人文件,由作者用颜色编码。这两个轴代表转换后的数据—它们本身没有任何意义,但是它们作为相互比较的点是有价值的。你可以看到汉密尔顿和麦迪逊的论文倾向于占据图表上不同的空间——这表明他们在文章中优先考虑不同的语言。这可能是在论文中写不同主题的副产品。尽管如此,每个人选择写的主题仍然可以揭示意识形态。
鉴于詹姆斯·麦迪逊和亚历山大·汉密尔顿在 18 世纪 90 年代后期的分裂,《联邦党人文集》中的词汇差异获得了新的意义。约翰·杰伊广泛参与外交事务,是美国第一任首席大法官,他的著作主要论述了外国影响的危险以及联邦制度防范其他国家的必要性。
从高层次来看,这很棒,但是单词本身呢?我们可以对每篇联邦党人论文的前 10 个 Tf-idf 分数进行排序,看看哪些短语最有特色。下面我列出了两篇论文的输出示例。鉴于联邦党人论文 10(防范政治派别)和联邦党人论文 11(联邦主义对经济贸易的有益影响)的主题,关键短语似乎非常相关。
Paper 10 | madison
faction 0.0806265359389276
majority 0.05308879709884898
number citizens 0.05002710059542178
small republic 0.04879259622492929
parties 0.048153279676380355
other 0.045091031288949604
interests 0.04313049392358246
passion interest 0.04168925049618481
republic 0.041154310712061014
number 0.040803681072543126
Paper 11 | hamilton
trade 0.08024350997462926
markets 0.06991189047603769
navigation 0.0674719898424911
navy 0.0562800477401255
commerce 0.05284120721585442
ships 0.04681698985492806
commercial 0.03619823151914739
america 0.03323133785132385
maritime 0.03230134296600752
naval 0.03230134296600752
对每位作者最常用的 10 个单词进行统计后发现:
Most common words for James Madison:[('government', 12),
('states', 12),
('state', 9),
('people', 6),
('other', 4),
('representatives', 4),
('governments', 3),
('all', 3),
('knowledge', 3),
('powers', 3)]
即使在《联邦党人文集》中,詹姆斯·麦迪逊也表现出对州与联邦政府之间的关系、代表政党的作用以及人民意愿等主题的偏好。
相比之下:
Most common words for Alexander Hamilton:[('government', 8),
('president', 8),
('states', 8),
('power', 7),
('executive', 6),
('senate', 6),
('state', 6),
('national', 6),
('union', 5),
('court', 5)]
亚历山大·汉密尔顿通过关于联邦政府各部门的语言以及对强调各州联合的术语的偏好,表明了他强烈的联邦主义立场。虽然没有迹象表明汉密尔顿和麦迪逊在《联邦党人文集》中的意识形态分歧,但有趣的是,考虑到两者之间的词汇选择肯定反映了不同的优先事项。
Most common words for John Jay:[('government', 3),
('nations', 2),
('people', 2),
('congress', 1),
('navigation fleet let', 1),
('national government', 1),
('efficiency government', 1),
('senate', 1),
('navigation', 1),
('militia obeyed', 1)]
对于约翰·杰伊来说,他的作品更有限,因为他只写了五篇论文。他们都讨论了外国利益对美国的影响,以及如何需要一个强大的联盟来对抗其他国家。文本分析很好地反映了这些主题——讨论民兵、舰队和效率。
非常感谢 NLTK、sci-kit learn、numpy 和 pandas 的开发者。还要感谢托马斯·休斯教授的文本分析 tf-ifd 可视化教程(https://github.com/tmhughes81/dap)。
欢迎在 LinkedIn 、 Twitter 、 Github 或 Medium 上与我联系!
深度学习中不平衡数据集的处理
给原力带来平衡!
Bring balance to the force!
想获得灵感?快来加入我的 超级行情快讯 。😎
并非所有的数据都是完美的。事实上,如果你能得到一个完美平衡的真实世界数据集,你将会非常幸运。大多数情况下,您的数据会有某种程度的类不平衡,这是因为您的每个类都有不同数量的示例。
为什么我们希望我们的数据是平衡的?
在将时间投入到深度学习项目中任何潜在的漫长任务之前,理解为什么我们应该这样做是很重要的,这样我们才能确定这是一项有价值的投资。只有当我们真正关心少数类时,类平衡技术才是真正必要的。
例如,假设我们正试图根据当前的市场状况、房子的属性和我们的预算来预测我们是否应该买房子。在这种情况下,非常重要的是,如果我们购买,那么这是正确的决定,因为这是如此巨大的投资。同时,如果我们的模型在我们应该买的时候说不要买,这也没什么大不了的。如果我们错过了一所房子,总会有其他房子可以买,但在如此巨大的资产上进行错误的投资将是非常糟糕的。
在这个例子中,我们绝对需要我们的少数“购买”类非常准确,而对于我们的“不购买”类来说,这没什么大不了的。然而,在实际情况下,由于在我们的数据中,购买比不购买要少得多,我们的模型将偏向于很好地学习“不购买”类,因为它拥有最多的数据,可能在“购买”类上表现不佳。这就需要平衡我们的数据,这样我们就可以更加重视正确的“买入”预测!
如果我们真的不关心少数阶级呢?例如,假设我们正在进行影像分类,您的类别分布如下所示:
乍一看,平衡我们的数据似乎会有所帮助。但也许我们对那些少数民族阶层不太感兴趣。也许我们的主要目标是获得最高可能的百分比精度。在这种情况下,做任何平衡都是没有意义的,因为我们大部分的百分比准确度都来自有更多训练例子的职业。第二,即使数据集不平衡,当目标是最高百分比准确度时,分类交叉熵损失往往表现得相当好。总之,我们的少数阶级对实现我们的主要目标没有多大贡献,所以平衡是不必要的。
综上所述,当我们遇到需要平衡数据的情况时,有两种方法可以帮助我们。
(1)重量平衡
重量平衡通过改变每个训练示例在计算损失时携带的重量来平衡我们的数据。通常,我们的损失函数中的每个示例和类别将具有相同的权重,即 1.0。但是有时我们可能希望某些类或者某些训练例子更重要。再次参考我们购买房子的例子,因为“购买”类的准确性对我们来说是最重要的,所以该类中的训练例子应该对损失函数有显著的影响。
我们可以简单地通过将每个例子的损失乘以取决于它们的类别的某个因子来给这些类别加权。在喀拉斯,我们可以这样做:
我们创建了一个字典,基本上说我们的“购买”类应该为损失函数保留 75%的权重,因为“不购买”类更重要,我们相应地将它设置为 25%。当然,可以很容易地调整这些值,为您的应用找到最佳设置。如果我们的一个类比另一个类有更多的例子,我们也可以使用这种平衡方法。与其花费时间和资源试图为少数阶级收集更多,我们可以尝试使用重量平衡,使所有阶级平等地为我们的损失做出贡献。
我们可以使用的另一种平衡训练示例权重的方法是下面显示的。主要思想是:在我们的数据集中,我们自然会有一些比其他人更容易分类的训练示例。在训练期间,这些示例将以 99%的准确率进行分类,而其他更具挑战性的示例可能仍然表现不佳。问题是那些容易分类的训练例子仍然是造成损失的原因。当有其他更具挑战性的数据点,如果正确分类,可以对我们的整体准确性做出更大贡献时,为什么我们仍然给予它们同等的权重?!
这正是焦点损失可以解决的问题!焦点损失不是对所有训练样本赋予相等的权重,而是降低分类良好的样本的权重。这实际上是将更多的训练重点放在难以分类的数据上!在我们有数据不平衡的实际设置中,我们的多数类将很快变得分类良好,因为我们有更多的数据。因此,为了确保我们也在我们的少数类上实现高准确度,我们可以在训练期间使用焦点损失来给予那些少数类样本更多的相对权重。聚焦损耗可作为自定义损耗函数在 Keras 中轻松实现:
(2)过采样和欠采样
选择合适的类权重有时会很复杂。做一个简单的逆频率可能不总是工作得很好。焦点丢失可能会有所帮助,但即使这样也会使所有分类良好的样本的权重下降。因此,平衡我们数据的另一种方法是通过抽样直接进行。请看下面的插图。**
Under and and Over Sampling
在上图的左侧和右侧,我们的蓝色类比橙色类有更多的样本。在这种情况下,我们有 2 个预处理选项,可以帮助训练我们的机器学习模型。
欠采样意味着我们将只从多数类中选择一些数据,只使用和少数类一样多的例子。应该进行这种选择以保持类的概率分布。那很容易!我们只是通过减少样本来平衡数据集!
过采样意味着我们将创建少数类的副本,以便拥有与多数类相同数量的实例。复制时将保持少数民族的分布。我们只是在没有获得更多数据的情况下平衡了数据集!如果您发现很难有效地设置类权重,那么采样是类平衡的一个很好的替代方法。
喜欢学习?
在 twitter 上关注我,我会在这里发布所有最新最棒的人工智能、技术和科学!也在 LinkedIn 上与我联系!
绝对初学者使用 XArray 处理 NetCDF 文件
探索 Python 中与气候相关的数据操作工具
Photo by Alberto Restifo on Unsplash
N etCDF 是一种独立于机器的、面向数组的、多维的、自描述的、可移植的数据格式,被各种科学团体所使用。它的文件扩展名是*。nc* 或*。cdf* (虽然据信两者之间有细微差别)。不像里的文件。csv 或*。xlsx* ,NetCDF 格式无法使用电子表格软件直接访问和查看。
即使你可以,你也不会用一堆元数据来处理一个四维数据。
我将以美国 大气辐射测量气候研究机构 (ARM)和欧洲 欧洲中期天气预报中心 (ECMWF)的气候数据为例。
目录
原载于我的博客edenau . github . io。
先决条件
我们将使用 Python 中的[xarray](http://xarray.pydata.org/en/stable/)
库进行数据处理。长话短说,它建立在numpy
(和dask
)库的基础上,并利用了pandas
的能力,但是你可能不需要了解它。正如您可能知道的,包依赖是 Python 中的一个难点。这就是为什么安装所有东西最方便的方法是使用以下命令:
$ conda install xarray dask netCDF4 bottleneck
建议有经验的 Python 程序员查看相关的文档了解更多细节。如果你是初学者,不用担心。我列出了你需要检查的依赖项:
- *Python*2.7/3.5+必需
- numpy 1.12+ 必填
- 熊猫 0.19.2+ 必填
- 为插补特征
- 瓶颈 加速跳 NaN
- *****netCDF 4-python*用于基本的 netCDF 读/写等操作
- ****dask-array0.16+与 dask 进行并行计算
如果您想要可视化您的数据集,您可能需要这些:
- matplotlib1.5+用于绘图
- cartopy 为地图
- seaborn 更好的调色板
对于绝对的初学者,您可以通过以下方式检查您的默认 Python 版本
**$ python --version
Python 2.7.5**
您还可以通过以下方式检查 Python3 是否已安装
**$ python3 --version
Python 3.4.9**
要检查软件包的版本,使用pip freeze
或conda list
。如果你通过conda
安装xarray
的话,事情应该没问题。
可供选择的事物
[iris](https://scitools.org.uk/iris/docs/latest/)
是xarray
的替代品,但需要做一些工作才能让它在 Windows 上工作,在 Mac OS 上效果不好。Iris 也是一个英文单词,所以谷歌‘iris’会给你很多不相关的结果。使用iris
对我来说是一种痛苦。
数据预览
“预览”和“了解”您的数据、其元数据和数据结构总是一个好主意。假设您已经安装了netCDF4-python
,您只需要两个命令ncdump
和ncview
。前者给出了 netCDF 数据集的文本表示(基本上是元数据和数据本身),而后者是一个非常强大的图形界面,用于即时数据可视化。
ncdump
转到数据集的目录并尝试
**$ ncdump -h twparmbeatmC1.c1.20050101.000000.cdf**
由于此时我们不需要查看每个数据条目的值,-h
确保只显示标题(元数据)。你会得到
**netcdf twparmbeatmC1.c1.20050101.000000 {
dimensions:
time = UNLIMITED ; // (8760 currently)
range = 2 ;
p = 37 ;
z = 512 ;
variables:
double base_time ;
base_time:long_name = "Base time in Epoch" ;
base_time:units = "seconds since 1970-1-1 0:00:00 0:00" ;
base_time:string = "2005-01-01 00.00, GMT" ;
base_time:ancillary_variables = "time_offset" ;
float prec_sfc(time) ;
prec_sfc:long_name = "Precipitation Rate" ;
prec_sfc:standard_name = "lwe_precipitation_rate" ;
prec_sfc:units = "mm/hour" ;
prec_sfc:missing_value = -9999.f ;
prec_sfc:_FillValue = -9999.f ;
prec_sfc:source = "twpsmet60sC1.b1" ;
float T_p(time, p) ;
T_p:long_name = "Dry Bulb Temperature, from sounding in p coordinate" ;
T_p:standard_name = "air_temperature" ;
T_p:units = "K" ;
T_p:missing_value = -9999.f ;
T_p:_FillValue = -9999.f ;
T_p:source = "twpsondewnpnC1.b1:tdry" ;// global attributes:
***< OTHER METADATA >***
}**
您可以看到维度、变量和其他不言自明的元数据。全局属性(上面没有打印)告诉我们如何收集和预处理数据。在本例中,它们是 ARM 在巴布亚新几内亚马努斯 147.4E 2.1S 处获取的测量数据。
当我们查看变量列表:1-dim prec_sfc
和 2-dim T_p
时,我们意识到它们具有不同的维度(!).降水率在每次都是标量测量,而温度在每次都是柱形(这次是在不同气压水平而不是海拔高度水平测量)。在气候科学中,四维数据是很常见的——纬度、经度、海拔/气压水平、时间。
ncview
试试下面的命令,它会给你一个图形界面,列出数据集中的所有变量,非常简单。
**$ ncview twparmbeatmC1.c1.20050101.000000.cdf**
The graphical interface in Linux using ncview
术语
Data structures of xarray
数据阵列
xarray.DataArray
是一个带标签的多维数组的实现,用于单个变量**,如降雨量、温度等…它具有以下关键属性:**
values
:保存数组值的numpy.ndarray
dims
:各轴的尺寸名称(如('lat', 'lon', 'z', 'time')
)coords
:一个类似 dict 的数组(坐标)容器,用于标记每个点(例如,一维数字数组、日期时间对象或字符串)attrs
:保存任意元数据(属性)的OrderedDict
资料组
xarray.DataSet
是 DataArrays 的集合。每个 NetCDF 文件都包含一个数据集。
使用 XArray 编码
数据导入
在你阅读数据之前,你不能摆弄它。使用open_dataset
或open_mfdataset
读取单个或多个 NetCDF 文件,并将其存储在名为DS
的数据集中。
**import xarray as xr# single file
dataDIR = '../data/ARM/twparmbeatmC1.c1.20050101.000000.cdf'
DS = xr.open_dataset(dataDIR)# OR multiple files
mfdataDIR = '../data/ARM/twparmbeatmC1.c1.*.000000.cdf'
DS = xr.open_mfdataset(mfdataDIR)**
数据检查
还记得数据数组的 4 个关键属性吗?您可以使用DS.values
、DS.var
、DS.dims
、DS.coords
和DS.attrs
进行数据检查。这在交互式 Python 中将变得非常方便。它们的功能非常明显,留给读者作为练习。).
数据阵列提取
从数据集DS
中提取数据数组非常简单,因为DS.<var_name>
就足够了。您可以考虑用dropna()
删除 NaN 条目,并用sel()
选择数据。sel()
中的method
参数允许我们通过使用'pad'
、'backfill'
或'nearest'
方法来启用最近邻(不精确)查找。要指定范围,使用slice()
。
您可以通过da.values
将xr.DataArray
转换为numpy.ndarray
。
**# Extract Dry Bulb Temperature in z-coordinate (T_z)
# Select the altitude nearest to 500m above surface
# Drop NaN, convert to Celcius
da = DS.T_z.sel(z=500,method='nearest').dropna(dim='time') - 273.15 # or .ffill(dim='time')# Select data in 2000s
da = da.sel(time=slice('2000-01-01', '2009-12-31'))
da_numpy = da.values**
按照惯例,DataSet 用大写字母DS
命名,DataArray 用小写字母da
命名。
日期时间操作
假设 DataArray da
有一个 DateTime 格式的维度time
,我们可以通过da.time.dt.<year/month/day/...>
提取出年 / 月 / 日 / 日 / 日。注意,输出仍然在 DataArray 中。
以下示例进一步尝试计算每个月任何变量的平均值/总和。我们先用assign_coords()
定义一个新的坐标系。为什么?尝试观察年和月在日期时间中的表现。如果我们需要系统知道 2000 年 1 月和 2001 年 1 月之间的差异,我们需要年和月来定义一个新坐标,我们称之为year_month
。
然后,我们可以根据新定义的坐标系,通过groupby('year_month')
对数据进行分组,然后进行mean()
或sum()
操作。
**# Contract the DataArray by taking mean for each Year-Month
def mean_in_year_month(da):
# Index of Year-Month starts at Jan 1991
month_cnt_1991 = (da.time.dt.year.to_index() - 1991) * 12 + da.time.dt.month.to_index()
# Assign newly defined Year-Month to coordinates, then group by it, then take the mean
return da.assign_coords(year_month = month_cnt_1991).groupby('year_month').mean()da_1 = mean_in_year_month(da1)**
数据数组合并
我们可以使用xr.merge()
合并多个数据数组。如果试图合并两个同名但不同值的变量,将引发xr.MergeError
。这确保了xr.merge()
是非破坏性的。
**DS_new = xr.merge([da_1,da_2,da_3]).dropna(dim='year_month')**
测绘
您可以简单地将数据数组作为matplotlib.pyplot
方法的参数。对于绝对的初学者,尝试使用plt.plot()
或plt.scatter()
绘制线图或散点图。记住通过plt.show()
显示数字,或者通过plt.savefig()
保存数字。
如果你需要地图,cartopy
库可以轻松生成一张。
**import cartopy.crs as ccrs
import matplotlib.pyplot as pltda = DS.t_sfc# Draw coastlines of the Earth
ax = plt.axes(projection=ccrs.PlateCarree())
ax.coastlines()
da.plot()
plt.show()**
通过几行额外的代码,您可以生成如下内容:
Plots generated by cartopy
数据导出
如前所述,您可以将 DataArray 转换为numpy.ndarray
,或者将 DataSet 或 DataArray 转换为pandas.DataFrame
,如下图所示。
**df = DS.to_dataframe()**
还可以通过以下方式将 DataArray 或 DataSet 导出到 NetCDF 文件
**dataDIR = '../data/new.nc'
DS.to_netcdf(dataDIR)**
评论
如果您对 Python 或编程感兴趣,以下文章可能会有所帮助:
** [## 我希望我能早点知道的 5 个 Python 特性
超越 lambda、map 和 filter 的 Python 技巧
towardsdatascience.com](/5-python-features-i-wish-i-had-known-earlier-bc16e4a13bf4) [## 使用交互式地图和动画可视化伦敦的自行车移动性
探索 Python 中的数据可视化工具
towardsdatascience.com](/visualizing-bike-mobility-in-london-using-interactive-maps-for-absolute-beginners-3b9f55ccb59)
本教程是为教学目的而写的,只包括xarray
的基础知识。希望这有所帮助!
原载于我的博客edenau . github . io。
参考
- https://www . unidata . ucar . edu/software/netcdf/netcdf-4/new docs/netcdf/NC dump . html
- http://xarray.pydata.org/en/stable/
- https://scitools . org . uk/cartopy/docs/v 0.15/matplotlib/intro . html
- http://earthpy.org/cartopy_backgroung.html**
处理深度学习模型中的过拟合
当您在训练数据上实现了模型的良好拟合,而在新的、看不见的数据上没有很好地概括时,就会发生过度拟合。换句话说,模型学习了特定于训练数据的模式,而这些模式在其他数据中是不相关的。
我们可以通过查看验证指标(如损失或准确性)来识别过度拟合。通常,验证度量在一定数量的时期后停止提高,之后开始下降。训练指标不断改进,因为模型试图找到最适合训练数据的方法。
有几种方式可以减少深度学习模型中的过度拟合。最好的选择是 获取更多的训练数据 。不幸的是,在现实世界中,由于时间、预算或技术限制,您通常没有这种可能性。
另一种减少过拟合的方法是 降低模型记忆训练数据的容量 。因此,模型将需要关注训练数据中的相关模式,这将导致更好的泛化。在本帖中,我们将讨论实现这一目标的三个选项。
项目的建立
我们首先导入必要的包并配置一些参数。我们将使用 Keras 来拟合深度学习模型。训练数据是来自 Kaggle 的 Twitter 美国航空公司情绪数据集。
# Basic packages
import pandas as pd
import numpy as np
import re
import collections
import matplotlib.pyplot as plt
from pathlib import Path# Packages for data preparation
from sklearn.model_selection import train_test_split
from nltk.corpus import stopwords
from keras.preprocessing.text import Tokenizer
from keras.utils.np_utils import to_categorical
from sklearn.preprocessing import LabelEncoder# Packages for modeling
from keras import models
from keras import layers
from keras import regularizersNB_WORDS = 10000 # Parameter indicating the number of words we'll put in the dictionary
NB_START_EPOCHS = 20 # Number of epochs we usually start to train with
BATCH_SIZE = 512 # Size of the batches used in the mini-batch gradient descent
MAX_LEN = 20 # Maximum number of words in a sequenceroot = Path('../')
input_path = root / 'input/'
ouput_path = root / 'output/'
source_path = root / 'source/'
一些助手功能
在本文中,我们将使用一些助手函数。
def deep_model(model, X_train, y_train, X_valid, y_valid):
'''
Function to train a multi-class model. The number of epochs and
batch_size are set by the constants at the top of the
notebook.
Parameters:
model : model with the chosen architecture
X_train : training features
y_train : training target
X_valid : validation features
Y_valid : validation target
Output:
model training history
'''
model.compile(optimizer='rmsprop'
, loss='categorical_crossentropy'
, metrics=['accuracy'])
history = model.fit(X_train
, y_train
, epochs=NB_START_EPOCHS
, batch_size=BATCH_SIZE
, validation_data=(X_valid, y_valid)
, verbose=0)
return history def eval_metric(model, history, metric_name):
'''
Function to evaluate a trained model on a chosen metric.
Training and validation metric are plotted in a
line chart for each epoch.
Parameters:
history : model training history
metric_name : loss or accuracy
Output:
line chart with epochs of x-axis and metric on
y-axis
'''
metric = history.history[metric_name]
val_metric = history.history['val_' + metric_name] e = range(1, NB_START_EPOCHS + 1) plt.plot(e, metric, 'bo', label='Train ' + metric_name)
plt.plot(e, val_metric, 'b', label='Validation ' + metric_name)
plt.xlabel('Epoch number')
plt.ylabel(metric_name)
plt.title('Comparing training and validation ' + metric_name + ' for ' + model.name)
plt.legend()
plt.show()def test_model(model, X_train, y_train, X_test, y_test, epoch_stop):
'''
Function to test the model on new data after training it
on the full training data with the optimal number of epochs.
Parameters:
model : trained model
X_train : training features
y_train : training target
X_test : test features
y_test : test target
epochs : optimal number of epochs
Output:
test accuracy and test loss
'''
model.fit(X_train
, y_train
, epochs=epoch_stop
, batch_size=BATCH_SIZE
, verbose=0)
results = model.evaluate(X_test, y_test)
print()
print('Test accuracy: {0:.2f}%'.format(results[1]*100))
return results def remove_stopwords(input_text):
'''
Function to remove English stopwords from a Pandas Series.
Parameters:
input_text : text to clean
Output:
cleaned Pandas Series
'''
stopwords_list = stopwords.words('english')
# Some words which might indicate a certain sentiment are kept via a whitelist
whitelist = ["n't", "not", "no"]
words = input_text.split()
clean_words = [word for word in words if (word not in stopwords_list or word in whitelist) and len(word) > 1]
return " ".join(clean_words)
def remove_mentions(input_text):
'''
Function to remove mentions, preceded by @, in a Pandas Series
Parameters:
input_text : text to clean
Output:
cleaned Pandas Series
'''
return re.sub(r'@\w+', '', input_text) def compare_models_by_metric(model_1, model_2, model_hist_1, model_hist_2, metric):
'''
Function to compare a metric between two models
Parameters:
model_hist_1 : training history of model 1
model_hist_2 : training history of model 2
metrix : metric to compare, loss, acc, val_loss or val_acc
Output:
plot of metrics of both models
'''
metric_model_1 = model_hist_1.history[metric]
metric_model_2 = model_hist_2.history[metric] e = range(1, NB_START_EPOCHS + 1)
metrics_dict = {
'acc' : 'Training Accuracy',
'loss' : 'Training Loss',
'val_acc' : 'Validation accuracy',
'val_loss' : 'Validation loss'
}
metric_label = metrics_dict[metric] plt.plot(e, metric_model_1, 'bo', label=model_1.name)
plt.plot(e, metric_model_2, 'b', label=model_2.name)
plt.xlabel('Epoch number')
plt.ylabel(metric_label)
plt.title('Comparing ' + metric_label + ' between models')
plt.legend()
plt.show()
def optimal_epoch(model_hist):
'''
Function to return the epoch number where the validation loss is
at its minimum
Parameters:
model_hist : training history of model Output:
epoch number with minimum validation loss
'''
min_epoch = np.argmin(model_hist.history['val_loss']) + 1
print("Minimum validation loss reached in epoch {}".format(min_epoch))
return min_epoch
数据准备
数据清理
我们用 tweets 加载 CSV 并执行随机洗牌。在分割训练集和测试集之前,对数据进行洗牌是一个很好的做法。这样,情感类别在训练集和测试集中平均分布。我们只将 文本 列作为输入,将 航空公司 _ 情绪 列作为目标。
接下来我们要做的是 去除停用词 。停用词对于预测情绪没有任何价值。此外,由于我们希望建立一个同样适用于其他航空公司的模型,我们 删除了提到的 。
df = pd.read_csv(input_path / 'Tweets.csv')
df = df.reindex(np.random.permutation(df.index))
df = df[['text', 'airline_sentiment']]
df.text = df.text.apply(remove_stopwords).apply(remove_mentions)
列车测试分离
模型性能的评估需要在单独的测试集上完成。因此,我们可以估计模型的概括程度。这是通过 scikit-learn 的train _ test _ split方法完成的。
X_train, X_test, y_train, y_test = train_test_split(df.text, df.airline_sentiment, test_size=0.1, random_state=37)
将单词转换成数字
要使用文本作为模型的输入,我们首先需要将单词转换成记号,这仅仅意味着将单词转换成引用字典中索引的整数。这里我们将只保留训练集中最常用的单词。
我们通过应用 滤镜 将文字整理成 小写 。单词由空格分隔。
tk = Tokenizer(num_words=NB_WORDS,
filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{"}~\t\n',
lower=True,
char_level=False,
split=' ')
tk.fit_on_texts(X_train)
创建字典后,我们可以将 tweet 的文本转换为带有 NB_WORDS 值的向量。用 mode=binary 表示,它包含了一个指示词是否出现在推文中。这是通过记号赋予器的texts _ to _ matrix方法完成的。
X_train_oh = tk.texts_to_matrix(X_train, mode='binary')
X_test_oh = tk.texts_to_matrix(X_test, mode='binary')
将目标类转换为数字
我们还需要将目标类转换成数字,然后用 Keras 中的to _ categorial方法对数字进行一次性编码
le = LabelEncoder()
y_train_le = le.fit_transform(y_train)
y_test_le = le.transform(y_test)
y_train_oh = to_categorical(y_train_le)
y_test_oh = to_categorical(y_test_le)
分离验证集
现在我们的数据已经准备好了,我们分离出一个验证集。当我们调整模型的参数时,这个验证集将用于评估模型的性能。
X_train_rest, X_valid, y_train_rest, y_valid = train_test_split(X_train_oh, y_train_oh, test_size=0.1, random_state=37)
深度学习
创建一个过度拟合的模型
我们从一个过度拟合的模型开始。它有两层紧密相连的 64 个元素。第一层的 input_shape 等于我们保存在字典中的单词数,我们为其创建了一个热编码特征。
因为我们需要预测 3 个不同的情感类别,所以最后一层有 3 个元素。 softmax 激活功能确保三个概率总和为 1。
要训练的参数数量计算为 (nb 输入 x 隐藏层中的 nb 元素)+ nb 偏差项 。第一层的输入数量等于我们语料库中的单词数量。后续层将前一层的输出数量作为输入。因此每层的参数数量为:
- 第一层:(10000 x 64) + 64 = 640064
- 第二层:(64 x 64) + 64 = 4160
- 最后一层:(64 x 3) + 3 = 195
base_model = models.Sequential()
base_model.add(layers.Dense(64, activation='relu', input_shape=(NB_WORDS,)))
base_model.add(layers.Dense(64, activation='relu'))
base_model.add(layers.Dense(3, activation='softmax'))
base_model.name = 'Baseline model'
因为这个项目是多类单标签预测,所以我们用categorial _ cross entropy作为损失函数,用 softmax 作为最终激活函数。我们在训练数据上拟合模型,并在验证集上验证。我们运行预定数量的历元,并观察模型何时开始过度拟合。
base_history = deep_model(base_model, X_train_rest, y_train_rest, X_valid, y_valid)
base_min = optimal_epoch(base_history)eval_metric(base_model, base_history, 'loss')
开始时, 验证损失 下降。但是在时期 3,这停止并且验证损失开始快速增加。这是模型开始过度拟合的时候。
训练损耗 继续下降,在纪元 20 几乎归零。这是正常的,因为模型被训练为尽可能好地拟合训练数据。
处理过拟合
现在,我们可以试着做一些关于过度拟合的事情。有不同的方法可以做到这一点。
- 通过移除图层或减少隐藏图层中的元素数量来减少网络容量
- 应用 正则化 ,这归结于为大权重的损失函数增加了一个成本
- 使用 脱落层 ,将某些特征设置为零,随机删除
降低网络容量
我们的第一个模型有大量可训练的参数。这个数字越高,模型就越容易记住每个训练样本的目标类。显然,这对于新数据的推广并不理想。
通过降低网络容量,您迫使它学习重要的模式或将损失降至最低。另一方面,过多地减少网络的容量会导致 配不上 。该模型将无法学习列车数据中的相关模式。
我们通过移除一个隐藏层并将剩余层中的元素数量减少到 16 个来减少网络容量。
reduced_model = models.Sequential()
reduced_model.add(layers.Dense(16, activation='relu', input_shape=(NB_WORDS,)))
reduced_model.add(layers.Dense(3, activation='softmax'))
reduced_model.name = 'Reduced model'reduced_history = deep_model(reduced_model, X_train_rest, y_train_rest, X_valid, y_valid)
reduced_min = optimal_epoch(reduced_history)eval_metric(reduced_model, reduced_history, 'loss')
我们可以看到,在简化模型开始过度拟合之前,需要更多的时期。验证损失也比我们的第一个模型上升得慢。
compare_models_by_metric(base_model, reduced_model, base_history, reduced_history, 'val_loss')
当我们比较基线模型的验证损失时,很明显,简化模型在以后开始过度拟合。验证损失低于基线模型的时间要长得多。
应用正则化
为了解决过拟合问题,我们可以对模型应用权重正则化。对于较大的权重(或参数值),这将增加网络损耗函数的成本。因此,您会得到一个更简单的模型,它将被迫只学习列车数据中的相关模式。
有 L1 正则化和L2 正则化。
- L1 正则化将会增加一个关于 参数的绝对值 的费用。这将导致一些权重等于零。
- L2 正则化将增加参数 的 平方值的成本。这将导致较小的重量。
让我们试试 L2 正则化。
reg_model = models.Sequential()
reg_model.add(layers.Dense(64, kernel_regularizer=regularizers.l2(0.001), activation='relu', input_shape=(NB_WORDS,)))
reg_model.add(layers.Dense(64, kernel_regularizer=regularizers.l2(0.001), activation='relu'))
reg_model.add(layers.Dense(3, activation='softmax'))
reg_model.name = 'L2 Regularization model'reg_history = deep_model(reg_model, X_train_rest, y_train_rest, X_valid, y_valid)
reg_min = optimal_epoch(reg_history)
对于正则化模型,我们注意到它在与基线模型相同的时期开始过拟合。然而,随后的损失增加速度要慢得多。
eval_metric(reg_model, reg_history, 'loss')
compare_models_by_metric(base_model, reg_model, base_history, reg_history, 'val_loss')
添加脱落层
我们尝试的最后一个选项是添加脱落层。脱落图层会将图层的输出要素随机设置为零。
drop_model = models.Sequential()
drop_model.add(layers.Dense(64, activation='relu', input_shape=(NB_WORDS,)))
drop_model.add(layers.Dropout(0.5))
drop_model.add(layers.Dense(64, activation='relu'))
drop_model.add(layers.Dropout(0.5))
drop_model.add(layers.Dense(3, activation='softmax'))
drop_model.name = 'Dropout layers model'drop_history = deep_model(drop_model, X_train_rest, y_train_rest, X_valid, y_valid)
drop_min = optimal_epoch(drop_history)eval_metric(drop_model, drop_history, 'loss')
具有脱落层的模型比基线模型晚开始过度拟合。损耗的增加也比基线模型慢。
compare_models_by_metric(base_model, drop_model, base_history, drop_history, 'val_loss')
具有脱落层的模型稍后开始过度拟合。与基线模型相比,损耗也保持低得多。
全训练数据的训练和测试数据的评估
乍一看,简化的模型似乎是最好的泛化模型。但是让我们在测试集上检查一下。
base_results = test_model(base_model, X_train_oh, y_train_oh, X_test_oh, y_test_oh, base_min)
reduced_results = test_model(reduced_model, X_train_oh, y_train_oh, X_test_oh, y_test_oh, reduced_min)
reg_results = test_model(reg_model, X_train_oh, y_train_oh, X_test_oh, y_test_oh, reg_min)
drop_results = test_model(drop_model, X_train_oh, y_train_oh, X_test_oh, y_test_oh, drop_min)
结论
如上所示,所有三个选项都有助于减少过度拟合。我们设法大幅提高测试数据的准确性。在这三个选项中,具有脱落层的模型在测试数据上表现最好。
你可以在 GitHub 上找到笔记本。好好享受吧!欢迎任何反馈。
处理稀疏矩阵——压缩稀疏行(CSR)矩阵背后的概念
Source: https://www.loginworks.com/web-scraping-blogs/text-mining/
有两种常见的矩阵——密集矩阵和稀疏矩阵
稀疏矩阵的大部分元素为零,而密集矩阵的大部分元素不为零。对于大矩阵,通常大部分元素为零。因此,只使用非零值来执行运算是有意义的,因为零乘以任何值都将始终等于零。
Source: https://www.coin-or.org/Ipopt/documentation/node38.html
Scipy 提供了各种只存储非零元素的稀疏矩阵函数。通过这样做,可以最小化数据存储所需的存储器。机器学习过程通常需要数据帧在内存中。它分解数据帧以适合 RAM。通过压缩,数据可以很容易地放入 RAM。仅使用稀疏矩阵的非零值来执行操作可以大大提高算法的执行速度。
压缩稀疏行(CSR)算法是 Scipy 提供的类型之一。下面是它的工作原理。
样本文本文档
短句
这不是一个短句
没有下雨。是吗?
第一步——步进
给单词编号。如果单词重复,分配相同的数字。通过这一步,我们知道整个文档中有多少单词。
索引从 0 开始。第一个单词是“short ”,它被索引为“0 ”,同样,每个唯一单词都将被索引。单词“Short”出现两次,因此它每次在文档中出现时都获得相同的索引值“0”。
步骤 2——文档的矢量表示
对于文档中的每一行,创建向量表示。计算唯一索引的数量。在这种情况下,我们有从 0 到 7 的 8 个索引,因此每个文档(行)使用 8 个值来表示,每个值表示对应于该索引的特定单词出现的次数。
(索引,单词)——(0,短,(1,句子),(2,这个,(3,是),(4,不是),(5,a),(6,它),(7,下雨)
01234567
[11000000]
[11111100]
[00021021]
这里,第一个向量的第一个位置中的值 1 表示索引为“0”的单词在文档中出现 1 次。第三个向量的第四个位置的值 2 表示索引为“3”的单词在该文档中出现了两次。
步骤 3——为每个文档创建稀疏向量
下面是每个文档的稀疏矩阵表示。它删除所有零值,只存储非零值。
文档 1: <0 1, 1 1>
第一个文档只在前两个位置有值,所以只表示它们。(0 1)表示第 0 个位置的值为 1,而(1 1)表示第 1 个位置的值为 1。在<0 1, 1 1>的(0-1)中,0 对应于索引,而 1 对应于特定索引在文档中出现的次数(值)
文件 2: <0 1, 1 1, 2 1, 3 1, 4 1, 5 1>
文件 3: < 3 2, 4 1, 6 2, 7 1>
CSR 矩阵表示需要三个数组——索引、值和指针。
索引
Ind — [0,1,0,1,2,3,4,5,3,4,6,7]
从上述步骤中获得的每个文档的索引被合并为一个。索引的大小是文档中非零值的个数。
值
瓦尔-[1,1,1,1,1,1,1,1,2,1,2,1]
值数组包含与从每个文档中获得的每个索引相对应的值。索引的大小是文档中非零值的个数。
指针
现在,我们已经组合了文档中所有行的索引和值。指针有助于在索引和值数组中标识完整文本文档中每个文档(行)的开头和结尾。
ptr -[0,2,8,12]
0 表示文档 1(行 1)的开始,它在开始后两个位置结束。从第三个位置开始,文档 2(行 2)开始并在第八个位置结束。从第 9 个位置开始,文档 3(第 3 行)开始并在第 12 个位置结束。这适用于索引数组和值数组。指针有助于理解索引和值数组。给定一个索引和指针数组,就可以知道每个文档的开始和结束。
传递这些数组将给出 CSR 矩阵,这就是 scipy 中 csr_matrix 函数的工作方式。
代码
来自 scipy.sparse import *
来自 scipy import *
ind = [0,1,0,1,2,3,4,5,3,4,6,7]
val = [1,1,1,1,1,1,1,1,2,1,2,1]
ptr = [0,2,8,12]
csr_matrix( (val,ind,ptr))
输出:< 3x8 sparse matrix of type ‘
具有压缩稀疏行格式的 12 个存储元素>
处理文本文档时,CSR 矩阵就是这样创建的。
……未完待续
灵感来自我的数据挖掘课。
放手键盘战士:有毒评论的文本挖掘
从整齐的文本数据到 R 模型拟合的 NLP 初学者终极指南
通过互联网与人交流和接触是很神奇的。我们可以听到一些人的想法,如果没有这些想法,他们永远也不可能见面。可能是住在地球对面的人,或者是我们永远没有机会聊天的人。我们甚至可以和他们成为朋友,这就是为什么我们在这个年龄说“超级联系”。
然而,也有很多勇士威胁着这个令人敬畏的空间。键盘战士。他们没有基本的礼节,只是躲在他们的显示器后面,抛出他们的仇恨言论。这种评论不仅伤害了他人的感情,也破坏了网上交流的可能性。我们不能用数据科学来解决这个问题吗?我们能不能用机器学习建立一个有毒评论的检测器,让在线交流更健康?
今天我带来了另一个检测有毒评论的 NLP 项目。这是一项最初在卡格尔举行的比赛。我们面临的挑战是建立一个能够检测评论毒性的多头模型。有 6 种不同类型的毒性,即毒性、严重毒性、淫秽、威胁、侮辱和基于身份的仇恨。我来解释一下 NLP 的基本步骤。你可以从 Kaggle 下载数据集,完整代码可以在我的 Github 上找到。
加载数据集
我们先来看看数据集。我们有comment_text
栏和 6 种不同类型的毒性。
# Reading the dataset
train = read_csv('train.csv')
test = read_csv('test.csv')
这 6 个变量是我们的目标变量,有一个二进制值。拟合模型来一次预测 6 个变量将是这个项目的一个有趣的部分,你可以在这篇文章的结尾查看。在进入模型拟合之前,让我们先做一些挖掘comment_text
。
整理文本数据
为了处理文本变量,我将把它们标记成整齐的文本格式。但什么是记号化或记号化呢?
标记是一个有意义的文本单元,比如一个单词,我们有兴趣用于分析,而标记化是将文本拆分成标记的过程。
在大多数的自然语言处理工作中,我们基本上是在一个单词单元中处理文本数据。如果你需要一个 unigram 或 one-hot-encoding 的基本概念,请查看我之前的文章。
# Tokenize the comment_text column
tr_token = train %>%
unnest_tokens(output = word, input = comment_text) %>%
anti_join(stop_words, by = 'word')
函数unnest_tokens()
将文本变量分割成单个单词,并以每行一个标记的结构返回它们。这是处理文本数据的一种更简单的方式。记号不一定总是一个单词。它们可以是一个 N-gram,一个句子,甚至一个段落,这取决于你的目的。我个人很喜欢这个功能,因为它感觉就像我在开始烹饪之前切完所有的蔬菜和肉类一样。
因此,在对文本数据进行标记后,我们得到了如上所示的数据帧。我们来统计一下每个词的出现频率,并把它们形象化。
# Count frequency of each word
freq_n = tr_token %>%
group_by(word) %>%
count() %>%
arrange(-n)# words frequency bar plot
freq_n[1:15, ] %>%
ggplot(aes(x = reorder(word, n), y = n)) +
geom_col(fill = 'lightseagreen') +
geom_text(aes(x = word, y = 1, label = paste0('(', n, ')')),
hjust = 0, vjust = .3, fontface = 'bold', size = 4) +
coord_flip() +
labs(x = NULL, y = NULL, title = 'Top 15 most Frequent Words') +
theme_bw()
文章、页面、维基百科、talk、 和 来源 是评论中使用频率最高的。那大概是因为我们通常会留下评论新闻 文章 或观点 章节 和 谈论 关于我们的想法或附加 信息 来自其他 来源 。
术语频率-逆文档频率
的确,这些词是最常见的术语。但是对于我们的分析来说,很难说它们是“重要”的词。有时出现频率很高的词可能是不太重要的词,比如停用词。想想我们有 20 条评论,一个单词,比如“states”,出现在所有的评论中。或者‘医疗’这个词只出现在 3 条评论里怎么办?哪个更重要?这就是 TF-IDF 的由来。
统计数据 tf-idf 旨在衡量一个单词对文档集合(或语料库)中的文档有多重要,例如,对小说集合中的一部小说或网站集合中的一个网站。
词频 (tf)就和我们上面统计的一样。一个单词在文档中出现的频率(在这种情况下是一个注释)。逆文档频率 (idf)是包含该单词的文档数量的倒数。所以我们通过将这两个概念相乘来赋予这个词权重。你不必害怕数学,因为bind_tf_idf()
会为你计算所有这些。
那么数据集中有多少文档呢?因为有 6 种不同类型的毒性,并且我们要检查每种毒性的重要单词,所以我们有 41 种不同的毒性组合。这意味着有 41 个不同的文档。
tr_token_n = tr_token %>%
group_by(toxic, severe_toxic, obscene, threat, insult, identity_hate) %>%
count()head(tr_token_n)
# -> 41 different combinations of ‘toxic comment’ documents
tr_token_n$document = 1:41tr_tfidf = tr_token %>%
left_join(tr_token_n) %>%
bind_tf_idf(term = word, document = document, n = n) %>%
arrange(desc(tf_idf))
有了tf
、idf
和tf_idf
,我们现在可以得到真正重要的单词了。让我们在柱状图中标出重要的“有毒”单词。结果将与我们上面看到的大不相同。
# toxic important word
tr_tfidf %>%
filter(toxic == 1) %>%
top_n(20) %>%
ggplot(aes(x = reorder(word, tf_idf), y = tf_idf)) +
geom_col(fill = 'coral2') +
coord_flip() +
theme_bw() +
labs(x = NULL, y = 'TF_IDF', title = 'Top 20 Important \"Toxic\" Words')
让我们回顾一下到目前为止我们所讨论的内容。对于文本数据,我们用unnest_tokens()
对它们进行标记,使它们成为整齐的格式,并以可视化的方式统计单词的频率。
语料库和文档术语矩阵
为了建立机器学习,我们必须将数据转换成 dtm。什么是 dtm?它只是一个简单的矩阵。但是我们在前面附加了‘文档-术语’,以指定它在行中包含文档,在列中包含术语(所以文档-术语矩阵)。tdm 则相反。它的行中有术语,列中有文档。看你选择用 dtm 还是 tdm。
所以从现在开始,我们将把整个数据集转换成 dtm 并拟合一个模型。你注意到了吗,到目前为止我们一直在用训练集工作?测试集从未被接触过。因此,我将对整个数据集进行标记,但这一次是以不同的方式。还有一种方法可以处理文本数据,那就是通过tm
包把它们做成语料库。
# combining train and test dataset
full = rbind(train, test)
target = full[, 3:8]
part = 1:nrow(train)# transforming comment_text into corpus
corpus = full$comment_text %>%
str_trim() %>%
tolower() %>%
VectorSource() %>%
VCorpus()corpus = tm_map(corpus, removePunctuation)
corpus = tm_map(corpus, stripWhitespace)
corpus = tm_map(corpus, removeNumbers)
corpus = tm_map(corpus, removeWords, stopwords('en'))
corpus = tm_map(corpus, stemDocument)# casting into dtm and data frame
term_dtm = DocumentTermMatrix(corpus)
term_m = removeSparseTerms(term_dtm, sparse = 0.97) %>% as.matrix()
经过降低大写字母、删除标点、空格、数字和单词等几个步骤的处理,我得到了数据框。它看起来怎么样?
每行对应于id
,所有列变量都是单词或术语(记住文档-术语矩阵!).大部分都填了 0。它看起来像有很多洞的瑞士奶酪。由于这个漏洞,我们通常通过removeSparseTerms()
来降低稀疏度,以缩减数据。
拟合模型
终于到了建模的时候了!我将使用 glmnet 来拟合模型。我们有 6 个需要预测的目标变量。是不是意味着我们要一个一个的造 6 个模型?绝对不行。我们可以简单地使用 for 循环,迭代相同的算法 6 次,如下所示。
tr_x = term_m[part, ]
tr_y = target[part, ]te_x = term_m[-part, ]
te_y = target[-part, ]pred_glmnet = tibble()
for(x in names(target)){
y = tr_y[[x]]
model_glmnet = cv.glmnet(x = tr_x, y = factor(y),
family = 'binomial',
type.measure = 'auc',
nfolds = 5,
alpha = 0)
pred = predict(model_glmnet, newx = te_x,
s = 'lambda.min', type = 'response')
pred = ifelse(pred < .5, 0, 1)
pred_glmnet[, x] = pred
}
摘要
哇!我们今天经历了很多事情。让我们用一个更大的图表再回顾一遍。
我们首先遵循图片中间的步骤。然后我们在底部也尝试了不同的步骤。利用tm
软件包,我们制作了语料库,将其转换成 dtm 并拟合出模型。
我们最终让检测器能够对仇恨者的评论进行分类。厉害!对我来说,数据科学最大的乐趣就是帮助解决问题,让世界变得更美好。你也可以使用情感分析或主题建模,获得更复杂的模型。如果你想深入文本挖掘的世界,这里的是很好的教程。如果你不喜欢用课本学习,你也可以从 Datacamp 获得这个在线课程。
参考
- 用 R:https://www.tidytextmining.com/进行文本挖掘
- 维基百科:【https://en.wikipedia.org/wiki/Tf%E2%80%93idf
感谢您的阅读,希望这篇文章对您有所帮助。如果你想鼓励一个有抱负的数据科学家,请给一个或两个或三个掌声!我会很感激任何反馈,所以请在下面分享你的想法,或者在 LinkedIn 上联系我。我会回来讲另一个有趣的故事。在那之前,机器学习快乐:-)
Apache Beam 实践,用 Python 构建数据管道
Apache Beam 是一个开源 SDK,它允许您从基于批处理或流的集成中构建多个数据管道,并以直接或分布式的方式运行它。您可以在每个管道中添加各种转换。但是 Beam 的真正强大之处在于它不基于特定的计算引擎,因此是平台无关的。您声明您想要使用哪个“跑步者”来计算您的变换。默认情况下,它使用您的本地计算资源,但是您可以指定一个 Spark 引擎或云数据流…
在本文中,我将创建一个接收 csv 文件的管道,计算历史 S&P500 数据集的打开和关闭列的平均值。这里的目标不是给出一个关于 Beam 特性的详细教程,而是给你一个总体的概念,告诉你可以用它做什么,以及它是否值得你更深入地使用 Beam 构建定制管道。虽然我只写批处理,但是流管道是 Beam 的一个强大特性!
Beam 的 SDK 可以用于各种语言,Java,Python…但是在本文中我将重点介绍 Python。
装置
在本文发表时,Apache Beam (2.8.1)只与 Python 2.7 兼容,但是 Python 3 版本应该很快就会推出。如果你安装了 python-snappy,Beam 可能会崩溃。这个问题是已知的,将在 Beam 2.9 中修复。
pip install apache-beam
创建接收 CSV 的基本管道
数据
在本例中,我们将使用包含标准普尔 500 历史值的 csv。数据看起来是这样的:
Date,Open,High,Low,Close,Volume
03–01–00,1469.25,1478,1438.359985,1455.219971,931800000
04–01–00,1455.219971,1455.219971,1397.430054,1399.420044,1009000000
基本管道
要创建管道,我们需要实例化管道对象,最终传递一些选项,并声明管道的步骤/转换。
import apache_beam as beam
from apache_beam.options.pipeline_options import PipelineOptionsoptions = PipelineOptions()
p = beam.Pipeline(options=options)
根据梁文档:
使用管道选项来配置管道的不同方面,例如将执行管道的管道流道,以及所选流道所需的任何特定于流道的配置。您的管道选项可能包括诸如项目 ID 或文件存储位置等信息。
上面的 PipelineOptions()方法是一个命令行解析器,它将读取通过以下方式传递的任何标准选项:
--<option>=<value>
自定义选项
您还可以构建自己的自定义选项。在这个例子中,我为我的管道设置了一个输入和一个输出文件夹:
**class** **MyOptions**(PipelineOptions):@classmethod
**def** **_add_argparse_args**(cls, parser):
parser**.**add_argument('--input',
help**=**'Input for the pipeline',
default**=**'./data/')
parser**.**add_argument('--output',
help**=**'Output for the pipeline',
default**=**'./output/')
转变原则
在 Beam 中,数据被表示为一个p 集合 对象。因此,为了开始接收数据,我们需要从 csv 中读取并将其存储为一个p 集合 ,然后我们可以对其应用转换。读取操作被视为一种转换,并遵循所有转换的语法:
[Output PCollection] **=** [Input PCollection] **|** [Transform]
这些转换可以像这样链接起来:
[Final Output PCollection] **=** ([Initial Input PCollection] **|** [First Transform] **|** [Second Transform] **|** [Third Transform])
该管道相当于一个应用方法。
输入和输出 p 集合以及每个中间 p 集合都被视为单独的数据容器。这允许对同一 PCollection 应用多个转换,因为初始 PCollection 是不可变的。例如:
[Output PCollection 1] **=** [Input PCollection] **|** [Transform 1]
[Output PCollection 2] **=** [Input PCollection] **|** [Transform 2]
读取输入数据和写入输出数据
因此,让我们首先使用提供的一个阅读器来阅读我们的 csv,不要忘记跳过标题行:
csv_lines = (p | ReadFromText(input_filename, skip_header_lines=1) | ...
在管道的另一端,我们希望输出一个文本文件。所以让我们使用标准编写器:
... **|** beam**.**io**.**WriteToText(output_filename)
转换
现在我们想对用 Reader 函数创建的 PCollection 应用一些转换。变换分别应用于 PCollection 的每个元素。
根据您选择的工作者,您的转换可以是分布式的。然后在每个节点上执行转换的实例。
运行在每个 worker 上的用户代码生成输出元素,这些元素最终被添加到转换生成的最终输出
*PCollection*
中。
Beam 有核心方法(ParDo,Combine ),允许应用自定义转换,但也有预写的转换,称为复合转换。在我们的例子中,我们将使用 ParDo 变换来应用我们自己的函数。
我们已经将 csv 读入到一个p 集合 中,所以让我们将其拆分,以便我们可以访问日期并关闭项目:
… beam.ParDo(Split()) …
并定义我们的 split 函数,这样我们只保留日期,然后关闭并将其作为 dictionnary 返回:
class Split(beam.DoFn):
def process(self, element):
Date,Open,High,Low,Close,Volume = element.split(“,”)
return [{
‘Open’: float(Open),
‘Close’: float(Close),
}]
现在我们有了需要的数据,我们可以使用一个标准组合器来计算整个 p 集合的平均值。
要做的第一件事是将数据表示为一个元组,这样我们就可以按键分组,然后将组合值与它所期望的相结合。为此,我们使用一个自定义函数“CollectOpen()”,它返回一个包含(1,<open_value>)的元组列表。</open_value>
class CollectOpen(beam.DoFn):
def process(self, element):
# Returns a list of tuples containing Date and Open value
result = [(1, element[‘Open’])]
return result
元组的第一个参数是固定的,因为我们想要计算整个数据集的平均值,但是您可以使它动态地只在由该键定义的子集上执行下一个转换。
GroupByKey 函数允许创建所有元素的 PCollection,对于这些元素,键(即元组的左侧)是相同的。
mean_open = (
csv_lines | beam.ParDo(CollectOpen()) |
"Grouping keys Open" >> beam.GroupByKey() |
"Calculating mean for Open" >> beam.CombineValues(
beam.combiners.MeanCombineFn()
)
)
当你给一个转换分配一个标签时,确保它是唯一的,否则 Beam 将抛出一个错误。
如果我们想链接所有东西,我们的最终管道可能是这样的:
csv_lines = (
p | beam.io.ReadFromText(input_filename) |
beam.ParDo(Split()) |
beam.ParDo(CollectOpen()) |
"Grouping keys Open" >> beam.GroupByKey() |
"Calculating mean" >> beam.CombineValues(
beam.combiners.MeanCombineFn()
) | beam**.**io**.**WriteToText(output_filename)
)
但是我们也可以用一种允许在 splitted PCollection 上添加未来变换的方式来编写它(例如像 close 的平均值):
csv_lines = (
p | beam.io.ReadFromText(input_filename) |
beam.ParDo(Split())
)mean_open = (
csv_lines | beam.ParDo(CollectOpen()) |
"Grouping keys Open" >> beam.GroupByKey() |
"Calculating mean for Open" >> beam.CombineValues(
beam.combiners.MeanCombineFn()
)
)output = (
mean_open | beam**.**io**.**WriteToText(output_filename)
)
同一 PCollection 上的多个转换
如果我想在 csv_lines PCollection 上添加另一个转换操作,我将获得第二个“转换的 PCollection”。Beam 以“分支”变换的形式很好地代表了它:
要应用不同的变换,我们需要:
csv_lines = (
p | beam.io.ReadFromText(input_filename) |
beam.ParDo(Split())
)mean_open = (
csv_lines | beam.ParDo(CollectOpen()) |
"Grouping keys Open" >> beam.GroupByKey() |
"Calculating mean for Open" >> beam.CombineValues(
beam.combiners.MeanCombineFn()
)
)mean_close = (
csv_lines | beam.ParDo(CollectClose()) |
"Grouping keys Close" >> beam.GroupByKey() |
"Calculating mean for Close" >> beam.CombineValues(
beam.combiners.MeanCombineFn()
)
)
但是现在我们有两个 PCollections: mean_open 和 mean_close,作为转换的结果。我们需要合并/连接这些结果,以获得一个可以用 writer 写入文件的集合。Beam 的CoGroupByKey
就是这么做的。我们的输出将如下所示:
output= (
{
‘Mean Open’: mean_open,
‘Mean Close’: mean_close
} |
apache_beam.CoGroupByKey() |
WriteToText(output_filename))
)
我们现在已经定义了端到端的管道。您可以使用我们之前定义的自定义参数通过命令行运行它:
python test_beam.py **--**input ./data/sp500.csv **--**output ./output/result.txt
文件中的最终结果如下所示:
(1, {‘Mean Close’: [1482.764536822227], ‘Mean Open’: [1482.5682959997862]})
光束读取器和写入器
在这个例子中,我们只使用了 csv 阅读器和文本编写器,但是 Beam 有更多的连接器(不幸的是,大多数连接器都适用于 Java 平台,但是还有一些 Python 连接器正在开发中)。您可以在以下位置找到可用连接器的列表及其文档:
Apache Beam 是一个开源、统一的模型和一组特定于语言的 SDK,用于定义和执行数据…
beam.apache.org](https://beam.apache.org/documentation/io/built-in/)
如果你足够勇敢,你也可以找到一个指南来编写你自己的连接器:
Apache Beam 是一个开源、统一的模型和一组特定于语言的 SDK,用于定义和执行数据…
beam.apache.org](https://beam.apache.org/documentation/io/authoring-overview/)
创建数据管道时的一般逻辑
每当需要实现数据管道时,我们都希望清楚管道/转换的需求和最终目标。在 Beam 文档中,我找到了这个小摘录,我认为它是开始用 Beam 构建管道时应该如何推理的核心:
**你输入的数据储存在哪里?**你有几组输入数据?这将决定您需要在管道的开始应用哪种类型的
Read
转换。**您的数据是什么样的?**它可能是明文、格式化的日志文件或数据库表中的行。一些波束转换专门作用于键/值对的
PCollection
;您需要确定您的数据是否以及如何被键控,以及如何在您的管道的PCollection
中最好地表示它。**您希望如何处理您的数据?**Beam SDK 中的核心转换是通用的。了解您需要如何更改或操作您的数据将决定您如何构建核心转换,如 ParDo ,或者您何时使用 Beam SDKs 中包含的预先编写的转换。
**你的输出数据是什么样的,应该放在哪里?**这将决定你需要在你的管道末端应用什么类型的
Write
转换。
使用分布式转轮
如前所述,您可以使用 Spark 之类的分布式计算引擎,而不是使用本地计算能力(DirectRunner)。您可以通过为管道选项设置以下选项来实现这一点(在命令行参数或选项列表中):
--runner SparkRunner --sparkMaster spark://host:port
更多选项可用这里,但这两个是基本的。
结论
在编写定制转换时,Beam 是非常低级的,它提供了用户可能需要的灵活性。它速度很快,可以处理云/分布式环境。如果你看一个更高级的 API/SDK,一些像 tf.transform 这样的库实际上是建立在 Beam 之上的,在编码更少的情况下为你提供强大的功能。权衡在于您所寻求的灵活性。
这篇文章的代码可以在 GitHub 这里找到。
我希望你喜欢这篇文章。如果你看到了,欢迎鼓掌或关注我:)
Tensorflow 数据验证实践
谷歌刚刚发布了他们的端到端大数据平台新产品 TFDV!数据科学中的一大难题是处理数据质量问题,即数据验证。让我们看看 google 是如何发布第一个版本的,以及这个新库有多有用。
1.装置
通过 pip 进行相当标准的安装过程,但是要确保你已经预先安装了一些依赖项,以确保它编译没有问题。就我而言,在 Ubuntu 16.04 上,我缺少 python-dev 和 python-snappy 来获得更快的性能。如果你开始使用 TFX 图书馆,Bazel 也是不错的选择。
sudo apt-get install python-dev python-snappy
pip install tensorflow-data-validation
这个 TFDV 是一个相当新的库,文档非常漂亮…所以我强烈建议您克隆库,看看您使用的每个函数接受什么参数。
2.加载数据和生成统计数据:
这里没有火箭科学,我们首先导入库,数据集分析/验证的第一步是在数据集上生成统计数据。我将在这里加载一个 csv 并指定一个分隔符,但是您也可以加载一个 TFRecords。为方便起见,下面是两个函数定义头:
import tensorflow_data_validation as tfdv**def generate_statistics_from_csv(** data_location,
column_names = None,
delimiter = ‘,’,
output_path = None,
stats_options = stats_api.StatsOptions(),
pipeline_options = None,):...**def generate_statistics_from_tfrecord(** data_location,
output_path = None,
stats_options = stats_api.StatsOptions(),
pipeline_options = None,):
让我们加载并生成我们的统计数据:
BASE_DIR = os.getcwd()
DATA_DIR = os.path.join(BASE_DIR, 'data')
TRAIN_DATA = os.path.join(DATA_DIR, 'train.csv')train_stats = tfdv.generate_statistics_from_csv(TRAIN_DATA, delimiter=';')
首先要注意的是,该操作非常占用内存。我的数据集只有一个 86Mb 的 csv 文件,但是内存使用量增加了近 1Gb,所以请确保您有大量的可用内存!
3.可视化统计数据:
统计数据生成后,您有两个选项来实现它们。您可以使用Facets Overview,根据您的平台,安装起来可能会很棘手,或者您可以使用内置的 TFDV 可视化功能,它提供的信息与 Facets 完全相同:
tfdv.visualize_statistics(stats)
如果您有大量缺失数据、高标准偏差等,立即发现是非常有用的。然而,我真的很喜欢 Dive 模块的方面,它让你以一种非常直观的方式探索你的数据集是什么样子的。
到目前为止,没有什么革命性的…但是大事来了…
4.描述您的数据集以供将来验证
TFDV 的主要特征是“图式”的概念。这基本上是对您的数据的描述,因此您可以将此描述与新的数据进行匹配,并验证它们…或者不验证。
它描述了数据的标准特征,如列数据类型、数据的存在/缺失、预期的值范围。
您可以在被视为参考数据集的数据集上创建模式,并可以重用它来验证具有相同结构的其他数据集。
一个更有趣的特性是,您可以在 TFTransform 中重用这个模式来自动声明您的数据集结构。
这就是事情变得容易的地方…要做到这一点,只有一行代码就足够了:
schema = tfdv.infer_schema(train_stats)
然而,正如它看起来那样简单,Tensorflow 团队提出了一个警告:
通常,TFDV 使用保守的试探法从统计数据中推断稳定的数据属性,以避免模式过度适应特定的数据集。强烈建议检查推断出的模式,并根据需要对其进行提炼**,以获取 TFDV 的启发式算法可能遗漏的任何有关数据的领域知识。**
为了存储您的模式,TFDV 使用 protobuf 库,它正在成为操作静态数据(数据结构、转换方案、冻结模型……)的统一方法。
如果有一点我们可能都同意,那就是没有一个数据集是完美的。当涉及到验证您的数据时,领域知识是必不可少的,这就是为什么自动化工具是一种独角兽!为了考虑到这一点,TFDV 附带了帮助函数,所以您需要手动硬编码不应从数据集派生的规则。
每个特性都有一组你可以访问和修改的属性。例如,假设我们希望在至少 50%的示例中填充特性 f1,这是通过以下代码行实现的:
tfdv.get_feature(schema, 'f1').presence.min_fraction = 0.5
每个特征由以下部分组成:
特征名称,类型,存在性,化合价,域
然后每个组件得到它自己的子组件子集。我不会在这里列出它们,但我能给你的最好建议是解析模式 protobuf,你会发现类似这样的东西:
num_examples:1000000000000000
weighted_num_examples: 1000000000000000
features {
name: “one_in_a_quadrillion”
type:INT
num_stats: {
common_stats: {
num_missing: 1
num_non_missing: 1000000000000000
min_num_values: 1
max_num_values: 1
weighted_common_stats {
num_non_missing: 1000000000000000
num_missing: 1
}
}
min: 0.0
max: 10.0
}
您也可以像这样简单地在笔记本上显示它:
tfdv.display_schema(schema)
5.验证更新的数据
你的“完美的”训练集现在已经描述过了,但是你每天都在不断地获取新的数据到 ETL,并且寻找一种方法来验证新的训练集,这是 TFDV 的核心。使用前面描述的模式/域…它将解析您的新集合并报告异常值、缺失或错误的数据。
我认为教程中的图片很好地展示了一个好的管道数据验证链应该是什么样子:
当您试图为您的模型将数据摄取管道产品化时,这基本上是您试图实现的目标。
让我们获取一个新的 CSV 文件,加载它,生成统计数据,并使用之前生成的模式解析它以进行验证:
NEW_DATA = os.path.join(DATA_DIR, 'test.csv')new_csv_stats = tfdv.generate_statistics_from_csv(NEW_DATA, delimiter=';')
anomalies = tfdv.validate_statistics(statistics=new_csv_stats, schema=schema)
然后,您可以通过以下方式显示这些异常:
tfdv.display_anomalies(anomalies)
您将获得一个列表,其中包含以下一条或多条错误消息,这些消息描述了新数据集未满足的条件(已知模式中的预期条件):
Integer larger than 1
BYTES type when expected INT type
BYTES type when expected STRING type
FLOAT type when expected INT type
FLOAT type when expected STRING type
INT type when expected STRING type
Integer smaller than 1
STRING type when expected INT type
Expected a string, but not the string seen
BYTES type when expected STRING type
FLOAT type when expected STRING type
INT type when expected STRING type
Invalid UTF8 string observed
Unexpected string values
The number of values in a given example is too large
The fraction of examples containing a feature is too small
The number of examples containing a feature is too small
The number of values in a given example is too small
No examples contain the value
The feature is present as an empty list
The feature is repeated in an example, but was expected to be a singleton
There is a float value that is too high
The type is not FLOAT
There is a float value that is too low
The feature is supposed to be floats encoded as strings, but there is a string that is not a float
The feature is supposed to be floats encoded as strings, but it was some other type (INT, BYTES, FLOAT)
The type is completely unknown
There is an unexpectedly large integer
The type was supposed to be INT, but it was not.
The feature is supposed to be ints encoded as strings, but some string was not an int.
The type was supposed to be STRING, but it was not.
There is an unexpectedly small integer
The feature is supposed to be ints encoded as strings, but it was some other type (INT, BYTES, FLOAT)
Unknown type in stats proto
There are no stats for a column at all
There is a new column that is not in the schema.
Training serving skew issue
Expected STRING type, but it was FLOAT.
Expected STRING type, but it was INT.
Control data is missing (either training data or previous day).
Treatment data is missing (either scoring data or current day).
L infinity between treatment and control is high.
No examples in the span.
我不得不说我对这里的结果感到有点失望…我知道我们只是在谈论数据验证,但是指向一个错误而不返回包含这些错误的子集感觉有点像未完成的工作。我也知道这个功能是相当昂贵的,如果你从 Talend 或 Alooma 这样的包中获得年度许可,价格在 1 万到 8 万美元之间,你会得到一个很好的流来处理你的缺陷,但我相信 TF 迟早会走这条路!
在“异常长描述”一栏中,您还有一些更多的细节,因此您应该对您所拥有的内容的可解释性感到满意。
更好的是,如果您认为与模式的偏差是意料之中的,您可以很容易地修改原始模式:
tfdv.get_domain(schema, 'f2').value.append('new_unique_value')
6.适应不同的数据集配置:
您可能希望使用模式定义来验证您的集合,但是根据上下文(在教程示例中,我们采用训练和预测,即标签/无标签),您可能需要忽略一些条件。这可以通过以下方式实现:
# All features are by default in both TRAINING and SERVING environments.schema.default_environment.append('TRAINING')
schema.default_environment.append('SERVING')
# Specify that labels column is not in SERVING environment.
tfdv.get_feature(schema, 'labels').not_in_environment.append('SERVING')
serving_anomalies_with_env = tfdv.validate_statistics(
serving_stats, schema, environment='SERVING')
7.在可视化工具中比较不同的集合
一张图片说明了一切:
你可以在一个比较可视化中获得以上所有的好处。但是,您只能购买 2 套:
tfdv.visualize_statistics(lhs_statistics=train_stats, rhs_statistics=test_stats, lhs_name=’TRAIN’, rhs_name=’TEST’)
8.我们在 TF 转换中使用您的模式
如前所述,您生成的模式将避免您手动描述您的要素类型。它可以通过以下方式加载到 TFTransform 中:
feature_spec = schema_utils.schema_as_feature_spec(schema).feature_specschema = dataset_schema.from_feature_spec(feature_spec)
9.最后一个音符
我将跳过偏斜和漂移比较器,因为它们是前面解释的逻辑的扩展,您可能希望在 git 中更深入地了解如何在 TFDV 中使用这些特性。两者都很有用!
总之,TFDV 正是它所代表的,一个数据验证工具,不多不少,与 Tensorflow 生态系统完美集成,为 TFTransform 提供更多自动化,并完成谷歌试图为机器学习从业者提供的端到端框架。它仍然感觉缺陷处理缺失,但是考虑到已经提供给我们的不同的库来减轻我们的痛点,我相信这很快就会到来!Google 计划的下一个版本是数据摄取模块,然后是作业管理、编排、监控…如下图顶部所示。
您可以在此找到完整的官方 TFDV 教程:
**[## Tensorflow 数据验证入门| TFX | TensorFlow
简而言之,模式描述了对“正确”数据的期望,因此可以用来检测数据中的错误…
www.tensorflow.org](https://www.tensorflow.org/tfx/data_validation/get_started)**