How to publish and manage R web services in Machine Learning Server with mrsdeploy

Applies to: Machine Learning Server, Microsoft R Server 9.x

This article details how you can publish and manage your analytic web services directly in R. You can deploy your R models, scripts, and code as web services using the functions in the mrsdeploy R package. The mrsdeploy R package containing these functions is installed with both Machine Learning Server (and Microsoft R Server) and Microsoft R Client.

These web services can be consumed in R by other authenticated users or in the language of their choice via Swagger.

Using the mrsdeploy R package, you can publish, update, and delete two kinds of R web services: standard R web services and realtime R web services.

Additionally, you can get a list of all services, retrieve a web service object for consumption, and share services with others. You can also publish or interact with a web service outside of R using the RESTful APIs, which provide direct programmatic access to a service's lifecycle.

Requirements

Before you can use the web service management functions in the mrsdeploy R package, you must:

Permissions for managing web services

Any authenticated user can:

  • Update and delete web services they have published
  • Retrieve any web service object for consumption
  • Retrieve a list of any or all web services

While any authenticated user can also publish a web service by default, roles can be used to further control permissions. Beginning in version 9.1, your administrator can also assign role-based authorization to further restrict the permissions around web services to give some users more control over web services than others. Ask your administrator for details on your role.

Publish and update web services

To deploy your analytics, you must publish them as web services in Machine Learning Server. Once hosted on Machine Learning Server, you can update and manage them. They can also be consumed by other users.

Versioning

Every time a web service is published, a version is assigned to the web service. Versioning enables users to better manage the release of their web services. Versions help the users consuming your service to easily identify it.

At publish time, you can specify an alphanumeric string that is meaningful to the users who consume the service in your organization. For example, you could use '2.0', 'v1.0.0', 'v1.0.0-alpha', or 'test-1'. Meaningful versions are helpful when you intend to share services with others. We highly recommend a consistent and meaningful versioning convention across your organization or team such as semantic versioning.

If you do not specify a version, a globally unique identifier (GUID) is automatically assigned by Machine Learning Server. These GUID version numbers are harder to remember by the users consuming your services and are therefore less desirable.

Publish service

To deploy your analytics, you must publish them as web services in Machine Learning Server. Once hosted on Machine Learning Server, you can update and manage them. They can also be consumed by other users.

After you've authenticated, use the publishService() function in the mrsdeploy package to publish a web service. See the package reference for publishService() for the full description of all arguments.

Function Response R Help
publishService(...) Returns an API instance client stub for consuming that service and viewing its service holdings) as an R6 class. View

You can publish web services to a local Machine Learning Server from your command line. If you create a remote session, you can also publish a web service to a remote Machine Learning Server from your local command line.

Standard web services

Standard web services, like all web services, are identified by their name and version. Additionally, a standard web service is also defined by any code, models, and any necessary model assets. When deploying, you should also define the required inputs and any output the application developers use to integrate the service in their applications. Additional arguments are possible -- many of which are shown in the following example.

Example of standard web service:

# Publish a standard service 'mtService' version 'v1.0.0'
# Assign service to 'api' variable
api <- publishService(
     "mtService",
     code = manualTransmission,
     model = carsModel,
     inputs = list(hp = "numeric", wt = "numeric"),
     outputs = list(answer = "numeric"),
     v = "v1.0.0"
)

For a full example, you can also follow the quickstart article "Deploying an R model as a web service." You can also see the Workflow examples at the end of this article.

R source Can come from
R code - A filepath to a local R script, such as:
    code = "/path/to/R/script.R"
- A block of R code as a character string, such as:
    code = "result <- x + y"
- A function handle, such as:
    code = function(hp, wt) {
            newdata <- data.frame(hp = hp, wt = wt)
            predict(model, newdata, type = "response")
   }
R model The R model can come from an object or a file-path to an external representation of R objects to be loaded and used with the code, including:
- A filepath to an RData file holding the external R objects to be loaded and used with the code, such as:
    model = "/path/to/glm-model.RData"
