Nothing
# Chain tests: Image classification patterns
# Conv → BatchNorm → Relu → GlobalAveragePool → Flatten → MatMul → Softmax
#
# Tests shape propagation through Conv+BN, ndims tracking, 4D→2D transitions.
run_onnx <- function(path, inputs, device = "cpu") {
m <- onnx_load(path, device = device)
res <- onnx_run(m, inputs)
res[[1]]
}
# Helper: build Conv+BN block with initializers
# Returns list(nodes, inputs_vi, initializers, output_name)
.make_conv_bn_block <- function(prefix, in_channels, out_channels,
kH = 3L, kW = 3L,
input_name = "X") {
conv_out <- paste0(prefix, "_conv_out")
bn_out <- paste0(prefix, "_bn_out")
w_name <- paste0(prefix, "_W")
b_name <- paste0(prefix, "_B")
sc_name <- paste0(prefix, "_scale")
bi_name <- paste0(prefix, "_bias")
mn_name <- paste0(prefix, "_mean")
vr_name <- paste0(prefix, "_var")
# Conv weights: [out_ch, in_ch, kH, kW]
n_weights <- out_channels * in_channels * kH * kW
w_data <- rep(1.0 / n_weights, n_weights) # small uniform
w_raw <- unlist(lapply(w_data, .float_bytes))
w_dims <- c(out_channels, in_channels, kH, kW)
w_t <- .onnx_tensor(w_name, w_dims, 1L, w_raw)
w_vi <- .onnx_value_info(w_name, 1L, w_dims)
# Conv bias
b_data <- rep(0.0, out_channels)
b_raw <- unlist(lapply(b_data, .float_bytes))
b_t <- .onnx_tensor(b_name, c(out_channels), 1L, b_raw)
b_vi <- .onnx_value_info(b_name, 1L, c(out_channels))
# BN params: scale=1, bias=0, mean=0, var=1
ones <- unlist(lapply(rep(1.0, out_channels), .float_bytes))
zeros <- unlist(lapply(rep(0.0, out_channels), .float_bytes))
sc_t <- .onnx_tensor(sc_name, c(out_channels), 1L, ones)
bi_t <- .onnx_tensor(bi_name, c(out_channels), 1L, zeros)
mn_t <- .onnx_tensor(mn_name, c(out_channels), 1L, zeros)
vr_t <- .onnx_tensor(vr_name, c(out_channels), 1L, ones)
sc_vi <- .onnx_value_info(sc_name, 1L, c(out_channels))
bi_vi <- .onnx_value_info(bi_name, 1L, c(out_channels))
mn_vi <- .onnx_value_info(mn_name, 1L, c(out_channels))
vr_vi <- .onnx_value_info(vr_name, 1L, c(out_channels))
conv_node <- .onnx_node("Conv", c(input_name, w_name, b_name), conv_out,
attrs = list(.onnx_attr_ints("kernel_shape", c(kH, kW)),
.onnx_attr_ints("pads", c(0L, 0L, 0L, 0L))))
bn_node <- .onnx_node("BatchNormalization",
c(conv_out, sc_name, bi_name, mn_name, vr_name),
bn_out)
list(
nodes = list(conv_node, bn_node),
inputs_vi = list(w_vi, b_vi, sc_vi, bi_vi, mn_vi, vr_vi),
initializers = list(w_t, b_t, sc_t, bi_t, mn_t, vr_t),
output_name = bn_out
)
}
# ── Minimal (3 ops): Conv → Relu → GlobalAveragePool ──────────
test_that("chain classify: Conv → Relu → GAP (minimal)", {
# Input: [1, 1, 4, 4], Conv 1→2 with 3x3 → [1, 2, 2, 2], Relu, GAP → [1, 2, 1, 1]
inp <- .onnx_value_info("X", 1L, c(1L, 1L, 4L, 4L))
outp <- .onnx_value_info("Y", 1L, c(1L, 2L, 1L, 1L))
# Conv weights [2, 1, 3, 3]
w_data <- rep(1.0 / 9, 18)
w_raw <- unlist(lapply(w_data, .float_bytes))
w_t <- .onnx_tensor("W", c(2L, 1L, 3L, 3L), 1L, w_raw)
w_vi <- .onnx_value_info("W", 1L, c(2L, 1L, 3L, 3L))
conv_node <- .onnx_node("Conv", c("X", "W"), "conv_out",
attrs = list(.onnx_attr_ints("kernel_shape", c(3L, 3L))))
relu_node <- .onnx_node("Relu", "conv_out", "relu_out")
gap_node <- .onnx_node("GlobalAveragePool", "relu_out", "Y")
graph <- .onnx_graph("test",
list(conv_node, relu_node, gap_node),
list(inp, w_vi), list(outp),
list(w_t))
path <- tempfile(fileext = ".onnx")
writeBin(.onnx_model(graph), path)
x <- seq(1, 16, by = 1)
result <- run_onnx(path, list(X = x))
r <- as.numeric(result)
expect_equal(length(r), 2)
# Conv averages 3x3 patches, Relu keeps positive, GAP averages spatial
expect_true(all(r > 0))
})
# ── Real (7 ops): Conv → BN → Relu → GAP → Flatten → MatMul → Softmax ──
test_that("chain classify: Conv→BN→Relu→GAP→Flatten→MatMul→Softmax (full)", {
# Input: [1, 1, 5, 5]
# Conv 1→4, kernel 3x3 → [1, 4, 3, 3]
# BN → [1, 4, 3, 3]
# Relu → [1, 4, 3, 3]
# GAP → [1, 4, 1, 1]
# Flatten → [1, 4]
# MatMul with W[4,3] → [1, 3]
# Softmax → [1, 3] (3-class classification)
inp <- .onnx_value_info("X", 1L, c(1L, 1L, 5L, 5L))
outp <- .onnx_value_info("Y", 1L, c(1L, 3L))
block <- .make_conv_bn_block("c1", 1L, 4L, 3L, 3L, "X")
relu_node <- .onnx_node("Relu", block$output_name, "relu_out")
gap_node <- .onnx_node("GlobalAveragePool", "relu_out", "gap_out")
flat_node <- .onnx_node("Flatten", "gap_out", "flat_out",
attrs = list(.onnx_attr_int("axis", 1L)))
# FC weights: [4, 3] (ONNX: K=4, N=3)
fc_data <- rep(0.5, 12)
fc_raw <- unlist(lapply(fc_data, .float_bytes))
fc_t <- .onnx_tensor("FC_W", c(4L, 3L), 1L, fc_raw)
fc_vi <- .onnx_value_info("FC_W", 1L, c(4L, 3L))
mm_node <- .onnx_node("MatMul", c("flat_out", "FC_W"), "mm_out")
sm_node <- .onnx_node("Softmax", "mm_out", "Y",
attrs = list(.onnx_attr_int("axis", 1L)))
all_nodes <- c(block$nodes, list(relu_node, gap_node, flat_node,
mm_node, sm_node))
all_inputs <- c(list(inp), block$inputs_vi, list(fc_vi))
all_inits <- c(block$initializers, list(fc_t))
graph <- .onnx_graph("test", all_nodes, all_inputs, list(outp), all_inits)
path <- tempfile(fileext = ".onnx")
writeBin(.onnx_model(graph), path)
x <- runif(25, 0, 1)
result <- run_onnx(path, list(X = x))
r <- as.numeric(result)
# Should output 3 probabilities summing to 1
expect_equal(length(r), 3)
expect_true(all(r >= 0 & r <= 1))
expect_equal(sum(r), 1.0, tolerance = 1e-4)
})
# ── Boundary: batch=2, single spatial pixel ───────────────────
test_that("chain classify: batch=2, 1x1 spatial (boundary)", {
# Input: [2, 1, 1, 1] — minimal spatial, batch of 2
# Conv 1→2, kernel 1x1 → [2, 2, 1, 1]
# Relu → [2, 2, 1, 1]
# GAP → [2, 2, 1, 1]
# Flatten → [2, 2]
# MatMul with [2, 3] → [2, 3]
# Softmax → [2, 3]
inp <- .onnx_value_info("X", 1L, c(2L, 1L, 1L, 1L))
outp <- .onnx_value_info("Y", 1L, c(2L, 3L))
# Conv 1x1: [2, 1, 1, 1]
w_data <- c(1.0, -1.0)
w_raw <- unlist(lapply(w_data, .float_bytes))
w_t <- .onnx_tensor("W", c(2L, 1L, 1L, 1L), 1L, w_raw)
w_vi <- .onnx_value_info("W", 1L, c(2L, 1L, 1L, 1L))
conv_node <- .onnx_node("Conv", c("X", "W"), "conv_out",
attrs = list(.onnx_attr_ints("kernel_shape", c(1L, 1L))))
relu_node <- .onnx_node("Relu", "conv_out", "relu_out")
gap_node <- .onnx_node("GlobalAveragePool", "relu_out", "gap_out")
flat_node <- .onnx_node("Flatten", "gap_out", "flat_out",
attrs = list(.onnx_attr_int("axis", 1L)))
fc_data <- rep(1.0, 6)
fc_raw <- unlist(lapply(fc_data, .float_bytes))
fc_t <- .onnx_tensor("FC", c(2L, 3L), 1L, fc_raw)
fc_vi <- .onnx_value_info("FC", 1L, c(2L, 3L))
mm_node <- .onnx_node("MatMul", c("flat_out", "FC"), "mm_out")
sm_node <- .onnx_node("Softmax", "mm_out", "Y",
attrs = list(.onnx_attr_int("axis", 1L)))
graph <- .onnx_graph("test",
list(conv_node, relu_node, gap_node, flat_node,
mm_node, sm_node),
list(inp, w_vi, fc_vi), list(outp),
list(w_t, fc_t))
path <- tempfile(fileext = ".onnx")
writeBin(.onnx_model(graph), path)
x <- c(3.0, -1.0) # batch of 2, one pixel each
result <- run_onnx(path, list(X = x))
r <- as.numeric(result)
expect_equal(length(r), 6) # 2 batches x 3 classes
# All softmax outputs in [0,1], total sum = 2 (two batches)
expect_true(all(r >= 0 & r <= 1))
expect_equal(sum(r), 2.0, tolerance = 1e-3)
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.