```
library(keras)
library(tensorflow)
<- dataset_mnist()
mnist
$train$x <- (mnist$train$x/255) %>%
mnistarray_reshape(., dim = c(dim(.), 1))
$test$x <- (mnist$test$x/255) %>%
mnistarray_reshape(., dim = c(dim(.), 1))
```

# Plumber API

In this tutorial you will learn how to deploy a TensorFlow model using a plumber API.

In this example we will build an endpoint that takes `POST`

requests sending images containing handwritten digits and returning the predicted number.

## Building the model

The first thing we are going to do is to build our model. W We will use the Keras API to build this model.

We will use the MNIST dataset to build our model.

Now, we are going to define our Keras model, it will be a simple convolutional neural network.

```
<- keras_model_sequential() %>%
model layer_conv_2d(filters = 16, kernel_size = c(3,3), activation = "relu") %>%
layer_max_pooling_2d(pool_size = c(2,2)) %>%
layer_conv_2d(filters = 16, kernel_size = c(3,3), activation = "relu") %>%
layer_max_pooling_2d(pool_size = c(2,2)) %>%
layer_flatten() %>%
layer_dense(units = 128, activation = "relu") %>%
layer_dense(units = 10, activation = "softmax")
%>%
model compile(
loss = "sparse_categorical_crossentropy",
optimizer = "adam",
metrics = "accuracy"
)
```

Next, we fit the model using the MNIST dataset:

```
%>%
model fit(
x = mnist$train$x, y = mnist$train$y,
batch_size = 32,
epochs = 5,
validation_sample = 0.2,
verbose = 2
)
```

When we are happy with our model accuracy in the validation dataset we can `evaluate`

the results on the test dataset with:

`%>% evaluate(x = mnist$test$x, y = mnist$test$y) model `

```
loss accuracy
0.03600643 0.98890001
```

OK, we have 99% accuracy on the test dataset and we want to deploy that model. First, let’s save the model in the `SavedModel`

format using:

`save_model_tf(model, "cnn-mnist")`

With the model built and saved we can now start building our plumber API file.

## Plumber API

A plumber API is defined by a `.R`

file with a few annotations. Here’s is how we can write our `api.R`

file:

```
library(keras)
<- load_model_tf("cnn-mnist/")
model
#* Predicts the number in an image
#* @param enc a base64 encoded 28x28 image
#* @post /cnn-mnist
function(enc) {
# decode and read the jpeg image
<- jpeg::readJPEG(source = base64enc::base64decode(enc))
img
# reshape
<- img %>%
img array_reshape(., dim = c(1, dim(.), 1))
# make the prediction
predict_classes(model, img)
}
```

Make sure to have the your SavedModel in the same folder as `api.R`

and call:

```
<- plumber::plumb("api.R")
p $run(port = 8000) p
```

You can now make requests to the http://lcoalhost:8000/cnn-minist/ endpoint. For example, let’s verify we can make a POST request to the API sending the first image from the test set:

```
<- mnist$test$x[1,,,]
img $test$y[1] mnist
```

`[1] 7`

First let’s encode the image:

```
<- img %>%
encoded_img ::writeJPEG() %>%
jpeg::base64encode()
base64enc encoded_img
```

`[1] "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9Pjv/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APHIYJbmZYYInllc4VEUszH2A6064tbi0k8q5glgcfwyIVP5GoqKsWGoXel3sd7YXD29zESUljOGXIwf0JrpLf4o+MLeNUbVftAU5BuIUlPTplhn/wDVXQ+PNcvk8D6bpmtPbz6vqDi9lCwqhtocYRRtHU8k9+orzKius8CeHrS/nutd1pWGiaQnm3GBnznyNsQ+p/w4zWL4h1u48Ra7d6rcDa1w+VQdEUcKo+gAFZtFbsfjDVIPB7+FoRBHYyymWVlT95JyDgknGMgdADx1rCor/9k="`

```
<- httr::POST("http://localhost:8000/cnn-mnist",
req body = list(enc = encoded_img),
encode = "json")
::content(req) httr
```

```
[[1]]
[1] 7
```

You can also access the Swagger interface by accessing http://127.0.0.1:8000/**swagger**/ and paste the encoded string in the UI to visualize the result.

## More advanced models

When building more advanced models you may not be able to save the entire model using the `save_model_tf`

function. In this case you can use the `save_model_weights_tf`

function.

For example:

`save_model_weights_tf(model, " cnn-model-weights")`

Then, in the `api.R`

file whenn loading the model you will need to rebuild the model using the exact same code that you used when training and saving and then use `load_model_weights_tf`

to load the model weights.

```
<- keras_model_sequential() %>%
model layer_conv_2d(filters = 16, kernel_size = c(3,3), activation = "relu") %>%
layer_max_pooling_2d(pool_size = c(2,2)) %>%
layer_conv_2d(filters = 16, kernel_size = c(3,3), activation = "relu") %>%
layer_max_pooling_2d(pool_size = c(2,2)) %>%
layer_flatten() %>%
layer_dense(units = 128, activation = "relu") %>%
layer_dense(units = 10, activation = "softmax")
load_model_weights_tf(model, "cnn-model-weights")
```

## Hosting the plumber API

Plumber is very flexible and allows multiple hosting options. See the plumber Hostinng documentation for more information.