- A filepath to an R file that is evaluated into an environment and loaded, such as:
    model = "/path/to/glm-model.R"
- A model object, such as:
    model = am.glm

Realtime web services

Realtime web services offer even lower latency to produce results faster and score more models in parallel. Learn more...

Realtime web services are also identified by their name and version. However, unlike standard web services, you cannot specify the following for realtime web services:

Realtime web services only accept model objects created with the supported functions from packages installed with the product.

Example of realtime service (supported since R Server 9.1):

# Publish a realtime service 'kyphosisService' version 'v1.0'
# Assign service to 'realtimeApi' variable
realtimeApi <- publishService(
     serviceType = "Realtime",
     name = "kyphosisService",
     code = NULL,
     model = kyphosisModel,
     v = "v1.0",
    alias = "kyphosisService"
)

For full examples, see the end of this article.

Learn how to get a list of all services, retrieve a web service object for consumption, and share services with others.

Update service

To change a web service after you've published it, while retaining the same name and version, use the updateService() function. For arguments, specify what needs to change, such as the R code, model, and inputs. When you update a service, it overwrites that named version.

After you've authenticated, use the updateService() function in the mrsdeploy package to update a web service.

See the package reference help page for updateService() for the full description of all arguments.

Function Response R Help
updateService(...) Returns an API instance client stub for consuming that service and viewing its service holdings) as an R6 class. View

Note

If you want to change the name or version number, use the publishService() function instead and specify the new name or version number.

Example:

# For web service called mtService with version number v1.0.0,
# update the model carsModel, code, inputs, and description. 
# Assign it to a variable called api.
api <- updateService(
     "mtService",
     "v1.0.0",
     code = manualTransmission,
     mode = carsModel,
     inputs = list(carData = "data.frame"),
     outputs = list(answer = "data.frame"),
     descr = "Updated after March data refresh."
)

Supported I/O data types

The following table lists the supported data types for the publishService and updateService function input and output schemas.

I/O data types Full support?
numeric Yes
integer Yes
logical Yes
character Yes
vector Yes
matrix Partial
(Not for logical & character matrices)
data.frame Yes
Note: Coercing an object during
I/O is a user-defined task

Delete web services

When you no longer want to keep a web service, you can delete it. Only the user who initially created the web service can use this function.

After you've authenticated, use the deleteService() function in the mrsdeploy package to delete a web service.

Each web service is uniquely defined by a name and version. See the package reference help page for deleteService() for the full description of all arguments.

Function Response R Help
deleteService(...) If it is successful, it returns a success status and message such as "Service mtService version v1.0.0 deleted." If it fails for any reason, then it stops execution with error message. View

Example:

result <- deleteService("mtService", "v1.0.0")
print(result)

Standard workflow examples

The following workflow examples demonstrate how to publish a web service, interact with it, and then consume it.

Each standard web service example uses the same code and models and returns the same results. However the code and model are represented in different formats each time, such as R scripts, objects, and files.

To learn more about standard web services, see here.

Before you begin

Important

Replace the connection details in the remoteLogin() function in each example with the details for your configuration. Connecting to R Server using the mrsdeploy package is covered in this article.

The base path for files is set to your working directory, but you can change that using ServiceOption as follows:

  • Specify a different base path for code and model arguments:

    opts <- serviceOption()
    opts$set("data-dir", "/base/path/to/some-other/location"))
    
  • Clear the path and expect the code and model files to be in the current working directory during publishService():

    opts <- serviceOption() s
    opts$set("data-dir", NULL))
    
  • Clear the path and require a FULLY QUALIFIED path for code and model parameters in publishService():

    opts <- serviceOption() s
    opts$set("data-dir", ""))
    

1. R code and model are objects

In this example, the code comes from an object (code = manualTransmission) and the model comes from a model object (model = carsModel). For an example of inputs/outputs as dataframes, see "Example 5".

