tests/testthat/test_Gradients.R

test_that("Gradient: Plot and Boxplot", {
  library(neuralnet)
  library(torch)

  data(iris)
  data <- iris[sample.int(150, size = 10), -5]
  nn <- neuralnet(Species ~ .,
                  iris,
                  linear.output = FALSE,
                  hidden = c(10, 8), act.fct = "tanh", rep = 1, threshold = 0.5
  )
  # create an converter for this model
  converter <- Converter$new(nn)

  expect_error(Gradient$new(converter, data, output_idx = c(1, 10)))
  Gradient$new(converter, data, output_idx = c(1, 2))
  expect_error(Gradient$new(converter, data, output_idx = list(c(1, 10))))
  Gradient$new(converter, data, output_idx = list(c(1, 3)))
  expect_error(Gradient$new(converter, data, output_idx = list(NULL, c(1, 2))))
  expect_error(Gradient$new(converter, data, output_label = c(1, 2)))
  expect_error(Gradient$new(converter, data, output_label = c("A", "b")))
  Gradient$new(converter, data, output_label = c("setosa", "virginica"))
  Gradient$new(converter, data, output_label = as.factor(c("setosa", "virginica")))
  Gradient$new(converter, data, output_label = list(c("setosa", "virginica")))
  expect_error(Gradient$new(converter, data,
                            output_label =  c("setosa", "virginica"),
                            output_idx = c(1, 2)))
  Gradient$new(converter, data, output_label =  c("setosa", "virginica"),
               output_idx = c(1, 3))

  # ggplot2
  grad <- Gradient$new(converter, data, dtype = "double")

  # ggplot2

  # Non-existing data points
  expect_error(plot(grad, data_idx = c(1,11)))
  expect_error(boxplot(grad, data_idx = 1:11))
  # Non-existing class
  expect_error(plot(grad, output_idx = c(5)))
  expect_error(boxplot(grad, output_idx = c(5)))

  p <- plot(grad)
  boxp <- boxplot(grad)
  expect_s4_class(p, "innsight_ggplot2")
  expect_s4_class(boxp, "innsight_ggplot2")
  p <- plot(grad, data_idx = 1:3)
  boxp <- boxplot(grad, data_idx = 1:4)
  expect_s4_class(p, "innsight_ggplot2")
  expect_s4_class(boxp, "innsight_ggplot2")
  p <- plot(grad, data_idx = 1:3, output_idx = 1:3)
  boxp <- boxplot(grad, data_idx = 1:5, output_idx = 1:3)
  expect_s4_class(p, "innsight_ggplot2")
  expect_s4_class(boxp, "innsight_ggplot2")
  boxp <- boxplot(grad, ref_data_idx = c(4))

  # plotly
  library(plotly)

  p <- plot(grad, as_plotly = TRUE)
  boxp <- boxplot(grad, as_plotly = TRUE)
  expect_s4_class(p, "innsight_plotly")
  expect_s4_class(boxp, "innsight_plotly")
  p <- plot(grad, data_idx = 1:3, as_plotly = TRUE)
  boxp <- boxplot(grad, data_idx = 1:4, as_plotly = TRUE, individual_max = 2,
                  individual_data_idx = c(1,2,5,6))
  expect_s4_class(p, "innsight_plotly")
  expect_s4_class(boxp, "innsight_plotly")
  p <- plot(grad, data_idx = 1:3, output_idx = 1:3, as_plotly = TRUE)
  boxp <- boxplot(grad, data_idx = 1:5, output_idx = 1:3, as_plotly = TRUE)
  expect_s4_class(p, "innsight_plotly")
  expect_s4_class(boxp, "innsight_plotly")

})


test_that("Gradient: Dense-Net (Neuralnet)", {
  library(neuralnet)
  library(torch)

  data(iris)
  data <- iris[sample.int(150, size = 10), -5]
  nn <- neuralnet(Species ~ .,
                  iris,
                  linear.output = FALSE,
                  hidden = c(10, 8), act.fct = "tanh", rep = 1, threshold = 0.5
  )
  # create an converter for this model
  converter <- Converter$new(nn)

  grad <- Gradient$new(converter, data)
  expect_equal(dim(grad$get_result()), c(10, 4, 3))

  grad <- Gradient$new(converter, data, dtype = "double")
  expect_equal(dim(grad$get_result()), c(10, 4, 3))

  grad <- Gradient$new(converter, data, times_input = TRUE)
  expect_equal(dim(grad$get_result()), c(10, 4, 3))

  grad <- Gradient$new(converter, data, ignore_last_act = FALSE)
  expect_equal(dim(grad$get_result()), c(10, 4, 3))
})


