Python Joblib庫使用學習總結

来源:https://www.cnblogs.com/shouke/archive/2023/06/10/17472017.html
-Advertisement-
Play Games

## 實踐環境 python 3.6.2 Joblib ## 簡介 Joblib是一組在Python中提供輕量級流水線的工具。特別是: 1. 函數的透明磁碟緩存和延遲重新計算(記憶模式) 2. 簡單易用的並行計算 Joblib已被優化得很快速,很健壯了,特別是在大數據上,並對numpy數組進行了特定 ...


實踐環境

python 3.6.2

Joblib

簡介

Joblib是一組在Python中提供輕量級流水線的工具。特別是:

  1. 函數的透明磁碟緩存和延遲重新計算(記憶模式)
  2. 簡單易用的並行計算

Joblib已被優化得很快速,很健壯了,特別是在大數據上,並對numpy數組進行了特定的優化。

主要功能

  1. 輸出值的透明快速磁碟緩存(Transparent and fast disk-caching of output value): Python函數的記憶體化或類似make的功能,適用於任意Python對象,包括非常大的numpy數組。通過將操作寫成一組具有定義良好的輸入和輸出的步驟:Python函數,將持久性和流執行邏輯與域邏輯或演算法代碼分離開來。Joblib可以將其計算保存到磁碟上,並僅在必要時重新運行:

    原文:

    Transparent and fast disk-caching of output value: a memoize or make-like functionality for Python functions that works well for arbitrary Python objects, including very large numpy arrays. Separate persistence and flow-execution logic from domain logic or algorithmic code by writing the operations as a set of steps with well-defined inputs and outputs: Python functions. Joblib can save their computation to disk and rerun it only if necessary:

    >>> from joblib import Memory
    >>> cachedir = 'your_cache_dir_goes_here'
    >>> mem = Memory(cachedir)
    >>> import numpy as np
    >>> a = np.vander(np.arange(3)).astype(float)
    >>> square = mem.cache(np.square)
    >>> b = square(a)                                   
    ______________________________________________________________________...
    [Memory] Calling square...
    square(array([[0., 0., 1.],
           [1., 1., 1.],
           [4., 2., 1.]]))
    _________________________________________________...square - ...s, 0.0min
    
    >>> c = square(a)
    # The above call did not trigger an evaluation
    
  2. 並行助手(parallel helper):輕鬆編寫可讀的並行代碼並快速調試

    >>> from joblib import Parallel, delayed
    >>> from math import sqrt
    >>> Parallel(n_jobs=1)(delayed(sqrt)(i**2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    >>> res = Parallel(n_jobs=1)(delayed(sqrt)(i**2) for i in range(10))
    >>> res
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
  3. 快速壓縮的持久化(Fast compressed Persistence):代替pickle在包含大數據的Python對象上高效工作(joblib.dump&joblib.load)。

parallel for loops

常見用法

Joblib提供了一個簡單的助手類,用於使用多進程為迴圈實現並行。核心思想是將要執行的代碼編寫為生成器表達式,並將其轉換為並行計算

>>> from math import sqrt
>>> [sqrt(i ** 2) for i in range(10)]
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]

使用以下代碼,可以分佈到2個CPU上:

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]

輸出可以是一個生成器,在可以獲取結果時立即返回結果,即使後續任務尚未完成。輸出的順序始終與輸入的順序相匹配:輸出的順序總是匹配輸入的順序:

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> parallel = Parallel(n_jobs=2, return_generator=True) # py3.7往後版本才支持return_generator參數
>>> output_generator = parallel(delayed(sqrt)(i ** 2) for i in range(10))
>>> print(type(output_generator))
<class 'generator'>
>>> print(next(output_generator))
0.0
>>> print(next(output_generator))
1.0
>>> print(list(output_generator))
[2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]

此生成器允許減少joblib.Parallel的記憶體占用調用

基於線程的並行VS基於進程的並行

預設情況下,joblib.Parallel使用'loky'後端模塊啟動單獨的Python工作進程,以便在分散的CPU上同時執行任務。對於一般的Python程式來說,這是一個合理的預設值,但由於輸入和輸出數據需要在隊列中序列化以便同工作進程進行通信,因此可能會導致大量開銷(請參閱序列化和進程)。

當你知道你調用的函數是基於一個已編譯的擴展,並且該擴展在大部分計算過程中釋放了Python全局解釋器鎖(GIL)時,使用線程而不是Python進程作為併發工作者會更有效。例如,在Cython函數的with nogil 塊中編寫CPU密集型代碼。