##########################################################
#       Create & Test a Logistic Regression Model        #
##########################################################

# For R Server 9.0, load mrsdeploy package     
library(mrsdeploy)

# Use logistic regression equation of vehicle transmission 
# in the data set mtcars to estimate the probability of 
# a vehicle being fitted with a manual transmission 
# based on horsepower (hp) and weight (wt)


# Create glm model with `mtcars` dataset
carsModel <- glm(formula = am ~ hp + wt, data = mtcars, family = binomial)

# Produce a prediction function that can use the model
manualTransmission <- function(hp, wt) {
     newdata <- data.frame(hp = hp, wt = wt)
     predict(carsModel, newdata, type = "response")
}
   
# test function locally by printing results
print(manualTransmission(120, 2.8)) # 0.6418125

##########################################################
#            Log into Microsoft R Server                 #
##########################################################
   
# Use `remoteLogin` to authenticate with R Server using 
# the local admin account. Use session = false so no 
# remote R session started
# REMEMBER: Replace with your login details
remoteLogin("http://localhost:12800", 
            username = “admin”, 
            password = “{{YOUR_PASSWORD}}”,
            session = FALSE)

##########################################################
#             Publish Model as a Service                 #
##########################################################

# Generate a unique serviceName for demos 
# and assign to variable serviceName
serviceName <- paste0("mtService", round(as.numeric(Sys.time()), 0))

# Publish as service using publishService() function from 
# mrsdeploy package. Use the service name variable and provide
# unique version number. Assign service to the variable `api`
api <- publishService(
     serviceName,
     code = manualTransmission,
     model = carsModel,
     inputs = list(hp = "numeric", wt = "numeric"),
     outputs = list(answer = "numeric"),
     v = "v1.0.0"
)

##########################################################
#                 Consume Service in R                   #
##########################################################
   
# Print capabilities that define the service holdings: service 
# name, version, descriptions, inputs, outputs, and the 
# name of the function to be consumed
print(api$capabilities())
   
# Consume service by calling function, `manualTransmission`
# contained in this service
result <- api$manualTransmission(120, 2.8)

# Print response output named `answer`
print(result$output("answer")) # 0.6418125   

##########################################################
#       Get Swagger File for this Service in R Now       #
##########################################################
   
# During this authenticated session, download the  
# Swagger-based JSON file that defines this service
swagger <- api$swagger()
cat(swagger, file = "swagger.json", append = FALSE)

# Now you can share Swagger-based JSON so others can consume it

##########################################################
#          Delete service version when finished          #
##########################################################

# User who published service or user with owner role can
# remove the service when it is no longer needed
status <- deleteService(serviceName, "v1.0.0")
status

##########################################################
#                   Log off of R Server                  #
##########################################################

# Log off of R Server
remoteLogout()

2. R code as object and RData as file

In this example, the code is still an object (code = manualTransmission), but the model now comes from a Rdata file (model = "transmission.RData"). The result is still the same as in the first example.

# For R Server 9.0, load mrsdeploy package on R Server     
library(mrsdeploy)

# --- AAD login ----------------------------------------------------------------

# Use `remoteLogin` to authenticate with R Server using 
# the local admin account. Use session = false so no 
# remote R session started
# REMEMBER: Replace with your login details
remoteLogin("http://localhost:12800", 
            username = “admin”, 
            password = “{{YOUR_PASSWORD}}”,
            session = FALSE)

model <- glm(formula = am ~ hp + wt, data = mtcars, family = binomial)
save(model, file = "transmission.RData")

manualTransmission <- function(hp, wt) {
  newdata <- data.frame(hp = hp, wt = wt)
  predict(model, newdata, type = "response")
}

# test locally: 0.6418125
print(manualTransmission(120, 2.8))

# Generate a unique serviceName for demos 
# and assign to variable serviceName
serviceName <- paste0("mtService", round(as.numeric(Sys.time()), 0))