test_that("Gradient: Dense-Net (keras)", {
  library(keras)
  library(torch)

  data <- matrix(rnorm(4 * 10), nrow = 10)

  model <- keras_model_sequential()
  model %>%
    layer_dense(units = 16, activation = "relu", input_shape = c(4)) %>%
    layer_dense(units = 8, activation = "tanh") %>%
    layer_dense(units = 3, activation = "softmax")

  converter <- Converter$new(model)

  expect_error(Gradient$new(converter))

  grad <- Gradient$new(converter, data)
  expect_equal(dim(grad$get_result()), c(10, 4, 3))

  grad <- Gradient$new(converter, data, dtype = "double")
  expect_equal(dim(grad$get_result()), c(10, 4, 3))

  grad <- Gradient$new(converter, data, times_input = TRUE)
  expect_equal(dim(grad$get_result()), c(10, 4, 3))

  grad <- Gradient$new(converter, data, ignore_last_act = FALSE)
  expect_equal(dim(grad$get_result()), c(10, 4, 3))

  # Test get_result
  res_array <- grad$get_result()
  expect_true(is.array(res_array))
  res_dataframe <- grad$get_result(type = "data.frame")
  expect_true(is.data.frame(res_dataframe))
  res_torch <- grad$get_result(type = "torch.tensor")
  expect_true(inherits(res_torch, "torch_tensor"))
  expect_error(grad$get_result(type = "adsf"))
})

test_that("SmoothGrad: Dense-Net", {
  library(keras)
  library(torch)

  data <- matrix(rnorm(4 * 10), nrow = 10)

  model <- keras_model_sequential()
  model %>%
    layer_dense(units = 16, activation = "relu", input_shape = c(4)) %>%
    layer_dense(units = 8, activation = "tanh") %>%
    layer_dense(units = 3, activation = "softmax")

  converter <- Converter$new(model)

  grad <- SmoothGrad$new(converter, data)
  expect_equal(dim(grad$get_result()), c(10, 4, 3))

  grad <- SmoothGrad$new(converter, data, dtype = "double")
  expect_equal(dim(grad$get_result()), c(10, 4, 3))

  grad <- SmoothGrad$new(converter, data, times_input = TRUE)
  expect_equal(dim(grad$get_result()), c(10, 4, 3))

  grad <- SmoothGrad$new(converter, data, ignore_last_act = FALSE)
  expect_equal(dim(grad$get_result()), c(10, 4, 3))

  grad <- SmoothGrad$new(converter, data, n = 5)
  expect_equal(dim(grad$get_result()), c(10, 4, 3))

  grad <- SmoothGrad$new(converter, data, noise_level = 1.5)
  expect_equal(dim(grad$get_result()), c(10, 4, 3))
})

