流畅的python 脚本之家,

  流畅的python 脚本之家,

  Python是一种非常适合处理数据和自动完成重复性工作的编程语言。在我们用数据训练机器学习模型之前,通常需要对数据进行预处理,Python非常适合这项工作。本文将向您展示如何使用Python代码使您的数据处理脚本比其他脚本快4倍。有需要可以参考一下。

  Python是一种非常适合处理数据和自动完成重复性工作的编程语言。在我们用数据训练机器学习模型之前,通常需要对数据进行预处理,而Python非常适合完成这项工作。例如,成千上万的图像需要调整大小。用Python没问题!你几乎总能找到可以轻松完成数据处理的Python库。

  不过,Python虽然易学易用,但并不是最快的语言。默认情况下,Python程序使用CPU作为单个进程运行。但是如果你的电脑是近几年配置的,一般是四核处理器,也就是4个CPU。这意味着,当您等待Python脚本完成数据处理时,您的计算机实际上有75%或更多的计算资源只是坐在那里无所事事!

  今天,我将教你如何通过并行运行Python函数来充分利用计算机的处理能力。得益于Python的concurrent.futures模块,我们可以把一个常见的数据处理脚本变成一个只需要3行代码就可以并行处理数据的脚本,速度提高了4倍。

  普通Python处理数据方法

  例如,我们有一个装满图像数据的文件夹,想要使用Python为每张图像创建缩略图。

  下面是一个简短的脚本,它使用Python内置的glob函数获取文件夹中所有JPEG图像的列表,然后使用Pillow图像处理库为每张图像保存一个大小为128像素的缩略图:

  导入全球

  导入操作系统

  从PIL进口图片

  def make_image_thumbnail(文件名):

  #缩略图将被命名为“original _ filename _ thumbnail . jpg”

  base_filename,file _ extension=OS . path . split ext(文件名)

  thumbnail _ filename=f“{ base _ filename } _ thumbnail { file _ extension }”

  #创建并保存缩略图

  image=Image.open(文件名)

  image.thumbnail(size=(128,128))

  image.save(thumbnail_filename,“JPEG”)

  返回缩略图_文件名

  #循环文件夹中的所有JPEG图像,为每个图像创建缩略图

  for image_file in glob.glob(*。jpg):

  缩略图文件=制作图像缩略图(图像文件)

  打印(f 将{图像文件}的缩略图保存为{缩略图文件} )

  该脚本遵循一种简单的模式,这种模式在数据处理脚本中很常见:

  首先,获取想要处理的文件(或其他数据)的列表。写一个辅助函数,可以处理以上文件的单个数据。使用for循环调用辅助函数并处理每个数据,一次一个。让我们用一个包含1000张JPEG图像的文件夹来测试这个脚本,看看它需要运行多长时间:

  $ time python3 thumbnails_1.py

  1430028941_4db9dedd10.jpg的缩略图保存为1430028941 _ 4 db 9 dedd 10 _ thumbnail . jpg

  [.大约1000多行输出.]

  真正的0m8.956s

  用户0m7.086s

  sys 0m0.743s

  运行程序用了8.9秒,但电脑的真实工作强度是多少?

  让我们再次运行程序,查看程序运行时的活动监视器:

  电脑75%的处理资源都是闲置的!这是什么情况?

  这个问题的原因是我的电脑有4个CPU,而Python只用了一个。所以程序只用一个CPU全力,其他三个什么都不用。

  因此,我需要一种方法来将工作负载分成四个独立的部分,以便我可以并行处理。好在Python里有一个方法可以很轻松的让我们做到!

  试试创建多进程

  >

  下面是一种可以让我们并行处理数据的方法:

  

  • 将JPEG文件划分为4小块。
  • 运行Python解释器的4个单独实例。
  • 让每个Python实例处理这4块数据中的一块。
  • 将这4部分的处理结果合并,获得结果的最终列表。

  4个Python拷贝程序在4个单独的CPU上运行,处理的工作量应该能比一个CPU大约高出4倍,对吧?

  最妙的是,Python已经替我们做完了最麻烦的那部分工作。我们只需告诉它想运行哪个函数以及使用多少实例就行了,剩下的工作它会完成。

  整个过程我们只需要改动3行代码。

  首先,我们需要导入concurrent.futures库,这个库就内置在Python中:

  importconcurrent.futures

  接着,我们需要告诉Python启动4个额外的Python实例。我们通过让Python创建一个Process Pool来完成这一步:

  withconcurrent.futures.ProcessPoolExecutor()asexecutor:

  默认情况下,它会为你电脑上的每个CPU创建一个Python进程,所以如果你有4个CPU,就会启动4个Python进程。

  最后一步是让创建的Process Pool用这4个进程在数据列表上执行我们的辅助函数。

  完成这一步,我们要将已有的for循环:

  

for image_file in glob.glob("*.jpg"):

  thumbnail_file = make_image_thumbnail(image_file)

  替换为新的调用executor.map():

  

 image_files = glob.glob("*.jpg")

   for image_file, thumbnail_file in zip(image_files,executor.map(make_image_thumbnail, image_files)):

  该executor.map()函数调用时需要输入辅助函数和待处理的数据列表。

  这个函数能帮我完成所有麻烦的工作,包括将列表分为多个子列表、将子列表发送到每个子进程、运行子进程以及合并结果等。干得漂亮!

  这也能为我们返回每个函数调用的结果。

  Executor.map()函数会按照和输入数据相同的顺序返回结果。所以我用了Python的zip()函数作为捷径,一步获取原始文件名和每一步中的匹配结果。

  这里是经过这三步改动后的程序代码:

  

import glob

  import os

  from PIL import Image

  import concurrent.futures

   def make_image_thumbnail(filename):

   # 缩略图会被命名为 "<original_filename>_thumbnail.jpg"

   base_filename, file_extension = os.path.splitext(filename)

   thumbnail_filename = f"{base_filename}_thumbnail{file_extension}"

   # 创建和保存缩略图

   image = Image.open(filename)

   image.thumbnail(size=(128, 128))

   image.save(thumbnail_filename, "JPEG")

   return thumbnail_filename

   # 创建Process Pool,默认为电脑的每个CPU创建一个

   with concurrent.futures.ProcessPoolExecutor() as executor:

   # 获取需要处理的文件列表

   image_files = glob.glob("*.jpg")

   # 处理文件列表,但通过Process Pool划分工作,使用全部CPU!

   for image_file, thumbnail_file in zip(image_files, executor.map(make_image_thumbnail, image_files)):

   print(f"A thumbnail for {image_file} was saved as {thumbnail_file}")

  我们来运行一下这段脚本,看看它是否以更快的速度完成数据处理:

  

 $ time python3 thumbnails_2.py

   A thumbnail for 1430028941_4db9dedd10.jpg was saved as 1430028941_4db9dedd10_thumbnail.jpg

   [... about 1000 more lines of output ...]

   real 0m2.274s

   user 0m8.959s

   sys 0m0.951s

  脚本在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投入到工作中。

  以上就是三行Python代码提高数据处理脚本速度的详细内容,更多关于Python数据处理的资料请关注盛行IT软件开发工作室其它相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: