模型轉程式碼:XGBoost等模型也能快速轉純C或Java程式碼
你是否常訓練炫酷的機器學習模型,用來分析資料或執行某些新奇的任務?你是否發現你的模型只能在一定開發環境上才能跑起來,很難部署也很難加入其它程式?今天我們將介紹一個炫酷的工具,它可以把構建在 scikit-learn 或XGBoost等庫上的 ML 模型直接轉化為不需要任何依賴項的 Java/Python/C 原始碼。
-
專案地址:https://github.com/BayesWitnesses/m2cgen/
那麼轉化為 Java/Python/C 原始碼有什麼用呢?想象一下如果我們使用 ML 框架(scikit-learn\XGBoost\LightGBM)訓練了一個模型,現在我們希望把這個模型做成應用或嵌入到已有的模型中,那麼我們肯定需要考慮這些問題:
-
如果產品環境並沒有 Python 執行時怎麼辦?
-
如果產品不能通過雲伺服器進行計算,只能在本地進行怎麼辦?
-
ML 模型的推斷速度太慢又怎麼辦?
這些問題都很難解決,也是開發者在做自己專案時常遇到的問題。如果我們能將用 Python 和 ML 庫構建的模型轉換一下,變成純 Java 或 C 寫的程式碼,且這些程式碼不會依賴各種庫,那麼部署或嵌入不就簡單了麼。在 m2cgen 這個專案中,它就可以將 ML 模型轉化為不帶有依賴項的純程式碼。
m2cgen(Model 2 Code Generator)是一個輕量級的 Python 庫,它能快速便捷地將已訓練統計模型轉化為 Python、C 和 Java 程式碼。目前 m2cgen 已經支援各種分類模型與迴歸模型,包括 支援向量機 、 決策樹 、 隨機森林 和梯度提升樹等,注意這些都是統計機器學習模型,深度神經網路還是老老實實使用 DL 框架吧。
模型轉換效果
我們可以通過幾個簡單案例瞭解 m2cgen 是如何轉換為純程式碼的,簡單而言即把模型架構和權重顯化了。如下所示如果我們簡單地訓練一個線性迴歸模型,當然程式碼看著簡單是因為我們直接呼叫了 scikit-learn 庫中的模型。
from sklearn.datasets import load_boston from sklearn import linear_model import m2cgen as m2c boston = load_boston() X, y = boston.data, boston.target estimator = linear_model.LinearRegression() estimator.fit(X, y) code = m2c.export_to_java(estimator)
上面最後一行將 scikit-learn 中的線性迴歸模型轉化為 Java 程式碼,注意這個模型已經擬合了訓練資料,或者說已經完成了訓練。轉化後的程式碼如下所示:
public class Model { public static double score(double[] input) { return (((((((((((((36.45948838508965) + ((input[0]) * (-0.10801135783679647))) + ((input[1]) * (0.04642045836688297))) + ((input[2]) * (0.02055862636707<mark data-type="institutions" data-id="e6402476-9c73-4340-b0c8-2b470157ce64">360</mark>8))) + ((input[3]) * (2.6867338193449406))) + ((input[4]) * (-17.76661122830004))) + ((input[5]) * (3.8098652068092163))) + ((input[6]) * (0.0006922246403454562))) + ((input[7]) * (-1.475566845600257))) + ((input[8]) * (0.30604947898516943))) + ((input[9]) * (-0.012334593916574394))) + ((input[10]) * (-0.9527472317072884))) + ((input[11]) * (0.009311683273794044))) + ((input[12]) * (-0.5247583778554867)); } }
如上 return 後面的語句,它就是一個線性迴歸的表示式,每一個 input[ * ] 都是一種特徵,它後面的數值就是訓練後的權重。所以整個線性模型有 13 個特徵及對應權重,以及另外一個偏置項。
我們還可以找到更多的案例,如果我們用XGBoost訓練一個簡單的分類模型,我們可以看到轉化的程式碼會大量使用 if-else 大法,不過我們本身也不用維護生成的程式碼,所以這種結構也沒什麼關係了。
import numpy as np def score(input): if (input[2]) >= (2.5999999): var0 = -0.0731707439 else: var0 = 0.142857149 if (input[2]) >= (2.5999999): var1 = -0.0705206916 else: var1 = 0.12477719 var2 = np.exp(((0.5) + (var0)) + (var1)) if (input[2]) >= (2.5999999): if (input[2]) >= (4.85000038): var3 = -0.0578680299 else: var3 = 0.132596686 else: var3 = -0.0714285821 if (input[2]) >= (2.5999999): if (input[2]) >= (4.85000038): var4 = -0.0552999191 else: var4 = 0.116139404 else: var4 = -0.0687687024 var5 = np.exp(((0.5) + (var3)) + (var4)) if (input[2]) >= (4.85000038): if (input[3]) >= (1.75): var6 = 0.142011836 else: var6 = 0.0405405387 else: if (input[3]) >= (1.6500001): var6 = 0.0428571403 else: var6 = -0.0730659068 if (input[2]) >= (4.85000038): if (input[3]) >= (1.75): var7 = 0.124653712 else: var7 = 0.035562478 else: if (input[3]) >= (1.6500001): var7 = 0.0425687581 else: var7 = -0.0704230517 var8 = np.exp(((0.5) + (var6)) + (var7)) var9 = ((var2) + (var5)) + (var8) return np.asarray([(var2) / (var9), (var5) / (var9), (var8) / (var9)])
不過上面這種程式碼也非常合理,本身決策樹就可以視為一種 if-else 的規則集合,不同輸入特徵 input[ * ] 滿足不同的條件就能得到不同的值,這些值最後能聯合計算分類結果。
專案細節
工具的安裝很簡單,直接用 pip 就行了:
pip install m2cgen
除了前面那樣在程式碼中呼叫轉換工具,我們還能通過命令列使用序列化的模型目標(pickle protocol)生成程式碼:
$ m2cgen <pickle_file> --language <language> [--indent <indent>] [--class_name <class_name>] [--package_name <package_name>] [--recursion-limit <recursion_limit>]
目前專案支援以下分類和迴歸模型的轉換:
分類模型輸出結果: