
Python是機(jī)器學(xué)習(xí)中的首選編程語(yǔ)言。它很容易使用,并且有許多優(yōu)秀的庫(kù)來(lái)幫助您更快地處理數(shù)據(jù)。但是,當(dāng)我們面對(duì)大量的數(shù)據(jù)時(shí),會(huì)出現(xiàn)一些問(wèn)題。
目前,術(shù)語(yǔ)“大數(shù)據(jù)”經(jīng)常用來(lái)表示包含成百上千個(gè)數(shù)據(jù)點(diǎn)的數(shù)據(jù)集。在這樣的規(guī)模下,向工作過(guò)程中添加任何額外的計(jì)算需要不斷關(guān)注效率。在設(shè)計(jì)機(jī)器學(xué)習(xí)系統(tǒng)時(shí),數(shù)據(jù)預(yù)處理非常重要——在這種情況下,我們必須對(duì)所有數(shù)據(jù)點(diǎn)使用某種操作。
默認(rèn)情況下,Python程序是用單個(gè)CPU內(nèi)核執(zhí)行的單個(gè)進(jìn)程。大多數(shù)現(xiàn)代機(jī)器學(xué)習(xí)硬件至少配備了雙核處理器。這意味著,如果不對(duì)其進(jìn)行優(yōu)化,則在對(duì)數(shù)據(jù)進(jìn)行預(yù)處理時(shí),將遇到“單核、九核”的情況——將浪費(fèi)50%以上的計(jì)算。當(dāng)四核處理器(Intel Core i5)和六核處理器(Intel Core i7)盛行時(shí),這一點(diǎn)將變得更加明顯。
幸運(yùn)的是,Python庫(kù)內(nèi)置了一些隱藏的特性,這些特性允許我們充分利用所有CPU核心能力。使用Python的并發(fā)。期貨模塊,只需要三行代碼就可以將一個(gè)普通的程序轉(zhuǎn)換成一個(gè)適合于多核處理器并行處理的程序。
標(biāo)準(zhǔn)方法
讓我們舉一個(gè)簡(jiǎn)單的例子。在一個(gè)文件夾中,有一個(gè)有成千上萬(wàn)張圖片的圖片數(shù)據(jù)集。在這里,我們決定使用1000份。我們要把所有圖像調(diào)整為600×600像素的分辨率,然后將它們傳輸?shù)缴疃壬窠?jīng)網(wǎng)絡(luò)。下面是Gythub上經(jīng)常會(huì)看到的標(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ù)腳本時(shí)經(jīng)常看到的簡(jiǎn)單模式。
1。從需要處理的文件(或其他數(shù)據(jù))列表開(kāi)始。
2。使用循環(huán)來(lái)逐個(gè)處理每個(gè)數(shù)據(jù),然后在每次迭代中運(yùn)行預(yù)處理。
讓我們?cè)谝粋€(gè)包含1000個(gè)JPEG文件的文件夾中測(cè)試程序,看看運(yùn)行需要多長(zhǎng)時(shí)間:
time python standard_res_conversion.py
在我的核心I7—7000 K 6內(nèi)核CPU上,運(yùn)行時(shí)間為7.9864秒!在這種高端CPU上,這種速度似乎是不可接受的,看看我們能做些什么。
更快的方式
為了理解并行化增強(qiáng),假設(shè)我們需要執(zhí)行相同的任務(wù),例如將1000個(gè)釘子釘?shù)侥绢^中,并且如果需要1秒鐘來(lái)釘一個(gè),則需要1人1000秒來(lái)完成任務(wù)。四個(gè)人只需250秒就可以合作。
在包含1000個(gè)圖像的示例中,Python可以做類(lèi)似的工作:
將 jpeg 文件列表分成 4 個(gè)小組;
運(yùn)行 Python 解釋器中的 4 個(gè)獨(dú)立實(shí)例;
讓 Python 的每個(gè)實(shí)例處理 4 個(gè)數(shù)據(jù)小組中的一個(gè);
結(jié)合四個(gè)處理過(guò)程得到的結(jié)果得出最終結(jié)果列表。
這種方法的重點(diǎn)是Python幫助我們處理所有困難的任務(wù)。我們只告訴它我們要運(yùn)行哪個(gè)函數(shù),要使用多少個(gè)Python實(shí)例,剩下的要用到它!只需更改三行代碼即可。例子:
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)
從上面的代碼中提取一條直線(xiàn):
with concurrent.futures.ProcessPoolExecutor() as executor:
你擁有的CPU內(nèi)核越多,你啟動(dòng)的Python進(jìn)程就越多,我的CPU就有6個(gè)內(nèi)核。實(shí)際的處理代碼如下:
executor.map(load_and_resize, image_files)
執(zhí)行人。MAP()將輸入的函數(shù)和列表作為輸入,列表中的每個(gè)元素都是對(duì)函數(shù)的單個(gè)輸入。由于我們有6個(gè)核心,我們將同時(shí)處理列表中的6個(gè)項(xiàng)目。
如果我們用下面的代碼再次運(yùn)行我們的程序:
time python fast_res_conversion.py
我們可以將運(yùn)行時(shí)間縮短到1.14265秒,并將速度提高近6倍。
注意:在生成更多的Python進(jìn)程并在它們之間整理數(shù)據(jù)方面有一些開(kāi)銷(xiāo),所以速度提升并不總是那么明顯。但總的來(lái)說(shuō),速度的增長(zhǎng)是相當(dāng)可觀的。
總是那么快嗎?
如果您有一個(gè)數(shù)據(jù)列表要處理并在每個(gè)數(shù)據(jù)點(diǎn)上執(zhí)行類(lèi)似的操作,那么使用Python并行池是一個(gè)不錯(cuò)的選擇。但有時(shí)這不是最好的解決辦法。并行池處理的數(shù)據(jù)將不會(huì)以任何可預(yù)測(cè)的順序處理。如果你對(duì)處理結(jié)果有特殊的順序要求,這個(gè)方法可能不適合你。
您處理的數(shù)據(jù)也必須是Python可以“烹調(diào)”的類(lèi)型。幸運(yùn)的是,這些指定的類(lèi)別非常常見(jiàn)。以下是Python的官方文件:
None, True, 及 False
整數(shù)、浮點(diǎn)數(shù)、復(fù)數(shù)
字符串、字節(jié)、字節(jié)數(shù)組
只包含可挑選對(duì)象的元組、列表、集合和字典
在模塊頂層定義的函數(shù)(使用 def ,而不是 lambda )
在模塊頂層定義的內(nèi)置函數(shù)
在模塊頂層定義的類(lèi)
這種類(lèi)的實(shí)例,其 __dict__ 或調(diào)用__getstate__() 的結(jié)果是可選擇的。