Python訓(xùn)練了個(gè)模型,怎么交給Java用呢?
最近碰到幾個(gè)人問(wèn),如何實(shí)現(xiàn) java 調(diào)用他們寫(xiě)好的 Python 應(yīng)用(模型)
這里我就把幾種常見(jiàn)的辦法做下匯總整理。
1. 通過(guò)命令行調(diào)用
如使用 java 的 ProcessBuilder API,
#hello.py
print("Hello?ProcessBuilder!")
import?java.util.stream.Collectors;
import?java.util.List;
import?java.io.InputStream;
import?java.io.InputStreamReader;
import?java.io.InputStream;
import?java.io.BufferedReader;
import?java.io.IOException;
public?class?PyEx0{
????private?static?List?readOutput(InputStream?inputStream)?throws?IOException? {
????????try?(BufferedReader?output?=?new?BufferedReader(new?InputStreamReader(inputStream)))?{
????????????return?output.lines()
????????????????.collect(Collectors.toList());
????????}
????}
????public?static?void?main(String[]?args)?throws?Exception?{
????????ProcessBuilder?processBuilder?=?new?ProcessBuilder("python",?"hello.py");
????????processBuilder.redirectErrorStream(true);
????????Process?process?=?processBuilder.start();
????????List?results?=?readOutput(process.getInputStream());
????????System.out.println(results);
????????int?exitCode?=?process.waitFor();
????????System.out.println(exitCode);
????}
}
2. 通過(guò) REST API

REST 是表現(xiàn)層狀態(tài)轉(zhuǎn)換(英語(yǔ):Representational State Transfer)的英文縮寫(xiě),是 Roy Thomas Fielding 博士于 2000 年在他的博士論文中提出來(lái)的一種萬(wàn)維網(wǎng)軟件架構(gòu)風(fēng)格,目的是便于不同軟件/程序在網(wǎng)絡(luò)(例如互聯(lián)網(wǎng))中互相傳遞信息。表現(xiàn)層狀態(tài)轉(zhuǎn)換是根基于超文本傳輸協(xié)議(HTTP)之上而確定的一組約束和屬性,是一種設(shè)計(jì)提供萬(wàn)維網(wǎng)絡(luò)服務(wù)的軟件構(gòu)建風(fēng)格。符合或兼容于這種架構(gòu)風(fēng)格(簡(jiǎn)稱為 REST 或 RESTful)的網(wǎng)絡(luò)服務(wù),允許客戶端發(fā)出以統(tǒng)一資源標(biāo)識(shí)符訪問(wèn)和操作網(wǎng)絡(luò)資源的請(qǐng)求,而與預(yù)先定義好的無(wú)狀態(tài)操作集一致化。因此表現(xiàn)層狀態(tài)轉(zhuǎn)換提供了在互聯(lián)網(wǎng)絡(luò)的計(jì)算系統(tǒng)之間,彼此資源可交互使用的協(xié)作性質(zhì)(interoperability)。相對(duì)于其它種類(lèi)的網(wǎng)絡(luò)服務(wù),例如 SOAP 服務(wù),則是以本身所定義的操作集,來(lái)訪問(wèn)網(wǎng)絡(luò)上的資源。
目前在三種主流的 Web 服務(wù)實(shí)現(xiàn)方案中,因?yàn)?REST 模式與復(fù)雜的 SOAP 和 XML-RPC 相比更加簡(jiǎn)潔,越來(lái)越多的 Web 服務(wù)開(kāi)始采用 REST 風(fēng)格設(shè)計(jì)和實(shí)現(xiàn)。例如,Amazon.com 提供接近 REST 風(fēng)格的 Web 服務(wù)執(zhí)行圖書(shū)查詢;雅虎提供的 Web 服務(wù)也是 REST 風(fēng)格的。
使用你熟悉的 Python 開(kāi)發(fā)框架構(gòu)建 Restful Server, 我之前介紹過(guò)一個(gè)專門(mén)用于部署機(jī)器學(xué)習(xí)框架的,詳見(jiàn)
100天搞定機(jī)器學(xué)習(xí) 番外:使用FastAPI構(gòu)建機(jī)器學(xué)習(xí)API
有興趣的同學(xué)也可以嘗試下試下比較流行的 GraphQL,網(wǎng)上關(guān)于基于 GraphQL 開(kāi)發(fā)模型 API 的文章也不少。
3. GraphQL
GraphQL 是一個(gè)開(kāi)源的,面向 API 而創(chuàng)造出來(lái)的數(shù)據(jù)查詢操作語(yǔ)言以及相應(yīng)的運(yùn)行環(huán)境。于 2012 年仍處于 Facebook 內(nèi)部開(kāi)發(fā)階段,直到 2015 年才公開(kāi)發(fā)布。2018 年 11 月 7 日,F(xiàn)acebook 將 GraphQL 項(xiàng)目轉(zhuǎn)移到新成立的 GraphQL 基金會(huì)(隸屬于非營(yíng)利性的 Linux 基金會(huì))。
GraphQL 相較于 REST 以及其他 web service 架構(gòu)提供了一種更加高效、強(qiáng)大和靈活的開(kāi)發(fā) web APIs 的方式。它通過(guò)由客戶端根據(jù)所需定義數(shù)據(jù)結(jié)構(gòu),同時(shí)由服務(wù)端負(fù)責(zé)返回相同數(shù)據(jù)結(jié)構(gòu)的對(duì)應(yīng)數(shù)據(jù)的方式避免了服務(wù)端大量冗余數(shù)據(jù)的返回,但與此同時(shí)也意味著這種方式不能有效利用起查詢結(jié)果的 web 緩存。GraphQL 這種查詢語(yǔ)言所帶來(lái)的靈活性和豐富性的同時(shí)也增加了復(fù)雜性,導(dǎo)致簡(jiǎn)單的 APIs 有可能并不適合這種方式。
GraphQL 支持?jǐn)?shù)據(jù)讀取、寫(xiě)入(操作)和數(shù)據(jù)變更訂閱(實(shí)時(shí)更新)。
主要的 GraphQL 客戶端有 Apollo Clien 和 Relay. GraphQL 的服務(wù)端在多個(gè)語(yǔ)言都有實(shí)現(xiàn)包括 Haskell, JavaScript, Python,Ruby, Java, C#, Scala, Go, Elixir, Erlang, PHP, R,和 Clojure.
2018 年 2 月 9 日 GraphQL 的部分模式定義語(yǔ)言(SDL)規(guī)范制定完成。

