From 35bbc17cf39e1cd5262fb83e3741bd792b742839 Mon Sep 17 00:00:00 2001 From: Peter Solymos Date: Wed, 23 Jun 2021 14:09:13 -0600 Subject: [PATCH] Add classification example Signed-off-by: Peter Solymos --- 03-classification/README.md | 143 ++++++++++++++++++++++++++++++++++++ 03-classification/model.rda | Bin 0 -> 6014 bytes 2 files changed, 143 insertions(+) create mode 100644 03-classification/README.md create mode 100644 03-classification/model.rda diff --git a/03-classification/README.md b/03-classification/README.md new file mode 100644 index 0000000..33b1654 --- /dev/null +++ b/03-classification/README.md @@ -0,0 +1,143 @@ +# Classification + +This examples uses [Support-vector Machines (SVM)](https://en.wikipedia.org/wiki/Support-vector_machine) to do [multinomial classification](https://en.wikipedia.org/wiki/Statistical_classification). + +Classification is predicting which class a new observation belongs to. The prediction is based on a model trained on a set of observations whose class membership is known. + +Multinomial problems have more then 2 possible classes. We use the [_Iris_ flower data set](https://en.wikipedia.org/wiki/Iris_flower_data_set) that contains measurements for 3 _Iris_ species. + +We will create a function that takes a JSON array of measurements as input and returns JSON with the predicted class and class membership probabilities corresponding to the measurements. + +> You will learn how to pre-load trained model objects to make predictions. + +You'll need the prerequisites listed [here](https://github.com/analythium/openfaas-rstats-templates/tree/master/examples). + +- [Classification](#classification) + - [Create a new function using a template](#create-a-new-function-using-a-template) + - [Customize the function](#customize-the-function) + - [Build, push, deploy the function](#build-push-deploy-the-function) + - [Testing](#testing) + +## Create a new function using a template + +Create a new function called `r-iris`. + +```bash +faas-cli new --lang rstats-base-plumber r-iris +``` + +## Customize the function + +Edit the `./r-iris/DESCRIPTION` file. + +```yaml +Package: OpenFaaStR +Version: 0.0.1 +Imports: + e1071 +Remotes: +SystemRequirements: +VersionedPackages: +``` + +Open R and perform model training, then save the trained model into `./r-iris/model.rda` (see the file [model.rda](model.rda) file): + +```R +library(e1071) # library with SVM function +data(iris) # Iris data set + +str(iris) # see the measured variables +'data.frame': 150 obs. of 5 variables: +# $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ... +# $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ... +# $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ... +# $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ... +# $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 ... +levels(iris$Species) # the 3 Iris species +# [1] "setosa" "versicolor" "virginica" + +## train model with probability=TRUE +model <- svm(Species ~ ., iris, probability=TRUE) +model # print our model info +# +# Call: +# svm(formula = Species ~ ., data = iris) +# +# +# Parameters: +# SVM-Type: C-classification +# SVM-Kernel: radial +# cost: 1 +# +# Number of Support Vectors: 51 + +## save the trained model +saveRDS(model, "./r-iris/model.rda") +``` + +Change the `./r-iris/handler.R` file. +Note: loading libraries is good practice, it makes trouble shooting installation related +issues much easier (i.e. when shared objects are not found doe to not building +the package against specific libraries). Startup messages can also be useful. + +When reading in the rda file, we don't need the directory because the file will be moved into the function's root directory: + +```R +library(e1071) +model <- readRDS("model.rda") +#* Iris +#* @serializer unboxedJSON +#* @post / +function(req) { + x <- as.data.frame( + jsonlite::fromJSON(paste(req$postBody)) + ) + p <- predict(model, x, probability=TRUE) + list( + species=as.character(p), + probabilities=as.list(drop(attr(p,"probabilities"))) + ) +} +``` + +Edit the `r-iris.yml` file as required, see [configuration](https://docs.openfaas.com/reference/yaml/) options. + +## Build, push, deploy the function + +`faas-cli up` is a [shorthand](https://docs.openfaas.com/cli/templates/) +for automating `faas-cli build`, `faas-cli push`, and `faas-cli deploy`. + +```bash +faas-cli up -f r-iris.yml +``` + +## Testing + +Test the Docker image locally after `docker run -p 5000:8080 $OPENFAAS_PREFIX/r-iris`: + +```bash +curl http://localhost:5000/ -H \ + "Content-Type: application/json" -d \ + '{"Sepal.Length":5.2,"Sepal.Width":3.4,"Petal.Length":1.5,"Petal.Width":0.2}' +``` + +Test the deployed instance: + +```bash +curl $OPENFAAS_URL/function/r-iris -H \ + "Content-Type: application/json" -d \ + '{"Sepal.Length":5.2,"Sepal.Width":3.4,"Petal.Length":1.5,"Petal.Width":0.2}' +``` + +The output should include the predicted species name and the probabilities: + +```json +{ + "species": "setosa", + "probabilities": { + "setosa": 0.9781, + "versicolor": 0.0126, + "virginica": 0.0093 + } +} +``` diff --git a/03-classification/model.rda b/03-classification/model.rda new file mode 100644 index 0000000000000000000000000000000000000000..2c6d0adcf2f24352da66507171cae9ba2524ff6b GIT binary patch literal 6014 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