test_that("Gradient: Conv1D-Net", {
  library(keras)
  library(torch)

  data <- array(rnorm(4 * 64 * 3), dim = c(4, 64, 3))

  model <- keras_model_sequential()
  model %>%
    layer_conv_1d(
      input_shape = c(64, 3), kernel_size = 16, filters = 8,
      activation = "softplus"
    ) %>%
    layer_conv_1d(kernel_size = 16, filters = 4, activation = "tanh") %>%
    layer_conv_1d(kernel_size = 16, filters = 2, activation = "relu") %>%
    layer_flatten() %>%
    layer_dense(units = 64, activation = "relu") %>%
    layer_dense(units = 16, activation = "relu") %>%
    layer_dense(units = 1, activation = "sigmoid")

  converter <- Converter$new(model)

  grad <- Gradient$new(converter, data, channels_first = FALSE)
  expect_equal(dim(grad$get_result()), c(4, 64, 3, 1))

  grad <- Gradient$new(converter, data,
    dtype = "double",
    channels_first = FALSE
  )
  expect_equal(dim(grad$get_result()), c(4, 64, 3, 1))

  grad <- Gradient$new(converter, data,
    times_input = TRUE,
    channels_first = FALSE
  )
  expect_equal(dim(grad$get_result()), c(4, 64, 3, 1))

  grad <- Gradient$new(converter, data,
    ignore_last_act = FALSE,
    channels_first = FALSE
  )
  expect_equal(dim(grad$get_result()), c(4, 64, 3, 1))

  # Test get_result
  res_array <- grad$get_result()
  expect_true(is.array(res_array))
  res_dataframe <- grad$get_result(type = "data.frame")
  expect_true(is.data.frame(res_dataframe))
  res_torch <- grad$get_result(type = "torch.tensor")
  expect_true(inherits(res_torch, "torch_tensor"))
  expect_error(grad$get_result(type = "adsf"))

  # Same for data with channels first
  data <- array(rnorm(4 * 64 * 3), dim = c(4, 3, 64))
  grad_last <- Gradient$new(converter, data)

  res_array <- grad_last$get_result()
  expect_true(is.array(res_array))
  res_dataframe <- grad_last$get_result(type = "data.frame")
  expect_true(is.data.frame(res_dataframe))
  res_torch <- grad_last$get_result(type = "torch.tensor")
  expect_true(inherits(res_torch, "torch_tensor"))
  expect_error(grad_last$get_result(type = "adsf"))

  # Test plot and boxplot functions
  p <- plot(grad)
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot(grad_last)
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot(grad, output_idx = c(1))
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot(grad, aggr_channels = "sum",
            data_idx = c(1,2))
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot(grad, aggr_channels = "mean")
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot(grad, aggr_channels = "norm")
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot(grad, aggr_channels = mean)
  expect_s4_class(p, "innsight_ggplot2")

  p <- boxplot(grad)
  expect_s4_class(p, "innsight_ggplot2")
  p <- boxplot(grad_last)
  expect_s4_class(p, "innsight_ggplot2")
  p <- boxplot(grad, output_idx = c(1))
  expect_s4_class(p, "innsight_ggplot2")
  p <- boxplot(grad, data_idx = 1:3)
  expect_s4_class(p, "innsight_ggplot2")
  p <- boxplot(grad, individual_max = 2, individual_data_idx = 1:2 )
  expect_s4_class(p, "innsight_ggplot2")
  p <- boxplot(grad, ref_data_idx = c(4))
  expect_s4_class(p, "innsight_ggplot2")

  skip_if_not_installed("plotly")
  p <- plot(grad, as_plotly = TRUE)
  expect_s4_class(p, "innsight_plotly")
  p <- boxplot(grad, as_plotly = TRUE)
  expect_s4_class(p, "innsight_plotly")
  p <- boxplot(grad, output_idx = c(1), as_plotly = TRUE)
  expect_s4_class(p, "innsight_plotly")
  p <- boxplot(grad, data_idx = 1:3, as_plotly = TRUE)
  expect_s4_class(p, "innsight_plotly")
  p <- boxplot(grad, individual_max = 2, individual_data_idx = 1:2,
               as_plotly = TRUE)
  expect_s4_class(p, "innsight_plotly")
  p <- boxplot(grad, as_plotly = TRUE, ref_data_idx = c(3))
  expect_s4_class(p, "innsight_plotly")
})


test_that("SmoothGrad: Conv1D-Net", {
  library(keras)
  library(torch)

  data <- array(rnorm(4 * 64 * 3), dim = c(4, 64, 3))

  model <- keras_model_sequential()
  model %>%
    layer_conv_1d(
      input_shape = c(64, 3), kernel_size = 16, filters = 8,
      activation = "softplus"
    ) %>%
    layer_conv_1d(kernel_size = 16, filters = 4, activation = "tanh") %>%
    layer_conv_1d(kernel_size = 16, filters = 2, activation = "relu") %>%
    layer_flatten() %>%
    layer_dense(units = 64, activation = "relu") %>%
    layer_dense(units = 16, activation = "relu") %>%
    layer_dense(units = 1, activation = "sigmoid")

  converter <- Converter$new(model)

  grad <- SmoothGrad$new(converter, data, channels_first = FALSE)
  expect_equal(dim(grad$get_result()), c(4, 64, 3, 1))

  grad <- SmoothGrad$new(converter, data,
    dtype = "double",
    channels_first = FALSE
  )
  expect_equal(dim(grad$get_result()), c(4, 64, 3, 1))

  grad <- SmoothGrad$new(converter, data,
    times_input = TRUE,
    channels_first = FALSE
  )
  expect_equal(dim(grad$get_result()), c(4, 64, 3, 1))

  grad <- SmoothGrad$new(converter, data,
    ignore_last_act = FALSE,
    channels_first = FALSE
  )
  expect_equal(dim(grad$get_result()), c(4, 64, 3, 1))

  grad <- SmoothGrad$new(converter, data, n = 5, channels_first = FALSE)
  expect_equal(dim(grad$get_result()), c(4, 64, 3, 1))

  grad <- SmoothGrad$new(converter, data,
    noise_level = 1.5,
    channels_first = FALSE
  )
  expect_equal(dim(grad$get_result()), c(4, 64, 3, 1))
})


