You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ch...@apache.org on 2019/01/11 14:33:44 UTC

[ignite] branch master updated: IGNITE-10810: [ML] Import models from MLeap

This is an automated email from the ASF dual-hosted git repository.

chief pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new d4fce67  IGNITE-10810: [ML] Import models from MLeap
d4fce67 is described below

commit d4fce67be1daf4d19f1cf35cdc613cdbe50c7a1a
Author: dmitrievanthony <dm...@gmail.com>
AuthorDate: Fri Jan 11 17:33:26 2019 +0300

    IGNITE-10810: [ML] Import models from MLeap
    
    This closes #5753
---
 examples/pom.xml                                   |   6 +
 .../resources/models/mleap/airbnb.model.rf.zip     | Bin 0 -> 35932 bytes
 .../examples/ml/mleap/MLeapModelParserExample.java |  73 ++++++++++++
 .../ignite/examples/ml/mleap/package-info.java     |  22 ++++
 modules/ml/mleap-model-parser/pom.xml              |  51 +++++++++
 .../org/apache/ignite/ml/mleap/MLeapModel.java     | 122 ++++++++++++++++++++
 .../apache/ignite/ml/mleap/MLeapModelParser.java   | 123 +++++++++++++++++++++
 .../org/apache/ignite/ml/mleap/package-info.java   |  22 ++++
 .../ignite/ml/mleap/IgniteMLeapTestSuite.java      |  30 +++++
 .../ignite/ml/mleap/MLeapModelParserTest.java      |  68 ++++++++++++
 .../test/resources/datasets/scikit-airbnb.rf.zip   | Bin 0 -> 216734 bytes
 pom.xml                                            |   2 +
 12 files changed, 519 insertions(+)

diff --git a/examples/pom.xml b/examples/pom.xml
index 6320a0f..252c972 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -191,6 +191,12 @@
                     <artifactId>ignite-spark</artifactId>
                     <version>${project.version}</version>
                 </dependency>
+
+                <dependency>
+                    <groupId>org.apache.ignite</groupId>
+                    <artifactId>ignite-ml-mleap-model-parser</artifactId>
+                    <version>${project.version}</version>
+                </dependency>
             </dependencies>
 
             <build>
