你寫的ML代碼占多少內(nèi)存?這件事很重要,但很多人還不懂
在進(jìn)行機(jī)器學(xué)習(xí)任務(wù)時(shí),你需要學(xué)會(huì)使用代碼快速檢查模型的內(nèi)存占用量。原因很簡單,硬件資源是有限的,單個(gè)機(jī)器學(xué)習(xí)模塊不應(yīng)該占用系統(tǒng)的所有內(nèi)存,這一點(diǎn)在邊緣計(jì)算場(chǎng)景中尤其重要。
比如,你寫了一個(gè)很棒的機(jī)器學(xué)習(xí)程序,或者搭建了一個(gè)不錯(cuò)的神經(jīng)網(wǎng)絡(luò)模型,然后想在某些 Web 服務(wù)或 REST API 上部署模型?;蛘吣闶腔诠S傳感器的數(shù)據(jù)流開發(fā)了模型,計(jì)劃將其部署在其中一臺(tái)工業(yè)計(jì)算機(jī)上。
這時(shí),你的模型可能是硬件上運(yùn)行的幾百個(gè)模型之一,所以你必須對(duì)內(nèi)存占用峰值有所了解。否則多個(gè)模型同時(shí)達(dá)到了內(nèi)存占用峰值,系統(tǒng)可能會(huì)崩潰。
因此,搞清楚代碼運(yùn)行時(shí)的內(nèi)存配置文件(動(dòng)態(tài)數(shù)量)非常重要。這與模型的大小和壓縮均無關(guān),可能是你事先已經(jīng)將其保存在磁盤上的特殊對(duì)象,例如 Scikit-learn Joblib dump、Python Pickle dump,TensorFlow HFD5 等。
Scalene:簡潔的內(nèi)存 / CPU/GPU 分析器
首先要討論的是 Scalene,它是一個(gè) Python 的高性能 CPU 和內(nèi)存分析器,由馬薩諸塞大學(xué)研發(fā)。其 GitHub 頁面是這樣介紹的:「 Scalene 是適用于 Python 的高性能 CPU、GPU 和內(nèi)存分析器,它可以執(zhí)行許多其他 Python 分析器無法做到的事情,提供詳細(xì)信息比其他分析器快幾個(gè)數(shù)量級(jí)?!?/p>
安裝
它是一個(gè) Python 包,所以按照通常方法安裝:
- pip install scalene
這樣適用于 Linux OS,作者沒有在 Windows 10 上進(jìn)行測(cè)試。
在 CLI 或 Jupyter Notebook 內(nèi)部使用
Scalene 的使用非常簡單:
- scalene <yourapp.py>
也可以使用魔術(shù)命令在 Jupyter notebook 中使用它:
- %load_ext scalene
輸出示例
下面是一個(gè)輸出示例。稍后將對(duì)此進(jìn)行更深入的研究。
這些是 Scalene 一些很酷的功能:
- 行和函數(shù):報(bào)告有關(guān)整個(gè)函數(shù)和每個(gè)獨(dú)立代碼行的信息;
- 線程:支持 Python 線程;
- 多進(jìn)程處理:支持使用 multiprocessing 庫;
- Python 與 C 的時(shí)間:Scalene 用在 Python 與本機(jī)代碼(例如庫)上的時(shí)間;
- 系統(tǒng)時(shí)間:區(qū)分系統(tǒng)時(shí)間(例如,休眠或執(zhí)行 I / O 操作);
- GPU:報(bào)告在英偉達(dá) GPU 上使用的時(shí)間(如果有);
- 復(fù)制量:報(bào)告每秒要復(fù)制的數(shù)據(jù)量;
- 泄漏檢測(cè):自動(dòng)查明可能造成內(nèi)存泄漏的線路。
ML 代碼具體示例
接下來看一下 Scalene 用于內(nèi)存配置標(biāo)準(zhǔn)機(jī)器學(xué)習(xí)代碼的工作。對(duì)三個(gè)模型使用 Scikit-learn 庫,并利用其綜合數(shù)據(jù)生成功能來創(chuàng)建數(shù)據(jù)集。
對(duì)比的是兩種不同類型的 ML 模型:
- 多元線性回歸模型;
- 具有相同數(shù)據(jù)集的深度神經(jīng)網(wǎng)絡(luò)模型。
線性回歸模型
使用標(biāo)準(zhǔn)導(dǎo)入和 NUM_FEATURES 、 NUM_SMPLES 兩個(gè)變量進(jìn)行一些實(shí)驗(yàn)。
這里沒有展示數(shù)據(jù)生成和模型擬合代碼,它們是非常標(biāo)準(zhǔn)的。作者將擬合的模型另存為 pickled dump,并將其與測(cè)試 CSV 文件一起加載以進(jìn)行推斷。
為了清晰起見,將所有內(nèi)容置于 Scalene 執(zhí)行和報(bào)告環(huán)境下循環(huán)運(yùn)行。
當(dāng)運(yùn)行命令時(shí):
- $ scalene linearmodel.py --html >> linearmodel-scalene.html
將這些結(jié)果作為輸出。注意,此處使用了 --html 標(biāo)志并將輸出通過管道傳輸?shù)?HTML 文件,以便于報(bào)告。
令人驚訝的是,內(nèi)存占用幾乎完全由外部 I / O(例如 Pandas 和 Scikit-learn estimator 加載)控制,少量會(huì)將測(cè)試數(shù)據(jù)寫到磁盤上的 CSV 文件中。實(shí)際的 ML 建模、Numpy、Pandas 操作和推理,根本不會(huì)影響內(nèi)存。
我們可以縮放數(shù)據(jù)集大小(行數(shù))和模型復(fù)雜度(特征數(shù)),并運(yùn)行相同的內(nèi)存配置文件以記錄各種操作在內(nèi)存消耗方面的表現(xiàn)。結(jié)果顯示在這里。
此處,X 軸代表特征 / 數(shù)據(jù)點(diǎn)集。注意該圖描繪的是百分比,而不是絕對(duì)值,展示了各種類型操作的相對(duì)重要性。
從這些實(shí)驗(yàn)中得出的結(jié)論是,Scikit-learn 線性回歸估計(jì)非常高效,并且不會(huì)為實(shí)際模型擬合或推理消耗大量內(nèi)存。
但就代碼而言,它確實(shí)有固定的內(nèi)存占用,并在加載時(shí)會(huì)消耗大量內(nèi)存。不過隨著數(shù)據(jù)大小和模型復(fù)雜性的增加,整個(gè)代碼占用百分比會(huì)下降。如果使用這樣的模型,則可能需要關(guān)注數(shù)據(jù)文件 I / O,優(yōu)化代碼以獲得更好的內(nèi)存性能。
深度神經(jīng)網(wǎng)絡(luò)如何?
如果我們使用 2 個(gè)隱藏層的神經(jīng)網(wǎng)絡(luò)(每個(gè)隱藏層有 50 個(gè)神經(jīng)元)運(yùn)行類似的實(shí)驗(yàn),那么結(jié)果如下所示。
代碼地址:https://github.com/tirthajyoti/Machine-Learning-with-Python/blob/master/Memory-profiling/Scalene/mlp.py
與線性回歸模型不同,神經(jīng)網(wǎng)絡(luò)模型在訓(xùn)練 / 擬合步驟中消耗大量內(nèi)存。但是,由于特征少且數(shù)據(jù)量大,擬合占用的內(nèi)存較少。此外,還可以嘗試各種體系結(jié)構(gòu)和超參數(shù),并記錄內(nèi)存使用情況,達(dá)到合適的設(shè)置。
復(fù)現(xiàn)說明
如果你使用相同的代碼復(fù)現(xiàn)實(shí)驗(yàn),結(jié)果可能會(huì)因硬件、磁盤 / CPU / GPU / 內(nèi)存類型的不同而大相徑庭。
一些關(guān)鍵建議
- 最好在代碼中編寫專注于單個(gè)任務(wù)的小型函數(shù);
- 保留一些自由變量,例如特征數(shù)和數(shù)據(jù)點(diǎn),借助最少的更改來運(yùn)行相同的代碼,在數(shù)據(jù) / 模型縮放時(shí)檢查內(nèi)存配置文件;
- 如果要將一種 ML 算法與另一種 ML 算法進(jìn)行比較,請(qǐng)讓整體代碼的結(jié)構(gòu)和流程盡可能相同以減少混亂。最好只更改 estimator 類并對(duì)比內(nèi)存配置文件;
- 數(shù)據(jù)和模型 I / O(導(dǎo)入語句,磁盤上的模型持久性)在內(nèi)存占用方面可能會(huì)出乎意料地占主導(dǎo)地位,具體取決于建模方案,優(yōu)化時(shí)切勿忽略這些;
- 出于相同原因,請(qǐng)考慮比較來自多個(gè)實(shí)現(xiàn) / 程序包的同一算法的內(nèi)存配置文件(例如 Keras、PyTorch、Scikitlearn)。如果內(nèi)存優(yōu)化是主要目標(biāo),那么即使在功能或性能上不是最佳,也必須尋找一種占用最小內(nèi)存且可以滿意完成工作的實(shí)現(xiàn)方式;
- 如果數(shù)據(jù) I / O 成為瓶頸,請(qǐng)?zhí)剿鞲斓倪x項(xiàng)或其他存儲(chǔ)類型,例如,用 parquet 文件和 Apache Arrow 存儲(chǔ)替換 Pandas CSV??梢钥纯催@篇文章:
《How fast is reading Parquet file (with Arrow) vs. CSV with Pandas?》
https://towardsdatascience.com/how-fast-is-reading-parquet-file-with-arrow-vs-csv-with-pandas-2f8095722e94
Scalene 能做的其他事
在本文中,僅討論了內(nèi)存分析的一小部分,目光放在了規(guī)范機(jī)器學(xué)習(xí)建模代碼上。事實(shí)上 Scalene CLI 也有其他可以利用的選項(xiàng):
- 僅分析 CPU 時(shí)間,不分析內(nèi)存;
- 僅使用非零內(nèi)存減少資源占用;
- 指定 CPU 和內(nèi)存分配的最小閾值;
- 設(shè)置 CPU 采樣率;
- 多線程并行,隨后檢查差異。
最終驗(yàn)證(可選)
在資源較少的情況下,你最好托管一個(gè)驗(yàn)證環(huán)境 / 服務(wù)器,該服務(wù)器將接受給定的建模代碼(如已開發(fā)),并通過這樣的內(nèi)存分析器運(yùn)行它以創(chuàng)建運(yùn)行時(shí)統(tǒng)計(jì)信息。如果它通過內(nèi)存占用空間的預(yù)定標(biāo)準(zhǔn),則只有建模代碼會(huì)被接受用于進(jìn)一步部署。
總結(jié)
在本文中,我們討論了對(duì)機(jī)器學(xué)習(xí)代碼進(jìn)行內(nèi)存配置的重要性。我們需要使其更好地部署在服務(wù)和機(jī)器中,讓平臺(tái)或工程團(tuán)隊(duì)能夠方便運(yùn)用。分析內(nèi)存也可以讓我們找到更高效的、面向特定數(shù)據(jù)或算法的優(yōu)化方式。
希望你能在使用這些工具和技術(shù)進(jìn)行機(jī)器學(xué)習(xí)部署時(shí)能夠獲得成功。