test_that("Gradient: Conv2D-Net", {
  library(keras)
  library(torch)

  data <- array(rnorm(4 * 32 * 32 * 3), dim = c(4, 32, 32, 3))

  model <- keras_model_sequential()
  model %>%
    layer_conv_2d(
      input_shape = c(32, 32, 3), kernel_size = 8, filters = 8,
      activation = "softplus", padding = "same"
    ) %>%
    layer_conv_2d(
      kernel_size = 8, filters = 4, activation = "tanh",
      padding = "same"
    ) %>%
    layer_conv_2d(
      kernel_size = 4, filters = 2, activation = "relu",
      padding = "same"
    ) %>%
    layer_flatten() %>%
    layer_dense(units = 64, activation = "relu") %>%
    layer_dense(units = 16, activation = "relu") %>%
    layer_dense(units = 2, activation = "sigmoid")

  converter <- Converter$new(model)

  grad <- Gradient$new(converter, data, channels_first = FALSE)
  expect_equal(dim(grad$get_result()), c(4, 32, 32, 3, 2))

  grad <- Gradient$new(converter, data,
    dtype = "double",
    channels_first = FALSE
  )
  expect_equal(dim(grad$get_result()), c(4, 32, 32, 3, 2))

  grad <- Gradient$new(converter, data,
    times_input = TRUE,
    channels_first = FALSE
  )
  expect_equal(dim(grad$get_result()), c(4, 32, 32, 3, 2))

  grad <- Gradient$new(converter, data,
    ignore_last_act = FALSE,
    channels_first = FALSE
  )
  expect_equal(dim(grad$get_result()), c(4, 32, 32, 3, 2))

  # Test get_result
  res_array <- grad$get_result()
  expect_true(is.array(res_array))
  res_dataframe <- grad$get_result(type = "data.frame")
  expect_true(is.data.frame(res_dataframe))
  res_torch <- grad$get_result(type = "torch.tensor")
  expect_true(inherits(res_torch, "torch_tensor"))
  expect_error(grad$get_result(type = "adsf"))

  # Same for data with channels first
  data <- array(rnorm(4 * 32 * 32 * 3), dim = c(4, 3, 32, 32))
  grad_first <- Gradient$new(converter, data)

  res_array <- grad_first$get_result()
  expect_true(is.array(res_array))
  res_dataframe <- grad_first$get_result(type = "data.frame")
  expect_true(is.data.frame(res_dataframe))
  res_torch <- grad_first$get_result(type = "torch.tensor")
  expect_true(inherits(res_torch, "torch_tensor"))
  expect_error(grad_first$get_result(type = "adsf"))

  # Test plot function
  p <- plot(grad)
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot(grad_first)
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot(grad, output_idx = c(1))
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot(grad, aggr_channels = "sum", data_idx = 1:2)
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot(grad, aggr_channels = "mean")
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot(grad, aggr_channels = "norm")
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot(grad, aggr_channels = function(x) -abs(sum(x)))
  expect_s4_class(p, "innsight_ggplot2")

  p <- plot_global(grad)
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot_global(grad, preprocess_FUN = identity)
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot_global(grad, preprocess_FUN = function(x) -abs(x))
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot_global(grad_first)
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot_global(grad, output_idx = c(1))
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot_global(grad, data_idx = 1:3)
  expect_s4_class(p, "innsight_ggplot2")
  p <- plot_global(grad, individual_max = 2, individual_data_idx = 1:2 )
  expect_s4_class(p, "innsight_ggplot2")

  skip_if_not_installed("plotly")
  p <- plot(grad, as_plotly = TRUE)
  expect_s4_class(p, "innsight_plotly")
  p <- plot_global(grad, as_plotly = TRUE)
  expect_s4_class(p, "innsight_plotly")
  p <- plot_global(grad, output_idx = c(1), as_plotly = TRUE)
  expect_s4_class(p, "innsight_plotly")
  p <- plot_global(grad, data_idx = 1:3, as_plotly = TRUE)
  expect_s4_class(p, "innsight_plotly")
  p <- plot_global(grad, individual_max = 2, individual_data_idx = 1:2,
               as_plotly = TRUE)
  expect_s4_class(p, "innsight_plotly")
  p <- plot_global(grad, as_plotly = TRUE, ref_data_idx = c(3))
  expect_s4_class(p, "innsight_plotly")
})