diff --git a/examples/src/main/resources/models/mleap/airbnb.model.rf.zip b/examples/src/main/resources/models/mleap/airbnb.model.rf.zip
new file mode 100644
index 0000000..0da815c
Binary files /dev/null and b/examples/src/main/resources/models/mleap/airbnb.model.rf.zip differ
diff --git a/examples/src/main/spark/org/apache/ignite/examples/ml/mleap/MLeapModelParserExample.java b/examples/src/main/spark/org/apache/ignite/examples/ml/mleap/MLeapModelParserExample.java
new file mode 100644
index 0000000..79958dd
--- /dev/null
+++ b/examples/src/main/spark/org/apache/ignite/examples/ml/mleap/MLeapModelParserExample.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.examples.ml.mleap;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.ml.inference.Model;
+import org.apache.ignite.ml.inference.builder.AsyncModelBuilder;
+import org.apache.ignite.ml.inference.builder.IgniteDistributedModelBuilder;
+import org.apache.ignite.ml.inference.reader.FileSystemModelReader;
+import org.apache.ignite.ml.inference.reader.ModelReader;
+import org.apache.ignite.ml.mleap.MLeapModelParser;
+
+/**
+ * This example demonstrates how to import MLeap model and use imported model for distributed inference in Apache
+ * Ignite.
+ */
+public class MLeapModelParserExample {
+    /** Test model resource name. */
+    private static final String TEST_MODEL_RES = "examples/src/main/resources/models/mleap/airbnb.model.rf.zip";
+
+    /** Parser. */
+    private static final MLeapModelParser parser = new MLeapModelParser();
+
+    /** Run example. */
+    public static void main(String... args) throws ExecutionException, InterruptedException {
+        try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) {
+            File mdlRsrc = IgniteUtils.resolveIgnitePath(TEST_MODEL_RES);
+            if (mdlRsrc == null)
+                throw new IllegalArgumentException("File not found [resource_path=" + TEST_MODEL_RES + "]");
+
+            ModelReader reader = new FileSystemModelReader(mdlRsrc.getPath());
+
+            AsyncModelBuilder mdlBuilder = new IgniteDistributedModelBuilder(ignite, 4, 4);
+
+            try (Model<HashMap<String, Double>, Future<Double>> mdl = mdlBuilder.build(reader, parser)) {
+                HashMap<String, Double> input = new HashMap<>();
+                input.put("bathrooms", 1.0);
+                input.put("bedrooms", 1.0);
+                input.put("security_deposit", 1.0);
+                input.put("cleaning_fee", 1.0);
+                input.put("extra_people", 1.0);
+                input.put("number_of_reviews", 1.0);
+                input.put("square_feet", 1.0);
+                input.put("review_scores_rating", 1.0);
+
+                Future<Double> prediction = mdl.predict(input);
+
+                System.out.println("Predicted price: " + prediction.get());
+            }
+        }
+    }
+}
diff --git a/examples/src/main/spark/org/apache/ignite/examples/ml/mleap/package-info.java b/examples/src/main/spark/org/apache/ignite/examples/ml/mleap/package-info.java
new file mode 100644
index 0000000..698846c
--- /dev/null
+++ b/examples/src/main/spark/org/apache/ignite/examples/ml/mleap/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * <!-- Package description. -->
+ * MLeap model inference examples.
+ */
+package org.apache.ignite.examples.ml.mleap;
\ No newline at end of file
diff --git a/modules/ml/mleap-model-parser/pom.xml b/modules/ml/mleap-model-parser/pom.xml
new file mode 100644
index 0000000..c57ee8b
--- /dev/null
+++ b/modules/ml/mleap-model-parser/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!--
+    POM file.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>ignite-parent</artifactId>
+        <groupId>org.apache.ignite</groupId>
+        <version>1</version>
+        <relativePath>../../../parent</relativePath>
+    </parent>
+
+    <artifactId>ignite-ml-mleap-model-parser</artifactId>
+    <version>2.8.0-SNAPSHOT</version>
+    <url>http://ignite.apache.org</url>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-ml</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>ml.combust.mleap</groupId>
+            <artifactId>mleap-runtime_2.11</artifactId>
+            <version>0.13.0</version>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/modules/ml/mleap-model-parser/src/main/java/org/apache/ignite/ml/mleap/MLeapModel.java b/modules/ml/mleap-model-parser/src/main/java/org/apache/ignite/ml/mleap/MLeapModel.java
new file mode 100644
index 0000000..2ebd8c0
--- /dev/null
+++ b/modules/ml/mleap-model-parser/src/main/java/org/apache/ignite/ml/mleap/MLeapModel.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.ml.mleap;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import ml.combust.mleap.core.types.ScalarType;
+import ml.combust.mleap.core.types.StructField;
+import ml.combust.mleap.core.types.StructType;
+import ml.combust.mleap.runtime.frame.DefaultLeapFrame;
+import ml.combust.mleap.runtime.frame.Row;
+import ml.combust.mleap.runtime.frame.Transformer;
+import ml.combust.mleap.runtime.javadsl.LeapFrameBuilder;
+import org.apache.ignite.ml.inference.Model;
+import scala.collection.immutable.Set;
+import scala.collection.immutable.Stream;
+import scala.util.Try;
+
+/**
+ * MLeap model imported and wrapped to be compatible with Apache Ignite infrastructure.
+ */
+public class MLeapModel implements Model<HashMap<String, Double>, Double> {
+    /** MLeap model (transformer in terms of MLeap). */
+    private final Transformer transformer;
+
+    /** List of field names. */
+    private final List<String> schema;
+
+    /** Output field name. */
+    private final String outputFieldName;
+
+    /**
+     * Constructs a new instance of MLeap model.
+     *
+     * @param transformer MLpeap model (transformer in terms of MLeap).
+     * @param schema List of field names.
+     * @param outputFieldName Output field name.
+     */
+    public MLeapModel(Transformer transformer, List<String> schema, String outputFieldName) {
+        this.transformer = transformer;
+        this.schema = schema;
+        this.outputFieldName = outputFieldName;
+    }
+
+    // TODO: IGNITE-10834 Add NamedVectors to replace HashMap in Model.
+    /** {@inheritDoc} */
+    @Override public Double predict(HashMap<String, Double> input) {
+        LeapFrameBuilder builder = new LeapFrameBuilder();
+        List<StructField> structFields = new ArrayList<>();
+
+        for (String fieldName : input.keySet())
+            structFields.add(new StructField(fieldName, ScalarType.Double()));
+
+        StructType schema = builder.createSchema(structFields);
+
+        List<Row> rows = new ArrayList<>();
+        rows.add(builder.createRowFromIterable(new ArrayList<>(input.values())));
+
+        DefaultLeapFrame inputFrame = builder.createFrame(schema, rows);
+
+        return predict(inputFrame);
+    }
+
+    /**
+     * Makes a prediction using default column order specified in {@link #schema}.
+     *
+     * @param input Input arguments.
+     * @return Prediction result.
+     */
+    public double predict(Double[] input) {
+        if (input.length != schema.size())
+            throw new IllegalArgumentException("Input size is not equal to schema size");
+
+        Map<String, Double> vec = IntStream.range(0, input.length)
+            .boxed()
+            .collect(Collectors.toMap(schema::get, i -> input[i]));
+
+        return predict(new HashMap<>(vec));
+    }
+
+    /**
+     * Makes a prediction using MLeap API.
+     *
+     * @param inputFrame Input MLeap frame.
+     * @return Prediction result.
+     */
+    public double predict(DefaultLeapFrame inputFrame) {
+        DefaultLeapFrame outputFrame = transformer.transform(inputFrame).get();
+
+        Try<DefaultLeapFrame> resFrame = outputFrame.select(new Set.Set1<>(outputFieldName).toSeq());
+        DefaultLeapFrame frame = resFrame.get();
+
+        Stream<?> stream = (Stream<?>)frame.productElement(1);
+        Row row = (Row)stream.head();
+
+        return (Double)row.get(0);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void close() {
+        transformer.close();
+    }
+}
diff --git a/modules/ml/mleap-model-parser/src/main/java/org/apache/ignite/ml/mleap/MLeapModelParser.java b/modules/ml/mleap-model-parser/src/main/java/org/apache/ignite/ml/mleap/MLeapModelParser.java
new file mode 100644
index 0000000..d3a1d81
--- /dev/null
+++ b/modules/ml/mleap-model-parser/src/main/java/org/apache/ignite/ml/mleap/MLeapModelParser.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.ml.mleap;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import ml.combust.mleap.core.types.ScalarType;
+import ml.combust.mleap.core.types.StructField;
+import ml.combust.mleap.core.types.StructType;
+import ml.combust.mleap.runtime.MleapContext;
+import ml.combust.mleap.runtime.frame.Transformer;
+import ml.combust.mleap.runtime.javadsl.BundleBuilder;
+import ml.combust.mleap.runtime.javadsl.ContextBuilder;
+import ml.combust.mleap.runtime.transformer.PipelineModel;
+import org.apache.ignite.ml.inference.parser.ModelParser;
+import scala.collection.JavaConverters;
+
+/**
+ * MLeap model parser.
+ */
+public class MLeapModelParser implements ModelParser<HashMap<String, Double>, Double, MLeapModel> {
+    /** */
+    private static final long serialVersionUID = -370352744966205715L;
+
+    /** Temporary file prefix. */
+    private static final String TMP_FILE_PREFIX = "mleap_model";
+
+    /** Temporary file postfix. */
+    private static final String TMP_FILE_POSTFIX = ".zip";
+
+    /** {@inheritDoc} */
+    @Override public MLeapModel parse(byte[] mdl) {
+        MleapContext mleapCtx = new ContextBuilder().createMleapContext();
+        BundleBuilder bundleBuilder = new BundleBuilder();
+
+        File file = null;
+        try {
+            file = File.createTempFile(TMP_FILE_PREFIX, TMP_FILE_POSTFIX);
+            try (FileOutputStream fos = new FileOutputStream(file)) {
+                fos.write(mdl);
+                fos.flush();
+            }
+
+            Transformer transformer = bundleBuilder.load(file, mleapCtx).root();
+            PipelineModel pipelineMdl = (PipelineModel)transformer.model();
+
+            List<String> inputSchema = checkAndGetInputSchema(pipelineMdl);
+            String outputSchema = checkAndGetOutputSchema(pipelineMdl);
+
+            return new MLeapModel(transformer, inputSchema, outputSchema);
+        }
+        catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        finally {
+            if (file != null)
+                file.delete();
+        }
+    }
+
+    /**
+     * Util method that checks that input schema contains only one double type.
+     *
+     * @param mdl Pipeline model.
+     * @return Name of output field.
+     */
+    private String checkAndGetOutputSchema(PipelineModel mdl) {
+        Transformer lastTransformer = mdl.transformers().last();
+        StructType outputSchema = lastTransformer.outputSchema();
+
+        List<StructField> output = new ArrayList<>(JavaConverters.seqAsJavaListConverter(outputSchema.fields()).asJava());
+
+        if (output.size() != 1)
+            throw new IllegalArgumentException("Parser supports only scalar outputs");
+
+        return output.get(0).name();
+    }
+
+    /**
+     * Util method that checks that output schema contains only double types and returns list of field names.
+     *
+     * @param mdl Pipeline model.
+     * @return List of field names.
+     */
+    private List<String> checkAndGetInputSchema(PipelineModel mdl) {
+        Transformer firstTransformer = mdl.transformers().head();
+        StructType inputSchema = firstTransformer.inputSchema();
+
+        List<StructField> input = new ArrayList<>(JavaConverters.seqAsJavaListConverter(inputSchema.fields()).asJava());
+
+        List<String> schema = new ArrayList<>();
+
+        for (StructField field : input) {
+            String fieldName = field.name();
+
+            schema.add(field.name());
+            if (!ScalarType.Double().base().equals(field.dataType().base()))
+                throw new IllegalArgumentException("Parser supports only double types [name=" +
+                    fieldName + ",type=" + field.dataType() + "]");
+        }
+
+        return schema;
+    }
+}
diff --git a/modules/ml/mleap-model-parser/src/main/java/org/apache/ignite/ml/mleap/package-info.java b/modules/ml/mleap-model-parser/src/main/java/org/apache/ignite/ml/mleap/package-info.java
new file mode 100644
index 0000000..bdfe18d
--- /dev/null
+++ b/modules/ml/mleap-model-parser/src/main/java/org/apache/ignite/ml/mleap/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * <!-- Package description. -->
+ * Base package for Mleap model parser.
+ */
+package org.apache.ignite.ml.mleap;
\ No newline at end of file
diff --git a/modules/ml/mleap-model-parser/src/test/java/org/apache/ignite/ml/mleap/IgniteMLeapTestSuite.java b/modules/ml/mleap-model-parser/src/test/java/org/apache/ignite/ml/mleap/IgniteMLeapTestSuite.java
new file mode 100644
index 0000000..109b91d
--- /dev/null
+++ b/modules/ml/mleap-model-parser/src/test/java/org/apache/ignite/ml/mleap/IgniteMLeapTestSuite.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.ml.mleap;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/** Test suite for all module tests. */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+    MLeapModelParserTest.class
+})
+public class IgniteMLeapTestSuite {
+    // No-op.
+}
diff --git a/modules/ml/mleap-model-parser/src/test/java/org/apache/ignite/ml/mleap/MLeapModelParserTest.java b/modules/ml/mleap-model-parser/src/test/java/org/apache/ignite/ml/mleap/MLeapModelParserTest.java
new file mode 100644
index 0000000..989c48b
--- /dev/null
+++ b/modules/ml/mleap-model-parser/src/test/java/org/apache/ignite/ml/mleap/MLeapModelParserTest.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.ml.mleap;
+
+import java.net.URL;
+import java.util.HashMap;
+import org.apache.ignite.ml.inference.builder.SingleModelBuilder;
+import org.apache.ignite.ml.inference.builder.SyncModelBuilder;
+import org.apache.ignite.ml.inference.reader.FileSystemModelReader;
+import org.apache.ignite.ml.inference.reader.ModelReader;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for {@link MLeapModelParser}.
+ */
+public class MLeapModelParserTest {
+    /** Test model resource name. */
+    private static final String TEST_MODEL_RESOURCE = "datasets/scikit-airbnb.rf.zip";
+
+    /** Parser. */
+    private final MLeapModelParser parser = new MLeapModelParser();
+
+    /** Model builder. */
+    private final SyncModelBuilder mdlBuilder = new SingleModelBuilder();
+
+    /** */
+    @Test
+    public void testParseAndPredict() {
+        URL url = MLeapModelParserTest.class.getClassLoader().getResource(TEST_MODEL_RESOURCE);
+        if (url == null)
+            throw new IllegalStateException("File not found [resource_name=" + TEST_MODEL_RESOURCE + "]");
+
+        ModelReader reader = new FileSystemModelReader(url.getPath());
+
+        try (MLeapModel mdl = mdlBuilder.build(reader, parser)) {
+            HashMap<String, Double> input = new HashMap<>();
+            input.put("imp_bathrooms", 1.0);
+            input.put("imp_bedrooms", 1.0);
+            input.put("imp_security_deposit", 1.0);
+            input.put("imp_cleaning_fee", 1.0);
+            input.put("imp_extra_people", 1.0);
+            input.put("imp_number_of_reviews", 1.0);
+            input.put("imp_square_feet", 1.0);
+            input.put("imp_review_scores_rating", 1.0);
+
+            double prediction = mdl.predict(input);
+
+            assertEquals(95.3919, prediction, 1e-5);
+        }
+    }
+}
diff --git a/modules/ml/mleap-model-parser/src/test/resources/datasets/scikit-airbnb.rf.zip b/modules/ml/mleap-model-parser/src/test/resources/datasets/scikit-airbnb.rf.zip
new file mode 100644
index 0000000..e24d3b4
Binary files /dev/null and b/modules/ml/mleap-model-parser/src/test/resources/datasets/scikit-airbnb.rf.zip differ
diff --git a/pom.xml b/pom.xml
index 87dbbf6..f8653f9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -118,6 +118,7 @@
         <profile>
             <id>all-scala</id><!-- used to update project versions and check all modules compilation -->
             <modules><!-- sorted alphabetically -->
+                <module>modules/ml/mleap-model-parser</module>
                 <module>modules/scalar-2.10</module>
                 <module>modules/scalar</module>
                 <module>modules/spark</module>
@@ -537,6 +538,7 @@
             </activation>
 
             <modules>
+                <module>modules/ml/mleap-model-parser</module>
                 <module>modules/scalar</module>
                 <module>modules/spark</module>
                 <module>modules/visor-console</module>