如果希望代碼有效地使用線程,只需傳遞preferre='threads'作為joblib.Parallel構造函數的參數即可。在這種情況下,joblib將自動使用"threading"後端,而不是預設的"loky"後端

>>> Parallel(n_jobs=2, prefer=threads')(
...     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]

也可以在上下文管理器的幫助下手動選擇特定的後端實現:

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
...    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
...
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]

後者在調用內部使用joblib.Parallel的庫時特別有用,不會將後端部分作為其公共API的一部分公開。

'loky'後端可能並不總是可獲取。

一些罕見的系統不支持多處理(例如Pyodide)。在這種情況下,loky後端不可用,使用線程作為預設後端。

除了內置的joblib後端之外,還可以使用幾個特定於集群的後端:

序列化與進程

要在多個python進程之間共用函數定義,必須依賴序列化協議。python中的標準協議是pickle ,但它在標準庫中的預設實現有幾個限制。例如,它不能序列化互動式定義的函數或在__main__模塊中定義的函數。

為了避免這種限制,loky後端現在依賴於cloudpickle以序列化python對象。cloudpicklepickle協議的另一種實現方式,允許序列化更多的對象,特別是互動式定義的函數。因此,對於大多數用途,loky後端應該可以完美的工作。

cloudpickle的主要缺點就是它可能比標準類庫中的pickle慢,特別是,對於大型python字典或列表來說,這一點至關重要,因為它們的序列化時間可能慢100倍。有兩種方法可以更改 joblib的序列化過程以緩和此問題:

  • 如果您在UNIX系統上,則可以切換回舊的multiprocessing後端。有了這個後端,可以使用很快速的pickle在工作進程中共用互動式定義的函數。該解決方案的主要問題是,使用fork啟動進程會破壞標準POSIX,並可能與numpyopenblas等第三方庫進行非正常交互。

  • 如果希望將loky後端與不同的序列化庫一起使用,則可以設置LOKY_PICKLER=mod_pickle環境變數,以使用mod_pickle作為loky的序列化庫。作為參數傳遞的模塊mod_pickle應按import mod_picke導入,並且應包含一個Pickler 對象,該對象將用於序列化為對象。可以設置LOKY_PICKLER=pickle以使用表中類庫中的pickling模塊。LOKY_PICKLER=pickle的主要缺點是不能序列化互動式定義的函數。為瞭解決該問題,可以將此解決方案與joblib.wrap_non_picklable_objects() 一起使用,joblib.wrap_non_picklable_objects()可用作裝飾器以為特定對下本地啟用cloudpickle。通過這種方式,可以為所有python對象使用速度快的picking,併在本地為互動式函數啟用慢速的pickling。查閱loky_wrapper獲取示例。

共用記憶體語義

joblib的預設後端將在獨立的Python進程中運行每個函數調用,因此它們不能更改主程式中定義的公共Python對象。

然而,如果並行函數確實需要依賴於線程的共用記憶體語義,則應顯示的使用require='sharemem',例如:

>>> shared_set = set()
>>> def collect(x):
...    shared_set.add(x)
...
>>> Parallel(n_jobs=2, require='sharedmem')(
...     delayed(collect)(i) for i in range(5))
[None, None, None, None, None]
>>> sorted(shared_set)
[0, 1, 2, 3, 4]

請記住,從性能的角度來看,依賴共用記憶體語義可能是次優的,因為對共用Python對象的併發訪問將受到鎖爭用的影響。

註意,不使用共用記憶體的情況下,任務進程之間的記憶體資源是相互獨立的,舉例說明如下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import time
import threading
from joblib import Parallel, delayed, parallel_backend
from collections import deque

GLOBAL_LIST = []

class TestClass():
    def __init__(self):
        self.job_queue = deque()

    def add_jobs(self):
        i = 0
        while i < 3:
            time.sleep(1)
            i += 1
            GLOBAL_LIST.append(i)
            self.job_queue.append(i)
            print('obj_id:', id(self),  'job_queue:', self.job_queue, 'global_list:', GLOBAL_LIST)


def get_job_queue_list(obj):
    i = 0
    while not obj.job_queue and i < 3:
        time.sleep(1)
        i += 1
        print('obj_id:', id(obj), 'job_queue:', obj.job_queue, 'global_list:', GLOBAL_LIST)
    return obj.job_queue