api <- publishService(
   serviceName,
   code = manualTransmission,
   model = "transmission.RData",
   inputs = list(hp = "numeric", wt = "numeric"),
   outputs = list(answer = "numeric"),
   v = "v1.0.2"
)

api

result <- api$manualTransmission(120, 2.8)
print(result$output("answer")) # 0.6418125

swagger <- api$swagger()
cat(swagger)

swagger <- api$swagger(json = FALSE)
swagger

services <- listServices(serviceName)
services

serviceName <- services[[1]]
serviceName

api <- getService(serviceName$name, serviceName$version)
api
result <- api$manualTransmission(120, 2.8)
print(result$output("answer")) # 0.6418125

cap <- api$capabilities()
cap
cap$swagger

status <- deleteService(cap$name, cap$version)
status

remoteLogout()

3. Code and model as R scripts

In this example, the code (code = transmission-code.R,) and the model comes from R scripts (model = "transmission.R"). The result is still the same as in the first example.

# For R Server 9.0, load mrsdeploy package on R Server     
library(mrsdeploy)

# --- AAD login ----------------------------------------------------------------

# Use `remoteLogin` to authenticate with R Server using 
# the local admin account. Use session = false so no 
# remote R session started
# REMEMBER: Replace with your login details
remoteLogin("http://localhost:12800", 
            username = “admin”, 
            password = “{{YOUR_PASSWORD}}”,
            session = FALSE)

# Information can come from a file
model <- "model <- glm(formula = am ~ hp + wt, data = mtcars, family = binomial)"
code <- "newdata <- data.frame(hp = hp, wt = wt)\n
         answer <- predict(model, newdata, type = "response")"

cat(model, file = "transmission.R", append = FALSE)
cat(code, file = "transmission-code.R", append = FALSE)

# Generate a unique serviceName for demos 
# and assign to variable serviceName
serviceName <- paste0("mtService", round(as.numeric(Sys.time()), 0))

api <- publishService(
   serviceName,
   code = "transmission-code.R",
   model = "transmission.R",
   inputs = list(hp = "numeric", wt = "numeric"),
   outputs = list(answer = "numeric"),
   v = "v1.0.3",
   alias = "manualTransmission"
)

api

result <- api$manualTransmission(120, 2.8)
result
print(result$output("answer")) # 0.6418125

swagger <- api$swagger()
cat(swagger)

swagger <- api$swagger(json = FALSE)
swagger

services <- listServices(serviceName)
services

serviceName <- services[[1]]
serviceName

api <- getService(serviceName$name, serviceName$version)
api
result <- api$manualTransmission(120, 2.8)
print(result$output("answer")) # 0.6418125

cap <- api$capabilities()
cap
cap$swagger

status <- deleteService(cap$name, cap$version)
status

remoteLogout()

4. Code as script and model as a RData file

In this example, the code (code = transmission-code.R,) comes from an R script, and the model from an RData file (model = "transmission.RData"). The result is still the same as in the first example.

# For R Server 9.0, load mrsdeploy package on R Server     
library(mrsdeploy)

# AAD login

# Use `remoteLogin` to authenticate with R Server using 
# the local admin account. Use session = false so no 
# remote R session started
# REMEMBER: Replace with your login details
remoteLogin("http://localhost:12800", 
            username = “admin”, 
            password = “{{YOUR_PASSWORD}}”,
            session = FALSE)

# model
model <- glm(formula = am ~ hp + wt, data = mtcars, family = binomial)
save(model, file = "transmission.RData")

# R code
code <- "newdata <- data.frame(hp = hp, wt = wt)\n
         answer <- predict(model, newdata, type = "response")"
cat(code, file = "transmission-code.R", sep="n", append = TRUE)

# Generate a unique serviceName for demos 
# and assign to variable serviceName
serviceName <- paste0("mtService", round(as.numeric(Sys.time()), 0))

api <- publishService(
   serviceName,
   code = "transmission-code.R",
   model = "transmission.RData",
   inputs = list(hp = "numeric", wt = "numeric"),
   outputs = list(answer = "numeric"),
   v = "v1.0.4",
   alias = "manualTransmission"
)