4. PMML
PMML(Predictive Model Markup Language)是預(yù)測(cè)模型的通用描述語(yǔ)言,簡(jiǎn)單的說(shuō)就是用 XML 語(yǔ)法保存模型的一種標(biāo)準(zhǔn)規(guī)范,按照這個(gè)格式保存模型,其他編程語(yǔ)言也可以加載模型完成預(yù)測(cè)。
更多 PMML 信息,可以訪問(wèn) https://github.com/jpmml
5. m2cgen
m2cgen[1], 模型轉(zhuǎn)代碼生成器,是一個(gè)輕量級(jí)庫(kù),它提供了一種將經(jīng)過(guò)訓(xùn)練的統(tǒng)計(jì)模型轉(zhuǎn)換為本機(jī)代碼(Python、C、Java、Go、JavaScript、Visual Basic、C#、PowerShell、R、PHP、Dart、 Haskell、Ruby、F#、Rust)。
安裝
pip?install?m2cgen
支持模型

使用方法 將訓(xùn)練好的模型轉(zhuǎn)為 Java code,只需一行代碼(最后一行)
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)
生成一個(gè) Java 類(lèi)
public?class?Model?{
????public?static?double?score(double[]?input)?{
????????return?(((((((((((((36.45948838508965)
?+?((input[0])?*?(-0.10801135783679647)))
?+?((input[1])?*?(0.04642045836688297)))
?+?((input[2])?*?(0.020558626367073608)))
?+?((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));
????}
}
6. Jython

Jython(原 JPython),是一個(gè)用 Java 語(yǔ)言寫(xiě)的 Python 解釋器。
Jython 程序可以和 Java 無(wú)縫集成。除了一些標(biāo)準(zhǔn)模塊,Jython 使用 Java 的模塊。Jython 幾乎擁有標(biāo)準(zhǔn)的 Python 中不依賴于 C 語(yǔ)言的全部模塊。比如,Jython 的用戶界面將使用 Swing,AWT 或者 SWT。Jython 可以被動(dòng)態(tài)或靜態(tài)地編譯成 Java 字節(jié)碼。
Jython 還包括 jythonc,一個(gè)將 Python 代碼轉(zhuǎn)換成 Java 代碼的編譯器。這意味著 Python 程序員能夠?qū)⒆约河?Python 代碼寫(xiě)的類(lèi)庫(kù)用在 Java 程序里。
這東西是不錯(cuò),但有個(gè)致命的問(wèn)題,不支持 Python3,除非你仍然在使用 Python2,否則沒(méi)有啥用。
7. GraalVM Python Runtime

GraalVM 提供了一個(gè)兼容 Python 3.8 的運(yùn)行時(shí)。GraalVM Python 運(yùn)行時(shí)的主要目標(biāo)是支持 SciPy 及其組成庫(kù),以及與來(lái)自豐富 Python 生態(tài)系統(tǒng)的其他數(shù)據(jù)科學(xué)和機(jī)器學(xué)習(xí)庫(kù)一起使用。
官方的數(shù)據(jù)說(shuō):graalpython(企業(yè)版) 比 CPython 快 8.92 倍,比 Jython 快 8.34 倍。

我裝的是社區(qū)版,沒(méi)有做過(guò)對(duì)比測(cè)試。
以后我會(huì)另外寫(xiě)文章介紹 GraalVM,今天就簡(jiǎn)單介紹下如何寫(xiě)一個(gè)簡(jiǎn)單的 Python 程序。
安裝 GraalVM
目前 GraalVM 提供了 四種平臺(tái)的預(yù)編譯版本(對(duì)應(yīng)的 Java 版本有 11 和 17),以及 Docker 的支持。
Linux
Linux AArch64
macOS
Windows
Docker Container
平臺(tái)的安裝方式都一樣,下載解壓縮,以 macOS java17 為例,
wget?https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.3.0/graalvm-ce-java17-darwin-amd64-21.3.0.tar.gz
tar?xf?graalvm-ce-java17-darwin-amd64-21.3.0.tar.gz
sudo?mv?graalvm-ce-java17-21.3.0?/Library/Java/JavaVirtualMachines/
sudo?xattr?-r?-d?com.apple.quarantine?/Library/Java/JavaVirtualMachines/graalvm-ce-java17-21.3.0/Contents/Home
驗(yàn)證下 所安裝的 java 版本和默認(rèn)的 java 版本,
$?/usr/libexec/java_home?-V
Matching?Java?Virtual?Machines?(3):
????17.0.1?(x86_64)?"GraalVM?Community"?-?"GraalVM?CE?21.3.0"?/Library/Java/JavaVirtualMachines/graalvm-ce-java17-21.3.0/Contents/Home
????1.8.311.11?(x86_64)?"Oracle?Corporation"?-?"Java"?/Library/Internet?Plug-Ins/JavaAppletPlugin.plugin/Contents/Home
????1.8.0_31?(x86_64)?"Oracle?Corporation"?-?"Java?SE?8"?/Library/Java/JavaVirtualMachines/jdk1.8.0_31.jdk/Contents/Home
/Library/Java/JavaVirtualMachines/graalvm-ce-java17-21.3.0/Contents/Home
安裝 Python
$?gu?install?python
$?gu?install?llvm-toolchain
$?graalpython?--version
Python?3.8.5?(GraalVM?CE?Native?21.3.0)
創(chuàng)建虛擬環(huán)境以及安裝包
$?graalpython?-m?venv?.venv
$?source?.venv/bin/activate
#下面這個(gè)編譯過(guò)程有些長(zhǎng)
$?graalpython?-m?ginstall?install?pandas
$?pip?list
Package?????????Version
---------------?-------
hpy?????????????0.0.3
numpy???????????1.16.4
pandas??????????0.25.0
pip?????????????20.1.1
polyglot????????16.7.4
python-dateutil?2.7.5
pytz????????????2018.7
setuptools??????47.1.0
setuptools-scm??1.15.0
six?????????????1.12.0
pandas 版本有些低 0.25.0。
例子
python 代碼
#hello.py
import?pandas?as?pd
def?get_data():
????d?=?{'col1':?[1,?2],?'col2':?[3,?4]}
????df?=?pd.DataFrame(data=d)
????return?df
get_data()
java 代碼
import?java.io.FileNotFoundException;
import?java.io.FileInputStream;
import?java.io.IOException;
import?java.io.InputStream;
import?java.io.InputStreamReader;
import?java.nio.file.Paths;
import?org.graalvm.polyglot.Context;
import?org.graalvm.polyglot.Source;
import?org.graalvm.polyglot.Value;
public?class?Pandas?{
????private?static?String?PYTHON?=?"python";
????private?static?String?VENV_EXECUTABLE?=Paths.get(".venv",?"bin",?"graalpython").toString();
????private?static?String?SOURCE_FILE_NAME?=?"ex.py";
????public?static?void?main(String[]?args)?throws?Exception?{
????????Context?context?=?Context.newBuilder(PYTHON).
????????????allowAllAccess(true).
????????????option("python.Executable",?VENV_EXECUTABLE).
????????????option("python.ForceImportSite",?"true").
????????????build();
????????try?{
????????????FileInputStream?input?=?new?FileInputStream(SOURCE_FILE_NAME);
????????????InputStreamReader?code?=?new?InputStreamReader(input);
????????????Source?source?=?Source.newBuilder(PYTHON,?code,?SOURCE_FILE_NAME).build();
?????Value?value?=?context.eval(source);
????????????System.out.println(value);
????????}?catch?(IOException?e)?{
????????????throw?new?RuntimeException(e);
????????}
????}
}
該方案還不夠成熟。
參考資料
m2cgen: https://github.com/BayesWitnesses/m2cgen
三連在看,月入百萬(wàn)??
