tests/testthat/test-chain-sequential-batchnorm.R

# Chain test: Sequential API with BatchNorm training
# Pattern from titanic_classification.R variant 3:
#   dense→BatchNorm→dense→BatchNorm→dense(softmax) + adam + fit→predict
#
# Uses synthetic linearly separable data (no external files).

# ── Sequential + BatchNorm: compile→fit→predict ─────────────

test_that("chain sequential-batchnorm: fit reduces loss", {
  set.seed(123)
  n <- 100L
  # 2-class linearly separable: class 0 centered at (-1,-1), class 1 at (1,1)
  x <- rbind(matrix(rnorm(n, -1, 0.5), n/2, 2),
             matrix(rnorm(n,  1, 0.5), n/2, 2))
  y <- rbind(matrix(c(1,0), n/2, 2, byrow = TRUE),
             matrix(c(0,1), n/2, 2, byrow = TRUE))

  m <- ggml_model_sequential() |>
    ggml_layer_dense(16L, activation = "relu", input_shape = 2L) |>
    ggml_layer_batch_norm() |>
    ggml_layer_dense(8L, activation = "relu") |>
    ggml_layer_batch_norm() |>
    ggml_layer_dense(2L, activation = "softmax") |>
    ggml_compile(optimizer = "adam", loss = "categorical_crossentropy")

  h <- ggml_fit(m, x, y, epochs = 100L, batch_size = 10L, verbose = 0L)

  # Loss should decrease
  expect_true(h$history$train_loss[length(h$history$train_loss)] < h$history$train_loss[1])
})

test_that("chain sequential-batchnorm: predict gives valid probabilities", {
  set.seed(123)
  n <- 96L
  x <- rbind(matrix(rnorm(n, -1, 0.5), n/2, 2),
             matrix(rnorm(n,  1, 0.5), n/2, 2))
  y <- rbind(matrix(c(1,0), n/2, 2, byrow = TRUE),
             matrix(c(0,1), n/2, 2, byrow = TRUE))

  m <- ggml_model_sequential() |>
    ggml_layer_dense(16L, activation = "relu", input_shape = 2L) |>
    ggml_layer_batch_norm() |>
    ggml_layer_dense(2L, activation = "softmax") |>
    ggml_compile(optimizer = "adam", loss = "categorical_crossentropy")

  m <- ggml_fit(m, x, y, epochs = 30L, batch_size = 16L, verbose = 0L)
  p <- ggml_predict(m, x, batch_size = 32L)

  expect_equal(nrow(p), nrow(x))
  expect_equal(ncol(p), 2L)
  # Probabilities sum to ~1
  row_sums <- rowSums(p)
  expect_true(all(abs(row_sums - 1.0) < 0.01))
  # All probabilities in [0,1]
  expect_true(all(p >= 0 & p <= 1))
})

test_that("chain sequential-batchnorm: accuracy > 70% on separable data", {
  set.seed(42)
  n <- 192L
  x <- rbind(matrix(rnorm(n, -3, 0.5), n/2, 2),
             matrix(rnorm(n,  3, 0.5), n/2, 2))
  y <- rbind(matrix(c(1,0), n/2, 2, byrow = TRUE),
             matrix(c(0,1), n/2, 2, byrow = TRUE))

  m <- ggml_model_sequential() |>
    ggml_layer_dense(32L, activation = "relu", input_shape = 2L) |>
    ggml_layer_batch_norm() |>
    ggml_layer_dense(2L, activation = "softmax") |>
    ggml_compile(optimizer = "adam", loss = "categorical_crossentropy")

  m <- ggml_fit(m, x, y, epochs = 120L, batch_size = 32L, verbose = 0L)
  p <- ggml_predict(m, x, batch_size = 32L)
  pred_class <- max.col(p)
  true_class <- max.col(y)
  acc <- mean(pred_class == true_class)
  expect_true(acc > 0.7)
})

Try the ggmlR package in your browser

Any scripts or data that you put into this service are public.

ggmlR documentation built on July 14, 2026, 1:08 a.m.