api

result <- api$manualTransmission(120, 2.8)
print(result$output("answer")) # 0.6418125

swagger <- api$swagger()
cat(swagger)

swagger <- api$swagger(json = FALSE)
swagger

services <- listServices(serviceName)
services

serviceName <- services[[1]]
serviceName

api <- getService(serviceName$name, serviceName$version)
api
result <- api$manualTransmission(120, 2.8)
print(result$output("answer")) # 0.6418125

cap <- api$capabilities()
cap
cap$swagger

status <- deleteService(cap$name, cap$version)
status

remoteLogout()

5. R code & model as objects, inputs/outputs as dataframes

In this example, the code comes from an object (code = manualTransmission) and the model comes from a model object (model = carsModel) as it was in example 1. However, in this example, the inputs and outputs are provided in the form of dataframes.

######## Create/Test Logistic Regression Model #########

# R Server 9.0, load mrsdeploy. Later versions can skip.    
library(mrsdeploy)

# Estimate the probability of a vehicle being fitted with 
# a manual transmission based on horsepower (hp) and weight (wt)

# load the mtcars dataset
data(mtcars)

# Split the mtcars dataset into 75% train and 25% test dataset
train_ind <- sample(seq_len(nrow(mtcars)), size = floor(0.75 * nrow(mtcars)))
train <- mtcars[train_ind,]
test <- mtcars[-train_ind,]

# Create glm model with training `mtcars` dataset
carsModel <- rxLogit(formula = am ~ hp + wt, data = train)

# Create a list to pass the data column info together with the model object
carsModelInfo <- list(predictiveModel = carsModel, colInfo = rxCreateColInfo(train))

# Produce a prediction function that can use the model and column info
manualTransmission <- function(carData) {
  newdata <- rxImport(carData, colInfo = carsModelInfo$colInfo)
  rxPredict(carsModelInfo$predictiveModel, newdata, type = "response")
}

# test function locally by printing results
print(manualTransmission(test))


############# Log into Microsoft R Server ##############

# Use `remoteLogin` to authenticate with R Server.
# REMEMBER: Replace with your login details
remoteLogin("http://localhost:12800", 
            username = "admin", 
            password = "{{YOUR_PASSWORD}}",
            session = FALSE)


############## Publish Model as a Service ##############

# Generate a unique serviceName for demos and assign to variable serviceName
serviceName <- paste0("mtService", round(as.numeric(Sys.time()), 0))

# Publish as service using publishService() function from 
# mrsdeploy package. Use the service name variable and provide
# unique version number. Assign service to the variable `api`
api <- publishService(
  serviceName,
  code = manualTransmission,
  model = carsModelInfo,
  inputs = list(carData = "data.frame"),
  outputs = list(answer = "data.frame"),
  v = "v1.0.0"
)

################## Consume Service in R ################

# Print capabilities that define the service holdings: service 
# name, version, descriptions, inputs, outputs, and the 
# name of the function to be consumed
print(api$capabilities())

# Consume service by calling function, `manualTransmission` contained in this service

# consume service using existing data frame `test`
result <- api$manualTransmission(test)
print(result$output("answer")) 

# consume service by constructing data frames with single row and multiple rows
emptyDataFrame <- data.frame(mpg = numeric(),
                             cyl = numeric(),
                             disp = numeric(),
                             hp = numeric(),
                             drat = numeric(),
                             wt = numeric(),
                             qsec = numeric(),
                             vs = numeric(),
                             am = numeric(),
                             gear = numeric(),
                             carb = numeric())

singleRowDataFrame <- rbind(emptyDataFrame, data.frame(mpg = 21.0,
                                                       cyl = 6,
                                                       disp = 160,
                                                       hp = 110,
                                                       drat = 3.90,
                                                       wt = 2.620,
                                                       qsec = 16.46,
                                                       vs = 0,
                                                       am = 1,
                                                       gear = 4,
                                                       carb = 4))