if __name__ == "__main__":
    obj = TestClass()

    def test_fun():
        with parallel_backend("multiprocessing", n_jobs=2):
            Parallel()(delayed(get_job_queue_list)(obj) for i in range(2))

    thread = threading.Thread(target=test_fun, name="parse_log")
    thread.start()

    time.sleep(1)
    obj.add_jobs()
    print('global_list_len:', len(GLOBAL_LIST))

控制台輸出:

obj_id: 1554577912664 job_queue: deque([]) global_list: []
obj_id: 1930069893920 job_queue: deque([]) global_list: []
obj_id: 2378500766968 job_queue: deque([1]) global_list: [1]
obj_id: 1554577912664 job_queue: deque([]) global_list: []
obj_id: 1930069893920 job_queue: deque([]) global_list: []
obj_id: 2378500766968 job_queue: deque([1, 2]) global_list: [1, 2]
obj_id: 1554577912664 job_queue: deque([]) global_list: []
obj_id: 1930069893920 job_queue: deque([]) global_list: []
obj_id: 2378500766968 job_queue: deque([1, 2, 3]) global_list: [1, 2, 3]
global_list_len: 3

通過輸出可知,通過joblib.Parallel開啟的進程,其占用記憶體和主線程占用的記憶體資源是相互獨立

復用worer池

一些演算法需要對並行函數進行多次連續調用,同時對中間結果進行處理。在一個迴圈中多次調用joblib.Parallel次優的,因為它會多次創建和銷毀一個workde(線程或進程)池,這可能會導致大量開銷。

在這種情況下,使用joblib.Parallel類的上下文管理器API更有效,以便對joblib.Parallel對象的多次調用可以復用同一worker池。

from joblib import Parallel, delayed
from math import sqrt

with Parallel(n_jobs=2) as parallel:
   accumulator = 0.
   n_iter = 0
   while accumulator < 1000:
       results = parallel(delayed(sqrt)(accumulator + i ** 2) for i in range(5))
       accumulator += sum(results)  # synchronization barrier
       n_iter += 1

print(accumulator, n_iter)  #輸出: 1136.5969161564717 14                          

請註意,現在基於進程的並行預設使用'loky'後端,該後端會自動嘗試自己維護和重用worker池,即使是在沒有上下文管理器的調用中也是如此

筆者實踐發現,即便採用這種實現方式,其運行效率也是非常低下的,應該儘量避免這種設計(實踐環境 Python3.6)

...略

Parallel參考文檔

class joblib.Parallel(n_jobs=default(None), backend=None, return_generator=False, verbose=default(0), timeout=None, pre_dispatch='2 * n_jobs', batch_size='auto', temp_folder=default(None), max_nbytes=default('1M'), mmap_mode=default('r'), prefer=default(None), require=default(None))

常用參數說明

  • n_jobs:int, 預設:None

    併發運行作業的最大數量,例如當backend='multiprocessing'時Python工作進程的數量,或者當backend='threading'時線程池的大小。如果設置為 -1,則使用所有CPU。如果設置為1,則根本不使用並行計算代碼,並且行為相當於一個簡單的python for迴圈。此模式與timeout不相容。如果n_jobs小於-1,則使用(n_cpus+1+n_jobs)。因此,如果n_jobs=-2,將使用除一個CPU之外的所有CPU。如果為None,則預設n_jobs=1,除非在parallel_backend()上下文管理器下執行調用,此時會為n_jobs設置另一個值。

  • backend: str, ParallelBackendBase實例或者None, 預設: 'loky'

    指定並行化後端實現。支持的後端有:

    • loky 在與工作Python進程交換輸入和輸出數據時,預設使用的loky可能會導致一些通信和記憶體開銷。在一些罕見的系統(如Pyiode)上,loky後端可能不可用。

    • multiprocessing 以前基於進程的後端,基於multiprocessing.Pool。不如loky健壯。

    • threading 是一個開銷很低的後端,但如果被調用的函數大量依賴於Python對象,它會受到Python GIL的影響。當執行瓶頸是顯式釋放GIL的已編譯擴展時,threading最有用(例如,with-nogil塊中封裝的Cython迴圈或對NumPy等庫的昂貴調用)。

    • 最後,可以通過調用register_pallel_backend()來註冊後端。

    不建議在類庫中調用Parallel時對backend名稱進行硬編碼,取而代之,建議設置軟提示(prefer)或硬約束(require),以便庫用戶可以使用parallel_backend()上下文管理器從外部更改backend

  • return_generator: bool

    如果為True,則對此實例的調用將返回一個生成器,併在結果可獲取時立即按原始順序返回結果。請註意,預期用途是一次運行一個調用。對同一個Parallel對象的多次調用將導致RuntimeError

  • prefer: str 可選值 ‘processes’, ‘threads’ ,None, 預設: None

    如果使用parallel_backen()上下文管理器時沒有指定特定後端,則選擇預設prefer給定值。預設的基於進程的後端是loky,而預設的基於線程的後端則是threading。如果指定了backend參數,則忽略該參數。

  • require: ‘sharedmem’ 或者None, 預設None

    用於選擇後端的硬約束。如果設置為'sharedmem',則所選後端將是單主機和基於線程的,即使用戶要求使用具有parallel_backend的非基於線程的後端。

參考文檔

https://joblib.readthedocs.io/en/latest/

https://joblib.readthedocs.io/

https://joblib.readthedocs.io/en/latest/parallel.html#common-usage

作者:授客
微信/QQ:1033553122
全國軟體測試QQ交流群:7156436

Git地址:https://gitee.com/ishouke
友情提示:限於時間倉促,文中可能存在錯誤,歡迎指正、評論!
作者五行缺錢,如果覺得文章對您有幫助,請掃描下邊的二維碼打賞作者,金額隨意,您的支持將是我繼續創作的源動力,打賞後如有任何疑問,請聯繫我!!!
           微信打賞                        支付寶打賞                  全國軟體測試交流QQ群  
              


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ## 懶人篇(搞機工具箱) 官網地址:[晨鐘網路科技 - 致力於開發小眾實用軟體 (jamcz.com)](http://jamcz.com/) 工具箱是一個GUI界面,集成abd環境,開箱即用,不用再折騰環境之類的,懶人必備,安裝包一共不到7MB。 ![image-2023052823061003 ...
  • # CSS三大特性 ## 1、繼承性 [CSS_特性繼承和層疊 - Bublly - 博客園 (cnblogs.com)](https://www.cnblogs.com/ZWeva/p/17471809.html) ## 2、層疊性 [CSS_特性繼承和層疊 - Bublly - 博客園 (cnb ...
  • # CSS特性 ## 1、繼承性 ##### 特性: 1、子元素有預設繼承父元素樣式的特點(**子承父業**) 2、可以繼承的常見屬性(文字控制屬性都可以繼承) 1.color 2.font-style、font-weight、font-size、font-family 3.text-indent, ...
  • # 一、顯示模塊 ## 1、塊級元素 #### 特點: 1.獨占一行(一行只能顯示一個) 2.寬度預設是父元素的寬度,高度預設由內容撐開 3.可以設置寬高 #### 例如: ``` div、p、h系列、ul、li、dl、dt、dd、form、header、.nav、footer. ``` ## 2、 ...
  • 📝背景 公司高級表單組件ProForm高階組件都建立在jsx的運用配置上,項目在實踐落地過程中積累了豐富的經驗,也充分感受到了jsx語法的靈活便捷和可維護性強大,享受到了用其開發的樂趣,獨樂樂不如眾樂樂,為了幫助大家更好的運用jsx,開發提效,特此總結分享。 💎效果對比 以前 以往我們開發一個列 ...
  • >譯者註:在微服務架構設計,構建API和服務間通信技術選型時,對 REST 和 gRPC 的理解和應用還存在知識盲區,近期看到國外的這篇文章:[A detailed comparison of REST and gRPC](https://kreya.app/blog/rest-vs-grpc/), ...
  • ## 矩陣乘法 |0|1| | | | |1|1| 這是一個矩陣,那麼我要讓它乘以一個這樣的矩陣 |1|0| | | | |0|1| 那麼它的結果就是 |0|1| | | | |1|1| 如果乘以它自身,那麼它的結果就是 |1|1| | | | |1|2| 那麼矩陣乘法的公式就應該是 ![](htt ...
  • 軟科高校數據獲取 主要獲取頁面五個欄位;分別是:校名、地區、管理部門、類別、雙一流 在這裡有一個坑就是後續的一些院校在類別和雙一流這個標簽上面都沒有這個值,所以使用try...except...來進行判斷的話會很慢很慢,如果有解決問題的小伙伴歡迎留言一起探討解決方案! 這個項目採用的是Seleniu ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...