如何从Java调用scikit-learn分类器?

问题描述 投票:28回答:6

我有一个使用Python的scikit-learn训练的分类器。如何使用Java程序中的分类器?我可以使用Jython吗?有没有办法在Python中保存分类器并在Java中加载它?还有其他方法可以使用它吗?

java python jython scikit-learn
6个回答
45
投票

你不能使用jython作为scikit-learn很大程度上依赖于numpy和scipy,它们有许多已编译的C和Fortran扩展,因此无法在jython中运行。

在java环境中使用scikit-learn的最简单方法是:

  • 将分类器公开为HTTP / Json服务,例如使用flaskbottlecornice等微框架,并使用HTTP客户端库从java调用它
  • 在python中编写一个命令行包装器应用程序,它读取stdin上的数据并使用某种格式(如CSV或JSON(或某些低级二进制表示))在stdout上输出预测,并使用Apache Commons Exec从java调用python程序。
  • 使python程序输出在拟合时学习的原始数值参数(通常作为浮点值数组)并重新实现java中的预测函数(这通常很容易用于预测线性模型,其中预测通常只是一个阈值点积) 。

如果您还需要在Java中重新实现特征提取,那么最后一种方法将会有更多工作。

最后,您可以使用诸如Weka或Mahout之类的Java库来实现您需要的算法,而不是尝试使用scikit-learn from Java。


16
投票

为此目的有JPMML项目。

首先,您可以直接从python使用sklearn2pmml库将scikit-learn模型序列化为PMML(内部为XML),或者首先将其转储到python中,然后使用java中的jpmml-sklearn或此库提供的命令行进行转换。接下来,您可以在Java代码中使用jpmml-evaluator加载pmml文件,反序列化并执行加载的模型。

这种方式不适用于所有scikit-learn模型,但使用many


4
投票

你可以使用一个搬运工,我已经测试了sklearn-porter(https://github.com/nok/sklearn-porter),它适用于Java。

我的代码如下:

import pandas as pd
from sklearn import tree
from sklearn_porter import Porter

train_dataset = pd.read_csv('./result2.csv').as_matrix()

X_train = train_dataset[:90, :8]
Y_train = train_dataset[:90, 8:]

X_test = train_dataset[90:, :8]
Y_test = train_dataset[90:, 8:]

print X_train.shape
print Y_train.shape


clf = tree.DecisionTreeClassifier()
clf = clf.fit(X_train, Y_train)

porter = Porter(clf, language='java')
output = porter.export(embed_data=True)
print(output)

就我而言,我正在使用DecisionTreeClassifier和输出

打印(输出)

是以下代码作为控制台中的文本:

class DecisionTreeClassifier {

  private static int findMax(int[] nums) {
    int index = 0;
    for (int i = 0; i < nums.length; i++) {
        index = nums[i] > nums[index] ? i : index;
    }
    return index;
  }


  public static int predict(double[] features) {
    int[] classes = new int[2];

    if (features[5] <= 51.5) {
        if (features[6] <= 21.0) {

            // HUGE amount of ifs..........

        }
    }

    return findMax(classes);
  }

  public static void main(String[] args) {
    if (args.length == 8) {

        // Features:
        double[] features = new double[args.length];
        for (int i = 0, l = args.length; i < l; i++) {
            features[i] = Double.parseDouble(args[i]);
        }

        // Prediction:
        int prediction = DecisionTreeClassifier.predict(features);
        System.out.println(prediction);

    }
  }
}

2
投票

以下是JPMML解决方案的一些代码:

--PYTHON PART--

# helper function to determine the string columns which have to be one-hot-encoded in order to apply an estimator.
def determine_categorical_columns(df):
    categorical_columns = []
    x = 0
    for col in df.dtypes:
        if col == 'object':
            val = df[df.columns[x]].iloc[0]
            if not isinstance(val,Decimal):
                categorical_columns.append(df.columns[x])
        x += 1
    return categorical_columns

categorical_columns = determine_categorical_columns(df)
other_columns = list(set(df.columns).difference(categorical_columns))


#construction of transformators for our example
labelBinarizers = [(d, LabelBinarizer()) for d in categorical_columns]
nones = [(d, None) for d in other_columns]
transformators = labelBinarizers+nones

mapper = DataFrameMapper(transformators,df_out=True)
gbc = GradientBoostingClassifier()

#construction of the pipeline
lm = PMMLPipeline([
    ("mapper", mapper),
    ("estimator", gbc)
])

--JAVA PART -

//Initialisation.
String pmmlFile = "ScikitLearnNew.pmml";
PMML pmml = org.jpmml.model.PMMLUtil.unmarshal(new FileInputStream(pmmlFile));
ModelEvaluatorFactory modelEvaluatorFactory = ModelEvaluatorFactory.newInstance();
MiningModelEvaluator evaluator = (MiningModelEvaluator) modelEvaluatorFactory.newModelEvaluator(pmml);

//Determine which features are required as input
HashMap<String, Field>() inputFieldMap = new HashMap<String, Field>();
for (int i = 0; i < evaluator.getInputFields().size();i++) {
  InputField curInputField = evaluator.getInputFields().get(i);
  String fieldName = curInputField.getName().getValue();
  inputFieldMap.put(fieldName.toLowerCase(),curInputField.getField());
}


//prediction

HashMap<String,String> argsMap = new HashMap<String,String>();
//... fill argsMap with input

Map<FieldName, ?> res;
// here we keep only features that are required by the model
Map<FieldName,String> args = new HashMap<FieldName, String>();
Iterator<String> iter = argsMap.keySet().iterator();
while (iter.hasNext()) {
  String key = iter.next();
  Field f = inputFieldMap.get(key);
  if (f != null) {
    FieldName name =f.getName();
    String value = argsMap.get(key);
    args.put(name, value);
  }
}
//the model is applied to input, a probability distribution is obtained
res = evaluator.evaluate(args);
SegmentResult segmentResult = (SegmentResult) res;
Object targetValue = segmentResult.getTargetValue();
ProbabilityDistribution probabilityDistribution = (ProbabilityDistribution) targetValue;

1
投票

我发现自己处于类似的情况。我建议雕刻出一个分类器微服务。您可以拥有一个在python中运行的分类器微服务,然后通过某些RESTFul API公开对该服务的调用,从而产生JSON / XML数据交换格式。我认为这是一种更清洁的方法。


0
投票

或者,您可以从训练有素的模型生成Python代码。这是一个可以帮助你https://github.com/BayesWitnesses/m2cgen的工具

© www.soinside.com 2019 - 2024. All rights reserved.