knitr::opts_chunk$set( collapse = TRUE, comment = "#>", # Executed locally (NOT_CRAN=true); skipped on CRAN to avoid the # "CPU time > elapsed" vignette NOTE from the CPU fallback. eval = identical(Sys.getenv("NOT_CRAN"), "true") && requireNamespace("parsnip", quietly = TRUE) )
ggmlR registers a "ggml" engine for parsnip::mlp(), giving you
GPU-accelerated neural networks inside the tidymodels ecosystem —
resampling, tuning, workflows, and recipes all work out of the box.
library(ggmlR) library(parsnip)
spec <- mlp( hidden_units = c(64L, 32L), epochs = 20L, dropout = 0.1 ) |> set_engine("ggml") |> set_mode("classification") fit_obj <- fit(spec, Species ~ ., data = iris) # Class predictions preds <- predict(fit_obj, new_data = iris) head(preds) # Probability predictions probs <- predict(fit_obj, new_data = iris, type = "prob") head(probs) # Accuracy cat(sprintf("Accuracy: %.4f\n", mean(preds$.pred_class == iris$Species)))
spec_reg <- mlp( hidden_units = c(64L, 32L), epochs = 50L ) |> set_engine("ggml") |> set_mode("regression") fit_reg <- fit(spec_reg, mpg ~ ., data = mtcars) preds_reg <- predict(fit_reg, new_data = mtcars) head(preds_reg)
The ggml engine maps standard parsnip arguments to ggmlR internals:
| parsnip | ggmlR | Default |
|---------|-------|---------|
| hidden_units | hidden_layers | c(128, 64) |
| epochs | epochs | 10 |
| dropout | dropout | 0.2 |
| activation | activation | "relu" |
| learn_rate | learn_rate | 0.001 |
# Customize architecture spec_custom <- mlp( hidden_units = c(128L, 64L, 32L), epochs = 30L, dropout = 0.3, activation = "relu" ) |> set_engine("ggml") |> set_mode("classification")
library(rsample) folds <- vfold_cv(iris, v = 5L) spec <- mlp(hidden_units = c(32L), epochs = 10L) |> set_engine("ggml") |> set_mode("classification") library(tune) library(yardstick) library(workflows) wf <- workflow() |> add_model(spec) |> add_formula(Species ~ .) results <- fit_resamples(wf, resamples = folds) collect_metrics(results)
ggmlR accepts only numeric features. Use recipes to handle factors,
missing values, and scaling:
library(recipes) library(workflows) rec <- recipe(Species ~ ., data = iris) |> step_normalize(all_numeric_predictors()) spec <- mlp(hidden_units = c(32L), epochs = 10L) |> set_engine("ggml") |> set_mode("classification") wf <- workflow() |> add_recipe(rec) |> add_model(spec) fit_obj <- fit(wf, data = iris) predict(fit_obj, new_data = iris)
For datasets with factors:
rec <- recipe(Species ~ ., data = iris) |> step_dummy(all_nominal_predictors()) |> step_normalize(all_numeric_predictors())
library(tune) library(dials) library(workflows) spec <- mlp( hidden_units = tune(), epochs = tune(), dropout = tune() ) |> set_engine("ggml") |> set_mode("classification") wf <- workflow() |> add_model(spec) |> add_formula(Species ~ .) grid <- grid_regular( hidden_units(range = c(16L, 128L)), epochs(range = c(10L, 50L)), dropout(range = c(0, 0.4)), levels = 3L ) folds <- vfold_cv(iris, v = 3L) results <- tune_grid(wf, resamples = folds, grid = grid) show_best(results, metric = "accuracy")
library(workflows) library(workflowsets) specs <- workflow_set( preproc = list(basic = Species ~ .), models = list( ggml = mlp(hidden_units = c(32L), epochs = 20L) |> set_engine("ggml"), nnet = mlp(hidden_units = 32L, epochs = 200L) |> set_engine("nnet") ) ) |> workflow_map("fit_resamples", resamples = vfold_cv(iris, v = 5L)) rank_results(specs, rank_metric = "accuracy")
After fitting a workflow or parsnip model you can reach the native ggmlR
object and the training time with the standard hardhat/tune extractors.
spec <- mlp(hidden_units = c(16L), epochs = 10L) |> set_engine("ggml") |> set_mode("classification") fit_obj <- fit(spec, Species ~ ., data = iris) # The native ggmlR engine object (class "ggmlr_parsnip_model"). # extract_fit_*() are re-exported by parsnip (originally from hardhat). eng <- parsnip::extract_fit_engine(fit_obj) class(eng) # Training time parsnip recorded for the fit (one-row tibble: stage_id, elapsed). parsnip::extract_fit_time(fit_obj)
The engine object is the same one returned by ggmlR's own fit wrappers, so all the inspection helpers work on it directly:
ggml_model_backend(eng) # "vulkan" or "cpu" (actual backend used) head(ggml_training_history(eng)) # per-epoch loss / accuracy curve generics::glance(eng) # one-row model summary generics::tidy(eng) # one row per layer
The fitted engine object holds a live compiled model. Keep these limits in mind, especially for resampling and tuning on a GPU.
Serialization / GPU state. A compiled model carries external pointers
(the ggml backend and scheduler) and, on GPU, weights living in Vulkan
buffers. These do not survive a plain saveRDS() / readRDS() or being
shipped to a worker process as-is. For mlr3 the learner marshals the model
(marshal_model() / unmarshal_model()); within tidymodels, persist via
ggmlR's own ggml_save_model() / ggml_load_model() (sequential/functional)
or ag_save_model() / ag_load_model() (autograd) rather than serializing
the raw engine object.
Parallel resampling. fit_resamples() / tune_grid() with a parallel
backend serialize fitted models across workers; because of the GPU state
above, prefer sequential execution (or CPU) when in doubt. In particular,
avoid control_grid(parallel_over = "everything") together with a live GPU
model — it can crash or return wrong results. The safe pattern is
parallel_over = "resamples" (the default), or running on CPU.
Autograd tradepath. Autograd (ag_sequential) models are reconstructed
on unmarshal from a captured (dims, hyperparameters) snapshot, not from
the training task. A custom model_fn that reads the task at fit time is
therefore not round-trippable through marshal (the task is NULL at rebuild).
Stick to dims/parameters-driven architectures if you need marshalable
autograd models.
One device per fit. A compiled model is bound to the backend chosen at
set_engine("ggml", backend = ...) (or auto-detected). It is not migrated
between CPU and GPU after fitting; re-fit to change device.
| Feature | Supported |
|---------|-----------|
| Classification | Yes (class, prob) |
| Regression | Yes (numeric) |
| GPU (Vulkan) | Yes (auto-detected) |
| Recipes / preprocessing | Yes |
| Resampling | Yes |
| Tuning | Yes |
| Workflows | Yes |
| extract_fit_engine() / extract_fit_time() | Yes |
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.