Comment puis-je appeler les classificateurs scikit-learn de Java?

j'ai un classificateur que J'ai formé en utilisant scikit-learn de Python. Comment utiliser le classificateur D'un programme Java? Je peux utiliser Jython? Est-il un moyen de sauver le classificateur en Python et de le charger dans Java? Est-il une autre façon de l'utiliser?

25
demandé sur Thomas Johnson 2012-10-05 06:50:26

5 réponses

vous ne pouvez pas utiliser jython comme scikit-learn dépend fortement de numpy et scipy qui ont beaucoup d'extensions compilées C et Fortran donc ne peut pas fonctionner en jython.

Le plus simple des façons d'utiliser scikit-learn dans un environnement java:

  • exposer le classificateur en tant que service HTTP / Json, par exemple en utilisant un microframme tel que fiole ou bouteille ou corniche et l'appeler depuis java en utilisant un client HTTP bibliothèque

  • écrire une application de wrapper en ligne de commande en python qui lit les données sur stdin et les prédictions de sortie sur stdout en utilisant un format tel que CSV ou JSON (ou une représentation binaire de niveau inférieur) et appeler le programme python de java par exemple en utilisant Apache Commons Exec.

  • faire de la sortie du programme python les paramètres numériques bruts appris au moment de l'ajustement (généralement sous forme de tableau de valeurs flottantes) et ré-implémenter la fonction de prédiction en java (ce qui est généralement facile pour les modèles linéaires prédictifs où la prédiction est souvent juste un produit de point battu).

La dernière approche sera beaucoup plus de travail si vous avez besoin de ré-implémenter la fonctionnalité d'extraction en Java.

enfin vous pouvez utiliser une bibliothèque Java comme Weka ou Mahout qui implémente les algorithmes dont vous avez besoin au lieu d'essayer d'utiliser scikit-learn à partir de Java.

37
répondu ogrisel 2012-10-05 09:05:29

Il y a JPMML projet à cet effet.

tout d'abord, vous pouvez sérialiser le modèle scikit-learn en PMML (qui est XML en interne) en utilisant sklearn2pmml bibliothèque directement à partir de python ou le jeter dans python en premier et convertir en utilisant jpmml-sklearn en java ou à partir d'une ligne de commande fournie par cette bibliothèque. Ensuite, vous pouvez charger le fichier pmml, désérialiser et exécuter le modèle chargé en utilisant jpmml-évaluateur dans votre Java code.

cette méthode ne fonctionne pas avec tous les modèles scikit-learn, mais avec beaucoup.

14
répondu Dmitry Spikhalskiy 2016-08-10 16:39:01

Vous pouvez utiliser un porteur, j'ai testé le sklearn-porter (https://github.com/nok/sklearn-porter), et cela fonctionne bien pour Java.

Mon code est le suivant:

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)

dans mon cas, j'utilise un classificateur Decisiontre, et la sortie de

print(sortie)

est le code suivant en tant que texte dans la console:

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);

    }
  }
}
3
répondu gustavoresque 2018-03-04 19:16:07

voici un peu de code pour la solution 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
répondu Volokh 2018-02-16 13:05:25

je me suis trouvé dans une situation similaire. Je vous recommande de découper un microservice plus classe. Vous pouvez avoir un microservice de classificateur qui tourne en python puis expose les appels à ce service sur une API RESTFul donnant un format D'échange de données JSON/XML. Je pense que c'est plus propre approche.

0
répondu Josh Uzo 2018-05-11 12:50:16