【💊 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
125會員
275內容數
哈囉,我是阿Han,是一位 👩‍💻 軟體研發工程師,喜歡閱讀、學習、撰寫文章及教學,擅長以圖代文,化繁為簡,除了幫助自己釐清思路之外,也希望藉由圖解的方式幫助大家共同學習,甚至手把手帶您設計出高品質的軟體產品。
留言
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 追蹤
Thumbnail
全方位分析脫離繼承戰的方法,大膽猜測誰會成為卡丁國下一任國王。
Thumbnail
當我們的系統發展到一定程度時, 難免會面臨到正式上線的問題, 要如何讓維運更加簡易呢? 尤其隨著複雜的客製化配置的出現時, 我們應該如何有效的管理, 甚至驗證配置是否如預期資料型態、格式…, 而正好 pydantic 可以滿足這樣的需求, 就讓我們來看看怎麼使用吧! 需安裝的套件 pip i
Thumbnail
要如何使用unicorn啟動多個FastAPI服務, 歡迎參考我們的「【💊 Python的解憂錦囊 - FastAPI】如何啟動多個Workers」。 當我們試著設計帶入模組化時… 我們在「【💊 Python的解憂錦囊 - FastAPI】使用 lifespan 來共享資料與管理生命週期
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」有說明如何使用uvicorn來啟動FastAPI服務, 假設今天我們的API是一個CPU密集型的運算服務時, 通常我們會希望開啟多個行程來幫忙處理, 那麼大致上的撰寫方式會像這樣: app = FastAPI( ti
Thumbnail
MinIO 是一個高性能的物件存儲系統,設計用於大規模的數據存儲需求, 甚至是各種非結構化數據也都能往這邊儲存, 也支持群集擴展, 非常適合正在尋找儲存方案的朋友們。 我們在「【💎 Message Queue - Kafka 案例篇】如何將檔案流上傳到minio - 完整檔案 」介紹了如
上兩篇有關List的文章,此篇文上兩章的延續,整理一些常用的方法和操作。 [Python]List(列表)新增、修改、刪除元素 [Python基礎]容器 list(列表),tuple(元組) 還有一些常用的 list 方法和操作,讓你能更靈活地處理列表數據
Thumbnail
避免 thread 競速(Race Condition)是多執行緒編程中常見的挑戰之一。 Race Condition 發生在多個執行緒同時訪問和修改共享資源時,因為執行緒之間的執行順序無法預測,可能會導致數據的不一致性或意外行為。 本文主要介紹如何使用Lock來避免此狀況出現。 首先先看沒
Thumbnail
訊息的即時傳遞已然成為現代社會的趨勢了, 影音也是如此, 即時! 即時! 即時! 已經是目前使用者體驗的必要元素了, 在這邊我們要分享的主題是如何在python程式語言的情境下使用ffmpeg來將音檔串流的轉換格式, 為什麼會有這樣的需求呢? 因為我們處理音檔時可能會需要統一輸出的格式, 當然背後也
這一節要來看看,有許多個力同時作用時,該怎麼處理。
Thumbnail
全方位分析脫離繼承戰的方法,大膽猜測誰會成為卡丁國下一任國王。
Thumbnail
當我們的系統發展到一定程度時, 難免會面臨到正式上線的問題, 要如何讓維運更加簡易呢? 尤其隨著複雜的客製化配置的出現時, 我們應該如何有效的管理, 甚至驗證配置是否如預期資料型態、格式…, 而正好 pydantic 可以滿足這樣的需求, 就讓我們來看看怎麼使用吧! 需安裝的套件 pip i
Thumbnail
要如何使用unicorn啟動多個FastAPI服務, 歡迎參考我們的「【💊 Python的解憂錦囊 - FastAPI】如何啟動多個Workers」。 當我們試著設計帶入模組化時… 我們在「【💊 Python的解憂錦囊 - FastAPI】使用 lifespan 來共享資料與管理生命週期
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」有說明如何使用uvicorn來啟動FastAPI服務, 假設今天我們的API是一個CPU密集型的運算服務時, 通常我們會希望開啟多個行程來幫忙處理, 那麼大致上的撰寫方式會像這樣: app = FastAPI( ti
Thumbnail
MinIO 是一個高性能的物件存儲系統,設計用於大規模的數據存儲需求, 甚至是各種非結構化數據也都能往這邊儲存, 也支持群集擴展, 非常適合正在尋找儲存方案的朋友們。 我們在「【💎 Message Queue - Kafka 案例篇】如何將檔案流上傳到minio - 完整檔案 」介紹了如
上兩篇有關List的文章,此篇文上兩章的延續,整理一些常用的方法和操作。 [Python]List(列表)新增、修改、刪除元素 [Python基礎]容器 list(列表),tuple(元組) 還有一些常用的 list 方法和操作,讓你能更靈活地處理列表數據
Thumbnail
避免 thread 競速(Race Condition)是多執行緒編程中常見的挑戰之一。 Race Condition 發生在多個執行緒同時訪問和修改共享資源時,因為執行緒之間的執行順序無法預測,可能會導致數據的不一致性或意外行為。 本文主要介紹如何使用Lock來避免此狀況出現。 首先先看沒
Thumbnail
訊息的即時傳遞已然成為現代社會的趨勢了, 影音也是如此, 即時! 即時! 即時! 已經是目前使用者體驗的必要元素了, 在這邊我們要分享的主題是如何在python程式語言的情境下使用ffmpeg來將音檔串流的轉換格式, 為什麼會有這樣的需求呢? 因為我們處理音檔時可能會需要統一輸出的格式, 當然背後也
這一節要來看看,有許多個力同時作用時,該怎麼處理。