---
title: "Writing Custom Keras Models"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Writing Custom Keras Models} 
  %\VignetteEngine{knitr::rmarkdown} 
  %\VignetteEncoding{UTF-8}
type: docs
repo: https://github.com/rstudio/keras
menu:
  main:
    name: "Custom Models"
    identifier: "keras-custom-models"
    parent: "keras-advanced"
    weight: 60
---

```{r setup, include = FALSE}
library(keras)
knitr::opts_chunk$set(comment = NA, eval = FALSE)
```

## Overview

In addition to [sequential models](sequential_model.html) and models created with the [functional API](functional_api.html), you may also define models by defining a custom `call()` (forward pass) operation. 

To create a custom Keras model, you call the `keras_model_custom()` function, passing it an R function which in turn returns another R function that implements the custom `call()` (forward pass) operation. The R function you pass takes a `model` argument, which provides access to the underlying Keras model object should you need it. 

Typically, you'll wrap your call to `keras_model_custom()` in yet another function that enables callers to easily instantiate your custom model.

## Creating a Custom Model

This example demonstrates the implementation of a simple custom model that implements a multi-layer-perceptron with optional dropout and batch normalization:

```{r}
library(keras)

keras_model_simple_mlp <- function(num_classes, 
                                   use_bn = FALSE, use_dp = FALSE, 
                                   name = NULL) {
  
  # define and return a custom model
  keras_model_custom(name = name, function(self) {
    
    # create layers we'll need for the call (this code executes once)
    self$dense1 <- layer_dense(units = 32, activation = "relu")
    self$dense2 <- layer_dense(units = num_classes, activation = "softmax")
    if (use_dp)
      self$dp <- layer_dropout(rate = 0.5)
    if (use_bn)
      self$bn <- layer_batch_normalization(axis = -1)
    
    # implement call (this code executes during training & inference)
    function(inputs, mask = NULL) {
      x <- self$dense1(inputs)
      if (use_dp)
        x <- self$dp(x)
      if (use_bn)
        x <- self$bn(x)
      self$dense2(x)
    }
  })
}
```

Note that we include a `name` parameter so that users can optionally provide a human readable name for the model.

Note also that when we create layers to be used in our forward pass we set them onto the `self` object so they are tracked appropriately by Keras.

In `call()`, you may specify custom losses by calling `self$add_loss()`. You can also access any other members of the Keras model you need (or even add fields to the model) by using `self$`.

## Using a Custom Model

To use a custom model, just call your model's high-level wrapper function. For example:

```{r}
library(keras)

# create the model 
model <- keras_model_simple_mlp(num_classes = 10, use_dp = TRUE)

# compile graph
model %>% compile(
  loss = 'categorical_crossentropy',
  optimizer = optimizer_rmsprop(),
  metrics = c('accuracy')
)

# Generate dummy data
data <- matrix(runif(1000*100), nrow = 1000, ncol = 100)
labels <- matrix(round(runif(1000, min = 0, max = 9)), nrow = 1000, ncol = 1)

# Convert labels to categorical one-hot encoding
one_hot_labels <- to_categorical(labels, num_classes = 10)

# Train the model
model %>% fit(data, one_hot_labels, epochs=10, batch_size=32)
```






