Python 是一门非常适合解决数据和自动化实现重复性工作的编程语言,咱们在用数据训练机器学习模型之前,通常都须要对数据进行预处理,而 Python 就非常适合实现这项工作,比方须要从新调整几十万张图像的尺寸,用 Python 没问题!你简直总是能找到一款能够轻松实现数据处理工作的 Python 库。
然而,尽管 Python 易于学习,使用方便,但它并非运行速度最快的语言。默认状况下,Python 程序应用一个 CPU 以单个过程运行。不过如果你是在最近几年配置的电脑,通常都是四核处理器,也就是有 4 个 CPU。这就意味着在你苦苦期待 Python 脚本实现数据处理工作时,你的电脑其实有 75% 甚至更多的计算资源就在那闲着没事干!
明天我(作者 Adam Geitgey——译者注)就教大家怎么通过并行运行 Python 函数,充分利用你的电脑的全副解决能力。得益于 Python 的 concurrent.futures 模块,咱们只需 3 行代码,就能将一个一般数据处理脚本变为能并行处理数据的脚本,提速 4 倍。
初学者有什么不懂的能够私信我——我刚整顿了一套 2021 最新的 0 根底入门教程,自私分享,获取办法:关注小编 CSDN,发私信:【学习材料】即可获取,内附:开发工具和安装包,以及零碎学习路线图。
一般 Python 解决数据办法
比方说,咱们有一个全是图像数据的文件夹,想用 Python 为每张图像创立缩略图。所以想学的同学,有必要听一下这位老师的课、支付 python 福利奥,想学的同学能够到梦雅老师的围鑫(同音):前边一组是:mengy , 后边一组是:7762, 把以上两组字母依照先后顺序组合起来即可,她会安顿学习的。
上面是一个短暂的脚本,用 Python 的内置 glob 函数获取文件夹中所有 JPEG 图像的列表,而后用 Pillow 图像处理库为每张图像保留大小为 128 像素的缩略图:
这段脚本沿用了一个简略的模式,你会在数据处理脚本中常常见到这种办法:
首先取得你想解决的文件(或其它数据)的列表
写一个辅助函数,可能解决上述文件的单个数据
应用 for 循环调用辅助函数,解决每一个单个数据,一次一个。
咱们用一个蕴含 1000 张 JPEG 图像的文件夹测试一下这段脚本,看看运行完要花多长时间:
运行程序花了 8.9 秒,然而电脑的实在工作强度怎么呢?
咱们再运行一遍程序,看看程序运行时的流动监视器状况:
电脑有 75% 的解决资源处于闲置状态!这是什么状况?
这个问题的起因就是我的电脑有 4 个 CPU,但 Python 只应用了一个。所以程序只是卯足了劲用其中一个 CPU,另外 3 个却鸿鹄之志。因而我须要一种办法能将工作量分成 4 个我能并行处理的独自局部。侥幸的是,Python 中有个办法很容易能让咱们做到!
试试创立多过程
上面是一种能够让咱们并行处理数据的办法:
1. 将 JPEG 文件划分为 4 小块。2. 运行 Python 解释器的 4 个独自实例。3. 让每个 Python 实例解决这 4 块数据中的一块。4. 将这 4 局部的处理结果合并,取得后果的最终列表。
4 个 Python 拷贝程序在 4 个独自的 CPU 上运行,解决的工作量应该能比一个 CPU 大概高出 4 倍,对吧?
最妙的是,Python 曾经替咱们做完了最麻烦的那局部工作。咱们只需通知它想运行哪个函数以及应用多少实例就行了,剩下的工作它会实现。整个过程咱们只须要改变 3 行代码。
首先,咱们须要导入 concurrent.futures 库,这个库就内置在 Python 中:
import concurrent.futures
接着,咱们须要通知 Python 启动 4 个额定的 Python 实例。咱们通过让 Python 创立一个 Process Pool 来实现这一步:
with concurrent.futures.ProcessPoolExecutor() as executor:
默认状况下,它会为你电脑上的每个 CPU 创立一个 Python 过程,所以如果你有 4 个 CPU,就会启动 4 个 Python 过程。所以想学的同学,有必要听一下这位老师的课、支付 python 福利奥,想学的同学能够到梦雅老师的围鑫(同音):前边一组是:mengy , 后边一组是:7762, 把以上两组字母依照先后顺序组合起来即可,她会安顿学习的。
最初一步是让创立的 Process Pool 用这 4 个过程在数据列表上执行咱们的辅助函数。实现这一步,咱们要将已有的 for 循环:
该 executor.map() 函数调用时须要输出辅助函数和待处理的数据列表。这个函数能帮我实现所有麻烦的工作,包含将列表分为多个子列表、将子列表发送到每个子过程、运行子过程以及合并后果等。干得丑陋!
这也能为咱们返回每个函数调用的后果。Executor.map() 函数会依照和输出数据雷同的程序返回后果。所以我用了 Python 的 zip() 函数作为捷径,一步获取原始文件名和每一步中的匹配后果。
这里是通过这三步改变后的程序代码:
咱们来运行一下这段脚本,看看它是否以更快的速度实现数据处理:
脚本在 2.2 秒就解决完了数据!比原来的版本提速 4 倍!之所以能更快的解决数据,是因为咱们应用了 4 个 CPU 而不是 1 个。
然而如果你认真看看,会发现“用户”工夫简直为 9 秒。那为何程序处理工夫为 2.2 秒,但不知怎么搞得运行工夫还是 9 秒?这仿佛不太可能啊?
这是因为“用户”工夫是所有 CPU 工夫的总和,咱们最终实现工作的 CPU 工夫总和一样,都是 9 秒,但咱们应用 4 个 CPU 实现的,理论解决数据工夫只有 2.2 秒!
留神 :启用更多 Python 过程以及给子过程调配数据都会占用工夫,因而靠这个办法并不能保障总是能大幅提高速度。如果你要解决十分大的数据集,这里有篇设置将数据集切分成多少小块的文章,能够读读,会对你帮忙甚大.
这种办法总能帮我的数据处理脚本提速吗?
如果你有一列数据,并且每个数据都能独自解决时,应用咱们这里所说的 Process Pools 是一个提速的好办法。上面是一些适宜应用并行处理的例子:
从一系列独自的网页服务器日志里抓取统计数据。
从一堆 XML,CSV 和 JSON 文件中解析数据。
对大量图片数据做预处理,建设机器学习数据集。
但也要记住,Process Pools 并不是万能的。应用 Process Pool 须要在独立的 Python 解决过程之间来回传递数据。如果你要解决的数据不能在处理过程中被无效地传递,这种办法就行不通了。简而言之,你解决的数据必须是 Python 晓得怎么应答的类型。
同时,也无奈依照一个料想的程序解决数据。如果你须要前一步的处理结果来进行下一步,这种办法也行不通。
那 GIL 的问题呢?
你可能晓得 Python 有个叫全局解释器锁(Global Interpreter Lock)的货色,即 GIL。这意味着即便你的程序是多线程的,每个线程也只能执行一个 Python 指令。GIL 确保任何时候都只有一个 Python 线程执行。换句话说,多线程的 Python 代码并不能真正地并行运行,从而无奈充分利用多核 CPU。
然而 Process Pool 能解决这个问题!因为咱们是运行独自的 Python 实例,每个实例都有本人的 GIL。这样咱们取得是真正能并行处理的 Python 代码!
不要胆怯并行处理!
有了 concurrent.futures 库,Python 就能让你简简单单地批改一下脚本后,立即让你电脑上所有 CPU 投入到工作中。不要胆怯尝试这种办法,一旦你把握了,它就跟一个 for 循环一样简略,却能让你的数据处理脚本快到飞起。