【💊 Python的解憂錦囊】multiprocess/multithread 如何重複使用大模型

閱讀時間約 7 分鐘
raw-image



關於多執行緒/多行程的使用方式

在Python 3.2版本之後加入了「concurrent.futures」啟動平行任務, 它可以更好的讓我們管理多執行緒/多行程的應用場景,讓我們在面對這種併發問題時可以不必害怕, 用一個非常簡單的方式就能夠處裡, 底下我們將為您展示一段程式碼:

import concurrent.futures
import time

# 定義一個任務,這裡示範了一個簡單的任務,打印數字並暫停一段時間
def task(number):
print(f"Starting task {number}")
time.sleep(2) # 模擬任務執行時間
print(f"Task {number} completed")
return f"Task {number} result"

if __name__ == "__main__":
# 創建 ThreadPoolExecutor,設置最大執行緒數量為3
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
# 使用 map 方法提交任務
results = executor.map(task, range(5)) # 提交5個任務

# 直接迭代獲取結果
for result in results:
print(f"Task result: {result}")

是不是使用起來非常的簡單又直觀? 單純看看上面的程式碼當然看不出來, 讓我們為您展示一下過往的處理方式:

import threading
import time

# 定義一個任務,這裡示範了一個簡單的任務,打印數字並暫停一段時間
def task(number):
print(f"Starting task {number}")
time.sleep(2) # 模擬任務執行時間
print(f"Task {number} completed")

if __name__ == "__main__":
threads = [] # 創建一個空列表,用於存放執行緒

# 創建並啟動5個執行緒,每個執行緒執行一個任務
for i in range(5):
thread = threading.Thread(target=task, args=(i,))
threads.append(thread)
thread.start()

# 等待所有執行緒完成
for thread in threads:
thread.join()

print("All tasks completed")

傳統在使用多執行緒處理任務時需要自行管理, 而並非像是「concurrent.futures」那麼的直觀, 簡單的事情簡單做, 錯誤會更少,因此我們應該考慮這種封裝到最簡易使用的技巧, 雖說語法糖在尚未了解其背後原理的狀況之下很危險, 但也不能完全不使用, 而是應該去認識它,並試著採用它, 讓我們複雜的程式更加的乾淨整潔, 以達到永續維護的效果。

P.S 上述的程式碼都是以多執行緒的方式展示, 而多行程也大同小異, 歡迎參考「ProcessPoolExecutor」。

多執行緒/多行程處理大模型任務時

相信在使用AI模型進行任務(NLP、NLU、語音辨識...)時, 最後期望資源最大化時就是multiprocess/multithread派上用場的時刻, 但問題來了,假設我們每個job都重新載入模型到記憶體運作的過程, 真的有比較省時嗎? 光是I/O就耗費大量的時間了, 還不如單核的快,因此我們需要的是如何預先建置多個worker的模型在等待我們的工作, 正巧在在3.7版之後,新增 initializer 與 initargs 引數,就讓我們一起來看看這個神奇的初始化參數吧!

我們可以利用幾個關鍵的元素來完成這件事情...

  • process/thread id識別碼。
  • init函數。
  • 全域模型變數。
# pid, 模型變數
models: Dict[int, 模型型別] = {}

def _init_models():
"""為每個worker初始化模型
"""
# 使用process id作為模型的識別碼, 讓正確的process取得正確的模型
pid = multiprocessing.current_process().pid

logger.debug('[模型載入中] %s', pid)

model = Model(...)

models[pid] = model

logger.debug('[模型載入完畢] %s', pid)

if __name__ == "__main__":
# 創建 ThreadPoolExecutor,設置最大執行緒數量為3
with concurrent.futures.ProcessPoolExecutor(
max_workers=3,
initializer=_init_models
) as executor:
# 使用 map 方法提交任務
results = executor.map(task, ...) # 提交N個任務

# 直接迭代獲取結果
for result in results:
print(f"Task result: {result}")

透過這樣的模式就能夠預先規劃資源分配, 最多就幾個模型會被載入到記憶體之中, 我們也知道GPU一張貴貴的, 不可能無限制的載入到記憶體運作,因此這方式可以讓我們在資源最大化的運作之下完成複雜的模型任務。

結語

資源有限的情況之下, 我們只能設法善用資源, 讓資源運用最佳化, 避免浪費, 畢竟硬體也都是成本的堆疊啊! 而在使用「multiprocess/multithread」時也是需要具備許多基礎知識才能夠更好的運用, 這次就針對多行程共用模型的情境進行分享, 若有更進階的使用方式也歡迎留言分享, 讓我們共同學習更進一步吧!

avatar-img
118會員
264內容數
哈囉,我是阿Han,是一位 👩‍💻 軟體研發工程師,喜歡閱讀、學習、撰寫文章及教學,擅長以圖代文,化繁為簡,除了幫助自己釐清思路之外,也希望藉由圖解的方式幫助大家共同學習,甚至手把手帶您設計出高品質的軟體產品。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
阿Han的沙龍 的其他內容
這個問題發生在我們開發Python的Websocket Server時, 使用以下的程式碼架設服務 start_server = websockets.serve(server, 'localhost', args.port) async with start_server:
假設我們今天想要訓練一個AI模型, 那麼我們會有一批大型資料集, 通常會根據比例來切分三個模型訓練所需的訓練集(train)、驗證集(dev)、測試集(test), 而我們本次會示範一下Python如何對一個List清單進行切分, 基本上大同小異, 我們只要掌握作法即可概念相通。 任務提示
我們在使用Python語言進行軟體開發時, 常常會需要dict這個資料結構來儲存複雜結構的資料, 就如同JSON一般, 我們會具有這樣的Key/Value模式組成的資料結構, 如下圖: 而當我們在Python的世界裡, 除了嚴謹規範資料欄位的@dataclass之外, 更常使用的就是「di
我們在「【🔒 Python 先修班】👆 打造友善的使用者互動CLI介面」有介紹Python的Click命令列參數設計介面的方式, 那我們除了設計出介面提供使用者互動之外, 有時候也需要一點驗證機制, 畢竟我們心裡都清楚「garbage in, garbage out」的後果, 為了減少這種狀
Python雖然是直譯式的腳本語言, 用起來非常方便, 但當我們的工具越發成熟時, 就會需要將使用方式、介面給設計好, 那通常都會用來處理後端伺服器的作業, 也比較面向IT端, 因此我們通常會以Command Line的形式與使用工具的人進行互動, 而內建模組雖然有「argparse」可以讓我們
排序這個動作在軟體開發中常常會使用到, 從使用者期望所見的順序到資料處理的效能議題都與排序息息相關, 因此掌握程式語言的排序功能是非常重要的一個環節, 而我們在閱讀他人的Go專案程式碼時也會看到排序的方式有些許不同, 那究竟有何差異呢? 就讓我們繼續看下去吧… 其實在進入今天的主題之前, 我們
這個問題發生在我們開發Python的Websocket Server時, 使用以下的程式碼架設服務 start_server = websockets.serve(server, 'localhost', args.port) async with start_server:
假設我們今天想要訓練一個AI模型, 那麼我們會有一批大型資料集, 通常會根據比例來切分三個模型訓練所需的訓練集(train)、驗證集(dev)、測試集(test), 而我們本次會示範一下Python如何對一個List清單進行切分, 基本上大同小異, 我們只要掌握作法即可概念相通。 任務提示
我們在使用Python語言進行軟體開發時, 常常會需要dict這個資料結構來儲存複雜結構的資料, 就如同JSON一般, 我們會具有這樣的Key/Value模式組成的資料結構, 如下圖: 而當我們在Python的世界裡, 除了嚴謹規範資料欄位的@dataclass之外, 更常使用的就是「di
我們在「【🔒 Python 先修班】👆 打造友善的使用者互動CLI介面」有介紹Python的Click命令列參數設計介面的方式, 那我們除了設計出介面提供使用者互動之外, 有時候也需要一點驗證機制, 畢竟我們心裡都清楚「garbage in, garbage out」的後果, 為了減少這種狀
Python雖然是直譯式的腳本語言, 用起來非常方便, 但當我們的工具越發成熟時, 就會需要將使用方式、介面給設計好, 那通常都會用來處理後端伺服器的作業, 也比較面向IT端, 因此我們通常會以Command Line的形式與使用工具的人進行互動, 而內建模組雖然有「argparse」可以讓我們
排序這個動作在軟體開發中常常會使用到, 從使用者期望所見的順序到資料處理的效能議題都與排序息息相關, 因此掌握程式語言的排序功能是非常重要的一個環節, 而我們在閱讀他人的Go專案程式碼時也會看到排序的方式有些許不同, 那究竟有何差異呢? 就讓我們繼續看下去吧… 其實在進入今天的主題之前, 我們
你可能也想看
Google News 追蹤
上兩篇有關List的文章,此篇文上兩章的延續,整理一些常用的方法和操作。 [Python]List(列表)新增、修改、刪除元素 [Python基礎]容器 list(列表),tuple(元組) 還有一些常用的 list 方法和操作,讓你能更靈活地處理列表數據
Thumbnail
避免 thread 競速(Race Condition)是多執行緒編程中常見的挑戰之一。 Race Condition 發生在多個執行緒同時訪問和修改共享資源時,因為執行緒之間的執行順序無法預測,可能會導致數據的不一致性或意外行為。 本文主要介紹如何使用Lock來避免此狀況出現。 首先先看沒
這一節要來看看,有許多個力同時作用時,該怎麼處理。
Thumbnail
Python語法包括條件語句、迴圈、函數和變數的使用。條件語句如if、elif和else用於進行條件判斷,for和while是兩種主要的迴圈,def用於定義函數。變數可以被賦予數字或字符串,並可使用類型提示來指定變數的類型。註解可以是單行或多行,並可用於解釋函數或類的用途和作用。
Thumbnail
今天來介紹python的函式 函式在python中是非常重要的一環,因為到了後期,程式會越來越複雜。 而函式可以想成是容易管理的小程式,當我們需要使用時,只需呼叫即可。
Thumbnail
在Python中,queue是一個非常有用的模块。 它提供了多種佇列(queue)實現,用於在多線程環境中安全地交換信息或者數據。 佇列(queue)是一種先進先出(FIFO)的數據結構,允許在佇列的一端插入元素,另一端取出元素。(FIFO 是First In, First Out 的縮寫)
Thumbnail
當你需要在 Python 中執行多個任務,但又不希望它們相互阻塞時,可以使用 threading 模組。 threading 模組允許你在單個程序中創建多個執行緒,這些執行緒可以同時運行,從而實現並行執行多個任務的效果。
Thumbnail
在日常中,常有重複性相當高的事情,不斷地重複在做,重複的事做久就會慢慢變成是一個習慣,這個習慣就會讓人下意識地完成一些事情。 習慣是一種自動化的行為模式,這些行為模式在重複進行的過程中變得固定且容易自動化。 在Python程式語言中,for迴圈就類似這種概念
上兩篇有關List的文章,此篇文上兩章的延續,整理一些常用的方法和操作。 [Python]List(列表)新增、修改、刪除元素 [Python基礎]容器 list(列表),tuple(元組) 還有一些常用的 list 方法和操作,讓你能更靈活地處理列表數據
Thumbnail
避免 thread 競速(Race Condition)是多執行緒編程中常見的挑戰之一。 Race Condition 發生在多個執行緒同時訪問和修改共享資源時,因為執行緒之間的執行順序無法預測,可能會導致數據的不一致性或意外行為。 本文主要介紹如何使用Lock來避免此狀況出現。 首先先看沒
這一節要來看看,有許多個力同時作用時,該怎麼處理。
Thumbnail
Python語法包括條件語句、迴圈、函數和變數的使用。條件語句如if、elif和else用於進行條件判斷,for和while是兩種主要的迴圈,def用於定義函數。變數可以被賦予數字或字符串,並可使用類型提示來指定變數的類型。註解可以是單行或多行,並可用於解釋函數或類的用途和作用。
Thumbnail
今天來介紹python的函式 函式在python中是非常重要的一環,因為到了後期,程式會越來越複雜。 而函式可以想成是容易管理的小程式,當我們需要使用時,只需呼叫即可。
Thumbnail
在Python中,queue是一個非常有用的模块。 它提供了多種佇列(queue)實現,用於在多線程環境中安全地交換信息或者數據。 佇列(queue)是一種先進先出(FIFO)的數據結構,允許在佇列的一端插入元素,另一端取出元素。(FIFO 是First In, First Out 的縮寫)
Thumbnail
當你需要在 Python 中執行多個任務,但又不希望它們相互阻塞時,可以使用 threading 模組。 threading 模組允許你在單個程序中創建多個執行緒,這些執行緒可以同時運行,從而實現並行執行多個任務的效果。
Thumbnail
在日常中,常有重複性相當高的事情,不斷地重複在做,重複的事做久就會慢慢變成是一個習慣,這個習慣就會讓人下意識地完成一些事情。 習慣是一種自動化的行為模式,這些行為模式在重複進行的過程中變得固定且容易自動化。 在Python程式語言中,for迴圈就類似這種概念