用TensorFlow實(shí)現(xiàn)ML模型并調(diào)優(yōu):每秒可做3億次預(yù)測(cè)
TensorFlow 是目前使用最廣泛的機(jī)器學(xué)習(xí)框架之一,它加快了研究速度,并減少了新模型的生產(chǎn)時(shí)間。在一篇論文中,來自原生程序化 DSP 公司 Zemanta 的數(shù)據(jù)科學(xué)總監(jiān) Davorin Kopič和工程師 Jan Hartman 展示了將在線廣告生態(tài)系統(tǒng)中的大規(guī)模機(jī)器學(xué)習(xí)模型轉(zhuǎn)換為 TensorFlow 框架的過程,并將在 TensorFlow 框架中實(shí)現(xiàn)的機(jī)器學(xué)習(xí)模型擴(kuò)展到每秒超過 3 億次預(yù)測(cè)。因此,該研究的主要內(nèi)容是在 TF 中實(shí)現(xiàn)模型并使用各種優(yōu)化技術(shù)以低延遲有效地為其提供服務(wù)。

論文地址:https://arxiv.org/abs/2109.09541
該研究使用的案例是線上廣告的點(diǎn)擊預(yù)測(cè)。在 RTB (實(shí)時(shí)競(jìng)價(jià))中,多個(gè) DSP(競(jìng)標(biāo)者)通過在網(wǎng)頁的加載過程中實(shí)時(shí)競(jìng)標(biāo)來競(jìng)爭(zhēng)在線廣告空間。廣告空間是按廣告印象出售的,這使得以市場(chǎng)價(jià)值出售虛擬廣告空間成為可能。通過使用機(jī)器學(xué)習(xí),RTB 還使廣告商能夠最大化其 KPI,例如點(diǎn)擊率 (CTR)。估算廣告的點(diǎn)擊率是 RTB 的核心問題之一,擁有一個(gè)好的點(diǎn)擊預(yù)測(cè)模型非常重要。
在 Golang 中實(shí)現(xiàn)的基于自定義邏輯回歸和分解機(jī) (FM) 的模型,其表達(dá)能力受限,并且需要手動(dòng)實(shí)現(xiàn)所有學(xué)習(xí)程序,這些都會(huì)減慢實(shí)驗(yàn)速度并限制模型的預(yù)測(cè)性能。因此,研究者決定采用 TensorFlow 框架,并用表達(dá)能力更強(qiáng)的模型替換現(xiàn)有模型。
面臨的挑戰(zhàn)
基于廣告競(jìng)標(biāo)這一具體用例,該研究遇到了一些挑戰(zhàn),共分為實(shí)現(xiàn)、服務(wù)和優(yōu)化三個(gè)方面。
一方面,為每臺(tái)機(jī)器配備一個(gè)或多個(gè)頂級(jí) GPU 的成本會(huì)高得令人望而卻步;另一方面,只有一小群 GPU 的機(jī)器將迫使研究過渡到基于服務(wù)的架構(gòu)。鑒于這兩種選擇都不是特別可取,而且該研究的模型與其他深度學(xué)習(xí)領(lǐng)域(例如計(jì)算機(jī)視覺或自然語言處理)的 SOTA 模型相比相對(duì)較小,因此該研究不在生產(chǎn)中使用 GPU 進(jìn)行推斷。并且由于該研究的模型使用稀疏權(quán)重,其用例也不適合 GPU 工作負(fù)載。
實(shí)現(xiàn)
為了給 TF 模型實(shí)現(xiàn)有效的訓(xùn)練循環(huán),該研究實(shí)現(xiàn)和測(cè)試了多種方法。高吞吐量在線訓(xùn)練和在 TF 中服務(wù)的案例研究很少,而且文檔往往不夠具體,這迫使研究者必須通讀源代碼和基準(zhǔn)原型才能發(fā)現(xiàn)實(shí)現(xiàn)過程中的陷阱。
TF 提供了一個(gè)龐大的生態(tài)系統(tǒng)和大量具有 SOTA 算法實(shí)現(xiàn)的庫(kù)。選擇功能豐富的已有實(shí)現(xiàn)非常容易,但是研究者發(fā)現(xiàn)這些實(shí)現(xiàn)大多未經(jīng)優(yōu)化,因此他們決定自己實(shí)現(xiàn)算法。TF 具有不同抽象級(jí)別的 API,不過,一些 API 雖然易于使用,但通常是效率低下的最底層操作(例如 Estimator API)。研究者最終選擇了 Keras3,因?yàn)樗且粋€(gè)圍繞底層 TF 操作的瘦包裝器(thin wrapper),并具備高水平的性能,且易于理解。由于 TF 是一個(gè)功能和資源都非常豐富的庫(kù),因此該研究還必須考慮要在其中實(shí)現(xiàn)多少機(jī)器學(xué)習(xí) pipeline。研究者選擇暫時(shí)擱置特征轉(zhuǎn)換和交互,僅實(shí)現(xiàn)學(xué)習(xí)算法——盡管它們是最小的可替換部分,但卻具備最大的改進(jìn)潛力。
由于 Golang TF 包裝器僅支持預(yù)測(cè),因此必須在 Python 中實(shí)現(xiàn)訓(xùn)練循環(huán)。腳本通過將其標(biāo)準(zhǔn)輸入作為子進(jìn)程實(shí)現(xiàn)與 Golang 數(shù)據(jù) pipeline 的連接。數(shù)據(jù)以高效的二進(jìn)制格式發(fā)送而無需解析,與 CSV 格式相比,該方法的速度提高了 25%。然后在后臺(tái)線程中讀取數(shù)據(jù),以防止模型在等待數(shù)據(jù)時(shí)空閑。基于此,該研究實(shí)現(xiàn)了在整個(gè)訓(xùn)練 pipeline 中保持高吞吐量。事實(shí)證明,高效的輸入和輸出也是低延遲預(yù)測(cè)的關(guān)鍵,該研究通過將所有輸入特征連接到單個(gè)張量(tensor)中,顯著減少了在序列化和復(fù)制輸入數(shù)據(jù)上花費(fèi)的時(shí)間。

服務(wù)
研究者發(fā)現(xiàn),由于計(jì)算密集型神經(jīng)網(wǎng)絡(luò)的存在,在使用 Golang TF 裝飾器的情況下,DeepFM 模型的 CPU 使用率要高得多。盡管帶來了指標(biāo)的顯著提升,但將這種方法擴(kuò)展到 100% 的流量會(huì)帶來大量的硬件成本。由于當(dāng)前全球面臨芯片短缺的問題,這意味代價(jià)是困難和昂貴的。
顯然,降低計(jì)算成本是很有必要的。然而縮小神經(jīng)網(wǎng)絡(luò)模型規(guī)模的同時(shí),也會(huì)降低模型的預(yù)測(cè)性能。在深入研究了 TF 之后,研究者發(fā)現(xiàn)如果在計(jì)算批處理時(shí)增加示例的數(shù)量,計(jì)算效率會(huì)大大提升。這種低線性增長(zhǎng)是由于 TF 代碼被高度向量化了,TF 也會(huì)產(chǎn)生每次計(jì)算調(diào)用的開銷,然后將其分批攤銷??紤]到這一點(diǎn),如果希望減少計(jì)算調(diào)用的數(shù)量,就需要將許多請(qǐng)求連接到一個(gè)計(jì)算中。
研究者構(gòu)建了全部包含在一個(gè)運(yùn)行的 bidder 實(shí)例的自動(dòng)批處理系統(tǒng),以避免網(wǎng)絡(luò)調(diào)用。由于每個(gè)實(shí)例每秒鐘接收數(shù)千個(gè)傳入請(qǐng)求,因此可以保證連接來自眾多請(qǐng)求的計(jì)算,創(chuàng)建更大的批次。研究者通過一些批處理線程實(shí)現(xiàn)了這一點(diǎn),這些線程接收來自傳入請(qǐng)求的數(shù)據(jù),創(chuàng)建批處理并在批處理完成后初始化計(jì)算。計(jì)算過程中,每隔幾毫秒即初始化一次以避免超時(shí),因?yàn)榕幚砜赡茉谶@個(gè)時(shí)間窗口中沒有填充。這種實(shí)現(xiàn)是高度優(yōu)化的,將計(jì)算調(diào)用的數(shù)量減少到五分之一,同時(shí)將 TF 計(jì)算的 CPU 占用量減半。
雖然在批處理器線程沒有獲得 CPU 時(shí)間的極少數(shù)情況下,會(huì)發(fā)生請(qǐng)求超時(shí),但只有不足 0.01% 的請(qǐng)求會(huì)出現(xiàn)這種情況。研究者觀察到平均延遲略有增加(平均約 5 毫秒),流量高峰時(shí)可能會(huì)更多一些。因此他們實(shí)施了 SLA(服務(wù)等級(jí)協(xié)議)和適當(dāng)?shù)谋O(jiān)控手段,以確保延遲的穩(wěn)定性。鑒于沒有大幅增加超時(shí)的百分比,這些方法仍是非常有效的,也是這一 TF 服務(wù)機(jī)制的核心。
本文作者之一 Davorin Kopič
優(yōu)化
研究者在 TF 中實(shí)施的模型最初比定制的 FMs 慢得多,為了尋找加速空間,研究者大量使用內(nèi)置的 TF 分析器來尋找執(zhí)行時(shí)間最長(zhǎng)的操作,并盡可能進(jìn)行了改進(jìn)。最常見的是各種冗余的 reshape 或轉(zhuǎn)換運(yùn)算。其中一個(gè)更有趣的發(fā)現(xiàn)是 Adam 優(yōu)化器比 Adagrad 慢得多 (大約 50%),盡管二者運(yùn)算數(shù)量上的差異很小。分析器顯示,對(duì)稀疏權(quán)值進(jìn)行梯度更新需要大量計(jì)算時(shí)間。這是因?yàn)槟P偷臋?quán)重是稀疏的 (特征大部分是分類的,因此非常稀疏) ,而優(yōu)化器沒有考慮到這個(gè)事實(shí)。
由于用 Adagrad 替換 Adam 意味著深度模型性能的顯著降低,研究者也尋找了其他解決方案:切換到 Lazy Adam 的優(yōu)化器被證明是非常有效的,因?yàn)樗梢苑浅S行У靥幚硐∈铏?quán)重問題。結(jié)果顯示,其整體加快了超過 40% 的訓(xùn)練速度,與 Adagrad 相接近。
由于使用了自適應(yīng)優(yōu)化器(比如 Adam),這也需要存儲(chǔ)權(quán)重矩和方差,每個(gè)參數(shù)將存儲(chǔ)三個(gè)值,將保存的模型大小增加了三倍。然而,這些值實(shí)際上并不用于預(yù)測(cè),只用于訓(xùn)練。研究者利用這一點(diǎn)構(gòu)建了優(yōu)化過程,去掉了這些值的模型,減少了 66% 的數(shù)據(jù)量,并降低了內(nèi)存使用量和成本。