test_that("SmoothGrad: Conv2D-Net", {
  library(keras)
  library(torch)

  data <- array(rnorm(4 * 32 * 32 * 3), dim = c(4, 32, 32, 3))

  model <- keras_model_sequential()
  model %>%
    layer_conv_2d(
      input_shape = c(32, 32, 3), kernel_size = 8, filters = 8,
      activation = "softplus", padding = "same"
    ) %>%
    layer_conv_2d(
      kernel_size = 8, filters = 4, activation = "tanh",
      padding = "same"
    ) %>%
    layer_conv_2d(
      kernel_size = 4, filters = 2, activation = "relu",
      padding = "same"
    ) %>%
    layer_flatten() %>%
    layer_dense(units = 64, activation = "relu") %>%
    layer_dense(units = 16, activation = "relu") %>%
    layer_dense(units = 2, activation = "sigmoid")

  converter <- Converter$new(model)

  grad <- SmoothGrad$new(converter, data, channels_first = FALSE)
  expect_equal(dim(grad$get_result()), c(4, 32, 32, 3, 2))

  grad <- SmoothGrad$new(converter, data,
    dtype = "double",
    channels_first = FALSE
  )
  expect_equal(dim(grad$get_result()), c(4, 32, 32, 3, 2))

  grad <- SmoothGrad$new(converter, data,
    times_input = TRUE,
    channels_first = FALSE
  )
  expect_equal(dim(grad$get_result()), c(4, 32, 32, 3, 2))

  grad <- SmoothGrad$new(converter, data,
    ignore_last_act = FALSE,
    channels_first = FALSE
  )
  expect_equal(dim(grad$get_result()), c(4, 32, 32, 3, 2))

  grad <- SmoothGrad$new(converter, data, n = 5, channels_first = FALSE)
  expect_equal(dim(grad$get_result()), c(4, 32, 32, 3, 2))

  grad <- SmoothGrad$new(converter, data,
    noise_level = 1.5,
    channels_first = FALSE
  )
  expect_equal(dim(grad$get_result()), c(4, 32, 32, 3, 2))
})

test_that("Gradient + SmoothGrad: Keras model with two inputs + two outputs", {
  library(keras)

  main_input <- layer_input(shape = c(10,10,2), name = 'main_input')
  lstm_out <- main_input %>%
    layer_conv_2d(2, c(2,2), activation = "relu") %>%
    layer_flatten() %>%
    layer_dense(units = 4)
  auxiliary_input <- layer_input(shape = c(5), name = 'aux_input')
  auxiliary_output <- layer_concatenate(c(lstm_out, auxiliary_input)) %>%
    layer_dense(units = 2, activation = 'softmax', name = 'aux_output')
  main_output <- layer_concatenate(c(lstm_out, auxiliary_input)) %>%
    layer_dense(units = 5, activation = 'tanh') %>%
    layer_dense(units = 4, activation = 'tanh') %>%
    layer_dense(units = 2, activation = 'tanh') %>%
    layer_dense(units = 3, activation = 'softmax', name = 'main_output')
  model <- keras_model(
    inputs = c(auxiliary_input, main_input),
    outputs = c(auxiliary_output, main_output)
  )

  converter <- Converter$new(model)
  data <- lapply(list(c(5), c(10,10,2)),
                 function(x) array(rnorm(10 * prod(x)), dim = c(10, x)))

  grad <- Gradient$new(converter, data, channels_first = FALSE,
                       output_idx = list(c(2), c(1,3)), times_input = TRUE)
  result <- grad$get_result()
  expect_equal(length(result), 2)
  expect_equal(length(result[[1]]), 2)
  expect_equal(dim(result[[1]][[1]]), c(10,5,1))
  expect_equal(dim(result[[1]][[2]]), c(10,10,10,2,1))
  expect_equal(length(result[[2]]), 2)
  expect_equal(dim(result[[2]][[1]]), c(10,5,2))
  expect_equal(dim(result[[2]][[2]]), c(10,10,10,2,2))

  grad <- SmoothGrad$new(converter, data, channels_first = FALSE,
                       output_idx = list(c(1), c(1,2)), times_input = TRUE)
  result <- grad$get_result()
  expect_equal(length(result), 2)
  expect_equal(length(result[[1]]), 2)
  expect_equal(dim(result[[1]][[1]]), c(10,5,1))
  expect_equal(dim(result[[1]][[2]]), c(10,10,10,2,1))
  expect_equal(length(result[[2]]), 2)
  expect_equal(dim(result[[2]][[1]]), c(10,5,2))
  expect_equal(dim(result[[2]][[2]]), c(10,10,10,2,2))
})
bips-hb/innsight documentation built on April 14, 2025, 6:01 p.m.