作者:William Koehrsen
当计算非常缓慢时,需要考虑的最重要的问题是:"当前我们的瓶颈是什么?"一旦你找出了解决方案,接下来的逻辑步骤就是找出如何度过瓶颈期。
在这里,我们的瓶颈是没有充分利用我们的硬件资源。例如,我们的计算机有8个核心,然而只有一个核心在运行计算。如果我们编写的代码不能"照顾"到我们所现有的全部资源。那么简单地更换一个更大的机器——比如说扩充RAM或核心的容量——是无法解决问题的。因此,我认为最佳的解决方案是重写代码,尽可能有效地利用我们现有的硬件。
在本文中,我们将看到如何重构我们的自动化特征工程代码,以便在所有笔记本的核心上并行运行,从而减少我们的运行计算时间(预估可以减少八倍以上的时间)。我们将使用两个开源库,即用于自动特征工程(https://medium.com/@williamkoehrsen/why-automated-feature-engineering-will-change-the-way-you-do-machine-learning-5c15bf188b96)的Featuretools(https://www.featuretools.com/)和用于Dask里的并行处理工具(https://dask.pydata.org/),然后用它们解决实际问题。
文中的一些具体的解决方案仅适用于上述问题。但是其中一些通用方法你可以学习用于自己的数据集。
虽然在这里我们将坚持使用一台多核计算机,但在将来,我们将使用相同的方法在多台计算机上运行计算。
完整的代码实现可以在GitHub上的Jupyter笔记本(https://github.com/Featuretools/Automated-Manual-Comparison/blob/master/Loan%20Repayment/notebooks/Featuretools%20on%20Dask.ipynb)中找到。如果你还不熟悉Featuretools,请点击:https://towardsdatascience.com/automated-feature-engineering-in-python-99baf11cc219。在本文,我们的主要关注点是Dask和特征工程的使用,会跳过一些自动化特征工程的细节。
问题:数据太多,时间不够。
在这里,我们将应用自动特征工具解决住宅信贷违约风险的问题(即预测客户是否会按时偿还贷款),我们有大量的数据,这导致我们需要很长的特征计算时间(https://www.kaggle.com/c/home-credit-default-risk)。使用深度特征合成(https://www.featurelabs.com/blog/deep-feature-synthesis/),我们能够从7个数据表、共计5800万行客户信息中自动生成1820个特征,调用这个只有一个核心的函数需要花费25个小时,即使是在一个64 GB内存的EC2实例上也是如此!
谈到我们的EC2实例 ,通常我们的笔记本电脑 有8个核心,为了加快计算速度,我们不需要更多的内存,但我们需要更好的利用这些核心Featuretools确实允许并行处理,方法是在调用深度特征合成时设置n_jobs参数。但是,目前该函数必须将完整的EntitySet发送到机器上的所有核心中 。使用大型EntitySet,如果每个核心的内存耗尽,这可能会产生一些问题。我们的改良方案是使用并行化,从目前的结果看来Dask解决了我们的问题。
解决方案:制造很多小问题
方法是将一个大问题分解成多个较小的问题,然后使用 Dask一次运行多个小问题,每个问题位于不同的核心上。这里最重要的一点是,我们必须确保每个问题 都独立于其他任务,以便它们能够同时运行。因为我们正在为数据集中的每个客户创建特征,所以我们目前的主要任务是为客户创建特征矩阵。
我们的做法概述如下:
通过对数据进行划分,将一个大问题转化为许多小问题。
编写函数,从每个子数据集中生成一个特征矩阵。
使用dask在所有核心上并行运行步骤2。
最后,我们得到许多较小的特征矩阵,然后我们把它们连接到一个最终的特征矩阵中。同样的方法,将一个大问题分解为多个并行运行的小问题,可以缩放到任意大小的数据集,并分布到其他库中实现计算,例如使用PySPark(http://spark.apache.org/docs/2.2.0/api/python/pyspark.html)的SPark(https://spark.apache.org/)。
无论我们拥有什么资源,我们都希望尽可能有效地利用它们,我们的目的是采用同样的框架处理更大的数据集。
分区数据:划分和征服
我们的第一步是创建原始数据集的小分区,每个分区包含来自七个表的所有信息,用于处理客户信息。然后,我们可以使用每个分区独立计算一组客户信息的特征矩阵。
完成此操作的方法是:获取所有客户信息的列表,将其分解为104个子列表,然后迭代这些子列表,最后将数据细分为只包括子列表中的客户信息,并将结果数据保存到磁盘中。此过程的基本伪代码是:
104个分区是根据以下几个准则选择的:
我们希望至少有和核心一样多的分区,并且这个数目应该是核心数量的倍数。
每个分区必须小到只能容纳单个核心的内存。
分区越多,完成每个任务的时间变化就越小。
(这样做额外的优化点是可以减少内存使用。这使我们的整个数据集从4GB缩小到到大约2GB左右。我建议你阅读这篇文章:https://pandas.pydata.org/pandas-docs/stable/categorical.html 。因为只有这样你才能有效地使用它们。
将所有104个分区保存到磁盘需要大约30分钟,但这样的步骤,我们以后不需重复第二次。
来自分区的实体集
Featuretools中的实体集是一种有用的数据结构,因为它包含多个表及其之间的关系。若要创建一个EntitySet从分区中,我们需要编写一个函数,该函数将从磁盘读取分区,然后生成EntitySet以及它们之间的关系。
此步骤的伪代码是:
注意,这个时候函数返回了EntitySet,而不是像对分区的数据那样保存它。的确保存原始数据是解决此问题的一个更好的选择,但是我们可能更希望修改EntitySets (比如添加有趣的值或领域知识特征 ),而不更改原始数据。动态生成EntitySet,然后将其传递到下一阶段:计算特征矩阵。
一个实体集合的特征矩阵
函数feature_matrix_from_entityset完全按照我们当初命名的目的那样做:即接受先前创建的EntitySet(实体集),并使用深度特征合成技术生成一个包含数千个特征的特征矩阵。然后将特征矩阵保存到磁盘中。为了确保每个分区都有相同的特征,我们只生成一次特征定义,然后使用Featuretools函数calculate_feature_matrix。
下面是整个函数(我们传入一个带有EntitySet和分区号的字典,这样我们就可以保存具有唯一名称的函数矩阵):
chunk_size是这个调用中唯一棘手的部分:它用于将特征矩阵计算分解为更小的部分,但是由于我们已经对数据进行了分区,这不再是必要的,只要整个EntitySet能够容纳内存就好。同时我发现,将chunk_size设置为观察时的数量,可以有效的节省时间。
现在我们有了从磁盘上的数据分区到特征矩阵所需要的所有单独的部分。我们之前所做的步骤包含了大部分准备工作,现在让Dask并行运行变的非常简单。
Dask:释放你的机器
Dask是一个并行计算库,它允许我们同时进行多项计算,或者使用一台机器(本地)上的进程/线程,或者使用多台单独的计算机(集群)。对于一台机器,Dask允许我们使用线程或进程并行运行计算。
进程不共享内存并在单个核心上运行,更适合于不需要通信的计算密集型任务。线程共享内存,但在Python中,由于全局解释器锁(GIL)的原因,两个线程不能在同一个程序中同时操作,只有一些操作可以使用线程并行运行。(有关线程/进程的更多信息,请点击:https://medium.com/@bfortuner/python-multithreading-vs-multiprocessing-73072ce5600b)
由于计算特征矩阵属于计算密集型,并且与之对应的每个分区都可以独立完成,所以在本文我们希望使用进程。任务不需要共享内存,因为每个特征矩阵不依赖于其他特征矩阵。在计算机科学方面,通过对数据进行划分,我们解决了我们的问题。不过这是一场"尴尬的"并行(http://www.cs.iusb.edu/~danav/teach/b424/b424_23_embpar.html),因为我们的核心不需要交流。
如果我们使用进程 (按下面的代码 那样做),我们将有8个核心,每个核心分配2GB内存(总共16 GB,具体内存取决于你的笔记本电脑)。
为了检查一切是否顺利,我们可以导航到localhost:8787,其中Dask已经为我们设置了一个Bokeh仪表板。在核心选项卡上,我们看到8个内存为2GB的核心:
目前,所有8个核心都处于闲置状态,因为我们没有给它们分配任何事情。下一步是创建一个"Dask包",你可以理解为是Dask分配给核心的任务列表。我们使用db.from_sequence方法和分区路径列表来生成"Dask包"。
然后,我们将计算任务映射到Dask包上。映射意味着获取一个函数和一个输入列表,并将该函数应用于列表中的每个元素。因为我们首先需要从每个分区创建一个EntitySet(实体集),所以我们将相关的函数映射到"Dask包"上:
接下来,我们进行另一次映射,这一次是为了生成特征矩阵:
这段代码将获取第一个映射 的输出(即实体集 ) ,并将其传递给第二个映射。这些步骤实际上并不运行计算,而是列出Dask随后将分配给核心的任务列表。要运行任务并生成特征矩阵,我们调用:
DASK根据由映射构造的任务图(有向无环图)自动将任务分配给核心。当计算发生时,我们可以在Bokeh仪表板上查看任务图和状态(https://en.wikipedia.org/wiki/Directed_acyclic_graph)。
上图中,左边的表示entity_set_from_partition函数,右边的是feature_matrix_from_entityset函数。从这个图中,我们可以看到两个函数之间有依赖关系,但每个分区的特征矩阵计算之间没有依赖关系。
Bokeh仪表板上还有许多其他可视化方法,包括任务流(下图左)和操作概要(下图右):
从任务流中,我们可以看到,所有八个核心同时使用,总共有208项任务要完成。配置文件告诉我们,花费时间最长的操作是计算每个分区的特征矩阵。在我的MacBook上,构建和保存所有104个功能矩阵需要6200秒(1.75小时)。这是一个相当大的改进,这就是重写代码到尽可能高效地使用可用硬件带来的好处。
我们没有更高一级的计算机,而是重写代码,以便最有效地利用我们拥有的资源。然后,如果我们有更高一级的计算机,我们可以使用相同的代码,以进一步减少计算时间。
构造一个特征矩阵
一旦我们有了单独的特征矩阵,如果我们使用增量学习(https://blog.bigml.com/2013/03/12/machine-learning-from-streaming-data-two-problems-two-solutions-two-concerns-and-two-lessons/),我们就可以直接使用它们进行建模。另一种选择是创建一个特征矩阵,可以在纯Python使用pandas完成:
单一特征矩阵有350,000行和1,820列,形状与我第一次使用单个核心时的形状相同。
结论
我们不应该考虑如何获得更好的计算设备,而应该考虑如何尽可能有效地使用我们拥有的硬件。在本文中,我们介绍了如何使用Dask并行代码,它允许我们使用笔记本电脑完成计算,计算速度是单核计算速度的8倍。
我们设计的解决方案主要利用了几个关键概念:
将问题分解成更小的、独立的块。
编写函数时一次处理一个块。
将每个块委托给一个核心并行计算。
现在我们不仅可以利用Featuretools自动特征工程的的速度和建模性能,还可以使用Dask并行地执行计算,并获得更快的结果。此外,我们还可以使用相同的方法来扩展到更大的数据集,从而使我们能够很好地处理任何机器学习问题。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/31509949/viewspace-2212324/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/31509949/viewspace-2212324/
文章浏览阅读1.7k次。unit SMTP_Connections;//------------------------------------------//定义单元//---------------------------------------------interfaceuses Classes, StdCtrls;const WinSock = wsock32.dll; Internet = 2; _利用winsock和api实现邮件的发送
文章浏览阅读1k次。有关eigen库的一些基本使用方法 https://blog.csdn.net/r1254/article/details/47418871
文章浏览阅读7.6k次,点赞2次,收藏26次。今天是初五,民间有破五的习俗,简单来说就是说该工作啦。回顾研究生毕业的一年半,期间从事过桌面应用开发、CAN\串口开发、无人机目标检测开发、爬虫、数据工程、大数据系统运维等。研究生期间学的深度学习也好久没再接触,自己配的深度学习机器(菜鸟攒机之深度学习(上))(菜鸟攒机之深度学习(下))在家吃灰了好久,当时还是卡皇的GTX 1080也已经被1080 Ti、2080、2080 Ti取代,Tenso...
文章浏览阅读2.7k次,点赞3次,收藏11次。STM32内置晶振和外置晶振的精度差别STM32系列处理好像都有内置的RC振荡器,这个内置RC振荡器可以代替外置晶振,可以节省成本和PCB空间。我之前有几个设计都是使用的内置的晶振,觉得使用起来很方便,我也知道内置振荡器的精度不如外置的晶振,但是一直没有遇到对于振荡器的精度有十分严格的要求的应用。不过这次对付一个对时间精度要求比较高的应用,我发现了内置RC振荡器和外置晶振的差别还是很大的,而其我使用的还是最廉价的外置晶振,其精度就远高于内置振荡器。例如有个应用我需要精确延时9999秒钟,使用_stm32内部晶振和外部晶振的区别
文章浏览阅读191次。2019独角兽企业重金招聘Python工程师标准>>> ..._vc++6.0opengl代码
文章浏览阅读1.2k次。 Winsock编程常见问答第四章 Winsock高级问题原文:http://tangentsoft.net/wskfaq/advanced.html 译者:jovia 时间:2010年4月2日 4.1 Winsock支持原始套接字吗?是的,Winsock支持原始套接字,但存在一些限制。例如:SOCKET sd = socket(AF_INET, SOC
文章浏览阅读1k次。Qt 针对 Windows 平台提供了两种安装包:MinGW版本: 使用MinGW作为默认编辑器,附带了GCC,GDB 等工具,在qtCreator中可以使用GDB 作为默认的调试器。MSVC版(VS2013 VS2015 VS2017..)使用MSVC作为默认编译器,需要使用CDB调试器。在写代码的时候,或者调试程序的时候,需要追踪到qt源码中查看实现,那么怎么在qt编码的时候,方便跳转到对应的qt源码中呢,以及调试的时候,可以在源码中下断点进行调试?1. 我们需要在安装qt版本到时候,同_qt如何设置右击跳转到源码
文章浏览阅读1.3k次。---------------------- android培训、java培训、期待与您交流! ----------------------毕老师视频看了一个月了,发现前面的东西很容易忘记。Java知识点真的是很多很碎,一定要时常的对前面的知识点进行复习总结才行,今天就对String中易错易混淆的知识点进行个总结。(1)new String()和new String(“”)都是声明一个
文章浏览阅读1.5k次。一.用Elasticsearch对大数据进行关键词检索1.本人采用ElasticsearchTemplate模板引擎进行检索,分为三步:第一步 利用QueryBuileders建立querybuilder类,并添加匹配条件must、notmust 和should等,其中字符串匹配可以采用queryStringQuery(会对关键词和文档进行分词)、termQuery精确检索不会进行分词Boo..._elasticsearchtemplate count
文章浏览阅读3.8k次。定义和用法encodeURIComponent() 函数可把字符串作为 URI 组件进行编码。语法encodeURIComponent(URIstring)返回值URIstring 的副本,其中的某些字符将被十六进制的转义序列进行替换。说明该方法不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码: - _ . ! ~ * ’ ( ) 。其他字符..._encodeuricomponent() 函数解决url传递过程中加号变空格的问题
文章浏览阅读9.1k次。环境:上位机:Window 7 64bit + VMware + Ubuntu 16.04 64bit下位机:stm32f407igt6 + usb3300测试结果:单次传输数据为8KB时,传输速度为4MB/s单次传输数据为16KB时,传输速度为8MB/s单次传输数据为60KB时,传输速度为20MB/s
文章浏览阅读1.4w次,点赞22次,收藏140次。文章目录一、从DXF文件获取数据的教程1、布局2、遍历布局的DXF实体3、访问实体的DXF属性4、得到一个纸空间布局5、通过查询语言检索实体6、按groupby()函数检索实体二、创建简单DXF绘图教程三、图层教程1、创建一个层定义2、变化层状态3、检查可用层4、删除一层四、块教程1、什么是块?2、创建块3、块引用(插入)4、什么是属性?5、使用属性定义6、获取/设置现有块引用的属性7、计算包装块引用8、爆炸块引用9、检查块引用的实体五、LWPolyline教程六、文本教程1、标准文本样式2、新文本风格3、_ezdxf