在 Ibotta,我們訓練了許多機器學習模型。這些模型為我們的推薦系統、搜索引擎、定價優化引擎、數據質量等提供動力。它們在與我們的移動應用程序交互時為數百萬用戶做出預測。
當我們使用 Spark 進行數據處理時,我們首選的機器學習框架是 scikit-learn。隨著計算機變得越來越便宜,機器學習解決方案的上市時間變得越來越關鍵,我們探索了加快模型訓練的各種方法。其中一個解決方案是將 Spark 和 scikit-learn 中的元素組合到我們自己的混合解決方案中。
sk-dist 的介紹
我們很高興地宣布我們的開源項目 sk-dist 的啟動。該項目的目標是為使用 Spark 分發 scikit 學習元估計器提供一個通用框架。元估計器的例子有決策樹集合(隨機林和額外隨機樹)、超參數調解器(網格搜索和隨機搜索)和多分類技術(一對多和多對一)。
我們的主要動機是填補傳統機器學習模型空間的空白。在神經網絡和深度學習的空間之外,我們發現我們的訓練模型的大部分計算時間并沒有花在訓練單個數據集的單個模型上。相反,大部分時間都花在使用元估計器在數據集上訓練模型的多次迭代上。
例子
讓我們談談手寫數字數據集。在這里,我們對手寫數字的圖像進行了適當的編碼、分類。我們可以很快在一臺機器上訓練 1797 條記錄的支持向量機,花費的時間不到一秒鐘。但超參數調整需要在訓練數據的不同子集上進行大量的訓練。
如下圖所示,我們已經構建了一個總計需要 1050 個訓練的參數網格。在擁有 100 多個核的 Spark 上使用 sk dist 只需 3.4 秒。這項工作的總時間是 7.2 分鐘,意思是在沒有并行化的單機上訓練要花這么長時間。import timefrom sklearn import datasets, svm
from skdist.distribute.search import DistGridSearchCV
from pyspark.sql import SparkSession # instantiate spark session
spark = (
SparkSession
.builder
.getOrCreate()
)
sc = spark.sparkContext # the digits dataset
digits = datasets.load_digits()
X = digits["data"]
y = digits["target"] # create a classifier: a support vector classifier
classifier = svm.SVC()
param_grid = {
"C": [0.01, 0.01, 0.1, 1.0, 10.0, 20.0, 50.0],
"gamma": ["scale", "auto", 0.001, 0.01, 0.1],
"kernel": ["rbf", "poly", "sigmoid"]
}
scoring = "f1_weighted"
cv = 10# hyperparameter optimization
start = time.time()
model = DistGridSearchCV(
classifier, param_grid,
sc=sc, cv=cv, scoring=scoring,
verbose=True
)
model.fit(X,y)
print("Train time: {0}".format(time.time() - start))
print("Best score: {0}".format(model.best_score_))------------------------------
Spark context found; running with spark
Fitting 10 folds for each of 105 candidates, totalling 1050 fits
Train time: 3.380601406097412
Best score: 0.981450024203508
這個例子演示了一個常見的場景,在這個場景中,將數據擬合到內存中并訓練單個分類器是很簡單的,但是適合超參數優化所需的匹配數量會迅速增加。下面是一個運行網格搜索問題的例子,和上面的 sk dist 示例類似:
帶sk-dist的網格搜索
對于 ibotta 傳統機器學習的實際應用,我們經常發現自己處于類似這樣的情況中:中小型數據(10k 到 1M 的記錄)和許多簡單分類器迭代以適應超參數調整、集成和多分類解決方案。
現有解決方案
傳統的機器學習元估計器訓練方法已經存在。第一個是最簡單的:scikit-learn 使用 joblib 內置的元估計器并行化。這與 sk-dist 的操作非常相似,但是它有一個主要的限制:性能受限于任何機器的資源。即使與理論上擁有數百個內核的單機相比,Spark 仍然具有一些優勢,如執行器的微調內存規范、容錯,以及成本控制選項,如對工作節點使用 spot 實例。
另一個現有的解決方案是 Spark ML,它是 Spark 的一個本地機器學習庫,支持許多與 scikit-learn 相同的算法來解決分類和回歸問題。它還具有諸如樹集合和網格搜索之類的元估計器,以及對多分類問題的支持。
分布在不同的維度上
如上所示,Spark ML 將針對分布在多個執行器上的數據來訓練單個模型。當數據量很大,以至于無法存入一臺機器上的內存時,這種方法可以很好地工作。然而,當數據量很小時,在單臺機器上這可能會比 scikit-learn 的學習效果差。此外,例如,當訓練一個隨機森林時,Spark ML 按順序訓練每個決策樹。此項工作的時間將與決策樹的數量成線性比例,和分配給該任務的資源無關。
對于網格搜索,Spark ML 實現了一個并行參數,該參數將并行地訓練各個模型。然而,每個單獨的模型仍在對分布在執行器之間的數據進行訓練。這項任務的總并行度只是純粹按照模型維度來的,而不是數據分布的維度。
最后,我們希望將我們的訓練分布在與 Spark ML 不同的維度上。當使用中小型數據時,將數據擬合到內存中不是問題。對于隨機森林的例子,我們希望將訓練數據完整地廣播給每個執行器,在每個執行者身上擬合一個獨立的決策樹,并將這些擬合的決策樹帶回給驅動器,以集合成一個隨機森林。這個維度比串行分布數據和訓練決策樹快幾個數量級。
特征
考慮到這些現有解決方案在我們的問題空間中的局限性,我們內部決定開發 sk-dist。歸根結底,我們希望發布的是模型,而不是數據。
雖然 sk-dist 主要關注元估計器的分布式訓練,但它也包括很多其它模塊,如 Spark 的 scikit-learn 模型的分布式預測模塊等。分布式訓練——使用 Spark 進行分布式元估計訓練,支持以下算法:帶網格搜索和隨機搜索的超參數優化、帶隨機林的樹集合、額外樹和隨機樹嵌入,以及一對一和一對多的多分類策略。
分布預測——具有 Spark 數據幀的擬合 scikit-learn 估計器的預測方法。這使得帶有 scikit-learn 的大規模分布式預測可以在沒有 Spark 的情況下進行。
特征編碼——分布特征編碼使用被稱為編碼器的靈活特征變換器來完成。不管有沒有 Spark,它都可以起作用。它將推斷數據類型,自動應用默認的特征變換器作為標準特征編碼技術的最佳實現。它還可以作為一個完全可定制的功能聯合,如編碼器,它的附加優勢是與 Spark 匹配的分布式 transformer。
用例
以下是判斷 sk-dist 是否適合解決你的機器學習問題的一些準則:傳統的機器學習方法,如廣義線性模型、隨機梯度下降、最近鄰、決策樹和樸素貝葉斯等,都能很好地應用于 sk-dist,這些方法都可以在 scikit-learn 中實現,并且可以直接應用于 sk-dist 元估計。
中小型數據、大數據不能很好地在 sk-dist 中起作用。記住,分布式訓練的維度是沿著模型的軸,而不是數據。數據不僅需要放在每個執行器的內存中,而且要小到可以傳播。根據 Spark 配置,最大傳播大小可能會受到限制。
Spark 定向和訪問——sk-dist 的核心功能需要運行 Spark。對于個人或小型數據科學團隊來說,這并不總是可行的。
這里一個重要的注意事項是,雖然神經網絡和深度學習在技術上可以用于 sk-dist,但這些技術需要大量的訓練數據,有時需要專門的基礎設施才能有效。深度學習不是 sk-dist 的最佳用例,因為它違反了上面的(1)和(2)。
開始
要開始使用 sk-dist,請查看安裝指南。代碼庫還包含一個示例庫,用于說明 sk-dist 的一些用例。歡迎所有人提交問題并為項目做出貢獻。
雷鋒網(公眾號:雷鋒網)雷鋒網雷鋒網
雷鋒網版權文章,未經授權禁止轉載。詳情見轉載須知。