找回密码
 会员注册
查看: 20|回复: 0

Python高效计算库Joblib的详细入门教程

[复制链接]

2

主题

0

回帖

7

积分

新手上路

积分
7
发表于 2024-9-10 05:53:34 | 显示全部楼层 |阅读模式
文章目录1.Joblib库是什么?2.核心功能介绍及演示2.1高效序列化和反序列化对象2.2快速磁盘缓存2.3并行计算1.Joblib库是什么?Joblib是一个用于在Python中进行高效计算的开源库,提供了一些用于内存映射和并行计算的工具,能大幅提高科学计算和数据分析的效率,特别适合于需要进行重复计算或大规模数据处理的任务。Joblib库的常用关键功能包括对象高效序列化、函数值临时缓存以及并行计算,能够优化数据处理流程。在Python中安装Joblib库也非常简单,通过pip执行以下命令即可:pipinstalljoblib1安装完成后,执行如下代码,如果能输出相应版本号,则说明已经成功安装。importjoblibprint(joblib.__version__)122.核心功能介绍及演示joblib库的主要功能体现在以下三方面:高效序列化和反序列化:除特殊Python对象外,Joblib库能将数值对象高效序列化保存到本地,常常用来保存、加载数据对象和模型对象;快速磁盘缓存:够提供高效的磁盘缓存和延迟加载,可以将函数的返回值缓存到磁盘上以避免重复计算;并行计算:能轻松将代码任务分配到多核处理器上。2.1高效序列化和反序列化对象类似于pickle库,Joblib库提供了dump和load函数,能够高效地将大型数据对象(例如大型数组、机器学习模型等)保存到本地文件或从本地文件加载回来。Joblib针对numpy数组进行了特定的优化,基于殊序列化格式,相比于通用的序列化更加高效。如下例子,对比了pickle库和Joblib库在保存和加载大规模数组上的效率。首先生成(10000,10000)(10000,10000)(10000,10000)的数组,两个库分别循环保存和加载数据555次,最终的平均处理时间如下(见注释部分):importnumpyasnpimportpickle,joblib,time#生成一个大型的numpy数组对象,例如10000x10000的数组large_array=np.random.rand(10000,10000)#循环5次n=5#平均处理时间2.54sforiinrange(n):withopen(f'pickle_data_{i}.pkl','wb')asf:pickle.dump(large_array,f)#平均处理时间0.72sforiinrange(n):withopen(f'pickle_data_{i}.pkl','rb')asf:load_large_array=pickle.load(f)#平均处理时间2.16sforiinrange(n):joblib.dump(large_array,f'joblib_data_{i}.joblib')#平均处理时间0.04sforiinrange(n):load2_large_array=joblib.load(f'joblib_data_{i}.joblib')1234567891011121314151617181920212223242526相比于pickle库加载.pkl文件,Joblib库加载.joblib文件的平均效率极高,保存文件的效率也有一定的优势,此外,Joblib库的接口更加易用,因此在处理含大量数据的任务时常常用来代替pickle库。这种保存再加载的方式,常用在将训练好的模型或计算的数据集保存后分发给其他用户,还常常用于大规模数据的深拷贝(相比于直接深拷贝,保存后加载的方式常常更快)等场景中。2.2快速磁盘缓存Joblib库的另一核心功能是能将函数的计算返回值快速缓存到磁盘上(记忆模式),当再次调用该函数时,如果函数的输入参数没有改变,则Joblib直接从缓存中加载结果而不是重新计算。如下例子,通过定义缓存目录,以及创建缓存器,添加指定装饰器后,当我们运行第一次函数时,会将函数计算结果缓存到磁盘,再次调用函数时,如果输入参数相同,则从磁盘调出相应的计算结果,避免重复计算。当然了,很自然的一个想法是,为什么不把函数的计算结果保存为哈希表,传入参数为键,计算结果为值,当然也是可行的,但这会极大占用内存,而Joblib是将原本应在内存上的计算结果缓存到磁盘,且缓存和调用的处理非常快。fromjoblibimportMemoryimporttimecachedir='./my_cache'#定义缓存目录memory=Memory(cachedir,verbose=0)@memory.cachedefexpensive_computation(a,b):print("Computingexpensive_computation...")sum_=0foriinrange(1000000):sum_+=a*b/10+a/breturnsum_#第一次调用,将计算并缓存结果result=expensive_computation(20,3)#0.0967s#第二次调用,将直接从缓存加载结果result=expensive_computation(20,3)#0.000997s123456789101112131415161718192021上述代码的装饰器可以理解为将函数expensive_computation作为参数传入memory.cache()方法当中,上述写法等价于memory.cache(expensive_computation())。显然,对于有大量重复计算的任务,该库能极大地提高处理效率。值得注意的是,上述定义的函数中,存在打印语句print(...),当首次执行函数时,会执行该打印语句,而函数是重复执行的,则会直接从缓存中继承曾经的计算结果,而不会经过中间具体的计算逻辑,也就不会打印相关语句。2.3并行计算Joblib的最核心的功能应该是提供了高级的(简单易用)并行化工具,能够使我们轻松地将计算任务分配到多个CPU核心上执行。如下所示,当我们有多个独立的任务需要执行,可以通过Joblib的Parallel和delayed功能并行处理这些任务,从而节省时间。fromjoblibimportParallel,delayedimportnumpyasnpdefprocess(i):data=np.random.rand(1000,1000)#普通的循环计算#5.798sforiinrange(1000):process(i)#Joblib的并行计算#3.237sParallel(n_jobs=4)(delayed(process)(i)foriinrange(1000))1234567891011121314上述式子在,n_jobs定义了线程数,若n_jobs=-1,则启用所有可用的CPU核心;delayed()中传入任务(函数)名,而后的(i)为任务分配传入参数,在Joblib的并行计算下,执行100010001000次任务process()的时间为3.237s3.237s3.237s,而循环依次执行的时间为5.798s5.798s5.798s。随着任务的计算复杂度增大、独立任务数增多,并行计算的优势会逐渐明显,但相对于我们开的并行任务数,这种优势有时并不那么显著。原因在于,默认情况下,joblib.Parallel是启动单独的Python工作进程,以便在分散的CPU上同时执行任务,但由于输入和输出数据需要在队列中序列化以便同工作进程进行通信,可能会导致大量开销。因此,小规模任务下,Joblib的并行计算效率可能较低。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-7 06:34 , Processed in 1.943678 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表