result <- api$manualTransmission(singleRowDataFrame)
print(result$output("answer"))

multipleRowsDataFrame <- rbind(emptyDataFrame, data.frame(mpg = c(21.0, 20.1),
                                                          cyl = c(6, 5),
                                                          disp = c(160, 159),
                                                          hp = c(110, 109),
                                                          drat = c(3.90, 2.94),
                                                          wt = c(2.620, 2.678),
                                                          qsec = c(16.46, 15.67),
                                                          vs = c(0, 0),
                                                          am = c(1, 1),
                                                          gear = c(4, 3),
                                                          carb = c(4, 2)))
result <- api$manualTransmission(multipleRowsDataFrame)
print(result$output("answer")) 


######### Get Swagger File for Service in R Now ########

# During this authenticated session, download the  
# Swagger-based JSON file that defines this service
swagger <- api$swagger()
cat(swagger, file = "swagger.json", append = FALSE)

# Now you can share Swagger-based JSON so others can consume it


######### Delete service version when finished #########

# User who published service or user with owner role can
# remove the service when it is no longer needed
status <- deleteService(serviceName, "v1.0.0")
status


################### Log off R Server ###################

remoteLogout()

Realtime workflow example

In this example, the local model object (model = kyphosisModel) is generated using the rxLogit modeling function in the RevoScaleR package.

Realtime web services were introduced in R Server 9.1. To learn more about the supported model formats, supported product versions, and supported platforms for realtime web services, see here.

##          REALTIME WEB SERVICE EXAMPLE                ##
 
##########################################################
#   Create/Test Logistic Regression Model with rxLogit   #
##########################################################
    
# Create logistic regression model 
# using rxLogit modeling function from RevoScaleR package
# and the Rpart `kyphosis` dataset available to all R users
kyphosisModel <- rxLogit(Kyphosis ~ Age, data=kyphosis)
 
# Test the model locally
testData <- data.frame(Kyphosis=c("absent"), Age=c(71), Number=c(3), Start=c(5))
rxPredict(kyphosisModel, data = testData)  # Kyphosis_Pred: 0.1941938
 
##########################################################
#            Log into Microsoft R Server                 #
##########################################################
   
# Use `remoteLogin` to authenticate with R Server using 
# the local admin account. Use session = false so no 
# remote R session started
# REMEMBER: replace with the login info for your organization
remoteLogin("http://localhost:12800", 
            username = "admin", 
            password = “{{YOUR_PASSWORD}}”,
            session = FALSE)

##########################################################
#    Publish Kyphosis Model as a Realtime Service        #
##########################################################

# Generate a unique serviceName for demos 
# and assign to variable serviceName
serviceName <- paste0("kyphosis", round(as.numeric(Sys.time()), 0))
 
# Publish as service using publishService() function. 
# Use the variable name for the service and version `v1.0`
# Assign service to the variable `realtimeApi`.
realtimeApi <- publishService(
     serviceType = "Realtime",
     name = serviceName,
     code = NULL,
     model = kyphosisModel,
     v = "v1.0",
     alias = "kyphosisService"
)
 
##########################################################
#           Consume Realtime Service in R                #
##########################################################
   
# Print capabilities that define the service holdings: service 
# name, version, descriptions, inputs, outputs, and the 
# name of the function to be consumed
print(realtimeApi$capabilities())
   
# Consume service by calling function contained in this service
realtimeResult <- realtimeApi$kyphosisService(testData)

# Print response output
print(realtimeResult$outputParameters) # 0.1941938   
 
##########################################################
#         Get Service-specific Swagger File in R         #
##########################################################
   
# During this authenticated session, download the  
# Swagger-based JSON file that defines this service
rtSwagger <- realtimeApi$swagger()
cat(rtSwagger, file = "realtimeSwagger.json", append = FALSE)
 
# Share Swagger-based JSON with those who need to consume it

See also