Predict on a Spark MLlib model PMML InferenceService

Setup

  1. Install pyspark 3.0.x and pyspark2pmml

    1. pip install pyspark~=3.0.0
    2. pip install pyspark2pmml
  2. Get JPMML-SparkML jar

Train a Spark MLlib model and export to PMML file

Launch pyspark with --jars to specify the location of the JPMML-SparkML uber-JAR

  1. pyspark --jars ./jpmml-sparkml-executable-1.6.3.jar

Fitting a Spark ML pipeline:

  1. from pyspark.ml import Pipeline
  2. from pyspark.ml.classification import DecisionTreeClassifier
  3. from pyspark.ml.feature import RFormula
  4. df = spark.read.csv("Iris.csv", header = True, inferSchema = True)
  5. formula = RFormula(formula = "Species ~ .")
  6. classifier = DecisionTreeClassifier()
  7. pipeline = Pipeline(stages = [formula, classifier])
  8. pipelineModel = pipeline.fit(df)
  9. from pyspark2pmml import PMMLBuilder
  10. pmmlBuilder = PMMLBuilder(sc, df, pipelineModel)
  11. pmmlBuilder.buildFile("DecisionTreeIris.pmml")

Upload the DecisionTreeIris.pmml to a GCS bucket, note that the PMMLServer expect model file name to be model.pmml

  1. gsutil cp ./DecisionTreeIris.pmml gs://$BUCKET_NAME/sparkpmml/model.pmml

Create the InferenceService with PMMLServer

Create the InferenceService with pmml predictor and specify the storageUri with bucket location you uploaded to

  1. apiVersion: "serving.kserve.io/v1beta1"
  2. kind: "InferenceService"
  3. metadata:
  4. name: "spark-pmml"
  5. spec:
  6. predictor:
  7. pmml:
  8. storageUri: gs://kfserving-examples/models/sparkpmml

Apply the InferenceService custom resource

  1. kubectl apply -f spark_pmml.yaml

Expected Output

  1. $ inferenceservice.serving.kserve.io/spark-pmml created

Wait the InferenceService to be ready

  1. kubectl wait --for=condition=Ready inferenceservice spark-pmml
  2. inferenceservice.serving.kserve.io/spark-pmml condition met

Run a prediction

The first step is to determine the ingress IP and ports and set INGRESS_HOST and INGRESS_PORT

  1. MODEL_NAME=spark-pmml
  2. INPUT_PATH=@./pmml-input.json
  3. SERVICE_HOSTNAME=$(kubectl get inferenceservice spark-pmml -o jsonpath='{.status.url}' | cut -d "/" -f 3)
  4. curl -v -H "Host: ${SERVICE_HOSTNAME}" http://${INGRESS_HOST}:${INGRESS_PORT}/v1/models/$MODEL_NAME:predict -d $INPUT_PATH

Expected Output

  1. * Connected to spark-pmml.default.35.237.217.209.xip.io (35.237.217.209) port 80 (#0)
  2. > POST /v1/models/spark-pmml:predict HTTP/1.1
  3. > Host: spark-pmml.default.35.237.217.209.xip.io
  4. > User-Agent: curl/7.73.0
  5. > Accept: */*
  6. > Content-Length: 45
  7. > Content-Type: application/x-www-form-urlencoded
  8. >
  9. * upload completely sent off: 45 out of 45 bytes
  10. * Mark bundle as not supporting multiuse
  11. < HTTP/1.1 200 OK
  12. < content-length: 39
  13. < content-type: application/json; charset=UTF-8
  14. < date: Sun, 07 Mar 2021 19:32:50 GMT
  15. < server: istio-envoy
  16. < x-envoy-upstream-service-time: 14
  17. <
  18. * Connection #0 to host spark-pmml.default.35.237.217.209.xip.io left intact
  19. {"predictions": [[1.0, 0.0, 1.0, 0.0]]}