Python是機器學(xué)習(xí)中的首選編程語言跪妥。它很容易使用鞋喇,并且有許多優(yōu)秀的庫來幫助您更快地處理數(shù)據(jù)。但是眉撵,當(dāng)我們面對大量的數(shù)據(jù)時侦香,會出現(xiàn)一些問題。
目前纽疟,術(shù)語“大數(shù)據(jù)”經(jīng)常用來表示包含成百上千個數(shù)據(jù)點的數(shù)據(jù)集罐韩。在這樣的規(guī)模下,向工作過程中添加任何額外的計算需要不斷關(guān)注效率污朽。在設(shè)計機器學(xué)習(xí)系統(tǒng)時散吵,數(shù)據(jù)預(yù)處理非常重要——在這種情況下,我們必須對所有數(shù)據(jù)點使用某種操作蟆肆。
默認情況下,Python程序是用單個CPU內(nèi)核執(zhí)行的單個進程。大多數(shù)現(xiàn)代機器學(xué)習(xí)硬件至少配備了雙核處理器矾飞。這意味著姓建,如果不對其進行優(yōu)化,則在對數(shù)據(jù)進行預(yù)處理時蛇损,將遇到“單核赁温、九核”的情況——將浪費50%以上的計算坛怪。當(dāng)四核處理器(Intel Core i5)和六核處理器(Intel Core i7)盛行時,這一點將變得更加明顯股囊。
幸運的是袜匿,Python庫內(nèi)置了一些隱藏的特性,這些特性允許我們充分利用所有CPU核心能力毁涉。使用Python的并發(fā)沉帮。期貨模塊,只需要三行代碼就可以將一個普通的程序轉(zhuǎn)換成一個適合于多核處理器并行處理的程序贫堰。
標(biāo)準(zhǔn)方法
讓我們舉一個簡單的例子穆壕。在一個文件夾中,有一個有成千上萬張圖片的圖片數(shù)據(jù)集其屏。在這里喇勋,我們決定使用1000份。我們要把所有圖像調(diào)整為600×600像素的分辨率偎行,然后將它們傳輸?shù)缴疃壬窠?jīng)網(wǎng)絡(luò)川背。下面是Gythub上經(jīng)常會看到的標(biāo)準(zhǔn)Python代碼:
import glob import os import cv2 ### Loop through all jpg files in the current folder? ### Resize each one to size 600x600 for image_filename in glob.glob("*.jpg"):? ### Read in the image data? img = cv2.imread(image_filename)? ### Resize the image? img = cv2.resize(img, (600, 600))?
上面的程序遵循處理數(shù)據(jù)腳本時經(jīng)常看到的簡單模式蛤袒。
1熄云。從需要處理的文件(或其他數(shù)據(jù))列表開始。
2妙真。使用循環(huán)來逐個處理每個數(shù)據(jù)缴允,然后在每次迭代中運行預(yù)處理。
讓我們在一個包含1000個JPEG文件的文件夾中測試程序珍德,看看運行需要多長時間:
time python standard_res_conversion.py
在我的核心I7—7000 K 6內(nèi)核CPU上练般,運行時間為7.9864秒!在這種高端CPU上锈候,這種速度似乎是不可接受的薄料,看看我們能做些什么。
更快的方式
為了理解并行化增強泵琳,假設(shè)我們需要執(zhí)行相同的任務(wù)摄职,例如將1000個釘子釘?shù)侥绢^中,并且如果需要1秒鐘來釘一個获列,則需要1人1000秒來完成任務(wù)琳钉。四個人只需250秒就可以合作。
在包含1000個圖像的示例中蛛倦,Python可以做類似的工作:
將 jpeg 文件列表分成 4 個小組歌懒;
運行 Python 解釋器中的 4 個獨立實例;
讓 Python 的每個實例處理 4 個數(shù)據(jù)小組中的一個溯壶;
結(jié)合四個處理過程得到的結(jié)果得出最終結(jié)果列表及皂。
這種方法的重點是Python幫助我們處理所有困難的任務(wù)甫男。我們只告訴它我們要運行哪個函數(shù),要使用多少個Python實例验烧,剩下的要用到它板驳!只需更改三行代碼即可。例子:
import glob import os import cv2 import concurrent.futures def load_and_resize(image_filename):? ### Read in the image data? img = cv2.imread(image_filename)? ### Resize the image? img = cv2.resize(img, (600, 600))? ### Create a pool of processes. By default, one is created for each CPU in your machine. with concurrent.futures.ProcessPoolExecutor() as executor:? ### Get a list of files to process? image_files = glob.glob("*.jpg")? ### Process the list of files, but split the work across the process pool to use all CPUs? ### Loop through all jpg files in the current folder? ### Resize each one to size 600x600? executor.map(load_and_resize, image_files)
從上面的代碼中提取一條直線:
with concurrent.futures.ProcessPoolExecutor() as executor:
你擁有的CPU內(nèi)核越多碍拆,你啟動的Python進程就越多若治,我的CPU就有6個內(nèi)核。實際的處理代碼如下:
executor.map(load_and_resize, image_files)
執(zhí)行人感混。MAP()將輸入的函數(shù)和列表作為輸入端幼,列表中的每個元素都是對函數(shù)的單個輸入。由于我們有6個核心弧满,我們將同時處理列表中的6個項目婆跑。
如果我們用下面的代碼再次運行我們的程序:
time python fast_res_conversion.py
我們可以將運行時間縮短到1.14265秒,并將速度提高近6倍庭呜。
注意:在生成更多的Python進程并在它們之間整理數(shù)據(jù)方面有一些開銷滑进,所以速度提升并不總是那么明顯。但總的來說募谎,速度的增長是相當(dāng)可觀的扶关。
總是那么快嗎?
如果您有一個數(shù)據(jù)列表要處理并在每個數(shù)據(jù)點上執(zhí)行類似的操作数冬,那么使用Python并行池是一個不錯的選擇节槐。但有時這不是最好的解決辦法。并行池處理的數(shù)據(jù)將不會以任何可預(yù)測的順序處理吉执。如果你對處理結(jié)果有特殊的順序要求,這個方法可能不適合你地来。
您處理的數(shù)據(jù)也必須是Python可以“烹調(diào)”的類型戳玫。幸運的是,這些指定的類別非常常見未斑。以下是Python的官方文件:
None, True, 及 False
整數(shù)咕宿、浮點數(shù)、復(fù)數(shù)
字符串蜡秽、字節(jié)府阀、字節(jié)數(shù)組
只包含可挑選對象的元組、列表芽突、集合和字典
在模塊頂層定義的函數(shù)(使用 def 试浙,而不是 lambda )
在模塊頂層定義的內(nèi)置函數(shù)
在模塊頂層定義的類
這種類的實例,其 __dict__ 或調(diào)用__getstate__() 的結(jié)果是可選擇的寞蚌。